diff --git a/docs/rh-log.txt b/docs/rh-log.txt index a3373ad5f..4ee417adf 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,13 @@ +October 18, 2008 (Changes by Graf Zahl) +- Added a global symbol table and changed DECORATE parser to put its global + symbols there instead of into AActor. +- Changed the expression evaluator's floating point precision to double. +- Started rewriting the DECORATE expression evaluator to allow more flexibility. + All the operators use the new functionality but functions, variables and + constants are yet to be redone. + While doing this rewrite I noticed that random2 was always evaluated as const. + This got fixed automatically. + October 13, 2008 - This may or may not be a problem, but GCC warned that FStateDefinitions:: AddStateDefines() does not initialize def.FStateDefine::DefineFlags, so diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d1bd7bd4c..ff346fd6a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -625,6 +625,7 @@ add_executable( zdoom WIN32 thingdef/thingdef.cpp thingdef/thingdef_codeptr.cpp thingdef/thingdef_exp.cpp + thingdef/thingdef_expression.cpp thingdef/thingdef_main.cpp thingdef/thingdef_parse.cpp thingdef/thingdef_properties.cpp diff --git a/src/namedef.h b/src/namedef.h index ab933d26e..1d9c8750f 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -254,6 +254,7 @@ xx(Z) xx(MomX) xx(MomY) xx(MomZ) +xx(Abs) // Various actor names which are used internally xx(MapSpot) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 961c0e078..52d34ee8f 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -3162,7 +3162,7 @@ struct FLineSpecial { const char *name; BYTE number; - BYTE min_args; + SBYTE min_args; SBYTE max_args; }; diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index 6a47456c8..0b2b3d9f3 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -183,7 +183,7 @@ int ParseExpression (FScanner &sc, bool _not, PClass *cls); bool IsExpressionConst(int id); int EvalExpressionI (int id, AActor *self, const PClass *cls=NULL); -float EvalExpressionF (int id, AActor *self, const PClass *cls=NULL); +double EvalExpressionF (int id, AActor *self, const PClass *cls=NULL); fixed_t EvalExpressionFix (int id, AActor *self, const PClass *cls=NULL); diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index 296dc93ba..680ac39f3 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -47,66 +47,17 @@ #include "a_pickups.h" #include "thingdef.h" #include "p_lnspec.h" - -void InitExpressions (); -void ClearExpressions (); +#include "doomstat.h" +#include "thingdef_exp.h" FRandom pr_exrandom ("EX_Random"); -enum ExpOp -{ - EX_NOP, +extern PSymbolTable GlobalSymbols; - EX_Const, - EX_Var, - EX_Compl, // ~exp - EX_Not, // !exp -// EX_Plus, // +exp - EX_Minus, // -exp - EX_Mul, // exp * exp - EX_Div, // exp / exp - EX_Mod, // exp % exp - EX_Add, // exp + exp - EX_Sub, // exp - exp - EX_LShift, // exp << exp - EX_RShift, // exp >> exp - EX_LT, // exp < exp - EX_GT, // exp > exp - EX_LE, // exp <= exp - EX_GE, // exp >= exp - EX_Eq, // exp == exp - EX_NE, // exp != exp - EX_And, // exp & exp - EX_Xor, // exp ^ exp - EX_Or, // exp | exp - EX_LogAnd, // exp && exp - EX_LogOr, // exp || exp - EX_Cond, // exp ? exp : exp - EX_Random, // random (min, max) - EX_Random2, // random2 ([mask]) - EX_Sin, // sin (angle) - EX_Cos, // cos (angle) - EX_ActionSpecial, - EX_Right, -}; -enum ExpValType -{ - VAL_Int, - VAL_Float, -}; -struct ExpVal -{ - ExpValType Type; - union - { - int Int; - float Float; - }; -}; typedef ExpVal (*ExpVarGet) (AActor *, int); @@ -122,7 +73,7 @@ ExpVal GetAngle (AActor *actor, int id) { ExpVal val; val.Type = VAL_Float; - val.Float = (float)actor->angle / ANGLE_1; + val.Float = (double)actor->angle / ANGLE_1; return val; } @@ -162,7 +113,7 @@ ExpVal GetPitch (AActor *actor, int id) { ExpVal val; val.Type = VAL_Float; - val.Float = (float)actor->pitch / ANGLE_1; + val.Float = (double)actor->pitch / ANGLE_1; return val; } @@ -251,448 +202,271 @@ static struct FExpVar ENamedName name; // identifier int array; // array size (0 if not an array) ExpVarGet get; + int ValueType; } ExpVars[] = { - { NAME_Alpha, 0, GetAlpha }, - { NAME_Angle, 0, GetAngle }, - { NAME_Args, 5, GetArgs }, - { NAME_CeilingZ, 0, GetCeilingZ }, - { NAME_FloorZ, 0, GetFloorZ }, - { NAME_Health, 0, GetHealth }, - { NAME_Pitch, 0, GetPitch }, - { NAME_Special, 0, GetSpecial }, - { NAME_TID, 0, GetTID }, - { NAME_TIDtoHate, 0, GetTIDToHate }, - { NAME_WaterLevel, 0, GetWaterLevel }, - { NAME_X, 0, GetX }, - { NAME_Y, 0, GetY }, - { NAME_Z, 0, GetZ }, - { NAME_MomX, 0, GetMomX }, - { NAME_MomY, 0, GetMomY }, - { NAME_MomZ, 0, GetMomZ }, + { NAME_Alpha, 0, GetAlpha, VAL_Float }, + { NAME_Angle, 0, GetAngle, VAL_Float }, + { NAME_Args, 5, GetArgs, VAL_Int }, + { NAME_CeilingZ, 0, GetCeilingZ, VAL_Float }, + { NAME_FloorZ, 0, GetFloorZ, VAL_Float }, + { NAME_Health, 0, GetHealth, VAL_Int }, + { NAME_Pitch, 0, GetPitch, VAL_Float }, + { NAME_Special, 0, GetSpecial, VAL_Int }, + { NAME_TID, 0, GetTID, VAL_Int }, + { NAME_TIDtoHate, 0, GetTIDToHate, VAL_Int }, + { NAME_WaterLevel, 0, GetWaterLevel, VAL_Int }, + { NAME_X, 0, GetX, VAL_Float }, + { NAME_Y, 0, GetY, VAL_Float }, + { NAME_Z, 0, GetZ, VAL_Float }, + { NAME_MomX, 0, GetMomX, VAL_Float }, + { NAME_MomY, 0, GetMomY, VAL_Float }, + { NAME_MomZ, 0, GetMomZ, VAL_Float }, }; -struct ExpData; -static ExpVal EvalExpression (ExpData *data, AActor *self, const PClass *cls); -struct ExpData -{ - ExpData () - { - Type = EX_NOP; - Value.Type = VAL_Int; - Value.Int = 0; - RNG = NULL; - for (int i = 0; i < 2; i++) - Children[i] = NULL; - } - ~ExpData () - { - for (int i = 0; i < 2; i++) - { - if (Children[i]) - { - delete Children[i]; - } - } - } - // Try to evaluate constant expression - void EvalConst (const PClass *cls) - { - if (Type == EX_NOP || Type == EX_Const || Type == EX_Var) - { - return; - } - else if (Type == EX_Compl || Type == EX_Not || /*Type == EX_Plus || */Type == EX_Minus) - { - if (Children[0]->Type == EX_Const) - { - Value = EvalExpression (this, NULL, cls); - Type = EX_Const; - } - } - else if (Type == EX_Cond) - { - if (Children[0]->Type == EX_Const) - { - bool cond = (Children[0]->Value.Type == VAL_Int) ? (Children[0]->Value.Int != 0) : (Children[0]->Value.Float != 0); - ExpData *data = Children[1]->Children[cond]; - delete Children[1]->Children[!cond]; - delete Children[1]; - delete Children[0]; - - Type = data->Type; - Value = data->Value; - for (int i = 0; i < 2; i++) - { - Children[i] = data->Children[i]; - data->Children[i] = NULL; - } - - delete data; - } - } - else if (Type != EX_Random && Type != EX_Sin && Type != EX_Cos && Type != EX_ActionSpecial) - { - if (Children[0]->Type == EX_Const && Children[1]->Type == EX_Const) - { - Value = EvalExpression (this, NULL, NULL); - Type = EX_Const; - delete Children[0]; Children[0] = NULL; - delete Children[1]; Children[1] = NULL; - } - } - } - bool Compare (ExpData *other) - { - if (!other) - return false; - - if (Type != other->Type || - Value.Type != other->Value.Type || - Value.Float != other->Value.Float || - RNG != other->RNG) - { - return false; - } - - for (int i = 0; i < 2; i++) - { - if (Children[i] && !Children[i]->Compare (other->Children[i])) - return false; - } - - return true; - } - - ExpOp Type; - ExpVal Value; - ExpData *Children[2]; - FRandom *RNG; // only used by random and random2 -}; - -TArray StateExpressions; +TDeletingArray StateExpressions; // // ParseExpression // [GRB] Parses an expression and stores it into Expression array // -static ExpData *ParseExpressionM (FScanner &sc, const PClass *cls); -static ExpData *ParseExpressionL (FScanner &sc, const PClass *cls); -static ExpData *ParseExpressionK (FScanner &sc, const PClass *cls); -static ExpData *ParseExpressionJ (FScanner &sc, const PClass *cls); -static ExpData *ParseExpressionI (FScanner &sc, const PClass *cls); -static ExpData *ParseExpressionH (FScanner &sc, const PClass *cls); -static ExpData *ParseExpressionG (FScanner &sc, const PClass *cls); -static ExpData *ParseExpressionF (FScanner &sc, const PClass *cls); -static ExpData *ParseExpressionE (FScanner &sc, const PClass *cls); -static ExpData *ParseExpressionD (FScanner &sc, const PClass *cls); -static ExpData *ParseExpressionC (FScanner &sc, const PClass *cls); -static ExpData *ParseExpressionB (FScanner &sc, const PClass *cls); -static ExpData *ParseExpressionA (FScanner &sc, const PClass *cls); +static FxExpression *ParseExpressionM (FScanner &sc, const PClass *cls); +static FxExpression *ParseExpressionL (FScanner &sc, const PClass *cls); +static FxExpression *ParseExpressionK (FScanner &sc, const PClass *cls); +static FxExpression *ParseExpressionJ (FScanner &sc, const PClass *cls); +static FxExpression *ParseExpressionI (FScanner &sc, const PClass *cls); +static FxExpression *ParseExpressionH (FScanner &sc, const PClass *cls); +static FxExpression *ParseExpressionG (FScanner &sc, const PClass *cls); +static FxExpression *ParseExpressionF (FScanner &sc, const PClass *cls); +static FxExpression *ParseExpressionE (FScanner &sc, const PClass *cls); +static FxExpression *ParseExpressionD (FScanner &sc, const PClass *cls); +static FxExpression *ParseExpressionC (FScanner &sc, const PClass *cls); +static FxExpression *ParseExpressionB (FScanner &sc, const PClass *cls); +static FxExpression *ParseExpressionA (FScanner &sc, const PClass *cls); -int ParseExpression (FScanner &sc, bool _not, PClass *cls) +FxExpression *ParseExpression (FScanner &sc, PClass *cls) { - static bool inited=false; - - if (!inited) - { - InitExpressions (); - atterm (ClearExpressions); - inited=true; - } + FxExpression *data = ParseExpressionM (sc, cls); - ExpData *data = ParseExpressionM (sc, cls); - - for (unsigned int i = 0; i < StateExpressions.Size (); i++) - { - if (StateExpressions[i]->Compare (data)) - { - delete data; - return i; - } - } - - return StateExpressions.Push (data); -} - -static ExpData *ParseExpressionM (FScanner &sc, const PClass *cls) -{ - ExpData *tmp = ParseExpressionL (sc, cls); - - if (sc.CheckToken('?')) - { - ExpData *data = new ExpData; - data->Type = EX_Cond; - data->Children[0] = tmp; - ExpData *choices = new ExpData; - data->Children[1] = choices; - choices->Type = EX_Right; - choices->Children[0] = ParseExpressionM (sc, cls); - sc.MustGetToken(':'); - choices->Children[1] = ParseExpressionM (sc, cls); - data->EvalConst (cls); - return data; - } - else - { - return tmp; - } -} - -static ExpData *ParseExpressionL (FScanner &sc, const PClass *cls) -{ - ExpData *tmp = ParseExpressionK (sc, cls); - - while (sc.CheckToken(TK_OrOr)) - { - ExpData *right = ParseExpressionK (sc, cls); - ExpData *data = new ExpData; - data->Type = EX_LogOr; - data->Children[0] = tmp; - data->Children[1] = right; - data->EvalConst (cls); - tmp = data; - } - return tmp; -} - -static ExpData *ParseExpressionK (FScanner &sc, const PClass *cls) -{ - ExpData *tmp = ParseExpressionJ (sc, cls); - - while (sc.CheckToken(TK_AndAnd)) - { - ExpData *right = ParseExpressionJ (sc, cls); - ExpData *data = new ExpData; - data->Type = EX_LogAnd; - data->Children[0] = tmp; - data->Children[1] = right; - data->EvalConst (cls); - tmp = data; - } - return tmp; -} - -static ExpData *ParseExpressionJ (FScanner &sc, const PClass *cls) -{ - ExpData *tmp = ParseExpressionI (sc, cls); - - while (sc.CheckToken('|')) - { - ExpData *right = ParseExpressionI (sc, cls); - ExpData *data = new ExpData; - data->Type = EX_Or; - data->Children[0] = tmp; - data->Children[1] = right; - data->EvalConst (cls); - tmp = data; - } - return tmp; -} - -static ExpData *ParseExpressionI (FScanner &sc, const PClass *cls) -{ - ExpData *tmp = ParseExpressionH (sc, cls); - - while (sc.CheckToken('^')) - { - ExpData *right = ParseExpressionH (sc, cls); - ExpData *data = new ExpData; - data->Type = EX_Xor; - data->Children[0] = tmp; - data->Children[1] = right; - data->EvalConst (cls); - tmp = data; - } - return tmp; -} - -static ExpData *ParseExpressionH (FScanner &sc, const PClass *cls) -{ - ExpData *tmp = ParseExpressionG (sc, cls); - - while (sc.CheckToken('&')) - { - ExpData *right = ParseExpressionG (sc, cls); - ExpData *data = new ExpData; - data->Type = EX_And; - data->Children[0] = tmp; - data->Children[1] = right; - data->EvalConst (cls); - tmp = data; - } - return tmp; -} - -static ExpData *ParseExpressionG (FScanner &sc, const PClass *cls) -{ - ExpData *tmp = ParseExpressionF (sc, cls); - - while (sc.GetToken() && (sc.TokenType == TK_Eq || sc.TokenType == TK_Neq)) - { - int token = sc.TokenType; - ExpData *right = ParseExpressionF (sc, cls); - ExpData *data = new ExpData; - data->Type = token == TK_Eq? EX_Eq : EX_NE; - data->Children[0] = tmp; - data->Children[1] = right; - data->EvalConst (cls); - tmp = data; - } - if (!sc.End) sc.UnGet(); - return tmp; -} - -static ExpData *ParseExpressionF (FScanner &sc, const PClass *cls) -{ - ExpData *tmp = ParseExpressionE (sc, cls); - - while (sc.GetToken() && (sc.TokenType == '<' || sc.TokenType == '>' || sc.TokenType == TK_Leq || sc.TokenType == TK_Geq)) - { - int token = sc.TokenType; - ExpData *right = ParseExpressionE (sc, cls); - ExpData *data = new ExpData; - data->Type = token == '<' ? EX_LT : token == '>' ? EX_GT : token == TK_Leq? EX_LE : EX_GE; - data->Children[0] = tmp; - data->Children[1] = right; - data->EvalConst (cls); - tmp = data; - } - if (!sc.End) sc.UnGet(); - return tmp; -} - -static ExpData *ParseExpressionE (FScanner &sc, const PClass *cls) -{ - ExpData *tmp = ParseExpressionD (sc, cls); - - while (sc.GetToken() && (sc.TokenType == TK_LShift || sc.TokenType == TK_RShift)) - { - int token = sc.TokenType; - ExpData *right = ParseExpressionD (sc, cls); - ExpData *data = new ExpData; - data->Type = token == TK_LShift? EX_LShift : EX_RShift; - data->Children[0] = tmp; - data->Children[1] = right; - data->EvalConst (cls); - tmp = data; - } - if (!sc.End) sc.UnGet(); - return tmp; -} - -static ExpData *ParseExpressionD (FScanner &sc, const PClass *cls) -{ - ExpData *tmp = ParseExpressionC (sc, cls); - - while (sc.GetToken() && (sc.TokenType == '+' || sc.TokenType == '-')) - { - int token = sc.TokenType; - ExpData *right = ParseExpressionC (sc, cls); - ExpData *data = new ExpData; - data->Type = token == '+'? EX_Add : EX_Sub; - data->Children[0] = tmp; - data->Children[1] = right; - data->EvalConst (cls); - tmp = data; - } - if (!sc.End) sc.UnGet(); - return tmp; -} - -static ExpData *ParseExpressionC (FScanner &sc, const PClass *cls) -{ - ExpData *tmp = ParseExpressionB (sc, cls); - - while (sc.GetToken() && (sc.TokenType == '*' || sc.TokenType == '/' || sc.TokenType == '%')) - { - int token = sc.TokenType; - ExpData *right = ParseExpressionB (sc, cls); - ExpData *data = new ExpData; - data->Type = token == '*'? EX_Mul : token == '/'? EX_Div : EX_Mod; - data->Children[0] = tmp; - data->Children[1] = right; - data->EvalConst (cls); - tmp = data; - } - if (!sc.End) sc.UnGet(); - return tmp; -} - -static ExpData *ParseExpressionB (FScanner &sc, const PClass *cls) -{ - ExpData *data = new ExpData; - - if (sc.CheckToken('~')) - { - data->Type = EX_Compl; - } - else if (sc.CheckToken('!')) - { - data->Type = EX_Not; - } - else if (sc.CheckToken('-')) - { - data->Type = EX_Minus; - } - else - { - sc.CheckToken('+'); - delete data; - return ParseExpressionA (sc, cls); - } - - data->Children[0] = ParseExpressionA (sc, cls); - data->EvalConst (cls); + FCompileContext ctx; + ctx.cls = cls; + data = data->Resolve(ctx); return data; } -static ExpData *ParseExpressionA (FScanner &sc, const PClass *cls) + +int ParseExpression (FScanner &sc, bool _not, PClass *cls) { + if (StateExpressions.Size()==0) + { + // StateExpressions[0] always is const 0; + FxExpression *data = new FxConstant(0, FScriptPosition()); + StateExpressions.Push (data); + } + + FxExpression *data = ParseExpression (sc, cls); + return StateExpressions.Push (data); +} + +static FxExpression *ParseExpressionM (FScanner &sc, const PClass *cls) +{ + FxExpression *condition = ParseExpressionL (sc, cls); + + if (sc.CheckToken('?')) + { + FxExpression *truex = ParseExpressionM (sc, cls); + sc.MustGetToken(':'); + FxExpression *falsex = ParseExpressionM (sc, cls); + return new FxConditional(condition, truex, falsex); + } + else + { + return condition; + } +} + +static FxExpression *ParseExpressionL (FScanner &sc, const PClass *cls) +{ + FxExpression *tmp = ParseExpressionK (sc, cls); + + while (sc.CheckToken(TK_OrOr)) + { + FxExpression *right = ParseExpressionK (sc, cls); + tmp = new FxBinaryLogical(TK_OrOr, tmp, right); + } + return tmp; +} + +static FxExpression *ParseExpressionK (FScanner &sc, const PClass *cls) +{ + FxExpression *tmp = ParseExpressionJ (sc, cls); + + while (sc.CheckToken(TK_AndAnd)) + { + FxExpression *right = ParseExpressionJ (sc, cls); + tmp = new FxBinaryLogical(TK_AndAnd, tmp, right); + } + return tmp; +} + +static FxExpression *ParseExpressionJ (FScanner &sc, const PClass *cls) +{ + FxExpression *tmp = ParseExpressionI (sc, cls); + + while (sc.CheckToken('|')) + { + FxExpression *right = ParseExpressionI (sc, cls); + tmp = new FxBinaryInt('|', tmp, right); + } + return tmp; +} + +static FxExpression *ParseExpressionI (FScanner &sc, const PClass *cls) +{ + FxExpression *tmp = ParseExpressionH (sc, cls); + + while (sc.CheckToken('^')) + { + FxExpression *right = ParseExpressionH (sc, cls); + tmp = new FxBinaryInt('^', tmp, right); + } + return tmp; +} + +static FxExpression *ParseExpressionH (FScanner &sc, const PClass *cls) +{ + FxExpression *tmp = ParseExpressionG (sc, cls); + + while (sc.CheckToken('&')) + { + FxExpression *right = ParseExpressionG (sc, cls); + tmp = new FxBinaryInt('&', tmp, right); + } + return tmp; +} + +static FxExpression *ParseExpressionG (FScanner &sc, const PClass *cls) +{ + FxExpression *tmp = ParseExpressionF (sc, cls); + + while (sc.GetToken() && (sc.TokenType == TK_Eq || sc.TokenType == TK_Neq)) + { + int token = sc.TokenType; + FxExpression *right = ParseExpressionF (sc, cls); + tmp = new FxCompareEq(token, tmp, right); + } + if (!sc.End) sc.UnGet(); + return tmp; +} + +static FxExpression *ParseExpressionF (FScanner &sc, const PClass *cls) +{ + FxExpression *tmp = ParseExpressionE (sc, cls); + + while (sc.GetToken() && (sc.TokenType == '<' || sc.TokenType == '>' || sc.TokenType == TK_Leq || sc.TokenType == TK_Geq)) + { + int token = sc.TokenType; + FxExpression *right = ParseExpressionE (sc, cls); + tmp = new FxCompareRel(token, tmp, right); + } + if (!sc.End) sc.UnGet(); + return tmp; +} + +static FxExpression *ParseExpressionE (FScanner &sc, const PClass *cls) +{ + FxExpression *tmp = ParseExpressionD (sc, cls); + + while (sc.GetToken() && (sc.TokenType == TK_LShift || sc.TokenType == TK_RShift || sc.TokenType == TK_URShift)) + { + int token = sc.TokenType; + FxExpression *right = ParseExpressionD (sc, cls); + tmp = new FxBinaryInt(token, tmp, right); + } + if (!sc.End) sc.UnGet(); + return tmp; +} + +static FxExpression *ParseExpressionD (FScanner &sc, const PClass *cls) +{ + FxExpression *tmp = ParseExpressionC (sc, cls); + + while (sc.GetToken() && (sc.TokenType == '+' || sc.TokenType == '-')) + { + int token = sc.TokenType; + FxExpression *right = ParseExpressionC (sc, cls); + tmp = new FxAddSub(token, tmp, right); + } + if (!sc.End) sc.UnGet(); + return tmp; +} + +static FxExpression *ParseExpressionC (FScanner &sc, const PClass *cls) +{ + FxExpression *tmp = ParseExpressionB (sc, cls); + + while (sc.GetToken() && (sc.TokenType == '*' || sc.TokenType == '/' || sc.TokenType == '%')) + { + int token = sc.TokenType; + FxExpression *right = ParseExpressionB (sc, cls); + tmp = new FxMulDiv(token, tmp, right); + } + if (!sc.End) sc.UnGet(); + return tmp; +} + +static FxExpression *ParseExpressionB (FScanner &sc, const PClass *cls) +{ + sc.GetToken(); + switch(sc.TokenType) + { + case '~': + return new FxUnaryNotBitwise(ParseExpressionA (sc, cls)); + + case '!': + return new FxUnaryNotBoolean(ParseExpressionA (sc, cls)); + + case '-': + return new FxMinusSign(ParseExpressionA (sc, cls)); + + case '+': + return new FxPlusSign(ParseExpressionA (sc, cls)); + + default: + sc.UnGet(); + return ParseExpressionA (sc, cls); + } +} + + +static FxExpression *ParseExpressionA (FScanner &sc, const PClass *cls) +{ + FScriptPosition scpos(sc); if (sc.CheckToken('(')) { - ExpData *data = ParseExpressionM (sc, cls); + FxExpression *data = ParseExpressionM (sc, cls); sc.MustGetToken(')'); return data; } else if (sc.CheckToken(TK_True)) { - ExpData *data = new ExpData; - data->Type = EX_Const; - data->Value.Type = VAL_Int; - data->Value.Int = 1; - - return data; + return new FxConstant(1, scpos); } else if (sc.CheckToken(TK_False)) { - ExpData *data = new ExpData; - data->Type = EX_Const; - data->Value.Type = VAL_Int; - data->Value.Int = 0; - - return data; + return new FxConstant(0, scpos); } else if (sc.CheckToken(TK_IntConst)) { - ExpData *data = new ExpData; - data->Type = EX_Const; - data->Value.Type = VAL_Int; - data->Value.Int = sc.Number; - - return data; + return new FxConstant(sc.Number, scpos); } else if (sc.CheckToken(TK_FloatConst)) { - ExpData *data = new ExpData; - data->Type = EX_Const; - data->Value.Type = VAL_Float; - data->Value.Float = sc.Float; - - return data; + return new FxConstant(sc.Float, scpos); } + /* else if (sc.CheckToken(TK_Class)) { // Accept class'SomeClassName'.SomeConstant @@ -707,7 +481,7 @@ static ExpData *ParseExpressionA (FScanner &sc, const PClass *cls) PSymbol *sym = cls->Symbols.FindSymbol (sc.String, true); if (sym != NULL && sym->SymbolType == SYM_Const) { - ExpData *data = new ExpData; + FxExpression *data = new FxExpression; data->Type = EX_Const; data->Value.Type = VAL_Int; data->Value.Int = static_cast(sym)->Value; @@ -719,9 +493,11 @@ static ExpData *ParseExpressionA (FScanner &sc, const PClass *cls) return NULL; } } + */ else if (sc.CheckToken(TK_Identifier)) { - switch (FName(sc.String)) + FName identifier = FName(sc.String); + switch (identifier) { case NAME_Random: { @@ -739,15 +515,12 @@ static ExpData *ParseExpressionA (FScanner &sc, const PClass *cls) } sc.MustGetToken('('); - ExpData *data = new ExpData; - data->Type = EX_Random; - data->RNG = rng; - - data->Children[0] = ParseExpressionM (sc, cls); + FxExpression *min = ParseExpressionM (sc, cls); sc.MustGetToken(','); - data->Children[1] = ParseExpressionM (sc, cls); + FxExpression *max = ParseExpressionM (sc, cls); sc.MustGetToken(')'); - return data; + + return new FxRandom(rng, min, max, sc); } break; @@ -768,25 +541,32 @@ static ExpData *ParseExpressionA (FScanner &sc, const PClass *cls) sc.MustGetToken('('); - ExpData *data = new ExpData; - data->Type = EX_Random2; - data->RNG = rng; + FxExpression *mask = NULL; if (!sc.CheckToken(')')) { - data->Children[0] = ParseExpressionM(sc, cls); + mask = ParseExpressionM(sc, cls); sc.MustGetToken(')'); } - return data; + return new FxRandom2(rng, mask, sc); } break; + case NAME_Abs: + { + sc.MustGetToken('('); + FxExpression *x = ParseExpressionM (sc, cls); + sc.MustGetToken(')'); + return new FxAbs(x); + } + case NAME_Sin: { sc.MustGetToken('('); - ExpData *data = new ExpData; + FxExpression *data = new FxExpression; data->Type = EX_Sin; + data->ValueType = VAL_Float; data->Children[0] = ParseExpressionM (sc, cls); @@ -799,8 +579,9 @@ static ExpData *ParseExpressionA (FScanner &sc, const PClass *cls) { sc.MustGetToken('('); - ExpData *data = new ExpData; + FxExpression *data = new FxExpression; data->Type = EX_Cos; + data->ValueType = VAL_Float; data->Children[0] = ParseExpressionM (sc, cls); @@ -821,16 +602,17 @@ static ExpData *ParseExpressionA (FScanner &sc, const PClass *cls) sc.MustGetToken('('); - ExpData *data = new ExpData, **left; + FxExpression *data = new FxExpression, **left; data->Type = EX_ActionSpecial; data->Value.Int = specnum; + data->ValueType = VAL_Int; data->Children[0] = ParseExpressionM (sc, cls); left = &data->Children[1]; for (i = 1; i < 5 && sc.CheckToken(','); ++i) { - ExpData *right = new ExpData; + FxExpression *right = new FxExpression; right->Type = EX_Right; right->Children[0] = ParseExpressionM (sc, cls); *left = right; @@ -849,14 +631,11 @@ static ExpData *ParseExpressionA (FScanner &sc, const PClass *cls) // Check if this is a constant if (cls != NULL) { - PSymbol *sym = cls->Symbols.FindSymbol (sc.String, true); + PSymbol *sym = cls->Symbols.FindSymbol (identifier, true); + if (sym == NULL) sym = GlobalSymbols.FindSymbol (identifier, true); if (sym != NULL && sym->SymbolType == SYM_Const) { - ExpData *data = new ExpData; - data->Type = EX_Const; - data->Value.Type = VAL_Int; - data->Value.Int = static_cast(sym)->Value; - return data; + return new FxConstant(static_cast(sym)->Value, sc); } } @@ -875,10 +654,11 @@ static ExpData *ParseExpressionA (FScanner &sc, const PClass *cls) if (varid == -1) sc.ScriptError ("Unknown value '%s'", sc.String); - ExpData *data = new ExpData; + FxExpression *data = new FxExpression; data->Type = EX_Var; data->Value.Type = VAL_Int; data->Value.Int = varid; + data->ValueType = ExpVars[varid].ValueType; if (ExpVars[varid].array) { @@ -908,7 +688,7 @@ bool IsExpressionConst(int id) { if (StateExpressions.Size() <= (unsigned int)id) return false; - return StateExpressions[id]->Type == EX_Const; + return StateExpressions[id]->isConstant(); } int EvalExpressionI (int id, AActor *self, const PClass *cls) @@ -920,7 +700,7 @@ int EvalExpressionI (int id, AActor *self, const PClass *cls) cls = self->GetClass(); } - ExpVal val = EvalExpression (StateExpressions[id], self, cls); + ExpVal val = StateExpressions[id]->EvalExpression (self, cls); switch (val.Type) { @@ -932,7 +712,7 @@ int EvalExpressionI (int id, AActor *self, const PClass *cls) } } -float EvalExpressionF (int id, AActor *self, const PClass *cls) +double EvalExpressionF (int id, AActor *self, const PClass *cls) { if (StateExpressions.Size() <= (unsigned int)id) return 0.f; @@ -941,13 +721,13 @@ float EvalExpressionF (int id, AActor *self, const PClass *cls) cls = self->GetClass(); } - ExpVal val = EvalExpression (StateExpressions[id], self, cls); + ExpVal val = StateExpressions[id]->EvalExpression (self, cls); switch (val.Type) { default: case VAL_Int: - return (float)val.Int; + return (double)val.Int; case VAL_Float: return val.Float; } @@ -962,7 +742,7 @@ fixed_t EvalExpressionFix (int id, AActor *self, const PClass *cls) cls = self->GetClass(); } - ExpVal val = EvalExpression (StateExpressions[id], self, cls); + ExpVal val = StateExpressions[id]->EvalExpression (self, cls); switch (val.Type) { @@ -974,20 +754,17 @@ fixed_t EvalExpressionFix (int id, AActor *self, const PClass *cls) } } -static ExpVal EvalExpression (ExpData *data, AActor *self, const PClass *cls) +ExpVal FxExpression::EvalExpression (AActor *self, const PClass *cls) { ExpVal val; val.Type = VAL_Int; // Placate GCC - switch (data->Type) + switch (Type) { case EX_NOP: - assert (data->Type != EX_NOP); - val = data->Value; - break; - case EX_Const: - val = data->Value; + assert (Type != EX_NOP); + val = Value; break; case EX_Var: if (!self) @@ -997,539 +774,18 @@ static ExpVal EvalExpression (ExpData *data, AActor *self, const PClass *cls) else { int id = 0; - if (ExpVars[data->Value.Int].array) + if (ExpVars[Value.Int].array) { - ExpVal idval = EvalExpression (data->Children[0], self, cls); - id = ((idval.Type == VAL_Int) ? idval.Int : (int)idval.Float) % ExpVars[data->Value.Int].array; + ExpVal idval = Children[0]->EvalExpression (self, cls); + id = ((idval.Type == VAL_Int) ? idval.Int : (int)idval.Float) % ExpVars[Value.Int].array; } - val = ExpVars[data->Value.Int].get (self, id); + val = ExpVars[Value.Int].get (self, id); } break; - case EX_Compl: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - - val.Type = VAL_Int; - val.Int = ~((a.Type == VAL_Int) ? a.Int : (int)a.Float); - } - break; - case EX_Not: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - - val.Type = VAL_Int; - val.Int = !((a.Type == VAL_Int) ? a.Int : (int)a.Float); - } - break; - case EX_Minus: - { - val = EvalExpression (data->Children[0], self, cls); - - if (val.Type == VAL_Int) - val.Int = -val.Int; - else - val.Float = -val.Float; - } - break; - case EX_Mul: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - { - val.Type = VAL_Int; - val.Int = a.Int * b.Int; - } - else - { - val.Type = VAL_Float; - val.Float = a.Int * b.Float; - } - } - else - { - val.Type = VAL_Float; - if (b.Type == VAL_Int) - val.Float = a.Float * b.Int; - else - val.Float = a.Float * b.Float; - } - } - break; - case EX_Div: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - if (b.Int == 0) - { - I_FatalError ("Division by zero"); - } - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - { - val.Type = VAL_Int; - val.Int = a.Int / b.Int; - } - else - { - val.Type = VAL_Float; - val.Float = a.Int / b.Float; - } - } - else - { - val.Type = VAL_Float; - if (b.Type == VAL_Int) - val.Float = a.Float / b.Int; - else - val.Float = a.Float / b.Float; - } - } - break; - case EX_Mod: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - if (b.Int == 0) - { - I_FatalError ("Division by zero"); - } - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - { - val.Type = VAL_Int; - val.Int = a.Int % b.Int; - } - else - { - val.Type = VAL_Float; - val.Float = fmodf (a.Int, b.Float); - } - } - else - { - val.Type = VAL_Float; - if (b.Type == VAL_Int) - val.Float = fmodf (a.Float, b.Int); - else - val.Float = fmodf (a.Float, b.Float); - } - } - break; - case EX_Add: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - { - val.Type = VAL_Int; - val.Int = a.Int + b.Int; - } - else - { - val.Type = VAL_Float; - val.Float = a.Int + b.Float; - } - } - else - { - val.Type = VAL_Float; - if (b.Type == VAL_Int) - val.Float = a.Float + b.Int; - else - val.Float = a.Float + b.Float; - } - } - break; - case EX_Sub: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - { - val.Type = VAL_Int; - val.Int = a.Int - b.Int; - } - else - { - val.Type = VAL_Float; - val.Float = a.Int - b.Float; - } - } - else - { - val.Type = VAL_Float; - if (b.Type == VAL_Int) - val.Float = a.Float - b.Int; - else - val.Float = a.Float - b.Float; - } - } - break; - case EX_LShift: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - val.Type = VAL_Int; - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - val.Int = a.Int << b.Int; - else - val.Int = a.Int << (int)b.Float; - } - else - { - if (b.Type == VAL_Int) - val.Int = (int)a.Float << b.Int; - else - val.Int = (int)a.Float << (int)b.Float; - } - } - break; - case EX_RShift: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - val.Type = VAL_Int; - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - val.Int = a.Int >> b.Int; - else - val.Int = a.Int >> (int)b.Float; - } - else - { - if (b.Type == VAL_Int) - val.Int = (int)a.Float >> b.Int; - else - val.Int = (int)a.Float >> (int)b.Float; - } - } - break; - case EX_LT: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - val.Type = VAL_Int; - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - val.Int = a.Int < b.Int; - else - val.Int = a.Int < b.Float; - } - else - { - if (b.Type == VAL_Int) - val.Int = a.Float < b.Int; - else - val.Int = a.Float < b.Float; - } - } - break; - case EX_GT: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - val.Type = VAL_Int; - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - val.Int = a.Int > b.Int; - else - val.Int = a.Int > b.Float; - } - else - { - if (b.Type == VAL_Int) - val.Int = a.Float > b.Int; - else - val.Int = a.Float > b.Float; - } - } - break; - case EX_LE: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - val.Type = VAL_Int; - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - val.Int = a.Int <= b.Int; - else - val.Int = a.Int <= b.Float; - } - else - { - if (b.Type == VAL_Int) - val.Int = a.Float <= b.Int; - else - val.Int = a.Float <= b.Float; - } - } - break; - case EX_GE: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - val.Type = VAL_Int; - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - val.Int = a.Int >= b.Int; - else - val.Int = a.Int >= b.Float; - } - else - { - if (b.Type == VAL_Int) - val.Int = a.Float >= b.Int; - else - val.Int = a.Float >= b.Float; - } - } - break; - case EX_Eq: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - val.Type = VAL_Int; - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - val.Int = a.Int == b.Int; - else - val.Int = a.Int == b.Float; - } - else - { - if (b.Type == VAL_Int) - val.Int = a.Float == b.Int; - else - val.Int = a.Float == b.Float; - } - } - break; - case EX_NE: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - val.Type = VAL_Int; - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - val.Int = a.Int != b.Int; - else - val.Int = a.Int != b.Float; - } - else - { - if (b.Type == VAL_Int) - val.Int = a.Float != b.Int; - else - val.Int = a.Float != b.Float; - } - } - break; - case EX_And: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - val.Type = VAL_Int; - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - val.Int = a.Int & b.Int; - else - val.Int = a.Int & (int)b.Float; - } - else - { - if (b.Type == VAL_Int) - val.Int = (int)a.Float & b.Int; - else - val.Int = (int)a.Float & (int)b.Float; - } - } - break; - case EX_Xor: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - val.Type = VAL_Int; - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - val.Int = a.Int ^ b.Int; - else - val.Int = a.Int ^ (int)b.Float; - } - else - { - if (b.Type == VAL_Int) - val.Int = (int)a.Float ^ b.Int; - else - val.Int = (int)a.Float ^ (int)b.Float; - } - } - break; - case EX_Or: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - val.Type = VAL_Int; - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - val.Int = a.Int | b.Int; - else - val.Int = a.Int | (int)b.Float; - } - else - { - if (b.Type == VAL_Int) - val.Int = (int)a.Float | b.Int; - else - val.Int = (int)a.Float | (int)b.Float; - } - } - break; - case EX_LogAnd: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - val.Type = VAL_Int; - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - val.Int = a.Int && b.Int; - else - val.Int = a.Int && b.Float; - } - else - { - if (b.Type == VAL_Int) - val.Int = a.Float && b.Int; - else - val.Int = a.Float && b.Float; - } - } - break; - case EX_LogOr: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - val.Type = VAL_Int; - - if (a.Type == VAL_Int) - { - if (b.Type == VAL_Int) - val.Int = a.Int || b.Int; - else - val.Int = a.Int || b.Float; - } - else - { - if (b.Type == VAL_Int) - val.Int = a.Float || b.Int; - else - val.Int = a.Float || b.Float; - } - } - break; - case EX_Cond: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - - if (a.Type == VAL_Float) - a.Int = (int)a.Float; - - val = EvalExpression (data->Children[1]->Children[!!a.Int], self, cls); - } - break; - - case EX_Random: - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - ExpVal b = EvalExpression (data->Children[1], self, cls); - - int min = (a.Type == VAL_Int) ? a.Int : (int)a.Float; - int max = (b.Type == VAL_Int) ? b.Int : (int)b.Float; - - val.Type = VAL_Int; - - if (max < min) - { - swap (max, min); - } - - val.Int = (*data->RNG)(max - min + 1) + min; - } - break; - - case EX_Random2: - { - if (data->Children[0] == NULL) - { - val.Type = VAL_Int; - val.Int = data->RNG->Random2(); - } - else - { - ExpVal a = EvalExpression (data->Children[0], self, cls); - val.Type = VAL_Int; - val.Int = data->RNG->Random2((a.Type == VAL_Int) ? a.Int : (int)a.Float); - } - } - break; - case EX_Sin: { - ExpVal a = EvalExpression (data->Children[0], self, cls); + ExpVal a = Children[0]->EvalExpression (self, cls); angle_t angle = (a.Type == VAL_Int) ? (a.Int * ANGLE_1) : angle_t(a.Float * ANGLE_1); val.Type = VAL_Float; @@ -1539,7 +795,7 @@ static ExpVal EvalExpression (ExpData *data, AActor *self, const PClass *cls) case EX_Cos: { - ExpVal a = EvalExpression (data->Children[0], self, cls); + ExpVal a = Children[0]->EvalExpression (self, cls); angle_t angle = (a.Type == VAL_Int) ? (a.Int * ANGLE_1) : angle_t(a.Float * ANGLE_1); val.Type = VAL_Float; @@ -1551,11 +807,11 @@ static ExpVal EvalExpression (ExpData *data, AActor *self, const PClass *cls) { int parms[5] = { 0, 0, 0, 0 }; int i = 0; - ExpData *parm = data; + FxExpression *parm = this; while (parm != NULL && i < 5) { - ExpVal val = EvalExpression (parm->Children[0], self, cls); + ExpVal val = parm->Children[0]->EvalExpression (self, cls); if (val.Type == VAL_Int) { parms[i] = val.Int; @@ -1569,14 +825,14 @@ static ExpVal EvalExpression (ExpData *data, AActor *self, const PClass *cls) } val.Type = VAL_Int; - val.Int = LineSpecials[data->Value.Int] (NULL, self, false, + val.Int = LineSpecials[Value.Int] (NULL, self, false, parms[0], parms[1], parms[2], parms[3], parms[4]); } break; case EX_Right: // This should never be a top-level expression. - assert (data->Type != EX_Right); + assert (Type != EX_Right); break; } @@ -1584,32 +840,66 @@ static ExpVal EvalExpression (ExpData *data, AActor *self, const PClass *cls) } -// -// InitExpressions -// [GRB] Set up expression data -// - -void InitExpressions () +bool FxExpression::isConstant() const { - // StateExpressions[0] always is const 0; - ExpData *data = new ExpData; - data->Type = EX_Const; - data->Value.Type = VAL_Int; - data->Value.Int = 0; + return false; +} - StateExpressions.Push (data); +FxExpression *FxExpression::Resolve(FCompileContext &ctx) +{ + if (Children[0]) Children[0] = Children[0]->Resolve(ctx); + if (Children[1]) Children[1] = Children[1]->Resolve(ctx); + return this; } -// -// ClearExpressions -// [GRB] Free all expression data -// -void ClearExpressions () +/* +some stuff for later +static FxExpression *ParseExpressionA (FScanner &sc, const PClass *cls) { - ExpData *data; + else if (sc.CheckToken(TK_Identifier)) + { + FName IdName = FName(sc.String); + switch (IdName) + { + default: + { + FScriptPosition scriptpos(sc); + if (sc.CheckToken('(')) + { + // function call + TArray arguments; - while (StateExpressions.Pop (data)) - delete data; + do + { + FxExpression *data = ParseExpressionM(sc, cls); + arguments.Push(data); + } + while (sc.CheckToken(',')); + return new FxFunctionCall(arguments, scriptpos); + } + else + { + FxExpression *data = new FxIdentifier(IdName, scriptpos); + if (sc.CheckToken('[')) + { + FxExpression *index = ParseExpressionM(sc, cls); + sc.MustGetToken(']'); + data = new FxArrayElement(data, index); + } + return data; + } + } + break; + } + } + else + { + FString tokname = sc.TokenName(sc.TokenType, sc.String); + sc.ScriptError ("Unexpected token %s", tokname.GetChars()); + return NULL; + } } + +*/ \ No newline at end of file diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h new file mode 100644 index 000000000..292cde670 --- /dev/null +++ b/src/thingdef/thingdef_exp.h @@ -0,0 +1,584 @@ +#ifndef THINGDEF_EXP_H +#define THINGDEF_EXP_H + +/* +** thingdef_exp.h +** +** Expression evaluation +** +**--------------------------------------------------------------------------- +** Copyright 2008 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be +** covered by the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or (at +** your option) any later version. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "m_random.h" + +#define CHECKRESOLVED() if (isresolved) return this; isresolved=true; +#define SAFE_DELETE(p) if (p!=NULL) { delete p; p=NULL; } +#define RESOLVE(p,c) if (p!=NULL) p = p->Resolve(c) +#define ABORT(p) if (!(p)) { delete this; return NULL; } +#define SAFE_RESOLVE(p,c) RESOLVE(p,c); ABORT(p) + + +//========================================================================== +// +// +// +//========================================================================== +/* +class FxType +{ + EBaseType type; +public: + FxType(EBaseType t) { type = t; } + FxType(const FxType & t) { type = t.type; } + FxType &operator =(const FxType &t) { type = t.type; return *this; } + + EBaseType GetBaseType() const { return type; } +}; +*/ + +//========================================================================== +// +// +// +//========================================================================== + +enum +{ + MSG_WARNING, + MSG_ERROR, + MSG_DEBUG, + MSG_LOG, + MSG_DEBUGLOG +}; + +//========================================================================== +// +// +// +//========================================================================== + +struct FScriptPosition +{ + FString FileName; + int ScriptLine; + + FScriptPosition() + { + ScriptLine=0; + } + FScriptPosition(const FScriptPosition &other) + { + FileName = other.FileName; + ScriptLine = other.ScriptLine; + } + FScriptPosition(FString fname, int line) + { + FileName = fname; + ScriptLine = line; + } + FScriptPosition(FScanner &sc) + { + FileName = sc.ScriptName; + ScriptLine = sc.GetMessageLine(); + } + FScriptPosition &operator=(const FScriptPosition &other) + { + FileName = other.FileName; + ScriptLine = other.ScriptLine; + return *this; + } + + void Message(int severity, const char *message,...); +}; + +//========================================================================== +// +// +// +//========================================================================== + +struct FCompileContext +{ + const PClass *cls; +}; + +//========================================================================== +// +// +// +//========================================================================== + +enum ExpOp +{ + EX_NOP, + + EX_Var, + + EX_Sin, // sin (angle) + EX_Cos, // cos (angle) + EX_ActionSpecial, + EX_Right, +}; + +//========================================================================== +// +// +// +//========================================================================== + +enum ExpValType +{ + VAL_Int, + VAL_Float, + VAL_Unknown, + + // only used for accessing class member fields to ensure proper conversion + VAL_Fixed, + VAL_Angle, +}; + +//========================================================================== +// +// +// +//========================================================================== + +struct ExpVal +{ + ExpValType Type; + union + { + int Int; + double Float; + }; + + int GetInt() + { + return Type == VAL_Int? Int : Type == VAL_Float? int(Float) : 0; + } + + double GetFloat() + { + return Type == VAL_Int? double(Int) : Type == VAL_Float? Float : 0; + } + + bool GetBool() + { + return Type == VAL_Int? !!Int : Type == VAL_Float? Float!=0. : false; + } + +}; + + +//========================================================================== +// +// +// +//========================================================================== + +struct FxExpression +{ + FxExpression () + { + isresolved = false; + ValueType = VAL_Unknown; + Type = EX_NOP; + Value.Type = VAL_Int; + Value.Int = 0; + for (int i = 0; i < 2; i++) + Children[i] = NULL; + } + virtual ~FxExpression () + { + for (int i = 0; i < 2; i++) + { + if (Children[i]) + { + delete Children[i]; + } + } + } + +protected: + FxExpression(const FScriptPosition &pos) + { + isresolved = false; + ScriptPosition = pos; + for (int i = 0; i < 2; i++) + Children[i] = NULL; + } +public: + virtual FxExpression *Resolve(FCompileContext &ctx); + FxExpression *ResolveAsBoolean(FCompileContext &ctx) + { + // This will need more handling if other types than Int and Float are added + return Resolve(ctx); + } + + virtual ExpVal EvalExpression (AActor *self, const PClass *cls); + virtual bool isConstant() const; + + int Type; + ExpVal Value; + FxExpression *Children[2]; + + FScriptPosition ScriptPosition; + int ValueType; +protected: + bool isresolved; +}; + +//========================================================================== +// +// ZxConstant +// +//========================================================================== + +class FxConstant : public FxExpression +{ + ExpVal value; + +public: + FxConstant(int val, const FScriptPosition &pos) : FxExpression(pos) + { + ValueType = value.Type = VAL_Int; + value.Int = val; + } + + FxConstant(double val, const FScriptPosition &pos) : FxExpression(pos) + { + ValueType = value.Type = VAL_Float; + value.Float = val; + } + + FxConstant(ExpVal cv, const FScriptPosition &pos) : FxExpression(pos) + { + value = cv; + ValueType = cv.Type; + } + + bool isConstant() const + { + return true; + } + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + + +//========================================================================== +// +// +// +//========================================================================== + +class FxIntCast : public FxExpression +{ + FxExpression *basex; + +public: + + FxIntCast(FxExpression *x); + ~FxIntCast(); + FxExpression *Resolve(FCompileContext&); + + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + + +//========================================================================== +// +// FxSign +// +//========================================================================== + +class FxPlusSign : public FxExpression +{ + FxExpression *Operand; + +public: + FxPlusSign(FxExpression*); + ~FxPlusSign(); + FxExpression *Resolve(FCompileContext&); +}; + +//========================================================================== +// +// FxSign +// +//========================================================================== + +class FxMinusSign : public FxExpression +{ + FxExpression *Operand; + +public: + FxMinusSign(FxExpression*); + ~FxMinusSign(); + FxExpression *Resolve(FCompileContext&); + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + +//========================================================================== +// +// FxUnaryNot +// +//========================================================================== + +class FxUnaryNotBitwise : public FxExpression +{ + FxExpression *Operand; + +public: + FxUnaryNotBitwise(FxExpression*); + ~FxUnaryNotBitwise(); + FxExpression *Resolve(FCompileContext&); + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + +//========================================================================== +// +// FxUnaryNot +// +//========================================================================== + +class FxUnaryNotBoolean : public FxExpression +{ + FxExpression *Operand; + +public: + FxUnaryNotBoolean(FxExpression*); + ~FxUnaryNotBoolean(); + FxExpression *Resolve(FCompileContext&); + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + +//========================================================================== +// +// FxBinary +// +//========================================================================== + +class FxBinary : public FxExpression +{ +public: + int Operator; + FxExpression *left; + FxExpression *right; + + FxBinary(int, FxExpression*, FxExpression*); + ~FxBinary(); + bool ResolveLR(FCompileContext& ctx, bool castnumeric); +}; + +//========================================================================== +// +// FxBinary +// +//========================================================================== + +class FxAddSub : public FxBinary +{ +public: + + FxAddSub(int, FxExpression*, FxExpression*); + FxExpression *Resolve(FCompileContext&); + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + +//========================================================================== +// +// FxBinary +// +//========================================================================== + +class FxMulDiv : public FxBinary +{ +public: + + FxMulDiv(int, FxExpression*, FxExpression*); + FxExpression *Resolve(FCompileContext&); + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + +//========================================================================== +// +// FxBinary +// +//========================================================================== + +class FxCompareRel : public FxBinary +{ +public: + + FxCompareRel(int, FxExpression*, FxExpression*); + FxExpression *Resolve(FCompileContext&); + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + +//========================================================================== +// +// FxBinary +// +//========================================================================== + +class FxCompareEq : public FxBinary +{ +public: + + FxCompareEq(int, FxExpression*, FxExpression*); + FxExpression *Resolve(FCompileContext&); + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + +//========================================================================== +// +// FxBinary +// +//========================================================================== + +class FxBinaryInt : public FxBinary +{ +public: + + FxBinaryInt(int, FxExpression*, FxExpression*); + FxExpression *Resolve(FCompileContext&); + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + +//========================================================================== +// +// FxBinaryLogical +// +//========================================================================== + +class FxBinaryLogical : public FxExpression +{ +public: + int Operator; + FxExpression *left; + FxExpression *right; + + FxBinaryLogical(int, FxExpression*, FxExpression*); + ~FxBinaryLogical(); + FxExpression *Resolve(FCompileContext&); + + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + +//========================================================================== +// +// FxConditional +// +//========================================================================== + +class FxConditional : public FxExpression +{ +public: + FxExpression *condition; + FxExpression *truex; + FxExpression *falsex; + + FxConditional(FxExpression*, FxExpression*, FxExpression*); + ~FxConditional(); + FxExpression *Resolve(FCompileContext&); + + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + +//========================================================================== +// +// +// +//========================================================================== + +class FxAbs : public FxExpression +{ + FxExpression *val; + +public: + + FxAbs(FxExpression *v); + ~FxAbs(); + FxExpression *Resolve(FCompileContext&); + + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + +//========================================================================== +// +// +// +//========================================================================== + +class FxRandom : public FxExpression +{ + FRandom * rng; + FxExpression *min, *max; + +public: + + FxRandom(FRandom *, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos); + ~FxRandom(); + FxExpression *Resolve(FCompileContext&); + + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + + + +//========================================================================== +// +// +// +//========================================================================== + +class FxRandom2 : public FxExpression +{ + FRandom * rng; + FxExpression *mask; + +public: + + FxRandom2(FRandom *, FxExpression *m, const FScriptPosition &pos); + ~FxRandom2(); + FxExpression *Resolve(FCompileContext&); + + ExpVal EvalExpression (AActor *self, const PClass *cls); +}; + + +FxExpression *ParseExpression (FScanner &sc, PClass *cls); + + +#endif \ No newline at end of file diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp new file mode 100644 index 000000000..b5b9663cf --- /dev/null +++ b/src/thingdef/thingdef_expression.cpp @@ -0,0 +1,1377 @@ +/* +** thingdef_expression.cpp +** +** Expression evaluation +** +**--------------------------------------------------------------------------- +** Copyright 2008 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be +** covered by the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or (at +** your option) any later version. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "actor.h" +#include "sc_man.h" +#include "tarray.h" +#include "templates.h" +#include "cmdlib.h" +#include "i_system.h" +#include "m_random.h" +#include "a_pickups.h" +#include "thingdef.h" +#include "p_lnspec.h" +#include "doomstat.h" +#include "thingdef_exp.h" + + +//========================================================================== +// +// FScriptPosition::Message +// +//========================================================================== + +void STACK_ARGS FScriptPosition::Message (int severity, const char *message, ...) +{ + FString composed; + + if (message == NULL) + { + composed = "Bad syntax."; + } + else + { + va_list arglist; + va_start (arglist, message); + composed.VFormat (message, arglist); + va_end (arglist); + } + const char *type = ""; + int level = PRINT_HIGH; + + switch (severity) + { + default: + return; + + case MSG_WARNING: + type = "warning"; + break; + + case MSG_ERROR: + type = "error"; + break; + + case MSG_DEBUG: + if (!developer) return; + type = "message"; + break; + + case MSG_LOG: + type = "message"; + level = PRINT_LOG; + break; + + case MSG_DEBUGLOG: + if (!developer) return; + type = "message"; + level = PRINT_LOG; + break; + } + + Printf (level, "Script %s, \"%s\" line %d:\n%s\n", type, + FileName.GetChars(), ScriptLine, composed.GetChars()); +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxConstant::EvalExpression (AActor *self, const PClass *cls) +{ + return value; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxIntCast::FxIntCast(FxExpression *x) +: FxExpression(x->ScriptPosition) +{ + basex=x; + ValueType = VAL_Int; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxIntCast::~FxIntCast() +{ + SAFE_DELETE(basex); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxIntCast::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(basex, ctx); + + if (basex->isConstant()) + { + ExpVal constval = basex->EvalExpression(NULL, ctx.cls); + FxExpression *x = new FxConstant(constval.GetInt(), ScriptPosition); + delete this; + return x; + } + else if (basex->ValueType == VAL_Int) + { + FxExpression *x = basex; + basex = NULL; + delete this; + return x; + } + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxIntCast::EvalExpression (AActor *self, const PClass *cls) +{ + ExpVal baseval = basex->EvalExpression(self, cls); + baseval.Int = baseval.GetInt(); + baseval.Type = VAL_Int; + return baseval; +} + + +//========================================================================== +// +// +// +//========================================================================== + +FxPlusSign::FxPlusSign(FxExpression *operand) +: FxExpression(operand->ScriptPosition) +{ + Operand=operand; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxPlusSign::~FxPlusSign() +{ + SAFE_DELETE(Operand); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxPlusSign::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(Operand, ctx); + + FxExpression *e = Operand; + Operand = NULL; + delete this; + return e; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxMinusSign::FxMinusSign(FxExpression *operand) +: FxExpression(operand->ScriptPosition) +{ + Operand=operand; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxMinusSign::~FxMinusSign() +{ + SAFE_DELETE(Operand); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxMinusSign::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(Operand, ctx); + + if (Operand->isConstant()) + { + ExpVal val = Operand->EvalExpression(NULL, ctx.cls); + FxExpression *e = val.Type == VAL_Int? + new FxConstant(-val.Int, ScriptPosition) : + new FxConstant(-val.Float, ScriptPosition); + delete this; + return e; + } + ValueType = Operand->ValueType; + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxMinusSign::EvalExpression (AActor *self, const PClass *cls) +{ + ExpVal ret; + + if (ValueType == VAL_Int) + { + ret.Int = -Operand->EvalExpression(self, cls).GetInt(); + ret.Type = VAL_Int; + } + else + { + ret.Float = -Operand->EvalExpression(self, cls).GetFloat(); + ret.Type = VAL_Float; + } + return ret; +} + + +//========================================================================== +// +// +// +//========================================================================== + +FxUnaryNotBitwise::FxUnaryNotBitwise(FxExpression *operand) +: FxExpression(operand->ScriptPosition) +{ + Operand=operand; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxUnaryNotBitwise::~FxUnaryNotBitwise() +{ + SAFE_DELETE(Operand); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxUnaryNotBitwise::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(Operand, ctx); + + /* DECORATE allows this. + if (Operand->ValueType != VAL_Int) + { + ScriptPosition.Message(MSG_ERROR, "Integer type expected"); + delete this; + return NULL; + } + */ + + if (Operand->isConstant()) + { + int result = ~Operand->EvalExpression(NULL, ctx.cls).GetInt(); + FxExpression *e = new FxConstant(result, ScriptPosition); + delete this; + return e; + } + ValueType = VAL_Int; + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxUnaryNotBitwise::EvalExpression (AActor *self, const PClass *cls) +{ + ExpVal ret; + + ret.Int = ~Operand->EvalExpression(self, cls).GetInt(); + ret.Type = VAL_Int; + return ret; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxUnaryNotBoolean::FxUnaryNotBoolean(FxExpression *operand) +: FxExpression(operand->ScriptPosition) +{ + Operand=operand; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxUnaryNotBoolean::~FxUnaryNotBoolean() +{ + SAFE_DELETE(Operand); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxUnaryNotBoolean::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + if (Operand) + { + Operand = Operand->ResolveAsBoolean(ctx); + } + if (!Operand) + { + delete this; + return NULL; + } + + if (Operand->isConstant()) + { + bool result = !Operand->EvalExpression(NULL, ctx.cls).GetBool(); + FxExpression *e = new FxConstant(result, ScriptPosition); + delete this; + return e; + } + ValueType = VAL_Int; + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxUnaryNotBoolean::EvalExpression (AActor *self, const PClass *cls) +{ + ExpVal ret; + + ret.Int = !Operand->EvalExpression(self, cls).GetBool(); + ret.Type = VAL_Int; + return ret; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxBinary::FxBinary(int o, FxExpression *l, FxExpression *r) +: FxExpression(l->ScriptPosition) +{ + Operator=o; + left=l; + right=r; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxBinary::~FxBinary() +{ + SAFE_DELETE(left); + SAFE_DELETE(right); +} + +//========================================================================== +// +// +// +//========================================================================== + +bool FxBinary::ResolveLR(FCompileContext& ctx, bool castnumeric) +{ + RESOLVE(left, ctx); + RESOLVE(right, ctx); + if (!left || !right) + { + delete this; + return false; + } + + ValueType = left->ValueType; + if (castnumeric && right->ValueType == VAL_Float) + { + ValueType = VAL_Float; + } + /* not for DECORATE - will be activated later + else if (left->ValueType != right->ValueType) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in expression"); + delete this; + return false; + } + */ + return true; +} + + +//========================================================================== +// +// +// +//========================================================================== + +FxAddSub::FxAddSub(int o, FxExpression *l, FxExpression *r) +: FxBinary(o, l, r) +{ +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxAddSub::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + if (!ResolveLR(ctx, true)) return NULL; + + if (left->isConstant() && right->isConstant()) + { + if (ValueType == VAL_Float) + { + double v; + double v1 = left->EvalExpression(NULL, ctx.cls).GetFloat(); + double v2 = right->EvalExpression(NULL, ctx.cls).GetFloat(); + + v = Operator == '+'? v1 + v2 : + Operator == '-'? v1 - v2 : 0; + + FxExpression *e = new FxConstant(v, ScriptPosition); + delete this; + return e; + } + else + { + int v; + int v1 = left->EvalExpression(NULL, ctx.cls).GetInt(); + int v2 = right->EvalExpression(NULL, ctx.cls).GetInt(); + + v = Operator == '+'? v1 + v2 : + Operator == '-'? v1 - v2 : 0; + + FxExpression *e = new FxConstant(v, ScriptPosition); + delete this; + return e; + + } + } + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxAddSub::EvalExpression (AActor *self, const PClass *cls) +{ + ExpVal ret; + + if (left->ValueType == VAL_Float || right->ValueType ==VAL_Float) + { + double v1 = left->EvalExpression(self, cls).GetFloat(); + double v2 = right->EvalExpression(self, cls).GetFloat(); + + ret.Type = VAL_Float; + ret.Float = Operator == '+'? v1 + v2 : + Operator == '-'? v1 - v2 : 0; + } + else + { + int v1 = left->EvalExpression(self, cls).GetInt(); + int v2 = right->EvalExpression(self, cls).GetInt(); + + ret.Type = VAL_Int; + ret.Int = Operator == '+'? v1 + v2 : + Operator == '-'? v1 - v2 : 0; + + } + return ret; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxMulDiv::FxMulDiv(int o, FxExpression *l, FxExpression *r) +: FxBinary(o, l, r) +{ +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxMulDiv::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + + if (!ResolveLR(ctx, true)) return NULL; + + if (left->isConstant() && right->isConstant()) + { + if (ValueType == VAL_Float) + { + double v; + double v1 = left->EvalExpression(NULL, ctx.cls).GetFloat(); + double v2 = right->EvalExpression(NULL, ctx.cls).GetFloat(); + + if (Operator != '*' && v2 == 0) + { + ScriptPosition.Message(MSG_ERROR, "Division by 0"); + delete this; + return NULL; + } + + v = Operator == '*'? v1 * v2 : + Operator == '/'? v1 / v2 : + Operator == '%'? fmod(v1, v2) : 0; + + FxExpression *e = new FxConstant(v, ScriptPosition); + delete this; + return e; + } + else + { + int v; + int v1 = left->EvalExpression(NULL, ctx.cls).GetInt(); + int v2 = right->EvalExpression(NULL, ctx.cls).GetInt(); + + if (Operator != '*' && v2 == 0) + { + ScriptPosition.Message(MSG_ERROR, "Division by 0"); + delete this; + return NULL; + } + + v = Operator == '*'? v1 * v2 : + Operator == '/'? v1 / v2 : + Operator == '%'? v1 % v2 : 0; + + FxExpression *e = new FxConstant(v, ScriptPosition); + delete this; + return e; + + } + } + return this; +} + + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxMulDiv::EvalExpression (AActor *self, const PClass *cls) +{ + ExpVal ret; + + if (left->ValueType == VAL_Float || right->ValueType ==VAL_Float) + { + double v1 = left->EvalExpression(self, cls).GetFloat(); + double v2 = right->EvalExpression(self, cls).GetFloat(); + + if (Operator != '*' && v2 == 0) + { + I_Error("Division by 0"); + } + + ret.Type = VAL_Float; + ret.Float = Operator == '*'? v1 * v2 : + Operator == '/'? v1 / v2 : + Operator == '%'? fmod(v1, v2) : 0; + } + else + { + int v1 = left->EvalExpression(self, cls).GetInt(); + int v2 = right->EvalExpression(self, cls).GetInt(); + + if (Operator != '*' && v2 == 0) + { + I_Error("Division by 0"); + } + + ret.Type = VAL_Int; + ret.Int = Operator == '*'? v1 * v2 : + Operator == '/'? v1 / v2 : + Operator == '%'? v1 % v2 : 0; + + } + return ret; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxCompareRel::FxCompareRel(int o, FxExpression *l, FxExpression *r) +: FxBinary(o, l, r) +{ +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxCompareRel::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + if (!ResolveLR(ctx, true)) return false; + + if (left->isConstant() && right->isConstant()) + { + int v; + + if (ValueType == VAL_Float) + { + double v1 = left->EvalExpression(NULL, ctx.cls).GetFloat(); + double v2 = right->EvalExpression(NULL, ctx.cls).GetFloat(); + v = Operator == '<'? v1 < v2 : + Operator == '>'? v1 > v2 : + Operator == TK_Geq? v1 >= v2 : + Operator == TK_Leq? v1 <= v2 : 0; + } + else + { + int v1 = left->EvalExpression(NULL, ctx.cls).GetInt(); + int v2 = right->EvalExpression(NULL, ctx.cls).GetInt(); + v = Operator == '<'? v1 < v2 : + Operator == '>'? v1 > v2 : + Operator == TK_Geq? v1 >= v2 : + Operator == TK_Leq? v1 <= v2 : 0; + } + FxExpression *e = new FxConstant(v, ScriptPosition); + delete this; + return e; + } + ValueType = VAL_Int; + return this; +} + + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxCompareRel::EvalExpression (AActor *self, const PClass *cls) +{ + ExpVal ret; + + ret.Type = VAL_Int; + + if (left->ValueType == VAL_Float || right->ValueType ==VAL_Float) + { + double v1 = left->EvalExpression(self, cls).GetFloat(); + double v2 = right->EvalExpression(self, cls).GetFloat(); + ret.Int = Operator == '<'? v1 < v2 : + Operator == '>'? v1 > v2 : + Operator == TK_Geq? v1 >= v2 : + Operator == TK_Leq? v1 <= v2 : 0; + } + else + { + int v1 = left->EvalExpression(self, cls).GetInt(); + int v2 = right->EvalExpression(self, cls).GetInt(); + ret.Int = Operator == '<'? v1 < v2 : + Operator == '>'? v1 > v2 : + Operator == TK_Geq? v1 >= v2 : + Operator == TK_Leq? v1 <= v2 : 0; + } + return ret; +} + + +//========================================================================== +// +// +// +//========================================================================== + +FxCompareEq::FxCompareEq(int o, FxExpression *l, FxExpression *r) +: FxBinary(o, l, r) +{ +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxCompareEq::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + + if (!ResolveLR(ctx, true)) return false; + + if (!left || !right) + { + delete this; + return NULL; + } + + if (left->isConstant() && right->isConstant()) + { + int v; + + if (ValueType == VAL_Float) + { + double v1 = left->EvalExpression(NULL, ctx.cls).GetFloat(); + double v2 = right->EvalExpression(NULL, ctx.cls).GetFloat(); + v = Operator == TK_Eq? v1 == v2 : v1 != v2; + } + else + { + int v1 = left->EvalExpression(NULL, ctx.cls).GetInt(); + int v2 = right->EvalExpression(NULL, ctx.cls).GetInt(); + v = Operator == TK_Eq? v1 == v2 : v1 != v2; + } + FxExpression *e = new FxConstant(v, ScriptPosition); + delete this; + return e; + } + ValueType = VAL_Int; + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxCompareEq::EvalExpression (AActor *self, const PClass *cls) +{ + ExpVal ret; + + ret.Type = VAL_Int; + + if (left->ValueType == VAL_Float || right->ValueType ==VAL_Float) + { + double v1 = left->EvalExpression(self, cls).GetFloat(); + double v2 = right->EvalExpression(self, cls).GetFloat(); + ret.Int = Operator == TK_Eq? v1 == v2 : v1 != v2; + } + else + { + int v1 = left->EvalExpression(self, cls).GetInt(); + int v2 = right->EvalExpression(self, cls).GetInt(); + ret.Int = Operator == TK_Eq? v1 == v2 : v1 != v2; + } + return ret; +} + + +//========================================================================== +// +// +// +//========================================================================== + +FxBinaryInt::FxBinaryInt(int o, FxExpression *l, FxExpression *r) +: FxBinary(o, l, r) +{ + ValueType = VAL_Int; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxBinaryInt::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + if (!ResolveLR(ctx, false)) return false; + + /* later! DECORATE doesn't do proper type checks + if (lax) + { + if (left->ValueType == VAL_Float) left = new FxIntCast(left); + if (right->ValueType == VAL_Float) right = new FxIntCast(right); + } + + if (left->ValueType != VAL_Int || right->->ValueType != VAL_Int) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in expression"); + delete this; + return NULL; + } + */ + + if (left->isConstant() && right->isConstant()) + { + int v1 = left->EvalExpression(NULL, ctx.cls).GetInt(); + int v2 = right->EvalExpression(NULL, ctx.cls).GetInt(); + + FxExpression *e = new FxConstant( + Operator == TK_LShift? v1 << v2 : + Operator == TK_RShift? v1 >> v2 : + Operator == TK_URShift? int(unsigned int(v1) >> v2) : + Operator == '&'? v1 & v2 : + Operator == '|'? v1 | v2 : + Operator == '^'? v1 ^ v2 : 0, ScriptPosition); + + delete this; + return e; + } + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxBinaryInt::EvalExpression (AActor *self, const PClass *cls) +{ + int v1 = left->EvalExpression(self, cls).GetInt(); + int v2 = right->EvalExpression(self, cls).GetInt(); + + ExpVal ret; + + ret.Type = VAL_Int; + ret.Int = + Operator == TK_LShift? v1 << v2 : + Operator == TK_RShift? v1 >> v2 : + Operator == TK_URShift? int(unsigned int(v1) >> v2) : + Operator == '&'? v1 & v2 : + Operator == '|'? v1 | v2 : + Operator == '^'? v1 ^ v2 : 0; + + return ret; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxBinaryLogical::FxBinaryLogical(int o, FxExpression *l, FxExpression *r) +: FxExpression(l->ScriptPosition) +{ + Operator=o; + left=l; + right=r; + ValueType = VAL_Int; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxBinaryLogical::~FxBinaryLogical() +{ + SAFE_DELETE(left); + SAFE_DELETE(right); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxBinaryLogical::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + if (left) left = left->ResolveAsBoolean(ctx); + if (right) right = right->ResolveAsBoolean(ctx); + if (!left || !right) + { + delete this; + return NULL; + } + + int b_left=-1, b_right=-1; + + if (left->isConstant()) b_left = left->EvalExpression(NULL, ctx.cls).GetBool(); + if (right->isConstant()) b_right = right->EvalExpression(NULL, ctx.cls).GetBool(); + + // Do some optimizations. This will throw out all sub-expressions that are not + // needed to retrieve the final result. + if (Operator == TK_AndAnd) + { + if (b_left==0 || b_right==0) + { + FxExpression *x = new FxConstant(0, ScriptPosition); + delete this; + return x; + } + else if (b_left==1 && b_right==1) + { + FxExpression *x = new FxConstant(1, ScriptPosition); + delete this; + return x; + } + else if (b_left==1) + { + FxExpression *x = right; + right=NULL; + delete this; + return x; + } + else if (b_right==1) + { + FxExpression *x = left; + left=NULL; + delete this; + return x; + } + } + else if (Operator == TK_OrOr) + { + if (b_left==1 || b_right==1) + { + FxExpression *x = new FxConstant(1, ScriptPosition); + delete this; + return x; + } + if (b_left==0 && b_right==0) + { + FxExpression *x = new FxConstant(0, ScriptPosition); + delete this; + return x; + } + else if (b_left==0) + { + FxExpression *x = right; + right=NULL; + delete this; + return x; + } + else if (b_right==0) + { + FxExpression *x = left; + left=NULL; + delete this; + return x; + } + } + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxBinaryLogical::EvalExpression (AActor *self, const PClass *cls) +{ + bool b_left = left->EvalExpression(self, cls).GetBool(); + ExpVal ret; + + ret.Type = VAL_Int; + ret.Int = false; + + if (Operator == TK_AndAnd) + { + ret.Int = (b_left && right->EvalExpression(self, cls).GetBool()); + } + else if (Operator == TK_OrOr) + { + ret.Int = (b_left || right->EvalExpression(self, cls).GetBool()); + } + return ret; +} + + +//========================================================================== +// +// +// +//========================================================================== + +FxConditional::FxConditional(FxExpression *c, FxExpression *t, FxExpression *f) +: FxExpression(c->ScriptPosition) +{ + condition = c; + truex=t; + falsex=f; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxConditional::~FxConditional() +{ + SAFE_DELETE(condition); + SAFE_DELETE(truex); + SAFE_DELETE(falsex); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxConditional::Resolve(FCompileContext& ctx) +{ + CHECKRESOLVED(); + if (condition) condition = condition->ResolveAsBoolean(ctx); + RESOLVE(truex, ctx); + RESOLVE(falsex, ctx); + ABORT(condition && truex && falsex); + + if (truex->ValueType == VAL_Int && falsex->ValueType == VAL_Int) + ValueType = VAL_Int; + else + ValueType = VAL_Float; + + if (condition->isConstant()) + { + ExpVal condval = condition->EvalExpression(NULL, ctx.cls); + bool result = condval.GetBool(); + + FxExpression *e = result? truex:falsex; + delete (result? falsex:truex); + falsex = truex = NULL; + delete this; + return e; + } + + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxConditional::EvalExpression (AActor *self, const PClass *cls) +{ + ExpVal condval = condition->EvalExpression(self, cls); + bool result = condval.GetBool(); + + FxExpression *e = result? truex:falsex; + return e->EvalExpression(self, cls); +} + +//========================================================================== +// +// +// +//========================================================================== +FxAbs::FxAbs(FxExpression *v) +: FxExpression(v->ScriptPosition) +{ + val = v; + Type = VAL_Int; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxAbs::~FxAbs() +{ + SAFE_DELETE(val); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxAbs::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(val, ctx); + + if (val->isConstant()) + { + ExpVal value = val->EvalExpression(NULL, ctx.cls); + switch (value.Type) + { + case VAL_Int: + value.Int = abs(value.Int); + break; + + case VAL_Float: + value.Float = fabs(value.Float); + + default: + // shouldn't happen + delete this; + return NULL; + } + FxExpression *x = new FxConstant(value, ScriptPosition); + delete this; + return x; + } + ValueType = val->ValueType; + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxAbs::EvalExpression (AActor *self, const PClass *cls) +{ + ExpVal value = val->EvalExpression(self, cls); + switch (value.Type) + { + default: + case VAL_Int: + value.Int = abs(value.Int); + break; + + case VAL_Float: + value.Float = fabs(value.Float); + break; + } + return value; +} + +//========================================================================== +// +// +// +//========================================================================== +FxRandom::FxRandom(FRandom * r, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos) +: FxExpression(pos) +{ + rng = r; + if (min && max) + { + min = new FxIntCast(mi); + max = new FxIntCast(ma); + } + else + { + SAFE_DELETE(mi); + SAFE_DELETE(ma); + min = max = NULL; + } + ValueType = VAL_Int; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxRandom::~FxRandom() +{ + SAFE_DELETE(min); + SAFE_DELETE(max); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxRandom::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + if (min && max) + { + RESOLVE(min, ctx); + RESOLVE(max, ctx); + ABORT(min && max); + } + return this; +}; + + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxRandom::EvalExpression (AActor *self, const PClass *cls) +{ + int minval = min->EvalExpression (self, cls).GetInt(); + int maxval = max->EvalExpression (self, cls).GetInt(); + + ExpVal val; + val.Type = VAL_Int; + + if (maxval < minval) + { + swap (maxval, minval); + } + + val.Int = (*rng)(maxval - minval + 1) + minval; + return val; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxRandom2::FxRandom2(FRandom *r, FxExpression *m, const FScriptPosition &pos) +: FxExpression(pos) +{ + rng = r; + if (m) mask = new FxIntCast(m); + else mask = new FxConstant(-1, pos); + ValueType = VAL_Int; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxRandom2::~FxRandom2() +{ + SAFE_DELETE(mask); +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxRandom2::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(mask, ctx); + return this; +} + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxRandom2::EvalExpression (AActor *self, const PClass *cls) +{ + ExpVal maskval = mask->EvalExpression(self, cls); + int imaskval = maskval.GetInt(); + + maskval.Type = VAL_Int; + maskval.Int = rng->Random2(imaskval); + return maskval; +} diff --git a/src/thingdef/thingdef_main.cpp b/src/thingdef/thingdef_main.cpp index 85513e156..55fe386a6 100644 --- a/src/thingdef/thingdef_main.cpp +++ b/src/thingdef/thingdef_main.cpp @@ -51,6 +51,7 @@ void FinishThingdef(); void ParseOldDecoration(FScanner &sc, EDefinitionType def); // STATIC FUNCTION PROTOTYPES -------------------------------------------- +PSymbolTable GlobalSymbols; //========================================================================== // @@ -82,11 +83,11 @@ static void ParseDecorate (FScanner &sc) } case TK_Const: - ParseConstant (sc, &RUNTIME_CLASS(AActor)->Symbols, RUNTIME_CLASS(AActor)); + ParseConstant (sc, &GlobalSymbols, RUNTIME_CLASS(AActor)); break; case TK_Enum: - ParseEnum (sc, &RUNTIME_CLASS(AActor)->Symbols, RUNTIME_CLASS(AActor)); + ParseEnum (sc, &GlobalSymbols, RUNTIME_CLASS(AActor)); break; case TK_Pickup: diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index 557bee5c7..a9db54cc2 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -50,6 +50,7 @@ #include "doomerrors.h" #include "autosegs.h" #include "i_system.h" +#include "thingdef_exp.h" //========================================================================== @@ -68,10 +69,11 @@ void ParseConstant (FScanner &sc, PSymbolTable * symt, PClass *cls) sc.MustGetToken(TK_Identifier); FName symname = sc.String; sc.MustGetToken('='); - int expr = ParseExpression (sc, false, cls); + FxExpression *expr = ParseExpression (sc, cls); sc.MustGetToken(';'); - int val = EvalExpressionI (expr, NULL, cls); + int val = expr->EvalExpression(NULL, cls).GetInt(); + delete expr; PSymbolConst *sym = new PSymbolConst; sym->SymbolName = symname; sym->SymbolType = SYM_Const; @@ -79,8 +81,8 @@ void ParseConstant (FScanner &sc, PSymbolTable * symt, PClass *cls) if (symt->AddSymbol (sym) == NULL) { delete sym; - sc.ScriptError ("'%s' is already defined in class '%s'.", - symname.GetChars(), cls->TypeName.GetChars()); + sc.ScriptError ("'%s' is already defined in '%s'.", + symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); } } @@ -103,8 +105,9 @@ void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls) FName symname = sc.String; if (sc.CheckToken('=')) { - int expr = ParseExpression(sc, false, cls); - currvalue = EvalExpressionI(expr, NULL, cls); + FxExpression *expr = ParseExpression (sc, cls); + currvalue = expr->EvalExpression(NULL, cls).GetInt(); + delete expr; } PSymbolConst *sym = new PSymbolConst; sym->SymbolName = symname; @@ -113,8 +116,8 @@ void ParseEnum (FScanner &sc, PSymbolTable *symt, PClass *cls) if (symt->AddSymbol (sym) == NULL) { delete sym; - sc.ScriptError ("'%s' is already defined in class '%s'.", - symname.GetChars(), cls->TypeName.GetChars()); + sc.ScriptError ("'%s' is already defined in '%s'.", + symname.GetChars(), cls? cls->TypeName.GetChars() : "Global"); } // This allows a comma after the last value but doesn't enforce it. if (sc.CheckToken('}')) break; diff --git a/zdoom.vcproj b/zdoom.vcproj index a8d235bc9..02cbb6cad 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -1063,6 +1063,14 @@ RelativePath=".\src\thingdef\thingdef_exp.cpp" > + + + +