96164ce613
add pointer arithmetic for unknown length pointer
3155 lines
109 KiB
C++
3155 lines
109 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;
|
|
AstNode *root;
|
|
ZigList<Token> *tokens;
|
|
ImportTableEntry *owner;
|
|
ErrColor err_color;
|
|
// These buffers are used freqently so we preallocate them once here.
|
|
Buf *void_buf;
|
|
};
|
|
|
|
ATTRIBUTE_PRINTF(4, 5)
|
|
ATTRIBUTE_NORETURN
|
|
static void ast_asm_error(ParseContext *pc, AstNode *node, size_t offset, const char *format, ...) {
|
|
assert(node->type == NodeTypeAsmExpr);
|
|
|
|
|
|
// TODO calculate or otherwise keep track of originating line/column number for strings
|
|
//SrcPos pos = node->data.asm_expr.offset_map.at(offset);
|
|
SrcPos pos = { node->line, node->column };
|
|
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
Buf *msg = buf_vprintf(format, ap);
|
|
va_end(ap);
|
|
|
|
ErrorMsg *err = err_msg_create_with_line(pc->owner->path, pos.line, pos.column,
|
|
pc->owner->source_code, pc->owner->line_offsets, msg);
|
|
|
|
print_err_msg(err, pc->err_color);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
ATTRIBUTE_PRINTF(3, 4)
|
|
ATTRIBUTE_NORETURN
|
|
static void ast_error(ParseContext *pc, Token *token, const char *format, ...) {
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
Buf *msg = buf_vprintf(format, ap);
|
|
va_end(ap);
|
|
|
|
|
|
ErrorMsg *err = err_msg_create_with_line(pc->owner->path, token->start_line, token->start_column,
|
|
pc->owner->source_code, pc->owner->line_offsets, msg);
|
|
err->line_start = token->start_line;
|
|
err->column_start = token->start_column;
|
|
|
|
print_err_msg(err, pc->err_color);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) {
|
|
AstNode *node = allocate<AstNode>(1);
|
|
node->type = type;
|
|
node->owner = pc->owner;
|
|
return node;
|
|
}
|
|
|
|
static void ast_update_node_line_info(AstNode *node, Token *first_token) {
|
|
assert(first_token);
|
|
node->line = first_token->start_line;
|
|
node->column = first_token->start_column;
|
|
}
|
|
|
|
static AstNode *ast_create_node(ParseContext *pc, NodeType type, Token *first_token) {
|
|
assert(first_token);
|
|
AstNode *node = ast_create_node_no_line_info(pc, type);
|
|
ast_update_node_line_info(node, first_token);
|
|
return node;
|
|
}
|
|
|
|
|
|
static void parse_asm_template(ParseContext *pc, AstNode *node) {
|
|
Buf *asm_template = node->data.asm_expr.asm_template;
|
|
|
|
enum State {
|
|
StateStart,
|
|
StatePercent,
|
|
StateTemplate,
|
|
StateVar,
|
|
};
|
|
|
|
ZigList<AsmToken> *tok_list = &node->data.asm_expr.token_list;
|
|
assert(tok_list->length == 0);
|
|
|
|
AsmToken *cur_tok = nullptr;
|
|
|
|
enum State state = StateStart;
|
|
|
|
for (size_t i = 0; i < buf_len(asm_template); i += 1) {
|
|
uint8_t c = *((uint8_t*)buf_ptr(asm_template) + i);
|
|
switch (state) {
|
|
case StateStart:
|
|
if (c == '%') {
|
|
tok_list->add_one();
|
|
cur_tok = &tok_list->last();
|
|
cur_tok->id = AsmTokenIdPercent;
|
|
cur_tok->start = i;
|
|
state = StatePercent;
|
|
} else {
|
|
tok_list->add_one();
|
|
cur_tok = &tok_list->last();
|
|
cur_tok->id = AsmTokenIdTemplate;
|
|
cur_tok->start = i;
|
|
state = StateTemplate;
|
|
}
|
|
break;
|
|
case StatePercent:
|
|
if (c == '%') {
|
|
cur_tok->end = i;
|
|
state = StateStart;
|
|
} else if (c == '[') {
|
|
cur_tok->id = AsmTokenIdVar;
|
|
state = StateVar;
|
|
} else if (c == '=') {
|
|
cur_tok->id = AsmTokenIdUniqueId;
|
|
cur_tok->end = i;
|
|
state = StateStart;
|
|
} else {
|
|
ast_asm_error(pc, node, i, "expected a '%%' or '['");
|
|
}
|
|
break;
|
|
case StateTemplate:
|
|
if (c == '%') {
|
|
cur_tok->end = i;
|
|
i -= 1;
|
|
cur_tok = nullptr;
|
|
state = StateStart;
|
|
}
|
|
break;
|
|
case StateVar:
|
|
if (c == ']') {
|
|
cur_tok->end = i;
|
|
state = StateStart;
|
|
} else if ((c >= 'a' && c <= 'z') ||
|
|
(c >= '0' && c <= '9') ||
|
|
(c == '_'))
|
|
{
|
|
// do nothing
|
|
} else {
|
|
ast_asm_error(pc, node, i, "invalid substitution character: '%c'", c);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (state) {
|
|
case StateStart:
|
|
break;
|
|
case StatePercent:
|
|
case StateVar:
|
|
ast_asm_error(pc, node, buf_len(asm_template), "unexpected end of assembly template");
|
|
break;
|
|
case StateTemplate:
|
|
cur_tok->end = buf_len(asm_template);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static Buf *token_buf(Token *token) {
|
|
assert(token->id == TokenIdStringLiteral || token->id == TokenIdSymbol);
|
|
return &token->data.str_lit.str;
|
|
}
|
|
|
|
static BigInt *token_bigint(Token *token) {
|
|
assert(token->id == TokenIdIntLiteral);
|
|
return &token->data.int_lit.bigint;
|
|
}
|
|
|
|
static BigFloat *token_bigfloat(Token *token) {
|
|
assert(token->id == TokenIdFloatLiteral);
|
|
return &token->data.float_lit.bigfloat;
|
|
}
|
|
|
|
static uint8_t token_char_lit(Token *token) {
|
|
assert(token->id == TokenIdCharLiteral);
|
|
return token->data.char_lit.c;
|
|
}
|
|
|
|
static void ast_buf_from_token(ParseContext *pc, Token *token, Buf *buf) {
|
|
if (token->id == TokenIdSymbol) {
|
|
buf_init_from_buf(buf, token_buf(token));
|
|
} else {
|
|
buf_init_from_mem(buf, buf_ptr(pc->buf) + token->start_pos, token->end_pos - token->start_pos);
|
|
}
|
|
}
|
|
|
|
ATTRIBUTE_NORETURN
|
|
static void ast_invalid_token_error(ParseContext *pc, Token *token) {
|
|
Buf token_value = BUF_INIT;
|
|
ast_buf_from_token(pc, token, &token_value);
|
|
ast_error(pc, token, "invalid token: '%s'", buf_ptr(&token_value));
|
|
}
|
|
|
|
static AstNode *ast_parse_block_or_expression(ParseContext *pc, size_t *token_index, bool mandatory);
|
|
static AstNode *ast_parse_block_expr_or_expression(ParseContext *pc, size_t *token_index, bool mandatory);
|
|
static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool mandatory);
|
|
static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mandatory);
|
|
static AstNode *ast_parse_if_try_test_expr(ParseContext *pc, size_t *token_index, bool mandatory);
|
|
static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool mandatory);
|
|
static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory);
|
|
static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory);
|
|
static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod);
|
|
static AstNode *ast_parse_return_expr(ParseContext *pc, size_t *token_index);
|
|
static AstNode *ast_parse_grouped_expr(ParseContext *pc, size_t *token_index, bool mandatory);
|
|
static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, bool mandatory);
|
|
static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory);
|
|
static AstNode *ast_parse_try_expr(ParseContext *pc, size_t *token_index);
|
|
static AstNode *ast_parse_await_expr(ParseContext *pc, size_t *token_index);
|
|
static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index);
|
|
|
|
static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {
|
|
if (token->id == token_id) {
|
|
return;
|
|
}
|
|
|
|
Buf token_value = BUF_INIT;
|
|
ast_buf_from_token(pc, token, &token_value);
|
|
ast_error(pc, token, "expected token '%s', found '%s'", token_name(token_id), token_name(token->id));
|
|
}
|
|
|
|
static Token *ast_eat_token(ParseContext *pc, size_t *token_index, TokenId token_id) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
ast_expect_token(pc, token, token_id);
|
|
*token_index += 1;
|
|
return token;
|
|
}
|
|
|
|
/*
|
|
ErrorSetExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression
|
|
*/
|
|
static AstNode *ast_parse_error_set_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, mandatory);
|
|
if (!prefix_op_expr) {
|
|
return nullptr;
|
|
}
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id == TokenIdBang) {
|
|
*token_index += 1;
|
|
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
|
node->data.bin_op_expr.op1 = prefix_op_expr;
|
|
node->data.bin_op_expr.bin_op = BinOpTypeErrorUnion;
|
|
node->data.bin_op_expr.op2 = ast_parse_prefix_op_expr(pc, token_index, true);
|
|
return node;
|
|
} else {
|
|
return prefix_op_expr;
|
|
}
|
|
}
|
|
|
|
/*
|
|
TypeExpr = ErrorSetExpr
|
|
*/
|
|
static AstNode *ast_parse_type_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
return ast_parse_error_set_expr(pc, token_index, mandatory);
|
|
}
|
|
|
|
/*
|
|
ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "var" | "...")
|
|
*/
|
|
static AstNode *ast_parse_param_decl(ParseContext *pc, size_t *token_index) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeParamDecl, token);
|
|
|
|
if (token->id == TokenIdKeywordNoAlias) {
|
|
node->data.param_decl.is_noalias = true;
|
|
*token_index += 1;
|
|
token = &pc->tokens->at(*token_index);
|
|
} else if (token->id == TokenIdKeywordCompTime) {
|
|
node->data.param_decl.is_inline = true;
|
|
*token_index += 1;
|
|
token = &pc->tokens->at(*token_index);
|
|
}
|
|
|
|
node->data.param_decl.name = nullptr;
|
|
|
|
if (token->id == TokenIdSymbol) {
|
|
Token *next_token = &pc->tokens->at(*token_index + 1);
|
|
if (next_token->id == TokenIdColon) {
|
|
node->data.param_decl.name = token_buf(token);
|
|
*token_index += 2;
|
|
}
|
|
}
|
|
|
|
Token *ellipsis_tok = &pc->tokens->at(*token_index);
|
|
if (ellipsis_tok->id == TokenIdEllipsis3) {
|
|
*token_index += 1;
|
|
node->data.param_decl.is_var_args = true;
|
|
} else if (ellipsis_tok->id == TokenIdKeywordVar) {
|
|
*token_index += 1;
|
|
node->data.param_decl.var_token = ellipsis_tok;
|
|
} else {
|
|
node->data.param_decl.type = ast_parse_type_expr(pc, token_index, true);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static void ast_parse_param_decl_list(ParseContext *pc, size_t *token_index,
|
|
ZigList<AstNode *> *params, bool *is_var_args)
|
|
{
|
|
*is_var_args = false;
|
|
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
|
|
for (;;) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id == TokenIdRParen) {
|
|
*token_index += 1;
|
|
return;
|
|
}
|
|
|
|
AstNode *param_decl_node = ast_parse_param_decl(pc, token_index);
|
|
bool expect_end = false;
|
|
assert(param_decl_node);
|
|
params->append(param_decl_node);
|
|
expect_end = param_decl_node->data.param_decl.is_var_args;
|
|
*is_var_args = expect_end;
|
|
|
|
token = &pc->tokens->at(*token_index);
|
|
*token_index += 1;
|
|
if (token->id == TokenIdRParen) {
|
|
return;
|
|
} else {
|
|
ast_expect_token(pc, token, TokenIdComma);
|
|
if (expect_end) {
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static void ast_parse_fn_call_param_list(ParseContext *pc, size_t *token_index, ZigList<AstNode*> *params) {
|
|
for (;;) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id == TokenIdRParen) {
|
|
*token_index += 1;
|
|
return;
|
|
}
|
|
|
|
AstNode *expr = ast_parse_expression(pc, token_index, true);
|
|
params->append(expr);
|
|
|
|
token = &pc->tokens->at(*token_index);
|
|
*token_index += 1;
|
|
if (token->id == TokenIdRParen) {
|
|
return;
|
|
} else {
|
|
ast_expect_token(pc, token, TokenIdComma);
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
/*
|
|
GroupedExpression : token(LParen) Expression token(RParen)
|
|
*/
|
|
static AstNode *ast_parse_grouped_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *l_paren = &pc->tokens->at(*token_index);
|
|
if (l_paren->id != TokenIdLParen) {
|
|
if (mandatory) {
|
|
ast_expect_token(pc, l_paren, TokenIdLParen);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeGroupedExpr, l_paren);
|
|
|
|
node->data.grouped_expr = ast_parse_expression(pc, token_index, true);
|
|
|
|
Token *r_paren = &pc->tokens->at(*token_index);
|
|
*token_index += 1;
|
|
ast_expect_token(pc, r_paren, TokenIdRParen);
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr
|
|
*/
|
|
static AstNode *ast_parse_array_type_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *l_bracket = &pc->tokens->at(*token_index);
|
|
if (l_bracket->id != TokenIdLBracket) {
|
|
if (mandatory) {
|
|
ast_expect_token(pc, l_bracket, TokenIdLBracket);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeArrayType, l_bracket);
|
|
node->data.array_type.size = ast_parse_expression(pc, token_index, false);
|
|
|
|
ast_eat_token(pc, token_index, TokenIdRBracket);
|
|
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id == TokenIdKeywordAlign) {
|
|
*token_index += 1;
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
node->data.array_type.align_expr = ast_parse_expression(pc, token_index, true);
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
|
|
token = &pc->tokens->at(*token_index);
|
|
}
|
|
if (token->id == TokenIdKeywordConst) {
|
|
*token_index += 1;
|
|
node->data.array_type.is_const = true;
|
|
|
|
token = &pc->tokens->at(*token_index);
|
|
}
|
|
if (token->id == TokenIdKeywordVolatile) {
|
|
*token_index += 1;
|
|
node->data.array_type.is_volatile = true;
|
|
}
|
|
|
|
node->data.array_type.child_type = ast_parse_type_expr(pc, token_index, true);
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen)
|
|
*/
|
|
static void ast_parse_asm_input_item(ParseContext *pc, size_t *token_index, AstNode *node) {
|
|
ast_eat_token(pc, token_index, TokenIdLBracket);
|
|
Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol);
|
|
ast_eat_token(pc, token_index, TokenIdRBracket);
|
|
|
|
Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral);
|
|
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
AstNode *expr_node = ast_parse_expression(pc, token_index, true);
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
|
|
AsmInput *asm_input = allocate<AsmInput>(1);
|
|
asm_input->asm_symbolic_name = token_buf(alias);
|
|
asm_input->constraint = token_buf(constraint);
|
|
asm_input->expr = expr_node;
|
|
node->data.asm_expr.input_list.append(asm_input);
|
|
}
|
|
|
|
/*
|
|
AsmOutputItem : "[" "Symbol" "]" "String" "(" ("Symbol" | "->" PrefixOpExpression) ")"
|
|
*/
|
|
static void ast_parse_asm_output_item(ParseContext *pc, size_t *token_index, AstNode *node) {
|
|
ast_eat_token(pc, token_index, TokenIdLBracket);
|
|
Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol);
|
|
ast_eat_token(pc, token_index, TokenIdRBracket);
|
|
|
|
Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral);
|
|
|
|
AsmOutput *asm_output = allocate<AsmOutput>(1);
|
|
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
*token_index += 1;
|
|
if (token->id == TokenIdSymbol) {
|
|
asm_output->variable_name = token_buf(token);
|
|
} else if (token->id == TokenIdArrow) {
|
|
asm_output->return_type = ast_parse_type_expr(pc, token_index, true);
|
|
} else {
|
|
ast_invalid_token_error(pc, token);
|
|
}
|
|
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
|
|
asm_output->asm_symbolic_name = token_buf(alias);
|
|
asm_output->constraint = token_buf(constraint);
|
|
node->data.asm_expr.output_list.append(asm_output);
|
|
}
|
|
|
|
/*
|
|
AsmClobbers: token(Colon) list(token(String), token(Comma))
|
|
*/
|
|
static void ast_parse_asm_clobbers(ParseContext *pc, size_t *token_index, AstNode *node) {
|
|
Token *colon_tok = &pc->tokens->at(*token_index);
|
|
|
|
if (colon_tok->id != TokenIdColon)
|
|
return;
|
|
|
|
*token_index += 1;
|
|
|
|
for (;;) {
|
|
Token *string_tok = &pc->tokens->at(*token_index);
|
|
ast_expect_token(pc, string_tok, TokenIdStringLiteral);
|
|
*token_index += 1;
|
|
|
|
Buf *clobber_buf = token_buf(string_tok);
|
|
node->data.asm_expr.clobber_list.append(clobber_buf);
|
|
|
|
Token *comma = &pc->tokens->at(*token_index);
|
|
|
|
if (comma->id == TokenIdComma) {
|
|
*token_index += 1;
|
|
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id == TokenIdRParen) {
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers)
|
|
*/
|
|
static void ast_parse_asm_input(ParseContext *pc, size_t *token_index, AstNode *node) {
|
|
Token *colon_tok = &pc->tokens->at(*token_index);
|
|
|
|
if (colon_tok->id != TokenIdColon)
|
|
return;
|
|
|
|
*token_index += 1;
|
|
|
|
Token *colon_again = &pc->tokens->at(*token_index);
|
|
if (colon_again->id == TokenIdColon) {
|
|
ast_parse_asm_clobbers(pc, token_index, node);
|
|
return;
|
|
}
|
|
|
|
for (;;) {
|
|
ast_parse_asm_input_item(pc, token_index, node);
|
|
|
|
Token *comma = &pc->tokens->at(*token_index);
|
|
|
|
if (comma->id == TokenIdComma) {
|
|
*token_index += 1;
|
|
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id == TokenIdColon || token->id == TokenIdRParen) {
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ast_parse_asm_clobbers(pc, token_index, node);
|
|
}
|
|
|
|
/*
|
|
AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput)
|
|
*/
|
|
static void ast_parse_asm_output(ParseContext *pc, size_t *token_index, AstNode *node) {
|
|
Token *colon_tok = &pc->tokens->at(*token_index);
|
|
|
|
if (colon_tok->id != TokenIdColon)
|
|
return;
|
|
|
|
*token_index += 1;
|
|
|
|
Token *colon_again = &pc->tokens->at(*token_index);
|
|
if (colon_again->id == TokenIdColon) {
|
|
ast_parse_asm_input(pc, token_index, node);
|
|
return;
|
|
}
|
|
|
|
for (;;) {
|
|
ast_parse_asm_output_item(pc, token_index, node);
|
|
|
|
Token *comma = &pc->tokens->at(*token_index);
|
|
|
|
if (comma->id == TokenIdComma) {
|
|
*token_index += 1;
|
|
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id == TokenIdColon || token->id == TokenIdRParen) {
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ast_parse_asm_input(pc, token_index, node);
|
|
}
|
|
|
|
/*
|
|
AsmExpression : token(Asm) option(token(Volatile)) token(LParen) token(String) option(AsmOutput) token(RParen)
|
|
*/
|
|
static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *asm_token = &pc->tokens->at(*token_index);
|
|
|
|
if (asm_token->id != TokenIdKeywordAsm) {
|
|
if (mandatory) {
|
|
ast_expect_token(pc, asm_token, TokenIdKeywordAsm);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeAsmExpr, asm_token);
|
|
|
|
*token_index += 1;
|
|
Token *lparen_tok = &pc->tokens->at(*token_index);
|
|
|
|
if (lparen_tok->id == TokenIdKeywordVolatile) {
|
|
node->data.asm_expr.is_volatile = true;
|
|
|
|
*token_index += 1;
|
|
lparen_tok = &pc->tokens->at(*token_index);
|
|
}
|
|
|
|
ast_expect_token(pc, lparen_tok, TokenIdLParen);
|
|
*token_index += 1;
|
|
|
|
Token *template_tok = ast_eat_token(pc, token_index, TokenIdStringLiteral);
|
|
|
|
node->data.asm_expr.asm_template = token_buf(template_tok);
|
|
parse_asm_template(pc, node);
|
|
|
|
ast_parse_asm_output(pc, token_index, node);
|
|
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))
|
|
*/
|
|
static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
size_t orig_token_index = *token_index;
|
|
|
|
Token *name_token = nullptr;
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id == TokenIdSymbol) {
|
|
*token_index += 1;
|
|
Token *colon_token = &pc->tokens->at(*token_index);
|
|
if (colon_token->id == TokenIdColon) {
|
|
*token_index += 1;
|
|
name_token = token;
|
|
token = &pc->tokens->at(*token_index);
|
|
} else if (mandatory) {
|
|
ast_expect_token(pc, colon_token, TokenIdColon);
|
|
zig_unreachable();
|
|
} else {
|
|
*token_index = orig_token_index;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
Token *suspend_token = token;
|
|
if (suspend_token->id == TokenIdKeywordSuspend) {
|
|
*token_index += 1;
|
|
} else if (mandatory) {
|
|
ast_expect_token(pc, suspend_token, TokenIdKeywordSuspend);
|
|
zig_unreachable();
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
|
|
Token *bar_token = &pc->tokens->at(*token_index);
|
|
if (bar_token->id == TokenIdBinOr) {
|
|
*token_index += 1;
|
|
} else if (mandatory) {
|
|
ast_expect_token(pc, suspend_token, TokenIdBinOr);
|
|
zig_unreachable();
|
|
} else {
|
|
*token_index = orig_token_index;
|
|
return nullptr;
|
|
}
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
|
|
if (name_token != nullptr) {
|
|
node->data.suspend.name = token_buf(name_token);
|
|
}
|
|
node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);
|
|
ast_eat_token(pc, token_index, TokenIdBinOr);
|
|
node->data.suspend.block = ast_parse_block(pc, token_index, true);
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
CompTimeExpression(body) = "comptime" body
|
|
*/
|
|
static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, bool require_block_body, bool mandatory) {
|
|
Token *comptime_token = &pc->tokens->at(*token_index);
|
|
if (comptime_token->id == TokenIdKeywordCompTime) {
|
|
*token_index += 1;
|
|
} else if (mandatory) {
|
|
ast_expect_token(pc, comptime_token, TokenIdKeywordCompTime);
|
|
zig_unreachable();
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeCompTime, comptime_token);
|
|
if (require_block_body)
|
|
node->data.comptime_expr.expr = ast_parse_block(pc, token_index, true);
|
|
else
|
|
node->data.comptime_expr.expr = ast_parse_block_or_expression(pc, token_index, true);
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType
|
|
KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable" | "suspend"
|
|
ErrorSetDecl = "error" "{" list(Symbol, ",") "}"
|
|
*/
|
|
static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
if (token->id == TokenIdIntLiteral) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeIntLiteral, token);
|
|
node->data.int_literal.bigint = token_bigint(token);
|
|
*token_index += 1;
|
|
return node;
|
|
} else if (token->id == TokenIdFloatLiteral) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeFloatLiteral, token);
|
|
node->data.float_literal.bigfloat = token_bigfloat(token);
|
|
node->data.float_literal.overflow = token->data.float_lit.overflow;
|
|
*token_index += 1;
|
|
return node;
|
|
} else if (token->id == TokenIdStringLiteral) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeStringLiteral, token);
|
|
node->data.string_literal.buf = token_buf(token);
|
|
node->data.string_literal.c = token->data.str_lit.is_c_str;
|
|
*token_index += 1;
|
|
return node;
|
|
} else if (token->id == TokenIdCharLiteral) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeCharLiteral, token);
|
|
node->data.char_literal.value = token_char_lit(token);
|
|
*token_index += 1;
|
|
return node;
|
|
} else if (token->id == TokenIdKeywordTrue) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeBoolLiteral, token);
|
|
node->data.bool_literal.value = true;
|
|
*token_index += 1;
|
|
return node;
|
|
} else if (token->id == TokenIdKeywordFalse) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeBoolLiteral, token);
|
|
node->data.bool_literal.value = false;
|
|
*token_index += 1;
|
|
return node;
|
|
} else if (token->id == TokenIdKeywordNull) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeNullLiteral, token);
|
|
*token_index += 1;
|
|
return node;
|
|
} else if (token->id == TokenIdKeywordContinue) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeContinue, token);
|
|
*token_index += 1;
|
|
Token *maybe_colon_token = &pc->tokens->at(*token_index);
|
|
if (maybe_colon_token->id == TokenIdColon) {
|
|
*token_index += 1;
|
|
Token *name = ast_eat_token(pc, token_index, TokenIdSymbol);
|
|
node->data.continue_expr.name = token_buf(name);
|
|
}
|
|
return node;
|
|
} else if (token->id == TokenIdKeywordUndefined) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeUndefinedLiteral, token);
|
|
*token_index += 1;
|
|
return node;
|
|
} else if (token->id == TokenIdKeywordThis) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeThisLiteral, token);
|
|
*token_index += 1;
|
|
return node;
|
|
} else if (token->id == TokenIdKeywordUnreachable) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeUnreachable, token);
|
|
*token_index += 1;
|
|
return node;
|
|
} else if (token->id == TokenIdKeywordSuspend) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeSuspend, token);
|
|
*token_index += 1;
|
|
return node;
|
|
} else if (token->id == TokenIdKeywordPromise) {
|
|
AstNode *node = ast_create_node(pc, NodeTypePromiseType, token);
|
|
*token_index += 1;
|
|
Token *arrow_tok = &pc->tokens->at(*token_index);
|
|
if (arrow_tok->id == TokenIdArrow) {
|
|
*token_index += 1;
|
|
node->data.promise_type.payload_type = ast_parse_type_expr(pc, token_index, true);
|
|
}
|
|
return node;
|
|
} else if (token->id == TokenIdKeywordError) {
|
|
Token *next_token = &pc->tokens->at(*token_index + 1);
|
|
if (next_token->id == TokenIdLBrace) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeErrorSetDecl, token);
|
|
*token_index += 2;
|
|
for (;;) {
|
|
Token *item_tok = &pc->tokens->at(*token_index);
|
|
if (item_tok->id == TokenIdRBrace) {
|
|
*token_index += 1;
|
|
return node;
|
|
} else if (item_tok->id == TokenIdSymbol) {
|
|
AstNode *symbol_node = ast_parse_symbol(pc, token_index);
|
|
node->data.err_set_decl.decls.append(symbol_node);
|
|
Token *opt_comma_tok = &pc->tokens->at(*token_index);
|
|
if (opt_comma_tok->id == TokenIdComma) {
|
|
*token_index += 1;
|
|
}
|
|
} else {
|
|
ast_invalid_token_error(pc, item_tok);
|
|
}
|
|
}
|
|
} else {
|
|
AstNode *node = ast_create_node(pc, NodeTypeErrorType, token);
|
|
*token_index += 1;
|
|
return node;
|
|
}
|
|
} else if (token->id == TokenIdAtSign) {
|
|
*token_index += 1;
|
|
Token *name_tok = &pc->tokens->at(*token_index);
|
|
Buf *name_buf;
|
|
if (name_tok->id == TokenIdKeywordExport) {
|
|
name_buf = buf_create_from_str("export");
|
|
*token_index += 1;
|
|
} else if (name_tok->id == TokenIdSymbol) {
|
|
name_buf = token_buf(name_tok);
|
|
*token_index += 1;
|
|
} else {
|
|
ast_expect_token(pc, name_tok, TokenIdSymbol);
|
|
zig_unreachable();
|
|
}
|
|
|
|
AstNode *name_node = ast_create_node(pc, NodeTypeSymbol, name_tok);
|
|
name_node->data.symbol_expr.symbol = name_buf;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, token);
|
|
node->data.fn_call_expr.fn_ref_expr = name_node;
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params);
|
|
node->data.fn_call_expr.is_builtin = true;
|
|
|
|
return node;
|
|
}
|
|
|
|
AstNode *block_expr_node = ast_parse_block_expr(pc, token_index, false);
|
|
if (block_expr_node) {
|
|
return block_expr_node;
|
|
}
|
|
|
|
if (token->id == TokenIdSymbol) {
|
|
*token_index += 1;
|
|
AstNode *node = ast_create_node(pc, NodeTypeSymbol, token);
|
|
node->data.symbol_expr.symbol = token_buf(token);
|
|
return node;
|
|
}
|
|
|
|
AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);
|
|
if (grouped_expr_node) {
|
|
return grouped_expr_node;
|
|
}
|
|
|
|
AstNode *array_type_node = ast_parse_array_type_expr(pc, token_index, false);
|
|
if (array_type_node) {
|
|
return array_type_node;
|
|
}
|
|
|
|
AstNode *fn_proto_node = ast_parse_fn_proto(pc, token_index, false, VisibModPrivate);
|
|
if (fn_proto_node) {
|
|
return fn_proto_node;
|
|
}
|
|
|
|
AstNode *asm_expr = ast_parse_asm_expr(pc, token_index, false);
|
|
if (asm_expr) {
|
|
return asm_expr;
|
|
}
|
|
|
|
AstNode *container_decl = ast_parse_container_decl(pc, token_index, false);
|
|
if (container_decl)
|
|
return container_decl;
|
|
|
|
if (!mandatory)
|
|
return nullptr;
|
|
|
|
ast_invalid_token_error(pc, token);
|
|
}
|
|
|
|
/*
|
|
CurlySuffixExpression : PrefixOpExpression option(ContainerInitExpression)
|
|
ContainerInitExpression : token(LBrace) ContainerInitBody token(RBrace)
|
|
ContainerInitBody : list(StructLiteralField, token(Comma)) | list(Expression, token(Comma))
|
|
*/
|
|
static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, mandatory);
|
|
if (!prefix_op_expr) {
|
|
return nullptr;
|
|
}
|
|
|
|
while (true) {
|
|
Token *first_token = &pc->tokens->at(*token_index);
|
|
if (first_token->id == TokenIdLBrace) {
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeContainerInitExpr, first_token);
|
|
node->data.container_init_expr.type = prefix_op_expr;
|
|
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id == TokenIdDot) {
|
|
node->data.container_init_expr.kind = ContainerInitKindStruct;
|
|
for (;;) {
|
|
if (token->id == TokenIdDot) {
|
|
ast_eat_token(pc, token_index, TokenIdDot);
|
|
Token *field_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
|
|
ast_eat_token(pc, token_index, TokenIdEq);
|
|
|
|
AstNode *field_node = ast_create_node(pc, NodeTypeStructValueField, token);
|
|
|
|
field_node->data.struct_val_field.name = token_buf(field_name_tok);
|
|
field_node->data.struct_val_field.expr = ast_parse_expression(pc, token_index, true);
|
|
|
|
node->data.container_init_expr.entries.append(field_node);
|
|
|
|
Token *comma_tok = &pc->tokens->at(*token_index);
|
|
if (comma_tok->id == TokenIdComma) {
|
|
*token_index += 1;
|
|
token = &pc->tokens->at(*token_index);
|
|
continue;
|
|
} else if (comma_tok->id != TokenIdRBrace) {
|
|
ast_expect_token(pc, comma_tok, TokenIdRBrace);
|
|
} else {
|
|
*token_index += 1;
|
|
break;
|
|
}
|
|
} else if (token->id == TokenIdRBrace) {
|
|
*token_index += 1;
|
|
break;
|
|
} else {
|
|
ast_invalid_token_error(pc, token);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
node->data.container_init_expr.kind = ContainerInitKindArray;
|
|
for (;;) {
|
|
if (token->id == TokenIdRBrace) {
|
|
*token_index += 1;
|
|
break;
|
|
} else {
|
|
AstNode *elem_node = ast_parse_expression(pc, token_index, true);
|
|
node->data.container_init_expr.entries.append(elem_node);
|
|
|
|
Token *comma_tok = &pc->tokens->at(*token_index);
|
|
if (comma_tok->id == TokenIdComma) {
|
|
*token_index += 1;
|
|
token = &pc->tokens->at(*token_index);
|
|
continue;
|
|
} else if (comma_tok->id != TokenIdRBrace) {
|
|
ast_expect_token(pc, comma_tok, TokenIdRBrace);
|
|
} else {
|
|
*token_index += 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
prefix_op_expr = node;
|
|
} else {
|
|
return prefix_op_expr;
|
|
}
|
|
}
|
|
}
|
|
|
|
static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index, Token *fn_token,
|
|
AstNode *async_allocator_type_node, CallingConvention cc, bool is_extern, VisibMod visib_mod)
|
|
{
|
|
AstNode *node = ast_create_node(pc, NodeTypeFnProto, fn_token);
|
|
node->data.fn_proto.visib_mod = visib_mod;
|
|
node->data.fn_proto.cc = cc;
|
|
node->data.fn_proto.is_extern = is_extern;
|
|
node->data.fn_proto.async_allocator_type = async_allocator_type_node;
|
|
|
|
Token *fn_name = &pc->tokens->at(*token_index);
|
|
|
|
if (fn_name->id == TokenIdSymbol) {
|
|
*token_index += 1;
|
|
node->data.fn_proto.name = token_buf(fn_name);
|
|
} else {
|
|
node->data.fn_proto.name = nullptr;
|
|
}
|
|
|
|
ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args);
|
|
|
|
Token *next_token = &pc->tokens->at(*token_index);
|
|
if (next_token->id == TokenIdKeywordAlign) {
|
|
*token_index += 1;
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
|
|
node->data.fn_proto.align_expr = ast_parse_expression(pc, token_index, true);
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
next_token = &pc->tokens->at(*token_index);
|
|
}
|
|
if (next_token->id == TokenIdKeywordSection) {
|
|
*token_index += 1;
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
|
|
node->data.fn_proto.section_expr = ast_parse_expression(pc, token_index, true);
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
next_token = &pc->tokens->at(*token_index);
|
|
}
|
|
if (next_token->id == TokenIdKeywordVar) {
|
|
node->data.fn_proto.return_var_token = next_token;
|
|
*token_index += 1;
|
|
next_token = &pc->tokens->at(*token_index);
|
|
} else {
|
|
if (next_token->id == TokenIdKeywordError) {
|
|
Token *maybe_lbrace_tok = &pc->tokens->at(*token_index + 1);
|
|
if (maybe_lbrace_tok->id == TokenIdLBrace) {
|
|
*token_index += 1;
|
|
node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token);
|
|
return node;
|
|
}
|
|
} else if (next_token->id == TokenIdBang) {
|
|
*token_index += 1;
|
|
node->data.fn_proto.auto_err_set = true;
|
|
next_token = &pc->tokens->at(*token_index);
|
|
}
|
|
node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | PtrDerefExpression | SliceExpression)
|
|
FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
|
|
ArrayAccessExpression : token(LBracket) Expression token(RBracket)
|
|
SliceExpression = "[" Expression ".." option(Expression) "]"
|
|
FieldAccessExpression : token(Dot) token(Symbol)
|
|
PtrDerefExpression = ".*"
|
|
StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression
|
|
*/
|
|
static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *primary_expr;
|
|
|
|
Token *async_token = &pc->tokens->at(*token_index);
|
|
if (async_token->id == TokenIdKeywordAsync) {
|
|
*token_index += 1;
|
|
|
|
AstNode *allocator_expr_node = nullptr;
|
|
Token *async_lparen_tok = &pc->tokens->at(*token_index);
|
|
if (async_lparen_tok->id == TokenIdCmpLessThan) {
|
|
*token_index += 1;
|
|
allocator_expr_node = ast_parse_prefix_op_expr(pc, token_index, true);
|
|
ast_eat_token(pc, token_index, TokenIdCmpGreaterThan);
|
|
}
|
|
|
|
Token *fncall_token = &pc->tokens->at(*token_index);
|
|
if (fncall_token->id == TokenIdKeywordFn) {
|
|
*token_index += 1;
|
|
return ast_parse_fn_proto_partial(pc, token_index, fncall_token, allocator_expr_node, CallingConventionAsync,
|
|
false, VisibModPrivate);
|
|
}
|
|
AstNode *node = ast_parse_suffix_op_expr(pc, token_index, true);
|
|
if (node->type != NodeTypeFnCallExpr) {
|
|
ast_error(pc, fncall_token, "expected function call, found '%s'", token_name(fncall_token->id));
|
|
}
|
|
node->data.fn_call_expr.is_async = true;
|
|
node->data.fn_call_expr.async_allocator = allocator_expr_node;
|
|
assert(node->data.fn_call_expr.fn_ref_expr != nullptr);
|
|
|
|
primary_expr = node;
|
|
} else {
|
|
primary_expr = ast_parse_primary_expr(pc, token_index, mandatory);
|
|
if (!primary_expr)
|
|
return nullptr;
|
|
}
|
|
|
|
while (true) {
|
|
Token *first_token = &pc->tokens->at(*token_index);
|
|
if (first_token->id == TokenIdLParen) {
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, first_token);
|
|
node->data.fn_call_expr.fn_ref_expr = primary_expr;
|
|
ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params);
|
|
|
|
primary_expr = node;
|
|
} else if (first_token->id == TokenIdLBracket) {
|
|
*token_index += 1;
|
|
|
|
AstNode *expr_node = ast_parse_expression(pc, token_index, true);
|
|
|
|
Token *ellipsis_or_r_bracket = &pc->tokens->at(*token_index);
|
|
|
|
if (ellipsis_or_r_bracket->id == TokenIdEllipsis2) {
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeSliceExpr, first_token);
|
|
node->data.slice_expr.array_ref_expr = primary_expr;
|
|
node->data.slice_expr.start = expr_node;
|
|
node->data.slice_expr.end = ast_parse_expression(pc, token_index, false);
|
|
|
|
ast_eat_token(pc, token_index, TokenIdRBracket);
|
|
|
|
primary_expr = node;
|
|
} else if (ellipsis_or_r_bracket->id == TokenIdRBracket) {
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeArrayAccessExpr, first_token);
|
|
node->data.array_access_expr.array_ref_expr = primary_expr;
|
|
node->data.array_access_expr.subscript = expr_node;
|
|
|
|
primary_expr = node;
|
|
} else {
|
|
ast_invalid_token_error(pc, ellipsis_or_r_bracket);
|
|
}
|
|
} else if (first_token->id == TokenIdDot) {
|
|
*token_index += 1;
|
|
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
if (token->id == TokenIdSymbol) {
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, first_token);
|
|
node->data.field_access_expr.struct_expr = primary_expr;
|
|
node->data.field_access_expr.field_name = token_buf(token);
|
|
|
|
primary_expr = node;
|
|
} else if (token->id == TokenIdStar) {
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypePtrDeref, first_token);
|
|
node->data.ptr_deref_expr.target = primary_expr;
|
|
|
|
primary_expr = node;
|
|
} else {
|
|
ast_invalid_token_error(pc, token);
|
|
}
|
|
|
|
} else {
|
|
return primary_expr;
|
|
}
|
|
}
|
|
}
|
|
|
|
static PrefixOp tok_to_prefix_op(Token *token) {
|
|
switch (token->id) {
|
|
case TokenIdBang: return PrefixOpBoolNot;
|
|
case TokenIdDash: return PrefixOpNegation;
|
|
case TokenIdMinusPercent: return PrefixOpNegationWrap;
|
|
case TokenIdTilde: return PrefixOpBinNot;
|
|
case TokenIdMaybe: return PrefixOpMaybe;
|
|
case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe;
|
|
case TokenIdAmpersand: return PrefixOpAddrOf;
|
|
default: return PrefixOpInvalid;
|
|
}
|
|
}
|
|
|
|
static AstNode *ast_parse_pointer_type(ParseContext *pc, size_t *token_index, Token *star_tok) {
|
|
AstNode *node = ast_create_node(pc, NodeTypePointerType, star_tok);
|
|
node->data.pointer_type.star_token = star_tok;
|
|
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id == TokenIdKeywordAlign) {
|
|
*token_index += 1;
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
node->data.pointer_type.align_expr = ast_parse_expression(pc, token_index, true);
|
|
|
|
token = &pc->tokens->at(*token_index);
|
|
if (token->id == TokenIdColon) {
|
|
*token_index += 1;
|
|
Token *bit_offset_start_tok = ast_eat_token(pc, token_index, TokenIdIntLiteral);
|
|
ast_eat_token(pc, token_index, TokenIdColon);
|
|
Token *bit_offset_end_tok = ast_eat_token(pc, token_index, TokenIdIntLiteral);
|
|
|
|
node->data.pointer_type.bit_offset_start = token_bigint(bit_offset_start_tok);
|
|
node->data.pointer_type.bit_offset_end = token_bigint(bit_offset_end_tok);
|
|
}
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
token = &pc->tokens->at(*token_index);
|
|
}
|
|
if (token->id == TokenIdKeywordConst) {
|
|
*token_index += 1;
|
|
node->data.pointer_type.is_const = true;
|
|
|
|
token = &pc->tokens->at(*token_index);
|
|
}
|
|
if (token->id == TokenIdKeywordVolatile) {
|
|
*token_index += 1;
|
|
node->data.pointer_type.is_volatile = true;
|
|
}
|
|
|
|
node->data.pointer_type.op_expr = ast_parse_prefix_op_expr(pc, token_index, true);
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression
|
|
PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await"
|
|
*/
|
|
static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id == TokenIdStar || token->id == TokenIdBracketStarBracket) {
|
|
*token_index += 1;
|
|
return ast_parse_pointer_type(pc, token_index, token);
|
|
}
|
|
if (token->id == TokenIdStarStar) {
|
|
*token_index += 1;
|
|
AstNode *child_node = ast_parse_pointer_type(pc, token_index, token);
|
|
child_node->column += 1;
|
|
AstNode *parent_node = ast_create_node(pc, NodeTypePointerType, token);
|
|
parent_node->data.pointer_type.star_token = token;
|
|
parent_node->data.pointer_type.op_expr = child_node;
|
|
return parent_node;
|
|
}
|
|
if (token->id == TokenIdKeywordTry) {
|
|
return ast_parse_try_expr(pc, token_index);
|
|
}
|
|
if (token->id == TokenIdKeywordAwait) {
|
|
return ast_parse_await_expr(pc, token_index);
|
|
}
|
|
PrefixOp prefix_op = tok_to_prefix_op(token);
|
|
if (prefix_op == PrefixOpInvalid) {
|
|
return ast_parse_suffix_op_expr(pc, token_index, mandatory);
|
|
}
|
|
|
|
*token_index += 1;
|
|
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token);
|
|
|
|
AstNode *prefix_op_expr = ast_parse_error_set_expr(pc, token_index, true);
|
|
node->data.prefix_op_expr.primary_expr = prefix_op_expr;
|
|
node->data.prefix_op_expr.prefix_op = prefix_op;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static BinOpType tok_to_mult_op(Token *token) {
|
|
switch (token->id) {
|
|
case TokenIdStar: return BinOpTypeMult;
|
|
case TokenIdTimesPercent: return BinOpTypeMultWrap;
|
|
case TokenIdStarStar: return BinOpTypeArrayMult;
|
|
case TokenIdSlash: return BinOpTypeDiv;
|
|
case TokenIdPercent: return BinOpTypeMod;
|
|
case TokenIdBang: return BinOpTypeErrorUnion;
|
|
case TokenIdBarBar: return BinOpTypeMergeErrorSets;
|
|
default: return BinOpTypeInvalid;
|
|
}
|
|
}
|
|
|
|
/*
|
|
MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
|
|
*/
|
|
static BinOpType ast_parse_mult_op(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
BinOpType result = tok_to_mult_op(token);
|
|
if (result == BinOpTypeInvalid) {
|
|
if (mandatory) {
|
|
ast_invalid_token_error(pc, token);
|
|
} else {
|
|
return BinOpTypeInvalid;
|
|
}
|
|
}
|
|
*token_index += 1;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
MultiplyExpression : CurlySuffixExpression MultiplyOperator MultiplyExpression | CurlySuffixExpression
|
|
*/
|
|
static AstNode *ast_parse_mult_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *operand_1 = ast_parse_curly_suffix_expr(pc, token_index, mandatory);
|
|
if (!operand_1)
|
|
return nullptr;
|
|
|
|
while (true) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
BinOpType mult_op = ast_parse_mult_op(pc, token_index, false);
|
|
if (mult_op == BinOpTypeInvalid)
|
|
return operand_1;
|
|
|
|
AstNode *operand_2 = ast_parse_curly_suffix_expr(pc, token_index, true);
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
|
node->data.bin_op_expr.op1 = operand_1;
|
|
node->data.bin_op_expr.bin_op = mult_op;
|
|
node->data.bin_op_expr.op2 = operand_2;
|
|
|
|
operand_1 = node;
|
|
}
|
|
}
|
|
|
|
static BinOpType tok_to_add_op(Token *token) {
|
|
switch (token->id) {
|
|
case TokenIdPlus: return BinOpTypeAdd;
|
|
case TokenIdPlusPercent: return BinOpTypeAddWrap;
|
|
case TokenIdDash: return BinOpTypeSub;
|
|
case TokenIdMinusPercent: return BinOpTypeSubWrap;
|
|
case TokenIdPlusPlus: return BinOpTypeArrayCat;
|
|
default: return BinOpTypeInvalid;
|
|
}
|
|
}
|
|
|
|
/*
|
|
AdditionOperator = "+" | "-" | "++" | "+%" | "-%"
|
|
*/
|
|
static BinOpType ast_parse_add_op(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
BinOpType result = tok_to_add_op(token);
|
|
if (result == BinOpTypeInvalid) {
|
|
if (mandatory) {
|
|
ast_invalid_token_error(pc, token);
|
|
} else {
|
|
return BinOpTypeInvalid;
|
|
}
|
|
}
|
|
*token_index += 1;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
AdditionExpression : MultiplyExpression AdditionOperator AdditionExpression | MultiplyExpression
|
|
*/
|
|
static AstNode *ast_parse_add_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *operand_1 = ast_parse_mult_expr(pc, token_index, mandatory);
|
|
if (!operand_1)
|
|
return nullptr;
|
|
|
|
while (true) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
BinOpType add_op = ast_parse_add_op(pc, token_index, false);
|
|
if (add_op == BinOpTypeInvalid)
|
|
return operand_1;
|
|
|
|
AstNode *operand_2 = ast_parse_mult_expr(pc, token_index, true);
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
|
node->data.bin_op_expr.op1 = operand_1;
|
|
node->data.bin_op_expr.bin_op = add_op;
|
|
node->data.bin_op_expr.op2 = operand_2;
|
|
|
|
operand_1 = node;
|
|
}
|
|
}
|
|
|
|
static BinOpType tok_to_bit_shift_op(Token *token) {
|
|
switch (token->id) {
|
|
case TokenIdBitShiftLeft: return BinOpTypeBitShiftLeft;
|
|
case TokenIdBitShiftRight: return BinOpTypeBitShiftRight;
|
|
default: return BinOpTypeInvalid;
|
|
}
|
|
}
|
|
|
|
/*
|
|
BitShiftOperator = "<<" | ">>" | "<<%"
|
|
*/
|
|
static BinOpType ast_parse_bit_shift_op(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
BinOpType result = tok_to_bit_shift_op(token);
|
|
if (result == BinOpTypeInvalid) {
|
|
if (mandatory) {
|
|
ast_invalid_token_error(pc, token);
|
|
} else {
|
|
return BinOpTypeInvalid;
|
|
}
|
|
}
|
|
*token_index += 1;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
BitShiftExpression : AdditionExpression BitShiftOperator BitShiftExpression | AdditionExpression
|
|
*/
|
|
static AstNode *ast_parse_bit_shift_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *operand_1 = ast_parse_add_expr(pc, token_index, mandatory);
|
|
if (!operand_1)
|
|
return nullptr;
|
|
|
|
while (true) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
BinOpType bit_shift_op = ast_parse_bit_shift_op(pc, token_index, false);
|
|
if (bit_shift_op == BinOpTypeInvalid)
|
|
return operand_1;
|
|
|
|
AstNode *operand_2 = ast_parse_add_expr(pc, token_index, true);
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
|
node->data.bin_op_expr.op1 = operand_1;
|
|
node->data.bin_op_expr.bin_op = bit_shift_op;
|
|
node->data.bin_op_expr.op2 = operand_2;
|
|
|
|
operand_1 = node;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
BinaryAndExpression : BitShiftExpression token(Ampersand) BinaryAndExpression | BitShiftExpression
|
|
*/
|
|
static AstNode *ast_parse_bin_and_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *operand_1 = ast_parse_bit_shift_expr(pc, token_index, mandatory);
|
|
if (!operand_1)
|
|
return nullptr;
|
|
|
|
while (true) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id != TokenIdAmpersand)
|
|
return operand_1;
|
|
*token_index += 1;
|
|
|
|
AstNode *operand_2 = ast_parse_bit_shift_expr(pc, token_index, true);
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
|
node->data.bin_op_expr.op1 = operand_1;
|
|
node->data.bin_op_expr.bin_op = BinOpTypeBinAnd;
|
|
node->data.bin_op_expr.op2 = operand_2;
|
|
|
|
operand_1 = node;
|
|
}
|
|
}
|
|
|
|
/*
|
|
BinaryXorExpression : BinaryAndExpression token(BinXor) BinaryXorExpression | BinaryAndExpression
|
|
*/
|
|
static AstNode *ast_parse_bin_xor_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *operand_1 = ast_parse_bin_and_expr(pc, token_index, mandatory);
|
|
if (!operand_1)
|
|
return nullptr;
|
|
|
|
while (true) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id != TokenIdBinXor)
|
|
return operand_1;
|
|
*token_index += 1;
|
|
|
|
AstNode *operand_2 = ast_parse_bin_and_expr(pc, token_index, true);
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
|
node->data.bin_op_expr.op1 = operand_1;
|
|
node->data.bin_op_expr.bin_op = BinOpTypeBinXor;
|
|
node->data.bin_op_expr.op2 = operand_2;
|
|
|
|
operand_1 = node;
|
|
}
|
|
}
|
|
|
|
/*
|
|
BinaryOrExpression : BinaryXorExpression token(BinOr) BinaryOrExpression | BinaryXorExpression
|
|
*/
|
|
static AstNode *ast_parse_bin_or_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *operand_1 = ast_parse_bin_xor_expr(pc, token_index, mandatory);
|
|
if (!operand_1)
|
|
return nullptr;
|
|
|
|
while (true) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id != TokenIdBinOr)
|
|
return operand_1;
|
|
*token_index += 1;
|
|
|
|
AstNode *operand_2 = ast_parse_bin_xor_expr(pc, token_index, true);
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
|
node->data.bin_op_expr.op1 = operand_1;
|
|
node->data.bin_op_expr.bin_op = BinOpTypeBinOr;
|
|
node->data.bin_op_expr.op2 = operand_2;
|
|
|
|
operand_1 = node;
|
|
}
|
|
}
|
|
|
|
static BinOpType tok_to_cmp_op(Token *token) {
|
|
switch (token->id) {
|
|
case TokenIdCmpEq: return BinOpTypeCmpEq;
|
|
case TokenIdCmpNotEq: return BinOpTypeCmpNotEq;
|
|
case TokenIdCmpLessThan: return BinOpTypeCmpLessThan;
|
|
case TokenIdCmpGreaterThan: return BinOpTypeCmpGreaterThan;
|
|
case TokenIdCmpLessOrEq: return BinOpTypeCmpLessOrEq;
|
|
case TokenIdCmpGreaterOrEq: return BinOpTypeCmpGreaterOrEq;
|
|
default: return BinOpTypeInvalid;
|
|
}
|
|
}
|
|
|
|
static BinOpType ast_parse_comparison_operator(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
BinOpType result = tok_to_cmp_op(token);
|
|
if (result == BinOpTypeInvalid) {
|
|
if (mandatory) {
|
|
ast_invalid_token_error(pc, token);
|
|
} else {
|
|
return BinOpTypeInvalid;
|
|
}
|
|
}
|
|
*token_index += 1;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
ComparisonExpression : BinaryOrExpression ComparisonOperator BinaryOrExpression | BinaryOrExpression
|
|
*/
|
|
static AstNode *ast_parse_comparison_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *operand_1 = ast_parse_bin_or_expr(pc, token_index, mandatory);
|
|
if (!operand_1)
|
|
return nullptr;
|
|
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
BinOpType cmp_op = ast_parse_comparison_operator(pc, token_index, false);
|
|
if (cmp_op == BinOpTypeInvalid)
|
|
return operand_1;
|
|
|
|
AstNode *operand_2 = ast_parse_bin_or_expr(pc, token_index, true);
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
|
node->data.bin_op_expr.op1 = operand_1;
|
|
node->data.bin_op_expr.bin_op = cmp_op;
|
|
node->data.bin_op_expr.op2 = operand_2;
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
BoolAndExpression = ComparisonExpression "and" BoolAndExpression | ComparisonExpression
|
|
*/
|
|
static AstNode *ast_parse_bool_and_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *operand_1 = ast_parse_comparison_expr(pc, token_index, mandatory);
|
|
if (!operand_1)
|
|
return nullptr;
|
|
|
|
while (true) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id != TokenIdKeywordAnd)
|
|
return operand_1;
|
|
*token_index += 1;
|
|
|
|
AstNode *operand_2 = ast_parse_comparison_expr(pc, token_index, true);
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
|
node->data.bin_op_expr.op1 = operand_1;
|
|
node->data.bin_op_expr.bin_op = BinOpTypeBoolAnd;
|
|
node->data.bin_op_expr.op2 = operand_2;
|
|
|
|
operand_1 = node;
|
|
}
|
|
}
|
|
|
|
/*
|
|
IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
|
|
TryExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)
|
|
TestExpression(body) = "if" "(" Expression ")" "|" option("*") Symbol "|" body option("else" BlockExpression(body))
|
|
*/
|
|
static AstNode *ast_parse_if_try_test_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *if_token = &pc->tokens->at(*token_index);
|
|
|
|
if (if_token->id == TokenIdKeywordIf) {
|
|
*token_index += 1;
|
|
} else if (mandatory) {
|
|
ast_expect_token(pc, if_token, TokenIdKeywordIf);
|
|
zig_unreachable();
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
AstNode *condition = ast_parse_expression(pc, token_index, true);
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
|
|
Token *open_bar_tok = &pc->tokens->at(*token_index);
|
|
Token *var_name_tok = nullptr;
|
|
bool var_is_ptr = false;
|
|
if (open_bar_tok->id == TokenIdBinOr) {
|
|
*token_index += 1;
|
|
|
|
Token *star_tok = &pc->tokens->at(*token_index);
|
|
if (star_tok->id == TokenIdStar) {
|
|
*token_index += 1;
|
|
var_is_ptr = true;
|
|
}
|
|
|
|
var_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
|
|
|
|
ast_eat_token(pc, token_index, TokenIdBinOr);
|
|
}
|
|
|
|
AstNode *body_node = ast_parse_block_or_expression(pc, token_index, true);
|
|
|
|
Token *else_tok = &pc->tokens->at(*token_index);
|
|
AstNode *else_node = nullptr;
|
|
Token *err_name_tok = nullptr;
|
|
if (else_tok->id == TokenIdKeywordElse) {
|
|
*token_index += 1;
|
|
|
|
Token *else_bar_tok = &pc->tokens->at(*token_index);
|
|
if (else_bar_tok->id == TokenIdBinOr) {
|
|
*token_index += 1;
|
|
|
|
err_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
|
|
|
|
ast_eat_token(pc, token_index, TokenIdBinOr);
|
|
}
|
|
|
|
else_node = ast_parse_block_expr_or_expression(pc, token_index, true);
|
|
}
|
|
|
|
if (err_name_tok != nullptr) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeIfErrorExpr, if_token);
|
|
node->data.if_err_expr.target_node = condition;
|
|
node->data.if_err_expr.var_is_ptr = var_is_ptr;
|
|
if (var_name_tok != nullptr) {
|
|
node->data.if_err_expr.var_symbol = token_buf(var_name_tok);
|
|
}
|
|
node->data.if_err_expr.then_node = body_node;
|
|
node->data.if_err_expr.err_symbol = token_buf(err_name_tok);
|
|
node->data.if_err_expr.else_node = else_node;
|
|
return node;
|
|
} else if (var_name_tok != nullptr) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeTestExpr, if_token);
|
|
node->data.test_expr.target_node = condition;
|
|
node->data.test_expr.var_is_ptr = var_is_ptr;
|
|
node->data.test_expr.var_symbol = token_buf(var_name_tok);
|
|
node->data.test_expr.then_node = body_node;
|
|
node->data.test_expr.else_node = else_node;
|
|
return node;
|
|
} else {
|
|
AstNode *node = ast_create_node(pc, NodeTypeIfBoolExpr, if_token);
|
|
node->data.if_bool_expr.condition = condition;
|
|
node->data.if_bool_expr.then_block = body_node;
|
|
node->data.if_bool_expr.else_node = else_node;
|
|
return node;
|
|
}
|
|
}
|
|
|
|
/*
|
|
ReturnExpression : "return" option(Expression)
|
|
*/
|
|
static AstNode *ast_parse_return_expr(ParseContext *pc, size_t *token_index) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
if (token->id != TokenIdKeywordReturn) {
|
|
return nullptr;
|
|
}
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, token);
|
|
node->data.return_expr.kind = ReturnKindUnconditional;
|
|
node->data.return_expr.expr = ast_parse_expression(pc, token_index, false);
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
TryExpression : "try" Expression
|
|
*/
|
|
static AstNode *ast_parse_try_expr(ParseContext *pc, size_t *token_index) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
if (token->id != TokenIdKeywordTry) {
|
|
return nullptr;
|
|
}
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, token);
|
|
node->data.return_expr.kind = ReturnKindError;
|
|
node->data.return_expr.expr = ast_parse_expression(pc, token_index, true);
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
AwaitExpression : "await" Expression
|
|
*/
|
|
static AstNode *ast_parse_await_expr(ParseContext *pc, size_t *token_index) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
if (token->id != TokenIdKeywordAwait) {
|
|
return nullptr;
|
|
}
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeAwaitExpr, token);
|
|
node->data.await_expr.expr = ast_parse_expression(pc, token_index, true);
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
BreakExpression = "break" option(":" Symbol) option(Expression)
|
|
*/
|
|
static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
if (token->id == TokenIdKeywordBreak) {
|
|
*token_index += 1;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
AstNode *node = ast_create_node(pc, NodeTypeBreak, token);
|
|
|
|
Token *maybe_colon_token = &pc->tokens->at(*token_index);
|
|
if (maybe_colon_token->id == TokenIdColon) {
|
|
*token_index += 1;
|
|
Token *name = ast_eat_token(pc, token_index, TokenIdSymbol);
|
|
node->data.break_expr.name = token_buf(name);
|
|
}
|
|
|
|
node->data.break_expr.expr = ast_parse_expression(pc, token_index, false);
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
CancelExpression = "cancel" Expression;
|
|
*/
|
|
static AstNode *ast_parse_cancel_expr(ParseContext *pc, size_t *token_index) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
if (token->id != TokenIdKeywordCancel) {
|
|
return nullptr;
|
|
}
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeCancel, token);
|
|
|
|
node->data.cancel_expr.expr = ast_parse_expression(pc, token_index, false);
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
ResumeExpression = "resume" Expression;
|
|
*/
|
|
static AstNode *ast_parse_resume_expr(ParseContext *pc, size_t *token_index) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
if (token->id != TokenIdKeywordResume) {
|
|
return nullptr;
|
|
}
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeResume, token);
|
|
|
|
node->data.resume_expr.expr = ast_parse_expression(pc, token_index, false);
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
Defer(body) = ("defer" | "errdefer") body
|
|
*/
|
|
static AstNode *ast_parse_defer_expr(ParseContext *pc, size_t *token_index) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
NodeType node_type;
|
|
ReturnKind kind;
|
|
|
|
if (token->id == TokenIdKeywordErrdefer) {
|
|
kind = ReturnKindError;
|
|
node_type = NodeTypeDefer;
|
|
*token_index += 1;
|
|
} else if (token->id == TokenIdKeywordDefer) {
|
|
kind = ReturnKindUnconditional;
|
|
node_type = NodeTypeDefer;
|
|
*token_index += 1;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
|
|
AstNode *node = ast_create_node(pc, node_type, token);
|
|
node->data.defer.kind = kind;
|
|
node->data.defer.expr = ast_parse_block_or_expression(pc, token_index, true);
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
VariableDeclaration = ("var" | "const") Symbol option(":" TypeExpr) option("align" "(" Expression ")") "=" Expression
|
|
*/
|
|
static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *token_index, bool mandatory,
|
|
VisibMod visib_mod, bool is_comptime, bool is_export)
|
|
{
|
|
Token *first_token = &pc->tokens->at(*token_index);
|
|
Token *var_token;
|
|
|
|
bool is_const;
|
|
if (first_token->id == TokenIdKeywordVar) {
|
|
is_const = false;
|
|
var_token = first_token;
|
|
*token_index += 1;
|
|
} else if (first_token->id == TokenIdKeywordConst) {
|
|
is_const = true;
|
|
var_token = first_token;
|
|
*token_index += 1;
|
|
} else if (mandatory) {
|
|
ast_invalid_token_error(pc, first_token);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, var_token);
|
|
|
|
node->data.variable_declaration.is_comptime = is_comptime;
|
|
node->data.variable_declaration.is_export = is_export;
|
|
node->data.variable_declaration.is_const = is_const;
|
|
node->data.variable_declaration.visib_mod = visib_mod;
|
|
|
|
Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol);
|
|
node->data.variable_declaration.symbol = token_buf(name_token);
|
|
|
|
Token *next_token = &pc->tokens->at(*token_index);
|
|
|
|
if (next_token->id == TokenIdColon) {
|
|
*token_index += 1;
|
|
node->data.variable_declaration.type = ast_parse_type_expr(pc, token_index, true);
|
|
next_token = &pc->tokens->at(*token_index);
|
|
}
|
|
|
|
if (next_token->id == TokenIdKeywordAlign) {
|
|
*token_index += 1;
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
node->data.variable_declaration.align_expr = ast_parse_expression(pc, token_index, true);
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
next_token = &pc->tokens->at(*token_index);
|
|
}
|
|
|
|
if (next_token->id == TokenIdKeywordSection) {
|
|
*token_index += 1;
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
node->data.variable_declaration.section_expr = ast_parse_expression(pc, token_index, true);
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
next_token = &pc->tokens->at(*token_index);
|
|
}
|
|
|
|
if (next_token->id == TokenIdEq) {
|
|
*token_index += 1;
|
|
node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true);
|
|
next_token = &pc->tokens->at(*token_index);
|
|
}
|
|
|
|
// peek ahead and ensure that all variable declarations are followed by a semicolon
|
|
ast_expect_token(pc, next_token, TokenIdSemicolon);
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
GlobalVarDecl = option("export") VariableDeclaration ";"
|
|
*/
|
|
static AstNode *ast_parse_global_var_decl(ParseContext *pc, size_t *token_index, VisibMod visib_mod) {
|
|
Token *first_token = &pc->tokens->at(*token_index);
|
|
|
|
bool is_export = false;;
|
|
if (first_token->id == TokenIdKeywordExport) {
|
|
*token_index += 1;
|
|
is_export = true;
|
|
}
|
|
|
|
AstNode *node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod, false, is_export);
|
|
if (node == nullptr) {
|
|
if (is_export) {
|
|
*token_index -= 1;
|
|
}
|
|
return nullptr;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
LocalVarDecl = option("comptime") VariableDeclaration
|
|
*/
|
|
static AstNode *ast_parse_local_var_decl(ParseContext *pc, size_t *token_index) {
|
|
Token *first_token = &pc->tokens->at(*token_index);
|
|
|
|
bool is_comptime = false;;
|
|
if (first_token->id == TokenIdKeywordCompTime) {
|
|
*token_index += 1;
|
|
is_comptime = true;
|
|
}
|
|
|
|
AstNode *node = ast_parse_variable_declaration_expr(pc, token_index, false, VisibModPrivate, is_comptime, false);
|
|
if (node == nullptr) {
|
|
if (is_comptime) {
|
|
*token_index -= 1;
|
|
}
|
|
return nullptr;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression
|
|
*/
|
|
static AstNode *ast_parse_bool_or_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *operand_1 = ast_parse_bool_and_expr(pc, token_index, mandatory);
|
|
if (!operand_1)
|
|
return nullptr;
|
|
|
|
while (true) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id != TokenIdKeywordOr)
|
|
return operand_1;
|
|
*token_index += 1;
|
|
|
|
AstNode *operand_2 = ast_parse_bool_and_expr(pc, token_index, true);
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
|
node->data.bin_op_expr.op1 = operand_1;
|
|
node->data.bin_op_expr.bin_op = BinOpTypeBoolOr;
|
|
node->data.bin_op_expr.op2 = operand_2;
|
|
|
|
operand_1 = node;
|
|
}
|
|
}
|
|
|
|
/*
|
|
WhileExpression(body) = option(Symbol ":") option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
|
|
*/
|
|
static AstNode *ast_parse_while_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
size_t orig_token_index = *token_index;
|
|
|
|
Token *name_token = nullptr;
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
if (token->id == TokenIdSymbol) {
|
|
*token_index += 1;
|
|
Token *colon_token = &pc->tokens->at(*token_index);
|
|
if (colon_token->id == TokenIdColon) {
|
|
*token_index += 1;
|
|
name_token = token;
|
|
token = &pc->tokens->at(*token_index);
|
|
} else if (mandatory) {
|
|
ast_expect_token(pc, colon_token, TokenIdColon);
|
|
zig_unreachable();
|
|
} else {
|
|
*token_index = orig_token_index;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
bool is_inline = false;
|
|
if (token->id == TokenIdKeywordInline) {
|
|
is_inline = true;
|
|
*token_index += 1;
|
|
token = &pc->tokens->at(*token_index);
|
|
}
|
|
|
|
Token *while_token;
|
|
if (token->id == TokenIdKeywordWhile) {
|
|
while_token = token;
|
|
*token_index += 1;
|
|
} else if (mandatory) {
|
|
ast_expect_token(pc, token, TokenIdKeywordWhile);
|
|
zig_unreachable();
|
|
} else {
|
|
*token_index = orig_token_index;
|
|
return nullptr;
|
|
}
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeWhileExpr, while_token);
|
|
if (name_token != nullptr) {
|
|
node->data.while_expr.name = token_buf(name_token);
|
|
}
|
|
node->data.while_expr.is_inline = is_inline;
|
|
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
node->data.while_expr.condition = ast_parse_expression(pc, token_index, true);
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
|
|
Token *open_bar_tok = &pc->tokens->at(*token_index);
|
|
if (open_bar_tok->id == TokenIdBinOr) {
|
|
*token_index += 1;
|
|
|
|
Token *star_tok = &pc->tokens->at(*token_index);
|
|
if (star_tok->id == TokenIdStar) {
|
|
*token_index += 1;
|
|
node->data.while_expr.var_is_ptr = true;
|
|
}
|
|
|
|
Token *var_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
|
|
node->data.while_expr.var_symbol = token_buf(var_name_tok);
|
|
ast_eat_token(pc, token_index, TokenIdBinOr);
|
|
}
|
|
|
|
Token *colon_tok = &pc->tokens->at(*token_index);
|
|
if (colon_tok->id == TokenIdColon) {
|
|
*token_index += 1;
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
node->data.while_expr.continue_expr = ast_parse_expression(pc, token_index, true);
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
}
|
|
|
|
node->data.while_expr.body = ast_parse_block_or_expression(pc, token_index, true);
|
|
|
|
Token *else_tok = &pc->tokens->at(*token_index);
|
|
if (else_tok->id == TokenIdKeywordElse) {
|
|
*token_index += 1;
|
|
|
|
Token *else_bar_tok = &pc->tokens->at(*token_index);
|
|
if (else_bar_tok->id == TokenIdBinOr) {
|
|
*token_index += 1;
|
|
|
|
Token *err_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
|
|
node->data.while_expr.err_symbol = token_buf(err_name_tok);
|
|
|
|
ast_eat_token(pc, token_index, TokenIdBinOr);
|
|
}
|
|
|
|
node->data.while_expr.else_node = ast_parse_block_or_expression(pc, token_index, true);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index) {
|
|
Token *token = ast_eat_token(pc, token_index, TokenIdSymbol);
|
|
AstNode *node = ast_create_node(pc, NodeTypeSymbol, token);
|
|
node->data.symbol_expr.symbol = token_buf(token);
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
|
|
*/
|
|
static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
size_t orig_token_index = *token_index;
|
|
|
|
Token *name_token = nullptr;
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
if (token->id == TokenIdSymbol) {
|
|
*token_index += 1;
|
|
Token *colon_token = &pc->tokens->at(*token_index);
|
|
if (colon_token->id == TokenIdColon) {
|
|
*token_index += 1;
|
|
name_token = token;
|
|
token = &pc->tokens->at(*token_index);
|
|
} else if (mandatory) {
|
|
ast_expect_token(pc, colon_token, TokenIdColon);
|
|
zig_unreachable();
|
|
} else {
|
|
*token_index = orig_token_index;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
bool is_inline = false;
|
|
if (token->id == TokenIdKeywordInline) {
|
|
is_inline = true;
|
|
*token_index += 1;
|
|
token = &pc->tokens->at(*token_index);
|
|
}
|
|
|
|
Token *for_token;
|
|
if (token->id == TokenIdKeywordFor) {
|
|
for_token = token;
|
|
*token_index += 1;
|
|
} else if (mandatory) {
|
|
ast_expect_token(pc, token, TokenIdKeywordFor);
|
|
zig_unreachable();
|
|
} else {
|
|
*token_index = orig_token_index;
|
|
return nullptr;
|
|
}
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeForExpr, for_token);
|
|
if (name_token != nullptr) {
|
|
node->data.for_expr.name = token_buf(name_token);
|
|
}
|
|
node->data.for_expr.is_inline = is_inline;
|
|
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
node->data.for_expr.array_expr = ast_parse_expression(pc, token_index, true);
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
|
|
Token *maybe_bar = &pc->tokens->at(*token_index);
|
|
if (maybe_bar->id == TokenIdBinOr) {
|
|
*token_index += 1;
|
|
|
|
Token *maybe_star = &pc->tokens->at(*token_index);
|
|
if (maybe_star->id == TokenIdStar) {
|
|
*token_index += 1;
|
|
node->data.for_expr.elem_is_ptr = true;
|
|
}
|
|
|
|
node->data.for_expr.elem_node = ast_parse_symbol(pc, token_index);
|
|
|
|
Token *maybe_comma = &pc->tokens->at(*token_index);
|
|
if (maybe_comma->id == TokenIdComma) {
|
|
*token_index += 1;
|
|
|
|
node->data.for_expr.index_node = ast_parse_symbol(pc, token_index);
|
|
}
|
|
|
|
ast_eat_token(pc, token_index, TokenIdBinOr);
|
|
}
|
|
|
|
node->data.for_expr.body = ast_parse_block_or_expression(pc, token_index, true);
|
|
|
|
Token *else_tok = &pc->tokens->at(*token_index);
|
|
if (else_tok->id == TokenIdKeywordElse) {
|
|
*token_index += 1;
|
|
|
|
node->data.for_expr.else_node = ast_parse_block_or_expression(pc, token_index, true);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
SwitchExpression = "switch" "(" Expression ")" "{" many(SwitchProng) "}"
|
|
SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression ","
|
|
SwitchItem = Expression | (Expression "..." Expression)
|
|
*/
|
|
static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *switch_token = &pc->tokens->at(*token_index);
|
|
if (switch_token->id == TokenIdKeywordSwitch) {
|
|
*token_index += 1;
|
|
} else if (mandatory) {
|
|
ast_expect_token(pc, switch_token, TokenIdKeywordSwitch);
|
|
zig_unreachable();
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeSwitchExpr, switch_token);
|
|
|
|
ast_eat_token(pc, token_index, TokenIdLParen);
|
|
node->data.switch_expr.expr = ast_parse_expression(pc, token_index, true);
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
ast_eat_token(pc, token_index, TokenIdLBrace);
|
|
|
|
for (;;) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
if (token->id == TokenIdRBrace) {
|
|
*token_index += 1;
|
|
|
|
return node;
|
|
}
|
|
|
|
AstNode *prong_node = ast_create_node(pc, NodeTypeSwitchProng, token);
|
|
node->data.switch_expr.prongs.append(prong_node);
|
|
|
|
if (token->id == TokenIdKeywordElse) {
|
|
*token_index += 1;
|
|
} else for (;;) {
|
|
AstNode *expr1 = ast_parse_expression(pc, token_index, true);
|
|
Token *ellipsis_tok = &pc->tokens->at(*token_index);
|
|
if (ellipsis_tok->id == TokenIdEllipsis3) {
|
|
*token_index += 1;
|
|
|
|
AstNode *range_node = ast_create_node(pc, NodeTypeSwitchRange, ellipsis_tok);
|
|
prong_node->data.switch_prong.items.append(range_node);
|
|
|
|
range_node->data.switch_range.start = expr1;
|
|
range_node->data.switch_range.end = ast_parse_expression(pc, token_index, true);
|
|
|
|
prong_node->data.switch_prong.any_items_are_range = true;
|
|
} else {
|
|
prong_node->data.switch_prong.items.append(expr1);
|
|
}
|
|
Token *comma_tok = &pc->tokens->at(*token_index);
|
|
if (comma_tok->id == TokenIdComma) {
|
|
*token_index += 1;
|
|
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
if (token->id == TokenIdFatArrow) {
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
ast_eat_token(pc, token_index, TokenIdFatArrow);
|
|
|
|
Token *maybe_bar = &pc->tokens->at(*token_index);
|
|
if (maybe_bar->id == TokenIdBinOr) {
|
|
*token_index += 1;
|
|
|
|
Token *star_or_symbol = &pc->tokens->at(*token_index);
|
|
AstNode *var_symbol_node;
|
|
bool var_is_ptr;
|
|
if (star_or_symbol->id == TokenIdStar) {
|
|
*token_index += 1;
|
|
var_is_ptr = true;
|
|
var_symbol_node = ast_parse_symbol(pc, token_index);
|
|
} else {
|
|
var_is_ptr = false;
|
|
var_symbol_node = ast_parse_symbol(pc, token_index);
|
|
}
|
|
|
|
prong_node->data.switch_prong.var_symbol = var_symbol_node;
|
|
prong_node->data.switch_prong.var_is_ptr = var_is_ptr;
|
|
ast_eat_token(pc, token_index, TokenIdBinOr);
|
|
}
|
|
|
|
prong_node->data.switch_prong.expr = ast_parse_expression(pc, token_index, true);
|
|
|
|
Token *trailing_token = &pc->tokens->at(*token_index);
|
|
if (trailing_token->id == TokenIdRBrace) {
|
|
*token_index += 1;
|
|
|
|
return node;
|
|
} else {
|
|
ast_eat_token(pc, token_index, TokenIdComma);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
BlockExpression(body) = Block | IfExpression(body) | IfErrorExpression(body) | TestExpression(body) | WhileExpression(body) | ForExpression(body) | SwitchExpression | CompTimeExpression(body) | SuspendExpression(body)
|
|
*/
|
|
static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
AstNode *if_expr = ast_parse_if_try_test_expr(pc, token_index, false);
|
|
if (if_expr)
|
|
return if_expr;
|
|
|
|
AstNode *while_expr = ast_parse_while_expr(pc, token_index, false);
|
|
if (while_expr)
|
|
return while_expr;
|
|
|
|
AstNode *for_expr = ast_parse_for_expr(pc, token_index, false);
|
|
if (for_expr)
|
|
return for_expr;
|
|
|
|
AstNode *switch_expr = ast_parse_switch_expr(pc, token_index, false);
|
|
if (switch_expr)
|
|
return switch_expr;
|
|
|
|
AstNode *block = ast_parse_block(pc, token_index, false);
|
|
if (block)
|
|
return block;
|
|
|
|
AstNode *comptime_node = ast_parse_comptime_expr(pc, token_index, false, false);
|
|
if (comptime_node)
|
|
return comptime_node;
|
|
|
|
AstNode *suspend_node = ast_parse_suspend_block(pc, token_index, false);
|
|
if (suspend_node)
|
|
return suspend_node;
|
|
|
|
if (mandatory)
|
|
ast_invalid_token_error(pc, token);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static BinOpType tok_to_ass_op(Token *token) {
|
|
switch (token->id) {
|
|
case TokenIdEq: return BinOpTypeAssign;
|
|
case TokenIdTimesEq: return BinOpTypeAssignTimes;
|
|
case TokenIdTimesPercentEq: return BinOpTypeAssignTimesWrap;
|
|
case TokenIdDivEq: return BinOpTypeAssignDiv;
|
|
case TokenIdModEq: return BinOpTypeAssignMod;
|
|
case TokenIdPlusEq: return BinOpTypeAssignPlus;
|
|
case TokenIdPlusPercentEq: return BinOpTypeAssignPlusWrap;
|
|
case TokenIdMinusEq: return BinOpTypeAssignMinus;
|
|
case TokenIdMinusPercentEq: return BinOpTypeAssignMinusWrap;
|
|
case TokenIdBitShiftLeftEq: return BinOpTypeAssignBitShiftLeft;
|
|
case TokenIdBitShiftRightEq: return BinOpTypeAssignBitShiftRight;
|
|
case TokenIdBitAndEq: return BinOpTypeAssignBitAnd;
|
|
case TokenIdBitXorEq: return BinOpTypeAssignBitXor;
|
|
case TokenIdBitOrEq: return BinOpTypeAssignBitOr;
|
|
default: return BinOpTypeInvalid;
|
|
}
|
|
}
|
|
|
|
/*
|
|
AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "*%=" | "+%=" | "-%=" | "<<%="
|
|
*/
|
|
static BinOpType ast_parse_ass_op(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
BinOpType result = tok_to_ass_op(token);
|
|
if (result == BinOpTypeInvalid) {
|
|
if (mandatory) {
|
|
ast_invalid_token_error(pc, token);
|
|
} else {
|
|
return BinOpTypeInvalid;
|
|
}
|
|
}
|
|
*token_index += 1;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
UnwrapExpression : BoolOrExpression (UnwrapMaybe | UnwrapError) | BoolOrExpression
|
|
UnwrapMaybe : "??" BoolOrExpression
|
|
UnwrapError = "catch" option("|" Symbol "|") Expression
|
|
*/
|
|
static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory);
|
|
if (!lhs)
|
|
return nullptr;
|
|
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
if (token->id == TokenIdDoubleQuestion) {
|
|
*token_index += 1;
|
|
|
|
AstNode *rhs = ast_parse_expression(pc, token_index, true);
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
|
node->data.bin_op_expr.op1 = lhs;
|
|
node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe;
|
|
node->data.bin_op_expr.op2 = rhs;
|
|
|
|
return node;
|
|
} else if (token->id == TokenIdKeywordCatch) {
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeUnwrapErrorExpr, token);
|
|
node->data.unwrap_err_expr.op1 = lhs;
|
|
|
|
Token *maybe_bar_tok = &pc->tokens->at(*token_index);
|
|
if (maybe_bar_tok->id == TokenIdBinOr) {
|
|
*token_index += 1;
|
|
node->data.unwrap_err_expr.symbol = ast_parse_symbol(pc, token_index);
|
|
ast_eat_token(pc, token_index, TokenIdBinOr);
|
|
}
|
|
node->data.unwrap_err_expr.op2 = ast_parse_expression(pc, token_index, true);
|
|
|
|
return node;
|
|
} else {
|
|
return lhs;
|
|
}
|
|
}
|
|
|
|
/*
|
|
AssignmentExpression : UnwrapExpression AssignmentOperator UnwrapExpression | UnwrapExpression
|
|
*/
|
|
static AstNode *ast_parse_ass_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *lhs = ast_parse_unwrap_expr(pc, token_index, mandatory);
|
|
if (!lhs)
|
|
return nullptr;
|
|
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
BinOpType ass_op = ast_parse_ass_op(pc, token_index, false);
|
|
if (ass_op == BinOpTypeInvalid)
|
|
return lhs;
|
|
|
|
AstNode *rhs = ast_parse_unwrap_expr(pc, token_index, true);
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
|
|
node->data.bin_op_expr.op1 = lhs;
|
|
node->data.bin_op_expr.bin_op = ass_op;
|
|
node->data.bin_op_expr.op2 = rhs;
|
|
|
|
return node;
|
|
}
|
|
|
|
static AstNode *ast_parse_block_expr_or_expression(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *block_expr = ast_parse_block_expr(pc, token_index, false);
|
|
if (block_expr)
|
|
return block_expr;
|
|
|
|
return ast_parse_expression(pc, token_index, mandatory);
|
|
}
|
|
|
|
/*
|
|
BlockOrExpression = Block | Expression
|
|
*/
|
|
static AstNode *ast_parse_block_or_expression(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
AstNode *block_expr = ast_parse_block(pc, token_index, false);
|
|
if (block_expr)
|
|
return block_expr;
|
|
|
|
return ast_parse_expression(pc, token_index, mandatory);
|
|
}
|
|
|
|
/*
|
|
Expression = TryExpression | ReturnExpression | BreakExpression | AssignmentExpression | CancelExpression | ResumeExpression
|
|
*/
|
|
static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
AstNode *return_expr = ast_parse_return_expr(pc, token_index);
|
|
if (return_expr)
|
|
return return_expr;
|
|
|
|
AstNode *try_expr = ast_parse_try_expr(pc, token_index);
|
|
if (try_expr)
|
|
return try_expr;
|
|
|
|
AstNode *break_expr = ast_parse_break_expr(pc, token_index);
|
|
if (break_expr)
|
|
return break_expr;
|
|
|
|
AstNode *cancel_expr = ast_parse_cancel_expr(pc, token_index);
|
|
if (cancel_expr)
|
|
return cancel_expr;
|
|
|
|
AstNode *resume_expr = ast_parse_resume_expr(pc, token_index);
|
|
if (resume_expr)
|
|
return resume_expr;
|
|
|
|
AstNode *ass_expr = ast_parse_ass_expr(pc, token_index, false);
|
|
if (ass_expr)
|
|
return ass_expr;
|
|
|
|
if (mandatory)
|
|
ast_invalid_token_error(pc, token);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool statement_terminates_without_semicolon(AstNode *node) {
|
|
switch (node->type) {
|
|
case NodeTypeIfBoolExpr:
|
|
if (node->data.if_bool_expr.else_node)
|
|
return statement_terminates_without_semicolon(node->data.if_bool_expr.else_node);
|
|
return node->data.if_bool_expr.then_block->type == NodeTypeBlock;
|
|
case NodeTypeIfErrorExpr:
|
|
if (node->data.if_err_expr.else_node)
|
|
return statement_terminates_without_semicolon(node->data.if_err_expr.else_node);
|
|
return node->data.if_err_expr.then_node->type == NodeTypeBlock;
|
|
case NodeTypeTestExpr:
|
|
if (node->data.test_expr.else_node)
|
|
return statement_terminates_without_semicolon(node->data.test_expr.else_node);
|
|
return node->data.test_expr.then_node->type == NodeTypeBlock;
|
|
case NodeTypeWhileExpr:
|
|
return node->data.while_expr.body->type == NodeTypeBlock;
|
|
case NodeTypeForExpr:
|
|
return node->data.for_expr.body->type == NodeTypeBlock;
|
|
case NodeTypeCompTime:
|
|
return node->data.comptime_expr.expr->type == NodeTypeBlock;
|
|
case NodeTypeDefer:
|
|
return node->data.defer.expr->type == NodeTypeBlock;
|
|
case NodeTypeSuspend:
|
|
return node->data.suspend.block != nullptr && node->data.suspend.block->type == NodeTypeBlock;
|
|
case NodeTypeSwitchExpr:
|
|
case NodeTypeBlock:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Block = option(Symbol ":") "{" many(Statement) "}"
|
|
Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" | ExportDecl
|
|
*/
|
|
static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
size_t orig_token_index = *token_index;
|
|
|
|
Token *name_token = nullptr;
|
|
Token *last_token = &pc->tokens->at(*token_index);
|
|
|
|
if (last_token->id == TokenIdSymbol) {
|
|
*token_index += 1;
|
|
Token *colon_token = &pc->tokens->at(*token_index);
|
|
if (colon_token->id == TokenIdColon) {
|
|
*token_index += 1;
|
|
name_token = last_token;
|
|
last_token = &pc->tokens->at(*token_index);
|
|
} else if (mandatory) {
|
|
ast_expect_token(pc, colon_token, TokenIdColon);
|
|
zig_unreachable();
|
|
} else {
|
|
*token_index = orig_token_index;
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if (last_token->id != TokenIdLBrace) {
|
|
if (mandatory) {
|
|
ast_expect_token(pc, last_token, TokenIdLBrace);
|
|
} else {
|
|
*token_index = orig_token_index;
|
|
return nullptr;
|
|
}
|
|
}
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeBlock, last_token);
|
|
if (name_token != nullptr) {
|
|
node->data.block.name = token_buf(name_token);
|
|
}
|
|
|
|
for (;;) {
|
|
last_token = &pc->tokens->at(*token_index);
|
|
if (last_token->id == TokenIdRBrace) {
|
|
*token_index += 1;
|
|
return node;
|
|
}
|
|
|
|
AstNode *statement_node = ast_parse_local_var_decl(pc, token_index);
|
|
if (!statement_node)
|
|
statement_node = ast_parse_defer_expr(pc, token_index);
|
|
if (!statement_node)
|
|
statement_node = ast_parse_block_expr(pc, token_index, false);
|
|
if (!statement_node)
|
|
statement_node = ast_parse_expression(pc, token_index, false);
|
|
|
|
if (!statement_node) {
|
|
ast_invalid_token_error(pc, last_token);
|
|
}
|
|
|
|
node->data.block.statements.append(statement_node);
|
|
|
|
if (!statement_terminates_without_semicolon(statement_node)) {
|
|
ast_eat_token(pc, token_index, TokenIdSemicolon);
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
/*
|
|
FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("(" Expression ")"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var")
|
|
*/
|
|
static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {
|
|
Token *first_token = &pc->tokens->at(*token_index);
|
|
Token *fn_token;
|
|
|
|
CallingConvention cc;
|
|
bool is_extern = false;
|
|
AstNode *async_allocator_type_node = nullptr;
|
|
if (first_token->id == TokenIdKeywordNakedCC) {
|
|
*token_index += 1;
|
|
fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
|
|
cc = CallingConventionNaked;
|
|
} else if (first_token->id == TokenIdKeywordAsync) {
|
|
*token_index += 1;
|
|
Token *next_token = &pc->tokens->at(*token_index);
|
|
if (next_token->id == TokenIdCmpLessThan) {
|
|
*token_index += 1;
|
|
async_allocator_type_node = ast_parse_type_expr(pc, token_index, true);
|
|
ast_eat_token(pc, token_index, TokenIdCmpGreaterThan);
|
|
}
|
|
fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
|
|
cc = CallingConventionAsync;
|
|
} else if (first_token->id == TokenIdKeywordStdcallCC) {
|
|
*token_index += 1;
|
|
fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
|
|
cc = CallingConventionStdcall;
|
|
} else if (first_token->id == TokenIdKeywordExtern) {
|
|
is_extern = true;
|
|
*token_index += 1;
|
|
Token *next_token = &pc->tokens->at(*token_index);
|
|
if (next_token->id == TokenIdKeywordFn) {
|
|
fn_token = next_token;
|
|
*token_index += 1;
|
|
} else if (mandatory) {
|
|
ast_expect_token(pc, next_token, TokenIdKeywordFn);
|
|
zig_unreachable();
|
|
} else {
|
|
*token_index -= 1;
|
|
return nullptr;
|
|
}
|
|
cc = CallingConventionC;
|
|
} else if (first_token->id == TokenIdKeywordFn) {
|
|
fn_token = first_token;
|
|
*token_index += 1;
|
|
cc = CallingConventionUnspecified;
|
|
} else if (mandatory) {
|
|
ast_expect_token(pc, first_token, TokenIdKeywordFn);
|
|
zig_unreachable();
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
|
|
return ast_parse_fn_proto_partial(pc, token_index, fn_token, async_allocator_type_node, cc, is_extern, visib_mod);
|
|
}
|
|
|
|
/*
|
|
FnDef = option("inline" | "export") FnProto Block
|
|
*/
|
|
static AstNode *ast_parse_fn_def(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {
|
|
Token *first_token = &pc->tokens->at(*token_index);
|
|
bool is_inline;
|
|
bool is_export;
|
|
if (first_token->id == TokenIdKeywordInline) {
|
|
*token_index += 1;
|
|
is_inline = true;
|
|
is_export = false;
|
|
} else if (first_token->id == TokenIdKeywordExport) {
|
|
*token_index += 1;
|
|
is_export = true;
|
|
is_inline = false;
|
|
} else {
|
|
is_inline = false;
|
|
is_export = false;
|
|
}
|
|
|
|
AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory, visib_mod);
|
|
if (!fn_proto) {
|
|
if (is_inline || is_export) {
|
|
*token_index -= 1;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
fn_proto->data.fn_proto.is_inline = is_inline;
|
|
fn_proto->data.fn_proto.is_export = is_export;
|
|
|
|
Token *semi_token = &pc->tokens->at(*token_index);
|
|
if (semi_token->id == TokenIdSemicolon) {
|
|
*token_index += 1;
|
|
return fn_proto;
|
|
}
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeFnDef, first_token);
|
|
node->data.fn_def.fn_proto = fn_proto;
|
|
node->data.fn_def.body = ast_parse_block(pc, token_index, true);
|
|
fn_proto->data.fn_proto.fn_def_node = node;
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"
|
|
*/
|
|
static AstNode *ast_parse_extern_decl(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {
|
|
Token *extern_kw = &pc->tokens->at(*token_index);
|
|
if (extern_kw->id != TokenIdKeywordExtern) {
|
|
if (mandatory) {
|
|
ast_expect_token(pc, extern_kw, TokenIdKeywordExtern);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
*token_index += 1;
|
|
|
|
Token *lib_name_tok = &pc->tokens->at(*token_index);
|
|
Buf *lib_name = nullptr;
|
|
if (lib_name_tok->id == TokenIdStringLiteral) {
|
|
lib_name = token_buf(lib_name_tok);
|
|
*token_index += 1;
|
|
}
|
|
|
|
AstNode *fn_proto_node = ast_parse_fn_proto(pc, token_index, false, visib_mod);
|
|
if (fn_proto_node) {
|
|
ast_eat_token(pc, token_index, TokenIdSemicolon);
|
|
|
|
fn_proto_node->data.fn_proto.is_extern = true;
|
|
fn_proto_node->data.fn_proto.lib_name = lib_name;
|
|
|
|
return fn_proto_node;
|
|
}
|
|
|
|
AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod, false, false);
|
|
if (var_decl_node) {
|
|
ast_eat_token(pc, token_index, TokenIdSemicolon);
|
|
|
|
var_decl_node->data.variable_declaration.is_extern = true;
|
|
var_decl_node->data.variable_declaration.lib_name = lib_name;
|
|
|
|
return var_decl_node;
|
|
}
|
|
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
ast_invalid_token_error(pc, token);
|
|
}
|
|
|
|
/*
|
|
UseDecl = "use" Expression ";"
|
|
*/
|
|
static AstNode *ast_parse_use(ParseContext *pc, size_t *token_index, VisibMod visib_mod) {
|
|
Token *use_kw = &pc->tokens->at(*token_index);
|
|
if (use_kw->id != TokenIdKeywordUse)
|
|
return nullptr;
|
|
*token_index += 1;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeUse, use_kw);
|
|
node->data.use.visib_mod = visib_mod;
|
|
node->data.use.expr = ast_parse_expression(pc, token_index, true);
|
|
|
|
ast_eat_token(pc, token_index, TokenIdSemicolon);
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
ContainerDecl = option("extern" | "packed")
|
|
("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression)))
|
|
"{" many(ContainerMember) "}"
|
|
ContainerMember = (ContainerField | FnDef | GlobalVarDecl)
|
|
ContainerField = Symbol option(":" PrefixOpExpression option("=" PrefixOpExpression ","
|
|
*/
|
|
static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, bool mandatory) {
|
|
Token *first_token = &pc->tokens->at(*token_index);
|
|
Token *container_kind_token;
|
|
|
|
ContainerLayout layout;
|
|
if (first_token->id == TokenIdKeywordExtern) {
|
|
container_kind_token = &pc->tokens->at(*token_index + 1);
|
|
layout = ContainerLayoutExtern;
|
|
} else if (first_token->id == TokenIdKeywordPacked) {
|
|
container_kind_token = &pc->tokens->at(*token_index + 1);
|
|
layout = ContainerLayoutPacked;
|
|
} else {
|
|
container_kind_token = first_token;
|
|
layout = ContainerLayoutAuto;
|
|
}
|
|
|
|
ContainerKind kind;
|
|
if (container_kind_token->id == TokenIdKeywordStruct) {
|
|
kind = ContainerKindStruct;
|
|
} else if (container_kind_token->id == TokenIdKeywordEnum) {
|
|
kind = ContainerKindEnum;
|
|
} else if (container_kind_token->id == TokenIdKeywordUnion) {
|
|
kind = ContainerKindUnion;
|
|
} else if (mandatory) {
|
|
ast_invalid_token_error(pc, container_kind_token);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
*token_index += (layout == ContainerLayoutAuto) ? 1 : 2;
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeContainerDecl, first_token);
|
|
node->data.container_decl.layout = layout;
|
|
node->data.container_decl.kind = kind;
|
|
|
|
if (kind == ContainerKindUnion) {
|
|
Token *lparen_token = &pc->tokens->at(*token_index);
|
|
if (lparen_token->id == TokenIdLParen) {
|
|
Token *enum_token = &pc->tokens->at(*token_index + 1);
|
|
if (enum_token->id == TokenIdKeywordEnum) {
|
|
Token *paren_token = &pc->tokens->at(*token_index + 2);
|
|
if (paren_token->id == TokenIdLParen) {
|
|
node->data.container_decl.auto_enum = true;
|
|
*token_index += 2;
|
|
node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, true);
|
|
ast_eat_token(pc, token_index, TokenIdRParen);
|
|
} else if (paren_token->id == TokenIdRParen) {
|
|
node->data.container_decl.auto_enum = true;
|
|
*token_index += 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!node->data.container_decl.auto_enum) {
|
|
node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, false);
|
|
}
|
|
|
|
ast_eat_token(pc, token_index, TokenIdLBrace);
|
|
|
|
for (;;) {
|
|
Token *visib_tok = &pc->tokens->at(*token_index);
|
|
VisibMod visib_mod;
|
|
if (visib_tok->id == TokenIdKeywordPub) {
|
|
*token_index += 1;
|
|
visib_mod = VisibModPub;
|
|
} else {
|
|
visib_mod = VisibModPrivate;
|
|
}
|
|
|
|
AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false, visib_mod);
|
|
if (fn_def_node) {
|
|
node->data.container_decl.decls.append(fn_def_node);
|
|
continue;
|
|
}
|
|
|
|
AstNode *var_decl_node = ast_parse_global_var_decl(pc, token_index, visib_mod);
|
|
if (var_decl_node) {
|
|
ast_eat_token(pc, token_index, TokenIdSemicolon);
|
|
node->data.container_decl.decls.append(var_decl_node);
|
|
continue;
|
|
}
|
|
|
|
Token *token = &pc->tokens->at(*token_index);
|
|
|
|
if (token->id == TokenIdRBrace) {
|
|
*token_index += 1;
|
|
break;
|
|
} else if (token->id == TokenIdSymbol) {
|
|
AstNode *field_node = ast_create_node(pc, NodeTypeStructField, token);
|
|
*token_index += 1;
|
|
|
|
node->data.container_decl.fields.append(field_node);
|
|
field_node->data.struct_field.visib_mod = visib_mod;
|
|
field_node->data.struct_field.name = token_buf(token);
|
|
|
|
Token *colon_token = &pc->tokens->at(*token_index);
|
|
if (colon_token->id == TokenIdColon) {
|
|
*token_index += 1;
|
|
field_node->data.struct_field.type = ast_parse_type_expr(pc, token_index, true);
|
|
}
|
|
Token *eq_token = &pc->tokens->at(*token_index);
|
|
if (eq_token->id == TokenIdEq) {
|
|
*token_index += 1;
|
|
field_node->data.struct_field.value = ast_parse_expression(pc, token_index, true);
|
|
}
|
|
|
|
Token *next_token = &pc->tokens->at(*token_index);
|
|
if (next_token->id == TokenIdComma) {
|
|
*token_index += 1;
|
|
continue;
|
|
}
|
|
|
|
if (next_token->id == TokenIdRBrace) {
|
|
*token_index += 1;
|
|
break;
|
|
}
|
|
|
|
ast_invalid_token_error(pc, next_token);
|
|
} else {
|
|
ast_invalid_token_error(pc, token);
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
TestDecl = "test" String Block
|
|
*/
|
|
static AstNode *ast_parse_test_decl_node(ParseContext *pc, size_t *token_index) {
|
|
Token *first_token = &pc->tokens->at(*token_index);
|
|
|
|
if (first_token->id != TokenIdKeywordTest) {
|
|
return nullptr;
|
|
}
|
|
*token_index += 1;
|
|
|
|
Token *name_tok = ast_eat_token(pc, token_index, TokenIdStringLiteral);
|
|
|
|
AstNode *node = ast_create_node(pc, NodeTypeTestDecl, first_token);
|
|
node->data.test_decl.name = token_buf(name_tok);
|
|
node->data.test_decl.body = ast_parse_block(pc, token_index, true);
|
|
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
TopLevelItem = ErrorValueDecl | CompTimeExpression(Block) | TopLevelDecl | TestDecl
|
|
TopLevelDecl = option("pub") (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
|
|
*/
|
|
static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, ZigList<AstNode *> *top_level_decls) {
|
|
for (;;) {
|
|
AstNode *comptime_expr_node = ast_parse_comptime_expr(pc, token_index, true, false);
|
|
if (comptime_expr_node) {
|
|
top_level_decls->append(comptime_expr_node);
|
|
continue;
|
|
}
|
|
|
|
AstNode *test_decl_node = ast_parse_test_decl_node(pc, token_index);
|
|
if (test_decl_node) {
|
|
top_level_decls->append(test_decl_node);
|
|
continue;
|
|
}
|
|
|
|
Token *visib_tok = &pc->tokens->at(*token_index);
|
|
VisibMod visib_mod;
|
|
if (visib_tok->id == TokenIdKeywordPub) {
|
|
*token_index += 1;
|
|
visib_mod = VisibModPub;
|
|
} else {
|
|
visib_mod = VisibModPrivate;
|
|
}
|
|
|
|
AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false, visib_mod);
|
|
if (fn_def_node) {
|
|
top_level_decls->append(fn_def_node);
|
|
continue;
|
|
}
|
|
|
|
AstNode *fn_proto_node = ast_parse_extern_decl(pc, token_index, false, visib_mod);
|
|
if (fn_proto_node) {
|
|
top_level_decls->append(fn_proto_node);
|
|
continue;
|
|
}
|
|
|
|
AstNode *use_node = ast_parse_use(pc, token_index, visib_mod);
|
|
if (use_node) {
|
|
top_level_decls->append(use_node);
|
|
continue;
|
|
}
|
|
|
|
AstNode *var_decl_node = ast_parse_global_var_decl(pc, token_index, visib_mod);
|
|
if (var_decl_node) {
|
|
ast_eat_token(pc, token_index, TokenIdSemicolon);
|
|
top_level_decls->append(var_decl_node);
|
|
continue;
|
|
}
|
|
|
|
return;
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
/*
|
|
Root = many(TopLevelItem) "EOF"
|
|
*/
|
|
static AstNode *ast_parse_root(ParseContext *pc, size_t *token_index) {
|
|
AstNode *node = ast_create_node(pc, NodeTypeRoot, &pc->tokens->at(*token_index));
|
|
|
|
ast_parse_top_level_decls(pc, token_index, &node->data.root.top_level_decls);
|
|
|
|
if (*token_index != pc->tokens->length - 1) {
|
|
ast_invalid_token_error(pc, &pc->tokens->at(*token_index));
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner,
|
|
ErrColor err_color)
|
|
{
|
|
ParseContext pc = {0};
|
|
pc.void_buf = buf_create_from_str("void");
|
|
pc.err_color = err_color;
|
|
pc.owner = owner;
|
|
pc.buf = buf;
|
|
pc.tokens = tokens;
|
|
size_t token_index = 0;
|
|
pc.root = ast_parse_root(&pc, &token_index);
|
|
return pc.root;
|
|
}
|
|
|
|
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 NodeTypeRoot:
|
|
visit_node_list(&node->data.root.top_level_decls, visit, context);
|
|
break;
|
|
case NodeTypeFnProto:
|
|
visit_field(&node->data.fn_proto.return_type, visit, context);
|
|
visit_node_list(&node->data.fn_proto.params, visit, context);
|
|
visit_field(&node->data.fn_proto.align_expr, visit, context);
|
|
visit_field(&node->data.fn_proto.section_expr, visit, context);
|
|
visit_field(&node->data.fn_proto.async_allocator_type, visit, context);
|
|
break;
|
|
case NodeTypeFnDef:
|
|
visit_field(&node->data.fn_def.fn_proto, visit, context);
|
|
visit_field(&node->data.fn_def.body, visit, context);
|
|
break;
|
|
case NodeTypeParamDecl:
|
|
visit_field(&node->data.param_decl.type, visit, context);
|
|
break;
|
|
case NodeTypeBlock:
|
|
visit_node_list(&node->data.block.statements, visit, context);
|
|
break;
|
|
case NodeTypeGroupedExpr:
|
|
visit_field(&node->data.grouped_expr, visit, context);
|
|
break;
|
|
case NodeTypeReturnExpr:
|
|
visit_field(&node->data.return_expr.expr, visit, context);
|
|
break;
|
|
case NodeTypeDefer:
|
|
visit_field(&node->data.defer.expr, visit, context);
|
|
break;
|
|
case NodeTypeVariableDeclaration:
|
|
visit_field(&node->data.variable_declaration.type, visit, context);
|
|
visit_field(&node->data.variable_declaration.expr, visit, context);
|
|
visit_field(&node->data.variable_declaration.align_expr, visit, context);
|
|
visit_field(&node->data.variable_declaration.section_expr, visit, context);
|
|
break;
|
|
case NodeTypeTestDecl:
|
|
visit_field(&node->data.test_decl.body, visit, context);
|
|
break;
|
|
case NodeTypeBinOpExpr:
|
|
visit_field(&node->data.bin_op_expr.op1, visit, context);
|
|
visit_field(&node->data.bin_op_expr.op2, visit, context);
|
|
break;
|
|
case NodeTypeUnwrapErrorExpr:
|
|
visit_field(&node->data.unwrap_err_expr.op1, visit, context);
|
|
visit_field(&node->data.unwrap_err_expr.symbol, visit, context);
|
|
visit_field(&node->data.unwrap_err_expr.op2, visit, context);
|
|
break;
|
|
case NodeTypeIntLiteral:
|
|
// none
|
|
break;
|
|
case NodeTypeFloatLiteral:
|
|
// none
|
|
break;
|
|
case NodeTypeStringLiteral:
|
|
// none
|
|
break;
|
|
case NodeTypeCharLiteral:
|
|
// none
|
|
break;
|
|
case NodeTypeSymbol:
|
|
// none
|
|
break;
|
|
case NodeTypePrefixOpExpr:
|
|
visit_field(&node->data.prefix_op_expr.primary_expr, visit, context);
|
|
break;
|
|
case NodeTypeFnCallExpr:
|
|
visit_field(&node->data.fn_call_expr.fn_ref_expr, visit, context);
|
|
visit_node_list(&node->data.fn_call_expr.params, visit, context);
|
|
visit_field(&node->data.fn_call_expr.async_allocator, visit, context);
|
|
break;
|
|
case NodeTypeArrayAccessExpr:
|
|
visit_field(&node->data.array_access_expr.array_ref_expr, visit, context);
|
|
visit_field(&node->data.array_access_expr.subscript, visit, context);
|
|
break;
|
|
case NodeTypeSliceExpr:
|
|
visit_field(&node->data.slice_expr.array_ref_expr, visit, context);
|
|
visit_field(&node->data.slice_expr.start, visit, context);
|
|
visit_field(&node->data.slice_expr.end, visit, context);
|
|
break;
|
|
case NodeTypeFieldAccessExpr:
|
|
visit_field(&node->data.field_access_expr.struct_expr, visit, context);
|
|
break;
|
|
case NodeTypePtrDeref:
|
|
visit_field(&node->data.ptr_deref_expr.target, visit, context);
|
|
break;
|
|
case NodeTypeUse:
|
|
visit_field(&node->data.use.expr, visit, context);
|
|
break;
|
|
case NodeTypeBoolLiteral:
|
|
// none
|
|
break;
|
|
case NodeTypeNullLiteral:
|
|
// none
|
|
break;
|
|
case NodeTypeUndefinedLiteral:
|
|
// none
|
|
break;
|
|
case NodeTypeThisLiteral:
|
|
// none
|
|
break;
|
|
case NodeTypeIfBoolExpr:
|
|
visit_field(&node->data.if_bool_expr.condition, visit, context);
|
|
visit_field(&node->data.if_bool_expr.then_block, visit, context);
|
|
visit_field(&node->data.if_bool_expr.else_node, visit, context);
|
|
break;
|
|
case NodeTypeIfErrorExpr:
|
|
visit_field(&node->data.if_err_expr.target_node, visit, context);
|
|
visit_field(&node->data.if_err_expr.then_node, visit, context);
|
|
visit_field(&node->data.if_err_expr.else_node, visit, context);
|
|
break;
|
|
case NodeTypeTestExpr:
|
|
visit_field(&node->data.test_expr.target_node, visit, context);
|
|
visit_field(&node->data.test_expr.then_node, visit, context);
|
|
visit_field(&node->data.test_expr.else_node, visit, context);
|
|
break;
|
|
case NodeTypeWhileExpr:
|
|
visit_field(&node->data.while_expr.condition, visit, context);
|
|
visit_field(&node->data.while_expr.body, visit, context);
|
|
break;
|
|
case NodeTypeForExpr:
|
|
visit_field(&node->data.for_expr.elem_node, visit, context);
|
|
visit_field(&node->data.for_expr.array_expr, visit, context);
|
|
visit_field(&node->data.for_expr.index_node, visit, context);
|
|
visit_field(&node->data.for_expr.body, visit, context);
|
|
break;
|
|
case NodeTypeSwitchExpr:
|
|
visit_field(&node->data.switch_expr.expr, visit, context);
|
|
visit_node_list(&node->data.switch_expr.prongs, visit, context);
|
|
break;
|
|
case NodeTypeSwitchProng:
|
|
visit_node_list(&node->data.switch_prong.items, visit, context);
|
|
visit_field(&node->data.switch_prong.var_symbol, visit, context);
|
|
visit_field(&node->data.switch_prong.expr, visit, context);
|
|
break;
|
|
case NodeTypeSwitchRange:
|
|
visit_field(&node->data.switch_range.start, visit, context);
|
|
visit_field(&node->data.switch_range.end, visit, context);
|
|
break;
|
|
case NodeTypeCompTime:
|
|
visit_field(&node->data.comptime_expr.expr, visit, context);
|
|
break;
|
|
case NodeTypeBreak:
|
|
// none
|
|
break;
|
|
case NodeTypeContinue:
|
|
// none
|
|
break;
|
|
case NodeTypeUnreachable:
|
|
// none
|
|
break;
|
|
case NodeTypeAsmExpr:
|
|
for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) {
|
|
AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
|
|
visit_field(&asm_input->expr, visit, context);
|
|
}
|
|
for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1) {
|
|
AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
|
|
visit_field(&asm_output->return_type, visit, context);
|
|
}
|
|
break;
|
|
case NodeTypeContainerDecl:
|
|
visit_node_list(&node->data.container_decl.fields, visit, context);
|
|
visit_node_list(&node->data.container_decl.decls, visit, context);
|
|
visit_field(&node->data.container_decl.init_arg_expr, visit, context);
|
|
break;
|
|
case NodeTypeStructField:
|
|
visit_field(&node->data.struct_field.type, visit, context);
|
|
visit_field(&node->data.struct_field.value, visit, context);
|
|
break;
|
|
case NodeTypeContainerInitExpr:
|
|
visit_field(&node->data.container_init_expr.type, visit, context);
|
|
visit_node_list(&node->data.container_init_expr.entries, visit, context);
|
|
break;
|
|
case NodeTypeStructValueField:
|
|
visit_field(&node->data.struct_val_field.expr, visit, context);
|
|
break;
|
|
case NodeTypeArrayType:
|
|
visit_field(&node->data.array_type.size, visit, context);
|
|
visit_field(&node->data.array_type.child_type, visit, context);
|
|
visit_field(&node->data.array_type.align_expr, visit, context);
|
|
break;
|
|
case NodeTypePromiseType:
|
|
visit_field(&node->data.promise_type.payload_type, visit, context);
|
|
break;
|
|
case NodeTypeErrorType:
|
|
// none
|
|
break;
|
|
case NodeTypePointerType:
|
|
visit_field(&node->data.pointer_type.align_expr, visit, context);
|
|
visit_field(&node->data.pointer_type.op_expr, visit, context);
|
|
break;
|
|
case NodeTypeErrorSetDecl:
|
|
visit_node_list(&node->data.err_set_decl.decls, visit, context);
|
|
break;
|
|
case NodeTypeCancel:
|
|
visit_field(&node->data.cancel_expr.expr, visit, context);
|
|
break;
|
|
case NodeTypeResume:
|
|
visit_field(&node->data.resume_expr.expr, visit, context);
|
|
break;
|
|
case NodeTypeAwaitExpr:
|
|
visit_field(&node->data.await_expr.expr, visit, context);
|
|
break;
|
|
case NodeTypeSuspend:
|
|
visit_field(&node->data.suspend.promise_symbol, visit, context);
|
|
visit_field(&node->data.suspend.block, visit, context);
|
|
break;
|
|
}
|
|
}
|