tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

compiler.test.mjs (4925B)


      1 import test from 'node:test';
      2 import assert from 'node:assert/strict';
      3 import {readFile} from 'node:fs/promises';
      4 import {createHash} from 'node:crypto';
      5 import {Graph} from '../dist/graph.js';
      6 import {natural,text,list,readText} from '../dist/codecs.js';
      7 import {compiledDefinitions,sourceLiteral,sourceText,SOURCE_LIMIT} from '../dist/compiler.js';
      8 test('compiler shipped pin matches authenticated reference and Haskell golden provenance',async()=>{
      9   const descriptor=JSON.parse(await readFile(new URL('../compiler.json',import.meta.url)));
     10   const goldens=JSON.parse(await readFile(new URL('./compiler-conformance.json',import.meta.url)));
     11   const shipped=await readFile(new URL('../bundles/'+descriptor.hash+'.arboricx',import.meta.url));
     12   assert.equal(createHash('sha256').update(shipped).digest('hex'),descriptor.hash);
     13   assert.equal(goldens.compilerBundleSha256,descriptor.hash);
     14   const historical=goldens.cases.filter(c=>c.mode==='io');
     15   assert.equal(historical.length,1);
     16   assert.equal(historical[0].compileOnly,true);
     17   assert.equal(historical[0].executionRejected,'Unsupported tricuIO version');
     18   assert.equal(historical[0].result,undefined);
     19   assert.match(descriptor.referenceCommit,/^[0-9a-f]{40}$/);
     20   // The site's accepted compiler is intentionally pinned independently of HEAD.
     21   // Authenticate it against its retained publication metadata below, not the
     22   // current compiler-library fixture. Never ask assert to diff megabyte buffers.
     23   const metadata=(await readFile(new URL('../compiler-reference.v1',import.meta.url),'utf8')).trim().split('\n');
     24   assert.equal(metadata[0],'tricu.compiler-library-fixture.v1');
     25   const fixtureDigest=createHash('sha256').update('tricu.compiler-library-fixture.v1\0').update(shipped).digest('hex');
     26   assert.deepEqual(metadata.filter(row=>row.startsWith('bundle\t')),['bundle\t'+fixtureDigest]);
     27   const sources=metadata.filter(row=>row.startsWith('module\t'));
     28   assert.equal(sources.length,34);
     29   for(const alias of ['source_binding','source_binding_analysis','source_binding_support'])
     30     assert.ok(sources.some(row=>row.split('\t')[2]===alias),alias);
     31   assert.ok(sources.every(row=>row.split('\t')[2]!=='patterns'));
     32   for(const row of sources){
     33     const pin=row.split('\t')[4];assert.match(pin,/^[0-9a-f]{64}$/);
     34     const source=await readFile(new URL('../../../test/fixtures/reference-source/'+pin+'.tri',import.meta.url));
     35     assert.equal(createHash('sha256').update('arboricx.source.v1\0').update(source).digest('hex'),pin);
     36   }
     37   assert.equal(descriptor.export,'source_unit.compileStableModuleDefinitions');
     38 });
     39 test('bounded compiler result decoder preserves lazy program handles',()=>{
     40   const g=new Graph({fuel:100000}),q=g.fromTernary('212110021100'),loop=g.app(q,q);
     41   const binding=g.fork(text(g,'loop'),loop);
     42   const success=defs=>g.fork(g.stem(g.leaf),g.fork(defs,g.leaf));
     43   const exports=compiledDefinitions(g,success(list(g,[binding])));
     44   assert.equal(compiledDefinitions(g,success(list(g,Array.from({length:64},(_,i)=>g.fork(text(g,'local'+i),loop))))).size,64);
     45   assert.equal(exports.get('loop'),loop); // No forcing/expansion of code to name exports.
     46   assert.throws(()=>compiledDefinitions(g,success(list(g,[binding,binding]))),/duplicate/);
     47   assert.throws(()=>compiledDefinitions(g,success(list(g,Array(4097).fill(binding)))),/limit/);
     48   assert.throws(()=>compiledDefinitions(g,success(g.leaf)),/no definitions/);
     49   assert.throws(()=>compiledDefinitions(g,g.fork(g.stem(g.stem(g.leaf)),g.leaf)),/tag/);
     50   assert.throws(()=>sourceLiteral(g,'x'.repeat(SOURCE_LIMIT+1)),/limit/);
     51   assert.throws(()=>sourceLiteral(g,''),/Enter/);
     52   assert.throws(()=>sourceLiteral(g,'\ud800'),/surrogate/);
     53 });
     54 test('structured compiler diagnostics ignore arbitrary found/context/remainder trees',()=>{
     55   const g=new Graph({fuel:100000}),q=g.fromTernary('212110021100'),loop=g.app(q,q);
     56   const location=g.fork(natural(g,2n),g.fork(natural(g,3n),loop));
     57   const diagnostic=g.fork(natural(g,2n),g.fork(natural(g,7n),g.fork(loop,g.fork(location,loop))));
     58   const failure=g.fork(g.leaf,g.fork(diagnostic,loop));
     59   assert.throws(()=>compiledDefinitions(g,failure),/^Error: Compiler diagnostic 2 at 2:3: expected token kind 7$/);
     60   assert.ok(g.stats.work<1000);
     61 });
     62 
     63 test('16,384 scalar source validation and Worker encoding have no hidden 256/main-heap cap',()=>{
     64  assert.equal(SOURCE_LIMIT,16384);
     65  const ascii='x'.repeat(SOURCE_LIMIT), unicode='🙂'.repeat(SOURCE_LIMIT);
     66  assert.equal(sourceText(ascii),ascii);assert.equal(sourceText(unicode),unicode);
     67  assert.throws(()=>sourceText(unicode+'x'),/limit/);
     68  assert.throws(()=>sourceText(ascii+'x'),/limit/);
     69  assert.throws(()=>sourceText(' \n '),/Enter/);
     70  assert.throws(()=>sourceText('x\udc00'),/surrogate/);
     71  const g=new Graph({nodes:1_000_000,fuel:2_000_000});
     72  const encoded=sourceLiteral(g,unicode);
     73  assert.ok(g.stats.nodes>250000);assert.equal(readText(g,encoded,SOURCE_LIMIT),unicode);
     74 });