From b7d51f647f3082dc653e54480a89499d75075a8a Mon Sep 17 00:00:00 2001 From: Konstantin Nazarov Date: Wed, 11 Sep 2024 23:25:17 +0100 Subject: [PATCH] Report errors with syntactic context in the compiler --- src/common.cpp | 8 ++- src/common.hpp | 1 + src/compiler.cpp | 154 ++++++++++++++++++++++++++--------------------- 3 files changed, 94 insertions(+), 69 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index da5312f..8f67b29 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -819,10 +819,10 @@ Result Object::rest() const { return ERROR(TypeMismatch); } Result Object::size() const { return ERROR(TypeMismatch); } Result syntax_is_list(const Value& value) { - if (!value.is()) return value.is(); + if (!value.is()) return value.is() || value.is(); auto val = TRY(value.to()->expression()); - return val.is(); + return val.is() || val.is(); } Result syntax_is_nil(const Value& value) { @@ -876,6 +876,10 @@ Result Syntax::rest() const { auto val = TRY(expression()); return val.rest(); } +Result Syntax::size() const { + auto val = TRY(expression()); + return val.size(); +} Result build_string(const String& value) { return value.copy(); } diff --git a/src/common.hpp b/src/common.hpp index 998a751..de4670c 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -725,6 +725,7 @@ class Syntax : public Object { Result second() const final; Result third() const final; Result rest() const final; + Result size() const final; virtual Result copy_value() const final; Result copy() const; diff --git a/src/compiler.cpp b/src/compiler.cpp index 10b3643..a06425f 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -339,7 +339,7 @@ Result Compiler::compile_comparison(Context& context, Symbol& op, } if (cur.is()) { - 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 Compiler::compile_comparison(Context& context, Symbol& op, Result 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 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 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()) { + return syntax_error(TRY(expr.first()), + "\"and\" form must have at least one parameter"); } uint64_t prev_reg = 0; @@ -456,10 +449,6 @@ Result Compiler::compile_and(Context& context, Symbol& op, bool is_first = true; while (!param.is()) { - 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 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()) { + return syntax_error(TRY(expr.first()), + "\"or\" form must have at least one parameter"); } uint64_t prev_reg = 0; @@ -503,10 +493,6 @@ Result Compiler::compile_or(Context& context, Symbol& op, bool is_first = true; while (!param.is()) { - 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 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 Compiler::compile_not(Context& context, Symbol& op, Result 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()) return ERROR(CompilationError); + if (!varname_unwrapped.is()) { + 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 Compiler::compile_def(Context& context, Symbol& op, Result 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 Compiler::compile_quote(Context& context, Symbol& op, Result 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 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 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 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()) { 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()) return ERROR(CompilationError); + if (!binding_name_unwrapped.is()) { + 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 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 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()) { auto expr_val = TRY(cur.first()); @@ -860,9 +883,6 @@ Result Compiler::compile_function_call(Context& context, int64_t maxreg = context.maxreg; while (!param.is()) { - if (!TRY(syntax_is_list(param))) { - return ERROR(CompilationError); - } Value param_val = TRY(param.first()); auto param_ex = TRY(compile_expr(context, param_val));