forked from Mirrors/openclonk
C4AulCompiler: Split into separate file
parent
c8d71d47c0
commit
792e12adad
|
@ -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
|
||||
|
|
|
@ -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)));
|
||||
}
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue