Merge branch script

liquid_container
Günther Brammer 2016-03-01 03:04:17 +01:00
commit b54ddc7663
31 changed files with 379 additions and 488 deletions

View File

@ -467,20 +467,14 @@ set(OC_CLONK_SOURCES
src/game/C4FullScreen.h
src/game/C4Game.cpp
src/game/C4Game.h
src/game/C4GameScript.cpp
src/game/C4GameScript.h
src/game/C4GameVersion.h
src/game/C4GraphicsSystem.cpp
src/game/C4GraphicsSystem.h
src/game/C4Physics.h
src/game/C4Viewport.cpp
src/game/C4Viewport.h
src/gamescript/C4Effect.cpp
src/gamescript/C4Effect.h
src/gamescript/C4FindObject.cpp
src/gamescript/C4FindObject.h
src/gamescript/C4GameScript.cpp
src/gamescript/C4GameScript.h
src/gamescript/C4TransferZone.cpp
src/gamescript/C4TransferZone.h
src/graphics/Bitmap256.cpp
src/graphics/Bitmap256.h
src/graphics/C4Draw.cpp
@ -627,6 +621,8 @@ set(OC_CLONK_SOURCES
src/landscape/C4Texture.h
src/landscape/C4TextureShape.cpp
src/landscape/C4TextureShape.h
src/landscape/C4TransferZone.cpp
src/landscape/C4TransferZone.h
src/landscape/C4Weather.cpp
src/landscape/C4Weather.h
src/lib/C4LogBuf.cpp
@ -697,6 +693,8 @@ set(OC_CLONK_SOURCES
src/object/C4Def.h
src/object/C4DefList.cpp
src/object/C4DefList.h
src/object/C4FindObject.cpp
src/object/C4FindObject.h
src/object/C4GameObjects.cpp
src/object/C4GameObjects.h
src/object/C4Id.cpp
@ -1057,6 +1055,8 @@ src/script/C4AulLink.cpp
src/script/C4AulParse.cpp
src/script/C4AulScriptFunc.cpp
src/script/C4AulScriptFunc.h
src/script/C4Effect.cpp
src/script/C4Effect.h
src/script/C4PropList.cpp
src/script/C4PropList.h
src/script/C4Script.cpp

View File

@ -28,7 +28,6 @@ class C4AulDefFunc;
class C4AulExec;
class C4AulFunc;
struct C4AulParSet;
class C4AulScript;
struct C4AulScriptContext;
class C4AulScriptEngine;
class C4AulScriptFunc;

View File

@ -356,7 +356,7 @@ C4Value C4GameObjects::GRBroadcast(const char *szFunction, C4AulParSet *pPars, b
for (C4Object *pObj : *this)
if (pObj && (pObj->Category & (C4D_Goal | C4D_Rule | C4D_Environment)) && pObj->Status)
{
C4Value vResult = pObj->Call(szFunction, pPars/*, fPassError*/);
C4Value vResult = pObj->Call(szFunction, pPars, fPassError);
// rejection tests abort on first nonzero result
if (fRejectTest && !!vResult) return vResult;
}

View File

@ -41,86 +41,12 @@ const char *C4AulError::what() const noexcept
return sMessage ? sMessage.getData() : "(unknown error)";
}
C4AulScript::C4AulScript()
{
// not compiled
State = ASS_NONE;
// prepare lists
Prev = Next = NULL;
Engine = NULL;
}
C4AulScript::~C4AulScript()
{
// clear
Clear();
// unreg
Unreg();
}
void C4AulScript::Unreg()
{
// remove from list
if (Prev) Prev->Next = Next; else if (Engine) Engine->Child0 = Next;
if (Next) Next->Prev = Prev; else if (Engine) Engine->ChildL = Prev;
Prev = Next = NULL;
Engine = NULL;
}
void C4AulScript::Clear()
{
// reset flags
State = ASS_NONE;
}
void C4AulScript::Reg2List(C4AulScriptEngine *pEngine)
{
// already regged? (def reloaded)
if (Engine) return;
// reg to list
if ((Engine = pEngine))
{
if ((Prev = Engine->ChildL))
Prev->Next = this;
else
Engine->Child0 = this;
Engine->ChildL = this;
}
else
Prev = NULL;
Next = NULL;
}
std::string C4AulScript::Translate(const std::string &text) const
{
try
{
if (stringTable)
return stringTable->Translate(text);
}
catch (C4LangStringTable::NoSuchTranslation &)
{
// Ignore, soldier on
}
if (Engine && Engine != this)
return Engine->Translate(text);
throw C4LangStringTable::NoSuchTranslation(text);
}
/*--- C4AulScriptEngine ---*/
C4AulScriptEngine::C4AulScriptEngine():
GlobalPropList(C4PropList::NewStatic(NULL, NULL, ::Strings.RegString("Global"))),
C4PropListStaticMember(NULL, NULL, ::Strings.RegString("Global")),
warnCnt(0), errCnt(0), lineCnt(0)
{
// /me r b engine
Engine = this;
ScriptName.Ref(C4CFN_System);
GlobalNamedNames.Reset();
GlobalNamed.Reset();
GlobalNamed.SetNameList(&GlobalNamedNames);
@ -128,12 +54,7 @@ C4AulScriptEngine::C4AulScriptEngine():
GlobalConsts.Reset();
GlobalConsts.SetNameList(&GlobalConstNames);
Child0 = ChildL = NULL;
RegisterGlobalConstant("Global", GlobalPropList);
}
C4PropListStatic * C4AulScriptEngine::GetPropList()
{
return GlobalPropList._getPropList()->IsStatic();
RegisterGlobalConstant("Global", C4VPropList(this));
}
C4AulScriptEngine::~C4AulScriptEngine()
@ -151,9 +72,7 @@ void C4AulScriptEngine::Clear()
if (Child0->Delete()) delete Child0;
else Child0->Unreg();
// clear own stuff
GlobalPropList._getPropList()->Clear();
// clear inherited
C4AulScript::Clear();
C4PropListStaticMember::Clear();
// reset values
warnCnt = errCnt = lineCnt = 0;
// resetting name lists will reset all data lists, too
@ -162,7 +81,7 @@ void C4AulScriptEngine::Clear()
GlobalConstNames.Reset();
GlobalConsts.Reset();
GlobalConsts.SetNameList(&GlobalConstNames);
RegisterGlobalConstant("Global", GlobalPropList);
RegisterGlobalConstant("Global", C4VPropList(this));
GlobalNamed.Reset();
GlobalNamed.SetNameList(&GlobalNamedNames);
UserFiles.clear();
@ -190,12 +109,12 @@ bool C4AulScriptEngine::GetGlobalConstant(const char *szName, C4Value *pTargetVa
return true;
}
bool C4AulScriptEngine::Denumerate(C4ValueNumbers * numbers)
void C4AulScriptEngine::Denumerate(C4ValueNumbers * numbers)
{
GlobalNamed.Denumerate(numbers);
// runtime data only: don't denumerate consts
GameScript.ScenPropList.Denumerate(numbers);
return true;
C4PropListStaticMember::Denumerate(numbers);
}
void C4AulScriptEngine::CompileFunc(StdCompiler *pComp, C4ValueNumbers * numbers)
@ -291,11 +210,11 @@ unsigned int C4AulFuncMap::Hash(const char * name)
return h;
}
C4AulFunc * C4AulFuncMap::GetFirstFunc(C4String * Name)
C4AulFunc * C4AulFuncMap::GetFirstFunc(const char * Name)
{
if (!Name) return NULL;
C4AulFunc * Func = Funcs[Hash(Name->GetCStr()) % HashSize];
while (Func && Name->GetCStr() != Func->GetName())
C4AulFunc * Func = Funcs[Hash(Name) % HashSize];
while (Func && !SEqual(Name, Func->GetName()))
Func = Func->MapNext;
return Func;
}

View File

@ -45,7 +45,7 @@ public:
class C4AulParseError : public C4AulError
{
public:
C4AulParseError(C4AulScript *pScript, const char *pMsg, const char *pIdtf = NULL, bool Warn = false); // constructor
C4AulParseError(C4ScriptHost *pScript, const char *pMsg, const char *pIdtf = NULL, bool Warn = false); // constructor
C4AulParseError(class C4AulParse * state, const char *pMsg, const char *pIdtf = NULL, bool Warn = false); // constructor
};
@ -61,7 +61,7 @@ class C4AulFuncMap
public:
C4AulFuncMap();
~C4AulFuncMap();
C4AulFunc * GetFirstFunc(C4String * Name);
C4AulFunc * GetFirstFunc(const char * Name);
C4AulFunc * GetNextSNFunc(const C4AulFunc * After);
private:
enum { HashSize = 1025 };
@ -75,44 +75,6 @@ protected:
friend class C4ScriptHost;
};
// aul script state
enum C4AulScriptState
{
ASS_ERROR, // erroneous script
ASS_NONE, // nothing
ASS_PREPARSED, // function list built; CodeSize set
ASS_LINKED, // includes and appends resolved
ASS_PARSED // byte code generated
};
// script profiler entry
class C4AulProfiler
{
private:
// map entry
struct Entry
{
C4AulScriptFunc *pFunc;
uint32_t tProfileTime;
bool operator < (const Entry &e2) const { return tProfileTime < e2.tProfileTime ; }
};
// items
std::vector<Entry> Times;
public:
void CollectEntry(C4AulScriptFunc *pFunc, uint32_t tProfileTime);
void Show();
static void Abort();
static void StartProfiling(C4AulScript *pScript);
static void StopProfiling();
};
// user text file to which scripts can write using FileWrite().
// actually just writes to an internal buffer
class C4AulUserFile
@ -130,68 +92,16 @@ public:
int32_t GetHandle() const { return handle; }
};
// script class
class C4AulScript
{
public:
C4AulScript(); // constructor
virtual ~C4AulScript(); // destructor
virtual void Clear(); // remove script, byte code and children
void Reg2List(C4AulScriptEngine *pEngine); // reg to linked list
void Unreg(); // remove from list
virtual bool Delete() { return true; } // allow deletion on pure class
StdCopyStrBuf ScriptName; // script name
virtual C4PropListStatic * GetPropList() { return 0; }
virtual C4ScriptHost * GetScriptHost() { return 0; }
virtual void ResetProfilerTimes(); // zero all profiler times of owned functions
virtual void CollectProfilerTimes(class C4AulProfiler &rProfiler);
bool IsReady() { return State == ASS_PARSED; } // whether script calls may be done
// helper functions
void Warn(const char *pMsg, ...) GNUC_FORMAT_ATTRIBUTE_O;
friend class C4AulParseError;
friend class C4AulFunc;
friend class C4AulScriptFunc;
friend class C4AulScriptEngine;
friend class C4AulParse;
friend class C4AulDebug;
friend class C4ScriptHost;
// Translate a string using the script's lang table
std::string Translate(const std::string &text) const;
protected:
C4LangStringTable *stringTable;
C4AulScriptEngine *Engine; //owning engine
C4AulScript *Prev, *Next; // tree structure
C4AulScriptState State; // script state
virtual bool ReloadScript(const char *szPath, const char *szLanguage); // reload given script
virtual bool Parse();
virtual bool ResolveIncludes(C4DefList *rDefs);
virtual bool ResolveAppends(C4DefList *rDefs);
virtual void UnLink();
};
// holds all C4AulScripts
class C4AulScriptEngine : public C4AulScript
class C4AulScriptEngine: public C4PropListStaticMember
{
protected:
C4AulFuncMap FuncLookUp;
C4AulFunc * GetFirstFunc(C4String * Name)
C4AulFunc * GetFirstFunc(const char * Name)
{ return FuncLookUp.GetFirstFunc(Name); }
C4AulFunc * GetNextSNFunc(const C4AulFunc * After)
{ return FuncLookUp.GetNextSNFunc(After); }
C4Value GlobalPropList;
C4AulScript *Child0, *ChildL; // tree structure
C4ScriptHost *Child0, *ChildL; // tree structure
// all open user files
// user files aren't saved - they are just open temporary e.g. during game saving
@ -216,21 +126,16 @@ public:
void Clear(); // clear data
void Link(C4DefList *rDefs); // link and parse all scripts
void ReLink(C4DefList *rDefs); // unlink, link and parse all scripts
virtual C4PropListStatic * GetPropList();
C4Value Call(const char * k, C4AulParSet *pPars=0, bool fPassErrors=false)
{ return GetPropList()->Call(k, pPars, fPassErrors); }
using C4AulScript::ReloadScript;
C4PropListStatic * GetPropList() { return this; }
bool ReloadScript(const char *szScript, const char *szLanguage); // search script and reload, if found
// For the list of functions in the PropertyDlg
std::list<const char*> GetFunctionNames(C4PropList *);
void ResetProfilerTimes(); // zero all profiler times of owned functions
void CollectProfilerTimes(class C4AulProfiler &rProfiler);
void RegisterGlobalConstant(const char *szName, const C4Value &rValue); // creates a new constants or overwrites an old one
bool GetGlobalConstant(const char *szName, C4Value *pTargetValue); // check if a constant exists; assign value to pTargetValue if not NULL
bool Denumerate(C4ValueNumbers *);
void Denumerate(C4ValueNumbers *);
void UnLink(); // called when a script is being reloaded (clears string table)
// Compile scenario script data (without strings and constants)
@ -242,10 +147,10 @@ public:
C4AulUserFile *GetUserFile(int32_t handle); // get user file given by handle
friend class C4AulFunc;
friend class C4AulProfiler;
friend class C4ScriptHost;
friend class C4AulParse;
friend class C4AulDebug;
friend class C4AulScript;
};
extern C4AulScriptEngine ScriptEngine;

