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