diff --git a/include/snuk/interpreter/error_code.h b/include/snuk/interpreter/error_code.h deleted file mode 100644 index 3b5c78a..0000000 --- a/include/snuk/interpreter/error_code.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "snuk/defines.h" - -typedef enum SnukErrorCode { - SNUK_ERROR_NONE = 0, - SNUK_ERROR_SHOULD_NOT_REACH_HERE, - SNUK_ERROR_SOMETHING_WENT_WRONG, - SNUK_ERROR_CONTROL_FLOW, - SNUK_ERROR_EXISTS, - SNUK_ERROR_NON_TYPE, - SNUK_ERROR_EXPECT_ASSIGN, - SNUK_ERROR_BUILTIN_INVALID_VALUE, - SNUK_ERROR_MEMBER_INITIALIZE, - SNUK_ERROR_SELF_CREATION, - SNUK_ERROR_PARAM_CREATION, - SNUK_ERROR_NON_FN, - SNUK_ERROR_PARAM_COUNT, - SNUK_ERROR_NO_PARAM, - SNUK_ERROR_PARAM, - SNUK_ERROR_PARAM_REQUIRED, - SNUK_ERROR_SELF, - SNUK_ERROR_SET_ENV_FAIL, - SNUK_ERROR_MEMBER, - SNUK_ERROR_INTERFACE, - SNUK_ERROR_TYPE_MISMATCH, - - SNUK_ERROR_MAX, -} SnukErrorCode; - -SNUK_API const char *snuk_error_code_get_msg(SnukErrorCode code); diff --git a/include/snuk/interpreter/interpreter.h b/include/snuk/interpreter/interpreter.h index 9bcfe20..1d4a2ea 100644 --- a/include/snuk/interpreter/interpreter.h +++ b/include/snuk/interpreter/interpreter.h @@ -5,6 +5,7 @@ #include "snuk/parser/snuk_expr.h" #include "snuk/parser/snuk_item.h" #include "snuk/refcount.h" +#include "snuk/snuk_error.h" #include "snuk/string_view.h" #include "snuk_env.h" #include "snuk_signal.h" @@ -19,6 +20,9 @@ * and for loops push and pop scopes. global is retained for the lifetime of * the interpreter so identifiers can fall through to the root. signal carries * the most recent control-flow signal raised during evaluation. + * + * err holds the first error encountered during item execution. The consumer + * reads it via snuk_interpreter_clear_error() after each exec_item call. */ typedef struct SnukInterpreter { SnukRefCounter *current; @@ -29,7 +33,9 @@ typedef struct SnukInterpreter { void *mem; SnukAllocator allocator; snLinearAllocator la; - SnukErrorCode err_code; + SnukError err; + SnukSrcLoc cur_loc; + char err_msg_buf[256]; } SnukInterpreter; /** @@ -90,3 +96,7 @@ SNUK_API bool snuk_interpreter_create_env( SnukInterpreter *intpret, SnukStringView name, SnukType *type, SnukValue value, bool is_const); SNUK_API bool snuk_interpreter_value_is_of_type(SnukInterpreter *intpret, SnukValue value, SnukType *type); + +SNUK_API SnukError snuk_interpreter_clear_error(SnukInterpreter *intpret); + +SNUK_API void snuk_interpreter_set_loc(SnukInterpreter *intpret, uint32_t line, uint32_t col); diff --git a/include/snuk/interpreter/interpreter_helper.h b/include/snuk/interpreter/interpreter_helper.h index d1b4eae..d2a7c62 100644 --- a/include/snuk/interpreter/interpreter_helper.h +++ b/include/snuk/interpreter/interpreter_helper.h @@ -1,19 +1,46 @@ #pragma once -#include "error_code.h" #include "interpreter.h" #include "snuk/darray.h" #include "snuk/defines.h" +#include "snuk/snuk_error.h" #include "snuk_scope.h" +#include +#include + SnukValue execute_block_expr( SnukInterpreter *intpret, SnukExpr *block, int capture_signals, int propogate_signals, bool weak_ref); SnukValue interpreter_copy_inst(SnukInterpreter *intpret, SnukValue inst); -SNUK_INLINE void interpreter_error(SnukInterpreter *intpret, SnukErrorCode err_code) { - if (intpret->err_code != SNUK_ERROR_NONE) return; - intpret->err_code = err_code; +SNUK_INLINE void interpreter_error(SnukInterpreter *intpret, SnukInterpError code, const char *msg) { + if (intpret->err.kind != SNUK_ERROR_KIND_NONE) return; + intpret->err = (SnukError){ + .kind = SNUK_ERROR_KIND_INTERP, + .code = (uint32_t)code, + .msg = msg, + .loc = intpret->cur_loc, + }; +} + +SNUK_INLINE void interpreter_error_fmt(SnukInterpreter *intpret, SnukInterpError code, const char *fmt, ...) { + if (intpret->err.kind != SNUK_ERROR_KIND_NONE) return; + va_list args; + va_start(args, fmt); + vsnprintf(intpret->err_msg_buf, sizeof(intpret->err_msg_buf), fmt, args); + va_end(args); + intpret->err = (SnukError){ + .kind = SNUK_ERROR_KIND_INTERP, + .code = (uint32_t)code, + .msg = intpret->err_msg_buf, + .loc = intpret->cur_loc, + }; +} + +SNUK_INLINE void interpreter_set_loc(SnukInterpreter *intpret, uint32_t line, uint32_t col) { + intpret->cur_loc.line = line; + intpret->cur_loc.col = col; } /** diff --git a/include/snuk/interpreter/snuk_value.h b/include/snuk/interpreter/snuk_value.h index e02475e..bf87136 100644 --- a/include/snuk/interpreter/snuk_value.h +++ b/include/snuk/interpreter/snuk_value.h @@ -1,6 +1,5 @@ #pragma once -#include "error_code.h" #include "snuk/defines.h" #include "snuk/parser/snuk_expr.h" #include "snuk/parser/snuk_type.h" @@ -43,7 +42,6 @@ typedef enum SnukValueType { */ struct SnukValue { SnukValueType type; - SnukErrorCode err_code; union { int64_t int_value; diff --git a/include/snuk/parser/parser.h b/include/snuk/parser/parser.h index 2ae3886..6121bec 100644 --- a/include/snuk/parser/parser.h +++ b/include/snuk/parser/parser.h @@ -2,6 +2,7 @@ #include "snuk/defines.h" #include "snuk/lexer.h" +#include "snuk/snuk_error.h" /* * We will have items similar to rust. @@ -32,8 +33,7 @@ typedef struct SnukParser { SnukAllocator *allocator; bool panic_mode; /**< Error and recovery state flags. */ - const char *err_msg; - SnukToken err_token; + SnukError err; } SnukParser; /** @@ -74,9 +74,19 @@ SNUK_API SnukItem *snuk_parser_next_item(SnukParser *parser); * @brief Report a parser error and enter panic mode. * * @param parser Parser context to operate on. - * @param err_msg Error message to print. + * @param code Parse error code. + * @param msg Error message to print. */ -void parser_error(SnukParser *parser, const char *err_msg); +void parser_error(SnukParser *parser, SnukParseError code, const char *msg); + +/** + * @brief Clear the parser error state. + * + * @param parser Parser context to operate on. + * + * @return Previous error value. + */ +SnukError snuk_parser_clear_error(SnukParser *parser); /** * @brief Recover parser state after an error. diff --git a/include/snuk/parser/parser_common.h b/include/snuk/parser/parser_common.h index 71731e5..60bd636 100644 --- a/include/snuk/parser/parser_common.h +++ b/include/snuk/parser/parser_common.h @@ -17,7 +17,8 @@ typedef struct SnukVar SnukVar; SNUK_INLINE void parser_advance(SnukParser *parser) { parser->previous = parser->current; parser->current = parser->next; - if (parser->current.type == SNUK_TOKEN_ERROR) parser_error(parser, "lexer error"); + if (parser->current.type == SNUK_TOKEN_ERROR) + parser_error(parser, SNUK_PARSE_ERR_LEXER_ERROR, "lexer error"); parser->next = snuk_lexer_next_token(&parser->lexer); } @@ -68,8 +69,12 @@ SNUK_INLINE bool parser_match(SnukParser *parser, SnukTokenType expected) { * @param expected Expected token type. * @param err_msg Error message to report if the token does not match. */ -SNUK_INLINE void parser_expect(SnukParser *parser, SnukTokenType expected, const char *err_msg) { - if (!parser_match(parser, expected)) parser_error(parser, err_msg); +SNUK_INLINE bool parser_expect(SnukParser *parser, SnukTokenType expected, const char *err_msg) { + if (!parser_match(parser, expected)) { + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, err_msg); + return false; + } + return true; } SNUK_INLINE bool parser_check_item_end(SnukParser *parser) { @@ -97,7 +102,8 @@ SNUK_INLINE bool parser_match_item_end(SnukParser *parser) { * @param parser Parser context to operate on. */ SNUK_INLINE void parser_expect_item_end(SnukParser *parser) { - if (!parser_match_item_end(parser)) parser_error(parser, "expected a new line or a semicolon"); + if (!parser_match_item_end(parser)) + parser_error(parser, SNUK_PARSE_ERR_EXPECTED_SEMICOLON_OR_NEWLINE, "expected a new line or a semicolon"); } SNUK_INLINE SnukStringView parser_copy_string_view(SnukParser *parser, SnukStringView sv) { diff --git a/include/snuk/parser/snuk_item.h b/include/snuk/parser/snuk_item.h index 4527cf0..14d1900 100644 --- a/include/snuk/parser/snuk_item.h +++ b/include/snuk/parser/snuk_item.h @@ -3,6 +3,7 @@ #include "parser_common.h" #include "snuk/darray.h" #include "snuk/defines.h" +#include "snuk/snuk_error.h" #include "snuk/string_view.h" #include "snuk_type.h" @@ -57,8 +58,7 @@ struct SnukItem { } interface_item; struct { - const char *msg; - SnukToken token; + SnukError error; } error; }; }; @@ -215,18 +215,16 @@ SNUK_INLINE SnukItem *build_interface_item(SnukParser *parser, SnukStringView na * @brief Build a error item. * * @param parser Parser context to operate on. - * @param msg The message - * @param token The token + * @param error The error * * @return Return error item. */ -SNUK_INLINE SnukItem *build_error_item(SnukParser *parser, const char *msg, SnukToken token) { +SNUK_INLINE SnukItem *build_error_item(SnukParser *parser, SnukError error) { SnukItem *item = parser_create_item(parser); *item = (SnukItem){ .type = SNUK_ITEM_ERROR, .error = { - .msg = msg, - .token = token, + .error = error, }, }; return item; diff --git a/include/snuk/snuk_error.h b/include/snuk/snuk_error.h new file mode 100644 index 0000000..6d930dc --- /dev/null +++ b/include/snuk/snuk_error.h @@ -0,0 +1,78 @@ +#pragma once + +#include + +typedef struct { + const char *file; // filename or NULL + uint32_t line; // 1-indexed + uint32_t col; // 1-indexed +} SnukSrcLoc; + +#define SNUK_SRC_LOC_NULL ((SnukSrcLoc){NULL, 0, 0}) + +typedef enum { + SNUK_ERROR_KIND_NONE = 0, + SNUK_ERROR_KIND_PARSE, + SNUK_ERROR_KIND_INTERP, +} SnukErrorKind; + +typedef enum { + SNUK_INTERP_ERR_NONE = 0, + SNUK_INTERP_ERR_TYPE, // type system violations + SNUK_INTERP_ERR_NAME, // name resolution / declaration conflicts + SNUK_INTERP_ERR_FUNCALL, // function call errors + SNUK_INTERP_ERR_ASSIGN, // assignment / initialization failures + SNUK_INTERP_ERR_CONTROL_FLOW, // break/continue/return outside valid scope + SNUK_INTERP_ERR_INTERFACE, // interface creation errors + SNUK_INTERP_ERR_INTERNAL, // unexpected/unreachable code paths +} SnukInterpError; + +typedef enum { + SNUK_PARSE_ERR_NONE = 0, + SNUK_PARSE_ERR_UNEXPECTED_TOKEN, + SNUK_PARSE_ERR_EXPECTED_IDENTIFIER, + SNUK_PARSE_ERR_EXPECTED_TYPE_ANNOTATION, + SNUK_PARSE_ERR_EXPECTED_SEMICOLON_OR_NEWLINE, + SNUK_PARSE_ERR_EXPECTED_CLOSE_BRACE, + SNUK_PARSE_ERR_EXPECTED_CLOSE_PAREN, + SNUK_PARSE_ERR_EXPECTED_CLOSE_BRACKET, + SNUK_PARSE_ERR_EXPECTED_KEYWORD, + SNUK_PARSE_ERR_EXPECTED_EXPRESSION, + SNUK_PARSE_ERR_EXPECTED_TYPE, + SNUK_PARSE_ERR_EXPECTED_MEMBER, + SNUK_PARSE_ERR_INVALID_LITERAL, + SNUK_PARSE_ERR_UNTERMINATED_STRING, + SNUK_PARSE_ERR_LEXER_ERROR, + SNUK_PARSE_ERR_UNKNOWN, +} SnukParseError; + +typedef struct { + SnukErrorKind kind; + uint32_t code; // cast from SnukInterpError or SnukParseError + const char *msg; // human-readable description + SnukSrcLoc loc; +} SnukError; + +#define SNUK_ERROR_NONE ((SnukError){SNUK_ERROR_KIND_NONE, 0, NULL, SNUK_SRC_LOC_NULL}) + +static inline const char *snuk_interp_error_msg(SnukInterpError code) { + switch (code) { + case SNUK_INTERP_ERR_NONE: + return "no error"; + case SNUK_INTERP_ERR_TYPE: + return "type error"; + case SNUK_INTERP_ERR_NAME: + return "name error"; + case SNUK_INTERP_ERR_FUNCALL: + return "function call error"; + case SNUK_INTERP_ERR_ASSIGN: + return "assignment error"; + case SNUK_INTERP_ERR_CONTROL_FLOW: + return "control flow error"; + case SNUK_INTERP_ERR_INTERFACE: + return "interface error"; + case SNUK_INTERP_ERR_INTERNAL: + return "internal error"; + } + return "unknown error"; +} diff --git a/repl/runtime.c b/repl/runtime.c index cee15c8..097939d 100644 --- a/repl/runtime.c +++ b/repl/runtime.c @@ -1,8 +1,8 @@ #include "runtime.h" -#include #include #include +#include void snuk_runtime_execute(Runtime *rt, const char *src) { SnukParser parser; @@ -12,11 +12,20 @@ void snuk_runtime_execute(Runtime *rt, const char *src) { while (true) { item = snuk_parser_next_item(&parser); if (!item) break; - // snuk_item_log(item); - // log_trace("", NULL); + + SnukError parse_err = snuk_parser_clear_error(&parser); + if (parse_err.kind != SNUK_ERROR_KIND_NONE) { + log_error("[Error] at line %u, col %u: %s", parse_err.loc.line, parse_err.loc.col, + parse_err.msg); + continue; + } + SnukValue value = snuk_interpreter_exec_item(&rt->interpreter, item); - if (value.err_code) { - log_error("%s", snuk_error_code_get_msg(value.err_code)); + SnukError err = snuk_interpreter_clear_error(&rt->interpreter); + if (err.kind != SNUK_ERROR_KIND_NONE) { + if (err.loc.line) + log_error("[Error] at line %u, col %u: %s", err.loc.line, err.loc.col, err.msg); + else log_error("[Error] %s", err.msg); } snuk_value_log(value); log_trace("", NULL); diff --git a/src/interpreter/CMakeLists.txt b/src/interpreter/CMakeLists.txt index ad5e9f9..1c4bf5c 100644 --- a/src/interpreter/CMakeLists.txt +++ b/src/interpreter/CMakeLists.txt @@ -5,7 +5,6 @@ set(PUBLIC_HEADERS snuk_scope.h snuk_env.h native.h - error_code.h ) set(HEADERS @@ -14,7 +13,6 @@ set(HEADERS set(SRCS interpreter.c snuk_value.c - error_code.c native.c ) diff --git a/src/interpreter/error_code.c b/src/interpreter/error_code.c deleted file mode 100644 index 998c9bf..0000000 --- a/src/interpreter/error_code.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "snuk/interpreter/error_code.h" - -static const char *error_messages[] = { - [SNUK_ERROR_NONE] = "All is well", - [SNUK_ERROR_SHOULD_NOT_REACH_HERE] = "Shouldn't reach here", - [SNUK_ERROR_SOMETHING_WENT_WRONG] = "Something went wrong", - [SNUK_ERROR_CONTROL_FLOW] = "control flow item outside scope", - [SNUK_ERROR_EXISTS] = "variable already exists", - [SNUK_ERROR_NON_TYPE] = "expected a type", - [SNUK_ERROR_EXPECT_ASSIGN] = "expected assignment expression", - [SNUK_ERROR_BUILTIN_INVALID_VALUE] = "invalid value to builtin type member value", - [SNUK_ERROR_MEMBER_INITIALIZE] = "failed to initialize member", - [SNUK_ERROR_SELF_CREATION] = "failed to create self", - [SNUK_ERROR_PARAM_CREATION] = "failed to create parameter", - [SNUK_ERROR_NON_FN] = "call expression on non function", - [SNUK_ERROR_PARAM_COUNT] = "parameter count mismatch", - [SNUK_ERROR_NO_PARAM] = "parameter doesn't exists", - [SNUK_ERROR_PARAM] = "error in parameter passing", - [SNUK_ERROR_PARAM_REQUIRED] = "parameter is required", - [SNUK_ERROR_SELF] = "failed to get self", - [SNUK_ERROR_SET_ENV_FAIL] = "failed to set env value", - [SNUK_ERROR_MEMBER] = "couldn't find the member", - [SNUK_ERROR_INTERFACE] = "failed to create interface", - [SNUK_ERROR_TYPE_MISMATCH] = "types are not same", -}; - -const char *snuk_error_code_get_msg(SnukErrorCode code) { - return error_messages[code]; -} diff --git a/src/interpreter/interpreter.c b/src/interpreter/interpreter.c index c2ff1de..5f540b1 100644 --- a/src/interpreter/interpreter.c +++ b/src/interpreter/interpreter.c @@ -65,7 +65,8 @@ void snuk_interpreter_init(SnukInterpreter *intpret) { .realloc = realloc_fn, .free = free_fn, }, - .err_code = SNUK_ERROR_NONE, + .err = SNUK_ERROR_NONE, + .cur_loc = SNUK_SRC_LOC_NULL, }; sn_linear_allocator_init(&intpret->la, intpret->mem, PAGES * snuk_page_size()); intpret->current = snuk_ref_counter_retain(intpret->global); @@ -206,10 +207,8 @@ SnukValue snuk_interpreter_exec_item(SnukInterpreter *intpret, SnukItem *item) { interpreter_clear_trash(intpret); SnukValue res = interpreter_exec_item(intpret, item, true); - if (intpret->signal != SNUK_SIGNAL_NONE) interpreter_error(intpret, SNUK_ERROR_CONTROL_FLOW); - - res.err_code = intpret->err_code; - intpret->err_code = SNUK_ERROR_NONE; + if (intpret->signal != SNUK_SIGNAL_NONE) + interpreter_error(intpret, SNUK_INTERP_ERR_CONTROL_FLOW, "break/continue/return outside of function or loop scope"); return res; } @@ -218,6 +217,16 @@ SnukValue snuk_interpreter_eval_expr(SnukInterpreter *intpret, SnukExpr *expr) { return interpreter_eval_expr(intpret, expr, true); } +SnukError snuk_interpreter_clear_error(SnukInterpreter *intpret) { + SnukError err = intpret->err; + intpret->err = SNUK_ERROR_NONE; + return err; +} + +void snuk_interpreter_set_loc(SnukInterpreter *intpret, uint32_t line, uint32_t col) { + interpreter_set_loc(intpret, line, col); +} + /** * @brief Evaluate a unary expression's operand and apply the operator. */ @@ -402,7 +411,7 @@ static SnukValue case SNUK_VALUE_MAX: default: - interpreter_error(intpret, SNUK_ERROR_SHOULD_NOT_REACH_HERE); + interpreter_error(intpret, SNUK_INTERP_ERR_INTERNAL, "unexpected value type in comparison"); break; } if (op == SNUK_TOKEN_BANG_EQUAL) res.bool_value = !res.bool_value; @@ -426,7 +435,7 @@ static SnukValue } fail: - interpreter_error(intpret, SNUK_ERROR_TYPE_MISMATCH); + interpreter_error(intpret, SNUK_INTERP_ERR_TYPE, "type mismatch: incompatible operand types in binary operation"); return (SnukValue){.type = SNUK_VALUE_UNKOWN}; } @@ -552,7 +561,7 @@ static void interpreter_print_value(SnukInterpreter *intpret, SnukValue value) { break; default: - interpreter_error(intpret, SNUK_ERROR_SHOULD_NOT_REACH_HERE); + interpreter_error(intpret, SNUK_INTERP_ERR_INTERNAL, "unhandled value type in value printer"); break; } } @@ -597,7 +606,7 @@ SnukValue execute_block_expr( } else if (intpret->signal & propogate_signals) { break; } else { - interpreter_error(intpret, SNUK_ERROR_SHOULD_NOT_REACH_HERE); + interpreter_error(intpret, SNUK_INTERP_ERR_INTERNAL, "unexpected control flow signal in block execution"); } } @@ -660,7 +669,7 @@ static SnukValue execute_while_expr(SnukInterpreter *intpret, SnukExpr *expr, bo goto end; case SNUK_SIGNAL_CONTINUE: - interpreter_error(intpret, SNUK_ERROR_SHOULD_NOT_REACH_HERE); + interpreter_error(intpret, SNUK_INTERP_ERR_INTERNAL, "continue signal not captured in while-loop signal mask"); break; case SNUK_SIGNAL_NONE: @@ -693,7 +702,8 @@ static SnukValue execute_for_expr(SnukInterpreter *intpret, SnukExpr *expr, bool if (expr->for_loop.init) { SnukValue val = interpreter_exec_item(intpret, expr->for_loop.init, false); if (intpret->signal != SNUK_SIGNAL_NONE) - interpreter_error(intpret, SNUK_ERROR_CONTROL_FLOW); + interpreter_error(intpret, SNUK_INTERP_ERR_CONTROL_FLOW, + "break/continue/return not allowed in for-loop initializer"); snuk_value_free(val); } @@ -718,7 +728,7 @@ static SnukValue execute_for_expr(SnukInterpreter *intpret, SnukExpr *expr, bool goto end; case SNUK_SIGNAL_CONTINUE: - interpreter_error(intpret, SNUK_ERROR_SHOULD_NOT_REACH_HERE); + interpreter_error(intpret, SNUK_INTERP_ERR_INTERNAL, "continue signal not captured in for-loop signal mask"); break; case SNUK_SIGNAL_NONE: @@ -766,7 +776,8 @@ static SnukValue execute_type_declaration(SnukInterpreter *intpret, SnukExpr *ex SnukValue val = interpreter_exec_item(intpret, expr->type_expr.members[i], true); snuk_value_free(val); if (intpret->signal != SNUK_SIGNAL_NONE) - interpreter_error(intpret, SNUK_ERROR_CONTROL_FLOW); + interpreter_error(intpret, SNUK_INTERP_ERR_CONTROL_FLOW, + "break/continue/return not allowed in type declaration body"); } interpreter_pop_scope(intpret); @@ -776,7 +787,8 @@ static SnukValue execute_type_declaration(SnukInterpreter *intpret, SnukExpr *ex // Syntax sugar if (expr->type_expr.name.len && !snuk_interpreter_create_env(intpret, expr->type_expr.name, value.type_value.type, value, false)) - interpreter_error(intpret, SNUK_ERROR_EXISTS); + interpreter_error_fmt(intpret, SNUK_INTERP_ERR_NAME, "variable '%.*s' already exists in this scope", + (int)expr->type_expr.name.len, expr->type_expr.name.str); return value; } @@ -785,7 +797,7 @@ static SnukValue execute_inst_creation(SnukInterpreter *intpret, SnukExpr *expr, SNUK_UNUSED(weak_ref); SnukValue type = snuk_interpreter_get_env(intpret, expr->type_inst_expr.type->name); if (type.type != SNUK_VALUE_TYPE) { - interpreter_error(intpret, SNUK_ERROR_NON_TYPE); + interpreter_error(intpret, SNUK_INTERP_ERR_TYPE, "expected a type value for instance creation"); return (SnukValue){.type = SNUK_VALUE_UNKOWN}; } @@ -810,7 +822,7 @@ static SnukValue execute_inst_creation(SnukInterpreter *intpret, SnukExpr *expr, for (uint64_t i = 0; i < init_count; ++i) { SnukExpr *assign = expr->type_inst_expr.init[i]; if (assign->type != SNUK_EXPR_ASSIGN) { - interpreter_error(intpret, SNUK_ERROR_EXPECT_ASSIGN); + interpreter_error(intpret, SNUK_INTERP_ERR_ASSIGN, "expected assignment expression in instance initializer"); break; } @@ -821,10 +833,14 @@ static SnukValue execute_inst_creation(SnukInterpreter *intpret, SnukExpr *expr, // if builtin type, make sure value of value member is right SnukValueType val_type = snuk_builtins_get_value_type(value.type_value.type->name); if (val_type != SNUK_VALUE_UNKOWN && snuk_string_view_equal(name, value_str)) - if (val.type != val_type) interpreter_error(intpret, SNUK_ERROR_BUILTIN_INVALID_VALUE); + if (val.type != val_type) + interpreter_error_fmt( + intpret, SNUK_INTERP_ERR_TYPE, + "invalid value type for builtin member 'value': expected a different type"); if (!interpreter_set_member(intpret, value, name, val)) - interpreter_error(intpret, SNUK_ERROR_MEMBER_INITIALIZE); + interpreter_error_fmt(intpret, SNUK_INTERP_ERR_ASSIGN, + "failed to initialize member '%.*s' in instance", (int)name.len, name.str); snuk_value_free(val); } @@ -833,7 +849,7 @@ static SnukValue execute_inst_creation(SnukInterpreter *intpret, SnukExpr *expr, self_value.type_value.weak_ref = true; if (!snuk_interpreter_create_env(intpret, self_str, self_value.type_value.type, self_value, true)) - interpreter_error(intpret, SNUK_ERROR_SELF_CREATION); + interpreter_error(intpret, SNUK_INTERP_ERR_ASSIGN, "failed to create 'self' reference in instance scope"); snuk_value_free(self_value); @@ -847,7 +863,8 @@ static SnukValue execute_inst_creation(SnukInterpreter *intpret, SnukExpr *expr, // Syntax sugar if (expr->type_inst_expr.name.len && !snuk_interpreter_create_env(intpret, expr->type_inst_expr.name, value.type_value.type, value, false)) - interpreter_error(intpret, SNUK_ERROR_EXISTS); + interpreter_error_fmt(intpret, SNUK_INTERP_ERR_NAME, "variable '%.*s' already exists in this scope", + (int)expr->type_inst_expr.name.len, expr->type_inst_expr.name.str); interpreter_trash(intpret, type); @@ -921,7 +938,8 @@ static SnukValue execute_fn_expr(SnukInterpreter *intpret, SnukExpr *expr, bool SnukValue value = (SnukValue){.type = SNUK_VALUE_UNKOWN}; if (param->value) value = interpreter_eval_expr(intpret, param->value, false); if (!snuk_interpreter_create_env(intpret, param->name, param->type, value, false)) - interpreter_error(intpret, SNUK_ERROR_PARAM_CREATION); + interpreter_error_fmt(intpret, SNUK_INTERP_ERR_NAME, "failed to create function parameter '%.*s'", + (int)param->name.len, param->name.str); snuk_value_free(value); } @@ -943,7 +961,8 @@ static SnukValue execute_fn_expr(SnukInterpreter *intpret, SnukExpr *expr, bool // Syntax sugar if (expr->fn_expr.name.len && !snuk_interpreter_create_env(intpret, expr->fn_expr.name, value.fn_value.type, value, false)) - interpreter_error(intpret, SNUK_ERROR_EXISTS); + interpreter_error_fmt(intpret, SNUK_INTERP_ERR_NAME, "function '%.*s' already exists in this scope", + (int)expr->fn_expr.name.len, expr->fn_expr.name.str); return value; } @@ -955,7 +974,7 @@ static SnukValue execute_fn_expr(SnukInterpreter *intpret, SnukExpr *expr, bool static SnukValue execute_call_expr(SnukInterpreter *intpret, SnukExpr *expr, bool weak_ref) { SnukValue fn = interpreter_eval_expr(intpret, expr->call.fn, weak_ref); if (fn.type != SNUK_VALUE_FN && fn.type != SNUK_VALUE_FN_NATIVE) { - interpreter_error(intpret, SNUK_ERROR_NON_FN); + interpreter_error(intpret, SNUK_INTERP_ERR_FUNCALL, "call expression on non-function value"); return (SnukValue){.type = SNUK_VALUE_UNKOWN}; } @@ -970,7 +989,9 @@ static SnukValue execute_call_expr(SnukInterpreter *intpret, SnukExpr *expr, boo uint64_t fn_param_count = snuk_darray_get_length(fn_scope->vars); uint64_t param_count = snuk_darray_get_length(expr->call.params); - if (fn_param_count < param_count) interpreter_error(intpret, SNUK_ERROR_PARAM_COUNT); + if (fn_param_count < param_count) + interpreter_error_fmt(intpret, SNUK_INTERP_ERR_FUNCALL, "function expects %llu argument(s) but %llu provided", + (unsigned long long)fn_param_count, (unsigned long long)param_count); bool named_params = false; for (uint64_t i = 0; i < param_count; ++i) { @@ -987,7 +1008,8 @@ static SnukValue execute_call_expr(SnukInterpreter *intpret, SnukExpr *expr, boo name = param->assign.identifier->identifier; fn_env = snuk_scope_lookup(fn_scope_rc, name, NULL); if (!fn_env) { - interpreter_error(intpret, SNUK_ERROR_NO_PARAM); + interpreter_error_fmt(intpret, SNUK_INTERP_ERR_FUNCALL, "unknown parameter '%.*s'", + (int)name.len, name.str); break; } type = fn_env->type; @@ -997,13 +1019,14 @@ static SnukValue execute_call_expr(SnukInterpreter *intpret, SnukExpr *expr, boo type = fn_env->type; value = param; } else { - interpreter_error(intpret, SNUK_ERROR_PARAM); + interpreter_error(intpret, SNUK_INTERP_ERR_FUNCALL, "cannot mix positional and named arguments"); break; } SnukValue val = interpreter_eval_expr(intpret, value, true); if (!snuk_interpreter_create_env(intpret, name, type, val, false)) - interpreter_error(intpret, SNUK_ERROR_PARAM_CREATION); + interpreter_error_fmt(intpret, SNUK_INTERP_ERR_FUNCALL, + "failed to bind argument to parameter '%.*s'", (int)name.len, name.str); snuk_value_free(val); } @@ -1014,10 +1037,11 @@ static SnukValue execute_call_expr(SnukInterpreter *intpret, SnukExpr *expr, boo SnukEnv *env = snuk_scope_lookup(intpret->current, fn_env->name, NULL); if (!env) { if (fn_env->value.type == SNUK_VALUE_UNKOWN) - interpreter_error(intpret, SNUK_ERROR_PARAM_REQUIRED); + interpreter_error_fmt(intpret, SNUK_INTERP_ERR_FUNCALL, "required parameter '%.*s' is missing", + (int)fn_env->name.len, fn_env->name.str); if (!snuk_interpreter_create_env(intpret, fn_env->name, fn_env->type, fn_env->value, false)) - interpreter_error(intpret, SNUK_ERROR_SOMETHING_WENT_WRONG); + interpreter_error(intpret, SNUK_INTERP_ERR_INTERNAL, "failed to apply default value for parameter"); } } @@ -1026,7 +1050,7 @@ static SnukValue execute_call_expr(SnukInterpreter *intpret, SnukExpr *expr, boo interpreter_pop_scope(intpret); - if (intpret->err_code) { + if (intpret->err.kind != SNUK_ERROR_KIND_NONE) { snuk_ref_counter_release(&new_scope); return (SnukValue){.type = SNUK_VALUE_UNKOWN}; } @@ -1119,7 +1143,8 @@ static SnukValue interpreter_exec_item(SnukInterpreter *intpret, SnukItem *item, SnukValue value = interpreter_eval_expr(intpret, item->var->value, weak_ref); if (!snuk_interpreter_create_env( intpret, item->var->name, item->var->type, value, item->type == SNUK_ITEM_CONST_DECL)) - interpreter_error(intpret, SNUK_ERROR_EXISTS); + interpreter_error_fmt(intpret, SNUK_INTERP_ERR_NAME, "variable '%.*s' already exists in this scope", + (int)item->var->name.len, item->var->name.str); return value; } @@ -1148,7 +1173,10 @@ static SnukValue interpreter_exec_item(SnukInterpreter *intpret, SnukItem *item, return execute_interface(intpret, item, weak_ref); case SNUK_ITEM_ERROR: - log_error("Error: %s", item->error.msg); + if (intpret->err.kind == SNUK_ERROR_KIND_NONE) { + intpret->err = item->error.error; + } + return (SnukValue){.type = SNUK_VALUE_NULL}; return (SnukValue){.type = SNUK_VALUE_NULL}; case SNUK_ITEM_MAX: @@ -1156,7 +1184,7 @@ static SnukValue interpreter_exec_item(SnukInterpreter *intpret, SnukItem *item, break; } - interpreter_error(intpret, SNUK_ERROR_SHOULD_NOT_REACH_HERE); + interpreter_error(intpret, SNUK_INTERP_ERR_INTERNAL, "unhandled item type in item executor"); return (SnukValue){.type = SNUK_VALUE_UNKOWN}; } @@ -1242,7 +1270,7 @@ static SnukValue interpreter_eval_expr(SnukInterpreter *intpret, SnukExpr *expr, case SNUK_EXPR_SELF: { SnukValue self_value = snuk_interpreter_get_env(intpret, self_str); if (self_value.type != SNUK_VALUE_TYPE_INST) - interpreter_error(intpret, SNUK_ERROR_SELF); + interpreter_error(intpret, SNUK_INTERP_ERR_NAME, "'self' is not available in this context"); return self_value; } @@ -1257,7 +1285,7 @@ static SnukValue interpreter_eval_expr(SnukInterpreter *intpret, SnukExpr *expr, break; } - interpreter_error(intpret, SNUK_ERROR_SHOULD_NOT_REACH_HERE); + interpreter_error(intpret, SNUK_INTERP_ERR_INTERNAL, "unhandled expression type in expression evaluator"); return (SnukValue){.type = SNUK_VALUE_UNKOWN}; } @@ -1268,20 +1296,22 @@ static SnukValue execute_assign_expr(SnukInterpreter *intpret, SnukExpr *expr, b case SNUK_EXPR_IDENTIFIER: if (!snuk_interpreter_set_env(intpret, identifier->identifier, value)) - interpreter_error(intpret, SNUK_ERROR_SET_ENV_FAIL); + interpreter_error_fmt(intpret, SNUK_INTERP_ERR_ASSIGN, "cannot assign to '%.*s': variable not found or type mismatch", + (int)identifier->identifier.len, identifier->identifier.str); break; case SNUK_EXPR_MEMBER: { SnukExpr *field = identifier->member_access.field; SnukValue type_or_inst = interpreter_eval_expr(intpret, identifier->member_access.type, weak_ref); if (!interpreter_set_member(intpret, type_or_inst, field->identifier, value)) - interpreter_error(intpret, SNUK_ERROR_SET_ENV_FAIL); + interpreter_error_fmt(intpret, SNUK_INTERP_ERR_ASSIGN, "cannot assign to member '%.*s': not found or locked", + (int)field->identifier.len, field->identifier.str); interpreter_trash(intpret, type_or_inst); break; } default: - interpreter_error(intpret, SNUK_ERROR_SHOULD_NOT_REACH_HERE); + interpreter_error(intpret, SNUK_INTERP_ERR_INTERNAL, "unhandled identifier type in assignment"); break; } return value; @@ -1336,7 +1366,7 @@ static SnukValue execute_member_get(SnukInterpreter *intpret, SnukExpr *expr, bo }; break; default: - interpreter_error(intpret, SNUK_ERROR_SHOULD_NOT_REACH_HERE); + interpreter_error(intpret, SNUK_INTERP_ERR_INTERNAL, "unhandled primitive type in member access wrapper"); break; } @@ -1366,7 +1396,10 @@ static SnukValue execute_member_get(SnukInterpreter *intpret, SnukExpr *expr, bo res.native_fn.instance = snuk_ref_counter_retain_weak(type_or_inst.type_value.closure); } - if (res.type == SNUK_VALUE_UNKOWN) interpreter_error(intpret, SNUK_ERROR_MEMBER); + if (res.type == SNUK_VALUE_UNKOWN) + interpreter_error_fmt( + intpret, SNUK_INTERP_ERR_NAME, "type or instance has no member '%.*s'", + (int)expr->member_access.field->identifier.len, expr->member_access.field->identifier.str); interpreter_trash(intpret, type_or_inst); return res; @@ -1375,7 +1408,7 @@ static SnukValue execute_member_get(SnukInterpreter *intpret, SnukExpr *expr, bo static SnukValue execute_extend(SnukInterpreter *intpret, SnukItem *item, bool weak_ref) { SnukValue type = interpreter_eval_expr(intpret, item->extend_item.type, weak_ref); if (type.type != SNUK_VALUE_TYPE) { - interpreter_error(intpret, SNUK_ERROR_NON_TYPE); + interpreter_error(intpret, SNUK_INTERP_ERR_TYPE, "expected a type value for extend"); return type; } @@ -1387,7 +1420,7 @@ static SnukValue execute_extend(SnukInterpreter *intpret, SnukItem *item, bool w SnukValue val = interpreter_exec_item(intpret, item->extend_item.members[i], true); snuk_value_free(val); if (intpret->signal != SNUK_SIGNAL_NONE) - interpreter_error(intpret, SNUK_ERROR_CONTROL_FLOW); + interpreter_error(intpret, SNUK_INTERP_ERR_CONTROL_FLOW, "break/continue/return not allowed in extend body"); } type.type_value.closure = snuk_ref_counter_move(&intpret->current); @@ -1405,7 +1438,8 @@ static SnukValue execute_interface(SnukInterpreter *intpret, SnukItem *item, boo }, }; if (!snuk_interpreter_create_env(intpret, item->interface_item.name, item->interface_item.type, value, false)) - interpreter_error(intpret, SNUK_ERROR_INTERFACE); + interpreter_error_fmt(intpret, SNUK_INTERP_ERR_INTERFACE, "failed to create interface '%.*s'", + (int)item->interface_item.name.len, item->interface_item.name.str); return value; } @@ -1433,7 +1467,8 @@ SnukValue interpreter_copy_inst(SnukInterpreter *intpret, SnukValue inst) { else val = snuk_value_copy(scope->vars[i]->value); if (!snuk_interpreter_create_env( intpret, scope->vars[i]->name, scope->vars[i]->type, val, scope->vars[i]->is_const)) - interpreter_error(intpret, SNUK_ERROR_MEMBER_INITIALIZE); + interpreter_error_fmt(intpret, SNUK_INTERP_ERR_ASSIGN, "failed to initialize member '%.*s' in instance copy", + (int)scope->vars[i]->name.len, scope->vars[i]->name.str); snuk_value_free(val); } @@ -1442,7 +1477,7 @@ SnukValue interpreter_copy_inst(SnukInterpreter *intpret, SnukValue inst) { self_value.type_value.weak_ref = true; if (!snuk_interpreter_create_env(intpret, self_str, self_value.type_value.type, self_value, true)) - interpreter_error(intpret, SNUK_ERROR_SELF_CREATION); + interpreter_error(intpret, SNUK_INTERP_ERR_ASSIGN, "failed to create 'self' reference in instance copy"); snuk_value_free(self_value); diff --git a/src/parser/parser.c b/src/parser/parser.c index 2ec2a6d..e930543 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -12,30 +12,47 @@ void snuk_parser_init(SnukParser *parser, const char *src, SnukAllocator *alloca parser->previous = (SnukToken){0}; parser->current = snuk_lexer_next_token(&parser->lexer); - if (parser->current.type == SNUK_TOKEN_ERROR) parser_error(parser, "lexer error"); + if (parser->current.type == SNUK_TOKEN_ERROR) + parser_error(parser, SNUK_PARSE_ERR_LEXER_ERROR, "lexer error"); parser->next = snuk_lexer_next_token(&parser->lexer); } +SnukError snuk_parser_clear_error(SnukParser *parser) { + SnukError err = parser->err; + parser->err = SNUK_ERROR_NONE; + return err; +} + void snuk_parser_deinit(SnukParser *parser) { if (!parser) return; snuk_lexer_deinit(&parser->lexer); *parser = (SnukParser){0}; } -void parser_error(SnukParser *parser, const char *err_msg) { +void parser_error(SnukParser *parser, SnukParseError code, const char *msg) { if (parser->panic_mode) return; parser->panic_mode = true; - parser->err_msg = err_msg; - parser->err_token = parser->current; + parser->err = (SnukError){ + .kind = SNUK_ERROR_KIND_PARSE, + .code = code, + .msg = msg, + .loc = { + .line = parser->current.line, + .col = parser->current.col, + }, + }; } SnukItem *parser_sync(SnukParser *parser) { + while (parser->current.type != SNUK_TOKEN_EOF && parser->current.type != SNUK_TOKEN_SEMICOLON + && parser->current.type != SNUK_TOKEN_VSEMICOLON) { + parser_advance(parser); + } + SnukItem *item = build_error_item(parser, parser->err); parser->panic_mode = false; - if (parser->previous.type != SNUK_TOKEN_VSEMICOLON && parser->previous.type != SNUK_TOKEN_SEMICOLON) - while (!parser_match_item_end(parser)) parser_advance(parser); - return build_error_item(parser, parser->err_msg, parser->err_token); + return item; } SnukItem *snuk_parser_next_item(SnukParser *parser) { diff --git a/src/parser/snuk_expr.c b/src/parser/snuk_expr.c index 74f0ed2..b8b7620 100644 --- a/src/parser/snuk_expr.c +++ b/src/parser/snuk_expr.c @@ -360,7 +360,7 @@ static SnukExpr *parse_precedence(SnukParser *parser, Precedence precedence) { parser_advance(parser); prefix_fn pfn = get_rule(parser->previous.type)->pfn; if (!pfn) { - parser_error(parser, "expected expression"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "expected expression"); return NULL; } @@ -398,7 +398,7 @@ static SnukExpr *parse_primary(SnukParser *parser) { break; default: // TODO: - parser_error(parser, "unexpected expression"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "unexpected expression"); break; } @@ -431,7 +431,7 @@ static SnukExpr *parse_assignment(SnukParser *parser, SnukExpr *left) { static SnukExpr *parse_compound_assignment(SnukParser *parser, SnukExpr *left) { if (left->type != SNUK_EXPR_IDENTIFIER && left->type != SNUK_EXPR_MEMBER) { - parser_error(parser, "invalid assignment target"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "invalid assignment target"); return NULL; } SnukTokenType op = parser->previous.type; @@ -568,7 +568,7 @@ static SnukExpr *parse_fn(SnukParser *parser) { } if (parser->previous.type != SNUK_TOKEN_RPAREN) { - parser_error(parser, "expected ')'"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "expected ')'"); return NULL; } @@ -594,7 +594,7 @@ static SnukExpr *parse_call(SnukParser *parser, SnukExpr *left) { } if (parser->previous.type != SNUK_TOKEN_RPAREN) { - parser_error(parser, "expected ')'"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "expected ')'"); return NULL; } return build_call_expr(parser, left, params); @@ -625,7 +625,7 @@ static SnukExpr *parse_list(SnukParser *parser) { } if (parser->previous.type != SNUK_TOKEN_RBRACKET) { - parser_error(parser, "expected ']' after list elements"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "expected ']' after list elements"); return NULL; } @@ -644,12 +644,12 @@ static SnukExpr *parse_type(SnukParser *parser, SnukStringView name) { SnukItem *item = snuk_item_parse(parser); snuk_darray_push(&members, item); } else { - parser_error(parser, "unexpected token"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "unexpected token"); } } if (parser->previous.type != SNUK_TOKEN_RBRACE) { - parser_error(parser, "expected '}'"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "expected '}'"); return NULL; } @@ -677,7 +677,7 @@ static SnukExpr *parse_type_inst(SnukParser *parser, SnukType *type) { } if (parser->previous.type != SNUK_TOKEN_RBRACE) { - parser_error(parser, "expected '}'"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "expected '}'"); return NULL; } @@ -706,7 +706,7 @@ static SnukExpr *parse_block(SnukParser *parser) { block_expr = build_block_expr(parser, block_expr, snuk_item_parse(parser)); if (parser->previous.type != SNUK_TOKEN_RBRACE) { - parser_error(parser, "block was not closed"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "block was not closed"); return NULL; } diff --git a/src/parser/snuk_item.c b/src/parser/snuk_item.c index e4c748e..5d6e73f 100644 --- a/src/parser/snuk_item.c +++ b/src/parser/snuk_item.c @@ -129,12 +129,12 @@ static SnukItem *parse_extend_item(SnukParser *parser) { SnukItem *item = snuk_item_parse(parser); extend_item = build_extend_item(parser, extend_item, NULL, item); } else { - parser_error(parser, "unexpected token"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "unexpected token"); } } if (parser->previous.type != SNUK_TOKEN_RBRACE) { - parser_error(parser, "expected '}'"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "expected '}'"); return NULL; } diff --git a/src/parser/snuk_type.c b/src/parser/snuk_type.c index 374c764..1039714 100644 --- a/src/parser/snuk_type.c +++ b/src/parser/snuk_type.c @@ -16,18 +16,19 @@ SnukType *snuk_type_parse_interface(SnukParser *parser) { while (!parser_match(parser, SNUK_TOKEN_RBRACE) && parser->current.type != SNUK_TOKEN_EOF) { if (!parser_match(parser, SNUK_TOKEN_VAR) && !parser_match(parser, SNUK_TOKEN_CONST)) { - parser_error(parser, "expected var or const"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "expected var or const"); return NULL; } SnukVar *var = snuk_var_parse(parser, false); parser_expect_item_end(parser); - if (var->value) parser_error(parser, "interface members should not have values"); + if (var->value) + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "interface members should not have values"); type = build_interface_type(parser, type, var); } if (parser->previous.type != SNUK_TOKEN_RBRACE) { - parser_error(parser, "expected '}'"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "expected '}'"); return NULL; } @@ -46,7 +47,7 @@ SnukType *snuk_type_parse(SnukParser *parser) { } if (parser->previous.type != SNUK_TOKEN_RPAREN) { - parser_error(parser, "expected ')'"); + parser_error(parser, SNUK_PARSE_ERR_UNEXPECTED_TOKEN, "expected ')'"); return NULL; }