View File

@ -248,7 +248,7 @@ C4AulDebug::ProcessLineResult C4AulDebug::ProcessLine(const StdStrBuf &Line)
}
else if (SEqualNoCase(szCmd, "LST"))
{
for (C4AulScript* script = ScriptEngine.Child0; script; script = script->Next)
for (C4ScriptHost* script = ScriptEngine.Child0; script; script = script->Next)
{
SendLine(RelativePath(script->ScriptName));
}
@ -266,14 +266,14 @@ C4AulDebug::ProcessLineResult C4AulDebug::ProcessLine(const StdStrBuf &Line)
int line = atoi(&scriptPath[colonPos+1]);
scriptPath.erase(colonPos);
C4AulScript *script;
C4ScriptHost *script;
for (script = ScriptEngine.Child0; script; script = script->Next)
{
if (SEqualNoCase(RelativePath(script->ScriptName), scriptPath.c_str()))
break;
}
auto sh = script ? script->GetScriptHost() : NULL;
auto sh = script;
if (sh)
{
C4AulBCC * found = NULL;
@ -333,7 +333,7 @@ C4AulDebug::ProcessLineResult C4AulDebug::ProcessLine(const StdStrBuf &Line)
}
else if ((varIndex = pCtx->Func->VarNamed.GetItemNr(szData)) != -1)
{
val = &pCtx->Vars[varIndex];
val = &pCtx->Pars[pCtx->Func->GetParCount() + varIndex];
}
}
const char* typeName = val ? GetC4VName(val->GetType()) : "any";

View File

@ -221,6 +221,8 @@ public:
pFunc(pFunc), ParType {C4ValueConv<ParTypes>::Type()...}, Public(Public)
{
Parent->SetPropertyByS(Name, C4VFunction(this));
for(int i = GetParCount(); i < C4AUL_MAX_Par; ++i)
ParType[i] = C4V_Any;
}
virtual int GetParCount() const
@ -250,7 +252,7 @@ public:
}
protected:
Func pFunc;
C4V_Type ParType[10];// type of the parameters
C4V_Type ParType[C4AUL_MAX_Par];// type of the parameters
bool Public;
};

View File

