tricu

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

name_index.py (4881B)


      1 #!/usr/bin/env python3
      2 """Rebuild the current index; check complete results, persistence and demand.
      3 
      4 Optional --unit selects an artifact-only stable module compiler for independent
      5 source outcomes and complete comparisons with --reference (published by default).
      6 No compiler generation, publication or acceptance is performed here.
      7 """
      8 import argparse
      9 import hashlib
     10 import json
     11 from pathlib import Path
     12 import subprocess
     13 from bundle_dag import merge_bundles
     14 from pattern_lowering import audit
     15 from shipped_compiler import verify
     16 from source_equation_cases import cases, quoted
     17 
     18 ROOT = Path(__file__).resolve().parent.parent
     19 
     20 
     21 def main():
     22     p = argparse.ArgumentParser(description=__doc__)
     23     p.add_argument('tricu', type=Path)
     24     p.add_argument('--directory', type=Path, required=True)
     25     p.add_argument('--unit', type=Path)
     26     p.add_argument('--reference', type=Path, default=ROOT/'test/fixtures/compiler-library.arboricx')
     27     args = p.parse_args()
     28     out = args.directory.resolve(); out.mkdir(parents=True, exist_ok=False)
     29     ws = out/'workspace'; ws.mkdir(); (ws/'tricu.workspace').write_text('tricu.workspace.v1\n')
     30     host = args.tricu.resolve(); compiler = verify(); sha = lambda p: hashlib.sha256(p.read_bytes()).hexdigest()
     31     report = dict(complete=False, compiler_sha256=compiler['sha256'], runtime_sha256=sha(host), commands=[])
     32 
     33     def run(argv, seconds=300, reject=False):
     34         command = list(map(str, argv))
     35         r = subprocess.run(command, capture_output=True, text=True, cwd=ROOT, timeout=seconds)
     36         report['commands'].append(dict(argv=command, code=r.returncode, stdout=r.stdout, stderr=r.stderr))
     37         (out/'report.json').write_text(json.dumps(report, indent=2)+'\n')
     38         if reject:
     39             assert r.returncode and 'first-binding mismatch' in r.stderr, r
     40         elif r.returncode:
     41             raise RuntimeError(r.stdout[-4000:]+r.stderr[-4000:])
     42 
     43     paths = dict(source_index='lib/source/index.tri', prelude='lib/prelude.tri', base='lib/base.tri',
     44                  bytes='lib/bytes.tri', conversions='lib/conversions.tri', native_math='lib/math/native.tri')
     45     for alias, path in paths.items(): (ws/(alias+'.tri')).write_bytes((ROOT/path).read_bytes())
     46     report['sources'] = {path: sha(ROOT/path) for path in paths.values()}
     47 
     48     def compile(alias):
     49         output = out/(alias+'.arboricx')
     50         run([host, 'arboricx', 'compile', '--file', ws/(alias+'.tri'), '--store', out/'store',
     51              '--output', output, '--compile-timeout', '240', '+RTS', '-N1', '-M8192m', '-RTS'])
     52         return output
     53 
     54     index = compile('source_index')
     55     report['modules'] = audit(ws, out/'store', compiler['sha256'], {})
     56     expr = ('let f=builtins.getFlake "git+file://'+str(ROOT)+'";'
     57             'p=f.inputs.nixpkgs.legacyPackages.x86_64-linux;'
     58             'in p.haskellPackages.ghcWithPackages(h:[h.cryptonite h.memory h.base16-bytestring h.vector])')
     59 
     60     def build(name):
     61         exe = out/name
     62         run(['nix', 'shell', '--impure', '--expr', expr, '--command', 'ghc', '-O2', '-rtsopts',
     63              '-XOverloadedStrings', '-XLambdaCase', '-XMultiWayIf', '-XScopedTypeVariables',
     64              '-isrc', '-outputdir', out/(name+'-objects'), ROOT/'test'/(name+'.hs'), '-o', exe])
     65         return exe
     66 
     67     checks = build('NameIndex'); run([checks, index, '+RTS', '-M2048m', '-RTS'])
     68     (ws/'wrong.tri').write_text('!export wrong\nwrong index key = t\n')
     69     bad = compile('wrong'); wrong = out/'wrong-index.arboricx'
     70     merge_bundles(wrong, [(index, [('sourceIndexBuild','sourceIndexBuild'), ('sourceIndexPut','sourceIndexPut')]),
     71                           (bad, [('wrong','sourceIndexGet')])], prune_unreachable=True)
     72     run([checks, wrong, '+RTS', '-M2048m', '-RTS'], reject=True)
     73     report['wrong_subject_rejected'] = True
     74     if args.unit:
     75         (ws/'parser.tri').write_bytes((ROOT/'lib/parser.tri').read_bytes())
     76         rows = ['(pair '+quoted(label)+' (pair '+('true' if truth else 'false')+' (pair '+env+
     77                 ' (pair '+quoted(source)+' '+expected+'))))' for label,source,expected,env,truth in cases]
     78         (ws/'corpus.tri').write_text('!import prelude *\n!import parser *\n!export fixtures\nfixtures = [\n'+
     79                                    '\n'.join(rows)+'\n]\n')
     80         corpus = compile('corpus'); checker = build('NameIndexUnit')
     81         run([checker, args.reference.resolve(), args.unit.resolve(), corpus, '+RTS', '-M8192m', '-RTS'])
     82         report['unit'] = dict(sha256=sha(args.unit), reference_sha256=sha(args.reference),
     83                               cases=len(cases), complete_results=True, environment_controls=6)
     84     report.update(complete=True, index_sha256=sha(index))
     85     (out/'report.json').write_text(json.dumps(report, indent=2)+'\n')
     86     print('PASS name index: complete model, persistence, demand, wrong-subject and source provenance')
     87 
     88 
     89 if __name__ == '__main__': main()