commit 7e8dfdfdc7d4c170a92bff7f518a4ab633821ce9
parent f7946e20cdc617207a36a264500f6167db548673
Author: citbl <citbl@citbl.org>
Date: Thu, 25 Jun 2026 17:34:59 +1000
parser refac
Diffstat:
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",