ox

The Ox programming language, compiler and tools (WIP)
Log | Files | Refs | README | LICENSE

commit 7e8dfdfdc7d4c170a92bff7f518a4ab633821ce9
parent f7946e20cdc617207a36a264500f6167db548673
Author: citbl <citbl@citbl.org>
Date:   Thu, 25 Jun 2026 17:34:59 +1000

parser refac

Diffstat:
Msrc/parser/ast.c | 21+++++++++++++++++++--
Msrc/parser/expr.c | 31+++++++++++++++++++++++++++++--
Msrc/parser/parser.c | 104++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/parser/parser_utils.c | 36++++++++++++++++++++++++++++++++++++
4 files changed, 163 insertions(+), 29 deletions(-)

diff --git a/src/parser/ast.c b/src/parser/ast.c @@ -92,7 +92,7 @@ make_string_node(Parser* par) } Node* -make_binary_node(OpType op, Node* lhs, Node* rhs) +make_binary_node(Parser* par, OpType op, Node* lhs, Node* rhs) { Node* node = (Node*)calloc(1, sizeof(Node)); if (node == NULL) panic("make_binary_node: could not alloc"); @@ -102,6 +102,9 @@ make_binary_node(OpType op, Node* lhs, Node* rhs) node->data.binary_expr.op = op; node->data.binary_expr.lhs = lhs; node->data.binary_expr.rhs = rhs; + node->filename = par->filename; + node->col = lhs->col; + node->line = lhs->line; return node; } @@ -121,7 +124,6 @@ make_call_node(Node* callee, NodeVec args) { Node* call = (Node*)calloc(1, sizeof(Node)); if (call == NULL) panic("make_call_node: could not alloc"); - assert(call != NULL); call->type = NODE_CALL_EXPR; call->scope = NULL; call->data.call_expr.callee = callee; @@ -130,3 +132,18 @@ make_call_node(Node* callee, NodeVec args) call->data.call_expr.len = args.len; return call; } + +Node* +make_struct_initialisation(Node* node, NodeVec fields) +{ + Node* s = (Node*)calloc(1, sizeof(Node)); + if (s == NULL) panic("make_struct_initialisation: could not alloc"); + s->type = NODE_STRUCT_IMPL; + s->scope = NULL; + s->data.struct_impl.name = node->data.ident.name; + s->data.struct_impl.fields = fields.items; + s->data.struct_impl.cap = fields.cap; + s->data.struct_impl.len = fields.len; + s->data.struct_impl.name = (Span) { 0 }; + return s; +} diff --git a/src/parser/expr.c b/src/parser/expr.c @@ -98,6 +98,29 @@ parse_ident(Parser* par) } NodeVec +parse_struct_fields_initialisation(Parser* par) +{ + NodeVec v = { 0 }; + if (peek(par).type == TOKEN_RBRACE) return v; + v.cap = 8; + v.items = (Node**)calloc(v.cap, sizeof(Node*)); + if (v.items == NULL) panic("parse_struct_fields_initialisation: could not alloc"); + for (;;) { + Node* arg = parse_expression(par); + + if (v.len == v.cap) { + v.cap *= 2; + v.items = (Node**)realloc(v.items, v.cap * sizeof(Node*)); + } + + v.items[v.len++] = arg; + + if (!match(par, TOKEN_COMMA)) break; // found `)` instead of `,` + } + return v; +} + +NodeVec parse_func_arguments(Parser* par) { NodeVec v = { 0 }; @@ -142,10 +165,14 @@ parse_postfix(Parser* par) // } else if (match(par, TOKEN_ARROW)) { // Token id = expect(par, TOKEN_IDENTIFIER); // expr = make_ptrmember_node(expr, id); - } else if (match(par, TOKEN_LPAREN)) { - NodeVec args = parse_func_arguments(par); // parse func call args + } else if (match(par, TOKEN_LPAREN)) { // IDENT( ... parse func call + NodeVec args = parse_func_arguments(par); expect(par, TOKEN_RPAREN); node = make_call_node(node, args); + } else if (match(par, TOKEN_LBRACE)) { // IDENT{ ... parse struct init + NodeVec fields = parse_struct_fields_initialisation(par); + expect(par, TOKEN_RBRACE); + node = make_struct_initialisation(node, fields); } else { break; } diff --git a/src/parser/parser.c b/src/parser/parser.c @@ -46,9 +46,7 @@ consume(Parser* par) bool check(Parser* p, TokenType type) -{ - return (peek(p).type == type); -} +{ return (peek(p).type == type); } Token expect(Parser* par, TokenType type) @@ -88,13 +86,13 @@ parse_multiplicative(Parser* par) for (;;) { if (match(par, TOKEN_STAR)) { Node* rhs = parse_unary(par); - node = make_binary_node(OP_MUL, node, rhs); + node = make_binary_node(par, OP_MUL, node, rhs); } else if (match(par, TOKEN_SLASH)) { Node* rhs = parse_unary(par); - node = make_binary_node(OP_DIV, node, rhs); + node = make_binary_node(par, OP_DIV, node, rhs); } else if (match(par, TOKEN_PERCENT)) { Node* rhs = parse_unary(par); - node = make_binary_node(OP_MOD, node, rhs); + node = make_binary_node(par, OP_MOD, node, rhs); } else break; } @@ -109,10 +107,10 @@ parse_additive(Parser* par) for (;;) { if (match(par, TOKEN_PLUS)) { Node* rhs = parse_multiplicative(par); - node = make_binary_node(OP_PLUS, node, rhs); + node = make_binary_node(par, OP_PLUS, node, rhs); } else if (match(par, TOKEN_MINUS)) { Node* rhs = parse_multiplicative(par); - node = make_binary_node(OP_MINUS, node, rhs); + node = make_binary_node(par, OP_MINUS, node, rhs); } else break; } @@ -126,16 +124,16 @@ parse_relational(Parser* par) for (;;) { if (match(par, TOKEN_LT)) { Node* rhs = parse_additive(par); - node = make_binary_node(OP_LT, node, rhs); + node = make_binary_node(par, OP_LT, node, rhs); } else if (match(par, TOKEN_LT_EQ)) { Node* rhs = parse_additive(par); - node = make_binary_node(OP_LT_EQ, node, rhs); + node = make_binary_node(par, OP_LT_EQ, node, rhs); } else if (match(par, TOKEN_GT)) { Node* rhs = parse_additive(par); - node = make_binary_node(OP_GT, node, rhs); + node = make_binary_node(par, OP_GT, node, rhs); } else if (match(par, TOKEN_GT_EQ)) { Node* rhs = parse_additive(par); - node = make_binary_node(OP_GT_EQ, node, rhs); + node = make_binary_node(par, OP_GT_EQ, node, rhs); } else break; } @@ -171,10 +169,10 @@ parse_equality(Parser* par) for (;;) { if (match(par, TOKEN_EQUALITY)) { // "==" Node* rhs = parse_relational(par); - node = make_binary_node(OP_EQUALITY, node, rhs); + node = make_binary_node(par, OP_EQUALITY, node, rhs); } else if (match(par, TOKEN_INEQUALITY)) { // "!=" Node* rhs = parse_relational(par); - node = make_binary_node(OP_INEQUALITY, node, rhs); + node = make_binary_node(par, OP_INEQUALITY, node, rhs); } else { // do not error out for other things like `=`, `&`, etc. let it pass. break; @@ -191,16 +189,14 @@ parse_assignment_expr(Parser* par) if (match(par, TOKEN_EQUAL)) { Node* right = parse_assignment_expr(par); // right-associative if (left->type != NODE_IDENT) panic("invalid assignment target"); - return make_binary_node(OP_ASSIGN, left, right); + return make_binary_node(par, OP_ASSIGN, left, right); } return left; } Node* parse_expression(Parser* par) -{ - return parse_assignment_expr(par); -} +{ return parse_assignment_expr(par); } Node* parse_expression_statement(Parser* par) @@ -227,7 +223,7 @@ parse_statement(Parser* par) if (tok.type == TOKEN_LBRACE) { consume(par); - return parse_block(par); + return parse_block(par, false); } bool tok_is_a_type = (tok.type == TOKEN_IDENT || tok.type == TOKEN_VARIADIC || tok.type == TOKEN_COMP_TIME); @@ -262,7 +258,34 @@ parse_statement(Parser* par) } Node* -parse_block(Parser* par) +parse_field_decl(Parser* par) +{ + // TODO there is an argument to make this like parse_variable_decl; and use the same code as of line ~451 + Node* type_node = parse_type(par); // consumes the type (e.g., "float") + Token ident = expect(par, TOKEN_IDENT); // variable or function name + Node* var = calloc(1, sizeof(Node)); + if (var == NULL) panic("parse_field_decl: var: could not alloc"); + var->type = NODE_FIELD_DECL; + var->scope = NULL; + var->data.field_decl.name = (Span) { ident.start, ident.end }; + var->data.field_decl.type = type_node; + var->filename = par->filename; + var->line = ident.line; + var->col = ident.col; + Token next_tok = peek(par); + if (next_tok.type == TOKEN_EQUAL) { + consume(par); // consume '=' + var->data.field_decl.init = parse_expression(par); // TODO we need to lock down what type of expression this could be, + // field init in a struct shouldn't be too dynamic. + } else { + var->data.field_decl.init = NULL; + } + expect(par, TOKEN_SEMICOLON); + return var; +} + +Node* +parse_block(Parser* par, bool is_struct_block) { Node* stmt; Node* block = (Node*)calloc(1, sizeof(Node)); @@ -270,7 +293,11 @@ parse_block(Parser* par) block->type = NODE_BLOCK; block->scope = NULL; while (peek(par).type != TOKEN_RBRACE && peek(par).type != TOKEN_EOF) { - stmt = parse_statement(par); + if (is_struct_block) { + stmt = parse_field_decl(par); + } else { + stmt = parse_statement(par); + } if (block->data.block.cap == block->data.block.len) { block->data.block.cap = block->data.block.cap == 0 ? 4 : block->data.block.cap * 2; @@ -287,6 +314,30 @@ parse_block(Parser* par) } Node* +parse_struct_decl(Parser* par) +{ + // loops through parse_field_decl, parse_func_decl + expect(par, TOKEN_STRUCT); + Token ident = expect(par, TOKEN_IDENT); // struct typedef name + if (!match(par, TOKEN_LBRACE)) { perror("Struct declaration without a body"); } + Node* body = parse_block(par, true); + + Node* strct = calloc(1, sizeof(Node)); + if (strct == NULL) panic("parse_struct_decl: func: could not alloc"); + + strct->type = NODE_STRUCT_DECL; + strct->scope = NULL; + + strct->data.struct_decl.name = (Span) { ident.start, ident.end }; + strct->data.struct_decl.body = body; + strct->data.struct_decl.exported = true; // TODO structs decl are all exported for now + strct->filename = par->filename; + strct->line = ident.line; + strct->col = ident.col; + return strct; +} + +Node* parse_declaration_statement(Parser* par) { Node* type_node = parse_type(par); // consumes the type (e.g., "float") @@ -333,7 +384,7 @@ parse_func_decl(Parser* par) Node* return_type = parse_type_or_void(par); expect(par, TOKEN_LBRACE); - Node* body = parse_block(par); + Node* body = parse_block(par, false); Node* fn = calloc(1, sizeof(Node)); if (fn == NULL) panic("parse_func_decl: func: could not alloc"); @@ -363,6 +414,7 @@ parse_decl_or_func_decl(Parser* par) Token ident = expect(par, TOKEN_IDENT); // variable or function name if (match(par, TOKEN_LPAREN)) { // function + // TODO use parse_func_decl instead of duplicating code Node* fn = calloc(1, sizeof(Node)); if (fn == NULL) panic("parse_decl_or_func_decl: func: could not alloc"); @@ -377,7 +429,7 @@ parse_decl_or_func_decl(Parser* par) expect(par, TOKEN_RPAREN); expect(par, TOKEN_LBRACE); - Node* body = parse_block(par); + Node* body = parse_block(par, false); fn->data.function_decl.body = body; fn->data.function_decl.name = (Span) { ident.start, ident.end }; @@ -386,7 +438,6 @@ parse_decl_or_func_decl(Parser* par) fn->line = ident.line; fn->col = ident.col; return fn; - } else { // variable Node* var = calloc(1, sizeof(Node)); if (var == NULL) panic("parse_decl_or_func_decl: var: could not alloc"); @@ -423,8 +474,11 @@ parse_declarations(Parser* par) case TOKEN_FX: return parse_func_decl(par); break; + case TOKEN_STRUCT: + return parse_struct_decl(par); + break; default: - printf("unknown token to parse!: %s\n", token_type_str(tok.type)); + printf("unknown token to parse: %s\n", token_type_str(tok.type)); return NULL; } return NULL; diff --git a/src/parser/parser_utils.c b/src/parser/parser_utils.c @@ -97,6 +97,15 @@ print_node(const char* source, Node* node, int level) print_node(source, node->data.function_decl.body, level + 1); } break; + case NODE_STRUCT_DECL: + // TODO @next fix the print of the struct decl, going through the fields + name = range_str(source, node->data.struct_decl.name.start, node->data.struct_decl.name.end, (char[IDENTSZ]) { 0 }); + printf("%*s STRUCT DECL: name='%s'\n", level, "", name); + if (node->data.struct_decl.body) { + printf("%*s ↳ body:\n", level * 2, ""); + print_node(source, node->data.struct_decl.body, level + 1); + } + break; case NODE_PARAM: name = range_str(source, node->data.param.name.start, node->data.param.name.end, (char[IDENTSZ]) { 0 }); printf("%*s ↳ param: name='%s'\n", level * 2, "", name); @@ -114,6 +123,18 @@ print_node(const char* source, Node* node, int level) print_node(source, node->data.var_decl.init, level + 1); } break; + case NODE_FIELD_DECL: + name = range_str(source, node->data.field_decl.name.start, node->data.field_decl.name.end, (char[IDENTSZ]) { 0 }); + printf("%*s FIELD DECL: name='%s'\n", level, "", name); + if (node->data.field_decl.type) { + printf("%*s ↳ type:\n", level * 2, ""); + print_node(source, node->data.field_decl.type, level + 1); + } + if (node->data.field_decl.init) { + printf("%*s ↳ init:\n", level * 2, ""); + print_node(source, node->data.field_decl.init, level + 1); + } + break; case NODE_PROGRAM: printf("%*s PROGRAM:\n", level, ""); if (node->data.program.decl) { @@ -144,6 +165,18 @@ print_node(const char* source, Node* node, int level) } } break; + case NODE_STRUCT_IMPL: + name = range_str(source, node->data.struct_impl.name.start, node->data.field_decl.name.end, (char[IDENTSZ]) { 0 }); + printf("%*s STRUCT IMPL: name='%s'\n", level, "", name); + if (node->data.struct_impl.fields) { + for (size_t i = 0; i < node->data.struct_impl.len; i++) { + print_node(source, node->data.struct_impl.fields[i], level + 1); + } + } + break; + case NODE_FIELD_IMPL: { + printf("\n\n\nTODO: print NODE_FIELD_IMPL\n\n\n"); + } break; case NODE_RETURN: printf("%*s RETURN statement:\n", level, ""); if (node->data.ret.expr) { print_node(source, node->data.ret.expr, level + 1); } @@ -310,8 +343,11 @@ node_type_str(NodeType t) { static const char* type_strings[] = { [NODE_PROGRAM] = "NODE_PROGRAM", [NODE_FUNCTION_DECL] = "NODE_FUNCTION_DECL", + [NODE_STRUCT_DECL] = "NODE_STRUCT_DECL", [NODE_PARAM] = "NODE_PARAM", [NODE_VAR_DECL] = "NODE_VAR_DECL", + [NODE_FIELD_DECL] = "NODE_FIELD_DECL", + [NODE_METHOD_DECL] = "NODE_METHOD_DECL", [NODE_BLOCK] = "NODE_BLOCK", [NODE_CALL_EXPR] = "NODE_CALL_EXPR", [NODE_RETURN] = "NODE_RETURN",