From 8139c5a516eaa217ed76acdf09496895451c5c5c Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Tue, 13 Nov 2018 05:08:37 -0800 Subject: New Zig formal grammar (#1685) Reverted #1628 and changed the grammar+parser of the language to not allow certain expr where types are expected --- src/parser.cpp | 4704 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 2338 insertions(+), 2366 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 59e70713ab..01c4f45e1b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -16,54 +16,157 @@ struct ParseContext { Buf *buf; - AstNode *root; + size_t current_token; ZigList *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 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 ast_parse_ptr_payload(ParseContext *pc); +static Optional 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(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 +static ZigList ast_parse_list(ParseContext *pc, TokenId sep, T *(*parser)(ParseContext*)) { + ZigList 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 +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 *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; - } - 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; + ast_invalid_token_error(pc, peek_token(pc)); } -} -/* -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 *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; } + + return res; } + + ast_invalid_token_error(pc, peek_token(pc)); } - zig_unreachable(); -} -static void ast_parse_fn_call_param_list(ParseContext *pc, size_t *token_index, ZigList *params) { - for (;;) { - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdRParen) { - *token_index += 1; - return; - } + 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; + } - AstNode *expr = ast_parse_expression(pc, token_index, true); - params->append(expr); + 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); - token = &pc->tokens->at(*token_index); - *token_index += 1; - if (token->id == TokenIdRParen) { - return; - } else { - ast_expect_token(pc, token, TokenIdComma); + 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; } + + return res; } - zig_unreachable(); + + Token *use = eat_token_if(pc, TokenIdKeywordUse); + if (use != nullptr) { + AstNode *expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdSemicolon); + + AstNode *res = ast_create_node(pc, NodeTypeUse, use); + res->data.use.visib_mod = visib_mod; + res->data.use.expr = expr; + return res; + } + + return nullptr; } -/* -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); +// 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 { - return nullptr; + fn = expect_token(pc, TokenIdKeywordFn); } + } else { + fn_cc = {}; + fn = eat_token_if(pc, TokenIdKeywordFn); + if (fn == nullptr) + return nullptr; } - *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeGroupedExpr, l_paren); + Token *identifier = eat_token_if(pc, TokenIdSymbol); + expect_token(pc, TokenIdLParen); + ZigList 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_parse_type_expr(pc); + } + + 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 *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; - node->data.grouped_expr = ast_parse_expression(pc, token_index, true); + 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 *r_paren = &pc->tokens->at(*token_index); - *token_index += 1; - ast_expect_token(pc, r_paren, TokenIdRParen); + AstNode *assign = ast_parse_assign_expr(pc); + if (assign != nullptr) { + expect_token(pc, TokenIdSemicolon); + return assign; + } - return node; + return nullptr; } -/* -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; +// 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; + + 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; + } + + AstNode *loop = ast_parse_loop_statement(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; + if (label != nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + return nullptr; +} - AstNode *node = ast_create_node(pc, NodeTypeArrayType, l_bracket); - node->data.array_type.size = ast_parse_expression(pc, token_index, false); +// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) +static AstNode *ast_parse_loop_statement(ParseContext *pc) { + Token *label = ast_parse_block_label(pc); + Token *first = label; - ast_eat_token(pc, token_index, TokenIdRBracket); + Token *inline_token = eat_token_if(pc, TokenIdKeywordInline); + if (first == nullptr) + first = inline_token; - 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); + 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; + } - token = &pc->tokens->at(*token_index); + 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; } - if (token->id == TokenIdKeywordConst) { - *token_index += 1; - node->data.array_type.is_const = true; - token = &pc->tokens->at(*token_index); + if (first != nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + return nullptr; +} + +// 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 *body = ast_parse_block_expr(pc); + bool requires_semi = false; + if (body == nullptr) { + requires_semi = true; + body = ast_parse_assign_expr(pc); } - if (token->id == TokenIdKeywordVolatile) { - *token_index += 1; - node->data.array_type.is_volatile = true; + + AstNode *else_body = nullptr; + if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { + else_body = ast_expect(pc, ast_parse_statement); } - node->data.array_type.child_type = ast_parse_type_expr(pc, token_index, true); + if (requires_semi && else_body == nullptr) + expect_token(pc, TokenIdSemicolon); - return node; + assert(res->type == NodeTypeForExpr); + res->data.for_expr.body = body; + res->data.for_expr.else_node = else_body; + return res; } -/* -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); +// 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 *body = ast_parse_block_expr(pc); + bool requires_semi = false; + if (body == nullptr) { + requires_semi = true; + body = ast_parse_assign_expr(pc); + } - Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral); + 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); + } - ast_eat_token(pc, token_index, TokenIdLParen); - AstNode *expr_node = ast_parse_expression(pc, token_index, true); - ast_eat_token(pc, token_index, TokenIdRParen); + if (requires_semi && else_body == nullptr) + expect_token(pc, TokenIdSemicolon); - AsmInput *asm_input = allocate(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); + 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; } -/* -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 *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral); +// 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; - AsmOutput *asm_output = allocate(1); + AstNode *assign_expr = ast_parse_assign_expr(pc); + if (assign_expr != nullptr) { + expect_token(pc, TokenIdSemicolon); + return assign_expr; + } - ast_eat_token(pc, token_index, TokenIdLParen); + return nullptr; +} - 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); +// 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_eat_token(pc, token_index, TokenIdRParen); + return ast_parse_block(pc); +} - asm_output->asm_symbolic_name = token_buf(alias); - asm_output->constraint = token_buf(constraint); - node->data.asm_expr.output_list.append(asm_output); +// 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); } -/* -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); +// 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; + } - if (colon_tok->id != TokenIdColon) - return; + return (AstNode*)nullptr; + }, + ast_parse_bool_or_expr + ); +} - *token_index += 1; +// 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, + ast_parse_bool_and_expr + ); +} - for (;;) { - Token *string_tok = &pc->tokens->at(*token_index); - ast_expect_token(pc, string_tok, TokenIdStringLiteral); - *token_index += 1; +// 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, + ast_parse_compare_expr + ); +} - Buf *clobber_buf = token_buf(string_tok); - node->data.asm_expr.clobber_list.append(clobber_buf); +// 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); +} - Token *comma = &pc->tokens->at(*token_index); +// 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); +} - if (comma->id == TokenIdComma) { - *token_index += 1; +// 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); +} - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdRParen) { - break; - } else { - continue; - } - } else { - break; - } - } +// 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); } -/* -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; - } - - for (;;) { - ast_parse_asm_input_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) { - break; - } else { - continue; - } - } else { - break; - } - } - - ast_parse_asm_clobbers(pc, token_index, node); +// 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); } -/* -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; - - Token *colon_again = &pc->tokens->at(*token_index); - if (colon_again->id == TokenIdColon) { - ast_parse_asm_input(pc, token_index, node); - return; - } - - 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) { - break; - } else { - continue; - } - } else { - break; - } - } - - ast_parse_asm_input(pc, token_index, node); +// 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 + ); } -/* -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); - - *token_index += 1; - Token *lparen_tok = &pc->tokens->at(*token_index); - - if (lparen_tok->id == TokenIdKeywordVolatile) { - node->data.asm_expr.is_volatile = true; - - *token_index += 1; - lparen_tok = &pc->tokens->at(*token_index); - } - - 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; - } - - 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; - } -} - -/* -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 { - 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 *block_expr_node = ast_parse_block_expr(pc, token_index, false); - if (block_expr_node) { - return block_expr_node; - } - - 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; - } - - AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); - if (grouped_expr_node) { - return grouped_expr_node; - } - - AstNode *array_type_node = ast_parse_array_type_expr(pc, token_index, false); - if (array_type_node) { - return array_type_node; - } - - AstNode *fn_proto_node = ast_parse_fn_proto(pc, token_index, false, VisibModPrivate); - if (fn_proto_node) { - return fn_proto_node; - } - - AstNode *asm_expr = ast_parse_asm_expr(pc, token_index, false); - if (asm_expr) { +// 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; - } - - AstNode *container_decl = ast_parse_container_decl(pc, token_index, false); - if (container_decl) - return container_decl; - if (!mandatory) - return nullptr; + AstNode *if_expr = ast_parse_if_expr(pc); + if (if_expr != nullptr) + return if_expr; - ast_invalid_token_error(pc, token); -} + 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); -/* -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; + 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; } - 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; - } - } - } - } - - prefix_op_expr = node; - } else { - return prefix_op_expr; - } + 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; } -} - -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); - - if (fn_name->id == TokenIdSymbol) { - *token_index += 1; - node->data.fn_proto.name = token_buf(fn_name); - } else { - node->data.fn_proto.name = nullptr; + 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; } - 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); - - 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); + 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; } - if (next_token->id == TokenIdKeywordSection) { - *token_index += 1; - ast_eat_token(pc, token_index, TokenIdLParen); - 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; - } - } 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); + 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; } - return node; -} - -/* -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; + 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; } - 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; - - 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); - - primary_expr = node; - } else if (token->id == TokenIdStar) { - *token_index += 2; - - AstNode *node = ast_create_node(pc, NodeTypePtrDeref, first_token); - node->data.ptr_deref_expr.target = primary_expr; - - primary_expr = node; - } else if (token->id == TokenIdQuestion) { - *token_index += 2; + AstNode *labeled_expr = ast_parse_labeled_expr(pc); + if (labeled_expr != nullptr) + return labeled_expr; - AstNode *node = ast_create_node(pc, NodeTypeUnwrapOptional, first_token); - node->data.unwrap_optional.expr = primary_expr; + AstNode *curly_suffix = ast_parse_curly_suffix_expr(pc); + if (curly_suffix != nullptr) + return curly_suffix; - primary_expr = node; - } else { - return primary_expr; - } - } else { - return primary_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; - } -} - -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; - - 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); - - 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); - - 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); - } - 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; - } - - node->data.pointer_type.op_expr = ast_parse_prefix_op_expr(pc, token_index, true); - return node; + return nullptr; } -/* -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; - } - 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_index += 1; - - - AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token); - - 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; - - return node; +// 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; -} - -/* -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) - 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; - - AstNode *operand_2 = ast_parse_curly_suffix_expr(pc, token_index, true); - - 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; - - operand_1 = node; - } -} - -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; - } -} -/* -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; + if (label != nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + return nullptr; } -/* -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) +// 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 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); + ZigList statements = {}; + AstNode *statement; + while ((statement = ast_parse_statement(pc)) != nullptr) + statements.append(statement); - 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; + expect_token(pc, TokenIdRBrace); - operand_1 = node; - } + AstNode *res = ast_create_node(pc, NodeTypeBlock, lbrace); + res->data.block.statements = statements; + return res; } -static BinOpType tok_to_bit_shift_op(Token *token) { - switch (token->id) { - case TokenIdBitShiftLeft: return BinOpTypeBitShiftLeft; - case TokenIdBitShiftRight: return BinOpTypeBitShiftRight; - default: return BinOpTypeInvalid; - } +// 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 + ); } -/* -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; +// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? +static AstNode *ast_parse_for_expr(ParseContext *pc) { + return ast_parse_for_expr_helper(pc, ast_parse_expr); } -/* -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) - 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 *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; - - operand_1 = node; - } +// 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); } - -/* -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) +// 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); - if (token->id != TokenIdAmpersand) - return operand_1; - *token_index += 1; + AstNode *res = ast_parse_init_list(pc); + if (res == nullptr) + return type_expr; - AstNode *operand_2 = ast_parse_bit_shift_expr(pc, token_index, true); - - 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; - - operand_1 = node; - } + assert(res->type == NodeTypeContainerInitExpr); + res->data.container_init_expr.type = type_expr; + return res; } -/* -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) +// 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); - if (token->id != TokenIdBinXor) - return operand_1; - *token_index += 1; - - AstNode *operand_2 = ast_parse_bin_and_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 = BinOpTypeBinXor; - 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; } -} - -/* -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) - return nullptr; - while (true) { - Token *token = &pc->tokens->at(*token_index); - if (token->id != TokenIdBinOr) - return operand_1; - *token_index += 1; + AstNode *res = ast_create_node(pc, NodeTypeContainerInitExpr, lbrace); + res->data.container_init_expr.kind = ContainerInitKindArray; - AstNode *operand_2 = ast_parse_bin_xor_expr(pc, token_index, true); + first = ast_parse_expr(pc); + if (first != nullptr) { + 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 = BinOpTypeBinOr; - node->data.bin_op_expr.op2 = operand_2; + 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); + } - operand_1 = node; + expect_token(pc, TokenIdRBrace); + return res; } -} -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; - } + expect_token(pc, TokenIdRBrace); + 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_index += 1; - return result; +// 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 + ); } -/* -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) +// 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; - 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 *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; - - return node; -} - -/* -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; - - while (true) { - Token *token = &pc->tokens->at(*token_index); - if (token->id != TokenIdKeywordAnd) - return operand_1; - *token_index += 1; + AstNode *op = ast_parse_bin_op_simple(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_comparison_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 = BinOpTypeBoolAnd; - 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; } -} -/* -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 { + AstNode *res = ast_parse_primary_type_expr(pc); + if (res == nullptr) return nullptr; - } - 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 *star_tok = &pc->tokens->at(*token_index); - if (star_tok->id == TokenIdStar) { - *token_index += 1; - var_is_ptr = true; + while (true) { + 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; } - var_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); - - ast_eat_token(pc, token_index, TokenIdBinOr); - } - - AstNode *body_node = ast_parse_block_or_expression(pc, token_index, true); - - 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 *else_bar_tok = &pc->tokens->at(*token_index); - if (else_bar_tok->id == TokenIdBinOr) { - *token_index += 1; - - err_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); - - ast_eat_token(pc, token_index, TokenIdBinOr); + 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; } - else_node = ast_parse_block_expr_or_expression(pc, token_index, true); - } - - 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); + break; + } + + return res; + +} + +// PrimaryTypeExpr +// <- BUILTININDENTIFIER FnCallArgumnets +// / CHAR_LITERAL +// / ContainerDecl +// / ErrorSetDecl +// / FLOAT +// / FnProto +// / GroupedExpr +// / IDENTIFIER +// / IfTypeExpr +// / INTEGER +// / KEYWORD_anyerror +// / KEYWORD_comptime TypeExpr +// / KEYWORD_error DOT IDENTIFIER +// / KEYWORD_false +// / KEYWORD_null +// / KEYWORD_promise +// / KEYWORD_true +// / KEYWORD_undefined +// / KEYWORD_unreachable +// / LabeledTypeExpr +// / 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"); } - 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; - } -} -/* -ReturnExpression : "return" option(Expression) -*/ -static AstNode *ast_parse_return_expr(ParseContext *pc, size_t *token_index) { - Token *token = &pc->tokens->at(*token_index); + 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; - if (token->id != TokenIdKeywordReturn) { - return nullptr; + 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; } - *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); - return node; -} - -/* -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) { - return nullptr; + 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; - - 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); - return node; -} + AstNode *container_decl = ast_parse_container_decl(pc); + if (container_decl != nullptr) + return container_decl; -/* -AwaitExpression : "await" Expression -*/ -static AstNode *ast_parse_await_expr(ParseContext *pc, size_t *token_index) { - Token *token = &pc->tokens->at(*token_index); + AstNode *error_set_decl = ast_parse_error_set_decl(pc); + if (error_set_decl != nullptr) + return error_set_decl; - if (token->id != TokenIdKeywordAwait) { - return nullptr; + 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; } - *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeAwaitExpr, token); - node->data.await_expr.expr = ast_parse_expression(pc, token_index, true); + AstNode *fn_proto = ast_parse_fn_proto(pc); + if (fn_proto != nullptr) + return fn_proto; - return node; -} + AstNode *grouped_expr = ast_parse_grouped_expr(pc); + if (grouped_expr != nullptr) + return grouped_expr; -/* -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); + Token *identifier = eat_token_if(pc, TokenIdSymbol); + if (identifier != nullptr) + return token_symbol(pc, identifier); - if (token->id == TokenIdKeywordBreak) { - *token_index += 1; - } else { - return nullptr; - } - AstNode *node = ast_create_node(pc, NodeTypeBreak, token); + AstNode *if_type_expr = ast_parse_if_type_expr(pc); + if (if_type_expr != nullptr) + return if_type_expr; - 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); + 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; } - node->data.break_expr.expr = ast_parse_expression(pc, token_index, false); - - return node; -} - -/* -CancelExpression = "cancel" Expression; -*/ -static AstNode *ast_parse_cancel_expr(ParseContext *pc, size_t *token_index) { - Token *token = &pc->tokens->at(*token_index); + Token *error_type = eat_token_if(pc, TokenIdKeywordAnyerror); + if (error_type != nullptr) + return ast_create_node(pc, NodeTypeErrorType, error_type); - if (token->id != TokenIdKeywordCancel) { - return nullptr; + 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; } - *token_index += 1; - - AstNode *node = ast_create_node(pc, NodeTypeCancel, token); - - node->data.cancel_expr.expr = ast_parse_expression(pc, token_index, false); - return node; -} - -/* -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) { - return nullptr; + 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; } - *token_index += 1; - - AstNode *node = ast_create_node(pc, NodeTypeResume, token); - node->data.resume_expr.expr = ast_parse_expression(pc, token_index, false); - - return node; -} - -/* -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 { - return nullptr; + 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 *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); + Token *null = eat_token_if(pc, TokenIdKeywordNull); + if (null != nullptr) + return ast_create_node(pc, NodeTypeNullLiteral, null); - return node; -} + Token *promise = eat_token_if(pc, TokenIdKeywordPromise); + if (promise != nullptr) + return ast_create_node(pc, NodeTypePromiseType, promise); -/* -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 { - return nullptr; + 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; } - AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, var_token); + Token *undefined = eat_token_if(pc, TokenIdKeywordUndefined); + if (undefined != nullptr) + return ast_create_node(pc, NodeTypeUndefinedLiteral, undefined); - 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; + Token *unreachable = eat_token_if(pc, TokenIdKeywordUnreachable); + if (unreachable != nullptr) + return ast_create_node(pc, NodeTypeUnreachable, unreachable); - Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); - node->data.variable_declaration.symbol = token_buf(name_token); - - Token *next_token = &pc->tokens->at(*token_index); - - 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); - } - - 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); - } - - 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); - } + AstNode *labeled_type_expr = ast_parse_labeled_type_expr(pc); + if (labeled_type_expr != nullptr) + return labeled_type_expr; - 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); + 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; } - // peek ahead and ensure that all variable declarations are followed by a semicolon - ast_expect_token(pc, next_token, TokenIdSemicolon); + AstNode *switch_expr = ast_parse_switch_expr(pc); + if (switch_expr != nullptr) + return switch_expr; - return node; + return nullptr; } -/* -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; - } - - 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; +// 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; } - return nullptr; - } - return node; -} - -/* -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); - bool is_comptime = false;; - if (first_token->id == TokenIdKeywordCompTime) { - *token_index += 1; - is_comptime = true; + assert(res->type == NodeTypeContainerDecl); + res->line = extern_token->start_line; + res->column = extern_token->start_column; + res->data.container_decl.layout = ContainerLayoutExtern; + return res; } - 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; + 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; -} - -/* -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) - 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); - 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; - - operand_1 = node; - } + return ast_parse_container_decl_auto(pc); } -/* -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; - } - } - - bool is_inline = false; - if (token->id == TokenIdKeywordInline) { - is_inline = true; - *token_index += 1; - token = &pc->tokens->at(*token_index); - } - - 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; +// 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; } - AstNode *node = ast_create_node(pc, NodeTypeWhileExpr, while_token); - if (name_token != nullptr) { - node->data.while_expr.name = token_buf(name_token); - } - 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); - - Token *open_bar_tok = &pc->tokens->at(*token_index); - if (open_bar_tok->id == TokenIdBinOr) { - *token_index += 1; - - 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 *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; + ZigList decls = ast_parse_list(pc, TokenIdComma, [](ParseContext *context) { + Token *ident = eat_token_if(context, TokenIdSymbol); + if (ident == nullptr) + return (AstNode*)nullptr; - Token *else_bar_tok = &pc->tokens->at(*token_index); - if (else_bar_tok->id == TokenIdBinOr) { - *token_index += 1; + return token_symbol(context, ident); + }); + expect_token(pc, TokenIdRBrace); - Token *err_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); - node->data.while_expr.err_symbol = token_buf(err_name_tok); + AstNode *res = ast_create_node(pc, NodeTypeErrorSetDecl, first); + res->data.err_set_decl.decls = decls; + return res; +} - ast_eat_token(pc, token_index, TokenIdBinOr); - } +// GroupedExpr <- LPAREN Expr RPAREN +static AstNode *ast_parse_grouped_expr(ParseContext *pc) { + Token *lparen = eat_token_if(pc, TokenIdLParen); + if (lparen == nullptr) + return nullptr; - node->data.while_expr.else_node = ast_parse_block_or_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; } -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; +// 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); } -/* -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; +// 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; } } - bool is_inline = false; - if (token->id == TokenIdKeywordInline) { - is_inline = true; - *token_index += 1; - token = &pc->tokens->at(*token_index); - } - - 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; - } - - AstNode *node = ast_create_node(pc, NodeTypeForExpr, for_token); - if (name_token != nullptr) { - node->data.for_expr.name = token_buf(name_token); + 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.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); + if (label != nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + return nullptr; +} - Token *maybe_bar = &pc->tokens->at(*token_index); - if (maybe_bar->id == TokenIdBinOr) { - *token_index += 1; +// 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 + ); +} - Token *maybe_star = &pc->tokens->at(*token_index); - if (maybe_star->id == TokenIdStar) { - *token_index += 1; - node->data.for_expr.elem_is_ptr = true; - } +// 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); +} - node->data.for_expr.elem_node = ast_parse_symbol(pc, token_index); +// 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); +} - Token *maybe_comma = &pc->tokens->at(*token_index); - if (maybe_comma->id == TokenIdComma) { - *token_index += 1; +// 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.for_expr.index_node = ast_parse_symbol(pc, token_index); - } + expect_token(pc, TokenIdLParen); + AstNode *expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + expect_token(pc, TokenIdLBrace); + ZigList prongs = ast_parse_list(pc, TokenIdComma, ast_parse_switch_prong); + expect_token(pc, TokenIdRBrace); - ast_eat_token(pc, token_index, TokenIdBinOr); - } + AstNode *res = ast_create_node(pc, NodeTypeSwitchExpr, switch_token); + res->data.switch_expr.expr = expr; + res->data.switch_expr.prongs = prongs; + return res; +} - node->data.for_expr.body = ast_parse_block_or_expression(pc, token_index, true); +// 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 *else_tok = &pc->tokens->at(*token_index); - if (else_tok->id == TokenIdKeywordElse) { - *token_index += 1; + 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.for_expr.else_node = ast_parse_block_or_expression(pc, token_index, true); - } + ZigList 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; } -/* -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 { +// 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; - } - - AstNode *node = ast_create_node(pc, NodeTypeSwitchExpr, switch_token); - 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); + Token *sym_name = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdRBracket); - for (;;) { - Token *token = &pc->tokens->at(*token_index); + Token *str = expect_token(pc, TokenIdStringLiteral); + expect_token(pc, TokenIdLParen); - if (token->id == TokenIdRBrace) { - *token_index += 1; + 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); + } - return node; - } + expect_token(pc, TokenIdRParen); - AstNode *prong_node = ast_create_node(pc, NodeTypeSwitchProng, token); - node->data.switch_expr.prongs.append(prong_node); + AsmOutput *res = allocate(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; +} - 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; +// AsmInput <- COLON AsmInputList AsmCloppers? +static AstNode *ast_parse_asm_input(ParseContext *pc) { + if (eat_token_if(pc, TokenIdColon) == nullptr) + return nullptr; - AstNode *range_node = ast_create_node(pc, NodeTypeSwitchRange, ellipsis_tok); - prong_node->data.switch_prong.items.append(range_node); + ZigList 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); - range_node->data.switch_range.start = expr1; - range_node->data.switch_range.end = ast_parse_expression(pc, token_index, true); + res->data.asm_expr.input_list = input_list; + return res; +} - 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; +// 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 *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdFatArrow) { - break; - } else { - continue; - } - } - break; - } + 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); - ast_eat_token(pc, token_index, TokenIdFatArrow); + AsmInput *res = allocate(1); + res->asm_symbolic_name = token_buf(sym_name); + res->constraint = token_buf(constraint); + res->expr = expr; + return res; +} - Token *maybe_bar = &pc->tokens->at(*token_index); - if (maybe_bar->id == TokenIdBinOr) { - *token_index += 1; +// AsmCloppers <- COLON StringList +static AstNode *ast_parse_asm_cloppers(ParseContext *pc) { + if (eat_token_if(pc, TokenIdColon) == nullptr) + return nullptr; - 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); - } + ZigList clobber_list = ast_parse_list(pc, TokenIdComma, [](ParseContext *context) { + Token *str = eat_token_if(context, TokenIdStringLiteral); + if (str != nullptr) + return token_buf(str); + return (Buf*)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); - } + AstNode *res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); + res->data.asm_expr.clobber_list = clobber_list; + return res; +} - prong_node->data.switch_prong.expr = ast_parse_expression(pc, token_index, true); +// BreakLabel <- COLON IDENTIFIER +static Token *ast_parse_break_label(ParseContext *pc) { + if (eat_token_if(pc, TokenIdColon) == nullptr) + return nullptr; - Token *trailing_token = &pc->tokens->at(*token_index); - if (trailing_token->id == TokenIdRBrace) { - *token_index += 1; + return expect_token(pc, TokenIdSymbol); +} - return node; - } else { - ast_eat_token(pc, token_index, TokenIdComma); - } +// BlockLabel <- IDENTIFIER COLON +static Token *ast_parse_block_label(ParseContext *pc) { + Token *ident = eat_token_if(pc, TokenIdSymbol); + if (ident == nullptr) + return nullptr; + // 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 ident; } -/* -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); +// 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; - AstNode *if_expr = ast_parse_if_try_test_expr(pc, token_index, false); - if (if_expr) - return if_expr; + Token *name = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdEq); + AstNode *expr = ast_expect(pc, ast_parse_expr); - AstNode *while_expr = ast_parse_while_expr(pc, token_index, false); - if (while_expr) - return while_expr; + 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; +} - AstNode *for_expr = ast_parse_for_expr(pc, token_index, false); - if (for_expr) - return for_expr; +// 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; - AstNode *switch_expr = ast_parse_switch_expr(pc, token_index, false); - if (switch_expr) - return switch_expr; + expect_token(pc, TokenIdLParen); + AstNode *expr = ast_expect(pc, ast_parse_assign_expr); + expect_token(pc, TokenIdRParen); + return expr; +} - AstNode *block = ast_parse_block(pc, token_index, false); - if (block) - return block; +// Section <- KEYWORD_section LPAREN Expr RPAREN +static AstNode *ast_parse_section(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdKeywordSection); + if (first == nullptr) + return nullptr; - AstNode *comptime_node = ast_parse_comptime_expr(pc, token_index, false, false); - if (comptime_node) - return comptime_node; + expect_token(pc, TokenIdLParen); + AstNode *res = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + return res; +} - AstNode *suspend_node = ast_parse_suspend_block(pc, token_index, false); - if (suspend_node) - return suspend_node; +// FnCC +// <- KEYWORD_nakedcc +// / KEYWORD_stdcallcc +// / KEYWORD_extern +// / KEYWORD_async (LARROW TypeExpr RARROW)? +static Optional ast_parse_fn_cc(ParseContext *pc) { + AstNodeFnProto res = {}; + if (eat_token_if(pc, TokenIdKeywordNakedCC) != nullptr) { + res.cc = CallingConventionNaked; + return Optional::some(res); + } + if (eat_token_if(pc, TokenIdKeywordStdcallCC) != nullptr) { + res.cc = CallingConventionStdcall; + return Optional::some(res); + } + if (eat_token_if(pc, TokenIdKeywordExtern) != nullptr) { + res.cc = CallingConventionC; + return Optional::some(res); + } + if (eat_token_if(pc, TokenIdKeywordAsync) != nullptr) { + res.cc = CallingConventionAsync; + if (eat_token_if(pc, TokenIdCmpLessThan) == nullptr) + return Optional::some(res); - if (mandatory) - ast_invalid_token_error(pc, token); + res.async_allocator_type = ast_expect(pc, ast_parse_type_expr); + expect_token(pc, TokenIdCmpGreaterThan); + return Optional::some(res); + } - return nullptr; + return Optional::none(); } -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; - } -} +// 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); -/* -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); + Token *name = eat_token_if(pc, TokenIdSymbol); + if (name != nullptr) { + if (eat_token_if(pc, TokenIdColon) != nullptr) { + if (first == nullptr) + first = name; } else { - return BinOpTypeInvalid; + // We put back the ident, so it can be parsed as a ParamType + // later. + put_back_token(pc); + name = nullptr; } } - *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) + AstNode *res; + if (first == nullptr) { + first = peek_token(pc); + res = ast_parse_param_type(pc); + } else { + res = ast_expect(pc, ast_parse_param_type); + } + + if (res == nullptr) return nullptr; - Token *token = &pc->tokens->at(*token_index); + 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; +} - if (token->id == TokenIdKeywordOrElse) { - *token_index += 1; +// 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; + } - AstNode *rhs = ast_parse_expression(pc, token_index, true); + 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, NodeTypeBinOpExpr, token); - node->data.bin_op_expr.op1 = lhs; - node->data.bin_op_expr.bin_op = BinOpTypeUnwrapOptional; - node->data.bin_op_expr.op2 = rhs; + 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; + } - return node; - } else if (token->id == TokenIdKeywordCatch) { - *token_index += 1; + return nullptr; +} - AstNode *node = ast_create_node(pc, NodeTypeUnwrapErrorExpr, token); - node->data.unwrap_err_expr.op1 = lhs; +// 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_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); + expect_token(pc, TokenIdLParen); + AstNode *condition = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + Optional opt_payload = ast_parse_ptr_payload(pc); - return node; - } else { - return lhs; + 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; } -/* -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) +// 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 *token = &pc->tokens->at(*token_index); - BinOpType ass_op = ast_parse_ass_op(pc, token_index, false); - if (ass_op == BinOpTypeInvalid) - return lhs; - - AstNode *rhs = ast_parse_unwrap_expr(pc, token_index, true); + expect_token(pc, TokenIdLParen); + AstNode *condition = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + Optional opt_payload = ast_parse_ptr_payload(pc); + AstNode *continue_expr = ast_parse_while_continue_expr(pc); - 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; + 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; } -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; +// 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; - return ast_parse_expression(pc, token_index, mandatory); -} + 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)); -/* -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; + 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); - return ast_parse_expression(pc, token_index, mandatory); + return res; } -/* -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; +// Payload <- PIPE IDENTIFIER PIPE +static Token *ast_parse_payload(ParseContext *pc) { + if (eat_token_if(pc, TokenIdBinOr) == nullptr) + return nullptr; - AstNode *try_expr = ast_parse_try_expr(pc, token_index); - if (try_expr) - return try_expr; + Token *res = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdBinOr); + return res; +} - AstNode *break_expr = ast_parse_break_expr(pc, token_index); - if (break_expr) - return break_expr; +// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE +static Optional ast_parse_ptr_payload(ParseContext *pc) { + if (eat_token_if(pc, TokenIdBinOr) == nullptr) + return Optional::none(); - AstNode *cancel_expr = ast_parse_cancel_expr(pc, token_index); - if (cancel_expr) - return cancel_expr; + Token *asterisk = eat_token_if(pc, TokenIdStar); + Token *payload = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdBinOr); - AstNode *resume_expr = ast_parse_resume_expr(pc, token_index); - if (resume_expr) - return resume_expr; + PtrPayload res; + res.asterisk = asterisk; + res.payload = payload; + return Optional::some(res); +} - AstNode *ass_expr = ast_parse_ass_expr(pc, token_index, false); - if (ass_expr) - return ass_expr; +// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE +static Optional ast_parse_ptr_index_payload(ParseContext *pc) { + if (eat_token_if(pc, TokenIdBinOr) == nullptr) + return Optional::none(); - if (mandatory) - ast_invalid_token_error(pc, token); + 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); - return nullptr; + PtrIndexPayload res; + res.asterisk = asterisk; + res.payload = payload; + res.index = index; + return Optional::some(res); } -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; - } -} +// 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; -/* -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; - } - } + expect_token(pc, TokenIdFatArrow); + Optional opt_payload = ast_parse_ptr_payload(pc); + AstNode *expr = ast_expect(pc, ast_parse_assign_expr); - if (last_token->id != TokenIdLBrace) { - if (mandatory) { - ast_expect_token(pc, last_token, TokenIdLBrace); - } else { - *token_index = orig_token_index; - return nullptr; - } + 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; } - *token_index += 1; - AstNode *node = ast_create_node(pc, NodeTypeBlock, last_token); - if (name_token != nullptr) { - node->data.block.name = token_buf(name_token); - } + return res; +} - for (;;) { - last_token = &pc->tokens->at(*token_index); - if (last_token->id == TokenIdRBrace) { - *token_index += 1; - return node; - } +// 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; - 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); + while (eat_token_if(pc, TokenIdComma) != nullptr) { + AstNode *item = ast_parse_switch_item(pc); + if (item == nullptr) + break; - if (!statement_node) { - ast_invalid_token_error(pc, last_token); + res->data.switch_prong.items.append(item); + res->data.switch_prong.any_items_are_range |= item->type == NodeTypeSwitchRange; } - node->data.block.statements.append(statement_node); - - if (!statement_terminates_without_semicolon(statement_node)) { - ast_eat_token(pc, token_index, TokenIdSemicolon); - } + return res; } - zig_unreachable(); + + Token *else_token = eat_token_if(pc, TokenIdKeywordElse); + if (else_token != nullptr) + return ast_create_node(pc, NodeTypeSwitchProng, else_token); + + return nullptr; } -/* -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 { +// SwitchItem <- Expr (DOT3 Expr)? +static AstNode *ast_parse_switch_item(ParseContext *pc) { + AstNode *expr = ast_parse_expr(pc); + if (expr == nullptr) return nullptr; + + 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; } - return ast_parse_fn_proto_partial(pc, token_index, fn_token, async_allocator_type_node, cc, is_extern, visib_mod); + return nullptr; + } -/* -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; +// 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; + + 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 *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory, visib_mod); - if (!fn_proto) { - if (is_inline || is_export) { - *token_index -= 1; - } - return nullptr; + return nullptr; +} + +// 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; + + 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; } - fn_proto->data.fn_proto.is_inline = is_inline; - fn_proto->data.fn_proto.is_export = is_export; + 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); - Token *semi_token = &pc->tokens->at(*token_index); - if (semi_token->id == TokenIdSemicolon) { - *token_index += 1; - return fn_proto; + return res; } - 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; +// BitShiftOp +// <- LARROW2 +// / RARROW2 +static AstNode *ast_parse_bit_shift_op(ParseContext *pc) { + BinOpType table[TokenIdCount] = {}; + table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft; + table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight; - 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; + 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 *fn_proto_node = ast_parse_fn_proto(pc, token_index, false, visib_mod); - if (fn_proto_node) { - ast_eat_token(pc, token_index, TokenIdSemicolon); - - fn_proto_node->data.fn_proto.is_extern = true; - fn_proto_node->data.fn_proto.lib_name = lib_name; + return nullptr; +} - return fn_proto_node; +// 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; } - 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; + return nullptr; +} - return var_decl_node; +// 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; } - 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) - return nullptr; - *token_index += 1; +// 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; + + 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 *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); + 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; + } - ast_eat_token(pc, token_index, TokenIdSemicolon); + Token *await = eat_token_if(pc, TokenIdKeywordAwait); + if (await != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeAwaitExpr, await); + return res; + } - return node; + return nullptr; } -/* -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 { - 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; } - *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; - } - } + + 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; } - } - if (!node->data.container_decl.auto_enum) { - node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, false); + + put_back_token(pc); } - ast_eat_token(pc, token_index, TokenIdDot); - ast_eat_token(pc, token_index, TokenIdLBrace); + 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; + } - 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; - } + if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) { + array->data.array_type.is_const = true; + continue; + } - 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; - } + if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) { + array->data.array_type.is_volatile = true; + 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; + break; } - 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); + 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; } - Token *next_token = &pc->tokens->at(*token_index); - if (next_token->id == TokenIdComma) { - *token_index += 1; + if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) { + child->data.pointer_type.is_const = true; continue; } - if (next_token->id == TokenIdRBrace) { - *token_index += 1; - break; + if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) { + child->data.pointer_type.is_volatile = true; + continue; } - ast_invalid_token_error(pc, next_token); - } else { - ast_invalid_token_error(pc, token); + break; } + + return ptr; } - return node; + return nullptr; } -/* -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); +// 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); - if (first_token->id != TokenIdKeywordTest) { - return nullptr; + AstNode *res = ast_create_node(pc, NodeTypeSliceExpr, lbracket); + res->data.slice_expr.start = start; + res->data.slice_expr.end = end; + return res; + } + + expect_token(pc, TokenIdRBracket); + + AstNode *res = ast_create_node(pc, NodeTypeArrayAccessExpr, lbracket); + res->data.array_access_expr.subscript = start; + return res; } - *token_index += 1; - Token *name_tok = ast_eat_token(pc, token_index, TokenIdStringLiteral); + 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); - 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); + 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; + } - return node; + return nullptr; } -/* -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 *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; - } +// 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; - AstNode *test_decl_node = ast_parse_test_decl_node(pc, token_index); - if (test_decl_node) { - top_level_decls->append(test_decl_node); - continue; - } + 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; + } - 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; - } + 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; - } +// 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; - 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; - } + ZigList params = ast_parse_list(pc, TokenIdComma, ast_parse_expr); + expect_token(pc, TokenIdRParen); - AstNode *use_node = ast_parse_use(pc, token_index, visib_mod); - if (use_node) { - top_level_decls->append(use_node); - continue; - } + AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, paren); + res->data.fn_call_expr.params = params; + return res; +} - 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; - } +// 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; + + 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; +} + +// 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; + } - return; + 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; } - zig_unreachable(); + + 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; + } + + return nullptr; } -/* -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)); +// 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; + + 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); + } - ast_parse_top_level_decls(pc, token_index, &node->data.root.top_level_decls); + expect_token(pc, TokenIdRParen); + } - if (*token_index != pc->tokens->length - 1) { - ast_invalid_token_error(pc, &pc->tokens->at(*token_index)); + 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; } - return node; + return nullptr; } -AstNode *ast_parse(Buf *buf, ZigList *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 *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); -- cgit v1.2.3 From 5b3f7a8e1f5cc0448d4100dc98f947a7cd7c5723 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Tue, 13 Nov 2018 14:37:03 +0100 Subject: Fixed error where we didn't expect the return type of a function --- src/parser.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/parser.cpp') diff --git a/src/parser.cpp b/src/parser.cpp index 01c4f45e1b..0864e7ed1b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -812,7 +812,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) { AstNode *return_type = nullptr; if (var == nullptr) { exmark = eat_token_if(pc, TokenIdBang); - return_type = ast_parse_type_expr(pc); + return_type = ast_expect(pc, ast_parse_type_expr); } AstNode *res = ast_create_node(pc, NodeTypeFnProto, first); @@ -1606,6 +1606,7 @@ static AstNode *ast_parse_suffix_expr(ParseContext *pc) { // / FLOAT // / FnProto // / GroupedExpr +// / LabeledTypeExpr // / IDENTIFIER // / IfTypeExpr // / INTEGER @@ -1618,7 +1619,6 @@ static AstNode *ast_parse_suffix_expr(ParseContext *pc) { // / KEYWORD_true // / KEYWORD_undefined // / KEYWORD_unreachable -// / LabeledTypeExpr // / STRINGLITERAL // / SwitchExpr static AstNode *ast_parse_primary_type_expr(ParseContext *pc) { @@ -1682,6 +1682,10 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) { if (grouped_expr != nullptr) return grouped_expr; + AstNode *labeled_type_expr = ast_parse_labeled_type_expr(pc); + if (labeled_type_expr != nullptr) + return labeled_type_expr; + Token *identifier = eat_token_if(pc, TokenIdSymbol); if (identifier != nullptr) return token_symbol(pc, identifier); @@ -1750,10 +1754,6 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) { if (unreachable != nullptr) return ast_create_node(pc, NodeTypeUnreachable, unreachable); - AstNode *labeled_type_expr = ast_parse_labeled_type_expr(pc); - if (labeled_type_expr != nullptr) - return labeled_type_expr; - Token *string_lit = eat_token_if(pc, TokenIdStringLiteral); if (string_lit != nullptr) { AstNode *res = ast_create_node(pc, NodeTypeStringLiteral, string_lit); -- cgit v1.2.3 From 704374e51294e14285b0b54030c1cb6154868098 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 17 Nov 2018 01:38:35 -0500 Subject: rename `section` keyword to `linksection` add zig fmt support for this syntax closes #1152 --- doc/docgen.zig | 2 +- doc/langref.html.in | 444 ++++++++++++++++++++++++------------------------ src/parser.cpp | 2 +- src/tokenizer.cpp | 4 +- src/tokenizer.hpp | 2 +- std/zig/ast.zig | 12 ++ std/zig/parse.zig | 37 +++- std/zig/parser_test.zig | 7 + std/zig/render.zig | 28 ++- std/zig/tokenizer.zig | 4 +- test/compile_errors.zig | 12 +- 11 files changed, 314 insertions(+), 240 deletions(-) (limited to 'src/parser.cpp') diff --git a/doc/docgen.zig b/doc/docgen.zig index 498719d774..2489e034bc 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -764,7 +764,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok std.zig.Token.Id.Keyword_pub, std.zig.Token.Id.Keyword_resume, std.zig.Token.Id.Keyword_return, - std.zig.Token.Id.Keyword_section, + std.zig.Token.Id.Keyword_linksection, std.zig.Token.Id.Keyword_stdcallcc, std.zig.Token.Id.Keyword_struct, std.zig.Token.Id.Keyword_suspend, diff --git a/doc/langref.html.in b/doc/langref.html.in index c61203dca6..a29b9be4c8 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7713,36 +7713,35 @@ fn readU32Be() u32 {}

For some discussion on the rationale behind these design decisions, see issue #663

{#header_close#} {#header_open|Grammar#} -

-Root <- skip ContainerMembers eof
+      
Root <- skip ContainerMembers eof
 
 # *** Top level ***
 ContainerMembers
-    <- TestDecl ContainerMembers
+    <- TestDecl ContainerMembers
      / TopLevelComptime ContainerMembers
      / KEYWORD_pub? TopLevelDecl ContainerMembers
      / KEYWORD_pub? ContainerField COMMA ContainerMembers
      / KEYWORD_pub? ContainerField
      /
 
-TestDecl <- KEYWORD_test STRINGLITERAL Block
+TestDecl <- KEYWORD_test STRINGLITERAL Block
 
-TopLevelComptime <- KEYWORD_comptime BlockExpr
+TopLevelComptime <- KEYWORD_comptime BlockExpr
 
 TopLevelDecl
-    <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / KEYWORD_inline)? FnProto (SEMICOLON / Block)
+    <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / KEYWORD_inline)? FnProto (SEMICOLON / Block)
      / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? VarDecl
      / KEYWORD_use Expr SEMICOLON
 
