release: Bux v1.0.0 language freeze
Bump compiler banners and package version to 1.0.0, activate SEMVER policy, and add RELEASE_v1.0.0 notes. Fix closure auto-Drop leaking outer Array drops into nested capture bodies (iter_hof). Fmt-clean examples/src for CI.
This commit is contained in:
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
> **Status:** v0.5.x — Bootstrap (`buxc`, Nim) and self-hosted (`buxc2`, Bux) both compile `.bux` → C → native binary.
|
> **Status:** **v1.0.0** — language freeze. Bootstrap (`buxc`, Nim) and self-hosted (`buxc2`, Bux) both compile `.bux` → C → native binary.
|
||||||
> **Selfhost loop:** deterministic C codegen + ELF verified.
|
> **Selfhost loop:** deterministic C codegen + ELF verified.
|
||||||
> **Gradual Ownership:** `@[Checked]` borrow checker, `@[Release]` zero-cost mode, `borrow &mut` expressions.
|
> **Gradual Ownership:** `@[Checked]` borrow checker, `@[Release]` zero-cost mode, `borrow &mut` expressions.
|
||||||
> **Closures:** multi-instance capturing closures via fat function pointers (`BuxFn { code, env }`) in both compilers.
|
> **Closures:** multi-instance capturing closures via fat function pointers (`BuxFn { code, env }`) in both compilers.
|
||||||
> **Tuples:** `(T, U)` types and `.0`/`.1` field access (bootstrap + selfhost).
|
> **Tuples:** `(T, U)` types and `.0`/`.1` field access (bootstrap + selfhost).
|
||||||
> **Green Threads:** M:N scheduler with channels (Go-style goroutines without GC).
|
> **Green Threads:** M:N scheduler with channels (Go-style goroutines without GC).
|
||||||
> **Examples:** 40+ programs pass (`make test-examples`). Apps: `boko-framework`, `jwt-pitbul`, `nexus`, `simpledb`.
|
> **Examples:** 40+ programs pass (`make test-examples`). Apps: `boko-framework`, `jwt-pitbul`, `nexus`, `simpledb`.
|
||||||
|
> **Semver:** after 1.0, breaking changes require MAJOR (`docs/SEMVER.md`).
|
||||||
|
|
||||||
Bux is a fast, compiled, strongly-typed systems programming language. Features a C backend for native code generation, raw multi-line strings, gradual ownership (opt-in borrow checking), multi-instance closures, async/await, generics, algebraic enums, and a package manager.
|
Bux is a fast, compiled, strongly-typed systems programming language. Features a C backend for native code generation, raw multi-line strings, gradual ownership (opt-in borrow checking), multi-instance closures, async/await, generics, algebraic enums, and a package manager.
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ module Main {
|
|||||||
let tlsEnv: String = Os_GetEnv("NEXUS_TLS");
|
let tlsEnv: String = Os_GetEnv("NEXUS_TLS");
|
||||||
if String_Len(tlsEnv) > 0 {
|
if String_Len(tlsEnv) > 0 {
|
||||||
if String_Eq(tlsEnv, "1") || String_Eq(tlsEnv, "true") || String_Eq(tlsEnv, "on") ||
|
if String_Eq(tlsEnv, "1") || String_Eq(tlsEnv, "true") || String_Eq(tlsEnv, "on") ||
|
||||||
String_Eq(tlsEnv, "https") {
|
String_Eq(tlsEnv, "https") {
|
||||||
config.tlsEnabled = true;
|
config.tlsEnabled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1345,7 +1345,7 @@ proc cmdDoc*(args: seq[string], opts: GlobalOptions): int =
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
proc cmdVersion*(args: seq[string], opts: GlobalOptions): int =
|
proc cmdVersion*(args: seq[string], opts: GlobalOptions): int =
|
||||||
echo "bux 0.1.0 (bootstrap)"
|
echo "bux 1.0.0 (bootstrap)"
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
proc runCli*(args: seq[string]): int =
|
proc runCli*(args: seq[string]): int =
|
||||||
|
|||||||
+20
-1
@@ -2734,18 +2734,37 @@ proc lowerClosureFunc(ctx: var LowerCtx, expr: Expr): HirFunc =
|
|||||||
f.retType = ctx.resolveTypeExpr(expr.exprClosureReturnType)
|
f.retType = ctx.resolveTypeExpr(expr.exprClosureReturnType)
|
||||||
else:
|
else:
|
||||||
f.retType = makeVoid()
|
f.retType = makeVoid()
|
||||||
# Body with closure rewriting
|
# Body with closure rewriting — isolate Drop/defer stack like lowerFunc.
|
||||||
|
# Nested closure lowering previously kept the outer function's deferStmts,
|
||||||
|
# so `return` inside a capture emitted Array_Drop for outer locals (session 88).
|
||||||
let savedDepth = ctx.closureDepth
|
let savedDepth = ctx.closureDepth
|
||||||
let savedExpr = ctx.currentClosureExpr
|
let savedExpr = ctx.currentClosureExpr
|
||||||
let savedEnv = ctx.envInstanceName
|
let savedEnv = ctx.envInstanceName
|
||||||
|
let oldDefers = ctx.deferStmts
|
||||||
|
let oldMovedOut = ctx.movedOutLocals
|
||||||
|
let oldPartialMoved = ctx.partialMovedFields
|
||||||
|
ctx.deferStmts = @[]
|
||||||
|
ctx.movedOutLocals = initHashSet[string]()
|
||||||
|
ctx.partialMovedFields = initTable[string, HashSet[string]]()
|
||||||
ctx.closureDepth = ctx.closureDepth + 1
|
ctx.closureDepth = ctx.closureDepth + 1
|
||||||
ctx.currentClosureExpr = expr
|
ctx.currentClosureExpr = expr
|
||||||
ctx.envInstanceName = f.envInstanceName
|
ctx.envInstanceName = f.envInstanceName
|
||||||
if expr.exprClosureBody != nil:
|
if expr.exprClosureBody != nil:
|
||||||
f.body = ctx.lowerBlock(expr.exprClosureBody)
|
f.body = ctx.lowerBlock(expr.exprClosureBody)
|
||||||
|
# Emit pending auto-drops for locals allocated *inside* the closure body only
|
||||||
|
if ctx.deferStmts.len > 0 and f.body != nil and f.body.kind == hBlock:
|
||||||
|
var lastIsReturn = false
|
||||||
|
if f.body.blockStmts.len > 0 and f.body.blockStmts[^1].kind == hReturn:
|
||||||
|
lastIsReturn = true
|
||||||
|
if not lastIsReturn:
|
||||||
|
for i in countdown(ctx.deferStmts.len - 1, 0):
|
||||||
|
ctx.emitDropOrPartial(f.body.blockStmts, ctx.deferStmts[i], "")
|
||||||
ctx.closureDepth = savedDepth
|
ctx.closureDepth = savedDepth
|
||||||
ctx.currentClosureExpr = savedExpr
|
ctx.currentClosureExpr = savedExpr
|
||||||
ctx.envInstanceName = savedEnv
|
ctx.envInstanceName = savedEnv
|
||||||
|
ctx.deferStmts = oldDefers
|
||||||
|
ctx.movedOutLocals = oldMovedOut
|
||||||
|
ctx.partialMovedFields = oldPartialMoved
|
||||||
ctx.extraFuncs.add(f)
|
ctx.extraFuncs.add(f)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[Package]
|
[Package]
|
||||||
Name = "buxc"
|
Name = "buxc"
|
||||||
Version = "0.3.0"
|
Version = "1.0.0"
|
||||||
Type = "bin"
|
Type = "bin"
|
||||||
|
|
||||||
[Build]
|
[Build]
|
||||||
|
|||||||
+17
-19
@@ -1,10 +1,11 @@
|
|||||||
# Bux — План към „добър“ език (v0.5 → v1.0)
|
# Bux — План към „добър“ език (v0.5 → **v1.0.0** ✅)
|
||||||
|
|
||||||
> **Дата:** 2026-07-23
|
> **Дата:** 2026-07-27
|
||||||
> **Текущо:** v0.5.x — `:type` macros + Array_Reverse (session 87)
|
> **Текущо:** **v1.0.0** — language freeze (session 88 / release)
|
||||||
> **Цел:** Език, с който се пишат реални проекти комфортно, безопасно (по избор) и с надежден toolchain.
|
> **Цел:** Език, с който се пишат реални проекти комфортно, безопасно (по избор) и с надежден toolchain.
|
||||||
> **Платформен фокус:** **Linux** (primary) · **cloud-native** (servers, containers, HTTP) · **embedded** (cross, freestanding-ish, CTFE).
|
> **Платформен фокус:** **Linux** (primary) · **cloud-native** (servers, containers, HTTP) · **embedded** (cross, freestanding-ish, CTFE).
|
||||||
> **Не-цел:** MS Windows като product platform (исторически CI/hello smoke остават; няма roadmap investment).
|
> **Не-цел:** MS Windows като product platform (исторически CI/hello smoke остават; няма roadmap investment).
|
||||||
|
> **Release notes:** `docs/RELEASE_v1.0.0.md` · **Semver:** `docs/SEMVER.md` (active).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -113,7 +114,7 @@ A (stdlib ergonomics) → B (compiler holes) → C (ownership depth)
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Acceptance criteria за „добър v1.0“
|
## Acceptance criteria за „добър v1.0“ — **met; tagged v1.0.0**
|
||||||
|
|
||||||
- [x] Всички examples + apps + selfhost smoke на CI (`make test` via `.github/workflows/ci.yml`); selfhost-loop optional
|
- [x] Всички examples + apps + selfhost smoke на CI (`make test` via `.github/workflows/ci.yml`); selfhost-loop optional
|
||||||
- [x] Array/Map/String/Test API покрива 90% от ежедневните нужди (+ Insert/Remove/Clone/case/GetOr)
|
- [x] Array/Map/String/Test API покрива 90% от ежедневните нужди (+ Insert/Remove/Clone/case/GetOr)
|
||||||
@@ -121,6 +122,7 @@ A (stdlib ergonomics) → B (compiler holes) → C (ownership depth)
|
|||||||
- [x] `bux test` + `bux fmt` + `bux check` са default developer loop (`--filter` / `--check` shipped)
|
- [x] `bux test` + `bux fmt` + `bux check` са default developer loop (`--filter` / `--check` shipped)
|
||||||
- [x] LanguageRef синхронизиран с компилатора (incl. C.1 elision)
|
- [x] LanguageRef синхронизиран с компилатора (incl. C.1 elision)
|
||||||
- [x] Поне един външен/temp проект build-ва с registry dep (`tools/smoke_registry.sh` + HTTP)
|
- [x] Поне един външен/temp проект build-ва с registry dep (`tools/smoke_registry.sh` + HTTP)
|
||||||
|
- [x] Version strings + SEMVER active + `docs/RELEASE_v1.0.0.md` (session 88)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1461,22 +1463,18 @@ bootstrap + **buxc2** `macro_tt_raw` (incl. slice) PASS.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Следващи стъпки
|
## Сесия 88 (v1.0.0 language freeze)
|
||||||
|
|
||||||
### P0 — Compiler / language
|
1. Version banners: bootstrap + selfhost CLI → **1.0.0**; root `bux.toml` → 1.0.0
|
||||||
|
2. `docs/SEMVER.md` — **Active** (post-1.0 MAJOR = breaking)
|
||||||
|
3. `docs/RELEASE_v1.0.0.md` — freeze notes + verify commands
|
||||||
|
4. README / QUALITY_PLAN / ROADMAP status → v1.0.0
|
||||||
|
5. Tag `v1.0.0` after `make test` gate
|
||||||
|
|
||||||
1. ~~… through session 86~~ ✅
|
**Post-1.0 backlog (MINOR, not freeze blockers):**
|
||||||
2. ~~**`:type` macro fragments**~~ ✅ session 87
|
- Generics in `:type` (`Array<int>`); operators-only tt paste
|
||||||
3. Optional: richer free-form (operators-only tt); generics in `:type` (`Array<int>`)
|
- `runtime_freestanding.c` + Cortex-M research
|
||||||
|
- LSP / IDE versioning independent of language MAJOR
|
||||||
### P1 — Linux / cloud-native
|
|
||||||
|
|
||||||
4. ~~(sessions 75–81)~~ ✅
|
|
||||||
|
|
||||||
### P2 — Embedded / cross
|
|
||||||
|
|
||||||
5. ~~riscv64 smoke + freestanding notes~~ ✅ session 85
|
|
||||||
6. Optional: real `runtime_freestanding.c` + Cortex-M qemu — not v1.0
|
|
||||||
|
|
||||||
### Изрично **не** правим
|
### Изрично **не** правим
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# Bux v1.0.0 — Language Freeze
|
||||||
|
|
||||||
|
**Date:** 2026-07-27
|
||||||
|
**Tag:** `v1.0.0`
|
||||||
|
|
||||||
|
## What 1.0 means
|
||||||
|
|
||||||
|
- **Language freeze** for public surface: syntax, stdlib APIs, and CLI that
|
||||||
|
scripts depend on follow `docs/SEMVER.md`.
|
||||||
|
- **Normative spec:** `docs/LanguageRef.md`.
|
||||||
|
- **Compilers:** bootstrap (`buxc`, Nim) and selfhost (`buxc2`, Bux) both target
|
||||||
|
the same language for the shipped feature set.
|
||||||
|
- **Platform focus:** Linux primary; cloud/containers; cross/static/minimal
|
||||||
|
runtime. Windows is not a product platform.
|
||||||
|
|
||||||
|
## Shipped surface (summary)
|
||||||
|
|
||||||
|
| Area | Highlights |
|
||||||
|
|------|------------|
|
||||||
|
| Frontend | Pratt parser, recovery, macros (`macro!`, fragments, juxta/`tt`/`type`) |
|
||||||
|
| Types | Generics (mono), tuples, fat `func` ABI, algebraic enums |
|
||||||
|
| Ownership | `@[Checked]`, `@[Release]`, Drop/RAII, field-move + remaining Drop |
|
||||||
|
| Concurrency | M:N tasks, channels, async |
|
||||||
|
| Stdlib | Array/Map/Set/String/Iter HOF, Net/TLS, registry, Test |
|
||||||
|
| Tooling | `fmt`, `test`, `doc`, `check`, LSP (separate versioning), DWARF `#line` |
|
||||||
|
| Ecosystem | path/git/HTTP registry, lock + `--locked`, 4 apps, benches |
|
||||||
|
|
||||||
|
## Explicitly *not* 1.0 blockers
|
||||||
|
|
||||||
|
- True freestanding / bare-metal (`runtime_freestanding.c`, Cortex-M)
|
||||||
|
- Operators-only token paste; full `Array<int>` in `:type` fragments
|
||||||
|
- Windows product investment
|
||||||
|
- LLVM / non-C backends
|
||||||
|
|
||||||
|
## Upgrade notes from 0.5.x
|
||||||
|
|
||||||
|
No mandatory source migration for projects that already build on late 0.5.x.
|
||||||
|
New package scaffolds still default to package version `0.1.0` (package semver,
|
||||||
|
not language version).
|
||||||
|
|
||||||
|
### Fix included in 1.0.0
|
||||||
|
|
||||||
|
- **Closure Drop isolation:** lowering a nested closure no longer emits
|
||||||
|
outer-function `Array_Drop` / auto-Drop on the closure’s return path
|
||||||
|
(broke `iter_hof` and any capturing HOF over droppable locals).
|
||||||
|
|
||||||
|
## Verify
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make build
|
||||||
|
./buxc --version # bux 1.0.0 (bootstrap)
|
||||||
|
make test # full gate
|
||||||
|
make selfhost # buxc2
|
||||||
|
# optional: make selfhost-loop
|
||||||
|
```
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
# Bux Language Roadmap — New Constructs
|
# Bux Language Roadmap — New Constructs
|
||||||
|
|
||||||
> **Updated:** 2026-07-15 | **Status:** In Progress
|
> **Updated:** 2026-07-27 | **Status:** ✅ Constructs for v1.0 shipped (language freeze)
|
||||||
> Recent: multi-instance closures (fat `BuxFn`), tuples in selfhost, Iter map/filter/fold, Rust-style diagnostics.
|
> Recent: macros (`tt`/`type`/juxta), stdlib daily APIs, Linux/riscv smoke; see `docs/RELEASE_v1.0.0.md`.
|
||||||
|
|
||||||
This document tracks planned language constructs beyond Phase 8 strategy.
|
This document tracks planned language constructs beyond Phase 8 strategy.
|
||||||
|
|
||||||
|
|||||||
+18
-15
@@ -1,6 +1,6 @@
|
|||||||
# Bux Semantic Versioning Policy
|
# Bux Semantic Versioning Policy
|
||||||
|
|
||||||
> Status: Draft for v0.x → v1.0 freeze (E.3)
|
> Status: **Active** as of **v1.0.0** (language freeze)
|
||||||
|
|
||||||
Bux follows [Semantic Versioning 2.0.0](https://semver.org/) with the
|
Bux follows [Semantic Versioning 2.0.0](https://semver.org/) with the
|
||||||
clarifications below.
|
clarifications below.
|
||||||
@@ -19,18 +19,18 @@ MAJOR.MINOR.PATCH[-prerelease]
|
|||||||
| **MINOR** | Backward-compatible features |
|
| **MINOR** | Backward-compatible features |
|
||||||
| **PATCH** | Backward-compatible bug fixes |
|
| **PATCH** | Backward-compatible bug fixes |
|
||||||
|
|
||||||
During **0.x** (pre-1.0):
|
### Before 1.0 (historical 0.x)
|
||||||
|
|
||||||
- `0.MINOR.PATCH` — MINOR may still introduce breaking changes (documented in
|
During **0.x**, MINOR could still introduce breaking changes (documented in
|
||||||
the release notes and `MIGRATION_*.sh` when needed).
|
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):
|
### From **1.0.0** (language freeze)
|
||||||
|
|
||||||
- Breaking changes require a MAJOR bump and a migration guide.
|
- Breaking changes require a **MAJOR** bump and a migration guide.
|
||||||
- The Language Reference is the normative spec; compiler bugs that contradict
|
- The **Language Reference** (`docs/LanguageRef.md`) is the normative spec;
|
||||||
the ref are fixed without a MAJOR bump.
|
compiler bugs that contradict the ref are fixed without a MAJOR bump.
|
||||||
|
- New diagnostics and stricter `@[Checked]` are allowed in MINOR when
|
||||||
|
documented; prefer gating with attributes when practical.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -48,12 +48,14 @@ After **1.0.0** (language freeze):
|
|||||||
(prefer contextual keywords)
|
(prefer contextual keywords)
|
||||||
- New diagnostics / stricter `@[Checked]` (document; may be gated)
|
- New diagnostics / stricter `@[Checked]` (document; may be gated)
|
||||||
- Formatter whitespace-only changes
|
- Formatter whitespace-only changes
|
||||||
|
- New optional CLI flags and commands
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Package versions (registry)
|
## Package versions (registry)
|
||||||
|
|
||||||
Registry packages use the same MAJOR.MINOR.PATCH scheme.
|
Registry packages use the same MAJOR.MINOR.PATCH scheme (independent of the
|
||||||
|
compiler version).
|
||||||
|
|
||||||
`bux add foo` / `bux add foo 0.1` resolution:
|
`bux add foo` / `bux add foo 0.1` resolution:
|
||||||
|
|
||||||
@@ -70,7 +72,8 @@ Lockfiles pin the **resolved** version and source path/URL.
|
|||||||
## Release checklist (maintainers)
|
## Release checklist (maintainers)
|
||||||
|
|
||||||
1. Update `docs/LanguageRef.md` if behaviour changed
|
1. Update `docs/LanguageRef.md` if behaviour changed
|
||||||
2. Update `docs/QUALITY_PLAN.md` / changelog notes
|
2. Update `docs/QUALITY_PLAN.md` / release notes
|
||||||
3. Run `make test` (includes `fmt-check`, examples, goldens)
|
3. Bump compiler version strings (`bootstrap/cli.nim`, `src/cli.bux`, root `bux.toml`)
|
||||||
4. Run `make selfhost-loop`
|
4. Run `make test` (includes `fmt-check`, examples, goldens)
|
||||||
5. Tag `vMAJOR.MINOR.PATCH`
|
5. Run `make selfhost` and preferably `make selfhost-loop`
|
||||||
|
6. Tag `vMAJOR.MINOR.PATCH` and push the tag
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ func HealthBody() -> String {
|
|||||||
func BuildHttp(body: String) -> String {
|
func BuildHttp(body: String) -> String {
|
||||||
let n: int = String_Len(body) as int;
|
let n: int = String_Len(body) as int;
|
||||||
return String_Concat(
|
return String_Concat(
|
||||||
String_Concat(
|
String_Concat(
|
||||||
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: ",
|
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: ",
|
||||||
String_FromInt(n as int64)
|
String_FromInt(n as int64)
|
||||||
),
|
),
|
||||||
String_Concat("\r\nConnection: close\r\n\r\n", body)
|
String_Concat("\r\nConnection: close\r\n\r\n", body)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ macro! double_each_sum {
|
|||||||
( $($x:expr),* ) => {
|
( $($x:expr),* ) => {
|
||||||
var __t: int = 0;
|
var __t: int = 0;
|
||||||
$(
|
$(
|
||||||
$( __t = __t + $x; )*
|
$( __t = __t + $x; )*
|
||||||
$( __t = __t + $x; )*
|
$( __t = __t + $x; )*
|
||||||
)*
|
)*
|
||||||
__t
|
__t
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-12
@@ -59,8 +59,8 @@ module Cli {
|
|||||||
return "rt/runtime_win.c";
|
return "rt/runtime_win.c";
|
||||||
}
|
}
|
||||||
if String_Eq(env, "minimal") || String_Eq(env, "thin") ||
|
if String_Eq(env, "minimal") || String_Eq(env, "thin") ||
|
||||||
String_Eq(env, "embed") || String_Eq(env, "embedded") ||
|
String_Eq(env, "embed") || String_Eq(env, "embedded") ||
|
||||||
String_Eq(env, "freestanding") {
|
String_Eq(env, "freestanding") {
|
||||||
return "rt/runtime_minimal.c";
|
return "rt/runtime_minimal.c";
|
||||||
}
|
}
|
||||||
if String_Eq(env, "full") || String_Eq(env, "posix") {
|
if String_Eq(env, "full") || String_Eq(env, "posix") {
|
||||||
@@ -141,7 +141,7 @@ module Cli {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Cli_LinkProgram(cFile: String, outBin: String, projectDir: String,
|
func Cli_LinkProgram(cFile: String, outBin: String, projectDir: String,
|
||||||
targetTriple: String, isRelease: bool, isStatic: bool) -> int {
|
targetTriple: String, isRelease: bool, isStatic: bool) -> int {
|
||||||
let relRt: String = Cli_RuntimeRel(isStatic, targetTriple);
|
let relRt: String = Cli_RuntimeRel(isStatic, targetTriple);
|
||||||
let rtPath: String = Cli_FindRtFile(projectDir, relRt);
|
let rtPath: String = Cli_FindRtFile(projectDir, relRt);
|
||||||
let ioPath: String = Cli_FindRtFile(projectDir, "rt/io.c");
|
let ioPath: String = Cli_FindRtFile(projectDir, "rt/io.c");
|
||||||
@@ -1179,8 +1179,8 @@ func Cli_PackageChecksum(dir: String) -> String {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
let cmd: String = String_Concat(
|
let cmd: String = String_Concat(
|
||||||
"find \"",
|
"find \"",
|
||||||
String_Concat(dir, "\" -name '*.bux' -type f 2>/dev/null | LC_ALL=C sort | xargs cat 2>/dev/null | sha1sum | awk '{print $1}'")
|
String_Concat(dir, "\" -name '*.bux' -type f 2>/dev/null | LC_ALL=C sort | xargs cat 2>/dev/null | sha1sum | awk '{print $1}'")
|
||||||
);
|
);
|
||||||
let out: String = bux_process_output(cmd);
|
let out: String = bux_process_output(cmd);
|
||||||
if out == null as String { return ""; }
|
if out == null as String { return ""; }
|
||||||
@@ -1205,7 +1205,7 @@ func Cli_DepResolvedPath(projectDir: String, depName: String, depUrl: String) ->
|
|||||||
return depUrl;
|
return depUrl;
|
||||||
}
|
}
|
||||||
if String_StartsWith(depUrl, "http://") || String_StartsWith(depUrl, "https://") ||
|
if String_StartsWith(depUrl, "http://") || String_StartsWith(depUrl, "https://") ||
|
||||||
String_EndsWith(depUrl, ".git") {
|
String_EndsWith(depUrl, ".git") {
|
||||||
return bux_path_join(bux_path_join(projectDir, "deps"), depName);
|
return bux_path_join(bux_path_join(projectDir, "deps"), depName);
|
||||||
}
|
}
|
||||||
// relative path
|
// relative path
|
||||||
@@ -1245,7 +1245,7 @@ func Cli_InstallLocked(projectDir: String) -> int {
|
|||||||
path = bux_path_join(projectDir, source);
|
path = bux_path_join(projectDir, source);
|
||||||
}
|
}
|
||||||
if String_StartsWith(source, "http://") || String_StartsWith(source, "https://") ||
|
if String_StartsWith(source, "http://") || String_StartsWith(source, "https://") ||
|
||||||
String_EndsWith(source, ".git") {
|
String_EndsWith(source, ".git") {
|
||||||
path = bux_path_join(bux_path_join(projectDir, "deps"), name);
|
path = bux_path_join(bux_path_join(projectDir, "deps"), name);
|
||||||
}
|
}
|
||||||
if !DirExists(path) {
|
if !DirExists(path) {
|
||||||
@@ -1342,7 +1342,7 @@ func Cli_Install(projectDir: String, lockedOnly: bool) -> int {
|
|||||||
|
|
||||||
// Fetch git deps into deps/<name> when missing
|
// Fetch git deps into deps/<name> when missing
|
||||||
if String_StartsWith(depUrl, "http://") || String_StartsWith(depUrl, "https://") ||
|
if String_StartsWith(depUrl, "http://") || String_StartsWith(depUrl, "https://") ||
|
||||||
String_EndsWith(depUrl, ".git") {
|
String_EndsWith(depUrl, ".git") {
|
||||||
let depsDir: String = bux_path_join(projectDir, "deps");
|
let depsDir: String = bux_path_join(projectDir, "deps");
|
||||||
discard bux_mkdir_if_needed(depsDir);
|
discard bux_mkdir_if_needed(depsDir);
|
||||||
let depPath: String = bux_path_join(depsDir, depName);
|
let depPath: String = bux_path_join(depsDir, depName);
|
||||||
@@ -2254,7 +2254,7 @@ func Cli_Run(args: *String, argCount: int) -> int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if argCount < 2 {
|
if argCount < 2 {
|
||||||
PrintLine("Bux Self-Hosting Compiler v0.2.0");
|
PrintLine("Bux Self-Hosting Compiler v1.0.0");
|
||||||
PrintLine("Usage: buxc <command> [args]");
|
PrintLine("Usage: buxc <command> [args]");
|
||||||
PrintLine("Commands: build, check, new, init, add, remove, fetch, install, search, fmt, doc, test, run, project, help, version");
|
PrintLine("Commands: build, check, new, init, add, remove, fetch, install, search, fmt, doc, test, run, project, help, version");
|
||||||
PrintLine(" test --filter <name> Only run tests/*.bux whose name contains <name>");
|
PrintLine(" test --filter <name> Only run tests/*.bux whose name contains <name>");
|
||||||
@@ -2272,12 +2272,12 @@ func Cli_Run(args: *String, argCount: int) -> int {
|
|||||||
|
|
||||||
let cmd: String = args[1];
|
let cmd: String = args[1];
|
||||||
if String_Eq(cmd, "version") || String_Eq(cmd, "--version") || String_Eq(cmd, "-v") {
|
if String_Eq(cmd, "version") || String_Eq(cmd, "--version") || String_Eq(cmd, "-v") {
|
||||||
PrintLine("Bux 0.2.0 (self-hosting bootstrap)");
|
PrintLine("Bux 1.0.0 (self-hosting)");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if String_Eq(cmd, "help") || String_Eq(cmd, "--help") || String_Eq(cmd, "-h") {
|
if String_Eq(cmd, "help") || String_Eq(cmd, "--help") || String_Eq(cmd, "-h") {
|
||||||
PrintLine("Bux Self-Hosting Compiler v0.2.0");
|
PrintLine("Bux Self-Hosting Compiler v1.0.0");
|
||||||
PrintLine("Usage: buxc <command> [args]");
|
PrintLine("Usage: buxc <command> [args]");
|
||||||
PrintLine("Commands: build, check, new, init, add, remove, fetch, install, search, fmt, doc, test, run, project, help, version");
|
PrintLine("Commands: build, check, new, init, add, remove, fetch, install, search, fmt, doc, test, run, project, help, version");
|
||||||
PrintLine(" test --filter <name> Only run tests/*.bux whose name contains <name>");
|
PrintLine(" test --filter <name> Only run tests/*.bux whose name contains <name>");
|
||||||
@@ -2330,7 +2330,7 @@ func Cli_Run(args: *String, argCount: int) -> int {
|
|||||||
if argCount >= 4 {
|
if argCount >= 4 {
|
||||||
let a3: String = args[3];
|
let a3: String = args[3];
|
||||||
if !String_Contains(a3, "/") && !String_StartsWith(a3, "http") &&
|
if !String_Contains(a3, "/") && !String_StartsWith(a3, "http") &&
|
||||||
!String_EndsWith(a3, ".git") {
|
!String_EndsWith(a3, ".git") {
|
||||||
return Cli_AddFromRegistry(args[2], a3);
|
return Cli_AddFromRegistry(args[2], a3);
|
||||||
}
|
}
|
||||||
return Cli_Add(args[2], a3);
|
return Cli_Add(args[2], a3);
|
||||||
|
|||||||
+6
-6
@@ -467,7 +467,7 @@ module MacroExpand {
|
|||||||
wrap.child1 = aexp;
|
wrap.child1 = aexp;
|
||||||
// group flag: tuple or non-empty slice lit
|
// group flag: tuple or non-empty slice lit
|
||||||
wrap.boolValue = aexp.kind == ekTuple ||
|
wrap.boolValue = aexp.kind == ekTuple ||
|
||||||
(aexp.kind == ekSlice && aexp.callArgCount > 0);
|
(aexp.kind == ekSlice && aexp.callArgCount > 0);
|
||||||
return wrap;
|
return wrap;
|
||||||
}
|
}
|
||||||
// type — session 87: named / pointer type from call-site expr shape
|
// type — session 87: named / pointer type from call-site expr shape
|
||||||
@@ -845,7 +845,7 @@ module MacroExpand {
|
|||||||
|
|
||||||
// Build callArgs for a call/macro-call, flattening MacroRep and MacroTt groups.
|
// Build callArgs for a call/macro-call, flattening MacroRep and MacroTt groups.
|
||||||
func Macro_SubstCallArgs(oldArgs: *ExprList, env: *MacroEnv, file: String, line: uint32, col: uint32,
|
func Macro_SubstCallArgs(oldArgs: *ExprList, env: *MacroEnv, file: String, line: uint32, col: uint32,
|
||||||
outCount: *int) -> *ExprList {
|
outCount: *int) -> *ExprList {
|
||||||
var first: *ExprList = null as *ExprList;
|
var first: *ExprList = null as *ExprList;
|
||||||
var last: *ExprList = null as *ExprList;
|
var last: *ExprList = null as *ExprList;
|
||||||
var count: int = 0;
|
var count: int = 0;
|
||||||
@@ -895,8 +895,8 @@ module MacroExpand {
|
|||||||
let bound: *Expr = Env_Lookup(env, a.strValue);
|
let bound: *Expr = Env_Lookup(env, a.strValue);
|
||||||
// Bare `$args:tt` group → flatten tuple/slice elements as call args
|
// Bare `$args:tt` group → flatten tuple/slice elements as call args
|
||||||
if bound != null as *Expr && bound.kind == ekMacroTt && bound.boolValue &&
|
if bound != null as *Expr && bound.kind == ekMacroTt && bound.boolValue &&
|
||||||
bound.child1 != null as *Expr &&
|
bound.child1 != null as *Expr &&
|
||||||
(bound.child1.kind == ekTuple || bound.child1.kind == ekSlice) {
|
(bound.child1.kind == ekTuple || bound.child1.kind == ekSlice) {
|
||||||
var tel: *ExprList = bound.child1.callArgs;
|
var tel: *ExprList = bound.child1.callArgs;
|
||||||
while tel != null as *ExprList {
|
while tel != null as *ExprList {
|
||||||
let ce: *Expr = Ast_CloneExpr(tel.expr);
|
let ce: *Expr = Ast_CloneExpr(tel.expr);
|
||||||
@@ -1197,8 +1197,8 @@ module MacroExpand {
|
|||||||
let k1: String = Macro_KindAt(kinds, 1);
|
let k1: String = Macro_KindAt(kinds, 1);
|
||||||
let only: *Expr = expArgs.expr;
|
let only: *Expr = expArgs.expr;
|
||||||
if String_Eq(k0, "ident") && String_Eq(k1, "tt") &&
|
if String_Eq(k0, "ident") && String_Eq(k1, "tt") &&
|
||||||
only != null as *Expr && only.kind == ekCall &&
|
only != null as *Expr && only.kind == ekCall &&
|
||||||
only.child1 != null as *Expr && only.child1.kind == ekIdent {
|
only.child1 != null as *Expr && only.child1.kind == ekIdent {
|
||||||
let callee: *Expr = only.child1;
|
let callee: *Expr = only.child1;
|
||||||
// Build MacroTt group from call args (tuple of elements)
|
// Build MacroTt group from call args (tuple of elements)
|
||||||
let inner: *Expr = bux_alloc(sizeof(Expr)) as *Expr;
|
let inner: *Expr = bux_alloc(sizeof(Expr)) as *Expr;
|
||||||
|
|||||||
+1
-1
@@ -92,7 +92,7 @@ module Manifest {
|
|||||||
while pi + 4 < pl {
|
while pi + 4 < pl {
|
||||||
// match ASCII 'P','a','t','h'
|
// match ASCII 'P','a','t','h'
|
||||||
if line[pi] == 80 as char8 && line[pi + 1] == 97 as char8 &&
|
if line[pi] == 80 as char8 && line[pi + 1] == 97 as char8 &&
|
||||||
line[pi + 2] == 116 as char8 && line[pi + 3] == 104 as char8 {
|
line[pi + 2] == 116 as char8 && line[pi + 3] == 104 as char8 {
|
||||||
var q1: int = -1;
|
var q1: int = -1;
|
||||||
var qj: uint = pi;
|
var qj: uint = pi;
|
||||||
while qj < pl {
|
while qj < pl {
|
||||||
|
|||||||
+4
-4
@@ -2465,8 +2465,8 @@ module Parser {
|
|||||||
if String_Eq(kname, "lit") { kname = "literal"; }
|
if String_Eq(kname, "lit") { kname = "literal"; }
|
||||||
if String_Eq(kname, "pattern") { kname = "pat"; }
|
if String_Eq(kname, "pattern") { kname = "pat"; }
|
||||||
if !(String_Eq(kname, "expr") || String_Eq(kname, "ident") || String_Eq(kname, "tt")
|
if !(String_Eq(kname, "expr") || String_Eq(kname, "ident") || String_Eq(kname, "tt")
|
||||||
|| String_Eq(kname, "literal") || String_Eq(kname, "block")
|
|| String_Eq(kname, "literal") || String_Eq(kname, "block")
|
||||||
|| String_Eq(kname, "stmt") || String_Eq(kname, "pat") || String_Eq(kname, "type")) {
|
|| String_Eq(kname, "stmt") || String_Eq(kname, "pat") || String_Eq(kname, "type")) {
|
||||||
kname = "expr";
|
kname = "expr";
|
||||||
}
|
}
|
||||||
if nIn == 0 {
|
if nIn == 0 {
|
||||||
@@ -2519,8 +2519,8 @@ module Parser {
|
|||||||
if String_Eq(kname, "lit") { kname = "literal"; }
|
if String_Eq(kname, "lit") { kname = "literal"; }
|
||||||
if String_Eq(kname, "pattern") { kname = "pat"; }
|
if String_Eq(kname, "pattern") { kname = "pat"; }
|
||||||
if !(String_Eq(kname, "expr") || String_Eq(kname, "ident") || String_Eq(kname, "tt")
|
if !(String_Eq(kname, "expr") || String_Eq(kname, "ident") || String_Eq(kname, "tt")
|
||||||
|| String_Eq(kname, "literal") || String_Eq(kname, "block")
|
|| String_Eq(kname, "literal") || String_Eq(kname, "block")
|
||||||
|| String_Eq(kname, "stmt") || String_Eq(kname, "pat") || String_Eq(kname, "type")) {
|
|| String_Eq(kname, "stmt") || String_Eq(kname, "pat") || String_Eq(kname, "type")) {
|
||||||
kname = "expr";
|
kname = "expr";
|
||||||
}
|
}
|
||||||
if rule.paramCount < 9 {
|
if rule.paramCount < 9 {
|
||||||
|
|||||||
+237
-237
@@ -100,8 +100,8 @@ module Registry {
|
|||||||
}
|
}
|
||||||
// prefer curl
|
// prefer curl
|
||||||
let curlCmd: String = String_Concat(
|
let curlCmd: String = String_Concat(
|
||||||
"curl -fsSL",
|
"curl -fsSL",
|
||||||
String_Concat(kflag, String_Concat(" --max-time 30 -o \"", String_Concat(cachePath, String_Concat("\" \"", String_Concat(url, "\"")))))
|
String_Concat(kflag, String_Concat(" --max-time 30 -o \"", String_Concat(cachePath, String_Concat("\" \"", String_Concat(url, "\"")))))
|
||||||
);
|
);
|
||||||
var ok: bool = false;
|
var ok: bool = false;
|
||||||
if bux_system("command -v curl >/dev/null 2>&1") == 0 {
|
if bux_system("command -v curl >/dev/null 2>&1") == 0 {
|
||||||
@@ -112,8 +112,8 @@ module Registry {
|
|||||||
nflag = " --no-check-certificate";
|
nflag = " --no-check-certificate";
|
||||||
}
|
}
|
||||||
let wgetCmd: String = String_Concat(
|
let wgetCmd: String = String_Concat(
|
||||||
"wget -q",
|
"wget -q",
|
||||||
String_Concat(nflag, String_Concat(" -T 30 -O \"", String_Concat(cachePath, String_Concat("\" \"", String_Concat(url, "\"")))))
|
String_Concat(nflag, String_Concat(" -T 30 -O \"", String_Concat(cachePath, String_Concat("\" \"", String_Concat(url, "\"")))))
|
||||||
);
|
);
|
||||||
ok = bux_system(wgetCmd) == 0 && Reg_FileExists(cachePath);
|
ok = bux_system(wgetCmd) == 0 && Reg_FileExists(cachePath);
|
||||||
}
|
}
|
||||||
@@ -141,240 +141,240 @@ module Registry {
|
|||||||
if String_StartsWith(src, "file:") {
|
if String_StartsWith(src, "file:") {
|
||||||
p = String_Slice(src, 5, bux_strlen(src) - 5);
|
p = String_Slice(src, 5, bux_strlen(src) - 5);
|
||||||
if String_StartsWith(p, "//") {
|
if String_StartsWith(p, "//") {
|
||||||
p = String_Slice(p, 2, bux_strlen(p) - 2);
|
p = String_Slice(p, 2, bux_strlen(p) - 2);
|
||||||
}
|
|
||||||
} else if String_StartsWith(src, "path:") {
|
|
||||||
p = String_Slice(src, 5, bux_strlen(src) - 5);
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
if String_StartsWith(p, "/") {
|
} else if String_StartsWith(src, "path:") {
|
||||||
return p;
|
p = String_Slice(src, 5, bux_strlen(src) - 5);
|
||||||
}
|
} else {
|
||||||
return bux_path_join(indexDir, p);
|
return "";
|
||||||
}
|
}
|
||||||
|
if String_StartsWith(p, "/") {
|
||||||
func Reg_SetPkg(reg: *Registry, idx: int, pkg: RegistryPackage) {
|
return p;
|
||||||
if idx == 0 { reg.p0 = pkg; }
|
|
||||||
else if idx == 1 { reg.p1 = pkg; }
|
|
||||||
else if idx == 2 { reg.p2 = pkg; }
|
|
||||||
else if idx == 3 { reg.p3 = pkg; }
|
|
||||||
else if idx == 4 { reg.p4 = pkg; }
|
|
||||||
else if idx == 5 { reg.p5 = pkg; }
|
|
||||||
else if idx == 6 { reg.p6 = pkg; }
|
|
||||||
else if idx == 7 { reg.p7 = pkg; }
|
|
||||||
else if idx == 8 { reg.p8 = pkg; }
|
|
||||||
else if idx == 9 { reg.p9 = pkg; }
|
|
||||||
else if idx == 10 { reg.p10 = pkg; }
|
|
||||||
else if idx == 11 { reg.p11 = pkg; }
|
|
||||||
else if idx == 12 { reg.p12 = pkg; }
|
|
||||||
else if idx == 13 { reg.p13 = pkg; }
|
|
||||||
else if idx == 14 { reg.p14 = pkg; }
|
|
||||||
else if idx == 15 { reg.p15 = pkg; }
|
|
||||||
}
|
|
||||||
|
|
||||||
func Reg_GetPkg(reg: Registry, idx: int) -> RegistryPackage {
|
|
||||||
if idx == 0 { return reg.p0; }
|
|
||||||
if idx == 1 { return reg.p1; }
|
|
||||||
if idx == 2 { return reg.p2; }
|
|
||||||
if idx == 3 { return reg.p3; }
|
|
||||||
if idx == 4 { return reg.p4; }
|
|
||||||
if idx == 5 { return reg.p5; }
|
|
||||||
if idx == 6 { return reg.p6; }
|
|
||||||
if idx == 7 { return reg.p7; }
|
|
||||||
if idx == 8 { return reg.p8; }
|
|
||||||
if idx == 9 { return reg.p9; }
|
|
||||||
if idx == 10 { return reg.p10; }
|
|
||||||
if idx == 11 { return reg.p11; }
|
|
||||||
if idx == 12 { return reg.p12; }
|
|
||||||
if idx == 13 { return reg.p13; }
|
|
||||||
if idx == 14 { return reg.p14; }
|
|
||||||
return reg.p15;
|
|
||||||
}
|
|
||||||
|
|
||||||
func Reg_ParseContent(content: String, indexPath: String) -> Registry {
|
|
||||||
var reg: Registry;
|
|
||||||
reg.path = indexPath;
|
|
||||||
reg.sourceUrl = "";
|
|
||||||
reg.count = 0;
|
|
||||||
let indexDir: String = bux_path_parent(indexPath);
|
|
||||||
var cur: RegistryPackage;
|
|
||||||
var inPkg: bool = false;
|
|
||||||
let nlines: uint = String_SplitCount(content, "\n");
|
|
||||||
var i: uint = 0;
|
|
||||||
while i <= nlines {
|
|
||||||
var line: String = "";
|
|
||||||
if i < nlines {
|
|
||||||
line = String_Trim(String_SplitPart(content, "\n", i));
|
|
||||||
}
|
|
||||||
let flush: bool = (i == nlines) || String_Eq(line, "[[package]]") || String_Eq(line, "[[Package]]");
|
|
||||||
if flush && inPkg && !String_Eq(cur.name, "") {
|
|
||||||
cur.resolvedPath = Reg_ResolveSource(cur.source, indexDir);
|
|
||||||
if reg.count < 16 {
|
|
||||||
Reg_SetPkg(®, reg.count, cur);
|
|
||||||
reg.count = reg.count + 1;
|
|
||||||
}
|
|
||||||
cur.name = "";
|
|
||||||
cur.version = "";
|
|
||||||
cur.source = "";
|
|
||||||
cur.description = "";
|
|
||||||
cur.resolvedPath = "";
|
|
||||||
}
|
|
||||||
if i == nlines { break; }
|
|
||||||
if String_Eq(line, "") || String_StartsWith(line, "#") {
|
|
||||||
i = i + 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if String_Eq(line, "[[package]]") || String_Eq(line, "[[Package]]") {
|
|
||||||
inPkg = true;
|
|
||||||
i = i + 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if !inPkg {
|
|
||||||
i = i + 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let eqc: uint = String_SplitCount(line, "=");
|
|
||||||
if eqc >= 2 {
|
|
||||||
let key: String = String_Trim(String_SplitPart(line, "=", 0));
|
|
||||||
let val: String = Reg_StripQuotes(String_SplitPart(line, "=", 1));
|
|
||||||
// lowercase-ish compare for common keys
|
|
||||||
if String_Eq(key, "name") || String_Eq(key, "Name") {
|
|
||||||
cur.name = val;
|
|
||||||
} else if String_Eq(key, "version") || String_Eq(key, "Version") {
|
|
||||||
cur.version = val;
|
|
||||||
} else if String_Eq(key, "source") || String_Eq(key, "Source") {
|
|
||||||
cur.source = val;
|
|
||||||
} else if String_Eq(key, "description") || String_Eq(key, "Description") {
|
|
||||||
cur.description = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i = i + 1;
|
|
||||||
}
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
|
|
||||||
func Reg_FindIndex() -> Registry {
|
|
||||||
var reg: Registry;
|
|
||||||
reg.path = "";
|
|
||||||
reg.sourceUrl = "";
|
|
||||||
reg.count = 0;
|
|
||||||
let env: String = bux_getenv("BUX_REGISTRY");
|
|
||||||
if env != null as String && !String_Eq(env, "") {
|
|
||||||
if Reg_IsHttpUrl(env) {
|
|
||||||
let local: String = Reg_FetchHttp(env);
|
|
||||||
if String_Eq(local, "") {
|
|
||||||
reg.sourceUrl = env;
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
let content: String = bux_read_file(local);
|
|
||||||
reg = Reg_ParseContent(content, local);
|
|
||||||
reg.sourceUrl = env;
|
|
||||||
reg.path = local;
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
if Reg_FileExists(env) {
|
|
||||||
let content: String = bux_read_file(env);
|
|
||||||
reg = Reg_ParseContent(content, env);
|
|
||||||
reg.path = env;
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let home: String = bux_getenv("HOME");
|
|
||||||
if home != null as String && !String_Eq(home, "") {
|
|
||||||
let homeIdx: String = bux_path_join(bux_path_join(home, ".bux"), "registry.toml");
|
|
||||||
if Reg_FileExists(homeIdx) {
|
|
||||||
let content: String = bux_read_file(homeIdx);
|
|
||||||
reg = Reg_ParseContent(content, homeIdx);
|
|
||||||
reg.path = homeIdx;
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// cwd-relative candidates
|
|
||||||
let cwd: String = bux_getcwd();
|
|
||||||
var c0: String = bux_path_join(cwd, "config/registry.toml");
|
|
||||||
if Reg_FileExists(c0) {
|
|
||||||
let content: String = bux_read_file(c0);
|
|
||||||
reg = Reg_ParseContent(content, c0);
|
|
||||||
reg.path = c0;
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
c0 = bux_path_join(cwd, "../config/registry.toml");
|
|
||||||
if Reg_FileExists(c0) {
|
|
||||||
let content: String = bux_read_file(c0);
|
|
||||||
reg = Reg_ParseContent(content, c0);
|
|
||||||
reg.path = c0;
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
c0 = bux_path_join(cwd, "../../config/registry.toml");
|
|
||||||
if Reg_FileExists(c0) {
|
|
||||||
let content: String = bux_read_file(c0);
|
|
||||||
reg = Reg_ParseContent(content, c0);
|
|
||||||
reg.path = c0;
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
|
|
||||||
func Reg_VersionOk(have: String, req: String) -> bool {
|
|
||||||
if String_Eq(req, "") || String_Eq(req, "*") { return true; }
|
|
||||||
return String_Eq(have, req);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup by name; versionReq "*" = last matching entry (highest listed last).
|
|
||||||
func Reg_Lookup(reg: Registry, name: String, versionReq: String) -> RegistryPackage {
|
|
||||||
var found: RegistryPackage;
|
|
||||||
found.name = "";
|
|
||||||
var i: int = 0;
|
|
||||||
while i < reg.count {
|
|
||||||
let p: RegistryPackage = Reg_GetPkg(reg, i);
|
|
||||||
if String_Eq(p.name, name) && Reg_VersionOk(p.version, versionReq) {
|
|
||||||
found = p;
|
|
||||||
}
|
|
||||||
i = i + 1;
|
|
||||||
}
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
func Reg_Search(reg: Registry, query: String) -> int {
|
|
||||||
// print hits; return count
|
|
||||||
var hits: int = 0;
|
|
||||||
// Dedupe by name: keep last version
|
|
||||||
// Simple O(n^2): for each pkg, if last occurrence of name, print
|
|
||||||
var i: int = 0;
|
|
||||||
while i < reg.count {
|
|
||||||
let p: RegistryPackage = Reg_GetPkg(reg, i);
|
|
||||||
var isLast: bool = true;
|
|
||||||
var j: int = i + 1;
|
|
||||||
while j < reg.count {
|
|
||||||
let q: RegistryPackage = Reg_GetPkg(reg, j);
|
|
||||||
if String_Eq(q.name, p.name) {
|
|
||||||
isLast = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
j = j + 1;
|
|
||||||
}
|
|
||||||
if isLast {
|
|
||||||
var ok: bool = true;
|
|
||||||
if !String_Eq(query, "") {
|
|
||||||
ok = String_Contains(p.name, query) || String_Contains(p.description, query);
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
Print(" ");
|
|
||||||
Print(p.name);
|
|
||||||
Print(" ");
|
|
||||||
Print(p.version);
|
|
||||||
Print(" — ");
|
|
||||||
if !String_Eq(p.description, "") {
|
|
||||||
PrintLine(p.description);
|
|
||||||
} else {
|
|
||||||
PrintLine(p.source);
|
|
||||||
}
|
|
||||||
hits = hits + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i = i + 1;
|
|
||||||
}
|
|
||||||
return hits;
|
|
||||||
}
|
}
|
||||||
|
return bux_path_join(indexDir, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reg_SetPkg(reg: *Registry, idx: int, pkg: RegistryPackage) {
|
||||||
|
if idx == 0 { reg.p0 = pkg; }
|
||||||
|
else if idx == 1 { reg.p1 = pkg; }
|
||||||
|
else if idx == 2 { reg.p2 = pkg; }
|
||||||
|
else if idx == 3 { reg.p3 = pkg; }
|
||||||
|
else if idx == 4 { reg.p4 = pkg; }
|
||||||
|
else if idx == 5 { reg.p5 = pkg; }
|
||||||
|
else if idx == 6 { reg.p6 = pkg; }
|
||||||
|
else if idx == 7 { reg.p7 = pkg; }
|
||||||
|
else if idx == 8 { reg.p8 = pkg; }
|
||||||
|
else if idx == 9 { reg.p9 = pkg; }
|
||||||
|
else if idx == 10 { reg.p10 = pkg; }
|
||||||
|
else if idx == 11 { reg.p11 = pkg; }
|
||||||
|
else if idx == 12 { reg.p12 = pkg; }
|
||||||
|
else if idx == 13 { reg.p13 = pkg; }
|
||||||
|
else if idx == 14 { reg.p14 = pkg; }
|
||||||
|
else if idx == 15 { reg.p15 = pkg; }
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reg_GetPkg(reg: Registry, idx: int) -> RegistryPackage {
|
||||||
|
if idx == 0 { return reg.p0; }
|
||||||
|
if idx == 1 { return reg.p1; }
|
||||||
|
if idx == 2 { return reg.p2; }
|
||||||
|
if idx == 3 { return reg.p3; }
|
||||||
|
if idx == 4 { return reg.p4; }
|
||||||
|
if idx == 5 { return reg.p5; }
|
||||||
|
if idx == 6 { return reg.p6; }
|
||||||
|
if idx == 7 { return reg.p7; }
|
||||||
|
if idx == 8 { return reg.p8; }
|
||||||
|
if idx == 9 { return reg.p9; }
|
||||||
|
if idx == 10 { return reg.p10; }
|
||||||
|
if idx == 11 { return reg.p11; }
|
||||||
|
if idx == 12 { return reg.p12; }
|
||||||
|
if idx == 13 { return reg.p13; }
|
||||||
|
if idx == 14 { return reg.p14; }
|
||||||
|
return reg.p15;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reg_ParseContent(content: String, indexPath: String) -> Registry {
|
||||||
|
var reg: Registry;
|
||||||
|
reg.path = indexPath;
|
||||||
|
reg.sourceUrl = "";
|
||||||
|
reg.count = 0;
|
||||||
|
let indexDir: String = bux_path_parent(indexPath);
|
||||||
|
var cur: RegistryPackage;
|
||||||
|
var inPkg: bool = false;
|
||||||
|
let nlines: uint = String_SplitCount(content, "\n");
|
||||||
|
var i: uint = 0;
|
||||||
|
while i <= nlines {
|
||||||
|
var line: String = "";
|
||||||
|
if i < nlines {
|
||||||
|
line = String_Trim(String_SplitPart(content, "\n", i));
|
||||||
|
}
|
||||||
|
let flush: bool = (i == nlines) || String_Eq(line, "[[package]]") || String_Eq(line, "[[Package]]");
|
||||||
|
if flush && inPkg && !String_Eq(cur.name, "") {
|
||||||
|
cur.resolvedPath = Reg_ResolveSource(cur.source, indexDir);
|
||||||
|
if reg.count < 16 {
|
||||||
|
Reg_SetPkg(®, reg.count, cur);
|
||||||
|
reg.count = reg.count + 1;
|
||||||
|
}
|
||||||
|
cur.name = "";
|
||||||
|
cur.version = "";
|
||||||
|
cur.source = "";
|
||||||
|
cur.description = "";
|
||||||
|
cur.resolvedPath = "";
|
||||||
|
}
|
||||||
|
if i == nlines { break; }
|
||||||
|
if String_Eq(line, "") || String_StartsWith(line, "#") {
|
||||||
|
i = i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if String_Eq(line, "[[package]]") || String_Eq(line, "[[Package]]") {
|
||||||
|
inPkg = true;
|
||||||
|
i = i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if !inPkg {
|
||||||
|
i = i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let eqc: uint = String_SplitCount(line, "=");
|
||||||
|
if eqc >= 2 {
|
||||||
|
let key: String = String_Trim(String_SplitPart(line, "=", 0));
|
||||||
|
let val: String = Reg_StripQuotes(String_SplitPart(line, "=", 1));
|
||||||
|
// lowercase-ish compare for common keys
|
||||||
|
if String_Eq(key, "name") || String_Eq(key, "Name") {
|
||||||
|
cur.name = val;
|
||||||
|
} else if String_Eq(key, "version") || String_Eq(key, "Version") {
|
||||||
|
cur.version = val;
|
||||||
|
} else if String_Eq(key, "source") || String_Eq(key, "Source") {
|
||||||
|
cur.source = val;
|
||||||
|
} else if String_Eq(key, "description") || String_Eq(key, "Description") {
|
||||||
|
cur.description = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reg_FindIndex() -> Registry {
|
||||||
|
var reg: Registry;
|
||||||
|
reg.path = "";
|
||||||
|
reg.sourceUrl = "";
|
||||||
|
reg.count = 0;
|
||||||
|
let env: String = bux_getenv("BUX_REGISTRY");
|
||||||
|
if env != null as String && !String_Eq(env, "") {
|
||||||
|
if Reg_IsHttpUrl(env) {
|
||||||
|
let local: String = Reg_FetchHttp(env);
|
||||||
|
if String_Eq(local, "") {
|
||||||
|
reg.sourceUrl = env;
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
let content: String = bux_read_file(local);
|
||||||
|
reg = Reg_ParseContent(content, local);
|
||||||
|
reg.sourceUrl = env;
|
||||||
|
reg.path = local;
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
if Reg_FileExists(env) {
|
||||||
|
let content: String = bux_read_file(env);
|
||||||
|
reg = Reg_ParseContent(content, env);
|
||||||
|
reg.path = env;
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let home: String = bux_getenv("HOME");
|
||||||
|
if home != null as String && !String_Eq(home, "") {
|
||||||
|
let homeIdx: String = bux_path_join(bux_path_join(home, ".bux"), "registry.toml");
|
||||||
|
if Reg_FileExists(homeIdx) {
|
||||||
|
let content: String = bux_read_file(homeIdx);
|
||||||
|
reg = Reg_ParseContent(content, homeIdx);
|
||||||
|
reg.path = homeIdx;
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// cwd-relative candidates
|
||||||
|
let cwd: String = bux_getcwd();
|
||||||
|
var c0: String = bux_path_join(cwd, "config/registry.toml");
|
||||||
|
if Reg_FileExists(c0) {
|
||||||
|
let content: String = bux_read_file(c0);
|
||||||
|
reg = Reg_ParseContent(content, c0);
|
||||||
|
reg.path = c0;
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
c0 = bux_path_join(cwd, "../config/registry.toml");
|
||||||
|
if Reg_FileExists(c0) {
|
||||||
|
let content: String = bux_read_file(c0);
|
||||||
|
reg = Reg_ParseContent(content, c0);
|
||||||
|
reg.path = c0;
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
c0 = bux_path_join(cwd, "../../config/registry.toml");
|
||||||
|
if Reg_FileExists(c0) {
|
||||||
|
let content: String = bux_read_file(c0);
|
||||||
|
reg = Reg_ParseContent(content, c0);
|
||||||
|
reg.path = c0;
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reg_VersionOk(have: String, req: String) -> bool {
|
||||||
|
if String_Eq(req, "") || String_Eq(req, "*") { return true; }
|
||||||
|
return String_Eq(have, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup by name; versionReq "*" = last matching entry (highest listed last).
|
||||||
|
func Reg_Lookup(reg: Registry, name: String, versionReq: String) -> RegistryPackage {
|
||||||
|
var found: RegistryPackage;
|
||||||
|
found.name = "";
|
||||||
|
var i: int = 0;
|
||||||
|
while i < reg.count {
|
||||||
|
let p: RegistryPackage = Reg_GetPkg(reg, i);
|
||||||
|
if String_Eq(p.name, name) && Reg_VersionOk(p.version, versionReq) {
|
||||||
|
found = p;
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reg_Search(reg: Registry, query: String) -> int {
|
||||||
|
// print hits; return count
|
||||||
|
var hits: int = 0;
|
||||||
|
// Dedupe by name: keep last version
|
||||||
|
// Simple O(n^2): for each pkg, if last occurrence of name, print
|
||||||
|
var i: int = 0;
|
||||||
|
while i < reg.count {
|
||||||
|
let p: RegistryPackage = Reg_GetPkg(reg, i);
|
||||||
|
var isLast: bool = true;
|
||||||
|
var j: int = i + 1;
|
||||||
|
while j < reg.count {
|
||||||
|
let q: RegistryPackage = Reg_GetPkg(reg, j);
|
||||||
|
if String_Eq(q.name, p.name) {
|
||||||
|
isLast = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
j = j + 1;
|
||||||
|
}
|
||||||
|
if isLast {
|
||||||
|
var ok: bool = true;
|
||||||
|
if !String_Eq(query, "") {
|
||||||
|
ok = String_Contains(p.name, query) || String_Contains(p.description, query);
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
Print(" ");
|
||||||
|
Print(p.name);
|
||||||
|
Print(" ");
|
||||||
|
Print(p.version);
|
||||||
|
Print(" — ");
|
||||||
|
if !String_Eq(p.description, "") {
|
||||||
|
PrintLine(p.description);
|
||||||
|
} else {
|
||||||
|
PrintLine(p.source);
|
||||||
|
}
|
||||||
|
hits = hits + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
return hits;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user