tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

tree-result.ts (2828B)


      1 import type { GraphPreview } from './graph.js';
      2 import type { Bundle } from './bundle.js';
      3 import { exportTree, type TreeFormat, type TreeFile } from './tree-export.js';
      4 export const RESULT_NODE_LIMIT = 100_000;
      5 export type TreeResultFormat = 'tricu' | 'triangle' | 'dag' | 'ternary';
      6 export function isTreeResult(value: string): value is TreeResultFormat {
      7   return ['tricu', 'triangle', 'dag', 'ternary'].includes(value);
      8 }
      9 // Backend snapshots describe physical sharing, not structural hash-consing.
     10 // Reject incomplete/cyclic observations instead of exporting a plausible prefix.
     11 export function snapshotBundle(snapshot: GraphPreview): Bundle {
     12   if (snapshot.truncated) throw new Error('Result snapshot exceeds 100,000 nodes; refusing a partial export');
     13   const aliases = new Map<number, number>(snapshot.aliases.map(a => [a.from, a.to]));
     14   const byId = new Map<number, GraphPreview['nodes'][number]>(snapshot.nodes.map(n => [n.id, n]));
     15   const resolve = (raw: number): number => {
     16     let id = raw; const seen = new Set<number>();
     17     while (aliases.has(id)) { if (seen.has(id)) throw new Error('Snapshot alias cycle'); seen.add(id); id = aliases.get(id)!; }
     18     return id;
     19   };
     20   const nodes: Bundle['nodes'] = [], mapped = new Map<number, number>(), active = new Set<number>();
     21   const root = resolve(snapshot.root), stack: Array<[number, boolean]> = [[root, false]];
     22   while (stack.length) {
     23     const [raw, leave] = stack.pop()!, id = resolve(raw);
     24     if (mapped.has(id)) continue;
     25     const node = byId.get(id); if (!node) throw new Error('Incomplete result snapshot');
     26     const arity = { leaf: 0, stem: 1, fork: 2 }[node.kind];
     27     if (arity === undefined || node.children.length !== arity) throw new Error('Invalid snapshot constructor');
     28     if (!leave) {
     29       if (active.has(id)) throw new Error('Cyclic result cannot be exported as a DAG');
     30       active.add(id); stack.push([id, true]);
     31       for (const child of [...node.children].reverse()) stack.push([child, false]);
     32     } else {
     33       active.delete(id); const cs = node.children.map(c => mapped.get(resolve(c))!);
     34       if (cs.some(c => c === undefined)) throw new Error('Invalid result snapshot order');
     35       mapped.set(id, nodes.length);
     36       nodes.push(node.kind === 'leaf' ? { kind: 'leaf' } : node.kind === 'stem' ? { kind: 'stem', child: cs[0]! } : { kind: 'fork', left: cs[0]!, right: cs[1]! });
     37     }
     38   }
     39   return { nodes, roots: [mapped.get(root)!], exports: [{ name: 'tree', root: mapped.get(root)!, kind: 'term', abi: 'arboricx.abi.tree.v1' }], metadata: {} };
     40 }
     41 export function treeResult(snapshot: GraphPreview, format: TreeResultFormat): { type: TreeResultFormat; value: string; file: TreeFile } {
     42   const file = exportTree(snapshotBundle(snapshot), 'tree', format as TreeFormat);
     43   return { type: format, value: file.text, file };
     44 }