tricu

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

compiler.ts (3844B)


      1 import { bundleHash, type BundleHash } from './bundle.js';
      2 import { Graph, type NodeId } from './graph.js';
      3 import { text, readText, readList, readNatural } from './codecs.js';
      4 export const COMPILER_EXPORT = 'source_unit.compileStableModuleDefinitions';
      5 export function compilerDescriptor(raw: unknown): { hash: BundleHash } {
      6   if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('Invalid compiler descriptor');
      7   const descriptor = raw as Record<string, unknown>;
      8   if (descriptor['schema'] !== 1 || descriptor['backend'] !== 'stable' || descriptor['export'] !== COMPILER_EXPORT) throw new Error('Invalid stable compiler descriptor');
      9   return { hash: bundleHash(descriptor['hash']) };
     10 }
     11 export const COMPILER_COLLECTION = Object.freeze({ threshold: 250_000 });
     12 export const SOURCE_LIMIT = 16_384;
     13 export function sourceText(source: unknown): string {
     14   if (typeof source !== 'string' || !source.trim()) throw new Error('Enter a source unit or native module');
     15   if (source.length > SOURCE_LIMIT * 2) throw new Error('Source limit exceeded');
     16   let count = 0;
     17   for (const char of source) {
     18     if (++count > SOURCE_LIMIT) throw new Error('Source limit exceeded');
     19     const scalar = char.codePointAt(0)!;
     20     if (scalar >= 0xd800 && scalar <= 0xdfff) throw new Error('Unpaired source surrogate');
     21   }
     22   return source;
     23 }
     24 export function sourceLiteral(graph: Graph, source: unknown): NodeId {
     25   return text(graph, sourceText(source), SOURCE_LIMIT);
     26 }
     27 export function compilerDiagnostic(graph: Graph, payload: NodeId): string {
     28   const [diagnostic] = graph.pair(payload, 'compiler error payload');
     29   const [code, tail] = graph.pair(diagnostic, 'diagnostic code');
     30   const [expected, fields] = graph.pair(tail, 'diagnostic expected');
     31   const [, locationFields] = graph.pair(fields, 'diagnostic found');
     32   const [location] = graph.pair(locationFields, 'diagnostic location');
     33   let where = '';
     34   if (graph.observe(location).node.kind !== 'leaf') {
     35     const [line, rest] = graph.pair(location, 'source position'), [column] = graph.pair(rest, 'source column');
     36     where = ` at ${readNatural(graph, line)}:${readNatural(graph, column)}`;
     37   }
     38   // Expected can be a token-kind number or a label; found/context/remainder
     39   // may be arbitrary trees. Decode only the compiler's known diagnostic profile.
     40   const number = readNatural(graph, code);
     41   const label = number === 2n ? `: expected token kind ${readNatural(graph, expected)}`
     42     : number === 20n ? `: unresolved name ${JSON.stringify(readText(graph, expected, 512))}`
     43     : number >= 30n ? `: expected ${JSON.stringify(readText(graph, expected, 512))}` : '';
     44   return `Compiler diagnostic ${number}${where}${label}`;
     45 }
     46 // Decode only the result spine/names. Compiled program trees remain lazy handles.
     47 // Do not normalize/serialize them merely to populate an export selector.
     48 export function compiledDefinitions(graph: Graph, result: NodeId, allowEmpty = false): Map<string, NodeId> {
     49   const [tag, payload] = graph.pair(result, 'compiler result');
     50   const flag = graph.observe(tag).node;
     51   if (flag.kind === 'leaf') throw new Error(compilerDiagnostic(graph, payload));
     52   if (flag.kind !== 'stem' || graph.observe(flag.child).node.kind !== 'leaf') throw new Error('Invalid compiler result tag');
     53   const [definitions] = graph.pair(payload, 'compiler definitions payload');
     54   const bindings = readList(graph, definitions, 16), exports = new Map<string, NodeId>();
     55   for (const binding of bindings) {
     56     const [nameTree, program] = graph.pair(binding, 'compiled binding');
     57     const name = readText(graph, nameTree, 128);
     58     if (!name || exports.has(name)) throw new Error('Empty/duplicate compiled definition name');
     59     exports.set(name, program);
     60   }
     61   if (!exports.size && !allowEmpty) throw new Error('Source unit produced no definitions');
     62   return exports;
     63 }