From 5e5322bf94c32ccb509570f6354c1bd73d7e6193 Mon Sep 17 00:00:00 2001 From: hachem Date: Sun, 30 Aug 2026 18:50:14 +0200 Subject: feat: emit nasm for if statements with conditional jumps --- src/codegen/nasm.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- tests/codegen_test.c | 25 +++++++++++++++++++++++++ 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 #include +#include #include #include @@ -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); } -- cgit v1.3