openclonk/src/script/C4AulParse.cpp

3024 lines
76 KiB
C++
Raw Normal View History

2009-05-08 13:28:41 +00:00
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2001-2004, 2006-2008 Sven Eberhardt
* Copyright (c) 2001-2004, 2006-2008 Peter Wortmann
* Copyright (c) 2006 Armin Burgmeier
* Copyright (c) 2006-2009 Günther Brammer
* Copyright (c) 2007 Matthes Bender
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
2009-11-25 18:38:54 +00:00
#include <utility>
2009-05-08 13:28:41 +00:00
#include <C4Include.h>
#include <C4Aul.h>
#include <C4Def.h>
#include <C4Game.h>
#include <C4Log.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_Strict "#strict"
#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"
#define C4AUL_Do "do"
#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_Method "Method"
#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"
#define C4AUL_TypeC4Object "object"
#define C4AUL_TypePropList "proplist"
#define C4AUL_TypeString "string"
#define C4AUL_TypeArray "array"
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
#define C4AUL_True "true"
#define C4AUL_False "false"
#define C4AUL_Nil "nil"
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_BOOL, // boolean constant
ATT_STRING, // string constant
ATT_NIL, // "nil"
2010-03-28 18:58:01 +00:00
ATT_C4ID, // C4ID constant
ATT_COMMA, // ","
ATT_COLON, // ":"
ATT_DCOLON, // "::"
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_SEP, // "|"
ATT_CALL, // "->"
ATT_STAR, // "*"
ATT_AMP, // "&"
2009-05-08 13:28:41 +00:00
ATT_TILDE, // '~'
ATT_LDOTS, // '...'
ATT_OPERATOR,// operator
2010-03-28 18:58:01 +00:00
ATT_EOF // end of file
};
2009-05-08 13:28:41 +00:00
class C4AulParseState
2010-03-28 18:58:01 +00:00
{
public:
2009-05-08 13:28:41 +00:00
enum Type { PARSER, PREPARSER };
C4AulParseState(C4AulScriptFunc *Fn, C4AulScript * a, enum Type Type):
2010-03-28 18:58:01 +00:00
Fn(Fn), a(a), SPos(Fn ? Fn->Script : a->Script.getData()),
TokenType(ATT_INVALID),
Done(false),
Type(Type),
fJump(false),
iStack(0),
pLoopStack(NULL)
{ }
2009-05-08 13:28:41 +00:00
~C4AulParseState()
2010-03-28 18:58:01 +00:00
{ while (pLoopStack) PopLoop(); ClearToken(); }
2009-05-08 13:28:41 +00:00
C4AulScriptFunc *Fn; C4AulScript * a;
const char *SPos; // current position in the script
char Idtf[C4AUL_MAX_Identifier]; // current identifier
C4AulTokenType TokenType; // current token type
long cInt; // current int constant (long for compatibility with x86_64)
bool Done; // done parsing?
enum Type Type; // emitting bytecode?
void Parse_Script();
void Parse_FuncHead();
void Parse_Desc();
void Parse_Function();
void Parse_Statement();
void Parse_Block();
int Parse_Params(int iMaxCnt, const char * sWarn, C4AulFunc * pFunc = 0);
void Parse_Array();
void Parse_PropList();
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();
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
enum HoldStringsPolicy { Discard, Hold, Ref };
2010-01-30 16:51:26 +00:00
void ClearToken(); // clear any data held with the current token
2009-05-08 13:28:41 +00:00
C4AulTokenType GetNextToken(char *pToken, long *pInt, HoldStringsPolicy HoldStrings, bool bOperator); // get next token of SPos
void Shift(HoldStringsPolicy HoldStrings = Hold, bool bOperator = true);
void Match(C4AulTokenType TokenType, const char * Message = NULL);
void UnexpectedToken(const char * Expected);
const char * GetTokenName(C4AulTokenType TokenType);
void Warn(const char *pMsg, const char *pIdtf=0);
void Error(const char *pMsg, const char *pIdtf=0);
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
private:
bool fJump;
int iStack;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
void AddBCC(C4AulBCCType eType, intptr_t X = 0);
void SetNoRef(); // Switches the bytecode to generate a value instead of a reference
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);
};
2009-05-08 13:28:41 +00:00
void C4AulScript::Warn(const char *pMsg, const char *pIdtf)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// display error
C4AulParseError warning(this, pMsg, pIdtf, true);
2009-05-08 13:28:41 +00:00
// display it
warning.show();
// count warnings
++::ScriptEngine.warnCnt;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::Warn(const char *pMsg, const char *pIdtf)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// do not show errors for System.c4g scripts that appear to be pure #appendto scripts
if (Fn && !Fn->Owner->Def && Fn->Owner->Appends) return;
// script doesn't own function -> skip
// (exception: global functions)
//if(pFunc) if(pFunc->pOrgScript != pScript && pScript != (C4AulScript *)&::ScriptEngine) return;
2009-05-08 13:28:41 +00:00
// display error
C4AulParseError warning(this, pMsg, pIdtf, true);
2009-05-08 13:28:41 +00:00
// display it
warning.show();
if (Fn && Fn->pOrgScript != a)
DebugLogF(" (as #appendto/#include to %s)", Fn->Owner->ScriptName.getData());
// count warnings
++::ScriptEngine.warnCnt;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::Error(const char *pMsg, const char *pIdtf)
2010-03-28 18:58:01 +00:00
{
throw new C4AulParseError(this, pMsg, pIdtf);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4AulParseError::C4AulParseError(C4AulParseState * 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 : "");
if (state->Fn && *(state->Fn->Name))
{
2009-05-08 13:28:41 +00:00
// Show function name
sMessage.AppendFormat(" (in %s", state->Fn->Name);
// Exact position
2010-03-28 18:58:01 +00:00
if (state->Fn->pOrgScript && state->SPos)
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(),
SGetLine(state->Fn->pOrgScript->Script.getData(), state->SPos),
SLineGetCharacters(state->Fn->pOrgScript->Script.getData(), state->SPos));
2009-05-08 13:28:41 +00:00
else
sMessage.AppendChar(')');
2010-03-28 18:58:01 +00:00
}
else if (state->a)
{
2009-05-08 13:28:41 +00:00
// Script name
sMessage.AppendFormat(" (%s:%d:%d)",
2010-03-28 18:58:01 +00:00
state->a->ScriptName.getData(),
SGetLine(state->a->Script.getData(), state->SPos),
SLineGetCharacters(state->a->Script.getData(), state->SPos));
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +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
void C4AulScriptFunc::ParseDesc()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// do nothing if no desc is given
if (!Desc.getLength()) return;
const char *DPos = Desc.getData();
// parse desc
while (*DPos)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
const char *DPos0 = DPos; // beginning of segment
const char *DPos2 = NULL; // pos of equal sign, if found
// parse until end of segment
while (*DPos && (*DPos != '|'))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// store break pos if found
if (*DPos == '=') if (!DPos2) DPos2 = DPos;
DPos++;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// if this was an assignment segment, get value to assign
if (DPos2)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
StdCopyStrBuf Val;
Val.Append(DPos2 + 1, DPos - DPos2 - 1);
// Image
if (SEqual2(DPos0, C4AUL_Image))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// image: special contents-image?
if (Val == C4AUL_Contents)
idImage = C4ID::Contents;
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Find phase separator (:)
char *colon;
2010-03-28 18:58:01 +00:00
for (colon = Val.getMData(); *colon != ':' && *colon != '\0'; ++ colon) {}
if (*colon == ':') *colon = '\0';
2009-05-08 13:28:41 +00:00
else colon = NULL;
// get image id
idImage = C4ID(Val.getData());
2009-05-08 13:28:41 +00:00
// get image phase
2010-03-28 18:58:01 +00:00
if (colon)
2009-05-08 13:28:41 +00:00
iImagePhase = atoi(colon + 1);
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Condition
else if (SEqual2(DPos0, C4AUL_Condition))
// condition? get condition func
Condition = Owner->GetFuncRecursive(Val.getData());
// Method
2010-03-28 18:58:01 +00:00
else if (SEqual2(DPos0, C4AUL_Method))
{
if (SEqual2(Val.getData(), C4AUL_MethodAll))
2009-05-08 13:28:41 +00:00
ControlMethod = C4AUL_ControlMethod_All;
2010-03-28 18:58:01 +00:00
else if (SEqual2(Val.getData(), C4AUL_MethodNone))
2009-05-08 13:28:41 +00:00
ControlMethod = C4AUL_ControlMethod_None;
2010-03-28 18:58:01 +00:00
else if (SEqual2(Val.getData(), C4AUL_MethodClassic))
2009-05-08 13:28:41 +00:00
ControlMethod = C4AUL_ControlMethod_Classic;
2010-03-28 18:58:01 +00:00
else if (SEqual2(Val.getData(), C4AUL_MethodJumpAndRun))
2009-05-08 13:28:41 +00:00
ControlMethod = C4AUL_ControlMethod_JumpAndRun;
else
// unrecognized: Default to all
ControlMethod = C4AUL_ControlMethod_All;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Long Description
2010-03-28 18:58:01 +00:00
else if (SEqual2(DPos0, C4AUL_Desc))
{
2009-11-25 18:38:54 +00:00
DescLong.Take(std::move(Val));
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// unrecognized? never mind
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
if (*DPos) DPos++;
}
2009-05-08 13:28:41 +00:00
assert(!Condition || !Condition->Owner->Def || Condition->Owner->Def == Owner->Def);
// copy desc text
DescText.CopyUntil(Desc.getData(), '|');
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4AulParseState::AdvanceSpaces()
2010-03-28 18:58:01 +00:00
{
char C, C2 = (char) 0;
// defaultly, not in comment
int InComment = 0; // 0/1/2 = no comment/line comment/multi line comment
// don't go past end
while ((C = *SPos))
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// loop until out of comment and non-whitespace is found
switch (InComment)
{
case 0:
if (C == '/')
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
SPos++;
switch (*SPos)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
case '/': InComment = 1; break;
case '*': InComment = 2; break;
default: SPos--; return true;
2009-05-08 13:28:41 +00:00
}
}
2010-03-28 18:58:01 +00:00
// Skip those stupid "zero width no-break spaces" (also known as Byte Order Marks)
else if (C == '\xEF' && *(SPos + 1) == '\xBB' && *(SPos + 2) == '\xBF')
SPos += 2;
else if ((BYTE) C > 32) return true;
break;
case 1:
if (((BYTE) C == 13) || ((BYTE) C == 10)) InComment = 0;
break;
case 2:
if ((C == '/') && (C2 == '*')) InComment = 0;
break;
}
// next char; store prev
SPos++; C2 = C;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// end of script reached; return false
return false;
}
2009-05-08 13:28:41 +00:00
//=========================== C4Script Operator Map ===================================
2010-03-28 18:58:01 +00:00
C4ScriptOpDef C4ScriptOpMap[] =
{
2010-03-22 21:16:46 +00:00
// priority postfix
2009-05-08 13:28:41 +00:00
// | identifier | right-associative
2010-03-22 21:16:46 +00:00
// | | Bytecode | | no second id
// | | | | | | RetType ParType1 ParType2
2009-05-08 13:28:41 +00:00
// prefix
2010-03-22 21:16:46 +00:00
{ 15, "++", AB_Inc1, 0, 1, 0, C4V_Int, C4V_Ref, C4V_Any},
{ 15, "--", AB_Dec1, 0, 1, 0, C4V_Int, C4V_Ref, C4V_Any},
{ 15, "~", AB_BitNot, 0, 1, 0, C4V_Int, C4V_Int, C4V_Any},
{ 15, "!", AB_Not, 0, 1, 0, C4V_Bool, C4V_Bool, C4V_Any},
{ 15, "+", AB_ERR, 0, 1, 0, C4V_Int, C4V_Int, C4V_Any},
{ 15, "-", AB_Neg, 0, 1, 0, C4V_Int, C4V_Int, C4V_Any},
2009-05-08 13:28:41 +00:00
// postfix (whithout second statement)
2010-03-22 21:16:46 +00:00
{ 16, "++", AB_Inc1_Postfix, 1, 1, 1, C4V_Int, C4V_Ref, C4V_Any},
{ 16, "--", AB_Dec1_Postfix, 1, 1, 1, C4V_Int, C4V_Ref, C4V_Any},
2009-05-08 13:28:41 +00:00
// postfix
2010-03-22 21:16:46 +00:00
{ 14, "**", AB_Pow, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 13, "/", AB_Div, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 13, "*", AB_Mul, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 13, "%", AB_Mod, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 12, "-", AB_Sub, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 12, "+", AB_Sum, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 11, "<<", AB_LeftShift, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 11, ">>", AB_RightShift, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 10, "<", AB_LessThan, 1, 0, 0, C4V_Bool, C4V_Int, C4V_Int},
{ 10, "<=", AB_LessThanEqual, 1, 0, 0, C4V_Bool, C4V_Int, C4V_Int},
{ 10, ">", AB_GreaterThan, 1, 0, 0, C4V_Bool, C4V_Int, C4V_Int},
{ 10, ">=", AB_GreaterThanEqual,1, 0, 0, C4V_Bool, C4V_Int, C4V_Int},
{ 9, "==", AB_Equal, 1, 0, 0, C4V_Bool, C4V_Any, C4V_Any},
{ 9, "!=", AB_NotEqual, 1, 0, 0, C4V_Bool, C4V_Any, C4V_Any},
{ 8, "&", AB_BitAnd, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 6, "^", AB_BitXOr, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 6, "|", AB_BitOr, 1, 0, 0, C4V_Int, C4V_Int, C4V_Int},
{ 5, "&&", AB_JUMPAND, 1, 0, 0, C4V_Bool, C4V_Bool, C4V_Bool},
{ 4, "||", AB_JUMPOR, 1, 0, 0, C4V_Bool, C4V_Bool, C4V_Bool},
{ 2, "*=", AB_MulIt, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "/=", AB_DivIt, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "%=", AB_ModIt, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "+=", AB_Inc, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "-=", AB_Dec, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "&=", AB_AndIt, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "|=", AB_OrIt, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "^=", AB_XOrIt, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Int},
{ 2, "=", AB_Set, 1, 1, 0, C4V_Ref, C4V_Ref, C4V_Any},
{ 0, NULL, AB_ERR, 0, 0, 0, C4V_Any, C4V_Any, C4V_Any}
2009-05-08 13:28:41 +00:00
};
int C4AulParseState::GetOperator(const char* pScript)
{
// 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;
// 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
{
return -1;
2009-05-08 13:28:41 +00:00
}
// it is a two-char-operator?
2010-03-28 18:58:01 +00:00
for (i=0; C4ScriptOpMap[i].Identifier; i++)
if (SLen(C4ScriptOpMap[i].Identifier) == 2)
if (SEqual2(pScript, C4ScriptOpMap[i].Identifier))
2009-05-08 13:28:41 +00:00
return i;
// if not, a one-char one?
2010-03-28 18:58:01 +00:00
for (i=0; C4ScriptOpMap[i].Identifier; i++)
if (SLen(C4ScriptOpMap[i].Identifier) == 1)
if (SEqual2(pScript, C4ScriptOpMap[i].Identifier))
2009-05-08 13:28:41 +00:00
return i;
return -1;
}
static bool IsNumber(char c, int base)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
return (c >= '0' && c <= '9' && c < ('0' + base)) ||
2010-03-28 18:58:01 +00:00
(c >= 'a' && c <= 'z' && c < ('a' + base - 10)) ||
(c >= 'A' && c <= 'Z' && c < ('A' + base - 10));
}
2009-05-08 13:28:41 +00:00
static int ToNumber(char c)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'z') return 10 + c - 'a';
if (c >= 'A' && c <= 'Z') return 10 + c - 'A';
assert(false);
2009-05-08 13:28:41 +00:00
return 0;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
static int32_t StrToI32(char *s, int base, char **scan_end)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
int sign = 1;
int32_t result = 0;
if (*s == '-')
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
sign = -1;
s++;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if (*s == '+')
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
s++;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
while (IsNumber(*s,base))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
int value = ToNumber(*s++);
assert (value < base && value >= 0);
result *= base;
result += value;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
if (scan_end != 0L) *scan_end = s;
if (result < 0)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
//overflow
// we need 2147483648 (2^31) to be -2147483648 in order for -2147483648 to work
//result = INT_MAX;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
result *= sign;
return result;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-01-30 16:51:26 +00:00
void C4AulParseState::ClearToken()
{
// if last token was a string, make sure its ref is deleted
if (TokenType == ATT_STRING && cInt)
{
reinterpret_cast<C4String *>(cInt)->DecRef();
TokenType = ATT_INVALID;
}
}
2009-05-08 13:28:41 +00:00
C4AulTokenType C4AulParseState::GetNextToken(char *pToken, long int *pInt, HoldStringsPolicy HoldStrings, bool bOperator)
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
const char *SPos0 = SPos;
int Len = 0;
// token get state
enum TokenGetState
2010-03-28 18:58:01 +00:00
{
TGS_None, // just started
TGS_Ident, // getting identifier
TGS_Int, // getting integer
2009-05-08 13:28:41 +00:00
TGS_IntHex, // getting hexadecimal integer
2010-03-28 18:58:01 +00:00
TGS_String, // getting string
TGS_Dir // getting directive
};
2009-05-08 13:28:41 +00:00
TokenGetState State = TGS_None;
std::string strbuf;
2009-05-08 13:28:41 +00:00
// loop until finished
while (true)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// get char
char C = *SPos;
switch (State)
2010-03-28 18:58:01 +00:00
{
case TGS_None:
{
// get token type by first char (+/- are operators)
if (((C >= '0') && (C <= '9')))
State = TGS_Int; // integer by 0-9
else if (C == '"')
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
State = TGS_String; // string by "
strbuf.reserve(512); // assume most strings to be smaller than this
}
else if (C == '#') State = TGS_Dir; // directive by "#"
else if (C == ',') {SPos++; return ATT_COMMA; } // ","
else if (C == ';') {SPos++; return ATT_SCOLON;} // ";"
else if (C == '(') {SPos++; return ATT_BOPEN; } // "("
else if (C == ')') {SPos++; return ATT_BCLOSE;} // ")"
else if (C == '[') {SPos++; return ATT_BOPEN2;} // "["
else if (C == ']') {SPos++; return ATT_BCLOSE2;}// "]"
else if (C == '{') {SPos++; return ATT_BLOPEN;} // "{"
else if (C == '}') {SPos++; return ATT_BLCLOSE;}// "}"
else if (C == ':') // ":"
{
SPos++;
// double-colon?
if (*SPos == ':') // "::"
{
2009-05-08 13:28:41 +00:00
SPos++;
2010-03-28 18:58:01 +00:00
return ATT_DCOLON;
}
else // ":"
return ATT_COLON;
}
else if (C == '-' && *(SPos + 1) == '>') // "->"
{
SPos+=2; return ATT_CALL;
}
else if (C == '.' && *(SPos + 1) == '.' && *(SPos + 2) == '.') // "..."
{
SPos+=3; return ATT_LDOTS;
}
else
{
if (bOperator)
{
// may it be an operator?
int iOpID;
if ((iOpID = GetOperator(SPos)) != -1)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// store operator ID in pInt
*pInt = iOpID;
SPos += SLen(C4ScriptOpMap[iOpID].Identifier);
return ATT_OPERATOR;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
else if (C == '*') { SPos++; return ATT_STAR; } // "*"
else if (C == '&') { SPos++; return ATT_AMP; } // "&"
else if (C == '~') { SPos++; return ATT_TILDE; } // "~"
// identifier by all non-special chars
if (C >= '@')
{
// only the alphabet and '_' is allowed
if ((C >= 'A' && C <= 'Z') || (C >= 'a' && C <= 'z') || (C == '_'))
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
State = TGS_Ident;
break;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// unrecognized char
// make sure to skip the invalid char so the error won't be output forever
++SPos;
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
// examine next char
++SPos; ++ Len;
// no operator expected and '-' or '+' found?
// this could be an int const; parse it directly
if (!bOperator && (C=='-' || C=='+'))
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// skip spaces between sign and int constant
if (AdvanceSpaces())
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// continue parsing int, if a numeral follows
C = *SPos;
if (((C >= '0') && (C <= '9')))
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
State = TGS_Int;
break;
2009-05-08 13:28:41 +00:00
}
}
}
2010-03-28 18:58:01 +00:00
// special char and/or error getting it as a signed int
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// unrecognized char
// show appropriate error message
if (C >= '!' && C <= '~')
throw new C4AulParseError(this, FormatString("unexpected character '%c' found", (int)(unsigned char) C).getData());
else
throw new C4AulParseError(this, FormatString("unexpected character 0x%x found", (int)(unsigned char) C).getData());
}
break;
}
case TGS_Ident: // ident and directive: parse until non ident-char is found
case TGS_Dir:
if ( !Inside(C, '0', '9')
&& !Inside(C, 'a', 'z')
&& !Inside(C, 'A', 'Z')
&& C != '_')
{
// return ident/directive
Len = Min(Len, C4AUL_MAX_Identifier);
SCopy(SPos0, pToken, Len);
// directive?
if (State == TGS_Dir) return ATT_DIR;
// check reserved names
if (SEqual(pToken, C4AUL_False)) { *pInt = false; return ATT_BOOL; }
if (SEqual(pToken, C4AUL_True)) { *pInt = true; return ATT_BOOL; }
if (SEqual(pToken, C4AUL_Nil)) { return ATT_NIL; }
// everything else is an identifier
return ATT_IDTF;
}
break;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case TGS_Int: // integer: parse until non-number is found
case TGS_IntHex:
if ((C < '0') || (C > '9'))
{
int base;
if (State == TGS_Int)
{
// turn to hex mode?
if (*SPos0 == '0' && C == 'x' && Len == 1)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
State = TGS_IntHex;
break;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// parse as decimal int
base = 10;
2009-05-08 13:28:41 +00:00
}
else
{
2010-03-28 18:58:01 +00:00
// parse as hexadecimal int: Also allow 'a' to 'f' and 'A' to 'F'
if ((C>='A' && C<='F') || (C>='a' && C<='f')) break;
base = 16;
}
// return integer
Len = Min(Len, C4AUL_MAX_Identifier);
SCopy(SPos0, pToken, Len);
// do not parse 0x prefix for hex
if (State == TGS_IntHex) pToken += 2;
// it's not, so return the int
*pInt = StrToI32(pToken, base, 0);
return ATT_INT;
}
break;
case TGS_String: // string: parse until '"'; check for eof!
// string end
if (C == '"')
{
SPos++;
// no string expected?
if (HoldStrings == Discard) { pInt=0; return ATT_STRING; }
// reg string (if not already done so)
C4String *pString;
pString = Strings.RegString(StdStrBuf(strbuf.data(),strbuf.size()));
pString->IncRef();
// return pointer on string object
2010-04-18 20:02:01 +00:00
*pInt = (intptr_t) pString;
2010-03-28 18:58:01 +00:00
return ATT_STRING;
}
else
{
if (C == '\\') // escape
switch (*(SPos + 1))
{
case '"': SPos ++; strbuf.push_back('"'); break;
case '\\': SPos ++; strbuf.push_back('\\'); break;
case 'n': SPos ++; strbuf.push_back('\n'); break;
case 'x':
{
++SPos;
// hexadecimal escape: \xAD.
// First char must be a hexdigit
if (!std::isxdigit(SPos[1]))
{
Warn("\\x used with no following hex digits");
strbuf.push_back('\\'); strbuf.push_back('x');
}
else
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
char ch = 0;
while (std::isxdigit(SPos[1]))
2009-10-21 02:34:15 +00:00
{
++SPos;
2010-03-28 18:58:01 +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;
};
strbuf.push_back(ch);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
break;
}
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')
{
ch *= 8;
ch += *++SPos -'0';
}
strbuf.push_back(ch);
}
break;
default:
{
// just insert "\"
strbuf.push_back('\\');
// show warning
char strEscape[2] = { *(SPos + 1), 0 };
Warn("unknown escape: ", strEscape);
}
}
else if (C == 0 || C == 10 || C == 13) // line break / feed
throw new C4AulParseError(this, "string not closed");
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
else
// copy character
strbuf.push_back(C);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
break;
}
2009-05-08 13:28:41 +00:00
// next char
SPos++; Len++;
}
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-03-28 18:58:01 +00:00
case AB_ARRAYA_R: return "ARRAYA_R"; // array access
case AB_ARRAYA_V: return "ARRAYA_V"; // not creating a reference
case AB_ARRAY_SLICE: return "ARRAY_SLICE";
2010-03-28 18:58:01 +00:00
case AB_VARN_R: return "VARN_R"; // a named var
2009-05-08 13:28:41 +00:00
case AB_VARN_V: return "VARN_V";
2010-03-28 18:58:01 +00:00
case AB_PARN_R: return "PARN_R"; // a named parameter
2009-05-08 13:28:41 +00:00
case AB_PARN_V: return "PARN_V";
2010-03-28 18:58:01 +00:00
case AB_LOCALN_R: return "LOCALN_R"; // a named local
2009-05-08 13:28:41 +00:00
case AB_LOCALN_V: return "LOCALN_V";
2010-03-28 18:58:01 +00:00
case AB_GLOBALN_R: return "GLOBALN_R"; // a named global
2009-05-08 13:28:41 +00:00
case AB_GLOBALN_V: return "GLOBALN_V";
2010-03-28 18:58:01 +00:00
case AB_PAR_R: return "PAR_R"; // Par statement
2009-05-08 13:28:41 +00:00
case AB_PAR_V: return "PAR_V";
2010-03-28 18:58:01 +00:00
case AB_FUNC: return "FUNC"; // function
2009-05-08 13:28:41 +00:00
// prefix
2010-03-28 18:58:01 +00:00
case AB_Inc1: return "Inc1"; // ++
case AB_Dec1: return "Dec1"; // --
case AB_BitNot: return "BitNot"; // ~
case AB_Not: return "Not"; // !
// +
case AB_Neg: return "Neg"; // -
2009-05-08 13:28:41 +00:00
// postfix (whithout second statement)
2010-03-28 18:58:01 +00:00
case AB_Inc1_Postfix: return "Inc1_Postfix"; // ++
case AB_Dec1_Postfix: return "Dec1_Postfix"; // --
2009-05-08 13:28:41 +00:00
// postfix
2010-03-28 18:58:01 +00:00
case AB_Pow: return "Pow"; // **
case AB_Div: return "Div"; // /
case AB_Mul: return "Mul"; // *
case AB_Mod: return "Mod"; // %
case AB_Sub: return "Sub"; // -
case AB_Sum: return "Sum"; // +
case AB_LeftShift: return "LeftShift"; // <<
case AB_RightShift: return "RightShift"; // >>
case AB_LessThan: return "LessThan"; // <
case AB_LessThanEqual: return "LessThanEqual"; // <=
case AB_GreaterThan: return "GreaterThan"; // >
case AB_GreaterThanEqual: return "GreaterThanEqual"; // >=
case AB_Equal: return "Equal"; // ==
case AB_NotEqual: return "NotEqual"; // !=
case AB_BitAnd: return "BitAnd"; // &
case AB_BitXOr: return "BitXOr"; // ^
case AB_BitOr: return "BitOr"; // |
case AB_MulIt: return "MulIt"; // *=
case AB_DivIt: return "DivIt"; // /=
case AB_ModIt: return "ModIt"; // %=
case AB_Inc: return "Inc"; // +=
case AB_Dec: return "Dec"; // -=
case AB_AndIt: return "AndIt"; // &=
case AB_OrIt: return "OrIt"; // |=
case AB_XOrIt: return "XOrIt"; // ^=
case AB_Set: return "Set"; // =
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
case AB_BOOL: return "bool"; // constant: bool
case AB_STRING: return "STRING"; // constant: string
case AB_C4ID: return "C4ID"; // constant: C4ID
case AB_NIL: return "NIL"; // constant: nil
case AB_ARRAY: return "ARRAY"; // semi-constant: array
case AB_PROPLIST: return "PROPLIST"; // semi-constant: array
case AB_PROPSET: return "PROPSET";
2010-03-28 18:58:01 +00:00
case AB_IVARN: return "IVARN"; // initialization of named var
case AB_JUMP: return "JUMP"; // jump
2009-05-08 13:28:41 +00:00
case AB_JUMPAND: return "JUMPAND";
case AB_JUMPOR: return "JUMPOR";
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
case AB_EOFN: return "EOFN"; // end of function
2009-05-08 13:28:41 +00:00
case AB_EOF: return "EOF";
default: return "?";
}
}
void C4AulScript::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
// range check
if (CodeSize >= CodeBufSize)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// create new buffer
CodeBufSize = CodeBufSize ? 2 * CodeBufSize : C4AUL_CodeBufSize;
C4AulBCC *nCode = new C4AulBCC [CodeBufSize];
// copy data
memcpy(nCode, Code, sizeof(*Code) * CodeSize );
// replace buffer
delete[] Code;
Code = nCode;
// adjust pointer
CPos = Code + CodeSize;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// store chunk
CPos->bccType = eType;
CPos->Par.X = X;
2009-05-08 13:28:41 +00:00
CPos->SPos = SPos;
switch (eType)
2010-03-28 18:58:01 +00:00
{
case AB_STRING: case AB_CALL: case AB_CALLFS: case AB_LOCALN_R: case AB_LOCALN_V:
CPos->Par.s->IncRef();
break;
default:
// TODO
break;
}
CPos++; CodeSize++;
}
void C4AulScript::ClearCode()
{
for (int i = 0; i < CodeSize; ++i)
{
switch (Code[i].bccType)
{
case AB_STRING: case AB_CALL: case AB_CALLFS: case AB_LOCALN_R: case AB_LOCALN_V:
2010-03-28 18:58:01 +00:00
Code[i].Par.s->DecRef();
2010-01-25 04:00:59 +00:00
break;
default:
// TODO
break;
}
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
delete[] Code;
Code = 0;
CodeSize = CodeBufSize = 0;
CPos = Code;
}
2009-05-08 13:28:41 +00:00
bool C4AulScript::Preparse()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// handle easiest case first
if (State < ASS_NONE) return false;
if (!Script) { State = ASS_PREPARSED; return true; }
2009-05-08 13:28:41 +00:00
// clear stuff
/* simply setting Includes to NULL will waste some space in the associative list
2010-03-28 18:58:01 +00:00
however, this is just a few bytes per updated definition in developer mode, which
seems acceptable for me. The mem will be released when destroying the list */
2009-05-08 13:28:41 +00:00
Includes = NULL; Appends=NULL;
CPos = Code;
while (Func0)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// belongs to this script?
2010-03-28 18:58:01 +00:00
if (Func0->SFunc())
if (Func0->SFunc()->pOrgScript == this)
2009-05-08 13:28:41 +00:00
// then desroy linked funcs, too
Func0->DestroyLinked();
// destroy func
delete Func0;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4AulParseState state(0, this, C4AulParseState::PREPARSER);
state.Parse_Script();
// no #strict? we don't like that :(
if (Strict < MAXSTRICT)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Engine->nonStrictCnt++;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// done, reset state var
Preparsing=false;
2009-05-08 13:28:41 +00:00
// #include will have to be resolved now...
IncludesResolved = false;
// return success
C4AulScript::State = ASS_PREPARSED;
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::AddBCC(C4AulBCCType eType, intptr_t X)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (Type != PARSER) return;
// Track stack size
2010-03-28 18:58:01 +00:00
switch (eType)
{
case AB_INT:
case AB_BOOL:
case AB_STRING:
case AB_C4ID:
case AB_PROPLIST:
case AB_NIL:
case AB_VARN_R:
case AB_VARN_V:
case AB_PARN_R:
case AB_PARN_V:
case AB_LOCALN_R:
case AB_LOCALN_V:
case AB_GLOBALN_R:
case AB_GLOBALN_V:
iStack++;
break;
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:
case AB_Equal:
case AB_NotEqual:
case AB_BitAnd:
case AB_BitXOr:
case AB_BitOr:
case AB_MulIt:
case AB_DivIt:
case AB_ModIt:
case AB_Inc:
case AB_Dec:
case AB_AndIt:
case AB_OrIt:
case AB_XOrIt:
case AB_Set:
case AB_ARRAYA_R:
case AB_ARRAYA_V:
case AB_CONDN:
case AB_COND:
case AB_IVARN:
case AB_RETURN:
2009-05-08 13:28:41 +00:00
// JUMPAND/JUMPOR are special: They either jump over instructions adding one to the stack
// 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:
iStack--;
break;
case AB_FUNC:
iStack-= reinterpret_cast<C4AulFunc *>(X)->GetParCount() - 1;
break;
case AB_CALL:
case AB_CALLFS:
iStack-=C4AUL_MAX_Par;
break;
case AB_Inc1:
case AB_Dec1:
case AB_BitNot:
case AB_Not:
case AB_Neg:
case AB_Inc1_Postfix:
case AB_Dec1_Postfix:
case AB_PAR_R:
case AB_PAR_V:
case AB_FOREACH_NEXT:
case AB_ERR:
case AB_EOFN:
case AB_EOF:
case AB_JUMP:
case AB_DEBUG:
break;
case AB_STACK:
iStack+=X;
break;
case AB_ARRAY:
iStack-=X-1;
break;
case AB_ARRAY_SLICE:
2010-03-28 18:58:01 +00:00
case AB_PROPSET:
iStack -= 2;
break;
default:
assert(false);
}
2009-05-08 13:28:41 +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
2010-03-28 18:58:01 +00:00
if (!fJump)
{
2009-05-08 13:28:41 +00:00
// Join together stack operations
2010-03-28 18:58:01 +00:00
if (eType == AB_STACK &&
a->CPos > a->Code &&
(a->CPos-1)->bccType == AB_STACK
&& (X <= 0 || (a->CPos-1)->Par.i >= 0))
{
(a->CPos-1)->Par.i += X;
2009-05-08 13:28:41 +00:00
// Empty? Remove it.
2010-03-28 18:58:01 +00:00
if (!(a->CPos-1)->Par.i)
{
2009-05-08 13:28:41 +00:00
a->CPos--;
a->CodeSize--;
}
2010-03-28 18:58:01 +00:00
return;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Add
a->AddBCC(eType, X, SPos);
// Reset jump flag
fJump = false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::SetNoRef()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (Type != PARSER) return;
C4AulBCC * CPos = a->CPos - 1;
switch (CPos->bccType)
2010-03-28 18:58:01 +00:00
{
case AB_ARRAYA_R: CPos->bccType = AB_ARRAYA_V; break;
case AB_PAR_R: CPos->bccType = AB_PAR_V; break;
case AB_PARN_R: CPos->bccType = AB_PARN_V; break;
case AB_VARN_R: CPos->bccType = AB_VARN_V; break;
case AB_LOCALN_R: CPos->bccType = AB_LOCALN_V; break;
case AB_GLOBALN_R: CPos->bccType = AB_GLOBALN_V; break;
default: break;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
int C4AulParseState::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;
return a->GetCodePos();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
static bool IsJump(C4AulBCCType t)
2010-03-28 18:58:01 +00:00
{
return t == AB_JUMP || t == AB_JUMPAND || t == AB_JUMPOR || t == AB_CONDN || t == AB_COND;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::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
C4AulBCC *pBCC = a->GetCodeByPos(iJumpOp);
assert(IsJump(pBCC->bccType));
pBCC->Par.i = a->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
void C4AulParseState::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
C4AulBCC *pBCC = a->GetCodeByPos(iJumpOp);
assert(IsJump(pBCC->bccType));
pBCC->Par.i = iWhere - iJumpOp;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::AddJump(C4AulBCCType eType, int iWhere)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
AddBCC(eType, iWhere - a->GetCodePos());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::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
void C4AulParseState::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
void C4AulParseState::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;
pNew->Pos = a->GetCodePos();
pNew->Next = pLoopStack->Controls;
pLoopStack->Controls = pNew;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
const char * C4AulParseState::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_BOOL: return "boolean constant";
case ATT_STRING: return "string constant";
case ATT_C4ID: return "id constant";
case ATT_NIL: return "nil";
case ATT_COMMA: return "','";
case ATT_COLON: return "':'";
case ATT_DCOLON: 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_SEP: return "'|'";
case ATT_CALL: return "'->'";
case ATT_STAR: return "'*'";
case ATT_AMP: return "'&'";
case ATT_TILDE: return "'~'";
case ATT_LDOTS: return "'...'";
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
void C4AulParseState::Shift(HoldStringsPolicy HoldStrings, bool bOperator)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
TokenType = GetNextToken(Idtf, &cInt, HoldStrings, bOperator);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::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
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::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
void C4AulScript::ParseFn(C4AulScriptFunc *Fn, bool fExprOnly)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// check if fn overloads other fn (all func tables are built now)
// *MUST* check Fn->Owner-list, because it may be the engine (due to linked globals)
2010-01-25 04:00:59 +00:00
if ((Fn->OwnerOverloaded = Fn->Owner->GetOverloadedFunc(Fn)))
2010-03-28 18:58:01 +00:00
if (Fn->Owner == Fn->OwnerOverloaded->Owner)
2009-05-08 13:28:41 +00:00
Fn->OwnerOverloaded->OverloadedBy=Fn;
// store byte code pos
// (relative position to code start; code pointer may change while
// parsing)
Fn->Code = (C4AulBCC *) (CPos - Code);
// parse
C4AulParseState state(Fn, this, C4AulParseState::PARSER);
// get first token
state.Shift();
2010-03-28 18:58:01 +00:00
if (!fExprOnly)
2009-05-08 13:28:41 +00:00
state.Parse_Function();
else
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
state.Parse_Expression();
AddBCC(AB_RETURN, 0, state.SPos);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// done
return;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::Parse_Script()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
int IncludeCount = 0;
bool fDone = false;
const char * SPos0 = SPos;
bool all_ok = true;
bool found_code = false;
2010-03-28 18:58:01 +00:00
while (!fDone) try
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +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)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
Shift();
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
SPos0 = SPos;
switch (TokenType)
2009-05-08 13:28:41 +00:00
{
case ATT_DIR:
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (found_code)
Warn(FormatString("Found %s after declarations", Idtf).getData());
// check for include statement
if (SEqual(Idtf, C4AUL_Include))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Shift();
// get id of script to include
if (TokenType != ATT_IDTF)
UnexpectedToken("identifier");
C4ID Id = C4ID(StdStrBuf(Idtf));
2009-05-08 13:28:41 +00:00
Shift();
// add to include list
C4AListEntry *e = a->Engine->itbl.push(Id);
if (!a->Includes) a->Includes = e;
IncludeCount++;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if (SEqual(Idtf, C4AUL_Append))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// for #appendto * '*' needs to be ATT_STAR, not an operator.
Shift(Hold, false);
// get id of script to include/append
C4ID Id;
switch (TokenType)
2010-03-28 18:58:01 +00:00
{
case ATT_IDTF:
Id = C4ID(StdStrBuf(Idtf));
2010-03-28 18:58:01 +00:00
Shift();
break;
case ATT_STAR: // "*"
Id = C4ID::None;
Shift();
break;
default:
// -> ID expected
UnexpectedToken("identifier or '*'");
}
2009-05-08 13:28:41 +00:00
// add to append list
C4AListEntry *e = a->Engine->atbl.push(Id);
if (!a->Appends) a->Appends = e;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else if (SEqual(Idtf, C4AUL_Strict))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// declare it as strict
a->Strict = C4AulScript::STRICT1;
Shift();
if (TokenType == ATT_INT)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (cInt == 2)
a->Strict = C4AulScript::STRICT2;
else
throw new C4AulParseError(this, "unknown strict level");
Shift();
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
// -> unknown directive
throw new C4AulParseError(this, "unknown directive: ", Idtf);
break;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
case ATT_IDTF:
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
found_code = true;
2010-03-28 18:58:01 +00:00
if (SEqual(Idtf, C4AUL_For))
{
2009-05-08 13:28:41 +00:00
throw new C4AulParseError(this, "unexpected for outside function");
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// check for variable definition (var)
2010-03-28 18:58:01 +00:00
else if (SEqual(Idtf, C4AUL_VarNamed))
{
2009-05-08 13:28:41 +00:00
throw new C4AulParseError(this, "unexpected variable definition outside function");
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// check for object-local variable definition (local)
2010-03-28 18:58:01 +00:00
else if (SEqual(Idtf, C4AUL_LocalNamed))
{
2009-05-08 13:28:41 +00:00
Shift();
Parse_Local();
Match(ATT_SCOLON);
break;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// check for variable definition (static)
2010-03-28 18:58:01 +00:00
else if (SEqual(Idtf, C4AUL_GlobalNamed))
{
2009-05-08 13:28:41 +00:00
Shift();
// constant?
if (TokenType == ATT_IDTF && SEqual(Idtf, C4AUL_Const))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Shift();
Parse_Const();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
Parse_Static();
Match(ATT_SCOLON);
break;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
Parse_FuncHead();
break;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
case ATT_EOF:
fDone = true;
break;
default:
UnexpectedToken("declaration");
}
2010-03-28 18:58:01 +00:00
all_ok = true;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
catch (C4AulError *err)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
// damn! something went wrong, print it out
// but only one error per function
if (all_ok)
err->show();
all_ok = false;
delete err;
2009-05-08 13:28:41 +00:00
}
// includes were added?
if (a->Includes)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// reverse include order, for compatiblity with the C4Script syntax
if (IncludeCount > 1)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4AListEntry *i = a->Includes;
while (IncludeCount > 1)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4AListEntry *i2 = i;
for (int cnt = IncludeCount - 1; cnt; cnt--) i2 = i2->next();
C4AListEntry xchg = *i; *i = *i2; *i2 = xchg;
i = i->next(); IncludeCount -= 2;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// push stop entry for include list
a->Engine->itbl.push();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// appends were added?
if (a->Appends)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// push stop entry for append list
a->Engine->atbl.push();
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::Parse_FuncHead()
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
if (SEqual(Idtf, C4AUL_Func))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Shift(Discard, false);
bool bReturnRef = false;
// get next token, must be func name or "&"
if (TokenType == ATT_AMP)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
bReturnRef = true;
Shift(Discard, false);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
if (TokenType != ATT_IDTF)
UnexpectedToken("function name");
// check: symbol already in use?
2010-03-28 18:58:01 +00:00
switch (Acc)
{
case AA_PRIVATE:
case AA_PROTECTED:
case AA_PUBLIC:
if (a->LocalNamed.GetItemNr(Idtf) != -1)
throw new C4AulParseError(this, "function definition: name already in use (local variable)");
if (a->Def)
break;
// func in global context: fallthru
case AA_GLOBAL:
if (a->Engine->GlobalNamedNames.GetItemNr(Idtf) != -1)
throw new C4AulParseError(this, "function definition: name already in use (global variable)");
if (a->Engine->GlobalConstNames.GetItemNr(Idtf) != -1)
Error("function definition: name already in use (global constant)", 0);
}
2009-05-08 13:28:41 +00:00
// create script fn
if (Acc == AA_GLOBAL)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// global func
Fn = new C4AulScriptFunc(a->Engine, Idtf);
C4AulFunc *FnLink = new C4AulFunc(a, NULL);
FnLink->LinkedTo = Fn; Fn->LinkedTo = FnLink;
Acc = AA_PUBLIC;
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
{
2009-05-08 13:28:41 +00:00
// normal, local func
Fn = new C4AulScriptFunc(a, Idtf);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// set up func (in the case we got an error)
Fn->Script = SPos; // temporary
Fn->Access = Acc; Fn->pOrgScript = a;
Fn->bReturnRef = bReturnRef;
2009-05-08 13:28:41 +00:00
Shift(Discard,false);
// expect an opening bracket now
if (TokenType != ATT_BOPEN)
UnexpectedToken("'('");
Shift(Discard,false);
// get pars
Fn->ParNamed.Reset(); // safety :)
int cpar = 0;
while (1)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// closing bracket?
if (TokenType == ATT_BCLOSE)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Fn->Script = SPos;
Shift();
// end of params
break;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +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
if (TokenType != ATT_IDTF && TokenType != ATT_AMP)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
UnexpectedToken("parameter or closing bracket");
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// type identifier?
if (SEqual(Idtf, C4AUL_TypeInt)) { Fn->ParType[cpar] = C4V_Int; Shift(Discard,false); }
else if (SEqual(Idtf, C4AUL_TypeBool)) { Fn->ParType[cpar] = C4V_Bool; Shift(Discard,false); }
else if (SEqual(Idtf, C4AUL_TypeC4ID)) { Fn->ParType[cpar] = C4V_PropList; Shift(Discard,false); }
2009-05-08 13:28:41 +00:00
else if (SEqual(Idtf, C4AUL_TypeC4Object)) { Fn->ParType[cpar] = C4V_C4Object; Shift(Discard,false); }
else if (SEqual(Idtf, C4AUL_TypePropList)) { Fn->ParType[cpar] = C4V_PropList; Shift(Discard,false); }
2009-05-08 13:28:41 +00:00
else if (SEqual(Idtf, C4AUL_TypeString)) { Fn->ParType[cpar] = C4V_String; Shift(Discard,false); }
else if (SEqual(Idtf, C4AUL_TypeArray)) { Fn->ParType[cpar] = C4V_Array; Shift(Discard,false); }
// ampersand?
2010-03-22 21:16:46 +00:00
if (TokenType == ATT_AMP) { Fn->ParType[cpar] = C4V_Ref; Shift(Discard,false); }
2009-05-08 13:28:41 +00:00
if (TokenType != ATT_IDTF)
2010-03-28 18:58:01 +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
{
2009-05-08 13:28:41 +00:00
Fn->ParNamed.AddName(Idtf);
Shift();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// end of params?
2010-03-28 18:58:01 +00:00
if (TokenType == ATT_BCLOSE)
{
2009-05-08 13:28:41 +00:00
Fn->Script = SPos;
Shift();
break;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// must be a comma now
if (TokenType != ATT_COMMA)
UnexpectedToken("comma or closing bracket");
Shift(Discard,false);
cpar++;
2010-03-28 18:58:01 +00:00
}
Fn->Script = SPos;
Match(ATT_BLOPEN);
2009-05-08 13:28:41 +00:00
Parse_Desc();
Parse_Function();
Match(ATT_BLCLOSE);
return;
}
2010-03-28 18:58:01 +00:00
throw new C4AulParseError(this, "Declaration expected, but found identifier ", Idtf);
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::Parse_Desc()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// check for function desc
if (TokenType == ATT_BOPEN2)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// parse for end of desc
const char *SPos0 = SPos;
int Len = 0;
int iBracketsOpen = 1;
while (true)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// another bracket open
if (*SPos == '[') iBracketsOpen++;
// a bracket closed
if (*SPos == ']') iBracketsOpen--;
// last bracket closed: at end of desc block
if (iBracketsOpen == 0) break;
// check for eof
if (!*SPos)
// -> function desc not closed
throw new C4AulParseError(this, "function desc not closed");
// next char
SPos++; Len++;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
SPos++;
// extract desc
Fn->Desc.Copy(SPos0, Len);
Fn->Script = SPos;
Shift();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
Fn->Desc.Clear();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::Parse_Function()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
iStack = 0;
Done = false;
while (!Done) switch (TokenType)
{
2010-03-28 18:58:01 +00:00
// a block end?
2009-05-08 13:28:41 +00:00
case ATT_BLCLOSE:
2010-03-28 18:58:01 +00:00
{
// all ok, insert a return
C4AulBCC * CPos = a->GetCodeByPos(Max(a->GetCodePos() - 1,0));
if (!CPos || CPos->bccType != AB_RETURN || fJump)
2010-03-28 18:58:01 +00:00
{
AddBCC(AB_DEBUG);
AddBCC(AB_NIL);
AddBCC(AB_RETURN);
2010-03-28 18:58:01 +00:00
}
// and break
Done = true;
// Do not blame this function for script errors between functions
Fn = 0;
return;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
case ATT_EOF:
2010-03-28 18:58:01 +00:00
{
Done = true;
2009-05-08 13:28:41 +00:00
return;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
default:
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Parse_Statement();
assert(!iStack);
}
2010-03-28 18:58:01 +00:00
}
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::Parse_Block()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Match(ATT_BLOPEN);
// insert block in byte code
while (1) switch (TokenType)
{
case ATT_BLCLOSE:
Shift();
return;
default:
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Parse_Statement();
break;
}
2010-03-28 18:58:01 +00:00
}
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::Parse_Statement()
2010-03-28 18:58:01 +00:00
{
2009-04-21 21:44:56 +00:00
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:
case ATT_OPERATOR:
case ATT_INT: // constant in cInt
case ATT_BOOL: // constant in cInt
case ATT_STRING: // reference in cInt
case ATT_C4ID: // converted ID in cInt
{
Parse_Expression();
SetNoRef();
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))
{
Shift();
Parse_Var();
}
// check for variable definition (local)
else if (SEqual(Idtf, C4AUL_LocalNamed))
{
Shift();
Parse_Local();
}
// check for variable definition (static)
else if (SEqual(Idtf, C4AUL_GlobalNamed))
{
Shift();
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
{
Shift();
Parse_If();
break;
}
else if (SEqual(Idtf, C4AUL_Else)) // else
{
throw new C4AulParseError(this, "misplaced 'else'");
}
else if (SEqual(Idtf, C4AUL_Do)) // while
{
Shift();
Parse_DoWhile();
break;
}
else if (SEqual(Idtf, C4AUL_While)) // while
{
Shift();
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*/
&& GetNextToken(Idtf, &cInt, Discard, true) == ATT_IDTF
&& 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();
}
if (!Fn->bReturnRef)
SetNoRef();
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();
SetNoRef();
AddBCC(AB_STACK, -1);
}
Match(ATT_SCOLON);
break;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
default:
{
// -> unexpected token
UnexpectedToken("statement");
}
}
}
2009-05-08 13:28:41 +00:00
int C4AulParseState::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;
do
switch (TokenType)
{
case ATT_BCLOSE:
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Shift();
// () -> size 0, (*,) -> size 2, (*,*,) -> size 3
if (size > 0)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
AddBCC(AB_INT, 0);
++size;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
fDone = true;
break;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
case ATT_COMMA:
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// got no parameter before a ","? then push a 0-constant
AddBCC(AB_INT, 0);
Shift();
++size;
break;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
case ATT_LDOTS:
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Shift();
// Push all unnamed parameters of the current function as parameters
int i = Fn->ParNamed.iSize;
while (size < iMaxCnt && i < C4AUL_MAX_Par)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
AddBCC(AB_PARN_R, i);
++i;
++size;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Do not allow more parameters even if there is place left
fDone = true;
Match(ATT_BCLOSE);
break;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
default:
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// get a parameter
Parse_Expression();
if (pFunc && (Type == PARSER))
2010-03-28 18:58:01 +00:00
{
2010-03-22 21:16:46 +00:00
bool anyfunctakesref = (pFunc->GetParType()[size] == C4V_Ref);
// pFunc either was the return value from a GetFirstFunc-Call or
2009-05-08 13:28:41 +00:00
// pFunc is the only function that could be called, so this loop is superflous
C4AulFunc * pFunc2 = pFunc;
2010-01-25 04:00:59 +00:00
while ((pFunc2 = a->Engine->GetNextSNFunc(pFunc2)))
2010-03-22 21:16:46 +00:00
if (pFunc2->GetParType()[size] == C4V_Ref) anyfunctakesref = true;
2009-05-08 13:28:41 +00:00
// Change the bytecode to the equivalent that does not produce a reference.
if (!anyfunctakesref)
SetNoRef();
C4V_Type from = C4V_Any;
C4V_Type to = pFunc->GetParType()[size];
2010-03-28 18:58:01 +00:00
switch ((a->CPos-1)->bccType)
{
case AB_INT: from = (a->CPos-1)->Par.i ? C4V_Int : C4V_Any; break;
case AB_STRING: from = C4V_String; break;
case AB_ARRAY: case AB_ARRAY_SLICE: from = C4V_Array; break;
2010-03-28 18:58:01 +00:00
case AB_BOOL: from = C4V_Bool; break;
// case AB_UNOP: case AB_BINOP: from = C4ScriptOpMap[(a->CPos-1)->Par.i].RetType; break;
case AB_FUNC:
if ((a->CPos-1)->Par.f) from = (a->CPos-1)->Par.f->GetRetType(); break;
case AB_CALL: case AB_CALLFS:
{
C4String * pName = (a->CPos-1)->Par.s;
C4AulFunc * pFunc2 = a->Engine->GetFirstFunc(pName->GetCStr());
bool allwarn = true;
while (pFunc2 && allwarn)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
from = pFunc2->GetRetType();
allwarn = allwarn && C4Value::WarnAboutConversion(from, to);
pFunc2 = a->Engine->GetNextSNFunc(pFunc2);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
case AB_ARRAYA_R: case AB_PAR_R: case AB_PARN_R: case AB_VARN_R: case AB_LOCALN_R: case AB_GLOBALN_R:
from = C4V_Ref; break;
default: break; // avoid compiler warning about unhandled enumerators
}
if (C4Value::WarnAboutConversion(from, to))
2010-03-28 18:58:01 +00:00
{
Warn(FormatString("Conversion from \"%s\" to \"%s\" in parameter %d of \"%s\"", GetC4VName(from), GetC4VName(to), size, sWarn).getData(), NULL);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +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
{
2009-05-08 13:28:41 +00:00
Shift();
fDone = true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else UnexpectedToken("',' or ')'");
break;
}
2010-03-28 18:58:01 +00:00
}
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)
2009-05-08 13:28:41 +00:00
Warn(FormatString("%s: passing %d parameters, but only %d are used", sWarn, size, iMaxCnt).getData(), NULL);
// Balance stack
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
void C4AulParseState::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;
do
switch (TokenType)
{
case ATT_BCLOSE2:
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Shift();
// [] -> size 0, [*,] -> size 2, [*,*,] -> size 3
if (size > 0)
2010-03-28 18:58:01 +00:00
{
AddBCC(AB_NIL);
2009-05-08 13:28:41 +00:00
++size;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
fDone = true;
break;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
case ATT_COMMA:
2010-03-28 18:58:01 +00:00
{
// got no parameter before a ","? then push nil
AddBCC(AB_NIL);
2009-05-08 13:28:41 +00:00
Shift();
++size;
break;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
default:
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Parse_Expression();
++size;
if (TokenType == ATT_COMMA)
Shift();
else if (TokenType == ATT_BCLOSE2)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Shift();
fDone = true;
break;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
UnexpectedToken("',' or ']'");
}
2010-03-28 18:58:01 +00:00
}
while (!fDone);
2009-05-08 13:28:41 +00:00
// add terminator
AddBCC(AB_ARRAY, size);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::Parse_PropList()
2010-03-28 18:58:01 +00:00
{
AddBCC(AB_PROPLIST);
Shift();
// insert block in byte code
while (1)
2010-03-28 18:58:01 +00:00
{
if (TokenType == ATT_BLCLOSE)
2010-03-28 18:58:01 +00:00
{
Shift();
return;
2010-03-28 18:58:01 +00:00
}
C4String * pKey;
if (TokenType == ATT_IDTF)
2010-03-28 18:58:01 +00:00
{
pKey = Strings.RegString(Idtf);
AddBCC(AB_STRING, (intptr_t) pKey);
Shift();
2010-03-28 18:58:01 +00:00
}
else if (TokenType == ATT_STRING)
2010-03-28 18:58:01 +00:00
{
AddBCC(AB_STRING, cInt);
Shift();
2010-03-28 18:58:01 +00:00
}
else UnexpectedToken("string or identifier");
if (TokenType != ATT_COLON && (TokenType != ATT_OPERATOR || !SEqual(C4ScriptOpMap[cInt].Identifier,"=")))
UnexpectedToken("':' or '='");
Shift();
Parse_Expression();
AddBCC(AB_PROPSET);
if (TokenType == ATT_COMMA)
Shift();
else if (TokenType != ATT_BLCLOSE)
UnexpectedToken("'}' or ','");
}
2010-03-28 18:58:01 +00:00
}
void C4AulParseState::Parse_DoWhile()
2010-03-28 18:58:01 +00:00
{
// Save position for later jump back
int iStart = JumpHere();
// We got a loop
PushLoop();
// Execute body
Parse_Statement();
// Execute condition
if (TokenType != ATT_IDTF || !SEqual(Idtf, C4AUL_While))
UnexpectedToken("'while'");
Shift();
Match(ATT_BOPEN);
Parse_Expression();
Match(ATT_BCLOSE);
SetNoRef();
// Jump back
AddJump(AB_COND, iStart);
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)
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
void C4AulParseState::Parse_While()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Save position for later jump back
int iStart = JumpHere();
// Execute condition
Match(ATT_BOPEN);
Parse_Expression();
Match(ATT_BCLOSE);
2009-05-08 13:28:41 +00:00
SetNoRef();
// Check condition
int iCond = a->GetCodePos();
AddBCC(AB_CONDN);
// 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
void C4AulParseState::Parse_If()
2010-03-28 18:58:01 +00:00
{
Match(ATT_BOPEN);
Parse_Expression();
Match(ATT_BCLOSE);
2009-05-08 13:28:41 +00:00
SetNoRef();
// create bytecode, remember position
int iCond = a->GetCodePos();
AddBCC(AB_CONDN);
// 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
int iJump = a->GetCodePos();
AddBCC(AB_JUMP);
// 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
void C4AulParseState::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
Shift();
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();
SetNoRef();
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();
SetNoRef();
// Jump out
iJumpOut = a->GetCodePos();
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
iJumpBody = a->GetCodePos();
AddBCC(AB_JUMP);
// Add incrementor code
iIncrementor = JumpHere();
Parse_Expression();
SetNoRef();
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
void C4AulParseState::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
int iStart = a->GetCodePos();
AddBCC(AB_FOREACH_NEXT, iVarID);
// jump out (FOREACH_NEXT will jump over this if
// we're not at the end of the array yet)
int iCond = a->GetCodePos();
AddBCC(AB_JUMP);
// 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
void C4AulParseState::Parse_Expression(int iParentPrio)
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_IDTF:
{
// check for parameter (par)
if (Fn->ParNamed.GetItemNr(Idtf) != -1)
{
// insert variable by id
AddBCC(AB_PARN_R, Fn->ParNamed.GetItemNr(Idtf));
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
AddBCC(AB_VARN_R, Fn->VarNamed.GetItemNr(Idtf));
Shift();
}
// check for variable (local)
else if (a->LocalNamed.GetItemNr(Idtf) != -1)
{
// 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);
AddBCC(AB_LOCALN_R, (intptr_t) pKey);
Shift();
}
// check for global variable (static)
else if (a->Engine->GlobalNamedNames.GetItemNr(Idtf) != -1)
{
// insert variable by id
AddBCC(AB_GLOBALN_R, a->Engine->GlobalNamedNames.GetItemNr(Idtf));
Shift();
}
// 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))
{
Error("return may not be used as a parameter", 0);
}
else if (SEqual(Idtf, C4AUL_Par))
{
// and for Par
Shift();
Parse_Params(1, C4AUL_Par);
AddBCC(AB_PAR_R);
}
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
}
else
{
// none of these? then it's a function
C4AulFunc *FoundFn;
// get regular function
if (Fn->Owner == &::ScriptEngine)
FoundFn = Fn->Owner->GetFuncRecursive(Idtf);
else
FoundFn = a->GetFuncRecursive(Idtf);
if (Type == PREPARSER)
{
2009-05-08 13:28:41 +00:00
Shift();
2010-03-28 18:58:01 +00:00
// The preparser just assumes that the syntax is correct: if no '(' follows, it must be a constant
if (TokenType == ATT_BOPEN)
Parse_Params(FoundFn ? FoundFn->GetParCount() : 10, Idtf, FoundFn);
}
else if (FoundFn)
{
2009-05-08 13:28:41 +00:00
Shift();
2010-03-28 18:58:01 +00:00
// Function parameters for all functions except "this", which can be used without
if (!SEqual(FoundFn->Name, C4AUL_this) || TokenType == ATT_BOPEN)
Parse_Params(FoundFn->GetParCount(), FoundFn->Name, FoundFn);
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
AddBCC(AB_STACK, FoundFn->GetParCount());
2010-04-18 20:02:01 +00:00
AddBCC(AB_FUNC, (intptr_t) FoundFn);
2010-03-28 18:58:01 +00:00
}
else
{
// -> func not found
// check for global constant (static const)
// global constants have lowest priority for backwards compatibility
// it is now allowed to have functional overloads of these constants
C4Value val;
if (a->Engine->GetGlobalConstant(Idtf, &val))
{
// store as direct constant
switch (val.GetType())
{
case C4V_Int: AddBCC(AB_INT, val.GetData().Int); break;
case C4V_Bool: AddBCC(AB_BOOL, val.GetData().Int); break;
case C4V_String:
AddBCC(AB_STRING, reinterpret_cast<intptr_t>(val._getStr()));
break;
case C4V_PropList: AddBCC(AB_C4ID, val.getC4ID().GetHandle()); break;
case C4V_Any:
// any: allow zero
if (!val.GetData())
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
AddBCC(AB_NIL, 0);
break;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
// otherwise: fall through to error
default:
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
throw new C4AulParseError(this,FormatString("internal error: constant %s has undefined type %d", Idtf, val.GetType()).getData());
2009-05-08 13:28:41 +00:00
}
}
2010-03-28 18:58:01 +00:00
Shift();
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
// identifier could not be resolved
throw new C4AulParseError(this, "unknown identifier: ", Idtf);
2009-05-08 13:28:41 +00:00
}
}
2010-03-28 18:58:01 +00:00
}
break;
}
case ATT_INT: // constant in cInt
{
AddBCC(AB_INT, cInt);
Shift();
break;
}
case ATT_BOOL: // constant in cInt
{
AddBCC(AB_BOOL, cInt);
Shift();
break;
}
case ATT_STRING: // reference in cInt
{
AddBCC(AB_STRING, cInt);
Shift();
break;
}
case ATT_C4ID: // converted ID in cInt
{
AddBCC(AB_C4ID, cInt);
Shift();
break;
}
case ATT_NIL:
{
AddBCC(AB_NIL);
Shift();
break;
}
case ATT_OPERATOR:
{
// -> must be a prefix operator
// get operator ID
int OpID = cInt;
// postfix?
if (C4ScriptOpMap[OpID].Postfix)
// oops. that's wrong
throw new C4AulParseError(this, "postfix operator without first expression");
Shift();
// generate code for the following expression
Parse_Expression(C4ScriptOpMap[OpID].Priority);
// ignore?
if (SEqual(C4ScriptOpMap[OpID].Identifier, "+"))
break;
2010-03-28 18:58:01 +00:00
// negate constant?
if (Type == PARSER && SEqual(C4ScriptOpMap[OpID].Identifier, "-"))
if ((a->CPos - 1)->bccType == AB_INT)
2009-05-08 13:28:41 +00:00
{
2010-03-28 18:58:01 +00:00
(a->CPos - 1)->Par.i = -(a->CPos - 1)->Par.i;
2009-05-08 13:28:41 +00:00
break;
}
2010-03-28 18:58:01 +00:00
// write byte code
AddBCC(C4ScriptOpMap[OpID].Code, OpID);
break;
}
case ATT_BOPEN:
{
// parse it like a function...
Shift();
Parse_Expression();
Match(ATT_BCLOSE);
break;
}
case ATT_BOPEN2:
{
Parse_Array();
break;
}
case ATT_BLOPEN:
{
Parse_PropList();
break;
}
default:
{
// -> unexpected token
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
void C4AulParseState::Parse_Expression2(int iParentPrio)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
while (1) switch (TokenType)
{
case ATT_OPERATOR:
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// expect postfix operator
int OpID = cInt;
2010-03-28 18:58:01 +00:00
if (!C4ScriptOpMap[OpID].Postfix)
{
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.
int nOpID;
2010-03-28 18:58:01 +00:00
for (nOpID = OpID+1; C4ScriptOpMap[nOpID].Identifier; nOpID++)
if (SEqual(C4ScriptOpMap[OpID].Identifier, C4ScriptOpMap[nOpID].Identifier))
if (C4ScriptOpMap[nOpID].Postfix)
2009-05-08 13:28:41 +00:00
break;
// not found?
2010-03-28 18:58:01 +00:00
if (!C4ScriptOpMap[nOpID].Identifier)
{
2009-05-08 13:28:41 +00:00
throw new C4AulParseError(this, "unexpected prefix operator: ", C4ScriptOpMap[OpID].Identifier);
}
2010-03-28 18:58:01 +00:00
// otherwise use the new-found correct postfix operator
OpID = nOpID;
}
2009-05-08 13:28:41 +00:00
// lower priority?
2010-03-28 18:58:01 +00:00
if (C4ScriptOpMap[OpID].RightAssociative ?
C4ScriptOpMap[OpID].Priority < iParentPrio :
C4ScriptOpMap[OpID].Priority <= iParentPrio)
2009-05-08 13:28:41 +00:00
return;
// If the operator does not modify the first argument, no reference is necessary
2010-03-28 18:58:01 +00:00
if (C4ScriptOpMap[OpID].Type1 != C4V_Ref)
2009-05-08 13:28:41 +00:00
SetNoRef();
Shift();
if (C4ScriptOpMap[OpID].Code == AB_JUMPAND || C4ScriptOpMap[OpID].Code == AB_JUMPOR)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// create bytecode, remember position
int iCond = a->GetCodePos();
// Jump or discard first parameter
AddBCC(C4ScriptOpMap[OpID].Code);
2009-05-08 13:28:41 +00:00
// parse second expression
Parse_Expression(C4ScriptOpMap[OpID].Priority);
// set condition jump target
SetJumpHere(iCond);
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
{
2009-05-08 13:28:41 +00:00
// expect second parameter for operator
2010-03-28 18:58:01 +00:00
if (!C4ScriptOpMap[OpID].NoSecondStatement)
{
Parse_Expression(C4ScriptOpMap[OpID].Priority);
// If the operator does not modify the second argument, no reference is necessary
2010-03-28 18:58:01 +00:00
if (C4ScriptOpMap[OpID].Type2 != C4V_Ref)
SetNoRef();
2010-03-28 18:58:01 +00:00
}
// write byte code
AddBCC(C4ScriptOpMap[OpID].Code, OpID);
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
break;
}
2009-05-08 13:28:41 +00:00
case ATT_BOPEN2:
2010-03-28 18:58:01 +00:00
{
// parse either [index], or [start:end] in which case either index is optional
2009-05-08 13:28:41 +00:00
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_R);
}
else if (TokenType == ATT_COLON)
{
Shift();
if (TokenType == ATT_BCLOSE2)
{
Shift();
AddBCC(AB_INT, INT_MAX); // second index missing -> implicit end index GetLength()
}
else
{
Parse_Expression();
Match(ATT_BCLOSE2);
}
AddBCC(AB_ARRAY_SLICE);
}
else
{
UnexpectedToken("']' or ':'");
}
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
case ATT_CALL:
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Here, a '~' is not an operator, but a token
Shift(Discard, false);
// C4ID -> namespace given
C4AulFunc *pFunc = NULL;
C4String *pName = NULL;
2009-05-08 13:28:41 +00:00
C4AulBCCType eCallType = AB_CALL;
2010-03-28 18:58:01 +00:00
// may it be a failsafe call?
if (TokenType == ATT_TILDE)
{
// store this and get the next token
eCallType = AB_CALLFS;
Shift();
}
// expect identifier of called function now
if (TokenType != ATT_IDTF) throw new C4AulParseError(this, "expecting func name after '->'");
// search a function with the given name
if (!(pFunc = a->Engine->GetFirstFunc(Idtf)))
{
// not failsafe?
if (eCallType != AB_CALLFS && Type == PARSER)
throw new C4AulParseError(this, FormatString("direct object call: function %s not found", Idtf).getData());
// otherwise: nothing to call - just execute parameters and discard them
Shift();
Parse_Params(0, NULL);
// remove target from stack, push a zero value as result
AddBCC(AB_STACK, -1); AddBCC(AB_STACK, +1);
// done
break;
}
if (Type == PARSER)
2009-07-21 14:46:39 +00:00
pName = ::Strings.RegString(Idtf);
2009-05-08 13:28:41 +00:00
// add call chunk
Shift();
Parse_Params(C4AUL_MAX_Par, pName ? pName->GetCStr() : Idtf, pFunc);
AddBCC(eCallType, reinterpret_cast<intptr_t>(pName));
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
default:
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
return;
}
2010-03-28 18:58:01 +00:00
}
}
2009-05-08 13:28:41 +00:00
void C4AulParseState::Parse_Var()
2010-03-28 18:58:01 +00:00
{
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();
if (TokenType == ATT_OPERATOR)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// only accept "="
int iOpID = cInt;
2010-03-28 18:58:01 +00:00
if (SEqual(C4ScriptOpMap[iOpID].Identifier,"="))
{
2009-05-08 13:28:41 +00:00
// insert initialization in byte code
Shift();
Parse_Expression();
AddBCC(AB_IVARN, iVarID);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
throw new C4AulParseError(this, "unexpected operator");
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
void C4AulParseState::Parse_Local()
2010-03-28 18:58:01 +00:00
{
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?
2010-03-28 18:58:01 +00:00
if (a->GetFunc(Idtf))
2009-05-08 13:28:41 +00:00
throw new C4AulParseError(this, "variable definition: name already in use");
// insert variable
a->LocalNamed.AddName(Idtf);
2010-03-28 18:58:01 +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
void C4AulParseState::Parse_Static()
2010-03-28 18:58:01 +00:00
{
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?
if (a->Engine->GetFuncRecursive(Idtf)) Error("function and variable with name ", Idtf);
if (a->Engine->GetGlobalConstant(Idtf, NULL)) Error("constant and variable with name ", Idtf);
2009-05-08 13:28:41 +00:00
// insert variable if not defined already
if (a->Engine->GlobalNamedNames.GetItemNr(Idtf) == -1)
{
2009-05-08 13:28:41 +00:00
a->Engine->GlobalNamedNames.AddName(Idtf);
}
}
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
void C4AulParseState::Parse_Const()
2010-03-28 18:58:01 +00:00
{
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);
// check func lists - functions of same name are allowed for backwards compatibility
// (e.g., for overloading constants such as OCF_Living() in chaos scenarios)
// it is not encouraged though, so better warn
2010-03-28 18:58:01 +00:00
if (a->Engine->GetFuncRecursive(Idtf))
Error("definition of constant hidden by function ", Idtf);
2010-03-28 18:58:01 +00:00
if (a->Engine->GlobalNamedNames.GetItemNr(Idtf) != -1)
Error("constant and variable with name ", Idtf);
2009-05-08 13:28:41 +00:00
Match(ATT_IDTF);
// expect '='
if (TokenType != ATT_OPERATOR || !SEqual(C4ScriptOpMap[cInt].Identifier,"="))
UnexpectedToken("'='");
// expect value. Theoretically, something like C4AulScript::ExecOperator could be used here
// this would allow for definitions like "static const OCF_Edible = 1<<23"
// 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)
// So allow only direct constants for now.
// Do not set a string constant to "Hold" (which would delete it in the next UnLink())
Shift(Ref, false);
if (Type == PREPARSER)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
C4Value vGlobalValue;
switch (TokenType)
2010-03-28 18:58:01 +00:00
{
case ATT_INT: vGlobalValue.SetInt(cInt); break;
case ATT_BOOL: vGlobalValue.SetBool(!!cInt); break;
case ATT_STRING: vGlobalValue.SetString(reinterpret_cast<C4String *>(cInt)); break; // increases ref count of C4String in cInt to 1
case ATT_NIL: vGlobalValue.Set0(); break;
case ATT_IDTF:
// identifier is only OK if it's another constant
if (!a->Engine->GetGlobalConstant(Idtf, &vGlobalValue))
2009-05-08 13:28:41 +00:00
UnexpectedToken("constant value");
2010-03-28 18:58:01 +00:00
break;
default:
UnexpectedToken("constant value");
}
2009-05-08 13:28:41 +00:00
// register as constant
a->Engine->RegisterGlobalConstant(Name, vGlobalValue);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// expect ',' (next global) or ';' (end of definition) now
Shift();
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
bool C4AulScript::Parse()
2010-03-28 18:58:01 +00:00
{
if (DEBUG_BYTECODE_DUMP)
2009-05-08 13:28:41 +00:00
{
C4ScriptHost * scripthost = 0;
if (Def) scripthost = &Def->Script;
if (scripthost) LogSilentF("parsing %s...\n", scripthost->GetFilePath());
2010-03-28 18:58:01 +00:00
else LogSilentF("parsing unknown...\n");
}
2009-05-08 13:28:41 +00:00
// parse children
C4AulScript *s = Child0;
2010-03-28 18:58:01 +00:00
while (s) { s->Parse(); s = s->Next; }
2009-05-08 13:28:41 +00:00
// check state
if (State != ASS_LINKED) return false;
2009-05-08 13:28:41 +00:00
// don't parse global funcs again, as they're parsed already through links
if (this == Engine) return false;
2009-05-08 13:28:41 +00:00
// delete existing code
ClearCode();
// parse script funcs
C4AulFunc *f;
for (f = Func0; f; f = f->Next)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// check whether it's a script func, or linked to one
C4AulScriptFunc *Fn;
if (!(Fn = f->SFunc()))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (f->LinkedTo) Fn = f->LinkedTo->SFunc();
// do only parse global funcs, because otherwise, the #append-links get parsed (->code overflow)
if (Fn) if (Fn->Owner != Engine) Fn=NULL;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
if (Fn)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// parse function
try
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
ParseFn(Fn);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
catch (C4AulError *err)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// do not show errors for System.c4g scripts that appear to be pure #appendto scripts
2010-03-28 18:58:01 +00:00
if (Fn->Owner->Def || !Fn->Owner->Appends)
2009-05-08 13:28:41 +00:00
{
// show
err->show();
// show a warning if the error is in a remote script
if (Fn->pOrgScript != this)
DebugLogF(" (as #appendto/#include to %s)", Fn->Owner->ScriptName.getData());
// and count (visible only ;) )
++::ScriptEngine.errCnt;
2009-05-08 13:28:41 +00:00
}
delete err;
// make all jumps that don't have their destination yet jump here
// intptr_t to make it work on 64bit
2010-03-28 18:58:01 +00:00
for (intptr_t i = reinterpret_cast<intptr_t>(Fn->Code); i < CPos - Code; i++)
{
2009-05-08 13:28:41 +00:00
C4AulBCC *pBCC = Code + i;
2010-03-28 18:58:01 +00:00
if (IsJump(pBCC->bccType))
if (!pBCC->Par.i)
pBCC->Par.i = CPos - Code - i;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// add an error chunk
AddBCC(AB_ERR);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
2010-04-01 21:08:06 +00:00
// add separator
2009-05-08 13:28:41 +00:00
AddBCC(AB_EOFN);
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// add eof chunk
AddBCC(AB_EOF);
// calc absolute code addresses for script funcs
for (f = Func0; f; f = f->Next)
{
C4AulScriptFunc *Fn;
if (!(Fn = f->SFunc()))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (f->LinkedTo) Fn = f->LinkedTo->SFunc();
if (Fn) if (Fn->Owner != Engine) Fn=NULL;
2010-03-28 18:58:01 +00:00
}
if (Fn)
2010-04-18 20:02:01 +00:00
Fn->Code = Code + (intptr_t) Fn->Code;
2009-05-08 13:28:41 +00:00
}
// save line count
Engine->lineCnt += SGetLine(Script.getData(), Script.getPtr(Script.getLength()));
// dump bytecode
if (DEBUG_BYTECODE_DUMP)
for (f = Func0; f; f = f->Next)
{
C4AulScriptFunc *Fn;
if (!(Fn = f->SFunc()))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (f->LinkedTo) Fn = f->LinkedTo->SFunc();
if (Fn) if (Fn->Owner != Engine) Fn=NULL;
2010-03-28 18:58:01 +00:00
}
if (Fn)
2009-05-08 13:28:41 +00:00
{
LogSilentF("%s:", Fn->Name);
2010-03-28 18:58:01 +00:00
for (C4AulBCC *pBCC = Fn->Code;; pBCC++)
{
C4AulBCCType eType = pBCC->bccType;
2009-05-08 13:28:41 +00:00
switch (eType)
2010-03-28 18:58:01 +00:00
{
case AB_FUNC:
LogSilentF("%s\t'%s'\n", GetTTName(eType), pBCC->Par.f->Name); break;
case AB_STRING: case AB_CALL: case AB_CALLFS:
LogSilentF("%s\t'%s'\n", GetTTName(eType), pBCC->Par.s->GetCStr()); break;
default:
LogSilentF("%s\t%ld\n", GetTTName(eType), pBCC->Par.X); break;
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
if (eType == AB_EOFN) break;
}
2009-05-08 13:28:41 +00:00
}
}
// finished
State = ASS_PARSED;
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4AulScript::ParseDescs()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// parse children
C4AulScript *s = Child0;
2010-03-28 18:58:01 +00:00
while (s) { s->ParseDescs(); s = s->Next; }
2009-05-08 13:28:41 +00:00
// check state
if (State < ASS_LINKED) return;
// parse descs of all script funcs
for (C4AulFunc *f = Func0; f; f = f->Next)
if (C4AulScriptFunc *Fn = f->SFunc()) Fn->ParseDesc();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4AulScript *C4AulScript::FindFirstNonStrictScript()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// self is not #strict?
if (Script && Strict < MAXSTRICT) return this;
2009-05-08 13:28:41 +00:00
// search children
C4AulScript *pNonStrScr;
for (C4AulScript *pScr=Child0; pScr; pScr=pScr->Next)
2010-01-25 04:00:59 +00:00
if ((pNonStrScr=pScr->FindFirstNonStrictScript()))
2009-05-08 13:28:41 +00:00
return pNonStrScr;
// nothing found
return NULL;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
#undef DEBUG_BYTECODE_DUMP