aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-08-30 04:59:36 +0200
committerhachem <im@hachem.wtf>2026-08-30 04:59:36 +0200
commit4ffc0c48d0823bcc48bf88aed67324b6266810c5 (patch)
tree063423b929c62999c0413fd426664a2757b5683a
parentdd6f0ff9bf51c4085bc9cde4955a2a6b5f83cde7 (diff)
add: parse assignment statements into procedures
-rw-r--r--src/main.c14
-rw-r--r--src/parser/ast.c19
-rw-r--r--src/parser/ast.h28
-rw-r--r--src/parser/parser.c67
-rw-r--r--tests/parser_test.c24
5 files changed, 140 insertions, 12 deletions
diff --git a/src/main.c b/src/main.c
index e65362c..2f5f4d1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -58,6 +58,20 @@ int main(int argc, char** argv)
(int)param.reg.length, param.reg.start);
}
printf(")\n");
+
+ for (size_t s = 0; s < proc.body_count; s += 1)
+ {
+ struct Statement statement = proc.body[s];
+ if (statement.kind == STATEMENT_ASSIGN)
+ {
+ struct AssignStatement assign = statement.assign;
+ printf(" %s%.*s %.*s %.*s\n",
+ assign.target_deref ? "^" : "",
+ (int)assign.target.length, assign.target.start,
+ (int)assign.op.length, assign.op.start,
+ (int)assign.value.length, assign.value.start);
+ }
+ }
}
free_program(&program);
diff --git a/src/parser/ast.c b/src/parser/ast.c
index b002ff2..4f10915 100644
--- a/src/parser/ast.c
+++ b/src/parser/ast.c
@@ -23,7 +23,10 @@ void free_program(struct Program* program)
free(program->data_decls);
for (size_t i = 0; i < program->proc_count; i += 1)
+ {
free(program->procs[i].params);
+ free(program->procs[i].body);
+ }
free(program->procs);
program->consts = NULL;
@@ -70,6 +73,9 @@ struct ProcDecl create_proc(void)
proc.params = NULL;
proc.param_count = 0;
proc.param_capacity = 0;
+ proc.body = NULL;
+ proc.body_count = 0;
+ proc.body_capacity = 0;
return proc;
}
@@ -86,6 +92,19 @@ void add_param(struct ProcDecl* proc, struct Param param)
proc->param_count += 1;
}
+void add_statement(struct ProcDecl* proc, struct Statement statement)
+{
+ if (proc->body_count == proc->body_capacity)
+ {
+ size_t capacity = proc->body_capacity < 8 ? 8 : proc->body_capacity * 2;
+ proc->body = realloc(proc->body, capacity * sizeof(struct Statement));
+ proc->body_capacity = capacity;
+ }
+
+ proc->body[proc->body_count] = statement;
+ proc->body_count += 1;
+}
+
void add_proc(struct Program* program, struct ProcDecl decl)
{
if (program->proc_count == program->proc_capacity)
diff --git a/src/parser/ast.h b/src/parser/ast.h
index 665bfd2..83483e9 100644
--- a/src/parser/ast.h
+++ b/src/parser/ast.h
@@ -1,6 +1,7 @@
#pragma once
#include <stddef.h>
+#include <stdbool.h>
#include "lexer/lexer.h"
@@ -22,12 +23,38 @@ struct Param
struct Token reg;
};
+enum StatementKind
+{
+ STATEMENT_ASSIGN,
+};
+
+struct AssignStatement
+{
+ bool target_deref;
+ struct Token target;
+ struct Token op;
+ struct Token value;
+};
+
+struct Statement
+{
+ enum StatementKind kind;
+ union
+ {
+ struct AssignStatement assign;
+ };
+};
+
struct ProcDecl
{
struct Token name;
struct Param* params;
size_t param_count;
size_t param_capacity;
+
+ struct Statement* body;
+ size_t body_count;
+ size_t body_capacity;
};
struct Program
@@ -52,4 +79,5 @@ void add_data(struct Program* program, struct DataDecl decl);
struct ProcDecl create_proc(void);
void add_param(struct ProcDecl* proc, struct Param param);
+void add_statement(struct ProcDecl* proc, struct Statement statement);
void add_proc(struct Program* program, struct ProcDecl decl);
diff --git a/src/parser/parser.c b/src/parser/parser.c
index acd4b7a..d86502f 100644
--- a/src/parser/parser.c
+++ b/src/parser/parser.c
@@ -114,6 +114,56 @@ static bool parse_params(struct Parser* parser, struct ProcDecl* proc)
return true;
}
+static bool is_assign_op(enum TokenType type)
+{
+ return type == TOKEN_EQUAL
+ || type == TOKEN_PLUS_EQUAL
+ || type == TOKEN_MINUS_EQUAL
+ || type == TOKEN_STAR_EQUAL
+ || type == TOKEN_SLASH_EQUAL;
+}
+
+static bool parse_value(struct Parser* parser, struct Token* out)
+{
+ if (check(parser, TOKEN_IDENTIFIER) || check(parser, TOKEN_INTEGER) || check(parser, TOKEN_CHAR))
+ {
+ advance_parser(parser);
+ *out = parser->previous;
+ return true;
+ }
+
+ error_at(parser, parser->current, "expected a value");
+ return false;
+}
+
+static bool parse_statement(struct Parser* parser, struct Statement* out)
+{
+ bool deref = match_token(parser, TOKEN_CARET);
+
+ if (!consume(parser, TOKEN_IDENTIFIER, "expected a statement"))
+ return false;
+ struct Token target = parser->previous;
+
+ if (!is_assign_op(parser->current.type))
+ {
+ error_at(parser, parser->current, "expected an assignment operator");
+ return false;
+ }
+
+ struct AssignStatement assign;
+ assign.target_deref = deref;
+ assign.target = target;
+ advance_parser(parser);
+ assign.op = parser->previous;
+
+ if (!parse_value(parser, &assign.value))
+ return false;
+
+ out->kind = STATEMENT_ASSIGN;
+ out->assign = assign;
+ return true;
+}
+
static bool parse_proc(struct Parser* parser, struct Program* program)
{
struct ProcDecl decl = create_proc();
@@ -133,27 +183,28 @@ static bool parse_proc(struct Parser* parser, struct Program* program)
if (!consume(parser, TOKEN_LEFT_BRACE, "expected '{' to begin procedure body"))
goto error;
- // TODO: parse body statements; for now skip the braced block
- int depth = 1;
- while (depth > 0)
+ while (!check(parser, TOKEN_RIGHT_BRACE))
{
if (check(parser, TOKEN_EOF))
{
error_at(parser, parser->current, "unterminated procedure body");
goto error;
}
- if (check(parser, TOKEN_LEFT_BRACE))
- depth += 1;
- else if (check(parser, TOKEN_RIGHT_BRACE))
- depth -= 1;
- advance_parser(parser);
+
+ struct Statement statement;
+ if (!parse_statement(parser, &statement))
+ goto error;
+
+ add_statement(&decl, statement);
}
+ advance_parser(parser);
add_proc(program, decl);
return true;
error:
free(decl.params);
+ free(decl.body);
return false;
}
diff --git a/tests/parser_test.c b/tests/parser_test.c
index 14eacc9..85626a0 100644
--- a/tests/parser_test.c
+++ b/tests/parser_test.c
@@ -55,14 +55,30 @@ static void test_parse_proc_params(struct TestContext* context)
free_program(&program);
}
-static void test_parse_proc_body_skipped(struct TestContext* context)
+static void test_parse_proc_body(struct TestContext* context)
{
- struct Lexer lexer = create_lexer("proc main\n{\nrax = 1\nsyscall\n}\n");
+ struct Lexer lexer = create_lexer("proc main\n{\nrax = 1\nrcx -= 1\n^rsi = rdx\n}\n");
struct Program program;
check(context, parse_program(&lexer, &program));
check(context, program.proc_count == 1);
- check(context, program.procs[0].param_count == 0);
+ check(context, program.procs[0].body_count == 3);
+
+ struct Statement first = program.procs[0].body[0];
+ check(context, first.kind == STATEMENT_ASSIGN);
+ check(context, !first.assign.target_deref);
+ check(context, text_is(first.assign.target, "rax"));
+ check(context, text_is(first.assign.op, "="));
+ check(context, text_is(first.assign.value, "1"));
+
+ struct Statement second = program.procs[0].body[1];
+ check(context, text_is(second.assign.target, "rcx"));
+ check(context, text_is(second.assign.op, "-="));
+
+ struct Statement third = program.procs[0].body[2];
+ check(context, third.assign.target_deref);
+ check(context, text_is(third.assign.target, "rsi"));
+ check(context, text_is(third.assign.value, "rdx"));
free_program(&program);
}
@@ -89,6 +105,6 @@ void run_parser_tests(struct TestContext* context)
test_parse_consts(context);
test_parse_data(context);
test_parse_proc_params(context);
- test_parse_proc_body_skipped(context);
+ test_parse_proc_body(context);
test_parse_errors(context);
}