aboutsummaryrefslogtreecommitdiff
path: root/tests/programs
diff options
context:
space:
mode:
authorGravatar zinc <zinc@downsta.ir> 2026-10-14 09:48:30 +0000
committerGravatar zinc <zinc@downsta.ir> 2026-10-14 09:48:30 +0000
commit4478d6d926e0d2ccb5c267f4b9d6648fa6bcc43a (patch)
tree8b28bb1e14eab791606cb7ec996a3ae8162fab1d /tests/programs
parenttodo: tick the parity runner; zc0 follow-ups it found (diff)
parentzc0: byte string types, u/m/d as names, enhancements in asm (diff)
Merge branch 'worktree-agent-a0734822346ae18db' into ccr-0054c02e-dfdvnd
Diffstat (limited to 'tests/programs')
-rw-r--r--tests/programs/bareio.zc2
-rw-r--r--tests/programs/zc0bstr.zc96
-rw-r--r--tests/programs/zc0umd.zc58
3 files changed, 155 insertions, 1 deletions
diff --git a/tests/programs/bareio.zc b/tests/programs/bareio.zc
index a06ef38..21d2b40 100644
--- a/tests/programs/bareio.zc
+++ b/tests/programs/bareio.zc
@@ -2,7 +2,7 @@ namespace;
include rt;
// bare-metal syscalls: clock, a RAM file, random, and the machine size.
-// zc0 subset, so a run-only image can carry it. names stay off u/m/d.
+// zc0 subset, so a run-only image can carry it.
fn main() {
let buf[16], n, t0, t1, i, fd, r, board[8];
t0 = rt.syscall(6, 0, 0, 0);
diff --git a/tests/programs/zc0bstr.zc b/tests/programs/zc0bstr.zc
new file mode 100644
index 0000000..bb4ffbc
--- /dev/null
+++ b/tests/programs/zc0bstr.zc
@@ -0,0 +1,96 @@
+namespace;
+include rt;
+
+// packed byte strings in the zc0 subset: the same program runs on the resident
+// compiler and, built into a run-only image, on stage 0 (src/tools/zc0.c). it
+// covers every b"..." escape, the packed layout, bstr locals, globals,
+// parameters and returns, + and the six comparisons, indexing, slicing, len
+// and embed. run from the repository root: the embeds read tests/data/embed.
+
+bstr greeting = b"hello";
+bstr shout;
+bstr plain = b"plain";
+embed "tests/data/embed/text.txt" as bstr text;
+embed "tests/data/embed/nul.bin" as bstr nul;
+embed "tests/data/embed/text.txt" as untyped;
+
+bstr tag(bstr s) { return b"<" + s + b">"; }
+bstr twice(bstr s);
+bstr twice(bstr s) { return s + s; }
+bstr pick(int n) {
+ bstr r;
+ if (n) r = b"yes"; else r = b"no";
+ return r;
+}
+int rank(bstr a, bstr b) { return (a < b) * 8 + (a <= b) * 4 + (a == b) * 2 + (a > b); }
+int count(bstr s, int c) {
+ int n = 0, i = 0;
+ while (i < len(s)) { if (s[i] == c) n++; i++; }
+ return n;
+}
+int num(int n) { rt.put_n(n); rt.put_c(32); return 0; }
+int nl() { rt.put_c(10); return 0; }
+int bytes(bstr s) {
+ int i = 0;
+ num(len(s));
+ while (i < len(s)) { num(s[i]); i++; }
+ nl();
+ return 0;
+}
+int words(bstr s) {
+ word* w = rt.bstr_storage(s);
+ int n = 1 + (w[0] >> 3) + 1, i = 0;
+ while (i < n) { num(w[i]); i++; }
+ nl();
+ return 0;
+}
+
+fn main() {
+ bstr a = b"abc", b, c;
+ bstr e = b"";
+ let u = b"x";
+ // every escape the lexer knows: n t r 0 e, and any other character stands for itself
+ bytes(b"\n\t\r\e\\\"\'\q\x41");
+ bytes(b"ab\0cd");
+ bytes(b"\0");
+ bytes(b"café");
+ bytes(b"caf\303\251");
+ bytes(b"quote \" inside, 'single', back\\slash");
+ // the layout: a length word, bytes packed 8 to a word, a 0 word after a full last word
+ words(e);
+ words(b"a");
+ words(b"abcdefg");
+ words(b"abcdefgh");
+ words(b"abcdefghi");
+ words(b"0123456789abcdef");
+ words(b"0123456789abcdefg");
+ words(text);
+ words(nul);
+ // declarations, assignment later, a return value, concatenation
+ b = tag(a);
+ c = twice(b) + e + u;
+ rt.put_s(a); rt.put_c(32); rt.put_s(b); rt.put_c(32); rt.put_s(c); nl();
+ shout = twice(greeting) + b"!";
+ rt.put_s(greeting); rt.put_c(32); rt.put_s(shout); rt.put_c(32); rt.put_s(plain); nl();
+ rt.put_s(tag(twice(tag(b"z")))); nl();
+ rt.put_s(pick(1) + b"/" + pick(0)); nl();
+ // comparisons: below, equal, above, a prefix, the empty string, a 9 byte string
+ num(rank(b"abc", b"abd")); num(rank(b"abc", b"abc")); num(rank(b"abd", b"abc"));
+ num(rank(b"ab", b"abc")); num(rank(b"", b"")); num(rank(b"", b"a"));
+ num(rank(b"abcdefghi", b"abcdefghi")); num(rank(b"abcdefgh", b"abcdefghi")); nl();
+ num(a == b"abc"); num(a != b"abc"); num(a == b); num(a != b);
+ num(tag(a) == b"<abc>"); num(shout > greeting); num(greeting >= shout); num(plain <= plain); nl();
+ // indexing, slicing, len
+ num(a[0]); num(a[2]); num(a[3]); num(len(a)); num(len(e)); num(len(c)); num(len(b"123456789")); nl();
+ rt.put_s(c[1:4]); rt.put_c(32); rt.put_s(a[1:2]); rt.put_c(32); rt.put_s(a[0:0]); rt.put_c('|');
+ rt.put_s(a[2:99]); rt.put_c('|'); rt.put_s(a[3:1]); rt.put_c('|'); rt.put_s((a + a)[2:4]); nl();
+ num(count(c, '<')); num(count(shout, 'l')); num(count(b"mississippi", 's')); num(count(e, 'x')); nl();
+ num(b"xyz"[1]); num(len(b"xyz")); num(len(a + a + a)); num(len(tag(e))); nl();
+ // embed: a global of the file's bytes, with or without the type, and a local
+ rt.put_s(text); num(len(text)); num(text[0]); num(text == untyped); num(len(nul)); num(nul[2]); nl();
+ embed "tests/data/embed/text.txt" as bstr here;
+ embed "tests/data/embed/answer.bin" as int answer;
+ embed "tests/data/embed/text.txt" as char* chars;
+ rt.put_s(here + b"|" + untyped); num(len(here)); num(here == text); num(answer); num(chars[6]); nl();
+ return 0;
+}
diff --git a/tests/programs/zc0umd.zc b/tests/programs/zc0umd.zc
new file mode 100644
index 0000000..9693111
--- /dev/null
+++ b/tests/programs/zc0umd.zc
@@ -0,0 +1,58 @@
+namespace;
+include rt;
+
+// u, m and d are ordinary names, and a tag is written .u, .m or .d after a type.
+// u*x, m*x and d*x with no space before the star is a recast, and u * x with a
+// space is a product. the same program runs on the resident compiler and, built
+// into a run-only image, on stage 0 (src/tools/zc0.c), which reads the tags and
+// recasts and does nothing with them.
+
+int u = 2, m = 3; // globals
+int[4] d4;
+int d(int x) { return x + 1; } // a function
+
+int mul3(int u, int m, int d) { return u * m * d; } // parameters
+int mixed(int.d u, int m, int d) { return u * m - d; }
+int tagged(int(int) f, int.m x) { return f(x); }
+
+// owned objects through locals named for their tags
+word*.u box(int v) { word* m = rt.new(2); m[0] = v; m[1] = v * 2; return u*m; }
+word*.u pair(int u, int m) {
+ word* d = rt.new(2);
+ d[0] = u; d[1] = m;
+ return u*d;
+}
+.u single(int v) { let u = rt.new(1); u[0] = v; return u*u; }
+int sum(word*.m m) { return m[0] + m[1]; }
+int eat(word*.d d) { return d[0] + d[1]; }
+int hold(.m u, .d d) { return u[0] * 100 + d[0]; }
+int dig(word*.m u) { int m = u[0], d = u[1]; return m * 10 + d; }
+
+fn main() {
+ int a = 4, b = 5;
+ int.m n = 9;
+ word*.m p = box(7);
+ .m q = single(6);
+ word* r = pair(8, 9);
+ let c = box(10), e = pair(1, 2);
+ rt.put_n(u * m); rt.put_c(32); // two globals multiplied
+ rt.put_n(u*=m); rt.put_c(32); // u *= m is not a recast either
+ rt.put_n(d(u) * d(m)); rt.put_c(32);
+ rt.put_n(mul3(2, 3, 4)); rt.put_c(32);
+ rt.put_n(mixed(7, 6, 5)); rt.put_c(32);
+ rt.put_n(tagged(d, n)); rt.put_c(10);
+ {
+ int u = 6, m = 7, d = 8; // locals shadow the globals
+ rt.put_n(u * m); rt.put_c(32);
+ rt.put_n(u * d - m); rt.put_c(32);
+ rt.put_n(m * u * d); rt.put_c(32);
+ rt.put_n(a * u + m * b); rt.put_c(10);
+ }
+ rt.put_n(sum(p)); rt.put_c(32);
+ rt.put_n(q[0]); rt.put_c(32);
+ rt.put_n(r[0] * r[1]); rt.put_c(32);
+ rt.put_n(dig(c)); rt.put_c(32);
+ rt.put_n(hold(e, single(5))); rt.put_c(32);
+ rt.put_n(eat(d*p)); rt.put_c(10);
+ return 0;
+}