zig/src/parser.cpp
Andrew Kelley 6aac423964
split IrInstruction into IrInst, IrInstSrc, IrInstGen
This makes it so that less memory is used for IR instructions, as well
as catching bugs when one expected one kind of instruction and received
the other.
2020-01-25 21:49:32 -05:00

3197 lines
110 KiB
C++

/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "parser.hpp"
#include "errmsg.hpp"
#include "analyze.hpp"
#include <stdarg.h>
#include <stdio.h>
#include <limits.h>
#include <errno.h>
struct ParseContext {
Buf *buf;
size_t current_token;
ZigList<Token> *tokens;
ZigType *owner;
ErrColor err_color;
};
struct PtrPayload {
Token *asterisk;
Token *payload;
};
struct PtrIndexPayload {
Token *asterisk;
Token *payload;
Token *index;
};
static AstNode *ast_parse_root(ParseContext *pc);
static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc);
static AstNode *ast_parse_test_decl(ParseContext *pc);
static AstNode *ast_parse_top_level_comptime(ParseContext *pc);
static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, Buf *doc_comments);
static AstNode *ast_parse_fn_proto(ParseContext *pc);
static AstNode *ast_parse_var_decl(ParseContext *pc);
static AstNode *ast_parse_container_field(ParseContext *pc);
static AstNode *ast_parse_statement(ParseContext *pc);
static AstNode *ast_parse_if_statement(ParseContext *pc);
static AstNode *ast_parse_labeled_statement(ParseContext *pc);
static AstNode *ast_parse_loop_statement(ParseContext *pc);
static AstNode *ast_parse_for_statement(ParseContext *pc);
static AstNode *ast_parse_while_statement(ParseContext *pc);
static AstNode *ast_parse_block_expr_statement(ParseContext *pc);
static AstNode *ast_parse_block_expr(ParseContext *pc);
static AstNode *ast_parse_assign_expr(ParseContext *pc);
static AstNode *ast_parse_expr(ParseContext *pc);
static AstNode *ast_parse_bool_or_expr(ParseContext *pc);
static AstNode *ast_parse_bool_and_expr(ParseContext *pc);
static AstNode *ast_parse_compare_expr(ParseContext *pc);
static AstNode *ast_parse_bitwise_expr(ParseContext *pc);
static AstNode *ast_parse_bit_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_block(ParseContext *pc);
static AstNode *ast_parse_loop_expr(ParseContext *pc);
static AstNode *ast_parse_for_expr(ParseContext *pc);
static AstNode *ast_parse_while_expr(ParseContext *pc);
static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc);
static AstNode *ast_parse_init_list(ParseContext *pc);
static AstNode *ast_parse_type_expr(ParseContext *pc);
static AstNode *ast_parse_error_union_expr(ParseContext *pc);
static AstNode *ast_parse_suffix_expr(ParseContext *pc);
static AstNode *ast_parse_primary_type_expr(ParseContext *pc);
static AstNode *ast_parse_container_decl(ParseContext *pc);
static AstNode *ast_parse_error_set_decl(ParseContext *pc);
static AstNode *ast_parse_grouped_expr(ParseContext *pc);
static AstNode *ast_parse_if_type_expr(ParseContext *pc);
static AstNode *ast_parse_labeled_type_expr(ParseContext *pc);
static AstNode *ast_parse_loop_type_expr(ParseContext *pc);
static AstNode *ast_parse_for_type_expr(ParseContext *pc);
static AstNode *ast_parse_while_type_expr(ParseContext *pc);
static AstNode *ast_parse_switch_expr(ParseContext *pc);
static AstNode *ast_parse_asm_expr(ParseContext *pc);
static AstNode *ast_parse_anon_lit(ParseContext *pc);
static AstNode *ast_parse_asm_output(ParseContext *pc);
static AsmOutput *ast_parse_asm_output_item(ParseContext *pc);
static AstNode *ast_parse_asm_input(ParseContext *pc);
static AsmInput *ast_parse_asm_input_item(ParseContext *pc);
static AstNode *ast_parse_asm_clobbers(ParseContext *pc);
static 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_link_section(ParseContext *pc);
static AstNode *ast_parse_callconv(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_fn_call_arguments(ParseContext *pc);
static AstNode *ast_parse_array_type_start(ParseContext *pc);
static AstNode *ast_parse_ptr_type_start(ParseContext *pc);
static AstNode *ast_parse_container_decl_auto(ParseContext *pc);
static AstNode *ast_parse_container_decl_type(ParseContext *pc);
static AstNode *ast_parse_byte_align(ParseContext *pc);
ATTRIBUTE_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->data.structure.root_struct->path,
token->start_line, token->start_column,
pc->owner->data.structure.root_struct->source_code,
pc->owner->data.structure.root_struct->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_NORETURN
static void ast_invalid_token_error(ParseContext *pc, Token *token) {
ast_error(pc, token, "invalid token: '%s'", token_name(token->id));
}
static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) {
AstNode *node = allocate<AstNode>(1, "AstNode");
node->type = type;
node->owner = pc->owner;
return node;
}
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_copy_line_info(ParseContext *pc, NodeType type, AstNode *from) {
assert(from);
AstNode *node = ast_create_node_no_line_info(pc, type);
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 == TokenIdMultilineStringLiteral || 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 NodeTypeAnyFrameType:
right = &prefix->data.anyframe_type.payload_type;
break;
case NodeTypeArrayType:
right = &prefix->data.array_type.child_type;
break;
case NodeTypeInferredArrayType:
right = &prefix->data.inferred_array_type.child_type;
break;
case NodeTypePointerType: {
// We might get two pointers from *_ptr_type_start
AstNode *child = prefix->data.pointer_type.op_expr;
if (child == nullptr)
child = prefix;
right = &child->data.pointer_type.op_expr;
break;
}
default:
zig_unreachable();
}
}
// If we have already consumed a token, and determined that
// this node is a prefix op, then we expect that the node has
// a child.
if (res != nullptr) {
*right = ast_expect(pc, child_parser);
} else {
// Otherwise, if we didn't consume a token, then we can return
// null, if the child expr did.
*right = child_parser(pc);
if (*right == nullptr)
return nullptr;
}
return res;
}
// Child (Op Child)(*/?)
static AstNode *ast_parse_bin_op_expr(
ParseContext *pc,
BinOpChain chain,
AstNode *(*op_parse)(ParseContext*),
AstNode *(*child_parse)(ParseContext*)
) {
AstNode *res = child_parse(pc);
if (res == nullptr)
return nullptr;
do {
AstNode *op = op_parse(pc);
if (op == nullptr)
break;
AstNode *left = res;
AstNode *right = ast_expect(pc, child_parse);
res = op;
switch (op->type) {
case NodeTypeBinOpExpr:
op->data.bin_op_expr.op1 = left;
op->data.bin_op_expr.op2 = right;
break;
case NodeTypeCatchExpr:
op->data.unwrap_err_expr.op1 = left;
op->data.unwrap_err_expr.op2 = right;
break;
default:
zig_unreachable();
}
} while (chain == BinOpChainInf);
return res;
}
// IfPrefix Body (KEYWORD_else Payload? Body)?
static AstNode *ast_parse_if_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) {
AstNode *res = ast_parse_if_prefix(pc);
if (res == nullptr)
return nullptr;
AstNode *body = ast_expect(pc, body_parser);
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 == NodeTypeIfOptional);
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;
}
AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ZigType *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);
}
// 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));
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;
if (buf_len(&members.doc_comments) != 0) {
node->data.container_decl.doc_comments = members.doc_comments;
}
return node;
}
static Token *ast_parse_doc_comments(ParseContext *pc, Buf *buf) {
Token *first_doc_token = nullptr;
Token *doc_token = nullptr;
while ((doc_token = eat_token_if(pc, TokenIdDocComment))) {
if (first_doc_token == nullptr) {
first_doc_token = doc_token;
}
if (buf->list.length == 0) {
buf_resize(buf, 0);
}
// chops off '///' but leaves '\n'
buf_append_mem(buf, buf_ptr(pc->buf) + doc_token->start_pos + 3,
doc_token->end_pos - doc_token->start_pos - 3);
}
return first_doc_token;
}
static void ast_parse_container_doc_comments(ParseContext *pc, Buf *buf) {
if (buf_len(buf) != 0 && peek_token(pc)->id == TokenIdContainerDocComment) {
buf_append_char(buf, '\n');
}
Token *doc_token = nullptr;
while ((doc_token = eat_token_if(pc, TokenIdContainerDocComment))) {
if (buf->list.length == 0) {
buf_resize(buf, 0);
}
// chops off '//!' but leaves '\n'
buf_append_mem(buf, buf_ptr(pc->buf) + doc_token->start_pos + 3,
doc_token->end_pos - doc_token->start_pos - 3);
}
}
// ContainerMembers
// <- TestDecl ContainerMembers
// / TopLevelComptime ContainerMembers
// / KEYWORD_pub? TopLevelDecl ContainerMembers
// / KEYWORD_comptime? ContainerField COMMA ContainerMembers
// / KEYWORD_comptime? ContainerField
// /
static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
AstNodeContainerDecl res = {};
Buf tld_doc_comment_buf = BUF_INIT;
buf_resize(&tld_doc_comment_buf, 0);
for (;;) {
ast_parse_container_doc_comments(pc, &tld_doc_comment_buf);
AstNode *test_decl = ast_parse_test_decl(pc);
if (test_decl != nullptr) {
res.decls.append(test_decl);
continue;
}
AstNode *top_level_comptime = ast_parse_top_level_comptime(pc);
if (top_level_comptime != nullptr) {
res.decls.append(top_level_comptime);
continue;
}
Buf doc_comment_buf = BUF_INIT;
ast_parse_doc_comments(pc, &doc_comment_buf);
Token *visib_token = eat_token_if(pc, TokenIdKeywordPub);
VisibMod visib_mod = visib_token != nullptr ? VisibModPub : VisibModPrivate;
AstNode *top_level_decl = ast_parse_top_level_decl(pc, visib_mod, &doc_comment_buf);
if (top_level_decl != nullptr) {
res.decls.append(top_level_decl);
continue;
}
if (visib_token != nullptr) {
ast_error(pc, peek_token(pc), "expected function or variable declaration after pub");
}
Token *comptime_token = eat_token_if(pc, TokenIdKeywordCompTime);
AstNode *container_field = ast_parse_container_field(pc);
if (container_field != nullptr) {
assert(container_field->type == NodeTypeStructField);
container_field->data.struct_field.doc_comments = doc_comment_buf;
container_field->data.struct_field.comptime_token = comptime_token;
res.fields.append(container_field);
if (eat_token_if(pc, TokenIdComma) != nullptr) {
continue;
} else {
break;
}
}
break;
}
res.doc_comments = tld_doc_comment_buf;
return res;
}
// TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block
static AstNode *ast_parse_test_decl(ParseContext *pc) {
Token *test = eat_token_if(pc, TokenIdKeywordTest);
if (test == nullptr)
return nullptr;
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;
// 1 token lookahead because it could be a comptime struct field
Token *lbrace = peek_token(pc);
if (lbrace->id != TokenIdLBrace) {
put_back_token(pc);
return nullptr;
}
AstNode *block = ast_expect(pc, ast_parse_block_expr);
AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime);
res->data.comptime_expr.expr = block;
return res;
}
// TopLevelDecl
// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)
// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
// / KEYWORD_use Expr SEMICOLON
static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, Buf *doc_comments) {
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)
first = eat_token_if(pc, TokenIdKeywordNoInline);
if (first != nullptr) {
Token *lib_name = nullptr;
if (first->id == TokenIdKeywordExtern)
lib_name = eat_token_if(pc, TokenIdStringLiteral);
if (first->id != TokenIdKeywordInline && first->id != TokenIdKeywordNoInline) {
Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
AstNode *var_decl = ast_parse_var_decl(pc);
if (var_decl != nullptr) {
assert(var_decl->type == NodeTypeVariableDeclaration);
var_decl->line = first->start_line;
var_decl->column = first->start_column;
var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw;
var_decl->data.variable_declaration.visib_mod = visib_mod;
var_decl->data.variable_declaration.doc_comments = *doc_comments;
var_decl->data.variable_declaration.is_extern = 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;
}
if (thread_local_kw != nullptr)
put_back_token(pc);
}
AstNode *fn_proto = ast_parse_fn_proto(pc);
if (fn_proto != nullptr) {
AstNode *body = ast_parse_block(pc);
if (body == nullptr)
expect_token(pc, TokenIdSemicolon);
assert(fn_proto->type == NodeTypeFnProto);
fn_proto->line = first->start_line;
fn_proto->column = first->start_column;
fn_proto->data.fn_proto.visib_mod = visib_mod;
fn_proto->data.fn_proto.doc_comments = *doc_comments;
// ast_parse_fn_cc may set it
if (!fn_proto->data.fn_proto.is_extern)
fn_proto->data.fn_proto.is_extern = first->id == TokenIdKeywordExtern;
fn_proto->data.fn_proto.is_export = first->id == TokenIdKeywordExport;
switch (first->id) {
case TokenIdKeywordInline:
fn_proto->data.fn_proto.fn_inline = FnInlineAlways;
break;
case TokenIdKeywordNoInline:
fn_proto->data.fn_proto.fn_inline = FnInlineNever;
break;
default:
fn_proto->data.fn_proto.fn_inline = FnInlineAuto;
break;
}
fn_proto->data.fn_proto.lib_name = token_buf(lib_name);
AstNode *res = fn_proto;
if (body != nullptr) {
res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto);
res->data.fn_def.fn_proto = fn_proto;
res->data.fn_def.body = body;
fn_proto->data.fn_proto.fn_def_node = res;
}
return res;
}
ast_invalid_token_error(pc, peek_token(pc));
}
Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
AstNode *var_decl = ast_parse_var_decl(pc);
if (var_decl != nullptr) {
assert(var_decl->type == NodeTypeVariableDeclaration);
var_decl->data.variable_declaration.visib_mod = visib_mod;
var_decl->data.variable_declaration.doc_comments = *doc_comments;
var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw;
return var_decl;
}
if (thread_local_kw != nullptr)
put_back_token(pc);
AstNode *fn_proto = ast_parse_fn_proto(pc);
if (fn_proto != nullptr) {
AstNode *body = ast_parse_block(pc);
if (body == nullptr)
expect_token(pc, TokenIdSemicolon);
assert(fn_proto->type == NodeTypeFnProto);
fn_proto->data.fn_proto.visib_mod = visib_mod;
fn_proto->data.fn_proto.doc_comments = *doc_comments;
AstNode *res = fn_proto;
if (body != nullptr) {
res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto);
res->data.fn_def.fn_proto = fn_proto;
res->data.fn_def.body = body;
fn_proto->data.fn_proto.fn_def_node = res;
}
return res;
}
Token *usingnamespace = eat_token_if(pc, TokenIdKeywordUsingNamespace);
if (usingnamespace != nullptr) {
AstNode *expr = ast_expect(pc, ast_parse_expr);
expect_token(pc, TokenIdSemicolon);
AstNode *res = ast_create_node(pc, NodeTypeUsingNamespace, usingnamespace);
res->data.using_namespace.visib_mod = visib_mod;
res->data.using_namespace.expr = expr;
return res;
}
return nullptr;
}
// FnProto <- FnCC? KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? 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.is_extern) {
fn = eat_token_if(pc, TokenIdKeywordFn);
if (fn == nullptr) {
put_back_token(pc);
return nullptr;
}
} else {
fn = expect_token(pc, TokenIdKeywordFn);
}
} else {
fn_cc = {};
fn = eat_token_if(pc, TokenIdKeywordFn);
if (fn == nullptr)
return nullptr;
}
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_link_section(pc);
AstNode *callconv_expr = ast_parse_callconv(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.callconv_expr = callconv_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;
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? LinkSection? (EQUAL Expr)? SEMICOLON
static AstNode *ast_parse_var_decl(ParseContext *pc) {
Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst);
if (mut_kw == nullptr)
mut_kw = eat_token_if(pc, TokenIdKeywordVar);
if (mut_kw == nullptr)
return nullptr;
Token *identifier = expect_token(pc, TokenIdSymbol);
AstNode *type_expr = nullptr;
if (eat_token_if(pc, TokenIdColon) != nullptr)
type_expr = ast_expect(pc, ast_parse_type_expr);
AstNode *align_expr = ast_parse_byte_align(pc);
AstNode *section_expr = ast_parse_link_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, mut_kw);
res->data.variable_declaration.is_const = mut_kw->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 ByteAlign?)? (EQUAL Expr)?
static AstNode *ast_parse_container_field(ParseContext *pc) {
Token *identifier = eat_token_if(pc, TokenIdSymbol);
if (identifier == nullptr)
return nullptr;
AstNode *type_expr = nullptr;
if (eat_token_if(pc, TokenIdColon) != nullptr) {
Token *var_tok = eat_token_if(pc, TokenIdKeywordVar);
if (var_tok != nullptr) {
type_expr = ast_create_node(pc, NodeTypeVarFieldType, var_tok);
} else {
type_expr = ast_expect(pc, ast_parse_type_expr);
}
}
AstNode *align_expr = ast_parse_byte_align(pc);
AstNode *expr = nullptr;
if (eat_token_if(pc, TokenIdEq) != 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;
res->data.struct_field.align_expr = align_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;
AstNode *assign = ast_parse_assign_expr(pc);
if (assign != nullptr) {
expect_token(pc, TokenIdSemicolon);
return assign;
}
return nullptr;
}
// IfStatement
// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
static AstNode *ast_parse_if_statement(ParseContext *pc) {
AstNode *res = ast_parse_if_prefix(pc);
if (res == nullptr)
return nullptr;
AstNode *body = ast_parse_block_expr(pc);
bool requires_semi = false;
if (body == nullptr) {
requires_semi = true;
body = ast_parse_assign_expr(pc);
}
if (body == nullptr) {
Token *tok = eat_token(pc);
ast_error(pc, tok, "expected if body, found '%s'", token_name(tok->id));
}
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 == NodeTypeIfOptional);
if (err_payload != nullptr) {
AstNodeTestExpr old = res->data.test_expr;
res->type = NodeTypeIfErrorExpr;
res->data.if_err_expr.target_node = old.target_node;
res->data.if_err_expr.var_is_ptr = old.var_is_ptr;
res->data.if_err_expr.var_symbol = old.var_symbol;
res->data.if_err_expr.then_node = body;
res->data.if_err_expr.err_symbol = token_buf(err_payload);
res->data.if_err_expr.else_node = else_body;
return res;
}
if (res->data.test_expr.var_symbol != nullptr) {
res->data.test_expr.then_node = body;
res->data.test_expr.else_node = else_body;
return res;
}
AstNodeTestExpr old = res->data.test_expr;
res->type = NodeTypeIfBoolExpr;
res->data.if_bool_expr.condition = old.target_node;
res->data.if_bool_expr.then_block = body;
res->data.if_bool_expr.else_node = else_body;
return res;
}
// LabeledStatement <- BlockLabel? (Block / LoopStatement)
static AstNode *ast_parse_labeled_statement(ParseContext *pc) {
Token *label = ast_parse_block_label(pc);
AstNode *block = ast_parse_block(pc);
if (block != nullptr) {
assert(block->type == NodeTypeBlock);
block->data.block.name = token_buf(label);
return block;
}
AstNode *loop = ast_parse_loop_statement(pc);
if (loop != nullptr) {
switch (loop->type) {
case NodeTypeForExpr:
loop->data.for_expr.name = token_buf(label);
break;
case NodeTypeWhileExpr:
loop->data.while_expr.name = token_buf(label);
break;
default:
zig_unreachable();
}
return loop;
}
if (label != nullptr)
ast_invalid_token_error(pc, peek_token(pc));
return nullptr;
}
// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)
static AstNode *ast_parse_loop_statement(ParseContext *pc) {
Token *inline_token = eat_token_if(pc, TokenIdKeywordInline);
AstNode *for_statement = ast_parse_for_statement(pc);
if (for_statement != nullptr) {
assert(for_statement->type == NodeTypeForExpr);
for_statement->data.for_expr.is_inline = inline_token != nullptr;
return for_statement;
}
AstNode *while_statement = ast_parse_while_statement(pc);
if (while_statement != nullptr) {
assert(while_statement->type == NodeTypeWhileExpr);
while_statement->data.while_expr.is_inline = inline_token != nullptr;
return while_statement;
}
if (inline_token != nullptr)
ast_invalid_token_error(pc, peek_token(pc));
return nullptr;
}
// ForStatement
// <- ForPrefix BlockExpr ( KEYWORD_else Statement )?
// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement )
static AstNode *ast_parse_for_statement(ParseContext *pc) {
AstNode *res = ast_parse_for_prefix(pc);
if (res == nullptr)
return nullptr;
AstNode *body = ast_parse_block_expr(pc);
bool requires_semi = false;
if (body == nullptr) {
requires_semi = true;
body = ast_parse_assign_expr(pc);
}
if (body == nullptr) {
Token *tok = eat_token(pc);
ast_error(pc, tok, "expected loop body, found '%s'", token_name(tok->id));
}
AstNode *else_body = nullptr;
if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) {
else_body = ast_expect(pc, ast_parse_statement);
}
if (requires_semi && else_body == nullptr)
expect_token(pc, TokenIdSemicolon);
assert(res->type == NodeTypeForExpr);
res->data.for_expr.body = body;
res->data.for_expr.else_node = else_body;
return res;
}
// WhileStatement
// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
static AstNode *ast_parse_while_statement(ParseContext *pc) {
AstNode *res = ast_parse_while_prefix(pc);
if (res == nullptr)
return nullptr;
AstNode *body = ast_parse_block_expr(pc);
bool requires_semi = false;
if (body == nullptr) {
requires_semi = true;
body = ast_parse_assign_expr(pc);
}
if (body == nullptr) {
Token *tok = eat_token(pc);
ast_error(pc, tok, "expected loop body, found '%s'", token_name(tok->id));
}
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 == 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;
}
// BlockExprStatement
// <- BlockExpr
// / AssignExpr SEMICOLON
static AstNode *ast_parse_block_expr_statement(ParseContext *pc) {
AstNode *block = ast_parse_block_expr(pc);
if (block != nullptr)
return block;
AstNode *assign_expr = ast_parse_assign_expr(pc);
if (assign_expr != nullptr) {
expect_token(pc, TokenIdSemicolon);
return assign_expr;
}
return nullptr;
}
// BlockExpr <- BlockLabel? Block
static AstNode *ast_parse_block_expr(ParseContext *pc) {
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;
}
return ast_parse_block(pc);
}
// AssignExpr <- Expr (AssignOp Expr)?
static AstNode *ast_parse_assign_expr(ParseContext *pc) {
return ast_parse_bin_op_expr(pc, BinOpChainOnce, ast_parse_assign_op, ast_parse_expr);
}
// Expr <- KEYWORD_try* BoolOrExpr
static AstNode *ast_parse_expr(ParseContext *pc) {
return ast_parse_prefix_op_expr(
pc,
[](ParseContext *context) {
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;
}
return (AstNode*)nullptr;
},
ast_parse_bool_or_expr
);
}
// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)*
static AstNode *ast_parse_bool_or_expr(ParseContext *pc) {
return ast_parse_bin_op_expr(
pc,
BinOpChainInf,
ast_parse_bin_op_simple<TokenIdKeywordOr, BinOpTypeBoolOr>,
ast_parse_bool_and_expr
);
}
// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)*
static AstNode *ast_parse_bool_and_expr(ParseContext *pc) {
return ast_parse_bin_op_expr(
pc,
BinOpChainInf,
ast_parse_bin_op_simple<TokenIdKeywordAnd, BinOpTypeBoolAnd>,
ast_parse_compare_expr
);
}
// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)?
static AstNode *ast_parse_compare_expr(ParseContext *pc) {
return ast_parse_bin_op_expr(pc, BinOpChainOnce, ast_parse_compare_op, ast_parse_bitwise_expr);
}
// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)*
static AstNode *ast_parse_bitwise_expr(ParseContext *pc) {
return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_bitwise_op, ast_parse_bit_shit_expr);
}
// 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);
}
// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)*
static AstNode *ast_parse_addition_expr(ParseContext *pc) {
return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_addition_op, ast_parse_multiply_expr);
}
// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)*
static AstNode *ast_parse_multiply_expr(ParseContext *pc) {
return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_multiply_op, ast_parse_prefix_expr);
}
// PrefixExpr <- PrefixOp* PrimaryExpr
static AstNode *ast_parse_prefix_expr(ParseContext *pc) {
return ast_parse_prefix_op_expr(
pc,
ast_parse_prefix_op,
ast_parse_primary_expr
);
}
// PrimaryExpr
// <- AsmExpr
// / IfExpr
// / KEYWORD_break BreakLabel? Expr?
// / KEYWORD_comptime Expr
// / KEYWORD_continue BreakLabel?
// / KEYWORD_resume Expr
// / KEYWORD_return Expr?
// / BlockLabel? LoopExpr
// / Block
// / CurlySuffixExpr
static AstNode *ast_parse_primary_expr(ParseContext *pc) {
AstNode *asm_expr = ast_parse_asm_expr(pc);
if (asm_expr != nullptr)
return asm_expr;
AstNode *if_expr = ast_parse_if_expr(pc);
if (if_expr != nullptr)
return if_expr;
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);
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;
}
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;
}
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 *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;
}
Token *label = ast_parse_block_label(pc);
AstNode *loop = ast_parse_loop_expr(pc);
if (loop != nullptr) {
switch (loop->type) {
case NodeTypeForExpr:
loop->data.for_expr.name = token_buf(label);
break;
case NodeTypeWhileExpr:
loop->data.while_expr.name = token_buf(label);
break;
default:
zig_unreachable();
}
return loop;
} else if (label != nullptr) {
// Restore the tokens that we eaten by ast_parse_block_label.
put_back_token(pc);
put_back_token(pc);
}
AstNode *block = ast_parse_block(pc);
if (block != nullptr)
return block;
AstNode *curly_suffix = ast_parse_curly_suffix_expr(pc);
if (curly_suffix != nullptr)
return curly_suffix;
return nullptr;
}
// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)?
static AstNode *ast_parse_if_expr(ParseContext *pc) {
return ast_parse_if_expr_helper(pc, ast_parse_expr);
}
// Block <- LBRACE Statement* RBRACE
static AstNode *ast_parse_block(ParseContext *pc) {
Token *lbrace = eat_token_if(pc, TokenIdLBrace);
if (lbrace == nullptr)
return nullptr;
ZigList<AstNode *> statements = {};
AstNode *statement;
while ((statement = ast_parse_statement(pc)) != nullptr)
statements.append(statement);
expect_token(pc, TokenIdRBrace);
AstNode *res = ast_create_node(pc, NodeTypeBlock, lbrace);
res->data.block.statements = statements;
return res;
}
// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr)
static AstNode *ast_parse_loop_expr(ParseContext *pc) {
return ast_parse_loop_expr_helper(
pc,
ast_parse_for_expr,
ast_parse_while_expr
);
}
// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?
static AstNode *ast_parse_for_expr(ParseContext *pc) {
return ast_parse_for_expr_helper(pc, ast_parse_expr);
}
// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
static AstNode *ast_parse_while_expr(ParseContext *pc) {
return ast_parse_while_expr_helper(pc, ast_parse_expr);
}
// CurlySuffixExpr <- TypeExpr InitList?
static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc) {
AstNode *type_expr = ast_parse_type_expr(pc);
if (type_expr == nullptr)
return nullptr;
AstNode *res = ast_parse_init_list(pc);
if (res == nullptr)
return type_expr;
assert(res->type == NodeTypeContainerInitExpr);
res->data.container_init_expr.type = type_expr;
return res;
}
// InitList
// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
// / LBRACE RBRACE
static AstNode *ast_parse_init_list(ParseContext *pc) {
Token *lbrace = eat_token_if(pc, TokenIdLBrace);
if (lbrace == nullptr)
return nullptr;
AstNode *first = ast_parse_field_init(pc);
if (first != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypeContainerInitExpr, lbrace);
res->data.container_init_expr.kind = ContainerInitKindStruct;
res->data.container_init_expr.entries.append(first);
while (eat_token_if(pc, TokenIdComma) != nullptr) {
AstNode *field_init = ast_parse_field_init(pc);
if (field_init == nullptr)
break;
res->data.container_init_expr.entries.append(field_init);
}
expect_token(pc, TokenIdRBrace);
return res;
}
AstNode *res = ast_create_node(pc, NodeTypeContainerInitExpr, lbrace);
res->data.container_init_expr.kind = ContainerInitKindArray;
first = ast_parse_expr(pc);
if (first != nullptr) {
res->data.container_init_expr.entries.append(first);
while (eat_token_if(pc, TokenIdComma) != nullptr) {
AstNode *expr = ast_parse_expr(pc);
if (expr == nullptr)
break;
res->data.container_init_expr.entries.append(expr);
}
expect_token(pc, TokenIdRBrace);
return res;
}
expect_token(pc, TokenIdRBrace);
return res;
}
// TypeExpr <- PrefixTypeOp* ErrorUnionExpr
static AstNode *ast_parse_type_expr(ParseContext *pc) {
return ast_parse_prefix_op_expr(
pc,
ast_parse_prefix_type_op,
ast_parse_error_union_expr
);
}
// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)?
static AstNode *ast_parse_error_union_expr(ParseContext *pc) {
AstNode *res = ast_parse_suffix_expr(pc);
if (res == nullptr)
return nullptr;
AstNode *op = ast_parse_bin_op_simple<TokenIdBang, BinOpTypeErrorUnion>(pc);
if (op == nullptr)
return res;
AstNode *right = ast_expect(pc, ast_parse_type_expr);
assert(op->type == NodeTypeBinOpExpr);
op->data.bin_op_expr.op1 = res;
op->data.bin_op_expr.op2 = right;
return op;
}
// SuffixExpr
// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
// / KEYWORD_noasync PrimaryTypeExpr SuffixOp* FnCallArguments
// / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
static AstNode *ast_parse_suffix_expr(ParseContext *pc) {
Token *async_token = eat_token(pc);
bool is_async = async_token->id == TokenIdKeywordAsync;
if (is_async || async_token->id == TokenIdKeywordNoAsync) {
if (is_async && 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...
put_back_token(pc);
put_back_token(pc);
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;
switch (suffix->type) {
case NodeTypeSliceExpr:
suffix->data.slice_expr.array_ref_expr = child;
break;
case NodeTypeArrayAccessExpr:
suffix->data.array_access_expr.array_ref_expr = child;
break;
case NodeTypeFieldAccessExpr:
suffix->data.field_access_expr.struct_expr = child;
break;
case NodeTypeUnwrapOptional:
suffix->data.unwrap_optional.expr = child;
break;
case NodeTypePtrDeref:
suffix->data.ptr_deref_expr.target = child;
break;
default:
zig_unreachable();
}
child = suffix;
}
// TODO: Both *_async_prefix and *_fn_call_arguments returns an
// AstNode *. All we really want here is the arguments of
// the call we parse. We therefor "leak" the node for now.
// Wait till we get async rework to fix this.
AstNode *args = ast_parse_fn_call_arguments(pc);
if (args == nullptr)
ast_invalid_token_error(pc, peek_token(pc));
assert(args->type == NodeTypeFnCallExpr);
AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, async_token);
res->data.fn_call_expr.modifier = is_async ? CallModifierAsync : CallModifierNoAsync;
res->data.fn_call_expr.seen = false;
res->data.fn_call_expr.fn_ref_expr = child;
res->data.fn_call_expr.params = args->data.fn_call_expr.params;
return res;
}
put_back_token(pc);
AstNode *res = ast_parse_primary_type_expr(pc);
if (res == nullptr)
return nullptr;
while (true) {
AstNode *suffix = ast_parse_suffix_op(pc);
if (suffix != nullptr) {
switch (suffix->type) {
case NodeTypeSliceExpr:
suffix->data.slice_expr.array_ref_expr = res;
break;
case NodeTypeArrayAccessExpr:
suffix->data.array_access_expr.array_ref_expr = res;
break;
case NodeTypeFieldAccessExpr:
suffix->data.field_access_expr.struct_expr = res;
break;
case NodeTypeUnwrapOptional:
suffix->data.unwrap_optional.expr = res;
break;
case NodeTypePtrDeref:
suffix->data.ptr_deref_expr.target = res;
break;
default:
zig_unreachable();
}
res = suffix;
continue;
}
AstNode * call = ast_parse_fn_call_arguments(pc);
if (call != nullptr) {
assert(call->type == NodeTypeFnCallExpr);
call->data.fn_call_expr.fn_ref_expr = res;
res = call;
continue;
}
break;
}
return res;
}
// PrimaryTypeExpr
// <- BUILTINIDENTIFIER FnCallArguments
// / CHAR_LITERAL
// / ContainerDecl
// / DOT IDENTIFIER
// / ErrorSetDecl
// / FLOAT
// / FnProto
// / GroupedExpr
// / LabeledTypeExpr
// / IDENTIFIER
// / IfTypeExpr
// / INTEGER
// / KEYWORD_comptime TypeExpr
// / KEYWORD_error DOT IDENTIFIER
// / KEYWORD_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");
}
AstNode *res = ast_expect(pc, ast_parse_fn_call_arguments);
AstNode *name_sym = ast_create_node(pc, NodeTypeSymbol, token);
name_sym->data.symbol_expr.symbol = name;
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.modifier = CallModifierBuiltin;
return res;
}
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;
}
AstNode *container_decl = ast_parse_container_decl(pc);
if (container_decl != nullptr)
return container_decl;
AstNode *anon_lit = ast_parse_anon_lit(pc);
if (anon_lit != nullptr)
return anon_lit;
AstNode *error_set_decl = ast_parse_error_set_decl(pc);
if (error_set_decl != nullptr)
return error_set_decl;
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;
}
AstNode *fn_proto = ast_parse_fn_proto(pc);
if (fn_proto != nullptr)
return fn_proto;
AstNode *grouped_expr = ast_parse_grouped_expr(pc);
if (grouped_expr != nullptr)
return grouped_expr;
AstNode *labeled_type_expr = ast_parse_labeled_type_expr(pc);
if (labeled_type_expr != nullptr)
return labeled_type_expr;
Token *identifier = eat_token_if(pc, TokenIdSymbol);
if (identifier != nullptr)
return token_symbol(pc, identifier);
AstNode *if_type_expr = ast_parse_if_type_expr(pc);
if (if_type_expr != nullptr)
return if_type_expr;
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;
}
Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime);
if (comptime != nullptr) {
AstNode *expr = ast_expect(pc, ast_parse_type_expr);
AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime);
res->data.comptime_expr.expr = expr;
return res;
}
Token *error = eat_token_if(pc, TokenIdKeywordError);
if (error != nullptr) {
Token *dot = expect_token(pc, TokenIdDot);
Token *name = expect_token(pc, TokenIdSymbol);
AstNode *left = ast_create_node(pc, NodeTypeErrorType, error);
AstNode *res = ast_create_node(pc, NodeTypeFieldAccessExpr, dot);
res->data.field_access_expr.struct_expr = left;
res->data.field_access_expr.field_name = token_buf(name);
return res;
}
Token *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;
}
Token *null = eat_token_if(pc, TokenIdKeywordNull);
if (null != nullptr)
return ast_create_node(pc, NodeTypeNullLiteral, null);
Token *anyframe = eat_token_if(pc, TokenIdKeywordAnyFrame);
if (anyframe != nullptr)
return ast_create_node(pc, NodeTypeAnyFrameType, anyframe);
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;
}
Token *undefined = eat_token_if(pc, TokenIdKeywordUndefined);
if (undefined != nullptr)
return ast_create_node(pc, NodeTypeUndefinedLiteral, undefined);
Token *unreachable = eat_token_if(pc, TokenIdKeywordUnreachable);
if (unreachable != nullptr)
return ast_create_node(pc, NodeTypeUnreachable, unreachable);
Token *string_lit = eat_token_if(pc, TokenIdStringLiteral);
if (string_lit == nullptr)
string_lit = eat_token_if(pc, TokenIdMultilineStringLiteral);
if (string_lit != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypeStringLiteral, string_lit);
res->data.string_literal.buf = token_buf(string_lit);
return res;
}
AstNode *switch_expr = ast_parse_switch_expr(pc);
if (switch_expr != nullptr)
return switch_expr;
return nullptr;
}
// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto
static AstNode *ast_parse_container_decl(ParseContext *pc) {
Token *layout_token = eat_token_if(pc, TokenIdKeywordExtern);
if (layout_token == nullptr)
layout_token = eat_token_if(pc, TokenIdKeywordPacked);
AstNode *res = ast_parse_container_decl_auto(pc);
if (res == nullptr) {
if (layout_token != nullptr)
put_back_token(pc);
return nullptr;
}
assert(res->type == NodeTypeContainerDecl);
if (layout_token != nullptr) {
res->line = layout_token->start_line;
res->column = layout_token->start_column;
res->data.container_decl.layout = layout_token->id == TokenIdKeywordExtern
? ContainerLayoutExtern
: ContainerLayoutPacked;
}
return res;
}
// 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;
}
ZigList<AstNode *> decls = ast_parse_list<AstNode>(pc, TokenIdComma, [](ParseContext *context) {
Buf doc_comment_buf = BUF_INIT;
Token *doc_token = ast_parse_doc_comments(context, &doc_comment_buf);
Token *ident = eat_token_if(context, TokenIdSymbol);
if (ident == nullptr)
return (AstNode*)nullptr;
AstNode *symbol_node = token_symbol(context, ident);
if (doc_token == nullptr)
return symbol_node;
AstNode *field_node = ast_create_node(context, NodeTypeErrorSetField, doc_token);
field_node->data.err_set_field.field_name = symbol_node;
field_node->data.err_set_field.doc_comments = doc_comment_buf;
return field_node;
});
expect_token(pc, TokenIdRBrace);
AstNode *res = ast_create_node(pc, NodeTypeErrorSetDecl, first);
res->data.err_set_decl.decls = decls;
return res;
}
// GroupedExpr <- LPAREN Expr RPAREN
static AstNode *ast_parse_grouped_expr(ParseContext *pc) {
Token *lparen = eat_token_if(pc, TokenIdLParen);
if (lparen == nullptr)
return nullptr;
AstNode *expr = ast_expect(pc, ast_parse_expr);
expect_token(pc, TokenIdRParen);
AstNode *res = ast_create_node(pc, NodeTypeGroupedExpr, lparen);
res->data.grouped_expr = expr;
return res;
}
// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
static AstNode *ast_parse_if_type_expr(ParseContext *pc) {
return ast_parse_if_expr_helper(pc, ast_parse_type_expr);
}
// LabeledTypeExpr
// <- BlockLabel Block
// / BlockLabel? LoopTypeExpr
static AstNode *ast_parse_labeled_type_expr(ParseContext *pc) {
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 *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;
}
if (label != nullptr) {
put_back_token(pc);
put_back_token(pc);
}
return nullptr;
}
// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr)
static AstNode *ast_parse_loop_type_expr(ParseContext *pc) {
return ast_parse_loop_expr_helper(
pc,
ast_parse_for_type_expr,
ast_parse_while_type_expr
);
}
// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)?
static AstNode *ast_parse_for_type_expr(ParseContext *pc) {
return ast_parse_for_expr_helper(pc, ast_parse_type_expr);
}
// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
static AstNode *ast_parse_while_type_expr(ParseContext *pc) {
return ast_parse_while_expr_helper(pc, ast_parse_type_expr);
}
// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
static AstNode *ast_parse_switch_expr(ParseContext *pc) {
Token *switch_token = eat_token_if(pc, TokenIdKeywordSwitch);
if (switch_token == nullptr)
return nullptr;
expect_token(pc, TokenIdLParen);
AstNode *expr = ast_expect(pc, ast_parse_expr);
expect_token(pc, TokenIdRParen);
expect_token(pc, TokenIdLBrace);
ZigList<AstNode *> prongs = ast_parse_list(pc, TokenIdComma, ast_parse_switch_prong);
expect_token(pc, TokenIdRBrace);
AstNode *res = ast_create_node(pc, NodeTypeSwitchExpr, switch_token);
res->data.switch_expr.expr = expr;
res->data.switch_expr.prongs = prongs;
return res;
}
// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN STRINGLITERAL AsmOutput? RPAREN
static AstNode *ast_parse_asm_expr(ParseContext *pc) {
Token *asm_token = eat_token_if(pc, TokenIdKeywordAsm);
if (asm_token == nullptr)
return nullptr;
Token *volatile_token = eat_token_if(pc, TokenIdKeywordVolatile);
expect_token(pc, TokenIdLParen);
AstNode *asm_template = ast_expect(pc, ast_parse_expr);
AstNode *res = ast_parse_asm_output(pc);
if (res == nullptr)
res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr);
expect_token(pc, TokenIdRParen);
res->line = asm_token->start_line;
res->column = asm_token->start_column;
res->data.asm_expr.volatile_token = volatile_token;
res->data.asm_expr.asm_template = asm_template;
return res;
}
static AstNode *ast_parse_anon_lit(ParseContext *pc) {
Token *period = eat_token_if(pc, TokenIdDot);
if (period == nullptr)
return nullptr;
// anon enum literal
Token *identifier = eat_token_if(pc, TokenIdSymbol);
if (identifier != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypeEnumLiteral, period);
res->data.enum_literal.period = period;
res->data.enum_literal.identifier = identifier;
return res;
}
// anon container literal
AstNode *res = ast_parse_init_list(pc);
if (res != nullptr)
return res;
put_back_token(pc);
return nullptr;
}
// AsmOutput <- COLON AsmOutputList AsmInput?
static AstNode *ast_parse_asm_output(ParseContext *pc) {
if (eat_token_if(pc, TokenIdColon) == nullptr)
return nullptr;
ZigList<AsmOutput *> output_list = ast_parse_list(pc, TokenIdComma, ast_parse_asm_output_item);
AstNode *res = ast_parse_asm_input(pc);
if (res == nullptr)
res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr);
res->data.asm_expr.output_list = output_list;
return res;
}
// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
static AsmOutput *ast_parse_asm_output_item(ParseContext *pc) {
if (eat_token_if(pc, TokenIdLBracket) == nullptr)
return nullptr;
Token *sym_name = expect_token(pc, TokenIdSymbol);
expect_token(pc, TokenIdRBracket);
Token *str = eat_token_if(pc, TokenIdMultilineStringLiteral);
if (str == nullptr)
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);
}
expect_token(pc, TokenIdRParen);
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;
}
// AsmInput <- COLON AsmInputList AsmClobbers?
static AstNode *ast_parse_asm_input(ParseContext *pc) {
if (eat_token_if(pc, TokenIdColon) == nullptr)
return nullptr;
ZigList<AsmInput *> input_list = ast_parse_list(pc, TokenIdComma, ast_parse_asm_input_item);
AstNode *res = ast_parse_asm_clobbers(pc);
if (res == nullptr)
res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr);
res->data.asm_expr.input_list = input_list;
return res;
}
// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN
static AsmInput *ast_parse_asm_input_item(ParseContext *pc) {
if (eat_token_if(pc, TokenIdLBracket) == nullptr)
return nullptr;
Token *sym_name = expect_token(pc, TokenIdSymbol);
expect_token(pc, TokenIdRBracket);
Token *constraint = eat_token_if(pc, TokenIdMultilineStringLiteral);
if (constraint == nullptr)
constraint = expect_token(pc, TokenIdStringLiteral);
expect_token(pc, TokenIdLParen);
AstNode *expr = ast_expect(pc, ast_parse_expr);
expect_token(pc, TokenIdRParen);
AsmInput *res = allocate<AsmInput>(1);
res->asm_symbolic_name = token_buf(sym_name);
res->constraint = token_buf(constraint);
res->expr = expr;
return res;
}
// AsmClobbers <- COLON StringList
static AstNode *ast_parse_asm_clobbers(ParseContext *pc) {
if (eat_token_if(pc, TokenIdColon) == nullptr)
return nullptr;
ZigList<Buf *> clobber_list = ast_parse_list<Buf>(pc, TokenIdComma, [](ParseContext *context) {
Token *str = eat_token_if(context, TokenIdStringLiteral);
if (str == nullptr)
str = eat_token_if(context, TokenIdMultilineStringLiteral);
if (str != nullptr)
return token_buf(str);
return (Buf*)nullptr;
});
AstNode *res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr);
res->data.asm_expr.clobber_list = clobber_list;
return res;
}
// BreakLabel <- COLON IDENTIFIER
static Token *ast_parse_break_label(ParseContext *pc) {
if (eat_token_if(pc, TokenIdColon) == nullptr)
return nullptr;
return expect_token(pc, TokenIdSymbol);
}
// BlockLabel <- IDENTIFIER COLON
static Token *ast_parse_block_label(ParseContext *pc) {
Token *ident = eat_token_if(pc, TokenIdSymbol);
if (ident == nullptr)
return nullptr;
// We do 2 token lookahead here, as we don't want to error when
// parsing identifiers.
if (eat_token_if(pc, TokenIdColon) == nullptr) {
put_back_token(pc);
return nullptr;
}
return ident;
}
// 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;
Token *name = eat_token_if(pc, TokenIdSymbol);
if (name == nullptr) {
// Because of anon literals ".{" is also valid.
put_back_token(pc);
return nullptr;
}
if (eat_token_if(pc, TokenIdEq) == nullptr) {
// Because ".Name" can also be intepreted as an enum literal, we should put back
// those two tokens again so that the parser can try to parse them as the enum
// literal later.
put_back_token(pc);
put_back_token(pc);
return nullptr;
}
AstNode *expr = ast_expect(pc, ast_parse_expr);
AstNode *res = ast_create_node(pc, NodeTypeStructValueField, first);
res->data.struct_val_field.name = token_buf(name);
res->data.struct_val_field.expr = expr;
return res;
}
// 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;
expect_token(pc, TokenIdLParen);
AstNode *expr = ast_expect(pc, ast_parse_assign_expr);
expect_token(pc, TokenIdRParen);
return expr;
}
// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN
static AstNode *ast_parse_link_section(ParseContext *pc) {
Token *first = eat_token_if(pc, TokenIdKeywordLinkSection);
if (first == nullptr)
return nullptr;
expect_token(pc, TokenIdLParen);
AstNode *res = ast_expect(pc, ast_parse_expr);
expect_token(pc, TokenIdRParen);
return res;
}
// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN
static AstNode *ast_parse_callconv(ParseContext *pc) {
Token *first = eat_token_if(pc, TokenIdKeywordCallconv);
if (first == nullptr)
return nullptr;
expect_token(pc, TokenIdLParen);
AstNode *res = ast_expect(pc, ast_parse_expr);
expect_token(pc, TokenIdRParen);
return res;
}
// FnCC
// <- KEYWORD_extern
// / KEYWORD_async
static Optional<AstNodeFnProto> ast_parse_fn_cc(ParseContext *pc) {
AstNodeFnProto res = {};
if (eat_token_if(pc, TokenIdKeywordAsync) != nullptr) {
res.is_async = true;
return Optional<AstNodeFnProto>::some(res);
}
if (eat_token_if(pc, TokenIdKeywordExtern) != nullptr) {
res.is_extern = true;
return Optional<AstNodeFnProto>::some(res);
}
return Optional<AstNodeFnProto>::none();
}
// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
static AstNode *ast_parse_param_decl(ParseContext *pc) {
Buf doc_comments = BUF_INIT;
ast_parse_doc_comments(pc, &doc_comments);
Token *first = eat_token_if(pc, TokenIdKeywordNoAlias);
if (first == nullptr)
first = eat_token_if(pc, TokenIdKeywordCompTime);
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;
}
}
AstNode *res;
if (first == nullptr) {
first = peek_token(pc);
res = ast_parse_param_type(pc);
} else {
res = ast_expect(pc, ast_parse_param_type);
}
if (res == nullptr)
return nullptr;
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.doc_comments = doc_comments;
res->data.param_decl.is_noalias = first->id == TokenIdKeywordNoAlias;
res->data.param_decl.is_comptime = first->id == TokenIdKeywordCompTime;
return res;
}
// 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 *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 *type_expr = ast_parse_type_expr(pc);
if (type_expr != nullptr) {
AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeParamDecl, type_expr);
res->data.param_decl.type = type_expr;
return res;
}
return nullptr;
}
// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload?
static AstNode *ast_parse_if_prefix(ParseContext *pc) {
Token *first = eat_token_if(pc, TokenIdKeywordIf);
if (first == nullptr)
return nullptr;
expect_token(pc, TokenIdLParen);
AstNode *condition = ast_expect(pc, ast_parse_expr);
expect_token(pc, TokenIdRParen);
Optional<PtrPayload> opt_payload = ast_parse_ptr_payload(pc);
PtrPayload payload;
AstNode *res = ast_create_node(pc, NodeTypeIfOptional, first);
res->data.test_expr.target_node = condition;
if (opt_payload.unwrap(&payload)) {
res->data.test_expr.var_symbol = token_buf(payload.payload);
res->data.test_expr.var_is_ptr = payload.asterisk != nullptr;
}
return res;
}
// 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;
expect_token(pc, TokenIdLParen);
AstNode *condition = ast_expect(pc, ast_parse_expr);
expect_token(pc, TokenIdRParen);
Optional<PtrPayload> opt_payload = ast_parse_ptr_payload(pc);
AstNode *continue_expr = ast_parse_while_continue_expr(pc);
PtrPayload payload;
AstNode *res = ast_create_node(pc, NodeTypeWhileExpr, while_token);
res->data.while_expr.condition = condition;
res->data.while_expr.continue_expr = continue_expr;
if (opt_payload.unwrap(&payload)) {
res->data.while_expr.var_symbol = token_buf(payload.payload);
res->data.while_expr.var_is_ptr = payload.asterisk != nullptr;
}
return res;
}
// 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;
expect_token(pc, TokenIdLParen);
AstNode *array_expr = ast_expect(pc, ast_parse_expr);
expect_token(pc, TokenIdRParen);
PtrIndexPayload payload;
if (!ast_parse_ptr_index_payload(pc).unwrap(&payload))
ast_invalid_token_error(pc, peek_token(pc));
AstNode *res = ast_create_node(pc, NodeTypeForExpr, for_token);
res->data.for_expr.array_expr = array_expr;
res->data.for_expr.elem_node = token_symbol(pc, payload.payload);
res->data.for_expr.elem_is_ptr = payload.asterisk != nullptr;
if (payload.index != nullptr)
res->data.for_expr.index_node = token_symbol(pc, payload.index);
return res;
}
// Payload <- PIPE IDENTIFIER PIPE
static Token *ast_parse_payload(ParseContext *pc) {
if (eat_token_if(pc, TokenIdBinOr) == nullptr)
return nullptr;
Token *res = expect_token(pc, TokenIdSymbol);
expect_token(pc, TokenIdBinOr);
return res;
}
// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE
static Optional<PtrPayload> ast_parse_ptr_payload(ParseContext *pc) {
if (eat_token_if(pc, TokenIdBinOr) == nullptr)
return Optional<PtrPayload>::none();
Token *asterisk = eat_token_if(pc, TokenIdStar);
Token *payload = expect_token(pc, TokenIdSymbol);
expect_token(pc, TokenIdBinOr);
PtrPayload res;
res.asterisk = asterisk;
res.payload = payload;
return Optional<PtrPayload>::some(res);
}
// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE
static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc) {
if (eat_token_if(pc, TokenIdBinOr) == nullptr)
return Optional<PtrIndexPayload>::none();
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);
PtrIndexPayload res;
res.asterisk = asterisk;
res.payload = payload;
res.index = index;
return Optional<PtrIndexPayload>::some(res);
}
// 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;
expect_token(pc, TokenIdFatArrow);
Optional<PtrPayload> opt_payload = ast_parse_ptr_payload(pc);
AstNode *expr = ast_expect(pc, ast_parse_assign_expr);
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;
}
return res;
}
// 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;
while (eat_token_if(pc, TokenIdComma) != nullptr) {
AstNode *item = ast_parse_switch_item(pc);
if (item == nullptr)
break;
res->data.switch_prong.items.append(item);
res->data.switch_prong.any_items_are_range |= item->type == NodeTypeSwitchRange;
}
return res;
}
Token *else_token = eat_token_if(pc, TokenIdKeywordElse);
if (else_token != nullptr)
return ast_create_node(pc, NodeTypeSwitchProng, else_token);
return nullptr;
}
// SwitchItem <- Expr (DOT3 Expr)?
static AstNode *ast_parse_switch_item(ParseContext *pc) {
AstNode *expr = ast_parse_expr(pc);
if (expr == nullptr)
return nullptr;
Token *dots = eat_token_if(pc, TokenIdEllipsis3);
if (dots != nullptr) {
AstNode *expr2 = ast_expect(pc, ast_parse_expr);
AstNode *res = ast_create_node(pc, NodeTypeSwitchRange, dots);
res->data.switch_range.start = expr;
res->data.switch_range.end = expr2;
return res;
}
return expr;
}
// AssignOp
// <- ASTERISKEQUAL
// / SLASHEQUAL
// / PERCENTEQUAL
// / PLUSEQUAL
// / MINUSEQUAL
// / LARROW2EQUAL
// / RARROW2EQUAL
// / AMPERSANDEQUAL
// / CARETEQUAL
// / PIPEEQUAL
// / ASTERISKPERCENTEQUAL
// / PLUSPERCENTEQUAL
// / MINUSPERCENTEQUAL
// / EQUAL
static AstNode *ast_parse_assign_op(ParseContext *pc) {
// In C, we have `T arr[N] = {[i] = T{}};` but it doesn't
// seem to work in C++...
BinOpType table[TokenIdCount] = {};
table[TokenIdBarBarEq] = BinOpTypeAssignMergeErrorSets;
table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd;
table[TokenIdBitOrEq] = BinOpTypeAssignBitOr;
table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft;
table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight;
table[TokenIdBitXorEq] = BinOpTypeAssignBitXor;
table[TokenIdDivEq] = BinOpTypeAssignDiv;
table[TokenIdEq] = BinOpTypeAssign;
table[TokenIdMinusEq] = BinOpTypeAssignMinus;
table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap;
table[TokenIdModEq] = BinOpTypeAssignMod;
table[TokenIdPlusEq] = BinOpTypeAssignPlus;
table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap;
table[TokenIdTimesEq] = BinOpTypeAssignTimes;
table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap;
BinOpType op = table[peek_token(pc)->id];
if (op != BinOpTypeInvalid) {
Token *op_token = eat_token(pc);
AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token);
res->data.bin_op_expr.bin_op = op;
return res;
}
return nullptr;
}
// CompareOp
// <- EQUALEQUAL
// / EXCLAMATIONMARKEQUAL
// / LARROW
// / RARROW
// / LARROWEQUAL
// / RARROWEQUAL
static AstNode *ast_parse_compare_op(ParseContext *pc) {
BinOpType table[TokenIdCount] = {};
table[TokenIdCmpEq] = BinOpTypeCmpEq;
table[TokenIdCmpNotEq] = BinOpTypeCmpNotEq;
table[TokenIdCmpLessThan] = BinOpTypeCmpLessThan;
table[TokenIdCmpGreaterThan] = BinOpTypeCmpGreaterThan;
table[TokenIdCmpLessOrEq] = BinOpTypeCmpLessOrEq;
table[TokenIdCmpGreaterOrEq] = BinOpTypeCmpGreaterOrEq;
BinOpType op = table[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 nullptr;
}
// BitwiseOp
// <- AMPERSAND
// / CARET
// / PIPE
// / KEYWORD_orelse
// / KEYWORD_catch Payload?
static AstNode *ast_parse_bitwise_op(ParseContext *pc) {
BinOpType table[TokenIdCount] = {};
table[TokenIdAmpersand] = BinOpTypeBinAnd;
table[TokenIdBinXor] = BinOpTypeBinXor;
table[TokenIdBinOr] = BinOpTypeBinOr;
table[TokenIdKeywordOrElse] = BinOpTypeUnwrapOptional;
BinOpType op = table[peek_token(pc)->id];
if (op != BinOpTypeInvalid) {
Token *op_token = eat_token(pc);
AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token);
res->data.bin_op_expr.bin_op = op;
return res;
}
Token *catch_token = eat_token_if(pc, TokenIdKeywordCatch);
if (catch_token != nullptr) {
Token *payload = ast_parse_payload(pc);
AstNode *res = ast_create_node(pc, NodeTypeCatchExpr, catch_token);
if (payload != nullptr)
res->data.unwrap_err_expr.symbol = token_symbol(pc, payload);
return res;
}
return nullptr;
}
// BitShiftOp
// <- LARROW2
// / RARROW2
static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {
BinOpType table[TokenIdCount] = {};
table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft;
table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight;
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 nullptr;
}
// 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 nullptr;
}
// 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 nullptr;
}
// PrefixOp
// <- EXCLAMATIONMARK
// / MINUS
// / TILDE
// / MINUSPERCENT
// / AMPERSAND
// / KEYWORD_try
// / KEYWORD_await
static AstNode *ast_parse_prefix_op(ParseContext *pc) {
PrefixOp table[TokenIdCount] = {};
table[TokenIdBang] = PrefixOpBoolNot;
table[TokenIdDash] = PrefixOpNegation;
table[TokenIdTilde] = PrefixOpBinNot;
table[TokenIdMinusPercent] = PrefixOpNegationWrap;
table[TokenIdAmpersand] = PrefixOpAddrOf;
PrefixOp op = table[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;
}
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;
}
Token *await = eat_token_if(pc, TokenIdKeywordAwait);
if (await != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypeAwaitExpr, await);
return res;
}
return nullptr;
}
// PrefixTypeOp
// <- QUESTIONMARK
// / KEYWORD_anyframe MINUSRARROW
// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile)*
// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile)*
static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
Token *questionmark = eat_token_if(pc, TokenIdQuestion);
if (questionmark != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypePrefixOpExpr, questionmark);
res->data.prefix_op_expr.prefix_op = PrefixOpOptional;
return res;
}
Token *anyframe = eat_token_if(pc, TokenIdKeywordAnyFrame);
if (anyframe != nullptr) {
if (eat_token_if(pc, TokenIdArrow) != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypeAnyFrameType, anyframe);
return res;
}
put_back_token(pc);
}
Token *arr_init_lbracket = eat_token_if(pc, TokenIdLBracket);
if (arr_init_lbracket != nullptr) {
Token *underscore = eat_token_if(pc, TokenIdSymbol);
if (underscore == nullptr) {
put_back_token(pc);
} else if (!buf_eql_str(token_buf(underscore), "_")) {
put_back_token(pc);
put_back_token(pc);
} else {
AstNode *sentinel = nullptr;
Token *colon = eat_token_if(pc, TokenIdColon);
if (colon != nullptr) {
sentinel = ast_expect(pc, ast_parse_expr);
}
expect_token(pc, TokenIdRBracket);
AstNode *node = ast_create_node(pc, NodeTypeInferredArrayType, arr_init_lbracket);
node->data.inferred_array_type.sentinel = sentinel;
return node;
}
}
AstNode *ptr = ast_parse_ptr_type_start(pc);
if (ptr != nullptr) {
assert(ptr->type == NodeTypePointerType);
// We might get two pointers from *_ptr_type_start
AstNode *child = ptr->data.pointer_type.op_expr;
if (child == nullptr)
child = ptr;
while (true) {
Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero);
if (allowzero_token != nullptr) {
child->data.pointer_type.allow_zero_token = allowzero_token;
continue;
}
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;
}
if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) {
child->data.pointer_type.is_const = true;
continue;
}
if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) {
child->data.pointer_type.is_volatile = true;
continue;
}
break;
}
return ptr;
}
AstNode *array = ast_parse_array_type_start(pc);
if (array != nullptr) {
assert(array->type == NodeTypeArrayType);
while (true) {
Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero);
if (allowzero_token != nullptr) {
array->data.array_type.allow_zero_token = allowzero_token;
continue;
}
AstNode *align_expr = ast_parse_byte_align(pc);
if (align_expr != nullptr) {
array->data.array_type.align_expr = align_expr;
continue;
}
if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) {
array->data.array_type.is_const = true;
continue;
}
if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) {
array->data.array_type.is_volatile = true;
continue;
}
break;
}
return array;
}
return nullptr;
}
// SuffixOp
// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET
// / DOT IDENTIFIER
// / DOTASTERISK
// / DOTQUESTIONMARK
static AstNode *ast_parse_suffix_op(ParseContext *pc) {
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) {
AstNode *sentinel = nullptr;
end = ast_parse_expr(pc);
if (eat_token_if(pc, TokenIdColon) != nullptr) {
sentinel = ast_parse_expr(pc);
}
expect_token(pc, TokenIdRBracket);
AstNode *res = ast_create_node(pc, NodeTypeSliceExpr, lbracket);
res->data.slice_expr.start = start;
res->data.slice_expr.end = end;
res->data.slice_expr.sentinel = sentinel;
return res;
}
expect_token(pc, TokenIdRBracket);
AstNode *res = ast_create_node(pc, NodeTypeArrayAccessExpr, lbracket);
res->data.array_access_expr.subscript = start;
return res;
}
Token *dot_asterisk = eat_token_if(pc, TokenIdDotStar);
if (dot_asterisk != nullptr)
return ast_create_node(pc, NodeTypePtrDeref, dot_asterisk);
Token *dot = eat_token_if(pc, TokenIdDot);
if (dot != nullptr) {
if (eat_token_if(pc, TokenIdQuestion) != nullptr)
return ast_create_node(pc, NodeTypeUnwrapOptional, dot);
Token *ident = expect_token(pc, TokenIdSymbol);
AstNode *res = ast_create_node(pc, NodeTypeFieldAccessExpr, dot);
res->data.field_access_expr.field_name = token_buf(ident);
return res;
}
return nullptr;
}
// FnCallArguments <- LPAREN ExprList RPAREN
static AstNode *ast_parse_fn_call_arguments(ParseContext *pc) {
Token *paren = eat_token_if(pc, TokenIdLParen);
if (paren == nullptr)
return nullptr;
ZigList<AstNode *> params = ast_parse_list(pc, TokenIdComma, ast_parse_expr);
expect_token(pc, TokenIdRParen);
AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, paren);
res->data.fn_call_expr.params = params;
res->data.fn_call_expr.seen = false;
return res;
}
// ArrayTypeStart <- LBRACKET Expr? RBRACKET
static AstNode *ast_parse_array_type_start(ParseContext *pc) {
Token *lbracket = eat_token_if(pc, TokenIdLBracket);
if (lbracket == nullptr)
return nullptr;
AstNode *size = ast_parse_expr(pc);
AstNode *sentinel = nullptr;
Token *colon = eat_token_if(pc, TokenIdColon);
if (colon != nullptr) {
sentinel = ast_expect(pc, ast_parse_expr);
}
expect_token(pc, TokenIdRBracket);
AstNode *res = ast_create_node(pc, NodeTypeArrayType, lbracket);
res->data.array_type.size = size;
res->data.array_type.sentinel = sentinel;
return res;
}
// PtrTypeStart
// <- ASTERISK
// / ASTERISK2
// / PTRUNKNOWN
// / PTRC
static AstNode *ast_parse_ptr_type_start(ParseContext *pc) {
AstNode *sentinel = nullptr;
Token *asterisk = eat_token_if(pc, TokenIdStar);
if (asterisk != nullptr) {
Token *colon = eat_token_if(pc, TokenIdColon);
if (colon != nullptr) {
sentinel = ast_expect(pc, ast_parse_expr);
}
AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk);
res->data.pointer_type.star_token = asterisk;
res->data.pointer_type.sentinel = sentinel;
return res;
}
Token *asterisk2 = eat_token_if(pc, TokenIdStarStar);
if (asterisk2 != nullptr) {
Token *colon = eat_token_if(pc, TokenIdColon);
if (colon != nullptr) {
sentinel = ast_expect(pc, ast_parse_expr);
}
AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk2);
AstNode *res2 = ast_create_node(pc, NodeTypePointerType, asterisk2);
res->data.pointer_type.star_token = asterisk2;
res2->data.pointer_type.star_token = asterisk2;
res2->data.pointer_type.sentinel = sentinel;
res->data.pointer_type.op_expr = res2;
return res;
}
Token *lbracket = eat_token_if(pc, TokenIdLBracket);
if (lbracket != nullptr) {
Token *star = eat_token_if(pc, TokenIdStar);
if (star == nullptr) {
put_back_token(pc);
} else {
Token *c_tok = eat_token_if(pc, TokenIdSymbol);
if (c_tok != nullptr) {
if (!buf_eql_str(token_buf(c_tok), "c")) {
put_back_token(pc); // c symbol
} else {
expect_token(pc, TokenIdRBracket);
AstNode *res = ast_create_node(pc, NodeTypePointerType, lbracket);
res->data.pointer_type.star_token = c_tok;
return res;
}
}
Token *colon = eat_token_if(pc, TokenIdColon);
if (colon != nullptr) {
sentinel = ast_expect(pc, ast_parse_expr);
}
expect_token(pc, TokenIdRBracket);
AstNode *res = ast_create_node(pc, NodeTypePointerType, lbracket);
res->data.pointer_type.star_token = lbracket;
res->data.pointer_type.sentinel = sentinel;
return res;
}
}
return nullptr;
}
// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE
static AstNode *ast_parse_container_decl_auto(ParseContext *pc) {
AstNode *res = ast_parse_container_decl_type(pc);
if (res == nullptr)
return nullptr;
expect_token(pc, TokenIdLBrace);
AstNodeContainerDecl members = ast_parse_container_members(pc);
expect_token(pc, TokenIdRBrace);
res->data.container_decl.fields = members.fields;
res->data.container_decl.decls = members.decls;
if (buf_len(&members.doc_comments) != 0) {
res->data.container_decl.doc_comments = members.doc_comments;
}
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) {
AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first);
res->data.container_decl.init_arg_expr = nullptr;
res->data.container_decl.kind = ContainerKindStruct;
return res;
}
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 = 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);
}
expect_token(pc, TokenIdRParen);
}
AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first);
res->data.container_decl.init_arg_expr = init_arg_expr;
res->data.container_decl.auto_enum = auto_enum;
res->data.container_decl.kind = ContainerKindUnion;
return res;
}
return nullptr;
}
// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN
static AstNode *ast_parse_byte_align(ParseContext *pc) {
if (eat_token_if(pc, TokenIdKeywordAlign) == 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) {
if (*node) {
visit(node, context);
}
}
static void visit_node_list(ZigList<AstNode *> *list, void (*visit)(AstNode **, void *context), void *context) {
if (list) {
for (size_t i = 0; i < list->length; i += 1) {
visit(&list->at(i), context);
}
}
}
void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context) {
switch (node->type) {
case NodeTypeFnProto:
visit_field(&node->data.fn_proto.return_type, visit, context);
visit_node_list(&node->data.fn_proto.params, visit, context);
visit_field(&node->data.fn_proto.align_expr, visit, context);
visit_field(&node->data.fn_proto.section_expr, visit, context);
break;
case NodeTypeFnDef:
visit_field(&node->data.fn_def.fn_proto, visit, context);
visit_field(&node->data.fn_def.body, visit, context);
break;
case NodeTypeParamDecl:
visit_field(&node->data.param_decl.type, visit, context);
break;
case NodeTypeBlock:
visit_node_list(&node->data.block.statements, visit, context);
break;
case NodeTypeGroupedExpr:
visit_field(&node->data.grouped_expr, visit, context);
break;
case NodeTypeReturnExpr:
visit_field(&node->data.return_expr.expr, visit, context);
break;
case NodeTypeDefer:
visit_field(&node->data.defer.expr, visit, context);
break;
case NodeTypeVariableDeclaration:
visit_field(&node->data.variable_declaration.type, visit, context);
visit_field(&node->data.variable_declaration.expr, visit, context);
visit_field(&node->data.variable_declaration.align_expr, visit, context);
visit_field(&node->data.variable_declaration.section_expr, visit, context);
break;
case NodeTypeTestDecl:
visit_field(&node->data.test_decl.body, visit, context);
break;
case NodeTypeBinOpExpr:
visit_field(&node->data.bin_op_expr.op1, visit, context);
visit_field(&node->data.bin_op_expr.op2, visit, context);
break;
case NodeTypeCatchExpr:
visit_field(&node->data.unwrap_err_expr.op1, visit, context);
visit_field(&node->data.unwrap_err_expr.symbol, visit, context);
visit_field(&node->data.unwrap_err_expr.op2, visit, context);
break;
case NodeTypeIntLiteral:
// none
break;
case NodeTypeFloatLiteral:
// none
break;
case NodeTypeStringLiteral:
// none
break;
case NodeTypeCharLiteral:
// none
break;
case NodeTypeSymbol:
// none
break;
case NodeTypePrefixOpExpr:
visit_field(&node->data.prefix_op_expr.primary_expr, visit, context);
break;
case NodeTypeFnCallExpr:
visit_field(&node->data.fn_call_expr.fn_ref_expr, visit, context);
visit_node_list(&node->data.fn_call_expr.params, visit, context);
break;
case NodeTypeArrayAccessExpr:
visit_field(&node->data.array_access_expr.array_ref_expr, visit, context);
visit_field(&node->data.array_access_expr.subscript, visit, context);
break;
case NodeTypeSliceExpr:
visit_field(&node->data.slice_expr.array_ref_expr, visit, context);
visit_field(&node->data.slice_expr.start, visit, context);
visit_field(&node->data.slice_expr.end, visit, context);
visit_field(&node->data.slice_expr.sentinel, visit, context);
break;
case NodeTypeFieldAccessExpr:
visit_field(&node->data.field_access_expr.struct_expr, visit, context);
break;
case NodeTypePtrDeref:
visit_field(&node->data.ptr_deref_expr.target, visit, context);
break;
case NodeTypeUnwrapOptional:
visit_field(&node->data.unwrap_optional.expr, visit, context);
break;
case NodeTypeUsingNamespace:
visit_field(&node->data.using_namespace.expr, visit, context);
break;
case NodeTypeBoolLiteral:
// none
break;
case NodeTypeNullLiteral:
// none
break;
case NodeTypeUndefinedLiteral:
// none
break;
case NodeTypeIfBoolExpr:
visit_field(&node->data.if_bool_expr.condition, visit, context);
visit_field(&node->data.if_bool_expr.then_block, visit, context);
visit_field(&node->data.if_bool_expr.else_node, visit, context);
break;
case NodeTypeIfErrorExpr:
visit_field(&node->data.if_err_expr.target_node, visit, context);
visit_field(&node->data.if_err_expr.then_node, visit, context);
visit_field(&node->data.if_err_expr.else_node, visit, context);
break;
case NodeTypeIfOptional:
visit_field(&node->data.test_expr.target_node, visit, context);
visit_field(&node->data.test_expr.then_node, visit, context);
visit_field(&node->data.test_expr.else_node, visit, context);
break;
case NodeTypeWhileExpr:
visit_field(&node->data.while_expr.condition, visit, context);
visit_field(&node->data.while_expr.body, visit, context);
break;
case NodeTypeForExpr:
visit_field(&node->data.for_expr.elem_node, visit, context);
visit_field(&node->data.for_expr.array_expr, visit, context);
visit_field(&node->data.for_expr.index_node, visit, context);
visit_field(&node->data.for_expr.body, visit, context);
break;
case NodeTypeSwitchExpr:
visit_field(&node->data.switch_expr.expr, visit, context);
visit_node_list(&node->data.switch_expr.prongs, visit, context);
break;
case NodeTypeSwitchProng:
visit_node_list(&node->data.switch_prong.items, visit, context);
visit_field(&node->data.switch_prong.var_symbol, visit, context);
visit_field(&node->data.switch_prong.expr, visit, context);
break;
case NodeTypeSwitchRange:
visit_field(&node->data.switch_range.start, visit, context);
visit_field(&node->data.switch_range.end, visit, context);
break;
case NodeTypeCompTime:
visit_field(&node->data.comptime_expr.expr, visit, context);
break;
case NodeTypeBreak:
// none
break;
case NodeTypeContinue:
// none
break;
case NodeTypeUnreachable:
// none
break;
case NodeTypeAsmExpr:
for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) {
AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
visit_field(&asm_input->expr, visit, context);
}
for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1) {
AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
visit_field(&asm_output->return_type, visit, context);
}
break;
case NodeTypeContainerDecl:
visit_node_list(&node->data.container_decl.fields, visit, context);
visit_node_list(&node->data.container_decl.decls, visit, context);
visit_field(&node->data.container_decl.init_arg_expr, visit, context);
break;
case NodeTypeStructField:
visit_field(&node->data.struct_field.type, visit, context);
visit_field(&node->data.struct_field.value, visit, context);
break;
case NodeTypeContainerInitExpr:
visit_field(&node->data.container_init_expr.type, visit, context);
visit_node_list(&node->data.container_init_expr.entries, visit, context);
break;
case NodeTypeStructValueField:
visit_field(&node->data.struct_val_field.expr, visit, context);
break;
case NodeTypeArrayType:
visit_field(&node->data.array_type.size, visit, context);
visit_field(&node->data.array_type.sentinel, visit, context);
visit_field(&node->data.array_type.child_type, visit, context);
visit_field(&node->data.array_type.align_expr, visit, context);
break;
case NodeTypeInferredArrayType:
visit_field(&node->data.array_type.sentinel, visit, context);
visit_field(&node->data.array_type.child_type, visit, context);
break;
case NodeTypeAnyFrameType:
visit_field(&node->data.anyframe_type.payload_type, visit, context);
break;
case NodeTypeErrorType:
// none
break;
case NodeTypePointerType:
visit_field(&node->data.pointer_type.sentinel, visit, context);
visit_field(&node->data.pointer_type.align_expr, visit, context);
visit_field(&node->data.pointer_type.op_expr, visit, context);
break;
case NodeTypeErrorSetDecl:
visit_node_list(&node->data.err_set_decl.decls, visit, context);
break;
case NodeTypeErrorSetField:
visit_field(&node->data.err_set_field.field_name, visit, context);
break;
case NodeTypeResume:
visit_field(&node->data.resume_expr.expr, visit, context);
break;
case NodeTypeAwaitExpr:
visit_field(&node->data.await_expr.expr, visit, context);
break;
case NodeTypeSuspend:
visit_field(&node->data.suspend.block, visit, context);
break;
case NodeTypeEnumLiteral:
case NodeTypeVarFieldType:
break;
}
}