update
This commit is contained in:
5
SPEC.md
5
SPEC.md
@@ -60,6 +60,7 @@ This document reflects the implementation that ships in this repository today (`
|
|||||||
- Reader hooks: `set-token-hook` installs a word that receives each token (pushed as a `Token` object) and must leave a truthy handled flag; `clear-token-hook` disables it. `libs/fn.sl`'s `extend-syntax` demonstrates rewriting `foo(1, 2)` into ordinary word calls.
|
- Reader hooks: `set-token-hook` installs a word that receives each token (pushed as a `Token` object) and must leave a truthy handled flag; `clear-token-hook` disables it. `libs/fn.sl`'s `extend-syntax` demonstrates rewriting `foo(1, 2)` into ordinary word calls.
|
||||||
- Prelude/BSS control: `prelude-clear`, `prelude-append`, `prelude-set`, `bss-clear`, `bss-append`, `bss-set` let user code override the `_start` stub or `.bss` layout.
|
- Prelude/BSS control: `prelude-clear`, `prelude-append`, `prelude-set`, `bss-clear`, `bss-append`, `bss-set` let user code override the `_start` stub or `.bss` layout.
|
||||||
- Definition helpers: `emit-definition` injects a `word ... end` definition on the fly (used by the struct macro). `parse-error` raises a custom diagnostic.
|
- Definition helpers: `emit-definition` injects a `word ... end` definition on the fly (used by the struct macro). `parse-error` raises a custom diagnostic.
|
||||||
|
- Assertions: `static_assert` is a compile-time-only primitive that pops a condition and raises `ParseError("static assertion failed at <path>:<line>:<column>")` when the value is zero/false.
|
||||||
- **Text macros** – `macro` is an immediate word implemented in Python; it prevents nesting by tracking active recordings and registers expansion tokens with `$n` substitution.
|
- **Text macros** – `macro` is an immediate word implemented in Python; it prevents nesting by tracking active recordings and registers expansion tokens with `$n` substitution.
|
||||||
- **Python bridges** – `:py name { ... } ;` executes once during parsing. The body may define `macro(ctx: MacroContext)` (with helpers such as `next_token`, `emit_literal`, `inject_tokens`, `new_label`, and direct `parser` access) and/or `intrinsic(builder: FunctionEmitter)` to emit assembly directly. The `fn` DSL (`libs/fn.sl`) and other syntax layers are ordinary `:py` blocks.
|
- **Python bridges** – `:py name { ... } ;` executes once during parsing. The body may define `macro(ctx: MacroContext)` (with helpers such as `next_token`, `emit_literal`, `inject_tokens`, `new_label`, and direct `parser` access) and/or `intrinsic(builder: FunctionEmitter)` to emit assembly directly. The `fn` DSL (`libs/fn.sl`) and other syntax layers are ordinary `:py` blocks.
|
||||||
|
|
||||||
@@ -76,10 +77,10 @@ This document reflects the implementation that ships in this repository today (`
|
|||||||
- **`mem.sl`** – `alloc`/`free` wrappers around `mmap`/`munmap` plus a byte-wise `memcpy` used by higher-level utilities.
|
- **`mem.sl`** – `alloc`/`free` wrappers around `mmap`/`munmap` plus a byte-wise `memcpy` used by higher-level utilities.
|
||||||
- **`io.sl`** – `read_file`, `write_file`, `read_stdin`, `write_buf`, `ewrite_buf`, `putc`, `puti`, `puts`, `eputs`, and a smart `print` that detects `(addr,len)` pairs located inside the default `.data` region.
|
- **`io.sl`** – `read_file`, `write_file`, `read_stdin`, `write_buf`, `ewrite_buf`, `putc`, `puti`, `puts`, `eputs`, and a smart `print` that detects `(addr,len)` pairs located inside the default `.data` region.
|
||||||
- **`utils.sl`** – String and number helpers (`strcmp`, `strconcat`, `strlen`, `digitsN>num`, `toint`, `count_digits`, `tostr`).
|
- **`utils.sl`** – String and number helpers (`strcmp`, `strconcat`, `strlen`, `digitsN>num`, `toint`, `count_digits`, `tostr`).
|
||||||
- **`arr.sl`** – Dynamically sized qword arrays with `arr_new`, `arr_len`, `arr_cap`, `arr_data`, `arr_push`, `arr_pop`, `arr_reserve`, `arr_free`.
|
- **`arr.sl`** – Dynamically sized qword arrays with `arr_new`, `arr_len`, `arr_cap`, `arr_data`, `arr_push`, `arr_pop`, `arr_reserve`, `arr_free`; built-in static-array sorting via `arr_sort`/`arr_sorted`; and dynamic-array sorting via `dyn_arr_sort`/`dyn_arr_sorted`.
|
||||||
- **`float.sl`** – SSE-based double-precision arithmetic (`f+`, `f-`, `f*`, `f/`, `fneg`, comparisons, `int>float`, `float>int`, `fput`, `fputln`).
|
- **`float.sl`** – SSE-based double-precision arithmetic (`f+`, `f-`, `f*`, `f/`, `fneg`, comparisons, `int>float`, `float>int`, `fput`, `fputln`).
|
||||||
- **`linux.sl`** – Auto-generated syscall macros (one constant block per entry in `syscall_64.tbl`) plus the `syscallN` helpers implemented purely in assembly so the file can be used in isolation.
|
- **`linux.sl`** – Auto-generated syscall macros (one constant block per entry in `syscall_64.tbl`) plus the `syscallN` helpers implemented purely in assembly so the file can be used in isolation.
|
||||||
- **`debug.sl`** – Diagnostics such as `dump`, `rdump`, and `int3`.
|
- **`debug.sl`** – Diagnostics and checks such as `dump`, `rdump`, `int3`, runtime `assert` (prints `assertion failed` and exits with code 1), `assert_msg` (message + condition; exits with message when false), `abort` (prints `abort` and exits with code 1), and `abort_msg` (prints caller-provided message and exits with code 1).
|
||||||
- **`stdlib.sl`** – Convenience aggregator that imports `core`, `mem`, `io`, and `utils` so most programs can simply `import stdlib/stdlib.sl`.
|
- **`stdlib.sl`** – Convenience aggregator that imports `core`, `mem`, `io`, and `utils` so most programs can simply `import stdlib/stdlib.sl`.
|
||||||
|
|
||||||
## 9. Testing and Usage Patterns
|
## 9. Testing and Usage Patterns
|
||||||
|
|||||||
33
main.py
33
main.py
@@ -1433,6 +1433,7 @@ class CompileTimeVM:
|
|||||||
self._dl_handles: List[Any] = [] # ctypes.CDLL handles
|
self._dl_handles: List[Any] = [] # ctypes.CDLL handles
|
||||||
self._dl_func_cache: Dict[str, Any] = {} # name → ctypes callable
|
self._dl_func_cache: Dict[str, Any] = {} # name → ctypes callable
|
||||||
self._ct_libs: List[str] = [] # library names from -l flags
|
self._ct_libs: List[str] = [] # library names from -l flags
|
||||||
|
self.current_location: Optional[SourceLocation] = None
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
self.stack.clear()
|
self.stack.clear()
|
||||||
@@ -1443,6 +1444,7 @@ class CompileTimeVM:
|
|||||||
self._list_capture_stack.clear()
|
self._list_capture_stack.clear()
|
||||||
self.r12 = 0
|
self.r12 = 0
|
||||||
self.r13 = 0
|
self.r13 = 0
|
||||||
|
self.current_location = None
|
||||||
|
|
||||||
def invoke(self, word: Word, *, runtime_mode: bool = False, libs: Optional[List[str]] = None) -> None:
|
def invoke(self, word: Word, *, runtime_mode: bool = False, libs: Optional[List[str]] = None) -> None:
|
||||||
self.reset()
|
self.reset()
|
||||||
@@ -2194,8 +2196,14 @@ class CompileTimeVM:
|
|||||||
if defn._begin_pairs is None:
|
if defn._begin_pairs is None:
|
||||||
defn._begin_pairs = self._begin_pairs(defn.body)
|
defn._begin_pairs = self._begin_pairs(defn.body)
|
||||||
self._resolve_words_in_body(defn)
|
self._resolve_words_in_body(defn)
|
||||||
if self.runtime_mode and defn._merged_runs is None:
|
if self.runtime_mode:
|
||||||
defn._merged_runs = self._find_mergeable_runs(defn)
|
# Merged JIT runs are a performance optimization, but have shown
|
||||||
|
# intermittent instability on some environments. Keep them opt-in.
|
||||||
|
if os.environ.get("L2_CT_MERGED_JIT", "0") == "1":
|
||||||
|
if defn._merged_runs is None:
|
||||||
|
defn._merged_runs = self._find_mergeable_runs(defn)
|
||||||
|
else:
|
||||||
|
defn._merged_runs = {}
|
||||||
return defn._label_positions, defn._for_pairs, defn._begin_pairs
|
return defn._label_positions, defn._for_pairs, defn._begin_pairs
|
||||||
|
|
||||||
def _find_mergeable_runs(self, defn: Definition) -> Dict[int, Tuple[int, str]]:
|
def _find_mergeable_runs(self, defn: Definition) -> Dict[int, Tuple[int, str]]:
|
||||||
@@ -2387,9 +2395,11 @@ class CompileTimeVM:
|
|||||||
|
|
||||||
n_nodes = len(nodes)
|
n_nodes = len(nodes)
|
||||||
ip = 0
|
ip = 0
|
||||||
|
prev_location = self.current_location
|
||||||
try:
|
try:
|
||||||
while ip < n_nodes:
|
while ip < n_nodes:
|
||||||
node = nodes[ip]
|
node = nodes[ip]
|
||||||
|
self.current_location = node.loc
|
||||||
kind = node.op
|
kind = node.op
|
||||||
|
|
||||||
if kind == "word":
|
if kind == "word":
|
||||||
@@ -2627,6 +2637,7 @@ class CompileTimeVM:
|
|||||||
|
|
||||||
raise ParseError(f"unsupported compile-time op {node!r}")
|
raise ParseError(f"unsupported compile-time op {node!r}")
|
||||||
finally:
|
finally:
|
||||||
|
self.current_location = prev_location
|
||||||
self.loop_stack = prev_loop_stack
|
self.loop_stack = prev_loop_stack
|
||||||
|
|
||||||
def _label_positions(self, nodes: Sequence[Op]) -> Dict[str, int]:
|
def _label_positions(self, nodes: Sequence[Op]) -> Dict[str, int]:
|
||||||
@@ -4579,6 +4590,23 @@ def _ct_parse_error(vm: CompileTimeVM) -> None:
|
|||||||
raise ParseError(message)
|
raise ParseError(message)
|
||||||
|
|
||||||
|
|
||||||
|
def _ct_static_assert(vm: CompileTimeVM) -> None:
|
||||||
|
condition = vm._resolve_handle(vm.pop())
|
||||||
|
if isinstance(condition, bool):
|
||||||
|
ok = condition
|
||||||
|
elif isinstance(condition, int):
|
||||||
|
ok = condition != 0
|
||||||
|
else:
|
||||||
|
raise ParseError(
|
||||||
|
f"static_assert expects integer/boolean condition, got {type(condition).__name__}"
|
||||||
|
)
|
||||||
|
if not ok:
|
||||||
|
loc = vm.current_location
|
||||||
|
if loc is not None:
|
||||||
|
raise ParseError(f"static assertion failed at {loc.path}:{loc.line}:{loc.column}")
|
||||||
|
raise ParseError("static assertion failed")
|
||||||
|
|
||||||
|
|
||||||
def _ct_lexer_new(vm: CompileTimeVM) -> None:
|
def _ct_lexer_new(vm: CompileTimeVM) -> None:
|
||||||
separators = vm.pop_str()
|
separators = vm.pop_str()
|
||||||
vm.push(SplitLexer(vm.parser, separators))
|
vm.push(SplitLexer(vm.parser, separators))
|
||||||
@@ -4902,6 +4930,7 @@ def _register_compile_time_primitives(dictionary: Dictionary) -> None:
|
|||||||
word_use_l2.immediate = True
|
word_use_l2.immediate = True
|
||||||
register("emit-definition", _ct_emit_definition, compile_only=True)
|
register("emit-definition", _ct_emit_definition, compile_only=True)
|
||||||
register("parse-error", _ct_parse_error, compile_only=True)
|
register("parse-error", _ct_parse_error, compile_only=True)
|
||||||
|
register("static_assert", _ct_static_assert, compile_only=True)
|
||||||
|
|
||||||
register("lexer-new", _ct_lexer_new, compile_only=True)
|
register("lexer-new", _ct_lexer_new, compile_only=True)
|
||||||
register("lexer-pop", _ct_lexer_pop, compile_only=True)
|
register("lexer-pop", _ct_lexer_pop, compile_only=True)
|
||||||
|
|||||||
109
stdlib/arr.sl
109
stdlib/arr.sl
@@ -127,3 +127,112 @@ end
|
|||||||
word arr_set
|
word arr_set
|
||||||
arr_data swap 8 * + swap !
|
arr_data swap 8 * + swap !
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#dyn_arr_clone [* | dyn_arr] -> [* | dyn_arr_copy]
|
||||||
|
word dyn_arr_clone
|
||||||
|
dup arr_len
|
||||||
|
dup arr_new
|
||||||
|
|
||||||
|
dup arr_data
|
||||||
|
3 pick arr_data
|
||||||
|
3 pick
|
||||||
|
arr_copy_elements
|
||||||
|
|
||||||
|
dup >r
|
||||||
|
swap !
|
||||||
|
drop
|
||||||
|
r>
|
||||||
|
end
|
||||||
|
|
||||||
|
#arr_item_ptr [*, i | arr] -> [* | ptr]
|
||||||
|
word arr_item_ptr
|
||||||
|
swap 8 * swap 8 + +
|
||||||
|
end
|
||||||
|
|
||||||
|
#arr_get [*, i | arr] -> [* | x]
|
||||||
|
# Get element from built-in static array
|
||||||
|
word arr_get_static
|
||||||
|
arr_item_ptr @
|
||||||
|
end
|
||||||
|
|
||||||
|
#arr_set [*, x, i | arr] -> [*]
|
||||||
|
# Set element in built-in static array
|
||||||
|
word arr_set_static
|
||||||
|
arr_item_ptr swap !
|
||||||
|
end
|
||||||
|
|
||||||
|
#arr_sort [* | arr] -> [* | arr]
|
||||||
|
# Sort built-in static array in-place in ascending order
|
||||||
|
word arr_sort
|
||||||
|
dup >r
|
||||||
|
dup arr_to_dyn
|
||||||
|
dyn_arr_sort
|
||||||
|
dup arr_data
|
||||||
|
r@ 8 +
|
||||||
|
swap
|
||||||
|
r@ @
|
||||||
|
arr_copy_elements
|
||||||
|
arr_free
|
||||||
|
rdrop
|
||||||
|
end
|
||||||
|
|
||||||
|
#dyn_arr_sort [* | dyn_arr] -> [* | dyn_arr]
|
||||||
|
:asm dyn_arr_sort {
|
||||||
|
mov rbx, [r12] ; arr
|
||||||
|
mov rcx, [rbx] ; len
|
||||||
|
cmp rcx, 1
|
||||||
|
jle .done
|
||||||
|
|
||||||
|
dec rcx ; outer = len - 1
|
||||||
|
.outer:
|
||||||
|
xor rdx, rdx ; j = 0
|
||||||
|
|
||||||
|
.inner:
|
||||||
|
cmp rdx, rcx
|
||||||
|
jge .next_outer
|
||||||
|
|
||||||
|
mov r8, [rbx + 16] ; data ptr
|
||||||
|
lea r9, [r8 + rdx*8] ; &data[j]
|
||||||
|
mov r10, [r9] ; a = data[j]
|
||||||
|
mov r11, [r9 + 8] ; b = data[j+1]
|
||||||
|
cmp r10, r11
|
||||||
|
jle .no_swap
|
||||||
|
|
||||||
|
mov [r9], r11
|
||||||
|
mov [r9 + 8], r10
|
||||||
|
|
||||||
|
.no_swap:
|
||||||
|
inc rdx
|
||||||
|
jmp .inner
|
||||||
|
|
||||||
|
.next_outer:
|
||||||
|
dec rcx
|
||||||
|
jnz .outer
|
||||||
|
|
||||||
|
.done:
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
#arr_clone [* | arr] -> [* | arr_copy]
|
||||||
|
# Clone built-in static array (len header + payload)
|
||||||
|
word arr_clone
|
||||||
|
dup @ 1 +
|
||||||
|
dup 8 * alloc
|
||||||
|
dup >r
|
||||||
|
rot rot
|
||||||
|
arr_copy_elements
|
||||||
|
r>
|
||||||
|
end
|
||||||
|
|
||||||
|
#arr_sorted [* | arr] -> [* | arr_sorted]
|
||||||
|
word arr_sorted
|
||||||
|
arr_clone
|
||||||
|
arr_sort
|
||||||
|
end
|
||||||
|
|
||||||
|
#dyn_arr_sorted [* | dyn_arr] -> [* | dyn_arr_sorted]
|
||||||
|
word dyn_arr_sorted
|
||||||
|
dyn_arr_clone
|
||||||
|
dyn_arr_sort
|
||||||
|
end
|
||||||
|
|||||||
@@ -70,3 +70,32 @@ end
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
#abort [*] -> [*]
|
||||||
|
word abort
|
||||||
|
"abort" eputs
|
||||||
|
1 exit
|
||||||
|
end
|
||||||
|
|
||||||
|
#abort_msg [* | msg] -> [*]
|
||||||
|
word abort_msg
|
||||||
|
eputs
|
||||||
|
1 exit
|
||||||
|
end
|
||||||
|
|
||||||
|
#assert [* | cond] -> [*]
|
||||||
|
word assert
|
||||||
|
if
|
||||||
|
else
|
||||||
|
"assertion failed" abort_msg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#assert_msg [*, msg | cond] -> [*]
|
||||||
|
word assert_msg
|
||||||
|
if
|
||||||
|
2drop
|
||||||
|
else
|
||||||
|
abort_msg
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -22,3 +22,18 @@
|
|||||||
7
|
7
|
||||||
8
|
8
|
||||||
9
|
9
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
3
|
||||||
|
1
|
||||||
|
2
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
4
|
||||||
|
6
|
||||||
|
9
|
||||||
|
4
|
||||||
|
9
|
||||||
|
6
|
||||||
|
|||||||
@@ -56,4 +56,44 @@ word main
|
|||||||
|
|
||||||
# free list allocation: bytes = (len + 1) * 8
|
# free list allocation: bytes = (len + 1) * 8
|
||||||
dup @ 1 + 8 * free
|
dup @ 1 + 8 * free
|
||||||
|
|
||||||
|
# dyn_arr_sorted (copy) should not mutate source
|
||||||
|
5 arr_new
|
||||||
|
3 swap arr_push
|
||||||
|
1 swap arr_push
|
||||||
|
2 swap arr_push
|
||||||
|
|
||||||
|
dup dyn_arr_sorted
|
||||||
|
dup 0 swap arr_get puti cr
|
||||||
|
dup 1 swap arr_get puti cr
|
||||||
|
dup 2 swap arr_get puti cr
|
||||||
|
arr_free
|
||||||
|
|
||||||
|
dup 0 swap arr_get puti cr
|
||||||
|
dup 1 swap arr_get puti cr
|
||||||
|
dup 2 swap arr_get puti cr
|
||||||
|
|
||||||
|
# dyn_arr_sort (alias) sorts in place
|
||||||
|
dyn_arr_sort
|
||||||
|
dup 0 swap arr_get puti cr
|
||||||
|
dup 1 swap arr_get puti cr
|
||||||
|
dup 2 swap arr_get puti cr
|
||||||
|
arr_free
|
||||||
|
|
||||||
|
# dyn_arr_sorted (alias) returns a sorted copy
|
||||||
|
5 arr_new
|
||||||
|
4 swap arr_push
|
||||||
|
9 swap arr_push
|
||||||
|
6 swap arr_push
|
||||||
|
|
||||||
|
dup dyn_arr_sorted
|
||||||
|
dup 0 swap arr_get puti cr
|
||||||
|
dup 1 swap arr_get puti cr
|
||||||
|
dup 2 swap arr_get puti cr
|
||||||
|
arr_free
|
||||||
|
|
||||||
|
dup 0 swap arr_get puti cr
|
||||||
|
dup 1 swap arr_get puti cr
|
||||||
|
dup 2 swap arr_get puti cr
|
||||||
|
arr_free
|
||||||
end
|
end
|
||||||
|
|||||||
10
tests/arr_static_sort.expected
Normal file
10
tests/arr_static_sort.expected
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
4
|
||||||
|
5
|
||||||
|
7
|
||||||
|
9
|
||||||
|
9
|
||||||
|
5
|
||||||
|
7
|
||||||
29
tests/arr_static_sort.sl
Normal file
29
tests/arr_static_sort.sl
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import ../stdlib/stdlib.sl
|
||||||
|
import ../stdlib/io.sl
|
||||||
|
import ../stdlib/arr.sl
|
||||||
|
|
||||||
|
word free_static
|
||||||
|
dup @ 1 + 8 * free
|
||||||
|
end
|
||||||
|
|
||||||
|
word main
|
||||||
|
[ 4 1 3 2 ] dup arr_sort
|
||||||
|
dup 0 swap arr_get_static puti cr
|
||||||
|
dup 1 swap arr_get_static puti cr
|
||||||
|
dup 2 swap arr_get_static puti cr
|
||||||
|
dup 3 swap arr_get_static puti cr
|
||||||
|
free_static
|
||||||
|
|
||||||
|
[ 9 5 7 ] dup arr_sorted
|
||||||
|
dup 0 swap arr_get_static puti cr
|
||||||
|
dup 1 swap arr_get_static puti cr
|
||||||
|
dup 2 swap arr_get_static puti cr
|
||||||
|
|
||||||
|
swap
|
||||||
|
dup 0 swap arr_get_static puti cr
|
||||||
|
dup 1 swap arr_get_static puti cr
|
||||||
|
dup 2 swap arr_get_static puti cr
|
||||||
|
|
||||||
|
free_static
|
||||||
|
free_static
|
||||||
|
end
|
||||||
0
tests/assert_msg_fail.expected
Normal file
0
tests/assert_msg_fail.expected
Normal file
3
tests/assert_msg_fail.meta.json
Normal file
3
tests/assert_msg_fail.meta.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"expected_exit": 1
|
||||||
|
}
|
||||||
6
tests/assert_msg_fail.sl
Normal file
6
tests/assert_msg_fail.sl
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import stdlib/debug.sl
|
||||||
|
|
||||||
|
word main
|
||||||
|
"boom msg" 0 assert_msg
|
||||||
|
0
|
||||||
|
end
|
||||||
1
tests/assert_msg_fail.stderr
Normal file
1
tests/assert_msg_fail.stderr
Normal file
@@ -0,0 +1 @@
|
|||||||
|
boom msg
|
||||||
2
tests/debug_assert.expected
Normal file
2
tests/debug_assert.expected
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
debug assert ok
|
||||||
|
assert_msg ok
|
||||||
10
tests/debug_assert.sl
Normal file
10
tests/debug_assert.sl
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import stdlib/debug.sl
|
||||||
|
import stdlib/io.sl
|
||||||
|
|
||||||
|
word main
|
||||||
|
1 assert
|
||||||
|
2 2 == assert
|
||||||
|
"should not print" 1 assert_msg
|
||||||
|
"debug assert ok" puts
|
||||||
|
"assert_msg ok" puts
|
||||||
|
end
|
||||||
1
tests/static_assert.expected
Normal file
1
tests/static_assert.expected
Normal file
@@ -0,0 +1 @@
|
|||||||
|
static assert ok
|
||||||
12
tests/static_assert.sl
Normal file
12
tests/static_assert.sl
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import stdlib/debug.sl
|
||||||
|
import stdlib/io.sl
|
||||||
|
|
||||||
|
word ct_checks
|
||||||
|
1 static_assert
|
||||||
|
2 3 < static_assert
|
||||||
|
end
|
||||||
|
compile-time ct_checks
|
||||||
|
|
||||||
|
word main
|
||||||
|
"static assert ok" puts
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user