/* * Copyright (c) 2015 Andrew Kelley * * This file is part of zig, which is MIT licensed. * See http://opensource.org/licenses/MIT */ #include "parser.hpp" #include "errmsg.hpp" #include "analyze.hpp" #include #include #include #include struct ParseContext { Buf *buf; AstNode *root; 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); // 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 }; 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, pc->owner->source_code, pc->owner->line_offsets, msg); print_err_msg(err, pc->err_color); exit(EXIT_FAILURE); } 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, 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); } static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) { AstNode *node = allocate(1); node->type = type; node->owner = pc->owner; return node; } static void ast_update_node_line_info(AstNode *node, Token *first_token) { assert(first_token); node->line = first_token->start_line; node->column = first_token->start_column; } 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); ast_update_node_line_info(node, first_token); return node; } static void parse_asm_template(ParseContext *pc, AstNode *node) { Buf *asm_template = node->data.asm_expr.asm_template; enum State { StateStart, StatePercent, StateTemplate, StateVar, }; ZigList *tok_list = &node->data.asm_expr.token_list; assert(tok_list->length == 0); AsmToken *cur_tok = nullptr; enum State state = StateStart; for (size_t i = 0; i < buf_len(asm_template); i += 1) { uint8_t c = *((uint8_t*)buf_ptr(asm_template) + i); switch (state) { case StateStart: if (c == '%') { tok_list->add_one(); cur_tok = &tok_list->last(); cur_tok->id = AsmTokenIdPercent; cur_tok->start = i; state = StatePercent; } else { tok_list->add_one(); cur_tok = &tok_list->last(); cur_tok->id = AsmTokenIdTemplate; cur_tok->start = i; state = StateTemplate; } break; case StatePercent: if (c == '%') { cur_tok->end = i; state = StateStart; } else if (c == '[') { cur_tok->id = AsmTokenIdVar; state = StateVar; } else if (c == '=') { cur_tok->id = AsmTokenIdUniqueId; cur_tok->end = i; state = StateStart; } else { ast_asm_error(pc, node, i, "expected a '%%' or '['"); } break; case StateTemplate: if (c == '%') { cur_tok->end = i; i -= 1; cur_tok = nullptr; state = StateStart; } break; case StateVar: if (c == ']') { cur_tok->end = i; state = StateStart; } else if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '_')) { // do nothing } else { ast_asm_error(pc, node, i, "invalid substitution character: '%c'", c); } break; } } switch (state) { case StateStart: break; case StatePercent: case StateVar: ast_asm_error(pc, node, buf_len(asm_template), "unexpected end of assembly template"); break; case StateTemplate: cur_tok->end = buf_len(asm_template); break; } } static Buf *token_buf(Token *token) { 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 BigFloat *token_bigfloat(Token *token) { assert(token->id == TokenIdFloatLiteral); return &token->data.float_lit.bigfloat; } static uint8_t token_char_lit(Token *token) { assert(token->id == TokenIdCharLiteral); return token->data.char_lit.c; } 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); } } 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)); } 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); static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) { if (token->id == token_id) { return; } 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)); } 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; } /* 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; } } /* 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); } /* 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); } return node; } 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 *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; } } } 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 *expr = ast_parse_expression(pc, token_index, true); params->append(expr); token = &pc->tokens->at(*token_index); *token_index += 1; if (token->id == TokenIdRParen) { return; } else { ast_expect_token(pc, token, TokenIdComma); } } zig_unreachable(); } /* GroupedExpression : token(LParen) Expression token(RParen) */ static AstNode *ast_parse_grouped_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *l_paren = &pc->tokens->at(*token_index); if (l_paren->id != TokenIdLParen) { if (mandatory) { ast_expect_token(pc, l_paren, TokenIdLParen); } else { return nullptr; } } *token_index += 1; AstNode *node = ast_create_node(pc, NodeTypeGroupedExpr, l_paren); node->data.grouped_expr = ast_parse_expression(pc, token_index, true); Token *r_paren = &pc->tokens->at(*token_index); *token_index += 1; ast_expect_token(pc, r_paren, TokenIdRParen); return node; } /* ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr */ static AstNode *ast_parse_array_type_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *l_bracket = &pc->tokens->at(*token_index); if (l_bracket->id != TokenIdLBracket) { if (mandatory) { ast_expect_token(pc, l_bracket, TokenIdLBracket); } else { return nullptr; } } *token_index += 1; AstNode *node = ast_create_node(pc, NodeTypeArrayType, l_bracket); node->data.array_type.size = ast_parse_expression(pc, token_index, false); ast_eat_token(pc, token_index, TokenIdRBracket); Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdKeywordAlign) { *token_index += 1; ast_eat_token(pc, token_index, TokenIdLParen); node->data.array_type.align_expr = ast_parse_expression(pc, token_index, true); ast_eat_token(pc, token_index, TokenIdRParen); token = &pc->tokens->at(*token_index); } if (token->id == TokenIdKeywordConst) { *token_index += 1; node->data.array_type.is_const = true; token = &pc->tokens->at(*token_index); } if (token->id == TokenIdKeywordVolatile) { *token_index += 1; node->data.array_type.is_volatile = true; } node->data.array_type.child_type = ast_parse_type_expr(pc, token_index, true); return node; } /* AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen) */ static void ast_parse_asm_input_item(ParseContext *pc, size_t *token_index, AstNode *node) { ast_eat_token(pc, token_index, TokenIdLBracket); Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol); ast_eat_token(pc, token_index, TokenIdRBracket); Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral); ast_eat_token(pc, token_index, TokenIdLParen); AstNode *expr_node = ast_parse_expression(pc, token_index, true); ast_eat_token(pc, token_index, TokenIdRParen); AsmInput *asm_input = allocate(1); asm_input->asm_symbolic_name = token_buf(alias); asm_input->constraint = token_buf(constraint); asm_input->expr = expr_node; node->data.asm_expr.input_list.append(asm_input); } /* AsmOutputItem : "[" "Symbol" "]" "String" "(" ("Symbol" | "->" PrefixOpExpression) ")" */ static void ast_parse_asm_output_item(ParseContext *pc, size_t *token_index, AstNode *node) { ast_eat_token(pc, token_index, TokenIdLBracket); Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol); ast_eat_token(pc, token_index, TokenIdRBracket); Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral); AsmOutput *asm_output = allocate(1); ast_eat_token(pc, token_index, TokenIdLParen); Token *token = &pc->tokens->at(*token_index); *token_index += 1; if (token->id == TokenIdSymbol) { asm_output->variable_name = token_buf(token); } else if (token->id == TokenIdArrow) { asm_output->return_type = ast_parse_type_expr(pc, token_index, true); } else { ast_invalid_token_error(pc, token); } ast_eat_token(pc, token_index, TokenIdRParen); asm_output->asm_symbolic_name = token_buf(alias); asm_output->constraint = token_buf(constraint); node->data.asm_expr.output_list.append(asm_output); } /* AsmClobbers: token(Colon) list(token(String), token(Comma)) */ static void ast_parse_asm_clobbers(ParseContext *pc, size_t *token_index, AstNode *node) { Token *colon_tok = &pc->tokens->at(*token_index); if (colon_tok->id != TokenIdColon) return; *token_index += 1; for (;;) { Token *string_tok = &pc->tokens->at(*token_index); ast_expect_token(pc, string_tok, TokenIdStringLiteral); *token_index += 1; Buf *clobber_buf = token_buf(string_tok); node->data.asm_expr.clobber_list.append(clobber_buf); Token *comma = &pc->tokens->at(*token_index); if (comma->id == TokenIdComma) { *token_index += 1; Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdRParen) { break; } else { continue; } } else { break; } } } /* 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); } /* 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); } /* 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) = option(Symbol ":") "suspend" option(("|" Symbol "|" body)) */ static AstNode *ast_parse_suspend_block(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; } } Token *suspend_token = token; if (suspend_token->id == TokenIdKeywordSuspend) { *token_index += 1; } else if (mandatory) { ast_expect_token(pc, suspend_token, TokenIdKeywordSuspend); zig_unreachable(); } else { return nullptr; } Token *bar_token = &pc->tokens->at(*token_index); if (bar_token->id == TokenIdBinOr) { *token_index += 1; } else if (mandatory) { ast_expect_token(pc, suspend_token, TokenIdBinOr); zig_unreachable(); } else { *token_index = orig_token_index; return nullptr; } AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); if (name_token != nullptr) { node->data.suspend.name = token_buf(name_token); } node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index); ast_eat_token(pc, token_index, TokenIdBinOr); node->data.suspend.block = ast_parse_block(pc, token_index, true); return node; } /* 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" | "this" | "unreachable" | "suspend" ErrorSetDecl = "error" "{" 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 == TokenIdKeywordThis) { AstNode *node = ast_create_node(pc, NodeTypeThisLiteral, 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 *next_token = &pc->tokens->at(*token_index + 1); if (next_token->id == TokenIdLBrace) { AstNode *node = ast_create_node(pc, NodeTypeErrorSetDecl, token); *token_index += 2; 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 { AstNode *node = ast_create_node(pc, NodeTypeErrorType, token); *token_index += 1; return node; } } 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) { return asm_expr; } AstNode *container_decl = ast_parse_container_decl(pc, token_index, false); if (container_decl) return container_decl; if (!mandatory) return nullptr; ast_invalid_token_error(pc, token); } /* CurlySuffixExpression : PrefixOpExpression option(ContainerInitExpression) ContainerInitExpression : token(LBrace) ContainerInitBody token(RBrace) ContainerInitBody : list(StructLiteralField, token(Comma)) | list(Expression, token(Comma)) */ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_index, bool mandatory) { AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, mandatory); if (!prefix_op_expr) { return nullptr; } while (true) { Token *first_token = &pc->tokens->at(*token_index); if (first_token->id == TokenIdLBrace) { *token_index += 1; 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; } } } 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; } 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); } 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); } return node; } /* SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | PtrDerefExpression | 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) PtrDerefExpression = ".*" StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression */ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { AstNode *primary_expr; Token *async_token = &pc->tokens->at(*token_index); if (async_token->id == TokenIdKeywordAsync) { *token_index += 1; AstNode *allocator_expr_node = nullptr; Token *async_lparen_tok = &pc->tokens->at(*token_index); if (async_lparen_tok->id == TokenIdCmpLessThan) { *token_index += 1; allocator_expr_node = ast_parse_prefix_op_expr(pc, token_index, true); ast_eat_token(pc, token_index, TokenIdCmpGreaterThan); } Token *fncall_token = &pc->tokens->at(*token_index); if (fncall_token->id == TokenIdKeywordFn) { *token_index += 1; return ast_parse_fn_proto_partial(pc, token_index, fncall_token, allocator_expr_node, CallingConventionAsync, false, VisibModPrivate); } AstNode *node = ast_parse_suffix_op_expr(pc, token_index, true); if (node->type != NodeTypeFnCallExpr) { ast_error(pc, fncall_token, "expected function call, found '%s'", token_name(fncall_token->id)); } node->data.fn_call_expr.is_async = true; node->data.fn_call_expr.async_allocator = allocator_expr_node; assert(node->data.fn_call_expr.fn_ref_expr != nullptr); primary_expr = node; } else { primary_expr = ast_parse_primary_expr(pc, token_index, mandatory); if (!primary_expr) return nullptr; } while (true) { Token *first_token = &pc->tokens->at(*token_index); if (first_token->id == TokenIdLParen) { *token_index += 1; AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, first_token); node->data.fn_call_expr.fn_ref_expr = primary_expr; ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params); primary_expr = node; } else if (first_token->id == TokenIdLBracket) { *token_index += 1; AstNode *expr_node = ast_parse_expression(pc, token_index, true); Token *ellipsis_or_r_bracket = &pc->tokens->at(*token_index); if (ellipsis_or_r_bracket->id == TokenIdEllipsis2) { *token_index += 1; AstNode *node = ast_create_node(pc, NodeTypeSliceExpr, first_token); node->data.slice_expr.array_ref_expr = primary_expr; node->data.slice_expr.start = expr_node; node->data.slice_expr.end = ast_parse_expression(pc, token_index, false); ast_eat_token(pc, token_index, TokenIdRBracket); primary_expr = node; } else if (ellipsis_or_r_bracket->id == TokenIdRBracket) { *token_index += 1; AstNode *node = ast_create_node(pc, NodeTypeArrayAccessExpr, first_token); node->data.array_access_expr.array_ref_expr = primary_expr; node->data.array_access_expr.subscript = expr_node; primary_expr = node; } else { ast_invalid_token_error(pc, ellipsis_or_r_bracket); } } else if (first_token->id == TokenIdDot) { *token_index += 1; Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdSymbol) { *token_index += 1; 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 += 1; AstNode *node = ast_create_node(pc, NodeTypePtrDeref, first_token); node->data.ptr_deref_expr.target = primary_expr; primary_expr = node; } else { ast_invalid_token_error(pc, token); } } 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 TokenIdMaybe: return PrefixOpMaybe; case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe; 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); 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 *bit_offset_end_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.bit_offset_end = token_bigint(bit_offset_end_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; } /* 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_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.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; } 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; } } /* 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; } } *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; } /* 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) return nullptr; while (true) { Token *token = &pc->tokens->at(*token_index); BinOpType add_op = ast_parse_add_op(pc, token_index, false); if (add_op == BinOpTypeInvalid) return operand_1; AstNode *operand_2 = ast_parse_mult_expr(pc, token_index, true); AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = operand_1; node->data.bin_op_expr.bin_op = add_op; node->data.bin_op_expr.op2 = operand_2; operand_1 = node; } } static BinOpType tok_to_bit_shift_op(Token *token) { switch (token->id) { case TokenIdBitShiftLeft: return BinOpTypeBitShiftLeft; case TokenIdBitShiftRight: return BinOpTypeBitShiftRight; default: return BinOpTypeInvalid; } } /* BitShiftOperator = "<<" | ">>" | "<<%" */ static BinOpType ast_parse_bit_shift_op(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); BinOpType result = tok_to_bit_shift_op(token); if (result == BinOpTypeInvalid) { if (mandatory) { ast_invalid_token_error(pc, token); } else { return BinOpTypeInvalid; } } *token_index += 1; return result; } /* BitShiftExpression : AdditionExpression BitShiftOperator BitShiftExpression | AdditionExpression */ static AstNode *ast_parse_bit_shift_expr(ParseContext *pc, size_t *token_index, bool mandatory) { AstNode *operand_1 = ast_parse_add_expr(pc, token_index, mandatory); if (!operand_1) 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; } } /* BinaryAndExpression : BitShiftExpression token(Ampersand) BinaryAndExpression | BitShiftExpression */ static AstNode *ast_parse_bin_and_expr(ParseContext *pc, size_t *token_index, bool mandatory) { AstNode *operand_1 = ast_parse_bit_shift_expr(pc, token_index, mandatory); if (!operand_1) return nullptr; while (true) { Token *token = &pc->tokens->at(*token_index); if (token->id != TokenIdAmpersand) return operand_1; *token_index += 1; 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; } } /* 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) 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 *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; operand_1 = node; } } /* 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 *operand_2 = ast_parse_bin_xor_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 = BinOpTypeBinOr; node->data.bin_op_expr.op2 = operand_2; operand_1 = node; } } 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; } } 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; } /* ComparisonExpression : BinaryOrExpression ComparisonOperator BinaryOrExpression | BinaryOrExpression */ static AstNode *ast_parse_comparison_expr(ParseContext *pc, size_t *token_index, bool mandatory) { AstNode *operand_1 = ast_parse_bin_or_expr(pc, token_index, mandatory); if (!operand_1) return nullptr; Token *token = &pc->tokens->at(*token_index); BinOpType cmp_op = ast_parse_comparison_operator(pc, token_index, false); if (cmp_op == BinOpTypeInvalid) return operand_1; AstNode *operand_2 = ast_parse_bin_or_expr(pc, token_index, true); AstNode *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 *operand_2 = ast_parse_comparison_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 = BinOpTypeBoolAnd; node->data.bin_op_expr.op2 = operand_2; operand_1 = node; } } /* IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) TryExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body) TestExpression(body) = "if" "(" Expression ")" "|" option("*") Symbol "|" body option("else" BlockExpression(body)) */ static AstNode *ast_parse_if_try_test_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *if_token = &pc->tokens->at(*token_index); if (if_token->id == TokenIdKeywordIf) { *token_index += 1; } else if (mandatory) { ast_expect_token(pc, if_token, TokenIdKeywordIf); zig_unreachable(); } else { return nullptr; } 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; } 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); } 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); } 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); if (token->id != TokenIdKeywordReturn) { return nullptr; } *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_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; } /* AwaitExpression : "await" Expression */ static AstNode *ast_parse_await_expr(ParseContext *pc, size_t *token_index) { Token *token = &pc->tokens->at(*token_index); if (token->id != TokenIdKeywordAwait) { return nullptr; } *token_index += 1; AstNode *node = ast_create_node(pc, NodeTypeAwaitExpr, token); node->data.await_expr.expr = ast_parse_expression(pc, token_index, true); return node; } /* 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); if (token->id == TokenIdKeywordBreak) { *token_index += 1; } else { return nullptr; } AstNode *node = ast_create_node(pc, NodeTypeBreak, token); Token *maybe_colon_token = &pc->tokens->at(*token_index); if (maybe_colon_token->id == TokenIdColon) { *token_index += 1; Token *name = ast_eat_token(pc, token_index, TokenIdSymbol); node->data.break_expr.name = token_buf(name); } 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); if (token->id != TokenIdKeywordCancel) { return nullptr; } *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_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; } 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); return node; } /* 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; } AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, var_token); 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 *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); } 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); } // peek ahead and ensure that all variable declarations are followed by a semicolon ast_expect_token(pc, next_token, TokenIdSemicolon); return node; } /* 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; } 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; } AstNode *node = ast_parse_variable_declaration_expr(pc, token_index, false, VisibModPrivate, is_comptime, false); if (node == nullptr) { if (is_comptime) { *token_index -= 1; } return nullptr; } return node; } /* 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; } } /* 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; 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; Token *else_bar_tok = &pc->tokens->at(*token_index); if (else_bar_tok->id == TokenIdBinOr) { *token_index += 1; Token *err_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); node->data.while_expr.err_symbol = token_buf(err_name_tok); ast_eat_token(pc, token_index, TokenIdBinOr); } node->data.while_expr.else_node = ast_parse_block_or_expression(pc, token_index, true); } return node; } 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; } /* ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body)) */ static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool mandatory) { size_t orig_token_index = *token_index; Token *name_token = nullptr; Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdSymbol) { *token_index += 1; Token *colon_token = &pc->tokens->at(*token_index); if (colon_token->id == TokenIdColon) { *token_index += 1; name_token = token; token = &pc->tokens->at(*token_index); } else if (mandatory) { ast_expect_token(pc, colon_token, TokenIdColon); zig_unreachable(); } else { *token_index = orig_token_index; return nullptr; } } bool is_inline = false; if (token->id == TokenIdKeywordInline) { is_inline = true; *token_index += 1; token = &pc->tokens->at(*token_index); } 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); } node->data.for_expr.is_inline = is_inline; ast_eat_token(pc, token_index, TokenIdLParen); node->data.for_expr.array_expr = ast_parse_expression(pc, token_index, true); ast_eat_token(pc, token_index, TokenIdRParen); Token *maybe_bar = &pc->tokens->at(*token_index); if (maybe_bar->id == TokenIdBinOr) { *token_index += 1; Token *maybe_star = &pc->tokens->at(*token_index); if (maybe_star->id == TokenIdStar) { *token_index += 1; node->data.for_expr.elem_is_ptr = true; } node->data.for_expr.elem_node = ast_parse_symbol(pc, token_index); Token *maybe_comma = &pc->tokens->at(*token_index); if (maybe_comma->id == TokenIdComma) { *token_index += 1; node->data.for_expr.index_node = ast_parse_symbol(pc, token_index); } ast_eat_token(pc, token_index, TokenIdBinOr); } node->data.for_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; node->data.for_expr.else_node = ast_parse_block_or_expression(pc, token_index, true); } return node; } /* 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 { 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); for (;;) { Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdRBrace) { *token_index += 1; return node; } AstNode *prong_node = ast_create_node(pc, NodeTypeSwitchProng, token); node->data.switch_expr.prongs.append(prong_node); 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; AstNode *range_node = ast_create_node(pc, NodeTypeSwitchRange, ellipsis_tok); prong_node->data.switch_prong.items.append(range_node); range_node->data.switch_range.start = expr1; range_node->data.switch_range.end = ast_parse_expression(pc, token_index, true); prong_node->data.switch_prong.any_items_are_range = true; } else { prong_node->data.switch_prong.items.append(expr1); } Token *comma_tok = &pc->tokens->at(*token_index); if (comma_tok->id == TokenIdComma) { *token_index += 1; Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdFatArrow) { break; } else { continue; } } break; } ast_eat_token(pc, token_index, TokenIdFatArrow); Token *maybe_bar = &pc->tokens->at(*token_index); if (maybe_bar->id == TokenIdBinOr) { *token_index += 1; 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); } 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); } prong_node->data.switch_prong.expr = ast_parse_expression(pc, token_index, true); Token *trailing_token = &pc->tokens->at(*token_index); if (trailing_token->id == TokenIdRBrace) { *token_index += 1; return node; } else { ast_eat_token(pc, token_index, TokenIdComma); } } } /* BlockExpression(body) = Block | IfExpression(body) | IfErrorExpression(body) | TestExpression(body) | WhileExpression(body) | ForExpression(body) | SwitchExpression | CompTimeExpression(body) | SuspendExpression(body) */ static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); AstNode *if_expr = ast_parse_if_try_test_expr(pc, token_index, false); if (if_expr) return if_expr; AstNode *while_expr = ast_parse_while_expr(pc, token_index, false); if (while_expr) return while_expr; AstNode *for_expr = ast_parse_for_expr(pc, token_index, false); if (for_expr) return for_expr; AstNode *switch_expr = ast_parse_switch_expr(pc, token_index, false); if (switch_expr) return switch_expr; AstNode *block = ast_parse_block(pc, token_index, false); if (block) return block; AstNode *comptime_node = ast_parse_comptime_expr(pc, token_index, false, false); if (comptime_node) return comptime_node; AstNode *suspend_node = ast_parse_suspend_block(pc, token_index, false); if (suspend_node) return suspend_node; if (mandatory) ast_invalid_token_error(pc, token); return nullptr; } static BinOpType tok_to_ass_op(Token *token) { switch (token->id) { case TokenIdEq: return BinOpTypeAssign; case TokenIdTimesEq: return BinOpTypeAssignTimes; case TokenIdTimesPercentEq: return BinOpTypeAssignTimesWrap; case TokenIdDivEq: return BinOpTypeAssignDiv; case TokenIdModEq: return BinOpTypeAssignMod; case TokenIdPlusEq: return BinOpTypeAssignPlus; case TokenIdPlusPercentEq: return BinOpTypeAssignPlusWrap; case TokenIdMinusEq: return BinOpTypeAssignMinus; case TokenIdMinusPercentEq: return BinOpTypeAssignMinusWrap; case TokenIdBitShiftLeftEq: return BinOpTypeAssignBitShiftLeft; case TokenIdBitShiftRightEq: return BinOpTypeAssignBitShiftRight; case TokenIdBitAndEq: return BinOpTypeAssignBitAnd; case TokenIdBitXorEq: return BinOpTypeAssignBitXor; case TokenIdBitOrEq: return BinOpTypeAssignBitOr; default: return BinOpTypeInvalid; } } /* AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "*%=" | "+%=" | "-%=" | "<<%=" */ static BinOpType ast_parse_ass_op(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); BinOpType result = tok_to_ass_op(token); if (result == BinOpTypeInvalid) { if (mandatory) { ast_invalid_token_error(pc, token); } else { return BinOpTypeInvalid; } } *token_index += 1; return result; } /* UnwrapExpression : BoolOrExpression (UnwrapMaybe | UnwrapError) | BoolOrExpression UnwrapMaybe : "??" BoolOrExpression UnwrapError = "catch" option("|" Symbol "|") Expression */ static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory) { AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory); if (!lhs) return nullptr; Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdDoubleQuestion) { *token_index += 1; AstNode *rhs = ast_parse_expression(pc, token_index, true); AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); node->data.bin_op_expr.op1 = lhs; node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe; node->data.bin_op_expr.op2 = rhs; return node; } else if (token->id == TokenIdKeywordCatch) { *token_index += 1; AstNode *node = ast_create_node(pc, NodeTypeUnwrapErrorExpr, token); node->data.unwrap_err_expr.op1 = lhs; 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); return node; } else { return lhs; } } /* AssignmentExpression : UnwrapExpression AssignmentOperator UnwrapExpression | UnwrapExpression */ static AstNode *ast_parse_ass_expr(ParseContext *pc, size_t *token_index, bool mandatory) { AstNode *lhs = ast_parse_unwrap_expr(pc, token_index, mandatory); if (!lhs) return nullptr; Token *token = &pc->tokens->at(*token_index); BinOpType ass_op = ast_parse_ass_op(pc, token_index, false); if (ass_op == BinOpTypeInvalid) return lhs; AstNode *rhs = ast_parse_unwrap_expr(pc, token_index, true); 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; return node; } 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; return ast_parse_expression(pc, token_index, mandatory); } /* 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; return ast_parse_expression(pc, token_index, mandatory); } /* 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; AstNode *try_expr = ast_parse_try_expr(pc, token_index); if (try_expr) return try_expr; AstNode *break_expr = ast_parse_break_expr(pc, token_index); if (break_expr) return break_expr; AstNode *cancel_expr = ast_parse_cancel_expr(pc, token_index); if (cancel_expr) return cancel_expr; AstNode *resume_expr = ast_parse_resume_expr(pc, token_index); if (resume_expr) return resume_expr; AstNode *ass_expr = ast_parse_ass_expr(pc, token_index, false); if (ass_expr) return ass_expr; if (mandatory) ast_invalid_token_error(pc, token); return nullptr; } bool statement_terminates_without_semicolon(AstNode *node) { switch (node->type) { case NodeTypeIfBoolExpr: if (node->data.if_bool_expr.else_node) return statement_terminates_without_semicolon(node->data.if_bool_expr.else_node); return node->data.if_bool_expr.then_block->type == NodeTypeBlock; case NodeTypeIfErrorExpr: if (node->data.if_err_expr.else_node) return statement_terminates_without_semicolon(node->data.if_err_expr.else_node); return node->data.if_err_expr.then_node->type == NodeTypeBlock; case NodeTypeTestExpr: if (node->data.test_expr.else_node) return statement_terminates_without_semicolon(node->data.test_expr.else_node); return node->data.test_expr.then_node->type == NodeTypeBlock; case NodeTypeWhileExpr: return node->data.while_expr.body->type == NodeTypeBlock; case NodeTypeForExpr: return node->data.for_expr.body->type == NodeTypeBlock; case NodeTypeCompTime: return node->data.comptime_expr.expr->type == NodeTypeBlock; case NodeTypeDefer: return node->data.defer.expr->type == NodeTypeBlock; case NodeTypeSuspend: return node->data.suspend.block != nullptr && node->data.suspend.block->type == NodeTypeBlock; case NodeTypeSwitchExpr: case NodeTypeBlock: return true; default: return false; } } /* Block = option(Symbol ":") "{" many(Statement) "}" Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" | ExportDecl */ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mandatory) { size_t orig_token_index = *token_index; Token *name_token = nullptr; Token *last_token = &pc->tokens->at(*token_index); if (last_token->id == TokenIdSymbol) { *token_index += 1; Token *colon_token = &pc->tokens->at(*token_index); if (colon_token->id == TokenIdColon) { *token_index += 1; name_token = last_token; last_token = &pc->tokens->at(*token_index); } else if (mandatory) { ast_expect_token(pc, colon_token, TokenIdColon); zig_unreachable(); } else { *token_index = orig_token_index; return nullptr; } } if (last_token->id != TokenIdLBrace) { if (mandatory) { ast_expect_token(pc, last_token, TokenIdLBrace); } else { *token_index = orig_token_index; return 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); } for (;;) { last_token = &pc->tokens->at(*token_index); if (last_token->id == TokenIdRBrace) { *token_index += 1; return node; } AstNode *statement_node = ast_parse_local_var_decl(pc, token_index); if (!statement_node) statement_node = ast_parse_defer_expr(pc, token_index); if (!statement_node) statement_node = ast_parse_block_expr(pc, token_index, false); if (!statement_node) statement_node = ast_parse_expression(pc, token_index, false); if (!statement_node) { ast_invalid_token_error(pc, last_token); } node->data.block.statements.append(statement_node); if (!statement_terminates_without_semicolon(statement_node)) { ast_eat_token(pc, token_index, TokenIdSemicolon); } } zig_unreachable(); } /* FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("(" Expression ")"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var") */ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) { Token *first_token = &pc->tokens->at(*token_index); Token *fn_token; CallingConvention cc; bool is_extern = false; AstNode *async_allocator_type_node = nullptr; if (first_token->id == TokenIdKeywordNakedCC) { *token_index += 1; fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn); cc = CallingConventionNaked; } else if (first_token->id == TokenIdKeywordAsync) { *token_index += 1; Token *next_token = &pc->tokens->at(*token_index); if (next_token->id == TokenIdCmpLessThan) { *token_index += 1; async_allocator_type_node = ast_parse_type_expr(pc, token_index, true); ast_eat_token(pc, token_index, TokenIdCmpGreaterThan); } fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn); cc = CallingConventionAsync; } else if (first_token->id == TokenIdKeywordStdcallCC) { *token_index += 1; fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn); cc = CallingConventionStdcall; } else if (first_token->id == TokenIdKeywordExtern) { is_extern = true; *token_index += 1; Token *next_token = &pc->tokens->at(*token_index); if (next_token->id == TokenIdKeywordFn) { fn_token = next_token; *token_index += 1; } else if (mandatory) { ast_expect_token(pc, next_token, TokenIdKeywordFn); zig_unreachable(); } else { *token_index -= 1; return nullptr; } cc = CallingConventionC; } else if (first_token->id == TokenIdKeywordFn) { fn_token = first_token; *token_index += 1; cc = CallingConventionUnspecified; } else if (mandatory) { ast_expect_token(pc, first_token, TokenIdKeywordFn); zig_unreachable(); } else { return nullptr; } return ast_parse_fn_proto_partial(pc, token_index, fn_token, async_allocator_type_node, cc, is_extern, visib_mod); } /* 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; } 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; } fn_proto->data.fn_proto.is_inline = is_inline; fn_proto->data.fn_proto.is_export = is_export; Token *semi_token = &pc->tokens->at(*token_index); if (semi_token->id == TokenIdSemicolon) { *token_index += 1; return fn_proto; } 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; } /* ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";" */ static AstNode *ast_parse_extern_decl(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) { Token *extern_kw = &pc->tokens->at(*token_index); if (extern_kw->id != TokenIdKeywordExtern) { if (mandatory) { ast_expect_token(pc, extern_kw, TokenIdKeywordExtern); } else { return nullptr; } } *token_index += 1; Token *lib_name_tok = &pc->tokens->at(*token_index); Buf *lib_name = nullptr; if (lib_name_tok->id == TokenIdStringLiteral) { lib_name = token_buf(lib_name_tok); *token_index += 1; } 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 fn_proto_node; } 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 var_decl_node; } Token *token = &pc->tokens->at(*token_index); ast_invalid_token_error(pc, token); } /* 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; AstNode *node = ast_create_node(pc, NodeTypeUse, use_kw); node->data.use.visib_mod = visib_mod; node->data.use.expr = ast_parse_expression(pc, token_index, true); ast_eat_token(pc, token_index, TokenIdSemicolon); return node; } /* 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; } *token_index += (layout == ContainerLayoutAuto) ? 1 : 2; AstNode *node = ast_create_node(pc, NodeTypeContainerDecl, first_token); node->data.container_decl.layout = layout; node->data.container_decl.kind = kind; if (kind == ContainerKindUnion) { Token *lparen_token = &pc->tokens->at(*token_index); if (lparen_token->id == TokenIdLParen) { Token *enum_token = &pc->tokens->at(*token_index + 1); if (enum_token->id == TokenIdKeywordEnum) { Token *paren_token = &pc->tokens->at(*token_index + 2); if (paren_token->id == TokenIdLParen) { node->data.container_decl.auto_enum = true; *token_index += 2; node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, true); ast_eat_token(pc, token_index, TokenIdRParen); } else if (paren_token->id == TokenIdRParen) { node->data.container_decl.auto_enum = true; *token_index += 3; } } } } if (!node->data.container_decl.auto_enum) { node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, false); } ast_eat_token(pc, token_index, TokenIdLBrace); for (;;) { Token *visib_tok = &pc->tokens->at(*token_index); VisibMod visib_mod; if (visib_tok->id == TokenIdKeywordPub) { *token_index += 1; visib_mod = VisibModPub; } else { visib_mod = VisibModPrivate; } AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false, visib_mod); if (fn_def_node) { node->data.container_decl.decls.append(fn_def_node); continue; } AstNode *var_decl_node = ast_parse_global_var_decl(pc, token_index, visib_mod); if (var_decl_node) { ast_eat_token(pc, token_index, TokenIdSemicolon); node->data.container_decl.decls.append(var_decl_node); continue; } Token *token = &pc->tokens->at(*token_index); if (token->id == TokenIdRBrace) { *token_index += 1; break; } else if (token->id == TokenIdSymbol) { AstNode *field_node = ast_create_node(pc, NodeTypeStructField, token); *token_index += 1; node->data.container_decl.fields.append(field_node); field_node->data.struct_field.visib_mod = visib_mod; field_node->data.struct_field.name = token_buf(token); Token *colon_token = &pc->tokens->at(*token_index); if (colon_token->id == TokenIdColon) { *token_index += 1; field_node->data.struct_field.type = ast_parse_type_expr(pc, token_index, true); } Token *eq_token = &pc->tokens->at(*token_index); if (eq_token->id == TokenIdEq) { *token_index += 1; field_node->data.struct_field.value = ast_parse_expression(pc, token_index, true); } Token *next_token = &pc->tokens->at(*token_index); if (next_token->id == TokenIdComma) { *token_index += 1; continue; } if (next_token->id == TokenIdRBrace) { *token_index += 1; break; } ast_invalid_token_error(pc, next_token); } else { ast_invalid_token_error(pc, token); } } return node; } /* TestDecl = "test" String Block */ static AstNode *ast_parse_test_decl_node(ParseContext *pc, size_t *token_index) { Token *first_token = &pc->tokens->at(*token_index); if (first_token->id != TokenIdKeywordTest) { return nullptr; } *token_index += 1; Token *name_tok = ast_eat_token(pc, token_index, TokenIdStringLiteral); AstNode *node = ast_create_node(pc, NodeTypeTestDecl, first_token); node->data.test_decl.name = token_buf(name_tok); node->data.test_decl.body = ast_parse_block(pc, token_index, true); return node; } /* 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; } AstNode *test_decl_node = ast_parse_test_decl_node(pc, token_index); if (test_decl_node) { top_level_decls->append(test_decl_node); continue; } Token *visib_tok = &pc->tokens->at(*token_index); VisibMod visib_mod; if (visib_tok->id == TokenIdKeywordPub) { *token_index += 1; visib_mod = VisibModPub; } else { visib_mod = VisibModPrivate; } AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false, visib_mod); if (fn_def_node) { top_level_decls->append(fn_def_node); continue; } 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; } AstNode *use_node = ast_parse_use(pc, token_index, visib_mod); if (use_node) { top_level_decls->append(use_node); continue; } AstNode *var_decl_node = ast_parse_global_var_decl(pc, token_index, visib_mod); if (var_decl_node) { ast_eat_token(pc, token_index, TokenIdSemicolon); top_level_decls->append(var_decl_node); continue; } return; } zig_unreachable(); } /* Root = many(TopLevelItem) "EOF" */ static AstNode *ast_parse_root(ParseContext *pc, size_t *token_index) { AstNode *node = ast_create_node(pc, NodeTypeRoot, &pc->tokens->at(*token_index)); ast_parse_top_level_decls(pc, token_index, &node->data.root.top_level_decls); if (*token_index != pc->tokens->length - 1) { ast_invalid_token_error(pc, &pc->tokens->at(*token_index)); } return node; } 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; } static void visit_field(AstNode **node, void (*visit)(AstNode **, void *context), void *context) { if (*node) { visit(node, context); } } static void visit_node_list(ZigList *list, void (*visit)(AstNode **, void *context), void *context) { if (list) { for (size_t i = 0; i < list->length; i += 1) { visit(&list->at(i), context); } } } 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); visit_field(&node->data.fn_proto.align_expr, visit, context); visit_field(&node->data.fn_proto.section_expr, visit, context); visit_field(&node->data.fn_proto.async_allocator_type, visit, context); break; case NodeTypeFnDef: visit_field(&node->data.fn_def.fn_proto, visit, context); visit_field(&node->data.fn_def.body, visit, context); break; case NodeTypeParamDecl: visit_field(&node->data.param_decl.type, visit, context); break; case NodeTypeBlock: visit_node_list(&node->data.block.statements, visit, context); break; case NodeTypeGroupedExpr: visit_field(&node->data.grouped_expr, visit, context); break; case NodeTypeReturnExpr: visit_field(&node->data.return_expr.expr, visit, context); break; case NodeTypeDefer: visit_field(&node->data.defer.expr, visit, context); break; case NodeTypeVariableDeclaration: visit_field(&node->data.variable_declaration.type, visit, context); visit_field(&node->data.variable_declaration.expr, visit, context); visit_field(&node->data.variable_declaration.align_expr, visit, context); visit_field(&node->data.variable_declaration.section_expr, visit, context); break; case NodeTypeTestDecl: visit_field(&node->data.test_decl.body, visit, context); break; case NodeTypeBinOpExpr: visit_field(&node->data.bin_op_expr.op1, visit, context); visit_field(&node->data.bin_op_expr.op2, visit, context); break; case NodeTypeUnwrapErrorExpr: visit_field(&node->data.unwrap_err_expr.op1, visit, context); visit_field(&node->data.unwrap_err_expr.symbol, visit, context); visit_field(&node->data.unwrap_err_expr.op2, visit, context); break; case NodeTypeIntLiteral: // none break; case NodeTypeFloatLiteral: // none break; case NodeTypeStringLiteral: // none break; case NodeTypeCharLiteral: // none break; case NodeTypeSymbol: // none break; case NodeTypePrefixOpExpr: visit_field(&node->data.prefix_op_expr.primary_expr, visit, context); break; case NodeTypeFnCallExpr: visit_field(&node->data.fn_call_expr.fn_ref_expr, visit, context); visit_node_list(&node->data.fn_call_expr.params, visit, context); visit_field(&node->data.fn_call_expr.async_allocator, visit, context); break; case NodeTypeArrayAccessExpr: visit_field(&node->data.array_access_expr.array_ref_expr, visit, context); visit_field(&node->data.array_access_expr.subscript, visit, context); break; case NodeTypeSliceExpr: visit_field(&node->data.slice_expr.array_ref_expr, visit, context); visit_field(&node->data.slice_expr.start, visit, context); visit_field(&node->data.slice_expr.end, visit, context); break; case NodeTypeFieldAccessExpr: visit_field(&node->data.field_access_expr.struct_expr, visit, context); break; case NodeTypePtrDeref: visit_field(&node->data.ptr_deref_expr.target, visit, context); break; case NodeTypeUse: visit_field(&node->data.use.expr, visit, context); break; case NodeTypeBoolLiteral: // none break; case NodeTypeNullLiteral: // none break; case NodeTypeUndefinedLiteral: // none break; case NodeTypeThisLiteral: // none break; case NodeTypeIfBoolExpr: visit_field(&node->data.if_bool_expr.condition, visit, context); visit_field(&node->data.if_bool_expr.then_block, visit, context); visit_field(&node->data.if_bool_expr.else_node, visit, context); break; case NodeTypeIfErrorExpr: visit_field(&node->data.if_err_expr.target_node, visit, context); visit_field(&node->data.if_err_expr.then_node, visit, context); visit_field(&node->data.if_err_expr.else_node, visit, context); break; case NodeTypeTestExpr: visit_field(&node->data.test_expr.target_node, visit, context); visit_field(&node->data.test_expr.then_node, visit, context); visit_field(&node->data.test_expr.else_node, visit, context); break; case NodeTypeWhileExpr: visit_field(&node->data.while_expr.condition, visit, context); visit_field(&node->data.while_expr.body, visit, context); break; case NodeTypeForExpr: visit_field(&node->data.for_expr.elem_node, visit, context); visit_field(&node->data.for_expr.array_expr, visit, context); visit_field(&node->data.for_expr.index_node, visit, context); visit_field(&node->data.for_expr.body, visit, context); break; case NodeTypeSwitchExpr: visit_field(&node->data.switch_expr.expr, visit, context); visit_node_list(&node->data.switch_expr.prongs, visit, context); break; case NodeTypeSwitchProng: visit_node_list(&node->data.switch_prong.items, visit, context); visit_field(&node->data.switch_prong.var_symbol, visit, context); visit_field(&node->data.switch_prong.expr, visit, context); break; case NodeTypeSwitchRange: visit_field(&node->data.switch_range.start, visit, context); visit_field(&node->data.switch_range.end, visit, context); break; case NodeTypeCompTime: visit_field(&node->data.comptime_expr.expr, visit, context); break; case NodeTypeBreak: // none break; case NodeTypeContinue: // none break; case NodeTypeUnreachable: // none break; case NodeTypeAsmExpr: for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) { AsmInput *asm_input = node->data.asm_expr.input_list.at(i); visit_field(&asm_input->expr, visit, context); } for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1) { AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); visit_field(&asm_output->return_type, visit, context); } break; case NodeTypeContainerDecl: visit_node_list(&node->data.container_decl.fields, visit, context); visit_node_list(&node->data.container_decl.decls, visit, context); visit_field(&node->data.container_decl.init_arg_expr, visit, context); break; case NodeTypeStructField: visit_field(&node->data.struct_field.type, visit, context); visit_field(&node->data.struct_field.value, visit, context); break; case NodeTypeContainerInitExpr: visit_field(&node->data.container_init_expr.type, visit, context); visit_node_list(&node->data.container_init_expr.entries, visit, context); break; case NodeTypeStructValueField: visit_field(&node->data.struct_val_field.expr, visit, context); break; case NodeTypeArrayType: visit_field(&node->data.array_type.size, visit, context); visit_field(&node->data.array_type.child_type, visit, context); visit_field(&node->data.array_type.align_expr, visit, context); break; case NodeTypePromiseType: visit_field(&node->data.promise_type.payload_type, visit, context); break; case NodeTypeErrorType: // none break; case NodeTypePointerType: visit_field(&node->data.pointer_type.align_expr, visit, context); visit_field(&node->data.pointer_type.op_expr, visit, context); break; case NodeTypeErrorSetDecl: visit_node_list(&node->data.err_set_decl.decls, visit, context); break; case NodeTypeCancel: visit_field(&node->data.cancel_expr.expr, visit, context); break; case NodeTypeResume: visit_field(&node->data.resume_expr.expr, visit, context); break; case NodeTypeAwaitExpr: visit_field(&node->data.await_expr.expr, visit, context); break; case NodeTypeSuspend: visit_field(&node->data.suspend.promise_symbol, visit, context); visit_field(&node->data.suspend.block, visit, context); break; } }