|
|
|
@ -172,8 +172,7 @@ class C4AulParseState
|
|
|
|
|
const char * GetTokenName(C4AulTokenType TokenType);
|
|
|
|
|
|
|
|
|
|
void Warn(const char *pMsg, const char *pIdtf=0);
|
|
|
|
|
void StrictError(const char *pMsg, const char *pIdtf=0);
|
|
|
|
|
void Strict2Error(const char *pMsg, const char *pIdtf=0);
|
|
|
|
|
void Error(const char *pMsg, const char *pIdtf=0);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
|
@ -236,19 +235,9 @@ void C4AulParseState::Warn(const char *pMsg, const char *pIdtf)
|
|
|
|
|
++::ScriptEngine.warnCnt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void C4AulParseState::StrictError(const char *pMsg, const char *pIdtf)
|
|
|
|
|
void C4AulParseState::Error(const char *pMsg, const char *pIdtf)
|
|
|
|
|
{
|
|
|
|
|
if (Fn ? (Fn->pOrgScript->Strict < C4AulScript::STRICT1) : (a->Strict < C4AulScript::STRICT1))
|
|
|
|
|
Warn(pMsg, pIdtf);
|
|
|
|
|
else
|
|
|
|
|
throw new C4AulParseError(this, pMsg, pIdtf);
|
|
|
|
|
}
|
|
|
|
|
void C4AulParseState::Strict2Error(const char *pMsg, const char *pIdtf)
|
|
|
|
|
{
|
|
|
|
|
if (Fn ? (Fn->pOrgScript->Strict < C4AulScript::STRICT2) : (a->Strict < C4AulScript::STRICT2))
|
|
|
|
|
Warn(pMsg, pIdtf);
|
|
|
|
|
else
|
|
|
|
|
throw new C4AulParseError(this, pMsg, pIdtf);
|
|
|
|
|
throw new C4AulParseError(this, pMsg, pIdtf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
C4AulParseError::C4AulParseError(C4AulParseState * state, const char *pMsg, const char *pIdtf, BOOL Warn)
|
|
|
|
@ -457,8 +446,8 @@ C4ScriptOpDef C4ScriptOpMap[] = {
|
|
|
|
|
{ 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_And, 1, 0, 0, C4V_Bool, C4V_Bool, C4V_Bool},
|
|
|
|
|
{ 4, "||", AB_Or, 1, 0, 0, C4V_Bool, C4V_Bool, C4V_Bool},
|
|
|
|
|
{ 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_Any, C4V_pC4Value, C4V_Int},
|
|
|
|
|
{ 2, "/=", AB_DivIt, 1, 1, 0, C4V_Any, C4V_pC4Value, C4V_Int},
|
|
|
|
|
{ 2, "%=", AB_ModIt, 1, 1, 0, C4V_Any, C4V_pC4Value, C4V_Int},
|
|
|
|
@ -481,20 +470,11 @@ int C4AulParseState::GetOperator(const char* pScript)
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
if(!*pScript) return 0;
|
|
|
|
|
// text with > 2 characters or text and #strict 2?
|
|
|
|
|
// then break (may be misinterpreted as operator
|
|
|
|
|
// operators are not alphabetical
|
|
|
|
|
if((*pScript >= 'a' && *pScript <= 'z') ||
|
|
|
|
|
(*pScript >= 'A' && *pScript <= 'Z'))
|
|
|
|
|
{
|
|
|
|
|
if(Fn ? (Fn->pOrgScript->Strict >= C4AulScript::STRICT2) : (a->Strict >= C4AulScript::STRICT2))
|
|
|
|
|
return -1;
|
|
|
|
|
if((*(pScript+1) >= 'a' && *(pScript+1) <= 'z') ||
|
|
|
|
|
(*(pScript+1) >= 'A' && *(pScript+1) <= 'Z'))
|
|
|
|
|
if((*(pScript+2) >= 'a' && *(pScript+2) <= 'z') ||
|
|
|
|
|
(*(pScript+2) >= 'A' && *(pScript+2) <= 'Z') ||
|
|
|
|
|
(*(pScript+2) >= '0' && *(pScript+2) <= '9') ||
|
|
|
|
|
*(pScript+2) == '_')
|
|
|
|
|
return -1;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// it is a two-char-operator?
|
|
|
|
@ -646,14 +626,8 @@ C4AulTokenType C4AulParseState::GetNextToken(char *pToken, long int *pInt, HoldS
|
|
|
|
|
// identifier by all non-special chars
|
|
|
|
|
if (C >= '@')
|
|
|
|
|
{
|
|
|
|
|
// Old Scripts could have wacky identifier
|
|
|
|
|
if (a->Strict < C4AulScript::STRICT2)
|
|
|
|
|
{
|
|
|
|
|
State = TGS_Ident;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// But now only the alphabet and '_' is allowed
|
|
|
|
|
else if ((C >= 'A' && C <= 'Z') || (C >= 'a' && C <= 'z') || (C == '_'))
|
|
|
|
|
// only the alphabet and '_' is allowed
|
|
|
|
|
if ((C >= 'A' && C <= 'Z') || (C >= 'a' && C <= 'z') || (C == '_'))
|
|
|
|
|
{
|
|
|
|
|
State = TGS_Ident;
|
|
|
|
|
break;
|
|
|
|
@ -757,12 +731,6 @@ C4AulTokenType C4AulParseState::GetNextToken(char *pToken, long int *pInt, HoldS
|
|
|
|
|
// return integer
|
|
|
|
|
Len = Min(Len, C4AUL_MAX_Identifier);
|
|
|
|
|
SCopy(SPos0, pToken, Len);
|
|
|
|
|
// or is it a func label?
|
|
|
|
|
if ((C == '(') || (C == ':'))
|
|
|
|
|
{
|
|
|
|
|
Strict2Error("stupid func label: ", pToken);
|
|
|
|
|
return ATT_IDTF;
|
|
|
|
|
}
|
|
|
|
|
// do not parse 0x prefix for hex
|
|
|
|
|
if (State == TGS_IntHex) pToken += 2;
|
|
|
|
|
// it's not, so return the int
|
|
|
|
@ -779,12 +747,6 @@ C4AulTokenType C4AulParseState::GetNextToken(char *pToken, long int *pInt, HoldS
|
|
|
|
|
// return C4ID string
|
|
|
|
|
Len = Min(Len, C4AUL_MAX_Identifier);
|
|
|
|
|
SCopy(SPos0, pToken, Len);
|
|
|
|
|
// another stupid label identifier?
|
|
|
|
|
if ((C == '(') || (C == ':' && *(SPos+1) != ':'))
|
|
|
|
|
{
|
|
|
|
|
Strict2Error("stupid func label: ", pToken);
|
|
|
|
|
return ATT_IDTF;
|
|
|
|
|
}
|
|
|
|
|
// check if valid
|
|
|
|
|
if (!LooksLikeID(pToken)) throw new C4AulParseError(this, "erroneous Ident: ", pToken);
|
|
|
|
|
// get id of it
|
|
|
|
@ -886,17 +848,13 @@ static const char * GetTTName(C4AulBCCType e)
|
|
|
|
|
case AB_LessThanEqual: return "LessThanEqual"; // <=
|
|
|
|
|
case AB_GreaterThan: return "GreaterThan"; // >
|
|
|
|
|
case AB_GreaterThanEqual: return "GreaterThanEqual"; // >=
|
|
|
|
|
case AB_EqualIdent: return "EqualIdent"; // old ==
|
|
|
|
|
case AB_Equal: return "Equal"; // new ==
|
|
|
|
|
case AB_NotEqualIdent: return "NotEqualIdent"; // old !=
|
|
|
|
|
case AB_NotEqual: return "NotEqual"; // new !=
|
|
|
|
|
case AB_Equal: return "Equal"; // ==
|
|
|
|
|
case AB_NotEqual: return "NotEqual"; // !=
|
|
|
|
|
case AB_SEqual: return "SEqual"; // S=, eq
|
|
|
|
|
case AB_SNEqual: return "SNEqual"; // ne
|
|
|
|
|
case AB_BitAnd: return "BitAnd"; // &
|
|
|
|
|
case AB_BitXOr: return "BitXOr"; // ^
|
|
|
|
|
case AB_BitOr: return "BitOr"; // |
|
|
|
|
|
case AB_And: return "And"; // &&
|
|
|
|
|
case AB_Or: return "Or"; // ||
|
|
|
|
|
case AB_MulIt: return "MulIt"; // *=
|
|
|
|
|
case AB_DivIt: return "DivIt"; // /=
|
|
|
|
|
case AB_ModIt: return "ModIt"; // %=
|
|
|
|
@ -1005,7 +963,7 @@ BOOL C4AulScript::Preparse()
|
|
|
|
|
state.Parse_Script();
|
|
|
|
|
|
|
|
|
|
// no #strict? we don't like that :(
|
|
|
|
|
if (!Strict)
|
|
|
|
|
if (Strict < MAXSTRICT)
|
|
|
|
|
{
|
|
|
|
|
Engine->nonStrictCnt++;
|
|
|
|
|
}
|
|
|
|
@ -1057,16 +1015,12 @@ void C4AulParseState::AddBCC(C4AulBCCType eType, intptr_t X)
|
|
|
|
|
case AB_GreaterThan:
|
|
|
|
|
case AB_GreaterThanEqual:
|
|
|
|
|
case AB_Equal:
|
|
|
|
|
case AB_EqualIdent:
|
|
|
|
|
case AB_NotEqual:
|
|
|
|
|
case AB_NotEqualIdent:
|
|
|
|
|
case AB_SEqual:
|
|
|
|
|
case AB_SNEqual:
|
|
|
|
|
case AB_BitAnd:
|
|
|
|
|
case AB_BitXOr:
|
|
|
|
|
case AB_BitOr:
|
|
|
|
|
case AB_And:
|
|
|
|
|
case AB_Or:
|
|
|
|
|
case AB_MulIt:
|
|
|
|
|
case AB_DivIt:
|
|
|
|
|
case AB_ModIt:
|
|
|
|
@ -1526,7 +1480,7 @@ void C4AulParseState::Parse_FuncHead()
|
|
|
|
|
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)
|
|
|
|
|
Strict2Error("function definition: name already in use (global variable)", 0);
|
|
|
|
|
Error("function definition: name already in use (global constant)", 0);
|
|
|
|
|
}
|
|
|
|
|
// create script fn
|
|
|
|
|
if (Acc == AA_GLOBAL)
|
|
|
|
@ -1545,7 +1499,7 @@ void C4AulParseState::Parse_FuncHead()
|
|
|
|
|
// set up func (in the case we got an error)
|
|
|
|
|
Fn->Script = SPos; // temporary
|
|
|
|
|
Fn->Access = Acc; Fn->pOrgScript = a;
|
|
|
|
|
Fn->bNewFormat = true; Fn->bReturnRef = bReturnRef;
|
|
|
|
|
Fn->bReturnRef = bReturnRef;
|
|
|
|
|
Shift(Discard,false);
|
|
|
|
|
// expect an opening bracket now
|
|
|
|
|
if (TokenType != ATT_BOPEN)
|
|
|
|
@ -1572,11 +1526,6 @@ void C4AulParseState::Parse_FuncHead()
|
|
|
|
|
{
|
|
|
|
|
UnexpectedToken("parameter or closing bracket");
|
|
|
|
|
}
|
|
|
|
|
char TypeIdtf[C4AUL_MAX_Identifier] = ""; // current identifier
|
|
|
|
|
if (TokenType == ATT_IDTF)
|
|
|
|
|
{
|
|
|
|
|
SCopy(Idtf, TypeIdtf);
|
|
|
|
|
}
|
|
|
|
|
// 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); }
|
|
|
|
@ -1589,12 +1538,7 @@ void C4AulParseState::Parse_FuncHead()
|
|
|
|
|
if (TokenType == ATT_AMP) { Fn->ParType[cpar] = C4V_pC4Value; Shift(Discard,false); }
|
|
|
|
|
if (TokenType != ATT_IDTF)
|
|
|
|
|
{
|
|
|
|
|
if (SEqual(TypeIdtf,""))
|
|
|
|
|
UnexpectedToken("parameter or closing bracket");
|
|
|
|
|
// A parameter with the same name as a type
|
|
|
|
|
Strict2Error("parameter has the same name as type ", TypeIdtf);
|
|
|
|
|
Fn->ParType[cpar] = C4V_Any;
|
|
|
|
|
Fn->ParNamed.AddName(TypeIdtf);
|
|
|
|
|
UnexpectedToken("parameter name");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@ -1614,132 +1558,14 @@ void C4AulParseState::Parse_FuncHead()
|
|
|
|
|
Shift(Discard,false);
|
|
|
|
|
cpar++;
|
|
|
|
|
}
|
|
|
|
|
// ok, expect opening block
|
|
|
|
|
if(TokenType != ATT_BLOPEN)
|
|
|
|
|
{
|
|
|
|
|
// warn
|
|
|
|
|
Strict2Error("'func': expecting opening block ('{') after func declaration", 0);
|
|
|
|
|
// not really new syntax (a sort of legacy mode)
|
|
|
|
|
Fn->bNewFormat = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Fn->Script = SPos;
|
|
|
|
|
Shift();
|
|
|
|
|
}
|
|
|
|
|
Fn->Script = SPos;
|
|
|
|
|
Match(ATT_BLOPEN);
|
|
|
|
|
Parse_Desc();
|
|
|
|
|
Parse_Function();
|
|
|
|
|
Match(ATT_BLCLOSE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Must be old-style function declaration now
|
|
|
|
|
if (a->Strict >= C4AulScript::STRICT2)
|
|
|
|
|
throw new C4AulParseError(this, "Declaration expected, but found identifier ", Idtf);
|
|
|
|
|
// check: symbol already in use?
|
|
|
|
|
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)");
|
|
|
|
|
}
|
|
|
|
|
char FuncIdtf[C4AUL_MAX_Identifier] = ""; // current identifier
|
|
|
|
|
SCopy(Idtf, FuncIdtf);
|
|
|
|
|
Shift();
|
|
|
|
|
if (TokenType != ATT_COLON)
|
|
|
|
|
throw new C4AulParseError(this, FormatString("declaration expected, but found identifier '%s'", FuncIdtf).getData());
|
|
|
|
|
// create script fn
|
|
|
|
|
if (Acc == AA_GLOBAL)
|
|
|
|
|
{
|
|
|
|
|
// global func
|
|
|
|
|
Fn = new C4AulScriptFunc(a->Engine, FuncIdtf);
|
|
|
|
|
C4AulFunc *FnLink = new C4AulFunc(a, NULL);
|
|
|
|
|
FnLink->LinkedTo = Fn; Fn->LinkedTo = FnLink;
|
|
|
|
|
Acc = AA_PUBLIC;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// normal, local func
|
|
|
|
|
Fn = new C4AulScriptFunc(a, FuncIdtf);
|
|
|
|
|
}
|
|
|
|
|
Fn->Script = SPos;
|
|
|
|
|
Fn->Access = Acc;
|
|
|
|
|
Fn->pOrgScript = a;
|
|
|
|
|
Fn->bNewFormat = false;
|
|
|
|
|
Fn->bReturnRef = false;
|
|
|
|
|
Shift();
|
|
|
|
|
Parse_Desc();
|
|
|
|
|
const char * SPos0 = SPos;
|
|
|
|
|
while(1) switch(TokenType)
|
|
|
|
|
{
|
|
|
|
|
// end of function
|
|
|
|
|
case ATT_EOF: case ATT_DIR: return;
|
|
|
|
|
case ATT_IDTF:
|
|
|
|
|
{
|
|
|
|
|
// check for func declaration
|
|
|
|
|
if (SEqual(Idtf, C4AUL_Private)) return;
|
|
|
|
|
else if (SEqual(Idtf, C4AUL_Protected)) return;
|
|
|
|
|
else if (SEqual(Idtf, C4AUL_Public)) return;
|
|
|
|
|
else if (SEqual(Idtf, C4AUL_Global)) return;
|
|
|
|
|
else if (SEqual(Idtf, C4AUL_Func)) return;
|
|
|
|
|
// check for variable definition (var)
|
|
|
|
|
else if(SEqual(Idtf, C4AUL_VarNamed))
|
|
|
|
|
{
|
|
|
|
|
Shift();
|
|
|
|
|
Parse_Var();
|
|
|
|
|
}
|
|
|
|
|
// check for variable definition (local)
|
|
|
|
|
else if(SEqual(Idtf, C4AUL_LocalNamed))
|
|
|
|
|
{
|
|
|
|
|
if(a->Def)
|
|
|
|
|
{
|
|
|
|
|
Shift();
|
|
|
|
|
Parse_Local();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
throw new C4AulParseError(this, "'local' variable declaration in global script");
|
|
|
|
|
}
|
|
|
|
|
// check for variable definition (static)
|
|
|
|
|
else if(SEqual(Idtf, C4AUL_GlobalNamed))
|
|
|
|
|
{
|
|
|
|
|
Shift();
|
|
|
|
|
Parse_Static();
|
|
|
|
|
}
|
|
|
|
|
// might be old style declaration
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
const char * SPos0_ = SPos;
|
|
|
|
|
Shift();
|
|
|
|
|
if (TokenType == ATT_COLON)
|
|
|
|
|
{
|
|
|
|
|
// Reset source position to the point before the label
|
|
|
|
|
SPos = SPos0;
|
|
|
|
|
Shift();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// The current token might be a label
|
|
|
|
|
// In that case the next round of the loop will need to reset the position to what's in SPos0_ now
|
|
|
|
|
SPos0 = SPos0_;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
SPos0 = SPos;
|
|
|
|
|
Shift();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
throw new C4AulParseError(this, "Declaration expected, but found identifier ", Idtf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void C4AulParseState::Parse_Desc()
|
|
|
|
@ -1785,23 +1611,18 @@ void C4AulParseState::Parse_Function()
|
|
|
|
|
// a block end?
|
|
|
|
|
case ATT_BLCLOSE:
|
|
|
|
|
{
|
|
|
|
|
// new-form func?
|
|
|
|
|
if(Fn->bNewFormat)
|
|
|
|
|
// all ok, insert a return
|
|
|
|
|
C4AulBCC * CPos = a->GetCodeByPos(Max(a->GetCodePos() - 1,0));
|
|
|
|
|
if (!CPos || CPos->bccType != AB_RETURN || fJump)
|
|
|
|
|
{
|
|
|
|
|
// all ok, insert a return
|
|
|
|
|
C4AulBCC * CPos = a->GetCodeByPos(Max(a->GetCodePos() - 1,0));
|
|
|
|
|
if (!CPos || CPos->bccType != AB_RETURN || fJump)
|
|
|
|
|
{
|
|
|
|
|
AddBCC(AB_INT);
|
|
|
|
|
AddBCC(AB_RETURN);
|
|
|
|
|
}
|
|
|
|
|
// and break
|
|
|
|
|
Done = TRUE;
|
|
|
|
|
// Do not blame this function for script errors between functions
|
|
|
|
|
Fn = 0;
|
|
|
|
|
return;
|
|
|
|
|
AddBCC(AB_NIL);
|
|
|
|
|
AddBCC(AB_RETURN);
|
|
|
|
|
}
|
|
|
|
|
throw new C4AulParseError(this, "no '{' found for '}'");
|
|
|
|
|
// and break
|
|
|
|
|
Done = TRUE;
|
|
|
|
|
// Do not blame this function for script errors between functions
|
|
|
|
|
Fn = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
case ATT_EOF:
|
|
|
|
|
{
|
|
|
|
@ -1967,23 +1788,11 @@ void C4AulParseState::Parse_Statement()
|
|
|
|
|
}
|
|
|
|
|
else if (SEqual(Idtf, C4AUL_Return)) // return
|
|
|
|
|
{
|
|
|
|
|
bool multi_params_hack = false;
|
|
|
|
|
Shift();
|
|
|
|
|
if (TokenType == ATT_BOPEN && Fn->pOrgScript->Strict < C4AulScript::STRICT2)
|
|
|
|
|
if(TokenType == ATT_SCOLON)
|
|
|
|
|
{
|
|
|
|
|
// parse return(retvals) - return(retval, unused, parameters, ...) allowed for backwards compatibility
|
|
|
|
|
if (Parse_Params(1, NULL) == 1)
|
|
|
|
|
{
|
|
|
|
|
// return (1 + 1) * 3 returns 6, not 2
|
|
|
|
|
Parse_Expression2();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
multi_params_hack = true;
|
|
|
|
|
}
|
|
|
|
|
else if(TokenType == ATT_SCOLON)
|
|
|
|
|
{
|
|
|
|
|
// allow return; without return value (implies 0)
|
|
|
|
|
AddBCC(AB_INT);
|
|
|
|
|
// allow return; without return value (implies nil)
|
|
|
|
|
AddBCC(AB_NIL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@ -1993,15 +1802,6 @@ void C4AulParseState::Parse_Statement()
|
|
|
|
|
if(!Fn->bReturnRef)
|
|
|
|
|
SetNoRef();
|
|
|
|
|
AddBCC(AB_RETURN);
|
|
|
|
|
if (multi_params_hack && TokenType != ATT_SCOLON)
|
|
|
|
|
{
|
|
|
|
|
// return (1, 1) * 3 returns 1
|
|
|
|
|
// but warn about it, the * 3 won't get executed and a stray ',' could lead to unexpected results
|
|
|
|
|
Warn("';' expected, but found ", GetTokenName(TokenType));
|
|
|
|
|
AddBCC(AB_STACK, +1);
|
|
|
|
|
Parse_Expression2();
|
|
|
|
|
AddBCC(AB_STACK, -1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (SEqual(Idtf, C4AUL_this)) // this on top level
|
|
|
|
|
{
|
|
|
|
@ -2017,7 +1817,7 @@ void C4AulParseState::Parse_Statement()
|
|
|
|
|
// Must be inside a loop
|
|
|
|
|
if(!pLoopStack)
|
|
|
|
|
{
|
|
|
|
|
Strict2Error("'break' is only allowed inside loops");
|
|
|
|
|
Error("'break' is only allowed inside loops");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@ -2037,7 +1837,7 @@ void C4AulParseState::Parse_Statement()
|
|
|
|
|
// Must be inside a loop
|
|
|
|
|
if(!pLoopStack)
|
|
|
|
|
{
|
|
|
|
|
Strict2Error("'continue' is only allowed inside loops");
|
|
|
|
|
Error("'continue' is only allowed inside loops");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@ -2093,23 +1893,14 @@ void C4AulParseState::Parse_Statement()
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
bool gotohack = false;
|
|
|
|
|
// none of these? then it's a function
|
|
|
|
|
// if it's a label, it will be missinterpreted here, which will be corrected later
|
|
|
|
|
// it may be the first goto() found? (old syntax only!)
|
|
|
|
|
if (SEqual(Idtf, C4AUL_Goto) && !Fn->pOrgScript->Strict)
|
|
|
|
|
// add AB_RETURN later on
|
|
|
|
|
gotohack=true;
|
|
|
|
|
C4AulFunc *FoundFn;
|
|
|
|
|
// old syntax: do not allow recursive calls in overloaded functions
|
|
|
|
|
if (!Fn->pOrgScript->Strict && Fn->OwnerOverloaded && SEqual(Idtf, Fn->Name))
|
|
|
|
|
FoundFn = Fn->OwnerOverloaded;
|
|
|
|
|
// get regular function
|
|
|
|
|
if(Fn->Owner == &::ScriptEngine)
|
|
|
|
|
FoundFn = a->Owner->GetFuncRecursive(Idtf);
|
|
|
|
|
else
|
|
|
|
|
// get regular function
|
|
|
|
|
if(Fn->Owner == &::ScriptEngine)
|
|
|
|
|
FoundFn = a->Owner->GetFuncRecursive(Idtf);
|
|
|
|
|
else
|
|
|
|
|
FoundFn = a->GetFuncRecursive(Idtf);
|
|
|
|
|
FoundFn = a->GetFuncRecursive(Idtf);
|
|
|
|
|
// ignore func-not-found errors in the preparser, because the function tables are not built yet
|
|
|
|
|
if (!FoundFn && Type == PARSER)
|
|
|
|
|
{
|
|
|
|
@ -2138,11 +1929,6 @@ void C4AulParseState::Parse_Statement()
|
|
|
|
|
if (TokenType == ATT_BOPEN || Type == PARSER)
|
|
|
|
|
Parse_Params(FoundFn ? FoundFn->GetParCount() : 10, FoundFn ? FoundFn->Name : Idtf, FoundFn);
|
|
|
|
|
AddBCC(AB_FUNC, (long) FoundFn);
|
|
|
|
|
if (gotohack)
|
|
|
|
|
{
|
|
|
|
|
AddBCC(AB_RETURN);
|
|
|
|
|
AddBCC(AB_STACK, +1);
|
|
|
|
|
}
|
|
|
|
|
Parse_Expression2();
|
|
|
|
|
SetNoRef();
|
|
|
|
|
AddBCC(AB_STACK, -1);
|
|
|
|
@ -2352,14 +2138,9 @@ void C4AulParseState::Parse_While()
|
|
|
|
|
// Save position for later jump back
|
|
|
|
|
int iStart = JumpHere();
|
|
|
|
|
// Execute condition
|
|
|
|
|
if (Fn->pOrgScript->Strict >= C4AulScript::STRICT2)
|
|
|
|
|
{
|
|
|
|
|
Match(ATT_BOPEN);
|
|
|
|
|
Parse_Expression();
|
|
|
|
|
Match(ATT_BCLOSE);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
Parse_Params(1, C4AUL_While);
|
|
|
|
|
Match(ATT_BOPEN);
|
|
|
|
|
Parse_Expression();
|
|
|
|
|
Match(ATT_BCLOSE);
|
|
|
|
|
SetNoRef();
|
|
|
|
|
// Check condition
|
|
|
|
|
int iCond = a->GetCodePos();
|
|
|
|
@ -2384,14 +2165,9 @@ void C4AulParseState::Parse_While()
|
|
|
|
|
|
|
|
|
|
void C4AulParseState::Parse_If()
|
|
|
|
|
{
|
|
|
|
|
if (Fn->pOrgScript->Strict >= C4AulScript::STRICT2)
|
|
|
|
|
{
|
|
|
|
|
Match(ATT_BOPEN);
|
|
|
|
|
Parse_Expression();
|
|
|
|
|
Match(ATT_BCLOSE);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
Parse_Params(1, C4AUL_If);
|
|
|
|
|
Match(ATT_BOPEN);
|
|
|
|
|
Parse_Expression();
|
|
|
|
|
Match(ATT_BCLOSE);
|
|
|
|
|
SetNoRef();
|
|
|
|
|
// create bytecode, remember position
|
|
|
|
|
int iCond = a->GetCodePos();
|
|
|
|
@ -2599,12 +2375,7 @@ void C4AulParseState::Parse_Expression(int iParentPrio)
|
|
|
|
|
throw new C4AulParseError(this, "'for' may not be used as a parameter");
|
|
|
|
|
else if (SEqual(Idtf, C4AUL_Return))
|
|
|
|
|
{
|
|
|
|
|
// return: treat as regular function with special byte code
|
|
|
|
|
Strict2Error("return used as a parameter", 0);
|
|
|
|
|
Shift();
|
|
|
|
|
Parse_Params(1, NULL);
|
|
|
|
|
AddBCC(AB_RETURN);
|
|
|
|
|
AddBCC(AB_STACK, +1);
|
|
|
|
|
Error("return may not be used as a parameter", 0);
|
|
|
|
|
}
|
|
|
|
|
else if (SEqual(Idtf, C4AUL_Par))
|
|
|
|
|
{
|
|
|
|
@ -2616,8 +2387,6 @@ void C4AulParseState::Parse_Expression(int iParentPrio)
|
|
|
|
|
else if (SEqual(Idtf, C4AUL_Inherited) || SEqual(Idtf, C4AUL_SafeInherited))
|
|
|
|
|
{
|
|
|
|
|
Shift();
|
|
|
|
|
// inherited keyword: check strict syntax
|
|
|
|
|
if (!Fn->pOrgScript->Strict) throw new C4AulParseError(this, "inherited disabled; use #strict syntax!");
|
|
|
|
|
// get function
|
|
|
|
|
if (Fn->OwnerOverloaded)
|
|
|
|
|
{
|
|
|
|
@ -2628,7 +2397,7 @@ void C4AulParseState::Parse_Expression(int iParentPrio)
|
|
|
|
|
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 call failsafe");
|
|
|
|
|
throw new C4AulParseError(this, "inherited function not found (use _inherited to disable this message)");
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// otherwise, parse parameters, but discard them
|
|
|
|
@ -2637,13 +2406,6 @@ void C4AulParseState::Parse_Expression(int iParentPrio)
|
|
|
|
|
AddBCC(AB_STACK, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (!Fn->pOrgScript->Strict && Fn->OwnerOverloaded && SEqual(Idtf, Fn->Name))
|
|
|
|
|
{
|
|
|
|
|
// old syntax: do not allow recursive calls in overloaded functions
|
|
|
|
|
Shift();
|
|
|
|
|
Parse_Params(Fn->OwnerOverloaded->GetParCount(), Fn->Name, Fn);
|
|
|
|
|
AddBCC(AB_FUNC, (long) Fn->OwnerOverloaded);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
C4AulFunc *FoundFn;
|
|
|
|
@ -2701,14 +2463,6 @@ void C4AulParseState::Parse_Expression(int iParentPrio)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Shift();
|
|
|
|
|
// now let's check whether it used old- or new-style
|
|
|
|
|
if (TokenType == ATT_BOPEN && Fn->pOrgScript->Strict < C4AulScript::STRICT2)
|
|
|
|
|
{
|
|
|
|
|
// old-style usage: ignore function call
|
|
|
|
|
// must not use parameters here (although generating the byte code for that would be possible)
|
|
|
|
|
Shift();
|
|
|
|
|
Match(ATT_BCLOSE, "parameters not allowed in functional usage of constants");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@ -2785,9 +2539,6 @@ void C4AulParseState::Parse_Expression(int iParentPrio)
|
|
|
|
|
}
|
|
|
|
|
case ATT_BOPEN2:
|
|
|
|
|
{
|
|
|
|
|
// Arrays are not tested in non-strict mode at all
|
|
|
|
|
if(!Fn->pOrgScript->Strict)
|
|
|
|
|
throw new C4AulParseError(this, "unexpected '['");
|
|
|
|
|
Parse_Array();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@ -2840,12 +2591,12 @@ void C4AulParseState::Parse_Expression2(int iParentPrio)
|
|
|
|
|
SetNoRef();
|
|
|
|
|
Shift();
|
|
|
|
|
|
|
|
|
|
if ((C4ScriptOpMap[OpID].Code == AB_And || C4ScriptOpMap[OpID].Code == AB_Or) && Fn->pOrgScript->Strict >= C4AulScript::STRICT2)
|
|
|
|
|
if (C4ScriptOpMap[OpID].Code == AB_JUMPAND || C4ScriptOpMap[OpID].Code == AB_JUMPOR)
|
|
|
|
|
{
|
|
|
|
|
// create bytecode, remember position
|
|
|
|
|
int iCond = a->GetCodePos();
|
|
|
|
|
// Jump or discard first parameter
|
|
|
|
|
AddBCC(C4ScriptOpMap[OpID].Code == AB_And ? AB_JUMPAND : AB_JUMPOR);
|
|
|
|
|
AddBCC(C4ScriptOpMap[OpID].Code);
|
|
|
|
|
// parse second expression
|
|
|
|
|
Parse_Expression(C4ScriptOpMap[OpID].Priority);
|
|
|
|
|
// set condition jump target
|
|
|
|
@ -2857,38 +2608,18 @@ void C4AulParseState::Parse_Expression2(int iParentPrio)
|
|
|
|
|
// expect second parameter for operator
|
|
|
|
|
if(!C4ScriptOpMap[OpID].NoSecondStatement)
|
|
|
|
|
{
|
|
|
|
|
switch (TokenType)
|
|
|
|
|
{
|
|
|
|
|
case ATT_IDTF: case ATT_INT: case ATT_BOOL: case ATT_STRING: case ATT_C4ID: case ATT_NIL:
|
|
|
|
|
case ATT_OPERATOR: case ATT_BOPEN: case ATT_BOPEN2:
|
|
|
|
|
Parse_Expression(C4ScriptOpMap[OpID].Priority);
|
|
|
|
|
// If the operator does not modify the second argument, no reference is necessary
|
|
|
|
|
if(C4ScriptOpMap[OpID].Type2 != C4V_pC4Value)
|
|
|
|
|
SetNoRef();
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
// Stuff like foo(42+,1) used to silently work
|
|
|
|
|
Strict2Error(FormatString("Operator %s: Second expression expected, but %s found",
|
|
|
|
|
C4ScriptOpMap[OpID].Identifier, GetTokenName(TokenType)).getData(), NULL);
|
|
|
|
|
AddBCC(AB_INT, 0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
Parse_Expression(C4ScriptOpMap[OpID].Priority);
|
|
|
|
|
// If the operator does not modify the second argument, no reference is necessary
|
|
|
|
|
if(C4ScriptOpMap[OpID].Type2 != C4V_pC4Value)
|
|
|
|
|
SetNoRef();
|
|
|
|
|
}
|
|
|
|
|
// write byte code, with a few backward compat changes
|
|
|
|
|
if (C4ScriptOpMap[OpID].Code == AB_Equal && Fn->pOrgScript->Strict < C4AulScript::STRICT2)
|
|
|
|
|
AddBCC(AB_EqualIdent, OpID);
|
|
|
|
|
else if (C4ScriptOpMap[OpID].Code == AB_NotEqual && Fn->pOrgScript->Strict < C4AulScript::STRICT2)
|
|
|
|
|
AddBCC(AB_NotEqualIdent, OpID);
|
|
|
|
|
else
|
|
|
|
|
AddBCC(C4ScriptOpMap[OpID].Code, OpID);
|
|
|
|
|
// write byte code
|
|
|
|
|
AddBCC(C4ScriptOpMap[OpID].Code, OpID);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case ATT_BOPEN2:
|
|
|
|
|
{
|
|
|
|
|
// Arrays are not tested in non-strict mode at all
|
|
|
|
|
if(!Fn->pOrgScript->Strict)
|
|
|
|
|
throw new C4AulParseError(this, "unexpected '['");
|
|
|
|
|
// Access the array
|
|
|
|
|
Shift();
|
|
|
|
|
Parse_Expression();
|
|
|
|
@ -3073,10 +2804,10 @@ void C4AulParseState::Parse_Static()
|
|
|
|
|
UnexpectedToken("variable name");
|
|
|
|
|
// global variable definition
|
|
|
|
|
// check: symbol already in use?
|
|
|
|
|
if(a->Engine->GetFuncRecursive(Idtf)) throw new C4AulParseError(this, "variable definition: name already in use");
|
|
|
|
|
if (a->Engine->GetGlobalConstant(Idtf, NULL)) Strict2Error("constant and variable with name ", Idtf);
|
|
|
|
|
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);
|
|
|
|
|
// insert variable if not defined already
|
|
|
|
|
if(a->Engine->GlobalNamedNames.GetItemNr(Idtf) == -1)
|
|
|
|
|
if (a->Engine->GlobalNamedNames.GetItemNr(Idtf) == -1)
|
|
|
|
|
a->Engine->GlobalNamedNames.AddName(Idtf);
|
|
|
|
|
}
|
|
|
|
|
Match(ATT_IDTF);
|
|
|
|
@ -3113,9 +2844,9 @@ void C4AulParseState::Parse_Const()
|
|
|
|
|
// (e.g., for overloading constants such as OCF_Living() in chaos scenarios)
|
|
|
|
|
// it is not encouraged though, so better warn
|
|
|
|
|
if(a->Engine->GetFuncRecursive(Idtf))
|
|
|
|
|
Strict2Error("definition of constant hidden by function ", Idtf);
|
|
|
|
|
Error("definition of constant hidden by function ", Idtf);
|
|
|
|
|
if(a->Engine->GlobalNamedNames.GetItemNr(Idtf) != -1)
|
|
|
|
|
Strict2Error("constant and variable with name ", Idtf);
|
|
|
|
|
Error("constant and variable with name ", Idtf);
|
|
|
|
|
Match(ATT_IDTF);
|
|
|
|
|
// expect '='
|
|
|
|
|
if (TokenType != ATT_OPERATOR || !SEqual(C4ScriptOpMap[cInt].Identifier,"="))
|
|
|
|
@ -3309,7 +3040,7 @@ void C4AulScript::ParseDescs()
|
|
|
|
|
C4AulScript *C4AulScript::FindFirstNonStrictScript()
|
|
|
|
|
{
|
|
|
|
|
// self is not #strict?
|
|
|
|
|
if (Script && !Strict) return this;
|
|
|
|
|
if (Script && Strict < MAXSTRICT) return this;
|
|
|
|
|
// search children
|
|
|
|
|
C4AulScript *pNonStrScr;
|
|
|
|
|
for (C4AulScript *pScr=Child0; pScr; pScr=pScr->Next)
|
|
|
|
|