aboutsummaryrefslogtreecommitdiff
path: root/tests/drivers/fuzz_compiler.py
diff options
context:
space:
mode:
authorGravatar Moon Flower <moonflower@elhenney.local> 2026-10-14 08:29:46 +0330
committerGravatar Moon Flower <moonflower@elhenney.local> 2026-10-14 08:29:46 +0330
commit1c53304d03414890af0da7432d1aea87e754a14a (patch)
tree97190d86cf548c9bc63f68c86a4d2a4f09809713 /tests/drivers/fuzz_compiler.py
parenta (diff)
feat: grow memory limits, add icon/actor libraries, overhaul LSP diagnostics
Compiler and VM - make every memory map limit dynamic and adjustable at both build time and runtime, including compiler tables, stacks, and every execution region - reserve and grow tables, operand and return stacks, frame segments, and image-building output without relocating live code or globals - rename i/o and string helpers consistently (puts -> put_s, putn -> put_n, scat -> cat_s) across compiler, runtime, examples, tests, and docs - recast and consume values assigned to globals from inside functions - allow fixed and const together, add typed closure returns (fn:t(...)), multi-include syntax (include rt, run, core;), and recurse type inference - never infer string literals as word, anywhere, as argument, variable, or return type - draft docs/BORROW.md: a borrow checker less intrusive than Rust's but more intelligent LSP and editor integration - require explicit includes and give every toolchain area its own namespace (rt, lsp, run, core) - hover for built-in types, inferred types, values known at hover time, array and pointer lifetime/size, library names in include statements, and inferred lifetimes with explanations - honor /* */ comments as documentation for variables and arguments, and stop misattributing section headers or trailing comments as variable hints - stream diagnostics and cancel stale requests on change - jump to definition and references, including cross-file - quick fixes with automatic library import, and namespace completion Hints and diagnostics - render hints as blue squigglies by default, with hints on/small/off per file - suppress magic-number hints via a preceding /* */ comment, and say so - warn on unused arguments; allow multiple _ discards in parameters, pattern matching, and tagged enums, warning if a _ is actually used - rewrite hint and comment prose in professional compiler-doc register Libraries - add icon.zc: run-time self-modification, find/replace syntax, type and block edits, and step-through inspection of locals and globals - add actor.zc: async actors with Actor<I,O>, implicit this, spawn/recv/send, injected into the compiler machinery at include time rather than built in Tooling and docs - reorganize sources, tests, and designs into subdirectories - unify windows entry points in tinyvm.bat and add a matching tinyvm.sh - add --help across the toolchain - polish the learning guide, stdlib docs, and design notes Tests - add accept, reject, and regression coverage across the compiler, LSP, - runtime, icon, actors, threads, formatting, and the toolchain contracts, plus fuzzers and property tests
Diffstat (limited to 'tests/drivers/fuzz_compiler.py')
-rw-r--r--tests/drivers/fuzz_compiler.py87
1 files changed, 87 insertions, 0 deletions
diff --git a/tests/drivers/fuzz_compiler.py b/tests/drivers/fuzz_compiler.py
new file mode 100644
index 0000000..512ec26
--- /dev/null
+++ b/tests/drivers/fuzz_compiler.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+"""Deterministic grammar fuzzing for the parser, runtime, and ownership paths.
+
+Usage: python3 tests/drivers/fuzz_compiler.py [path/to/zy] [path/to/zym] [--js]
+"""
+import random
+import subprocess
+import sys
+
+
+args = [arg for arg in sys.argv[1:] if arg != "--js"]
+js = "--js" in sys.argv[1:]
+zy = args[0] if len(args) > 0 else "bin/zy"
+zym = args[1] if len(args) > 1 else "bin/zym"
+rng = random.Random(0x5A17)
+values = []
+library_values = []
+
+
+def expr(depth):
+ if depth == 0 or rng.randrange(4) == 0:
+ value = rng.randrange(10)
+ return str(value), value
+ left, a = expr(depth - 1)
+ right, b = expr(depth - 1)
+ op = rng.choice(("+", "-", "*"))
+ if op == "+":
+ value = a + b
+ elif op == "-":
+ value = a - b
+ else:
+ value = a * b
+ return f"({left} {op} {right})", value
+
+
+lines = ["include std;", "fn churn(n) {", " int i = 0;", " while (i < n) {", " { int* p = new(int); *p = i; }", " i++;", " }", " return 0;", "}", "fn stable() { return 73; }", "fn main() {", " let base = live_objects();"]
+for _ in range(128):
+ source, value = expr(4)
+ lines.append(f" put_n({source}); put_c(32);")
+ values.append(value)
+for _ in range(32):
+ a = "".join(rng.choice("abc") for _ in range(rng.randrange(11)))
+ b = "".join(rng.choice("abc") for _ in range(rng.randrange(6)))
+ lines.append(f' put_n(Text.len("{a}")); put_c(32); put_n(Text.eq("{a}", "{b}")); put_c(32);')
+ lines.append(f' put_n(Text.starts("{a}", "{b}")); put_c(32); put_n(Text.ends("{a}", "{b}")); put_c(32); put_n(Text.find("{a}", "{b}")); put_c(32);')
+ lines.append(f' put_n(Bytes.len(b"{a}")); put_c(32); put_n(Bytes.eq(b"{a}", b"{b}")); put_c(32);')
+ lines.append(f' put_n(Bytes.starts(b"{a}", b"{b}")); put_c(32); put_n(Bytes.ends(b"{a}", b"{b}")); put_c(32); put_n(Bytes.find(b"{a}", b"{b}")); put_c(32);')
+ library_values += [len(a), int(a == b), int(a.startswith(b)), int(a.endswith(b)), a.find(b), len(a.encode()), int(a == b), int(a.startswith(b)), int(a.endswith(b)), a.find(b)]
+lines += [
+ " churn(300); gc(); put_n(live_objects() - base); put_c(32);",
+ ' char* source = "fn f() { return 7; }"; let parsed = tokens(source);',
+ ' put_n(parsed[0] > 0); put_c(32); put_n(eq_w(source, "fn f() { return 7; }")); put_c(32);',
+ ' char* eval_source = "return 42;"; int e = 0;',
+ ' while (e < 16) { let q = tokens(eval_source); put_n(eval(q)); put_c(32); e++; }',
+ ' put_n(stable()); put_c(32); put_n(eq_w(eval_source, "return 42;")); put_c(10);',
+ " return 0;",
+ "}",
+]
+program = "\n".join(lines) + "\n"
+expected = " ".join(str(x) for x in values + library_values + [0, 1, 1, *([42] * 16), 73, 1]) + "\n"
+
+
+def run(binary):
+ result = subprocess.run(binary, input=program, text=True, capture_output=True, timeout=30)
+ if result.returncode or result.stdout != expected:
+ actual_words = result.stdout.split()
+ expected_words = expected.split()
+ mismatch = next((i for i, pair in enumerate(zip(actual_words, expected_words)) if pair[0] != pair[1]), min(len(actual_words), len(expected_words)))
+ raise SystemExit(
+ f"{' '.join(binary)} failed deterministic fuzz seed 0x5a17\n"
+ f"exit={result.returncode}, token mismatch at {mismatch}\n"
+ f"expected={expected_words[max(0, mismatch - 3):mismatch + 4]!r}\n"
+ f"stdout={actual_words[max(0, mismatch - 3):mismatch + 4]!r}\n"
+ f"stderr={result.stderr!r}"
+ )
+ return result.stdout
+
+
+hosts = [(zy, [zy]), (zym, [zym])]
+if js:
+ hosts.append(("javascript", ["node", "src/js/tinyvm.js"]))
+outputs = [run(command) for _, command in hosts]
+if any(output != outputs[0] for output in outputs[1:]):
+ raise SystemExit("compiler hosts disagreed on deterministic fuzz output")
+print("ok compiler/runtime/stdlib fuzz: 128 generated expressions, 300 scoped allocations and GC, "
+ "16 eval recompiles, unchanged source strings, and 32 randomized Text/Bytes cases on "
+ + ", ".join(name for name, _ in hosts))