From 649875b17e78a504880e9c8914c512b527b5f224 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 8 Jan 2015 21:36:42 -0600 Subject: [PATCH] Add if/else blocks for action sequences - So now you can do something like this for an action: { if (health > 1000) { A_Scream; } else { A_XScream; } } Yes, the braces are required. Because I see too many instances where somebody writes an if statement in ACS and doesn't understand why it doesn't work right because they forgot braces. - Fixed: Not actually putting an action between { and } would crash. --- src/thingdef/thingdef.cpp | 54 +++++++------ src/thingdef/thingdef.h | 2 +- src/thingdef/thingdef_exp.h | 21 ++++- src/thingdef/thingdef_expression.cpp | 114 +++++++++++++++++++++++++++ src/thingdef/thingdef_states.cpp | 51 +++++++++--- 5 files changed, 207 insertions(+), 35 deletions(-) diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index d1c76943a..60a0da8de 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -297,34 +297,42 @@ static void FinishThingdef() if (func == NULL) { FCompileContext ctx(tcall->ActorClass); - tcall->Code->Resolve(ctx); - VMFunctionBuilder buildit; + tcall->Code = static_cast(tcall->Code->Resolve(ctx)); - // Allocate registers used to pass parameters in. - // self, stateowner, state (all are pointers) - buildit.Registers[REGT_POINTER].Get(3); - - // Emit a tail call via FxVMFunctionCall - tcall->Code->Emit(&buildit, true); - - VMScriptFunction *sfunc = buildit.MakeFunction(); - sfunc->NumArgs = NAP; - func = sfunc; - - if (dump != NULL) + // Make sure resolving it didn't obliterate it. + if (tcall->Code != NULL) { - char label[64]; - int labellen = mysnprintf(label, countof(label), "Function %s.States[%d] (*%d)", - tcall->ActorClass->TypeName.GetChars(), tcall->FirstState, tcall->NumStates); - DumpFunction(dump, sfunc, label, labellen); - codesize += sfunc->CodeSize; + VMFunctionBuilder buildit; + + // Allocate registers used to pass parameters in. + // self, stateowner, state (all are pointers) + buildit.Registers[REGT_POINTER].Get(3); + + // Emit a tail call via FxVMFunctionCall + tcall->Code->Emit(&buildit, true); + + VMScriptFunction *sfunc = buildit.MakeFunction(); + sfunc->NumArgs = NAP; + func = sfunc; + + if (dump != NULL) + { + char label[64]; + int labellen = mysnprintf(label, countof(label), "Function %s.States[%d] (*%d)", + tcall->ActorClass->TypeName.GetChars(), tcall->FirstState, tcall->NumStates); + DumpFunction(dump, sfunc, label, labellen); + codesize += sfunc->CodeSize; + } } } - delete tcall->Code; - tcall->Code = NULL; - for (int k = 0; k < tcall->NumStates; ++k) + if (tcall->Code != NULL) { - tcall->ActorClass->OwnedStates[tcall->FirstState + k].SetAction(func); + delete tcall->Code; + tcall->Code = NULL; + for (int k = 0; k < tcall->NumStates; ++k) + { + tcall->ActorClass->OwnedStates[tcall->FirstState + k].SetAction(func); + } } } diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index ecec26775..9eb6d2677 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -189,7 +189,7 @@ AFuncDesc *FindFunction(const char * string); void ParseStates(FScanner &sc, PClassActor *actor, AActor *defaults, Baggage &bag); void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray &out_params, PFunction *afd, FString statestring, FStateDefinitions *statedef); -void ParseActions(FScanner &sc, FState state, FString statestring, FStateTempCall *tcall, Baggage &bag); +FxTailable *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag); class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag); PFunction *FindGlobalActionFunction(const char *name); diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 47cda7c46..7f8bad6ea 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -854,10 +854,29 @@ public: FxSequence(const FScriptPosition &pos) : FxTailable(pos) {} FxExpression *Resolve(FCompileContext&); ExpEmit Emit(VMFunctionBuilder *build, bool tailcall); - void Add(FxTailable *expr) { Expressions.Push(expr); } + void Add(FxTailable *expr) { if (expr != NULL) Expressions.Push(expr); } VMFunction *GetDirectFunction(); }; +//========================================================================== +// +// FxIfStatement +// +//========================================================================== + +class FxIfStatement : public FxTailable +{ + FxExpression *Condition; + FxTailable *WhenTrue; + FxTailable *WhenFalse; + +public: + FxIfStatement(FxExpression *cond, FxTailable *true_part, FxTailable *false_part, const FScriptPosition &pos); + ~FxIfStatement(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build, bool tailcall); +}; + //========================================================================== // // diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index c219c92da..eb8256778 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -3311,6 +3311,120 @@ VMFunction *FxSequence::GetDirectFunction() return NULL; } +//========================================================================== +// +// FxIfStatement +// +//========================================================================== + +FxIfStatement::FxIfStatement(FxExpression *cond, FxTailable *true_part, + FxTailable *false_part, const FScriptPosition &pos) +: FxTailable(pos) +{ + Condition = cond; + WhenTrue = true_part; + WhenFalse = false_part; + assert(cond != NULL); +} + +FxIfStatement::~FxIfStatement() +{ + SAFE_DELETE(Condition); + SAFE_DELETE(WhenTrue); + SAFE_DELETE(WhenFalse); +} + +FxExpression *FxIfStatement::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + if (WhenTrue == NULL && WhenFalse == NULL) + { // We don't do anything either way, so disappear + delete this; + return NULL; + } + Condition = Condition->ResolveAsBoolean(ctx); + ABORT(Condition); + if (WhenTrue != NULL) + { + WhenTrue = static_cast(WhenTrue->Resolve(ctx)); + ABORT(WhenTrue); + } + if (WhenFalse != NULL) + { + WhenFalse = static_cast(WhenFalse->Resolve(ctx)); + ABORT(WhenFalse); + } + ValueType = VAL_Unknown; + + if (Condition->isConstant()) + { + ExpVal condval = static_cast(Condition)->GetValue(); + bool result = condval.GetBool(); + + FxTailable *e = result ? WhenTrue : WhenFalse; + delete (result ? WhenFalse : WhenTrue); + WhenTrue = WhenFalse = NULL; + delete this; + return e; + } + return this; +} + +ExpEmit FxIfStatement::Emit(VMFunctionBuilder *build, bool tailcall) +{ + ExpEmit v; + size_t jumpspot; + FxTailable *path1, *path2; + int condcheck; + + // This is pretty much copied from FxConditional, except we don't + // keep any results. + ExpEmit cond = Condition->Emit(build); + assert(cond.RegType == REGT_INT && !cond.Konst); + + if (WhenTrue != NULL) + { + path1 = WhenTrue; + path2 = WhenFalse; + condcheck = 1; + } + else + { + // When there is only a false path, reverse the condition so we can + // treat it as a true path. + assert(WhenFalse != NULL); + path1 = WhenFalse; + path2 = NULL; + condcheck = 0; + } + + // Test condition. + build->Emit(OP_EQ_K, condcheck, cond.RegNum, build->GetConstantInt(0)); + jumpspot = build->Emit(OP_JMP, 0); + cond.Free(build); + + // Evaluate first path + v = path1->Emit(build, tailcall); + v.Free(build); + if (path2 != NULL) + { + size_t path1jump = build->Emit(OP_JMP, 0); + // Evaluate second path + build->BackpatchToHere(jumpspot); + v = path2->Emit(build, tailcall); + v.Free(build); + jumpspot = path1jump; + } + build->BackpatchToHere(jumpspot); + if (tailcall) + { + // When tailcall is true, execution is not expected to get past + // this if statement, so issue a RET. + build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0); + } + return ExpEmit(); +} + //========================================================================== // //========================================================================== diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index 94f5de1bc..fedfd924e 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -314,7 +314,7 @@ do_stop: continue; } - ParseActions(sc, state, statestring, tcall, bag); + tcall->Code = ParseActions(sc, state, statestring, bag); goto endofstate; } sc.UnGet(); @@ -352,26 +352,57 @@ endofstate: // //========================================================================== -void ParseActions(FScanner &sc, FState state, FString statestring, FStateTempCall *tcall, Baggage &bag) +FxTailable *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag) { // If it's not a '{', then it should be a single action. // Otherwise, it's a sequence of actions. if (!sc.Compare("{")) { - tcall->Code = ParseAction(sc, state, statestring, bag); - return; + return ParseAction(sc, state, statestring, bag); } - FxSequence *seq = new FxSequence(sc); + const FScriptPosition pos(sc); + + FxSequence *seq = NULL; sc.MustGetString(); while (!sc.Compare("}")) { - FxVMFunctionCall *call = ParseAction(sc, state, statestring, bag); - seq->Add(call); - sc.MustGetStringName(";"); - sc.MustGetString(); + FxTailable *add; + if (sc.Compare("if")) + { // Hangle an if statement + FxExpression *cond; + FxTailable *true_part, *false_part = NULL; + sc.MustGetStringName("("); + cond = ParseExpression(sc, bag.Info); + sc.MustGetStringName(")"); + sc.MustGetStringName("{"); // braces are mandatory + true_part = ParseActions(sc, state, statestring, bag); + sc.MustGetString(); + if (sc.Compare("else")) + { + sc.MustGetStringName("{"); // braces are still mandatory + false_part = ParseActions(sc, state, statestring, bag); + sc.MustGetString(); + } + add = new FxIfStatement(cond, true_part, false_part, sc); + } + else + { // Handle a regular action function call + add = ParseAction(sc, state, statestring, bag); + sc.MustGetStringName(";"); + sc.MustGetString(); + } + // Only return a sequence if it has actual content. + if (add != NULL) + { + if (seq == NULL) + { + seq = new FxSequence(pos); + } + seq->Add(add); + } } - tcall->Code = seq; + return seq; } //==========================================================================