mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-12-11 12:51:27 +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,7 +297,11 @@ static void FinishThingdef()
|
|||
if (func == NULL)
|
||||
{
|
||||
FCompileContext ctx(tcall->ActorClass);
|
||||
tcall->Code->Resolve(ctx);
|
||||
tcall->Code = static_cast<FxTailable *>(tcall->Code->Resolve(ctx));
|
||||
|
||||
// Make sure resolving it didn't obliterate it.
|
||||
if (tcall->Code != NULL)
|
||||
{
|
||||
VMFunctionBuilder buildit;
|
||||
|
||||
// Allocate registers used to pass parameters in.
|
||||
|
@ -320,6 +324,9 @@ static void FinishThingdef()
|
|||
codesize += sfunc->CodeSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tcall->Code != NULL)
|
||||
{
|
||||
delete tcall->Code;
|
||||
tcall->Code = NULL;
|
||||
for (int k = 0; k < tcall->NumStates; ++k)
|
||||
|
@ -327,6 +334,7 @@ static void FinishThingdef()
|
|||
tcall->ActorClass->OwnedStates[tcall->FirstState + k].SetAction(func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < PClassActor::AllActorClasses.Size(); i++)
|
||||
{
|
||||
|
|
|
@ -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<FxExpression *> &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);
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
|
|
|
@ -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<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;
|
||||
}
|
||||
|
||||
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);
|
||||
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();
|
||||
}
|
||||
tcall->Code = seq;
|
||||
// Only return a sequence if it has actual content.
|
||||
if (add != NULL)
|
||||
{
|
||||
if (seq == NULL)
|
||||
{
|
||||
seq = new FxSequence(pos);
|
||||
}
|
||||
seq->Add(add);
|
||||
}
|
||||
}
|
||||
return seq;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
Loading…
Reference in a new issue