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)));
}
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) {
uint64_t size = 0;
for (uint64_t i = 0; i < str.size(); i++) {
@ -491,6 +503,11 @@ Result<Value> reverse(Value& val) {
return res;
}
Result<void> debug_print(const char* val) {
std::cout << val << "\n";
return Result<void>();
}
Result<void> debug_print(const String& val) {
auto ba = TRY(ByteArray::create(val));
@ -841,6 +858,8 @@ Result<Value> Syntax::rest() const {
return val.rest();
}
Result<String> build_string(const String& value) { return value.copy(); }
Result<String> build_string(const Value& value) {
if (value.is<String>()) {
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(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)));
}
static Result<String> create(const Symbol& sym);
static Result<String> create(const char* str) {
uint64_t size = strlen(str);
auto pod = TRY(arena_alloc<PodString>(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<PodSymbol>&& val) : _value(std::move(val)) {}
@ -1166,6 +1170,16 @@ class Error : public Object {
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> message() const;
@ -1273,6 +1287,7 @@ Result<Value> reverse(Value& val);
Result<void> debug_print(const String& val);
Result<void> debug_print(const Value& val);
Result<void> debug_print(const char* val);
template <class T>
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<String> build_string(const Value& value);
Result<String> build_string(const String& 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>
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(); }
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>
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; \
}))

View file

@ -72,7 +72,7 @@ Result<Value> Reader::read_one() {
return read_symbol();
}
return ERROR(ReadError);
return syntax_error(saved_position, "unknown token");
}
Result<Value> Reader::read_multiple() {
@ -88,29 +88,10 @@ Result<Value> Reader::read_multiple() {
res = Value(TRY(Pair::create(val, res)));
}
// We should never get here
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() {
if (!match('(')) return ERROR(ReadError);
@ -123,7 +104,7 @@ Result<Value> Reader::read_list() {
forward_whitespace();
if (is_eof()) {
return ERROR(ReadError);
return syntax_error(start, "unterminated list");
}
if (match(')')) {
@ -143,6 +124,7 @@ Result<Value> Reader::read_list() {
res = TRY(Pair::create(val, res));
}
// We should never get here
return ERROR(ReadError);
}
@ -159,7 +141,7 @@ Result<Value> 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<Value> Reader::read_array_code() {
res = TRY(Pair::create(val, res));
}
// We should never get here
return ERROR(ReadError);
}
@ -195,7 +178,7 @@ Result<Value> 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<Value> Reader::read_array_data() {
res = TRY(res.append(val));
}
// We should never get here
return ERROR(ReadError);
}
@ -237,7 +221,7 @@ Result<Value> 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<Value> Reader::read_dict_code() {
res = TRY(Pair::create(val2, res));
}
// We should never get here
return ERROR(ReadError);
}
@ -275,7 +260,7 @@ Result<Value> 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<Value> Reader::read_dict_data() {
res = TRY(res.insert(val1, val2));
}
// We should never get here
return ERROR(ReadError);
}
@ -309,7 +295,8 @@ Result<Value> 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<Value> 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<Value> 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<Value> 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<Value> 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<Value> Reader::read_string() {
forward();
}
if (!match('"')) return ERROR(UnterminatedStringLiteral);
if (!match('"')) {
return syntax_error(startloc, "unterminated string");
}
forward();
@ -434,7 +427,9 @@ Result<Value> 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<Value> 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<Value> 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<Value> 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<Value> 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<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) {
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);
return r.read_one();
}
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);
return r.read_one();
}
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 r = Reader(fname, s, syntax, as_code);
return r.read_one();
}
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);
return r.read_multiple();
}

View file

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

View file

@ -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<void> {
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() {}

View file

@ -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<Value> run_string(const String& src) {
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() {
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<Module>();
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<Nil>()) {
TRY(debug_print(res));
if (maybe_res.has_value()) {
auto res = maybe_res.release_value();
globals = TRY(vm.globals());
if (!res.is<Nil>()) {
TRY(debug_print(res));
}
} else {
// Need to print the error
print_error(maybe_res);
}
}

View file

@ -379,7 +379,20 @@ Result<String> Writer::write_stack(const Stack& 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) {