diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2018-11-17 02:18:56 -0500 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2018-11-17 02:18:56 -0500 |
| commit | c21884e1d64e4193e03be4f3064917a26b34b142 (patch) | |
| tree | 91e09e1df556e260ea2f1b171eaa05bd30646e1f /src/parser.cpp | |
| parent | 2928b01afc5f0d84669ac6a70eedab4117d805f3 (diff) | |
| parent | 704374e51294e14285b0b54030c1cb6154868098 (diff) | |
| download | zig-c21884e1d64e4193e03be4f3064917a26b34b142.tar.gz zig-c21884e1d64e4193e03be4f3064917a26b34b142.zip | |
Merge remote-tracking branch 'origin/master' into llvm8
Diffstat (limited to 'src/parser.cpp')
| -rw-r--r-- | src/parser.cpp | 4636 |
1 files changed, 2304 insertions, 2332 deletions
diff --git a/src/parser.cpp b/src/parser.cpp index 59e70713ab..b5c67875de 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -16,54 +16,157 @@ struct ParseContext { Buf *buf; - AstNode *root; + size_t current_token; ZigList<Token> *tokens; ImportTableEntry *owner; ErrColor err_color; - // These buffers are used freqently so we preallocate them once here. - Buf *void_buf; }; -ATTRIBUTE_PRINTF(4, 5) -ATTRIBUTE_NORETURN -static void ast_asm_error(ParseContext *pc, AstNode *node, size_t offset, const char *format, ...) { - assert(node->type == NodeTypeAsmExpr); +struct PtrPayload { + Token *asterisk; + Token *payload; +}; +struct PtrIndexPayload { + Token *asterisk; + Token *payload; + Token *index; +}; - // TODO calculate or otherwise keep track of originating line/column number for strings - //SrcPos pos = node->data.asm_expr.offset_map.at(offset); - SrcPos pos = { node->line, node->column }; +static AstNode *ast_parse_root(ParseContext *pc); +static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc); +static AstNode *ast_parse_test_decl(ParseContext *pc); +static AstNode *ast_parse_top_level_comptime(ParseContext *pc); +static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod); +static AstNode *ast_parse_fn_proto(ParseContext *pc); +static AstNode *ast_parse_var_decl(ParseContext *pc); +static AstNode *ast_parse_container_field(ParseContext *pc); +static AstNode *ast_parse_statement(ParseContext *pc); +static AstNode *ast_parse_if_statement(ParseContext *pc); +static AstNode *ast_parse_labeled_statement(ParseContext *pc); +static AstNode *ast_parse_loop_statement(ParseContext *pc); +static AstNode *ast_parse_for_statement(ParseContext *pc); +static AstNode *ast_parse_while_statement(ParseContext *pc); +static AstNode *ast_parse_block_expr_statement(ParseContext *pc); +static AstNode *ast_parse_block_expr(ParseContext *pc); +static AstNode *ast_parse_assign_expr(ParseContext *pc); +static AstNode *ast_parse_expr(ParseContext *pc); +static AstNode *ast_parse_bool_or_expr(ParseContext *pc); +static AstNode *ast_parse_bool_and_expr(ParseContext *pc); +static AstNode *ast_parse_compare_expr(ParseContext *pc); +static AstNode *ast_parse_bitwise_expr(ParseContext *pc); +static AstNode *ast_parse_bit_shit_expr(ParseContext *pc); +static AstNode *ast_parse_addition_expr(ParseContext *pc); +static AstNode *ast_parse_multiply_expr(ParseContext *pc); +static AstNode *ast_parse_prefix_expr(ParseContext *pc); +static AstNode *ast_parse_primary_expr(ParseContext *pc); +static AstNode *ast_parse_if_expr(ParseContext *pc); +static AstNode *ast_parse_labeled_expr(ParseContext *pc); +static AstNode *ast_parse_block(ParseContext *pc); +static AstNode *ast_parse_loop_expr(ParseContext *pc); +static AstNode *ast_parse_for_expr(ParseContext *pc); +static AstNode *ast_parse_while_expr(ParseContext *pc); +static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc); +static AstNode *ast_parse_init_list(ParseContext *pc); +static AstNode *ast_parse_type_expr(ParseContext *pc); +static AstNode *ast_parse_error_union_expr(ParseContext *pc); +static AstNode *ast_parse_suffix_expr(ParseContext *pc); +static AstNode *ast_parse_primary_type_expr(ParseContext *pc); +static AstNode *ast_parse_container_decl(ParseContext *pc); +static AstNode *ast_parse_error_set_decl(ParseContext *pc); +static AstNode *ast_parse_grouped_expr(ParseContext *pc); +static AstNode *ast_parse_if_type_expr(ParseContext *pc); +static AstNode *ast_parse_labeled_type_expr(ParseContext *pc); +static AstNode *ast_parse_loop_type_expr(ParseContext *pc); +static AstNode *ast_parse_for_type_expr(ParseContext *pc); +static AstNode *ast_parse_while_type_expr(ParseContext *pc); +static AstNode *ast_parse_switch_expr(ParseContext *pc); +static AstNode *ast_parse_asm_expr(ParseContext *pc); +static AstNode *ast_parse_asm_output(ParseContext *pc); +static AsmOutput *ast_parse_asm_output_item(ParseContext *pc); +static AstNode *ast_parse_asm_input(ParseContext *pc); +static AsmInput *ast_parse_asm_input_item(ParseContext *pc); +static AstNode *ast_parse_asm_cloppers(ParseContext *pc); +static Token *ast_parse_break_label(ParseContext *pc); +static Token *ast_parse_block_label(ParseContext *pc); +static AstNode *ast_parse_field_init(ParseContext *pc); +static AstNode *ast_parse_while_continue_expr(ParseContext *pc); +static AstNode *ast_parse_section(ParseContext *pc); +static Optional<AstNodeFnProto> ast_parse_fn_cc(ParseContext *pc); +static AstNode *ast_parse_param_decl(ParseContext *pc); +static AstNode *ast_parse_param_type(ParseContext *pc); +static AstNode *ast_parse_if_prefix(ParseContext *pc); +static AstNode *ast_parse_while_prefix(ParseContext *pc); +static AstNode *ast_parse_for_prefix(ParseContext *pc); +static Token *ast_parse_payload(ParseContext *pc); +static Optional<PtrPayload> ast_parse_ptr_payload(ParseContext *pc); +static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc); +static AstNode *ast_parse_switch_prong(ParseContext *pc); +static AstNode *ast_parse_switch_case(ParseContext *pc); +static AstNode *ast_parse_switch_item(ParseContext *pc); +static AstNode *ast_parse_assign_op(ParseContext *pc); +static AstNode *ast_parse_compare_op(ParseContext *pc); +static AstNode *ast_parse_bitwise_op(ParseContext *pc); +static AstNode *ast_parse_bit_shift_op(ParseContext *pc); +static AstNode *ast_parse_addition_op(ParseContext *pc); +static AstNode *ast_parse_multiply_op(ParseContext *pc); +static AstNode *ast_parse_prefix_op(ParseContext *pc); +static AstNode *ast_parse_prefix_type_op(ParseContext *pc); +static AstNode *ast_parse_suffix_op(ParseContext *pc); +static AstNode *ast_parse_async_prefix(ParseContext *pc); +static AstNode *ast_parse_fn_call_argumnets(ParseContext *pc); +static AstNode *ast_parse_array_type_start(ParseContext *pc); +static AstNode *ast_parse_ptr_type_start(ParseContext *pc); +static AstNode *ast_parse_container_decl_auto(ParseContext *pc); +static AstNode *ast_parse_container_decl_type(ParseContext *pc); +static AstNode *ast_parse_byte_align(ParseContext *pc); +ATTRIBUTE_PRINTF(3, 4) +ATTRIBUTE_NORETURN +static void ast_error(ParseContext *pc, Token *token, const char *format, ...) { va_list ap; va_start(ap, format); Buf *msg = buf_vprintf(format, ap); va_end(ap); - ErrorMsg *err = err_msg_create_with_line(pc->owner->path, pos.line, pos.column, + + ErrorMsg *err = err_msg_create_with_line(pc->owner->path, token->start_line, token->start_column, pc->owner->source_code, pc->owner->line_offsets, msg); + err->line_start = token->start_line; + err->column_start = token->start_column; print_err_msg(err, pc->err_color); exit(EXIT_FAILURE); } -ATTRIBUTE_PRINTF(3, 4) +ATTRIBUTE_PRINTF(4, 5) ATTRIBUTE_NORETURN -static void ast_error(ParseContext *pc, Token *token, const char *format, ...) { +static void ast_asm_error(ParseContext *pc, AstNode *node, size_t offset, const char *format, ...) { + assert(node->type == NodeTypeAsmExpr); va_list ap; va_start(ap, format); Buf *msg = buf_vprintf(format, ap); va_end(ap); - - ErrorMsg *err = err_msg_create_with_line(pc->owner->path, token->start_line, token->start_column, + ErrorMsg *err = err_msg_create_with_line(pc->owner->path, node->line, node->column, pc->owner->source_code, pc->owner->line_offsets, msg); - err->line_start = token->start_line; - err->column_start = token->start_column; print_err_msg(err, pc->err_color); exit(EXIT_FAILURE); } +static Buf ast_token_str(Buf *input, Token *token) { + Buf str = BUF_INIT; + buf_init_from_mem(&str, buf_ptr(input) + token->start_pos, token->end_pos - token->start_pos); + return str; +} + +ATTRIBUTE_NORETURN +static void ast_invalid_token_error(ParseContext *pc, Token *token) { + Buf token_value = ast_token_str(pc->buf, token); + ast_error(pc, token, "invalid token: '%s'", buf_ptr(&token_value)); +} + static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) { AstNode *node = allocate<AstNode>(1); node->type = type; @@ -71,21 +174,316 @@ static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) { return node; } -static void ast_update_node_line_info(AstNode *node, Token *first_token) { +static AstNode *ast_create_node(ParseContext *pc, NodeType type, Token *first_token) { assert(first_token); + AstNode *node = ast_create_node_no_line_info(pc, type); node->line = first_token->start_line; node->column = first_token->start_column; + return node; } -static AstNode *ast_create_node(ParseContext *pc, NodeType type, Token *first_token) { - assert(first_token); +static AstNode *ast_create_node_copy_line_info(ParseContext *pc, NodeType type, AstNode *from) { + assert(from); AstNode *node = ast_create_node_no_line_info(pc, type); - ast_update_node_line_info(node, first_token); + node->line = from->line; + node->column = from->column; return node; } +static Token *peek_token_i(ParseContext *pc, size_t i) { + return &pc->tokens->at(pc->current_token + i); +} + +static Token *peek_token(ParseContext *pc) { + return peek_token_i(pc, 0); +} + +static Token *eat_token(ParseContext *pc) { + Token *res = peek_token(pc); + pc->current_token += 1; + return res; +} + +static Token *eat_token_if(ParseContext *pc, TokenId id) { + Token *res = peek_token(pc); + if (res->id == id) + return eat_token(pc); + + return nullptr; +} + +static Token *expect_token(ParseContext *pc, TokenId id) { + Token *res = eat_token(pc); + if (res->id != id) + ast_error(pc, res, "expected token '%s', found '%s'", token_name(id), token_name(res->id)); + + return res; +} + +static void put_back_token(ParseContext *pc) { + pc->current_token -= 1; +} + +static Buf *token_buf(Token *token) { + if (token == nullptr) + return nullptr; + assert(token->id == TokenIdStringLiteral || token->id == TokenIdSymbol); + return &token->data.str_lit.str; +} + +static BigInt *token_bigint(Token *token) { + assert(token->id == TokenIdIntLiteral); + return &token->data.int_lit.bigint; +} + +static AstNode *token_symbol(ParseContext *pc, Token *token) { + assert(token->id == TokenIdSymbol); + AstNode *res = ast_create_node(pc, NodeTypeSymbol, token); + res->data.symbol_expr.symbol = token_buf(token); + return res; +} + +// (Rule SEP)* Rule? +template<typename T> +static ZigList<T *> ast_parse_list(ParseContext *pc, TokenId sep, T *(*parser)(ParseContext*)) { + ZigList<T *> res = {}; + while (true) { + T *curr = parser(pc); + if (curr == nullptr) + break; + + res.append(curr); + if (eat_token_if(pc, sep) == nullptr) + break; + } + + return res; +} + +static AstNode *ast_expect(ParseContext *pc, AstNode *(*parser)(ParseContext*)) { + AstNode *res = parser(pc); + if (res == nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + return res; +} + +enum BinOpChain { + BinOpChainOnce, + BinOpChainInf, +}; + +// Op* Child +static AstNode *ast_parse_prefix_op_expr( + ParseContext *pc, + AstNode *(*op_parser)(ParseContext *), + AstNode *(*child_parser)(ParseContext *) +) { + AstNode *res = nullptr; + AstNode **right = &res; + while (true) { + AstNode *prefix = op_parser(pc); + if (prefix == nullptr) + break; + + *right = prefix; + switch (prefix->type) { + case NodeTypePrefixOpExpr: + right = &prefix->data.prefix_op_expr.primary_expr; + break; + case NodeTypeReturnExpr: + right = &prefix->data.return_expr.expr; + break; + case NodeTypeAwaitExpr: + right = &prefix->data.await_expr.expr; + break; + case NodeTypePromiseType: + right = &prefix->data.promise_type.payload_type; + break; + case NodeTypeArrayType: + right = &prefix->data.array_type.child_type; + break; + case NodeTypePointerType: { + // We might get two pointers from *_ptr_type_start + AstNode *child = prefix->data.pointer_type.op_expr; + if (child == nullptr) + child = prefix; + right = &child->data.pointer_type.op_expr; + break; + } + default: + zig_unreachable(); + } + } + + // If we have already consumed a token, and determined that + // this node is a prefix op, then we expect that the node has + // a child. + if (res != nullptr) { + *right = ast_expect(pc, child_parser); + } else { + // Otherwise, if we didn't consume a token, then we can return + // null, if the child expr did. + *right = child_parser(pc); + if (*right == nullptr) + return nullptr; + } + + return res; +} + +// Child (Op Child)(*/?) +static AstNode *ast_parse_bin_op_expr( + ParseContext *pc, + BinOpChain chain, + AstNode *(*op_parse)(ParseContext*), + AstNode *(*child_parse)(ParseContext*) +) { + AstNode *res = child_parse(pc); + if (res == nullptr) + return nullptr; + + do { + AstNode *op = op_parse(pc); + if (op == nullptr) + break; + + AstNode *left = res; + AstNode *right = ast_expect(pc, child_parse); + res = op; + switch (op->type) { + case NodeTypeBinOpExpr: + op->data.bin_op_expr.op1 = left; + op->data.bin_op_expr.op2 = right; + break; + case NodeTypeUnwrapErrorExpr: + op->data.unwrap_err_expr.op1 = left; + op->data.unwrap_err_expr.op2 = right; + break; + default: + zig_unreachable(); + } + } while (chain == BinOpChainInf); + + return res; +} + +// IfPrefix Body (KEYWORD_else Payload? Body)? +static AstNode *ast_parse_if_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) { + AstNode *res = ast_parse_if_prefix(pc); + if (res == nullptr) + return nullptr; + + AstNode *body = ast_expect(pc, body_parser); + Token *err_payload = nullptr; + AstNode *else_body = nullptr; + if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { + err_payload = ast_parse_payload(pc); + else_body = ast_expect(pc, body_parser); + } + + assert(res->type == NodeTypeTestExpr); + if (err_payload != nullptr) { + AstNodeTestExpr old = res->data.test_expr; + res->type = NodeTypeIfErrorExpr; + res->data.if_err_expr.target_node = old.target_node; + res->data.if_err_expr.var_is_ptr = old.var_is_ptr; + res->data.if_err_expr.var_symbol = old.var_symbol; + res->data.if_err_expr.then_node = body; + res->data.if_err_expr.err_symbol = token_buf(err_payload); + res->data.if_err_expr.else_node = else_body; + return res; + } + + if (res->data.test_expr.var_symbol != nullptr) { + res->data.test_expr.then_node = body; + res->data.test_expr.else_node = else_body; + return res; + } + + AstNodeTestExpr old = res->data.test_expr; + res->type = NodeTypeIfBoolExpr; + res->data.if_bool_expr.condition = old.target_node; + res->data.if_bool_expr.then_block = body; + res->data.if_bool_expr.else_node = else_body; + return res; +} + +// KEYWORD_inline? (ForLoop / WhileLoop) +static AstNode *ast_parse_loop_expr_helper( + ParseContext *pc, + AstNode *(*for_parser)(ParseContext *), + AstNode *(*while_parser)(ParseContext *) +) { + Token *inline_token = eat_token_if(pc, TokenIdKeywordInline); + AstNode *for_expr = for_parser(pc); + if (for_expr != nullptr) { + assert(for_expr->type == NodeTypeForExpr); + for_expr->data.for_expr.is_inline = inline_token != nullptr; + return for_expr; + } + + AstNode *while_expr = while_parser(pc); + if (while_expr != nullptr) { + assert(while_expr->type == NodeTypeWhileExpr); + while_expr->data.while_expr.is_inline = inline_token != nullptr; + return while_expr; + } + + if (inline_token != nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + return nullptr; +} + +// ForPrefix Body (KEYWORD_else Body)? +static AstNode *ast_parse_for_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) { + AstNode *res = ast_parse_for_prefix(pc); + if (res == nullptr) + return nullptr; + + AstNode *body = ast_expect(pc, body_parser); + AstNode *else_body = nullptr; + if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) + else_body = ast_expect(pc, body_parser); + + assert(res->type == NodeTypeForExpr); + res->data.for_expr.body = body; + res->data.for_expr.else_node = else_body; + return res; +} + +// WhilePrefix Body (KEYWORD_else Payload? Body)? +static AstNode *ast_parse_while_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) { + AstNode *res = ast_parse_while_prefix(pc); + if (res == nullptr) + return nullptr; + + AstNode *body = ast_expect(pc, body_parser); + Token *err_payload = nullptr; + AstNode *else_body = nullptr; + if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { + err_payload = ast_parse_payload(pc); + else_body = ast_expect(pc, body_parser); + } + + assert(res->type == NodeTypeWhileExpr); + res->data.while_expr.body = body; + res->data.while_expr.err_symbol = token_buf(err_payload); + res->data.while_expr.else_node = else_body; + return res; +} + +template<TokenId id, BinOpType op> +AstNode *ast_parse_bin_op_simple(ParseContext *pc) { + Token *op_token = eat_token_if(pc, id); + if (op_token == nullptr) + return nullptr; + + AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); + res->data.bin_op_expr.bin_op = op; + return res; +} -static void parse_asm_template(ParseContext *pc, AstNode *node) { +static void ast_parse_asm_template(ParseContext *pc, AstNode *node) { Buf *asm_template = node->data.asm_expr.asm_template; enum State { @@ -172,2733 +570,2310 @@ static void parse_asm_template(ParseContext *pc, AstNode *node) { } } -static Buf *token_buf(Token *token) { - assert(token->id == TokenIdStringLiteral || token->id == TokenIdSymbol); - return &token->data.str_lit.str; +AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner, + ErrColor err_color) +{ + ParseContext pc = {}; + pc.err_color = err_color; + pc.owner = owner; + pc.buf = buf; + pc.tokens = tokens; + return ast_parse_root(&pc); } -static BigInt *token_bigint(Token *token) { - assert(token->id == TokenIdIntLiteral); - return &token->data.int_lit.bigint; -} +// Root <- skip ContainerMembers eof +static AstNode *ast_parse_root(ParseContext *pc) { + Token *first = peek_token(pc); + AstNodeContainerDecl members = ast_parse_container_members(pc); + if (pc->current_token != pc->tokens->length - 1) + ast_invalid_token_error(pc, peek_token(pc)); -static BigFloat *token_bigfloat(Token *token) { - assert(token->id == TokenIdFloatLiteral); - return &token->data.float_lit.bigfloat; -} + AstNode *node = ast_create_node(pc, NodeTypeContainerDecl, first); + node->data.container_decl.fields = members.fields; + node->data.container_decl.decls = members.decls; + node->data.container_decl.layout = ContainerLayoutAuto; + node->data.container_decl.kind = ContainerKindStruct; + node->data.container_decl.is_root = true; -static uint8_t token_char_lit(Token *token) { - assert(token->id == TokenIdCharLiteral); - return token->data.char_lit.c; + return node; } -static void ast_buf_from_token(ParseContext *pc, Token *token, Buf *buf) { - if (token->id == TokenIdSymbol) { - buf_init_from_buf(buf, token_buf(token)); - } else { - buf_init_from_mem(buf, buf_ptr(pc->buf) + token->start_pos, token->end_pos - token->start_pos); - } -} +// ContainerMembers +// <- TestDecl ContainerMembers +// / TopLevelComptime ContainerMembers +// / KEYWORD_pub? TopLevelDecl ContainerMembers +// / KEYWORD_pub? ContainerField COMMA ContainerMembers +// / KEYWORD_pub? ContainerField +// / +static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { + AstNodeContainerDecl res = {}; + for (;;) { + AstNode *test_decl = ast_parse_test_decl(pc); + if (test_decl != nullptr) { + res.decls.append(test_decl); + continue; + } -ATTRIBUTE_NORETURN -static void ast_invalid_token_error(ParseContext *pc, Token *token) { - Buf token_value = BUF_INIT; - ast_buf_from_token(pc, token, &token_value); - ast_error(pc, token, "invalid token: '%s'", buf_ptr(&token_value)); -} + AstNode *top_level_comptime = ast_parse_top_level_comptime(pc); + if (top_level_comptime != nullptr) { + res.decls.append(top_level_comptime); + continue; + } -static AstNode *ast_parse_block_or_expression(ParseContext *pc, size_t *token_index, bool mandatory); -static AstNode *ast_parse_block_expr_or_expression(ParseContext *pc, size_t *token_index, bool mandatory); -static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool mandatory); -static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mandatory); -static AstNode *ast_parse_if_try_test_expr(ParseContext *pc, size_t *token_index, bool mandatory); -static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool mandatory); -static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory); -static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory); -static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod); -static AstNode *ast_parse_return_expr(ParseContext *pc, size_t *token_index); -static AstNode *ast_parse_grouped_expr(ParseContext *pc, size_t *token_index, bool mandatory); -static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, bool mandatory); -static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory); -static AstNode *ast_parse_try_expr(ParseContext *pc, size_t *token_index); -static AstNode *ast_parse_await_expr(ParseContext *pc, size_t *token_index); -static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index); + Token *visib_token = eat_token_if(pc, TokenIdKeywordPub); + VisibMod visib_mod = visib_token != nullptr ? VisibModPub : VisibModPrivate; -static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) { - if (token->id == token_id) { - return; - } + AstNode *top_level_decl = ast_parse_top_level_decl(pc, visib_mod); + if (top_level_decl != nullptr) { + res.decls.append(top_level_decl); + continue; + } - Buf token_value = BUF_INIT; - ast_buf_from_token(pc, token, &token_value); - ast_error(pc, token, "expected token '%s', found '%s'", token_name(token_id), token_name(token->id)); -} + AstNode *container_field = ast_parse_container_field(pc); + if (container_field != nullptr) { + assert(container_field->type == NodeTypeStructField); + container_field->data.struct_field.visib_mod = visib_mod; + res.fields.append(container_field); + if (eat_token_if(pc, TokenIdComma) != nullptr) { + continue; + } else { + break; + } + } -static Token *ast_eat_token(ParseContext *pc, size_t *token_index, TokenId token_id) { - Token *token = &pc->tokens->at(*token_index); - ast_expect_token(pc, token, token_id); - *token_index += 1; - return token; -} + // We visib_token wasn't eaten, then we haven't consumed the first token in this rule yet. + // It is therefore safe to return and let the caller continue parsing. + if (visib_token == nullptr) + break; -/* -ErrorSetExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression -*/ -static AstNode *ast_parse_error_set_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, mandatory); - if (!prefix_op_expr) { - return nullptr; + ast_invalid_token_error(pc, peek_token(pc)); } - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdBang) { - *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); - node->data.bin_op_expr.op1 = prefix_op_expr; - node->data.bin_op_expr.bin_op = BinOpTypeErrorUnion; - node->data.bin_op_expr.op2 = ast_parse_prefix_op_expr(pc, token_index, true); - return node; - } else { - return prefix_op_expr; - } -} -/* -TypeExpr = ErrorSetExpr -*/ -static AstNode *ast_parse_type_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - return ast_parse_error_set_expr(pc, token_index, mandatory); + return res; } -/* -ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "var" | "...") -*/ -static AstNode *ast_parse_param_decl(ParseContext *pc, size_t *token_index) { - Token *token = &pc->tokens->at(*token_index); - - AstNode *node = ast_create_node(pc, NodeTypeParamDecl, token); - - if (token->id == TokenIdKeywordNoAlias) { - node->data.param_decl.is_noalias = true; - *token_index += 1; - token = &pc->tokens->at(*token_index); - } else if (token->id == TokenIdKeywordCompTime) { - node->data.param_decl.is_inline = true; - *token_index += 1; - token = &pc->tokens->at(*token_index); - } - - node->data.param_decl.name = nullptr; - - if (token->id == TokenIdSymbol) { - Token *next_token = &pc->tokens->at(*token_index + 1); - if (next_token->id == TokenIdColon) { - node->data.param_decl.name = token_buf(token); - *token_index += 2; - } - } - - Token *ellipsis_tok = &pc->tokens->at(*token_index); - if (ellipsis_tok->id == TokenIdEllipsis3) { - *token_index += 1; - node->data.param_decl.is_var_args = true; - } else if (ellipsis_tok->id == TokenIdKeywordVar) { - *token_index += 1; - node->data.param_decl.var_token = ellipsis_tok; - } else { - node->data.param_decl.type = ast_parse_type_expr(pc, token_index, true); - } +// TestDecl <- KEYWORD_test STRINGLITERAL Block +static AstNode *ast_parse_test_decl(ParseContext *pc) { + Token *test = eat_token_if(pc, TokenIdKeywordTest); + if (test == nullptr) + return nullptr; - return node; + Token *name = expect_token(pc, TokenIdStringLiteral); + AstNode *block = ast_expect(pc, ast_parse_block); + AstNode *res = ast_create_node(pc, NodeTypeTestDecl, test); + res->data.test_decl.name = token_buf(name); + res->data.test_decl.body = block; + return res; } +// TopLevelComptime <- KEYWORD_comptime BlockExpr +static AstNode *ast_parse_top_level_comptime(ParseContext *pc) { + Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime); + if (comptime == nullptr) + return nullptr; -static void ast_parse_param_decl_list(ParseContext *pc, size_t *token_index, - ZigList<AstNode *> *params, bool *is_var_args) -{ - *is_var_args = false; - - ast_eat_token(pc, token_index, TokenIdLParen); - - for (;;) { - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdRParen) { - *token_index += 1; - return; + AstNode *block = ast_expect(pc, ast_parse_block_expr); + AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); + res->data.comptime_expr.expr = block; + return res; +} + +// TopLevelDecl +// <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / KEYWORD_inline)? FnProto (SEMICOLON / Block) +// / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? VarDecl +// / KEYWORD_use Expr SEMICOLON +static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) { + Token *first = eat_token_if(pc, TokenIdKeywordExport); + if (first == nullptr) + first = eat_token_if(pc, TokenIdKeywordExtern); + if (first == nullptr) + first = eat_token_if(pc, TokenIdKeywordInline); + if (first != nullptr) { + Token *lib_name = nullptr; + if (first->id == TokenIdKeywordExtern) + lib_name = eat_token_if(pc, TokenIdStringLiteral); + + if (first->id != TokenIdKeywordInline) { + AstNode *var_decl = ast_parse_var_decl(pc); + if (var_decl != nullptr) { + assert(var_decl->type == NodeTypeVariableDeclaration); + var_decl->line = first->start_line; + var_decl->column = first->start_column; + var_decl->data.variable_declaration.visib_mod = visib_mod; + var_decl->data.variable_declaration.is_extern = first->id == TokenIdKeywordExtern; + var_decl->data.variable_declaration.is_export = first->id == TokenIdKeywordExport; + var_decl->data.variable_declaration.lib_name = token_buf(lib_name); + return var_decl; + } } - AstNode *param_decl_node = ast_parse_param_decl(pc, token_index); - bool expect_end = false; - assert(param_decl_node); - params->append(param_decl_node); - expect_end = param_decl_node->data.param_decl.is_var_args; - *is_var_args = expect_end; - - token = &pc->tokens->at(*token_index); - *token_index += 1; - if (token->id == TokenIdRParen) { - return; - } else { - ast_expect_token(pc, token, TokenIdComma); - if (expect_end) { - ast_eat_token(pc, token_index, TokenIdRParen); - return; + AstNode *fn_proto = ast_parse_fn_proto(pc); + if (fn_proto != nullptr) { + AstNode *body = ast_parse_block(pc); + if (body == nullptr) + expect_token(pc, TokenIdSemicolon); + + assert(fn_proto->type == NodeTypeFnProto); + fn_proto->line = first->start_line; + fn_proto->column = first->start_column; + fn_proto->data.fn_proto.visib_mod = visib_mod; + fn_proto->data.fn_proto.is_extern = first->id == TokenIdKeywordExtern; + fn_proto->data.fn_proto.is_export = first->id == TokenIdKeywordExport; + fn_proto->data.fn_proto.is_inline = first->id == TokenIdKeywordInline; + fn_proto->data.fn_proto.lib_name = token_buf(lib_name); + AstNode *res = fn_proto; + if (body != nullptr) { + res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto); + res->data.fn_def.fn_proto = fn_proto; + res->data.fn_def.body = body; + fn_proto->data.fn_proto.fn_def_node = res; } - } - } - zig_unreachable(); -} -static void ast_parse_fn_call_param_list(ParseContext *pc, size_t *token_index, ZigList<AstNode*> *params) { - for (;;) { - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdRParen) { - *token_index += 1; - return; + return res; } - AstNode *expr = ast_parse_expression(pc, token_index, true); - params->append(expr); - - token = &pc->tokens->at(*token_index); - *token_index += 1; - if (token->id == TokenIdRParen) { - return; - } else { - ast_expect_token(pc, token, TokenIdComma); - } + ast_invalid_token_error(pc, peek_token(pc)); } - zig_unreachable(); -} -/* -GroupedExpression : token(LParen) Expression token(RParen) -*/ -static AstNode *ast_parse_grouped_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *l_paren = &pc->tokens->at(*token_index); - if (l_paren->id != TokenIdLParen) { - if (mandatory) { - ast_expect_token(pc, l_paren, TokenIdLParen); - } else { - return nullptr; - } + AstNode *var_decl = ast_parse_var_decl(pc); + if (var_decl != nullptr) { + assert(var_decl->type == NodeTypeVariableDeclaration); + var_decl->data.variable_declaration.visib_mod = visib_mod; + return var_decl; } - *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeGroupedExpr, l_paren); + AstNode *fn_proto = ast_parse_fn_proto(pc); + if (fn_proto != nullptr) { + AstNode *body = ast_parse_block(pc); + if (body == nullptr) + expect_token(pc, TokenIdSemicolon); - node->data.grouped_expr = ast_parse_expression(pc, token_index, true); - - Token *r_paren = &pc->tokens->at(*token_index); - *token_index += 1; - ast_expect_token(pc, r_paren, TokenIdRParen); - - return node; -} - -/* -ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr -*/ -static AstNode *ast_parse_array_type_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *l_bracket = &pc->tokens->at(*token_index); - if (l_bracket->id != TokenIdLBracket) { - if (mandatory) { - ast_expect_token(pc, l_bracket, TokenIdLBracket); - } else { - return nullptr; + assert(fn_proto->type == NodeTypeFnProto); + fn_proto->data.fn_proto.visib_mod = visib_mod; + AstNode *res = fn_proto; + if (body != nullptr) { + res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto); + res->data.fn_def.fn_proto = fn_proto; + res->data.fn_def.body = body; + fn_proto->data.fn_proto.fn_def_node = res; } - } - - *token_index += 1; - - AstNode *node = ast_create_node(pc, NodeTypeArrayType, l_bracket); - node->data.array_type.size = ast_parse_expression(pc, token_index, false); - ast_eat_token(pc, token_index, TokenIdRBracket); - - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdKeywordAlign) { - *token_index += 1; - ast_eat_token(pc, token_index, TokenIdLParen); - node->data.array_type.align_expr = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); - - token = &pc->tokens->at(*token_index); + return res; } - if (token->id == TokenIdKeywordConst) { - *token_index += 1; - node->data.array_type.is_const = true; - - token = &pc->tokens->at(*token_index); - } - if (token->id == TokenIdKeywordVolatile) { - *token_index += 1; - node->data.array_type.is_volatile = true; - } - - node->data.array_type.child_type = ast_parse_type_expr(pc, token_index, true); - - return node; -} -/* -AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen) -*/ -static void ast_parse_asm_input_item(ParseContext *pc, size_t *token_index, AstNode *node) { - ast_eat_token(pc, token_index, TokenIdLBracket); - Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol); - ast_eat_token(pc, token_index, TokenIdRBracket); - - Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral); - - ast_eat_token(pc, token_index, TokenIdLParen); - AstNode *expr_node = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); - - AsmInput *asm_input = allocate<AsmInput>(1); - asm_input->asm_symbolic_name = token_buf(alias); - asm_input->constraint = token_buf(constraint); - asm_input->expr = expr_node; - node->data.asm_expr.input_list.append(asm_input); -} - -/* -AsmOutputItem : "[" "Symbol" "]" "String" "(" ("Symbol" | "->" PrefixOpExpression) ")" -*/ -static void ast_parse_asm_output_item(ParseContext *pc, size_t *token_index, AstNode *node) { - ast_eat_token(pc, token_index, TokenIdLBracket); - Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol); - ast_eat_token(pc, token_index, TokenIdRBracket); + Token *use = eat_token_if(pc, TokenIdKeywordUse); + if (use != nullptr) { + AstNode *expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdSemicolon); - Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral); - - AsmOutput *asm_output = allocate<AsmOutput>(1); - - ast_eat_token(pc, token_index, TokenIdLParen); - - Token *token = &pc->tokens->at(*token_index); - *token_index += 1; - if (token->id == TokenIdSymbol) { - asm_output->variable_name = token_buf(token); - } else if (token->id == TokenIdArrow) { - asm_output->return_type = ast_parse_type_expr(pc, token_index, true); - } else { - ast_invalid_token_error(pc, token); + AstNode *res = ast_create_node(pc, NodeTypeUse, use); + res->data.use.visib_mod = visib_mod; + res->data.use.expr = expr; + return res; } - ast_eat_token(pc, token_index, TokenIdRParen); - - asm_output->asm_symbolic_name = token_buf(alias); - asm_output->constraint = token_buf(constraint); - node->data.asm_expr.output_list.append(asm_output); + return nullptr; } -/* -AsmClobbers: token(Colon) list(token(String), token(Comma)) -*/ -static void ast_parse_asm_clobbers(ParseContext *pc, size_t *token_index, AstNode *node) { - Token *colon_tok = &pc->tokens->at(*token_index); - - if (colon_tok->id != TokenIdColon) - return; - - *token_index += 1; - - for (;;) { - Token *string_tok = &pc->tokens->at(*token_index); - ast_expect_token(pc, string_tok, TokenIdStringLiteral); - *token_index += 1; - - Buf *clobber_buf = token_buf(string_tok); - node->data.asm_expr.clobber_list.append(clobber_buf); - - Token *comma = &pc->tokens->at(*token_index); - - if (comma->id == TokenIdComma) { - *token_index += 1; - - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdRParen) { - break; - } else { - continue; +// FnProto <- FnCC? KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? Section? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr) +static AstNode *ast_parse_fn_proto(ParseContext *pc) { + Token *first = peek_token(pc); + AstNodeFnProto fn_cc; + Token *fn; + if (ast_parse_fn_cc(pc).unwrap(&fn_cc)) { + // The extern keyword for fn CC is also used for container decls. + // We therefore put it back, as allow container decl to consume it + // later. + if (fn_cc.cc == CallingConventionC) { + fn = eat_token_if(pc, TokenIdKeywordFn); + if (fn == nullptr) { + put_back_token(pc); + return nullptr; } } else { - break; + fn = expect_token(pc, TokenIdKeywordFn); } - } -} - -/* -AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers) -*/ -static void ast_parse_asm_input(ParseContext *pc, size_t *token_index, AstNode *node) { - Token *colon_tok = &pc->tokens->at(*token_index); - - if (colon_tok->id != TokenIdColon) - return; - - *token_index += 1; - - Token *colon_again = &pc->tokens->at(*token_index); - if (colon_again->id == TokenIdColon) { - ast_parse_asm_clobbers(pc, token_index, node); - return; + } else { + fn_cc = {}; + fn = eat_token_if(pc, TokenIdKeywordFn); + if (fn == nullptr) + return nullptr; } - for (;;) { - ast_parse_asm_input_item(pc, token_index, node); + Token *identifier = eat_token_if(pc, TokenIdSymbol); + expect_token(pc, TokenIdLParen); + ZigList<AstNode *> params = ast_parse_list(pc, TokenIdComma, ast_parse_param_decl); + expect_token(pc, TokenIdRParen); + + AstNode *align_expr = ast_parse_byte_align(pc); + AstNode *section_expr = ast_parse_section(pc); + Token *var = eat_token_if(pc, TokenIdKeywordVar); + Token *exmark = nullptr; + AstNode *return_type = nullptr; + if (var == nullptr) { + exmark = eat_token_if(pc, TokenIdBang); + return_type = ast_expect(pc, ast_parse_type_expr); + } + + AstNode *res = ast_create_node(pc, NodeTypeFnProto, first); + res->data.fn_proto = fn_cc; + res->data.fn_proto.name = token_buf(identifier); + res->data.fn_proto.params = params; + res->data.fn_proto.align_expr = align_expr; + res->data.fn_proto.section_expr = section_expr; + res->data.fn_proto.return_var_token = var; + res->data.fn_proto.auto_err_set = exmark != nullptr; + res->data.fn_proto.return_type = return_type; + + // It seems that the Zig compiler expects varargs to be the + // last parameter in the decl list. This is not encoded in + // the grammar, which allows varargs anywhere in the decl. + // Since varargs is gonna be removed at some point, I'm not + // gonna encode this "varargs is always last" rule in the + // grammar, and just enforce it here, until varargs is removed. + for (size_t i = 0; i < params.length; i++) { + AstNode *param_decl = params.at(i); + assert(param_decl->type == NodeTypeParamDecl); + if (param_decl->data.param_decl.is_var_args) + res->data.fn_proto.is_var_args = true; + if (i != params.length - 1 && res->data.fn_proto.is_var_args) + ast_error(pc, first, "Function prototype have varargs as a none last paramter."); + } + return res; +} + +// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? Section? (EQUAL Expr)? SEMICOLON +static AstNode *ast_parse_var_decl(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdKeywordConst); + if (first == nullptr) + first = eat_token_if(pc, TokenIdKeywordVar); + if (first == nullptr) + return nullptr; - Token *comma = &pc->tokens->at(*token_index); + Token *identifier = expect_token(pc, TokenIdSymbol); + AstNode *type_expr = nullptr; + if (eat_token_if(pc, TokenIdColon) != nullptr) + type_expr = ast_expect(pc, ast_parse_type_expr); + + AstNode *align_expr = ast_parse_byte_align(pc); + AstNode *section_expr = ast_parse_section(pc); + AstNode *expr = nullptr; + if (eat_token_if(pc, TokenIdEq) != nullptr) + expr = ast_expect(pc, ast_parse_expr); + + expect_token(pc, TokenIdSemicolon); + + AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, first); + res->data.variable_declaration.is_const = first->id == TokenIdKeywordConst; + res->data.variable_declaration.symbol = token_buf(identifier); + res->data.variable_declaration.type = type_expr; + res->data.variable_declaration.align_expr = align_expr; + res->data.variable_declaration.section_expr = section_expr; + res->data.variable_declaration.expr = expr; + return res; +} + +// ContainerField <- IDENTIFIER (COLON TypeExpr)? (EQUAL Expr)? +static AstNode *ast_parse_container_field(ParseContext *pc) { + Token *identifier = eat_token_if(pc, TokenIdSymbol); + if (identifier == nullptr) + return nullptr; - if (comma->id == TokenIdComma) { - *token_index += 1; + AstNode *type_expr = nullptr; + if (eat_token_if(pc, TokenIdColon) != nullptr) + type_expr = ast_expect(pc, ast_parse_type_expr); + AstNode *expr = nullptr; + if (eat_token_if(pc, TokenIdEq) != nullptr) + expr = ast_expect(pc, ast_parse_expr); + + + AstNode *res = ast_create_node(pc, NodeTypeStructField, identifier); + res->data.struct_field.name = token_buf(identifier); + res->data.struct_field.type = type_expr; + res->data.struct_field.value = expr; + return res; +} + +// Statement +// <- KEYWORD_comptime? VarDecl +// / KEYWORD_comptime BlockExprStatement +// / KEYWORD_suspend (SEMICOLON / BlockExprStatement) +// / KEYWORD_defer BlockExprStatement +// / KEYWORD_errdefer BlockExprStatement +// / IfStatement +// / LabeledStatement +// / SwitchExpr +// / AssignExpr SEMICOLON +static AstNode *ast_parse_statement(ParseContext *pc) { + Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime); + AstNode *var_decl = ast_parse_var_decl(pc); + if (var_decl != nullptr) { + assert(var_decl->type == NodeTypeVariableDeclaration); + var_decl->data.variable_declaration.is_comptime = comptime != nullptr; + return var_decl; + } + + if (comptime != nullptr) { + AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); + AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); + res->data.comptime_expr.expr = statement; + return res; + } + + Token *suspend = eat_token_if(pc, TokenIdKeywordSuspend); + if (suspend != nullptr) { + AstNode *statement = nullptr; + if (eat_token_if(pc, TokenIdSemicolon) == nullptr) + statement = ast_expect(pc, ast_parse_block_expr_statement); + + AstNode *res = ast_create_node(pc, NodeTypeSuspend, suspend); + res->data.suspend.block = statement; + return res; + } + + Token *defer = eat_token_if(pc, TokenIdKeywordDefer); + if (defer == nullptr) + defer = eat_token_if(pc, TokenIdKeywordErrdefer); + if (defer != nullptr) { + AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); + AstNode *res = ast_create_node(pc, NodeTypeDefer, defer); + res->data.defer.kind = ReturnKindUnconditional; + res->data.defer.expr = statement; + if (defer->id == TokenIdKeywordErrdefer) + res->data.defer.kind = ReturnKindError; + return res; + } + + AstNode *if_statement = ast_parse_if_statement(pc); + if (if_statement != nullptr) + return if_statement; + + AstNode *labeled_statement = ast_parse_labeled_statement(pc); + if (labeled_statement != nullptr) + return labeled_statement; + + AstNode *switch_expr = ast_parse_switch_expr(pc); + if (switch_expr != nullptr) + return switch_expr; - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdColon || token->id == TokenIdRParen) { - break; - } else { - continue; - } - } else { - break; - } + AstNode *assign = ast_parse_assign_expr(pc); + if (assign != nullptr) { + expect_token(pc, TokenIdSemicolon); + return assign; } - ast_parse_asm_clobbers(pc, token_index, node); + return nullptr; } -/* -AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput) -*/ -static void ast_parse_asm_output(ParseContext *pc, size_t *token_index, AstNode *node) { - Token *colon_tok = &pc->tokens->at(*token_index); - - if (colon_tok->id != TokenIdColon) - return; - - *token_index += 1; +// IfStatement +// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? +// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) +static AstNode *ast_parse_if_statement(ParseContext *pc) { + AstNode *res = ast_parse_if_prefix(pc); + if (res == nullptr) + return nullptr; - Token *colon_again = &pc->tokens->at(*token_index); - if (colon_again->id == TokenIdColon) { - ast_parse_asm_input(pc, token_index, node); - return; + AstNode *body = ast_parse_block_expr(pc); + bool requires_semi = false; + if (body == nullptr) { + requires_semi = true; + body = ast_parse_assign_expr(pc); + } + + Token *err_payload = nullptr; + AstNode *else_body = nullptr; + if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { + err_payload = ast_parse_payload(pc); + else_body = ast_expect(pc, ast_parse_statement); + } + + if (requires_semi && else_body == nullptr) + expect_token(pc, TokenIdSemicolon); + + assert(res->type == NodeTypeTestExpr); + if (err_payload != nullptr) { + AstNodeTestExpr old = res->data.test_expr; + res->type = NodeTypeIfErrorExpr; + res->data.if_err_expr.target_node = old.target_node; + res->data.if_err_expr.var_is_ptr = old.var_is_ptr; + res->data.if_err_expr.var_symbol = old.var_symbol; + res->data.if_err_expr.then_node = body; + res->data.if_err_expr.err_symbol = token_buf(err_payload); + res->data.if_err_expr.else_node = else_body; + return res; + } + + if (res->data.test_expr.var_symbol != nullptr) { + res->data.test_expr.then_node = body; + res->data.test_expr.else_node = else_body; + return res; + } + + AstNodeTestExpr old = res->data.test_expr; + res->type = NodeTypeIfBoolExpr; + res->data.if_bool_expr.condition = old.target_node; + res->data.if_bool_expr.then_block = body; + res->data.if_bool_expr.else_node = else_body; + return res; +} + +// LabeledStatement <- BlockLabel? (Block / LoopStatement) +static AstNode *ast_parse_labeled_statement(ParseContext *pc) { + Token *label = ast_parse_block_label(pc); + AstNode *block = ast_parse_block(pc); + if (block != nullptr) { + assert(block->type == NodeTypeBlock); + block->data.block.name = token_buf(label); + return block; } - for (;;) { - ast_parse_asm_output_item(pc, token_index, node); - - Token *comma = &pc->tokens->at(*token_index); - - if (comma->id == TokenIdComma) { - *token_index += 1; - - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdColon || token->id == TokenIdRParen) { + AstNode *loop = ast_parse_loop_statement(pc); + if (loop != nullptr) { + switch (loop->type) { + case NodeTypeForExpr: + loop->data.for_expr.name = token_buf(label); break; - } else { - continue; - } - } else { - break; + case NodeTypeWhileExpr: + loop->data.while_expr.name = token_buf(label); + break; + default: + zig_unreachable(); } + return loop; } - ast_parse_asm_input(pc, token_index, node); + if (label != nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + return nullptr; } -/* -AsmExpression : token(Asm) option(token(Volatile)) token(LParen) token(String) option(AsmOutput) token(RParen) -*/ -static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *asm_token = &pc->tokens->at(*token_index); - - if (asm_token->id != TokenIdKeywordAsm) { - if (mandatory) { - ast_expect_token(pc, asm_token, TokenIdKeywordAsm); - } else { - return nullptr; - } - } - - AstNode *node = ast_create_node(pc, NodeTypeAsmExpr, asm_token); +// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) +static AstNode *ast_parse_loop_statement(ParseContext *pc) { + Token *label = ast_parse_block_label(pc); + Token *first = label; - *token_index += 1; - Token *lparen_tok = &pc->tokens->at(*token_index); + Token *inline_token = eat_token_if(pc, TokenIdKeywordInline); + if (first == nullptr) + first = inline_token; - if (lparen_tok->id == TokenIdKeywordVolatile) { - node->data.asm_expr.is_volatile = true; - - *token_index += 1; - lparen_tok = &pc->tokens->at(*token_index); + AstNode *for_statement = ast_parse_for_statement(pc); + if (for_statement != nullptr) { + assert(for_statement->type == NodeTypeForExpr); + for_statement->data.for_expr.name = token_buf(label); + for_statement->data.for_expr.is_inline = inline_token != nullptr; + return for_statement; } - ast_expect_token(pc, lparen_tok, TokenIdLParen); - *token_index += 1; - - Token *template_tok = ast_eat_token(pc, token_index, TokenIdStringLiteral); - - node->data.asm_expr.asm_template = token_buf(template_tok); - parse_asm_template(pc, node); - - ast_parse_asm_output(pc, token_index, node); - - ast_eat_token(pc, token_index, TokenIdRParen); - - return node; -} - -/* -SuspendExpression(body) = "suspend" option( body ) -*/ -static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *suspend_token = &pc->tokens->at(*token_index); - - if (suspend_token->id == TokenIdKeywordSuspend) { - *token_index += 1; - } else if (mandatory) { - ast_expect_token(pc, suspend_token, TokenIdKeywordSuspend); - zig_unreachable(); - } else { - return nullptr; + AstNode *while_statement = ast_parse_while_statement(pc); + if (while_statement != nullptr) { + assert(while_statement->type == NodeTypeWhileExpr); + while_statement->data.while_expr.name = token_buf(label); + while_statement->data.while_expr.is_inline = inline_token != nullptr; + return while_statement; } - Token *lbrace = &pc->tokens->at(*token_index); - if (lbrace->id == TokenIdLBrace) { - AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); - node->data.suspend.block = ast_parse_block(pc, token_index, true); - return node; - } else if (mandatory) { - ast_expect_token(pc, lbrace, TokenIdLBrace); - zig_unreachable(); - } else { - *token_index -= 1; - return nullptr; - } + if (first != nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + return nullptr; } -/* -CompTimeExpression(body) = "comptime" body -*/ -static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, bool require_block_body, bool mandatory) { - Token *comptime_token = &pc->tokens->at(*token_index); - if (comptime_token->id == TokenIdKeywordCompTime) { - *token_index += 1; - } else if (mandatory) { - ast_expect_token(pc, comptime_token, TokenIdKeywordCompTime); - zig_unreachable(); - } else { +// ForStatement +// <- ForPrefix BlockExpr ( KEYWORD_else Statement )? +// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) +static AstNode *ast_parse_for_statement(ParseContext *pc) { + AstNode *res = ast_parse_for_prefix(pc); + if (res == nullptr) return nullptr; - } - - AstNode *node = ast_create_node(pc, NodeTypeCompTime, comptime_token); - if (require_block_body) - node->data.comptime_expr.expr = ast_parse_block(pc, token_index, true); - else - node->data.comptime_expr.expr = ast_parse_block_or_expression(pc, token_index, true); - return node; -} -/* -PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType -KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "unreachable" | "suspend" -ErrorSetDecl = "error" Token(Dot) "{" list(Symbol, ",") "}" -*/ -static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *token = &pc->tokens->at(*token_index); - - if (token->id == TokenIdIntLiteral) { - AstNode *node = ast_create_node(pc, NodeTypeIntLiteral, token); - node->data.int_literal.bigint = token_bigint(token); - *token_index += 1; - return node; - } else if (token->id == TokenIdFloatLiteral) { - AstNode *node = ast_create_node(pc, NodeTypeFloatLiteral, token); - node->data.float_literal.bigfloat = token_bigfloat(token); - node->data.float_literal.overflow = token->data.float_lit.overflow; - *token_index += 1; - return node; - } else if (token->id == TokenIdStringLiteral) { - AstNode *node = ast_create_node(pc, NodeTypeStringLiteral, token); - node->data.string_literal.buf = token_buf(token); - node->data.string_literal.c = token->data.str_lit.is_c_str; - *token_index += 1; - return node; - } else if (token->id == TokenIdCharLiteral) { - AstNode *node = ast_create_node(pc, NodeTypeCharLiteral, token); - node->data.char_literal.value = token_char_lit(token); - *token_index += 1; - return node; - } else if (token->id == TokenIdKeywordTrue) { - AstNode *node = ast_create_node(pc, NodeTypeBoolLiteral, token); - node->data.bool_literal.value = true; - *token_index += 1; - return node; - } else if (token->id == TokenIdKeywordFalse) { - AstNode *node = ast_create_node(pc, NodeTypeBoolLiteral, token); - node->data.bool_literal.value = false; - *token_index += 1; - return node; - } else if (token->id == TokenIdKeywordNull) { - AstNode *node = ast_create_node(pc, NodeTypeNullLiteral, token); - *token_index += 1; - return node; - } else if (token->id == TokenIdKeywordContinue) { - AstNode *node = ast_create_node(pc, NodeTypeContinue, token); - *token_index += 1; - Token *maybe_colon_token = &pc->tokens->at(*token_index); - if (maybe_colon_token->id == TokenIdColon) { - *token_index += 1; - Token *name = ast_eat_token(pc, token_index, TokenIdSymbol); - node->data.continue_expr.name = token_buf(name); - } - return node; - } else if (token->id == TokenIdKeywordUndefined) { - AstNode *node = ast_create_node(pc, NodeTypeUndefinedLiteral, token); - *token_index += 1; - return node; - } else if (token->id == TokenIdKeywordUnreachable) { - AstNode *node = ast_create_node(pc, NodeTypeUnreachable, token); - *token_index += 1; - return node; - } else if (token->id == TokenIdKeywordSuspend) { - AstNode *node = ast_create_node(pc, NodeTypeSuspend, token); - *token_index += 1; - return node; - } else if (token->id == TokenIdKeywordPromise) { - AstNode *node = ast_create_node(pc, NodeTypePromiseType, token); - *token_index += 1; - Token *arrow_tok = &pc->tokens->at(*token_index); - if (arrow_tok->id == TokenIdArrow) { - *token_index += 1; - node->data.promise_type.payload_type = ast_parse_type_expr(pc, token_index, true); - } - return node; - } else if (token->id == TokenIdKeywordError) { - Token *dot_token = &pc->tokens->at(*token_index + 1); - Token *brace_token = &pc->tokens->at(*token_index + 2); - if (dot_token->id != TokenIdDot || brace_token->id != TokenIdLBrace) { - AstNode *node = ast_create_node(pc, NodeTypeErrorType, token); - *token_index += 1; - return node; - } - - AstNode *node = ast_create_node(pc, NodeTypeErrorSetDecl, token); - *token_index += 3; - for (;;) { - Token *item_tok = &pc->tokens->at(*token_index); - if (item_tok->id == TokenIdRBrace) { - *token_index += 1; - return node; - } else if (item_tok->id == TokenIdSymbol) { - AstNode *symbol_node = ast_parse_symbol(pc, token_index); - node->data.err_set_decl.decls.append(symbol_node); - Token *opt_comma_tok = &pc->tokens->at(*token_index); - if (opt_comma_tok->id == TokenIdComma) { - *token_index += 1; - } - } else { - ast_invalid_token_error(pc, item_tok); - } - } - } else if (token->id == TokenIdAtSign) { - *token_index += 1; - Token *name_tok = &pc->tokens->at(*token_index); - Buf *name_buf; - if (name_tok->id == TokenIdKeywordExport) { - name_buf = buf_create_from_str("export"); - *token_index += 1; - } else if (name_tok->id == TokenIdSymbol) { - name_buf = token_buf(name_tok); - *token_index += 1; - } else { - ast_expect_token(pc, name_tok, TokenIdSymbol); - zig_unreachable(); - } - - AstNode *name_node = ast_create_node(pc, NodeTypeSymbol, name_tok); - name_node->data.symbol_expr.symbol = name_buf; - - AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, token); - node->data.fn_call_expr.fn_ref_expr = name_node; - ast_eat_token(pc, token_index, TokenIdLParen); - ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params); - node->data.fn_call_expr.is_builtin = true; - - return node; + AstNode *body = ast_parse_block_expr(pc); + bool requires_semi = false; + if (body == nullptr) { + requires_semi = true; + body = ast_parse_assign_expr(pc); } - AstNode *block_expr_node = ast_parse_block_expr(pc, token_index, false); - if (block_expr_node) { - return block_expr_node; + AstNode *else_body = nullptr; + if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { + else_body = ast_expect(pc, ast_parse_statement); } - if (token->id == TokenIdSymbol) { - *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeSymbol, token); - node->data.symbol_expr.symbol = token_buf(token); - return node; - } + if (requires_semi && else_body == nullptr) + expect_token(pc, TokenIdSemicolon); - AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); - if (grouped_expr_node) { - return grouped_expr_node; - } + assert(res->type == NodeTypeForExpr); + res->data.for_expr.body = body; + res->data.for_expr.else_node = else_body; + return res; +} - AstNode *array_type_node = ast_parse_array_type_expr(pc, token_index, false); - if (array_type_node) { - return array_type_node; - } +// WhileStatement +// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? +// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) +static AstNode *ast_parse_while_statement(ParseContext *pc) { + AstNode *res = ast_parse_while_prefix(pc); + if (res == nullptr) + return nullptr; - AstNode *fn_proto_node = ast_parse_fn_proto(pc, token_index, false, VisibModPrivate); - if (fn_proto_node) { - return fn_proto_node; + AstNode *body = ast_parse_block_expr(pc); + bool requires_semi = false; + if (body == nullptr) { + requires_semi = true; + body = ast_parse_assign_expr(pc); } - AstNode *asm_expr = ast_parse_asm_expr(pc, token_index, false); - if (asm_expr) { - return asm_expr; + Token *err_payload = nullptr; + AstNode *else_body = nullptr; + if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { + err_payload = ast_parse_payload(pc); + else_body = ast_expect(pc, ast_parse_statement); } - AstNode *container_decl = ast_parse_container_decl(pc, token_index, false); - if (container_decl) - return container_decl; - - if (!mandatory) - return nullptr; + if (requires_semi && else_body == nullptr) + expect_token(pc, TokenIdSemicolon); - ast_invalid_token_error(pc, token); + assert(res->type == NodeTypeWhileExpr); + res->data.while_expr.body = body; + res->data.while_expr.err_symbol = token_buf(err_payload); + res->data.while_expr.else_node = else_body; + return res; } -/* -CurlySuffixExpression : PrefixOpExpression option(ContainerInitExpression) -ContainerInitExpression : token(Dot) token(LBrace) ContainerInitBody token(RBrace) -ContainerInitBody : list(StructLiteralField, token(Comma)) | list(Expression, token(Comma)) -*/ -static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, mandatory); - if (!prefix_op_expr) { - return nullptr; - } - - while (true) { - Token *first_token = &pc->tokens->at(*token_index); - Token *second_token = &pc->tokens->at(*token_index + 1); - if (first_token->id == TokenIdDot && second_token->id == TokenIdLBrace) { - *token_index += 2; - - AstNode *node = ast_create_node(pc, NodeTypeContainerInitExpr, first_token); - node->data.container_init_expr.type = prefix_op_expr; - - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdDot) { - node->data.container_init_expr.kind = ContainerInitKindStruct; - for (;;) { - if (token->id == TokenIdDot) { - ast_eat_token(pc, token_index, TokenIdDot); - Token *field_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); - ast_eat_token(pc, token_index, TokenIdEq); - - AstNode *field_node = ast_create_node(pc, NodeTypeStructValueField, token); - - field_node->data.struct_val_field.name = token_buf(field_name_tok); - field_node->data.struct_val_field.expr = ast_parse_expression(pc, token_index, true); - - node->data.container_init_expr.entries.append(field_node); - - Token *comma_tok = &pc->tokens->at(*token_index); - if (comma_tok->id == TokenIdComma) { - *token_index += 1; - token = &pc->tokens->at(*token_index); - continue; - } else if (comma_tok->id != TokenIdRBrace) { - ast_expect_token(pc, comma_tok, TokenIdRBrace); - } else { - *token_index += 1; - break; - } - } else if (token->id == TokenIdRBrace) { - *token_index += 1; - break; - } else { - ast_invalid_token_error(pc, token); - } - } - } else { - node->data.container_init_expr.kind = ContainerInitKindArray; - for (;;) { - if (token->id == TokenIdRBrace) { - *token_index += 1; - break; - } else { - AstNode *elem_node = ast_parse_expression(pc, token_index, true); - node->data.container_init_expr.entries.append(elem_node); - - Token *comma_tok = &pc->tokens->at(*token_index); - if (comma_tok->id == TokenIdComma) { - *token_index += 1; - token = &pc->tokens->at(*token_index); - continue; - } else if (comma_tok->id != TokenIdRBrace) { - ast_expect_token(pc, comma_tok, TokenIdRBrace); - } else { - *token_index += 1; - break; - } - } - } - } +// BlockExprStatement +// <- BlockExpr +// / AssignExpr SEMICOLON +static AstNode *ast_parse_block_expr_statement(ParseContext *pc) { + AstNode *block = ast_parse_block_expr(pc); + if (block != nullptr) + return block; - prefix_op_expr = node; - } else { - return prefix_op_expr; - } + AstNode *assign_expr = ast_parse_assign_expr(pc); + if (assign_expr != nullptr) { + expect_token(pc, TokenIdSemicolon); + return assign_expr; } -} -static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index, Token *fn_token, - AstNode *async_allocator_type_node, CallingConvention cc, bool is_extern, VisibMod visib_mod) -{ - AstNode *node = ast_create_node(pc, NodeTypeFnProto, fn_token); - node->data.fn_proto.visib_mod = visib_mod; - node->data.fn_proto.cc = cc; - node->data.fn_proto.is_extern = is_extern; - node->data.fn_proto.async_allocator_type = async_allocator_type_node; - - Token *fn_name = &pc->tokens->at(*token_index); + return nullptr; +} - if (fn_name->id == TokenIdSymbol) { - *token_index += 1; - node->data.fn_proto.name = token_buf(fn_name); - } else { - node->data.fn_proto.name = nullptr; +// BlockExpr <- BlockLabel? Block +static AstNode *ast_parse_block_expr(ParseContext *pc) { + Token *label = ast_parse_block_label(pc); + if (label != nullptr) { + AstNode *res = ast_expect(pc, ast_parse_block); + assert(res->type == NodeTypeBlock); + res->data.block.name = token_buf(label); + return res; } - ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args); - - Token *next_token = &pc->tokens->at(*token_index); - if (next_token->id == TokenIdKeywordAlign) { - *token_index += 1; - ast_eat_token(pc, token_index, TokenIdLParen); + return ast_parse_block(pc); +} - node->data.fn_proto.align_expr = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); - next_token = &pc->tokens->at(*token_index); - } - if (next_token->id == TokenIdKeywordSection) { - *token_index += 1; - ast_eat_token(pc, token_index, TokenIdLParen); +// AssignExpr <- Expr (AssignOp Expr)? +static AstNode *ast_parse_assign_expr(ParseContext *pc) { + return ast_parse_bin_op_expr(pc, BinOpChainOnce, ast_parse_assign_op, ast_parse_expr); +} - node->data.fn_proto.section_expr = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); - next_token = &pc->tokens->at(*token_index); - } - if (next_token->id == TokenIdKeywordVar) { - node->data.fn_proto.return_var_token = next_token; - *token_index += 1; - next_token = &pc->tokens->at(*token_index); - } else { - if (next_token->id == TokenIdKeywordError) { - Token *maybe_lbrace_tok = &pc->tokens->at(*token_index + 1); - if (maybe_lbrace_tok->id == TokenIdLBrace) { - *token_index += 1; - node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token); - return node; +// Expr <- KEYWORD_try* BoolOrExpr +static AstNode *ast_parse_expr(ParseContext *pc) { + return ast_parse_prefix_op_expr( + pc, + [](ParseContext *context) { + Token *try_token = eat_token_if(context, TokenIdKeywordTry); + if (try_token != nullptr) { + AstNode *res = ast_create_node(context, NodeTypeReturnExpr, try_token); + res->data.return_expr.kind = ReturnKindError; + return res; } - } else if (next_token->id == TokenIdBang) { - *token_index += 1; - node->data.fn_proto.auto_err_set = true; - next_token = &pc->tokens->at(*token_index); - } - node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true); - } - return node; + return (AstNode*)nullptr; + }, + ast_parse_bool_or_expr + ); } -/* -SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ".*" | ".?") -FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) -ArrayAccessExpression : token(LBracket) Expression token(RBracket) -SliceExpression = "[" Expression ".." option(Expression) "]" -FieldAccessExpression : token(Dot) token(Symbol) -StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression -*/ -static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *primary_expr; - - Token *async_token = &pc->tokens->at(*token_index); - if (async_token->id == TokenIdKeywordAsync) { - *token_index += 1; - - AstNode *allocator_expr_node = nullptr; - Token *async_lparen_tok = &pc->tokens->at(*token_index); - if (async_lparen_tok->id == TokenIdCmpLessThan) { - *token_index += 1; - allocator_expr_node = ast_parse_prefix_op_expr(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdCmpGreaterThan); - } - - Token *fncall_token = &pc->tokens->at(*token_index); - if (fncall_token->id == TokenIdKeywordFn) { - *token_index += 1; - return ast_parse_fn_proto_partial(pc, token_index, fncall_token, allocator_expr_node, CallingConventionAsync, - false, VisibModPrivate); - } - AstNode *node = ast_parse_suffix_op_expr(pc, token_index, true); - if (node->type != NodeTypeFnCallExpr) { - ast_error(pc, fncall_token, "expected function call, found '%s'", token_name(fncall_token->id)); - } - node->data.fn_call_expr.is_async = true; - node->data.fn_call_expr.async_allocator = allocator_expr_node; - assert(node->data.fn_call_expr.fn_ref_expr != nullptr); - - primary_expr = node; - } else { - primary_expr = ast_parse_primary_expr(pc, token_index, mandatory); - if (!primary_expr) - return nullptr; - } - - while (true) { - Token *first_token = &pc->tokens->at(*token_index); - if (first_token->id == TokenIdLParen) { - *token_index += 1; - - AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, first_token); - node->data.fn_call_expr.fn_ref_expr = primary_expr; - ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params); - - primary_expr = node; - } else if (first_token->id == TokenIdLBracket) { - *token_index += 1; - - AstNode *expr_node = ast_parse_expression(pc, token_index, true); - - Token *ellipsis_or_r_bracket = &pc->tokens->at(*token_index); - - if (ellipsis_or_r_bracket->id == TokenIdEllipsis2) { - *token_index += 1; - - AstNode *node = ast_create_node(pc, NodeTypeSliceExpr, first_token); - node->data.slice_expr.array_ref_expr = primary_expr; - node->data.slice_expr.start = expr_node; - node->data.slice_expr.end = ast_parse_expression(pc, token_index, false); - - ast_eat_token(pc, token_index, TokenIdRBracket); - - primary_expr = node; - } else if (ellipsis_or_r_bracket->id == TokenIdRBracket) { - *token_index += 1; - - AstNode *node = ast_create_node(pc, NodeTypeArrayAccessExpr, first_token); - node->data.array_access_expr.array_ref_expr = primary_expr; - node->data.array_access_expr.subscript = expr_node; - - primary_expr = node; - } else { - ast_invalid_token_error(pc, ellipsis_or_r_bracket); - } - } else if (first_token->id == TokenIdDot) { - Token *token = &pc->tokens->at(*token_index + 1); - - if (token->id == TokenIdSymbol) { - *token_index += 2; +// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)* +static AstNode *ast_parse_bool_or_expr(ParseContext *pc) { + return ast_parse_bin_op_expr( + pc, + BinOpChainInf, + ast_parse_bin_op_simple<TokenIdKeywordOr, BinOpTypeBoolOr>, + ast_parse_bool_and_expr + ); +} - AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, first_token); - node->data.field_access_expr.struct_expr = primary_expr; - node->data.field_access_expr.field_name = token_buf(token); +// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)* +static AstNode *ast_parse_bool_and_expr(ParseContext *pc) { + return ast_parse_bin_op_expr( + pc, + BinOpChainInf, + ast_parse_bin_op_simple<TokenIdKeywordAnd, BinOpTypeBoolAnd>, + ast_parse_compare_expr + ); +} - primary_expr = node; - } else if (token->id == TokenIdStar) { - *token_index += 2; +// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)? +static AstNode *ast_parse_compare_expr(ParseContext *pc) { + return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_compare_op, ast_parse_bitwise_expr); +} - AstNode *node = ast_create_node(pc, NodeTypePtrDeref, first_token); - node->data.ptr_deref_expr.target = primary_expr; +// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* +static AstNode *ast_parse_bitwise_expr(ParseContext *pc) { + return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_bitwise_op, ast_parse_bit_shit_expr); +} - primary_expr = node; - } else if (token->id == TokenIdQuestion) { - *token_index += 2; +// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)* +static AstNode *ast_parse_bit_shit_expr(ParseContext *pc) { + return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_bit_shift_op, ast_parse_addition_expr); +} - AstNode *node = ast_create_node(pc, NodeTypeUnwrapOptional, first_token); - node->data.unwrap_optional.expr = primary_expr; +// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)* +static AstNode *ast_parse_addition_expr(ParseContext *pc) { + return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_addition_op, ast_parse_multiply_expr); +} - primary_expr = node; - } else { - return primary_expr; - } - } else { - return primary_expr; - } - } +// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)* +static AstNode *ast_parse_multiply_expr(ParseContext *pc) { + return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_multiply_op, ast_parse_prefix_expr); } -static PrefixOp tok_to_prefix_op(Token *token) { - switch (token->id) { - case TokenIdBang: return PrefixOpBoolNot; - case TokenIdDash: return PrefixOpNegation; - case TokenIdMinusPercent: return PrefixOpNegationWrap; - case TokenIdTilde: return PrefixOpBinNot; - case TokenIdQuestion: return PrefixOpOptional; - case TokenIdAmpersand: return PrefixOpAddrOf; - default: return PrefixOpInvalid; - } +// PrefixExpr <- PrefixOp* PrimaryExpr +static AstNode *ast_parse_prefix_expr(ParseContext *pc) { + return ast_parse_prefix_op_expr( + pc, + ast_parse_prefix_op, + ast_parse_primary_expr + ); } -static AstNode *ast_parse_pointer_type(ParseContext *pc, size_t *token_index, Token *star_tok) { - AstNode *node = ast_create_node(pc, NodeTypePointerType, star_tok); - node->data.pointer_type.star_token = star_tok; +// PrimaryExpr +// <- AsmExpr +// / IfExpr +// / KEYWORD_break BreakLabel? Expr? +// / KEYWORD_cancel Expr +// / KEYWORD_comptime Expr +// / KEYWORD_continue BreakLabel? +// / KEYWORD_resume Expr +// / KEYWORD_return Expr? +// / LabeledExpr +// / CurlySuffixExpr +static AstNode *ast_parse_primary_expr(ParseContext *pc) { + AstNode *asm_expr = ast_parse_asm_expr(pc); + if (asm_expr != nullptr) + return asm_expr; - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdKeywordAlign) { - *token_index += 1; - ast_eat_token(pc, token_index, TokenIdLParen); - node->data.pointer_type.align_expr = ast_parse_expression(pc, token_index, true); + AstNode *if_expr = ast_parse_if_expr(pc); + if (if_expr != nullptr) + return if_expr; - token = &pc->tokens->at(*token_index); - if (token->id == TokenIdColon) { - *token_index += 1; - Token *bit_offset_start_tok = ast_eat_token(pc, token_index, TokenIdIntLiteral); - ast_eat_token(pc, token_index, TokenIdColon); - Token *host_int_bytes_tok = ast_eat_token(pc, token_index, TokenIdIntLiteral); + Token *break_token = eat_token_if(pc, TokenIdKeywordBreak); + if (break_token != nullptr) { + Token *label = ast_parse_break_label(pc); + AstNode *expr = ast_parse_expr(pc); - node->data.pointer_type.bit_offset_start = token_bigint(bit_offset_start_tok); - node->data.pointer_type.host_int_bytes = token_bigint(host_int_bytes_tok); - } - ast_eat_token(pc, token_index, TokenIdRParen); - token = &pc->tokens->at(*token_index); + AstNode *res = ast_create_node(pc, NodeTypeBreak, break_token); + res->data.break_expr.name = token_buf(label); + res->data.break_expr.expr = expr; + return res; } - if (token->id == TokenIdKeywordConst) { - *token_index += 1; - node->data.pointer_type.is_const = true; - token = &pc->tokens->at(*token_index); - } - if (token->id == TokenIdKeywordVolatile) { - *token_index += 1; - node->data.pointer_type.is_volatile = true; + Token *cancel = eat_token_if(pc, TokenIdKeywordCancel); + if (cancel != nullptr) { + AstNode *expr = ast_expect(pc, ast_parse_expr); + AstNode *res = ast_create_node(pc, NodeTypeCancel, cancel); + res->data.cancel_expr.expr = expr; + return res; } - node->data.pointer_type.op_expr = ast_parse_prefix_op_expr(pc, token_index, true); - return node; -} - -/* -PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression -PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" -*/ -static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdStar || token->id == TokenIdBracketStarBracket) { - *token_index += 1; - return ast_parse_pointer_type(pc, token_index, token); - } - if (token->id == TokenIdStarStar) { - *token_index += 1; - AstNode *child_node = ast_parse_pointer_type(pc, token_index, token); - child_node->column += 1; - AstNode *parent_node = ast_create_node(pc, NodeTypePointerType, token); - parent_node->data.pointer_type.star_token = token; - parent_node->data.pointer_type.op_expr = child_node; - return parent_node; + Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime); + if (comptime != nullptr) { + AstNode *expr = ast_expect(pc, ast_parse_expr); + AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); + res->data.comptime_expr.expr = expr; + return res; } - if (token->id == TokenIdKeywordTry) { - return ast_parse_try_expr(pc, token_index); - } - if (token->id == TokenIdKeywordAwait) { - return ast_parse_await_expr(pc, token_index); - } - PrefixOp prefix_op = tok_to_prefix_op(token); - if (prefix_op == PrefixOpInvalid) { - return ast_parse_suffix_op_expr(pc, token_index, mandatory); + + Token *continue_token = eat_token_if(pc, TokenIdKeywordContinue); + if (continue_token != nullptr) { + Token *label = ast_parse_break_label(pc); + AstNode *res = ast_create_node(pc, NodeTypeContinue, continue_token); + res->data.continue_expr.name = token_buf(label); + return res; } - *token_index += 1; + Token *resume = eat_token_if(pc, TokenIdKeywordResume); + if (resume != nullptr) { + AstNode *expr = ast_expect(pc, ast_parse_expr); + AstNode *res = ast_create_node(pc, NodeTypeResume, resume); + res->data.resume_expr.expr = expr; + return res; + } + Token *return_token = eat_token_if(pc, TokenIdKeywordReturn); + if (return_token != nullptr) { + AstNode *expr = ast_parse_expr(pc); + AstNode *res = ast_create_node(pc, NodeTypeReturnExpr, return_token); + res->data.return_expr.expr = expr; + return res; + } - AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token); + AstNode *labeled_expr = ast_parse_labeled_expr(pc); + if (labeled_expr != nullptr) + return labeled_expr; - AstNode *prefix_op_expr = ast_parse_error_set_expr(pc, token_index, true); - node->data.prefix_op_expr.primary_expr = prefix_op_expr; - node->data.prefix_op_expr.prefix_op = prefix_op; + AstNode *curly_suffix = ast_parse_curly_suffix_expr(pc); + if (curly_suffix != nullptr) + return curly_suffix; - return node; + return nullptr; } +// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? +static AstNode *ast_parse_if_expr(ParseContext *pc) { + return ast_parse_if_expr_helper(pc, ast_parse_expr); +} -static BinOpType tok_to_mult_op(Token *token) { - switch (token->id) { - case TokenIdStar: return BinOpTypeMult; - case TokenIdTimesPercent: return BinOpTypeMultWrap; - case TokenIdStarStar: return BinOpTypeArrayMult; - case TokenIdSlash: return BinOpTypeDiv; - case TokenIdPercent: return BinOpTypeMod; - case TokenIdBang: return BinOpTypeErrorUnion; - case TokenIdBarBar: return BinOpTypeMergeErrorSets; - default: return BinOpTypeInvalid; +// LabeledExpr <- BlockLabel? (Block / LoopExpr) +static AstNode *ast_parse_labeled_expr(ParseContext *pc) { + Token *label = ast_parse_block_label(pc); + AstNode *block = ast_parse_block(pc); + if (block != nullptr) { + assert(block->type == NodeTypeBlock); + block->data.block.name = token_buf(label); + return block; } -} -/* -MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%" -*/ -static BinOpType ast_parse_mult_op(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *token = &pc->tokens->at(*token_index); - BinOpType result = tok_to_mult_op(token); - if (result == BinOpTypeInvalid) { - if (mandatory) { - ast_invalid_token_error(pc, token); - } else { - return BinOpTypeInvalid; + AstNode *loop = ast_parse_loop_expr(pc); + if (loop != nullptr) { + switch (loop->type) { + case NodeTypeForExpr: + loop->data.for_expr.name = token_buf(label); + break; + case NodeTypeWhileExpr: + loop->data.while_expr.name = token_buf(label); + break; + default: + zig_unreachable(); } + return loop; } - *token_index += 1; - return result; + + if (label != nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + return nullptr; } -/* -MultiplyExpression : CurlySuffixExpression MultiplyOperator MultiplyExpression | CurlySuffixExpression -*/ -static AstNode *ast_parse_mult_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *operand_1 = ast_parse_curly_suffix_expr(pc, token_index, mandatory); - if (!operand_1) +// Block <- LBRACE Statement* RBRACE +static AstNode *ast_parse_block(ParseContext *pc) { + Token *lbrace = eat_token_if(pc, TokenIdLBrace); + if (lbrace == nullptr) return nullptr; - while (true) { - Token *token = &pc->tokens->at(*token_index); - BinOpType mult_op = ast_parse_mult_op(pc, token_index, false); - if (mult_op == BinOpTypeInvalid) - return operand_1; + ZigList<AstNode *> statements = {}; + AstNode *statement; + while ((statement = ast_parse_statement(pc)) != nullptr) + statements.append(statement); - AstNode *operand_2 = ast_parse_curly_suffix_expr(pc, token_index, true); + expect_token(pc, TokenIdRBrace); - AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); - node->data.bin_op_expr.op1 = operand_1; - node->data.bin_op_expr.bin_op = mult_op; - node->data.bin_op_expr.op2 = operand_2; + AstNode *res = ast_create_node(pc, NodeTypeBlock, lbrace); + res->data.block.statements = statements; + return res; +} - operand_1 = node; - } +// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr) +static AstNode *ast_parse_loop_expr(ParseContext *pc) { + return ast_parse_loop_expr_helper( + pc, + ast_parse_for_expr, + ast_parse_while_expr + ); } -static BinOpType tok_to_add_op(Token *token) { - switch (token->id) { - case TokenIdPlus: return BinOpTypeAdd; - case TokenIdPlusPercent: return BinOpTypeAddWrap; - case TokenIdDash: return BinOpTypeSub; - case TokenIdMinusPercent: return BinOpTypeSubWrap; - case TokenIdPlusPlus: return BinOpTypeArrayCat; - default: return BinOpTypeInvalid; - } +// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? +static AstNode *ast_parse_for_expr(ParseContext *pc) { + return ast_parse_for_expr_helper(pc, ast_parse_expr); } -/* -AdditionOperator = "+" | "-" | "++" | "+%" | "-%" -*/ -static BinOpType ast_parse_add_op(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *token = &pc->tokens->at(*token_index); - BinOpType result = tok_to_add_op(token); - if (result == BinOpTypeInvalid) { - if (mandatory) { - ast_invalid_token_error(pc, token); - } else { - return BinOpTypeInvalid; - } - } - *token_index += 1; - return result; +// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? +static AstNode *ast_parse_while_expr(ParseContext *pc) { + return ast_parse_while_expr_helper(pc, ast_parse_expr); } -/* -AdditionExpression : MultiplyExpression AdditionOperator AdditionExpression | MultiplyExpression -*/ -static AstNode *ast_parse_add_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *operand_1 = ast_parse_mult_expr(pc, token_index, mandatory); - if (!operand_1) +// CurlySuffixExpr <- TypeExpr InitList? +static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc) { + AstNode *type_expr = ast_parse_type_expr(pc); + if (type_expr == nullptr) return nullptr; - while (true) { - Token *token = &pc->tokens->at(*token_index); - BinOpType add_op = ast_parse_add_op(pc, token_index, false); - if (add_op == BinOpTypeInvalid) - return operand_1; - - AstNode *operand_2 = ast_parse_mult_expr(pc, token_index, true); + AstNode *res = ast_parse_init_list(pc); + if (res == nullptr) + return type_expr; - AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); - node->data.bin_op_expr.op1 = operand_1; - node->data.bin_op_expr.bin_op = add_op; - node->data.bin_op_expr.op2 = operand_2; - - operand_1 = node; - } -} - -static BinOpType tok_to_bit_shift_op(Token *token) { - switch (token->id) { - case TokenIdBitShiftLeft: return BinOpTypeBitShiftLeft; - case TokenIdBitShiftRight: return BinOpTypeBitShiftRight; - default: return BinOpTypeInvalid; - } + assert(res->type == NodeTypeContainerInitExpr); + res->data.container_init_expr.type = type_expr; + return res; } -/* -BitShiftOperator = "<<" | ">>" | "<<%" -*/ -static BinOpType ast_parse_bit_shift_op(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *token = &pc->tokens->at(*token_index); - BinOpType result = tok_to_bit_shift_op(token); - if (result == BinOpTypeInvalid) { - if (mandatory) { - ast_invalid_token_error(pc, token); - } else { - return BinOpTypeInvalid; - } - } - *token_index += 1; - return result; -} - -/* -BitShiftExpression : AdditionExpression BitShiftOperator BitShiftExpression | AdditionExpression -*/ -static AstNode *ast_parse_bit_shift_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *operand_1 = ast_parse_add_expr(pc, token_index, mandatory); - if (!operand_1) +// InitList +// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE +// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE +// / LBRACE RBRACE +static AstNode *ast_parse_init_list(ParseContext *pc) { + Token *lbrace = eat_token_if(pc, TokenIdLBrace); + if (lbrace == nullptr) return nullptr; - while (true) { - Token *token = &pc->tokens->at(*token_index); - BinOpType bit_shift_op = ast_parse_bit_shift_op(pc, token_index, false); - if (bit_shift_op == BinOpTypeInvalid) - return operand_1; - - AstNode *operand_2 = ast_parse_add_expr(pc, token_index, true); + AstNode *first = ast_parse_field_init(pc); + if (first != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeContainerInitExpr, lbrace); + res->data.container_init_expr.kind = ContainerInitKindStruct; + res->data.container_init_expr.entries.append(first); - AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); - node->data.bin_op_expr.op1 = operand_1; - node->data.bin_op_expr.bin_op = bit_shift_op; - node->data.bin_op_expr.op2 = operand_2; + while (eat_token_if(pc, TokenIdComma) != nullptr) { + AstNode *field_init = ast_parse_field_init(pc); + if (field_init == nullptr) + break; + res->data.container_init_expr.entries.append(field_init); + } - operand_1 = node; + expect_token(pc, TokenIdRBrace); + return res; } -} + AstNode *res = ast_create_node(pc, NodeTypeContainerInitExpr, lbrace); + res->data.container_init_expr.kind = ContainerInitKindArray; -/* -BinaryAndExpression : BitShiftExpression token(Ampersand) BinaryAndExpression | BitShiftExpression -*/ -static AstNode *ast_parse_bin_and_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *operand_1 = ast_parse_bit_shift_expr(pc, token_index, mandatory); - if (!operand_1) - return nullptr; + first = ast_parse_expr(pc); + if (first != nullptr) { + res->data.container_init_expr.entries.append(first); - while (true) { - Token *token = &pc->tokens->at(*token_index); - if (token->id != TokenIdAmpersand) - return operand_1; - *token_index += 1; + while (eat_token_if(pc, TokenIdComma) != nullptr) { + AstNode *expr = ast_parse_expr(pc); + if (expr == nullptr) + break; + res->data.container_init_expr.entries.append(expr); + } - AstNode *operand_2 = ast_parse_bit_shift_expr(pc, token_index, true); + expect_token(pc, TokenIdRBrace); + return res; + } - AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); - node->data.bin_op_expr.op1 = operand_1; - node->data.bin_op_expr.bin_op = BinOpTypeBinAnd; - node->data.bin_op_expr.op2 = operand_2; + expect_token(pc, TokenIdRBrace); + return res; +} - operand_1 = node; - } +// TypeExpr <- PrefixTypeOp* ErrorUnionExpr +static AstNode *ast_parse_type_expr(ParseContext *pc) { + return ast_parse_prefix_op_expr( + pc, + ast_parse_prefix_type_op, + ast_parse_error_union_expr + ); } -/* -BinaryXorExpression : BinaryAndExpression token(BinXor) BinaryXorExpression | BinaryAndExpression -*/ -static AstNode *ast_parse_bin_xor_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *operand_1 = ast_parse_bin_and_expr(pc, token_index, mandatory); - if (!operand_1) +// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? +static AstNode *ast_parse_error_union_expr(ParseContext *pc) { + AstNode *res = ast_parse_suffix_expr(pc); + if (res == nullptr) return nullptr; - while (true) { - Token *token = &pc->tokens->at(*token_index); - if (token->id != TokenIdBinXor) - return operand_1; - *token_index += 1; + AstNode *op = ast_parse_bin_op_simple<TokenIdBang, BinOpTypeErrorUnion>(pc); + if (op == nullptr) + return res; + + AstNode *right = ast_expect(pc, ast_parse_type_expr); + assert(op->type == NodeTypeBinOpExpr); + op->data.bin_op_expr.op1 = res; + op->data.bin_op_expr.op2 = right; + return op; +} + +// SuffixExpr +// <- AsyncPrefix PrimaryTypeExpr SuffixOp* FnCallArgumnets +// / PrimaryTypeExpr (SuffixOp / FnCallArgumnets)* +static AstNode *ast_parse_suffix_expr(ParseContext *pc) { + AstNode *async_call = ast_parse_async_prefix(pc); + if (async_call != nullptr) { + if (eat_token_if(pc, TokenIdKeywordFn) != nullptr) { + // HACK: If we see the keyword `fn`, then we assume that + // we are parsing an async fn proto, and not a call. + // We therefore put back all tokens consumed by the async + // prefix... + // HACK: This loop is not actually enough to put back all the + // tokens. Let's hope this is fine for most code right now + // and wait till we get the async rework for a syntax update. + do { + put_back_token(pc); + } while (peek_token(pc)->id != TokenIdKeywordAsync); + + return ast_parse_primary_type_expr(pc); + } + + AstNode *child = ast_expect(pc, ast_parse_primary_type_expr); + while (true) { + AstNode *suffix = ast_parse_suffix_op(pc); + if (suffix == nullptr) + break; - AstNode *operand_2 = ast_parse_bin_and_expr(pc, token_index, true); + switch (suffix->type) { + case NodeTypeSliceExpr: + suffix->data.slice_expr.array_ref_expr = child; + break; + case NodeTypeArrayAccessExpr: + suffix->data.array_access_expr.array_ref_expr = child; + break; + case NodeTypeFieldAccessExpr: + suffix->data.field_access_expr.struct_expr = child; + break; + case NodeTypeUnwrapOptional: + suffix->data.unwrap_optional.expr = child; + break; + case NodeTypePtrDeref: + suffix->data.ptr_deref_expr.target = child; + break; + default: + zig_unreachable(); + } + child = suffix; + } - AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); - node->data.bin_op_expr.op1 = operand_1; - node->data.bin_op_expr.bin_op = BinOpTypeBinXor; - node->data.bin_op_expr.op2 = operand_2; + // TODO: Both *_async_prefix and *_fn_call_argumnets returns an + // AstNode *. All we really want here is the arguments of + // the call we parse. We therefor "leak" the node for now. + // Wait till we get async rework to fix this. + AstNode *args = ast_parse_fn_call_argumnets(pc); + if (args == nullptr) + ast_invalid_token_error(pc, peek_token(pc)); - operand_1 = node; + assert(args->type == NodeTypeFnCallExpr); + async_call->data.fn_call_expr.fn_ref_expr = child; + async_call->data.fn_call_expr.params = args->data.fn_call_expr.params; + async_call->data.fn_call_expr.is_builtin = false; + return async_call; } -} -/* -BinaryOrExpression : BinaryXorExpression token(BinOr) BinaryOrExpression | BinaryXorExpression -*/ -static AstNode *ast_parse_bin_or_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *operand_1 = ast_parse_bin_xor_expr(pc, token_index, mandatory); - if (!operand_1) + AstNode *res = ast_parse_primary_type_expr(pc); + if (res == nullptr) return nullptr; while (true) { - Token *token = &pc->tokens->at(*token_index); - if (token->id != TokenIdBinOr) - return operand_1; - *token_index += 1; + AstNode *suffix = ast_parse_suffix_op(pc); + if (suffix != nullptr) { + switch (suffix->type) { + case NodeTypeSliceExpr: + suffix->data.slice_expr.array_ref_expr = res; + break; + case NodeTypeArrayAccessExpr: + suffix->data.array_access_expr.array_ref_expr = res; + break; + case NodeTypeFieldAccessExpr: + suffix->data.field_access_expr.struct_expr = res; + break; + case NodeTypeUnwrapOptional: + suffix->data.unwrap_optional.expr = res; + break; + case NodeTypePtrDeref: + suffix->data.ptr_deref_expr.target = res; + break; + default: + zig_unreachable(); + } + res = suffix; + continue; + } - AstNode *operand_2 = ast_parse_bin_xor_expr(pc, token_index, true); + AstNode * call = ast_parse_fn_call_argumnets(pc); + if (call != nullptr) { + assert(call->type == NodeTypeFnCallExpr); + call->data.fn_call_expr.fn_ref_expr = res; + res = call; + continue; + } - AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); - node->data.bin_op_expr.op1 = operand_1; - node->data.bin_op_expr.bin_op = BinOpTypeBinOr; - node->data.bin_op_expr.op2 = operand_2; + break; + } + + return res; + +} + +// PrimaryTypeExpr +// <- BUILTININDENTIFIER FnCallArgumnets +// / CHAR_LITERAL +// / ContainerDecl +// / ErrorSetDecl +// / FLOAT +// / FnProto +// / GroupedExpr +// / LabeledTypeExpr +// / IDENTIFIER +// / IfTypeExpr +// / INTEGER +// / KEYWORD_anyerror +// / KEYWORD_comptime TypeExpr +// / KEYWORD_error DOT IDENTIFIER +// / KEYWORD_false +// / KEYWORD_null +// / KEYWORD_promise +// / KEYWORD_true +// / KEYWORD_undefined +// / KEYWORD_unreachable +// / STRINGLITERAL +// / SwitchExpr +static AstNode *ast_parse_primary_type_expr(ParseContext *pc) { + // TODO: This is not in line with the grammar. + // Because the prev stage 1 tokenizer does not parse + // @[a-zA-Z_][a-zA-Z0-9_] as one token, it has to do a + // hack, where it accepts '@' (IDENTIFIER / KEYWORD_export). + // I'd say that it's better if '@' is part of the builtin + // identifier token. + Token *at_sign = eat_token_if(pc, TokenIdAtSign); + if (at_sign != nullptr) { + Buf *name; + Token *token = eat_token_if(pc, TokenIdKeywordExport); + if (token == nullptr) { + token = expect_token(pc, TokenIdSymbol); + name = token_buf(token); + } else { + name = buf_create_from_str("export"); + } - operand_1 = node; - } -} + AstNode *res = ast_expect(pc, ast_parse_fn_call_argumnets); + AstNode *name_sym = ast_create_node(pc, NodeTypeSymbol, token); + name_sym->data.symbol_expr.symbol = name; -static BinOpType tok_to_cmp_op(Token *token) { - switch (token->id) { - case TokenIdCmpEq: return BinOpTypeCmpEq; - case TokenIdCmpNotEq: return BinOpTypeCmpNotEq; - case TokenIdCmpLessThan: return BinOpTypeCmpLessThan; - case TokenIdCmpGreaterThan: return BinOpTypeCmpGreaterThan; - case TokenIdCmpLessOrEq: return BinOpTypeCmpLessOrEq; - case TokenIdCmpGreaterOrEq: return BinOpTypeCmpGreaterOrEq; - default: return BinOpTypeInvalid; + assert(res->type == NodeTypeFnCallExpr); + res->line = at_sign->start_line; + res->column = at_sign->start_column; + res->data.fn_call_expr.fn_ref_expr = name_sym; + res->data.fn_call_expr.is_builtin = true; + return res; } -} -static BinOpType ast_parse_comparison_operator(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *token = &pc->tokens->at(*token_index); - BinOpType result = tok_to_cmp_op(token); - if (result == BinOpTypeInvalid) { - if (mandatory) { - ast_invalid_token_error(pc, token); - } else { - return BinOpTypeInvalid; - } + Token *char_lit = eat_token_if(pc, TokenIdCharLiteral); + if (char_lit != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeCharLiteral, char_lit); + res->data.char_literal.value = char_lit->data.char_lit.c; + return res; } - *token_index += 1; - return result; -} -/* -ComparisonExpression : BinaryOrExpression ComparisonOperator BinaryOrExpression | BinaryOrExpression -*/ -static AstNode *ast_parse_comparison_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *operand_1 = ast_parse_bin_or_expr(pc, token_index, mandatory); - if (!operand_1) - return nullptr; - - Token *token = &pc->tokens->at(*token_index); - BinOpType cmp_op = ast_parse_comparison_operator(pc, token_index, false); - if (cmp_op == BinOpTypeInvalid) - return operand_1; - - AstNode *operand_2 = ast_parse_bin_or_expr(pc, token_index, true); + AstNode *container_decl = ast_parse_container_decl(pc); + if (container_decl != nullptr) + return container_decl; - AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); - node->data.bin_op_expr.op1 = operand_1; - node->data.bin_op_expr.bin_op = cmp_op; - node->data.bin_op_expr.op2 = operand_2; + AstNode *error_set_decl = ast_parse_error_set_decl(pc); + if (error_set_decl != nullptr) + return error_set_decl; - return node; -} + Token *float_lit = eat_token_if(pc, TokenIdFloatLiteral); + if (float_lit != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeFloatLiteral, float_lit); + res->data.float_literal.bigfloat = &float_lit->data.float_lit.bigfloat; + res->data.float_literal.overflow = float_lit->data.float_lit.overflow; + return res; + } -/* -BoolAndExpression = ComparisonExpression "and" BoolAndExpression | ComparisonExpression - */ -static AstNode *ast_parse_bool_and_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *operand_1 = ast_parse_comparison_expr(pc, token_index, mandatory); - if (!operand_1) - return nullptr; + AstNode *fn_proto = ast_parse_fn_proto(pc); + if (fn_proto != nullptr) + return fn_proto; - while (true) { - Token *token = &pc->tokens->at(*token_index); - if (token->id != TokenIdKeywordAnd) - return operand_1; - *token_index += 1; + AstNode *grouped_expr = ast_parse_grouped_expr(pc); + if (grouped_expr != nullptr) + return grouped_expr; - AstNode *operand_2 = ast_parse_comparison_expr(pc, token_index, true); + AstNode *labeled_type_expr = ast_parse_labeled_type_expr(pc); + if (labeled_type_expr != nullptr) + return labeled_type_expr; - AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); - node->data.bin_op_expr.op1 = operand_1; - node->data.bin_op_expr.bin_op = BinOpTypeBoolAnd; - node->data.bin_op_expr.op2 = operand_2; + Token *identifier = eat_token_if(pc, TokenIdSymbol); + if (identifier != nullptr) + return token_symbol(pc, identifier); - operand_1 = node; - } -} + AstNode *if_type_expr = ast_parse_if_type_expr(pc); + if (if_type_expr != nullptr) + return if_type_expr; -/* -IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) -TryExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body) -TestExpression(body) = "if" "(" Expression ")" "|" option("*") Symbol "|" body option("else" BlockExpression(body)) -*/ -static AstNode *ast_parse_if_try_test_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *if_token = &pc->tokens->at(*token_index); - - if (if_token->id == TokenIdKeywordIf) { - *token_index += 1; - } else if (mandatory) { - ast_expect_token(pc, if_token, TokenIdKeywordIf); - zig_unreachable(); - } else { - return nullptr; + Token *int_lit = eat_token_if(pc, TokenIdIntLiteral); + if (int_lit != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeIntLiteral, int_lit); + res->data.int_literal.bigint = &int_lit->data.int_lit.bigint; + return res; } - ast_eat_token(pc, token_index, TokenIdLParen); - AstNode *condition = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); - - Token *open_bar_tok = &pc->tokens->at(*token_index); - Token *var_name_tok = nullptr; - bool var_is_ptr = false; - if (open_bar_tok->id == TokenIdBinOr) { - *token_index += 1; + Token *error_type = eat_token_if(pc, TokenIdKeywordAnyerror); + if (error_type != nullptr) + return ast_create_node(pc, NodeTypeErrorType, error_type); - Token *star_tok = &pc->tokens->at(*token_index); - if (star_tok->id == TokenIdStar) { - *token_index += 1; - var_is_ptr = true; - } + Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime); + if (comptime != nullptr) { + AstNode *expr = ast_expect(pc, ast_parse_type_expr); + AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); + res->data.comptime_expr.expr = expr; + return res; + } - var_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); + Token *error = eat_token_if(pc, TokenIdKeywordError); + if (error != nullptr) { + Token *dot = expect_token(pc, TokenIdDot); + Token *name = expect_token(pc, TokenIdSymbol); + AstNode *left = ast_create_node(pc, NodeTypeErrorType, error); + AstNode *res = ast_create_node(pc, NodeTypeFieldAccessExpr, dot); + res->data.field_access_expr.struct_expr = left; + res->data.field_access_expr.field_name = token_buf(name); + return res; + } - ast_eat_token(pc, token_index, TokenIdBinOr); + Token *false_token = eat_token_if(pc, TokenIdKeywordFalse); + if (false_token != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeBoolLiteral, false_token); + res->data.bool_literal.value = false; + return res; } - AstNode *body_node = ast_parse_block_or_expression(pc, token_index, true); + Token *null = eat_token_if(pc, TokenIdKeywordNull); + if (null != nullptr) + return ast_create_node(pc, NodeTypeNullLiteral, null); - Token *else_tok = &pc->tokens->at(*token_index); - AstNode *else_node = nullptr; - Token *err_name_tok = nullptr; - if (else_tok->id == TokenIdKeywordElse) { - *token_index += 1; + Token *promise = eat_token_if(pc, TokenIdKeywordPromise); + if (promise != nullptr) + return ast_create_node(pc, NodeTypePromiseType, promise); - Token *else_bar_tok = &pc->tokens->at(*token_index); - if (else_bar_tok->id == TokenIdBinOr) { - *token_index += 1; + Token *true_token = eat_token_if(pc, TokenIdKeywordTrue); + if (true_token != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeBoolLiteral, true_token); + res->data.bool_literal.value = true; + return res; + } - err_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); + Token *undefined = eat_token_if(pc, TokenIdKeywordUndefined); + if (undefined != nullptr) + return ast_create_node(pc, NodeTypeUndefinedLiteral, undefined); - ast_eat_token(pc, token_index, TokenIdBinOr); - } + Token *unreachable = eat_token_if(pc, TokenIdKeywordUnreachable); + if (unreachable != nullptr) + return ast_create_node(pc, NodeTypeUnreachable, unreachable); - else_node = ast_parse_block_expr_or_expression(pc, token_index, true); + Token *string_lit = eat_token_if(pc, TokenIdStringLiteral); + if (string_lit != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeStringLiteral, string_lit); + res->data.string_literal.buf = token_buf(string_lit); + res->data.string_literal.c = string_lit->data.str_lit.is_c_str; + return res; } - if (err_name_tok != nullptr) { - AstNode *node = ast_create_node(pc, NodeTypeIfErrorExpr, if_token); - node->data.if_err_expr.target_node = condition; - node->data.if_err_expr.var_is_ptr = var_is_ptr; - if (var_name_tok != nullptr) { - node->data.if_err_expr.var_symbol = token_buf(var_name_tok); - } - node->data.if_err_expr.then_node = body_node; - node->data.if_err_expr.err_symbol = token_buf(err_name_tok); - node->data.if_err_expr.else_node = else_node; - return node; - } else if (var_name_tok != nullptr) { - AstNode *node = ast_create_node(pc, NodeTypeTestExpr, if_token); - node->data.test_expr.target_node = condition; - node->data.test_expr.var_is_ptr = var_is_ptr; - node->data.test_expr.var_symbol = token_buf(var_name_tok); - node->data.test_expr.then_node = body_node; - node->data.test_expr.else_node = else_node; - return node; - } else { - AstNode *node = ast_create_node(pc, NodeTypeIfBoolExpr, if_token); - node->data.if_bool_expr.condition = condition; - node->data.if_bool_expr.then_block = body_node; - node->data.if_bool_expr.else_node = else_node; - return node; - } + AstNode *switch_expr = ast_parse_switch_expr(pc); + if (switch_expr != nullptr) + return switch_expr; + + return nullptr; } -/* -ReturnExpression : "return" option(Expression) -*/ -static AstNode *ast_parse_return_expr(ParseContext *pc, size_t *token_index) { - Token *token = &pc->tokens->at(*token_index); +// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto +static AstNode *ast_parse_container_decl(ParseContext *pc) { + Token *extern_token = eat_token_if(pc, TokenIdKeywordExtern); + if (extern_token != nullptr) { + AstNode *res = ast_parse_container_decl_auto(pc); + if (res == nullptr) { + put_back_token(pc); + return nullptr; + } - if (token->id != TokenIdKeywordReturn) { - return nullptr; + assert(res->type == NodeTypeContainerDecl); + res->line = extern_token->start_line; + res->column = extern_token->start_column; + res->data.container_decl.layout = ContainerLayoutExtern; + return res; } - *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, token); - node->data.return_expr.kind = ReturnKindUnconditional; - node->data.return_expr.expr = ast_parse_expression(pc, token_index, false); + Token *packed_token = eat_token_if(pc, TokenIdKeywordPacked); + if (packed_token != nullptr) { + AstNode *res = ast_expect(pc, ast_parse_container_decl_auto); + assert(res->type == NodeTypeContainerDecl); + res->line = packed_token->start_line; + res->column = packed_token->start_column; + res->data.container_decl.layout = ContainerLayoutPacked; + return res; + } - return node; + return ast_parse_container_decl_auto(pc); } -/* -TryExpression : "try" Expression -*/ -static AstNode *ast_parse_try_expr(ParseContext *pc, size_t *token_index) { - Token *token = &pc->tokens->at(*token_index); - - if (token->id != TokenIdKeywordTry) { +// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE +static AstNode *ast_parse_error_set_decl(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdKeywordError); + if (first == nullptr) + return nullptr; + if (eat_token_if(pc, TokenIdLBrace) == nullptr) { + put_back_token(pc); return nullptr; } - *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, token); - node->data.return_expr.kind = ReturnKindError; - node->data.return_expr.expr = ast_parse_expression(pc, token_index, true); + ZigList<AstNode *> decls = ast_parse_list<AstNode>(pc, TokenIdComma, [](ParseContext *context) { + Token *ident = eat_token_if(context, TokenIdSymbol); + if (ident == nullptr) + return (AstNode*)nullptr; - return node; -} + return token_symbol(context, ident); + }); + expect_token(pc, TokenIdRBrace); -/* -AwaitExpression : "await" Expression -*/ -static AstNode *ast_parse_await_expr(ParseContext *pc, size_t *token_index) { - Token *token = &pc->tokens->at(*token_index); + AstNode *res = ast_create_node(pc, NodeTypeErrorSetDecl, first); + res->data.err_set_decl.decls = decls; + return res; +} - if (token->id != TokenIdKeywordAwait) { +// GroupedExpr <- LPAREN Expr RPAREN +static AstNode *ast_parse_grouped_expr(ParseContext *pc) { + Token *lparen = eat_token_if(pc, TokenIdLParen); + if (lparen == nullptr) return nullptr; - } - *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeAwaitExpr, token); - node->data.await_expr.expr = ast_parse_expression(pc, token_index, true); + AstNode *expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); - return node; + AstNode *res = ast_create_node(pc, NodeTypeGroupedExpr, lparen); + res->data.grouped_expr = expr; + return res; } -/* -BreakExpression = "break" option(":" Symbol) option(Expression) -*/ -static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) { - Token *token = &pc->tokens->at(*token_index); +// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? +static AstNode *ast_parse_if_type_expr(ParseContext *pc) { + return ast_parse_if_expr_helper(pc, ast_parse_type_expr); +} - if (token->id == TokenIdKeywordBreak) { - *token_index += 1; - } else { - return nullptr; +// LabeledTypeExpr +// <- BlockLabel Block +// / BlockLabel? LoopTypeExpr +static AstNode *ast_parse_labeled_type_expr(ParseContext *pc) { + Token *label = ast_parse_block_label(pc); + if (label != nullptr) { + AstNode *block = ast_parse_block(pc); + if (block != nullptr) { + assert(block->type == NodeTypeBlock); + block->data.block.name = token_buf(label); + return block; + } } - AstNode *node = ast_create_node(pc, NodeTypeBreak, token); - Token *maybe_colon_token = &pc->tokens->at(*token_index); - if (maybe_colon_token->id == TokenIdColon) { - *token_index += 1; - Token *name = ast_eat_token(pc, token_index, TokenIdSymbol); - node->data.break_expr.name = token_buf(name); + AstNode *loop = ast_parse_loop_type_expr(pc); + if (loop != nullptr) { + switch (loop->type) { + case NodeTypeForExpr: + loop->data.for_expr.name = token_buf(label); + break; + case NodeTypeWhileExpr: + loop->data.while_expr.name = token_buf(label); + break; + default: + zig_unreachable(); + } + return loop; } - node->data.break_expr.expr = ast_parse_expression(pc, token_index, false); + if (label != nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + return nullptr; +} - return node; +// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) +static AstNode *ast_parse_loop_type_expr(ParseContext *pc) { + return ast_parse_loop_expr_helper( + pc, + ast_parse_for_type_expr, + ast_parse_while_type_expr + ); } -/* -CancelExpression = "cancel" Expression; -*/ -static AstNode *ast_parse_cancel_expr(ParseContext *pc, size_t *token_index) { - Token *token = &pc->tokens->at(*token_index); +// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? +static AstNode *ast_parse_for_type_expr(ParseContext *pc) { + return ast_parse_for_expr_helper(pc, ast_parse_type_expr); +} - if (token->id != TokenIdKeywordCancel) { - return nullptr; - } - *token_index += 1; +// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? +static AstNode *ast_parse_while_type_expr(ParseContext *pc) { + return ast_parse_while_expr_helper(pc, ast_parse_type_expr); +} - AstNode *node = ast_create_node(pc, NodeTypeCancel, token); +// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE +static AstNode *ast_parse_switch_expr(ParseContext *pc) { + Token *switch_token = eat_token_if(pc, TokenIdKeywordSwitch); + if (switch_token == nullptr) + return nullptr; - node->data.cancel_expr.expr = ast_parse_expression(pc, token_index, false); + expect_token(pc, TokenIdLParen); + AstNode *expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + expect_token(pc, TokenIdLBrace); + ZigList<AstNode *> prongs = ast_parse_list(pc, TokenIdComma, ast_parse_switch_prong); + expect_token(pc, TokenIdRBrace); - return node; + AstNode *res = ast_create_node(pc, NodeTypeSwitchExpr, switch_token); + res->data.switch_expr.expr = expr; + res->data.switch_expr.prongs = prongs; + return res; } -/* -ResumeExpression = "resume" Expression; -*/ -static AstNode *ast_parse_resume_expr(ParseContext *pc, size_t *token_index) { - Token *token = &pc->tokens->at(*token_index); - - if (token->id != TokenIdKeywordResume) { +// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN STRINGLITERAL AsmOutput? RPAREN +static AstNode *ast_parse_asm_expr(ParseContext *pc) { + Token *asm_token = eat_token_if(pc, TokenIdKeywordAsm); + if (asm_token == nullptr) return nullptr; - } - *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeResume, token); + Token *volatile_token = eat_token_if(pc, TokenIdKeywordVolatile); + expect_token(pc, TokenIdLParen); + Token *asm_template = expect_token(pc, TokenIdStringLiteral); + AstNode *res = ast_parse_asm_output(pc); + if (res == nullptr) + res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); + expect_token(pc, TokenIdRParen); + + res->line = asm_token->start_line; + res->column = asm_token->start_column; + res->data.asm_expr.is_volatile = volatile_token != nullptr; + res->data.asm_expr.asm_template = token_buf(asm_template); + ast_parse_asm_template(pc, res); + return res; +} + +// AsmOutput <- COLON AsmOutputList AsmInput? +static AstNode *ast_parse_asm_output(ParseContext *pc) { + if (eat_token_if(pc, TokenIdColon) == nullptr) + return nullptr; - node->data.resume_expr.expr = ast_parse_expression(pc, token_index, false); + ZigList<AsmOutput *> output_list = ast_parse_list(pc, TokenIdComma, ast_parse_asm_output_item); + AstNode *res = ast_parse_asm_input(pc); + if (res == nullptr) + res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); - return node; + res->data.asm_expr.output_list = output_list; + return res; } -/* -Defer(body) = ("defer" | "errdefer") body -*/ -static AstNode *ast_parse_defer_expr(ParseContext *pc, size_t *token_index) { - Token *token = &pc->tokens->at(*token_index); - - NodeType node_type; - ReturnKind kind; - - if (token->id == TokenIdKeywordErrdefer) { - kind = ReturnKindError; - node_type = NodeTypeDefer; - *token_index += 1; - } else if (token->id == TokenIdKeywordDefer) { - kind = ReturnKindUnconditional; - node_type = NodeTypeDefer; - *token_index += 1; - } else { +// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN +static AsmOutput *ast_parse_asm_output_item(ParseContext *pc) { + if (eat_token_if(pc, TokenIdLBracket) == nullptr) return nullptr; + + Token *sym_name = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdRBracket); + + Token *str = expect_token(pc, TokenIdStringLiteral); + expect_token(pc, TokenIdLParen); + + Token *var_name = eat_token_if(pc, TokenIdSymbol); + AstNode *return_type = nullptr; + if (var_name == nullptr) { + expect_token(pc, TokenIdArrow); + return_type = ast_expect(pc, ast_parse_type_expr); } - AstNode *node = ast_create_node(pc, node_type, token); - node->data.defer.kind = kind; - node->data.defer.expr = ast_parse_block_or_expression(pc, token_index, true); + expect_token(pc, TokenIdRParen); - return node; + AsmOutput *res = allocate<AsmOutput>(1); + res->asm_symbolic_name = token_buf(sym_name); + res->constraint = token_buf(str); + res->variable_name = token_buf(var_name); + res->return_type = return_type; + return res; } -/* -VariableDeclaration = ("var" | "const") Symbol option(":" TypeExpr) option("align" "(" Expression ")") "=" Expression -*/ -static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *token_index, bool mandatory, - VisibMod visib_mod, bool is_comptime, bool is_export) -{ - Token *first_token = &pc->tokens->at(*token_index); - Token *var_token; - - bool is_const; - if (first_token->id == TokenIdKeywordVar) { - is_const = false; - var_token = first_token; - *token_index += 1; - } else if (first_token->id == TokenIdKeywordConst) { - is_const = true; - var_token = first_token; - *token_index += 1; - } else if (mandatory) { - ast_invalid_token_error(pc, first_token); - } else { +// AsmInput <- COLON AsmInputList AsmCloppers? +static AstNode *ast_parse_asm_input(ParseContext *pc) { + if (eat_token_if(pc, TokenIdColon) == nullptr) return nullptr; - } - AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, var_token); + ZigList<AsmInput *> input_list = ast_parse_list(pc, TokenIdComma, ast_parse_asm_input_item); + AstNode *res = ast_parse_asm_cloppers(pc); + if (res == nullptr) + res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); - node->data.variable_declaration.is_comptime = is_comptime; - node->data.variable_declaration.is_export = is_export; - node->data.variable_declaration.is_const = is_const; - node->data.variable_declaration.visib_mod = visib_mod; + res->data.asm_expr.input_list = input_list; + return res; +} - Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); - node->data.variable_declaration.symbol = token_buf(name_token); +// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN +static AsmInput *ast_parse_asm_input_item(ParseContext *pc) { + if (eat_token_if(pc, TokenIdLBracket) == nullptr) + return nullptr; - Token *next_token = &pc->tokens->at(*token_index); + Token *sym_name = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdRBracket); + Token *constraint = expect_token(pc, TokenIdStringLiteral); + expect_token(pc, TokenIdLParen); + AstNode *expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); - if (next_token->id == TokenIdColon) { - *token_index += 1; - node->data.variable_declaration.type = ast_parse_type_expr(pc, token_index, true); - next_token = &pc->tokens->at(*token_index); - } + AsmInput *res = allocate<AsmInput>(1); + res->asm_symbolic_name = token_buf(sym_name); + res->constraint = token_buf(constraint); + res->expr = expr; + return res; +} - if (next_token->id == TokenIdKeywordAlign) { - *token_index += 1; - ast_eat_token(pc, token_index, TokenIdLParen); - node->data.variable_declaration.align_expr = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); - next_token = &pc->tokens->at(*token_index); - } +// AsmCloppers <- COLON StringList +static AstNode *ast_parse_asm_cloppers(ParseContext *pc) { + if (eat_token_if(pc, TokenIdColon) == nullptr) + return nullptr; - if (next_token->id == TokenIdKeywordSection) { - *token_index += 1; - ast_eat_token(pc, token_index, TokenIdLParen); - node->data.variable_declaration.section_expr = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); - next_token = &pc->tokens->at(*token_index); - } + ZigList<Buf *> clobber_list = ast_parse_list<Buf>(pc, TokenIdComma, [](ParseContext *context) { + Token *str = eat_token_if(context, TokenIdStringLiteral); + if (str != nullptr) + return token_buf(str); + return (Buf*)nullptr; + }); - if (next_token->id == TokenIdEq) { - *token_index += 1; - node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true); - next_token = &pc->tokens->at(*token_index); - } + AstNode *res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); + res->data.asm_expr.clobber_list = clobber_list; + return res; +} - // peek ahead and ensure that all variable declarations are followed by a semicolon - ast_expect_token(pc, next_token, TokenIdSemicolon); +// BreakLabel <- COLON IDENTIFIER +static Token *ast_parse_break_label(ParseContext *pc) { + if (eat_token_if(pc, TokenIdColon) == nullptr) + return nullptr; - return node; + return expect_token(pc, TokenIdSymbol); } -/* -GlobalVarDecl = option("export") VariableDeclaration ";" -*/ -static AstNode *ast_parse_global_var_decl(ParseContext *pc, size_t *token_index, VisibMod visib_mod) { - Token *first_token = &pc->tokens->at(*token_index); - - bool is_export = false;; - if (first_token->id == TokenIdKeywordExport) { - *token_index += 1; - is_export = true; - } +// BlockLabel <- IDENTIFIER COLON +static Token *ast_parse_block_label(ParseContext *pc) { + Token *ident = eat_token_if(pc, TokenIdSymbol); + if (ident == nullptr) + return nullptr; - AstNode *node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod, false, is_export); - if (node == nullptr) { - if (is_export) { - *token_index -= 1; - } + // We do 2 token lookahead here, as we don't want to error when + // parsing identifiers. + if (eat_token_if(pc, TokenIdColon) == nullptr) { + put_back_token(pc); return nullptr; } - return node; + + return ident; } -/* -LocalVarDecl = option("comptime") VariableDeclaration -*/ -static AstNode *ast_parse_local_var_decl(ParseContext *pc, size_t *token_index) { - Token *first_token = &pc->tokens->at(*token_index); +// FieldInit <- DOT IDENTIFIER EQUAL Expr +static AstNode *ast_parse_field_init(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdDot); + if (first == nullptr) + return nullptr; - bool is_comptime = false;; - if (first_token->id == TokenIdKeywordCompTime) { - *token_index += 1; - is_comptime = true; - } + Token *name = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdEq); + AstNode *expr = ast_expect(pc, ast_parse_expr); - AstNode *node = ast_parse_variable_declaration_expr(pc, token_index, false, VisibModPrivate, is_comptime, false); - if (node == nullptr) { - if (is_comptime) { - *token_index -= 1; - } - return nullptr; - } - return node; + AstNode *res = ast_create_node(pc, NodeTypeStructValueField, first); + res->data.struct_val_field.name = token_buf(name); + res->data.struct_val_field.expr = expr; + return res; } -/* -BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression -*/ -static AstNode *ast_parse_bool_or_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *operand_1 = ast_parse_bool_and_expr(pc, token_index, mandatory); - if (!operand_1) +// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN +static AstNode *ast_parse_while_continue_expr(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdColon); + if (first == nullptr) return nullptr; - while (true) { - Token *token = &pc->tokens->at(*token_index); - if (token->id != TokenIdKeywordOr) - return operand_1; - *token_index += 1; - - AstNode *operand_2 = ast_parse_bool_and_expr(pc, token_index, true); + expect_token(pc, TokenIdLParen); + AstNode *expr = ast_expect(pc, ast_parse_assign_expr); + expect_token(pc, TokenIdRParen); + return expr; +} - AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); - node->data.bin_op_expr.op1 = operand_1; - node->data.bin_op_expr.bin_op = BinOpTypeBoolOr; - node->data.bin_op_expr.op2 = operand_2; +// Section <- KEYWORD_section LPAREN Expr RPAREN +static AstNode *ast_parse_section(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdKeywordLinkSection); + if (first == nullptr) + return nullptr; - operand_1 = node; - } + expect_token(pc, TokenIdLParen); + AstNode *res = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + return res; } -/* -WhileExpression(body) = option(Symbol ":") option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body)) -*/ -static AstNode *ast_parse_while_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - size_t orig_token_index = *token_index; - - Token *name_token = nullptr; - Token *token = &pc->tokens->at(*token_index); - - if (token->id == TokenIdSymbol) { - *token_index += 1; - Token *colon_token = &pc->tokens->at(*token_index); - if (colon_token->id == TokenIdColon) { - *token_index += 1; - name_token = token; - token = &pc->tokens->at(*token_index); - } else if (mandatory) { - ast_expect_token(pc, colon_token, TokenIdColon); - zig_unreachable(); - } else { - *token_index = orig_token_index; - return nullptr; - } +// FnCC +// <- KEYWORD_nakedcc +// / KEYWORD_stdcallcc +// / KEYWORD_extern +// / KEYWORD_async (LARROW TypeExpr RARROW)? +static Optional<AstNodeFnProto> ast_parse_fn_cc(ParseContext *pc) { + AstNodeFnProto res = {}; + if (eat_token_if(pc, TokenIdKeywordNakedCC) != nullptr) { + res.cc = CallingConventionNaked; + return Optional<AstNodeFnProto>::some(res); } - - bool is_inline = false; - if (token->id == TokenIdKeywordInline) { - is_inline = true; - *token_index += 1; - token = &pc->tokens->at(*token_index); + if (eat_token_if(pc, TokenIdKeywordStdcallCC) != nullptr) { + res.cc = CallingConventionStdcall; + return Optional<AstNodeFnProto>::some(res); } - - Token *while_token; - if (token->id == TokenIdKeywordWhile) { - while_token = token; - *token_index += 1; - } else if (mandatory) { - ast_expect_token(pc, token, TokenIdKeywordWhile); - zig_unreachable(); - } else { - *token_index = orig_token_index; - return nullptr; + if (eat_token_if(pc, TokenIdKeywordExtern) != nullptr) { + res.cc = CallingConventionC; + return Optional<AstNodeFnProto>::some(res); } + if (eat_token_if(pc, TokenIdKeywordAsync) != nullptr) { + res.cc = CallingConventionAsync; + if (eat_token_if(pc, TokenIdCmpLessThan) == nullptr) + return Optional<AstNodeFnProto>::some(res); - AstNode *node = ast_create_node(pc, NodeTypeWhileExpr, while_token); - if (name_token != nullptr) { - node->data.while_expr.name = token_buf(name_token); + res.async_allocator_type = ast_expect(pc, ast_parse_type_expr); + expect_token(pc, TokenIdCmpGreaterThan); + return Optional<AstNodeFnProto>::some(res); } - node->data.while_expr.is_inline = is_inline; - ast_eat_token(pc, token_index, TokenIdLParen); - node->data.while_expr.condition = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); + return Optional<AstNodeFnProto>::none(); +} - Token *open_bar_tok = &pc->tokens->at(*token_index); - if (open_bar_tok->id == TokenIdBinOr) { - *token_index += 1; +// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType +static AstNode *ast_parse_param_decl(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdKeywordNoAlias); + if (first == nullptr) + first = eat_token_if(pc, TokenIdKeywordCompTime); - Token *star_tok = &pc->tokens->at(*token_index); - if (star_tok->id == TokenIdStar) { - *token_index += 1; - node->data.while_expr.var_is_ptr = true; + Token *name = eat_token_if(pc, TokenIdSymbol); + if (name != nullptr) { + if (eat_token_if(pc, TokenIdColon) != nullptr) { + if (first == nullptr) + first = name; + } else { + // We put back the ident, so it can be parsed as a ParamType + // later. + put_back_token(pc); + name = nullptr; } - - Token *var_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); - node->data.while_expr.var_symbol = token_buf(var_name_tok); - ast_eat_token(pc, token_index, TokenIdBinOr); } - Token *colon_tok = &pc->tokens->at(*token_index); - if (colon_tok->id == TokenIdColon) { - *token_index += 1; - ast_eat_token(pc, token_index, TokenIdLParen); - node->data.while_expr.continue_expr = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); - } - - node->data.while_expr.body = ast_parse_block_or_expression(pc, token_index, true); - - Token *else_tok = &pc->tokens->at(*token_index); - if (else_tok->id == TokenIdKeywordElse) { - *token_index += 1; - - Token *else_bar_tok = &pc->tokens->at(*token_index); - if (else_bar_tok->id == TokenIdBinOr) { - *token_index += 1; - - Token *err_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); - node->data.while_expr.err_symbol = token_buf(err_name_tok); - - ast_eat_token(pc, token_index, TokenIdBinOr); - } - - node->data.while_expr.else_node = ast_parse_block_or_expression(pc, token_index, true); + AstNode *res; + if (first == nullptr) { + first = peek_token(pc); + res = ast_parse_param_type(pc); + } else { + res = ast_expect(pc, ast_parse_param_type); } - return node; -} + if (res == nullptr) + return nullptr; -static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index) { - Token *token = ast_eat_token(pc, token_index, TokenIdSymbol); - AstNode *node = ast_create_node(pc, NodeTypeSymbol, token); - node->data.symbol_expr.symbol = token_buf(token); - return node; + assert(res->type == NodeTypeParamDecl); + res->line = first->start_line; + res->column = first->start_column; + res->data.param_decl.name = token_buf(name); + res->data.param_decl.is_noalias = first->id == TokenIdKeywordNoAlias; + res->data.param_decl.is_inline = first->id == TokenIdKeywordCompTime; + return res; } -/* -ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body)) -*/ -static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - size_t orig_token_index = *token_index; - - Token *name_token = nullptr; - Token *token = &pc->tokens->at(*token_index); - - if (token->id == TokenIdSymbol) { - *token_index += 1; - Token *colon_token = &pc->tokens->at(*token_index); - if (colon_token->id == TokenIdColon) { - *token_index += 1; - name_token = token; - token = &pc->tokens->at(*token_index); - } else if (mandatory) { - ast_expect_token(pc, colon_token, TokenIdColon); - zig_unreachable(); - } else { - *token_index = orig_token_index; - return nullptr; - } - } - - bool is_inline = false; - if (token->id == TokenIdKeywordInline) { - is_inline = true; - *token_index += 1; - token = &pc->tokens->at(*token_index); +// ParamType +// <- KEYWORD_var +// / DOT3 +// / TypeExpr +static AstNode *ast_parse_param_type(ParseContext *pc) { + Token *var_token = eat_token_if(pc, TokenIdKeywordVar); + if (var_token != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeParamDecl, var_token); + res->data.param_decl.var_token = var_token; + return res; } - Token *for_token; - if (token->id == TokenIdKeywordFor) { - for_token = token; - *token_index += 1; - } else if (mandatory) { - ast_expect_token(pc, token, TokenIdKeywordFor); - zig_unreachable(); - } else { - *token_index = orig_token_index; - return nullptr; + Token *dots = eat_token_if(pc, TokenIdEllipsis3); + if (dots != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeParamDecl, dots); + res->data.param_decl.is_var_args = true; + return res; } - AstNode *node = ast_create_node(pc, NodeTypeForExpr, for_token); - if (name_token != nullptr) { - node->data.for_expr.name = token_buf(name_token); + AstNode *type_expr = ast_parse_type_expr(pc); + if (type_expr != nullptr) { + AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeParamDecl, type_expr); + res->data.param_decl.type = type_expr; + return res; } - node->data.for_expr.is_inline = is_inline; - ast_eat_token(pc, token_index, TokenIdLParen); - node->data.for_expr.array_expr = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); - - Token *maybe_bar = &pc->tokens->at(*token_index); - if (maybe_bar->id == TokenIdBinOr) { - *token_index += 1; - - Token *maybe_star = &pc->tokens->at(*token_index); - if (maybe_star->id == TokenIdStar) { - *token_index += 1; - node->data.for_expr.elem_is_ptr = true; - } + return nullptr; +} - node->data.for_expr.elem_node = ast_parse_symbol(pc, token_index); +// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload? +static AstNode *ast_parse_if_prefix(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdKeywordIf); + if (first == nullptr) + return nullptr; - Token *maybe_comma = &pc->tokens->at(*token_index); - if (maybe_comma->id == TokenIdComma) { - *token_index += 1; + expect_token(pc, TokenIdLParen); + AstNode *condition = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + Optional<PtrPayload> opt_payload = ast_parse_ptr_payload(pc); - node->data.for_expr.index_node = ast_parse_symbol(pc, token_index); - } - - ast_eat_token(pc, token_index, TokenIdBinOr); + PtrPayload payload; + AstNode *res = ast_create_node(pc, NodeTypeTestExpr, first); + res->data.test_expr.target_node = condition; + if (opt_payload.unwrap(&payload)) { + res->data.test_expr.var_symbol = token_buf(payload.payload); + res->data.test_expr.var_is_ptr = payload.asterisk != nullptr; } + return res; +} - node->data.for_expr.body = ast_parse_block_or_expression(pc, token_index, true); +// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? +static AstNode *ast_parse_while_prefix(ParseContext *pc) { + Token *while_token = eat_token_if(pc, TokenIdKeywordWhile); + if (while_token == nullptr) + return nullptr; - Token *else_tok = &pc->tokens->at(*token_index); - if (else_tok->id == TokenIdKeywordElse) { - *token_index += 1; + expect_token(pc, TokenIdLParen); + AstNode *condition = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + Optional<PtrPayload> opt_payload = ast_parse_ptr_payload(pc); + AstNode *continue_expr = ast_parse_while_continue_expr(pc); - node->data.for_expr.else_node = ast_parse_block_or_expression(pc, token_index, true); + PtrPayload payload; + AstNode *res = ast_create_node(pc, NodeTypeWhileExpr, while_token); + res->data.while_expr.condition = condition; + res->data.while_expr.continue_expr = continue_expr; + if (opt_payload.unwrap(&payload)) { + res->data.while_expr.var_symbol = token_buf(payload.payload); + res->data.while_expr.var_is_ptr = payload.asterisk != nullptr; } - return node; + return res; } -/* -SwitchExpression = "switch" "(" Expression ")" "{" many(SwitchProng) "}" -SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression "," -SwitchItem = Expression | (Expression "..." Expression) -*/ -static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *switch_token = &pc->tokens->at(*token_index); - if (switch_token->id == TokenIdKeywordSwitch) { - *token_index += 1; - } else if (mandatory) { - ast_expect_token(pc, switch_token, TokenIdKeywordSwitch); - zig_unreachable(); - } else { +// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload +static AstNode *ast_parse_for_prefix(ParseContext *pc) { + Token *for_token = eat_token_if(pc, TokenIdKeywordFor); + if (for_token == nullptr) return nullptr; - } - AstNode *node = ast_create_node(pc, NodeTypeSwitchExpr, switch_token); + expect_token(pc, TokenIdLParen); + AstNode *array_expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + PtrIndexPayload payload; + if (!ast_parse_ptr_index_payload(pc).unwrap(&payload)) + ast_invalid_token_error(pc, peek_token(pc)); - ast_eat_token(pc, token_index, TokenIdLParen); - node->data.switch_expr.expr = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); - ast_eat_token(pc, token_index, TokenIdLBrace); + AstNode *res = ast_create_node(pc, NodeTypeForExpr, for_token); + res->data.for_expr.array_expr = array_expr; + res->data.for_expr.elem_node = token_symbol(pc, payload.payload); + res->data.for_expr.elem_is_ptr = payload.asterisk != nullptr; + if (payload.index != nullptr) + res->data.for_expr.index_node = token_symbol(pc, payload.index); - for (;;) { - Token *token = &pc->tokens->at(*token_index); + return res; +} - if (token->id == TokenIdRBrace) { - *token_index += 1; +// Payload <- PIPE IDENTIFIER PIPE +static Token *ast_parse_payload(ParseContext *pc) { + if (eat_token_if(pc, TokenIdBinOr) == nullptr) + return nullptr; - return node; - } + Token *res = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdBinOr); + return res; +} - AstNode *prong_node = ast_create_node(pc, NodeTypeSwitchProng, token); - node->data.switch_expr.prongs.append(prong_node); +// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE +static Optional<PtrPayload> ast_parse_ptr_payload(ParseContext *pc) { + if (eat_token_if(pc, TokenIdBinOr) == nullptr) + return Optional<PtrPayload>::none(); - if (token->id == TokenIdKeywordElse) { - *token_index += 1; - } else for (;;) { - AstNode *expr1 = ast_parse_expression(pc, token_index, true); - Token *ellipsis_tok = &pc->tokens->at(*token_index); - if (ellipsis_tok->id == TokenIdEllipsis3) { - *token_index += 1; + Token *asterisk = eat_token_if(pc, TokenIdStar); + Token *payload = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdBinOr); - AstNode *range_node = ast_create_node(pc, NodeTypeSwitchRange, ellipsis_tok); - prong_node->data.switch_prong.items.append(range_node); + PtrPayload res; + res.asterisk = asterisk; + res.payload = payload; + return Optional<PtrPayload>::some(res); +} - range_node->data.switch_range.start = expr1; - range_node->data.switch_range.end = ast_parse_expression(pc, token_index, true); +// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE +static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc) { + if (eat_token_if(pc, TokenIdBinOr) == nullptr) + return Optional<PtrIndexPayload>::none(); - prong_node->data.switch_prong.any_items_are_range = true; - } else { - prong_node->data.switch_prong.items.append(expr1); - } - Token *comma_tok = &pc->tokens->at(*token_index); - if (comma_tok->id == TokenIdComma) { - *token_index += 1; + Token *asterisk = eat_token_if(pc, TokenIdStar); + Token *payload = expect_token(pc, TokenIdSymbol); + Token *index = nullptr; + if (eat_token_if(pc, TokenIdComma) != nullptr) + index = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdBinOr); - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdFatArrow) { - break; - } else { - continue; - } - } - break; - } + PtrIndexPayload res; + res.asterisk = asterisk; + res.payload = payload; + res.index = index; + return Optional<PtrIndexPayload>::some(res); +} - ast_eat_token(pc, token_index, TokenIdFatArrow); +// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr +static AstNode *ast_parse_switch_prong(ParseContext *pc) { + AstNode *res = ast_parse_switch_case(pc); + if (res == nullptr) + return nullptr; - Token *maybe_bar = &pc->tokens->at(*token_index); - if (maybe_bar->id == TokenIdBinOr) { - *token_index += 1; + expect_token(pc, TokenIdFatArrow); + Optional<PtrPayload> opt_payload = ast_parse_ptr_payload(pc); + AstNode *expr = ast_expect(pc, ast_parse_assign_expr); - Token *star_or_symbol = &pc->tokens->at(*token_index); - AstNode *var_symbol_node; - bool var_is_ptr; - if (star_or_symbol->id == TokenIdStar) { - *token_index += 1; - var_is_ptr = true; - var_symbol_node = ast_parse_symbol(pc, token_index); - } else { - var_is_ptr = false; - var_symbol_node = ast_parse_symbol(pc, token_index); - } + PtrPayload payload; + assert(res->type == NodeTypeSwitchProng); + res->data.switch_prong.expr = expr; + if (opt_payload.unwrap(&payload)) { + res->data.switch_prong.var_symbol = token_symbol(pc, payload.payload); + res->data.switch_prong.var_is_ptr = payload.asterisk != nullptr; + } - prong_node->data.switch_prong.var_symbol = var_symbol_node; - prong_node->data.switch_prong.var_is_ptr = var_is_ptr; - ast_eat_token(pc, token_index, TokenIdBinOr); - } + return res; +} - prong_node->data.switch_prong.expr = ast_parse_expression(pc, token_index, true); +// SwitchCase +// <- SwitchItem (COMMA SwitchItem)* COMMA? +// / KEYWORD_else +static AstNode *ast_parse_switch_case(ParseContext *pc) { + AstNode *first = ast_parse_switch_item(pc); + if (first != nullptr) { + AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeSwitchProng, first); + res->data.switch_prong.items.append(first); + res->data.switch_prong.any_items_are_range = first->type == NodeTypeSwitchRange; - Token *trailing_token = &pc->tokens->at(*token_index); - if (trailing_token->id == TokenIdRBrace) { - *token_index += 1; + while (eat_token_if(pc, TokenIdComma) != nullptr) { + AstNode *item = ast_parse_switch_item(pc); + if (item == nullptr) + break; - return node; - } else { - ast_eat_token(pc, token_index, TokenIdComma); + res->data.switch_prong.items.append(item); + res->data.switch_prong.any_items_are_range |= item->type == NodeTypeSwitchRange; } + return res; } -} - -/* -BlockExpression(body) = Block | IfExpression(body) | IfErrorExpression(body) | TestExpression(body) | WhileExpression(body) | ForExpression(body) | SwitchExpression | CompTimeExpression(body) | SuspendExpression(body) -*/ -static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *token = &pc->tokens->at(*token_index); - - AstNode *if_expr = ast_parse_if_try_test_expr(pc, token_index, false); - if (if_expr) - return if_expr; - - AstNode *while_expr = ast_parse_while_expr(pc, token_index, false); - if (while_expr) - return while_expr; - AstNode *for_expr = ast_parse_for_expr(pc, token_index, false); - if (for_expr) - return for_expr; - - AstNode *switch_expr = ast_parse_switch_expr(pc, token_index, false); - if (switch_expr) - return switch_expr; - - AstNode *block = ast_parse_block(pc, token_index, false); - if (block) - return block; - - AstNode *comptime_node = ast_parse_comptime_expr(pc, token_index, false, false); - if (comptime_node) - return comptime_node; - - AstNode *suspend_node = ast_parse_suspend_block(pc, token_index, false); - if (suspend_node) - return suspend_node; - - if (mandatory) - ast_invalid_token_error(pc, token); + Token *else_token = eat_token_if(pc, TokenIdKeywordElse); + if (else_token != nullptr) + return ast_create_node(pc, NodeTypeSwitchProng, else_token); return nullptr; } -static BinOpType tok_to_ass_op(Token *token) { - switch (token->id) { - case TokenIdEq: return BinOpTypeAssign; - case TokenIdTimesEq: return BinOpTypeAssignTimes; - case TokenIdTimesPercentEq: return BinOpTypeAssignTimesWrap; - case TokenIdDivEq: return BinOpTypeAssignDiv; - case TokenIdModEq: return BinOpTypeAssignMod; - case TokenIdPlusEq: return BinOpTypeAssignPlus; - case TokenIdPlusPercentEq: return BinOpTypeAssignPlusWrap; - case TokenIdMinusEq: return BinOpTypeAssignMinus; - case TokenIdMinusPercentEq: return BinOpTypeAssignMinusWrap; - case TokenIdBitShiftLeftEq: return BinOpTypeAssignBitShiftLeft; - case TokenIdBitShiftRightEq: return BinOpTypeAssignBitShiftRight; - case TokenIdBitAndEq: return BinOpTypeAssignBitAnd; - case TokenIdBitXorEq: return BinOpTypeAssignBitXor; - case TokenIdBitOrEq: return BinOpTypeAssignBitOr; - default: return BinOpTypeInvalid; - } -} +// SwitchItem <- Expr (DOT3 Expr)? +static AstNode *ast_parse_switch_item(ParseContext *pc) { + AstNode *expr = ast_parse_expr(pc); + if (expr == nullptr) + return nullptr; -/* -AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "*%=" | "+%=" | "-%=" | "<<%=" -*/ -static BinOpType ast_parse_ass_op(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *token = &pc->tokens->at(*token_index); - BinOpType result = tok_to_ass_op(token); - if (result == BinOpTypeInvalid) { - if (mandatory) { - ast_invalid_token_error(pc, token); - } else { - return BinOpTypeInvalid; - } + Token *dots = eat_token_if(pc, TokenIdEllipsis3); + if (dots != nullptr) { + AstNode *expr2 = ast_expect(pc, ast_parse_expr); + AstNode *res = ast_create_node(pc, NodeTypeSwitchRange, dots); + res->data.switch_range.start = expr; + res->data.switch_range.end = expr2; + return res; + } + + return expr; +} + +// AssignOp +// <- ASTERISKEQUAL +// / SLASHEQUAL +// / PERCENTEQUAL +// / PLUSEQUAL +// / MINUSEQUAL +// / LARROW2EQUAL +// / RARROW2EQUAL +// / AMPERSANDEQUAL +// / CARETEQUAL +// / PIPEEQUAL +// / ASTERISKPERCENTEQUAL +// / PLUSPERCENTEQUAL +// / MINUSPERCENTEQUAL +// / EQUAL +static AstNode *ast_parse_assign_op(ParseContext *pc) { + // In C, we have `T arr[N] = {[i] = T{}};` but it doesn't + // seem to work in C++... + BinOpType table[TokenIdCount] = {}; + table[TokenIdBarBarEq] = BinOpTypeAssignMergeErrorSets; + table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd; + table[TokenIdBitOrEq] = BinOpTypeAssignBitOr; + table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft; + table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight; + table[TokenIdBitXorEq] = BinOpTypeAssignBitXor; + table[TokenIdDivEq] = BinOpTypeAssignDiv; + table[TokenIdEq] = BinOpTypeAssign; + table[TokenIdMinusEq] = BinOpTypeAssignMinus; + table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap; + table[TokenIdModEq] = BinOpTypeAssignMod; + table[TokenIdPlusEq] = BinOpTypeAssignPlus; + table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap; + table[TokenIdTimesEq] = BinOpTypeAssignTimes; + table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap; + + BinOpType op = table[peek_token(pc)->id]; + if (op != BinOpTypeInvalid) { + Token *op_token = eat_token(pc); + AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); + res->data.bin_op_expr.bin_op = op; + return res; } - *token_index += 1; - return result; -} -/* -UnwrapExpression : BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression -UnwrapOptional = "orelse" Expression -UnwrapError = "catch" option("|" Symbol "|") Expression -*/ -static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory); - if (!lhs) - return nullptr; + return nullptr; - Token *token = &pc->tokens->at(*token_index); +} - if (token->id == TokenIdKeywordOrElse) { - *token_index += 1; +// CompareOp +// <- EQUALEQUAL +// / EXCLAMATIONMARKEQUAL +// / LARROW +// / RARROW +// / LARROWEQUAL +// / RARROWEQUAL +static AstNode *ast_parse_compare_op(ParseContext *pc) { + BinOpType table[TokenIdCount] = {}; + table[TokenIdCmpEq] = BinOpTypeCmpEq; + table[TokenIdCmpNotEq] = BinOpTypeCmpNotEq; + table[TokenIdCmpLessThan] = BinOpTypeCmpLessThan; + table[TokenIdCmpGreaterThan] = BinOpTypeCmpGreaterThan; + table[TokenIdCmpLessOrEq] = BinOpTypeCmpLessOrEq; + table[TokenIdCmpGreaterOrEq] = BinOpTypeCmpGreaterOrEq; - AstNode *rhs = ast_parse_expression(pc, token_index, true); + BinOpType op = table[peek_token(pc)->id]; + if (op != BinOpTypeInvalid) { + Token *op_token = eat_token(pc); + AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); + res->data.bin_op_expr.bin_op = op; + return res; + } - AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); - node->data.bin_op_expr.op1 = lhs; - node->data.bin_op_expr.bin_op = BinOpTypeUnwrapOptional; - node->data.bin_op_expr.op2 = rhs; + return nullptr; +} - return node; - } else if (token->id == TokenIdKeywordCatch) { - *token_index += 1; +// BitwiseOp +// <- AMPERSAND +// / CARET +// / PIPE +// / KEYWORD_orelse +// / KEYWORD_catch Payload? +static AstNode *ast_parse_bitwise_op(ParseContext *pc) { + BinOpType table[TokenIdCount] = {}; + table[TokenIdAmpersand] = BinOpTypeBinAnd; + table[TokenIdBinXor] = BinOpTypeBinXor; + table[TokenIdBinOr] = BinOpTypeBinOr; + table[TokenIdKeywordOrElse] = BinOpTypeUnwrapOptional; - AstNode *node = ast_create_node(pc, NodeTypeUnwrapErrorExpr, token); - node->data.unwrap_err_expr.op1 = lhs; + BinOpType op = table[peek_token(pc)->id]; + if (op != BinOpTypeInvalid) { + Token *op_token = eat_token(pc); + AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); + res->data.bin_op_expr.bin_op = op; + return res; + } - Token *maybe_bar_tok = &pc->tokens->at(*token_index); - if (maybe_bar_tok->id == TokenIdBinOr) { - *token_index += 1; - node->data.unwrap_err_expr.symbol = ast_parse_symbol(pc, token_index); - ast_eat_token(pc, token_index, TokenIdBinOr); - } - node->data.unwrap_err_expr.op2 = ast_parse_expression(pc, token_index, true); + Token *catch_token = eat_token_if(pc, TokenIdKeywordCatch); + if (catch_token != nullptr) { + Token *payload = ast_parse_payload(pc); + AstNode *res = ast_create_node(pc, NodeTypeUnwrapErrorExpr, catch_token); + if (payload != nullptr) + res->data.unwrap_err_expr.symbol = token_symbol(pc, payload); - return node; - } else { - return lhs; + return res; } -} -/* -AssignmentExpression : UnwrapExpression AssignmentOperator UnwrapExpression | UnwrapExpression -*/ -static AstNode *ast_parse_ass_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *lhs = ast_parse_unwrap_expr(pc, token_index, mandatory); - if (!lhs) - return nullptr; - - Token *token = &pc->tokens->at(*token_index); - BinOpType ass_op = ast_parse_ass_op(pc, token_index, false); - if (ass_op == BinOpTypeInvalid) - return lhs; + return nullptr; +} - AstNode *rhs = ast_parse_unwrap_expr(pc, token_index, true); +// BitShiftOp +// <- LARROW2 +// / RARROW2 +static AstNode *ast_parse_bit_shift_op(ParseContext *pc) { + BinOpType table[TokenIdCount] = {}; + table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft; + table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight; - AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); - node->data.bin_op_expr.op1 = lhs; - node->data.bin_op_expr.bin_op = ass_op; - node->data.bin_op_expr.op2 = rhs; + BinOpType op = table[peek_token(pc)->id]; + if (op != BinOpTypeInvalid) { + Token *op_token = eat_token(pc); + AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); + res->data.bin_op_expr.bin_op = op; + return res; + } - return node; + return nullptr; } -static AstNode *ast_parse_block_expr_or_expression(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *block_expr = ast_parse_block_expr(pc, token_index, false); - if (block_expr) - return block_expr; +// AdditionOp +// <- PLUS +// / MINUS +// / PLUS2 +// / PLUSPERCENT +// / MINUSPERCENT +static AstNode *ast_parse_addition_op(ParseContext *pc) { + BinOpType table[TokenIdCount] = {}; + table[TokenIdPlus] = BinOpTypeAdd; + table[TokenIdDash] = BinOpTypeSub; + table[TokenIdPlusPlus] = BinOpTypeArrayCat; + table[TokenIdPlusPercent] = BinOpTypeAddWrap; + table[TokenIdMinusPercent] = BinOpTypeSubWrap; + + BinOpType op = table[peek_token(pc)->id]; + if (op != BinOpTypeInvalid) { + Token *op_token = eat_token(pc); + AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); + res->data.bin_op_expr.bin_op = op; + return res; + } - return ast_parse_expression(pc, token_index, mandatory); + return nullptr; } -/* -BlockOrExpression = Block | Expression -*/ -static AstNode *ast_parse_block_or_expression(ParseContext *pc, size_t *token_index, bool mandatory) { - AstNode *block_expr = ast_parse_block(pc, token_index, false); - if (block_expr) - return block_expr; +// MultiplyOp +// <- PIPE2 +// / ASTERISK +// / SLASH +// / PERCENT +// / ASTERISK2 +// / ASTERISKPERCENT +static AstNode *ast_parse_multiply_op(ParseContext *pc) { + BinOpType table[TokenIdCount] = {}; + table[TokenIdBarBar] = BinOpTypeMergeErrorSets; + table[TokenIdStar] = BinOpTypeMult; + table[TokenIdSlash] = BinOpTypeDiv; + table[TokenIdPercent] = BinOpTypeMod; + table[TokenIdStarStar] = BinOpTypeArrayMult; + table[TokenIdTimesPercent] = BinOpTypeMultWrap; + + BinOpType op = table[peek_token(pc)->id]; + if (op != BinOpTypeInvalid) { + Token *op_token = eat_token(pc); + AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); + res->data.bin_op_expr.bin_op = op; + return res; + } - return ast_parse_expression(pc, token_index, mandatory); + return nullptr; } -/* -Expression = TryExpression | ReturnExpression | BreakExpression | AssignmentExpression | CancelExpression | ResumeExpression -*/ -static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *token = &pc->tokens->at(*token_index); - - AstNode *return_expr = ast_parse_return_expr(pc, token_index); - if (return_expr) - return return_expr; +// PrefixOp +// <- EXCLAMATIONMARK +// / MINUS +// / TILDE +// / MINUSPERCENT +// / AMPERSAND +// / KEYWORD_try +// / KEYWORD_await +static AstNode *ast_parse_prefix_op(ParseContext *pc) { + PrefixOp table[TokenIdCount] = {}; + table[TokenIdBang] = PrefixOpBoolNot; + table[TokenIdDash] = PrefixOpNegation; + table[TokenIdTilde] = PrefixOpBinNot; + table[TokenIdMinusPercent] = PrefixOpNegationWrap; + table[TokenIdAmpersand] = PrefixOpAddrOf; - AstNode *try_expr = ast_parse_try_expr(pc, token_index); - if (try_expr) - return try_expr; - - AstNode *break_expr = ast_parse_break_expr(pc, token_index); - if (break_expr) - return break_expr; - - AstNode *cancel_expr = ast_parse_cancel_expr(pc, token_index); - if (cancel_expr) - return cancel_expr; - - AstNode *resume_expr = ast_parse_resume_expr(pc, token_index); - if (resume_expr) - return resume_expr; + PrefixOp op = table[peek_token(pc)->id]; + if (op != PrefixOpInvalid) { + Token *op_token = eat_token(pc); + AstNode *res = ast_create_node(pc, NodeTypePrefixOpExpr, op_token); + res->data.prefix_op_expr.prefix_op = op; + return res; + } - AstNode *ass_expr = ast_parse_ass_expr(pc, token_index, false); - if (ass_expr) - return ass_expr; + Token *try_token = eat_token_if(pc, TokenIdKeywordTry); + if (try_token != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeReturnExpr, try_token); + res->data.return_expr.kind = ReturnKindError; + return res; + } - if (mandatory) - ast_invalid_token_error(pc, token); + Token *await = eat_token_if(pc, TokenIdKeywordAwait); + if (await != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeAwaitExpr, await); + return res; + } return nullptr; } -bool statement_terminates_without_semicolon(AstNode *node) { - switch (node->type) { - case NodeTypeIfBoolExpr: - if (node->data.if_bool_expr.else_node) - return statement_terminates_without_semicolon(node->data.if_bool_expr.else_node); - return node->data.if_bool_expr.then_block->type == NodeTypeBlock; - case NodeTypeIfErrorExpr: - if (node->data.if_err_expr.else_node) - return statement_terminates_without_semicolon(node->data.if_err_expr.else_node); - return node->data.if_err_expr.then_node->type == NodeTypeBlock; - case NodeTypeTestExpr: - if (node->data.test_expr.else_node) - return statement_terminates_without_semicolon(node->data.test_expr.else_node); - return node->data.test_expr.then_node->type == NodeTypeBlock; - case NodeTypeWhileExpr: - return node->data.while_expr.body->type == NodeTypeBlock; - case NodeTypeForExpr: - return node->data.for_expr.body->type == NodeTypeBlock; - case NodeTypeCompTime: - return node->data.comptime_expr.expr->type == NodeTypeBlock; - case NodeTypeDefer: - return node->data.defer.expr->type == NodeTypeBlock; - case NodeTypeSuspend: - return node->data.suspend.block != nullptr && node->data.suspend.block->type == NodeTypeBlock; - case NodeTypeSwitchExpr: - case NodeTypeBlock: - return true; - default: - return false; - } -} - -/* -Block = option(Symbol ":") "{" many(Statement) "}" -Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" | ExportDecl -*/ -static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mandatory) { - size_t orig_token_index = *token_index; - - Token *name_token = nullptr; - Token *last_token = &pc->tokens->at(*token_index); - - if (last_token->id == TokenIdSymbol) { - *token_index += 1; - Token *colon_token = &pc->tokens->at(*token_index); - if (colon_token->id == TokenIdColon) { - *token_index += 1; - name_token = last_token; - last_token = &pc->tokens->at(*token_index); - } else if (mandatory) { - ast_expect_token(pc, colon_token, TokenIdColon); - zig_unreachable(); - } else { - *token_index = orig_token_index; - return nullptr; - } +// PrefixTypeOp +// <- QUESTIONMARK +// / KEYWORD_promise MINUSRARROW +// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile)* +// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile)* +static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { + Token *questionmark = eat_token_if(pc, TokenIdQuestion); + if (questionmark != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypePrefixOpExpr, questionmark); + res->data.prefix_op_expr.prefix_op = PrefixOpOptional; + return res; } - if (last_token->id != TokenIdLBrace) { - if (mandatory) { - ast_expect_token(pc, last_token, TokenIdLBrace); - } else { - *token_index = orig_token_index; - return nullptr; + Token *promise = eat_token_if(pc, TokenIdKeywordPromise); + if (promise != nullptr) { + if (eat_token_if(pc, TokenIdArrow) != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypePromiseType, promise); + return res; } - } - *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeBlock, last_token); - if (name_token != nullptr) { - node->data.block.name = token_buf(name_token); + put_back_token(pc); } - for (;;) { - last_token = &pc->tokens->at(*token_index); - if (last_token->id == TokenIdRBrace) { - *token_index += 1; - return node; - } - - AstNode *statement_node = ast_parse_local_var_decl(pc, token_index); - if (!statement_node) - statement_node = ast_parse_defer_expr(pc, token_index); - if (!statement_node) - statement_node = ast_parse_block_expr(pc, token_index, false); - if (!statement_node) - statement_node = ast_parse_expression(pc, token_index, false); + AstNode *array = ast_parse_array_type_start(pc); + if (array != nullptr) { + assert(array->type == NodeTypeArrayType); + while (true) { + AstNode *align_expr = ast_parse_byte_align(pc); + if (align_expr != nullptr) { + array->data.array_type.align_expr = align_expr; + continue; + } - if (!statement_node) { - ast_invalid_token_error(pc, last_token); - } + if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) { + array->data.array_type.is_const = true; + continue; + } - node->data.block.statements.append(statement_node); + if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) { + array->data.array_type.is_volatile = true; + continue; + } - if (!statement_terminates_without_semicolon(statement_node)) { - ast_eat_token(pc, token_index, TokenIdSemicolon); + break; } - } - zig_unreachable(); -} -/* -FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("(" Expression ")"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var") -*/ -static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) { - Token *first_token = &pc->tokens->at(*token_index); - Token *fn_token; - - CallingConvention cc; - bool is_extern = false; - AstNode *async_allocator_type_node = nullptr; - if (first_token->id == TokenIdKeywordNakedCC) { - *token_index += 1; - fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn); - cc = CallingConventionNaked; - } else if (first_token->id == TokenIdKeywordAsync) { - *token_index += 1; - Token *next_token = &pc->tokens->at(*token_index); - if (next_token->id == TokenIdCmpLessThan) { - *token_index += 1; - async_allocator_type_node = ast_parse_type_expr(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdCmpGreaterThan); - } - fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn); - cc = CallingConventionAsync; - } else if (first_token->id == TokenIdKeywordStdcallCC) { - *token_index += 1; - fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn); - cc = CallingConventionStdcall; - } else if (first_token->id == TokenIdKeywordExtern) { - is_extern = true; - *token_index += 1; - Token *next_token = &pc->tokens->at(*token_index); - if (next_token->id == TokenIdKeywordFn) { - fn_token = next_token; - *token_index += 1; - } else if (mandatory) { - ast_expect_token(pc, next_token, TokenIdKeywordFn); - zig_unreachable(); - } else { - *token_index -= 1; - return nullptr; - } - cc = CallingConventionC; - } else if (first_token->id == TokenIdKeywordFn) { - fn_token = first_token; - *token_index += 1; - cc = CallingConventionUnspecified; - } else if (mandatory) { - ast_expect_token(pc, first_token, TokenIdKeywordFn); - zig_unreachable(); - } else { - return nullptr; - } + return array; + } + + AstNode *ptr = ast_parse_ptr_type_start(pc); + if (ptr != nullptr) { + assert(ptr->type == NodeTypePointerType); + // We might get two pointers from *_ptr_type_start + AstNode *child = ptr->data.pointer_type.op_expr; + if (child == nullptr) + child = ptr; + while (true) { + if (eat_token_if(pc, TokenIdKeywordAlign) != nullptr) { + expect_token(pc, TokenIdLParen); + AstNode *align_expr = ast_parse_expr(pc); + child->data.pointer_type.align_expr = align_expr; + if (eat_token_if(pc, TokenIdColon) != nullptr) { + Token *bit_offset_start = expect_token(pc, TokenIdIntLiteral); + expect_token(pc, TokenIdColon); + Token *host_int_bytes = expect_token(pc, TokenIdIntLiteral); + child->data.pointer_type.bit_offset_start = token_bigint(bit_offset_start); + child->data.pointer_type.host_int_bytes = token_bigint(host_int_bytes); + } + expect_token(pc, TokenIdRParen); + continue; + } - return ast_parse_fn_proto_partial(pc, token_index, fn_token, async_allocator_type_node, cc, is_extern, visib_mod); -} + if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) { + child->data.pointer_type.is_const = true; + continue; + } -/* -FnDef = option("inline" | "export") FnProto Block -*/ -static AstNode *ast_parse_fn_def(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) { - Token *first_token = &pc->tokens->at(*token_index); - bool is_inline; - bool is_export; - if (first_token->id == TokenIdKeywordInline) { - *token_index += 1; - is_inline = true; - is_export = false; - } else if (first_token->id == TokenIdKeywordExport) { - *token_index += 1; - is_export = true; - is_inline = false; - } else { - is_inline = false; - is_export = false; - } + if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) { + child->data.pointer_type.is_volatile = true; + continue; + } - AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory, visib_mod); - if (!fn_proto) { - if (is_inline || is_export) { - *token_index -= 1; + break; } - return nullptr; - } - fn_proto->data.fn_proto.is_inline = is_inline; - fn_proto->data.fn_proto.is_export = is_export; - - Token *semi_token = &pc->tokens->at(*token_index); - if (semi_token->id == TokenIdSemicolon) { - *token_index += 1; - return fn_proto; + return ptr; } - AstNode *node = ast_create_node(pc, NodeTypeFnDef, first_token); - node->data.fn_def.fn_proto = fn_proto; - node->data.fn_def.body = ast_parse_block(pc, token_index, true); - fn_proto->data.fn_proto.fn_def_node = node; - return node; + return nullptr; } -/* -ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";" -*/ -static AstNode *ast_parse_extern_decl(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) { - Token *extern_kw = &pc->tokens->at(*token_index); - if (extern_kw->id != TokenIdKeywordExtern) { - if (mandatory) { - ast_expect_token(pc, extern_kw, TokenIdKeywordExtern); - } else { - return nullptr; - } - } - *token_index += 1; - - Token *lib_name_tok = &pc->tokens->at(*token_index); - Buf *lib_name = nullptr; - if (lib_name_tok->id == TokenIdStringLiteral) { - lib_name = token_buf(lib_name_tok); - *token_index += 1; - } +// SuffixOp +// <- LBRACKET Expr (DOT2 Expr?)? RBRACKET +// / DOT IDENTIFIER +// / DOTASTERISK +// / DOTQUESTIONMARK +static AstNode *ast_parse_suffix_op(ParseContext *pc) { + Token *lbracket = eat_token_if(pc, TokenIdLBracket); + if (lbracket != nullptr) { + AstNode *start = ast_expect(pc, ast_parse_expr); + AstNode *end = nullptr; + if (eat_token_if(pc, TokenIdEllipsis2) != nullptr) { + end = ast_parse_expr(pc); + expect_token(pc, TokenIdRBracket); - AstNode *fn_proto_node = ast_parse_fn_proto(pc, token_index, false, visib_mod); - if (fn_proto_node) { - ast_eat_token(pc, token_index, TokenIdSemicolon); + AstNode *res = ast_create_node(pc, NodeTypeSliceExpr, lbracket); + res->data.slice_expr.start = start; + res->data.slice_expr.end = end; + return res; + } - fn_proto_node->data.fn_proto.is_extern = true; - fn_proto_node->data.fn_proto.lib_name = lib_name; + expect_token(pc, TokenIdRBracket); - return fn_proto_node; + AstNode *res = ast_create_node(pc, NodeTypeArrayAccessExpr, lbracket); + res->data.array_access_expr.subscript = start; + return res; } - AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod, false, false); - if (var_decl_node) { - ast_eat_token(pc, token_index, TokenIdSemicolon); - - var_decl_node->data.variable_declaration.is_extern = true; - var_decl_node->data.variable_declaration.lib_name = lib_name; + Token *dot = eat_token_if(pc, TokenIdDot); + if (dot != nullptr) { + if (eat_token_if(pc, TokenIdStar) != nullptr) + return ast_create_node(pc, NodeTypePtrDeref, dot); + if (eat_token_if(pc, TokenIdQuestion) != nullptr) + return ast_create_node(pc, NodeTypeUnwrapOptional, dot); - return var_decl_node; + Token *ident = expect_token(pc, TokenIdSymbol); + AstNode *res = ast_create_node(pc, NodeTypeFieldAccessExpr, dot); + res->data.field_access_expr.field_name = token_buf(ident); + return res; } - Token *token = &pc->tokens->at(*token_index); - ast_invalid_token_error(pc, token); + return nullptr; } -/* -UseDecl = "use" Expression ";" -*/ -static AstNode *ast_parse_use(ParseContext *pc, size_t *token_index, VisibMod visib_mod) { - Token *use_kw = &pc->tokens->at(*token_index); - if (use_kw->id != TokenIdKeywordUse) +// AsyncPrefix <- KEYWORD_async (LARROW PrefixExpr RARROW)? +static AstNode *ast_parse_async_prefix(ParseContext *pc) { + Token *async = eat_token_if(pc, TokenIdKeywordAsync); + if (async == nullptr) return nullptr; - *token_index += 1; - - AstNode *node = ast_create_node(pc, NodeTypeUse, use_kw); - node->data.use.visib_mod = visib_mod; - node->data.use.expr = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdSemicolon); + AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, async); + res->data.fn_call_expr.is_async = true; + if (eat_token_if(pc, TokenIdCmpLessThan) != nullptr) { + AstNode *prefix_expr = ast_expect(pc, ast_parse_prefix_expr); + expect_token(pc, TokenIdCmpGreaterThan); + res->data.fn_call_expr.async_allocator = prefix_expr; + } - return node; + return res; } -/* -ContainerDecl = option("extern" | "packed") - ("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression))) - "{" many(ContainerMember) "}" -ContainerMember = (ContainerField | FnDef | GlobalVarDecl) -ContainerField = Symbol option(":" PrefixOpExpression option("=" PrefixOpExpression "," -*/ -static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *first_token = &pc->tokens->at(*token_index); - Token *container_kind_token; - - ContainerLayout layout; - if (first_token->id == TokenIdKeywordExtern) { - container_kind_token = &pc->tokens->at(*token_index + 1); - layout = ContainerLayoutExtern; - } else if (first_token->id == TokenIdKeywordPacked) { - container_kind_token = &pc->tokens->at(*token_index + 1); - layout = ContainerLayoutPacked; - } else { - container_kind_token = first_token; - layout = ContainerLayoutAuto; - } - - ContainerKind kind; - if (container_kind_token->id == TokenIdKeywordStruct) { - kind = ContainerKindStruct; - } else if (container_kind_token->id == TokenIdKeywordEnum) { - kind = ContainerKindEnum; - } else if (container_kind_token->id == TokenIdKeywordUnion) { - kind = ContainerKindUnion; - } else if (mandatory) { - ast_invalid_token_error(pc, container_kind_token); - } else { +// FnCallArgumnets <- LPAREN ExprList RPAREN +static AstNode *ast_parse_fn_call_argumnets(ParseContext *pc) { + Token *paren = eat_token_if(pc, TokenIdLParen); + if (paren == nullptr) return nullptr; - } - *token_index += (layout == ContainerLayoutAuto) ? 1 : 2; - - AstNode *node = ast_create_node(pc, NodeTypeContainerDecl, first_token); - node->data.container_decl.layout = layout; - node->data.container_decl.kind = kind; - - if (kind == ContainerKindUnion) { - Token *lparen_token = &pc->tokens->at(*token_index); - if (lparen_token->id == TokenIdLParen) { - Token *enum_token = &pc->tokens->at(*token_index + 1); - if (enum_token->id == TokenIdKeywordEnum) { - Token *paren_token = &pc->tokens->at(*token_index + 2); - if (paren_token->id == TokenIdLParen) { - node->data.container_decl.auto_enum = true; - *token_index += 2; - node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); - } else if (paren_token->id == TokenIdRParen) { - node->data.container_decl.auto_enum = true; - *token_index += 3; - } - } - } - } - if (!node->data.container_decl.auto_enum) { - node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, false); - } - - ast_eat_token(pc, token_index, TokenIdDot); - ast_eat_token(pc, token_index, TokenIdLBrace); - - for (;;) { - Token *visib_tok = &pc->tokens->at(*token_index); - VisibMod visib_mod; - if (visib_tok->id == TokenIdKeywordPub) { - *token_index += 1; - visib_mod = VisibModPub; - } else { - visib_mod = VisibModPrivate; - } - - AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false, visib_mod); - if (fn_def_node) { - node->data.container_decl.decls.append(fn_def_node); - continue; - } - AstNode *var_decl_node = ast_parse_global_var_decl(pc, token_index, visib_mod); - if (var_decl_node) { - ast_eat_token(pc, token_index, TokenIdSemicolon); - node->data.container_decl.decls.append(var_decl_node); - continue; - } - - Token *token = &pc->tokens->at(*token_index); - - if (token->id == TokenIdRBrace) { - *token_index += 1; - break; - } else if (token->id == TokenIdSymbol) { - AstNode *field_node = ast_create_node(pc, NodeTypeStructField, token); - *token_index += 1; - - node->data.container_decl.fields.append(field_node); - field_node->data.struct_field.visib_mod = visib_mod; - field_node->data.struct_field.name = token_buf(token); - - Token *colon_token = &pc->tokens->at(*token_index); - if (colon_token->id == TokenIdColon) { - *token_index += 1; - field_node->data.struct_field.type = ast_parse_type_expr(pc, token_index, true); - } - Token *eq_token = &pc->tokens->at(*token_index); - if (eq_token->id == TokenIdEq) { - *token_index += 1; - field_node->data.struct_field.value = ast_parse_expression(pc, token_index, true); - } - - Token *next_token = &pc->tokens->at(*token_index); - if (next_token->id == TokenIdComma) { - *token_index += 1; - continue; - } - - if (next_token->id == TokenIdRBrace) { - *token_index += 1; - break; - } + ZigList<AstNode *> params = ast_parse_list(pc, TokenIdComma, ast_parse_expr); + expect_token(pc, TokenIdRParen); - ast_invalid_token_error(pc, next_token); - } else { - ast_invalid_token_error(pc, token); - } - } - - return node; + AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, paren); + res->data.fn_call_expr.params = params; + return res; } -/* -TestDecl = "test" String Block -*/ -static AstNode *ast_parse_test_decl_node(ParseContext *pc, size_t *token_index) { - Token *first_token = &pc->tokens->at(*token_index); - - if (first_token->id != TokenIdKeywordTest) { +// ArrayTypeStart <- LBRACKET Expr? RBRACKET +static AstNode *ast_parse_array_type_start(ParseContext *pc) { + Token *lbracket = eat_token_if(pc, TokenIdLBracket); + if (lbracket == nullptr) return nullptr; - } - *token_index += 1; - - Token *name_tok = ast_eat_token(pc, token_index, TokenIdStringLiteral); - AstNode *node = ast_create_node(pc, NodeTypeTestDecl, first_token); - node->data.test_decl.name = token_buf(name_tok); - node->data.test_decl.body = ast_parse_block(pc, token_index, true); - - return node; + AstNode *size = ast_parse_expr(pc); + expect_token(pc, TokenIdRBracket); + AstNode *res = ast_create_node(pc, NodeTypeArrayType, lbracket); + res->data.array_type.size = size; + return res; } -/* -TopLevelItem = ErrorValueDecl | CompTimeExpression(Block) | TopLevelDecl | TestDecl -TopLevelDecl = option("pub") (FnDef | ExternDecl | GlobalVarDecl | UseDecl) -*/ -static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, ZigList<AstNode *> *top_level_decls) { - for (;;) { - AstNode *comptime_expr_node = ast_parse_comptime_expr(pc, token_index, true, false); - if (comptime_expr_node) { - top_level_decls->append(comptime_expr_node); - continue; - } +// PtrTypeStart +// <- ASTERISK +// / ASTERISK2 +// / LBRACKET ASTERISK RBRACKET +static AstNode *ast_parse_ptr_type_start(ParseContext *pc) { + Token *asterisk = eat_token_if(pc, TokenIdStar); + if (asterisk != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk); + res->data.pointer_type.star_token = asterisk; + return res; + } - AstNode *test_decl_node = ast_parse_test_decl_node(pc, token_index); - if (test_decl_node) { - top_level_decls->append(test_decl_node); - continue; - } + Token *asterisk2 = eat_token_if(pc, TokenIdStarStar); + if (asterisk2 != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk2); + AstNode *res2 = ast_create_node(pc, NodeTypePointerType, asterisk2); + res->data.pointer_type.star_token = asterisk2; + res2->data.pointer_type.star_token = asterisk2; + res->data.pointer_type.op_expr = res2; + return res; + } - Token *visib_tok = &pc->tokens->at(*token_index); - VisibMod visib_mod; - if (visib_tok->id == TokenIdKeywordPub) { - *token_index += 1; - visib_mod = VisibModPub; - } else { - visib_mod = VisibModPrivate; - } + Token *multptr = eat_token_if(pc, TokenIdBracketStarBracket); + if (multptr != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypePointerType, multptr); + res->data.pointer_type.star_token = multptr; + return res; + } - AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false, visib_mod); - if (fn_def_node) { - top_level_decls->append(fn_def_node); - continue; - } + return nullptr; +} - AstNode *fn_proto_node = ast_parse_extern_decl(pc, token_index, false, visib_mod); - if (fn_proto_node) { - top_level_decls->append(fn_proto_node); - continue; - } +// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE +static AstNode *ast_parse_container_decl_auto(ParseContext *pc) { + AstNode *res = ast_parse_container_decl_type(pc); + if (res == nullptr) + return nullptr; - AstNode *use_node = ast_parse_use(pc, token_index, visib_mod); - if (use_node) { - top_level_decls->append(use_node); - continue; - } + expect_token(pc, TokenIdLBrace); + AstNodeContainerDecl members = ast_parse_container_members(pc); + expect_token(pc, TokenIdRBrace); + + res->data.container_decl.fields = members.fields; + res->data.container_decl.decls = members.decls; + return res; +} + +// ContainerDeclType +// <- (KEYWORD_struct / KEYWORD_enum) (LPAREN Expr RPAREN)? +// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? +static AstNode *ast_parse_container_decl_type(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdKeywordStruct); + if (first == nullptr) + first = eat_token_if(pc, TokenIdKeywordEnum); + if (first != nullptr) { + AstNode *init_arg_expr = nullptr; + if (eat_token_if(pc, TokenIdLParen) != nullptr) { + init_arg_expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + } + AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first); + res->data.container_decl.init_arg_expr = init_arg_expr; + res->data.container_decl.kind = first->id == TokenIdKeywordStruct + ? ContainerKindStruct + : ContainerKindEnum; + return res; + } + + first = eat_token_if(pc, TokenIdKeywordUnion); + if (first != nullptr) { + AstNode *init_arg_expr = nullptr; + bool auto_enum = false; + if (eat_token_if(pc, TokenIdLParen) != nullptr) { + if (eat_token_if(pc, TokenIdKeywordEnum) != nullptr) { + auto_enum = true; + if (eat_token_if(pc, TokenIdLParen) != nullptr) { + init_arg_expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + } + } else { + init_arg_expr = ast_expect(pc, ast_parse_expr); + } - AstNode *var_decl_node = ast_parse_global_var_decl(pc, token_index, visib_mod); - if (var_decl_node) { - ast_eat_token(pc, token_index, TokenIdSemicolon); - top_level_decls->append(var_decl_node); - continue; + expect_token(pc, TokenIdRParen); } - return; + AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first); + res->data.container_decl.init_arg_expr = init_arg_expr; + res->data.container_decl.auto_enum = auto_enum; + res->data.container_decl.kind = ContainerKindUnion; + return res; } - zig_unreachable(); -} -/* -Root = many(TopLevelItem) "EOF" - */ -static AstNode *ast_parse_root(ParseContext *pc, size_t *token_index) { - AstNode *node = ast_create_node(pc, NodeTypeRoot, &pc->tokens->at(*token_index)); - - ast_parse_top_level_decls(pc, token_index, &node->data.root.top_level_decls); - - if (*token_index != pc->tokens->length - 1) { - ast_invalid_token_error(pc, &pc->tokens->at(*token_index)); - } - - return node; + return nullptr; } -AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner, - ErrColor err_color) -{ - ParseContext pc = {0}; - pc.void_buf = buf_create_from_str("void"); - pc.err_color = err_color; - pc.owner = owner; - pc.buf = buf; - pc.tokens = tokens; - size_t token_index = 0; - pc.root = ast_parse_root(&pc, &token_index); - return pc.root; +// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN +static AstNode *ast_parse_byte_align(ParseContext *pc) { + if (eat_token_if(pc, TokenIdKeywordAlign) == nullptr) + return nullptr; + + expect_token(pc, TokenIdLParen); + AstNode *res = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + return res; } static void visit_field(AstNode **node, void (*visit)(AstNode **, void *context), void *context) { @@ -2917,9 +2892,6 @@ static void visit_node_list(ZigList<AstNode *> *list, void (*visit)(AstNode **, void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context) { switch (node->type) { - case NodeTypeRoot: - visit_node_list(&node->data.root.top_level_decls, visit, context); - break; case NodeTypeFnProto: visit_field(&node->data.fn_proto.return_type, visit, context); visit_node_list(&node->data.fn_proto.params, visit, context); |
