forked from Mirrors/openclonk
Aul: Gracefully handle errors in codegen (#1840)
By continuing to generate bytecode even after an error is found, we're able to find more syntax errors and will also be able to keep the value stack at the expected height.directional-lights
parent
ff0325dfac
commit
eda6cc9c7f
|
@ -151,6 +151,7 @@ public:
|
||||||
virtual void visit(const ::aul::ast::ParExpr *n) override;
|
virtual void visit(const ::aul::ast::ParExpr *n) override;
|
||||||
virtual void visit(const ::aul::ast::AppendtoPragma *n) override;
|
virtual void visit(const ::aul::ast::AppendtoPragma *n) override;
|
||||||
virtual void visit(const ::aul::ast::IncludePragma *n) override;
|
virtual void visit(const ::aul::ast::IncludePragma *n) override;
|
||||||
|
virtual void visit(const ::aul::ast::Script *n) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class C4AulCompiler::CodegenAstVisitor : public ::aul::DefaultRecursiveVisitor
|
class C4AulCompiler::CodegenAstVisitor : public ::aul::DefaultRecursiveVisitor
|
||||||
|
@ -212,6 +213,49 @@ class C4AulCompiler::CodegenAstVisitor : public ::aul::DefaultRecursiveVisitor
|
||||||
void RemoveLastBCC();
|
void RemoveLastBCC();
|
||||||
C4AulBCC MakeSetter(const char *SPos, bool fLeaveValue);
|
C4AulBCC MakeSetter(const char *SPos, bool fLeaveValue);
|
||||||
|
|
||||||
|
void HandleError(const C4AulError &e)
|
||||||
|
{
|
||||||
|
AddBCC(nullptr, AB_ERR, (intptr_t)::Strings.RegString(e.what()));
|
||||||
|
target_host->Engine->ErrorHandler->OnError(e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
bool SafeVisit(const T &node)
|
||||||
|
{
|
||||||
|
// Swallows exceptions during evaluation of node. Use if you want to
|
||||||
|
// keep doing syntax checks for subsequent children. (Generated code
|
||||||
|
// will cause a runtime error if executed.)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
node->accept(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (C4AulParseError &e)
|
||||||
|
{
|
||||||
|
HandleError(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StackGuard
|
||||||
|
{
|
||||||
|
// Ensures that the Aul value stack ends up at the expected height
|
||||||
|
CodegenAstVisitor *parent;
|
||||||
|
const int32_t target_stack_height;
|
||||||
|
public:
|
||||||
|
explicit StackGuard(CodegenAstVisitor *parent, int32_t offset = 0) : parent(parent), target_stack_height(parent->stack_height + offset)
|
||||||
|
{}
|
||||||
|
~StackGuard()
|
||||||
|
{
|
||||||
|
assert(parent->stack_height == target_stack_height);
|
||||||
|
if (parent->stack_height != target_stack_height)
|
||||||
|
{
|
||||||
|
parent->HandleError(Error(parent->target_host, parent->host, nullptr, parent->Fn, "internal error: value stack left unbalanced"));
|
||||||
|
parent->AddBCC(nullptr, AB_STACK, target_stack_height - parent->stack_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CodegenAstVisitor(C4ScriptHost *host, C4ScriptHost *source_host) : target_host(host), host(source_host) {}
|
CodegenAstVisitor(C4ScriptHost *host, C4ScriptHost *source_host) : target_host(host), host(source_host) {}
|
||||||
explicit CodegenAstVisitor(C4AulScriptFunc *func) : Fn(func), target_host(func->pOrgScript), host(target_host) {}
|
explicit CodegenAstVisitor(C4AulScriptFunc *func) : Fn(func), target_host(func->pOrgScript), host(target_host) {}
|
||||||
|
@ -502,7 +546,7 @@ void C4AulCompiler::PreparseAstVisitor::visit(const ::aul::ast::FunctionDecl *n)
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DefaultRecursiveVisitor::visit(n);
|
DefaultRecursiveVisitor::visit(n);
|
||||||
Fn = nullptr;
|
Fn = nullptr;
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
|
@ -544,6 +588,21 @@ void C4AulCompiler::PreparseAstVisitor::visit(const ::aul::ast::IncludePragma *n
|
||||||
host->Includes.emplace_back(n->what.c_str());
|
host->Includes.emplace_back(n->what.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void C4AulCompiler::PreparseAstVisitor::visit(const::aul::ast::Script * n)
|
||||||
|
{
|
||||||
|
for (const auto &d : n->declarations)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
d->accept(this);
|
||||||
|
}
|
||||||
|
catch (C4AulParseError &e)
|
||||||
|
{
|
||||||
|
target_host->Engine->GetErrorHandler()->OnError(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int C4AulCompiler::CodegenAstVisitor::GetStackValue(C4AulBCCType eType, intptr_t X)
|
int C4AulCompiler::CodegenAstVisitor::GetStackValue(C4AulBCCType eType, intptr_t X)
|
||||||
{
|
{
|
||||||
switch (eType)
|
switch (eType)
|
||||||
|
@ -894,27 +953,31 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::Noop *) {}
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::StringLit *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::StringLit *n)
|
||||||
{
|
{
|
||||||
|
StackGuard g(this, 1);
|
||||||
AddBCC(n->loc, AB_STRING, (intptr_t)::Strings.RegString(n->value.c_str()));
|
AddBCC(n->loc, AB_STRING, (intptr_t)::Strings.RegString(n->value.c_str()));
|
||||||
type_of_stack_top = C4V_String;
|
type_of_stack_top = C4V_String;
|
||||||
}
|
}
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::IntLit *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::IntLit *n)
|
||||||
{
|
{
|
||||||
|
StackGuard g(this, 1);
|
||||||
AddBCC(n->loc, AB_INT, n->value);
|
AddBCC(n->loc, AB_INT, n->value);
|
||||||
type_of_stack_top = C4V_Int;
|
type_of_stack_top = C4V_Int;
|
||||||
}
|
}
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::BoolLit *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::BoolLit *n)
|
||||||
{
|
{
|
||||||
|
StackGuard g(this, 1);
|
||||||
AddBCC(n->loc, AB_BOOL, n->value);
|
AddBCC(n->loc, AB_BOOL, n->value);
|
||||||
type_of_stack_top = C4V_Bool;
|
type_of_stack_top = C4V_Bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ArrayLit *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ArrayLit *n)
|
||||||
{
|
{
|
||||||
|
StackGuard g(this, 1);
|
||||||
for (const auto &e : n->values)
|
for (const auto &e : n->values)
|
||||||
{
|
{
|
||||||
e->accept(this);
|
SafeVisit(e);
|
||||||
}
|
}
|
||||||
AddBCC(n->loc, AB_NEW_ARRAY, n->values.size());
|
AddBCC(n->loc, AB_NEW_ARRAY, n->values.size());
|
||||||
type_of_stack_top = C4V_Array;
|
type_of_stack_top = C4V_Array;
|
||||||
|
@ -922,10 +985,12 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ArrayLit *n)
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ProplistLit *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ProplistLit *n)
|
||||||
{
|
{
|
||||||
|
StackGuard g(this, 1);
|
||||||
for (const auto &e : n->values)
|
for (const auto &e : n->values)
|
||||||
{
|
{
|
||||||
|
StackGuard g(this, 2);
|
||||||
AddBCC(n->loc, AB_STRING, (intptr_t)::Strings.RegString(e.first.c_str()));
|
AddBCC(n->loc, AB_STRING, (intptr_t)::Strings.RegString(e.first.c_str()));
|
||||||
e.second->accept(this);
|
SafeVisit(e.second);
|
||||||
}
|
}
|
||||||
AddBCC(n->loc, AB_NEW_PROPLIST, n->values.size());
|
AddBCC(n->loc, AB_NEW_PROPLIST, n->values.size());
|
||||||
type_of_stack_top = C4V_PropList;
|
type_of_stack_top = C4V_PropList;
|
||||||
|
@ -933,18 +998,21 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ProplistLit *n)
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::NilLit *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::NilLit *n)
|
||||||
{
|
{
|
||||||
|
StackGuard g(this, 1);
|
||||||
AddBCC(n->loc, AB_NIL);
|
AddBCC(n->loc, AB_NIL);
|
||||||
type_of_stack_top = C4V_Nil;
|
type_of_stack_top = C4V_Nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ThisLit *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ThisLit *n)
|
||||||
{
|
{
|
||||||
|
StackGuard g(this, 1);
|
||||||
AddBCC(n->loc, AB_THIS);
|
AddBCC(n->loc, AB_THIS);
|
||||||
type_of_stack_top = C4V_PropList;
|
type_of_stack_top = C4V_PropList;
|
||||||
}
|
}
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::VarExpr *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::VarExpr *n)
|
||||||
{
|
{
|
||||||
|
StackGuard g(this, 1);
|
||||||
assert(Fn);
|
assert(Fn);
|
||||||
C4Value dummy;
|
C4Value dummy;
|
||||||
const char *cname = n->identifier.c_str();
|
const char *cname = n->identifier.c_str();
|
||||||
|
@ -1004,18 +1072,22 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::VarExpr *n)
|
||||||
case C4V_Function:
|
case C4V_Function:
|
||||||
AddBCC(n->loc, AB_CFUNCTION, reinterpret_cast<intptr_t>(v._getFunction()));
|
AddBCC(n->loc, AB_CFUNCTION, reinterpret_cast<intptr_t>(v._getFunction()));
|
||||||
default:
|
default:
|
||||||
|
AddBCC(n->loc, AB_NIL);
|
||||||
throw Error(target_host, host, n, Fn, "internal error: global constant of unexpected type: %s (of type %s)", cname, v.GetTypeName());
|
throw Error(target_host, host, n, Fn, "internal error: global constant of unexpected type: %s (of type %s)", cname, v.GetTypeName());
|
||||||
}
|
}
|
||||||
type_of_stack_top = v.GetType();
|
type_of_stack_top = v.GetType();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
AddBCC(n->loc, AB_NIL);
|
||||||
throw Error(target_host, host, n, Fn, "symbol not found in any symbol table: %s", cname);
|
throw Error(target_host, host, n, Fn, "symbol not found in any symbol table: %s", cname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::UnOpExpr *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::UnOpExpr *n)
|
||||||
{
|
{
|
||||||
|
StackGuard g(this, 1);
|
||||||
|
|
||||||
n->operand->accept(this);
|
n->operand->accept(this);
|
||||||
const auto &op = C4ScriptOpMap[n->op];
|
const auto &op = C4ScriptOpMap[n->op];
|
||||||
if (op.Changer)
|
if (op.Changer)
|
||||||
|
@ -1038,7 +1110,9 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::UnOpExpr *n)
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::BinOpExpr *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::BinOpExpr *n)
|
||||||
{
|
{
|
||||||
n->lhs->accept(this);
|
StackGuard g(this, 1);
|
||||||
|
|
||||||
|
SafeVisit(n->lhs);
|
||||||
|
|
||||||
const auto &op = C4ScriptOpMap[n->op];
|
const auto &op = C4ScriptOpMap[n->op];
|
||||||
if (op.Code == AB_JUMPAND || op.Code == AB_JUMPOR || op.Code == AB_JUMPNNIL)
|
if (op.Code == AB_JUMPAND || op.Code == AB_JUMPOR || op.Code == AB_JUMPNNIL)
|
||||||
|
@ -1047,19 +1121,26 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::BinOpExpr *n)
|
||||||
// because we don't want to evaluate their rhs operand when the
|
// because we don't want to evaluate their rhs operand when the
|
||||||
// lhs one already decided the result
|
// lhs one already decided the result
|
||||||
int jump = AddBCC(n->loc, op.Code);
|
int jump = AddBCC(n->loc, op.Code);
|
||||||
n->rhs->accept(this);
|
SafeVisit(n->rhs);
|
||||||
UpdateJump(jump, AddJumpTarget());
|
UpdateJump(jump, AddJumpTarget());
|
||||||
}
|
}
|
||||||
else if (op.Changer)
|
else if (op.Changer)
|
||||||
{
|
{
|
||||||
C4AulBCC setter = MakeSetter(n->loc, true);
|
try
|
||||||
n->rhs->accept(this);
|
{
|
||||||
AddBCC(n->loc, op.Code);
|
C4AulBCC setter = MakeSetter(n->loc, true);
|
||||||
AddBCC(n->loc, setter);
|
SafeVisit(n->rhs);
|
||||||
|
AddBCC(n->loc, op.Code);
|
||||||
|
AddBCC(n->loc, setter);
|
||||||
|
}
|
||||||
|
catch (C4AulParseError &e)
|
||||||
|
{
|
||||||
|
HandleError(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
n->rhs->accept(this);
|
SafeVisit(n->rhs);
|
||||||
AddBCC(n->loc, op.Code, 0);
|
AddBCC(n->loc, op.Code, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1068,18 +1149,26 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::BinOpExpr *n)
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::AssignmentExpr *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::AssignmentExpr *n)
|
||||||
{
|
{
|
||||||
n->lhs->accept(this);
|
StackGuard g(this, 1);
|
||||||
C4AulBCC setter = MakeSetter(n->loc, false);
|
SafeVisit(n->lhs);
|
||||||
n->rhs->accept(this);
|
try
|
||||||
AddBCC(n->loc, setter);
|
{
|
||||||
|
C4AulBCC setter = MakeSetter(n->loc, false);
|
||||||
|
SafeVisit(n->rhs);
|
||||||
|
AddBCC(n->loc, setter);
|
||||||
|
}
|
||||||
|
catch (C4AulParseError &e)
|
||||||
|
{
|
||||||
|
HandleError(e);
|
||||||
|
}
|
||||||
// Assignment does not change the type of the variable
|
// Assignment does not change the type of the variable
|
||||||
}
|
}
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::SubscriptExpr *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::SubscriptExpr *n)
|
||||||
{
|
{
|
||||||
n->object->accept(this);
|
StackGuard g(this, 1);
|
||||||
n->index->accept(this);
|
SafeVisit(n->object);
|
||||||
|
SafeVisit(n->index);
|
||||||
AddBCC(n->loc, AB_ARRAYA);
|
AddBCC(n->loc, AB_ARRAYA);
|
||||||
|
|
||||||
// FIXME: Check if the subscripted object is a literal and if so, retrieve type
|
// FIXME: Check if the subscripted object is a literal and if so, retrieve type
|
||||||
|
@ -1088,9 +1177,10 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::SubscriptExpr *n)
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::SliceExpr *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::SliceExpr *n)
|
||||||
{
|
{
|
||||||
n->object->accept(this);
|
StackGuard g(this, 1);
|
||||||
n->start->accept(this);
|
SafeVisit(n->object);
|
||||||
n->end->accept(this);
|
SafeVisit(n->start);
|
||||||
|
SafeVisit(n->end);
|
||||||
AddBCC(n->loc, AB_ARRAY_SLICE);
|
AddBCC(n->loc, AB_ARRAY_SLICE);
|
||||||
|
|
||||||
type_of_stack_top = C4V_Array;
|
type_of_stack_top = C4V_Array;
|
||||||
|
@ -1114,17 +1204,31 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::CallExpr *n)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (n->callee == C4AUL_Inherited || n->callee == C4AUL_SafeInherited)
|
||||||
|
{
|
||||||
|
// inherited can only be called within the same context
|
||||||
|
if (n->context)
|
||||||
|
{
|
||||||
|
throw Error(target_host, host, n, Fn, "\"%s\" can't be called in a different context", cname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n->callee == C4AUL_Inherited && !Fn->OwnerOverloaded)
|
||||||
|
{
|
||||||
|
throw Error(target_host, host, n, Fn, "inherited function not found (use " C4AUL_SafeInherited " to disable this message)");
|
||||||
|
}
|
||||||
|
|
||||||
const auto pre_call_stack = stack_height;
|
const auto pre_call_stack = stack_height;
|
||||||
|
|
||||||
if (n->context)
|
if (n->context)
|
||||||
n->context->accept(this);
|
SafeVisit(n->context);
|
||||||
|
|
||||||
std::vector<C4V_Type> known_par_types;
|
std::vector<C4V_Type> known_par_types;
|
||||||
known_par_types.reserve(n->args.size());
|
known_par_types.reserve(n->args.size());
|
||||||
|
|
||||||
for (const auto &arg : n->args)
|
for (const auto &arg : n->args)
|
||||||
{
|
{
|
||||||
arg->accept(this);
|
SafeVisit(arg);
|
||||||
known_par_types.push_back(type_of_stack_top);
|
known_par_types.push_back(type_of_stack_top);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1133,28 +1237,7 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::CallExpr *n)
|
||||||
// Special handling for the overload chain
|
// Special handling for the overload chain
|
||||||
if (n->callee == C4AUL_Inherited || n->callee == C4AUL_SafeInherited)
|
if (n->callee == C4AUL_Inherited || n->callee == C4AUL_SafeInherited)
|
||||||
{
|
{
|
||||||
if (n->context)
|
|
||||||
{
|
|
||||||
throw Error(target_host, host, n, Fn, "\"%s\" can't be called in a different context", cname);
|
|
||||||
}
|
|
||||||
callee = Fn->OwnerOverloaded;
|
callee = Fn->OwnerOverloaded;
|
||||||
if (!callee)
|
|
||||||
{
|
|
||||||
if (n->callee == C4AUL_SafeInherited)
|
|
||||||
{
|
|
||||||
// pop all args off the stack
|
|
||||||
if (!n->args.empty())
|
|
||||||
AddBCC(n->loc, AB_STACK, -(intptr_t)n->args.size());
|
|
||||||
// and "return" nil
|
|
||||||
AddBCC(n->loc, AB_NIL);
|
|
||||||
type_of_stack_top = C4V_Nil;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw Error(target_host, host, n, Fn, "inherited function not found (use " C4AUL_SafeInherited " to disable this message)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t fn_argc = C4AUL_MAX_Par;
|
size_t fn_argc = C4AUL_MAX_Par;
|
||||||
|
@ -1167,9 +1250,24 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::CallExpr *n)
|
||||||
callee = target_host->Engine->GetFunc(cname);
|
callee = target_host->Engine->GetFunc(cname);
|
||||||
|
|
||||||
if (callee)
|
if (callee)
|
||||||
|
{
|
||||||
fn_argc = callee->GetParCount();
|
fn_argc = callee->GetParCount();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
throw Error(target_host, host, n, Fn, "called function not found: %s", cname);
|
{
|
||||||
|
// pop all args off the stack
|
||||||
|
if (!n->args.empty())
|
||||||
|
AddBCC(n->loc, AB_STACK, -(intptr_t)n->args.size());
|
||||||
|
// and "return" nil
|
||||||
|
AddBCC(n->loc, AB_NIL);
|
||||||
|
type_of_stack_top = C4V_Nil;
|
||||||
|
|
||||||
|
if (n->callee != C4AUL_SafeInherited)
|
||||||
|
{
|
||||||
|
HandleError(Error(target_host, host, n, Fn, "called function not found: %s", cname));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n->args.size() > fn_argc)
|
if (n->args.size() > fn_argc)
|
||||||
|
@ -1266,7 +1364,9 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::CallExpr *n)
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ParExpr *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ParExpr *n)
|
||||||
{
|
{
|
||||||
n->arg->accept(this);
|
StackGuard g(this, 1);
|
||||||
|
|
||||||
|
SafeVisit(n->arg);
|
||||||
AddBCC(n->loc, AB_PAR);
|
AddBCC(n->loc, AB_PAR);
|
||||||
type_of_stack_top = C4V_Any;
|
type_of_stack_top = C4V_Any;
|
||||||
}
|
}
|
||||||
|
@ -1275,17 +1375,20 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::Block *n)
|
||||||
{
|
{
|
||||||
for (const auto &s : n->children)
|
for (const auto &s : n->children)
|
||||||
{
|
{
|
||||||
const auto pre_statement_stack = stack_height;
|
StackGuard g(this, 0);
|
||||||
s->accept(this);
|
if (SafeVisit(s))
|
||||||
// If the statement has left a stack value, pop it off
|
{
|
||||||
MaybePopValueOf(s);
|
// If the statement has left a stack value, pop it off
|
||||||
assert(pre_statement_stack == stack_height);
|
MaybePopValueOf(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::Return *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::Return *n)
|
||||||
{
|
{
|
||||||
n->value->accept(this);
|
StackGuard g(this, 0);
|
||||||
|
|
||||||
|
SafeVisit(n->value);
|
||||||
AddBCC(n->loc, AB_RETURN);
|
AddBCC(n->loc, AB_RETURN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1356,28 +1459,29 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::ForLoop *n)
|
||||||
|
|
||||||
if (n->init)
|
if (n->init)
|
||||||
{
|
{
|
||||||
n->init->accept(this);
|
if (SafeVisit(n->init))
|
||||||
MaybePopValueOf(n->init);
|
MaybePopValueOf(n->init);
|
||||||
}
|
}
|
||||||
PushLoop();
|
PushLoop();
|
||||||
int cond = AddJumpTarget();
|
int cond = AddJumpTarget();
|
||||||
if (n->cond)
|
if (n->cond)
|
||||||
{
|
{
|
||||||
n->cond->accept(this);
|
SafeVisit(n->cond);
|
||||||
active_loops.top().breaks.push_back(AddBCC(n->cond->loc, AB_CONDN));
|
active_loops.top().breaks.push_back(AddBCC(n->cond->loc, AB_CONDN));
|
||||||
}
|
}
|
||||||
|
|
||||||
int incr = cond;
|
int incr = cond;
|
||||||
if (n->incr)
|
if (n->incr)
|
||||||
{
|
{
|
||||||
int cond_jump = AddBCC(n->loc, AB_JUMP);
|
int cond_jump = AddBCC(n->loc, AB_JUMP);
|
||||||
incr = AddJumpTarget();
|
incr = AddJumpTarget();
|
||||||
n->incr->accept(this);
|
if (SafeVisit(n->incr))
|
||||||
MaybePopValueOf(n->incr);
|
MaybePopValueOf(n->incr);
|
||||||
AddJumpTo(n->loc, AB_JUMP, cond);
|
AddJumpTo(n->loc, AB_JUMP, cond);
|
||||||
UpdateJump(cond_jump, AddJumpTarget());
|
UpdateJump(cond_jump, AddJumpTarget());
|
||||||
}
|
}
|
||||||
n->body->accept(this);
|
if (SafeVisit(n->body))
|
||||||
MaybePopValueOf(n->body);
|
MaybePopValueOf(n->body);
|
||||||
AddJumpTo(n->loc, AB_JUMP, incr);
|
AddJumpTo(n->loc, AB_JUMP, incr);
|
||||||
PopLoop(incr);
|
PopLoop(incr);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1403,16 +1507,17 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::RangeLoop *n)
|
||||||
if (var_id == -1)
|
if (var_id == -1)
|
||||||
throw Error(target_host, host, n, Fn, "internal error: unable to find variable in foreach: %s", cname);
|
throw Error(target_host, host, n, Fn, "internal error: unable to find variable in foreach: %s", cname);
|
||||||
// Emit code for array
|
// Emit code for array
|
||||||
n->cond->accept(this);
|
SafeVisit(n->cond);
|
||||||
// Emit code for iteration
|
// Emit code for iteration
|
||||||
AddBCC(n->loc, AB_INT, 0);
|
AddBCC(n->loc, AB_INT, 0);
|
||||||
int cond = AddJumpTarget();
|
int cond = AddJumpTarget();
|
||||||
PushLoop();
|
PushLoop();
|
||||||
AddVarAccess(n->loc, AB_FOREACH_NEXT, var_id);
|
AddVarAccess(n->loc, AB_FOREACH_NEXT, var_id);
|
||||||
AddLoopControl(n->loc, Loop::Control::Break); // Will be skipped by AB_FOREACH_NEXT as long as more entries exist
|
AddLoopControl(n->loc, Loop::Control::Break); // Will be skipped by AB_FOREACH_NEXT as long as more entries exist
|
||||||
// Emit body
|
|
||||||
n->body->accept(this);
|
// Emit body
|
||||||
MaybePopValueOf(n->body);
|
if (SafeVisit(n->body))
|
||||||
|
MaybePopValueOf(n->body);
|
||||||
// continue starts the next iteration of the loop
|
// continue starts the next iteration of the loop
|
||||||
AddLoopControl(n->loc, Loop::Control::Continue);
|
AddLoopControl(n->loc, Loop::Control::Continue);
|
||||||
PopLoop(cond);
|
PopLoop(cond);
|
||||||
|
@ -1460,41 +1565,25 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::DoLoop *n)
|
||||||
{
|
{
|
||||||
int body = AddJumpTarget();
|
int body = AddJumpTarget();
|
||||||
PushLoop();
|
PushLoop();
|
||||||
try
|
if (SafeVisit(n->body))
|
||||||
{
|
|
||||||
n->body->accept(this);
|
|
||||||
MaybePopValueOf(n->body);
|
MaybePopValueOf(n->body);
|
||||||
int cond = AddJumpTarget();
|
int cond = AddJumpTarget();
|
||||||
n->cond->accept(this);
|
SafeVisit(n->cond);
|
||||||
AddJumpTo(n->loc, AB_COND, body);
|
AddJumpTo(n->loc, AB_COND, body);
|
||||||
PopLoop(cond);
|
PopLoop(cond);
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
PopLoop(AddJumpTarget());
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::WhileLoop *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::WhileLoop *n)
|
||||||
{
|
{
|
||||||
int cond = AddJumpTarget();
|
int cond = AddJumpTarget();
|
||||||
PushLoop();
|
PushLoop();
|
||||||
try
|
SafeVisit(n->cond);
|
||||||
{
|
active_loops.top().breaks.push_back(AddBCC(n->cond->loc, AB_CONDN));
|
||||||
n->cond->accept(this);
|
if (SafeVisit(n->body))
|
||||||
active_loops.top().breaks.push_back(AddBCC(n->cond->loc, AB_CONDN));
|
|
||||||
n->body->accept(this);
|
|
||||||
MaybePopValueOf(n->body);
|
MaybePopValueOf(n->body);
|
||||||
// continue starts the next iteration of the loop
|
// continue starts the next iteration of the loop
|
||||||
AddLoopControl(n->loc, Loop::Control::Continue);
|
AddLoopControl(n->loc, Loop::Control::Continue);
|
||||||
PopLoop(cond);
|
PopLoop(cond);
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
PopLoop(AddJumpTarget());
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::Break *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::Break *n)
|
||||||
|
@ -1511,17 +1600,17 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::Continue *n)
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::If *n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::If *n)
|
||||||
{
|
{
|
||||||
n->cond->accept(this);
|
SafeVisit(n->cond);
|
||||||
int jump = AddBCC(n->loc, AB_CONDN);
|
int jump = AddBCC(n->loc, AB_CONDN);
|
||||||
n->iftrue->accept(this);
|
if (SafeVisit(n->iftrue))
|
||||||
MaybePopValueOf(n->iftrue);
|
MaybePopValueOf(n->iftrue);
|
||||||
if (n->iffalse)
|
if (n->iffalse)
|
||||||
{
|
{
|
||||||
int jumpout = AddBCC(n->loc, AB_JUMP);
|
int jumpout = AddBCC(n->loc, AB_JUMP);
|
||||||
UpdateJump(jump, AddJumpTarget());
|
UpdateJump(jump, AddJumpTarget());
|
||||||
jump = jumpout;
|
jump = jumpout;
|
||||||
n->iffalse->accept(this);
|
if (SafeVisit(n->iffalse))
|
||||||
MaybePopValueOf(n->iffalse);
|
MaybePopValueOf(n->iffalse);
|
||||||
}
|
}
|
||||||
UpdateJump(jump, AddJumpTarget());
|
UpdateJump(jump, AddJumpTarget());
|
||||||
}
|
}
|
||||||
|
@ -1537,7 +1626,7 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::VarDecl *n)
|
||||||
if (dec.init)
|
if (dec.init)
|
||||||
{
|
{
|
||||||
// Emit code for the initializer
|
// Emit code for the initializer
|
||||||
dec.init->accept(this);
|
SafeVisit(dec.init);
|
||||||
int var_idx = Fn->VarNamed.GetItemNr(cname);
|
int var_idx = Fn->VarNamed.GetItemNr(cname);
|
||||||
assert(var_idx >= 0 && "CodegenAstVisitor: var not found in variable table");
|
assert(var_idx >= 0 && "CodegenAstVisitor: var not found in variable table");
|
||||||
if (var_idx < 0)
|
if (var_idx < 0)
|
||||||
|
@ -1598,7 +1687,7 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::FunctionDecl *n)
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
EmitFunctionCode(n);
|
EmitFunctionCode(n);
|
||||||
Fn = nullptr;
|
Fn = nullptr;
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
|
@ -1610,6 +1699,7 @@ void C4AulCompiler::CodegenAstVisitor::visit(const ::aul::ast::FunctionDecl *n)
|
||||||
|
|
||||||
void C4AulCompiler::CodegenAstVisitor::visit(const::aul::ast::FunctionExpr * n)
|
void C4AulCompiler::CodegenAstVisitor::visit(const::aul::ast::FunctionExpr * n)
|
||||||
{
|
{
|
||||||
|
AddBCC(n->loc, AB_NIL);
|
||||||
throw Error(target_host, host, n, Fn, "can't define a function in a function-scoped proplist");
|
throw Error(target_host, host, n, Fn, "can't define a function in a function-scoped proplist");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1617,16 +1707,7 @@ void C4AulCompiler::CodegenAstVisitor::visit(const::aul::ast::Script * n)
|
||||||
{
|
{
|
||||||
for (const auto &d : n->declarations)
|
for (const auto &d : n->declarations)
|
||||||
{
|
{
|
||||||
try
|
SafeVisit(d);
|
||||||
{
|
|
||||||
d->accept(this);
|
|
||||||
}
|
|
||||||
catch (C4AulParseError &e)
|
|
||||||
{
|
|
||||||
// Inform handler
|
|
||||||
host->Engine->ErrorHandler->OnError(e.what());
|
|
||||||
// try next declaration
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,50 @@ for (var i in a) {
|
||||||
}
|
}
|
||||||
return b;
|
return b;
|
||||||
)"));
|
)"));
|
||||||
|
|
||||||
|
// Test syntax errors inside loops
|
||||||
|
{
|
||||||
|
// Syntax error in for loop initializer
|
||||||
|
ErrorHandler errh;
|
||||||
|
EXPECT_CALL(errh, OnError(::testing::_));
|
||||||
|
EXPECT_THROW(RunCode("for (var i = missing();;) break;"), C4AulExecError);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Syntax error in for loop condition
|
||||||
|
ErrorHandler errh;
|
||||||
|
EXPECT_CALL(errh, OnError(::testing::_));
|
||||||
|
EXPECT_THROW(RunCode("for (; missing();) break;"), C4AulExecError);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Syntax error in for loop incrementor
|
||||||
|
ErrorHandler errh;
|
||||||
|
EXPECT_CALL(errh, OnError(::testing::_));
|
||||||
|
EXPECT_THROW(RunCode("for (;; missing()) continue;"), C4AulExecError);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Syntax error in for loop body
|
||||||
|
ErrorHandler errh;
|
||||||
|
EXPECT_CALL(errh, OnError(::testing::_));
|
||||||
|
EXPECT_THROW(RunCode("for (;;) missing();"), C4AulExecError);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Syntax error in while loop condition
|
||||||
|
ErrorHandler errh;
|
||||||
|
EXPECT_CALL(errh, OnError(::testing::_));
|
||||||
|
EXPECT_THROW(RunCode("while (missing()) break;"), C4AulExecError);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Syntax error in while loop body
|
||||||
|
ErrorHandler errh;
|
||||||
|
EXPECT_CALL(errh, OnError(::testing::_));
|
||||||
|
EXPECT_THROW(RunCode("while (1) missing();"), C4AulExecError);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Syntax error in for-in loop body
|
||||||
|
ErrorHandler errh;
|
||||||
|
EXPECT_CALL(errh, OnError(::testing::_));
|
||||||
|
EXPECT_THROW(RunCode("for (var i in [1]) { missing(); }"), C4AulExecError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(AulTest, Locals)
|
TEST_F(AulTest, Locals)
|
||||||
|
|
Loading…
Reference in New Issue