diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-12-06 18:52:39 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-12-06 18:52:39 -0500 |
| commit | e7d28344fa3ee81d6ad7ca5ce1f83d50d8502118 (patch) | |
| tree | 012b2556f2bda10ae663fab8efb235efe30e02f4 /src/stage1/parser.cpp | |
| parent | 817cf6a82efa7ed274371a28621bbf88a723d9b7 (diff) | |
| parent | 20d86d9c63476b6312b87dc5b0e4aa4822eb7717 (diff) | |
| download | zig-e7d28344fa3ee81d6ad7ca5ce1f83d50d8502118.tar.gz zig-e7d28344fa3ee81d6ad7ca5ce1f83d50d8502118.zip | |
Merge pull request #13560 from ziglang/wasi-bootstrap
Nuke the C++ implementation of Zig from orbit using WASI
Diffstat (limited to 'src/stage1/parser.cpp')
| -rw-r--r-- | src/stage1/parser.cpp | 3603 |
1 files changed, 0 insertions, 3603 deletions
diff --git a/src/stage1/parser.cpp b/src/stage1/parser.cpp deleted file mode 100644 index eaa15c47b0..0000000000 --- a/src/stage1/parser.cpp +++ /dev/null @@ -1,3603 +0,0 @@ -/* - * 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 <stdarg.h> -#include <stdio.h> -#include <limits.h> -#include <errno.h> - -struct ParseContext { - Buf *buf; - // Shortcut to `owner->data.structure.root_struct->token_ids`. - TokenId *token_ids; - // Shortcut to `owner->data.structure.root_struct->token_locs`. - TokenLoc *token_locs; - ZigType *owner; - TokenIndex current_token; - ErrColor err_color; - // Shortcut to `owner->data.structure.root_struct->token_count`. - uint32_t token_count; -}; - -struct PtrPayload { - TokenIndex asterisk; - TokenIndex payload; -}; - -struct PtrIndexPayload { - TokenIndex asterisk; - TokenIndex payload; - TokenIndex index; -}; - -static AstNode *ast_parse_root(ParseContext *pc); -static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc); -static AstNode *ast_parse_test_decl(ParseContext *pc); -static AstNode *ast_parse_top_level_comptime(ParseContext *pc); -static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, - TokenIndex doc_comments); -static AstNode *ast_parse_fn_proto(ParseContext *pc); -static AstNode *ast_parse_var_decl(ParseContext *pc); -static AstNode *ast_parse_container_field(ParseContext *pc); -static AstNode *ast_parse_statement(ParseContext *pc); -static AstNode *ast_parse_if_statement(ParseContext *pc); -static AstNode *ast_parse_labeled_statement(ParseContext *pc); -static AstNode *ast_parse_loop_statement(ParseContext *pc); -static AstNode *ast_parse_for_statement(ParseContext *pc); -static AstNode *ast_parse_while_statement(ParseContext *pc); -static AstNode *ast_parse_block_expr_statement(ParseContext *pc); -static AstNode *ast_parse_block_expr(ParseContext *pc); -static AstNode *ast_parse_assign_expr(ParseContext *pc); -static AstNode *ast_parse_expr(ParseContext *pc); -static AstNode *ast_parse_bool_or_expr(ParseContext *pc); -static AstNode *ast_parse_bool_and_expr(ParseContext *pc); -static AstNode *ast_parse_compare_expr(ParseContext *pc); -static AstNode *ast_parse_bitwise_expr(ParseContext *pc); -static AstNode *ast_parse_bit_shift_expr(ParseContext *pc); -static AstNode *ast_parse_addition_expr(ParseContext *pc); -static AstNode *ast_parse_multiply_expr(ParseContext *pc); -static AstNode *ast_parse_prefix_expr(ParseContext *pc); -static AstNode *ast_parse_primary_expr(ParseContext *pc); -static AstNode *ast_parse_if_expr(ParseContext *pc); -static AstNode *ast_parse_block(ParseContext *pc); -static AstNode *ast_parse_loop_expr(ParseContext *pc); -static AstNode *ast_parse_for_expr(ParseContext *pc); -static AstNode *ast_parse_while_expr(ParseContext *pc); -static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc); -static AstNode *ast_parse_init_list(ParseContext *pc); -static AstNode *ast_parse_type_expr(ParseContext *pc); -static AstNode *ast_parse_error_union_expr(ParseContext *pc); -static AstNode *ast_parse_suffix_expr(ParseContext *pc); -static AstNode *ast_parse_primary_type_expr(ParseContext *pc); -static AstNode *ast_parse_container_decl(ParseContext *pc); -static AstNode *ast_parse_error_set_decl(ParseContext *pc); -static AstNode *ast_parse_grouped_expr(ParseContext *pc); -static AstNode *ast_parse_if_type_expr(ParseContext *pc); -static AstNode *ast_parse_labeled_type_expr(ParseContext *pc); -static AstNode *ast_parse_loop_type_expr(ParseContext *pc); -static AstNode *ast_parse_for_type_expr(ParseContext *pc); -static AstNode *ast_parse_while_type_expr(ParseContext *pc); -static AstNode *ast_parse_switch_expr(ParseContext *pc); -static AstNode *ast_parse_asm_expr(ParseContext *pc); -static AstNode *ast_parse_anon_lit(ParseContext *pc); -static AstNode *ast_parse_asm_output(ParseContext *pc); -static AsmOutput *ast_parse_asm_output_item(ParseContext *pc); -static AstNode *ast_parse_asm_input(ParseContext *pc); -static AsmInput *ast_parse_asm_input_item(ParseContext *pc); -static AstNode *ast_parse_asm_clobbers(ParseContext *pc); -static TokenIndex ast_parse_break_label(ParseContext *pc); -static TokenIndex ast_parse_block_label(ParseContext *pc); -static AstNode *ast_parse_field_init(ParseContext *pc); -static AstNode *ast_parse_while_continue_expr(ParseContext *pc); -static AstNode *ast_parse_link_section(ParseContext *pc); -static AstNode *ast_parse_callconv(ParseContext *pc); -static AstNode *ast_parse_param_decl(ParseContext *pc); -static AstNode *ast_parse_param_type(ParseContext *pc); -static AstNode *ast_parse_if_prefix(ParseContext *pc); -static AstNode *ast_parse_while_prefix(ParseContext *pc); -static AstNode *ast_parse_for_prefix(ParseContext *pc); -static TokenIndex ast_parse_payload(ParseContext *pc); -static Optional<PtrPayload> ast_parse_ptr_payload(ParseContext *pc); -static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc); -static AstNode *ast_parse_switch_prong(ParseContext *pc); -static AstNode *ast_parse_switch_case(ParseContext *pc); -static AstNode *ast_parse_switch_item(ParseContext *pc); -static AstNode *ast_parse_assign_op(ParseContext *pc); -static AstNode *ast_parse_compare_op(ParseContext *pc); -static AstNode *ast_parse_bitwise_op(ParseContext *pc); -static AstNode *ast_parse_bit_shift_op(ParseContext *pc); -static AstNode *ast_parse_addition_op(ParseContext *pc); -static AstNode *ast_parse_multiply_op(ParseContext *pc); -static AstNode *ast_parse_prefix_op(ParseContext *pc); -static AstNode *ast_parse_prefix_type_op(ParseContext *pc); -static AstNode *ast_parse_suffix_op(ParseContext *pc); -static AstNode *ast_parse_fn_call_arguments(ParseContext *pc); -static AstNode *ast_parse_array_type_start(ParseContext *pc); -static AstNode *ast_parse_ptr_type_start(ParseContext *pc); -static AstNode *ast_parse_container_decl_auto(ParseContext *pc); -static AstNode *ast_parse_container_decl_type(ParseContext *pc); -static AstNode *ast_parse_byte_align(ParseContext *pc); - -ATTRIBUTE_NORETURN -static void ast_error_offset(RootStruct *root_struct, ErrColor err_color, - TokenIndex token, size_t bad_index, Buf *msg) -{ - assert(token < root_struct->token_count); - uint32_t byte_offset = root_struct->token_locs[token].offset; - ErrorMsg *err = err_msg_create_with_offset(root_struct->path, - byte_offset + bad_index, buf_ptr(root_struct->source_code), msg); - - print_err_msg(err, err_color); - exit(EXIT_FAILURE); -} - -ATTRIBUTE_PRINTF(3, 4) -ATTRIBUTE_NORETURN -static void ast_error(ParseContext *pc, TokenIndex token, const char *format, ...) { - va_list ap; - va_start(ap, format); - Buf *msg = buf_vprintf(format, ap); - va_end(ap); - - RootStruct *root_struct = pc->owner->data.structure.root_struct; - ast_error_offset(root_struct, pc->err_color, token, 0, msg); -} - -ATTRIBUTE_NORETURN -static void ast_invalid_token_error(ParseContext *pc, TokenIndex token) { - ast_error(pc, token, "invalid token: '%s'", token_name(pc->token_ids[token])); -} - -static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) { - AstNode *node = heap::c_allocator.create<AstNode>(); - node->type = type; - node->owner = pc->owner; - return node; -} - -static AstNode *ast_create_node(ParseContext *pc, NodeType type, TokenIndex first_token) { - assert(first_token); - AstNode *node = ast_create_node_no_line_info(pc, type); - node->main_token = first_token; - return node; -} - -static AstNode *ast_create_node_copy_line_info(ParseContext *pc, NodeType type, AstNode *from) { - assert(from); - AstNode *node = ast_create_node_no_line_info(pc, type); - node->main_token = from->main_token; - return node; -} - -static TokenIndex peek_token(ParseContext *pc) { - return pc->current_token; -} - -static TokenIndex eat_token(ParseContext *pc) { - TokenIndex res = peek_token(pc); - pc->current_token += 1; - return res; -} - -static TokenIndex eat_token_if(ParseContext *pc, TokenId id) { - TokenIndex res = peek_token(pc); - if (pc->token_ids[res] == id) { - return eat_token(pc); - } - - return 0; -} - -static TokenIndex expect_token(ParseContext *pc, TokenId id) { - TokenIndex res = eat_token(pc); - TokenId actual_id = pc->token_ids[res]; - if (actual_id != id) - ast_error(pc, res, "expected token '%s', found '%s'", token_name(id), token_name(actual_id)); - - return res; -} - -static void put_back_token(ParseContext *pc) { - pc->current_token -= 1; -} - -static Buf *token_buf(ParseContext *pc, TokenIndex token) { - Error err; - - if (token == 0) - return nullptr; - - RootStruct *root_struct = pc->owner->data.structure.root_struct; - if (root_struct->token_ids[token] == TokenIdIdentifier) { - return token_identifier_buf(root_struct, token); - } else if (root_struct->token_ids[token] == TokenIdStringLiteral) { - assert(root_struct->token_ids[token] == TokenIdStringLiteral); - const char *source = buf_ptr(root_struct->source_code); - size_t byte_offset = root_struct->token_locs[token].offset; - size_t bad_index; - Buf *str = buf_alloc(); - if ((err = source_string_literal_buf(source + byte_offset, str, &bad_index))) { - ast_error_offset(root_struct, pc->err_color, token, bad_index, - buf_create_from_str("invalid string literal character")); - } - return str; - } else { - zig_unreachable(); - } -} - -static AstNode *token_identifier(ParseContext *pc, TokenIndex token) { - assert(pc->token_ids[token] == TokenIdIdentifier); - return ast_create_node(pc, NodeTypeIdentifier, token); -} - -// (Rule SEP)* Rule? -template<typename T> -static ZigList<T *> ast_parse_list(ParseContext *pc, TokenId sep, T *(*parser)(ParseContext*)) { - ZigList<T *> res = {}; - while (true) { - T *curr = parser(pc); - if (curr == nullptr) - break; - - res.append(curr); - if (eat_token_if(pc, sep) == 0) - break; - } - - return res; -} - -static AstNode *ast_expect(ParseContext *pc, AstNode *(*parser)(ParseContext*)) { - AstNode *res = parser(pc); - if (res == nullptr) - ast_invalid_token_error(pc, peek_token(pc)); - return res; -} - -enum BinOpChain { - BinOpChainOnce, - BinOpChainInf, -}; - -// Op* Child -static AstNode *ast_parse_prefix_op_expr( - ParseContext *pc, - AstNode *(*op_parser)(ParseContext *), - AstNode *(*child_parser)(ParseContext *) -) { - AstNode *res = nullptr; - AstNode **right = &res; - while (true) { - AstNode *prefix = op_parser(pc); - if (prefix == nullptr) - break; - - *right = prefix; - switch (prefix->type) { - case NodeTypePrefixOpExpr: - right = &prefix->data.prefix_op_expr.primary_expr; - break; - case NodeTypeReturnExpr: - right = &prefix->data.return_expr.expr; - break; - case NodeTypeAwaitExpr: - right = &prefix->data.await_expr.expr; - break; - case NodeTypeAnyFrameType: - right = &prefix->data.anyframe_type.payload_type; - break; - case NodeTypeArrayType: - right = &prefix->data.array_type.child_type; - break; - case NodeTypeInferredArrayType: - right = &prefix->data.inferred_array_type.child_type; - break; - case NodeTypePointerType: { - // We might get two pointers from *_ptr_type_start - AstNode *child = prefix->data.pointer_type.op_expr; - if (child == nullptr) - child = prefix; - right = &child->data.pointer_type.op_expr; - break; - } - default: - zig_unreachable(); - } - } - - // If we have already consumed a token, and determined that - // this node is a prefix op, then we expect that the node has - // a child. - if (res != nullptr) { - *right = ast_expect(pc, child_parser); - } else { - // Otherwise, if we didn't consume a token, then we can return - // null, if the child expr did. - *right = child_parser(pc); - if (*right == nullptr) - return nullptr; - } - - return res; -} - -// Child (Op Child)(*/?) -static AstNode *ast_parse_bin_op_expr( - ParseContext *pc, - BinOpChain chain, - AstNode *(*op_parse)(ParseContext*), - AstNode *(*child_parse)(ParseContext*) -) { - AstNode *res = child_parse(pc); - if (res == nullptr) - return nullptr; - - do { - AstNode *op = op_parse(pc); - if (op == nullptr) - break; - - AstNode *left = res; - AstNode *right = ast_expect(pc, child_parse); - res = op; - switch (op->type) { - case NodeTypeBinOpExpr: - op->data.bin_op_expr.op1 = left; - op->data.bin_op_expr.op2 = right; - break; - case NodeTypeCatchExpr: - op->data.unwrap_err_expr.op1 = left; - op->data.unwrap_err_expr.op2 = right; - break; - default: - zig_unreachable(); - } - } while (chain == BinOpChainInf); - - return res; -} - -// IfPrefix Body (KEYWORD_else Payload? Body)? -static AstNode *ast_parse_if_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) { - AstNode *res = ast_parse_if_prefix(pc); - if (res == nullptr) - return nullptr; - - AstNode *body = ast_expect(pc, body_parser); - TokenIndex err_payload = 0; - AstNode *else_body = nullptr; - if (eat_token_if(pc, TokenIdKeywordElse) != 0) { - err_payload = ast_parse_payload(pc); - else_body = ast_expect(pc, body_parser); - } - - assert(res->type == NodeTypeIfOptional); - if (err_payload != 0) { - AstNodeTestExpr old = res->data.test_expr; - res->type = NodeTypeIfErrorExpr; - res->data.if_err_expr.target_node = old.target_node; - res->data.if_err_expr.var_is_ptr = old.var_is_ptr; - res->data.if_err_expr.var_symbol = old.var_symbol; - res->data.if_err_expr.then_node = body; - res->data.if_err_expr.err_symbol = token_buf(pc, err_payload); - res->data.if_err_expr.else_node = else_body; - return res; - } - - if (res->data.test_expr.var_symbol != nullptr) { - res->data.test_expr.then_node = body; - res->data.test_expr.else_node = else_body; - return res; - } - - AstNodeTestExpr old = res->data.test_expr; - res->type = NodeTypeIfBoolExpr; - res->data.if_bool_expr.condition = old.target_node; - res->data.if_bool_expr.then_block = body; - res->data.if_bool_expr.else_node = else_body; - return res; -} - -// KEYWORD_inline? (ForLoop / WhileLoop) -static AstNode *ast_parse_loop_expr_helper( - ParseContext *pc, - AstNode *(*for_parser)(ParseContext *), - AstNode *(*while_parser)(ParseContext *) -) { - TokenIndex inline_token = eat_token_if(pc, TokenIdKeywordInline); - AstNode *for_expr = for_parser(pc); - if (for_expr != nullptr) { - assert(for_expr->type == NodeTypeForExpr); - for_expr->data.for_expr.is_inline = inline_token != 0; - return for_expr; - } - - AstNode *while_expr = while_parser(pc); - if (while_expr != nullptr) { - assert(while_expr->type == NodeTypeWhileExpr); - while_expr->data.while_expr.is_inline = inline_token != 0; - return while_expr; - } - - if (inline_token != 0) - ast_invalid_token_error(pc, peek_token(pc)); - return nullptr; -} - -// ForPrefix Body (KEYWORD_else Body)? -static AstNode *ast_parse_for_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) { - AstNode *res = ast_parse_for_prefix(pc); - if (res == nullptr) - return nullptr; - - AstNode *body = ast_expect(pc, body_parser); - AstNode *else_body = nullptr; - if (eat_token_if(pc, TokenIdKeywordElse) != 0) - else_body = ast_expect(pc, body_parser); - - assert(res->type == NodeTypeForExpr); - res->data.for_expr.body = body; - res->data.for_expr.else_node = else_body; - return res; -} - -// WhilePrefix Body (KEYWORD_else Payload? Body)? -static AstNode *ast_parse_while_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) { - AstNode *res = ast_parse_while_prefix(pc); - if (res == nullptr) - return nullptr; - - AstNode *body = ast_expect(pc, body_parser); - TokenIndex err_payload = 0; - AstNode *else_body = nullptr; - if (eat_token_if(pc, TokenIdKeywordElse) != 0) { - err_payload = ast_parse_payload(pc); - else_body = ast_expect(pc, body_parser); - } - - assert(res->type == NodeTypeWhileExpr); - res->data.while_expr.body = body; - res->data.while_expr.err_symbol = token_buf(pc, err_payload); - res->data.while_expr.else_node = else_body; - return res; -} - -template<TokenId id, BinOpType op> -AstNode *ast_parse_bin_op_simple(ParseContext *pc) { - TokenIndex op_token = eat_token_if(pc, id); - if (op_token == 0) - return nullptr; - - AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); - res->data.bin_op_expr.bin_op = op; - return res; -} - -AstNode *ast_parse(Buf *buf, ZigType *owner, ErrColor err_color) { - RootStruct *root_struct = owner->data.structure.root_struct; - - ParseContext pc = {}; - pc.err_color = err_color; - pc.owner = owner; - pc.buf = buf; - pc.token_ids = root_struct->token_ids; - pc.token_locs = root_struct->token_locs; - pc.token_count = root_struct->token_count; - pc.current_token = 1; // Skip over the first (invalid) token. - return ast_parse_root(&pc); -} - -// Root <- skip ContainerMembers eof -static AstNode *ast_parse_root(ParseContext *pc) { - TokenIndex first = peek_token(pc); - AstNodeContainerDecl members = ast_parse_container_members(pc); - if (pc->current_token != pc->token_count - 1) - ast_invalid_token_error(pc, peek_token(pc)); - - AstNode *node = ast_create_node(pc, NodeTypeContainerDecl, first); - node->data.container_decl.fields = members.fields; - node->data.container_decl.decls = members.decls; - node->data.container_decl.layout = ContainerLayoutAuto; - node->data.container_decl.kind = ContainerKindStruct; - node->data.container_decl.is_root = true; - node->data.container_decl.doc_comments = members.doc_comments; - - return node; -} - -static TokenIndex ast_parse_multi_tok(ParseContext *pc, TokenId token_id) { - TokenIndex first_token = eat_token_if(pc, token_id); - TokenIndex token = first_token; - while (token != 0) { - token = eat_token_if(pc, token_id); - } - return first_token; -} - -static TokenIndex ast_parse_doc_comments(ParseContext *pc) { - return ast_parse_multi_tok(pc, TokenIdDocComment); -} - -static TokenIndex ast_parse_container_doc_comments(ParseContext *pc) { - return ast_parse_multi_tok(pc, TokenIdContainerDocComment); -} - -enum ContainerFieldState { - // no fields have been seen - ContainerFieldStateNone, - // currently parsing fields - ContainerFieldStateSeen, - // saw fields and then a declaration after them - ContainerFieldStateEnd, -}; - -// ContainerMembers -// <- TestDecl ContainerMembers -// / TopLevelComptime ContainerMembers -// / KEYWORD_pub? TopLevelDecl ContainerMembers -// / ContainerField COMMA ContainerMembers -// / ContainerField -// / -static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { - AstNodeContainerDecl res = {}; - ContainerFieldState field_state = ContainerFieldStateNone; - TokenIndex first_token = 0; - res.doc_comments = ast_parse_container_doc_comments(pc); - for (;;) { - TokenIndex peeked_token = peek_token(pc); - - AstNode *test_decl = ast_parse_test_decl(pc); - if (test_decl != nullptr) { - if (field_state == ContainerFieldStateSeen) { - field_state = ContainerFieldStateEnd; - first_token = peeked_token; - } - res.decls.append(test_decl); - continue; - } - - AstNode *top_level_comptime = ast_parse_top_level_comptime(pc); - if (top_level_comptime != nullptr) { - if (field_state == ContainerFieldStateSeen) { - field_state = ContainerFieldStateEnd; - first_token = peeked_token; - } - res.decls.append(top_level_comptime); - continue; - } - - TokenIndex first_doc_token = ast_parse_doc_comments(pc); - - peeked_token = peek_token(pc); - - TokenIndex visib_token = eat_token_if(pc, TokenIdKeywordPub); - VisibMod visib_mod = (visib_token != 0) ? VisibModPub : VisibModPrivate; - - AstNode *top_level_decl = ast_parse_top_level_decl(pc, visib_mod, first_doc_token); - if (top_level_decl != nullptr) { - if (field_state == ContainerFieldStateSeen) { - field_state = ContainerFieldStateEnd; - first_token = peeked_token; - } - res.decls.append(top_level_decl); - continue; - } - - if (visib_token != 0) { - ast_error(pc, peek_token(pc), "expected function or variable declaration after pub"); - } - - TokenIndex comptime_token = eat_token_if(pc, TokenIdKeywordCompTime); - - AstNode *container_field = ast_parse_container_field(pc); - if (container_field != nullptr) { - switch (field_state) { - case ContainerFieldStateNone: - field_state = ContainerFieldStateSeen; - break; - case ContainerFieldStateSeen: - break; - case ContainerFieldStateEnd: - ast_error(pc, first_token, "declarations are not allowed between container fields"); - } - - assert(container_field->type == NodeTypeStructField); - container_field->data.struct_field.doc_comments = first_doc_token; - container_field->data.struct_field.comptime_token = comptime_token; - res.fields.append(container_field); - if (eat_token_if(pc, TokenIdComma) != 0) { - continue; - } else { - break; - } - } - - break; - } - return res; -} - -// TestDecl <- KEYWORD_test STRINGLITERALSINGLE? Block -static AstNode *ast_parse_test_decl(ParseContext *pc) { - TokenIndex test = eat_token_if(pc, TokenIdKeywordTest); - if (test == 0) - return nullptr; - - TokenIndex name = eat_token_if(pc, TokenIdStringLiteral); - AstNode *block = ast_expect(pc, ast_parse_block); - AstNode *res = ast_create_node(pc, NodeTypeTestDecl, test); - res->data.test_decl.name = name ? token_buf(pc, name) : nullptr; - res->data.test_decl.body = block; - return res; -} - -// TopLevelComptime <- KEYWORD_comptime BlockExpr -static AstNode *ast_parse_top_level_comptime(ParseContext *pc) { - TokenIndex comptime = eat_token_if(pc, TokenIdKeywordCompTime); - if (comptime == 0) - return nullptr; - - // 1 token lookahead because it could be a comptime struct field - TokenIndex lbrace = peek_token(pc); - if (pc->token_ids[lbrace] != TokenIdLBrace) { - put_back_token(pc); - return nullptr; - } - - AstNode *block = ast_expect(pc, ast_parse_block_expr); - AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); - res->data.comptime_expr.expr = block; - return res; -} - -// TopLevelDecl -// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) -// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl -// / KEYWORD_use Expr SEMICOLON -static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, - TokenIndex doc_comments) -{ - TokenIndex first = eat_token_if(pc, TokenIdKeywordExport); - if (first == 0) - first = eat_token_if(pc, TokenIdKeywordExtern); - if (first == 0) - first = eat_token_if(pc, TokenIdKeywordInline); - if (first == 0) - first = eat_token_if(pc, TokenIdKeywordNoInline); - if (first != 0) { - TokenIndex lib_name = 0; - if (pc->token_ids[first] == TokenIdKeywordExtern) - lib_name = eat_token_if(pc, TokenIdStringLiteral); - - if (pc->token_ids[first] != TokenIdKeywordNoInline && pc->token_ids[first] != TokenIdKeywordInline) { - TokenIndex thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal); - AstNode *var_decl = ast_parse_var_decl(pc); - if (var_decl != nullptr) { - assert(var_decl->type == NodeTypeVariableDeclaration); - if (pc->token_ids[first] == TokenIdKeywordExtern && var_decl->data.variable_declaration.expr != nullptr) { - ast_error(pc, first, "extern variables have no initializers"); - } - var_decl->main_token = first; - var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw; - var_decl->data.variable_declaration.visib_mod = visib_mod; - var_decl->data.variable_declaration.doc_comments = doc_comments; - var_decl->data.variable_declaration.is_extern = pc->token_ids[first] == TokenIdKeywordExtern; - var_decl->data.variable_declaration.is_export = pc->token_ids[first] == TokenIdKeywordExport; - var_decl->data.variable_declaration.lib_name = token_buf(pc, lib_name); - return var_decl; - } - - if (thread_local_kw != 0) - put_back_token(pc); - } - - AstNode *fn_proto = ast_parse_fn_proto(pc); - if (fn_proto != nullptr) { - AstNode *body = ast_parse_block(pc); - if (body == nullptr) - expect_token(pc, TokenIdSemicolon); - - assert(fn_proto->type == NodeTypeFnProto); - fn_proto->main_token = first; - fn_proto->data.fn_proto.visib_mod = visib_mod; - fn_proto->data.fn_proto.doc_comments = doc_comments; - if (!fn_proto->data.fn_proto.is_extern) - fn_proto->data.fn_proto.is_extern = pc->token_ids[first] == TokenIdKeywordExtern; - fn_proto->data.fn_proto.is_export = pc->token_ids[first] == TokenIdKeywordExport; - switch (pc->token_ids[first]) { - case TokenIdKeywordInline: - fn_proto->data.fn_proto.fn_inline = FnInlineAlways; - break; - case TokenIdKeywordNoInline: - fn_proto->data.fn_proto.fn_inline = FnInlineNever; - break; - default: - fn_proto->data.fn_proto.fn_inline = FnInlineAuto; - break; - } - fn_proto->data.fn_proto.lib_name = token_buf(pc, lib_name); - - AstNode *res = fn_proto; - if (body != nullptr) { - if (fn_proto->data.fn_proto.is_extern) { - ast_error(pc, first, "extern functions have no body"); - } - res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto); - res->data.fn_def.fn_proto = fn_proto; - res->data.fn_def.body = body; - fn_proto->data.fn_proto.fn_def_node = res; - } - - return res; - } - - ast_invalid_token_error(pc, peek_token(pc)); - } - - TokenIndex thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal); - AstNode *var_decl = ast_parse_var_decl(pc); - if (var_decl != nullptr) { - assert(var_decl->type == NodeTypeVariableDeclaration); - var_decl->data.variable_declaration.visib_mod = visib_mod; - var_decl->data.variable_declaration.doc_comments = doc_comments; - var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw; - return var_decl; - } - - if (thread_local_kw != 0) - put_back_token(pc); - - AstNode *fn_proto = ast_parse_fn_proto(pc); - if (fn_proto != nullptr) { - AstNode *body = ast_parse_block(pc); - if (body == nullptr) - expect_token(pc, TokenIdSemicolon); - - assert(fn_proto->type == NodeTypeFnProto); - fn_proto->data.fn_proto.visib_mod = visib_mod; - fn_proto->data.fn_proto.doc_comments = doc_comments; - AstNode *res = fn_proto; - if (body != nullptr) { - res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto); - res->data.fn_def.fn_proto = fn_proto; - res->data.fn_def.body = body; - fn_proto->data.fn_proto.fn_def_node = res; - } - - return res; - } - - TokenIndex usingnamespace = eat_token_if(pc, TokenIdKeywordUsingNamespace); - if (usingnamespace != 0) { - AstNode *expr = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdSemicolon); - - AstNode *res = ast_create_node(pc, NodeTypeUsingNamespace, usingnamespace); - res->data.using_namespace.visib_mod = visib_mod; - res->data.using_namespace.expr = expr; - return res; - } - - return nullptr; -} - -// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_anytype / TypeExpr) -static AstNode *ast_parse_fn_proto(ParseContext *pc) { - TokenIndex first = eat_token_if(pc, TokenIdKeywordFn); - if (first == 0) { - return nullptr; - } - - TokenIndex identifier = eat_token_if(pc, TokenIdIdentifier); - expect_token(pc, TokenIdLParen); - ZigList<AstNode *> params = ast_parse_list(pc, TokenIdComma, ast_parse_param_decl); - expect_token(pc, TokenIdRParen); - - AstNode *align_expr = ast_parse_byte_align(pc); - AstNode *section_expr = ast_parse_link_section(pc); - AstNode *callconv_expr = ast_parse_callconv(pc); - TokenIndex exmark = 0; - AstNode *return_type = nullptr; - - exmark = eat_token_if(pc, TokenIdBang); - return_type = ast_parse_type_expr(pc); - if (return_type == nullptr) { - TokenIndex next = peek_token(pc); - ast_error( - pc, - next, - "expected return type (use 'void' to return nothing), found: '%s'", - token_name(pc->token_ids[next]) - ); - } - - AstNode *res = ast_create_node(pc, NodeTypeFnProto, first); - res->data.fn_proto = {}; - res->data.fn_proto.name = token_buf(pc, identifier); - res->data.fn_proto.params = params; - res->data.fn_proto.align_expr = align_expr; - res->data.fn_proto.section_expr = section_expr; - res->data.fn_proto.callconv_expr = callconv_expr; - res->data.fn_proto.auto_err_set = exmark != 0; - res->data.fn_proto.return_type = return_type; - - for (size_t i = 0; i < params.length; i++) { - AstNode *param_decl = params.at(i); - assert(param_decl->type == NodeTypeParamDecl); - if (param_decl->data.param_decl.is_var_args) - res->data.fn_proto.is_var_args = true; - if (i != params.length - 1 && res->data.fn_proto.is_var_args) - ast_error(pc, first, "Function prototype have varargs as a none last parameter."); - } - return res; -} - -// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON -static AstNode *ast_parse_var_decl(ParseContext *pc) { - TokenIndex mut_kw = eat_token_if(pc, TokenIdKeywordConst); - if (mut_kw == 0) - mut_kw = eat_token_if(pc, TokenIdKeywordVar); - if (mut_kw == 0) - return nullptr; - - TokenIndex identifier = expect_token(pc, TokenIdIdentifier); - AstNode *type_expr = nullptr; - if (eat_token_if(pc, TokenIdColon) != 0) - type_expr = ast_expect(pc, ast_parse_type_expr); - - AstNode *align_expr = ast_parse_byte_align(pc); - AstNode *section_expr = ast_parse_link_section(pc); - AstNode *expr = nullptr; - if (eat_token_if(pc, TokenIdEq) != 0) - expr = ast_expect(pc, ast_parse_expr); - - expect_token(pc, TokenIdSemicolon); - - AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw); - res->data.variable_declaration.is_const = pc->token_ids[mut_kw] == TokenIdKeywordConst; - res->data.variable_declaration.symbol = token_buf(pc, identifier); - res->data.variable_declaration.type = type_expr; - res->data.variable_declaration.align_expr = align_expr; - res->data.variable_declaration.section_expr = section_expr; - res->data.variable_declaration.expr = expr; - return res; -} - -// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? -static AstNode *ast_parse_container_field(ParseContext *pc) { - TokenIndex identifier = eat_token_if(pc, TokenIdIdentifier); - if (identifier == 0) - return nullptr; - - AstNode *type_expr = nullptr; - if (eat_token_if(pc, TokenIdColon) != 0) { - TokenIndex anytype_tok = eat_token_if(pc, TokenIdKeywordAnyType); - if (anytype_tok != 0) { - type_expr = ast_create_node(pc, NodeTypeAnyTypeField, anytype_tok); - } else { - type_expr = ast_expect(pc, ast_parse_type_expr); - } - } - AstNode *align_expr = ast_parse_byte_align(pc); - AstNode *expr = nullptr; - if (eat_token_if(pc, TokenIdEq) != 0) - expr = ast_expect(pc, ast_parse_expr); - - AstNode *res = ast_create_node(pc, NodeTypeStructField, identifier); - res->data.struct_field.name = token_buf(pc, identifier); - res->data.struct_field.type = type_expr; - res->data.struct_field.value = expr; - res->data.struct_field.align_expr = align_expr; - return res; -} - -// Statement -// <- KEYWORD_comptime? VarDecl -// / KEYWORD_comptime BlockExprStatement -// / KEYWORD_nosuspend BlockExprStatement -// / KEYWORD_suspend (SEMICOLON / BlockExprStatement) -// / KEYWORD_defer BlockExprStatement -// / KEYWORD_errdefer Payload? BlockExprStatement -// / IfStatement -// / LabeledStatement -// / SwitchExpr -// / AssignExpr SEMICOLON -static AstNode *ast_parse_statement(ParseContext *pc) { - TokenIndex comptime = eat_token_if(pc, TokenIdKeywordCompTime); - AstNode *var_decl = ast_parse_var_decl(pc); - if (var_decl != nullptr) { - assert(var_decl->type == NodeTypeVariableDeclaration); - var_decl->data.variable_declaration.is_comptime = comptime != 0; - return var_decl; - } - - if (comptime != 0) { - AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); - AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); - res->data.comptime_expr.expr = statement; - return res; - } - - TokenIndex nosuspend = eat_token_if(pc, TokenIdKeywordNoSuspend); - if (nosuspend != 0) { - AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); - AstNode *res = ast_create_node(pc, NodeTypeNoSuspend, nosuspend); - res->data.nosuspend_expr.expr = statement; - return res; - } - - TokenIndex suspend = eat_token_if(pc, TokenIdKeywordSuspend); - if (suspend != 0) { - AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); - AstNode *res = ast_create_node(pc, NodeTypeSuspend, suspend); - res->data.suspend.block = statement; - return res; - } - - TokenIndex defer = eat_token_if(pc, TokenIdKeywordDefer); - if (defer == 0) - defer = eat_token_if(pc, TokenIdKeywordErrdefer); - if (defer != 0) { - TokenIndex payload = (pc->token_ids[defer] == TokenIdKeywordErrdefer) ? - ast_parse_payload(pc) : 0; - AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); - AstNode *res = ast_create_node(pc, NodeTypeDefer, defer); - - res->data.defer.kind = ReturnKindUnconditional; - res->data.defer.expr = statement; - if (pc->token_ids[defer] == TokenIdKeywordErrdefer) { - res->data.defer.kind = ReturnKindError; - if (payload != 0) - res->data.defer.err_payload = token_identifier(pc, payload); - } - return res; - } - - AstNode *if_statement = ast_parse_if_statement(pc); - if (if_statement != nullptr) - return if_statement; - - AstNode *labeled_statement = ast_parse_labeled_statement(pc); - if (labeled_statement != nullptr) - return labeled_statement; - - AstNode *switch_expr = ast_parse_switch_expr(pc); - if (switch_expr != nullptr) - return switch_expr; - - AstNode *assign = ast_parse_assign_expr(pc); - if (assign != nullptr) { - expect_token(pc, TokenIdSemicolon); - return assign; - } - - return nullptr; -} - -// IfStatement -// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? -// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) -static AstNode *ast_parse_if_statement(ParseContext *pc) { - AstNode *res = ast_parse_if_prefix(pc); - if (res == nullptr) - return nullptr; - - AstNode *body = ast_parse_block_expr(pc); - bool requires_semi = false; - if (body == nullptr) { - requires_semi = true; - body = ast_parse_assign_expr(pc); - } - - if (body == nullptr) { - TokenIndex tok = eat_token(pc); - ast_error(pc, tok, "expected if body, found '%s'", token_name(pc->token_ids[tok])); - } - - TokenIndex err_payload = 0; - AstNode *else_body = nullptr; - if (eat_token_if(pc, TokenIdKeywordElse) != 0) { - err_payload = ast_parse_payload(pc); - else_body = ast_expect(pc, ast_parse_statement); - } - - if (requires_semi && else_body == nullptr) - expect_token(pc, TokenIdSemicolon); - - assert(res->type == NodeTypeIfOptional); - if (err_payload != 0) { - AstNodeTestExpr old = res->data.test_expr; - res->type = NodeTypeIfErrorExpr; - res->data.if_err_expr.target_node = old.target_node; - res->data.if_err_expr.var_is_ptr = old.var_is_ptr; - res->data.if_err_expr.var_symbol = old.var_symbol; - res->data.if_err_expr.then_node = body; - res->data.if_err_expr.err_symbol = token_buf(pc, err_payload); - res->data.if_err_expr.else_node = else_body; - return res; - } - - if (res->data.test_expr.var_symbol != nullptr) { - res->data.test_expr.then_node = body; - res->data.test_expr.else_node = else_body; - return res; - } - - AstNodeTestExpr old = res->data.test_expr; - res->type = NodeTypeIfBoolExpr; - res->data.if_bool_expr.condition = old.target_node; - res->data.if_bool_expr.then_block = body; - res->data.if_bool_expr.else_node = else_body; - return res; -} - -// LabeledStatement <- BlockLabel? (Block / LoopStatement) -static AstNode *ast_parse_labeled_statement(ParseContext *pc) { - TokenIndex label = ast_parse_block_label(pc); - AstNode *block = ast_parse_block(pc); - if (block != nullptr) { - assert(block->type == NodeTypeBlock); - block->data.block.name = token_buf(pc, label); - return block; - } - - AstNode *loop = ast_parse_loop_statement(pc); - if (loop != nullptr) { - switch (loop->type) { - case NodeTypeForExpr: - loop->data.for_expr.name = token_buf(pc, label); - break; - case NodeTypeWhileExpr: - loop->data.while_expr.name = token_buf(pc, label); - break; - default: - zig_unreachable(); - } - return loop; - } - - if (label != 0) - ast_invalid_token_error(pc, peek_token(pc)); - return nullptr; -} - -// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) -static AstNode *ast_parse_loop_statement(ParseContext *pc) { - TokenIndex inline_token = eat_token_if(pc, TokenIdKeywordInline); - AstNode *for_statement = ast_parse_for_statement(pc); - if (for_statement != nullptr) { - assert(for_statement->type == NodeTypeForExpr); - for_statement->data.for_expr.is_inline = inline_token != 0; - return for_statement; - } - - AstNode *while_statement = ast_parse_while_statement(pc); - if (while_statement != nullptr) { - assert(while_statement->type == NodeTypeWhileExpr); - while_statement->data.while_expr.is_inline = inline_token != 0; - return while_statement; - } - - if (inline_token != 0) - ast_invalid_token_error(pc, peek_token(pc)); - return nullptr; -} - -// ForStatement -// <- ForPrefix BlockExpr ( KEYWORD_else Statement )? -// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) -static AstNode *ast_parse_for_statement(ParseContext *pc) { - AstNode *res = ast_parse_for_prefix(pc); - if (res == nullptr) - return nullptr; - - AstNode *body = ast_parse_block_expr(pc); - bool requires_semi = false; - if (body == nullptr) { - requires_semi = true; - body = ast_parse_assign_expr(pc); - } - - if (body == nullptr) { - TokenIndex tok = eat_token(pc); - ast_error(pc, tok, "expected loop body, found '%s'", token_name(pc->token_ids[tok])); - } - - AstNode *else_body = nullptr; - if (eat_token_if(pc, TokenIdKeywordElse) != 0) { - else_body = ast_expect(pc, ast_parse_statement); - } - - if (requires_semi && else_body == nullptr) - expect_token(pc, TokenIdSemicolon); - - assert(res->type == NodeTypeForExpr); - res->data.for_expr.body = body; - res->data.for_expr.else_node = else_body; - return res; -} - -// WhileStatement -// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? -// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) -static AstNode *ast_parse_while_statement(ParseContext *pc) { - AstNode *res = ast_parse_while_prefix(pc); - if (res == nullptr) - return nullptr; - - AstNode *body = ast_parse_block_expr(pc); - bool requires_semi = false; - if (body == nullptr) { - requires_semi = true; - body = ast_parse_assign_expr(pc); - } - - if (body == nullptr) { - TokenIndex tok = eat_token(pc); - ast_error(pc, tok, "expected loop body, found '%s'", token_name(pc->token_ids[tok])); - } - - TokenIndex err_payload = 0; - AstNode *else_body = nullptr; - if (eat_token_if(pc, TokenIdKeywordElse) != 0) { - err_payload = ast_parse_payload(pc); - else_body = ast_expect(pc, ast_parse_statement); - } - - if (requires_semi && else_body == nullptr) - expect_token(pc, TokenIdSemicolon); - - assert(res->type == NodeTypeWhileExpr); - res->data.while_expr.body = body; - res->data.while_expr.err_symbol = token_buf(pc, err_payload); - res->data.while_expr.else_node = else_body; - return res; -} - - -// BlockExprStatement -// <- BlockExpr -// / AssignExpr SEMICOLON -static AstNode *ast_parse_block_expr_statement(ParseContext *pc) { - AstNode *block = ast_parse_block_expr(pc); - if (block != nullptr) - return block; - - AstNode *assign_expr = ast_parse_assign_expr(pc); - if (assign_expr != nullptr) { - expect_token(pc, TokenIdSemicolon); - return assign_expr; - } - - return nullptr; -} - -// BlockExpr <- BlockLabel? Block -static AstNode *ast_parse_block_expr(ParseContext *pc) { - TokenIndex label = ast_parse_block_label(pc); - if (label != 0) { - AstNode *res = ast_expect(pc, ast_parse_block); - assert(res->type == NodeTypeBlock); - res->data.block.name = token_buf(pc, label); - return res; - } - - return ast_parse_block(pc); -} - -// AssignExpr <- Expr (AssignOp Expr)? -static AstNode *ast_parse_assign_expr(ParseContext *pc) { - return ast_parse_bin_op_expr(pc, BinOpChainOnce, ast_parse_assign_op, ast_parse_expr); -} - -// Expr <- KEYWORD_try* BoolOrExpr -static AstNode *ast_parse_expr(ParseContext *pc) { - return ast_parse_prefix_op_expr( - pc, - [](ParseContext *context) { - TokenIndex try_token = eat_token_if(context, TokenIdKeywordTry); - if (try_token != 0) { - AstNode *res = ast_create_node(context, NodeTypeReturnExpr, try_token); - res->data.return_expr.kind = ReturnKindError; - return res; - } - - return (AstNode*)nullptr; - }, - ast_parse_bool_or_expr - ); -} - -// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)* -static AstNode *ast_parse_bool_or_expr(ParseContext *pc) { - return ast_parse_bin_op_expr( - pc, - BinOpChainInf, - ast_parse_bin_op_simple<TokenIdKeywordOr, BinOpTypeBoolOr>, - ast_parse_bool_and_expr - ); -} - -// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)* -static AstNode *ast_parse_bool_and_expr(ParseContext *pc) { - return ast_parse_bin_op_expr( - pc, - BinOpChainInf, - ast_parse_bin_op_simple<TokenIdKeywordAnd, BinOpTypeBoolAnd>, - ast_parse_compare_expr - ); -} - -// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)? -static AstNode *ast_parse_compare_expr(ParseContext *pc) { - return ast_parse_bin_op_expr(pc, BinOpChainOnce, ast_parse_compare_op, ast_parse_bitwise_expr); -} - -// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* -static AstNode *ast_parse_bitwise_expr(ParseContext *pc) { - return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_bitwise_op, ast_parse_bit_shift_expr); -} - -// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)* -static AstNode *ast_parse_bit_shift_expr(ParseContext *pc) { - return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_bit_shift_op, ast_parse_addition_expr); -} - -// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)* -static AstNode *ast_parse_addition_expr(ParseContext *pc) { - return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_addition_op, ast_parse_multiply_expr); -} - -// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)* -static AstNode *ast_parse_multiply_expr(ParseContext *pc) { - return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_multiply_op, ast_parse_prefix_expr); -} - -// PrefixExpr <- PrefixOp* PrimaryExpr -static AstNode *ast_parse_prefix_expr(ParseContext *pc) { - return ast_parse_prefix_op_expr( - pc, - ast_parse_prefix_op, - ast_parse_primary_expr - ); -} - -// PrimaryExpr -// <- AsmExpr -// / IfExpr -// / KEYWORD_break BreakLabel? Expr? -// / KEYWORD_comptime Expr -// / KEYWORD_nosuspend Expr -// / KEYWORD_continue BreakLabel? -// / KEYWORD_resume Expr -// / KEYWORD_return Expr? -// / BlockLabel? LoopExpr -// / Block -// / CurlySuffixExpr -static AstNode *ast_parse_primary_expr(ParseContext *pc) { - AstNode *asm_expr = ast_parse_asm_expr(pc); - if (asm_expr != nullptr) - return asm_expr; - - AstNode *if_expr = ast_parse_if_expr(pc); - if (if_expr != nullptr) - return if_expr; - - TokenIndex break_token = eat_token_if(pc, TokenIdKeywordBreak); - if (break_token != 0) { - TokenIndex label = ast_parse_break_label(pc); - AstNode *expr = ast_parse_expr(pc); - - AstNode *res = ast_create_node(pc, NodeTypeBreak, break_token); - res->data.break_expr.name = token_buf(pc, label); - res->data.break_expr.expr = expr; - return res; - } - - TokenIndex comptime = eat_token_if(pc, TokenIdKeywordCompTime); - if (comptime != 0) { - AstNode *expr = ast_expect(pc, ast_parse_expr); - AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); - res->data.comptime_expr.expr = expr; - return res; - } - - TokenIndex nosuspend = eat_token_if(pc, TokenIdKeywordNoSuspend); - if (nosuspend != 0) { - AstNode *expr = ast_expect(pc, ast_parse_expr); - AstNode *res = ast_create_node(pc, NodeTypeNoSuspend, nosuspend); - res->data.nosuspend_expr.expr = expr; - return res; - } - - TokenIndex continue_token = eat_token_if(pc, TokenIdKeywordContinue); - if (continue_token != 0) { - TokenIndex label = ast_parse_break_label(pc); - AstNode *res = ast_create_node(pc, NodeTypeContinue, continue_token); - res->data.continue_expr.name = token_buf(pc, label); - return res; - } - - TokenIndex resume = eat_token_if(pc, TokenIdKeywordResume); - if (resume != 0) { - AstNode *expr = ast_expect(pc, ast_parse_expr); - AstNode *res = ast_create_node(pc, NodeTypeResume, resume); - res->data.resume_expr.expr = expr; - return res; - } - - TokenIndex return_token = eat_token_if(pc, TokenIdKeywordReturn); - if (return_token != 0) { - AstNode *expr = ast_parse_expr(pc); - AstNode *res = ast_create_node(pc, NodeTypeReturnExpr, return_token); - res->data.return_expr.expr = expr; - return res; - } - - TokenIndex label = ast_parse_block_label(pc); - AstNode *loop = ast_parse_loop_expr(pc); - if (loop != nullptr) { - switch (loop->type) { - case NodeTypeForExpr: - loop->data.for_expr.name = token_buf(pc, label); - break; - case NodeTypeWhileExpr: - loop->data.while_expr.name = token_buf(pc, label); - break; - default: - zig_unreachable(); - } - return loop; - } else if (label != 0) { - // Restore the tokens that we eaten by ast_parse_block_label. - put_back_token(pc); - put_back_token(pc); - } - - AstNode *block = ast_parse_block(pc); - if (block != nullptr) - return block; - - AstNode *curly_suffix = ast_parse_curly_suffix_expr(pc); - if (curly_suffix != nullptr) - return curly_suffix; - - return nullptr; -} - -// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? -static AstNode *ast_parse_if_expr(ParseContext *pc) { - return ast_parse_if_expr_helper(pc, ast_parse_expr); -} - -// Block <- LBRACE Statement* RBRACE -static AstNode *ast_parse_block(ParseContext *pc) { - TokenIndex lbrace = eat_token_if(pc, TokenIdLBrace); - if (lbrace == 0) - return nullptr; - - ZigList<AstNode *> statements = {}; - AstNode *statement; - while ((statement = ast_parse_statement(pc)) != nullptr) - statements.append(statement); - - expect_token(pc, TokenIdRBrace); - - AstNode *res = ast_create_node(pc, NodeTypeBlock, lbrace); - res->data.block.statements = statements; - return res; -} - -// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr) -static AstNode *ast_parse_loop_expr(ParseContext *pc) { - return ast_parse_loop_expr_helper( - pc, - ast_parse_for_expr, - ast_parse_while_expr - ); -} - -// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? -static AstNode *ast_parse_for_expr(ParseContext *pc) { - return ast_parse_for_expr_helper(pc, ast_parse_expr); -} - -// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? -static AstNode *ast_parse_while_expr(ParseContext *pc) { - return ast_parse_while_expr_helper(pc, ast_parse_expr); -} - -// CurlySuffixExpr <- TypeExpr InitList? -static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc) { - AstNode *type_expr = ast_parse_type_expr(pc); - if (type_expr == nullptr) - return nullptr; - - AstNode *res = ast_parse_init_list(pc); - if (res == nullptr) - return type_expr; - - assert(res->type == NodeTypeContainerInitExpr); - res->data.container_init_expr.type = type_expr; - return res; -} - -// InitList -// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE -// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE -// / LBRACE RBRACE -static AstNode *ast_parse_init_list(ParseContext *pc) { - TokenIndex lbrace = eat_token_if(pc, TokenIdLBrace); - if (lbrace == 0) - return nullptr; - - AstNode *first = ast_parse_field_init(pc); - if (first != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeContainerInitExpr, lbrace); - res->data.container_init_expr.kind = ContainerInitKindStruct; - res->data.container_init_expr.entries.append(first); - - while (eat_token_if(pc, TokenIdComma) != 0) { - AstNode *field_init = ast_parse_field_init(pc); - if (field_init == nullptr) - break; - res->data.container_init_expr.entries.append(field_init); - } - - expect_token(pc, TokenIdRBrace); - return res; - } - - AstNode *res = ast_create_node(pc, NodeTypeContainerInitExpr, lbrace); - res->data.container_init_expr.kind = ContainerInitKindArray; - - first = ast_parse_expr(pc); - if (first != nullptr) { - res->data.container_init_expr.entries.append(first); - - while (eat_token_if(pc, TokenIdComma) != 0) { - AstNode *expr = ast_parse_expr(pc); - if (expr == nullptr) - break; - res->data.container_init_expr.entries.append(expr); - } - - expect_token(pc, TokenIdRBrace); - return res; - } - - expect_token(pc, TokenIdRBrace); - return res; -} - -// TypeExpr <- PrefixTypeOp* ErrorUnionExpr -static AstNode *ast_parse_type_expr(ParseContext *pc) { - return ast_parse_prefix_op_expr( - pc, - ast_parse_prefix_type_op, - ast_parse_error_union_expr - ); -} - -// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? -static AstNode *ast_parse_error_union_expr(ParseContext *pc) { - AstNode *res = ast_parse_suffix_expr(pc); - if (res == nullptr) - return nullptr; - - AstNode *op = ast_parse_bin_op_simple<TokenIdBang, BinOpTypeErrorUnion>(pc); - if (op == nullptr) - return res; - - AstNode *right = ast_expect(pc, ast_parse_type_expr); - assert(op->type == NodeTypeBinOpExpr); - op->data.bin_op_expr.op1 = res; - op->data.bin_op_expr.op2 = right; - return op; -} - -// SuffixExpr -// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments -// / PrimaryTypeExpr (SuffixOp / FnCallArguments)* -static AstNode *ast_parse_suffix_expr(ParseContext *pc) { - TokenIndex async_token = eat_token_if(pc, TokenIdKeywordAsync); - if (async_token) { - AstNode *child = ast_expect(pc, ast_parse_primary_type_expr); - while (true) { - AstNode *suffix = ast_parse_suffix_op(pc); - if (suffix == nullptr) - break; - - switch (suffix->type) { - case NodeTypeSliceExpr: - suffix->data.slice_expr.array_ref_expr = child; - break; - case NodeTypeArrayAccessExpr: - suffix->data.array_access_expr.array_ref_expr = child; - break; - case NodeTypeFieldAccessExpr: - suffix->data.field_access_expr.struct_expr = child; - break; - case NodeTypeUnwrapOptional: - suffix->data.unwrap_optional.expr = child; - break; - case NodeTypePtrDeref: - suffix->data.ptr_deref_expr.target = child; - break; - default: - zig_unreachable(); - } - child = suffix; - } - - // TODO: Both *_async_prefix and *_fn_call_arguments returns an - // AstNode *. All we really want here is the arguments of - // the call we parse. We therefor "leak" the node for now. - // Wait till we get async rework to fix this. - AstNode *args = ast_parse_fn_call_arguments(pc); - if (args == nullptr) - ast_invalid_token_error(pc, peek_token(pc)); - - assert(args->type == NodeTypeFnCallExpr); - - AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, async_token); - res->data.fn_call_expr.modifier = CallModifierAsync; - res->data.fn_call_expr.seen = false; - res->data.fn_call_expr.fn_ref_expr = child; - res->data.fn_call_expr.params = args->data.fn_call_expr.params; - return res; - } - - AstNode *res = ast_parse_primary_type_expr(pc); - if (res == nullptr) - return nullptr; - - while (true) { - AstNode *suffix = ast_parse_suffix_op(pc); - if (suffix != nullptr) { - switch (suffix->type) { - case NodeTypeSliceExpr: - suffix->data.slice_expr.array_ref_expr = res; - break; - case NodeTypeArrayAccessExpr: - suffix->data.array_access_expr.array_ref_expr = res; - break; - case NodeTypeFieldAccessExpr: - suffix->data.field_access_expr.struct_expr = res; - break; - case NodeTypeUnwrapOptional: - suffix->data.unwrap_optional.expr = res; - break; - case NodeTypePtrDeref: - suffix->data.ptr_deref_expr.target = res; - break; - default: - zig_unreachable(); - } - res = suffix; - continue; - } - - AstNode * call = ast_parse_fn_call_arguments(pc); - if (call != nullptr) { - assert(call->type == NodeTypeFnCallExpr); - call->data.fn_call_expr.fn_ref_expr = res; - res = call; - continue; - } - - break; - } - - return res; - -} - -// PrimaryTypeExpr -// <- BUILTINIDENTIFIER FnCallArguments -// / CHAR_LITERAL -// / ContainerDecl -// / DOT IDENTIFIER -// / ErrorSetDecl -// / FLOAT -// / FnProto -// / GroupedExpr -// / LabeledTypeExpr -// / IDENTIFIER -// / IfTypeExpr -// / INTEGER -// / KEYWORD_comptime TypeExpr -// / KEYWORD_error DOT IDENTIFIER -// / KEYWORD_promise -// / KEYWORD_unreachable -// / STRINGLITERAL -// / SwitchExpr -static AstNode *ast_parse_primary_type_expr(ParseContext *pc) { - TokenIndex builtin_tok = eat_token_if(pc, TokenIdBuiltin); - if (builtin_tok != 0) { - AstNode *res = ast_expect(pc, ast_parse_fn_call_arguments); - AstNode *name_sym = ast_create_node(pc, NodeTypeIdentifier, builtin_tok); - - assert(res->type == NodeTypeFnCallExpr); - res->main_token = builtin_tok; - res->data.fn_call_expr.fn_ref_expr = name_sym; - res->data.fn_call_expr.modifier = CallModifierBuiltin; - return res; - } - - TokenIndex char_lit = eat_token_if(pc, TokenIdCharLiteral); - if (char_lit != 0) { - return ast_create_node(pc, NodeTypeCharLiteral, char_lit); - } - - AstNode *container_decl = ast_parse_container_decl(pc); - if (container_decl != nullptr) - return container_decl; - - AstNode *anon_lit = ast_parse_anon_lit(pc); - if (anon_lit != nullptr) - return anon_lit; - - AstNode *error_set_decl = ast_parse_error_set_decl(pc); - if (error_set_decl != nullptr) - return error_set_decl; - - TokenIndex float_lit = eat_token_if(pc, TokenIdFloatLiteral); - if (float_lit != 0) { - return ast_create_node(pc, NodeTypeFloatLiteral, float_lit); - } - - AstNode *fn_proto = ast_parse_fn_proto(pc); - if (fn_proto != nullptr) - return fn_proto; - - AstNode *grouped_expr = ast_parse_grouped_expr(pc); - if (grouped_expr != nullptr) - return grouped_expr; - - AstNode *labeled_type_expr = ast_parse_labeled_type_expr(pc); - if (labeled_type_expr != nullptr) - return labeled_type_expr; - - TokenIndex identifier = eat_token_if(pc, TokenIdIdentifier); - if (identifier != 0) - return token_identifier(pc, identifier); - - AstNode *if_type_expr = ast_parse_if_type_expr(pc); - if (if_type_expr != nullptr) - return if_type_expr; - - TokenIndex int_lit = eat_token_if(pc, TokenIdIntLiteral); - if (int_lit != 0) { - return ast_create_node(pc, NodeTypeIntLiteral, int_lit); - } - - TokenIndex comptime = eat_token_if(pc, TokenIdKeywordCompTime); - if (comptime != 0) { - AstNode *expr = ast_expect(pc, ast_parse_type_expr); - AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); - res->data.comptime_expr.expr = expr; - return res; - } - - TokenIndex error = eat_token_if(pc, TokenIdKeywordError); - if (error != 0) { - TokenIndex dot = expect_token(pc, TokenIdDot); - TokenIndex name = expect_token(pc, TokenIdIdentifier); - AstNode *left = ast_create_node(pc, NodeTypeErrorType, error); - AstNode *res = ast_create_node(pc, NodeTypeFieldAccessExpr, dot); - res->data.field_access_expr.struct_expr = left; - res->data.field_access_expr.field_name = token_buf(pc, name); - return res; - } - - TokenIndex anyframe = eat_token_if(pc, TokenIdKeywordAnyFrame); - if (anyframe != 0) - return ast_create_node(pc, NodeTypeAnyFrameType, anyframe); - - TokenIndex unreachable = eat_token_if(pc, TokenIdKeywordUnreachable); - if (unreachable != 0) - return ast_create_node(pc, NodeTypeUnreachable, unreachable); - - - TokenIndex string_lit = eat_token_if(pc, TokenIdStringLiteral); - if (string_lit != 0) { - return ast_create_node(pc, NodeTypeStringLiteral, string_lit); - } - - TokenIndex multiline_str_lit = ast_parse_multi_tok(pc, TokenIdMultilineStringLiteralLine); - if (multiline_str_lit != 0) { - return ast_create_node(pc, NodeTypeStringLiteral, multiline_str_lit); - } - - AstNode *switch_expr = ast_parse_switch_expr(pc); - if (switch_expr != nullptr) - return switch_expr; - - return nullptr; -} - -// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto -static AstNode *ast_parse_container_decl(ParseContext *pc) { - TokenIndex layout_token = eat_token_if(pc, TokenIdKeywordExtern); - if (layout_token == 0) - layout_token = eat_token_if(pc, TokenIdKeywordPacked); - - AstNode *res = ast_parse_container_decl_auto(pc); - if (res == nullptr) { - if (layout_token != 0) - put_back_token(pc); - return nullptr; - } - - assert(res->type == NodeTypeContainerDecl); - if (layout_token != 0) { - res->main_token = layout_token; - res->data.container_decl.layout = pc->token_ids[layout_token] == TokenIdKeywordExtern - ? ContainerLayoutExtern - : ContainerLayoutPacked; - } - return res; -} - -// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE -static AstNode *ast_parse_error_set_decl(ParseContext *pc) { - TokenIndex first = eat_token_if(pc, TokenIdKeywordError); - if (first == 0) - return nullptr; - if (eat_token_if(pc, TokenIdLBrace) == 0) { - put_back_token(pc); - return nullptr; - } - - ZigList<AstNode *> decls = ast_parse_list<AstNode>(pc, TokenIdComma, [](ParseContext *context) { - TokenIndex doc_token = ast_parse_doc_comments(context); - TokenIndex ident = eat_token_if(context, TokenIdIdentifier); - if (ident == 0) - return (AstNode*)nullptr; - - AstNode *symbol_node = token_identifier(context, ident); - if (doc_token == 0) - return symbol_node; - - AstNode *field_node = ast_create_node(context, NodeTypeErrorSetField, doc_token); - field_node->data.err_set_field.field_name = symbol_node; - field_node->data.err_set_field.doc_comments = doc_token; - return field_node; - }); - expect_token(pc, TokenIdRBrace); - - AstNode *res = ast_create_node(pc, NodeTypeErrorSetDecl, first); - res->data.err_set_decl.decls = decls; - return res; -} - -// GroupedExpr <- LPAREN Expr RPAREN -static AstNode *ast_parse_grouped_expr(ParseContext *pc) { - TokenIndex lparen = eat_token_if(pc, TokenIdLParen); - if (lparen == 0) - return nullptr; - - AstNode *expr = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - - AstNode *res = ast_create_node(pc, NodeTypeGroupedExpr, lparen); - res->data.grouped_expr = expr; - return res; -} - -// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? -static AstNode *ast_parse_if_type_expr(ParseContext *pc) { - return ast_parse_if_expr_helper(pc, ast_parse_type_expr); -} - -// LabeledTypeExpr -// <- BlockLabel Block -// / BlockLabel? LoopTypeExpr -static AstNode *ast_parse_labeled_type_expr(ParseContext *pc) { - TokenIndex label = ast_parse_block_label(pc); - if (label != 0) { - AstNode *block = ast_parse_block(pc); - if (block != nullptr) { - assert(block->type == NodeTypeBlock); - block->data.block.name = token_buf(pc, label); - return block; - } - } - - AstNode *loop = ast_parse_loop_type_expr(pc); - if (loop != nullptr) { - switch (loop->type) { - case NodeTypeForExpr: - loop->data.for_expr.name = token_buf(pc, label); - break; - case NodeTypeWhileExpr: - loop->data.while_expr.name = token_buf(pc, label); - break; - default: - zig_unreachable(); - } - return loop; - } - - if (label != 0) { - put_back_token(pc); - put_back_token(pc); - } - return nullptr; -} - -// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) -static AstNode *ast_parse_loop_type_expr(ParseContext *pc) { - return ast_parse_loop_expr_helper( - pc, - ast_parse_for_type_expr, - ast_parse_while_type_expr - ); -} - -// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? -static AstNode *ast_parse_for_type_expr(ParseContext *pc) { - return ast_parse_for_expr_helper(pc, ast_parse_type_expr); -} - -// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? -static AstNode *ast_parse_while_type_expr(ParseContext *pc) { - return ast_parse_while_expr_helper(pc, ast_parse_type_expr); -} - -// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE -static AstNode *ast_parse_switch_expr(ParseContext *pc) { - TokenIndex switch_token = eat_token_if(pc, TokenIdKeywordSwitch); - if (switch_token == 0) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *expr = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - expect_token(pc, TokenIdLBrace); - ZigList<AstNode *> prongs = ast_parse_list(pc, TokenIdComma, ast_parse_switch_prong); - expect_token(pc, TokenIdRBrace); - - AstNode *res = ast_create_node(pc, NodeTypeSwitchExpr, switch_token); - res->data.switch_expr.expr = expr; - res->data.switch_expr.prongs = prongs; - return res; -} - -// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN STRINGLITERAL AsmOutput? RPAREN -static AstNode *ast_parse_asm_expr(ParseContext *pc) { - TokenIndex asm_token = eat_token_if(pc, TokenIdKeywordAsm); - if (asm_token == 0) - return nullptr; - - TokenIndex volatile_token = eat_token_if(pc, TokenIdKeywordVolatile); - expect_token(pc, TokenIdLParen); - AstNode *asm_template = ast_expect(pc, ast_parse_expr); - AstNode *res = ast_parse_asm_output(pc); - if (res == nullptr) - res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); - expect_token(pc, TokenIdRParen); - - res->main_token = asm_token; - res->data.asm_expr.volatile_token = volatile_token; - res->data.asm_expr.asm_template = asm_template; - return res; -} - -static AstNode *ast_parse_anon_lit(ParseContext *pc) { - TokenIndex period = eat_token_if(pc, TokenIdDot); - if (period == 0) - return nullptr; - - // anon enum literal - TokenIndex identifier = eat_token_if(pc, TokenIdIdentifier); - if (identifier != 0) { - return ast_create_node(pc, NodeTypeEnumLiteral, period); - } - - // anon container literal - AstNode *res = ast_parse_init_list(pc); - if (res != nullptr) - return res; - put_back_token(pc); - return nullptr; -} - -// AsmOutput <- COLON AsmOutputList AsmInput? -static AstNode *ast_parse_asm_output(ParseContext *pc) { - if (eat_token_if(pc, TokenIdColon) == 0) - return nullptr; - - ZigList<AsmOutput *> output_list = ast_parse_list(pc, TokenIdComma, ast_parse_asm_output_item); - AstNode *res = ast_parse_asm_input(pc); - if (res == nullptr) - res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); - - res->data.asm_expr.output_list = output_list; - return res; -} - -// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN -static AsmOutput *ast_parse_asm_output_item(ParseContext *pc) { - if (eat_token_if(pc, TokenIdLBracket) == 0) - return nullptr; - - TokenIndex sym_name = expect_token(pc, TokenIdIdentifier); - expect_token(pc, TokenIdRBracket); - - TokenIndex str = ast_parse_multi_tok(pc, TokenIdMultilineStringLiteralLine); - if (str == 0) - str = expect_token(pc, TokenIdStringLiteral); - expect_token(pc, TokenIdLParen); - - TokenIndex var_name = eat_token_if(pc, TokenIdIdentifier); - AstNode *return_type = nullptr; - if (var_name == 0) { - expect_token(pc, TokenIdArrow); - return_type = ast_expect(pc, ast_parse_type_expr); - } - - expect_token(pc, TokenIdRParen); - - AsmOutput *res = heap::c_allocator.create<AsmOutput>(); - res->asm_symbolic_name = token_buf(pc, sym_name); - res->constraint = token_buf(pc, str); - res->variable_name = token_buf(pc, var_name); - res->return_type = return_type; - return res; -} - -// AsmInput <- COLON AsmInputList AsmClobbers? -static AstNode *ast_parse_asm_input(ParseContext *pc) { - if (eat_token_if(pc, TokenIdColon) == 0) - return nullptr; - - ZigList<AsmInput *> input_list = ast_parse_list(pc, TokenIdComma, ast_parse_asm_input_item); - AstNode *res = ast_parse_asm_clobbers(pc); - if (res == nullptr) - res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); - - res->data.asm_expr.input_list = input_list; - return res; -} - -// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN -static AsmInput *ast_parse_asm_input_item(ParseContext *pc) { - if (eat_token_if(pc, TokenIdLBracket) == 0) - return nullptr; - - TokenIndex sym_name = expect_token(pc, TokenIdIdentifier); - expect_token(pc, TokenIdRBracket); - - TokenIndex constraint = expect_token(pc, TokenIdStringLiteral); - expect_token(pc, TokenIdLParen); - AstNode *expr = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - - AsmInput *res = heap::c_allocator.create<AsmInput>(); - res->asm_symbolic_name = token_buf(pc, sym_name); - res->constraint = token_buf(pc, constraint); - res->expr = expr; - return res; -} - -// AsmClobbers <- COLON StringList -static AstNode *ast_parse_asm_clobbers(ParseContext *pc) { - if (eat_token_if(pc, TokenIdColon) == 0) - return nullptr; - - ZigList<Buf *> clobber_list = ast_parse_list<Buf>(pc, TokenIdComma, [](ParseContext *context) { - TokenIndex str = eat_token_if(context, TokenIdStringLiteral); - if (str == 0) - str = ast_parse_multi_tok(context, TokenIdMultilineStringLiteralLine); - if (str != 0) - return token_buf(context, str); - return (Buf*)nullptr; - }); - - AstNode *res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); - res->data.asm_expr.clobber_list = clobber_list; - return res; -} - -// BreakLabel <- COLON IDENTIFIER -static TokenIndex ast_parse_break_label(ParseContext *pc) { - if (eat_token_if(pc, TokenIdColon) == 0) - return 0; - - return expect_token(pc, TokenIdIdentifier); -} - -// BlockLabel <- IDENTIFIER COLON -static TokenIndex ast_parse_block_label(ParseContext *pc) { - TokenIndex ident = eat_token_if(pc, TokenIdIdentifier); - if (ident == 0) - return 0; - - // We do 2 token lookahead here, as we don't want to error when - // parsing identifiers. - if (eat_token_if(pc, TokenIdColon) == 0) { - put_back_token(pc); - return 0; - } - - return ident; -} - -// FieldInit <- DOT IDENTIFIER EQUAL Expr -static AstNode *ast_parse_field_init(ParseContext *pc) { - TokenIndex first = eat_token_if(pc, TokenIdDot); - if (first == 0) - return nullptr; - - TokenIndex name = eat_token_if(pc, TokenIdIdentifier); - if (name == 0) { - // Because of anon literals ".{" is also valid. - put_back_token(pc); - return nullptr; - } - if (eat_token_if(pc, TokenIdEq) == 0) { - // Because ".Name" can also be interpreted as an enum literal, we should put back - // those two tokens again so that the parser can try to parse them as the enum - // literal later. - put_back_token(pc); - put_back_token(pc); - return nullptr; - } - AstNode *expr = ast_expect(pc, ast_parse_expr); - - AstNode *res = ast_create_node(pc, NodeTypeStructValueField, first); - res->data.struct_val_field.name = token_buf(pc, name); - res->data.struct_val_field.expr = expr; - return res; -} - -// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN -static AstNode *ast_parse_while_continue_expr(ParseContext *pc) { - TokenIndex first = eat_token_if(pc, TokenIdColon); - if (first == 0) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *expr = ast_expect(pc, ast_parse_assign_expr); - expect_token(pc, TokenIdRParen); - return expr; -} - -// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN -static AstNode *ast_parse_link_section(ParseContext *pc) { - TokenIndex first = eat_token_if(pc, TokenIdKeywordLinkSection); - if (first == 0) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *res = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - return res; -} - -// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN -static AstNode *ast_parse_callconv(ParseContext *pc) { - TokenIndex first = eat_token_if(pc, TokenIdKeywordCallconv); - if (first == 0) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *res = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - return res; -} - -// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType -static AstNode *ast_parse_param_decl(ParseContext *pc) { - TokenIndex first_doc_comment = ast_parse_doc_comments(pc); - - TokenIndex first = eat_token_if(pc, TokenIdKeywordNoAlias); - if (first == 0) - first = eat_token_if(pc, TokenIdKeywordCompTime); - - TokenIndex name = eat_token_if(pc, TokenIdIdentifier); - if (name != 0) { - if (eat_token_if(pc, TokenIdColon) != 0) { - if (first == 0) - first = name; - } else { - // We put back the ident, so it can be parsed as a ParamType - // later. - put_back_token(pc); - name = 0; - } - } - - AstNode *res; - if (first == 0) { - first = peek_token(pc); - res = ast_parse_param_type(pc); - } else { - res = ast_expect(pc, ast_parse_param_type); - } - - if (res == nullptr) - return nullptr; - - assert(res->type == NodeTypeParamDecl); - res->main_token = first; - res->data.param_decl.name = token_buf(pc, name); - res->data.param_decl.doc_comments = first_doc_comment; - res->data.param_decl.is_noalias = pc->token_ids[first] == TokenIdKeywordNoAlias; - res->data.param_decl.is_comptime = pc->token_ids[first] == TokenIdKeywordCompTime; - return res; -} - -// ParamType -// <- KEYWORD_anytype -// / DOT3 -// / TypeExpr -static AstNode *ast_parse_param_type(ParseContext *pc) { - TokenIndex anytype_token = eat_token_if(pc, TokenIdKeywordAnyType); - if (anytype_token != 0) { - AstNode *res = ast_create_node(pc, NodeTypeParamDecl, anytype_token); - res->data.param_decl.anytype_token = anytype_token; - return res; - } - - TokenIndex dots = eat_token_if(pc, TokenIdEllipsis3); - if (dots != 0) { - AstNode *res = ast_create_node(pc, NodeTypeParamDecl, dots); - res->data.param_decl.is_var_args = true; - return res; - } - - AstNode *type_expr = ast_parse_type_expr(pc); - if (type_expr != nullptr) { - AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeParamDecl, type_expr); - res->data.param_decl.type = type_expr; - return res; - } - - return nullptr; -} - -// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload? -static AstNode *ast_parse_if_prefix(ParseContext *pc) { - TokenIndex first = eat_token_if(pc, TokenIdKeywordIf); - if (first == 0) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *condition = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - Optional<PtrPayload> opt_payload = ast_parse_ptr_payload(pc); - - PtrPayload payload; - AstNode *res = ast_create_node(pc, NodeTypeIfOptional, first); - res->data.test_expr.target_node = condition; - if (opt_payload.unwrap(&payload)) { - res->data.test_expr.var_symbol = token_buf(pc, payload.payload); - res->data.test_expr.var_is_ptr = payload.asterisk != 0; - } - return res; -} - -// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? -static AstNode *ast_parse_while_prefix(ParseContext *pc) { - TokenIndex while_token = eat_token_if(pc, TokenIdKeywordWhile); - if (while_token == 0) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *condition = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - Optional<PtrPayload> opt_payload = ast_parse_ptr_payload(pc); - AstNode *continue_expr = ast_parse_while_continue_expr(pc); - - PtrPayload payload; - AstNode *res = ast_create_node(pc, NodeTypeWhileExpr, while_token); - res->data.while_expr.condition = condition; - res->data.while_expr.continue_expr = continue_expr; - if (opt_payload.unwrap(&payload)) { - res->data.while_expr.var_symbol = token_buf(pc, payload.payload); - res->data.while_expr.var_is_ptr = payload.asterisk != 0; - } - - return res; -} - -// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload -static AstNode *ast_parse_for_prefix(ParseContext *pc) { - TokenIndex for_token = eat_token_if(pc, TokenIdKeywordFor); - if (for_token == 0) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *array_expr = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - PtrIndexPayload payload; - if (!ast_parse_ptr_index_payload(pc).unwrap(&payload)) - ast_invalid_token_error(pc, peek_token(pc)); - - AstNode *res = ast_create_node(pc, NodeTypeForExpr, for_token); - res->data.for_expr.array_expr = array_expr; - res->data.for_expr.elem_node = token_identifier(pc, payload.payload); - res->data.for_expr.elem_is_ptr = payload.asterisk != 0; - if (payload.index != 0) - res->data.for_expr.index_node = token_identifier(pc, payload.index); - - return res; -} - -// Payload <- PIPE IDENTIFIER PIPE -static TokenIndex ast_parse_payload(ParseContext *pc) { - if (eat_token_if(pc, TokenIdBinOr) == 0) - return 0; - - TokenIndex res = expect_token(pc, TokenIdIdentifier); - expect_token(pc, TokenIdBinOr); - return res; -} - -// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE -static Optional<PtrPayload> ast_parse_ptr_payload(ParseContext *pc) { - if (eat_token_if(pc, TokenIdBinOr) == 0) - return Optional<PtrPayload>::none(); - - TokenIndex asterisk = eat_token_if(pc, TokenIdStar); - TokenIndex payload = expect_token(pc, TokenIdIdentifier); - expect_token(pc, TokenIdBinOr); - - PtrPayload res; - res.asterisk = asterisk; - res.payload = payload; - return Optional<PtrPayload>::some(res); -} - -// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE -static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc) { - if (eat_token_if(pc, TokenIdBinOr) == 0) - return Optional<PtrIndexPayload>::none(); - - TokenIndex asterisk = eat_token_if(pc, TokenIdStar); - TokenIndex payload = expect_token(pc, TokenIdIdentifier); - TokenIndex index = 0; - if (eat_token_if(pc, TokenIdComma) != 0) - index = expect_token(pc, TokenIdIdentifier); - expect_token(pc, TokenIdBinOr); - - PtrIndexPayload res; - res.asterisk = asterisk; - res.payload = payload; - res.index = index; - return Optional<PtrIndexPayload>::some(res); -} - -// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr -static AstNode *ast_parse_switch_prong(ParseContext *pc) { - AstNode *res = ast_parse_switch_case(pc); - if (res == nullptr) - return nullptr; - - expect_token(pc, TokenIdFatArrow); - Optional<PtrIndexPayload> opt_payload = ast_parse_ptr_index_payload(pc); - AstNode *expr = ast_expect(pc, ast_parse_assign_expr); - - PtrIndexPayload payload; - assert(res->type == NodeTypeSwitchProng); - res->data.switch_prong.expr = expr; - if (opt_payload.unwrap(&payload)) { - res->data.switch_prong.var_symbol = token_identifier(pc, payload.payload); - res->data.switch_prong.var_is_ptr = payload.asterisk != 0; - } - - return res; -} - -// SwitchCase -// <- SwitchItem (COMMA SwitchItem)* COMMA? -// / KEYWORD_else -static AstNode *ast_parse_switch_case(ParseContext *pc) { - bool is_inline = eat_token_if(pc, TokenIdKeywordInline) != 0; - AstNode *first = ast_parse_switch_item(pc); - if (first != nullptr) { - AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeSwitchProng, first); - res->data.switch_prong.is_inline = is_inline; - res->data.switch_prong.items.append(first); - res->data.switch_prong.any_items_are_range = first->type == NodeTypeSwitchRange; - - while (eat_token_if(pc, TokenIdComma) != 0) { - AstNode *item = ast_parse_switch_item(pc); - if (item == nullptr) - break; - - res->data.switch_prong.items.append(item); - res->data.switch_prong.any_items_are_range |= item->type == NodeTypeSwitchRange; - } - - return res; - } - - TokenIndex else_token = eat_token_if(pc, TokenIdKeywordElse); - if (else_token != 0) { - AstNode *res = ast_create_node(pc, NodeTypeSwitchProng, else_token); - res->data.switch_prong.is_inline = is_inline; - return res; - } - - if (is_inline) pc->current_token -= 1; - return nullptr; -} - -// SwitchItem <- Expr (DOT3 Expr)? -static AstNode *ast_parse_switch_item(ParseContext *pc) { - AstNode *expr = ast_parse_expr(pc); - if (expr == nullptr) - return nullptr; - - TokenIndex dots = eat_token_if(pc, TokenIdEllipsis3); - if (dots != 0) { - AstNode *expr2 = ast_expect(pc, ast_parse_expr); - AstNode *res = ast_create_node(pc, NodeTypeSwitchRange, dots); - res->data.switch_range.start = expr; - res->data.switch_range.end = expr2; - return res; - } - - return expr; -} - -// AssignOp -// <- ASTERISKEQUAL -// / SLASHEQUAL -// / PERCENTEQUAL -// / PLUSEQUAL -// / MINUSEQUAL -// / LARROW2EQUAL -// / LARROW2PIPEEQUAL -// / RARROW2EQUAL -// / AMPERSANDEQUAL -// / CARETEQUAL -// / PIPEEQUAL -// / ASTERISKPERCENTEQUAL -// / PLUSPERCENTEQUAL -// / MINUSPERCENTEQUAL -// / ASTERISKPIPEEQUAL -// / PLUSPIPEEQUAL -// / MINUSPIPEEQUAL -// / EQUAL -static AstNode *ast_parse_assign_op(ParseContext *pc) { - // In C, we have `T arr[N] = {[i] = T{}};` but it doesn't - // seem to work in C++... - BinOpType table[TokenIdCount] = {}; - table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd; - table[TokenIdBitOrEq] = BinOpTypeAssignBitOr; - table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft; - table[TokenIdBitShiftLeftPipeEq] = BinOpTypeAssignBitShiftLeftSat; - table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight; - table[TokenIdBitXorEq] = BinOpTypeAssignBitXor; - table[TokenIdDivEq] = BinOpTypeAssignDiv; - table[TokenIdEq] = BinOpTypeAssign; - table[TokenIdMinusEq] = BinOpTypeAssignMinus; - table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap; - table[TokenIdMinusPipeEq] = BinOpTypeAssignMinusSat; - table[TokenIdModEq] = BinOpTypeAssignMod; - table[TokenIdPlusEq] = BinOpTypeAssignPlus; - table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap; - table[TokenIdPlusPipeEq] = BinOpTypeAssignPlusSat; - table[TokenIdTimesEq] = BinOpTypeAssignTimes; - table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap; - table[TokenIdTimesPipeEq] = BinOpTypeAssignTimesSat; - - BinOpType op = table[pc->token_ids[pc->current_token]]; - if (op != BinOpTypeInvalid) { - TokenIndex op_token = eat_token(pc); - AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); - res->data.bin_op_expr.bin_op = op; - return res; - } - - return nullptr; - -} - -// CompareOp -// <- EQUALEQUAL -// / EXCLAMATIONMARKEQUAL -// / LARROW -// / RARROW -// / LARROWEQUAL -// / RARROWEQUAL -static AstNode *ast_parse_compare_op(ParseContext *pc) { - BinOpType table[TokenIdCount] = {}; - table[TokenIdCmpEq] = BinOpTypeCmpEq; - table[TokenIdCmpNotEq] = BinOpTypeCmpNotEq; - table[TokenIdCmpLessThan] = BinOpTypeCmpLessThan; - table[TokenIdCmpGreaterThan] = BinOpTypeCmpGreaterThan; - table[TokenIdCmpLessOrEq] = BinOpTypeCmpLessOrEq; - table[TokenIdCmpGreaterOrEq] = BinOpTypeCmpGreaterOrEq; - - BinOpType op = table[pc->token_ids[pc->current_token]]; - if (op != BinOpTypeInvalid) { - TokenIndex op_token = eat_token(pc); - AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); - res->data.bin_op_expr.bin_op = op; - return res; - } - - return nullptr; -} - -// BitwiseOp -// <- AMPERSAND -// / CARET -// / PIPE -// / KEYWORD_orelse -// / KEYWORD_catch Payload? -static AstNode *ast_parse_bitwise_op(ParseContext *pc) { - BinOpType table[TokenIdCount] = {}; - table[TokenIdAmpersand] = BinOpTypeBinAnd; - table[TokenIdBinXor] = BinOpTypeBinXor; - table[TokenIdBinOr] = BinOpTypeBinOr; - table[TokenIdKeywordOrElse] = BinOpTypeUnwrapOptional; - - BinOpType op = table[pc->token_ids[pc->current_token]]; - if (op != BinOpTypeInvalid) { - TokenIndex op_token = eat_token(pc); - AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); - res->data.bin_op_expr.bin_op = op; - return res; - } - - TokenIndex catch_token = eat_token_if(pc, TokenIdKeywordCatch); - if (catch_token != 0) { - TokenIndex payload = ast_parse_payload(pc); - AstNode *res = ast_create_node(pc, NodeTypeCatchExpr, catch_token); - if (payload != 0) - res->data.unwrap_err_expr.symbol = token_identifier(pc, payload); - - return res; - } - - return nullptr; -} - -// BitShiftOp -// <- LARROW2 -// / LARROW2PIPE -// / RARROW2 -static AstNode *ast_parse_bit_shift_op(ParseContext *pc) { - BinOpType table[TokenIdCount] = {}; - table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft; - table[TokenIdBitShiftLeftPipe] = BinOpTypeBitShiftLeftSat; - table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight; - - BinOpType op = table[pc->token_ids[pc->current_token]]; - if (op != BinOpTypeInvalid) { - TokenIndex op_token = eat_token(pc); - AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); - res->data.bin_op_expr.bin_op = op; - return res; - } - - return nullptr; -} - -// AdditionOp -// <- PLUS -// / MINUS -// / PLUS2 -// / PLUSPERCENT -// / MINUSPERCENT -// / PLUSPIPE -// / MINUSPIPE -static AstNode *ast_parse_addition_op(ParseContext *pc) { - BinOpType table[TokenIdCount] = {}; - table[TokenIdPlus] = BinOpTypeAdd; - table[TokenIdDash] = BinOpTypeSub; - table[TokenIdPlusPlus] = BinOpTypeArrayCat; - table[TokenIdPlusPercent] = BinOpTypeAddWrap; - table[TokenIdMinusPercent] = BinOpTypeSubWrap; - table[TokenIdPlusPipe] = BinOpTypeAddSat; - table[TokenIdMinusPipe] = BinOpTypeSubSat; - - BinOpType op = table[pc->token_ids[pc->current_token]]; - if (op != BinOpTypeInvalid) { - TokenIndex op_token = eat_token(pc); - AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); - res->data.bin_op_expr.bin_op = op; - return res; - } - - return nullptr; -} - -// MultiplyOp -// <- PIPE2 -// / ASTERISK -// / SLASH -// / PERCENT -// / ASTERISK2 -// / ASTERISKPERCENT -// / ASTERISKPIPE -static AstNode *ast_parse_multiply_op(ParseContext *pc) { - BinOpType table[TokenIdCount] = {}; - table[TokenIdBarBar] = BinOpTypeMergeErrorSets; - table[TokenIdStar] = BinOpTypeMult; - table[TokenIdSlash] = BinOpTypeDiv; - table[TokenIdPercent] = BinOpTypeMod; - table[TokenIdStarStar] = BinOpTypeArrayMult; - table[TokenIdTimesPercent] = BinOpTypeMultWrap; - table[TokenIdTimesPipe] = BinOpTypeMultSat; - - BinOpType op = table[pc->token_ids[pc->current_token]]; - if (op != BinOpTypeInvalid) { - TokenIndex op_token = eat_token(pc); - AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); - res->data.bin_op_expr.bin_op = op; - return res; - } - - return nullptr; -} - -// PrefixOp -// <- EXCLAMATIONMARK -// / MINUS -// / TILDE -// / MINUSPERCENT -// / AMPERSAND -// / KEYWORD_try -// / KEYWORD_await -static AstNode *ast_parse_prefix_op(ParseContext *pc) { - PrefixOp table[TokenIdCount] = {}; - table[TokenIdBang] = PrefixOpBoolNot; - table[TokenIdDash] = PrefixOpNegation; - table[TokenIdTilde] = PrefixOpBinNot; - table[TokenIdMinusPercent] = PrefixOpNegationWrap; - table[TokenIdAmpersand] = PrefixOpAddrOf; - - PrefixOp op = table[pc->token_ids[pc->current_token]]; - if (op != PrefixOpInvalid) { - TokenIndex op_token = eat_token(pc); - AstNode *res = ast_create_node(pc, NodeTypePrefixOpExpr, op_token); - res->data.prefix_op_expr.prefix_op = op; - return res; - } - - TokenIndex try_token = eat_token_if(pc, TokenIdKeywordTry); - if (try_token != 0) { - AstNode *res = ast_create_node(pc, NodeTypeReturnExpr, try_token); - res->data.return_expr.kind = ReturnKindError; - return res; - } - - TokenIndex await = eat_token_if(pc, TokenIdKeywordAwait); - if (await != 0) { - AstNode *res = ast_create_node(pc, NodeTypeAwaitExpr, await); - return res; - } - - return nullptr; -} - -// PrefixTypeOp -// <- QUESTIONMARK -// / KEYWORD_anyframe MINUSRARROW -// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile)* -// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile)* -static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { - TokenIndex questionmark = eat_token_if(pc, TokenIdQuestion); - if (questionmark != 0) { - AstNode *res = ast_create_node(pc, NodeTypePrefixOpExpr, questionmark); - res->data.prefix_op_expr.prefix_op = PrefixOpOptional; - return res; - } - - TokenIndex anyframe = eat_token_if(pc, TokenIdKeywordAnyFrame); - if (anyframe != 0) { - if (eat_token_if(pc, TokenIdArrow) != 0) { - AstNode *res = ast_create_node(pc, NodeTypeAnyFrameType, anyframe); - return res; - } - - put_back_token(pc); - } - - TokenIndex arr_init_lbracket = eat_token_if(pc, TokenIdLBracket); - if (arr_init_lbracket != 0) { - TokenIndex underscore = eat_token_if(pc, TokenIdIdentifier); - if (underscore == 0) { - put_back_token(pc); - } else if (!buf_eql_str(token_buf(pc, underscore), "_")) { - put_back_token(pc); - put_back_token(pc); - } else { - AstNode *sentinel = nullptr; - TokenIndex colon = eat_token_if(pc, TokenIdColon); - if (colon != 0) { - sentinel = ast_expect(pc, ast_parse_expr); - } - expect_token(pc, TokenIdRBracket); - AstNode *node = ast_create_node(pc, NodeTypeInferredArrayType, arr_init_lbracket); - node->data.inferred_array_type.sentinel = sentinel; - return node; - } - } - - - AstNode *ptr = ast_parse_ptr_type_start(pc); - if (ptr != nullptr) { - assert(ptr->type == NodeTypePointerType); - // We might get two pointers from *_ptr_type_start - AstNode *child = ptr->data.pointer_type.op_expr; - if (child == nullptr) - child = ptr; - while (true) { - TokenIndex allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero); - if (allowzero_token != 0) { - child->data.pointer_type.allow_zero_token = allowzero_token; - continue; - } - - if (eat_token_if(pc, TokenIdKeywordAlign) != 0) { - expect_token(pc, TokenIdLParen); - AstNode *align_expr = ast_expect(pc, ast_parse_expr); - child->data.pointer_type.align_expr = align_expr; - if (eat_token_if(pc, TokenIdColon) != 0) { - TokenIndex bit_offset_start = expect_token(pc, TokenIdIntLiteral); - expect_token(pc, TokenIdColon); - TokenIndex host_int_bytes = expect_token(pc, TokenIdIntLiteral); - child->data.pointer_type.bit_offset_start = bit_offset_start; - child->data.pointer_type.host_int_bytes = host_int_bytes; - } - expect_token(pc, TokenIdRParen); - continue; - } - - if (eat_token_if(pc, TokenIdKeywordConst) != 0) { - child->data.pointer_type.is_const = true; - continue; - } - - if (eat_token_if(pc, TokenIdKeywordVolatile) != 0) { - child->data.pointer_type.is_volatile = true; - continue; - } - - break; - } - - return ptr; - } - - AstNode *array = ast_parse_array_type_start(pc); - if (array != nullptr) { - assert(array->type == NodeTypeArrayType); - while (true) { - TokenIndex allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero); - if (allowzero_token != 0) { - array->data.array_type.allow_zero_token = allowzero_token; - continue; - } - - AstNode *align_expr = ast_parse_byte_align(pc); - if (align_expr != nullptr) { - array->data.array_type.align_expr = align_expr; - continue; - } - - if (eat_token_if(pc, TokenIdKeywordConst) != 0) { - array->data.array_type.is_const = true; - continue; - } - - if (eat_token_if(pc, TokenIdKeywordVolatile) != 0) { - array->data.array_type.is_volatile = true; - continue; - } - break; - } - - return array; - } - - - return nullptr; -} - -// SuffixOp -// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET -// / DOT IDENTIFIER -// / DOTASTERISK -// / DOTQUESTIONMARK -static AstNode *ast_parse_suffix_op(ParseContext *pc) { - TokenIndex lbracket = eat_token_if(pc, TokenIdLBracket); - if (lbracket != 0) { - AstNode *start = ast_expect(pc, ast_parse_expr); - AstNode *end = nullptr; - if (eat_token_if(pc, TokenIdEllipsis2) != 0) { - AstNode *sentinel = nullptr; - end = ast_parse_expr(pc); - if (eat_token_if(pc, TokenIdColon) != 0) { - sentinel = ast_parse_expr(pc); - } - expect_token(pc, TokenIdRBracket); - - AstNode *res = ast_create_node(pc, NodeTypeSliceExpr, lbracket); - res->data.slice_expr.start = start; - res->data.slice_expr.end = end; - res->data.slice_expr.sentinel = sentinel; - return res; - } - - expect_token(pc, TokenIdRBracket); - - AstNode *res = ast_create_node(pc, NodeTypeArrayAccessExpr, lbracket); - res->data.array_access_expr.subscript = start; - return res; - } - - TokenIndex dot_asterisk = eat_token_if(pc, TokenIdDotStar); - if (dot_asterisk != 0) - return ast_create_node(pc, NodeTypePtrDeref, dot_asterisk); - - TokenIndex dot = eat_token_if(pc, TokenIdDot); - if (dot != 0) { - if (eat_token_if(pc, TokenIdQuestion) != 0) - return ast_create_node(pc, NodeTypeUnwrapOptional, dot); - - TokenIndex ident = expect_token(pc, TokenIdIdentifier); - AstNode *res = ast_create_node(pc, NodeTypeFieldAccessExpr, dot); - res->data.field_access_expr.field_name = token_buf(pc, ident); - return res; - } - - return nullptr; -} - -// FnCallArguments <- LPAREN ExprList RPAREN -static AstNode *ast_parse_fn_call_arguments(ParseContext *pc) { - TokenIndex paren = eat_token_if(pc, TokenIdLParen); - if (paren == 0) - return nullptr; - - ZigList<AstNode *> params = ast_parse_list(pc, TokenIdComma, ast_parse_expr); - expect_token(pc, TokenIdRParen); - - AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, paren); - res->data.fn_call_expr.params = params; - res->data.fn_call_expr.seen = false; - return res; -} - -// ArrayTypeStart <- LBRACKET Expr? RBRACKET -static AstNode *ast_parse_array_type_start(ParseContext *pc) { - TokenIndex lbracket = eat_token_if(pc, TokenIdLBracket); - if (lbracket == 0) - return nullptr; - - AstNode *size = ast_parse_expr(pc); - AstNode *sentinel = nullptr; - TokenIndex colon = eat_token_if(pc, TokenIdColon); - if (colon != 0) { - sentinel = ast_expect(pc, ast_parse_expr); - } - expect_token(pc, TokenIdRBracket); - AstNode *res = ast_create_node(pc, NodeTypeArrayType, lbracket); - res->data.array_type.size = size; - res->data.array_type.sentinel = sentinel; - return res; -} - -// PtrTypeStart -// <- ASTERISK -// / ASTERISK2 -// / PTRUNKNOWN -// / PTRC -static AstNode *ast_parse_ptr_type_start(ParseContext *pc) { - AstNode *sentinel = nullptr; - - TokenIndex asterisk = eat_token_if(pc, TokenIdStar); - if (asterisk != 0) { - TokenIndex colon = eat_token_if(pc, TokenIdColon); - if (colon != 0) { - sentinel = ast_expect(pc, ast_parse_expr); - } - AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk); - res->data.pointer_type.star_token = asterisk; - res->data.pointer_type.sentinel = sentinel; - return res; - } - - TokenIndex asterisk2 = eat_token_if(pc, TokenIdStarStar); - if (asterisk2 != 0) { - TokenIndex colon = eat_token_if(pc, TokenIdColon); - if (colon != 0) { - sentinel = ast_expect(pc, ast_parse_expr); - } - AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk2); - AstNode *res2 = ast_create_node(pc, NodeTypePointerType, asterisk2); - res->data.pointer_type.star_token = asterisk2; - res2->data.pointer_type.star_token = asterisk2; - res2->data.pointer_type.sentinel = sentinel; - res->data.pointer_type.op_expr = res2; - return res; - } - - TokenIndex lbracket = eat_token_if(pc, TokenIdLBracket); - if (lbracket != 0) { - TokenIndex star = eat_token_if(pc, TokenIdStar); - if (star == 0) { - put_back_token(pc); - } else { - TokenIndex c_tok = eat_token_if(pc, TokenIdIdentifier); - if (c_tok != 0) { - if (!buf_eql_str(token_buf(pc, c_tok), "c")) { - put_back_token(pc); // c symbol - } else { - expect_token(pc, TokenIdRBracket); - AstNode *res = ast_create_node(pc, NodeTypePointerType, lbracket); - res->data.pointer_type.star_token = c_tok; - return res; - } - } - - TokenIndex colon = eat_token_if(pc, TokenIdColon); - if (colon != 0) { - sentinel = ast_expect(pc, ast_parse_expr); - } - expect_token(pc, TokenIdRBracket); - AstNode *res = ast_create_node(pc, NodeTypePointerType, lbracket); - res->data.pointer_type.star_token = lbracket; - res->data.pointer_type.sentinel = sentinel; - return res; - } - } - - return nullptr; -} - -// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE -static AstNode *ast_parse_container_decl_auto(ParseContext *pc) { - AstNode *res = ast_parse_container_decl_type(pc); - if (res == nullptr) - return nullptr; - - expect_token(pc, TokenIdLBrace); - AstNodeContainerDecl members = ast_parse_container_members(pc); - expect_token(pc, TokenIdRBrace); - - res->data.container_decl.fields = members.fields; - res->data.container_decl.decls = members.decls; - res->data.container_decl.doc_comments = members.doc_comments; - return res; -} - -// ContainerDeclType -// <- KEYWORD_struct (LPAREN Expr RPAREN)? -// / KEYWORD_enum (LPAREN Expr RPAREN)? -// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? -// / KEYWORD_opaque -static AstNode *ast_parse_container_decl_type(ParseContext *pc) { - TokenIndex first = eat_token_if(pc, TokenIdKeywordStruct); - if (first != 0) { - bool explicit_backing_int = false; - if (eat_token_if(pc, TokenIdLParen) != 0) { - explicit_backing_int = true; - ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - } - AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first); - res->data.container_decl.init_arg_expr = nullptr; - res->data.container_decl.kind = ContainerKindStruct; - // We want this to be an error in semantic analysis not parsing to make sharing - // the test suite between stage1 and self hosted easier. - res->data.container_decl.unsupported_explicit_backing_int = explicit_backing_int; - return res; - } - - first = eat_token_if(pc, TokenIdKeywordOpaque); - if (first != 0) { - AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first); - res->data.container_decl.init_arg_expr = nullptr; - res->data.container_decl.kind = ContainerKindOpaque; - return res; - } - - first = eat_token_if(pc, TokenIdKeywordEnum); - if (first != 0) { - AstNode *init_arg_expr = nullptr; - if (eat_token_if(pc, TokenIdLParen) != 0) { - init_arg_expr = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - } - AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first); - res->data.container_decl.init_arg_expr = init_arg_expr; - res->data.container_decl.kind = ContainerKindEnum; - return res; - } - - first = eat_token_if(pc, TokenIdKeywordUnion); - if (first != 0) { - AstNode *init_arg_expr = nullptr; - bool auto_enum = false; - if (eat_token_if(pc, TokenIdLParen) != 0) { - if (eat_token_if(pc, TokenIdKeywordEnum) != 0) { - auto_enum = true; - if (eat_token_if(pc, TokenIdLParen) != 0) { - init_arg_expr = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - } - } else { - init_arg_expr = ast_expect(pc, ast_parse_expr); - } - - expect_token(pc, TokenIdRParen); - } - - AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first); - res->data.container_decl.init_arg_expr = init_arg_expr; - res->data.container_decl.auto_enum = auto_enum; - res->data.container_decl.kind = ContainerKindUnion; - return res; - } - - return nullptr; -} - -// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN -static AstNode *ast_parse_byte_align(ParseContext *pc) { - if (eat_token_if(pc, TokenIdKeywordAlign) == 0) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *res = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - return res; -} - -static void visit_field(AstNode **node, void (*visit)(AstNode **, void *context), void *context) { - if (*node) { - visit(node, context); - } -} - -static void visit_node_list(ZigList<AstNode *> *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 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); - 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); - visit_field(&node->data.defer.err_payload, 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 NodeTypeCatchExpr: - 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 NodeTypeIdentifier: - // 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); - 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); - visit_field(&node->data.slice_expr.sentinel, 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 NodeTypeUnwrapOptional: - visit_field(&node->data.unwrap_optional.expr, visit, context); - break; - case NodeTypeUsingNamespace: - visit_field(&node->data.using_namespace.expr, visit, context); - 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 NodeTypeIfOptional: - 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 NodeTypeNoSuspend: - 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.sentinel, visit, context); - visit_field(&node->data.array_type.child_type, visit, context); - visit_field(&node->data.array_type.align_expr, visit, context); - break; - case NodeTypeInferredArrayType: - visit_field(&node->data.array_type.sentinel, visit, context); - visit_field(&node->data.array_type.child_type, visit, context); - break; - case NodeTypeAnyFrameType: - visit_field(&node->data.anyframe_type.payload_type, visit, context); - break; - case NodeTypeErrorType: - // none - break; - case NodeTypePointerType: - visit_field(&node->data.pointer_type.sentinel, visit, context); - 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 NodeTypeErrorSetField: - visit_field(&node->data.err_set_field.field_name, 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.block, visit, context); - break; - case NodeTypeEnumLiteral: - case NodeTypeAnyTypeField: - break; - } -} - -Error source_string_literal_buf(const char *source, Buf *out, size_t *bad_index) { - size_t byte_offset = 0; - - assert(source[byte_offset] == '"'); - byte_offset += 1; - - buf_resize(out, 0); - - uint32_t codepoint; - - enum { - StateStart, - StateBackslash, - StateUnicodeLBrace, - StateUnicodeDigit, - } state = StateStart; - for (;;byte_offset += 1) { - switch (state) { - case StateStart: switch (source[byte_offset]) { - case '\\': - state = StateBackslash; - continue; - case '\n': - *bad_index = byte_offset; - return ErrorInvalidCharacter; - case '"': - return ErrorNone; - default: - buf_append_char(out, source[byte_offset]); - continue; - } - case StateBackslash: switch (source[byte_offset]) { - case 'n': - buf_append_char(out, '\n'); - state = StateStart; - continue; - case 'r': - buf_append_char(out, '\r'); - state = StateStart; - continue; - case '\\': - buf_append_char(out, '\\'); - state = StateStart; - continue; - case 't': - buf_append_char(out, '\t'); - state = StateStart; - continue; - case '\'': - buf_append_char(out, '\''); - state = StateStart; - continue; - case '"': - buf_append_char(out, '"'); - state = StateStart; - continue; - case 'x': { - byte_offset += 1; - uint8_t digit1; - if (source[byte_offset] >= '0' && source[byte_offset] <= '9') { - digit1 = source[byte_offset] - '0'; - } else if (source[byte_offset] >= 'a' && source[byte_offset] <= 'z') { - digit1 = source[byte_offset] - 'a' + 10; - } else if (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') { - digit1 = source[byte_offset] - 'A' + 10; - } else { - *bad_index = byte_offset; - return ErrorInvalidCharacter; - } - - byte_offset += 1; - uint8_t digit0; - if (source[byte_offset] >= '0' && source[byte_offset] <= '9') { - digit0 = source[byte_offset] - '0'; - } else if (source[byte_offset] >= 'a' && source[byte_offset] <= 'z') { - digit0 = source[byte_offset] - 'a' + 10; - } else if (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') { - digit0 = source[byte_offset] - 'A' + 10; - } else { - *bad_index = byte_offset; - return ErrorInvalidCharacter; - } - - buf_append_char(out, digit1 * 16 + digit0); - state = StateStart; - continue; - } - case 'u': - state = StateUnicodeLBrace; - continue; - default: - *bad_index = byte_offset; - return ErrorInvalidCharacter; - } - case StateUnicodeLBrace: switch (source[byte_offset]) { - case '{': - state = StateUnicodeDigit; - codepoint = 0; - continue; - default: - *bad_index = byte_offset; - return ErrorInvalidCharacter; - } - case StateUnicodeDigit: { - uint8_t digit; - if (source[byte_offset] >= '0' && source[byte_offset] <= '9') { - digit = source[byte_offset] - '0'; - } else if (source[byte_offset] >= 'a' && source[byte_offset] <= 'z') { - digit = source[byte_offset] - 'a' + 10; - } else if (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') { - digit = source[byte_offset] - 'A' + 10; - } else if (source[byte_offset] == '}') { - if (codepoint < 0x80) { - buf_append_char(out, codepoint); - } else if (codepoint < 0x800) { - buf_append_char(out, 0xc0 | (codepoint >> 6)); - buf_append_char(out, 0x80 | (codepoint & 0x3f)); - } else if (codepoint < 0x10000) { - buf_append_char(out, 0xe0 | (codepoint >> 12)); - buf_append_char(out, 0x80 | ((codepoint >> 6) & 0x3f)); - buf_append_char(out, 0x80 | (codepoint & 0x3f)); - } else if (codepoint < 0x110000) { - buf_append_char(out, 0xf0 | (codepoint >> 18)); - buf_append_char(out, 0x80 | ((codepoint >> 12) & 0x3f)); - buf_append_char(out, 0x80 | ((codepoint >> 6) & 0x3f)); - buf_append_char(out, 0x80 | (codepoint & 0x3f)); - } else { - *bad_index = byte_offset; - return ErrorUnicodePointTooLarge; - } - state = StateStart; - continue; - } else { - *bad_index = byte_offset; - return ErrorInvalidCharacter; - } - codepoint = codepoint * 16 + digit; - continue; - } - } - } - zig_unreachable(); -} - -static uint32_t utf8_code_point(const uint8_t *bytes) { - if (bytes[0] <= 0x7f) { - return bytes[0]; - } else if (bytes[0] >= 0xc0 && bytes[0] <= 0xdf) { - uint32_t result = bytes[0] & 0x1f; - result <<= 6; - result |= bytes[1] & 0x3f; - return result; - } else if (bytes[0] >= 0xe0 && bytes[0] <= 0xef) { - uint32_t result = bytes[0] & 0xf; - - result <<= 6; - result |= bytes[1] & 0x3f; - - result <<= 6; - result |= bytes[2] & 0x3f; - - return result; - } else if (bytes[0] >= 0xf0 && bytes[0] <= 0xf7) { - uint32_t result = bytes[0] & 0x7; - - result <<= 6; - result |= bytes[1] & 0x3f; - - result <<= 6; - result |= bytes[2] & 0x3f; - - result <<= 6; - result |= bytes[3] & 0x3f; - - return result; - } else { - zig_unreachable(); - } -} - -Error source_char_literal(const char *source, uint32_t *result, size_t *bad_index) { - if (source[0] != '\\') { - *result = utf8_code_point((const uint8_t *)source); - return ErrorNone; - } - - uint32_t byte_offset = 1; - uint32_t codepoint; - - enum State { - StateBackslash, - StateUnicodeLBrace, - StateUnicodeDigit, - } state = StateBackslash; - - for (;;byte_offset += 1) { - switch (state) { - case StateBackslash: switch (source[byte_offset]) { - case 'n': - *result = '\n'; - return ErrorNone; - case 'r': - *result = '\r'; - return ErrorNone; - case '\\': - *result = '\\'; - return ErrorNone; - case 't': - *result = '\t'; - return ErrorNone; - case '\'': - *result = '\''; - return ErrorNone; - case '"': - *result = '"'; - return ErrorNone; - case 'x': { - byte_offset += 1; - uint8_t digit1; - if (source[byte_offset] >= '0' && source[byte_offset] <= '9') { - digit1 = source[byte_offset] - '0'; - } else if (source[byte_offset] >= 'a' && source[byte_offset] <= 'z') { - digit1 = source[byte_offset] - 'a' + 10; - } else if (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') { - digit1 = source[byte_offset] - 'A' + 10; - } else { - *bad_index = byte_offset; - return ErrorInvalidCharacter; - } - - byte_offset += 1; - uint8_t digit0; - if (source[byte_offset] >= '0' && source[byte_offset] <= '9') { - digit0 = source[byte_offset] - '0'; - } else if (source[byte_offset] >= 'a' && source[byte_offset] <= 'z') { - digit0 = source[byte_offset] - 'a' + 10; - } else if (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') { - digit0 = source[byte_offset] - 'A' + 10; - } else { - *bad_index = byte_offset; - return ErrorInvalidCharacter; - } - - *result = digit1 * 16 + digit0; - return ErrorNone; - } - case 'u': - state = StateUnicodeLBrace; - continue; - default: - *bad_index = byte_offset; - return ErrorInvalidCharacter; - } - case StateUnicodeLBrace: switch (source[byte_offset]) { - case '{': - state = StateUnicodeDigit; - codepoint = 0; - continue; - default: - *bad_index = byte_offset; - return ErrorInvalidCharacter; - } - case StateUnicodeDigit: { - uint8_t digit; - if (source[byte_offset] >= '0' && source[byte_offset] <= '9') { - digit = source[byte_offset] - '0'; - } else if (source[byte_offset] >= 'a' && source[byte_offset] <= 'z') { - digit = source[byte_offset] - 'a' + 10; - } else if (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') { - digit = source[byte_offset] - 'A' + 10; - } else if (source[byte_offset] == '}') { - if (codepoint < 0x110000) { - *result = codepoint; - return ErrorNone; - } else { - *bad_index = byte_offset; - return ErrorUnicodePointTooLarge; - } - } else { - *bad_index = byte_offset; - return ErrorInvalidCharacter; - } - codepoint = codepoint * 16 + digit; - continue; - } - } - } -} - -static Buf *token_identifier_buf2(RootStruct *root_struct, TokenIndex token, bool *is_at_syntax) { - Error err; - const char *source = buf_ptr(root_struct->source_code); - size_t byte_offset = root_struct->token_locs[token].offset; - if (root_struct->token_ids[token] == TokenIdBuiltin) { - byte_offset += 1; - } else { - assert(root_struct->token_ids[token] == TokenIdIdentifier); - } - assert(source[byte_offset] != '.'); // wrong token index - - if (source[byte_offset] == '@') { - *is_at_syntax = true; - size_t bad_index; - Buf *str = buf_alloc(); - if ((err = source_string_literal_buf(source + byte_offset + 1, str, &bad_index))) { - ast_error_offset(root_struct, ErrColorAuto, token, bad_index + 1, - buf_create_from_str("invalid string literal character")); - } - return str; - } else { - *is_at_syntax = false; - size_t start = byte_offset; - for (;; byte_offset += 1) { - if (source[byte_offset] == 0) break; - if ((source[byte_offset] >= 'a' && source[byte_offset] <= 'z') || - (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') || - (source[byte_offset] >= '0' && source[byte_offset] <= '9') || - source[byte_offset] == '_') - { - continue; - } - break; - } - return buf_create_from_mem(source + start, byte_offset - start); - } -} - -Buf *token_identifier_buf(RootStruct *root_struct, TokenIndex token) { - bool trash; - return token_identifier_buf2(root_struct, token, &trash); -} - -Buf *node_identifier_buf(AstNode *node) { - bool trash; - return node_identifier_buf2(node, &trash); -} - -Buf *node_identifier_buf2(AstNode *node, bool *is_at_syntax) { - assert(node->type == NodeTypeIdentifier); - // Currently, stage1 runs astgen for every comptime function call, - // resulting the allocation here wasting memory. As a workaround until - // the code is adjusted to make astgen run only once per source node, - // we memoize the result into the AST here. - if (node->data.identifier.name == nullptr) { - RootStruct *root_struct = node->owner->data.structure.root_struct; - node->data.identifier.name = token_identifier_buf2(root_struct, node->main_token, - &node->data.identifier.is_at_syntax); - } - *is_at_syntax = node->data.identifier.is_at_syntax; - return node->data.identifier.name; -} - -void token_number_literal_bigint(RootStruct *root_struct, BigInt *result, TokenIndex token) { - const char *source = buf_ptr(root_struct->source_code); - uint32_t byte_offset = root_struct->token_locs[token].offset; - - bigint_init_unsigned(result, 0); - BigInt radix_bi; - - if (source[byte_offset] == '0') { - byte_offset += 1; - switch (source[byte_offset]) { - case 'b': - byte_offset += 1; - bigint_init_unsigned(&radix_bi, 2); - break; - case 'o': - byte_offset += 1; - bigint_init_unsigned(&radix_bi, 8); - break; - case 'x': - byte_offset += 1; - bigint_init_unsigned(&radix_bi, 16); - break; - default: - bigint_init_unsigned(&radix_bi, 10); - break; - } - } else { - bigint_init_unsigned(&radix_bi, 10); - } - - BigInt digit_value_bi = {}; - BigInt multiplied = {}; - - for (;source[byte_offset] != 0; byte_offset += 1) { - uint8_t digit; - if (source[byte_offset] >= '0' && source[byte_offset] <= '9') { - digit = source[byte_offset] - '0'; - } else if (source[byte_offset] >= 'a' && source[byte_offset] <= 'z') { - digit = source[byte_offset] - 'a' + 10; - } else if (source[byte_offset] >= 'A' && source[byte_offset] <= 'Z') { - digit = source[byte_offset] - 'A' + 10; - } else if (source[byte_offset] == '_') { - continue; - } else { - break; - } - bigint_deinit(&digit_value_bi); - bigint_init_unsigned(&digit_value_bi, digit); - - bigint_deinit(&multiplied); - bigint_mul(&multiplied, result, &radix_bi); - - bigint_add(result, &multiplied, &digit_value_bi); - } - - bigint_deinit(&digit_value_bi); - bigint_deinit(&multiplied); - bigint_deinit(&radix_bi); -} - |