-FnProto <- FnCC? KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? Section? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)
+FnProto <- FnCC? KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)
 
-VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? Section? (EQUAL Expr)? SEMICOLON
+VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
 
-ContainerField <- IDENTIFIER (COLON TypeExpr)? (EQUAL Expr)?
+ContainerField <- IDENTIFIER (COLON TypeExpr)? (EQUAL Expr)?
 
 # *** Block Level ***
 Statement
-    <- KEYWORD_comptime? VarDecl
+    <- KEYWORD_comptime? VarDecl
      / KEYWORD_comptime BlockExprStatement
      / KEYWORD_suspend (SEMICOLON / BlockExprStatement)
      / KEYWORD_defer BlockExprStatement
@@ -7753,50 +7752,50 @@ Statement
      / AssignExpr SEMICOLON
 
 IfStatement
-    <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
+    <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
      / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
 
-LabeledStatement <- BlockLabel? (Block / LoopStatement)
+LabeledStatement <- BlockLabel? (Block / LoopStatement)
 
-LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)
+LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)
 
 ForStatement
-    <- ForPrefix BlockExpr ( KEYWORD_else Statement )?
+    <- ForPrefix BlockExpr ( KEYWORD_else Statement )?
      / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement )
 
 WhileStatement
-    <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
+    <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
      / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
 
 BlockExprStatement
