diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index c9eca78cf..b878e50f1 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -964,6 +964,26 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +//========================================================================== +// +// FxForLoop +// +//========================================================================== + +class FxForLoop : public FxExpression +{ + FxExpression *Init; + FxExpression *Condition; + FxExpression *Iteration; + FxExpression *Code; + +public: + FxForLoop(FxExpression *init, FxExpression *condition, FxExpression *iteration, FxExpression *code, const FScriptPosition &pos); + ~FxForLoop(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // FxJumpStatement diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 27de1381c..5017ae421 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -4060,6 +4060,133 @@ ExpEmit FxDoWhileLoop::Emit(VMFunctionBuilder *build) return ExpEmit(); } +//========================================================================== +// +// FxForLoop +// +//========================================================================== + +FxForLoop::FxForLoop(FxExpression *init, FxExpression *condition, FxExpression *iteration, FxExpression *code, const FScriptPosition &pos) +: FxExpression(pos), Init(init), Condition(condition), Iteration(iteration), Code(code) +{ + ValueType = TypeVoid; +} + +FxForLoop::~FxForLoop() +{ + SAFE_DELETE(Init); + SAFE_DELETE(Condition); + SAFE_DELETE(Iteration); + SAFE_DELETE(Code); +} + +FxExpression *FxForLoop::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE_OPT(Init, ctx); + SAFE_RESOLVE_OPT(Condition, ctx); + SAFE_RESOLVE_OPT(Iteration, ctx); + SAFE_RESOLVE_OPT(Code, ctx); + + ctx.HandleJumps(TK_Break, this); + ctx.HandleJumps(TK_Continue, this); + + if (Condition != nullptr) + { + if (Condition->ValueType != TypeBool) + { + Condition = new FxBoolCast(Condition); + SAFE_RESOLVE(Condition, ctx); + } + + if (Condition->isConstant()) + { + if (static_cast(Condition)->GetValue().GetBool() == false) + { // Nothing happens + FxExpression *nop = new FxNop(ScriptPosition); + delete this; + return nop; + } + else + { // "for (..; true; ..)" + delete Condition; + Condition = nullptr; + } + } + } + if (Condition == nullptr && Code == nullptr) + { // "for (..; ; ..) { }" + // Someone could be using this for testing. + ScriptPosition.Message(MSG_WARNING, "Infinite empty loop"); + } + + return this; +} + +ExpEmit FxForLoop::Emit(VMFunctionBuilder *build) +{ + assert((Condition && Condition->ValueType == TypeBool && !Condition->isConstant()) || Condition == nullptr); + + size_t loopstart, loopend; + size_t codestart; + size_t jumpspot; + + // Init statement. + if (Init != nullptr) + { + ExpEmit init = Init->Emit(build); + init.Free(build); + } + + // Evaluate the condition and execute/break out of the loop. + codestart = build->GetAddress(); + if (Condition != nullptr) + { + ExpEmit cond = Condition->Emit(build); + build->Emit(OP_TEST, cond.RegNum, 0); + cond.Free(build); + jumpspot = build->Emit(OP_JMP, 0); + } + + // Execute the loop's content. + if (Code != nullptr) + { + ExpEmit code = Code->Emit(build); + code.Free(build); + } + + // Iteration statement. + loopstart = build->GetAddress(); + if (Iteration != nullptr) + { + ExpEmit iter = Iteration->Emit(build); + iter.Free(build); + } + build->Backpatch(build->Emit(OP_JMP, 0), codestart); + + // End of loop. + loopend = build->GetAddress(); + if (Condition != nullptr) + { + build->Backpatch(jumpspot, loopend); + } + + // Give a proper address to any break/continue statement within this loop. + for (unsigned int i = 0; i < JumpAddresses.Size(); i++) + { + if (JumpAddresses[i]->Token == TK_Break) + { + build->Backpatch(JumpAddresses[i]->Address, loopend); + } + else + { // Continue statement. + build->Backpatch(JumpAddresses[i]->Address, loopstart); + } + } + + return ExpEmit(); +} + //========================================================================== // // FxJumpStatement diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index dc5687211..ecc62bf29 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -524,6 +524,49 @@ static FxExpression *ParseDoWhile(FScanner &sc, FState state, FString statestrin return new FxDoWhileLoop(cond, code, sc); } +static FxExpression *ParseFor(FScanner &sc, FState state, FString statestring, Baggage &bag, + PPrototype *&retproto, bool &lastwasret) +{ + FxExpression *init = nullptr; + FxExpression *cond = nullptr; + FxExpression *iter = nullptr; + FxExpression *code = nullptr; + PPrototype *proto; + bool ret; + + // Parse the statements. + sc.MustGetStringName("("); + sc.MustGetString(); + if (!sc.Compare(";")) + { + init = ParseAction(sc, state, statestring, bag); // That's all DECORATE can handle for now. + sc.MustGetStringName(";"); + } + sc.MustGetString(); + if (!sc.Compare(";")) + { + sc.UnGet(); + cond = ParseExpression(sc, bag.Info); + sc.MustGetStringName(";"); + } + sc.MustGetString(); + if (!sc.Compare(")")) + { + iter = ParseAction(sc, state, statestring, bag); + sc.MustGetStringName(")"); + } + + // Now parse the loop's content. + sc.MustGetStringName("{"); // Enforce braces like for if statements. + code = ParseActions(sc, state, statestring, bag, proto, ret); + sc.MustGetString(); + + retproto = ReturnCheck(retproto, proto, sc); + lastwasret = false; + + return new FxForLoop(init, cond, iter, code, sc); +} + FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, PPrototype *&retproto, bool &endswithret) { @@ -560,6 +603,10 @@ FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Bagg { // Handle a do-while loop add = ParseDoWhile(sc, state, statestring, bag, proto, lastwasret); } + else if (sc.Compare("for")) + { // Handle a for loop + add = ParseFor(sc, state, statestring, bag, proto, lastwasret); + } else if (sc.Compare("return")) { // Handle a return statement lastwasret = true;