diff --git a/src/common/scripting/backend/codegen.cpp b/src/common/scripting/backend/codegen.cpp index f00c37683..d98053736 100644 --- a/src/common/scripting/backend/codegen.cpp +++ b/src/common/scripting/backend/codegen.cpp @@ -11382,12 +11382,14 @@ ExpEmit FxForLoop::Emit(VMFunctionBuilder *build) // //========================================================================== -FxForEachLoop::FxForEachLoop(FName vn, FxExpression* arrayvar, FxExpression* arrayvar2, FxExpression* code, const FScriptPosition& pos) - : FxLoopStatement(EFX_ForEachLoop, pos), loopVarName(vn), Array(arrayvar), Array2(arrayvar2), Code(code) +FxForEachLoop::FxForEachLoop(FName vn, FxExpression* arrayvar, FxExpression* arrayvar2, FxExpression* arrayvar3, FxExpression* arrayvar4, FxExpression* code, const FScriptPosition& pos) + : FxLoopStatement(EFX_ForEachLoop, pos), loopVarName(vn), Array(arrayvar), Array2(arrayvar2), Array3(arrayvar3), Array4(arrayvar4), Code(code) { ValueType = TypeVoid; if (Array != nullptr) Array->NeedResult = false; if (Array2 != nullptr) Array2->NeedResult = false; + if (Array3 != nullptr) Array3->NeedResult = false; + if (Array4 != nullptr) Array4->NeedResult = false; if (Code != nullptr) Code->NeedResult = false; } @@ -11395,6 +11397,8 @@ FxForEachLoop::~FxForEachLoop() { SAFE_DELETE(Array); SAFE_DELETE(Array2); + SAFE_DELETE(Array3); + SAFE_DELETE(Array4); SAFE_DELETE(Code); } @@ -11403,6 +11407,16 @@ FxExpression* FxForEachLoop::DoResolve(FCompileContext& ctx) CHECKRESOLVED(); SAFE_RESOLVE(Array, ctx); SAFE_RESOLVE(Array2, ctx); + SAFE_RESOLVE(Array3, ctx); + SAFE_RESOLVE(Array4, ctx); + + if(Array->ValueType->isMap() || Array->ValueType->isMapIterator()) + { + auto mapLoop = new FxMapForEachLoop(NAME_None, loopVarName, Array, Array2, Array3, Array4, Code, ScriptPosition); + Array = Array2 = Array3 = Array4 = Code = nullptr; + delete this; + return mapLoop->Resolve(ctx); + } // Instead of writing a new code generator for this, convert this into // @@ -11488,18 +11502,29 @@ FxExpression *FxMapForEachLoop::Resolve(FCompileContext &ctx) delete this; return nullptr; } + if(valueVarName == NAME_None) + { + ScriptPosition.Message(MSG_ERROR, "missing value for foreach( k, v : m )"); + delete this; + return nullptr; + } - auto keyType = is_iterator ? static_cast(MapExpr->ValueType)->KeyType : static_cast(MapExpr->ValueType)->KeyType; - auto valType = is_iterator ? static_cast(MapExpr->ValueType)->ValueType : static_cast(MapExpr->ValueType)->ValueType; - auto block = new FxCompoundStatement(ScriptPosition); - auto k = new FxLocalVariableDeclaration(keyType, keyVarName, nullptr, 0, ScriptPosition); + + auto valType = is_iterator ? static_cast(MapExpr->ValueType)->ValueType : static_cast(MapExpr->ValueType)->ValueType; + auto keyType = is_iterator ? static_cast(MapExpr->ValueType)->KeyType : static_cast(MapExpr->ValueType)->KeyType; + auto v = new FxLocalVariableDeclaration(valType, valueVarName, nullptr, 0, ScriptPosition); - - block->Add(k); block->Add(v); + if(keyVarName != NAME_None) + { + auto k = new FxLocalVariableDeclaration(keyType, keyVarName, nullptr, 0, ScriptPosition); + block->Add(k); + } + + if(MapExpr->ValueType->isMapIterator()) { /* @@ -11517,7 +11542,11 @@ FxExpression *FxMapForEachLoop::Resolve(FCompileContext &ctx) auto inner_block = new FxCompoundStatement(ScriptPosition); - inner_block->Add(new FxAssign(new FxIdentifier(keyVarName, ScriptPosition), new FxMemberFunctionCall(MapExpr, "GetKey", {}, ScriptPosition), true)); + if(keyVarName != NAME_None) + { + inner_block->Add(new FxAssign(new FxIdentifier(keyVarName, ScriptPosition), new FxMemberFunctionCall(MapExpr, "GetKey", {}, ScriptPosition), true)); + } + inner_block->Add(new FxAssign(new FxIdentifier(valueVarName, ScriptPosition), new FxMemberFunctionCall(MapExpr2, "GetValue", {}, ScriptPosition), true)); inner_block->Add(Code); @@ -11556,7 +11585,10 @@ FxExpression *FxMapForEachLoop::Resolve(FCompileContext &ctx) auto inner_block = new FxCompoundStatement(ScriptPosition); - inner_block->Add(new FxAssign(new FxIdentifier(keyVarName, ScriptPosition), new FxMemberFunctionCall(new FxIdentifier("@it", ScriptPosition), "GetKey", {}, ScriptPosition), true)); + if(keyVarName != NAME_None) + { + inner_block->Add(new FxAssign(new FxIdentifier(keyVarName, ScriptPosition), new FxMemberFunctionCall(new FxIdentifier("@it", ScriptPosition), "GetKey", {}, ScriptPosition), true)); + } inner_block->Add(new FxAssign(new FxIdentifier(valueVarName, ScriptPosition), new FxMemberFunctionCall(new FxIdentifier("@it", ScriptPosition), "GetValue", {}, ScriptPosition), true)); inner_block->Add(Code); diff --git a/src/common/scripting/backend/codegen.h b/src/common/scripting/backend/codegen.h index 07b1ecac0..ac86c5013 100644 --- a/src/common/scripting/backend/codegen.h +++ b/src/common/scripting/backend/codegen.h @@ -2074,10 +2074,12 @@ class FxForEachLoop : public FxLoopStatement FName loopVarName; FxExpression* Array; FxExpression* Array2; + FxExpression* Array3; + FxExpression* Array4; FxExpression* Code; public: - FxForEachLoop(FName vn, FxExpression* arrayvar, FxExpression* arrayvar2, FxExpression* code, const FScriptPosition& pos); + FxForEachLoop(FName vn, FxExpression* arrayvar, FxExpression* arrayvar2, FxExpression* arrayvar3, FxExpression* arrayvar4, FxExpression* code, const FScriptPosition& pos); ~FxForEachLoop(); FxExpression* DoResolve(FCompileContext&); }; diff --git a/src/common/scripting/frontend/zcc_compile.cpp b/src/common/scripting/frontend/zcc_compile.cpp index 08c9ec34e..8eac47a11 100644 --- a/src/common/scripting/frontend/zcc_compile.cpp +++ b/src/common/scripting/frontend/zcc_compile.cpp @@ -3388,9 +3388,11 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast, bool substitute) auto iter = static_cast(ast); auto var = iter->ItName->Name; FxExpression* const itArray = ConvertNode(iter->ItArray); - FxExpression* const itArray2 = ConvertNode(iter->ItArray); // the handler needs two copies of this - here's the easiest place to create them. + FxExpression* const itArray2 = ConvertNode(iter->ItArray); + FxExpression* const itArray3 = ConvertNode(iter->ItArray); + FxExpression* const itArray4 = ConvertNode(iter->ItArray); // the handler needs copies of this - here's the easiest place to create them. FxExpression* const body = ConvertImplicitScopeNode(ast, iter->LoopStatement); - return new FxForEachLoop(iter->ItName->Name, itArray, itArray2, body, *ast); + return new FxForEachLoop(iter->ItName->Name, itArray, itArray2, itArray3, itArray4, body, *ast); } case AST_MapIterationStmt: