aboutsummaryrefslogtreecommitdiff
path: root/src/parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser.cpp')
-rw-r--r--src/parser.cpp1591
1 files changed, 1591 insertions, 0 deletions
diff --git a/src/parser.cpp b/src/parser.cpp
new file mode 100644
index 000000000..d42790099
--- /dev/null
+++ b/src/parser.cpp
@@ -0,0 +1,1591 @@
+struct AstNode;
+struct Type;
+struct AstScope;
+
+// NOTE(bill): Just used to quickly check if there is double declaration in the same scope
+// No type checking actually happens
+struct AstEntity {
+ Token token;
+ AstScope *parent;
+ AstNode *declaration;
+};
+
+struct AstScope {
+ AstScope *parent;
+ Map<AstEntity> entities; // Key: Token.string
+};
+
+
+struct Parser {
+ gbArena arena;
+ Tokenizer tokenizer;
+ gbArray(Token) tokens;
+ Token * cursor; // NOTE(bill): Current token, easy to peek forward and backwards if needed
+
+ AstScope *file_scope;
+ AstScope *curr_scope;
+ isize scope_level;
+
+#define MAX_PARSER_ERROR_COUNT 10
+ isize error_count;
+ isize error_prev_line;
+ isize error_prev_column;
+};
+
+enum AstNodeKind {
+ AstNode_Invalid,
+
+ AstNode_BasicLiteral,
+ AstNode_Identifier,
+
+AstNode__ExpressionBegin,
+ AstNode_BadExpression, // NOTE(bill): Naughty expression
+ AstNode_TagExpression,
+ AstNode_UnaryExpression,
+ AstNode_BinaryExpression,
+ AstNode_ParenExpression,
+ AstNode_CallExpression,
+ AstNode_SelectorExpression,
+ AstNode_IndexExpression,
+ AstNode_CastExpression,
+ AstNode_DereferenceExpression,
+AstNode__ExpressionEnd,
+
+AstNode__StatementBegin,
+ AstNode_BadStatement,
+ AstNode_EmptyStatement,
+ AstNode_ExpressionStatement,
+ AstNode_IncDecStatement,
+ AstNode_AssignStatement,
+
+AstNode__ComplexStatementBegin,
+ AstNode_BlockStatement,
+ AstNode_IfStatement,
+ AstNode_ReturnStatement,
+ AstNode_ForStatement,
+ AstNode_DeferStatement,
+AstNode__ComplexStatementEnd,
+
+AstNode__StatementEnd,
+
+AstNode__DeclarationBegin,
+ AstNode_BadDeclaration,
+ AstNode_VariableDeclaration,
+ AstNode_ProcedureDeclaration,
+ AstNode_TypeDeclaration,
+AstNode__DeclarationEnd,
+
+AstNode__TypeBegin,
+ AstNode_Field,
+ AstNode_ProcedureType,
+ AstNode_PointerType,
+ AstNode_ArrayType,
+ AstNode_StructType,
+AstNode__TypeEnd,
+
+ AstNode_Count,
+};
+
+enum DeclarationKind {
+ Declaration_Invalid,
+
+ Declaration_Mutable,
+ Declaration_Immutable,
+
+ Declaration_Count,
+};
+
+
+struct AstNode {
+ AstNodeKind kind;
+ AstNode *prev, *next; // NOTE(bill): allow for Linked list
+ Type *type;
+ union {
+ // NOTE(bill): open/close for debugging/errors
+ Token basic_literal;
+ struct {
+ Token token;
+ AstEntity *entity;
+ } identifier;
+ struct {
+ Token token;
+ Token name;
+ } tag_expression;
+
+ struct { Token begin, end; } bad_expression;
+ struct { Token op; AstNode *operand; } unary_expression;
+ struct { Token op; AstNode *left, *right; } binary_expression;
+ struct { AstNode *expression; Token open, close; } paren_expression;
+ struct { Token token; AstNode *operand, *selector; } selector_expression;
+ struct { AstNode *expression, *value; Token open, close; } index_expression;
+ struct { Token token; AstNode *type_expression, *operand; } cast_expression;
+ struct {
+ AstNode *proc, *arg_list;
+ isize arg_list_count;
+ Token open, close;
+ } call_expression;
+ struct { Token op; AstNode *operand; } dereference_expression;
+
+ struct { Token begin, end; } bad_statement;
+ struct { Token token; } empty_statement;
+ struct { AstNode *expression; } expression_statement;
+ struct { Token op; AstNode *expression; } inc_dec_statement;
+ struct {
+ Token op;
+ AstNode *lhs_list, *rhs_list;
+ isize lhs_count, rhs_count;
+ } assign_statement;
+ struct {
+ AstNode *list;
+ isize list_count;
+ Token open, close;
+ } block_statement;
+ struct {
+ Token token;
+ AstNode *cond, *body, *else_statement;
+ } if_statement;
+ struct {
+ Token token;
+ AstNode *results; // NOTE(bill): Return values
+ isize result_count;
+ } return_statement;
+ struct {
+ Token token;
+ AstNode *init, *cond, *end;
+ AstNode *body;
+ } for_statement;
+ struct {
+ Token token;
+ AstNode *statement;
+ } defer_statement;
+
+ struct { Token begin, end; } bad_declaration;
+ struct {
+ DeclarationKind kind;
+ AstNode *name_list;
+ AstNode *type_expression;
+ AstNode *value_list;
+ isize name_list_count, value_list_count;
+ } variable_declaration;
+
+ struct {
+ AstNode *name_list;
+ isize name_list_count;
+ AstNode *type_expression;
+ } field;
+ struct {
+ Token token;
+ AstNode *param_list; // AstNode_Field
+ isize param_count;
+ AstNode *results_list; // type expression list
+ isize result_count;
+ } procedure_type;
+ struct {
+ DeclarationKind kind;
+ AstNode *name; // AstNode_Identifier
+ AstNode *procedure_type; // AstNode_ProcedureType
+ AstNode *body; // AstNode_BlockStatement
+ AstNode *tag; // AstNode_TagExpression;
+ } procedure_declaration;
+ struct {
+ Token token;
+ AstNode *name;
+ AstNode *type_expression;
+ } type_declaration;
+
+
+ struct {
+ Token token;
+ AstNode *type_expression;
+ } pointer_type;
+ struct {
+ Token token;
+ AstNode *count;
+ AstNode *element;
+ } array_type;
+ struct {
+ Token token;
+ AstNode *field_list; // AstNode_Field
+ isize field_count;
+ } struct_type;
+ };
+};
+
+
+#define DLIST_SET(curr_element, next_element) do { \
+ (curr_element)->next = (next_element); \
+ (curr_element)->next->prev = (curr_element); \
+ (curr_element) = (curr_element)->next; \
+} while (0)
+
+#define DLIST_APPEND(root_element, curr_element, next_element) do { \
+ if ((root_element) == NULL) \
+ (root_element) = (curr_element) = (next_element); \
+ else \
+ DLIST_SET(curr_element, next_element); \
+} while (0)
+
+
+gb_inline AstScope *make_ast_scope(Parser *p, AstScope *parent) {
+ AstScope *scope = gb_alloc_item(gb_arena_allocator(&p->arena), AstScope);
+ map_init(&scope->entities, gb_heap_allocator());
+ scope->parent = parent;
+ return scope;
+}
+
+
+gb_inline b32 is_ast_node_expression(AstNode *node) {
+ return gb_is_between(node->kind, AstNode__ExpressionBegin+1, AstNode__ExpressionEnd-1);
+}
+gb_inline b32 is_ast_node_statement(AstNode *node) {
+ return gb_is_between(node->kind, AstNode__StatementBegin+1, AstNode__StatementEnd-1);
+}
+gb_inline b32 is_ast_node_complex_statement(AstNode *node) {
+ return gb_is_between(node->kind, AstNode__ComplexStatementBegin+1, AstNode__ComplexStatementEnd-1);
+}
+gb_inline b32 is_ast_node_declaration(AstNode *node) {
+ return gb_is_between(node->kind, AstNode__DeclarationBegin+1, AstNode__DeclarationEnd-1);
+}
+gb_inline b32 is_ast_node_type(AstNode *node) {
+ return gb_is_between(node->kind, AstNode__TypeBegin+1, AstNode__TypeEnd-1);
+}
+
+
+Token ast_node_token(AstNode *node) {
+ switch (node->kind) {
+ case AstNode_BasicLiteral:
+ return node->basic_literal;
+ case AstNode_Identifier:
+ return node->identifier.token;
+ case AstNode_BadExpression:
+ return node->bad_expression.begin;
+ case AstNode_UnaryExpression:
+ return node->unary_expression.op;
+ case AstNode_BinaryExpression:
+ return ast_node_token(node->binary_expression.left);
+ case AstNode_ParenExpression:
+ return node->paren_expression.open;
+ case AstNode_CallExpression:
+ return ast_node_token(node->call_expression.proc);
+ case AstNode_SelectorExpression:
+ return ast_node_token(node->selector_expression.selector);
+ case AstNode_IndexExpression:
+ return node->index_expression.open;
+ case AstNode_CastExpression:
+ return node->cast_expression.token;
+ case AstNode_DereferenceExpression:
+ return node->dereference_expression.op;
+ case AstNode_BadStatement:
+ return node->bad_statement.begin;
+ case AstNode_EmptyStatement:
+ return node->empty_statement.token;
+ case AstNode_ExpressionStatement:
+ return ast_node_token(node->expression_statement.expression);
+ case AstNode_IncDecStatement:
+ return node->inc_dec_statement.op;
+ case AstNode_AssignStatement:
+ return node->assign_statement.op;
+ case AstNode_BlockStatement:
+ return node->block_statement.open;
+ case AstNode_IfStatement:
+ return node->if_statement.token;
+ case AstNode_ReturnStatement:
+ return node->return_statement.token;
+ case AstNode_ForStatement:
+ return node->for_statement.token;
+ case AstNode_DeferStatement:
+ return node->defer_statement.token;
+ case AstNode_BadDeclaration:
+ return node->bad_declaration.begin;
+ case AstNode_VariableDeclaration:
+ return ast_node_token(node->variable_declaration.name_list);
+ case AstNode_ProcedureDeclaration:
+ return node->procedure_declaration.name->identifier.token;
+ case AstNode_TypeDeclaration:
+ return node->type_declaration.token;
+ case AstNode_Field:
+ return ast_node_token(node->field.name_list);
+ case AstNode_ProcedureType:
+ return node->procedure_type.token;
+ case AstNode_PointerType:
+ return node->pointer_type.token;
+ case AstNode_ArrayType:
+ return node->array_type.token;
+ case AstNode_StructType:
+ return node->struct_type.token;
+ }
+
+ Token null_token = {};
+ return null_token;
+;}
+
+gb_inline void destroy_ast_scope(AstScope *scope) {
+ // NOTE(bill): No need to free the actual pointer to the AstScope
+ // as there should be enough room in the arena
+ map_destroy(&scope->entities);
+}
+
+gb_inline AstScope *open_ast_scope(Parser *p) {
+ AstScope *scope = make_ast_scope(p, p->curr_scope);
+ p->curr_scope = scope;
+ p->scope_level++;
+ return p->curr_scope;
+}
+
+gb_inline void close_ast_scope(Parser *p) {
+ GB_ASSERT_NOT_NULL(p->curr_scope);
+ GB_ASSERT(p->scope_level > 0);
+ {
+ AstScope *parent = p->curr_scope->parent;
+ if (p->curr_scope) {
+ destroy_ast_scope(p->curr_scope);
+ }
+ p->curr_scope = parent;
+ p->scope_level--;
+ }
+}
+
+AstEntity *make_ast_entity(Parser *p, Token token, AstNode *declaration, AstScope *parent) {
+ AstEntity *entity = gb_alloc_item(gb_arena_allocator(&p->arena), AstEntity);
+ entity->token = token;
+ entity->declaration = declaration;
+ entity->parent = parent;
+ return entity;
+}
+
+u64 hash_token(Token t) {
+ return hash_string(t.string);
+}
+
+AstEntity *ast_scope_lookup(AstScope *scope, Token token) {
+ return map_get(&scope->entities, hash_token(token));
+}
+
+AstEntity *ast_scope_insert(AstScope *scope, AstEntity entity) {
+ AstEntity *prev = ast_scope_lookup(scope, entity.token);
+ if (prev == NULL) {
+ map_set(&scope->entities, hash_token(entity.token), entity);
+ }
+ return prev;
+}
+
+// NOTE(bill): And this below is why is need a new language! Discriminated unions are a pain in C/C++
+gb_inline AstNode *make_node(Parser *p, AstNodeKind kind) {
+ AstNode *node = gb_alloc_item(gb_arena_allocator(&p->arena), AstNode);
+ node->kind = kind;
+ return node;
+}
+
+gb_inline AstNode *make_bad_expression(Parser *p, Token begin, Token end) {
+ AstNode *result = make_node(p, AstNode_BadExpression);
+ result->bad_expression.begin = begin;
+ result->bad_expression.end = end;
+ return result;
+}
+
+gb_inline AstNode *make_tag_expression(Parser *p, Token token, Token name) {
+ AstNode *result = make_node(p, AstNode_TagExpression);
+ result->tag_expression.token = token;
+ result->tag_expression.name = name;
+ return result;
+}
+
+gb_inline AstNode *make_unary_expression(Parser *p, Token op, AstNode *operand) {
+ AstNode *result = make_node(p, AstNode_UnaryExpression);
+ result->unary_expression.op = op;
+ result->unary_expression.operand = operand;
+ return result;
+}
+
+gb_inline AstNode *make_binary_expression(Parser *p, Token op, AstNode *left, AstNode *right) {
+ AstNode *result = make_node(p, AstNode_BinaryExpression);
+ result->binary_expression.op = op;
+ result->binary_expression.left = left;
+ result->binary_expression.right = right;
+ return result;
+}
+
+gb_inline AstNode *make_paren_expression(Parser *p, AstNode *expression, Token open, Token close) {
+ AstNode *result = make_node(p, AstNode_ParenExpression);
+ result->paren_expression.expression = expression;
+ result->paren_expression.open = open;
+ result->paren_expression.close = close;
+ return result;
+}
+
+gb_inline AstNode *make_call_expression(Parser *p, AstNode *proc, AstNode *arg_list, isize arg_list_count, Token open, Token close) {
+ AstNode *result = make_node(p, AstNode_CallExpression);
+ result->call_expression.proc = proc;
+ result->call_expression.arg_list = arg_list;
+ result->call_expression.arg_list_count = arg_list_count;
+ result->call_expression.open = open;
+ result->call_expression.close = close;
+ return result;
+}
+
+gb_inline AstNode *make_selector_expression(Parser *p, Token token, AstNode *operand, AstNode *selector) {
+ AstNode *result = make_node(p, AstNode_SelectorExpression);
+ result->selector_expression.operand = operand;
+ result->selector_expression.selector = selector;
+ return result;
+}
+
+gb_inline AstNode *make_index_expression(Parser *p, AstNode *expression, AstNode *value, Token open, Token close) {
+ AstNode *result = make_node(p, AstNode_IndexExpression);
+ result->index_expression.expression = expression;
+ result->index_expression.value = value;
+ result->index_expression.open = open;
+ result->index_expression.close = close;
+ return result;
+}
+
+gb_inline AstNode *make_cast_expression(Parser *p, Token token, AstNode *type_expression, AstNode *operand) {
+ AstNode *result = make_node(p, AstNode_CastExpression);
+ result->cast_expression.token = token;
+ result->cast_expression.type_expression = type_expression;
+ result->cast_expression.operand = operand;
+ return result;
+}
+
+
+gb_inline AstNode *make_dereference_expression(Parser *p, AstNode *operand, Token op) {
+ AstNode *result = make_node(p, AstNode_DereferenceExpression);
+ result->dereference_expression.operand = operand;
+ result->dereference_expression.op = op;
+ return result;
+}
+
+
+gb_inline AstNode *make_basic_literal(Parser *p, Token basic_literal) {
+ AstNode *result = make_node(p, AstNode_BasicLiteral);
+ result->basic_literal = basic_literal;
+ return result;
+}
+
+gb_inline AstNode *make_identifier(Parser *p, Token token, AstEntity *entity = NULL) {
+ AstNode *result = make_node(p, AstNode_Identifier);
+ result->identifier.token = token;
+ result->identifier.entity = entity;
+ return result;
+}
+
+gb_inline AstNode *make_bad_statement(Parser *p, Token begin, Token end) {
+ AstNode *result = make_node(p, AstNode_BadStatement);
+ result->bad_statement.begin = begin;
+ result->bad_statement.end = end;
+ return result;
+}
+
+gb_inline AstNode *make_empty_statement(Parser *p, Token token) {
+ AstNode *result = make_node(p, AstNode_EmptyStatement);
+ result->empty_statement.token = token;
+ return result;
+}
+
+gb_inline AstNode *make_expression_statement(Parser *p, AstNode *expression) {
+ AstNode *result = make_node(p, AstNode_ExpressionStatement);
+ result->expression_statement.expression = expression;
+ return result;
+}
+
+gb_inline AstNode *make_inc_dec_statement(Parser *p, Token op, AstNode *expression) {
+ AstNode *result = make_node(p, AstNode_IncDecStatement);
+ result->inc_dec_statement.op = op;
+ result->inc_dec_statement.expression = expression;
+ return result;
+}
+
+gb_inline AstNode *make_assign_statement(Parser *p, Token op, AstNode *lhs_list, isize lhs_count, AstNode *rhs_list, isize rhs_count) {
+ AstNode *result = make_node(p, AstNode_AssignStatement);
+ result->assign_statement.op = op;
+ result->assign_statement.lhs_list = lhs_list;
+ result->assign_statement.lhs_count = lhs_count;
+ result->assign_statement.rhs_list = rhs_list;
+ result->assign_statement.rhs_count = rhs_count;
+ return result;
+}
+
+gb_inline AstNode *make_block_statement(Parser *p, AstNode *list, isize list_count, Token open, Token close) {
+ AstNode *result = make_node(p, AstNode_BlockStatement);
+ result->block_statement.list = list;
+ result->block_statement.list_count = list_count;
+ result->block_statement.open = open;
+ result->block_statement.close = close;
+ return result;
+}
+
+gb_inline AstNode *make_if_statement(Parser *p, Token token, AstNode *cond, AstNode *body, AstNode *else_statement) {
+ AstNode *result = make_node(p, AstNode_IfStatement);
+ result->if_statement.token = token;
+ result->if_statement.cond = cond;
+ result->if_statement.body = body;
+ result->if_statement.else_statement = else_statement;
+ return result;
+}
+
+gb_inline AstNode *make_return_statement(Parser *p, Token token, AstNode *results, isize result_count) {
+ AstNode *result = make_node(p, AstNode_ReturnStatement);
+ result->return_statement.token = token;
+ result->return_statement.results = results;
+ result->return_statement.result_count = result_count;
+ return result;
+}
+
+gb_inline AstNode *make_for_statement(Parser *p, Token token, AstNode *init, AstNode *cond, AstNode *end, AstNode *body) {
+ AstNode *result = make_node(p, AstNode_ForStatement);
+ result->for_statement.token = token;
+ result->for_statement.init = init;
+ result->for_statement.cond = cond;
+ result->for_statement.end = end;
+ result->for_statement.body = body;
+ return result;
+}
+gb_inline AstNode *make_defer_statement(Parser *p, Token token, AstNode *statement) {
+ AstNode *result = make_node(p, AstNode_DeferStatement);
+ result->defer_statement.token = token;
+ result->defer_statement.statement = statement;
+ return result;
+}
+
+gb_inline AstNode *make_bad_declaration(Parser *p, Token begin, Token end) {
+ AstNode *result = make_node(p, AstNode_BadDeclaration);
+ result->bad_declaration.begin = begin;
+ result->bad_declaration.end = end;
+ return result;
+}
+
+gb_inline AstNode *make_variable_declaration(Parser *p, DeclarationKind kind, AstNode *name_list, isize name_list_count, AstNode *type_expression, AstNode *value_list, isize value_list_count) {
+ AstNode *result = make_node(p, AstNode_VariableDeclaration);
+ result->variable_declaration.kind = kind;
+ result->variable_declaration.name_list = name_list;
+ result->variable_declaration.name_list_count = name_list_count;
+ result->variable_declaration.type_expression = type_expression;
+ result->variable_declaration.value_list = value_list;
+ result->variable_declaration.value_list_count = value_list_count;
+ return result;
+}
+
+gb_inline AstNode *make_field(Parser *p, AstNode *name_list, isize name_list_count, AstNode *type_expression) {
+ AstNode *result = make_node(p, AstNode_Field);
+ result->field.name_list = name_list;
+ result->field.name_list_count = name_list_count;
+ result->field.type_expression = type_expression;
+ return result;
+}
+
+gb_inline AstNode *make_procedure_type(Parser *p, Token token, AstNode *param_list, isize param_count, AstNode *results_list, isize result_count) {
+ AstNode *result = make_node(p, AstNode_ProcedureType);
+ result->procedure_type.token = token;
+ result->procedure_type.param_list = param_list;
+ result->procedure_type.param_count = param_count;
+ result->procedure_type.results_list = results_list;
+ result->procedure_type.result_count = result_count;
+ return result;
+}
+
+gb_inline AstNode *make_procedure_declaration(Parser *p, DeclarationKind kind, AstNode *name, AstNode *procedure_type, AstNode *body, AstNode *tag) {
+ AstNode *result = make_node(p, AstNode_ProcedureDeclaration);
+ result->procedure_declaration.kind = kind;
+ result->procedure_declaration.name = name;
+ result->procedure_declaration.procedure_type = procedure_type;
+ result->procedure_declaration.body = body;
+ result->procedure_declaration.tag = tag;
+ return result;
+}
+
+gb_inline AstNode *make_pointer_type(Parser *p, Token token, AstNode *type_expression) {
+ AstNode *result = make_node(p, AstNode_PointerType);
+ result->pointer_type.token = token;
+ result->pointer_type.type_expression = type_expression;
+ return result;
+}
+
+gb_inline AstNode *make_array_type(Parser *p, Token token, AstNode *count, AstNode *element) {
+ AstNode *result = make_node(p, AstNode_ArrayType);
+ result->array_type.token = token;
+ result->array_type.count = count;
+ result->array_type.element = element;
+ return result;
+}
+
+gb_inline AstNode *make_struct_type(Parser *p, Token token, AstNode *field_list, isize field_count) {
+ AstNode *result = make_node(p, AstNode_StructType);
+ result->struct_type.token = token;
+ result->struct_type.field_list = field_list;
+ result->struct_type.field_count = field_count;
+ return result;
+}
+
+gb_inline AstNode *make_type_declaration(Parser *p, Token token, AstNode *name, AstNode *type_expression) {
+ AstNode *result = make_node(p, AstNode_TypeDeclaration);
+ result->type_declaration.token = token;
+ result->type_declaration.name = name;
+ result->type_declaration.type_expression = type_expression;
+ return result;
+}
+
+
+#define print_parse_error(p, token, fmt, ...) print_parse_error_(p, __FUNCTION__, token, fmt, ##__VA_ARGS__)
+void print_parse_error_(Parser *p, char *function, Token token, char *fmt, ...) {
+ va_list va;
+
+ // NOTE(bill): Duplicate error, skip it
+ if (p->error_prev_line == token.line && p->error_prev_column == token.column) {
+ goto error;
+ }
+ p->error_prev_line = token.line;
+ p->error_prev_column = token.column;
+
+#if 1
+ gb_printf_err("%s()\n", function);
+#endif
+ va_start(va, fmt);
+ gb_printf_err("%s(%td:%td) %s\n",
+ p->tokenizer.fullpath, token.line, token.column,
+ gb_bprintf_va(fmt, va));
+ va_end(va);
+
+error:
+ p->error_count++;
+ // NOTE(bill): If there are too many errors, just quit
+ if (p->error_count > MAX_PARSER_ERROR_COUNT) {
+ gb_exit(1);
+ return;
+ }
+}
+
+
+gb_inline b32 next_token(Parser *p) {
+ if (p->cursor+1 < p->tokens + gb_array_count(p->tokens)) {
+ p->cursor++;
+ return true;
+ } else {
+ print_parse_error(p, p->cursor[0], "Token is EOF");
+ return false;
+ }
+}
+
+gb_inline Token expect_token(Parser *p, TokenKind kind) {
+ Token prev = p->cursor[0];
+ if (prev.kind != kind)
+ print_parse_error(p, p->cursor[0], "Expected `%s`, got `%s`",
+ token_kind_to_string(kind),
+ token_kind_to_string(prev.kind));
+ next_token(p);
+ return prev;
+}
+
+gb_inline Token expect_operator(Parser *p) {
+ Token prev = p->cursor[0];
+ if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1))
+ print_parse_error(p, p->cursor[0], "Expected an operator, got `%s`",
+ token_kind_to_string(prev.kind));
+ next_token(p);
+ return prev;
+}
+
+gb_inline Token expect_keyword(Parser *p) {
+ Token prev = p->cursor[0];
+ if (!gb_is_between(prev.kind, Token__KeywordBegin+1, Token__KeywordEnd-1))
+ print_parse_error(p, p->cursor[0], "Expected a keyword, got `%s`",
+ token_kind_to_string(prev.kind));
+ next_token(p);
+ return prev;
+}
+
+gb_inline b32 allow_token(Parser *p, TokenKind kind) {
+ Token prev = p->cursor[0];
+ if (prev.kind == kind) {
+ next_token(p);
+ return true;
+ }
+ return false;
+}
+
+
+
+b32 init_parser(Parser *p, char *filename) {
+ if (init_tokenizer(&p->tokenizer, filename)) {
+ gb_array_init(p->tokens, gb_heap_allocator());
+ for (;;) {
+ Token token = tokenizer_get_token(&p->tokenizer);
+ if (token.kind == Token_Invalid)
+ return false;
+ gb_array_append(p->tokens, token);
+
+ if (token.kind == Token_EOF)
+ break;
+ }
+
+ p->cursor = &p->tokens[0];
+
+ // NOTE(bill): Is this big enough or too small?
+ isize arena_size = gb_max(gb_size_of(AstNode), gb_size_of(AstScope));
+ arena_size *= 1.25*gb_array_count(p->tokens);
+ gb_arena_init_from_allocator(&p->arena, gb_heap_allocator(), arena_size);
+
+ open_ast_scope(p);
+ p->file_scope = p->curr_scope;
+
+ return true;
+ }
+ return false;
+}
+
+void destroy_parser(Parser *p) {
+ close_ast_scope(p);
+ gb_arena_free(&p->arena);
+ gb_array_free(p->tokens);
+ destroy_tokenizer(&p->tokenizer);
+}
+
+
+
+gb_internal void add_ast_entity(Parser *p, AstScope *scope, AstNode *declaration, AstNode *name_list) {
+ for (AstNode *n = name_list; n != NULL; n = n->next) {
+ GB_ASSERT_MSG(n->kind == AstNode_Identifier, "Identifier is already declared or resolved");
+
+ AstEntity *entity = make_ast_entity(p, n->identifier.token, declaration, scope);
+ n->identifier.entity = entity;
+
+ AstEntity *insert_entity = ast_scope_insert(scope, *entity);
+ if (insert_entity != NULL &&
+ !are_strings_equal(insert_entity->token.string, make_string("_"))) {
+ print_parse_error(p, entity->token,
+ "There is already a previous declaration of `%.*s` in the current scope at (%td:%td)",
+ LIT(insert_entity->token.string),
+ insert_entity->token.line, insert_entity->token.column);
+ }
+ }
+}
+
+AstNode *parse_expression(Parser *p, b32 lhs);
+
+AstNode *parse_identifier(Parser *p) {
+ Token identifier = expect_token(p, Token_Identifier);
+ return make_identifier(p, identifier);
+}
+
+AstNode *parse_rhs(Parser *p) {
+ return parse_expression(p, false);
+}
+
+AstNode *unparen_expression(AstNode *node) {
+ for (;;) {
+ if (node->kind != AstNode_ParenExpression)
+ return node;
+ node = node->paren_expression.expression;
+ }
+}
+
+AstNode *parse_atom_expression(Parser *p, b32 lhs) {
+ AstNode *operand = NULL; // Operand
+ switch (p->cursor[0].kind) {
+ case Token_Identifier:
+ operand = parse_identifier(p);
+ if (!lhs) {
+ // TODO(bill): Handle?
+ }
+ break;
+
+ case Token_Integer:
+ case Token_Float:
+ case Token_String:
+ case Token_Rune:
+ operand = make_basic_literal(p, p->cursor[0]);
+ next_token(p);
+ break;
+
+ case Token_OpenParen: {
+ Token open, close;
+ // NOTE(bill): Skip the Paren Expression
+ open = expect_token(p, Token_OpenParen);
+ operand = parse_rhs(p);
+ close = expect_token(p, Token_CloseParen);
+ operand = make_paren_expression(p, operand, open, close);
+ } break;
+ }
+
+ b32 loop = true;
+
+ while (loop) {
+ switch (p->cursor[0].kind) {
+ case Token_OpenParen: {
+ if (lhs) {
+ // TODO(bill): Handle this shit! Is this even allowed in this language?!
+ }
+ AstNode *arg_list = NULL;
+ AstNode *arg_list_curr = NULL;
+ isize arg_list_count = 0;
+ Token open_paren, close_paren;
+
+ open_paren = expect_token(p, Token_OpenParen);
+
+ while (p->cursor[0].kind != Token_CloseParen &&
+ p->cursor[0].kind != Token_EOF) {
+ if (p->cursor[0].kind == Token_Comma)
+ print_parse_error(p, p->cursor[0], "Expected an expression not a ,");
+
+ DLIST_APPEND(arg_list, arg_list_curr, parse_rhs(p));
+ arg_list_count++;
+
+ if (p->cursor[0].kind != Token_Comma) {
+ if (p->cursor[0].kind == Token_CloseParen)
+ break;
+ }
+
+ next_token(p);
+ }
+
+ close_paren = expect_token(p, Token_CloseParen);
+
+ operand = make_call_expression(p, operand, arg_list, arg_list_count, open_paren, close_paren);
+ } break;
+
+ case Token_Period: {
+ Token token = p->cursor[0];
+ next_token(p);
+ if (lhs) {
+ // TODO(bill): handle this
+ }
+ switch (p->cursor[0].kind) {
+ case Token_Identifier:
+ operand = make_selector_expression(p, token, operand, parse_identifier(p));
+ break;
+ default: {
+ Token token = p->cursor[0];
+ print_parse_error(p, token, "Expected a selector");
+ next_token(p);
+ operand = make_selector_expression(p, token, operand, NULL);
+ } break;
+ }
+ } break;
+
+ case Token_OpenBracket: {
+ if (lhs) {
+ // TODO(bill): Handle this
+ }
+ AstNode *value;
+ Token open, close;
+
+ open = expect_token(p, Token_OpenBracket);
+ value = parse_expression(p, false);
+ close = expect_token(p, Token_CloseBracket);
+
+ operand = make_index_expression(p, operand, value, open, close);
+ } break;
+
+ case Token_Pointer: // Deference
+ operand = make_dereference_expression(p, operand, expect_token(p, Token_Pointer));
+ break;
+
+ default:
+ loop = false;
+ break;
+ }
+
+ lhs = false; // NOTE(bill): 'tis not lhs anymore
+ }
+
+ return operand;
+}
+
+AstNode *parse_type(Parser *p);
+
+AstNode *parse_unary_expression(Parser *p, b32 lhs) {
+ switch (p->cursor[0].kind) {
+ case Token_Pointer:
+ case Token_Add:
+ case Token_Sub:
+ case Token_Not:
+ case Token_Xor: {
+ AstNode *operand;
+ Token op = p->cursor[0];
+ next_token(p);
+ operand = parse_unary_expression(p, false);
+ return make_unary_expression(p, op, operand);
+ } break;
+
+ case Token_cast: {
+ AstNode *type_expression, *operand;
+ Token token = p->cursor[0];
+ next_token(p);
+ expect_token(p, Token_OpenParen);
+ type_expression = parse_type(p);
+ expect_token(p, Token_CloseParen);
+ operand = parse_unary_expression(p, false);
+ return make_cast_expression(p, token, type_expression, operand);
+ } break;
+ }
+
+ return parse_atom_expression(p, lhs);
+}
+
+AstNode *parse_binary_expression(Parser *p, b32 lhs, i32 prec_in) {
+ AstNode *expression = parse_unary_expression(p, lhs);
+ for (i32 prec = token_precedence(p->cursor[0]); prec >= prec_in; prec--) {
+ for (;;) {
+ AstNode *right;
+ Token op = p->cursor[0];
+ i32 op_prec = token_precedence(op);
+ if (op_prec != prec)
+ break;
+ expect_operator(p); // NOTE(bill): error checks too
+ if (lhs) {
+ // TODO(bill): error checking
+ lhs = false;
+ }
+ right = parse_binary_expression(p, false, prec+1);
+ if (!right)
+ print_parse_error(p, op, "Expected expression on the right hand side of the binary operator");
+ expression = make_binary_expression(p, op, expression, right);
+ }
+ }
+ return expression;
+}
+
+AstNode *parse_expression(Parser *p, b32 lhs) {
+ return parse_binary_expression(p, lhs, 0+1);
+}
+
+
+AstNode *parse_expression_list(Parser *p, b32 lhs, isize *list_count_) {
+ AstNode *list_root = NULL;
+ AstNode *list_curr = NULL;
+ isize list_count = 0;
+
+ do {
+ DLIST_APPEND(list_root, list_curr, parse_expression(p, lhs));
+ list_count++;
+ if (p->cursor[0].kind != Token_Comma ||
+ p->cursor[0].kind == Token_EOF)
+ break;
+ next_token(p);
+ } while (true);
+
+ if (list_count_) *list_count_ = list_count;
+
+ return list_root;
+}
+
+AstNode *parse_lhs_expression_list(Parser *p, isize *list_count) {
+ return parse_expression_list(p, true, list_count);
+}
+
+AstNode *parse_rhs_expression_list(Parser *p, isize *list_count) {
+ return parse_expression_list(p, false, list_count);
+}
+
+AstNode *parse_declaration(Parser *p, AstNode *name_list, isize name_list_count);
+
+AstNode *parse_simple_statement(Parser *p) {
+ isize lhs_count = 0, rhs_count = 0;
+ AstNode *lhs_expression_list = parse_lhs_expression_list(p, &lhs_count);
+
+ AstNode *statement = NULL;
+ Token token = p->cursor[0];
+ switch (token.kind) {
+ case Token_Eq:
+ case Token_AddEq:
+ case Token_SubEq:
+ case Token_MulEq:
+ case Token_QuoEq:
+ case Token_ModEq:
+ case Token_AndEq:
+ case Token_OrEq:
+ case Token_XorEq:
+ case Token_AndNotEq:
+ case Token_CmpAndEq:
+ case Token_CmpOrEq:
+ {
+ if (p->curr_scope == p->file_scope) {
+ print_parse_error(p, p->cursor[0], "You cannot use a simple statement in the file scope");
+ return make_bad_statement(p, p->cursor[0], p->cursor[0]);
+ }
+ next_token(p);
+ AstNode *rhs_expression_list = parse_rhs_expression_list(p, &rhs_count);
+ if (rhs_expression_list == NULL) {
+ print_parse_error(p, token, "No right-hand side in assignment statement.");
+ return make_bad_statement(p, token, p->cursor[0]);
+ }
+ return make_assign_statement(p, token,
+ lhs_expression_list, lhs_count,
+ rhs_expression_list, rhs_count);
+ } break;
+
+ case Token_Colon: // Declare
+ return parse_declaration(p, lhs_expression_list, lhs_count);
+ }
+
+ if (lhs_count > 1) {
+ print_parse_error(p, token, "Expected 1 expression");
+ return make_bad_statement(p, token, p->cursor[0]);
+ }
+
+ token = p->cursor[0];
+ switch (token.kind) {
+ case Token_Increment:
+ case Token_Decrement:
+ if (p->curr_scope == p->file_scope) {
+ print_parse_error(p, p->cursor[0], "You cannot use a simple statement in the file scope");
+ return make_bad_statement(p, p->cursor[0], p->cursor[0]);
+ }
+ statement = make_inc_dec_statement(p, token, lhs_expression_list);
+ next_token(p);
+ return statement;
+ }
+
+ return make_expression_statement(p, lhs_expression_list);
+}
+
+AstNode *parse_statement_list(Parser *p, isize *list_count_);
+AstNode *parse_statement(Parser *p);
+AstNode *parse_body(Parser *p, AstScope *scope);
+
+AstNode *parse_block_statement(Parser *p) {
+ if (p->curr_scope == p->file_scope) {
+ print_parse_error(p, p->cursor[0], "You cannot use a block statement in the file scope");
+ return make_bad_statement(p, p->cursor[0], p->cursor[0]);
+ }
+ AstNode *block_statement;
+
+ open_ast_scope(p);
+ block_statement = parse_body(p, p->curr_scope);
+ close_ast_scope(p);
+ return block_statement;
+}
+
+AstNode *convert_statement_to_expression(Parser *p, AstNode *statement, char *kind) {
+ if (statement == NULL)
+ return NULL;
+
+ if (statement->kind == AstNode_ExpressionStatement)
+ return statement->expression_statement.expression;
+
+ print_parse_error(p, p->cursor[0], "Expected `%s`, found a simple statement.", kind);
+ return make_bad_expression(p, p->cursor[0], p->cursor[1]);
+}
+
+AstNode *parse_identfier_list(Parser *p, isize *list_count_) {
+ AstNode *list_root = NULL;
+ AstNode *list_curr = NULL;
+ isize list_count = 0;
+
+ do {
+ DLIST_APPEND(list_root, list_curr, parse_identifier(p));
+ list_count++;
+ if (p->cursor[0].kind != Token_Comma ||
+ p->cursor[0].kind == Token_EOF)
+ break;
+ next_token(p);
+ } while (true);
+
+ if (list_count_) *list_count_ = list_count;
+
+ return list_root;
+}
+
+
+AstNode *parse_identifier_or_type(Parser *p);
+
+AstNode *parse_type_attempt(Parser *p) {
+ AstNode *type = parse_identifier_or_type(p);
+ if (type != NULL) {
+ // TODO(bill): Handle?
+ }
+ return type;
+}
+
+AstNode *parse_type(Parser *p) {
+ AstNode *type = parse_type_attempt(p);
+ if (type == NULL) {
+ Token token = p->cursor[0];
+ print_parse_error(p, token, "Expected a type");
+ next_token(p);
+ return make_bad_expression(p, token, p->cursor[0]);
+ }
+ return type;
+}
+
+AstNode *parse_field_declaration(Parser *p, AstScope *scope) {
+ AstNode *name_list = NULL;
+ isize name_list_count = 0;
+ name_list = parse_lhs_expression_list(p, &name_list_count);
+ if (name_list_count == 0)
+ print_parse_error(p, p->cursor[0], "Empty field declaration");
+
+ expect_token(p, Token_Colon);
+
+ AstNode *type_expression = parse_type_attempt(p);
+ if (type_expression == NULL)
+ print_parse_error(p, p->cursor[0], "Expected a type for this field declaration");
+
+ AstNode *field = make_field(p, name_list, name_list_count, type_expression);
+ add_ast_entity(p, scope, field, name_list);
+ return field;
+}
+
+AstNode *parse_procedure_type(Parser *p, AstScope **scope_) {
+ Token token = expect_token(p, Token_proc);
+ AstScope *scope = make_ast_scope(p, p->file_scope); // Procedure's scope
+ AstNode *params = NULL;
+ AstNode *results = NULL;
+ isize param_count = 0;
+ isize result_count = 0;
+
+ expect_token(p, Token_OpenParen);
+ if (p->cursor[0].kind != Token_CloseParen) {
+ // IMPORTANT TODO(bill): Allow for lhs-expression list style types
+ // proc(x, y: int, a, b: f32);
+ AstNode *params_curr = NULL;
+ do {
+ AstNode *type_node = parse_type(p);
+ DLIST_APPEND(params, params_curr, type_node);
+ param_count++;
+ if (p->cursor[0].kind != Token_Comma ||
+ p->cursor[0].kind == Token_EOF)
+ break;
+ next_token(p);
+ } while (true);
+ }
+ expect_token(p, Token_CloseParen);
+
+ // NOTE(bill): Has results
+ if (allow_token(p, Token_ArrowRight)) {
+ if (p->cursor[0].kind != Token_Semicolon) {
+ AstNode *results_curr = NULL;
+ do {
+ DLIST_APPEND(results, results_curr, parse_type(p));
+ result_count++;
+ if (p->cursor[0].kind != Token_Comma ||
+ p->cursor[0].kind == Token_EOF)
+ break;
+ next_token(p);
+ } while (true);
+ } else {
+ print_parse_error(p, p->cursor[0], "Expected at least one type after the `->`");
+ }
+ }
+
+ if (scope_) *scope_ = scope;
+ return make_procedure_type(p, token, params, param_count, results, result_count);
+}
+
+
+AstNode *parse_identifier_or_type(Parser *p) {
+ switch (p->cursor[0].kind) {
+ case Token_Identifier:
+ return parse_identifier(p);
+
+ case Token_Pointer:
+ return make_pointer_type(p, expect_token(p, Token_Pointer), parse_type(p));
+
+ case Token_OpenBracket: {
+ Token token = expect_token(p, Token_OpenBracket);
+ AstNode *count_expression = NULL;
+
+ if (p->cursor[0].kind != Token_CloseBracket)
+ count_expression = parse_rhs(p);
+ expect_token(p, Token_CloseBracket);
+ return make_array_type(p, token, count_expression, parse_type(p));
+ }
+
+ case Token_struct: {
+ Token token = expect_token(p, Token_struct);
+ Token open, close;
+ AstNode *field_list = NULL;
+ AstNode *field_list_curr = NULL;
+ isize field_list_count = 0;
+
+ open = expect_token(p, Token_OpenBrace);
+
+ AstScope *scope = make_ast_scope(p, NULL); // NOTE(bill): The struct needs its own scope with NO parent
+ while (p->cursor[0].kind == Token_Identifier ||
+ p->cursor[0].kind == Token_Mul) {
+ DLIST_APPEND(field_list, field_list_curr, parse_field_declaration(p, scope));
+ expect_token(p, Token_Semicolon);
+ field_list_count++;
+ }
+ destroy_ast_scope(scope);
+
+ close = expect_token(p, Token_CloseBrace);
+
+ return make_struct_type(p, token, field_list, field_list_count);
+ }
+
+ case Token_proc:
+ return parse_procedure_type(p, NULL);
+
+
+ case Token_OpenParen: {
+ // NOTE(bill): Skip the paren expression
+ AstNode *type_expression;
+ Token open, close;
+ open = expect_token(p, Token_OpenParen);
+ type_expression = parse_type(p);
+ close = expect_token(p, Token_CloseParen);
+ return make_paren_expression(p, type_expression, open, close);
+ }
+
+ case Token_Colon:
+ case Token_Eq:
+ break;
+
+ default:
+ print_parse_error(p, p->cursor[0], "Expected type after type separator `:`");
+ break;
+ }
+
+ return NULL;
+}
+
+AstNode *parse_parameters(Parser *p, AstScope *scope, isize *param_count_, b32 use_parens) {
+ AstNode *param_list = NULL;
+ AstNode *param_list_curr = NULL;
+ isize param_count = 0;
+ if (use_parens) expect_token(p, Token_OpenParen);
+ while (p->cursor[0].kind != Token_CloseParen) {
+ DLIST_APPEND(param_list, param_list_curr, parse_field_declaration(p, scope));
+ param_count++;
+ if (p->cursor[0].kind != Token_Comma)
+ break;
+ next_token(p);
+ }
+ if (use_parens) expect_token(p, Token_CloseParen);
+
+ if (param_count_) *param_count_ = param_count;
+ return param_list;
+}
+
+AstNode *parse_results(Parser *p, AstScope *scope, isize *result_count_) {
+ AstNode *result_list = NULL;
+ AstNode *result_list_curr = NULL;
+ isize result_count = 0;
+
+ if (allow_token(p, Token_ArrowRight)) {
+ while (p->cursor[0].kind != Token_OpenBrace &&
+ p->cursor[0].kind != Token_Semicolon) {
+ DLIST_APPEND(result_list, result_list_curr, parse_type(p));
+ result_count++;
+ if (p->cursor[0].kind != Token_Comma)
+ break;
+ next_token(p);
+ }
+
+ if (result_count == 0)
+ print_parse_error(p, p->cursor[0], "Expected return types after `->`");
+ }
+
+ if (result_count_) *result_count_ = result_count;
+ return result_list;
+}
+
+void parse_procedure_signature(Parser *p, AstScope *scope,
+ AstNode **param_list, isize *param_count,
+ AstNode **result_list, isize *result_count) {
+ *param_list = parse_parameters(p, scope, param_count, true);
+ *result_list = parse_results(p, scope, result_count);
+}
+
+AstNode *parse_body(Parser *p, AstScope *scope) {
+ AstNode *statement_list = NULL;
+ isize statement_list_count = 0;
+ Token open, close;
+ open = expect_token(p, Token_OpenBrace);
+ statement_list = parse_statement_list(p, &statement_list_count);
+ close = expect_token(p, Token_CloseBrace);
+
+ return make_block_statement(p, statement_list, statement_list_count, open, close);
+}
+
+
+AstNode *parse_tag_expression(Parser *p) {
+ Token token = expect_token(p, Token_Hash);
+ Token name = expect_token(p, Token_Identifier);
+ return make_tag_expression(p, token, name);
+}
+
+AstNode *parse_procedure_declaration(Parser *p, Token proc_token, AstNode *name, DeclarationKind kind) {
+ AstNode *param_list = NULL;
+ AstNode *result_list = NULL;
+ isize param_count = 0;
+ isize result_count = 0;
+
+ AstScope *scope = open_ast_scope(p);
+
+ parse_procedure_signature(p, scope, &param_list, &param_count, &result_list, &result_count);
+
+ AstNode *body = NULL, *tag = NULL;
+ if (p->cursor[0].kind == Token_OpenBrace) {
+ body = parse_body(p, scope);
+ } else if (p->cursor[0].kind == Token_Hash) {
+ tag = parse_tag_expression(p);
+ }
+
+ close_ast_scope(p);
+
+ AstNode *proc_type = make_procedure_type(p, proc_token, param_list, param_count, result_list, result_count);
+ return make_procedure_declaration(p, kind, name, proc_type, body, tag);
+}
+
+AstNode *parse_declaration(Parser *p, AstNode *name_list, isize name_list_count) {
+ AstNode *value_list = NULL;
+ AstNode *type_expression = NULL;
+ isize value_list_count = 0;
+ if (allow_token(p, Token_Colon)) {
+ type_expression = parse_identifier_or_type(p);
+ } else if (p->cursor[0].kind != Token_Eq && p->cursor[0].kind != Token_Semicolon) {
+ print_parse_error(p, p->cursor[0], "Expected type separator `:` or `=`");
+ }
+
+ DeclarationKind declaration_kind = Declaration_Mutable;
+
+ if (p->cursor[0].kind == Token_Eq ||
+ p->cursor[0].kind == Token_Colon) {
+ if (p->cursor[0].kind == Token_Colon)
+ declaration_kind = Declaration_Immutable;
+ next_token(p);
+
+ if (p->cursor[0].kind == Token_proc) { // NOTE(bill): Procedure declarations
+ Token proc_token = p->cursor[0];
+ AstNode *name = name_list;
+ if (name_list_count != 1) {
+ print_parse_error(p, p->cursor[0], "You can only declare one procedure at a time (at the moment)");
+ return make_bad_declaration(p, name->identifier.token, p->cursor[0]);
+ }
+
+ // TODO(bill): Allow for mutable procedures
+ if (declaration_kind != Declaration_Immutable) {
+ print_parse_error(p, p->cursor[0], "Only immutable procedures are supported (at the moment)");
+ return make_bad_declaration(p, name->identifier.token, p->cursor[0]);
+ }
+ next_token(p); // Skip `proc` token
+
+ AstNode *procedure_declaration = parse_procedure_declaration(p, proc_token, name, declaration_kind);
+ add_ast_entity(p, p->curr_scope, procedure_declaration, name_list);
+ return procedure_declaration;
+
+ } else {
+ value_list = parse_rhs_expression_list(p, &value_list_count);
+ if (value_list_count > name_list_count) {
+ print_parse_error(p, p->cursor[0], "Too many values on the right hand side of the declaration");
+ } else if (value_list_count < name_list_count &&
+ declaration_kind == Declaration_Immutable) {
+ print_parse_error(p, p->cursor[0], "All constant declarations must be defined");
+ } else if (value_list == NULL) {
+ print_parse_error(p, p->cursor[0], "Expected an expression for this declaration");
+ }
+ }
+ }
+
+ if (declaration_kind == Declaration_Mutable) {
+ if (type_expression == NULL && value_list == NULL) {
+ print_parse_error(p, p->cursor[0], "Missing variable type or initialization");
+ return make_bad_declaration(p, p->cursor[0], p->cursor[0]);
+ }
+ } else if (declaration_kind == Declaration_Immutable) {
+ if (type_expression == NULL && value_list == NULL && name_list_count > 0) {
+ print_parse_error(p, p->cursor[0], "Missing constant value");
+ return make_bad_declaration(p, p->cursor[0], p->cursor[0]);
+ }
+ } else {
+ print_parse_error(p, p->cursor[0], "Unknown type of variable declaration");
+ return make_bad_declaration(p, p->cursor[0], p->cursor[0]);
+ }
+
+ AstNode *variable_declaration = make_variable_declaration(p, declaration_kind, name_list, name_list_count, type_expression, value_list, value_list_count);
+ add_ast_entity(p, p->curr_scope, variable_declaration, name_list);
+ return variable_declaration;
+}
+
+
+AstNode *parse_if_statement(Parser *p) {
+ if (p->curr_scope == p->file_scope) {
+ print_parse_error(p, p->cursor[0], "You cannot use an if statement in the file scope");
+ return make_bad_statement(p, p->cursor[0], p->cursor[0]);
+ }
+
+ Token token = expect_token(p, Token_if);
+ AstNode *cond, *body, *else_statement;
+
+ open_ast_scope(p);
+
+ cond = convert_statement_to_expression(p, parse_simple_statement(p), "boolean expression");
+
+ if (cond == NULL) {
+ print_parse_error(p, p->cursor[0], "Expected condition for if statement");
+ }
+
+ body = parse_block_statement(p);
+ else_statement = NULL;
+ if (allow_token(p, Token_else)) {
+ switch (p->cursor[0].kind) {
+ case Token_if:
+ else_statement = parse_if_statement(p);
+ break;
+ case Token_OpenBrace:
+ else_statement = parse_block_statement(p);
+ break;
+ default:
+ print_parse_error(p, p->cursor[0], "Expected if statement block statement");
+ else_statement = make_bad_statement(p, p->cursor[0], p->cursor[1]);
+ break;
+ }
+ }
+
+ close_ast_scope(p);
+ return make_if_statement(p, token, cond, body, else_statement);
+}
+
+AstNode *parse_return_statement(Parser *p) {
+ if (p->curr_scope == p->file_scope) {
+ print_parse_error(p, p->cursor[0], "You cannot use a return statement in the file scope");
+ return make_bad_statement(p, p->cursor[0], p->cursor[0]);
+ }
+
+ Token token = expect_token(p, Token_return);
+ AstNode *result = NULL;
+ isize result_count = 0;
+ if (p->cursor[0].kind != Token_Semicolon)
+ result = parse_rhs_expression_list(p, &result_count);
+ expect_token(p, Token_Semicolon);
+
+ return make_return_statement(p, token, result, result_count);
+}
+
+AstNode *parse_for_statement(Parser *p) {
+ if (p->curr_scope == p->file_scope) {
+ print_parse_error(p, p->cursor[0], "You cannot use a for statement in the file scope");
+ return make_bad_statement(p, p->cursor[0], p->cursor[0]);
+ }
+
+ Token token = expect_token(p, Token_for);
+ open_ast_scope(p);
+ AstNode *init_statement = NULL, *cond = NULL, *end_statement = NULL, *body = NULL;
+
+ if (p->cursor[0].kind != Token_OpenBrace) {
+ cond = parse_simple_statement(p);
+ if (is_ast_node_complex_statement(cond)) {
+ print_parse_error(p, p->cursor[0],
+ "You are not allowed that type of statement in a for statement, it's too complex!");
+ }
+
+ if (allow_token(p, Token_Semicolon)) {
+ init_statement = cond;
+ cond = NULL;
+ if (p->cursor[0].kind != Token_Semicolon) {
+ cond = parse_simple_statement(p);
+ }
+ expect_token(p, Token_Semicolon);
+ if (p->cursor[0].kind != Token_OpenBrace) {
+ end_statement = parse_simple_statement(p);
+ }
+ }
+ }
+ body = parse_block_statement(p);
+
+ close_ast_scope(p);
+
+ return make_for_statement(p, token, init_statement, cond, end_statement, body);
+}
+
+AstNode *parse_defer_statement(Parser *p) {
+ if (p->curr_scope == p->file_scope) {
+ print_parse_error(p, p->cursor[0], "You cannot use a defer statement in the file scope");
+ return make_bad_statement(p, p->cursor[0], p->cursor[0]);
+ }
+
+ Token token = expect_token(p, Token_defer);
+ AstNode *statement = parse_statement(p);
+ switch (statement->kind) {
+ case AstNode_EmptyStatement:
+ print_parse_error(p, token, "Empty statement after defer (e.g. `;`)");
+ break;
+ case AstNode_DeferStatement:
+ print_parse_error(p, token, "You cannot defer a defer statement");
+ break;
+ case AstNode_ReturnStatement:
+ print_parse_error(p, token, "You cannot a return statement");
+ break;
+ }
+
+ return make_defer_statement(p, token, statement);
+}
+
+AstNode *parse_type_declaration(Parser *p) {
+ Token token = expect_token(p, Token_type);
+ AstNode *name = parse_identifier(p);
+ expect_token(p, Token_Colon);
+ AstNode *type_expression = parse_type(p);
+
+ AstNode *type_declaration = make_type_declaration(p, token, name, type_expression);
+
+ if (type_expression->kind != AstNode_StructType)
+ expect_token(p, Token_Semicolon);
+
+ return type_declaration;
+}
+
+AstNode *parse_statement(Parser *p) {
+ AstNode *s = NULL;
+ Token token = p->cursor[0];
+ switch (token.kind) {
+ case Token_type:
+ return parse_type_declaration(p);
+
+ // Operands
+ case Token_Identifier:
+ case Token_Integer:
+ case Token_Float:
+ case Token_Rune:
+ case Token_String:
+ case Token_OpenParen:
+ // Unary Operators
+ case Token_Add:
+ case Token_Sub:
+ case Token_Xor:
+ case Token_Not:
+ s = parse_simple_statement(p);
+ if (s->kind != AstNode_ProcedureDeclaration && !allow_token(p, Token_Semicolon)) {
+ print_parse_error(p, p->cursor[0], "Expected `;` after statement, got `%s`", token_kind_to_string(p->cursor[0].kind));
+ }
+ return s;
+
+ // TODO(bill): other keywords
+ case Token_if: return parse_if_statement(p);
+ case Token_return: return parse_return_statement(p);
+ case Token_for: return parse_for_statement(p);
+ case Token_defer: return parse_defer_statement(p);
+ // case Token_match:
+ // case Token_case:
+
+ case Token_OpenBrace: return parse_block_statement(p);
+ // case Token_CloseBrace: s = make_empty_statement(p, token); break;
+
+ case Token_Semicolon:
+ s = make_empty_statement(p, token);
+ next_token(p);
+ return s;
+ }
+
+ print_parse_error(p, token, "Expected a statement, got `%s`", token_kind_to_string(token.kind));
+ return make_bad_statement(p, token, p->cursor[0]);
+}
+
+AstNode *parse_statement_list(Parser *p, isize *list_count_) {
+ AstNode *list_root = NULL;
+ AstNode *list_curr = NULL;
+ isize list_count = 0;
+
+ while (p->cursor[0].kind != Token_case &&
+ p->cursor[0].kind != Token_default &&
+ p->cursor[0].kind != Token_CloseBrace &&
+ p->cursor[0].kind != Token_EOF) {
+ DLIST_APPEND(list_root, list_curr, parse_statement(p));
+ list_count++;
+ }
+
+ if (list_count_) *list_count_ = list_count;
+
+ return list_root;
+}