forked from Mirrors/openclonk
Implement direct execution of whole function definitions
This implements the changes made in c6e4bfd
on the AST-based parser.
directional-lights
parent
1a8db9e109
commit
021a6e89f5
|
@ -139,6 +139,7 @@ class C4AulCompiler::PreparseAstVisitor : public ::aul::DefaultRecursiveVisitor
|
|||
|
||||
public:
|
||||
PreparseAstVisitor(C4ScriptHost *host, C4ScriptHost *source_host, C4AulScriptFunc *func = nullptr) : target_host(host), host(source_host), Fn(func) {}
|
||||
explicit PreparseAstVisitor(C4AulScriptFunc *func) : Fn(func), target_host(func->pOrgScript), host(target_host) {}
|
||||
|
||||
virtual ~PreparseAstVisitor() {}
|
||||
|
||||
|
@ -248,7 +249,14 @@ public:
|
|||
virtual void visit(const ::aul::ast::FunctionExpr *n) override;
|
||||
|
||||
template<class T>
|
||||
void EmitFunctionCode(const T *n) { EmitFunctionCode(n, n); }
|
||||
void EmitFunctionCode(const T *n)
|
||||
{
|
||||
// This dynamic_cast resolves the problem where we have a Function*
|
||||
// and want to emit code to it. All classes derived from Function
|
||||
// are also ultimately derived from Node, so this call is fine
|
||||
// without any additional checking.
|
||||
EmitFunctionCode(n, dynamic_cast<const ::aul::ast::Node*>(n));
|
||||
}
|
||||
|
||||
private:
|
||||
void EmitFunctionCode(const ::aul::ast::Function *f, const ::aul::ast::Node *n);
|
||||
|
@ -368,10 +376,16 @@ void C4AulCompiler::Compile(C4ScriptHost *host, C4ScriptHost *source_host, const
|
|||
|
||||
void C4AulCompiler::Compile(C4AulScriptFunc *func, const ::aul::ast::Function *def)
|
||||
{
|
||||
CodegenAstVisitor v(func);
|
||||
// Don't visit the whole definition here; that would create a new function
|
||||
// and we don't want that.
|
||||
def->body->accept(&v);
|
||||
{
|
||||
// Don't visit the whole definition here; that would create a new function
|
||||
// and we don't want that.
|
||||
PreparseAstVisitor v(func);
|
||||
def->body->accept(&v);
|
||||
}
|
||||
{
|
||||
CodegenAstVisitor v(func);
|
||||
v.EmitFunctionCode(def);
|
||||
}
|
||||
}
|
||||
|
||||
#define ENSURE_COND(cond, failmsg) do { if (!(cond)) throw Error(target_host, host, n, Fn, failmsg); } while (0)
|
||||
|
@ -413,11 +427,16 @@ void C4AulCompiler::PreparseAstVisitor::visit(const ::aul::ast::VarDecl *n)
|
|||
if (!Fn)
|
||||
throw Error(target_host, host, n, Fn, "internal error: function-local var declaration outside of function");
|
||||
|
||||
if (target_host->Engine->GlobalNamedNames.GetItemNr(cname) >= 0 || target_host->Engine->GlobalConstNames.GetItemNr(cname) >= 0)
|
||||
Warn(target_host, host, n, Fn, "function-local variable hides a global variable: %s", cname);
|
||||
C4String *s = ::Strings.FindString(cname);
|
||||
if (s && target_host->GetPropList()->HasProperty(s))
|
||||
Warn(target_host, host, n, Fn, "function-local variable hides an object-local variable: %s", cname);
|
||||
if (target_host)
|
||||
{
|
||||
// if target_host is unset, we're parsing this func for direct execution,
|
||||
// in which case we don't want to warn about variable hiding.
|
||||
if (target_host->Engine->GlobalNamedNames.GetItemNr(cname) >= 0 || target_host->Engine->GlobalConstNames.GetItemNr(cname) >= 0)
|
||||
Warn(target_host, host, n, Fn, "function-local variable hides a global variable: %s", cname);
|
||||
C4String *s = ::Strings.FindString(cname);
|
||||
if (s && target_host->GetPropList()->HasProperty(s))
|
||||
Warn(target_host, host, n, Fn, "function-local variable hides an object-local variable: %s", cname);
|
||||
}
|
||||
Fn->VarNamed.AddName(cname);
|
||||
break;
|
||||
}
|
||||
|
@ -1130,7 +1149,7 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::CallExpr *n)
|
|||
// if this is a function without explicit context, we resolve it
|
||||
if (!callee)
|
||||
callee = Fn->Parent->GetFunc(cname);
|
||||
if (!callee)
|
||||
if (!callee && target_host)
|
||||
callee = target_host->Engine->GetFunc(cname);
|
||||
|
||||
if (callee)
|
||||
|
@ -1168,6 +1187,7 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::CallExpr *n)
|
|||
}
|
||||
}
|
||||
|
||||
// Check passed parameters for this call (as far as possible)
|
||||
std::vector<C4V_Type> expected_par_types;
|
||||
if (n->context)
|
||||
{
|
||||
|
@ -1175,7 +1195,7 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::CallExpr *n)
|
|||
// Since we don't know the context in which this call will happen at
|
||||
// runtime, we'll check whether all available functions with the same
|
||||
// name agree on their parameters.
|
||||
const C4AulFunc *candidate = target_host->Engine->GetFirstFunc(cname);
|
||||
const C4AulFunc *candidate = target_host ? target_host->Engine->GetFirstFunc(cname) : nullptr;
|
||||
if (candidate)
|
||||
{
|
||||
expected_par_types.assign(candidate->GetParType(), candidate->GetParType() + candidate->GetParCount());
|
||||
|
|
|
@ -1013,7 +1013,7 @@ void C4AulProfiler::Show()
|
|||
// done!
|
||||
}
|
||||
|
||||
C4Value C4AulExec::DirectExec(C4PropList *p, const char *szScript, const char *szContext, bool fPassErrors, C4AulScriptContext* context)
|
||||
C4Value C4AulExec::DirectExec(C4PropList *p, const char *szScript, const char *szContext, bool fPassErrors, C4AulScriptContext* context, bool parse_function)
|
||||
{
|
||||
#ifdef DEBUGREC_SCRIPT
|
||||
if (Config.General.DebugRec)
|
||||
|
@ -1035,7 +1035,16 @@ C4Value C4AulExec::DirectExec(C4PropList *p, const char *szScript, const char *s
|
|||
// Parse function
|
||||
try
|
||||
{
|
||||
pFunc->ParseFn(&::ScriptEngine, context);
|
||||
if (parse_function)
|
||||
{
|
||||
// Expect a full function (e.g. "func foo() { return bar(); }")
|
||||
pFunc->ParseDirectExecFunc(&::ScriptEngine, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Expect a single statement (e.g. "bar()")
|
||||
pFunc->ParseDirectExecStatement(&::ScriptEngine, context);
|
||||
}
|
||||
C4AulParSet Pars;
|
||||
C4Value vRetVal(Exec(pFunc.get(), p, Pars.Par, fPassErrors));
|
||||
// profiler
|
||||
|
|
|
@ -78,7 +78,7 @@ private:
|
|||
friend class C4AulProfiler;
|
||||
public:
|
||||
C4Value Exec(C4AulScriptFunc *pSFunc, C4PropList * p, C4Value pPars[], bool fPassErrors);
|
||||
C4Value DirectExec(C4PropList *p, const char *szScript, const char *szContext, bool fPassErrors = false, C4AulScriptContext* context = NULL);
|
||||
C4Value DirectExec(C4PropList *p, const char *szScript, const char *szContext, bool fPassErrors = false, C4AulScriptContext* context = NULL, bool parse_function = false);
|
||||
|
||||
void StartTrace();
|
||||
inline void StartDirectExec() { if (fProfiling) tDirectExecStart = C4TimeMilliseconds::Now(); }
|
||||
|
|
|
@ -757,25 +757,42 @@ void C4AulParse::UnexpectedToken(const char * Expected)
|
|||
throw C4AulParseError(this, FormatString("%s expected, but found %s", Expected, GetTokenName(TokenType)).getData());
|
||||
}
|
||||
|
||||
void C4AulScriptFunc::ParseFn(C4AulScriptEngine *Engine, C4AulScriptContext* context)
|
||||
void C4AulScriptFunc::ParseDirectExecFunc(C4AulScriptEngine *Engine, C4AulScriptContext* context)
|
||||
{
|
||||
ClearCode();
|
||||
// parse
|
||||
C4AulParse state(this, context, Engine);
|
||||
auto func = state.Parse_DirectExec(Script);
|
||||
auto func = state.Parse_DirectExec(Script, true);
|
||||
C4AulCompiler::Compile(this, func.get());
|
||||
}
|
||||
|
||||
std::unique_ptr<::aul::ast::FunctionDecl> C4AulParse::Parse_DirectExec(const char *code)
|
||||
void C4AulScriptFunc::ParseDirectExecStatement(C4AulScriptEngine *Engine, C4AulScriptContext* context)
|
||||
{
|
||||
ClearCode();
|
||||
// parse
|
||||
C4AulParse state(this, context, Engine);
|
||||
auto func = state.Parse_DirectExec(Script, false);
|
||||
C4AulCompiler::Compile(this, func.get());
|
||||
}
|
||||
|
||||
std::unique_ptr<::aul::ast::FunctionDecl> C4AulParse::Parse_DirectExec(const char *code, bool whole_function)
|
||||
{
|
||||
// get first token
|
||||
Shift();
|
||||
auto expr = Parse_Expression();
|
||||
Match(ATT_EOF);
|
||||
// Synthesize a wrapping function which we can call
|
||||
auto func = std::make_unique<::aul::ast::FunctionDecl>("$internal$eval");
|
||||
func->body = std::make_unique<::aul::ast::Block>();
|
||||
func->body->children.push_back(std::make_unique<::aul::ast::Return>(std::move(expr)));
|
||||
std::unique_ptr<::aul::ast::FunctionDecl> func;
|
||||
if (whole_function)
|
||||
{
|
||||
func = Parse_ToplevelFunctionDecl();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto expr = Parse_Expression();
|
||||
func = std::make_unique<::aul::ast::FunctionDecl>("$internal$eval");
|
||||
func->body = std::make_unique<::aul::ast::Block>();
|
||||
func->body->children.push_back(std::make_unique<::aul::ast::Return>(std::move(expr)));
|
||||
}
|
||||
Match(ATT_EOF);
|
||||
return func;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ public:
|
|||
C4AulParse(class C4ScriptHost *host);
|
||||
C4AulParse(C4AulScriptFunc * Fn, C4AulScriptContext* context, C4AulScriptEngine *Engine);
|
||||
~C4AulParse();
|
||||
std::unique_ptr<::aul::ast::FunctionDecl> Parse_DirectExec(const char *code);
|
||||
std::unique_ptr<::aul::ast::FunctionDecl> Parse_DirectExec(const char *code, bool whole_function);
|
||||
std::unique_ptr<::aul::ast::Script> Parse_Script(C4ScriptHost *);
|
||||
|
||||
private:
|
||||
|
|
|
@ -207,7 +207,8 @@ public:
|
|||
C4AulScriptFunc(C4PropListStatic * Parent, const C4AulScriptFunc &FromFunc); // copy script/code, etc from given func
|
||||
~C4AulScriptFunc();
|
||||
|
||||
void ParseFn(C4AulScriptEngine *Engine, C4AulScriptContext* context = NULL);
|
||||
void ParseDirectExecFunc(C4AulScriptEngine *Engine, C4AulScriptContext* context = NULL);
|
||||
void ParseDirectExecStatement(C4AulScriptEngine *Engine, C4AulScriptContext* context = NULL);
|
||||
|
||||
virtual bool GetPublic() const { return true; }
|
||||
virtual int GetParCount() const { return ParCount; }
|
||||
|
|
Loading…
Reference in New Issue