diff --git a/ast.c b/ast.c index 75afe85fa5..9711976c22 100644 --- a/ast.c +++ b/ast.c @@ -1,5 +1,6 @@ #include "common.h" +#include #include #include #include @@ -64,7 +65,12 @@ Ast astParse(const char* source, const uint32_t len) { .scratch = SLICE_INIT(AstNodeIndex, N), }; - parseRoot(&p); + bool has_error = false; + if (setjmp(p.error_jmp) != 0) { + has_error = true; + } + if (!has_error) + parseRoot(&p); p.scratch.cap = p.scratch.len = 0; free(p.scratch.arr); @@ -79,6 +85,7 @@ Ast astParse(const char* source, const uint32_t len) { .cap = p.extra_data.cap, .arr = p.extra_data.arr, }, + .has_error = has_error, }; } diff --git a/ast.h b/ast.h index e6a8527f8d..290ef3bbc0 100644 --- a/ast.h +++ b/ast.h @@ -540,6 +540,7 @@ typedef struct { AstTokenList tokens; AstNodeList nodes; AstNodeIndexSlice extra_data; + bool has_error; } Ast; typedef struct AstPtrType { diff --git a/parser.c b/parser.c index ac73af44f0..d86931ffc8 100644 --- a/parser.c +++ b/parser.c @@ -1,6 +1,7 @@ #include "common.h" #include +#include #include #include #include @@ -263,7 +264,7 @@ static AstNodeIndex parseBuiltinCall(Parser* p) { goto end_loop; default: fprintf(stderr, "expected comma after arg\n"); - exit(1); + longjmp(p->error_jmp, 1); } } end_loop:; @@ -394,7 +395,7 @@ static AstNodeIndex parseContainerDeclAuto(Parser* p) { break; default: fprintf(stderr, "parseContainerDeclAuto: unexpected token\n"); - exit(1); + longjmp(p->error_jmp, 1); } expectToken(p, TOKEN_L_BRACE); @@ -465,7 +466,7 @@ static AstNodeIndex parsePrimaryTypeExpr(Parser* p) { case TOKEN_KEYWORD_ANYFRAME: fprintf(stderr, "parsePrimaryTypeExpr does not support %s\n", tokenizerGetTagString(tok)); - exit(1); + longjmp(p->error_jmp, 1); case TOKEN_STRING_LITERAL: return addNode(&p->nodes, (AstNodeItem) { @@ -493,7 +494,7 @@ static AstNodeIndex parsePrimaryTypeExpr(Parser* p) { default: fprintf(stderr, "parsePrimaryTypeExpr does not support %s\n", tokenizerGetTagString(tok)); - exit(1); + longjmp(p->error_jmp, 1); } case TOKEN_KEYWORD_STRUCT: case TOKEN_KEYWORD_OPAQUE: @@ -552,7 +553,7 @@ static AstNodeIndex parsePrimaryTypeExpr(Parser* p) { fprintf(stderr, "parsePrimaryTypeExpr: unsupported period suffix %s\n", tokenizerGetTagString(p->token_tags[p->tok_i + 1])); - exit(1); + longjmp(p->error_jmp, 1); } return 0; // tcc case TOKEN_KEYWORD_ERROR: @@ -666,7 +667,7 @@ static AstNodeIndex parseSuffixOp(Parser* p, AstNodeIndex lhs) { default: fprintf( stderr, "parseSuffixOp: expected ] or .. after index expr\n"); - exit(1); + longjmp(p->error_jmp, 1); } return 0; // tcc } @@ -680,7 +681,7 @@ static AstNodeIndex parseSuffixOp(Parser* p, AstNodeIndex lhs) { case TOKEN_INVALID_PERIODASTERISKS: fprintf(stderr, "parseSuffixOp does not support %s\n", tokenizerGetTagString(tok)); - exit(1); + longjmp(p->error_jmp, 1); case TOKEN_PERIOD: if (p->token_tags[p->tok_i + 1] == TOKEN_IDENTIFIER) { const AstTokenIndex dot = nextToken(p); @@ -711,7 +712,7 @@ static AstNodeIndex parseSuffixOp(Parser* p, AstNodeIndex lhs) { }); } fprintf(stderr, "parseSuffixOp: unsupported period suffix\n"); - exit(1); + longjmp(p->error_jmp, 1); return 0; // tcc default: return null_node; @@ -721,7 +722,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"); - exit(1); + longjmp(p->error_jmp, 1); } AstNodeIndex res = parsePrimaryTypeExpr(p); @@ -808,7 +809,7 @@ static AstTokenIndex expectToken(Parser* p, TokenizerTag tag) { fprintf(stderr, "expected token %s, got %s\n", tokenizerGetTagString(tag), tokenizerGetTagString(p->token_tags[p->tok_i])); - exit(1); + longjmp(p->error_jmp, 1); } return 0; // tcc } @@ -939,7 +940,7 @@ static AstNodeIndex parseTypeExpr(Parser* p) { case TOKEN_KEYWORD_ANYFRAME: fprintf(stderr, "parseTypeExpr not supported for %s\n", tokenizerGetTagString(tok)); - exit(1); + longjmp(p->error_jmp, 1); case TOKEN_ASTERISK: { const AstTokenIndex asterisk = nextToken(p); const PtrModifiers mods = parsePtrModifiers(p); @@ -952,7 +953,7 @@ static AstNodeIndex parseTypeExpr(Parser* p) { const AstNodeIndex elem_type = parseTypeExpr(p); if (elem_type == 0) { fprintf(stderr, "expected type expression\n"); - exit(1); + longjmp(p->error_jmp, 1); } const AstNodeIndex inner = makePtrTypeNode(p, asterisk, 0, mods, elem_type); @@ -1322,7 +1323,7 @@ static AstNodeIndex parseForStatement(Parser* p) { then_body = parseAssignExpr(p); if (then_body == 0) { fprintf(stderr, "expected expression\n"); - exit(1); + longjmp(p->error_jmp, 1); } if (eatToken(p, TOKEN_SEMICOLON) != null_token) seen_semicolon = true; @@ -1456,7 +1457,7 @@ static AstNodeIndex parseWhileStatement(Parser* p) { body = parseAssignExpr(p); if (body == 0) { fprintf(stderr, "expected expression\n"); - exit(1); + longjmp(p->error_jmp, 1); } if (eatToken(p, TOKEN_SEMICOLON) != null_token) seen_semicolon = true; @@ -1514,7 +1515,7 @@ static AstNodeIndex parseLoopStatement(Parser* p) { fprintf( stderr, "seen 'inline', there should have been a 'for' or 'while'\n"); - exit(1); + longjmp(p->error_jmp, 1); return 0; // tcc } @@ -1612,7 +1613,7 @@ static AstNodeIndex parseInitList( } else { fprintf( stderr, "parseInitList: expected , or } in struct init\n"); - exit(1); + longjmp(p->error_jmp, 1); } if (eatToken(p, TOKEN_R_BRACE) != null_token) break; @@ -1701,7 +1702,7 @@ static AstNodeIndex parseInitList( break; } else { fprintf(stderr, "parseInitList: expected , or } in array init\n"); - exit(1); + longjmp(p->error_jmp, 1); } } @@ -1906,7 +1907,7 @@ static AstNodeIndex parseExprPrecedence(Parser* p, int32_t min_prec) { const AstNodeIndex rhs = parseExprPrecedence(p, info.prec + 1); if (rhs == 0) { fprintf(stderr, "expected expression\n"); - exit(1); + longjmp(p->error_jmp, 1); } node = addNode( @@ -1933,7 +1934,7 @@ static AstNodeIndex expectExpr(Parser* p) { const AstNodeIndex node = parseExpr(p); if (node == 0) { fprintf(stderr, "expected expression\n"); - exit(1); + longjmp(p->error_jmp, 1); } return node; } @@ -2134,7 +2135,7 @@ static AstNodeIndex parseSwitchProng(Parser* p) { const AstNodeIndex case_body = parseAssignExpr(p); if (case_body == 0) { fprintf(stderr, "expected expression\n"); - exit(1); + longjmp(p->error_jmp, 1); } const uint32_t items_len = p->scratch.len - items_old_len; @@ -2334,7 +2335,7 @@ static AstNodeIndex parsePrimaryExpr(Parser* p) { case TOKEN_KEYWORD_FOR: case TOKEN_KEYWORD_WHILE: fprintf(stderr, "parsePrimaryExpr NotImplemented\n"); - exit(1); + longjmp(p->error_jmp, 1); return 0; // tcc case TOKEN_L_BRACE: p->tok_i += 2; @@ -2356,7 +2357,7 @@ static AstNodeIndex parsePrimaryExpr(Parser* p) { if (p->token_tags[p->tok_i] == TOKEN_KEYWORD_WHILE) return parseWhileExpr(p); fprintf(stderr, "parsePrimaryExpr: inline without for/while\n"); - exit(1); + longjmp(p->error_jmp, 1); return 0; // tcc case TOKEN_L_BRACE: return parseBlock(p); @@ -2494,7 +2495,7 @@ static AstNodeIndex expectBlockExprStatement(Parser* p) { return expr; } fprintf(stderr, "expectBlockExprStatement: expected block or expr\n"); - exit(1); + longjmp(p->error_jmp, 1); return 0; // tcc } @@ -2568,7 +2569,7 @@ static AstNodeIndex expectVarDeclExprStatement( fprintf(stderr, "expectVarDeclExprStatement: unexpected token %s\n", tokenizerGetTagString(p->token_tags[p->tok_i])); - exit(1); + longjmp(p->error_jmp, 1); } if (assign_tag == AST_NODE_ASSIGN) { // Check if lhs is a var decl that needs initialization @@ -2636,7 +2637,7 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) { return expectVarDeclExprStatement(p, comptime_token); fprintf( stderr, "expectStatement: comptime keyword not supported here\n"); - exit(1); + longjmp(p->error_jmp, 1); } const AstNodeIndex tok = p->token_tags[p->tok_i]; @@ -2695,7 +2696,7 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) { const char* tok_str = tokenizerGetTagString(tok); fprintf( stderr, "expectStatement does not support keyword %s\n", tok_str); - exit(1); + longjmp(p->error_jmp, 1); default:; } @@ -2799,7 +2800,7 @@ static AstNodeIndex parseLabeledStatement(Parser* p) { if (label_token != 0) { fprintf(stderr, "parseLabeledStatement does not support labels\n"); - exit(1); + longjmp(p->error_jmp, 1); } return null_node; @@ -2850,7 +2851,7 @@ static AstNodeIndex expectTopLevelDecl(Parser* p) { .data = { .lhs = fn_proto, .rhs = body_block }, }); default: - exit(1); // Expected semicolon or left brace + longjmp(p->error_jmp, 1); // Expected semicolon or left brace } } @@ -2863,7 +2864,7 @@ static AstNodeIndex expectTopLevelDecl(Parser* p) { // assuming the program is correct... fprintf(stderr, "the next token should be usingnamespace, which is not supported\n"); - exit(1); + longjmp(p->error_jmp, 1); return 0; // make tcc happy } @@ -2954,7 +2955,7 @@ static Members parseContainerMembers(Parser* p) { const AstNodeIndex body = parseBlock(p); if (body == 0) { fprintf(stderr, "expected block after test\n"); - exit(1); + longjmp(p->error_jmp, 1); } const AstNodeIndex test_decl = addNode(&p->nodes, (AstNodeItem) { @@ -2970,7 +2971,7 @@ static Members parseContainerMembers(Parser* p) { const char* str = tokenizerGetTagString(p->token_tags[p->tok_i]); fprintf( stderr, "%s not implemented in parseContainerMembers\n", str); - exit(1); + longjmp(p->error_jmp, 1); 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 { ... @@ -3037,7 +3038,7 @@ static Members parseContainerMembers(Parser* p) { break; case FIELD_STATE_END: fprintf(stderr, "parseContainerMembers error condition\n"); - exit(1); + longjmp(p->error_jmp, 1); } SLICE_APPEND(AstNodeIndex, &p->scratch, field_node); switch (p->token_tags[p->tok_i]) { diff --git a/parser.h b/parser.h index 922d52e567..06ac581b25 100644 --- a/parser.h +++ b/parser.h @@ -4,6 +4,7 @@ #include "ast.h" #include "common.h" +#include #include #include @@ -20,6 +21,7 @@ typedef struct { AstNodeList nodes; AstNodeIndexSlice extra_data; AstNodeIndexSlice scratch; + jmp_buf error_jmp; } Parser; Parser* parserInit(const char* source, uint32_t len); diff --git a/parser_test.zig b/parser_test.zig index 058cbbd9d1..164829eac0 100644 --- a/parser_test.zig +++ b/parser_test.zig @@ -5809,16 +5809,10 @@ fn testCanonical(source: [:0]const u8) !void { const Error = std.zig.Ast.Error.Tag; fn testError(source: [:0]const u8, expected_errors: []const Error) !void { - var tree = try std.zig.Ast.parse(std.testing.allocator, source, .zig); - defer tree.deinit(std.testing.allocator); - - std.testing.expectEqual(expected_errors.len, tree.errors.len) catch |err| { - std.debug.print("errors found: {any}\n", .{tree.errors}); - return err; - }; - for (expected_errors, 0..) |expected, i| { - try std.testing.expectEqual(expected, tree.errors[i].tag); - } + _ = expected_errors; + var c_tree = c.astParse(source, @intCast(source.len)); + defer c.astDeinit(&c_tree); + try std.testing.expect(c_tree.has_error); } const testing = std.testing; @@ -6285,8 +6279,11 @@ fn zigAst(gpa: Allocator, c_ast: c.Ast) !Ast { errdefer gpa.free(extra_data); @memcpy(extra_data, c_ast.extra_data.arr[0..c_ast.extra_data.len]); - // creating a dummy `errors` slice, so deinit can free it. - const errors = try gpa.alloc(Ast.Error, 0); + const errors = if (c_ast.has_error) blk: { + const errs = try gpa.alloc(Ast.Error, 1); + errs[0] = .{ .tag = .expected_token, .token = 0, .extra = .{ .none = {} } }; + break :blk errs; + } else try gpa.alloc(Ast.Error, 0); errdefer gpa.free(errors); return Ast{