aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-08-30 18:50:14 +0200
committerhachem <im@hachem.wtf>2026-08-30 18:50:14 +0200
commit5e5322bf94c32ccb509570f6354c1bd73d7e6193 (patch)
tree5039090c61e91dfdb84e56e56ea6640e2897b4b3
parent287b075294610e224c67eb7a746c9fd7ae318275 (diff)
feat: emit nasm for if statements with conditional jumps
-rw-r--r--src/codegen/nasm.c49
-rw-r--r--tests/codegen_test.c25
2 files changed, 72 insertions, 2 deletions
diff --git a/src/codegen/nasm.c b/src/codegen/nasm.c
index 37d13a7..5bb84d0 100644
--- a/src/codegen/nasm.c
+++ b/src/codegen/nasm.c
@@ -1,5 +1,6 @@
#include <stdio.h>
#include <stddef.h>
+#include <stdint.h>
#include <string.h>
#include <stdbool.h>
@@ -83,7 +84,47 @@ static void emit_assign(struct AssignStatement* assign, FILE* out)
fprintf(out, "\n");
}
-static void emit_statement(struct Statement* statement, FILE* out)
+static const char* jump_if_false(enum TokenType comparison)
+{
+ switch (comparison)
+ {
+ case TOKEN_EQUAL_EQUAL: return "jne";
+ case TOKEN_BANG_EQUAL: return "je";
+ case TOKEN_LESS: return "jge";
+ case TOKEN_LESS_EQUAL: return "jg";
+ case TOKEN_GREATER: return "jle";
+ case TOKEN_GREATER_EQUAL: return "jl";
+ default: return NULL;
+ }
+}
+
+static void emit_statement(struct Statement* statement, FILE* out, uint32_t* label_id);
+
+static void emit_if(struct IfStatement* branch, FILE* out, uint32_t* label_id)
+{
+ const char* jump = jump_if_false(branch->comparison.type);
+ if (jump == NULL || branch->left->kind == EXPR_BINARY || branch->right->kind == EXPR_BINARY)
+ {
+ fprintf(out, "\t; TODO: unsupported if\n");
+ return;
+ }
+
+ uint32_t id = *label_id;
+ *label_id += 1;
+
+ fprintf(out, "\tcmp ");
+ emit_operand(branch->left, out);
+ fprintf(out, ", ");
+ emit_operand(branch->right, out);
+ fprintf(out, "\n");
+ fprintf(out, "\t%s .if_end_%u\n", jump, id);
+
+ emit_statement(branch->body, out, label_id);
+
+ fprintf(out, ".if_end_%u:\n", id);
+}
+
+static void emit_statement(struct Statement* statement, FILE* out, uint32_t* label_id)
{
switch (statement->kind)
{
@@ -99,6 +140,9 @@ static void emit_statement(struct Statement* statement, FILE* out)
case STATEMENT_SYSCALL:
fprintf(out, "\tsyscall\n");
break;
+ case STATEMENT_IF:
+ emit_if(&statement->branch, out, label_id);
+ break;
default:
fprintf(out, "\t; TODO: unsupported statement\n");
break;
@@ -113,8 +157,9 @@ static void emit_proc(struct ProcDecl* proc, FILE* out)
else
fprintf(out, "%.*s:\n", (int)proc->name.length, proc->name.start);
+ uint32_t label_id = 0;
for (size_t i = 0; i < proc->body_count; i += 1)
- emit_statement(&proc->body[i], out);
+ emit_statement(&proc->body[i], out, &label_id);
}
void generate_nasm(struct Program* program, FILE* out)
diff --git a/tests/codegen_test.c b/tests/codegen_test.c
index 0d8c5e8..eb2ff95 100644
--- a/tests/codegen_test.c
+++ b/tests/codegen_test.c
@@ -60,8 +60,33 @@ static void test_generate_text(struct TestContext* context)
free_program(&program);
}
+static void test_generate_if(struct TestContext* context)
+{
+ struct Lexer lexer = create_lexer("proc main\n{\nif rcx != 0\ngoto loop\n}\n");
+ struct Program program;
+ check(context, parse_program(&lexer, &program));
+
+ FILE* out = tmpfile();
+ generate_nasm(&program, out);
+ fflush(out);
+ rewind(out);
+
+ char buffer[1024];
+ size_t read = fread(buffer, 1, sizeof(buffer) - 1, out);
+ buffer[read] = '\0';
+ fclose(out);
+
+ check(context, strstr(buffer, "cmp rcx, 0") != NULL);
+ check(context, strstr(buffer, "je .if_end_0") != NULL);
+ check(context, strstr(buffer, "jmp loop") != NULL);
+ check(context, strstr(buffer, ".if_end_0:") != NULL);
+
+ free_program(&program);
+}
+
void run_codegen_tests(struct TestContext* context)
{
test_generate_consts_and_data(context);
test_generate_text(context);
+ test_generate_if(context);
}