general update, updated cimport so it parses struct definitions from imported headers, improved the error reporting, added ifdef and ifndef, added proper support for extern variadic functions, added some new sections and pages to the doc tool and added --check to only check the program corectness and run compile time defs without producing a binary
This commit is contained in:
14
test.py
14
test.py
@@ -152,6 +152,7 @@ class TestCaseConfig:
|
||||
tags: List[str] = field(default_factory=list)
|
||||
requires: List[str] = field(default_factory=list)
|
||||
libs: List[str] = field(default_factory=list)
|
||||
compile_args: List[str] = field(default_factory=list)
|
||||
|
||||
@classmethod
|
||||
def from_meta(cls, data: Dict[str, Any]) -> "TestCaseConfig":
|
||||
@@ -207,6 +208,11 @@ class TestCaseConfig:
|
||||
if not isinstance(libs, list) or not all(isinstance(item, str) for item in libs):
|
||||
raise ValueError("libs must be a list of strings")
|
||||
cfg.libs = [item.strip() for item in libs if item.strip()]
|
||||
if "compile_args" in data:
|
||||
ca = data["compile_args"]
|
||||
if not isinstance(ca, list) or not all(isinstance(item, str) for item in ca):
|
||||
raise ValueError("compile_args must be a list of strings")
|
||||
cfg.compile_args = list(ca)
|
||||
return cfg
|
||||
|
||||
|
||||
@@ -442,6 +448,7 @@ class TestRunner:
|
||||
cmd.extend(["-l", lib])
|
||||
for lib in (extra_libs or []):
|
||||
cmd.extend(["-l", lib])
|
||||
cmd.extend(case.config.compile_args)
|
||||
if self.args.ct_run_main:
|
||||
cmd.append("--ct-run-main")
|
||||
if self.args.verbose:
|
||||
@@ -477,7 +484,7 @@ class TestRunner:
|
||||
obj_path = case.build_dir / f"{case.binary_stub}_fixture.o"
|
||||
archive_path = case.build_dir / f"lib{case.binary_stub}_fixture.a"
|
||||
|
||||
compile_cmd = [cc, "-O2", "-c", str(c_source), "-o", str(obj_path)]
|
||||
compile_cmd = [cc, "-O2", "-fno-stack-protector", "-c", str(c_source), "-o", str(obj_path)]
|
||||
archive_cmd = [ar, "rcs", str(archive_path), str(obj_path)]
|
||||
|
||||
if self.args.verbose:
|
||||
@@ -655,6 +662,11 @@ class TestRunner:
|
||||
return self._sort_lines(text)
|
||||
if case.source.stem == "ct_test" and label == "compile":
|
||||
return self._mask_build_path(text, case.binary_stub)
|
||||
if label == "compile":
|
||||
# Normalize absolute source paths to relative for stable compile error comparison
|
||||
source_dir = str(case.source.parent.resolve())
|
||||
if source_dir:
|
||||
text = text.replace(source_dir + "/", "")
|
||||
return text
|
||||
|
||||
def _sort_lines(self, text: str) -> str:
|
||||
|
||||
9
tests/cimport_structs.c
Normal file
9
tests/cimport_structs.c
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "cimport_structs.h"
|
||||
|
||||
long point_sum_ptr(struct Point *p) {
|
||||
return p->x + p->y;
|
||||
}
|
||||
|
||||
long pair_sum_ptr(struct Pair *p) {
|
||||
return p->a + p->b;
|
||||
}
|
||||
6
tests/cimport_structs.expected
Normal file
6
tests/cimport_structs.expected
Normal file
@@ -0,0 +1,6 @@
|
||||
16
|
||||
10
|
||||
20
|
||||
30
|
||||
16
|
||||
300
|
||||
18
tests/cimport_structs.h
Normal file
18
tests/cimport_structs.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef TEST_STRUCTS_H
|
||||
#define TEST_STRUCTS_H
|
||||
|
||||
struct Point {
|
||||
long x;
|
||||
long y;
|
||||
};
|
||||
|
||||
struct Pair {
|
||||
long a;
|
||||
long b;
|
||||
};
|
||||
|
||||
/* Pointer-based helpers (simple scalar ABI). */
|
||||
long point_sum_ptr(struct Point *p);
|
||||
long pair_sum_ptr(struct Pair *p);
|
||||
|
||||
#endif
|
||||
31
tests/cimport_structs.sl
Normal file
31
tests/cimport_structs.sl
Normal file
@@ -0,0 +1,31 @@
|
||||
import stdlib.sl
|
||||
|
||||
# Test cimport: extract struct definitions and extern functions from a C header.
|
||||
cimport "cimport_structs.h"
|
||||
|
||||
word main
|
||||
# Verify that cstruct Point was generated with correct layout
|
||||
Point.size puti cr # 16 bytes (two i64 = 8+8)
|
||||
|
||||
# Allocate a Point, set fields, read them back
|
||||
Point.size alloc dup >r
|
||||
r@ 10 Point.x!
|
||||
r@ 20 Point.y!
|
||||
r@ Point.x@ puti cr # 10
|
||||
r@ Point.y@ puti cr # 20
|
||||
|
||||
# Call C helper that takes a pointer (simple scalar ABI)
|
||||
r@ point_sum_ptr puti cr # 30
|
||||
r> Point.size free
|
||||
|
||||
# Verify Pair struct layout
|
||||
Pair.size puti cr # 16
|
||||
|
||||
Pair.size alloc dup >r
|
||||
r@ 100 Pair.a!
|
||||
r@ 200 Pair.b!
|
||||
r@ pair_sum_ptr puti cr # 300
|
||||
r> Pair.size free
|
||||
|
||||
0
|
||||
end
|
||||
12
tests/error_recovery.compile.expected
Normal file
12
tests/error_recovery.compile.expected
Normal file
@@ -0,0 +1,12 @@
|
||||
error: unexpected 'end' at 6:8
|
||||
--> error_recovery.sl:6:8
|
||||
|
|
||||
6 | end end
|
||||
| ^^^
|
||||
error: unexpected 'end' at 10:8
|
||||
--> error_recovery.sl:10:8
|
||||
|
|
||||
10 | end end
|
||||
| ^^^
|
||||
|
||||
2 error(s) emitted
|
||||
4
tests/error_recovery.meta.json
Normal file
4
tests/error_recovery.meta.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"expect_compile_error": true,
|
||||
"description": "Compiler reports multiple errors via error recovery"
|
||||
}
|
||||
15
tests/error_recovery.sl
Normal file
15
tests/error_recovery.sl
Normal file
@@ -0,0 +1,15 @@
|
||||
# This file intentionally has multiple errors to test error recovery.
|
||||
# The compiler should report all of them rather than stopping at the first.
|
||||
# No stdlib import — keeps line numbers stable.
|
||||
|
||||
word foo
|
||||
end end
|
||||
end
|
||||
|
||||
word bar
|
||||
end end
|
||||
end
|
||||
|
||||
word main
|
||||
0
|
||||
end
|
||||
7
tests/ifdef.expected
Normal file
7
tests/ifdef.expected
Normal file
@@ -0,0 +1,7 @@
|
||||
flag_on
|
||||
|
||||
nope_off
|
||||
|
||||
yes
|
||||
|
||||
nested_ok
|
||||
3
tests/ifdef.meta.json
Normal file
3
tests/ifdef.meta.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"compile_args": ["-D", "TESTFLAG"]
|
||||
}
|
||||
44
tests/ifdef.sl
Normal file
44
tests/ifdef.sl
Normal file
@@ -0,0 +1,44 @@
|
||||
import stdlib/stdlib.sl
|
||||
import stdlib/io.sl
|
||||
|
||||
# Test ifdef: TESTFLAG is defined via -D TESTFLAG
|
||||
ifdef TESTFLAG
|
||||
word show_flag
|
||||
"flag_on" puts cr
|
||||
end
|
||||
endif
|
||||
|
||||
# Test ifndef: NOPE is NOT defined
|
||||
ifndef NOPE
|
||||
word show_nope
|
||||
"nope_off" puts cr
|
||||
end
|
||||
endif
|
||||
|
||||
# Test ifdef with elsedef
|
||||
ifdef TESTFLAG
|
||||
word branch
|
||||
"yes" puts cr
|
||||
end
|
||||
elsedef
|
||||
word branch
|
||||
"no" puts cr
|
||||
end
|
||||
endif
|
||||
|
||||
# Test nested: inner depends on outer
|
||||
ifdef TESTFLAG
|
||||
ifndef NOPE
|
||||
word nested
|
||||
"nested_ok" puts cr
|
||||
end
|
||||
endif
|
||||
endif
|
||||
|
||||
word main
|
||||
show_flag
|
||||
show_nope
|
||||
branch
|
||||
nested
|
||||
0
|
||||
end
|
||||
3
tests/ifndef.expected
Normal file
3
tests/ifndef.expected
Normal file
@@ -0,0 +1,3 @@
|
||||
guard_ok
|
||||
|
||||
else_ok
|
||||
33
tests/ifndef.sl
Normal file
33
tests/ifndef.sl
Normal file
@@ -0,0 +1,33 @@
|
||||
import stdlib/stdlib.sl
|
||||
import stdlib/io.sl
|
||||
|
||||
# No -D flags, so ifdef FOO is false, ifndef FOO is true
|
||||
|
||||
ifdef FOO
|
||||
word dead_code
|
||||
"BUG" puts cr
|
||||
end
|
||||
endif
|
||||
|
||||
ifndef FOO
|
||||
word guarded
|
||||
"guard_ok" puts cr
|
||||
end
|
||||
endif
|
||||
|
||||
# elsedef: ifdef FALSE → skip, elsedef → include
|
||||
ifdef MISSING
|
||||
word wrong
|
||||
"BUG" puts cr
|
||||
end
|
||||
elsedef
|
||||
word right
|
||||
"else_ok" puts cr
|
||||
end
|
||||
endif
|
||||
|
||||
word main
|
||||
guarded
|
||||
right
|
||||
0
|
||||
end
|
||||
20
tests/variadic_extern.c
Normal file
20
tests/variadic_extern.c
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
/* Sums variadic long args until sentinel -1 is seen. */
|
||||
long va_sum_sentinel(long first, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, first);
|
||||
long total = first;
|
||||
while (1) {
|
||||
long v = va_arg(ap, long);
|
||||
if (v == -1) break;
|
||||
total += v;
|
||||
}
|
||||
va_end(ap);
|
||||
return total;
|
||||
}
|
||||
|
||||
/* Non-variadic helper for comparison. */
|
||||
long add_two(long a, long b) {
|
||||
return a + b;
|
||||
}
|
||||
5
tests/variadic_extern.expected
Normal file
5
tests/variadic_extern.expected
Normal file
@@ -0,0 +1,5 @@
|
||||
30
|
||||
hello
|
||||
42 99
|
||||
60
|
||||
|
||||
4
tests/variadic_extern.meta.json
Normal file
4
tests/variadic_extern.meta.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"description": "Test variadic and non-variadic extern declarations with companion C file",
|
||||
"libs": ["libc.so.6"]
|
||||
}
|
||||
34
tests/variadic_extern.sl
Normal file
34
tests/variadic_extern.sl
Normal file
@@ -0,0 +1,34 @@
|
||||
import stdlib.sl
|
||||
|
||||
# Test variadic extern declarations.
|
||||
# For variadic externs, the TOS literal before the call is the number of
|
||||
# extra variadic arguments. The compiler consumes it (not passed to C).
|
||||
# String literals push (ptr, len) — use drop to discard the length for C.
|
||||
|
||||
# printf: 1 fixed param (fmt), variadic args via TOS count
|
||||
extern int printf(const char *fmt, ...)
|
||||
extern int fflush(long stream)
|
||||
|
||||
# Custom C variadic: sums args until sentinel -1 is seen
|
||||
extern long va_sum_sentinel(long first, ...)
|
||||
|
||||
# Non-variadic extern for comparison
|
||||
extern long add_two(long a, long b)
|
||||
|
||||
word main
|
||||
# Test 1: non-variadic add_two
|
||||
10 20 add_two puti cr
|
||||
|
||||
# Test 2: printf with 0 variadic args (just format string)
|
||||
"hello\n" drop 0 printf drop
|
||||
0 fflush drop
|
||||
|
||||
# Test 3: printf with 2 variadic args
|
||||
"%d %d\n" drop 42 99 2 printf drop
|
||||
0 fflush drop
|
||||
|
||||
# Test 4: va_sum_sentinel(10, 20, 30, -1) = 60
|
||||
10 20 30 -1 3 va_sum_sentinel puti cr
|
||||
|
||||
0
|
||||
end
|
||||
Reference in New Issue
Block a user