- Type `?`/`!` as Result/Option Ok payload (not always int); fix unwrap C types - LSP 0.18 document formatting (bux fmt) + VS Code format-on-save - Macro `:type` generics (Array_New<$t>) and operators-only tt paste - Ship runtime_freestanding.c + BUX_RUNTIME=freestanding + smokes/examples
75 KiB
Bux — План към „добър“ език (v0.5 → v1.0.0 ✅)
Дата: 2026-07-27
Текущо: v1.0.0 — language freeze (session 88 / release)
Цел: Език, с който се пишат реални проекти комфортно, безопасно (по избор) и с надежден toolchain.
Платформен фокус: Linux (primary) · cloud-native (servers, containers, HTTP) · embedded (cross, freestanding-ish, CTFE).
Не-цел: MS Windows като product platform (исторически CI/hello smoke остават; няма roadmap investment).
Release notes:docs/RELEASE_v1.0.0.md· Semver:docs/SEMVER.md(active).
Диагноза (къде сме)
| Слой | Състояние | Оценка |
|---|---|---|
| Frontend (lex/parse) | Пълен Pratt parser, recovery | ★★★★☆ |
| 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], move, Drop, elision, field-move + remaining-field Drop |
★★★★★ |
| Concurrency | M:N tasks + channels + async | ★★★★☆ |
| Stdlib | Array/Map/Set/String/Iter HOF разширени | ★★★★☆ |
| Tooling | LSP 0.5 hover/def/outline/refs/rename + fmt/test/doc | ★★★★★ |
| Ecosystem / registry | path+git + file + HTTP index (bux search/add) |
★★★★☆ |
| Документация | README + QUALITY_PLAN синхронизирани (2026-07-15) | ★★★★☆ |
Силна ниша: gradual ownership (C-скорост на писане + opt-in Rust-safety).
Слабо място: ergonomics на stdlib + maturity на tooling + пълнота на borrow checker.
Какво значи „добър“ за Bux
- Ежедневен DX — колекции, string, assert, грешки, които разбираш за секунди.
- Предвидима безопасност —
@[Checked]да хваща 80% от UAF/double-borrow без lifetime hell. - Selfhost като dogfood — компилаторът и apps (
nexus,boko) са proof. - Инструменти — fmt, test, LSP, package install без ръчна магия.
- Стабилна спецификация — LanguageRef = реалното поведение.
- Целеви среди — Linux servers/containers, cloud HTTP services, embedded/cross (ARM/RISC-V), не desktop Windows.
Не целим „по-добър Rust“. Целим единствения език с gradual safety + Go-стил concurrency без GC, удобен за cloud + systems на Linux.
Фази
A — Ergonomics & Stdlib (P0) ✅ (core done)
| # | Задача | Защо | Статус |
|---|---|---|---|
| A.1 | Array: Pop, Clear, IsEmpty, First, Last, Cap, Reserve | Без това колекциите са неудобни | ✅ (тази сесия) |
| A.2 | String: IsEmpty, ReplaceAll | Чести операции; само first-replace досега | ✅ (тази сесия) |
| A.3 | Os_Exit + Test_AssertEqString / richer asserts | Тестове и CLI без raw bux_exit |
✅ (тази сесия) |
| A.4 | Map_Remove / Set polish | Completeness на колекциите | ✅ (тази сесия) |
| A.5 | Iter: map/filter/fold върху closures | Higher-order без boilerplate | ✅ generic Iter_Map/Filter/Fold + Int aliases |
| A.6 | Result helpers: Expect, UnwrapErr, Or | По-малко match boilerplate | ✅ (тази сесия) |
B — Compiler Correctness (P0)
| # | Задача | Защо | Статус |
|---|---|---|---|
| B.1 | Proper tuple types в C backend | (T,U) → Tuple_T_U struct + .0/.1 |
✅ bootstrap + selfhost |
| B.2 | Function pointer types | func(T)->U fat ABI |
✅ bootstrap + selfhost |
| B.3 | Match expression lowering (literals, ranges, enums) | Expression-context match → if-else | ✅ bootstrap + selfhost |
| B.3b | Pattern bindings (Some(value) => value) |
Payload idents bound in arm body | ✅ bootstrap + selfhost |
| B.3c | Match arm guards (p if cond => …) |
Bindings visible in guard; sequential found-flag lower | ✅ bootstrap + selfhost |
| B.4 | Closures multi-instance | Fat BuxFn + heap env |
✅ bootstrap + selfhost |
| B.4b | Closures: || empty params + loop/return body |
Lexer || vs empty closure; while/break/return |
✅ bootstrap + selfhost |
| B.5 | По-добри diagnostics (snippet + hint) | DX #1 за нови потребители | ✅ |
| B.6 | Bootstrap ↔ selfhost feature parity | empty ||, match-as-expr, string interp f"..." bootstrap+selfhost |
✅ |
C — Gradual Ownership 2.0 (P1)
| # | Задача | Защо | Статус |
|---|---|---|---|
| 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 | ✅ full (docs + Release wins + tests + example) |
D — Tooling (P1)
| # | Задача | Защо | Статус |
|---|---|---|---|
| D.1 | LSP: hover, go-to-def, diagnostics | IDE = adoption | ✅ v0.11.0: + interface dispatch hierarchy |
| 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 |
✅ local index + HTTP(S) URL cache + file/git + search |
| E.2 | 3–5 production-quality apps в apps/ |
Showcase | ✅ 4 apps + make test-apps smoke (build + CLI) |
| E.3 | Language freeze + semver policy | Trust | ✅ v1.0.0 + active docs/SEMVER.md |
| E.4 | Debugger/DWARF basics | Systems audience | ✅ #line→.bux + -g / --release; make test-dwarf |
| E.5 | Benchmarks vs C/Zig/Nim (micro + nexus) | Marketing + regression | ✅ micro + C/Nim/Zig twins + make bench-nexus (wrk) |
Препоръчан ред на работа
A (stdlib ergonomics) → B (compiler holes) → C (ownership depth)
↓ ↓
D (tooling) ←────────── dogfood apps
↓
E (v1.0 ecosystem)
Правило: всяка сесия ship-ва нещо runnable (stdlib API, fix, example), не само docs.
Acceptance criteria за „добър v1.0“ — met; tagged v1.0.0
- Всички examples + apps + selfhost smoke на CI (
make testvia.github/workflows/ci.yml); selfhost-loop optional - Array/Map/String/Test API покрива 90% от ежедневните нужди (+ Insert/Remove/Clone/case/GetOr)
@[Checked]хваща use-after-move + double&mut+ dangling return / elision failbux test+bux fmt+bux checkса default developer loop (--filter/--checkshipped)- LanguageRef синхронизиран с компилатора (incl. C.1 elision)
- Поне един външен/temp проект build-ва с registry dep (
tools/smoke_registry.sh+ HTTP) - Version strings + SEMVER active +
docs/RELEASE_v1.0.0.md(session 88)
Сесия 1 (stdlib ergonomics)
Array_Pop,Array_Clear,Array_IsEmpty,Array_First,Array_Last,Array_Cap,Array_ReserveString_IsEmpty,String_ReplaceAllOs_ExitTest_AssertEqString,Test_AssertNeqInt,Test_AssertEqBool- Example + docs update
Сесия 2 (collections + tuples)
Map_Remove/Map_Clear/Map_IsEmpty(+ StringMap)Set_Remove/Set_Clear/Set_IsEmptyResult_Expect/Result_UnwrapErr/Result_OrOption_Expect/Option_Or- Tuples:
(T, U)→typedef struct { T _0; U _1; } Tuple_T_U+ field access.0/.1 - Examples:
tuples,func_ptr,map_remove
Сесия 3 (diagnostics + collections)
- Rust-style errors in bootstrap CLI:
--> file:line:col, source snippet,^caret,= help:hints SourceLocation.filepropagated from lexer- Better caret for type-mismatch on
let(points at initializer) Array_Contains/Array_IndexOf/Array_ExtendIter_AnyEq/Iter_AllEq/Iter_Collect- Selfhost
Diagnostic_Hintfor common messages
Сесия 4 (diagnostics depth + LSP + strings)
- Multi-char underlines (
^^^^^^^under tokens/strings/idents) - Quoted-name highlighting for
undeclared identifier 'x' - Golden error tests (
tests/error_golden/,make test-errors) - LSP runs
buxc checkand publishes real diagnostics String_IsBlank/String_Repeat
Сесия 5 (multi-instance closures)
- Fat function pointers for all
func(...)types:BuxFn { code(env, args...), env } - Capturing closures: heap-allocate env per creation site (independent instances)
- Capture-less closures + named funcs: adapters with
env = NULL - Calls through func values:
f.code(f.env, args...) - Example
multi_closure.bux— MakeAdder(10)/MakeAdder(20) yield 11 and 21 - Selfhost parity: same fat ABI in
src/hir_lower.bux+src/c_backend.bux(makers, adapters)
Сесия 6 (selfhost tuples)
- Parser:
(T, U)types,(a, b)exprs, field access.0/.1 - Sema: tekTuple / ekTuple
- HIR lower →
hStructInitofTuple_int_int - C backend:
typedef struct Tuple_int_int { int _0; int _1; } - Verified with
buxc2onexamples/tuples.bux
Сесия 7 (Iter HOF)
Iter_MapInt/FilterInt/FoldInt/ForEachInt/AnyInt/AllInt/SumInt- Named funcs + capturing closures (fat ABI)
- Example
iter_hof.bux(sum=15, product=120) - Selfhost fix: pointer
->field access; no bogus bounds check onarr.data[len](Push)
Сесия 8 (match expression + error goldens)
- Match expression lowering (B.3):
pkLiteral,pkRange(a..b/a..=b), enum tags, wildcard- Bugfix: literal arms were always-true (
elsebranch) →match 1 { 1=>10, 2=>20 }returned 10 for all skMatchnow also lowers vialowerMatch(void result)
- Bugfix: literal arms were always-true (
- Expanded
examples/pattern_matching.bux(enums + classify ranges + string arms) - Error golden cases (
tests/error_golden/):parse_error— incompletelet x = ;use_after_move—@[Checked]+own Stringdouble_mut_borrow— two&mutof same var
run.shnormalize: absolute paths in "parse errors in …" lines
Сесия 9 (selfhost match — B.6 parity)
- AST:
MatchArmlinked list;Pattern.patChild1/2for ranges;Expr.matchArms - Parser: full
parserParsePattern/parserParseMatchExpr(was skip-arms stub)- literals,
_, ident,Enum::Variant/Enum::Variant(...), ranges - statement
match→skExpr+ekMatch
- literals,
- Sema: type-check subject + arms; propagate first-arm type to
expr.refType - HIR lower:
Lcx_LowerMatch→ alloca result + if-else stores (enum.tag/ simple / literal / range) - Last-expr return:
Lcx_LowerBlockconverts finalskExprintoreturn(needed forfunc F() -> T { match ... }) - Verified:
pattern_matchingvia buxc2; simple enum + ranges; selfhost-loop IDENTICAL ✓
Сесия 10 (pattern bindings — B.3b)
- Bootstrap sema:
extractPatternBindingsresolves enum payload field types from variant decl - Bootstrap HIR:
matchPatternBindings→alloca name; name = subject.data.Variant_ibefore arm body - Match result type: fall back to
currentFuncRetTypewhen arm bodies only use bindings - Multi-field enum layout: positional
fields.len > 1→ nested struct in_Dataunion (no overlay) - Selfhost: parse
patArgslinked list;Sema_BindPattern;Lcx_PatternBindingsin match arms - Example:
pattern_matching.buxusesOption::Some(value) => value(real binding, notopt.data.Some_0) - Verified: bootstrap + buxc2 + all examples + error goldens + selfhost-loop IDENTICAL ✓
Сесия 11 (empty || closures + match-as-expr — B.4b)
- Bug:
|| -> int { ... }lexed astkPipePipe(logical-or), not twotkPipe→ empty-param closures failed to parse - Fix bootstrap: primary
of tkPipePipe:→ zero-paramekClosure - Fix selfhost:
parserParseEmptyClosurefortkPipePipe - Match-as-expr: expression-form match now skips newlines (same as statement form) →
let x = match n { ... }works - Verified control-flow in closure body: while/break, early return from loop, multi-return if-chain
- Pattern bind reuse: one alloca per binding name per function (
patternBoundNames) so two matches can both usev - Selfhost match-as-expr: let-init + binary operands expand yield blocks (
Lcx_IsMatchYield/__binop_N) - Examples:
closure_control.bux,match_let.bux - Verified: bootstrap + buxc2 + all examples + error goldens + selfhost-loop IDENTICAL ✓
Сесия 12 (string interpolation selfhost — B.6)
- Selfhost parser:
parserParseStringInterp— interleaved lit/expr parts incallArgs, nested fragment parse viaLexer_Tokenize+ sub-parser - Selfhost sema/HIR:
ekStringInterp→String_Concat+String_FromInt/FromBool/FromFloat - Bootstrap fix:
f"plain"no longer keeps thefprefix in the literal;\{/\}preserved by lexer and unescaped by interp parser - Lexer:
\{/\}allowed (bootstrap + selfhost) so LanguageRef escape rules work - Compiler hygiene: original dense
&&/||in the large interp loop caused bootstrap OOM (~27 GB) when compilingparser.bux— rewrite with simpler control flow - Example:
examples/string_interp.bux(name/int/bool/plain/escaped braces) - Verified: bootstrap + buxc2 + all 41 examples + error goldens + selfhost-loop IDENTICAL ✓
Сесия 13 (LSP hover / go-to-def / outline — D.1)
- Richer symbol index:
funcsignatures (params+-> Ret),let/var/constwith types,struct/enum/union/interface/type/module - Skip comments/strings during scan (no false
funchits) - Hover: markdown
bux signature+ kind; accurate word range - Go-to-definition: current file + workspace index (scan
.buxunder rootUri) - Document symbols (outline) via
textDocument/documentSymbol - didChange refreshes symbols immediately; didSave/didOpen still run
buxc checkdiagnostics - Fix: responses write to stdout (was writing to stdin stream → broken pipe)
- Version
bux-lsp0.2.0; smoke-tested via JSON-RPC
Сесия 14 (generic Iter map/filter/fold)
Iter_Map<T,U>/Filter<T>/Fold<T,Acc>/Any/All/ForEach— fatfuncparams + monomorphization- Int aliases keep working:
Iter_MapInt→Iter_Map<int,int>, … - Bootstrap fixes:
- call return type for local fat-func (
f: func(T)->U) after mono (was alwaysint→ String map truncated pointers) - generic call
Foo<T>(…)now type-checks args (closures get capture analysis)
- call return type for local fat-func (
- Selfhost fixes:
Lcx_SubstituteTyperecurses intotekFunc(was leavingBuxFn_U_T)- fat typedef emit covers cstr shapes +
#ifndefguards
- Example:
examples/iter_generic.bux(int↔String map, filter, fold, closures) - Verified: bootstrap + buxc2 + selfhost-loop IDENTICAL ✓
Сесия 15 (struct/tuple patterns)
- Tuple patterns:
match t { (a, b) => a + b }— bindsubject._0/._1 - Struct patterns:
Point { x: px, y: py }+ shorthandPoint { x, y } - Bootstrap:
matchPatternBindingsfor pkTuple/pkStruct; field types from struct decl; range registration of local tuple typedefs - Selfhost: parse
()/Name { … }patterns;Sema_BindPattern+Lcx_PatternBindingswith Scope_Define - Fix: operator-overload path treated
String_Eq(null, "")as non-empty → crash ona + bafter pattern bind - Example:
examples/struct_tuple_pat.bux - Verified: bootstrap + buxc2 + selfhost-loop IDENTICAL ✓
Сесия 16 (match block arms + block expressions)
- Block-as-expression:
let r = { let x = 1; x + 2 }— last skExpr is the value - Multi-stmt match arms:
1 => { PrintLine("…"); let a = 10; a + 1 } - Bootstrap:
lowerBlock(..., asExpr)promotes last expression; nested enum/struct pattern bindings - Selfhost: parse
{ … }asekBlock; yield temps__blk_N; fixIsMatchYieldnull-strValue false positive; retTypeKind-2for expr blocks - Nested:
Shape::Dot(Point { x, y })works on bootstrap; selfhost covers struct/tuple/enum + block arms - Example:
examples/match_block.bux - Verified: bootstrap + buxc2 + selfhost-loop IDENTICAL ✓
Сесия 17 (deeper nested patterns + multi-field enum layout)
- Multi-field payload type: nested struct
Enum_Variant_Payload(suffix avoids clash with tagEnum_Variant) - Bootstrap: full
resolveTypeExprfor enum field types (tuples/pointers); sema synthetic field lookup on payload types;data.Two.Two_0construction - LIR C backend: emit tuple typedefs before enums that embed them; topo deps for
*_Payload - Selfhost: enum variants parse full types (
parserParseType) — fixesVal((int,int)); storefieldTypeName*; structs before enums (e.g.Dot(Point)) - Patterns:
Pair::Two(a, b),Box::Val((a, c)),Shape::Dot(Point { x, y })+ block arms - Example:
examples/nested_patterns.bux - Verified: bootstrap + buxc2 + selfhost-loop IDENTICAL ✓
Сесия 18 (match arm guards — B.3c)
- Syntax:
p if cond => body(also after ranges:1..10 if x % 2 == 0) - Parser fix (critical):
isTypeArgListAheadtreatedx < 0as generic when a laterx > 0existed in another arm → infinite parse. Stop lookahead on=>, keywords, literals, arithmetic, comparisons. - Sema: bind inner pattern first; type-check guard as bool in arm scope
- HIR lower: sequential
foundflag (no shared if-else DAG):Bindings are in scope for the guard expression.if (!found) { if (inner_cond) { binds; if (guard) { result = body; found = true; } } } - Selfhost:
pkGuarded+patGuardExpr; same lower strategy; fixreturn match {…}to expand yield block before return - Example:
examples/match_guards.bux(ident/literal/range/enum payload guards) - Verified: bootstrap + buxc2 + all examples + error goldens + selfhost-loop IDENTICAL ✓
Сесия 19 (generic HOF type inference)
- Bootstrap
inferTypeArgs: structural unify of param TypeExpr vs arg Type*Iter<T>/*Array<T>→ extract T from pointee type args or mangledArray_intfunc(T)->U→ bind T/U from function-value type (not whole func as T)- bare
Accfrom init; multi-paramIter_Fold<T,Acc>
- Selfhost: improved
Sema_InferGenericArgs+ return-type subst after inference- Fix:
ekCastmust type-check operand (was skipping → no inference underas) - HIR fallback mono from first *Array/*Iter arg when count=0
- Fix:
- Works without explicit type args:
Array_Push(&nums, 1),Array_Get,Array_Len,Array_IterIter_Map(&it, f),Iter_Filter,Iter_Fold,Iter_Any(int↔String)
- Example:
examples/generic_infer_hof.bux - Verified: bootstrap + buxc2 + all examples + selfhost-loop IDENTICAL ✓
Сесия 20 (LSP hover from real sema — D.1)
bux-lsp0.3.0 links bootstrap (--path:../bootstrap) and runsanalyzeFullon open/save- typeIndex: global scope (stdlib + file) → hover signatures with real types
- File-local priority: user decls override stdlib name collisions (
Max<T>vsMath.Max) - Locals: walk function bodies for
let/varwith explicit type annotations - didChange: fast lightweight rescan; keeps previous typeIndex until save/hover refresh
- Hover shows
bux signature+_kind_ · sema - Smoke:
Main() -> int,PrintLine(String) -> void,Max<T>(a: T, b: T) -> T
Сесия 21 (pattern binding shadowing)
- Problem: C/LIR function-scoped locals — nested
Some(n) => match … Some(n)emitted store beforeint n, andlet v+ patternvcaused redeclaration. - Bootstrap: every pattern binding → unique C name
__pN_srcviapatternRenamesmap; body/guard idents rewritten; binds before body lower insidelowerMatch. - Selfhost: same model —
Lcx_BindPatIdent+patMapFrom/Torename table; arm-scoped push/pop of map. - Semantics: nested pattern name shadows correctly; outer
let vsurvives after match that bindsv. - Example:
examples/pattern_shadow.bux - Verified: bootstrap + buxc2 + all examples + selfhost-loop IDENTICAL ✓
Сесия 22 (Ownership 2.0 — C.2 + C.4 + *p= fix)
- C.2 Exclusive &mut data-flow (
@[Checked]):- Track long-lived let-bound borrows (
activeMutBorrows/activeSharedBorrows) - Reject: second
&mut x, use/assign ofxwhile mutably borrowed,&xwhile&mutlive - Call-site temps conflict with existing let-bound borrows
- Track long-lived let-bound borrows (
- C.4 Golden tests:
tests/error_golden/exclusive_mut_let/tests/error_golden/use_while_mut_borrow/
- Bugfix:
*p = exprnow stores through the pointer (was assigning to a temp) — ownership examples finally mutate correctly - Example:
examples/ownership_checked.bux(unchecked zero-cost + checked OK path) - Verified: 7 error goldens + borrow_test + all examples + selfhost-loop
Сесия 23 (Ownership 2.0 — C.3 auto-drop early return / branches)
- Bootstrap auto-drop for
@[Drop]+ collections:- Parser:
@[Drop]/@[Release]on structs and funcs (declAttrs) autoDropFuncName+ monomorphizeArray_Drop/Free(etc.) so stdlib links- Inject
Type_Drop(&x)onletviadeferStmts
- Parser:
- Early return / multi-path:
- Every
returnsnapshots the full live defer stack (no clear-after-first-return) - Materialize return value before Drop (
return a.idis not use-after-drop) - Move-on-return: skip Drop for a local returned by value (
return out)
- Every
- Branch / loop scopes:
- Bootstrap:
lowerBlockscopesdeferStmts— branch-local drops at block exit; siblings do not see each other - Selfhost C backend:
CBE_EmitDeferskeeps stack for multi-return;CBE_EmitAndPopDefersFrompops branch/loop locals afterif/while/loop
- Bootstrap:
- Selfhost fixes: null-safe ret type; temp name counter; use function
retTypeNamefor__retdrop_N - Example:
examples/drop_early_return.bux(Early + Branched + Scoped → 5 drops) - Verified: bootstrap + buxc2 drop tests, 7 error goldens, key examples, selfhost-loop IDENTICAL ✓
Сесия 24 (tooling — D.2 fmt --check + D.3 test --filter)
- Bootstrap
bux fmt(bootstrap/fmt.nim):- Indent-by-brace-depth formatter (parity with
src/fmt.bux) bux fmt [path...]writes;bux fmt --checkexits 1 if any file would change- Collects single file or recursive
.buxunder directories
- Indent-by-brace-depth formatter (parity with
- Bootstrap
bux test --filter:--filter <s>/--filter=<s>— only runtests/*.buxwhose name containss- Summary table (
PASS/FAIL[:code]) +Results: N passed, M failed, T total - Exit
0all pass,1failures or no match
- 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
- CI hooks:
make fmt-checksmoke (clean→0, dirty→1); full-tree enforce deferred until a one-shot format pass onlib//examples/ - Idempotence fix: drop trailing split-empty so re-format is a no-op
- Verified: unit suite +
./buxc test --filter first _test_runner+ selfhostbuxc2fmt/test parity
Сесия 25 (Ownership 2.0 — C.1 lifetime elision)
- Elision rules in
@[Checked](bootstrap/sema.nim):- Each elided input
&/&mut→ distinct#elidedN - One input lifetime → assigned to elided return
- First param
self/Selfpreferred when multiple inputs - Multiple inputs + elided return →
lifetime elision failed(need'a)
- Each elided input
- 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
'amismatch between return and value
- Body check for lifetime-only generics (
func F<'a>(...)) — no longer skipped - Diagnostics hints for elision / dangling / mismatch
- Tests: 8 new borrow_test cases; goldens
return_local_ref,elision_multi_input - Example:
examples/lifetime_elision.bux(Identity / explicit / ViaLet / self) - LanguageRef + QUALITY_PLAN updated
- Verified: borrow_test 24/24, 9 error goldens, example runs
Сесия 26 (C.1 selfhost parity)
- Lexer (
src/lexer.bux+tkLifetime=111):'avs char'x'(same heuristic as bootstrap) - Parser:
&'a T/&'a mut T→TypeExpr.refLifetimefunc F<'a, T>(…)— lifetime params accepted and skipped for mono slots
- Sema lifetime elision (fixed 8-slot maps, same rules as bootstrap):
- single-input elision,
selfpreference, multi-input fail - return-local / no-input-ref / explicit mismatch
- let-bound ref lifetime propagation
- single-input elision,
- Fixed
checkFuncelse-branch that wipedcheckedFuncwhen retType was void - Verified:
buxc2 run lifetime_elisionPASS; goldens on buxc2 show same errors; bootstrap still green; selfhost-loop expected IDENTICAL
Сесия 27 (tooling — D.4 bux doc + D.5 stdlib goldens)
- D.5 Stdlib goldens (
tests/stdlib_golden/):- Packages:
array,string,collections(Map/Set/Result/Option) run.shbuilds viabuxc runand matches expected PASS linesmake test-stdlibwired intomake test
- Packages:
- D.4
bux doc:- Bootstrap:
bootstrap/docgen.nim—///+ adjacent/* */ - Selfhost:
Cli_Docline scanner for/// bux doc [--out file] [path](default pathlib/)make docs→docs/api/stdlib.md
- Bootstrap:
- Stdlib docs:
///on Array / String / Test public helpers - Verified:
make test-stdlib,./buxc doc lib/Array.bux | head, selfhost build
Сесия 28 (LSP v0.4.0 — position-sensitive locals + inferred lets)
LocalBindingwith scope range (scopeStartLine…scopeEndLine) per let/param- Sema-backed inference (
checkExprForLsp/resolveType):let x = 42→ hoverlet x: int· inferredlet s: String = "…"→ annotated, not inferred- params:
param a: intvisible for whole function
- Position-sensitive hover / go-to-def / completion (innermost scope wins on shadowing)
- Nested scopes: if/while/for/match/block arms
- Version bux-lsp 0.4.0; tests:
tools/test_lsp_locals.nim,tools/smoke_lsp_hover.sh - Verified: hover shows
let sum: int · inferred,param a: int,let n: int · inferred
Сесия 29 (full-tree bux fmt + CI enforce)
- One-shot format of
lib/(33),examples/(23),src/(15),tests/(8),apps/(12) - Idempotent: second
--check→ 0 would reformat on all trees - CI:
make fmt-checkenforces full tree + dirty-path smoke (exit 1) make fmthelper to reformat the same roots- Verified:
test-stdlib, key examples, selfhost + selfhost-loop IDENTICAL ✓
Сесия 30 (E.1 package registry + E.3 semver draft)
- Registry index (
config/registry.toml,$BUX_REGISTRY,~/.bux/registry.toml)[[package]]withname/version/source/descriptionfile:/path:(relative to index) or git URL
- CLI:
bux search [q],bux add <name>resolves registry,bux installlocks path/git - Demo package:
registry/packages/greet(Greet_Hello,Greet_Version) - Smoke:
tools/smoke_registry.sh/make test-registry— temp app outside tree - Semver policy:
docs/SEMVER.md(0.x vs 1.0, registry version match) - Packages.md updated
Сесия 31 (E.1b HTTP registry + E.2 apps smoke + E.5 micro-bench)
- HTTP registry index (
bootstrap/registry.nim):$BUX_REGISTRYacceptshttp:///https://URLs- Fetch via
curl(fallbackwget) →~/.bux/cache/registry_http.toml BUX_REGISTRY_REFRESH=1forces re-download; cache keyed by URL meta filebux searchshows URL + cached path; clear errors on fetch failure
- Smoke:
tools/smoke_registry.sh— local path flow + pythonhttp.serverHTTP search - E.2 apps smoke (
tools/smoke_apps.sh/make test-apps):- Build
simpledb,jwt-pitbul,nexus,boko-framework - simpledb set/get/has/count/del; jwt-pitbul sign/verify/decode
- Removed obsolete JWT-disable workaround from simpledb README
- Build
- E.5 micro-benchmarks (
benches/micro,benches/c,make bench):- Bux:
int_loop,fib30,string_concat,array_push - C refs (
gcc -O2):fib30,int_loopfor relative comparison
- Bux:
- Docs:
Packages.md,config/registry.toml,benches/README.md - Verified:
make test-registry,tools/smoke_apps.sh,tools/bench.sh
Сесия 32 (E.5 nexus throughput + language twins)
- Nexus env config (
apps/nexus/src/Main.bux):NEXUS_PORT,NEXUS_WORKERS,NEXUS_BIND,NEXUS_PUBLIC
- Throughput harness (
tools/bench_nexus.sh/make bench-nexus):- Start nexus on
:18080,wrk -t4 -c64 -d5s→/api/health - Sample (this machine): ~44.5k req/s, p50 ~0.94 ms (Connection: close)
- Start nexus on
- Language twins for micro kernels:
benches/nim/fib + int_loop (nim c -d:release)benches/zig/sources (built whenzigis on PATH)make benchruns Bux + C + Nim (+ Zig if present)
- Docs:
benches/README.md, nexus README, BuildAndTest - Verified:
tools/bench.sh,tools/bench_nexus.sh
Сесия 33 (E.4 Debugger / DWARF basics)
- Source map: HIR
loc→ LIRlocLine/locFileon every stmt/expr (setSourceLoc) - C backend: emit
#line N "path.bux"when location changes; force map at each func entry - Build modes:
- default:
cc -O0 -g+#linemaps --release/build --release:-O2 -DNDEBUG, no#line, no-gBUX_CFLAGSappended for custom flags
- default:
- Smoke:
tools/smoke_dwarf.sh/make test-dwarf#linefor stdlib + userMain.bux.debug_infopresent in debug binarygdb list Mainshows real Bux source
- Verified: smoke PASS;
gdb list Main→ hello.bux body
Сесия 34 (LSP refs/rename + CI wiring)
- bux-lsp 0.5.0 (
tools/lsp_server.nim):textDocument/references— scoped locals (same binding vialookupLocalAt) + workspace globalstextDocument/prepareRename+rename→WorkspaceEdit.changes- Ident scan skips strings/comments; keyword rename rejected
- Smoke:
tools/smoke_lsp_rename.sh(sum→total ≥2 edits); wired intomake test-lsp - CI:
make testnow runstest-registry+test-dwarf+test-appsafter stdlib goldens - Verified: rename/hover smokes PASS
Сесия 35 (Nexus HTTP/1.1 keep-alive)
- Server loop (
apps/nexus/src/Server.bux):HandleConnectionserves up to 1000 requests per TCP fdBuildResponse(..., keepAlive)→Connection: keep-alive+Keep-Alive:orclose
- Policy (
RawRequest_WantsKeepAliveon raw bytes):- HTTP/1.1 default keep-alive;
Connection: closeforces close - HTTP/1.0 needs explicit keep-alive
- (Avoided fragile
Array<HeaderEntry>walk — keys corrupted under for-in/Get)
- HTTP/1.1 default keep-alive;
- Bench: ~81k req/s vs ~45k with close-only (
wrk -t4 -c64 -d5s /api/health) - Version banner 0.3.0; README / benches notes updated
- Verified: curl headers +
make bench-nexus
Сесия 36 (header ownership + selfhost flags + LSP workspace/symbol)
- Root cause (Nexus headers UAF): auto-drop of local
headersafter shallow-copy intoHttpRequest/ParseResultfreed the buffer while still referenced. Not Array ABI — move-out-of-field not tracked. - Fix (
apps/nexus/src/Parser.bux): after embedding, zeroheaders.data/len/capso auto-drop is a no-op;Request_WantsKeepAliveagain usesRequestHeader_Getsafely. Bench still ~76–80k RPS. - Selfhost compile flags (
src/cli.bux):- default
-O0 -g(was always-O2) --release→-O2 -DNDEBUG(was-O3 -flto)BUX_CFLAGSappended viabux_getenv#linemaps remain bootstrap-only (selfhost C backend has no LIR #line yet)
- default
- LSP 0.6.0:
workspace/symbolover open docs + workspace index (cap 200);tools/smoke_lsp_workspace.sh+make test-lsp - Verified: nexus keep-alive + header Get;
make lsp+ workspace smoke
Сесия 37 (compiler: field-move skip auto-Drop)
- Root cause (formal): auto-Drop of locals that were moved by value into
a struct field / let / return still ran → UAF (Nexus
headersinHttpRequest). bootstrap/hir_lower.nim:movedOutLocals: HashSet[string]markMovedOutFromAstonekStructInitfields,letinit,returnvalueshouldSkipDropat return / block exit / function tail
- Nexus: removed zeroing workaround; error path still Drops; success path
transfers ownership;
HandleConnectionDropsreq.headersafter response - Example:
examples/move_field.bux(Array intoBox { items }) - Verified: move_field PASS; ParseRequest C has Drop only on error path;
bench-nexus~88k RPS; drop_early_return still 5 drops
Сесия 38 (selfhost field-move + #line)
- Field-move Drop (
src/c_backend.bux):CBE_MarkMovedFromNodeonhStructInit/ return values (nested fields)- Already skipped Drop via
CBE_IsMovedin defer emit
- Struct emit fix (
src/hir_lower.bux):- Field types
Array<int>→Array_int(etc.) so parent structs are not skipped as “generic” (was incompletetypedef struct Box Boxonly)
- Field types
- #line maps (selfhost C backend):
- Emit
#line N "file"on statements whennode.line > 0 BUX_DEBUG_FILEsets the path;BUX_NO_LINE=1disables
- Emit
- Verified: selfhost
move_fieldPASS; Make() has noArray_Drop(&items)after field move;#linepoints at.buxsources
Сесия 39 (LSP 0.7 deeper rename)
- Member index in
analyzeFile:- struct/union/interface fields (
name: Type) - enum variants (
Name/Name(...))
- struct/union/interface fields (
- Access classification for each hit:
- bare /
.member/::Variant/name:field-init
- bare /
- Rename targets:
- local — same binding only (shadowing-safe)
- member — decl +
.x+::Red+{ x: }— not bare locals namedx - global — bare +
::paths; skips.memberfalse positives
- Smoke:
tools/smoke_lsp_rename_deep.sh(x→px ≥3, Red→Crimson ≥2, local x→xx =2) - Version bux-lsp 0.7.0; wired into
make test-lsp
Сесия 40 (LSP 0.8 call hierarchy)
- Providers:
textDocument/prepareCallHierarchycallHierarchy/incomingCalls— who calls FcallHierarchy/outgoingCalls— what F calls
- Graph: textual scan of known
funcsymbols +Name(call sites; enclosing function via nearest priorfuncdecl line - Skips the declaration itself; workspace disk scan for other
.buxfiles - Smoke:
tools/smoke_lsp_call_hierarchy.sh(Add ← Compute; Compute → Add/Mul) - Version bux-lsp 0.8.0;
make test-lsp
Сесия 41 (selfhost multi-file #line paths)
Decl.sourceFilestamped when parsing/merging each.buxfile (Cli_StampSourceFile/Cli_MergeFileInto/ projectsrc/loop)HirFunc.sourceFilecopied inLcx_LowerFunc- C backend: before each function, set
currentFilefromsourceFileand emit#line 1 "path"+ per-stmt#line N "path" - No env required — stdlib + user multi-file paths appear automatically
(
lib/Fs.bux,./src/Util.bux,./src/Main.bux, …) - Overrides:
BUX_DEBUG_FILE(force one path),BUX_NO_LINE=1(disable) - Verified: multi-file project runs;
#linepaths distinct per source
Сесия 42 (selfhost CI smoke)
tools/smoke_selfhost.sh:- build/use
buxc2(make selfhost) - move_field: run PASS + no
Array_Drop(&items)after field move - multi-file #line: Util.bux + Main.bux +
lib/*.buxpaths inmain.c
- build/use
make test-selfhost-smokedepends onselfhost- Wired into default
make test - Verified: smoke script PASS
Сесия 43 (LSP 0.9 method call hierarchy)
- Index methods in
analyzeFile:- Track
extend Type/impl Typebrace body funcinside → kindmethod, containerType, detailType.func …
- Track
- Call graph includes methods as callables
.Method(sites (iaDot) + freeFunc(calls- CallHierarchyItem: SymbolKind.Method (6), display
Type.Method datafield keeps bare name for graph match
- Smoke:
tools/smoke_lsp_method_hierarchy.shScale→Len;Main→Scale; prepare on method
- Version bux-lsp 0.9.0;
make test-lsp
Сесия 44 (LSP 0.10 method + type + receiver rename)
rtkMethod: rename method decl +.Method(+ bareMethod(- (previous global path skipped
iaDot→ broke method rename)
- (previous global path skipped
- Type rename (
isType):struct/extend Type/self: Type/ctors; skip.field selfreceiver: synthetic local when sema omits method params; clip to enclosingfuncbody via textual bounds (sibling methods safe)- Smoke:
tools/smoke_lsp_rename_method.sh- Len→Length ≥2, Point→Vec2 ≥4, self→this =3 (one method only)
- Version bux-lsp 0.10.0;
make test-lsp
Сесия 45 (LSP 0.11 interface dispatch hierarchy)
- Index:
interface I { func M… }→ iface methods + symbolextend Type for I→implsrelation + implementor methods
- Call hierarchy:
- prepare on interface method → item with
data: "I#M", kind Interface - outgoing on iface method → implementor methods (dispatch targets)
- incoming on iface method → callers of
.M(
- prepare on interface method → item with
- Smoke:
tools/smoke_lsp_iface_hierarchy.sh- Drawable.Draw → Circle implementor; Render → Draw
- Version bux-lsp 0.11.0
Сесия 46 (LSP 0.12 module-path segment rename)
- Index
import A::B::C/import A::B::{…}path segments (PathSegInfo) rtkPathSegrename with left-prefix match:Std::Io→ only segments under prefixStd(notFoo::Io, not bareIo)- path head
Stdonly when followed by::(not bare locals) - does not clobber enum
Color::Red(member path still wins on variants)
- Last import segment that is also a known symbol falls through to global rename
- Workspace scan for unopened
.buxfiles - Smoke:
tools/smoke_lsp_rename_path.sh- Io→Net ≥2 (Main+Util), not Foo::Io; Std→Core ≥2; Red→Crimson ≥2
- Version bux-lsp 0.12.0;
make test-lsp
Сесия 47 (HirNode-level sourceFile / mid-function #line)
HirNode.sourceFile— per-statement path for#line(rare multi-file spans)Lcx_StampSourceFile: after lowering a func/closure body, fill empty node paths fromDecl.sourceFile/HirFunc.sourceFile(keeps pre-set paths)LowerCtx.currentSourceFile+ closures inherit enclosing file- C backend:
lastDebugFile+ prefernode.sourceFileover funccurrentFile; re-emit#linewhen line or file changes mid-function BUX_DEBUG_FILEtruly forces one path (no longer overwritten by per-func)- Smoke:
tools/smoke_selfhost.sh— Util_Double body#line→ Util.bux only make test-selfhost-smoke/ rebuild selfhost
Сесия 48 (optional CI: selfhost-loop)
tools/selfhost_loop.sh: bootstrap determinism (buxc × 2)- path-normalized
#lineC compare (abs path / build-dir noise stripped) - stripped ELF compare; fails on real C or ELF mismatch
- path-normalized
- Lexer:
maxTokens32k → 131k (hir_lower was at the ceiling for buxc2 parse) - Optional fixed-point
BUX_SELFHOST_FIXED_POINT=1: buxc2 → buxc3- experimental; selfhost still diverges on full compiler C emit
- GitHub Actions
.github/workflows/selfhost-loop.ymlworkflow_dispatch(+ optional fixed_point input)- weekly cron (Sunday 06:00 UTC)
- push to
mainwhensrc//lib// loop script change - not on every PR / not in default
make test
- Verified:
make selfhost-loopPASS (determinism)
Сесия 49 (LSP 0.13 textDocument/implementation)
implementationProvider+textDocument/implementation- Interface type under cursor → locations of implementing types
(
extend Type for Iface/ type decl) - Interface method under cursor → implementor method decls
(reuses
collectImplementorFuncs+workspaceImpls) - Also: call-site / shared method name matching known iface methods; implementor method → sibling implementors of same iface method
- Smoke:
tools/smoke_lsp_implementation.sh- Drawable → ≥2 types; Draw → ≥2 methods (Circle + Square)
- Version bux-lsp 0.13.0;
make test-lsp
Сесия 50 (LSP 0.14 workspace import path index)
workspaceImportPaths: URI → full import paths (["Std","Io"])registerWorkspaceImportsat end ofanalyzeFile(scan + open/edit)isKnownImportPathPrefixuses workspace index first — no open doc required- Path segment snapshot fix (no shared seq mutation across segments)
- Smoke:
tools/smoke_lsp_workspace_imports.sh- only Main opened; Util closed on disk via
rootUriscan - Io→Net ≥2 edits including
Util.buxin WorkspaceEdit
- only Main opened; Util closed on disk via
- Version bux-lsp 0.14.0;
make test-lsp
Сесия 51 (Expr/Stmt sourceFile for multi-file / macros)
Expr.sourceFile/Stmt.sourceFile/Block.sourceFileAst_StampExprFile/Ast_StampStmtFile/Ast_StampBlockFile/Ast_StampPatternFile— fill empty slots only (grafted nodes keep path)Cli_StampSourceFilestamps decl body + const init + default params- Lower:
Lcx_SetNodeLoc/Lcx_SourceFileForprefer AST file, elsecurrentSourceFile;Lcx_StampSourceFilestill fills empties at func end - Smoke: Main body
#line→ Main.bux only (no Util leak); Util isolation kept - Rebuild selfhost;
make test-selfhost-smoke
Сесия 52 (selfhost fixed-point buxc2→buxc3→buxc4 green)
- Sema global scope: allocate via
Scope_New(8192), not 1024 — heap overflow on ~1200 decls crashed buxc2 inScope_Lookup - HIR buffers: funcs 512→4096, structs 64→512, enums/consts/gen* raised
CBE_FuncParam: avoid nested.paramN.nameon*HirParam(wrong.vs->)- Adapters:
__fat_envnotenv(no clash withCtfeEnv* env) - Unary
!: parenthesize operand so!(a && b)≠!a && b - Fat typedef skip-void: rewritten without nested
!for older gens - Fixed-point loop: compare gen2 vs gen3 (same CBE), not bootstrap vs selfhost
- Verified:
BUX_SELFHOST_FIXED_POINT=1 make selfhost-loop— C+ELF IDENTICAL
Сесия 53 (main CI workflow — make test)
.github/workflows/ci.yml- triggers:
pull_request,pushtomain,workflow_dispatch - job: install Nim 2.0.x + gcc/make/ssl →
make test - timeout 90m; concurrency cancel-in-progress
- failure artifact: selfhost
main.c(best-effort)
- triggers:
- selfhost-loop.yml remains optional (not on every PR)
- Docs: README / BuildAndTest / QUALITY_PLAN point to ci vs selfhost-loop
Сесия 54 (LSP 0.15 type hierarchy)
typeHierarchyProvider+ prepare / supertypes / subtypes- prepare on
struct/enum/interface/type→ TypeHierarchyItem - subtypes of interface → types with
extend T for I(Circle, Square) - supertypes of type → interfaces it implements
- Uses open-doc
impls+workspaceImpls+ symbol resolve - Smoke:
tools/smoke_lsp_type_hierarchy.sh - Version bux-lsp 0.15.0;
make test-lsp
Сесия 55 (macro / quote hygiene foundation)
- AST graft API (force overwrite):
Ast_GraftExpr/Stmt/Block/PatternFile - Clone:
Ast_CloneExpr/Stmt/Block/Pattern(+ list / match arms) - Quote policies:
Ast_QuoteDefSite— clone, keepsourceFile(macro body / template)Ast_QuoteCallSite/Ast_QuoteStmtCallSite— clone + graft call-site path
- Helpers:
Ast_SetExprLoc/Ast_ExprSourceFile - HIR:
Lcx_GraftSourceFile; mono instances force-graftgenDecl.sourceFile - Smoke:
tools/smoke_graft_hygiene.sh(Array mono → lib; Main no leak) - Wired into
make test-selfhost-smoke
Сесия 56 (CBE binary parentheses — C precedence safety)
- Bug (selfhost): tree HIR→C emitted nested binaries without parens.
return (a + b) * c→ Creturn a + b * c;→ 7 instead of 9return (a - b) / c→a - b / c→ 8 instead of 3
- Fix selfhost (
src/c_backend.bux):hBinaryalways emits(left op right)(same policy as bootstrap HIR CBE)- Unary operand parens (session 52) kept:
!(a && b)
- Bootstrap LIR (
bootstrap/lir_c_backend.nim): defensive parens on arith/bitwise and unary!/-/~(operands are temps today; future-proof) - Example:
examples/c_precedence.bux— MulSum/SubDiv/ShiftSum/Mix - Smoke:
tools/smoke_selfhost.shchecks run values and generated C contains(a + b) * c/(a - b) / c - Wired into
EXAMPLES+make test-selfhost-smoke - Verified: bootstrap + buxc2 →
9/3/6/7+PASS c_precedence; smoke PASS
Сесия 57 (CI split jobs + macOS smoke)
.github/workflows/ci.yml— no longer one 90m monolithicmake test:build(ubuntu):make build→ artifactbuxc-linux- Parallel (reuse artifact,
BUX_SKIP_BUILD=1):unit—fmt-check+test-unitexamples—test-examplesgoldens— errors + stdlib + registry + dwarfapps—test-appsselfhost—test-selfhost-smoke
macos: Homebrew OpenSSL + rebuild + fmt/unit/examplesci-gate: single required status (all of the above must succeed)
- Makefile:
test-unitextracted fromtestensure-buxc+BUX_SKIP_BUILD=1for CI artifact reuse$(OUT)rebuild only whenbootstrap/*.nimchanges- portable examples runner (optional
timeout; macOS without coreutils OK)
- macOS / non-GNU ld:
- bootstrap:
-Wl,--build-id=noneonlywhen defined(linux) - selfhost:
bux_cc_ld_stable()inrt/runtime.c(Linux-only build-id) - CI sets
BUX_CFLAGS=-I… -L…for Homebrewlibcrypto
- bootstrap:
- Docs: BuildAndTest + README CI table; local
make teststill full sequential - Verified locally:
BUX_SKIP_BUILD=1 make fmt-check test-unit test-errors
Сесия 58 (LSP 0.16 workspace type hierarchy index)
- Gap: type hierarchy subtypes/supertypes only saw open-doc
implsor method-keyedworkspaceImpls. Closed files with emptyextend T for I {}(no methods) returned []. workspaceTypeRels(tools/lsp_server.nim):- URI →
seq[(typeName, iface, line)]from everyanalyzeFile registerWorkspaceTypeRelsreplaces per-URI (re-open / re-scan safe)- filled on
scanWorkspace+ open/edit — no open doc required
- URI →
- Consumers:
collectSubtypeItems/collectSupertypeItemsprefer type-rel indexcollectTypeImplementorLocs(textDocument/implementation) same
- Smoke:
tools/smoke_lsp_type_hierarchy_ws.sh- open only Main; Drawable.bux + Shapes.bux closed
- empty extends → Drawable subtypes Circle+Square; Circle supers Drawable+Named
- Version bux-lsp 0.16.0; wired into
make test-lsp - Verified: single-file hierarchy + workspace smoke PASS
Сесия 59 (user-facing macro! / quote! — declarative MVP)
- Syntax (bootstrap):
macro! name { ($x:expr, …) => { template } }- Invoke:
name!(args…)— distinct from unwrapexpr!via following( - Built-in
quote!(e)— identity expand + call-site graft
- Lexer: keyword
macro;$identfragment tokens ($x) - AST:
dkMacro+MacroRule/MacroFragment;ekMacroCall - Expansion (
bootstrap/macroexpand.nim) before sema:- Collect macro decls; match rule by arity
- Deep clone + substitute
$frags+ graft call-siteSourceLocation - Nested expand (depth ≤ 32)
- CLI:
build/check/runcallexpandMacrosafter merge - Example:
examples/macro_twice.bux→ 42 / 42 / 43 + PASS - LanguageRef: Macros section (limits documented)
- Verified:
./buxc run macro_twice; hello + lexer/parser tests green - Not yet: selfhost expand parity;
$(…)*repetition; more frag kinds
Сесия 60 (selfhost macro! / quote! expand parity)
- Lexer/token:
tkMacro, keywordmacro,$identfragments - AST:
dkMacro(rules inchildDecl1chain),ekMacroCall - Parser:
macro! name { ($x:expr) => {…} }, invokename!(…) src/macroexpand.bux:- collect macros → match rule by arity → clone+subst
$frags - call-site graft (line/col/
sourceFile) - built-in
quote!(e)
- collect macros → match rule by arity → clone+subst
- CLI: expand before sema (project / check / compile paths)
- Sema fixes (needed for block templates):
ekBlockvalue = lastskExprtype (was alwaystyVoid)let x: T = …setssym.typeKindfrom annotation (not only init)
- Smoke:
tools/smoke_selfhost.shrunsexamples/macro_twice.buxvia buxc2 - Verified: buxc2 + bootstrap →
42/42/43+PASS macro_twice
Сесия 61 (macro $(…)* + ident/tt fragments)
- Fragment kinds:
expr|ident|tt(tt ≡ expr for now) - Pattern rep (trailing):
$( $x:expr ),*/$( $x:expr )* - Template rep:
$( stmts… )*→skMacroRep, expanded per list item - Lexer: bare
tkDollarfor$(…)(vs$ident) - Bootstrap
macroexpand.nim: list bindings, match rules, gensym locals - Selfhost parity:
useNamesencodes kinds/rep:,Subst_Block_Flat, gensym - Sema: block-as-expr checks last value inside child scope (no UAF of locals)
- Example:
examples/macro_repeat.bux— sum_n / empty / call0 / id_tt - Verified: bootstrap + buxc2 →
6/0/42/7+PASS macro_repeat
Сесия 62 (C.4 @[Release] polish + Checked docs)
- Three-tier model documented in LanguageRef:
- default (no checks) →
@[Checked]→@[Release](force off)
- default (no checks) →
- Bootstrap:
releaseFunc;checkedFunc = Checked ∧ ¬Release - Selfhost: same rule; stacked attrs loop (
@[Checked]+@[Release]) - Parser: multi-line stacked
@[…](skip newlines between attrs) - Tests (
borrow_test): Release alone; Checked+Release wins; Checked still errors - Example:
examples/ownership_release.bux— Unchecked / Safe / Hot / HotDangle - Verified: 27/27 borrow tests; example PASS
Сесия 63 (nested $(…)* / multi-rep / compound zip)
- Bootstrap (
macroexpand.nim+ parser):- Compound rep:
$( $a:expr, $b:expr ),*→ parallel lists, zip in template - Multi-rep:
$(…)* ; $(…)*with call-site;groups (exprMacroGroupLens) - Nested template: outer binds list → inner
$(…)*expands once (no list names left) MacroFragment.names/.kindsfor multi-name frags
- Compound rep:
- Selfhost parity (
src/macroexpand.bux,parser.bux):- Two named rep lists + zip in
Subst_Block_Flat - Kinds encoding
rep:expr+expr,@2/ multi-segrep:…;rep:… - Macro call
;groups →genericCalleegroup-length string
- Two named rep lists + zip in
- Example:
examples/macro_nested.buxadd_pairs→ 33,sum_groups→ 63,double_each_sum→ 14,named_sum→ 18
- LanguageRef: multi-rep / compound / nested docs; limits updated
- Verified: bootstrap + buxc2 → PASS macro_nested / macro_repeat / macro_twice
Сесия 64 (CI Nim cache + faster macOS)
- Nim pin + toolchain cache:
NIM_VERSION: 2.0.8(stable cache keys; was2.0.x)- Cache
.nim_runtimeonbuild/unit/macos/selfhost-loop - Skip
setup-nim-actionon cache hit; restorePATHonly
nimcacheproject-local:MakefileNIMFLAGS ?= --nimcache:nimcachefor bootstrap + unit testsactions/cachekeyed onbootstrap/**/*.nim(+ tests for unit)
- Leaner macOS job:
- Runner
macos-14; timeout 35m make test-unit+make test-examples-smoke(not full EXAMPLES / not fmt)EXAMPLES_SMOKE: hello, ownership*, strings, map, c_precedence, macro_*- OpenSSL: install only if missing (
brew list)
- Runner
- Docs: BuildAndTest CI table;
.gitignore.nim_runtime/ - Verified locally:
make buildusesnimcache/;test-examples-smokePASS
Сесия 65 (Drop / RAII docs — field-move story)
- LanguageRef — Drop and RAII (under Gradual Ownership):
@[Drop]vsextend T for Drop(staticType_Drop, no vtable)- When auto-drop runs (block end, early return, branches) — not gated on Checked
- Field-move skip Drop with
MakeBox+ simplified C (noArray_Drop(&items)) - Move-on-return, assignment, call-arg transfers; error-path still Drops
- Limits: whole-local moves, static dispatch, manual free pitfalls
- TOC links Ownership + Drop; Stdlib
Array_Drop+Std::Dropsection - README Drop line mentions field-move
- Cross-refs:
examples/move_field.bux,examples/drop_early_return.bux, selfhost smoke - Verified:
move_fieldC has noArray_Dropon moveditems; example PASS
Сесия 66 (macro hygiene + frag kinds literal / block)
- Fragment kinds (bootstrap + selfhost):
literal/lit— onlyekLiteral(rejects1 + 2)block— onlyekBlock{ … }- Shared
fragMatches/Macro_FragMatchesat match time
- Hygiene gensym:
- Bootstrap: also rename
forbinders; walk for bodies in collect - Selfhost: gensym
skFor+ recurse if/while/for/MacroRep bodies - CBE: two
with_acc!→__m1_n/__m2_n(no collision)
- Bootstrap: also rename
- Example:
examples/macro_hygiene.bux→ 11/21/7/3/42 + PASS - LanguageRef: kind table + hygiene layers (graft + gensym)
- Makefile:
macro_hygienein EXAMPLES + EXAMPLES_SMOKE - Verified: bootstrap + buxc2; negative
only_lit!(1+2)→ no matching rule
Сесия 67 (CI Windows smoke)
.github/workflows/ci.yml—windowsjob (windows-latest, bash shell):- Cache Nim 2.0.8 (prebuilt zip — fast) +
nimcache nim c -o:buxc.exebootstrap- Pure Nim unit tests: lexer / parser / sema / hir / borrow
- CLI smoke:
buxc.exe new+--version
- Cache Nim 2.0.8 (prebuilt zip — fast) +
- Scope (honest): no
bux runexamples on Windows yet —rt/runtime.cis POSIX (ucontext,pthread, sockets, OpenSSL link). Job still gates bootstrap regressions on Win. ci-gate:windowsis a required job- Docs: BuildAndTest CI table + Windows note
- Locally: YAML validated; full Win run is on GHA only
Сесия 68 (partial field moves + Drop goldens)
- Bug:
return bag.itemsstill ranBag_Drop(&bag)→ double-free / corrupt Array (ASSERT fail). Also dead double-Drop after terminalreturn. - Bootstrap (
hir_lower.nim):markMovedOutFromAsthandlesekFieldwhen field type is droppable (autoDropFuncName) — not forreturn a.id(int)- Scope exit: skip re-emitting drops when last stmt always-returns; pop defers
- Selfhost (
c_backend.bux):CBE_MarkMovedFromNodeHint+ droppable type check; return usescurrentRetType- Store/let rhs walks field access for partial moves
- Example + golden smoke:
examples/move_field_partial.buxtools/smoke_drop_move.sh+make test-drop-move(CI goldens job)
- Verified: partial PASS;
TakeItemshas noBag_Drop; drop_early_return 5; move_field PASS
Сесия 69 (macro unhygienic binders)
- Problem: gensym renamed all template
let/varbinders, sovar $name: int = …with$name:identcould not introduce a call-site name. - Bootstrap (
macroexpand.nim):binderIdentFromFrag+expandUnhygienicsetsubstStmt: rewriteskLet/skForbinder when name is$frag→ ekIdentcollectLetNamesskips unhygienic names
- Selfhost (
macroexpand.bux):Env_AddUnhy/Env_IsUnhy/Env_BinderFromFragSubst_Stmtrewrites binders;Macro_GensymBlock(ex, body, env)skips them
- Example:
examples/macro_unhygienic.bux→ 11/21/6/10/1 + PASS- C:
counter/other/nkept;acc/scratch→__mN_*
- C:
- LanguageRef: unhygienic binder table; EXAMPLES + EXAMPLES_SMOKE
- Verified: bootstrap + buxc2; macro_hygiene still PASS
Сесия 70 (per-field Drop + mono defer restore)
- Bug (critical):
lowerFuncrestoreddeferStmts/movedOutLocalsonly when the inner mono function still had pending defers. NestedgenerateMethodInstance→lowerFunc(e.g.Array_LeninsidePeekTagAndTake) wiped the caller's Drop stack → leaked moved Arrays. - Fix bootstrap: always restore
deferStmts/movedOutLocals/partialMovedFieldsafter lowering a function body. - Per-field Drop after partial move:
- Track
partialMovedFields: local → {field names} - Skip parent
Type_Drop; emit Drop for remaining droppable fields emitDropOrPartialat return / block exit / function tail
- Track
- Selfhost CBE (
c_backend.bux):- partial (var, field) slots + local type registry on
hAlloca CBE_EmitRemainingFieldDropswhen skipping moved parent Drop
- partial (var, field) slots + local type registry on
- Example + smoke:
examples/move_field_remaining.bux(PairBag left move → Tracked_Drop right)tools/smoke_drop_move.shchecks PeekTag Array_Drop + remaining Tracked_Drop
- LanguageRef: remaining-field rule; limits updated
- Verified: bootstrap + buxc2 remaining/partial/move_field; smoke; EXAMPLES
Сесия 71 (Windows MinGW + hello smoke)
rt/runtime_win.c— minimal runtime without pthread / ucontext / sockets / OpenSSL. Real alloc, strings, files, time, env; stubs for tasks/crypto/net.- Bootstrap CLI (
bootstrap/cli.nim):- Windows (or
BUX_RUNTIME=win) copiesruntime_win.cinstead ofruntime.c - Link:
-ffunction-sections -Wl,--gc-sections -lm(no-pthread/-lcrypto) - Host
gccon Windows;.exesuffix on build/run - Fixed:
-llibs after.cinputs (GNU ld order)
- Windows (or
- CI (
.github/workflows/ci.ymlwindows job):- MinGW via
msys2/setup-msys2(mingw-w64-x86_64-gcc) tools/smoke_windows_hello.shafter unit/CLI smoke
- MinGW via
- Docs: BuildAndTest CI table +
rt/tree - Verified locally: normal
hello+BUX_RUNTIME=winsmoke PASS
Сесия 72 (macro stmt / pat fragments)
- Kinds:
mfkStmt/mfkPat(+ aliasespattern,litalready) - AST wrappers:
ekMacroStmt/ekMacroPat(expand-only) - Call-site parse:
- stmt keywords →
parseStmt→ MacroStmt _→parsePattern→ MacroPat- else expr;
patcoerces viaexprToPattern(ident/lit/path/call/tuple/struct/range)
- stmt keywords →
- Expand:
coerceArgat match; store normalized MacroStmt/MacroPat$sas skExpr splices MacroStmt into the statement list$pas pkIdent pattern substitutes bound MacroPat
- Selfhost: same kinds, coerce, splice, pattern subst
- Example:
examples/macro_stmt_pat.bux— setup/do_twice/matches/if_let_like - LanguageRef kind table + docs
- Verified: bootstrap + buxc2
macro_stmt_patPASS
Сесия 73 (nested a.b.c field-move Drop)
- Bootstrap (
hir_lower.nim):fieldPathFromAst→ base local + path@["inner","items"]partialMovedFieldsstores dotted paths ("inner.items")remainingDropsAtrecursive: exact path = skip; prefix = recurse; other droppable fields →Type_Drop(&(base.a.b))- Typed intermediate
hFieldAccessso LIR/C keepInnernotint
- Selfhost CBE: full dotted path on mark; recursive
CBE_EmitRemainingAt - Example:
examples/move_field_nested.bux—outer.inner.items→ 2 Tracked drops - Smoke + EXAMPLES; LanguageRef nested path section
- Selfhost: recursive remaining drops + skip Drop when
HasPartialMoved(struct emit multi-pass topo for Outer{Inner}) - Verified: bootstrap + buxc2
nested_drops=4PASS; fulltest-drop-move
Сесия 74 (field moves through pointers)
- Pointer aliases:
let p = &bag/p = &bag→ptrAliases[p] = bag - fieldPathFromAst: peel
(*p)(ekUnary tkStar); resolve alias to owner p.field(auto-deref) and(*p).fieldmark owner + path- Nested via ptr:
p.inner.items→ owner +"inner.items" - Selfhost CBE: alias slots + resolve in
CBE_BaseVarName; record on store/assign - Example:
examples/move_field_ptr.bux→ptr_drops=5 - Smoke + LanguageRef; limits: local aliases only (not cross-function params)
- Selfhost: unary C parens fix
(*p).field; alias slots + BaseVar resolve - Verified: bootstrap + buxc2
ptr_drops=5PASS; fulltest-drop-move
Платформен фокус (v0.5 → v1.0 ✅)
| Ниша | Какво значи за Bux | Статус / посока |
|---|---|---|
| Linux | Host + CI + full rt/runtime.c (pthread, ucontext, sockets, OpenSSL) |
✅ primary; macOS secondary smoke only |
| Cloud-native | HTTP/HTTPS, registry (lock+HTTPS), containers, musl docs | ✅ sessions 75–79 |
| Embedded | Cross (--target), CTFE tables, thin runtime, no-GC story |
✅ minimal + aarch64 + riscv64 smoke (85) + freestanding notes; bare-metal still spike |
| Windows | Не е product target | ⛔ no further investment (existing MinGW hello = historical) |
Правило: нов runtime / stdlib / CI effort отива към Linux + cloud + embedded. Windows-only work не влиза в следващи сесии.
Сесия 75 (Linux / cloud / embedded foundation)
rt/runtime_minimal.c— thin runtime (no pthread / ucontext / sockets / OpenSSL); same feature surface as historicalruntime_win.c, documented for Linux static/embed.- Bootstrap CLI (
bootstrap/cli.nim):BUX_RUNTIME=full|minimal|thin|embed|win--static/BUX_STATIC=1→ fully-static link; defaults to minimal runtime--target <triple>→ prefers<triple>-gcc, elseclang -target; defaults minimalBUX_CCoverride; thin link uses-ffunction-sections -Wl,--gc-sections -lm
- CTFE bitwise + hex (
bootstrap/sema.nim):^&|<<>>in const eval0x/0b/0ointeger literals in CTFE
- Example
examples/ctfe_crc.bux— recursive CRC-8 table cells +Pow2(8)size - Smoke
tools/smoke_linux_targets.sh/make test-linux-targets:- minimal hello run
--static --release+filestatically linked--target aarch64-linux-gnuwhen cross-gcc present- ctfe_crc under minimal
- Container
examples/docker/Dockerfile.static+tools/build_static_hello.sh - CI goldens job installs
gcc-aarch64-linux-gnuand runstest-linux-targets - Docs: BuildAndTest + QUALITY_PLAN platform section
Verified: smoke 4/4 PASS; CRC_1=#define 7; aarch64 static ELF.
Сесия 76 (P0: ownership + macros + selfhost parity)
- Cross-function pointer ownership (
bootstrap/hir_lower.nim):TakeItems(&bag)where callee doesreturn p.items/let x = p.items- Call site marks owner
bag+ partial pathitems; remaining fields still Drop - Example
examples/move_cross_fn.bux+ smoke intest-drop-move
- Macro
$x:tt— broader thanexpr: any single call-site AST fragment (bootstrap + selfhost); exampleexamples/macro_tt.bux - Selfhost link parity (
src/cli.bux):--static,--target,BUX_RUNTIME,BUX_CC,BUX_STATICCli_LinkProgramshared by single-file + project builds- Thin runtime → no pthread/OpenSSL; full → POSIX as before
- Docs: LanguageRef tt + QUALITY_PLAN
Verified: move_cross_fn PASS; macro_tt PASS; smoke_drop_move; buxc2 --static project → static ELF + Hello, Bux!.
Сесия 77 (selfhost cross-fn + Nexus production polish)
- Selfhost cross-fn ownership (
src/c_backend.bux):CBE_MarkCrossFuncFromCallscans callee HIR forp.fieldmoves- Call site
TakeItems(&bag)→ partial move onbag(no double-free) - Verified:
buxc2 run move_cross_fn→cross_fn_drops=2PASS
- Runtime stop handlers (
rt/runtime.c):bux_install_stop_handlers/bux_should_stop/bux_set_stop_listen_fd- SIGINT/SIGTERM set flag + close listen fd (unblock accept)
- Thin/win runtimes: no-op stubs
- Stdlib
Os_InstallStopHandlers/Os_ShouldStop/Os_SetStopListenFd - Nexus 0.4.0:
- Graceful stop: main=acceptor; poison workers (
fd=-1); exit on SIGTERM - Access log:
METHOD path status ms(NEXUS_ACCESS_LOG) - Max body:
NEXUS_MAX_BODY(default 1 MiB) → 413 - Config fields +
/api/healthversion 0.4.0
- Graceful stop: main=acceptor; poison workers (
- TLS deferred (needs SSL context in runtime — not this session)
Verified: SIGTERM exits nexus; health JSON 0.4.0; access log lines; selfhost cross_fn.
Сесия 78 (Nexus TLS + container story)
- Runtime TLS (
rt/runtime.c+ OpenSSLlibssl):bux_tls_server_ctx/accept/send/recv/close/error- Thin/win: stubs; full POSIX links
-lssl -lcrypto
- Stdlib
Std::Net—Tls_ServerCtx,Tls_Accept,Tls_Send,Tls_Recv, … - Nexus 0.5.0:
NEXUS_TLS=1+NEXUS_TLS_CERT/NEXUS_TLS_KEY(PEM)ConnectionTask.tlshandle;ConnRecv/ConnSenddual plain/TLS- Banner
https://when TLS; SIGTERM still graceful
- Smoke
tools/smoke_nexus_tls.sh/make test-nexus-tls(openssl self-signed + curl -k) - Containers:
examples/docker/Dockerfile.nexus(debian-slim + libssl3)examples/http_health.bux+Dockerfile.health+tools/build_health_bin.sh
- CLI link flags bootstrap + selfhost:
-lssl -lcrypto
Verified: curl -k https://…/api/health → 0.5.0; SIGTERM exit; health binary HTTP.
Сесия 79 (registry lock/HTTPS + musl path)
bux installlock checksums — sha1 of sorted*.buxsources per packagebux install --locked— CI mode: verify paths + checksums; no re-resolveBUX_REGISTRY_INSECURE=1— self-signed HTTPS registry fetch (curl -k)- Smoke
tools/smoke_registry.sh:- lock deterministic (diff two installs)
--lockedok / missing fail / checksum mismatch fail- HTTP + HTTPS self-signed index
- musl path
tools/smoke_musl_static.sh/make test-musl-staticBUX_CC=musl-gccor zig musl wrapper +BUX_RUNTIME=minimal --static- SKIP when toolchain absent (documented)
- Docs: Packages.md lock section; BuildAndTest musl; Dockerfile.alpine-health
Verified: registry smoke full PASS; musl SKIP (no toolchain on host).
Сесия 80 (selfhost install --locked + Nexus mTLS)
- Selfhost
install/install --locked(src/cli.bux):- Write
bux.lockwith sha1 checksums (shellsha1sumof sorted*.bux) --lockedverifies paths + checksums (CI parity with bootstrap session 79)- Manifest: parse
[Dependencies]+{ Path = "..." }inline tables
- Write
- mTLS (
rt/runtime.cbux_tls_server_ctx_ex):- Optional client CA →
SSL_VERIFY_PEER | FAIL_IF_NO_PEER_CERT Tls_ServerCtxMtlsinStd::Net- Nexus
NEXUS_TLS_CLIENT_CA→ require client certs (v0.6.0)
- Optional client CA →
- Smokes:
tools/smoke_selfhost_install.sh,tools/smoke_nexus_mtls.shmake test-selfhost-install/make test-nexus-mtls
Verified: selfhost lock/locked/mismatch; mTLS reject without cert + accept with cert.
Сесия 81 (selfhost full registry)
src/registry.bux— load index from$BUX_REGISTRY/~/.bux/config/registry.toml- HTTP(S) fetch via curl/wget →
~/.bux/cache/registry_http.toml BUX_REGISTRY_REFRESH,BUX_REGISTRY_INSECURE(parity with bootstrap)
- HTTP(S) fetch via curl/wget →
- CLI
search/add <name>/add <name> <version|url> - Build path deps — merge
depUrlabsolute/relative packagesrc/(not onlydeps/) - Runtime discovery —
BUX_STDLIB/../rtwhen building outside the monorepo tree - Smoke
tools/smoke_selfhost_registry.sh/make test-selfhost-registry
Verified: search greet; add+install+run Hello, Bux!; HTTP registry search.
Сесия 82 (CI cloud/selfhost smokes + polish)
- CI apps job —
test-nexus-tls+test-nexus-mtls(openssl + curl) - CI selfhost job —
test-selfhost-install+test-selfhost-registry - Registry paths —
expandFilenamefor file:/path: sources (no..in lock) - Docs already cover sessions 75–81 platform stack
Verified: local smokes previously green; CI wiring ready for GHA.
Сесия 83 (stdlib daily API — collections_extra)
- Array (
lib/Array.bux):Array_RemoveAt— shift-left remove, returns valueArray_Insert— insert at0..=len(append at end)Array_SwapRemove— O(1) unordered removeArray_Clone— shallow value clone into new buffer
- String (
lib/String.bux):String_Cmp—strcmpwrapperString_IndexOf— byte index or-1String_ToUpper/String_ToLower— ASCII only via StringBuilder
- Map (
lib/Map.bux):Map_GetOr/StringMap_GetOr— default when key missing
- Example
examples/collections_extra.bux+ MakefileEXAMPLES - Goldens
tests/stdlib_golden/{array,string}extended - Docs
docs/Stdlib.mdtables updated
Verified: collections_extra PASS; make test-stdlib 3/3 PASS.
Сесия 84 (macro raw tt groups + expression-level $(…),*)
- Delimiter-balanced
:ttgroups (bootstrap + selfhost):$args:ttthat is a multi-element tuple(a, b)is wrapped asekMacroTtwith group flag- Splice
$f($args)flattens tof(a, b)(notf((a, b))) - Value-position splice unwraps to the inner tuple / fragment
- Contrast
:exprkeeps the tuple as one argument
- Expression-level rep in templates:
- Parse
$( expr ),*/$( expr )*inside call argument lists whenmacroTemplateMode ekMacroRepexpands to N call args (zip list fragments)- Example:
apply_rep!(Add3, 1, 2, 3)→Add3(1, 2, 3)
- Parse
- Example
examples/macro_tt_raw.bux+ Makefile EXAMPLES / EXAMPLES_SMOKE - Docs LanguageRef tt + expression-level rep + limits
- Hardening:
CBE_NormalizeTypeName/String_StartsWithnull-safe (fixed selfhost segfault onif_let_like+ enum field-move path)
Verified: bootstrap + buxc2 macro_tt_raw PASS; macro_stmt_pat PASS; macro regressions OK.
Сесия 85 (riscv64 cross smoke + freestanding notes + slice :tt)
- riscv64 cross smoke (
tools/smoke_linux_targets.sh):- Shared
try_crosshelper for aarch64 + riscv64 PASSwhen${triple}-gccpresent; SKIP otherwise (no false fail)- Documented: clang
-targetalone needs a sysroot
- Shared
- Freestanding / bare-metal research (
docs/BuildAndTest.md):- Table: thin/static/cross ✅ vs true freestanding / Cortex-M 🔬
- Clarifies
runtime_minimalstill uses libc (not no-libc)
- Delimiter-balanced
:tt+ slice lit (session 84 extension):[a, b]slice groups flatten like tuples in$f($args)- Selfhost parser: primary
[a, b, …]→ekSlice(parity with bootstrap) examples/macro_tt_raw.buxcaseapply_tt!(Add, [8, 9])→ 17
- Docs: LanguageRef, BuildAndTest, Makefile target blurb
Verified: make test-linux-targets — minimal/static/aarch64/ctfe PASS; riscv64 SKIP;
bootstrap + buxc2 macro_tt_raw (incl. slice) PASS.
Сесия 86 (free-form juxta :tt paste)
- Pattern juxtaposition:
$f:ident $args:ttwithout comma between fragments (bootstrap + selfhost parser continue on next$…) - Expand-time call split: when rule is exactly two fixed frags
ident+ttand the call site has one arg that isekCallwith ident callee:- bind
$f→ callee - bind
$args→ MacroTt group of the call’s arguments $f($args)flattens as before →f(a, b)
- bind
- Example
apply_juxta!(Add(2, 5))/apply_juxta!(Add3(1, 2, 4))inexamples/macro_tt_raw.bux - LanguageRef juxta section; comma form still works
Verified: bootstrap + buxc2 macro_tt_raw (h=7, i=7) PASS; macro regressions OK.
Сесия 87 (:type fragments + Array_Reverse / Test_AssertNeqString)
- Macro
$t:type(bootstrap + selfhost):- Fragment kind
type(typeis a keyword — special-cased in parsers) - Call-site coerce:
int→ named;*int→ pointer - Subst into
sizeof($t),as $t,let x: $t
- Fragment kind
- Example
examples/macro_type.bux— size_of / cast_zero + reverse/neq - Stdlib:
Array_Reverse,Test_AssertNeqString - Docs: LanguageRef type table; Stdlib Array_Reverse
Verified: bootstrap + buxc2 macro_type PASS (sizeof int=4, *int=8).
Сесия 88 (v1.0.0 language freeze)
- Version banners: bootstrap + selfhost CLI → 1.0.0; root
bux.toml→ 1.0.0 docs/SEMVER.md— Active (post-1.0 MAJOR = breaking)docs/RELEASE_v1.0.0.md— freeze notes + verify commands- README / QUALITY_PLAN / ROADMAP status → v1.0.0
- Tag
v1.0.0aftermake testgate
Post-1.0 backlog (MINOR, not freeze blockers):
Generics in✅ session 5:type(Array<int>); operators-only tt paste✅ session 5 (Cortex-M / board BSP still research)runtime_freestanding.c- LSP / IDE versioning independent of language MAJOR
Follow-up (2026-07-28)
?/!payload types — bootstrap no longer hardcodesint; Ok/Some type fromResult<T,E>/ enum fields (bootstrap/sema.nim,hir_lower.nim).- Example
try_generic— String Result propagation. - LSP 0.18 —
textDocument/formatting(+ range) viaformatSource/bux fmt; VS Code format-on-save default;tools/smoke_lsp_formatting.sh. - Macros: generic
:type+Array_New<$t>; operators-only$op($a,$b)/ juxta binary split (examples/macro_type_generic,macro_op_paste). - Freestanding:
rt/runtime_freestanding.c,BUX_RUNTIME=freestanding,make test-freestanding.
Изрично не правим
- Повече Windows examples / Win OpenSSL / Win sockets
- Windows като required CI gate за product features (остава optional historical smoke ако CI вече го има)
- Desktop GUI / Win32 APIs