Implement direct execution of whole function definitions

This implements the changes made in c6e4bfd on the AST-based parser.
directional-lights
Nicolas Hake 2016-10-18 14:47:58 +02:00
parent 1a8db9e109
commit 021a6e89f5
6 changed files with 72 additions and 25 deletions

View File

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

View File

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

View File

@ -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(); }

View File

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

View File

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

View File

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