ascii_classifiers.py (3650B)
1 #!/usr/bin/env python3 2 """Compile current native_math and independently check complete ASCII predicates. 3 4 No reused library dependencies; the packaged stable compiler is authenticated. 5 This focused source check does not publish or accept a compiler generation. 6 """ 7 import argparse 8 import hashlib 9 import json 10 from pathlib import Path 11 import subprocess 12 13 from bundle_dag import merge_bundles 14 from pattern_lowering import audit 15 from shipped_compiler import verify as verify_compiler 16 17 ROOT = Path(__file__).resolve().parent.parent 18 19 20 def main(): 21 parser = argparse.ArgumentParser(description=__doc__) 22 parser.add_argument('tricu', type=Path) 23 parser.add_argument('--directory', type=Path, required=True) 24 args = parser.parse_args() 25 root = args.directory.resolve() 26 root.mkdir(parents=True, exist_ok=False) 27 workspace = root / 'workspace' 28 workspace.mkdir() 29 (workspace / 'tricu.workspace').write_text('tricu.workspace.v1\n') 30 host = args.tricu.resolve() 31 compiler = verify_compiler() 32 source = (ROOT / 'lib/math/native.tri').read_bytes() 33 (workspace / 'native_math.tri').write_bytes(source) 34 report = dict(complete=False, compiler_sha256=compiler['sha256'], 35 runtime_sha256=hashlib.sha256(host.read_bytes()).hexdigest(), 36 source_sha256=hashlib.sha256(source).hexdigest(), commands=[]) 37 38 def command(argv, timeout=180, rejection=False): 39 cmd = list(map(str, argv)) 40 out = subprocess.run(cmd, cwd=ROOT, capture_output=True, text=True, timeout=timeout) 41 report['commands'].append(dict(argv=cmd, code=out.returncode, 42 stdout=out.stdout, stderr=out.stderr)) 43 (root / 'report.json').write_text(json.dumps(report, indent=2) + '\n') 44 if rejection: 45 assert out.returncode != 0 and 'classifier mismatch' in out.stderr, out 46 elif out.returncode: 47 raise RuntimeError(out.stdout[-4000:] + out.stderr[-4000:]) 48 49 candidate = root / 'native_math.arboricx' 50 command([host, 'arboricx', 'compile', '--file', workspace / 'native_math.tri', 51 '--store', root / 'store', '--output', candidate, '--compile-timeout', '150', 52 '+RTS', '-N1', '-M4096m', '-RTS']) 53 report['modules'] = audit(workspace, root / 'store', compiler['sha256'], {}) 54 expr = ('let f=builtins.getFlake "git+file://' + str(ROOT) + '";' 55 'p=f.inputs.nixpkgs.legacyPackages.x86_64-linux;' 56 'in p.haskellPackages.ghcWithPackages(h:[h.cryptonite h.memory h.base16-bytestring h.vector])') 57 command(['nix', 'shell', '--impure', '--expr', expr, '--command', 'ghc', '-O2', '-rtsopts', 58 '-XOverloadedStrings', '-XLambdaCase', '-XMultiWayIf', '-XScopedTypeVariables', 59 '-isrc', '-outputdir', root / 'objects', ROOT / 'test/AsciiClassifiers.hs', 60 '-o', root / 'controls'], 300) 61 command([root / 'controls', candidate, '+RTS', '-M2048m', '-RTS']) 62 wrong = root / 'wrong.arboricx' 63 merge_bundles(wrong, [(candidate, [('letter', 'digit'), ('letter', 'letter'), 64 ('lowerHex', 'lowerHex')])], prune_unreachable=True) 65 command([root / 'controls', wrong, '+RTS', '-M2048m', '-RTS'], rejection=True) 66 report.update(complete=True, wrong_subject_rejected=True, 67 controls_sha256=hashlib.sha256((ROOT / 'test/AsciiClassifiers.hs').read_bytes()).hexdigest()) 68 (root / 'report.json').write_text(json.dumps(report, indent=2) + '\n') 69 print('PASS ASCII classifiers: canonical naturals, complete Booleans, wrong-subject and provenance controls') 70 71 72 if __name__ == '__main__': 73 main()