@ -56,24 +56,23 @@ StdStrBuf C4AulScriptContext::ReturnDump(StdStrBuf Dump)
Dump.AppendChar('(');
int iNullPars = 0;
for (int i = 0; i < Func->GetParCount(); i++)
if (Pars + i < Vars)
{
if (!Pars[i])
iNullPars++;
else
{
if (!Pars[i])
iNullPars++;
else
if (i > iNullPars)
Dump.AppendChar(',');
// Insert missing null parameters
while (iNullPars > 0)
{
if (i > iNullPars)
Dump.AppendChar(',');
// Insert missing null parameters
while (iNullPars > 0)
{
Dump.Append("0,");
iNullPars--;
}
// Insert parameter
Dump.Append(Pars[i].GetDataString());
Dump.Append("0,");
iNullPars--;
}
// Insert parameter
Dump.Append(Pars[i].GetDataString());
}
}
Dump.AppendChar(')');
}
else
@ -146,37 +145,52 @@ void C4AulExec::ClearPointers(C4Object * obj)
C4Value C4AulExec::Exec(C4AulScriptFunc *pSFunc, C4PropList * p, C4Value *pnPars, bool fPassErrors)
{
// Push parameters
C4Value *pPars = pCurVal + 1;
if (pnPars)
for (int i = 0; i < C4AUL_MAX_Par; i++)
PushValue(pnPars[i]);
if (pCurVal + 1 - pPars > pSFunc->GetParCount())
PopValues(pCurVal + 1 - pPars - pSFunc->GetParCount());
else
PushNullVals(pSFunc->GetParCount() - (pCurVal + 1 - pPars));
// Push a new context
C4AulScriptContext ctx;
ctx.tTime = 0;
ctx.Obj = p;
ctx.Return = NULL;
ctx.Pars = pPars;
ctx.Vars = pCurVal + 1;
ctx.Func = pSFunc;
ctx.CPos = NULL;
PushContext(ctx);
// Execute
return Exec(pSFunc->GetCode(), fPassErrors);
}
C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
{
// Save start context
C4AulScriptContext *pOldCtx = pCurCtx;
C4Value *pPars = pCurVal + 1;
try
{
// Push parameters
assert(pnPars);
for (int i = 0; i < pSFunc->GetParCount(); i++)
PushValue(pnPars[i]);
// Push a new context
C4AulScriptContext ctx;
ctx.tTime = 0;
ctx.Obj = p;
ctx.Return = NULL;
ctx.Pars = pPars;
ctx.Func = pSFunc;
ctx.CPos = NULL;
PushContext(ctx);
// Execute
return Exec(pSFunc->GetCode());
}
catch (C4AulError &e)
{
if(!e.shown) e.show();
// Unwind stack
while (pCurCtx > pOldCtx)
{
pCurCtx->dump(StdStrBuf(" by: "));
PopContext();
}
PopValuesUntil(pPars - 1);
// Pass?
if (fPassErrors)
throw;
// Trace
LogCallStack();
}
// Return nothing
return C4VNull;
}
C4Value C4AulExec::Exec(C4AulBCC *pCPos)
{
try
{
@ -231,14 +245,10 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
case AB_ERR:
throw C4AulExecError("syntax error: see above for details");
case AB_PARN_CONTEXT:
case AB_DUP_CONTEXT:
PushValue(AulExec.GetContext(AulExec.GetContextDepth()-2)->Pars[pCPos->Par.i]);
break;
case AB_VARN_CONTEXT:
PushValue(AulExec.GetContext(AulExec.GetContextDepth()-2)->Vars[pCPos->Par.i]);
break;
case AB_LOCALN:
if (!pCurCtx->Obj)
throw C4AulExecError("can't access local variables without this");
@ -717,7 +727,7 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
if (pCurVal->_getInt() >= pArray->GetSize())
break;
// Get next
pCurCtx->Vars[pCPos->Par.i] = pArray->GetItem(iItem);
pCurVal[pCPos->Par.i] = pArray->GetItem(iItem);
// Save position
pCurVal->SetInt(iItem + 1);
// Jump over next instruction
@ -792,29 +802,11 @@ C4Value C4AulExec::Exec(C4AulBCC *pCPos, bool fPassErrors)
}
catch (C4AulError &e)
{
if(!e.shown) e.show();
// Save current position
assert(pCurCtx->Func->GetCode() <= pCPos);
pCurCtx->CPos = pCPos;
// Unwind stack
C4Value *pUntil = NULL;
while (pCurCtx >= pOldCtx)
{
pCurCtx->dump(StdStrBuf(" by: "));
pUntil = pCurCtx->Pars - 1;
PopContext();
}
if (pUntil)
PopValuesUntil(pUntil);
// Pass?
if (fPassErrors)
throw;
// Trace
LogCallStack();
throw;
}
// Return nothing
return C4VNull;
}
C4AulBCC *C4AulExec::Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4PropList *pContext)
@ -839,7 +831,6 @@ C4AulBCC *C4AulExec::Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4
throw C4AulExecError("using removed object");
ctx.Return = pReturn;
ctx.Pars = pPars;
ctx.Vars = pCurVal + 1;
ctx.Func = pSFunc;
ctx.CPos = NULL;
PushContext(ctx);
@ -912,33 +903,20 @@ void C4AulExec::StartTrace()
iTraceStart = ContextStackSize();
}
void C4AulExec::StartProfiling(C4AulScript *pProfiledScript)
void C4AulExec::StartProfiling(C4ScriptHost *pProfiledScript)
{
// stop previous profiler run
if (fProfiling) AbortProfiling();
if (fProfiling) StopProfiling();
fProfiling = true;
// resets profling times and starts recording the times
this->pProfiledScript = pProfiledScript;
C4TimeMilliseconds tNow = C4TimeMilliseconds::Now();
tDirectExecStart = tNow; // in case profiling is started from DirectExec
tDirectExecTotal = 0;
pProfiledScript->ResetProfilerTimes();
for (C4AulScriptContext *pCtx = Contexts; pCtx <= pCurCtx; ++pCtx)
pCtx->tTime = tNow;
}
void C4AulExec::StopProfiling()
{
// stop the profiler and displays results
if (!fProfiling) return;
fProfiling = false;
// collect profiler times
C4AulProfiler Profiler;
Profiler.CollectEntry(NULL, tDirectExecTotal);
pProfiledScript->CollectProfilerTimes(Profiler);
Profiler.Show();
}
void C4AulExec::PushContext(const C4AulScriptContext &rContext)
{
if (pCurCtx >= Contexts + MAX_CONTEXT_STACK - 1)
@ -977,19 +955,27 @@ void C4AulExec::PopContext()
pCurCtx--;
}
void C4AulProfiler::StartProfiling(C4AulScript *pScript)
void C4AulProfiler::StartProfiling(C4ScriptHost *pScript)
{
AulExec.StartProfiling(pScript);
if(pScript)
ResetTimes(pScript->GetPropList());
else
ResetTimes();
}
void C4AulProfiler::StopProfiling()
{
if (!AulExec.IsProfiling()) return;
AulExec.StopProfiling();
}
void C4AulProfiler::Abort()
{
AulExec.AbortProfiling();
// collect profiler times
C4AulProfiler Profiler;
Profiler.CollectEntry(NULL, AulExec.tDirectExecTotal);
if(AulExec.pProfiledScript)
Profiler.CollectTimes(AulExec.pProfiledScript->GetPropList());
else
Profiler.CollectTimes();
Profiler.Show();
}
void C4AulProfiler::CollectEntry(C4AulScriptFunc *pFunc, uint32_t tProfileTime)
@ -1032,65 +1018,66 @@ C4Value C4AulExec::DirectExec(C4PropList *p, const char *szScript, const char *s
#endif
// profiler
StartDirectExec();
C4AulScript * script = &::GameScript;
if (p == ::ScriptEngine.GetPropList())
script = &::ScriptEngine;
C4PropListStatic * script = ::GameScript.GetPropList();
if (p && p->IsStatic())
script = p->IsStatic();
else if (p && p->GetDef())
script = &p->GetDef()->Script;
script = p->GetDef();
// Add a new function
C4AulScriptFunc *pFunc = new C4AulScriptFunc(script->GetPropList(), script->GetScriptHost(), 0, szScript);
auto pFunc = std::make_unique<C4AulScriptFunc>(script, nullptr, nullptr, szScript);
// Parse function
try
{
pFunc->ParseFn(context);
C4Value vRetVal(Exec(pFunc, p, NULL, fPassErrors));
delete pFunc; pFunc = 0;
pFunc->ParseFn(&::ScriptEngine, context);
C4AulParSet Pars;
C4Value vRetVal(Exec(pFunc.get(), p, Pars.Par, fPassErrors));
// profiler
StopDirectExec();
return vRetVal;
}
catch (C4AulError &ex)
{
delete pFunc;
if(fPassErrors)
throw;
ex.show();
LogCallStack();
StopDirectExec();
return C4VNull;
}
}
void C4AulScript::ResetProfilerTimes()
void C4AulProfiler::ResetTimes(C4PropListStatic * p)
{
// zero all profiler times of owned functions
C4AulScriptFunc *pSFunc;
for (C4String *pFn = GetPropList()->EnumerateOwnFuncs(); pFn; pFn = GetPropList()->EnumerateOwnFuncs(pFn))
if ((pSFunc = GetPropList()->GetFunc(pFn)->SFunc()))
for (C4String *pFn = p->EnumerateOwnFuncs(); pFn; pFn = p->EnumerateOwnFuncs(pFn))
if ((pSFunc = p->GetFunc(pFn)->SFunc()))
pSFunc->tProfileTime = 0;
}
void C4AulScript::CollectProfilerTimes(C4AulProfiler &rProfiler)
void C4AulProfiler::CollectTimes(C4PropListStatic * p)
{
// collect all profiler times of owned functions
C4AulScriptFunc *pSFunc;
for (C4String *pFn = GetPropList()->EnumerateOwnFuncs(); pFn; pFn = GetPropList()->EnumerateOwnFuncs(pFn))
if ((pSFunc = GetPropList()->GetFunc(pFn)->SFunc()))
rProfiler.CollectEntry(pSFunc, pSFunc->tProfileTime);
for (C4String *pFn = p->EnumerateOwnFuncs(); pFn; pFn = p->EnumerateOwnFuncs(pFn))
if ((pSFunc = p->GetFunc(pFn)->SFunc()))
CollectEntry(pSFunc, pSFunc->tProfileTime);
}
void C4AulScriptEngine::ResetProfilerTimes()
void C4AulProfiler::ResetTimes()
{
// zero all profiler times of owned functions
C4AulScript::ResetProfilerTimes();
ResetTimes(::ScriptEngine.GetPropList());
// reset sub-scripts
for (C4AulScript *pScript = Child0; pScript; pScript = pScript->Next)
pScript->ResetProfilerTimes();
for (C4ScriptHost *pScript = ::ScriptEngine.Child0; pScript; pScript = pScript->Next)
ResetTimes(pScript->GetPropList());
}
void C4AulScriptEngine::CollectProfilerTimes(C4AulProfiler &rProfiler)
void C4AulProfiler::CollectTimes()
{
// collect all profiler times of owned functions
C4AulScript::CollectProfilerTimes(rProfiler);
CollectTimes(::ScriptEngine.GetPropList());
// collect sub-scripts
for (C4AulScript *pScript = Child0; pScript; pScript = pScript->Next)
pScript->CollectProfilerTimes(rProfiler);
for (C4ScriptHost *pScript = ::ScriptEngine.Child0; pScript; pScript = pScript->Next)
CollectTimes(pScript->GetPropList());
}

