mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-12-11 21:00:53 +00:00
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:
parent
7d0faa5bd5
commit
649875b17e
5 changed files with 207 additions and 35 deletions
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
};
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
Loading…
Reference in a new issue