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.
This commit is contained in:
Randy Heit 2015-01-08 21:36:42 -06:00
parent 7d0faa5bd5
commit 649875b17e
5 changed files with 207 additions and 35 deletions

View file

@ -297,34 +297,42 @@ static void FinishThingdef()
if (func == NULL) if (func == NULL)
{ {
FCompileContext ctx(tcall->ActorClass); FCompileContext ctx(tcall->ActorClass);
tcall->Code->Resolve(ctx); tcall->Code = static_cast<FxTailable *>(tcall->Code->Resolve(ctx));
VMFunctionBuilder buildit;
// Allocate registers used to pass parameters in. // Make sure resolving it didn't obliterate it.
// self, stateowner, state (all are pointers) if (tcall->Code != NULL)
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]; VMFunctionBuilder buildit;
int labellen = mysnprintf(label, countof(label), "Function %s.States[%d] (*%d)",
tcall->ActorClass->TypeName.GetChars(), tcall->FirstState, tcall->NumStates); // Allocate registers used to pass parameters in.
DumpFunction(dump, sfunc, label, labellen); // self, stateowner, state (all are pointers)
codesize += sfunc->CodeSize; 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; if (tcall->Code != NULL)
tcall->Code = NULL;
for (int k = 0; k < tcall->NumStates; ++k)
{ {
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);
}
} }
} }

View file

@ -189,7 +189,7 @@ AFuncDesc *FindFunction(const char * string);
void ParseStates(FScanner &sc, PClassActor *actor, AActor *defaults, Baggage &bag); void ParseStates(FScanner &sc, PClassActor *actor, AActor *defaults, Baggage &bag);
void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression *> &out_params, void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression *> &out_params,
PFunction *afd, FString statestring, FStateDefinitions *statedef); 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); class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag);
PFunction *FindGlobalActionFunction(const char *name); PFunction *FindGlobalActionFunction(const char *name);

View file

@ -854,10 +854,29 @@ public:
FxSequence(const FScriptPosition &pos) : FxTailable(pos) {} FxSequence(const FScriptPosition &pos) : FxTailable(pos) {}
FxExpression *Resolve(FCompileContext&); FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build, bool tailcall); 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(); 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);
};
//========================================================================== //==========================================================================
// //
// //

View file

@ -3311,6 +3311,120 @@ VMFunction *FxSequence::GetDirectFunction()
return NULL; 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<FxTailable *>(WhenTrue->Resolve(ctx));
ABORT(WhenTrue);
}
if (WhenFalse != NULL)
{
WhenFalse = static_cast<FxTailable *>(WhenFalse->Resolve(ctx));
ABORT(WhenFalse);
}
ValueType = VAL_Unknown;
if (Condition->isConstant())
{
ExpVal condval = static_cast<FxConstant *>(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();
}
//========================================================================== //==========================================================================
// //
//========================================================================== //==========================================================================

View file

@ -314,7 +314,7 @@ do_stop:
continue; continue;
} }
ParseActions(sc, state, statestring, tcall, bag); tcall->Code = ParseActions(sc, state, statestring, bag);
goto endofstate; goto endofstate;
} }
sc.UnGet(); 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. // If it's not a '{', then it should be a single action.
// Otherwise, it's a sequence of actions. // Otherwise, it's a sequence of actions.
if (!sc.Compare("{")) if (!sc.Compare("{"))
{ {
tcall->Code = ParseAction(sc, state, statestring, bag); return ParseAction(sc, state, statestring, bag);
return;
} }
FxSequence *seq = new FxSequence(sc); const FScriptPosition pos(sc);
FxSequence *seq = NULL;
sc.MustGetString(); sc.MustGetString();
while (!sc.Compare("}")) while (!sc.Compare("}"))
{ {
FxVMFunctionCall *call = ParseAction(sc, state, statestring, bag); FxTailable *add;
seq->Add(call); if (sc.Compare("if"))
sc.MustGetStringName(";"); { // Hangle an if statement
sc.MustGetString(); 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;
} }
//========================================================================== //==========================================================================