diff --git a/CLAUDE.md b/CLAUDE.md index 407d9f8..3e5866a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -53,6 +53,19 @@ development topics: - `docs/src/program/algorithm-index.md` algorithm documentation +## Algorithm registry + +`docs/algorithms/registry.json` is the central mapping +between algorithm specifications (`.tex` pseudocode in +`docs/algorithms/`) and their assembly implementations +(`.s` files in `program/src/dropset/`). Each entry maps +an algorithm name to its `asm` file path. The registry +also contains `syscalls` (Solana runtime syscall URLs) +and `cpis` (cross-program invocation target URLs). + +Use the registry when comparing spec vs implementation, +e.g. verifying a `.tex` algorithm matches its `.s` file. + ## Key commands ```bash diff --git a/cfg/dictionary.txt b/cfg/dictionary.txt index 6b7ccf1..3a8f034 100644 --- a/cfg/dictionary.txt +++ b/cfg/dictionary.txt @@ -2,6 +2,7 @@ bindgen clippy clrs codegen +cpis dasmac dropset dtolnay diff --git a/docs/.vitepress/buildAlgorithmIndex.js b/docs/.vitepress/buildAlgorithmIndex.js index 7ebb455..12aadfa 100644 --- a/docs/.vitepress/buildAlgorithmIndex.js +++ b/docs/.vitepress/buildAlgorithmIndex.js @@ -2,6 +2,8 @@ // test case files for "// Verifies:" tags. Outputs algorithms/index.json // with deps, reverse deps, page locations, and associated test cases. // Algorithm name is the filename stem (used as key and display name). +// The manually maintained registry.json maps each algorithm to its +// assembly implementation and defines external syscall URLs. import { readFileSync, writeFileSync, readdirSync } from "fs"; import { join, basename, relative } from "path"; @@ -17,6 +19,9 @@ const CASES_DIR = join( "cases", ); const OUTPUT = join(ALGO_DIR, "index.json"); +const REGISTRY = JSON.parse( + readFileSync(join(ALGO_DIR, "registry.json"), "utf-8"), +); // Recursively find all .md files under a directory. function findMdFiles(dir) { @@ -42,10 +47,17 @@ export function buildAlgorithmIndex() { const calls = new Set(); const syscalls = new Set(); - for (const match of code.matchAll(/\\CALL\{([\w-]+)\}/g)) { + const cpis = new Set(); + for (const match of code.matchAll( + /\\CALL\{([\w-]+)\}(?:\{([\w-:]+)\})?/g, + )) { if (match[1] === name) continue; if (match[1].startsWith("sol-")) { syscalls.add(match[1].replace(/-/g, "_")); + // Capture CPI target if present (e.g. sol-invoke-signed-c with arg). + if (match[2]) { + cpis.add(match[2].replace(/-/g, "_")); + } } else { calls.add(match[1]); } @@ -55,10 +67,23 @@ export function buildAlgorithmIndex() { page: null, calls: [...calls], syscalls: [...syscalls], + cpis: [...cpis], calledBy: [], }; } + // Validate registry against .tex files. + const texNames = new Set(Object.keys(index)); + const regNames = new Set(Object.keys(REGISTRY.algorithms)); + for (const name of texNames) { + if (!regNames.has(name)) + throw new Error(`${name}.tex has no entry in registry.json`); + } + for (const name of regNames) { + if (!texNames.has(name)) + throw new Error(`registry.json lists "${name}" but no .tex file exists`); + } + // Filter calls to only reference known algorithms (removes notation-only // names like "Store" that have no .tex definition). for (const entry of Object.values(index)) { @@ -69,7 +94,7 @@ export function buildAlgorithmIndex() { for (const fullPath of findMdFiles(SRC_DIR)) { const md = readFileSync(fullPath, "utf-8"); const relPath = relative(SRC_DIR, fullPath); - for (const match of md.matchAll(/