diff --git a/.llm/BETA_10_DEVELOPER_EXPERIENCE_API_DISCOVERY.md b/.llm/BETA_10_DEVELOPER_EXPERIENCE_API_DISCOVERY.md new file mode 100644 index 0000000..297ed53 --- /dev/null +++ b/.llm/BETA_10_DEVELOPER_EXPERIENCE_API_DISCOVERY.md @@ -0,0 +1,62 @@ +# 1.0.0-beta.10 Developer Experience API Discovery + +Status: release scope for `1.0.0-beta.10`. + +`1.0.0-beta.10` is a tooling/docs slice on top of the beta.8 concrete alias +foundation and beta.9 collection alias unification work. It improves API +discovery for the existing source-authored standard library and adds +editor-facing source metadata without adding new source-language execution +semantics, compiler-known runtime names, or runtime helpers. + +## Scope + +- Upgrade `scripts/render-stdlib-api-doc.js` so the generated + `docs/language/STDLIB_API.md` catalog lists exact exported helper + signatures, not only helper names. +- Parse each `lib/std/*.slo` module, collect module-local `(type ...)` + aliases, and normalize those aliases recursively in public helper + signatures. +- Verify exported helper names have matching `(fn ...)` forms. +- Omit non-exported helper functions and `(type ...)` aliases from the public + catalog. +- Regenerate `docs/language/STDLIB_API.md`. +- Add `glagol symbols ` for deterministic + `slovo.symbols` S-expression metadata over modules, imports, exports, + aliases, structs, enums, functions, tests, spans/ranges, and workspace + package names. +- Update README, language docs, compiler docs, and the post-beta roadmap to + describe beta API discovery clearly. + +## Public Contract + +The generated catalog is a beta discovery aid for the current `lib/std` +surface. Public signatures show concrete types such as `(vec i32)`, +`(option string)`, and `(result u64 i32)` instead of module-local alias names +such as `VecI32`, `OptionString`, or `ResultU64`. + +The catalog remains generated from source and is not a hand-maintained API +freeze. It can help reviewers see current helper signatures, but it does not +make those helpers stable `1.0.0` standard-library APIs. + +The `symbols` command is an editor-integration building block, not an LSP +server. Its output is deterministic machine-readable S-expression text and +uses the beta10 `slovo.symbols` schema label. + +## Explicit Non-Scope + +- no executable generics +- no generic aliases or parameterized aliases +- no maps or sets +- no traits, inference, monomorphization, or iterators +- no new compiler-known runtime names +- no runtime helper or ABI/layout changes +- no LSP server, watch mode, SARIF, or daemon protocol +- no stable `1.0.0` standard-library freeze + +## Checks + +Focused checks for this slice: + +- `node scripts/render-stdlib-api-doc.js` +- `cargo test --test symbols_beta10` +- `git diff --check -- scripts/render-stdlib-api-doc.js docs/language/STDLIB_API.md compiler/src/main.rs compiler/src/symbols.rs compiler/tests/symbols_beta10.rs README.md docs/language/SPEC-v1.md docs/language/ROADMAP.md docs/language/RELEASE_NOTES.md docs/compiler/ROADMAP.md docs/compiler/RELEASE_NOTES.md docs/POST_BETA_ROADMAP.md .llm/BETA_10_DEVELOPER_EXPERIENCE_API_DISCOVERY.md` diff --git a/.llm/reviews/BETA_10_RELEASE_REVIEW.md b/.llm/reviews/BETA_10_RELEASE_REVIEW.md new file mode 100644 index 0000000..1ae72ac --- /dev/null +++ b/.llm/reviews/BETA_10_RELEASE_REVIEW.md @@ -0,0 +1,47 @@ +# 1.0.0-beta.10 Release Review + +Status: ready for publication after the controller release gate. + +## Verdict + +No blocking issues found after integrating the stdlib API catalog worker and +the compiler symbol-metadata worker. + +## Scope Checked + +- `docs/language/STDLIB_API.md` now lists exact exported `lib/std` helper + signatures instead of helper names only. +- `scripts/render-stdlib-api-doc.js` verifies exported helpers have matching + `(fn ...)` forms, omits non-exported helpers and aliases, and normalizes + module-local concrete aliases in public signatures. +- `glagol symbols ` emits deterministic + `slovo.symbols` metadata for modules, imports, exports, aliases, structs, + enums, functions, tests, spans/ranges, and workspace package labels. +- README, roadmaps, release notes, specification text, whitepapers, and PDFs + describe beta10 as tooling/API-discovery work only. +- Docs do not claim executable generics, maps, sets, new runtime helpers, + stable ABI/layout, LSP/watch protocols, or a stable stdlib API freeze. + +## Verification + +- `node scripts/render-stdlib-api-doc.js` +- `cargo fmt --check` +- `cargo check` +- `cargo test --test symbols_beta10` +- `cargo test --test dx_v1_7` +- `cargo test --test cli_v1_1` +- `cargo test --test promotion_gate` +- `MD_TO_PDF_PACKAGE= ./scripts/render-doc-pdfs.sh` +- `git diff --check` +- `./scripts/release-gate.sh` + +Final full `./scripts/release-gate.sh` result: passed docs, generated stdlib +API catalog consistency, private-publication text checks, formatter checks, the +full cargo test suite, ignored promotion checks, binary smoke, and LLVM smoke. + +## Residual Risk + +The `symbols` command is a stable-shaped beta metadata export, not a complete +editor protocol. Future editor work still needs a separate LSP/watch contract, +diagnostic stability policy, local package API docs, and compatibility tests +before claiming full editor integration. diff --git a/README.md b/README.md index 9519f31..85ca828 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This repository is the canonical public monorepo for the language design, standard library source, compiler, runtime, examples, benchmarks, and technical documents. -Current release: `1.0.0-beta.9`. +Current release: `1.0.0-beta.10`. ## Repository Layout @@ -24,7 +24,7 @@ scripts/ local release and document tooling ## Beta Scope -`1.0.0-beta.9` keeps the `1.0.0-beta` language baseline, includes the +`1.0.0-beta.10` keeps the `1.0.0-beta` language baseline, includes the `1.0.0-beta.1` tooling/install hardening slice, the `1.0.0-beta.2` runtime/resource foundation bundle, the `1.0.0-beta.3` standard-library stabilization bundle, the `1.0.0-beta.4` language-usability diagnostics @@ -32,11 +32,13 @@ bundle, the `1.0.0-beta.5` local package/workspace discipline bundle, and the `1.0.0-beta.6` loopback networking foundation, plus the `1.0.0-beta.7` serialization/data-interchange foundation and the `1.0.0-beta.8` concrete type alias foundation, and the `1.0.0-beta.9` collection alias unification and -generic reservation slice. The language baseline supports practical local +generic reservation slice, plus the `1.0.0-beta.10` developer-experience API +discovery slice. The language baseline supports practical local command-line, file, and loopback-network programs with: - modules, explicit imports, packages, and local workspaces -- `new`, `check`, `fmt`, `test`, `doc`, and `build` +- `new`, `check`, `fmt`, `test`, `doc`, `symbols`, `build`, `run`, and + `clean` - `i32`, `i64`, `u32`, `u64`, `f64`, `bool`, `string`, and internal `unit` - structs, enums, fixed arrays, concrete vectors, option/result families, and current `match` @@ -47,15 +49,24 @@ command-line, file, and loopback-network programs with: - JSON string quoting and compact JSON text construction through `std.json` - hosted native builds through LLVM IR, Clang, and `runtime/runtime.c` -Still deferred before stable: generics, maps/sets, broad package registry -semantics, DNS/TLS/async networking, LSP/watch/debug-adapter guarantees, -stable ABI and layout, and a stable standard-library compatibility freeze. +The generated standard-library API catalog is a beta discovery aid: it lists +exported helper signatures from `lib/std/*.slo`, normalizes module-local +concrete aliases such as `VecI32` and `ResultU64` to their concrete public +types, and omits non-exported helpers and `(type ...)` aliases. +`glagol symbols ` emits deterministic +editor-facing S-expression metadata for modules, imports, exports, aliases, +structs, enums, functions, tests, source spans, and workspace package names. -The next likely language slice after `1.0.0-beta.9` should continue from the -reserved generic and collection diagnostics without claiming executable -generics, maps, sets, traits, inference, monomorphization, iterators, ABI -stability, or a standard-library API freeze until the contract and gates are -explicit. +Still deferred before stable: executable generics, maps/sets, broad package +registry semantics, DNS/TLS/async networking, LSP/watch/debug-adapter +guarantees, stable ABI and layout, runtime changes for generic collections, +and a stable standard-library compatibility freeze. + +The next likely language slice after `1.0.0-beta.10` should continue from the +developer-experience and reserved generic/collection diagnostics without +claiming executable generics, maps, sets, traits, inference, +monomorphization, iterators, ABI stability, runtime changes, or a +standard-library API freeze until the contract and gates are explicit. ## Build And Test @@ -230,6 +241,26 @@ and documentation only. It does not implement executable generics, maps, sets, traits, inference, monomorphization, iterators, stable ABI/layout promises, or a stable standard-library API freeze. +## 1.0.0-beta.10 Developer Experience API Discovery + +The `1.0.0-beta.10` release upgrades the generated standard-library API +catalog from exported helper names to exact exported helper signatures. The +renderer verifies that every exported helper has a matching `(fn ...)` form, +normalizes module-local concrete aliases from the public signatures, omits +non-exported helpers and `(type ...)` aliases, and keeps the catalog generated +from `lib/std/*.slo`. + +It also adds `glagol symbols ` as an +editor-facing metadata command. The output is deterministic S-expression text +using `slovo.symbols` schema version `1.0.0-beta.10`; it includes module paths, +source spans/ranges, imports, exports, aliases, structs, enums, functions, +tests, and workspace package labels. + +This release is tooling, documentation, and API-discovery work. It does not +add executable generics, maps, sets, new runtime helpers, new compiler-known +runtime names, ABI/layout guarantees, an LSP server, watch mode, or a stable +standard-library API freeze. + ## Documentation - [Language Manifest](docs/language/MANIFEST.md) diff --git a/compiler/Cargo.lock b/compiler/Cargo.lock index c49dc87..3ee538c 100644 --- a/compiler/Cargo.lock +++ b/compiler/Cargo.lock @@ -4,4 +4,4 @@ version = 3 [[package]] name = "glagol" -version = "1.0.0-beta.9" +version = "1.0.0-beta.10" diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index bdad071..1535702 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "glagol" -version = "1.0.0-beta.9" +version = "1.0.0-beta.10" edition = "2021" description = "Glagol, the first compiler for the Slovo language" license = "MIT OR Apache-2.0" diff --git a/compiler/src/main.rs b/compiler/src/main.rs index bcb4016..56d5078 100644 --- a/compiler/src/main.rs +++ b/compiler/src/main.rs @@ -11,6 +11,7 @@ mod project; mod scaffold; mod sexpr; mod std_runtime; +mod symbols; mod test_runner; mod token; mod types; @@ -94,19 +95,26 @@ fn run_invocation_inner(invocation: Invocation) -> ! { if invocation.mode == Mode::Doc { run_doc(invocation); } + if invocation.mode == Mode::Symbols && project::is_project_input(&invocation.path) { + run_project_symbols(invocation); + } if invocation.mode == Mode::Format && invocation.fmt_action != FmtAction::Stdout { run_fmt_action(invocation); } - let project_capable_mode = matches!(invocation.mode, Mode::Check | Mode::Build | Mode::Run) - || (invocation.mode == Mode::RunTests && invocation.manifest_mode_name == "test"); + let project_capable_mode = matches!( + invocation.mode, + Mode::Check | Mode::Build | Mode::Run | Mode::Symbols + ) || (invocation.mode == Mode::RunTests + && invocation.manifest_mode_name == "test"); if project_capable_mode && project::is_project_input(&invocation.path) { match invocation.mode { Mode::Check => run_project_check(invocation), Mode::RunTests => run_project_test(invocation), Mode::Build => run_project_build(invocation), Mode::Run => run_project_run(invocation), - _ => unreachable!("project mode is selected only for check/test/build/run"), + Mode::Symbols => run_project_symbols(invocation), + _ => unreachable!("project mode is selected only for check/test/build/run/symbols"), } } @@ -211,6 +219,25 @@ fn run_project_run(invocation: Invocation) -> ! { run_native_from_llvm(invocation, output.text, Some(output.artifact), Vec::new()); } +fn run_project_symbols(invocation: Invocation) -> ! { + let loaded = match project::load_project_sources_for_tools(&invocation.path) { + Ok(loaded) => loaded, + Err(failure) => exit_tool_failure(invocation, failure), + }; + let output = match symbols::render_project(&loaded) { + Ok(output) => output, + Err(diagnostics) => exit_tool_failure( + invocation.clone(), + project::ToolFailure { + diagnostics, + sources: loaded.sources.clone(), + artifact: Some(loaded.artifact.clone()), + }, + ), + }; + finish_symbols_output(invocation, output, Some(&loaded.artifact)); +} + fn exit_project_failure(invocation: Invocation, failure: project::ProjectTestFailure) -> ! { let rendered = render_source_diagnostics_multi( &failure.diagnostics, @@ -233,11 +260,52 @@ fn exit_project_failure(invocation: Invocation, failure: project::ProjectTestFai process::exit(ExitCode::SourceFailure.code()); } +fn exit_tool_failure(invocation: Invocation, failure: project::ToolFailure) -> ! { + let rendered = render_source_diagnostics_multi( + &failure.diagnostics, + &failure.sources, + invocation.diagnostics, + ); + eprint!("{}", rendered.stderr); + write_manifest_if_requested_with_project( + &invocation, + false, + PrimaryOutput::Diagnostics { + text: &rendered.machine_text, + }, + None, + None, + failure.artifact.as_ref(), + ); + process::exit(ExitCode::SourceFailure.code()); +} + fn run_text_mode(invocation: Invocation, mode: Mode, source: &str) -> ! { if mode == Mode::RunTests { run_test_mode(invocation, source); } + if mode == Mode::Symbols { + match symbols::render_file(&invocation.path, source) { + Ok(output) => finish_symbols_output(invocation, output, None), + Err(diagnostics) => { + let rendered = + render_source_diagnostics(&diagnostics, source, invocation.diagnostics); + eprint!("{}", rendered.stderr); + write_manifest_if_requested( + &invocation, + false, + PrimaryOutput::Diagnostics { + text: &rendered.machine_text, + }, + None, + None, + ); + process::exit(ExitCode::SourceFailure.code()); + } + } + } + let foreign_imports = c_imports_for_manifest(&invocation.path, source); let result = match mode { Mode::EmitLlvm => driver::compile_to_llvm(&invocation.path, source), @@ -247,6 +315,7 @@ fn run_text_mode(invocation: Invocation, mode: Mode, source: &str) -> ! { Mode::InspectLoweringSurface => driver::inspect_lowering_surface(&invocation.path, source), Mode::InspectLoweringChecked => driver::inspect_lowering_checked(&invocation.path, source), Mode::CheckTests => driver::check_tests(&invocation.path, source), + Mode::Symbols => unreachable!("symbols mode is handled separately"), Mode::RunTests => unreachable!("test mode is handled separately"), Mode::Build => unreachable!("build is handled separately"), Mode::Run => unreachable!("run is handled separately"), @@ -315,6 +384,48 @@ fn run_text_mode(invocation: Invocation, mode: Mode, source: &str) -> ! { } } +fn finish_symbols_output( + invocation: Invocation, + output: String, + project_artifact: Option<&project::ProjectArtifact>, +) -> ! { + let primary_output = if let Some(output_path) = invocation.output_path.as_deref() { + if let Err(err) = fs::write(output_path, &output) { + let message = format!("cannot write `{}`: {}", output_path, err); + emit_message_diagnostic( + &message, + "OutputWriteFailed", + ExitCode::ArtifactFailure, + &invocation, + PrimaryOutput::Diagnostics { + text: message.as_str(), + }, + None, + ); + } + PrimaryOutput::Path { + kind: Mode::Symbols.output_kind(), + path: output_path, + } + } else { + print!("{}", output); + PrimaryOutput::Stdout { + kind: Mode::Symbols.output_kind(), + text: &output, + } + }; + + write_manifest_if_requested_with_project( + &invocation, + true, + primary_output, + None, + None, + project_artifact, + ); + process::exit(0); +} + fn run_new(invocation: Invocation) -> ! { match scaffold::create_project( &invocation.path, @@ -1210,7 +1321,7 @@ fn parse_args(raw_args: &[String]) -> Result { command_line: command_line.clone(), })?); } - "check" | "fmt" | "test" | "build" | "run" | "clean" | "new" | "doc" + "check" | "fmt" | "test" | "build" | "run" | "clean" | "new" | "doc" | "symbols" if path.is_none() => { let next = match arg.as_str() { @@ -1222,6 +1333,7 @@ fn parse_args(raw_args: &[String]) -> Result { "clean" => Mode::Clean, "new" => Mode::New, "doc" => Mode::Doc, + "symbols" => Mode::Symbols, _ => unreachable!(), }; set_mode( @@ -1489,6 +1601,7 @@ enum Mode { Clean, New, Doc, + Symbols, } impl Mode { @@ -1507,6 +1620,7 @@ impl Mode { Self::Clean => "clean", Self::New => "new", Self::Doc => "doc", + Self::Symbols => "symbols", } } @@ -1523,6 +1637,7 @@ impl Mode { Self::Clean => "no-output", Self::New => "no-output", Self::Doc => "documentation", + Self::Symbols => "symbols", } } @@ -2425,6 +2540,6 @@ fn normalized_output_path(path: &str) -> Option { fn print_usage() { eprintln!( - "usage: glagol [check|fmt|test|build|run|clean] [--json-diagnostics] [--no-color] [--manifest ] [--link-c ] [-o ] [--filter ] [-- ...]\n glagol fmt [--check|--write] \n glagol new [--name ] [--template binary|library|workspace]\n glagol doc -o \n glagol [--emit=llvm|--format|--print-tree|--inspect-lowering=surface|--inspect-lowering=checked|--check-tests|--run-tests] [--json-diagnostics] [--no-color] [-o ] [--manifest ] [--filter ] \n glagol --version" + "usage: glagol [check|fmt|test|build|run|clean|symbols] [--json-diagnostics] [--no-color] [--manifest ] [--link-c ] [-o ] [--filter ] [-- ...]\n glagol fmt [--check|--write] \n glagol new [--name ] [--template binary|library|workspace]\n glagol doc -o \n glagol symbols \n glagol [--emit=llvm|--format|--print-tree|--inspect-lowering=surface|--inspect-lowering=checked|--check-tests|--run-tests] [--json-diagnostics] [--no-color] [-o ] [--manifest ] [--filter ] \n glagol --version" ); } diff --git a/compiler/src/symbols.rs b/compiler/src/symbols.rs new file mode 100644 index 0000000..e29b7cf --- /dev/null +++ b/compiler/src/symbols.rs @@ -0,0 +1,667 @@ +use std::collections::HashMap; + +use crate::{ + diag, lexer, + project::{ProjectArtifact, SourceFile, ToolProject}, + sexpr::{Atom, SExpr, SExprKind}, + token::Span, +}; + +pub fn render_file(path: &str, source: &str) -> Result> { + let source = SourceFile { + path: path.to_string(), + source: source.to_string(), + }; + render_sources(&[source], None) +} + +pub fn render_project(project: &ToolProject) -> Result> { + render_sources(&project.sources, Some(&project.artifact)) +} + +fn render_sources( + sources: &[SourceFile], + artifact: Option<&ProjectArtifact>, +) -> Result> { + let package_by_path = package_names_by_path(artifact); + let mut modules = Vec::new(); + let mut diagnostics = Vec::new(); + + let mut sorted_sources = sources.iter().collect::>(); + sorted_sources.sort_by(|left, right| left.path.cmp(&right.path)); + + for source in sorted_sources { + match module_symbols( + source, + package_by_path.get(&source.path).map(String::as_str), + ) { + Ok(module) => modules.push(module), + Err(mut errs) => diagnostics.append(&mut errs), + } + } + + if !diagnostics.is_empty() { + return Err(diagnostics); + } + + let mut out = String::new(); + out.push_str("(symbols\n"); + out.push_str(" (schema slovo.symbols)\n"); + out.push_str(" (version \"1.0.0-beta.10\")\n"); + out.push_str(" (modules"); + if modules.is_empty() { + out.push_str(")\n"); + } else { + out.push('\n'); + for module in &modules { + render_module(module, &mut out); + } + out.push_str(" )\n"); + } + out.push_str(")\n"); + Ok(out) +} + +fn package_names_by_path(artifact: Option<&ProjectArtifact>) -> HashMap { + let mut packages = HashMap::new(); + let Some(workspace) = artifact.and_then(|artifact| artifact.workspace.as_ref()) else { + return packages; + }; + for package in &workspace.packages { + for module in &package.modules { + packages.insert(module.path.clone(), package.name.clone()); + } + } + packages +} + +fn module_symbols( + source: &SourceFile, + package: Option<&str>, +) -> Result> { + let tokens = lexer::lex(&source.path, &source.source)?; + let forms = crate::sexpr::parse(&source.path, &tokens)?; + + let mut module = ModuleSymbols { + name: "main".to_string(), + path: source.path.clone(), + package: package.map(str::to_string), + module_span: None, + exports: Vec::new(), + imports: Vec::new(), + type_aliases: Vec::new(), + structs: Vec::new(), + enums: Vec::new(), + functions: Vec::new(), + tests: Vec::new(), + source: source.source.clone(), + }; + + for form in &forms { + match list_head(form) { + Some("module") => extract_module(form, &mut module), + Some("import") => extract_import(form, &mut module.imports), + Some("type") => extract_type_alias(form, &mut module.type_aliases), + Some("struct") => extract_struct(form, &mut module.structs), + Some("enum") => extract_enum(form, &mut module.enums), + Some("fn") => extract_function(form, &mut module.functions), + Some("test") => extract_test(form, &mut module.tests), + _ => {} + } + } + + Ok(module) +} + +fn extract_module(form: &SExpr, module: &mut ModuleSymbols) { + let Some(items) = expect_list(form) else { + return; + }; + if let Some(name) = items.get(1).and_then(expect_ident) { + module.name = name.to_string(); + module.module_span = Some(form.span); + } + if let Some(export_items) = items.get(2).and_then(expect_list) { + if matches!(export_items.first().and_then(expect_ident), Some("export")) { + module + .exports + .extend(export_items[1..].iter().filter_map(named_symbol_from_ident)); + } + } +} + +fn extract_import(form: &SExpr, imports: &mut Vec) { + let Some(items) = expect_list(form) else { + return; + }; + let Some(module_name) = items.get(1).and_then(expect_ident) else { + return; + }; + let names = items + .get(2) + .and_then(expect_list) + .map(|items| items.iter().filter_map(named_symbol_from_ident).collect()) + .unwrap_or_default(); + imports.push(ImportSymbol { + module: module_name.to_string(), + span: form.span, + module_span: items[1].span, + names, + }); +} + +fn extract_type_alias(form: &SExpr, aliases: &mut Vec) { + let Some(items) = expect_list(form) else { + return; + }; + let Some(name) = items.get(1).and_then(named_symbol_from_ident) else { + return; + }; + aliases.push(TypeAliasSymbol { + name, + span: form.span, + target_span: items.get(2).map(|item| item.span), + }); +} + +fn extract_struct(form: &SExpr, structs: &mut Vec) { + let Some(items) = expect_list(form) else { + return; + }; + let Some(name) = items.get(1).and_then(named_symbol_from_ident) else { + return; + }; + let fields = items[2..] + .iter() + .filter_map(|item| { + let pair = expect_list(item)?; + let field = pair.first().and_then(named_symbol_from_ident)?; + Some(FieldSymbol { + name: field, + span: item.span, + type_span: pair.get(1).map(|ty| ty.span), + }) + }) + .collect(); + structs.push(StructSymbol { + name, + span: form.span, + fields, + }); +} + +fn extract_enum(form: &SExpr, enums: &mut Vec) { + let Some(items) = expect_list(form) else { + return; + }; + let Some(name) = items.get(1).and_then(named_symbol_from_ident) else { + return; + }; + let variants = items[2..] + .iter() + .filter_map(|item| { + if let Some(name) = named_symbol_from_ident(item) { + return Some(VariantSymbol { + name, + span: item.span, + payload_span: None, + }); + } + let variant_items = expect_list(item)?; + let name = variant_items.first().and_then(named_symbol_from_ident)?; + Some(VariantSymbol { + name, + span: item.span, + payload_span: variant_items.get(1).map(|payload| payload.span), + }) + }) + .collect(); + enums.push(EnumSymbol { + name, + span: form.span, + variants, + }); +} + +fn extract_function(form: &SExpr, functions: &mut Vec) { + let Some(items) = expect_list(form) else { + return; + }; + let Some(name) = items.get(1).and_then(named_symbol_from_ident) else { + return; + }; + let params = items + .get(2) + .and_then(expect_list) + .map(|items| { + items + .iter() + .filter_map(|item| { + let pair = expect_list(item)?; + let name = pair.first().and_then(named_symbol_from_ident)?; + Some(ParamSymbol { + name, + span: item.span, + type_span: pair.get(1).map(|ty| ty.span), + }) + }) + .collect() + }) + .unwrap_or_default(); + functions.push(FunctionSymbol { + name, + span: form.span, + params, + return_span: items.get(4).map(|item| item.span), + }); +} + +fn extract_test(form: &SExpr, tests: &mut Vec) { + let Some(items) = expect_list(form) else { + return; + }; + let Some(name_expr) = items.get(1) else { + return; + }; + let Some(name) = expect_string(name_expr) else { + return; + }; + tests.push(TestSymbol { + name: NamedSymbol { + name: name.to_string(), + span: name_expr.span, + }, + span: form.span, + }); +} + +fn render_module(module: &ModuleSymbols, out: &mut String) { + out.push_str(" (module\n"); + out.push_str(&format!( + " (name {})\n", + diag::render_string(&module.name) + )); + if let Some(package) = module.package.as_deref() { + out.push_str(&format!( + " (package {})\n", + diag::render_string(package) + )); + } + out.push_str(&format!( + " (path {})\n", + diag::render_string(&module.path) + )); + if let Some(span) = module.module_span { + render_span(" ", "module_span", span, &module.source, out); + } + render_named_symbols( + "exports", + "export", + &module.exports, + " ", + &module.source, + out, + ); + render_imports(&module.imports, module, out); + render_type_aliases(&module.type_aliases, module, out); + render_structs(&module.structs, module, out); + render_enums(&module.enums, module, out); + render_functions(&module.functions, module, out); + render_tests(&module.tests, module, out); + out.push_str(" )\n"); +} + +fn render_named_symbols( + section: &str, + item_name: &str, + symbols: &[NamedSymbol], + indent: &str, + source: &str, + out: &mut String, +) { + out.push_str(indent); + out.push('('); + out.push_str(section); + if symbols.is_empty() { + out.push_str(")\n"); + return; + } + out.push('\n'); + for symbol in symbols { + out.push_str(indent); + out.push_str(" ("); + out.push_str(item_name); + out.push('\n'); + out.push_str(indent); + out.push_str(" (name "); + out.push_str(&diag::render_string(&symbol.name)); + out.push_str(")\n"); + let symbol_indent = format!("{} ", indent); + render_span(&symbol_indent, "span", symbol.span, source, out); + out.push_str(indent); + out.push_str(" )\n"); + } + out.push_str(indent); + out.push_str(")\n"); +} + +fn render_imports(imports: &[ImportSymbol], module: &ModuleSymbols, out: &mut String) { + out.push_str(" (imports"); + if imports.is_empty() { + out.push_str(")\n"); + return; + } + out.push('\n'); + for import in imports { + out.push_str(" (import\n"); + out.push_str(&format!( + " (module {})\n", + diag::render_string(&import.module) + )); + render_span(" ", "span", import.span, &module.source, out); + render_span( + " ", + "module_span", + import.module_span, + &module.source, + out, + ); + render_named_symbols( + "names", + "name", + &import.names, + " ", + &module.source, + out, + ); + out.push_str(" )\n"); + } + out.push_str(" )\n"); +} + +fn render_type_aliases(aliases: &[TypeAliasSymbol], module: &ModuleSymbols, out: &mut String) { + out.push_str(" (type_aliases"); + if aliases.is_empty() { + out.push_str(")\n"); + return; + } + out.push('\n'); + for alias in aliases { + out.push_str(" (type_alias\n"); + render_decl_name(&alias.name, " ", &module.source, out); + render_span(" ", "span", alias.span, &module.source, out); + if let Some(span) = alias.target_span { + render_span(" ", "target_span", span, &module.source, out); + } + out.push_str(" )\n"); + } + out.push_str(" )\n"); +} + +fn render_structs(structs: &[StructSymbol], module: &ModuleSymbols, out: &mut String) { + out.push_str(" (structs"); + if structs.is_empty() { + out.push_str(")\n"); + return; + } + out.push('\n'); + for struct_symbol in structs { + out.push_str(" (struct\n"); + render_decl_name(&struct_symbol.name, " ", &module.source, out); + render_span( + " ", + "span", + struct_symbol.span, + &module.source, + out, + ); + out.push_str(" (fields"); + if struct_symbol.fields.is_empty() { + out.push_str(")\n"); + } else { + out.push('\n'); + for field in &struct_symbol.fields { + out.push_str(" (field\n"); + render_decl_name(&field.name, " ", &module.source, out); + render_span(" ", "span", field.span, &module.source, out); + if let Some(span) = field.type_span { + render_span(" ", "type_span", span, &module.source, out); + } + out.push_str(" )\n"); + } + out.push_str(" )\n"); + } + out.push_str(" )\n"); + } + out.push_str(" )\n"); +} + +fn render_enums(enums: &[EnumSymbol], module: &ModuleSymbols, out: &mut String) { + out.push_str(" (enums"); + if enums.is_empty() { + out.push_str(")\n"); + return; + } + out.push('\n'); + for enum_symbol in enums { + out.push_str(" (enum\n"); + render_decl_name(&enum_symbol.name, " ", &module.source, out); + render_span(" ", "span", enum_symbol.span, &module.source, out); + out.push_str(" (variants"); + if enum_symbol.variants.is_empty() { + out.push_str(")\n"); + } else { + out.push('\n'); + for variant in &enum_symbol.variants { + out.push_str(" (variant\n"); + render_decl_name(&variant.name, " ", &module.source, out); + render_span(" ", "span", variant.span, &module.source, out); + if let Some(span) = variant.payload_span { + render_span(" ", "payload_span", span, &module.source, out); + } + out.push_str(" )\n"); + } + out.push_str(" )\n"); + } + out.push_str(" )\n"); + } + out.push_str(" )\n"); +} + +fn render_functions(functions: &[FunctionSymbol], module: &ModuleSymbols, out: &mut String) { + out.push_str(" (functions"); + if functions.is_empty() { + out.push_str(")\n"); + return; + } + out.push('\n'); + for function in functions { + out.push_str(" (function\n"); + render_decl_name(&function.name, " ", &module.source, out); + render_span(" ", "span", function.span, &module.source, out); + if let Some(span) = function.return_span { + render_span(" ", "return_span", span, &module.source, out); + } + out.push_str(" (params"); + if function.params.is_empty() { + out.push_str(")\n"); + } else { + out.push('\n'); + for param in &function.params { + out.push_str(" (param\n"); + render_decl_name(¶m.name, " ", &module.source, out); + render_span(" ", "span", param.span, &module.source, out); + if let Some(span) = param.type_span { + render_span(" ", "type_span", span, &module.source, out); + } + out.push_str(" )\n"); + } + out.push_str(" )\n"); + } + out.push_str(" )\n"); + } + out.push_str(" )\n"); +} + +fn render_tests(tests: &[TestSymbol], module: &ModuleSymbols, out: &mut String) { + out.push_str(" (tests"); + if tests.is_empty() { + out.push_str(")\n"); + return; + } + out.push('\n'); + for test in tests { + out.push_str(" (test\n"); + render_decl_name(&test.name, " ", &module.source, out); + render_span(" ", "span", test.span, &module.source, out); + out.push_str(" )\n"); + } + out.push_str(" )\n"); +} + +fn render_decl_name(symbol: &NamedSymbol, indent: &str, source: &str, out: &mut String) { + out.push_str(indent); + out.push_str(&format!("(name {})\n", diag::render_string(&symbol.name))); + render_span(indent, "name_span", symbol.span, source, out); +} + +fn render_span(indent: &str, label: &str, span: Span, source: &str, out: &mut String) { + let start = position(source, span.start); + let end = position(source, span.end); + out.push_str(indent); + out.push('('); + out.push_str(label); + out.push_str(&format!( + " (span (start {}) (end {})) (range (start (line {}) (column {})) (end (line {}) (column {}))))\n", + span.start, span.end, start.line, start.column, end.line, end.column + )); +} + +fn position(source: &str, offset: usize) -> Position { + let mut line = 1; + let mut column = 1; + for (index, byte) in source.as_bytes().iter().enumerate() { + if index >= offset { + break; + } + if *byte == b'\n' { + line += 1; + column = 1; + } else { + column += 1; + } + } + Position { line, column } +} + +fn list_head(form: &SExpr) -> Option<&str> { + expect_list(form)?.first().and_then(expect_ident) +} + +fn expect_list(form: &SExpr) -> Option<&[SExpr]> { + match &form.kind { + SExprKind::List(items) => Some(items), + _ => None, + } +} + +fn expect_ident(form: &SExpr) -> Option<&str> { + match &form.kind { + SExprKind::Atom(Atom::Ident(name)) => Some(name), + _ => None, + } +} + +fn expect_string(form: &SExpr) -> Option<&str> { + match &form.kind { + SExprKind::Atom(Atom::String(name)) => Some(name), + _ => None, + } +} + +fn named_symbol_from_ident(form: &SExpr) -> Option { + Some(NamedSymbol { + name: expect_ident(form)?.to_string(), + span: form.span, + }) +} + +struct ModuleSymbols { + name: String, + path: String, + package: Option, + module_span: Option, + exports: Vec, + imports: Vec, + type_aliases: Vec, + structs: Vec, + enums: Vec, + functions: Vec, + tests: Vec, + source: String, +} + +struct NamedSymbol { + name: String, + span: Span, +} + +struct ImportSymbol { + module: String, + span: Span, + module_span: Span, + names: Vec, +} + +struct TypeAliasSymbol { + name: NamedSymbol, + span: Span, + target_span: Option, +} + +struct StructSymbol { + name: NamedSymbol, + span: Span, + fields: Vec, +} + +struct FieldSymbol { + name: NamedSymbol, + span: Span, + type_span: Option, +} + +struct EnumSymbol { + name: NamedSymbol, + span: Span, + variants: Vec, +} + +struct VariantSymbol { + name: NamedSymbol, + span: Span, + payload_span: Option, +} + +struct FunctionSymbol { + name: NamedSymbol, + span: Span, + params: Vec, + return_span: Option, +} + +struct ParamSymbol { + name: NamedSymbol, + span: Span, + type_span: Option, +} + +struct TestSymbol { + name: NamedSymbol, + span: Span, +} + +struct Position { + line: usize, + column: usize, +} diff --git a/compiler/tests/symbols_beta10.rs b/compiler/tests/symbols_beta10.rs new file mode 100644 index 0000000..03fd4c1 --- /dev/null +++ b/compiler/tests/symbols_beta10.rs @@ -0,0 +1,184 @@ +use std::{ + fs, + path::PathBuf, + process::{Command, Output}, + sync::atomic::{AtomicUsize, Ordering}, +}; + +static NEXT_ID: AtomicUsize = AtomicUsize::new(0); + +#[test] +fn symbols_single_file_reports_editor_facing_source_metadata() { + let source = r#"(module docs (export main)) + +(type Count i32) + +(struct Point + (x i32) + (y i32)) + +(enum Status Ready (Blocked i32)) + +(fn main () -> i32 + 0) + +(test "main returns zero" + (= (main) 0)) +"#; + let file = write_file("symbols-file", source); + + let output = run_glagol(["symbols".as_ref(), file.as_os_str()]); + + assert_success("symbols file", &output); + assert!(output.stderr.is_empty(), "symbols wrote stderr"); + let stdout = String::from_utf8_lossy(&output.stdout); + assert!(stdout.starts_with("(symbols\n")); + assert!(stdout.contains(" (schema slovo.symbols)\n")); + assert!(stdout.contains(" (version \"1.0.0-beta.10\")\n")); + assert!(stdout.contains(&format!(" (path \"{}\")\n", file.display()))); + assert!(stdout.contains(" (name \"docs\")\n")); + assert!(stdout.contains(" (module_span (span ")); + assert!(stdout.contains("(range (start (line 1) (column 1))")); + assert!(stdout.contains(" (name \"main\")\n")); + assert!(stdout.contains(" (name \"Count\")\n")); + assert!(stdout.contains(" (name \"Point\")\n")); + assert!(stdout.contains(" (name \"Status\")\n")); + assert!(stdout.contains(" (name \"Blocked\")\n")); + assert!(stdout.contains(" (name \"main returns zero\")\n")); +} + +#[test] +fn symbols_project_writes_output_and_manifest_without_stdout() { + let project = write_project( + "symbols-project", + &[( + "math", + "(module math (export one))\n\n(fn one () -> i32\n 1)\n", + )], + "(module main)\n\n(import math (one))\n\n(fn main () -> i32\n (one))\n", + ); + let output_path = unique_path("symbols-project-out").with_extension("sexpr"); + let manifest_path = unique_path("symbols-project-manifest").with_extension("slo"); + + let output = run_glagol([ + "symbols".as_ref(), + project.as_os_str(), + "-o".as_ref(), + output_path.as_os_str(), + "--manifest".as_ref(), + manifest_path.as_os_str(), + ]); + + assert_success("symbols project", &output); + assert!(output.stdout.is_empty(), "symbols -o wrote stdout"); + assert!(output.stderr.is_empty(), "symbols project wrote stderr"); + + let symbols = fs::read_to_string(&output_path).expect("read symbols output"); + assert!(symbols.contains(" (name \"main\")\n")); + assert!(symbols.contains(" (name \"math\")\n")); + assert!(symbols.contains(" (module \"math\")\n")); + assert!(symbols.contains(" (name \"one\")\n")); + assert!(symbols.contains(" (imports\n")); + assert!(symbols.contains(" (functions\n")); + + let manifest = fs::read_to_string(&manifest_path).expect("read symbols manifest"); + assert!(manifest.contains(" (mode symbols)\n")); + assert!(manifest.contains(" (kind symbols)\n")); + assert!(manifest.contains(&format!(" (path \"{}\")\n", output_path.display()))); +} + +#[test] +fn symbols_workspace_includes_package_names_deterministically() { + let workspace = unique_path("symbols-workspace"); + let scaffold = run_glagol([ + "new".as_ref(), + workspace.as_os_str(), + "--template".as_ref(), + "workspace".as_ref(), + ]); + assert_success("workspace scaffold", &scaffold); + + let first = run_glagol(["symbols".as_ref(), workspace.as_os_str()]); + let second = run_glagol(["symbols".as_ref(), workspace.as_os_str()]); + + assert_success("symbols workspace first", &first); + assert_success("symbols workspace second", &second); + assert_eq!( + first.stdout, second.stdout, + "workspace symbols output was not deterministic" + ); + let stdout = String::from_utf8_lossy(&first.stdout); + assert!(stdout.contains(" (package \"app\")\n")); + assert!(stdout.contains(" (package \"libutil\")\n")); + assert!(stdout.contains(" (module \"libutil.libutil\")\n")); +} + +#[test] +fn symbols_usage_is_part_of_the_public_cli_surface() { + let output = run_glagol([std::ffi::OsStr::new("--help")]); + + assert_success("glagol help", &output); + let stderr = String::from_utf8_lossy(&output.stderr); + assert!(stderr.contains("glagol symbols ")); +} + +fn write_project(name: &str, modules: &[(&str, &str)], main: &str) -> PathBuf { + let project = unique_path(name); + fs::create_dir_all(project.join("src")).expect("create project src"); + fs::write( + project.join("slovo.toml"), + format!( + "[project]\nname = \"{}\"\nsource_root = \"src\"\nentry = \"main\"\n", + name + ), + ) + .expect("write manifest"); + for (module, source) in modules { + fs::write(project.join("src").join(format!("{}.slo", module)), source) + .expect("write module"); + } + fs::write(project.join("src/main.slo"), main).expect("write main"); + project +} + +fn write_file(name: &str, source: &str) -> PathBuf { + let path = unique_path(name).with_extension("slo"); + fs::write(&path, source).expect("write fixture"); + path +} + +fn unique_path(name: &str) -> PathBuf { + let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); + let nanos = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("system clock before UNIX_EPOCH") + .as_nanos(); + std::env::temp_dir().join(format!( + "glagol-symbols-beta10-{}-{}-{}-{}", + std::process::id(), + nanos, + id, + name + )) +} + +fn run_glagol(args: I) -> Output +where + I: IntoIterator, + S: AsRef, +{ + Command::new(env!("CARGO_BIN_EXE_glagol")) + .args(args) + .output() + .expect("run glagol") +} + +fn assert_success(context: &str, output: &Output) { + assert!( + output.status.success(), + "{} failed\nstdout:\n{}\nstderr:\n{}", + context, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); +} diff --git a/docs/POST_BETA_ROADMAP.md b/docs/POST_BETA_ROADMAP.md index 82c3385..cd31f4a 100644 --- a/docs/POST_BETA_ROADMAP.md +++ b/docs/POST_BETA_ROADMAP.md @@ -254,18 +254,36 @@ Why ninth: generics are important, but they should be introduced after the compiler, docs, tests, and stdlib have enough real pressure to validate the design. -### 10. Developer Experience +### 10. Developer Experience API Discovery -Goal: make Slovo comfortable for repeated daily use. +Goal: make Slovo comfortable for repeated daily use by making the current +standard-library API surface easier to inspect before deeper editor work. Work: +- upgrade the generated `lib/std` API catalog from exported names to exact + exported helper signatures +- normalize module-local beta.8/beta.9 concrete aliases in public signatures + so local aliases do not leak into docs +- validate that exported helpers have matching `(fn ...)` forms +- keep non-exported helpers and `(type ...)` aliases out of the public catalog - language-server diagnostics and document symbols +- editor-facing symbol metadata for files, projects, and workspaces - project watch mode -- generated API documentation for local packages and `lib/std` +- generated API documentation for local packages - clearer benchmark harness output - machine-readable diagnostics stability policy +Released in `1.0.0-beta.10`: `docs/language/STDLIB_API.md` now lists exact +exported helper signatures from `lib/std/*.slo`, and the renderer validates +exported helper forms while normalizing module-local aliases to concrete +public types. `glagol symbols ` now emits +deterministic `slovo.symbols` metadata for editor integrations without +starting an LSP server. This is beta API discovery only; it does not add +executable generics, maps, sets, runtime changes, or a stable standard-library +API freeze. LSP, watch mode, local-package API docs, benchmark-output work, +and a machine-readable diagnostics stability policy remain deferred. + Why tenth: editor support matters, but it should follow a stable enough syntax, project model, and diagnostic model. diff --git a/docs/compiler/RELEASE_NOTES.md b/docs/compiler/RELEASE_NOTES.md index 3bcda4d..a80bac7 100644 --- a/docs/compiler/RELEASE_NOTES.md +++ b/docs/compiler/RELEASE_NOTES.md @@ -10,11 +10,47 @@ integration/readiness release, not the first real beta. ## Unreleased -Next scoped Glagol work is expected to continue after the `1.0.0-beta.9` -reservation update. +Next scoped Glagol work is expected to continue after the `1.0.0-beta.10` +developer-experience API discovery and symbol-metadata update. No unreleased compiler scope is committed here yet. +## 1.0.0-beta.10 + +Release label: `1.0.0-beta.10` + +Release date: 2026-05-22 + +Release state: developer-experience API discovery and symbol-metadata update + +### Summary + +The beta.10 tooling/docs slice upgrades generated standard-library API +discovery and adds deterministic editor-facing symbol metadata without +changing source-language execution semantics. + +- `scripts/render-stdlib-api-doc.js` now validates exported `lib/std` helper + names against matching `(fn ...)` forms. +- `docs/language/STDLIB_API.md` now lists exact exported helper signatures + rather than names only. +- Module-local concrete aliases are normalized in public signatures, so beta.8 + and beta.9 aliases do not leak into the public catalog. +- Non-exported helper functions and `(type ...)` aliases remain omitted from + the catalog. +- `glagol symbols ` emits deterministic + `slovo.symbols` S-expression metadata for modules, imports, exports, + aliases, structs, enums, functions, tests, source spans/ranges, and + workspace package labels. +- `compiler/tests/symbols_beta10.rs` covers single-file, project, workspace, + `-o`/manifest, and help-surface behavior. + +### Explicit Deferrals + +This release does not implement source-language runtime changes, executable +generics, generic stdlib dispatch, generic vectors, maps, sets, iterators, +collection unification, stable collection ABI/layout, new standard-library +runtime APIs, an LSP server, SARIF, watch mode, or daemon protocols. + ## 1.0.0-beta.9 Release label: `1.0.0-beta.9` diff --git a/docs/compiler/ROADMAP.md b/docs/compiler/ROADMAP.md index 4d640e5..acb510b 100644 --- a/docs/compiler/ROADMAP.md +++ b/docs/compiler/ROADMAP.md @@ -22,9 +22,9 @@ general-purpose beta release. A Glagol feature is done only when it has parser/lowerer support, checker behavior, diagnostics for invalid forms, backend behavior or explicit unsupported diagnostics, and tests. -Current stage: `1.0.0-beta.9`, released on 2026-05-22 as a compiler -reservation update for generic-shaped collection and stdlib syntax. It keeps -the `1.0.0-beta` language/compiler support baseline and includes the +Current stage: `1.0.0-beta.10`, released on 2026-05-22 as a +developer-experience API discovery and symbol-metadata update. It keeps the `1.0.0-beta` +language/compiler support baseline and includes the `1.0.0-beta.1` tooling hardening release, the `1.0.0-beta.2` runtime/resource foundation release, the `1.0.0-beta.3` standard-library stabilization release, the `1.0.0-beta.4` language-usability diagnostics release, the @@ -36,10 +36,17 @@ matching source-facade, test-runner, hosted smoke, and benchmark-scaffold coverage, the `1.0.0-beta.8` concrete type alias foundation update, and focused diagnostics/formatter/project-mode gates for reserved generic functions, parameterized aliases, generic type parameters, maps, sets, and -future generic stdlib calls. +future generic stdlib calls. The beta.10 tooling/docs slice upgrades +generated `lib/std` API discovery so public documentation lists exact exported +helper signatures after alias normalization, and adds +`glagol symbols ` for deterministic +editor-facing source metadata over modules, imports, exports, aliases, +structs, enums, functions, tests, spans/ranges, and workspace package labels. +It adds no source-language runtime behavior. -Next stage target: post-`1.0.0-beta.9` collection/generic planning. Generic -vectors, maps, sets, generic stdlib dispatch, and collection unification remain +Next stage target: post-`1.0.0-beta.10` developer-experience and +collection/generic planning. Generic vectors, maps, sets, generic stdlib +dispatch, runtime collection changes, and collection unification remain unimplemented until a later scoped contract promotes them explicitly. The final experimental precursor scope is `exp-125`. Its unsigned direct-value diff --git a/docs/language/RELEASE_NOTES.md b/docs/language/RELEASE_NOTES.md index ce0ea5a..2a3b39b 100644 --- a/docs/language/RELEASE_NOTES.md +++ b/docs/language/RELEASE_NOTES.md @@ -8,7 +8,7 @@ Historical `exp-*` releases listed here are experimental maturity milestones. The pushed tag `v2.0.0-beta.1` is historical. It is now documented as an experimental integration/readiness release, not as a beta maturity claim. -The current release is `1.0.0-beta.9`, published on 2026-05-22. It keeps the +The current release is `1.0.0-beta.10`, published on 2026-05-22. It keeps the `1.0.0-beta` language surface, includes the first post-beta tooling/install hardening bundle from `1.0.0-beta.1`, and adds the first runtime/resource foundation bundle from `1.0.0-beta.2` plus the first standard-library @@ -19,12 +19,48 @@ loopback networking foundation bundle from `1.0.0-beta.6`, and the first serialization/data-interchange foundation bundle from `1.0.0-beta.7`, and the first concrete type alias foundation from `1.0.0-beta.8`, plus the first collection alias unification and generic reservation slice from -`1.0.0-beta.9`. +`1.0.0-beta.9`, and the first developer-experience API discovery slice from +`1.0.0-beta.10`. ## Unreleased No unreleased language scope is committed here yet. +## 1.0.0-beta.10 + +Release label: `1.0.0-beta.10` + +Release name: Developer Experience API Discovery + +Release date: 2026-05-22 + +Status: released beta documentation/API-discovery update on the +`1.0.0-beta` language baseline. + +`1.0.0-beta.10` upgrades `docs/language/STDLIB_API.md` from an exported-name +index to a generated exported-signature catalog and adds an editor-facing +symbol metadata command: + +- `scripts/render-stdlib-api-doc.js` parses each `lib/std/*.slo` module, + validates that exported helpers have matching `(fn ...)` forms, and renders + their public parameter and return types. +- Module-local concrete aliases introduced in beta.8 and used across beta.9 + collection/value-family facades are normalized before rendering, so public + signatures show concrete types such as `(vec i32)`, `(option string)`, and + `(result u64 i32)` instead of local names such as `VecI32` or `ResultU64`. +- Non-exported helper functions and `(type ...)` aliases remain omitted from + the public catalog. +- `glagol symbols ` emits deterministic + `slovo.symbols` S-expression metadata for modules, imports, exports, + aliases, structs, enums, functions, tests, source spans/ranges, and + workspace package labels. + +This release does not add executable generics, generic aliases, +parameterized aliases, maps, sets, traits, inference, monomorphization, +iterators, runtime changes, new compiler-known runtime names, stable +ABI/layout promises, an LSP server, watch mode, SARIF, daemon protocols, or a +stable standard-library API freeze. + ## 1.0.0-beta.9 Release label: `1.0.0-beta.9` diff --git a/docs/language/ROADMAP.md b/docs/language/ROADMAP.md index dffae0c..d6410b6 100644 --- a/docs/language/ROADMAP.md +++ b/docs/language/ROADMAP.md @@ -10,8 +10,8 @@ Long-horizon planning lives in release train from the historical `v2.0.0-beta.1` tag toward and beyond the first real general-purpose beta Slovo contract. -Current stage: `1.0.0-beta.9`, released on 2026-05-22 as the first post-beta -collection alias unification and generic reservation update. It keeps the `1.0.0-beta` language contract and +Current stage: `1.0.0-beta.10`, released on 2026-05-22 as the first post-beta +developer-experience API discovery update. It keeps the `1.0.0-beta` language contract and includes the `1.0.0-beta.1` tooling hardening release, the `1.0.0-beta.2` runtime/resource foundation release, the `1.0.0-beta.3` standard-library stabilization release, the `1.0.0-beta.4` language-usability diagnostics @@ -20,16 +20,22 @@ release, the `1.0.0-beta.5` package/workspace discipline release, and a narrow opaque `i32` handles and concrete `result` families, plus a narrow `std.json` source facade for compact JSON text construction, plus top-level module-local transparent aliases for supported concrete target types, plus local alias use -inside the current concrete vector, option, and result facades. JSON parsing, -recursive JSON values, executable generics, generic aliases, parameterized -aliases, cross-module alias visibility, maps/sets, traits, inference, -monomorphization, iterators, DNS, TLS, UDP, async IO, non-loopback binding, -HTTP frameworks, rich host-error ADTs, stable ABI/layout, and a stable -standard-library API freeze remain deferred. +inside the current concrete vector, option, and result facades, plus a +generated standard-library API catalog that lists exact exported helper +signatures with module-local aliases normalized to concrete public types, plus +`glagol symbols` deterministic source metadata for files, projects, and +workspaces. JSON +parsing, recursive JSON values, executable generics, generic aliases, +parameterized aliases, cross-module alias visibility, maps/sets, traits, +inference, monomorphization, iterators, runtime changes for generic +collections, DNS, TLS, UDP, async IO, non-loopback binding, HTTP frameworks, +rich host-error ADTs, stable ABI/layout, and a stable standard-library API +freeze remain deferred. -Next stage target: continue from the reserved generic/map/set diagnostics and -collection-unification design pressure without claiming executable generics -until the exact scope is frozen from the manifest and roadmap. +Next stage target: continue from developer-experience and reserved +generic/map/set diagnostics without claiming executable generics, an LSP/watch +protocol, or stable standard-library APIs until the exact scope is frozen from +the manifest and roadmap. The final experimental precursor scope is `exp-125`, defined in `.llm/EXP_125_UNSIGNED_U32_U64_NUMERIC_AND_STDLIB_BREADTH_ALPHA.md`. Its diff --git a/docs/language/SPEC-v1.md b/docs/language/SPEC-v1.md index efbf8d9..de9a0d0 100644 --- a/docs/language/SPEC-v1.md +++ b/docs/language/SPEC-v1.md @@ -1,8 +1,9 @@ # Slovo v1 Specification Status: living beta contract for `1.0.0-beta`, with the post-beta -`1.0.0-beta.1` tooling/install update and `1.0.0-beta.2` runtime/resource -foundation update. The language contract integrates +`1.0.0-beta.1` tooling/install update, `1.0.0-beta.2` runtime/resource +foundation update, and `1.0.0-beta.10` developer-experience API discovery +update. The language contract integrates promoted language slices through `exp-125` and the historical publication baseline through `exp-123`. `1.0.0-beta` is the first real general-purpose beta release. `exp-125` completed the unsigned numeric and stdlib breadth @@ -118,7 +119,16 @@ Current v1 release surface and explicit experimental targets: existing concrete vector, option, and result source facades may use module-local concrete aliases internally while exported helper behavior and cross-module types remain concrete; executable generics, maps, sets, traits, - inference, monomorphization, iterators, and stable ABI/layout remain deferred + inference, monomorphization, iterators, stable ABI/layout, and runtime + changes remain out of scope, once the matching Glagol gates pass +- `1.0.0-beta.10` developer-experience API discovery target: the generated + `docs/language/STDLIB_API.md` catalog lists exact exported helper + signatures from `lib/std/*.slo`, normalizes module-local concrete aliases to + public concrete types, and omits non-exported helpers and `(type ...)` + aliases; `glagol symbols` emits deterministic editor-facing source metadata + for files, projects, and workspaces; this is beta API discovery only, not + executable generics, maps, sets, runtime changes, LSP/watch, or a stable + `1.0.0` standard-library freeze - `exp-1` owned runtime strings: compiler-known `std.string.concat` accepts two `string` values and returns an immutable runtime-owned `string`; existing string equality, length, printing, locals, parameters, returns, and calls work @@ -943,6 +953,14 @@ structs, functions, and tests. It is a documentation generator, not a semantic reflection API, and it does not expose stable typed-core, debug metadata, source-map, ABI, layout, runtime reflection, or compiler-internal contracts. +`glagol symbols ` emits deterministic +machine-readable S-expression metadata using the `slovo.symbols` schema. It +reports module paths, package labels when available, imports, exports, +module-local type aliases, structs, enums, functions, tests, and source +spans/ranges. It is an editor-integration building block, not an LSP server, +watch protocol, semantic reflection API, debug metadata, source-map contract, +ABI/layout promise, or stable compiler-internal contract. + The repository must provide a local release-gate script or documented command entry point that runs the full v1 release gate for the matching Slovo and Glagol release without network, tag, push, or release-publication side effects. @@ -5494,7 +5512,7 @@ Successful modes that intentionally produce no primary text may use: Recommended v1 modes are `check`, `format`, `emit-llvm`, `inspect-lowering`, and `test`. Recommended output and artifact kinds are `diagnostics`, `formatted-source`, `llvm-ir`, `lowering-inspector`, `stdout`, -`stderr`, `no-output`, and `test-report`. +`stderr`, `symbols`, `no-output`, and `test-report`. A failed invocation still writes a manifest when the tool mode supports manifests. In that case `(success false)` is paired with a diagnostics artifact diff --git a/docs/language/STDLIB_API.md b/docs/language/STDLIB_API.md index 0b94a6f..ed07fd4 100644 --- a/docs/language/STDLIB_API.md +++ b/docs/language/STDLIB_API.md @@ -1,20 +1,23 @@ # Slovo Standard Library API Catalog -Generated from `lib/std/*.slo` by `scripts/render-stdlib-api-doc.sh`. +Generated from `lib/std/*.slo` by `scripts/render-stdlib-api-doc.js`. 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.9`; future releases may mark new helpers this way before they graduate. +- `experimental`: not used for exported `lib/std` helpers in `1.0.0-beta.10`; 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. +The catalog is a beta API discovery aid, not a stable `1.0.0` standard-library freeze. +Module-local concrete aliases are normalized in signatures so names such as `VecI32` and `ResultU64` do not leak into the public catalog. +Only exported `(fn ...)` helpers are listed; `(type ...)` aliases and non-exported helpers are omitted. ## Summary - Modules: 19 -- Exported helpers: 572 +- Exported helper signatures: 572 +- Exported type aliases omitted: 0 - Default tier: `beta-supported` ## Modules @@ -23,704 +26,704 @@ The catalog is a beta compatibility aid, not a stable `1.0.0` API freeze. - Path: `lib/std/cli.slo` - Tier: `beta-supported` -- Exported helpers: 26 +- Exported helper signatures: 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` +- `arg_text_result ((index i32)) -> (result string i32)` +- `arg_text_option ((index i32)) -> (option string)` +- `arg_i32_result ((index i32)) -> (result i32 i32)` +- `arg_i32_option ((index i32)) -> (option i32)` +- `arg_i32_or_zero ((index i32)) -> i32` +- `arg_i32_or ((index i32) (fallback i32)) -> i32` +- `arg_u32_result ((index i32)) -> (result u32 i32)` +- `arg_u32_option ((index i32)) -> (option u32)` +- `arg_u32_or_zero ((index i32)) -> u32` +- `arg_u32_or ((index i32) (fallback u32)) -> u32` +- `arg_i64_result ((index i32)) -> (result i64 i32)` +- `arg_i64_option ((index i32)) -> (option i64)` +- `arg_i64_or_zero ((index i32)) -> i64` +- `arg_i64_or ((index i32) (fallback i64)) -> i64` +- `arg_u64_result ((index i32)) -> (result u64 i32)` +- `arg_u64_option ((index i32)) -> (option u64)` +- `arg_u64_or_zero ((index i32)) -> u64` +- `arg_u64_or ((index i32) (fallback u64)) -> u64` +- `arg_f64_result ((index i32)) -> (result f64 i32)` +- `arg_f64_option ((index i32)) -> (option f64)` +- `arg_f64_or_zero ((index i32)) -> f64` +- `arg_f64_or ((index i32) (fallback f64)) -> f64` +- `arg_bool_result ((index i32)) -> (result bool i32)` +- `arg_bool_option ((index i32)) -> (option bool)` +- `arg_bool_or_false ((index i32)) -> bool` +- `arg_bool_or ((index i32) (fallback bool)) -> bool` ### std.env - Path: `lib/std/env.slo` - Tier: `beta-supported` -- Exported helpers: 29 +- Exported helper signatures: 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` +- `get ((name string)) -> string` +- `get_result ((name string)) -> (result string i32)` +- `get_option ((name string)) -> (option string)` +- `has ((name string)) -> bool` +- `get_or ((name string) (fallback string)) -> string` +- `get_i32_result ((name string)) -> (result i32 i32)` +- `get_i32_option ((name string)) -> (option i32)` +- `get_i32_or_zero ((name string)) -> i32` +- `get_i32_or ((name string) (fallback i32)) -> i32` +- `get_u32_result ((name string)) -> (result u32 i32)` +- `get_u32_option ((name string)) -> (option u32)` +- `get_u32_or_zero ((name string)) -> u32` +- `get_u32_or ((name string) (fallback u32)) -> u32` +- `get_i64_result ((name string)) -> (result i64 i32)` +- `get_i64_option ((name string)) -> (option i64)` +- `get_i64_or_zero ((name string)) -> i64` +- `get_i64_or ((name string) (fallback i64)) -> i64` +- `get_u64_result ((name string)) -> (result u64 i32)` +- `get_u64_option ((name string)) -> (option u64)` +- `get_u64_or_zero ((name string)) -> u64` +- `get_u64_or ((name string) (fallback u64)) -> u64` +- `get_f64_result ((name string)) -> (result f64 i32)` +- `get_f64_option ((name string)) -> (option f64)` +- `get_f64_or_zero ((name string)) -> f64` +- `get_f64_or ((name string) (fallback f64)) -> f64` +- `get_bool_result ((name string)) -> (result bool i32)` +- `get_bool_option ((name string)) -> (option bool)` +- `get_bool_or_false ((name string)) -> bool` +- `get_bool_or ((name string) (fallback bool)) -> bool` ### std.fs - Path: `lib/std/fs.slo` - Tier: `beta-supported` -- Exported helpers: 43 +- Exported helper signatures: 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` +- `read_text ((path string)) -> string` +- `read_text_result ((path string)) -> (result string i32)` +- `read_text_option ((path string)) -> (option string)` +- `write_text_status ((path string) (text string)) -> i32` +- `write_text_result ((path string) (text string)) -> (result i32 i32)` +- `exists ((path string)) -> bool` +- `is_file ((path string)) -> bool` +- `is_dir ((path string)) -> bool` +- `remove_file_result ((path string)) -> (result i32 i32)` +- `create_dir_result ((path string)) -> (result i32 i32)` +- `remove_file_ok ((path string)) -> bool` +- `create_dir_ok ((path string)) -> bool` +- `open_text_read_result ((path string)) -> (result i32 i32)` +- `read_open_text_result ((handle i32)) -> (result string i32)` +- `close_result ((handle i32)) -> (result i32 i32)` +- `read_text_via_handle_result ((path string)) -> (result string i32)` +- `close_ok ((handle i32)) -> bool` +- `read_text_or ((path string) (fallback string)) -> string` +- `write_text_ok ((path string) (text string)) -> bool` +- `read_i32_result ((path string)) -> (result i32 i32)` +- `read_i32_option ((path string)) -> (option i32)` +- `read_i32_or_zero ((path string)) -> i32` +- `read_i32_or ((path string) (fallback i32)) -> i32` +- `read_u32_result ((path string)) -> (result u32 i32)` +- `read_u32_option ((path string)) -> (option u32)` +- `read_u32_or_zero ((path string)) -> u32` +- `read_u32_or ((path string) (fallback u32)) -> u32` +- `read_i64_result ((path string)) -> (result i64 i32)` +- `read_i64_option ((path string)) -> (option i64)` +- `read_i64_or_zero ((path string)) -> i64` +- `read_i64_or ((path string) (fallback i64)) -> i64` +- `read_u64_result ((path string)) -> (result u64 i32)` +- `read_u64_option ((path string)) -> (option u64)` +- `read_u64_or_zero ((path string)) -> u64` +- `read_u64_or ((path string) (fallback u64)) -> u64` +- `read_f64_result ((path string)) -> (result f64 i32)` +- `read_f64_option ((path string)) -> (option f64)` +- `read_f64_or_zero ((path string)) -> f64` +- `read_f64_or ((path string) (fallback f64)) -> f64` +- `read_bool_result ((path string)) -> (result bool i32)` +- `read_bool_option ((path string)) -> (option bool)` +- `read_bool_or_false ((path string)) -> bool` +- `read_bool_or ((path string) (fallback bool)) -> bool` ### std.io - Path: `lib/std/io.slo` - Tier: `beta-supported` -- Exported helpers: 41 +- Exported helper signatures: 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` +- `print_i32_zero ((value i32)) -> i32` +- `print_u32_zero ((value u32)) -> i32` +- `print_i64_zero ((value i64)) -> i32` +- `print_u64_zero ((value u64)) -> i32` +- `print_f64_zero ((value f64)) -> i32` +- `print_string_zero ((value string)) -> i32` +- `print_bool_zero ((value bool)) -> i32` +- `print_i32_value ((value i32)) -> i32` +- `print_u32_value ((value u32)) -> u32` +- `print_i64_value ((value i64)) -> i64` +- `print_u64_value ((value u64)) -> u64` +- `print_f64_value ((value f64)) -> f64` +- `print_string_value ((value string)) -> string` +- `print_bool_value ((value bool)) -> bool` +- `read_stdin_result () -> (result string i32)` +- `read_stdin_option () -> (option string)` +- `read_stdin_or ((fallback string)) -> string` +- `read_stdin_i32_result () -> (result i32 i32)` +- `read_stdin_i32_option () -> (option i32)` +- `read_stdin_i32_or_zero () -> i32` +- `read_stdin_i32_or ((fallback i32)) -> i32` +- `read_stdin_u32_result () -> (result u32 i32)` +- `read_stdin_u32_option () -> (option u32)` +- `read_stdin_u32_or_zero () -> u32` +- `read_stdin_u32_or ((fallback u32)) -> u32` +- `read_stdin_i64_result () -> (result i64 i32)` +- `read_stdin_i64_option () -> (option i64)` +- `read_stdin_i64_or_zero () -> i64` +- `read_stdin_i64_or ((fallback i64)) -> i64` +- `read_stdin_u64_result () -> (result u64 i32)` +- `read_stdin_u64_option () -> (option u64)` +- `read_stdin_u64_or_zero () -> u64` +- `read_stdin_u64_or ((fallback u64)) -> u64` +- `read_stdin_f64_result () -> (result f64 i32)` +- `read_stdin_f64_option () -> (option f64)` +- `read_stdin_f64_or_zero () -> f64` +- `read_stdin_f64_or ((fallback f64)) -> f64` +- `read_stdin_bool_result () -> (result bool i32)` +- `read_stdin_bool_option () -> (option bool)` +- `read_stdin_bool_or_false () -> bool` +- `read_stdin_bool_or ((fallback bool)) -> bool` ### std.json - Path: `lib/std/json.slo` - Tier: `beta-supported` -- Exported helpers: 24 +- Exported helper signatures: 24 -- `quote_string` -- `null_value` -- `bool_value` -- `i32_value` -- `u32_value` -- `i64_value` -- `u64_value` -- `f64_value` -- `field_string` -- `field_bool` -- `field_i32` -- `field_u32` -- `field_i64` -- `field_u64` -- `field_f64` -- `field_null` -- `array0` -- `array1` -- `array2` -- `array3` -- `object0` -- `object1` -- `object2` -- `object3` +- `quote_string ((value string)) -> string` +- `null_value () -> string` +- `bool_value ((value bool)) -> string` +- `i32_value ((value i32)) -> string` +- `u32_value ((value u32)) -> string` +- `i64_value ((value i64)) -> string` +- `u64_value ((value u64)) -> string` +- `f64_value ((value f64)) -> string` +- `field_string ((name string) (value string)) -> string` +- `field_bool ((name string) (value bool)) -> string` +- `field_i32 ((name string) (value i32)) -> string` +- `field_u32 ((name string) (value u32)) -> string` +- `field_i64 ((name string) (value i64)) -> string` +- `field_u64 ((name string) (value u64)) -> string` +- `field_f64 ((name string) (value f64)) -> string` +- `field_null ((name string)) -> string` +- `array0 () -> string` +- `array1 ((first string)) -> string` +- `array2 ((first string) (second string)) -> string` +- `array3 ((first string) (second string) (third string)) -> string` +- `object0 () -> string` +- `object1 ((first string)) -> string` +- `object2 ((first string) (second string)) -> string` +- `object3 ((first string) (second string) (third string)) -> string` ### std.math - Path: `lib/std/math.slo` - Tier: `beta-supported` -- Exported helpers: 45 +- Exported helper signatures: 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` +- `abs_i32 ((value i32)) -> i32` +- `neg_i32 ((value i32)) -> i32` +- `rem_i32 ((left i32) (right i32)) -> i32` +- `bit_and_i32 ((left i32) (right i32)) -> i32` +- `bit_or_i32 ((left i32) (right i32)) -> i32` +- `bit_xor_i32 ((left i32) (right i32)) -> i32` +- `is_even_i32 ((value i32)) -> bool` +- `is_odd_i32 ((value i32)) -> bool` +- `min_i32 ((left i32) (right i32)) -> i32` +- `max_i32 ((left i32) (right i32)) -> i32` +- `clamp_i32 ((value i32) (low i32) (high i32)) -> i32` +- `square_i32 ((value i32)) -> i32` +- `cube_i32 ((value i32)) -> i32` +- `is_zero_i32 ((value i32)) -> bool` +- `is_positive_i32 ((value i32)) -> bool` +- `is_negative_i32 ((value i32)) -> bool` +- `in_range_i32 ((value i32) (low i32) (high i32)) -> bool` +- `abs_i64 ((value i64)) -> i64` +- `neg_i64 ((value i64)) -> i64` +- `rem_i64 ((left i64) (right i64)) -> i64` +- `bit_and_i64 ((left i64) (right i64)) -> i64` +- `bit_or_i64 ((left i64) (right i64)) -> i64` +- `bit_xor_i64 ((left i64) (right i64)) -> i64` +- `is_even_i64 ((value i64)) -> bool` +- `is_odd_i64 ((value i64)) -> bool` +- `min_i64 ((left i64) (right i64)) -> i64` +- `max_i64 ((left i64) (right i64)) -> i64` +- `clamp_i64 ((value i64) (low i64) (high i64)) -> i64` +- `square_i64 ((value i64)) -> i64` +- `cube_i64 ((value i64)) -> i64` +- `is_zero_i64 ((value i64)) -> bool` +- `is_positive_i64 ((value i64)) -> bool` +- `is_negative_i64 ((value i64)) -> bool` +- `in_range_i64 ((value i64) (low i64) (high i64)) -> bool` +- `abs_f64 ((value f64)) -> f64` +- `neg_f64 ((value f64)) -> f64` +- `min_f64 ((left f64) (right f64)) -> f64` +- `max_f64 ((left f64) (right f64)) -> f64` +- `clamp_f64 ((value f64) (low f64) (high f64)) -> f64` +- `square_f64 ((value f64)) -> f64` +- `cube_f64 ((value f64)) -> f64` +- `is_zero_f64 ((value f64)) -> bool` +- `is_positive_f64 ((value f64)) -> bool` +- `is_negative_f64 ((value f64)) -> bool` +- `in_range_f64 ((value f64) (low f64) (high f64)) -> bool` ### std.net - Path: `lib/std/net.slo` - Tier: `beta-supported` -- Exported helpers: 9 +- Exported helper signatures: 9 -- `tcp_connect_loopback_result` -- `tcp_listen_loopback_result` -- `tcp_bound_port_result` -- `tcp_accept_result` -- `tcp_read_all_result` -- `tcp_write_text_result` -- `tcp_close_result` -- `tcp_write_text_ok` -- `tcp_close_ok` +- `tcp_connect_loopback_result ((port i32)) -> (result i32 i32)` +- `tcp_listen_loopback_result ((port i32)) -> (result i32 i32)` +- `tcp_bound_port_result ((handle i32)) -> (result i32 i32)` +- `tcp_accept_result ((listener i32)) -> (result i32 i32)` +- `tcp_read_all_result ((handle i32)) -> (result string i32)` +- `tcp_write_text_result ((handle i32) (text string)) -> (result i32 i32)` +- `tcp_close_result ((handle i32)) -> (result i32 i32)` +- `tcp_write_text_ok ((handle i32) (text string)) -> bool` +- `tcp_close_ok ((handle i32)) -> bool` ### std.num - Path: `lib/std/num.slo` - Tier: `beta-supported` -- Exported helpers: 14 +- Exported helper signatures: 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` +- `i32_to_i64 ((value i32)) -> i64` +- `i32_to_f64 ((value i32)) -> f64` +- `i64_to_f64 ((value i64)) -> f64` +- `i64_to_i32_result ((value i64)) -> (result i32 i32)` +- `f64_to_i32_result ((value f64)) -> (result i32 i32)` +- `f64_to_i64_result ((value f64)) -> (result i64 i32)` +- `i32_to_string ((value i32)) -> string` +- `u32_to_string ((value u32)) -> string` +- `i64_to_string ((value i64)) -> string` +- `u64_to_string ((value u64)) -> string` +- `f64_to_string ((value f64)) -> string` +- `i64_to_i32_or ((value i64) (fallback i32)) -> i32` +- `f64_to_i32_or ((value f64) (fallback i32)) -> i32` +- `f64_to_i64_or ((value f64) (fallback i64)) -> i64` ### std.option - Path: `lib/std/option.slo` - Tier: `beta-supported` -- Exported helpers: 49 +- Exported helper signatures: 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` +- `some_i32 ((value i32)) -> (option i32)` +- `none_i32 () -> (option i32)` +- `is_some_i32 ((value (option i32))) -> bool` +- `is_none_i32 ((value (option i32))) -> bool` +- `unwrap_some_i32 ((value (option i32))) -> i32` +- `unwrap_or_i32 ((value (option i32)) (fallback i32)) -> i32` +- `some_or_err_i32 ((value (option i32)) (err_code i32)) -> (result i32 i32)` +- `some_u32 ((value u32)) -> (option u32)` +- `none_u32 () -> (option u32)` +- `is_some_u32 ((value (option u32))) -> bool` +- `is_none_u32 ((value (option u32))) -> bool` +- `unwrap_some_u32 ((value (option u32))) -> u32` +- `unwrap_or_u32 ((value (option u32)) (fallback u32)) -> u32` +- `some_or_err_u32 ((value (option u32)) (err_code i32)) -> (result u32 i32)` +- `some_i64 ((value i64)) -> (option i64)` +- `none_i64 () -> (option i64)` +- `is_some_i64 ((value (option i64))) -> bool` +- `is_none_i64 ((value (option i64))) -> bool` +- `unwrap_some_i64 ((value (option i64))) -> i64` +- `unwrap_or_i64 ((value (option i64)) (fallback i64)) -> i64` +- `some_or_err_i64 ((value (option i64)) (err_code i32)) -> (result i64 i32)` +- `some_u64 ((value u64)) -> (option u64)` +- `none_u64 () -> (option u64)` +- `is_some_u64 ((value (option u64))) -> bool` +- `is_none_u64 ((value (option u64))) -> bool` +- `unwrap_some_u64 ((value (option u64))) -> u64` +- `unwrap_or_u64 ((value (option u64)) (fallback u64)) -> u64` +- `some_or_err_u64 ((value (option u64)) (err_code i32)) -> (result u64 i32)` +- `some_f64 ((value f64)) -> (option f64)` +- `none_f64 () -> (option f64)` +- `is_some_f64 ((value (option f64))) -> bool` +- `is_none_f64 ((value (option f64))) -> bool` +- `unwrap_some_f64 ((value (option f64))) -> f64` +- `unwrap_or_f64 ((value (option f64)) (fallback f64)) -> f64` +- `some_or_err_f64 ((value (option f64)) (err_code i32)) -> (result f64 i32)` +- `some_bool ((value bool)) -> (option bool)` +- `none_bool () -> (option bool)` +- `is_some_bool ((value (option bool))) -> bool` +- `is_none_bool ((value (option bool))) -> bool` +- `unwrap_some_bool ((value (option bool))) -> bool` +- `unwrap_or_bool ((value (option bool)) (fallback bool)) -> bool` +- `some_or_err_bool ((value (option bool)) (err_code i32)) -> (result bool i32)` +- `some_string ((value string)) -> (option string)` +- `none_string () -> (option string)` +- `is_some_string ((value (option string))) -> bool` +- `is_none_string ((value (option string))) -> bool` +- `unwrap_some_string ((value (option string))) -> string` +- `unwrap_or_string ((value (option string)) (fallback string)) -> string` +- `some_or_err_string ((value (option string)) (err_code i32)) -> (result string i32)` ### std.process - Path: `lib/std/process.slo` - Tier: `beta-supported` -- Exported helpers: 31 +- Exported helper signatures: 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` +- `argc () -> i32` +- `arg ((index i32)) -> string` +- `arg_result ((index i32)) -> (result string i32)` +- `arg_option ((index i32)) -> (option string)` +- `has_arg ((index i32)) -> bool` +- `arg_or ((index i32) (fallback string)) -> string` +- `arg_or_empty ((index i32)) -> string` +- `arg_i32_result ((index i32)) -> (result i32 i32)` +- `arg_i32_option ((index i32)) -> (option i32)` +- `arg_i32_or_zero ((index i32)) -> i32` +- `arg_i32_or ((index i32) (fallback i32)) -> i32` +- `arg_u32_result ((index i32)) -> (result u32 i32)` +- `arg_u32_option ((index i32)) -> (option u32)` +- `arg_u32_or_zero ((index i32)) -> u32` +- `arg_u32_or ((index i32) (fallback u32)) -> u32` +- `arg_i64_result ((index i32)) -> (result i64 i32)` +- `arg_i64_option ((index i32)) -> (option i64)` +- `arg_i64_or_zero ((index i32)) -> i64` +- `arg_i64_or ((index i32) (fallback i64)) -> i64` +- `arg_u64_result ((index i32)) -> (result u64 i32)` +- `arg_u64_option ((index i32)) -> (option u64)` +- `arg_u64_or_zero ((index i32)) -> u64` +- `arg_u64_or ((index i32) (fallback u64)) -> u64` +- `arg_f64_result ((index i32)) -> (result f64 i32)` +- `arg_f64_option ((index i32)) -> (option f64)` +- `arg_f64_or_zero ((index i32)) -> f64` +- `arg_f64_or ((index i32) (fallback f64)) -> f64` +- `arg_bool_result ((index i32)) -> (result bool i32)` +- `arg_bool_option ((index i32)) -> (option bool)` +- `arg_bool_or_false ((index i32)) -> bool` +- `arg_bool_or ((index i32) (fallback bool)) -> bool` ### std.random - Path: `lib/std/random.slo` - Tier: `beta-supported` -- Exported helpers: 2 +- Exported helper signatures: 2 -- `random_i32` -- `random_i32_non_negative` +- `random_i32 () -> i32` +- `random_i32_non_negative () -> bool` ### std.result - Path: `lib/std/result.slo` - Tier: `beta-supported` -- Exported helpers: 56 +- Exported helper signatures: 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` +- `ok_i32 ((value i32)) -> (result i32 i32)` +- `err_i32 ((code i32)) -> (result i32 i32)` +- `is_ok_i32 ((value (result i32 i32))) -> bool` +- `is_err_i32 ((value (result i32 i32))) -> bool` +- `unwrap_ok_i32 ((value (result i32 i32))) -> i32` +- `unwrap_err_i32 ((value (result i32 i32))) -> i32` +- `unwrap_or_i32 ((value (result i32 i32)) (fallback i32)) -> i32` +- `ok_or_none_i32 ((value (result i32 i32))) -> (option i32)` +- `ok_u32 ((value u32)) -> (result u32 i32)` +- `err_u32 ((code i32)) -> (result u32 i32)` +- `is_ok_u32 ((value (result u32 i32))) -> bool` +- `is_err_u32 ((value (result u32 i32))) -> bool` +- `unwrap_ok_u32 ((value (result u32 i32))) -> u32` +- `unwrap_err_u32 ((value (result u32 i32))) -> i32` +- `unwrap_or_u32 ((value (result u32 i32)) (fallback u32)) -> u32` +- `ok_or_none_u32 ((value (result u32 i32))) -> (option u32)` +- `ok_i64 ((value i64)) -> (result i64 i32)` +- `err_i64 ((code i32)) -> (result i64 i32)` +- `is_ok_i64 ((value (result i64 i32))) -> bool` +- `is_err_i64 ((value (result i64 i32))) -> bool` +- `unwrap_ok_i64 ((value (result i64 i32))) -> i64` +- `unwrap_err_i64 ((value (result i64 i32))) -> i32` +- `unwrap_or_i64 ((value (result i64 i32)) (fallback i64)) -> i64` +- `ok_or_none_i64 ((value (result i64 i32))) -> (option i64)` +- `ok_u64 ((value u64)) -> (result u64 i32)` +- `err_u64 ((code i32)) -> (result u64 i32)` +- `is_ok_u64 ((value (result u64 i32))) -> bool` +- `is_err_u64 ((value (result u64 i32))) -> bool` +- `unwrap_ok_u64 ((value (result u64 i32))) -> u64` +- `unwrap_err_u64 ((value (result u64 i32))) -> i32` +- `unwrap_or_u64 ((value (result u64 i32)) (fallback u64)) -> u64` +- `ok_or_none_u64 ((value (result u64 i32))) -> (option u64)` +- `ok_string ((value string)) -> (result string i32)` +- `err_string ((code i32)) -> (result string i32)` +- `is_ok_string ((value (result string i32))) -> bool` +- `is_err_string ((value (result string i32))) -> bool` +- `unwrap_ok_string ((value (result string i32))) -> string` +- `unwrap_err_string ((value (result string i32))) -> i32` +- `unwrap_or_string ((value (result string i32)) (fallback string)) -> string` +- `ok_or_none_string ((value (result string i32))) -> (option string)` +- `ok_f64 ((value f64)) -> (result f64 i32)` +- `err_f64 ((code i32)) -> (result f64 i32)` +- `is_ok_f64 ((value (result f64 i32))) -> bool` +- `is_err_f64 ((value (result f64 i32))) -> bool` +- `unwrap_ok_f64 ((value (result f64 i32))) -> f64` +- `unwrap_err_f64 ((value (result f64 i32))) -> i32` +- `unwrap_or_f64 ((value (result f64 i32)) (fallback f64)) -> f64` +- `ok_or_none_f64 ((value (result f64 i32))) -> (option f64)` +- `ok_bool ((value bool)) -> (result bool i32)` +- `err_bool ((code i32)) -> (result bool i32)` +- `is_ok_bool ((value (result bool i32))) -> bool` +- `is_err_bool ((value (result bool i32))) -> bool` +- `unwrap_ok_bool ((value (result bool i32))) -> bool` +- `unwrap_err_bool ((value (result bool i32))) -> i32` +- `unwrap_or_bool ((value (result bool i32)) (fallback bool)) -> bool` +- `ok_or_none_bool ((value (result bool i32))) -> (option bool)` ### std.string - Path: `lib/std/string.slo` - Tier: `beta-supported` -- Exported helpers: 26 +- Exported helper signatures: 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` +- `len ((value string)) -> i32` +- `concat ((left string) (right string)) -> string` +- `parse_i32_result ((value string)) -> (result i32 i32)` +- `parse_i32_option ((value string)) -> (option i32)` +- `parse_u32_result ((value string)) -> (result u32 i32)` +- `parse_u32_option ((value string)) -> (option u32)` +- `parse_i64_result ((value string)) -> (result i64 i32)` +- `parse_i64_option ((value string)) -> (option i64)` +- `parse_u64_result ((value string)) -> (result u64 i32)` +- `parse_u64_option ((value string)) -> (option u64)` +- `parse_f64_result ((value string)) -> (result f64 i32)` +- `parse_f64_option ((value string)) -> (option f64)` +- `parse_bool_result ((value string)) -> (result bool i32)` +- `parse_bool_option ((value string)) -> (option bool)` +- `parse_i32_or_zero ((value string)) -> i32` +- `parse_u32_or_zero ((value string)) -> u32` +- `parse_i64_or_zero ((value string)) -> i64` +- `parse_u64_or_zero ((value string)) -> u64` +- `parse_f64_or_zero ((value string)) -> f64` +- `parse_bool_or_false ((value string)) -> bool` +- `parse_i32_or ((value string) (fallback i32)) -> i32` +- `parse_u32_or ((value string) (fallback u32)) -> u32` +- `parse_i64_or ((value string) (fallback i64)) -> i64` +- `parse_u64_or ((value string) (fallback u64)) -> u64` +- `parse_f64_or ((value string) (fallback f64)) -> f64` +- `parse_bool_or ((value string) (fallback bool)) -> bool` ### std.time - Path: `lib/std/time.slo` - Tier: `beta-supported` -- Exported helpers: 2 +- Exported helper signatures: 2 -- `monotonic_ms` -- `sleep_ms_zero` +- `monotonic_ms () -> i32` +- `sleep_ms_zero () -> i32` ### std.vec_bool - Path: `lib/std/vec_bool.slo` - Tier: `beta-supported` -- Exported helpers: 35 +- Exported helper signatures: 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` +- `empty () -> (vec bool)` +- `append ((values (vec bool)) (value bool)) -> (vec bool)` +- `len ((values (vec bool))) -> i32` +- `at ((values (vec bool)) (position i32)) -> bool` +- `singleton ((value bool)) -> (vec bool)` +- `append2 ((values (vec bool)) (first bool) (second bool)) -> (vec bool)` +- `append3 ((values (vec bool)) (first bool) (second bool) (third bool)) -> (vec bool)` +- `pair ((first bool) (second bool)) -> (vec bool)` +- `triple ((first bool) (second bool) (third bool)) -> (vec bool)` +- `is_empty ((values (vec bool))) -> bool` +- `index_or ((values (vec bool)) (position i32) (fallback bool)) -> bool` +- `first_or ((values (vec bool)) (fallback bool)) -> bool` +- `last_or ((values (vec bool)) (fallback bool)) -> bool` +- `index_option ((values (vec bool)) (position i32)) -> (option bool)` +- `first_option ((values (vec bool))) -> (option bool)` +- `last_option ((values (vec bool))) -> (option bool)` +- `index_of_option ((values (vec bool)) (target bool)) -> (option i32)` +- `last_index_of_option ((values (vec bool)) (target bool)) -> (option i32)` +- `contains ((values (vec bool)) (target bool)) -> bool` +- `count_of ((values (vec bool)) (target bool)) -> i32` +- `concat ((left (vec bool)) (right (vec bool))) -> (vec bool)` +- `take ((values (vec bool)) (count i32)) -> (vec bool)` +- `starts_with ((values (vec bool)) (prefix (vec bool))) -> bool` +- `without_prefix ((values (vec bool)) (prefix (vec bool))) -> (vec bool)` +- `ends_with ((values (vec bool)) (suffix (vec bool))) -> bool` +- `without_suffix ((values (vec bool)) (suffix (vec bool))) -> (vec bool)` +- `drop ((values (vec bool)) (count i32)) -> (vec bool)` +- `reverse ((values (vec bool))) -> (vec bool)` +- `subvec ((values (vec bool)) (start i32) (end_exclusive i32)) -> (vec bool)` +- `insert_at ((values (vec bool)) (position i32) (value bool)) -> (vec bool)` +- `insert_range ((values (vec bool)) (position i32) (inserted (vec bool))) -> (vec bool)` +- `replace_at ((values (vec bool)) (position i32) (replacement bool)) -> (vec bool)` +- `replace_range ((values (vec bool)) (start i32) (end_exclusive i32) (replacement (vec bool))) -> (vec bool)` +- `remove_at ((values (vec bool)) (position i32)) -> (vec bool)` +- `remove_range ((values (vec bool)) (start i32) (end_exclusive i32)) -> (vec bool)` ### std.vec_f64 - Path: `lib/std/vec_f64.slo` - Tier: `beta-supported` -- Exported helpers: 35 +- Exported helper signatures: 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` +- `empty () -> (vec f64)` +- `append ((values (vec f64)) (value f64)) -> (vec f64)` +- `len ((values (vec f64))) -> i32` +- `at ((values (vec f64)) (position i32)) -> f64` +- `singleton ((value f64)) -> (vec f64)` +- `append2 ((values (vec f64)) (first f64) (second f64)) -> (vec f64)` +- `append3 ((values (vec f64)) (first f64) (second f64) (third f64)) -> (vec f64)` +- `pair ((first f64) (second f64)) -> (vec f64)` +- `triple ((first f64) (second f64) (third f64)) -> (vec f64)` +- `is_empty ((values (vec f64))) -> bool` +- `index_or ((values (vec f64)) (position i32) (fallback f64)) -> f64` +- `first_or ((values (vec f64)) (fallback f64)) -> f64` +- `last_or ((values (vec f64)) (fallback f64)) -> f64` +- `index_option ((values (vec f64)) (position i32)) -> (option f64)` +- `first_option ((values (vec f64))) -> (option f64)` +- `last_option ((values (vec f64))) -> (option f64)` +- `index_of_option ((values (vec f64)) (target f64)) -> (option i32)` +- `last_index_of_option ((values (vec f64)) (target f64)) -> (option i32)` +- `contains ((values (vec f64)) (target f64)) -> bool` +- `sum ((values (vec f64))) -> f64` +- `concat ((left (vec f64)) (right (vec f64))) -> (vec f64)` +- `take ((values (vec f64)) (count i32)) -> (vec f64)` +- `starts_with ((values (vec f64)) (prefix (vec f64))) -> bool` +- `without_prefix ((values (vec f64)) (prefix (vec f64))) -> (vec f64)` +- `ends_with ((values (vec f64)) (suffix (vec f64))) -> bool` +- `without_suffix ((values (vec f64)) (suffix (vec f64))) -> (vec f64)` +- `drop ((values (vec f64)) (count i32)) -> (vec f64)` +- `reverse ((values (vec f64))) -> (vec f64)` +- `subvec ((values (vec f64)) (start i32) (end_exclusive i32)) -> (vec f64)` +- `insert_at ((values (vec f64)) (position i32) (value f64)) -> (vec f64)` +- `insert_range ((values (vec f64)) (position i32) (inserted (vec f64))) -> (vec f64)` +- `replace_at ((values (vec f64)) (position i32) (replacement f64)) -> (vec f64)` +- `replace_range ((values (vec f64)) (start i32) (end_exclusive i32) (replacement (vec f64))) -> (vec f64)` +- `remove_at ((values (vec f64)) (position i32)) -> (vec f64)` +- `remove_range ((values (vec f64)) (start i32) (end_exclusive i32)) -> (vec f64)` ### std.vec_i32 - Path: `lib/std/vec_i32.slo` - Tier: `beta-supported` -- Exported helpers: 39 +- Exported helper signatures: 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` +- `empty () -> (vec i32)` +- `append ((values (vec i32)) (value i32)) -> (vec i32)` +- `len ((values (vec i32))) -> i32` +- `at ((values (vec i32)) (position i32)) -> i32` +- `singleton ((value i32)) -> (vec i32)` +- `append2 ((values (vec i32)) (first i32) (second i32)) -> (vec i32)` +- `append3 ((values (vec i32)) (first i32) (second i32) (third i32)) -> (vec i32)` +- `pair ((first i32) (second i32)) -> (vec i32)` +- `triple ((first i32) (second i32) (third i32)) -> (vec i32)` +- `repeat ((value i32) (count i32)) -> (vec i32)` +- `range ((start i32) (end_exclusive i32)) -> (vec i32)` +- `range_from_zero ((count i32)) -> (vec i32)` +- `is_empty ((values (vec i32))) -> bool` +- `index_or ((values (vec i32)) (position i32) (fallback i32)) -> i32` +- `first_or ((values (vec i32)) (fallback i32)) -> i32` +- `last_or ((values (vec i32)) (fallback i32)) -> i32` +- `index_option ((values (vec i32)) (position i32)) -> (option i32)` +- `first_option ((values (vec i32))) -> (option i32)` +- `last_option ((values (vec i32))) -> (option i32)` +- `index_of_option ((values (vec i32)) (target i32)) -> (option i32)` +- `last_index_of_option ((values (vec i32)) (target i32)) -> (option i32)` +- `count_of ((values (vec i32)) (target i32)) -> i32` +- `contains ((values (vec i32)) (target i32)) -> bool` +- `sum ((values (vec i32))) -> i32` +- `concat ((left (vec i32)) (right (vec i32))) -> (vec i32)` +- `take ((values (vec i32)) (count i32)) -> (vec i32)` +- `starts_with ((values (vec i32)) (prefix (vec i32))) -> bool` +- `without_prefix ((values (vec i32)) (prefix (vec i32))) -> (vec i32)` +- `ends_with ((values (vec i32)) (suffix (vec i32))) -> bool` +- `without_suffix ((values (vec i32)) (suffix (vec i32))) -> (vec i32)` +- `drop ((values (vec i32)) (count i32)) -> (vec i32)` +- `reverse ((values (vec i32))) -> (vec i32)` +- `subvec ((values (vec i32)) (start i32) (end_exclusive i32)) -> (vec i32)` +- `insert_at ((values (vec i32)) (position i32) (value i32)) -> (vec i32)` +- `insert_range ((values (vec i32)) (position i32) (inserted (vec i32))) -> (vec i32)` +- `replace_at ((values (vec i32)) (position i32) (replacement i32)) -> (vec i32)` +- `replace_range ((values (vec i32)) (start i32) (end_exclusive i32) (replacement (vec i32))) -> (vec i32)` +- `remove_at ((values (vec i32)) (position i32)) -> (vec i32)` +- `remove_range ((values (vec i32)) (start i32) (end_exclusive i32)) -> (vec i32)` ### std.vec_i64 - Path: `lib/std/vec_i64.slo` - Tier: `beta-supported` -- Exported helpers: 31 +- Exported helper signatures: 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` +- `empty () -> (vec i64)` +- `append ((values (vec i64)) (value i64)) -> (vec i64)` +- `len ((values (vec i64))) -> i32` +- `at ((values (vec i64)) (position i32)) -> i64` +- `singleton ((value i64)) -> (vec i64)` +- `append2 ((values (vec i64)) (first i64) (second i64)) -> (vec i64)` +- `append3 ((values (vec i64)) (first i64) (second i64) (third i64)) -> (vec i64)` +- `pair ((first i64) (second i64)) -> (vec i64)` +- `triple ((first i64) (second i64) (third i64)) -> (vec i64)` +- `is_empty ((values (vec i64))) -> bool` +- `index_or ((values (vec i64)) (position i32) (fallback i64)) -> i64` +- `first_or ((values (vec i64)) (fallback i64)) -> i64` +- `last_or ((values (vec i64)) (fallback i64)) -> i64` +- `index_option ((values (vec i64)) (position i32)) -> (option i64)` +- `first_option ((values (vec i64))) -> (option i64)` +- `last_option ((values (vec i64))) -> (option i64)` +- `index_of_option ((values (vec i64)) (target i64)) -> (option i32)` +- `last_index_of_option ((values (vec i64)) (target i64)) -> (option i32)` +- `contains ((values (vec i64)) (target i64)) -> bool` +- `sum ((values (vec i64))) -> i64` +- `concat ((left (vec i64)) (right (vec i64))) -> (vec i64)` +- `take ((values (vec i64)) (count i32)) -> (vec i64)` +- `drop ((values (vec i64)) (count i32)) -> (vec i64)` +- `reverse ((values (vec i64))) -> (vec i64)` +- `subvec ((values (vec i64)) (start i32) (end_exclusive i32)) -> (vec i64)` +- `insert_at ((values (vec i64)) (position i32) (value i64)) -> (vec i64)` +- `insert_range ((values (vec i64)) (position i32) (inserted (vec i64))) -> (vec i64)` +- `replace_at ((values (vec i64)) (position i32) (replacement i64)) -> (vec i64)` +- `replace_range ((values (vec i64)) (start i32) (end_exclusive i32) (replacement (vec i64))) -> (vec i64)` +- `remove_at ((values (vec i64)) (position i32)) -> (vec i64)` +- `remove_range ((values (vec i64)) (start i32) (end_exclusive i32)) -> (vec i64)` ### std.vec_string - Path: `lib/std/vec_string.slo` - Tier: `beta-supported` -- Exported helpers: 35 +- Exported helper signatures: 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` +- `empty () -> (vec string)` +- `append ((values (vec string)) (value string)) -> (vec string)` +- `len ((values (vec string))) -> i32` +- `at ((values (vec string)) (position i32)) -> string` +- `singleton ((value string)) -> (vec string)` +- `append2 ((values (vec string)) (first string) (second string)) -> (vec string)` +- `append3 ((values (vec string)) (first string) (second string) (third string)) -> (vec string)` +- `pair ((first string) (second string)) -> (vec string)` +- `triple ((first string) (second string) (third string)) -> (vec string)` +- `is_empty ((values (vec string))) -> bool` +- `index_or ((values (vec string)) (position i32) (fallback string)) -> string` +- `first_or ((values (vec string)) (fallback string)) -> string` +- `last_or ((values (vec string)) (fallback string)) -> string` +- `index_option ((values (vec string)) (position i32)) -> (option string)` +- `first_option ((values (vec string))) -> (option string)` +- `last_option ((values (vec string))) -> (option string)` +- `index_of_option ((values (vec string)) (target string)) -> (option i32)` +- `last_index_of_option ((values (vec string)) (target string)) -> (option i32)` +- `contains ((values (vec string)) (target string)) -> bool` +- `count_of ((values (vec string)) (target string)) -> i32` +- `concat ((left (vec string)) (right (vec string))) -> (vec string)` +- `take ((values (vec string)) (count i32)) -> (vec string)` +- `starts_with ((values (vec string)) (prefix (vec string))) -> bool` +- `without_prefix ((values (vec string)) (prefix (vec string))) -> (vec string)` +- `ends_with ((values (vec string)) (suffix (vec string))) -> bool` +- `without_suffix ((values (vec string)) (suffix (vec string))) -> (vec string)` +- `drop ((values (vec string)) (count i32)) -> (vec string)` +- `reverse ((values (vec string))) -> (vec string)` +- `subvec ((values (vec string)) (start i32) (end_exclusive i32)) -> (vec string)` +- `insert_at ((values (vec string)) (position i32) (value string)) -> (vec string)` +- `insert_range ((values (vec string)) (position i32) (inserted (vec string))) -> (vec string)` +- `replace_at ((values (vec string)) (position i32) (replacement string)) -> (vec string)` +- `replace_range ((values (vec string)) (start i32) (end_exclusive i32) (replacement (vec string))) -> (vec string)` +- `remove_at ((values (vec string)) (position i32)) -> (vec string)` +- `remove_range ((values (vec string)) (start i32) (end_exclusive i32)) -> (vec string)` diff --git a/docs/papers/GLAGOL_COMPILER_MANIFEST.pdf b/docs/papers/GLAGOL_COMPILER_MANIFEST.pdf index a779905..ed75967 100644 Binary files a/docs/papers/GLAGOL_COMPILER_MANIFEST.pdf and b/docs/papers/GLAGOL_COMPILER_MANIFEST.pdf differ diff --git a/docs/papers/GLAGOL_WHITEPAPER.md b/docs/papers/GLAGOL_WHITEPAPER.md index d421c23..c5fb3e1 100644 --- a/docs/papers/GLAGOL_WHITEPAPER.md +++ b/docs/papers/GLAGOL_WHITEPAPER.md @@ -5,7 +5,7 @@ Sanjin Gumbarevic
hermeticum_lab@protonmail.com -Publication release: `1.0.0-beta.9` +Publication release: `1.0.0-beta.10` Technical behavior baseline: compiler and language support through `1.0.0-beta`; tooling and install workflow through `1.0.0-beta.1`; @@ -15,12 +15,14 @@ stabilization through `1.0.0-beta.3`; language-usability diagnostics through loopback networking foundation through `1.0.0-beta.6`; serialization/data-interchange foundation through `1.0.0-beta.7`; concrete type alias foundation through `1.0.0-beta.8`; -generic and collection reservation diagnostics through `1.0.0-beta.9` +generic and collection reservation diagnostics through `1.0.0-beta.9`; +developer-experience API discovery and symbol metadata through +`1.0.0-beta.10` Date: 2026-05-22 Evidence source: paired Slovo/Glagol monorepo verification and benchmark -reruns from the public source tree; beta.9 release-gate verification from the +reruns from the public source tree; beta.10 release-gate verification from the public monorepo Maturity: beta @@ -32,7 +34,7 @@ Slovo. It exists to make the language support boundary inspectable: tokens, S-expression tree, AST, typed AST, LLVM IR, hosted native executable, tests, diagnostics, and release documents should agree. -The current publication release, `1.0.0-beta.9`, keeps the first real +The current publication release, `1.0.0-beta.10`, keeps the first real general-purpose beta toolchain baseline from `1.0.0-beta` and records the first post-beta tooling/install hardening update plus the first runtime/resource foundation update plus the first standard-library @@ -40,21 +42,23 @@ stabilization update plus the first language-usability diagnostics update and the first local package/workspace discipline update plus the first loopback networking foundation update plus the first serialization/data-interchange foundation update plus the first concrete type alias foundation and the first -generic/collection reservation diagnostic update. The beta +generic/collection reservation diagnostic update, plus the first +developer-experience API discovery and symbol-metadata update. The beta baseline includes the completed `u32` / `u64` unsigned compiler and stdlib breadth scope, the narrow `std.net` loopback TCP runtime family, the narrow `std.json.quote_string` runtime family, transparent concrete alias parsing and erasure, explicit beta9 diagnostics for reserved generic/map/set shapes, and -the current ten-scaffold benchmark suite. This paper records the -current beta +the current ten-scaffold benchmark suite. It also includes alias-normalized +stdlib signature generation and deterministic `glagol symbols` metadata for +files, projects, and workspaces. This paper records the current beta implementation surface, the benchmark method and results, the distinction between Glagol and Lisp-family implementations, the beta.1 tooling update, the beta.2 runtime/resource foundation, the beta.3 standard-library stabilization slice, the beta.4 diagnostics usability slice, the beta.5 package discipline slice, the beta.6 networking foundation slice, the beta.7 serialization foundation slice, the beta.8 concrete alias foundation slice, the beta.9 -generic/collection reservation slice, and the compiler path from beta to -stable. +generic/collection reservation slice, the beta.10 API-discovery and +symbol-metadata slice, and the compiler path from beta to stable. ## 1. Compiler Thesis @@ -316,8 +320,9 @@ diagnostics, `1.0.0-beta.5` tightens package/workspace discipline, and `1.0.0-beta.7` adds a narrow JSON construction foundation, and `1.0.0-beta.8` adds transparent concrete type aliases, and `1.0.0-beta.9` adds focused diagnostics for reserved generic, map, set, and future generic -stdlib forms. None of these post-beta slices claims changed benchmark -performance. The beta.7 `json-quote-loop` +stdlib forms, and `1.0.0-beta.10` adds alias-normalized stdlib signature +generation plus deterministic `glagol symbols` metadata. None of these +post-beta slices claims changed benchmark performance. The beta.7 `json-quote-loop` scaffold is present for local follow-up timing and is not part of the exp-123 nine-row result table below. @@ -407,7 +412,7 @@ coverage and compatibility: - package behavior becoming stable before dependency, manifest, and versioning rules are precise -## 9. Path Beyond `1.0.0-beta.9` +## 9. Path Beyond `1.0.0-beta.10` Glagol now implements the first real beta Slovo contract, the first post-beta tooling/install hardening release, the first runtime/resource @@ -416,7 +421,8 @@ first diagnostics usability release, the first package/workspace discipline release, the first loopback networking foundation release, and the first serialization/data-interchange foundation release, the first concrete type alias foundation release, and the first generic/collection reservation -diagnostic release. The remaining path is from beta to stable. +diagnostic release, and the first API-discovery and symbol-metadata release. +The remaining path is from beta to stable. Recommended compiler sequence: diff --git a/docs/papers/GLAGOL_WHITEPAPER.pdf b/docs/papers/GLAGOL_WHITEPAPER.pdf index 87d207f..92664d7 100644 Binary files a/docs/papers/GLAGOL_WHITEPAPER.pdf and b/docs/papers/GLAGOL_WHITEPAPER.pdf differ diff --git a/docs/papers/SLOVO_MANIFEST.pdf b/docs/papers/SLOVO_MANIFEST.pdf index 064c7a9..97ed375 100644 Binary files a/docs/papers/SLOVO_MANIFEST.pdf and b/docs/papers/SLOVO_MANIFEST.pdf differ diff --git a/docs/papers/SLOVO_WHITEPAPER.md b/docs/papers/SLOVO_WHITEPAPER.md index d0cd68a..7cf84e6 100644 --- a/docs/papers/SLOVO_WHITEPAPER.md +++ b/docs/papers/SLOVO_WHITEPAPER.md @@ -5,7 +5,7 @@ Sanjin Gumbarevic
hermeticum_lab@protonmail.com -Publication release: `1.0.0-beta.9` +Publication release: `1.0.0-beta.10` Technical behavior baseline: language surface through `1.0.0-beta`; tooling and install workflow through `1.0.0-beta.1`; runtime/resource foundation through @@ -15,11 +15,12 @@ discipline through `1.0.0-beta.5`; loopback networking foundation through `1.0.0-beta.6`; serialization/data-interchange foundation through `1.0.0-beta.7`; concrete type alias foundation through `1.0.0-beta.8`; collection alias unification and generic reservation through `1.0.0-beta.9` +and developer-experience API discovery through `1.0.0-beta.10` Date: 2026-05-22 Evidence source: paired Slovo/Glagol monorepo verification and benchmark -reruns from the public source tree; beta.9 release-gate verification from the +reruns from the public source tree; beta.10 release-gate verification from the public monorepo Maturity: beta @@ -34,7 +35,7 @@ explicit types, explicit failure through `option` and `result`, lexical `unsafe`, and native compilation through the Glagol compiler to LLVM IR and hosted executables. -The current publication release, `1.0.0-beta.9`, keeps the first real +The current publication release, `1.0.0-beta.10`, keeps the first real general-purpose beta language baseline from `1.0.0-beta` and records the first post-beta tooling/install hardening update plus the first runtime/resource foundation update, the first standard-library stabilization update, and the @@ -42,27 +43,31 @@ first language-usability diagnostics update, plus the first local package/workspace discipline update, the first loopback networking foundation update, the first serialization/data-interchange foundation update, and the first concrete type alias foundation plus the first collection alias -unification and generic-reservation update. The beta baseline includes the +unification and generic-reservation update, plus the first +developer-experience API discovery update. The beta baseline includes the completed `u32` / `u64` unsigned scope, the staged stdlib breadth that makes ordinary command-line programs practical, the current narrow `std.net` loopback TCP surface, the current narrow `std.json` text-construction surface, module-local transparent aliases for supported concrete types, and the current ten-scaffold benchmark -suite. This paper records the current beta -technical state, the difference between Slovo and Lisp-family languages, the -benchmark methodology, the beta.1 tooling update, the beta.2 runtime/resource +suite. It also includes an alias-normalized generated standard-library +signature catalog and deterministic `glagol symbols` source metadata for +editor-facing integrations. This paper records the current beta technical +state, the difference between Slovo and Lisp-family languages, the benchmark +methodology, the beta.1 tooling update, the beta.2 runtime/resource foundation, the beta.3 standard-library stabilization slice, the beta.4 diagnostics usability slice, the beta.5 package discipline slice, the beta.6 -networking foundation slice, the beta.7 serialization foundation slice, and -the beta.8 concrete alias foundation slice, the beta.9 collection alias and -generic-reservation slice, and the remaining path from beta to stable. +networking foundation slice, the beta.7 serialization foundation slice, the +beta.8 concrete alias foundation slice, the beta.9 collection alias and +generic-reservation slice, the beta.10 API-discovery and symbol-metadata +slice, and the remaining path from beta to stable. ## 1. Scope This document is a technical state paper for the current beta baseline. It summarizes the behavior represented by the paired Slovo and Glagol source tree, with `1.0.0-beta` as the current language-surface baseline and -`1.0.0-beta.9` as the current publication baseline. +`1.0.0-beta.10` as the current publication baseline. The support rule remains strict: @@ -74,7 +79,7 @@ The support rule remains strict: - partial parser recognition or speculative examples do not count as support Historical `exp-*` releases remain experimental alpha maturity. The current -publication accompanies `1.0.0-beta.9`. +publication accompanies `1.0.0-beta.10`. ## 2. Design Thesis @@ -388,8 +393,10 @@ diagnostics, `1.0.0-beta.5` tightens package/workspace discipline, and `1.0.0-beta.7` adds a narrow JSON construction foundation, and `1.0.0-beta.8` adds transparent concrete type aliases, and `1.0.0-beta.9` applies those aliases inside current collection/value-family facades while -reserving generic, map, and set syntax through diagnostics. None of these -post-beta slices claims changed benchmark performance. The beta.7 `json-quote-loop` +reserving generic, map, and set syntax through diagnostics, and +`1.0.0-beta.10` adds alias-normalized stdlib signature discovery plus +deterministic `glagol symbols` metadata. None of these post-beta slices claims +changed benchmark performance. The beta.7 `json-quote-loop` scaffold is present for local follow-up timing and is not part of the exp-123 nine-row result table below. @@ -512,7 +519,7 @@ Major remaining gaps before `1.0.0`: - semantic versioning and deprecation policy - a clear separation between stable and experimental features -## 10. Path Beyond `1.0.0-beta.9` +## 10. Path Beyond `1.0.0-beta.10` The beta threshold is now real. The next work should treat `1.0.0-beta` as the language compatibility-governed baseline, `1.0.0-beta.1` as the first @@ -524,7 +531,9 @@ and `1.0.0-beta.6` as the first loopback networking foundation point, and `1.0.0-beta.7` as the first serialization/data-interchange foundation point, `1.0.0-beta.8` as the first concrete type alias foundation point, and `1.0.0-beta.9` as the first collection alias unification and generic -reservation point, then move deliberately toward stable general-purpose status. +reservation point, and `1.0.0-beta.10` as the first API-discovery and +symbol-metadata point, then move deliberately toward stable general-purpose +status. Recommended sequence: diff --git a/docs/papers/SLOVO_WHITEPAPER.pdf b/docs/papers/SLOVO_WHITEPAPER.pdf index e71e038..aad1e24 100644 Binary files a/docs/papers/SLOVO_WHITEPAPER.pdf and b/docs/papers/SLOVO_WHITEPAPER.pdf differ diff --git a/scripts/render-stdlib-api-doc.js b/scripts/render-stdlib-api-doc.js index 8a8415b..d6878bc 100755 --- a/scripts/render-stdlib-api-doc.js +++ b/scripts/render-stdlib-api-doc.js @@ -7,15 +7,67 @@ 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"); +const readmePath = path.join(repoRoot, "README.md"); function tokenize(source) { const tokens = []; - const pattern = /\(|\)|[^\s()]+/g; - let match; - while ((match = pattern.exec(source)) !== null) { - tokens.push(match[0]); + let index = 0; + + while (index < source.length) { + const ch = source[index]; + + if (/\s/.test(ch)) { + index += 1; + continue; + } + + if (ch === ";") { + while (index < source.length && source[index] !== "\n") { + index += 1; + } + continue; + } + + if (ch === "(" || ch === ")") { + tokens.push(ch); + index += 1; + continue; + } + + if (ch === "\"") { + const start = index; + index += 1; + while (index < source.length) { + if (source[index] === "\\") { + index += 2; + continue; + } + if (source[index] === "\"") { + index += 1; + break; + } + index += 1; + } + if (source[index - 1] !== "\"") { + throw new Error("unterminated string literal"); + } + tokens.push(source.slice(start, index)); + continue; + } + + const start = index; + while ( + index < source.length && + !/\s/.test(source[index]) && + source[index] !== "(" && + source[index] !== ")" && + source[index] !== ";" + ) { + index += 1; + } + tokens.push(source.slice(start, index)); } + return tokens; } @@ -40,12 +92,24 @@ function parseList(tokens, cursor) { return list; } -function moduleForm(source, file) { +function topLevelForms(source, file) { const tokens = tokenize(source); - if (tokens[0] !== "(") { + const cursor = { index: 0 }; + const forms = []; + while (cursor.index < tokens.length) { + if (tokens[cursor.index] !== "(") { + throw new Error(`${file}: expected top-level list at token ${cursor.index}`); + } + forms.push(parseList(tokens, cursor)); + } + return forms; +} + +function moduleForm(forms, file) { + const form = forms.find((item) => Array.isArray(item) && item[0] === "module"); + if (!form) { 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`); } @@ -59,6 +123,71 @@ function moduleForm(source, file) { }; } +function renderType(type, aliases, stack = []) { + if (Array.isArray(type)) { + return `(${type.map((part) => renderType(part, aliases, stack)).join(" ")})`; + } + + if (aliases.has(type)) { + if (stack.includes(type)) { + throw new Error(`cyclic type alias while rendering ${stack.concat(type).join(" -> ")}`); + } + return renderType(aliases.get(type), aliases, stack.concat(type)); + } + + return type; +} + +function functionSignature(fnForm, aliases, file) { + if (fnForm.length < 5 || typeof fnForm[1] !== "string" || !Array.isArray(fnForm[2]) || fnForm[3] !== "->") { + throw new Error(`${file}: malformed (fn ...) form for ${fnForm[1] || ""}`); + } + + const name = fnForm[1]; + const params = fnForm[2].map((param) => { + if (!Array.isArray(param) || param.length !== 2 || typeof param[0] !== "string") { + throw new Error(`${file}: malformed parameter in ${name}`); + } + return `(${param[0]} ${renderType(param[1], aliases)})`; + }); + const renderedParams = params.length === 0 ? "()" : `(${params.join(" ")})`; + const returnType = renderType(fnForm[4], aliases); + return `${name} ${renderedParams} -> ${returnType}`; +} + +function indexForms(forms, file) { + const aliases = new Map(); + const functions = new Map(); + + for (const form of forms) { + if (!Array.isArray(form) || form.length < 1) { + continue; + } + + if (form[0] === "type") { + if (form.length !== 3 || typeof form[1] !== "string") { + throw new Error(`${file}: malformed (type ...) form`); + } + if (aliases.has(form[1]) || functions.has(form[1])) { + throw new Error(`${file}: duplicate public name ${form[1]}`); + } + aliases.set(form[1], form[2]); + } + + if (form[0] === "fn") { + if (typeof form[1] !== "string") { + throw new Error(`${file}: malformed (fn ...) form`); + } + if (functions.has(form[1]) || aliases.has(form[1])) { + throw new Error(`${file}: duplicate public name ${form[1]}`); + } + functions.set(form[1], form); + } + } + + return { aliases, functions }; +} + function stdModules() { return fs .readdirSync(stdDir) @@ -67,31 +196,48 @@ function stdModules() { .map((fileName) => { const relativePath = path.join("lib", "std", fileName); const source = fs.readFileSync(path.join(stdDir, fileName), "utf8"); - const module = moduleForm(source, relativePath); + const forms = topLevelForms(source, relativePath); + const module = moduleForm(forms, relativePath); + const { aliases, functions } = indexForms(forms, relativePath); + const omittedAliasExports = []; + const signatures = []; + + for (const exported of module.exports) { + if (functions.has(exported)) { + signatures.push(functionSignature(functions.get(exported), aliases, relativePath)); + } else if (aliases.has(exported)) { + omittedAliasExports.push(exported); + } else { + throw new Error(`${relativePath}: export ${exported} has no matching (fn ...) form`); + } + } + return { fileName, relativePath, name: module.name, - exports: module.exports, + signatures, + omittedAliasExports, }; }); } -function packageVersion() { - const cargoToml = fs.readFileSync(cargoTomlPath, "utf8"); - const versionMatch = cargoToml.match(/^version\s*=\s*"([^"]+)"$/m); +function releaseVersion() { + const readme = fs.readFileSync(readmePath, "utf8"); + const versionMatch = readme.match(/^Current release:\s*`([^`]+)`\.$/m); if (!versionMatch) { - throw new Error("compiler/Cargo.toml must declare a package version"); + throw new Error("README.md must declare the current release"); } return versionMatch[1]; } function render(modules, version) { - const totalExports = modules.reduce((count, module) => count + module.exports.length, 0); + const totalExports = modules.reduce((count, module) => count + module.signatures.length, 0); + const totalOmittedAliases = modules.reduce((count, module) => count + module.omittedAliasExports.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("Generated from `lib/std/*.slo` by `scripts/render-stdlib-api-doc.js`."); out.push("Do not edit this file by hand."); out.push(""); out.push("## Stability Tiers"); @@ -100,12 +246,15 @@ function render(modules, version) { 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("The catalog is a beta API discovery aid, not a stable `1.0.0` standard-library freeze."); + out.push("Module-local concrete aliases are normalized in signatures so names such as `VecI32` and `ResultU64` do not leak into the public catalog."); + out.push("Only exported `(fn ...)` helpers are listed; `(type ...)` aliases and non-exported helpers are omitted."); out.push(""); out.push("## Summary"); out.push(""); out.push(`- Modules: ${modules.length}`); - out.push(`- Exported helpers: ${totalExports}`); + out.push(`- Exported helper signatures: ${totalExports}`); + out.push(`- Exported type aliases omitted: ${totalOmittedAliases}`); out.push("- Default tier: `beta-supported`"); out.push(""); out.push("## Modules"); @@ -115,14 +264,14 @@ function render(modules, version) { out.push(""); out.push(`- Path: \`${module.relativePath}\``); out.push("- Tier: `beta-supported`"); - out.push(`- Exported helpers: ${module.exports.length}`); + out.push(`- Exported helper signatures: ${module.signatures.length}`); out.push(""); - for (const exported of module.exports) { - out.push(`- \`${exported}\``); + for (const signature of module.signatures) { + out.push(`- \`${signature}\``); } out.push(""); } return `${out.join("\n")}\n`; } -fs.writeFileSync(outputPath, render(stdModules(), packageVersion())); +fs.writeFileSync(outputPath, render(stdModules(), releaseVersion()));