View File

@ -43,7 +43,6 @@ struct C4AulScriptContext
C4PropList *Obj;
C4Value *Return;
C4Value *Pars;
C4Value *Vars;
C4AulScriptFunc *Func;
C4AulBCC *CPos;
C4TimeMilliseconds tTime; // initialized only by profiler if active
@ -68,20 +67,20 @@ private:
bool fProfiling;
C4TimeMilliseconds tDirectExecStart;
uint32_t tDirectExecTotal; // profiler time for DirectExec
C4AulScript *pProfiledScript;
C4ScriptHost *pProfiledScript;
C4AulScriptContext Contexts[MAX_CONTEXT_STACK];
C4Value Values[MAX_VALUE_STACK];
void StartProfiling(C4ScriptHost *pScript); // starts recording the times
bool IsProfiling() { return fProfiling; }
void StopProfiling() { fProfiling=false; }
friend class C4AulProfiler;
public:
C4Value Exec(C4AulScriptFunc *pSFunc, C4PropList * p, C4Value pPars[], bool fPassErrors);
C4Value Exec(C4AulBCC *pCPos, bool fPassErrors);
C4Value DirectExec(C4PropList *p, const char *szScript, const char *szContext, bool fPassErrors = false, C4AulScriptContext* context = NULL);
void StartTrace();
void StartProfiling(C4AulScript *pScript); // resets profling times and starts recording the times
void StopProfiling(); // stop the profiler and displays results
void AbortProfiling() { fProfiling=false; }
inline void StartDirectExec() { if (fProfiling) tDirectExecStart = C4TimeMilliseconds::Now(); }
inline void StopDirectExec() { if (fProfiling) tDirectExecTotal += C4TimeMilliseconds::Now() - tDirectExecStart; }
@ -93,6 +92,7 @@ public:
void ClearPointers(C4Object *);
private:
C4Value Exec(C4AulBCC *pCPos);
void PushContext(const C4AulScriptContext &rContext);
void PopContext();
@ -184,7 +184,7 @@ private:
int LocalValueStackSize() const
{
return ContextStackSize()
? pCurVal - pCurCtx->Vars - pCurCtx->Func->VarNamed.iSize + 1
? pCurVal - pCurCtx->Pars - pCurCtx->Func->GetParCount() - pCurCtx->Func->VarNamed.iSize + 1
: pCurVal - Values + 1;
}
@ -231,4 +231,32 @@ private:
extern C4AulExec AulExec;
// script profiler entry
class C4AulProfiler
{
private:
// map entry
struct Entry
{
C4AulScriptFunc *pFunc;
uint32_t tProfileTime;
bool operator < (const Entry &e2) const { return tProfileTime < e2.tProfileTime ; }
};
// items
std::vector<Entry> Times;
void CollectEntry(C4AulScriptFunc *pFunc, uint32_t tProfileTime);
void CollectTimes(C4PropListStatic * p);
void CollectTimes();
static void ResetTimes(C4PropListStatic * p);
static void ResetTimes();
void Show();
public:
static void Abort() { AulExec.StopProfiling(); }
static void StartProfiling(C4ScriptHost *pScript); // reset times and start collecting new ones
static void StopProfiling(); // stop the profiler and displays results
};
#endif // C4AULEXEC_H

View File

