feat: lifetime elision, tooling CI, registry, and LSP locals
Ship the QUALITY_PLAN stretch from ownership through ecosystem: C.1 lifetime elision (bootstrap + selfhost), bux fmt/test/doc CI hooks, stdlib goldens, package registry (bux search/add), and LSP 0.4 position-sensitive locals with inferred let types. Full-tree format pass plus Map/Set remove double-free fix.
This commit is contained in:
+49
-4
@@ -152,15 +152,60 @@ This runs:
|
||||
|
||||
### Project Tests (`bux test`)
|
||||
```bash
|
||||
./buxc test
|
||||
./buxc test # run all tests/*.bux in the current package
|
||||
./buxc test --filter first # only tests whose name contains "first"
|
||||
./buxc test --filter=first _test_runner
|
||||
```
|
||||
|
||||
Builds the project and runs the resulting binary. Reports:
|
||||
- `Tests passed` on exit code 0
|
||||
- `Tests failed (exit code N)` on non-zero exit
|
||||
Discovers `tests/*.bux`, builds each as a temp package, and runs it. Prints a
|
||||
summary table and exits:
|
||||
- `0` — all selected tests passed
|
||||
- `1` — at least one failure, or no tests matched the filter
|
||||
|
||||
Use `Std::Test` module for assertions inside test code.
|
||||
|
||||
### Format (`bux fmt`)
|
||||
```bash
|
||||
./buxc fmt examples/hello.bux # reformat one file
|
||||
./buxc fmt lib/ # reformat a directory tree
|
||||
make fmt # reformat lib/ examples/ src/ tests/ apps/
|
||||
./buxc fmt --check path/ # exit 1 if any file would change
|
||||
make fmt-check # CI: full-tree clean + dirty smoke
|
||||
```
|
||||
|
||||
Indentation is 4 spaces by brace depth. The formatter is idempotent (safe to re-run).
|
||||
`make fmt-check` enforces a clean tree under `lib/`, `examples/`, `src/`, `tests/`, and `apps/`.
|
||||
|
||||
### Stdlib golden tests
|
||||
```bash
|
||||
make test-stdlib
|
||||
# or: tests/stdlib_golden/run.sh ./buxc
|
||||
```
|
||||
|
||||
Behavioral packages under `tests/stdlib_golden/` (`array`, `string`, `collections`)
|
||||
assert core Array/String/Map/Set/Result/Option APIs and match expected `PASS` lines.
|
||||
|
||||
### API docs (`bux doc`)
|
||||
```bash
|
||||
./buxc doc lib/ # Markdown to stdout
|
||||
./buxc doc --out docs/api/stdlib.md lib/
|
||||
make docs # writes docs/api/stdlib.md
|
||||
```
|
||||
|
||||
Scans `///` line comments (and bootstrap also accepts adjacent `/* */`) immediately
|
||||
before `func` / `struct` / `enum` / `interface` / `module` declarations.
|
||||
|
||||
### Language Server (`bux-lsp` 0.4.0)
|
||||
```bash
|
||||
make lsp # → tools/bux-lsp
|
||||
nim r --path:bootstrap tools/test_lsp_locals.nim
|
||||
./tools/smoke_lsp_hover.sh
|
||||
```
|
||||
|
||||
Features: diagnostics (`buxc check`), hover, go-to-def, outline, completion.
|
||||
**Locals are position-sensitive** (nested scopes / shadowing). **Inferred `let` types**
|
||||
appear on hover (`let x: int · inferred`).
|
||||
|
||||
### Example Programs
|
||||
```bash
|
||||
make test-examples
|
||||
|
||||
@@ -604,6 +604,44 @@ Moves happen in three contexts:
|
||||
msg = "reassigned"; // OK: reinitialization
|
||||
PrintLine(msg);
|
||||
```
|
||||
- **No dangling returns**: cannot return a reference to a local (or by-value parameter)
|
||||
```bux
|
||||
@[Checked]
|
||||
func Bad(p: &int) -> &int {
|
||||
var x: int = 1;
|
||||
return &x; // ERROR: cannot return reference to local variable
|
||||
}
|
||||
```
|
||||
|
||||
### Lifetime elision (C.1)
|
||||
|
||||
In `@[Checked]` functions, most reference signatures need **no** lifetime annotations.
|
||||
Elision applies the usual single-input rules:
|
||||
|
||||
1. Each elided input `&T` / `&mut T` parameter gets a distinct lifetime.
|
||||
2. If there is **exactly one** input lifetime, it is assigned to all elided outputs.
|
||||
3. If the first parameter is named `self` / `Self`, that input lifetime is preferred for outputs.
|
||||
4. Multiple input references + elided return → **error** (write an explicit lifetime).
|
||||
|
||||
```bux
|
||||
// Elided — one input ref, return shares its lifetime
|
||||
@[Checked]
|
||||
func Identity(p: &int) -> &int {
|
||||
return p; // OK
|
||||
}
|
||||
|
||||
// Explicit — required when several inputs could be returned
|
||||
@[Checked]
|
||||
func Pick<'a>(a: &'a int, b: &'a int) -> &'a int {
|
||||
return a;
|
||||
}
|
||||
|
||||
// Syntax: &'a T and &mut / &'a mut T (lifetime before `mut`)
|
||||
// Type parameters: func F<'a, T>(...)
|
||||
```
|
||||
|
||||
Unchecked functions ignore lifetime rules (C-like). Explicit `'a` is optional
|
||||
documentation when a single input would already elide correctly.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+76
-28
@@ -1,6 +1,8 @@
|
||||
# Bux Package Manager
|
||||
|
||||
> **Status:** Implemented (Phase 9.1)
|
||||
> **Status:** Path + git + **local/file registry** (E.1). HTTP registry index URL optional later.
|
||||
|
||||
See also: [SEMVER.md](SEMVER.md) for version policy.
|
||||
|
||||
---
|
||||
|
||||
@@ -20,52 +22,97 @@ License = "MIT"
|
||||
Output = "Bin"
|
||||
|
||||
[Dependencies]
|
||||
Std = "1.0"
|
||||
Json = { Version = "2.1", Source = "https://github.com/bux-lang/json" }
|
||||
greet = { Path = "/abs/path/to/greet" }
|
||||
Json = { Version = "2.1", Source = "https://github.com/bux-lang/json" }
|
||||
Utils = { Path = "../Utils" }
|
||||
# Registry name-only (resolved by `bux add` / `bux install`):
|
||||
# greet = "0.1.1"
|
||||
```
|
||||
|
||||
### Dependency Forms
|
||||
|
||||
| Form | Example | Description |
|
||||
|------|---------|-------------|
|
||||
| Version string | `Std = "1.0"` | Registry dependency |
|
||||
| Wildcard | `Std = "*"` | Latest version |
|
||||
| Version string | `greet = "0.1.1"` | Registry dependency |
|
||||
| Wildcard | `greet = "*"` | Latest registry version |
|
||||
| Inline table (git) | `{ Version = "1.4", Source = "https://..." }` | Git URL + version |
|
||||
| Inline table (path) | `{ Path = "../Lib" }` | Local path dependency |
|
||||
|
||||
---
|
||||
|
||||
## Package registry (E.1)
|
||||
|
||||
### Index file
|
||||
|
||||
Default locations (first hit wins):
|
||||
|
||||
1. `$BUX_REGISTRY` — path to a `registry.toml`
|
||||
2. `~/.bux/registry.toml`
|
||||
3. `config/registry.toml` next to the Bux repo / compiler
|
||||
|
||||
Format:
|
||||
|
||||
```toml
|
||||
[[package]]
|
||||
name = "greet"
|
||||
version = "0.1.1"
|
||||
source = "file:../registry/packages/greet" # relative to the index file
|
||||
description = "Hello helpers"
|
||||
|
||||
[[package]]
|
||||
name = "net"
|
||||
version = "1.0.0"
|
||||
source = "https://github.com/example/bux-net.git"
|
||||
description = "TCP helpers"
|
||||
```
|
||||
|
||||
`file:` / `path:` sources are resolved relative to the registry file.
|
||||
Git URLs are cloned into `~/.bux/packages/<name>/` on install.
|
||||
|
||||
### CLI
|
||||
|
||||
```bash
|
||||
# Search the index
|
||||
bux search
|
||||
bux search greet
|
||||
|
||||
# Add by registry name (writes Path or git Source into bux.toml)
|
||||
bux add greet
|
||||
bux add greet 0.1.1
|
||||
|
||||
# Explicit sources still work
|
||||
bux add utils --path "../utils"
|
||||
bux add network --git "https://github.com/bux-lang/network"
|
||||
|
||||
# Resolve + write bux.lock
|
||||
bux install
|
||||
```
|
||||
|
||||
Demo package in this monorepo: `registry/packages/greet` (registered in
|
||||
`config/registry.toml`). Smoke test: `tools/smoke_registry.sh`.
|
||||
|
||||
---
|
||||
|
||||
## CLI Commands
|
||||
|
||||
### `bux add <name> [version]`
|
||||
|
||||
Add a dependency to `bux.toml`.
|
||||
Add a dependency to `bux.toml` (registry / `--path` / `--git`).
|
||||
|
||||
```bash
|
||||
# Add registry dependency
|
||||
bux add json "2.1"
|
||||
### `bux search [query]`
|
||||
|
||||
# Add path-based dependency
|
||||
bux add utils --path "../utils"
|
||||
|
||||
# Add git dependency
|
||||
bux add network --git "https://github.com/bux-lang/network"
|
||||
```
|
||||
List packages in the active registry (filter by name/description).
|
||||
|
||||
### `bux install`
|
||||
|
||||
Resolve dependencies and generate `bux.lock`.
|
||||
|
||||
```bash
|
||||
bux install
|
||||
```
|
||||
|
||||
What it does:
|
||||
1. Reads `[Dependencies]` from `bux.toml`
|
||||
2. Resolves path-based deps (verifies directory exists)
|
||||
3. Clones/pulls git-based deps to `~/.bux/packages/<name>/`
|
||||
4. Generates `bux.lock` with exact versions and sources
|
||||
3. Clones git-based deps to `~/.bux/packages/<name>/`
|
||||
4. Resolves bare version names via the registry index
|
||||
5. Generates `bux.lock` with exact versions and sources
|
||||
|
||||
### `bux build` / `bux run`
|
||||
|
||||
@@ -84,10 +131,9 @@ Auto-generated. **Do not edit manually.**
|
||||
|
||||
```toml
|
||||
[[Package]]
|
||||
Name = "json"
|
||||
Version = "2.1.3"
|
||||
Source = "https://github.com/bux-lang/json"
|
||||
Checksum = "8dcb2a7f..."
|
||||
Name = "greet"
|
||||
Version = "0.1.1"
|
||||
Source = "/home/user/z-git/bux/bux/registry/packages/greet"
|
||||
|
||||
[[Package]]
|
||||
Name = "utils"
|
||||
@@ -103,7 +149,7 @@ The lockfile ensures **reproducible builds** — every developer gets the exact
|
||||
|
||||
1. **Path-based** deps are resolved relative to the manifest directory
|
||||
2. **Git-based** deps are cloned to `~/.bux/packages/<name>/`
|
||||
3. **Version-based** deps (without Source) require a registry (future feature)
|
||||
3. **Version-based** deps look up `config/registry.toml` (or `$BUX_REGISTRY`)
|
||||
4. Dependencies are loaded from `<dep>/src/*.bux` at build time
|
||||
5. Later declarations shadow earlier ones (project > deps > stdlib)
|
||||
|
||||
@@ -114,8 +160,10 @@ The lockfile ensures **reproducible builds** — every developer gets the exact
|
||||
```bash
|
||||
bux new mylib
|
||||
cd mylib
|
||||
# Edit src/Main.bux → module MyLib { pub func Add(...) }
|
||||
bux build # Builds as library (Type = "lib")
|
||||
# Edit src/*.bux → module MyLib { func Add(...) }
|
||||
# Set Type = "lib" in bux.toml
|
||||
# Register in your registry.toml with source = "file:..."
|
||||
bux build
|
||||
```
|
||||
|
||||
## Example: Using a Library
|
||||
|
||||
+131
-23
@@ -1,7 +1,7 @@
|
||||
# Bux — План към „добър“ език (v0.5 → v1.0)
|
||||
|
||||
> **Дата:** 2026-07-18
|
||||
> **Текущо:** v0.5.x — selfhost loop, gradual ownership, green threads, **43+ examples**, match + guards + **generic HOF inference** + pattern bindings + **`f"..."` interp** bootstrap+selfhost ✅
|
||||
> **Текущо:** v0.5.x — selfhost, C.1, tooling, LSP 0.4, full-tree fmt, **package registry (E.1)** ✅
|
||||
> **Цел:** Език, с който се пишат реални проекти комфортно, безопасно (по избор) и с надежден toolchain.
|
||||
|
||||
---
|
||||
@@ -14,11 +14,11 @@
|
||||
| Sema / generics | Monomorphization, trait bounds basic | ★★★★☆ |
|
||||
| HIR → C | Tuples + fat `func` ABI в bootstrap **и** selfhost | ★★★★☆ |
|
||||
| Selfhost (`src/`) | ~12k LOC, binary-identical loop, closures+tuples | ★★★★★ |
|
||||
| Gradual ownership | `@[Checked]`, `&`/`&mut`, move, Drop | ★★★☆☆ (basic) |
|
||||
| Gradual ownership | `@[Checked]`, `&`/`&mut`, move, Drop, **lifetime elision** | ★★★★☆ |
|
||||
| Concurrency | M:N tasks + channels + async | ★★★★☆ |
|
||||
| Stdlib | Array/Map/Set/String/Iter HOF разширени | ★★★★☆ |
|
||||
| Tooling | `test-errors`, LSP diagnostics + hover/def/outline | ★★★★☆ |
|
||||
| Ecosystem / registry | path+git deps; няма централен registry | ★☆☆☆☆ |
|
||||
| Ecosystem / registry | path+git + **file registry index** (`bux search/add`) | ★★★☆☆ |
|
||||
| Документация | README + QUALITY_PLAN синхронизирани (2026-07-15) | ★★★★☆ |
|
||||
|
||||
**Силна ниша:** gradual ownership (C-скорост на писане + opt-in Rust-safety).
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
| # | Задача | Защо | Статус |
|
||||
|---|--------|------|--------|
|
||||
| C.1 | Lifetime elision за common cases | Без `'a` в 90% от API-тата | ⏳ |
|
||||
| C.1 | Lifetime elision за common cases | Без `'a` в 90% от API-тата | ✅ bootstrap + selfhost |
|
||||
| C.2 | Exclusive `&mut` vs shared `&` data-flow | По-малко false negatives | ✅ let-bound + use-while + call conflict |
|
||||
| C.3 | Auto-drop edge cases (early return, branches) | RAII да е надежден | ✅ bootstrap + selfhost |
|
||||
| C.4 | `@[Release]` zero-cost path документация + golden tests | Killer story: safe default, free hot path | ✅ partial (unchecked path + goldens) |
|
||||
@@ -78,21 +78,21 @@
|
||||
|
||||
| # | Задача | Защо | Статус |
|
||||
|---|--------|------|--------|
|
||||
| D.1 | LSP: hover, go-to-def, diagnostics | IDE = adoption | ✅ hover/def/outline + **sema types on hover** (v0.3.0) + `buxc` diags |
|
||||
| D.2 | `bux fmt` стабилен + CI check | Единен style | ⏳ |
|
||||
| D.3 | `bux test` с `--filter`, exit codes, summary table | CI-friendly | ⏳ partial (`bux test` exists) |
|
||||
| D.4 | `bux doc` от `///` comments | Самодокументиращ се stdlib | ⏳ |
|
||||
| D.5 | Golden tests за stdlib modules | Регресии без изненади | ⏳ |
|
||||
| D.1 | LSP: hover, go-to-def, diagnostics | IDE = adoption | ✅ v0.4.0: **position-sensitive locals** + **inferred `let`** + sema hover |
|
||||
| D.2 | `bux fmt` стабилен + CI check | Единен style | ✅ full-tree format + `make fmt-check` enforce |
|
||||
| D.3 | `bux test` с `--filter`, exit codes, summary table | CI-friendly | ✅ `--filter` / summary / exit 0\|1 |
|
||||
| D.4 | `bux doc` от `///` comments | Самодокументиращ се stdlib | ✅ bootstrap+selfhost + `make docs` |
|
||||
| D.5 | Golden tests за stdlib modules | Регресии без изненади | ✅ `tests/stdlib_golden/` + `make test-stdlib` |
|
||||
|
||||
### E — Ecosystem & v1.0 (P2)
|
||||
|
||||
| # | Задача | Защо |
|
||||
|---|--------|------|
|
||||
| E.1 | Package registry protocol (git/HTTP) | `bux add foo` без path hacks |
|
||||
| E.2 | 3–5 production-quality apps в `apps/` | Showcase |
|
||||
| E.3 | Language freeze + semver policy | Trust |
|
||||
| E.4 | Debugger/DWARF basics | Systems audience |
|
||||
| E.5 | Benchmarks vs C/Zig/Nim (micro + nexus) | Marketing + regression |
|
||||
| # | Задача | Защо | Статус |
|
||||
|---|--------|------|--------|
|
||||
| E.1 | Package registry protocol (git/HTTP) | `bux add foo` без path hacks | ✅ local index + file/git sources + `search` |
|
||||
| E.2 | 3–5 production-quality apps в `apps/` | Showcase | ⏳ partial (`nexus`, `boko`, `simpledb`, `jwt-pitbul`) |
|
||||
| E.3 | Language freeze + semver policy | Trust | ✅ draft `docs/SEMVER.md` |
|
||||
| E.4 | Debugger/DWARF basics | Systems audience | ⏳ |
|
||||
| E.5 | Benchmarks vs C/Zig/Nim (micro + nexus) | Marketing + regression | ⏳ |
|
||||
|
||||
---
|
||||
|
||||
@@ -114,10 +114,10 @@ A (stdlib ergonomics) → B (compiler holes) → C (ownership depth)
|
||||
|
||||
- [ ] Всички examples + selfhost-loop + 3 apps минават на CI
|
||||
- [ ] Array/Map/String/Test API покрива 90% от ежедневните нужди
|
||||
- [ ] `@[Checked]` хваща use-after-move + double `&mut` в documented subset
|
||||
- [ ] `bux test` + `bux fmt` + `bux check` са default developer loop
|
||||
- [ ] LanguageRef синхронизиран с компилатора
|
||||
- [ ] Поне един външен проект (не в monorepo) build-ва с git dep
|
||||
- [x] `@[Checked]` хваща use-after-move + double `&mut` + dangling return / elision fail
|
||||
- [x] `bux test` + `bux fmt` + `bux check` са default developer loop (`--filter` / `--check` shipped)
|
||||
- [x] LanguageRef синхронизиран с компилатора (incl. C.1 elision)
|
||||
- [x] Поне един външен/temp проект build-ва с registry dep (`tools/smoke_registry.sh`)
|
||||
|
||||
---
|
||||
|
||||
@@ -388,8 +388,116 @@ A (stdlib ergonomics) → B (compiler holes) → C (ownership depth)
|
||||
|
||||
---
|
||||
|
||||
## Сесия 24 (tooling — D.2 fmt --check + D.3 test --filter)
|
||||
|
||||
1. **Bootstrap `bux fmt`** (`bootstrap/fmt.nim`):
|
||||
- Indent-by-brace-depth formatter (parity with `src/fmt.bux`)
|
||||
- `bux fmt [path...]` writes; `bux fmt --check` exits 1 if any file would change
|
||||
- Collects single file or recursive `.bux` under directories
|
||||
2. **Bootstrap `bux test --filter`**:
|
||||
- `--filter <s>` / `--filter=<s>` — only run `tests/*.bux` whose name contains `s`
|
||||
- Summary table (`PASS` / `FAIL[:code]`) + `Results: N passed, M failed, T total`
|
||||
- Exit `0` all pass, `1` failures or no match
|
||||
3. **Selfhost parity** (`src/cli.bux`, `src/fmt.bux`):
|
||||
- `Fmt_WouldChange` / `Fmt_CheckFile`; `Cli_Fmt(dir, checkOnly)`
|
||||
- `Cli_Test(dir, filter)` with summary + skip count; filter skips Main package run
|
||||
4. **CI hooks:** `make fmt-check` smoke (clean→0, dirty→1); full-tree enforce deferred
|
||||
until a one-shot format pass on `lib/`/`examples/`
|
||||
5. **Idempotence fix:** drop trailing split-empty so re-format is a no-op
|
||||
6. Verified: unit suite + `./buxc test --filter first _test_runner` + selfhost `buxc2`
|
||||
fmt/test parity
|
||||
|
||||
---
|
||||
|
||||
## Сесия 25 (Ownership 2.0 — C.1 lifetime elision)
|
||||
|
||||
1. **Elision rules** in `@[Checked]` (`bootstrap/sema.nim`):
|
||||
- Each elided input `&`/`&mut` → distinct `#elidedN`
|
||||
- One input lifetime → assigned to elided return
|
||||
- First param `self`/`Self` preferred when multiple inputs
|
||||
- Multiple inputs + elided return → `lifetime elision failed` (need `'a`)
|
||||
2. **Return checks:**
|
||||
- `cannot return reference to local variable` (`return &local` / let-bound local ref)
|
||||
- `no input reference to borrow from` (return ref with zero input refs)
|
||||
- Explicit `'a` mismatch between return and value
|
||||
3. **Body check for lifetime-only generics** (`func F<'a>(...)`) — no longer skipped
|
||||
4. **Diagnostics hints** for elision / dangling / mismatch
|
||||
5. **Tests:** 8 new borrow_test cases; goldens `return_local_ref`, `elision_multi_input`
|
||||
6. **Example:** `examples/lifetime_elision.bux` (Identity / explicit / ViaLet / self)
|
||||
7. LanguageRef + QUALITY_PLAN updated
|
||||
8. Verified: borrow_test 24/24, 9 error goldens, example runs
|
||||
|
||||
## Сесия 26 (C.1 selfhost parity)
|
||||
|
||||
1. **Lexer** (`src/lexer.bux` + `tkLifetime=111`): `'a` vs char `'x'` (same heuristic as bootstrap)
|
||||
2. **Parser:**
|
||||
- `&'a T` / `&'a mut T` → `TypeExpr.refLifetime`
|
||||
- `func F<'a, T>(…)` — lifetime params accepted and **skipped** for mono slots
|
||||
3. **Sema** lifetime elision (fixed 8-slot maps, same rules as bootstrap):
|
||||
- single-input elision, `self` preference, multi-input fail
|
||||
- return-local / no-input-ref / explicit mismatch
|
||||
- let-bound ref lifetime propagation
|
||||
4. Fixed `checkFunc` else-branch that wiped `checkedFunc` when retType was void
|
||||
5. Verified: `buxc2 run lifetime_elision` PASS; goldens on buxc2 show same errors;
|
||||
bootstrap still green; **selfhost-loop** expected IDENTICAL
|
||||
|
||||
---
|
||||
|
||||
## Сесия 27 (tooling — D.4 bux doc + D.5 stdlib goldens)
|
||||
|
||||
1. **D.5 Stdlib goldens** (`tests/stdlib_golden/`):
|
||||
- Packages: `array`, `string`, `collections` (Map/Set/Result/Option)
|
||||
- `run.sh` builds via `buxc run` and matches expected PASS lines
|
||||
- `make test-stdlib` wired into `make test`
|
||||
2. **D.4 `bux doc`**:
|
||||
- Bootstrap: `bootstrap/docgen.nim` — `///` + adjacent `/* */`
|
||||
- Selfhost: `Cli_Doc` line scanner for `///`
|
||||
- `bux doc [--out file] [path]` (default path `lib/`)
|
||||
- `make docs` → `docs/api/stdlib.md`
|
||||
3. **Stdlib docs:** `///` on Array / String / Test public helpers
|
||||
4. Verified: `make test-stdlib`, `./buxc doc lib/Array.bux | head`, selfhost build
|
||||
|
||||
---
|
||||
|
||||
## Сесия 28 (LSP v0.4.0 — position-sensitive locals + inferred lets)
|
||||
|
||||
1. **`LocalBinding`** with scope range (`scopeStartLine`…`scopeEndLine`) per let/param
|
||||
2. **Sema-backed inference** (`checkExprForLsp` / `resolveType`):
|
||||
- `let x = 42` → hover `let x: int` · inferred
|
||||
- `let s: String = "…"` → annotated, not inferred
|
||||
- params: `param a: int` visible for whole function
|
||||
3. **Position-sensitive** hover / go-to-def / completion (innermost scope wins on shadowing)
|
||||
4. Nested scopes: if/while/for/match/block arms
|
||||
5. Version **bux-lsp 0.4.0**; tests: `tools/test_lsp_locals.nim`, `tools/smoke_lsp_hover.sh`
|
||||
6. Verified: hover shows `let sum: int · inferred`, `param a: int`, `let n: int · inferred`
|
||||
|
||||
---
|
||||
|
||||
## Сесия 29 (full-tree `bux fmt` + CI enforce)
|
||||
|
||||
1. **One-shot format** of `lib/` (33), `examples/` (23), `src/` (15), `tests/` (8), `apps/` (12)
|
||||
2. **Idempotent:** second `--check` → 0 would reformat on all trees
|
||||
3. **CI:** `make fmt-check` enforces full tree + dirty-path smoke (exit 1)
|
||||
4. **`make fmt`** helper to reformat the same roots
|
||||
5. Verified: `test-stdlib`, key examples, **selfhost + selfhost-loop IDENTICAL ✓**
|
||||
|
||||
---
|
||||
|
||||
## Сесия 30 (E.1 package registry + E.3 semver draft)
|
||||
|
||||
1. **Registry index** (`config/registry.toml`, `$BUX_REGISTRY`, `~/.bux/registry.toml`)
|
||||
- `[[package]]` with `name` / `version` / `source` / `description`
|
||||
- `file:` / `path:` (relative to index) or git URL
|
||||
2. **CLI:** `bux search [q]`, `bux add <name>` resolves registry, `bux install` locks path/git
|
||||
3. **Demo package:** `registry/packages/greet` (`Greet_Hello`, `Greet_Version`)
|
||||
4. **Smoke:** `tools/smoke_registry.sh` / `make test-registry` — temp app outside tree
|
||||
5. **Semver policy:** `docs/SEMVER.md` (0.x vs 1.0, registry version match)
|
||||
6. Packages.md updated
|
||||
|
||||
---
|
||||
|
||||
## Следващи стъпки
|
||||
|
||||
1. C.1 Lifetime elision
|
||||
2. Phase D tooling: `bux fmt` CI, `bux test --filter`, golden stdlib tests
|
||||
3. LSP: position-sensitive locals; inferred `let` types
|
||||
1. E.2 polish apps / E.5 benchmarks
|
||||
2. HTTP-fetchable registry index URL (beyond local file)
|
||||
3. LSP: workspace rename / references (optional)
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# Bux Semantic Versioning Policy
|
||||
|
||||
> Status: Draft for v0.x → v1.0 freeze (E.3)
|
||||
|
||||
Bux follows [Semantic Versioning 2.0.0](https://semver.org/) with the
|
||||
clarifications below.
|
||||
|
||||
---
|
||||
|
||||
## Version numbers
|
||||
|
||||
```
|
||||
MAJOR.MINOR.PATCH[-prerelease]
|
||||
```
|
||||
|
||||
| Component | When it increases |
|
||||
|-----------|-------------------|
|
||||
| **MAJOR** | Breaking language / stdlib / CLI changes |
|
||||
| **MINOR** | Backward-compatible features |
|
||||
| **PATCH** | Backward-compatible bug fixes |
|
||||
|
||||
During **0.x** (pre-1.0):
|
||||
|
||||
- `0.MINOR.PATCH` — MINOR may still introduce breaking changes (documented in
|
||||
the release notes and `MIGRATION_*.sh` when needed).
|
||||
- Prefer deprecation warnings for at least one MINOR before removal when
|
||||
practical.
|
||||
|
||||
After **1.0.0** (language freeze):
|
||||
|
||||
- Breaking changes require a MAJOR bump and a migration guide.
|
||||
- The Language Reference is the normative spec; compiler bugs that contradict
|
||||
the ref are fixed without a MAJOR bump.
|
||||
|
||||
---
|
||||
|
||||
## What counts as “breaking”
|
||||
|
||||
- Removing or renaming a public stdlib symbol
|
||||
- Changing the type or semantics of a public API
|
||||
- Changing CLI flags that scripts rely on (`build`, `test`, `fmt --check`, …)
|
||||
- Changing `bux.toml` / `bux.lock` fields in an incompatible way
|
||||
- Changing the fat `func` / tuple C ABI in a way that breaks linked code
|
||||
|
||||
**Not breaking:**
|
||||
|
||||
- New keywords that were previously valid identifiers only if reserved carefully
|
||||
(prefer contextual keywords)
|
||||
- New diagnostics / stricter `@[Checked]` (document; may be gated)
|
||||
- Formatter whitespace-only changes
|
||||
|
||||
---
|
||||
|
||||
## Package versions (registry)
|
||||
|
||||
Registry packages use the same MAJOR.MINOR.PATCH scheme.
|
||||
|
||||
`bux add foo` / `bux add foo 0.1` resolution:
|
||||
|
||||
| Request | Matches |
|
||||
|---------|---------|
|
||||
| `*` / omitted | Latest entry for `foo` in the index |
|
||||
| `0.1.1` | Exact version |
|
||||
| `0.1` | First version with that prefix (e.g. `0.1.1`) |
|
||||
|
||||
Lockfiles pin the **resolved** version and source path/URL.
|
||||
|
||||
---
|
||||
|
||||
## Release checklist (maintainers)
|
||||
|
||||
1. Update `docs/LanguageRef.md` if behaviour changed
|
||||
2. Update `docs/QUALITY_PLAN.md` / changelog notes
|
||||
3. Run `make test` (includes `fmt-check`, examples, goldens)
|
||||
4. Run `make selfhost-loop`
|
||||
5. Tag `vMAJOR.MINOR.PATCH`
|
||||
@@ -0,0 +1,749 @@
|
||||
# API Reference
|
||||
|
||||
Generated by `bux doc` from `///` and `/* */` documentation comments.
|
||||
|
||||
## `Array`
|
||||
|
||||
_Source: `lib/Array.bux`_
|
||||
|
||||
### `Array` _struct_
|
||||
|
||||
```bux
|
||||
struct Array<T> {
|
||||
```
|
||||
|
||||
Growable contiguous buffer of `T` (len + capacity).
|
||||
|
||||
### `Array_New` _func_
|
||||
|
||||
```bux
|
||||
func Array_New<T>(cap: uint) -> Array<T> {
|
||||
```
|
||||
|
||||
Create an empty array with the given initial capacity.
|
||||
|
||||
### `Array_Push` _func_
|
||||
|
||||
```bux
|
||||
func Array_Push<T>(self: *Array<T>, value: T) {
|
||||
```
|
||||
|
||||
Append `value`, growing capacity if needed.
|
||||
|
||||
### `Array_Get` _func_
|
||||
|
||||
```bux
|
||||
func Array_Get<T>(self: *Array<T>, index: uint) -> T {
|
||||
```
|
||||
|
||||
Element at `index` (bounds-checked unless `@[Release]`).
|
||||
|
||||
### `Array_Set` _func_
|
||||
|
||||
```bux
|
||||
func Array_Set<T>(self: *Array<T>, index: uint, value: T) {
|
||||
```
|
||||
|
||||
Write `value` at `index` (bounds-checked unless `@[Release]`).
|
||||
|
||||
### `Array_Len` _func_
|
||||
|
||||
```bux
|
||||
func Array_Len<T>(self: *Array<T>) -> uint {
|
||||
```
|
||||
|
||||
Number of live elements.
|
||||
|
||||
### `Array_Free` _func_
|
||||
|
||||
```bux
|
||||
func Array_Free<T>(self: *Array<T>) {
|
||||
```
|
||||
|
||||
Free the backing buffer and reset length/capacity to zero.
|
||||
|
||||
### `Array_Drop` _func_
|
||||
|
||||
```bux
|
||||
func Array_Drop<T>(self: *Array<T>) {
|
||||
```
|
||||
|
||||
Drop trait entry — same as `Array_Free`.
|
||||
|
||||
### `Array_IsEmpty` _func_
|
||||
|
||||
```bux
|
||||
func Array_IsEmpty<T>(self: *Array<T>) -> bool {
|
||||
```
|
||||
|
||||
True if the array has no elements.
|
||||
|
||||
### `Array_Cap` _func_
|
||||
|
||||
```bux
|
||||
func Array_Cap<T>(self: *Array<T>) -> uint {
|
||||
```
|
||||
|
||||
Current capacity (not length).
|
||||
|
||||
### `Array_Clear` _func_
|
||||
|
||||
```bux
|
||||
func Array_Clear<T>(self: *Array<T>) {
|
||||
```
|
||||
|
||||
Drop length to zero; keeps allocated capacity.
|
||||
|
||||
### `Array_Reserve` _func_
|
||||
|
||||
```bux
|
||||
func Array_Reserve<T>(self: *Array<T>, minCap: uint) {
|
||||
```
|
||||
|
||||
Ensure capacity is at least `minCap` (does not shrink).
|
||||
|
||||
### `Array_First` _func_
|
||||
|
||||
```bux
|
||||
func Array_First<T>(self: *Array<T>) -> T {
|
||||
```
|
||||
|
||||
First element (bounds-checked if empty).
|
||||
|
||||
### `Array_Last` _func_
|
||||
|
||||
```bux
|
||||
func Array_Last<T>(self: *Array<T>) -> T {
|
||||
```
|
||||
|
||||
Last element (bounds-checked if empty).
|
||||
|
||||
### `Array_Pop` _func_
|
||||
|
||||
```bux
|
||||
func Array_Pop<T>(self: *Array<T>) -> T {
|
||||
```
|
||||
|
||||
Remove and return the last element (bounds-checked if empty).
|
||||
|
||||
### `Array_Contains` _func_
|
||||
|
||||
```bux
|
||||
func Array_Contains<T>(self: *Array<T>, value: T) -> bool {
|
||||
```
|
||||
|
||||
Linear search: true if `value` is present (uses `==`).
|
||||
|
||||
### `Array_IndexOf` _func_
|
||||
|
||||
```bux
|
||||
func Array_IndexOf<T>(self: *Array<T>, value: T) -> int {
|
||||
```
|
||||
|
||||
Index of first equal element, or `-1` if not found.
|
||||
|
||||
### `Array_Extend` _func_
|
||||
|
||||
```bux
|
||||
func Array_Extend<T>(self: *Array<T>, other: *Array<T>) {
|
||||
```
|
||||
|
||||
Append all elements of `other` onto `self`.
|
||||
|
||||
## `Channel`
|
||||
|
||||
_Source: `lib/Channel.bux`_
|
||||
|
||||
### `Channel_SendInt` _func_
|
||||
|
||||
```bux
|
||||
func Channel_SendInt(ch: *Channel<int>, value: int) {
|
||||
```
|
||||
|
||||
Convenience wrappers for common types
|
||||
|
||||
## `Iter`
|
||||
|
||||
_Source: `lib/Iter.bux`_
|
||||
|
||||
### `Array_Iter` _func_
|
||||
|
||||
```bux
|
||||
func Array_Iter<T>(arr: *Array<T>) -> Iter<T> {
|
||||
```
|
||||
|
||||
Create an iterator from an Array
|
||||
|
||||
### `Iter_HasNext` _func_
|
||||
|
||||
```bux
|
||||
func Iter_HasNext<T>(it: *Iter<T>) -> bool {
|
||||
```
|
||||
|
||||
Check if there are more elements
|
||||
|
||||
### `Iter_Next` _func_
|
||||
|
||||
```bux
|
||||
func Iter_Next<T>(it: *Iter<T>) -> T {
|
||||
```
|
||||
|
||||
Get the next element and advance (undefined if HasNext is false)
|
||||
|
||||
### `Iter_Peek` _func_
|
||||
|
||||
```bux
|
||||
func Iter_Peek<T>(it: *Iter<T>) -> T {
|
||||
```
|
||||
|
||||
Peek current element without advancing (undefined if HasNext is false)
|
||||
|
||||
### `Iter_Reset` _func_
|
||||
|
||||
```bux
|
||||
func Iter_Reset<T>(it: *Iter<T>) {
|
||||
```
|
||||
|
||||
Reset iterator to the beginning
|
||||
|
||||
### `Iter_Pos` _func_
|
||||
|
||||
```bux
|
||||
func Iter_Pos<T>(it: *Iter<T>) -> uint {
|
||||
```
|
||||
|
||||
Current position
|
||||
|
||||
### `Iter_Len` _func_
|
||||
|
||||
```bux
|
||||
func Iter_Len<T>(it: *Iter<T>) -> uint {
|
||||
```
|
||||
|
||||
Remaining length
|
||||
|
||||
### `Iter_Count` _func_
|
||||
|
||||
```bux
|
||||
func Iter_Count<T>(it: *Iter<T>) -> uint {
|
||||
```
|
||||
|
||||
Count remaining elements
|
||||
|
||||
### `Iter_Skip` _func_
|
||||
|
||||
```bux
|
||||
func Iter_Skip<T>(it: *Iter<T>, n: uint) {
|
||||
```
|
||||
|
||||
Skip N elements
|
||||
|
||||
### `Iter_Take` _func_
|
||||
|
||||
```bux
|
||||
func Iter_Take<T>(it: *Iter<T>, n: uint) -> Iter<T> {
|
||||
```
|
||||
|
||||
Take first N elements (by limiting len)
|
||||
|
||||
### `Iter_AnyEq` _func_
|
||||
|
||||
```bux
|
||||
func Iter_AnyEq<T>(it: *Iter<T>, value: T) -> bool {
|
||||
```
|
||||
|
||||
True if any remaining element equals value
|
||||
|
||||
### `Iter_AllEq` _func_
|
||||
|
||||
```bux
|
||||
func Iter_AllEq<T>(it: *Iter<T>, value: T) -> bool {
|
||||
```
|
||||
|
||||
True if every remaining element equals value (true if empty)
|
||||
|
||||
### `Iter_Collect` _func_
|
||||
|
||||
```bux
|
||||
func Iter_Collect<T>(it: *Iter<T>) -> Array<T> {
|
||||
```
|
||||
|
||||
Collect remaining elements into a new Array
|
||||
|
||||
### `Iter_Map` _func_
|
||||
|
||||
```bux
|
||||
func Iter_Map<T, U>(it: *Iter<T>, f: func(T) -> U) -> Array<U> {
|
||||
```
|
||||
|
||||
Map each remaining element through f: T → U, collect into Array<U>
|
||||
|
||||
### `Iter_Filter` _func_
|
||||
|
||||
```bux
|
||||
func Iter_Filter<T>(it: *Iter<T>, pred: func(T) -> bool) -> Array<T> {
|
||||
```
|
||||
|
||||
Keep remaining elements for which pred returns true
|
||||
|
||||
### `Iter_Fold` _func_
|
||||
|
||||
```bux
|
||||
func Iter_Fold<T, Acc>(it: *Iter<T>, init: Acc, f: func(Acc, T) -> Acc) -> Acc {
|
||||
```
|
||||
|
||||
Left-fold: f(f(...f(init, x0), x1), ...)
|
||||
|
||||
### `Iter_ForEach` _func_
|
||||
|
||||
```bux
|
||||
func Iter_ForEach<T>(it: *Iter<T>, f: func(T) -> int) {
|
||||
```
|
||||
|
||||
Call f for each remaining element (return value of f is ignored)
|
||||
|
||||
### `Iter_Any` _func_
|
||||
|
||||
```bux
|
||||
func Iter_Any<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool {
|
||||
```
|
||||
|
||||
True if any remaining element satisfies pred
|
||||
|
||||
### `Iter_All` _func_
|
||||
|
||||
```bux
|
||||
func Iter_All<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool {
|
||||
```
|
||||
|
||||
True if all remaining elements satisfy pred (true if empty)
|
||||
|
||||
### `Iter_SumInt` _func_
|
||||
|
||||
```bux
|
||||
func Iter_SumInt(it: *Iter<int>) -> int {
|
||||
```
|
||||
|
||||
Sum remaining ints (specialized fold)
|
||||
|
||||
## `Json`
|
||||
|
||||
_Source: `lib/Json.bux`_
|
||||
|
||||
### `JsonValue` _struct_
|
||||
|
||||
```bux
|
||||
struct JsonValue {
|
||||
```
|
||||
|
||||
=== Core type ===
|
||||
|
||||
### `Json_Null` _func_
|
||||
|
||||
```bux
|
||||
func Json_Null() -> JsonValue {
|
||||
```
|
||||
|
||||
=== Constructors ===
|
||||
|
||||
### `Json_ArrayLen` _func_
|
||||
|
||||
```bux
|
||||
func Json_ArrayLen(v: JsonValue) -> uint {
|
||||
```
|
||||
|
||||
=== Array helpers ===
|
||||
|
||||
### `Json_ObjectLen` _func_
|
||||
|
||||
```bux
|
||||
func Json_ObjectLen(v: JsonValue) -> uint {
|
||||
```
|
||||
|
||||
=== Object helpers ===
|
||||
|
||||
### `Json_IsNull` _func_
|
||||
|
||||
```bux
|
||||
func Json_IsNull(v: JsonValue) -> bool {
|
||||
```
|
||||
|
||||
=== Accessors ===
|
||||
|
||||
### `JsonParser` _struct_
|
||||
|
||||
```bux
|
||||
struct JsonParser {
|
||||
```
|
||||
|
||||
=== Parser ===
|
||||
|
||||
### `Json_Parse` _func_
|
||||
|
||||
```bux
|
||||
func Json_Parse(s: String) -> JsonValue {
|
||||
```
|
||||
|
||||
=== Public parser ===
|
||||
|
||||
### `Json_StringifyImpl` _func_
|
||||
|
||||
```bux
|
||||
func Json_StringifyImpl(sb: *StringBuilder, v: JsonValue) {
|
||||
```
|
||||
|
||||
=== Serializer ===
|
||||
|
||||
## `Map`
|
||||
|
||||
_Source: `lib/Map.bux`_
|
||||
|
||||
### `Map_Remove` _func_
|
||||
|
||||
```bux
|
||||
func Map_Remove<K, V>(m: *Map<K, V>, key: K) -> bool {
|
||||
```
|
||||
|
||||
Remove key if present. Rebuilds the table to keep open-addressing correct.
|
||||
|
||||
## `Net`
|
||||
|
||||
_Source: `lib/Net.bux`_
|
||||
|
||||
### `Net_Create` _func_
|
||||
|
||||
```bux
|
||||
func Net_Create() -> int {
|
||||
```
|
||||
|
||||
Create a TCP socket. Returns -1 on error.
|
||||
|
||||
### `Net_SetReuse` _func_
|
||||
|
||||
```bux
|
||||
func Net_SetReuse(fd: int) -> bool {
|
||||
```
|
||||
|
||||
Enable SO_REUSEADDR on a socket.
|
||||
|
||||
### `Net_Bind` _func_
|
||||
|
||||
```bux
|
||||
func Net_Bind(fd: int, addr: String, port: int) -> bool {
|
||||
```
|
||||
|
||||
Bind a socket to an address and port.
|
||||
|
||||
### `Net_Listen` _func_
|
||||
|
||||
```bux
|
||||
func Net_Listen(fd: int, backlog: int) -> bool {
|
||||
```
|
||||
|
||||
Start listening for connections.
|
||||
|
||||
### `Net_Accept` _func_
|
||||
|
||||
```bux
|
||||
func Net_Accept(fd: int) -> int {
|
||||
```
|
||||
|
||||
Accept a connection. Returns new fd or -1 on error.
|
||||
|
||||
### `Net_Connect` _func_
|
||||
|
||||
```bux
|
||||
func Net_Connect(fd: int, addr: String, port: int) -> bool {
|
||||
```
|
||||
|
||||
Connect to a remote address and port.
|
||||
|
||||
### `Net_Send` _func_
|
||||
|
||||
```bux
|
||||
func Net_Send(fd: int, data: String) -> int {
|
||||
```
|
||||
|
||||
Send data. Returns bytes sent or -1 on error.
|
||||
|
||||
### `Net_Recv` _func_
|
||||
|
||||
```bux
|
||||
func Net_Recv(fd: int, maxLen: int) -> String {
|
||||
```
|
||||
|
||||
Receive up to maxLen bytes. Returns empty string on error/EOF.
|
||||
|
||||
### `Net_Close` _func_
|
||||
|
||||
```bux
|
||||
func Net_Close(fd: int) -> bool {
|
||||
```
|
||||
|
||||
Close a socket.
|
||||
|
||||
### `Net_LastError` _func_
|
||||
|
||||
```bux
|
||||
func Net_LastError() -> String {
|
||||
```
|
||||
|
||||
Get last socket error as a string.
|
||||
|
||||
## `Option`
|
||||
|
||||
_Source: `lib/Option.bux`_
|
||||
|
||||
### `Option_Expect` _func_
|
||||
|
||||
```bux
|
||||
func Option_Expect(o: Option, msg: String) -> int {
|
||||
```
|
||||
|
||||
Unwrap Some or panic with a custom message
|
||||
|
||||
### `Option_Or` _func_
|
||||
|
||||
```bux
|
||||
func Option_Or(o: Option, other: Option) -> Option {
|
||||
```
|
||||
|
||||
If o is Some return it, otherwise return other
|
||||
|
||||
## `Os`
|
||||
|
||||
_Source: `lib/Os.bux`_
|
||||
|
||||
### `Os_Exit` _func_
|
||||
|
||||
```bux
|
||||
func Os_Exit(code: int) {
|
||||
```
|
||||
|
||||
Terminate the process with the given exit code
|
||||
|
||||
## `Result`
|
||||
|
||||
_Source: `lib/Result.bux`_
|
||||
|
||||
### `Result_Expect` _func_
|
||||
|
||||
```bux
|
||||
func Result_Expect(r: Result, msg: String) -> int {
|
||||
```
|
||||
|
||||
Unwrap Ok or panic with a custom message
|
||||
|
||||
### `Result_UnwrapErr` _func_
|
||||
|
||||
```bux
|
||||
func Result_UnwrapErr(r: Result) -> String {
|
||||
```
|
||||
|
||||
Extract Err payload (panics if Ok)
|
||||
|
||||
### `Result_Or` _func_
|
||||
|
||||
```bux
|
||||
func Result_Or(r: Result, other: Result) -> Result {
|
||||
```
|
||||
|
||||
If r is Ok return it, otherwise return other
|
||||
|
||||
## `Set`
|
||||
|
||||
_Source: `lib/Set.bux`_
|
||||
|
||||
### `Set_Remove` _func_
|
||||
|
||||
```bux
|
||||
func Set_Remove<T>(s: *Set<T>, value: T) -> bool {
|
||||
```
|
||||
|
||||
Remove value if present. Rebuilds the table to keep open-addressing correct.
|
||||
|
||||
## `String`
|
||||
|
||||
_Source: `lib/String.bux`_
|
||||
|
||||
### `String_Len` _func_
|
||||
|
||||
```bux
|
||||
func String_Len(s: String) -> uint {
|
||||
```
|
||||
|
||||
Byte length of a C string (`strlen`).
|
||||
|
||||
### `String_IsEmpty` _func_
|
||||
|
||||
```bux
|
||||
func String_IsEmpty(s: String) -> bool {
|
||||
```
|
||||
|
||||
True if the string has zero length.
|
||||
|
||||
### `String_IsNull` _func_
|
||||
|
||||
```bux
|
||||
func String_IsNull(s: String) -> bool {
|
||||
```
|
||||
|
||||
True if the pointer is null.
|
||||
|
||||
### `String_Eq` _func_
|
||||
|
||||
```bux
|
||||
func String_Eq(a: String, b: String) -> bool {
|
||||
```
|
||||
|
||||
Lexicographic equality.
|
||||
|
||||
### `String_Concat` _func_
|
||||
|
||||
```bux
|
||||
func String_Concat(a: String, b: String) -> String {
|
||||
```
|
||||
|
||||
Allocate and return `a` concatenated with `b`.
|
||||
|
||||
### `String_Copy` _func_
|
||||
|
||||
```bux
|
||||
func String_Copy(s: String) -> String {
|
||||
```
|
||||
|
||||
Heap-copy of `s`.
|
||||
|
||||
### `String_StartsWith` _func_
|
||||
|
||||
```bux
|
||||
func String_StartsWith(s: String, prefix: String) -> bool {
|
||||
```
|
||||
|
||||
True if `s` begins with `prefix`.
|
||||
|
||||
### `String_EndsWith` _func_
|
||||
|
||||
```bux
|
||||
func String_EndsWith(s: String, suffix: String) -> bool {
|
||||
```
|
||||
|
||||
True if `s` ends with `suffix`.
|
||||
|
||||
### `String_Contains` _func_
|
||||
|
||||
```bux
|
||||
func String_Contains(s: String, substr: String) -> bool {
|
||||
```
|
||||
|
||||
True if `substr` occurs anywhere in `s`.
|
||||
|
||||
### `String_IsBlank` _func_
|
||||
|
||||
```bux
|
||||
func String_IsBlank(s: String) -> bool {
|
||||
```
|
||||
|
||||
True if empty or only whitespace (space, tab, CR, LF).
|
||||
|
||||
### `String_Repeat` _func_
|
||||
|
||||
```bux
|
||||
func String_Repeat(s: String, count: uint) -> String {
|
||||
```
|
||||
|
||||
Repeat `s`, `count` times (`count == 0` → empty string).
|
||||
|
||||
### `String_ReplaceAll` _func_
|
||||
|
||||
```bux
|
||||
func String_ReplaceAll(s: String, old: String, new: String) -> String {
|
||||
```
|
||||
|
||||
Replace every non-overlapping occurrence of `old` with `new`.
|
||||
Empty `old` is a no-op (returns `s` unchanged). Safe if `new` contains `old`.
|
||||
|
||||
## `Test`
|
||||
|
||||
_Source: `lib/Test.bux`_
|
||||
|
||||
### `Test_Exit` _func_
|
||||
|
||||
```bux
|
||||
func Test_Exit(code: int) {
|
||||
```
|
||||
|
||||
Exit the process with `code` (for test runners).
|
||||
|
||||
### `Test_Assert` _func_
|
||||
|
||||
```bux
|
||||
func Test_Assert(cond: bool) {
|
||||
```
|
||||
|
||||
Assert `cond` is true; abort on failure.
|
||||
|
||||
### `Test_AssertEqInt` _func_
|
||||
|
||||
```bux
|
||||
func Test_AssertEqInt(a: int, b: int) {
|
||||
```
|
||||
|
||||
Assert two ints are equal; print both values and exit 1 on mismatch.
|
||||
|
||||
### `Test_AssertNeqInt` _func_
|
||||
|
||||
```bux
|
||||
func Test_AssertNeqInt(a: int, b: int) {
|
||||
```
|
||||
|
||||
Assert two ints differ.
|
||||
|
||||
### `Test_AssertEqString` _func_
|
||||
|
||||
```bux
|
||||
func Test_AssertEqString(a: String, b: String) {
|
||||
```
|
||||
|
||||
Assert two strings are equal (`String_Eq`).
|
||||
|
||||
### `Test_AssertEqBool` _func_
|
||||
|
||||
```bux
|
||||
func Test_AssertEqBool(a: bool, b: bool) {
|
||||
```
|
||||
|
||||
Assert two bools are equal.
|
||||
|
||||
### `Test_AssertTrue` _func_
|
||||
|
||||
```bux
|
||||
func Test_AssertTrue(cond: bool) {
|
||||
```
|
||||
|
||||
Assert `cond` is true.
|
||||
|
||||
### `Test_AssertFalse` _func_
|
||||
|
||||
```bux
|
||||
func Test_AssertFalse(cond: bool) {
|
||||
```
|
||||
|
||||
Assert `cond` is false.
|
||||
|
||||
### `Test_Fail` _func_
|
||||
|
||||
```bux
|
||||
func Test_Fail(msg: String) {
|
||||
```
|
||||
|
||||
Fail the test with a message and exit 1.
|
||||
|
||||
### `Test_Pass` _func_
|
||||
|
||||
```bux
|
||||
func Test_Pass(msg: String) {
|
||||
```
|
||||
|
||||
Print a PASS line (for human-readable runners / goldens).
|
||||
|
||||
Reference in New Issue
Block a user