hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-10-12 11:41:05 +0200
committerhachem <im@hachem.wtf>2026-10-12 11:41:05 +0200
commit62636576bce8e8121a3d70f63547fae40a52f697 (patch)
tree077dfe4f04235cce63b7abb1b05bb539232ae74a /tests
parentc0f000ea166a24781fcdbf23c080b203cd9b8ed7 (diff)
chore: refactor
Diffstat (limited to 'tests')
-rw-r--r--tests/codegen_test.c689
-rw-r--r--tests/lexer_test.c111
-rw-r--r--tests/main.c16
-rw-r--r--tests/parser_test.c467
-rw-r--r--tests/sema_test.c166
-rw-r--r--tests/test.h21
-rw-r--r--tests/tests.h8
7 files changed, 0 insertions, 1478 deletions
diff --git a/tests/codegen_test.c b/tests/codegen_test.c
deleted file mode 100644
index 0a4a841..0000000
--- a/tests/codegen_test.c
+++ /dev/null
@@ -1,689 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-
-#include "tests.h"
-#include "parser.h"
-#include "codegen.h"
-
-static void generate_to_buffer(struct Program* program, char* buffer, size_t size)
-{
- FILE* out = tmpfile();
- if (out == NULL)
- {
- buffer[0] = '\0';
- return;
- }
-
- generate_nasm(program, out);
- fflush(out);
- rewind(out);
-
- size_t read = fread(buffer, 1, size - 1, out);
- buffer[read] = '\0';
- fclose(out);
-}
-
-static void generate_fasm_to_buffer(struct Program* program, char* buffer, size_t size)
-{
- FILE* out = tmpfile();
- if (out == NULL)
- {
- buffer[0] = '\0';
- return;
- }
-
- generate_fasm(program, out);
- fflush(out);
- rewind(out);
-
- size_t read = fread(buffer, 1, size - 1, out);
- buffer[read] = '\0';
- fclose(out);
-}
-
-static void generate_aarch64_to_buffer(struct Program* program, char* buffer, size_t size)
-{
- FILE* out = tmpfile();
- if (out == NULL)
- {
- buffer[0] = '\0';
- return;
- }
-
- generate_aarch64(program, out);
- fflush(out);
- rewind(out);
-
- size_t read = fread(buffer, 1, size - 1, out);
- buffer[read] = '\0';
- fclose(out);
-}
-
-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));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "%define N (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);
-
- free_program(&program);
-}
-
-static void test_generate_fasm(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "[entry: main]\nconst N = 5\ndata msg = \"hi\\n\"\nproc main\n{\nrax = N\nsyscall\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_fasm_to_buffer(&program, buffer, sizeof(buffer));
-
- // fasm framing differs from nasm
- check(context, strstr(buffer, "format ELF64") != NULL);
- check(context, strstr(buffer, "N = 5") != NULL);
- check(context, strstr(buffer, "section '.data' writeable") != NULL);
- check(context, strstr(buffer, "msg db \"hi\", 10") != NULL); // escape expanded to a byte
- check(context, strstr(buffer, ".len = $ - msg") != NULL);
- check(context, strstr(buffer, "section '.text' executable") != NULL);
- check(context, strstr(buffer, "public main") != NULL);
- // instruction bodies are identical to nasm
- check(context, strstr(buffer, "mov rax, N") != NULL);
- // nasm-only forms must be absent
- check(context, strstr(buffer, "%define") == NULL);
- check(context, strstr(buffer, "bits 64") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_select(struct TestContext* context)
-{
- // destination distinct from both sources: default-move else, then cmov
- struct Lexer lexer = create_lexer("proc main\n{\nrax = rbx if rcx < rdx else rsi\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "mov rax, rsi") != NULL);
- check(context, strstr(buffer, "cmp rcx, rdx") != NULL);
- check(context, strstr(buffer, "cmovl rax, rbx") != NULL);
- check(context, strstr(buffer, "; TODO") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_select_aarch64(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "[enable: logical_registers]\nproc main\n{\nr1 = r2 if r3 < r4 else r5\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_aarch64_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "cmp x2, x3") != NULL);
- check(context, strstr(buffer, "csel x0, x1, x4, lt") != NULL);
- check(context, strstr(buffer, "; TODO") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_aarch64(struct TestContext* context)
-{
- // logical registers map r1 -> x0, r2 -> x1; three-operand arithmetic and svc
- struct Lexer lexer = create_lexer(
- "[enable: logical_registers]\nproc main\n{\nr1 = 5\nr1 = r1 + r2\n"
- "while r2 > 0\nr2 = r2 - 1\nsyscall\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_aarch64_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, ".text") != NULL);
- check(context, strstr(buffer, "mov x0, #5") != NULL); // r1 = 5
- check(context, strstr(buffer, "add x0, x0, x1") != NULL); // r1 = r1 + r2
- check(context, strstr(buffer, "cmp x1, #0") != NULL); // while r2 > 0
- check(context, strstr(buffer, "sub x1, x1, #1") != NULL); // r2 = r2 - 1
- check(context, strstr(buffer, "svc #0") != NULL); // syscall
- // no x86 leaked in
- check(context, strstr(buffer, "rax") == NULL);
- check(context, strstr(buffer, "; TODO") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_text(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "proc main\n{\nrax = SYS_WRITE\nrsi = message\nrdx = message.len\n^rdi = rax\nloop:\nsyscall\ngoto loop\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "main:") != NULL);
- check(context, strstr(buffer, "mov rax, SYS_WRITE") != NULL);
- check(context, strstr(buffer, "mov rsi, message") != NULL);
- check(context, strstr(buffer, "mov rdx, message.len") != NULL);
- check(context, strstr(buffer, "mov [rdi], rax") != NULL);
- check(context, strstr(buffer, "loop:") != NULL);
- check(context, strstr(buffer, "syscall") != NULL);
- check(context, strstr(buffer, "jmp loop") != NULL);
-
- 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));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- 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);
-}
-
-static void test_generate_if_else(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "proc main\n{\nif rax > 3\n{\nrdi = 1\n}\nelse\n{\nrdi = 0\n}\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "jle .if_else_0") != NULL);
- check(context, strstr(buffer, "mov rdi, 1") != NULL);
- check(context, strstr(buffer, "jmp .if_end_0") != NULL);
- check(context, strstr(buffer, ".if_else_0:") != NULL);
- check(context, strstr(buffer, "mov rdi, 0") != NULL);
- check(context, strstr(buffer, ".if_end_0:") != NULL);
-
- free_program(&program);
-}
-
-static void test_generate_while(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "proc main\n{\nwhile rcx > 0\n{\nrbx += rcx\nrcx -= 1\n}\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, ".while_0:") != NULL);
- check(context, strstr(buffer, "cmp rcx, 0") != NULL);
- check(context, strstr(buffer, "jle .while_end_0") != NULL);
- check(context, strstr(buffer, "add rbx, rcx") != NULL);
- check(context, strstr(buffer, "jmp .while_0") != NULL);
- check(context, strstr(buffer, ".while_end_0:") != NULL);
- check(context, strstr(buffer, "; TODO") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_while_named(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "proc main\n{\nwhile .drain rcx > 0\n{\nrcx -= 1\n}\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, ".drain:") != NULL);
- check(context, strstr(buffer, "jle .drain_end") != NULL);
- check(context, strstr(buffer, "jmp .drain") != NULL);
- check(context, strstr(buffer, ".drain_end:") != NULL);
- check(context, strstr(buffer, ".while_0") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_negative(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "const N = -8\nproc main\n{\nrax = -5\nrbx = rax + -3\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "%define N (-8)") != NULL);
- check(context, strstr(buffer, "mov rax, -5") != NULL);
- check(context, strstr(buffer, "add rbx, -3") != NULL);
-
- free_program(&program);
-}
-
-static void test_generate_call(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "proc print_number(value: rdi)\n{\nsyscall\n}\nproc main\n{\nprint_number(r12)\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "mov rdi, r12") != NULL);
- check(context, strstr(buffer, "call print_number") != NULL);
- check(context, strstr(buffer, "\tret\n") != NULL);
-
- free_program(&program);
-}
-
-static void test_generate_param_substitution(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc print_number(value: rdi)\n{\nrax = value\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "mov rax, rdi") != NULL);
- check(context, strstr(buffer, "value") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_divide(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\nrax /= rbx\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "cqo") != NULL);
- check(context, strstr(buffer, "idiv rbx") != NULL);
-
- free_program(&program);
-}
-
-static void test_generate_multiply(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\nrax = rbx * rcx\nrbx = rcx * 3 + rdx\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "mov rax, rbx") != NULL);
- check(context, strstr(buffer, "imul rax, rcx") != NULL);
- check(context, strstr(buffer, "imul rbx, 3") != NULL);
- check(context, strstr(buffer, "add rbx, rdx") != NULL);
- check(context, strstr(buffer, "; TODO") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_modulo(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\nrbx %= rcx\nrax = rsi % rdi\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- // modulo takes idiv's remainder from rdx
- check(context, strstr(buffer, "mov rax, rbx") != NULL);
- check(context, strstr(buffer, "idiv rcx") != NULL);
- check(context, strstr(buffer, "mov rbx, rdx") != NULL);
- check(context, strstr(buffer, "mov rax, rdx") != NULL);
- check(context, strstr(buffer, "; TODO") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_divide_nonrax(struct TestContext* context)
-{
- // division into a register other than rax routes through rax:rdx
- struct Lexer lexer = create_lexer("proc main\n{\nrbx /= rcx\nrsi = rdi / rcx\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "mov rax, rbx") != NULL);
- check(context, strstr(buffer, "idiv rcx") != NULL);
- check(context, strstr(buffer, "mov rbx, rax") != NULL);
- check(context, strstr(buffer, "mov rsi, rax") != NULL);
- check(context, strstr(buffer, "; TODO") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_divide_scratch(struct TestContext* context)
-{
- // an immediate or rax/rdx divisor is routed through the r11 scratch
- struct Lexer lexer = create_lexer(
- "proc main\n{\nrbx /= 10\nrcx /= rax\nrsi = rdi / rdx\nr11 /= rax\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "mov r11, 10") != NULL); // immediate
- check(context, strstr(buffer, "mov r11, rax") != NULL); // rax divisor saved
- check(context, strstr(buffer, "mov r11, rdx") != NULL); // rdx divisor saved
- check(context, strstr(buffer, "xchg rax, r11") != NULL); // dst is the scratch
- check(context, strstr(buffer, "idiv r11") != NULL);
- check(context, strstr(buffer, "; TODO") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_stack_frame(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc work\n{\nstack buffer[32]\nsyscall\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "push rbp") != NULL);
- check(context, strstr(buffer, "mov rbp, rsp") != NULL);
- check(context, strstr(buffer, "sub rsp, 32") != NULL);
- check(context, strstr(buffer, "leave") != NULL);
-
- free_program(&program);
-}
-
-static void test_generate_address_expr(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "proc work\n{\nstack buffer[32]\nrsi = buffer + 31\nrdx = buffer + 32 - rsi\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "lea rsi, [rbp - 32]") != NULL);
- check(context, strstr(buffer, "add rsi, 31") != NULL);
- check(context, strstr(buffer, "lea rdx, [rbp - 32]") != NULL);
- check(context, strstr(buffer, "add rdx, 32") != NULL);
- check(context, strstr(buffer, "sub rdx, rsi") != NULL);
- check(context, strstr(buffer, "; TODO") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_sized_store(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "proc main\n{\n^byte rsi = rdx\n^dword rsi = rax\n^byte rsi = 5\n^rsi = rbx\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "mov byte [rsi], dl") != NULL);
- check(context, strstr(buffer, "mov dword [rsi], eax") != NULL);
- check(context, strstr(buffer, "mov byte [rsi], 5") != NULL);
- check(context, strstr(buffer, "mov [rsi], rbx") != NULL);
-
- free_program(&program);
-}
-
-static void test_generate_entry_and_bits(struct TestContext* context)
-{
- struct Lexer with_entry = create_lexer("[bits: 64]\n[entry: main]\nproc main\n{\nsyscall\n}\nproc helper\n{\nsyscall\n}\n");
- struct Program program;
- check(context, parse_program(&with_entry, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "bits 64") != NULL);
- check(context, strstr(buffer, "global main") != NULL);
- check(context, strstr(buffer, "main:") != NULL);
- check(context, strstr(buffer, "_start") == NULL);
- // the entry proc does not return; the non-entry helper does
- check(context, strstr(buffer, "helper:\n\tsyscall\n\tret") != NULL);
-
- free_program(&program);
-}
-
-static void test_generate_no_entry(struct TestContext* context)
-{
- struct Lexer no_entry = create_lexer("proc main\n{\nsyscall\n}\n");
- struct Program program;
- check(context, parse_program(&no_entry, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "global") == NULL);
- check(context, strstr(buffer, "main:") != NULL);
-
- free_program(&program);
-}
-
-static void test_generate_logical_registers(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "[enable: logical_registers]\nproc main\n{\nr1 = 5\nr4 = r10\nr6 = r1.8\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "mov rax, 5") != NULL); // r1 -> rax
- check(context, strstr(buffer, "mov rdx, r11") != NULL); // r4 -> rdx, r10 -> r11
- check(context, strstr(buffer, "mov rdi, al") != NULL); // r6 -> rdi, r1.8 -> al
-
- free_program(&program);
-}
-
-static void test_generate_logical_disabled(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\nr1 = 5\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- // without the extension, r1 is passed through untouched
- check(context, strstr(buffer, "mov r1, 5") != NULL);
-
- free_program(&program);
-}
-
-static void test_generate_load(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "proc main\n{\nrax = ^rsi\nrbx = ^byte rsi\nrcx = ^dword rsi\nrdx = ^rsi + 4\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "mov rax, [rsi]") != NULL);
- check(context, strstr(buffer, "movzx rbx, byte [rsi]") != NULL);
- check(context, strstr(buffer, "mov ecx, [rsi]") != NULL);
- check(context, strstr(buffer, "mov rdx, [rsi]") != NULL);
- check(context, strstr(buffer, "add rdx, 4") != NULL);
- check(context, strstr(buffer, "; TODO") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_signed_load(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "proc main\n{\nrax = ^signed byte rsi\nrbx = ^signed word rsi\nrcx = ^signed dword rsi\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "movsx rax, byte [rsi]") != NULL);
- check(context, strstr(buffer, "movsx rbx, word [rsi]") != NULL);
- check(context, strstr(buffer, "movsxd rcx, dword [rsi]") != NULL);
- check(context, strstr(buffer, "; TODO") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_enum_struct(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "enum Color\n{\nRed,\nGreen,\nBlue\n}\n"
- "struct Point\n{\nx: qword\ny: qword\nflag: byte\n}\n"
- "proc main\n{\nrax = Color.Blue\nrbx = Point.y\nrcx = Point.flag\nrdx = Point.size\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "mov rax, 2") != NULL); // Color.Blue -> 2
- check(context, strstr(buffer, "mov rbx, 8") != NULL); // Point.y -> 8
- check(context, strstr(buffer, "mov rcx, 16") != NULL); // Point.flag -> 16
- check(context, strstr(buffer, "mov rdx, 17") != NULL); // Point.size -> 17
-
- free_program(&program);
-}
-
-static void test_generate_floats(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "proc main\n{\nxmm0 = 3.5\nxmm0 *= xmm1\nrax = 4\nxmm2 = rax\nrbx = xmm0\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "__float0: dq 3.5") != NULL);
- check(context, strstr(buffer, "movsd xmm0, [__float0]") != NULL);
- check(context, strstr(buffer, "mulsd xmm0, xmm1") != NULL); // float arithmetic
- check(context, strstr(buffer, "cvtsi2sd xmm2, rax") != NULL); // int -> float
- check(context, strstr(buffer, "cvttsd2si rbx, xmm0") != NULL); // float -> int
- check(context, strstr(buffer, "; TODO") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_float_compare(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\nif xmm0 > 4.0\ngoto done\ndone:\nsyscall\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "ucomisd xmm0, [__float0]") != NULL);
- check(context, strstr(buffer, "jbe .if_end") != NULL); // '>' skips when <=
- check(context, strstr(buffer, "; TODO") == NULL);
-
- free_program(&program);
-}
-
-static void test_generate_add_zero_peephole(struct TestContext* context)
-{
- // `+= 0` / `-= 0` (e.g. a struct field at offset 0) is dropped; `*= 0` is not
- struct Lexer lexer = create_lexer("proc main\n{\nrax += 0\nrbx -= 0\nrcx *= 0\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "add rax, 0") == NULL);
- check(context, strstr(buffer, "sub rbx, 0") == NULL);
- check(context, strstr(buffer, "imul rcx, 0") != NULL);
-
- free_program(&program);
-}
-
-static void test_generate_float_memory(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\n^rsi = xmm0\nxmm1 = ^rsi\n}\n");
- struct Program program;
- check(context, parse_program(&lexer, &program));
-
- char buffer[1024];
- generate_to_buffer(&program, buffer, sizeof(buffer));
-
- check(context, strstr(buffer, "movsd [rsi], xmm0") != NULL); // float store
- check(context, strstr(buffer, "movsd xmm1, [rsi]") != NULL); // float load
- check(context, strstr(buffer, "; TODO") == NULL);
-
- free_program(&program);
-}
-
-void run_codegen_tests(struct TestContext* context)
-{
- test_generate_floats(context);
- test_generate_float_compare(context);
- test_generate_float_memory(context);
- test_generate_add_zero_peephole(context);
- test_generate_enum_struct(context);
- test_generate_load(context);
- test_generate_signed_load(context);
- test_generate_logical_registers(context);
- test_generate_logical_disabled(context);
- test_generate_entry_and_bits(context);
- test_generate_no_entry(context);
- test_generate_consts_and_data(context);
- test_generate_fasm(context);
- test_generate_aarch64(context);
- test_generate_select(context);
- test_generate_select_aarch64(context);
- test_generate_text(context);
- test_generate_if(context);
- test_generate_if_else(context);
- test_generate_while(context);
- test_generate_while_named(context);
- test_generate_negative(context);
- test_generate_call(context);
- test_generate_param_substitution(context);
- test_generate_divide(context);
- test_generate_multiply(context);
- test_generate_modulo(context);
- test_generate_divide_nonrax(context);
- test_generate_divide_scratch(context);
- test_generate_stack_frame(context);
- test_generate_address_expr(context);
- test_generate_sized_store(context);
-}
diff --git a/tests/lexer_test.c b/tests/lexer_test.c
deleted file mode 100644
index 0810c3b..0000000
--- a/tests/lexer_test.c
+++ /dev/null
@@ -1,111 +0,0 @@
-#include <string.h>
-#include <stdbool.h>
-
-#include "lexer.h"
-#include "tests.h"
-
-static bool token_matches(struct Token token, enum TokenType type, const char* text)
-{
- size_t length = strlen(text);
- return token.type == type
- && token.length == length
- && memcmp(token.start, text, length) == 0;
-}
-
-static void test_identifiers_and_integers(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("rax 42");
- check(context, token_matches(scan_token(&lexer), TOKEN_IDENTIFIER, "rax"));
- check(context, token_matches(scan_token(&lexer), TOKEN_INTEGER, "42"));
- check(context, scan_token(&lexer).type == TOKEN_EOF);
-}
-
-static void test_number_bases(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("42 0x1F 0b1010");
- check(context, token_matches(scan_token(&lexer), TOKEN_INTEGER, "42"));
- check(context, token_matches(scan_token(&lexer), TOKEN_INTEGER, "0x1F"));
- check(context, token_matches(scan_token(&lexer), TOKEN_INTEGER, "0b1010"));
-}
-
-static void test_float_literals(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("3.14 1.0 42");
- check(context, token_matches(scan_token(&lexer), TOKEN_FLOAT, "3.14"));
- check(context, token_matches(scan_token(&lexer), TOKEN_FLOAT, "1.0"));
- check(context, scan_token(&lexer).type == TOKEN_INTEGER);
-}
-
-static void test_operators(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("= == += != /");
- check(context, scan_token(&lexer).type == TOKEN_EQUAL);
- check(context, scan_token(&lexer).type == TOKEN_EQUAL_EQUAL);
- check(context, scan_token(&lexer).type == TOKEN_PLUS_EQUAL);
- check(context, scan_token(&lexer).type == TOKEN_BANG_EQUAL);
- check(context, scan_token(&lexer).type == TOKEN_SLASH);
-}
-
-static void test_literals(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("\"hi\\n\" '0'");
- check(context, token_matches(scan_token(&lexer), TOKEN_STRING, "\"hi\\n\""));
- check(context, token_matches(scan_token(&lexer), TOKEN_CHAR, "'0'"));
-}
-
-static void test_keywords(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("const proc mainly");
- check(context, scan_token(&lexer).type == TOKEN_CONST);
- check(context, scan_token(&lexer).type == TOKEN_PROC);
- check(context, scan_token(&lexer).type == TOKEN_IDENTIFIER);
-}
-
-static void test_size_keywords(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("byte word dword qword");
- check(context, scan_token(&lexer).type == TOKEN_BYTE);
- check(context, scan_token(&lexer).type == TOKEN_WORD);
- check(context, scan_token(&lexer).type == TOKEN_DWORD);
- check(context, scan_token(&lexer).type == TOKEN_QWORD);
-}
-
-static void test_enum_struct_keywords(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("enum struct");
- check(context, scan_token(&lexer).type == TOKEN_ENUM);
- check(context, scan_token(&lexer).type == TOKEN_STRUCT);
-}
-
-static void test_line_counting(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("a\nb\nc");
- check(context, scan_token(&lexer).line == 1);
- check(context, scan_token(&lexer).line == 2);
- check(context, scan_token(&lexer).line == 3);
-}
-
-static void test_comments(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("rax // line comment\n/* block\ncomment */ rbx");
- check(context, token_matches(scan_token(&lexer), TOKEN_IDENTIFIER, "rax"));
-
- struct Token second = scan_token(&lexer);
- check(context, token_matches(second, TOKEN_IDENTIFIER, "rbx"));
- check(context, second.line == 3);
- check(context, scan_token(&lexer).type == TOKEN_EOF);
-}
-
-void run_lexer_tests(struct TestContext* context)
-{
- test_identifiers_and_integers(context);
- test_number_bases(context);
- test_float_literals(context);
- test_operators(context);
- test_literals(context);
- test_keywords(context);
- test_size_keywords(context);
- test_enum_struct_keywords(context);
- test_line_counting(context);
- test_comments(context);
-}
diff --git a/tests/main.c b/tests/main.c
deleted file mode 100644
index dfd3ed9..0000000
--- a/tests/main.c
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <stdio.h>
-
-#include "tests.h"
-
-int main(void)
-{
- struct TestContext context = { 0, 0 };
-
- run_lexer_tests(&context);
- run_parser_tests(&context);
- run_sema_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/parser_test.c b/tests/parser_test.c
deleted file mode 100644
index eecc9b9..0000000
--- a/tests/parser_test.c
+++ /dev/null
@@ -1,467 +0,0 @@
-#include <string.h>
-#include <stdbool.h>
-
-#include "tests.h"
-#include "parser.h"
-
-static bool text_is(struct Token token, const char* text)
-{
- size_t length = strlen(text);
- return token.length == length && memcmp(token.start, text, length) == 0;
-}
-
-static bool primary_is(const struct Expr* expr, const char* text)
-{
- return expr->kind == EXPR_PRIMARY && text_is(expr->primary.token, text);
-}
-
-static void test_parse_consts(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("const A = 1\nconst B = 60\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
- check(context, program.const_count == 2);
- check(context, text_is(program.consts[0].name, "A"));
- check(context, primary_is(program.consts[0].value, "1"));
- check(context, text_is(program.consts[1].name, "B"));
- check(context, primary_is(program.consts[1].value, "60"));
-
- free_program(&program);
-}
-
-static void test_parse_const_expr(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("const A = 1\nconst B = A + 2 * 3\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
-
- const struct Expr* value = program.consts[1].value;
- check(context, value->kind == EXPR_BINARY);
- check(context, text_is(value->binary.op, "+"));
- check(context, primary_is(value->binary.left, "A"));
- check(context, value->binary.right->kind == EXPR_BINARY);
-
- free_program(&program);
-}
-
-static void test_parse_unary(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("const N = -8\nproc main\n{\nrax = -5\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
-
- const struct Expr* konst = program.consts[0].value;
- check(context, konst->kind == EXPR_UNARY);
- check(context, text_is(konst->unary.op, "-"));
- check(context, primary_is(konst->unary.operand, "8"));
-
- const struct Expr* value = program.procs[0].body[0].assign.value;
- check(context, value->kind == EXPR_UNARY);
- check(context, primary_is(value->unary.operand, "5"));
-
- free_program(&program);
-}
-
-static void test_parse_data(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("data msg = \"hi\"\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
- check(context, program.data_count == 1);
- check(context, text_is(program.data_decls[0].name, "msg"));
- check(context, text_is(program.data_decls[0].value, "\"hi\""));
-
- free_program(&program);
-}
-
-static void test_parse_proc_params(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc f(a: rdi, b: rsi)\n{\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
- check(context, program.proc_count == 1);
- check(context, text_is(program.procs[0].name, "f"));
- check(context, program.procs[0].param_count == 2);
- check(context, text_is(program.procs[0].params[0].name, "a"));
- check(context, text_is(program.procs[0].params[0].reg, "rdi"));
- check(context, text_is(program.procs[0].params[1].name, "b"));
- check(context, text_is(program.procs[0].params[1].reg, "rsi"));
-
- free_program(&program);
-}
-
-static void test_parse_proc_body(struct TestContext* context)
-{
- 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].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, primary_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, primary_is(third.assign.value, "rdx"));
-
- free_program(&program);
-}
-
-static void test_parse_simple_statements(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\nloop:\nsyscall\ngoto loop\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
- check(context, program.procs[0].body_count == 3);
-
- struct Statement label = program.procs[0].body[0];
- check(context, label.kind == STATEMENT_LABEL);
- check(context, text_is(label.label.name, "loop"));
-
- check(context, program.procs[0].body[1].kind == STATEMENT_SYSCALL);
-
- struct Statement jump = program.procs[0].body[2];
- check(context, jump.kind == STATEMENT_GOTO);
- check(context, text_is(jump.jump.label, "loop"));
-
- free_program(&program);
-}
-
-static void test_parse_expressions(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\nrsi = buffer + 31\nrdx = message.len\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
- check(context, program.procs[0].body_count == 2);
-
- const struct Expr* sum = program.procs[0].body[0].assign.value;
- check(context, sum->kind == EXPR_BINARY);
- check(context, text_is(sum->binary.op, "+"));
- check(context, primary_is(sum->binary.left, "buffer"));
- check(context, primary_is(sum->binary.right, "31"));
-
- const struct Expr* member = program.procs[0].body[1].assign.value;
- check(context, member->kind == EXPR_MEMBER);
- check(context, primary_is(member->member.object, "message"));
- check(context, text_is(member->member.member, "len"));
-
- free_program(&program);
-}
-
-static void test_parse_if(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\nif rax != 0\ngoto loop\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
- check(context, program.procs[0].body_count == 1);
-
- struct Statement branch = program.procs[0].body[0];
- check(context, branch.kind == STATEMENT_IF);
- check(context, primary_is(branch.branch.left, "rax"));
- check(context, text_is(branch.branch.comparison, "!="));
- check(context, primary_is(branch.branch.right, "0"));
- check(context, branch.branch.body_count == 1);
- check(context, branch.branch.body->kind == STATEMENT_GOTO);
- check(context, text_is(branch.branch.body->jump.label, "loop"));
- check(context, branch.branch.else_count == 0);
-
- free_program(&program);
-}
-
-static void test_parse_if_block(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "proc main\n{\nif rax > 3\n{\nrdi = 1\nrbx = 2\n}\nelse\n{\nrdi = 0\n}\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
- check(context, program.procs[0].body_count == 1);
-
- struct Statement branch = program.procs[0].body[0];
- check(context, branch.kind == STATEMENT_IF);
- check(context, branch.branch.body_count == 2);
- check(context, branch.branch.body[0].kind == STATEMENT_ASSIGN);
- check(context, text_is(branch.branch.body[1].assign.target, "rbx"));
- check(context, branch.branch.else_count == 1);
- check(context, text_is(branch.branch.else_body[0].assign.target, "rdi"));
-
- free_program(&program);
-}
-
-static void test_parse_else_if(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "proc main\n{\nif rax == 0\nrdi = 1\nelse if rax == 1\nrdi = 2\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
-
- struct Statement branch = program.procs[0].body[0];
- check(context, branch.kind == STATEMENT_IF);
- check(context, branch.branch.body_count == 1);
- check(context, branch.branch.else_count == 1);
- check(context, branch.branch.else_body[0].kind == STATEMENT_IF);
- check(context, primary_is(branch.branch.else_body[0].branch.right, "1"));
-
- free_program(&program);
-}
-
-static void test_parse_while(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "proc main\n{\nwhile rcx > 0\n{\nrbx += rcx\nrcx -= 1\n}\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
- check(context, program.procs[0].body_count == 1);
-
- struct Statement loop = program.procs[0].body[0];
- check(context, loop.kind == STATEMENT_WHILE);
- check(context, primary_is(loop.loop.left, "rcx"));
- check(context, text_is(loop.loop.comparison, ">"));
- check(context, primary_is(loop.loop.right, "0"));
- check(context, loop.loop.body_count == 2);
- check(context, text_is(loop.loop.body[1].assign.target, "rcx"));
- check(context, !loop.loop.named);
-
- free_program(&program);
-}
-
-static void test_parse_select(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\nr1 = r2 if r3 < r4 else r5\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
-
- struct Statement s = program.procs[0].body[0];
- check(context, s.kind == STATEMENT_SELECT);
- check(context, text_is(s.select.target, "r1"));
- check(context, primary_is(s.select.if_value, "r2"));
- check(context, primary_is(s.select.left, "r3"));
- check(context, text_is(s.select.comparison, "<"));
- check(context, primary_is(s.select.right, "r4"));
- check(context, primary_is(s.select.else_value, "r5"));
-
- free_program(&program);
-}
-
-static void test_parse_while_named(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\nwhile .drain rcx > 0\n{\nrcx -= 1\n}\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
-
- struct Statement loop = program.procs[0].body[0];
- check(context, loop.kind == STATEMENT_WHILE);
- check(context, loop.loop.named);
- check(context, text_is(loop.loop.name, "drain"));
- check(context, primary_is(loop.loop.left, "rcx"));
-
- free_program(&program);
-}
-
-static void test_parse_call(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\nprint_number(r12)\nf(a, b)\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
- check(context, program.procs[0].body_count == 2);
-
- struct CallStatement first = program.procs[0].body[0].call;
- check(context, program.procs[0].body[0].kind == STATEMENT_CALL);
- check(context, text_is(first.name, "print_number"));
- check(context, first.arg_count == 1);
- check(context, primary_is(first.args[0], "r12"));
-
- struct CallStatement second = program.procs[0].body[1].call;
- check(context, second.arg_count == 2);
- check(context, primary_is(second.args[0], "a"));
- check(context, primary_is(second.args[1], "b"));
-
- free_program(&program);
-}
-
-static void test_parse_stack(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\nstack buffer[32]\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
- check(context, program.procs[0].body_count == 1);
-
- struct Statement statement = program.procs[0].body[0];
- check(context, statement.kind == STATEMENT_STACK);
- check(context, text_is(statement.stack.name, "buffer"));
- check(context, primary_is(statement.stack.size, "32"));
-
- free_program(&program);
-}
-
-static void test_parse_sized_deref(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\n^byte rsi = rdx\n^rdi = rax\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
- check(context, program.procs[0].body_count == 2);
-
- struct AssignStatement sized = program.procs[0].body[0].assign;
- check(context, sized.target_deref);
- check(context, sized.store_size == STORE_SIZE_BYTE);
- check(context, text_is(sized.target, "rsi"));
-
- struct AssignStatement plain = program.procs[0].body[1].assign;
- check(context, plain.target_deref);
- check(context, plain.store_size == STORE_SIZE_NONE);
-
- free_program(&program);
-}
-
-static void test_parse_signed_deref(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\nrax = ^signed byte rsi\nrbx = ^byte rsi\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
-
- const struct Expr* signed_load = program.procs[0].body[0].assign.value;
- check(context, signed_load->kind == EXPR_DEREF);
- check(context, signed_load->deref.is_signed);
- check(context, signed_load->deref.size == STORE_SIZE_BYTE);
-
- const struct Expr* plain_load = program.procs[0].body[1].assign.value;
- check(context, plain_load->kind == EXPR_DEREF);
- check(context, !plain_load->deref.is_signed);
-
- free_program(&program);
-}
-
-static void test_parse_directives(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("[bits: 32]\n[entry: kmain]\n[enable: logical_registers]\nproc kmain\n{\nsyscall\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
- check(context, program.config.bits == 32);
- check(context, program.config.has_entry);
- check(context, text_is(program.config.entry, "kmain"));
- check(context, program.config.logical_registers);
-
- free_program(&program);
-}
-
-static void test_parse_bad_directive(struct TestContext* context)
-{
- struct Lexer bad_bits = create_lexer("[bits: 8]\nproc main\n{\nsyscall\n}\n");
- struct Program program;
- check(context, !parse_program(&bad_bits, &program));
- free_program(&program);
-
- struct Lexer bad_key = create_lexer("[target: nasm]\nproc main\n{\nsyscall\n}\n");
- check(context, !parse_program(&bad_key, &program));
- free_program(&program);
-}
-
-static void test_parse_register_size_suffix(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer("proc main\n{\nr1 = r2.8\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
-
- const struct Expr* value = program.procs[0].body[0].assign.value;
- check(context, value->kind == EXPR_MEMBER);
- check(context, value->member.member.type == TOKEN_INTEGER);
- check(context, text_is(value->member.member, "8"));
- check(context, value->member.object->kind == EXPR_PRIMARY);
- check(context, text_is(value->member.object->primary.token, "r2"));
-
- free_program(&program);
-}
-
-static void test_parse_enum_struct(struct TestContext* context)
-{
- struct Lexer lexer = create_lexer(
- "enum Color\n{\nRed,\nGreen,\nBlue\n}\nstruct Point\n{\nx\ny: byte\n}\n");
- struct Program program;
-
- check(context, parse_program(&lexer, &program));
- check(context, program.enum_count == 1);
- check(context, text_is(program.enums[0].name, "Color"));
- check(context, program.enums[0].member_count == 3);
- check(context, text_is(program.enums[0].members[1], "Green"));
-
- check(context, program.struct_count == 1);
- check(context, text_is(program.structs[0].name, "Point"));
- check(context, program.structs[0].field_count == 2);
- check(context, program.structs[0].fields[0].size == STORE_SIZE_QWORD);
- check(context, program.structs[0].fields[1].size == STORE_SIZE_BYTE);
-
- free_program(&program);
-}
-
-static void test_parse_errors(struct TestContext* context)
-{
- struct Program program;
-
- struct Lexer missing_name = create_lexer("const = 1\n");
- check(context, !parse_program(&missing_name, &program));
- free_program(&program);
-
- struct Lexer wrong_value = create_lexer("data x = 5\n");
- check(context, !parse_program(&wrong_value, &program));
- free_program(&program);
-
- struct Lexer unterminated = create_lexer("proc main\n{\nrax = 1\n");
- check(context, !parse_program(&unterminated, &program));
- free_program(&program);
-}
-
-void run_parser_tests(struct TestContext* context)
-{
- test_parse_consts(context);
- test_parse_const_expr(context);
- test_parse_unary(context);
- test_parse_data(context);
- test_parse_proc_params(context);
- test_parse_proc_body(context);
- test_parse_simple_statements(context);
- test_parse_expressions(context);
- test_parse_if(context);
- test_parse_if_block(context);
- test_parse_else_if(context);
- test_parse_while(context);
- test_parse_while_named(context);
- test_parse_select(context);
- test_parse_call(context);
- test_parse_stack(context);
- test_parse_sized_deref(context);
- test_parse_signed_deref(context);
- test_parse_directives(context);
- test_parse_bad_directive(context);
- test_parse_register_size_suffix(context);
- test_parse_enum_struct(context);
- test_parse_errors(context);
-}
diff --git a/tests/sema_test.c b/tests/sema_test.c
deleted file mode 100644
index 3c12001..0000000
--- a/tests/sema_test.c
+++ /dev/null
@@ -1,166 +0,0 @@
-#include <stdbool.h>
-
-#include "sema.h"
-#include "tests.h"
-#include "parser.h"
-
-static bool analyze_source(const char* source)
-{
- struct Lexer lexer = create_lexer(source);
- struct Program program;
- if (!parse_program(&lexer, &program))
- {
- free_program(&program);
- return false;
- }
-
- struct Source diagnostics = { "<test>", source };
- bool ok = analyze_program(diagnostics, &program);
- free_program(&program);
- return ok;
-}
-
-static void test_valid_program(struct TestContext* context)
-{
- check(context, analyze_source("const N = 1\ndata msg = \"hi\"\nproc main\n{\nsyscall\n}\n"));
-}
-
-static void test_no_entry_is_ok(struct TestContext* context)
-{
- // without an [entry: ...] directive there is no required entry point
- check(context, analyze_source("proc helper\n{\nsyscall\n}\n"));
-}
-
-static void test_defined_entry(struct TestContext* context)
-{
- check(context, analyze_source("[entry: start]\nproc start\n{\nsyscall\n}\n"));
-}
-
-static void test_undefined_entry(struct TestContext* context)
-{
- check(context, !analyze_source("[entry: main]\nproc helper\n{\nsyscall\n}\n"));
-}
-
-static void test_duplicate_const(struct TestContext* context)
-{
- check(context, !analyze_source("const X = 1\nconst X = 2\nproc main\n{\nsyscall\n}\n"));
-}
-
-static void test_duplicate_across_kinds(struct TestContext* context)
-{
- check(context, !analyze_source("data foo = \"a\"\nproc foo\n{\nsyscall\n}\nproc main\n{\nsyscall\n}\n"));
-}
-
-static void test_undefined_name(struct TestContext* context)
-{
- check(context, !analyze_source("proc main\n{\nrax = MISSING\n}\n"));
-}
-
-static void test_undefined_label(struct TestContext* context)
-{
- check(context, !analyze_source("proc main\n{\ngoto nowhere\n}\n"));
-}
-
-static void test_undefined_call(struct TestContext* context)
-{
- check(context, !analyze_source("proc main\n{\nnope(rax)\n}\n"));
-}
-
-static void test_call_arg_count(struct TestContext* context)
-{
- check(context, !analyze_source("proc f(a: rdi)\n{\nsyscall\n}\nproc main\n{\nf(rax, rbx)\n}\n"));
-}
-
-static void test_assign_to_const(struct TestContext* context)
-{
- check(context, !analyze_source("const K = 5\nproc main\n{\nK = 1\n}\n"));
-}
-
-static void test_const_expr_ok(struct TestContext* context)
-{
- check(context, analyze_source("const A = 1\nconst B = A + 2 * 3\nproc main\n{\nsyscall\n}\n"));
-}
-
-static void test_const_expr_rejects_register(struct TestContext* context)
-{
- check(context, !analyze_source("const X = rax + 1\nproc main\n{\nsyscall\n}\n"));
-}
-
-static void test_const_expr_rejects_data(struct TestContext* context)
-{
- check(context, !analyze_source("data d = \"x\"\nconst X = d\nproc main\n{\nsyscall\n}\n"));
-}
-
-static void test_deref_needs_register(struct TestContext* context)
-{
- check(context, !analyze_source("proc main\n{\nrax = ^MISSING\n}\n"));
- check(context, analyze_source("proc main\n{\nrax = ^rsi\n}\n"));
-}
-
-static void test_signed_load_needs_size(struct TestContext* context)
-{
- check(context, analyze_source("proc main\n{\nrax = ^signed byte rsi\n}\n"));
- check(context, !analyze_source("proc main\n{\nrax = ^signed rsi\n}\n"));
- check(context, !analyze_source("proc main\n{\nrax = ^signed qword rsi\n}\n"));
-}
-
-static void test_stack_size_constant(struct TestContext* context)
-{
- check(context, analyze_source(
- "struct P\n{\nx\ny\n}\nconst N = 4\n"
- "proc main\n{\nstack a[P.size]\nstack b[N * 2]\nsyscall\n}\n"));
- check(context, !analyze_source("proc main\n{\nstack a[rax]\n}\n"));
-}
-
-static void test_enum_struct_members(struct TestContext* context)
-{
- check(context, analyze_source(
- "enum Color\n{\nRed,\nGreen\n}\nstruct Point\n{\nx\ny\n}\n"
- "proc main\n{\nrax = Color.Green\nrbx = Point.y\nrcx = Point.size\n}\n"));
- check(context, !analyze_source(
- "enum Color\n{\nRed\n}\nproc main\n{\nrax = Color.Nope\n}\n"));
- check(context, !analyze_source(
- "struct Point\n{\nx\n}\nproc main\n{\nrax = Point.z\n}\n"));
-}
-
-static void test_references_resolve(struct TestContext* context)
-{
- // registers, params, consts, data (+ .len), stack buffers, labels, calls
- check(context, analyze_source(
- "const N = 1\n"
- "data msg = \"hi\"\n"
- "proc helper(value: rdi)\n{\nrax = value\n}\n"
- "proc main\n{\n"
- "stack buf[8]\n"
- "rax = N\n"
- "rsi = msg\n"
- "rdx = msg.len\n"
- "rdi = buf + 1\n"
- "helper(rax)\n"
- "loop:\n"
- "goto loop\n"
- "}\n"));
-}
-
-void run_sema_tests(struct TestContext* context)
-{
- test_valid_program(context);
- test_no_entry_is_ok(context);
- test_defined_entry(context);
- test_undefined_entry(context);
- test_duplicate_const(context);
- test_duplicate_across_kinds(context);
- test_undefined_name(context);
- test_undefined_label(context);
- test_undefined_call(context);
- test_call_arg_count(context);
- test_assign_to_const(context);
- test_const_expr_ok(context);
- test_const_expr_rejects_register(context);
- test_const_expr_rejects_data(context);
- test_deref_needs_register(context);
- test_signed_load_needs_size(context);
- test_stack_size_constant(context);
- test_enum_struct_members(context);
- test_references_resolve(context);
-}
diff --git a/tests/test.h b/tests/test.h
deleted file mode 100644
index c5e2e73..0000000
--- a/tests/test.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-#include <stdio.h>
-
-struct TestContext
-{
- int checks;
- int failures;
-};
-
-#define check(context, condition) \
- do \
- { \
- (context)->checks += 1; \
- if (!(condition)) \
- { \
- (context)->failures += 1; \
- printf(" FAIL %s:%d: %s\n", __FILE__, __LINE__, #condition); \
- } \
- } \
- while (0)
diff --git a/tests/tests.h b/tests/tests.h
deleted file mode 100644
index 2caf9b3..0000000
--- a/tests/tests.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma once
-
-#include "test.h"
-
-void run_lexer_tests(struct TestContext* context);
-void run_parser_tests(struct TestContext* context);
-void run_sema_tests(struct TestContext* context);
-void run_codegen_tests(struct TestContext* context);