From 3ceb85bd93fa87a5be3682ab8995abea82a63ea3 Mon Sep 17 00:00:00 2001 From: Carlos Maniero Date: Thu, 20 Apr 2023 13:32:36 -0300 Subject: parser: Stop exiting on parser error Previously, when an error occurred during parsing, the application would exit, making it difficult to test the parser and limiting the compiler's extensibility. This commit improves the parser's error handling by allowing for continued execution after an error, enabling easier testing and increased flexibility. The parser is prepared to handle multiples errors, although the current implementation always returns a single error, it may be useful given multiples functions where we can show errors by context. Signed-off-by: Carlos Maniero Reviwed-by: Johnny Richard --- src/parser.c | 138 +++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 97 insertions(+), 41 deletions(-) (limited to 'src/parser.c') diff --git a/src/parser.c b/src/parser.c index b1e6a1f..4646de0 100644 --- a/src/parser.c +++ b/src/parser.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -30,83 +31,136 @@ parser_init(parser_t *parser, lexer_t *lexer) assert(parser && "parser must be defined"); assert(lexer && "lexer must be defined"); parser->lexer = lexer; + parser->errors_len = 0; } -static token_t -expected_token(parser_t *parser, token_kind_t kind) +static bool +expected_token(token_t *token, parser_t *parser, token_kind_t kind) { - token_t token; - lexer_next_token(parser->lexer, &token); - - if (token.kind == TOKEN_EOF) { - fprintf( - stderr, - "%s:%d:%d: [ERROR]: expected '%s' but got end of file\n", - token.filepath, token.row + 1, token.col + 1, + lexer_next_token(parser->lexer, token); + parser_error_t error; + + if (token->kind == TOKEN_EOF) { + error.token = *token; + sprintf( + error.message, + "expected '%s' but got end of file", token_kind_to_str(kind) ); - exit(EXIT_FAILURE); + + parser->errors[parser->errors_len++] = error; + return false; } - if (token.kind != kind) { - fprintf( - stderr, - "%s:%d:%d: [ERROR]: expected '%s' but got '%s'\n", - token.filepath, token.row + 1, token.col + 1, - token_kind_to_str(kind), token_kind_to_str(token.kind) + if (token->kind != kind) { + error.token = *token; + sprintf( + error.message, + "expected '%s' but got '%s'", + token_kind_to_str(kind), token_kind_to_str(token->kind) ); - exit(EXIT_FAILURE); + parser->errors[parser->errors_len++] = error; + return false; } - return token; + return true; +} + +static bool +drop_expected_token(parser_t *parser, token_kind_t kind) +{ + token_t ignored_token; + return expected_token(&ignored_token, parser, kind); } -static type_t -parser_parse_type(parser_t *parser) +static bool +parser_parse_type(type_t *type, parser_t *parser) { - token_t token = expected_token(parser, TOKEN_NAME); + token_t token; + + if(!expected_token(&token, parser, TOKEN_NAME)) return false; if (string_view_eq(token.value, string_view_from_str("i32"))) { - return TYPE_I32; + *type = TYPE_I32; + return true; } - fprintf(stderr, "[ERROR]: expected type 'i32' but got '"SVFMT"'\n", SVARG(&token.value)); - exit(EXIT_FAILURE); + parser_error_t error; + error.token = token; + + sprintf( + error.message, + "type '"SVFMT"' is not defined", + SVARG(&token.value) + ); + + parser->errors[parser->errors_len++] = error; + return false; } -void +bool parser_parse_return_stmt(parser_t *parser, ast_node_t *node) { - expected_token(parser, TOKEN_OCURLY); - token_t return_keyword_token = expected_token(parser, TOKEN_NAME); + token_t return_keyword_token; + + if (!drop_expected_token(parser, TOKEN_OCURLY)) return false; + + if(!expected_token(&return_keyword_token, parser, TOKEN_NAME)) return false; if (!string_view_eq(return_keyword_token.value, string_view_from_str("return"))) { - // TODO: Add filename:row:col prefix to expected token exceptions - fprintf(stderr, "[ERROR]: expected 'return' keyword but got '"SVFMT"'\n", SVARG(&return_keyword_token.value)); - exit(EXIT_FAILURE); + parser_error_t error; + error.token = return_keyword_token; + + sprintf( + error.message, + "expected 'return' keyword but got '"SVFMT"'", + SVARG(&return_keyword_token.value) + ); + + parser->errors[parser->errors_len++] = error; + return false; } - token_t number_token = expected_token(parser, TOKEN_NUMBER); - expected_token(parser, TOKEN_SEMICOLON); - expected_token(parser, TOKEN_CCURLY); + token_t number_token; + + if (!expected_token(&number_token, parser, TOKEN_NUMBER)) return false; + + if (!drop_expected_token(parser, TOKEN_SEMICOLON)) return false; + if (!drop_expected_token(parser, TOKEN_CCURLY)) return false; char number_as_str[number_token.value.size]; string_view_to_str(&number_token.value, number_as_str); ast_node_init_return_stmt(node, atoi(number_as_str)); + return true; } -void +bool parser_parse_function_declaration(parser_t *parser, ast_node_t *node) { - token_t func_name_token = expected_token(parser, TOKEN_NAME); - expected_token(parser, TOKEN_OPAREN); - expected_token(parser, TOKEN_CPAREN); - expected_token(parser, TOKEN_COLON); - type_t return_type = parser_parse_type(parser); + token_t func_name_token; + + if (!expected_token(&func_name_token, parser, TOKEN_NAME)) { + return false; + } + + if (!drop_expected_token(parser, TOKEN_OPAREN)) return false; + if (!drop_expected_token(parser, TOKEN_CPAREN)) return false; + if (!drop_expected_token(parser, TOKEN_COLON)) return false; + + type_t return_type; + + if(!parser_parse_type(&return_type, parser)) { + return false; + } ast_node_t *return_node = ast_node_new(); - parser_parse_return_stmt(parser, return_node); + + bool parsed_return = parser_parse_return_stmt(parser, return_node); + + if (!parsed_return) { + return false; + } ast_node_init_function_declaration( node, @@ -114,4 +168,6 @@ parser_parse_function_declaration(parser_t *parser, ast_node_t *node) return_type, return_node ); + + return true; } -- cgit v1.2.3