diff --git a/src/common/engine/namedef.h b/src/common/engine/namedef.h index f06759b3760..27586711bcf 100644 --- a/src/common/engine/namedef.h +++ b/src/common/engine/namedef.h @@ -136,6 +136,8 @@ xx(FQuat) xx(let) xx(BlockThingsIterator) xx(BlockLinesIterator) +xx(ActorIterator) +xx(ThinkerIterator) xx(Min) xx(Max) diff --git a/src/common/scripting/backend/codegen.cpp b/src/common/scripting/backend/codegen.cpp index 495b6adea67..269ea5dca2d 100644 --- a/src/common/scripting/backend/codegen.cpp +++ b/src/common/scripting/backend/codegen.cpp @@ -11210,6 +11210,16 @@ FxExpression* FxForEachLoop::DoResolve(FCompileContext& ctx) delete this; return blockIt->Resolve(ctx); } + else if(Array->ValueType->isObjectPointer() && (((PObjectPointer*)Array->ValueType)->PointedClass()->TypeName == NAME_ActorIterator || ((PObjectPointer*)Array->ValueType)->PointedClass()->TypeName == NAME_ThinkerIterator)) + { + auto castIt = new FxCastForEachLoop(NAME_None, loopVarName, Array, Code, ScriptPosition); + delete Array2; + delete Array3; + delete Array4; + Array = Array2 = Array3 = Array4 = Code = nullptr; + delete this; + return castIt->Resolve(ctx); + } // Instead of writing a new code generator for this, convert this into // @@ -11522,6 +11532,112 @@ FxExpression *FxBlockIteratorForEachLoop::Resolve(FCompileContext &ctx) return block->Resolve(ctx); } +//========================================================================== +// +// FxCastForEachLoop +// +//========================================================================== + +FxCastForEachLoop::FxCastForEachLoop(FName cv, FName vv, FxExpression* castiteartorexpr, FxExpression* code, const FScriptPosition& pos) + : FxExpression(EFX_BlockForEachLoop, pos), castClassName(cv), varVarName(vv), CastIteratorExpr(castiteartorexpr), Code(code) +{ + ValueType = TypeVoid; + if (CastIteratorExpr != nullptr) CastIteratorExpr->NeedResult = false; + if (Code != nullptr) Code->NeedResult = false; +} + +FxCastForEachLoop::~FxCastForEachLoop() +{ + SAFE_DELETE(CastIteratorExpr); + SAFE_DELETE(Code); +} + +FxExpression *FxCastForEachLoop::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(CastIteratorExpr, ctx); + + if(!(CastIteratorExpr->ValueType->isObjectPointer())) + { + ScriptPosition.Message(MSG_ERROR, "foreach(Type var : it ) - 'it' must be an actor or thinker iterator, but is a %s",CastIteratorExpr->ValueType->DescriptiveName()); + delete this; + return nullptr; + } + else if(varVarName == NAME_None) + { + ScriptPosition.Message(MSG_ERROR, "missing var for foreach(Type var : it )"); + delete this; + return nullptr; + } + + PType * varType = nullptr; + PClass * itType = ((PObjectPointer*)CastIteratorExpr->ValueType)->PointedClass(); + + FName fieldName = NAME_None; + + if(itType->TypeName == NAME_ActorIterator) + { + fieldName = "Actor"; + } + else if(itType->TypeName == NAME_ThinkerIterator) + { + fieldName = "Thinker"; + } + else + { + ScriptPosition.Message(MSG_ERROR, "foreach(Type var : it ) - 'it' must be an actor or thinker iterator, but is a %s",CastIteratorExpr->ValueType->DescriptiveName()); + delete this; + return nullptr; + } + + if(castClassName != NAME_None) + { + fieldName = castClassName; + } + + PClass * varTypeClass = PClass::FindClass(fieldName); + varType = varTypeClass->VMType; + + if(!varType) + { + ScriptPosition.Message(MSG_ERROR, "foreach(Type var : it ) - could not find class '%s'",castClassName.GetChars()); + delete this; + return nullptr; + } + + varType = NewPointer(varType, false); + + /* + { + CastType var; + ActorIterator|ThinkerIterator @it = expr; + while(var = CastType(@it.Next())) + body + } + */ + + auto block = new FxCompoundStatement(ScriptPosition); + + block->Add(new FxLocalVariableDeclaration(varType, varVarName, nullptr, 0, ScriptPosition)); + + block->Add(new FxLocalVariableDeclaration(CastIteratorExpr->ValueType, "@it", CastIteratorExpr, 0, ScriptPosition)); + + auto inner_block = new FxCompoundStatement(ScriptPosition); + + FxExpression * nextCallCast = new FxMemberFunctionCall(new FxIdentifier("@it", ScriptPosition), "Next", {}, ScriptPosition); + + if(castClassName != NAME_None) + { + nextCallCast = new FxDynamicCast(varTypeClass, nextCallCast); + } + + block->Add(new FxWhileLoop(new FxAssign(new FxIdentifier(varVarName, ScriptPosition), nextCallCast), Code, ScriptPosition)); + + CastIteratorExpr = Code = nullptr; + delete this; + return block->Resolve(ctx); +} + //========================================================================== // // FxJumpStatement diff --git a/src/common/scripting/backend/codegen.h b/src/common/scripting/backend/codegen.h index 4557600c04e..75e771f1f87 100644 --- a/src/common/scripting/backend/codegen.h +++ b/src/common/scripting/backend/codegen.h @@ -275,6 +275,7 @@ enum EFxType EFX_ForEachLoop, EFX_MapForEachLoop, EFX_BlockForEachLoop, + EFX_CastForEachLoop, EFX_JumpStatement, EFX_ReturnStatement, EFX_ClassTypeCast, @@ -2107,6 +2108,26 @@ class FxBlockIteratorForEachLoop : public FxExpression //ExpEmit Emit(VMFunctionBuilder *build); This node is transformed, so it won't ever be emitted itself }; +//========================================================================== +// +// FxCastForEachLoop +// +//========================================================================== + +class FxCastForEachLoop : public FxExpression +{ + FName castClassName; + FName varVarName; + FxExpression* CastIteratorExpr; + FxExpression* Code; + +public: + FxCastForEachLoop(FName cv, FName vv, FxExpression* castiteartorexpr, FxExpression* code, const FScriptPosition& pos); + ~FxCastForEachLoop(); + 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 1c947cf4605..1fc73905174 100644 --- a/src/common/scripting/frontend/ast.cpp +++ b/src/common/scripting/frontend/ast.cpp @@ -1015,6 +1015,21 @@ static void PrintBlockIterationStmt(FLispString &out, const ZCC_TreeNode *node) out.Close(); } +static void PrintCastIterationStmt(FLispString &out, const ZCC_TreeNode *node) +{ + auto inode = (ZCC_CastIterationStmt *)node; + out.Break(); + out.Open("cast-iteration-stmt"); + PrintVarName(out, inode->ItCast); + out.Break(); + PrintVarName(out, inode->ItVar); + out.Break(); + PrintNodes(out, inode->ItIterator); + out.Break(); + PrintNodes(out, inode->LoopStatement); + out.Close(); +} + static const NodePrinterFunc TreeNodePrinter[] = { PrintIdentifier, @@ -1084,6 +1099,7 @@ static const NodePrinterFunc TreeNodePrinter[] = PrintArrayIterationStmt, PrintMapIterationStmt, PrintBlockIterationStmt, + PrintCastIterationStmt, }; 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 1dcdd1f72f6..25e47f63b62 100644 --- a/src/common/scripting/frontend/zcc-parse.lemon +++ b/src/common/scripting/frontend/zcc-parse.lemon @@ -1958,6 +1958,7 @@ statement(X) ::= iteration_statement(X). statement(X) ::= array_iteration_statement(X). statement(X) ::= map_iteration_statement(X). statement(X) ::= block_iteration_statement(X). +statement(X) ::= cast_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*/ } @@ -2152,6 +2153,18 @@ block_iteration_statement(X) ::= FOREACH(T) LPAREN variable_name(VAR) COMMA vari X = iter; } +%type cast_iteration_statement{ZCC_Statement *} + +cast_iteration_statement(X) ::= FOREACH(T) LPAREN variable_name(CAST) variable_name(VAR) COLON expr(EX) RPAREN statement(ST). +{ + NEW_AST_NODE(CastIterationStmt, iter, T); + iter->ItCast = CAST; + iter->ItVar = VAR; + iter->ItIterator = 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 b8042854819..5aae8f58362 100644 --- a/src/common/scripting/frontend/zcc_compile.cpp +++ b/src/common/scripting/frontend/zcc_compile.cpp @@ -3413,6 +3413,16 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast, bool substitute) return new FxBlockIteratorForEachLoop(var, pos, flags, itBlock, body, *ast); } + case AST_CastIterationStmt: + { + auto iter = static_cast(ast); + auto cls = iter->ItCast->Name; + auto var = iter->ItVar->Name; + FxExpression* const itIterator = ConvertNode(iter->ItIterator); + FxExpression* const body = ConvertImplicitScopeNode(ast, iter->LoopStatement); + return new FxCastForEachLoop(cls, var, itIterator, body, *ast); + } + case AST_IterationStmt: { auto iter = static_cast(ast); diff --git a/src/common/scripting/frontend/zcc_parser.h b/src/common/scripting/frontend/zcc_parser.h index 0cdbb381c55..faf1e3597aa 100644 --- a/src/common/scripting/frontend/zcc_parser.h +++ b/src/common/scripting/frontend/zcc_parser.h @@ -147,6 +147,7 @@ enum EZCCTreeNodeType AST_ArrayIterationStmt, AST_MapIterationStmt, AST_BlockIterationStmt, + AST_CastIterationStmt, NUM_AST_NODE_TYPES }; @@ -551,6 +552,14 @@ struct ZCC_BlockIterationStmt : ZCC_Statement ZCC_Statement* LoopStatement; }; +struct ZCC_CastIterationStmt : ZCC_Statement +{ + ZCC_VarName* ItCast; + ZCC_VarName* ItVar; + ZCC_Expression* ItIterator; + ZCC_Statement* LoopStatement; +}; + struct ZCC_IfStmt : ZCC_Statement { ZCC_Expression *Condition;