diff options
| author | hachem <im@hachem.wtf> | 2026-10-12 11:41:05 +0200 |
|---|---|---|
| committer | hachem <im@hachem.wtf> | 2026-10-12 11:41:05 +0200 |
| commit | 62636576bce8e8121a3d70f63547fae40a52f697 (patch) | |
| tree | 077dfe4f04235cce63b7abb1b05bb539232ae74a /tests | |
| parent | c0f000ea166a24781fcdbf23c080b203cd9b8ed7 (diff) | |
chore: refactor
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/codegen_test.c | 689 | ||||
| -rw-r--r-- | tests/lexer_test.c | 111 | ||||
| -rw-r--r-- | tests/main.c | 16 | ||||
| -rw-r--r-- | tests/parser_test.c | 467 | ||||
| -rw-r--r-- | tests/sema_test.c | 166 | ||||
| -rw-r--r-- | tests/test.h | 21 | ||||
| -rw-r--r-- | tests/tests.h | 8 |
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); |
