diff options
| author | 2026-10-14 08:29:46 +0330 | |
|---|---|---|
| committer | 2026-10-14 08:29:46 +0330 | |
| commit | 1c53304d03414890af0da7432d1aea87e754a14a (patch) | |
| tree | 97190d86cf548c9bc63f68c86a4d2a4f09809713 /tests/drivers/lsp_test.js | |
| parent | a (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/lsp_test.js')
| -rw-r--r-- | tests/drivers/lsp_test.js | 52 |
1 files changed, 29 insertions, 23 deletions
diff --git a/tests/drivers/lsp_test.js b/tests/drivers/lsp_test.js index a783e1e..ab43c41 100644 --- a/tests/drivers/lsp_test.js +++ b/tests/drivers/lsp_test.js @@ -51,7 +51,7 @@ check(nf && nf.error && nf.error.code === -32601, 'unknown request gets MethodNo check(r.msgs.length === 2, 'an unknown notification is ignored'); // a clean program has no diagnostics -const good = 'fn main() {\n\tput_n(1 + 2);\n\treturn 0;\n}\n'; +const good = 'fn main() {\n\treturn 1 + 2;\n}\n'; r = run([init(1), open(A, good)]); let d = diags(r.msgs, A); check(d.length === 1 && d[0].params.diagnostics.length === 0, 'a clean program publishes an empty list', d); @@ -64,7 +64,7 @@ const e1 = d.length && d[0].params.diagnostics[0]; check(e1 && e1.severity === 1 && e1.range.start.line === 1 && /expected ';', found 'return'/.test(e1.message), 'a missing semicolon is reported on its line', e1); // utf-16 columns: an emoji (two code units) and an accented letter before the error -const uni = '// café 😀\nfn main() { put_s("é😀"); return undefined_name; }\n'; +const uni = '// café 😀\nfn main() { let text = "é😀"; return text[0] + undefined_name; }\n'; r = run([init(1), open(A, uni)]); d = diags(r.msgs, A); const e2 = d.length && d[0].params.diagnostics[0]; @@ -82,7 +82,7 @@ const da = diags(r.msgs, A), db = diags(r.msgs, B2); check(da.length === 2 && JSON.stringify(da[0].params.diagnostics) === JSON.stringify(da[1].params.diagnostics) && db[0].params.diagnostics.length === 0, 'documents are checked in isolation'); // syntax modifications made by the program itself apply to the check, like the compiler -const pipeDef = 'immediate fn pipe_op() { next(); add_binop("|>", 1, "c"); return 0; }\npipe_op\n'; +const pipeDef = 'include run; immediate fn pipe_op() { run.next(); run.add_binop("|>", 1, "c"); return 0; }\npipe_op\n'; const pipeUse = 'fn double(x) { return x * 2; }\nfn main() { return 4 |> double; }\n'; r = run([init(1), open(A, pipeDef + pipeUse), open(B2, pipeUse)]); check(diags(r.msgs, A)[0].params.diagnostics.filter(x => x.severity === 1).length === 0, 'a parse word that adds an operator is understood'); @@ -108,11 +108,11 @@ function tokens(text) { } return out; } -const meth = 'struct point {\n\tlet x, y;\n\tfn sum() { return this.x + this.y; }\n}\nfn main() {\n\tpoint* p = new(point);\n\treturn p.sum();\n}\n'; +const meth = 'struct point {\n\tlet x, y;\n\tfn sum() { return this.x + this.y; }\n}\nfn main() {\n\tpoint p;\n\treturn p.sum();\n}\n'; const tk = tokens(meth); check(tk.filter(t => t === 'sum:function').length === 2, 'a method is a function where it is declared and where it is called', tk); check(tk.includes('this:parameter') && tk.includes('x:property'), 'this is a parameter and a field is a property', tk); -check(tk.filter(t => t === 'point:type').length >= 3, 'a user type is a type, also before a postfix star', tk); +check(tk.filter(t => t === 'point:type').length >= 2, 'a user type is a type, also before a postfix star', tk); // a large document goes through the streaming reader let big = ''; @@ -120,7 +120,8 @@ for (let i = 0; i < 2500; i++) big += 'fn f' + i + '(a, b) { return a * b + ' + big += 'fn main() { return f0(1, 2); }\n'; r = run([init(1), open(A, big)]); d = diags(r.msgs, A); -check(d.length === 1 && d[0].params.diagnostics.filter(x => x.severity === 1).length === 0, 'a document of ' + big.length + ' bytes is checked', d.length); +// a large check streams what it has found, so there may be several lists; the last is the whole answer +check(d.length >= 1 && d[d.length - 1].params.diagnostics.filter(x => x.severity === 1).length === 0, 'a document of ' + big.length + ' bytes is checked', d.length); // local tables grow past their initial reservation, and the next document still checks let many = 'fn main() {\n'; @@ -141,54 +142,58 @@ const hsrc = '// adds two numbers\nint add(int a, int b) { return a + b; }\nint let hv = hover(hsrc, 4, at(hsrc, 4, 'add')); check(hv && hv.includes('int add(int a, int b)') && hv.includes('adds two numbers'), 'hover on a call shows the signature and the doc', hv); check(hv && /^```zc\n/.test(hv) && /\n```\n\nadds two numbers/.test(hv), 'hover separates the code fence from Markdown documentation', hv); -const unicodeHover = 'fn main() { put_s("é😀"); put_c(10); return 0; }\n'; -hv = hover(unicodeHover, 0, at(unicodeHover, 0, 'put_c')); -check(hv && hv.includes('put_c('), 'hover converts UTF-16 columns after BMP and astral characters', hv); +const unicodeHover = 'fn main() { let text = "é😀"; int value = 1; return value; }\n'; +hv = hover(unicodeHover, 0, at(unicodeHover, 0, 'value')); +check(hv && hv.includes('(int) let value'), 'hover converts UTF-16 columns after BMP and astral characters', hv); hv = hover(hsrc, 0, hsrc.split('\n')[0].length + 10); check(hv === null, 'an out-of-range hover column cannot select a name on the following line', hv); hv = hover(hsrc, 6, at(hsrc, 6, 'twice')); check(hv && hv.includes('int twice(int a)') && !hv.includes('adds two numbers'), 'a function with no doc still shows its signature', hv); hv = hover(hsrc, 1, at(hsrc, 1, 'a', 2)); -check(hv && hv.includes('parameter') && hv.includes('int a'), 'hover on a parameter shows its declaration', hv); +check(hv && hv.includes('(int) let a // parameter;'), 'hover on a parameter shows its declaration', hv); hv = hover(hsrc, 5, at(hsrc, 5, 'i')); check(hv && hv.includes('let i') && hv.includes('local'), 'hover on a local shows its declaration', hv); hv = hover(hsrc, 6, at(hsrc, 6, 'i', 1)); check(hv && hv.includes('let i') && hv.includes('local'), 'hover on a use of a local shows its declaration', hv); -const tsrc = 'fn main() {\n\tlet i = 3;\n\tlet p = new(int);\n\treturn i;\n}\n'; +const tsrc = 'fn main() {\n\tlet i = 3;\n\tint* p;\n\treturn i;\n}\n'; // an untyped parameter and an untyped function show the types annotate gives them const isrc = 'fn twice(x) { return x * 2; }\nfn main() {\n\tlet n = twice(4);\n\treturn n;\n}\n'; let hi = hover(isrc, 0, 9); -check(hi && hi.includes('x // parameter; type int'), 'an untyped parameter shows its inferred type', hi); +check(hi && hi.includes('(int) let x // parameter;'), 'an untyped parameter shows its inferred type', hi); hi = hover(isrc, 0, 3); check(hi && hi.includes('int twice(int x)'), 'an untyped function shows its inferred parameter and return types', hi); hv = hover(tsrc, 1, 5); -check(hv && hv.includes('type int'), 'an untyped number local shows the type annotate gives it', hv); -hv = hover(tsrc, 2, 5); -check(hv && hv.includes('type int*'), 'a pointer local shows its pointer type on its declaration line', hv); +check(hv && hv.includes('(int) let i'), 'an untyped number local shows the type annotate gives it', hv); +hv = hover(tsrc, 2, 6); +check(hv && hv.includes('(int*) let p; // local'), 'a pointer local shows its pointer type on its declaration line', hv); hv = hover('fn main() {\n\tlet i = 3;\n\treturn i;\n}\n', 2, 8); check(hv && hv.includes('let i'), 'a return of a local is a use, not a declaration', hv); hv = hover(hsrc, 6, at(hsrc, 6, 'return')); check(hv && hv.includes('leaves the function with a value'), 'a keyword shows what it does', hv); -hv = hover('fn main() {\n\tput_c(32);\n\treturn 0;\n}\n', 1, 1); +const builtinSource = 'include rt; fn main() {\n\trt.put_c(32);\n\treturn 0;\n}\n'; +hv = hover(builtinSource, 1, at(builtinSource, 1, 'put_c')); check(hv && hv.includes('put_c('), 'a built-in function shows its signature', hv); -const kwsrc = 'fn main() {\n\tint* p = new(int);\n\tfree(p);\n\treturn 0;\n}\n'; +const kwsrc = 'include rt; fn main() {\n\tint* p = new(int);\n\trt.free(p);\n\treturn 0;\n}\n'; hv = hover(kwsrc, 1, at(kwsrc, 1, 'new')); -check(hv && hv.includes('new(T)') && hv.includes('fresh object'), 'hover on new shows its form and what it does', hv); +check(hv && hv.includes('new(T)') && hv.includes('fresh heap object') && hv.includes('owns it'), 'hover on new shows its form and ownership behavior', hv); hv = hover(kwsrc, 2, at(kwsrc, 2, 'free')); -check(hv && hv.includes('frees a heap object'), 'hover on free shows its doc', hv); +check(hv && hv.includes('releases a heap object') && hv.includes('consumes an owned object automatically'), 'hover on free shows inferred consumption', hv); const kdoc = '// the number of lines\nfn main() {\n\treturn 0;\n}\n'; hv = hover(kdoc, 2, 1); check(hv === null || !hv.includes('number of lines'), 'a doc comment is not attached to a keyword in a body', hv); // a method of a struct is indented in the body, and its hover is still its line and doc -const msrc = 'struct point {\n\tlet x;\n\t// the sum of the parts\n\tfn sum() { return this.x; }\n\tstatic fn zero() { return 0; }\n}\nfn main() {\n\tpoint* p = new(point);\n\treturn p.sum() + point.zero();\n}\n'; +const msrc = 'struct point {\n\tlet x;\n\t// the sum of the parts\n\tfn sum() { return this.x; }\n\tstatic fn zero() { return 0; }\n}\nfn main() {\n\tpoint p;\n\treturn p.sum() + point.zero();\n}\n'; hv = hover(msrc, 8, at(msrc, 8, 'sum')); check(hv && hv.includes('fn sum()') && hv.includes('the sum of the parts'), 'a method called through a pointer shows its signature and doc', hv); hv = hover(msrc, 8, at(msrc, 8, 'zero')); check(hv && hv.includes('static fn zero()'), 'a static function of a struct shows its signature', hv); +hv = hover(msrc, 4, at(msrc, 4, 'static')); +check(hv && hv.includes('static fn name') && hv.includes('hidden this'), 'static keyword hover explains the declaration form', hv); +check(tokens(msrc).includes('static:keyword'), 'static is highlighted as a keyword', tokens(msrc)); // a typed method shows its return type and parameters -const tmsrc = 'struct point {\n\tlet x;\n\t// the sum of the parts\n\tint sum(int k) { return this.x + k; }\n}\nfn main() {\n\tpoint* p = new(point);\n\treturn p.sum(1);\n}\n'; +const tmsrc = 'struct point {\n\tlet x;\n\t// the sum of the parts\n\tint sum(int k) { return this.x + k; }\n}\nfn main() {\n\tpoint p;\n\treturn p.sum(1);\n}\n'; hv = hover(tmsrc, 7, at(tmsrc, 7, 'sum')); check(hv && hv.includes('int sum(int k)') && hv.includes('the sum of the parts'), 'a typed method shows its return type and parameters', hv); @@ -219,7 +224,8 @@ check(dirty.length === 0, 'every example and test program checks clean (' + clea let missed = [], n = 0; for (const dir of ['errors', 'typeerrors', 'langerrors']) { for (const f of fs.readdirSync('tests/' + dir).filter(x => x.endsWith('.zc'))) { - const want = fs.readFileSync('tests/' + dir + '/' + f, 'utf8').split(/\r?\n/)[0].replace('// expect: ', ''); + const wantLine = fs.readFileSync('tests/' + dir + '/' + f, 'utf8').split(/\r?\n/).find(line => line.startsWith('// expect: ')); + const want = wantLine ? wantLine.slice('// expect: '.length) : ''; const out = cli('tests/' + dir + '/' + f); n++; if (!out.includes(want)) missed.push(dir + '/' + f + ' wanted [' + want + '] got [' + out.split('\n')[0] + ']'); @@ -236,7 +242,7 @@ const sct = tokens(scp); check(sct.includes('cap:parameter') && sct.includes('cap:variable') && !sct.includes('cap:keyword') && !sct.includes('cap:function'), 'a name like a built-in is a variable or parameter where the scope says', sct); check(sct.includes('len:keyword'), 'a built-in that is a keyword in the compiler is still a keyword', sct); let hcv = hover(scp, 4, at(scp, 4, 'cap')); -check(hcv && hcv.includes('int cap') && hcv.includes('local'), 'hover on a use of a local named like a built-in shows the local', hcv); +check(hcv && hcv.includes('(int) let cap') && hcv.includes('local'), 'hover on a use of a local named like a built-in shows the local', hcv); hcv = hover(scp, 1, at(scp, 1, 'cap', 1)); check(hcv && hcv.includes('parameter'), 'hover on a use of a parameter named like a built-in shows the parameter', hcv); hcv = hover(scp, 4, at(scp, 4, 'len')); |
