#!/usr/bin/env node "use strict"; const fs = require("fs"); const path = require("path"); const repoRoot = path.resolve(__dirname, ".."); const stdDir = path.join(repoRoot, "lib", "std"); const outputPath = path.join(repoRoot, "docs", "language", "STDLIB_API.md"); const cargoTomlPath = path.join(repoRoot, "compiler", "Cargo.toml"); function tokenize(source) { const tokens = []; const pattern = /\(|\)|[^\s()]+/g; let match; while ((match = pattern.exec(source)) !== null) { tokens.push(match[0]); } return tokens; } function parseList(tokens, cursor) { if (tokens[cursor.index] !== "(") { throw new Error(`expected list at token ${cursor.index}`); } cursor.index += 1; const list = []; while (cursor.index < tokens.length && tokens[cursor.index] !== ")") { if (tokens[cursor.index] === "(") { list.push(parseList(tokens, cursor)); } else { list.push(tokens[cursor.index]); cursor.index += 1; } } if (tokens[cursor.index] !== ")") { throw new Error("unterminated list"); } cursor.index += 1; return list; } function moduleForm(source, file) { const tokens = tokenize(source); if (tokens[0] !== "(") { throw new Error(`${file}: expected module form`); } const form = parseList(tokens, { index: 0 }); if (form[0] !== "module" || typeof form[1] !== "string") { throw new Error(`${file}: expected (module name ...) form`); } const exportForm = form.find((item) => Array.isArray(item) && item[0] === "export"); if (!exportForm) { throw new Error(`${file}: module must have an explicit export list`); } return { name: form[1], exports: exportForm.slice(1), }; } function stdModules() { return fs .readdirSync(stdDir) .filter((name) => name.endsWith(".slo")) .sort() .map((fileName) => { const relativePath = path.join("lib", "std", fileName); const source = fs.readFileSync(path.join(stdDir, fileName), "utf8"); const module = moduleForm(source, relativePath); return { fileName, relativePath, name: module.name, exports: module.exports, }; }); } function packageVersion() { const cargoToml = fs.readFileSync(cargoTomlPath, "utf8"); const versionMatch = cargoToml.match(/^version\s*=\s*"([^"]+)"$/m); if (!versionMatch) { throw new Error("compiler/Cargo.toml must declare a package version"); } return versionMatch[1]; } function render(modules, version) { const totalExports = modules.reduce((count, module) => count + module.exports.length, 0); const out = []; out.push("# Slovo Standard Library API Catalog"); out.push(""); out.push("Generated from `lib/std/*.slo` by `scripts/render-stdlib-api-doc.sh`."); out.push("Do not edit this file by hand."); out.push(""); out.push("## Stability Tiers"); out.push(""); out.push("- `beta-supported`: exported from `lib/std` and covered by source-search, promotion, or facade gates in the current beta line."); out.push(`- \`experimental\`: not used for exported \`lib/std\` helpers in \`${version}\`; future releases may mark new helpers this way before they graduate.`); out.push("- `internal`: helper names that are not exported from their module; they are intentionally omitted from this catalog."); out.push(""); out.push("The catalog is a beta compatibility aid, not a stable `1.0.0` API freeze."); out.push(""); out.push("## Summary"); out.push(""); out.push(`- Modules: ${modules.length}`); out.push(`- Exported helpers: ${totalExports}`); out.push("- Default tier: `beta-supported`"); out.push(""); out.push("## Modules"); out.push(""); for (const module of modules) { out.push(`### std.${module.name}`); out.push(""); out.push(`- Path: \`${module.relativePath}\``); out.push("- Tier: `beta-supported`"); out.push(`- Exported helpers: ${module.exports.length}`); out.push(""); for (const exported of module.exports) { out.push(`- \`${exported}\``); } out.push(""); } return `${out.join("\n")}\n`; } fs.writeFileSync(outputPath, render(stdModules(), packageVersion()));