-    <- BlockExpr
+    <- BlockExpr
      / AssignExpr SEMICOLON
 
-BlockExpr <- BlockLabel? Block
+BlockExpr <- BlockLabel? Block
 
 # *** Expression Level ***
-AssignExpr <- Expr (AssignOp Expr)?
+AssignExpr <- Expr (AssignOp Expr)?
 
-Expr <- KEYWORD_try* BoolOrExpr
+Expr <- KEYWORD_try* BoolOrExpr
 
-BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*
+BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*
 
-BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)*
+BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)*
 
-CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)?
+CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)?
 
-BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)*
+BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)*
 
-BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)*
+BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)*
 
-AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)*
+AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)*
 
-MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)*
+MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)*
 
-PrefixExpr <- PrefixOp* PrimaryExpr
+PrefixExpr <- PrefixOp* PrimaryExpr
 
 PrimaryExpr
-    <- AsmExpr
+    <- AsmExpr
      / IfExpr
      / KEYWORD_break BreakLabel? Expr?
      / KEYWORD_cancel Expr
@@ -7807,35 +7806,35 @@ PrimaryExpr
      / LabeledExpr
      / CurlySuffixExpr
 
-IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)?
+IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)?
 
-LabeledExpr <- BlockLabel? (Block / LoopExpr)
+LabeledExpr <- BlockLabel? (Block / LoopExpr)
 
-Block <- LBRACE Statement* RBRACE
+Block <- LBRACE Statement* RBRACE
 
-LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr)
+LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr)
 
-ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?
+ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?
 
-WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
+WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
 
-CurlySuffixExpr <- TypeExpr InitList?
+CurlySuffixExpr <- TypeExpr InitList?
 
 InitList
-    <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
+    <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
      / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
      / LBRACE RBRACE
 
-TypeExpr <- PrefixTypeOp* ErrorUnionExpr
+TypeExpr <- PrefixTypeOp* ErrorUnionExpr
 
-ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)?
+ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)?
 
 SuffixExpr
-    <- AsyncPrefix PrimaryTypeExpr SuffixOp* FnCallArgumnets
+    <- AsyncPrefix PrimaryTypeExpr SuffixOp* FnCallArgumnets
      / PrimaryTypeExpr (SuffixOp / FnCallArgumnets)*
 
 PrimaryTypeExpr
-    <- BUILTININDENTIFIER FnCallArgumnets
+    <- BUILTININDENTIFIER FnCallArgumnets
      / CHAR_LITERAL
      / ContainerDecl
      / ErrorSetDecl
@@ -7858,91 +7857,91 @@ PrimaryTypeExpr
      / STRINGLITERAL
      / SwitchExpr
 
-ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto
+ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto
 
-ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE
+ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE
 
-GroupedExpr <- LPAREN Expr RPAREN
+GroupedExpr <- LPAREN Expr RPAREN
 
-IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
+IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
 
 LabeledTypeExpr
-    <- BlockLabel Block
+    <- BlockLabel Block
      / BlockLabel? LoopTypeExpr
 
-LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr)
+LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr)
 
-ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)?
+ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)?
 
-WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
+WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
 
-SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
+SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
 
 # *** Assembly ***
-AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN STRINGLITERAL AsmOutput? RPAREN
+AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN STRINGLITERAL AsmOutput? RPAREN
 
-AsmOutput <- COLON AsmOutputList AsmInput?
+AsmOutput <- COLON AsmOutputList AsmInput?
 
-AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
+AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
 
-AsmInput <- COLON AsmInputList AsmCloppers?
+AsmInput <- COLON AsmInputList AsmCloppers?
 
-AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN
+AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN
 
-AsmCloppers <- COLON StringList
+AsmCloppers <- COLON StringList
 
 # *** Helper grammar ***
-BreakLabel <- COLON IDENTIFIER
+BreakLabel <- COLON IDENTIFIER
 
-BlockLabel <- IDENTIFIER COLON
+BlockLabel <- IDENTIFIER COLON
 
-FieldInit <- DOT IDENTIFIER EQUAL Expr
+FieldInit <- DOT IDENTIFIER EQUAL Expr
 
-WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN
+WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN
 
-Section <- KEYWORD_section LPAREN Expr RPAREN
+LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN
 
 # Fn specific
 FnCC
-    <- KEYWORD_nakedcc
+    <- KEYWORD_nakedcc
      / KEYWORD_stdcallcc
      / KEYWORD_extern
      / KEYWORD_async (LARROW TypeExpr RARROW)?
 
-ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
+ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
 
 ParamType
-    <- KEYWORD_var
+    <- KEYWORD_var
      / DOT3
      / TypeExpr
 
 # Control flow prefixes
-IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload?
+IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload?
 
-WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
+WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
 
-ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
+ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
 
 # Payloads
-Payload <- PIPE IDENTIFIER PIPE
+Payload <- PIPE IDENTIFIER PIPE
 
-PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE
+PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE
 
-PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE
+PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE
 
 
 # Switch specific
-SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr
+SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr
 
 SwitchCase
-    <- SwitchItem (COMMA SwitchItem)* COMMA?
+    <- SwitchItem (COMMA SwitchItem)* COMMA?
      / KEYWORD_else
 
-SwitchItem <- Expr (DOT3 Expr)?
+SwitchItem <- Expr (DOT3 Expr)?
 
 # Operators
 AssignOp
-    <- ASTERISKEQUAL
+    <- ASTERISKEQUAL
      / SLASHEQUAL
      / PERCENTEQUAL
      / PLUSEQUAL
@@ -7958,7 +7957,7 @@ AssignOp
      / EQUAL
 
 CompareOp
-    <- EQUALEQUAL
+    <- EQUALEQUAL
      / EXCLAMATIONMARKEQUAL
      / LARROW
      / RARROW
@@ -7966,25 +7965,25 @@ CompareOp
      / RARROWEQUAL
 
 BitwiseOp
-    <- AMPERSAND
+    <- AMPERSAND
      / CARET
      / PIPE
      / KEYWORD_orelse
      / KEYWORD_catch Payload?
 
 BitShiftOp
-    <- LARROW2
+    <- LARROW2
      / RARROW2
 
 AdditionOp
-    <- PLUS
+    <- PLUS
      / MINUS
      / PLUS2
      / PLUSPERCENT
      / MINUSPERCENT
 
 MultiplyOp
-    <- PIPE2
+    <- PIPE2
      / ASTERISK
      / SLASH
      / PERCENT
@@ -7992,7 +7991,7 @@ MultiplyOp
      / ASTERISKPERCENT
 
 PrefixOp
