diff --git a/README.md b/README.md index 320ce39..3131673 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,7 @@ filesystem APIs. - [Language Manifest](docs/language/MANIFEST.md) - [Language Specification](docs/language/SPEC-v1.md) +- [Standard Library API Catalog](docs/language/STDLIB_API.md) - [Compiler Manifest](docs/compiler/GLAGOL_COMPILER_MANIFEST.md) - [Post-Beta Roadmap](docs/POST_BETA_ROADMAP.md) - [Slovo Whitepaper](docs/papers/SLOVO_WHITEPAPER.md) diff --git a/docs/README.md b/docs/README.md index 9e46e3f..a0a6dcd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,5 +9,6 @@ This directory contains the public documentation for Slovo and Glagol. - `papers/`: technical whitepapers and generated PDF publications - `assets/`: fonts and CSS used for PDF generation -Use `../scripts/render-doc-pdfs.sh` from the repository root to regenerate the -publication PDFs. +Use `../scripts/render-stdlib-api-doc.sh` from the repository root to regenerate +the standard-library API catalog. Use `../scripts/render-doc-pdfs.sh` to +regenerate the publication PDFs. diff --git a/docs/language/STDLIB_API.md b/docs/language/STDLIB_API.md new file mode 100644 index 0000000..e198d65 --- /dev/null +++ b/docs/language/STDLIB_API.md @@ -0,0 +1,679 @@ +# Slovo Standard Library API Catalog + +Generated from `lib/std/*.slo` by `scripts/render-stdlib-api-doc.sh`. +Do not edit this file by hand. + +## Stability Tiers + +- `beta-supported`: exported from `lib/std` and covered by source-search, promotion, or facade gates in the current beta line. +- `experimental`: not used for exported `lib/std` helpers in `1.0.0-beta.2`; future releases may mark new helpers this way before they graduate. +- `internal`: helper names that are not exported from their module; they are intentionally omitted from this catalog. + +The catalog is a beta compatibility aid, not a stable `1.0.0` API freeze. + +## Summary + +- Modules: 17 +- Exported helpers: 539 +- Default tier: `beta-supported` + +## Modules + +### std.cli + +- Path: `lib/std/cli.slo` +- Tier: `beta-supported` +- Exported helpers: 26 + +- `arg_text_result` +- `arg_text_option` +- `arg_i32_result` +- `arg_i32_option` +- `arg_i32_or_zero` +- `arg_i32_or` +- `arg_u32_result` +- `arg_u32_option` +- `arg_u32_or_zero` +- `arg_u32_or` +- `arg_i64_result` +- `arg_i64_option` +- `arg_i64_or_zero` +- `arg_i64_or` +- `arg_u64_result` +- `arg_u64_option` +- `arg_u64_or_zero` +- `arg_u64_or` +- `arg_f64_result` +- `arg_f64_option` +- `arg_f64_or_zero` +- `arg_f64_or` +- `arg_bool_result` +- `arg_bool_option` +- `arg_bool_or_false` +- `arg_bool_or` + +### std.env + +- Path: `lib/std/env.slo` +- Tier: `beta-supported` +- Exported helpers: 29 + +- `get` +- `get_result` +- `get_option` +- `has` +- `get_or` +- `get_i32_result` +- `get_i32_option` +- `get_i32_or_zero` +- `get_i32_or` +- `get_u32_result` +- `get_u32_option` +- `get_u32_or_zero` +- `get_u32_or` +- `get_i64_result` +- `get_i64_option` +- `get_i64_or_zero` +- `get_i64_or` +- `get_u64_result` +- `get_u64_option` +- `get_u64_or_zero` +- `get_u64_or` +- `get_f64_result` +- `get_f64_option` +- `get_f64_or_zero` +- `get_f64_or` +- `get_bool_result` +- `get_bool_option` +- `get_bool_or_false` +- `get_bool_or` + +### std.fs + +- Path: `lib/std/fs.slo` +- Tier: `beta-supported` +- Exported helpers: 43 + +- `read_text` +- `read_text_result` +- `read_text_option` +- `write_text_status` +- `write_text_result` +- `exists` +- `is_file` +- `is_dir` +- `remove_file_result` +- `create_dir_result` +- `remove_file_ok` +- `create_dir_ok` +- `open_text_read_result` +- `read_open_text_result` +- `close_result` +- `read_text_via_handle_result` +- `close_ok` +- `read_text_or` +- `write_text_ok` +- `read_i32_result` +- `read_i32_option` +- `read_i32_or_zero` +- `read_i32_or` +- `read_u32_result` +- `read_u32_option` +- `read_u32_or_zero` +- `read_u32_or` +- `read_i64_result` +- `read_i64_option` +- `read_i64_or_zero` +- `read_i64_or` +- `read_u64_result` +- `read_u64_option` +- `read_u64_or_zero` +- `read_u64_or` +- `read_f64_result` +- `read_f64_option` +- `read_f64_or_zero` +- `read_f64_or` +- `read_bool_result` +- `read_bool_option` +- `read_bool_or_false` +- `read_bool_or` + +### std.io + +- Path: `lib/std/io.slo` +- Tier: `beta-supported` +- Exported helpers: 41 + +- `print_i32_zero` +- `print_u32_zero` +- `print_i64_zero` +- `print_u64_zero` +- `print_f64_zero` +- `print_string_zero` +- `print_bool_zero` +- `print_i32_value` +- `print_u32_value` +- `print_i64_value` +- `print_u64_value` +- `print_f64_value` +- `print_string_value` +- `print_bool_value` +- `read_stdin_result` +- `read_stdin_option` +- `read_stdin_or` +- `read_stdin_i32_result` +- `read_stdin_i32_option` +- `read_stdin_i32_or_zero` +- `read_stdin_i32_or` +- `read_stdin_u32_result` +- `read_stdin_u32_option` +- `read_stdin_u32_or_zero` +- `read_stdin_u32_or` +- `read_stdin_i64_result` +- `read_stdin_i64_option` +- `read_stdin_i64_or_zero` +- `read_stdin_i64_or` +- `read_stdin_u64_result` +- `read_stdin_u64_option` +- `read_stdin_u64_or_zero` +- `read_stdin_u64_or` +- `read_stdin_f64_result` +- `read_stdin_f64_option` +- `read_stdin_f64_or_zero` +- `read_stdin_f64_or` +- `read_stdin_bool_result` +- `read_stdin_bool_option` +- `read_stdin_bool_or_false` +- `read_stdin_bool_or` + +### std.math + +- Path: `lib/std/math.slo` +- Tier: `beta-supported` +- Exported helpers: 45 + +- `abs_i32` +- `neg_i32` +- `rem_i32` +- `bit_and_i32` +- `bit_or_i32` +- `bit_xor_i32` +- `is_even_i32` +- `is_odd_i32` +- `min_i32` +- `max_i32` +- `clamp_i32` +- `square_i32` +- `cube_i32` +- `is_zero_i32` +- `is_positive_i32` +- `is_negative_i32` +- `in_range_i32` +- `abs_i64` +- `neg_i64` +- `rem_i64` +- `bit_and_i64` +- `bit_or_i64` +- `bit_xor_i64` +- `is_even_i64` +- `is_odd_i64` +- `min_i64` +- `max_i64` +- `clamp_i64` +- `square_i64` +- `cube_i64` +- `is_zero_i64` +- `is_positive_i64` +- `is_negative_i64` +- `in_range_i64` +- `abs_f64` +- `neg_f64` +- `min_f64` +- `max_f64` +- `clamp_f64` +- `square_f64` +- `cube_f64` +- `is_zero_f64` +- `is_positive_f64` +- `is_negative_f64` +- `in_range_f64` + +### std.num + +- Path: `lib/std/num.slo` +- Tier: `beta-supported` +- Exported helpers: 14 + +- `i32_to_i64` +- `i32_to_f64` +- `i64_to_f64` +- `i64_to_i32_result` +- `f64_to_i32_result` +- `f64_to_i64_result` +- `i32_to_string` +- `u32_to_string` +- `i64_to_string` +- `u64_to_string` +- `f64_to_string` +- `i64_to_i32_or` +- `f64_to_i32_or` +- `f64_to_i64_or` + +### std.option + +- Path: `lib/std/option.slo` +- Tier: `beta-supported` +- Exported helpers: 49 + +- `some_i32` +- `none_i32` +- `is_some_i32` +- `is_none_i32` +- `unwrap_some_i32` +- `unwrap_or_i32` +- `some_or_err_i32` +- `some_u32` +- `none_u32` +- `is_some_u32` +- `is_none_u32` +- `unwrap_some_u32` +- `unwrap_or_u32` +- `some_or_err_u32` +- `some_i64` +- `none_i64` +- `is_some_i64` +- `is_none_i64` +- `unwrap_some_i64` +- `unwrap_or_i64` +- `some_or_err_i64` +- `some_u64` +- `none_u64` +- `is_some_u64` +- `is_none_u64` +- `unwrap_some_u64` +- `unwrap_or_u64` +- `some_or_err_u64` +- `some_f64` +- `none_f64` +- `is_some_f64` +- `is_none_f64` +- `unwrap_some_f64` +- `unwrap_or_f64` +- `some_or_err_f64` +- `some_bool` +- `none_bool` +- `is_some_bool` +- `is_none_bool` +- `unwrap_some_bool` +- `unwrap_or_bool` +- `some_or_err_bool` +- `some_string` +- `none_string` +- `is_some_string` +- `is_none_string` +- `unwrap_some_string` +- `unwrap_or_string` +- `some_or_err_string` + +### std.process + +- Path: `lib/std/process.slo` +- Tier: `beta-supported` +- Exported helpers: 31 + +- `argc` +- `arg` +- `arg_result` +- `arg_option` +- `has_arg` +- `arg_or` +- `arg_or_empty` +- `arg_i32_result` +- `arg_i32_option` +- `arg_i32_or_zero` +- `arg_i32_or` +- `arg_u32_result` +- `arg_u32_option` +- `arg_u32_or_zero` +- `arg_u32_or` +- `arg_i64_result` +- `arg_i64_option` +- `arg_i64_or_zero` +- `arg_i64_or` +- `arg_u64_result` +- `arg_u64_option` +- `arg_u64_or_zero` +- `arg_u64_or` +- `arg_f64_result` +- `arg_f64_option` +- `arg_f64_or_zero` +- `arg_f64_or` +- `arg_bool_result` +- `arg_bool_option` +- `arg_bool_or_false` +- `arg_bool_or` + +### std.random + +- Path: `lib/std/random.slo` +- Tier: `beta-supported` +- Exported helpers: 2 + +- `random_i32` +- `random_i32_non_negative` + +### std.result + +- Path: `lib/std/result.slo` +- Tier: `beta-supported` +- Exported helpers: 56 + +- `ok_i32` +- `err_i32` +- `is_ok_i32` +- `is_err_i32` +- `unwrap_ok_i32` +- `unwrap_err_i32` +- `unwrap_or_i32` +- `ok_or_none_i32` +- `ok_u32` +- `err_u32` +- `is_ok_u32` +- `is_err_u32` +- `unwrap_ok_u32` +- `unwrap_err_u32` +- `unwrap_or_u32` +- `ok_or_none_u32` +- `ok_i64` +- `err_i64` +- `is_ok_i64` +- `is_err_i64` +- `unwrap_ok_i64` +- `unwrap_err_i64` +- `unwrap_or_i64` +- `ok_or_none_i64` +- `ok_u64` +- `err_u64` +- `is_ok_u64` +- `is_err_u64` +- `unwrap_ok_u64` +- `unwrap_err_u64` +- `unwrap_or_u64` +- `ok_or_none_u64` +- `ok_string` +- `err_string` +- `is_ok_string` +- `is_err_string` +- `unwrap_ok_string` +- `unwrap_err_string` +- `unwrap_or_string` +- `ok_or_none_string` +- `ok_f64` +- `err_f64` +- `is_ok_f64` +- `is_err_f64` +- `unwrap_ok_f64` +- `unwrap_err_f64` +- `unwrap_or_f64` +- `ok_or_none_f64` +- `ok_bool` +- `err_bool` +- `is_ok_bool` +- `is_err_bool` +- `unwrap_ok_bool` +- `unwrap_err_bool` +- `unwrap_or_bool` +- `ok_or_none_bool` + +### std.string + +- Path: `lib/std/string.slo` +- Tier: `beta-supported` +- Exported helpers: 26 + +- `len` +- `concat` +- `parse_i32_result` +- `parse_i32_option` +- `parse_u32_result` +- `parse_u32_option` +- `parse_i64_result` +- `parse_i64_option` +- `parse_u64_result` +- `parse_u64_option` +- `parse_f64_result` +- `parse_f64_option` +- `parse_bool_result` +- `parse_bool_option` +- `parse_i32_or_zero` +- `parse_u32_or_zero` +- `parse_i64_or_zero` +- `parse_u64_or_zero` +- `parse_f64_or_zero` +- `parse_bool_or_false` +- `parse_i32_or` +- `parse_u32_or` +- `parse_i64_or` +- `parse_u64_or` +- `parse_f64_or` +- `parse_bool_or` + +### std.time + +- Path: `lib/std/time.slo` +- Tier: `beta-supported` +- Exported helpers: 2 + +- `monotonic_ms` +- `sleep_ms_zero` + +### std.vec_bool + +- Path: `lib/std/vec_bool.slo` +- Tier: `beta-supported` +- Exported helpers: 35 + +- `empty` +- `append` +- `len` +- `at` +- `singleton` +- `append2` +- `append3` +- `pair` +- `triple` +- `is_empty` +- `index_or` +- `first_or` +- `last_or` +- `index_option` +- `first_option` +- `last_option` +- `index_of_option` +- `last_index_of_option` +- `contains` +- `count_of` +- `concat` +- `take` +- `starts_with` +- `without_prefix` +- `ends_with` +- `without_suffix` +- `drop` +- `reverse` +- `subvec` +- `insert_at` +- `insert_range` +- `replace_at` +- `replace_range` +- `remove_at` +- `remove_range` + +### std.vec_f64 + +- Path: `lib/std/vec_f64.slo` +- Tier: `beta-supported` +- Exported helpers: 35 + +- `empty` +- `append` +- `len` +- `at` +- `singleton` +- `append2` +- `append3` +- `pair` +- `triple` +- `is_empty` +- `index_or` +- `first_or` +- `last_or` +- `index_option` +- `first_option` +- `last_option` +- `index_of_option` +- `last_index_of_option` +- `contains` +- `sum` +- `concat` +- `take` +- `starts_with` +- `without_prefix` +- `ends_with` +- `without_suffix` +- `drop` +- `reverse` +- `subvec` +- `insert_at` +- `insert_range` +- `replace_at` +- `replace_range` +- `remove_at` +- `remove_range` + +### std.vec_i32 + +- Path: `lib/std/vec_i32.slo` +- Tier: `beta-supported` +- Exported helpers: 39 + +- `empty` +- `append` +- `len` +- `at` +- `singleton` +- `append2` +- `append3` +- `pair` +- `triple` +- `repeat` +- `range` +- `range_from_zero` +- `is_empty` +- `index_or` +- `first_or` +- `last_or` +- `index_option` +- `first_option` +- `last_option` +- `index_of_option` +- `last_index_of_option` +- `count_of` +- `contains` +- `sum` +- `concat` +- `take` +- `starts_with` +- `without_prefix` +- `ends_with` +- `without_suffix` +- `drop` +- `reverse` +- `subvec` +- `insert_at` +- `insert_range` +- `replace_at` +- `replace_range` +- `remove_at` +- `remove_range` + +### std.vec_i64 + +- Path: `lib/std/vec_i64.slo` +- Tier: `beta-supported` +- Exported helpers: 31 + +- `empty` +- `append` +- `len` +- `at` +- `singleton` +- `append2` +- `append3` +- `pair` +- `triple` +- `is_empty` +- `index_or` +- `first_or` +- `last_or` +- `index_option` +- `first_option` +- `last_option` +- `index_of_option` +- `last_index_of_option` +- `contains` +- `sum` +- `concat` +- `take` +- `drop` +- `reverse` +- `subvec` +- `insert_at` +- `insert_range` +- `replace_at` +- `replace_range` +- `remove_at` +- `remove_range` + +### std.vec_string + +- Path: `lib/std/vec_string.slo` +- Tier: `beta-supported` +- Exported helpers: 35 + +- `empty` +- `append` +- `len` +- `at` +- `singleton` +- `append2` +- `append3` +- `pair` +- `triple` +- `is_empty` +- `index_or` +- `first_or` +- `last_or` +- `index_option` +- `first_option` +- `last_option` +- `index_of_option` +- `last_index_of_option` +- `contains` +- `count_of` +- `concat` +- `take` +- `starts_with` +- `without_prefix` +- `ends_with` +- `without_suffix` +- `drop` +- `reverse` +- `subvec` +- `insert_at` +- `insert_range` +- `replace_at` +- `replace_range` +- `remove_at` +- `remove_range` + diff --git a/scripts/release-gate.sh b/scripts/release-gate.sh index 01914af..e9064da 100755 --- a/scripts/release-gate.sh +++ b/scripts/release-gate.sh @@ -8,6 +8,14 @@ compiler_dir="${repo_root}/compiler" cd "${repo_root}" git diff --check bash -n scripts/install.sh +bash -n scripts/render-stdlib-api-doc.sh + +"${repo_root}/scripts/render-stdlib-api-doc.sh" +git diff --check +if ! git diff --quiet -- docs/language/STDLIB_API.md; then + echo "standard library API catalog changed; review and commit generated docs before release" >&2 + exit 1 +fi required_pdfs=( "docs/papers/SLOVO_WHITEPAPER.pdf" @@ -62,4 +70,4 @@ cargo test --test promotion_gate -- --ignored cargo test --test binary_smoke -- --ignored cargo test --test llvm_smoke -- --ignored -echo "release gate passed: docs, fmt, tests, promotion, binary, and LLVM smoke checks completed" +echo "release gate passed: docs, stdlib API catalog, fmt, tests, promotion, binary, and LLVM smoke checks completed" diff --git a/scripts/render-stdlib-api-doc.js b/scripts/render-stdlib-api-doc.js new file mode 100755 index 0000000..8a8415b --- /dev/null +++ b/scripts/render-stdlib-api-doc.js @@ -0,0 +1,128 @@ +#!/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())); diff --git a/scripts/render-stdlib-api-doc.sh b/scripts/render-stdlib-api-doc.sh new file mode 100755 index 0000000..79eb955 --- /dev/null +++ b/scripts/render-stdlib-api-doc.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env sh +set -eu + +script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +node "${script_dir}/render-stdlib-api-doc.js"