C4AulCompiler: Split into separate file

directional-lights
Nicolas Hake 2016-04-29 11:30:10 +02:00
parent c8d71d47c0
commit 792e12adad
5 changed files with 500 additions and 452 deletions

View File

@ -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

View File

@ -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<C4AulFunc *>(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<intptr_t>(::Strings.RegString(msg)));
}

View File

@ -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

View File

@ -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<C4AulFunc *>(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<intptr_t>(::Strings.RegString(msg)));
}
const char * C4AulParse::GetTokenName(C4AulTokenType TokenType)
{
switch (TokenType)

View File

@ -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;