-    <- EXCLAMATIONMARK
+    <- EXCLAMATIONMARK
      / MINUS
      / TILDE
      / MINUSPERCENT
@@ -8001,77 +8000,77 @@ PrefixOp
      / KEYWORD_await
 
 PrefixTypeOp
-    <- QUESTIONMARK
+    <- QUESTIONMARK
      / KEYWORD_promise MINUSRARROW
      / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile)*
      / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile)*
 
 SuffixOp
-    <- LBRACKET Expr (DOT2 Expr?)? RBRACKET
+    <- LBRACKET Expr (DOT2 Expr?)? RBRACKET
      / DOT IDENTIFIER
      / DOTASTERISK
      / DOTQUESTIONMARK
 
-AsyncPrefix <- KEYWORD_async (LARROW PrefixExpr RARROW)?
+AsyncPrefix <- KEYWORD_async (LARROW PrefixExpr RARROW)?
 
-FnCallArgumnets <- LPAREN ExprList RPAREN
+FnCallArgumnets <- LPAREN ExprList RPAREN
 
 # Ptr specific
-ArrayTypeStart <- LBRACKET Expr? RBRACKET
+ArrayTypeStart <- LBRACKET Expr? RBRACKET
 
 PtrTypeStart
-    <- ASTERISK
+    <- ASTERISK
      / ASTERISK2
      / LBRACKET ASTERISK RBRACKET
 
 # ContainerDecl specific
-ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE
+ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE
 
 ContainerDeclType
-    <- (KEYWORD_struct / KEYWORD_enum) (LPAREN Expr RPAREN)?
+    <- (KEYWORD_struct / KEYWORD_enum) (LPAREN Expr RPAREN)?
      / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
 
 # Alignment
-ByteAlign <- KEYWORD_align LPAREN Expr RPAREN
+ByteAlign <- KEYWORD_align LPAREN Expr RPAREN
 
 # Lists
-IdentifierList <- (IDENTIFIER COMMA)* IDENTIFIER?
+IdentifierList <- (IDENTIFIER COMMA)* IDENTIFIER?
 
-SwitchProngList <- (SwitchProng COMMA)* SwitchProng?
+SwitchProngList <- (SwitchProng COMMA)* SwitchProng?
 
-AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem?
+AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem?
 
-AsmInputList <- (AsmInputItem COMMA)* AsmInputItem?
+AsmInputList <- (AsmInputItem COMMA)* AsmInputItem?
 
-StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL?
+StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL?
 
-ParamDeclList <- (ParamDecl COMMA)* ParamDecl?
+ParamDeclList <- (ParamDecl COMMA)* ParamDecl?
 
-ExprList <- (Expr COMMA)* Expr?
+ExprList <- (Expr COMMA)* Expr?
 
 # *** Tokens ***
-eof <- !.
-hex <- [0-9a-fA-F]
+eof <- !.
+hex <- [0-9a-fA-F]
 char_escape
