tricu

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

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 }