zig/src/parser.cpp
Andrew Kelley af8661405b
fix usingnamespace
It used to be that usingnamespace was only allowed at top level. This
made it OK to put the state inside the AST node data structure. However,
now usingnamespace can occur inside any aggregate data structure, and
therefore the state must be in the TopLevelDeclaration rather than in
the AST node.

There were two other problems with the usingnamespace implementation:

 * It was passing the wrong destination ScopeDecl, so it could cause an
   incorrect error such as "import of file outside package path".
 * When doing `usingnamespace` on a file that already had
   `pub usingnamespace` in it would "steal" the usingnamespace, causing
   incorrect "use of undeclared identifier" errors in the target file.

closes #2632
closes #2580
2019-07-19 16:56:44 -04:00

3066 lines
105 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);
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_enum_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 Optional<AstNodeFnProto> ast_parse_fn_cc(ParseContext *pc);
static AstNode *ast_parse_param_decl(ParseContext *pc);
static AstNode *ast_parse_param_type(ParseContext *pc);
static AstNode *ast_parse_if_prefix(ParseContext *pc);
static AstNode *ast_parse_while_prefix(ParseContext *pc);
static AstNode *ast_parse_for_prefix(ParseContext *pc);
static Token *ast_parse_payload(ParseContext *pc);
static Optional<PtrPayload> ast_parse_ptr_payload(ParseContext *pc);
static Optional<PtrIndexPayload> ast_parse_ptr_index_payload(ParseContext *pc);
static AstNode *ast_parse_switch_prong(ParseContext *pc);
static AstNode *ast_parse_switch_case(ParseContext *pc);
static AstNode *ast_parse_switch_item(ParseContext *pc);
static AstNode *ast_parse_assign_op(ParseContext *pc);
static AstNode *ast_parse_compare_op(ParseContext *pc);
static AstNode *ast_parse_bitwise_op(ParseContext *pc);
static AstNode *ast_parse_bit_shift_op(ParseContext *pc);
static AstNode *ast_parse_addition_op(ParseContext *pc);
static AstNode *ast_parse_multiply_op(ParseContext *pc);
static AstNode *ast_parse_prefix_op(ParseContext *pc);
static AstNode *ast_parse_prefix_type_op(ParseContext *pc);
static AstNode *ast_parse_suffix_op(ParseContext *pc);
static AstNode *ast_parse_async_prefix(ParseContext *pc);
static AstNode *ast_parse_fn_call_argumnets(ParseContext *pc);
static AstNode *ast_parse_array_type_start(ParseContext *pc);
static AstNode *ast_parse_ptr_type_start(ParseContext *pc);
static AstNode *ast_parse_container_decl_auto(ParseContext *pc);
static AstNode *ast_parse_container_decl_type(ParseContext *pc);
static AstNode *ast_parse_byte_align(ParseContext *pc);
ATTRIBUTE_PRINTF(3, 4)
ATTRIBUTE_NORETURN
static void ast_error(ParseContext *pc, Token *token, const char *format, ...) {
va_list ap;
va_start(ap, format);
Buf *msg = buf_vprintf(format, ap);
va_end(ap);
ErrorMsg *err = err_msg_create_with_line(pc->owner->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);
}
static Buf ast_token_str(Buf *input, Token *token) {
Buf str = BUF_INIT;
buf_init_from_mem(&str, buf_ptr(input) + token->start_pos, token->end_pos - token->start_pos);
return str;
}
ATTRIBUTE_NORETURN
static void ast_invalid_token_error(ParseContext *pc, Token *token) {
Buf token_value = ast_token_str(pc->buf, token);
ast_error(pc, token, "invalid token: '%s'", buf_ptr(&token_value));
}
static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) {
AstNode *node = allocate<AstNode>(1);
node->type = type;
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 == TokenIdSymbol);
return &token->data.str_lit.str;
}
static BigInt *token_bigint(Token *token) {
assert(token->id == TokenIdIntLiteral);
return &token->data.int_lit.bigint;
}
static AstNode *token_symbol(ParseContext *pc, Token *token) {
assert(token->id == TokenIdSymbol);
AstNode *res = ast_create_node(pc, NodeTypeSymbol, token);
res->data.symbol_expr.symbol = token_buf(token);
return res;
}
// (Rule SEP)* Rule?
template<typename T>
static ZigList<T *> ast_parse_list(ParseContext *pc, TokenId sep, T *(*parser)(ParseContext*)) {
ZigList<T *> res = {};
while (true) {
T *curr = parser(pc);
if (curr == nullptr)
break;
res.append(curr);
if (eat_token_if(pc, sep) == nullptr)
break;
}
return res;
}
static AstNode *ast_expect(ParseContext *pc, AstNode *(*parser)(ParseContext*)) {
AstNode *res = parser(pc);
if (res == nullptr)
ast_invalid_token_error(pc, peek_token(pc));
return res;
}
enum BinOpChain {
BinOpChainOnce,
BinOpChainInf,
};
// Op* Child
static AstNode *ast_parse_prefix_op_expr(
ParseContext *pc,
AstNode *(*op_parser)(ParseContext *),
AstNode *(*child_parser)(ParseContext *)
) {
AstNode *res = nullptr;
AstNode **right = &res;
while (true) {
AstNode *prefix = op_parser(pc);
if (prefix == nullptr)
break;
*right = prefix;
switch (prefix->type) {
case NodeTypePrefixOpExpr:
right = &prefix->data.prefix_op_expr.primary_expr;
break;
case NodeTypeReturnExpr:
right = &prefix->data.return_expr.expr;
break;
case NodeTypeAwaitExpr:
right = &prefix->data.await_expr.expr;
break;
case NodeTypePromiseType:
right = &prefix->data.promise_type.payload_type;
break;
case NodeTypeArrayType:
right = &prefix->data.array_type.child_type;
break;
case 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;
return node;
}
// ContainerMembers
// <- TestDecl ContainerMembers
// / TopLevelComptime ContainerMembers
// / KEYWORD_pub? TopLevelDecl ContainerMembers
// / KEYWORD_pub? ContainerField COMMA ContainerMembers
// / KEYWORD_pub? ContainerField
// /
static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
AstNodeContainerDecl res = {};
for (;;) {
AstNode *test_decl = ast_parse_test_decl(pc);
if (test_decl != nullptr) {
res.decls.append(test_decl);
continue;
}
AstNode *top_level_comptime = ast_parse_top_level_comptime(pc);
if (top_level_comptime != nullptr) {
res.decls.append(top_level_comptime);
continue;
}
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);
if (top_level_decl != nullptr) {
res.decls.append(top_level_decl);
continue;
}
AstNode *container_field = ast_parse_container_field(pc);
if (container_field != nullptr) {
assert(container_field->type == NodeTypeStructField);
container_field->data.struct_field.visib_mod = visib_mod;
res.fields.append(container_field);
if (eat_token_if(pc, TokenIdComma) != nullptr) {
continue;
} else {
break;
}
}
// We visib_token wasn't eaten, then we haven't consumed the first token in this rule yet.
// It is therefore safe to return and let the caller continue parsing.
if (visib_token == nullptr)
break;
ast_invalid_token_error(pc, peek_token(pc));
}
return res;
}
// TestDecl <- KEYWORD_test STRINGLITERAL Block
static AstNode *ast_parse_test_decl(ParseContext *pc) {
Token *test = eat_token_if(pc, TokenIdKeywordTest);
if (test == nullptr)
return nullptr;
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;
AstNode *block = ast_expect(pc, ast_parse_block_expr);
AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime);
res->data.comptime_expr.expr = block;
return res;
}
// TopLevelDecl
// <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / KEYWORD_inline)? FnProto (SEMICOLON / Block)
// / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? KEYWORD_threadlocal? VarDecl
// / KEYWORD_use Expr SEMICOLON
static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) {
Token *first = eat_token_if(pc, TokenIdKeywordExport);
if (first == nullptr)
first = eat_token_if(pc, TokenIdKeywordExtern);
if (first == nullptr)
first = eat_token_if(pc, TokenIdKeywordInline);
if (first != nullptr) {
Token *lib_name = nullptr;
if (first->id == TokenIdKeywordExtern)
lib_name = eat_token_if(pc, TokenIdStringLiteral);
if (first->id != TokenIdKeywordInline) {
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.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.is_extern = first->id == TokenIdKeywordExtern;
fn_proto->data.fn_proto.is_export = first->id == TokenIdKeywordExport;
fn_proto->data.fn_proto.is_inline = first->id == TokenIdKeywordInline;
fn_proto->data.fn_proto.lib_name = token_buf(lib_name);
AstNode *res = fn_proto;
if (body != nullptr) {
res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto);
res->data.fn_def.fn_proto = fn_proto;
res->data.fn_def.body = body;
fn_proto->data.fn_proto.fn_def_node = res;
}
return res;
}
ast_invalid_token_error(pc, peek_token(pc));
}
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.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;
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.cc == CallingConventionC) {
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);
Token *var = eat_token_if(pc, TokenIdKeywordVar);
Token *exmark = nullptr;
AstNode *return_type = nullptr;
if (var == nullptr) {
exmark = eat_token_if(pc, TokenIdBang);
return_type = ast_expect(pc, ast_parse_type_expr);
}
AstNode *res = ast_create_node(pc, NodeTypeFnProto, first);
res->data.fn_proto = fn_cc;
res->data.fn_proto.name = token_buf(identifier);
res->data.fn_proto.params = params;
res->data.fn_proto.align_expr = align_expr;
res->data.fn_proto.section_expr = section_expr;
res->data.fn_proto.return_var_token = var;
res->data.fn_proto.auto_err_set = exmark != nullptr;
res->data.fn_proto.return_type = return_type;
// It seems that the Zig compiler expects varargs to be the
// last parameter in the decl list. This is not encoded in
// the grammar, which allows varargs anywhere in the decl.
// Since varargs is gonna be removed at some point, I'm not
// gonna encode this "varargs is always last" rule in the
// grammar, and just enforce it here, until varargs is removed.
for (size_t i = 0; i < params.length; i++) {
AstNode *param_decl = params.at(i);
assert(param_decl->type == NodeTypeParamDecl);
if (param_decl->data.param_decl.is_var_args)
res->data.fn_proto.is_var_args = true;
if (i != params.length - 1 && res->data.fn_proto.is_var_args)
ast_error(pc, first, "Function prototype have varargs as a none last paramter.");
}
return res;
}
// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? 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)? (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)
type_expr = ast_expect(pc, ast_parse_type_expr);
AstNode *expr = nullptr;
if (eat_token_if(pc, TokenIdEq) != nullptr)
expr = ast_expect(pc, ast_parse_expr);
AstNode *res = ast_create_node(pc, NodeTypeStructField, identifier);
res->data.struct_field.name = token_buf(identifier);
res->data.struct_field.type = type_expr;
res->data.struct_field.value = expr;
return res;
}
// Statement
// <- KEYWORD_comptime? VarDecl
// / KEYWORD_comptime BlockExprStatement
// / KEYWORD_suspend (SEMICOLON / BlockExprStatement)
// / KEYWORD_defer BlockExprStatement
// / KEYWORD_errdefer BlockExprStatement
// / IfStatement
// / LabeledStatement
// / SwitchExpr
// / AssignExpr SEMICOLON
static AstNode *ast_parse_statement(ParseContext *pc) {
Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime);
AstNode *var_decl = ast_parse_var_decl(pc);
if (var_decl != nullptr) {
assert(var_decl->type == NodeTypeVariableDeclaration);
var_decl->data.variable_declaration.is_comptime = comptime != nullptr;
return var_decl;
}
if (comptime != nullptr) {
AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement);
AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime);
res->data.comptime_expr.expr = statement;
return res;
}
Token *suspend = eat_token_if(pc, TokenIdKeywordSuspend);
if (suspend != nullptr) {
AstNode *statement = nullptr;
if (eat_token_if(pc, TokenIdSemicolon) == nullptr)
statement = ast_expect(pc, ast_parse_block_expr_statement);
AstNode *res = ast_create_node(pc, NodeTypeSuspend, suspend);
res->data.suspend.block = statement;
return res;
}
Token *defer = eat_token_if(pc, TokenIdKeywordDefer);
if (defer == nullptr)
defer = eat_token_if(pc, TokenIdKeywordErrdefer);
if (defer != nullptr) {
AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement);
AstNode *res = ast_create_node(pc, NodeTypeDefer, defer);
res->data.defer.kind = ReturnKindUnconditional;
res->data.defer.expr = statement;
if (defer->id == TokenIdKeywordErrdefer)
res->data.defer.kind = ReturnKindError;
return res;
}
AstNode *if_statement = ast_parse_if_statement(pc);
if (if_statement != nullptr)
return if_statement;
AstNode *labeled_statement = ast_parse_labeled_statement(pc);
if (labeled_statement != nullptr)
return labeled_statement;
AstNode *switch_expr = ast_parse_switch_expr(pc);
if (switch_expr != nullptr)
return switch_expr;
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_cancel 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 *cancel = eat_token_if(pc, TokenIdKeywordCancel);
if (cancel != nullptr) {
AstNode *expr = ast_expect(pc, ast_parse_expr);
AstNode *res = ast_create_node(pc, NodeTypeCancel, cancel);
res->data.cancel_expr.expr = expr;
return res;
}
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
// <- AsyncPrefix PrimaryTypeExpr SuffixOp* FnCallArguments
// / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
static AstNode *ast_parse_suffix_expr(ParseContext *pc) {
AstNode *async_call = ast_parse_async_prefix(pc);
if (async_call != nullptr) {
if (eat_token_if(pc, TokenIdKeywordFn) != nullptr) {
// HACK: If we see the keyword `fn`, then we assume that
// we are parsing an async fn proto, and not a call.
// We therefore put back all tokens consumed by the async
// prefix...
// HACK: This loop is not actually enough to put back all the
// tokens. Let's hope this is fine for most code right now
// and wait till we get the async rework for a syntax update.
do {
put_back_token(pc);
} while (peek_token(pc)->id != TokenIdKeywordAsync);
return ast_parse_primary_type_expr(pc);
}
AstNode *child = ast_expect(pc, ast_parse_primary_type_expr);
while (true) {
AstNode *suffix = ast_parse_suffix_op(pc);
if (suffix == nullptr)
break;
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_argumnets returns an
// AstNode *. All we really want here is the arguments of
// the call we parse. We therefor "leak" the node for now.
// Wait till we get async rework to fix this.
AstNode *args = ast_parse_fn_call_argumnets(pc);
if (args == nullptr)
ast_invalid_token_error(pc, peek_token(pc));
assert(args->type == NodeTypeFnCallExpr);
async_call->data.fn_call_expr.fn_ref_expr = child;
async_call->data.fn_call_expr.params = args->data.fn_call_expr.params;
async_call->data.fn_call_expr.is_builtin = false;
return async_call;
}
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_argumnets(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_argumnets);
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.is_builtin = true;
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 *enum_lit = ast_parse_enum_lit(pc);
if (enum_lit != nullptr)
return enum_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 *promise = eat_token_if(pc, TokenIdKeywordPromise);
if (promise != nullptr)
return ast_create_node(pc, NodeTypePromiseType, promise);
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) {
AstNode *res = ast_create_node(pc, NodeTypeStringLiteral, string_lit);
res->data.string_literal.buf = token_buf(string_lit);
res->data.string_literal.c = string_lit->data.str_lit.is_c_str;
return res;
}
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) {
Token *ident = eat_token_if(context, TokenIdSymbol);
if (ident == nullptr)
return (AstNode*)nullptr;
return token_symbol(context, ident);
});
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)
ast_invalid_token_error(pc, peek_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);
Token *asm_template = expect_token(pc, TokenIdStringLiteral);
AstNode *res = ast_parse_asm_output(pc);
if (res == nullptr)
res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr);
expect_token(pc, TokenIdRParen);
res->line = asm_token->start_line;
res->column = asm_token->start_column;
res->data.asm_expr.volatile_token = volatile_token;
res->data.asm_expr.asm_template = asm_template;
return res;
}
static AstNode *ast_parse_enum_lit(ParseContext *pc) {
Token *period = eat_token_if(pc, TokenIdDot);
if (period == nullptr)
return nullptr;
Token *identifier = expect_token(pc, TokenIdSymbol);
AstNode *res = ast_create_node(pc, NodeTypeEnumLiteral, period);
res->data.enum_literal.period = period;
res->data.enum_literal.identifier = identifier;
return res;
}
// 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) {
Token *sym_name = eat_token_if(pc, TokenIdBracketUnderscoreBracket);
if (sym_name == nullptr) {
if (eat_token_if(pc, TokenIdLBracket) == nullptr) {
return nullptr;
} else {
sym_name = expect_token(pc, TokenIdSymbol);
expect_token(pc, TokenIdRBracket);
}
}
Token *str = expect_token(pc, TokenIdStringLiteral);
expect_token(pc, TokenIdLParen);
Token *var_name = eat_token_if(pc, TokenIdSymbol);
AstNode *return_type = nullptr;
if (var_name == nullptr) {
expect_token(pc, TokenIdArrow);
return_type = ast_expect(pc, ast_parse_type_expr);
}
expect_token(pc, TokenIdRParen);
AsmOutput *res = allocate<AsmOutput>(1);
res->asm_symbolic_name = (sym_name->id == TokenIdBracketUnderscoreBracket) ? buf_create_from_str("_") : 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) {
Token *sym_name = eat_token_if(pc, TokenIdBracketUnderscoreBracket);
if (sym_name == nullptr) {
if (eat_token_if(pc, TokenIdLBracket) == nullptr) {
return nullptr;
} else {
sym_name = expect_token(pc, TokenIdSymbol);
expect_token(pc, TokenIdRBracket);
}
}
Token *constraint = expect_token(pc, TokenIdStringLiteral);
expect_token(pc, TokenIdLParen);
AstNode *expr = ast_expect(pc, ast_parse_expr);
expect_token(pc, TokenIdRParen);
AsmInput *res = allocate<AsmInput>(1);
res->asm_symbolic_name = (sym_name->id == TokenIdBracketUnderscoreBracket) ? buf_create_from_str("_") : 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)
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 = expect_token(pc, TokenIdSymbol);
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;
}
// FnCC
// <- KEYWORD_nakedcc
// / KEYWORD_stdcallcc
// / KEYWORD_extern
// / KEYWORD_async (LARROW TypeExpr RARROW)?
static Optional<AstNodeFnProto> ast_parse_fn_cc(ParseContext *pc) {
AstNodeFnProto res = {};
if (eat_token_if(pc, TokenIdKeywordNakedCC) != nullptr) {
res.cc = CallingConventionNaked;
return Optional<AstNodeFnProto>::some(res);
}
if (eat_token_if(pc, TokenIdKeywordStdcallCC) != nullptr) {
res.cc = CallingConventionStdcall;
return Optional<AstNodeFnProto>::some(res);
}
if (eat_token_if(pc, TokenIdKeywordExtern) != nullptr) {
res.cc = CallingConventionC;
return Optional<AstNodeFnProto>::some(res);
}
if (eat_token_if(pc, TokenIdKeywordAsync) != nullptr) {
res.cc = CallingConventionAsync;
if (eat_token_if(pc, TokenIdCmpLessThan) == nullptr)
return Optional<AstNodeFnProto>::some(res);
res.async_allocator_type = ast_expect(pc, ast_parse_type_expr);
expect_token(pc, TokenIdCmpGreaterThan);
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) {
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.is_noalias = first->id == TokenIdKeywordNoAlias;
res->data.param_decl.is_inline = 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_promise MINUSRARROW
// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile)*
// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile)*
static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
Token *questionmark = eat_token_if(pc, TokenIdQuestion);
if (questionmark != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypePrefixOpExpr, questionmark);
res->data.prefix_op_expr.prefix_op = PrefixOpOptional;
return res;
}
Token *promise = eat_token_if(pc, TokenIdKeywordPromise);
if (promise != nullptr) {
if (eat_token_if(pc, TokenIdArrow) != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypePromiseType, promise);
return res;
}
put_back_token(pc);
}
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;
}
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;
}
Token *arr_init = eat_token_if(pc, TokenIdBracketUnderscoreBracket);
if (arr_init != nullptr) {
return ast_create_node(pc, NodeTypeInferredArrayType, arr_init);
}
return nullptr;
}
// SuffixOp
// <- LBRACKET Expr (DOT2 Expr?)? RBRACKET
// / DOT IDENTIFIER
// / DOTASTERISK
// / DOTQUESTIONMARK
static AstNode *ast_parse_suffix_op(ParseContext *pc) {
Token *lbracket = eat_token_if(pc, TokenIdLBracket);
if (lbracket != nullptr) {
AstNode *start = ast_expect(pc, ast_parse_expr);
AstNode *end = nullptr;
if (eat_token_if(pc, TokenIdEllipsis2) != nullptr) {
end = ast_parse_expr(pc);
expect_token(pc, TokenIdRBracket);
AstNode *res = ast_create_node(pc, NodeTypeSliceExpr, lbracket);
res->data.slice_expr.start = start;
res->data.slice_expr.end = end;
return res;
}
expect_token(pc, TokenIdRBracket);
AstNode *res = ast_create_node(pc, NodeTypeArrayAccessExpr, lbracket);
res->data.array_access_expr.subscript = start;
return res;
}
Token *dot = eat_token_if(pc, TokenIdDot);
if (dot != nullptr) {
if (eat_token_if(pc, TokenIdStar) != nullptr)
return ast_create_node(pc, NodeTypePtrDeref, dot);
if (eat_token_if(pc, TokenIdQuestion) != nullptr)
return ast_create_node(pc, NodeTypeUnwrapOptional, dot);
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;
}
// AsyncPrefix <- KEYWORD_async (LARROW PrefixExpr RARROW)?
static AstNode *ast_parse_async_prefix(ParseContext *pc) {
Token *async = eat_token_if(pc, TokenIdKeywordAsync);
if (async == nullptr)
return nullptr;
AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, async);
res->data.fn_call_expr.is_async = true;
res->data.fn_call_expr.seen = false;
if (eat_token_if(pc, TokenIdCmpLessThan) != nullptr) {
AstNode *prefix_expr = ast_expect(pc, ast_parse_prefix_expr);
expect_token(pc, TokenIdCmpGreaterThan);
res->data.fn_call_expr.async_allocator = prefix_expr;
}
return res;
}
// FnCallArguments <- LPAREN ExprList RPAREN
static AstNode *ast_parse_fn_call_argumnets(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);
expect_token(pc, TokenIdRBracket);
AstNode *res = ast_create_node(pc, NodeTypeArrayType, lbracket);
res->data.array_type.size = size;
return res;
}
// PtrTypeStart
// <- ASTERISK
// / ASTERISK2
// / PTRUNKNOWN
// / PTRC
static AstNode *ast_parse_ptr_type_start(ParseContext *pc) {
Token *asterisk = eat_token_if(pc, TokenIdStar);
if (asterisk != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk);
res->data.pointer_type.star_token = asterisk;
return res;
}
Token *asterisk2 = eat_token_if(pc, TokenIdStarStar);
if (asterisk2 != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk2);
AstNode *res2 = ast_create_node(pc, NodeTypePointerType, asterisk2);
res->data.pointer_type.star_token = asterisk2;
res2->data.pointer_type.star_token = asterisk2;
res->data.pointer_type.op_expr = res2;
return res;
}
Token *multptr = eat_token_if(pc, TokenIdBracketStarBracket);
if (multptr != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypePointerType, multptr);
res->data.pointer_type.star_token = multptr;
return res;
}
Token *cptr = eat_token_if(pc, TokenIdBracketStarCBracket);
if (cptr != nullptr) {
AstNode *res = ast_create_node(pc, NodeTypePointerType, cptr);
res->data.pointer_type.star_token = cptr;
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;
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);
visit_field(&node->data.fn_proto.async_allocator_type, visit, context);
break;
case NodeTypeFnDef:
visit_field(&node->data.fn_def.fn_proto, visit, context);
visit_field(&node->data.fn_def.body, visit, context);
break;
case NodeTypeParamDecl:
visit_field(&node->data.param_decl.type, visit, context);
break;
case NodeTypeBlock:
visit_node_list(&node->data.block.statements, visit, context);
break;
case NodeTypeGroupedExpr:
visit_field(&node->data.grouped_expr, visit, context);
break;
case NodeTypeReturnExpr:
visit_field(&node->data.return_expr.expr, visit, context);
break;
case NodeTypeDefer:
visit_field(&node->data.defer.expr, visit, context);
break;
case NodeTypeVariableDeclaration:
visit_field(&node->data.variable_declaration.type, visit, context);
visit_field(&node->data.variable_declaration.expr, visit, context);
visit_field(&node->data.variable_declaration.align_expr, visit, context);
visit_field(&node->data.variable_declaration.section_expr, visit, context);
break;
case NodeTypeTestDecl:
visit_field(&node->data.test_decl.body, visit, context);
break;
case NodeTypeBinOpExpr:
visit_field(&node->data.bin_op_expr.op1, visit, context);
visit_field(&node->data.bin_op_expr.op2, visit, context);
break;
case 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);
visit_field(&node->data.fn_call_expr.async_allocator, visit, context);
break;
case NodeTypeArrayAccessExpr:
visit_field(&node->data.array_access_expr.array_ref_expr, visit, context);
visit_field(&node->data.array_access_expr.subscript, visit, context);
break;
case NodeTypeSliceExpr:
visit_field(&node->data.slice_expr.array_ref_expr, visit, context);
visit_field(&node->data.slice_expr.start, visit, context);
visit_field(&node->data.slice_expr.end, visit, context);
break;
case NodeTypeFieldAccessExpr:
visit_field(&node->data.field_access_expr.struct_expr, visit, context);
break;
case NodeTypePtrDeref:
visit_field(&node->data.ptr_deref_expr.target, visit, context);
break;
case 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.child_type, visit, context);
visit_field(&node->data.array_type.align_expr, visit, context);
break;
case NodeTypeInferredArrayType:
visit_field(&node->data.array_type.child_type, visit, context);
break;
case NodeTypePromiseType:
visit_field(&node->data.promise_type.payload_type, visit, context);
break;
case NodeTypeErrorType:
// none
break;
case NodeTypePointerType:
visit_field(&node->data.pointer_type.align_expr, visit, context);
visit_field(&node->data.pointer_type.op_expr, visit, context);
break;
case NodeTypeErrorSetDecl:
visit_node_list(&node->data.err_set_decl.decls, visit, context);
break;
case NodeTypeCancel:
visit_field(&node->data.cancel_expr.expr, visit, context);
break;
case NodeTypeResume:
visit_field(&node->data.resume_expr.expr, visit, context);
break;
case NodeTypeAwaitExpr:
visit_field(&node->data.await_expr.expr, visit, context);
break;
case NodeTypeSuspend:
visit_field(&node->data.suspend.block, visit, context);
break;
case NodeTypeEnumLiteral:
break;
}
}