From 5bd533d40c825e33315c98a06d24263cb2afe980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Wed, 11 Feb 2026 13:20:10 +0000 Subject: [PATCH] parser: replace fprintf+longjmp with fail(), add forward declarations Introduce a fail(p, "msg") inline function that stores the error message in a buffer and longjmps, replacing ~52 fprintf(stderr,...)+longjmp pairs. The error message is propagated through Ast.err_msg so callers can decide whether/how to display it. Also add forward declarations for all static functions and move PtrModifiers typedef to the type definitions section. Co-Authored-By: Claude Opus 4.6 --- ast.c | 18 +++- ast.h | 1 + parser.c | 286 ++++++++++++++++++++++++++----------------------------- parser.h | 13 +++ 4 files changed, 164 insertions(+), 154 deletions(-) diff --git a/ast.c b/ast.c index 9711976c22..e9a1d04eb3 100644 --- a/ast.c +++ b/ast.c @@ -2,8 +2,8 @@ #include #include -#include #include +#include #include "ast.h" #include "parser.h" @@ -47,6 +47,9 @@ Ast astParse(const char* source, const uint32_t len) { uint32_t estimated_node_count = (tokens.len + 2) / 2; + char err_buf[PARSE_ERR_BUF_SIZE]; + err_buf[0] = '\0'; + Parser p = { .source = source, .source_len = len, @@ -63,6 +66,7 @@ Ast astParse(const char* source, const uint32_t len) { }, .extra_data = SLICE_INIT(AstNodeIndex, N), .scratch = SLICE_INIT(AstNodeIndex, N), + .err_buf = err_buf, }; bool has_error = false; @@ -75,6 +79,15 @@ Ast astParse(const char* source, const uint32_t len) { p.scratch.cap = p.scratch.len = 0; free(p.scratch.arr); + char* err_msg = NULL; + if (has_error && err_buf[0] != '\0') { + const size_t len2 = strlen(err_buf); + err_msg = malloc(len2 + 1); + if (!err_msg) + exit(1); + memcpy(err_msg, err_buf, len2 + 1); + } + return (Ast) { .source = source, .source_len = len, @@ -86,10 +99,13 @@ Ast astParse(const char* source, const uint32_t len) { .arr = p.extra_data.arr, }, .has_error = has_error, + .err_msg = err_msg, }; } void astDeinit(Ast* tree) { + free(tree->err_msg); + tree->tokens.cap = tree->tokens.len = 0; free(tree->tokens.tags); free(tree->tokens.starts); diff --git a/ast.h b/ast.h index 290ef3bbc0..aa444c01c4 100644 --- a/ast.h +++ b/ast.h @@ -541,6 +541,7 @@ typedef struct { AstNodeList nodes; AstNodeIndexSlice extra_data; bool has_error; + char* err_msg; } Ast; typedef struct AstPtrType { diff --git a/parser.c b/parser.c index a14e0663c2..8e255e2aab 100644 --- a/parser.c +++ b/parser.c @@ -2,8 +2,6 @@ #include #include -#include -#include #include #include @@ -23,29 +21,6 @@ typedef struct { bool trailing; } Members; -static AstNodeIndex parsePrefixExpr(Parser*); -static AstNodeIndex parseTypeExpr(Parser*); -static AstNodeIndex parseBlock(Parser* p); -static AstNodeIndex parseLabeledStatement(Parser*); -static AstNodeIndex parseExpr(Parser*); -static AstNodeIndex expectExpr(Parser*); -static AstNodeIndex expectSemicolon(Parser*); -static AstTokenIndex expectToken(Parser*, TokenizerTag); -static AstNodeIndex parseFnProto(Parser*); -static Members parseContainerMembers(Parser*); -static AstNodeIndex parseInitList(Parser*, AstNodeIndex, AstTokenIndex); -static AstNodeIndex expectBlockExprStatement(Parser*); -static AstNodeIndex parseWhileExpr(Parser*); -static AstNodeIndex parseAssignExpr(Parser*); -static void parsePtrPayload(Parser*); -static void parsePayload(Parser*); -static AstNodeIndex parseSwitchExpr(Parser*); -static AstNodeIndex parseForExpr(Parser*); -static AstNodeIndex parseAsmExpr(Parser*); -static AstNodeIndex parseIfExpr(Parser*); -static uint32_t forPrefix(Parser*); -static AstNodeIndex parseLabeledStatement(Parser*); - typedef struct { enum { FIELD_STATE_NONE, FIELD_STATE_SEEN, FIELD_STATE_END } tag; union { @@ -66,6 +41,87 @@ typedef struct { uint32_t old_len; } CleanupScratch; +typedef struct { + AstNodeIndex align_node; + AstNodeIndex addrspace_node; + AstNodeIndex bit_range_start; + AstNodeIndex bit_range_end; +} PtrModifiers; + +static CleanupScratch initCleanupScratch(Parser*); +static void cleanupScratch(CleanupScratch*); +static AstSubRange listToSpan(Parser*, const AstNodeIndex*, uint32_t); +static AstSubRange membersToSpan(const Members, Parser*); +static AstTokenIndex nextToken(Parser*); +static AstTokenIndex eatToken(Parser*, TokenizerTag); +static AstTokenIndex assertToken(Parser*, TokenizerTag); +static bool tokensOnSameLine(Parser*, AstTokenIndex, AstTokenIndex); +static AstTokenIndex eatDocComments(Parser*); +static AstNodeIndex setNode(Parser*, uint32_t, AstNodeItem); +static void astNodeListEnsureCapacity(AstNodeList*, uint32_t); +static AstNodeIndex addNode(AstNodeList*, AstNodeItem); +static AstNodeIndex addExtra(Parser*, const AstNodeIndex*, uint32_t); +static AstNodeIndex parseByteAlign(Parser*); +static AstNodeIndex parseAddrSpace(Parser*); +static AstNodeIndex parseLinkSection(Parser*); +static AstNodeIndex parseCallconv(Parser*); +static AstNodeIndex expectContainerField(Parser*); +static AstNodeIndex parseBuiltinCall(Parser*); +static AstNodeIndex parseContainerDeclAuto(Parser*); +static AstNodeIndex parsePrimaryTypeExpr(Parser*); +static AstNodeIndex parseSuffixOp(Parser*, AstNodeIndex); +static AstNodeIndex parseSuffixExpr(Parser*); +static AstTokenIndex expectToken(Parser*, TokenizerTag); +static AstNodeIndex expectSemicolon(Parser*); +static AstNodeIndex parseErrorUnionExpr(Parser*); +static PtrModifiers parsePtrModifiers(Parser*); +static AstNodeIndex makePtrTypeNode( + Parser*, AstTokenIndex, AstNodeIndex, PtrModifiers, AstNodeIndex); +static AstNodeIndex parseTypeExpr(Parser*); +static uint32_t reserveNode(Parser*, AstNodeTag); +static AstNodeIndex parseFnProto(Parser*); +static AstTokenIndex parseBlockLabel(Parser*); +static uint32_t forPrefix(Parser*); +static AstNodeIndex parseForExpr(Parser*); +static AstNodeIndex parseForStatement(Parser*); +static AstNodeIndex parseWhileContinueExpr(Parser*); +static AstNodeIndex parseWhileExpr(Parser*); +static AstNodeIndex parseWhileStatement(Parser*); +static AstNodeIndex parseLoopStatement(Parser*); +static AstNodeIndex parseVarDeclProto(Parser*); +static AstTokenIndex parseBreakLabel(Parser*); +static AstNodeIndex parseFieldInit(Parser*); +static AstNodeIndex parseInitList(Parser*, AstNodeIndex, AstTokenIndex); +static AstNodeIndex parseCurlySuffixExpr(Parser*); +static uint32_t tokenTagLexemeLen(TokenizerTag); +static AstNodeIndex parseExprPrecedence(Parser*, int32_t); +static AstNodeIndex parseExpr(Parser*); +static AstNodeIndex expectExpr(Parser*); +static AstNodeIndex parseAsmOutputItem(Parser*); +static AstNodeIndex parseAsmInputItem(Parser*); +static AstNodeIndex parseAsmExpr(Parser*); +static AstNodeIndex parseSwitchItem(Parser*); +static AstNodeIndex parseSwitchProng(Parser*); +static AstNodeIndex parseSwitchExpr(Parser*); +static void parsePtrPayload(Parser*); +static void parsePayload(Parser*); +static AstNodeIndex parseIfExpr(Parser*); +static AstNodeIndex parsePrimaryExpr(Parser*); +static AstNodeIndex parsePrefixExpr(Parser*); +static AstNodeTag assignOpTag(TokenizerTag); +static AstNodeIndex finishAssignExpr(Parser*, AstNodeIndex); +static AstNodeIndex parseAssignExpr(Parser*); +static AstNodeIndex parseBlockExpr(Parser*); +static AstNodeIndex expectBlockExprStatement(Parser*); +static AstNodeIndex expectVarDeclExprStatement(Parser*, AstTokenIndex); +static AstNodeIndex expectStatement(Parser*, bool); +static AstNodeIndex parseBlock(Parser*); +static AstNodeIndex parseLabeledStatement(Parser*); +static AstNodeIndex parseGlobalVarDecl(Parser*); +static AstNodeIndex expectTopLevelDecl(Parser*); +static void findNextContainerMember(Parser*); +static Members parseContainerMembers(Parser*); + static CleanupScratch initCleanupScratch(Parser* p) { return (CleanupScratch) { .scratch = &p->scratch, @@ -109,10 +165,7 @@ static AstTokenIndex eatToken(Parser* p, TokenizerTag tag) { static AstTokenIndex assertToken(Parser* p, TokenizerTag tag) { const AstTokenIndex token = nextToken(p); if (p->token_tags[token] != tag) { - fprintf(stderr, "assertToken: expected %s, got %s\n", - tokenizerGetTagString(tag), - tokenizerGetTagString(p->token_tags[token])); - longjmp(p->error_jmp, 1); + fail(p, "unexpected token"); } return token; } @@ -134,8 +187,7 @@ static AstTokenIndex eatDocComments(Parser* p) { while ((tok = eatToken(p, TOKEN_DOC_COMMENT)) != null_token) { if (first == null_token) { if (tok > 0 && tokensOnSameLine(p, tok - 1, tok)) { - fprintf(stderr, "same_line_doc_comment\n"); - longjmp(p->error_jmp, 1); + fail(p, "same_line_doc_comment"); } first = tok; } @@ -233,8 +285,7 @@ static AstNodeIndex expectContainerField(Parser* p) { const AstNodeIndex type_expr = parseTypeExpr(p); if (type_expr == 0) { - fprintf(stderr, "expected type expression\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected type expression"); } const AstNodeIndex align_expr = parseByteAlign(p); const AstNodeIndex value_expr @@ -297,8 +348,7 @@ static AstNodeIndex parseBuiltinCall(Parser* p) { p->tok_i++; goto end_loop; default: - fprintf(stderr, "expected comma after arg\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected comma after arg"); } } end_loop:; @@ -428,8 +478,7 @@ static AstNodeIndex parseContainerDeclAuto(Parser* p) { } break; default: - fprintf(stderr, "parseContainerDeclAuto: unexpected token\n"); - longjmp(p->error_jmp, 1); + fail(p, "parseContainerDeclAuto: unexpected token"); } expectToken(p, TOKEN_L_BRACE); @@ -498,9 +547,7 @@ static AstNodeIndex parsePrimaryTypeExpr(Parser* p) { .data = {}, }); case TOKEN_KEYWORD_ANYFRAME: - fprintf(stderr, "parsePrimaryTypeExpr does not support %s\n", - tokenizerGetTagString(tok)); - longjmp(p->error_jmp, 1); + fail(p, "unsupported primary type expression"); case TOKEN_STRING_LITERAL: return addNode(&p->nodes, (AstNodeItem) { @@ -526,9 +573,7 @@ static AstNodeIndex parsePrimaryTypeExpr(Parser* p) { p->tok_i++; // consume extern/packed return parseContainerDeclAuto(p); default: - fprintf(stderr, "parsePrimaryTypeExpr does not support %s\n", - tokenizerGetTagString(tok)); - longjmp(p->error_jmp, 1); + fail(p, "unsupported primary type expression"); } case TOKEN_KEYWORD_STRUCT: case TOKEN_KEYWORD_OPAQUE: @@ -600,10 +645,7 @@ static AstNodeIndex parsePrimaryTypeExpr(Parser* p) { return parseInitList(p, null_node, lbrace); } default: - fprintf(stderr, - "parsePrimaryTypeExpr: unsupported period suffix %s\n", - tokenizerGetTagString(p->token_tags[p->tok_i + 1])); - longjmp(p->error_jmp, 1); + fail(p, "unsupported period suffix"); } return 0; // tcc case TOKEN_KEYWORD_ERROR: @@ -636,13 +678,11 @@ static AstNodeIndex parsePrimaryTypeExpr(Parser* p) { const AstTokenIndex main_token = nextToken(p); const AstTokenIndex period = eatToken(p, TOKEN_PERIOD); if (period == null_token) { - fprintf(stderr, "expected '.'\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected '.'"); } const AstTokenIndex identifier = eatToken(p, TOKEN_IDENTIFIER); if (identifier == null_token) { - fprintf(stderr, "expected identifier\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected identifier"); } return addNode(&p->nodes, (AstNodeItem) { @@ -727,9 +767,7 @@ static AstNodeIndex parseSuffixOp(Parser* p, AstNodeIndex lhs) { }); } default: - fprintf( - stderr, "parseSuffixOp: expected ] or .. after index expr\n"); - longjmp(p->error_jmp, 1); + fail(p, "parseSuffixOp: expected ] or .. after index expr"); } return 0; // tcc } @@ -741,9 +779,7 @@ static AstNodeIndex parseSuffixOp(Parser* p, AstNodeIndex lhs) { .data = { .lhs = lhs, .rhs = 0 }, }); case TOKEN_INVALID_PERIODASTERISKS: - fprintf(stderr, "parseSuffixOp does not support %s\n", - tokenizerGetTagString(tok)); - longjmp(p->error_jmp, 1); + fail(p, "unsupported suffix op"); case TOKEN_PERIOD: if (p->token_tags[p->tok_i + 1] == TOKEN_IDENTIFIER) { const AstTokenIndex dot = nextToken(p); @@ -773,8 +809,7 @@ static AstNodeIndex parseSuffixOp(Parser* p, AstNodeIndex lhs) { .data = { .lhs = lhs, .rhs = nextToken(p) }, }); } - fprintf(stderr, "parseSuffixOp: unsupported period suffix\n"); - longjmp(p->error_jmp, 1); + fail(p, "parseSuffixOp: unsupported period suffix"); return 0; // tcc default: return null_node; @@ -783,8 +818,7 @@ static AstNodeIndex parseSuffixOp(Parser* p, AstNodeIndex lhs) { static AstNodeIndex parseSuffixExpr(Parser* p) { if (eatToken(p, TOKEN_KEYWORD_ASYNC) != null_token) { - fprintf(stderr, "async not supported\n"); - longjmp(p->error_jmp, 1); + fail(p, "async not supported"); } AstNodeIndex res = parsePrimaryTypeExpr(p); @@ -868,10 +902,7 @@ static AstTokenIndex expectToken(Parser* p, TokenizerTag tag) { if (p->token_tags[p->tok_i] == tag) { return nextToken(p); } else { - fprintf(stderr, "expected token %s, got %s\n", - tokenizerGetTagString(tag), - tokenizerGetTagString(p->token_tags[p->tok_i])); - longjmp(p->error_jmp, 1); + fail(p, "unexpected token"); } return 0; // tcc } @@ -901,13 +932,6 @@ static AstNodeIndex parseErrorUnionExpr(Parser* p) { }); } -typedef struct { - AstNodeIndex align_node; - AstNodeIndex addrspace_node; - AstNodeIndex bit_range_start; - AstNodeIndex bit_range_end; -} PtrModifiers; - static PtrModifiers parsePtrModifiers(Parser* p) { PtrModifiers mods = {}; @@ -1000,9 +1024,7 @@ static AstNodeIndex parseTypeExpr(Parser* p) { .data = { .lhs = parseTypeExpr(p), .rhs = 0 }, }); case TOKEN_KEYWORD_ANYFRAME: - fprintf(stderr, "parseTypeExpr not supported for %s\n", - tokenizerGetTagString(tok)); - longjmp(p->error_jmp, 1); + fail(p, "unsupported type expression"); case TOKEN_ASTERISK: { const AstTokenIndex asterisk = nextToken(p); const PtrModifiers mods = parsePtrModifiers(p); @@ -1014,8 +1036,7 @@ static AstNodeIndex parseTypeExpr(Parser* p) { const PtrModifiers mods = parsePtrModifiers(p); const AstNodeIndex elem_type = parseTypeExpr(p); if (elem_type == 0) { - fprintf(stderr, "expected type expression\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected type expression"); } const AstNodeIndex inner = makePtrTypeNode(p, asterisk, 0, mods, elem_type); @@ -1058,8 +1079,7 @@ static AstNodeIndex parseTypeExpr(Parser* p) { const PtrModifiers mods = parsePtrModifiers(p); const AstNodeIndex elem_type = parseTypeExpr(p); if (mods.bit_range_start != 0) { - fprintf(stderr, "invalid_bit_range\n"); - longjmp(p->error_jmp, 1); + fail(p, "invalid_bit_range"); } return makePtrTypeNode(p, lbracket, sentinel, mods, elem_type); } @@ -1259,8 +1279,7 @@ static SmallSpan parseParamDeclList(Parser* p) { } if (varargs == 2) { - fprintf(stderr, "varargs_nonfinal\n"); - longjmp(p->error_jmp, 1); + fail(p, "varargs_nonfinal"); } const uint32_t params_len = p->scratch.len - scratch_top.old_len; @@ -1420,8 +1439,7 @@ static uint32_t forPrefix(Parser* p) { // Parse payload |a, *b, c| if (eatToken(p, TOKEN_PIPE) == null_token) { - fprintf(stderr, "expected loop payload\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected loop payload"); } { while (true) { @@ -1514,8 +1532,7 @@ static AstNodeIndex parseForStatement(Parser* p) { } else { then_body = parseAssignExpr(p); if (then_body == 0) { - fprintf(stderr, "expected expression\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected expression"); } if (eatToken(p, TOKEN_SEMICOLON) != null_token) seen_semicolon = true; @@ -1542,8 +1559,7 @@ static AstNodeIndex parseForStatement(Parser* p) { } if (!seen_semicolon && block == 0) { - fprintf(stderr, "expected_semi_or_else\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected_semi_or_else"); } if (inputs == 1) { @@ -1653,8 +1669,7 @@ static AstNodeIndex parseWhileStatement(Parser* p) { } else { body = parseAssignExpr(p); if (body == 0) { - fprintf(stderr, "expected expression\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected expression"); } if (eatToken(p, TOKEN_SEMICOLON) != null_token) seen_semicolon = true; @@ -1662,8 +1677,7 @@ static AstNodeIndex parseWhileStatement(Parser* p) { if (seen_semicolon || eatToken(p, TOKEN_KEYWORD_ELSE) == null_token) { if (!seen_semicolon && block == 0) { - fprintf(stderr, "expected_semi_or_else\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected_semi_or_else"); } if (cont_expr != 0) { return addNode(&p->nodes, @@ -1714,9 +1728,7 @@ static AstNodeIndex parseLoopStatement(Parser* p) { if (inline_token == null_token) return null_node; - fprintf( - stderr, "seen 'inline', there should have been a 'for' or 'while'\n"); - longjmp(p->error_jmp, 1); + fail(p, "seen 'inline', there should have been a 'for' or 'while'"); return 0; // tcc } @@ -1812,9 +1824,7 @@ static AstNodeIndex parseInitList( p->tok_i++; break; } else { - fprintf( - stderr, "parseInitList: expected , or } in struct init\n"); - longjmp(p->error_jmp, 1); + fail(p, "parseInitList: expected , or } in struct init"); } if (eatToken(p, TOKEN_R_BRACE) != null_token) break; @@ -1902,8 +1912,7 @@ static AstNodeIndex parseInitList( p->tok_i++; break; } else { - fprintf(stderr, "parseInitList: expected , or } in array init\n"); - longjmp(p->error_jmp, 1); + fail(p, "parseInitList: expected , or } in array init"); } } @@ -2145,8 +2154,7 @@ static AstNodeIndex parseExprPrecedence(Parser* p, int32_t min_prec) { break; if (info.prec == banned_prec) { - fprintf(stderr, "chained comparison operators\n"); - longjmp(p->error_jmp, 1); + fail(p, "chained comparison operators"); } const AstTokenIndex oper_token = nextToken(p); @@ -2154,8 +2162,7 @@ static AstNodeIndex parseExprPrecedence(Parser* p, int32_t min_prec) { parsePayload(p); const AstNodeIndex rhs = parseExprPrecedence(p, info.prec + 1); if (rhs == 0) { - fprintf(stderr, "expected expression\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected expression"); } { @@ -2165,12 +2172,10 @@ static AstNodeIndex parseExprPrecedence(Parser* p, int32_t min_prec) { const char char_before = p->source[tok_start - 1]; const char char_after = p->source[tok_start + tok_len]; if (tok_tag == TOKEN_AMPERSAND && char_after == '&') { - fprintf(stderr, "invalid ampersand ampersand\n"); - longjmp(p->error_jmp, 1); + fail(p, "invalid ampersand ampersand"); } else if (isspace((unsigned char)char_before) != isspace((unsigned char)char_after)) { - fprintf(stderr, "mismatched binary op whitespace\n"); - longjmp(p->error_jmp, 1); + fail(p, "mismatched binary op whitespace"); } } } @@ -2198,8 +2203,7 @@ static AstNodeIndex parseExpr(Parser* p) { return parseExprPrecedence(p, 0); } static AstNodeIndex expectExpr(Parser* p) { const AstNodeIndex node = parseExpr(p); if (node == 0) { - fprintf(stderr, "expected expression\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected expression"); } return node; } @@ -2399,8 +2403,7 @@ static AstNodeIndex parseSwitchProng(Parser* p) { parsePtrPayload(p); const AstNodeIndex case_body = parseAssignExpr(p); if (case_body == 0) { - fprintf(stderr, "expected expression\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected expression"); } const uint32_t items_len = p->scratch.len - items_old_len; @@ -2602,8 +2605,7 @@ static AstNodeIndex parsePrimaryExpr(Parser* p) { return parseForExpr(p); if (p->token_tags[p->tok_i] == TOKEN_KEYWORD_WHILE) return parseWhileExpr(p); - fprintf(stderr, "expected for or while after inline\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected for or while after inline"); return 0; // tcc case TOKEN_KEYWORD_FOR: p->tok_i += 2; @@ -2630,8 +2632,7 @@ static AstNodeIndex parsePrimaryExpr(Parser* p) { return parseForExpr(p); if (p->token_tags[p->tok_i] == TOKEN_KEYWORD_WHILE) return parseWhileExpr(p); - fprintf(stderr, "parsePrimaryExpr: inline without for/while\n"); - longjmp(p->error_jmp, 1); + fail(p, "parsePrimaryExpr: inline without for/while"); return 0; // tcc case TOKEN_L_BRACE: return parseBlock(p); @@ -2768,8 +2769,7 @@ static AstNodeIndex expectBlockExprStatement(Parser* p) { expectSemicolon(p); return expr; } - fprintf(stderr, "expectBlockExprStatement: expected block or expr\n"); - longjmp(p->error_jmp, 1); + fail(p, "expectBlockExprStatement: expected block or expr"); return 0; // tcc } @@ -2799,8 +2799,7 @@ static AstNodeIndex expectVarDeclExprStatement( if (equal_token == null_token) { if (lhs_count > 1) { // Destructure requires '=' - fprintf(stderr, "expected '='\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected '='"); } const AstNodeIndex lhs = p->scratch.arr[scratch_top.old_len]; const AstNodeTag lhs_tag = p->nodes.tags[lhs]; @@ -2809,8 +2808,7 @@ static AstNodeIndex expectVarDeclExprStatement( || lhs_tag == AST_NODE_LOCAL_VAR_DECL || lhs_tag == AST_NODE_GLOBAL_VAR_DECL) { // var decl without init requires '=' - fprintf(stderr, "expected '='\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected '='"); } // Expression statement: finish with assignment operators or semicolon const AstNodeIndex expr = finishAssignExpr(p, lhs); @@ -2890,8 +2888,7 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) { { const AstNodeIndex assign = parseAssignExpr(p); if (assign == 0) { - fprintf(stderr, "expected expression\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected expression"); } expectSemicolon(p); return addNode(&p->nodes, @@ -2967,8 +2964,7 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) { } else { then_body = parseAssignExpr(p); if (then_body == 0) { - fprintf(stderr, "expected block or assignment\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected block or assignment"); } if (eatToken(p, TOKEN_SEMICOLON) != null_token) return addNode(&p->nodes, @@ -2981,8 +2977,7 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) { } if (eatToken(p, TOKEN_KEYWORD_ELSE) == null_token) { if (else_required) { - fprintf(stderr, "expected_semi_or_else\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected_semi_or_else"); } return addNode(&p->nodes, (AstNodeItem) { @@ -3007,10 +3002,7 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) { case TOKEN_KEYWORD_ENUM: case TOKEN_KEYWORD_STRUCT: case TOKEN_KEYWORD_UNION:; - const char* tok_str = tokenizerGetTagString(tok); - fprintf( - stderr, "expectStatement does not support keyword %s\n", tok_str); - longjmp(p->error_jmp, 1); + fail(p, "unsupported statement keyword"); default:; } @@ -3113,8 +3105,7 @@ static AstNodeIndex parseLabeledStatement(Parser* p) { return loop_stmt; if (label_token != 0) { - fprintf(stderr, "parseLabeledStatement does not support labels\n"); - longjmp(p->error_jmp, 1); + fail(p, "parseLabeledStatement does not support labels"); } return null_node; @@ -3165,7 +3156,7 @@ static AstNodeIndex expectTopLevelDecl(Parser* p) { .data = { .lhs = fn_proto, .rhs = body_block }, }); default: - longjmp(p->error_jmp, 1); // Expected semicolon or left brace + fail(p, "expected semicolon or lbrace"); } } @@ -3176,9 +3167,7 @@ static AstNodeIndex expectTopLevelDecl(Parser* p) { } // assuming the program is correct... - fprintf(stderr, - "the next token should be usingnamespace, which is not supported\n"); - longjmp(p->error_jmp, 1); + fail(p, "the next token should be usingnamespace, which is not supported"); return 0; // make tcc happy } @@ -3260,8 +3249,7 @@ static Members parseContainerMembers(Parser* p) { switch (p->token_tags[p->tok_i]) { case TOKEN_KEYWORD_TEST: { if (doc_comment != null_token) { - fprintf(stderr, "test_doc_comment\n"); - longjmp(p->error_jmp, 1); + fail(p, "test_doc_comment"); } const AstTokenIndex test_token = nextToken(p); // test name can be a string literal or identifier, or omitted @@ -3272,8 +3260,7 @@ static Members parseContainerMembers(Parser* p) { : null_token; const AstNodeIndex body = parseBlock(p); if (body == 0) { - fprintf(stderr, "expected block after test\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected block after test"); } const AstNodeIndex test_decl = addNode(&p->nodes, (AstNodeItem) { @@ -3286,18 +3273,14 @@ static Members parseContainerMembers(Parser* p) { break; } case TOKEN_KEYWORD_USINGNAMESPACE:; - const char* str = tokenizerGetTagString(p->token_tags[p->tok_i]); - fprintf( - stderr, "%s not implemented in parseContainerMembers\n", str); - longjmp(p->error_jmp, 1); + fail(p, "not implemented in parseContainerMembers"); case TOKEN_KEYWORD_COMPTIME: // comptime can be a container field modifier or a comptime // block/decl. Check if it's followed by a block (comptime { ... // }). if (p->token_tags[p->tok_i + 1] == TOKEN_L_BRACE) { if (doc_comment != null_token) { - fprintf(stderr, "comptime_doc_comment\n"); - longjmp(p->error_jmp, 1); + fail(p, "comptime_doc_comment"); } const AstTokenIndex comptime_token = nextToken(p); const AstNodeIndex block_node = parseBlock(p); @@ -3359,8 +3342,7 @@ static Members parseContainerMembers(Parser* p) { case FIELD_STATE_SEEN: break; case FIELD_STATE_END: - fprintf(stderr, "parseContainerMembers error condition\n"); - longjmp(p->error_jmp, 1); + fail(p, "parseContainerMembers error condition"); } SLICE_APPEND(AstNodeIndex, &p->scratch, field_node); switch (p->token_tags[p->tok_i]) { @@ -3373,8 +3355,7 @@ static Members parseContainerMembers(Parser* p) { trailing = false; goto break_loop; default: - fprintf(stderr, "expected comma after field\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected comma after field"); } } } @@ -3424,8 +3405,7 @@ void parseRoot(Parser* p) { AstSubRange root_decls = membersToSpan(root_members, p); if (p->token_tags[p->tok_i] != TOKEN_EOF) { - fprintf(stderr, "expected EOF\n"); - longjmp(p->error_jmp, 1); + fail(p, "expected EOF"); } p->nodes.datas[0].lhs = root_decls.start; diff --git a/parser.h b/parser.h index 06ac581b25..448194d8c9 100644 --- a/parser.h +++ b/parser.h @@ -7,6 +7,7 @@ #include #include #include +#include typedef struct { const char* source; @@ -22,8 +23,20 @@ typedef struct { AstNodeIndexSlice extra_data; AstNodeIndexSlice scratch; jmp_buf error_jmp; + char* err_buf; } Parser; +#define PARSE_ERR_BUF_SIZE 200 + +_Noreturn static inline void fail(Parser* p, const char* msg) { + size_t len = strlen(msg); + if (len >= PARSE_ERR_BUF_SIZE) + len = PARSE_ERR_BUF_SIZE - 1; + memcpy(p->err_buf, msg, len); + p->err_buf[len] = '\0'; + longjmp(p->error_jmp, 1); +} + Parser* parserInit(const char* source, uint32_t len); void parserDeinit(Parser* parser); void parseRoot(Parser* parser);