Report syntax errors when reading in the REPL

This commit is contained in:
Konstantin Nazarov 2024-09-08 02:31:54 +01:00
parent 5cb8921c42
commit 1958cfb285
Signed by: knazarov
GPG key ID: 4CFE0A42FA409C22
9 changed files with 175 additions and 52 deletions

View file

@ -62,6 +62,18 @@ Result<Symbol> Symbol::create(String& rhs) {
return Symbol(TRY(MkGcRoot(pod))); return Symbol(TRY(MkGcRoot(pod)));
} }
Result<String> String::create(const Symbol& rhs) {
uint64_t rhs_size = rhs.size();
uint64_t res_size = rhs_size;
auto pod = TRY(arena_alloc<PodString>(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> ByteArray::create(const String& str) { Result<ByteArray> ByteArray::create(const String& str) {
uint64_t size = 0; uint64_t size = 0;
for (uint64_t i = 0; i < str.size(); i++) { for (uint64_t i = 0; i < str.size(); i++) {
@ -491,6 +503,11 @@ Result<Value> reverse(Value& val) {
return res; return res;
} }
Result<void> debug_print(const char* val) {
std::cout << val << "\n";
return Result<void>();
}
Result<void> debug_print(const String& val) { Result<void> debug_print(const String& val) {
auto ba = TRY(ByteArray::create(val)); auto ba = TRY(ByteArray::create(val));
@ -841,6 +858,8 @@ Result<Value> Syntax::rest() const {
return val.rest(); return val.rest();
} }
Result<String> build_string(const String& value) { return value.copy(); }
Result<String> build_string(const Value& value) { Result<String> build_string(const Value& value) {
if (value.is<String>()) { if (value.is<String>()) {
return value.to<String>()->copy(); return value.to<String>()->copy();
@ -850,3 +869,13 @@ Result<String> build_string(const Value& value) {
} }
Result<String> build_string(const char* value) { return String::create(value); } Result<String> build_string(const char* value) { return String::create(value); }
Result<String> build_string(int64_t value) {
auto val = Value(TRY(Int64::create(value)));
return write_one(val);
}
Result<String> build_string(uint64_t value) {
auto val = Value(TRY(Int64::create((int64_t)value)));
return write_one(val);
}

View file

@ -476,6 +476,8 @@ class String : public Object {
return String(TRY(MkGcRoot(pod))); return String(TRY(MkGcRoot(pod)));
} }
static Result<String> create(const Symbol& sym);
static Result<String> create(const char* str) { static Result<String> create(const char* str) {
uint64_t size = strlen(str); uint64_t size = strlen(str);
auto pod = TRY(arena_alloc<PodString>(size * sizeof(char32_t))); auto pod = TRY(arena_alloc<PodString>(size * sizeof(char32_t)));
@ -557,6 +559,8 @@ class String : public Object {
class Symbol : public Object { class Symbol : public Object {
public: public:
friend class String;
Symbol() {} Symbol() {}
Symbol(Symbol&& rhs) : _value(std::move(rhs._value)) {} Symbol(Symbol&& rhs) : _value(std::move(rhs._value)) {}
Symbol(GcRoot<PodSymbol>&& val) : _value(std::move(val)) {} Symbol(GcRoot<PodSymbol>&& val) : _value(std::move(val)) {}
@ -1166,6 +1170,16 @@ class Error : public Object {
return Error(TRY(MkGcRoot(pod))); return Error(TRY(MkGcRoot(pod)));
} }
static Result<Error> create(const Symbol& name, const String& message) {
auto pod = TRY(arena_alloc<PodError>());
pod->header.tag = Tag::Error;
pod->name = name.pod();
pod->message = message.pod();
return Error(TRY(MkGcRoot(pod)));
}
Result<Value> name() const; Result<Value> name() const;
Result<Value> message() const; Result<Value> message() const;
@ -1273,6 +1287,7 @@ Result<Value> reverse(Value& val);
Result<void> debug_print(const String& val); Result<void> debug_print(const String& val);
Result<void> debug_print(const Value& val); Result<void> debug_print(const Value& val);
Result<void> debug_print(const char* val);
template <class T> template <class T>
Result<void> debug_print(const T& val) Result<void> debug_print(const T& val)
@ -1301,7 +1316,10 @@ Result<Value> syntax_unwrap(const Value& value);
Result<Value> syntax_unwrap_all(const Value& value); Result<Value> syntax_unwrap_all(const Value& value);
Result<String> build_string(const Value& value); Result<String> build_string(const Value& value);
Result<String> build_string(const String& value);
Result<String> build_string(const char* value); Result<String> build_string(const char* value);
Result<String> build_string(int64_t value);
Result<String> build_string(uint64_t value);
template <class T, class... Args> template <class T, class... Args>
Result<String> build_string(const T& value, Args&&... args) { Result<String> build_string(const T& value, Args&&... args) {

View file

@ -16,3 +16,12 @@ Result<void> seterrobj(const Value& val) {
} }
void reseterrobj() { errobj = Value(); } void reseterrobj() { errobj = Value(); }
const char* errname(ErrorCode errcode) {
switch (errcode) {
case ErrorCode::SyntaxError:
return "syntax-error";
default:
return "unknown";
}
}

View file

@ -27,6 +27,8 @@ class Value;
template <class T> template <class T>
class Result; class Result;
const char* errname(ErrorCode errcode);
void seterr(const char* err); void seterr(const char* err);
const char* geterr(void); const char* geterr(void);
@ -43,3 +45,13 @@ void reseterrobj();
seterr("Error " STRINGIZE(code) " at " __FILE__ ":" STRINGIZE(__LINE__)); \ seterr("Error " STRINGIZE(code) " at " __FILE__ ":" STRINGIZE(__LINE__)); \
ErrorCode::code; \ 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; \
}))

View file

@ -72,7 +72,7 @@ Result<Value> Reader::read_one() {
return read_symbol(); return read_symbol();
} }
return ERROR(ReadError); return syntax_error(saved_position, "unknown token");
} }
Result<Value> Reader::read_multiple() { Result<Value> Reader::read_multiple() {
@ -88,29 +88,10 @@ Result<Value> Reader::read_multiple() {
res = Value(TRY(Pair::create(val, res))); res = Value(TRY(Pair::create(val, res)));
} }
// We should never get here
return ERROR(ReadError); return ERROR(ReadError);
} }
Result<Value> 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<Value> Reader::read_list() { Result<Value> Reader::read_list() {
if (!match('(')) return ERROR(ReadError); if (!match('(')) return ERROR(ReadError);
@ -123,7 +104,7 @@ Result<Value> Reader::read_list() {
forward_whitespace(); forward_whitespace();
if (is_eof()) { if (is_eof()) {
return ERROR(ReadError); return syntax_error(start, "unterminated list");
} }
if (match(')')) { if (match(')')) {
@ -143,6 +124,7 @@ Result<Value> Reader::read_list() {
res = TRY(Pair::create(val, res)); res = TRY(Pair::create(val, res));
} }
// We should never get here
return ERROR(ReadError); return ERROR(ReadError);
} }
@ -159,7 +141,7 @@ Result<Value> Reader::read_array_code() {
forward_whitespace(); forward_whitespace();
if (is_eof()) { if (is_eof()) {
return ERROR(ReadError); return syntax_error(start, "unterminated array");
} }
if (match(']')) { if (match(']')) {
@ -180,6 +162,7 @@ Result<Value> Reader::read_array_code() {
res = TRY(Pair::create(val, res)); res = TRY(Pair::create(val, res));
} }
// We should never get here
return ERROR(ReadError); return ERROR(ReadError);
} }
@ -195,7 +178,7 @@ Result<Value> Reader::read_array_data() {
forward_whitespace(); forward_whitespace();
if (is_eof()) { if (is_eof()) {
return ERROR(ReadError); return syntax_error(start, "unterminated array");
} }
if (match(']')) { if (match(']')) {
@ -215,6 +198,7 @@ Result<Value> Reader::read_array_data() {
res = TRY(res.append(val)); res = TRY(res.append(val));
} }
// We should never get here
return ERROR(ReadError); return ERROR(ReadError);
} }
@ -237,7 +221,7 @@ Result<Value> Reader::read_dict_code() {
forward_whitespace(); forward_whitespace();
if (is_eof()) { if (is_eof()) {
return ERROR(ReadError); return syntax_error(start, "unterminated dict");
} }
if (match('}')) { if (match('}')) {
@ -260,6 +244,7 @@ Result<Value> Reader::read_dict_code() {
res = TRY(Pair::create(val2, res)); res = TRY(Pair::create(val2, res));
} }
// We should never get here
return ERROR(ReadError); return ERROR(ReadError);
} }
@ -275,7 +260,7 @@ Result<Value> Reader::read_dict_data() {
forward_whitespace(); forward_whitespace();
if (is_eof()) { if (is_eof()) {
return ERROR(ReadError); return syntax_error(start, "unterminated dict");
} }
if (match('}')) { if (match('}')) {
@ -296,6 +281,7 @@ Result<Value> Reader::read_dict_data() {
res = TRY(res.insert(val1, val2)); res = TRY(res.insert(val1, val2));
} }
// We should never get here
return ERROR(ReadError); return ERROR(ReadError);
} }
@ -309,7 +295,8 @@ Result<Value> Reader::read_bool() {
SourcePosition start = position_; SourcePosition start = position_;
if (match("true")) { if (match("true")) {
forward(4); 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_; SourcePosition end = position_;
auto res = Value(TRY(Bool::create(true))); auto res = Value(TRY(Bool::create(true)));
@ -321,7 +308,8 @@ Result<Value> Reader::read_bool() {
} }
if (match("false")) { if (match("false")) {
forward(5); 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_; SourcePosition end = position_;
auto res = Value(TRY(Bool::create(false))); auto res = Value(TRY(Bool::create(false)));
@ -332,6 +320,7 @@ Result<Value> Reader::read_bool() {
} }
} }
// We don't report full error here, because it's not propagated to the user
return ERROR(ReadError); return ERROR(ReadError);
} }
@ -339,6 +328,7 @@ Result<Value> Reader::read_nil() {
SourcePosition start = position_; SourcePosition start = position_;
if (match("nil")) { if (match("nil")) {
forward(3); 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); if (!is_separator(get()) && !is_eof()) return ERROR(ReadError);
SourcePosition end = position_; SourcePosition end = position_;
@ -350,6 +340,7 @@ Result<Value> Reader::read_nil() {
} }
} }
// We don't report full error here, because it's not propagated to the user
return ERROR(ReadError); return ERROR(ReadError);
} }
@ -388,7 +379,9 @@ Result<Value> Reader::read_string() {
forward(); forward();
} }
if (!match('"')) return ERROR(UnterminatedStringLiteral); if (!match('"')) {
return syntax_error(startloc, "unterminated string");
}
forward(); forward();
@ -434,7 +427,9 @@ Result<Value> Reader::read_string() {
result = TRY(result.concat(&next, 1)); 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_; SourcePosition endloc = position_;
auto res = Value(std::move(result)); auto res = Value(std::move(result));
@ -462,13 +457,19 @@ Result<Value> Reader::read_number() {
while (is_digit(get())) forward(); while (is_digit(get())) forward();
if (match('e') || match('E')) { 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')) { } else if (match('e') || match('E')) {
is_float = true; 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')) { } 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())) { } else if (is_digit(get())) {
do { do {
forward(); forward();
@ -483,13 +484,17 @@ Result<Value> Reader::read_number() {
} }
if (match('e') || match('E')) { if (match('e') || match('E')) {
is_float = true; is_float = true;
if (!forward_exponent()) return ERROR(InvalidNumericLiteral); if (!forward_exponent()) {
return syntax_error(startloc, "invalid numeric literal");
}
} }
} }
Value res; 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]; char buf[32];
for (size_t i = 0; i < position_.offset - start; ++i) { for (size_t i = 0; i < position_.offset - start; ++i) {
@ -506,7 +511,9 @@ Result<Value> Reader::read_number() {
res = Value(TRY(Int64::create(strtoll(buf, 0, 10)))); 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_; SourcePosition endloc = position_;
@ -529,7 +536,9 @@ Result<Value> Reader::read_symbol() {
String str = TRY(_str.slice(start, end)); 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_; SourcePosition endloc = position_;
@ -647,26 +656,31 @@ bool Reader::forward_exponent() {
return forward_decimal_number(); return forward_decimal_number();
} }
Result<Value> Reader::syntax_error(SourcePosition pos, const char* msg) {
return ERROR_FMT(SyntaxError, _fname, ":", pos.line, ":", pos.column,
" Syntax error: ", msg);
}
Result<Value> read_one(const Value& value, bool syntax, bool as_code) { Result<Value> read_one(const Value& value, bool syntax, bool as_code) {
if (!value.is<String>()) return ERROR(TypeMismatch); if (!value.is<String>()) return ERROR(TypeMismatch);
auto fname = TRY(String::create("")); auto fname = TRY(String::create("<unknown>"));
auto r = Reader(fname, *value.to<String>(), syntax, as_code); auto r = Reader(fname, *value.to<String>(), syntax, as_code);
return r.read_one(); return r.read_one();
} }
Result<Value> read_one(const String& value, bool syntax, bool as_code) { Result<Value> read_one(const String& value, bool syntax, bool as_code) {
auto fname = TRY(String::create("")); auto fname = TRY(String::create("<unknown>"));
auto r = Reader(fname, value, syntax, as_code); auto r = Reader(fname, value, syntax, as_code);
return r.read_one(); return r.read_one();
} }
Result<Value> read_one(const char* value, bool syntax, bool as_code) { Result<Value> read_one(const char* value, bool syntax, bool as_code) {
auto fname = TRY(String::create("")); auto fname = TRY(String::create("<unknown>"));
auto s = TRY(String::create(value)); auto s = TRY(String::create(value));
auto r = Reader(fname, s, syntax, as_code); auto r = Reader(fname, s, syntax, as_code);
return r.read_one(); return r.read_one();
} }
Result<Value> read_multiple(const String& value, bool syntax, bool as_code) { Result<Value> read_multiple(const String& value, bool syntax, bool as_code) {
auto fname = TRY(String::create("")); auto fname = TRY(String::create("<unknown>"));
auto r = Reader(fname, value, syntax, as_code); auto r = Reader(fname, value, syntax, as_code);
return r.read_multiple(); return r.read_multiple();
} }

View file

@ -29,7 +29,6 @@ class Reader {
bool is_string_start(); bool is_string_start();
Result<Value> read_list(); Result<Value> read_list();
Result<Value> read_list_rec();
Result<Value> read_string(); Result<Value> read_string();
Result<Value> read_array(); Result<Value> read_array();
Result<Value> read_array_code(); Result<Value> read_array_code();
@ -43,6 +42,8 @@ class Reader {
Result<Value> read_nil(); Result<Value> read_nil();
Result<Value> read_quote(); Result<Value> read_quote();
Result<Value> syntax_error(SourcePosition pos, const char* message);
char32_t get(size_t offset = 0); char32_t get(size_t offset = 0);
bool match(const char* str); bool match(const char* str);
bool match(char c); bool match(char c);

View file

@ -11,8 +11,8 @@ class Result {
Result(T&& res) : _value(std::move(res)), _error(ErrorCode::Success) {} Result(T&& res) : _value(std::move(res)), _error(ErrorCode::Success) {}
Result(ErrorCode err) : _error(err) {} Result(ErrorCode err) : _error(err) {}
bool has_error() { return _error != ErrorCode::Success; } bool has_error() const { return _error != ErrorCode::Success; }
bool has_value() { return !has_error(); } bool has_value() const { return !has_error(); }
T& value() { return _value; } T& value() { return _value; }
T& operator*() { return _value; } T& operator*() { return _value; }
@ -36,8 +36,8 @@ class Result<void> {
Result& operator=(Result&& res) = default; Result& operator=(Result&& res) = default;
bool has_error() { return err != ErrorCode::Success; } bool has_error() const { return err != ErrorCode::Success; }
bool has_value() { return !has_error(); } bool has_value() const { return !has_error(); }
void value() {} void value() {}
void release_value() {} void release_value() {}

View file

@ -4,6 +4,7 @@
#include "common.hpp" #include "common.hpp"
#include "compiler.hpp" #include "compiler.hpp"
#include "die.hpp" #include "die.hpp"
#include "error.hpp"
#include "fio.hpp" #include "fio.hpp"
#include "lineedit.hpp" #include "lineedit.hpp"
#include "reader.hpp" #include "reader.hpp"
@ -27,21 +28,47 @@ Result<Value> run_string(const String& src) {
return res; return res;
} }
Result<void> print_error(const Result<Value>& res) {
if (res.has_error()) {
auto errobj = TRY(geterrobj().copy());
if (errobj.is<Nil>()) {
debug_print(geterr());
} else {
debug_print(errobj);
}
}
return Result<void>();
}
Result<void> run_repl() { Result<void> run_repl() {
Dict globals = TRY(Dict::create()); Dict globals = TRY(Dict::create());
while (true) { while (true) {
auto src = TRY(read_line("valeri> ")); 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)); auto compiled = TRY(compile(parsed));
Module& mod = *compiled.to<Module>(); Module& mod = *compiled.to<Module>();
auto vm = TRY(VM::create()); auto vm = TRY(VM::create());
auto res = TRY(vm.run(mod, globals)); auto maybe_res = vm.run(mod, globals);
if (maybe_res.has_value()) {
auto res = maybe_res.release_value();
globals = TRY(vm.globals()); globals = TRY(vm.globals());
if (!res.is<Nil>()) { if (!res.is<Nil>()) {
TRY(debug_print(res)); TRY(debug_print(res));
} }
} else {
// Need to print the error
print_error(maybe_res);
}
} }
return Result<void>(); return Result<void>();

View file

@ -379,7 +379,20 @@ Result<String> Writer::write_stack(const Stack& val) {
} }
Result<String> Writer::write_error(const Error& val) { Result<String> Writer::write_error(const Error& val) {
return TRY(String::create("#<error>")); auto res = TRY(String::create("#<error:"));
auto name = TRY(val.name());
auto name_str = TRY(String::create(*name.to<Symbol>()));
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<String> write_one(const Value& value) { Result<String> write_one(const Value& value) {