diff --git a/src/common/scripting/backend/codegen.cpp b/src/common/scripting/backend/codegen.cpp index 3738aeb5dc..f00c37683b 100644 --- a/src/common/scripting/backend/codegen.cpp +++ b/src/common/scripting/backend/codegen.cpp @@ -2728,7 +2728,7 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx) { FArgumentList args; args.Push(Right); - auto call = new FxMemberFunctionCall(Base, NAME_Copy, args, ScriptPosition); + auto call = new FxMemberFunctionCall(Base, NAME_Copy, std::move(args), ScriptPosition); Right = Base = nullptr; delete this; return call->Resolve(ctx); @@ -2756,7 +2756,7 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx) { FArgumentList args; args.Push(Right); - auto call = new FxMemberFunctionCall(Base, NAME_Copy, args, ScriptPosition); + auto call = new FxMemberFunctionCall(Base, NAME_Copy, std::move(args), ScriptPosition); Right = Base = nullptr; delete this; return call->Resolve(ctx); @@ -8202,7 +8202,7 @@ static bool CheckFunctionCompatiblity(FScriptPosition &ScriptPosition, PFunction // //========================================================================== -FxFunctionCall::FxFunctionCall(FName methodname, FName rngname, FArgumentList &args, const FScriptPosition &pos) +FxFunctionCall::FxFunctionCall(FName methodname, FName rngname, FArgumentList &&args, const FScriptPosition &pos) : FxExpression(EFX_FunctionCall, pos) { MethodName = methodname; @@ -8629,7 +8629,7 @@ FxExpression *FxFunctionCall::Resolve(FCompileContext& ctx) // //========================================================================== -FxMemberFunctionCall::FxMemberFunctionCall(FxExpression *self, FName methodname, FArgumentList &args, const FScriptPosition &pos) +FxMemberFunctionCall::FxMemberFunctionCall(FxExpression *self, FName methodname, FArgumentList &&args, const FScriptPosition &pos) : FxExpression(EFX_MemberFunctionCall, pos) { Self = self; @@ -9174,7 +9174,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) if (a->IsMap()) { // Copy and Move must turn their parameter into a pointer to the backing struct type. - auto o = static_cast(a->ValueType); + auto o = static_cast(a->ValueType); auto backingtype = o->BackingType; if (mapKeyType != o->KeyType || mapValueType != o->ValueType) { @@ -11416,9 +11416,9 @@ FxExpression* FxForEachLoop::DoResolve(FCompileContext& ctx) FName sizevar = "@size"; FName itvar = "@i"; - FArgumentList al; + auto block = new FxCompoundStatement(ScriptPosition); - auto arraysize = new FxMemberFunctionCall(Array, NAME_Size, al, ScriptPosition); + auto arraysize = new FxMemberFunctionCall(Array, NAME_Size, {}, ScriptPosition); auto size = new FxLocalVariableDeclaration(TypeSInt32, sizevar, arraysize, 0, ScriptPosition); auto it = new FxLocalVariableDeclaration(TypeSInt32, itvar, new FxConstant(0, ScriptPosition), 0, ScriptPosition); block->Add(size); @@ -11446,6 +11446,130 @@ FxExpression* FxForEachLoop::DoResolve(FCompileContext& ctx) return block->Resolve(ctx); } +//========================================================================== +// +// FxMapForEachLoop +// +//========================================================================== + +FxMapForEachLoop::FxMapForEachLoop(FName kv, FName vv, FxExpression* mapexpr, FxExpression* mapexpr2, FxExpression* mapexpr3, FxExpression* mapexpr4, FxExpression* code, const FScriptPosition& pos) + : FxExpression(EFX_MapForEachLoop,pos), keyVarName(kv), valueVarName(vv), MapExpr(mapexpr), MapExpr2(mapexpr2), MapExpr3(mapexpr3), MapExpr4(mapexpr4), Code(code) +{ + ValueType = TypeVoid; + if (MapExpr != nullptr) MapExpr->NeedResult = false; + if (MapExpr2 != nullptr) MapExpr2->NeedResult = false; + if (MapExpr3 != nullptr) MapExpr3->NeedResult = false; + if (MapExpr4 != nullptr) MapExpr4->NeedResult = false; + if (Code != nullptr) Code->NeedResult = false; +} + +FxMapForEachLoop::~FxMapForEachLoop() +{ + SAFE_DELETE(MapExpr); + SAFE_DELETE(MapExpr2); + SAFE_DELETE(MapExpr3); + SAFE_DELETE(MapExpr4); + SAFE_DELETE(Code); +} + +FxExpression *FxMapForEachLoop::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(MapExpr, ctx); + SAFE_RESOLVE(MapExpr2, ctx); + SAFE_RESOLVE(MapExpr3, ctx); + SAFE_RESOLVE(MapExpr4, ctx); + + bool is_iterator = false; + + if(!(MapExpr->ValueType->isMap() || (is_iterator = MapExpr->ValueType->isMapIterator()))) + { + ScriptPosition.Message(MSG_ERROR, "foreach( k, v : m ) - 'm' must be a map or a map iterator, but is a %s",MapExpr->ValueType->DescriptiveName()); + 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 v = new FxLocalVariableDeclaration(valType, valueVarName, nullptr, 0, ScriptPosition); + + block->Add(k); + block->Add(v); + + if(MapExpr->ValueType->isMapIterator()) + { + /* + { + KeyType k; + ValueType v; + if(it.ReInit()) while(it.Next()) + { + k = it.GetKey(); + v = it.GetValue(); + body + } + } + */ + + auto inner_block = new FxCompoundStatement(ScriptPosition); + + 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); + + auto reInit = new FxMemberFunctionCall(MapExpr3, "ReInit", {}, ScriptPosition); + block->Add(new FxIfStatement(reInit, new FxWhileLoop(new FxMemberFunctionCall(MapExpr4, "Next", {}, ScriptPosition), inner_block, ScriptPosition), nullptr, ScriptPosition)); + + MapExpr = MapExpr2 = MapExpr3 = MapExpr4 = Code = nullptr; + delete this; + return block->Resolve(ctx); + } + else + { + /* + { + KeyType k; + ValueType v; + MapIterator @it; + @it.Init(map); + while(@it.Next()) + { + k = @it.GetKey(); + v = @it.GetValue(); + body + } + } + */ + + PType * itType = NewMapIterator(keyType, valType); + auto it = new FxLocalVariableDeclaration(itType, "@it", nullptr, 0, ScriptPosition); + block->Add(it); + + FArgumentList al_map; + al_map.Push(MapExpr); + + block->Add(new FxMemberFunctionCall(new FxIdentifier("@it", ScriptPosition), "Init", std::move(al_map), ScriptPosition)); + + auto inner_block = new FxCompoundStatement(ScriptPosition); + + 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); + + block->Add(new FxWhileLoop(new FxMemberFunctionCall(new FxIdentifier("@it", ScriptPosition), "Next", {}, ScriptPosition), inner_block, ScriptPosition)); + + delete MapExpr2; + delete MapExpr3; + delete MapExpr4; + MapExpr = MapExpr2 = MapExpr3 = MapExpr4 = Code = nullptr; + delete this; + return block->Resolve(ctx); + } +} //========================================================================== // @@ -12288,8 +12412,7 @@ FxExpression *FxLocalVariableDeclaration::Resolve(FCompileContext &ctx) if (IsDynamicArray()) { auto stackVar = new FxStackVariable(ValueType, StackOffset, ScriptPosition); - FArgumentList argsList; - clearExpr = new FxMemberFunctionCall(stackVar, "Clear", argsList, ScriptPosition); + clearExpr = new FxMemberFunctionCall(stackVar, "Clear", {}, ScriptPosition); SAFE_RESOLVE(clearExpr, ctx); } @@ -12607,10 +12730,9 @@ FxExpression *FxLocalArrayDeclaration::Resolve(FCompileContext &ctx) else { FArgumentList argsList; - argsList.Clear(); argsList.Push(v); - FxExpression *funcCall = new FxMemberFunctionCall(stackVar, NAME_Push, argsList, (const FScriptPosition) v->ScriptPosition); + FxExpression *funcCall = new FxMemberFunctionCall(stackVar, NAME_Push, std::move(argsList), (const FScriptPosition) v->ScriptPosition); SAFE_RESOLVE(funcCall, ctx); v = funcCall; diff --git a/src/common/scripting/backend/codegen.h b/src/common/scripting/backend/codegen.h index 3f73a09c52..07b1ecac04 100644 --- a/src/common/scripting/backend/codegen.h +++ b/src/common/scripting/backend/codegen.h @@ -275,6 +275,7 @@ enum EFxType EFX_DoWhileLoop, EFX_ForLoop, EFX_ForEachLoop, + EFX_MapForEachLoop, EFX_JumpStatement, EFX_ReturnStatement, EFX_ClassTypeCast, @@ -1621,7 +1622,7 @@ public: FName MethodName; FArgumentList ArgList; - FxFunctionCall(FName methodname, FName rngname, FArgumentList &args, const FScriptPosition &pos); + FxFunctionCall(FName methodname, FName rngname, FArgumentList &&args, const FScriptPosition &pos); ~FxFunctionCall(); FxExpression *Resolve(FCompileContext&); }; @@ -1638,10 +1639,11 @@ class FxMemberFunctionCall : public FxExpression FxExpression *Self; FName MethodName; FArgumentList ArgList; + bool ResolveSelf; public: - FxMemberFunctionCall(FxExpression *self, FName methodname, FArgumentList &args, const FScriptPosition &pos); + FxMemberFunctionCall(FxExpression *self, FName methodname, FArgumentList &&args, const FScriptPosition &pos); ~FxMemberFunctionCall(); FxExpression *Resolve(FCompileContext&); }; @@ -2080,6 +2082,29 @@ public: FxExpression* DoResolve(FCompileContext&); }; +//========================================================================== +// +// FxMapForEachLoop +// +//========================================================================== + +class FxMapForEachLoop : public FxExpression +{ + FName keyVarName; + FName valueVarName; + FxExpression* MapExpr; + FxExpression* MapExpr2; + FxExpression* MapExpr3; + FxExpression* MapExpr4; + FxExpression* Code; + +public: + FxMapForEachLoop(FName kv, FName vv, FxExpression* mapexpr, FxExpression* mapexpr2, FxExpression* mapexpr3, FxExpression* mapexpr4, FxExpression* code, const FScriptPosition& pos); + ~FxMapForEachLoop(); + FxExpression *Resolve(FCompileContext&); + //ExpEmit Emit(VMFunctionBuilder *build); This node is transformed, so it won't ever be emitted itself +}; + //========================================================================== // // FxJumpStatement diff --git a/src/common/scripting/frontend/ast.cpp b/src/common/scripting/frontend/ast.cpp index 2841c16431..d190f9c5e0 100644 --- a/src/common/scripting/frontend/ast.cpp +++ b/src/common/scripting/frontend/ast.cpp @@ -983,6 +983,21 @@ static void PrintArrayIterationStmt(FLispString &out, const ZCC_TreeNode *node) out.Close(); } +static void PrintMapIterationStmt(FLispString &out, const ZCC_TreeNode *node) +{ + auto inode = (ZCC_MapIterationStmt *)node; + out.Break(); + out.Open("map-iteration-stmt"); + PrintVarName(out, inode->ItKey); + out.Break(); + PrintVarName(out, inode->ItValue); + out.Break(); + PrintNodes(out, inode->ItMap); + out.Break(); + PrintNodes(out, inode->LoopStatement); + out.Close(); +} + static const NodePrinterFunc TreeNodePrinter[] = { PrintIdentifier, @@ -1050,6 +1065,7 @@ static const NodePrinterFunc TreeNodePrinter[] = PrintMixinDef, PrintMixinStmt, PrintArrayIterationStmt, + PrintMapIterationStmt, }; FString ZCC_PrintAST(const ZCC_TreeNode *root) diff --git a/src/common/scripting/frontend/zcc-parse.lemon b/src/common/scripting/frontend/zcc-parse.lemon index 347fddcc1e..c708f325d9 100644 --- a/src/common/scripting/frontend/zcc-parse.lemon +++ b/src/common/scripting/frontend/zcc-parse.lemon @@ -1956,6 +1956,7 @@ statement(X) ::= expression_statement(A) SEMICOLON. { X = A; /*X-overwrites-A*/ statement(X) ::= selection_statement(X). statement(X) ::= iteration_statement(X). statement(X) ::= array_iteration_statement(X). +statement(X) ::= map_iteration_statement(X). statement(X) ::= jump_statement(X). statement(X) ::= assign_statement(A) SEMICOLON. { X = A; /*X-overwrites-A*/ } statement(X) ::= assign_decl_statement(A) SEMICOLON.{ X = A; /*X-overwrites-A*/ } @@ -2125,6 +2126,18 @@ array_iteration_statement(X) ::= FOREACH(T) LPAREN variable_name(IN) COLON expr( X = iter; } +%type map_iteration_statement{ZCC_Statement *} + +map_iteration_statement(X) ::= FOREACH(T) LPAREN variable_name(KEY) COMMA variable_name(VAL) COLON expr(EX) RPAREN statement(ST). +{ + NEW_AST_NODE(MapIterationStmt, iter, T); + iter->ItKey = KEY; + iter->ItValue = VAL; + iter->ItMap = EX; + iter->LoopStatement = ST; + X = iter; +} + while_or_until(X) ::= WHILE(T). { X.Int = ZCC_WHILE; diff --git a/src/common/scripting/frontend/zcc_compile.cpp b/src/common/scripting/frontend/zcc_compile.cpp index d7a1a54b74..08c9ec34e7 100644 --- a/src/common/scripting/frontend/zcc_compile.cpp +++ b/src/common/scripting/frontend/zcc_compile.cpp @@ -2999,12 +2999,12 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast, bool substitute) { case AST_ExprID: // The function name is a simple identifier. - return new FxFunctionCall(static_cast(fcall->Function)->Identifier, NAME_None, ConvertNodeList(args, fcall->Parameters), *ast); + return new FxFunctionCall(static_cast(fcall->Function)->Identifier, NAME_None, std::move(ConvertNodeList(args, fcall->Parameters)), *ast); case AST_ExprMemberAccess: { auto ema = static_cast(fcall->Function); - return new FxMemberFunctionCall(ConvertNode(ema->Left, true), ema->Right, ConvertNodeList(args, fcall->Parameters), *ast); + return new FxMemberFunctionCall(ConvertNode(ema->Left, true), ema->Right, std::move(ConvertNodeList(args, fcall->Parameters)), *ast); } case AST_ExprBinary: @@ -3014,7 +3014,7 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast, bool substitute) auto binary = static_cast(fcall->Function); if (binary->Left->NodeType == AST_ExprID && binary->Right->NodeType == AST_ExprID) { - return new FxFunctionCall(static_cast(binary->Left)->Identifier, static_cast(binary->Right)->Identifier, ConvertNodeList(args, fcall->Parameters), *ast); + return new FxFunctionCall(static_cast(binary->Left)->Identifier, static_cast(binary->Right)->Identifier, std::move(ConvertNodeList(args, fcall->Parameters)), *ast); } } // fall through if this isn't an array access node. @@ -3391,7 +3391,19 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast, bool substitute) FxExpression* const itArray2 = ConvertNode(iter->ItArray); // the handler needs two 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); + } + case AST_MapIterationStmt: + { + auto iter = static_cast(ast); + auto key = iter->ItKey->Name; + auto var = iter->ItValue->Name; + FxExpression* const itMap = ConvertNode(iter->ItMap); + FxExpression* const itMap2 = ConvertNode(iter->ItMap); + FxExpression* const itMap3 = ConvertNode(iter->ItMap); + FxExpression* const itMap4 = ConvertNode(iter->ItMap); + FxExpression* const body = ConvertImplicitScopeNode(ast, iter->LoopStatement); + return new FxMapForEachLoop(key, var, itMap, itMap2, itMap3, itMap4, body, *ast); } case AST_IterationStmt: diff --git a/src/common/scripting/frontend/zcc_parser.cpp b/src/common/scripting/frontend/zcc_parser.cpp index 24b2ecb1f3..02f56d232b 100644 --- a/src/common/scripting/frontend/zcc_parser.cpp +++ b/src/common/scripting/frontend/zcc_parser.cpp @@ -1177,6 +1177,19 @@ ZCC_TreeNode *TreeNodeDeepCopy_Internal(ZCC_AST *ast, ZCC_TreeNode *orig, bool c break; } + case AST_MapIterationStmt: + { + TreeNodeDeepCopy_Start(MapIterationStmt); + + // ZCC_MapIterationStmt + copy->ItKey = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ItKey, true, copiedNodesList)); + copy->ItValue = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ItValue, true, copiedNodesList)); + copy->LoopStatement = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->LoopStatement, true, copiedNodesList)); + copy->ItMap = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ItMap, true, copiedNodesList)); + + break; + } + case AST_IfStmt: { TreeNodeDeepCopy_Start(IfStmt); diff --git a/src/common/scripting/frontend/zcc_parser.h b/src/common/scripting/frontend/zcc_parser.h index c9c1a2933d..446d0f8b4d 100644 --- a/src/common/scripting/frontend/zcc_parser.h +++ b/src/common/scripting/frontend/zcc_parser.h @@ -145,6 +145,7 @@ enum EZCCTreeNodeType AST_MixinDef, AST_MixinStmt, AST_ArrayIterationStmt, + AST_MapIterationStmt, NUM_AST_NODE_TYPES }; @@ -532,6 +533,14 @@ struct ZCC_ArrayIterationStmt : ZCC_Statement ZCC_Statement* LoopStatement; }; +struct ZCC_MapIterationStmt : ZCC_Statement +{ + ZCC_VarName* ItKey; + ZCC_VarName* ItValue; + ZCC_Expression* ItMap; + ZCC_Statement* LoopStatement; +}; + struct ZCC_IfStmt : ZCC_Statement { ZCC_Expression *Condition; diff --git a/src/scripting/decorate/thingdef_exp.cpp b/src/scripting/decorate/thingdef_exp.cpp index 2658ebe184..1b27342be0 100644 --- a/src/scripting/decorate/thingdef_exp.cpp +++ b/src/scripting/decorate/thingdef_exp.cpp @@ -477,7 +477,7 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) { FArgumentList args; args.Push(exp); - exp = new FxFunctionCall(NAME_ResolveState, NAME_None, args, sc); + exp = new FxFunctionCall(NAME_ResolveState, NAME_None, std::move(args), sc); } sc.MustGetToken(')'); return exp; @@ -512,7 +512,7 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) ParseFunctionParameters(sc, cls, args, func, "", nullptr); } // FxVMFunctionCall cannot be used here as it lacks some important checks - return new FxFunctionCall(identifier, NAME_None, args, sc); + return new FxFunctionCall(identifier, NAME_None, std::move(args), sc); } } @@ -543,7 +543,7 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) while (sc.CheckToken(',')); sc.MustGetToken(')'); } - return new FxFunctionCall(identifier, NAME_None, args, sc); + return new FxFunctionCall(identifier, NAME_None, std::move(args), sc); } } else diff --git a/src/scripting/decorate/thingdef_states.cpp b/src/scripting/decorate/thingdef_states.cpp index 36a209d692..570905741a 100644 --- a/src/scripting/decorate/thingdef_states.cpp +++ b/src/scripting/decorate/thingdef_states.cpp @@ -578,7 +578,7 @@ FxExpression* ParseAction(FScanner &sc, FState state, FString statestring, Bagga { FArgumentList args; ParseFunctionParameters(sc, bag.Info, args, afd, statestring, &bag.statedef); - call = new FxFunctionCall(symname, NAME_None, args, sc); + call = new FxFunctionCall(symname, NAME_None, std::move(args), sc); return call; } sc.ScriptError("Invalid parameter '%s'\n", sc.String);