reference.py (8367B)
1 #!/usr/bin/env python3 2 """Build static agent references and picker metadata from published interfaces.""" 3 import argparse 4 import hashlib 5 import json 6 from pathlib import Path 7 import re 8 import sys 9 10 11 def generate(root, output): 12 site = root / 'ext/demo-site' 13 sys.path.insert(0, str(root / 'test')) 14 from bundle_dag import read 15 reference = output / 'reference' 16 reference.mkdir(parents=True, exist_ok=True) 17 def write(name, text): 18 path = reference / name 19 path.parent.mkdir(parents=True, exist_ok=True) 20 path.write_text(text) 21 write('LICENSE.txt', (root / 'LICENSE').read_text()) 22 notes = json.loads((site / 'reference-input/libraries.json').read_text()) 23 releases = json.loads((site / 'packages.json').read_text())['releases'] 24 rows = [] 25 # Keep all releases. Browser-side SemVer ordering chooses the initial view. 26 for release in releases: 27 if not release['name'].startswith('tricu-lib-'): 28 continue 29 short = release['name'].removeprefix('tricu-lib-') 30 bundle = site / 'bundles' / (release['hash'] + '.arboricx') 31 assert hashlib.sha256(bundle.read_bytes()).hexdigest() == release['hash'] 32 names = list(read(bundle)[3]) 33 note = notes.get(short, {}) if release['version'] == '0.1.0' else {} 34 alias = note.get('alias', ''.join(word.capitalize() for word in short.split('-'))) 35 description = note.get('description', f'{short.replace("-", " ").capitalize()} library; inspect its complete interface and source reference below.') 36 filename = release['name'] + '-' + release['version'] + '.md' 37 rows.append(dict(release, title=short, description=description, alias=alias, 38 names=names, reference='libraries/' + filename, **({'usage': note['usage']} if 'usage' in note else {}))) 39 source = (root / release['title']).resolve() 40 assert source.is_relative_to(root / 'lib') and source.is_file() 41 relative = source.relative_to(root).as_posix() 42 # Source is a labelled checkout reference; export membership comes from 43 # the immutable published bundle, including inherited/reexported names. 44 write('source/' + relative + '.txt', source.read_text()) 45 write('libraries/' + filename, 46 f'# {short} · {release["version"]}\n\n{description}\n\n' 47 f'```tricu\n!import {release["manifest"]} as {alias}\n```\n\n' 48 f'Package: `{release["name"]}`\n\nManifest: `{release["manifest"]}`\n\n' 49 f'Bundle SHA-256: `{release["hash"]}`\n\n' 50 f'[Download bundle](https://arbori.cx/_arboricx/bundle/hash/{release["hash"]})\n\n' 51 '## Calling conventions\n\n' + note.get('usage', 'Consult the source and protocol guides for argument order, encoding and effects.') + '\n\n' 52 '[Complete examples](../examples.md) · [Language features](../language-features.md) · [Contracts](../contracts.md) · [Host ABI](../host-abi.md) · [Parser protocol](../parser.md)\n\n' 53 f'## Complete published interface ({len(names)} exports)\n\n```text\n' + '\n'.join(names) + '\n```\n\n' 54 f'## Source reference\n\n[Repository source](../source/{relative}.txt) from this site build. ' 55 f'Source SHA-256: `{hashlib.sha256(source.read_bytes()).hexdigest()}`. ' 56 'The export list above describes the selected published bundle; repository source is provided for implementation context. ' 57 'Workspace import names in source resolve through the [library directory](../libraries.md) when adapting code for the playground.\n') 58 write('libraries.md', '# Published tricu libraries\n\nChoose a release to get its complete exports, manifest import and calling conventions. ' 59 'The playground picker initially shows the newest version of each library; all releases are listed here. ' 60 '[Start with the playground guide](getting-started.md).\n\n' + '\n'.join( 61 f'- [{r["title"]} {r["version"]}]({r["reference"]}) — {r["description"]}' for r in rows) + '\n') 62 # Publish readable source and language/protocol contracts at ordinary HTTP URLs. 63 for path in (root / 'lib').rglob('*.tri'): 64 write('source/' + path.relative_to(root).as_posix() + '.txt', path.read_text()) 65 for path in (root / 'docs').glob('*.md'): 66 text = path.read_text().replace('.md.html', '.md') 67 text = re.sub(r'\]\(\.\./lib/([^)]*?\.tri)(#[^)]*)?\)', lambda m: '](source/lib/' + m[1] + '.txt' + (m[2] or '') + ')', text) 68 text = re.sub(r'\]\(\.\./([^)]*)\)', r'](https://git.eversole.co/James/tricu/file/\1)', text) 69 write(path.name, text) 70 write('getting-started.md', (site / 'reference-input/getting-started.md').read_text()) 71 # Examples pin explicit release versions; adding a publication cannot silently 72 # change their program meaning or independent expected results. 73 pins = {r['name'].removeprefix('tricu-lib-'): r['manifest'] for r in rows if r['version'] == '0.1.0'} 74 examples = json.loads((site / 'reference-input/examples.json').read_text()) 75 for example in examples: 76 example['source'] = re.sub(r'\{\{([^}]+)\}\}', lambda m: pins[m[1]], example['source']) 77 for row in rows: 78 example = next((e for e in examples if row['manifest'] in e['source']), None) 79 if example: 80 row['example'] = example['source'] + '\nExpression: ' + example['expression'] + '\nInterpretation: ' + dict(number='Natural number', string='Text')[example['output']] + '\nExecution: ' + ('Pure value' if example['mode'] == 'pure' else 'Text / line IO') + '\nExpected result: ' + example['expected'] 81 write('libraries.json', json.dumps(dict(schema=1, releases=rows), indent=2) + '\n') 82 write('examples.json', json.dumps(examples, indent=2) + '\n') 83 write('examples.md', '# Playground examples\n\nReplace Source with a complete module below. Wait for Compiled, enter its expression, choose its settings and Evaluate.\n\n' + '\n'.join( 84 f'## {e["title"]}\n\n```tricu\n{e["source"]}```\n\nExpression: `{e["expression"]}`. ' 85 f'Output: **Evaluated result**. Interpretation: **{dict(number="Natural number", string="Text")[e["output"]]}**. ' 86 f'Execution: **{"Pure value" if e["mode"] == "pure" else "Text / line IO"}**.\n\n' 87 f'Expected result: `{e["expected"]}`.\n\n' + (f'Terminal output:\n\n```text\n{e["terminal"]}```\n\n' if e['terminal'] else '') for e in examples)) 88 (output / 'llms.txt').write_text('''# Arboricx and tricu 89 90 > tricu is a programming language built on Tree Calculus. Arboricx carries compiled programs between hosts. This site is the language homepage and browser playground. 91 92 ## Write programs 93 94 - [Playground guide](./reference/getting-started.md): source, expressions, imports, output settings, IO, diagnostics and sharing. 95 - [Library directory](./reference/libraries.md): complete published interfaces, versions, exact manifest imports and calling conventions. 96 - [Machine-readable library index](./reference/libraries.json): the same metadata used by Add import. 97 - [Language features](./reference/language-features.md): patterns, recursion, local bindings, sequencing and library protocols. 98 - [Language contract](./reference/language.md): source syntax and semantics. 99 - [Worked examples](./reference/examples.md): complete programs, settings and expected results. 100 - [Modules and packages](./reference/modules.md): source modules and portable bundle identities. 101 102 ## Protocols and boundaries 103 104 - [Contracts](./reference/contracts.md): input validation and Result protocols. 105 - [Parser combinators](./reference/parser.md): replies, progress and rollback. 106 - [Host ABI](./reference/host-abi.md): IO actions and host capabilities. 107 - [Native CLI](./reference/build-and-run.md): run and package programs outside the browser. 108 - [License](./reference/LICENSE.txt): source and documentation licensing. 109 110 Start with the playground guide, then fetch the relevant library release pages. Static references are available without executing WebAssembly. 111 ''') 112 113 114 if __name__ == '__main__': 115 parser = argparse.ArgumentParser(description=__doc__) 116 parser.add_argument('--root', type=Path, required=True) 117 parser.add_argument('--output', type=Path, required=True) 118 args = parser.parse_args() 119 generate(args.root.resolve(), args.output.resolve())