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