diff --git a/src/p_xlat.cpp b/src/p_xlat.cpp index 07828ac50..6023b1892 100644 --- a/src/p_xlat.cpp +++ b/src/p_xlat.cpp @@ -130,21 +130,33 @@ void P_TranslateLineDef (line_t *ld, maplinedef_t *mld) ld->activation = SPAC_UseThrough; } // Set special arguments. + FXlatExprState state; + state.tag = tag; + state.linetype = special; for (int t = 0; t < LINETRANS_MAXARGS; ++t) { - ld->args[t] = linetrans->args[t]; - // Apply tag modifications, if needed. - int tagop = (linetrans->flags >> (LINETRANS_TAGSHIFT + t*TAGOP_NUMBITS)) & TAGOP_MASK; - switch (tagop) + int arg = linetrans->args[t]; + int argop = (linetrans->flags >> (LINETRANS_TAGSHIFT + t*TAGOP_NUMBITS)) & TAGOP_MASK; + + switch (argop) { - case TAGOP_None: default: break; - case TAGOP_Add: ld->args[t] += tag; break; - case TAGOP_Mul: ld->args[t] *= tag; break; - case TAGOP_Div: ld->args[t] = tag / ld->args[t]; break; - case TAGOP_Mod: ld->args[t] = tag % ld->args[t]; break; - case TAGOP_And: ld->args[t] &= tag; break; - case TAGOP_Or: ld->args[t] |= tag; break; - case TAGOP_Xor: ld->args[t] ^= tag; break; + case ARGOP_Const: + ld->args[t] = arg; + break; + case ARGOP_Tag: + ld->args[t] = tag; + break; + case ARGOP_Expr: + { + int *xnode = &XlatExpressions[arg]; + state.bIsConstant = true; + XlatExprEval[*xnode](&ld->args[t], xnode, &state); + } + break; + default: + assert(0); + ld->args[t] = 0; + break; } } @@ -349,3 +361,132 @@ int P_TranslateSectorSpecial (int special) return special | mask; } +static const int *Expr_Const(int *dest, const int *xnode, FXlatExprState *state) +{ + *dest = xnode[-1]; + return xnode - 2; +} + +static const int *Expr_Tag(int *dest, const int *xnode, FXlatExprState *state) +{ + *dest = state->tag; + state->bIsConstant = false; + return xnode - 1; +} + +static const int *Expr_Add(int *dest, const int *xnode, FXlatExprState *state) +{ + int op1, op2; + + xnode = XlatExprEval[xnode[-1]](&op2, xnode-1, state); + xnode = XlatExprEval[xnode[0]](&op1, xnode, state); + *dest = op1 + op2; + return xnode; +} + +static const int *Expr_Sub(int *dest, const int *xnode, FXlatExprState *state) +{ + int op1, op2; + + xnode = XlatExprEval[xnode[-1]](&op2, xnode-1, state); + xnode = XlatExprEval[xnode[0]](&op1, xnode, state); + *dest = op1 - op2; + return xnode; +} + +static const int *Expr_Mul(int *dest, const int *xnode, FXlatExprState *state) +{ + int op1, op2; + + xnode = XlatExprEval[xnode[-1]](&op2, xnode-1, state); + xnode = XlatExprEval[xnode[0]](&op1, xnode, state); + *dest = op1 * op2; + return xnode; +} + +static void Div0Check(int &op1, int &op2, const FXlatExprState *state) +{ + if (op2 == 0) + { + Printf("Xlat: Division by 0 for line type %d\n", state->linetype); + // Set some safe values + op1 = 0; + op2 = 1; + } +} + +static const int *Expr_Div(int *dest, const int *xnode, FXlatExprState *state) +{ + int op1, op2; + + xnode = XlatExprEval[xnode[-1]](&op2, xnode-1, state); + xnode = XlatExprEval[xnode[0]](&op1, xnode, state); + Div0Check(op1, op2, state); + *dest = op1 / op2; + return xnode; +} + +static const int *Expr_Mod(int *dest, const int *xnode, FXlatExprState *state) +{ + int op1, op2; + + xnode = XlatExprEval[xnode[-1]](&op2, xnode-1, state); + xnode = XlatExprEval[xnode[0]](&op1, xnode, state); + Div0Check(op1, op2, state); + *dest = op1 % op2; + return xnode; +} + +static const int *Expr_And(int *dest, const int *xnode, FXlatExprState *state) +{ + int op1, op2; + + xnode = XlatExprEval[xnode[-1]](&op2, xnode-1, state); + xnode = XlatExprEval[xnode[0]](&op1, xnode, state); + *dest = op1 & op2; + return xnode; +} + +static const int *Expr_Or(int *dest, const int *xnode, FXlatExprState *state) +{ + int op1, op2; + + xnode = XlatExprEval[xnode[-1]](&op2, xnode-1, state); + xnode = XlatExprEval[xnode[0]](&op1, xnode, state); + *dest = op1 | op2; + return xnode; +} + +static const int *Expr_Xor(int *dest, const int *xnode, FXlatExprState *state) +{ + int op1, op2; + + xnode = XlatExprEval[xnode[-1]](&op2, xnode-1, state); + xnode = XlatExprEval[xnode[0]](&op1, xnode, state); + *dest = op1 ^ op2; + return xnode; +} + +static const int *Expr_Neg(int *dest, const int *xnode, FXlatExprState *state) +{ + int op; + + xnode = XlatExprEval[xnode[-1]](&op, xnode-1, state); + *dest = -op; + return xnode; +} + +const int* (*XlatExprEval[XEXP_COUNT])(int *dest, const int *xnode, FXlatExprState *state) = +{ + Expr_Const, + Expr_Tag, + Expr_Add, + Expr_Sub, + Expr_Mul, + Expr_Div, + Expr_Mod, + Expr_And, + Expr_Or, + Expr_Xor, + Expr_Neg +}; \ No newline at end of file diff --git a/src/xlat/parse_xlat.cpp b/src/xlat/parse_xlat.cpp index 17cd42d21..0b5c36235 100644 --- a/src/xlat/parse_xlat.cpp +++ b/src/xlat/parse_xlat.cpp @@ -57,6 +57,7 @@ DEFINE_TOKEN_TRANS(XLAT_) static FString LastTranslator; TAutoGrowArray SimpleLineTranslations; +TArray XlatExpressions; FBoomTranslator Boomish[MAX_BOOMISH]; int NumBoomish; TAutoGrowArray SectorTranslations; @@ -74,7 +75,7 @@ struct SpecialArgs struct SpecialArg { int arg; - ELineTransTagOp tagop; + ELineTransArgOp argop; }; struct ListFilter @@ -108,6 +109,7 @@ struct XlatParseContext : public FParseContext XlatParseContext(void *parser, ParseFunc parse, int *tt) : FParseContext(parser, parse, tt) { + DefiningLineType = -1; } //========================================================================== @@ -152,6 +154,8 @@ struct XlatParseContext : public FParseContext } return false; } + + int DefiningLineType; }; #include "xlat_parser.c" @@ -166,6 +170,7 @@ struct XlatParseContext : public FParseContext void P_ClearTranslator() { SimpleLineTranslations.Clear(); + XlatExpressions.Clear(); NumBoomish = 0; SectorTranslations.Clear(); SectorMasks.Clear(); diff --git a/src/xlat/xlat.h b/src/xlat/xlat.h index 765b9fd78..c5471ac8e 100644 --- a/src/xlat/xlat.h +++ b/src/xlat/xlat.h @@ -4,18 +4,13 @@ #include "doomtype.h" #include "tarray.h" -enum ELineTransTagOp +enum ELineTransArgOp { - TAGOP_None, - TAGOP_Add, - TAGOP_Mul, - TAGOP_Div, - TAGOP_Mod, - TAGOP_And, - TAGOP_Or, - TAGOP_Xor, + ARGOP_Const, + ARGOP_Tag, + ARGOP_Expr, - TAGOP_NUMBITS = 3, + TAGOP_NUMBITS = 2, TAGOP_MASK = (1 << TAGOP_NUMBITS) - 1 }; @@ -25,6 +20,23 @@ enum LINETRANS_TAGSHIFT = 30 - LINETRANS_MAXARGS * TAGOP_NUMBITS, }; +enum +{ + XEXP_Const, + XEXP_Tag, + XEXP_Add, + XEXP_Sub, + XEXP_Mul, + XEXP_Div, + XEXP_Mod, + XEXP_And, + XEXP_Or, + XEXP_Xor, + XEXP_Neg, + + XEXP_COUNT +}; + struct FLineTrans { int special; @@ -95,12 +107,21 @@ struct FLineFlagTrans bool ismask; }; +struct FXlatExprState +{ + int linetype; + int tag; + bool bIsConstant; +}; + extern TAutoGrowArray SimpleLineTranslations; +extern TArray XlatExpressions; extern FBoomTranslator Boomish[MAX_BOOMISH]; extern int NumBoomish; extern TAutoGrowArray SectorTranslations; extern TArray SectorMasks; extern FLineFlagTrans LineFlagTranslations[16]; +extern const int* (*XlatExprEval[XEXP_COUNT])(int *dest, const int *xnode, FXlatExprState *state); #endif diff --git a/src/xlat/xlat_parser.y b/src/xlat/xlat_parser.y index 0fc702f74..084e25160 100644 --- a/src/xlat/xlat_parser.y +++ b/src/xlat/xlat_parser.y @@ -89,84 +89,85 @@ single_enum ::= SYM(A) EQUALS exp(B). // //========================================================================== -linetype_declaration ::= exp(linetype) EQUALS exp(flags) COMMA exp(special) LPAREN special_args(arg) RPAREN. +%type linetype_exp {int} +linetype_exp(Z) ::= exp(A). +{ + Z = static_cast(context)->DefiningLineType = A; +} + +linetype_declaration ::= linetype_exp(linetype) EQUALS exp(flags) COMMA exp(special) LPAREN special_args(arg) RPAREN. { SimpleLineTranslations.SetVal(linetype, FLineTrans(special&0xffff, flags+arg.addflags, arg.args[0], arg.args[1], arg.args[2], arg.args[3], arg.args[4])); + static_cast(context)->DefiningLineType = -1; } -linetype_declaration ::= exp EQUALS exp COMMA SYM(S) LPAREN special_args RPAREN. +linetype_declaration ::= linetype_exp EQUALS exp COMMA SYM(S) LPAREN special_args RPAREN. { Printf ("%s, line %d: %s is undefined\n", context->SourceFile, context->SourceLine, S.sym); + static_cast(context)->DefiningLineType = -1; } +%type exp_with_tag {int} +exp_with_tag(A) ::= NUM(B). { XlatExpressions.Push(B.val); A = XlatExpressions.Push(XEXP_Const); } +exp_with_tag(A) ::= TAG. { A = XlatExpressions.Push(XEXP_Tag); } +exp_with_tag(A) ::= exp_with_tag PLUS exp_with_tag. { A = XlatExpressions.Push(XEXP_Add); } +exp_with_tag(A) ::= exp_with_tag MINUS exp_with_tag. { A = XlatExpressions.Push(XEXP_Sub); } +exp_with_tag(A) ::= exp_with_tag MULTIPLY exp_with_tag. { A = XlatExpressions.Push(XEXP_Mul); } +exp_with_tag(A) ::= exp_with_tag DIVIDE exp_with_tag. { A = XlatExpressions.Push(XEXP_Div); } +exp_with_tag(A) ::= exp_with_tag MODULUS exp_with_tag. { A = XlatExpressions.Push(XEXP_Mod); } +exp_with_tag(A) ::= exp_with_tag OR exp_with_tag. { A = XlatExpressions.Push(XEXP_Or); } +exp_with_tag(A) ::= exp_with_tag AND exp_with_tag. { A = XlatExpressions.Push(XEXP_And); } +exp_with_tag(A) ::= exp_with_tag XOR exp_with_tag. { A = XlatExpressions.Push(XEXP_Xor); } +exp_with_tag(A) ::= MINUS exp_with_tag. [NEG] { A = XlatExpressions.Push(XEXP_Neg); } +exp_with_tag(A) ::= LPAREN exp_with_tag(B) RPAREN. { A = B; } + + %type special_arg {SpecialArg} -special_arg(Z) ::= exp(A). +special_arg(Z) ::= exp_with_tag(A). { - Z.arg = A; - Z.tagop = TAGOP_None; -} -special_arg(Z) ::= TAG. -{ - Z.arg = 0; - Z.tagop = TAGOP_Add; -} -special_arg(Z) ::= TAG PLUS exp(A). -{ - Z.arg = A; - Z.tagop = TAGOP_Add; -} -special_arg(Z) ::= TAG MINUS exp(A). -{ - Z.arg = -A; - Z.tagop = TAGOP_Add; -} -special_arg(Z) ::= TAG MULTIPLY exp(A). -{ - Z.arg = A; - Z.tagop = TAGOP_Mul; -} -special_arg(Z) ::= TAG DIVIDE exp(A). -{ - Z.arg = A; - Z.tagop = TAGOP_Div; - if (A == 0) - { - context->PrintError("Division by zero"); + if (XlatExpressions[A] == XEXP_Tag) + { // Store tags directly + Z.arg = 0; + Z.argop = ARGOP_Tag; + XlatExpressions.Delete(A); + } + else + { // Try and evaluate it. If it's a constant, store it and erase the + // expression. Otherwise, store the index to the expression. We make + // no attempt to simplify non-constant expressions. + FXlatExprState state; + int val; + const int *endpt; + int *xnode; + + state.linetype = static_cast(context)->DefiningLineType; + state.tag = 0; + state.bIsConstant = true; + xnode = &XlatExpressions[A]; + endpt = XlatExprEval[*xnode](&val, xnode, &state); + if (state.bIsConstant) + { + Z.arg = val; + Z.argop = ARGOP_Const; + endpt++; + assert(endpt >= &XlatExpressions[0]); + XlatExpressions.Resize((unsigned)(endpt - &XlatExpressions[0])); + } + else + { + Z.arg = A; + Z.argop = ARGOP_Expr; + } } } -special_arg(Z) ::= TAG MODULUS exp(A). -{ - Z.arg = A; - Z.tagop = TAGOP_Mod; - if (A == 0) - { - context->PrintError("Division by zero"); - } -} -special_arg(Z) ::= TAG OR exp(A). -{ - Z.arg = A; - Z.tagop = TAGOP_Or; -} -special_arg(Z) ::= TAG AND exp(A). -{ - Z.arg = A; - Z.tagop = TAGOP_And; -} -special_arg(Z) ::= TAG XOR exp(A). -{ - Z.arg = A; - Z.tagop = TAGOP_Xor; -} - %type multi_special_arg {SpecialArgs} multi_special_arg(Z) ::= special_arg(A). { - Z.addflags = A.tagop << LINETRANS_TAGSHIFT; + Z.addflags = A.argop << LINETRANS_TAGSHIFT; Z.argcount = 1; Z.args[0] = A.arg; Z.args[1] = 0; @@ -179,7 +180,7 @@ multi_special_arg(Z) ::= multi_special_arg(A) COMMA special_arg(B). Z = A; if (Z.argcount < LINETRANS_MAXARGS) { - Z.addflags |= B.tagop << (LINETRANS_TAGSHIFT + Z.argcount * TAGOP_NUMBITS); + Z.addflags |= B.argop << (LINETRANS_TAGSHIFT + Z.argcount * TAGOP_NUMBITS); Z.args[Z.argcount] = B.arg; Z.argcount++; }