diff --git a/src/common.cpp b/src/common.cpp index 28aec5a..2a2547c 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -62,6 +62,18 @@ Result Symbol::create(String& rhs) { return Symbol(TRY(MkGcRoot(pod))); } +Result String::create(const Symbol& rhs) { + uint64_t rhs_size = rhs.size(); + uint64_t res_size = rhs_size; + + auto pod = TRY(arena_alloc(res_size * sizeof(char32_t))); + pod->header.tag = Tag::String; + pod->size = res_size; + memcpy(pod->data, rhs._value->data, sizeof(char32_t) * rhs_size); + + return String(TRY(MkGcRoot(pod))); +} + Result ByteArray::create(const String& str) { uint64_t size = 0; for (uint64_t i = 0; i < str.size(); i++) { @@ -491,6 +503,11 @@ Result reverse(Value& val) { return res; } +Result debug_print(const char* val) { + std::cout << val << "\n"; + return Result(); +} + Result debug_print(const String& val) { auto ba = TRY(ByteArray::create(val)); @@ -841,6 +858,8 @@ Result Syntax::rest() const { return val.rest(); } +Result build_string(const String& value) { return value.copy(); } + Result build_string(const Value& value) { if (value.is()) { return value.to()->copy(); @@ -850,3 +869,13 @@ Result build_string(const Value& value) { } Result build_string(const char* value) { return String::create(value); } + +Result build_string(int64_t value) { + auto val = Value(TRY(Int64::create(value))); + return write_one(val); +} + +Result build_string(uint64_t value) { + auto val = Value(TRY(Int64::create((int64_t)value))); + return write_one(val); +} diff --git a/src/common.hpp b/src/common.hpp index 67279fa..a931786 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -476,6 +476,8 @@ class String : public Object { return String(TRY(MkGcRoot(pod))); } + static Result create(const Symbol& sym); + static Result create(const char* str) { uint64_t size = strlen(str); auto pod = TRY(arena_alloc(size * sizeof(char32_t))); @@ -557,6 +559,8 @@ class String : public Object { class Symbol : public Object { public: + friend class String; + Symbol() {} Symbol(Symbol&& rhs) : _value(std::move(rhs._value)) {} Symbol(GcRoot&& val) : _value(std::move(val)) {} @@ -1166,6 +1170,16 @@ class Error : public Object { return Error(TRY(MkGcRoot(pod))); } + static Result create(const Symbol& name, const String& message) { + auto pod = TRY(arena_alloc()); + pod->header.tag = Tag::Error; + + pod->name = name.pod(); + pod->message = message.pod(); + + return Error(TRY(MkGcRoot(pod))); + } + Result name() const; Result message() const; @@ -1273,6 +1287,7 @@ Result reverse(Value& val); Result debug_print(const String& val); Result debug_print(const Value& val); +Result debug_print(const char* val); template Result debug_print(const T& val) @@ -1301,7 +1316,10 @@ Result syntax_unwrap(const Value& value); Result syntax_unwrap_all(const Value& value); Result build_string(const Value& value); +Result build_string(const String& value); Result build_string(const char* value); +Result build_string(int64_t value); +Result build_string(uint64_t value); template Result build_string(const T& value, Args&&... args) { diff --git a/src/error.cpp b/src/error.cpp index 020db62..e3c8701 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -16,3 +16,12 @@ Result seterrobj(const Value& val) { } void reseterrobj() { errobj = Value(); } + +const char* errname(ErrorCode errcode) { + switch (errcode) { + case ErrorCode::SyntaxError: + return "syntax-error"; + default: + return "unknown"; + } +} diff --git a/src/error.hpp b/src/error.hpp index 5df3a85..afc1144 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -27,6 +27,8 @@ class Value; template class Result; +const char* errname(ErrorCode errcode); + void seterr(const char* err); const char* geterr(void); @@ -43,3 +45,13 @@ void reseterrobj(); seterr("Error " STRINGIZE(code) " at " __FILE__ ":" STRINGIZE(__LINE__)); \ ErrorCode::code; \ })) + +#define ERROR_FMT(code, ...) \ + (({ \ + auto name = TRY(Symbol::create(errname(ErrorCode::code))); \ + auto message = TRY(build_string(__VA_ARGS__)); \ + auto err = Value(TRY(Error::create(name, message))); \ + TRY(seterrobj(err)); \ + seterr("Error " STRINGIZE(code) " at " __FILE__ ":" STRINGIZE(__LINE__)); \ + ErrorCode::code; \ + })) diff --git a/src/reader.cpp b/src/reader.cpp index 55ba9c2..5957fbb 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -72,7 +72,7 @@ Result Reader::read_one() { return read_symbol(); } - return ERROR(ReadError); + return syntax_error(saved_position, "unknown token"); } Result Reader::read_multiple() { @@ -88,29 +88,10 @@ Result Reader::read_multiple() { res = Value(TRY(Pair::create(val, res))); } + // We should never get here return ERROR(ReadError); } -Result Reader::read_list_rec() { - forward_whitespace(); - - if (is_eof()) { - return ERROR(ReadError); - } - - if (match(')')) { - forward(); - return Value(TRY(Nil::create())); - } - - auto val = TRY(read_one()); - auto rest = TRY(read_list_rec()); - - auto res = Value(TRY(Pair::create(val, rest))); - - return res; -} - Result Reader::read_list() { if (!match('(')) return ERROR(ReadError); @@ -123,7 +104,7 @@ Result Reader::read_list() { forward_whitespace(); if (is_eof()) { - return ERROR(ReadError); + return syntax_error(start, "unterminated list"); } if (match(')')) { @@ -143,6 +124,7 @@ Result Reader::read_list() { res = TRY(Pair::create(val, res)); } + // We should never get here return ERROR(ReadError); } @@ -159,7 +141,7 @@ Result Reader::read_array_code() { forward_whitespace(); if (is_eof()) { - return ERROR(ReadError); + return syntax_error(start, "unterminated array"); } if (match(']')) { @@ -180,6 +162,7 @@ Result Reader::read_array_code() { res = TRY(Pair::create(val, res)); } + // We should never get here return ERROR(ReadError); } @@ -195,7 +178,7 @@ Result Reader::read_array_data() { forward_whitespace(); if (is_eof()) { - return ERROR(ReadError); + return syntax_error(start, "unterminated array"); } if (match(']')) { @@ -215,6 +198,7 @@ Result Reader::read_array_data() { res = TRY(res.append(val)); } + // We should never get here return ERROR(ReadError); } @@ -237,7 +221,7 @@ Result Reader::read_dict_code() { forward_whitespace(); if (is_eof()) { - return ERROR(ReadError); + return syntax_error(start, "unterminated dict"); } if (match('}')) { @@ -260,6 +244,7 @@ Result Reader::read_dict_code() { res = TRY(Pair::create(val2, res)); } + // We should never get here return ERROR(ReadError); } @@ -275,7 +260,7 @@ Result Reader::read_dict_data() { forward_whitespace(); if (is_eof()) { - return ERROR(ReadError); + return syntax_error(start, "unterminated dict"); } if (match('}')) { @@ -296,6 +281,7 @@ Result Reader::read_dict_data() { res = TRY(res.insert(val1, val2)); } + // We should never get here return ERROR(ReadError); } @@ -309,7 +295,8 @@ Result Reader::read_bool() { SourcePosition start = position_; if (match("true")) { forward(4); - if (!is_separator(get()) && !is_eof()) return ERROR(ReadError); + // We don't report full error here, because it's not propagated to the user + if (!is_separator(get()) && !is_eof()) return ERROR(SyntaxError); SourcePosition end = position_; auto res = Value(TRY(Bool::create(true))); @@ -321,7 +308,8 @@ Result Reader::read_bool() { } if (match("false")) { forward(5); - if (!is_separator(get()) && !is_eof()) return ERROR(ReadError); + // We don't report full error here, because it's not propagated to the user + if (!is_separator(get()) && !is_eof()) return ERROR(SyntaxError); SourcePosition end = position_; auto res = Value(TRY(Bool::create(false))); @@ -332,6 +320,7 @@ Result Reader::read_bool() { } } + // We don't report full error here, because it's not propagated to the user return ERROR(ReadError); } @@ -339,6 +328,7 @@ Result Reader::read_nil() { SourcePosition start = position_; if (match("nil")) { forward(3); + // We don't report full error here, because it's not propagated to the user if (!is_separator(get()) && !is_eof()) return ERROR(ReadError); SourcePosition end = position_; @@ -350,6 +340,7 @@ Result Reader::read_nil() { } } + // We don't report full error here, because it's not propagated to the user return ERROR(ReadError); } @@ -388,7 +379,9 @@ Result Reader::read_string() { forward(); } - if (!match('"')) return ERROR(UnterminatedStringLiteral); + if (!match('"')) { + return syntax_error(startloc, "unterminated string"); + } forward(); @@ -434,7 +427,9 @@ Result Reader::read_string() { result = TRY(result.concat(&next, 1)); } - if (!is_separator(get()) && !is_eof()) return ERROR(ReadError); + if (!is_separator(get()) && !is_eof()) { + return syntax_error(position_, "string followed by invalid tokens"); + } SourcePosition endloc = position_; auto res = Value(std::move(result)); @@ -462,13 +457,19 @@ Result Reader::read_number() { while (is_digit(get())) forward(); if (match('e') || match('E')) { - if (!forward_exponent()) return ERROR(InvalidNumericLiteral); + if (!forward_exponent()) { + return syntax_error(startloc, "invalid numeric literal"); + } } } else if (match('e') || match('E')) { is_float = true; - if (!forward_exponent()) return ERROR(InvalidNumericLiteral); + if (!forward_exponent()) { + return syntax_error(startloc, "invalid numeric literal"); + } } else if (match('x') || match('X')) { - if (!forward_hex_number()) return ERROR(InvalidNumericLiteral); + if (!forward_hex_number()) { + return syntax_error(startloc, "invalid numeric literal"); + } } else if (is_digit(get())) { do { forward(); @@ -483,13 +484,17 @@ Result Reader::read_number() { } if (match('e') || match('E')) { is_float = true; - if (!forward_exponent()) return ERROR(InvalidNumericLiteral); + if (!forward_exponent()) { + return syntax_error(startloc, "invalid numeric literal"); + } } } Value res; - if (position_.offset - start >= 32) return ERROR(InvalidNumericLiteral); + if (position_.offset - start >= 32) { + return syntax_error(startloc, "numeric literal is too large"); + } char buf[32]; for (size_t i = 0; i < position_.offset - start; ++i) { @@ -506,7 +511,9 @@ Result Reader::read_number() { res = Value(TRY(Int64::create(strtoll(buf, 0, 10)))); } - if (!is_separator(get()) && !is_eof()) return ERROR(ReadError); + if (!is_separator(get()) && !is_eof()) { + return syntax_error(startloc, "number is followed by invalid tokens"); + } SourcePosition endloc = position_; @@ -529,7 +536,9 @@ Result Reader::read_symbol() { String str = TRY(_str.slice(start, end)); - if (!is_separator(get()) && !is_eof()) return ERROR(ReadError); + if (!is_separator(get()) && !is_eof()) { + return syntax_error(startloc, "symbol is followed by invalid tokens"); + } SourcePosition endloc = position_; @@ -647,26 +656,31 @@ bool Reader::forward_exponent() { return forward_decimal_number(); } +Result Reader::syntax_error(SourcePosition pos, const char* msg) { + return ERROR_FMT(SyntaxError, _fname, ":", pos.line, ":", pos.column, + " Syntax error: ", msg); +} + Result read_one(const Value& value, bool syntax, bool as_code) { if (!value.is()) return ERROR(TypeMismatch); - auto fname = TRY(String::create("")); + auto fname = TRY(String::create("")); auto r = Reader(fname, *value.to(), syntax, as_code); return r.read_one(); } Result read_one(const String& value, bool syntax, bool as_code) { - auto fname = TRY(String::create("")); + auto fname = TRY(String::create("")); auto r = Reader(fname, value, syntax, as_code); return r.read_one(); } Result read_one(const char* value, bool syntax, bool as_code) { - auto fname = TRY(String::create("")); + auto fname = TRY(String::create("")); auto s = TRY(String::create(value)); auto r = Reader(fname, s, syntax, as_code); return r.read_one(); } Result read_multiple(const String& value, bool syntax, bool as_code) { - auto fname = TRY(String::create("")); + auto fname = TRY(String::create("")); auto r = Reader(fname, value, syntax, as_code); return r.read_multiple(); } diff --git a/src/reader.hpp b/src/reader.hpp index 89f3314..b285640 100644 --- a/src/reader.hpp +++ b/src/reader.hpp @@ -29,7 +29,6 @@ class Reader { bool is_string_start(); Result read_list(); - Result read_list_rec(); Result read_string(); Result read_array(); Result read_array_code(); @@ -43,6 +42,8 @@ class Reader { Result read_nil(); Result read_quote(); + Result syntax_error(SourcePosition pos, const char* message); + char32_t get(size_t offset = 0); bool match(const char* str); bool match(char c); diff --git a/src/result.hpp b/src/result.hpp index 8da24f5..2aa6407 100644 --- a/src/result.hpp +++ b/src/result.hpp @@ -11,8 +11,8 @@ class Result { Result(T&& res) : _value(std::move(res)), _error(ErrorCode::Success) {} Result(ErrorCode err) : _error(err) {} - bool has_error() { return _error != ErrorCode::Success; } - bool has_value() { return !has_error(); } + bool has_error() const { return _error != ErrorCode::Success; } + bool has_value() const { return !has_error(); } T& value() { return _value; } T& operator*() { return _value; } @@ -36,8 +36,8 @@ class Result { Result& operator=(Result&& res) = default; - bool has_error() { return err != ErrorCode::Success; } - bool has_value() { return !has_error(); } + bool has_error() const { return err != ErrorCode::Success; } + bool has_value() const { return !has_error(); } void value() {} void release_value() {} diff --git a/src/valeri.cpp b/src/valeri.cpp index 297f2c6..20c7ce6 100644 --- a/src/valeri.cpp +++ b/src/valeri.cpp @@ -4,6 +4,7 @@ #include "common.hpp" #include "compiler.hpp" #include "die.hpp" +#include "error.hpp" #include "fio.hpp" #include "lineedit.hpp" #include "reader.hpp" @@ -27,20 +28,46 @@ Result run_string(const String& src) { return res; } +Result print_error(const Result& res) { + if (res.has_error()) { + auto errobj = TRY(geterrobj().copy()); + if (errobj.is()) { + debug_print(geterr()); + } else { + debug_print(errobj); + } + } + + return Result(); +} + Result run_repl() { Dict globals = TRY(Dict::create()); while (true) { auto src = TRY(read_line("valeri> ")); - auto parsed = TRY(read_multiple(src)); + auto maybe_parsed = read_multiple(src); + if (maybe_parsed.has_error()) { + print_error(maybe_parsed); + continue; + } + + auto parsed = maybe_parsed.release_value(); auto compiled = TRY(compile(parsed)); Module& mod = *compiled.to(); auto vm = TRY(VM::create()); - auto res = TRY(vm.run(mod, globals)); - globals = TRY(vm.globals()); + auto maybe_res = vm.run(mod, globals); - if (!res.is()) { - TRY(debug_print(res)); + if (maybe_res.has_value()) { + auto res = maybe_res.release_value(); + globals = TRY(vm.globals()); + + if (!res.is()) { + TRY(debug_print(res)); + } + } else { + // Need to print the error + print_error(maybe_res); } } diff --git a/src/writer.cpp b/src/writer.cpp index 2017dba..fe5d4e5 100644 --- a/src/writer.cpp +++ b/src/writer.cpp @@ -379,7 +379,20 @@ Result Writer::write_stack(const Stack& val) { } Result Writer::write_error(const Error& val) { - return TRY(String::create("#")); + auto res = TRY(String::create("#())); + + auto message = TRY(val.message()); + auto message_str = TRY(write_one(message)); + + res = TRY(res.concat(name_str)); + res = TRY(res.concat(" ")); + res = TRY(res.concat(message_str)); + + res = TRY(res.concat(">")); + + return res; } Result write_one(const Value& value) {