From 792e12adad4e373ee31d70fa8193a2c08683bb91 Mon Sep 17 00:00:00 2001 From: Nicolas Hake Date: Fri, 29 Apr 2016 11:30:10 +0200 Subject: [PATCH] C4AulCompiler: Split into separate file --- CMakeLists.txt | 2 + src/script/C4AulCompiler.cpp | 429 +++++++++++++++++++++++++++++++++++ src/script/C4AulCompiler.h | 68 ++++++ src/script/C4AulParse.cpp | 410 --------------------------------- src/script/C4AulParse.h | 43 +--- 5 files changed, 500 insertions(+), 452 deletions(-) create mode 100644 src/script/C4AulCompiler.cpp create mode 100644 src/script/C4AulCompiler.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cd12e0b7f..fad3bb4b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1058,6 +1058,8 @@ src/lib/C4Real.h src/lib/C4Random.cpp src/lib/C4Random.h src/script/C4Aul.cpp +src/script/C4AulCompiler.h +src/script/C4AulCompiler.cpp src/script/C4AulDefFunc.h src/script/C4AulExec.cpp src/script/C4AulExec.h diff --git a/src/script/C4AulCompiler.cpp b/src/script/C4AulCompiler.cpp new file mode 100644 index 000000000..99145b651 --- /dev/null +++ b/src/script/C4AulCompiler.cpp @@ -0,0 +1,429 @@ +/* +* OpenClonk, http://www.openclonk.org +* +* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ +* Copyright (c) 2009-2016, The OpenClonk Team and contributors +* +* Distributed under the terms of the ISC license; see accompanying file +* "COPYING" for details. +* +* "Clonk" is a registered trademark of Matthes Bender, used with permission. +* See accompanying file "TRADEMARK" for details. +* +* To redistribute this file separately, substitute the full license texts +* for the above references. +*/ + +#include "C4Include.h" +#include "script/C4AulCompiler.h" + +#include "script/C4Aul.h" +#include "script/C4AulScriptFunc.h" + +int C4AulCompiler::GetStackValue(C4AulBCCType eType, intptr_t X) +{ + switch (eType) + { + case AB_INT: + case AB_BOOL: + case AB_STRING: + case AB_CPROPLIST: + case AB_CARRAY: + case AB_CFUNCTION: + case AB_NIL: + case AB_LOCALN: + case AB_GLOBALN: + case AB_DUP: + case AB_DUP_CONTEXT: + case AB_THIS: + return 1; + + case AB_Pow: + case AB_Div: + case AB_Mul: + case AB_Mod: + case AB_Sub: + case AB_Sum: + case AB_LeftShift: + case AB_RightShift: + case AB_LessThan: + case AB_LessThanEqual: + case AB_GreaterThan: + case AB_GreaterThanEqual: + case AB_Equal: + case AB_NotEqual: + case AB_BitAnd: + case AB_BitXOr: + case AB_BitOr: + case AB_PROP_SET: + case AB_ARRAYA: + case AB_CONDN: + case AB_COND: + case AB_POP_TO: + case AB_RETURN: + // JUMPAND/JUMPOR/JUMPNNIL are special: They either jump over instructions adding one to the stack + // or decrement the stack. Thus, for stack counting purposes, they decrement. + case AB_JUMPAND: + case AB_JUMPOR: + case AB_JUMPNNIL: + return -1; + + case AB_FUNC: + return -reinterpret_cast(X)->GetParCount() + 1; + + case AB_CALL: + case AB_CALLFS: + return -C4AUL_MAX_Par; + + case AB_STACK_SET: + case AB_LOCALN_SET: + case AB_PROP: + case AB_GLOBALN_SET: + case AB_Inc: + case AB_Dec: + case AB_BitNot: + case AB_Not: + case AB_Neg: + case AB_PAR: + case AB_FOREACH_NEXT: + case AB_ERR: + case AB_EOFN: + case AB_JUMP: + case AB_DEBUG: + return 0; + + case AB_STACK: + return X; + + case AB_NEW_ARRAY: + return -X + 1; + + case AB_NEW_PROPLIST: + return -X * 2 + 1; + + case AB_ARRAYA_SET: + case AB_ARRAY_SLICE: + return -2; + + case AB_ARRAY_SLICE_SET: + return -3; + + default: + assert(false); + } + return 0; +} + +int C4AulCompiler::AddBCC(const char * TokenSPos, C4AulBCCType eType, intptr_t X) +{ + // Track stack size + iStack += GetStackValue(eType, X); + + // Use stack operation instead of 0-Any (enable optimization) + if (eType == AB_NIL) + { + eType = AB_STACK; + X = 1; + } + + // Join checks only if it's not a jump target + if (!fJump && Fn->GetLastCode()) + { + C4AulBCC *pCPos1 = Fn->GetLastCode(); + + // Skip noop stack operation + if (eType == AB_STACK && X == 0) + { + return Fn->GetCodePos() - 1; + } + + // Join together stack operations + if (eType == AB_STACK && pCPos1->bccType == AB_STACK && + (X <= 0 || pCPos1->Par.i >= 0)) + { + pCPos1->Par.i += X; + // Empty? Remove it. This relies on the parser not issuing + // multiple negative stack operations consecutively, as + // that could result in removing a jump target bytecode. + if (!pCPos1->Par.i) + Fn->RemoveLastBCC(); + return Fn->GetCodePos() - 1; + } + + // Prune unneeded Incs / Decs + if (eType == AB_STACK && X < 0 && (pCPos1->bccType == AB_Inc || pCPos1->bccType == AB_Dec)) + { + if (!pCPos1->Par.X) + { + pCPos1->bccType = eType; + pCPos1->Par.i = X; + return Fn->GetCodePos() - 1; + } + else + { + // If it was a result modifier, we can safely remove it knowing that it was neither + // the first chunk nor a jump target. We can therefore apply additional optimizations. + Fn->RemoveLastBCC(); + pCPos1--; + } + } + + // Join STACK_SET + STACK -1 to POP_TO (equivalent) + if (eType == AB_STACK && X == -1 && pCPos1->bccType == AB_STACK_SET) + { + pCPos1->bccType = AB_POP_TO; + return Fn->GetCodePos() - 1; + } + + // Join POP_TO + DUP to AB_STACK_SET if both target the same slot + if (eType == AB_DUP && pCPos1->bccType == AB_POP_TO && X == pCPos1->Par.i + 1) + { + pCPos1->bccType = AB_STACK_SET; + return Fn->GetCodePos() - 1; + } + + // Reduce some constructs like SUM + INT 1 to INC or DEC + if ((eType == AB_Sum || eType == AB_Sub) && + pCPos1->bccType == AB_INT && + (pCPos1->Par.i == 1 || pCPos1->Par.i == -1)) + { + if ((pCPos1->Par.i > 0) == (eType == AB_Sum)) + pCPos1->bccType = AB_Inc; + else + pCPos1->bccType = AB_Dec; + pCPos1->Par.i = X; + return Fn->GetCodePos() - 1; + } + + // Reduce Not + CONDN to COND, Not + COND to CONDN + if ((eType == AB_CONDN || eType == AB_COND) && pCPos1->bccType == AB_Not) + { + pCPos1->bccType = eType == AB_CONDN ? AB_COND : AB_CONDN; + pCPos1->Par.i = X + 1; + return Fn->GetCodePos() - 1; + } + } + + // Add + Fn->AddBCC(eType, X, TokenSPos); + + // Reset jump flag + fJump = false; + + return Fn->GetCodePos() - 1; +} + +void C4AulCompiler::RemoveLastBCC() +{ + // Security: This is unsafe on anything that might get optimized away + C4AulBCC *pBCC = Fn->GetLastCode(); + assert(pBCC->bccType != AB_STACK && pBCC->bccType != AB_STACK_SET && pBCC->bccType != AB_POP_TO); + // Correct stack + iStack -= GetStackValue(pBCC->bccType, pBCC->Par.X); + // Remove + Fn->RemoveLastBCC(); +} + +C4V_Type C4AulCompiler::GetLastRetType(C4AulScriptEngine * Engine, C4V_Type to) +{ + C4V_Type from; + switch (Fn->GetLastCode()->bccType) + { + case AB_INT: from = Config.Developer.ExtraWarnings || Fn->GetLastCode()->Par.i ? C4V_Int : C4V_Any; break; + case AB_STRING: from = C4V_String; break; + case AB_NEW_ARRAY: case AB_CARRAY: case AB_ARRAY_SLICE: from = C4V_Array; break; + case AB_CFUNCTION: from = C4V_Function; break; + case AB_NEW_PROPLIST: case AB_CPROPLIST: from = C4V_PropList; break; + case AB_BOOL: from = C4V_Bool; break; + case AB_FUNC: + from = Fn->GetLastCode()->Par.f->GetRetType(); break; + case AB_CALL: case AB_CALLFS: + { + C4String * pName = Fn->GetLastCode()->Par.s; + C4AulFunc * pFunc2 = Engine->GetFirstFunc(pName->GetCStr()); + bool allwarn = true; + from = C4V_Any; + while (pFunc2 && allwarn) + { + from = pFunc2->GetRetType(); + if (!C4Value::WarnAboutConversion(from, to)) + { + allwarn = false; + from = C4V_Any; + } + pFunc2 = Engine->GetNextSNFunc(pFunc2); + } + break; + } + case AB_Inc: case AB_Dec: case AB_BitNot: case AB_Neg: + case AB_Pow: case AB_Div: case AB_Mul: case AB_Mod: case AB_Sub: case AB_Sum: + case AB_LeftShift: case AB_RightShift: case AB_BitAnd: case AB_BitXOr: case AB_BitOr: + from = C4V_Int; break; + case AB_Not: case AB_LessThan: case AB_LessThanEqual: case AB_GreaterThan: case AB_GreaterThanEqual: + case AB_Equal: case AB_NotEqual: + from = C4V_Bool; break; + case AB_DUP: + { + int pos = Fn->GetLastCode()->Par.i + iStack - 2 + Fn->VarNamed.iSize + Fn->GetParCount(); + if (pos < Fn->GetParCount()) + from = Fn->GetParType()[pos]; + else + from = C4V_Any; + break; + } + default: + from = C4V_Any; break; + } + return from; +} + +C4AulBCC C4AulCompiler::MakeSetter(const char * SPos, bool fLeaveValue) +{ + C4AulBCC Value = *(Fn->GetLastCode()), Setter = Value; + // Check type + switch (Value.bccType) + { + case AB_ARRAYA: Setter.bccType = AB_ARRAYA_SET; break; + case AB_ARRAY_SLICE: Setter.bccType = AB_ARRAY_SLICE_SET; break; + case AB_DUP: + Setter.bccType = AB_STACK_SET; + // the setter additionally has the new value on the stack + --Setter.Par.i; + break; + case AB_STACK_SET: Setter.bccType = AB_STACK_SET; break; + case AB_LOCALN: + Setter.bccType = AB_LOCALN_SET; + break; + case AB_PROP: + Setter.bccType = AB_PROP_SET; + break; + case AB_GLOBALN: Setter.bccType = AB_GLOBALN_SET; break; + default: + throw C4AulParseError(Fn, SPos, "assignment to a constant"); + } + // If the new value is produced using the old one, the parameters to get the old one need to be duplicated. + // Otherwise, the setter can just use the parameters originally meant for the getter. + // All getters push one value, so the parameter count is one more than the values they pop from the stack. + int iParCount = 1 - GetStackValue(Value.bccType, Value.Par.X); + if (Value.bccType == AB_STACK_SET) + { + // STACK_SET has a side effect, so it can't be simply removed. + // Discard the unused value the usual way instead. + if (!fLeaveValue) + AddBCC(SPos, AB_STACK, -1); + // The original parameter isn't needed anymore, since in contrast to the other getters + // it does not indicate a position. + iParCount = 0; + } + else if (!fLeaveValue || iParCount) + { + RemoveLastBCC(); + fJump = true; // In case the original BCC was a jump target + } + if (fLeaveValue && iParCount) + { + for (int i = 0; i < iParCount; i++) + AddBCC(SPos, AB_DUP, 1 - iParCount); + // Finally re-add original BCC + AddBCC(SPos, Value.bccType, Value.Par.X); + } + // Done. The returned BCC should be added later once the value to be set was pushed on top. + assert(iParCount == -GetStackValue(Setter.bccType, Setter.Par.X)); + return Setter; +} + +int C4AulCompiler::JumpHere() +{ + // Set flag so the next generated code chunk won't get joined + fJump = true; + return Fn->GetCodePos(); +} + +static bool IsJump(C4AulBCCType t) +{ + return t == AB_JUMP || t == AB_JUMPAND || t == AB_JUMPOR || t == AB_JUMPNNIL || t == AB_CONDN || t == AB_COND; +} + +void C4AulCompiler::SetJumpHere(int iJumpOp) +{ + // Set target + C4AulBCC *pBCC = Fn->GetCodeByPos(iJumpOp); + assert(IsJump(pBCC->bccType)); + pBCC->Par.i = Fn->GetCodePos() - iJumpOp; + // Set flag so the next generated code chunk won't get joined + fJump = true; +} + +void C4AulCompiler::SetJump(int iJumpOp, int iWhere) +{ + // Set target + C4AulBCC *pBCC = Fn->GetCodeByPos(iJumpOp); + assert(IsJump(pBCC->bccType)); + pBCC->Par.i = iWhere - iJumpOp; +} + +void C4AulCompiler::AddJump(const char * SPos, C4AulBCCType eType, int iWhere) +{ + AddBCC(SPos, eType, iWhere - Fn->GetCodePos()); +} + +void C4AulCompiler::PushLoop() +{ + Loop *pNew = new Loop(); + pNew->StackSize = iStack; + pNew->Controls = NULL; + pNew->Next = pLoopStack; + pLoopStack = pNew; +} + +void C4AulCompiler::PopLoop(int ContinueJump) +{ + // Set targets for break/continue + for (Loop::Control *pCtrl = pLoopStack->Controls; pCtrl; pCtrl = pCtrl->Next) + if (pCtrl->Break) + SetJumpHere(pCtrl->Pos); + else + SetJump(pCtrl->Pos, ContinueJump); + // Delete loop controls + Loop *pLoop = pLoopStack; + while (pLoop->Controls) + { + // Unlink + Loop::Control *pCtrl = pLoop->Controls; + pLoop->Controls = pCtrl->Next; + // Delete + delete pCtrl; + } + // Unlink & delete + pLoopStack = pLoop->Next; + delete pLoop; +} + +void C4AulCompiler::AddLoopControl(const char * SPos, bool fBreak) +{ + // Insert code + if (pLoopStack->StackSize != iStack) + AddBCC(SPos, AB_STACK, pLoopStack->StackSize - iStack); + Loop::Control *pNew = new Loop::Control(); + pNew->Break = fBreak; + pNew->Pos = Fn->GetCodePos(); + pNew->Next = pLoopStack->Controls; + pLoopStack->Controls = pNew; + AddBCC(SPos, AB_JUMP); +} + +void C4AulCompiler::ErrorOut(const char * SPos, C4AulError & e) +{ + // make all jumps that don't have their destination yet jump here + for (unsigned int i = 0; i < Fn->Code.size(); i++) + { + C4AulBCC *pBCC = &Fn->Code[i]; + if (IsJump(pBCC->bccType)) + if (!pBCC->Par.i) + pBCC->Par.i = Fn->Code.size() - i; + } + // add an error chunk + const char * msg = e.what(); + if (SEqual2(msg, "ERROR: ")) msg += 7; + AddBCC(SPos, AB_ERR, reinterpret_cast(::Strings.RegString(msg))); +} diff --git a/src/script/C4AulCompiler.h b/src/script/C4AulCompiler.h new file mode 100644 index 000000000..983e2ed6d --- /dev/null +++ b/src/script/C4AulCompiler.h @@ -0,0 +1,68 @@ +/* +* OpenClonk, http://www.openclonk.org +* +* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ +* Copyright (c) 2009-2016, The OpenClonk Team and contributors +* +* Distributed under the terms of the ISC license; see accompanying file +* "COPYING" for details. +* +* "Clonk" is a registered trademark of Matthes Bender, used with permission. +* See accompanying file "TRADEMARK" for details. +* +* To redistribute this file separately, substitute the full license texts +* for the above references. +*/ + +#ifndef INC_C4AulCompiler +#define INC_C4AulCompiler + +#include "script/C4Value.h" + +enum C4AulBCCType : int; + +class C4AulCompiler +{ +public: + C4AulScriptFunc *Fn; + bool fJump = false; + int iStack = 0; + + int GetStackValue(C4AulBCCType eType, intptr_t X = 0); + int AddBCC(const char * SPos, C4AulBCCType eType, intptr_t X = 0); + void ErrorOut(const char * SPos, class C4AulError & e); + void RemoveLastBCC(); + C4V_Type GetLastRetType(C4AulScriptEngine * Engine, C4V_Type to); // for warning purposes + + C4AulBCC MakeSetter(const char * TokenSPos, bool fLeaveValue = false); // Prepares to generate a setter for the last value that was generated + + int JumpHere(); // Get position for a later jump to next instruction added + void SetJumpHere(int iJumpOp); // Use the next inserted instruction as jump target for the given jump operation + void SetJump(int iJumpOp, int iWhere); + void AddJump(const char * SPos, C4AulBCCType eType, int iWhere); + + // Keep track of loops and break/continue usages + struct Loop + { + struct Control + { + bool Break; + int Pos; + Control *Next; + }; + Control *Controls; + int StackSize; + Loop *Next; + }; + Loop *pLoopStack = NULL; + + void PushLoop(); + void PopLoop(int ContinueJump); + void AddLoopControl(const char * SPos, bool fBreak); + ~C4AulCompiler() + { + while (pLoopStack) PopLoop(0); + } +}; + +#endif diff --git a/src/script/C4AulParse.cpp b/src/script/C4AulParse.cpp index 52ddb9ce0..5dec1c545 100644 --- a/src/script/C4AulParse.cpp +++ b/src/script/C4AulParse.cpp @@ -739,427 +739,17 @@ bool C4ScriptHost::Preparse() return true; } - -int C4AulCompiler::GetStackValue(C4AulBCCType eType, intptr_t X) -{ - switch (eType) - { - case AB_INT: - case AB_BOOL: - case AB_STRING: - case AB_CPROPLIST: - case AB_CARRAY: - case AB_CFUNCTION: - case AB_NIL: - case AB_LOCALN: - case AB_GLOBALN: - case AB_DUP: - case AB_DUP_CONTEXT: - case AB_THIS: - return 1; - - case AB_Pow: - case AB_Div: - case AB_Mul: - case AB_Mod: - case AB_Sub: - case AB_Sum: - case AB_LeftShift: - case AB_RightShift: - case AB_LessThan: - case AB_LessThanEqual: - case AB_GreaterThan: - case AB_GreaterThanEqual: - case AB_Equal: - case AB_NotEqual: - case AB_BitAnd: - case AB_BitXOr: - case AB_BitOr: - case AB_PROP_SET: - case AB_ARRAYA: - case AB_CONDN: - case AB_COND: - case AB_POP_TO: - case AB_RETURN: - // JUMPAND/JUMPOR/JUMPNNIL are special: They either jump over instructions adding one to the stack - // or decrement the stack. Thus, for stack counting purposes, they decrement. - case AB_JUMPAND: - case AB_JUMPOR: - case AB_JUMPNNIL: - return -1; - - case AB_FUNC: - return -reinterpret_cast(X)->GetParCount() + 1; - - case AB_CALL: - case AB_CALLFS: - return -C4AUL_MAX_Par; - - case AB_STACK_SET: - case AB_LOCALN_SET: - case AB_PROP: - case AB_GLOBALN_SET: - case AB_Inc: - case AB_Dec: - case AB_BitNot: - case AB_Not: - case AB_Neg: - case AB_PAR: - case AB_FOREACH_NEXT: - case AB_ERR: - case AB_EOFN: - case AB_JUMP: - case AB_DEBUG: - return 0; - - case AB_STACK: - return X; - - case AB_NEW_ARRAY: - return -X+1; - - case AB_NEW_PROPLIST: - return -X * 2 + 1; - - case AB_ARRAYA_SET: - case AB_ARRAY_SLICE: - return -2; - - case AB_ARRAY_SLICE_SET: - return -3; - - default: - assert(false); - } - return 0; -} - void C4AulParse::DebugChunk() { if (C4AulDebug::GetDebugger()) AddBCC(AB_DEBUG); } -int C4AulCompiler::AddBCC(const char * TokenSPos, C4AulBCCType eType, intptr_t X) -{ - // Track stack size - iStack += GetStackValue(eType, X); - - // Use stack operation instead of 0-Any (enable optimization) - if (eType == AB_NIL) - { - eType = AB_STACK; - X = 1; - } - - // Join checks only if it's not a jump target - if (!fJump && Fn->GetLastCode()) - { - C4AulBCC *pCPos1 = Fn->GetLastCode(); - - // Skip noop stack operation - if(eType == AB_STACK && X == 0) - { - return Fn->GetCodePos() - 1; - } - - // Join together stack operations - if(eType == AB_STACK && pCPos1->bccType == AB_STACK && - (X <= 0 || pCPos1->Par.i >= 0)) - { - pCPos1->Par.i += X; - // Empty? Remove it. This relies on the parser not issuing - // multiple negative stack operations consecutively, as - // that could result in removing a jump target bytecode. - if (!pCPos1->Par.i) - Fn->RemoveLastBCC(); - return Fn->GetCodePos() - 1; - } - - // Prune unneeded Incs / Decs - if(eType == AB_STACK && X < 0 && (pCPos1->bccType == AB_Inc || pCPos1->bccType == AB_Dec)) - { - if(!pCPos1->Par.X) - { - pCPos1->bccType = eType; - pCPos1->Par.i = X; - return Fn->GetCodePos() - 1; - } - else - { - // If it was a result modifier, we can safely remove it knowing that it was neither - // the first chunk nor a jump target. We can therefore apply additional optimizations. - Fn->RemoveLastBCC(); - pCPos1--; - } - } - - // Join STACK_SET + STACK -1 to POP_TO (equivalent) - if(eType == AB_STACK && X == -1 && pCPos1->bccType == AB_STACK_SET) - { - pCPos1->bccType = AB_POP_TO; - return Fn->GetCodePos() - 1; - } - - // Join POP_TO + DUP to AB_STACK_SET if both target the same slot - if(eType == AB_DUP && pCPos1->bccType == AB_POP_TO && X == pCPos1->Par.i + 1) - { - pCPos1->bccType = AB_STACK_SET; - return Fn->GetCodePos() - 1; - } - - // Reduce some constructs like SUM + INT 1 to INC or DEC - if((eType == AB_Sum || eType == AB_Sub) && - pCPos1->bccType == AB_INT && - (pCPos1->Par.i == 1 || pCPos1->Par.i == -1)) - { - if((pCPos1->Par.i > 0) == (eType == AB_Sum)) - pCPos1->bccType = AB_Inc; - else - pCPos1->bccType = AB_Dec; - pCPos1->Par.i = X; - return Fn->GetCodePos() - 1; - } - - // Reduce Not + CONDN to COND, Not + COND to CONDN - if((eType == AB_CONDN || eType == AB_COND) && pCPos1->bccType == AB_Not) - { - pCPos1->bccType = eType == AB_CONDN ? AB_COND : AB_CONDN; - pCPos1->Par.i = X + 1; - return Fn->GetCodePos() - 1; - } - - } - - // Add - Fn->AddBCC(eType, X, TokenSPos); - - // Reset jump flag - fJump = false; - - return Fn->GetCodePos() - 1; -} - -void C4AulCompiler::RemoveLastBCC() -{ - // Security: This is unsafe on anything that might get optimized away - C4AulBCC *pBCC = Fn->GetLastCode(); - assert(pBCC->bccType != AB_STACK && pBCC->bccType != AB_STACK_SET && pBCC->bccType != AB_POP_TO); - // Correct stack - iStack -= GetStackValue(pBCC->bccType, pBCC->Par.X); - // Remove - Fn->RemoveLastBCC(); -} - -C4V_Type C4AulCompiler::GetLastRetType(C4AulScriptEngine * Engine, C4V_Type to) -{ - C4V_Type from; - switch (Fn->GetLastCode()->bccType) - { - case AB_INT: from = Config.Developer.ExtraWarnings || Fn->GetLastCode()->Par.i ? C4V_Int : C4V_Any; break; - case AB_STRING: from = C4V_String; break; - case AB_NEW_ARRAY: case AB_CARRAY: case AB_ARRAY_SLICE: from = C4V_Array; break; - case AB_CFUNCTION: from = C4V_Function; break; - case AB_NEW_PROPLIST: case AB_CPROPLIST: from = C4V_PropList; break; - case AB_BOOL: from = C4V_Bool; break; - case AB_FUNC: - from = Fn->GetLastCode()->Par.f->GetRetType(); break; - case AB_CALL: case AB_CALLFS: - { - C4String * pName = Fn->GetLastCode()->Par.s; - C4AulFunc * pFunc2 = Engine->GetFirstFunc(pName->GetCStr()); - bool allwarn = true; - from = C4V_Any; - while (pFunc2 && allwarn) - { - from = pFunc2->GetRetType(); - if (!C4Value::WarnAboutConversion(from, to)) - { - allwarn = false; - from = C4V_Any; - } - pFunc2 = Engine->GetNextSNFunc(pFunc2); - } - break; - } - case AB_Inc: case AB_Dec: case AB_BitNot: case AB_Neg: - case AB_Pow: case AB_Div: case AB_Mul: case AB_Mod: case AB_Sub: case AB_Sum: - case AB_LeftShift: case AB_RightShift: case AB_BitAnd: case AB_BitXOr: case AB_BitOr: - from = C4V_Int; break; - case AB_Not: case AB_LessThan: case AB_LessThanEqual: case AB_GreaterThan: case AB_GreaterThanEqual: - case AB_Equal: case AB_NotEqual: - from = C4V_Bool; break; - case AB_DUP: - { - int pos = Fn->GetLastCode()->Par.i + iStack - 2 + Fn->VarNamed.iSize + Fn->GetParCount(); - if (pos < Fn->GetParCount()) - from = Fn->GetParType()[pos]; - else - from = C4V_Any; - break; - } - default: - from = C4V_Any; break; - } - return from; -} - -C4AulBCC C4AulCompiler::MakeSetter(const char * SPos, bool fLeaveValue) -{ - C4AulBCC Value = *(Fn->GetLastCode()), Setter = Value; - // Check type - switch (Value.bccType) - { - case AB_ARRAYA: Setter.bccType = AB_ARRAYA_SET; break; - case AB_ARRAY_SLICE: Setter.bccType = AB_ARRAY_SLICE_SET; break; - case AB_DUP: - Setter.bccType = AB_STACK_SET; - // the setter additionally has the new value on the stack - --Setter.Par.i; - break; - case AB_STACK_SET: Setter.bccType = AB_STACK_SET; break; - case AB_LOCALN: - Setter.bccType = AB_LOCALN_SET; - break; - case AB_PROP: - Setter.bccType = AB_PROP_SET; - break; - case AB_GLOBALN: Setter.bccType = AB_GLOBALN_SET; break; - default: - throw C4AulParseError(Fn, SPos, "assignment to a constant"); - } - // If the new value is produced using the old one, the parameters to get the old one need to be duplicated. - // Otherwise, the setter can just use the parameters originally meant for the getter. - // All getters push one value, so the parameter count is one more than the values they pop from the stack. - int iParCount = 1 - GetStackValue(Value.bccType, Value.Par.X); - if (Value.bccType == AB_STACK_SET) - { - // STACK_SET has a side effect, so it can't be simply removed. - // Discard the unused value the usual way instead. - if (!fLeaveValue) - AddBCC(SPos, AB_STACK, -1); - // The original parameter isn't needed anymore, since in contrast to the other getters - // it does not indicate a position. - iParCount = 0; - } - else if (!fLeaveValue || iParCount) - { - RemoveLastBCC(); - fJump = true; // In case the original BCC was a jump target - } - if (fLeaveValue && iParCount) - { - for(int i = 0; i < iParCount; i++) - AddBCC(SPos, AB_DUP, 1 - iParCount); - // Finally re-add original BCC - AddBCC(SPos, Value.bccType, Value.Par.X); - } - // Done. The returned BCC should be added later once the value to be set was pushed on top. - assert(iParCount == -GetStackValue(Setter.bccType, Setter.Par.X)); - return Setter; -} - int C4AulParse::AddVarAccess(C4AulBCCType eType, intptr_t varnum) { return AddBCC(eType, 1 + varnum - (codegen.iStack + Fn->VarNamed.iSize)); } -int C4AulCompiler::JumpHere() -{ - // Set flag so the next generated code chunk won't get joined - fJump = true; - return Fn->GetCodePos(); -} - -static bool IsJump(C4AulBCCType t) -{ - return t == AB_JUMP || t == AB_JUMPAND || t == AB_JUMPOR || t == AB_JUMPNNIL || t == AB_CONDN || t == AB_COND; -} - -void C4AulCompiler::SetJumpHere(int iJumpOp) -{ - // Set target - C4AulBCC *pBCC = Fn->GetCodeByPos(iJumpOp); - assert(IsJump(pBCC->bccType)); - pBCC->Par.i = Fn->GetCodePos() - iJumpOp; - // Set flag so the next generated code chunk won't get joined - fJump = true; -} - -void C4AulCompiler::SetJump(int iJumpOp, int iWhere) -{ - // Set target - C4AulBCC *pBCC = Fn->GetCodeByPos(iJumpOp); - assert(IsJump(pBCC->bccType)); - pBCC->Par.i = iWhere - iJumpOp; -} - -void C4AulCompiler::AddJump(const char * SPos, C4AulBCCType eType, int iWhere) -{ - AddBCC(SPos, eType, iWhere - Fn->GetCodePos()); -} - -void C4AulCompiler::PushLoop() -{ - Loop *pNew = new Loop(); - pNew->StackSize = iStack; - pNew->Controls = NULL; - pNew->Next = pLoopStack; - pLoopStack = pNew; -} - -void C4AulCompiler::PopLoop(int ContinueJump) -{ - // Set targets for break/continue - for (Loop::Control *pCtrl = pLoopStack->Controls; pCtrl; pCtrl = pCtrl->Next) - if (pCtrl->Break) - SetJumpHere(pCtrl->Pos); - else - SetJump(pCtrl->Pos, ContinueJump); - // Delete loop controls - Loop *pLoop = pLoopStack; - while (pLoop->Controls) - { - // Unlink - Loop::Control *pCtrl = pLoop->Controls; - pLoop->Controls = pCtrl->Next; - // Delete - delete pCtrl; - } - // Unlink & delete - pLoopStack = pLoop->Next; - delete pLoop; -} - -void C4AulCompiler::AddLoopControl(const char * SPos, bool fBreak) -{ - // Insert code - if (pLoopStack->StackSize != iStack) - AddBCC(SPos, AB_STACK, pLoopStack->StackSize - iStack); - Loop::Control *pNew = new Loop::Control(); - pNew->Break = fBreak; - pNew->Pos = Fn->GetCodePos(); - pNew->Next = pLoopStack->Controls; - pLoopStack->Controls = pNew; - AddBCC(SPos, AB_JUMP); -} - -void C4AulCompiler::ErrorOut(const char * SPos, C4AulError & e) -{ - // make all jumps that don't have their destination yet jump here - for (unsigned int i = 0; i < Fn->Code.size(); i++) - { - C4AulBCC *pBCC = &Fn->Code[i]; - if (IsJump(pBCC->bccType)) - if (!pBCC->Par.i) - pBCC->Par.i = Fn->Code.size() - i; - } - // add an error chunk - const char * msg = e.what(); - if (SEqual2(msg, "ERROR: ")) msg += 7; - AddBCC(SPos, AB_ERR, reinterpret_cast(::Strings.RegString(msg))); -} - const char * C4AulParse::GetTokenName(C4AulTokenType TokenType) { switch (TokenType) diff --git a/src/script/C4AulParse.h b/src/script/C4AulParse.h index 45cfe154c..34a641f11 100644 --- a/src/script/C4AulParse.h +++ b/src/script/C4AulParse.h @@ -18,53 +18,12 @@ #define INC_C4AulParse #include "script/C4Aul.h" +#include "script/C4AulCompiler.h" #include "script/C4AulScriptFunc.h" enum C4AulBCCType : int; enum C4AulTokenType : int; -class C4AulCompiler -{ -public: - C4AulScriptFunc *Fn; - bool fJump = false; - int iStack = 0; - - int GetStackValue(C4AulBCCType eType, intptr_t X = 0); - int AddBCC(const char * SPos, C4AulBCCType eType, intptr_t X = 0); - void ErrorOut(const char * SPos, C4AulError & e); - void RemoveLastBCC(); - C4V_Type GetLastRetType(C4AulScriptEngine * Engine, C4V_Type to); // for warning purposes - - C4AulBCC MakeSetter(const char * TokenSPos, bool fLeaveValue = false); // Prepares to generate a setter for the last value that was generated - - int JumpHere(); // Get position for a later jump to next instruction added - void SetJumpHere(int iJumpOp); // Use the next inserted instruction as jump target for the given jump operation - void SetJump(int iJumpOp, int iWhere); - void AddJump(const char * SPos, C4AulBCCType eType, int iWhere); - - // Keep track of loops and break/continue usages - struct Loop - { - struct Control - { - bool Break; - int Pos; - Control *Next; - }; - Control *Controls; - int StackSize; - Loop *Next; - }; - Loop *pLoopStack = NULL; - - void PushLoop(); - void PopLoop(int ContinueJump); - void AddLoopControl(const char * SPos, bool fBreak); - ~C4AulCompiler() - { while (pLoopStack) PopLoop(0); } -}; - struct C4ScriptOpDef { unsigned short Priority;