2009-05-08 13:28:41 +00:00
|
|
|
/*
|
|
|
|
* OpenClonk, http://www.openclonk.org
|
|
|
|
*
|
2010-12-23 00:01:24 +00:00
|
|
|
* Copyright (c) 2001-2004, 2006-2008, 2010 Sven Eberhardt
|
2011-09-01 14:58:52 +00:00
|
|
|
* Copyright (c) 2001-2004, 2006-2008, 2010 Peter Wortmann
|
2010-12-23 00:01:24 +00:00
|
|
|
* Copyright (c) 2004, 2007 Matthes Bender
|
2013-01-09 23:23:06 +00:00
|
|
|
* Copyright (c) 2006-2012 Günther Brammer
|
2010-12-23 00:01:24 +00:00
|
|
|
* Copyright (c) 2006, 2010 Armin Burgmeier
|
2013-01-09 23:23:06 +00:00
|
|
|
* Copyright (c) 2009-2010, 2012 Nicolas Hake
|
2010-12-23 00:01:24 +00:00
|
|
|
* Copyright (c) 2010 Benjamin Herr
|
2011-09-01 10:47:54 +00:00
|
|
|
* Copyright (c) 2010 Martin Plicht
|
2009-05-08 13:28:41 +00:00
|
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
|
|
|
|
*
|
|
|
|
* Portions might be copyrighted by other authors who have contributed
|
|
|
|
* to OpenClonk.
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
* See isc_license.txt for full license and disclaimer.
|
|
|
|
*
|
|
|
|
* "Clonk" is a registered trademark of Matthes Bender.
|
|
|
|
* See clonk_trademark_license.txt for full license.
|
|
|
|
*/
|
|
|
|
// parses scripts
|
|
|
|
|
2011-03-14 15:32:01 +00:00
|
|
|
#include <C4Include.h>
|
2009-11-25 18:38:54 +00:00
|
|
|
#include <utility>
|
|
|
|
|
2009-05-08 13:28:41 +00:00
|
|
|
#include <C4Aul.h>
|
|
|
|
|
2011-04-10 01:40:55 +00:00
|
|
|
#include <C4AulDebug.h>
|
2009-05-08 13:28:41 +00:00
|
|
|
#include <C4Def.h>
|
|
|
|
#include <C4Game.h>
|
2009-06-12 18:52:21 +00:00
|
|
|
#include <C4Log.h>
|
2011-01-24 00:15:58 +00:00
|
|
|
#include <C4Config.h>
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
#define DEBUG_BYTECODE_DUMP 0
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
#define C4AUL_Include "#include"
|
|
|
|
#define C4AUL_Append "#appendto"
|
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_Goto "goto"
|
|
|
|
#define C4AUL_Break "break"
|
|
|
|
#define C4AUL_Continue "continue"
|
|
|
|
#define C4AUL_Inherited "inherited"
|
|
|
|
#define C4AUL_SafeInherited "_inherited"
|
|
|
|
#define C4AUL_this "this"
|
|
|
|
|
|
|
|
#define C4AUL_Image "Image"
|
2009-05-08 13:28:41 +00:00
|
|
|
#define C4AUL_Contents "Contents"
|
2010-03-28 18:58:01 +00:00
|
|
|
#define C4AUL_Condition "Condition"
|
2009-05-08 13:28:41 +00:00
|
|
|
#define C4AUL_Desc "Desc"
|
|
|
|
|
|
|
|
#define C4AUL_MethodAll "All"
|
|
|
|
#define C4AUL_MethodNone "None"
|
|
|
|
#define C4AUL_MethodClassic "Classic"
|
|
|
|
#define C4AUL_MethodJumpAndRun "JumpAndRun"
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
#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
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
#define C4AUL_CodeBufSize 16
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
// script token type
|
|
|
|
enum C4AulTokenType
|
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, // "->~"
|
2010-03-28 18:58:01 +00:00
|
|
|
ATT_STAR, // "*"
|
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
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
class C4AulParse
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
|
|
|
public:
|
2009-05-08 13:28:41 +00:00
|
|
|
enum Type { PARSER, PREPARSER };
|
2012-04-12 19:03:14 +00:00
|
|
|
C4AulParse(C4ScriptHost * a, enum Type Type):
|
2012-10-27 23:52:18 +00:00
|
|
|
Fn(0), Host(a), pOrgScript(a), Engine(a->Engine),
|
|
|
|
SPos(a->Script.getData()), TokenSPos(SPos),
|
|
|
|
TokenType(ATT_INVALID),
|
|
|
|
Type(Type),
|
|
|
|
ContextToExecIn(NULL),
|
|
|
|
fJump(false),
|
|
|
|
iStack(0),
|
|
|
|
pLoopStack(NULL)
|
|
|
|
{ }
|
2013-01-12 18:13:27 +00:00
|
|
|
C4AulParse(C4AulScriptFunc * Fn, C4AulScriptContext* context, enum Type Type):
|
2012-10-27 23:52:18 +00:00
|
|
|
Fn(Fn), Host(Fn->pOrgScript), pOrgScript(Fn->pOrgScript), Engine(Fn->Owner->Engine),
|
|
|
|
SPos(Fn->Script), TokenSPos(SPos),
|
2010-03-28 18:58:01 +00:00
|
|
|
TokenType(ATT_INVALID),
|
|
|
|
Type(Type),
|
2013-01-12 18:13:27 +00:00
|
|
|
ContextToExecIn(context),
|
2010-03-28 18:58:01 +00:00
|
|
|
fJump(false),
|
|
|
|
iStack(0),
|
2010-04-28 21:43:25 +00:00
|
|
|
pLoopStack(NULL)
|
2010-03-28 18:58:01 +00:00
|
|
|
{ }
|
2012-04-12 19:03:14 +00:00
|
|
|
~C4AulParse()
|
2010-03-28 18:58:01 +00:00
|
|
|
{ while (pLoopStack) PopLoop(); ClearToken(); }
|
2013-01-12 18:13:27 +00:00
|
|
|
void Parse_DirectExec();
|
|
|
|
void Parse_Script(C4ScriptHost *);
|
|
|
|
|
|
|
|
private:
|
2012-10-27 23:52:18 +00:00
|
|
|
C4AulScriptFunc *Fn; C4ScriptHost * Host; C4ScriptHost * pOrgScript;
|
|
|
|
C4AulScriptEngine *Engine;
|
2009-05-08 13:28:41 +00:00
|
|
|
const char *SPos; // current position in the script
|
2012-05-16 21:24:40 +00:00
|
|
|
const char *TokenSPos; // start of the current token in the script
|
2009-05-08 13:28:41 +00:00
|
|
|
char Idtf[C4AUL_MAX_Identifier]; // current identifier
|
|
|
|
C4AulTokenType TokenType; // current token type
|
2011-02-21 23:07:08 +00:00
|
|
|
int32_t cInt; // current int constant
|
|
|
|
C4String * cStr; // current string constant
|
2009-05-08 13:28:41 +00:00
|
|
|
enum Type Type; // emitting bytecode?
|
2010-04-26 20:16:45 +00:00
|
|
|
C4AulScriptContext* ContextToExecIn;
|
2009-05-08 13:28:41 +00:00
|
|
|
void Parse_Function();
|
|
|
|
void Parse_Statement();
|
|
|
|
void Parse_Block();
|
|
|
|
int Parse_Params(int iMaxCnt, const char * sWarn, C4AulFunc * pFunc = 0);
|
|
|
|
void Parse_Array();
|
2009-04-12 00:56:41 +00:00
|
|
|
void Parse_PropList();
|
2009-08-28 02:44:53 +00:00
|
|
|
void Parse_DoWhile();
|
2009-05-08 13:28:41 +00:00
|
|
|
void Parse_While();
|
|
|
|
void Parse_If();
|
|
|
|
void Parse_For();
|
|
|
|
void Parse_ForEach();
|
|
|
|
void Parse_Expression(int iParentPrio = -1);
|
|
|
|
void Parse_Expression2(int iParentPrio = -1);
|
|
|
|
void Parse_Var();
|
|
|
|
void Parse_Local();
|
|
|
|
void Parse_Static();
|
|
|
|
void Parse_Const();
|
2012-05-27 16:00:45 +00:00
|
|
|
C4Value Parse_ConstExpression(C4PropListStatic * parent, C4String * Name);
|
|
|
|
C4Value Parse_ConstPropList(const C4PropListStatic * parent, C4String * Name);
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool AdvanceSpaces(); // skip whitespaces; return whether script ended
|
2009-05-08 13:28:41 +00:00
|
|
|
int GetOperator(const char* pScript);
|
|
|
|
// Simply discard the string, put it in the Table and delete it with the script or delete it when refcount drops
|
2011-02-19 21:37:42 +00:00
|
|
|
enum OperatorPolicy { OperatorsPlease = 0, StarsPlease };
|
2010-01-30 16:51:26 +00:00
|
|
|
void ClearToken(); // clear any data held with the current token
|
2011-02-19 21:37:42 +00:00
|
|
|
C4AulTokenType GetNextToken(OperatorPolicy Operator = OperatorsPlease); // get next token of SPos
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2011-02-19 21:37:42 +00:00
|
|
|
void Shift(OperatorPolicy Operator = OperatorsPlease);
|
2009-05-08 13:28:41 +00:00
|
|
|
void Match(C4AulTokenType TokenType, const char * Message = NULL);
|
2010-09-08 12:54:39 +00:00
|
|
|
void UnexpectedToken(const char * Expected) NORETURN;
|
2011-02-19 21:37:42 +00:00
|
|
|
static const char * GetTokenName(C4AulTokenType TokenType);
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-05-25 22:22:22 +00:00
|
|
|
void Warn(const char *pMsg, ...) GNUC_FORMAT_ATTRIBUTE_O;
|
|
|
|
void Error(const char *pMsg, ...) GNUC_FORMAT_ATTRIBUTE_O;
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
bool fJump;
|
|
|
|
int iStack;
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-04-07 13:04:19 +00:00
|
|
|
int GetStackValue(C4AulBCCType eType, intptr_t X = 0);
|
2011-10-13 22:28:29 +00:00
|
|
|
int AddBCC(C4AulBCCType eType, intptr_t X = 0);
|
2010-04-07 13:04:19 +00:00
|
|
|
void RemoveLastBCC();
|
2011-05-01 16:53:50 +00:00
|
|
|
C4V_Type GetLastRetType(C4V_Type to); // for warning purposes
|
2010-03-28 18:58:01 +00:00
|
|
|
|
2010-04-07 13:04:19 +00:00
|
|
|
C4AulBCC MakeSetter(bool fLeaveValue = false); // Prepares to generate a setter for the last value that was generated
|
2010-03-28 18:58:01 +00:00
|
|
|
|
|
|
|
int JumpHere(); // Get position for a later jump to next instruction added
|
|
|
|
void SetJumpHere(int iJumpOp); // Use the next inserted instruction as jump target for the given jump operation
|
|
|
|
void SetJump(int iJumpOp, int iWhere);
|
|
|
|
void AddJump(C4AulBCCType eType, int iWhere);
|
|
|
|
|
|
|
|
// Keep track of loops and break/continue usages
|
|
|
|
struct Loop
|
|
|
|
{
|
|
|
|
struct Control
|
|
|
|
{
|
|
|
|
bool Break;
|
|
|
|
int Pos;
|
|
|
|
Control *Next;
|
|
|
|
};
|
|
|
|
Control *Controls;
|
|
|
|
int StackSize;
|
|
|
|
Loop *Next;
|
2009-05-08 13:28:41 +00:00
|
|
|
};
|
2010-03-28 18:58:01 +00:00
|
|
|
Loop *pLoopStack;
|
|
|
|
|
|
|
|
void PushLoop();
|
|
|
|
void PopLoop();
|
|
|
|
void AddLoopControl(bool fBreak);
|
2013-01-12 18:13:27 +00:00
|
|
|
friend class C4AulParseError;
|
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 C4AulScript::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);
|
|
|
|
StdStrBuf Buf;
|
|
|
|
Buf.FormatV(pMsg, args);
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-05-25 22:22:22 +00:00
|
|
|
C4AulParseError warning(this, Buf.getData(), 0, true);
|
2009-05-08 13:28:41 +00:00
|
|
|
// display it
|
|
|
|
warning.show();
|
|
|
|
// count warnings
|
2009-06-05 15:21:11 +00:00
|
|
|
++::ScriptEngine.warnCnt;
|
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::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);
|
|
|
|
StdStrBuf Buf;
|
|
|
|
Buf.FormatV(pMsg, args);
|
|
|
|
|
|
|
|
C4AulParseError warning(this, Buf.getData(), 0, true);
|
2009-05-08 13:28:41 +00:00
|
|
|
warning.show();
|
2012-10-27 23:52:18 +00:00
|
|
|
if (pOrgScript != Host)
|
|
|
|
DebugLogF(" (as #appendto/#include to %s)", Host->ScriptName.getData());
|
2009-05-08 13:28:41 +00:00
|
|
|
// count warnings
|
2009-06-05 15:21:11 +00:00
|
|
|
++::ScriptEngine.warnCnt;
|
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);
|
|
|
|
|
|
|
|
throw new C4AulParseError(this, Buf.getData());
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
C4AulParseError::C4AulParseError(C4AulParse * state, const char *pMsg, const char *pIdtf, bool Warn)
|
2010-03-28 18:58:01 +00:00
|
|
|
: C4AulError()
|
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// compose error string
|
|
|
|
sMessage.Format("%s: %s%s",
|
2010-03-28 18:58:01 +00:00
|
|
|
Warn ? "WARNING" : "ERROR",
|
|
|
|
pMsg,
|
|
|
|
pIdtf ? pIdtf : "");
|
2011-10-13 22:40:55 +00:00
|
|
|
if (state->Fn && state->Fn->GetName())
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// Show function name
|
2011-10-13 22:40:55 +00:00
|
|
|
sMessage.AppendFormat(" (in %s", state->Fn->GetName());
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
// Exact position
|
2012-05-16 21:24:40 +00:00
|
|
|
if (state->Fn->pOrgScript && state->TokenSPos)
|
2009-05-08 13:28:41 +00:00
|
|
|
sMessage.AppendFormat(", %s:%d:%d)",
|
2010-03-28 18:58:01 +00:00
|
|
|
state->Fn->pOrgScript->ScriptName.getData(),
|
2012-05-16 21:24:40 +00:00
|
|
|
SGetLine(state->Fn->pOrgScript->GetScript(), state->TokenSPos),
|
|
|
|
SLineGetCharacters(state->Fn->pOrgScript->GetScript(), state->TokenSPos));
|
2009-05-08 13:28:41 +00:00
|
|
|
else
|
|
|
|
sMessage.AppendChar(')');
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-01-26 23:42:41 +00:00
|
|
|
else if (state->pOrgScript)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// Script name
|
|
|
|
sMessage.AppendFormat(" (%s:%d:%d)",
|
2012-01-26 23:42:41 +00:00
|
|
|
state->pOrgScript->ScriptName.getData(),
|
2012-05-16 21:24:40 +00:00
|
|
|
SGetLine(state->pOrgScript->GetScript(), state->TokenSPos),
|
|
|
|
SLineGetCharacters(state->pOrgScript->GetScript(), state->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
|
|
|
|
if (state->pOrgScript != state->Host && state->Host)
|
|
|
|
sMessage.AppendFormat(" (as #appendto/#include to %s)", state->Host->ScriptName.getData());
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
C4AulParseError::C4AulParseError(C4AulScript *pScript, const char *pMsg, const char *pIdtf, bool Warn)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// compose error string
|
|
|
|
sMessage.Format("%s: %s%s",
|
2010-03-28 18:58:01 +00:00
|
|
|
Warn ? "WARNING" : "ERROR",
|
|
|
|
pMsg,
|
|
|
|
pIdtf ? pIdtf : "");
|
|
|
|
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
|
|
|
|
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 ===================================
|
2011-05-01 15:16:02 +00:00
|
|
|
struct C4ScriptOpDef
|
|
|
|
{
|
|
|
|
unsigned short Priority;
|
|
|
|
const char* Identifier;
|
|
|
|
C4AulBCCType Code;
|
|
|
|
C4AulBCCType ResultModifier; // code to apply to result after it was calculated
|
|
|
|
bool Postfix;
|
|
|
|
bool Changer; // changes first operand to result, rewrite to "a = a (op) b"
|
|
|
|
bool NoSecondStatement; // no second statement expected (++/-- postfix)
|
|
|
|
C4V_Type RetType; // type returned. ignored by C4V
|
|
|
|
C4V_Type Type1;
|
|
|
|
C4V_Type Type2;
|
|
|
|
};
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2011-05-01 15:16:02 +00:00
|
|
|
static C4ScriptOpDef C4ScriptOpMap[] =
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-08 00:47:45 +00:00
|
|
|
// priority postfix
|
|
|
|
// | identifier | changer
|
|
|
|
// | | Bytecode Result | | no second id
|
|
|
|
// | | | Modifier | | | RetType ParType1 ParType2
|
2009-05-08 13:28:41 +00:00
|
|
|
// prefix
|
2010-04-08 00:47:45 +00:00
|
|
|
{ 15, "++", AB_Inc, AB_ERR, 0, 1, 0, C4V_Int, C4V_Int, C4V_Any},
|
|
|
|
{ 15, "--", AB_Dec, AB_ERR, 0, 1, 0, C4V_Int, C4V_Int, C4V_Any},
|
|
|
|
{ 15, "~", AB_BitNot, AB_ERR, 0, 0, 0, C4V_Int, C4V_Int, C4V_Any},
|
|
|
|
{ 15, "!", AB_Not, AB_ERR, 0, 0, 0, C4V_Bool, C4V_Bool, C4V_Any},
|
|
|
|
{ 15, "+", AB_ERR, AB_ERR, 0, 0, 0, C4V_Int, C4V_Int, C4V_Any},
|
|
|
|
{ 15, "-", AB_Neg, AB_ERR, 0, 0, 0, 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)
|
2010-04-08 00:47:45 +00:00
|
|
|
{ 16, "++", AB_Inc, AB_Dec, 1, 1, 1, C4V_Int, C4V_Int, C4V_Any},
|
|
|
|
{ 16, "--", AB_Dec, AB_Inc, 1, 1, 1, C4V_Int, C4V_Int, C4V_Any},
|
|
|
|
|
2009-05-08 13:28:41 +00:00
|
|
|
// postfix
|
2010-04-08 00:47:45 +00:00
|
|
|
{ 14, "**", AB_Pow, AB_ERR, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 13, "/", AB_Div, AB_ERR, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 13, "*", AB_Mul, AB_ERR, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 13, "%", AB_Mod, AB_ERR, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 12, "-", AB_Sub, AB_ERR, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 12, "+", AB_Sum, AB_ERR, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 11, "<<", AB_LeftShift, AB_ERR, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 11, ">>", AB_RightShift, AB_ERR, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 10, "<", AB_LessThan, AB_ERR, 1, 0, 0, C4V_Bool, C4V_Int, C4V_Int},
|
|
|
|
{ 10, "<=", AB_LessThanEqual, AB_ERR, 1, 0, 0, C4V_Bool, C4V_Int, C4V_Int},
|
|
|
|
{ 10, ">", AB_GreaterThan, AB_ERR, 1, 0, 0, C4V_Bool, C4V_Int, C4V_Int},
|
|
|
|
{ 10, ">=", AB_GreaterThanEqual,AB_ERR, 1, 0, 0, C4V_Bool, C4V_Int, C4V_Int},
|
2012-06-10 22:47:28 +00:00
|
|
|
{ 9, "===", AB_Identical, AB_ERR, 1, 0, 0, C4V_Bool, C4V_Any, C4V_Any},
|
|
|
|
{ 9, "!==", AB_NotIdentical, AB_ERR, 1, 0, 0, C4V_Bool, C4V_Any, C4V_Any},
|
2010-04-08 00:47:45 +00:00
|
|
|
{ 9, "==", AB_Equal, AB_ERR, 1, 0, 0, C4V_Bool, C4V_Any, C4V_Any},
|
|
|
|
{ 9, "!=", AB_NotEqual, AB_ERR, 1, 0, 0, C4V_Bool, C4V_Any, C4V_Any},
|
|
|
|
{ 8, "&", AB_BitAnd, AB_ERR, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 6, "^", AB_BitXOr, AB_ERR, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 6, "|", AB_BitOr, AB_ERR, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 5, "&&", AB_JUMPAND, AB_ERR, 1, 0, 0, C4V_Bool, C4V_Bool, C4V_Bool},
|
|
|
|
{ 4, "||", AB_JUMPOR, AB_ERR, 1, 0, 0, C4V_Bool, C4V_Bool, C4V_Bool},
|
2012-04-15 13:44:01 +00:00
|
|
|
{ 3, "??", AB_JUMPNNIL, AB_ERR, 1, 0, 0, C4V_Bool, C4V_Any, C4V_Any},
|
2010-04-07 13:04:19 +00:00
|
|
|
|
|
|
|
// changers
|
2010-04-08 00:47:45 +00:00
|
|
|
{ 2, "*=", AB_Mul, AB_ERR, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 2, "/=", AB_Div, AB_ERR, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 2, "%=", AB_Mod, AB_ERR, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 2, "+=", AB_Sum, AB_ERR, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 2, "-=", AB_Sub, AB_ERR, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 2, "&=", AB_BitAnd, AB_ERR, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 2, "|=", AB_BitOr, AB_ERR, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
{ 2, "^=", AB_BitXOr, AB_ERR, 1, 1, 0, C4V_Int, C4V_Int, C4V_Int},
|
|
|
|
|
2011-05-09 12:37:28 +00:00
|
|
|
{ 0, NULL, AB_ERR, AB_ERR, 0, 0, 0, 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
C4AulTokenType C4AulParse::GetNextToken(OperatorPolicy Operator)
|
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
|
|
|
|
2012-01-24 17:35:21 +00:00
|
|
|
Len = 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 (C == '*' && Operator == StarsPlease)
|
|
|
|
return ATT_STAR; // "*"
|
|
|
|
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
|
|
|
{
|
2012-01-24 17:35:21 +00:00
|
|
|
Warn("\\x used with no following hex digits");
|
|
|
|
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;
|
|
|
|
while (SPos[1] >= '0' && SPos[1] <= '7')
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-01-24 17:35:21 +00:00
|
|
|
ch *= 8;
|
|
|
|
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
|
2012-05-25 22:22:22 +00:00
|
|
|
Warn("unknown escape \"%c\"", *(SPos + 1));
|
2012-01-24 17:35:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (C == 0 || C == 10 || C == 13) // line break / feed
|
|
|
|
throw new C4AulParseError(this, "string not closed");
|
|
|
|
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 <= '~')
|
|
|
|
throw new C4AulParseError(this, FormatString("unexpected character '%c' found", C).getData());
|
|
|
|
else
|
|
|
|
throw new C4AulParseError(this, FormatString("unexpected character 0x%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
|
|
|
|
2010-04-26 20:16:45 +00:00
|
|
|
case AB_PARN_CONTEXT: return "AB_PARN_CONTEXT";
|
|
|
|
case AB_VARN_CONTEXT: return "AB_VARN_CONTEXT";
|
|
|
|
|
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"; // >=
|
2012-06-10 22:47:28 +00:00
|
|
|
case AB_Identical: return "Identical"; // ===
|
|
|
|
case AB_NotIdentical: return "NotIdentical"; // !==
|
2010-03-28 18:58:01 +00:00
|
|
|
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
|
|
|
|
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
|
|
|
|
2012-06-18 02:07:36 +00:00
|
|
|
default: assert(false);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-21 16:14:32 +00:00
|
|
|
void C4AulScriptFunc::AddBCC(C4AulBCCType eType, intptr_t X, const char * SPos)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// store chunk
|
2010-10-25 21:48:08 +00:00
|
|
|
C4AulBCC bcc;
|
|
|
|
bcc.bccType = eType;
|
|
|
|
bcc.Par.X = X;
|
|
|
|
Code.push_back(bcc);
|
|
|
|
PosForCode.push_back(SPos);
|
|
|
|
|
2009-05-09 13:17:24 +00:00
|
|
|
switch (eType)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-08-14 23:53:18 +00:00
|
|
|
case AB_STRING: case AB_CALL: case AB_CALLFS: case AB_LOCALN: case AB_PROP:
|
|
|
|
/* case AB_LOCALN_SET/AB_PROP_SET: -- expected to already have a reference upon creation, see MakeSetter */
|
2011-04-13 00:16:55 +00:00
|
|
|
bcc.Par.s->IncRef();
|
2010-03-28 18:58:01 +00:00
|
|
|
break;
|
2010-10-25 21:48:08 +00:00
|
|
|
default: break;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-21 16:14:32 +00:00
|
|
|
void C4AulScriptFunc::RemoveLastBCC()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-10-25 21:48:08 +00:00
|
|
|
C4AulBCC *pBCC = &Code.back();
|
2010-04-07 13:04:19 +00:00
|
|
|
switch (pBCC->bccType)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-08-14 23:53:18 +00:00
|
|
|
case AB_STRING: case AB_CALL: case AB_CALLFS: case AB_LOCALN: case AB_LOCALN_SET: case AB_PROP: case AB_PROP_SET:
|
2010-04-07 13:04:19 +00:00
|
|
|
pBCC->Par.s->DecRef();
|
|
|
|
break;
|
2010-10-25 21:48:08 +00:00
|
|
|
default: break;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-10-25 21:48:08 +00:00
|
|
|
Code.pop_back();
|
|
|
|
PosForCode.pop_back();
|
2010-04-07 13:04:19 +00:00
|
|
|
}
|
|
|
|
|
2012-10-21 16:14:32 +00:00
|
|
|
void C4AulScriptFunc::ClearCode()
|
2010-04-07 13:04:19 +00:00
|
|
|
{
|
2010-10-25 21:48:08 +00:00
|
|
|
while(Code.size() > 0)
|
2010-04-07 13:04:19 +00:00
|
|
|
RemoveLastBCC();
|
2012-10-21 16:14:32 +00:00
|
|
|
// This function is now broken until an AddBCC call
|
2010-10-25 21:48:08 +00:00
|
|
|
}
|
|
|
|
|
2010-12-22 01:10:58 +00:00
|
|
|
int C4AulScriptFunc::GetLineOfCode(C4AulBCC * bcc)
|
2010-10-25 21:48:08 +00:00
|
|
|
{
|
2012-10-27 21:53:42 +00:00
|
|
|
return SGetLine(pOrgScript ? pOrgScript->GetScript() : Script, PosForCode[bcc - &Code[0]]);
|
2010-12-22 01:10:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
C4AulBCC * C4AulScriptFunc::GetCode()
|
|
|
|
{
|
2012-10-21 16:14:32 +00:00
|
|
|
assert(!Code.empty());
|
|
|
|
return &Code[0];
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +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
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
C4AulParse state(this, C4AulParse::PREPARSER);
|
2013-01-12 18:13:27 +00:00
|
|
|
state.Parse_Script(this);
|
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
|
|
|
|
C4AulScript::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
|
|
|
|
2010-04-07 13:04:19 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
int C4AulParse::GetStackValue(C4AulBCCType eType, intptr_t X)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
|
|
|
switch (eType)
|
|
|
|
{
|
|
|
|
case AB_INT:
|
|
|
|
case AB_BOOL:
|
|
|
|
case AB_STRING:
|
2010-09-09 00:18:19 +00:00
|
|
|
case AB_CPROPLIST:
|
|
|
|
case AB_CARRAY:
|
2011-09-24 23:20:18 +00:00
|
|
|
case AB_CFUNCTION:
|
2010-03-28 18:58:01 +00:00
|
|
|
case AB_NIL:
|
2010-04-26 20:16:45 +00:00
|
|
|
case AB_PARN_CONTEXT:
|
|
|
|
case AB_VARN_CONTEXT:
|
2010-04-07 13:04:19 +00:00
|
|
|
case AB_LOCALN:
|
|
|
|
case AB_GLOBALN:
|
|
|
|
case AB_DUP:
|
2012-06-18 02:07:36 +00:00
|
|
|
case AB_THIS:
|
2010-04-07 13:04:19 +00:00
|
|
|
return 1;
|
2010-03-28 18:58:01 +00:00
|
|
|
|
|
|
|
case AB_Pow:
|
|
|
|
case AB_Div:
|
|
|
|
case AB_Mul:
|
|
|
|
case AB_Mod:
|
|
|
|
case AB_Sub:
|
|
|
|
case AB_Sum:
|
|
|
|
case AB_LeftShift:
|
|
|
|
case AB_RightShift:
|
|
|
|
case AB_LessThan:
|
|
|
|
case AB_LessThanEqual:
|
|
|
|
case AB_GreaterThan:
|
|
|
|
case AB_GreaterThanEqual:
|
2012-06-10 22:47:28 +00:00
|
|
|
case AB_Identical:
|
|
|
|
case AB_NotIdentical:
|
2010-03-28 18:58:01 +00:00
|
|
|
case AB_Equal:
|
|
|
|
case AB_NotEqual:
|
|
|
|
case AB_BitAnd:
|
|
|
|
case AB_BitXOr:
|
|
|
|
case AB_BitOr:
|
2010-08-14 23:53:18 +00:00
|
|
|
case AB_PROP_SET:
|
2010-04-07 13:04:19 +00:00
|
|
|
case AB_ARRAYA:
|
2010-03-28 18:58:01 +00:00
|
|
|
case AB_CONDN:
|
|
|
|
case AB_COND:
|
2011-05-02 19:53:58 +00:00
|
|
|
case AB_POP_TO:
|
2010-03-28 18:58:01 +00:00
|
|
|
case AB_RETURN:
|
2012-04-15 13:44:01 +00:00
|
|
|
// JUMPAND/JUMPOR/JUMPNNIL are special: They either jump over instructions adding one to the stack
|
2009-05-08 13:28:41 +00:00
|
|
|
// or decrement the stack. Thus, for stack counting purposes, they decrement.
|
2010-03-28 18:58:01 +00:00
|
|
|
case AB_JUMPAND:
|
|
|
|
case AB_JUMPOR:
|
2012-04-15 13:44:01 +00:00
|
|
|
case AB_JUMPNNIL:
|
2010-04-07 13:04:19 +00:00
|
|
|
return -1;
|
2010-03-28 18:58:01 +00:00
|
|
|
|
|
|
|
case AB_FUNC:
|
2010-04-07 13:04:19 +00:00
|
|
|
return -reinterpret_cast<C4AulFunc *>(X)->GetParCount() + 1;
|
2010-03-28 18:58:01 +00:00
|
|
|
|
|
|
|
case AB_CALL:
|
|
|
|
case AB_CALLFS:
|
2010-04-07 13:04:19 +00:00
|
|
|
return -C4AUL_MAX_Par;
|
2010-03-28 18:58:01 +00:00
|
|
|
|
2011-05-02 19:53:58 +00:00
|
|
|
case AB_STACK_SET:
|
2010-04-07 13:04:19 +00:00
|
|
|
case AB_LOCALN_SET:
|
2010-08-14 23:53:18 +00:00
|
|
|
case AB_PROP:
|
2010-04-07 13:04:19 +00:00
|
|
|
case AB_GLOBALN_SET:
|
|
|
|
case AB_Inc:
|
|
|
|
case AB_Dec:
|
2010-03-28 18:58:01 +00:00
|
|
|
case AB_BitNot:
|
|
|
|
case AB_Not:
|
|
|
|
case AB_Neg:
|
2010-04-07 13:04:19 +00:00
|
|
|
case AB_PAR:
|
2010-03-28 18:58:01 +00:00
|
|
|
case AB_FOREACH_NEXT:
|
|
|
|
case AB_ERR:
|
|
|
|
case AB_EOFN:
|
|
|
|
case AB_JUMP:
|
|
|
|
case AB_DEBUG:
|
2010-04-07 13:04:19 +00:00
|
|
|
return 0;
|
2010-03-28 18:58:01 +00:00
|
|
|
|
|
|
|
case AB_STACK:
|
2010-04-07 13:04:19 +00:00
|
|
|
return X;
|
2010-03-28 18:58:01 +00:00
|
|
|
|
2011-04-09 19:35:16 +00:00
|
|
|
case AB_NEW_ARRAY:
|
2010-04-07 13:04:19 +00:00
|
|
|
return -X+1;
|
2010-03-28 18:58:01 +00:00
|
|
|
|
2011-04-09 19:35:16 +00:00
|
|
|
case AB_NEW_PROPLIST:
|
|
|
|
return -X * 2 + 1;
|
|
|
|
|
2010-04-07 13:04:19 +00:00
|
|
|
case AB_ARRAYA_SET:
|
2010-04-05 15:41:36 +00:00
|
|
|
case AB_ARRAY_SLICE:
|
2010-04-07 13:04:19 +00:00
|
|
|
return -2;
|
2010-03-28 18:58:01 +00:00
|
|
|
|
2010-08-03 20:47:45 +00:00
|
|
|
case AB_ARRAY_SLICE_SET:
|
|
|
|
return -3;
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
}
|
2010-04-07 13:04:19 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
int C4AulParse::AddBCC(C4AulBCCType eType, intptr_t X)
|
2010-04-07 13:04:19 +00:00
|
|
|
{
|
2011-10-13 22:28:29 +00:00
|
|
|
if (Type != PARSER) return -1;
|
2010-04-07 13:04:19 +00:00
|
|
|
|
|
|
|
// Track stack size
|
|
|
|
iStack += GetStackValue(eType, X);
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-07-20 21:04:29 +00:00
|
|
|
// Use stack operation instead of 0-Any (enable optimization)
|
2010-03-28 18:58:01 +00:00
|
|
|
if (eType == AB_NIL)
|
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
eType = AB_STACK;
|
|
|
|
X = 1;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
// Join checks only if it's not a jump target
|
2012-10-21 16:14:32 +00:00
|
|
|
if (!fJump && Fn->GetLastCode())
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-10-21 16:14:32 +00:00
|
|
|
C4AulBCC *pCPos1 = Fn->GetLastCode();
|
2011-05-06 22:18:00 +00:00
|
|
|
|
|
|
|
// Skip noop stack operation
|
|
|
|
if(eType == AB_STACK && X == 0)
|
|
|
|
{
|
2012-10-21 16:14:32 +00:00
|
|
|
return Fn->GetCodePos() - 1;
|
2011-05-06 22:18:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Join together stack operations
|
2010-04-07 13:04:19 +00:00
|
|
|
if(eType == AB_STACK && pCPos1->bccType == AB_STACK &&
|
|
|
|
(X <= 0 || pCPos1->Par.i >= 0))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-07 13:04:19 +00:00
|
|
|
pCPos1->Par.i += X;
|
2011-05-06 22:18:00 +00:00
|
|
|
// Empty? Remove it. This relies on the parser not issuing
|
|
|
|
// multiple negative stack operations consecutively, as
|
|
|
|
// that could result in removing a jump target bytecode.
|
2010-04-07 13:04:19 +00:00
|
|
|
if (!pCPos1->Par.i)
|
2012-10-21 16:14:32 +00:00
|
|
|
Fn->RemoveLastBCC();
|
|
|
|
return Fn->GetCodePos() - 1;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-04-07 13:04:19 +00:00
|
|
|
|
2010-04-08 00:47:45 +00:00
|
|
|
// Prune unneeded Incs / Decs
|
|
|
|
if(eType == AB_STACK && X < 0 && (pCPos1->bccType == AB_Inc || pCPos1->bccType == AB_Dec))
|
2011-03-28 18:58:42 +00:00
|
|
|
{
|
2010-04-08 00:47:45 +00:00
|
|
|
if(C4ScriptOpMap[pCPos1->Par.i].ResultModifier != pCPos1->bccType)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-08 00:47:45 +00:00
|
|
|
pCPos1->bccType = eType;
|
|
|
|
pCPos1->Par.i = X;
|
2012-10-21 16:14:32 +00:00
|
|
|
return Fn->GetCodePos() - 1;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-04-08 00:47:45 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// If it was a result modifier, we can safely remove it knowing that it was neither
|
|
|
|
// the first chunk nor a jump target. We can therefore apply additional optimizations.
|
2012-10-21 16:14:32 +00:00
|
|
|
Fn->RemoveLastBCC();
|
2010-04-08 00:47:45 +00:00
|
|
|
pCPos1--;
|
|
|
|
}
|
2011-03-28 18:58:42 +00:00
|
|
|
}
|
2010-04-08 00:47:45 +00:00
|
|
|
|
2011-05-02 19:53:58 +00:00
|
|
|
// Join STACK_SET + STACK -1 to POP_TO (equivalent)
|
|
|
|
if(eType == AB_STACK && X == -1 && pCPos1->bccType == AB_STACK_SET)
|
2010-04-07 13:04:19 +00:00
|
|
|
{
|
2011-05-02 19:53:58 +00:00
|
|
|
pCPos1->bccType = AB_POP_TO;
|
2012-10-21 16:14:32 +00:00
|
|
|
return Fn->GetCodePos() - 1;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-04-07 13:04:19 +00:00
|
|
|
|
|
|
|
// Reduce some constructs like SUM + INT 1 to INC or DEC
|
|
|
|
if((eType == AB_Sum || eType == AB_Sub) &&
|
|
|
|
pCPos1->bccType == AB_INT &&
|
|
|
|
(pCPos1->Par.i == 1 || pCPos1->Par.i == -1))
|
|
|
|
{
|
|
|
|
if((pCPos1->Par.i > 0) == (eType == AB_Sum))
|
|
|
|
pCPos1->bccType = AB_Inc;
|
|
|
|
else
|
|
|
|
pCPos1->bccType = AB_Dec;
|
|
|
|
pCPos1->Par.i = X;
|
2012-10-21 16:14:32 +00:00
|
|
|
return Fn->GetCodePos() - 1;
|
2010-04-07 13:04:19 +00:00
|
|
|
}
|
|
|
|
|
2011-10-13 22:28:32 +00:00
|
|
|
// Reduce Not + CONDN to COND
|
|
|
|
if(eType == AB_CONDN && pCPos1->bccType == AB_Not)
|
|
|
|
{
|
|
|
|
pCPos1->bccType = AB_COND;
|
|
|
|
pCPos1->Par.i = X;
|
2012-10-21 16:14:32 +00:00
|
|
|
return Fn->GetCodePos() - 1;
|
2011-10-13 22:28:32 +00:00
|
|
|
}
|
|
|
|
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
// Add
|
2012-10-21 16:14:32 +00:00
|
|
|
Fn->AddBCC(eType, X, TokenSPos);
|
2009-05-08 13:28:41 +00:00
|
|
|
|
|
|
|
// Reset jump flag
|
|
|
|
fJump = false;
|
2011-10-13 22:28:29 +00:00
|
|
|
|
2012-10-21 16:14:32 +00:00
|
|
|
return Fn->GetCodePos() - 1;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::RemoveLastBCC()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-07 13:04:19 +00:00
|
|
|
// Security: This is unsafe on anything that might get optimized away
|
2012-10-21 16:14:32 +00:00
|
|
|
C4AulBCC *pBCC = Fn->GetLastCode();
|
2010-04-07 13:04:19 +00:00
|
|
|
assert(pBCC->bccType != AB_STACK);
|
|
|
|
// Correct stack
|
2010-09-22 00:14:16 +00:00
|
|
|
iStack -= GetStackValue(pBCC->bccType, pBCC->Par.X);
|
2010-04-07 13:04:19 +00:00
|
|
|
// Remove
|
2012-10-21 16:14:32 +00:00
|
|
|
Fn->RemoveLastBCC();
|
2010-04-07 13:04:19 +00:00
|
|
|
}
|
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
C4V_Type C4AulParse::GetLastRetType(C4V_Type to)
|
2011-05-01 16:53:50 +00:00
|
|
|
{
|
|
|
|
C4V_Type from;
|
2012-10-21 16:14:32 +00:00
|
|
|
switch (Fn->GetLastCode()->bccType)
|
2011-05-01 16:53:50 +00:00
|
|
|
{
|
2012-10-21 16:14:32 +00:00
|
|
|
case AB_INT: from = Config.Developer.ExtraWarnings || Fn->GetLastCode()->Par.i ? C4V_Int : C4V_Any; break;
|
2011-05-01 16:53:50 +00:00
|
|
|
case AB_STRING: from = C4V_String; break;
|
|
|
|
case AB_NEW_ARRAY: case AB_CARRAY: case AB_ARRAY_SLICE: from = C4V_Array; break;
|
2011-09-24 23:20:18 +00:00
|
|
|
case AB_CFUNCTION: from = C4V_Function; break;
|
2011-05-01 16:53:50 +00:00
|
|
|
case AB_NEW_PROPLIST: case AB_CPROPLIST: from = C4V_PropList; break;
|
|
|
|
case AB_BOOL: from = C4V_Bool; break;
|
|
|
|
case AB_FUNC:
|
2012-10-21 16:14:32 +00:00
|
|
|
from = Fn->GetLastCode()->Par.f->GetRetType(); break;
|
2011-05-01 16:53:50 +00:00
|
|
|
case AB_CALL: case AB_CALLFS:
|
|
|
|
{
|
2012-10-21 16:14:32 +00:00
|
|
|
C4String * pName = Fn->GetLastCode()->Par.s;
|
2012-10-27 23:52:18 +00:00
|
|
|
C4AulFunc * pFunc2 = Engine->GetFirstFunc(pName);
|
2011-05-01 16:53:50 +00:00
|
|
|
bool allwarn = true;
|
|
|
|
from = C4V_Any;
|
|
|
|
while (pFunc2 && allwarn)
|
|
|
|
{
|
|
|
|
from = pFunc2->GetRetType();
|
|
|
|
if (!C4Value::WarnAboutConversion(from, to))
|
|
|
|
{
|
|
|
|
allwarn = false;
|
|
|
|
from = C4V_Any;
|
|
|
|
}
|
2012-10-27 23:52:18 +00:00
|
|
|
pFunc2 = Engine->GetNextSNFunc(pFunc2);
|
2011-05-01 16:53:50 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case AB_Inc: case AB_Dec: case AB_BitNot: case AB_Neg:
|
|
|
|
case AB_Pow: case AB_Div: case AB_Mul: case AB_Mod: case AB_Sub: case AB_Sum:
|
|
|
|
case AB_LeftShift: case AB_RightShift: case AB_BitAnd: case AB_BitXOr: case AB_BitOr:
|
|
|
|
from = C4V_Int; break;
|
2012-06-10 22:47:28 +00:00
|
|
|
case AB_Not: case AB_LessThan: case AB_LessThanEqual: case AB_GreaterThan: case AB_GreaterThanEqual:
|
|
|
|
case AB_Identical: case AB_NotIdentical: case AB_Equal: case AB_NotEqual:
|
2011-05-01 16:53:50 +00:00
|
|
|
from = C4V_Bool; break;
|
|
|
|
default:
|
|
|
|
from = C4V_Any; break;
|
|
|
|
}
|
|
|
|
return from;
|
|
|
|
}
|
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
C4AulBCC C4AulParse::MakeSetter(bool fLeaveValue)
|
2010-04-07 13:04:19 +00:00
|
|
|
{
|
|
|
|
if(Type != PARSER) { C4AulBCC Dummy; Dummy.bccType = AB_ERR; return Dummy; }
|
2012-10-21 16:14:32 +00:00
|
|
|
C4AulBCC Value = *(Fn->GetLastCode()), Setter = Value;
|
2010-04-07 13:04:19 +00:00
|
|
|
// Check type
|
|
|
|
switch (Value.bccType)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-07 13:04:19 +00:00
|
|
|
case AB_ARRAYA: Setter.bccType = AB_ARRAYA_SET; break;
|
2010-08-03 20:47:45 +00:00
|
|
|
case AB_ARRAY_SLICE: Setter.bccType = AB_ARRAY_SLICE_SET; break;
|
2011-05-02 19:53:58 +00:00
|
|
|
case AB_DUP:
|
|
|
|
Setter.bccType = AB_STACK_SET;
|
|
|
|
// the setter additionally has the new value on the stack
|
|
|
|
--Setter.Par.i;
|
|
|
|
break;
|
2010-04-07 13:04:19 +00:00
|
|
|
case AB_LOCALN:
|
|
|
|
Setter.bccType = AB_LOCALN_SET;
|
|
|
|
Setter.Par.s->IncRef(); // so string isn't dropped by RemoveLastBCC, see also C4AulScript::AddBCC
|
|
|
|
break;
|
2010-08-14 23:53:18 +00:00
|
|
|
case AB_PROP:
|
|
|
|
Setter.bccType = AB_PROP_SET;
|
|
|
|
Setter.Par.s->IncRef(); // so string isn't dropped by RemoveLastBCC, see also C4AulScript::AddBCC
|
|
|
|
break;
|
2010-04-07 13:04:19 +00:00
|
|
|
case AB_GLOBALN: Setter.bccType = AB_GLOBALN_SET; break;
|
|
|
|
default:
|
2011-04-10 00:05:11 +00:00
|
|
|
throw new C4AulParseError(this, "assignment to a constant");
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-04-07 13:04:19 +00:00
|
|
|
// Remove value BCC
|
|
|
|
RemoveLastBCC();
|
|
|
|
// Want the value?
|
|
|
|
if(fLeaveValue)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-07 13:04:19 +00:00
|
|
|
// Duplicate parameters on stack
|
|
|
|
// (all push one value on the stack as result, so we have -(N-1) parameters)
|
2010-09-22 00:14:16 +00:00
|
|
|
int iParCount = -GetStackValue(Value.bccType, Value.Par.X) + 1;
|
2010-04-07 13:04:19 +00:00
|
|
|
for(int i = 0; i < iParCount; i++)
|
2011-05-02 19:53:58 +00:00
|
|
|
AddBCC(AB_DUP, 1 - iParCount);
|
2010-04-07 13:04:19 +00:00
|
|
|
// Finally re-add original BCC
|
2010-09-22 00:14:16 +00:00
|
|
|
AddBCC(Value.bccType, Value.Par.X);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-04-07 13:04:19 +00:00
|
|
|
// Done. The returned BCC should be added later once the value to be set was pushed on top.
|
2010-09-22 00:14:16 +00:00
|
|
|
assert(GetStackValue(Value.bccType, Value.Par.X) == GetStackValue(Setter.bccType, Setter.Par.X)+1);
|
2010-04-07 13:04:19 +00:00
|
|
|
return Setter;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
int C4AulParse::JumpHere()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// Set flag so the next generated code chunk won't get joined
|
|
|
|
fJump = true;
|
2012-10-21 16:14:32 +00:00
|
|
|
return Fn->GetCodePos();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2009-08-28 02:44:53 +00:00
|
|
|
static bool IsJump(C4AulBCCType t)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-04-15 13:44:01 +00:00
|
|
|
return t == AB_JUMP || t == AB_JUMPAND || t == AB_JUMPOR || t == AB_JUMPNNIL || t == AB_CONDN || t == AB_COND;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-08-28 02:44:53 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::SetJumpHere(int iJumpOp)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
if (Type != PARSER) return;
|
|
|
|
// Set target
|
2012-10-21 16:14:32 +00:00
|
|
|
C4AulBCC *pBCC = Fn->GetCodeByPos(iJumpOp);
|
2009-08-28 02:44:53 +00:00
|
|
|
assert(IsJump(pBCC->bccType));
|
2012-10-21 16:14:32 +00:00
|
|
|
pBCC->Par.i = Fn->GetCodePos() - iJumpOp;
|
2009-05-08 13:28:41 +00:00
|
|
|
// Set flag so the next generated code chunk won't get joined
|
|
|
|
fJump = true;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::SetJump(int iJumpOp, int iWhere)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
if (Type != PARSER) return;
|
|
|
|
// Set target
|
2012-10-21 16:14:32 +00:00
|
|
|
C4AulBCC *pBCC = Fn->GetCodeByPos(iJumpOp);
|
2009-08-28 02:44:53 +00:00
|
|
|
assert(IsJump(pBCC->bccType));
|
2009-05-09 13:17:24 +00:00
|
|
|
pBCC->Par.i = iWhere - iJumpOp;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::AddJump(C4AulBCCType eType, int iWhere)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-10-21 16:14:32 +00:00
|
|
|
AddBCC(eType, iWhere - Fn->GetCodePos());
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::PushLoop()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
if (Type != PARSER) return;
|
|
|
|
Loop *pNew = new Loop();
|
|
|
|
pNew->StackSize = iStack;
|
|
|
|
pNew->Controls = NULL;
|
|
|
|
pNew->Next = pLoopStack;
|
|
|
|
pLoopStack = pNew;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::PopLoop()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
if (Type != PARSER) return;
|
|
|
|
// Delete loop controls
|
|
|
|
Loop *pLoop = pLoopStack;
|
2010-03-28 18:58:01 +00:00
|
|
|
while (pLoop->Controls)
|
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// Unlink
|
|
|
|
Loop::Control *pCtrl = pLoop->Controls;
|
|
|
|
pLoop->Controls = pCtrl->Next;
|
|
|
|
// Delete
|
|
|
|
delete pCtrl;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
// Unlink & delete
|
|
|
|
pLoopStack = pLoop->Next;
|
|
|
|
delete pLoop;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::AddLoopControl(bool fBreak)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
if (Type != PARSER) return;
|
|
|
|
Loop::Control *pNew = new Loop::Control();
|
|
|
|
pNew->Break = fBreak;
|
2012-10-21 16:14:32 +00:00
|
|
|
pNew->Pos = Fn->GetCodePos();
|
2009-05-08 13:28:41 +00:00
|
|
|
pNew->Next = pLoopStack->Controls;
|
|
|
|
pLoopStack->Controls = pNew;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
const char * C4AulParse::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_STAR: return "'*'";
|
|
|
|
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
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Shift(OperatorPolicy Operator)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-02-19 21:37:42 +00:00
|
|
|
TokenType = GetNextToken(Operator);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Match(C4AulTokenType RefTokenType, const char * Message)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
if (TokenType != RefTokenType)
|
|
|
|
// error
|
|
|
|
throw new C4AulParseError(this, Message ? Message :
|
2010-03-28 18:58:01 +00:00
|
|
|
FormatString("%s expected, but found %s", GetTokenName(RefTokenType), GetTokenName(TokenType)).getData());
|
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
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
throw new 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
|
|
|
|
2012-01-26 23:42:41 +00:00
|
|
|
void C4AulScriptFunc::ParseFn(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
|
2013-01-12 18:13:27 +00:00
|
|
|
C4AulParse state(this, context, C4AulParse::PARSER);
|
|
|
|
state.Parse_DirectExec();
|
|
|
|
}
|
|
|
|
|
|
|
|
void C4AulParse::Parse_DirectExec()
|
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// get first token
|
2013-01-12 18:13:27 +00:00
|
|
|
Shift();
|
|
|
|
Parse_Expression();
|
|
|
|
Match(ATT_EOF);
|
|
|
|
AddBCC(AB_RETURN);
|
|
|
|
AddBCC(AB_EOFN);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2013-01-12 18:13:27 +00:00
|
|
|
void 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;
|
|
|
|
bool all_ok = true;
|
|
|
|
bool found_code = false;
|
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:
|
|
|
|
if (found_code)
|
|
|
|
Warn("Found %s after declarations", Idtf);
|
|
|
|
// 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
|
|
|
|
if (TokenType != ATT_IDTF)
|
|
|
|
UnexpectedToken("identifier");
|
|
|
|
if (Type == PREPARSER)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-09-04 00:11:16 +00:00
|
|
|
// add to include list
|
2012-10-27 23:52:18 +00:00
|
|
|
Host->Includes.push_back(C4ID(StdStrBuf(Idtf)));
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
Shift();
|
|
|
|
}
|
|
|
|
else if (SEqual(Idtf, C4AUL_Append))
|
|
|
|
{
|
|
|
|
if (pOrgScript->GetPropList()->GetDef())
|
|
|
|
throw new C4AulParseError(this, "#appendto in a Definition");
|
|
|
|
// for #appendto * '*' needs to be ATT_STAR, not an operator.
|
|
|
|
Shift(StarsPlease);
|
|
|
|
if (Type == PREPARSER)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-09-04 00:11:16 +00:00
|
|
|
// get id of script to include/append
|
|
|
|
C4ID Id;
|
|
|
|
switch (TokenType)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-09-04 00:11:16 +00:00
|
|
|
case ATT_IDTF:
|
|
|
|
Id = C4ID(StdStrBuf(Idtf));
|
|
|
|
break;
|
|
|
|
case ATT_STAR: // "*"
|
|
|
|
Id = C4ID::None;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// -> ID expected
|
|
|
|
UnexpectedToken("identifier or '*'");
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
// add to append list
|
2012-10-27 23:52:18 +00:00
|
|
|
Host->Appends.push_back(Id);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
Shift();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
// -> unknown directive
|
|
|
|
throw new C4AulParseError(this, "unknown directive: ", Idtf);
|
|
|
|
break;
|
|
|
|
case ATT_IDTF:
|
|
|
|
// need a keyword here to avoid parsing random function contents
|
|
|
|
// after a syntax error in a function
|
|
|
|
found_code = true;
|
|
|
|
// check for object-local variable definition (local)
|
|
|
|
if (SEqual(Idtf, C4AUL_LocalNamed))
|
|
|
|
{
|
|
|
|
Parse_Local();
|
|
|
|
Match(ATT_SCOLON);
|
2009-05-08 13:28:41 +00:00
|
|
|
break;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
// check for variable definition (static)
|
|
|
|
else if (SEqual(Idtf, C4AUL_GlobalNamed))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-09-04 00:11:16 +00:00
|
|
|
Parse_Static();
|
|
|
|
Match(ATT_SCOLON);
|
2009-05-08 13:28:41 +00:00
|
|
|
break;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
else
|
|
|
|
Parse_Function();
|
|
|
|
break;
|
|
|
|
case ATT_EOF:
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
UnexpectedToken("declaration");
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-09-09 00:18:19 +00:00
|
|
|
all_ok = true;
|
|
|
|
}
|
|
|
|
catch (C4AulError *err)
|
|
|
|
{
|
|
|
|
// damn! something went wrong, print it out
|
|
|
|
// but only one error per function
|
|
|
|
if (all_ok)
|
2012-01-26 23:42:41 +00:00
|
|
|
{
|
2010-09-09 00:18:19 +00:00
|
|
|
err->show();
|
2012-01-26 23:42:41 +00:00
|
|
|
// and count (visible only ;) )
|
|
|
|
++::ScriptEngine.errCnt;
|
|
|
|
}
|
2010-09-09 00:18:19 +00:00
|
|
|
all_ok = false;
|
|
|
|
delete err;
|
2012-01-26 23:42:41 +00:00
|
|
|
|
|
|
|
if (Fn)
|
|
|
|
{
|
|
|
|
// make all jumps that don't have their destination yet jump here
|
2012-10-21 16:14:32 +00:00
|
|
|
for (unsigned int i = 0; i < Fn->Code.size(); i++)
|
2012-01-26 23:42:41 +00:00
|
|
|
{
|
2012-10-21 16:14:32 +00:00
|
|
|
C4AulBCC *pBCC = &Fn->Code[i];
|
2012-01-26 23:42:41 +00:00
|
|
|
if (IsJump(pBCC->bccType))
|
|
|
|
if (!pBCC->Par.i)
|
2012-10-21 16:14:32 +00:00
|
|
|
pBCC->Par.i = Fn->Code.size() - i;
|
2012-01-26 23:42:41 +00:00
|
|
|
}
|
|
|
|
// add an error chunk
|
|
|
|
AddBCC(AB_ERR);
|
|
|
|
}
|
2010-09-09 00:18:19 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_Function()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
C4AulAccess Acc = AA_PUBLIC;
|
|
|
|
// Access?
|
|
|
|
if (SEqual(Idtf, C4AUL_Private)) { Acc = AA_PRIVATE; Shift(); }
|
|
|
|
else if (SEqual(Idtf, C4AUL_Protected)) { Acc = AA_PROTECTED; Shift(); }
|
|
|
|
else if (SEqual(Idtf, C4AUL_Public)) { Acc = AA_PUBLIC; Shift(); }
|
|
|
|
else if (SEqual(Idtf, C4AUL_Global)) { Acc = AA_GLOBAL; Shift(); }
|
|
|
|
// check for func declaration
|
2011-04-17 23:50:33 +00:00
|
|
|
if (!SEqual(Idtf, C4AUL_Func))
|
|
|
|
throw new C4AulParseError(this, "Declaration expected, but found identifier ", Idtf);
|
|
|
|
Shift();
|
|
|
|
// get next token, must be func name
|
|
|
|
if (TokenType != ATT_IDTF)
|
|
|
|
UnexpectedToken("function name");
|
|
|
|
// check: symbol already in use?
|
|
|
|
switch (Acc)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-04-17 23:50:33 +00:00
|
|
|
case AA_PRIVATE:
|
|
|
|
case AA_PROTECTED:
|
|
|
|
case AA_PUBLIC:
|
2012-10-27 23:52:18 +00:00
|
|
|
if (Host->LocalNamed.GetItemNr(Idtf) != -1)
|
2011-04-17 23:50:33 +00:00
|
|
|
throw new C4AulParseError(this, "function definition: name already in use (local variable)");
|
2012-10-27 23:52:18 +00:00
|
|
|
if (Host->GetPropList())
|
2011-04-17 23:50:33 +00:00
|
|
|
break;
|
|
|
|
// func in global context: fallthru
|
|
|
|
case AA_GLOBAL:
|
2012-10-27 23:52:18 +00:00
|
|
|
if (Host != pOrgScript)
|
2012-04-13 19:43:43 +00:00
|
|
|
throw new C4AulParseError(this, "global func in appendto/included script: ", Idtf);
|
2012-10-27 23:52:18 +00:00
|
|
|
if (Engine->GlobalNamedNames.GetItemNr(Idtf) != -1)
|
2011-04-17 23:50:33 +00:00
|
|
|
throw new C4AulParseError(this, "function definition: name already in use (global variable)");
|
2012-10-27 23:52:18 +00:00
|
|
|
if (Engine->GlobalConstNames.GetItemNr(Idtf) != -1)
|
2012-05-25 22:22:22 +00:00
|
|
|
Error("function definition: name already in use (global constant)");
|
2011-04-17 23:50:33 +00:00
|
|
|
}
|
2012-02-02 00:16:14 +00:00
|
|
|
// get script fn
|
|
|
|
C4AulScript * owner;
|
|
|
|
if (Acc == AA_GLOBAL)
|
2012-10-27 23:52:18 +00:00
|
|
|
owner = Engine;
|
2012-02-02 00:16:14 +00:00
|
|
|
else
|
2012-10-27 23:52:18 +00:00
|
|
|
owner = Host;
|
2012-02-02 00:16:14 +00:00
|
|
|
Fn = 0;
|
2012-05-27 22:31:55 +00:00
|
|
|
C4AulFunc * f = owner->GetPropList()->GetFunc(Idtf);
|
2012-02-02 00:16:14 +00:00
|
|
|
while (f)
|
2011-04-17 23:50:33 +00:00
|
|
|
{
|
2012-05-27 22:31:55 +00:00
|
|
|
if (f->SFunc() && f->SFunc()->pOrgScript == pOrgScript && f->Owner == owner)
|
2012-01-26 23:42:41 +00:00
|
|
|
{
|
2012-02-02 00:16:14 +00:00
|
|
|
if (Fn)
|
|
|
|
//throw new C4AulParseError(this, "Duplicate function ", Idtf);
|
2012-05-25 22:22:22 +00:00
|
|
|
Warn("Duplicate function %s", Idtf);
|
2012-02-02 00:16:14 +00:00
|
|
|
Fn = f->SFunc();
|
2012-01-26 23:42:41 +00:00
|
|
|
}
|
2012-05-27 22:31:55 +00:00
|
|
|
f = f->SFunc() ? f->SFunc()->OwnerOverloaded : 0;
|
2011-04-17 23:50:33 +00:00
|
|
|
}
|
2012-02-02 00:16:14 +00:00
|
|
|
// first preparser run or a new func in a reloaded script
|
|
|
|
if (!Fn && Type == PREPARSER)
|
|
|
|
{
|
|
|
|
Fn = new C4AulScriptFunc(owner, pOrgScript, Idtf, SPos);
|
2012-05-27 22:31:55 +00:00
|
|
|
Fn->SetOverloaded(owner->GetPropList()->GetFunc(Fn->Name));
|
|
|
|
owner->GetPropList()->SetPropertyByS(Fn->Name, C4VFunction(Fn));
|
2012-02-02 00:16:14 +00:00
|
|
|
}
|
|
|
|
assert(Fn);
|
|
|
|
if (Type == PARSER)
|
2011-04-17 23:50:33 +00:00
|
|
|
{
|
2012-10-21 16:14:32 +00:00
|
|
|
Fn->ClearCode();
|
2011-04-17 23:50:33 +00:00
|
|
|
}
|
2012-02-02 00:16:14 +00:00
|
|
|
|
|
|
|
// Parse function body
|
2011-04-17 23:50:33 +00:00
|
|
|
Shift();
|
|
|
|
// expect an opening bracket now
|
|
|
|
if (TokenType != ATT_BOPEN)
|
|
|
|
UnexpectedToken("'('");
|
|
|
|
Shift();
|
|
|
|
// get pars
|
|
|
|
int cpar = 0;
|
2012-01-26 23:42:41 +00:00
|
|
|
while (TokenType != ATT_BCLOSE)
|
2011-04-17 23:50:33 +00:00
|
|
|
{
|
|
|
|
// too many parameters?
|
|
|
|
if (cpar >= C4AUL_MAX_Par)
|
|
|
|
throw new C4AulParseError(this, "'func' parameter list: too many parameters (max 10)");
|
|
|
|
// must be a name or type now
|
2009-05-08 13:28:41 +00:00
|
|
|
if (TokenType != ATT_IDTF)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-04-17 23:50:33 +00:00
|
|
|
UnexpectedToken("parameter or closing bracket");
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
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(); }
|
|
|
|
Fn->ParType[cpar] = t;
|
2011-09-24 16:37:28 +00:00
|
|
|
if (TokenType == ATT_BCLOSE || TokenType == ATT_COMMA)
|
|
|
|
{
|
2012-01-26 23:42:41 +00:00
|
|
|
if (Type == PREPARSER) Fn->AddPar(Idtf);
|
2011-09-24 16:37:28 +00:00
|
|
|
if (Config.Developer.ExtraWarnings)
|
2012-10-21 09:21:49 +00:00
|
|
|
Warn("'%s' used as parameter name", Idtf);
|
2011-09-24 16:37:28 +00:00
|
|
|
}
|
|
|
|
else if (TokenType != ATT_IDTF)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-04-17 23:50:33 +00:00
|
|
|
UnexpectedToken("parameter name");
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
else
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-01-26 23:42:41 +00:00
|
|
|
if (Type == PREPARSER) Fn->AddPar(Idtf);
|
2011-04-17 23:50:33 +00:00
|
|
|
Shift();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
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
|
|
|
|
if (TokenType != ATT_COMMA)
|
|
|
|
UnexpectedToken("comma or closing bracket");
|
|
|
|
Shift();
|
|
|
|
cpar++;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2012-01-26 23:42:41 +00:00
|
|
|
Match(ATT_BCLOSE);
|
2011-04-17 23:50:33 +00:00
|
|
|
Match(ATT_BLOPEN);
|
2011-04-17 21:52:55 +00:00
|
|
|
// Push variables
|
|
|
|
if (Fn->VarNamed.iSize)
|
|
|
|
AddBCC(AB_STACK, Fn->VarNamed.iSize);
|
2009-05-08 13:28:41 +00:00
|
|
|
iStack = 0;
|
2011-12-22 20:57:17 +00:00
|
|
|
while (TokenType != ATT_BLCLOSE)
|
2011-04-17 21:52:55 +00:00
|
|
|
{
|
2011-12-22 20:57:17 +00:00
|
|
|
Parse_Statement();
|
|
|
|
assert(!iStack);
|
|
|
|
}
|
|
|
|
// return nil if the function doesn't return anything
|
2012-10-21 16:14:32 +00:00
|
|
|
C4AulBCC * CPos = Fn->GetLastCode();
|
2011-12-22 20:57:17 +00:00
|
|
|
if (!CPos || CPos->bccType != AB_RETURN || fJump)
|
|
|
|
{
|
|
|
|
if (C4AulDebug::GetDebugger())
|
|
|
|
AddBCC(AB_DEBUG);
|
|
|
|
AddBCC(AB_NIL);
|
|
|
|
AddBCC(AB_RETURN);
|
2011-04-17 21:52:55 +00:00
|
|
|
}
|
2012-01-26 23:42:41 +00:00
|
|
|
// add separator
|
|
|
|
AddBCC(AB_EOFN);
|
2012-05-27 22:50:13 +00:00
|
|
|
|
|
|
|
// dump bytecode
|
|
|
|
if (DEBUG_BYTECODE_DUMP && Type == PARSER)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s:\n", Fn->GetName());
|
|
|
|
std::map<C4AulBCC *, int> labels;
|
|
|
|
int labeln = 0;
|
|
|
|
for (C4AulBCC *pBCC = Fn->GetCode(); pBCC->bccType != AB_EOFN; pBCC++)
|
|
|
|
{
|
|
|
|
switch (pBCC->bccType)
|
|
|
|
{
|
|
|
|
case AB_JUMP: case AB_JUMPAND: case AB_JUMPOR: case AB_JUMPNNIL: case AB_CONDN: case AB_COND:
|
|
|
|
labels[pBCC + pBCC->Par.i] = ++labeln; break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (C4AulBCC *pBCC = Fn->GetCode();; pBCC++)
|
|
|
|
{
|
|
|
|
C4AulBCCType eType = pBCC->bccType;
|
|
|
|
if (labels.find(pBCC) != labels.end())
|
|
|
|
fprintf(stderr, "%d:\n", labels[pBCC]);
|
|
|
|
fprintf(stderr, "\t%d\t%s", Fn->GetLineOfCode(pBCC), GetTTName(eType));
|
|
|
|
switch (eType)
|
|
|
|
{
|
|
|
|
case AB_FUNC:
|
|
|
|
fprintf(stderr, "\t%s\n", pBCC->Par.f->GetName()); break;
|
|
|
|
case AB_CALL: case AB_CALLFS: case AB_LOCALN: case AB_LOCALN_SET: case AB_PROP: case AB_PROP_SET:
|
|
|
|
fprintf(stderr, "\t%s\n", pBCC->Par.s->GetCStr()); break;
|
|
|
|
case AB_STRING:
|
|
|
|
fprintf(stderr, "\t\"%s\"\n", pBCC->Par.s->GetCStr()); break;
|
|
|
|
case AB_DEBUG: case AB_NIL: case AB_RETURN:
|
2012-06-18 02:07:36 +00:00
|
|
|
case AB_PAR: case AB_THIS:
|
2012-05-27 22:50:13 +00:00
|
|
|
case AB_ARRAYA: case AB_ARRAYA_SET: case AB_ARRAY_SLICE: case AB_ARRAY_SLICE_SET:
|
2012-10-21 16:14:32 +00:00
|
|
|
case AB_ERR: case AB_EOFN:
|
2012-05-27 22:50:13 +00:00
|
|
|
assert(!pBCC->Par.X); fprintf(stderr, "\n"); break;
|
2012-10-21 13:51:34 +00:00
|
|
|
case AB_CARRAY:
|
|
|
|
fprintf(stderr, "\t%s\n", C4VArray(pBCC->Par.a).GetDataString().getData()); break;
|
|
|
|
case AB_CPROPLIST:
|
|
|
|
fprintf(stderr, "\t%s\n", C4VPropList(pBCC->Par.p).GetDataString().getData()); break;
|
2012-05-27 22:50:13 +00:00
|
|
|
case AB_JUMP: case AB_JUMPAND: case AB_JUMPOR: case AB_JUMPNNIL: case AB_CONDN: case AB_COND:
|
|
|
|
fprintf(stderr, "\t%d\n", labels[pBCC + pBCC->Par.i]); break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr, "\t%d\n", pBCC->Par.i); break;
|
|
|
|
}
|
|
|
|
if (eType == AB_EOFN) break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-22 20:57:17 +00:00
|
|
|
// Do not blame this function for script errors between functions
|
|
|
|
Fn = 0;
|
2012-01-26 23:42:41 +00:00
|
|
|
Shift();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_Block()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
Match(ATT_BLOPEN);
|
2011-12-22 20:57:17 +00:00
|
|
|
while (TokenType != ATT_BLCLOSE)
|
|
|
|
{
|
|
|
|
Parse_Statement();
|
|
|
|
}
|
|
|
|
Shift();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-08-26 14:59:00 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_Statement()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-04-10 01:40:55 +00:00
|
|
|
if (C4AulDebug::GetDebugger())
|
|
|
|
AddBCC(AB_DEBUG);
|
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:
|
|
|
|
Parse_Block();
|
|
|
|
return;
|
|
|
|
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:
|
2010-03-28 18:58:01 +00:00
|
|
|
Parse_Expression();
|
|
|
|
AddBCC(AB_STACK, -1);
|
|
|
|
Match(ATT_SCOLON);
|
|
|
|
return;
|
2010-04-01 21:08:06 +00:00
|
|
|
// additional function separator
|
2010-03-28 18:58:01 +00:00
|
|
|
case ATT_SCOLON:
|
|
|
|
Shift();
|
|
|
|
break;
|
|
|
|
case ATT_IDTF:
|
|
|
|
// check for variable definition (var)
|
|
|
|
if (SEqual(Idtf, C4AUL_VarNamed))
|
|
|
|
Parse_Var();
|
|
|
|
// check for variable definition (local)
|
|
|
|
else if (SEqual(Idtf, C4AUL_LocalNamed))
|
|
|
|
Parse_Local();
|
|
|
|
// check for variable definition (static)
|
|
|
|
else if (SEqual(Idtf, C4AUL_GlobalNamed))
|
|
|
|
Parse_Static();
|
|
|
|
// 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))
|
|
|
|
{
|
|
|
|
throw new C4AulParseError(this, "unexpected end of function");
|
|
|
|
}
|
|
|
|
// get function by identifier: first check special functions
|
|
|
|
else if (SEqual(Idtf, C4AUL_If)) // if
|
|
|
|
{
|
|
|
|
Parse_If();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (SEqual(Idtf, C4AUL_Else)) // else
|
|
|
|
{
|
|
|
|
throw new C4AulParseError(this, "misplaced 'else'");
|
|
|
|
}
|
|
|
|
else if (SEqual(Idtf, C4AUL_Do)) // while
|
|
|
|
{
|
|
|
|
Parse_DoWhile();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (SEqual(Idtf, C4AUL_While)) // while
|
|
|
|
{
|
|
|
|
Parse_While();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (SEqual(Idtf, C4AUL_For)) // for
|
|
|
|
{
|
|
|
|
Shift();
|
|
|
|
// Look if it's the for([var] foo in array)-form
|
|
|
|
const char * SPos0 = SPos;
|
|
|
|
// must be followed by a bracket
|
|
|
|
Match(ATT_BOPEN);
|
|
|
|
// optional var
|
|
|
|
if (TokenType == ATT_IDTF && SEqual(Idtf, C4AUL_VarNamed))
|
|
|
|
Shift();
|
|
|
|
// variable and "in"
|
|
|
|
if (TokenType == ATT_IDTF /*&& (iVarID = Fn->VarNamed.GetItemNr(Idtf)) != -1*/
|
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
|
|
|
|
SPos = SPos0;
|
|
|
|
Shift();
|
|
|
|
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
|
|
|
|
SPos = SPos0;
|
|
|
|
Shift();
|
|
|
|
Parse_For();
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
break;
|
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)
|
|
|
|
AddBCC(AB_NIL);
|
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;
|
|
|
|
Parse_Expression();
|
|
|
|
}
|
|
|
|
AddBCC(AB_RETURN);
|
|
|
|
}
|
|
|
|
else if (SEqual(Idtf, C4AUL_Break)) // break
|
|
|
|
{
|
|
|
|
Shift();
|
|
|
|
if (Type == PARSER)
|
|
|
|
{
|
|
|
|
// Must be inside a loop
|
|
|
|
if (!pLoopStack)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
Error("'break' is only allowed inside loops");
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// Insert code
|
|
|
|
if (pLoopStack->StackSize != iStack)
|
|
|
|
AddBCC(AB_STACK, pLoopStack->StackSize - iStack);
|
|
|
|
AddLoopControl(true);
|
|
|
|
AddBCC(AB_JUMP);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (SEqual(Idtf, C4AUL_Continue)) // continue
|
|
|
|
{
|
|
|
|
Shift();
|
|
|
|
if (Type == PARSER)
|
|
|
|
{
|
|
|
|
// Must be inside a loop
|
|
|
|
if (!pLoopStack)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
Error("'continue' is only allowed inside loops");
|
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
|
|
|
// Insert code
|
|
|
|
if (pLoopStack->StackSize != iStack)
|
|
|
|
AddBCC(AB_STACK, pLoopStack->StackSize - iStack);
|
|
|
|
AddLoopControl(false);
|
|
|
|
AddBCC(AB_JUMP);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
Parse_Expression();
|
|
|
|
AddBCC(AB_STACK, -1);
|
|
|
|
}
|
|
|
|
Match(ATT_SCOLON);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
UnexpectedToken("statement");
|
|
|
|
}
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
int C4AulParse::Parse_Params(int iMaxCnt, const char * sWarn, C4AulFunc * pFunc)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
int size = 0;
|
|
|
|
// so it's a regular function; force "("
|
|
|
|
Match(ATT_BOPEN);
|
|
|
|
bool fDone = false;
|
2011-05-02 21:52:41 +00:00
|
|
|
do switch (TokenType)
|
|
|
|
{
|
2012-09-04 00:11:16 +00:00
|
|
|
case ATT_BCLOSE:
|
|
|
|
Shift();
|
|
|
|
if (size > 0)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-02-02 21:06:53 +00:00
|
|
|
if (sWarn && Config.Developer.ExtraWarnings)
|
|
|
|
Warn(FormatString("parameter %d of call to %s is empty", size, sWarn).getData(), NULL);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
fDone = true;
|
|
|
|
break;
|
|
|
|
case ATT_COMMA:
|
|
|
|
// got no parameter before a ","
|
|
|
|
if (sWarn && Config.Developer.ExtraWarnings)
|
|
|
|
Warn(FormatString("parameter %d of call to %s is empty", size, sWarn).getData(), NULL);
|
|
|
|
AddBCC(AB_NIL);
|
|
|
|
Shift();
|
|
|
|
++size;
|
|
|
|
break;
|
|
|
|
case ATT_LDOTS:
|
|
|
|
// functions using ... always take as many parameters as possible
|
|
|
|
assert(Type == PREPARSER || Fn->ParCount == C4AUL_MAX_Par);
|
|
|
|
Fn->ParCount = C4AUL_MAX_Par;
|
|
|
|
Shift();
|
|
|
|
// Push all unnamed parameters of the current function as parameters
|
|
|
|
for (int i = Fn->ParNamed.iSize; i < C4AUL_MAX_Par; ++i)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-09-04 00:11:16 +00:00
|
|
|
AddBCC(AB_DUP, 1 + i - (iStack + Fn->VarNamed.iSize + Fn->GetParCount()));
|
|
|
|
++size;
|
|
|
|
if (size >= iMaxCnt)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Do not allow more parameters even if there is place left
|
|
|
|
fDone = true;
|
|
|
|
Match(ATT_BCLOSE);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// get a parameter
|
|
|
|
Parse_Expression();
|
|
|
|
if (pFunc && (Type == PARSER) && size < iMaxCnt)
|
|
|
|
{
|
|
|
|
C4V_Type to = pFunc->GetParType()[size];
|
|
|
|
// pFunc either is the return value from a GetFirstFunc-Call or
|
|
|
|
// the only function that could be called. When in doubt, don't warn.
|
|
|
|
C4AulFunc * pFunc2 = pFunc;
|
2012-10-27 23:52:18 +00:00
|
|
|
while ((pFunc2 = Engine->GetNextSNFunc(pFunc2)))
|
2012-09-04 00:11:16 +00:00
|
|
|
if (pFunc2->GetParType()[size] != to) to = C4V_Any;
|
|
|
|
C4V_Type from = GetLastRetType(to);
|
|
|
|
if (C4Value::WarnAboutConversion(from, to))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-09-04 00:11:16 +00:00
|
|
|
Warn(FormatString("parameter %d of call to %s is %s instead of %s", size, sWarn, GetC4VName(from), GetC4VName(to)).getData(), NULL);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
++size;
|
|
|
|
// end of parameter list?
|
|
|
|
if (TokenType == ATT_COMMA)
|
|
|
|
Shift();
|
|
|
|
else if (TokenType == ATT_BCLOSE)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-09-04 00:11:16 +00:00
|
|
|
Shift();
|
|
|
|
fDone = true;
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
else UnexpectedToken("',' or ')'");
|
|
|
|
break;
|
|
|
|
} while (!fDone);
|
2009-05-08 13:28:41 +00:00
|
|
|
// too many parameters?
|
2010-03-28 18:58:01 +00:00
|
|
|
if (sWarn && size > iMaxCnt && Type == PARSER)
|
2011-02-02 21:06:53 +00:00
|
|
|
Warn(FormatString("call to %s gives %d parameters, but only %d are used", sWarn, size, iMaxCnt).getData(), NULL);
|
2012-05-27 22:50:13 +00:00
|
|
|
// Balance stack// FIXME: not for CALL/FUNC
|
2010-03-28 18:58:01 +00:00
|
|
|
if (size != iMaxCnt)
|
2009-05-08 13:28:41 +00:00
|
|
|
AddBCC(AB_STACK, iMaxCnt - size);
|
|
|
|
return size;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_Array()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// force "["
|
|
|
|
Match(ATT_BOPEN2);
|
|
|
|
// Create an array
|
|
|
|
int size = 0;
|
|
|
|
bool fDone = false;
|
2012-09-04 00:11:16 +00:00
|
|
|
do switch (TokenType)
|
|
|
|
{
|
|
|
|
case ATT_BCLOSE2:
|
|
|
|
Shift();
|
|
|
|
// [] -> size 0, [*,] -> size 2, [*,*,] -> size 3
|
|
|
|
if (size > 0)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-02-02 21:07:46 +00:00
|
|
|
if (Config.Developer.ExtraWarnings)
|
|
|
|
Warn(FormatString("array entry %d is empty", size).getData(), NULL);
|
2009-07-17 02:50:50 +00:00
|
|
|
AddBCC(AB_NIL);
|
2009-05-08 13:28:41 +00:00
|
|
|
++size;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
fDone = true;
|
|
|
|
break;
|
|
|
|
case ATT_COMMA:
|
|
|
|
// got no parameter before a ","? then push nil
|
|
|
|
if (Config.Developer.ExtraWarnings)
|
|
|
|
Warn(FormatString("array entry %d is empty", size).getData(), NULL);
|
|
|
|
AddBCC(AB_NIL);
|
|
|
|
Shift();
|
|
|
|
++size;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Parse_Expression();
|
|
|
|
++size;
|
|
|
|
if (TokenType == ATT_COMMA)
|
|
|
|
Shift();
|
|
|
|
else if (TokenType == ATT_BCLOSE2)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-09-04 00:11:16 +00:00
|
|
|
Shift();
|
|
|
|
fDone = true;
|
|
|
|
break;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
else
|
|
|
|
UnexpectedToken("',' or ']'");
|
|
|
|
} while (!fDone);
|
2009-05-08 13:28:41 +00:00
|
|
|
// add terminator
|
2011-04-09 19:35:16 +00:00
|
|
|
AddBCC(AB_NEW_ARRAY, size);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_PropList()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-04-09 19:35:16 +00:00
|
|
|
int size = 0;
|
2011-11-07 00:39:43 +00:00
|
|
|
if (TokenType == ATT_IDTF && SEqual(Idtf, C4AUL_New))
|
|
|
|
{
|
|
|
|
Shift();
|
|
|
|
AddBCC(AB_STRING, (intptr_t) &Strings.P[P_Prototype]);
|
|
|
|
Parse_Expression();
|
2012-02-25 21:10:03 +00:00
|
|
|
C4V_Type from = GetLastRetType(C4V_PropList);
|
|
|
|
if (C4Value::WarnAboutConversion(from, C4V_PropList))
|
|
|
|
{
|
|
|
|
Warn(FormatString("Prototype is %s instead of %s", GetC4VName(from), GetC4VName(C4V_PropList)).getData(), NULL);
|
|
|
|
}
|
2012-10-21 16:14:32 +00:00
|
|
|
if (Fn->GetLastCode()->bccType == AB_CPROPLIST && Fn->GetLastCode()->Par.p->GetDef())
|
2012-02-25 21:10:03 +00:00
|
|
|
{
|
|
|
|
throw new C4AulParseError(this, "Can't use new on definitions yet.");
|
|
|
|
}
|
2011-11-07 00:39:43 +00:00
|
|
|
++size;
|
|
|
|
}
|
|
|
|
Match(ATT_BLOPEN);
|
2010-09-09 00:18:19 +00:00
|
|
|
while (TokenType != ATT_BLCLOSE)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-04-12 00:56:41 +00:00
|
|
|
C4String * pKey;
|
|
|
|
if (TokenType == ATT_IDTF)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-04-12 00:56:41 +00:00
|
|
|
pKey = Strings.RegString(Idtf);
|
|
|
|
AddBCC(AB_STRING, (intptr_t) pKey);
|
|
|
|
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
|
|
|
{
|
2011-02-21 23:07:08 +00:00
|
|
|
AddBCC(AB_STRING, reinterpret_cast<intptr_t>(cStr));
|
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();
|
|
|
|
Parse_Expression();
|
2011-04-09 19:35:16 +00:00
|
|
|
++size;
|
2009-04-12 00:56:41 +00:00
|
|
|
if (TokenType == ATT_COMMA)
|
|
|
|
Shift();
|
|
|
|
else if (TokenType != ATT_BLCLOSE)
|
|
|
|
UnexpectedToken("'}' or ','");
|
|
|
|
}
|
2011-04-09 19:35:16 +00:00
|
|
|
AddBCC(AB_NEW_PROPLIST, size);
|
2010-09-09 00:18:19 +00:00
|
|
|
Shift();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-04-12 00:56:41 +00:00
|
|
|
|
2012-05-27 16:00:45 +00:00
|
|
|
C4Value C4AulParse::Parse_ConstPropList(const C4PropListStatic * parent, C4String * Name)
|
2011-11-07 00:39:43 +00:00
|
|
|
{
|
2013-02-12 00:11:31 +00:00
|
|
|
C4Value v;
|
2012-05-27 16:00:45 +00:00
|
|
|
if (!Name)
|
|
|
|
throw new C4AulParseError(this, "a static proplist is not allowed to be anonymous");
|
2012-06-01 15:27:59 +00:00
|
|
|
C4PropListStatic * p;
|
2012-05-27 16:00:45 +00:00
|
|
|
if (Type == PREPARSER)
|
2012-06-01 15:27:59 +00:00
|
|
|
{
|
2012-08-15 17:43:02 +00:00
|
|
|
p = C4PropList::NewStatic(NULL, parent, Name);
|
2012-06-01 15:27:59 +00:00
|
|
|
}
|
2012-05-27 16:00:45 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
bool r;
|
|
|
|
if (parent)
|
|
|
|
r = parent->GetPropertyByS(Name, &v);
|
|
|
|
else
|
2012-10-27 23:52:18 +00:00
|
|
|
r = Engine->GetGlobalConstant(Name->GetCStr(), &v);
|
2012-05-27 16:00:45 +00:00
|
|
|
if (!r || !v.getPropList())
|
|
|
|
{
|
|
|
|
// the proplist couldn't be parsed or was overwritten by a later constant.
|
2013-02-12 00:11:31 +00:00
|
|
|
// create a temporary replacement, make v hold the reference to it for now
|
2012-08-15 17:43:02 +00:00
|
|
|
v.SetPropList(C4PropList::NewStatic(NULL, parent, Name));
|
2012-05-27 16:00:45 +00:00
|
|
|
}
|
|
|
|
p = v.getPropList()->IsStatic();
|
|
|
|
if (!p)
|
|
|
|
throw new C4AulParseError(this, "internal error: constant proplist is not static");
|
|
|
|
if (p->GetParent() != parent || p->GetParentKeyName() != Name)
|
|
|
|
{
|
2012-08-08 23:21:54 +00:00
|
|
|
throw new C4AulParseError(this, "internal error: constant proplist has the wrong parent");
|
2012-05-27 16:00:45 +00:00
|
|
|
}
|
|
|
|
// In case of script reloads
|
|
|
|
p->Thaw();
|
|
|
|
}
|
|
|
|
Shift();
|
2011-11-07 00:39:43 +00:00
|
|
|
while (TokenType != ATT_BLCLOSE)
|
|
|
|
{
|
|
|
|
C4String * pKey;
|
|
|
|
if (TokenType == ATT_IDTF)
|
|
|
|
{
|
2012-05-27 16:00:45 +00:00
|
|
|
pKey = Strings.RegString(Idtf);
|
2011-11-07 00:39:43 +00:00
|
|
|
Shift();
|
|
|
|
}
|
|
|
|
else if (TokenType == ATT_STRING)
|
|
|
|
{
|
|
|
|
pKey = cStr;
|
|
|
|
Shift();
|
|
|
|
}
|
|
|
|
else UnexpectedToken("string or identifier");
|
|
|
|
if (TokenType != ATT_COLON && TokenType != ATT_SET)
|
|
|
|
UnexpectedToken("':' or '='");
|
|
|
|
Shift();
|
2012-05-27 16:00:45 +00:00
|
|
|
Parse_ConstExpression(p, pKey);
|
2011-11-07 00:39:43 +00:00
|
|
|
if (TokenType == ATT_COMMA)
|
|
|
|
Shift();
|
|
|
|
else if (TokenType != ATT_BLCLOSE)
|
|
|
|
UnexpectedToken("'}' or ','");
|
|
|
|
}
|
2012-05-27 16:00:45 +00:00
|
|
|
if (Type == PARSER)
|
2012-06-01 15:27:59 +00:00
|
|
|
p->Freeze();
|
2012-05-27 16:00:45 +00:00
|
|
|
return C4VPropList(p);
|
2011-11-07 00:39:43 +00:00
|
|
|
}
|
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_DoWhile()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-10-23 23:00:29 +00:00
|
|
|
Shift();
|
2009-08-28 02:44:53 +00:00
|
|
|
// Save position for later jump back
|
2013-01-08 20:21:11 +00:00
|
|
|
int Start = JumpHere();
|
2009-08-28 02:44:53 +00:00
|
|
|
// We got a loop
|
|
|
|
PushLoop();
|
|
|
|
// Execute body
|
|
|
|
Parse_Statement();
|
2013-01-08 20:21:11 +00:00
|
|
|
int BeforeCond = JumpHere();
|
2009-08-28 02:44:53 +00:00
|
|
|
// Execute condition
|
|
|
|
if (TokenType != ATT_IDTF || !SEqual(Idtf, C4AUL_While))
|
|
|
|
UnexpectedToken("'while'");
|
|
|
|
Shift();
|
|
|
|
Match(ATT_BOPEN);
|
|
|
|
Parse_Expression();
|
|
|
|
Match(ATT_BCLOSE);
|
|
|
|
// Jump back
|
2013-01-08 20:21:11 +00:00
|
|
|
AddJump(AB_COND, Start);
|
2009-08-28 02:44:53 +00:00
|
|
|
if (Type != PARSER) return;
|
|
|
|
// Set targets for break/continue
|
2010-03-28 18:58:01 +00:00
|
|
|
for (Loop::Control *pCtrl = pLoopStack->Controls; pCtrl; pCtrl = pCtrl->Next)
|
|
|
|
if (pCtrl->Break)
|
2009-08-28 02:44:53 +00:00
|
|
|
SetJumpHere(pCtrl->Pos);
|
|
|
|
else
|
2013-01-08 20:21:11 +00:00
|
|
|
SetJump(pCtrl->Pos, BeforeCond);
|
2009-08-28 02:44:53 +00:00
|
|
|
PopLoop();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-08-28 02:44:53 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_While()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-10-23 23:00:29 +00:00
|
|
|
Shift();
|
2009-05-08 13:28:41 +00:00
|
|
|
// Save position for later jump back
|
|
|
|
int iStart = JumpHere();
|
|
|
|
// Execute condition
|
2009-07-22 23:02:41 +00:00
|
|
|
Match(ATT_BOPEN);
|
|
|
|
Parse_Expression();
|
|
|
|
Match(ATT_BCLOSE);
|
2009-05-08 13:28:41 +00:00
|
|
|
// Check condition
|
2011-10-13 22:28:29 +00:00
|
|
|
int iCond = AddBCC(AB_CONDN);
|
2009-05-08 13:28:41 +00:00
|
|
|
// We got a loop
|
|
|
|
PushLoop();
|
|
|
|
// Execute body
|
|
|
|
Parse_Statement();
|
|
|
|
if (Type != PARSER) return;
|
|
|
|
// Jump back
|
|
|
|
AddJump(AB_JUMP, iStart);
|
|
|
|
// Set target for conditional jump
|
|
|
|
SetJumpHere(iCond);
|
|
|
|
// Set targets for break/continue
|
2010-03-28 18:58:01 +00:00
|
|
|
for (Loop::Control *pCtrl = pLoopStack->Controls; pCtrl; pCtrl = pCtrl->Next)
|
|
|
|
if (pCtrl->Break)
|
2009-05-08 13:28:41 +00:00
|
|
|
SetJumpHere(pCtrl->Pos);
|
|
|
|
else
|
|
|
|
SetJump(pCtrl->Pos, iStart);
|
|
|
|
PopLoop();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_If()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-10-23 23:00:29 +00:00
|
|
|
Shift();
|
2009-07-22 23:02:41 +00:00
|
|
|
Match(ATT_BOPEN);
|
|
|
|
Parse_Expression();
|
|
|
|
Match(ATT_BCLOSE);
|
2009-05-08 13:28:41 +00:00
|
|
|
// create bytecode, remember position
|
2011-10-13 22:28:29 +00:00
|
|
|
int iCond = AddBCC(AB_CONDN);
|
2009-05-08 13:28:41 +00:00
|
|
|
// parse controlled statement
|
|
|
|
Parse_Statement();
|
|
|
|
if (TokenType == ATT_IDTF && SEqual(Idtf, C4AUL_Else))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// add jump
|
2011-10-13 22:28:29 +00:00
|
|
|
int iJump = AddBCC(AB_JUMP);
|
2009-05-08 13:28:41 +00:00
|
|
|
// set condition jump target
|
|
|
|
SetJumpHere(iCond);
|
|
|
|
Shift();
|
|
|
|
// expect a command now
|
|
|
|
Parse_Statement();
|
|
|
|
// set jump target
|
|
|
|
SetJumpHere(iJump);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
else
|
|
|
|
// set condition jump target
|
|
|
|
SetJumpHere(iCond);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_For()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
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))
|
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
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
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
Parse_Expression();
|
|
|
|
AddBCC(AB_STACK, -1);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
// Consume first semicolon
|
|
|
|
Match(ATT_SCOLON);
|
|
|
|
// Condition
|
|
|
|
int iCondition = -1, iJumpBody = -1, iJumpOut = -1;
|
|
|
|
if (TokenType != ATT_SCOLON)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// Add condition code
|
|
|
|
iCondition = JumpHere();
|
|
|
|
Parse_Expression();
|
|
|
|
// Jump out
|
2011-10-13 22:28:29 +00:00
|
|
|
iJumpOut = AddBCC(AB_CONDN);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
// Consume second semicolon
|
|
|
|
Match(ATT_SCOLON);
|
|
|
|
// Incrementor
|
|
|
|
int iIncrementor = -1;
|
|
|
|
if (TokenType != ATT_BCLOSE)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// Must jump over incrementor
|
2011-10-13 22:28:29 +00:00
|
|
|
iJumpBody = AddBCC(AB_JUMP);
|
2009-05-08 13:28:41 +00:00
|
|
|
// Add incrementor code
|
|
|
|
iIncrementor = JumpHere();
|
|
|
|
Parse_Expression();
|
|
|
|
AddBCC(AB_STACK, -1);
|
|
|
|
// Jump to condition
|
|
|
|
if (iCondition != -1)
|
|
|
|
AddJump(AB_JUMP, iCondition);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
// Consume closing bracket
|
|
|
|
Match(ATT_BCLOSE);
|
|
|
|
// Allow break/continue from now on
|
|
|
|
PushLoop();
|
|
|
|
// Body
|
|
|
|
int iBody = JumpHere();
|
2010-03-28 18:58:01 +00:00
|
|
|
if (iJumpBody != -1)
|
2009-05-08 13:28:41 +00:00
|
|
|
SetJumpHere(iJumpBody);
|
|
|
|
Parse_Statement();
|
|
|
|
if (Type != PARSER) return;
|
|
|
|
// Where to jump back?
|
|
|
|
int iJumpBack;
|
2010-03-28 18:58:01 +00:00
|
|
|
if (iIncrementor != -1)
|
2009-05-08 13:28:41 +00:00
|
|
|
iJumpBack = iIncrementor;
|
2010-03-28 18:58:01 +00:00
|
|
|
else if (iCondition != -1)
|
2009-05-08 13:28:41 +00:00
|
|
|
iJumpBack = iCondition;
|
|
|
|
else
|
|
|
|
iJumpBack = iBody;
|
|
|
|
AddJump(AB_JUMP, iJumpBack);
|
|
|
|
// Set target for condition
|
2010-03-28 18:58:01 +00:00
|
|
|
if (iJumpOut != -1)
|
2009-05-08 13:28:41 +00:00
|
|
|
SetJumpHere(iJumpOut);
|
|
|
|
// Set targets for break/continue
|
2010-03-28 18:58:01 +00:00
|
|
|
for (Loop::Control *pCtrl = pLoopStack->Controls; pCtrl; pCtrl = pCtrl->Next)
|
|
|
|
if (pCtrl->Break)
|
2009-05-08 13:28:41 +00:00
|
|
|
SetJumpHere(pCtrl->Pos);
|
|
|
|
else
|
|
|
|
SetJump(pCtrl->Pos, iJumpBack);
|
|
|
|
PopLoop();
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_ForEach()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
if (TokenType == ATT_IDTF && SEqual(Idtf, C4AUL_VarNamed))
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
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
|
|
|
|
if (TokenType != ATT_IDTF)
|
|
|
|
UnexpectedToken("variable name");
|
|
|
|
if (Type == PREPARSER)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// insert variable
|
|
|
|
Fn->VarNamed.AddName(Idtf);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
// search variable (fail if not found)
|
|
|
|
int iVarID = Fn->VarNamed.GetItemNr(Idtf);
|
2010-03-28 18:58:01 +00:00
|
|
|
if (iVarID < 0)
|
2009-05-08 13:28:41 +00:00
|
|
|
throw new C4AulParseError(this, "internal error: var definition: var not found in variable table");
|
|
|
|
Shift();
|
|
|
|
if (TokenType != ATT_IDTF || !SEqual(Idtf, C4AUL_In))
|
|
|
|
UnexpectedToken("'in'");
|
|
|
|
Shift();
|
|
|
|
// get expression for array
|
|
|
|
Parse_Expression();
|
|
|
|
Match(ATT_BCLOSE);
|
|
|
|
// push initial position (0)
|
|
|
|
AddBCC(AB_INT);
|
|
|
|
// get array element
|
2011-10-13 22:28:29 +00:00
|
|
|
int iStart = AddBCC(AB_FOREACH_NEXT, iVarID);
|
2009-05-08 13:28:41 +00:00
|
|
|
// jump out (FOREACH_NEXT will jump over this if
|
|
|
|
// we're not at the end of the array yet)
|
2011-10-13 22:28:29 +00:00
|
|
|
int iCond = AddBCC(AB_JUMP);
|
2009-05-08 13:28:41 +00:00
|
|
|
// got a loop...
|
|
|
|
PushLoop();
|
|
|
|
// loop body
|
|
|
|
Parse_Statement();
|
|
|
|
if (Type != PARSER) return;
|
|
|
|
// jump back
|
|
|
|
AddJump(AB_JUMP, iStart);
|
|
|
|
// set condition jump target
|
|
|
|
SetJumpHere(iCond);
|
|
|
|
// set jump targets for break/continue
|
2010-03-28 18:58:01 +00:00
|
|
|
for (Loop::Control *pCtrl = pLoopStack->Controls; pCtrl; pCtrl = pCtrl->Next)
|
|
|
|
if (pCtrl->Break)
|
2009-05-08 13:28:41 +00:00
|
|
|
SetJumpHere(pCtrl->Pos);
|
|
|
|
else
|
|
|
|
SetJump(pCtrl->Pos, iStart);
|
|
|
|
PopLoop();
|
|
|
|
// remove array and counter from stack
|
|
|
|
AddBCC(AB_STACK, -2);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_Expression(int iParentPrio)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-09-04 00:11:16 +00:00
|
|
|
int ndx;
|
2012-10-21 14:13:53 +00:00
|
|
|
C4ScriptOpDef * op;
|
2012-08-13 17:38:17 +00:00
|
|
|
C4AulFunc *FoundFn = 0;
|
|
|
|
C4Value val;
|
2009-05-08 13:28:41 +00:00
|
|
|
switch (TokenType)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
|
|
|
case ATT_IDTF:
|
|
|
|
// check for parameter (par)
|
|
|
|
if (Fn->ParNamed.GetItemNr(Idtf) != -1)
|
|
|
|
{
|
|
|
|
// insert variable by id
|
2011-04-17 23:48:37 +00:00
|
|
|
AddBCC(AB_DUP, 1 + Fn->ParNamed.GetItemNr(Idtf) - (iStack + Fn->VarNamed.iSize + Fn->GetParCount()));
|
2010-03-28 18:58:01 +00:00
|
|
|
Shift();
|
|
|
|
}
|
|
|
|
// check for variable (var)
|
|
|
|
else if (Fn->VarNamed.GetItemNr(Idtf) != -1)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// insert variable by id
|
2011-05-02 19:53:58 +00:00
|
|
|
AddBCC(AB_DUP, 1 + Fn->VarNamed.GetItemNr(Idtf) - (iStack + Fn->VarNamed.iSize));
|
2010-03-28 18:58:01 +00:00
|
|
|
Shift();
|
|
|
|
}
|
2010-04-26 20:16:45 +00:00
|
|
|
else if (ContextToExecIn && (ndx = ContextToExecIn->Func->ParNamed.GetItemNr(Idtf)) != -1)
|
|
|
|
{
|
|
|
|
AddBCC(AB_PARN_CONTEXT, ndx);
|
|
|
|
Shift();
|
|
|
|
}
|
|
|
|
else if (ContextToExecIn && (ndx = ContextToExecIn->Func->VarNamed.GetItemNr(Idtf)) != -1)
|
|
|
|
{
|
|
|
|
AddBCC(AB_VARN_CONTEXT, ndx);
|
|
|
|
Shift();
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
// check for variable (local)
|
2012-10-27 23:52:18 +00:00
|
|
|
else if (Host && Host->LocalNamed.GetItemNr(Idtf) != -1)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
|
|
|
// global func?
|
|
|
|
if (Fn->Owner == &::ScriptEngine)
|
|
|
|
throw new C4AulParseError(this, "using local variable in global function!");
|
|
|
|
// insert variable by id
|
|
|
|
C4String * pKey = Strings.RegString(Idtf);
|
2010-04-07 13:04:19 +00:00
|
|
|
AddBCC(AB_LOCALN, (intptr_t) pKey);
|
2010-03-28 18:58:01 +00:00
|
|
|
Shift();
|
|
|
|
}
|
2010-10-19 22:37:31 +00:00
|
|
|
else if (SEqual(Idtf, C4AUL_True))
|
|
|
|
{
|
|
|
|
AddBCC(AB_BOOL, 1);
|
|
|
|
Shift();
|
|
|
|
}
|
|
|
|
else if (SEqual(Idtf, C4AUL_False))
|
|
|
|
{
|
|
|
|
AddBCC(AB_BOOL, 0);
|
|
|
|
Shift();
|
|
|
|
}
|
|
|
|
else if (SEqual(Idtf, C4AUL_Nil))
|
|
|
|
{
|
|
|
|
AddBCC(AB_NIL);
|
|
|
|
Shift();
|
|
|
|
}
|
2011-11-07 00:39:43 +00:00
|
|
|
else if (SEqual(Idtf, C4AUL_New))
|
|
|
|
{
|
|
|
|
Parse_PropList();
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
// function identifier: check special functions
|
|
|
|
else if (SEqual(Idtf, C4AUL_If))
|
|
|
|
// -> if is not a valid parameter
|
|
|
|
throw new C4AulParseError(this, "'if' may not be used as a parameter");
|
|
|
|
else if (SEqual(Idtf, C4AUL_While))
|
|
|
|
// -> while is not a valid parameter
|
|
|
|
throw new C4AulParseError(this, "'while' may not be used as a parameter");
|
|
|
|
else if (SEqual(Idtf, C4AUL_Else))
|
|
|
|
// -> else is not a valid parameter
|
|
|
|
throw new C4AulParseError(this, "misplaced 'else'");
|
|
|
|
else if (SEqual(Idtf, C4AUL_For))
|
|
|
|
// -> for is not a valid parameter
|
|
|
|
throw new C4AulParseError(this, "'for' may not be used as a parameter");
|
|
|
|
else if (SEqual(Idtf, C4AUL_Return))
|
|
|
|
{
|
2012-05-25 22:22:22 +00:00
|
|
|
Error("return may not be used as a parameter");
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
|
|
|
else if (SEqual(Idtf, C4AUL_Par))
|
|
|
|
{
|
2011-04-17 23:48:37 +00:00
|
|
|
// functions using Par() always take as many parameters as possible
|
|
|
|
Fn->ParCount = C4AUL_MAX_Par;
|
2010-03-28 18:58:01 +00:00
|
|
|
// and for Par
|
|
|
|
Shift();
|
2012-08-13 17:07:56 +00:00
|
|
|
Match(ATT_BOPEN);
|
|
|
|
Parse_Expression();
|
|
|
|
Match(ATT_BCLOSE);
|
2010-04-07 13:04:19 +00:00
|
|
|
AddBCC(AB_PAR);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-06-18 02:07:36 +00:00
|
|
|
else if (SEqual(Idtf, C4AUL_this))
|
|
|
|
{
|
|
|
|
Shift();
|
|
|
|
if (TokenType == ATT_BOPEN)
|
|
|
|
{
|
2012-08-13 17:07:56 +00:00
|
|
|
Shift();
|
|
|
|
Match(ATT_BCLOSE);
|
2012-06-18 02:07:36 +00:00
|
|
|
}
|
|
|
|
AddBCC(AB_THIS);
|
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
else if (SEqual(Idtf, C4AUL_Inherited) || SEqual(Idtf, C4AUL_SafeInherited))
|
|
|
|
{
|
|
|
|
Shift();
|
|
|
|
// get function
|
|
|
|
if (Fn->OwnerOverloaded)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// add direct call to byte code
|
|
|
|
Parse_Params(Fn->OwnerOverloaded->GetParCount(), NULL, Fn->OwnerOverloaded);
|
2010-04-18 20:02:01 +00:00
|
|
|
AddBCC(AB_FUNC, (intptr_t) Fn->OwnerOverloaded);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
// not found? raise an error, if it's not a safe call
|
|
|
|
if (SEqual(Idtf, C4AUL_Inherited) && Type == PARSER)
|
|
|
|
throw new C4AulParseError(this, "inherited function not found (use _inherited to disable this message)");
|
|
|
|
else
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2010-03-28 18:58:01 +00:00
|
|
|
// otherwise, parse parameters, but discard them
|
|
|
|
Parse_Params(0, NULL);
|
|
|
|
// Push a null as return value
|
|
|
|
AddBCC(AB_STACK, 1);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-08-13 17:38:17 +00:00
|
|
|
else if (Type == PREPARSER)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-08-13 17:38:17 +00:00
|
|
|
Shift();
|
|
|
|
// The preparser just assumes that the syntax is correct and all identifiers
|
|
|
|
// will be defined: if no '(' follows, it must be a variable or constant,
|
|
|
|
// otherwise a function with parameters
|
|
|
|
if (TokenType == ATT_BOPEN)
|
|
|
|
Parse_Params(10, NULL);
|
|
|
|
}
|
|
|
|
else if ((FoundFn = Fn->Owner->GetPropList()->GetFunc(Idtf)))
|
|
|
|
{
|
|
|
|
assert(Host == Fn->Owner || Fn->Owner == Engine || (Host && !Host->GetPropList()));
|
|
|
|
if (Config.Developer.ExtraWarnings && !FoundFn->GetPublic())
|
|
|
|
Warn("using deprecated function %s", Idtf);
|
|
|
|
Shift();
|
|
|
|
Parse_Params(FoundFn->GetParCount(), FoundFn->GetName(), FoundFn);
|
|
|
|
AddBCC(AB_FUNC, (intptr_t) FoundFn);
|
|
|
|
}
|
|
|
|
// -> func not found
|
|
|
|
// check for global variables (static) or constants (static const)
|
|
|
|
// the global namespace has the lowest priority so that local
|
|
|
|
// functions and variables can overload it
|
|
|
|
else if ((ndx = Engine->GlobalNamedNames.GetItemNr(Idtf)) != -1)
|
|
|
|
{
|
|
|
|
// insert variable by id
|
|
|
|
AddBCC(AB_GLOBALN, ndx);
|
|
|
|
Shift();
|
|
|
|
}
|
|
|
|
else if (Engine->GetGlobalConstant(Idtf, &val))
|
|
|
|
{
|
|
|
|
// store as direct constant
|
|
|
|
switch (val.GetType())
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-08-13 17:38:17 +00:00
|
|
|
case C4V_Nil: AddBCC(AB_NIL, 0); break;
|
|
|
|
case C4V_Int: AddBCC(AB_INT, val._getInt()); break;
|
|
|
|
case C4V_Bool: AddBCC(AB_BOOL, val._getBool()); break;
|
|
|
|
case C4V_String:
|
|
|
|
AddBCC(AB_STRING, reinterpret_cast<intptr_t>(val._getStr()));
|
|
|
|
break;
|
|
|
|
case C4V_PropList:
|
|
|
|
AddBCC(AB_CPROPLIST, reinterpret_cast<intptr_t>(val._getPropList()));
|
|
|
|
break;
|
|
|
|
case C4V_Array:
|
|
|
|
AddBCC(AB_CARRAY, reinterpret_cast<intptr_t>(val._getArray()));
|
|
|
|
break;
|
|
|
|
case C4V_Function:
|
|
|
|
AddBCC(AB_CFUNCTION, reinterpret_cast<intptr_t>(val._getFunction()));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new C4AulParseError(this, FormatString("internal error: constant %s has unsupported type %d", Idtf, val.GetType()).getData());
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2012-08-13 17:38:17 +00:00
|
|
|
Shift();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// identifier could not be resolved
|
|
|
|
throw new C4AulParseError(this, "unknown identifier: ", Idtf);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ATT_INT: // constant in cInt
|
|
|
|
AddBCC(AB_INT, cInt);
|
|
|
|
Shift();
|
|
|
|
break;
|
2011-02-21 23:07:08 +00:00
|
|
|
case ATT_STRING: // reference in cStr
|
|
|
|
AddBCC(AB_STRING, reinterpret_cast<intptr_t>(cStr));
|
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
|
|
|
|
throw new C4AulParseError(this, "postfix operator without first expression");
|
|
|
|
Shift();
|
|
|
|
// generate code for the following expression
|
2012-10-21 14:13:53 +00:00
|
|
|
Parse_Expression(op->Priority);
|
2011-05-01 16:53:50 +00:00
|
|
|
{
|
2012-10-21 14:13:53 +00:00
|
|
|
C4V_Type to = op->Type1;
|
|
|
|
C4V_Type from = GetLastRetType(to);
|
|
|
|
if (C4Value::WarnAboutConversion(from, to))
|
|
|
|
{
|
|
|
|
Warn(FormatString("operator \"%s\" gets %s instead of %s", op->Identifier, GetC4VName(from), GetC4VName(to)).getData(), NULL);
|
|
|
|
}
|
2011-05-01 16:53:50 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
// ignore?
|
2012-10-21 14:13:53 +00:00
|
|
|
if (SEqual(op->Identifier, "+"))
|
2009-07-17 02:50:50 +00:00
|
|
|
break;
|
2010-03-28 18:58:01 +00:00
|
|
|
// negate constant?
|
2012-10-21 14:13:53 +00:00
|
|
|
if (Type == PARSER && SEqual(op->Identifier, "-"))
|
2012-10-21 16:14:32 +00:00
|
|
|
if (Fn->GetLastCode()->bccType == AB_INT)
|
2009-05-08 13:28:41 +00:00
|
|
|
{
|
2012-10-21 16:14:32 +00:00
|
|
|
Fn->GetLastCode()->Par.i = - Fn->GetLastCode()->Par.i;
|
2009-05-08 13:28:41 +00:00
|
|
|
break;
|
|
|
|
}
|
2010-04-07 13:04:19 +00:00
|
|
|
// changer? make a setter BCC, leave value for operator
|
|
|
|
C4AulBCC Changer;
|
2012-10-21 14:13:53 +00:00
|
|
|
if(op->Changer)
|
2010-04-07 13:04:19 +00:00
|
|
|
Changer = MakeSetter(true);
|
2010-03-28 18:58:01 +00:00
|
|
|
// write byte code
|
2012-11-05 17:17:51 +00:00
|
|
|
AddBCC(op->Code, 0);
|
2010-04-07 13:04:19 +00:00
|
|
|
// writter setter
|
2012-10-21 14:13:53 +00:00
|
|
|
if(op->Changer)
|
2010-09-22 00:14:16 +00:00
|
|
|
AddBCC(Changer.bccType, Changer.Par.X);
|
2010-03-28 18:58:01 +00:00
|
|
|
break;
|
|
|
|
case ATT_BOPEN:
|
|
|
|
Shift();
|
|
|
|
Parse_Expression();
|
|
|
|
Match(ATT_BCLOSE);
|
|
|
|
break;
|
|
|
|
case ATT_BOPEN2:
|
|
|
|
Parse_Array();
|
|
|
|
break;
|
|
|
|
case ATT_BLOPEN:
|
|
|
|
Parse_PropList();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
UnexpectedToken("expression");
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
Parse_Expression2(iParentPrio);
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_Expression2(int iParentPrio)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
while (1) switch (TokenType)
|
2010-08-14 23:53:18 +00:00
|
|
|
{
|
2012-09-04 00:11:16 +00:00
|
|
|
case ATT_SET:
|
|
|
|
// back out of any kind of parent operator
|
|
|
|
// (except other setters, as those are right-associative)
|
|
|
|
if(iParentPrio > 1)
|
|
|
|
return;
|
2010-04-07 13:04:19 +00:00
|
|
|
{
|
|
|
|
// generate setter
|
|
|
|
C4AulBCC Setter = MakeSetter(false);
|
|
|
|
// parse value to set
|
|
|
|
Shift();
|
|
|
|
Parse_Expression(1);
|
|
|
|
// write setter
|
2010-09-22 00:14:16 +00:00
|
|
|
AddBCC(Setter.bccType, Setter.Par.X);
|
2010-04-07 13:04:19 +00:00
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
break;
|
|
|
|
case ATT_OPERATOR:
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// expect postfix operator
|
2012-10-21 14:13:53 +00:00
|
|
|
C4ScriptOpDef * op = &C4ScriptOpMap[cInt];
|
|
|
|
if (!op->Postfix)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// does an operator with the same name exist?
|
|
|
|
// when it's a postfix-operator, it can be used instead.
|
2012-10-21 14:13:53 +00:00
|
|
|
C4ScriptOpDef * postfixop;
|
|
|
|
for (postfixop = op + 1; postfixop->Identifier; ++postfixop)
|
|
|
|
if (SEqual(op->Identifier, postfixop->Identifier))
|
|
|
|
if (postfixop->Postfix)
|
2009-05-08 13:28:41 +00:00
|
|
|
break;
|
|
|
|
// not found?
|
2012-10-21 14:13:53 +00:00
|
|
|
if (!postfixop->Identifier)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-10-21 14:13:53 +00:00
|
|
|
throw new C4AulParseError(this, "unexpected prefix operator: ", op->Identifier);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
// otherwise use the new-found correct postfix operator
|
2012-10-21 14:13:53 +00:00
|
|
|
op = postfixop;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-04-07 13:04:19 +00:00
|
|
|
|
|
|
|
// changer?
|
|
|
|
C4AulBCC Setter;
|
2012-10-21 14:13:53 +00:00
|
|
|
if (op->Changer)
|
2010-04-07 13:04:19 +00:00
|
|
|
{
|
|
|
|
// changer: back out only if parent operator is stronger
|
|
|
|
// (everything but setters and other changers, as changers are right-associative)
|
2012-10-21 14:13:53 +00:00
|
|
|
if(iParentPrio > op->Priority)
|
2010-04-07 13:04:19 +00:00
|
|
|
return;
|
|
|
|
// generate setter, leave value on stack for operator
|
|
|
|
Setter = MakeSetter(true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// normal operator: back out if parent operator is at least as strong
|
|
|
|
// (non-setter operators are left-associative)
|
2012-10-21 14:13:53 +00:00
|
|
|
if(iParentPrio >= op->Priority)
|
2010-04-07 13:04:19 +00:00
|
|
|
return;
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
Shift();
|
|
|
|
|
2012-10-21 14:13:53 +00:00
|
|
|
if (op->Code == AB_JUMPAND || op->Code == AB_JUMPOR || op->Code == AB_JUMPNNIL)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// create bytecode, remember position
|
|
|
|
// Jump or discard first parameter
|
2012-10-21 14:13:53 +00:00
|
|
|
int iCond = AddBCC(op->Code);
|
2009-05-08 13:28:41 +00:00
|
|
|
// parse second expression
|
2012-10-21 14:13:53 +00:00
|
|
|
Parse_Expression(op->Priority);
|
2009-05-08 13:28:41 +00:00
|
|
|
// set condition jump target
|
|
|
|
SetJumpHere(iCond);
|
2010-04-07 13:04:19 +00:00
|
|
|
// write setter (unused - could also optimize to skip self-assign, but must keep stack balanced)
|
2012-10-21 14:13:53 +00:00
|
|
|
if (op->Changer)
|
2010-09-22 00:14:16 +00:00
|
|
|
AddBCC(Setter.bccType, Setter.Par.X);
|
2009-05-08 13:28:41 +00:00
|
|
|
break;
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
else
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-10-21 14:13:53 +00:00
|
|
|
C4V_Type to = op->Type1;
|
2011-05-01 16:53:50 +00:00
|
|
|
C4V_Type from = GetLastRetType(to);
|
|
|
|
if (C4Value::WarnAboutConversion(from, to))
|
|
|
|
{
|
2012-10-21 14:13:53 +00:00
|
|
|
Warn(FormatString("operator \"%s\" left side gets %s instead of %s", op->Identifier, GetC4VName(from), GetC4VName(to)).getData(), NULL);
|
2011-05-01 16:53:50 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
// expect second parameter for operator
|
2012-10-21 14:13:53 +00:00
|
|
|
if (!op->NoSecondStatement)
|
|
|
|
Parse_Expression(op->Priority);
|
|
|
|
to = op->Type2;
|
2011-05-01 16:53:50 +00:00
|
|
|
from = GetLastRetType(to);
|
|
|
|
if (C4Value::WarnAboutConversion(from, to))
|
|
|
|
{
|
2012-10-21 14:13:53 +00:00
|
|
|
Warn(FormatString("operator \"%s\" right side gets %s instead of %s", op->Identifier, GetC4VName(from), GetC4VName(to)).getData(), NULL);
|
2011-05-01 16:53:50 +00:00
|
|
|
}
|
2009-07-22 23:02:41 +00:00
|
|
|
// write byte code
|
2012-10-21 14:13:53 +00:00
|
|
|
AddBCC(op->Code, op - &C4ScriptOpMap[0]);
|
2010-04-08 00:47:45 +00:00
|
|
|
// write setter and mofidier
|
2012-10-21 14:13:53 +00:00
|
|
|
if (op->Changer)
|
2010-04-08 00:47:45 +00:00
|
|
|
{
|
2010-09-22 00:14:16 +00:00
|
|
|
AddBCC(Setter.bccType, Setter.Par.X);
|
2012-10-21 14:13:53 +00:00
|
|
|
if(op->ResultModifier != AB_ERR)
|
|
|
|
AddBCC(op->ResultModifier, op - &C4ScriptOpMap[0]);
|
2010-04-08 00:47:45 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-10-21 14:13:53 +00:00
|
|
|
break;
|
2012-09-04 00:11:16 +00:00
|
|
|
case ATT_BOPEN2:
|
|
|
|
// parse either [index], or [start:end] in which case either index is optional
|
|
|
|
Shift();
|
|
|
|
if (TokenType == ATT_COLON)
|
|
|
|
AddBCC(AB_INT, 0); // slice with first index missing -> implicit start index zero
|
|
|
|
else
|
|
|
|
Parse_Expression();
|
|
|
|
|
|
|
|
if (TokenType == ATT_BCLOSE2)
|
|
|
|
{
|
|
|
|
Shift();
|
|
|
|
AddBCC(AB_ARRAYA);
|
|
|
|
}
|
|
|
|
else if (TokenType == ATT_COLON)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
Shift();
|
2010-04-05 15:41:36 +00:00
|
|
|
if (TokenType == ATT_BCLOSE2)
|
|
|
|
{
|
|
|
|
Shift();
|
2012-09-04 00:11:16 +00:00
|
|
|
AddBCC(AB_INT, INT_MAX); // second index missing -> implicit end index GetLength()
|
2010-04-05 15:41:36 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-09-04 00:11:16 +00:00
|
|
|
Parse_Expression();
|
|
|
|
Match(ATT_BCLOSE2);
|
2010-04-05 15:41:36 +00:00
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
AddBCC(AB_ARRAY_SLICE);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
UnexpectedToken("']' or ':'");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ATT_DOT:
|
|
|
|
Shift();
|
|
|
|
if (TokenType != ATT_IDTF)
|
|
|
|
UnexpectedToken("Identifier");
|
2010-08-14 23:53:18 +00:00
|
|
|
{
|
|
|
|
C4String * pKey = Strings.RegString(Idtf);
|
|
|
|
AddBCC(AB_PROP, (intptr_t) pKey);
|
|
|
|
Shift();
|
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
break;
|
|
|
|
case ATT_CALL: case ATT_CALLFS:
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
C4AulFunc *pFunc = NULL;
|
2009-05-09 13:17:24 +00:00
|
|
|
C4String *pName = NULL;
|
2011-02-19 21:18:27 +00:00
|
|
|
C4AulBCCType eCallType = (TokenType == ATT_CALL) ? AB_CALL : AB_CALLFS;
|
|
|
|
Shift();
|
2010-03-28 18:58:01 +00:00
|
|
|
// expect identifier of called function now
|
2012-09-04 00:11:16 +00:00
|
|
|
if (TokenType != ATT_IDTF)
|
|
|
|
throw new C4AulParseError(this, "expecting func name after '->'");
|
2009-05-09 13:17:24 +00:00
|
|
|
if (Type == PARSER)
|
2012-05-27 22:53:41 +00:00
|
|
|
{
|
2009-07-21 14:46:39 +00:00
|
|
|
pName = ::Strings.RegString(Idtf);
|
2012-10-27 23:52:18 +00:00
|
|
|
pFunc = Engine->GetFirstFunc(pName);
|
2012-05-27 22:53:41 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
Shift();
|
2009-05-09 13:17:24 +00:00
|
|
|
Parse_Params(C4AUL_MAX_Par, pName ? pName->GetCStr() : Idtf, pFunc);
|
2010-01-25 15:57:57 +00:00
|
|
|
AddBCC(eCallType, reinterpret_cast<intptr_t>(pName));
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2012-09-04 00:11:16 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
2010-08-14 23:53:18 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_Var()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-10-23 23:00:29 +00:00
|
|
|
Shift();
|
2009-05-08 13:28:41 +00:00
|
|
|
while (1)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// get desired variable name
|
|
|
|
if (TokenType != ATT_IDTF)
|
|
|
|
UnexpectedToken("variable name");
|
|
|
|
if (Type == PREPARSER)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// insert variable
|
|
|
|
Fn->VarNamed.AddName(Idtf);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
// search variable (fail if not found)
|
|
|
|
int iVarID = Fn->VarNamed.GetItemNr(Idtf);
|
2010-03-28 18:58:01 +00:00
|
|
|
if (iVarID < 0)
|
2009-05-08 13:28:41 +00:00
|
|
|
throw new C4AulParseError(this, "internal error: var definition: var not found in variable table");
|
|
|
|
Shift();
|
2010-04-07 13:04:19 +00:00
|
|
|
if(TokenType == ATT_SET)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2010-04-07 13:04:19 +00:00
|
|
|
// insert initialization in byte code
|
|
|
|
Shift();
|
|
|
|
Parse_Expression();
|
2011-05-02 19:53:58 +00:00
|
|
|
AddBCC(AB_POP_TO, 1 + iVarID - (iStack + Fn->VarNamed.iSize));
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
|
|
|
switch (TokenType)
|
|
|
|
{
|
|
|
|
case ATT_COMMA:
|
|
|
|
Shift();
|
|
|
|
break;
|
|
|
|
case ATT_SCOLON:
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
UnexpectedToken("',' 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-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_Local()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-10-23 23:00:29 +00:00
|
|
|
Shift();
|
2009-05-08 13:28:41 +00:00
|
|
|
while (1)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
if (Type == PREPARSER)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// get desired variable name
|
|
|
|
if (TokenType != ATT_IDTF)
|
|
|
|
UnexpectedToken("variable name");
|
|
|
|
// check: symbol already in use?
|
2012-10-27 23:52:18 +00:00
|
|
|
if (Host->GetPropList() && Host->GetPropList()->GetFunc(Idtf))
|
2009-05-08 13:28:41 +00:00
|
|
|
throw new C4AulParseError(this, "variable definition: name already in use");
|
|
|
|
// insert variable
|
2012-10-27 23:52:18 +00:00
|
|
|
Host->LocalNamed.AddName(Idtf);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
2010-09-09 20:36:00 +00:00
|
|
|
char Name[C4AUL_MAX_Identifier] = ""; // current identifier
|
|
|
|
SCopy(Idtf, Name);
|
2009-05-08 13:28:41 +00:00
|
|
|
Match(ATT_IDTF);
|
2010-09-09 20:36:00 +00:00
|
|
|
if (TokenType == ATT_SET)
|
|
|
|
{
|
2012-10-27 23:52:18 +00:00
|
|
|
if (!Host->GetPropList())
|
2011-09-29 01:49:21 +00:00
|
|
|
throw new C4AulParseError(this, "local variables can only be initialized on proplists");
|
2011-02-19 21:37:42 +00:00
|
|
|
Shift();
|
2012-05-27 16:00:45 +00:00
|
|
|
C4RefCntPointer<C4String> key = ::Strings.RegString(Name);
|
2012-10-27 23:52:18 +00:00
|
|
|
assert(Host->GetPropList()->IsStatic());
|
|
|
|
Parse_ConstExpression(Host->GetPropList()->IsStatic(), key);
|
2010-09-09 20:36:00 +00:00
|
|
|
}
|
2010-03-28 18:58:01 +00:00
|
|
|
switch (TokenType)
|
|
|
|
{
|
|
|
|
case ATT_COMMA:
|
|
|
|
Shift();
|
|
|
|
break;
|
|
|
|
case ATT_SCOLON:
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
UnexpectedToken("',' 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-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_Static()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-10-23 23:00:29 +00:00
|
|
|
Shift();
|
|
|
|
// constant?
|
|
|
|
if (TokenType == ATT_IDTF && SEqual(Idtf, C4AUL_Const))
|
|
|
|
{
|
|
|
|
Parse_Const();
|
|
|
|
return;
|
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
while (1)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
if (Type == PREPARSER)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
// get desired variable name
|
|
|
|
if (TokenType != ATT_IDTF)
|
|
|
|
UnexpectedToken("variable name");
|
|
|
|
// global variable definition
|
|
|
|
// check: symbol already in use?
|
2012-10-27 23:52:18 +00:00
|
|
|
if (Engine->GetPropList()->GetFunc(Idtf)) Error("function and variable with name %s", Idtf);
|
|
|
|
if (Engine->GetGlobalConstant(Idtf, NULL)) Error("constant and variable with name %s", Idtf);
|
2009-05-08 13:28:41 +00:00
|
|
|
// insert variable if not defined already
|
2012-10-27 23:52:18 +00:00
|
|
|
if (Engine->GlobalNamedNames.GetItemNr(Idtf) == -1)
|
2010-01-25 15:57:57 +00:00
|
|
|
{
|
2012-10-27 23:52:18 +00:00
|
|
|
Engine->GlobalNamedNames.AddName(Idtf);
|
2009-05-08 13:28:41 +00:00
|
|
|
}
|
2010-01-25 15:57:57 +00:00
|
|
|
}
|
2009-05-08 13:28:41 +00:00
|
|
|
Match(ATT_IDTF);
|
2010-03-28 18:58:01 +00:00
|
|
|
switch (TokenType)
|
|
|
|
{
|
|
|
|
case ATT_COMMA:
|
|
|
|
Shift();
|
|
|
|
break;
|
|
|
|
case ATT_SCOLON:
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
UnexpectedToken("',' 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-05-27 16:00:45 +00:00
|
|
|
C4Value C4AulParse::Parse_ConstExpression(C4PropListStatic * parent, C4String * Name)
|
2010-09-09 00:18:19 +00:00
|
|
|
{
|
|
|
|
C4Value r;
|
2010-09-09 20:36:00 +00:00
|
|
|
switch (TokenType)
|
2010-09-09 00:18:19 +00:00
|
|
|
{
|
2010-09-09 20:36:00 +00:00
|
|
|
case ATT_INT: r.SetInt(cInt); break;
|
2011-02-21 23:07:08 +00:00
|
|
|
case ATT_STRING: r.SetString(cStr); break; // increases ref count of C4String in cStr
|
2010-09-09 20:36:00 +00:00
|
|
|
case ATT_IDTF:
|
|
|
|
// identifier is only OK if it's another constant
|
2010-10-19 22:37:31 +00:00
|
|
|
if (SEqual(Idtf, C4AUL_True))
|
|
|
|
r.SetBool(true);
|
|
|
|
else if (SEqual(Idtf, C4AUL_False))
|
|
|
|
r.SetBool(false);
|
|
|
|
else if (SEqual(Idtf, C4AUL_Nil))
|
|
|
|
r.Set0();
|
2011-11-07 00:39:43 +00:00
|
|
|
else if (SEqual(Idtf, C4AUL_New))
|
2012-05-27 16:00:45 +00:00
|
|
|
r = Parse_ConstPropList(parent, Name);
|
2012-10-27 23:52:18 +00:00
|
|
|
else if (Host && Host->LocalNamed.GetItemNr(Idtf) != -1)
|
|
|
|
Host->GetPropList()->GetPropertyByS(::Strings.FindString(Idtf), &r);
|
|
|
|
else if (!Engine->GetGlobalConstant(Idtf, &r))
|
2012-05-18 15:40:46 +00:00
|
|
|
if (Type == PARSER)
|
|
|
|
UnexpectedToken("constant value");
|
2010-09-09 20:36:00 +00:00
|
|
|
break;
|
|
|
|
case ATT_BOPEN2:
|
2010-09-09 00:18:19 +00:00
|
|
|
{
|
2010-09-09 20:36:00 +00:00
|
|
|
Shift();
|
|
|
|
// Create an array
|
2012-05-27 16:00:45 +00:00
|
|
|
if (Type == PARSER)
|
2010-09-09 00:18:19 +00:00
|
|
|
r.SetArray(new C4ValueArray());
|
2010-09-09 20:36:00 +00:00
|
|
|
int size = 0;
|
|
|
|
bool fDone = false;
|
|
|
|
do
|
|
|
|
switch (TokenType)
|
|
|
|
{
|
|
|
|
case ATT_BCLOSE2:
|
2010-09-09 00:18:19 +00:00
|
|
|
{
|
2010-09-09 20:36:00 +00:00
|
|
|
// [] -> size 0, [*,] -> size 2, [*,*,] -> size 3
|
|
|
|
if (size > 0)
|
2010-09-09 00:18:19 +00:00
|
|
|
{
|
2012-05-27 16:00:45 +00:00
|
|
|
if (Type == PARSER)
|
2010-09-09 00:18:19 +00:00
|
|
|
r._getArray()->SetItem(size, C4VNull);
|
2010-09-09 20:36:00 +00:00
|
|
|
++size;
|
2010-09-09 00:18:19 +00:00
|
|
|
}
|
2010-09-09 20:36:00 +00:00
|
|
|
fDone = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ATT_COMMA:
|
|
|
|
{
|
|
|
|
// got no parameter before a ","? then push nil
|
2012-05-27 16:00:45 +00:00
|
|
|
if (Type == PARSER)
|
2010-09-09 00:18:19 +00:00
|
|
|
r._getArray()->SetItem(size, C4VNull);
|
2010-09-09 20:36:00 +00:00
|
|
|
Shift();
|
|
|
|
++size;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
2012-05-27 16:00:45 +00:00
|
|
|
if (Type == PARSER)
|
|
|
|
r._getArray()->SetItem(size, Parse_ConstExpression(NULL, NULL));
|
2010-09-09 20:36:00 +00:00
|
|
|
else
|
2012-05-27 16:00:45 +00:00
|
|
|
Parse_ConstExpression(NULL, NULL);
|
2010-09-09 20:36:00 +00:00
|
|
|
++size;
|
|
|
|
if (TokenType == ATT_COMMA)
|
2010-09-09 00:18:19 +00:00
|
|
|
Shift();
|
2010-09-09 20:36:00 +00:00
|
|
|
else if (TokenType == ATT_BCLOSE2)
|
2010-09-09 00:18:19 +00:00
|
|
|
{
|
2010-09-09 20:36:00 +00:00
|
|
|
fDone = true;
|
|
|
|
break;
|
2010-09-09 00:18:19 +00:00
|
|
|
}
|
2010-09-09 20:36:00 +00:00
|
|
|
else
|
|
|
|
UnexpectedToken("',' or ']'");
|
2010-09-09 00:18:19 +00:00
|
|
|
}
|
|
|
|
}
|
2010-09-09 20:36:00 +00:00
|
|
|
while (!fDone);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ATT_BLOPEN:
|
2012-09-04 00:11:16 +00:00
|
|
|
r = Parse_ConstPropList(parent, Name);
|
|
|
|
break;
|
2011-02-19 21:37:42 +00:00
|
|
|
case ATT_OPERATOR:
|
|
|
|
{
|
|
|
|
// -> must be a prefix operator
|
2012-10-21 14:13:53 +00:00
|
|
|
C4ScriptOpDef * op = &C4ScriptOpMap[cInt];
|
|
|
|
if (SEqual(op->Identifier, "+"))
|
2011-02-19 21:37:42 +00:00
|
|
|
{
|
|
|
|
Shift();
|
|
|
|
if (TokenType == ATT_INT)
|
|
|
|
{
|
|
|
|
r.SetInt(cInt); break;
|
|
|
|
}
|
|
|
|
}
|
2012-10-21 14:13:53 +00:00
|
|
|
if (SEqual(op->Identifier, "-"))
|
2011-02-19 21:37:42 +00:00
|
|
|
{
|
|
|
|
Shift();
|
|
|
|
if (TokenType == ATT_INT)
|
|
|
|
{
|
|
|
|
r.SetInt(-cInt); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// fallthrough
|
2010-09-09 20:36:00 +00:00
|
|
|
default:
|
|
|
|
UnexpectedToken("constant value");
|
2010-09-09 00:18:19 +00:00
|
|
|
}
|
|
|
|
// expect ',' (next global) or ';' (end of definition) now
|
|
|
|
Shift();
|
2010-09-09 20:41:41 +00:00
|
|
|
if (TokenType == ATT_OPERATOR)
|
|
|
|
{
|
2012-10-21 14:13:53 +00:00
|
|
|
C4ScriptOpDef * op = &C4ScriptOpMap[cInt];
|
|
|
|
if (op->Code == AB_BitOr)
|
2010-09-09 20:41:41 +00:00
|
|
|
{
|
|
|
|
Shift();
|
2012-05-27 16:00:45 +00:00
|
|
|
C4Value r2 = Parse_ConstExpression(NULL, NULL);
|
2010-09-09 20:41:41 +00:00
|
|
|
r.SetInt(r.getInt() | r2.getInt());
|
|
|
|
}
|
|
|
|
}
|
2012-05-27 16:00:45 +00:00
|
|
|
// store as constant or property
|
|
|
|
if (Name)
|
|
|
|
{
|
|
|
|
if (parent)
|
|
|
|
parent->SetPropertyByS(Name, r);
|
|
|
|
else
|
2012-10-23 00:05:33 +00:00
|
|
|
{
|
|
|
|
C4Value oldval;
|
2012-10-27 23:52:18 +00:00
|
|
|
if (Type == PREPARSER && Engine->GetGlobalConstant(Name->GetCStr(), &oldval) && oldval != r)
|
2012-10-23 00:05:33 +00:00
|
|
|
Warn("redefining constant %s from %s to %s",
|
|
|
|
Name->GetCStr(), oldval.GetDataString().getData(), r.GetDataString().getData());
|
2012-10-27 23:52:18 +00:00
|
|
|
Engine->RegisterGlobalConstant(Name->GetCStr(), r);
|
2012-10-23 00:05:33 +00:00
|
|
|
}
|
2012-05-27 16:00:45 +00:00
|
|
|
}
|
2010-09-09 00:18:19 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2012-04-12 19:03:14 +00:00
|
|
|
void C4AulParse::Parse_Const()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-10-23 23:00:29 +00:00
|
|
|
Shift();
|
2009-05-08 13:28:41 +00:00
|
|
|
// get global constant definition(s)
|
|
|
|
while (1)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2009-05-08 13:28:41 +00:00
|
|
|
char Name[C4AUL_MAX_Identifier] = ""; // current identifier
|
|
|
|
// get desired variable name
|
|
|
|
if (TokenType != ATT_IDTF)
|
|
|
|
UnexpectedToken("constant name");
|
|
|
|
SCopy(Idtf, Name);
|
2011-01-02 18:40:48 +00:00
|
|
|
// check func lists - functions of same name are not allowed
|
2012-10-27 23:52:18 +00:00
|
|
|
if (Engine->GetPropList()->GetFunc(Idtf))
|
2012-05-25 22:22:22 +00:00
|
|
|
Error("definition of constant hidden by function %s", Idtf);
|
2012-10-27 23:52:18 +00:00
|
|
|
if (Engine->GlobalNamedNames.GetItemNr(Idtf) != -1)
|
2012-05-25 22:22:22 +00:00
|
|
|
Error("constant and variable with name %s", Idtf);
|
2009-05-08 13:28:41 +00:00
|
|
|
Match(ATT_IDTF);
|
|
|
|
// expect '='
|
2010-04-07 13:04:19 +00:00
|
|
|
if (TokenType != ATT_SET)
|
2009-05-08 13:28:41 +00:00
|
|
|
UnexpectedToken("'='");
|
|
|
|
// expect value. Theoretically, something like C4AulScript::ExecOperator could be used here
|
2011-07-05 11:10:56 +00:00
|
|
|
// this would allow for definitions like "static const OCF_CrewMember = 1<<20"
|
2009-05-08 13:28:41 +00:00
|
|
|
// However, such stuff should better be generalized, so the preparser (and parser)
|
|
|
|
// can evaluate any constant expression, including functions with constant retval (e.g. Sqrt)
|
2011-02-19 21:37:42 +00:00
|
|
|
// So allow only simple constants for now.
|
|
|
|
Shift();
|
2010-09-09 00:18:19 +00:00
|
|
|
|
2012-06-01 15:27:59 +00:00
|
|
|
C4RefCntPointer<C4String> key = ::Strings.RegString(Name);
|
2012-05-27 16:00:45 +00:00
|
|
|
Parse_ConstExpression(NULL, key);
|
2012-01-24 23:36:11 +00:00
|
|
|
|
2010-09-09 00:18:19 +00:00
|
|
|
switch (TokenType)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-09-04 00:11:16 +00:00
|
|
|
case ATT_COMMA:
|
|
|
|
Shift();
|
|
|
|
break;
|
|
|
|
case ATT_SCOLON:
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
UnexpectedToken("',' 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
|
|
|
|
2009-08-15 18:50:32 +00:00
|
|
|
bool C4AulScript::Parse()
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2011-10-13 16:01:02 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
//assert(sf->pOrgScript == *s);
|
|
|
|
C4AulScriptFunc *sfc;
|
|
|
|
if (sf->pOrgScript != this)
|
|
|
|
sfc = new C4AulScriptFunc(this, *sf);
|
|
|
|
else
|
|
|
|
sfc = sf;
|
|
|
|
sfc->SetOverloaded(to->GetFunc(sf->Name));
|
|
|
|
to->SetPropertyByS(prop->Key, C4VFunction(sfc));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case C4V_PropList:
|
|
|
|
{
|
|
|
|
C4PropListStatic * p = prop->Value._getPropList()->IsStatic();
|
|
|
|
assert(p);
|
|
|
|
if (prop->Key != &::Strings.P[P_Prototype])
|
|
|
|
if (!p || p->GetParent() != to)
|
|
|
|
{
|
2012-08-15 17:43:02 +00:00
|
|
|
p = C4PropList::NewStatic(NULL, 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
|
|
|
|
2012-01-26 23:20:55 +00:00
|
|
|
for (std::list<C4ScriptHost *>::iterator s = SourceScripts.begin(); s != SourceScripts.end(); ++s)
|
|
|
|
{
|
2012-08-08 23:21:54 +00:00
|
|
|
CopyPropList((*s)->LocalValues, p);
|
2012-01-26 23:20:55 +00:00
|
|
|
if (*s == this)
|
|
|
|
continue;
|
|
|
|
// definition appends
|
|
|
|
if (GetPropList() && GetPropList()->GetDef() && (*s)->GetPropList() && (*s)->GetPropList()->GetDef())
|
|
|
|
GetPropList()->GetDef()->IncludeDefinition((*s)->GetPropList()->GetDef());
|
|
|
|
// copy local var definitions
|
|
|
|
for (int ivar = 0; ivar < (*s)->LocalNamed.iSize; ivar ++)
|
|
|
|
LocalNamed.AddName((*s)->LocalNamed.pNames[ivar]);
|
|
|
|
}
|
|
|
|
|
2012-01-26 23:42:41 +00:00
|
|
|
// parse
|
2012-04-12 19:03:14 +00:00
|
|
|
C4AulParse state(this, C4AulParse::PARSER);
|
2012-01-26 23:42:41 +00:00
|
|
|
for (std::list<C4ScriptHost *>::iterator s = SourceScripts.begin(); s != SourceScripts.end(); ++s)
|
2010-03-28 18:58:01 +00:00
|
|
|
{
|
2012-05-27 22:50:13 +00:00
|
|
|
if (DEBUG_BYTECODE_DUMP)
|
|
|
|
{
|
2013-01-12 18:13:27 +00:00
|
|
|
fprintf(stderr, "parsing %s...\n", (*s)->ScriptName.getData());
|
2012-05-27 22:50:13 +00:00
|
|
|
}
|
2013-01-12 18:13:27 +00:00
|
|
|
state.Parse_Script(*s);
|
2010-03-28 18:58:01 +00:00
|
|
|
}
|
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
|
|
|
|
|
|
|
#undef DEBUG_BYTECODE_DUMP
|