diff --git a/README.md b/README.md
index af0cfd4..3018c56 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,8 @@ would look authoritative while being wrong.
| `t27_check` | Parses and typechecks `.t27` source with the real compiler front end (`wasm/explorer.wasm`, no network) and returns each diagnostic with its line, the source line and a short fix hint for foreign syntax (`in` as a name, `as u3`, `x++`, `a ? b : c`, `match`). The hint rules live in `specs/t27mcp/check_hints.t27`, run as `wasm/check_hints.wasm` (see `wasm/BUILD`); a message the spec does not know gets no hint. `ok: true` means it parses and typechecks, not that it is right: the type checker does not yet resolve names or unknown types (gHashTag/t27#8087), and tests are not run |
| `t27_examples` | Short worked examples of `.t27` idioms for a topic (`clocked`, `comb`, `fn`, `test`, `reset` with async, sync or none): 1-3 slices of real specs, at most 40 lines each, cut from the corpus at a pinned commit, with one line on what each shows. Every slice parses and typechecks and its tests pass on the lab. The topic matcher and the pointer table live in `specs/t27mcp/examples.t27`, run as `wasm/examples.wasm` (see `wasm/BUILD`); an unknown topic returns the list of topics and no example |
| `t27f_view` | Draws a still of the T27F demo reel as an SVG file and returns its path. By default the still is a presentation slide (`specs/t27f/deck.t27`); `look: 0` draws the plain map (`specs/t27f/reel.t27`). A scene still shows the whole glass card (`STILL_CARD_ALPHA`); an exact `frame` shows the card as the video would (`card_alpha`). Every position, size and colour comes from the specs, run as `wasm/t27f.wasm` (see `wasm/BUILD`) |
-| `t27f_graph` | Draws the T27F graph view (`specs/t27f/spec_graph_view.t27`) as an SVG file and returns its path: a box per node and a line per edge, placed, sized and coloured by the spec's layout functions, each box labelled with its node kind, inference label (exact, derived, suggested, unknown) and source lines. Until t27c exports graphs, only the `spec_graph` fixtures can be drawn: `fixture: 0` is tutorial 08, `fixture: 1` adds a suggestion |
+| `t27f_graph` | Draws the T27F graph view (`specs/t27f/spec_graph_view.t27`) as an SVG file and returns its path: a box per node and a line per edge, placed, sized and coloured by the spec's layout functions, each box labelled with its node kind, inference label (exact, derived, suggested, unknown) and source lines. Pass `path` (a published `.t27` spec, e.g. `specs/tutorial/08_modules_and_arrays.t27`) to draw that spec's exported graph, or `fixture` for the built-in samples (`fixture: 0` is tutorial 08, `fixture: 1` adds a suggestion). A path graph is exact only (nodes module, const, fn, test, invariant, bench, use; edges calls, uses, constrains; no derived edges) and holds at most 192 nodes and 254 edges: a larger spec is refused with that verdict, never drawn truncated |
+| `t27f_outline` | Reads a `.t27` spec through its T27F graph instead of the whole file. `path` is a published spec, e.g. `specs/tutorial/08_modules_and_arrays.t27`. Returns compact text: one line per node (kind, name, source lines a-b, label), one per edge (kind, from -> to, line) and a short section "fn X is tested by: ..." (derived from the exact calls edges, and labelled so). The wording, which facts print, how a span prints, which refusal text a verdict takes and the tested-by view are decided in `specs/t27f/spec_graph_outline.t27`, run as `wasm/t27f.wasm` (see `wasm/BUILD`); the host only moves bytes and fills the spec's `{}` templates. The graph is exact only (the exporter, `specs/t27f/spec_graph_export.t27`, run as `wasm/t27f.wasm`, emits no derived, suggested or unknown edges; top-level items only) and capped at 192 nodes and 254 edges. A spec over the cap (e.g. `specs/numeric/gf16.t27`, 436 nodes) returns the refusal verdict with the counts, never a truncated graph |
Every tool takes `fresh: true` to bypass the five-minute cache. That parameter
is not decoration: the publish pipeline is slower than a conversation, so a
diff --git a/graph.mjs b/graph.mjs
new file mode 100644
index 0000000..dde52db
--- /dev/null
+++ b/graph.mjs
@@ -0,0 +1,57 @@
+// Host I/O of t27f_outline and t27f_graph {path}. The exporter, the layout and every decision about the
+// outline run in wasm/t27f.wasm: specs/t27f/spec_graph_export.t27 (nodes and edges),
+// spec_graph_view.t27 (layout) and spec_graph_outline.t27 (the wording, which facts print, how a span
+// prints, which refusal text a verdict takes, the tests of a fn). See wasm/BUILD. This file moves bytes in,
+// reads values out through the gx_* exports and fills the `{}` marks of the spec's templates, left to
+// right. It chooses no template, filters no fact and computes no span.
+import * as G from './wasm/spec_graph_view.mjs';
+import * as O from './wasm/spec_graph_outline.mjs';
+
+// Name of constant `v` among the G constants that start with `prefix` (NODE_FN -> 'fn').
+const nm = (prefix, v) => Object.keys(G).find((c) => c.startsWith(prefix) && !c.endsWith('KINDS') && G[c] === v)?.slice(prefix.length).toLowerCase() ?? String(v);
+
+// Fill the `{}` marks of a spec template with `vals`, in order.
+const fill = (tpl, ...vals) => { let i = 0; return tpl.replace(/\{\}/g, () => String(vals[i++])); };
+
+// Write the spec into the wasm input buffer and run the exporter; returns the exporter's verdict.
+export function load(R, path, src) {
+ const body = Buffer.from(src, 'utf8'), p = Buffer.from(path, 'utf8');
+ if (!R.gx_set_len(body.length)) throw new Error(`${path} is ${body.length} bytes; the exporter's input buffer holds ${R.gx_src_cap()}`);
+ if (!R.gx_set_path_len(p.length)) throw new Error(`path is ${p.length} bytes; the limit is 512`);
+ const mem = new Uint8Array(R.memory.buffer);
+ mem.set(body, R.gx_src_ptr());
+ mem.set(p, R.gx_path_ptr());
+ return R.gx_load();
+}
+
+export const nodeName = (R, i) => Buffer.from(R.memory.buffer, R.gx_node_name_ptr(i), R.gx_node_name_len(i)).toString('utf8');
+
+// The text of a refused graph: the spec picks the template, the host fills in the numbers.
+export function refusal(R, path, v) {
+ const t = O.REFUSAL_TEXT[R.refusal_index(v)];
+ if (v === G.ERR_TOO_MANY_NODES) return fill(t, path, R.gx_node_count(), G.MAX_NODES);
+ if (v === G.ERR_TOO_MANY_EDGES) return fill(t, path, R.gx_edge_count(), G.MAX_EDGES);
+ return fill(t, path, nm('ERR_', v).replace(/_/g, ' '), v);
+}
+
+export function outline(R, path, src) {
+ const v = load(R, path, src);
+ if (v !== G.GRAPH_OK) return refusal(R, path, v);
+ const T = O.OUTLINE_TEXT, n = R.gx_node_count(), m = R.gx_edge_count(), out = [];
+ const span = (i) => { const a = R.gx_line_start(i), b = R.gx_line_end(i); return R.span_is_range(a, b) ? `${a}-${b}` : String(a); };
+ out.push(fill(T[0], path, R.gx_line_count(), n, m, G.MAX_NODES, G.MAX_EDGES), T[1]);
+ for (let i = 0; i < n; i++) if (R.shows_in_outline(R.gx_node_label(i))) out.push(fill(T[4], i, nm('NODE_', R.gx_node_kind(i)), nodeName(R, i), span(i), nm('LABEL_', R.gx_node_label(i))));
+ out.push(T[2]);
+ for (let j = 0; j < m; j++) {
+ const f = R.gx_edge_from(j), t = R.gx_edge_to(j);
+ if (R.shows_in_outline(R.gx_edge_label(j))) out.push(fill(T[5], nm('EDGE_', R.gx_edge_kind(j)), f, nodeName(R, f), t, nodeName(R, t), R.gx_edge_line(j)));
+ }
+ out.push(T[3]);
+ for (let i = 0; i < n; i++) {
+ if (R.gx_node_kind(i) !== G.NODE_FN) continue;
+ const names = [];
+ for (let k = 0; k < R.gx_o_tested_by_count(i); k++) names.push(nodeName(R, R.gx_o_tested_by_of(i, k)));
+ out.push(names.length ? fill(T[6], nodeName(R, i), names.join('; ')) : fill(T[7], nodeName(R, i)));
+ }
+ return out.join('\n');
+}
diff --git a/package.json b/package.json
index 4de3372..e49dd60 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"type": "module",
"bin": { "t27-mcp": "./server.mjs" },
"main": "server.mjs",
- "files": ["server.mjs", "check.mjs", "examples.mjs", "brief.mjs", "deck-view.mjs", "wasm/explorer.wasm", "wasm/check_hints.wasm", "wasm/check_hints.mjs", "wasm/examples.wasm", "wasm/examples_table.mjs", "wasm/spec_brief.wasm", "wasm/spec_brief.mjs", "wasm/t27f.wasm", "wasm/reel.mjs", "wasm/deck.mjs", "README.md"],
+ "files": ["server.mjs", "graph.mjs", "check.mjs", "examples.mjs", "brief.mjs", "deck-view.mjs", "wasm/explorer.wasm", "wasm/check_hints.wasm", "wasm/check_hints.mjs", "wasm/examples.wasm", "wasm/examples_table.mjs", "wasm/spec_brief.wasm", "wasm/spec_brief.mjs", "wasm/t27f.wasm", "wasm/spec_graph_view.mjs", "wasm/spec_graph_export.mjs", "wasm/spec_graph_outline.mjs", "wasm/reel.mjs", "wasm/deck.mjs", "README.md"],
"engines": { "node": ">=18" },
"scripts": {
"start": "node server.mjs",
diff --git a/server.mjs b/server.mjs
index acbf95e..0bd3e13 100644
--- a/server.mjs
+++ b/server.mjs
@@ -22,6 +22,7 @@ import { slide } from './deck-view.mjs';
import { check } from './check.mjs';
import { examples } from './examples.mjs';
import { brief, one, failureReasons } from './brief.mjs';
+import * as GX from './graph.mjs';
const VERSION = '1.1.0';
const SITE = process.env.T27_SITE || 'https://t27.ai';
@@ -178,9 +179,15 @@ const SPEC_FILTERS = {
// deck look, specs/t27f/deck.t27 (gHashTag/t27); their bodies run as wasm/t27f.wasm and their constants
// are wasm/reel.mjs and wasm/deck.mjs. This glue writes SVG only; the deck look is deck-view.mjs.
// t27f_graph draws specs/t27f/spec_graph_view.t27 the same way; its constants are wasm/spec_graph_view.mjs.
+// t27f_outline and t27f_graph {path} read a spec's bytes through specs/t27f/spec_graph_export.t27 (graph.mjs is the I/O).
const R = new WebAssembly.Instance(new WebAssembly.Module(readFileSync(new URL('./wasm/t27f.wasm', import.meta.url)))).exports;
const PAINT = ['none', '#3a3f4b', '#3b82f6', '#8a7a3a', '#ffd45a', '#22c55e', '#1f2229']; // host palette by STYLE_*
const esc = (v) => String(v).replace(/[<>&"]/g, (c) => `${c.charCodeAt(0)};`);
+// A corpus-relative .t27 path for the exporter tools; the fetch itself is specSource's.
+const specPath = (p) => {
+ if (typeof p !== 'string' || !/^[A-Za-z0-9_\-./]+\.t27$/.test(p) || p.startsWith('/') || p.split('/').includes('..')) throw new Error('path must be a corpus path to a .t27 file, e.g. specs/tutorial/08_modules_and_arrays.t27');
+ return p;
+};
const tools = [
{
@@ -667,29 +674,50 @@ const tools = [
return `${path}\nT27F demo reel, ${look === deck.LOOK_DECK ? `${['cover', 'stage', 'split'][R.layout_for(R.demo_kind(s))]} slide` : 'map'}, scene ${s} (${name}), frame ${f} of ${n}, ${W}x${H}, focus cell ${focus}; channel for host ${host}: ${ch}.`;
},
},
+ {
+ name: 't27f_outline',
+ description:
+ "A spec's structure from its T27F graph instead of its whole source: one line per declaration (kind, name, lines a-b, label) and per edge (calls, uses, constrains: from -> to, line of first use), plus a short derived list of which tests call which fn. Read by specs/t27f/spec_graph_export.t27, a deterministic byte scanner: every node and edge is EXACT, no derived or suggested edge is invented; the tested-by list is marked derived and is only an inversion of the exact test -> fn calls. The graph holds at most 192 nodes and 254 edges (top-level declarations and the edges between them); a larger spec is refused with that verdict, never truncated. Only the top level is read: nothing inside a struct or enum body, names are matched as text, not resolved.",
+ inputSchema: { type: 'object', properties: { path: { type: 'string', description: 'Exact corpus path, e.g. specs/tutorial/08_modules_and_arrays.t27' }, fresh: { type: 'boolean' } }, required: ['path'] },
+ handler: async ({ path, fresh }) => GX.outline(R, specPath(path), await specSource(specPath(path), fresh)),
+ },
{
name: 't27f_graph',
description:
- 'Draw the T27F graph view (specs/t27f/spec_graph_view.t27) of a spec graph as an SVG file and return its path: a box per node, a line per edge, placed and styled by the spec, labelled by kind, inference label and source lines. Only the spec_graph fixtures exist until t27c exports graphs: 0 tutorial 08, 1 tutorial 08 with a suggestion.',
- inputSchema: { type: 'object', properties: { fixture: { type: 'integer', description: '0 tutorial 08 (default), 1 with a suggestion' }, out: { type: 'string', description: 'Output file (default: the temp directory)' } } },
- handler: async ({ fixture = 0, out }) => {
- if (fixture !== 0 && fixture !== 1) throw new Error('fixture must be 0 or 1');
- const v = R.fx_verdict(fixture), hex = (c) => `#${c.toString(16).padStart(6, '0')}`;
- if (v !== G.VIEW_OK) throw new Error(`the view refuses fixture ${fixture}: verdict ${v}`);
+ "Draw the T27F graph view (specs/t27f/spec_graph_view.t27) of a spec graph as an SVG file and return its path: a box per node, a line per edge, placed and styled by the spec, labelled by kind, name, inference label and source lines. With path, the graph is the exact-only export of that corpus spec (specs/t27f/spec_graph_export.t27: no derived edges; at most 192 nodes and 254 edges, a larger spec is refused, never truncated). With fixture, the hand-read tutorial 08 graph: 0 as is, 1 with a suggestion. path wins when both are given.",
+ inputSchema: { type: 'object', properties: { path: { type: 'string', description: 'Exact corpus path, e.g. specs/tutorial/08_modules_and_arrays.t27' }, fixture: { type: 'integer', description: '0 tutorial 08 (default), 1 with a suggestion' }, out: { type: 'string', description: 'Output file (default: the temp directory)' }, fresh: { type: 'boolean' } } },
+ handler: async ({ path, fixture = 0, out, fresh }) => {
+ // q reads the same quantities from the exported graph (gx_*) or from a fixture (fx_*).
+ let q, tag;
+ if (path !== undefined) {
+ const p = specPath(path), v = GX.load(R, p, await specSource(p, fresh));
+ if (v !== G.GRAPH_OK) throw new Error(GX.refusal(R, p, v));
+ const g = { width: 'view_width', height: 'view_height', verdict: 'view_verdict', node_x: 'view_node_x', node_y: 'view_node_y', node_w: 'view_node_w', node_h: 'view_node_h', node_fill: 'view_node_fill', node_stroke: 'view_node_stroke', node_dash: 'view_node_dash', edge_x1: 'view_edge_x1', edge_y1: 'view_edge_y1', edge_x2: 'view_edge_x2', edge_y2: 'view_edge_y2', edge_stroke: 'view_edge_stroke', edge_dash: 'view_edge_dash', node_line_start: 'line_start', node_line_end: 'line_end' };
+ q = (k, ...a) => R[`gx_${g[k] ?? k}`](...a);
+ tag = p;
+ } else {
+ if (fixture !== 0 && fixture !== 1) throw new Error('fixture must be 0 or 1');
+ q = (k, ...a) => R[`fx_${k}`](fixture, ...a);
+ tag = `fixture ${fixture}`;
+ }
+ const v = q('verdict'), hex = (c) => `#${c.toString(16).padStart(6, '0')}`;
+ if (v !== G.VIEW_OK) throw new Error(`the view refuses ${tag}: verdict ${v}`);
const nm = (p, k) => Object.keys(G).find((c) => c.startsWith(p) && !c.endsWith('KINDS') && G[c] === k)?.slice(p.length).toLowerCase() ?? k;
- const W = R.fx_width(fixture), H = R.fx_height(fixture), svg = [``];
- for (let j = 0; j < R.fx_edge_count(fixture); j++) {
- const d = R.fx_edge_dash(fixture, j);
- svg.push(`${nm('EDGE_', R.fx_edge_kind(fixture, j))}, line ${R.fx_edge_line(fixture, j)}`);
+ const W = q('width'), H = q('height'), svg = [``];
+ for (let j = 0; j < q('edge_count'); j++) {
+ const d = q('edge_dash', j);
+ svg.push(`${nm('EDGE_', q('edge_kind', j))}, line ${q('edge_line', j)}`);
}
- for (let i = 0; i < R.fx_node_count(fixture); i++) {
- const x = R.fx_node_x(fixture, i), y = R.fx_node_y(fixture, i), d = R.fx_node_dash(fixture, i), a = R.fx_node_line_start(fixture, i), b = R.fx_node_line_end(fixture, i);
- svg.push(``,
- `${nm('NODE_', R.fx_node_kind(fixture, i))} ${i} - ${nm('LABEL_', R.fx_node_label(fixture, i))} - L${a}${b > a ? `-${b}` : ''}`);
+ for (let i = 0; i < q('node_count'); i++) {
+ const x = q('node_x', i), y = q('node_y', i), d = q('node_dash', i), a = q('node_line_start', i), b = q('node_line_end', i);
+ const who = path !== undefined ? GX.nodeName(R, i) : String(i), w = q('node_w', i);
+ const full = `${nm('NODE_', q('node_kind', i))} ${who} - ${nm('LABEL_', q('node_label', i))} - L${a}${b > a ? `-${b}` : ''}`, fit = Math.floor((w - 16) / (G.VIEW_FONT_PX * 0.6)); // host text fitting; the box is the spec's
+ svg.push(``,
+ `${esc(full)}${esc(full.length > fit ? `${full.slice(0, fit - 1)}...` : full)}`);
}
- const path = out || join(tmpdir(), `t27f-graph-fx${fixture}.svg`);
- writeFileSync(path, `\n`);
- return `${path}\nT27F graph view, fixture ${fixture}: ${R.fx_node_count(fixture)} nodes, ${R.fx_edge_count(fixture)} edges, ${W}x${H}.`;
+ const file = out || join(tmpdir(), path !== undefined ? `t27f-graph-${path.replace(/[^A-Za-z0-9]+/g, '_')}.svg` : `t27f-graph-fx${fixture}.svg`);
+ writeFileSync(file, `\n`);
+ return `${file}\nT27F graph view, ${tag}: ${q('node_count')} nodes, ${q('edge_count')} edges, ${W}x${H}${path !== undefined ? ', exact only' : ''}.`;
},
},
];
diff --git a/wasm/BUILD b/wasm/BUILD
index a14a1b1..edee409 100644
--- a/wasm/BUILD
+++ b/wasm/BUILD
@@ -4,7 +4,9 @@ Every number t27f_view draws comes from two specs in gHashTag/t27:
specs/t27f/reel.t27 (module T27fReel: the map, the camera, the timeline) and
specs/t27f/deck.t27 (module T27fDeck: the default slide look). Every number
t27f_graph draws comes from specs/t27f/spec_graph_view.t27 (the T27F graph
-view); t27c gen inlines the spec_graph and ui_plan items it uses. Nothing in this
+view); t27c gen inlines the spec_graph and ui_plan items it uses. Every node and
+edge of t27f_outline, and of t27f_graph {path}, comes from
+specs/t27f/spec_graph_export.t27 (the exact-only exporter). Nothing in this
directory is a second home for that math:
reel.zig generated: t27c gen specs/t27f/reel.t27 (do not edit)
@@ -13,13 +15,27 @@ directory is a second home for that math:
deck.mjs generated: t27c gen-js specs/t27f/deck.t27 (do not edit)
spec_graph_view.zig, spec_graph_view.mjs
generated: t27c gen / gen-js specs/t27f/spec_graph_view.t27
- export.zig hand glue, 46 lines: exports every scalar pub fn of reel.zig,
- deck.zig and spec_graph_view.zig (up to five parameters) under its own name with
- the C calling convention. deck.zig inlines what it imports from
+ spec_graph_export.zig, spec_graph_export.mjs
+ generated: t27c gen / gen-js specs/t27f/spec_graph_export.t27
+ spec_graph_outline.zig, spec_graph_outline.mjs
+ generated: t27c gen / gen-js specs/t27f/spec_graph_outline.t27
+ export.zig hand glue, 138 lines. The first 46 lines export every scalar pub fn of
+ reel.zig, deck.zig, spec_graph_view.zig and spec_graph_outline.zig (up to five
+ parameters) under its
+ own name with the C calling convention. deck.zig inlines what it imports from
reel (half_height), so a name an earlier module already has is
- exported once, from the first. The comptime walk over three
- modules needs a raised eval branch quota. It names no spec function and holds no number.
+ exported once, from the first. The comptime walk over four
+ modules needs a raised eval branch quota. It names no spec function and holds
+ no number. The other 92 lines are the exporter glue, described below.
t27f.wasm built from export.zig + reel.zig + deck.zig + spec_graph_view.zig
+ + spec_graph_export.zig + spec_graph_outline.zig
+ ../graph.mjs hand host I/O, 57 lines: writes the source bytes into wasm memory, reads
+ values back through the gx_ exports and fills the `{}` marks of the
+ templates in spec_graph_outline.mjs (OUTLINE_TEXT, REFUSAL_TEXT), left to
+ right. Which template, which facts print, how a span prints and which tests
+ call a fn are answers of t27 functions (refusal_index, shows_in_outline,
+ span_is_range, gx_o_tested_by_count / gx_o_tested_by_of); the host loops over
+ nodes and edges and chooses nothing.
The bodies run as t27f.wasm, because gen-js emits constants only. reel.mjs and
deck.mjs supply the constants (presets, regions, type sizes, palette, scene
@@ -34,6 +50,10 @@ Rebuild (on the Railway lab, never on the owner's Mac), from specs/t27f/:
t27c gen-js deck.t27 > deck.mjs
t27c gen spec_graph_view.t27 > spec_graph_view.zig
t27c gen-js spec_graph_view.t27 > spec_graph_view.mjs
+ t27c gen spec_graph_export.t27 > spec_graph_export.zig
+ t27c gen-js spec_graph_export.t27 > spec_graph_export.mjs
+ t27c gen spec_graph_outline.t27 > spec_graph_outline.zig
+ t27c gen-js spec_graph_outline.t27 > spec_graph_outline.mjs
zig build-exe export.zig -target wasm32-freestanding -fno-entry -rdynamic \
-O ReleaseSmall -femit-bin=t27f.wasm
@@ -47,18 +67,65 @@ before it, #9992 STILL_CARD_ALPHA). deck.t27 at #10036: 16/16 tests pass,
gHashTag/t27 950631580 (landed by #10059): 8/8 tests, 0 vacuous; t27b 8
passed, 4 invariants held.
+The exporter (t27f_outline, t27f_graph {path}) was added later. spec_graph.t27,
+spec_graph_view.t27 and spec_graph_export.t27 were regenerated from gHashTag/t27 master
+290ff17df, after #10173 (exporter) and #10181 (SpecGraph holds up to 192 nodes and 254
+edges), with t27c 0.5.2 and zig 0.16.0 on the Railway lab. The sha256 of the sources below
+were checked against that master. t27c test-report: spec_graph_export 20 of 20 pass, 0
+vacuous; spec_graph_view 9 of 9, 0 vacuous; spec_graph 14 of 14, 0 vacuous.
+
+spec_graph_export.t27 is a deterministic byte scanner: export_graph(path, src) returns a
+SpecGraph and validate_graph returns the verdict. It emits only EXACT nodes (module, const,
+fn, test, invariant, bench, use) and EXACT edges (calls, uses, constrains), never derived,
+suggested or unknown ones.
+
+The outline wording and decisions come from specs/t27f/spec_graph_outline.t27 (gHashTag/t27#10200,
+PR #10202): OUTLINE_TEXT and REFUSAL_TEXT (templates with `{}` marks, the order of the marks is in
+the spec's comments), shows_in_outline, span_is_range, refusal_index, and the tested_by view of
+exact calls edges (tested_by_count, tested_by_of). t27c check 0 errors; t27c test-report 8 of 8
+pass, 0 vacuous, on the Railway lab. The source was generated from the branch t27f/outline-spec of
+gHashTag/t27 (head d1d8d74fa, on master 2d358a708). The outline spec carries its own small copy
+of the tested_by rule until the derive spec (gHashTag/t27#10198) lands.
+
+spec_graph_export.zig, spec_graph_view.zig and spec_graph_outline.zig each carry their own
+SpecGraph type, because t27c gen inlines the items a module uses. The glue copies the graph from
+the exporter's type into the other two field by field with `inline for`.
+
+Glue in export.zig (the 92 added lines; no spec logic):
+ - an input buffer: gx_src_ptr and gx_src_cap (2 MiB; the biggest spec in specs/ is
+ 1,364,985 bytes) and gx_path_ptr (512 bytes), with the setters gx_set_len and
+ gx_set_path_len. The host writes the UTF-8 bytes there.
+ - gx_load(): runs export_graph on the buffers, copies the graph, returns the verdict.
+ - scalar getters, one per SpecGraph field (gx_; an out-of-range index reads 0),
+ a gx_view_ wrapper for each scalar view_* fn of spec_graph_view.zig and a gx_o_
+ wrapper for each scalar fn of spec_graph_outline.zig that takes a graph, each bound to the
+ loaded graph.
+ - how names (strings) come back: gx_node_name_ptr(i) and gx_node_name_len(i) return a
+ pointer and a length into the source buffer. The host decodes those bytes from wasm
+ memory; a name is a slice of the source, never a copy.
+Host side, ../graph.mjs: byte input and output only, see the list above. It still loops over
+nodes and edges and names a node kind, edge kind, label or verdict from the generated constants
+of spec_graph_view.mjs (those name tables are not emitted by gen-js).
+
sha256
+ a3dbe0ec5110c48183ebea101109598e8542bb0ffdf9e061c672162d7da03fe7 spec_graph.t27 (source)
+ ef4ee24d5debc51be4a10835947c86a0211b867dd6ca97890a15198daa744728 spec_graph_export.t27 (source)
+ 5297308f1d54c2688801995d56db402809fc794a7dbe5f28ad119c5b3593ede7 spec_graph_export.zig
+ 0f379cfd0ff5d3d5c6c62c8510c191bdaef7bdb89f59126b6c28c741ab3aec5c spec_graph_export.mjs
c47b21cea1d7169da6528e321cf2d2c513d3f552c0cd04fe9d37bbd5314ccacf reel.t27 (source)
bd8ef9fc155365c805ba716d97155bed1e4017c378dbc335e252bcb244512147 deck.t27 (source)
997f3fc1d04e78ef3c8681c7b0b726d75341cc96928710771bca279cbd1184ba reel.zig
b7e1efaba309134633cd13cafe5515c0eab1ae403c229d0fca6ae3ea1dafe00c deck.zig
4dc1a0db9968c7a74495b499c480c2093425f653d49265d2f21c83f24e1835ac reel.mjs
5a060aa5777ae6f7ff0317da0c86551410a1ab87e179ad2f8bc75ba08c2c2f7c deck.mjs
- 54ad20408cb50a1f4fbe18981019f7ba4f13b87fa7f6940449e38b4479bf35e6 spec_graph_view.t27 (source)
- 92c1f60e44c3acb90a42481990d3f95400b7283549fc95d4bac4ac917edac0b1 spec_graph_view.zig
- 18756722b37160ffa7dd021b4bcf02485dccf1506e9b085e6a36d017d9d6911b spec_graph_view.mjs
- e9757024442a6723ab9b97fe32b958b8d6b755b1d3d08337d8e3fb04cfe2a140 export.zig
- 950948f93497c97dc5d3da16155a2af00946717308aaedf001fa3755b95a783f t27f.wasm
+ 22579bc98aecb3c4405991eef8b5060eb4151b17b200dfbae07a75d8b1e8547a spec_graph_view.t27 (source)
+ 191d55d9bb54d57017943a1903790e4661dfd237dfe507804cef3fcdfab33c19 spec_graph_view.zig
+ 739118fa01cf501bdb3eab925ef9404873230a82907faf5e6e44c006e206c962 spec_graph_view.mjs
+ a5ff9792202408ac5bc68f9a479a38671c5eebd970886ab48ee65508feeeab2f export.zig
+ 92e0bf52018e6eabebec1705d04a15450a2ebd8abe1b737891a9ad3c0991a1c7 spec_graph_outline.t27 (source)
+ 581f9c9af4f5b489c8a7e275e757fae648b84ef6d00c5b333fd417a8e97cb0e8 spec_graph_outline.zig
+ a8102abac8b3096b109c9337ad69ad61149efc36870f74bdd4d3a0a1205a21ff spec_graph_outline.mjs
+ deedd9c104392469348df452c72d1f4e4f1a68930ba8acb130110bf92c65cd56 t27f.wasm
t27_check (gHashTag/t27#10073, epic #10066)
diff --git a/wasm/export.zig b/wasm/export.zig
index b3c6d5f..a36a3e4 100644
--- a/wasm/export.zig
+++ b/wasm/export.zig
@@ -1,7 +1,7 @@
// Hand glue: export every pub fn of the t27c-generated Zig modules whose parameters and result are
// integers or bools, under its own name, so a wasm host can call the spec's bodies. It knows no
// spec: it decides nothing and computes nothing. Build: see wasm/BUILD in gHashTag/t27-mcp.
-const mods = .{ @import("reel.zig"), @import("deck.zig"), @import("spec_graph_view.zig") };
+const mods = .{ @import("reel.zig"), @import("deck.zig"), @import("spec_graph_view.zig"), @import("spec_graph_outline.zig") };
fn seen(comptime i: usize, comptime name: []const u8) bool {
inline for (mods, 0..) |m, k| if (k < i and @hasDecl(m, name)) return true;
@@ -44,3 +44,95 @@ comptime {
if (ok) @export(&C(v).c, .{ .name = d.name });
};
}
+
+// ---- t27f_outline and t27f_graph {path}: the input and output of specs/t27f/spec_graph_export.t27.
+// Still glue, still no spec knowledge. The host writes a spec's bytes into gx_src_ptr()..+gx_set_len(n)
+// and its path into gx_path_ptr()..+gx_set_path_len(n), then gx_load() runs export_graph and keeps the
+// graph in two copies: eg (the exporter's SpecGraph) and vg (the view's own SpecGraph, a distinct Zig
+// type with the same fields, since t27c gen inlines the IR into each module). Everything else is a
+// getter named after a field of the IR or a layout function of the view; name bytes stay in the source
+// buffer, so gx_node_name_ptr/len return the exporter's own slice.
+const X = @import("spec_graph_export.zig");
+const V = @import("spec_graph_view.zig");
+const O = @import("spec_graph_outline.zig");
+
+var src_buf: [2 << 20]u8 = undefined;
+var path_buf: [512]u8 = undefined;
+var src_len: usize = 0;
+var path_len: usize = 0;
+var eg: X.SpecGraph = undefined;
+var vg: V.SpecGraph = undefined;
+var og: O.SpecGraph = undefined;
+
+export fn gx_src_ptr() usize { return @intFromPtr(&src_buf); }
+export fn gx_src_cap() u32 { return src_buf.len; }
+export fn gx_path_ptr() usize { return @intFromPtr(&path_buf); }
+export fn gx_set_len(n: u32) bool { if (n > src_buf.len) return false; src_len = n; return true; }
+export fn gx_set_path_len(n: u32) bool { if (n > path_buf.len) return false; path_len = n; return true; }
+export fn gx_load() u8 {
+ eg = X.export_graph(path_buf[0..path_len], src_buf[0..src_len]);
+ inline for (@typeInfo(V.SpecGraph).@"struct".fields) |f| @field(vg, f.name) = @field(eg, f.name);
+ inline for (@typeInfo(O.SpecGraph).@"struct".fields) |f| @field(og, f.name) = @field(eg, f.name);
+ return X.validate_graph(eg);
+}
+
+fn Field(comptime name: []const u8) type {
+ const F = @TypeOf(@field(eg, name));
+ return switch (@typeInfo(F)) {
+ .int => struct { fn get() callconv(.c) F { return @field(eg, name); } },
+ .array => |a| struct {
+ fn get(i: u32) callconv(.c) a.child { return if (i < a.len) @field(eg, name)[i] else 0; }
+ fn ptr(i: u32) callconv(.c) usize { return if (i < a.len) @intFromPtr(@field(eg, name)[i].ptr) else 0; }
+ fn len(i: u32) callconv(.c) usize { return if (i < a.len) @field(eg, name)[i].len else 0; }
+ },
+ else => struct {},
+ };
+}
+
+fn Layout(comptime f: anytype, comptime outline: bool) type {
+ const I = @typeInfo(@TypeOf(f)).@"fn";
+ const P = I.params;
+ const R = I.return_type.?;
+ return switch (P.len) {
+ 1 => struct { fn c() callconv(.c) R { return f(if (outline) og else vg); } },
+ 2 => struct { fn c(a: P[1].type.?) callconv(.c) R { return f(if (outline) og else vg, a); } },
+ 3 => struct { fn c(a: P[1].type.?, b: P[2].type.?) callconv(.c) R { return f(if (outline) og else vg, a, b); } },
+ 4 => struct { fn c(a: P[1].type.?, b: P[2].type.?, d: P[3].type.?) callconv(.c) R { return f(if (outline) og else vg, a, b, d); } },
+ else => @compileError("arity"),
+ };
+}
+
+comptime {
+ for (@typeInfo(X.SpecGraph).@"struct".fields) |f| {
+ const F = Field(f.name);
+ switch (@typeInfo(f.type)) {
+ .int => @export(&F.get, .{ .name = "gx_" ++ f.name }),
+ .array => |a| if (scalar(a.child)) {
+ @export(&F.get, .{ .name = "gx_" ++ f.name });
+ } else {
+ @export(&F.ptr, .{ .name = "gx_" ++ f.name ++ "_ptr" });
+ @export(&F.len, .{ .name = "gx_" ++ f.name ++ "_len" });
+ },
+ else => {},
+ }
+ }
+ @setEvalBranchQuota(100000);
+ for (@typeInfo(V).@"struct".decls) |d| {
+ const v = @field(V, d.name);
+ if (@typeInfo(@TypeOf(v)) != .@"fn") continue;
+ const I = @typeInfo(@TypeOf(v)).@"fn";
+ if (I.params.len < 1 or I.params.len > 4 or I.params[0].type.? != V.SpecGraph or !scalar(I.return_type.?)) continue;
+ var ok = true;
+ for (I.params[1..]) |p| ok = ok and scalar(p.type.?);
+ if (ok) @export(&Layout(v, false).c, .{ .name = "gx_" ++ d.name });
+ }
+ for (@typeInfo(O).@"struct".decls) |d| {
+ const v = @field(O, d.name);
+ if (@typeInfo(@TypeOf(v)) != .@"fn") continue;
+ const I = @typeInfo(@TypeOf(v)).@"fn";
+ if (I.params.len < 1 or I.params.len > 4 or I.params[0].type.? != O.SpecGraph or !scalar(I.return_type.?)) continue;
+ var ok = true;
+ for (I.params[1..]) |p| ok = ok and scalar(p.type.?);
+ if (ok) @export(&Layout(v, true).c, .{ .name = "gx_o_" ++ d.name });
+ }
+}
diff --git a/wasm/spec_graph_export.mjs b/wasm/spec_graph_export.mjs
new file mode 100644
index 0000000..8e3b69b
--- /dev/null
+++ b/wasm/spec_graph_export.mjs
@@ -0,0 +1,133 @@
+// Generated by `t27c gen-js` from spec_graph_export.t27. Do not edit.
+//
+// Every name and every value here comes from that spec. Change the spec and
+// regenerate; an edit made here is lost the next time anyone builds.
+
+export const NO_MODULE_NAME = "(no module)";
+export const TUTORIAL_08_SRC = "// SPDX-License-Identifier: Apache-2.0\n; 08 -- Modules, visibility and arrays\n; Lesson 8, the last one. How a spec names itself, what it exposes, how it\n; pulls in another spec, and the array/index syntax used by every lookup table\n; in the corpus.\n; .\n; After this, open specs/numeric/gf16.t27 -- it is a real spec built entirely\n; out of what these eight lessons cover.\n; phi^2 + 1/phi^2 = 3 | TRINITY\n\nmodule tutorial-08-modules;\n\n// ============================================================================\n// use\n//\n// use path::to::spec;\n//\n// Pulls in another spec. Paths are `::`-separated and rooted at the corpus,\n// not at the filesystem.\n// ============================================================================\n\nuse base::types;\n\n// ============================================================================\n// pub\n//\n// `pub` makes a name part of this spec's surface. Without it the name is\n// internal: usable here, invisible elsewhere. Default to leaving things\n// private -- a spec's exported surface is a promise to whoever imports it.\n// ============================================================================\n\npub const EXPORTED_WIDTH : u8 = 16;\n\n// No `pub`: an implementation detail of this module.\nconst INTERNAL_SCALE : u8 = 4;\n\npub fn scaled_width() u8 {\n return EXPORTED_WIDTH / INTERNAL_SCALE;\n}\n\n// ============================================================================\n// Arrays\n//\n// pub const NAME : [N]TYPE = [a, b, c];\n//\n// The length and the element type are part of the declared type, written out\n// like every other width in this language. Fixed tables like this are how the\n// numeric specs carry their constants.\n//\n// The Zig-style `[_]TYPE{ a, b, c }` parses, but it lowers to an untyped tuple:\n// indexing one with a runtime value does not compile. Write the type.\n// ============================================================================\n\npub const TRIT_VALUES : [3]i8 = [-1, 0, 1];\n\npub const POWERS_OF_TWO : [8]u16 = [1, 2, 4, 8, 16, 32, 64, 128];\n\npub const EXPONENT_BIAS : [4]u8 = [15, 31, 63, 127];\n\n// ============================================================================\n// Indexing\n//\n// `array[i]`, zero-based.\n// ============================================================================\n\npub fn power_of_two(index: u8) u16 {\n return POWERS_OF_TWO[index];\n}\n\npub fn bias_for_width(index: u8) u8 {\n return EXPONENT_BIAS[index];\n}\n\n// ============================================================================\n// Tests\n// ============================================================================\n\ntest \"the power table matches its own definition\" {\n try std.testing.expectEqual(@as(u16, 1), power_of_two(0));\n try std.testing.expectEqual(@as(u16, 128), power_of_two(7));\n}\n\ntest \"bias table carries the four standard widths\" {\n try std.testing.expectEqual(@as(u8, 15), bias_for_width(0));\n try std.testing.expectEqual(@as(u8, 127), bias_for_width(3));\n}\n\ntest \"a private constant is still usable inside its own module\" {\n try std.testing.expectEqual(@as(u8, 4), scaled_width());\n}\n\ninvariant exported_width_is_a_power_of_two\n assert EXPORTED_WIDTH == 16\n";
+export const Decl = Object.freeze({ __struct__: "Decl", fields: [["kind", "u8"], ["label", "u8"], ["na", "usize"], ["nb", "usize"], ["sa", "usize"], ["sb", "usize"], ["is_module", "bool"]] });
+export const LineScan = Object.freeze({ __struct__: "LineScan", fields: [["depth", "u32"], ["tail", "u8"]] });
+export const NameSpans = Object.freeze({ __struct__: "NameSpans", fields: [["ma", "[192]usize"], ["mb", "[192]usize"], ["ia", "[192]usize"]] });
+// t27c gen-js: fn ex_space was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn ex_word was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn skip_space was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn word_end was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn token_end was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn line_end_at was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn count_lines was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn skip_lit was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn closing_quote was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn scan_line was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn opaque_keyword was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn classify was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn edge_seen was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn token_edges was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn line_edges was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn body_edges was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn read_edges was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn export_graph was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn export_verdict was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn node_matches was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn nodes_in_common was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn edge_in was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn edges_shared was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+export const EDGE_CALLS = 0;
+export const EDGE_CONSTRAINS = 3;
+export const EDGE_DATA_FLOW = 4;
+export const EDGE_FROM_KINDS = [60, 61, 4, 16, 198];
+export const EDGE_KINDS = 5;
+export const EDGE_TESTED_BY = 2;
+export const EDGE_TO_KINDS = [4, 66, 56, 6, 188];
+export const EDGE_USES = 1;
+export const ERR_DUPLICATE_EDGE = 18;
+export const ERR_EDGE_DANGLING = 13;
+export const ERR_EDGE_ENDPOINT_KIND = 14;
+export const ERR_EDGE_KIND = 12;
+export const ERR_EDGE_LABEL = 16;
+export const ERR_EDGE_LINE = 17;
+export const ERR_EDGE_UNKNOWN_NODE = 15;
+export const ERR_GRAPH_VERSION = 1;
+export const ERR_LABEL = 7;
+export const ERR_MODULE = 5;
+export const ERR_NODE_KIND = 6;
+export const ERR_NODE_NAME = 8;
+export const ERR_SPAN_MISSING = 11;
+export const ERR_SPAN_ORDER = 9;
+export const ERR_SPAN_OUTSIDE = 10;
+export const ERR_SPEC_PATH = 2;
+export const ERR_TOO_MANY_EDGES = 4;
+export const ERR_TOO_MANY_NODES = 3;
+export const GRAPH_IR_VERSION = 1;
+export const GRAPH_OK = 0;
+export const LABEL_DERIVED = 1;
+export const LABEL_EXACT = 0;
+export const LABEL_KINDS = 4;
+export const LABEL_SUGGESTED = 2;
+export const LABEL_UNKNOWN = 3;
+export const MAX_EDGES = 254;
+export const MAX_NODES = 192;
+export const NODE_BENCH = 5;
+export const NODE_CONST = 1;
+export const NODE_FN = 2;
+export const NODE_INVARIANT = 4;
+export const NODE_KINDS = 8;
+export const NODE_MODULE = 0;
+export const NODE_OPAQUE = 7;
+export const NODE_TEST = 3;
+export const NODE_USE = 6;
+export const NO_LINE = 0;
+export const SpecGraph = Object.freeze({ __struct__: "SpecGraph", fields: [["ir_version", "u32"], ["spec_path", "str"], ["line_count", "u32"], ["node_count", "u32"], ["node_kind", "[192]u8"], ["node_label", "[192]u8"], ["node_name", "[192]str"], ["line_start", "[192]u32"], ["line_end", "[192]u32"], ["edge_count", "u32"], ["edge_kind", "[254]u8"], ["edge_from", "[254]u8"], ["edge_to", "[254]u8"], ["edge_label", "[254]u8"], ["edge_line", "[254]u32"]] });
+// t27c gen-js: fn blank_graph was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn count_edge_kind was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn count_edge_label was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn count_node_kind was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn count_node_label was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn has_edge was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn kind_bit was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn span_holds was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn tutorial_08_graph was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn valid_edge_kind was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn valid_label was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn valid_node_kind was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn validate_edge was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn validate_graph was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn validate_graph_header was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn validate_node was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn with_edge was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn with_node was not emitted -- this backend lowers declarations, not bodies.
+
+// Declaration order, which the spec's own laws depend on.
+export const __STRUCT_ORDER__ = ["Decl", "LineScan", "NameSpans", "SpecGraph"];
+export const __DECL_ORDER__ = ["NO_MODULE_NAME", "TUTORIAL_08_SRC", "Decl", "LineScan", "NameSpans", "EDGE_CALLS", "EDGE_CONSTRAINS", "EDGE_DATA_FLOW", "EDGE_FROM_KINDS", "EDGE_KINDS", "EDGE_TESTED_BY", "EDGE_TO_KINDS", "EDGE_USES", "ERR_DUPLICATE_EDGE", "ERR_EDGE_DANGLING", "ERR_EDGE_ENDPOINT_KIND", "ERR_EDGE_KIND", "ERR_EDGE_LABEL", "ERR_EDGE_LINE", "ERR_EDGE_UNKNOWN_NODE", "ERR_GRAPH_VERSION", "ERR_LABEL", "ERR_MODULE", "ERR_NODE_KIND", "ERR_NODE_NAME", "ERR_SPAN_MISSING", "ERR_SPAN_ORDER", "ERR_SPAN_OUTSIDE", "ERR_SPEC_PATH", "ERR_TOO_MANY_EDGES", "ERR_TOO_MANY_NODES", "GRAPH_IR_VERSION", "GRAPH_OK", "LABEL_DERIVED", "LABEL_EXACT", "LABEL_KINDS", "LABEL_SUGGESTED", "LABEL_UNKNOWN", "MAX_EDGES", "MAX_NODES", "NODE_BENCH", "NODE_CONST", "NODE_FN", "NODE_INVARIANT", "NODE_KINDS", "NODE_MODULE", "NODE_OPAQUE", "NODE_TEST", "NODE_USE", "NO_LINE", "SpecGraph"];
+
+// What this spec holds and this backend did not print. Empty is the whole
+// story most of the time; an entry here is a promise the artifact does not
+// keep, and reading it is how a tool tells a partial module from a complete
+// one without parsing comments.
+//
+// A LIST, not a map keyed by name. `specs/numeric/formats.t27` declares four
+// separate consts called `result`, one per test block, and under an object
+// literal three of the four omissions vanished into the fourth -- a record of
+// what went missing that itself went missing. A spec is free to reuse a
+// name; this file is not free to lose the second one.
+export const __NOT_EMITTED__ = Object.freeze([]);
diff --git a/wasm/spec_graph_export.zig b/wasm/spec_graph_export.zig
new file mode 100644
index 0000000..c3b1636
--- /dev/null
+++ b/wasm/spec_graph_export.zig
@@ -0,0 +1,1240 @@
+// Generated from t27 spec: t27f_spec_graph_export (module name)
+// DO NOT EDIT — generated by t27c
+// phi^2 + 1/phi^2 = 3 | TRINITY
+
+const std = @import("std");
+fn __t27_assert_fail(comptime fmt: []const u8, args: anytype) noreturn {
+ if (@inComptime()) {
+ @compileError("assertion failed");
+ } else {
+ std.debug.print(fmt, args);
+ @panic("assertion failed");
+ }
+}
+fn assert_eq(a: anytype, b: anytype) void {
+ if (a != b) __t27_assert_fail("\n {any}\n != {any}\n", .{ a, b });
+}
+
+pub const NO_MODULE_NAME: []const u8 = "(no module)";
+pub const TUTORIAL_08_SRC: []const u8 = "// SPDX-License-Identifier: Apache-2.0\n; 08 -- Modules, visibility and arrays\n; Lesson 8, the last one. How a spec names itself, what it exposes, how it\n; pulls in another spec, and the array/index syntax used by every lookup table\n; in the corpus.\n; .\n; After this, open specs/numeric/gf16.t27 -- it is a real spec built entirely\n; out of what these eight lessons cover.\n; phi^2 + 1/phi^2 = 3 | TRINITY\n\nmodule tutorial-08-modules;\n\n// ============================================================================\n// use\n//\n// use path::to::spec;\n//\n// Pulls in another spec. Paths are `::`-separated and rooted at the corpus,\n// not at the filesystem.\n// ============================================================================\n\nuse base::types;\n\n// ============================================================================\n// pub\n//\n// `pub` makes a name part of this spec's surface. Without it the name is\n// internal: usable here, invisible elsewhere. Default to leaving things\n// private -- a spec's exported surface is a promise to whoever imports it.\n// ============================================================================\n\npub const EXPORTED_WIDTH : u8 = 16;\n\n// No `pub`: an implementation detail of this module.\nconst INTERNAL_SCALE : u8 = 4;\n\npub fn scaled_width() u8 {\n return EXPORTED_WIDTH / INTERNAL_SCALE;\n}\n\n// ============================================================================\n// Arrays\n//\n// pub const NAME : [N]TYPE = [a, b, c];\n//\n// The length and the element type are part of the declared type, written out\n// like every other width in this language. Fixed tables like this are how the\n// numeric specs carry their constants.\n//\n// The Zig-style `[_]TYPE{ a, b, c }` parses, but it lowers to an untyped tuple:\n// indexing one with a runtime value does not compile. Write the type.\n// ============================================================================\n\npub const TRIT_VALUES : [3]i8 = [-1, 0, 1];\n\npub const POWERS_OF_TWO : [8]u16 = [1, 2, 4, 8, 16, 32, 64, 128];\n\npub const EXPONENT_BIAS : [4]u8 = [15, 31, 63, 127];\n\n// ============================================================================\n// Indexing\n//\n// `array[i]`, zero-based.\n// ============================================================================\n\npub fn power_of_two(index: u8) u16 {\n return POWERS_OF_TWO[index];\n}\n\npub fn bias_for_width(index: u8) u8 {\n return EXPONENT_BIAS[index];\n}\n\n// ============================================================================\n// Tests\n// ============================================================================\n\ntest \"the power table matches its own definition\" {\n try std.testing.expectEqual(@as(u16, 1), power_of_two(0));\n try std.testing.expectEqual(@as(u16, 128), power_of_two(7));\n}\n\ntest \"bias table carries the four standard widths\" {\n try std.testing.expectEqual(@as(u8, 15), bias_for_width(0));\n try std.testing.expectEqual(@as(u8, 127), bias_for_width(3));\n}\n\ntest \"a private constant is still usable inside its own module\" {\n try std.testing.expectEqual(@as(u8, 4), scaled_width());\n}\n\ninvariant exported_width_is_a_power_of_two\n assert EXPORTED_WIDTH == 16\n";
+pub const Decl = struct {
+ kind: u8,
+ label: u8,
+ na: usize,
+ nb: usize,
+ sa: usize,
+ sb: usize,
+ is_module: bool,
+};
+pub const LineScan = struct {
+ depth: u32,
+ tail: u8,
+};
+pub const NameSpans = struct {
+ ma: [192]usize,
+ mb: [192]usize,
+ ia: [192]usize,
+};
+pub fn ex_space(c: u8) bool {
+ return ((c == ' ') or (c == '\t')) or (c == '\r');
+}
+pub fn ex_word(c: u8) bool {
+ if ((c >= '0') and (c <= '9')) {
+ return true;
+ }
+ if ((c >= 'a') and (c <= 'z')) {
+ return true;
+ }
+ if ((c >= 'A') and (c <= 'Z')) {
+ return true;
+ }
+ return c == '_';
+}
+pub fn skip_space(src: []const u8, i: usize, b: usize) usize {
+ var k: usize = i;
+ _ = &k;
+ while ((k < b) and ex_space(src[k])) {
+ k += 1;
+ }
+ return k;
+}
+pub fn word_end(src: []const u8, i: usize, b: usize) usize {
+ var k: usize = i;
+ _ = &k;
+ while ((k < b) and ex_word(src[k])) {
+ k += 1;
+ }
+ return k;
+}
+pub fn token_end(src: []const u8, i: usize, b: usize) usize {
+ var k: usize = i;
+ _ = &k;
+ while ((((k < b) and (ex_space(src[k]) == false)) and (src[k] != ';')) and (src[k] != '{')) {
+ k += 1;
+ }
+ return k;
+}
+pub fn line_end_at(src: []const u8, a: usize) usize {
+ var k: usize = a;
+ _ = &k;
+ while ((k < src.len) and (src[k] != '\n')) {
+ k += 1;
+ }
+ return k;
+}
+pub fn count_lines(src: []const u8) u32 {
+ var c: u32 = 0;
+ _ = &c;
+ var k: usize = 0;
+ _ = &k;
+ while (k < src.len) {
+ if (src[k] == '\n') {
+ c += 1;
+ }
+ k += 1;
+ }
+ if ((src.len > 0) and (src[src.len - 1] != '\n')) {
+ c += 1;
+ }
+ return c;
+}
+pub fn skip_lit(src: []const u8, b: usize, i: usize) usize {
+ const c: u8 = src[i];
+ if (c == '"') {
+ var j: usize = i + 1;
+ _ = &j;
+ while ((j < b) and (src[j] != '"')) {
+ if (src[j] == '\\') {
+ j += 1;
+ }
+ j += 1;
+ }
+ if (j >= b) {
+ return b;
+ }
+ return j + 1;
+ }
+ if (c == '\'') {
+ if ((((i + 2) < b) and (src[i + 1] != '\\')) and (src[i + 2] == '\'')) {
+ return i + 3;
+ }
+ if ((((i + 3) < b) and (src[i + 1] == '\\')) and (src[i + 3] == '\'')) {
+ return i + 4;
+ }
+ }
+ return i;
+}
+pub fn closing_quote(src: []const u8, i: usize, b: usize) usize {
+ var j: usize = i;
+ _ = &j;
+ while ((j < b) and (src[j] != '"')) {
+ if (src[j] == '\\') {
+ j += 1;
+ }
+ j += 1;
+ }
+ if (j > b) {
+ return b;
+ }
+ return j;
+}
+pub fn scan_line(src: []const u8, a: usize, b: usize, depth_in: u32) LineScan {
+ var depth: u32 = depth_in;
+ _ = &depth;
+ var tail: u8 = 0;
+ _ = &tail;
+ var i: usize = skip_space(src, a, b);
+ _ = &i;
+ if ((i < b) and (src[i] == ';')) {
+ return LineScan{ .depth = depth, .tail = 0 };
+ }
+ var stop: bool = false;
+ _ = &stop;
+ while ((i < b) and (stop == false)) {
+ const c: u8 = src[i];
+ if (((c == '/') and ((i + 1) < b)) and (src[i + 1] == '/')) {
+ stop = true;
+ } else {
+ const j: usize = skip_lit(src, b, i);
+ if (j != i) {
+ tail = '"';
+ i = j;
+ } else {
+ if (c == '{') {
+ depth += 1;
+ }
+ if (c == '}') {
+ if (depth > 0) {
+ depth -= 1;
+ }
+ }
+ if (ex_space(c) == false) {
+ tail = c;
+ }
+ i += 1;
+ }
+ }
+ }
+ return LineScan{ .depth = depth, .tail = tail };
+}
+pub fn opaque_keyword(kw: []const u8) bool {
+ return ((((std.mem.eql(u8, kw, "struct")) or (std.mem.eql(u8, kw, "enum"))) or (std.mem.eql(u8, kw, "var"))) or (std.mem.eql(u8, kw, "spec"))) or (std.mem.eql(u8, kw, "endmodule"));
+}
+pub fn classify(src: []const u8, a: usize, b: usize) Decl {
+ const @"i0": usize = skip_space(src, a, b);
+ const j0: usize = word_end(src, @"i0", b);
+ var i: usize = @"i0";
+ _ = &i;
+ var j: usize = j0;
+ _ = &j;
+ var kw: []const u8 = src[@"i0" .. j0];
+ _ = &kw;
+ if (std.mem.eql(u8, kw, "pub")) {
+ const @"i1": usize = skip_space(src, j0, b);
+ const j1: usize = word_end(src, @"i1", b);
+ if (j1 > @"i1") {
+ i = @"i1";
+ j = j1;
+ kw = src[@"i1" .. j1];
+ }
+ }
+ var d: Decl = Decl{ .kind = NODE_OPAQUE, .label = LABEL_UNKNOWN, .na = i, .nb = j, .sa = i, .sb = j, .is_module = false };
+ _ = &d;
+ if (j == i) {
+ d.nb = i + 1;
+ d.sb = i + 1;
+ return d;
+ }
+ const k: usize = skip_space(src, j, b);
+ if (std.mem.eql(u8, kw, "module")) {
+ const e: usize = token_end(src, k, b);
+ if (e == k) {
+ return d;
+ }
+ return Decl{ .kind = NODE_MODULE, .label = LABEL_EXACT, .na = k, .nb = e, .sa = k, .sb = e, .is_module = true };
+ }
+ if (std.mem.eql(u8, kw, "use")) {
+ const e: usize = token_end(src, k, b);
+ if (e == k) {
+ return d;
+ }
+ var s: usize = e;
+ _ = &s;
+ while ((s > k) and (src[s - 1] != ':')) {
+ s -= 1;
+ }
+ return Decl{ .kind = NODE_USE, .label = LABEL_EXACT, .na = k, .nb = e, .sa = s, .sb = e, .is_module = false };
+ }
+ if ((std.mem.eql(u8, kw, "const")) or (std.mem.eql(u8, kw, "fn"))) {
+ const e: usize = word_end(src, k, b);
+ if (e == k) {
+ return d;
+ }
+ var kind: u8 = NODE_CONST;
+ _ = &kind;
+ if (std.mem.eql(u8, kw, "fn")) {
+ kind = NODE_FN;
+ }
+ return Decl{ .kind = kind, .label = LABEL_EXACT, .na = k, .nb = e, .sa = k, .sb = e, .is_module = false };
+ }
+ if (((std.mem.eql(u8, kw, "test")) or (std.mem.eql(u8, kw, "invariant"))) or (std.mem.eql(u8, kw, "bench"))) {
+ var kind: u8 = NODE_TEST;
+ _ = &kind;
+ if (std.mem.eql(u8, kw, "invariant")) {
+ kind = NODE_INVARIANT;
+ }
+ if (std.mem.eql(u8, kw, "bench")) {
+ kind = NODE_BENCH;
+ }
+ if ((k < b) and (src[k] == '"')) {
+ const q: usize = closing_quote(src, k + 1, b);
+ if (q == (k + 1)) {
+ return d;
+ }
+ return Decl{ .kind = kind, .label = LABEL_EXACT, .na = k + 1, .nb = q, .sa = k + 1, .sb = q, .is_module = false };
+ }
+ const e: usize = word_end(src, k, b);
+ if (e == k) {
+ return d;
+ }
+ return Decl{ .kind = kind, .label = LABEL_EXACT, .na = k, .nb = e, .sa = k, .sb = e, .is_module = false };
+ }
+ if (opaque_keyword(kw)) {
+ const e: usize = word_end(src, k, b);
+ if (e == k) {
+ return Decl{ .kind = NODE_OPAQUE, .label = LABEL_EXACT, .na = i, .nb = j, .sa = i, .sb = j, .is_module = false };
+ }
+ return Decl{ .kind = NODE_OPAQUE, .label = LABEL_EXACT, .na = k, .nb = e, .sa = k, .sb = e, .is_module = false };
+ }
+ return d;
+}
+pub fn edge_seen(g: SpecGraph, ek: u8, f: u32, t: u32) bool {
+ var j: u32 = 0;
+ _ = &j;
+ while ((j < g.edge_count) and (j < MAX_EDGES)) {
+ const m: usize = @as(usize, @intCast(j));
+ if (((g.edge_kind[m] == ek) and (@as(u32, @intCast(g.edge_from[m])) == f)) and (@as(u32, @intCast(g.edge_to[m])) == t)) {
+ return true;
+ }
+ j += 1;
+ }
+ return false;
+}
+pub fn token_edges(g0: SpecGraph, src: []const u8, sp: NameSpans, k: u32, i: usize, e: usize, b: usize, line: u32) SpecGraph {
+ var g: SpecGraph = g0;
+ _ = &g;
+ const tok: []const u8 = src[i .. e];
+ const ks: usize = @as(usize, @intCast(k));
+ var t: u32 = 1;
+ _ = &t;
+ var found: bool = false;
+ _ = &found;
+ while ((t < g.node_count) and (found == false)) {
+ const ts: usize = @as(usize, @intCast(t));
+ const tk: u8 = g.node_kind[ts];
+ if (((tk == NODE_FN) or (tk == NODE_CONST)) or (tk == NODE_USE)) {
+ const cand: []const u8 = src[sp.ma[ts] .. sp.mb[ts]];
+ if (std.mem.eql(u8, tok, cand)) {
+ found = true;
+ const own: bool = ((t == k) and (i >= sp.ma[ks])) and (i < sp.mb[ks]);
+ if (own == false) {
+ var ek: u8 = EDGE_USES;
+ _ = &ek;
+ var emit: bool = true;
+ _ = &emit;
+ if (tk == NODE_FN) {
+ ek = EDGE_CALLS;
+ const p: usize = skip_space(src, e, b);
+ emit = (p < b) and (src[p] == '(');
+ }
+ if ((tk == NODE_CONST) and (g.node_kind[ks] == NODE_INVARIANT)) {
+ ek = EDGE_CONSTRAINS;
+ }
+ if (emit and (edge_seen(g, ek, k, t) == false)) {
+ g = with_edge(g, ek, @as(u8, @truncate(k)), @as(u8, @truncate(t)), LABEL_EXACT, line);
+ }
+ }
+ }
+ }
+ t += 1;
+ }
+ return g;
+}
+pub fn line_edges(g0: SpecGraph, src: []const u8, sp: NameSpans, k: u32, p: usize, b: usize, line: u32) SpecGraph {
+ var g: SpecGraph = g0;
+ _ = &g;
+ var i: usize = skip_space(src, p, b);
+ _ = &i;
+ if ((i < b) and (src[i] == ';')) {
+ return g;
+ }
+ while (i < b) {
+ const c: u8 = src[i];
+ if (((c == '/') and ((i + 1) < b)) and (src[i + 1] == '/')) {
+ i = b;
+ } else {
+ const j: usize = skip_lit(src, b, i);
+ if (j != i) {
+ i = j;
+ } else if (ex_word(c)) {
+ const e: usize = word_end(src, i, b);
+ if ((c < '0') or (c > '9')) {
+ g = token_edges(g, src, sp, k, i, e, b, line);
+ }
+ i = e;
+ } else {
+ i += 1;
+ }
+ }
+ }
+ return g;
+}
+pub fn body_edges(g0: SpecGraph, src: []const u8, sp: NameSpans, k: u32) SpecGraph {
+ var g: SpecGraph = g0;
+ _ = &g;
+ const ks: usize = @as(usize, @intCast(k));
+ const last: u32 = g.line_end[ks];
+ var line: u32 = g.line_start[ks];
+ _ = &line;
+ var p: usize = sp.ia[ks];
+ _ = &p;
+ while ((line <= last) and (p < src.len)) {
+ const b: usize = line_end_at(src, p);
+ g = line_edges(g, src, sp, k, p, b, line);
+ p = b + 1;
+ line += 1;
+ }
+ return g;
+}
+pub fn read_edges(g0: SpecGraph, src: []const u8, sp: NameSpans) SpecGraph {
+ var g: SpecGraph = g0;
+ _ = &g;
+ var k: u32 = 1;
+ _ = &k;
+ if (g.node_label[0] == LABEL_EXACT) {
+ while (k < g.node_count) {
+ if (g.node_kind[@as(usize, @intCast(k))] == NODE_USE) {
+ g = with_edge(g, EDGE_USES, 0, @as(u8, @truncate(k)), LABEL_EXACT, g.line_start[@as(usize, @intCast(k))]);
+ }
+ k += 1;
+ }
+ }
+ k = 1;
+ while (k < g.node_count) {
+ const kind: u8 = g.node_kind[@as(usize, @intCast(k))];
+ if ((((kind == NODE_FN) or (kind == NODE_TEST)) or (kind == NODE_INVARIANT)) or (kind == NODE_BENCH)) {
+ g = body_edges(g, src, sp, k);
+ }
+ k += 1;
+ }
+ return g;
+}
+pub fn export_graph(path: []const u8, src: []const u8) SpecGraph {
+ const n: usize = src.len;
+ var g: SpecGraph = blank_graph();
+ _ = &g;
+ g.spec_path = path;
+ g.line_count = count_lines(src);
+ g = with_node(g, NODE_MODULE, LABEL_UNKNOWN, NO_MODULE_NAME, 1, g.line_count);
+ var sp: NameSpans = NameSpans{ .ma = .{ 0 } ** 192, .mb = .{ 0 } ** 192, .ia = .{ 0 } ** 192 };
+ _ = &sp;
+ var pos: usize = 0;
+ _ = &pos;
+ var line: u32 = 0;
+ _ = &line;
+ var depth: u32 = 0;
+ _ = &depth;
+ var live: bool = false;
+ _ = &live;
+ var closed: bool = true;
+ _ = &closed;
+ var cur: u32 = 0;
+ _ = &cur;
+ var mod_seen: bool = false;
+ _ = &mod_seen;
+ while (pos < n) {
+ const b: usize = line_end_at(src, pos);
+ line += 1;
+ const sc: LineScan = scan_line(src, pos, b, depth);
+ if (depth > 0) {
+ if (live and (cur < MAX_NODES)) {
+ g.line_end[@as(usize, @intCast(cur))] = line;
+ }
+ depth = sc.depth;
+ if (depth == 0) {
+ closed = true;
+ }
+ } else if (sc.tail != 0) {
+ const indented: bool = (src[pos] == ' ') or (src[pos] == '\t');
+ const first: usize = skip_space(src, pos, b);
+ if ((live and (closed == false)) and indented) {
+ if (cur < MAX_NODES) {
+ g.line_end[@as(usize, @intCast(cur))] = line;
+ }
+ depth = sc.depth;
+ if ((depth == 0) and ((sc.tail == ';') or (sc.tail == '}'))) {
+ closed = true;
+ }
+ } else if (src[first] == '}') {
+ depth = 0;
+ } else {
+ const d: Decl = classify(src, pos, b);
+ if (d.is_module and (mod_seen == false)) {
+ mod_seen = true;
+ g.node_name[0] = src[d.na .. d.nb];
+ g.node_label[0] = LABEL_EXACT;
+ live = false;
+ depth = 0;
+ } else {
+ var kind: u8 = d.kind;
+ _ = &kind;
+ if (kind == NODE_MODULE) {
+ kind = NODE_OPAQUE;
+ }
+ cur = g.node_count;
+ g = with_node(g, kind, d.label, src[d.na .. d.nb], line, line);
+ if (cur < MAX_NODES) {
+ sp.ma[@as(usize, @intCast(cur))] = d.sa;
+ sp.mb[@as(usize, @intCast(cur))] = d.sb;
+ sp.ia[@as(usize, @intCast(cur))] = pos;
+ }
+ live = true;
+ depth = sc.depth;
+ closed = (depth == 0) and ((sc.tail == ';') or (sc.tail == '}'));
+ }
+ }
+ }
+ pos = b + 1;
+ }
+ if (g.node_count <= MAX_NODES) {
+ g = read_edges(g, src, sp);
+ }
+ return g;
+}
+pub fn export_verdict(path: []const u8, src: []const u8) u8 {
+ return validate_graph(export_graph(path, src));
+}
+pub fn node_matches(e: SpecGraph, f: SpecGraph, i: u32) bool {
+ const k: usize = @as(usize, @intCast(i));
+ if (e.node_kind[k] != f.node_kind[k]) {
+ return false;
+ }
+ if (e.node_label[k] != f.node_label[k]) {
+ return false;
+ }
+ const en: []const u8 = e.node_name[k];
+ const fn_: []const u8 = f.node_name[k];
+ if ((std.mem.eql(u8, en, fn_)) == false) {
+ return false;
+ }
+ if (e.line_start[k] != f.line_start[k]) {
+ return false;
+ }
+ return e.line_end[k] == f.line_end[k];
+}
+pub fn nodes_in_common(e: SpecGraph, f: SpecGraph) u32 {
+ var c: u32 = 0;
+ _ = &c;
+ var i: u32 = 0;
+ _ = &i;
+ while (((i < e.node_count) and (i < f.node_count)) and (i < MAX_NODES)) {
+ if (node_matches(e, f, i)) {
+ c += 1;
+ }
+ i += 1;
+ }
+ return c;
+}
+pub fn edge_in(g: SpecGraph, ek: u8, f: u8, t: u8, l: u8, line: u32) bool {
+ var j: u32 = 0;
+ _ = &j;
+ while ((j < g.edge_count) and (j < MAX_EDGES)) {
+ const m: usize = @as(usize, @intCast(j));
+ if (((((g.edge_kind[m] == ek) and (g.edge_from[m] == f)) and (g.edge_to[m] == t)) and (g.edge_label[m] == l)) and (g.edge_line[m] == line)) {
+ return true;
+ }
+ j += 1;
+ }
+ return false;
+}
+pub fn edges_shared(a: SpecGraph, b: SpecGraph) u32 {
+ var c: u32 = 0;
+ _ = &c;
+ var j: u32 = 0;
+ _ = &j;
+ while ((j < a.edge_count) and (j < MAX_EDGES)) {
+ const m: usize = @as(usize, @intCast(j));
+ if (edge_in(b, a.edge_kind[m], a.edge_from[m], a.edge_to[m], a.edge_label[m], a.edge_line[m])) {
+ c += 1;
+ }
+ j += 1;
+ }
+ return c;
+}
+test "line_counts_follow_the_newlines" {
+ if (!(count_lines("") == 0)) __t27_assert_fail("\n assertion failed:\n count_lines(\"\") = {any}\n", .{ count_lines("") });
+ if (!(count_lines("a") == 1)) __t27_assert_fail("\n assertion failed:\n count_lines(\"a\") = {any}\n", .{ count_lines("a") });
+ if (!(count_lines("a\n") == 1)) __t27_assert_fail("\n assertion failed:\n count_lines(\"a\\n\") = {any}\n", .{ count_lines("a\n") });
+ if (!(count_lines("a\nb") == 2)) __t27_assert_fail("\n assertion failed:\n count_lines(\"a\\nb\") = {any}\n", .{ count_lines("a\nb") });
+ if (!(count_lines("a\n\n") == 2)) __t27_assert_fail("\n assertion failed:\n count_lines(\"a\\n\\n\") = {any}\n", .{ count_lines("a\n\n") });
+ if (!(count_lines("\n") == 1)) __t27_assert_fail("\n assertion failed:\n count_lines(\"\\n\") = {any}\n", .{ count_lines("\n") });
+ if (!(count_lines("a\n") != 2)) __t27_assert_fail("\n assertion failed:\n count_lines(\"a\\n\") = {any}\n", .{ count_lines("a\n") });
+ if (!(count_lines("a\nb") != 1)) __t27_assert_fail("\n assertion failed:\n count_lines(\"a\\nb\") = {any}\n", .{ count_lines("a\nb") });
+ if (!(count_lines(TUTORIAL_08_SRC) == 93)) __t27_assert_fail("\n assertion failed:\n count_lines(TUTORIAL_08_SRC) = {any}\n", .{ count_lines(TUTORIAL_08_SRC) });
+}
+test "the_module_line_names_node_zero" {
+ const g: SpecGraph = export_graph("specs/x.t27", "module m_one;\n");
+ if (!(g.node_count == 1)) __t27_assert_fail("\n assertion failed:\n g.node_count = {any}\n", .{ g.node_count });
+ if (!(g.node_kind[0] == NODE_MODULE)) __t27_assert_fail("\n assertion failed:\n g.node_kind[0] = {any}\n NODE_MODULE = {any}\n", .{ g.node_kind[0], NODE_MODULE });
+ if (!(g.node_label[0] == LABEL_EXACT)) __t27_assert_fail("\n assertion failed:\n g.node_label[0] = {any}\n LABEL_EXACT = {any}\n", .{ g.node_label[0], LABEL_EXACT });
+ if (!(std.mem.eql(u8, g.node_name[0], "m_one"))) __t27_assert_fail("\n assertion failed:\n g.node_name[0] = {any}\n", .{ g.node_name[0] });
+ if (!(g.line_start[0] == 1)) __t27_assert_fail("\n assertion failed:\n g.line_start[0] = {any}\n", .{ g.line_start[0] });
+ if (!(g.line_end[0] == 1)) __t27_assert_fail("\n assertion failed:\n g.line_end[0] = {any}\n", .{ g.line_end[0] });
+ if (!(std.mem.eql(u8, g.spec_path, "specs/x.t27"))) __t27_assert_fail("\n assertion failed:\n g.spec_path = {any}\n", .{ g.spec_path });
+ if (!(validate_graph(g) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(g), GRAPH_OK });
+ if (!(std.mem.eql(u8, export_graph("specs/x.t27", "module a-b-c;\n").node_name[0], "a-b-c"))) __t27_assert_fail("\n assertion failed:\n export_graph(\"specs/x.t27\", \"module a-b-c;\\n\").node_name[0] = {any}\n", .{ export_graph("specs/x.t27", "module a-b-c;\n").node_name[0] });
+ const h: SpecGraph = export_graph("specs/x.t27", "const A : u8 = 1;\n");
+ if (!(h.node_label[0] == LABEL_UNKNOWN)) __t27_assert_fail("\n assertion failed:\n h.node_label[0] = {any}\n LABEL_UNKNOWN = {any}\n", .{ h.node_label[0], LABEL_UNKNOWN });
+ if (!(std.mem.eql(u8, h.node_name[0], NO_MODULE_NAME))) __t27_assert_fail("\n assertion failed:\n h.node_name[0] = {any}\n NO_MODULE_NAME = {any}\n", .{ h.node_name[0], NO_MODULE_NAME });
+ if (!((std.mem.eql(u8, h.node_name[0], "m_one")) == false)) __t27_assert_fail("\n assertion failed:\n std.mem.eql(u8, h.node_name[0], \"m_one\") = {any}\n", .{ std.mem.eql(u8, h.node_name[0], "m_one") });
+ if (!(h.node_count == 2)) __t27_assert_fail("\n assertion failed:\n h.node_count = {any}\n", .{ h.node_count });
+ if (!(validate_graph(h) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(h) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(h), GRAPH_OK });
+}
+test "every_declaration_kind_reads_as_its_node_kind" {
+ const src: []const u8 = "module m;\nuse p::q::Thing;\npub const A : u8 = 1;\nconst B : u8 = 2;\npub fn f() u8 { return A; }\nfn g() u8 { return B; }\ntest \"t one\" { assert f() == 1; }\ninvariant inv_one assert A == 1;\nbench bench_one { f(); }\n";
+ const g: SpecGraph = export_graph("specs/x.t27", src);
+ if (!(validate_graph(g) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(g), GRAPH_OK });
+ if (!(g.node_count == 9)) __t27_assert_fail("\n assertion failed:\n g.node_count = {any}\n", .{ g.node_count });
+ if (!(g.line_count == 9)) __t27_assert_fail("\n assertion failed:\n g.line_count = {any}\n", .{ g.line_count });
+ if (!(g.node_kind[0] == NODE_MODULE)) __t27_assert_fail("\n assertion failed:\n g.node_kind[0] = {any}\n NODE_MODULE = {any}\n", .{ g.node_kind[0], NODE_MODULE });
+ if (!(g.node_kind[1] == NODE_USE)) __t27_assert_fail("\n assertion failed:\n g.node_kind[1] = {any}\n NODE_USE = {any}\n", .{ g.node_kind[1], NODE_USE });
+ if (!(g.node_kind[2] == NODE_CONST)) __t27_assert_fail("\n assertion failed:\n g.node_kind[2] = {any}\n NODE_CONST = {any}\n", .{ g.node_kind[2], NODE_CONST });
+ if (!(g.node_kind[3] == NODE_CONST)) __t27_assert_fail("\n assertion failed:\n g.node_kind[3] = {any}\n NODE_CONST = {any}\n", .{ g.node_kind[3], NODE_CONST });
+ if (!(g.node_kind[4] == NODE_FN)) __t27_assert_fail("\n assertion failed:\n g.node_kind[4] = {any}\n NODE_FN = {any}\n", .{ g.node_kind[4], NODE_FN });
+ if (!(g.node_kind[5] == NODE_FN)) __t27_assert_fail("\n assertion failed:\n g.node_kind[5] = {any}\n NODE_FN = {any}\n", .{ g.node_kind[5], NODE_FN });
+ if (!(g.node_kind[6] == NODE_TEST)) __t27_assert_fail("\n assertion failed:\n g.node_kind[6] = {any}\n NODE_TEST = {any}\n", .{ g.node_kind[6], NODE_TEST });
+ if (!(g.node_kind[7] == NODE_INVARIANT)) __t27_assert_fail("\n assertion failed:\n g.node_kind[7] = {any}\n NODE_INVARIANT = {any}\n", .{ g.node_kind[7], NODE_INVARIANT });
+ if (!(g.node_kind[8] == NODE_BENCH)) __t27_assert_fail("\n assertion failed:\n g.node_kind[8] = {any}\n NODE_BENCH = {any}\n", .{ g.node_kind[8], NODE_BENCH });
+ if (!(std.mem.eql(u8, g.node_name[1], "p::q::Thing"))) __t27_assert_fail("\n assertion failed:\n g.node_name[1] = {any}\n", .{ g.node_name[1] });
+ if (!(std.mem.eql(u8, g.node_name[2], "A"))) __t27_assert_fail("\n assertion failed:\n g.node_name[2] = {any}\n", .{ g.node_name[2] });
+ if (!(std.mem.eql(u8, g.node_name[4], "f"))) __t27_assert_fail("\n assertion failed:\n g.node_name[4] = {any}\n", .{ g.node_name[4] });
+ if (!(std.mem.eql(u8, g.node_name[6], "t one"))) __t27_assert_fail("\n assertion failed:\n g.node_name[6] = {any}\n", .{ g.node_name[6] });
+ if (!(std.mem.eql(u8, g.node_name[7], "inv_one"))) __t27_assert_fail("\n assertion failed:\n g.node_name[7] = {any}\n", .{ g.node_name[7] });
+ if (!(std.mem.eql(u8, g.node_name[8], "bench_one"))) __t27_assert_fail("\n assertion failed:\n g.node_name[8] = {any}\n", .{ g.node_name[8] });
+ if (!((g.line_start[1] == 2) and (g.line_end[1] == 2))) __t27_assert_fail("\n assertion failed:\n g.line_start[1] = {any}\n g.line_end[1] = {any}\n", .{ g.line_start[1], g.line_end[1] });
+ if (!((g.line_start[4] == 5) and (g.line_end[4] == 5))) __t27_assert_fail("\n assertion failed:\n g.line_start[4] = {any}\n g.line_end[4] = {any}\n", .{ g.line_start[4], g.line_end[4] });
+ if (!((g.line_start[6] == 7) and (g.line_end[6] == 7))) __t27_assert_fail("\n assertion failed:\n g.line_start[6] = {any}\n g.line_end[6] = {any}\n", .{ g.line_start[6], g.line_end[6] });
+ if (!((g.line_start[8] == 9) and (g.line_end[8] == 9))) __t27_assert_fail("\n assertion failed:\n g.line_start[8] = {any}\n g.line_end[8] = {any}\n", .{ g.line_start[8], g.line_end[8] });
+ if (!(count_node_kind(g, NODE_CONST) == 2)) __t27_assert_fail("\n assertion failed:\n count_node_kind(g, NODE_CONST) = {any}\n", .{ count_node_kind(g, NODE_CONST) });
+ if (!(count_node_kind(g, NODE_FN) == 2)) __t27_assert_fail("\n assertion failed:\n count_node_kind(g, NODE_FN) = {any}\n", .{ count_node_kind(g, NODE_FN) });
+ if (!(count_node_label(g, LABEL_EXACT) == 9)) __t27_assert_fail("\n assertion failed:\n count_node_label(g, LABEL_EXACT) = {any}\n", .{ count_node_label(g, LABEL_EXACT) });
+ if (!(count_node_label(g, LABEL_UNKNOWN) == 0)) __t27_assert_fail("\n assertion failed:\n count_node_label(g, LABEL_UNKNOWN) = {any}\n", .{ count_node_label(g, LABEL_UNKNOWN) });
+ if (!(g.edge_count == 6)) __t27_assert_fail("\n assertion failed:\n g.edge_count = {any}\n", .{ g.edge_count });
+ if (!(has_edge(g, EDGE_USES, 0, 1))) @panic("assertion failed");
+ if (!(has_edge(g, EDGE_USES, 4, 2))) @panic("assertion failed");
+ if (!(has_edge(g, EDGE_USES, 5, 3))) @panic("assertion failed");
+ if (!(has_edge(g, EDGE_CALLS, 6, 4))) @panic("assertion failed");
+ if (!(has_edge(g, EDGE_CONSTRAINS, 7, 2))) @panic("assertion failed");
+ if (!(has_edge(g, EDGE_CALLS, 8, 4))) @panic("assertion failed");
+ if (!(has_edge(g, EDGE_USES, 7, 2) == false)) __t27_assert_fail("\n assertion failed:\n has_edge(g, EDGE_USES, 7, 2) = {any}\n", .{ has_edge(g, EDGE_USES, 7, 2) });
+ if (!(has_edge(g, EDGE_CALLS, 4, 6) == false)) __t27_assert_fail("\n assertion failed:\n has_edge(g, EDGE_CALLS, 4, 6) = {any}\n", .{ has_edge(g, EDGE_CALLS, 4, 6) });
+ if (!(has_edge(g, EDGE_USES, 5, 2) == false)) __t27_assert_fail("\n assertion failed:\n has_edge(g, EDGE_USES, 5, 2) = {any}\n", .{ has_edge(g, EDGE_USES, 5, 2) });
+}
+test "other_declarations_are_opaque" {
+ const src: []const u8 = "module m;\nstruct S {\n x: u8,\n}\nenum E { A, B }\nwhat is this;\n";
+ const g: SpecGraph = export_graph("specs/x.t27", src);
+ if (!(validate_graph(g) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(g), GRAPH_OK });
+ if (!(g.node_count == 4)) __t27_assert_fail("\n assertion failed:\n g.node_count = {any}\n", .{ g.node_count });
+ if (!(g.node_kind[1] == NODE_OPAQUE)) __t27_assert_fail("\n assertion failed:\n g.node_kind[1] = {any}\n NODE_OPAQUE = {any}\n", .{ g.node_kind[1], NODE_OPAQUE });
+ if (!(g.node_label[1] == LABEL_EXACT)) __t27_assert_fail("\n assertion failed:\n g.node_label[1] = {any}\n LABEL_EXACT = {any}\n", .{ g.node_label[1], LABEL_EXACT });
+ if (!(std.mem.eql(u8, g.node_name[1], "S"))) __t27_assert_fail("\n assertion failed:\n g.node_name[1] = {any}\n", .{ g.node_name[1] });
+ if (!((g.line_start[1] == 2) and (g.line_end[1] == 4))) __t27_assert_fail("\n assertion failed:\n g.line_start[1] = {any}\n g.line_end[1] = {any}\n", .{ g.line_start[1], g.line_end[1] });
+ if (!(g.node_kind[2] == NODE_OPAQUE)) __t27_assert_fail("\n assertion failed:\n g.node_kind[2] = {any}\n NODE_OPAQUE = {any}\n", .{ g.node_kind[2], NODE_OPAQUE });
+ if (!(g.node_label[2] == LABEL_EXACT)) __t27_assert_fail("\n assertion failed:\n g.node_label[2] = {any}\n LABEL_EXACT = {any}\n", .{ g.node_label[2], LABEL_EXACT });
+ if (!(std.mem.eql(u8, g.node_name[2], "E"))) __t27_assert_fail("\n assertion failed:\n g.node_name[2] = {any}\n", .{ g.node_name[2] });
+ if (!((g.line_start[2] == 5) and (g.line_end[2] == 5))) __t27_assert_fail("\n assertion failed:\n g.line_start[2] = {any}\n g.line_end[2] = {any}\n", .{ g.line_start[2], g.line_end[2] });
+ if (!(g.node_kind[3] == NODE_OPAQUE)) __t27_assert_fail("\n assertion failed:\n g.node_kind[3] = {any}\n NODE_OPAQUE = {any}\n", .{ g.node_kind[3], NODE_OPAQUE });
+ if (!(g.node_label[3] == LABEL_UNKNOWN)) __t27_assert_fail("\n assertion failed:\n g.node_label[3] = {any}\n LABEL_UNKNOWN = {any}\n", .{ g.node_label[3], LABEL_UNKNOWN });
+ if (!(std.mem.eql(u8, g.node_name[3], "what"))) __t27_assert_fail("\n assertion failed:\n g.node_name[3] = {any}\n", .{ g.node_name[3] });
+ if (!(g.node_label[3] != LABEL_EXACT)) __t27_assert_fail("\n assertion failed:\n g.node_label[3] = {any}\n LABEL_EXACT = {any}\n", .{ g.node_label[3], LABEL_EXACT });
+ if (!(count_node_label(g, LABEL_UNKNOWN) == 1)) __t27_assert_fail("\n assertion failed:\n count_node_label(g, LABEL_UNKNOWN) = {any}\n", .{ count_node_label(g, LABEL_UNKNOWN) });
+}
+test "a_declaration_with_no_readable_name_is_unknown" {
+ const nameless_fn: SpecGraph = export_graph("specs/x.t27", "module m;\nfn () u8 { return 1; }\n");
+ if (!(validate_graph(nameless_fn) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(nameless_fn) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(nameless_fn), GRAPH_OK });
+ if (!(nameless_fn.node_kind[1] == NODE_OPAQUE)) __t27_assert_fail("\n assertion failed:\n nameless_fn.node_kind[1] = {any}\n NODE_OPAQUE = {any}\n", .{ nameless_fn.node_kind[1], NODE_OPAQUE });
+ if (!(nameless_fn.node_label[1] == LABEL_UNKNOWN)) __t27_assert_fail("\n assertion failed:\n nameless_fn.node_label[1] = {any}\n LABEL_UNKNOWN = {any}\n", .{ nameless_fn.node_label[1], LABEL_UNKNOWN });
+ if (!(std.mem.eql(u8, nameless_fn.node_name[1], "fn"))) __t27_assert_fail("\n assertion failed:\n nameless_fn.node_name[1] = {any}\n", .{ nameless_fn.node_name[1] });
+ const nameless_const: SpecGraph = export_graph("specs/x.t27", "module m;\nconst : u8 = 1;\n");
+ if (!(nameless_const.node_kind[1] == NODE_OPAQUE)) __t27_assert_fail("\n assertion failed:\n nameless_const.node_kind[1] = {any}\n NODE_OPAQUE = {any}\n", .{ nameless_const.node_kind[1], NODE_OPAQUE });
+ if (!(nameless_const.node_label[1] == LABEL_UNKNOWN)) __t27_assert_fail("\n assertion failed:\n nameless_const.node_label[1] = {any}\n LABEL_UNKNOWN = {any}\n", .{ nameless_const.node_label[1], LABEL_UNKNOWN });
+ const nameless_test: SpecGraph = export_graph("specs/x.t27", "module m;\ntest {\n}\n");
+ if (!(nameless_test.node_label[1] == LABEL_UNKNOWN)) __t27_assert_fail("\n assertion failed:\n nameless_test.node_label[1] = {any}\n LABEL_UNKNOWN = {any}\n", .{ nameless_test.node_label[1], LABEL_UNKNOWN });
+ const empty_title: SpecGraph = export_graph("specs/x.t27", "module m;\ntest \"\" { }\n");
+ if (!(empty_title.node_label[1] == LABEL_UNKNOWN)) __t27_assert_fail("\n assertion failed:\n empty_title.node_label[1] = {any}\n LABEL_UNKNOWN = {any}\n", .{ empty_title.node_label[1], LABEL_UNKNOWN });
+ const pub_alone: SpecGraph = export_graph("specs/x.t27", "module m;\npub\n");
+ if (!(pub_alone.node_label[1] == LABEL_UNKNOWN)) __t27_assert_fail("\n assertion failed:\n pub_alone.node_label[1] = {any}\n LABEL_UNKNOWN = {any}\n", .{ pub_alone.node_label[1], LABEL_UNKNOWN });
+ if (!(std.mem.eql(u8, pub_alone.node_name[1], "pub"))) __t27_assert_fail("\n assertion failed:\n pub_alone.node_name[1] = {any}\n", .{ pub_alone.node_name[1] });
+ if (!(export_graph("specs/x.t27", "module m;\nfn a() u8 { return 1; }\n").node_kind[1] == NODE_FN)) __t27_assert_fail("\n assertion failed:\n export_graph(\"specs/x.t27\", \"module m;\\nfn a() u8 {{ return 1; }}\\n\").node_kind[1] = {any}\n NODE_FN = {any}\n", .{ export_graph("specs/x.t27", "module m;\nfn a() u8 { return 1; }\n").node_kind[1], NODE_FN });
+ if (!(export_graph("specs/x.t27", "module m;\nconst A : u8 = 1;\n").node_kind[1] == NODE_CONST)) __t27_assert_fail("\n assertion failed:\n export_graph(\"specs/x.t27\", \"module m;\\nconst A : u8 = 1;\\n\").node_kind[1] = {any}\n NODE_CONST = {any}\n", .{ export_graph("specs/x.t27", "module m;\nconst A : u8 = 1;\n").node_kind[1], NODE_CONST });
+ if (!(export_graph("specs/x.t27", "module m;\ntest \"t\" { }\n").node_kind[1] == NODE_TEST)) __t27_assert_fail("\n assertion failed:\n export_graph(\"specs/x.t27\", \"module m;\\ntest \\\"t\\\" {{ }}\\n\").node_kind[1] = {any}\n NODE_TEST = {any}\n", .{ export_graph("specs/x.t27", "module m;\ntest \"t\" { }\n").node_kind[1], NODE_TEST });
+}
+test "a_span_runs_to_the_matching_closing_brace" {
+ const src: []const u8 = "module m;\nfn f() u8 {\n if (x) {\n y();\n }\n return 1;\n}\nconst C : u8 = 1;\n";
+ const g: SpecGraph = export_graph("specs/x.t27", src);
+ if (!(validate_graph(g) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(g), GRAPH_OK });
+ if (!(g.node_count == 3)) __t27_assert_fail("\n assertion failed:\n g.node_count = {any}\n", .{ g.node_count });
+ if (!(g.line_start[1] == 2)) __t27_assert_fail("\n assertion failed:\n g.line_start[1] = {any}\n", .{ g.line_start[1] });
+ if (!(g.line_end[1] == 7)) __t27_assert_fail("\n assertion failed:\n g.line_end[1] = {any}\n", .{ g.line_end[1] });
+ if (!(g.line_end[1] != 5)) __t27_assert_fail("\n assertion failed:\n g.line_end[1] = {any}\n", .{ g.line_end[1] });
+ if (!(g.node_kind[2] == NODE_CONST)) __t27_assert_fail("\n assertion failed:\n g.node_kind[2] = {any}\n NODE_CONST = {any}\n", .{ g.node_kind[2], NODE_CONST });
+ if (!((g.line_start[2] == 8) and (g.line_end[2] == 8))) __t27_assert_fail("\n assertion failed:\n g.line_start[2] = {any}\n g.line_end[2] = {any}\n", .{ g.line_start[2], g.line_end[2] });
+ const open_body: SpecGraph = export_graph("specs/x.t27", "module m;\nfn f() u8 {\n return 1;\n");
+ if (!(open_body.line_end[1] == 3)) __t27_assert_fail("\n assertion failed:\n open_body.line_end[1] = {any}\n", .{ open_body.line_end[1] });
+ if (!(validate_graph(open_body) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(open_body) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(open_body), GRAPH_OK });
+ const one_line: SpecGraph = export_graph("specs/x.t27", "module m;\nfn f() u8 { return 1; }\nfn g() u8 { return 2; }\n");
+ if (!((one_line.line_start[1] == 2) and (one_line.line_end[1] == 2))) __t27_assert_fail("\n assertion failed:\n one_line.line_start[1] = {any}\n one_line.line_end[1] = {any}\n", .{ one_line.line_start[1], one_line.line_end[1] });
+ if (!((one_line.line_start[2] == 3) and (one_line.line_end[2] == 3))) __t27_assert_fail("\n assertion failed:\n one_line.line_start[2] = {any}\n one_line.line_end[2] = {any}\n", .{ one_line.line_start[2], one_line.line_end[2] });
+}
+test "braces_in_strings_comments_and_characters_do_not_count" {
+ const closing: []const u8 = "module m;\nfn f() u8 {\n let s = \"}\";\n // }\n let c = '}';\n ; }\n return 1;\n}\nconst C : u8 = 1;\n";
+ const g: SpecGraph = export_graph("specs/x.t27", closing);
+ if (!(validate_graph(g) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(g), GRAPH_OK });
+ if (!(g.node_count == 3)) __t27_assert_fail("\n assertion failed:\n g.node_count = {any}\n", .{ g.node_count });
+ if (!((g.line_start[1] == 2) and (g.line_end[1] == 8))) __t27_assert_fail("\n assertion failed:\n g.line_start[1] = {any}\n g.line_end[1] = {any}\n", .{ g.line_start[1], g.line_end[1] });
+ if (!((g.line_start[2] == 9) and (g.line_end[2] == 9))) __t27_assert_fail("\n assertion failed:\n g.line_start[2] = {any}\n g.line_end[2] = {any}\n", .{ g.line_start[2], g.line_end[2] });
+ const opening: []const u8 = "module m;\nfn f() u8 {\n let s = \"{\";\n // {\n let c = '{';\n ; {\n return 1;\n}\nconst C : u8 = 1;\n";
+ const h: SpecGraph = export_graph("specs/x.t27", opening);
+ if (!(h.node_count == 3)) __t27_assert_fail("\n assertion failed:\n h.node_count = {any}\n", .{ h.node_count });
+ if (!(h.line_end[1] == 8)) __t27_assert_fail("\n assertion failed:\n h.line_end[1] = {any}\n", .{ h.line_end[1] });
+ if (!(h.line_start[2] == 9)) __t27_assert_fail("\n assertion failed:\n h.line_start[2] = {any}\n", .{ h.line_start[2] });
+ const escaped: []const u8 = "module m;\nfn f() u8 {\n let s = \"a\\\"}\";\n}\nconst C : u8 = 1;\n";
+ const k: SpecGraph = export_graph("specs/x.t27", escaped);
+ if (!(k.node_count == 3)) __t27_assert_fail("\n assertion failed:\n k.node_count = {any}\n", .{ k.node_count });
+ if (!(k.line_end[1] == 4)) __t27_assert_fail("\n assertion failed:\n k.line_end[1] = {any}\n", .{ k.line_end[1] });
+ if (!(k.line_start[2] == 5)) __t27_assert_fail("\n assertion failed:\n k.line_start[2] = {any}\n", .{ k.line_start[2] });
+ const real: []const u8 = "module m;\nfn f() u8 {\n let s = 1;\n}\nconst C : u8 = 1;\n";
+ _ = u8; // dead after const-inlining
+ if (!(export_graph("specs/x.t27", real).line_end[1] == 4)) __t27_assert_fail("\n assertion failed:\n export_graph(\"specs/x.t27\", real).line_end[1] = {any}\n", .{ export_graph("specs/x.t27", real).line_end[1] });
+ if (!(export_graph("specs/x.t27", real).line_end[1] != 3)) __t27_assert_fail("\n assertion failed:\n export_graph(\"specs/x.t27\", real).line_end[1] = {any}\n", .{ export_graph("specs/x.t27", real).line_end[1] });
+}
+test "a_declaration_with_no_brace_ends_at_its_last_continuation_line" {
+ const src: []const u8 = "module m;\ninvariant big_one\n assert A == 1\n and B == 2\nconst A : u8 =\n 1;\nconst B : u8 = 2;\ninvariant after\nassert 1 == 1\n";
+ _ = u8; // dead after const-inlining
+ const g: SpecGraph = export_graph("specs/x.t27", src);
+ if (!(validate_graph(g) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(g), GRAPH_OK });
+ if (!(g.node_count == 6)) __t27_assert_fail("\n assertion failed:\n g.node_count = {any}\n", .{ g.node_count });
+ if (!(g.node_kind[1] == NODE_INVARIANT)) __t27_assert_fail("\n assertion failed:\n g.node_kind[1] = {any}\n NODE_INVARIANT = {any}\n", .{ g.node_kind[1], NODE_INVARIANT });
+ if (!((g.line_start[1] == 2) and (g.line_end[1] == 4))) __t27_assert_fail("\n assertion failed:\n g.line_start[1] = {any}\n g.line_end[1] = {any}\n", .{ g.line_start[1], g.line_end[1] });
+ if (!(g.node_kind[2] == NODE_CONST)) __t27_assert_fail("\n assertion failed:\n g.node_kind[2] = {any}\n NODE_CONST = {any}\n", .{ g.node_kind[2], NODE_CONST });
+ if (!((g.line_start[2] == 5) and (g.line_end[2] == 6))) __t27_assert_fail("\n assertion failed:\n g.line_start[2] = {any}\n g.line_end[2] = {any}\n", .{ g.line_start[2], g.line_end[2] });
+ if (!((g.line_start[3] == 7) and (g.line_end[3] == 7))) __t27_assert_fail("\n assertion failed:\n g.line_start[3] = {any}\n g.line_end[3] = {any}\n", .{ g.line_start[3], g.line_end[3] });
+ if (!(g.node_kind[4] == NODE_INVARIANT)) __t27_assert_fail("\n assertion failed:\n g.node_kind[4] = {any}\n NODE_INVARIANT = {any}\n", .{ g.node_kind[4], NODE_INVARIANT });
+ if (!(g.line_end[4] == 8)) __t27_assert_fail("\n assertion failed:\n g.line_end[4] = {any}\n", .{ g.line_end[4] });
+ if (!(g.node_kind[5] == NODE_OPAQUE)) __t27_assert_fail("\n assertion failed:\n g.node_kind[5] = {any}\n NODE_OPAQUE = {any}\n", .{ g.node_kind[5], NODE_OPAQUE });
+ if (!(g.node_label[5] == LABEL_UNKNOWN)) __t27_assert_fail("\n assertion failed:\n g.node_label[5] = {any}\n LABEL_UNKNOWN = {any}\n", .{ g.node_label[5], LABEL_UNKNOWN });
+ if (!(g.line_start[5] == 9)) __t27_assert_fail("\n assertion failed:\n g.line_start[5] = {any}\n", .{ g.line_start[5] });
+ if (!(g.edge_count == 2)) __t27_assert_fail("\n assertion failed:\n g.edge_count = {any}\n", .{ g.edge_count });
+ if (!(edge_in(g, EDGE_CONSTRAINS, 1, 2, LABEL_EXACT, 3))) @panic("assertion failed");
+ if (!(edge_in(g, EDGE_CONSTRAINS, 1, 3, LABEL_EXACT, 4))) @panic("assertion failed");
+}
+test "blank_lines_and_comments_between_declarations_belong_to_none" {
+ const src: []const u8 = "// header\n; prose\n\nmodule m;\n\n// about A\nconst A : u8 = 1;\n\n; about f\nfn f() u8 {\n\n return A;\n\n}\n\n// trailing\n";
+ _ = u8; // dead after const-inlining
+ const g: SpecGraph = export_graph("specs/x.t27", src);
+ if (!(validate_graph(g) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(g), GRAPH_OK });
+ if (!(g.node_count == 3)) __t27_assert_fail("\n assertion failed:\n g.node_count = {any}\n", .{ g.node_count });
+ if (!(g.line_count == 16)) __t27_assert_fail("\n assertion failed:\n g.line_count = {any}\n", .{ g.line_count });
+ if (!((g.line_start[1] == 7) and (g.line_end[1] == 7))) __t27_assert_fail("\n assertion failed:\n g.line_start[1] = {any}\n g.line_end[1] = {any}\n", .{ g.line_start[1], g.line_end[1] });
+ if (!((g.line_start[2] == 10) and (g.line_end[2] == 14))) __t27_assert_fail("\n assertion failed:\n g.line_start[2] = {any}\n g.line_end[2] = {any}\n", .{ g.line_start[2], g.line_end[2] });
+ if (!(g.line_end[2] != 16)) __t27_assert_fail("\n assertion failed:\n g.line_end[2] = {any}\n", .{ g.line_end[2] });
+ if (!(has_edge(g, EDGE_USES, 2, 1))) @panic("assertion failed");
+}
+test "a_block_form_module_still_exports_its_declarations" {
+ const src: []const u8 = "module m {\n const A : u8 = 1;\n fn f() u8 { return A; }\n}\n";
+ const g: SpecGraph = export_graph("specs/x.t27", src);
+ if (!(validate_graph(g) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(g), GRAPH_OK });
+ if (!(std.mem.eql(u8, g.node_name[0], "m"))) __t27_assert_fail("\n assertion failed:\n g.node_name[0] = {any}\n", .{ g.node_name[0] });
+ if (!(g.node_label[0] == LABEL_EXACT)) __t27_assert_fail("\n assertion failed:\n g.node_label[0] = {any}\n LABEL_EXACT = {any}\n", .{ g.node_label[0], LABEL_EXACT });
+ if (!(g.node_count == 3)) __t27_assert_fail("\n assertion failed:\n g.node_count = {any}\n", .{ g.node_count });
+ if (!(g.node_kind[1] == NODE_CONST)) __t27_assert_fail("\n assertion failed:\n g.node_kind[1] = {any}\n NODE_CONST = {any}\n", .{ g.node_kind[1], NODE_CONST });
+ if (!(g.line_start[1] == 2)) __t27_assert_fail("\n assertion failed:\n g.line_start[1] = {any}\n", .{ g.line_start[1] });
+ if (!(g.node_kind[2] == NODE_FN)) __t27_assert_fail("\n assertion failed:\n g.node_kind[2] = {any}\n NODE_FN = {any}\n", .{ g.node_kind[2], NODE_FN });
+ if (!(has_edge(g, EDGE_USES, 2, 1))) @panic("assertion failed");
+ if (!(g.node_count != 4)) __t27_assert_fail("\n assertion failed:\n g.node_count = {any}\n", .{ g.node_count });
+ if (!(g.line_end[2] == 3)) __t27_assert_fail("\n assertion failed:\n g.line_end[2] = {any}\n", .{ g.line_end[2] });
+}
+test "a_second_module_line_is_opaque_not_a_second_module" {
+ const g: SpecGraph = export_graph("specs/x.t27", "module a;\nmodule b;\n");
+ if (!(validate_graph(g) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(g), GRAPH_OK });
+ if (!(count_node_kind(g, NODE_MODULE) == 1)) __t27_assert_fail("\n assertion failed:\n count_node_kind(g, NODE_MODULE) = {any}\n", .{ count_node_kind(g, NODE_MODULE) });
+ if (!(std.mem.eql(u8, g.node_name[0], "a"))) __t27_assert_fail("\n assertion failed:\n g.node_name[0] = {any}\n", .{ g.node_name[0] });
+ if (!(g.node_kind[1] == NODE_OPAQUE)) __t27_assert_fail("\n assertion failed:\n g.node_kind[1] = {any}\n NODE_OPAQUE = {any}\n", .{ g.node_kind[1], NODE_OPAQUE });
+ if (!(g.node_label[1] == LABEL_EXACT)) __t27_assert_fail("\n assertion failed:\n g.node_label[1] = {any}\n LABEL_EXACT = {any}\n", .{ g.node_label[1], LABEL_EXACT });
+ if (!(std.mem.eql(u8, g.node_name[1], "b"))) __t27_assert_fail("\n assertion failed:\n g.node_name[1] = {any}\n", .{ g.node_name[1] });
+ if (!((std.mem.eql(u8, g.node_name[0], "b")) == false)) __t27_assert_fail("\n assertion failed:\n std.mem.eql(u8, g.node_name[0], \"b\") = {any}\n", .{ std.mem.eql(u8, g.node_name[0], "b") });
+}
+test "edges_follow_the_direction_rules_and_carry_their_line" {
+ const src: []const u8 = "module m;\nuse x::imp;\nconst K : u8 = 1;\nfn leaf() u8 { return K; }\nfn mid() u8 {\n let a = leaf();\n let b = leaf();\n return a + b + imp();\n}\nfn rec(n: u8) u8 { return rec(n); }\nfn plain() u8 { let v = leaf; return v; }\nfn quiet() u8 { let s = \"leaf() K imp()\"; return 0; } // leaf() K\n";
+ _ = u8; // dead after const-inlining
+ const g: SpecGraph = export_graph("specs/x.t27", src);
+ if (!(validate_graph(g) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(g), GRAPH_OK });
+ if (!(g.node_count == 8)) __t27_assert_fail("\n assertion failed:\n g.node_count = {any}\n", .{ g.node_count });
+ if (!(g.edge_count == 5)) __t27_assert_fail("\n assertion failed:\n g.edge_count = {any}\n", .{ g.edge_count });
+ if (!(edge_in(g, EDGE_USES, 0, 1, LABEL_EXACT, 2))) @panic("assertion failed");
+ if (!(edge_in(g, EDGE_USES, 3, 2, LABEL_EXACT, 4))) @panic("assertion failed");
+ if (!(edge_in(g, EDGE_CALLS, 4, 3, LABEL_EXACT, 6))) @panic("assertion failed");
+ if (!(edge_in(g, EDGE_CALLS, 4, 3, LABEL_EXACT, 7) == false)) __t27_assert_fail("\n assertion failed:\n edge_in(g, EDGE_CALLS, 4, 3, LABEL_EXACT, 7) = {any}\n", .{ edge_in(g, EDGE_CALLS, 4, 3, LABEL_EXACT, 7) });
+ if (!(count_edge_kind(g, EDGE_CALLS) == 2)) __t27_assert_fail("\n assertion failed:\n count_edge_kind(g, EDGE_CALLS) = {any}\n", .{ count_edge_kind(g, EDGE_CALLS) });
+ if (!(edge_in(g, EDGE_USES, 4, 1, LABEL_EXACT, 8))) @panic("assertion failed");
+ if (!(edge_in(g, EDGE_CALLS, 5, 5, LABEL_EXACT, 10))) @panic("assertion failed");
+ if (!(g.edge_line[4] == 10)) __t27_assert_fail("\n assertion failed:\n g.edge_line[4] = {any}\n", .{ g.edge_line[4] });
+ if (!(has_edge(g, EDGE_CALLS, 6, 3) == false)) __t27_assert_fail("\n assertion failed:\n has_edge(g, EDGE_CALLS, 6, 3) = {any}\n", .{ has_edge(g, EDGE_CALLS, 6, 3) });
+ if (!(has_edge(g, EDGE_USES, 7, 2) == false)) __t27_assert_fail("\n assertion failed:\n has_edge(g, EDGE_USES, 7, 2) = {any}\n", .{ has_edge(g, EDGE_USES, 7, 2) });
+ if (!(has_edge(g, EDGE_USES, 7, 1) == false)) __t27_assert_fail("\n assertion failed:\n has_edge(g, EDGE_USES, 7, 1) = {any}\n", .{ has_edge(g, EDGE_USES, 7, 1) });
+ if (!(has_edge(g, EDGE_CALLS, 7, 3) == false)) __t27_assert_fail("\n assertion failed:\n has_edge(g, EDGE_CALLS, 7, 3) = {any}\n", .{ has_edge(g, EDGE_CALLS, 7, 3) });
+ if (!(has_edge(g, EDGE_USES, 4, 3) == false)) __t27_assert_fail("\n assertion failed:\n has_edge(g, EDGE_USES, 4, 3) = {any}\n", .{ has_edge(g, EDGE_USES, 4, 3) });
+ if (!(has_edge(g, EDGE_CALLS, 3, 3) == false)) __t27_assert_fail("\n assertion failed:\n has_edge(g, EDGE_CALLS, 3, 3) = {any}\n", .{ has_edge(g, EDGE_CALLS, 3, 3) });
+}
+test "a_test_that_calls_a_fn_calls_it_and_the_scanner_does_not_read_it_backwards" {
+ const src: []const u8 = "module m;\nfn f() u8 { return 1; }\ntest \"calls f\" {\n assert f() == 1;\n assert f() == 1;\n}\n";
+ _ = u8; // dead after const-inlining
+ const g: SpecGraph = export_graph("specs/x.t27", src);
+ if (!(validate_graph(g) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(g), GRAPH_OK });
+ if (!(g.edge_count == 1)) __t27_assert_fail("\n assertion failed:\n g.edge_count = {any}\n", .{ g.edge_count });
+ if (!(edge_in(g, EDGE_CALLS, 2, 1, LABEL_EXACT, 4))) @panic("assertion failed");
+ if (!(count_edge_kind(g, EDGE_TESTED_BY) == 0)) __t27_assert_fail("\n assertion failed:\n count_edge_kind(g, EDGE_TESTED_BY) = {any}\n", .{ count_edge_kind(g, EDGE_TESTED_BY) });
+ if (!(has_edge(g, EDGE_TESTED_BY, 1, 2) == false)) __t27_assert_fail("\n assertion failed:\n has_edge(g, EDGE_TESTED_BY, 1, 2) = {any}\n", .{ has_edge(g, EDGE_TESTED_BY, 1, 2) });
+}
+test "a_name_in_a_non_body_position_makes_no_edge" {
+ const g: SpecGraph = export_graph("specs/x.t27", "module m;\nuse a::K;\nconst K : u8 = 1;\nconst L : u8 = K;\n");
+ if (!(validate_graph(g) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(g), GRAPH_OK });
+ if (!(g.edge_count == 1)) __t27_assert_fail("\n assertion failed:\n g.edge_count = {any}\n", .{ g.edge_count });
+ if (!(has_edge(g, EDGE_USES, 0, 1))) @panic("assertion failed");
+ const h: SpecGraph = export_graph("specs/x.t27", "module m;\nconst K : u8 = 1;\nfn f() u8 { return K; }\n");
+ if (!(has_edge(h, EDGE_USES, 2, 1))) @panic("assertion failed");
+ const n: SpecGraph = export_graph("specs/x.t27", "use a::K;\n");
+ if (!(n.edge_count == 0)) __t27_assert_fail("\n assertion failed:\n n.edge_count = {any}\n", .{ n.edge_count });
+ if (!(validate_graph(n) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(n) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(n), GRAPH_OK });
+ const y: SpecGraph = export_graph("specs/x.t27", "module m;\nuse a::K;\n");
+ if (!(y.edge_count == 1)) __t27_assert_fail("\n assertion failed:\n y.edge_count = {any}\n", .{ y.edge_count });
+}
+test "the_scanner_never_emits_derived_or_suggested" {
+ const g: SpecGraph = export_graph("specs/tutorial/08_modules_and_arrays.t27", TUTORIAL_08_SRC);
+ if (!(count_node_label(g, LABEL_DERIVED) == 0)) __t27_assert_fail("\n assertion failed:\n count_node_label(g, LABEL_DERIVED) = {any}\n", .{ count_node_label(g, LABEL_DERIVED) });
+ if (!(count_node_label(g, LABEL_SUGGESTED) == 0)) __t27_assert_fail("\n assertion failed:\n count_node_label(g, LABEL_SUGGESTED) = {any}\n", .{ count_node_label(g, LABEL_SUGGESTED) });
+ if (!(count_edge_label(g, LABEL_DERIVED) == 0)) __t27_assert_fail("\n assertion failed:\n count_edge_label(g, LABEL_DERIVED) = {any}\n", .{ count_edge_label(g, LABEL_DERIVED) });
+ if (!(count_edge_label(g, LABEL_SUGGESTED) == 0)) __t27_assert_fail("\n assertion failed:\n count_edge_label(g, LABEL_SUGGESTED) = {any}\n", .{ count_edge_label(g, LABEL_SUGGESTED) });
+ if (!(count_edge_kind(g, EDGE_TESTED_BY) == 0)) __t27_assert_fail("\n assertion failed:\n count_edge_kind(g, EDGE_TESTED_BY) = {any}\n", .{ count_edge_kind(g, EDGE_TESTED_BY) });
+ if (!(count_edge_kind(g, EDGE_DATA_FLOW) == 0)) __t27_assert_fail("\n assertion failed:\n count_edge_kind(g, EDGE_DATA_FLOW) = {any}\n", .{ count_edge_kind(g, EDGE_DATA_FLOW) });
+ const f: SpecGraph = tutorial_08_graph();
+ if (!(count_edge_label(f, LABEL_DERIVED) == 7)) __t27_assert_fail("\n assertion failed:\n count_edge_label(f, LABEL_DERIVED) = {any}\n", .{ count_edge_label(f, LABEL_DERIVED) });
+ if (!(count_edge_kind(f, EDGE_TESTED_BY) == 3)) __t27_assert_fail("\n assertion failed:\n count_edge_kind(f, EDGE_TESTED_BY) = {any}\n", .{ count_edge_kind(f, EDGE_TESTED_BY) });
+}
+test "more_nodes_than_the_ir_holds_is_refused_not_truncated" {
+ const at_cap: []const u8 = "module m;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\n";
+ const a: SpecGraph = export_graph("specs/x.t27", at_cap);
+ if (!(a.node_count == MAX_NODES)) __t27_assert_fail("\n assertion failed:\n a.node_count = {any}\n MAX_NODES = {any}\n", .{ a.node_count, MAX_NODES });
+ if (!(validate_graph(a) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(a) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(a), GRAPH_OK });
+ const over: []const u8 = "module m;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\nconst a : u8 = 1;\n";
+ _ = u8; // dead after const-inlining
+ const g: SpecGraph = export_graph("specs/x.t27", over);
+ if (!(g.node_count == (MAX_NODES + 1))) __t27_assert_fail("\n assertion failed:\n g.node_count = {any}\n MAX_NODES + 1 = {any}\n", .{ g.node_count, MAX_NODES + 1 });
+ if (!(validate_graph(g) == ERR_TOO_MANY_NODES)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n ERR_TOO_MANY_NODES = {any}\n", .{ validate_graph(g), ERR_TOO_MANY_NODES });
+ if (!(export_verdict("specs/x.t27", over) == ERR_TOO_MANY_NODES)) __t27_assert_fail("\n assertion failed:\n export_verdict(\"specs/x.t27\", over) = {any}\n ERR_TOO_MANY_NODES = {any}\n", .{ export_verdict("specs/x.t27", over), ERR_TOO_MANY_NODES });
+ if (!(export_verdict("specs/x.t27", over) != GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n export_verdict(\"specs/x.t27\", over) = {any}\n GRAPH_OK = {any}\n", .{ export_verdict("specs/x.t27", over), GRAPH_OK });
+ if (!(export_verdict("specs/x.t27", over) != ERR_TOO_MANY_EDGES)) __t27_assert_fail("\n assertion failed:\n export_verdict(\"specs/x.t27\", over) = {any}\n ERR_TOO_MANY_EDGES = {any}\n", .{ export_verdict("specs/x.t27", over), ERR_TOO_MANY_EDGES });
+ if (!(export_verdict("specs/x.t27", at_cap) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n export_verdict(\"specs/x.t27\", at_cap) = {any}\n GRAPH_OK = {any}\n", .{ export_verdict("specs/x.t27", at_cap), GRAPH_OK });
+}
+test "more_edges_than_the_ir_holds_is_refused_not_truncated" {
+ const at_cap: []const u8 = "module m;\nfn a() u8 { return 1; }\nfn b() u8 { return 1; }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\n";
+ const a: SpecGraph = export_graph("specs/x.t27", at_cap);
+ if (!(a.node_count == 130)) __t27_assert_fail("\n assertion failed:\n a.node_count = {any}\n", .{ a.node_count });
+ if (!(a.edge_count == MAX_EDGES)) __t27_assert_fail("\n assertion failed:\n a.edge_count = {any}\n MAX_EDGES = {any}\n", .{ a.edge_count, MAX_EDGES });
+ if (!(validate_graph(a) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(a) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(a), GRAPH_OK });
+ const over: []const u8 = "module m;\nfn a() u8 { return 1; }\nfn b() u8 { return 1; }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\ntest \"t\" { a(); b(); }\n";
+ _ = u8; // dead after const-inlining
+ const g: SpecGraph = export_graph("specs/x.t27", over);
+ if (!(g.node_count == 131)) __t27_assert_fail("\n assertion failed:\n g.node_count = {any}\n", .{ g.node_count });
+ if (!(g.edge_count == (MAX_EDGES + 2))) __t27_assert_fail("\n assertion failed:\n g.edge_count = {any}\n MAX_EDGES + 2 = {any}\n", .{ g.edge_count, MAX_EDGES + 2 });
+ if (!(validate_graph(g) == ERR_TOO_MANY_EDGES)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n ERR_TOO_MANY_EDGES = {any}\n", .{ validate_graph(g), ERR_TOO_MANY_EDGES });
+ if (!(export_verdict("specs/x.t27", over) == ERR_TOO_MANY_EDGES)) __t27_assert_fail("\n assertion failed:\n export_verdict(\"specs/x.t27\", over) = {any}\n ERR_TOO_MANY_EDGES = {any}\n", .{ export_verdict("specs/x.t27", over), ERR_TOO_MANY_EDGES });
+ if (!(export_verdict("specs/x.t27", over) != ERR_TOO_MANY_NODES)) __t27_assert_fail("\n assertion failed:\n export_verdict(\"specs/x.t27\", over) = {any}\n ERR_TOO_MANY_NODES = {any}\n", .{ export_verdict("specs/x.t27", over), ERR_TOO_MANY_NODES });
+ if (!(export_verdict("specs/x.t27", at_cap) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n export_verdict(\"specs/x.t27\", at_cap) = {any}\n GRAPH_OK = {any}\n", .{ export_verdict("specs/x.t27", at_cap), GRAPH_OK });
+}
+test "an_empty_source_or_a_missing_path_is_refused" {
+ if (!(export_verdict("specs/x.t27", "") == ERR_SPEC_PATH)) __t27_assert_fail("\n assertion failed:\n export_verdict(\"specs/x.t27\", \"\") = {any}\n ERR_SPEC_PATH = {any}\n", .{ export_verdict("specs/x.t27", ""), ERR_SPEC_PATH });
+ if (!(export_verdict("", "module m;\n") == ERR_SPEC_PATH)) __t27_assert_fail("\n assertion failed:\n export_verdict(\"\", \"module m;\\n\") = {any}\n ERR_SPEC_PATH = {any}\n", .{ export_verdict("", "module m;\n"), ERR_SPEC_PATH });
+ if (!(export_verdict("specs/x.t27", "module m;\n") == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n export_verdict(\"specs/x.t27\", \"module m;\\n\") = {any}\n GRAPH_OK = {any}\n", .{ export_verdict("specs/x.t27", "module m;\n"), GRAPH_OK });
+ if (!(export_verdict("specs/x.t27", "\n") == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n export_verdict(\"specs/x.t27\", \"\\n\") = {any}\n GRAPH_OK = {any}\n", .{ export_verdict("specs/x.t27", "\n"), GRAPH_OK });
+ if (!(export_graph("specs/x.t27", "").node_count == 1)) __t27_assert_fail("\n assertion failed:\n export_graph(\"specs/x.t27\", \"\").node_count = {any}\n", .{ export_graph("specs/x.t27", "").node_count });
+}
+test "tutorial_08_export_is_a_valid_graph_of_the_lesson" {
+ const g: SpecGraph = export_graph("specs/tutorial/08_modules_and_arrays.t27", TUTORIAL_08_SRC);
+ if (!(validate_graph(g) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(g) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(g), GRAPH_OK });
+ if (!(std.mem.eql(u8, g.spec_path, "specs/tutorial/08_modules_and_arrays.t27"))) __t27_assert_fail("\n assertion failed:\n g.spec_path = {any}\n", .{ g.spec_path });
+ if (!(g.line_count == 93)) __t27_assert_fail("\n assertion failed:\n g.line_count = {any}\n", .{ g.line_count });
+ if (!(g.node_count == 14)) __t27_assert_fail("\n assertion failed:\n g.node_count = {any}\n", .{ g.node_count });
+ if (!(g.edge_count == 9)) __t27_assert_fail("\n assertion failed:\n g.edge_count = {any}\n", .{ g.edge_count });
+ if (!(std.mem.eql(u8, g.node_name[0], "tutorial-08-modules"))) __t27_assert_fail("\n assertion failed:\n g.node_name[0] = {any}\n", .{ g.node_name[0] });
+ if (!((g.line_start[0] == 1) and (g.line_end[0] == 93))) __t27_assert_fail("\n assertion failed:\n g.line_start[0] = {any}\n g.line_end[0] = {any}\n", .{ g.line_start[0], g.line_end[0] });
+ if (!(count_node_kind(g, NODE_USE) == 1)) __t27_assert_fail("\n assertion failed:\n count_node_kind(g, NODE_USE) = {any}\n", .{ count_node_kind(g, NODE_USE) });
+ if (!(count_node_kind(g, NODE_CONST) == 5)) __t27_assert_fail("\n assertion failed:\n count_node_kind(g, NODE_CONST) = {any}\n", .{ count_node_kind(g, NODE_CONST) });
+ if (!(count_node_kind(g, NODE_FN) == 3)) __t27_assert_fail("\n assertion failed:\n count_node_kind(g, NODE_FN) = {any}\n", .{ count_node_kind(g, NODE_FN) });
+ if (!(count_node_kind(g, NODE_TEST) == 3)) __t27_assert_fail("\n assertion failed:\n count_node_kind(g, NODE_TEST) = {any}\n", .{ count_node_kind(g, NODE_TEST) });
+ if (!(count_node_kind(g, NODE_INVARIANT) == 1)) __t27_assert_fail("\n assertion failed:\n count_node_kind(g, NODE_INVARIANT) = {any}\n", .{ count_node_kind(g, NODE_INVARIANT) });
+ if (!(count_node_kind(g, NODE_OPAQUE) == 0)) __t27_assert_fail("\n assertion failed:\n count_node_kind(g, NODE_OPAQUE) = {any}\n", .{ count_node_kind(g, NODE_OPAQUE) });
+ const cut: SpecGraph = export_graph("specs/tutorial/08_modules_and_arrays.t27", "module tutorial-08-modules;\nuse base::types;\n");
+ if (!(cut.node_count == 2)) __t27_assert_fail("\n assertion failed:\n cut.node_count = {any}\n", .{ cut.node_count });
+ if (!(cut.node_count != g.node_count)) __t27_assert_fail("\n assertion failed:\n cut.node_count = {any}\n g.node_count = {any}\n", .{ cut.node_count, g.node_count });
+}
+test "tutorial_08_exports_the_fixture_exactly_except_what_is_derived" {
+ const e: SpecGraph = export_graph("specs/tutorial/08_modules_and_arrays.t27", TUTORIAL_08_SRC);
+ const f: SpecGraph = tutorial_08_graph();
+ if (!(validate_graph(e) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(e) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(e), GRAPH_OK });
+ if (!(validate_graph(f) == GRAPH_OK)) __t27_assert_fail("\n assertion failed:\n validate_graph(f) = {any}\n GRAPH_OK = {any}\n", .{ validate_graph(f), GRAPH_OK });
+ if (!(std.mem.eql(u8, e.spec_path, f.spec_path))) __t27_assert_fail("\n assertion failed:\n e.spec_path = {any}\n f.spec_path = {any}\n", .{ e.spec_path, f.spec_path });
+ if (!(e.line_count == f.line_count)) __t27_assert_fail("\n assertion failed:\n e.line_count = {any}\n f.line_count = {any}\n", .{ e.line_count, f.line_count });
+ if (!(nodes_in_common(e, f) == 14)) __t27_assert_fail("\n assertion failed:\n nodes_in_common(e, f) = {any}\n", .{ nodes_in_common(e, f) });
+ if (!(node_matches(e, f, 0))) @panic("assertion failed");
+ if (!(node_matches(e, f, 4))) @panic("assertion failed");
+ if (!(node_matches(e, f, 10))) @panic("assertion failed");
+ if (!(node_matches(e, f, 13))) @panic("assertion failed");
+ if (!(f.node_count == 15)) __t27_assert_fail("\n assertion failed:\n f.node_count = {any}\n", .{ f.node_count });
+ if (!(e.node_count == 14)) __t27_assert_fail("\n assertion failed:\n e.node_count = {any}\n", .{ e.node_count });
+ if (!(f.node_label[14] == LABEL_UNKNOWN)) __t27_assert_fail("\n assertion failed:\n f.node_label[14] = {any}\n LABEL_UNKNOWN = {any}\n", .{ f.node_label[14], LABEL_UNKNOWN });
+ if (!(e.edge_count == 9)) __t27_assert_fail("\n assertion failed:\n e.edge_count = {any}\n", .{ e.edge_count });
+ if (!(edges_shared(e, f) == 9)) __t27_assert_fail("\n assertion failed:\n edges_shared(e, f) = {any}\n", .{ edges_shared(e, f) });
+ if (!(count_edge_label(f, LABEL_EXACT) == 9)) __t27_assert_fail("\n assertion failed:\n count_edge_label(f, LABEL_EXACT) = {any}\n", .{ count_edge_label(f, LABEL_EXACT) });
+ if (!(edges_shared(f, e) == 9)) __t27_assert_fail("\n assertion failed:\n edges_shared(f, e) = {any}\n", .{ edges_shared(f, e) });
+ if (!(f.edge_count == 16)) __t27_assert_fail("\n assertion failed:\n f.edge_count = {any}\n", .{ f.edge_count });
+ if (!((f.edge_count - edges_shared(f, e)) == 7)) __t27_assert_fail("\n assertion failed:\n f.edge_count - edges_shared(f, e) = {any}\n", .{ f.edge_count - edges_shared(f, e) });
+ if (!(count_edge_label(f, LABEL_DERIVED) == 7)) __t27_assert_fail("\n assertion failed:\n count_edge_label(f, LABEL_DERIVED) = {any}\n", .{ count_edge_label(f, LABEL_DERIVED) });
+ if (!(count_edge_kind(f, EDGE_DATA_FLOW) == 4)) __t27_assert_fail("\n assertion failed:\n count_edge_kind(f, EDGE_DATA_FLOW) = {any}\n", .{ count_edge_kind(f, EDGE_DATA_FLOW) });
+ if (!(count_edge_kind(f, EDGE_TESTED_BY) == 3)) __t27_assert_fail("\n assertion failed:\n count_edge_kind(f, EDGE_TESTED_BY) = {any}\n", .{ count_edge_kind(f, EDGE_TESTED_BY) });
+ if (!(count_edge_kind(e, EDGE_DATA_FLOW) == 0)) __t27_assert_fail("\n assertion failed:\n count_edge_kind(e, EDGE_DATA_FLOW) = {any}\n", .{ count_edge_kind(e, EDGE_DATA_FLOW) });
+ if (!(count_edge_kind(e, EDGE_TESTED_BY) == 0)) __t27_assert_fail("\n assertion failed:\n count_edge_kind(e, EDGE_TESTED_BY) = {any}\n", .{ count_edge_kind(e, EDGE_TESTED_BY) });
+ if (!(edge_in(e, EDGE_USES, 0, 1, LABEL_EXACT, 22))) @panic("assertion failed");
+ if (!(edge_in(e, EDGE_USES, 4, 2, LABEL_EXACT, 38))) @panic("assertion failed");
+ if (!(edge_in(e, EDGE_USES, 4, 3, LABEL_EXACT, 38))) @panic("assertion failed");
+ if (!(edge_in(e, EDGE_USES, 8, 6, LABEL_EXACT, 67))) @panic("assertion failed");
+ if (!(edge_in(e, EDGE_USES, 9, 7, LABEL_EXACT, 71))) @panic("assertion failed");
+ if (!(edge_in(e, EDGE_CALLS, 10, 8, LABEL_EXACT, 79))) @panic("assertion failed");
+ if (!(edge_in(e, EDGE_CALLS, 11, 9, LABEL_EXACT, 84))) @panic("assertion failed");
+ if (!(edge_in(e, EDGE_CALLS, 12, 4, LABEL_EXACT, 89))) @panic("assertion failed");
+ if (!(edge_in(e, EDGE_CONSTRAINS, 13, 2, LABEL_EXACT, 93))) @panic("assertion failed");
+ var moved: SpecGraph = tutorial_08_graph();
+ _ = &moved;
+ moved.line_end[4] = 40;
+ if (!(node_matches(e, moved, 4) == false)) __t27_assert_fail("\n assertion failed:\n node_matches(e, moved, 4) = {any}\n", .{ node_matches(e, moved, 4) });
+ if (!(nodes_in_common(e, moved) == 13)) __t27_assert_fail("\n assertion failed:\n nodes_in_common(e, moved) = {any}\n", .{ nodes_in_common(e, moved) });
+ var shifted: SpecGraph = tutorial_08_graph();
+ _ = &shifted;
+ shifted.edge_line[1] = 37;
+ if (!(edges_shared(e, shifted) == 8)) __t27_assert_fail("\n assertion failed:\n edges_shared(e, shifted) = {any}\n", .{ edges_shared(e, shifted) });
+ if (!(edges_shared(e, shifted) != edges_shared(e, f))) __t27_assert_fail("\n assertion failed:\n edges_shared(e, shifted) = {any}\n edges_shared(e, f) = {any}\n", .{ edges_shared(e, shifted), edges_shared(e, f) });
+}
+pub const EDGE_CALLS: u8 = 0;
+pub const EDGE_CONSTRAINS: u8 = 3;
+pub const EDGE_DATA_FLOW: u8 = 4;
+pub const EDGE_FROM_KINDS: [5]u32 = .{ 60, 61, 4, 16, 198 };
+pub const EDGE_KINDS: u8 = 5;
+pub const EDGE_TESTED_BY: u8 = 2;
+pub const EDGE_TO_KINDS: [5]u32 = .{ 4, 66, 56, 6, 188 };
+pub const EDGE_USES: u8 = 1;
+pub const ERR_DUPLICATE_EDGE: u8 = 18;
+pub const ERR_EDGE_DANGLING: u8 = 13;
+pub const ERR_EDGE_ENDPOINT_KIND: u8 = 14;
+pub const ERR_EDGE_KIND: u8 = 12;
+pub const ERR_EDGE_LABEL: u8 = 16;
+pub const ERR_EDGE_LINE: u8 = 17;
+pub const ERR_EDGE_UNKNOWN_NODE: u8 = 15;
+pub const ERR_GRAPH_VERSION: u8 = 1;
+pub const ERR_LABEL: u8 = 7;
+pub const ERR_MODULE: u8 = 5;
+pub const ERR_NODE_KIND: u8 = 6;
+pub const ERR_NODE_NAME: u8 = 8;
+pub const ERR_SPAN_MISSING: u8 = 11;
+pub const ERR_SPAN_ORDER: u8 = 9;
+pub const ERR_SPAN_OUTSIDE: u8 = 10;
+pub const ERR_SPEC_PATH: u8 = 2;
+pub const ERR_TOO_MANY_EDGES: u8 = 4;
+pub const ERR_TOO_MANY_NODES: u8 = 3;
+pub const GRAPH_IR_VERSION: u32 = 1;
+pub const GRAPH_OK: u8 = 0;
+pub const LABEL_DERIVED: u8 = 1;
+pub const LABEL_EXACT: u8 = 0;
+pub const LABEL_KINDS: u8 = 4;
+pub const LABEL_SUGGESTED: u8 = 2;
+pub const LABEL_UNKNOWN: u8 = 3;
+pub const MAX_EDGES: u32 = 254;
+pub const MAX_NODES: u32 = 192;
+pub const NODE_BENCH: u8 = 5;
+pub const NODE_CONST: u8 = 1;
+pub const NODE_FN: u8 = 2;
+pub const NODE_INVARIANT: u8 = 4;
+pub const NODE_KINDS: u8 = 8;
+pub const NODE_MODULE: u8 = 0;
+pub const NODE_OPAQUE: u8 = 7;
+pub const NODE_TEST: u8 = 3;
+pub const NODE_USE: u8 = 6;
+pub const NO_LINE: u32 = 0;
+pub const SpecGraph = struct {
+ ir_version: u32,
+ spec_path: []const u8,
+ line_count: u32,
+ node_count: u32,
+ node_kind: [192]u8,
+ node_label: [192]u8,
+ node_name: [192][]const u8,
+ line_start: [192]u32,
+ line_end: [192]u32,
+ edge_count: u32,
+ edge_kind: [254]u8,
+ edge_from: [254]u8,
+ edge_to: [254]u8,
+ edge_label: [254]u8,
+ edge_line: [254]u32,
+};
+pub fn blank_graph() SpecGraph {
+ return SpecGraph{ .ir_version = GRAPH_IR_VERSION, .spec_path = "", .line_count = 0, .node_count = 0, .node_kind = .{ 0 } ** 192, .node_label = .{ 0 } ** 192, .node_name = .{ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, .line_start = .{ 0 } ** 192, .line_end = .{ 0 } ** 192, .edge_count = 0, .edge_kind = .{ 0 } ** 254, .edge_from = .{ 0 } ** 254, .edge_to = .{ 0 } ** 254, .edge_label = .{ 0 } ** 254, .edge_line = .{ 0 } ** 254 };
+}
+pub fn count_edge_kind(g: SpecGraph, k: u8) u32 {
+ var c: u32 = 0;
+ _ = &c;
+ var j: u32 = 0;
+ _ = &j;
+ while (j < g.edge_count) {
+ if (g.edge_kind[@as(usize, @intCast(j))] == k) {
+ c = c + 1;
+ }
+ j = j + 1;
+ }
+ return c;
+}
+pub fn count_edge_label(g: SpecGraph, l: u8) u32 {
+ var c: u32 = 0;
+ _ = &c;
+ var j: u32 = 0;
+ _ = &j;
+ while (j < g.edge_count) {
+ if (g.edge_label[@as(usize, @intCast(j))] == l) {
+ c = c + 1;
+ }
+ j = j + 1;
+ }
+ return c;
+}
+pub fn count_node_kind(g: SpecGraph, k: u8) u32 {
+ var c: u32 = 0;
+ _ = &c;
+ var n: u32 = 0;
+ _ = &n;
+ while (n < g.node_count) {
+ if (g.node_kind[@as(usize, @intCast(n))] == k) {
+ c = c + 1;
+ }
+ n = n + 1;
+ }
+ return c;
+}
+pub fn count_node_label(g: SpecGraph, l: u8) u32 {
+ var c: u32 = 0;
+ _ = &c;
+ var n: u32 = 0;
+ _ = &n;
+ while (n < g.node_count) {
+ if (g.node_label[@as(usize, @intCast(n))] == l) {
+ c = c + 1;
+ }
+ n = n + 1;
+ }
+ return c;
+}
+pub fn has_edge(g: SpecGraph, k: u8, f: u32, t: u32) bool {
+ var j: u32 = 0;
+ _ = &j;
+ while (j < g.edge_count) {
+ const m: usize = @as(usize, @intCast(j));
+ if (((g.edge_kind[m] == k) and (@as(u32, @intCast(g.edge_from[m])) == f)) and (@as(u32, @intCast(g.edge_to[m])) == t)) {
+ return true;
+ }
+ j = j + 1;
+ }
+ return false;
+}
+pub fn kind_bit(k: u8) u32 {
+ return @as(u32, @intCast(1)) << @intCast(@as(u32, @intCast(k)));
+}
+pub fn span_holds(g: SpecGraph, n: u32, line: u32) bool {
+ const k: usize = @as(usize, @intCast(n));
+ if (g.line_start[k] == NO_LINE) {
+ return false;
+ }
+ return (g.line_start[k] <= line) and (line <= g.line_end[k]);
+}
+pub fn tutorial_08_graph() SpecGraph {
+ var g: SpecGraph = blank_graph();
+ _ = &g;
+ g.spec_path = "specs/tutorial/08_modules_and_arrays.t27";
+ g.line_count = 93;
+ g = with_node(g, NODE_MODULE, LABEL_EXACT, "tutorial-08-modules", 1, 93);
+ g = with_node(g, NODE_USE, LABEL_EXACT, "base::types", 22, 22);
+ g = with_node(g, NODE_CONST, LABEL_EXACT, "EXPORTED_WIDTH", 32, 32);
+ g = with_node(g, NODE_CONST, LABEL_EXACT, "INTERNAL_SCALE", 35, 35);
+ g = with_node(g, NODE_FN, LABEL_EXACT, "scaled_width", 37, 39);
+ g = with_node(g, NODE_CONST, LABEL_EXACT, "TRIT_VALUES", 54, 54);
+ g = with_node(g, NODE_CONST, LABEL_EXACT, "POWERS_OF_TWO", 56, 56);
+ g = with_node(g, NODE_CONST, LABEL_EXACT, "EXPONENT_BIAS", 58, 58);
+ g = with_node(g, NODE_FN, LABEL_EXACT, "power_of_two", 66, 68);
+ g = with_node(g, NODE_FN, LABEL_EXACT, "bias_for_width", 70, 72);
+ g = with_node(g, NODE_TEST, LABEL_EXACT, "the power table matches its own definition", 78, 81);
+ g = with_node(g, NODE_TEST, LABEL_EXACT, "bias table carries the four standard widths", 83, 86);
+ g = with_node(g, NODE_TEST, LABEL_EXACT, "a private constant is still usable inside its own module", 88, 90);
+ g = with_node(g, NODE_INVARIANT, LABEL_EXACT, "exported_width_is_a_power_of_two", 92, 93);
+ g = with_node(g, NODE_OPAQUE, LABEL_UNKNOWN, "std.testing", 79, 89);
+ g = with_edge(g, EDGE_USES, 0, 1, LABEL_EXACT, 22);
+ g = with_edge(g, EDGE_USES, 4, 2, LABEL_EXACT, 38);
+ g = with_edge(g, EDGE_USES, 4, 3, LABEL_EXACT, 38);
+ g = with_edge(g, EDGE_USES, 8, 6, LABEL_EXACT, 67);
+ g = with_edge(g, EDGE_USES, 9, 7, LABEL_EXACT, 71);
+ g = with_edge(g, EDGE_DATA_FLOW, 2, 4, LABEL_DERIVED, 38);
+ g = with_edge(g, EDGE_DATA_FLOW, 3, 4, LABEL_DERIVED, 38);
+ g = with_edge(g, EDGE_DATA_FLOW, 6, 8, LABEL_DERIVED, 67);
+ g = with_edge(g, EDGE_DATA_FLOW, 7, 9, LABEL_DERIVED, 71);
+ g = with_edge(g, EDGE_CALLS, 10, 8, LABEL_EXACT, 79);
+ g = with_edge(g, EDGE_CALLS, 11, 9, LABEL_EXACT, 84);
+ g = with_edge(g, EDGE_CALLS, 12, 4, LABEL_EXACT, 89);
+ g = with_edge(g, EDGE_TESTED_BY, 8, 10, LABEL_DERIVED, NO_LINE);
+ g = with_edge(g, EDGE_TESTED_BY, 9, 11, LABEL_DERIVED, NO_LINE);
+ g = with_edge(g, EDGE_TESTED_BY, 4, 12, LABEL_DERIVED, NO_LINE);
+ g = with_edge(g, EDGE_CONSTRAINS, 13, 2, LABEL_EXACT, 93);
+ return g;
+}
+pub fn valid_edge_kind(k: u8) bool {
+ return k < EDGE_KINDS;
+}
+pub fn valid_label(l: u8) bool {
+ return l < LABEL_KINDS;
+}
+pub fn valid_node_kind(k: u8) bool {
+ return k < NODE_KINDS;
+}
+pub fn validate_edge(g: SpecGraph, j: u32) u8 {
+ const k: usize = @as(usize, @intCast(j));
+ const ek: u8 = g.edge_kind[k];
+ if (valid_edge_kind(ek) == false) {
+ return ERR_EDGE_KIND;
+ }
+ const f: u32 = @as(u32, @intCast(g.edge_from[k]));
+ const t: u32 = @as(u32, @intCast(g.edge_to[k]));
+ if ((f >= g.node_count) or (t >= g.node_count)) {
+ return ERR_EDGE_DANGLING;
+ }
+ const fk: u8 = g.node_kind[@as(usize, @intCast(f))];
+ const tk: u8 = g.node_kind[@as(usize, @intCast(t))];
+ if ((EDGE_FROM_KINDS[@as(usize, @intCast(ek))] & kind_bit(fk)) == 0) {
+ return ERR_EDGE_ENDPOINT_KIND;
+ }
+ if ((EDGE_TO_KINDS[@as(usize, @intCast(ek))] & kind_bit(tk)) == 0) {
+ return ERR_EDGE_ENDPOINT_KIND;
+ }
+ const fl: u8 = g.node_label[@as(usize, @intCast(f))];
+ const tl: u8 = g.node_label[@as(usize, @intCast(t))];
+ if ((fl == LABEL_UNKNOWN) or (tl == LABEL_UNKNOWN)) {
+ return ERR_EDGE_UNKNOWN_NODE;
+ }
+ const el: u8 = g.edge_label[k];
+ if (el >= LABEL_UNKNOWN) {
+ return ERR_EDGE_LABEL;
+ }
+ if ((el < fl) or (el < tl)) {
+ return ERR_EDGE_LABEL;
+ }
+ const line: u32 = g.edge_line[k];
+ if (line == NO_LINE) {
+ if (el != LABEL_DERIVED) {
+ return ERR_EDGE_LINE;
+ }
+ } else if ((span_holds(g, f, line) == false) and (span_holds(g, t, line) == false)) {
+ return ERR_EDGE_LINE;
+ }
+ var i: u32 = 0;
+ _ = &i;
+ while (i < j) {
+ const m: usize = @as(usize, @intCast(i));
+ if (((g.edge_kind[m] == ek) and (g.edge_from[m] == g.edge_from[k])) and (g.edge_to[m] == g.edge_to[k])) {
+ return ERR_DUPLICATE_EDGE;
+ }
+ i = i + 1;
+ }
+ return GRAPH_OK;
+}
+pub fn validate_graph(g: SpecGraph) u8 {
+ const h: u8 = validate_graph_header(g);
+ if (h != GRAPH_OK) {
+ return h;
+ }
+ var n: u32 = 0;
+ _ = &n;
+ while (n < g.node_count) {
+ const v: u8 = validate_node(g, n);
+ if (v != GRAPH_OK) {
+ return v;
+ }
+ n = n + 1;
+ }
+ var j: u32 = 0;
+ _ = &j;
+ while (j < g.edge_count) {
+ const w: u8 = validate_edge(g, j);
+ if (w != GRAPH_OK) {
+ return w;
+ }
+ j = j + 1;
+ }
+ return GRAPH_OK;
+}
+pub fn validate_graph_header(g: SpecGraph) u8 {
+ if (g.ir_version != GRAPH_IR_VERSION) {
+ return ERR_GRAPH_VERSION;
+ }
+ if ((std.mem.eql(u8, g.spec_path, "")) or (g.line_count == 0)) {
+ return ERR_SPEC_PATH;
+ }
+ if (g.node_count > MAX_NODES) {
+ return ERR_TOO_MANY_NODES;
+ }
+ if (g.edge_count > MAX_EDGES) {
+ return ERR_TOO_MANY_EDGES;
+ }
+ if (g.node_count == 0) {
+ return ERR_MODULE;
+ }
+ if (g.node_kind[0] != NODE_MODULE) {
+ return ERR_MODULE;
+ }
+ if ((g.line_start[0] != 1) or (g.line_end[0] != g.line_count)) {
+ return ERR_MODULE;
+ }
+ return GRAPH_OK;
+}
+pub fn validate_node(g: SpecGraph, n: u32) u8 {
+ const k: usize = @as(usize, @intCast(n));
+ if (valid_node_kind(g.node_kind[k]) == false) {
+ return ERR_NODE_KIND;
+ }
+ if (valid_label(g.node_label[k]) == false) {
+ return ERR_LABEL;
+ }
+ if (std.mem.eql(u8, g.node_name[k], "")) {
+ return ERR_NODE_NAME;
+ }
+ const s: u32 = g.line_start[k];
+ const e: u32 = g.line_end[k];
+ if (s > e) {
+ return ERR_SPAN_ORDER;
+ }
+ if ((s == NO_LINE) and (e != NO_LINE)) {
+ return ERR_SPAN_ORDER;
+ }
+ if (e > g.line_count) {
+ return ERR_SPAN_OUTSIDE;
+ }
+ const l: u8 = g.node_label[k];
+ if ((s == NO_LINE) and ((l == LABEL_EXACT) or (l == LABEL_SUGGESTED))) {
+ return ERR_SPAN_MISSING;
+ }
+ if ((n > 0) and (g.node_kind[k] == NODE_MODULE)) {
+ return ERR_MODULE;
+ }
+ return GRAPH_OK;
+}
+pub fn with_edge(g: SpecGraph, kind: u8, f: u8, t: u8, label: u8, line: u32) SpecGraph {
+ var h: SpecGraph = g;
+ _ = &h;
+ if (h.edge_count < MAX_EDGES) {
+ const k: usize = @as(usize, @intCast(h.edge_count));
+ h.edge_kind[k] = kind;
+ h.edge_from[k] = f;
+ h.edge_to[k] = t;
+ h.edge_label[k] = label;
+ h.edge_line[k] = line;
+ }
+ h.edge_count = h.edge_count + 1;
+ return h;
+}
+pub fn with_node(g: SpecGraph, kind: u8, label: u8, name: []const u8, s: u32, e: u32) SpecGraph {
+ var h: SpecGraph = g;
+ _ = &h;
+ if (h.node_count < MAX_NODES) {
+ const k: usize = @as(usize, @intCast(h.node_count));
+ h.node_kind[k] = kind;
+ h.node_label[k] = label;
+ h.node_name[k] = name;
+ h.line_start[k] = s;
+ h.line_end[k] = e;
+ }
+ h.node_count = h.node_count + 1;
+ return h;
+}
diff --git a/wasm/spec_graph_outline.mjs b/wasm/spec_graph_outline.mjs
new file mode 100644
index 0000000..6cdd58b
--- /dev/null
+++ b/wasm/spec_graph_outline.mjs
@@ -0,0 +1,72 @@
+// Generated by `t27c gen-js` from spec_graph_outline.t27. Do not edit.
+//
+// Every name and every value here comes from that spec. Change the spec and
+// regenerate; an edit made here is lost the next time anyone builds.
+
+export const OUTLINE_TEXT = ["{}: {} lines, {} nodes, {} edges (cap {} nodes / {} edges); exact only, no derived edges", "nodes (kind name lines a-b label):", "edges (kind from -> to, line of first use):", "derived (the tested_by view of the exact calls edges from a test to a fn):", " {} {} {} lines {} {}", " {} {} {} -> {} {} line {}", " fn {} is tested by: {}", " fn {} is called by no test"];
+export const REFUSAL_TEXT = ["{}: refused, too many nodes: {} nodes found, the graph holds at most {}. No graph is drawn or truncated.", "{}: refused, too many edges: {} edges found, the graph holds at most {}. No graph is drawn or truncated.", "{}: refused, {} (verdict {})."];
+export const REFUSE_NODES = 0;
+export const REFUSE_EDGES = 1;
+export const REFUSE_OTHER = 2;
+// t27c gen-js: fn refusal_index was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn shows_in_outline was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn span_is_range was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn is_testing_kind was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn tested_by_count was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn tested_by_of was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn mark_count was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+export const EDGE_CALLS = 0;
+export const EDGE_CONSTRAINS = 3;
+export const EDGE_DATA_FLOW = 4;
+export const EDGE_TESTED_BY = 2;
+export const EDGE_USES = 1;
+export const ERR_MODULE = 5;
+export const ERR_TOO_MANY_EDGES = 4;
+export const ERR_TOO_MANY_NODES = 3;
+export const GRAPH_IR_VERSION = 1;
+export const GRAPH_OK = 0;
+export const LABEL_DERIVED = 1;
+export const LABEL_EXACT = 0;
+export const LABEL_SUGGESTED = 2;
+export const LABEL_UNKNOWN = 3;
+export const MAX_EDGES = 254;
+export const MAX_NODES = 192;
+export const NODE_BENCH = 5;
+export const NODE_CONST = 1;
+export const NODE_FN = 2;
+export const NODE_INVARIANT = 4;
+export const NODE_MODULE = 0;
+export const NODE_OPAQUE = 7;
+export const NODE_TEST = 3;
+export const NODE_USE = 6;
+export const NO_INDEX = 255;
+export const NO_LINE = 0;
+export const SpecGraph = Object.freeze({ __struct__: "SpecGraph", fields: [["ir_version", "u32"], ["spec_path", "str"], ["line_count", "u32"], ["node_count", "u32"], ["node_kind", "[192]u8"], ["node_label", "[192]u8"], ["node_name", "[192]str"], ["line_start", "[192]u32"], ["line_end", "[192]u32"], ["edge_count", "u32"], ["edge_kind", "[254]u8"], ["edge_from", "[254]u8"], ["edge_to", "[254]u8"], ["edge_label", "[254]u8"], ["edge_line", "[254]u32"]] });
+// t27c gen-js: fn blank_graph was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn tutorial_08_graph was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn with_edge was not emitted -- this backend lowers declarations, not bodies.
+// t27c gen-js: fn with_node was not emitted -- this backend lowers declarations, not bodies.
+
+// Declaration order, which the spec's own laws depend on.
+export const __STRUCT_ORDER__ = ["SpecGraph"];
+export const __DECL_ORDER__ = ["OUTLINE_TEXT", "REFUSAL_TEXT", "REFUSE_NODES", "REFUSE_EDGES", "REFUSE_OTHER", "EDGE_CALLS", "EDGE_CONSTRAINS", "EDGE_DATA_FLOW", "EDGE_TESTED_BY", "EDGE_USES", "ERR_MODULE", "ERR_TOO_MANY_EDGES", "ERR_TOO_MANY_NODES", "GRAPH_IR_VERSION", "GRAPH_OK", "LABEL_DERIVED", "LABEL_EXACT", "LABEL_SUGGESTED", "LABEL_UNKNOWN", "MAX_EDGES", "MAX_NODES", "NODE_BENCH", "NODE_CONST", "NODE_FN", "NODE_INVARIANT", "NODE_MODULE", "NODE_OPAQUE", "NODE_TEST", "NODE_USE", "NO_INDEX", "NO_LINE", "SpecGraph"];
+
+// What this spec holds and this backend did not print. Empty is the whole
+// story most of the time; an entry here is a promise the artifact does not
+// keep, and reading it is how a tool tells a partial module from a complete
+// one without parsing comments.
+//
+// A LIST, not a map keyed by name. `specs/numeric/formats.t27` declares four
+// separate consts called `result`, one per test block, and under an object
+// literal three of the four omissions vanished into the fourth -- a record of
+// what went missing that itself went missing. A spec is free to reuse a
+// name; this file is not free to lose the second one.
+export const __NOT_EMITTED__ = Object.freeze([]);
diff --git a/wasm/spec_graph_outline.zig b/wasm/spec_graph_outline.zig
new file mode 100644
index 0000000..284ff84
--- /dev/null
+++ b/wasm/spec_graph_outline.zig
@@ -0,0 +1,267 @@
+// Generated from t27 spec: t27f_spec_graph_outline (module name)
+// DO NOT EDIT — generated by t27c
+// phi^2 + 1/phi^2 = 3 | TRINITY
+
+const std = @import("std");
+fn __t27_assert_fail(comptime fmt: []const u8, args: anytype) noreturn {
+ if (@inComptime()) {
+ @compileError("assertion failed");
+ } else {
+ std.debug.print(fmt, args);
+ @panic("assertion failed");
+ }
+}
+fn assert_eq(a: anytype, b: anytype) void {
+ if (a != b) __t27_assert_fail("\n {any}\n != {any}\n", .{ a, b });
+}
+
+pub const OUTLINE_TEXT: [8][]const u8 = .{ "{}: {} lines, {} nodes, {} edges (cap {} nodes / {} edges); exact only, no derived edges", "nodes (kind name lines a-b label):", "edges (kind from -> to, line of first use):", "derived (the tested_by view of the exact calls edges from a test to a fn):", " {} {} {} lines {} {}", " {} {} {} -> {} {} line {}", " fn {} is tested by: {}", " fn {} is called by no test" };
+pub const REFUSAL_TEXT: [3][]const u8 = .{ "{}: refused, too many nodes: {} nodes found, the graph holds at most {}. No graph is drawn or truncated.", "{}: refused, too many edges: {} edges found, the graph holds at most {}. No graph is drawn or truncated.", "{}: refused, {} (verdict {})." };
+pub const REFUSE_NODES: u32 = 0;
+pub const REFUSE_EDGES: u32 = 1;
+pub const REFUSE_OTHER: u32 = 2;
+pub fn refusal_index(verdict: u8) u32 {
+ if (verdict == ERR_TOO_MANY_NODES) {
+ return REFUSE_NODES;
+ }
+ if (verdict == ERR_TOO_MANY_EDGES) {
+ return REFUSE_EDGES;
+ }
+ return REFUSE_OTHER;
+}
+pub fn shows_in_outline(label: u8) bool {
+ return label == LABEL_EXACT;
+}
+pub fn span_is_range(a: u32, b: u32) bool {
+ return b > a;
+}
+fn is_testing_kind(k: u8) bool {
+ return ((k == NODE_TEST) or (k == NODE_INVARIANT)) or (k == NODE_BENCH);
+}
+pub fn tested_by_count(g: SpecGraph, f: u32) u32 {
+ if (f >= g.node_count) {
+ return 0;
+ }
+ if (g.node_kind[@as(usize, @intCast(f))] != NODE_FN) {
+ return 0;
+ }
+ var c: u32 = 0;
+ _ = &c;
+ var j: u32 = 0;
+ _ = &j;
+ while (j < g.edge_count) {
+ const m: usize = @as(usize, @intCast(j));
+ if ((((g.edge_kind[m] == EDGE_CALLS) and (g.edge_label[m] == LABEL_EXACT)) and (@as(u32, @intCast(g.edge_to[m])) == f)) and is_testing_kind(g.node_kind[@as(usize, @intCast(g.edge_from[m]))])) {
+ c = c + 1;
+ }
+ j = j + 1;
+ }
+ return c;
+}
+pub fn tested_by_of(g: SpecGraph, f: u32, n: u32) u32 {
+ if (f >= g.node_count) {
+ return NO_INDEX;
+ }
+ if (g.node_kind[@as(usize, @intCast(f))] != NODE_FN) {
+ return NO_INDEX;
+ }
+ var seen: u32 = 0;
+ _ = &seen;
+ var j: u32 = 0;
+ _ = &j;
+ while (j < g.edge_count) {
+ const m: usize = @as(usize, @intCast(j));
+ if ((((g.edge_kind[m] == EDGE_CALLS) and (g.edge_label[m] == LABEL_EXACT)) and (@as(u32, @intCast(g.edge_to[m])) == f)) and is_testing_kind(g.node_kind[@as(usize, @intCast(g.edge_from[m]))])) {
+ if (seen == n) {
+ return @as(u32, @intCast(g.edge_from[m]));
+ }
+ seen = seen + 1;
+ }
+ j = j + 1;
+ }
+ return NO_INDEX;
+}
+pub fn mark_count(t: []const u8) u32 {
+ var c: u32 = 0;
+ _ = &c;
+ var k: usize = 0;
+ _ = &k;
+ while ((k + 1) < t.len) {
+ if ((t[k] == '{') and (t[k + 1] == '}')) {
+ c = c + 1;
+ k = k + 2;
+ } else {
+ k = k + 1;
+ }
+ }
+ return c;
+}
+test "shows_in_outline_only_an_exact_fact_is_printed" {
+ if (!(shows_in_outline(LABEL_EXACT))) @panic("assertion failed");
+ if (!(shows_in_outline(LABEL_DERIVED) == false)) __t27_assert_fail("\n assertion failed:\n shows_in_outline(LABEL_DERIVED) = {any}\n", .{ shows_in_outline(LABEL_DERIVED) });
+ if (!(shows_in_outline(LABEL_SUGGESTED) == false)) __t27_assert_fail("\n assertion failed:\n shows_in_outline(LABEL_SUGGESTED) = {any}\n", .{ shows_in_outline(LABEL_SUGGESTED) });
+ if (!(shows_in_outline(LABEL_UNKNOWN) == false)) __t27_assert_fail("\n assertion failed:\n shows_in_outline(LABEL_UNKNOWN) = {any}\n", .{ shows_in_outline(LABEL_UNKNOWN) });
+}
+test "span_is_range_one_line_alone_two_lines_a_range" {
+ if (!(span_is_range(22, 22) == false)) __t27_assert_fail("\n assertion failed:\n span_is_range(22, 22) = {any}\n", .{ span_is_range(22, 22) });
+ if (!(span_is_range(37, 39))) @panic("assertion failed");
+ if (!(span_is_range(5, 4) == false)) __t27_assert_fail("\n assertion failed:\n span_is_range(5, 4) = {any}\n", .{ span_is_range(5, 4) });
+}
+test "refusal_index_over_the_cap_is_named" {
+ if (!(refusal_index(ERR_TOO_MANY_NODES) == REFUSE_NODES)) __t27_assert_fail("\n assertion failed:\n refusal_index(ERR_TOO_MANY_NODES) = {any}\n REFUSE_NODES = {any}\n", .{ refusal_index(ERR_TOO_MANY_NODES), REFUSE_NODES });
+ if (!(refusal_index(ERR_TOO_MANY_EDGES) == REFUSE_EDGES)) __t27_assert_fail("\n assertion failed:\n refusal_index(ERR_TOO_MANY_EDGES) = {any}\n REFUSE_EDGES = {any}\n", .{ refusal_index(ERR_TOO_MANY_EDGES), REFUSE_EDGES });
+ if (!(refusal_index(9) == REFUSE_OTHER)) __t27_assert_fail("\n assertion failed:\n refusal_index(9) = {any}\n REFUSE_OTHER = {any}\n", .{ refusal_index(9), REFUSE_OTHER });
+ if (!(refusal_index(GRAPH_OK) == REFUSE_OTHER)) __t27_assert_fail("\n assertion failed:\n refusal_index(GRAPH_OK) = {any}\n REFUSE_OTHER = {any}\n", .{ refusal_index(GRAPH_OK), REFUSE_OTHER });
+}
+test "refusal_indices_are_distinct" {
+ if (!(refusal_index(ERR_TOO_MANY_NODES) != refusal_index(ERR_TOO_MANY_EDGES))) __t27_assert_fail("\n assertion failed:\n refusal_index(ERR_TOO_MANY_NODES) = {any}\n refusal_index(ERR_TOO_MANY_EDGES) = {any}\n", .{ refusal_index(ERR_TOO_MANY_NODES), refusal_index(ERR_TOO_MANY_EDGES) });
+ if (!(refusal_index(ERR_TOO_MANY_EDGES) != refusal_index(ERR_MODULE))) __t27_assert_fail("\n assertion failed:\n refusal_index(ERR_TOO_MANY_EDGES) = {any}\n refusal_index(ERR_MODULE) = {any}\n", .{ refusal_index(ERR_TOO_MANY_EDGES), refusal_index(ERR_MODULE) });
+ if (!(refusal_index(ERR_TOO_MANY_NODES) != refusal_index(ERR_MODULE))) __t27_assert_fail("\n assertion failed:\n refusal_index(ERR_TOO_MANY_NODES) = {any}\n refusal_index(ERR_MODULE) = {any}\n", .{ refusal_index(ERR_TOO_MANY_NODES), refusal_index(ERR_MODULE) });
+}
+test "templates_carry_the_marks_their_comments_name" {
+ if (!(mark_count(OUTLINE_TEXT[0]) == 6)) __t27_assert_fail("\n assertion failed:\n mark_count(OUTLINE_TEXT[0]) = {any}\n", .{ mark_count(OUTLINE_TEXT[0]) });
+ if (!(mark_count(OUTLINE_TEXT[1]) == 0)) __t27_assert_fail("\n assertion failed:\n mark_count(OUTLINE_TEXT[1]) = {any}\n", .{ mark_count(OUTLINE_TEXT[1]) });
+ if (!(mark_count(OUTLINE_TEXT[2]) == 0)) __t27_assert_fail("\n assertion failed:\n mark_count(OUTLINE_TEXT[2]) = {any}\n", .{ mark_count(OUTLINE_TEXT[2]) });
+ if (!(mark_count(OUTLINE_TEXT[3]) == 0)) __t27_assert_fail("\n assertion failed:\n mark_count(OUTLINE_TEXT[3]) = {any}\n", .{ mark_count(OUTLINE_TEXT[3]) });
+ if (!(mark_count(OUTLINE_TEXT[4]) == 5)) __t27_assert_fail("\n assertion failed:\n mark_count(OUTLINE_TEXT[4]) = {any}\n", .{ mark_count(OUTLINE_TEXT[4]) });
+ if (!(mark_count(OUTLINE_TEXT[5]) == 6)) __t27_assert_fail("\n assertion failed:\n mark_count(OUTLINE_TEXT[5]) = {any}\n", .{ mark_count(OUTLINE_TEXT[5]) });
+ if (!(mark_count(OUTLINE_TEXT[6]) == 2)) __t27_assert_fail("\n assertion failed:\n mark_count(OUTLINE_TEXT[6]) = {any}\n", .{ mark_count(OUTLINE_TEXT[6]) });
+ if (!(mark_count(OUTLINE_TEXT[7]) == 1)) __t27_assert_fail("\n assertion failed:\n mark_count(OUTLINE_TEXT[7]) = {any}\n", .{ mark_count(OUTLINE_TEXT[7]) });
+ if (!(mark_count(REFUSAL_TEXT[0]) == 3)) __t27_assert_fail("\n assertion failed:\n mark_count(REFUSAL_TEXT[0]) = {any}\n", .{ mark_count(REFUSAL_TEXT[0]) });
+ if (!(mark_count(REFUSAL_TEXT[1]) == 3)) __t27_assert_fail("\n assertion failed:\n mark_count(REFUSAL_TEXT[1]) = {any}\n", .{ mark_count(REFUSAL_TEXT[1]) });
+ if (!(mark_count(REFUSAL_TEXT[2]) == 3)) __t27_assert_fail("\n assertion failed:\n mark_count(REFUSAL_TEXT[2]) = {any}\n", .{ mark_count(REFUSAL_TEXT[2]) });
+}
+test "tested_by_on_tutorial_08_each_fn_has_its_one_test" {
+ const g: SpecGraph = tutorial_08_graph();
+ if (!(tested_by_count(g, 4) == 1)) __t27_assert_fail("\n assertion failed:\n tested_by_count(g, 4) = {any}\n", .{ tested_by_count(g, 4) });
+ if (!(tested_by_count(g, 8) == 1)) __t27_assert_fail("\n assertion failed:\n tested_by_count(g, 8) = {any}\n", .{ tested_by_count(g, 8) });
+ if (!(tested_by_count(g, 9) == 1)) __t27_assert_fail("\n assertion failed:\n tested_by_count(g, 9) = {any}\n", .{ tested_by_count(g, 9) });
+ if (!(tested_by_of(g, 4, 0) == 12)) __t27_assert_fail("\n assertion failed:\n tested_by_of(g, 4, 0) = {any}\n", .{ tested_by_of(g, 4, 0) });
+ if (!(tested_by_of(g, 8, 0) == 10)) __t27_assert_fail("\n assertion failed:\n tested_by_of(g, 8, 0) = {any}\n", .{ tested_by_of(g, 8, 0) });
+ if (!(tested_by_of(g, 9, 0) == 11)) __t27_assert_fail("\n assertion failed:\n tested_by_of(g, 9, 0) = {any}\n", .{ tested_by_of(g, 9, 0) });
+}
+test "tested_by_a_second_index_and_a_non_fn_answer_no_test" {
+ const g: SpecGraph = tutorial_08_graph();
+ if (!(tested_by_of(g, 4, 1) == NO_INDEX)) __t27_assert_fail("\n assertion failed:\n tested_by_of(g, 4, 1) = {any}\n NO_INDEX = {any}\n", .{ tested_by_of(g, 4, 1), NO_INDEX });
+ if (!(tested_by_count(g, 2) == 0)) __t27_assert_fail("\n assertion failed:\n tested_by_count(g, 2) = {any}\n", .{ tested_by_count(g, 2) });
+ if (!(tested_by_of(g, 2, 0) == NO_INDEX)) __t27_assert_fail("\n assertion failed:\n tested_by_of(g, 2, 0) = {any}\n NO_INDEX = {any}\n", .{ tested_by_of(g, 2, 0), NO_INDEX });
+ if (!(tested_by_count(g, 99) == 0)) __t27_assert_fail("\n assertion failed:\n tested_by_count(g, 99) = {any}\n", .{ tested_by_count(g, 99) });
+}
+test "negative_control_a_node_that_is_not_a_fn_has_no_tests" {
+ const g: SpecGraph = tutorial_08_graph();
+ if (!(tested_by_count(g, 13) == 0)) __t27_assert_fail("\n assertion failed:\n tested_by_count(g, 13) = {any}\n", .{ tested_by_count(g, 13) });
+ if (!(tested_by_count(g, 10) == 0)) __t27_assert_fail("\n assertion failed:\n tested_by_count(g, 10) = {any}\n", .{ tested_by_count(g, 10) });
+ if (!(tested_by_of(g, 13, 0) == NO_INDEX)) __t27_assert_fail("\n assertion failed:\n tested_by_of(g, 13, 0) = {any}\n NO_INDEX = {any}\n", .{ tested_by_of(g, 13, 0), NO_INDEX });
+}
+pub const EDGE_CALLS: u8 = 0;
+pub const EDGE_CONSTRAINS: u8 = 3;
+pub const EDGE_DATA_FLOW: u8 = 4;
+pub const EDGE_TESTED_BY: u8 = 2;
+pub const EDGE_USES: u8 = 1;
+pub const ERR_MODULE: u8 = 5;
+pub const ERR_TOO_MANY_EDGES: u8 = 4;
+pub const ERR_TOO_MANY_NODES: u8 = 3;
+pub const GRAPH_IR_VERSION: u32 = 1;
+pub const GRAPH_OK: u8 = 0;
+pub const LABEL_DERIVED: u8 = 1;
+pub const LABEL_EXACT: u8 = 0;
+pub const LABEL_SUGGESTED: u8 = 2;
+pub const LABEL_UNKNOWN: u8 = 3;
+pub const MAX_EDGES: u32 = 254;
+pub const MAX_NODES: u32 = 192;
+pub const NODE_BENCH: u8 = 5;
+pub const NODE_CONST: u8 = 1;
+pub const NODE_FN: u8 = 2;
+pub const NODE_INVARIANT: u8 = 4;
+pub const NODE_MODULE: u8 = 0;
+pub const NODE_OPAQUE: u8 = 7;
+pub const NODE_TEST: u8 = 3;
+pub const NODE_USE: u8 = 6;
+pub const NO_INDEX: u32 = 255;
+pub const NO_LINE: u32 = 0;
+pub const SpecGraph = struct {
+ ir_version: u32,
+ spec_path: []const u8,
+ line_count: u32,
+ node_count: u32,
+ node_kind: [192]u8,
+ node_label: [192]u8,
+ node_name: [192][]const u8,
+ line_start: [192]u32,
+ line_end: [192]u32,
+ edge_count: u32,
+ edge_kind: [254]u8,
+ edge_from: [254]u8,
+ edge_to: [254]u8,
+ edge_label: [254]u8,
+ edge_line: [254]u32,
+};
+pub fn blank_graph() SpecGraph {
+ return SpecGraph{ .ir_version = GRAPH_IR_VERSION, .spec_path = "", .line_count = 0, .node_count = 0, .node_kind = .{ 0 } ** 192, .node_label = .{ 0 } ** 192, .node_name = .{ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, .line_start = .{ 0 } ** 192, .line_end = .{ 0 } ** 192, .edge_count = 0, .edge_kind = .{ 0 } ** 254, .edge_from = .{ 0 } ** 254, .edge_to = .{ 0 } ** 254, .edge_label = .{ 0 } ** 254, .edge_line = .{ 0 } ** 254 };
+}
+pub fn tutorial_08_graph() SpecGraph {
+ var g: SpecGraph = blank_graph();
+ _ = &g;
+ g.spec_path = "specs/tutorial/08_modules_and_arrays.t27";
+ g.line_count = 93;
+ g = with_node(g, NODE_MODULE, LABEL_EXACT, "tutorial-08-modules", 1, 93);
+ g = with_node(g, NODE_USE, LABEL_EXACT, "base::types", 22, 22);
+ g = with_node(g, NODE_CONST, LABEL_EXACT, "EXPORTED_WIDTH", 32, 32);
+ g = with_node(g, NODE_CONST, LABEL_EXACT, "INTERNAL_SCALE", 35, 35);
+ g = with_node(g, NODE_FN, LABEL_EXACT, "scaled_width", 37, 39);
+ g = with_node(g, NODE_CONST, LABEL_EXACT, "TRIT_VALUES", 54, 54);
+ g = with_node(g, NODE_CONST, LABEL_EXACT, "POWERS_OF_TWO", 56, 56);
+ g = with_node(g, NODE_CONST, LABEL_EXACT, "EXPONENT_BIAS", 58, 58);
+ g = with_node(g, NODE_FN, LABEL_EXACT, "power_of_two", 66, 68);
+ g = with_node(g, NODE_FN, LABEL_EXACT, "bias_for_width", 70, 72);
+ g = with_node(g, NODE_TEST, LABEL_EXACT, "the power table matches its own definition", 78, 81);
+ g = with_node(g, NODE_TEST, LABEL_EXACT, "bias table carries the four standard widths", 83, 86);
+ g = with_node(g, NODE_TEST, LABEL_EXACT, "a private constant is still usable inside its own module", 88, 90);
+ g = with_node(g, NODE_INVARIANT, LABEL_EXACT, "exported_width_is_a_power_of_two", 92, 93);
+ g = with_node(g, NODE_OPAQUE, LABEL_UNKNOWN, "std.testing", 79, 89);
+ g = with_edge(g, EDGE_USES, 0, 1, LABEL_EXACT, 22);
+ g = with_edge(g, EDGE_USES, 4, 2, LABEL_EXACT, 38);
+ g = with_edge(g, EDGE_USES, 4, 3, LABEL_EXACT, 38);
+ g = with_edge(g, EDGE_USES, 8, 6, LABEL_EXACT, 67);
+ g = with_edge(g, EDGE_USES, 9, 7, LABEL_EXACT, 71);
+ g = with_edge(g, EDGE_DATA_FLOW, 2, 4, LABEL_DERIVED, 38);
+ g = with_edge(g, EDGE_DATA_FLOW, 3, 4, LABEL_DERIVED, 38);
+ g = with_edge(g, EDGE_DATA_FLOW, 6, 8, LABEL_DERIVED, 67);
+ g = with_edge(g, EDGE_DATA_FLOW, 7, 9, LABEL_DERIVED, 71);
+ g = with_edge(g, EDGE_CALLS, 10, 8, LABEL_EXACT, 79);
+ g = with_edge(g, EDGE_CALLS, 11, 9, LABEL_EXACT, 84);
+ g = with_edge(g, EDGE_CALLS, 12, 4, LABEL_EXACT, 89);
+ g = with_edge(g, EDGE_TESTED_BY, 8, 10, LABEL_DERIVED, NO_LINE);
+ g = with_edge(g, EDGE_TESTED_BY, 9, 11, LABEL_DERIVED, NO_LINE);
+ g = with_edge(g, EDGE_TESTED_BY, 4, 12, LABEL_DERIVED, NO_LINE);
+ g = with_edge(g, EDGE_CONSTRAINS, 13, 2, LABEL_EXACT, 93);
+ return g;
+}
+pub fn with_edge(g: SpecGraph, kind: u8, f: u8, t: u8, label: u8, line: u32) SpecGraph {
+ var h: SpecGraph = g;
+ _ = &h;
+ if (h.edge_count < MAX_EDGES) {
+ const k: usize = @as(usize, @intCast(h.edge_count));
+ h.edge_kind[k] = kind;
+ h.edge_from[k] = f;
+ h.edge_to[k] = t;
+ h.edge_label[k] = label;
+ h.edge_line[k] = line;
+ }
+ h.edge_count = h.edge_count + 1;
+ return h;
+}
+pub fn with_node(g: SpecGraph, kind: u8, label: u8, name: []const u8, s: u32, e: u32) SpecGraph {
+ var h: SpecGraph = g;
+ _ = &h;
+ if (h.node_count < MAX_NODES) {
+ const k: usize = @as(usize, @intCast(h.node_count));
+ h.node_kind[k] = kind;
+ h.node_label[k] = label;
+ h.node_name[k] = name;
+ h.line_start[k] = s;
+ h.line_end[k] = e;
+ }
+ h.node_count = h.node_count + 1;
+ return h;
+}
diff --git a/wasm/spec_graph_view.mjs b/wasm/spec_graph_view.mjs
index 3b85a82..dac7fe8 100644
--- a/wasm/spec_graph_view.mjs
+++ b/wasm/spec_graph_view.mjs
@@ -91,6 +91,7 @@ export const VIEW_LANE_HALF = 8;
// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
+// t27c gen-js: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-js: a InvariantBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-js: a InvariantBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-js: a InvariantBlock was not emitted -- it is checked by the compiler, not by the artifact.
@@ -128,8 +129,8 @@ export const LABEL_EXACT = 0;
export const LABEL_KINDS = 4;
export const LABEL_SUGGESTED = 2;
export const LABEL_UNKNOWN = 3;
-export const MAX_EDGES = 24;
-export const MAX_NODES = 20;
+export const MAX_EDGES = 254;
+export const MAX_NODES = 192;
export const NODE_BENCH = 5;
export const NODE_CONST = 1;
export const NODE_FN = 2;
@@ -141,7 +142,7 @@ export const NODE_TEST = 3;
export const NODE_USE = 6;
export const NO_INDEX = 255;
export const NO_LINE = 0;
-export const SpecGraph = Object.freeze({ __struct__: "SpecGraph", fields: [["ir_version", "u32"], ["spec_path", "str"], ["line_count", "u32"], ["node_count", "u32"], ["node_kind", "[20]u8"], ["node_label", "[20]u8"], ["node_name", "[20]str"], ["line_start", "[20]u32"], ["line_end", "[20]u32"], ["edge_count", "u32"], ["edge_kind", "[24]u8"], ["edge_from", "[24]u8"], ["edge_to", "[24]u8"], ["edge_label", "[24]u8"], ["edge_line", "[24]u32"]] });
+export const SpecGraph = Object.freeze({ __struct__: "SpecGraph", fields: [["ir_version", "u32"], ["spec_path", "str"], ["line_count", "u32"], ["node_count", "u32"], ["node_kind", "[192]u8"], ["node_label", "[192]u8"], ["node_name", "[192]str"], ["line_start", "[192]u32"], ["line_end", "[192]u32"], ["edge_count", "u32"], ["edge_kind", "[254]u8"], ["edge_from", "[254]u8"], ["edge_to", "[254]u8"], ["edge_label", "[254]u8"], ["edge_line", "[254]u32"]] });
export const VERDICT_KINDS = 19;
// t27c gen-js: fn blank_graph was not emitted -- this backend lowers declarations, not bodies.
// t27c gen-js: fn kind_bit was not emitted -- this backend lowers declarations, not bodies.
diff --git a/wasm/spec_graph_view.zig b/wasm/spec_graph_view.zig
index 851cdc7..6d35097 100644
--- a/wasm/spec_graph_view.zig
+++ b/wasm/spec_graph_view.zig
@@ -626,6 +626,26 @@ test "refused_graphs_are_not_drawn" {
if (!(view_verdict(x) == VIEW_ERR_EDGE_NO_LINE)) __t27_assert_fail("\n assertion failed:\n view_verdict(x) = {any}\n VIEW_ERR_EDGE_NO_LINE = {any}\n", .{ view_verdict(x), VIEW_ERR_EDGE_NO_LINE });
if (!(view_verdict(blank_graph()) == ERR_SPEC_PATH)) __t27_assert_fail("\n assertion failed:\n view_verdict(blank_graph()) = {any}\n ERR_SPEC_PATH = {any}\n", .{ view_verdict(blank_graph()), ERR_SPEC_PATH });
}
+test "a_graph_at_node_capacity_is_drawn_in_a_tall_canvas" {
+ const base: SpecGraph = tutorial_08_graph();
+ var g: SpecGraph = base;
+ _ = &g;
+ while (g.node_count < MAX_NODES) {
+ g = with_node(g, NODE_CONST, LABEL_EXACT, "extra", 1, 1);
+ }
+ if (!(g.node_count == MAX_NODES)) __t27_assert_fail("\n assertion failed:\n g.node_count = {any}\n MAX_NODES = {any}\n", .{ g.node_count, MAX_NODES });
+ if (!(view_verdict(g) == VIEW_OK)) __t27_assert_fail("\n assertion failed:\n view_verdict(g) = {any}\n VIEW_OK = {any}\n", .{ view_verdict(g), VIEW_OK });
+ if (!(view_column_count(g) == view_column_count(base))) __t27_assert_fail("\n assertion failed:\n view_column_count(g) = {any}\n view_column_count(base) = {any}\n", .{ view_column_count(g), view_column_count(base) });
+ if (!(view_width(g) == view_width(base))) __t27_assert_fail("\n assertion failed:\n view_width(g) = {any}\n view_width(base) = {any}\n", .{ view_width(g), view_width(base) });
+ if (!(view_known_depth(g) >= 177)) __t27_assert_fail("\n assertion failed:\n view_known_depth(g) = {any}\n", .{ view_known_depth(g) });
+ if (!(view_height(g) > 11000)) __t27_assert_fail("\n assertion failed:\n view_height(g) = {any}\n", .{ view_height(g) });
+ if (!(view_height(g) > view_height(base))) __t27_assert_fail("\n assertion failed:\n view_height(g) = {any}\n view_height(base) = {any}\n", .{ view_height(g), view_height(base) });
+ const last: u32 = MAX_NODES - 1;
+ if (!(view_node_column(g, last) == view_node_column(g, last - 1))) __t27_assert_fail("\n assertion failed:\n view_node_column(g, last) = {any}\n view_node_column(g, last - 1) = {any}\n", .{ view_node_column(g, last), view_node_column(g, last - 1) });
+ if (!(view_node_y(g, last) == (view_node_y(g, last - 1) + VIEW_ROW_STEP))) __t27_assert_fail("\n assertion failed:\n view_node_y(g, last) = {any}\n view_node_y(g, last - 1) + VIEW_ROW_STEP = {any}\n", .{ view_node_y(g, last), view_node_y(g, last - 1) + VIEW_ROW_STEP });
+ if (!(view_overlap(g, last, last - 1) == false)) __t27_assert_fail("\n assertion failed:\n view_overlap(g, last, last - 1) = {any}\n", .{ view_overlap(g, last, last - 1) });
+ if (!(view_overlap(g, last, last))) @panic("assertion failed");
+}
test "nodes_bind_through_spec_graph" {
const g: SpecGraph = tutorial_08_graph();
if (!(view_node_widget(g, 0) == WIDGET_HONEYCOMB_CARD)) __t27_assert_fail("\n assertion failed:\n view_node_widget(g, 0) = {any}\n WIDGET_HONEYCOMB_CARD = {any}\n", .{ view_node_widget(g, 0), WIDGET_HONEYCOMB_CARD });
@@ -758,8 +778,8 @@ pub const LABEL_EXACT: u8 = 0;
pub const LABEL_KINDS: u8 = 4;
pub const LABEL_SUGGESTED: u8 = 2;
pub const LABEL_UNKNOWN: u8 = 3;
-pub const MAX_EDGES: u32 = 24;
-pub const MAX_NODES: u32 = 20;
+pub const MAX_EDGES: u32 = 254;
+pub const MAX_NODES: u32 = 192;
pub const NODE_BENCH: u8 = 5;
pub const NODE_CONST: u8 = 1;
pub const NODE_FN: u8 = 2;
@@ -776,21 +796,21 @@ pub const SpecGraph = struct {
spec_path: []const u8,
line_count: u32,
node_count: u32,
- node_kind: [20]u8,
- node_label: [20]u8,
- node_name: [20][]const u8,
- line_start: [20]u32,
- line_end: [20]u32,
+ node_kind: [192]u8,
+ node_label: [192]u8,
+ node_name: [192][]const u8,
+ line_start: [192]u32,
+ line_end: [192]u32,
edge_count: u32,
- edge_kind: [24]u8,
- edge_from: [24]u8,
- edge_to: [24]u8,
- edge_label: [24]u8,
- edge_line: [24]u32,
+ edge_kind: [254]u8,
+ edge_from: [254]u8,
+ edge_to: [254]u8,
+ edge_label: [254]u8,
+ edge_line: [254]u32,
};
pub const VERDICT_KINDS: u8 = 19;
pub fn blank_graph() SpecGraph {
- return SpecGraph{ .ir_version = GRAPH_IR_VERSION, .spec_path = "", .line_count = 0, .node_count = 0, .node_kind = .{ 0 } ** 20, .node_label = .{ 0 } ** 20, .node_name = .{ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, .line_start = .{ 0 } ** 20, .line_end = .{ 0 } ** 20, .edge_count = 0, .edge_kind = .{ 0 } ** 24, .edge_from = .{ 0 } ** 24, .edge_to = .{ 0 } ** 24, .edge_label = .{ 0 } ** 24, .edge_line = .{ 0 } ** 24 };
+ return SpecGraph{ .ir_version = GRAPH_IR_VERSION, .spec_path = "", .line_count = 0, .node_count = 0, .node_kind = .{ 0 } ** 192, .node_label = .{ 0 } ** 192, .node_name = .{ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, .line_start = .{ 0 } ** 192, .line_end = .{ 0 } ** 192, .edge_count = 0, .edge_kind = .{ 0 } ** 254, .edge_from = .{ 0 } ** 254, .edge_to = .{ 0 } ** 254, .edge_label = .{ 0 } ** 254, .edge_line = .{ 0 } ** 254 };
}
pub fn kind_bit(k: u8) u32 {
return @as(u32, @intCast(1)) << @intCast(@as(u32, @intCast(k)));
diff --git a/wasm/t27f.wasm b/wasm/t27f.wasm
old mode 100644
new mode 100755
index 89d2ec5..c2309e1
Binary files a/wasm/t27f.wasm and b/wasm/t27f.wasm differ