@ -46,7 +46,6 @@ struct C4AulParSet
// base function class
class C4AulFunc: public C4RefCnt
{
friend class C4AulScript;
friend class C4AulScriptEngine;
friend class C4AulFuncMap;
friend class C4AulParse;

View File

@ -24,16 +24,6 @@
#include <C4Game.h>
#include <C4GameObjects.h>
bool C4AulScript::ResolveIncludes(C4DefList *rDefs)
{
return false;
}
bool C4AulScript::ResolveAppends(C4DefList *rDefs)
{
return false;
}
// ResolveAppends and ResolveIncludes must be called both
// for each script. ResolveAppends has to be called first!
bool C4ScriptHost::ResolveAppends(C4DefList *rDefs)
@ -47,8 +37,8 @@ bool C4ScriptHost::ResolveAppends(C4DefList *rDefs)
C4Def *Def = rDefs ? rDefs->GetByName(*a) : NULL;
if (Def)
{
if (std::find(Def->Script.SourceScripts.begin(), Def->Script.SourceScripts.end(), GetScriptHost()) == Def->Script.SourceScripts.end())
Def->Script.SourceScripts.push_back(GetScriptHost());
if (std::find(Def->Script.SourceScripts.begin(), Def->Script.SourceScripts.end(), this) == Def->Script.SourceScripts.end())
Def->Script.SourceScripts.push_back(this);
}
else
{
@ -67,8 +57,8 @@ bool C4ScriptHost::ResolveAppends(C4DefList *rDefs)
if (!pDef) break;
if (pDef == GetPropList()) continue;
// append
if (std::find(pDef->Script.SourceScripts.begin(), pDef->Script.SourceScripts.end(), GetScriptHost()) == pDef->Script.SourceScripts.end())
pDef->Script.SourceScripts.push_back(GetScriptHost());
if (std::find(pDef->Script.SourceScripts.begin(), pDef->Script.SourceScripts.end(), this) == pDef->Script.SourceScripts.end())
pDef->Script.SourceScripts.push_back(this);
}
}
}
@ -103,8 +93,8 @@ bool C4ScriptHost::ResolveIncludes(C4DefList *rDefs)
for (std::list<C4ScriptHost *>::reverse_iterator s = Def->Script.SourceScripts.rbegin(); s != Def->Script.SourceScripts.rend(); ++s)
{
if (std::find(GetScriptHost()->SourceScripts.begin(), GetScriptHost()->SourceScripts.end(), *s) == GetScriptHost()->SourceScripts.end())
GetScriptHost()->SourceScripts.push_front(*s);
if (std::find(SourceScripts.begin(), SourceScripts.end(), *s) == SourceScripts.end())
SourceScripts.push_front(*s);
}
}
else
@ -122,11 +112,6 @@ bool C4ScriptHost::ResolveIncludes(C4DefList *rDefs)
return true;
}
void C4AulScript::UnLink()
{
}
void C4ScriptHost::UnLink()
{
C4PropList * p = GetPropList();
@ -147,44 +132,35 @@ void C4AulScriptEngine::UnLink()
warnCnt = errCnt = lineCnt = 0;
// unlink scripts
for (C4AulScript *s = Child0; s; s = s->Next)
for (C4ScriptHost *s = Child0; s; s = s->Next)
s->UnLink();
GetPropList()->Thaw();
if (State > ASS_PREPARSED) State = ASS_PREPARSED;
// Do not clear global variables and constants, because they are registered by the
// preparser or other parts. Note that keeping those fields means that you cannot delete a global
// variable or constant at runtime by removing it from the script.
}
bool C4AulScript::ReloadScript(const char *szPath, const char *szLanguage)
{
return false;
}
void C4AulScriptEngine::Link(C4DefList *rDefs)
{
try
{
// resolve appends
for (C4AulScript *s = Child0; s; s = s->Next)
for (C4ScriptHost *s = Child0; s; s = s->Next)
s->ResolveAppends(rDefs);
// resolve includes
for (C4AulScript *s = Child0; s; s = s->Next)
for (C4ScriptHost *s = Child0; s; s = s->Next)
s->ResolveIncludes(rDefs);
// parse the scripts to byte code
for (C4AulScript *s = Child0; s; s = s->Next)
for (C4ScriptHost *s = Child0; s; s = s->Next)
s->Parse();
// engine is always parsed (for global funcs)
State = ASS_PARSED;
if (rDefs)
rDefs->CallEveryDefinition();
// Done modifying the proplists now
for (C4AulScript *s = Child0; s; s = s->Next)
for (C4ScriptHost *s = Child0; s; s = s->Next)
s->GetPropList()->Freeze();
GetPropList()->Freeze();
}
@ -217,7 +193,7 @@ void C4AulScriptEngine::ReLink(C4DefList *rDefs)
bool C4AulScriptEngine::ReloadScript(const char *szScript, const char *szLanguage)
{
C4AulScript * s;
C4ScriptHost * s;
for (s = Child0; s; s = s->Next)
if (s->ReloadScript(szScript, szLanguage))
break;

View File

@ -129,11 +129,11 @@ public:
iStack(0),
pLoopStack(NULL)
{ }
C4AulParse(C4AulScriptFunc * Fn, C4AulScriptContext* context, enum Type Type):
Fn(Fn), Host(Fn->pOrgScript), pOrgScript(Fn->pOrgScript), Engine(pOrgScript->Engine),
C4AulParse(C4AulScriptFunc * Fn, C4AulScriptContext* context, C4AulScriptEngine *Engine):
Fn(Fn), Host(NULL), pOrgScript(NULL), Engine(Engine),
SPos(Fn->Script), TokenSPos(SPos),
TokenType(ATT_INVALID),
Type(Type),
Type(C4AulParse::PARSER),
ContextToExecIn(context),
fJump(false),
iStack(0),
@ -168,7 +168,6 @@ private:
void Parse_For();
void Parse_ForEach();
void Parse_Expression(int iParentPrio = -1);
void Parse_Expression2(int iParentPrio = -1);
void Parse_Var();
void Parse_Local();
void Parse_Static();
@ -179,12 +178,10 @@ private:
bool AdvanceSpaces(); // skip whitespaces; return whether script ended
int GetOperator(const char* pScript);
// Simply discard the string, put it in the Table and delete it with the script or delete it when refcount drops
enum OperatorPolicy { OperatorsPlease = 0, StarsPlease };
void ClearToken(); // clear any data held with the current token
C4AulTokenType GetNextToken(OperatorPolicy Operator = OperatorsPlease); // get next token of SPos
C4AulTokenType GetNextToken(); // get next token of SPos
void Shift(OperatorPolicy Operator = OperatorsPlease);
void Shift();
void Match(C4AulTokenType TokenType, const char * Expected = NULL);
void Check(C4AulTokenType TokenType, const char * Expected = NULL);
void UnexpectedToken(const char * Expected) NORETURN;
@ -231,7 +228,7 @@ private:
friend class C4AulParseError;
};
void C4AulScript::Warn(const char *pMsg, ...)
void C4ScriptHost::Warn(const char *pMsg, ...)
{
va_list args; va_start(args, pMsg);
StdStrBuf Buf;
@ -303,7 +300,7 @@ C4AulParseError::C4AulParseError(C4AulParse * state, const char *pMsg, const cha
}
C4AulParseError::C4AulParseError(C4AulScript *pScript, const char *pMsg, const char *pIdtf, bool Warn)
C4AulParseError::C4AulParseError(C4ScriptHost *pScript, const char *pMsg, const char *pIdtf, bool Warn)
{
// compose error string
sMessage.Format("%s: %s%s",
@ -466,7 +463,7 @@ void C4AulParse::ClearToken()
}
}
C4AulTokenType C4AulParse::GetNextToken(OperatorPolicy Operator)
C4AulTokenType C4AulParse::GetNextToken()
{
// clear mem of prev token
ClearToken();
@ -519,8 +516,6 @@ C4AulTokenType C4AulParse::GetNextToken(OperatorPolicy Operator)
{ SPos+=2; return ATT_CALLFS;}// "->~"
else if (C == '-' && *SPos == '>')
{ ++SPos; return ATT_CALL; } // "->"
else if (C == '*' && Operator == StarsPlease)
return ATT_STAR; // "*"
else if ((cInt = GetOperator(SPos - 1)) != -1)
{
SPos += SLen(C4ScriptOpMap[cInt].Identifier) - 1;
@ -641,9 +636,6 @@ static const char * GetTTName(C4AulBCCType e)
case AB_THIS: return "THIS";
case AB_FUNC: return "FUNC"; // function
case AB_PARN_CONTEXT: return "AB_PARN_CONTEXT";
case AB_VARN_CONTEXT: return "AB_VARN_CONTEXT";
// prefix
case AB_Inc: return "Inc"; // ++
case AB_Dec: return "Dec"; // --
@ -682,6 +674,7 @@ static const char * GetTTName(C4AulBCCType e)
case AB_NIL: return "NIL"; // constant: nil
case AB_NEW_ARRAY: return "NEW_ARRAY"; // semi-constant: array
case AB_DUP: return "DUP"; // duplicate value from stack
case AB_DUP_CONTEXT: return "AB_DUP_CONTEXT"; // duplicate value from stack of parent function
case AB_NEW_PROPLIST: return "NEW_PROPLIST"; // create a new proplist
case AB_POP_TO: return "POP_TO"; // initialization of named var
case AB_JUMP: return "JUMP"; // jump
@ -808,7 +801,7 @@ bool C4ScriptHost::Preparse()
GetPropList()->Properties.Swap(&LocalValues);
// return success
C4AulScript::State = ASS_PREPARSED;
this->State = ASS_PREPARSED;
return true;
}
@ -824,11 +817,10 @@ int C4AulParse::GetStackValue(C4AulBCCType eType, intptr_t X)
case AB_CARRAY:
case AB_CFUNCTION:
case AB_NIL:
case AB_PARN_CONTEXT:
case AB_VARN_CONTEXT:
case AB_LOCALN:
case AB_GLOBALN:
case AB_DUP:
case AB_DUP_CONTEXT:
case AB_THIS:
return 1;
@ -977,6 +969,13 @@ int C4AulParse::AddBCC(C4AulBCCType eType, intptr_t X)
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 &&
@ -990,11 +989,11 @@ int C4AulParse::AddBCC(C4AulBCCType eType, intptr_t X)
return Fn->GetCodePos() - 1;
}
// Reduce Not + CONDN to COND
if(eType == AB_CONDN && pCPos1->bccType == AB_Not)
// Reduce Not + CONDN to COND, Not + COND to CONDN
if((eType == AB_CONDN || eType == AB_COND) && pCPos1->bccType == AB_Not)
{
pCPos1->bccType = AB_COND;
pCPos1->Par.i = X;
pCPos1->bccType = eType == AB_CONDN ? AB_COND : AB_CONDN;
pCPos1->Par.i = X + 1;
return Fn->GetCodePos() - 1;
}
@ -1013,7 +1012,7 @@ void C4AulParse::RemoveLastBCC()
{
// Security: This is unsafe on anything that might get optimized away
C4AulBCC *pBCC = Fn->GetLastCode();
assert(pBCC->bccType != AB_STACK);
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
@ -1036,7 +1035,7 @@ C4V_Type C4AulParse::GetLastRetType(C4V_Type to)
case AB_CALL: case AB_CALLFS:
{
C4String * pName = Fn->GetLastCode()->Par.s;
C4AulFunc * pFunc2 = Engine->GetFirstFunc(pName);
C4AulFunc * pFunc2 = Engine->GetFirstFunc(pName->GetCStr());
bool allwarn = true;
from = C4V_Any;
while (pFunc2 && allwarn)
@ -1078,6 +1077,7 @@ C4AulBCC C4AulParse::MakeSetter(bool fLeaveValue)
// 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;
Setter.Par.s->IncRef(); // so string isn't dropped by RemoveLastBCC, see also C4AulScript::AddBCC
@ -1090,21 +1090,34 @@ C4AulBCC C4AulParse::MakeSetter(bool fLeaveValue)
default:
throw C4AulParseError(this, "assignment to a constant");
}
// Remove value BCC
RemoveLastBCC();
// Want the value?
if(fLeaveValue)
// 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(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)
{
// Duplicate parameters on stack
// (all push one value on the stack as result, so we have -(N-1) parameters)
int iParCount = -GetStackValue(Value.bccType, Value.Par.X) + 1;
for(int i = 0; i < iParCount; i++)
AddBCC(AB_DUP, 1 - iParCount);
// Finally re-add original BCC
AddBCC(Value.bccType, Value.Par.X);
}
// Done. The returned BCC should be added later once the value to be set was pushed on top.
assert(GetStackValue(Value.bccType, Value.Par.X) == GetStackValue(Setter.bccType, Setter.Par.X)+1);
assert(iParCount == -GetStackValue(Setter.bccType, Setter.Par.X));
return Setter;
}
@ -1204,7 +1217,6 @@ const char * C4AulParse::GetTokenName(C4AulTokenType TokenType)
case ATT_BLCLOSE: return "'}'";
case ATT_CALL: return "'->'";
case ATT_CALLFS: return "'->~'";
case ATT_STAR: return "'*'";
case ATT_LDOTS: return "'...'";
case ATT_SET: return "'='";
case ATT_OPERATOR: return "operator";
@ -1213,9 +1225,9 @@ const char * C4AulParse::GetTokenName(C4AulTokenType TokenType)
}
}
void C4AulParse::Shift(OperatorPolicy Operator)
void C4AulParse::Shift()
{
TokenType = GetNextToken(Operator);
TokenType = GetNextToken();
}
void C4AulParse::Check(C4AulTokenType RefTokenType, const char * Expected)
{
@ -1232,11 +1244,11 @@ void C4AulParse::UnexpectedToken(const char * Expected)
throw C4AulParseError(this, FormatString("%s expected, but found %s", Expected, GetTokenName(TokenType)).getData());
}
void C4AulScriptFunc::ParseFn(C4AulScriptContext* context)
void C4AulScriptFunc::ParseFn(C4AulScriptEngine *Engine, C4AulScriptContext* context)
{
ClearCode();
// parse
C4AulParse state(this, context, C4AulParse::PARSER);
C4AulParse state(this, context, Engine);
state.Parse_DirectExec();
}
@ -1287,8 +1299,7 @@ void C4AulParse::Parse_Script(C4ScriptHost * scripthost)
{
if (pOrgScript->GetPropList()->GetDef())
throw C4AulParseError(this, "#appendto in a Definition");
// for #appendto * '*' needs to be ATT_STAR, not an operator.
Shift(StarsPlease);
Shift();
if (Type == PREPARSER)
{
// get id of script to include/append
@ -1298,9 +1309,13 @@ void C4AulParse::Parse_Script(C4ScriptHost * scripthost)
case ATT_IDTF:
Id = StdCopyStrBuf(Idtf);
break;
case ATT_STAR: // "*"
Id = StdCopyStrBuf("*");
break;
case ATT_OPERATOR:
if (SEqual(C4ScriptOpMap[cInt].Identifier, "*"))
{
Id = StdCopyStrBuf("*");
break;
}
//fallthrough
default:
// -> ID expected
UnexpectedToken("identifier or '*'");
@ -1695,16 +1710,11 @@ void C4AulParse::Parse_Statement()
int C4AulParse::Parse_Params(int iMaxCnt, const char * sWarn, C4AulFunc * pFunc)
{
int size = 0;
int size = 0, WarnCnt = iMaxCnt;
// so it's a regular function; force "("
Match(ATT_BOPEN);
bool fDone = false;
do switch (TokenType)
while(TokenType != ATT_BCLOSE) switch(TokenType)
{
case ATT_BCLOSE:
Shift();
fDone = true;
break;
case ATT_COMMA:
// got no parameter before a ","
if (sWarn && Config.Developer.ExtraWarnings)
@ -1723,26 +1733,29 @@ int C4AulParse::Parse_Params(int iMaxCnt, const char * sWarn, C4AulFunc * pFunc)
// Push all unnamed parameters of the current function as parameters
for (int i = Fn->ParNamed.iSize; i < C4AUL_MAX_Par; ++i)
{
AddBCC(AB_DUP, 1 + i - (iStack + Fn->VarNamed.iSize + Fn->GetParCount()));
++size;
if (size >= iMaxCnt)
break;
AddBCC(AB_DUP, 1 + i - (iStack + Fn->VarNamed.iSize + Fn->GetParCount()));
++size;
}
// Do not allow more parameters even if there is place left
fDone = true;
Match(ATT_BCLOSE);
// Do not allow more parameters even if there is space left
Check(ATT_BCLOSE);
break;
default:
// get a parameter
Parse_Expression();
if (pFunc && (Type == PARSER) && size < iMaxCnt)
C4AulFunc * pFunc2 = pFunc ? pFunc : Engine->GetFirstFunc(sWarn);
if (pFunc2 && (Type == PARSER) && size < iMaxCnt)
{
C4V_Type to = pFunc->GetParType()[size];
// pFunc either is the return value from a GetFirstFunc-Call or
// the only function that could be called. When in doubt, don't warn.
C4AulFunc * pFunc2 = pFunc;
while ((pFunc2 = Engine->GetNextSNFunc(pFunc2)))
WarnCnt = pFunc2->GetParCount();
C4V_Type to = pFunc2->GetParType()[size];
// While script can arrange to call any function by changing proplists, the parser has
// no hope of anticipating that, so checking functions of the same name will have to do.
if(!pFunc) while ((pFunc2 = Engine->GetNextSNFunc(pFunc2)))
{
WarnCnt = std::max(WarnCnt, pFunc2->GetParCount());
if (pFunc2->GetParType()[size] != to) to = C4V_Any;
}
C4V_Type from = GetLastRetType(to);
if (C4Value::WarnAboutConversion(from, to))
{
@ -1751,17 +1764,14 @@ int C4AulParse::Parse_Params(int iMaxCnt, const char * sWarn, C4AulFunc * pFunc)
}
++size;
// end of parameter list?
if (TokenType == ATT_BCLOSE)
{
Shift();
fDone = true;
}
else Match(ATT_COMMA, "',' or ')'");
if (TokenType != ATT_BCLOSE)
Match(ATT_COMMA, "',' or ')'");
break;
} while (!fDone);
}
Match(ATT_BCLOSE);
// too many parameters?
if (sWarn && size > iMaxCnt && Type == PARSER)
Warn(FormatString("call to %s gives %d parameters, but only %d are used", sWarn, size, iMaxCnt).getData(), NULL);
if (sWarn && size > WarnCnt && Type == PARSER && !SEqual(sWarn, C4AUL_Inherited) && (pFunc || Config.Developer.ExtraWarnings))
Warn(FormatString("call to %s gives %d parameters, but only %d are used", sWarn, size, WarnCnt).getData(), NULL);
// Balance stack// FIXME: not for CALL/FUNC
if (size != iMaxCnt)
AddBCC(AB_STACK, iMaxCnt - size);
@ -1931,7 +1941,11 @@ void C4AulParse::Parse_DoWhile()
PushLoop();
// Execute body
Parse_Statement();
int BeforeCond = JumpHere();
int BeforeCond = -1;
if (Type == PARSER)
for (Loop::Control *pCtrl2 = pLoopStack->Controls; pCtrl2; pCtrl2 = pCtrl2->Next)
if (!pCtrl2->Break)
BeforeCond = JumpHere();
// Execute condition
if (TokenType != ATT_IDTF || !SEqual(Idtf, C4AUL_While))
UnexpectedToken("'while'");
@ -2105,7 +2119,7 @@ void C4AulParse::Parse_ForEach()
// push initial position (0)
AddBCC(AB_INT);
// get array element
int iStart = AddBCC(AB_FOREACH_NEXT, iVarID);
int iStart = AddBCC(AB_FOREACH_NEXT, 1 + iVarID - (iStack + Fn->VarNamed.iSize));
// jump out (FOREACH_NEXT will jump over this if
// we're not at the end of the array yet)
int iCond = AddBCC(AB_JUMP);
@ -2129,6 +2143,13 @@ void C4AulParse::Parse_ForEach()
AddBCC(AB_STACK, -2);
}
static bool GetPropertyByS(const C4PropList * p, const char * s, C4Value & v)
{
C4String * k = Strings.FindString(s);
if (!k) return false;
return p->GetPropertyByS(k, &v);
}
void C4AulParse::Parse_Expression(int iParentPrio)
{
int ndx;
@ -2154,22 +2175,37 @@ void C4AulParse::Parse_Expression(int iParentPrio)
}
else if (ContextToExecIn && (ndx = ContextToExecIn->Func->ParNamed.GetItemNr(Idtf)) != -1)
{
AddBCC(AB_PARN_CONTEXT, ndx);
AddBCC(AB_DUP_CONTEXT, ndx);
Shift();
}
else if (ContextToExecIn && (ndx = ContextToExecIn->Func->VarNamed.GetItemNr(Idtf)) != -1)
{
AddBCC(AB_VARN_CONTEXT, ndx);
AddBCC(AB_DUP_CONTEXT, ContextToExecIn->Func->GetParCount() + ndx);
Shift();
}
// check for variable (local)
else if (Host && Host->GetPropList() == Fn->Parent && Host->LocalNamed.GetItemNr(Idtf) != -1)
{
// insert variable by id
C4String * pKey = Strings.RegString(Idtf);
AddBCC(AB_LOCALN, (intptr_t) pKey);
AddBCC(AB_LOCALN, (intptr_t) Strings.RegString(Idtf));
Shift();
}
else if ((!Host || Host->GetPropList() != Fn->Parent) && GetPropertyByS(Fn->Parent, Idtf, val))
{
if (FoundFn = val.getFunction())
{
if (Config.Developer.ExtraWarnings && !FoundFn->GetPublic())
Warn("using deprecated function %s", Idtf);
Shift();
Parse_Params(FoundFn->GetParCount(), FoundFn->GetName(), FoundFn);
AddBCC(AB_FUNC, (intptr_t) FoundFn);
}
else
{
AddBCC(AB_LOCALN, (intptr_t) Strings.RegString(Idtf));
Shift();
}
}
else if (SEqual(Idtf, C4AUL_True))
{
AddBCC(AB_BOOL, 1);
@ -2236,7 +2272,7 @@ void C4AulParse::Parse_Expression(int iParentPrio)
if (Fn->OwnerOverloaded)
{
// add direct call to byte code
Parse_Params(Fn->OwnerOverloaded->GetParCount(), NULL, Fn->OwnerOverloaded);
Parse_Params(Fn->OwnerOverloaded->GetParCount(), C4AUL_Inherited, Fn->OwnerOverloaded);
AddBCC(AB_FUNC, (intptr_t) Fn->OwnerOverloaded);
}
else
@ -2258,7 +2294,7 @@ void C4AulParse::Parse_Expression(int iParentPrio)
// will be defined: if no '(' follows, it must be a variable or constant,
// otherwise a function with parameters
if (TokenType == ATT_BOPEN)
Parse_Params(10, NULL);
Parse_Params(C4AUL_MAX_Par, NULL);
}
else if ((FoundFn = Fn->Parent->GetFunc(Idtf)))
{
@ -2369,11 +2405,7 @@ void C4AulParse::Parse_Expression(int iParentPrio)
default:
UnexpectedToken("expression");
}
Parse_Expression2(iParentPrio);
}
void C4AulParse::Parse_Expression2(int iParentPrio)
{
while (1) switch (TokenType)
{
case ATT_SET:
@ -2522,7 +2554,6 @@ void C4AulParse::Parse_Expression2(int iParentPrio)
break;
case ATT_CALL: case ATT_CALLFS:
{
C4AulFunc *pFunc = NULL;
C4String *pName = NULL;
C4AulBCCType eCallType = (TokenType == ATT_CALL) ? AB_CALL : AB_CALLFS;
Shift();
@ -2532,10 +2563,9 @@ void C4AulParse::Parse_Expression2(int iParentPrio)
if (Type == PARSER)
{
pName = ::Strings.RegString(Idtf);
pFunc = Engine->GetFirstFunc(pName);
}
Shift();
Parse_Params(C4AUL_MAX_Par, pName ? pName->GetCStr() : Idtf, pFunc);
Parse_Params(C4AUL_MAX_Par, pName ? pName->GetCStr() : Idtf, NULL);
AddBCC(eCallType, reinterpret_cast<intptr_t>(pName));
}
break;
@ -2833,11 +2863,6 @@ void C4AulParse::Parse_Const()
}
}
bool C4AulScript::Parse()
{
return true;
}
void C4ScriptHost::CopyPropList(C4Set<C4Property> & from, C4PropListStatic * to)
{
// append all funcs and local variable initializations

View File

@ -30,6 +30,7 @@ enum C4AulBCCType
AB_ARRAY_SLICE, // array slicing
AB_ARRAY_SLICE_SET,
AB_DUP, // duplicate value from stack
AB_DUP_CONTEXT, // duplicate value from stack of parent function
AB_STACK_SET, // copy top of stack to stack
AB_POP_TO, // pop top of stack to stack
AB_LOCALN, // a property of this
@ -40,9 +41,6 @@ enum C4AulBCCType
AB_THIS, // this()
AB_FUNC, // function
AB_PARN_CONTEXT,
AB_VARN_CONTEXT,
// prefix
AB_Inc, // ++
AB_Dec, // --
@ -145,7 +143,7 @@ public:
C4AulScriptFunc(C4PropListStatic * Parent, const C4AulScriptFunc &FromFunc); // copy script/code, etc from given func
~C4AulScriptFunc();
void ParseFn(C4AulScriptContext* context = NULL);
void ParseFn(C4AulScriptEngine *Engine, C4AulScriptContext* context = NULL);
virtual bool GetPublic() const { return true; }
virtual int GetParCount() const { return ParCount; }

View File

@ -661,11 +661,11 @@ static bool FnStartCallTrace(C4PropList * _this)
static bool FnStartScriptProfiler(C4PropList * _this, C4Def * pDef)
{
// get script to profile
C4AulScript *pScript;
C4ScriptHost *pScript;
if (pDef)
pScript = &pDef->Script;
else
pScript = &::ScriptEngine;
pScript = NULL;
// profile it
C4AulProfiler::StartProfiling(pScript);
return true;

View File

@ -24,7 +24,11 @@
/*--- C4ScriptHost ---*/
C4ScriptHost::C4ScriptHost()
C4ScriptHost::C4ScriptHost():
// prepare lists
Prev(NULL), Next(NULL),
Engine(NULL),
State(ASS_NONE) // not compiled
{
Script = NULL;
stringTable = 0;
@ -38,12 +42,12 @@ C4ScriptHost::C4ScriptHost()
}
C4ScriptHost::~C4ScriptHost()
{
Unreg();
Clear();
}
void C4ScriptHost::Clear()
{
C4AulScript::Clear();
ComponentHost.Clear();
Script.Clear();
LocalNamed.Reset();
@ -58,6 +62,35 @@ void C4ScriptHost::Clear()
// remove includes
Includes.clear();
Appends.clear();
// reset flags
State = ASS_NONE;
}
void C4ScriptHost::Unreg()
{
// remove from list
if (Prev) Prev->Next = Next; else if (Engine) Engine->Child0 = Next;
if (Next) Next->Prev = Prev; else if (Engine) Engine->ChildL = Prev;
Prev = Next = NULL;
Engine = NULL;
}
void C4ScriptHost::Reg2List(C4AulScriptEngine *pEngine)
{
// already regged? (def reloaded)
if (Engine) return;
// reg to list
if ((Engine = pEngine))
{
if ((Prev = Engine->ChildL))
Prev->Next = this;
else
Engine->Child0 = this;
Engine->ChildL = this;
}
else
Prev = NULL;
Next = NULL;
}
bool C4ScriptHost::Load(C4Group &hGroup, const char *szFilename,
@ -138,12 +171,13 @@ bool C4ScriptHost::ReloadScript(const char *szPath, const char *szLanguage)
return false;
}
void C4ScriptHost::SetError(const char *szMessage)
std::string C4ScriptHost::Translate(const std::string &text) const
{
if (stringTable)
return stringTable->Translate(text);
throw C4LangStringTable::NoSuchTranslation(text);
}
/*--- C4ExtraScriptHost ---*/
C4ExtraScriptHost::C4ExtraScriptHost(C4String *parent_key_name):

View File

@ -23,47 +23,68 @@
#include <C4ComponentHost.h>
#include <C4Aul.h>
// aul script state
enum C4AulScriptState
{
ASS_NONE, // nothing
ASS_PREPARSED, // function list built; CodeSize set
ASS_LINKED, // includes and appends resolved
ASS_PARSED // byte code generated
};
// generic script host for objects
class C4ScriptHost : public C4AulScript
class C4ScriptHost
{
public:
~C4ScriptHost();
bool Delete() { return false; } // do NOT delete this - it's just a class member!
virtual ~C4ScriptHost();
virtual bool Delete() { return false; } // do NOT delete this - it's just a class member!
void Clear();
virtual bool Load(C4Group &hGroup, const char *szFilename,
const char *szLanguage, C4LangStringTable *pLocalTable);
virtual bool LoadData(const char *szFilename, const char *szData, class C4LangStringTable *pLocalTable);
void Reg2List(C4AulScriptEngine *pEngine); // reg to linked list
virtual C4PropListStatic * GetPropList() { return 0; }
const char *GetScript() const { return Script.getData(); }
virtual C4ScriptHost * GetScriptHost() { return this; }
bool IsReady() { return State == ASS_PARSED; } // whether script calls may be done
// Translate a string using the script's lang table
std::string Translate(const std::string &text) const;
std::list<C4ScriptHost *> SourceScripts;
StdCopyStrBuf ScriptName; // script name
protected:
C4ScriptHost();
void SetError(const char *szMessage);
void Unreg(); // remove from list
void MakeScript();
bool ReloadScript(const char *szPath, const char *szLanguage);
virtual bool ReloadScript(const char *szPath, const char *szLanguage);
C4ComponentHost ComponentHost;
bool Preparse(); // preparse script; return if successfull
virtual bool Parse(); // parse preparsed script; return if successfull
virtual void UnLink(); // reset to unlinked state
void Warn(const char *pMsg, ...) GNUC_FORMAT_ATTRIBUTE_O;
C4AulScriptEngine *Engine; //owning engine
C4ScriptHost *Prev, *Next; // tree structure
std::list<StdCopyStrBuf> Includes; // include list
std::list<StdCopyStrBuf> Appends; // append list
virtual void AddEngineFunctions() {}; // add any engine functions specific to this script host
void CopyPropList(C4Set<C4Property> & from, C4PropListStatic * to);
bool ResolveIncludes(C4DefList *rDefs); // resolve includes
bool ResolveAppends(C4DefList *rDefs); // resolve appends
virtual bool ResolveIncludes(C4DefList *rDefs); // resolve includes
virtual bool ResolveAppends(C4DefList *rDefs); // resolve appends
bool Resolving; // set while include-resolving, to catch circular includes
bool IncludesResolved;
StdStrBuf Script; // script
C4LangStringTable *stringTable;
C4ValueMapNames LocalNamed;
C4Set<C4Property> LocalValues;
C4AulScriptState State; // script state
friend class C4AulParse;
friend class C4AulScriptFunc;
friend class C4AulProfiler;
friend class C4AulScriptEngine;
friend class C4AulDebug;
};

View File

@ -303,7 +303,7 @@ C4String *C4StringTable::RegString(StdStrBuf String)
return new C4String(String);
}
C4String *C4StringTable::FindString(const char *strString)
C4String *C4StringTable::FindString(const char *strString) const
{
return Set.Get(strString);
}

View File

@ -483,7 +483,7 @@ public:
C4String *RegString(StdStrBuf String);
C4String *RegString(const char * s) { return RegString(StdStrBuf(s)); }
// Find existing C4String
C4String *FindString(const char *strString);
C4String *FindString(const char *strString) const;
private:
C4Set<C4String *> Set;

View File

@ -141,13 +141,6 @@ public:
// Change and set Type to int in case it was nil or bool before
// Use with care: These don't handle int32_t overflow
C4Value & operator += (int32_t by) { Data.Int += by; Type=C4V_Int; return *this; }
C4Value & operator -= (int32_t by) { Data.Int -= by; Type=C4V_Int; return *this; }
C4Value & operator *= (int32_t by) { Data.Int *= by; Type=C4V_Int; return *this; }
C4Value & operator /= (int32_t by) { Data.Int /= by; Type=C4V_Int; return *this; }
C4Value & operator %= (int32_t by) { Data.Int %= by; Type=C4V_Int; return *this; }
C4Value & operator &= (int32_t by) { Data.Int &= by; Type=C4V_Int; return *this; }
C4Value & operator ^= (int32_t by) { Data.Int ^= by; Type=C4V_Int; return *this; }
C4Value & operator |= (int32_t by) { Data.Int |= by; Type=C4V_Int; return *this; }
C4Value & operator ++ () { Data.Int++; Type=C4V_Int; return *this; }
C4Value operator ++ (int) { C4Value old = *this; ++(*this); return old; }
C4Value & operator -- () { Data.Int--; Type=C4V_Int; return *this; }

View File

@ -37,7 +37,6 @@ class C4ValueArray;
class C4Def;
class C4Effect;
class C4AulParSet;
class C4AulScript;
class C4AulScriptFunc;
#include "script/C4Value.h"

View File

@ -81,9 +81,9 @@ TEST(DirectExecTest, HostUnmodifedByParseTest)
TestHost host2 = host;
host.test_equality(host2);
char szScript[] = "8*5";
C4AulScriptFunc *pFunc = new C4AulScriptFunc(host.GetPropList(), host.GetScriptHost(), 0, szScript);
C4AulScriptFunc *pFunc = new C4AulScriptFunc(host.GetPropList(), nullptr, nullptr, szScript);
host.test_equality(host2);
pFunc->ParseFn();
pFunc->ParseFn(&::ScriptEngine);
host.test_equality(host2);
delete pFunc;
}

View File

@ -32,7 +32,7 @@ TEST_F(AulPredefFunctionTest, Translate)
{
// Expect the engine to warn when it can't find a translation
LogMock log;
EXPECT_CALL(log, DebugLogF(R"(WARNING: Translate: no translation for string "%s")", _));
EXPECT_CALL(log, DebugLogF(testing::StrEq(R"(WARNING: Translate: no translation for string "%s")"), _));
EXPECT_CALL(log, DebugLog(StartsWith(" by: "))).Times(AnyNumber()); // ignore stack trace
EXPECT_EQ(C4VString("a"), RunExpr("Translate(\"a\")"));

View File

@ -123,3 +123,9 @@ TEST_F(AulTest, Eval)
EXPECT_EQ(C4VInt(42), RunCode("local i = 42; func Main() { return eval(\"this.i\"); }", false));
EXPECT_EQ(C4VInt(42), RunCode("local i; func Main() { eval(\"this.i = 42\"); return i; }", false));
}
TEST_F(AulTest, Vars)
{
EXPECT_EQ(C4VInt(42), RunCode("var i = 21; i = i + i; return i;"));
EXPECT_EQ(C4VInt(42), RunCode("var i = -42; i = Abs(i); return i;"));
}