Report errors with syntactic context in the compiler
This commit is contained in:
parent
d280c34e66
commit
b7d51f647f
3 changed files with 94 additions and 69 deletions
|
@ -819,10 +819,10 @@ Result<Value> Object::rest() const { return ERROR(TypeMismatch); }
|
|||
Result<uint64_t> Object::size() const { return ERROR(TypeMismatch); }
|
||||
|
||||
Result<bool> syntax_is_list(const Value& value) {
|
||||
if (!value.is<Syntax>()) return value.is<Pair>();
|
||||
if (!value.is<Syntax>()) return value.is<Pair>() || value.is<Nil>();
|
||||
|
||||
auto val = TRY(value.to<Syntax>()->expression());
|
||||
return val.is<Pair>();
|
||||
return val.is<Pair>() || val.is<Nil>();
|
||||
}
|
||||
|
||||
Result<bool> syntax_is_nil(const Value& value) {
|
||||
|
@ -876,6 +876,10 @@ Result<Value> Syntax::rest() const {
|
|||
auto val = TRY(expression());
|
||||
return val.rest();
|
||||
}
|
||||
Result<uint64_t> Syntax::size() const {
|
||||
auto val = TRY(expression());
|
||||
return val.size();
|
||||
}
|
||||
|
||||
Result<String> build_string(const String& value) { return value.copy(); }
|
||||
|
||||
|
|
|
@ -725,6 +725,7 @@ class Syntax : public Object {
|
|||
Result<Value> second() const final;
|
||||
Result<Value> third() const final;
|
||||
Result<Value> rest() const final;
|
||||
Result<uint64_t> size() const final;
|
||||
|
||||
virtual Result<Value> copy_value() const final;
|
||||
Result<Syntax> copy() const;
|
||||
|
|
154
src/compiler.cpp
154
src/compiler.cpp
|
@ -339,7 +339,7 @@ Result<Expression> Compiler::compile_comparison(Context& context, Symbol& op,
|
|||
}
|
||||
|
||||
if (cur.is<Nil>()) {
|
||||
return ERROR(CompilationError);
|
||||
return syntax_error(expr, "Comparison must have at least one argument");
|
||||
}
|
||||
|
||||
uint64_t result = context.alloc_reg();
|
||||
|
@ -382,25 +382,18 @@ Result<Expression> Compiler::compile_comparison(Context& context, Symbol& op,
|
|||
|
||||
Result<Expression> Compiler::compile_if(Context& context, Symbol& op,
|
||||
const Value& expr) {
|
||||
Value first = TRY(expr.rest());
|
||||
Value rest = TRY(expr.rest());
|
||||
Expression ex = TRY(Expression::create());
|
||||
if (!TRY(syntax_is_list(first))) {
|
||||
return ERROR(CompilationError);
|
||||
|
||||
auto num_params = TRY(rest.size());
|
||||
if (num_params != 3) {
|
||||
return syntax_error(TRY(expr.first()),
|
||||
"\"if\" form must have exactly 3 arguments");
|
||||
}
|
||||
|
||||
Value second = TRY(first.rest());
|
||||
|
||||
if (!TRY(syntax_is_list(second))) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
|
||||
Value third = TRY(second.rest());
|
||||
|
||||
if (!TRY(syntax_is_list(third))) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
|
||||
auto condition = TRY(first.first());
|
||||
auto condition = TRY(rest.first());
|
||||
auto option1 = TRY(rest.second());
|
||||
auto option2 = TRY(rest.third());
|
||||
|
||||
auto condition_comp = TRY(compile_expr(context, condition));
|
||||
|
||||
|
@ -408,14 +401,12 @@ Result<Expression> Compiler::compile_if(Context& context, Symbol& op,
|
|||
uint64_t firstreg = condition_comp.reg;
|
||||
uint64_t reg = firstreg;
|
||||
|
||||
auto option1 = TRY(second.first());
|
||||
auto option1_comp = TRY(compile_expr(context, option1));
|
||||
|
||||
uint64_t option1_reg = option1_comp.reg;
|
||||
|
||||
context.maxreg = firstreg + 1;
|
||||
|
||||
auto option2 = TRY(third.first());
|
||||
auto option2_comp = TRY(compile_expr(context, option2));
|
||||
|
||||
int64_t true_const = TRY(context.add_const(TRY(Bool::create(true))));
|
||||
|
@ -443,8 +434,10 @@ Result<Expression> Compiler::compile_and(Context& context, Symbol& op,
|
|||
const Value& expr) {
|
||||
Value param = TRY(expr.rest());
|
||||
Expression ex = TRY(Expression::create());
|
||||
if (!TRY(syntax_is_list(param))) {
|
||||
return ERROR(CompilationError);
|
||||
|
||||
if (param.is<Nil>()) {
|
||||
return syntax_error(TRY(expr.first()),
|
||||
"\"and\" form must have at least one parameter");
|
||||
}
|
||||
|
||||
uint64_t prev_reg = 0;
|
||||
|
@ -456,10 +449,6 @@ Result<Expression> Compiler::compile_and(Context& context, Symbol& op,
|
|||
bool is_first = true;
|
||||
|
||||
while (!param.is<Nil>()) {
|
||||
if (!TRY(syntax_is_list(param))) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
|
||||
auto rest = TRY(param.rest());
|
||||
Value param_val = TRY(param.first());
|
||||
|
||||
|
@ -490,8 +479,9 @@ Result<Expression> Compiler::compile_or(Context& context, Symbol& op,
|
|||
const Value& expr) {
|
||||
Value param = TRY(expr.rest());
|
||||
Expression ex = TRY(Expression::create());
|
||||
if (!TRY(syntax_is_list(param))) {
|
||||
return ERROR(CompilationError);
|
||||
if (param.is<Nil>()) {
|
||||
return syntax_error(TRY(expr.first()),
|
||||
"\"or\" form must have at least one parameter");
|
||||
}
|
||||
|
||||
uint64_t prev_reg = 0;
|
||||
|
@ -503,10 +493,6 @@ Result<Expression> Compiler::compile_or(Context& context, Symbol& op,
|
|||
bool is_first = true;
|
||||
|
||||
while (!param.is<Nil>()) {
|
||||
if (!TRY(syntax_is_list(param))) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
|
||||
auto rest = TRY(param.rest());
|
||||
Value param_val = TRY(param.first());
|
||||
|
||||
|
@ -537,8 +523,9 @@ Result<Expression> Compiler::compile_not(Context& context, Symbol& op,
|
|||
const Value& expr) {
|
||||
Value first = TRY(expr.rest());
|
||||
Expression ex = TRY(Expression::create());
|
||||
if (!TRY(syntax_is_list(first))) {
|
||||
return ERROR(CompilationError);
|
||||
if (TRY(first.size()) != 1) {
|
||||
return syntax_error(TRY(expr.first()),
|
||||
"\"not\" form must have exactly one parameter");
|
||||
}
|
||||
|
||||
auto first_expr = TRY(first.first());
|
||||
|
@ -562,19 +549,44 @@ Result<Expression> Compiler::compile_not(Context& context, Symbol& op,
|
|||
|
||||
Result<Expression> Compiler::compile_def(Context& context, Symbol& op,
|
||||
const Value& expr) {
|
||||
uint64_t expr_size = TRY(expr.size());
|
||||
if (expr_size < 2) {
|
||||
return syntax_error(TRY(expr.first()),
|
||||
"\"def\" form must have at least one parameter");
|
||||
}
|
||||
|
||||
if (expr_size > 3) {
|
||||
return syntax_error(TRY(expr.first()),
|
||||
"Too many parameters for \"def\" form");
|
||||
}
|
||||
|
||||
auto varname = TRY(expr.second());
|
||||
auto varname_unwrapped = TRY(syntax_unwrap(varname));
|
||||
if (!varname_unwrapped.is<Symbol>()) return ERROR(CompilationError);
|
||||
if (!varname_unwrapped.is<Symbol>()) {
|
||||
return syntax_error(varname,
|
||||
"Variable name in \"def\" form must be a symbol");
|
||||
}
|
||||
|
||||
Expression ex = TRY(Expression::create());
|
||||
|
||||
int64_t gname = TRY(context.add_const(varname_unwrapped));
|
||||
uint64_t maxreg = context.maxreg;
|
||||
|
||||
// No value for the variable is specified - just load "nil" into it
|
||||
if (expr_size == 2) {
|
||||
int64_t c = TRY(context.add_const(Value(TRY(Nil::create()))));
|
||||
TRY(ex.add_opcode(Oc::GlobalStore, {1, (int64_t)gname}, {1, (int64_t)c}));
|
||||
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)maxreg}, {1, (int64_t)c}));
|
||||
ex.reg = maxreg;
|
||||
context.maxreg = maxreg + 1;
|
||||
return std::move(ex);
|
||||
}
|
||||
|
||||
auto var_expr = TRY(expr.third());
|
||||
|
||||
uint64_t maxreg = context.maxreg;
|
||||
Expression ex = TRY(Expression::create());
|
||||
|
||||
auto comp = TRY(compile_expr(context, var_expr));
|
||||
ex.add_code(comp.code);
|
||||
|
||||
int64_t gname = TRY(context.add_const(varname_unwrapped));
|
||||
TRY(ex.add_opcode(Oc::GlobalStore, {1, (int64_t)gname},
|
||||
{0, (int64_t)comp.reg}));
|
||||
|
||||
|
@ -586,6 +598,12 @@ Result<Expression> Compiler::compile_def(Context& context, Symbol& op,
|
|||
|
||||
Result<Expression> Compiler::compile_quote(Context& context, Symbol& op,
|
||||
const Value& expr) {
|
||||
uint64_t expr_size = TRY(expr.size());
|
||||
if (expr_size != 2) {
|
||||
return syntax_error(TRY(expr.first()),
|
||||
"\"quote\" form must have exactly one parameter");
|
||||
}
|
||||
|
||||
auto quoted = TRY(expr.second());
|
||||
quoted = TRY(syntax_unwrap_all(quoted));
|
||||
|
||||
|
@ -603,6 +621,12 @@ Result<Expression> Compiler::compile_quote(Context& context, Symbol& op,
|
|||
|
||||
Result<Expression> Compiler::compile_syntax(Context& context, Symbol& op,
|
||||
const Value& expr) {
|
||||
uint64_t expr_size = TRY(expr.size());
|
||||
if (expr_size != 2) {
|
||||
return syntax_error(TRY(expr.first()),
|
||||
"\"syntax\" form must have exactly one parameter");
|
||||
}
|
||||
|
||||
auto quoted = TRY(expr.second());
|
||||
|
||||
uint64_t maxreg = context.maxreg;
|
||||
|
@ -634,19 +658,16 @@ Result<Expression> Compiler::compile_fn(Context& context, Symbol& op,
|
|||
ctx.fname = TRY(name.copy());
|
||||
|
||||
rest = TRY(rest.rest());
|
||||
|
||||
if (!TRY(syntax_is_list(rest))) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
}
|
||||
|
||||
auto param = TRY(rest.first());
|
||||
|
||||
if (!TRY(syntax_is_list(param))) {
|
||||
return syntax_error(param, "Function parameters must be a list");
|
||||
}
|
||||
|
||||
uint64_t arity = 0;
|
||||
while (!TRY(syntax_is_nil(param))) {
|
||||
if (!TRY(syntax_is_list(param))) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
auto param_name = TRY(param.first());
|
||||
auto param_name_unwrapped = TRY(syntax_unwrap(param_name));
|
||||
|
||||
|
@ -662,10 +683,6 @@ Result<Expression> Compiler::compile_fn(Context& context, Symbol& op,
|
|||
|
||||
Value body = TRY(rest.rest());
|
||||
|
||||
if (!TRY(syntax_is_list(body))) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
|
||||
auto ex = TRY(compile_body(ctx, body));
|
||||
|
||||
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)0}, {0, (int64_t)ex.reg}));
|
||||
|
@ -756,30 +773,30 @@ Result<Expression> Compiler::compile_let(Context& context, Symbol& op,
|
|||
// Save the variable bindings to restore it later
|
||||
Dict saved_vars = TRY(context.variables_dict.copy());
|
||||
|
||||
auto first = TRY(expr.rest());
|
||||
auto rest = TRY(expr.rest());
|
||||
|
||||
if (!TRY(syntax_is_list(first))) {
|
||||
return ERROR(CompilationError);
|
||||
if (TRY(expr.size()) < 2) {
|
||||
return syntax_error(TRY(expr.first()),
|
||||
"\"let\" form must have at least one parameter");
|
||||
}
|
||||
|
||||
uint64_t maxreg = context.maxreg;
|
||||
|
||||
auto bindings = TRY(first.first());
|
||||
auto bindings = TRY(rest.first());
|
||||
bindings = TRY(syntax_unwrap(bindings));
|
||||
|
||||
Expression ex_res = TRY(Expression::create());
|
||||
|
||||
while (!bindings.is<Nil>()) {
|
||||
auto binding = TRY(bindings.first());
|
||||
|
||||
if (!TRY(syntax_is_list(binding))) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
|
||||
auto binding_name = TRY(binding.first());
|
||||
auto binding_name_unwrapped = TRY(syntax_unwrap(binding_name));
|
||||
auto binding_expr = TRY(binding.second());
|
||||
|
||||
if (!binding_name_unwrapped.is<Symbol>()) return ERROR(CompilationError);
|
||||
if (!binding_name_unwrapped.is<Symbol>()) {
|
||||
return syntax_error(binding_name, "Binding name must be a symbol");
|
||||
}
|
||||
|
||||
int64_t reg = TRY(context.update_var(binding_name_unwrapped));
|
||||
auto ex = TRY(compile_expr(context, binding_expr));
|
||||
|
@ -791,13 +808,9 @@ Result<Expression> Compiler::compile_let(Context& context, Symbol& op,
|
|||
bindings = TRY(bindings.rest());
|
||||
}
|
||||
|
||||
Value second = TRY(first.rest());
|
||||
Value body = TRY(rest.rest());
|
||||
|
||||
if (!TRY(syntax_is_list(second))) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
|
||||
auto ex = TRY(compile_body(context, second));
|
||||
auto ex = TRY(compile_body(context, body));
|
||||
|
||||
TRY(ex.add_opcode(Oc::Mov, {0, (int64_t)maxreg}, {0, (int64_t)ex.reg}));
|
||||
ex_res.add_code(ex.code);
|
||||
|
@ -816,6 +829,16 @@ Result<Expression> Compiler::compile_body(Context& context, const Value& expr) {
|
|||
|
||||
int64_t maxreg = context.maxreg;
|
||||
|
||||
// Body is empty, in which case just replace it with "nil" value
|
||||
if (TRY(expr.size()) == 0) {
|
||||
int64_t c = TRY(context.add_const(Value(TRY(Nil::create()))));
|
||||
TRY(ex_res.add_opcode(Oc::Mov, {0, (int64_t)maxreg}, {1, (int64_t)c}));
|
||||
ex_res.reg = maxreg;
|
||||
context.maxreg = maxreg + 1;
|
||||
return std::move(ex_res);
|
||||
}
|
||||
|
||||
cur = TRY(syntax_unwrap(cur));
|
||||
while (!cur.is<Nil>()) {
|
||||
auto expr_val = TRY(cur.first());
|
||||
|
||||
|
@ -860,9 +883,6 @@ Result<Expression> Compiler::compile_function_call(Context& context,
|
|||
|
||||
int64_t maxreg = context.maxreg;
|
||||
while (!param.is<Nil>()) {
|
||||
if (!TRY(syntax_is_list(param))) {
|
||||
return ERROR(CompilationError);
|
||||
}
|
||||
Value param_val = TRY(param.first());
|
||||
|
||||
auto param_ex = TRY(compile_expr(context, param_val));
|
||||
|
|
Loading…
Reference in a new issue