Release 1.0.0-beta.10 developer experience api discovery
This commit is contained in:
parent
5a3ed0c41e
commit
f8f0862ee3
62
.llm/BETA_10_DEVELOPER_EXPERIENCE_API_DISCOVERY.md
Normal file
62
.llm/BETA_10_DEVELOPER_EXPERIENCE_API_DISCOVERY.md
Normal file
@ -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 <file.slo|project|workspace>` 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`
|
||||||
47
.llm/reviews/BETA_10_RELEASE_REVIEW.md
Normal file
47
.llm/reviews/BETA_10_RELEASE_REVIEW.md
Normal file
@ -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 <file.slo|project|workspace>` 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=<local md-to-pdf package path> ./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.
|
||||||
55
README.md
55
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
|
standard library source, compiler, runtime, examples, benchmarks, and technical
|
||||||
documents.
|
documents.
|
||||||
|
|
||||||
Current release: `1.0.0-beta.9`.
|
Current release: `1.0.0-beta.10`.
|
||||||
|
|
||||||
## Repository Layout
|
## Repository Layout
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ scripts/ local release and document tooling
|
|||||||
|
|
||||||
## Beta Scope
|
## 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`
|
`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
|
runtime/resource foundation bundle, the `1.0.0-beta.3` standard-library
|
||||||
stabilization bundle, the `1.0.0-beta.4` language-usability diagnostics
|
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`
|
`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
|
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
|
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:
|
command-line, file, and loopback-network programs with:
|
||||||
|
|
||||||
- modules, explicit imports, packages, and local workspaces
|
- 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`
|
- `i32`, `i64`, `u32`, `u64`, `f64`, `bool`, `string`, and internal `unit`
|
||||||
- structs, enums, fixed arrays, concrete vectors, option/result families, and
|
- structs, enums, fixed arrays, concrete vectors, option/result families, and
|
||||||
current `match`
|
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`
|
- JSON string quoting and compact JSON text construction through `std.json`
|
||||||
- hosted native builds through LLVM IR, Clang, and `runtime/runtime.c`
|
- hosted native builds through LLVM IR, Clang, and `runtime/runtime.c`
|
||||||
|
|
||||||
Still deferred before stable: generics, maps/sets, broad package registry
|
The generated standard-library API catalog is a beta discovery aid: it lists
|
||||||
semantics, DNS/TLS/async networking, LSP/watch/debug-adapter guarantees,
|
exported helper signatures from `lib/std/*.slo`, normalizes module-local
|
||||||
stable ABI and layout, and a stable standard-library compatibility freeze.
|
concrete aliases such as `VecI32` and `ResultU64` to their concrete public
|
||||||
|
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.
|
||||||
|
|
||||||
The next likely language slice after `1.0.0-beta.9` should continue from the
|
Still deferred before stable: executable generics, maps/sets, broad package
|
||||||
reserved generic and collection diagnostics without claiming executable
|
registry semantics, DNS/TLS/async networking, LSP/watch/debug-adapter
|
||||||
generics, maps, sets, traits, inference, monomorphization, iterators, ABI
|
guarantees, stable ABI and layout, runtime changes for generic collections,
|
||||||
stability, or a standard-library API freeze until the contract and gates are
|
and a stable standard-library compatibility freeze.
|
||||||
explicit.
|
|
||||||
|
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
|
## 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
|
traits, inference, monomorphization, iterators, stable ABI/layout promises, or
|
||||||
a stable standard-library API freeze.
|
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 <file.slo|project|workspace>` 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
|
## Documentation
|
||||||
|
|
||||||
- [Language Manifest](docs/language/MANIFEST.md)
|
- [Language Manifest](docs/language/MANIFEST.md)
|
||||||
|
|||||||
2
compiler/Cargo.lock
generated
2
compiler/Cargo.lock
generated
@ -4,4 +4,4 @@ version = 3
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glagol"
|
name = "glagol"
|
||||||
version = "1.0.0-beta.9"
|
version = "1.0.0-beta.10"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "glagol"
|
name = "glagol"
|
||||||
version = "1.0.0-beta.9"
|
version = "1.0.0-beta.10"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Glagol, the first compiler for the Slovo language"
|
description = "Glagol, the first compiler for the Slovo language"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|||||||
@ -11,6 +11,7 @@ mod project;
|
|||||||
mod scaffold;
|
mod scaffold;
|
||||||
mod sexpr;
|
mod sexpr;
|
||||||
mod std_runtime;
|
mod std_runtime;
|
||||||
|
mod symbols;
|
||||||
mod test_runner;
|
mod test_runner;
|
||||||
mod token;
|
mod token;
|
||||||
mod types;
|
mod types;
|
||||||
@ -94,19 +95,26 @@ fn run_invocation_inner(invocation: Invocation) -> ! {
|
|||||||
if invocation.mode == Mode::Doc {
|
if invocation.mode == Mode::Doc {
|
||||||
run_doc(invocation);
|
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 {
|
if invocation.mode == Mode::Format && invocation.fmt_action != FmtAction::Stdout {
|
||||||
run_fmt_action(invocation);
|
run_fmt_action(invocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
let project_capable_mode = matches!(invocation.mode, Mode::Check | Mode::Build | Mode::Run)
|
let project_capable_mode = matches!(
|
||||||
|| (invocation.mode == Mode::RunTests && invocation.manifest_mode_name == "test");
|
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) {
|
if project_capable_mode && project::is_project_input(&invocation.path) {
|
||||||
match invocation.mode {
|
match invocation.mode {
|
||||||
Mode::Check => run_project_check(invocation),
|
Mode::Check => run_project_check(invocation),
|
||||||
Mode::RunTests => run_project_test(invocation),
|
Mode::RunTests => run_project_test(invocation),
|
||||||
Mode::Build => run_project_build(invocation),
|
Mode::Build => run_project_build(invocation),
|
||||||
Mode::Run => run_project_run(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());
|
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) -> ! {
|
fn exit_project_failure(invocation: Invocation, failure: project::ProjectTestFailure) -> ! {
|
||||||
let rendered = render_source_diagnostics_multi(
|
let rendered = render_source_diagnostics_multi(
|
||||||
&failure.diagnostics,
|
&failure.diagnostics,
|
||||||
@ -233,11 +260,52 @@ fn exit_project_failure(invocation: Invocation, failure: project::ProjectTestFai
|
|||||||
process::exit(ExitCode::SourceFailure.code());
|
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) -> ! {
|
fn run_text_mode(invocation: Invocation, mode: Mode, source: &str) -> ! {
|
||||||
if mode == Mode::RunTests {
|
if mode == Mode::RunTests {
|
||||||
run_test_mode(invocation, source);
|
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 foreign_imports = c_imports_for_manifest(&invocation.path, source);
|
||||||
let result = match mode {
|
let result = match mode {
|
||||||
Mode::EmitLlvm => driver::compile_to_llvm(&invocation.path, source),
|
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::InspectLoweringSurface => driver::inspect_lowering_surface(&invocation.path, source),
|
||||||
Mode::InspectLoweringChecked => driver::inspect_lowering_checked(&invocation.path, source),
|
Mode::InspectLoweringChecked => driver::inspect_lowering_checked(&invocation.path, source),
|
||||||
Mode::CheckTests => driver::check_tests(&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::RunTests => unreachable!("test mode is handled separately"),
|
||||||
Mode::Build => unreachable!("build is handled separately"),
|
Mode::Build => unreachable!("build is handled separately"),
|
||||||
Mode::Run => unreachable!("run 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) -> ! {
|
fn run_new(invocation: Invocation) -> ! {
|
||||||
match scaffold::create_project(
|
match scaffold::create_project(
|
||||||
&invocation.path,
|
&invocation.path,
|
||||||
@ -1210,7 +1321,7 @@ fn parse_args(raw_args: &[String]) -> Result<Args, ParseError> {
|
|||||||
command_line: command_line.clone(),
|
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() =>
|
if path.is_none() =>
|
||||||
{
|
{
|
||||||
let next = match arg.as_str() {
|
let next = match arg.as_str() {
|
||||||
@ -1222,6 +1333,7 @@ fn parse_args(raw_args: &[String]) -> Result<Args, ParseError> {
|
|||||||
"clean" => Mode::Clean,
|
"clean" => Mode::Clean,
|
||||||
"new" => Mode::New,
|
"new" => Mode::New,
|
||||||
"doc" => Mode::Doc,
|
"doc" => Mode::Doc,
|
||||||
|
"symbols" => Mode::Symbols,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
set_mode(
|
set_mode(
|
||||||
@ -1489,6 +1601,7 @@ enum Mode {
|
|||||||
Clean,
|
Clean,
|
||||||
New,
|
New,
|
||||||
Doc,
|
Doc,
|
||||||
|
Symbols,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mode {
|
impl Mode {
|
||||||
@ -1507,6 +1620,7 @@ impl Mode {
|
|||||||
Self::Clean => "clean",
|
Self::Clean => "clean",
|
||||||
Self::New => "new",
|
Self::New => "new",
|
||||||
Self::Doc => "doc",
|
Self::Doc => "doc",
|
||||||
|
Self::Symbols => "symbols",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1523,6 +1637,7 @@ impl Mode {
|
|||||||
Self::Clean => "no-output",
|
Self::Clean => "no-output",
|
||||||
Self::New => "no-output",
|
Self::New => "no-output",
|
||||||
Self::Doc => "documentation",
|
Self::Doc => "documentation",
|
||||||
|
Self::Symbols => "symbols",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2425,6 +2540,6 @@ fn normalized_output_path(path: &str) -> Option<PathBuf> {
|
|||||||
|
|
||||||
fn print_usage() {
|
fn print_usage() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"usage: glagol [check|fmt|test|build|run|clean] [--json-diagnostics] [--no-color] [--manifest <path>] [--link-c <path>] [-o <path>] [--filter <substring>] <file.slo|project> [-- <program-args>...]\n glagol fmt [--check|--write] <file.slo|project>\n glagol new <project-dir> [--name <name>] [--template binary|library|workspace]\n glagol doc <file.slo|project> -o <dir>\n glagol [--emit=llvm|--format|--print-tree|--inspect-lowering=surface|--inspect-lowering=checked|--check-tests|--run-tests] [--json-diagnostics] [--no-color] [-o <path>] [--manifest <path>] [--filter <substring>] <file.slo>\n glagol --version"
|
"usage: glagol [check|fmt|test|build|run|clean|symbols] [--json-diagnostics] [--no-color] [--manifest <path>] [--link-c <path>] [-o <path>] [--filter <substring>] <file.slo|project> [-- <program-args>...]\n glagol fmt [--check|--write] <file.slo|project>\n glagol new <project-dir> [--name <name>] [--template binary|library|workspace]\n glagol doc <file.slo|project> -o <dir>\n glagol symbols <file.slo|project|workspace>\n glagol [--emit=llvm|--format|--print-tree|--inspect-lowering=surface|--inspect-lowering=checked|--check-tests|--run-tests] [--json-diagnostics] [--no-color] [-o <path>] [--manifest <path>] [--filter <substring>] <file.slo>\n glagol --version"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
667
compiler/src/symbols.rs
Normal file
667
compiler/src/symbols.rs
Normal file
@ -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<String, Vec<diag::Diagnostic>> {
|
||||||
|
let source = SourceFile {
|
||||||
|
path: path.to_string(),
|
||||||
|
source: source.to_string(),
|
||||||
|
};
|
||||||
|
render_sources(&[source], None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_project(project: &ToolProject) -> Result<String, Vec<diag::Diagnostic>> {
|
||||||
|
render_sources(&project.sources, Some(&project.artifact))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_sources(
|
||||||
|
sources: &[SourceFile],
|
||||||
|
artifact: Option<&ProjectArtifact>,
|
||||||
|
) -> Result<String, Vec<diag::Diagnostic>> {
|
||||||
|
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::<Vec<_>>();
|
||||||
|
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<String, String> {
|
||||||
|
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<ModuleSymbols, Vec<diag::Diagnostic>> {
|
||||||
|
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<ImportSymbol>) {
|
||||||
|
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<TypeAliasSymbol>) {
|
||||||
|
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<StructSymbol>) {
|
||||||
|
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<EnumSymbol>) {
|
||||||
|
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<FunctionSymbol>) {
|
||||||
|
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<TestSymbol>) {
|
||||||
|
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<NamedSymbol> {
|
||||||
|
Some(NamedSymbol {
|
||||||
|
name: expect_ident(form)?.to_string(),
|
||||||
|
span: form.span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ModuleSymbols {
|
||||||
|
name: String,
|
||||||
|
path: String,
|
||||||
|
package: Option<String>,
|
||||||
|
module_span: Option<Span>,
|
||||||
|
exports: Vec<NamedSymbol>,
|
||||||
|
imports: Vec<ImportSymbol>,
|
||||||
|
type_aliases: Vec<TypeAliasSymbol>,
|
||||||
|
structs: Vec<StructSymbol>,
|
||||||
|
enums: Vec<EnumSymbol>,
|
||||||
|
functions: Vec<FunctionSymbol>,
|
||||||
|
tests: Vec<TestSymbol>,
|
||||||
|
source: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NamedSymbol {
|
||||||
|
name: String,
|
||||||
|
span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ImportSymbol {
|
||||||
|
module: String,
|
||||||
|
span: Span,
|
||||||
|
module_span: Span,
|
||||||
|
names: Vec<NamedSymbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TypeAliasSymbol {
|
||||||
|
name: NamedSymbol,
|
||||||
|
span: Span,
|
||||||
|
target_span: Option<Span>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StructSymbol {
|
||||||
|
name: NamedSymbol,
|
||||||
|
span: Span,
|
||||||
|
fields: Vec<FieldSymbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FieldSymbol {
|
||||||
|
name: NamedSymbol,
|
||||||
|
span: Span,
|
||||||
|
type_span: Option<Span>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EnumSymbol {
|
||||||
|
name: NamedSymbol,
|
||||||
|
span: Span,
|
||||||
|
variants: Vec<VariantSymbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VariantSymbol {
|
||||||
|
name: NamedSymbol,
|
||||||
|
span: Span,
|
||||||
|
payload_span: Option<Span>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FunctionSymbol {
|
||||||
|
name: NamedSymbol,
|
||||||
|
span: Span,
|
||||||
|
params: Vec<ParamSymbol>,
|
||||||
|
return_span: Option<Span>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ParamSymbol {
|
||||||
|
name: NamedSymbol,
|
||||||
|
span: Span,
|
||||||
|
type_span: Option<Span>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestSymbol {
|
||||||
|
name: NamedSymbol,
|
||||||
|
span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Position {
|
||||||
|
line: usize,
|
||||||
|
column: usize,
|
||||||
|
}
|
||||||
184
compiler/tests/symbols_beta10.rs
Normal file
184
compiler/tests/symbols_beta10.rs
Normal file
@ -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 <file.slo|project|workspace>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
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<I, S>(args: I) -> Output
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = S>,
|
||||||
|
S: AsRef<std::ffi::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)
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -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
|
compiler, docs, tests, and stdlib have enough real pressure to validate the
|
||||||
design.
|
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:
|
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
|
- language-server diagnostics and document symbols
|
||||||
|
- editor-facing symbol metadata for files, projects, and workspaces
|
||||||
- project watch mode
|
- project watch mode
|
||||||
- generated API documentation for local packages and `lib/std`
|
- generated API documentation for local packages
|
||||||
- clearer benchmark harness output
|
- clearer benchmark harness output
|
||||||
- machine-readable diagnostics stability policy
|
- 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 <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.
|
||||||
|
|
||||||
Why tenth: editor support matters, but it should follow a stable enough syntax,
|
Why tenth: editor support matters, but it should follow a stable enough syntax,
|
||||||
project model, and diagnostic model.
|
project model, and diagnostic model.
|
||||||
|
|
||||||
|
|||||||
@ -10,11 +10,47 @@ integration/readiness release, not the first real beta.
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
Next scoped Glagol work is expected to continue after the `1.0.0-beta.9`
|
Next scoped Glagol work is expected to continue after the `1.0.0-beta.10`
|
||||||
reservation update.
|
developer-experience API discovery and symbol-metadata update.
|
||||||
|
|
||||||
No unreleased compiler scope is committed here yet.
|
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 <file.slo|project|workspace>` 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
|
## 1.0.0-beta.9
|
||||||
|
|
||||||
Release label: `1.0.0-beta.9`
|
Release label: `1.0.0-beta.9`
|
||||||
|
|||||||
@ -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.
|
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
|
Current stage: `1.0.0-beta.10`, released on 2026-05-22 as a
|
||||||
reservation update for generic-shaped collection and stdlib syntax. It keeps
|
developer-experience API discovery and symbol-metadata update. It keeps the `1.0.0-beta`
|
||||||
the `1.0.0-beta` language/compiler support baseline and includes the
|
language/compiler support baseline and includes the
|
||||||
`1.0.0-beta.1` tooling hardening release, the `1.0.0-beta.2` runtime/resource
|
`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,
|
foundation release, the `1.0.0-beta.3` standard-library stabilization release,
|
||||||
the `1.0.0-beta.4` language-usability diagnostics release, the
|
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
|
coverage, the `1.0.0-beta.8` concrete type alias foundation update, and
|
||||||
focused diagnostics/formatter/project-mode gates for reserved generic
|
focused diagnostics/formatter/project-mode gates for reserved generic
|
||||||
functions, parameterized aliases, generic type parameters, maps, sets, and
|
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 <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.
|
||||||
|
|
||||||
Next stage target: post-`1.0.0-beta.9` collection/generic planning. Generic
|
Next stage target: post-`1.0.0-beta.10` developer-experience and
|
||||||
vectors, maps, sets, generic stdlib dispatch, and collection unification remain
|
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.
|
unimplemented until a later scoped contract promotes them explicitly.
|
||||||
|
|
||||||
The final experimental precursor scope is `exp-125`. Its unsigned direct-value
|
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
|
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.
|
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
|
`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
|
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
|
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
|
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
|
first concrete type alias foundation from `1.0.0-beta.8`, plus the first
|
||||||
collection alias unification and generic reservation slice from
|
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
|
## Unreleased
|
||||||
|
|
||||||
No unreleased language scope is committed here yet.
|
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 <file.slo|project|workspace>` 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
|
## 1.0.0-beta.9
|
||||||
|
|
||||||
Release label: `1.0.0-beta.9`
|
Release label: `1.0.0-beta.9`
|
||||||
|
|||||||
@ -10,8 +10,8 @@ Long-horizon planning lives in
|
|||||||
release train from the historical `v2.0.0-beta.1` tag toward and beyond the
|
release train from the historical `v2.0.0-beta.1` tag toward and beyond the
|
||||||
first real general-purpose beta Slovo contract.
|
first real general-purpose beta Slovo contract.
|
||||||
|
|
||||||
Current stage: `1.0.0-beta.9`, released on 2026-05-22 as the first post-beta
|
Current stage: `1.0.0-beta.10`, 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
|
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`
|
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
|
runtime/resource foundation release, the `1.0.0-beta.3` standard-library
|
||||||
stabilization release, the `1.0.0-beta.4` language-usability diagnostics
|
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`
|
opaque `i32` handles and concrete `result` families, plus a narrow `std.json`
|
||||||
source facade for compact JSON text construction, plus top-level module-local
|
source facade for compact JSON text construction, plus top-level module-local
|
||||||
transparent aliases for supported concrete target types, plus local alias use
|
transparent aliases for supported concrete target types, plus local alias use
|
||||||
inside the current concrete vector, option, and result facades. JSON parsing,
|
inside the current concrete vector, option, and result facades, plus a
|
||||||
recursive JSON values, executable generics, generic aliases, parameterized
|
generated standard-library API catalog that lists exact exported helper
|
||||||
aliases, cross-module alias visibility, maps/sets, traits, inference,
|
signatures with module-local aliases normalized to concrete public types, plus
|
||||||
monomorphization, iterators, DNS, TLS, UDP, async IO, non-loopback binding,
|
`glagol symbols` deterministic source metadata for files, projects, and
|
||||||
HTTP frameworks, rich host-error ADTs, stable ABI/layout, and a stable
|
workspaces. JSON
|
||||||
standard-library API freeze remain deferred.
|
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
|
Next stage target: continue from developer-experience and reserved
|
||||||
collection-unification design pressure without claiming executable generics
|
generic/map/set diagnostics without claiming executable generics, an LSP/watch
|
||||||
until the exact scope is frozen from the manifest and roadmap.
|
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
|
The final experimental precursor scope is `exp-125`, defined in
|
||||||
`.llm/EXP_125_UNSIGNED_U32_U64_NUMERIC_AND_STDLIB_BREADTH_ALPHA.md`. Its
|
`.llm/EXP_125_UNSIGNED_U32_U64_NUMERIC_AND_STDLIB_BREADTH_ALPHA.md`. Its
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
# Slovo v1 Specification
|
# Slovo v1 Specification
|
||||||
|
|
||||||
Status: living beta contract for `1.0.0-beta`, with the post-beta
|
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
|
`1.0.0-beta.1` tooling/install update, `1.0.0-beta.2` runtime/resource
|
||||||
foundation update. The language contract integrates
|
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
|
promoted language slices through `exp-125` and the historical publication
|
||||||
baseline through `exp-123`. `1.0.0-beta` is the first real general-purpose
|
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
|
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
|
existing concrete vector, option, and result source facades may use
|
||||||
module-local concrete aliases internally while exported helper behavior and
|
module-local concrete aliases internally while exported helper behavior and
|
||||||
cross-module types remain concrete; executable generics, maps, sets, traits,
|
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
|
- `exp-1` owned runtime strings: compiler-known `std.string.concat` accepts two
|
||||||
`string` values and returns an immutable runtime-owned `string`; existing
|
`string` values and returns an immutable runtime-owned `string`; existing
|
||||||
string equality, length, printing, locals, parameters, returns, and calls work
|
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,
|
reflection API, and it does not expose stable typed-core, debug metadata,
|
||||||
source-map, ABI, layout, runtime reflection, or compiler-internal contracts.
|
source-map, ABI, layout, runtime reflection, or compiler-internal contracts.
|
||||||
|
|
||||||
|
`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,
|
||||||
|
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
|
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
|
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.
|
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`,
|
Recommended v1 modes are `check`, `format`, `emit-llvm`,
|
||||||
`inspect-lowering`, and `test`. Recommended output and artifact kinds are
|
`inspect-lowering`, and `test`. Recommended output and artifact kinds are
|
||||||
`diagnostics`, `formatted-source`, `llvm-ir`, `lowering-inspector`, `stdout`,
|
`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
|
A failed invocation still writes a manifest when the tool mode supports
|
||||||
manifests. In that case `(success false)` is paired with a diagnostics artifact
|
manifests. In that case `(success false)` is paired with a diagnostics artifact
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -5,7 +5,7 @@
|
|||||||
Sanjin Gumbarevic<br>
|
Sanjin Gumbarevic<br>
|
||||||
hermeticum_lab@protonmail.com
|
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
|
Technical behavior baseline: compiler and language support through
|
||||||
`1.0.0-beta`; tooling and install workflow through `1.0.0-beta.1`;
|
`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`;
|
loopback networking foundation through `1.0.0-beta.6`;
|
||||||
serialization/data-interchange foundation through `1.0.0-beta.7`;
|
serialization/data-interchange foundation through `1.0.0-beta.7`;
|
||||||
concrete type alias foundation through `1.0.0-beta.8`;
|
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
|
Date: 2026-05-22
|
||||||
|
|
||||||
Evidence source: paired Slovo/Glagol monorepo verification and benchmark
|
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
|
public monorepo
|
||||||
|
|
||||||
Maturity: beta
|
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,
|
S-expression tree, AST, typed AST, LLVM IR, hosted native executable, tests,
|
||||||
diagnostics, and release documents should agree.
|
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
|
general-purpose beta toolchain baseline from `1.0.0-beta` and records the
|
||||||
first post-beta tooling/install hardening update plus the first
|
first post-beta tooling/install hardening update plus the first
|
||||||
runtime/resource foundation update plus the first standard-library
|
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
|
the first local package/workspace discipline update plus the first loopback
|
||||||
networking foundation update plus the first serialization/data-interchange
|
networking foundation update plus the first serialization/data-interchange
|
||||||
foundation update plus the first concrete type alias foundation and the first
|
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
|
baseline includes the completed `u32` / `u64` unsigned compiler and stdlib
|
||||||
breadth scope, the narrow `std.net` loopback TCP runtime family, the narrow
|
breadth scope, the narrow `std.net` loopback TCP runtime family, the narrow
|
||||||
`std.json.quote_string` runtime family, transparent concrete alias parsing and
|
`std.json.quote_string` runtime family, transparent concrete alias parsing and
|
||||||
erasure, explicit beta9 diagnostics for reserved generic/map/set shapes, and
|
erasure, explicit beta9 diagnostics for reserved generic/map/set shapes, and
|
||||||
the current ten-scaffold benchmark suite. This paper records the
|
the current ten-scaffold benchmark suite. It also includes alias-normalized
|
||||||
current beta
|
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
|
implementation surface, the benchmark method and results, the distinction
|
||||||
between Glagol and Lisp-family implementations, the beta.1 tooling update, the
|
between Glagol and Lisp-family implementations, the beta.1 tooling update, the
|
||||||
beta.2 runtime/resource foundation, the beta.3 standard-library stabilization
|
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.4 diagnostics usability slice, the beta.5 package discipline
|
||||||
slice, the beta.6 networking foundation slice, the beta.7 serialization
|
slice, the beta.6 networking foundation slice, the beta.7 serialization
|
||||||
foundation slice, the beta.8 concrete alias foundation slice, the beta.9
|
foundation slice, the beta.8 concrete alias foundation slice, the beta.9
|
||||||
generic/collection reservation slice, and the compiler path from beta to
|
generic/collection reservation slice, the beta.10 API-discovery and
|
||||||
stable.
|
symbol-metadata slice, and the compiler path from beta to stable.
|
||||||
|
|
||||||
## 1. Compiler Thesis
|
## 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.7` adds a narrow JSON construction foundation, and
|
||||||
`1.0.0-beta.8` adds transparent concrete type aliases, and `1.0.0-beta.9`
|
`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
|
adds focused diagnostics for reserved generic, map, set, and future generic
|
||||||
stdlib forms. None of these post-beta slices claims changed benchmark
|
stdlib forms, and `1.0.0-beta.10` adds alias-normalized stdlib signature
|
||||||
performance. The beta.7 `json-quote-loop`
|
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
|
scaffold is present for local follow-up timing and is not part of the exp-123
|
||||||
nine-row result table below.
|
nine-row result table below.
|
||||||
|
|
||||||
@ -407,7 +412,7 @@ coverage and compatibility:
|
|||||||
- package behavior becoming stable before dependency, manifest, and versioning
|
- package behavior becoming stable before dependency, manifest, and versioning
|
||||||
rules are precise
|
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
|
Glagol now implements the first real beta Slovo contract, the first
|
||||||
post-beta tooling/install hardening release, the first runtime/resource
|
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
|
release, the first loopback networking foundation release, and the first
|
||||||
serialization/data-interchange foundation release, the first concrete type
|
serialization/data-interchange foundation release, the first concrete type
|
||||||
alias foundation release, and the first generic/collection reservation
|
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:
|
Recommended compiler sequence:
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -5,7 +5,7 @@
|
|||||||
Sanjin Gumbarevic<br>
|
Sanjin Gumbarevic<br>
|
||||||
hermeticum_lab@protonmail.com
|
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
|
Technical behavior baseline: language surface through `1.0.0-beta`; tooling
|
||||||
and install workflow through `1.0.0-beta.1`; runtime/resource foundation through
|
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.6`; serialization/data-interchange foundation through
|
||||||
`1.0.0-beta.7`; concrete type alias foundation through `1.0.0-beta.8`;
|
`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`
|
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
|
Date: 2026-05-22
|
||||||
|
|
||||||
Evidence source: paired Slovo/Glagol monorepo verification and benchmark
|
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
|
public monorepo
|
||||||
|
|
||||||
Maturity: beta
|
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
|
`unsafe`, and native compilation through the Glagol compiler to LLVM IR and
|
||||||
hosted executables.
|
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
|
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
|
post-beta tooling/install hardening update plus the first runtime/resource
|
||||||
foundation update, the first standard-library stabilization update, and the
|
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
|
package/workspace discipline update, the first loopback networking foundation
|
||||||
update, the first serialization/data-interchange foundation update, and the
|
update, the first serialization/data-interchange foundation update, and the
|
||||||
first concrete type alias foundation plus the first collection alias
|
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`
|
completed `u32` / `u64`
|
||||||
unsigned scope, the staged stdlib breadth that makes ordinary command-line
|
unsigned scope, the staged stdlib breadth that makes ordinary command-line
|
||||||
programs practical, the current narrow `std.net` loopback TCP surface, the
|
programs practical, the current narrow `std.net` loopback TCP surface, the
|
||||||
current narrow `std.json` text-construction surface, module-local transparent
|
current narrow `std.json` text-construction surface, module-local transparent
|
||||||
aliases for supported concrete types, and the current ten-scaffold benchmark
|
aliases for supported concrete types, and the current ten-scaffold benchmark
|
||||||
suite. This paper records the current beta
|
suite. It also includes an alias-normalized generated standard-library
|
||||||
technical state, the difference between Slovo and Lisp-family languages, the
|
signature catalog and deterministic `glagol symbols` source metadata for
|
||||||
benchmark methodology, the beta.1 tooling update, the beta.2 runtime/resource
|
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
|
foundation, the beta.3 standard-library stabilization slice, the beta.4
|
||||||
diagnostics usability slice, the beta.5 package discipline slice, the beta.6
|
diagnostics usability slice, the beta.5 package discipline slice, the beta.6
|
||||||
networking foundation slice, the beta.7 serialization foundation slice, and
|
networking foundation slice, the beta.7 serialization foundation slice, the
|
||||||
the beta.8 concrete alias foundation slice, the beta.9 collection alias and
|
beta.8 concrete alias foundation slice, the beta.9 collection alias and
|
||||||
generic-reservation slice, and the remaining path from beta to stable.
|
generic-reservation slice, the beta.10 API-discovery and symbol-metadata
|
||||||
|
slice, and the remaining path from beta to stable.
|
||||||
|
|
||||||
## 1. Scope
|
## 1. Scope
|
||||||
|
|
||||||
This document is a technical state paper for the current beta baseline. It
|
This document is a technical state paper for the current beta baseline. It
|
||||||
summarizes the behavior represented by the paired Slovo and Glagol source
|
summarizes the behavior represented by the paired Slovo and Glagol source
|
||||||
tree, with `1.0.0-beta` as the current language-surface baseline and
|
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:
|
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
|
- partial parser recognition or speculative examples do not count as support
|
||||||
|
|
||||||
Historical `exp-*` releases remain experimental alpha maturity. The current
|
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
|
## 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.7` adds a narrow JSON construction foundation, and
|
||||||
`1.0.0-beta.8` adds transparent concrete type aliases, and `1.0.0-beta.9`
|
`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
|
applies those aliases inside current collection/value-family facades while
|
||||||
reserving generic, map, and set syntax through diagnostics. None of these
|
reserving generic, map, and set syntax through diagnostics, and
|
||||||
post-beta slices claims changed benchmark performance. The beta.7 `json-quote-loop`
|
`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
|
scaffold is present for local follow-up timing and is not part of the exp-123
|
||||||
nine-row result table below.
|
nine-row result table below.
|
||||||
|
|
||||||
@ -512,7 +519,7 @@ Major remaining gaps before `1.0.0`:
|
|||||||
- semantic versioning and deprecation policy
|
- semantic versioning and deprecation policy
|
||||||
- a clear separation between stable and experimental features
|
- 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 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
|
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.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.8` as the first concrete type alias foundation point, and
|
||||||
`1.0.0-beta.9` as the first collection alias unification and generic
|
`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:
|
Recommended sequence:
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -7,15 +7,67 @@ const path = require("path");
|
|||||||
const repoRoot = path.resolve(__dirname, "..");
|
const repoRoot = path.resolve(__dirname, "..");
|
||||||
const stdDir = path.join(repoRoot, "lib", "std");
|
const stdDir = path.join(repoRoot, "lib", "std");
|
||||||
const outputPath = path.join(repoRoot, "docs", "language", "STDLIB_API.md");
|
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) {
|
function tokenize(source) {
|
||||||
const tokens = [];
|
const tokens = [];
|
||||||
const pattern = /\(|\)|[^\s()]+/g;
|
let index = 0;
|
||||||
let match;
|
|
||||||
while ((match = pattern.exec(source)) !== null) {
|
while (index < source.length) {
|
||||||
tokens.push(match[0]);
|
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;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,12 +92,24 @@ function parseList(tokens, cursor) {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
function moduleForm(source, file) {
|
function topLevelForms(source, file) {
|
||||||
const tokens = tokenize(source);
|
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`);
|
throw new Error(`${file}: expected module form`);
|
||||||
}
|
}
|
||||||
const form = parseList(tokens, { index: 0 });
|
|
||||||
if (form[0] !== "module" || typeof form[1] !== "string") {
|
if (form[0] !== "module" || typeof form[1] !== "string") {
|
||||||
throw new Error(`${file}: expected (module name ...) form`);
|
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] || "<unknown>"}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
function stdModules() {
|
||||||
return fs
|
return fs
|
||||||
.readdirSync(stdDir)
|
.readdirSync(stdDir)
|
||||||
@ -67,31 +196,48 @@ function stdModules() {
|
|||||||
.map((fileName) => {
|
.map((fileName) => {
|
||||||
const relativePath = path.join("lib", "std", fileName);
|
const relativePath = path.join("lib", "std", fileName);
|
||||||
const source = fs.readFileSync(path.join(stdDir, fileName), "utf8");
|
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 {
|
return {
|
||||||
fileName,
|
fileName,
|
||||||
relativePath,
|
relativePath,
|
||||||
name: module.name,
|
name: module.name,
|
||||||
exports: module.exports,
|
signatures,
|
||||||
|
omittedAliasExports,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function packageVersion() {
|
function releaseVersion() {
|
||||||
const cargoToml = fs.readFileSync(cargoTomlPath, "utf8");
|
const readme = fs.readFileSync(readmePath, "utf8");
|
||||||
const versionMatch = cargoToml.match(/^version\s*=\s*"([^"]+)"$/m);
|
const versionMatch = readme.match(/^Current release:\s*`([^`]+)`\.$/m);
|
||||||
if (!versionMatch) {
|
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];
|
return versionMatch[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
function render(modules, version) {
|
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 = [];
|
const out = [];
|
||||||
out.push("# Slovo Standard Library API Catalog");
|
out.push("# Slovo Standard Library API Catalog");
|
||||||
out.push("");
|
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("Do not edit this file by hand.");
|
||||||
out.push("");
|
out.push("");
|
||||||
out.push("## Stability Tiers");
|
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(`- \`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("- `internal`: helper names that are not exported from their module; they are intentionally omitted from this catalog.");
|
||||||
out.push("");
|
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("");
|
||||||
out.push("## Summary");
|
out.push("## Summary");
|
||||||
out.push("");
|
out.push("");
|
||||||
out.push(`- Modules: ${modules.length}`);
|
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("- Default tier: `beta-supported`");
|
||||||
out.push("");
|
out.push("");
|
||||||
out.push("## Modules");
|
out.push("## Modules");
|
||||||
@ -115,14 +264,14 @@ function render(modules, version) {
|
|||||||
out.push("");
|
out.push("");
|
||||||
out.push(`- Path: \`${module.relativePath}\``);
|
out.push(`- Path: \`${module.relativePath}\``);
|
||||||
out.push("- Tier: `beta-supported`");
|
out.push("- Tier: `beta-supported`");
|
||||||
out.push(`- Exported helpers: ${module.exports.length}`);
|
out.push(`- Exported helper signatures: ${module.signatures.length}`);
|
||||||
out.push("");
|
out.push("");
|
||||||
for (const exported of module.exports) {
|
for (const signature of module.signatures) {
|
||||||
out.push(`- \`${exported}\``);
|
out.push(`- \`${signature}\``);
|
||||||
}
|
}
|
||||||
out.push("");
|
out.push("");
|
||||||
}
|
}
|
||||||
return `${out.join("\n")}\n`;
|
return `${out.join("\n")}\n`;
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFileSync(outputPath, render(stdModules(), packageVersion()));
|
fs.writeFileSync(outputPath, render(stdModules(), releaseVersion()));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user