Release 1.0.0-beta.11 local package api documentation
This commit is contained in:
parent
f8f0862ee3
commit
87f90ba264
57
.llm/BETA_11_LOCAL_PACKAGE_API_DOCUMENTATION.md
Normal file
57
.llm/BETA_11_LOCAL_PACKAGE_API_DOCUMENTATION.md
Normal file
@ -0,0 +1,57 @@
|
||||
# 1.0.0-beta.11 Local Package API Documentation
|
||||
|
||||
Status: release scope for `1.0.0-beta.11`.
|
||||
|
||||
`1.0.0-beta.11` extends the beta.10 API discovery lane. The release keeps the
|
||||
`1.0.0-beta` source-language and runtime baseline unchanged while making local
|
||||
package and module documentation show the public API surface users need to
|
||||
review.
|
||||
|
||||
## Scope
|
||||
|
||||
- Extend `glagol doc <file|project|workspace> -o <dir>` so generated Markdown
|
||||
includes deterministic exported/public API sections for local source files,
|
||||
projects, packages, and workspaces.
|
||||
- Render exact exported function signatures with parameter names, parameter
|
||||
types, and return types.
|
||||
- Render exported struct field names and field types.
|
||||
- Render exported enum variant names and payload types for payloadless and
|
||||
current single-payload variants.
|
||||
- Keep non-exported functions, structs, enums, tests, and `(type ...)` aliases
|
||||
out of the public API sections.
|
||||
- Normalize module-local concrete aliases before rendering public types, so
|
||||
private names such as `VecI32`, `OptionString`, or `ResultU64` do not leak
|
||||
into local package/module public docs.
|
||||
- Update README, language docs, compiler docs, and the post-beta roadmap to
|
||||
describe the beta11 documentation contract clearly.
|
||||
|
||||
## Public Contract
|
||||
|
||||
The generated local documentation is a beta API discovery aid. It exposes what
|
||||
the current local module/package export lists make public, with concrete public
|
||||
types after alias normalization.
|
||||
|
||||
The public API sections are deterministic and suitable for human review, but
|
||||
they are not a stable machine-readable Markdown schema. Headings, anchors, file
|
||||
names, and surrounding prose remain beta-scoped unless a later release freezes
|
||||
them explicitly.
|
||||
|
||||
## Explicit Non-Scope
|
||||
|
||||
- no stable Markdown schema
|
||||
- no stable stdlib/API compatibility freeze
|
||||
- no LSP server or watch mode
|
||||
- no SARIF or daemon protocol
|
||||
- no diagnostics schema policy
|
||||
- no executable generics
|
||||
- no maps or sets
|
||||
- no re-exports, glob imports, or hierarchical modules
|
||||
- no package registry semantics
|
||||
- no new compiler-known runtime names
|
||||
- no runtime helper or ABI/layout changes
|
||||
|
||||
## Checks
|
||||
|
||||
Focused checks for this slice:
|
||||
|
||||
- `git diff --check -- README.md docs/POST_BETA_ROADMAP.md docs/language/ROADMAP.md docs/language/RELEASE_NOTES.md docs/language/SPEC-v1.md docs/compiler/ROADMAP.md docs/compiler/RELEASE_NOTES.md .llm/BETA_11_LOCAL_PACKAGE_API_DOCUMENTATION.md`
|
||||
73
.llm/reviews/BETA_11_RELEASE_REVIEW.md
Normal file
73
.llm/reviews/BETA_11_RELEASE_REVIEW.md
Normal file
@ -0,0 +1,73 @@
|
||||
# 1.0.0-beta.11 Release Review
|
||||
|
||||
Status: ready for publication after the controller release gate.
|
||||
|
||||
## Verdict
|
||||
|
||||
No blocking issues remain after controller follow-up. The original workspace
|
||||
package API leak was reproduced, fixed, and covered by a regression test before
|
||||
publication.
|
||||
|
||||
## Resolved Finding
|
||||
|
||||
- Workspace package API docs originally included compiler-loaded standard
|
||||
library modules when a workspace package imported `std.option`.
|
||||
- Controller fix: workspace package API rendering now filters package modules
|
||||
to the package source root before collecting public API sections, matching
|
||||
the existing project-mode local-root behavior. Ordinary module summaries can
|
||||
still show loaded standard-library modules, but package API sections no
|
||||
longer present those modules as package-owned API.
|
||||
- Regression added:
|
||||
`doc_workspace_package_api_excludes_loaded_std_modules`.
|
||||
|
||||
## Scope Checked
|
||||
|
||||
- `compiler/src/docgen.rs` adds per-module public API sections and package API
|
||||
sections, renders exported function signatures, struct fields, enum variants,
|
||||
and normalizes module-local aliases before public rendering.
|
||||
- `compiler/tests/doc_api_beta11.rs` covers file, project, workspace scaffold,
|
||||
workspace packages importing `std.*`, alias normalization, non-exported
|
||||
function omission, and byte-identical repeat generation for a file input.
|
||||
- README, compiler/language roadmaps, release notes, SPEC-v1, `STDLIB_API.md`,
|
||||
and the beta11 `.llm` contract include the main deferrals: no stable Markdown
|
||||
schema, no stable stdlib/API compatibility freeze, no LSP/watch, no
|
||||
SARIF/daemon protocol, no diagnostics schema policy, no executable generics,
|
||||
no maps/sets, no re-exports/globs/hierarchical modules, and no registry
|
||||
semantics.
|
||||
- Version bumps in `compiler/Cargo.toml` and `compiler/Cargo.lock` move
|
||||
`glagol` from `1.0.0-beta.10` to `1.0.0-beta.11`.
|
||||
|
||||
## Test Coverage Notes
|
||||
|
||||
- Workspace packages that import `std.*` are now covered by a focused
|
||||
regression that verifies package API excludes loaded standard-library
|
||||
modules and helpers.
|
||||
- Determinism is asserted byte-for-byte only for file docs. Project and
|
||||
workspace determinism are indirectly exercised by sorted rendering but not
|
||||
protected by repeat-generation tests.
|
||||
- Non-export filtering is asserted for functions and aliases. There is no
|
||||
explicit negative test for non-exported structs, non-exported enums, or tests
|
||||
appearing in public API sections.
|
||||
- Alias normalization is covered for file/module docs, but not for project or
|
||||
workspace package API fixtures.
|
||||
|
||||
## Verification
|
||||
|
||||
- `cargo fmt --check`: passed.
|
||||
- `cargo check`: passed.
|
||||
- `cargo test --test doc_api_beta11`: passed, 6 tests.
|
||||
- `cargo test --test dx_v1_7`: passed, 13 tests.
|
||||
- `cargo test --test symbols_beta10`: passed, 4 tests.
|
||||
- `cargo test --test promotion_gate`: passed, 1 unignored test.
|
||||
- `git diff --check`: passed.
|
||||
- Stale beta10 current-release/version scan over README, docs, `.llm`, and
|
||||
compiler package metadata: no matches.
|
||||
- Private/local publication text scan over README, docs, scripts, compiler
|
||||
source/tests, lib, examples, benchmarks, tests, and `.llm`: no matches.
|
||||
- `node scripts/render-stdlib-api-doc.js`: passed and refreshed the catalog
|
||||
release wording to `1.0.0-beta.11`.
|
||||
- Manual workspace std-import doc generation after the fix: package API no
|
||||
longer includes loaded standard-library modules.
|
||||
- `./scripts/release-gate.sh`: 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.
|
||||
52
README.md
52
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.10`.
|
||||
Current release: `1.0.0-beta.11`.
|
||||
|
||||
## Repository Layout
|
||||
|
||||
@ -24,16 +24,17 @@ scripts/ local release and document tooling
|
||||
|
||||
## Beta Scope
|
||||
|
||||
`1.0.0-beta.10` keeps the `1.0.0-beta` language baseline, includes the
|
||||
`1.0.0-beta.11` 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
|
||||
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, plus the `1.0.0-beta.10` developer-experience API
|
||||
discovery slice. The language baseline supports practical local
|
||||
alias foundation, the `1.0.0-beta.9` collection alias unification and
|
||||
generic reservation slice, the `1.0.0-beta.10` developer-experience API
|
||||
discovery slice, and the `1.0.0-beta.11` local package API documentation
|
||||
slice. The language baseline supports practical local
|
||||
command-line, file, and loopback-network programs with:
|
||||
|
||||
- modules, explicit imports, packages, and local workspaces
|
||||
@ -56,17 +57,24 @@ types, and omits non-exported helpers and `(type ...)` aliases.
|
||||
`glagol symbols <file.slo|project|workspace>` emits deterministic
|
||||
editor-facing S-expression metadata for modules, imports, exports, aliases,
|
||||
structs, enums, functions, tests, source spans, and workspace package names.
|
||||
`glagol doc <file|project|workspace> -o <dir>` now includes deterministic public
|
||||
API sections for local package and module documentation: exact exported
|
||||
function signatures, exported struct fields, exported enum variants and payload
|
||||
types, non-export filtering, and module-local alias normalization.
|
||||
|
||||
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.
|
||||
registry semantics, stable Markdown schema, stable stdlib/API compatibility
|
||||
freeze, DNS/TLS/async networking, LSP/watch guarantees, SARIF and daemon
|
||||
protocols, diagnostics schema policy, re-exports/globs/hierarchical modules,
|
||||
stable ABI and layout, and runtime changes for generic collections.
|
||||
|
||||
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.
|
||||
The next likely language slice after `1.0.0-beta.11` should continue from the
|
||||
developer-experience, diagnostics, and reserved generic/collection lanes
|
||||
without claiming executable generics, maps, sets, traits, inference,
|
||||
monomorphization, iterators, ABI stability, runtime changes, LSP/watch
|
||||
protocols, registry semantics, stable Markdown schema, or a
|
||||
standard-library/API compatibility freeze until the contract and gates are
|
||||
explicit.
|
||||
|
||||
## Build And Test
|
||||
|
||||
@ -261,6 +269,24 @@ 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.
|
||||
|
||||
## 1.0.0-beta.11 Local Package API Documentation
|
||||
|
||||
The `1.0.0-beta.11` release extends the beta.10 API discovery lane to local
|
||||
package and module documentation. `glagol doc <file|project|workspace> -o <dir>`
|
||||
includes deterministic exported/public API sections for local modules and
|
||||
workspace packages.
|
||||
|
||||
Those sections list exact exported function signatures, exported struct fields,
|
||||
and exported enum variants with payload types. They omit non-exported
|
||||
functions, structs, enums, tests, and aliases from the public API surface, and
|
||||
they normalize module-local concrete aliases before rendering public types.
|
||||
|
||||
This remains beta API discovery. It does not freeze the Markdown schema, create
|
||||
a stable stdlib/API compatibility freeze, add LSP/watch behavior, define
|
||||
SARIF or daemon protocols, set diagnostics schema policy, implement executable
|
||||
generics, maps, or sets, add re-exports, globs, or hierarchical modules, or
|
||||
define package registry semantics.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Language Manifest](docs/language/MANIFEST.md)
|
||||
|
||||
2
compiler/Cargo.lock
generated
2
compiler/Cargo.lock
generated
@ -4,4 +4,4 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "glagol"
|
||||
version = "1.0.0-beta.10"
|
||||
version = "1.0.0-beta.11"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "glagol"
|
||||
version = "1.0.0-beta.10"
|
||||
version = "1.0.0-beta.11"
|
||||
edition = "2021"
|
||||
description = "Glagol, the first compiler for the Slovo language"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
use std::{fs, path::Path};
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
fs,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ast::Program,
|
||||
@ -6,6 +10,7 @@ use crate::{
|
||||
lexer, lower,
|
||||
project::{self, ProjectArtifact, SourceFile, ToolFailure, WorkspaceArtifact},
|
||||
sexpr::{Atom, SExpr, SExprKind},
|
||||
types::Type,
|
||||
};
|
||||
|
||||
pub fn generate(input: &str, output_dir: &str) -> Result<(), ToolFailure> {
|
||||
@ -56,16 +61,23 @@ fn render_project(
|
||||
artifact: &ProjectArtifact,
|
||||
sources: &[SourceFile],
|
||||
) -> Result<String, ToolFailure> {
|
||||
let modules = sources
|
||||
.iter()
|
||||
.map(document_source)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let mut out = String::new();
|
||||
out.push_str("# Project ");
|
||||
out.push_str(&artifact.project_name);
|
||||
out.push_str("\n\n");
|
||||
if let Some(workspace) = &artifact.workspace {
|
||||
render_workspace(&mut out, workspace);
|
||||
render_workspace_package_public_api(&mut out, workspace, &modules);
|
||||
} else {
|
||||
render_project_package_public_api(&mut out, artifact, &modules);
|
||||
}
|
||||
for source in sources {
|
||||
let module = document_source(source)?;
|
||||
render_module(&mut out, &module);
|
||||
for module in &modules {
|
||||
render_module(&mut out, module);
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
@ -120,27 +132,58 @@ fn document_source(source: &SourceFile) -> Result<DocModule, ToolFailure> {
|
||||
sources: vec![source.clone()],
|
||||
artifact: None,
|
||||
})?;
|
||||
let program = lower::lower_program(&source.path, &forms).ok();
|
||||
let lowerable_forms = forms
|
||||
.iter()
|
||||
.filter(|form| !matches!(list_head(form), Some("import")))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let program = lower::lower_program(&source.path, &lowerable_forms).ok();
|
||||
Ok(module_from_forms(&source.path, &forms, program.as_ref()))
|
||||
}
|
||||
|
||||
struct DocModule {
|
||||
path: String,
|
||||
title: String,
|
||||
imports: Vec<String>,
|
||||
exports: Vec<String>,
|
||||
structs: Vec<String>,
|
||||
functions: Vec<String>,
|
||||
tests: Vec<String>,
|
||||
public_api: PublicApi,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PublicApi {
|
||||
functions: Vec<DocFunction>,
|
||||
structs: Vec<DocStruct>,
|
||||
enums: Vec<DocEnum>,
|
||||
}
|
||||
|
||||
struct DocFunction {
|
||||
name: String,
|
||||
signature: String,
|
||||
}
|
||||
|
||||
struct DocStruct {
|
||||
name: String,
|
||||
fields: Vec<String>,
|
||||
}
|
||||
|
||||
struct DocEnum {
|
||||
name: String,
|
||||
variants: Vec<String>,
|
||||
}
|
||||
|
||||
fn module_from_forms(file: &str, forms: &[SExpr], program: Option<&Program>) -> DocModule {
|
||||
let mut module = DocModule {
|
||||
path: file.to_string(),
|
||||
title: file.to_string(),
|
||||
imports: Vec::new(),
|
||||
exports: Vec::new(),
|
||||
structs: Vec::new(),
|
||||
functions: Vec::new(),
|
||||
tests: Vec::new(),
|
||||
public_api: PublicApi::default(),
|
||||
};
|
||||
|
||||
for form in forms {
|
||||
@ -205,6 +248,7 @@ fn module_from_forms(file: &str, forms: &[SExpr], program: Option<&Program>) ->
|
||||
})
|
||||
.collect();
|
||||
module.tests = program.tests.iter().map(|test| test.name.clone()).collect();
|
||||
module.public_api = public_api_from_program(program, &module.exports);
|
||||
}
|
||||
|
||||
module.imports.sort();
|
||||
@ -221,11 +265,120 @@ fn render_module(out: &mut String, module: &DocModule) {
|
||||
out.push_str("\n\n");
|
||||
render_list(out, "Imports", &module.imports);
|
||||
render_list(out, "Exports", &module.exports);
|
||||
render_public_api_section(out, "###", &module.public_api);
|
||||
render_list(out, "Structs", &module.structs);
|
||||
render_list(out, "Functions", &module.functions);
|
||||
render_list(out, "Tests", &module.tests);
|
||||
}
|
||||
|
||||
fn render_project_package_public_api(
|
||||
out: &mut String,
|
||||
artifact: &ProjectArtifact,
|
||||
modules: &[DocModule],
|
||||
) {
|
||||
let local_root = Path::new(&artifact.project_root).join(&artifact.source_root);
|
||||
let local_modules = modules
|
||||
.iter()
|
||||
.filter(|module| Path::new(&module.path).starts_with(&local_root))
|
||||
.collect::<Vec<_>>();
|
||||
render_package_public_api(out, &artifact.project_name, &local_modules);
|
||||
}
|
||||
|
||||
fn render_workspace_package_public_api(
|
||||
out: &mut String,
|
||||
workspace: &WorkspaceArtifact,
|
||||
modules: &[DocModule],
|
||||
) {
|
||||
let by_path = modules
|
||||
.iter()
|
||||
.map(|module| (module.path.as_str(), module))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
|
||||
for package in &workspace.packages {
|
||||
let local_root = Path::new(&package.root).join(&package.source_root);
|
||||
let package_modules = package
|
||||
.modules
|
||||
.iter()
|
||||
.filter(|module| Path::new(&module.path).starts_with(&local_root))
|
||||
.filter_map(|module| by_path.get(module.path.as_str()).copied())
|
||||
.collect::<Vec<_>>();
|
||||
render_package_public_api(
|
||||
out,
|
||||
&format!("{} {}", package.name, package.version),
|
||||
&package_modules,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_package_public_api(out: &mut String, package_name: &str, modules: &[&DocModule]) {
|
||||
out.push_str("## Package API ");
|
||||
out.push_str(package_name);
|
||||
out.push_str("\n\n");
|
||||
|
||||
let public_modules = modules
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(|module| !module.public_api.is_empty())
|
||||
.collect::<Vec<_>>();
|
||||
if public_modules.is_empty() {
|
||||
out.push_str("None.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for module in public_modules {
|
||||
out.push_str("### Module ");
|
||||
out.push_str(&module.title);
|
||||
out.push_str("\n\n");
|
||||
render_public_api_body(out, "####", &module.public_api);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_public_api_section(out: &mut String, heading: &str, public_api: &PublicApi) {
|
||||
out.push_str(heading);
|
||||
out.push_str(" Public API\n\n");
|
||||
render_public_api_body(out, "####", public_api);
|
||||
}
|
||||
|
||||
fn render_public_api_body(out: &mut String, heading: &str, public_api: &PublicApi) {
|
||||
if public_api.is_empty() {
|
||||
out.push_str("None.\n\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if !public_api.functions.is_empty() {
|
||||
out.push_str(heading);
|
||||
out.push_str(" Functions\n");
|
||||
for function in &public_api.functions {
|
||||
render_code_bullet(out, &function.signature);
|
||||
}
|
||||
out.push('\n');
|
||||
}
|
||||
|
||||
if !public_api.structs.is_empty() {
|
||||
out.push_str(heading);
|
||||
out.push_str(" Structs\n");
|
||||
for struct_decl in &public_api.structs {
|
||||
render_code_bullet(out, &format!("struct {}", struct_decl.name));
|
||||
for field in &struct_decl.fields {
|
||||
render_indented_code_bullet(out, field);
|
||||
}
|
||||
}
|
||||
out.push('\n');
|
||||
}
|
||||
|
||||
if !public_api.enums.is_empty() {
|
||||
out.push_str(heading);
|
||||
out.push_str(" Enums\n");
|
||||
for enum_decl in &public_api.enums {
|
||||
render_code_bullet(out, &format!("enum {}", enum_decl.name));
|
||||
for variant in &enum_decl.variants {
|
||||
render_indented_code_bullet(out, variant);
|
||||
}
|
||||
}
|
||||
out.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
fn render_list(out: &mut String, title: &str, values: &[String]) {
|
||||
out.push_str("### ");
|
||||
out.push_str(title);
|
||||
@ -242,6 +395,182 @@ fn render_list(out: &mut String, title: &str, values: &[String]) {
|
||||
out.push('\n');
|
||||
}
|
||||
|
||||
fn list_head(expr: &SExpr) -> Option<&str> {
|
||||
list(expr).and_then(|items| items.first()).and_then(ident)
|
||||
}
|
||||
|
||||
fn render_code_bullet(out: &mut String, value: &str) {
|
||||
out.push_str("- `");
|
||||
out.push_str(&value.replace('`', "\\`"));
|
||||
out.push_str("`\n");
|
||||
}
|
||||
|
||||
fn render_indented_code_bullet(out: &mut String, value: &str) {
|
||||
out.push_str(" - `");
|
||||
out.push_str(&value.replace('`', "\\`"));
|
||||
out.push_str("`\n");
|
||||
}
|
||||
|
||||
impl PublicApi {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.functions.is_empty() && self.structs.is_empty() && self.enums.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
fn public_api_from_program(program: &Program, exports: &[String]) -> PublicApi {
|
||||
let export_names = exports.iter().cloned().collect::<BTreeSet<_>>();
|
||||
let aliases = alias_targets(program);
|
||||
|
||||
let mut functions = program
|
||||
.functions
|
||||
.iter()
|
||||
.filter(|function| export_names.contains(&function.name))
|
||||
.map(|function| DocFunction {
|
||||
name: function.name.clone(),
|
||||
signature: function_signature(
|
||||
&function.name,
|
||||
function
|
||||
.params
|
||||
.iter()
|
||||
.map(|param| (param.name.as_str(), ¶m.ty)),
|
||||
&function.return_type,
|
||||
&aliases,
|
||||
),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
functions.sort_by(|left, right| left.name.cmp(&right.name));
|
||||
|
||||
let mut structs = program
|
||||
.structs
|
||||
.iter()
|
||||
.filter(|struct_decl| export_names.contains(&struct_decl.name))
|
||||
.map(|struct_decl| DocStruct {
|
||||
name: struct_decl.name.clone(),
|
||||
fields: struct_decl
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
format!(
|
||||
"{}: {}",
|
||||
field.name,
|
||||
display_public_type(&field.ty, &aliases)
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
structs.sort_by(|left, right| left.name.cmp(&right.name));
|
||||
|
||||
let mut enums = program
|
||||
.enums
|
||||
.iter()
|
||||
.filter(|enum_decl| export_names.contains(&enum_decl.name))
|
||||
.map(|enum_decl| DocEnum {
|
||||
name: enum_decl.name.clone(),
|
||||
variants: enum_decl
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| match &variant.payload_ty {
|
||||
Some(payload_ty) => {
|
||||
format!(
|
||||
"{}({})",
|
||||
variant.name,
|
||||
display_public_type(payload_ty, &aliases)
|
||||
)
|
||||
}
|
||||
None => variant.name.clone(),
|
||||
})
|
||||
.collect(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
enums.sort_by(|left, right| left.name.cmp(&right.name));
|
||||
|
||||
PublicApi {
|
||||
functions,
|
||||
structs,
|
||||
enums,
|
||||
}
|
||||
}
|
||||
|
||||
fn function_signature<'a>(
|
||||
name: &str,
|
||||
params: impl Iterator<Item = (&'a str, &'a Type)>,
|
||||
return_type: &Type,
|
||||
aliases: &BTreeMap<String, Type>,
|
||||
) -> String {
|
||||
let params = params
|
||||
.map(|(name, ty)| format!("{}: {}", name, display_public_type(ty, aliases)))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
format!(
|
||||
"fn {}({}) -> {}",
|
||||
name,
|
||||
params,
|
||||
display_public_type(return_type, aliases)
|
||||
)
|
||||
}
|
||||
|
||||
fn alias_targets(program: &Program) -> BTreeMap<String, Type> {
|
||||
let raw = program
|
||||
.type_aliases
|
||||
.iter()
|
||||
.map(|alias| (alias.name.clone(), alias.target.clone()))
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
raw.keys()
|
||||
.map(|name| {
|
||||
let mut visiting = BTreeSet::new();
|
||||
(
|
||||
name.clone(),
|
||||
resolve_alias_type(&Type::Named(name.clone()), &raw, &mut visiting),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn display_public_type(ty: &Type, aliases: &BTreeMap<String, Type>) -> String {
|
||||
let mut visiting = BTreeSet::new();
|
||||
resolve_alias_type(ty, aliases, &mut visiting).to_string()
|
||||
}
|
||||
|
||||
fn resolve_alias_type(
|
||||
ty: &Type,
|
||||
aliases: &BTreeMap<String, Type>,
|
||||
visiting: &mut BTreeSet<String>,
|
||||
) -> Type {
|
||||
match ty {
|
||||
Type::Named(name) => {
|
||||
let Some(target) = aliases.get(name) else {
|
||||
return ty.clone();
|
||||
};
|
||||
if !visiting.insert(name.clone()) {
|
||||
return ty.clone();
|
||||
}
|
||||
let resolved = resolve_alias_type(target, aliases, visiting);
|
||||
visiting.remove(name);
|
||||
resolved
|
||||
}
|
||||
Type::Ptr(inner) => Type::Ptr(Box::new(resolve_alias_type(inner, aliases, visiting))),
|
||||
Type::Array(inner, len) => {
|
||||
Type::Array(Box::new(resolve_alias_type(inner, aliases, visiting)), *len)
|
||||
}
|
||||
Type::Vec(inner) => Type::Vec(Box::new(resolve_alias_type(inner, aliases, visiting))),
|
||||
Type::Slice(inner) => Type::Slice(Box::new(resolve_alias_type(inner, aliases, visiting))),
|
||||
Type::Option(inner) => Type::Option(Box::new(resolve_alias_type(inner, aliases, visiting))),
|
||||
Type::Result(ok, err) => Type::Result(
|
||||
Box::new(resolve_alias_type(ok, aliases, visiting)),
|
||||
Box::new(resolve_alias_type(err, aliases, visiting)),
|
||||
),
|
||||
Type::I32
|
||||
| Type::I64
|
||||
| Type::U32
|
||||
| Type::U64
|
||||
| Type::F64
|
||||
| Type::Bool
|
||||
| Type::Unit
|
||||
| Type::String => ty.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn list(expr: &SExpr) -> Option<&[SExpr]> {
|
||||
match &expr.kind {
|
||||
SExprKind::List(items) => Some(items),
|
||||
|
||||
420
compiler/tests/doc_api_beta11.rs
Normal file
420
compiler/tests/doc_api_beta11.rs
Normal file
@ -0,0 +1,420 @@
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Output},
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
#[test]
|
||||
fn doc_file_renders_public_api_with_signatures_and_shapes() {
|
||||
let source = r#"(module api (export Point Status make))
|
||||
|
||||
(struct Point
|
||||
(x i32)
|
||||
(label string))
|
||||
|
||||
(enum Status Ready (Blocked i32))
|
||||
|
||||
(fn helper ((value i32)) -> i32
|
||||
value)
|
||||
|
||||
(fn make ((x i32) (label string)) -> Point
|
||||
(Point (x x) (label label)))
|
||||
|
||||
(test "make is documented"
|
||||
true)
|
||||
"#;
|
||||
let file = write_file("file-api", source);
|
||||
let docs = unique_path("file-api-docs");
|
||||
|
||||
let output = run_glagol([
|
||||
OsStr::new("doc"),
|
||||
file.as_os_str(),
|
||||
OsStr::new("-o"),
|
||||
docs.as_os_str(),
|
||||
]);
|
||||
|
||||
assert_success("doc file", &output);
|
||||
let index = read_index(&docs);
|
||||
assert!(index.contains("## Module api"));
|
||||
assert!(index.contains("### Imports\n\nNone.\n\n"));
|
||||
assert!(index.contains("- `Point`"));
|
||||
assert!(index.contains("- `make(x i32, label string) -> Point`"));
|
||||
assert!(index.contains("- `make is documented`"));
|
||||
|
||||
let api = public_api_for_module(&index, "api");
|
||||
assert!(api.contains("- `fn make(x: i32, label: string) -> Point`"));
|
||||
assert!(api.contains("- `struct Point`"));
|
||||
assert!(api.contains(" - `x: i32`"));
|
||||
assert!(api.contains(" - `label: string`"));
|
||||
assert!(api.contains("- `enum Status`"));
|
||||
assert!(api.contains(" - `Ready`"));
|
||||
assert!(api.contains(" - `Blocked(i32)`"));
|
||||
assert!(
|
||||
!api.contains("helper"),
|
||||
"non-exported helper leaked into public API:\n{}",
|
||||
api
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doc_project_renders_package_and_module_public_api() {
|
||||
let project = write_project(
|
||||
"project-api",
|
||||
&[(
|
||||
"math",
|
||||
r#"(module math (export add Pair))
|
||||
|
||||
(struct Pair
|
||||
(left i32)
|
||||
(right i32))
|
||||
|
||||
(fn add ((left i32) (right i32)) -> i32
|
||||
(+ left right))
|
||||
|
||||
(fn private_double ((value i32)) -> i32
|
||||
(+ value value))
|
||||
"#,
|
||||
)],
|
||||
"(module main)\n\n(import math (add Pair))\n\n(fn main () -> i32\n (add 1 2))\n",
|
||||
);
|
||||
let docs = unique_path("project-api-docs");
|
||||
|
||||
let output = run_glagol([
|
||||
OsStr::new("doc"),
|
||||
project.as_os_str(),
|
||||
OsStr::new("-o"),
|
||||
docs.as_os_str(),
|
||||
]);
|
||||
|
||||
assert_success("doc project", &output);
|
||||
let index = read_index(&docs);
|
||||
assert!(index.contains("# Project project-api"));
|
||||
assert!(index.contains("## Package API project-api"));
|
||||
assert!(index.contains("## Module math"));
|
||||
assert!(index.contains("## Module main"));
|
||||
assert!(index.contains("- `math`"));
|
||||
assert!(index.contains("- `add`"));
|
||||
|
||||
let package_api = package_api(&index, "project-api");
|
||||
assert!(package_api.contains("### Module math"));
|
||||
assert!(package_api.contains("- `fn add(left: i32, right: i32) -> i32`"));
|
||||
assert!(package_api.contains("- `struct Pair`"));
|
||||
assert!(
|
||||
!package_api.contains("private_double"),
|
||||
"non-exported function leaked into package API:\n{}",
|
||||
package_api
|
||||
);
|
||||
|
||||
let math_api = public_api_for_module(&index, "math");
|
||||
assert!(math_api.contains("- `fn add(left: i32, right: i32) -> i32`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doc_workspace_renders_each_package_api_deterministically() {
|
||||
let workspace = unique_path("workspace-api");
|
||||
let scaffold = run_glagol([
|
||||
OsStr::new("new"),
|
||||
workspace.as_os_str(),
|
||||
OsStr::new("--template"),
|
||||
OsStr::new("workspace"),
|
||||
]);
|
||||
assert_success("workspace scaffold", &scaffold);
|
||||
|
||||
let docs = unique_path("workspace-api-docs");
|
||||
let output = run_glagol([
|
||||
OsStr::new("doc"),
|
||||
workspace.as_os_str(),
|
||||
OsStr::new("-o"),
|
||||
docs.as_os_str(),
|
||||
]);
|
||||
|
||||
assert_success("doc workspace", &output);
|
||||
let index = read_index(&docs);
|
||||
assert!(index.contains("## Workspace"));
|
||||
assert!(index.contains("- `packages/app`"));
|
||||
assert!(index.contains("- `packages/libutil`"));
|
||||
assert!(index.contains("## Package API app 0.1.0"));
|
||||
assert!(index.contains("## Package API libutil 0.1.0"));
|
||||
|
||||
let app_api = package_api(&index, "app 0.1.0");
|
||||
assert!(app_api.contains("None."));
|
||||
|
||||
let lib_api = package_api(&index, "libutil 0.1.0");
|
||||
assert!(lib_api.contains("### Module libutil"));
|
||||
assert!(lib_api.contains("- `fn answer() -> i32`"));
|
||||
assert!(lib_api.contains("- `fn label() -> string`"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doc_workspace_package_api_excludes_loaded_std_modules() {
|
||||
let workspace = write_workspace_with_std_import("workspace-std-api");
|
||||
let docs = unique_path("workspace-std-api-docs");
|
||||
|
||||
let output = run_glagol([
|
||||
OsStr::new("doc"),
|
||||
workspace.as_os_str(),
|
||||
OsStr::new("-o"),
|
||||
docs.as_os_str(),
|
||||
]);
|
||||
|
||||
assert_success("doc workspace std import", &output);
|
||||
let index = read_index(&docs);
|
||||
assert!(
|
||||
index.contains("## Module option"),
|
||||
"module summaries should still include loaded std module docs:\n{}",
|
||||
index
|
||||
);
|
||||
|
||||
let app_api = package_api(&index, "app 0.1.0");
|
||||
assert!(app_api.contains("### Module main"));
|
||||
assert!(app_api.contains("- `fn local_some(value: i32) -> (option i32)`"));
|
||||
assert!(
|
||||
!app_api.contains("Module std.option") && !app_api.contains("Module option"),
|
||||
"loaded std module leaked into package API:\n{}",
|
||||
app_api
|
||||
);
|
||||
assert!(
|
||||
!app_api.contains("some_i32"),
|
||||
"loaded std helper leaked into package API:\n{}",
|
||||
app_api
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_api_normalizes_local_aliases_and_omits_alias_exports() {
|
||||
let source = r#"(module aliases (export Count Score Status measure))
|
||||
|
||||
(type Count i32)
|
||||
(type MaybeCount (option Count))
|
||||
|
||||
(struct Score
|
||||
(value Count)
|
||||
(maybe MaybeCount))
|
||||
|
||||
(enum Status Ready (Blocked Count) (Maybe MaybeCount))
|
||||
|
||||
(fn hidden ((value Count)) -> Count
|
||||
value)
|
||||
|
||||
(fn measure ((value Count) (maybe MaybeCount)) -> Count
|
||||
value)
|
||||
"#;
|
||||
let file = write_file("alias-api", source);
|
||||
let docs = unique_path("alias-api-docs");
|
||||
|
||||
let output = run_glagol([
|
||||
OsStr::new("doc"),
|
||||
file.as_os_str(),
|
||||
OsStr::new("-o"),
|
||||
docs.as_os_str(),
|
||||
]);
|
||||
|
||||
assert_success("doc aliases", &output);
|
||||
let index = read_index(&docs);
|
||||
assert!(
|
||||
index.contains("- `Count`"),
|
||||
"exports summary should retain the alias name"
|
||||
);
|
||||
assert!(
|
||||
index.contains("- `hidden(value Count) -> Count`"),
|
||||
"function summary should retain non-public declarations"
|
||||
);
|
||||
|
||||
let api = public_api_for_module(&index, "aliases");
|
||||
assert!(api.contains("- `fn measure(value: i32, maybe: (option i32)) -> i32`"));
|
||||
assert!(api.contains(" - `value: i32`"));
|
||||
assert!(api.contains(" - `maybe: (option i32)`"));
|
||||
assert!(api.contains(" - `Blocked(i32)`"));
|
||||
assert!(api.contains(" - `Maybe((option i32))`"));
|
||||
assert!(
|
||||
!api.contains("Count"),
|
||||
"alias names leaked into public API:\n{}",
|
||||
api
|
||||
);
|
||||
assert!(
|
||||
!api.contains("hidden"),
|
||||
"non-exported function leaked into public API:\n{}",
|
||||
api
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_doc_generation_is_byte_identical() {
|
||||
let source = r#"(module stable (export value))
|
||||
|
||||
(fn value () -> i32
|
||||
42)
|
||||
"#;
|
||||
let file = write_file("stable-api", source);
|
||||
let docs = unique_path("stable-api-docs");
|
||||
|
||||
let first = run_glagol([
|
||||
OsStr::new("doc"),
|
||||
file.as_os_str(),
|
||||
OsStr::new("-o"),
|
||||
docs.as_os_str(),
|
||||
]);
|
||||
assert_success("first doc", &first);
|
||||
let first_bytes = fs::read(docs.join("index.md")).expect("read first docs");
|
||||
|
||||
let second = run_glagol([
|
||||
OsStr::new("doc"),
|
||||
file.as_os_str(),
|
||||
OsStr::new("-o"),
|
||||
docs.as_os_str(),
|
||||
]);
|
||||
assert_success("second doc", &second);
|
||||
let second_bytes = fs::read(docs.join("index.md")).expect("read second docs");
|
||||
|
||||
assert_eq!(first_bytes, second_bytes);
|
||||
}
|
||||
|
||||
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_workspace_with_std_import(name: &str) -> PathBuf {
|
||||
let workspace = unique_path(name);
|
||||
let package = workspace.join("packages/app");
|
||||
fs::create_dir_all(package.join("src")).expect("create workspace package src");
|
||||
fs::write(
|
||||
workspace.join("slovo.toml"),
|
||||
"[workspace]\nmembers = [\"packages/app\"]\ndefault_package = \"app\"\n",
|
||||
)
|
||||
.expect("write workspace manifest");
|
||||
fs::write(
|
||||
package.join("slovo.toml"),
|
||||
"[package]\nname = \"app\"\nversion = \"0.1.0\"\nsource_root = \"src\"\nentry = \"main\"\n",
|
||||
)
|
||||
.expect("write package manifest");
|
||||
fs::write(
|
||||
package.join("src/main.slo"),
|
||||
r#"(module main (export local_some))
|
||||
|
||||
(import std.option (some_i32))
|
||||
|
||||
(fn local_some ((value i32)) -> (option i32)
|
||||
(some_i32 value))
|
||||
"#,
|
||||
)
|
||||
.expect("write package main");
|
||||
workspace
|
||||
}
|
||||
|
||||
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 read_index(docs: &Path) -> String {
|
||||
fs::read_to_string(docs.join("index.md")).expect("read generated docs")
|
||||
}
|
||||
|
||||
fn package_api<'a>(docs: &'a str, package: &str) -> &'a str {
|
||||
let heading = format!("## Package API {}", package);
|
||||
let start = docs
|
||||
.find(&heading)
|
||||
.unwrap_or_else(|| panic!("missing package API heading `{}`\n{}", heading, docs));
|
||||
let rest = &docs[start..];
|
||||
let end = rest
|
||||
.find("\n## Package API ")
|
||||
.or_else(|| rest.find("\n## Module "))
|
||||
.unwrap_or(rest.len());
|
||||
&rest[..end]
|
||||
}
|
||||
|
||||
fn public_api_for_module<'a>(docs: &'a str, module: &str) -> &'a str {
|
||||
let heading = format!("## Module {}", module);
|
||||
let module_start = if docs.starts_with(&heading) {
|
||||
0
|
||||
} else {
|
||||
let marker = format!("\n{}", heading);
|
||||
docs.find(&marker)
|
||||
.map(|index| index + 1)
|
||||
.unwrap_or_else(|| panic!("missing module heading `{}`\n{}", heading, docs))
|
||||
};
|
||||
let module_docs = &docs[module_start..];
|
||||
let module_end = module_docs
|
||||
.find("\n## Module ")
|
||||
.unwrap_or(module_docs.len());
|
||||
let module_docs = &module_docs[..module_end];
|
||||
let public_start = module_docs.find("### Public API").unwrap_or_else(|| {
|
||||
panic!(
|
||||
"missing public API for module `{}`\n{}",
|
||||
module, module_docs
|
||||
)
|
||||
});
|
||||
let public_docs = &module_docs[public_start..];
|
||||
let public_end = public_docs
|
||||
.find("\n### Structs")
|
||||
.unwrap_or(public_docs.len());
|
||||
&public_docs[..public_end]
|
||||
}
|
||||
|
||||
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-doc-api-beta11-{}-{}-{}-{}",
|
||||
std::process::id(),
|
||||
nanos,
|
||||
id,
|
||||
name
|
||||
))
|
||||
}
|
||||
|
||||
fn run_glagol<I, S>(args: I) -> Output
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
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)
|
||||
);
|
||||
assert!(
|
||||
output.stdout.is_empty(),
|
||||
"{} wrote stdout:\n{}",
|
||||
context,
|
||||
String::from_utf8_lossy(&output.stdout)
|
||||
);
|
||||
assert!(
|
||||
output.stderr.is_empty(),
|
||||
"{} wrote stderr:\n{}",
|
||||
context,
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
@ -270,7 +270,6 @@ Work:
|
||||
- language-server diagnostics and document symbols
|
||||
- editor-facing symbol metadata for files, projects, and workspaces
|
||||
- project watch mode
|
||||
- generated API documentation for local packages
|
||||
- clearer benchmark harness output
|
||||
- machine-readable diagnostics stability policy
|
||||
|
||||
@ -281,12 +280,45 @@ public types. `glagol symbols <file.slo|project|workspace>` 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.
|
||||
API freeze. LSP, watch mode, benchmark-output work, stable Markdown schema,
|
||||
stable stdlib/API compatibility freeze, SARIF/daemon protocols, 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.
|
||||
|
||||
### 11. Local Package API Documentation
|
||||
|
||||
Goal: extend beta API discovery from `lib/std` and symbol metadata to the
|
||||
local packages and modules users document with `glagol doc`.
|
||||
|
||||
Work:
|
||||
|
||||
- make `glagol doc <file|project|workspace> -o <dir>` include deterministic
|
||||
exported/public API sections for local packages and modules
|
||||
- list exact exported function signatures
|
||||
- list exported struct fields
|
||||
- list exported enum variants and payload types
|
||||
- keep non-exported functions, structs, enums, tests, and aliases out of the
|
||||
public API sections
|
||||
- normalize module-local concrete aliases in public docs so local alias names
|
||||
do not leak across module/package boundaries
|
||||
- keep Markdown layout and generated file names beta-scoped rather than stable
|
||||
|
||||
Released in `1.0.0-beta.11`: local file, project, package, and workspace docs
|
||||
generated by `glagol doc <file|project|workspace> -o <dir>` include
|
||||
deterministic public API sections with exact exported function signatures,
|
||||
exported struct fields, exported enum variants/payload types, non-export
|
||||
filtering, and module-local alias normalization. This extends beta10 API
|
||||
discovery only; it does not freeze the Markdown schema, create a stable
|
||||
stdlib/API compatibility freeze, add LSP/watch, define SARIF/daemon protocols,
|
||||
set a diagnostics schema policy, implement executable generics/maps/sets, add
|
||||
re-exports/globs/hierarchical modules, or define registry semantics.
|
||||
|
||||
Why eleventh: local packages are useful only if their public surface can be
|
||||
reviewed without reading every source file, but the documentation format
|
||||
should remain flexible until the package and editor stories are stronger.
|
||||
|
||||
## Stable `1.0.0` Gate
|
||||
|
||||
Slovo should not become stable until all of these are true:
|
||||
|
||||
@ -10,11 +10,42 @@ integration/readiness release, not the first real beta.
|
||||
|
||||
## Unreleased
|
||||
|
||||
Next scoped Glagol work is expected to continue after the `1.0.0-beta.10`
|
||||
developer-experience API discovery and symbol-metadata update.
|
||||
Next scoped Glagol work is expected to continue after the `1.0.0-beta.11`
|
||||
local package API documentation update.
|
||||
|
||||
No unreleased compiler scope is committed here yet.
|
||||
|
||||
## 1.0.0-beta.11
|
||||
|
||||
Release label: `1.0.0-beta.11`
|
||||
|
||||
Release date: 2026-05-22
|
||||
|
||||
Release state: local package API documentation update
|
||||
|
||||
### Summary
|
||||
|
||||
The beta.11 docs/tooling contract extends beta.10 API discovery so generated
|
||||
local documentation exposes the public API of local files, projects, packages,
|
||||
and workspaces without changing source-language execution semantics.
|
||||
|
||||
- `glagol doc <file|project|workspace> -o <dir>` includes deterministic
|
||||
exported/public API sections for local modules and workspace packages.
|
||||
- Public API sections render exact exported function signatures, exported
|
||||
struct fields, and exported enum variants with payload types.
|
||||
- Module-local concrete aliases are normalized in public docs before rendering.
|
||||
- Non-exported functions, structs, enums, tests, and `(type ...)` aliases stay
|
||||
out of the public API sections.
|
||||
|
||||
### Explicit Deferrals
|
||||
|
||||
This release does not define a stable Markdown schema, stable stdlib/API
|
||||
compatibility freeze, an LSP server, watch mode, SARIF, daemon protocols,
|
||||
diagnostics schema policy, executable generics, generic vectors, maps, sets,
|
||||
iterators, re-exports, globs, hierarchical modules, package registry
|
||||
semantics, runtime collection changes, new standard-library runtime APIs, or
|
||||
stable ABI/layout promises.
|
||||
|
||||
## 1.0.0-beta.10
|
||||
|
||||
Release label: `1.0.0-beta.10`
|
||||
|
||||
@ -22,8 +22,8 @@ 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.10`, released on 2026-05-22 as a
|
||||
developer-experience API discovery and symbol-metadata update. It keeps the `1.0.0-beta`
|
||||
Current stage: `1.0.0-beta.11`, released on 2026-05-22 as a local package API
|
||||
documentation 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,
|
||||
@ -42,11 +42,19 @@ helper signatures after alias normalization, and adds
|
||||
`glagol symbols <file.slo|project|workspace>` 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.
|
||||
The beta.11 documentation slice extends
|
||||
`glagol doc <file|project|workspace> -o <dir>` with deterministic
|
||||
exported/public API sections for local packages and modules, including exact
|
||||
exported function signatures, exported struct fields, exported enum
|
||||
variants/payload types, non-export filtering, and module-local alias
|
||||
normalization. It adds no source-language runtime behavior.
|
||||
|
||||
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
|
||||
Next stage target: post-`1.0.0-beta.11` developer-experience,
|
||||
diagnostics-schema, package, and collection/generic planning. Generic vectors,
|
||||
maps, sets, generic stdlib dispatch, runtime collection changes, collection
|
||||
unification, stable Markdown schema, LSP/watch protocols, SARIF/daemon
|
||||
protocols, re-exports/globs/hierarchical modules, diagnostics schema policy,
|
||||
registry semantics, and a stable stdlib/API compatibility freeze remain
|
||||
unimplemented until a later scoped contract promotes them explicitly.
|
||||
|
||||
The final experimental precursor scope is `exp-125`. Its unsigned direct-value
|
||||
|
||||
@ -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.10`, published on 2026-05-22. It keeps the
|
||||
The current release is `1.0.0-beta.11`, 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,13 +19,45 @@ 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`, and the first developer-experience API discovery slice from
|
||||
`1.0.0-beta.10`.
|
||||
`1.0.0-beta.9`, the first developer-experience API discovery slice from
|
||||
`1.0.0-beta.10`, and the local package API documentation extension from
|
||||
`1.0.0-beta.11`.
|
||||
|
||||
## Unreleased
|
||||
|
||||
No unreleased language scope is committed here yet.
|
||||
|
||||
## 1.0.0-beta.11
|
||||
|
||||
Release label: `1.0.0-beta.11`
|
||||
|
||||
Release name: Local Package API Documentation
|
||||
|
||||
Release date: 2026-05-22
|
||||
|
||||
Status: released beta documentation/API-discovery update on the
|
||||
`1.0.0-beta` language baseline.
|
||||
|
||||
`1.0.0-beta.11` extends the beta.10 API discovery lane from the generated
|
||||
standard-library catalog and `glagol symbols` metadata into local package and
|
||||
module documentation:
|
||||
|
||||
- `glagol doc <file|project|workspace> -o <dir>` includes deterministic
|
||||
exported/public API sections for local source files, projects, packages, and
|
||||
workspaces.
|
||||
- Public API sections list exact exported function signatures, exported struct
|
||||
fields, and exported enum variants with payload types.
|
||||
- Module-local concrete aliases are normalized before public rendering, so
|
||||
local names do not leak into package/module API docs.
|
||||
- Non-exported functions, structs, enums, tests, and `(type ...)` aliases
|
||||
remain omitted from the public API surface.
|
||||
|
||||
This release does not define a stable Markdown schema, stable stdlib/API
|
||||
compatibility freeze, LSP server, watch mode, SARIF, daemon protocols,
|
||||
diagnostics schema policy, executable generics, maps, sets, re-exports, globs,
|
||||
hierarchical modules, package registry semantics, runtime changes, new
|
||||
compiler-known runtime names, or stable ABI/layout promises.
|
||||
|
||||
## 1.0.0-beta.10
|
||||
|
||||
Release label: `1.0.0-beta.10`
|
||||
|
||||
@ -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.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
|
||||
Current stage: `1.0.0-beta.11`, released on 2026-05-22 as a post-beta local
|
||||
package API documentation 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
|
||||
@ -24,18 +24,24 @@ 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,
|
||||
workspaces, plus `glagol doc <file|project|workspace> -o <dir>` public API
|
||||
sections for local packages/modules with exact exported function signatures,
|
||||
exported struct fields, exported enum variants/payload types, non-export
|
||||
filtering, and module-local alias normalization. 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.
|
||||
rich host-error ADTs, stable ABI/layout, stable Markdown schema, stable
|
||||
stdlib/API compatibility freeze, LSP/watch, SARIF/daemon protocols,
|
||||
diagnostics schema policy, re-exports/globs/hierarchical modules, and package
|
||||
registry semantics remain deferred.
|
||||
|
||||
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.
|
||||
Next stage target: continue from developer-experience, diagnostics, package,
|
||||
and reserved generic/map/set planning without claiming executable generics, an
|
||||
LSP/watch protocol, stable Markdown schema, registry semantics, or
|
||||
stable standard-library/API compatibility freeze 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
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
Status: living beta contract for `1.0.0-beta`, with the post-beta
|
||||
`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
|
||||
foundation update, `1.0.0-beta.10` developer-experience API discovery update,
|
||||
and `1.0.0-beta.11` local package API documentation 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
|
||||
@ -129,6 +129,16 @@ Current v1 release surface and explicit experimental targets:
|
||||
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
|
||||
- `1.0.0-beta.11` local package API documentation target:
|
||||
`glagol doc <file|project|workspace> -o <dir>` includes deterministic
|
||||
exported/public API sections for local packages and modules with exact
|
||||
exported function signatures, exported struct fields, exported enum
|
||||
variants/payload types, non-export filtering, and module-local alias
|
||||
normalization; this remains beta API discovery only, not a stable Markdown
|
||||
schema, stable stdlib/API
|
||||
compatibility freeze, LSP/watch contract, SARIF/daemon protocol, diagnostics
|
||||
schema policy, executable generics, maps/sets, re-exports, globs,
|
||||
hierarchical modules, or registry semantics
|
||||
- `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
|
||||
@ -953,6 +963,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.
|
||||
|
||||
As of `1.0.0-beta.11`, `glagol doc <file|project|workspace> -o <dir>` also
|
||||
includes deterministic exported/public API sections for local packages and
|
||||
modules. These sections render exact exported function signatures, exported
|
||||
struct fields, and exported enum variants with payload types. They omit
|
||||
non-exported functions, structs, enums, tests, and aliases from the public API
|
||||
surface and normalize module-local concrete aliases before rendering public
|
||||
types.
|
||||
|
||||
`glagol symbols <file.slo|project|workspace>` emits deterministic
|
||||
machine-readable S-expression metadata using the `slovo.symbols` schema. It
|
||||
reports module paths, package labels when available, imports, exports,
|
||||
@ -1075,6 +1093,25 @@ solving, package publishing, archive formats, optional/dev/target
|
||||
dependencies, feature flags, package build scripts, or stable package
|
||||
ABI/layout promises.
|
||||
|
||||
`1.0.0-beta.11` extends the generated local-package documentation surface, but
|
||||
not the package model itself. `glagol doc <file|project|workspace> -o <dir>`
|
||||
includes deterministic exported/public API sections for local modules and
|
||||
workspace packages:
|
||||
|
||||
- exported functions are rendered with exact parameter and return types
|
||||
- exported structs are rendered with exported field names and field types
|
||||
- exported enums are rendered with exported variant names and payload types
|
||||
- non-exported functions, structs, enums, tests, and `(type ...)` aliases are
|
||||
excluded from the public API sections
|
||||
- module-local concrete aliases are normalized to their concrete public target
|
||||
types before rendering
|
||||
|
||||
The generated Markdown remains a beta documentation format. This does not
|
||||
define a stable Markdown schema, stable stdlib/API compatibility freeze,
|
||||
LSP/watch behavior, SARIF or daemon protocols, diagnostics schema policy,
|
||||
executable generics, maps/sets, re-exports, glob imports, hierarchical modules,
|
||||
registry semantics, runtime behavior, or stable ABI/layout.
|
||||
|
||||
### 4.4.4 Post-Beta Networking Foundation
|
||||
|
||||
Status: released in `1.0.0-beta.6`.
|
||||
@ -1232,6 +1269,44 @@ generic aliases, parameterized aliases, maps, sets, traits, inference,
|
||||
monomorphization, iterators, new runtime helpers, stable ABI/layout promises,
|
||||
or a stable standard-library API freeze.
|
||||
|
||||
### 4.4.8 Post-Beta Local Package API Documentation
|
||||
|
||||
Status: released in `1.0.0-beta.11` as a documentation/API-discovery update on
|
||||
top of the beta.10 discovery lane.
|
||||
|
||||
`1.0.0-beta.11` extends `glagol doc <file|project|workspace> -o <dir>` so the
|
||||
generated Markdown includes deterministic exported/public API sections for
|
||||
local source files, projects, packages, and workspaces. The public API surface
|
||||
is derived from explicit module exports and local package boundaries.
|
||||
|
||||
The public API sections include:
|
||||
|
||||
- exact exported function signatures, including parameter names, parameter
|
||||
types, and return types
|
||||
- exported struct field names and field types
|
||||
- exported enum variant names and payload types, including payloadless variants
|
||||
and current single-payload variants
|
||||
- deterministic ordering that follows the existing module/package
|
||||
documentation ordering
|
||||
|
||||
Non-exported functions, structs, enums, tests, and `(type ...)` aliases are
|
||||
not part of the public API sections. A declaration can still appear in other
|
||||
source-structure summaries when those summaries already exist, but it must not
|
||||
be presented as exported/public API unless the module export list exposes it.
|
||||
|
||||
Module-local concrete aliases are normalized before public rendering. Public
|
||||
docs show concrete target types such as `(vec i32)`, `(option string)`, and
|
||||
`(result u64 i32)` rather than private alias names introduced by the
|
||||
documented module. This mirrors beta.10 `lib/std` catalog behavior for local
|
||||
packages and modules.
|
||||
|
||||
This is not a stable documentation schema. It does not freeze Markdown
|
||||
headings, anchors, file names, machine parsing contracts, stable stdlib/API
|
||||
compatibility freeze, LSP/watch behavior, SARIF or daemon protocols,
|
||||
diagnostics schema policy, executable generics, maps/sets, re-exports, glob
|
||||
imports, hierarchical modules, registry semantics, runtime behavior, package
|
||||
ABI, or layout.
|
||||
|
||||
## 4.5 v2.0.0-beta.1 Experimental Integration Readiness
|
||||
|
||||
Status: current experimental Slovo-side release contract, released 2026-05-17.
|
||||
@ -5678,12 +5753,18 @@ manifest source root and uses deterministic v1.3 module ordering. Plain
|
||||
`glagol fmt <file.slo>` continues to write formatted source to stdout.
|
||||
|
||||
Documentation generation records modules, imports/exports, structs, functions,
|
||||
and tests as deterministic Markdown. It is generated documentation, not
|
||||
runtime reflection, typed-core reflection, debug metadata, DWARF, source maps,
|
||||
or ABI/layout information.
|
||||
tests, and beta.11 exported/public API sections as deterministic Markdown.
|
||||
Public API sections include exact exported function signatures, exported
|
||||
struct fields, exported enum variants/payload types, non-export filtering, and
|
||||
module-local alias normalization. It is generated documentation, not runtime
|
||||
reflection, typed-core reflection, debug metadata, DWARF, source maps,
|
||||
ABI/layout information, or a stable Markdown schema.
|
||||
|
||||
LSP, watch mode, daemon protocols, SARIF, debug adapters, stable debug
|
||||
metadata, DWARF emission, and standalone source-map files remain deferred.
|
||||
LSP, watch mode, daemon protocols, SARIF, debug adapters, diagnostics schema
|
||||
policy, stable debug metadata, DWARF emission, standalone source-map files,
|
||||
stable stdlib/API compatibility freeze, executable generics, maps/sets,
|
||||
re-exports, globs, hierarchical modules, and registry semantics remain
|
||||
deferred.
|
||||
|
||||
## 10. Slice 6: Unsafe, Memory, And FFI
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ 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.10`; future releases may mark new helpers this way before they graduate.
|
||||
- `experimental`: not used for exported `lib/std` helpers in `1.0.0-beta.11`; 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 API discovery aid, not a stable `1.0.0` standard-library freeze.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user