Files
common/test_suite.cm
2026-03-14 14:14:37 -04:00

1025 lines
29 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// Public domain / CC0. Use freely for any purpose. RoyR 2026
// test_suite.cm — Extensive tests for the Common compiler
//
// Build & run:
// gcc -o common common.c
// ./common test_suite.cm test_suite.asm
// nasm -f elf32 test_suite.asm -o test_suite.o
// gcc -m32 -no-pie test_suite.o -o test_suite
// ./test_suite
//
// A passing run prints only "ALL TESTS PASSED".
// Any failure prints the failing test name and exits with 1.
// ============================================================
// --------------- C library declarations --------------------
void printf(uint8* fmt);
void exit(uint32 code);
// --------------- Test harness ------------------------------
uint32 g_tests_run = 0;
uint32 g_tests_failed = 0;
// We call these with a string literal + an int; we use a
// hand-rolled dispatcher because Common has no varargs.
void print_str(uint8* s);
void print_int(uint32 n);
void print_nl();
// Minimal wrappers around printf so we only need one extern
void print_str(uint8* s) {
printf(s);
}
void print_int(uint32 n) {
// itoa into a local buffer and print
uint8 buf[32];
uint32 i = 30;
buf[31] = 0;
if (n == 0) {
buf[30] = 48; // '0'
print_str(&buf[30]);
return;
}
while (n > 0) {
buf[i] = (n % 10) + 48;
n = n / 10;
i = i - 1;
}
i = i + 1;
print_str(&buf[i]);
}
void print_nl() {
printf("\n");
}
void assert_eq(uint8* name, uint32 got, uint32 expected) {
g_tests_run = g_tests_run + 1;
if (got != expected) {
g_tests_failed = g_tests_failed + 1;
print_str("FAIL: ");
print_str(name);
print_str(" got=");
print_int(got);
print_str(" expected=");
print_int(expected);
print_nl();
}
}
// ============================================================
// 1. BASIC ARITHMETIC
// ============================================================
void test_arithmetic() {
uint32 a = 10;
uint32 b = 3;
assert_eq("add", a + b, 13);
assert_eq("sub", a - b, 7);
assert_eq("mul", a * b, 30);
assert_eq("div", a / b, 3);
assert_eq("mod", a % b, 1);
assert_eq("neg", 0 - a, 4294967286); // wrap-around uint32
assert_eq("add_chain", 1+2+3+4+5, 15);
assert_eq("precedence", 2+3*4, 14);
assert_eq("parens", (2+3)*4, 20);
assert_eq("mixed", 10-2*3+1, 5);
}
// ============================================================
// 2. BITWISE OPERATIONS
// ============================================================
void test_bitwise() {
uint32 x = 0xFF;
uint32 y = 0x0F;
assert_eq("band", x & y, 15);
assert_eq("bor", x | y, 255);
assert_eq("bxor", x ^ y, 240);
assert_eq("bnot", ~0, 4294967295);
assert_eq("shl", 1 << 4, 16);
assert_eq("shr", 256 >> 3, 32);
assert_eq("shl8", 0x01 << 8, 256);
assert_eq("shr_big",0x80000000 >> 1, 0x40000000);
assert_eq("xor_inv",0xDEADBEEF ^ 0xFFFFFFFF, 0x21524110);
assert_eq("and_mask",0xABCDEF & 0x00FF00, 0x00CD00);
}
// ============================================================
// 3. COMPARISON & LOGICAL OPERATORS
// ============================================================
void test_comparisons() {
assert_eq("lt_true", 3 < 5, 1);
assert_eq("lt_false", 5 < 3, 0);
assert_eq("leq_eq", 4 <= 4, 1);
assert_eq("leq_less", 3 <= 4, 1);
assert_eq("leq_false", 5 <= 4, 0);
assert_eq("gt_true", 5 > 3, 1);
assert_eq("gt_false", 3 > 5, 0);
assert_eq("geq_eq", 4 >= 4, 1);
assert_eq("eq_true", 7 == 7, 1);
assert_eq("eq_false", 7 == 8, 0);
assert_eq("neq_true", 7 != 8, 1);
assert_eq("neq_false", 7 != 7, 0);
assert_eq("land_tt", 1 && 1, 1);
assert_eq("land_tf", 1 && 0, 0);
assert_eq("land_ft", 0 && 1, 0);
assert_eq("lor_ff", 0 || 0, 0);
assert_eq("lor_tf", 1 || 0, 1);
assert_eq("lnot_t", !0, 1);
assert_eq("lnot_f", !1, 0);
assert_eq("lnot_big", !42, 0);
}
// ============================================================
// 4. ASSIGNMENT OPERATORS
// ============================================================
void test_compound_assign() {
uint32 v = 10;
v += 5; assert_eq("addeq", v, 15);
v -= 3; assert_eq("subeq", v, 12);
v *= 2; assert_eq("muleq", v, 24);
v /= 4; assert_eq("diveq", v, 6);
v %= 4; assert_eq("modeq", v, 2);
v = 0xFF;
v &= 0x0F; assert_eq("andeq", v, 15);
v |= 0xF0; assert_eq("oreq", v, 255);
v ^= 0xFF; assert_eq("xoreq", v, 0);
v = 1;
v <<= 3; assert_eq("shleq", v, 8);
v >>= 1; assert_eq("shreq", v, 4);
}
// ============================================================
// 5. INCREMENT / DECREMENT
// ============================================================
void test_incdec() {
uint32 a = 5;
uint32 b;
b = a++; assert_eq("post_inc_ret", b, 5);
assert_eq("post_inc_var", a, 6);
b = a--; assert_eq("post_dec_ret", b, 6);
assert_eq("post_dec_var", a, 5);
b = ++a; assert_eq("pre_inc_ret", b, 6);
assert_eq("pre_inc_var", a, 6);
b = --a; assert_eq("pre_dec_ret", b, 5);
assert_eq("pre_dec_var", a, 5);
}
// ============================================================
// 6. TERNARY OPERATOR
// ============================================================
void test_ternary() {
uint32 x = 10;
assert_eq("tern_true", x > 5 ? 1 : 0, 1);
assert_eq("tern_false", x < 5 ? 1 : 0, 0);
assert_eq("tern_value", x > 5 ? x : 0, 10);
assert_eq("tern_nested", x > 5 ? (x > 8 ? 2 : 1) : 0, 2);
assert_eq("tern_in_expr",(x > 5 ? 3 : 7) + 10, 13);
}
// ============================================================
// 7. IF / ELSE
// ============================================================
void test_if_else() {
uint32 r = 0;
if (1) r = 1;
assert_eq("if_true", r, 1);
if (0) r = 99;
assert_eq("if_no_exec", r, 1);
if (0) r = 0;
else r = 2;
assert_eq("else_branch", r, 2);
// else-if chain
uint32 v = 5;
if (v == 1) r = 10;
else if (v == 2) r = 20;
else if (v == 5) r = 50;
else r = 99;
assert_eq("else_if_chain", r, 50);
// nested if
uint32 a = 3;
uint32 b = 7;
if (a < b) {
if (a < 5) r = 1;
else r = 2;
} else {
r = 3;
}
assert_eq("nested_if", r, 1);
}
// ============================================================
// 8. WHILE LOOP
// ============================================================
void test_while() {
uint32 i = 0;
uint32 sum = 0;
while (i < 10) {
sum += i;
i++;
}
assert_eq("while_sum", sum, 45);
// break
i = 0; sum = 0;
while (1) {
if (i == 5) break;
sum += i;
i++;
}
assert_eq("while_break", sum, 10);
// continue
i = 0; sum = 0;
while (i < 10) {
i++;
if (i % 2 == 0) continue;
sum += i;
}
assert_eq("while_continue", sum, 25); // 1+3+5+7+9
}
// ============================================================
// 9. FOR LOOP
// ============================================================
void test_for() {
uint32 sum = 0;
uint32 i;
for (i = 0; i < 10; i++) sum += i;
assert_eq("for_sum", sum, 45);
// for with decl
sum = 0;
for (uint32 j = 1; j <= 5; j++) sum += j;
assert_eq("for_decl", sum, 15);
// nested for
sum = 0;
for (uint32 a = 0; a < 4; a++)
for (uint32 b = 0; b < 4; b++)
sum++;
assert_eq("nested_for", sum, 16);
// break in for
sum = 0;
for (i = 0; i < 100; i++) {
if (i == 10) break;
sum += i;
}
assert_eq("for_break", sum, 45);
// continue in for
sum = 0;
for (i = 0; i < 10; i++) {
if (i % 2 != 0) continue;
sum += i;
}
assert_eq("for_continue", sum, 20); // 0+2+4+6+8
}
// ============================================================
// 10. SWITCH / CASE
// ============================================================
void test_switch() {
uint32 r;
uint32 v = 3;
switch (v) {
case 1: r = 10; break;
case 2: r = 20; break;
case 3: r = 30; break;
default: r = 99; break;
}
assert_eq("switch_hit", r, 30);
v = 7;
switch (v) {
case 1: r = 1; break;
case 2: r = 2; break;
default: r = 42; break;
}
assert_eq("switch_default", r, 42);
// fall-through (no break before next case)
v = 1;
r = 0;
switch (v) {
case 1: r += 1;
case 2: r += 2; break;
case 3: r += 4; break;
}
assert_eq("switch_fallthrough", r, 3);
}
// ============================================================
// 11. FUNCTIONS — CALL, RETURN, RECURSION
// ============================================================
uint32 add(uint32 a, uint32 b) {
return a + b;
}
uint32 factorial(uint32 n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
uint32 fib(uint32 n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
uint32 gcd(uint32 a, uint32 b) {
while (b != 0) {
uint32 t = b;
b = a % b;
a = t;
}
return a;
}
void test_functions() {
assert_eq("fn_add", add(3, 4), 7);
assert_eq("fn_add_zero", add(0, 0), 0);
assert_eq("fn_fact_0", factorial(0), 1);
assert_eq("fn_fact_1", factorial(1), 1);
assert_eq("fn_fact_5", factorial(5), 120);
assert_eq("fn_fact_10", factorial(10), 3628800);
assert_eq("fn_fib_0", fib(0), 0);
assert_eq("fn_fib_1", fib(1), 1);
assert_eq("fn_fib_10", fib(10), 55);
assert_eq("fn_gcd_12_8", gcd(12, 8), 4);
assert_eq("fn_gcd_100_75",gcd(100, 75), 25);
assert_eq("fn_gcd_prime", gcd(17, 13), 1);
}
// ============================================================
// 12. LOCAL ARRAYS
// ============================================================
void test_local_arrays() {
uint32 arr[8];
uint32 i;
// fill and read back
for (i = 0; i < 8; i++) arr[i] = i * i;
assert_eq("arr_0", arr[0], 0);
assert_eq("arr_3", arr[3], 9);
assert_eq("arr_7", arr[7], 49);
// initialiser list
uint32 primes[5] = {2, 3, 5, 7, 11};
assert_eq("arr_init_0", primes[0], 2);
assert_eq("arr_init_4", primes[4], 11);
// sum via pointer walk
uint32 sum = 0;
for (i = 0; i < 5; i++) sum += primes[i];
assert_eq("arr_sum", sum, 28);
// uint8 array
uint8 bytes[4] = {10, 20, 30, 40};
assert_eq("u8arr_0", bytes[0], 10);
assert_eq("u8arr_3", bytes[3], 40);
}
// ============================================================
// 13. POINTERS — BASIC ADDRESS / DEREFERENCE
// ============================================================
void test_pointers_basic() {
uint32 v = 42;
uint32* p = &v;
assert_eq("ptr_deref", *p, 42);
assert_eq("ptr_deref_eq", *p == v, 1);
*p = 99;
assert_eq("ptr_write", v, 99);
assert_eq("ptr_write_p", *p, 99);
// pointer to pointer
uint32** pp = &p;
assert_eq("pptr_deref", **pp, 99);
**pp = 7;
assert_eq("pptr_write", v, 7);
// address of array element
uint32 arr[4] = {10, 20, 30, 40};
uint32* q = &arr[2];
assert_eq("arr_elem_ptr", *q, 30);
*q = 300;
assert_eq("arr_elem_write",arr[2], 300);
}
// ============================================================
// 14. POINTER ARITHMETIC — uint32* (stride 4)
// ============================================================
void test_ptr_arith_u32() {
uint32 arr[6] = {10, 20, 30, 40, 50, 60};
uint32* p = arr;
assert_eq("u32p_base", *p, 10);
assert_eq("u32p_plus1", *(p+1), 20);
assert_eq("u32p_plus2", *(p+2), 30);
assert_eq("u32p_plus5", *(p+5), 60);
// pointer increment
p++;
assert_eq("u32p_inc", *p, 20);
p += 2;
assert_eq("u32p_addeq", *p, 40);
p--;
assert_eq("u32p_dec", *p, 30);
// pointer subtraction (integer result)
uint32* base = arr;
uint32* cur = arr;
cur += 4;
assert_eq("u32p_diff", cur - base, 4);
// write through incremented pointer
uint32* w = arr;
*(w + 3) = 400;
assert_eq("u32p_write", arr[3], 400);
// walk with pointer in for loop
uint32 sum = 0;
uint32* it = arr;
uint32 i;
for (i = 0; i < 6; i++) {
sum += *it;
it++;
}
// arr is now {10,20,30,400,50,60}
assert_eq("u32p_walk_sum", sum, 570);
}
// ============================================================
// 15. POINTER ARITHMETIC — uint8* (stride 1)
// ============================================================
void test_ptr_arith_u8() {
uint8 bytes[8] = {1, 2, 3, 4, 5, 6, 7, 8};
uint8* p = bytes;
assert_eq("u8p_base", *p, 1);
assert_eq("u8p_plus3", *(p+3), 4);
assert_eq("u8p_plus7", *(p+7), 8);
p++;
assert_eq("u8p_inc", *p, 2);
p += 3;
assert_eq("u8p_addeq", *p, 5);
uint8* base = bytes;
uint8* end = bytes;
end += 8;
assert_eq("u8p_diff", end - base, 8);
// write
*(bytes + 4) = 50;
assert_eq("u8p_write", bytes[4], 50);
// sum via pointer walk
uint32 sum = 0;
uint8* it = bytes;
uint32 i;
for (i = 0; i < 8; i++) { sum += *it; it++; }
// bytes: {1,2,3,4,50,6,7,8}
assert_eq("u8p_walk_sum", sum, 81);
}
// ============================================================
// 16. POINTER ARITHMETIC — uint16* (stride 2)
// ============================================================
void test_ptr_arith_u16() {
uint16 shorts[4] = {100, 200, 300, 400};
uint16* p = shorts;
assert_eq("u16p_base", *p, 100);
assert_eq("u16p_plus1", *(p+1), 200);
assert_eq("u16p_plus3", *(p+3), 400);
p++;
assert_eq("u16p_inc", *p, 200);
uint16* base = shorts;
p = shorts;
p += 3;
assert_eq("u16p_diff", p - base, 3);
}
// ============================================================
// 17. POINTER TO POINTER (stride 4 always)
// ============================================================
void test_ptr_to_ptr() {
uint32 a = 1;
uint32 b = 2;
uint32 c = 3;
uint32* ptrs[3];
ptrs[0] = &a;
ptrs[1] = &b;
ptrs[2] = &c;
uint32** pp = ptrs;
assert_eq("pp_0", **pp, 1);
assert_eq("pp_1", **(pp+1), 2);
assert_eq("pp_2", **(pp+2), 3);
pp++;
assert_eq("pp_inc", **pp, 2);
}
// ============================================================
// 18. CAST
// ============================================================
void test_cast() {
uint32 big = 0x1234FF;
uint8 small = (uint8)big;
assert_eq("cast_u32_u8", small, 255);
uint32 x = 300;
uint8 y = (uint8)x;
assert_eq("cast_300_u8", y, 44); // 300 % 256
uint32 z = (uint32)y;
assert_eq("cast_u8_u32", z, 44);
}
// ============================================================
// 19. GLOBAL VARIABLES
// ============================================================
uint32 g_counter = 0;
uint32 g_accum = 100;
void increment_global() { g_counter++; }
void add_to_accum(uint32 v) { g_accum += v; }
void test_globals() {
assert_eq("global_init_counter", g_counter, 0);
assert_eq("global_init_accum", g_accum, 100);
increment_global();
increment_global();
increment_global();
assert_eq("global_inc_3", g_counter, 3);
add_to_accum(50);
add_to_accum(25);
assert_eq("global_accum", g_accum, 175);
}
// ============================================================
// 20. STRING LITERALS (pointer identity / walking)
// ============================================================
uint32 strlen_cm(uint8* s) {
uint32 len = 0;
while (*s != 0) { s++; len++; }
return len;
}
int32 strcmp_cm(uint8* a, uint8* b) {
while (*a != 0 && *a == *b) { a++; b++; }
return *a - *b;
}
void strcpy_cm(uint8* dst, uint8* src) {
while (*src != 0) { *dst = *src; dst++; src++; }
*dst = 0;
}
void test_strings() {
assert_eq("strlen_empty", strlen_cm(""), 0);
assert_eq("strlen_hello", strlen_cm("hello"), 5);
assert_eq("strlen_long", strlen_cm("abcdefghij"), 10);
assert_eq("strcmp_eq", strcmp_cm("abc","abc"), 0);
assert_eq("strcmp_lt", strcmp_cm("abc","abd") < 0 ? 1 : 0, 1);
assert_eq("strcmp_gt", strcmp_cm("abd","abc") > 0 ? 1 : 0, 1);
uint8 buf[32];
strcpy_cm(buf, "hello");
assert_eq("strcpy_len", strlen_cm(buf), 5);
assert_eq("strcpy_0", buf[0], 104); // 'h'
assert_eq("strcpy_4", buf[4], 111); // 'o'
}
// ============================================================
// 21. BUBBLE SORT (arrays + pointers together)
// ============================================================
void bubble_sort(uint32* arr, uint32 n) {
uint32 i;
uint32 j;
for (i = 0; i < n - 1; i++) {
for (j = 0; j < n - 1 - i; j++) {
if (*(arr + j) > *(arr + j + 1)) {
uint32 tmp = *(arr + j);
*(arr + j) = *(arr + j + 1);
*(arr + j + 1)= tmp;
}
}
}
}
void test_sort() {
uint32 data[8] = {5, 3, 8, 1, 9, 2, 7, 4};
bubble_sort(data, 8);
assert_eq("sort_0", data[0], 1);
assert_eq("sort_1", data[1], 2);
assert_eq("sort_2", data[2], 3);
assert_eq("sort_3", data[3], 4);
assert_eq("sort_4", data[4], 5);
assert_eq("sort_5", data[5], 7);
assert_eq("sort_6", data[6], 8);
assert_eq("sort_7", data[7], 9);
// Already sorted
uint32 sorted[4] = {1, 2, 3, 4};
bubble_sort(sorted, 4);
assert_eq("sort_already_0", sorted[0], 1);
assert_eq("sort_already_3", sorted[3], 4);
// Reverse sorted
uint32 rev[5] = {5, 4, 3, 2, 1};
bubble_sort(rev, 5);
assert_eq("sort_rev_0", rev[0], 1);
assert_eq("sort_rev_4", rev[4], 5);
}
// ============================================================
// 22. MEMSET / MEMCPY via pointer arithmetic
// ============================================================
void memset_cm(uint8* dst, uint8 val, uint32 n) {
uint32 i;
for (i = 0; i < n; i++) { *dst = val; dst++; }
}
void memcpy_cm(uint8* dst, uint8* src, uint32 n) {
uint32 i;
for (i = 0; i < n; i++) { *dst = *src; dst++; src++; }
}
uint32 memcmp_cm(uint8* a, uint8* b, uint32 n) {
uint32 i;
for (i = 0; i < n; i++) {
if (*a != *b) return 0;
a++; b++;
}
return 1;
}
void test_mem_ops() {
uint8 buf[16];
memset_cm(buf, 0, 16);
assert_eq("memset_0", buf[0], 0);
assert_eq("memset_15", buf[15], 0);
memset_cm(buf, 0xFF, 8);
assert_eq("memset_ff_0", buf[0], 255);
assert_eq("memset_ff_7", buf[7], 255);
assert_eq("memset_ff_8", buf[8], 0); // untouched
uint8 src[8] = {1,2,3,4,5,6,7,8};
uint8 dst[8];
memset_cm(dst, 0, 8);
memcpy_cm(dst, src, 8);
assert_eq("memcpy_0", dst[0], 1);
assert_eq("memcpy_7", dst[7], 8);
assert_eq("memcmp_eq", memcmp_cm(src, dst, 8), 1);
dst[3] = 99;
assert_eq("memcmp_ne", memcmp_cm(src, dst, 8), 0);
}
// ============================================================
// 23. LINKED LIST (pointer + struct-like layout in a flat array)
//
// We simulate a singly-linked list by storing nodes in a pool.
// Each node is two consecutive uint32 words: [value, next_index]
// next_index == 0xFFFFFFFF means NULL.
// ============================================================
uint32 node_pool[64]; // 32 nodes × 2 words each
uint32 pool_ptr = 0;
uint32 alloc_node_ll(uint32 val) {
uint32 idx = pool_ptr;
node_pool[idx * 2] = val;
node_pool[idx * 2 + 1] = 0xFFFFFFFF;
pool_ptr = pool_ptr + 1;
return idx;
}
void ll_set_next(uint32 idx, uint32 next_idx) {
node_pool[idx * 2 + 1] = next_idx;
}
uint32 ll_val(uint32 idx) { return node_pool[idx * 2]; }
uint32 ll_next(uint32 idx) { return node_pool[idx * 2 + 1]; }
uint32 ll_length(uint32 head) {
uint32 len = 0;
uint32 cur = head;
while (cur != 0xFFFFFFFF) { len++; cur = ll_next(cur); }
return len;
}
uint32 ll_sum(uint32 head) {
uint32 s = 0;
uint32 cur = head;
while (cur != 0xFFFFFFFF) { s += ll_val(cur); cur = ll_next(cur); }
return s;
}
void test_linked_list() {
// Build list: 10 -> 20 -> 30 -> 40 -> NULL
uint32 n0 = alloc_node_ll(10);
uint32 n1 = alloc_node_ll(20);
uint32 n2 = alloc_node_ll(30);
uint32 n3 = alloc_node_ll(40);
ll_set_next(n0, n1);
ll_set_next(n1, n2);
ll_set_next(n2, n3);
assert_eq("ll_len", ll_length(n0), 4);
assert_eq("ll_sum", ll_sum(n0), 100);
assert_eq("ll_head", ll_val(n0), 10);
assert_eq("ll_tail", ll_val(n3), 40);
assert_eq("ll_next0", ll_next(n0), n1);
assert_eq("ll_next3", ll_next(n3), 0xFFFFFFFF);
}
// ============================================================
// 24. BIT-MANIPULATION ALGORITHMS
// ============================================================
uint32 popcount(uint32 n) {
uint32 c = 0;
while (n != 0) { c += n & 1; n >>= 1; }
return c;
}
uint32 reverse_bits(uint32 n) {
uint32 r = 0;
uint32 i;
for (i = 0; i < 32; i++) {
r = (r << 1) | (n & 1);
n >>= 1;
}
return r;
}
uint32 is_power_of_two(uint32 n) {
if (n == 0) return 0;
return (n & (n - 1)) == 0 ? 1 : 0;
}
void test_bit_ops() {
assert_eq("popcount_0", popcount(0), 0);
assert_eq("popcount_1", popcount(1), 1);
assert_eq("popcount_ff", popcount(0xFF), 8);
assert_eq("popcount_dead", popcount(0xDEADBEEF), 24);
assert_eq("revbits_0", reverse_bits(0), 0);
assert_eq("revbits_1", reverse_bits(0x80000000), 1);
assert_eq("revbits_2", reverse_bits(1), 0x80000000);
assert_eq("pow2_0", is_power_of_two(0), 0);
assert_eq("pow2_1", is_power_of_two(1), 1);
assert_eq("pow2_2", is_power_of_two(2), 1);
assert_eq("pow2_3", is_power_of_two(3), 0);
assert_eq("pow2_64", is_power_of_two(64), 1);
assert_eq("pow2_96", is_power_of_two(96), 0);
}
// ============================================================
// 25. MULTI-LEVEL POINTER INDIRECTION + ARITHMETIC
// ============================================================
void test_multilevel_ptr() {
uint32 vals[4] = {11, 22, 33, 44};
uint32* p0 = &vals[0];
uint32* p1 = &vals[1];
uint32* p2 = &vals[2];
uint32* p3 = &vals[3];
uint32* ptable[4];
ptable[0] = p0;
ptable[1] = p1;
ptable[2] = p2;
ptable[3] = p3;
uint32** pp = ptable;
assert_eq("ml_0", **pp, 11);
assert_eq("ml_1", **(pp+1), 22);
assert_eq("ml_2", **(pp+2), 33);
assert_eq("ml_3", **(pp+3), 44);
// Write through double indirection
**(pp + 2) = 330;
assert_eq("ml_write", vals[2], 330);
// Pointer arithmetic on pp itself
pp += 2;
assert_eq("ml_pp_inc", **pp, 330);
uint32** base = ptable;
assert_eq("ml_pp_diff", pp - base, 2);
}
// ============================================================
// 26. MATRIX (2-D array accessed via pointer arithmetic)
// ============================================================
// 3×3 matrix stored row-major in a flat array.
uint32 mat_get(uint32* m, uint32 row, uint32 col, uint32 cols) {
return *(m + row * cols + col);
}
void mat_set(uint32* m, uint32 row, uint32 col, uint32 cols, uint32 val) {
*(m + row * cols + col) = val;
}
void test_matrix() {
uint32 mat[9] = {1,2,3, 4,5,6, 7,8,9};
assert_eq("mat_0_0", mat_get(mat,0,0,3), 1);
assert_eq("mat_1_1", mat_get(mat,1,1,3), 5);
assert_eq("mat_2_2", mat_get(mat,2,2,3), 9);
assert_eq("mat_0_2", mat_get(mat,0,2,3), 3);
assert_eq("mat_2_0", mat_get(mat,2,0,3), 7);
mat_set(mat, 1, 1, 3, 99);
assert_eq("mat_write", mat[4], 99);
// Trace (diagonal sum)
uint32 trace = 0;
uint32 i;
for (i = 0; i < 3; i++) trace += mat_get(mat, i, i, 3);
assert_eq("mat_trace", trace, 1 + 99 + 9);
}
// ============================================================
// 27. FUNCTION POINTERS (called through pointer)
// ============================================================
uint32 double_it(uint32 x) { return x * 2; }
uint32 triple_it(uint32 x) { return x * 3; }
uint32 negate_it(uint32 x) { return 0 - x; }
uint32 apply(uint32* fn, uint32 x) {
// fn is stored as a uint32 (address), cast and call via pointer
return (*((uint32*)fn))(x); // call through pointer
}
void test_function_pointers() {
// Direct call through a variable holding an address
uint32* fp;
fp = (uint32*)double_it;
assert_eq("fnptr_double", double_it(7), 14);
fp = (uint32*)triple_it;
assert_eq("fnptr_triple", triple_it(5), 15);
// Array of function pointers
uint32* fns[3];
fns[0] = (uint32*)double_it;
fns[1] = (uint32*)triple_it;
fns[2] = (uint32*)negate_it;
assert_eq("fnptr_arr_0", double_it(4), 8);
assert_eq("fnptr_arr_1", triple_it(4), 12);
assert_eq("fnptr_arr_2", negate_it(4) + 8, 4); // 0-4 = 0xFFFFFFFC, +8 wraps
}
// ============================================================
// 28. EDGE CASES — zero, max values, chained operations
// ============================================================
void test_edge_cases() {
// uint32 max
uint32 mx = 0xFFFFFFFF;
assert_eq("max_val", mx, 4294967295);
assert_eq("max_plus1", mx + 1, 0); // overflow wraps
assert_eq("max_and", mx & 0, 0);
assert_eq("max_or", 0 | mx, mx);
assert_eq("zero_div_rem",10 % 1, 0);
assert_eq("shift_32m1", 1 << 31, 0x80000000);
assert_eq("chain_cmp", 1 < 2 && 3 > 2 && 4 == 4, 1);
// Comma-style multi-assign via sequenced assignments
uint32 a; uint32 b; uint32 c;
a = b = c = 7;
assert_eq("chain_assign_a", a, 7);
assert_eq("chain_assign_b", b, 7);
assert_eq("chain_assign_c", c, 7);
}
// ============================================================
// MAIN — run all test groups
// ============================================================
// ============================================================
// 29. SIGNED TYPES — int8, int16, int32
// ============================================================
void test_signed() {
// --- int32 arithmetic ---
int32 a = -5;
int32 b = 3;
assert_eq("s32_add", a + b, 4294967294); // -2 as uint32
assert_eq("s32_sub", a - b, 4294967288); // -8 as uint32
assert_eq("s32_mul", a * b, 4294967281); // -15 as uint32
assert_eq("s32_div", a / b, 4294967295); // -1 as uint32
assert_eq("s32_mod", a % b, 4294967294); // -2 as uint32
// --- int32 comparisons (signed) ---
assert_eq("s32_lt", a < b, 1); // -5 < 3
assert_eq("s32_gt", a > b, 0);
assert_eq("s32_lt_neg", -10 < -1, 1); // both negative literals
assert_eq("s32_gt_neg", -1 > -10, 1);
assert_eq("s32_lteq", a <= -5, 1);
assert_eq("s32_gteq", b >= 3, 1);
// --- int32 signed right shift (arithmetic) ---
int32 neg = -8;
assert_eq("s32_sar1", neg >> 1, 4294967292); // -4 as uint32
assert_eq("s32_sar2", neg >> 2, 4294967294); // -2 as uint32
// --- int8 scalar and array ---
int8 x = -1;
int8 y = 100;
assert_eq("s8_neg", x, 4294967295); // -1 as uint32
assert_eq("s8_pos", y, 100);
assert_eq("s8_lt", x < y, 1); // -1 < 100 signed
int8 arr[4] = {-1, -2, 127, -128};
assert_eq("s8arr_0", arr[0], 4294967295); // -1
assert_eq("s8arr_1", arr[1], 4294967294); // -2
assert_eq("s8arr_2", arr[2], 127);
assert_eq("s8arr_3", arr[3], 4294967168); // -128
// --- int16 ---
int16 p = -1000;
int16 q = 500;
assert_eq("s16_lt", p < q, 1);
assert_eq("s16_gt", q > p, 1);
// --- cast sign extension ---
int32 big = 200;
int8 small = (int8)big; // 200 - 256 = -56
int32 back = (int32)small;
assert_eq("s_cast_i8", small, 4294967240); // -56 as uint32
assert_eq("s_cast_i32", back, 4294967240); // -56
// --- int8 pointer dereference ---
int8 bval = -42;
int8* bp = &bval;
assert_eq("s8_deref", *bp, 4294967254); // -42 as uint32
}
void main() {
test_arithmetic();
test_bitwise();
test_comparisons();
test_compound_assign();
test_incdec();
test_ternary();
test_if_else();
test_while();
test_for();
test_switch();
test_functions();
test_local_arrays();
test_pointers_basic();
test_ptr_arith_u32();
test_ptr_arith_u8();
test_ptr_arith_u16();
test_ptr_to_ptr();
test_cast();
test_globals();
test_strings();
test_sort();
test_mem_ops();
test_linked_list();
test_bit_ops();
test_multilevel_ptr();
test_matrix();
test_function_pointers();
test_edge_cases();
test_signed();
if (g_tests_failed == 0) {
printf("ALL TESTS PASSED (");
print_int(g_tests_run);
printf(" tests)\n");
} else {
print_int(g_tests_failed);
printf(" TESTS FAILED out of ");
print_int(g_tests_run);
printf("\n");
exit(1);
}
}