From 2d85efce2ad0ae4150223979e26997f8d11e3f87 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 19 Oct 2016 19:05:48 +0200 Subject: [PATCH] - added processing of compound statements to the compiler. This means that anonymous functions without control statements are generating code now. - added local variable declarations to the code generator. This is not tested yet, that will come with the next commit. --- src/scripting/codegeneration/codegen.cpp | 141 ++++++++++++++++++++++- src/scripting/codegeneration/codegen.h | 38 +++++- src/scripting/thingdef.h | 1 + src/scripting/zscript/zcc_compile.cpp | 57 ++++++++- 4 files changed, 232 insertions(+), 5 deletions(-) diff --git a/src/scripting/codegeneration/codegen.cpp b/src/scripting/codegeneration/codegen.cpp index 7a7ff7f13..d115cbbd8 100644 --- a/src/scripting/codegeneration/codegen.cpp +++ b/src/scripting/codegeneration/codegen.cpp @@ -5023,14 +5023,18 @@ ExpEmit FxFlopFunctionCall::Emit(VMFunctionBuilder *build) FxExpression *FxCompoundStatement::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); + Outer = ctx.Block; + ctx.Block = this; for (unsigned i = 0; i < Expressions.Size(); ++i) { if (NULL == (Expressions[i] = Expressions[i]->Resolve(ctx))) { + ctx.Block = Outer; delete this; - return NULL; + return nullptr; } } + ctx.Block = Outer; return this; } @@ -5048,6 +5052,11 @@ ExpEmit FxCompoundStatement::Emit(VMFunctionBuilder *build) // Throw away any result. We don't care about it. v.Free(build); } + // Release all local variables in this block. + for (auto l : LocalVars) + { + l->Release(build); + } return ExpEmit(); } @@ -5066,6 +5075,52 @@ VMFunction *FxCompoundStatement::GetDirectFunction() return NULL; } +//========================================================================== +// +// FxCompoundStatement :: FindLocalVariable +// +// Looks for a variable name in any of the containing compound statements +// +//========================================================================== + +FxLocalVariableDeclaration *FxCompoundStatement::FindLocalVariable(FName name) +{ + auto block = this; + while (block != nullptr) + { + for (auto l : block->LocalVars) + { + if (l->Name == name) + { + return l; + } + } + block = block->Outer; + } + return nullptr; +} + +//========================================================================== +// +// FxCompoundStatement :: CheckLocalVariable +// +// Checks if the current block already contains a local variable +// of the given name. +// +//========================================================================== + +bool FxCompoundStatement::CheckLocalVariable(FName name) +{ + for (auto l : LocalVars) + { + if (l->Name == name) + { + return true; + } + } + return false; +} + //========================================================================== // // FxIfStatement @@ -6142,3 +6197,87 @@ ExpEmit FxDamageValue::Emit(VMFunctionBuilder *build) return ExpEmit(); } + +//========================================================================== +// +// declares a single local variable (no arrays) +// +//========================================================================== + +FxLocalVariableDeclaration::FxLocalVariableDeclaration(PType *type, FName name, FxExpression *initval, const FScriptPosition &p) + :FxExpression(p) +{ + ValueType = type; + Name = name; + Init = initval == nullptr? nullptr : new FxTypeCast(initval, type, false); +} + +FxExpression *FxLocalVariableDeclaration::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE_OPT(Init, ctx); + if (ctx.Block == nullptr) + { + ScriptPosition.Message(MSG_ERROR, "Variable declaration outside compound statement"); + delete this; + return nullptr; + } + ctx.Block->LocalVars.Push(this); + return this; +} + +ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build) +{ + if (Init == nullptr) + { + RegNum = build->Registers[ValueType->GetRegType()].Get(1); + } + else + { + ExpEmit emitval = Init->Emit(build); + + int regtype = emitval.RegType; + if (regtype < REGT_INT || regtype > REGT_TYPE) + { + ScriptPosition.Message(MSG_ERROR, "Attempted to assign a non-value"); + return ExpEmit(); + } + if (emitval.Konst) + { + auto constval = static_cast(Init); + RegNum = build->Registers[regtype].Get(1); + switch (regtype) + { + default: + case REGT_INT: + build->Emit(OP_LK, build->GetConstantInt(constval->GetValue().GetInt()), RegNum); + break; + + case REGT_FLOAT: + build->Emit(OP_LKF, build->GetConstantFloat(constval->GetValue().GetFloat()), RegNum); + break; + + case REGT_POINTER: + build->Emit(OP_LKP, build->GetConstantAddress(constval->GetValue().GetPointer(), ATAG_GENERIC), RegNum); + break; + + case REGT_STRING: + build->Emit(OP_LKS, build->GetConstantString(constval->GetValue().GetString()), RegNum); + } + emitval.Free(build); + } + else + { + // take over the register that got allocated while emitting the Init expression. + RegNum = emitval.RegNum; + } + } + return ExpEmit(); +} + +void FxLocalVariableDeclaration::Release(VMFunctionBuilder *build) +{ + // Release the register after the containing block gets closed + assert(RegNum != -1); + build->Registers[ValueType->GetRegType()].Return(RegNum, 1); +} diff --git a/src/scripting/codegeneration/codegen.h b/src/scripting/codegeneration/codegen.h index 57a6d4257..75cdadf8d 100644 --- a/src/scripting/codegeneration/codegen.h +++ b/src/scripting/codegeneration/codegen.h @@ -63,10 +63,12 @@ class FxJumpStatement; //========================================================================== struct FScriptPosition; class FxLoopStatement; +class FxCompoundStatement; struct FCompileContext { - FxLoopStatement *Loop; + FxLoopStatement *Loop = nullptr; + FxCompoundStatement *Block = nullptr; PPrototype *ReturnProto; PFunction *Function; // The function that is currently being compiled (or nullptr for constant evaluation.) PClass *Class; // The type of the owning class. @@ -162,6 +164,12 @@ struct ExpVal return regtype == REGT_INT ? double(Int) : regtype == REGT_FLOAT ? Float : 0; } + void *GetPointer() const + { + int regtype = Type->GetRegType(); + return regtype == REGT_POINTER ? pointer : nullptr; + } + const FString GetString() const { return Type == TypeString ? *(FString *)&pointer : Type == TypeName ? FString(FName(ENamedName(Int)).GetChars()) : ""; @@ -1082,10 +1090,15 @@ public: // FxCompoundStatement // //========================================================================== +class FxLocalVariableDeclaration; class FxCompoundStatement : public FxExpression { + TArray LocalVars; TDeletingArray Expressions; + FxCompoundStatement *Outer = nullptr; + + friend class FxLocalVariableDeclaration; public: FxCompoundStatement(const FScriptPosition &pos) : FxExpression(pos) {} @@ -1093,6 +1106,8 @@ public: ExpEmit Emit(VMFunctionBuilder *build); void Add(FxExpression *expr) { if (expr != NULL) Expressions.Push(expr); } VMFunction *GetDirectFunction(); + FxLocalVariableDeclaration *FindLocalVariable(FName name); + bool CheckLocalVariable(FName name); }; //========================================================================== @@ -1341,7 +1356,26 @@ public: } }; -FxExpression *ParseExpression (FScanner &sc, PClassActor *cls, bool mustresolve = false); +//========================================================================== +// +// +// +//========================================================================== +class FxLocalVariableDeclaration : public FxExpression +{ + friend class FxCompoundStatement; + + FName Name; + FxExpression *Init; + int RegNum = -1; + +public: + FxLocalVariableDeclaration(PType *type, FName name, FxExpression *initval, const FScriptPosition &p); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); + void Release(VMFunctionBuilder *build); + +}; #endif diff --git a/src/scripting/thingdef.h b/src/scripting/thingdef.h index 7c2b90e50..bdee476ee 100644 --- a/src/scripting/thingdef.h +++ b/src/scripting/thingdef.h @@ -144,6 +144,7 @@ inline void ResetBaggage (Baggage *bag, PClassActor *stateclass) AFuncDesc *FindFunction(const char * string); +FxExpression *ParseExpression(FScanner &sc, PClassActor *cls, bool mustresolve = false); void ParseStates(FScanner &sc, PClassActor *actor, AActor *defaults, Baggage &bag); void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray &out_params, PFunction *afd, FString statestring, FStateDefinitions *statedef); diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index e86a7c959..e641524af 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -2313,8 +2313,27 @@ void ZCCCompiler::CompileStates() FxExpression *ZCCCompiler::ConvertAST(ZCC_TreeNode *ast) { - // FxReturnStatement will have to be done more intelligently, of course. - return new FxReturnStatement(ConvertNode(ast), *ast); + // there are two possibilities here: either a single function call or a compound statement. For a compound statement we also need to check if the last thing added was a return. + if (ast->NodeType == AST_ExprFuncCall) + { + return new FxReturnStatement(ConvertNode(ast), *ast); + } + else + { + // This must be done here so that we can check for a trailing return statement. + auto x = new FxCompoundStatement(*ast); + auto compound = static_cast(ast); + bool isreturn = false; + auto node = compound->Content; + if (node != nullptr) do + { + x->Add(ConvertNode(node)); + isreturn = node->NodeType == AST_ReturnStmt; + node = static_cast(node->SiblingNext); + } while (node != compound->Content); + if (!isreturn) x->Add(new FxReturnStatement(nullptr, *ast)); + return x; + } } FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast) @@ -2514,8 +2533,42 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast) return new FxConditional(condition, left, right); } + + case AST_ExpressionStmt: + return ConvertNode(static_cast(ast)->Expression); + + case AST_ReturnStmt: + { + auto ret = static_cast(ast); + FArgumentList *args = ConvertNodeList(ret->Values); + if (args->Size() == 0) + { + return new FxReturnStatement(nullptr, *ast); + } + else if (args->Size() == 1) + { + return new FxReturnStatement((*args)[0], *ast); + } + else + { + Error(ast, "Return with multiple values not implemented yet."); + return new FxReturnStatement(nullptr, *ast); + } } + case AST_CompoundStmt: + { + auto x = new FxCompoundStatement(*ast); + auto compound = static_cast(ast); + auto node = compound->Content; + if (node != nullptr) do + { + x->Add(ConvertNode(node)); + node = static_cast(node->SiblingNext); + } while (node != compound->Content); + return x; + } + } // only for development. I_Error is more convenient here than a normal error. I_Error("ConvertNode encountered unsupported node of type %d", ast->NodeType); return nullptr;