Files
2026-07-13 12:29:49 +08:00

1846 lines
64 KiB
TypeScript

// The grammar completeness matrix: the subset means "TypeScript minus the
// ecosystem minus the purity violations" — NEVER "minus basic syntax". This
// suite enumerates the ENTIRE TS/ES grammar surface (every statement form,
// every expression/operator family, every declaration form) and pins each
// production to exactly one verdict:
//
// emits SUPPORTED — transpiles clean, and the emitted Zig compiles
// (the zig half runs when a toolchain is on PATH).
// gate BANNED or deferred — stops with EXACTLY the named teaching rule
// (NS9001 marks a genuine roadmap deferral with a tailored
// message, never an accidental hole).
// tsc rejected by the type system / strict module semantics itself —
// tsc's own diagnostic is the teacher (`with`, `export =`, JSX in
// .ts, `this` under noImplicitThis, ...).
// check like `gate`, but asserted through the checker alone because the
// fixture cannot be made tsc-clean (decorators).
//
// COMPLETENESS IS MACHINE-CHECKED: `productions` is the canonical grammar
// enumeration and the matrix must cover it exactly — a production missing
// from the table (or an unknown row) fails the suite, so the grammar can
// never grow a silent gap again.
import test from "node:test";
import assert from "node:assert/strict";
import { execFileSync, spawnSync } from "node:child_process";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { transpile, transpileFiles, checkOnly, ruleIds } from "./helpers.ts";
const pkg = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
const hasZig = spawnSync("zig", ["version"], { stdio: "ignore" }).status === 0;
type Row =
| { readonly verdict: "emits"; readonly src?: string; readonly files?: Record<string, string> }
| { readonly verdict: "gate"; readonly id: string; readonly src?: string; readonly files?: Record<string, string> }
| { readonly verdict: "tsc"; readonly src: string }
| { readonly verdict: "check"; readonly id: string; readonly src: string };
/// The canonical enumeration. Grouped by grammar chapter; names are stable
/// row keys, not TS API names.
const productions: readonly string[] = [
// ---- statements
"stmt/const-declaration",
"stmt/let-declaration",
"stmt/let-uninitialized-typed",
"stmt/var-declaration",
"stmt/object-destructuring-const",
"stmt/array-destructuring",
"stmt/empty",
"stmt/expression",
"stmt/block",
"stmt/if-else",
"stmt/do-while",
"stmt/while",
"stmt/for-classic",
"stmt/for-multi-init-comma-incrementor",
"stmt/for-countdown",
"stmt/for-in",
"stmt/for-of",
"stmt/for-of-entries",
"stmt/for-await-of",
"stmt/continue",
"stmt/continue-labeled",
"stmt/break",
"stmt/break-labeled",
"stmt/labeled-loop",
"stmt/labeled-block",
"stmt/labeled-unreferenced",
"stmt/return",
"stmt/switch-union-kind",
"stmt/switch-union-kind-default",
"stmt/switch-value-default",
"stmt/switch-plain-number",
"stmt/switch-plain-string",
"stmt/throw",
"stmt/throw-heterogeneous",
"stmt/catch-kind-narrowing",
"stmt/throw-error-object",
"stmt/try-catch-finally",
"stmt/try-unsafe-finally",
"stmt/with",
"stmt/debugger",
// ---- declarations & module surface
"decl/function",
"decl/function-nested",
"decl/function-generator",
"decl/function-async",
"decl/function-generic",
"decl/class",
"decl/class-extends",
"decl/class-accessor",
"decl/class-static",
"decl/class-static-mutable",
"decl/class-private-keyword",
"decl/class-private-hash",
"decl/class-generic",
"decl/interface",
"decl/interface-generic",
"decl/type-alias",
"decl/enum",
"decl/namespace",
"decl/decorator",
"decl/module-let",
"decl/parameter-default",
"decl/parameter-rest",
"decl/parameter-destructuring",
"decl/import-named",
"decl/import-type",
"decl/import-namespace",
"decl/import-namespace-sdk",
"decl/import-default",
"decl/import-equals",
"decl/export-named",
"decl/export-default",
"decl/export-assignment",
"decl/export-list",
"decl/export-list-renamed",
"decl/re-export-value",
"decl/re-export-value-renamed",
"decl/re-export-star",
"decl/re-export-type-only",
// ---- expressions: literals & templates
"expr/literal-number",
"expr/literal-bigint",
"expr/literal-string",
"expr/literal-boolean-null",
"expr/literal-regex",
"expr/template-no-substitution",
"expr/template-with-holes",
"expr/template-tagged",
"expr/array-literal-spread",
"expr/object-literal-spread",
"expr/object-literal-shorthand",
"expr/object-literal-computed-key",
"expr/object-literal-accessor",
// ---- expressions: access & calls
"expr/property-access",
"expr/element-access",
"expr/optional-chain-property",
"expr/optional-chain-call",
"expr/optional-chain-element",
"expr/call-function",
"expr/call-recursive",
"expr/call-spread-arguments",
"expr/arguments-object",
"expr/new-uint8array",
"expr/new-class",
"expr/new-map-set",
"expr/new-function",
"expr/new-regexp",
"expr/new-promise",
"expr/new-date",
// ---- expressions: functions as values
"expr/arrow-callback",
"expr/function-reference-callback",
"expr/arrow-stored",
"expr/function-expression",
"expr/class-expression",
"expr/this",
"expr/yield",
"expr/await",
// ---- expressions: operator families
"expr/ternary",
"expr/comma-outside-for",
"expr/void",
"expr/assignment-as-value",
"expr/assignment-as-value-read-elsewhere",
"expr/typeof-value",
"expr/in-operator",
"expr/instanceof",
"expr/delete",
"op/unary-not",
"op/unary-minus",
"op/unary-plus",
"op/unary-bitnot",
"op/increment-statement",
"op/increment-as-value",
"op/increment-as-value-conditional",
"op/add-sub-mul",
"op/div-mod",
"op/exponent",
"op/relational",
"op/strict-equality",
"op/loose-equality",
"op/logical-and-or",
"op/nullish-coalescing",
"op/bitwise-and-or-xor",
"op/shifts",
"op/compound-arithmetic-assign",
"op/compound-bitwise-assign",
"op/compound-logical-assign",
"op/string-concat",
"op/relational-strings",
// ---- erasable type-level syntax
"type/as-assertion",
"type/satisfies",
"type/non-null-assertion",
"type/typeof-type-query",
// ---- runtime surfaces reached through globals
"global/eval",
"global/dynamic-import",
"global/object-statics",
"global/array-statics",
"global/json",
"global/symbol",
"global/bigint-call",
"global/jsx",
];
const matrix: Record<string, Row> = {
// ------------------------------------------------------------- statements
"stmt/const-declaration": {
verdict: "emits",
src: `export function f(): number { const x = 2; return x; }`,
},
"stmt/let-declaration": {
verdict: "emits",
src: `export function f(): number { let x = 2; x = 3; return x; }`,
},
"stmt/let-uninitialized-typed": {
verdict: "emits",
src: `export function f(b: boolean): number { let x: number; if (b) { x = 1; } else { x = 2; } return x; }`,
},
"stmt/var-declaration": {
verdict: "gate",
id: "NS1049",
src: `export function f(): number { var x = 1; x = 2; return x; }`,
},
"stmt/object-destructuring-const": {
verdict: "emits",
src: `
export interface Stats { readonly total: number; readonly done: number; readonly label: Uint8Array; }
export function pct(stats: Stats): number {
const { total, done: doneCount } = stats;
if (total === 0) return 0;
return doneCount * 100 / total;
}`,
},
"stmt/array-destructuring": {
verdict: "gate",
id: "NS1045",
src: `export function f(xs: readonly number[]): number { const [a] = xs; return a; }`,
},
"stmt/empty": {
verdict: "emits",
src: `export function f(): number { ; return 1; }`,
},
"stmt/expression": {
verdict: "emits",
src: `export function f(): number { let x = 0; x += 1; return x; }`,
},
"stmt/block": {
verdict: "emits",
src: `export function f(): number { let x = 0; { x = 2; } return x; }`,
},
"stmt/if-else": {
verdict: "emits",
src: `export function f(b: boolean): number { if (b) { return 1; } else { return 2; } }`,
},
"stmt/do-while": {
verdict: "emits",
src: `
export function count(n: number): number {
let i = 0;
let total = 0;
do { total += 1; i += 1; } while (i < n);
do { if (total % 2 === 0) { total += 3; continue; } total += 1; } while (total < 10);
return total;
}`,
},
"stmt/while": {
verdict: "emits",
src: `export function f(n: number): number { let i = 0; while (i < n) { i += 1; } return i; }`,
},
"stmt/for-classic": {
verdict: "emits",
src: `export function f(xs: Uint8Array): number { let t = 0; for (let i = 0; i < xs.length; i++) { t += xs[i]; } return t; }`,
},
"stmt/for-multi-init-comma-incrementor": {
verdict: "emits",
src: `export function meet(n: number): number { let acc = 0; for (let i = 0, j = n; i < j; i++, j--) { acc += 1; } return acc; }`,
},
"stmt/for-countdown": {
verdict: "emits",
src: `export function down(n: number): number { let acc = 0; for (let i = n; i > 0; i--) { acc += i; } return acc; }`,
},
"stmt/for-in": {
verdict: "gate",
id: "NS1009",
src: `export function f(o: { readonly a: number }): number { let n = 0; for (const k in o) { n += 1; } return n; }`,
},
"stmt/for-of": {
verdict: "emits",
src: `export function f(xs: Uint8Array): number { let t = 0; for (const b of xs) { t += b; } return t; }`,
},
"stmt/for-of-entries": {
// The destructured-tuple loop form ONLY (`[i, x]`, two identifiers);
// general tuple destructuring stays taught (stmt/array-destructuring).
verdict: "emits",
src: `export function f(xs: readonly number[]): number { let t = 0; for (const [i, x] of xs.entries()) { t += i * x; } return t; }`,
},
"stmt/for-await-of": {
verdict: "tsc",
src: `export function f(xs: readonly number[]): number { let t = 0; for await (const x of xs) { t += x; } return t; }`,
},
"stmt/continue": {
verdict: "emits",
src: `export function f(xs: Uint8Array): number { let t = 0; for (const b of xs) { if (b === 0) continue; t += b; } return t; }`,
},
"stmt/continue-labeled": {
verdict: "emits",
src: `
export function f(): number {
let acc = 0;
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (j === 1) continue outer;
acc += 1;
}
}
return acc;
}`,
},
"stmt/break": {
verdict: "emits",
src: `export function f(xs: Uint8Array): number { let t = 0; for (const b of xs) { if (b === 9) break; t += b; } return t; }`,
},
"stmt/break-labeled": {
verdict: "emits",
src: `
export function f(): number {
let acc = 0;
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) break outer;
acc += 1;
}
}
return acc;
}`,
},
"stmt/labeled-loop": {
verdict: "emits",
src: `
export function f(n: number): number {
let i = 0;
scan: while (i < n) {
i += 1;
if (i === 5) break scan;
}
return i;
}`,
},
"stmt/labeled-block": {
verdict: "emits",
src: `
export function f(b: boolean): number {
let acc = 0;
work: { acc += 1; if (b) break work; acc += 10; }
return acc;
}`,
},
"stmt/labeled-unreferenced": {
// JS allows a label nothing jumps to; Zig rejects an unused one, so the
// emitted loop drops it.
verdict: "emits",
src: `
export function f(n: number): number {
let acc = 0;
quietly: for (let i = 0; i < n; i++) { acc += i; }
return acc;
}`,
},
"stmt/return": {
verdict: "emits",
src: `export function f(): number { return 7; }`,
},
"stmt/switch-union-kind": {
verdict: "emits",
src: `
export type Msg = { readonly kind: "a" } | { readonly kind: "b"; readonly v: number };
export function f(msg: Msg): number {
switch (msg.kind) {
case "a": return 0;
case "b": return msg.v;
}
}`,
},
"stmt/switch-union-kind-default": {
verdict: "emits",
src: `
export type Msg = { readonly kind: "a" } | { readonly kind: "b"; readonly v: number } | { readonly kind: "c" };
export function f(msg: Msg): number {
switch (msg.kind) {
case "b": return msg.v;
default: return -1;
}
}
export function g(msg: Msg): number {
// Every kind covered AND a default: the default is dead code in JS.
switch (msg.kind) {
case "a": return 1;
case "b": return 2;
case "c": return 3;
default: return -1;
}
}`,
},
"stmt/switch-value-default": {
verdict: "emits",
src: `
export type Filter = "all" | "active" | "done";
export function f(filter: Filter): number {
switch (filter) {
case "all": return 0;
default: return 1;
}
}`,
},
"stmt/switch-plain-number": {
// Plain number/string scrutinees lower to an if/else chain with JS
// strict-equality and case-order semantics (default matches only after
// every case misses, wherever it sits).
verdict: "emits",
src: `export function f(n: number): number { switch (n) { case 1: return 10; default: return 0; } }`,
},
"stmt/switch-plain-string": {
verdict: "emits",
src: `export function f(s: string): number { switch (s) { case "a": return 1; case "b": return 2; default: return 0; } }`,
},
"stmt/throw": {
// R20: exceptions are deterministic control flow — `throw` carries a
// subset value (the core's ONE error shape, NS1057) to the nearest
// catch; an uncaught throw is a defined panic at the exported
// boundary, exactly where node's process would crash.
verdict: "emits",
src: `export type Failure = { readonly kind: "negative" } | { readonly kind: "overflow" };
export function f(n: number): number {
if (n < 0) throw { kind: "negative" } as Failure;
return n;
}`,
},
"stmt/throw-heterogeneous": {
// R20 heterogeneous throws: distinct kind-tagged shapes merge into the
// core's thrown union; the payload slot is that union.
verdict: "emits",
src: `export interface ParseError { readonly kind: "parse"; readonly at: number; }
export interface IoError { readonly kind: "io"; readonly code: number; }
export function f(n: number): number {
if (n < 0) throw { kind: "parse", at: n } as ParseError;
if (n > 100) throw { kind: "io", code: n } as IoError;
return n;
}`,
},
"stmt/catch-kind-narrowing": {
// `catch (e)` binds the thrown union; kind tests narrow it like any
// discriminated union — no `as` ceremony.
verdict: "emits",
src: `export interface ParseError { readonly kind: "parse"; readonly at: number; }
export interface IoError { readonly kind: "io"; readonly code: number; }
function go(n: number): number {
if (n < 0) throw { kind: "parse", at: n } as ParseError;
if (n > 100) throw { kind: "io", code: n } as IoError;
return n * 2;
}
export function f(n: number): number {
try { return go(n); } catch (e) {
if (e.kind === "parse") return e.at - 1;
if (e.kind === "io") return e.code + 1;
return 0;
}
}`,
},
"stmt/throw-error-object": {
// `Error` objects carry engine stack traces with no native shape;
// thrown values are subset values.
verdict: "gate",
id: "NS1057",
src: `export function f(): number { throw new Error("x"); }`,
},
"stmt/try-catch-finally": {
verdict: "emits",
src: `export function f(): number { try { return 1; } catch { return 0; } finally { } }`,
},
"stmt/try-unsafe-finally": {
// `finally` runs via a scoped defer on every exit; a `return` inside
// it would override the pending return/exception (no-unsafe-finally).
verdict: "gate",
id: "NS1058",
src: `export function f(): number { try { return 1; } finally { return 0; } }`,
},
"stmt/with": {
verdict: "tsc",
src: `export function f(o: { a: number }): number { with (o) { return a; } }`,
},
"stmt/debugger": {
verdict: "gate",
id: "NS1013",
src: `export function f(): number { debugger; return 1; }`,
},
// ------------------------------------------- declarations, module surface
"decl/function": {
verdict: "emits",
src: `function helper(n: number): number { return n + 1; }
export function f(): number { return helper(1); }`,
},
"decl/function-nested": {
verdict: "gate",
id: "NS1046",
src: `export function f(): number { function inner(): number { return 1; } return inner(); }`,
},
"decl/function-generator": {
verdict: "gate",
id: "NS1042",
src: `export function* gen(): Generator<number> { yield 1; }
export function f(): number { return 1; }`,
},
"decl/function-async": {
verdict: "gate",
id: "NS1002",
src: `export async function load(): Promise<number> { return 1; }
export function f(): number { return 1; }`,
},
"decl/function-generic": {
// R15e: user generics monomorphize per call site from tsc's resolved
// type arguments (`first__Task`, `first__f64`), deduped; unresolvable
// call sites teach NS1053.
verdict: "emits",
src: `export function first<T>(xs: readonly T[]): T | null { return xs.length > 0 ? xs[0] : null; }
export function firstOr(xs: readonly number[], d: number): number { return first(xs) ?? d; }`,
},
"decl/class": {
// R19: data classes — annotated fields, one constructor, plain
// methods; `new` constructs a record-shaped value, methods emit as
// module-level fns (mutating ones take the receiver by pointer).
verdict: "emits",
src: `export class Counter {
n: number = 0;
bump(): void { this.n += 1; }
}
export function f(): number { const c = new Counter(); c.bump(); return c.n; }`,
},
"decl/class-extends": {
verdict: "gate",
id: "NS1055",
src: `class Base { n: number = 0; }
export class Derived extends Base { m: number = 1; }
export function f(): number { return 1; }`,
},
"decl/class-accessor": {
verdict: "gate",
id: "NS1056",
src: `export class Config { get doubled(): number { return 4; } }
export function f(): number { return 1; }`,
},
"decl/class-static": {
// R19b: static methods are receiver-less module fns, static readonly
// fields are module consts — both under the class's mangled names.
verdict: "emits",
src: `export class Config {
n: number = 0;
static readonly LIMIT = 4;
static clamp(v: number): number { return v > Config.LIMIT ? Config.LIMIT : v; }
}
export function f(n: number): number { const c = new Config(); return c.n + Config.clamp(n); }`,
},
"decl/class-static-mutable": {
// A mutable static is module state by another spelling.
verdict: "gate",
id: "NS1010",
src: `export class Config { static shared: number = 2; }
export function f(): number { return 1; }`,
},
"decl/class-private-keyword": {
// `private`/`protected` keywords are erased: tsc enforces them at the
// type level, which is their whole meaning.
verdict: "emits",
src: `export class Counter {
private n: number = 0;
bump(): void { this.n += 1; }
value(): number { return this.n; }
}
export function f(): number { const c = new Counter(); c.bump(); return c.value(); }`,
},
"decl/class-private-hash": {
verdict: "gate",
id: "NS1056",
src: `export class Config { #secret: number = 1; }
export function f(): number { return 1; }`,
},
"decl/class-generic": {
verdict: "gate",
id: "NS1053",
src: `export class Box<T> { v: T | null = null; }
export function f(): number { return 1; }`,
},
"decl/interface": {
verdict: "emits",
src: `export interface P { readonly x: number; }
export function f(p: P): number { return p.x; }`,
},
"decl/interface-generic": {
// Generic interfaces/aliases are templates: instantiations resolve
// structurally through the type table (`Box<Task>` -> `Box__Task`).
verdict: "emits",
src: `export interface Box<T> { readonly v: T; }
export interface Task { readonly id: number; }
export function f(b: Box<Task>): number { return b.v.id; }`,
},
"decl/type-alias": {
verdict: "emits",
src: `export type Filter = "all" | "done";
export function f(x: Filter): boolean { return x === "all"; }`,
},
"decl/enum": {
verdict: "gate",
id: "NS1008",
src: `export enum Color { Red, Green }
export function f(): number { return 1; }`,
},
"decl/namespace": {
verdict: "gate",
id: "NS1008",
src: `export namespace Util { export const N = 1; }
export function f(): number { return 1; }`,
},
"decl/decorator": {
// Standard decorators cannot be spelled tsc-clean without a class (which
// gates on its own), so the pin runs through the checker alone.
verdict: "check",
id: "NS1008",
src: `function dec(value: unknown, context: unknown): void {}
@dec class C {}
export function f(): number { return 1; }`,
},
"decl/module-let": {
verdict: "gate",
id: "NS1010",
src: `let counter = 0;
export function f(): number { return counter; }`,
},
"decl/parameter-default": {
verdict: "gate",
id: "NS1019",
src: `export function f(n: number = 3): number { return n; }`,
},
"decl/parameter-rest": {
verdict: "gate",
id: "NS1019",
src: `export function f(...xs: readonly number[]): number { return xs.length; }`,
},
"decl/parameter-destructuring": {
verdict: "gate",
id: "NS1045",
src: `export function f({ a }: { readonly a: number }): number { return a; }`,
},
"decl/import-named": {
verdict: "emits",
files: {
"core.ts": `import { bump } from "./util.ts";
export function f(n: number): number { return bump(n); }`,
"util.ts": `export function bump(n: number): number { return n + 1; }`,
},
},
"decl/import-type": {
verdict: "emits",
files: {
"core.ts": `import type { Cfg } from "./util.ts";
export function f(c: Cfg): number { return c.n; }`,
"util.ts": `export interface Cfg { readonly n: number; }`,
},
},
"decl/import-namespace": {
verdict: "emits",
files: {
"core.ts": `import * as util from "./util.ts";
export function f(n: number): number { const c: util.Cfg = { step: util.SEED }; return util.bump(n) + c.step; }`,
"util.ts": `export interface Cfg { readonly step: number; }
export const SEED = 5;
export function bump(n: number): number { return n + 1; }`,
},
},
"decl/import-namespace-sdk": {
verdict: "gate",
id: "NS1039",
src: `import * as sdk from "@native-sdk/core";
export function f(): number { return 1; }`,
},
"decl/import-default": {
verdict: "gate",
id: "NS1047",
files: {
"core.ts": `import bump from "./util.ts";
export function f(n: number): number { return bump(n); }`,
"util.ts": `export default function bump(n: number): number { return n + 1; }`,
},
},
"decl/import-equals": {
verdict: "tsc",
src: `import util = require("./util.ts");
export function f(): number { return 1; }`,
},
"decl/export-named": {
verdict: "emits",
src: `export const LIMIT = 9;
export function f(): number { return LIMIT; }`,
},
"decl/export-default": {
verdict: "gate",
id: "NS1047",
src: `export default function f(): number { return 1; }`,
},
"decl/export-assignment": {
verdict: "tsc",
src: `function f(): number { return 1; }
export = f;`,
},
"decl/export-list": {
// Export lists bind names over existing declarations in the flat
// namespace — the un-renamed entry exports the declaration itself.
verdict: "emits",
src: `function f(): number { return 1; }
export { f };
export function g(): number { return f(); }`,
},
"decl/export-list-renamed": {
// `export { a as b }` binds the NEW name over the declaration (a
// `pub const b = a;` alias in the emitted module).
verdict: "emits",
src: `function f(): number { return 1; }
export { f as bump };
export function g(): number { return f(); }`,
},
"decl/re-export-value": {
verdict: "emits",
files: {
"core.ts": `export { bump } from "./util.ts";
export function g(n: number): number { return n + 1; }`,
"util.ts": `export function bump(n: number): number { return n + 1; }`,
},
},
"decl/re-export-value-renamed": {
verdict: "emits",
files: {
"core.ts": `export { bump as inc } from "./util.ts";
export function g(n: number): number { return n + 1; }`,
"util.ts": `export function bump(n: number): number { return n + 1; }`,
},
},
"decl/re-export-star": {
verdict: "gate",
id: "NS1047",
files: {
"core.ts": `export * from "./util.ts";
export function f(): number { return 1; }`,
"util.ts": `export function bump(n: number): number { return n + 1; }`,
},
},
"decl/re-export-type-only": {
verdict: "emits",
files: {
"core.ts": `import type { Cfg } from "./api.ts";
export function f(c: Cfg): number { return c.n; }`,
"api.ts": `export type { Cfg } from "./impl.ts";`,
"impl.ts": `export interface Cfg { readonly n: number; }`,
},
},
// ------------------------------------------------------ literal & template
"expr/literal-number": {
verdict: "emits",
src: `export function f(): number { return 0x10 + 0b101 + 0o17 + 1_000 + 1.5e2; }`,
},
"expr/literal-bigint": {
verdict: "gate",
id: "NS1044",
src: `export function f(): number { const b = 10n; return b === 10n ? 1 : 0; }`,
},
"expr/literal-string": {
verdict: "emits",
src: `export function f(tag: string): boolean { return tag === "app.add"; }`,
},
"expr/literal-boolean-null": {
verdict: "emits",
src: `export function f(x: number | null): boolean { return x === null ? true : false; }`,
},
"expr/literal-regex": {
verdict: "gate",
id: "NS1040",
src: `export function f(): number { const re = /a+/; return re === re ? 1 : 0; }`,
},
"expr/template-no-substitution": {
verdict: "emits",
src: `export function f(): boolean { const s = \`abc\`; return s === "abc"; }`,
},
"expr/template-with-holes": {
verdict: "emits",
src: `import { asciiBytes } from "@native-sdk/core";
export function f(xs: Uint8Array): Uint8Array { return asciiBytes(\`\${xs.length} items\`); }`,
},
"expr/template-tagged": {
verdict: "gate",
id: "NS1018",
src: `export function f(): number { const s = String.raw\`abc\`; return s === "abc" ? 1 : 0; }`,
},
"expr/array-literal-spread": {
verdict: "emits",
src: `export function f(xs: readonly number[], x: number): readonly number[] { return [...xs, x]; }`,
},
"expr/object-literal-spread": {
verdict: "emits",
src: `
export interface Model { readonly a: number; readonly b: number; }
export function f(m: Model): Model { return { ...m, a: m.a + 1 }; }`,
},
"expr/object-literal-shorthand": {
verdict: "emits",
src: `
export interface P { readonly x: number; }
export function f(x: number): P { return { x }; }`,
},
"expr/object-literal-computed-key": {
verdict: "gate",
id: "NS1012",
src: `export function f(k: string): number { const o = { [k]: 1 } as unknown as { a: number }; return o.a; }`,
},
"expr/object-literal-accessor": {
verdict: "gate",
id: "NS1012",
src: `export function f(): number { const o = { get a(): number { return 1; } }; return o.a; }`,
},
// ------------------------------------------------------------ access, calls
"expr/property-access": {
verdict: "emits",
src: `export interface P { readonly x: number; }
export function f(p: P): number { return p.x; }`,
},
"expr/element-access": {
verdict: "emits",
src: `export function f(xs: Uint8Array, i: number): number { return xs[i]; }`,
},
"expr/optional-chain-property": {
verdict: "emits",
src: `
export interface Sel { readonly at: number; }
export function f(sel: Sel | null): number { return sel?.at ?? -1; }`,
},
"expr/optional-chain-call": {
verdict: "gate",
id: "NS1046",
src: `export function f(g: (() => number) | null): number { return g?.() ?? 0; }`,
},
"expr/optional-chain-element": {
// Element hops null-propagate like property hops (R7b); the chain value
// is optional and `??` consumes it.
verdict: "emits",
src: `
export interface M { readonly xs: Uint8Array; }
export function f(m: M | null): number { return m?.xs[0] ?? 0; }`,
},
"expr/call-function": {
verdict: "emits",
src: `function add(a: number, b: number): number { return a + b; }
export function f(): number { return add(1, 2); }`,
},
"expr/call-recursive": {
verdict: "emits",
src: `export function fib(n: number): number { if (n < 2) return n; return fib(n - 1) + fib(n - 2); }`,
},
"expr/call-spread-arguments": {
verdict: "gate",
id: "NS1019",
src: `function add(a: number, b: number): number { return a + b; }
export function f(xs: readonly [number, number]): number { return add(...xs); }`,
},
"expr/arguments-object": {
verdict: "gate",
id: "NS1019",
src: `export function f(): number { return (arguments as unknown as { length: number }).length; }`,
},
"expr/new-uint8array": {
verdict: "emits",
src: `export function f(n: number): Uint8Array { const b = new Uint8Array(4); return b; }`,
},
"expr/new-class": {
// `new` of an AMBIENT class (a user data class emits — decl/class);
// `new Error` has its own teach (NS1057, stmt/throw-error-object).
verdict: "gate",
id: "NS1006",
src: `export function f(): number { const e = new WeakRef({ n: 1 }); return e === e ? 1 : 0; }`,
},
"expr/new-map-set": {
verdict: "gate",
id: "NS1011",
src: `export function f(): number { const m = new Map<number, number>(); return m === m ? 1 : 0; }`,
},
"expr/new-function": {
verdict: "gate",
id: "NS1013",
src: `export function f(): number { const g = new Function("return 1"); return g === g ? 1 : 0; }`,
},
"expr/new-regexp": {
verdict: "gate",
id: "NS1040",
src: `export function f(): number { const re = new RegExp("a+"); return re === re ? 1 : 0; }`,
},
"expr/new-promise": {
verdict: "gate",
id: "NS1002",
src: `export function f(): number { const p = new Promise<number>((resolve) => resolve(1)); return p === p ? 1 : 0; }`,
},
"expr/new-date": {
verdict: "gate",
id: "NS1005",
src: `export function f(): number { const d = new Date(); return d === d ? 1 : 0; }`,
},
// -------------------------------------------------------- function values
"expr/arrow-callback": {
verdict: "emits",
src: `export function f(xs: readonly number[]): readonly number[] { return xs.map((x) => x * 2); }`,
},
"expr/function-reference-callback": {
// A bare reference to a module-level fn (or a hoisted local helper) in
// callback position inlines exactly like the arrow form spelled at the
// site: `xs.map(double)` IS `xs.map((n) => double's body)`.
verdict: "emits",
src: `function double(n: number): number { return n * 2; }
export function f(xs: readonly number[]): readonly number[] { return xs.map(double); }`,
},
"expr/arrow-stored": {
// R15d: a const-bound, capture-free, fully-annotated local helper
// hoists to a module-level fn; captures/reassignment/other uses teach
// (NS1054/NS1046).
verdict: "emits",
src: `export function f(): number { const g = (x: number): number => x + 1; return g(1); }`,
},
"expr/function-expression": {
verdict: "emits",
src: `export function f(): number { const g = function (x: number): number { return x + 1; }; return g(1); }`,
},
"expr/class-expression": {
verdict: "gate",
id: "NS1006",
src: `export function f(): number { const C = class {}; return C === C ? 1 : 0; }`,
},
"expr/this": {
verdict: "tsc",
src: `export function f(): number { return (this as unknown as { n: number }).n; }`,
},
"expr/yield": {
verdict: "gate",
id: "NS1042",
src: `export function* gen(): Generator<number> { yield 1; yield 2; }
export function f(): number { return 1; }`,
},
"expr/await": {
verdict: "gate",
id: "NS1002",
src: `const x = await Promise.resolve(1);
export function f(): number { return 1; }`,
},
// ------------------------------------------------------ operator families
"expr/ternary": {
verdict: "emits",
src: `export function f(b: boolean): number { return b ? 1 : 2; }`,
},
"expr/comma-outside-for": {
verdict: "gate",
id: "NS1043",
src: `export function f(): number { let a = 0; const x = (a++, a); return x; }`,
},
"expr/void": {
verdict: "gate",
id: "NS1043",
src: `export function f(): number { void 0; return 1; }`,
},
"expr/assignment-as-value": {
// Legal when the split statement is order-exact (sole mention,
// unskippable position); the taught remainder keeps NS1043.
verdict: "emits",
src: `export function f(): number { let y = 0; const z = (y = 5); return z * 10 + y; }`,
},
"expr/assignment-as-value-read-elsewhere": {
verdict: "gate",
id: "NS1043",
src: `export function f(): number { let y = 0; const z = (y = 5) + y; return z; }`,
},
"expr/typeof-value": {
verdict: "gate",
id: "NS1041",
src: `export function f(x: number): number { const t = typeof x; return t === "number" ? 1 : 0; }`,
},
"expr/in-operator": {
verdict: "gate",
id: "NS1041",
src: `export function f(o: { readonly a: number }): boolean { return "a" in o; }`,
},
"expr/instanceof": {
verdict: "gate",
id: "NS1041",
src: `export function f(x: unknown): boolean { return x instanceof Uint8Array; }`,
},
"expr/delete": {
verdict: "gate",
id: "NS1012",
src: `export function f(o: { a?: number }): number { delete o.a; return 1; }`,
},
"op/unary-not": {
verdict: "emits",
src: `export function f(b: boolean): boolean { return !b; }`,
},
"op/unary-minus": {
verdict: "emits",
src: `export function f(n: number): number { return -n; }`,
},
"op/unary-plus": {
verdict: "emits",
src: `export function f(n: number): number { return +n + 1; }`,
},
"op/unary-bitnot": {
verdict: "emits",
src: `export function f(n: number): number { let i = 0; i = n | 0; return ~i; }`,
},
"op/increment-statement": {
verdict: "emits",
src: `export function f(): number { let x = 0; x++; ++x; x--; --x; return x; }`,
},
"op/increment-as-value": {
// `arr[i++]` / `const n = ++count`: the step lowers to its own
// statement (JS-order-exact by the sole-mention + unskippable rule).
verdict: "emits",
src: `export function f(): number { let x = 1; const y = ++x; return y; }`,
},
"op/increment-as-value-conditional": {
verdict: "gate",
id: "NS1043",
src: `export function f(b: boolean): number { let x = 1; const y = b ? x++ : 0; return y; }`,
},
"op/add-sub-mul": {
verdict: "emits",
src: `export function f(a: number, b: number): number { return a + b - a * b; }`,
},
"op/div-mod": {
verdict: "emits",
src: `export function f(a: number, b: number): number { return a / b + a % b; }`,
},
"op/exponent": {
verdict: "emits",
src: `export function f(a: number, b: number): number { let v = a ** b; v **= 2; return v; }`,
},
"op/relational": {
verdict: "emits",
src: `export function f(a: number, b: number): boolean { return a < b && a <= b && a > b === false && a >= b === false; }`,
},
"op/strict-equality": {
verdict: "emits",
src: `export function f(a: number, b: number): boolean { return a === b || a !== b; }`,
},
"op/loose-equality": {
verdict: "gate",
id: "NS1048",
src: `export function f(a: number, b: number): boolean { return a == b; }`,
},
"op/logical-and-or": {
verdict: "emits",
src: `export function f(a: boolean, b: boolean): boolean { return (a && b) || (!a && !b); }`,
},
"op/nullish-coalescing": {
verdict: "emits",
src: `export function f(x: number | null): number { return x ?? 7; }`,
},
"op/bitwise-and-or-xor": {
verdict: "emits",
src: `export function f(a: number, b: number): number { let x = 0; let y = 0; x = a | 0; y = b | 0; return (x & y) + (x | y) + (x ^ y); }`,
},
"op/shifts": {
verdict: "emits",
src: `export function f(a: number, b: number): number { let x = 0; let n = 0; x = a | 0; n = b | 0; return (x << n) + (x >> n) + (x >>> n); }`,
},
"op/compound-arithmetic-assign": {
verdict: "emits",
src: `export function f(a: number): number { let v = a; v += 1; v -= 2; v *= 3; v /= 2; v %= 7; v **= 2; return v; }`,
},
"op/compound-bitwise-assign": {
verdict: "emits",
src: `export function f(a: number): number { let v = 0; v = a | 0; v &= 255; v |= 1; v ^= 3; v <<= 2; v >>= 1; v >>>= 1; return v; }`,
},
"op/compound-logical-assign": {
verdict: "emits",
src: `
export function f(a: boolean, b: boolean, n: number | null): number {
let x = a;
x &&= b;
x ||= a;
let v = n;
v ??= 9;
if (x) return v ?? -1;
return 0;
}`,
},
"op/string-concat": {
verdict: "gate",
id: "NS1018",
src: `export function f(a: string, b: string): boolean { return (a + b) === "ab"; }`,
},
"op/relational-strings": {
verdict: "gate",
id: "NS1004",
src: `export function f(a: string, b: string): boolean { return a < b; }`,
},
// --------------------------------------------------- erasable type syntax
"type/as-assertion": {
verdict: "emits",
src: `export type Filter = "all" | "done";
export function f(): boolean { const x = "all" as Filter; return x === "all"; }`,
},
"type/satisfies": {
verdict: "emits",
src: `
export interface P { readonly x: number; }
const TABLE: P = { x: 1 } satisfies P;
export function f(): number { return TABLE.x; }`,
},
"type/non-null-assertion": {
verdict: "emits",
src: `export function f(x: number | null): number { if (x === null) return 0; return x!; }`,
},
"type/typeof-type-query": {
// `typeof CONST` resolves through the checker's own type query and
// widens to the value's slot type (type-level only; erases under node).
verdict: "emits",
src: `const LIMIT = 9;
export type Limit = typeof LIMIT;
export function f(l: Limit): number { return l; }`,
},
// ----------------------------------------------------------- global gates
"global/eval": {
verdict: "gate",
id: "NS1013",
src: `export function f(): number { eval("1"); return 1; }`,
},
"global/dynamic-import": {
verdict: "tsc",
src: `export function f(): number { return 1; }
import("./other.ts");`,
},
"global/object-statics": {
verdict: "gate",
id: "NS1041",
src: `export function f(o: { readonly a: number }): number { return Object.keys(o).length; }`,
},
"global/array-statics": {
// NS1059: the Array constructors get their own accurate teaching — they
// BUILD arrays through runtime protocols; the literal/spread/loop forms
// build the same arrays statically (Array.isArray stays NS1041, the
// runtime type test it actually is).
verdict: "gate",
id: "NS1059",
src: `export function f(xs: readonly number[]): number { const c = Array.from(xs); return c.length; }`,
},
"global/json": {
verdict: "gate",
id: "NS1041",
src: `export function f(): number { const s = JSON.stringify(1); return s === "1" ? 1 : 0; }`,
},
"global/symbol": {
verdict: "gate",
id: "NS1044",
src: `export function f(): number { const s = Symbol("x"); return s === s ? 1 : 0; }`,
},
"global/bigint-call": {
verdict: "gate",
id: "NS1044",
src: `export function f(): number { const b = BigInt(1); return b === b ? 1 : 0; }`,
},
"global/jsx": {
verdict: "tsc",
src: `export const x = <div/>;`,
},
};
// ---------------------------------------------------------------------------
// The mutation matrix: the grammar productions above classify SYNTAX; this
// section machine-pins the library-surface classification of every mutating
// array method under the ownership rule (locally-owned arrays mutate freely;
// teaching errors fire only at the semantic boundaries). One row per method x
// ownership verdict, so the classification can never grow a silent gap.
const mutationSurface: readonly string[] = [
// ---- every supported method on a locally-owned array
"owned/push",
"owned/pop",
"owned/shift",
"owned/unshift",
"owned/splice",
"owned/reverse",
"owned/fill",
"owned/sort",
"owned/indexed-write",
"owned/append-write",
"owned/reassigned-all-owning",
"owned/borrowed-readonly-pass",
// ---- the semantic boundaries
"boundary/parameter",
"boundary/model-field",
"boundary/module-const",
"boundary/alias",
"boundary/reassigned-mixed",
"boundary/escape-call",
"boundary/escape-store",
"boundary/borrow-alias-out",
"boundary/sort-shared",
"boundary/indexed-write-parameter",
"boundary/append-write-parameter",
// ---- shape stops (owned, but the form has no mapping)
"shape/copyWithin",
"shape/sort-value-position",
"shape/push-value-position",
"shape/append-write-compound",
"shape/iterating-length-change",
];
const mutationMatrix: Record<string, Row> = {
"owned/push": {
verdict: "emits",
src: `export function f(n: number): readonly number[] { const out: number[] = []; out.push(n); return out; }`,
},
"owned/pop": {
verdict: "emits",
src: `export function f(xs: readonly number[]): number { const w = xs.slice(); return w.pop() ?? -1; }`,
},
"owned/shift": {
verdict: "emits",
src: `export function f(xs: readonly number[]): number { const w = xs.slice(); return w.shift() ?? -1; }`,
},
"owned/unshift": {
verdict: "emits",
src: `export function f(xs: readonly number[]): readonly number[] { const w = xs.slice(); w.unshift(0); return w; }`,
},
"owned/splice": {
verdict: "emits",
src: `export function f(xs: readonly number[]): readonly number[] { const w = xs.slice(); const cut = w.splice(1, 2, 9); return cut.concat(w); }`,
},
"owned/reverse": {
verdict: "emits",
src: `export function f(xs: readonly number[]): readonly number[] { const w = xs.slice(); w.reverse(); return w; }`,
},
"owned/fill": {
verdict: "emits",
src: `export function f(xs: readonly number[]): readonly number[] { const w = xs.slice(); w.fill(0, 1, -1); return w; }`,
},
"owned/sort": {
verdict: "emits",
src: `export function f(xs: readonly number[]): readonly number[] { const w = xs.slice(); w.sort((a, b) => a - b); return w; }`,
},
"owned/indexed-write": {
verdict: "emits",
src: `export function f(xs: readonly number[]): readonly number[] { const w = xs.slice(); if (w.length > 0) w[0] = 9; return w; }`,
},
"boundary/parameter": {
verdict: "gate",
id: "NS1001",
src: `export function f(xs: number[], x: number): number[] { xs.push(x); return xs; }`,
},
"boundary/model-field": {
verdict: "gate",
id: "NS1001",
src: `
export interface Model { readonly xs: readonly number[]; }
export function f(model: Model): readonly number[] { (model.xs as number[]).push(1); return model.xs; }`,
},
"boundary/module-const": {
verdict: "gate",
id: "NS1001",
src: `const TABLE: number[] = [1];
export function f(): number { TABLE.pop(); return TABLE.length; }`,
},
"boundary/alias": {
verdict: "gate",
id: "NS1001",
src: `export function f(): number { const a: number[] = [1]; const b = a; b.push(2); return a.length; }`,
},
"boundary/reassigned-mixed": {
// ONE assignment that is not a fresh construction ends the binding's
// ownership everywhere (the flow-sensitive rule needs every assignment
// owning).
verdict: "gate",
id: "NS1001",
src: `
export function f(xs: number[]): readonly number[] {
let w = xs.slice();
w = xs;
w.pop();
return w;
}`,
},
"boundary/escape-call": {
// A MUTABLE parameter could write or retain the array — passing there
// still ends ownership (`readonly T[]` reader params borrow instead:
// owned/borrowed-readonly-pass).
verdict: "gate",
id: "NS1051",
src: `
function probe(xs: number[]): number { return xs.length; }
export function f(): number { const out: number[] = [1]; const t = probe(out); out.push(2); return t; }`,
},
"boundary/borrow-alias-out": {
// A readonly parameter the callee RETURNS aliases the array out — the
// borrow only holds when the callee provably cannot alias it onward.
verdict: "gate",
id: "NS1051",
src: `
function pass(xs: readonly number[]): readonly number[] { return xs; }
export function f(): number { const out: number[] = [1]; const kept = pass(out); out.push(2); return kept.length; }`,
},
"boundary/escape-store": {
verdict: "gate",
id: "NS1051",
src: `
export interface Pair { readonly xs: readonly number[]; }
export function f(): Pair { const out: number[] = [1]; const p: Pair = { xs: out }; out.pop(); return p; }`,
},
"boundary/sort-shared": {
verdict: "gate",
id: "NS1022",
src: `export function f(xs: number[]): number[] { xs.sort((a, b) => a - b); return xs; }`,
},
"boundary/indexed-write-parameter": {
verdict: "gate",
id: "NS1001",
src: `export function f(xs: number[]): number { xs[0] = 1; return xs[0]; }`,
},
"shape/copyWithin": {
verdict: "gate",
id: "NS1001",
src: `export function f(xs: readonly number[]): readonly number[] { const w = xs.slice(); w.copyWithin(0, 1); return w; }`,
},
"shape/sort-value-position": {
verdict: "gate",
id: "NS9001",
src: `export function f(xs: readonly number[]): readonly number[] { const w = xs.slice(); return w.sort((a, b) => a - b); }`,
},
"shape/push-value-position": {
verdict: "gate",
id: "NS9001",
src: `export function f(n: number): number { const out: number[] = []; const len = out.push(n); return len; }`,
},
"owned/append-write": {
// `xs[xs.length] = v` is the one growth shape: exactly a push.
verdict: "emits",
src: `export function f(): readonly number[] { const out = [1]; out[out.length] = 2; out[out.length] = 3; return out; }`,
},
"owned/reassigned-all-owning": {
// Flow-sensitive ownership: every assignment installs a fresh owning
// construction, so the binding can never hold a shared array.
verdict: "emits",
src: `
export function f(xs: readonly number[]): readonly number[] {
let w = xs.slice();
w.push(1);
w = xs.filter((x) => x > 0);
w.push(2);
return w;
}`,
},
"owned/borrowed-readonly-pass": {
// Passing to a readonly READER parameter borrows; ownership survives.
verdict: "emits",
src: `
function total(xs: readonly number[]): number { let t = 0; for (const x of xs) { t += x; } return t; }
export function f(n: number): number {
const out: number[] = [n];
const before = total(out);
out.push(n + 1);
return before + total(out);
}`,
},
"boundary/append-write-parameter": {
verdict: "gate",
id: "NS1001",
src: `export function f(xs: number[]): number { xs[xs.length] = 1; return xs.length; }`,
},
"shape/append-write-compound": {
// The compound forms read the missing slot first (JS undefined).
verdict: "gate",
id: "NS9001",
src: `export function f(): readonly number[] { const out = [1]; out[out.length] += 2; return out; }`,
},
"shape/iterating-length-change": {
verdict: "gate",
id: "NS9001",
src: `
export function f(): number {
const out: number[] = [1, 2];
let t = 0;
for (const x of out) { t += x; out.pop(); }
return t;
}`,
},
};
test("mutation matrix: the enumeration and the table cover each other exactly", () => {
const enumerated = new Set(mutationSurface);
const rows = new Set(Object.keys(mutationMatrix));
const missing = [...enumerated].filter((p) => !rows.has(p));
const unknown = [...rows].filter((p) => !enumerated.has(p));
assert.deepEqual(missing, [], `mutation rows without a matrix entry (classify them!): ${missing.join(", ")}`);
assert.deepEqual(unknown, [], `mutation matrix rows outside the enumeration: ${unknown.join(", ")}`);
assert.equal(mutationSurface.length, new Set(mutationSurface).size, "duplicate mutation row names");
});
test("mutation matrix: every row produces exactly its classified outcome", () => {
for (const name of mutationSurface) {
const row = mutationMatrix[name];
assert.notEqual(row.verdict, "check");
const result = row.verdict !== "tsc" && row.files ? transpileFiles(row.files) : transpile((row as { src: string }).src);
assert.equal(
result.typeErrors.length,
0,
`${name}: fixture must be tsc-clean\n${result.typeErrors.join("\n")}`,
);
if (row.verdict === "gate") {
assert.equal(result.ok, false, `${name}: expected ${row.id}, but it transpiled`);
const ids = result.diagnostics.map((d) => d.id);
assert.ok(ids.includes(row.id), `${name}: expected ${row.id}, got ${ids.join(", ") || "none"}`);
const d = result.diagnostics.find((x) => x.id === row.id)!;
assert.ok(d.message.length > d.title.length + 20, `${name}: ${row.id} message reads as a bare fallback`);
} else {
const details = result.diagnostics.map((d) => `${d.id} ${d.message}`).join("\n");
assert.equal(result.ok, true, `${name}: expected clean transpile\n${details}`);
}
}
});
test("mutation matrix: every SUPPORTED row's Zig compiles", { skip: !hasZig, timeout: 300_000 }, () => {
const work = fs.mkdtempSync(path.join(os.tmpdir(), "native-core-mutation-"));
try {
fs.copyFileSync(path.join(pkg, "rt", "rt.zig"), path.join(work, "rt.zig"));
const imports: string[] = [];
let i = 0;
for (const name of mutationSurface) {
const row = mutationMatrix[name];
if (row.verdict !== "emits") continue;
const result = transpile(row.src!);
assert.equal(result.ok, true, `${name}: transpile failed before the zig step`);
const file = `m_${String(i++).padStart(3, "0")}.zig`;
fs.writeFileSync(path.join(work, file), result.zig!);
imports.push(` // ${name}\n refAllDecls(@import("${file}"));`);
}
const driver = [
`const refAllDecls = @import("std").testing.refAllDecls;`,
``,
`test {`,
...imports,
`}`,
``,
].join("\n");
fs.writeFileSync(path.join(work, "driver.zig"), driver);
try {
execFileSync("zig", ["test", "driver.zig"], { cwd: work, encoding: "utf8", stdio: "pipe" });
} catch (e) {
const err = e as { stderr?: string; stdout?: string };
assert.fail(`emitted Zig failed to compile:\n${err.stderr ?? ""}${err.stdout ?? ""}`);
}
} finally {
fs.rmSync(work, { recursive: true, force: true });
}
});
// ---------------------------------------------------------------------------
// The byte-text method matrix: the library-surface classification of the
// everyday string methods on core bytes (`Uint8Array`). One row per method x
// verdict — the supported set must emit, and every STAYS-OUT spelling must
// reach exactly its named teaching (never a bare tsc "property does not
// exist") — so this surface can never grow a silent gap either.
const textSurface: readonly string[] = [
// ---- the supported byte-honest surface
"text/toUpperCase",
"text/toLowerCase",
"text/repeat",
"text/startsWith",
"text/endsWith",
"text/includes-bytes",
"text/includes-number",
"text/indexOf-bytes",
"text/indexOf-number",
"text/lastIndexOf-bytes",
"text/lastIndexOf-number",
"text/padStart",
"text/padEnd",
"text/trim",
"text/trimStart",
"text/trimEnd",
"text/split",
"text/at",
// ---- taught edges of the supported set
"text-edge/repeat-negative-literal",
"text-edge/repeat-fractional-literal",
"text-edge/split-empty-literal-separator",
"text-edge/includes-fromIndex",
// ---- the stays-out set, each with its named reason
"text-out/charCodeAt",
"text-out/charAt",
"text-out/codePointAt",
"text-out/normalize",
"text-out/replace",
"text-out/replaceAll",
"text-out/localeCompare",
"text-out/toLocaleUpperCase",
"text-out/toLocaleLowerCase",
"text-out/match",
"text-out/matchAll",
"text-out/search",
];
const textMatrix: Record<string, Row> = {
"text/toUpperCase": {
verdict: "emits",
src: `export function f(s: Uint8Array): Uint8Array { return s.toUpperCase(); }`,
},
"text/toLowerCase": {
verdict: "emits",
src: `export function f(s: Uint8Array): Uint8Array { return s.toLowerCase(); }`,
},
"text/repeat": {
verdict: "emits",
src: `export function f(s: Uint8Array, n: number): Uint8Array { return s.repeat(n); }`,
},
"text/startsWith": {
verdict: "emits",
src: `export function f(s: Uint8Array, t: Uint8Array): boolean { return s.startsWith(t); }`,
},
"text/endsWith": {
verdict: "emits",
src: `export function f(s: Uint8Array, t: Uint8Array): boolean { return s.endsWith(t); }`,
},
"text/includes-bytes": {
verdict: "emits",
src: `export function f(s: Uint8Array, t: Uint8Array): boolean { return s.includes(t); }`,
},
"text/includes-number": {
// The dispatch-by-argument-type rule: a number keeps JS TypedArray
// element search (one byte value), a bytes needle is substring search.
verdict: "emits",
src: `export function f(s: Uint8Array, b: number): boolean { return s.includes(b); }`,
},
"text/indexOf-bytes": {
verdict: "emits",
src: `export function f(s: Uint8Array, t: Uint8Array): number { return s.indexOf(t); }`,
},
"text/indexOf-number": {
verdict: "emits",
src: `export function f(s: Uint8Array, b: number): number { return s.indexOf(b); }`,
},
"text/lastIndexOf-bytes": {
verdict: "emits",
src: `export function f(s: Uint8Array, t: Uint8Array): number { return s.lastIndexOf(t); }`,
},
"text/lastIndexOf-number": {
verdict: "emits",
src: `export function f(s: Uint8Array, b: number): number { return s.lastIndexOf(b); }`,
},
"text/padStart": {
verdict: "emits",
src: `
import { asciiBytes } from "@native-sdk/core";
export function f(s: Uint8Array, n: number): Uint8Array { return s.padStart(n, asciiBytes("0")); }
export function g(s: Uint8Array): Uint8Array { return s.padStart(8); }`,
},
"text/padEnd": {
verdict: "emits",
src: `export function f(s: Uint8Array, n: number): Uint8Array { return s.padEnd(n); }`,
},
"text/trim": {
verdict: "emits",
src: `export function f(s: Uint8Array): Uint8Array { return s.trim(); }`,
},
"text/trimStart": {
verdict: "emits",
src: `export function f(s: Uint8Array): Uint8Array { return s.trimStart(); }`,
},
"text/trimEnd": {
verdict: "emits",
src: `export function f(s: Uint8Array): Uint8Array { return s.trimEnd(); }`,
},
"text/split": {
verdict: "emits",
src: `
import { asciiBytes } from "@native-sdk/core";
export function f(s: Uint8Array): number {
const parts = s.split(asciiBytes(","));
let total = 0;
for (const p of parts) total += p.length;
return total + parts.length;
}`,
},
"text/at": {
verdict: "emits",
src: `export function f(s: Uint8Array): number { return s.at(-1) ?? -1; }`,
},
"text-edge/repeat-negative-literal": {
// JS throws RangeError; a compile-time-knowable negative stops the
// build instead of shipping the guaranteed panic.
verdict: "gate",
id: "NS9001",
src: `export function f(s: Uint8Array): Uint8Array { return s.repeat(-1); }`,
},
"text-edge/repeat-fractional-literal": {
// The count is an integer position (JS truncates; the subset keeps
// integer flows integer end to end).
verdict: "gate",
id: "NS1016",
src: `export function f(s: Uint8Array): Uint8Array { return s.repeat(2.5); }`,
},
"text-edge/split-empty-literal-separator": {
// Per-code-point splitting would expose the UTF-16/UTF-8 seam.
verdict: "gate",
id: "NS9001",
src: `
import { asciiBytes } from "@native-sdk/core";
export function f(s: Uint8Array): number { return s.split(asciiBytes("")).length; }`,
},
"text-edge/includes-fromIndex": {
verdict: "gate",
id: "NS9001",
src: `export function f(s: Uint8Array, b: number): boolean { return s.includes(b, 2); }`,
},
"text-out/charCodeAt": {
verdict: "gate",
id: "NS1060",
src: `export function f(s: Uint8Array): number { return s.charCodeAt(0); }`,
},
"text-out/charAt": {
verdict: "gate",
id: "NS1060",
src: `export function f(s: Uint8Array): Uint8Array { return s.charAt(0); }`,
},
"text-out/codePointAt": {
verdict: "gate",
id: "NS1060",
src: `export function f(s: Uint8Array): number { return s.codePointAt(0); }`,
},
"text-out/normalize": {
verdict: "gate",
id: "NS1060",
src: `export function f(s: Uint8Array): Uint8Array { return s.normalize(); }`,
},
"text-out/replace": {
verdict: "gate",
id: "NS1060",
src: `export function f(s: Uint8Array, t: Uint8Array): Uint8Array { return s.replace(t, t); }`,
},
"text-out/replaceAll": {
verdict: "gate",
id: "NS1060",
src: `export function f(s: Uint8Array, t: Uint8Array): Uint8Array { return s.replaceAll(t, t); }`,
},
"text-out/localeCompare": {
verdict: "gate",
id: "NS1005",
src: `export function f(s: Uint8Array, t: Uint8Array): number { return s.localeCompare(t); }`,
},
"text-out/toLocaleUpperCase": {
verdict: "gate",
id: "NS1005",
src: `export function f(s: Uint8Array): Uint8Array { return s.toLocaleUpperCase(); }`,
},
"text-out/toLocaleLowerCase": {
verdict: "gate",
id: "NS1005",
src: `export function f(s: Uint8Array): Uint8Array { return s.toLocaleLowerCase(); }`,
},
"text-out/match": {
verdict: "gate",
id: "NS1040",
src: `export function f(s: Uint8Array): boolean { return s.match(1); }`,
},
"text-out/matchAll": {
verdict: "gate",
id: "NS1040",
src: `export function f(s: Uint8Array): boolean { return s.matchAll(1); }`,
},
"text-out/search": {
verdict: "gate",
id: "NS1040",
src: `export function f(s: Uint8Array): number { return s.search(1); }`,
},
};
test("text matrix: the enumeration and the table cover each other exactly", () => {
const enumerated = new Set(textSurface);
const rows = new Set(Object.keys(textMatrix));
const missing = [...enumerated].filter((p) => !rows.has(p));
const unknown = [...rows].filter((p) => !enumerated.has(p));
assert.deepEqual(missing, [], `text rows without a matrix entry (classify them!): ${missing.join(", ")}`);
assert.deepEqual(unknown, [], `text matrix rows outside the enumeration: ${unknown.join(", ")}`);
assert.equal(textSurface.length, new Set(textSurface).size, "duplicate text row names");
});
test("text matrix: every row produces exactly its classified outcome", () => {
for (const name of textSurface) {
const row = textMatrix[name];
assert.notEqual(row.verdict, "check");
assert.notEqual(row.verdict, "tsc");
const result = transpile((row as { src: string }).src);
assert.equal(
result.typeErrors.length,
0,
`${name}: fixture must be tsc-clean\n${result.typeErrors.join("\n")}`,
);
if (row.verdict === "gate") {
assert.equal(result.ok, false, `${name}: expected ${row.id}, but it transpiled`);
const ids = result.diagnostics.map((d) => d.id);
assert.ok(ids.includes(row.id), `${name}: expected ${row.id}, got ${ids.join(", ") || "none"}`);
const d = result.diagnostics.find((x) => x.id === row.id)!;
assert.ok(d.message.length > d.title.length + 20, `${name}: ${row.id} message reads as a bare fallback`);
} else {
const details = result.diagnostics.map((d) => `${d.id} ${d.message}`).join("\n");
assert.equal(result.ok, true, `${name}: expected clean transpile\n${details}`);
}
}
});
test("text matrix: every SUPPORTED row's Zig compiles", { skip: !hasZig, timeout: 300_000 }, () => {
const work = fs.mkdtempSync(path.join(os.tmpdir(), "native-core-text-"));
try {
fs.copyFileSync(path.join(pkg, "rt", "rt.zig"), path.join(work, "rt.zig"));
const imports: string[] = [];
let i = 0;
for (const name of textSurface) {
const row = textMatrix[name];
if (row.verdict !== "emits") continue;
const result = transpile(row.src!);
assert.equal(result.ok, true, `${name}: transpile failed before the zig step`);
const file = `t_${String(i++).padStart(3, "0")}.zig`;
fs.writeFileSync(path.join(work, file), result.zig!);
imports.push(` // ${name}\n refAllDecls(@import("${file}"));`);
}
const driver = [
`const refAllDecls = @import("std").testing.refAllDecls;`,
``,
`test {`,
...imports,
`}`,
``,
].join("\n");
fs.writeFileSync(path.join(work, "driver.zig"), driver);
try {
execFileSync("zig", ["test", "driver.zig"], { cwd: work, encoding: "utf8", stdio: "pipe" });
} catch (e) {
const err = e as { stderr?: string; stdout?: string };
assert.fail(`emitted Zig failed to compile:\n${err.stderr ?? ""}${err.stdout ?? ""}`);
}
} finally {
fs.rmSync(work, { recursive: true, force: true });
}
});
test("grammar matrix: the enumeration and the table cover each other exactly", () => {
const enumerated = new Set(productions);
const rows = new Set(Object.keys(matrix));
const missing = [...enumerated].filter((p) => !rows.has(p));
const unknown = [...rows].filter((p) => !enumerated.has(p));
assert.deepEqual(missing, [], `productions without a matrix row (classify them!): ${missing.join(", ")}`);
assert.deepEqual(unknown, [], `matrix rows outside the enumeration: ${unknown.join(", ")}`);
assert.equal(productions.length, new Set(productions).size, "duplicate production names");
});
test("grammar matrix: every production produces exactly its classified outcome", () => {
for (const name of productions) {
const row = matrix[name];
if (row.verdict === "check") {
const ids = ruleIds(checkOnly(row.src));
assert.ok(ids.includes(row.id), `${name}: expected ${row.id} from the checker, got ${ids.join(", ") || "none"}`);
continue;
}
const result = row.files ? transpileFiles(row.files) : transpile(row.src!);
if (row.verdict === "tsc") {
assert.ok(result.typeErrors.length > 0, `${name}: expected tsc itself to reject this`);
continue;
}
assert.equal(
result.typeErrors.length,
0,
`${name}: fixture must be tsc-clean\n${result.typeErrors.join("\n")}`,
);
if (row.verdict === "gate") {
assert.equal(result.ok, false, `${name}: expected ${row.id}, but it transpiled`);
const ids = result.diagnostics.map((d) => d.id);
assert.ok(ids.includes(row.id), `${name}: expected ${row.id}, got ${ids.join(", ") || "none"}`);
} else {
const details = result.diagnostics.map((d) => `${d.id} ${d.message}`).join("\n");
assert.equal(result.ok, true, `${name}: expected clean transpile\n${details}`);
}
}
});
test("grammar matrix: no gated diagnostic is a bare fallback — each names its construct", () => {
for (const name of productions) {
const row = matrix[name];
if (row.verdict !== "gate") continue;
const result = row.files ? transpileFiles(row.files) : transpile(row.src!);
const d = result.diagnostics.find((x) => x.id === row.id)!;
// Teaching contract: rule + fix + why — the message always carries a
// site-specific lead-in longer than the bare rule title.
assert.ok(d.message.length > d.title.length + 20, `${name}: ${row.id} message reads as a bare fallback`);
}
});
test("grammar matrix: every SUPPORTED production's Zig compiles", { skip: !hasZig, timeout: 300_000 }, () => {
const work = fs.mkdtempSync(path.join(os.tmpdir(), "native-core-grammar-"));
try {
fs.copyFileSync(path.join(pkg, "rt", "rt.zig"), path.join(work, "rt.zig"));
const imports: string[] = [];
let i = 0;
for (const name of productions) {
const row = matrix[name];
if (row.verdict !== "emits") continue;
const result = row.files ? transpileFiles(row.files) : transpile(row.src!);
assert.equal(result.ok, true, `${name}: transpile failed before the zig step`);
const file = `g_${String(i++).padStart(3, "0")}.zig`;
fs.writeFileSync(path.join(work, file), result.zig!);
imports.push(` // ${name}\n refAllDecls(@import("${file}"));`);
}
const driver = [
`const refAllDecls = @import("std").testing.refAllDecls;`,
``,
`test {`,
...imports,
`}`,
``,
].join("\n");
fs.writeFileSync(path.join(work, "driver.zig"), driver);
try {
execFileSync("zig", ["test", "driver.zig"], { cwd: work, encoding: "utf8", stdio: "pipe" });
} catch (e) {
const err = e as { stderr?: string; stdout?: string };
assert.fail(`emitted Zig failed to compile:\n${err.stderr ?? ""}${err.stdout ?? ""}`);
}
} finally {
fs.rmSync(work, { recursive: true, force: true });
}
});