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 <noreply@anthropic.com>
This commit is contained in:
18
ast.c
18
ast.c
@@ -2,8 +2,8 @@
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
|
||||
1
ast.h
1
ast.h
@@ -541,6 +541,7 @@ typedef struct {
|
||||
AstNodeList nodes;
|
||||
AstNodeIndexSlice extra_data;
|
||||
bool has_error;
|
||||
char* err_msg;
|
||||
} Ast;
|
||||
|
||||
typedef struct AstPtrType {
|
||||
|
||||
286
parser.c
286
parser.c
@@ -2,8 +2,6 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -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;
|
||||
|
||||
13
parser.h
13
parser.h
@@ -7,6 +7,7 @@
|
||||
#include <setjmp.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user