aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-08-30 17:57:24 +0200
committerhachem <im@hachem.wtf>2026-08-30 17:57:24 +0200
commitea2ebf063cc6a87c73068a3e8b06fb744655159f (patch)
treefa928314c3618262c48501265cb56bc7b00c4b66
parent542e15041ea1d93e74a06c588f16ce7956d6a9bb (diff)
add: start code gen
-rw-r--r--src/codegen/nasm.c48
-rw-r--r--src/codegen/nasm.h7
-rw-r--r--src/main.c118
-rw-r--r--tests/codegen_test.c37
-rw-r--r--tests/main.c1
-rw-r--r--tests/tests.h1
6 files changed, 114 insertions, 98 deletions
diff --git a/src/codegen/nasm.c b/src/codegen/nasm.c
new file mode 100644
index 0000000..0782ee1
--- /dev/null
+++ b/src/codegen/nasm.c
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include <stddef.h>
+
+#include "codegen/nasm.h"
+
+static void emit_consts(struct Program* program, FILE* out)
+{
+ for (size_t i = 0; i < program->const_count; i += 1)
+ {
+ struct ConstDecl decl = program->consts[i];
+ fprintf(out, "%.*s equ %.*s\n",
+ (int)decl.name.length, decl.name.start,
+ (int)decl.value.length, decl.value.start);
+ }
+}
+
+static void emit_data(struct Program* program, FILE* out)
+{
+ fprintf(out, "section .data\n");
+
+ for (size_t i = 0; i < program->data_count; i += 1)
+ {
+ struct DataDecl decl = program->data_decls[i];
+
+ // the value lexeme keeps its surrounding double quotes; NASM backtick
+ // strings interpret the same escapes, so re-wrap the inner content
+ fprintf(out, "%.*s: db `%.*s`\n",
+ (int)decl.name.length, decl.name.start,
+ (int)(decl.value.length - 2), decl.value.start + 1);
+ fprintf(out, ".len equ $ - %.*s\n",
+ (int)decl.name.length, decl.name.start);
+ }
+}
+
+void generate_nasm(struct Program* program, FILE* out)
+{
+ if (program->const_count > 0)
+ {
+ emit_consts(program, out);
+ fprintf(out, "\n");
+ }
+
+ emit_data(program, out);
+ fprintf(out, "\n");
+
+ fprintf(out, "section .text\n");
+ fprintf(out, "global _start\n");
+}
diff --git a/src/codegen/nasm.h b/src/codegen/nasm.h
new file mode 100644
index 0000000..b9c2c90
--- /dev/null
+++ b/src/codegen/nasm.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include <stdio.h>
+
+#include "parser/ast.h"
+
+void generate_nasm(struct Program* program, FILE* out);
diff --git a/src/main.c b/src/main.c
index 9a31f83..a908a73 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,80 +1,11 @@
#include <stdio.h>
-#include <stddef.h>
#include "io/file.h"
#include "cli/args.h"
#include "lexer/lexer.h"
+#include "codegen/nasm.h"
#include "parser/parser.h"
-static void print_expr(struct Expr* expr)
-{
- switch (expr->kind)
- {
- case EXPR_PRIMARY:
- printf("%.*s", (int)expr->primary.token.length, expr->primary.token.start);
- break;
- case EXPR_BINARY:
- print_expr(expr->binary.left);
- printf(" %.*s ", (int)expr->binary.op.length, expr->binary.op.start);
- print_expr(expr->binary.right);
- break;
- case EXPR_MEMBER:
- print_expr(expr->member.object);
- printf(".%.*s", (int)expr->member.member.length, expr->member.member.start);
- break;
- }
-}
-
-static void print_statement(struct Statement* statement, const char* indent)
-{
- switch (statement->kind)
- {
- case STATEMENT_ASSIGN:
- {
- struct AssignStatement assign = statement->assign;
- printf("%s%s%.*s %.*s ", indent,
- assign.target_deref ? "^" : "",
- (int)assign.target.length, assign.target.start,
- (int)assign.op.length, assign.op.start);
- print_expr(assign.value);
- printf("\n");
- break;
- }
- case STATEMENT_LABEL:
- printf("%s%.*s:\n", indent, (int)statement->label.name.length, statement->label.name.start);
- break;
- case STATEMENT_GOTO:
- printf("%sgoto %.*s\n", indent, (int)statement->jump.label.length, statement->jump.label.start);
- break;
- case STATEMENT_SYSCALL:
- printf("%ssyscall\n", indent);
- break;
- case STATEMENT_IF:
- printf("%sif ", indent);
- print_expr(statement->branch.left);
- printf(" %.*s ", (int)statement->branch.comparison.length, statement->branch.comparison.start);
- print_expr(statement->branch.right);
- printf("\n");
- print_statement(statement->branch.body, " ");
- break;
- case STATEMENT_CALL:
- printf("%s%.*s(", indent, (int)statement->call.name.length, statement->call.name.start);
- for (size_t i = 0; i < statement->call.arg_count; i += 1)
- {
- if (i > 0)
- printf(", ");
- print_expr(statement->call.args[i]);
- }
- printf(")\n");
- break;
- case STATEMENT_STACK:
- printf("%sstack %.*s[%.*s]\n", indent,
- (int)statement->stack.name.length, statement->stack.name.start,
- (int)statement->stack.size.length, statement->stack.size.start);
- break;
- }
-}
-
int main(int argc, char** argv)
{
struct Args args;
@@ -85,6 +16,12 @@ int main(int argc, char** argv)
if (result == PARSE_ERROR)
return 1;
+ if (args.target != ASSEMBLER_NASM)
+ {
+ fprintf(stderr, "error: only the nasm target is supported\n");
+ return 1;
+ }
+
struct File source;
if (!read_file(args.input_path, &source))
return 1;
@@ -99,39 +36,24 @@ int main(int argc, char** argv)
return 1;
}
- for (size_t i = 0; i < program.const_count; i += 1)
- {
- struct ConstDecl decl = program.consts[i];
- printf("const %.*s = %.*s\n",
- (int)decl.name.length, decl.name.start,
- (int)decl.value.length, decl.value.start);
- }
-
- for (size_t i = 0; i < program.data_count; i += 1)
+ FILE* out = stdout;
+ if (args.output_path != NULL)
{
- struct DataDecl decl = program.data_decls[i];
- printf("data %.*s = %.*s\n",
- (int)decl.name.length, decl.name.start,
- (int)decl.value.length, decl.value.start);
- }
-
- for (size_t i = 0; i < program.proc_count; i += 1)
- {
- struct ProcDecl proc = program.procs[i];
- printf("proc %.*s(", (int)proc.name.length, proc.name.start);
- for (size_t p = 0; p < proc.param_count; p += 1)
+ out = fopen(args.output_path, "w");
+ if (out == NULL)
{
- struct Param param = proc.params[p];
- printf("%s%.*s: %.*s", p == 0 ? "" : ", ",
- (int)param.name.length, param.name.start,
- (int)param.reg.length, param.reg.start);
+ fprintf(stderr, "error: could not open '%s' for writing\n", args.output_path);
+ free_program(&program);
+ free_file(&source);
+ return 1;
}
- printf(")\n");
-
- for (size_t s = 0; s < proc.body_count; s += 1)
- print_statement(&proc.body[s], " ");
}
+ generate_nasm(&program, out);
+
+ if (out != stdout)
+ fclose(out);
+
free_program(&program);
free_file(&source);
}
diff --git a/tests/codegen_test.c b/tests/codegen_test.c
new file mode 100644
index 0000000..64421ea
--- /dev/null
+++ b/tests/codegen_test.c
@@ -0,0 +1,37 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "parser/parser.h"
+#include "codegen/nasm.h"
+#include "tests.h"
+
+static void test_generate_consts_and_data(struct TestContext* context)
+{
+ struct Lexer lexer = create_lexer("const N = 5\ndata msg = \"hi\"\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, "N equ 5") != NULL);
+ check(context, strstr(buffer, "section .data") != NULL);
+ check(context, strstr(buffer, "msg: db `hi`") != NULL);
+ check(context, strstr(buffer, ".len equ $ - msg") != NULL);
+ check(context, strstr(buffer, "section .text") != NULL);
+ check(context, strstr(buffer, "global _start") != NULL);
+
+ free_program(&program);
+}
+
+void run_codegen_tests(struct TestContext* context)
+{
+ test_generate_consts_and_data(context);
+}
diff --git a/tests/main.c b/tests/main.c
index b69bb3e..f0caf52 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -8,6 +8,7 @@ int main(void)
run_lexer_tests(&context);
run_parser_tests(&context);
+ run_codegen_tests(&context);
printf("\n%d checks, %d failure(s)\n", context.checks, context.failures);
return context.failures == 0 ? 0 : 1;
diff --git a/tests/tests.h b/tests/tests.h
index 9136910..32ee4a6 100644
--- a/tests/tests.h
+++ b/tests/tests.h
@@ -4,3 +4,4 @@
void run_lexer_tests(struct TestContext* context);
void run_parser_tests(struct TestContext* context);
+void run_codegen_tests(struct TestContext* context);