editable-conformance.mjs (4062B)
1 // Opt-in real demo compilation with explicit native manifest imports. No host 2 // recompilation; expected results are retained independent Haskell captures. 3 import assert from 'node:assert/strict'; 4 import {readFile} from 'node:fs/promises'; 5 import {createHash} from 'node:crypto'; 6 import {Graph} from '../dist/graph.js'; 7 import {decodeBundle,loadBundle,COMPILER_BUNDLE_LIMIT} from '../dist/bundle.js'; 8 import {compiledDefinitions,sourceLiteral,COMPILER_EXPORT} from '../dist/compiler.js'; 9 import {stageModule,selectModuleExports} from '../dist/module-stage.js'; 10 import {moduleIdentity} from '../dist/module-identity.js'; 11 import {list,text,readText} from '../dist/codecs.js'; 12 import {runIO} from '../dist/io.js'; 13 const root=new URL('../',import.meta.url); 14 const catalogue=JSON.parse(await readFile(new URL('catalogue.json',root))); 15 const environments=JSON.parse(await readFile(new URL('environments.json',root))); 16 const goldens=JSON.parse(await readFile(new URL('demos.json',root))); 17 async function bundle(hash,limit){ 18 const bytes=await readFile(new URL('bundles/'+hash+'.arboricx',root)); 19 assert.equal(createHash('sha256').update(bytes).digest('hex'),hash); 20 return decodeBundle(bytes,limit); 21 } 22 for(const id of ['parser','lexer'])goldens.demos[id]=JSON.parse(await readFile(new URL(id+'-demo.json',root))); 23 const releases=JSON.parse(await readFile(new URL('packages.json',root))).releases; 24 const compiler=await bundle(environments.compiler,COMPILER_BUNDLE_LIMIT); 25 for(const entry of catalogue.filter(e=>!process.argv[2]||e.id===process.argv[2])){ 26 const started=performance.now(); 27 const g=new Graph({nodes:6_000_000,fuel:Infinity,stack:100_000},{threshold:250_000}); 28 const library=loadBundle(g,compiler),stage=stageModule(g,library,entry.source+'\n-- Edited locally\n'); 29 assert.ok(stage.native); 30 // Header strings/shapes now live in JS; release native parsing temporaries. 31 g.collect(library.values()); 32 const modules=new Map(),scope=new Map(); 33 const roots=()=>[...library.values(),...[...modules.values()].flatMap(m=>[...m.values()])]; 34 let qualifiedCount=0; 35 for(const [ref,qualifier] of stage.imports){ 36 if(!modules.has(ref)){ 37 const release=releases.find(r=>r.manifest===ref);assert.ok(release); 38 const decoded=await bundle(release.hash);assert.equal(await moduleIdentity(decoded),ref); 39 modules.set(ref,loadBundle(g,decoded)); 40 } 41 for(const [name,value] of modules.get(ref)){ 42 const qualified=readText(g,g.apply(library.get('source_module.sourceModuleQualified'),[text(g,qualifier),text(g,name)]),512); 43 assert.ok(!scope.has(qualified));scope.set(qualified,value); 44 if(++qualifiedCount%32===0)g.collect(roots()); 45 } 46 } 47 g.collect(roots()); 48 const bindings=[...scope].map(([name,value])=>g.fork(text(g,name),value)); 49 const locals=compiledDefinitions(g,g.apply(library.get(COMPILER_EXPORT),[list(g,bindings),sourceLiteral(g,stage.body)])); 50 for(const name of locals.keys())assert.ok(!scope.has(name)); 51 const definitions=selectModuleExports(stage,locals,modules); 52 g.collect(definitions.values()); 53 console.log(entry.id,'compile ms',Math.round(performance.now()-started),'stats',JSON.stringify(g.stats)); 54 // Exactly apply the default published export using the first Haskell capture 55 // for that export; evaluating the draft must reproduce the independent tree. 56 const expected=goldens.demos[entry.id].haskellCases.find(c=>c.export===entry.export); 57 assert.ok(expected); 58 g.beginOperation(); 59 let result=g.apply(definitions.get(entry.export),expected.arguments.map(a=>g.fromTernary(a))); 60 let terminal=''; 61 if(entry.mode==='io')result=await runIO(g,result,{before:async()=>{},after(){},running(){},write(s){terminal+=s;},read:async()=>expected.input},true,definitions.values()); 62 const actual=g.toTernary(result); 63 // Compare small digests rather than permitting gigantic diagnostic diffs. 64 const sha=s=>createHash('sha256').update(s).digest('hex'); 65 assert.equal(sha(actual),sha(expected.result),entry.id+' result');assert.equal(terminal,expected.terminal); 66 console.log('PASS',entry.id,'draft equals independent Haskell result'); 67 }