-    <- "\\x" hex hex
+    <- "\\x" hex hex
      / "\\u" hex hex hex hex
      / "\\U" hex hex hex hex hex hex
      / "\\" [nr\\t'"]
 char_char
-    <- char_escape
+    <- char_escape
      / [^\\'\n]
 string_char
-    <- char_escape
+    <- char_escape
      / [^\\"\n]
 
-line_comment <- '//'[^\n]*
-line_string <- ("\\\\" [^\n]* [ \n]*)+
-line_cstring <- ("c\\\\" [^\n]* [ \n]*)+
-skip <- ([ \n] / line_comment)*
+line_comment <- '//'[^\n]*
+line_string <- ("\\\\" [^\n]* [ \n]*)+
+line_cstring <- ("c\\\\" [^\n]* [ \n]*)+
+skip <- ([ \n] / line_comment)*
 
-CHAR_LITERAL <- "'" char_char "'" skip
+CHAR_LITERAL <- "'" char_char "'" skip
 FLOAT
-    <- "0b" [01]+  "." [01]+  ([eE] [-+]? [01]+)?  skip
+    <- "0b" [01]+  "." [01]+  ([eE] [-+]? [01]+)?  skip
      / "0o" [0-7]+ "." [0-7]+ ([eE] [-+]? [0-7]+)? skip
      / "0x" hex+   "." hex+   ([pP] [-+]? hex+)?   skip
      /      [0-9]+ "." [0-9]+ ([eE] [-+]? [0-9]+)? skip
@@ -8080,128 +8079,128 @@ FLOAT
      / "0x" hex+   "."? [pP] [-+]? hex+   skip
      /      [0-9]+ "."? [eE] [-+]? [0-9]+ skip
 INTEGER
-    <- "0b" [01]+  skip
+    <- "0b" [01]+  skip
      / "0o" [0-7]+ skip
      / "0x" hex+   skip
      /      [0-9]+ skip
 STRINGLITERAL
-    <- "c"? "\"" string_char* "\"" skip
+    <- "c"? "\"" string_char* "\"" skip
      / line_string                 skip
      / line_cstring                skip
 IDENTIFIER
-    <- !keyword ("c" !["\\] / [A-Zabd-z_]) [A-Za-z0-9_]* skip
+    <- !keyword ("c" !["\\] / [A-Zabd-z_]) [A-Za-z0-9_]* skip
      / "@\"" string_char* "\""                            skip
-BUILTININDENTIFIER <- "@"[A-Za-z_][A-Za-z0-9_]* skip
-
-
-AMPERSAND            <- '&'      ![=]      skip
-AMPERSANDEQUAL       <- '&='               skip
-ASTERISK             <- '*'      ![*%=]    skip
-ASTERISK2            <- '**'               skip
-ASTERISKEQUAL        <- '*='               skip
-ASTERISKPERCENT      <- '*%'     ![=]      skip
-ASTERISKPERCENTEQUAL <- '*%='              skip
-CARET                <- '^'      ![=]      skip
-CARETEQUAL           <- '^='               skip
-COLON                <- ':'                skip
-COMMA                <- ','                skip
-DOT                  <- '.'      ![*.?]    skip
-DOT2                 <- '..'     ![.]      skip
-DOT3                 <- '...'              skip
-DOTASTERISK          <- '.*'               skip
-DOTQUESTIONMARK      <- '.?'               skip
-EQUAL                <- '='      ![>=]     skip
-EQUALEQUAL           <- '=='               skip
-EQUALRARROW          <- '=>'               skip
-EXCLAMATIONMARK      <- '!'      ![=]      skip
-EXCLAMATIONMARKEQUAL <- '!='               skip
-LARROW               <- '<'      ![<=]     skip
-LARROW2              <- '<<'     ![=]      skip
-LARROW2EQUAL         <- '<<='              skip
-LARROWEQUAL          <- '<='               skip
-LBRACE               <- '{'                skip
-LBRACKET             <- '['                skip
-LPAREN               <- '('                skip
-MINUS                <- '-'      ![%=>]    skip
-MINUSEQUAL           <- '-='               skip
-MINUSPERCENT         <- '-%'     ![=]      skip
-MINUSPERCENTEQUAL    <- '-%='              skip
-MINUSRARROW          <- '->'               skip
-PERCENT              <- '%'      ![=]      skip
-PERCENTEQUAL         <- '%='               skip
-PIPE                 <- '|'      ![|=]     skip
-PIPE2                <- '||'               skip
-PIPEEQUAL            <- '|='               skip
-PLUS                 <- '+'      ![%+=]    skip
-PLUS2                <- '++'               skip
-PLUSEQUAL            <- '+='               skip
-PLUSPERCENT          <- '+%'     ![=]      skip
-PLUSPERCENTEQUAL     <- '+%='              skip
-QUESTIONMARK         <- '?'                skip
-RARROW               <- '>'      ![>=]     skip
-RARROW2              <- '>>'     ![=]      skip
-RARROW2EQUAL         <- '>>='              skip
-RARROWEQUAL          <- '>='               skip
-RBRACE               <- '}'                skip
-RBRACKET             <- ']'                skip
-RPAREN               <- ')'                skip
-SEMICOLON            <- ';'                skip
-SLASH                <- '/'      ![=]      skip
-SLASHEQUAL           <- '/='               skip
-TILDE                <- '~'                skip
-
-end_of_word <- ![a-zA-Z0-9_] skip
-KEYWORD_align       <- 'align'       end_of_word
-KEYWORD_and         <- 'and'         end_of_word
-KEYWORD_anyerror    <- 'anyerror'    end_of_word
-KEYWORD_asm         <- 'asm'         end_of_word
-KEYWORD_async       <- 'async'       end_of_word
-KEYWORD_await       <- 'await'       end_of_word
-KEYWORD_break       <- 'break'       end_of_word
-KEYWORD_cancel      <- 'cancel'      end_of_word
-KEYWORD_catch       <- 'catch'       end_of_word
-KEYWORD_comptime    <- 'comptime'    end_of_word
-KEYWORD_const       <- 'const'       end_of_word
-KEYWORD_continue    <- 'continue'    end_of_word
-KEYWORD_defer       <- 'defer'       end_of_word
-KEYWORD_else        <- 'else'        end_of_word
-KEYWORD_enum        <- 'enum'        end_of_word
-KEYWORD_errdefer    <- 'errdefer'    end_of_word
-KEYWORD_error       <- 'error'       end_of_word
-KEYWORD_export      <- 'export'      end_of_word
-KEYWORD_extern      <- 'extern'      end_of_word
-KEYWORD_false       <- 'false'       end_of_word
-KEYWORD_fn          <- 'fn'          end_of_word
-KEYWORD_for         <- 'for'         end_of_word
-KEYWORD_if          <- 'if'          end_of_word
-KEYWORD_inline      <- 'inline'      end_of_word
-KEYWORD_nakedcc     <- 'nakedcc'     end_of_word
-KEYWORD_noalias     <- 'noalias'     end_of_word
-KEYWORD_null        <- 'null'        end_of_word
-KEYWORD_or          <- 'or'          end_of_word
-KEYWORD_orelse      <- 'orelse'      end_of_word
-KEYWORD_packed      <- 'packed'      end_of_word
-KEYWORD_promise     <- 'promise'     end_of_word
-KEYWORD_pub         <- 'pub'         end_of_word
-KEYWORD_resume      <- 'resume'      end_of_word
-KEYWORD_return      <- 'return'      end_of_word
-KEYWORD_section     <- 'section'     end_of_word
-KEYWORD_stdcallcc   <- 'stdcallcc'   end_of_word
-KEYWORD_struct      <- 'struct'      end_of_word
-KEYWORD_suspend     <- 'suspend'     end_of_word
-KEYWORD_switch      <- 'switch'      end_of_word
-KEYWORD_test        <- 'test'        end_of_word
-KEYWORD_true        <- 'true'        end_of_word
-KEYWORD_try         <- 'try'         end_of_word
-KEYWORD_undefined   <- 'undefined'   end_of_word
-KEYWORD_union       <- 'union'       end_of_word
-KEYWORD_unreachable <- 'unreachable' end_of_word
-KEYWORD_use         <- 'use'         end_of_word
-KEYWORD_var         <- 'var'         end_of_word
-KEYWORD_volatile    <- 'volatile'    end_of_word
-KEYWORD_while       <- 'while'       end_of_word
-
-keyword <- KEYWORD_align / KEYWORD_and / KEYWORD_anyerror / KEYWORD_asm
+BUILTININDENTIFIER <- "@"[A-Za-z_][A-Za-z0-9_]* skip
+
+
+AMPERSAND            <- '&'      ![=]      skip
+AMPERSANDEQUAL       <- '&='               skip
+ASTERISK             <- '*'      ![*%=]    skip
+ASTERISK2            <- '**'               skip
+ASTERISKEQUAL        <- '*='               skip
+ASTERISKPERCENT      <- '*%'     ![=]      skip
+ASTERISKPERCENTEQUAL <- '*%='              skip
+CARET                <- '^'      ![=]      skip
+CARETEQUAL           <- '^='               skip
+COLON                <- ':'                skip
+COMMA                <- ','                skip
+DOT                  <- '.'      ![*.?]    skip
+DOT2                 <- '..'     ![.]      skip
+DOT3                 <- '...'              skip
+DOTASTERISK          <- '.*'               skip
+DOTQUESTIONMARK      <- '.?'               skip
+EQUAL                <- '='      ![>=]     skip
+EQUALEQUAL           <- '=='               skip
+EQUALRARROW          <- '=>'               skip
+EXCLAMATIONMARK      <- '!'      ![=]      skip
+EXCLAMATIONMARKEQUAL <- '!='               skip
+LARROW               <- '<'      ![<=]     skip
+LARROW2              <- '<<'     ![=]      skip
+LARROW2EQUAL         <- '<<='              skip
+LARROWEQUAL          <- '<='               skip
+LBRACE               <- '{'                skip
+LBRACKET             <- '['                skip
+LPAREN               <- '('                skip
+MINUS                <- '-'      ![%=>]    skip
+MINUSEQUAL           <- '-='               skip
+MINUSPERCENT         <- '-%'     ![=]      skip
+MINUSPERCENTEQUAL    <- '-%='              skip
+MINUSRARROW          <- '->'               skip
+PERCENT              <- '%'      ![=]      skip
+PERCENTEQUAL         <- '%='               skip
+PIPE                 <- '|'      ![|=]     skip
+PIPE2                <- '||'               skip
+PIPEEQUAL            <- '|='               skip
+PLUS                 <- '+'      ![%+=]    skip
+PLUS2                <- '++'               skip
+PLUSEQUAL            <- '+='               skip
+PLUSPERCENT          <- '+%'     ![=]      skip
+PLUSPERCENTEQUAL     <- '+%='              skip
+QUESTIONMARK         <- '?'                skip
+RARROW               <- '>'      ![>=]     skip
+RARROW2              <- '>>'     ![=]      skip
+RARROW2EQUAL         <- '>>='              skip
+RARROWEQUAL          <- '>='               skip
+RBRACE               <- '}'                skip
+RBRACKET             <- ']'                skip
+RPAREN               <- ')'                skip
+SEMICOLON            <- ';'                skip
+SLASH                <- '/'      ![=]      skip
+SLASHEQUAL           <- '/='               skip
+TILDE                <- '~'                skip
+
+end_of_word <- ![a-zA-Z0-9_] skip
+KEYWORD_align       <- 'align'       end_of_word
+KEYWORD_and         <- 'and'         end_of_word
+KEYWORD_anyerror    <- 'anyerror'    end_of_word
+KEYWORD_asm         <- 'asm'         end_of_word
+KEYWORD_async       <- 'async'       end_of_word
+KEYWORD_await       <- 'await'       end_of_word
+KEYWORD_break       <- 'break'       end_of_word
+KEYWORD_cancel      <- 'cancel'      end_of_word
+KEYWORD_catch       <- 'catch'       end_of_word
+KEYWORD_comptime    <- 'comptime'    end_of_word
+KEYWORD_const       <- 'const'       end_of_word
+KEYWORD_continue    <- 'continue'    end_of_word
+KEYWORD_defer       <- 'defer'       end_of_word
+KEYWORD_else        <- 'else'        end_of_word
+KEYWORD_enum        <- 'enum'        end_of_word
+KEYWORD_errdefer    <- 'errdefer'    end_of_word
+KEYWORD_error       <- 'error'       end_of_word
+KEYWORD_export      <- 'export'      end_of_word
+KEYWORD_extern      <- 'extern'      end_of_word
+KEYWORD_false       <- 'false'       end_of_word
+KEYWORD_fn          <- 'fn'          end_of_word
+KEYWORD_for         <- 'for'         end_of_word
+KEYWORD_if          <- 'if'          end_of_word
+KEYWORD_inline      <- 'inline'      end_of_word
+KEYWORD_nakedcc     <- 'nakedcc'     end_of_word
+KEYWORD_noalias     <- 'noalias'     end_of_word
+KEYWORD_null        <- 'null'        end_of_word
+KEYWORD_or          <- 'or'          end_of_word
+KEYWORD_orelse      <- 'orelse'      end_of_word
+KEYWORD_packed      <- 'packed'      end_of_word
+KEYWORD_promise     <- 'promise'     end_of_word
+KEYWORD_pub         <- 'pub'         end_of_word
+KEYWORD_resume      <- 'resume'      end_of_word
+KEYWORD_return      <- 'return'      end_of_word
+KEYWORD_linksection <- 'linksection' end_of_word
+KEYWORD_stdcallcc   <- 'stdcallcc'   end_of_word
+KEYWORD_struct      <- 'struct'      end_of_word
+KEYWORD_suspend     <- 'suspend'     end_of_word
+KEYWORD_switch      <- 'switch'      end_of_word
+KEYWORD_test        <- 'test'        end_of_word
+KEYWORD_true        <- 'true'        end_of_word
+KEYWORD_try         <- 'try'         end_of_word
+KEYWORD_undefined   <- 'undefined'   end_of_word
+KEYWORD_union       <- 'union'       end_of_word
+KEYWORD_unreachable <- 'unreachable' end_of_word
+KEYWORD_use         <- 'use'         end_of_word
+KEYWORD_var         <- 'var'         end_of_word
+KEYWORD_volatile    <- 'volatile'    end_of_word
+KEYWORD_while       <- 'while'       end_of_word
+
+keyword <- KEYWORD_align / KEYWORD_and / KEYWORD_anyerror / KEYWORD_asm
          / KEYWORD_async / KEYWORD_await / KEYWORD_break / KEYWORD_cancel
          / KEYWORD_catch / KEYWORD_comptime / KEYWORD_const / KEYWORD_continue
          / KEYWORD_defer / KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer
@@ -8209,12 +8208,11 @@ keyword <- KEYWORD_align / KEYWORD_and / KEYWORD_anyerror / KEYWORD_asm
          / KEYWORD_fn / KEYWORD_for / KEYWORD_if / KEYWORD_inline
          / KEYWORD_nakedcc / KEYWORD_noalias / KEYWORD_null / KEYWORD_or
          / KEYWORD_orelse / KEYWORD_packed / KEYWORD_promise / KEYWORD_pub
-         / KEYWORD_resume / KEYWORD_return / KEYWORD_section
+         / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection
          / KEYWORD_stdcallcc / KEYWORD_struct / KEYWORD_suspend
          / KEYWORD_switch / KEYWORD_test / KEYWORD_true / KEYWORD_try
          / KEYWORD_undefined / KEYWORD_union / KEYWORD_unreachable
-         / KEYWORD_use / KEYWORD_var / KEYWORD_volatile / KEYWORD_while
-
+ / KEYWORD_use / KEYWORD_var / KEYWORD_volatile / KEYWORD_while
{#header_close#} {#header_open|Zen#}
    diff --git a/src/parser.cpp b/src/parser.cpp index 0864e7ed1b..b5c67875de 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2082,7 +2082,7 @@ static AstNode *ast_parse_while_continue_expr(ParseContext *pc) { // Section <- KEYWORD_section LPAREN Expr RPAREN static AstNode *ast_parse_section(ParseContext *pc) { - Token *first = eat_token_if(pc, TokenIdKeywordSection); + Token *first = eat_token_if(pc, TokenIdKeywordLinkSection); if (first == nullptr) return nullptr; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 86a25b1aac..921ee4de09 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -140,7 +140,7 @@ static const struct ZigKeyword zig_keywords[] = { {"pub", TokenIdKeywordPub}, {"resume", TokenIdKeywordResume}, {"return", TokenIdKeywordReturn}, - {"section", TokenIdKeywordSection}, + {"linksection", TokenIdKeywordLinkSection}, {"stdcallcc", TokenIdKeywordStdcallCC}, {"struct", TokenIdKeywordStruct}, {"suspend", TokenIdKeywordSuspend}, @@ -1583,7 +1583,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordPromise: return "promise"; case TokenIdKeywordPub: return "pub"; case TokenIdKeywordReturn: return "return"; - case TokenIdKeywordSection: return "section"; + case TokenIdKeywordLinkSection: return "linksection"; case TokenIdKeywordStdcallCC: return "stdcallcc"; case TokenIdKeywordStruct: return "struct"; case TokenIdKeywordSwitch: return "switch"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 2d0d7d56a4..1574e95571 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -82,7 +82,7 @@ enum TokenId { TokenIdKeywordPub, TokenIdKeywordResume, TokenIdKeywordReturn, - TokenIdKeywordSection, + TokenIdKeywordLinkSection, TokenIdKeywordStdcallCC, TokenIdKeywordStruct, TokenIdKeywordSuspend, diff --git a/std/zig/ast.zig b/std/zig/ast.zig index bf2c3593a4..bb2099fb75 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -503,6 +503,7 @@ pub const Node = struct { lib_name: ?*Node, type_node: ?*Node, align_node: ?*Node, + section_node: ?*Node, init_node: ?*Node, semicolon_token: TokenIndex, @@ -519,6 +520,11 @@ pub const Node = struct { i -= 1; } + if (self.section_node) |section_node| { + if (i < 1) return section_node; + i -= 1; + } + if (self.init_node) |init_node| { if (i < 1) return init_node; i -= 1; @@ -821,6 +827,7 @@ pub const Node = struct { body_node: ?*Node, lib_name: ?*Node, // populated if this is an extern declaration align_expr: ?*Node, // populated if align(A) is present + section_expr: ?*Node, // populated if linksection(A) is present pub const ParamList = SegmentedList(*Node, 2); @@ -845,6 +852,11 @@ pub const Node = struct { i -= 1; } + if (self.section_expr) |section_expr| { + if (i < 1) return section_expr; + i -= 1; + } + switch (self.return_type) { // TODO allow this and next prong to share bodies since the types are the same ReturnType.Explicit => |node| { diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 2fb811bc41..a216484d7d 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -291,6 +291,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .body_node = null, .lib_name = ctx.lib_name, .align_expr = null, + .section_expr = null, }); try ctx.decls.push(&fn_proto.base); stack.append(State{ .FnDef = fn_proto }) catch unreachable; @@ -601,6 +602,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .extern_export_token = ctx.extern_export_token, .type_node = null, .align_node = null, + .section_node = null, .init_node = null, .lib_name = ctx.lib_name, // initialized later @@ -622,7 +624,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.VarDeclAlign => |var_decl| { - try stack.append(State{ .VarDeclEq = var_decl }); + try stack.append(State{ .VarDeclSection = var_decl }); const next_token = nextToken(&tok_it, &tree); const next_token_index = next_token.index; @@ -637,6 +639,22 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { prevToken(&tok_it, &tree); continue; }, + State.VarDeclSection => |var_decl| { + try stack.append(State{ .VarDeclEq = var_decl }); + + const next_token = nextToken(&tok_it, &tree); + const next_token_index = next_token.index; + const next_token_ptr = next_token.ptr; + if (next_token_ptr.id == Token.Id.Keyword_linksection) { + try stack.append(State{ .ExpectToken = Token.Id.RParen }); + try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &var_decl.section_node } }); + try stack.append(State{ .ExpectToken = Token.Id.LParen }); + continue; + } + + prevToken(&tok_it, &tree); + continue; + }, State.VarDeclEq => |var_decl| { const token = nextToken(&tok_it, &tree); const token_index = token.index; @@ -719,7 +737,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { continue; }, State.FnProtoAlign => |fn_proto| { - stack.append(State{ .FnProtoReturnType = fn_proto }) catch unreachable; + stack.append(State{ .FnProtoSection = fn_proto }) catch unreachable; if (eatToken(&tok_it, &tree, Token.Id.Keyword_align)) |align_token| { try stack.append(State{ .ExpectToken = Token.Id.RParen }); @@ -728,6 +746,16 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { } continue; }, + State.FnProtoSection => |fn_proto| { + stack.append(State{ .FnProtoReturnType = fn_proto }) catch unreachable; + + if (eatToken(&tok_it, &tree, Token.Id.Keyword_linksection)) |align_token| { + try stack.append(State{ .ExpectToken = Token.Id.RParen }); + try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &fn_proto.section_expr } }); + try stack.append(State{ .ExpectToken = Token.Id.LParen }); + } + continue; + }, State.FnProtoReturnType => |fn_proto| { const token = nextToken(&tok_it, &tree); const token_index = token.index; @@ -1524,6 +1552,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .body_node = null, .lib_name = null, .align_expr = null, + .section_expr = null, }); ctx.opt_ctx.store(&fn_proto.base); stack.append(State{ .FnProto = fn_proto }) catch unreachable; @@ -2579,6 +2608,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .body_node = null, .lib_name = null, .align_expr = null, + .section_expr = null, }); opt_ctx.store(&fn_proto.base); stack.append(State{ .FnProto = fn_proto }) catch unreachable; @@ -2600,6 +2630,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { .body_node = null, .lib_name = null, .align_expr = null, + .section_expr = null, }); opt_ctx.store(&fn_proto.base); stack.append(State{ .FnProto = fn_proto }) catch unreachable; @@ -2985,12 +3016,14 @@ const State = union(enum) { VarDecl: VarDeclCtx, VarDeclAlign: *ast.Node.VarDecl, + VarDeclSection: *ast.Node.VarDecl, VarDeclEq: *ast.Node.VarDecl, VarDeclSemiColon: *ast.Node.VarDecl, FnDef: *ast.Node.FnProto, FnProto: *ast.Node.FnProto, FnProtoAlign: *ast.Node.FnProto, + FnProtoSection: *ast.Node.FnProto, FnProtoReturnType: *ast.Node.FnProto, ParamDecl: *ast.Node.FnProto, diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 1744c9a067..2b60fb9b00 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1,3 +1,10 @@ +test "zig fmt: linksection" { + try testCanonical( + \\export var aoeu: u64 linksection(".text.derp") = 1234; + \\export nakedcc fn _start() linksection(".text.boot") noreturn {} + \\ + ); +} test "zig fmt: shebang line" { try testCanonical( \\#!/usr/bin/env zig diff --git a/std/zig/render.zig b/std/zig/render.zig index 832cb7fe86..e55a0beb93 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -1148,6 +1148,17 @@ fn renderExpression( try renderToken(tree, stream, align_rparen, indent, start_col, Space.Space); // ) } + if (fn_proto.section_expr) |section_expr| { + const section_rparen = tree.nextToken(section_expr.lastToken()); + const section_lparen = tree.prevToken(section_expr.firstToken()); + const section_kw = tree.prevToken(section_lparen); + + try renderToken(tree, stream, section_kw, indent, start_col, Space.None); // section + try renderToken(tree, stream, section_lparen, indent, start_col, Space.None); // ( + try renderExpression(allocator, stream, tree, indent, start_col, section_expr, Space.None); + try renderToken(tree, stream, section_rparen, indent, start_col, Space.Space); // ) + } + switch (fn_proto.return_type) { ast.Node.FnProto.ReturnType.Explicit => |node| { return renderExpression(allocator, stream, tree, indent, start_col, node, space); @@ -1698,12 +1709,14 @@ fn renderVarDecl( try renderToken(tree, stream, var_decl.mut_token, indent, start_col, Space.Space); // var const name_space = if (var_decl.type_node == null and (var_decl.align_node != null or - var_decl.init_node != null)) Space.Space else Space.None; + var_decl.section_node != null or var_decl.init_node != null)) Space.Space else Space.None; try renderToken(tree, stream, var_decl.name_token, indent, start_col, name_space); if (var_decl.type_node) |type_node| { try renderToken(tree, stream, tree.nextToken(var_decl.name_token), indent, start_col, Space.Space); - const s = if (var_decl.align_node != null or var_decl.init_node != null) Space.Space else Space.None; + const s = if (var_decl.align_node != null or + var_decl.section_node != null or + var_decl.init_node != null) Space.Space else Space.None; try renderExpression(allocator, stream, tree, indent, start_col, type_node, s); } @@ -1714,6 +1727,17 @@ fn renderVarDecl( try renderToken(tree, stream, align_kw, indent, start_col, Space.None); // align try renderToken(tree, stream, lparen, indent, start_col, Space.None); // ( try renderExpression(allocator, stream, tree, indent, start_col, align_node, Space.None); + const s = if (var_decl.section_node != null or var_decl.init_node != null) Space.Space else Space.None; + try renderToken(tree, stream, rparen, indent, start_col, s); // ) + } + + if (var_decl.section_node) |section_node| { + const lparen = tree.prevToken(section_node.firstToken()); + const section_kw = tree.prevToken(lparen); + const rparen = tree.nextToken(section_node.lastToken()); + try renderToken(tree, stream, section_kw, indent, start_col, Space.None); // linksection + try renderToken(tree, stream, lparen, indent, start_col, Space.None); // ( + try renderExpression(allocator, stream, tree, indent, start_col, section_node, Space.None); const s = if (var_decl.init_node != null) Space.Space else Space.None; try renderToken(tree, stream, rparen, indent, start_col, s); // ) } diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index a3ba67052a..4941fe2cc2 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -46,7 +46,7 @@ pub const Token = struct { Keyword{ .bytes = "pub", .id = Id.Keyword_pub }, Keyword{ .bytes = "resume", .id = Id.Keyword_resume }, Keyword{ .bytes = "return", .id = Id.Keyword_return }, - Keyword{ .bytes = "section", .id = Id.Keyword_section }, + Keyword{ .bytes = "linksection", .id = Id.Keyword_linksection }, Keyword{ .bytes = "stdcallcc", .id = Id.Keyword_stdcallcc }, Keyword{ .bytes = "struct", .id = Id.Keyword_struct }, Keyword{ .bytes = "suspend", .id = Id.Keyword_suspend }, @@ -175,7 +175,7 @@ pub const Token = struct { Keyword_pub, Keyword_resume, Keyword_return, - Keyword_section, + Keyword_linksection, Keyword_stdcallcc, Keyword_struct, Keyword_suspend, diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 22be733d17..7339be7fad 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3867,32 +3867,32 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( "setting a section on an extern variable", - \\extern var foo: i32 section(".text2"); + \\extern var foo: i32 linksection(".text2"); \\export fn entry() i32 { \\ return foo; \\} , - ".tmp_source.zig:1:29: error: cannot set section of external variable 'foo'", + ".tmp_source.zig:1:33: error: cannot set section of external variable 'foo'", ); cases.add( "setting a section on a local variable", \\export fn entry() i32 { - \\ var foo: i32 section(".text2") = 1234; + \\ var foo: i32 linksection(".text2") = 1234; \\ return foo; \\} , - ".tmp_source.zig:2:26: error: cannot set section of local variable 'foo'", + ".tmp_source.zig:2:30: error: cannot set section of local variable 'foo'", ); cases.add( "setting a section on an extern fn", - \\extern fn foo() section(".text2") void; + \\extern fn foo() linksection(".text2") void; \\export fn entry() void { \\ foo(); \\} , - ".tmp_source.zig:1:25: error: cannot set section of external function 'foo'", + ".tmp_source.zig:1:29: error: cannot set section of external function 'foo'", ); cases.add( -- cgit v1.2.3