diff --git a/parser.c b/parser.c index 051f6e7d05..ac73af44f0 100644 --- a/parser.c +++ b/parser.c @@ -838,76 +838,75 @@ static AstNodeIndex parseErrorUnionExpr(Parser* p) { }); } -// parsePtrModifiersAndType parses pointer modifiers (allowzero, align, -// addrspace, const, volatile, sentinel) and the child type for a pointer -// started at main_token. -static AstNodeIndex parsePtrModifiersAndType( - Parser* p, AstTokenIndex main_token) { - AstNodeIndex sentinel = 0; - AstNodeIndex align_expr = 0; - AstNodeIndex bit_range_start = 0; - AstNodeIndex bit_range_end = 0; - AstNodeIndex addrspace_expr = 0; +typedef struct { + AstNodeIndex align_node; + AstNodeIndex addrspace_node; + AstNodeIndex bit_range_start; + AstNodeIndex bit_range_end; +} PtrModifiers; - // sentinel: *:0 - if (eatToken(p, TOKEN_COLON) != null_token) - sentinel = expectExpr(p); +static PtrModifiers parsePtrModifiers(Parser* p) { + PtrModifiers mods = {}; - // allowzero, const, volatile (before align) - while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO) - p->tok_i++; - - // align(expr) or align(expr:expr:expr) - if (eatToken(p, TOKEN_KEYWORD_ALIGN) != null_token) { - expectToken(p, TOKEN_L_PAREN); - align_expr = expectExpr(p); - if (eatToken(p, TOKEN_COLON) != null_token) { - bit_range_start = expectExpr(p); - expectToken(p, TOKEN_COLON); - bit_range_end = expectExpr(p); + while (true) { + switch (p->token_tags[p->tok_i]) { + case TOKEN_KEYWORD_CONST: + case TOKEN_KEYWORD_VOLATILE: + case TOKEN_KEYWORD_ALLOWZERO: + p->tok_i++; + continue; + case TOKEN_KEYWORD_ALIGN: + p->tok_i++; + expectToken(p, TOKEN_L_PAREN); + mods.align_node = expectExpr(p); + if (eatToken(p, TOKEN_COLON) != null_token) { + mods.bit_range_start = expectExpr(p); + expectToken(p, TOKEN_COLON); + mods.bit_range_end = expectExpr(p); + } + expectToken(p, TOKEN_R_PAREN); + continue; + case TOKEN_KEYWORD_ADDRSPACE: + p->tok_i++; + expectToken(p, TOKEN_L_PAREN); + mods.addrspace_node = expectExpr(p); + expectToken(p, TOKEN_R_PAREN); + continue; + default: + return mods; } - expectToken(p, TOKEN_R_PAREN); } +} - // addrspace - addrspace_expr = parseAddrSpace(p); - - // const, volatile, allowzero (after align/addrspace) - while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO) - p->tok_i++; - - const AstNodeIndex child_type = parseTypeExpr(p); - - if (bit_range_start != 0) { +static AstNodeIndex makePtrTypeNode(Parser* p, AstTokenIndex main_token, + AstNodeIndex sentinel, PtrModifiers mods, AstNodeIndex elem_type) { + if (mods.bit_range_start != 0) { return addNode(&p->nodes, (AstNodeItem) { .tag = AST_NODE_PTR_TYPE_BIT_RANGE, .main_token = main_token, .data = { .lhs = addExtra(p, - (AstNodeIndex[]) { OPT(sentinel), align_expr, - OPT(addrspace_expr), bit_range_start, - bit_range_end }, + (AstNodeIndex[]) { OPT(sentinel), mods.align_node, + OPT(mods.addrspace_node), mods.bit_range_start, + mods.bit_range_end }, 5), - .rhs = child_type, + .rhs = elem_type, }, }); } - if (addrspace_expr != 0 || (sentinel != 0 && align_expr != 0)) { + if (mods.addrspace_node != 0 || (sentinel != 0 && mods.align_node != 0)) { return addNode(&p->nodes, (AstNodeItem) { .tag = AST_NODE_PTR_TYPE, .main_token = main_token, .data = { .lhs = addExtra(p, - (AstNodeIndex[]) { OPT(sentinel), OPT(align_expr), - OPT(addrspace_expr) }, + (AstNodeIndex[]) { OPT(sentinel), + OPT(mods.align_node), + OPT(mods.addrspace_node) }, 3), - .rhs = child_type, + .rhs = elem_type, }, }); } @@ -916,14 +915,14 @@ static AstNodeIndex parsePtrModifiersAndType( (AstNodeItem) { .tag = AST_NODE_PTR_TYPE_SENTINEL, .main_token = main_token, - .data = { .lhs = sentinel, .rhs = child_type }, + .data = { .lhs = sentinel, .rhs = elem_type }, }); } return addNode(&p->nodes, (AstNodeItem) { .tag = AST_NODE_PTR_TYPE_ALIGNED, .main_token = main_token, - .data = { .lhs = align_expr, .rhs = child_type }, + .data = { .lhs = mods.align_node, .rhs = elem_type }, }); } @@ -943,77 +942,20 @@ static AstNodeIndex parseTypeExpr(Parser* p) { exit(1); case TOKEN_ASTERISK: { const AstTokenIndex asterisk = nextToken(p); - return parsePtrModifiersAndType(p, asterisk); + const PtrModifiers mods = parsePtrModifiers(p); + const AstNodeIndex elem_type = parseTypeExpr(p); + return makePtrTypeNode(p, asterisk, 0, mods, elem_type); } case TOKEN_ASTERISK_ASTERISK: { - // ** is two nested pointer types sharing the same token. - // Inner pointer gets modifiers, outer wraps it with none. - // (Matches upstream Parse.zig asterisk_asterisk case) const AstTokenIndex asterisk = nextToken(p); - - // Parse inner pointer modifiers (no sentinel for **) - while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO) - p->tok_i++; - AstNodeIndex align_expr = 0; - AstNodeIndex bit_range_start = 0; - AstNodeIndex bit_range_end = 0; - if (eatToken(p, TOKEN_KEYWORD_ALIGN) != null_token) { - expectToken(p, TOKEN_L_PAREN); - align_expr = expectExpr(p); - if (eatToken(p, TOKEN_COLON) != null_token) { - bit_range_start = expectExpr(p); - expectToken(p, TOKEN_COLON); - bit_range_end = expectExpr(p); - } - expectToken(p, TOKEN_R_PAREN); - } - const AstNodeIndex addrspace_expr = parseAddrSpace(p); - while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO) - p->tok_i++; + const PtrModifiers mods = parsePtrModifiers(p); const AstNodeIndex elem_type = parseTypeExpr(p); - assert(elem_type != 0); - - AstNodeIndex inner; - if (bit_range_start != 0) { - inner = addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_PTR_TYPE_BIT_RANGE, - .main_token = asterisk, - .data = { - .lhs = addExtra(p, - (AstNodeIndex[]) { OPT(0), align_expr, - OPT(addrspace_expr), bit_range_start, - bit_range_end }, - 5), - .rhs = elem_type, - }, - }); - } else if (addrspace_expr != 0) { - inner = addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_PTR_TYPE, - .main_token = asterisk, - .data = { - .lhs = addExtra(p, - (AstNodeIndex[]) { OPT(0), OPT(align_expr), - addrspace_expr }, - 3), - .rhs = elem_type, - }, - }); - } else { - inner = addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_PTR_TYPE_ALIGNED, - .main_token = asterisk, - .data = { .lhs = align_expr, .rhs = elem_type }, - }); + if (elem_type == 0) { + fprintf(stderr, "expected type expression\n"); + exit(1); } - + const AstNodeIndex inner + = makePtrTypeNode(p, asterisk, 0, mods, elem_type); return addNode(&p->nodes, (AstNodeItem) { .tag = AST_NODE_PTR_TYPE_ALIGNED, @@ -1029,7 +971,6 @@ static AstNodeIndex parseTypeExpr(Parser* p) { AstNodeIndex sentinel = 0; if (p->token_tags[p->tok_i] == TOKEN_IDENTIFIER) { // Check for 'c' modifier: [*c] - // The 'c' is a regular identifier token const char c = p->source[p->token_starts[p->tok_i]]; if (c == 'c' && p->token_starts[p->tok_i + 1] @@ -1041,133 +982,19 @@ static AstNodeIndex parseTypeExpr(Parser* p) { sentinel = expectExpr(p); } expectToken(p, TOKEN_R_BRACKET); - // Reuse shared pointer modifier + type parsing - // If we captured a sentinel from [*:s], temporarily store it - // and let parsePtrModifiersAndType handle the rest. - // But parsePtrModifiersAndType expects sentinel after main - // token via `:`. Since we already consumed it, we need to - // handle this inline. - - // allowzero, const, volatile (before align) - while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO) - p->tok_i++; - - AstNodeIndex align_expr = 0; - AstNodeIndex bit_range_start = 0; - AstNodeIndex bit_range_end = 0; - if (eatToken(p, TOKEN_KEYWORD_ALIGN) != null_token) { - expectToken(p, TOKEN_L_PAREN); - align_expr = expectExpr(p); - if (eatToken(p, TOKEN_COLON) != null_token) { - bit_range_start = expectExpr(p); - expectToken(p, TOKEN_COLON); - bit_range_end = expectExpr(p); - } - expectToken(p, TOKEN_R_PAREN); - } - const AstNodeIndex addrspace_expr = parseAddrSpace(p); - - while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO) - p->tok_i++; - + const PtrModifiers mods = parsePtrModifiers(p); const AstNodeIndex elem_type = parseTypeExpr(p); - - if (bit_range_start != 0) { - return addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_PTR_TYPE_BIT_RANGE, - .main_token = lbracket, - .data = { - .lhs = addExtra(p, - (AstNodeIndex[]) { OPT(sentinel), - align_expr, OPT(addrspace_expr), - bit_range_start, bit_range_end }, - 5), - .rhs = elem_type, - }, - }); - } - if (addrspace_expr != 0 || (sentinel != 0 && align_expr != 0)) { - return addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_PTR_TYPE, - .main_token = lbracket, - .data = { - .lhs = addExtra(p, - (AstNodeIndex[]) { OPT(sentinel), - OPT(align_expr), - OPT(addrspace_expr) }, - 3), - .rhs = elem_type, - }, - }); - } - if (sentinel != 0) { - return addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_PTR_TYPE_SENTINEL, - .main_token = lbracket, - .data = { .lhs = sentinel, .rhs = elem_type }, - }); - } - return addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_PTR_TYPE_ALIGNED, - .main_token = lbracket, - .data = { .lhs = align_expr, .rhs = elem_type }, - }); + return makePtrTypeNode(p, lbracket, sentinel, mods, elem_type); } const AstNodeIndex len_expr = parseExpr(p); const AstNodeIndex sentinel = eatToken(p, TOKEN_COLON) != null_token ? expectExpr(p) : 0; expectToken(p, TOKEN_R_BRACKET); if (len_expr == 0) { - // Slice type: []T or [:s]T — reuse shared modifier parsing - // allowzero, const, volatile - while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO) - p->tok_i++; - const AstNodeIndex align_expr = parseByteAlign(p); - const AstNodeIndex addrspace_expr = parseAddrSpace(p); - while (p->token_tags[p->tok_i] == TOKEN_KEYWORD_CONST - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_VOLATILE - || p->token_tags[p->tok_i] == TOKEN_KEYWORD_ALLOWZERO) - p->tok_i++; + // Slice type: []T or [:s]T + const PtrModifiers mods = parsePtrModifiers(p); const AstNodeIndex elem_type = parseTypeExpr(p); - if (addrspace_expr != 0 || (sentinel != 0 && align_expr != 0)) { - return addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_PTR_TYPE, - .main_token = lbracket, - .data = { - .lhs = addExtra(p, - (AstNodeIndex[]) { OPT(sentinel), - OPT(align_expr), - OPT(addrspace_expr) }, - 3), - .rhs = elem_type, - }, - }); - } - if (sentinel != 0) { - return addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_PTR_TYPE_SENTINEL, - .main_token = lbracket, - .data = { .lhs = sentinel, .rhs = elem_type }, - }); - } - return addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_PTR_TYPE_ALIGNED, - .main_token = lbracket, - .data = { .lhs = align_expr, .rhs = elem_type }, - }); + return makePtrTypeNode(p, lbracket, sentinel, mods, elem_type); } // Array type: [N]T or [N:s]T const AstNodeIndex elem_type = parseTypeExpr(p); @@ -1493,7 +1320,10 @@ static AstNodeIndex parseForStatement(Parser* p) { then_body = block; } else { then_body = parseAssignExpr(p); - assert(then_body != 0); + if (then_body == 0) { + fprintf(stderr, "expected expression\n"); + exit(1); + } if (eatToken(p, TOKEN_SEMICOLON) != null_token) seen_semicolon = true; } @@ -1545,6 +1375,15 @@ static AstNodeIndex parseForStatement(Parser* p) { }); } +static AstNodeIndex parseWhileContinueExpr(Parser* p) { + if (eatToken(p, TOKEN_COLON) == null_token) + return null_node; + expectToken(p, TOKEN_L_PAREN); + const AstNodeIndex expr = parseAssignExpr(p); + expectToken(p, TOKEN_R_PAREN); + return expr; +} + static AstNodeIndex parseWhileExpr(Parser* p) { const AstTokenIndex while_token = eatToken(p, TOKEN_KEYWORD_WHILE); if (while_token == null_token) @@ -1555,13 +1394,7 @@ static AstNodeIndex parseWhileExpr(Parser* p) { expectToken(p, TOKEN_R_PAREN); parsePtrPayload(p); - // Continue expression: : (expr) - AstNodeIndex cont_expr = 0; - if (eatToken(p, TOKEN_COLON) != null_token) { - expectToken(p, TOKEN_L_PAREN); - cont_expr = parseAssignExpr(p); - expectToken(p, TOKEN_R_PAREN); - } + const AstNodeIndex cont_expr = parseWhileContinueExpr(p); const AstNodeIndex body = expectExpr(p); @@ -1611,12 +1444,7 @@ static AstNodeIndex parseWhileStatement(Parser* p) { expectToken(p, TOKEN_R_PAREN); parsePtrPayload(p); - AstNodeIndex cont_expr = 0; - if (eatToken(p, TOKEN_COLON) != null_token) { - expectToken(p, TOKEN_L_PAREN); - cont_expr = parseAssignExpr(p); - expectToken(p, TOKEN_R_PAREN); - } + const AstNodeIndex cont_expr = parseWhileContinueExpr(p); // Statement body: block, or assign expr AstNodeIndex body; @@ -1626,7 +1454,10 @@ static AstNodeIndex parseWhileStatement(Parser* p) { body = block; } else { body = parseAssignExpr(p); - assert(body != 0); + if (body == 0) { + fprintf(stderr, "expected expression\n"); + exit(1); + } if (eatToken(p, TOKEN_SEMICOLON) != null_token) seen_semicolon = true; } @@ -2073,7 +1904,10 @@ static AstNodeIndex parseExprPrecedence(Parser* p, int32_t min_prec) { if (tok_tag == TOKEN_KEYWORD_CATCH) parsePayload(p); const AstNodeIndex rhs = parseExprPrecedence(p, info.prec + 1); - assert(rhs != 0); + if (rhs == 0) { + fprintf(stderr, "expected expression\n"); + exit(1); + } node = addNode( &p->nodes, @@ -2097,7 +1931,10 @@ static AstNodeIndex parseExpr(Parser* p) { return parseExprPrecedence(p, 0); } static AstNodeIndex expectExpr(Parser* p) { const AstNodeIndex node = parseExpr(p); - assert(node != 0); + if (node == 0) { + fprintf(stderr, "expected expression\n"); + exit(1); + } return node; } @@ -2258,6 +2095,84 @@ static AstNodeIndex parseAsmExpr(Parser* p) { }); } +static AstNodeIndex parseSwitchItem(Parser* p) { + const AstNodeIndex expr = parseExpr(p); + if (expr == 0) + return null_node; + if (p->token_tags[p->tok_i] == TOKEN_ELLIPSIS3) { + const AstTokenIndex range_tok = nextToken(p); + const AstNodeIndex range_end = expectExpr(p); + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_SWITCH_RANGE, + .main_token = range_tok, + .data = { .lhs = expr, .rhs = range_end }, + }); + } + return expr; +} + +static AstNodeIndex parseSwitchProng(Parser* p) { + const uint32_t items_old_len = p->scratch.len; + + if (eatToken(p, TOKEN_KEYWORD_ELSE) == null_token) { + while (true) { + const AstNodeIndex item = parseSwitchItem(p); + if (item == 0) + break; + SLICE_APPEND(AstNodeIndex, &p->scratch, item); + if (eatToken(p, TOKEN_COMMA) == null_token) + break; + } + if (p->scratch.len == items_old_len) + return null_node; + } + + const AstTokenIndex arrow + = expectToken(p, TOKEN_EQUAL_ANGLE_BRACKET_RIGHT); + parsePtrPayload(p); + const AstNodeIndex case_body = parseAssignExpr(p); + if (case_body == 0) { + fprintf(stderr, "expected expression\n"); + exit(1); + } + + const uint32_t items_len = p->scratch.len - items_old_len; + AstNodeIndex case_node; + switch (items_len) { + case 0: + case 1: + case_node = addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_SWITCH_CASE_ONE, + .main_token = arrow, + .data = { + .lhs + = items_len >= 1 ? p->scratch.arr[items_old_len] : 0, + .rhs = case_body, + }, + }); + break; + default: { + const AstSubRange span + = listToSpan(p, &p->scratch.arr[items_old_len], items_len); + case_node = addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_SWITCH_CASE, + .main_token = arrow, + .data = { + .lhs = addExtra(p, + (AstNodeIndex[]) { span.start, span.end }, 2), + .rhs = case_body, + }, + }); + } break; + } + + p->scratch.len = items_old_len; + return case_node; +} + static AstNodeIndex parseSwitchExpr(Parser* p) { const AstTokenIndex switch_token = eatToken(p, TOKEN_KEYWORD_SWITCH); if (switch_token == null_token) @@ -2275,77 +2190,10 @@ static AstNodeIndex parseSwitchExpr(Parser* p) { if (eatToken(p, TOKEN_R_BRACE) != null_token) break; eatDocComments(p); - // Parse switch case items - const uint32_t items_old_len = p->scratch.len; - - while (true) { - if (p->token_tags[p->tok_i] == TOKEN_KEYWORD_ELSE) { - p->tok_i++; - break; - } - if (p->token_tags[p->tok_i] == TOKEN_EQUAL_ANGLE_BRACKET_RIGHT) - break; - const AstNodeIndex item = expectExpr(p); - if (p->token_tags[p->tok_i] == TOKEN_ELLIPSIS3) { - const AstTokenIndex range_tok = nextToken(p); - const AstNodeIndex range_end = expectExpr(p); - SLICE_APPEND(AstNodeIndex, &p->scratch, - addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_SWITCH_RANGE, - .main_token = range_tok, - .data = { .lhs = item, .rhs = range_end }, - })); - } else { - SLICE_APPEND(AstNodeIndex, &p->scratch, item); - } - if (p->token_tags[p->tok_i] == TOKEN_COMMA) - p->tok_i++; - } - - const AstTokenIndex arrow - = expectToken(p, TOKEN_EQUAL_ANGLE_BRACKET_RIGHT); - parsePtrPayload(p); - const AstNodeIndex case_body = parseAssignExpr(p); - assert(case_body != 0); - - const uint32_t items_len = p->scratch.len - items_old_len; - AstNodeIndex case_node; - switch (items_len) { - case 0: - case 1: - case_node = addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_SWITCH_CASE_ONE, - .main_token = arrow, - .data = { - .lhs = items_len >= 1 - ? p->scratch.arr[items_old_len] - : 0, - .rhs = case_body, - }, - }); + const AstNodeIndex case_node = parseSwitchProng(p); + if (case_node == 0) break; - default: { - const AstSubRange span - = listToSpan(p, &p->scratch.arr[items_old_len], items_len); - case_node = addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_SWITCH_CASE, - .main_token = arrow, - .data = { - .lhs = addExtra(p, - (AstNodeIndex[]) { span.start, span.end }, 2), - .rhs = case_body, - }, - }); - } break; - } - - // Restore scratch to before items but keep case_node count - p->scratch.len = items_old_len; SLICE_APPEND(AstNodeIndex, &p->scratch, case_node); - if (p->token_tags[p->tok_i] == TOKEN_COMMA) p->tok_i++; } @@ -2601,14 +2449,10 @@ static AstNodeTag assignOpTag(TokenizerTag tok) { } } -static AstNodeIndex parseAssignExpr(Parser* p) { - const AstNodeIndex expr = parseExpr(p); - if (expr == 0) - return null_node; - +static AstNodeIndex finishAssignExpr(Parser* p, AstNodeIndex lhs) { const AstNodeTag assign_tag = assignOpTag(p->token_tags[p->tok_i]); if (assign_tag == AST_NODE_ROOT) - return expr; + return lhs; const AstTokenIndex op_token = nextToken(p); const AstNodeIndex rhs = expectExpr(p); @@ -2616,12 +2460,18 @@ static AstNodeIndex parseAssignExpr(Parser* p) { (AstNodeItem) { .tag = assign_tag, .main_token = op_token, - .data = { .lhs = expr, .rhs = rhs }, + .data = { .lhs = lhs, .rhs = rhs }, }); } -static AstNodeIndex expectBlockExprStatement(Parser* p) { - // Try block first (labeled or unlabeled) +static AstNodeIndex parseAssignExpr(Parser* p) { + const AstNodeIndex expr = parseExpr(p); + if (expr == 0) + return null_node; + return finishAssignExpr(p, expr); +} + +static AstNodeIndex parseBlockExpr(Parser* p) { if (p->token_tags[p->tok_i] == TOKEN_L_BRACE) return parseBlock(p); if (p->token_tags[p->tok_i] == TOKEN_IDENTIFIER @@ -2630,6 +2480,13 @@ static AstNodeIndex expectBlockExprStatement(Parser* p) { p->tok_i += 2; return parseBlock(p); } + return null_node; +} + +static AstNodeIndex expectBlockExprStatement(Parser* p) { + const AstNodeIndex block_expr = parseBlockExpr(p); + if (block_expr != 0) + return block_expr; // Assign expr + semicolon const AstNodeIndex expr = parseAssignExpr(p); if (expr != 0) { @@ -2641,7 +2498,8 @@ static AstNodeIndex expectBlockExprStatement(Parser* p) { return 0; // tcc } -static AstNodeIndex expectVarDeclExprStatement(Parser* p) { +static AstNodeIndex expectVarDeclExprStatement( + Parser* p, AstTokenIndex comptime_token) { CleanupScratch scratch_top __attribute__((__cleanup__(cleanupScratch))) = initCleanupScratch(p); @@ -2665,15 +2523,46 @@ static AstNodeIndex expectVarDeclExprStatement(Parser* p) { switch (p->token_tags[p->tok_i]) { case TOKEN_SEMICOLON: p->tok_i++; + if (comptime_token != null_token) { + const AstNodeTag lhs_tag = p->nodes.tags[lhs]; + if (lhs_tag != AST_NODE_SIMPLE_VAR_DECL + && lhs_tag != AST_NODE_ALIGNED_VAR_DECL + && lhs_tag != AST_NODE_LOCAL_VAR_DECL + && lhs_tag != AST_NODE_GLOBAL_VAR_DECL) { + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_COMPTIME, + .main_token = comptime_token, + .data = { .lhs = lhs, .rhs = 0 }, + }); + } + } return lhs; case TOKEN_R_BRACE: // Expression that doesn't need semicolon (block-terminated) + if (comptime_token != null_token) { + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_COMPTIME, + .main_token = comptime_token, + .data = { .lhs = lhs, .rhs = 0 }, + }); + } return lhs; default: { // Check if expression ended with a block (previous token is }) // and thus doesn't need a semicolon - if (p->token_tags[p->tok_i - 1] == TOKEN_R_BRACE) + if (p->token_tags[p->tok_i - 1] == TOKEN_R_BRACE) { + if (comptime_token != null_token) { + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_COMPTIME, + .main_token = comptime_token, + .data = { .lhs = lhs, .rhs = 0 }, + }); + } return lhs; + } const AstNodeTag assign_tag = assignOpTag(p->token_tags[p->tok_i]); if (assign_tag == AST_NODE_ROOT) { fprintf(stderr, @@ -2743,27 +2632,8 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) { }); } // comptime var decl or expression - if (allow_defer_var) { - // Pass through to expectVarDeclExprStatement. - // For var decls, the comptime prefix is detected from token - // positions by the renderer (no wrapping needed). - // For expressions, the result is wrapped in a comptime node. - const AstNodeIndex inner = expectVarDeclExprStatement(p); - const AstNodeTag inner_tag = p->nodes.tags[inner]; - if (inner_tag == AST_NODE_SIMPLE_VAR_DECL - || inner_tag == AST_NODE_ALIGNED_VAR_DECL - || inner_tag == AST_NODE_LOCAL_VAR_DECL - || inner_tag == AST_NODE_GLOBAL_VAR_DECL - || inner_tag == AST_NODE_ASSIGN_DESTRUCTURE) { - return inner; - } - return addNode(&p->nodes, - (AstNodeItem) { - .tag = AST_NODE_COMPTIME, - .main_token = comptime_token, - .data = { .lhs = inner, .rhs = 0 }, - }); - } + if (allow_defer_var) + return expectVarDeclExprStatement(p, comptime_token); fprintf( stderr, "expectStatement: comptime keyword not supported here\n"); exit(1); @@ -2834,7 +2704,7 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) { return labeled_statement; if (allow_defer_var) { - return expectVarDeclExprStatement(p); + return expectVarDeclExprStatement(p, null_token); } else { const AstNodeIndex assign_expr = parseAssignExpr(p); expectSemicolon(p); @@ -3082,7 +2952,10 @@ static Members parseContainerMembers(Parser* p) { ? nextToken(p) : null_token; const AstNodeIndex body = parseBlock(p); - assert(body != 0); + if (body == 0) { + fprintf(stderr, "expected block after test\n"); + exit(1); + } const AstNodeIndex test_decl = addNode(&p->nodes, (AstNodeItem) { .tag = AST_NODE_TEST_DECL,