2009-05-08 13:28:41 +00:00
/*
* OpenClonk , http : //www.openclonk.org
*
2013-12-17 20:01:09 +00:00
* Copyright ( c ) 2001 - 2009 , RedWolf Design GmbH , http : //www.clonk.de/
2016-04-03 18:18:29 +00:00
* Copyright ( c ) 2009 - 2016 , The OpenClonk Team and contributors
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* Distributed under the terms of the ISC license ; see accompanying file
* " COPYING " for details .
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* " Clonk " is a registered trademark of Matthes Bender , used with permission .
* See accompanying file " TRADEMARK " for details .
2009-05-08 13:28:41 +00:00
*
2013-12-17 20:01:09 +00:00
* To redistribute this file separately , substitute the full license texts
* for the above references .
2009-05-08 13:28:41 +00:00
*/
// parses scripts
2016-04-03 18:07:56 +00:00
# include "C4Include.h"
2016-04-07 18:21:43 +00:00
# include "script/C4AulParse.h"
2016-05-12 17:43:48 +00:00
2017-04-30 08:49:09 +00:00
# include "object/C4Def.h"
2016-04-03 18:07:56 +00:00
# include "script/C4AulDebug.h"
# include "script/C4AulExec.h"
2009-05-08 13:28:41 +00:00
2016-05-10 12:10:34 +00:00
# ifndef DEBUG_BYTECODE_DUMP
2009-05-08 13:28:41 +00:00
# define DEBUG_BYTECODE_DUMP 0
2016-05-10 12:10:34 +00:00
# endif
2015-08-30 13:35:28 +00:00
# include <iomanip>
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
# define C4AUL_Include "#include"
# define C4AUL_Append "#appendto"
2017-02-13 13:53:17 +00:00
# define C4AUL_Warning "#warning"
# define C4Aul_Warning_enable "enable"
# define C4Aul_Warning_disable "disable"
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
# define C4AUL_Func "func"
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
# define C4AUL_Private "private"
# define C4AUL_Protected "protected"
# define C4AUL_Public "public"
# define C4AUL_Global "global"
2009-05-08 13:28:41 +00:00
# define C4AUL_Const "const"
2010-03-28 18:58:01 +00:00
# define C4AUL_If "if"
# define C4AUL_Else "else"
2010-04-07 13:04:19 +00:00
# define C4AUL_Do "do"
2010-03-28 18:58:01 +00:00
# define C4AUL_While "while"
# define C4AUL_For "for"
# define C4AUL_In "in"
# define C4AUL_Return "return"
# define C4AUL_Var "Var"
# define C4AUL_Par "Par"
# define C4AUL_Break "break"
# define C4AUL_Continue "continue"
# define C4AUL_this "this"
# define C4AUL_GlobalNamed "static"
# define C4AUL_LocalNamed "local"
# define C4AUL_VarNamed "var"
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
# define C4AUL_TypeInt "int"
# define C4AUL_TypeBool "bool"
# define C4AUL_TypeC4ID "id"
2011-09-24 16:37:28 +00:00
# define C4AUL_TypeDef "def"
# define C4AUL_TypeEffect "effect"
2010-03-28 18:58:01 +00:00
# define C4AUL_TypeC4Object "object"
# define C4AUL_TypePropList "proplist"
# define C4AUL_TypeString "string"
# define C4AUL_TypeArray "array"
2011-09-24 23:20:18 +00:00
# define C4AUL_TypeFunction "func"
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
# define C4AUL_True "true"
# define C4AUL_False "false"
2009-07-17 02:50:50 +00:00
# define C4AUL_Nil "nil"
2011-11-07 00:39:43 +00:00
# define C4AUL_New "new"
2009-05-08 13:28:41 +00:00
// script token type
2016-04-24 13:20:28 +00:00
enum C4AulTokenType : int
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
ATT_INVALID , // invalid token
2010-03-28 18:58:01 +00:00
ATT_DIR , // directive
ATT_IDTF , // identifier
ATT_INT , // integer constant
ATT_STRING , // string constant
2010-08-14 23:53:18 +00:00
ATT_DOT , // "."
2010-03-28 18:58:01 +00:00
ATT_COMMA , // ","
ATT_COLON , // ":"
ATT_SCOLON , // ";"
ATT_BOPEN , // "("
ATT_BCLOSE , // ")"
ATT_BOPEN2 , // "["
2009-05-08 13:28:41 +00:00
ATT_BCLOSE2 , // "]"
2010-03-28 18:58:01 +00:00
ATT_BLOPEN , // "{"
2009-05-08 13:28:41 +00:00
ATT_BLCLOSE , // "}"
2010-03-28 18:58:01 +00:00
ATT_CALL , // "->"
2011-02-19 21:18:27 +00:00
ATT_CALLFS , // "->~"
2009-05-08 13:28:41 +00:00
ATT_LDOTS , // '...'
2010-04-07 13:04:19 +00:00
ATT_SET , // '='
2009-05-08 13:28:41 +00:00
ATT_OPERATOR , // operator
2010-03-28 18:58:01 +00:00
ATT_EOF // end of file
} ;
2009-05-08 13:28:41 +00:00
2016-05-12 17:43:48 +00:00
C4AulParse : : C4AulParse ( C4ScriptHost * a ) :
2017-05-03 18:28:00 +00:00
Fn ( nullptr ) , Host ( a ) , pOrgScript ( a ) , Engine ( a - > Engine ) ,
2016-04-07 18:21:43 +00:00
SPos ( a - > Script . getData ( ) ) , TokenSPos ( SPos ) ,
TokenType ( ATT_INVALID ) ,
2016-11-02 23:58:02 +00:00
ContextToExecIn ( nullptr )
2016-04-07 18:21:43 +00:00
{ }
C4AulParse : : C4AulParse ( C4AulScriptFunc * Fn , C4AulScriptContext * context , C4AulScriptEngine * Engine ) :
2016-11-02 23:58:02 +00:00
Fn ( Fn ) , Host ( nullptr ) , pOrgScript ( nullptr ) , Engine ( Engine ) ,
2016-04-07 18:21:43 +00:00
SPos ( Fn - > Script ) , TokenSPos ( SPos ) ,
TokenType ( ATT_INVALID ) ,
2016-04-24 21:23:09 +00:00
ContextToExecIn ( context )
2016-05-12 17:43:48 +00:00
{ }
2016-04-07 18:21:43 +00:00
C4AulParse : : ~ C4AulParse ( )
2010-03-28 18:58:01 +00:00
{
2016-04-07 18:21:43 +00:00
ClearToken ( ) ;
}
2009-05-08 13:28:41 +00:00
2016-01-03 02:50:04 +00:00
void C4ScriptHost : : Warn ( const char * pMsg , . . . )
2010-03-28 18:58:01 +00:00
{
2012-05-25 22:22:22 +00:00
va_list args ; va_start ( args , pMsg ) ;
2017-02-13 13:53:17 +00:00
StdStrBuf Buf = FormatStringV ( pMsg , args ) ;
2016-04-27 22:11:46 +00:00
Buf . AppendFormat ( " (%s) " , ScriptName . getData ( ) ) ;
2017-02-13 11:28:14 +00:00
Engine - > GetErrorHandler ( ) - > OnWarning ( Buf . getData ( ) ) ;
va_end ( args ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2017-02-13 13:53:17 +00:00
void C4AulParse : : Warn ( C4AulWarningId warning , . . . )
2010-03-28 18:58:01 +00:00
{
2017-02-13 13:53:17 +00:00
if ( ! pOrgScript - > IsWarningEnabled ( TokenSPos , warning ) )
return ;
va_list args ; va_start ( args , warning ) ;
StdStrBuf Buf = FormatStringV ( C4AulWarningMessages [ static_cast < size_t > ( warning ) ] , args ) ;
2016-04-27 22:11:46 +00:00
AppendPosition ( Buf ) ;
2017-02-13 13:53:17 +00:00
Buf . AppendFormat ( " [%s] " , C4AulWarningIDs [ static_cast < size_t > ( warning ) ] ) ;
2017-02-13 11:28:14 +00:00
Engine - > GetErrorHandler ( ) - > OnWarning ( Buf . getData ( ) ) ;
va_end ( args ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2012-05-25 22:22:22 +00:00
void C4AulParse : : Error ( const char * pMsg , . . . )
2010-03-28 18:58:01 +00:00
{
2012-05-25 22:22:22 +00:00
va_list args ; va_start ( args , pMsg ) ;
StdStrBuf Buf ;
Buf . FormatV ( pMsg , args ) ;
2015-12-07 14:15:49 +00:00
throw C4AulParseError ( this , Buf . getData ( ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-04-27 22:11:46 +00:00
void C4AulParse : : AppendPosition ( StdStrBuf & Buf )
2010-03-28 18:58:01 +00:00
{
2016-04-27 22:11:46 +00:00
if ( Fn & & Fn - > GetName ( ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Show function name
2016-04-27 22:11:46 +00:00
Buf . AppendFormat ( " (in %s " , Fn - > GetName ( ) ) ;
2009-05-08 13:28:41 +00:00
// Exact position
2016-04-27 22:11:46 +00:00
if ( Fn - > pOrgScript & & TokenSPos )
Buf . AppendFormat ( " , %s:%d:%d) " ,
Fn - > pOrgScript - > ScriptName . getData ( ) ,
SGetLine ( Fn - > pOrgScript - > GetScript ( ) , TokenSPos ) ,
SLineGetCharacters ( Fn - > pOrgScript - > GetScript ( ) , TokenSPos ) ) ;
2009-05-08 13:28:41 +00:00
else
2016-04-27 22:11:46 +00:00
Buf . AppendChar ( ' ) ' ) ;
2010-03-28 18:58:01 +00:00
}
2016-04-27 22:11:46 +00:00
else if ( pOrgScript )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Script name
2016-04-27 22:11:46 +00:00
Buf . AppendFormat ( " (%s:%d:%d) " ,
pOrgScript - > ScriptName . getData ( ) ,
SGetLine ( pOrgScript - > GetScript ( ) , TokenSPos ) ,
SLineGetCharacters ( pOrgScript - > GetScript ( ) , TokenSPos ) ) ;
2009-05-08 13:28:41 +00:00
}
2013-01-20 22:54:54 +00:00
// show a warning if the error is in a remote script
2016-04-27 22:11:46 +00:00
if ( pOrgScript ! = Host & & Host )
Buf . AppendFormat ( " (as #appendto/#include to %s) " , Host - > ScriptName . getData ( ) ) ;
}
2009-05-08 13:28:41 +00:00
2016-04-27 22:11:46 +00:00
C4AulParseError : : C4AulParseError ( C4AulParse * state , const char * pMsg )
{
// compose error string
2016-10-31 10:16:14 +00:00
sMessage . Copy ( pMsg ) ;
2016-04-27 22:11:46 +00:00
state - > AppendPosition ( sMessage ) ;
2010-03-28 18:58:01 +00:00
}
2016-04-27 22:11:46 +00:00
C4AulParseError : : C4AulParseError ( C4ScriptHost * pScript , const char * pMsg )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// compose error string
2016-10-31 10:16:14 +00:00
sMessage . Copy ( pMsg ) ;
2010-03-28 18:58:01 +00:00
if ( pScript )
{
2009-05-08 13:28:41 +00:00
// Script name
sMessage . AppendFormat ( " (%s) " ,
2010-03-28 18:58:01 +00:00
pScript - > ScriptName . getData ( ) ) ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-04-27 22:11:46 +00:00
C4AulParseError : : C4AulParseError ( C4AulScriptFunc * Fn , const char * SPos , const char * pMsg )
{
// compose error string
2016-10-31 10:16:14 +00:00
sMessage . Copy ( pMsg ) ;
2016-04-27 22:11:46 +00:00
if ( ! Fn ) return ;
sMessage . Append ( " ( " ) ;
// Show function name
if ( Fn - > GetName ( ) )
sMessage . AppendFormat ( " in %s " , Fn - > GetName ( ) ) ;
if ( Fn - > GetName ( ) & & Fn - > pOrgScript & & SPos )
sMessage . Append ( " , " ) ;
// Exact position
if ( Fn - > pOrgScript & & SPos )
sMessage . AppendFormat ( " %s:%d:%d) " ,
Fn - > pOrgScript - > ScriptName . getData ( ) ,
SGetLine ( Fn - > pOrgScript - > GetScript ( ) , SPos ) ,
SLineGetCharacters ( Fn - > pOrgScript - > GetScript ( ) , SPos ) ) ;
else
sMessage . AppendChar ( ' ) ' ) ;
}
2012-04-12 19:03:14 +00:00
bool C4AulParse : : AdvanceSpaces ( )
2010-03-28 18:58:01 +00:00
{
2012-01-26 23:42:41 +00:00
if ( ! SPos )
return false ;
2012-01-24 17:35:21 +00:00
while ( * SPos )
2009-05-08 13:28:41 +00:00
{
2012-01-24 17:35:21 +00:00
if ( * SPos = = ' / ' )
2010-03-28 18:58:01 +00:00
{
2012-01-24 17:35:21 +00:00
// // comment
if ( SPos [ 1 ] = = ' / ' )
2009-05-08 13:28:41 +00:00
{
2012-01-24 17:35:21 +00:00
SPos + = 2 ;
while ( * SPos & & * SPos ! = 13 & & * SPos ! = 10 )
+ + SPos ;
2009-05-08 13:28:41 +00:00
}
2012-01-24 17:35:21 +00:00
// /* comment */
else if ( SPos [ 1 ] = = ' * ' )
{
2010-03-28 18:58:01 +00:00
SPos + = 2 ;
2012-01-24 17:35:21 +00:00
while ( * SPos & & ( * SPos ! = ' * ' | | SPos [ 1 ] ! = ' / ' ) )
+ + SPos ;
SPos + = 2 ;
}
else
return true ;
2010-03-28 18:58:01 +00:00
}
2012-01-24 17:35:21 +00:00
// Skip any "zero width no-break spaces" (also known as Byte Order Marks)
else if ( * SPos = = ' \xEF ' & & SPos [ 1 ] = = ' \xBB ' & & SPos [ 2 ] = = ' \xBF ' )
SPos + = 3 ;
else if ( ( unsigned ) * SPos > 32 )
return true ;
else
+ + SPos ;
2009-05-08 13:28:41 +00:00
}
2012-01-24 17:35:21 +00:00
// end of script reached
2010-03-28 18:58:01 +00:00
return false ;
}
2009-05-08 13:28:41 +00:00
//=========================== C4Script Operator Map ===================================
2016-04-07 18:21:43 +00:00
const C4ScriptOpDef C4ScriptOpMap [ ] =
2010-03-28 18:58:01 +00:00
{
2012-12-23 23:46:00 +00:00
// priority postfix
// | identifier | changer
// | | Bytecode | | no second id
// | | | | | | RetType ParType1 ParType2
2009-05-08 13:28:41 +00:00
// prefix
2017-05-03 18:28:00 +00:00
{ 15 , " ++ " , AB_Inc , false , true , false , C4V_Int , C4V_Int , C4V_Any } ,
{ 15 , " -- " , AB_Dec , false , true , false , C4V_Int , C4V_Int , C4V_Any } ,
{ 15 , " ~ " , AB_BitNot , false , false , false , C4V_Int , C4V_Int , C4V_Any } ,
{ 15 , " ! " , AB_Not , false , false , false , C4V_Bool , C4V_Bool , C4V_Any } ,
{ 15 , " + " , AB_ERR , false , false , false , C4V_Int , C4V_Int , C4V_Any } ,
{ 15 , " - " , AB_Neg , false , false , false , C4V_Int , C4V_Int , C4V_Any } ,
2010-04-07 13:04:19 +00:00
2009-05-08 13:28:41 +00:00
// postfix (whithout second statement)
2017-05-03 18:28:00 +00:00
{ 16 , " ++ " , AB_Inc , true , true , true , C4V_Int , C4V_Int , C4V_Any } ,
{ 16 , " -- " , AB_Dec , true , true , true , C4V_Int , C4V_Int , C4V_Any } ,
2010-04-08 00:47:45 +00:00
2009-05-08 13:28:41 +00:00
// postfix
2017-05-03 18:28:00 +00:00
{ 14 , " ** " , AB_Pow , true , false , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 13 , " / " , AB_Div , true , false , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 13 , " * " , AB_Mul , true , false , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 13 , " % " , AB_Mod , true , false , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 12 , " - " , AB_Sub , true , false , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 12 , " + " , AB_Sum , true , false , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 11 , " << " , AB_LeftShift , true , false , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 11 , " >> " , AB_RightShift , true , false , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 10 , " < " , AB_LessThan , true , false , false , C4V_Bool , C4V_Int , C4V_Int } ,
{ 10 , " <= " , AB_LessThanEqual , true , false , false , C4V_Bool , C4V_Int , C4V_Int } ,
{ 10 , " > " , AB_GreaterThan , true , false , false , C4V_Bool , C4V_Int , C4V_Int } ,
{ 10 , " >= " , AB_GreaterThanEqual , true , false , false , C4V_Bool , C4V_Int , C4V_Int } ,
{ 9 , " == " , AB_Equal , true , false , false , C4V_Bool , C4V_Any , C4V_Any } ,
{ 9 , " != " , AB_NotEqual , true , false , false , C4V_Bool , C4V_Any , C4V_Any } ,
{ 8 , " & " , AB_BitAnd , true , false , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 6 , " ^ " , AB_BitXOr , true , false , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 6 , " | " , AB_BitOr , true , false , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 5 , " && " , AB_JUMPAND , true , false , false , C4V_Bool , C4V_Bool , C4V_Bool } ,
{ 4 , " || " , AB_JUMPOR , true , false , false , C4V_Bool , C4V_Bool , C4V_Bool } ,
{ 3 , " ?? " , AB_JUMPNNIL , true , false , false , C4V_Any , C4V_Any , C4V_Any } ,
2010-04-07 13:04:19 +00:00
// changers
2017-05-03 18:28:00 +00:00
{ 2 , " *= " , AB_Mul , true , true , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 2 , " /= " , AB_Div , true , true , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 2 , " %= " , AB_Mod , true , true , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 2 , " += " , AB_Sum , true , true , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 2 , " -= " , AB_Sub , true , true , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 2 , " &= " , AB_BitAnd , true , true , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 2 , " |= " , AB_BitOr , true , true , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 2 , " ^= " , AB_BitXOr , true , true , false , C4V_Int , C4V_Int , C4V_Int } ,
{ 0 , nullptr , AB_ERR , false , false , false , C4V_Nil , C4V_Nil , C4V_Nil }
2009-05-08 13:28:41 +00:00
} ;
2012-04-12 19:03:14 +00:00
int C4AulParse : : GetOperator ( const char * pScript )
2009-05-08 13:28:41 +00:00
{
// return value:
// >= 0: operator found. could be found in C4ScriptOfDef
// -1: isn't an operator
unsigned int i ;
2010-03-28 18:58:01 +00:00
if ( ! * pScript ) return 0 ;
2009-07-22 23:02:41 +00:00
// operators are not alphabetical
2010-03-28 18:58:01 +00:00
if ( ( * pScript > = ' a ' & & * pScript < = ' z ' ) | |
( * pScript > = ' A ' & & * pScript < = ' Z ' ) )
2009-05-08 13:28:41 +00:00
{
2009-07-22 23:02:41 +00:00
return - 1 ;
2009-05-08 13:28:41 +00:00
}
2012-06-10 22:47:28 +00:00
// find the longest operator
int len = 0 ; int maxfound = - 1 ;
2010-03-28 18:58:01 +00:00
for ( i = 0 ; C4ScriptOpMap [ i ] . Identifier ; i + + )
2012-06-10 22:47:28 +00:00
{
if ( SEqual2 ( pScript , C4ScriptOpMap [ i ] . Identifier ) )
{
int oplen = SLen ( C4ScriptOpMap [ i ] . Identifier ) ;
if ( oplen > len )
{
len = oplen ;
maxfound = i ;
}
}
}
return maxfound ;
2009-05-08 13:28:41 +00:00
}
2012-04-12 19:03:14 +00:00
void C4AulParse : : ClearToken ( )
2010-01-30 16:51:26 +00:00
{
// if last token was a string, make sure its ref is deleted
2011-02-21 23:07:08 +00:00
if ( TokenType = = ATT_STRING & & cStr )
2010-01-30 16:51:26 +00:00
{
2011-02-21 23:07:08 +00:00
cStr - > DecRef ( ) ;
2010-01-30 16:51:26 +00:00
TokenType = ATT_INVALID ;
}
}
2016-01-02 02:47:19 +00:00
C4AulTokenType C4AulParse : : GetNextToken ( )
2010-03-28 18:58:01 +00:00
{
2010-01-30 16:51:26 +00:00
// clear mem of prev token
ClearToken ( ) ;
2009-05-08 13:28:41 +00:00
// move to start of token
if ( ! AdvanceSpaces ( ) ) return ATT_EOF ;
// store offset
2012-05-16 21:24:40 +00:00
TokenSPos = SPos ;
2009-05-08 13:28:41 +00:00
2012-01-24 17:35:21 +00:00
// get char
char C = * ( SPos + + ) ;
// Mostly sorted by frequency, except that tokens that have
// other tokens as prefixes need to be checked for first.
if ( Inside ( C , ' a ' , ' z ' ) | | Inside ( C , ' A ' , ' Z ' ) | | C = = ' _ ' | | C = = ' # ' )
2010-03-28 18:58:01 +00:00
{
2012-01-24 17:35:21 +00:00
// identifier or directive
bool dir = C = = ' # ' ;
int Len = 1 ;
C = * SPos ;
while ( Inside ( C , ' 0 ' , ' 9 ' ) | | Inside ( C , ' a ' , ' z ' ) | | Inside ( C , ' A ' , ' Z ' ) | | C = = ' _ ' )
{
+ + Len ;
C = * ( + + SPos ) ;
}
2009-05-08 13:28:41 +00:00
2017-02-13 13:53:17 +00:00
// Special case for #warning because we don't want to give it to the parser
if ( dir & & SEqual2 ( TokenSPos , C4AUL_Warning ) )
{
// Look for end of line or end of file
while ( * SPos ! = ' \n ' & & * SPos ! = ' \0 ' ) + + SPos ;
Parse_WarningPragma ( ) ;
// And actually return the next token.
return GetNextToken ( ) ;
}
2015-11-15 12:53:01 +00:00
Len = std : : min ( Len , C4AUL_MAX_Identifier ) ;
2012-05-16 21:24:40 +00:00
SCopy ( TokenSPos , Idtf , Len ) ;
2012-01-24 17:35:21 +00:00
return dir ? ATT_DIR : ATT_IDTF ;
}
else if ( C = = ' ( ' ) return ATT_BOPEN ; // "("
else if ( C = = ' ) ' ) return ATT_BCLOSE ; // ")"
else if ( C = = ' , ' ) return ATT_COMMA ; // ","
else if ( C = = ' ; ' ) return ATT_SCOLON ; // ";"
else if ( Inside ( C , ' 0 ' , ' 9 ' ) )
{
// integer
if ( C = = ' 0 ' & & * SPos = = ' x ' )
2010-03-28 18:58:01 +00:00
{
2012-01-24 17:35:21 +00:00
// hexadecimal
cInt = StrToI32 ( SPos + 1 , 16 , & SPos ) ;
return ATT_INT ;
}
else
2010-03-28 18:58:01 +00:00
{
2012-01-24 17:35:21 +00:00
// decimal
2012-05-16 21:24:40 +00:00
cInt = StrToI32 ( TokenSPos , 10 , & SPos ) ;
2012-01-24 17:35:21 +00:00
return ATT_INT ;
2010-03-28 18:58:01 +00:00
}
2012-01-24 17:35:21 +00:00
}
else if ( C = = ' - ' & & * SPos = = ' > ' & & * ( SPos + 1 ) = = ' ~ ' )
{ SPos + = 2 ; return ATT_CALLFS ; } // "->~"
else if ( C = = ' - ' & & * SPos = = ' > ' )
{ + + SPos ; return ATT_CALL ; } // "->"
else if ( ( cInt = GetOperator ( SPos - 1 ) ) ! = - 1 )
{
SPos + = SLen ( C4ScriptOpMap [ cInt ] . Identifier ) - 1 ;
return ATT_OPERATOR ;
}
else if ( C = = ' = ' ) return ATT_SET ; // "="
else if ( C = = ' { ' ) return ATT_BLOPEN ; // "{"
else if ( C = = ' } ' ) return ATT_BLCLOSE ; // "}"
else if ( C = = ' " ' )
{
// string
std : : string strbuf ;
strbuf . reserve ( 512 ) ; // assume most strings to be smaller than this
// string end
while ( * SPos ! = ' " ' )
{
C = * SPos ;
+ + SPos ;
if ( C = = ' \\ ' ) // escape
switch ( * SPos )
2010-03-28 18:58:01 +00:00
{
2012-01-24 17:35:21 +00:00
case ' " ' : + + SPos ; strbuf . push_back ( ' " ' ) ; break ;
case ' \\ ' : + + SPos ; strbuf . push_back ( ' \\ ' ) ; break ;
case ' n ' : + + SPos ; strbuf . push_back ( ' \n ' ) ; break ;
case ' t ' : + + SPos ; strbuf . push_back ( ' \t ' ) ; break ;
case ' x ' :
2009-05-08 13:28:41 +00:00
{
2012-01-24 17:35:21 +00:00
+ + SPos ;
// hexadecimal escape: \xAD.
// First char must be a hexdigit
if ( ! std : : isxdigit ( * SPos ) )
2010-03-28 18:58:01 +00:00
{
2017-02-13 13:53:17 +00:00
Warn ( C4AulWarningId : : invalid_hex_escape ) ;
2012-01-24 17:35:21 +00:00
strbuf . push_back ( ' \\ ' ) ; strbuf . push_back ( ' x ' ) ;
2010-03-28 18:58:01 +00:00
}
2012-01-24 17:35:21 +00:00
else
2010-03-28 18:58:01 +00:00
{
char ch = 0 ;
2012-01-24 17:35:21 +00:00
while ( std : : isxdigit ( * SPos ) )
2010-03-28 18:58:01 +00:00
{
2012-01-24 17:35:21 +00:00
ch * = 16 ;
if ( * SPos > = ' 0 ' & & * SPos < = ' 9 ' )
ch + = * SPos - ' 0 ' ;
else if ( * SPos > = ' a ' & & * SPos < = ' f ' )
ch + = * SPos - ' a ' + 10 ;
else if ( * SPos > = ' A ' & & * SPos < = ' F ' )
ch + = * SPos - ' A ' + 10 ;
+ + SPos ;
} ;
2010-03-28 18:58:01 +00:00
strbuf . push_back ( ch ) ;
}
break ;
2012-01-24 17:35:21 +00:00
}
case ' 0 ' : case ' 1 ' : case ' 2 ' : case ' 3 ' : case ' 4 ' : case ' 5 ' : case ' 6 ' : case ' 7 ' :
{
// Octal escape: \142
char ch = 0 ;
2016-10-20 14:53:23 +00:00
while ( SPos [ 0 ] > = ' 0 ' & & SPos [ 0 ] < = ' 7 ' )
2010-03-28 18:58:01 +00:00
{
2012-01-24 17:35:21 +00:00
ch * = 8 ;
2016-10-20 14:53:23 +00:00
ch + = * SPos + + - ' 0 ' ;
2010-03-28 18:58:01 +00:00
}
2012-01-24 17:35:21 +00:00
strbuf . push_back ( ch ) ;
break ;
}
default :
{
// just insert "\"
strbuf . push_back ( ' \\ ' ) ;
// show warning
2017-02-13 13:53:17 +00:00
Warn ( C4AulWarningId : : invalid_escape_sequence , * ( SPos + 1 ) ) ;
2012-01-24 17:35:21 +00:00
}
}
else if ( C = = 0 | | C = = 10 | | C = = 13 ) // line break / feed
2015-12-07 14:15:49 +00:00
throw C4AulParseError ( this , " string not closed " ) ;
2012-01-24 17:35:21 +00:00
else
// copy character
strbuf . push_back ( C ) ;
2010-03-28 18:58:01 +00:00
}
2012-01-24 17:35:21 +00:00
+ + SPos ;
cStr = Strings . RegString ( StdStrBuf ( strbuf . data ( ) , strbuf . size ( ) ) ) ;
// hold onto string, ClearToken will deref it
cStr - > IncRef ( ) ;
return ATT_STRING ;
}
else if ( C = = ' [ ' ) return ATT_BOPEN2 ; // "["
else if ( C = = ' ] ' ) return ATT_BCLOSE2 ; // "]"
else if ( C = = ' . ' & & * SPos = = ' . ' & & * ( SPos + 1 ) = = ' . ' )
{ SPos + = 2 ; return ATT_LDOTS ; } // "..."
else if ( C = = ' . ' ) return ATT_DOT ; // "."
else if ( C = = ' : ' ) return ATT_COLON ; // ":"
else
{
// show appropriate error message
if ( C > = ' ! ' & & C < = ' ~ ' )
2015-12-07 14:15:49 +00:00
throw C4AulParseError ( this , FormatString ( " unexpected character '%c' found " , C ) . getData ( ) ) ;
2012-01-24 17:35:21 +00:00
else
2017-05-03 18:28:00 +00:00
throw C4AulParseError ( this , FormatString ( R " (unexpected character \ x%x found) " , (int)(unsigned char) C).getData()) ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
static const char * GetTTName ( C4AulBCCType e )
{
2010-03-28 18:58:01 +00:00
switch ( e )
2009-05-08 13:28:41 +00:00
{
2010-04-07 13:04:19 +00:00
case AB_ARRAYA : return " ARRAYA " ; // array access
case AB_ARRAYA_SET : return " ARRAYA_SET " ; // setter
2010-08-14 23:53:18 +00:00
case AB_PROP : return " PROP " ;
case AB_PROP_SET : return " PROP_SET " ;
2010-04-05 15:41:36 +00:00
case AB_ARRAY_SLICE : return " ARRAY_SLICE " ;
2010-08-03 20:47:45 +00:00
case AB_ARRAY_SLICE_SET : return " ARRAY_SLICE_SET " ;
2011-05-02 19:53:58 +00:00
case AB_STACK_SET : return " STACK_SET " ;
2010-04-07 13:04:19 +00:00
case AB_LOCALN : return " LOCALN " ; // a named local
case AB_LOCALN_SET : return " LOCALN_SET " ;
case AB_GLOBALN : return " GLOBALN " ; // a named global
case AB_GLOBALN_SET : return " GLOBALN_SET " ;
case AB_PAR : return " PAR " ; // Par statement
2012-06-18 02:07:36 +00:00
case AB_THIS : return " THIS " ;
2010-03-28 18:58:01 +00:00
case AB_FUNC : return " FUNC " ; // function
2009-05-08 13:28:41 +00:00
// prefix
2010-04-07 13:04:19 +00:00
case AB_Inc : return " Inc " ; // ++
case AB_Dec : return " Dec " ; // --
2010-03-28 18:58:01 +00:00
case AB_BitNot : return " BitNot " ; // ~
case AB_Not : return " Not " ; // !
case AB_Neg : return " Neg " ; // -
2009-05-08 13:28:41 +00:00
// postfix
2010-03-28 18:58:01 +00:00
case AB_Pow : return " Pow " ; // **
case AB_Div : return " Div " ; // /
case AB_Mul : return " Mul " ; // *
case AB_Mod : return " Mod " ; // %
case AB_Sub : return " Sub " ; // -
case AB_Sum : return " Sum " ; // +
case AB_LeftShift : return " LeftShift " ; // <<
case AB_RightShift : return " RightShift " ; // >>
case AB_LessThan : return " LessThan " ; // <
case AB_LessThanEqual : return " LessThanEqual " ; // <=
case AB_GreaterThan : return " GreaterThan " ; // >
case AB_GreaterThanEqual : return " GreaterThanEqual " ; // >=
case AB_Equal : return " Equal " ; // ==
case AB_NotEqual : return " NotEqual " ; // !=
case AB_BitAnd : return " BitAnd " ; // &
case AB_BitXOr : return " BitXOr " ; // ^
case AB_BitOr : return " BitOr " ; // |
case AB_CALL : return " CALL " ; // direct object call
case AB_CALLFS : return " CALLFS " ; // failsafe direct call
case AB_STACK : return " STACK " ; // push nulls / pop
case AB_INT : return " INT " ; // constant: int
2010-10-25 21:48:08 +00:00
case AB_BOOL : return " BOOL " ; // constant: bool
2010-03-28 18:58:01 +00:00
case AB_STRING : return " STRING " ; // constant: string
2010-09-09 00:18:19 +00:00
case AB_CPROPLIST : return " CPROPLIST " ; // constant: proplist
case AB_CARRAY : return " CARRAY " ; // constant: array
2011-09-24 23:20:18 +00:00
case AB_CFUNCTION : return " CFUNCTION " ; // constant: function
2010-03-28 18:58:01 +00:00
case AB_NIL : return " NIL " ; // constant: nil
2011-04-09 19:35:16 +00:00
case AB_NEW_ARRAY : return " NEW_ARRAY " ; // semi-constant: array
case AB_DUP : return " DUP " ; // duplicate value from stack
2016-01-16 22:03:40 +00:00
case AB_DUP_CONTEXT : return " AB_DUP_CONTEXT " ; // duplicate value from stack of parent function
2011-04-09 19:35:16 +00:00
case AB_NEW_PROPLIST : return " NEW_PROPLIST " ; // create a new proplist
2011-05-02 19:53:58 +00:00
case AB_POP_TO : return " POP_TO " ; // initialization of named var
2010-03-28 18:58:01 +00:00
case AB_JUMP : return " JUMP " ; // jump
2009-05-08 13:28:41 +00:00
case AB_JUMPAND : return " JUMPAND " ;
case AB_JUMPOR : return " JUMPOR " ;
2012-04-15 13:44:01 +00:00
case AB_JUMPNNIL : return " JUMPNNIL " ; // nil-coalescing operator ("??")
2010-03-28 18:58:01 +00:00
case AB_CONDN : return " CONDN " ; // conditional jump (negated, pops stack)
case AB_COND : return " COND " ; // conditional jump (pops stack)
2009-05-08 13:28:41 +00:00
case AB_FOREACH_NEXT : return " FOREACH_NEXT " ; // foreach: next element
2010-03-28 18:58:01 +00:00
case AB_RETURN : return " RETURN " ; // return statement
case AB_ERR : return " ERR " ; // parse error at this position
2010-10-25 21:48:08 +00:00
case AB_DEBUG : return " DEBUG " ; // debug break
2010-03-28 18:58:01 +00:00
case AB_EOFN : return " EOFN " ; // end of function
2009-05-08 13:28:41 +00:00
}
2016-04-30 00:08:03 +00:00
assert ( false ) ; return " UNKNOWN " ;
2009-05-08 13:28:41 +00:00
}
2016-04-24 21:23:09 +00:00
void C4AulScriptFunc : : DumpByteCode ( )
2016-01-17 01:38:26 +00:00
{
2016-04-24 21:23:09 +00:00
if ( DEBUG_BYTECODE_DUMP )
2016-01-17 01:38:26 +00:00
{
2016-04-24 21:23:09 +00:00
fprintf ( stderr , " %s: \n " , GetName ( ) ) ;
2016-01-17 01:38:26 +00:00
std : : map < C4AulBCC * , int > labels ;
int labeln = 0 ;
2016-04-30 00:08:03 +00:00
for ( auto & bcc : Code )
2016-01-17 01:38:26 +00:00
{
2016-04-30 00:08:03 +00:00
switch ( bcc . bccType )
2016-01-17 01:38:26 +00:00
{
case AB_JUMP : case AB_JUMPAND : case AB_JUMPOR : case AB_JUMPNNIL : case AB_CONDN : case AB_COND :
2016-04-30 00:08:03 +00:00
labels [ & bcc + bcc . Par . i ] = + + labeln ; break ;
2016-01-17 01:38:26 +00:00
default : break ;
}
}
2016-04-30 00:08:03 +00:00
for ( auto & bcc : Code )
2016-01-17 01:38:26 +00:00
{
2016-04-30 00:08:03 +00:00
C4AulBCCType eType = bcc . bccType ;
if ( labels . find ( & bcc ) ! = labels . end ( ) )
fprintf ( stderr , " %d: \n " , labels [ & bcc ] ) ;
2016-05-10 12:10:07 +00:00
fprintf ( stderr , " \t %d \t %-20s " , GetLineOfCode ( & bcc ) , GetTTName ( eType ) ) ;
2016-01-17 01:38:26 +00:00
switch ( eType )
{
case AB_FUNC :
2016-04-30 00:08:03 +00:00
fprintf ( stderr , " \t %s \n " , bcc . Par . f - > GetFullName ( ) . getData ( ) ) ; break ;
2016-04-07 17:35:45 +00:00
case AB_ERR :
2016-04-30 00:08:03 +00:00
if ( bcc . Par . s )
2016-01-17 01:38:26 +00:00
case AB_CALL : case AB_CALLFS : case AB_LOCALN : case AB_LOCALN_SET : case AB_PROP : case AB_PROP_SET :
2016-04-30 00:08:03 +00:00
fprintf ( stderr , " \t %s \n " , bcc . Par . s - > GetCStr ( ) ) ; break ;
2016-01-17 01:38:26 +00:00
case AB_STRING :
{
2016-04-30 00:08:03 +00:00
const StdStrBuf & s = bcc . Par . s - > GetData ( ) ;
2016-01-17 01:38:26 +00:00
std : : string es ;
std : : for_each ( s . getData ( ) , s . getData ( ) + s . getLength ( ) , [ & es ] ( char c ) {
if ( std : : isgraph ( ( unsigned char ) c ) )
{
es + = c ;
}
else
{
switch ( c )
{
2017-05-03 18:28:00 +00:00
case ' \' ' : es . append ( R " ( \' ) " ) ; break ;
case ' \" ' : es . append ( R " ( \" ) " ) ; break ;
case ' \\ ' : es . append ( R " ( \\ ) " ) ; break ;
case ' \a ' : es . append ( R " ( \a ) " ) ; break ;
case ' \b ' : es . append ( R " ( \b ) " ) ; break ;
case ' \f ' : es . append ( R " ( \f ) " ) ; break ;
case ' \n ' : es . append ( R " ( \n ) " ) ; break ;
case ' \r ' : es . append ( R " ( \r ) " ) ; break ;
case ' \t ' : es . append ( R " ( \t ) " ) ; break ;
case ' \v ' : es . append ( R " ( \v ) " ) ; break ;
2016-01-17 01:38:26 +00:00
default :
{
std : : stringstream hex ;
2017-05-03 18:28:00 +00:00
hex < < R " ( \ x) " < < std : : hex < < std : : setw ( 2 ) < < std : : setfill ( ' 0 ' ) < < static_cast < int > ( ( unsigned char ) c ) ;
2016-01-17 01:38:26 +00:00
es . append ( hex . str ( ) ) ;
break ;
}
}
}
} ) ;
fprintf ( stderr , " \t \" %s \" \n " , es . c_str ( ) ) ; break ;
}
case AB_DEBUG : case AB_NIL : case AB_RETURN :
case AB_PAR : case AB_THIS :
case AB_ARRAYA : case AB_ARRAYA_SET : case AB_ARRAY_SLICE : case AB_ARRAY_SLICE_SET :
2016-04-07 17:35:45 +00:00
case AB_EOFN :
2016-04-30 00:08:03 +00:00
assert ( ! bcc . Par . X ) ; fprintf ( stderr , " \n " ) ; break ;
2016-01-17 01:38:26 +00:00
case AB_CARRAY :
2016-04-30 00:08:03 +00:00
fprintf ( stderr , " \t %s \n " , C4VArray ( bcc . Par . a ) . GetDataString ( ) . getData ( ) ) ; break ;
2016-01-17 01:38:26 +00:00
case AB_CPROPLIST :
2016-04-30 00:08:03 +00:00
fprintf ( stderr , " \t %s \n " , C4VPropList ( bcc . Par . p ) . GetDataString ( ) . getData ( ) ) ; break ;
2016-01-17 01:38:26 +00:00
case AB_JUMP : case AB_JUMPAND : case AB_JUMPOR : case AB_JUMPNNIL : case AB_CONDN : case AB_COND :
2016-04-30 00:08:03 +00:00
fprintf ( stderr , " \t % -d \n " , labels [ & bcc + bcc . Par . i ] ) ; break ;
2016-01-17 01:38:26 +00:00
default :
2016-04-30 00:08:03 +00:00
fprintf ( stderr , " \t % -d \n " , bcc . Par . i ) ; break ;
2016-01-17 01:38:26 +00:00
}
}
}
}
2011-10-13 16:01:02 +00:00
bool C4ScriptHost : : Preparse ( )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// handle easiest case first
2009-08-15 18:50:32 +00:00
if ( State < ASS_NONE ) return false ;
2009-05-08 13:28:41 +00:00
// clear stuff
2011-02-25 23:48:19 +00:00
Includes . clear ( ) ; Appends . clear ( ) ;
2011-10-04 20:16:27 +00:00
2012-05-27 22:31:55 +00:00
GetPropList ( ) - > C4PropList : : Clear ( ) ;
GetPropList ( ) - > SetProperty ( P_Prototype , C4VPropList ( Engine - > GetPropList ( ) ) ) ;
LocalValues . Clear ( ) ;
2011-10-04 20:16:27 +00:00
2013-03-18 23:35:00 +00:00
// Add any engine functions specific to this script
AddEngineFunctions ( ) ;
2017-02-13 13:53:17 +00:00
// Insert default warnings
assert ( enabledWarnings . empty ( ) ) ;
auto & warnings = enabledWarnings [ Script . getData ( ) ] ;
# define DIAG(id, text, enabled) warnings.set(static_cast<size_t>(C4AulWarningId::id), enabled);
# include "C4AulWarnings.h"
# undef DIAG
2016-05-12 17:43:48 +00:00
C4AulParse parser ( this ) ;
ast = parser . Parse_Script ( this ) ;
C4AulCompiler : : Preparse ( this , this , ast . get ( ) ) ;
2009-05-08 13:28:41 +00:00
// #include will have to be resolved now...
IncludesResolved = false ;
2012-05-27 22:31:55 +00:00
// Parse will write the properties back after the ones from included scripts
GetPropList ( ) - > Properties . Swap ( & LocalValues ) ;
2009-05-08 13:28:41 +00:00
// return success
2016-01-03 02:38:05 +00:00
this - > State = ASS_PREPARSED ;
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-04-29 10:11:26 +00:00
static const char * GetTokenName ( C4AulTokenType TokenType )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
switch ( TokenType )
2010-03-28 18:58:01 +00:00
{
case ATT_INVALID : return " invalid token " ;
case ATT_DIR : return " directive " ;
case ATT_IDTF : return " identifier " ;
case ATT_INT : return " integer constant " ;
case ATT_STRING : return " string constant " ;
2011-02-25 17:12:36 +00:00
case ATT_DOT : return " '.' " ;
2010-03-28 18:58:01 +00:00
case ATT_COMMA : return " ',' " ;
case ATT_COLON : return " ':' " ;
case ATT_SCOLON : return " ';' " ;
case ATT_BOPEN : return " '(' " ;
case ATT_BCLOSE : return " ')' " ;
case ATT_BOPEN2 : return " '[' " ;
case ATT_BCLOSE2 : return " ']' " ;
case ATT_BLOPEN : return " '{' " ;
case ATT_BLCLOSE : return " '}' " ;
case ATT_CALL : return " '->' " ;
2011-02-19 21:18:27 +00:00
case ATT_CALLFS : return " '->~' " ;
2010-03-28 18:58:01 +00:00
case ATT_LDOTS : return " '...' " ;
2011-02-25 17:12:36 +00:00
case ATT_SET : return " '=' " ;
2010-03-28 18:58:01 +00:00
case ATT_OPERATOR : return " operator " ;
case ATT_EOF : return " end of file " ;
default : return " unrecognized token " ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-01-02 02:47:19 +00:00
void C4AulParse : : Shift ( )
2010-03-28 18:58:01 +00:00
{
2016-01-02 02:47:19 +00:00
TokenType = GetNextToken ( ) ;
2010-03-28 18:58:01 +00:00
}
2013-09-06 23:54:00 +00:00
void C4AulParse : : Check ( C4AulTokenType RefTokenType , const char * Expected )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if ( TokenType ! = RefTokenType )
2013-09-06 23:54:00 +00:00
UnexpectedToken ( Expected ? Expected : GetTokenName ( RefTokenType ) ) ;
}
void C4AulParse : : Match ( C4AulTokenType RefTokenType , const char * Expected )
{
Check ( RefTokenType , Expected ) ;
2009-05-08 13:28:41 +00:00
Shift ( ) ;
2010-03-28 18:58:01 +00:00
}
2012-04-12 19:03:14 +00:00
void C4AulParse : : UnexpectedToken ( const char * Expected )
2010-03-28 18:58:01 +00:00
{
2015-12-07 14:15:49 +00:00
throw C4AulParseError ( this , FormatString ( " %s expected, but found %s " , Expected , GetTokenName ( TokenType ) ) . getData ( ) ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2017-02-13 13:53:17 +00:00
void C4AulParse : : Parse_WarningPragma ( )
{
assert ( SEqual2 ( TokenSPos , C4AUL_Warning ) ) ;
assert ( std : : isspace ( TokenSPos [ sizeof ( C4AUL_Warning ) - 1 ] ) ) ;
// Read parameters in to string buffer. The sizeof() includes the terminating \0, but
// that's okay because we need to skip (at least) one whitespace character anyway.
std : : string line ( TokenSPos + sizeof ( C4AUL_Warning ) , SPos ) ;
auto end = line . end ( ) ;
auto cursor = std : : find_if_not ( begin ( line ) , end , IsWhiteSpace ) ;
if ( cursor = = end )
throw C4AulParseError ( this , " ' " C4Aul_Warning_enable " ' or ' " C4Aul_Warning_disable " ' expected, but found end of line " ) ;
// Split directive on whitespace
auto start = cursor ;
cursor = std : : find_if ( start , end , IsWhiteSpace ) ;
bool enable_warning = false ;
if ( std : : equal ( start , cursor , C4Aul_Warning_enable ) )
{
enable_warning = true ;
}
else if ( std : : equal ( start , cursor , C4Aul_Warning_disable ) )
{
enable_warning = false ;
}
else
{
throw C4AulParseError ( this , FormatString ( " ' " C4Aul_Warning_enable " ' or ' " C4Aul_Warning_disable " ' expected, but found '%s' " , std : : string ( start , cursor ) . c_str ( ) ) . getData ( ) ) ;
}
cursor = std : : find_if_not ( cursor , end , IsWhiteSpace ) ;
if ( cursor = = end )
{
// enable or disable all warnings
# define DIAG(id, text, enabled) pOrgScript->EnableWarning(TokenSPos, C4AulWarningId::id, enable_warning);
# include "C4AulWarnings.h"
# undef DIAG
return ;
}
// enable or disable specific warnings
static const std : : map < std : : string , C4AulWarningId > warnings {
# define DIAG(id, text, enabled) std::make_pair(#id, C4AulWarningId::id),
# include "C4AulWarnings.h"
# undef DIAG
} ;
while ( cursor ! = end )
{
start = std : : find_if_not ( cursor , end , IsWhiteSpace ) ;
cursor = std : : find_if ( start , end , IsWhiteSpace ) ;
auto entry = warnings . find ( std : : string ( start , cursor ) ) ;
if ( entry ! = warnings . end ( ) )
{
pOrgScript - > EnableWarning ( TokenSPos , entry - > second , enable_warning ) ;
}
}
}
2016-10-18 12:47:58 +00:00
void C4AulScriptFunc : : ParseDirectExecFunc ( C4AulScriptEngine * Engine , C4AulScriptContext * context )
2010-03-28 18:58:01 +00:00
{
2012-10-21 16:14:32 +00:00
ClearCode ( ) ;
2009-05-08 13:28:41 +00:00
// parse
2016-01-15 21:31:52 +00:00
C4AulParse state ( this , context , Engine ) ;
2016-10-18 12:47:58 +00:00
auto func = state . Parse_DirectExec ( Script , true ) ;
2016-05-12 17:43:48 +00:00
C4AulCompiler : : Compile ( this , func . get ( ) ) ;
2013-01-12 18:13:27 +00:00
}
2016-10-18 12:47:58 +00:00
void C4AulScriptFunc : : ParseDirectExecStatement ( C4AulScriptEngine * Engine , C4AulScriptContext * context )
{
ClearCode ( ) ;
// parse
C4AulParse state ( this , context , Engine ) ;
auto func = state . Parse_DirectExec ( Script , false ) ;
C4AulCompiler : : Compile ( this , func . get ( ) ) ;
}
std : : unique_ptr < : : aul : : ast : : FunctionDecl > C4AulParse : : Parse_DirectExec ( const char * code , bool whole_function )
2013-01-12 18:13:27 +00:00
{
2009-05-08 13:28:41 +00:00
// get first token
2013-01-12 18:13:27 +00:00
Shift ( ) ;
2016-05-12 17:43:48 +00:00
// Synthesize a wrapping function which we can call
2016-10-18 12:47:58 +00:00
std : : unique_ptr < : : aul : : ast : : FunctionDecl > func ;
if ( whole_function )
{
func = Parse_ToplevelFunctionDecl ( ) ;
}
else
{
auto expr = Parse_Expression ( ) ;
func = std : : make_unique < : : aul : : ast : : FunctionDecl > ( " $internal$eval " ) ;
func - > body = std : : make_unique < : : aul : : ast : : Block > ( ) ;
func - > body - > children . push_back ( std : : make_unique < : : aul : : ast : : Return > ( std : : move ( expr ) ) ) ;
}
Match ( ATT_EOF ) ;
2016-05-12 17:43:48 +00:00
return func ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-05-12 17:43:48 +00:00
std : : unique_ptr < : : aul : : ast : : Script > C4AulParse : : Parse_Script ( C4ScriptHost * scripthost )
2010-03-28 18:58:01 +00:00
{
2013-01-12 18:13:27 +00:00
pOrgScript = scripthost ;
SPos = pOrgScript - > Script . getData ( ) ;
2009-05-08 13:28:41 +00:00
const char * SPos0 = SPos ;
2016-07-25 12:57:54 +00:00
bool first_error = true ;
2016-05-12 17:43:48 +00:00
auto script = : : aul : : ast : : Script : : New ( SPos0 ) ;
2011-10-23 23:00:29 +00:00
while ( true ) try
2010-09-09 00:18:19 +00:00
{
// Go to the next token if the current token could not be processed or no token has yet been parsed
if ( SPos = = SPos0 )
{
Shift ( ) ;
}
SPos0 = SPos ;
switch ( TokenType )
2009-05-08 13:28:41 +00:00
{
2012-09-04 00:11:16 +00:00
case ATT_DIR :
// check for include statement
if ( SEqual ( Idtf , C4AUL_Include ) )
2010-03-28 18:58:01 +00:00
{
2012-09-04 00:11:16 +00:00
Shift ( ) ;
// get id of script to include
2013-09-06 23:54:00 +00:00
Check ( ATT_IDTF , " script name " ) ;
2016-05-12 17:43:48 +00:00
script - > declarations . push_back ( : : aul : : ast : : IncludePragma : : New ( TokenSPos , Idtf ) ) ;
2012-09-04 00:11:16 +00:00
Shift ( ) ;
}
else if ( SEqual ( Idtf , C4AUL_Append ) )
{
if ( pOrgScript - > GetPropList ( ) - > GetDef ( ) )
2015-12-07 14:15:49 +00:00
throw C4AulParseError ( this , " #appendto in a Definition " ) ;
2016-01-02 02:47:19 +00:00
Shift ( ) ;
2016-05-12 17:43:48 +00:00
// get id of script to include/append
switch ( TokenType )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
case ATT_IDTF :
script - > declarations . push_back ( : : aul : : ast : : AppendtoPragma : : New ( TokenSPos , Idtf ) ) ;
break ;
case ATT_OPERATOR :
if ( SEqual ( C4ScriptOpMap [ cInt ] . Identifier , " * " ) )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
script - > declarations . push_back ( : : aul : : ast : : AppendtoPragma : : New ( TokenSPos ) ) ;
2012-09-04 00:11:16 +00:00
break ;
2010-03-28 18:58:01 +00:00
}
2016-05-12 17:43:48 +00:00
//fallthrough
default :
// -> ID expected
UnexpectedToken ( " identifier or '*' " ) ;
2010-03-28 18:58:01 +00:00
}
2012-09-04 00:11:16 +00:00
Shift ( ) ;
}
else
// -> unknown directive
2016-04-27 09:55:40 +00:00
Error ( " unknown directive: %s " , Idtf ) ;
2012-09-04 00:11:16 +00:00
break ;
case ATT_IDTF :
// need a keyword here to avoid parsing random function contents
// after a syntax error in a function
// check for object-local variable definition (local)
2016-05-12 17:43:48 +00:00
if ( SEqual ( Idtf , C4AUL_LocalNamed ) | | SEqual ( Idtf , C4AUL_GlobalNamed ) )
2012-09-04 00:11:16 +00:00
{
2016-05-12 17:43:48 +00:00
script - > declarations . push_back ( Parse_Var ( ) ) ;
2012-09-04 00:11:16 +00:00
Match ( ATT_SCOLON ) ;
2010-03-28 18:58:01 +00:00
}
2012-09-04 00:11:16 +00:00
// check for variable definition (static)
else
2016-05-12 17:43:48 +00:00
script - > declarations . push_back ( Parse_ToplevelFunctionDecl ( ) ) ;
2012-09-04 00:11:16 +00:00
break ;
case ATT_EOF :
2016-05-12 17:43:48 +00:00
return script ;
2012-09-04 00:11:16 +00:00
default :
UnexpectedToken ( " declaration " ) ;
2009-05-08 13:28:41 +00:00
}
2016-07-25 12:57:54 +00:00
first_error = true ;
2010-09-09 00:18:19 +00:00
}
2015-12-07 14:15:49 +00:00
catch ( C4AulError & err )
2010-09-09 00:18:19 +00:00
{
2016-07-25 12:57:54 +00:00
if ( first_error )
2012-01-26 23:42:41 +00:00
{
2014-04-20 13:52:09 +00:00
+ + Engine - > errCnt ;
2016-07-25 12:57:54 +00:00
: : ScriptEngine . ErrorHandler - > OnError ( err . what ( ) ) ;
2012-01-26 23:42:41 +00:00
}
2016-07-25 12:57:54 +00:00
first_error = false ;
2010-09-09 00:18:19 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-05-12 17:43:48 +00:00
std : : unique_ptr < : : aul : : ast : : FunctionDecl > C4AulParse : : Parse_ToplevelFunctionDecl ( )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
const char * NodeStart = TokenSPos ;
2015-03-25 21:23:42 +00:00
bool is_global = SEqual ( Idtf , C4AUL_Global ) ;
// skip access modifier
if ( SEqual ( Idtf , C4AUL_Private ) | |
SEqual ( Idtf , C4AUL_Protected ) | |
SEqual ( Idtf , C4AUL_Public ) | |
SEqual ( Idtf , C4AUL_Global ) )
{
Shift ( ) ;
}
2009-05-08 13:28:41 +00:00
// check for func declaration
2011-04-17 23:50:33 +00:00
if ( ! SEqual ( Idtf , C4AUL_Func ) )
2016-04-27 09:55:40 +00:00
Error ( " Declaration expected, but found identifier: %s " , Idtf ) ;
2011-04-17 23:50:33 +00:00
Shift ( ) ;
// get next token, must be func name
2013-09-06 23:54:00 +00:00
Check ( ATT_IDTF , " function name " ) ;
2016-05-12 17:43:48 +00:00
auto func = : : aul : : ast : : FunctionDecl : : New ( NodeStart , Idtf ) ;
func - > is_global = is_global ;
2014-04-19 21:34:31 +00:00
Shift ( ) ;
2016-05-12 17:43:48 +00:00
Parse_Function ( func . get ( ) ) ;
return func ;
2014-04-19 21:34:31 +00:00
}
2016-05-12 17:43:48 +00:00
void C4AulParse : : Parse_Function ( : : aul : : ast : : Function * func )
2014-04-19 21:34:31 +00:00
{
2013-09-06 23:54:00 +00:00
Match ( ATT_BOPEN ) ;
2011-04-17 23:50:33 +00:00
// get pars
2012-01-26 23:42:41 +00:00
while ( TokenType ! = ATT_BCLOSE )
2011-04-17 23:50:33 +00:00
{
// too many parameters?
2016-05-12 17:43:48 +00:00
if ( func - > params . size ( ) > = C4AUL_MAX_Par )
2015-12-07 14:15:49 +00:00
throw C4AulParseError ( this , " 'func' parameter list: too many parameters (max 10) " ) ;
2014-05-04 14:58:01 +00:00
if ( TokenType = = ATT_LDOTS )
{
2016-05-12 17:43:48 +00:00
func - > has_unnamed_params = true ;
2014-05-04 14:58:01 +00:00
Shift ( ) ;
// don't allow any more parameters after ellipsis
break ;
}
2011-04-17 23:50:33 +00:00
// must be a name or type now
2014-05-04 14:58:01 +00:00
Check ( ATT_IDTF , " parameter, '...', or ')' " ) ;
2011-04-17 23:50:33 +00:00
// type identifier?
2011-12-22 20:57:17 +00:00
C4V_Type t = C4V_Any ;
if ( SEqual ( Idtf , C4AUL_TypeInt ) ) { t = C4V_Int ; Shift ( ) ; }
else if ( SEqual ( Idtf , C4AUL_TypeBool ) ) { t = C4V_Bool ; Shift ( ) ; }
else if ( SEqual ( Idtf , C4AUL_TypeC4ID ) ) { t = C4V_Def ; Shift ( ) ; }
else if ( SEqual ( Idtf , C4AUL_TypeDef ) ) { t = C4V_Def ; Shift ( ) ; }
else if ( SEqual ( Idtf , C4AUL_TypeEffect ) ) { t = C4V_Effect ; Shift ( ) ; }
else if ( SEqual ( Idtf , C4AUL_TypeC4Object ) ) { t = C4V_Object ; Shift ( ) ; }
else if ( SEqual ( Idtf , C4AUL_TypePropList ) ) { t = C4V_PropList ; Shift ( ) ; }
else if ( SEqual ( Idtf , C4AUL_TypeString ) ) { t = C4V_String ; Shift ( ) ; }
else if ( SEqual ( Idtf , C4AUL_TypeArray ) ) { t = C4V_Array ; Shift ( ) ; }
else if ( SEqual ( Idtf , C4AUL_TypeFunction ) ) { t = C4V_Function ; Shift ( ) ; }
2014-04-19 21:34:31 +00:00
// a parameter name which matched a type name?
2016-05-12 17:43:48 +00:00
std : : string par_name ;
2011-09-24 16:37:28 +00:00
if ( TokenType = = ATT_BCLOSE | | TokenType = = ATT_COMMA )
{
2016-05-12 17:43:48 +00:00
par_name = Idtf ;
2017-02-13 13:53:17 +00:00
Warn ( C4AulWarningId : : type_name_used_as_par_name , Idtf ) ;
2011-09-24 16:37:28 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2014-04-19 21:34:31 +00:00
Check ( ATT_IDTF , " parameter name " ) ;
2016-05-12 17:43:48 +00:00
par_name = Idtf ;
2011-04-17 23:50:33 +00:00
Shift ( ) ;
2010-03-28 18:58:01 +00:00
}
2016-05-12 17:43:48 +00:00
func - > params . emplace_back ( par_name , t ) ;
2011-04-17 23:50:33 +00:00
// end of params?
if ( TokenType = = ATT_BCLOSE )
2010-03-28 18:58:01 +00:00
{
2011-04-17 23:50:33 +00:00
break ;
2010-03-28 18:58:01 +00:00
}
2011-04-17 23:50:33 +00:00
// must be a comma now
2013-09-06 23:54:00 +00:00
Match ( ATT_COMMA , " ',' or ')' " ) ;
2009-05-08 13:28:41 +00:00
}
2012-01-26 23:42:41 +00:00
Match ( ATT_BCLOSE ) ;
2016-05-12 17:43:48 +00:00
func - > body = Parse_Block ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-05-12 17:43:48 +00:00
std : : unique_ptr < : : aul : : ast : : Block > C4AulParse : : Parse_Block ( )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
auto block = : : aul : : ast : : Block : : New ( TokenSPos ) ;
2009-05-08 13:28:41 +00:00
Match ( ATT_BLOPEN ) ;
2011-12-22 20:57:17 +00:00
while ( TokenType ! = ATT_BLCLOSE )
{
2016-05-12 17:43:48 +00:00
block - > children . push_back ( Parse_Statement ( ) ) ;
2011-12-22 20:57:17 +00:00
}
Shift ( ) ;
2016-05-12 17:43:48 +00:00
return block ;
2010-03-28 18:58:01 +00:00
}
2009-08-26 14:59:00 +00:00
2016-05-12 17:43:48 +00:00
std : : unique_ptr < : : aul : : ast : : Stmt > C4AulParse : : Parse_Statement ( )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
const char * NodeStart = TokenSPos ;
std : : unique_ptr < : : aul : : ast : : Stmt > stmt ;
2009-05-08 13:28:41 +00:00
switch ( TokenType )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// do we have a block start?
2010-03-28 18:58:01 +00:00
case ATT_BLOPEN :
2016-05-12 17:43:48 +00:00
return Parse_Block ( ) ;
2010-03-28 18:58:01 +00:00
case ATT_BOPEN :
case ATT_BOPEN2 :
2010-04-07 13:04:19 +00:00
case ATT_SET :
2010-03-28 18:58:01 +00:00
case ATT_OPERATOR :
2011-02-21 23:07:08 +00:00
case ATT_INT :
case ATT_STRING :
2016-05-12 17:43:48 +00:00
{
stmt = Parse_Expression ( ) ;
2010-03-28 18:58:01 +00:00
Match ( ATT_SCOLON ) ;
2016-05-12 17:43:48 +00:00
return stmt ;
}
2010-04-01 21:08:06 +00:00
// additional function separator
2010-03-28 18:58:01 +00:00
case ATT_SCOLON :
Shift ( ) ;
2016-05-12 17:43:48 +00:00
return : : aul : : ast : : Noop : : New ( NodeStart ) ;
2010-03-28 18:58:01 +00:00
case ATT_IDTF :
2016-05-12 17:43:48 +00:00
// check for variable definition
if ( SEqual ( Idtf , C4AUL_VarNamed ) | | SEqual ( Idtf , C4AUL_LocalNamed ) | | SEqual ( Idtf , C4AUL_GlobalNamed ) )
stmt = Parse_Var ( ) ;
2010-03-28 18:58:01 +00:00
// check new-form func begin
else if ( SEqual ( Idtf , C4AUL_Func ) | |
SEqual ( Idtf , C4AUL_Private ) | |
SEqual ( Idtf , C4AUL_Protected ) | |
SEqual ( Idtf , C4AUL_Public ) | |
SEqual ( Idtf , C4AUL_Global ) )
{
2015-12-07 14:15:49 +00:00
throw C4AulParseError ( this , " unexpected end of function " ) ;
2010-03-28 18:58:01 +00:00
}
// get function by identifier: first check special functions
else if ( SEqual ( Idtf , C4AUL_If ) ) // if
{
2016-05-12 17:43:48 +00:00
return Parse_If ( ) ;
2010-03-28 18:58:01 +00:00
}
else if ( SEqual ( Idtf , C4AUL_Else ) ) // else
{
2015-12-07 14:15:49 +00:00
throw C4AulParseError ( this , " misplaced 'else' " ) ;
2010-03-28 18:58:01 +00:00
}
else if ( SEqual ( Idtf , C4AUL_Do ) ) // while
{
2016-05-12 17:43:48 +00:00
stmt = Parse_DoWhile ( ) ;
2010-03-28 18:58:01 +00:00
}
else if ( SEqual ( Idtf , C4AUL_While ) ) // while
{
2016-05-12 17:43:48 +00:00
return Parse_While ( ) ;
2010-03-28 18:58:01 +00:00
}
else if ( SEqual ( Idtf , C4AUL_For ) ) // for
{
2016-05-12 17:43:48 +00:00
PushParsePos ( ) ;
2010-03-28 18:58:01 +00:00
Shift ( ) ;
// Look if it's the for([var] foo in array)-form
// must be followed by a bracket
Match ( ATT_BOPEN ) ;
// optional var
if ( TokenType = = ATT_IDTF & & SEqual ( Idtf , C4AUL_VarNamed ) )
Shift ( ) ;
// variable and "in"
2015-03-25 18:04:04 +00:00
if ( TokenType = = ATT_IDTF
2011-02-19 21:37:42 +00:00
& & GetNextToken ( ) = = ATT_IDTF
2010-03-28 18:58:01 +00:00
& & SEqual ( Idtf , C4AUL_In ) )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// reparse the stuff in the brackets like normal statements
2016-05-12 17:43:48 +00:00
PopParsePos ( ) ;
return Parse_ForEach ( ) ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
else
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// reparse the stuff in the brackets like normal statements
2016-05-12 17:43:48 +00:00
PopParsePos ( ) ;
return Parse_For ( ) ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
else if ( SEqual ( Idtf , C4AUL_Return ) ) // return
{
Shift ( ) ;
if ( TokenType = = ATT_SCOLON )
{
// allow return; without return value (implies nil)
2016-05-12 17:43:48 +00:00
stmt = : : aul : : ast : : Return : : New ( NodeStart , : : aul : : ast : : NilLit : : New ( NodeStart ) ) ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
else
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// return retval;
2016-05-12 17:43:48 +00:00
stmt = : : aul : : ast : : Return : : New ( NodeStart , Parse_Expression ( ) ) ;
2010-03-28 18:58:01 +00:00
}
}
else if ( SEqual ( Idtf , C4AUL_Break ) ) // break
{
Shift ( ) ;
2016-05-12 17:43:48 +00:00
stmt = : : aul : : ast : : Break : : New ( NodeStart ) ;
2010-03-28 18:58:01 +00:00
}
else if ( SEqual ( Idtf , C4AUL_Continue ) ) // continue
{
Shift ( ) ;
2016-05-12 17:43:48 +00:00
stmt = : : aul : : ast : : Continue : : New ( NodeStart ) ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
else
{
2016-05-12 17:43:48 +00:00
stmt = Parse_Expression ( ) ;
2010-03-28 18:58:01 +00:00
}
Match ( ATT_SCOLON ) ;
2016-05-12 17:43:48 +00:00
assert ( stmt ) ;
return stmt ;
2010-03-28 18:58:01 +00:00
default :
UnexpectedToken ( " statement " ) ;
}
}
2009-05-08 13:28:41 +00:00
2016-05-13 11:14:23 +00:00
void C4AulParse : : Parse_CallParams ( : : aul : : ast : : CallExpr * call )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
assert ( call ! = nullptr ) ;
assert ( call - > args . empty ( ) ) ;
2009-05-08 13:28:41 +00:00
// so it's a regular function; force "("
Match ( ATT_BOPEN ) ;
2016-02-01 01:57:32 +00:00
while ( TokenType ! = ATT_BCLOSE ) switch ( TokenType )
2011-05-02 21:52:41 +00:00
{
2012-09-04 00:11:16 +00:00
case ATT_COMMA :
// got no parameter before a ","
2017-03-13 23:45:27 +00:00
Warn ( C4AulWarningId : : empty_parameter_in_call , ( unsigned ) call - > args . size ( ) , call - > callee . c_str ( ) ) ;
2016-05-12 17:43:48 +00:00
call - > args . push_back ( : : aul : : ast : : NilLit : : New ( TokenSPos ) ) ;
2012-09-04 00:11:16 +00:00
Shift ( ) ;
break ;
case ATT_LDOTS :
// functions using ... always take as many parameters as possible
Shift ( ) ;
2016-05-12 17:43:48 +00:00
call - > append_unnamed_pars = true ;
2016-02-01 01:57:32 +00:00
// Do not allow more parameters even if there is space left
Check ( ATT_BCLOSE ) ;
2012-09-04 00:11:16 +00:00
break ;
default :
// get a parameter
2016-05-12 17:43:48 +00:00
call - > args . push_back ( Parse_Expression ( ) ) ;
2012-09-04 00:11:16 +00:00
// end of parameter list?
2016-02-01 01:57:32 +00:00
if ( TokenType ! = ATT_BCLOSE )
Match ( ATT_COMMA , " ',' or ')' " ) ;
2012-09-04 00:11:16 +00:00
break ;
2016-02-01 01:57:32 +00:00
}
Match ( ATT_BCLOSE ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-05-12 17:43:48 +00:00
std : : unique_ptr < : : aul : : ast : : ArrayLit > C4AulParse : : Parse_Array ( )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
auto arr = : : aul : : ast : : ArrayLit : : New ( TokenSPos ) ;
2009-05-08 13:28:41 +00:00
// force "["
Match ( ATT_BOPEN2 ) ;
// Create an array
2015-12-17 15:55:51 +00:00
while ( TokenType ! = ATT_BCLOSE2 )
2012-09-04 00:11:16 +00:00
{
2015-12-17 15:55:51 +00:00
// got no parameter before a ","? then push nil
if ( TokenType = = ATT_COMMA )
2010-03-28 18:58:01 +00:00
{
2017-03-13 23:45:27 +00:00
Warn ( C4AulWarningId : : empty_parameter_in_array , ( unsigned ) arr - > values . size ( ) ) ;
2016-05-12 17:43:48 +00:00
arr - > values . emplace_back ( : : aul : : ast : : NilLit : : New ( TokenSPos ) ) ;
2010-03-28 18:58:01 +00:00
}
2015-12-17 15:55:51 +00:00
else
2016-05-12 17:43:48 +00:00
arr - > values . emplace_back ( Parse_Expression ( ) ) ;
2015-12-17 15:55:51 +00:00
if ( TokenType = = ATT_BCLOSE2 )
2012-09-04 00:11:16 +00:00
break ;
2015-12-17 15:55:51 +00:00
Match ( ATT_COMMA , " ',' or ']' " ) ;
// [] -> size 0, [*,] -> size 2, [*,*,] -> size 3
if ( TokenType = = ATT_BCLOSE2 )
{
2017-03-13 23:45:27 +00:00
Warn ( C4AulWarningId : : empty_parameter_in_array , ( unsigned ) arr - > values . size ( ) ) ;
2016-05-12 17:43:48 +00:00
arr - > values . emplace_back ( : : aul : : ast : : NilLit : : New ( TokenSPos ) ) ;
2010-03-28 18:58:01 +00:00
}
2015-12-17 15:55:51 +00:00
}
Shift ( ) ;
2016-05-12 17:43:48 +00:00
return arr ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-05-12 17:43:48 +00:00
std : : unique_ptr < : : aul : : ast : : ProplistLit > C4AulParse : : Parse_PropList ( )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
auto proplist = : : aul : : ast : : ProplistLit : : New ( TokenSPos ) ;
2011-11-07 00:39:43 +00:00
if ( TokenType = = ATT_IDTF & & SEqual ( Idtf , C4AUL_New ) )
{
Shift ( ) ;
2016-05-12 17:43:48 +00:00
proplist - > values . emplace_back ( Strings . P [ P_Prototype ] . GetCStr ( ) , Parse_Expression ( ) ) ;
2011-11-07 00:39:43 +00:00
}
Match ( ATT_BLOPEN ) ;
2010-09-09 00:18:19 +00:00
while ( TokenType ! = ATT_BLCLOSE )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
std : : string key ;
2009-04-12 00:56:41 +00:00
if ( TokenType = = ATT_IDTF )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
key = Idtf ;
2009-04-12 00:56:41 +00:00
Shift ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-04-12 00:56:41 +00:00
else if ( TokenType = = ATT_STRING )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
key = cStr - > GetCStr ( ) ;
2009-04-12 00:56:41 +00:00
Shift ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-04-12 00:56:41 +00:00
else UnexpectedToken ( " string or identifier " ) ;
2010-04-07 13:04:19 +00:00
if ( TokenType ! = ATT_COLON & & TokenType ! = ATT_SET )
2009-04-12 00:56:41 +00:00
UnexpectedToken ( " ':' or '=' " ) ;
Shift ( ) ;
2016-05-12 17:43:48 +00:00
proplist - > values . emplace_back ( key , Parse_Expression ( ) ) ;
2009-04-12 00:56:41 +00:00
if ( TokenType = = ATT_COMMA )
Shift ( ) ;
else if ( TokenType ! = ATT_BLCLOSE )
UnexpectedToken ( " '}' or ',' " ) ;
}
2010-09-09 00:18:19 +00:00
Shift ( ) ;
2016-05-12 17:43:48 +00:00
return proplist ;
2010-03-28 18:58:01 +00:00
}
2009-04-12 00:56:41 +00:00
2016-05-12 17:43:48 +00:00
std : : unique_ptr < : : aul : : ast : : DoLoop > C4AulParse : : Parse_DoWhile ( )
2011-11-07 00:39:43 +00:00
{
2016-05-12 17:43:48 +00:00
auto loop = : : aul : : ast : : DoLoop : : New ( TokenSPos ) ;
2014-09-28 20:49:19 +00:00
Shift ( ) ;
2016-05-12 17:43:48 +00:00
loop - > body = Parse_Statement ( ) ;
2009-08-28 02:44:53 +00:00
// Execute condition
if ( TokenType ! = ATT_IDTF | | ! SEqual ( Idtf , C4AUL_While ) )
UnexpectedToken ( " 'while' " ) ;
Shift ( ) ;
Match ( ATT_BOPEN ) ;
2016-05-12 17:43:48 +00:00
loop - > cond = Parse_Expression ( ) ;
2009-08-28 02:44:53 +00:00
Match ( ATT_BCLOSE ) ;
2016-05-12 17:43:48 +00:00
return loop ;
2010-03-28 18:58:01 +00:00
}
2009-08-28 02:44:53 +00:00
2016-05-12 17:43:48 +00:00
std : : unique_ptr < : : aul : : ast : : WhileLoop > C4AulParse : : Parse_While ( )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
auto loop = : : aul : : ast : : WhileLoop : : New ( TokenSPos ) ;
2011-10-23 23:00:29 +00:00
Shift ( ) ;
2009-05-08 13:28:41 +00:00
// Execute condition
2009-07-22 23:02:41 +00:00
Match ( ATT_BOPEN ) ;
2016-05-12 17:43:48 +00:00
loop - > cond = Parse_Expression ( ) ;
2009-07-22 23:02:41 +00:00
Match ( ATT_BCLOSE ) ;
2009-05-08 13:28:41 +00:00
// Execute body
2016-05-12 17:43:48 +00:00
loop - > body = Parse_Statement ( ) ;
return loop ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-05-12 17:43:48 +00:00
std : : unique_ptr < : : aul : : ast : : If > C4AulParse : : Parse_If ( )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
auto stmt = : : aul : : ast : : If : : New ( TokenSPos ) ;
2011-10-23 23:00:29 +00:00
Shift ( ) ;
2009-07-22 23:02:41 +00:00
Match ( ATT_BOPEN ) ;
2016-05-12 17:43:48 +00:00
stmt - > cond = Parse_Expression ( ) ;
2009-07-22 23:02:41 +00:00
Match ( ATT_BCLOSE ) ;
2009-05-08 13:28:41 +00:00
// parse controlled statement
2016-05-12 17:43:48 +00:00
stmt - > iftrue = Parse_Statement ( ) ;
2009-05-08 13:28:41 +00:00
if ( TokenType = = ATT_IDTF & & SEqual ( Idtf , C4AUL_Else ) )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Shift ( ) ;
// expect a command now
2016-05-12 17:43:48 +00:00
stmt - > iffalse = Parse_Statement ( ) ;
2010-03-28 18:58:01 +00:00
}
2016-05-12 17:43:48 +00:00
return stmt ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-05-12 17:43:48 +00:00
std : : unique_ptr < : : aul : : ast : : ForLoop > C4AulParse : : Parse_For ( )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
auto loop = : : aul : : ast : : ForLoop : : New ( TokenSPos ) ;
Match ( ATT_IDTF ) ; Match ( ATT_BOPEN ) ;
2009-05-08 13:28:41 +00:00
// Initialization
2010-03-28 18:58:01 +00:00
if ( TokenType = = ATT_IDTF & & SEqual ( Idtf , C4AUL_VarNamed ) )
{
2016-05-12 17:43:48 +00:00
loop - > init = Parse_Var ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if ( TokenType ! = ATT_SCOLON )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
loop - > init = Parse_Expression ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Consume first semicolon
Match ( ATT_SCOLON ) ;
// Condition
if ( TokenType ! = ATT_SCOLON )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
loop - > cond = Parse_Expression ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Consume second semicolon
Match ( ATT_SCOLON ) ;
// Incrementor
if ( TokenType ! = ATT_BCLOSE )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
loop - > incr = Parse_Expression ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Consume closing bracket
Match ( ATT_BCLOSE ) ;
2016-05-12 17:43:48 +00:00
loop - > body = Parse_Statement ( ) ;
return loop ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2016-05-12 17:43:48 +00:00
std : : unique_ptr < : : aul : : ast : : RangeLoop > C4AulParse : : Parse_ForEach ( )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
auto loop = : : aul : : ast : : RangeLoop : : New ( TokenSPos ) ;
Match ( ATT_IDTF ) ; Match ( ATT_BOPEN ) ;
2009-05-08 13:28:41 +00:00
if ( TokenType = = ATT_IDTF & & SEqual ( Idtf , C4AUL_VarNamed ) )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
loop - > scoped_var = true ;
2009-05-08 13:28:41 +00:00
Shift ( ) ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// get variable name
2013-09-06 23:54:00 +00:00
Check ( ATT_IDTF , " variable name " ) ;
2016-05-12 17:43:48 +00:00
loop - > var = Idtf ;
2009-05-08 13:28:41 +00:00
Shift ( ) ;
if ( TokenType ! = ATT_IDTF | | ! SEqual ( Idtf , C4AUL_In ) )
UnexpectedToken ( " 'in' " ) ;
Shift ( ) ;
// get expression for array
2016-05-12 17:43:48 +00:00
loop - > cond = Parse_Expression ( ) ;
2009-05-08 13:28:41 +00:00
Match ( ATT_BCLOSE ) ;
// loop body
2016-05-12 17:43:48 +00:00
loop - > body = Parse_Statement ( ) ;
return loop ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2015-12-24 01:00:23 +00:00
static bool GetPropertyByS ( const C4PropList * p , const char * s , C4Value & v )
{
C4String * k = Strings . FindString ( s ) ;
if ( ! k ) return false ;
return p - > GetPropertyByS ( k , & v ) ;
}
2016-05-12 17:43:48 +00:00
std : : unique_ptr < : : aul : : ast : : Expr > C4AulParse : : Parse_Expression ( int iParentPrio )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
const char * NodeStart = TokenSPos ;
std : : unique_ptr < : : aul : : ast : : Expr > expr ;
2016-04-07 18:21:43 +00:00
const C4ScriptOpDef * op ;
2017-05-03 18:28:00 +00:00
C4AulFunc * FoundFn = nullptr ;
2012-08-13 17:38:17 +00:00
C4Value val ;
2009-05-08 13:28:41 +00:00
switch ( TokenType )
2010-03-28 18:58:01 +00:00
{
case ATT_IDTF :
2016-05-12 17:43:48 +00:00
// XXX: Resolving literals here means that you can't create a variable
// with the names "true", "false", "nil" or "this" anymore. I don't
// consider this too much of a problem, because there is never a reason
// to do this and it makes my job a lot easier
if ( SEqual ( Idtf , C4AUL_True ) )
2010-03-28 18:58:01 +00:00
{
Shift ( ) ;
2016-05-12 17:43:48 +00:00
expr = : : aul : : ast : : BoolLit : : New ( NodeStart , true ) ;
2010-03-28 18:58:01 +00:00
}
2016-05-12 17:43:48 +00:00
else if ( SEqual ( Idtf , C4AUL_False ) )
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
Shift ( ) ;
2016-05-12 17:43:48 +00:00
expr = : : aul : : ast : : BoolLit : : New ( NodeStart , false ) ;
2010-03-28 18:58:01 +00:00
}
2016-05-12 17:43:48 +00:00
else if ( SEqual ( Idtf , C4AUL_Nil ) )
2010-04-26 20:16:45 +00:00
{
Shift ( ) ;
2016-05-12 17:43:48 +00:00
expr = : : aul : : ast : : NilLit : : New ( NodeStart ) ;
2010-04-26 20:16:45 +00:00
}
2016-05-12 17:43:48 +00:00
else if ( SEqual ( Idtf , C4AUL_this ) )
2010-04-26 20:16:45 +00:00
{
Shift ( ) ;
2016-05-12 17:43:48 +00:00
if ( TokenType = = ATT_BOPEN )
2015-12-24 01:00:23 +00:00
{
Shift ( ) ;
2016-05-12 17:43:48 +00:00
Match ( ATT_BCLOSE ) ;
// TODO: maybe warn about "this" with parentheses?
2015-12-24 01:00:23 +00:00
}
2016-05-12 17:43:48 +00:00
expr = : : aul : : ast : : ThisLit : : New ( NodeStart ) ;
2015-12-24 01:00:23 +00:00
}
2016-05-12 17:43:48 +00:00
// XXX: Other things that people aren't allowed to do anymore: name their variables or functions any of:
// "if", "else", "for", "while", "do", "return", or "Par".
// We could allow variables with these names and disambiguate based on the syntax, but no.
else if ( SEqual ( Idtf , C4AUL_If ) | | SEqual ( Idtf , C4AUL_Else ) | | SEqual ( Idtf , C4AUL_For ) | | SEqual ( Idtf , C4AUL_While ) | | SEqual ( Idtf , C4AUL_Do ) | | SEqual ( Idtf , C4AUL_Return ) )
2011-11-07 00:39:43 +00:00
{
2016-05-12 17:43:48 +00:00
Error ( " reserved identifier not allowed in expressions: %s " , Idtf ) ;
2010-03-28 18:58:01 +00:00
}
else if ( SEqual ( Idtf , C4AUL_Par ) )
{
Shift ( ) ;
2016-05-12 17:43:48 +00:00
// "Par" is special in that it isn't a function and thus doesn't accept an arbitrary number of parameters
2012-08-13 17:07:56 +00:00
Match ( ATT_BOPEN ) ;
2016-05-12 17:43:48 +00:00
expr = : : aul : : ast : : ParExpr : : New ( NodeStart , Parse_Expression ( ) ) ;
2012-08-13 17:07:56 +00:00
Match ( ATT_BCLOSE ) ;
2010-03-28 18:58:01 +00:00
}
2016-05-12 17:43:48 +00:00
else if ( SEqual ( Idtf , C4AUL_New ) )
2012-06-18 02:07:36 +00:00
{
2016-05-12 17:43:48 +00:00
// Because people might call a variables or functions "new", we need to look ahead and guess whether it's a proplist constructor.
PushParsePos ( ) ;
2012-06-18 02:07:36 +00:00
Shift ( ) ;
2016-05-12 17:43:48 +00:00
if ( TokenType = = ATT_IDTF )
2012-06-18 02:07:36 +00:00
{
2016-05-12 17:43:48 +00:00
// this must be a proplist because two identifiers can't immediately follow each other
PopParsePos ( ) ;
expr = Parse_PropList ( ) ;
}
else
{
// Some non-identifier means this is either a variable, a function, or a syntax error. Which one exactly is something we'll figure out later.
PopParsePos ( ) ;
2012-06-18 02:07:36 +00:00
}
}
2016-05-12 17:43:48 +00:00
else if ( SEqual ( Idtf , C4AUL_Func ) )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
PushParsePos ( ) ;
2010-03-28 18:58:01 +00:00
Shift ( ) ;
2016-05-12 17:43:48 +00:00
if ( TokenType = = ATT_BOPEN )
2009-05-08 13:28:41 +00:00
{
2016-05-12 17:43:48 +00:00
auto func = : : aul : : ast : : FunctionExpr : : New ( NodeStart ) ;
Parse_Function ( func . get ( ) ) ;
expr = std : : move ( func ) ;
DiscardParsePos ( ) ;
2010-03-28 18:58:01 +00:00
}
else
2016-05-12 17:43:48 +00:00
{
PopParsePos ( ) ;
}
2010-03-28 18:58:01 +00:00
}
2016-05-12 17:43:48 +00:00
if ( ! expr )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
// If we end up here, it must be a proper identifier (or a reserved word that's used as an identifier).
// Time to look ahead and see whether it's a function call.
std : : string identifier = Idtf ;
2012-08-13 17:38:17 +00:00
Shift ( ) ;
if ( TokenType = = ATT_BOPEN )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
// Well, it looks like one, at least
auto func = : : aul : : ast : : CallExpr : : New ( NodeStart ) ;
func - > callee = identifier ;
Parse_CallParams ( func . get ( ) ) ;
expr = std : : move ( func ) ;
}
else
{
// It's most certainly not a function call.
expr = : : aul : : ast : : VarExpr : : New ( NodeStart , identifier ) ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
break ;
case ATT_INT : // constant in cInt
2016-05-12 17:43:48 +00:00
expr = : : aul : : ast : : IntLit : : New ( NodeStart , cInt ) ;
2010-03-28 18:58:01 +00:00
Shift ( ) ;
break ;
2011-02-21 23:07:08 +00:00
case ATT_STRING : // reference in cStr
2016-05-12 17:43:48 +00:00
expr = : : aul : : ast : : StringLit : : New ( NodeStart , cStr - > GetCStr ( ) ) ;
2010-03-28 18:58:01 +00:00
Shift ( ) ;
break ;
case ATT_OPERATOR :
// -> must be a prefix operator
2012-10-21 14:13:53 +00:00
op = & C4ScriptOpMap [ cInt ] ;
2010-03-28 18:58:01 +00:00
// postfix?
2012-10-21 14:13:53 +00:00
if ( op - > Postfix )
2010-03-28 18:58:01 +00:00
// oops. that's wrong
2015-12-07 14:15:49 +00:00
throw C4AulParseError ( this , " postfix operator without first expression " ) ;
2010-03-28 18:58:01 +00:00
Shift ( ) ;
// generate code for the following expression
2016-05-12 17:43:48 +00:00
expr = Parse_Expression ( op - > Priority ) ;
if ( SEqual ( op - > Identifier , " + " ) )
2011-05-01 16:53:50 +00:00
{
2016-05-12 17:43:48 +00:00
// This is a no-op.
2011-05-01 16:53:50 +00:00
}
2016-05-12 17:43:48 +00:00
else
2016-02-04 00:23:07 +00:00
{
2016-05-12 17:43:48 +00:00
expr = : : aul : : ast : : UnOpExpr : : New ( NodeStart , op - C4ScriptOpMap , std : : move ( expr ) ) ;
2016-02-04 00:23:07 +00:00
}
2010-03-28 18:58:01 +00:00
break ;
case ATT_BOPEN :
Shift ( ) ;
2016-05-12 17:43:48 +00:00
expr = Parse_Expression ( ) ;
2010-03-28 18:58:01 +00:00
Match ( ATT_BCLOSE ) ;
break ;
case ATT_BOPEN2 :
2016-05-12 17:43:48 +00:00
expr = Parse_Array ( ) ;
2010-03-28 18:58:01 +00:00
break ;
case ATT_BLOPEN :
2016-05-12 17:43:48 +00:00
expr = Parse_PropList ( ) ;
2010-03-28 18:58:01 +00:00
break ;
default :
UnexpectedToken ( " expression " ) ;
2009-05-08 13:28:41 +00:00
}
2016-05-12 17:43:48 +00:00
assert ( expr ) ;
2017-05-03 18:28:00 +00:00
while ( true )
2010-08-14 23:53:18 +00:00
{
2016-05-12 17:43:48 +00:00
NodeStart = TokenSPos ;
switch ( TokenType )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
case ATT_SET :
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
// back out of any kind of parent operator
// (except other setters, as those are right-associative)
if ( iParentPrio > 1 )
return expr ;
Shift ( ) ;
2016-05-13 11:50:29 +00:00
expr = : : aul : : ast : : AssignmentExpr : : New ( NodeStart , std : : move ( expr ) , Parse_Expression ( 1 ) ) ;
2016-05-12 17:43:48 +00:00
break ;
}
case ATT_OPERATOR :
{
// expect postfix operator
const C4ScriptOpDef * op = & C4ScriptOpMap [ cInt ] ;
if ( ! op - > Postfix )
2010-03-28 18:58:01 +00:00
{
2016-05-12 17:43:48 +00:00
// does an operator with the same name exist?
// when it's a postfix-operator, it can be used instead.
const C4ScriptOpDef * postfixop ;
for ( postfixop = op + 1 ; postfixop - > Identifier ; + + postfixop )
if ( SEqual ( op - > Identifier , postfixop - > Identifier ) )
if ( postfixop - > Postfix )
break ;
// not found?
if ( ! postfixop - > Identifier )
{
Error ( " unexpected prefix operator: %s " , op - > Identifier ) ;
}
// otherwise use the new-found correct postfix operator
op = postfixop ;
2009-05-08 13:28:41 +00:00
}
2010-04-07 13:04:19 +00:00
2016-05-12 17:43:48 +00:00
if ( iParentPrio + ! op - > Changer > op - > Priority )
return expr ;
2009-05-08 13:28:41 +00:00
2016-05-12 17:43:48 +00:00
Shift ( ) ;
if ( op - > NoSecondStatement )
2011-05-01 16:53:50 +00:00
{
2016-05-12 17:43:48 +00:00
// Postfix unary op
expr = : : aul : : ast : : UnOpExpr : : New ( NodeStart , op - C4ScriptOpMap , std : : move ( expr ) ) ;
2011-05-01 16:53:50 +00:00
}
2016-05-12 17:43:48 +00:00
else
2011-05-01 16:53:50 +00:00
{
2016-05-12 17:43:48 +00:00
expr = : : aul : : ast : : BinOpExpr : : New ( NodeStart , op - C4ScriptOpMap , std : : move ( expr ) , Parse_Expression ( op - > Priority ) ) ;
2011-05-01 16:53:50 +00:00
}
2016-05-12 17:43:48 +00:00
break ;
2009-05-08 13:28:41 +00:00
}
2016-05-12 17:43:48 +00:00
case ATT_BOPEN2 :
2010-04-05 15:41:36 +00:00
{
2016-05-12 17:43:48 +00:00
// parse either [index], or [start:end] in which case either index is optional
2010-04-05 15:41:36 +00:00
Shift ( ) ;
2016-05-12 17:43:48 +00:00
: : aul : : ast : : ExprPtr start ;
if ( TokenType = = ATT_COLON )
start = : : aul : : ast : : IntLit : : New ( TokenSPos , 0 ) ; // slice with first index missing -> implicit start index zero
else
start = Parse_Expression ( ) ;
2009-05-08 13:28:41 +00:00
2016-05-12 17:43:48 +00:00
if ( TokenType = = ATT_BCLOSE2 )
2014-04-19 21:34:31 +00:00
{
2016-05-12 17:43:48 +00:00
expr = : : aul : : ast : : SubscriptExpr : : New ( NodeStart , std : : move ( expr ) , std : : move ( start ) ) ;
2014-04-19 21:34:31 +00:00
}
2016-05-12 17:43:48 +00:00
else if ( TokenType = = ATT_COLON )
2010-09-09 20:36:00 +00:00
{
2016-05-12 17:43:48 +00:00
Shift ( ) ;
: : aul : : ast : : ExprPtr end ;
if ( TokenType = = ATT_BCLOSE2 )
{
end = : : aul : : ast : : IntLit : : New ( TokenSPos , std : : numeric_limits < int32_t > : : max ( ) ) ;
}
else
{
end = Parse_Expression ( ) ;
}
expr = : : aul : : ast : : SliceExpr : : New ( NodeStart , std : : move ( expr ) , std : : move ( start ) , std : : move ( end ) ) ;
2010-09-09 20:36:00 +00:00
}
2015-12-17 15:55:51 +00:00
else
{
2016-05-12 17:43:48 +00:00
UnexpectedToken ( " ']' or ':' " ) ;
2010-09-09 00:18:19 +00:00
}
2016-05-12 17:43:48 +00:00
Match ( ATT_BCLOSE2 ) ;
break ;
2010-09-09 00:18:19 +00:00
}
2016-05-12 17:43:48 +00:00
case ATT_DOT :
Shift ( ) ;
Check ( ATT_IDTF , " property name " ) ;
expr = : : aul : : ast : : SubscriptExpr : : New ( NodeStart , std : : move ( expr ) , : : aul : : ast : : StringLit : : New ( TokenSPos , Idtf ) ) ;
2014-09-28 20:49:19 +00:00
Shift ( ) ;
2010-09-09 20:36:00 +00:00
break ;
2016-05-12 17:43:48 +00:00
case ATT_CALL : case ATT_CALLFS :
2011-02-19 21:37:42 +00:00
{
2016-05-12 17:43:48 +00:00
auto call = : : aul : : ast : : CallExpr : : New ( NodeStart ) ;
call - > context = std : : move ( expr ) ;
call - > safe_call = TokenType = = ATT_CALLFS ;
2011-02-19 21:37:42 +00:00
Shift ( ) ;
2016-05-12 17:43:48 +00:00
Check ( ATT_IDTF , " function name after '->' " ) ;
call - > callee = Idtf ;
2011-02-19 21:37:42 +00:00
Shift ( ) ;
2016-05-12 17:43:48 +00:00
Parse_CallParams ( call . get ( ) ) ;
expr = std : : move ( call ) ;
break ;
2011-02-19 21:37:42 +00:00
}
2016-05-12 17:43:48 +00:00
default :
return expr ;
2011-02-19 21:37:42 +00:00
}
2010-09-09 00:18:19 +00:00
}
2016-05-12 17:43:48 +00:00
}
std : : unique_ptr < : : aul : : ast : : VarDecl > C4AulParse : : Parse_Var ( )
{
auto decl = : : aul : : ast : : VarDecl : : New ( TokenSPos ) ;
if ( SEqual ( Idtf , C4AUL_VarNamed ) )
2013-10-09 16:33:02 +00:00
{
2016-05-12 17:43:48 +00:00
decl - > scope = : : aul : : ast : : VarDecl : : Scope : : Func ;
2013-10-09 16:33:02 +00:00
}
2016-05-12 17:43:48 +00:00
else if ( SEqual ( Idtf , C4AUL_LocalNamed ) )
2010-09-09 20:41:41 +00:00
{
2016-05-12 17:43:48 +00:00
decl - > scope = : : aul : : ast : : VarDecl : : Scope : : Object ;
2010-09-09 20:41:41 +00:00
}
2016-05-12 17:43:48 +00:00
else if ( SEqual ( Idtf , C4AUL_GlobalNamed ) )
2012-05-27 16:00:45 +00:00
{
2016-05-12 17:43:48 +00:00
decl - > scope = : : aul : : ast : : VarDecl : : Scope : : Global ;
}
else
{
assert ( 0 & & " C4AulParse::Parse_Var called with invalid parse state (current token should be scope of variable) " ) ;
// Uh this shouldn't happen, ever
Error ( " internal error: C4AulParse::Parse_Var called with invalid parse state (current token should be scope of variable, but is '%s') " , Idtf ) ;
2012-05-27 16:00:45 +00:00
}
2011-10-23 23:00:29 +00:00
Shift ( ) ;
2016-05-12 17:43:48 +00:00
if ( TokenType = = ATT_IDTF & & SEqual ( Idtf , C4AUL_Const ) )
{
decl - > constant = true ;
Shift ( ) ;
}
2017-05-03 18:28:00 +00:00
while ( true )
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// get desired variable name
2016-05-12 17:43:48 +00:00
Check ( ATT_IDTF , " variable name " ) ;
std : : string identifier = Idtf ;
2013-09-06 23:54:00 +00:00
Shift ( ) ;
2016-05-12 17:43:48 +00:00
: : aul : : ast : : ExprPtr init ;
if ( TokenType = = ATT_SET )
{
Shift ( ) ;
init = Parse_Expression ( ) ;
}
decl - > decls . push_back ( { identifier , std : : move ( init ) } ) ;
2013-09-06 23:54:00 +00:00
if ( TokenType = = ATT_SCOLON )
2016-05-12 17:43:48 +00:00
return decl ;
2013-09-06 23:54:00 +00:00
Match ( ATT_COMMA , " ',' or ';' " ) ;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2012-08-08 23:21:54 +00:00
void C4ScriptHost : : CopyPropList ( C4Set < C4Property > & from , C4PropListStatic * to )
{
// append all funcs and local variable initializations
const C4Property * prop = from . First ( ) ;
while ( prop )
{
switch ( prop - > Value . GetType ( ) )
{
case C4V_Function :
{
C4AulScriptFunc * sf = prop - > Value . getFunction ( ) - > SFunc ( ) ;
2013-03-18 23:35:00 +00:00
if ( sf )
{
C4AulScriptFunc * sfc ;
if ( sf - > pOrgScript ! = this )
2014-04-20 13:52:09 +00:00
sfc = new C4AulScriptFunc ( to , * sf ) ;
2013-03-18 23:35:00 +00:00
else
sfc = sf ;
sfc - > SetOverloaded ( to - > GetFunc ( sf - > Name ) ) ;
to - > SetPropertyByS ( prop - > Key , C4VFunction ( sfc ) ) ;
}
2012-08-08 23:21:54 +00:00
else
2013-03-18 23:35:00 +00:00
{
// engine function
to - > SetPropertyByS ( prop - > Key , prop - > Value ) ;
}
2012-08-08 23:21:54 +00:00
}
break ;
case C4V_PropList :
{
C4PropListStatic * p = prop - > Value . _getPropList ( ) - > IsStatic ( ) ;
assert ( p ) ;
if ( prop - > Key ! = & : : Strings . P [ P_Prototype ] )
if ( ! p | | p - > GetParent ( ) ! = to )
{
2016-11-02 23:58:02 +00:00
p = C4PropList : : NewStatic ( nullptr , to , prop - > Key ) ;
2012-08-08 23:21:54 +00:00
CopyPropList ( prop - > Value . _getPropList ( ) - > Properties , p ) ;
}
to - > SetPropertyByS ( prop - > Key , C4VPropList ( p ) ) ;
}
2013-01-18 00:45:06 +00:00
break ;
2012-08-08 23:21:54 +00:00
case C4V_Array : // FIXME: copy the array if necessary
default :
to - > SetPropertyByS ( prop - > Key , prop - > Value ) ;
}
prop = from . Next ( prop ) ;
}
}
2011-10-13 16:01:02 +00:00
bool C4ScriptHost : : Parse ( )
{
2009-05-08 13:28:41 +00:00
// check state
2009-08-15 18:50:32 +00:00
if ( State ! = ASS_LINKED ) return false ;
2011-10-13 16:01:02 +00:00
2012-04-13 19:43:43 +00:00
if ( ! Appends . empty ( ) )
{
// #appendto scripts are not allowed to contain global functions or belong to definitions
// so their contents are not reachable
return true ;
}
2012-08-08 23:17:26 +00:00
C4PropListStatic * p = GetPropList ( ) ;
2012-05-27 22:31:55 +00:00
2017-05-03 18:28:00 +00:00
for ( auto & SourceScript : SourceScripts )
2012-01-26 23:20:55 +00:00
{
2017-05-03 18:28:00 +00:00
CopyPropList ( SourceScript - > LocalValues , p ) ;
if ( SourceScript = = this )
2012-01-26 23:20:55 +00:00
continue ;
// definition appends
2017-05-03 18:28:00 +00:00
if ( GetPropList ( ) & & GetPropList ( ) - > GetDef ( ) & & SourceScript - > GetPropList ( ) & & SourceScript - > GetPropList ( ) - > GetDef ( ) )
GetPropList ( ) - > GetDef ( ) - > IncludeDefinition ( SourceScript - > GetPropList ( ) - > GetDef ( ) ) ;
2012-01-26 23:20:55 +00:00
}
2016-05-12 17:43:48 +00:00
// generate bytecode
for ( auto & s : SourceScripts )
C4AulCompiler : : Compile ( this , s , s - > ast . get ( ) ) ;
2009-05-08 13:28:41 +00:00
// save line count
Engine - > lineCnt + = SGetLine ( Script . getData ( ) , Script . getPtr ( Script . getLength ( ) ) ) ;
// finished
State = ASS_PARSED ;
2009-08-15 18:50:32 +00:00
return true ;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2017-02-13 13:53:17 +00:00
void C4ScriptHost : : EnableWarning ( const char * pos , C4AulWarningId warning , bool enable )
{
auto entry = enabledWarnings . emplace ( pos , decltype ( enabledWarnings ) : : mapped_type { } ) ;
if ( entry . second )
{
// If there was no earlier entry for this position, copy the previous
// warning state
assert ( entry . first ! = enabledWarnings . begin ( ) ) ;
auto previous = entry . first ;
- - previous ;
entry . first - > second = previous - > second ;
}
entry . first - > second . set ( static_cast < size_t > ( warning ) , enable ) ;
}
bool C4ScriptHost : : IsWarningEnabled ( const char * pos , C4AulWarningId warning ) const
{
assert ( ! enabledWarnings . empty ( ) ) ;
if ( enabledWarnings . empty ( ) )
return false ;
// find nearest set of warnings at or before the current position
auto entry = enabledWarnings . upper_bound ( pos ) ;
assert ( entry ! = enabledWarnings . begin ( ) ) ;
if ( entry ! = enabledWarnings . begin ( ) )
{
- - entry ;
}
return entry - > second . test ( static_cast < size_t > ( warning ) ) ;
}
2016-05-12 17:43:48 +00:00
void C4AulParse : : PushParsePos ( )
{
parse_pos_stack . push ( TokenSPos ) ;
}
void C4AulParse : : PopParsePos ( )
{
assert ( ! parse_pos_stack . empty ( ) ) ;
SPos = parse_pos_stack . top ( ) ;
DiscardParsePos ( ) ;
Shift ( ) ;
}
void C4AulParse : : DiscardParsePos ( )
{
assert ( ! parse_pos_stack . empty ( ) ) ;
parse_pos_stack . pop ( ) ;
}