1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
#include <stdbool.h>
#include "parser/parser.h"
#include "sema/sema.h"
#include "tests.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_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_references_resolve(context);
}
|