From d274751e89f75cc0f0e25c6e5e87ef91b64597ee Mon Sep 17 00:00:00 2001 From: zinc Date: Wed, 14 Oct 2026 08:30:01 +0000 Subject: zc: untyped local const, c precedence for && and ||, import fixes for every module const name = value; inside a function declares a local constant that takes the initializer's type. the let quick fix now writes a bare const. the self-hosted compiler bound || tighter than &&, unlike zc0 and the readme. no existing source mixed them without parentheses, so only new code was affected. missing-name diagnostics and their quick fixes now search every module: rt, core, run, loaded files, and std, icon and actor scanned from source, including static groups (std.Text.itoa). typos suggest module exports (putc -> rt.put_c) and get a change-to fix. --- tests/drivers/hint_fixes_test.js | 16 ++++++++++++++-- tests/langerrors/const_local_no_init.zc | 8 ++++++++ tests/langerrors/const_local_write.zc | 9 +++++++++ tests/programs/const_local.zc | 11 +++++++++++ tests/programs/precedence.zc | 13 +++++++++++++ tests/test.sh | 2 ++ 6 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 tests/langerrors/const_local_no_init.zc create mode 100644 tests/langerrors/const_local_write.zc create mode 100644 tests/programs/const_local.zc create mode 100644 tests/programs/precedence.zc (limited to 'tests') diff --git a/tests/drivers/hint_fixes_test.js b/tests/drivers/hint_fixes_test.js index 9513a85..fffcbf5 100644 --- a/tests/drivers/hint_fixes_test.js +++ b/tests/drivers/hint_fixes_test.js @@ -48,8 +48,8 @@ fixes('a local that is never read is renamed to _', head + '\tlet unused = rt.ge none('a local written later is not renamed', head + '\tlet later = 1;\n\tlater = 2;\n\treturn 0;\n}\n', 'later', 0); fixes('a typed constant local becomes const', head + '\tint x = 5;\n\trt.put_n(x);\n\treturn 0;\n}\n', 'x = 5', 0, head + '\tconst int x = 5;\n\trt.put_n(x);\n\treturn 0;\n}\n', 'make `x` a const'); -fixes('an untyped constant local becomes const int', head + '\tlet y = 7;\n\trt.put_n(y);\n\treturn 0;\n}\n', 'y = 7', 0, - head + '\tconst int y = 7;\n\trt.put_n(y);\n\treturn 0;\n}\n', 'make `y` a const'); +fixes('an untyped constant local becomes an untyped const', head + '\tlet y = 7;\n\trt.put_n(y);\n\treturn 0;\n}\n', 'y = 7', 0, + head + '\tconst y = 7;\n\trt.put_n(y);\n\treturn 0;\n}\n', 'make `y` a const'); none('one of several declarators is left alone', head + '\tint x = 5, z = rt.get_c();\n\trt.put_n(x + z);\n\treturn 0;\n}\n', 'x = 5', 0); fixes('a power-of-two multiply becomes a parenthesized shift', head + '\tlet y = rt.get_c();\n\tlet q[4];\n\tq[0] = y;\n\trt.put_n(q[0] * 16 + y);\n\treturn 0;\n}\n', '* 16', 0, head + '\tlet y = rt.get_c();\n\tlet q[4];\n\tq[0] = y;\n\trt.put_n((q[0] << 4) + y);\n\treturn 0;\n}\n', 'write << 4'); @@ -62,5 +62,17 @@ fixes('a compiler name is imported from run', 'include rt;\nfn main() {\n\trt.pu 'include rt;\ninclude run;\nfn main() {\n\trt.put_n(run.evals("return 1;"));\n\treturn 0;\n}\n', 'include run and use run.evals'); fixes('an imported module only needs the qualifier', 'include rt;\nfn main() {\n\tput_c(65);\n\treturn 0;\n}\n', 'put_c', 0, 'include rt;\nfn main() {\n\trt.put_c(65);\n\treturn 0;\n}\n', 'use rt.put_c'); +fixes('a library function is found in its static group', 'include rt;\nfn main() {\n\trt.put_s(itoa(5));\n\treturn 0;\n}\n', 'itoa', 0, + 'include rt;\ninclude std;\nfn main() {\n\trt.put_s(std.Text.itoa(5));\n\treturn 0;\n}\n', 'include std and use std.Text.itoa'); +fixes('a group-qualified name gets its module', 'include rt;\nfn main() {\n\trt.put_s(Text.itoa(5));\n\treturn 0;\n}\n', 'Text', 0, + 'include rt;\ninclude std;\nfn main() {\n\trt.put_s(std.Text.itoa(5));\n\treturn 0;\n}\n', 'include std and use std.Text'); +fixes('a top-level library function is imported', 'include rt;\nfn main() {\n\treturn validate(0);\n}\n', 'validate', 0, + 'include rt;\ninclude icon;\nfn main() {\n\treturn icon.validate(0);\n}\n', 'include icon and use icon.validate'); +fixes('a library read from src/lib is imported', 'include rt;\nfn main() {\n\treturn vector();\n}\n', 'vector', 0, + 'include rt;\ninclude actor;\nfn main() {\n\treturn actor.vector();\n}\n', 'include actor and use actor.vector'); +fixes('a misspelled export is corrected and imported', 'fn main() {\n\tputc(65);\n\treturn 0;\n}\n', 'putc', 0, + 'include rt;\nfn main() {\n\trt.put_c(65);\n\treturn 0;\n}\n', 'include rt and use rt.put_c'); +fixes('a misspelled local function is corrected', 'int count_items() { return 1; }\nfn main() {\n\treturn count_item();\n}\n', 'count_item(', 0, + 'int count_items() { return 1; }\nfn main() {\n\treturn count_items();\n}\n', 'change to `count_items`'); console.log(ok + ' hint fix checks passed, ' + bad + ' failed'); process.exit(bad ? 1 : 0); diff --git a/tests/langerrors/const_local_no_init.zc b/tests/langerrors/const_local_no_init.zc new file mode 100644 index 0000000..15832a1 --- /dev/null +++ b/tests/langerrors/const_local_no_init.zc @@ -0,0 +1,8 @@ +namespace; +include rt; + +// expect: a const needs an initializer +fn main() { + const k; + return 0; +} diff --git a/tests/langerrors/const_local_write.zc b/tests/langerrors/const_local_write.zc new file mode 100644 index 0000000..31220be --- /dev/null +++ b/tests/langerrors/const_local_write.zc @@ -0,0 +1,9 @@ +namespace; +include rt; + +// expect: const storage cannot be modified +fn main() { + const k = 5; + k = 6; + rt.put_n(k); +} diff --git a/tests/programs/const_local.zc b/tests/programs/const_local.zc new file mode 100644 index 0000000..83c423c --- /dev/null +++ b/tests/programs/const_local.zc @@ -0,0 +1,11 @@ +// a local const without a type takes its initializer's type and can't be written. +include rt; +fn main() { + const k = 6, j = k * 7; + const half = 2.5; + const name = "zc"; + int typed = j; + rt.put_n(k); rt.put_c(32); rt.put_n(typed); rt.put_c(32); + if (half > 2.0) rt.put_s(name); + rt.put_c(10); +} diff --git a/tests/programs/precedence.zc b/tests/programs/precedence.zc new file mode 100644 index 0000000..18b323d --- /dev/null +++ b/tests/programs/precedence.zc @@ -0,0 +1,13 @@ +// && binds tighter than ||, and both bind below the comparisons and |, as in c. +include rt; +int calls; +int seen(int v) { calls++; return v; } +fn main() { + int t = 1, f = 0; + rt.put_n(f && t || t); rt.put_c(32); // (f && t) || t is 1; f && (t || t) would be 0 + rt.put_n(t || t && f); rt.put_c(32); // t || (t && f) is 1 + rt.put_n(f && f || t && t); rt.put_c(32); + rt.put_n(1 | 0 && 2 == 2); rt.put_c(32); + calls = 0; int r = seen(1) || seen(0) && seen(1); + rt.put_n(r); rt.put_c(32); rt.put_n(calls); rt.put_c(10); // || stops after the first call +} diff --git a/tests/test.sh b/tests/test.sh index 449da87..031d6d9 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -109,6 +109,8 @@ run(){ # $1 = vm command, $2 = label chk "$($vm < tests/programs/typeok.zc)" "14" "$l a program the typechecker accepts" chk "$($vm < tests/programs/typeoff.zc)" "6" "$l the typechecker is off by default" chk "$($vm < tests/programs/enhance.zc)" "100 0 343 3,2 5 -2 0" "$l virtual enhancements" + chk "$($vm < tests/programs/precedence.zc)" "1 1 1 1 1 1" "$l && binds tighter than ||, as in c" + chk "$($vm < tests/programs/const_local.zc)" "6 42 zc" "$l an untyped local const takes its initializer's type" chk "$($vm < tests/programs/joins.zc)" "0 0 5 80 8 3 9 0" "$l joins free what one path gave away; switch, ?:, no prototypes" chk "$($vm < tests/programs/buddy.zc)" "131072 262144 1 0" "$l buddy allocator merges freed blocks" chk "$($vm < tests/programs/growheap.zc)" "144 1 7 1" "$l the heap grows past its initial size and a bigger block fits after the frees" -- cgit v1.2.3