feat: lifetime elision, tooling CI, registry, and LSP locals

Ship the QUALITY_PLAN stretch from ownership through ecosystem: C.1
lifetime elision (bootstrap + selfhost), bux fmt/test/doc CI hooks,
stdlib goldens, package registry (bux search/add), and LSP 0.4
position-sensitive locals with inferred let types. Full-tree format
pass plus Map/Set remove double-free fix.
This commit is contained in:
2026-07-19 16:35:08 +03:00
parent 3eb1ad3a82
commit 53b43b0f79
130 changed files with 20494 additions and 16738 deletions
+54 -3
View File
@@ -3,9 +3,9 @@ SRC := bootstrap/main.nim
OUT := buxc
BUILD_DIR := build
EXAMPLES := hello fibonacci factorial structs enums methods algebraic_enums generics generics_struct generic_infer generic_infer2 extend_generic pattern_matching strings strings2 map result_option try_operator ownership ownership_checked drop_early_return ctfe async concurrency os_time process json iter trait_bounds channel sync jwt stdlib_ergonomics tuples func_ptr map_remove array_iter_extra string_extra multi_closure iter_hof closure_control match_let string_interp iter_generic generic_infer_hof struct_tuple_pat match_block nested_patterns match_guards pattern_shadow
EXAMPLES := hello fibonacci factorial structs enums methods algebraic_enums generics generics_struct generic_infer generic_infer2 extend_generic pattern_matching strings strings2 map result_option try_operator ownership ownership_checked drop_early_return lifetime_elision ctfe async concurrency os_time process json iter trait_bounds channel sync jwt stdlib_ergonomics tuples func_ptr map_remove array_iter_extra string_extra multi_closure iter_hof closure_control match_let string_interp iter_generic generic_infer_hof struct_tuple_pat match_block nested_patterns match_guards pattern_shadow
.PHONY: all build dev debug test clean clean-all test-examples selfhost test-golden test-errors selfhost-loop lsp
.PHONY: all build dev debug test clean clean-all test-examples selfhost test-golden test-errors test-stdlib selfhost-loop lsp fmt-check docs
all: build
@@ -19,7 +19,7 @@ dev:
debug: dev
@echo "Debug binary: buxc_debug"
test: build test-examples test-errors
test: build fmt-check test-examples test-errors test-stdlib
@echo "Running lexer tests..."
$(NIM) c -r tests/lexer_test.nim
@echo "Running parser tests..."
@@ -105,6 +105,43 @@ test-errors: build
@chmod +x tests/error_golden/run.sh
@tests/error_golden/run.sh ./$(OUT)
test-stdlib: build
@echo "=== Stdlib golden tests ==="
@chmod +x tests/stdlib_golden/run.sh
@tests/stdlib_golden/run.sh ./$(OUT)
# Generate stdlib API docs from /// comments → docs/api/stdlib.md
docs: build
@mkdir -p docs/api
@./$(OUT) doc --out docs/api/stdlib.md lib/
@echo "docs/api/stdlib.md updated"
# CI: full-tree format check (lib / examples / src / tests / apps) + dirty-path smoke.
fmt-check: build
@echo "=== fmt --check (full tree) ==="
@./$(OUT) fmt --check lib/
@./$(OUT) fmt --check examples/
@./$(OUT) fmt --check src/
@./$(OUT) fmt --check tests/
@./$(OUT) fmt --check apps/
@echo "=== fmt --check dirty-path smoke ==="
@mkdir -p /tmp/bux_fmt_smoke
@printf 'func Main() -> int {\nreturn 0;\n}\n' > /tmp/bux_fmt_smoke/bad.bux
@if ./$(OUT) fmt --check /tmp/bux_fmt_smoke/bad.bux >/dev/null 2>&1; then \
echo "error: expected --check to fail on dirty file"; exit 1; \
fi
@echo "fmt --check passed (tree clean + dirty exits 1)"
# One-shot reformat of the same trees (run before committing style-only fixes)
.PHONY: fmt
fmt: build
@./$(OUT) fmt lib/
@./$(OUT) fmt examples/
@./$(OUT) fmt src/
@./$(OUT) fmt tests/
@./$(OUT) fmt apps/
@echo "Formatted lib/ examples/ src/ tests/ apps/"
selfhost-loop: build
@echo "=== Selfhost loop: bootstrap determinism check ==="
@echo "Build A..."
@@ -145,3 +182,17 @@ lsp: tools/bux-lsp
tools/bux-lsp: tools/lsp_server.nim bootstrap/*.nim
cd tools && $(NIM) c -d:release --opt:size --path:../bootstrap -o:bux-lsp lsp_server.nim
.PHONY: test-lsp
test-lsp: lsp
@echo "=== LSP unit (locals / inference) ==="
$(NIM) r --path:bootstrap tools/test_lsp_locals.nim
@echo "=== LSP hover smoke ==="
@chmod +x tools/smoke_lsp_hover.sh
@tools/smoke_lsp_hover.sh
.PHONY: test-registry
test-registry: build
@echo "=== Registry smoke (E.1) ==="
@chmod +x tools/smoke_registry.sh
@tools/smoke_registry.sh
+3 -3
View File
@@ -432,7 +432,7 @@ func ReadFile(path: String) -> Result<String, IoError> {
| `8.2.1` `own` keyword | ✅ | `own T` parsed and resolves to `T`; ready for borrow checker integration |
| `8.2.2` `borrow` / `&` | ✅ | `&T` shared reference type checked and enforced |
| `8.2.3` `mut` references | ✅ | `&mut T` mutable reference type checked and enforced |
| `8.2.4` Lifetime elision | | Simple rules for common cases; explicit `'a` for complex |
| `8.2.4` Lifetime elision | | Single-input elision + dangling return; explicit `'a` for multi-input |
| `8.2.5` Opt-in checker | ✅ | `@[Checked]` attribute enables borrow checking: writes through `&T` are rejected |
```bux
@@ -681,8 +681,8 @@ buxc2 == buxc3 ✅ (binary-identical)
| `10.2.3` `&mut T` exclusive mutable check | ✅ | No aliasing of mutable refs |
| `10.2.4` Bounds checking on slices | ✅ | `Slice_Get` / `Array_Get` with `bux_bounds_check` |
| `10.2.5` `@[Release]` zero-cost mode | ✅ | Disables borrow + bounds checks, passes `-O3 -flto` |
| `10.2.6` Lifetime elision (simple rules) | | 80% of cases without annotations |
| `10.2.7` Explicit lifetimes `'a` | | Only for complex cases |
| `10.2.6` Lifetime elision (simple rules) | | Single-input elision; multi-input requires `'a` |
| `10.2.7` Explicit lifetimes `'a` | | Parsed + checked; multi-input + mismatch |
### 10.3 — Compiler Architecture Upgrade (v0.6.0 target)
+26 -5
View File
@@ -246,7 +246,7 @@ func Main() -> int {
| **Package Manager** | `bux add`, `bux install`, `bux.lock`, path + git deps |
| **Cross-Compilation** | `--target <triple>` via clang (e.g. `aarch64-linux-gnu`) |
| **Diagnostics** | Rust-style snippets, multi-char underlines, `= help:` hints |
| **Tooling** | `bux new/build/run/test/check/fmt`, LSP (`tools/lsp_server.nim` + `buxc check`) |
| **Tooling** | `bux new/build/run/test/check/fmt/doc`, LSP 0.4.0 (locals + inferred lets) |
---
@@ -299,6 +299,8 @@ bux/
| [`docs/Stdlib.md`](docs/Stdlib.md) | Standard library API |
| [`docs/BuildAndTest.md`](docs/BuildAndTest.md) | Build, test, and tooling |
| [`docs/QUALITY_PLAN.md`](docs/QUALITY_PLAN.md) | Roadmap toward a “good” v1.0 |
| [`docs/Packages.md`](docs/Packages.md) | Package manager + registry |
| [`docs/SEMVER.md`](docs/SEMVER.md) | Versioning policy |
| [`docs/ROADMAP.md`](docs/ROADMAP.md) | Feature status (constructs) |
| [`PLAN.md`](PLAN.md) | Long-form phase plan |
@@ -319,14 +321,33 @@ make test-errors
# Full unit + example suite
make test
# Full-tree format check (lib/ examples/ src/ tests/ apps/)
make fmt-check
# Reformat those trees
make fmt
# Stdlib behavioral goldens (Array / String / collections)
make test-stdlib
# Generate stdlib API docs from /// comments
make docs
# Build self-hosted compiler (Bux → C → native)
make selfhost
# Run all tests
make test
# Package tests (filter + summary table)
./buxc test --filter first _test_runner
# Run example programs
make test-examples
# Format / CI format check
./buxc fmt path/to/file.bux
./buxc fmt --check path/
# API docs
./buxc doc --out docs/api/stdlib.md lib/
# Package registry
./buxc search greet
make test-registry # add greet → install → build temp app
# Verify selfhost binary parity (buxc2 → buxc3, identical)
make selfhost-loop
+116 -116
View File
@@ -5,143 +5,143 @@
// =============================================================================
module Boko {
import Std::Io::{PrintLine, Print, PrintInt};
import Std::Net::{Net_Create, Net_SetReuse, Net_Bind, Net_Listen, Net_Accept, Net_Send, Net_Recv, Net_Close, Net_LastError};
import Std::String::{
String_Len, String_Eq, String_IsNull,
String_StartsWith, String_Contains,
String_Find, String_Offset, String_Slice,
String_SplitCount, String_SplitPart,
StringBuilder, StringBuilder_New, StringBuilder_Append,
StringBuilder_AppendInt, StringBuilder_Build, StringBuilder_Free
};
import Std::Map::{StringMap, StringMap_New, StringMap_Set, StringMap_Get, StringMap_Has};
import Std::Io::{PrintLine, Print, PrintInt};
import Std::Net::{Net_Create, Net_SetReuse, Net_Bind, Net_Listen, Net_Accept, Net_Send, Net_Recv, Net_Close, Net_LastError};
import Std::String::{
String_Len, String_Eq, String_IsNull,
String_StartsWith, String_Contains,
String_Find, String_Offset, String_Slice,
String_SplitCount, String_SplitPart,
StringBuilder, StringBuilder_New, StringBuilder_Append,
StringBuilder_AppendInt, StringBuilder_Build, StringBuilder_Free
};
import Std::Map::{StringMap, StringMap_New, StringMap_Set, StringMap_Get, StringMap_Has};
// =============================================================================
// HTTP Methods
// =============================================================================
enum HttpVerb {
GET,
POST,
PUT,
DELETE,
PATCH,
HEAD,
OPTIONS,
}
// =============================================================================
// HTTP Methods
// =============================================================================
enum HttpVerb {
GET,
POST,
PUT,
DELETE,
PATCH,
HEAD,
OPTIONS,
}
func HttpVerb_MethodName(verb: HttpVerb) -> String {
if verb.tag == HttpVerb_GET { return "GET"; }
if verb.tag == HttpVerb_POST { return "POST"; }
if verb.tag == HttpVerb_PUT { return "PUT"; }
if verb.tag == HttpVerb_DELETE { return "DELETE"; }
if verb.tag == HttpVerb_PATCH { return "PATCH"; }
if verb.tag == HttpVerb_HEAD { return "HEAD"; }
if verb.tag == HttpVerb_OPTIONS { return "OPTIONS"; }
return "?";
}
func HttpVerb_MethodName(verb: HttpVerb) -> String {
if verb.tag == HttpVerb_GET { return "GET"; }
if verb.tag == HttpVerb_POST { return "POST"; }
if verb.tag == HttpVerb_PUT { return "PUT"; }
if verb.tag == HttpVerb_DELETE { return "DELETE"; }
if verb.tag == HttpVerb_PATCH { return "PATCH"; }
if verb.tag == HttpVerb_HEAD { return "HEAD"; }
if verb.tag == HttpVerb_OPTIONS { return "OPTIONS"; }
return "?";
}
func HttpVerb_Parse(methodStr: String) -> HttpVerb {
if String_Eq(methodStr, "GET") { return HttpVerb { tag: HttpVerb_GET }; }
if String_Eq(methodStr, "POST") { return HttpVerb { tag: HttpVerb_POST }; }
if String_Eq(methodStr, "PUT") { return HttpVerb { tag: HttpVerb_PUT }; }
if String_Eq(methodStr, "DELETE") { return HttpVerb { tag: HttpVerb_DELETE }; }
if String_Eq(methodStr, "PATCH") { return HttpVerb { tag: HttpVerb_PATCH }; }
if String_Eq(methodStr, "HEAD") { return HttpVerb { tag: HttpVerb_HEAD }; }
if String_Eq(methodStr, "OPTIONS") { return HttpVerb { tag: HttpVerb_OPTIONS }; }
return HttpVerb { tag: HttpVerb_GET };
}
func HttpVerb_Parse(methodStr: String) -> HttpVerb {
if String_Eq(methodStr, "GET") { return HttpVerb { tag: HttpVerb_GET }; }
if String_Eq(methodStr, "POST") { return HttpVerb { tag: HttpVerb_POST }; }
if String_Eq(methodStr, "PUT") { return HttpVerb { tag: HttpVerb_PUT }; }
if String_Eq(methodStr, "DELETE") { return HttpVerb { tag: HttpVerb_DELETE }; }
if String_Eq(methodStr, "PATCH") { return HttpVerb { tag: HttpVerb_PATCH }; }
if String_Eq(methodStr, "HEAD") { return HttpVerb { tag: HttpVerb_HEAD }; }
if String_Eq(methodStr, "OPTIONS") { return HttpVerb { tag: HttpVerb_OPTIONS }; }
return HttpVerb { tag: HttpVerb_GET };
}
// =============================================================================
// Request — parsed incoming HTTP request
// =============================================================================
struct Request {
method: HttpVerb,
path: String,
body: String,
headers: StringMap<String>,
query: StringMap<String>,
pathParams: StringMap<String>,
}
// =============================================================================
// Request — parsed incoming HTTP request
// =============================================================================
struct Request {
method: HttpVerb,
path: String,
body: String,
headers: StringMap<String>,
query: StringMap<String>,
pathParams: StringMap<String>,
}
extend Request {
func GetHeader(self: Request, name: String) -> String {
if StringMap_Has<String>(&self.headers, name) {
return StringMap_Get<String>(&self.headers, name);
extend Request {
func GetHeader(self: Request, name: String) -> String {
if StringMap_Has<String>(&self.headers, name) {
return StringMap_Get<String>(&self.headers, name);
}
return "";
}
return "";
}
func GetQuery(self: Request, name: String) -> String {
if StringMap_Has<String>(&self.query, name) {
return StringMap_Get<String>(&self.query, name);
func GetQuery(self: Request, name: String) -> String {
if StringMap_Has<String>(&self.query, name) {
return StringMap_Get<String>(&self.query, name);
}
return "";
}
return "";
}
func HasQuery(self: Request, name: String) -> bool {
return StringMap_Has<String>(&self.query, name);
}
func GetPathParam(self: Request, name: String) -> String {
if StringMap_Has<String>(&self.pathParams, name) {
return StringMap_Get<String>(&self.pathParams, name);
func HasQuery(self: Request, name: String) -> bool {
return StringMap_Has<String>(&self.query, name);
}
func GetPathParam(self: Request, name: String) -> String {
if StringMap_Has<String>(&self.pathParams, name) {
return StringMap_Get<String>(&self.pathParams, name);
}
return "";
}
return "";
}
}
// =============================================================================
// Response — outgoing HTTP response
// =============================================================================
struct Response {
statusCode: int,
contentType: String,
body: String,
extraHeaders: String,
}
// =============================================================================
// Response — outgoing HTTP response
// =============================================================================
struct Response {
statusCode: int,
contentType: String,
body: String,
extraHeaders: String,
}
// --- Constructors ---
func Response_New(status: int, contentType: String, body: String) -> Response {
return Response { statusCode: status, contentType: contentType, body: body, extraHeaders: "" };
}
// --- Constructors ---
func Response_New(status: int, contentType: String, body: String) -> Response {
return Response { statusCode: status, contentType: contentType, body: body, extraHeaders: "" };
}
func Response_Ok(body: String) -> Response {
return Response_New(200, "text/html; charset=utf-8", body);
}
func Response_Ok(body: String) -> Response {
return Response_New(200, "text/html; charset=utf-8", body);
}
func Response_Html(html: String) -> Response {
return Response_New(200, "text/html; charset=utf-8", html);
}
func Response_Html(html: String) -> Response {
return Response_New(200, "text/html; charset=utf-8", html);
}
func Response_Json(json: String) -> Response {
return Response_New(200, "application/json; charset=utf-8", json);
}
func Response_Json(json: String) -> Response {
return Response_New(200, "application/json; charset=utf-8", json);
}
func Response_Text(text: String) -> Response {
return Response_New(200, "text/plain; charset=utf-8", text);
}
func Response_Text(text: String) -> Response {
return Response_New(200, "text/plain; charset=utf-8", text);
}
func Response_Redirect(url: String) -> Response {
let sb: StringBuilder = StringBuilder_New();
StringBuilder_Append(&sb, "Location: ");
StringBuilder_Append(&sb, url);
StringBuilder_Append(&sb, "\r\n");
let headers: String = StringBuilder_Build(&sb);
StringBuilder_Free(&sb);
return Response { statusCode: 302, contentType: "", body: "", extraHeaders: headers };
}
func Response_Redirect(url: String) -> Response {
let sb: StringBuilder = StringBuilder_New();
StringBuilder_Append(&sb, "Location: ");
StringBuilder_Append(&sb, url);
StringBuilder_Append(&sb, "\r\n");
let headers: String = StringBuilder_Build(&sb);
StringBuilder_Free(&sb);
return Response { statusCode: 302, contentType: "", body: "", extraHeaders: headers };
}
func Response_NotFound() -> Response {
return Response_New(404, "application/json; charset=utf-8", "{\"error\":\"not_found\"}");
}
func Response_NotFound() -> Response {
return Response_New(404, "application/json; charset=utf-8", "{\"error\":\"not_found\"}");
}
func Response_Error(status: int, message: String) -> Response {
let sb: StringBuilder = StringBuilder_New();
StringBuilder_Append(&sb, "{\"error\":\"");
StringBuilder_Append(&sb, message);
StringBuilder_Append(&sb, "\"}");
func Response_Error(status: int, message: String) -> Response {
let sb: StringBuilder = StringBuilder_New();
StringBuilder_Append(&sb, "{\"error\":\"");
StringBuilder_Append(&sb, message);
StringBuilder_Append(&sb, "\"}");
let body: String = StringBuilder_Build(&sb);
StringBuilder_Free(&sb);
return Response_New(status, "application/json; charset=utf-8", body);
+85 -85
View File
@@ -3,56 +3,56 @@
// =============================================================================
module Main {
import Std::Io::{PrintLine, Print};
import Std::String::{
String_Eq, String_Len,
StringBuilder, StringBuilder_New, StringBuilder_Append,
StringBuilder_Build, StringBuilder_Free
};
import Boko::{
App, App_New, App_Run,
Request, Response,
Response_Html, Response_Json, Response_NotFound, Response_Redirect,
Path_Match,
HttpVerb
};
import Std::Io::{PrintLine, Print};
import Std::String::{
String_Eq, String_Len,
StringBuilder, StringBuilder_New, StringBuilder_Append,
StringBuilder_Build, StringBuilder_Free
};
import Boko::{
App, App_New, App_Run,
Request, Response,
Response_Html, Response_Json, Response_NotFound, Response_Redirect,
Path_Match,
HttpVerb
};
// =============================================================================
// Boko_Router — user-defined dispatch (called by the framework)
// =============================================================================
func Boko_Router(req: Request) -> Response {
// --- GET / ---
if String_Eq(req.path, "/") && req.method.tag == HttpVerb_GET {
return Response_Html(PageHome());
}
// =============================================================================
// Boko_Router — user-defined dispatch (called by the framework)
// =============================================================================
func Boko_Router(req: Request) -> Response {
// --- GET / ---
if String_Eq(req.path, "/") && req.method.tag == HttpVerb_GET {
return Response_Html(PageHome());
}
// --- GET /api/health ---
if String_Eq(req.path, "/api/health") {
return Response_Json("{\"status\":\"ok\",\"framework\":\"Boko\",\"version\":\"0.2.0\"}");
}
// --- GET /api/health ---
if String_Eq(req.path, "/api/health") {
return Response_Json("{\"status\":\"ok\",\"framework\":\"Boko\",\"version\":\"0.2.0\"}");
}
// --- GET /api/info ---
if String_Eq(req.path, "/api/info") {
return Response_Json("{\"name\":\"Boko\",\"language\":\"Bux\",\"inspiration\":\"FastAPI\",\"features\":[\"routing\",\"path-params\",\"query-params\",\"json\"]}");
}
// --- GET /api/info ---
if String_Eq(req.path, "/api/info") {
return Response_Json("{\"name\":\"Boko\",\"language\":\"Bux\",\"inspiration\":\"FastAPI\",\"features\":[\"routing\",\"path-params\",\"query-params\",\"json\"]}");
}
// --- GET /hello?name=World ---
if String_Eq(req.path, "/hello") {
var name: String = req.GetQuery("name");
if String_Len(name) == 0 { name = "World"; }
let html: String = f"<h1>Hello, {name}!</h1>";
return Response_Html(html);
}
// --- GET /hello?name=World ---
if String_Eq(req.path, "/hello") {
var name: String = req.GetQuery("name");
if String_Len(name) == 0 { name = "World"; }
let html: String = f"<h1>Hello, {name}!</h1>";
return Response_Html(html);
}
// --- GET /users/{id} ---
if Path_Match("/users/{id}", req.path, &req) {
let id: String = req.GetPathParam("id");
let sb: StringBuilder = StringBuilder_New();
StringBuilder_Append(&sb, "{\"id\":");
StringBuilder_Append(&sb, id);
StringBuilder_Append(&sb, ",\"name\":\"User ");
StringBuilder_Append(&sb, id);
StringBuilder_Append(&sb, "\"}");
// --- GET /users/{id} ---
if Path_Match("/users/{id}", req.path, &req) {
let id: String = req.GetPathParam("id");
let sb: StringBuilder = StringBuilder_New();
StringBuilder_Append(&sb, "{\"id\":");
StringBuilder_Append(&sb, id);
StringBuilder_Append(&sb, ",\"name\":\"User ");
StringBuilder_Append(&sb, id);
StringBuilder_Append(&sb, "\"}");
let json: String = StringBuilder_Build(&sb);
StringBuilder_Free(&sb);
return Response_Json(json);
@@ -98,46 +98,46 @@ func Boko_Router(req: Request) -> Response {
// =============================================================================
func PageHome() -> String {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Boko Framework</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:system-ui,sans-serif;background:#0d1117;color:#c9d1d9;min-height:100vh;display:flex;align-items:center;justify-content:center}
.card{background:#161b22;border:1px solid #30363d;border-radius:12px;padding:3rem;max-width:560px;width:100%}
h1{color:#58a6ff;font-size:2rem;margin-bottom:.5rem}
.tag{color:#8b949e;font-size:.9rem;margin-bottom:2rem}
h3{color:#d2a8ff;margin:1.5rem 0 .75rem}
.ep{display:flex;gap:1rem;padding:.35rem 0;font-family:monospace;font-size:.9rem}
.ep .m{color:#3fb950;font-weight:bold;min-width:52px}
.ep .p{color:#c9d1d9}
.ep .d{color:#484f58;margin-left:auto}
a{color:#58a6ff}
</style>
</head>
<body>
<div class="card">
<h1>⚡ Boko</h1>
<p class="tag">Async web framework for Bux — inspired by FastAPI</p>
<h3>Try it</h3>
<div class="ep"><span class="m">GET</span><span class="p"><a href="/hello?name=Bux">/hello?name=Bux</a></span><span class="d">query param</span></div>
<div class="ep"><span class="m">GET</span><span class="p"><a href="/users/42">/users/42</a></span><span class="d">path param</span></div>
<div class="ep"><span class="m">GET</span><span class="p"><a href="/posts/7/comments/3">/posts/7/comments/3</a></span><span class="d">multi params</span></div>
<div class="ep"><span class="m">GET</span><span class="p"><a href="/redirect">/redirect</a></span><span class="d">302 → /</span></div>
<div class="ep"><span class="m">GET</span><span class="p"><a href="/api/health">/api/health</a></span><span class="d">JSON</span></div>
<div class="ep"><span class="m">GET</span><span class="p"><a href="/api/info">/api/info</a></span><span class="d">JSON</span></div>
<h3>Features</h3>
<div class="ep"><span class="m">✓</span><span class="p">Path routing with {params}</span></div>
<div class="ep"><span class="m">✓</span><span class="p">Query parameter extraction</span></div>
<div class="ep"><span class="m">✓</span><span class="p">JSON / HTML / Text responses</span></div>
<div class="ep"><span class="m">✓</span><span class="p">Multi-threaded (configurable)</span></div>
<div class="ep"><span class="m">✓</span><span class="p">Redirects (302)</span></div>
<div class="ep"><span class="m">✓</span><span class="p">POST body access</span></div>
</div>
</body>
</html>`;
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Boko Framework</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:system-ui,sans-serif;background:#0d1117;color:#c9d1d9;min-height:100vh;display:flex;align-items:center;justify-content:center}
.card{background:#161b22;border:1px solid #30363d;border-radius:12px;padding:3rem;max-width:560px;width:100%}
h1{color:#58a6ff;font-size:2rem;margin-bottom:.5rem}
.tag{color:#8b949e;font-size:.9rem;margin-bottom:2rem}
h3{color:#d2a8ff;margin:1.5rem 0 .75rem}
.ep{display:flex;gap:1rem;padding:.35rem 0;font-family:monospace;font-size:.9rem}
.ep .m{color:#3fb950;font-weight:bold;min-width:52px}
.ep .p{color:#c9d1d9}
.ep .d{color:#484f58;margin-left:auto}
a{color:#58a6ff}
</style>
</head>
<body>
<div class="card">
<h1>⚡ Boko</h1>
<p class="tag">Async web framework for Bux — inspired by FastAPI</p>
<h3>Try it</h3>
<div class="ep"><span class="m">GET</span><span class="p"><a href="/hello?name=Bux">/hello?name=Bux</a></span><span class="d">query param</span></div>
<div class="ep"><span class="m">GET</span><span class="p"><a href="/users/42">/users/42</a></span><span class="d">path param</span></div>
<div class="ep"><span class="m">GET</span><span class="p"><a href="/posts/7/comments/3">/posts/7/comments/3</a></span><span class="d">multi params</span></div>
<div class="ep"><span class="m">GET</span><span class="p"><a href="/redirect">/redirect</a></span><span class="d">302 → /</span></div>
<div class="ep"><span class="m">GET</span><span class="p"><a href="/api/health">/api/health</a></span><span class="d">JSON</span></div>
<div class="ep"><span class="m">GET</span><span class="p"><a href="/api/info">/api/info</a></span><span class="d">JSON</span></div>
<h3>Features</h3>
<div class="ep"><span class="m">✓</span><span class="p">Path routing with {params}</span></div>
<div class="ep"><span class="m">✓</span><span class="p">Query parameter extraction</span></div>
<div class="ep"><span class="m">✓</span><span class="p">JSON / HTML / Text responses</span></div>
<div class="ep"><span class="m">✓</span><span class="p">Multi-threaded (configurable)</span></div>
<div class="ep"><span class="m">✓</span><span class="p">Redirects (302)</span></div>
<div class="ep"><span class="m">✓</span><span class="p">POST body access</span></div>
</div>
</body>
</html>`;
}
// =============================================================================
+425 -425
View File
@@ -5,464 +5,464 @@
// =============================================================================
module Main {
import Std::Io::{PrintLine, Print, PrintInt};
import Std::String::{
String_Len, String_Eq,
String_SplitCount, String_SplitPart,
StringBuilder, StringBuilder_New, StringBuilder_Append,
StringBuilder_AppendInt, StringBuilder_Build, StringBuilder_Free
};
import Std::Crypto::Jwt::{
JwtAlg,
Jwt_MakeHeader,
Jwt_Encode,
Jwt_Decode,
Jwt_EncodeHS256, Jwt_EncodeHS384, Jwt_EncodeHS512,
Jwt_EncodeRS256, Jwt_EncodeES256, Jwt_EncodeEdDSA
};
import Std::Crypto::Base64::{Base64URL_Decode, Base64_Encode};
import Std::Crypto::Ed25519::{Ed25519_Keypair};
import Std::Io::{PrintLine, Print, PrintInt};
import Std::String::{
String_Len, String_Eq,
String_SplitCount, String_SplitPart,
StringBuilder, StringBuilder_New, StringBuilder_Append,
StringBuilder_AppendInt, StringBuilder_Build, StringBuilder_Free
};
import Std::Crypto::Jwt::{
JwtAlg,
Jwt_MakeHeader,
Jwt_Encode,
Jwt_Decode,
Jwt_EncodeHS256, Jwt_EncodeHS384, Jwt_EncodeHS512,
Jwt_EncodeRS256, Jwt_EncodeES256, Jwt_EncodeEdDSA
};
import Std::Crypto::Base64::{Base64URL_Decode, Base64_Encode};
import Std::Crypto::Ed25519::{Ed25519_Keypair};
extern func bux_argc() -> int;
extern func bux_argv(index: int) -> String;
extern func bux_alloc(size: uint) -> *void;
extern func bux_read_file(path: String) -> String;
extern func bux_file_exists(path: String) -> int;
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_argc() -> int;
extern func bux_argv(index: int) -> String;
extern func bux_alloc(size: uint) -> *void;
extern func bux_read_file(path: String) -> String;
extern func bux_file_exists(path: String) -> int;
extern func bux_base64_encode(data: String, len: int) -> String;
// =============================================================================
// Constants
// =============================================================================
const AppName: String = "jwt-pitbul";
const Version: String = "0.2.0";
// =============================================================================
// Constants
// =============================================================================
const AppName: String = "jwt-pitbul";
const Version: String = "0.2.0";
// =============================================================================
// Algebraic enums for optionals and results
// =============================================================================
enum AlgOption {
Some(JwtAlg),
None,
}
func AlgOption_MakeSome(value: JwtAlg) -> AlgOption {
let o: AlgOption = AlgOption { tag: AlgOption_Some };
o.data.Some_0 = value;
return o;
}
func AlgOption_MakeNone() -> AlgOption {
return AlgOption { tag: AlgOption_None };
}
enum CmdOption {
Some(Cmd),
None,
}
func CmdOption_MakeSome(value: Cmd) -> CmdOption {
let o: CmdOption = CmdOption { tag: CmdOption_Some };
o.data.Some_0 = value;
return o;
}
func CmdOption_MakeNone() -> CmdOption {
return CmdOption { tag: CmdOption_None };
}
enum KeyTypeOption {
Some(KeyType),
None,
}
func KeyTypeOption_MakeSome(value: KeyType) -> KeyTypeOption {
let o: KeyTypeOption = KeyTypeOption { tag: KeyTypeOption_Some };
o.data.Some_0 = value;
return o;
}
func KeyTypeOption_MakeNone() -> KeyTypeOption {
return KeyTypeOption { tag: KeyTypeOption_None };
}
enum KeyResult {
Ok(String),
Err(String),
}
func KeyResult_MakeOk(value: String) -> KeyResult {
let r: KeyResult = KeyResult { tag: KeyResult_Ok };
r.data.Ok_0 = value;
return r;
}
func KeyResult_MakeErr(msg: String) -> KeyResult {
let r: KeyResult = KeyResult { tag: KeyResult_Err };
r.data.Err_0 = msg;
return r;
}
enum ExitResult {
Ok(int),
Err(String),
}
func ExitResult_MakeOk(value: int) -> ExitResult {
let r: ExitResult = ExitResult { tag: ExitResult_Ok };
r.data.Ok_0 = value;
return r;
}
func ExitResult_MakeErr(msg: String) -> ExitResult {
let r: ExitResult = ExitResult { tag: ExitResult_Err };
r.data.Err_0 = msg;
return r;
}
// =============================================================================
// Commands
// =============================================================================
enum Cmd {
Sign,
Verify,
Decode,
Keygen,
Help,
}
func ParseCmd(name: String) -> CmdOption {
if String_Eq(name, "sign") { return CmdOption_MakeSome(Cmd { tag: Cmd_Sign }); }
if String_Eq(name, "verify") { return CmdOption_MakeSome(Cmd { tag: Cmd_Verify }); }
if String_Eq(name, "decode") { return CmdOption_MakeSome(Cmd { tag: Cmd_Decode }); }
if String_Eq(name, "keygen") { return CmdOption_MakeSome(Cmd { tag: Cmd_Keygen }); }
if String_Eq(name, "help") || String_Eq(name, "--help") || String_Eq(name, "-h") {
return CmdOption_MakeSome(Cmd { tag: Cmd_Help });
// =============================================================================
// Algebraic enums for optionals and results
// =============================================================================
enum AlgOption {
Some(JwtAlg),
None,
}
return CmdOption_MakeNone();
}
// =============================================================================
// Key type generation
// =============================================================================
enum KeyType {
Rsa,
Ecdsa,
Ed25519,
}
func AlgOption_MakeSome(value: JwtAlg) -> AlgOption {
let o: AlgOption = AlgOption { tag: AlgOption_Some };
o.data.Some_0 = value;
return o;
}
func ParseKeyType(name: String) -> KeyTypeOption {
if String_Eq(name, "rsa") { return KeyTypeOption_MakeSome(KeyType { tag: KeyType_Rsa }); }
if String_Eq(name, "ecdsa") { return KeyTypeOption_MakeSome(KeyType { tag: KeyType_Ecdsa }); }
if String_Eq(name, "ed25519") { return KeyTypeOption_MakeSome(KeyType { tag: KeyType_Ed25519 }); }
return KeyTypeOption_MakeNone();
}
func AlgOption_MakeNone() -> AlgOption {
return AlgOption { tag: AlgOption_None };
}
// =============================================================================
// Extend JwtAlg with parsing / introspection methods
// =============================================================================
extend JwtAlg {
func Name(self: JwtAlg) -> String {
match self {
JwtAlg::HS256 => "HS256",
JwtAlg::HS384 => "HS384",
JwtAlg::HS512 => "HS512",
JwtAlg::RS256 => "RS256",
JwtAlg::RS384 => "RS384",
JwtAlg::RS512 => "RS512",
JwtAlg::ES256 => "ES256",
JwtAlg::ES384 => "ES384",
JwtAlg::EdDSA => "EdDSA",
_ => "HS256",
enum CmdOption {
Some(Cmd),
None,
}
func CmdOption_MakeSome(value: Cmd) -> CmdOption {
let o: CmdOption = CmdOption { tag: CmdOption_Some };
o.data.Some_0 = value;
return o;
}
func CmdOption_MakeNone() -> CmdOption {
return CmdOption { tag: CmdOption_None };
}
enum KeyTypeOption {
Some(KeyType),
None,
}
func KeyTypeOption_MakeSome(value: KeyType) -> KeyTypeOption {
let o: KeyTypeOption = KeyTypeOption { tag: KeyTypeOption_Some };
o.data.Some_0 = value;
return o;
}
func KeyTypeOption_MakeNone() -> KeyTypeOption {
return KeyTypeOption { tag: KeyTypeOption_None };
}
enum KeyResult {
Ok(String),
Err(String),
}
func KeyResult_MakeOk(value: String) -> KeyResult {
let r: KeyResult = KeyResult { tag: KeyResult_Ok };
r.data.Ok_0 = value;
return r;
}
func KeyResult_MakeErr(msg: String) -> KeyResult {
let r: KeyResult = KeyResult { tag: KeyResult_Err };
r.data.Err_0 = msg;
return r;
}
enum ExitResult {
Ok(int),
Err(String),
}
func ExitResult_MakeOk(value: int) -> ExitResult {
let r: ExitResult = ExitResult { tag: ExitResult_Ok };
r.data.Ok_0 = value;
return r;
}
func ExitResult_MakeErr(msg: String) -> ExitResult {
let r: ExitResult = ExitResult { tag: ExitResult_Err };
r.data.Err_0 = msg;
return r;
}
// =============================================================================
// Commands
// =============================================================================
enum Cmd {
Sign,
Verify,
Decode,
Keygen,
Help,
}
func ParseCmd(name: String) -> CmdOption {
if String_Eq(name, "sign") { return CmdOption_MakeSome(Cmd { tag: Cmd_Sign }); }
if String_Eq(name, "verify") { return CmdOption_MakeSome(Cmd { tag: Cmd_Verify }); }
if String_Eq(name, "decode") { return CmdOption_MakeSome(Cmd { tag: Cmd_Decode }); }
if String_Eq(name, "keygen") { return CmdOption_MakeSome(Cmd { tag: Cmd_Keygen }); }
if String_Eq(name, "help") || String_Eq(name, "--help") || String_Eq(name, "-h") {
return CmdOption_MakeSome(Cmd { tag: Cmd_Help });
}
return CmdOption_MakeNone();
}
func IsHmac(self: JwtAlg) -> bool {
return self.tag == JwtAlg_HS256 || self.tag == JwtAlg_HS384 || self.tag == JwtAlg_HS512;
// =============================================================================
// Key type generation
// =============================================================================
enum KeyType {
Rsa,
Ecdsa,
Ed25519,
}
func NeedsPemFile(self: JwtAlg) -> bool {
return self.tag == JwtAlg_RS256 || self.tag == JwtAlg_RS384 || self.tag == JwtAlg_RS512 ||
self.tag == JwtAlg_ES256 || self.tag == JwtAlg_ES384;
func ParseKeyType(name: String) -> KeyTypeOption {
if String_Eq(name, "rsa") { return KeyTypeOption_MakeSome(KeyType { tag: KeyType_Rsa }); }
if String_Eq(name, "ecdsa") { return KeyTypeOption_MakeSome(KeyType { tag: KeyType_Ecdsa }); }
if String_Eq(name, "ed25519") { return KeyTypeOption_MakeSome(KeyType { tag: KeyType_Ed25519 }); }
return KeyTypeOption_MakeNone();
}
}
func ParseAlg(name: String) -> AlgOption {
if String_Eq(name, "HS256") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_HS256 }); }
if String_Eq(name, "HS384") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_HS384 }); }
if String_Eq(name, "HS512") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_HS512 }); }
if String_Eq(name, "RS256") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_RS256 }); }
if String_Eq(name, "RS384") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_RS384 }); }
if String_Eq(name, "RS512") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_RS512 }); }
if String_Eq(name, "ES256") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_ES256 }); }
if String_Eq(name, "ES384") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_ES384 }); }
if String_Eq(name, "EdDSA") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_EdDSA }); }
return AlgOption_MakeNone();
}
// =============================================================================
// Help / Usage
// =============================================================================
func PrintUsage() {
PrintLine("╔══════════════════════════════════════════════════════╗");
PrintLine(f"║ {AppName} — JWT CLI Tool v{Version} ║");
PrintLine("║ Sign, verify, decode JSON Web Tokens ║");
PrintLine("╚══════════════════════════════════════════════════════╝");
PrintLine("");
PrintLine("Usage:");
PrintLine(f" {AppName} sign <alg> <key> <claims>");
PrintLine(f" {AppName} verify <token> <alg> <key>");
PrintLine(f" {AppName} decode <token>");
PrintLine(f" {AppName} keygen <type>");
PrintLine("");
PrintLine("Commands:");
PrintLine(" sign Create a signed JWT from claims JSON");
PrintLine(" verify Verify a JWT signature and print payload");
PrintLine(" decode Decode a JWT without verification");
PrintLine(" keygen Generate cryptographic keys");
PrintLine("");
PrintLine("Algorithms:");
PrintLine(" HS256, HS384, HS512 — HMAC (symmetric)");
PrintLine(" RS256, RS384, RS512 — RSA PKCS#1 v1.5");
PrintLine(" ES256, ES384 — ECDSA P-256 / P-384");
PrintLine(" EdDSA — Ed25519");
PrintLine("");
PrintLine("Key formats:");
PrintLine(" HMAC: raw secret string");
PrintLine(" RSA: path to PEM private/public key file");
PrintLine(" ECDSA: path to PEM private/public key file");
PrintLine(" EdDSA: base64-encoded 32-byte raw key");
PrintLine("");
PrintLine("Key generation:");
PrintLine(f" {AppName} keygen rsa # RSA 2048-bit (PEM)");
PrintLine(f" {AppName} keygen ecdsa # ECDSA P-256 (PEM)");
PrintLine(f" {AppName} keygen ed25519 # Ed25519 (raw base64)");
PrintLine("");
PrintLine("Examples:");
Print(f" {AppName} sign HS256 'my-secret' ");
PrintLine("'{\"sub\":\"123\"}'");
PrintLine(f" {AppName} verify eyJh... HS256 'my-secret'");
PrintLine(f" {AppName} decode eyJh...");
PrintLine(f" {AppName} keygen ed25519");
}
// =============================================================================
// Key resolution — for RSA/ECDSA, read PEM file; for HMAC/EdDSA, pass through
// =============================================================================
func ResolveKey(alg: JwtAlg, keyArg: String) -> KeyResult {
if !alg.NeedsPemFile() {
return KeyResult_MakeOk(keyArg);
}
if bux_file_exists(keyArg) == 0 {
return KeyResult_MakeErr(f"ERROR: PEM file not found: {keyArg}");
}
let pem: String = bux_read_file(keyArg);
if String_Len(pem) == 0 {
return KeyResult_MakeErr(f"ERROR: could not read PEM file: {keyArg}");
}
return KeyResult_MakeOk(pem);
}
// =============================================================================
// Command: sign
// =============================================================================
func CmdSign(algName: String, keyArg: String, claimsJson: String) -> ExitResult {
let algOpt: AlgOption = ParseAlg(algName);
if algOpt.tag != AlgOption_Some {
return ExitResult_MakeErr(f"ERROR: unknown algorithm '{algName}'");
}
let alg: JwtAlg = algOpt.data.Some_0;
let keyRes: KeyResult = ResolveKey(alg, keyArg);
if keyRes.tag != KeyResult_Ok {
return ExitResult_MakeErr(keyRes.data.Err_0);
}
let key: String = keyRes.data.Ok_0;
let header: String = Jwt_MakeHeader(alg);
let token: String = Jwt_Encode(header, claimsJson, alg, key);
if String_Len(token) == 0 {
return ExitResult_MakeErr("ERROR: signing failed");
}
PrintLine(token);
return ExitResult_MakeOk(0);
}
// =============================================================================
// Command: verify
// =============================================================================
func CmdVerify(token: String, algName: String, keyArg: String) -> ExitResult {
let algOpt: AlgOption = ParseAlg(algName);
if algOpt.tag != AlgOption_Some {
return ExitResult_MakeErr(f"ERROR: unknown algorithm '{algName}'");
}
let alg: JwtAlg = algOpt.data.Some_0;
let keyRes: KeyResult = ResolveKey(alg, keyArg);
if keyRes.tag != KeyResult_Ok {
return ExitResult_MakeErr(keyRes.data.Err_0);
}
let key: String = keyRes.data.Ok_0;
var header: String = "";
var payload: String = "";
if !Jwt_Decode(token, alg, key, &header, &payload) {
return ExitResult_MakeErr("✗ Signature INVALID (or malformed token)");
}
PrintLine("✓ Signature valid");
PrintLine("");
PrintLine("Header:");
PrintLine(header);
PrintLine("");
PrintLine("Payload:");
PrintLine(payload);
return ExitResult_MakeOk(0);
}
// =============================================================================
// Command: decode (no verification)
// =============================================================================
func CmdDecode(token: String) -> ExitResult {
let partCount: uint = String_SplitCount(token, ".");
if partCount != 3 {
return ExitResult_MakeErr("ERROR: not a valid JWT (expected 3 parts)");
}
let headerB64: String = String_SplitPart(token, ".", 0);
let payloadB64: String = String_SplitPart(token, ".", 1);
let sigB64: String = String_SplitPart(token, ".", 2);
let headerJson: String = Base64URL_Decode(headerB64);
let payloadJson: String = Base64URL_Decode(payloadB64);
PrintLine("Decoded (no verification):");
PrintLine("");
PrintLine("Header:");
PrintLine(headerJson);
PrintLine("");
PrintLine("Payload:");
PrintLine(payloadJson);
PrintLine("");
Print("Signature (base64url): ");
PrintLine(sigB64);
return ExitResult_MakeOk(0);
}
// =============================================================================
// Command: keygen
// =============================================================================
func CmdKeygen(keyType: String) -> ExitResult {
let ktOpt: KeyTypeOption = ParseKeyType(keyType);
if ktOpt.tag != KeyTypeOption_Some {
return ExitResult_MakeErr(f"ERROR: unknown key type '{keyType}'. Use: rsa, ecdsa, ed25519");
}
let kt: KeyType = ktOpt.data.Some_0;
if kt.tag == KeyType_Ed25519 {
let pubBuf: *void = bux_alloc(32);
let priv: *void = bux_alloc(32);
if !Ed25519_Keypair(pubBuf, priv) {
return ExitResult_MakeErr("ERROR: Ed25519 key generation failed (OpenSSL 1.1.1+ required)");
// =============================================================================
// Extend JwtAlg with parsing / introspection methods
// =============================================================================
extend JwtAlg {
func Name(self: JwtAlg) -> String {
match self {
JwtAlg::HS256 => "HS256",
JwtAlg::HS384 => "HS384",
JwtAlg::HS512 => "HS512",
JwtAlg::RS256 => "RS256",
JwtAlg::RS384 => "RS384",
JwtAlg::RS512 => "RS512",
JwtAlg::ES256 => "ES256",
JwtAlg::ES384 => "ES384",
JwtAlg::EdDSA => "EdDSA",
_ => "HS256",
}
}
let pubB64: String = bux_base64_encode(pubBuf as String, 32);
let privB64: String = bux_base64_encode(priv as String, 32);
PrintLine("Ed25519 keypair (base64):");
PrintLine(f" Public: {pubB64}");
PrintLine(f" Private: {privB64}");
return ExitResult_MakeOk(0);
func IsHmac(self: JwtAlg) -> bool {
return self.tag == JwtAlg_HS256 || self.tag == JwtAlg_HS384 || self.tag == JwtAlg_HS512;
}
func NeedsPemFile(self: JwtAlg) -> bool {
return self.tag == JwtAlg_RS256 || self.tag == JwtAlg_RS384 || self.tag == JwtAlg_RS512 ||
self.tag == JwtAlg_ES256 || self.tag == JwtAlg_ES384;
}
}
if kt.tag == KeyType_Rsa {
PrintLine("RSA key generation requires OpenSSL CLI:");
PrintLine(" openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048");
PrintLine(" openssl rsa -in private.pem -pubout -out public.pem");
func ParseAlg(name: String) -> AlgOption {
if String_Eq(name, "HS256") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_HS256 }); }
if String_Eq(name, "HS384") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_HS384 }); }
if String_Eq(name, "HS512") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_HS512 }); }
if String_Eq(name, "RS256") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_RS256 }); }
if String_Eq(name, "RS384") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_RS384 }); }
if String_Eq(name, "RS512") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_RS512 }); }
if String_Eq(name, "ES256") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_ES256 }); }
if String_Eq(name, "ES384") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_ES384 }); }
if String_Eq(name, "EdDSA") { return AlgOption_MakeSome(JwtAlg { tag: JwtAlg_EdDSA }); }
return AlgOption_MakeNone();
}
// =============================================================================
// Help / Usage
// =============================================================================
func PrintUsage() {
PrintLine("╔══════════════════════════════════════════════════════╗");
PrintLine(f"║ {AppName} — JWT CLI Tool v{Version} ║");
PrintLine("║ Sign, verify, decode JSON Web Tokens ║");
PrintLine("╚══════════════════════════════════════════════════════╝");
PrintLine("");
PrintLine("(Key generation from within Bux requires PEM write support — coming soon)");
return ExitResult_MakeOk(0);
}
if kt.tag == KeyType_Ecdsa {
PrintLine("ECDSA key generation requires OpenSSL CLI:");
PrintLine(" openssl ecparam -genkey -name prime256v1 -noout -out ec_private.pem");
PrintLine(" openssl ec -in ec_private.pem -pubout -out ec_public.pem");
PrintLine("Usage:");
PrintLine(f" {AppName} sign <alg> <key> <claims>");
PrintLine(f" {AppName} verify <token> <alg> <key>");
PrintLine(f" {AppName} decode <token>");
PrintLine(f" {AppName} keygen <type>");
PrintLine("");
PrintLine("(Key generation from within Bux requires PEM write support — coming soon)");
PrintLine("Commands:");
PrintLine(" sign Create a signed JWT from claims JSON");
PrintLine(" verify Verify a JWT signature and print payload");
PrintLine(" decode Decode a JWT without verification");
PrintLine(" keygen Generate cryptographic keys");
PrintLine("");
PrintLine("Algorithms:");
PrintLine(" HS256, HS384, HS512 — HMAC (symmetric)");
PrintLine(" RS256, RS384, RS512 — RSA PKCS#1 v1.5");
PrintLine(" ES256, ES384 — ECDSA P-256 / P-384");
PrintLine(" EdDSA — Ed25519");
PrintLine("");
PrintLine("Key formats:");
PrintLine(" HMAC: raw secret string");
PrintLine(" RSA: path to PEM private/public key file");
PrintLine(" ECDSA: path to PEM private/public key file");
PrintLine(" EdDSA: base64-encoded 32-byte raw key");
PrintLine("");
PrintLine("Key generation:");
PrintLine(f" {AppName} keygen rsa # RSA 2048-bit (PEM)");
PrintLine(f" {AppName} keygen ecdsa # ECDSA P-256 (PEM)");
PrintLine(f" {AppName} keygen ed25519 # Ed25519 (raw base64)");
PrintLine("");
PrintLine("Examples:");
Print(f" {AppName} sign HS256 'my-secret' ");
PrintLine("'{\"sub\":\"123\"}'");
PrintLine(f" {AppName} verify eyJh... HS256 'my-secret'");
PrintLine(f" {AppName} decode eyJh...");
PrintLine(f" {AppName} keygen ed25519");
}
// =============================================================================
// Key resolution — for RSA/ECDSA, read PEM file; for HMAC/EdDSA, pass through
// =============================================================================
func ResolveKey(alg: JwtAlg, keyArg: String) -> KeyResult {
if !alg.NeedsPemFile() {
return KeyResult_MakeOk(keyArg);
}
if bux_file_exists(keyArg) == 0 {
return KeyResult_MakeErr(f"ERROR: PEM file not found: {keyArg}");
}
let pem: String = bux_read_file(keyArg);
if String_Len(pem) == 0 {
return KeyResult_MakeErr(f"ERROR: could not read PEM file: {keyArg}");
}
return KeyResult_MakeOk(pem);
}
// =============================================================================
// Command: sign
// =============================================================================
func CmdSign(algName: String, keyArg: String, claimsJson: String) -> ExitResult {
let algOpt: AlgOption = ParseAlg(algName);
if algOpt.tag != AlgOption_Some {
return ExitResult_MakeErr(f"ERROR: unknown algorithm '{algName}'");
}
let alg: JwtAlg = algOpt.data.Some_0;
let keyRes: KeyResult = ResolveKey(alg, keyArg);
if keyRes.tag != KeyResult_Ok {
return ExitResult_MakeErr(keyRes.data.Err_0);
}
let key: String = keyRes.data.Ok_0;
let header: String = Jwt_MakeHeader(alg);
let token: String = Jwt_Encode(header, claimsJson, alg, key);
if String_Len(token) == 0 {
return ExitResult_MakeErr("ERROR: signing failed");
}
PrintLine(token);
return ExitResult_MakeOk(0);
}
return ExitResult_MakeErr(f"ERROR: unknown key type '{keyType}'");
}
// =============================================================================
// Dispatch a parsed command to its handler
// =============================================================================
func RunCommand(cmd: Cmd, args: Array<String>) -> ExitResult {
if cmd.tag == Cmd_Help {
PrintUsage();
// =============================================================================
// Command: verify
// =============================================================================
func CmdVerify(token: String, algName: String, keyArg: String) -> ExitResult {
let algOpt: AlgOption = ParseAlg(algName);
if algOpt.tag != AlgOption_Some {
return ExitResult_MakeErr(f"ERROR: unknown algorithm '{algName}'");
}
let alg: JwtAlg = algOpt.data.Some_0;
let keyRes: KeyResult = ResolveKey(alg, keyArg);
if keyRes.tag != KeyResult_Ok {
return ExitResult_MakeErr(keyRes.data.Err_0);
}
let key: String = keyRes.data.Ok_0;
var header: String = "";
var payload: String = "";
if !Jwt_Decode(token, alg, key, &header, &payload) {
return ExitResult_MakeErr("✗ Signature INVALID (or malformed token)");
}
PrintLine("✓ Signature valid");
PrintLine("");
PrintLine("Header:");
PrintLine(header);
PrintLine("");
PrintLine("Payload:");
PrintLine(payload);
return ExitResult_MakeOk(0);
}
if cmd.tag == Cmd_Sign {
if args.len < 5 {
return ExitResult_MakeErr("ERROR: 'sign' requires: <alg> <key> <claims_json>");
// =============================================================================
// Command: decode (no verification)
// =============================================================================
func CmdDecode(token: String) -> ExitResult {
let partCount: uint = String_SplitCount(token, ".");
if partCount != 3 {
return ExitResult_MakeErr("ERROR: not a valid JWT (expected 3 parts)");
}
return CmdSign(args[2], args[3], args[4]);
let headerB64: String = String_SplitPart(token, ".", 0);
let payloadB64: String = String_SplitPart(token, ".", 1);
let sigB64: String = String_SplitPart(token, ".", 2);
let headerJson: String = Base64URL_Decode(headerB64);
let payloadJson: String = Base64URL_Decode(payloadB64);
PrintLine("Decoded (no verification):");
PrintLine("");
PrintLine("Header:");
PrintLine(headerJson);
PrintLine("");
PrintLine("Payload:");
PrintLine(payloadJson);
PrintLine("");
Print("Signature (base64url): ");
PrintLine(sigB64);
return ExitResult_MakeOk(0);
}
if cmd.tag == Cmd_Verify {
if args.len < 5 {
return ExitResult_MakeErr("ERROR: 'verify' requires: <token> <alg> <key>");
// =============================================================================
// Command: keygen
// =============================================================================
func CmdKeygen(keyType: String) -> ExitResult {
let ktOpt: KeyTypeOption = ParseKeyType(keyType);
if ktOpt.tag != KeyTypeOption_Some {
return ExitResult_MakeErr(f"ERROR: unknown key type '{keyType}'. Use: rsa, ecdsa, ed25519");
}
return CmdVerify(args[2], args[3], args[4]);
}
if cmd.tag == Cmd_Decode {
if args.len < 3 {
return ExitResult_MakeErr("ERROR: 'decode' requires: <token>");
let kt: KeyType = ktOpt.data.Some_0;
if kt.tag == KeyType_Ed25519 {
let pubBuf: *void = bux_alloc(32);
let priv: *void = bux_alloc(32);
if !Ed25519_Keypair(pubBuf, priv) {
return ExitResult_MakeErr("ERROR: Ed25519 key generation failed (OpenSSL 1.1.1+ required)");
}
let pubB64: String = bux_base64_encode(pubBuf as String, 32);
let privB64: String = bux_base64_encode(priv as String, 32);
PrintLine("Ed25519 keypair (base64):");
PrintLine(f" Public: {pubB64}");
PrintLine(f" Private: {privB64}");
return ExitResult_MakeOk(0);
}
return CmdDecode(args[2]);
}
if cmd.tag == Cmd_Keygen {
if args.len < 3 {
return ExitResult_MakeErr("ERROR: 'keygen' requires: <type>");
if kt.tag == KeyType_Rsa {
PrintLine("RSA key generation requires OpenSSL CLI:");
PrintLine(" openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:2048");
PrintLine(" openssl rsa -in private.pem -pubout -out public.pem");
PrintLine("");
PrintLine("(Key generation from within Bux requires PEM write support — coming soon)");
return ExitResult_MakeOk(0);
}
return CmdKeygen(args[2]);
}
return ExitResult_MakeErr("ERROR: unhandled command");
}
// =============================================================================
// Main Entry Point
// =============================================================================
func Main() -> int {
let argc: int = bux_argc();
// Collect CLI arguments into a generic Array<String>
let args: Array<String> = Array_New<String>(argc as uint);
for i in 0..argc {
Array_Push<String>(&args, bux_argv(i));
if kt.tag == KeyType_Ecdsa {
PrintLine("ECDSA key generation requires OpenSSL CLI:");
PrintLine(" openssl ecparam -genkey -name prime256v1 -noout -out ec_private.pem");
PrintLine(" openssl ec -in ec_private.pem -pubout -out ec_public.pem");
PrintLine("");
PrintLine("(Key generation from within Bux requires PEM write support — coming soon)");
return ExitResult_MakeOk(0);
}
return ExitResult_MakeErr(f"ERROR: unknown key type '{keyType}'");
}
if args.len < 2 {
PrintUsage();
return 0;
// =============================================================================
// Dispatch a parsed command to its handler
// =============================================================================
func RunCommand(cmd: Cmd, args: Array<String>) -> ExitResult {
if cmd.tag == Cmd_Help {
PrintUsage();
return ExitResult_MakeOk(0);
}
if cmd.tag == Cmd_Sign {
if args.len < 5 {
return ExitResult_MakeErr("ERROR: 'sign' requires: <alg> <key> <claims_json>");
}
return CmdSign(args[2], args[3], args[4]);
}
if cmd.tag == Cmd_Verify {
if args.len < 5 {
return ExitResult_MakeErr("ERROR: 'verify' requires: <token> <alg> <key>");
}
return CmdVerify(args[2], args[3], args[4]);
}
if cmd.tag == Cmd_Decode {
if args.len < 3 {
return ExitResult_MakeErr("ERROR: 'decode' requires: <token>");
}
return CmdDecode(args[2]);
}
if cmd.tag == Cmd_Keygen {
if args.len < 3 {
return ExitResult_MakeErr("ERROR: 'keygen' requires: <type>");
}
return CmdKeygen(args[2]);
}
return ExitResult_MakeErr("ERROR: unhandled command");
}
let command: String = args[1];
let cmdOpt: CmdOption = ParseCmd(command);
if cmdOpt.tag != CmdOption_Some {
PrintLine(f"ERROR: unknown command '{command}'");
PrintLine(f"Run '{AppName} help' for usage.");
return 1;
}
let cmd: Cmd = cmdOpt.data.Some_0;
// =============================================================================
// Main Entry Point
// =============================================================================
func Main() -> int {
let argc: int = bux_argc();
let result: ExitResult = RunCommand(cmd, args);
if result.tag == ExitResult_Err {
PrintLine(result.data.Err_0);
return 1;
// Collect CLI arguments into a generic Array<String>
let args: Array<String> = Array_New<String>(argc as uint);
for i in 0..argc {
Array_Push<String>(&args, bux_argv(i));
}
if args.len < 2 {
PrintUsage();
return 0;
}
let command: String = args[1];
let cmdOpt: CmdOption = ParseCmd(command);
if cmdOpt.tag != CmdOption_Some {
PrintLine(f"ERROR: unknown command '{command}'");
PrintLine(f"Run '{AppName} help' for usage.");
return 1;
}
let cmd: Cmd = cmdOpt.data.Some_0;
let result: ExitResult = RunCommand(cmd, args);
if result.tag == ExitResult_Err {
PrintLine(result.data.Err_0);
return 1;
}
return result.data.Ok_0;
}
return result.data.Ok_0;
}
} // module Main
+16 -16
View File
@@ -1,21 +1,21 @@
module Config {
pub struct ServerConfig {
bindAddr: String;
port: int;
workerCount: int;
publicDir: String;
backlog: int;
}
pub struct ServerConfig {
bindAddr: String;
port: int;
workerCount: int;
publicDir: String;
backlog: int;
}
pub const func DefaultConfig() -> ServerConfig {
return ServerConfig {
bindAddr: "0.0.0.0",
port: 8080,
workerCount: 4,
publicDir: "public",
backlog: 128,
};
}
pub const func DefaultConfig() -> ServerConfig {
return ServerConfig {
bindAddr: "0.0.0.0",
port: 8080,
workerCount: 4,
publicDir: "public",
backlog: 128,
};
}
}
+50 -50
View File
@@ -1,65 +1,65 @@
module Errors {
import Http::{HttpRequest};
import Http::{HttpRequest};
pub enum HttpError {
BadRequest,
NotFound,
MethodNotAllowed,
InternalError(String),
}
pub enum HttpError {
BadRequest,
NotFound,
MethodNotAllowed,
InternalError(String),
}
pub enum ParseResult {
Ok(HttpRequest),
Err(HttpError),
}
pub enum ParseResult {
Ok(HttpRequest),
Err(HttpError),
}
pub enum FileResult {
Ok(String),
Err(HttpError),
}
pub enum FileResult {
Ok(String),
Err(HttpError),
}
pub func ParseResult_NewOk(req: HttpRequest) -> ParseResult {
let r: ParseResult = ParseResult { tag: ParseResult_Ok };
r.data.Ok_0 = req;
return r;
}
pub func ParseResult_NewOk(req: HttpRequest) -> ParseResult {
let r: ParseResult = ParseResult { tag: ParseResult_Ok };
r.data.Ok_0 = req;
return r;
}
pub func ParseResult_NewErr(err: HttpError) -> ParseResult {
let r: ParseResult = ParseResult { tag: ParseResult_Err };
r.data.Err_0 = err;
return r;
}
pub func ParseResult_NewErr(err: HttpError) -> ParseResult {
let r: ParseResult = ParseResult { tag: ParseResult_Err };
r.data.Err_0 = err;
return r;
}
pub func FileResult_NewOk(content: String) -> FileResult {
let r: FileResult = FileResult { tag: FileResult_Ok };
r.data.Ok_0 = content;
return r;
}
pub func FileResult_NewOk(content: String) -> FileResult {
let r: FileResult = FileResult { tag: FileResult_Ok };
r.data.Ok_0 = content;
return r;
}
pub func FileResult_NewErr(err: HttpError) -> FileResult {
let r: FileResult = FileResult { tag: FileResult_Err };
r.data.Err_0 = err;
return r;
}
pub func FileResult_NewErr(err: HttpError) -> FileResult {
let r: FileResult = FileResult { tag: FileResult_Err };
r.data.Err_0 = err;
return r;
}
pub func FileResult_IsOk(r: FileResult) -> bool {
return r.tag == FileResult_Ok;
}
pub func FileResult_IsOk(r: FileResult) -> bool {
return r.tag == FileResult_Ok;
}
pub func FileResult_Unwrap(r: FileResult) -> String {
return r.data.Ok_0;
}
pub func FileResult_Unwrap(r: FileResult) -> String {
return r.data.Ok_0;
}
pub func FileResult_UnwrapErr(r: FileResult) -> HttpError {
return r.data.Err_0;
}
pub func FileResult_UnwrapErr(r: FileResult) -> HttpError {
return r.data.Err_0;
}
pub func HttpError_ToString(err: HttpError) -> String {
if err.tag == HttpError_BadRequest { return "Bad Request"; }
if err.tag == HttpError_NotFound { return "Not Found"; }
if err.tag == HttpError_MethodNotAllowed { return "Method Not Allowed"; }
return err.data.InternalError_0;
}
pub func HttpError_ToString(err: HttpError) -> String {
if err.tag == HttpError_BadRequest { return "Bad Request"; }
if err.tag == HttpError_NotFound { return "Not Found"; }
if err.tag == HttpError_MethodNotAllowed { return "Method Not Allowed"; }
return err.data.InternalError_0;
}
}
+72 -72
View File
@@ -1,94 +1,94 @@
module Handlers {
import Std::String::{String_Eq, String_Contains};
import Http::{HttpMethod, HttpRequest, HttpResponse, Http_NewResponse, Http_MimeType, RequestHeader_Get};
import Errors::{HttpError, FileResult, FileResult_NewOk, FileResult_NewErr, FileResult_IsOk, FileResult_Unwrap, FileResult_UnwrapErr, HttpError_ToString};
import Std::String::{String_Eq, String_Contains};
import Http::{HttpMethod, HttpRequest, HttpResponse, Http_NewResponse, Http_MimeType, RequestHeader_Get};
import Errors::{HttpError, FileResult, FileResult_NewOk, FileResult_NewErr, FileResult_IsOk, FileResult_Unwrap, FileResult_UnwrapErr, HttpError_ToString};
extern func bux_strlen(s: String) -> uint;
extern func bux_file_exists(path: String) -> int;
extern func bux_read_file(path: String) -> String;
extern func bux_path_join(a: String, b: String) -> String;
extern func bux_sb_new(initial_cap: uint) -> *void;
extern func bux_sb_append(sb: *void, s: String);
extern func bux_sb_append_int(sb: *void, n: int64);
extern func bux_sb_build(sb: *void) -> String;
extern func bux_sb_free(sb: *void);
extern func bux_strlen(s: String) -> uint;
extern func bux_file_exists(path: String) -> int;
extern func bux_read_file(path: String) -> String;
extern func bux_path_join(a: String, b: String) -> String;
extern func bux_sb_new(initial_cap: uint) -> *void;
extern func bux_sb_append(sb: *void, s: String);
extern func bux_sb_append_int(sb: *void, n: int64);
extern func bux_sb_build(sb: *void) -> String;
extern func bux_sb_free(sb: *void);
pub func NotFoundResponse() -> HttpResponse {
return Http_NewResponse(404, "application/json; charset=utf-8", "{\"error\":\"not_found\"}");
}
pub func MethodNotAllowedResponse() -> HttpResponse {
return Http_NewResponse(405, "text/plain; charset=utf-8", "Method Not Allowed");
}
pub func ReadStaticFile(requestPath: String) -> FileResult {
if String_Contains(requestPath, "..") {
return FileResult_NewErr(HttpError { tag: HttpError_NotFound });
pub func NotFoundResponse() -> HttpResponse {
return Http_NewResponse(404, "application/json; charset=utf-8", "{\"error\":\"not_found\"}");
}
var filePath: String = requestPath;
if String_Eq(filePath, "/") {
filePath = "/index.html";
pub func MethodNotAllowedResponse() -> HttpResponse {
return Http_NewResponse(405, "text/plain; charset=utf-8", "Method Not Allowed");
}
let fullPath: String = bux_path_join("public", filePath);
if bux_file_exists(fullPath) == 0 {
return FileResult_NewErr(HttpError { tag: HttpError_NotFound });
pub func ReadStaticFile(requestPath: String) -> FileResult {
if String_Contains(requestPath, "..") {
return FileResult_NewErr(HttpError { tag: HttpError_NotFound });
}
var filePath: String = requestPath;
if String_Eq(filePath, "/") {
filePath = "/index.html";
}
let fullPath: String = bux_path_join("public", filePath);
if bux_file_exists(fullPath) == 0 {
return FileResult_NewErr(HttpError { tag: HttpError_NotFound });
}
let content: String = bux_read_file(fullPath);
return FileResult_NewOk(content);
}
let content: String = bux_read_file(fullPath);
return FileResult_NewOk(content);
}
func FileErrorResponse(err: HttpError) -> HttpResponse {
match err {
HttpError::NotFound => NotFoundResponse(),
_ => Http_NewResponse(500, "text/plain; charset=utf-8", HttpError_ToString(err)),
}
}
pub func ServeStaticFile(req: HttpRequest) -> HttpResponse {
if req.method != HttpMethod_GET && req.method != HttpMethod_HEAD {
return MethodNotAllowedResponse();
func FileErrorResponse(err: HttpError) -> HttpResponse {
match err {
HttpError::NotFound => NotFoundResponse(),
_ => Http_NewResponse(500, "text/plain; charset=utf-8", HttpError_ToString(err)),
}
}
let result: FileResult = ReadStaticFile(req.path);
if FileResult_IsOk(result) {
let content: String = FileResult_Unwrap(result);
return Http_NewResponse(200, Http_MimeType(req.path), content);
}
return FileErrorResponse(FileResult_UnwrapErr(result));
}
pub func ServeStaticFile(req: HttpRequest) -> HttpResponse {
if req.method != HttpMethod_GET && req.method != HttpMethod_HEAD {
return MethodNotAllowedResponse();
}
pub func HandleApiHealth() -> HttpResponse {
return Http_NewResponse(200, "application/json; charset=utf-8",
let result: FileResult = ReadStaticFile(req.path);
if FileResult_IsOk(result) {
let content: String = FileResult_Unwrap(result);
return Http_NewResponse(200, Http_MimeType(req.path), content);
}
return FileErrorResponse(FileResult_UnwrapErr(result));
}
pub func HandleApiHealth() -> HttpResponse {
return Http_NewResponse(200, "application/json; charset=utf-8",
"{\"status\":\"ok\",\"server\":\"Nexus\",\"version\":\"0.2.0\"}");
}
}
pub func HandleApiInfo() -> HttpResponse {
return Http_NewResponse(200, "application/json; charset=utf-8",
pub func HandleApiInfo() -> HttpResponse {
return Http_NewResponse(200, "application/json; charset=utf-8",
"{\"name\":\"Nexus\",\"language\":\"Bux\",\"features\":[\"HTTP/1.1\",\"thread-pool\",\"algebraic-enums\"]}");
}
}
pub func HandleWebSocketUpgrade(req: HttpRequest) -> HttpResponse {
let wsKey: String = RequestHeader_Get(req, "Sec-WebSocket-Key");
pub func HandleWebSocketUpgrade(req: HttpRequest) -> HttpResponse {
let wsKey: String = RequestHeader_Get(req, "Sec-WebSocket-Key");
var resp: HttpResponse;
resp.statusCode = 101;
resp.contentType = "";
resp.body = "";
var resp: HttpResponse;
resp.statusCode = 101;
resp.contentType = "";
resp.body = "";
let sb: *void = bux_sb_new(256);
bux_sb_append(sb, "Upgrade: websocket\r\n");
bux_sb_append(sb, "Connection: Upgrade\r\n");
bux_sb_append(sb, "Sec-WebSocket-Accept: ");
bux_sb_append(sb, wsKey);
bux_sb_append(sb, "\r\n");
resp.extraHeaders = bux_sb_build(sb);
bux_sb_free(sb);
let sb: *void = bux_sb_new(256);
bux_sb_append(sb, "Upgrade: websocket\r\n");
bux_sb_append(sb, "Connection: Upgrade\r\n");
bux_sb_append(sb, "Sec-WebSocket-Accept: ");
bux_sb_append(sb, wsKey);
bux_sb_append(sb, "\r\n");
resp.extraHeaders = bux_sb_build(sb);
bux_sb_free(sb);
return resp;
}
return resp;
}
}
+97 -97
View File
@@ -1,107 +1,107 @@
module Http {
import Std::Array::{Array};
import Std::String::{String_Eq, String_EndsWith, String_Contains};
import Std::Array::{Array};
import Std::String::{String_Eq, String_EndsWith, String_Contains};
pub enum HttpMethod {
GET,
POST,
PUT,
DELETE,
PATCH,
HEAD,
OPTIONS,
UNKNOWN,
}
pub struct HeaderEntry {
key: String;
value: String;
}
pub struct HttpRequest {
method: HttpMethod;
path: String;
version: String;
body: String;
headers: Array<HeaderEntry>;
}
pub struct HttpResponse {
statusCode: int;
contentType: String;
body: String;
extraHeaders: String;
}
pub func Http_StatusText(code: int) -> String {
if code == 200 { return "OK"; }
if code == 201 { return "Created"; }
if code == 204 { return "No Content"; }
if code == 301 { return "Moved Permanently"; }
if code == 302 { return "Found"; }
if code == 304 { return "Not Modified"; }
if code == 400 { return "Bad Request"; }
if code == 401 { return "Unauthorized"; }
if code == 403 { return "Forbidden"; }
if code == 404 { return "Not Found"; }
if code == 405 { return "Method Not Allowed"; }
if code == 413 { return "Payload Too Large"; }
if code == 414 { return "URI Too Long"; }
if code == 500 { return "Internal Server Error"; }
if code == 501 { return "Not Implemented"; }
if code == 503 { return "Service Unavailable"; }
return "Unknown";
}
pub func Http_MimeType(path: String) -> String {
if String_EndsWith(path, ".html") || String_EndsWith(path, ".htm") { return "text/html; charset=utf-8"; }
if String_EndsWith(path, ".css") { return "text/css; charset=utf-8"; }
if String_EndsWith(path, ".js") { return "application/javascript; charset=utf-8"; }
if String_EndsWith(path, ".json") { return "application/json; charset=utf-8"; }
if String_EndsWith(path, ".xml") { return "application/xml; charset=utf-8"; }
if String_EndsWith(path, ".txt") { return "text/plain; charset=utf-8"; }
if String_EndsWith(path, ".png") { return "image/png"; }
if String_EndsWith(path, ".jpg") || String_EndsWith(path, ".jpeg") { return "image/jpeg"; }
if String_EndsWith(path, ".gif") { return "image/gif"; }
if String_EndsWith(path, ".svg") { return "image/svg+xml"; }
if String_EndsWith(path, ".ico") { return "image/x-icon"; }
if String_EndsWith(path, ".webp") { return "image/webp"; }
if String_EndsWith(path, ".woff2") { return "font/woff2"; }
if String_EndsWith(path, ".woff") { return "font/woff"; }
if String_EndsWith(path, ".wasm") { return "application/wasm"; }
return "application/octet-stream";
}
pub func Http_MethodName(m: HttpMethod) -> String {
match m {
HttpMethod::GET => "GET",
HttpMethod::POST => "POST",
HttpMethod::PUT => "PUT",
HttpMethod::DELETE => "DELETE",
HttpMethod::PATCH => "PATCH",
HttpMethod::HEAD => "HEAD",
HttpMethod::OPTIONS => "OPTIONS",
HttpMethod::UNKNOWN => "UNKNOWN",
pub enum HttpMethod {
GET,
POST,
PUT,
DELETE,
PATCH,
HEAD,
OPTIONS,
UNKNOWN,
}
}
pub func Http_NewResponse(code: int, contentType: String, body: String) -> HttpResponse {
var resp: HttpResponse;
resp.statusCode = code;
resp.contentType = contentType;
resp.body = body;
resp.extraHeaders = "";
return resp;
}
pub struct HeaderEntry {
key: String;
value: String;
}
pub func RequestHeader_Get(req: HttpRequest, key: String) -> String {
for entry in req.headers {
if String_Eq(entry.key, key) {
return entry.value;
pub struct HttpRequest {
method: HttpMethod;
path: String;
version: String;
body: String;
headers: Array<HeaderEntry>;
}
pub struct HttpResponse {
statusCode: int;
contentType: String;
body: String;
extraHeaders: String;
}
pub func Http_StatusText(code: int) -> String {
if code == 200 { return "OK"; }
if code == 201 { return "Created"; }
if code == 204 { return "No Content"; }
if code == 301 { return "Moved Permanently"; }
if code == 302 { return "Found"; }
if code == 304 { return "Not Modified"; }
if code == 400 { return "Bad Request"; }
if code == 401 { return "Unauthorized"; }
if code == 403 { return "Forbidden"; }
if code == 404 { return "Not Found"; }
if code == 405 { return "Method Not Allowed"; }
if code == 413 { return "Payload Too Large"; }
if code == 414 { return "URI Too Long"; }
if code == 500 { return "Internal Server Error"; }
if code == 501 { return "Not Implemented"; }
if code == 503 { return "Service Unavailable"; }
return "Unknown";
}
pub func Http_MimeType(path: String) -> String {
if String_EndsWith(path, ".html") || String_EndsWith(path, ".htm") { return "text/html; charset=utf-8"; }
if String_EndsWith(path, ".css") { return "text/css; charset=utf-8"; }
if String_EndsWith(path, ".js") { return "application/javascript; charset=utf-8"; }
if String_EndsWith(path, ".json") { return "application/json; charset=utf-8"; }
if String_EndsWith(path, ".xml") { return "application/xml; charset=utf-8"; }
if String_EndsWith(path, ".txt") { return "text/plain; charset=utf-8"; }
if String_EndsWith(path, ".png") { return "image/png"; }
if String_EndsWith(path, ".jpg") || String_EndsWith(path, ".jpeg") { return "image/jpeg"; }
if String_EndsWith(path, ".gif") { return "image/gif"; }
if String_EndsWith(path, ".svg") { return "image/svg+xml"; }
if String_EndsWith(path, ".ico") { return "image/x-icon"; }
if String_EndsWith(path, ".webp") { return "image/webp"; }
if String_EndsWith(path, ".woff2") { return "font/woff2"; }
if String_EndsWith(path, ".woff") { return "font/woff"; }
if String_EndsWith(path, ".wasm") { return "application/wasm"; }
return "application/octet-stream";
}
pub func Http_MethodName(m: HttpMethod) -> String {
match m {
HttpMethod::GET => "GET",
HttpMethod::POST => "POST",
HttpMethod::PUT => "PUT",
HttpMethod::DELETE => "DELETE",
HttpMethod::PATCH => "PATCH",
HttpMethod::HEAD => "HEAD",
HttpMethod::OPTIONS => "OPTIONS",
HttpMethod::UNKNOWN => "UNKNOWN",
}
}
return "";
}
pub func Http_NewResponse(code: int, contentType: String, body: String) -> HttpResponse {
var resp: HttpResponse;
resp.statusCode = code;
resp.contentType = contentType;
resp.body = body;
resp.extraHeaders = "";
return resp;
}
pub func RequestHeader_Get(req: HttpRequest, key: String) -> String {
for entry in req.headers {
if String_Eq(entry.key, key) {
return entry.value;
}
}
return "";
}
}
+37 -37
View File
@@ -1,49 +1,49 @@
module Main {
import Config::{ServerConfig, DefaultConfig};
import Http::{HttpMethod};
import Router::{Handler, Route, Router};
import Server::{RunServer};
import Std::Array::{Array, Array_New, Array_Push};
import Config::{ServerConfig, DefaultConfig};
import Http::{HttpMethod};
import Router::{Handler, Route, Router};
import Server::{RunServer};
import Std::Array::{Array, Array_New, Array_Push};
func BuildRouter() -> Router {
var routes: Array<Route> = Array_New<Route>(8);
func BuildRouter() -> Router {
var routes: Array<Route> = Array_New<Route>(8);
Array_Push<Route>(&routes, Route {
method: HttpMethod { tag: HttpMethod_GET },
path: "/api/health",
handler: Handler { tag: Handler_ApiHealth },
});
Array_Push<Route>(&routes, Route {
method: HttpMethod { tag: HttpMethod_GET },
path: "/api/health",
handler: Handler { tag: Handler_ApiHealth },
});
Array_Push<Route>(&routes, Route {
method: HttpMethod { tag: HttpMethod_GET },
path: "/api/info",
handler: Handler { tag: Handler_ApiInfo },
});
Array_Push<Route>(&routes, Route {
method: HttpMethod { tag: HttpMethod_GET },
path: "/api/info",
handler: Handler { tag: Handler_ApiInfo },
});
Array_Push<Route>(&routes, Route {
method: HttpMethod { tag: HttpMethod_GET },
path: "/ws",
handler: Handler { tag: Handler_WsUpgrade },
});
Array_Push<Route>(&routes, Route {
method: HttpMethod { tag: HttpMethod_GET },
path: "/ws",
handler: Handler { tag: Handler_WsUpgrade },
});
Array_Push<Route>(&routes, Route {
method: HttpMethod { tag: HttpMethod_GET },
path: "/",
handler: Handler { tag: Handler_StaticFile },
});
Array_Push<Route>(&routes, Route {
method: HttpMethod { tag: HttpMethod_GET },
path: "/",
handler: Handler { tag: Handler_StaticFile },
});
return Router {
routes: routes,
notFound: Handler { tag: Handler_NotFound },
};
}
return Router {
routes: routes,
notFound: Handler { tag: Handler_NotFound },
};
}
func Main() -> int {
let config: ServerConfig = DefaultConfig();
let router: Router = BuildRouter();
func Main() -> int {
let config: ServerConfig = DefaultConfig();
let router: Router = BuildRouter();
return RunServer(config, router);
}
return RunServer(config, router);
}
}
+110 -110
View File
@@ -1,130 +1,130 @@
module Parser {
import Std::String::{String_Len, String_Eq, String_Trim};
import Std::Array::{Array, Array_New, Array_Push};
import Http::{HttpMethod, HttpRequest, HeaderEntry};
import Errors::{HttpError, ParseResult, ParseResult_NewOk, ParseResult_NewErr};
import Std::String::{String_Len, String_Eq, String_Trim};
import Std::Array::{Array, Array_New, Array_Push};
import Http::{HttpMethod, HttpRequest, HeaderEntry};
import Errors::{HttpError, ParseResult, ParseResult_NewOk, ParseResult_NewErr};
extern func bux_strlen(s: String) -> uint;
extern func bux_strstr(haystack: String, needle: String) -> String;
extern func bux_str_slice(s: String, start: uint, len: uint) -> String;
extern func bux_str_offset(pos: String, base: String) -> uint;
extern func bux_strlen(s: String) -> uint;
extern func bux_strstr(haystack: String, needle: String) -> String;
extern func bux_str_slice(s: String, start: uint, len: uint) -> String;
extern func bux_str_offset(pos: String, base: String) -> uint;
pub func ParseMethod(s: String) -> HttpMethod {
if String_Eq(s, "GET") { return HttpMethod { tag: HttpMethod_GET }; }
if String_Eq(s, "POST") { return HttpMethod { tag: HttpMethod_POST }; }
if String_Eq(s, "PUT") { return HttpMethod { tag: HttpMethod_PUT }; }
if String_Eq(s, "DELETE") { return HttpMethod { tag: HttpMethod_DELETE }; }
if String_Eq(s, "PATCH") { return HttpMethod { tag: HttpMethod_PATCH }; }
if String_Eq(s, "HEAD") { return HttpMethod { tag: HttpMethod_HEAD }; }
if String_Eq(s, "OPTIONS") { return HttpMethod { tag: HttpMethod_OPTIONS }; }
return HttpMethod { tag: HttpMethod_UNKNOWN };
}
pub func ParseMethod(s: String) -> HttpMethod {
if String_Eq(s, "GET") { return HttpMethod { tag: HttpMethod_GET }; }
if String_Eq(s, "POST") { return HttpMethod { tag: HttpMethod_POST }; }
if String_Eq(s, "PUT") { return HttpMethod { tag: HttpMethod_PUT }; }
if String_Eq(s, "DELETE") { return HttpMethod { tag: HttpMethod_DELETE }; }
if String_Eq(s, "PATCH") { return HttpMethod { tag: HttpMethod_PATCH }; }
if String_Eq(s, "HEAD") { return HttpMethod { tag: HttpMethod_HEAD }; }
if String_Eq(s, "OPTIONS") { return HttpMethod { tag: HttpMethod_OPTIONS }; }
return HttpMethod { tag: HttpMethod_UNKNOWN };
}
func Slice(raw: String, start: int, len: int) -> String {
if start < 0 || len <= 0 { return ""; }
return bux_str_slice(raw, start as uint, len as uint);
}
func Slice(raw: String, start: int, len: int) -> String {
if start < 0 || len <= 0 { return ""; }
return bux_str_slice(raw, start as uint, len as uint);
}
func FindCrlf(raw: String, start: int) -> int {
let rawLen: uint = bux_strlen(raw);
if start as uint >= rawLen { return -1; }
let tail: String = bux_str_slice(raw, start as uint, rawLen - start as uint);
let hit: String = bux_strstr(tail, "\r\n");
if String_Len(hit) == 0 { return -1; }
let offset: uint = bux_str_offset(hit, tail);
return start + offset as int;
}
func FindCrlf(raw: String, start: int) -> int {
let rawLen: uint = bux_strlen(raw);
if start as uint >= rawLen { return -1; }
let tail: String = bux_str_slice(raw, start as uint, rawLen - start as uint);
let hit: String = bux_strstr(tail, "\r\n");
if String_Len(hit) == 0 { return -1; }
let offset: uint = bux_str_offset(hit, tail);
return start + offset as int;
}
func ParseHeaders(raw: String, start: int, end: int) -> Array<HeaderEntry> {
var headers: Array<HeaderEntry> = Array_New<HeaderEntry>(16);
var pos: int = start;
while pos < end {
let lineEnd: int = FindCrlf(raw, pos);
if lineEnd < 0 || lineEnd >= end { break; }
let lineLen: int = lineEnd - pos;
if lineLen > 0 {
let line: String = Slice(raw, pos, lineLen);
let colon: String = bux_strstr(line, ":");
if String_Len(colon) > 0 {
let keyLen: uint = bux_str_offset(colon, line);
let key: String = String_Trim(Slice(line, 0, keyLen as int));
let valStart: int = keyLen as int + 1;
let val: String = String_Trim(Slice(line, valStart, lineLen - valStart));
let entry: HeaderEntry = HeaderEntry { key: key, value: val };
Array_Push<HeaderEntry>(&headers, entry);
func ParseHeaders(raw: String, start: int, end: int) -> Array<HeaderEntry> {
var headers: Array<HeaderEntry> = Array_New<HeaderEntry>(16);
var pos: int = start;
while pos < end {
let lineEnd: int = FindCrlf(raw, pos);
if lineEnd < 0 || lineEnd >= end { break; }
let lineLen: int = lineEnd - pos;
if lineLen > 0 {
let line: String = Slice(raw, pos, lineLen);
let colon: String = bux_strstr(line, ":");
if String_Len(colon) > 0 {
let keyLen: uint = bux_str_offset(colon, line);
let key: String = String_Trim(Slice(line, 0, keyLen as int));
let valStart: int = keyLen as int + 1;
let val: String = String_Trim(Slice(line, valStart, lineLen - valStart));
let entry: HeaderEntry = HeaderEntry { key: key, value: val };
Array_Push<HeaderEntry>(&headers, entry);
}
}
pos = lineEnd + 2;
}
pos = lineEnd + 2;
}
return headers;
}
pub func ParseRequest(raw: String) -> ParseResult {
let rawLen: uint = bux_strlen(raw);
if rawLen == 0 {
return ParseResult_NewErr(HttpError { tag: HttpError_BadRequest });
return headers;
}
// Find end of request line
let lineEnd: int = FindCrlf(raw, 0);
if lineEnd < 0 {
return ParseResult_NewErr(HttpError { tag: HttpError_BadRequest });
}
pub func ParseRequest(raw: String) -> ParseResult {
let rawLen: uint = bux_strlen(raw);
if rawLen == 0 {
return ParseResult_NewErr(HttpError { tag: HttpError_BadRequest });
}
// Split request line: METHOD PATH VERSION
var methodEnd: int = -1;
var pathStart: int = -1;
var pathEnd: int = -1;
var i: int = 0;
while i < lineEnd {
if raw[i] as int == 32 { // space
if methodEnd < 0 {
methodEnd = i;
pathStart = i + 1;
} else if pathEnd < 0 {
pathEnd = i;
break;
// Find end of request line
let lineEnd: int = FindCrlf(raw, 0);
if lineEnd < 0 {
return ParseResult_NewErr(HttpError { tag: HttpError_BadRequest });
}
// Split request line: METHOD PATH VERSION
var methodEnd: int = -1;
var pathStart: int = -1;
var pathEnd: int = -1;
var i: int = 0;
while i < lineEnd {
if raw[i] as int == 32 { // space
if methodEnd < 0 {
methodEnd = i;
pathStart = i + 1;
} else if pathEnd < 0 {
pathEnd = i;
break;
}
}
i = i + 1;
}
i = i + 1;
}
if methodEnd < 0 || pathStart < 0 || pathEnd < 0 {
return ParseResult_NewErr(HttpError { tag: HttpError_BadRequest });
}
let methodStr: String = Slice(raw, 0, methodEnd);
let path: String = Slice(raw, pathStart, pathEnd - pathStart);
let version: String = Slice(raw, pathEnd + 1, lineEnd - pathEnd - 1);
// Find header/body boundary
let boundary: String = bux_strstr(raw, "\r\n\r\n");
var headers: Array<HeaderEntry> = Array_New<HeaderEntry>(16);
var body: String = "";
if String_Len(boundary) > 0 {
let headerEnd: uint = bux_str_offset(boundary, raw);
headers = ParseHeaders(raw, lineEnd + 2, headerEnd as int);
let bodyStart: uint = headerEnd + 4;
if bodyStart < rawLen {
body = bux_str_slice(raw, bodyStart, rawLen - bodyStart);
if methodEnd < 0 || pathStart < 0 || pathEnd < 0 {
return ParseResult_NewErr(HttpError { tag: HttpError_BadRequest });
}
} else {
headers = ParseHeaders(raw, lineEnd + 2, rawLen as int);
}
if String_Eq(path, "") {
return ParseResult_NewErr(HttpError { tag: HttpError_BadRequest });
}
let methodStr: String = Slice(raw, 0, methodEnd);
let path: String = Slice(raw, pathStart, pathEnd - pathStart);
let version: String = Slice(raw, pathEnd + 1, lineEnd - pathEnd - 1);
let req: HttpRequest = HttpRequest {
method: ParseMethod(methodStr),
path: path,
version: version,
body: body,
headers: headers,
};
return ParseResult_NewOk(req);
}
// Find header/body boundary
let boundary: String = bux_strstr(raw, "\r\n\r\n");
var headers: Array<HeaderEntry> = Array_New<HeaderEntry>(16);
var body: String = "";
if String_Len(boundary) > 0 {
let headerEnd: uint = bux_str_offset(boundary, raw);
headers = ParseHeaders(raw, lineEnd + 2, headerEnd as int);
let bodyStart: uint = headerEnd + 4;
if bodyStart < rawLen {
body = bux_str_slice(raw, bodyStart, rawLen - bodyStart);
}
} else {
headers = ParseHeaders(raw, lineEnd + 2, rawLen as int);
}
if String_Eq(path, "") {
return ParseResult_NewErr(HttpError { tag: HttpError_BadRequest });
}
let req: HttpRequest = HttpRequest {
method: ParseMethod(methodStr),
path: path,
version: version,
body: body,
headers: headers,
};
return ParseResult_NewOk(req);
}
}
+37 -37
View File
@@ -1,46 +1,46 @@
module Router {
import Std::Array::{Array};
import Std::String::{String_Eq};
import Http::{HttpMethod, HttpRequest, HttpResponse, Http_NewResponse};
import Handlers::{ServeStaticFile, HandleApiHealth, HandleApiInfo, HandleWebSocketUpgrade, NotFoundResponse};
import Std::Array::{Array};
import Std::String::{String_Eq};
import Http::{HttpMethod, HttpRequest, HttpResponse, Http_NewResponse};
import Handlers::{ServeStaticFile, HandleApiHealth, HandleApiInfo, HandleWebSocketUpgrade, NotFoundResponse};
pub enum Handler {
StaticFile,
ApiHealth,
ApiInfo,
WsUpgrade,
NotFound,
}
pub struct Route {
method: HttpMethod;
path: String;
handler: Handler;
}
pub struct Router {
routes: Array<Route>;
notFound: Handler;
}
pub func Handler_Handle(h: Handler, req: HttpRequest) -> HttpResponse {
match h {
Handler::StaticFile => ServeStaticFile(req),
Handler::ApiHealth => HandleApiHealth(),
Handler::ApiInfo => HandleApiInfo(),
Handler::WsUpgrade => HandleWebSocketUpgrade(req),
Handler::NotFound => NotFoundResponse(),
pub enum Handler {
StaticFile,
ApiHealth,
ApiInfo,
WsUpgrade,
NotFound,
}
}
pub func Router_Dispatch(r: Router, req: HttpRequest) -> HttpResponse {
for route in r.routes {
if route.method == req.method && String_Eq(route.path, req.path) {
return Handler_Handle(route.handler, req);
pub struct Route {
method: HttpMethod;
path: String;
handler: Handler;
}
pub struct Router {
routes: Array<Route>;
notFound: Handler;
}
pub func Handler_Handle(h: Handler, req: HttpRequest) -> HttpResponse {
match h {
Handler::StaticFile => ServeStaticFile(req),
Handler::ApiHealth => HandleApiHealth(),
Handler::ApiInfo => HandleApiInfo(),
Handler::WsUpgrade => HandleWebSocketUpgrade(req),
Handler::NotFound => NotFoundResponse(),
}
}
return Handler_Handle(r.notFound, req);
}
pub func Router_Dispatch(r: Router, req: HttpRequest) -> HttpResponse {
for route in r.routes {
if route.method == req.method && String_Eq(route.path, req.path) {
return Handler_Handle(route.handler, req);
}
}
return Handler_Handle(r.notFound, req);
}
}
+148 -148
View File
@@ -1,176 +1,176 @@
module Server {
import Std::Io::{Print, PrintLine, PrintInt};
import Std::Net::{Net_Create, Net_SetReuse, Net_Bind, Net_Listen, Net_Accept, Net_Send, Net_Recv, Net_Close, Net_LastError};
import Std::String::{String_Len, String_StartsWith};
import Std::Channel::{Channel, Channel_New, Channel_Send, Channel_Recv};
import Config::{ServerConfig};
import Http::{HttpRequest, HttpResponse, Http_StatusText, Http_NewResponse};
import Errors::{ParseResult};
import Parser::{ParseRequest};
import Router::{Router, Router_Dispatch};
import Std::Io::{Print, PrintLine, PrintInt};
import Std::Net::{Net_Create, Net_SetReuse, Net_Bind, Net_Listen, Net_Accept, Net_Send, Net_Recv, Net_Close, Net_LastError};
import Std::String::{String_Len, String_StartsWith};
import Std::Channel::{Channel, Channel_New, Channel_Send, Channel_Recv};
import Config::{ServerConfig};
import Http::{HttpRequest, HttpResponse, Http_StatusText, Http_NewResponse};
import Errors::{ParseResult};
import Parser::{ParseRequest};
import Router::{Router, Router_Dispatch};
extern func bux_strlen(s: String) -> uint;
extern func bux_sb_new(initial_cap: uint) -> *void;
extern func bux_sb_append(sb: *void, s: String);
extern func bux_sb_append_int(sb: *void, n: int64);
extern func bux_sb_build(sb: *void) -> String;
extern func bux_sb_free(sb: *void);
extern func bux_strlen(s: String) -> uint;
extern func bux_sb_new(initial_cap: uint) -> *void;
extern func bux_sb_append(sb: *void, s: String);
extern func bux_sb_append_int(sb: *void, n: int64);
extern func bux_sb_build(sb: *void) -> String;
extern func bux_sb_free(sb: *void);
pub struct ConnectionTask {
fd: int;
}
pub func BuildResponse(resp: HttpResponse) -> String {
let sb: *void = bux_sb_new(4096);
bux_sb_append(sb, "HTTP/1.1 ");
bux_sb_append_int(sb, resp.statusCode as int64);
bux_sb_append(sb, " ");
bux_sb_append(sb, Http_StatusText(resp.statusCode));
bux_sb_append(sb, "\r\n");
bux_sb_append(sb, "Server: Nexus/0.2.0 (Bux)\r\n");
if bux_strlen(resp.extraHeaders) > 0 {
bux_sb_append(sb, resp.extraHeaders);
pub struct ConnectionTask {
fd: int;
}
if bux_strlen(resp.contentType) > 0 {
bux_sb_append(sb, "Content-Type: ");
bux_sb_append(sb, resp.contentType);
pub func BuildResponse(resp: HttpResponse) -> String {
let sb: *void = bux_sb_new(4096);
bux_sb_append(sb, "HTTP/1.1 ");
bux_sb_append_int(sb, resp.statusCode as int64);
bux_sb_append(sb, " ");
bux_sb_append(sb, Http_StatusText(resp.statusCode));
bux_sb_append(sb, "\r\n");
bux_sb_append(sb, "Server: Nexus/0.2.0 (Bux)\r\n");
if bux_strlen(resp.extraHeaders) > 0 {
bux_sb_append(sb, resp.extraHeaders);
}
if bux_strlen(resp.contentType) > 0 {
bux_sb_append(sb, "Content-Type: ");
bux_sb_append(sb, resp.contentType);
bux_sb_append(sb, "\r\n");
}
let bodyLen: uint = bux_strlen(resp.body);
bux_sb_append(sb, "Content-Length: ");
bux_sb_append_int(sb, bodyLen as int64);
bux_sb_append(sb, "\r\n");
bux_sb_append(sb, "Connection: close\r\n");
bux_sb_append(sb, "\r\n");
if bodyLen > 0 {
bux_sb_append(sb, resp.body);
}
let result: String = bux_sb_build(sb);
bux_sb_free(sb);
return result;
}
let bodyLen: uint = bux_strlen(resp.body);
bux_sb_append(sb, "Content-Length: ");
bux_sb_append_int(sb, bodyLen as int64);
bux_sb_append(sb, "\r\n");
bux_sb_append(sb, "Connection: close\r\n");
bux_sb_append(sb, "\r\n");
pub func HandleConnection(fd: int, router: Router) {
let raw: String = Net_Recv(fd, 8192);
if String_Len(raw) == 0 {
return;
}
if bodyLen > 0 {
bux_sb_append(sb, resp.body);
}
let result: String = bux_sb_build(sb);
bux_sb_free(sb);
return result;
}
pub func HandleConnection(fd: int, router: Router) {
let raw: String = Net_Recv(fd, 8192);
if String_Len(raw) == 0 {
return;
}
// HTTP/2 preface detection
if String_StartsWith(raw, "PRI * HTTP/2.0") {
let resp: HttpResponse = Http_NewResponse(200, "text/plain; charset=utf-8",
// HTTP/2 preface detection
if String_StartsWith(raw, "PRI * HTTP/2.0") {
let resp: HttpResponse = Http_NewResponse(200, "text/plain; charset=utf-8",
"HTTP/2 detected — full support planned for future release.\r\n");
Net_Send(fd, BuildResponse(resp));
return;
}
Net_Send(fd, BuildResponse(resp));
return;
}
let parsed: ParseResult = ParseRequest(raw);
if parsed.tag == ParseResult_Ok {
let req: HttpRequest = parsed.data.Ok_0;
let resp: HttpResponse = Router_Dispatch(router, req);
Net_Send(fd, BuildResponse(resp));
} else {
let resp: HttpResponse = Http_NewResponse(400, "text/plain; charset=utf-8", "Bad Request");
Net_Send(fd, BuildResponse(resp));
}
}
pub struct WorkerCtx {
taskQueue: *Channel<ConnectionTask>;
router: Router;
}
pub func Worker(ctx: *WorkerCtx) {
while true {
let task: ConnectionTask = Channel_Recv<ConnectionTask>(ctx.taskQueue);
HandleConnection(task.fd, ctx.router);
Net_Close(task.fd);
}
}
pub struct AcceptorCtx {
serverFd: int;
taskQueue: *Channel<ConnectionTask>;
}
pub func Acceptor(ctx: *AcceptorCtx) {
while true {
let fd: int = Net_Accept(ctx.serverFd);
if fd >= 0 {
let task: ConnectionTask = ConnectionTask { fd: fd };
Channel_Send<ConnectionTask>(ctx.taskQueue, task);
let parsed: ParseResult = ParseRequest(raw);
if parsed.tag == ParseResult_Ok {
let req: HttpRequest = parsed.data.Ok_0;
let resp: HttpResponse = Router_Dispatch(router, req);
Net_Send(fd, BuildResponse(resp));
} else {
let resp: HttpResponse = Http_NewResponse(400, "text/plain; charset=utf-8", "Bad Request");
Net_Send(fd, BuildResponse(resp));
}
}
}
pub func RunServer(config: ServerConfig, router: Router) -> int {
PrintLine("================================================");
PrintLine(" Nexus HTTP Server v0.2.0");
PrintLine(" Production-ready HTTP/1.1 with thread-pool");
PrintLine(" Built with Bux");
PrintLine("================================================");
PrintLine("");
let serverFd: int = Net_Create();
if serverFd < 0 {
PrintLine("FATAL: socket() failed");
return 1;
pub struct WorkerCtx {
taskQueue: *Channel<ConnectionTask>;
router: Router;
}
if !Net_SetReuse(serverFd) {
PrintLine("WARN: SO_REUSEADDR failed");
pub func Worker(ctx: *WorkerCtx) {
while true {
let task: ConnectionTask = Channel_Recv<ConnectionTask>(ctx.taskQueue);
HandleConnection(task.fd, ctx.router);
Net_Close(task.fd);
}
}
if !Net_Bind(serverFd, config.bindAddr, config.port) {
Print("FATAL: bind failed: ");
PrintLine(Net_LastError());
Net_Close(serverFd);
return 1;
pub struct AcceptorCtx {
serverFd: int;
taskQueue: *Channel<ConnectionTask>;
}
if !Net_Listen(serverFd, config.backlog) {
PrintLine("FATAL: listen() failed");
Net_Close(serverFd);
return 1;
pub func Acceptor(ctx: *AcceptorCtx) {
while true {
let fd: int = Net_Accept(ctx.serverFd);
if fd >= 0 {
let task: ConnectionTask = ConnectionTask { fd: fd };
Channel_Send<ConnectionTask>(ctx.taskQueue, task);
}
}
}
Print("Listening on http://");
Print(config.bindAddr);
Print(":");
PrintInt(config.port);
PrintLine("");
PrintInt(config.workerCount);
PrintLine(" worker threads | static: ./public/");
PrintLine("Endpoints: / /api/health /api/info /ws");
PrintLine("Press Ctrl+C to stop.");
PrintLine("");
pub func RunServer(config: ServerConfig, router: Router) -> int {
PrintLine("================================================");
PrintLine(" Nexus HTTP Server v0.2.0");
PrintLine(" Production-ready HTTP/1.1 with thread-pool");
PrintLine(" Built with Bux");
PrintLine("================================================");
PrintLine("");
let taskQueue: Channel<ConnectionTask> = Channel_New<ConnectionTask>(config.backlog as int64);
let workerCtx: WorkerCtx = WorkerCtx { taskQueue: &taskQueue, router: router };
let acceptorCtx: AcceptorCtx = AcceptorCtx { serverFd: serverFd, taskQueue: &taskQueue };
let serverFd: int = Net_Create();
if serverFd < 0 {
PrintLine("FATAL: socket() failed");
return 1;
}
// Spawn workers (main thread will also become one)
var i: int = 0;
while i < config.workerCount - 1 {
spawn Worker(&workerCtx);
i = i + 1;
if !Net_SetReuse(serverFd) {
PrintLine("WARN: SO_REUSEADDR failed");
}
if !Net_Bind(serverFd, config.bindAddr, config.port) {
Print("FATAL: bind failed: ");
PrintLine(Net_LastError());
Net_Close(serverFd);
return 1;
}
if !Net_Listen(serverFd, config.backlog) {
PrintLine("FATAL: listen() failed");
Net_Close(serverFd);
return 1;
}
Print("Listening on http://");
Print(config.bindAddr);
Print(":");
PrintInt(config.port);
PrintLine("");
PrintInt(config.workerCount);
PrintLine(" worker threads | static: ./public/");
PrintLine("Endpoints: / /api/health /api/info /ws");
PrintLine("Press Ctrl+C to stop.");
PrintLine("");
let taskQueue: Channel<ConnectionTask> = Channel_New<ConnectionTask>(config.backlog as int64);
let workerCtx: WorkerCtx = WorkerCtx { taskQueue: &taskQueue, router: router };
let acceptorCtx: AcceptorCtx = AcceptorCtx { serverFd: serverFd, taskQueue: &taskQueue };
// Spawn workers (main thread will also become one)
var i: int = 0;
while i < config.workerCount - 1 {
spawn Worker(&workerCtx);
i = i + 1;
}
// Spawn acceptor
spawn Acceptor(&acceptorCtx);
// Main thread works too
Worker(&workerCtx);
return 0;
}
// Spawn acceptor
spawn Acceptor(&acceptorCtx);
// Main thread works too
Worker(&workerCtx);
return 0;
}
}
+302 -302
View File
@@ -1,339 +1,339 @@
module Main {
import Std::Io::{PrintLine, Print, PrintInt, ReadFile, WriteFile, FileExists};
import Std::Os::{Os_ArgsCount, Os_Args};
import Std::String::{String_Len, String_Eq, String_StartsWith, String_Find, String_Slice, String_Offset};
import Std::Io::{PrintLine, Print, PrintInt, ReadFile, WriteFile, FileExists};
import Std::Os::{Os_ArgsCount, Os_Args};
import Std::String::{String_Len, String_Eq, String_StartsWith, String_Find, String_Slice, String_Offset};
extern func bux_strlen(s: String) -> uint;
extern func bux_strstr(haystack: String, needle: String) -> String;
extern func bux_str_slice(s: String, start: uint, len: uint) -> String;
extern func bux_str_split_count(s: String, delim: String) -> uint;
extern func bux_str_split_part(s: String, delim: String, index: uint) -> String;
extern func bux_sb_new(initial_cap: uint) -> *void;
extern func bux_sb_append(sb: *void, s: String);
extern func bux_sb_append_char(sb: *void, c: char8);
extern func bux_strlen(s: String) -> uint;
extern func bux_strstr(haystack: String, needle: String) -> String;
extern func bux_str_slice(s: String, start: uint, len: uint) -> String;
extern func bux_str_split_count(s: String, delim: String) -> uint;
extern func bux_str_split_part(s: String, delim: String, index: uint) -> String;
extern func bux_sb_new(initial_cap: uint) -> *void;
extern func bux_sb_append(sb: *void, s: String);
extern func bux_sb_append_char(sb: *void, c: char8);
func SB_AppendChar(sb: *void, c: int) {
bux_sb_append_char(sb, c as char8);
}
extern func bux_sb_build(sb: *void) -> String;
extern func bux_sb_free(sb: *void);
extern func bux_alloc(size: uint) -> *void;
extern func bux_free(ptr: *void);
func SB_AppendChar(sb: *void, c: int) {
bux_sb_append_char(sb, c as char8);
}
extern func bux_sb_build(sb: *void) -> String;
extern func bux_sb_free(sb: *void);
extern func bux_alloc(size: uint) -> *void;
extern func bux_free(ptr: *void);
struct Database {
path: String,
sb: *void,
loaded: bool,
count: uint,
}
struct Database {
path: String,
sb: *void,
loaded: bool,
count: uint,
}
func DB_New(filePath: String) -> Database {
var db: Database;
db.path = filePath;
db.sb = bux_sb_new(4096);
db.loaded = false;
db.count = 0;
return db;
}
func DB_New(filePath: String) -> Database {
var db: Database;
db.path = filePath;
db.sb = bux_sb_new(4096);
db.loaded = false;
db.count = 0;
return db;
}
func DB_Load(self: *Database) -> bool {
if self.loaded { return true; }
func DB_Load(self: *Database) -> bool {
if self.loaded { return true; }
if !FileExists(self.path) {
if !FileExists(self.path) {
self.loaded = true;
return true;
}
let raw: String = ReadFile(self.path);
if String_Len(raw) == 0 || bux_strlen(raw) == 0 {
self.loaded = true;
return true;
}
bux_sb_append(self.sb, raw);
let lineCount: uint = bux_str_split_count(raw, "\n");
var i: uint = 0;
var n: uint = 0;
while i < lineCount {
let line: String = bux_str_split_part(raw, "\n", i);
if bux_strlen(line) > 0 {
n = n + 1;
}
i = i + 1;
}
self.count = n;
self.loaded = true;
return true;
}
let raw: String = ReadFile(self.path);
if String_Len(raw) == 0 || bux_strlen(raw) == 0 {
self.loaded = true;
return true;
}
bux_sb_append(self.sb, raw);
let lineCount: uint = bux_str_split_count(raw, "\n");
var i: uint = 0;
var n: uint = 0;
while i < lineCount {
let line: String = bux_str_split_part(raw, "\n", i);
if bux_strlen(line) > 0 {
n = n + 1;
}
i = i + 1;
}
self.count = n;
self.loaded = true;
return true;
}
func DB_Get(self: *Database, key: String) -> String {
let raw: String = bux_sb_build(self.sb);
let lineCount: uint = bux_str_split_count(raw, "\n");
var i: uint = 0;
while i < lineCount {
let line: String = bux_str_split_part(raw, "\n", i);
if String_StartsWith(line, key) {
let eqPos: String = String_Find(line, "=");
if String_Len(eqPos) > 0 {
let keyLen: uint = bux_strlen(key);
let valStart: uint = keyLen + 1;
let lineLen: uint = bux_strlen(line);
if valStart < lineLen {
return bux_str_slice(line, valStart, lineLen - valStart);
}
}
}
i = i + 1;
}
return "";
}
func DB_Set(self: *Database, key: String, value: String) {
let raw: String = bux_sb_build(self.sb);
let lineCount: uint = bux_str_split_count(raw, "\n");
var newSb: *void = bux_sb_new(4096);
var found: bool = false;
var i: uint = 0;
while i < lineCount {
let line: String = bux_str_split_part(raw, "\n", i);
if bux_strlen(line) > 0 {
func DB_Get(self: *Database, key: String) -> String {
let raw: String = bux_sb_build(self.sb);
let lineCount: uint = bux_str_split_count(raw, "\n");
var i: uint = 0;
while i < lineCount {
let line: String = bux_str_split_part(raw, "\n", i);
if String_StartsWith(line, key) {
let eqPos: String = String_Find(line, "=");
if String_Len(eqPos) > 0 {
bux_sb_append(newSb, key);
SB_AppendChar(newSb, 61);
bux_sb_append(newSb, value);
SB_AppendChar(newSb, 10);
found = true;
let keyLen: uint = bux_strlen(key);
let valStart: uint = keyLen + 1;
let lineLen: uint = bux_strlen(line);
if valStart < lineLen {
return bux_str_slice(line, valStart, lineLen - valStart);
}
}
}
i = i + 1;
}
return "";
}
func DB_Set(self: *Database, key: String, value: String) {
let raw: String = bux_sb_build(self.sb);
let lineCount: uint = bux_str_split_count(raw, "\n");
var newSb: *void = bux_sb_new(4096);
var found: bool = false;
var i: uint = 0;
while i < lineCount {
let line: String = bux_str_split_part(raw, "\n", i);
if bux_strlen(line) > 0 {
if String_StartsWith(line, key) {
let eqPos: String = String_Find(line, "=");
if String_Len(eqPos) > 0 {
bux_sb_append(newSb, key);
SB_AppendChar(newSb, 61);
bux_sb_append(newSb, value);
SB_AppendChar(newSb, 10);
found = true;
} else {
bux_sb_append(newSb, line);
SB_AppendChar(newSb, 10);
}
} else {
bux_sb_append(newSb, line);
SB_AppendChar(newSb, 10);
}
} else {
bux_sb_append(newSb, line);
SB_AppendChar(newSb, 10);
}
i = i + 1;
}
i = i + 1;
if !found {
bux_sb_append(newSb, key);
SB_AppendChar(newSb, 61);
bux_sb_append(newSb, value);
SB_AppendChar(newSb, 10);
self.count = self.count + 1;
}
bux_sb_free(self.sb);
self.sb = newSb;
}
if !found {
bux_sb_append(newSb, key);
SB_AppendChar(newSb, 61);
bux_sb_append(newSb, value);
SB_AppendChar(newSb, 10);
self.count = self.count + 1;
}
func DB_Del(self: *Database, key: String) {
let raw: String = bux_sb_build(self.sb);
let lineCount: uint = bux_str_split_count(raw, "\n");
bux_sb_free(self.sb);
self.sb = newSb;
}
func DB_Del(self: *Database, key: String) {
let raw: String = bux_sb_build(self.sb);
let lineCount: uint = bux_str_split_count(raw, "\n");
var newSb: *void = bux_sb_new(4096);
var i: uint = 0;
var delCount: uint = 0;
while i < lineCount {
let line: String = bux_str_split_part(raw, "\n", i);
if bux_strlen(line) > 0 {
if String_StartsWith(line, key) {
let eqPos: String = String_Find(line, "=");
if String_Len(eqPos) > 0 {
delCount = delCount + 1;
var newSb: *void = bux_sb_new(4096);
var i: uint = 0;
var delCount: uint = 0;
while i < lineCount {
let line: String = bux_str_split_part(raw, "\n", i);
if bux_strlen(line) > 0 {
if String_StartsWith(line, key) {
let eqPos: String = String_Find(line, "=");
if String_Len(eqPos) > 0 {
delCount = delCount + 1;
} else {
bux_sb_append(newSb, line);
SB_AppendChar(newSb, 10);
}
} else {
bux_sb_append(newSb, line);
SB_AppendChar(newSb, 10);
}
}
i = i + 1;
}
if delCount > 0 {
self.count = self.count - delCount;
}
bux_sb_free(self.sb);
self.sb = newSb;
}
func DB_Has(self: *Database, key: String) -> bool {
let val: String = DB_Get(self, key);
return bux_strlen(val) > 0;
}
func DB_Count(self: *Database) -> uint {
return self.count;
}
func DB_Keys(self: *Database) -> *String {
if self.count == 0 { return null as *String; }
let raw: String = bux_sb_build(self.sb);
let lineCount: uint = bux_str_split_count(raw, "\n");
let keys: *String = bux_alloc(self.count * sizeof(String)) as *String;
var found: uint = 0;
var i: uint = 0;
while i < lineCount && found < self.count {
let line: String = bux_str_split_part(raw, "\n", i);
if bux_strlen(line) > 0 {
let eqPos: String = String_Find(line, "=");
if String_Len(eqPos) > 0 {
let keyLen: uint = String_Offset(eqPos, line);
keys[found] = bux_str_slice(line, 0, keyLen);
found = found + 1;
}
}
i = i + 1;
}
return keys;
}
func DB_Save(self: *Database) -> bool {
let content: String = bux_sb_build(self.sb);
let ok: bool = WriteFile(self.path, content);
return ok;
}
func PrintUsage() {
PrintLine("SimpleDB — file-backed key-value database");
PrintLine("Usage:");
PrintLine(" simpledb <dbfile> set <key> <value>");
PrintLine(" simpledb <dbfile> get <key>");
PrintLine(" simpledb <dbfile> del <key>");
PrintLine(" simpledb <dbfile> has <key>");
PrintLine(" simpledb <dbfile> keys");
PrintLine(" simpledb <dbfile> count");
}
func Main() -> int {
let argc: int = Os_ArgsCount();
if argc < 3 {
PrintUsage();
return 1;
}
let dbFile: String = Os_Args(1);
let cmd: String = Os_Args(2);
var db: Database = DB_New(dbFile);
if String_Eq(cmd, "get") {
DB_Load(&db);
if argc < 4 {
PrintLine("get: missing key");
return 1;
}
let key: String = Os_Args(3);
let val: String = DB_Get(&db, key);
if bux_strlen(val) > 0 {
PrintLine(val);
} else {
bux_sb_append(newSb, line);
SB_AppendChar(newSb, 10);
Print("(not found) ");
PrintLine(key);
}
}
i = i + 1;
}
if delCount > 0 {
self.count = self.count - delCount;
}
bux_sb_free(self.sb);
self.sb = newSb;
}
func DB_Has(self: *Database, key: String) -> bool {
let val: String = DB_Get(self, key);
return bux_strlen(val) > 0;
}
func DB_Count(self: *Database) -> uint {
return self.count;
}
func DB_Keys(self: *Database) -> *String {
if self.count == 0 { return null as *String; }
let raw: String = bux_sb_build(self.sb);
let lineCount: uint = bux_str_split_count(raw, "\n");
let keys: *String = bux_alloc(self.count * sizeof(String)) as *String;
var found: uint = 0;
var i: uint = 0;
while i < lineCount && found < self.count {
let line: String = bux_str_split_part(raw, "\n", i);
if bux_strlen(line) > 0 {
let eqPos: String = String_Find(line, "=");
if String_Len(eqPos) > 0 {
let keyLen: uint = String_Offset(eqPos, line);
keys[found] = bux_str_slice(line, 0, keyLen);
found = found + 1;
}
}
i = i + 1;
}
return keys;
}
func DB_Save(self: *Database) -> bool {
let content: String = bux_sb_build(self.sb);
let ok: bool = WriteFile(self.path, content);
return ok;
}
func PrintUsage() {
PrintLine("SimpleDB — file-backed key-value database");
PrintLine("Usage:");
PrintLine(" simpledb <dbfile> set <key> <value>");
PrintLine(" simpledb <dbfile> get <key>");
PrintLine(" simpledb <dbfile> del <key>");
PrintLine(" simpledb <dbfile> has <key>");
PrintLine(" simpledb <dbfile> keys");
PrintLine(" simpledb <dbfile> count");
}
func Main() -> int {
let argc: int = Os_ArgsCount();
if argc < 3 {
PrintUsage();
return 1;
}
let dbFile: String = Os_Args(1);
let cmd: String = Os_Args(2);
var db: Database = DB_New(dbFile);
if String_Eq(cmd, "get") {
DB_Load(&db);
if argc < 4 {
PrintLine("get: missing key");
return 1;
}
let key: String = Os_Args(3);
let val: String = DB_Get(&db, key);
if bux_strlen(val) > 0 {
PrintLine(val);
} else {
Print("(not found) ");
PrintLine(key);
}
return 0;
}
if String_Eq(cmd, "set") {
DB_Load(&db);
if argc < 5 {
PrintLine("set: missing key or value");
return 1;
}
let key: String = Os_Args(3);
let val: String = Os_Args(4);
DB_Set(&db, key, val);
let saved: bool = DB_Save(&db);
if saved {
Print("OK ");
Print(key);
Print(" = ");
PrintLine(val);
} else {
PrintLine("set: save failed");
}
return 0;
}
if String_Eq(cmd, "del") {
DB_Load(&db);
if argc < 4 {
PrintLine("del: missing key");
return 1;
}
let key: String = Os_Args(3);
DB_Del(&db, key);
DB_Save(&db);
Print("DEL ");
PrintLine(key);
return 0;
}
if String_Eq(cmd, "has") {
DB_Load(&db);
if argc < 4 {
PrintLine("has: missing key");
return 1;
}
let key: String = Os_Args(3);
let found: bool = DB_Has(&db, key);
if found {
Print("true ");
PrintLine(key);
} else {
Print("false ");
PrintLine(key);
}
return 0;
}
if String_Eq(cmd, "keys") {
DB_Load(&db);
let n: uint = DB_Count(&db);
Print("keys: ");
PrintInt(n as int);
PrintLine("");
if n == 0 {
return 0;
}
let list: *String = DB_Keys(&db);
var i: uint = 0;
while i < n {
Print(" ");
PrintLine(list[i]);
i = i + 1;
if String_Eq(cmd, "set") {
DB_Load(&db);
if argc < 5 {
PrintLine("set: missing key or value");
return 1;
}
let key: String = Os_Args(3);
let val: String = Os_Args(4);
DB_Set(&db, key, val);
let saved: bool = DB_Save(&db);
if saved {
Print("OK ");
Print(key);
Print(" = ");
PrintLine(val);
} else {
PrintLine("set: save failed");
}
return 0;
}
bux_free(list as *void);
return 0;
if String_Eq(cmd, "del") {
DB_Load(&db);
if argc < 4 {
PrintLine("del: missing key");
return 1;
}
let key: String = Os_Args(3);
DB_Del(&db, key);
DB_Save(&db);
Print("DEL ");
PrintLine(key);
return 0;
}
if String_Eq(cmd, "has") {
DB_Load(&db);
if argc < 4 {
PrintLine("has: missing key");
return 1;
}
let key: String = Os_Args(3);
let found: bool = DB_Has(&db, key);
if found {
Print("true ");
PrintLine(key);
} else {
Print("false ");
PrintLine(key);
}
return 0;
}
if String_Eq(cmd, "keys") {
DB_Load(&db);
let n: uint = DB_Count(&db);
Print("keys: ");
PrintInt(n as int);
PrintLine("");
if n == 0 {
return 0;
}
let list: *String = DB_Keys(&db);
var i: uint = 0;
while i < n {
Print(" ");
PrintLine(list[i]);
i = i + 1;
}
bux_free(list as *void);
return 0;
}
if String_Eq(cmd, "count") {
DB_Load(&db);
let n: uint = DB_Count(&db);
Print("count: ");
PrintInt(n as int);
PrintLine("");
return 0;
}
Print("unknown command: ");
PrintLine(cmd);
return 1;
}
if String_Eq(cmd, "count") {
DB_Load(&db);
let n: uint = DB_Count(&db);
Print("count: ");
PrintInt(n as int);
PrintLine("");
return 0;
}
Print("unknown command: ");
PrintLine(cmd);
return 1;
}
}
+334 -26
View File
@@ -1,6 +1,9 @@
import std/[os, strutils, terminal, strformat, osproc, sets]
import std/[os, strutils, terminal, strformat, osproc, sets, algorithm, tables]
import lexer, parser, ast, sema, manifest, hir_lower, lir_lower, lir_c_backend
import source_location
import fmt
import docgen
import registry
type
ColorMode* = enum
@@ -21,16 +24,25 @@ Usage: bux [options] <command> [command-options]
Commands:
new <name> Create a new Bux package
init Initialize a Bux package in the current directory
add <name> [ver] Add a dependency (--path, --git)
add <name> [ver] Add a dependency (--path, --git, or registry)
install Resolve and install dependencies
search [query] Search the package registry
build Build the current package
run Build and run the current package
test Run tests in tests/ directory
check Type-check the current package
fmt [path] Format .bux sources (default: .)
doc [path] Generate Markdown API docs from /// comments
clean Remove build artifacts
help Show this help message
version Show version
Command options:
test --filter <s> Only run tests whose name contains <s>
fmt --check Exit 1 if any file would be reformatted (CI)
doc --out <file> Write docs to file (default: stdout)
add --path / --git Explicit source; else resolve via registry
Global options:
--color <auto|on|off> Control colored output (default: auto)
-q, --quiet Suppress non-error output
@@ -192,6 +204,14 @@ proc hintForMessage(msg: string): string =
return "provide the missing argument (positional or named)"
if "use of moved value" in m:
return "the value was moved; clone it or restructure ownership"
if "cannot return reference to local" in m:
return "return a value, or return a reference borrowed from a function parameter"
if "lifetime elision failed" in m:
return "add an explicit lifetime, e.g. func F<'a>(x: &'a T, y: &'a U) -> &'a T"
if "lifetime mismatch" in m:
return "returned reference must share a lifetime with the return type (annotate with 'a)"
if "no input reference to borrow from" in m:
return "add a '&T' parameter to borrow from, or return an owned value"
if "shared reference" in m or "checked function" in m:
return "use '&mut T' for mutation, or drop @[Checked] for unchecked code"
if "double mutable borrow" in m or "already mutably borrowed" in m:
@@ -378,7 +398,11 @@ proc cmdAdd*(args: seq[string], opts: GlobalOptions): int =
printError("--git requires a value", useColor)
return 1
else:
version = args[i]
if not args[i].startsWith("-"):
version = args[i]
else:
printError(&"unknown add option '{args[i]}'", useColor)
return 1
inc i
# Append to bux.toml
var depLine = ""
@@ -387,7 +411,19 @@ proc cmdAdd*(args: seq[string], opts: GlobalOptions): int =
elif gitUrl.len > 0:
depLine = &"{depName} = {{ Version = \"{version}\", Source = \"{gitUrl}\" }}"
else:
depLine = &"{depName} = \"{version}\""
# Registry resolve (E.1)
let reg = loadRegistry()
if reg.path.len == 0:
printError("no package registry found (set BUX_REGISTRY or install config/registry.toml)", useColor)
return 1
let pkg = registryLookup(reg, depName, version)
if pkg.name.len == 0:
printError(&"package '{depName}' not found in registry ({reg.path})", useColor)
printError("hint: bux search | bux add name --git <url> | bux add name --path <dir>", useColor)
return 1
depLine = formatRegistryDepLine(depName, pkg)
if not opts.quiet:
printInfo(&"Resolved '{depName}' {pkg.version} from registry {reg.path}", useColor)
var content = readFile(manifestPath)
# Ensure [Dependencies] section exists
if content.find("[Dependencies]") < 0:
@@ -399,6 +435,34 @@ proc cmdAdd*(args: seq[string], opts: GlobalOptions): int =
printInfo(&"Added dependency '{depName}' to bux.toml", useColor)
return 0
proc cmdSearch*(args: seq[string], opts: GlobalOptions): int =
let useColor = shouldUseColor(opts)
let query = if args.len > 0: args[0] else: ""
let reg = loadRegistry()
if reg.path.len == 0:
printError("no package registry found (set BUX_REGISTRY)", useColor)
return 1
if not opts.quiet:
echo &"Registry: {reg.path}"
let hits = registrySearch(reg, query)
if hits.len == 0:
if not opts.quiet:
echo "No packages matched."
return 1
# Dedupe by name showing latest version
var seen = initTable[string, RegistryPackage]()
for p in hits:
seen[p.name.toLowerAscii()] = p
var names: seq[string] = @[]
for k in seen.keys:
names.add(k)
names.sort(system.cmp)
for k in names:
let p = seen[k]
let desc = if p.description.len > 0: p.description else: p.source
echo &" {p.name} {p.version} — {desc}"
return 0
proc cmdInstall*(args: seq[string], opts: GlobalOptions): int =
let useColor = shouldUseColor(opts)
let root = getCurrentDir()
@@ -411,6 +475,7 @@ proc cmdInstall*(args: seq[string], opts: GlobalOptions): int =
let cacheDir = getHomeDir() / ".bux" / "packages"
if not dirExists(cacheDir):
createDir(cacheDir)
let reg = loadRegistry()
# Resolve each dependency
for dep in man.dependencies:
case dep.kind
@@ -433,20 +498,43 @@ proc cmdInstall*(args: seq[string], opts: GlobalOptions): int =
if not dirExists(depDir):
if not opts.quiet:
printInfo(&"Cloning '{dep.name}' from {dep.gitUrl}...", useColor)
let (outp, code) = execCmdEx(&"git clone {dep.gitUrl} {depDir} 2>&1")
let (outp, code) = execCmdEx(&"git clone --quiet {quoteShell(dep.gitUrl)} {quoteShell(depDir)} 2>&1")
if code != 0:
printError(&"failed to clone {dep.gitUrl}: {outp}", useColor)
return 1
else:
if not opts.quiet:
printInfo(&"Using cached '{dep.name}' from {depDir}", useColor)
# Lock stores git URL; build loads from cache by name
lock.entries.add(LockEntry(name: dep.name, version: dep.gitVersion, source: dep.gitUrl))
of dkVersion:
# For version-based deps without a registry, we just record them
# TODO: lookup in registry
lock.entries.add(LockEntry(name: dep.name, version: dep.versionReq, source: "registry"))
if not opts.quiet:
printInfo(&"Recorded dependency '{dep.name}' = {dep.versionReq}", useColor)
# Registry lookup (E.1)
if reg.path.len == 0:
printError(&"cannot resolve '{dep.name}': no package registry (set BUX_REGISTRY)", useColor)
return 1
let pkg = registryLookup(reg, dep.name, dep.versionReq)
if pkg.name.len == 0:
printError(&"package '{dep.name}' not found in registry", useColor)
return 1
if pkg.resolvedPath.len > 0 and dirExists(pkg.resolvedPath):
lock.entries.add(LockEntry(name: dep.name, version: pkg.version, source: pkg.resolvedPath))
if not opts.quiet:
printInfo(&"Resolved '{dep.name}' {pkg.version} → {pkg.resolvedPath}", useColor)
elif isGitSource(pkg.source):
let depDir = cacheDir / dep.name
if not dirExists(depDir):
if not opts.quiet:
printInfo(&"Cloning '{dep.name}' from {pkg.source}...", useColor)
let (outp, code) = execCmdEx(&"git clone --quiet {quoteShell(pkg.source)} {quoteShell(depDir)} 2>&1")
if code != 0:
printError(&"failed to clone {pkg.source}: {outp}", useColor)
return 1
lock.entries.add(LockEntry(name: dep.name, version: pkg.version, source: pkg.source))
if not opts.quiet:
printInfo(&"Resolved '{dep.name}' {pkg.version} → git {pkg.source}", useColor)
else:
printError(&"registry entry '{dep.name}' has unusable source '{pkg.source}'", useColor)
return 1
# Save lockfile
let lockPath = root / "bux.lock"
saveLockfile(lockPath, lock)
@@ -722,18 +810,93 @@ proc cmdClean*(args: seq[string], opts: GlobalOptions): int =
printInfo("clean: build directory removed", useColor)
return 0
proc parseTestArgs(args: seq[string]): tuple[filter: string, paths: seq[string], ok: bool] =
## Parse `test` args: optional `--filter <s>` / `--filter=<s>`, rest are ignored paths.
result.filter = ""
result.paths = @[]
result.ok = true
var i = 0
while i < args.len:
let a = args[i]
if a == "--filter":
if i + 1 >= args.len:
stderr.writeLine("error: --filter requires an argument")
result.ok = false
return
inc i
result.filter = args[i]
elif a.startsWith("--filter="):
result.filter = a["--filter=".len .. ^1]
elif a == "--help" or a == "-h":
echo "Usage: bux test [--filter <name>] [project-dir]"
echo " --filter <name> Only run tests whose filename contains <name>"
result.ok = false # treat as early exit without error in caller? use special
# Signal help via empty filter and a sentinel path
result.paths = @["__help__"]
return
elif a.startsWith("-"):
stderr.writeLine(&"error: unknown test option '{a}'")
result.ok = false
return
else:
result.paths.add(a)
inc i
proc parseFmtArgs(args: seq[string]): tuple[checkOnly: bool, paths: seq[string], ok: bool, help: bool] =
result.checkOnly = false
result.paths = @[]
result.ok = true
result.help = false
var i = 0
while i < args.len:
let a = args[i]
if a == "--check":
result.checkOnly = true
elif a == "--help" or a == "-h":
result.help = true
return
elif a.startsWith("-"):
stderr.writeLine(&"error: unknown fmt option '{a}'")
result.ok = false
return
else:
result.paths.add(a)
inc i
proc cmdTest*(args: seq[string], opts: GlobalOptions): int =
let useColor = shouldUseColor(opts)
let root = getCurrentDir()
let (filter, paths, ok) = parseTestArgs(args)
if not ok:
if paths.len == 1 and paths[0] == "__help__":
return 0
return 1
let root = if paths.len > 0: absolutePath(paths[0]) else: getCurrentDir()
let testsDir = root / "tests"
var testFiles: seq[string] = @[]
if dirExists(testsDir):
for kind, path in walkDir(testsDir):
if kind == pcFile and path.endsWith(".bux"):
let testName = splitFile(path).name
if filter.len > 0 and filter notin testName:
continue
testFiles.add(path)
testFiles.sort(system.cmp)
if testFiles.len == 0:
printError("no tests found in tests/ directory", useColor)
if filter.len > 0:
printError(&"no tests matching filter '{filter}' in tests/", useColor)
else:
printError("no tests found in tests/ directory", useColor)
return 1
if not opts.quiet:
if filter.len > 0:
echo &"Running tests (filter: {filter}) in {testsDir}"
else:
echo &"Running tests in {testsDir}"
echo "┌──────────────────────────────┬────────┐"
echo "│ Test │ Status │"
echo "├──────────────────────────────┼────────┤"
var passed = 0
var failed = 0
for testFile in testFiles:
@@ -742,25 +905,167 @@ proc cmdTest*(args: seq[string], opts: GlobalOptions): int =
removeDir(tmpDir)
createDir(tmpDir / "src")
copyFile(testFile, tmpDir / "src" / "Main.bux")
writeFile(tmpDir / "bux.toml", "[package]\nname = \"" & testName & "\"\nversion = \"0.1.0\"\n")
writeFile(tmpDir / "bux.toml",
"[Package]\nName = \"" & testName & "\"\nVersion = \"0.1.0\"\nType = \"bin\"\n\n[Build]\nOutput = \"Bin\"\n")
let buildRes = cmdBuild(@[tmpDir], opts)
var status: string
var statusOk = false
if buildRes != 0:
printError(&" FAIL {testName} (build)", useColor)
status = "FAIL"
failed += 1
else:
var execFile = tmpDir / "build" / testName
if not fileExists(execFile):
execFile = tmpDir / "build" / "bux_out"
let exitCode = execCmd(execFile)
if exitCode == 0:
status = "PASS"
statusOk = true
passed += 1
else:
status = &"FAIL:{exitCode}"
failed += 1
removeDir(tmpDir)
if not opts.quiet:
# Pad name to 28 chars for the table column
var nameCol = testName
if nameCol.len > 28:
nameCol = nameCol[0 .. 24] & "..."
else:
nameCol = nameCol & repeat(' ', 28 - nameCol.len)
var stCol = status
if stCol.len < 6:
stCol = stCol & repeat(' ', 6 - stCol.len)
if useColor:
if statusOk:
stdout.setForegroundColor(fgGreen)
else:
stdout.setForegroundColor(fgRed)
stdout.writeLine(&"│ {nameCol} │ {stCol} │")
stdout.resetAttributes()
else:
echo &"│ {nameCol} │ {stCol} │"
if not opts.quiet:
echo "└──────────────────────────────┴────────┘"
echo &"\nResults: {passed} passed, {failed} failed, {testFiles.len} total"
# CI-friendly exit codes: 0 = all pass, 1 = some failed
return if failed > 0: 1 else: 0
proc cmdFmt*(args: seq[string], opts: GlobalOptions): int =
let useColor = shouldUseColor(opts)
let (checkOnly, paths, ok, help) = parseFmtArgs(args)
if not ok:
return 1
if help:
echo "Usage: bux fmt [--check] [path...]"
echo " --check Do not write; exit 1 if any file would be reformatted"
echo " path File or directory (default: .)"
return 0
let targets = if paths.len > 0: paths else: @["."]
var files: seq[string] = @[]
for t in targets:
let collected = collectBuxFiles(t)
for f in collected:
if f notin files:
files.add(f)
files.sort(system.cmp)
if files.len == 0:
printError("no .bux files found", useColor)
return 1
var changed = 0
var failed = 0
var unchanged = 0
for path in files:
let (okf, didChange, msg) = formatFile(path, checkOnly)
if not okf:
printError(&"{path}: {msg}", useColor)
failed += 1
continue
var execFile = tmpDir / "build" / testName
if not fileExists(execFile):
execFile = tmpDir / "build" / "bux_out"
let exitCode = execCmd(execFile)
if exitCode == 0:
printInfo(&" PASS {testName}", useColor)
passed += 1
if didChange:
changed += 1
if not opts.quiet:
if checkOnly:
printError(&" would reformat {path}", useColor)
else:
printInfo(&" formatted {path}", useColor)
else:
printError(&" FAIL {testName} (exit {exitCode})", useColor)
failed += 1
removeDir(tmpDir)
echo &"\nResults: {passed} passed, {failed} failed"
return if failed > 0: 1 else: 0
unchanged += 1
if opts.verbose and not opts.quiet:
echo &" ok {path}"
if not opts.quiet:
if checkOnly:
echo &"\nfmt --check: {changed} would reformat, {unchanged} ok, {failed} errors"
else:
echo &"\nFormatted {changed}/{files.len} files ({unchanged} already clean)"
if failed > 0:
return 1
if checkOnly and changed > 0:
return 1
return 0
proc cmdDoc*(args: seq[string], opts: GlobalOptions): int =
## Generate Markdown docs from `///` / adjacent `/* */` comments.
var outPath = ""
var paths: seq[string] = @[]
var i = 0
while i < args.len:
let a = args[i]
if a == "--out" or a == "-o":
if i + 1 >= args.len:
stderr.writeLine("error: --out requires a path")
return 1
inc i
outPath = args[i]
elif a.startsWith("--out="):
outPath = a["--out=".len .. ^1]
elif a == "--help" or a == "-h":
echo "Usage: bux doc [--out file.md] [path...]"
echo " Scans .bux files for /// and /* */ docs preceding declarations."
echo " Default path: lib/ (stdlib) when omitted."
return 0
elif a.startsWith("-"):
stderr.writeLine(&"error: unknown doc option '{a}'")
return 1
else:
paths.add(a)
inc i
if paths.len == 0:
# Prefer stdlib if present
if dirExists("lib"):
paths = @["lib"]
else:
paths = @["."]
let items = generateDocs(paths)
let title =
if paths.len == 1 and paths[0] == "lib": "Bux Standard Library"
else: "API Reference"
let md = renderMarkdown(items, title)
if outPath.len > 0:
try:
let parent = parentDir(outPath)
if parent.len > 0 and not dirExists(parent):
createDir(parent)
writeFile(outPath, md)
if not opts.quiet:
echo &"Wrote {items.len} documented items → {outPath}"
except CatchableError as e:
stderr.writeLine("error: " & e.msg)
return 1
else:
stdout.write(md)
if items.len == 0 and not opts.quiet:
stderr.writeLine("warning: no /// or /* */ documented declarations found")
return 0
proc cmdVersion*(args: seq[string], opts: GlobalOptions): int =
echo "bux 0.1.0 (bootstrap)"
@@ -782,10 +1087,13 @@ proc runCli*(args: seq[string]): int =
of "init": return cmdInit(cmdArgs, opts)
of "add": return cmdAdd(cmdArgs, opts)
of "install": return cmdInstall(cmdArgs, opts)
of "search": return cmdSearch(cmdArgs, opts)
of "build": return cmdBuild(cmdArgs, opts)
of "run": return cmdRun(cmdArgs, opts)
of "check": return cmdCheck(cmdArgs, opts)
of "test": return cmdTest(cmdArgs, opts)
of "fmt": return cmdFmt(cmdArgs, opts)
of "doc": return cmdDoc(cmdArgs, opts)
of "clean": return cmdClean(cmdArgs, opts)
of "version", "--version", "-v": return cmdVersion(cmdArgs, opts)
of "help", "--help", "-h":
+202
View File
@@ -0,0 +1,202 @@
## docgen.nim — Extract `///` (and adjacent `/* */`) docs into Markdown.
## Used by `bux doc [path...]`.
import std/[os, strutils, strformat, algorithm]
type
DocItem* = object
kind*: string ## module | func | struct | enum | interface | extern
name*: string
signature*: string ## first declaration line (trimmed)
docs*: string
file*: string
line*: int
proc isDeclStart(line: string): bool =
let s = line.strip()
if s.len == 0: return false
# Leading attributes @[Checked] etc. — not a decl by themselves
if s.startsWith("@["): return false
if s.startsWith("func ") or s.startsWith("pub func ") or
s.startsWith("extern func ") or s.startsWith("const func ") or
s.startsWith("async func "):
return true
if s.startsWith("struct ") or s.startsWith("pub struct ") or
s.startsWith("enum ") or s.startsWith("pub enum ") or
s.startsWith("union ") or s.startsWith("interface ") or
s.startsWith("module ") or s.startsWith("type "):
return true
return false
proc declKindAndName(line: string): tuple[kind, name: string] =
var s = line.strip()
# Strip leading pub/extern/const/async
for prefix in ["pub ", "extern ", "const ", "async "]:
if s.startsWith(prefix):
s = s[prefix.len .. ^1].strip()
var kind = "item"
if s.startsWith("func "):
kind = "func"
s = s["func ".len .. ^1]
elif s.startsWith("struct "):
kind = "struct"
s = s["struct ".len .. ^1]
elif s.startsWith("enum "):
kind = "enum"
s = s["enum ".len .. ^1]
elif s.startsWith("union "):
kind = "union"
s = s["union ".len .. ^1]
elif s.startsWith("interface "):
kind = "interface"
s = s["interface ".len .. ^1]
elif s.startsWith("module "):
kind = "module"
s = s["module ".len .. ^1]
elif s.startsWith("type "):
kind = "type"
s = s["type ".len .. ^1]
# Name: until `<` `(` `{` `:` space
var name = ""
for ch in s:
if ch in {' ', '<', '(', '{', ':', ';'}:
break
name.add(ch)
if name.len == 0:
name = s
# extern funcs already stripped "extern "
if kind == "func" and line.strip().startsWith("extern"):
kind = "extern"
return (kind, name)
proc extractDocsFromSource*(source, path: string): seq[DocItem] =
result = @[]
var pending: seq[string] = @[]
var inBlockComment = false
var blockDoc: seq[string] = @[]
var lineNo = 0
for rawLine in source.splitLines():
inc lineNo
var line = rawLine
let stripped = line.strip()
# Block comment handling (/* ... */ used in stdlib today)
if inBlockComment:
let endIdx = stripped.find("*/")
if endIdx >= 0:
let before = stripped[0 ..< endIdx].strip()
if before.len > 0:
blockDoc.add(before)
inBlockComment = false
# Treat completed block as pending doc if non-empty
if blockDoc.len > 0:
pending = blockDoc
blockDoc = @[]
continue
else:
blockDoc.add(stripped)
continue
if stripped.startsWith("/*") and not stripped.startsWith("/***"):
let rest = stripped["/*".len .. ^1]
let endIdx = rest.find("*/")
if endIdx >= 0:
let body = rest[0 ..< endIdx].strip()
if body.len > 0:
pending = @[body]
else:
inBlockComment = true
blockDoc = @[]
let body = rest.strip()
if body.len > 0:
blockDoc.add(body)
continue
# Triple-slash doc comments
if stripped.startsWith("///"):
var body = stripped["///".len .. ^1]
if body.startsWith(" "):
body = body[1 .. ^1]
pending.add(body)
continue
# Empty line: keep pending docs (allow blank lines inside doc blocks)
if stripped.len == 0:
continue
# Attributes immediately before decl: keep pending
if stripped.startsWith("@["):
continue
if isDeclStart(line):
if pending.len > 0:
let (kind, name) = declKindAndName(line)
result.add(DocItem(
kind: kind,
name: name,
signature: stripped,
docs: pending.join("\n"),
file: path,
line: lineNo
))
pending = @[]
continue
# Other code clears pending (except plain // comments)
if stripped.startsWith("//"):
continue
pending = @[]
proc collectBuxFilesForDoc*(root: string): seq[string] =
result = @[]
if fileExists(root) and root.endsWith(".bux"):
result.add(root)
return
if not dirExists(root):
return
for path in walkDirRec(root):
if path.endsWith(".bux"):
result.add(path)
result.sort(system.cmp)
proc renderMarkdown*(items: seq[DocItem], title: string = "API Reference"): string =
var sb: string
sb.add(&"# {title}\n\n")
sb.add("Generated by `bux doc` from `///` and `/* */` documentation comments.\n\n")
if items.len == 0:
sb.add("_No documented items found._\n")
return sb
# Group by file
var byFile: seq[string] = @[]
for it in items:
if it.file notin byFile:
byFile.add(it.file)
byFile.sort(system.cmp)
for f in byFile:
let base = splitFile(f).name
sb.add(&"## `{base}`\n\n")
sb.add(&"_Source: `{f}`_\n\n")
for it in items:
if it.file != f:
continue
sb.add(&"### `{it.name}` _{it.kind}_\n\n")
sb.add("```bux\n")
sb.add(it.signature)
sb.add("\n```\n\n")
if it.docs.len > 0:
sb.add(it.docs)
sb.add("\n\n")
return sb
proc generateDocs*(paths: seq[string]): seq[DocItem] =
result = @[]
var files: seq[string] = @[]
for p in paths:
for f in collectBuxFilesForDoc(p):
if f notin files:
files.add(f)
files.sort(system.cmp)
for f in files:
let src = readFile(f)
result.add(extractDocsFromSource(src, f))
+106
View File
@@ -0,0 +1,106 @@
## fmt.nim — Indentation-based Bux source formatter (bootstrap).
## Mirrors selfhost `src/fmt.bux`: re-indent by brace depth, preserve content.
import std/[strutils, os, algorithm]
proc isInStringOrComment(line: string, pos: int): bool =
## Simplified: track `//`, `"..."`, and `'...'` up to `pos`.
var inString = false
var inChar = false
var inComment = false
var i = 0
while i < pos and i < line.len:
let c = line[i]
let n = if i + 1 < line.len: line[i + 1] else: '\0'
if inComment:
inc i
continue
if c == '/' and n == '/':
inComment = true
inc i
continue
if c == '"' and not inChar:
inString = not inString
if c == '\'' and not inString:
inChar = not inChar
inc i
return inString or inChar or inComment
proc countBraceDelta(line: string): int =
var delta = 0
for i in 0 ..< line.len:
if isInStringOrComment(line, i):
continue
let c = line[i]
if c == '{':
inc delta
elif c == '}':
dec delta
return delta
proc formatSource*(source: string): string =
## Re-indent each non-empty line to 4 spaces × brace depth.
## Idempotent: formatting a clean file is a no-op.
var sb: string
var indent = 0
# Nim's splitLines leaves a trailing "" when the source ends with '\n'.
# Drop that artifact so we don't accumulate blank lines on re-format.
var lines = source.splitLines(keepEol = false)
if source.len > 0 and source.endsWith('\n') and lines.len > 0 and lines[^1].len == 0:
lines.setLen(lines.len - 1)
for line in lines:
let trimmed = line.strip(leading = true, trailing = false)
if trimmed.len == 0:
sb.add('\n')
continue
let delta = countBraceDelta(trimmed)
let firstChar = trimmed[0]
if firstChar == '}':
dec indent
if indent < 0:
indent = 0
for _ in 0 ..< indent:
sb.add(" ")
sb.add(trimmed)
sb.add('\n')
if firstChar != '}':
indent = indent + delta
else:
# Net delta after the initial decrease for a leading `}`
indent = indent + delta + 1
if indent < 0:
indent = 0
return sb
proc formatFile*(path: string, checkOnly: bool): tuple[ok: bool, changed: bool, msg: string] =
## Format `path` in place, or only check if reformatting would change it.
if not fileExists(path):
return (false, false, "file not found: " & path)
let source = readFile(path)
let formatted = formatSource(source)
if formatted == source:
return (true, false, "")
if checkOnly:
return (true, true, "would reformat")
try:
writeFile(path, formatted)
return (true, true, "formatted")
except CatchableError as e:
return (false, false, e.msg)
proc collectBuxFiles*(root: string): seq[string] =
## Collect `.bux` files: single file, or recursive directory walk.
result = @[]
if fileExists(root) and root.endsWith(".bux"):
result.add(root)
return
if not dirExists(root):
return
for path in walkDirRec(root):
if path.endsWith(".bux"):
result.add(path)
result.sort(system.cmp)
+167
View File
@@ -0,0 +1,167 @@
## registry.nim — Bux package registry index (E.1)
##
## Index format (TOML-ish, one package per [[package]] table):
##
## [[package]]
## name = "greet"
## version = "0.1.0"
## source = "file:packages/greet" # relative to the registry file
## description = "Hello helpers"
##
## [[package]]
## name = "net"
## version = "1.2.0"
## source = "https://github.com/bux-lang/net.git"
##
## Lookup order for the index file:
## 1. $BUX_REGISTRY (file path)
## 2. ~/.bux/registry.toml
## 3. <repo>/config/registry.toml next to the compiler / cwd
import std/[os, strutils, strformat, algorithm]
type
RegistryPackage* = object
name*: string
version*: string
source*: string ## raw source as written in the index
description*: string
resolvedPath*: string ## absolute path for file: sources (filled on load)
Registry* = object
path*: string ## index file path
packages*: seq[RegistryPackage]
proc resolvePackageSource(pkg: var RegistryPackage, indexDir: string) =
if pkg.source.startsWith("file:"):
var p = pkg.source["file:".len .. ^1]
if p.startsWith("//"):
p = p[2 .. ^1]
if not p.isAbsolute:
p = indexDir / p
pkg.resolvedPath = p.absolutePath
elif pkg.source.startsWith("path:"):
var p = pkg.source["path:".len .. ^1]
if not p.isAbsolute:
p = indexDir / p
pkg.resolvedPath = p.absolutePath
pkg.source = "file:" & pkg.resolvedPath
proc parseRegistryToml(content, indexPath: string): seq[RegistryPackage] =
## Minimal parser for repeated [[package]] blocks with string keys.
result = @[]
var cur: RegistryPackage
var inPkg = false
let indexDir = indexPath.parentDir
for raw in content.splitLines():
let line = raw.strip()
if line.len == 0 or line.startsWith("#"):
continue
if line == "[[package]]" or line == "[[Package]]":
if inPkg and cur.name.len > 0:
resolvePackageSource(cur, indexDir)
result.add(cur)
cur = RegistryPackage()
inPkg = true
continue
if not inPkg:
continue
let eq = line.find('=')
if eq < 0: continue
let key = line[0 ..< eq].strip().toLowerAscii()
var val = line[eq + 1 .. ^1].strip()
if val.len >= 2 and val[0] == '"' and val[^1] == '"':
val = val[1 ..< ^1]
case key
of "name": cur.name = val
of "version": cur.version = val
of "source": cur.source = val
of "description": cur.description = val
else: discard
if inPkg and cur.name.len > 0:
resolvePackageSource(cur, indexDir)
result.add(cur)
proc findRegistryIndex*(): string =
## Locate the registry index file.
let env = getEnv("BUX_REGISTRY")
if env.len > 0 and fileExists(env):
return env.absolutePath
let homeIdx = getHomeDir() / ".bux" / "registry.toml"
if fileExists(homeIdx):
return homeIdx
let candidates = @[
getAppDir() / ".." / "config" / "registry.toml",
getAppDir() / "config" / "registry.toml",
getCurrentDir() / "config" / "registry.toml",
getCurrentDir() / ".." / "config" / "registry.toml",
]
for c in candidates:
if fileExists(c):
return c.absolutePath
return ""
proc loadRegistry*(path: string = ""): Registry =
result.path = if path.len > 0: path else: findRegistryIndex()
result.packages = @[]
if result.path.len == 0 or not fileExists(result.path):
return
try:
let content = readFile(result.path)
result.packages = parseRegistryToml(content, result.path)
except CatchableError:
result.packages = @[]
proc registryLookup*(reg: Registry, name: string, versionReq: string = "*"): RegistryPackage =
## Find a package by name. versionReq `*` picks the last matching entry
## (index order; put newest last). Exact version matches preferred.
result = RegistryPackage()
var candidates: seq[RegistryPackage] = @[]
for p in reg.packages:
if p.name.toLowerAscii() == name.toLowerAscii():
candidates.add(p)
if candidates.len == 0:
return
if versionReq.len == 0 or versionReq == "*":
return candidates[^1]
for p in candidates:
if p.version == versionReq:
return p
# Semver prefix match: "1" matches "1.0.0"
for p in candidates:
if p.version.startsWith(versionReq):
return p
return candidates[^1]
proc registrySearch*(reg: Registry, query: string): seq[RegistryPackage] =
result = @[]
let q = query.toLowerAscii()
for p in reg.packages:
if q.len == 0 or
q in p.name.toLowerAscii() or
q in p.description.toLowerAscii():
result.add(p)
result.sort(proc (a, b: RegistryPackage): int =
cmp(a.name.toLowerAscii(), b.name.toLowerAscii()))
proc isGitSource*(source: string): bool =
source.startsWith("http://") or source.startsWith("https://") or
source.startsWith("git@") or source.startsWith("git://") or
source.startsWith("ssh://")
proc isFileSource*(source: string): bool =
source.startsWith("file:") or source.startsWith("path:")
proc formatRegistryDepLine*(name: string, pkg: RegistryPackage): string =
## Produce a bux.toml Dependencies line for a resolved registry package.
if pkg.resolvedPath.len > 0 and dirExists(pkg.resolvedPath):
return &"{name} = {{ Path = \"{pkg.resolvedPath}\" }}"
if isGitSource(pkg.source):
let ver = if pkg.version.len > 0: pkg.version else: "*"
return &"{name} = {{ Version = \"{ver}\", Source = \"{pkg.source}\" }}"
if isFileSource(pkg.source) and pkg.resolvedPath.len > 0:
return &"{name} = {{ Path = \"{pkg.resolvedPath}\" }}"
# Fallback: version-only (install will re-resolve)
let ver = if pkg.version.len > 0: pkg.version else: "*"
return &"{name} = \"{ver}\""
+178 -5
View File
@@ -51,6 +51,11 @@ type
## When true, ekIdent skips use-while-borrowed (we're forming `&x` itself)
suppressUseWhileBorrow*: bool
currentRetType*: Type ## return type of the function being checked
## Lifetime elision / ref-origin tracking (@[Checked] only)
## Binding name → lifetime id ("'a", "#elided0", "#local", …)
varRefLifetime*: Table[string, string]
## Expected lifetime of the function's returned reference ("" if ret is not a ref)
returnLifetime*: string
closureDepth*: int ## nesting depth inside closures
currentClosureExpr*: Expr ## current closure being analyzed
closureScope*: Scope ## scope at which the current closure was entered
@@ -164,6 +169,138 @@ proc checkTempMutBorrow(sema: var Sema, varName: string, loc: SourceLocation) =
elif sema.activeSharedBorrows.getOrDefault(varName, 0) > 0:
sema.emitError(loc, &"cannot mutably borrow '{varName}' while it is shared-borrowed")
# ---------------------------------------------------------------------------
# Lifetime elision (C.1) — Rust-style simple rules for @[Checked]
# ---------------------------------------------------------------------------
#
# Rules (common cases, no annotations required):
# 1. Each elided input reference (&T / &mut T param) gets a distinct lifetime.
# 2. If there is exactly one input lifetime, it is assigned to all elided outputs.
# 3. If the first param is `self` / `Self`, its lifetime is preferred for outputs.
# 4. Multiple input refs + elided return → error (need explicit `'a`).
# 5. Returning a reference derived from a local (or by-value param) is rejected.
#
const
LifetimeLocal* = "#local" ## ref derived from a local / by-value place
LifetimeOutNone* = "#out" ## return ref with no input to borrow from
LifetimeAmbiguous* = "#ambiguous"
proc isRefTypeExpr(te: TypeExpr): bool =
te != nil and te.kind in {tekRef, tekMutRef}
proc applyLifetimeElision*(sema: var Sema, decl: Decl) =
## Assign elided lifetimes for ref params/return of `decl`. Populates
## `varRefLifetime` (params) and `returnLifetime`.
sema.varRefLifetime = initTable[string, string]()
sema.returnLifetime = ""
if not sema.checkedFunc:
return
var inputLts: seq[string] = @[]
var anon = 0
for p in decl.declFuncParams:
if not isRefTypeExpr(p.ptype):
continue
var lt = p.ptype.refLifetime
if lt.len == 0:
lt = "#elided" & $anon
inc anon
inputLts.add(lt)
sema.varRefLifetime[p.name] = lt
let ret = decl.declFuncReturnType
if not isRefTypeExpr(ret):
return
var rlt = ret.refLifetime
if rlt.len == 0:
if inputLts.len == 1:
rlt = inputLts[0]
elif inputLts.len == 0:
rlt = LifetimeOutNone
elif decl.declFuncParams.len > 0 and
decl.declFuncParams[0].name in ["self", "Self"]:
rlt = inputLts[0]
else:
sema.emitError(decl.loc,
"lifetime elision failed: return type needs an explicit lifetime " &
"(multiple input references); e.g. func F<'a>(a: &'a T, b: &'a U) -> &'a T")
rlt = LifetimeAmbiguous
sema.returnLifetime = rlt
proc exprRefLifetime*(sema: Sema, expr: Expr, scope: Scope): string =
## Best-effort lifetime of a reference-producing expression.
if expr == nil:
return ""
case expr.kind
of ekIdent:
if sema.varRefLifetime.hasKey(expr.exprIdent):
return sema.varRefLifetime[expr.exprIdent]
return ""
of ekUnary:
if expr.exprUnaryOp == tkAmp:
let name = extractBorrowedIdent(expr)
if name.len == 0:
return LifetimeLocal
# Reborrow of an existing ref binding keeps its lifetime
if sema.varRefLifetime.hasKey(name):
return sema.varRefLifetime[name]
# Address-of a by-value local or by-value parameter → local (dangling if returned)
return LifetimeLocal
# Dereference: *r still carries r's lifetime for field/ref purposes
if expr.exprUnaryOp == tkStar:
return sema.exprRefLifetime(expr.exprUnaryOperand, scope)
return ""
of ekBorrow:
# `borrow &x` / `borrow &mut x` — same origin rules as unary &
if expr.exprBorrowOperand != nil:
return sema.exprRefLifetime(expr.exprBorrowOperand, scope)
return LifetimeLocal
of ekField:
# Field projection through a ref keeps the base lifetime: (*p).x or p.x
if expr.exprFieldObj != nil:
let baseLt = sema.exprRefLifetime(expr.exprFieldObj, scope)
if baseLt.len > 0:
return baseLt
# Base is an ident of a struct local — field address would be local
if expr.exprFieldObj.kind == ekIdent:
if sema.varRefLifetime.hasKey(expr.exprFieldObj.exprIdent):
return sema.varRefLifetime[expr.exprFieldObj.exprIdent]
return LifetimeLocal
return ""
else:
return ""
proc checkReturnLifetime*(sema: var Sema, retExpr: Expr, scope: Scope, loc: SourceLocation) =
## Reject dangling returns and explicit lifetime mismatches in @[Checked].
if not sema.checkedFunc or sema.returnLifetime.len == 0 or retExpr == nil:
return
let got = sema.exprRefLifetime(retExpr, scope)
if sema.returnLifetime == LifetimeOutNone:
sema.emitError(loc,
"cannot return a reference: function has no input reference to borrow from")
return
if got == LifetimeLocal:
sema.emitError(loc, "cannot return reference to local variable")
return
if got.len == 0:
# Non-trivial expression (call, etc.) — leave for later analysis
return
if got == LifetimeAmbiguous or sema.returnLifetime == LifetimeAmbiguous:
return
# Explicit lifetime mismatch (both sides named with ')
if got.startsWith("'") and sema.returnLifetime.startsWith("'") and got != sema.returnLifetime:
sema.emitError(loc,
&"lifetime mismatch: returning '{got}' but function returns '{sema.returnLifetime}'")
return
# Distinct elided inputs returned into another elided input's return slot
if got.startsWith("#elided") and sema.returnLifetime.startsWith("#elided") and
got != sema.returnLifetime:
sema.emitError(loc,
"lifetime mismatch: returned reference does not outlive the return type " &
"(multiple input references; annotate with an explicit lifetime)")
# ---------------------------------------------------------------------------
# Generic type inference helpers
# ---------------------------------------------------------------------------
@@ -472,7 +609,7 @@ proc inferTypeArgs(sema: var Sema, funcDecl: Decl, argTypes: seq[Type],
# Type resolution from AST TypeExpr
# ---------------------------------------------------------------------------
proc resolveType(sema: var Sema, te: TypeExpr): Type =
proc resolveType*(sema: var Sema, te: TypeExpr): Type =
if te == nil:
return makeUnknown()
case te.kind
@@ -918,7 +1055,7 @@ proc collectGlobals*(sema: var Sema) =
# Expression type checking
# ---------------------------------------------------------------------------
proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type
proc checkExpr*(sema: var Sema, expr: Expr, scope: Scope): Type
proc checkStmt(sema: var Sema, stmt: Stmt, scope: Scope): Type
proc typeImplements(sema: Sema, t: Type, interfaceName: string): bool =
@@ -1099,7 +1236,7 @@ proc resolveCallArgs(sema: var Sema, expr: Expr, calleeDecl: Decl, scope: Scope)
expr.exprCallArgs = newArgs
expr.exprCallArgNames = newNames
proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
proc checkExpr*(sema: var Sema, expr: Expr, scope: Scope): Type =
if expr == nil:
return makeUnknown()
case expr.kind
@@ -1835,6 +1972,11 @@ proc checkStmt(sema: var Sema, stmt: Stmt, scope: Scope): Type =
# Untyped let + `&x` is typed as &mut by unary lowering
isMut = initType.isMutRef
sema.checkCreateBorrow(bname, isMut, stmt.stmtLetInit.loc)
# Propagate ref lifetime to the new binding (for return-site checks)
if declaredType.isRef or declaredType.isMutRef or initType.isRef or initType.isMutRef:
let lt = sema.exprRefLifetime(stmt.stmtLetInit, scope)
if lt.len > 0:
sema.varRefLifetime[stmt.stmtLetName] = lt
return makeVoid()
of skIf:
let condType = sema.checkExpr(stmt.stmtIfCond, scope)
@@ -1897,6 +2039,8 @@ proc checkStmt(sema: var Sema, stmt: Stmt, scope: Scope): Type =
let retSym = scope.lookup(stmt.stmtReturnValue.exprIdent)
if retSym != nil and retSym.isOwn:
sema.movedVars.add(stmt.stmtReturnValue.exprIdent)
# Lifetime: reject dangling returns / explicit mismatches
sema.checkReturnLifetime(stmt.stmtReturnValue, scope, stmt.loc)
return makeVoid()
of skBreak, skContinue:
return makeVoid()
@@ -1951,9 +2095,15 @@ proc checkStmt(sema: var Sema, stmt: Stmt, scope: Scope): Type =
proc checkFunc(sema: var Sema, decl: Decl) =
if decl.declFuncBody == nil:
return
# Skip body type-checking for generic functions — their bodies contain
# Skip body type-checking for type-generic functions — their bodies contain
# type parameters that cannot be fully resolved until monomorphization.
if decl.declFuncTypeParams.len > 0:
# Lifetime-only params (`'a`) are fine: we still check the body for elision.
var hasTypeGeneric = false
for tp in decl.declFuncTypeParams:
if not tp.isLifetime:
hasTypeGeneric = true
break
if hasTypeGeneric:
return
let wasChecked = sema.checkedFunc
let wasAsync = sema.currentFuncIsAsync
@@ -1963,10 +2113,18 @@ proc checkFunc(sema: var Sema, decl: Decl) =
sema.movedVars = @[]
sema.activeMutBorrows = initTable[string, SourceLocation]()
sema.activeSharedBorrows = initTable[string, int]()
# C.1: elide lifetimes on params / return before walking the body
sema.applyLifetimeElision(decl)
else:
sema.varRefLifetime = initTable[string, string]()
sema.returnLifetime = ""
var funcScope = newScope(sema.globalScope)
# Add type parameters to type table for resolution
var addedTypeParams: seq[string] = @[]
for tp in decl.declFuncTypeParams:
if tp.isLifetime:
# Lifetime params are not types; skip typeTable
continue
sema.typeTable[tp.name] = makeTypeParam(tp.name)
addedTypeParams.add(tp.name)
# Add parameters
@@ -1982,6 +2140,8 @@ proc checkFunc(sema: var Sema, decl: Decl) =
sema.typeTable.del(tp)
sema.checkedFunc = wasChecked
sema.currentFuncIsAsync = wasAsync
sema.varRefLifetime = initTable[string, string]()
sema.returnLifetime = ""
# ---------------------------------------------------------------------------
# Second pass: check all function bodies
@@ -2026,3 +2186,16 @@ proc analyzeFull*(modu: Module): tuple[result: SemaResult, sema: Sema] =
sema.collectGlobals()
sema.checkBodies()
result = (SemaResult(diagnostics: sema.diagnostics), sema)
proc checkExprForLsp*(sema: var Sema, expr: Expr, scope: Scope): Type =
## Type-check an expression for IDE use (no borrow/move side effects).
let wasChecked = sema.checkedFunc
let savedMoved = sema.movedVars
let savedMut = sema.activeMutBorrows
let savedShared = sema.activeSharedBorrows
sema.checkedFunc = false
result = sema.checkExpr(expr, scope)
sema.checkedFunc = wasChecked
sema.movedVars = savedMoved
sema.activeMutBorrows = savedMut
sema.activeSharedBorrows = savedShared
+22
View File
@@ -0,0 +1,22 @@
# Bux package registry index (E.1)
# Used by `bux add <name>` and `bux install` when no --path/--git is given.
#
# Override with: export BUX_REGISTRY=/path/to/registry.toml
# Or copy to: ~/.bux/registry.toml
#
# source forms:
# file:relative/or/absolute — local package (relative to this file)
# path:relative/or/absolute — same as file:
# https://...git — git clone into ~/.bux/packages/<name>
[[package]]
name = "greet"
version = "0.1.0"
source = "file:../registry/packages/greet"
description = "Tiny Hello helper library (demo registry package)"
[[package]]
name = "greet"
version = "0.1.1"
source = "file:../registry/packages/greet"
description = "Tiny Hello helper library (demo registry package, patch)"
+49 -4
View File
@@ -152,15 +152,60 @@ This runs:
### Project Tests (`bux test`)
```bash
./buxc test
./buxc test # run all tests/*.bux in the current package
./buxc test --filter first # only tests whose name contains "first"
./buxc test --filter=first _test_runner
```
Builds the project and runs the resulting binary. Reports:
- `Tests passed` on exit code 0
- `Tests failed (exit code N)` on non-zero exit
Discovers `tests/*.bux`, builds each as a temp package, and runs it. Prints a
summary table and exits:
- `0` — all selected tests passed
- `1` — at least one failure, or no tests matched the filter
Use `Std::Test` module for assertions inside test code.
### Format (`bux fmt`)
```bash
./buxc fmt examples/hello.bux # reformat one file
./buxc fmt lib/ # reformat a directory tree
make fmt # reformat lib/ examples/ src/ tests/ apps/
./buxc fmt --check path/ # exit 1 if any file would change
make fmt-check # CI: full-tree clean + dirty smoke
```
Indentation is 4 spaces by brace depth. The formatter is idempotent (safe to re-run).
`make fmt-check` enforces a clean tree under `lib/`, `examples/`, `src/`, `tests/`, and `apps/`.
### Stdlib golden tests
```bash
make test-stdlib
# or: tests/stdlib_golden/run.sh ./buxc
```
Behavioral packages under `tests/stdlib_golden/` (`array`, `string`, `collections`)
assert core Array/String/Map/Set/Result/Option APIs and match expected `PASS` lines.
### API docs (`bux doc`)
```bash
./buxc doc lib/ # Markdown to stdout
./buxc doc --out docs/api/stdlib.md lib/
make docs # writes docs/api/stdlib.md
```
Scans `///` line comments (and bootstrap also accepts adjacent `/* */`) immediately
before `func` / `struct` / `enum` / `interface` / `module` declarations.
### Language Server (`bux-lsp` 0.4.0)
```bash
make lsp # → tools/bux-lsp
nim r --path:bootstrap tools/test_lsp_locals.nim
./tools/smoke_lsp_hover.sh
```
Features: diagnostics (`buxc check`), hover, go-to-def, outline, completion.
**Locals are position-sensitive** (nested scopes / shadowing). **Inferred `let` types**
appear on hover (`let x: int · inferred`).
### Example Programs
```bash
make test-examples
+38
View File
@@ -604,6 +604,44 @@ Moves happen in three contexts:
msg = "reassigned"; // OK: reinitialization
PrintLine(msg);
```
- **No dangling returns**: cannot return a reference to a local (or by-value parameter)
```bux
@[Checked]
func Bad(p: &int) -> &int {
var x: int = 1;
return &x; // ERROR: cannot return reference to local variable
}
```
### Lifetime elision (C.1)
In `@[Checked]` functions, most reference signatures need **no** lifetime annotations.
Elision applies the usual single-input rules:
1. Each elided input `&T` / `&mut T` parameter gets a distinct lifetime.
2. If there is **exactly one** input lifetime, it is assigned to all elided outputs.
3. If the first parameter is named `self` / `Self`, that input lifetime is preferred for outputs.
4. Multiple input references + elided return → **error** (write an explicit lifetime).
```bux
// Elided — one input ref, return shares its lifetime
@[Checked]
func Identity(p: &int) -> &int {
return p; // OK
}
// Explicit — required when several inputs could be returned
@[Checked]
func Pick<'a>(a: &'a int, b: &'a int) -> &'a int {
return a;
}
// Syntax: &'a T and &mut / &'a mut T (lifetime before `mut`)
// Type parameters: func F<'a, T>(...)
```
Unchecked functions ignore lifetime rules (C-like). Explicit `'a` is optional
documentation when a single input would already elide correctly.
---
+76 -28
View File
@@ -1,6 +1,8 @@
# Bux Package Manager
> **Status:** Implemented (Phase 9.1)
> **Status:** Path + git + **local/file registry** (E.1). HTTP registry index URL optional later.
See also: [SEMVER.md](SEMVER.md) for version policy.
---
@@ -20,52 +22,97 @@ License = "MIT"
Output = "Bin"
[Dependencies]
Std = "1.0"
Json = { Version = "2.1", Source = "https://github.com/bux-lang/json" }
greet = { Path = "/abs/path/to/greet" }
Json = { Version = "2.1", Source = "https://github.com/bux-lang/json" }
Utils = { Path = "../Utils" }
# Registry name-only (resolved by `bux add` / `bux install`):
# greet = "0.1.1"
```
### Dependency Forms
| Form | Example | Description |
|------|---------|-------------|
| Version string | `Std = "1.0"` | Registry dependency |
| Wildcard | `Std = "*"` | Latest version |
| Version string | `greet = "0.1.1"` | Registry dependency |
| Wildcard | `greet = "*"` | Latest registry version |
| Inline table (git) | `{ Version = "1.4", Source = "https://..." }` | Git URL + version |
| Inline table (path) | `{ Path = "../Lib" }` | Local path dependency |
---
## Package registry (E.1)
### Index file
Default locations (first hit wins):
1. `$BUX_REGISTRY` — path to a `registry.toml`
2. `~/.bux/registry.toml`
3. `config/registry.toml` next to the Bux repo / compiler
Format:
```toml
[[package]]
name = "greet"
version = "0.1.1"
source = "file:../registry/packages/greet" # relative to the index file
description = "Hello helpers"
[[package]]
name = "net"
version = "1.0.0"
source = "https://github.com/example/bux-net.git"
description = "TCP helpers"
```
`file:` / `path:` sources are resolved relative to the registry file.
Git URLs are cloned into `~/.bux/packages/<name>/` on install.
### CLI
```bash
# Search the index
bux search
bux search greet
# Add by registry name (writes Path or git Source into bux.toml)
bux add greet
bux add greet 0.1.1
# Explicit sources still work
bux add utils --path "../utils"
bux add network --git "https://github.com/bux-lang/network"
# Resolve + write bux.lock
bux install
```
Demo package in this monorepo: `registry/packages/greet` (registered in
`config/registry.toml`). Smoke test: `tools/smoke_registry.sh`.
---
## CLI Commands
### `bux add <name> [version]`
Add a dependency to `bux.toml`.
Add a dependency to `bux.toml` (registry / `--path` / `--git`).
```bash
# Add registry dependency
bux add json "2.1"
### `bux search [query]`
# Add path-based dependency
bux add utils --path "../utils"
# Add git dependency
bux add network --git "https://github.com/bux-lang/network"
```
List packages in the active registry (filter by name/description).
### `bux install`
Resolve dependencies and generate `bux.lock`.
```bash
bux install
```
What it does:
1. Reads `[Dependencies]` from `bux.toml`
2. Resolves path-based deps (verifies directory exists)
3. Clones/pulls git-based deps to `~/.bux/packages/<name>/`
4. Generates `bux.lock` with exact versions and sources
3. Clones git-based deps to `~/.bux/packages/<name>/`
4. Resolves bare version names via the registry index
5. Generates `bux.lock` with exact versions and sources
### `bux build` / `bux run`
@@ -84,10 +131,9 @@ Auto-generated. **Do not edit manually.**
```toml
[[Package]]
Name = "json"
Version = "2.1.3"
Source = "https://github.com/bux-lang/json"
Checksum = "8dcb2a7f..."
Name = "greet"
Version = "0.1.1"
Source = "/home/user/z-git/bux/bux/registry/packages/greet"
[[Package]]
Name = "utils"
@@ -103,7 +149,7 @@ The lockfile ensures **reproducible builds** — every developer gets the exact
1. **Path-based** deps are resolved relative to the manifest directory
2. **Git-based** deps are cloned to `~/.bux/packages/<name>/`
3. **Version-based** deps (without Source) require a registry (future feature)
3. **Version-based** deps look up `config/registry.toml` (or `$BUX_REGISTRY`)
4. Dependencies are loaded from `<dep>/src/*.bux` at build time
5. Later declarations shadow earlier ones (project > deps > stdlib)
@@ -114,8 +160,10 @@ The lockfile ensures **reproducible builds** — every developer gets the exact
```bash
bux new mylib
cd mylib
# Edit src/Main.bux → module MyLib { pub func Add(...) }
bux build # Builds as library (Type = "lib")
# Edit src/*.bux → module MyLib { func Add(...) }
# Set Type = "lib" in bux.toml
# Register in your registry.toml with source = "file:..."
bux build
```
## Example: Using a Library
+131 -23
View File
@@ -1,7 +1,7 @@
# Bux — План към „добър“ език (v0.5 → v1.0)
> **Дата:** 2026-07-18
> **Текущо:** v0.5.x — selfhost loop, gradual ownership, green threads, **43+ examples**, match + guards + **generic HOF inference** + pattern bindings + **`f"..."` interp** bootstrap+selfhost
> **Текущо:** v0.5.x — selfhost, C.1, tooling, LSP 0.4, full-tree fmt, **package registry (E.1)**
> **Цел:** Език, с който се пишат реални проекти комфортно, безопасно (по избор) и с надежден toolchain.
---
@@ -14,11 +14,11 @@
| Sema / generics | Monomorphization, trait bounds basic | ★★★★☆ |
| HIR → C | Tuples + fat `func` ABI в bootstrap **и** selfhost | ★★★★☆ |
| Selfhost (`src/`) | ~12k LOC, binary-identical loop, closures+tuples | ★★★★★ |
| Gradual ownership | `@[Checked]`, `&`/`&mut`, move, Drop | ★★★☆☆ (basic) |
| Gradual ownership | `@[Checked]`, `&`/`&mut`, move, Drop, **lifetime elision** | ★★★★☆ |
| Concurrency | M:N tasks + channels + async | ★★★★☆ |
| Stdlib | Array/Map/Set/String/Iter HOF разширени | ★★★★☆ |
| Tooling | `test-errors`, LSP diagnostics + hover/def/outline | ★★★★☆ |
| Ecosystem / registry | path+git deps; няма централен registry | ★☆☆☆☆ |
| Ecosystem / registry | path+git + **file registry index** (`bux search/add`) | ★★★☆☆ |
| Документация | README + QUALITY_PLAN синхронизирани (2026-07-15) | ★★★★☆ |
**Силна ниша:** gradual ownership (C-скорост на писане + opt-in Rust-safety).
@@ -69,7 +69,7 @@
| # | Задача | Защо | Статус |
|---|--------|------|--------|
| C.1 | Lifetime elision за common cases | Без `'a` в 90% от API-тата | |
| C.1 | Lifetime elision за common cases | Без `'a` в 90% от API-тата | ✅ bootstrap + selfhost |
| C.2 | Exclusive `&mut` vs shared `&` data-flow | По-малко false negatives | ✅ let-bound + use-while + call conflict |
| C.3 | Auto-drop edge cases (early return, branches) | RAII да е надежден | ✅ bootstrap + selfhost |
| C.4 | `@[Release]` zero-cost path документация + golden tests | Killer story: safe default, free hot path | ✅ partial (unchecked path + goldens) |
@@ -78,21 +78,21 @@
| # | Задача | Защо | Статус |
|---|--------|------|--------|
| D.1 | LSP: hover, go-to-def, diagnostics | IDE = adoption | ✅ hover/def/outline + **sema types on hover** (v0.3.0) + `buxc` diags |
| D.2 | `bux fmt` стабилен + CI check | Единен style | |
| D.3 | `bux test` с `--filter`, exit codes, summary table | CI-friendly | ⏳ partial (`bux test` exists) |
| D.4 | `bux doc` от `///` comments | Самодокументиращ се stdlib | |
| D.5 | Golden tests за stdlib modules | Регресии без изненади | |
| D.1 | LSP: hover, go-to-def, diagnostics | IDE = adoption | ✅ v0.4.0: **position-sensitive locals** + **inferred `let`** + sema hover |
| D.2 | `bux fmt` стабилен + CI check | Единен style | ✅ full-tree format + `make fmt-check` enforce |
| D.3 | `bux test` с `--filter`, exit codes, summary table | CI-friendly | `--filter` / summary / exit 0\|1 |
| D.4 | `bux doc` от `///` comments | Самодокументиращ се stdlib | ✅ bootstrap+selfhost + `make docs` |
| D.5 | Golden tests за stdlib modules | Регресии без изненади | `tests/stdlib_golden/` + `make test-stdlib` |
### E — Ecosystem & v1.0 (P2)
| # | Задача | Защо |
|---|--------|------|
| E.1 | Package registry protocol (git/HTTP) | `bux add foo` без path hacks |
| E.2 | 35 production-quality apps в `apps/` | Showcase |
| E.3 | Language freeze + semver policy | Trust |
| E.4 | Debugger/DWARF basics | Systems audience |
| E.5 | Benchmarks vs C/Zig/Nim (micro + nexus) | Marketing + regression |
| # | Задача | Защо | Статус |
|---|--------|------|--------|
| E.1 | Package registry protocol (git/HTTP) | `bux add foo` без path hacks | ✅ local index + file/git sources + `search` |
| E.2 | 35 production-quality apps в `apps/` | Showcase | ⏳ partial (`nexus`, `boko`, `simpledb`, `jwt-pitbul`) |
| E.3 | Language freeze + semver policy | Trust | ✅ draft `docs/SEMVER.md` |
| E.4 | Debugger/DWARF basics | Systems audience | ⏳ |
| E.5 | Benchmarks vs C/Zig/Nim (micro + nexus) | Marketing + regression | ⏳ |
---
@@ -114,10 +114,10 @@ A (stdlib ergonomics) → B (compiler holes) → C (ownership depth)
- [ ] Всички examples + selfhost-loop + 3 apps минават на CI
- [ ] Array/Map/String/Test API покрива 90% от ежедневните нужди
- [ ] `@[Checked]` хваща use-after-move + double `&mut` в documented subset
- [ ] `bux test` + `bux fmt` + `bux check` са default developer loop
- [ ] LanguageRef синхронизиран с компилатора
- [ ] Поне един външен проект (не в monorepo) build-ва с git dep
- [x] `@[Checked]` хваща use-after-move + double `&mut` + dangling return / elision fail
- [x] `bux test` + `bux fmt` + `bux check` са default developer loop (`--filter` / `--check` shipped)
- [x] LanguageRef синхронизиран с компилатора (incl. C.1 elision)
- [x] Поне един външен/temp проект build-ва с registry dep (`tools/smoke_registry.sh`)
---
@@ -388,8 +388,116 @@ A (stdlib ergonomics) → B (compiler holes) → C (ownership depth)
---
## Сесия 24 (tooling — D.2 fmt --check + D.3 test --filter)
1. **Bootstrap `bux fmt`** (`bootstrap/fmt.nim`):
- Indent-by-brace-depth formatter (parity with `src/fmt.bux`)
- `bux fmt [path...]` writes; `bux fmt --check` exits 1 if any file would change
- Collects single file or recursive `.bux` under directories
2. **Bootstrap `bux test --filter`**:
- `--filter <s>` / `--filter=<s>` — only run `tests/*.bux` whose name contains `s`
- Summary table (`PASS` / `FAIL[:code]`) + `Results: N passed, M failed, T total`
- Exit `0` all pass, `1` failures or no match
3. **Selfhost parity** (`src/cli.bux`, `src/fmt.bux`):
- `Fmt_WouldChange` / `Fmt_CheckFile`; `Cli_Fmt(dir, checkOnly)`
- `Cli_Test(dir, filter)` with summary + skip count; filter skips Main package run
4. **CI hooks:** `make fmt-check` smoke (clean→0, dirty→1); full-tree enforce deferred
until a one-shot format pass on `lib/`/`examples/`
5. **Idempotence fix:** drop trailing split-empty so re-format is a no-op
6. Verified: unit suite + `./buxc test --filter first _test_runner` + selfhost `buxc2`
fmt/test parity
---
## Сесия 25 (Ownership 2.0 — C.1 lifetime elision)
1. **Elision rules** in `@[Checked]` (`bootstrap/sema.nim`):
- Each elided input `&`/`&mut` → distinct `#elidedN`
- One input lifetime → assigned to elided return
- First param `self`/`Self` preferred when multiple inputs
- Multiple inputs + elided return → `lifetime elision failed` (need `'a`)
2. **Return checks:**
- `cannot return reference to local variable` (`return &local` / let-bound local ref)
- `no input reference to borrow from` (return ref with zero input refs)
- Explicit `'a` mismatch between return and value
3. **Body check for lifetime-only generics** (`func F<'a>(...)`) — no longer skipped
4. **Diagnostics hints** for elision / dangling / mismatch
5. **Tests:** 8 new borrow_test cases; goldens `return_local_ref`, `elision_multi_input`
6. **Example:** `examples/lifetime_elision.bux` (Identity / explicit / ViaLet / self)
7. LanguageRef + QUALITY_PLAN updated
8. Verified: borrow_test 24/24, 9 error goldens, example runs
## Сесия 26 (C.1 selfhost parity)
1. **Lexer** (`src/lexer.bux` + `tkLifetime=111`): `'a` vs char `'x'` (same heuristic as bootstrap)
2. **Parser:**
- `&'a T` / `&'a mut T` → `TypeExpr.refLifetime`
- `func F<'a, T>(…)` — lifetime params accepted and **skipped** for mono slots
3. **Sema** lifetime elision (fixed 8-slot maps, same rules as bootstrap):
- single-input elision, `self` preference, multi-input fail
- return-local / no-input-ref / explicit mismatch
- let-bound ref lifetime propagation
4. Fixed `checkFunc` else-branch that wiped `checkedFunc` when retType was void
5. Verified: `buxc2 run lifetime_elision` PASS; goldens on buxc2 show same errors;
bootstrap still green; **selfhost-loop** expected IDENTICAL
---
## Сесия 27 (tooling — D.4 bux doc + D.5 stdlib goldens)
1. **D.5 Stdlib goldens** (`tests/stdlib_golden/`):
- Packages: `array`, `string`, `collections` (Map/Set/Result/Option)
- `run.sh` builds via `buxc run` and matches expected PASS lines
- `make test-stdlib` wired into `make test`
2. **D.4 `bux doc`**:
- Bootstrap: `bootstrap/docgen.nim` — `///` + adjacent `/* */`
- Selfhost: `Cli_Doc` line scanner for `///`
- `bux doc [--out file] [path]` (default path `lib/`)
- `make docs` → `docs/api/stdlib.md`
3. **Stdlib docs:** `///` on Array / String / Test public helpers
4. Verified: `make test-stdlib`, `./buxc doc lib/Array.bux | head`, selfhost build
---
## Сесия 28 (LSP v0.4.0 — position-sensitive locals + inferred lets)
1. **`LocalBinding`** with scope range (`scopeStartLine`…`scopeEndLine`) per let/param
2. **Sema-backed inference** (`checkExprForLsp` / `resolveType`):
- `let x = 42` → hover `let x: int` · inferred
- `let s: String = "…"` → annotated, not inferred
- params: `param a: int` visible for whole function
3. **Position-sensitive** hover / go-to-def / completion (innermost scope wins on shadowing)
4. Nested scopes: if/while/for/match/block arms
5. Version **bux-lsp 0.4.0**; tests: `tools/test_lsp_locals.nim`, `tools/smoke_lsp_hover.sh`
6. Verified: hover shows `let sum: int · inferred`, `param a: int`, `let n: int · inferred`
---
## Сесия 29 (full-tree `bux fmt` + CI enforce)
1. **One-shot format** of `lib/` (33), `examples/` (23), `src/` (15), `tests/` (8), `apps/` (12)
2. **Idempotent:** second `--check` → 0 would reformat on all trees
3. **CI:** `make fmt-check` enforces full tree + dirty-path smoke (exit 1)
4. **`make fmt`** helper to reformat the same roots
5. Verified: `test-stdlib`, key examples, **selfhost + selfhost-loop IDENTICAL ✓**
---
## Сесия 30 (E.1 package registry + E.3 semver draft)
1. **Registry index** (`config/registry.toml`, `$BUX_REGISTRY`, `~/.bux/registry.toml`)
- `[[package]]` with `name` / `version` / `source` / `description`
- `file:` / `path:` (relative to index) or git URL
2. **CLI:** `bux search [q]`, `bux add <name>` resolves registry, `bux install` locks path/git
3. **Demo package:** `registry/packages/greet` (`Greet_Hello`, `Greet_Version`)
4. **Smoke:** `tools/smoke_registry.sh` / `make test-registry` — temp app outside tree
5. **Semver policy:** `docs/SEMVER.md` (0.x vs 1.0, registry version match)
6. Packages.md updated
---
## Следващи стъпки
1. C.1 Lifetime elision
2. Phase D tooling: `bux fmt` CI, `bux test --filter`, golden stdlib tests
3. LSP: position-sensitive locals; inferred `let` types
1. E.2 polish apps / E.5 benchmarks
2. HTTP-fetchable registry index URL (beyond local file)
3. LSP: workspace rename / references (optional)
+76
View File
@@ -0,0 +1,76 @@
# Bux Semantic Versioning Policy
> Status: Draft for v0.x → v1.0 freeze (E.3)
Bux follows [Semantic Versioning 2.0.0](https://semver.org/) with the
clarifications below.
---
## Version numbers
```
MAJOR.MINOR.PATCH[-prerelease]
```
| Component | When it increases |
|-----------|-------------------|
| **MAJOR** | Breaking language / stdlib / CLI changes |
| **MINOR** | Backward-compatible features |
| **PATCH** | Backward-compatible bug fixes |
During **0.x** (pre-1.0):
- `0.MINOR.PATCH` — MINOR may still introduce breaking changes (documented in
the release notes and `MIGRATION_*.sh` when needed).
- Prefer deprecation warnings for at least one MINOR before removal when
practical.
After **1.0.0** (language freeze):
- Breaking changes require a MAJOR bump and a migration guide.
- The Language Reference is the normative spec; compiler bugs that contradict
the ref are fixed without a MAJOR bump.
---
## What counts as “breaking”
- Removing or renaming a public stdlib symbol
- Changing the type or semantics of a public API
- Changing CLI flags that scripts rely on (`build`, `test`, `fmt --check`, …)
- Changing `bux.toml` / `bux.lock` fields in an incompatible way
- Changing the fat `func` / tuple C ABI in a way that breaks linked code
**Not breaking:**
- New keywords that were previously valid identifiers only if reserved carefully
(prefer contextual keywords)
- New diagnostics / stricter `@[Checked]` (document; may be gated)
- Formatter whitespace-only changes
---
## Package versions (registry)
Registry packages use the same MAJOR.MINOR.PATCH scheme.
`bux add foo` / `bux add foo 0.1` resolution:
| Request | Matches |
|---------|---------|
| `*` / omitted | Latest entry for `foo` in the index |
| `0.1.1` | Exact version |
| `0.1` | First version with that prefix (e.g. `0.1.1`) |
Lockfiles pin the **resolved** version and source path/URL.
---
## Release checklist (maintainers)
1. Update `docs/LanguageRef.md` if behaviour changed
2. Update `docs/QUALITY_PLAN.md` / changelog notes
3. Run `make test` (includes `fmt-check`, examples, goldens)
4. Run `make selfhost-loop`
5. Tag `vMAJOR.MINOR.PATCH`
+749
View File
@@ -0,0 +1,749 @@
# API Reference
Generated by `bux doc` from `///` and `/* */` documentation comments.
## `Array`
_Source: `lib/Array.bux`_
### `Array` _struct_
```bux
struct Array<T> {
```
Growable contiguous buffer of `T` (len + capacity).
### `Array_New` _func_
```bux
func Array_New<T>(cap: uint) -> Array<T> {
```
Create an empty array with the given initial capacity.
### `Array_Push` _func_
```bux
func Array_Push<T>(self: *Array<T>, value: T) {
```
Append `value`, growing capacity if needed.
### `Array_Get` _func_
```bux
func Array_Get<T>(self: *Array<T>, index: uint) -> T {
```
Element at `index` (bounds-checked unless `@[Release]`).
### `Array_Set` _func_
```bux
func Array_Set<T>(self: *Array<T>, index: uint, value: T) {
```
Write `value` at `index` (bounds-checked unless `@[Release]`).
### `Array_Len` _func_
```bux
func Array_Len<T>(self: *Array<T>) -> uint {
```
Number of live elements.
### `Array_Free` _func_
```bux
func Array_Free<T>(self: *Array<T>) {
```
Free the backing buffer and reset length/capacity to zero.
### `Array_Drop` _func_
```bux
func Array_Drop<T>(self: *Array<T>) {
```
Drop trait entry — same as `Array_Free`.
### `Array_IsEmpty` _func_
```bux
func Array_IsEmpty<T>(self: *Array<T>) -> bool {
```
True if the array has no elements.
### `Array_Cap` _func_
```bux
func Array_Cap<T>(self: *Array<T>) -> uint {
```
Current capacity (not length).
### `Array_Clear` _func_
```bux
func Array_Clear<T>(self: *Array<T>) {
```
Drop length to zero; keeps allocated capacity.
### `Array_Reserve` _func_
```bux
func Array_Reserve<T>(self: *Array<T>, minCap: uint) {
```
Ensure capacity is at least `minCap` (does not shrink).
### `Array_First` _func_
```bux
func Array_First<T>(self: *Array<T>) -> T {
```
First element (bounds-checked if empty).
### `Array_Last` _func_
```bux
func Array_Last<T>(self: *Array<T>) -> T {
```
Last element (bounds-checked if empty).
### `Array_Pop` _func_
```bux
func Array_Pop<T>(self: *Array<T>) -> T {
```
Remove and return the last element (bounds-checked if empty).
### `Array_Contains` _func_
```bux
func Array_Contains<T>(self: *Array<T>, value: T) -> bool {
```
Linear search: true if `value` is present (uses `==`).
### `Array_IndexOf` _func_
```bux
func Array_IndexOf<T>(self: *Array<T>, value: T) -> int {
```
Index of first equal element, or `-1` if not found.
### `Array_Extend` _func_
```bux
func Array_Extend<T>(self: *Array<T>, other: *Array<T>) {
```
Append all elements of `other` onto `self`.
## `Channel`
_Source: `lib/Channel.bux`_
### `Channel_SendInt` _func_
```bux
func Channel_SendInt(ch: *Channel<int>, value: int) {
```
Convenience wrappers for common types
## `Iter`
_Source: `lib/Iter.bux`_
### `Array_Iter` _func_
```bux
func Array_Iter<T>(arr: *Array<T>) -> Iter<T> {
```
Create an iterator from an Array
### `Iter_HasNext` _func_
```bux
func Iter_HasNext<T>(it: *Iter<T>) -> bool {
```
Check if there are more elements
### `Iter_Next` _func_
```bux
func Iter_Next<T>(it: *Iter<T>) -> T {
```
Get the next element and advance (undefined if HasNext is false)
### `Iter_Peek` _func_
```bux
func Iter_Peek<T>(it: *Iter<T>) -> T {
```
Peek current element without advancing (undefined if HasNext is false)
### `Iter_Reset` _func_
```bux
func Iter_Reset<T>(it: *Iter<T>) {
```
Reset iterator to the beginning
### `Iter_Pos` _func_
```bux
func Iter_Pos<T>(it: *Iter<T>) -> uint {
```
Current position
### `Iter_Len` _func_
```bux
func Iter_Len<T>(it: *Iter<T>) -> uint {
```
Remaining length
### `Iter_Count` _func_
```bux
func Iter_Count<T>(it: *Iter<T>) -> uint {
```
Count remaining elements
### `Iter_Skip` _func_
```bux
func Iter_Skip<T>(it: *Iter<T>, n: uint) {
```
Skip N elements
### `Iter_Take` _func_
```bux
func Iter_Take<T>(it: *Iter<T>, n: uint) -> Iter<T> {
```
Take first N elements (by limiting len)
### `Iter_AnyEq` _func_
```bux
func Iter_AnyEq<T>(it: *Iter<T>, value: T) -> bool {
```
True if any remaining element equals value
### `Iter_AllEq` _func_
```bux
func Iter_AllEq<T>(it: *Iter<T>, value: T) -> bool {
```
True if every remaining element equals value (true if empty)
### `Iter_Collect` _func_
```bux
func Iter_Collect<T>(it: *Iter<T>) -> Array<T> {
```
Collect remaining elements into a new Array
### `Iter_Map` _func_
```bux
func Iter_Map<T, U>(it: *Iter<T>, f: func(T) -> U) -> Array<U> {
```
Map each remaining element through f: T → U, collect into Array<U>
### `Iter_Filter` _func_
```bux
func Iter_Filter<T>(it: *Iter<T>, pred: func(T) -> bool) -> Array<T> {
```
Keep remaining elements for which pred returns true
### `Iter_Fold` _func_
```bux
func Iter_Fold<T, Acc>(it: *Iter<T>, init: Acc, f: func(Acc, T) -> Acc) -> Acc {
```
Left-fold: f(f(...f(init, x0), x1), ...)
### `Iter_ForEach` _func_
```bux
func Iter_ForEach<T>(it: *Iter<T>, f: func(T) -> int) {
```
Call f for each remaining element (return value of f is ignored)
### `Iter_Any` _func_
```bux
func Iter_Any<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool {
```
True if any remaining element satisfies pred
### `Iter_All` _func_
```bux
func Iter_All<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool {
```
True if all remaining elements satisfy pred (true if empty)
### `Iter_SumInt` _func_
```bux
func Iter_SumInt(it: *Iter<int>) -> int {
```
Sum remaining ints (specialized fold)
## `Json`
_Source: `lib/Json.bux`_
### `JsonValue` _struct_
```bux
struct JsonValue {
```
=== Core type ===
### `Json_Null` _func_
```bux
func Json_Null() -> JsonValue {
```
=== Constructors ===
### `Json_ArrayLen` _func_
```bux
func Json_ArrayLen(v: JsonValue) -> uint {
```
=== Array helpers ===
### `Json_ObjectLen` _func_
```bux
func Json_ObjectLen(v: JsonValue) -> uint {
```
=== Object helpers ===
### `Json_IsNull` _func_
```bux
func Json_IsNull(v: JsonValue) -> bool {
```
=== Accessors ===
### `JsonParser` _struct_
```bux
struct JsonParser {
```
=== Parser ===
### `Json_Parse` _func_
```bux
func Json_Parse(s: String) -> JsonValue {
```
=== Public parser ===
### `Json_StringifyImpl` _func_
```bux
func Json_StringifyImpl(sb: *StringBuilder, v: JsonValue) {
```
=== Serializer ===
## `Map`
_Source: `lib/Map.bux`_
### `Map_Remove` _func_
```bux
func Map_Remove<K, V>(m: *Map<K, V>, key: K) -> bool {
```
Remove key if present. Rebuilds the table to keep open-addressing correct.
## `Net`
_Source: `lib/Net.bux`_
### `Net_Create` _func_
```bux
func Net_Create() -> int {
```
Create a TCP socket. Returns -1 on error.
### `Net_SetReuse` _func_
```bux
func Net_SetReuse(fd: int) -> bool {
```
Enable SO_REUSEADDR on a socket.
### `Net_Bind` _func_
```bux
func Net_Bind(fd: int, addr: String, port: int) -> bool {
```
Bind a socket to an address and port.
### `Net_Listen` _func_
```bux
func Net_Listen(fd: int, backlog: int) -> bool {
```
Start listening for connections.
### `Net_Accept` _func_
```bux
func Net_Accept(fd: int) -> int {
```
Accept a connection. Returns new fd or -1 on error.
### `Net_Connect` _func_
```bux
func Net_Connect(fd: int, addr: String, port: int) -> bool {
```
Connect to a remote address and port.
### `Net_Send` _func_
```bux
func Net_Send(fd: int, data: String) -> int {
```
Send data. Returns bytes sent or -1 on error.
### `Net_Recv` _func_
```bux
func Net_Recv(fd: int, maxLen: int) -> String {
```
Receive up to maxLen bytes. Returns empty string on error/EOF.
### `Net_Close` _func_
```bux
func Net_Close(fd: int) -> bool {
```
Close a socket.
### `Net_LastError` _func_
```bux
func Net_LastError() -> String {
```
Get last socket error as a string.
## `Option`
_Source: `lib/Option.bux`_
### `Option_Expect` _func_
```bux
func Option_Expect(o: Option, msg: String) -> int {
```
Unwrap Some or panic with a custom message
### `Option_Or` _func_
```bux
func Option_Or(o: Option, other: Option) -> Option {
```
If o is Some return it, otherwise return other
## `Os`
_Source: `lib/Os.bux`_
### `Os_Exit` _func_
```bux
func Os_Exit(code: int) {
```
Terminate the process with the given exit code
## `Result`
_Source: `lib/Result.bux`_
### `Result_Expect` _func_
```bux
func Result_Expect(r: Result, msg: String) -> int {
```
Unwrap Ok or panic with a custom message
### `Result_UnwrapErr` _func_
```bux
func Result_UnwrapErr(r: Result) -> String {
```
Extract Err payload (panics if Ok)
### `Result_Or` _func_
```bux
func Result_Or(r: Result, other: Result) -> Result {
```
If r is Ok return it, otherwise return other
## `Set`
_Source: `lib/Set.bux`_
### `Set_Remove` _func_
```bux
func Set_Remove<T>(s: *Set<T>, value: T) -> bool {
```
Remove value if present. Rebuilds the table to keep open-addressing correct.
## `String`
_Source: `lib/String.bux`_
### `String_Len` _func_
```bux
func String_Len(s: String) -> uint {
```
Byte length of a C string (`strlen`).
### `String_IsEmpty` _func_
```bux
func String_IsEmpty(s: String) -> bool {
```
True if the string has zero length.
### `String_IsNull` _func_
```bux
func String_IsNull(s: String) -> bool {
```
True if the pointer is null.
### `String_Eq` _func_
```bux
func String_Eq(a: String, b: String) -> bool {
```
Lexicographic equality.
### `String_Concat` _func_
```bux
func String_Concat(a: String, b: String) -> String {
```
Allocate and return `a` concatenated with `b`.
### `String_Copy` _func_
```bux
func String_Copy(s: String) -> String {
```
Heap-copy of `s`.
### `String_StartsWith` _func_
```bux
func String_StartsWith(s: String, prefix: String) -> bool {
```
True if `s` begins with `prefix`.
### `String_EndsWith` _func_
```bux
func String_EndsWith(s: String, suffix: String) -> bool {
```
True if `s` ends with `suffix`.
### `String_Contains` _func_
```bux
func String_Contains(s: String, substr: String) -> bool {
```
True if `substr` occurs anywhere in `s`.
### `String_IsBlank` _func_
```bux
func String_IsBlank(s: String) -> bool {
```
True if empty or only whitespace (space, tab, CR, LF).
### `String_Repeat` _func_
```bux
func String_Repeat(s: String, count: uint) -> String {
```
Repeat `s`, `count` times (`count == 0` → empty string).
### `String_ReplaceAll` _func_
```bux
func String_ReplaceAll(s: String, old: String, new: String) -> String {
```
Replace every non-overlapping occurrence of `old` with `new`.
Empty `old` is a no-op (returns `s` unchanged). Safe if `new` contains `old`.
## `Test`
_Source: `lib/Test.bux`_
### `Test_Exit` _func_
```bux
func Test_Exit(code: int) {
```
Exit the process with `code` (for test runners).
### `Test_Assert` _func_
```bux
func Test_Assert(cond: bool) {
```
Assert `cond` is true; abort on failure.
### `Test_AssertEqInt` _func_
```bux
func Test_AssertEqInt(a: int, b: int) {
```
Assert two ints are equal; print both values and exit 1 on mismatch.
### `Test_AssertNeqInt` _func_
```bux
func Test_AssertNeqInt(a: int, b: int) {
```
Assert two ints differ.
### `Test_AssertEqString` _func_
```bux
func Test_AssertEqString(a: String, b: String) {
```
Assert two strings are equal (`String_Eq`).
### `Test_AssertEqBool` _func_
```bux
func Test_AssertEqBool(a: bool, b: bool) {
```
Assert two bools are equal.
### `Test_AssertTrue` _func_
```bux
func Test_AssertTrue(cond: bool) {
```
Assert `cond` is true.
### `Test_AssertFalse` _func_
```bux
func Test_AssertFalse(cond: bool) {
```
Assert `cond` is false.
### `Test_Fail` _func_
```bux
func Test_Fail(msg: String) {
```
Fail the test with a message and exit 1.
### `Test_Pass` _func_
```bux
func Test_Pass(msg: String) {
```
Print a PASS line (for human-readable runners / goldens).
+4 -4
View File
@@ -10,20 +10,20 @@ enum Result {
func Main() -> int {
let r1: Result = Result { tag: Result_Ok };
r1.data.Ok_0 = 42;
let r2: Result = Result { tag: Result_Err };
r2.data.Err_0 = "error message";
if r1.tag == Result_Ok {
PrintLine("r1 is Ok:");
PrintInt(r1.data.Ok_0);
PrintLine("");
}
if r2.tag == Result_Err {
PrintLine("r2 is Err:");
PrintLine(r2.data.Err_0);
}
return 0;
}
+2 -2
View File
@@ -1,8 +1,8 @@
// borrow.bux — Test explicit borrow expressions
struct Point {
x: int;
y: int;
x: int;
y: int;
}
// NOT @[Checked] — borrow keyword available everywhere
+2 -2
View File
@@ -23,12 +23,12 @@ func ColorName(c: Color) -> String {
func Main() -> int {
let myColor: Color = Color::Green;
PrintLine("My color is:");
PrintLine(ColorName(myColor));
PrintLine("Color value:");
PrintInt(myColor as int);
PrintLine("");
return 0;
}
+1 -1
View File
@@ -27,4 +27,4 @@ func Main() -> int {
PrintLine("");
return 0;
}
}
+2 -2
View File
@@ -11,13 +11,13 @@ func Factorial(n: int) -> int {
func Main() -> int {
PrintLine("Factorials:");
var i: int = 1;
while i <= 10 {
let fact: int = Factorial(i);
PrintLine(Fmt_Fmt2("{0}! = {1}", String_FromInt(i), String_FromInt(fact)));
i = i + 1;
}
return 0;
}
+2 -2
View File
@@ -11,13 +11,13 @@ func Fibonacci(n: int) -> int {
func Main() -> int {
PrintLine("Fibonacci sequence:");
var i: int = 0;
while i < 10 {
let fib: int = Fibonacci(i);
PrintLine(Fmt_Fmt1("{0}", String_FromInt(fib)));
i = i + 1;
}
return 0;
}
+4 -4
View File
@@ -13,14 +13,14 @@ func Main() -> int {
// Inferred: T = int
let m1: int = Max(10, 20);
let m2: int = Max(5, 3);
PrintLine("Max(10, 20) = ");
PrintInt(m1);
PrintLine("");
PrintLine("Max(5, 3) = ");
PrintInt(m2);
PrintLine("");
return 0;
}
}
+1 -1
View File
@@ -20,4 +20,4 @@ func Main() -> int {
PrintInt(p.GetFirst());
PrintLine("");
return 0;
}
}
+3 -3
View File
@@ -13,14 +13,14 @@ func Max<T>(a: T, b: T) -> T {
func Main() -> int {
let m1: int = Max<int>(10, 20);
let m2: int = Max<int>(5, 3);
PrintLine("Max(10, 20) = ");
PrintInt(m1);
PrintLine("");
PrintLine("Max(5, 3) = ");
PrintInt(m2);
PrintLine("");
return 0;
}
+4 -4
View File
@@ -31,21 +31,21 @@ func Main() -> int {
PrintLine("Box value:");
PrintInt(b.Get());
PrintLine("");
b.Set(100);
PrintLine("Box after Set(100):");
PrintInt(b.Get());
PrintLine("");
let p: Pair<int, String> = Pair<int, String> { first: 10, second: "hello" };
PrintLine("Pair first:");
PrintInt(p.GetFirst());
PrintLine("");
let bp: *Box<int> = &b;
PrintLine("Box via pointer:");
PrintInt(bp.Get());
PrintLine("");
return 0;
}
+5 -5
View File
@@ -5,20 +5,20 @@ import Std::Fmt::{Fmt_Fmt1, Fmt_FmtInt};
func Main() -> int {
PrintLine("=== Json Demo ===");
let s: String = "{\"name\":\"Bux\",\"count\":42,\"items\":[\"a\",\"b\",\"c\"]}";
let j: JsonValue = Json_Parse(s);
PrintLine("Parsed and stringified:");
PrintLine(Json_Stringify(j));
PrintLine("\nAccess fields:");
PrintLine(Fmt_Fmt1("name = {0}", Json_AsString(Json_ObjectGet(j, "name"))));
PrintLine(Fmt_FmtInt("count = {0}", Json_AsNumber(Json_ObjectGet(j, "count")) as int64));
let items: JsonValue = Json_ObjectGet(j, "items");
PrintLine(Fmt_Fmt1("items[0] = {0}", Json_AsString(Json_ArrayGet(items, 0))));
PrintLine(Fmt_Fmt1("items[1] = {0}", Json_AsString(Json_ArrayGet(items, 1))));
PrintLine("\nBuild JSON programmatically:");
var obj: JsonValue = Json_Object();
Json_ObjectSet(&obj, "lang", Json_String("Bux"));
@@ -28,6 +28,6 @@ func Main() -> int {
Json_ArrayPush(&arr, Json_String("safe"));
Json_ObjectSet(&obj, "tags", arr);
PrintLine(Json_Stringify(obj));
return 0;
}
+7 -7
View File
@@ -29,30 +29,30 @@ func Jwt_Create(payload: JsonValue, secret: String) -> String {
let header: JsonValue = Json_Object();
Json_ObjectSet(&header, "alg", Json_String("HS256"));
Json_ObjectSet(&header, "typ", Json_String("JWT"));
let headerB64: String = Base64UrlEncode(Json_Stringify(header));
let payloadB64: String = Base64UrlEncode(Json_Stringify(payload));
let signingInput: String = String_Concat(headerB64, ".");
signingInput = String_Concat(signingInput, payloadB64);
let sigRaw: String = Crypto_HmacSha256Raw(secret, signingInput);
let sigB64: String = Base64UrlEncodeSig(sigRaw);
return String_Concat(String_Concat(signingInput, "."), sigB64);
}
func Main() -> int {
PrintLine("=== JWT Demo ===");
let payload: JsonValue = Json_Object();
Json_ObjectSet(&payload, "sub", Json_String("user123"));
Json_ObjectSet(&payload, "name", Json_String("Bux Developer"));
Json_ObjectSet(&payload, "admin", Json_Bool(true));
let token: String = Jwt_Create(payload, "my-secret-key");
PrintLine("Token:");
PrintLine(token);
return 0;
}
+51
View File
@@ -0,0 +1,51 @@
// lifetime_elision.bux — C.1: elided lifetimes for common &[Checked] APIs
// No 'a annotations needed when there is a single input reference.
import Std::Io::{PrintLine, PrintInt};
import Std::Test::{Test_AssertEqInt, Test_Pass};
// Elided: param and return share one lifetime automatically
@[Checked]
func Identity(p: &int) -> &int {
return p;
}
// Explicit lifetime for documentation / multi-ref (same lifetime both sides)
@[Checked]
func IdentityNamed<'a>(p: &'a int) -> &'a int {
return p;
}
// Via intermediate let binding — lifetime is propagated
@[Checked]
func ViaLet(p: &int) -> &int {
let r: &int = p;
return r;
}
// self-style first param: elision prefers the first input for the return
@[Checked]
func FirstOf(self: &int, _other: int) -> &int {
return self;
}
@[Checked]
func Main() -> int {
var x: int = 10;
var y: int = 20;
let a: &int = Identity(&x);
Test_AssertEqInt(*a, 10);
let b: &int = IdentityNamed(&y);
Test_AssertEqInt(*b, 20);
let c: &int = ViaLet(&x);
Test_AssertEqInt(*c, 10);
let d: &int = FirstOf(&y, 0);
Test_AssertEqInt(*d, 20);
Test_Pass("lifetime_elision");
PrintLine("lifetime_elision: ok");
return 0;
}
+7 -7
View File
@@ -5,26 +5,26 @@ import Std::Map::{Map, Map_New, Map_Set, Map_Get, Map_Has, Map_Free};
func Main() -> int {
PrintLine("=== Map<int, String> ===");
let m: Map<int, String> = Map_New<int, String>(16);
Map_Set<int, String>(&m, 1, "one");
Map_Set<int, String>(&m, 2, "two");
Map_Set<int, String>(&m, 3, "three");
PrintLine("Get 1:");
PrintLine(Map_Get<int, String>(&m, 1));
PrintLine("Get 2:");
PrintLine(Map_Get<int, String>(&m, 2));
if Map_Has<int, String>(&m, 1) {
PrintLine("Has 1: yes");
}
// Test update
Map_Set<int, String>(&m, 2, "updated");
PrintLine("Updated 2:");
PrintLine(Map_Get<int, String>(&m, 2));
Map_Free<int, String>(&m);
return 0;
}
}
+3 -3
View File
@@ -11,7 +11,7 @@ extend Rectangle {
func Area(self: Rectangle) -> int {
return self.width * self.height;
}
func Perimeter(self: Rectangle) -> int {
return 2 * (self.width + self.height);
}
@@ -19,7 +19,7 @@ extend Rectangle {
func Main() -> int {
let rect: Rectangle = Rectangle { width: 10, height: 5 };
PrintLine("Rectangle:");
PrintLine("Width = ");
PrintInt(rect.width);
@@ -33,6 +33,6 @@ func Main() -> int {
PrintLine("Perimeter = ");
PrintInt(rect.Perimeter());
PrintLine("");
return 0;
}
+3 -3
View File
@@ -9,16 +9,16 @@ func Main() -> int {
PrintLine(Fmt_Fmt1("Current directory: {0}", Os_GetCwd()));
PrintLine(Fmt_FmtInt("Command-line args: {0}", Os_ArgsCount() as int64));
PrintLine(Fmt_Fmt1("HOME env: {0}", Os_GetEnv("HOME")));
PrintLine("\n=== Time Demo ===");
let start: int64 = Time_NowMs();
PrintLine(Fmt_FmtInt("Start: {0} ms", start));
PrintLine("Sleeping 150ms...");
Time_SleepMs(150);
let end: int64 = Time_NowMs();
PrintLine(Fmt_FmtInt("End: {0} ms", end));
PrintLine(Fmt_FmtInt("Elapsed: {0} ms", end - start));
return 0;
}
+4 -4
View File
@@ -23,17 +23,17 @@ func UncheckedSwap(a: *int, b: *int) {
func Main() -> int {
var x: int = 10;
// &mut allows mutation
ScaleInPlace(&x, 3);
PrintInt(x); // 30
PrintLine("");
// & allows reading
let y: int = GetValue(&x);
PrintInt(y); // 30
PrintLine("");
// Unchecked: raw pointers work like C
var a: int = 5;
var b: int = 7;
@@ -42,6 +42,6 @@ func Main() -> int {
PrintLine("");
PrintInt(b); // 5
PrintLine("");
return 0;
}
+3 -3
View File
@@ -5,12 +5,12 @@ import Std::Fmt::{Fmt_Fmt1, Fmt_FmtInt};
func Main() -> int {
PrintLine("Process demo:");
let output: String = Process_Output("echo Hello from subprocess");
PrintLine(Fmt_Fmt1("Captured: {0}", output));
let rc: int = Process_Run("true");
PrintLine(Fmt_FmtInt("Exit code: {0}", rc));
return 0;
}
+6 -6
View File
@@ -82,14 +82,14 @@ func SafeDivide(a: int, b: int) -> Option {
func Main() -> int {
PrintLine("Result demo:");
let r1: Result = Divide(10, 2);
if Result_IsOk(r1) {
PrintLine("10 / 2 = ");
PrintInt(Result_Unwrap(r1));
PrintLine("");
}
let r2: Result = Divide(10, 0);
if Result_IsErr(r2) {
PrintLine("10 / 0 failed");
@@ -97,20 +97,20 @@ func Main() -> int {
PrintLine("unwrap_or = ");
PrintInt(Result_UnwrapOr(r2, -1));
PrintLine("");
PrintLine("Option demo:");
let o1: Option = SafeDivide(20, 4);
if Option_IsSome(o1) {
PrintLine("20 / 4 = ");
PrintInt(Option_UnwrapOr(o1, 0));
PrintLine("");
}
let o2: Option = SafeDivide(20, 0);
PrintLine("20 / 0 fallback = ");
PrintInt(Option_UnwrapOr(o2, -1));
PrintLine("");
return 0;
}
+7 -7
View File
@@ -5,30 +5,30 @@ import Std::String::{String_Len, String_Eq, String_Concat};
func Main() -> int {
let hello: String = "Hello";
let world: String = "World";
PrintLine("String operations:");
// Length
PrintLine("Length of 'Hello':");
PrintInt(String_Len(hello));
PrintLine("");
// Concatenation
let greeting: String = String_Concat(hello, ", ");
let greeting2: String = String_Concat(greeting, world);
let full: String = String_Concat(greeting2, "!");
PrintLine("Concatenated:");
PrintLine(full);
// Equality
if String_Eq(hello, "Hello") {
PrintLine("'Hello' equals 'Hello'");
}
if !String_Eq(hello, world) {
PrintLine("'Hello' does not equal 'World'");
}
return 0;
}
+2 -2
View File
@@ -65,10 +65,10 @@ func Main() -> int {
StringBuilder_Append(&sb, "World");
StringBuilder_Append(&sb, "! #");
StringBuilder_AppendInt(&sb, 42);
Print("StringBuilder: ");
PrintLine(StringBuilder_Build(&sb));
StringBuilder_Free(&sb);
return 0;
}
}
+2 -2
View File
@@ -16,7 +16,7 @@ func Main() -> int {
let p1: Point = Point { x: 10, y: 20 };
let p2: Point = Point { x: 5, y: 15 };
let sum: Point = AddPoints(p1, p2);
PrintLine("Point sum:");
PrintLine("x = ");
PrintInt(sum.x);
@@ -24,6 +24,6 @@ func Main() -> int {
PrintLine("y = ");
PrintInt(sum.y);
PrintLine("");
return 0;
}
+3 -3
View File
@@ -39,7 +39,7 @@ func ComputeWithError() -> Result {
func Main() -> int {
PrintLine("Try operator demo:");
let r1: Result = Compute();
if r1.tag == Result_Ok {
PrintLine("Compute() = ");
@@ -48,11 +48,11 @@ func Main() -> int {
} else {
PrintLine("Compute() failed");
}
let r2: Result = ComputeWithError();
if r2.tag == Result_Err {
PrintLine("ComputeWithError() failed as expected");
}
return 0;
}
+122 -114
View File
@@ -1,135 +1,143 @@
module Std::Array {
extern func bux_alloc(size: uint) -> *void;
extern func bux_realloc(ptr: *void, size: uint) -> *void;
extern func bux_free(ptr: *void);
extern func bux_bounds_check(index: uint, len: uint);
extern func bux_alloc(size: uint) -> *void;
extern func bux_realloc(ptr: *void, size: uint) -> *void;
extern func bux_free(ptr: *void);
extern func bux_bounds_check(index: uint, len: uint);
struct Array<T> {
data: *T,
len: uint,
cap: uint,
}
/// Growable contiguous buffer of `T` (len + capacity).
struct Array<T> {
data: *T,
len: uint,
cap: uint,
}
func Array_New<T>(cap: uint) -> Array<T> {
let data = bux_alloc(cap * sizeof(T)) as *T;
return Array<T> { data: data, len: 0, cap: cap };
}
/// Create an empty array with the given initial capacity.
func Array_New<T>(cap: uint) -> Array<T> {
let data = bux_alloc(cap * sizeof(T)) as *T;
return Array<T> { data: data, len: 0, cap: cap };
}
func Array_Push<T>(self: *Array<T>, value: T) {
if self.len >= self.cap {
self.cap = self.cap * 2;
/// Append `value`, growing capacity if needed.
func Array_Push<T>(self: *Array<T>, value: T) {
if self.len >= self.cap {
self.cap = self.cap * 2;
self.data = bux_realloc(self.data as *void, self.cap * sizeof(T)) as *T;
}
self.data[self.len] = value;
self.len = self.len + 1;
}
/// Element at `index` (bounds-checked unless `@[Release]`).
func Array_Get<T>(self: *Array<T>, index: uint) -> T {
bux_bounds_check(index, self.len);
return self.data[index];
}
/// Write `value` at `index` (bounds-checked unless `@[Release]`).
func Array_Set<T>(self: *Array<T>, index: uint, value: T) {
bux_bounds_check(index, self.len);
self.data[index] = value;
}
/// Number of live elements.
func Array_Len<T>(self: *Array<T>) -> uint {
return self.len;
}
/// Free the backing buffer and reset length/capacity to zero.
func Array_Free<T>(self: *Array<T>) {
bux_free(self.data as *void);
self.data = null as *T;
self.len = 0;
self.cap = 0;
}
/// Drop trait entry — same as `Array_Free`.
func Array_Drop<T>(self: *Array<T>) {
Array_Free<T>(self);
}
func Array_operator_index_get<T>(self: *Array<T>, idx: uint) -> T {
return Array_Get<T>(self, idx);
}
func Array_operator_index_set<T>(self: *Array<T>, idx: uint, value: T) {
Array_Set<T>(self, idx, value);
}
/// True if the array has no elements.
func Array_IsEmpty<T>(self: *Array<T>) -> bool {
return self.len == 0;
}
/// Current capacity (not length).
func Array_Cap<T>(self: *Array<T>) -> uint {
return self.cap;
}
/// Drop length to zero; keeps allocated capacity.
func Array_Clear<T>(self: *Array<T>) {
self.len = 0;
}
/// Ensure capacity is at least `minCap` (does not shrink).
func Array_Reserve<T>(self: *Array<T>, minCap: uint) {
if minCap <= self.cap {
return;
}
self.cap = minCap;
self.data = bux_realloc(self.data as *void, self.cap * sizeof(T)) as *T;
}
self.data[self.len] = value;
self.len = self.len + 1;
}
func Array_Get<T>(self: *Array<T>, index: uint) -> T {
bux_bounds_check(index, self.len);
return self.data[index];
}
func Array_Set<T>(self: *Array<T>, index: uint, value: T) {
bux_bounds_check(index, self.len);
self.data[index] = value;
}
func Array_Len<T>(self: *Array<T>) -> uint {
return self.len;
}
func Array_Free<T>(self: *Array<T>) {
bux_free(self.data as *void);
self.data = null as *T;
self.len = 0;
self.cap = 0;
}
func Array_Drop<T>(self: *Array<T>) {
Array_Free<T>(self);
}
func Array_operator_index_get<T>(self: *Array<T>, idx: uint) -> T {
return Array_Get<T>(self, idx);
}
func Array_operator_index_set<T>(self: *Array<T>, idx: uint, value: T) {
Array_Set<T>(self, idx, value);
}
/* True if the array has no elements */
func Array_IsEmpty<T>(self: *Array<T>) -> bool {
return self.len == 0;
}
/* Current capacity (not length) */
func Array_Cap<T>(self: *Array<T>) -> uint {
return self.cap;
}
/* Drop length to zero; keeps allocated capacity */
func Array_Clear<T>(self: *Array<T>) {
self.len = 0;
}
/* Ensure capacity is at least minCap (does not shrink) */
func Array_Reserve<T>(self: *Array<T>, minCap: uint) {
if minCap <= self.cap {
return;
/// First element (bounds-checked if empty).
func Array_First<T>(self: *Array<T>) -> T {
return Array_Get<T>(self, 0);
}
self.cap = minCap;
self.data = bux_realloc(self.data as *void, self.cap * sizeof(T)) as *T;
}
/* First element (panics if empty via bounds check) */
func Array_First<T>(self: *Array<T>) -> T {
return Array_Get<T>(self, 0);
}
/// Last element (bounds-checked if empty).
func Array_Last<T>(self: *Array<T>) -> T {
return Array_Get<T>(self, self.len - 1);
}
/* Last element (panics if empty via bounds check) */
func Array_Last<T>(self: *Array<T>) -> T {
return Array_Get<T>(self, self.len - 1);
}
/// Remove and return the last element (bounds-checked if empty).
func Array_Pop<T>(self: *Array<T>) -> T {
bux_bounds_check(0, self.len);
self.len = self.len - 1;
return self.data[self.len];
}
/* Remove and return the last element (panics if empty) */
func Array_Pop<T>(self: *Array<T>) -> T {
bux_bounds_check(0, self.len);
self.len = self.len - 1;
return self.data[self.len];
}
/* Linear search: true if value is present (uses ==) */
func Array_Contains<T>(self: *Array<T>, value: T) -> bool {
var i: uint = 0;
while i < self.len {
if self.data[i] == value {
return true;
/// Linear search: true if `value` is present (uses `==`).
func Array_Contains<T>(self: *Array<T>, value: T) -> bool {
var i: uint = 0;
while i < self.len {
if self.data[i] == value {
return true;
}
i = i + 1;
}
i = i + 1;
return false;
}
return false;
}
/* Index of first equal element, or -1 if not found */
func Array_IndexOf<T>(self: *Array<T>, value: T) -> int {
var i: uint = 0;
while i < self.len {
if self.data[i] == value {
return i as int;
/// Index of first equal element, or `-1` if not found.
func Array_IndexOf<T>(self: *Array<T>, value: T) -> int {
var i: uint = 0;
while i < self.len {
if self.data[i] == value {
return i as int;
}
i = i + 1;
}
i = i + 1;
return -1;
}
return -1;
}
/* Append all elements of other onto self */
func Array_Extend<T>(self: *Array<T>, other: *Array<T>) {
var i: uint = 0;
while i < other.len {
Array_Push<T>(self, other.data[i]);
i = i + 1;
/// Append all elements of `other` onto `self`.
func Array_Extend<T>(self: *Array<T>, other: *Array<T>) {
var i: uint = 0;
while i < other.len {
Array_Push<T>(self, other.data[i]);
i = i + 1;
}
}
}
}
+48 -48
View File
@@ -1,64 +1,64 @@
module Std::Channel {
extern func bux_channel_new(capacity: int64, elem_size: int64) -> *void;
extern func bux_channel_send(handle: *void, elem: *void);
extern func bux_channel_recv(handle: *void, out: *void) -> int;
extern func bux_channel_close(handle: *void);
extern func bux_channel_free(handle: *void);
extern func bux_channel_new(capacity: int64, elem_size: int64) -> *void;
extern func bux_channel_send(handle: *void, elem: *void);
extern func bux_channel_recv(handle: *void, out: *void) -> int;
extern func bux_channel_close(handle: *void);
extern func bux_channel_free(handle: *void);
struct Channel<T> {
handle: *void;
}
struct Channel<T> {
handle: *void;
}
func Channel_New<T>(capacity: int64) -> Channel<T> {
return Channel<T> { handle: bux_channel_new(capacity, sizeof(T)) };
}
func Channel_New<T>(capacity: int64) -> Channel<T> {
return Channel<T> { handle: bux_channel_new(capacity, sizeof(T)) };
}
func Channel_Send<T>(ch: *Channel<T>, value: T) {
bux_channel_send(ch.handle, (&value) as *void);
}
func Channel_Send<T>(ch: *Channel<T>, value: T) {
bux_channel_send(ch.handle, (&value) as *void);
}
func Channel_Recv<T>(ch: *Channel<T>) -> T {
var result: T;
bux_channel_recv(ch.handle, (&result) as *void);
return result;
}
func Channel_Recv<T>(ch: *Channel<T>) -> T {
var result: T;
bux_channel_recv(ch.handle, (&result) as *void);
return result;
}
func Channel_Recv_Ok<T>(ch: *Channel<T>, out: *T) -> bool {
return bux_channel_recv(ch.handle, out as *void) != 0;
}
func Channel_Recv_Ok<T>(ch: *Channel<T>, out: *T) -> bool {
return bux_channel_recv(ch.handle, out as *void) != 0;
}
func Channel_Close<T>(ch: *Channel<T>) {
bux_channel_close(ch.handle);
}
func Channel_Close<T>(ch: *Channel<T>) {
bux_channel_close(ch.handle);
}
func Channel_Free<T>(ch: *Channel<T>) {
bux_channel_free(ch.handle);
}
func Channel_Free<T>(ch: *Channel<T>) {
bux_channel_free(ch.handle);
}
func Channel_Drop<T>(ch: *Channel<T>) {
Channel_Free<T>(ch);
}
func Channel_Drop<T>(ch: *Channel<T>) {
Channel_Free<T>(ch);
}
/* Convenience wrappers for common types */
func Channel_SendInt(ch: *Channel<int>, value: int) {
bux_channel_send(ch.handle, (&value) as *void);
}
/* Convenience wrappers for common types */
func Channel_SendInt(ch: *Channel<int>, value: int) {
bux_channel_send(ch.handle, (&value) as *void);
}
func Channel_RecvInt(ch: *Channel<int>) -> int {
var result: int = 0;
bux_channel_recv(ch.handle, (&result) as *void);
return result;
}
func Channel_RecvInt(ch: *Channel<int>) -> int {
var result: int = 0;
bux_channel_recv(ch.handle, (&result) as *void);
return result;
}
func Channel_SendFloat64(ch: *Channel<float64>, value: float64) {
bux_channel_send(ch.handle, (&value) as *void);
}
func Channel_SendFloat64(ch: *Channel<float64>, value: float64) {
bux_channel_send(ch.handle, (&value) as *void);
}
func Channel_RecvFloat64(ch: *Channel<float64>) -> float64 {
var result: float64 = 0.0;
bux_channel_recv(ch.handle, (&result) as *void);
return result;
}
func Channel_RecvFloat64(ch: *Channel<float64>) -> float64 {
var result: float64 = 0.0;
bux_channel_recv(ch.handle, (&result) as *void);
return result;
}
}
+61 -61
View File
@@ -10,72 +10,72 @@
// =============================================================================
module Std::Crypto {
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len};
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len};
// Re-use the same externs from submodules (merged by compiler)
extern func bux_sha256(data: String, len: int, out: *void);
extern func bux_hmac_sha256(key: String, keylen: int, msg: String, msglen: int, out: *void);
extern func bux_random_bytes(buf: *void, len: int) -> int;
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_base64_decode(data: String, len: int, outlen: *int) -> String;
extern func bux_bytes_to_hex(data: *void, len: int) -> String;
// Re-use the same externs from submodules (merged by compiler)
extern func bux_sha256(data: String, len: int, out: *void);
extern func bux_hmac_sha256(key: String, keylen: int, msg: String, msglen: int, out: *void);
extern func bux_random_bytes(buf: *void, len: int) -> int;
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_base64_decode(data: String, len: int, outlen: *int) -> String;
extern func bux_bytes_to_hex(data: *void, len: int) -> String;
// --- Legacy function names (delegate to new submodule functions) ---
// --- Legacy function names (delegate to new submodule functions) ---
// SHA-256 → hex
func Crypto_Sha256(data: String) -> String {
let len: int = String_Len(data) as int;
let hashBuf: *void = Alloc(32);
bux_sha256(data, len, hashBuf);
let result: String = bux_bytes_to_hex(hashBuf as *void, 32);
Free(hashBuf);
return result;
}
// HMAC-SHA256 → hex
func Crypto_HmacSha256(key: String, message: String) -> String {
let keylen: int = String_Len(key) as int;
let msglen: int = String_Len(message) as int;
let hmacBuf: *void = Alloc(32);
bux_hmac_sha256(key, keylen, message, msglen, hmacBuf);
let result: String = bux_bytes_to_hex(hmacBuf as *void, 32);
Free(hmacBuf);
return result;
}
// Random bytes → base64
func Crypto_RandomBytes(n: int) -> String {
if n <= 0 { return ""; }
let buf: *void = Alloc(n as uint);
if bux_random_bytes(buf, n) != 1 {
Free(buf);
return "";
// SHA-256 → hex
func Crypto_Sha256(data: String) -> String {
let len: int = String_Len(data) as int;
let hashBuf: *void = Alloc(32);
bux_sha256(data, len, hashBuf);
let result: String = bux_bytes_to_hex(hashBuf as *void, 32);
Free(hashBuf);
return result;
}
let result: String = bux_base64_encode(buf as String, n);
Free(buf);
return result;
}
// Base64 encode
func Crypto_Base64Encode(s: String) -> String {
return bux_base64_encode(s, String_Len(s) as int);
}
// HMAC-SHA256 → hex
func Crypto_HmacSha256(key: String, message: String) -> String {
let keylen: int = String_Len(key) as int;
let msglen: int = String_Len(message) as int;
let hmacBuf: *void = Alloc(32);
bux_hmac_sha256(key, keylen, message, msglen, hmacBuf);
let result: String = bux_bytes_to_hex(hmacBuf as *void, 32);
Free(hmacBuf);
return result;
}
// HMAC-SHA256 raw → base64
func Crypto_HmacSha256Raw(key: String, message: String) -> String {
let keylen: int = String_Len(key) as int;
let msglen: int = String_Len(message) as int;
let hmacBuf: *void = Alloc(32);
bux_hmac_sha256(key, keylen, message, msglen, hmacBuf);
let result: String = bux_base64_encode(hmacBuf as String, 32);
Free(hmacBuf);
return result;
}
// Random bytes → base64
func Crypto_RandomBytes(n: int) -> String {
if n <= 0 { return ""; }
let buf: *void = Alloc(n as uint);
if bux_random_bytes(buf, n) != 1 {
Free(buf);
return "";
}
let result: String = bux_base64_encode(buf as String, n);
Free(buf);
return result;
}
// Base64 decode
func Crypto_Base64Decode(s: String) -> String {
let outlen: int = 0;
return bux_base64_decode(s, String_Len(s) as int, &outlen);
}
// Base64 encode
func Crypto_Base64Encode(s: String) -> String {
return bux_base64_encode(s, String_Len(s) as int);
}
// HMAC-SHA256 raw → base64
func Crypto_HmacSha256Raw(key: String, message: String) -> String {
let keylen: int = String_Len(key) as int;
let msglen: int = String_Len(message) as int;
let hmacBuf: *void = Alloc(32);
bux_hmac_sha256(key, keylen, message, msglen, hmacBuf);
let result: String = bux_base64_encode(hmacBuf as String, 32);
Free(hmacBuf);
return result;
}
// Base64 decode
func Crypto_Base64Decode(s: String) -> String {
let outlen: int = 0;
return bux_base64_decode(s, String_Len(s) as int, &outlen);
}
}
+74 -74
View File
@@ -8,93 +8,93 @@
module Std::Fmt {
import Std::String::{
String_Eq,
String_FromInt,
String_FromFloat,
String_FromBool,
StringBuilder,
StringBuilder_New,
StringBuilder_Append,
StringBuilder_Build,
String_Chars
};
import Std::String::{
String_Eq,
String_FromInt,
String_FromFloat,
String_FromBool,
StringBuilder,
StringBuilder_New,
StringBuilder_Append,
StringBuilder_Build,
String_Chars
};
extern func bux_strlen(s: String) -> uint;
extern func bux_str_to_int(s: String) -> int64;
extern func bux_alloc(size: uint) -> *void;
extern func bux_strlen(s: String) -> uint;
extern func bux_str_to_int(s: String) -> int64;
extern func bux_alloc(size: uint) -> *void;
// Core formatting engine: replace {0}..{9} in template with args
func Fmt_Format(tmpl: String, argStrs: *String, argCount: int) -> String {
let sb: StringBuilder = StringBuilder_New();
var i: uint = 0;
let tmplLen: uint = bux_strlen(tmpl);
while i < tmplLen {
// Check for {
let ch: String = String_Chars(tmpl, i);
if String_Eq(ch, "{") {
let digitIdx: uint = i + 1;
if digitIdx < tmplLen {
let digitCh: String = String_Chars(tmpl, digitIdx);
let d: int64 = bux_str_to_int(digitCh);
if d >= 0 && d < argCount as int64 {
// Consume {d}
i = i + 2; // skip past digit
// Check for closing }
if i < tmplLen {
let closeCh: String = String_Chars(tmpl, i);
if String_Eq(closeCh, "}") {
i = i + 1; // skip }
let argStr: String = argStrs[d as uint];
StringBuilder_Append(&sb, argStr);
continue;
// Core formatting engine: replace {0}..{9} in template with args
func Fmt_Format(tmpl: String, argStrs: *String, argCount: int) -> String {
let sb: StringBuilder = StringBuilder_New();
var i: uint = 0;
let tmplLen: uint = bux_strlen(tmpl);
while i < tmplLen {
// Check for {
let ch: String = String_Chars(tmpl, i);
if String_Eq(ch, "{") {
let digitIdx: uint = i + 1;
if digitIdx < tmplLen {
let digitCh: String = String_Chars(tmpl, digitIdx);
let d: int64 = bux_str_to_int(digitCh);
if d >= 0 && d < argCount as int64 {
// Consume {d}
i = i + 2; // skip past digit
// Check for closing }
if i < tmplLen {
let closeCh: String = String_Chars(tmpl, i);
if String_Eq(closeCh, "}") {
i = i + 1; // skip }
let argStr: String = argStrs[d as uint];
StringBuilder_Append(&sb, argStr);
continue;
}
}
}
}
}
// Normal character: append it
StringBuilder_Append(&sb, ch);
i = i + 1;
}
// Normal character: append it
StringBuilder_Append(&sb, ch);
i = i + 1;
return StringBuilder_Build(&sb);
}
return StringBuilder_Build(&sb);
}
// Convenience wrappers
func Fmt_Fmt1(tmpl: String, a1: String) -> String {
var args: *String = bux_alloc(sizeof(String)) as *String;
args[0] = a1;
return Fmt_Format(tmpl, args, 1);
}
// Convenience wrappers
func Fmt_Fmt1(tmpl: String, a1: String) -> String {
var args: *String = bux_alloc(sizeof(String)) as *String;
args[0] = a1;
return Fmt_Format(tmpl, args, 1);
}
func Fmt_FmtInt(tmpl: String, val: int64) -> String {
let s: String = String_FromInt(val);
return Fmt_Fmt1(tmpl, s);
}
func Fmt_FmtInt(tmpl: String, val: int64) -> String {
let s: String = String_FromInt(val);
return Fmt_Fmt1(tmpl, s);
}
func Fmt_FmtBool(tmpl: String, val: bool) -> String {
let s: String = String_FromBool(val);
return Fmt_Fmt1(tmpl, s);
}
func Fmt_FmtBool(tmpl: String, val: bool) -> String {
let s: String = String_FromBool(val);
return Fmt_Fmt1(tmpl, s);
}
func Fmt_FmtFloat(tmpl: String, val: float64) -> String {
let s: String = String_FromFloat(val);
return Fmt_Fmt1(tmpl, s);
}
func Fmt_FmtFloat(tmpl: String, val: float64) -> String {
let s: String = String_FromFloat(val);
return Fmt_Fmt1(tmpl, s);
}
func Fmt_Fmt2(tmpl: String, a1: String, a2: String) -> String {
var args: *String = bux_alloc(2 * sizeof(String)) as *String;
args[0] = a1;
args[1] = a2;
return Fmt_Format(tmpl, args, 2);
}
func Fmt_Fmt2(tmpl: String, a1: String, a2: String) -> String {
var args: *String = bux_alloc(2 * sizeof(String)) as *String;
args[0] = a1;
args[1] = a2;
return Fmt_Format(tmpl, args, 2);
}
func Fmt_Fmt3(tmpl: String, a1: String, a2: String, a3: String) -> String {
var args: *String = bux_alloc(3 * sizeof(String)) as *String;
args[0] = a1;
args[1] = a2;
args[2] = a3;
return Fmt_Format(tmpl, args, 3);
}
func Fmt_Fmt3(tmpl: String, a1: String, a2: String, a3: String) -> String {
var args: *String = bux_alloc(3 * sizeof(String)) as *String;
args[0] = a1;
args[1] = a2;
args[2] = a3;
return Fmt_Format(tmpl, args, 3);
}
}
+12 -12
View File
@@ -1,19 +1,19 @@
module Std::Fs {
extern func bux_dir_exists(path: String) -> int;
extern func bux_mkdir_if_needed(path: String) -> int;
extern func bux_list_dir(dir: String, ext: String, out_count: *int) -> *String;
extern func bux_dir_exists(path: String) -> int;
extern func bux_mkdir_if_needed(path: String) -> int;
extern func bux_list_dir(dir: String, ext: String, out_count: *int) -> *String;
func DirExists(path: String) -> bool {
return bux_dir_exists(path) != 0;
}
func DirExists(path: String) -> bool {
return bux_dir_exists(path) != 0;
}
func Mkdir(path: String) -> bool {
return bux_mkdir_if_needed(path) != 0;
}
func Mkdir(path: String) -> bool {
return bux_mkdir_if_needed(path) != 0;
}
func ListDir(dir: String, ext: String, count: *int) -> *String {
return bux_list_dir(dir, ext, count);
}
func ListDir(dir: String, ext: String, count: *int) -> *String {
return bux_list_dir(dir, ext, count);
}
}
+21 -21
View File
@@ -1,28 +1,28 @@
module Std::Io {
extern func PrintLine(s: String);
extern func Print(s: String);
extern func PrintInt(n: int);
extern func PrintInt64(n: int64);
extern func PrintFloat(f: float64);
extern func PrintBool(b: bool);
extern func ReadLine() -> String;
extern func bux_read_file(path: String) -> String;
extern func bux_write_file(path: String, content: String) -> int;
extern func bux_file_exists(path: String) -> int;
extern func PrintLine(s: String);
extern func Print(s: String);
extern func PrintInt(n: int);
extern func PrintInt64(n: int64);
extern func PrintFloat(f: float64);
extern func PrintBool(b: bool);
extern func ReadLine() -> String;
extern func bux_read_file(path: String) -> String;
extern func bux_write_file(path: String, content: String) -> int;
extern func bux_file_exists(path: String) -> int;
func ReadFile(path: String) -> String {
return bux_read_file(path);
}
func ReadFile(path: String) -> String {
return bux_read_file(path);
}
func WriteFile(path: String, content: String) -> bool {
let r: int = bux_write_file(path, content);
return r != 0;
}
func WriteFile(path: String, content: String) -> bool {
let r: int = bux_write_file(path, content);
return r != 0;
}
func FileExists(path: String) -> bool {
let r: int = bux_file_exists(path);
return r != 0;
}
func FileExists(path: String) -> bool {
let r: int = bux_file_exists(path);
return r != 0;
}
}
+201 -201
View File
@@ -1,233 +1,233 @@
module Std::Iter {
import Std::Array::*;
import Std::Array::*;
struct Iter<T> {
data: *T,
len: uint,
pos: uint,
}
/* Create an iterator from an Array */
func Array_Iter<T>(arr: *Array<T>) -> Iter<T> {
return Iter<T> { data: arr.data, len: arr.len, pos: 0 };
}
/* Check if there are more elements */
func Iter_HasNext<T>(it: *Iter<T>) -> bool {
return it.pos < it.len;
}
/* Get the next element and advance (undefined if HasNext is false) */
func Iter_Next<T>(it: *Iter<T>) -> T {
let val: T = it.data[it.pos];
it.pos = it.pos + 1;
return val;
}
/* Peek current element without advancing (undefined if HasNext is false) */
func Iter_Peek<T>(it: *Iter<T>) -> T {
return it.data[it.pos];
}
/* Reset iterator to the beginning */
func Iter_Reset<T>(it: *Iter<T>) {
it.pos = 0;
}
/* Current position */
func Iter_Pos<T>(it: *Iter<T>) -> uint {
return it.pos;
}
/* Remaining length */
func Iter_Len<T>(it: *Iter<T>) -> uint {
return it.len;
}
/* Count remaining elements */
func Iter_Count<T>(it: *Iter<T>) -> uint {
return it.len - it.pos;
}
/* Skip N elements */
func Iter_Skip<T>(it: *Iter<T>, n: uint) {
it.pos = it.pos + n;
if it.pos > it.len {
it.pos = it.len;
struct Iter<T> {
data: *T,
len: uint,
pos: uint,
}
}
/* Take first N elements (by limiting len) */
func Iter_Take<T>(it: *Iter<T>, n: uint) -> Iter<T> {
var endPos: uint = it.pos + n;
if endPos > it.len {
endPos = it.len;
/* Create an iterator from an Array */
func Array_Iter<T>(arr: *Array<T>) -> Iter<T> {
return Iter<T> { data: arr.data, len: arr.len, pos: 0 };
}
return Iter<T> { data: it.data, len: endPos, pos: it.pos };
}
/* True if any remaining element equals value */
func Iter_AnyEq<T>(it: *Iter<T>, value: T) -> bool {
var i: uint = it.pos;
while i < it.len {
if it.data[i] == value {
return true;
/* Check if there are more elements */
func Iter_HasNext<T>(it: *Iter<T>) -> bool {
return it.pos < it.len;
}
/* Get the next element and advance (undefined if HasNext is false) */
func Iter_Next<T>(it: *Iter<T>) -> T {
let val: T = it.data[it.pos];
it.pos = it.pos + 1;
return val;
}
/* Peek current element without advancing (undefined if HasNext is false) */
func Iter_Peek<T>(it: *Iter<T>) -> T {
return it.data[it.pos];
}
/* Reset iterator to the beginning */
func Iter_Reset<T>(it: *Iter<T>) {
it.pos = 0;
}
/* Current position */
func Iter_Pos<T>(it: *Iter<T>) -> uint {
return it.pos;
}
/* Remaining length */
func Iter_Len<T>(it: *Iter<T>) -> uint {
return it.len;
}
/* Count remaining elements */
func Iter_Count<T>(it: *Iter<T>) -> uint {
return it.len - it.pos;
}
/* Skip N elements */
func Iter_Skip<T>(it: *Iter<T>, n: uint) {
it.pos = it.pos + n;
if it.pos > it.len {
it.pos = it.len;
}
i = i + 1;
}
return false;
}
/* True if every remaining element equals value (true if empty) */
func Iter_AllEq<T>(it: *Iter<T>, value: T) -> bool {
var i: uint = it.pos;
while i < it.len {
if it.data[i] != value {
return false;
/* Take first N elements (by limiting len) */
func Iter_Take<T>(it: *Iter<T>, n: uint) -> Iter<T> {
var endPos: uint = it.pos + n;
if endPos > it.len {
endPos = it.len;
}
i = i + 1;
return Iter<T> { data: it.data, len: endPos, pos: it.pos };
}
return true;
}
/* Collect remaining elements into a new Array */
func Iter_Collect<T>(it: *Iter<T>) -> Array<T> {
let remaining: uint = it.len - it.pos;
var cap: uint = remaining;
if cap == 0 {
cap = 1;
}
var arr: Array<T> = Array_New<T>(cap);
var i: uint = it.pos;
while i < it.len {
Array_Push<T>(&arr, it.data[i]);
i = i + 1;
}
return arr;
}
// ---------------------------------------------------------------------------
// Higher-order helpers (generic; fat func pointers / closures)
// ---------------------------------------------------------------------------
/* Map each remaining element through f: T → U, collect into Array<U> */
func Iter_Map<T, U>(it: *Iter<T>, f: func(T) -> U) -> Array<U> {
let remaining: uint = it.len - it.pos;
var cap: uint = remaining;
if cap == 0 {
cap = 1;
}
var out: Array<U> = Array_New<U>(cap);
var i: uint = it.pos;
while i < it.len {
let mapped: U = f(it.data[i]);
Array_Push<U>(&out, mapped);
i = i + 1;
}
return out;
}
/* Keep remaining elements for which pred returns true */
func Iter_Filter<T>(it: *Iter<T>, pred: func(T) -> bool) -> Array<T> {
let remaining: uint = it.len - it.pos;
var cap: uint = remaining;
if cap == 0 {
cap = 1;
}
var out: Array<T> = Array_New<T>(cap);
var i: uint = it.pos;
while i < it.len {
let v: T = it.data[i];
if pred(v) {
Array_Push<T>(&out, v);
/* True if any remaining element equals value */
func Iter_AnyEq<T>(it: *Iter<T>, value: T) -> bool {
var i: uint = it.pos;
while i < it.len {
if it.data[i] == value {
return true;
}
i = i + 1;
}
i = i + 1;
return false;
}
return out;
}
/* Left-fold: f(f(...f(init, x0), x1), ...) */
func Iter_Fold<T, Acc>(it: *Iter<T>, init: Acc, f: func(Acc, T) -> Acc) -> Acc {
var acc: Acc = init;
var i: uint = it.pos;
while i < it.len {
acc = f(acc, it.data[i]);
i = i + 1;
}
return acc;
}
/* Call f for each remaining element (return value of f is ignored) */
func Iter_ForEach<T>(it: *Iter<T>, f: func(T) -> int) {
var i: uint = it.pos;
while i < it.len {
let _ignored: int = f(it.data[i]);
i = i + 1;
}
}
/* True if any remaining element satisfies pred */
func Iter_Any<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool {
var i: uint = it.pos;
while i < it.len {
if pred(it.data[i]) {
return true;
/* True if every remaining element equals value (true if empty) */
func Iter_AllEq<T>(it: *Iter<T>, value: T) -> bool {
var i: uint = it.pos;
while i < it.len {
if it.data[i] != value {
return false;
}
i = i + 1;
}
i = i + 1;
return true;
}
return false;
}
/* True if all remaining elements satisfy pred (true if empty) */
func Iter_All<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool {
var i: uint = it.pos;
while i < it.len {
if !pred(it.data[i]) {
return false;
/* Collect remaining elements into a new Array */
func Iter_Collect<T>(it: *Iter<T>) -> Array<T> {
let remaining: uint = it.len - it.pos;
var cap: uint = remaining;
if cap == 0 {
cap = 1;
}
i = i + 1;
var arr: Array<T> = Array_New<T>(cap);
var i: uint = it.pos;
while i < it.len {
Array_Push<T>(&arr, it.data[i]);
i = i + 1;
}
return arr;
}
return true;
}
/* Sum remaining ints (specialized fold) */
func Iter_SumInt(it: *Iter<int>) -> int {
var total: int = 0;
var i: uint = it.pos;
while i < it.len {
total = total + it.data[i];
i = i + 1;
// ---------------------------------------------------------------------------
// Higher-order helpers (generic; fat func pointers / closures)
// ---------------------------------------------------------------------------
/* Map each remaining element through f: T → U, collect into Array<U> */
func Iter_Map<T, U>(it: *Iter<T>, f: func(T) -> U) -> Array<U> {
let remaining: uint = it.len - it.pos;
var cap: uint = remaining;
if cap == 0 {
cap = 1;
}
var out: Array<U> = Array_New<U>(cap);
var i: uint = it.pos;
while i < it.len {
let mapped: U = f(it.data[i]);
Array_Push<U>(&out, mapped);
i = i + 1;
}
return out;
}
return total;
}
// ---------------------------------------------------------------------------
// Int-specialized aliases (backward compatible with earlier examples)
// ---------------------------------------------------------------------------
/* Keep remaining elements for which pred returns true */
func Iter_Filter<T>(it: *Iter<T>, pred: func(T) -> bool) -> Array<T> {
let remaining: uint = it.len - it.pos;
var cap: uint = remaining;
if cap == 0 {
cap = 1;
}
var out: Array<T> = Array_New<T>(cap);
var i: uint = it.pos;
while i < it.len {
let v: T = it.data[i];
if pred(v) {
Array_Push<T>(&out, v);
}
i = i + 1;
}
return out;
}
func Iter_MapInt(it: *Iter<int>, f: func(int) -> int) -> Array<int> {
return Iter_Map<int, int>(it, f);
}
/* Left-fold: f(f(...f(init, x0), x1), ...) */
func Iter_Fold<T, Acc>(it: *Iter<T>, init: Acc, f: func(Acc, T) -> Acc) -> Acc {
var acc: Acc = init;
var i: uint = it.pos;
while i < it.len {
acc = f(acc, it.data[i]);
i = i + 1;
}
return acc;
}
func Iter_FilterInt(it: *Iter<int>, pred: func(int) -> bool) -> Array<int> {
return Iter_Filter<int>(it, pred);
}
/* Call f for each remaining element (return value of f is ignored) */
func Iter_ForEach<T>(it: *Iter<T>, f: func(T) -> int) {
var i: uint = it.pos;
while i < it.len {
let _ignored: int = f(it.data[i]);
i = i + 1;
}
}
func Iter_FoldInt(it: *Iter<int>, init: int, f: func(int, int) -> int) -> int {
return Iter_Fold<int, int>(it, init, f);
}
/* True if any remaining element satisfies pred */
func Iter_Any<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool {
var i: uint = it.pos;
while i < it.len {
if pred(it.data[i]) {
return true;
}
i = i + 1;
}
return false;
}
func Iter_ForEachInt(it: *Iter<int>, f: func(int) -> int) {
Iter_ForEach<int>(it, f);
}
/* True if all remaining elements satisfy pred (true if empty) */
func Iter_All<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool {
var i: uint = it.pos;
while i < it.len {
if !pred(it.data[i]) {
return false;
}
i = i + 1;
}
return true;
}
func Iter_AnyInt(it: *Iter<int>, pred: func(int) -> bool) -> bool {
return Iter_Any<int>(it, pred);
}
/* Sum remaining ints (specialized fold) */
func Iter_SumInt(it: *Iter<int>) -> int {
var total: int = 0;
var i: uint = it.pos;
while i < it.len {
total = total + it.data[i];
i = i + 1;
}
return total;
}
func Iter_AllInt(it: *Iter<int>, pred: func(int) -> bool) -> bool {
return Iter_All<int>(it, pred);
}
// ---------------------------------------------------------------------------
// Int-specialized aliases (backward compatible with earlier examples)
// ---------------------------------------------------------------------------
func Iter_MapInt(it: *Iter<int>, f: func(int) -> int) -> Array<int> {
return Iter_Map<int, int>(it, f);
}
func Iter_FilterInt(it: *Iter<int>, pred: func(int) -> bool) -> Array<int> {
return Iter_Filter<int>(it, pred);
}
func Iter_FoldInt(it: *Iter<int>, init: int, f: func(int, int) -> int) -> int {
return Iter_Fold<int, int>(it, init, f);
}
func Iter_ForEachInt(it: *Iter<int>, f: func(int) -> int) {
Iter_ForEach<int>(it, f);
}
func Iter_AnyInt(it: *Iter<int>, pred: func(int) -> bool) -> bool {
return Iter_Any<int>(it, pred);
}
func Iter_AllInt(it: *Iter<int>, pred: func(int) -> bool) -> bool {
return Iter_All<int>(it, pred);
}
}
+408 -408
View File
@@ -1,297 +1,287 @@
module Std::Json {
import Std::Mem::{Alloc, Realloc, Free};
import Std::String;
import Std::Mem::{Alloc, Realloc, Free};
import Std::String;
/* === Tags === */
const JsonTagNull: int = 0;
const JsonTagBool: int = 1;
const JsonTagNumber: int = 2;
const JsonTagString: int = 3;
const JsonTagArray: int = 4;
const JsonTagObject: int = 5;
/* === Tags === */
const JsonTagNull: int = 0;
const JsonTagBool: int = 1;
const JsonTagNumber: int = 2;
const JsonTagString: int = 3;
const JsonTagArray: int = 4;
const JsonTagObject: int = 5;
/* === Core type === */
struct JsonValue {
tag: int,
boolVal: bool,
numVal: float64,
strVal: String,
arrData: *JsonValue,
arrLen: uint,
arrCap: uint,
objKeys: *String,
objValues: *JsonValue,
objLen: uint,
objCap: uint
}
/* === Core type === */
struct JsonValue {
tag: int,
boolVal: bool,
numVal: float64,
strVal: String,
arrData: *JsonValue,
arrLen: uint,
arrCap: uint,
objKeys: *String,
objValues: *JsonValue,
objLen: uint,
objCap: uint
}
/* === Constructors === */
func Json_Null() -> JsonValue {
return JsonValue {
tag: JsonTagNull, boolVal: false, numVal: 0.0, strVal: "",
arrData: null, arrLen: 0, arrCap: 0,
objKeys: null, objValues: null, objLen: 0, objCap: 0
};
}
/* === Constructors === */
func Json_Null() -> JsonValue {
return JsonValue {
tag: JsonTagNull, boolVal: false, numVal: 0.0, strVal: "",
arrData: null, arrLen: 0, arrCap: 0,
objKeys: null, objValues: null, objLen: 0, objCap: 0
};
}
func Json_Bool(b: bool) -> JsonValue {
return JsonValue {
tag: JsonTagBool, boolVal: b, numVal: 0.0, strVal: "",
arrData: null, arrLen: 0, arrCap: 0,
objKeys: null, objValues: null, objLen: 0, objCap: 0
};
}
func Json_Bool(b: bool) -> JsonValue {
return JsonValue {
tag: JsonTagBool, boolVal: b, numVal: 0.0, strVal: "",
arrData: null, arrLen: 0, arrCap: 0,
objKeys: null, objValues: null, objLen: 0, objCap: 0
};
}
func Json_Number(n: float64) -> JsonValue {
return JsonValue {
tag: JsonTagNumber, boolVal: false, numVal: n, strVal: "",
arrData: null, arrLen: 0, arrCap: 0,
objKeys: null, objValues: null, objLen: 0, objCap: 0
};
}
func Json_Number(n: float64) -> JsonValue {
return JsonValue {
tag: JsonTagNumber, boolVal: false, numVal: n, strVal: "",
arrData: null, arrLen: 0, arrCap: 0,
objKeys: null, objValues: null, objLen: 0, objCap: 0
};
}
func Json_String(s: String) -> JsonValue {
return JsonValue {
tag: JsonTagString, boolVal: false, numVal: 0.0, strVal: s,
arrData: null, arrLen: 0, arrCap: 0,
objKeys: null, objValues: null, objLen: 0, objCap: 0
};
}
func Json_String(s: String) -> JsonValue {
return JsonValue {
tag: JsonTagString, boolVal: false, numVal: 0.0, strVal: s,
arrData: null, arrLen: 0, arrCap: 0,
objKeys: null, objValues: null, objLen: 0, objCap: 0
};
}
func Json_Array() -> JsonValue {
return JsonValue {
tag: JsonTagArray, boolVal: false, numVal: 0.0, strVal: "",
arrData: null, arrLen: 0, arrCap: 0,
objKeys: null, objValues: null, objLen: 0, objCap: 0
};
}
func Json_Array() -> JsonValue {
return JsonValue {
tag: JsonTagArray, boolVal: false, numVal: 0.0, strVal: "",
arrData: null, arrLen: 0, arrCap: 0,
objKeys: null, objValues: null, objLen: 0, objCap: 0
};
}
func Json_Object() -> JsonValue {
return JsonValue {
tag: JsonTagObject, boolVal: false, numVal: 0.0, strVal: "",
arrData: null, arrLen: 0, arrCap: 0,
objKeys: null, objValues: null, objLen: 0, objCap: 0
};
}
func Json_Object() -> JsonValue {
return JsonValue {
tag: JsonTagObject, boolVal: false, numVal: 0.0, strVal: "",
arrData: null, arrLen: 0, arrCap: 0,
objKeys: null, objValues: null, objLen: 0, objCap: 0
};
}
/* === Array helpers === */
func Json_ArrayLen(v: JsonValue) -> uint {
if v.tag != JsonTagArray { return 0; }
return v.arrLen;
}
/* === Array helpers === */
func Json_ArrayLen(v: JsonValue) -> uint {
if v.tag != JsonTagArray { return 0; }
return v.arrLen;
}
func Json_ArrayGet(v: JsonValue, index: uint) -> JsonValue {
if v.tag != JsonTagArray { return Json_Null(); }
if index >= v.arrLen { return Json_Null(); }
return v.arrData[index];
}
func Json_ArrayGet(v: JsonValue, index: uint) -> JsonValue {
if v.tag != JsonTagArray { return Json_Null(); }
if index >= v.arrLen { return Json_Null(); }
return v.arrData[index];
}
func Json_ArrayPush(self: *JsonValue, val: JsonValue) {
if self.tag != JsonTagArray { return; }
if self.arrLen >= self.arrCap {
let arrNewCap: uint = self.arrCap;
if arrNewCap == 0 {
self.arrCap = 4;
self.arrData = Alloc(4 * sizeof(JsonValue)) as *JsonValue;
} else {
let doubleCap: uint = arrNewCap * 2;
self.arrCap = doubleCap;
self.arrData = Realloc(self.arrData as *void, doubleCap * sizeof(JsonValue)) as *JsonValue;
func Json_ArrayPush(self: *JsonValue, val: JsonValue) {
if self.tag != JsonTagArray { return; }
if self.arrLen >= self.arrCap {
let arrNewCap: uint = self.arrCap;
if arrNewCap == 0 {
self.arrCap = 4;
self.arrData = Alloc(4 * sizeof(JsonValue)) as *JsonValue;
} else {
let doubleCap: uint = arrNewCap * 2;
self.arrCap = doubleCap;
self.arrData = Realloc(self.arrData as *void, doubleCap * sizeof(JsonValue)) as *JsonValue;
}
}
self.arrData[self.arrLen] = val;
self.arrLen = self.arrLen + 1;
}
self.arrData[self.arrLen] = val;
self.arrLen = self.arrLen + 1;
}
/* === Object helpers === */
func Json_ObjectLen(v: JsonValue) -> uint {
if v.tag != JsonTagObject { return 0; }
return v.objLen;
}
/* === Object helpers === */
func Json_ObjectLen(v: JsonValue) -> uint {
if v.tag != JsonTagObject { return 0; }
return v.objLen;
}
func Json_ObjectGet(v: JsonValue, key: String) -> JsonValue {
if v.tag != JsonTagObject { return Json_Null(); }
var i: uint = 0;
while i < v.objLen {
if String_Eq(v.objKeys[i], key) {
return v.objValues[i];
func Json_ObjectGet(v: JsonValue, key: String) -> JsonValue {
if v.tag != JsonTagObject { return Json_Null(); }
var i: uint = 0;
while i < v.objLen {
if String_Eq(v.objKeys[i], key) {
return v.objValues[i];
}
i = i + 1;
}
i = i + 1;
return Json_Null();
}
return Json_Null();
}
func Json_ObjectHas(v: JsonValue, key: String) -> bool {
if v.tag != JsonTagObject { return false; }
var i: uint = 0;
while i < v.objLen {
if String_Eq(v.objKeys[i], key) {
return true;
func Json_ObjectHas(v: JsonValue, key: String) -> bool {
if v.tag != JsonTagObject { return false; }
var i: uint = 0;
while i < v.objLen {
if String_Eq(v.objKeys[i], key) {
return true;
}
i = i + 1;
}
i = i + 1;
return false;
}
return false;
}
func Json_ObjectSet(self: *JsonValue, key: String, val: JsonValue) {
if self.tag != JsonTagObject { return; }
var i: uint = 0;
while i < self.objLen {
if String_Eq(self.objKeys[i], key) {
self.objValues[i] = val;
return;
func Json_ObjectSet(self: *JsonValue, key: String, val: JsonValue) {
if self.tag != JsonTagObject { return; }
var i: uint = 0;
while i < self.objLen {
if String_Eq(self.objKeys[i], key) {
self.objValues[i] = val;
return;
}
i = i + 1;
}
i = i + 1;
}
if self.objLen >= self.objCap {
let objNewCap: uint = self.objCap;
if objNewCap == 0 {
self.objCap = 4;
self.objKeys = Alloc(4 * sizeof(String)) as *String;
self.objValues = Alloc(4 * sizeof(JsonValue)) as *JsonValue;
} else {
let doubleCap: uint = objNewCap * 2;
self.objCap = doubleCap;
self.objKeys = Realloc(self.objKeys as *void, doubleCap * sizeof(String)) as *String;
self.objValues = Realloc(self.objValues as *void, doubleCap * sizeof(JsonValue)) as *JsonValue;
if self.objLen >= self.objCap {
let objNewCap: uint = self.objCap;
if objNewCap == 0 {
self.objCap = 4;
self.objKeys = Alloc(4 * sizeof(String)) as *String;
self.objValues = Alloc(4 * sizeof(JsonValue)) as *JsonValue;
} else {
let doubleCap: uint = objNewCap * 2;
self.objCap = doubleCap;
self.objKeys = Realloc(self.objKeys as *void, doubleCap * sizeof(String)) as *String;
self.objValues = Realloc(self.objValues as *void, doubleCap * sizeof(JsonValue)) as *JsonValue;
}
}
self.objKeys[self.objLen] = key;
self.objValues[self.objLen] = val;
self.objLen = self.objLen + 1;
}
self.objKeys[self.objLen] = key;
self.objValues[self.objLen] = val;
self.objLen = self.objLen + 1;
}
/* === Accessors === */
func Json_IsNull(v: JsonValue) -> bool {
return v.tag == JsonTagNull;
}
func Json_AsBool(v: JsonValue) -> bool {
if v.tag == JsonTagBool { return v.boolVal; }
return false;
}
func Json_AsNumber(v: JsonValue) -> float64 {
if v.tag == JsonTagNumber { return v.numVal; }
return 0.0;
}
func Json_AsString(v: JsonValue) -> String {
if v.tag == JsonTagString { return v.strVal; }
return "";
}
/* === Parser === */
struct JsonParser {
src: String,
pos: uint,
len: uint,
error: String
}
func JsonParser_Peek(p: *JsonParser) -> int {
if p.pos >= p.len { return 0; }
return p.src[p.pos] as int;
}
func JsonParser_Advance(p: *JsonParser) {
if p.pos < p.len {
p.pos = p.pos + 1;
/* === Accessors === */
func Json_IsNull(v: JsonValue) -> bool {
return v.tag == JsonTagNull;
}
}
func JsonParser_SkipWhitespace(p: *JsonParser) {
while true {
let c: int = JsonParser_Peek(p);
if c == 32 || c == 9 || c == 10 || c == 13 {
JsonParser_Advance(p);
} else {
return;
}
func Json_AsBool(v: JsonValue) -> bool {
if v.tag == JsonTagBool { return v.boolVal; }
return false;
}
}
func JsonParser_Match(p: *JsonParser, expected: String) -> bool {
let elen: uint = String_Len(expected);
if p.pos + elen > p.len { return false; }
var i: uint = 0;
while i < elen {
if p.src[p.pos + i] != expected[i] {
return false;
}
i = i + 1;
func Json_AsNumber(v: JsonValue) -> float64 {
if v.tag == JsonTagNumber { return v.numVal; }
return 0.0;
}
p.pos = p.pos + elen;
return true;
}
func JsonParser_ParseValue(p: *JsonParser) -> JsonValue;
func JsonParser_ParseString(p: *JsonParser) -> String {
if JsonParser_Peek(p) != 34 {
p.error = "Expected string";
func Json_AsString(v: JsonValue) -> String {
if v.tag == JsonTagString { return v.strVal; }
return "";
}
JsonParser_Advance(p);
let sb: StringBuilder = StringBuilder_New();
while true {
let c: int = JsonParser_Peek(p);
if c == 0 || c == 34 {
break;
}
if c == 92 {
JsonParser_Advance(p);
let esc: int = JsonParser_Peek(p);
if esc == 0 {
p.error = "Unterminated string escape";
StringBuilder_Free(&sb);
return "";
}
if esc == 110 { StringBuilder_AppendChar(&sb, 10 as char8); } // \n
else if esc == 116 { StringBuilder_AppendChar(&sb, 9 as char8); } // \t
else if esc == 114 { StringBuilder_AppendChar(&sb, 13 as char8); } // \r
else if esc == 98 { StringBuilder_AppendChar(&sb, 8 as char8); } // \b
else if esc == 102 { StringBuilder_AppendChar(&sb, 12 as char8); } // \f
else if esc == 34 { StringBuilder_AppendChar(&sb, 34 as char8); } // \"
else if esc == 92 { StringBuilder_AppendChar(&sb, 92 as char8); } // \\
else {
// Unknown escape — keep literal
StringBuilder_AppendChar(&sb, 92 as char8);
StringBuilder_AppendChar(&sb, esc as char8);
}
JsonParser_Advance(p);
} else {
StringBuilder_AppendChar(&sb, c as char8);
JsonParser_Advance(p);
/* === Parser === */
struct JsonParser {
src: String,
pos: uint,
len: uint,
error: String
}
func JsonParser_Peek(p: *JsonParser) -> int {
if p.pos >= p.len { return 0; }
return p.src[p.pos] as int;
}
func JsonParser_Advance(p: *JsonParser) {
if p.pos < p.len {
p.pos = p.pos + 1;
}
}
if JsonParser_Peek(p) != 34 {
p.error = "Unterminated string";
func JsonParser_SkipWhitespace(p: *JsonParser) {
while true {
let c: int = JsonParser_Peek(p);
if c == 32 || c == 9 || c == 10 || c == 13 {
JsonParser_Advance(p);
} else {
return;
}
}
}
func JsonParser_Match(p: *JsonParser, expected: String) -> bool {
let elen: uint = String_Len(expected);
if p.pos + elen > p.len { return false; }
var i: uint = 0;
while i < elen {
if p.src[p.pos + i] != expected[i] {
return false;
}
i = i + 1;
}
p.pos = p.pos + elen;
return true;
}
func JsonParser_ParseValue(p: *JsonParser) -> JsonValue;
func JsonParser_ParseString(p: *JsonParser) -> String {
if JsonParser_Peek(p) != 34 {
p.error = "Expected string";
return "";
}
JsonParser_Advance(p);
let sb: StringBuilder = StringBuilder_New();
while true {
let c: int = JsonParser_Peek(p);
if c == 0 || c == 34 {
break;
}
if c == 92 {
JsonParser_Advance(p);
let esc: int = JsonParser_Peek(p);
if esc == 0 {
p.error = "Unterminated string escape";
StringBuilder_Free(&sb);
return "";
}
if esc == 110 { StringBuilder_AppendChar(&sb, 10 as char8); } // \n
else if esc == 116 { StringBuilder_AppendChar(&sb, 9 as char8); } // \t
else if esc == 114 { StringBuilder_AppendChar(&sb, 13 as char8); } // \r
else if esc == 98 { StringBuilder_AppendChar(&sb, 8 as char8); } // \b
else if esc == 102 { StringBuilder_AppendChar(&sb, 12 as char8); } // \f
else if esc == 34 { StringBuilder_AppendChar(&sb, 34 as char8); } // \"
else if esc == 92 { StringBuilder_AppendChar(&sb, 92 as char8); } // \\
else {
// Unknown escape — keep literal
StringBuilder_AppendChar(&sb, 92 as char8);
StringBuilder_AppendChar(&sb, esc as char8);
}
JsonParser_Advance(p);
} else {
StringBuilder_AppendChar(&sb, c as char8);
JsonParser_Advance(p);
}
}
if JsonParser_Peek(p) != 34 {
p.error = "Unterminated string";
StringBuilder_Free(&sb);
return "";
}
JsonParser_Advance(p);
let result: String = StringBuilder_Build(&sb);
StringBuilder_Free(&sb);
return "";
return result;
}
JsonParser_Advance(p);
let result: String = StringBuilder_Build(&sb);
StringBuilder_Free(&sb);
return result;
}
func JsonParser_ParseNumber(p: *JsonParser) -> JsonValue {
let start: uint = p.pos;
let c0: int = JsonParser_Peek(p);
if c0 == 45 {
JsonParser_Advance(p);
}
while true {
let c: int = JsonParser_Peek(p);
if c >= 48 && c <= 57 {
func JsonParser_ParseNumber(p: *JsonParser) -> JsonValue {
let start: uint = p.pos;
let c0: int = JsonParser_Peek(p);
if c0 == 45 {
JsonParser_Advance(p);
} else {
break;
}
}
if JsonParser_Peek(p) == 46 {
JsonParser_Advance(p);
while true {
let c: int = JsonParser_Peek(p);
if c >= 48 && c <= 57 {
@@ -300,193 +290,203 @@ func JsonParser_ParseNumber(p: *JsonParser) -> JsonValue {
break;
}
}
if JsonParser_Peek(p) == 46 {
JsonParser_Advance(p);
while true {
let c: int = JsonParser_Peek(p);
if c >= 48 && c <= 57 {
JsonParser_Advance(p);
} else {
break;
}
}
}
let numStr: String = String_Slice(p.src, start, p.pos - start);
let n: float64 = String_ToFloat(numStr);
return Json_Number(n);
}
let numStr: String = String_Slice(p.src, start, p.pos - start);
let n: float64 = String_ToFloat(numStr);
return Json_Number(n);
}
func JsonParser_ParseArray(p: *JsonParser) -> JsonValue {
JsonParser_Advance(p);
var arr: JsonValue = Json_Array();
JsonParser_SkipWhitespace(p);
if JsonParser_Peek(p) == 93 {
func JsonParser_ParseArray(p: *JsonParser) -> JsonValue {
JsonParser_Advance(p);
return arr;
}
while true {
var arr: JsonValue = Json_Array();
JsonParser_SkipWhitespace(p);
let val: JsonValue = JsonParser_ParseValue(p);
if p.error != "" { return Json_Null(); }
Json_ArrayPush(&arr, val);
JsonParser_SkipWhitespace(p);
let c: int = JsonParser_Peek(p);
if c == 93 {
if JsonParser_Peek(p) == 93 {
JsonParser_Advance(p);
return arr;
}
if c == 44 {
JsonParser_Advance(p);
} else {
p.error = "Expected ',' or ']' in array";
return Json_Null();
while true {
JsonParser_SkipWhitespace(p);
let val: JsonValue = JsonParser_ParseValue(p);
if p.error != "" { return Json_Null(); }
Json_ArrayPush(&arr, val);
JsonParser_SkipWhitespace(p);
let c: int = JsonParser_Peek(p);
if c == 93 {
JsonParser_Advance(p);
return arr;
}
if c == 44 {
JsonParser_Advance(p);
} else {
p.error = "Expected ',' or ']' in array";
return Json_Null();
}
}
}
}
func JsonParser_ParseObject(p: *JsonParser) -> JsonValue {
JsonParser_Advance(p);
var obj: JsonValue = Json_Object();
JsonParser_SkipWhitespace(p);
if JsonParser_Peek(p) == 125 {
func JsonParser_ParseObject(p: *JsonParser) -> JsonValue {
JsonParser_Advance(p);
return obj;
}
while true {
var obj: JsonValue = Json_Object();
JsonParser_SkipWhitespace(p);
let key: String = JsonParser_ParseString(p);
if p.error != "" { return Json_Null(); }
JsonParser_SkipWhitespace(p);
if JsonParser_Peek(p) != 58 {
p.error = "Expected ':' after object key";
return Json_Null();
}
JsonParser_Advance(p);
JsonParser_SkipWhitespace(p);
let val: JsonValue = JsonParser_ParseValue(p);
if p.error != "" { return Json_Null(); }
Json_ObjectSet(&obj, key, val);
JsonParser_SkipWhitespace(p);
let c: int = JsonParser_Peek(p);
if c == 125 {
if JsonParser_Peek(p) == 125 {
JsonParser_Advance(p);
return obj;
}
if c == 44 {
while true {
JsonParser_SkipWhitespace(p);
let key: String = JsonParser_ParseString(p);
if p.error != "" { return Json_Null(); }
JsonParser_SkipWhitespace(p);
if JsonParser_Peek(p) != 58 {
p.error = "Expected ':' after object key";
return Json_Null();
}
JsonParser_Advance(p);
} else {
p.error = "Expected ',' or '}' in object";
JsonParser_SkipWhitespace(p);
let val: JsonValue = JsonParser_ParseValue(p);
if p.error != "" { return Json_Null(); }
Json_ObjectSet(&obj, key, val);
JsonParser_SkipWhitespace(p);
let c: int = JsonParser_Peek(p);
if c == 125 {
JsonParser_Advance(p);
return obj;
}
if c == 44 {
JsonParser_Advance(p);
} else {
p.error = "Expected ',' or '}' in object";
return Json_Null();
}
}
}
func JsonParser_ParseValue(p: *JsonParser) -> JsonValue {
JsonParser_SkipWhitespace(p);
let c: int = JsonParser_Peek(p);
if c == 0 {
p.error = "Unexpected end of input";
return Json_Null();
}
}
}
func JsonParser_ParseValue(p: *JsonParser) -> JsonValue {
JsonParser_SkipWhitespace(p);
let c: int = JsonParser_Peek(p);
if c == 0 {
p.error = "Unexpected end of input";
return Json_Null();
}
if c == 34 {
return Json_String(JsonParser_ParseString(p));
}
if c == 123 {
return JsonParser_ParseObject(p);
}
if c == 91 {
return JsonParser_ParseArray(p);
}
if c == 116 {
if JsonParser_Match(p, "true") {
return Json_Bool(true);
if c == 34 {
return Json_String(JsonParser_ParseString(p));
}
p.error = "Expected 'true'";
return Json_Null();
}
if c == 102 {
if JsonParser_Match(p, "false") {
return Json_Bool(false);
if c == 123 {
return JsonParser_ParseObject(p);
}
p.error = "Expected 'false'";
return Json_Null();
}
if c == 110 {
if JsonParser_Match(p, "null") {
if c == 91 {
return JsonParser_ParseArray(p);
}
if c == 116 {
if JsonParser_Match(p, "true") {
return Json_Bool(true);
}
p.error = "Expected 'true'";
return Json_Null();
}
p.error = "Expected 'null'";
if c == 102 {
if JsonParser_Match(p, "false") {
return Json_Bool(false);
}
p.error = "Expected 'false'";
return Json_Null();
}
if c == 110 {
if JsonParser_Match(p, "null") {
return Json_Null();
}
p.error = "Expected 'null'";
return Json_Null();
}
if (c >= 48 && c <= 57) || c == 45 {
return JsonParser_ParseNumber(p);
}
p.error = "Unexpected character";
return Json_Null();
}
if (c >= 48 && c <= 57) || c == 45 {
return JsonParser_ParseNumber(p);
}
p.error = "Unexpected character";
return Json_Null();
}
/* === Public parser === */
func Json_Parse(s: String) -> JsonValue {
var p: JsonParser = JsonParser { src: s, pos: 0, len: String_Len(s), error: "" };
let result: JsonValue = JsonParser_ParseValue(&p);
JsonParser_SkipWhitespace(&p);
if p.error == "" && p.pos != p.len {
p.error = "Trailing data after JSON value";
return Json_Null();
}
return result;
}
/* === Serializer === */
func Json_StringifyImpl(sb: *StringBuilder, v: JsonValue) {
if v.tag == JsonTagNull {
StringBuilder_Append(sb, "null");
return;
}
if v.tag == JsonTagBool {
if v.boolVal {
StringBuilder_Append(sb, "true");
} else {
StringBuilder_Append(sb, "false");
/* === Public parser === */
func Json_Parse(s: String) -> JsonValue {
var p: JsonParser = JsonParser { src: s, pos: 0, len: String_Len(s), error: "" };
let result: JsonValue = JsonParser_ParseValue(&p);
JsonParser_SkipWhitespace(&p);
if p.error == "" && p.pos != p.len {
p.error = "Trailing data after JSON value";
return Json_Null();
}
return;
return result;
}
if v.tag == JsonTagNumber {
StringBuilder_AppendFloat(sb, v.numVal);
return;
}
if v.tag == JsonTagString {
StringBuilder_AppendChar(sb, 34 as char8);
StringBuilder_Append(sb, v.strVal);
StringBuilder_AppendChar(sb, 34 as char8);
return;
}
if v.tag == JsonTagArray {
StringBuilder_AppendChar(sb, 91 as char8);
var i: uint = 0;
while i < v.arrLen {
if i > 0 {
StringBuilder_AppendChar(sb, 44 as char8);
/* === Serializer === */
func Json_StringifyImpl(sb: *StringBuilder, v: JsonValue) {
if v.tag == JsonTagNull {
StringBuilder_Append(sb, "null");
return;
}
if v.tag == JsonTagBool {
if v.boolVal {
StringBuilder_Append(sb, "true");
} else {
StringBuilder_Append(sb, "false");
}
Json_StringifyImpl(sb, v.arrData[i]);
i = i + 1;
return;
}
StringBuilder_AppendChar(sb, 93 as char8);
return;
}
if v.tag == JsonTagObject {
StringBuilder_AppendChar(sb, 123 as char8);
var i: uint = 0;
while i < v.objLen {
if i > 0 {
StringBuilder_AppendChar(sb, 44 as char8);
if v.tag == JsonTagNumber {
StringBuilder_AppendFloat(sb, v.numVal);
return;
}
if v.tag == JsonTagString {
StringBuilder_AppendChar(sb, 34 as char8);
StringBuilder_Append(sb, v.strVal);
StringBuilder_AppendChar(sb, 34 as char8);
return;
}
if v.tag == JsonTagArray {
StringBuilder_AppendChar(sb, 91 as char8);
var i: uint = 0;
while i < v.arrLen {
if i > 0 {
StringBuilder_AppendChar(sb, 44 as char8);
}
Json_StringifyImpl(sb, v.arrData[i]);
i = i + 1;
}
StringBuilder_AppendChar(sb, 34 as char8);
StringBuilder_Append(sb, v.objKeys[i]);
StringBuilder_AppendChar(sb, 34 as char8);
StringBuilder_AppendChar(sb, 58 as char8);
Json_StringifyImpl(sb, v.objValues[i]);
i = i + 1;
StringBuilder_AppendChar(sb, 93 as char8);
return;
}
if v.tag == JsonTagObject {
StringBuilder_AppendChar(sb, 123 as char8);
var i: uint = 0;
while i < v.objLen {
if i > 0 {
StringBuilder_AppendChar(sb, 44 as char8);
}
StringBuilder_AppendChar(sb, 34 as char8);
StringBuilder_Append(sb, v.objKeys[i]);
StringBuilder_AppendChar(sb, 34 as char8);
StringBuilder_AppendChar(sb, 58 as char8);
Json_StringifyImpl(sb, v.objValues[i]);
i = i + 1;
}
StringBuilder_AppendChar(sb, 125 as char8);
return;
}
StringBuilder_AppendChar(sb, 125 as char8);
return;
}
}
func Json_Stringify(v: JsonValue) -> String {
let sb: StringBuilder = StringBuilder_New();
Json_StringifyImpl(&sb, v);
return StringBuilder_Build(&sb);
}
func Json_Stringify(v: JsonValue) -> String {
let sb: StringBuilder = StringBuilder_New();
Json_StringifyImpl(&sb, v);
return StringBuilder_Build(&sb);
}
}
+211 -203
View File
@@ -1,242 +1,250 @@
module Std::Map {
extern func bux_hash_bytes(ptr: *void, size: uint) -> uint;
extern func bux_hash_string(s: String) -> uint;
extern func bux_hash_bytes(ptr: *void, size: uint) -> uint;
extern func bux_hash_string(s: String) -> uint;
// ---------------------------------------------------------------------------
// Generic Map<K, V> — works with value-type keys (int, float, etc.)
// For String keys, use StringMap below.
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Generic Map<K, V> — works with value-type keys (int, float, etc.)
// For String keys, use StringMap below.
// ---------------------------------------------------------------------------
struct MapEntry<K, V> {
key: K,
value: V,
occupied: bool,
}
struct Map<K, V> {
entries: *MapEntry<K, V>,
cap: uint,
len: uint,
}
func Map_New<K, V>(cap: uint) -> Map<K, V> {
let total: uint = cap * sizeof(MapEntry<K, V>);
let data: *MapEntry<K, V> = bux_alloc(total) as *MapEntry<K, V>;
var i: uint = 0;
while i < cap {
data[i].occupied = false;
i = i + 1;
struct MapEntry<K, V> {
key: K,
value: V,
occupied: bool,
}
return Map<K, V> { entries: data, cap: cap, len: 0 };
}
func Map_Set<K, V>(m: *Map<K, V>, key: K, value: V) {
var keyPtr: *K = &key;
let hash: uint = bux_hash_bytes(keyPtr as *void, sizeof(K));
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if m.entries[idx].key == key {
m.entries[idx].value = value;
return;
struct Map<K, V> {
entries: *MapEntry<K, V>,
cap: uint,
len: uint,
}
func Map_New<K, V>(cap: uint) -> Map<K, V> {
let total: uint = cap * sizeof(MapEntry<K, V>);
let data: *MapEntry<K, V> = bux_alloc(total) as *MapEntry<K, V>;
var i: uint = 0;
while i < cap {
data[i].occupied = false;
i = i + 1;
}
idx = (idx + 1) % m.cap;
return Map<K, V> { entries: data, cap: cap, len: 0 };
}
m.entries[idx].key = key;
m.entries[idx].value = value;
m.entries[idx].occupied = true;
m.len = m.len + 1;
}
func Map_Get<K, V>(m: *Map<K, V>, key: K) -> V {
var keyPtr: *K = &key;
let hash: uint = bux_hash_bytes(keyPtr as *void, sizeof(K));
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if m.entries[idx].key == key {
return m.entries[idx].value;
func Map_Set<K, V>(m: *Map<K, V>, key: K, value: V) {
var keyPtr: *K = &key;
let hash: uint = bux_hash_bytes(keyPtr as *void, sizeof(K));
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if m.entries[idx].key == key {
m.entries[idx].value = value;
return;
}
idx = (idx + 1) % m.cap;
}
idx = (idx + 1) % m.cap;
m.entries[idx].key = key;
m.entries[idx].value = value;
m.entries[idx].occupied = true;
m.len = m.len + 1;
}
// Return zero value for missing key
var zero: V = 0 as V;
return zero;
}
func Map_Has<K, V>(m: *Map<K, V>, key: K) -> bool {
var keyPtr: *K = &key;
let hash: uint = bux_hash_bytes(keyPtr as *void, sizeof(K));
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if m.entries[idx].key == key {
return true;
func Map_Get<K, V>(m: *Map<K, V>, key: K) -> V {
var keyPtr: *K = &key;
let hash: uint = bux_hash_bytes(keyPtr as *void, sizeof(K));
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if m.entries[idx].key == key {
return m.entries[idx].value;
}
idx = (idx + 1) % m.cap;
}
idx = (idx + 1) % m.cap;
// Return zero value for missing key
var zero: V = 0 as V;
return zero;
}
return false;
}
func Map_Len<K, V>(m: *Map<K, V>) -> uint {
return m.len;
}
func Map_IsEmpty<K, V>(m: *Map<K, V>) -> bool {
return m.len == 0;
}
/* Remove key if present. Rebuilds the table to keep open-addressing correct. */
func Map_Remove<K, V>(m: *Map<K, V>, key: K) -> bool {
if !Map_Has<K, V>(m, key) {
func Map_Has<K, V>(m: *Map<K, V>, key: K) -> bool {
var keyPtr: *K = &key;
let hash: uint = bux_hash_bytes(keyPtr as *void, sizeof(K));
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if m.entries[idx].key == key {
return true;
}
idx = (idx + 1) % m.cap;
}
return false;
}
var fresh: Map<K, V> = Map_New<K, V>(m.cap);
var i: uint = 0;
while i < m.cap {
if m.entries[i].occupied {
if m.entries[i].key != key {
Map_Set<K, V>(&fresh, m.entries[i].key, m.entries[i].value);
func Map_Len<K, V>(m: *Map<K, V>) -> uint {
return m.len;
}
func Map_IsEmpty<K, V>(m: *Map<K, V>) -> bool {
return m.len == 0;
}
/* Remove key if present. Rebuilds the table to keep open-addressing correct. */
func Map_Remove<K, V>(m: *Map<K, V>, key: K) -> bool {
if !Map_Has<K, V>(m, key) {
return false;
}
var fresh: Map<K, V> = Map_New<K, V>(m.cap);
var i: uint = 0;
while i < m.cap {
if m.entries[i].occupied {
if m.entries[i].key != key {
Map_Set<K, V>(&fresh, m.entries[i].key, m.entries[i].value);
}
}
i = i + 1;
}
i = i + 1;
bux_free(m.entries as *void);
m.entries = fresh.entries;
m.cap = fresh.cap;
m.len = fresh.len;
// Ownership transferred to `m` — clear `fresh` so auto-Drop does not free twice
fresh.entries = null as *MapEntry<K, V>;
fresh.cap = 0;
fresh.len = 0;
return true;
}
bux_free(m.entries as *void);
m.entries = fresh.entries;
m.cap = fresh.cap;
m.len = fresh.len;
return true;
}
func Map_Clear<K, V>(m: *Map<K, V>) {
var i: uint = 0;
while i < m.cap {
m.entries[i].occupied = false;
i = i + 1;
}
m.len = 0;
}
func Map_Free<K, V>(m: *Map<K, V>) {
bux_free(m.entries as *void);
m.entries = null as *MapEntry<K, V>;
m.cap = 0;
m.len = 0;
}
func Map_Drop<K, V>(m: *Map<K, V>) {
Map_Free<K, V>(m);
}
// ---------------------------------------------------------------------------
// StringMap<V> — specialized Map for String keys, using strcmp
// ---------------------------------------------------------------------------
struct StringMapEntry<V> {
key: String,
value: V,
occupied: bool,
}
struct StringMap<V> {
entries: *StringMapEntry<V>,
cap: uint,
len: uint,
}
func StringMap_New<V>(cap: uint) -> StringMap<V> {
let total: uint = cap * sizeof(StringMapEntry<V>);
let data: *StringMapEntry<V> = bux_alloc(total) as *StringMapEntry<V>;
var i: uint = 0;
while i < cap {
data[i].occupied = false;
i = i + 1;
}
return StringMap<V> { entries: data, cap: cap, len: 0 };
}
func StringMap_Set<V>(m: *StringMap<V>, key: String, value: V) {
let hash: uint = bux_hash_string(key);
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if String_Eq(m.entries[idx].key, key) {
m.entries[idx].value = value;
return;
func Map_Clear<K, V>(m: *Map<K, V>) {
var i: uint = 0;
while i < m.cap {
m.entries[i].occupied = false;
i = i + 1;
}
idx = (idx + 1) % m.cap;
m.len = 0;
}
m.entries[idx].key = key;
m.entries[idx].value = value;
m.entries[idx].occupied = true;
m.len = m.len + 1;
}
func StringMap_Get<V>(m: *StringMap<V>, key: String) -> V {
let hash: uint = bux_hash_string(key);
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if String_Eq(m.entries[idx].key, key) {
return m.entries[idx].value;
func Map_Free<K, V>(m: *Map<K, V>) {
bux_free(m.entries as *void);
m.entries = null as *MapEntry<K, V>;
m.cap = 0;
m.len = 0;
}
func Map_Drop<K, V>(m: *Map<K, V>) {
Map_Free<K, V>(m);
}
// ---------------------------------------------------------------------------
// StringMap<V> — specialized Map for String keys, using strcmp
// ---------------------------------------------------------------------------
struct StringMapEntry<V> {
key: String,
value: V,
occupied: bool,
}
struct StringMap<V> {
entries: *StringMapEntry<V>,
cap: uint,
len: uint,
}
func StringMap_New<V>(cap: uint) -> StringMap<V> {
let total: uint = cap * sizeof(StringMapEntry<V>);
let data: *StringMapEntry<V> = bux_alloc(total) as *StringMapEntry<V>;
var i: uint = 0;
while i < cap {
data[i].occupied = false;
i = i + 1;
}
idx = (idx + 1) % m.cap;
return StringMap<V> { entries: data, cap: cap, len: 0 };
}
var zero: V = 0 as V;
return zero;
}
func StringMap_Has<V>(m: *StringMap<V>, key: String) -> bool {
let hash: uint = bux_hash_string(key);
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if String_Eq(m.entries[idx].key, key) {
return true;
func StringMap_Set<V>(m: *StringMap<V>, key: String, value: V) {
let hash: uint = bux_hash_string(key);
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if String_Eq(m.entries[idx].key, key) {
m.entries[idx].value = value;
return;
}
idx = (idx + 1) % m.cap;
}
idx = (idx + 1) % m.cap;
m.entries[idx].key = key;
m.entries[idx].value = value;
m.entries[idx].occupied = true;
m.len = m.len + 1;
}
return false;
}
func StringMap_Len<V>(m: *StringMap<V>) -> uint {
return m.len;
}
func StringMap_Get<V>(m: *StringMap<V>, key: String) -> V {
let hash: uint = bux_hash_string(key);
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if String_Eq(m.entries[idx].key, key) {
return m.entries[idx].value;
}
idx = (idx + 1) % m.cap;
}
var zero: V = 0 as V;
return zero;
}
func StringMap_IsEmpty<V>(m: *StringMap<V>) -> bool {
return m.len == 0;
}
func StringMap_Remove<V>(m: *StringMap<V>, key: String) -> bool {
if !StringMap_Has<V>(m, key) {
func StringMap_Has<V>(m: *StringMap<V>, key: String) -> bool {
let hash: uint = bux_hash_string(key);
var idx: uint = hash % m.cap;
while m.entries[idx].occupied {
if String_Eq(m.entries[idx].key, key) {
return true;
}
idx = (idx + 1) % m.cap;
}
return false;
}
var fresh: StringMap<V> = StringMap_New<V>(m.cap);
var i: uint = 0;
while i < m.cap {
if m.entries[i].occupied {
if !String_Eq(m.entries[i].key, key) {
StringMap_Set<V>(&fresh, m.entries[i].key, m.entries[i].value);
}
func StringMap_Len<V>(m: *StringMap<V>) -> uint {
return m.len;
}
func StringMap_IsEmpty<V>(m: *StringMap<V>) -> bool {
return m.len == 0;
}
func StringMap_Remove<V>(m: *StringMap<V>, key: String) -> bool {
if !StringMap_Has<V>(m, key) {
return false;
}
i = i + 1;
var fresh: StringMap<V> = StringMap_New<V>(m.cap);
var i: uint = 0;
while i < m.cap {
if m.entries[i].occupied {
if !String_Eq(m.entries[i].key, key) {
StringMap_Set<V>(&fresh, m.entries[i].key, m.entries[i].value);
}
}
i = i + 1;
}
bux_free(m.entries as *void);
m.entries = fresh.entries;
m.cap = fresh.cap;
m.len = fresh.len;
// Ownership transferred to `m` — clear `fresh` so auto-Drop does not free twice
fresh.entries = null as *StringMapEntry<V>;
fresh.cap = 0;
fresh.len = 0;
return true;
}
bux_free(m.entries as *void);
m.entries = fresh.entries;
m.cap = fresh.cap;
m.len = fresh.len;
return true;
}
func StringMap_Clear<V>(m: *StringMap<V>) {
var i: uint = 0;
while i < m.cap {
m.entries[i].occupied = false;
i = i + 1;
func StringMap_Clear<V>(m: *StringMap<V>) {
var i: uint = 0;
while i < m.cap {
m.entries[i].occupied = false;
i = i + 1;
}
m.len = 0;
}
m.len = 0;
}
func StringMap_Free<V>(m: *StringMap<V>) {
bux_free(m.entries as *void);
m.entries = null as *StringMapEntry<V>;
m.cap = 0;
m.len = 0;
}
func StringMap_Free<V>(m: *StringMap<V>) {
bux_free(m.entries as *void);
m.entries = null as *StringMapEntry<V>;
m.cap = 0;
m.len = 0;
}
}
}
+40 -40
View File
@@ -1,44 +1,44 @@
module Std::Math {
extern func bux_sqrt(x: float64) -> float64;
extern func bux_pow(x: float64, y: float64) -> float64;
extern func bux_abs_i64(x: int64) -> int64;
extern func bux_abs_f64(x: float64) -> float64;
extern func bux_min_i64(a: int64, b: int64) -> int64;
extern func bux_max_i64(a: int64, b: int64) -> int64;
extern func bux_min_f64(a: float64, b: float64) -> float64;
extern func bux_max_f64(a: float64, b: float64) -> float64;
extern func bux_sqrt(x: float64) -> float64;
extern func bux_pow(x: float64, y: float64) -> float64;
extern func bux_abs_i64(x: int64) -> int64;
extern func bux_abs_f64(x: float64) -> float64;
extern func bux_min_i64(a: int64, b: int64) -> int64;
extern func bux_max_i64(a: int64, b: int64) -> int64;
extern func bux_min_f64(a: float64, b: float64) -> float64;
extern func bux_max_f64(a: float64, b: float64) -> float64;
func Sqrt(x: float64) -> float64 {
return bux_sqrt(x);
}
func Pow(x: float64, y: float64) -> float64 {
return bux_pow(x, y);
}
func Abs(n: int64) -> int64 {
return bux_abs_i64(n);
}
func AbsF(f: float64) -> float64 {
return bux_abs_f64(f);
}
func Min(a: int64, b: int64) -> int64 {
return bux_min_i64(a, b);
}
func Max(a: int64, b: int64) -> int64 {
return bux_max_i64(a, b);
}
func MinF(a: float64, b: float64) -> float64 {
return bux_min_f64(a, b);
}
func MaxF(a: float64, b: float64) -> float64 {
return bux_max_f64(a, b);
}
func Sqrt(x: float64) -> float64 {
return bux_sqrt(x);
}
func Pow(x: float64, y: float64) -> float64 {
return bux_pow(x, y);
}
func Abs(n: int64) -> int64 {
return bux_abs_i64(n);
}
func AbsF(f: float64) -> float64 {
return bux_abs_f64(f);
}
func Min(a: int64, b: int64) -> int64 {
return bux_min_i64(a, b);
}
func Max(a: int64, b: int64) -> int64 {
return bux_max_i64(a, b);
}
func MinF(a: float64, b: float64) -> float64 {
return bux_min_f64(a, b);
}
func MaxF(a: float64, b: float64) -> float64 {
return bux_max_f64(a, b);
}
}
+20 -20
View File
@@ -1,29 +1,29 @@
module Std::Mem {
extern func bux_alloc(size: uint) -> *void;
extern func bux_realloc(ptr: *void, size: uint) -> *void;
extern func bux_free(ptr: *void);
extern func bux_mem_eq(a: *void, b: *void, size: uint) -> int;
extern func bux_alloc(size: uint) -> *void;
extern func bux_realloc(ptr: *void, size: uint) -> *void;
extern func bux_free(ptr: *void);
extern func bux_mem_eq(a: *void, b: *void, size: uint) -> int;
func Alloc(size: uint) -> *void {
return bux_alloc(size);
}
func Alloc(size: uint) -> *void {
return bux_alloc(size);
}
func Realloc(ptr: *void, size: uint) -> *void {
return bux_realloc(ptr, size);
}
func Realloc(ptr: *void, size: uint) -> *void {
return bux_realloc(ptr, size);
}
func Free(ptr: *void) {
bux_free(ptr);
}
func Free(ptr: *void) {
bux_free(ptr);
}
func MemEq(a: *void, b: *void, size: uint) -> bool {
return bux_mem_eq(a, b, size) != 0;
}
func MemEq(a: *void, b: *void, size: uint) -> bool {
return bux_mem_eq(a, b, size) != 0;
}
func New<T>() -> *T {
let sz: uint = sizeof(T);
return bux_alloc(sz) as *T;
}
func New<T>() -> *T {
let sz: uint = sizeof(T);
return bux_alloc(sz) as *T;
}
}
+50 -50
View File
@@ -1,62 +1,62 @@
module Std::Net {
extern func bux_socket_create() -> int;
extern func bux_socket_reuse(fd: int) -> int;
extern func bux_socket_bind(fd: int, addr: String, port: int) -> int;
extern func bux_socket_listen(fd: int, backlog: int) -> int;
extern func bux_socket_accept(fd: int) -> int;
extern func bux_socket_connect(fd: int, addr: String, port: int) -> int;
extern func bux_socket_send(fd: int, data: String, len: int) -> int;
extern func bux_socket_recv(fd: int, maxLen: int) -> String;
extern func bux_socket_close(fd: int) -> int;
extern func bux_socket_error() -> String;
extern func bux_socket_create() -> int;
extern func bux_socket_reuse(fd: int) -> int;
extern func bux_socket_bind(fd: int, addr: String, port: int) -> int;
extern func bux_socket_listen(fd: int, backlog: int) -> int;
extern func bux_socket_accept(fd: int) -> int;
extern func bux_socket_connect(fd: int, addr: String, port: int) -> int;
extern func bux_socket_send(fd: int, data: String, len: int) -> int;
extern func bux_socket_recv(fd: int, maxLen: int) -> String;
extern func bux_socket_close(fd: int) -> int;
extern func bux_socket_error() -> String;
/* Create a TCP socket. Returns -1 on error. */
func Net_Create() -> int {
return bux_socket_create();
}
/* Create a TCP socket. Returns -1 on error. */
func Net_Create() -> int {
return bux_socket_create();
}
/* Enable SO_REUSEADDR on a socket. */
func Net_SetReuse(fd: int) -> bool {
return bux_socket_reuse(fd) == 0;
}
/* Enable SO_REUSEADDR on a socket. */
func Net_SetReuse(fd: int) -> bool {
return bux_socket_reuse(fd) == 0;
}
/* Bind a socket to an address and port. */
func Net_Bind(fd: int, addr: String, port: int) -> bool {
return bux_socket_bind(fd, addr, port) == 0;
}
/* Bind a socket to an address and port. */
func Net_Bind(fd: int, addr: String, port: int) -> bool {
return bux_socket_bind(fd, addr, port) == 0;
}
/* Start listening for connections. */
func Net_Listen(fd: int, backlog: int) -> bool {
return bux_socket_listen(fd, backlog) == 0;
}
/* Start listening for connections. */
func Net_Listen(fd: int, backlog: int) -> bool {
return bux_socket_listen(fd, backlog) == 0;
}
/* Accept a connection. Returns new fd or -1 on error. */
func Net_Accept(fd: int) -> int {
return bux_socket_accept(fd);
}
/* Accept a connection. Returns new fd or -1 on error. */
func Net_Accept(fd: int) -> int {
return bux_socket_accept(fd);
}
/* Connect to a remote address and port. */
func Net_Connect(fd: int, addr: String, port: int) -> bool {
return bux_socket_connect(fd, addr, port) == 0;
}
/* Connect to a remote address and port. */
func Net_Connect(fd: int, addr: String, port: int) -> bool {
return bux_socket_connect(fd, addr, port) == 0;
}
/* Send data. Returns bytes sent or -1 on error. */
func Net_Send(fd: int, data: String) -> int {
return bux_socket_send(fd, data, bux_strlen(data) as int);
}
/* Send data. Returns bytes sent or -1 on error. */
func Net_Send(fd: int, data: String) -> int {
return bux_socket_send(fd, data, bux_strlen(data) as int);
}
/* Receive up to maxLen bytes. Returns empty string on error/EOF. */
func Net_Recv(fd: int, maxLen: int) -> String {
return bux_socket_recv(fd, maxLen);
}
/* Receive up to maxLen bytes. Returns empty string on error/EOF. */
func Net_Recv(fd: int, maxLen: int) -> String {
return bux_socket_recv(fd, maxLen);
}
/* Close a socket. */
func Net_Close(fd: int) -> bool {
return bux_socket_close(fd) == 0;
}
/* Close a socket. */
func Net_Close(fd: int) -> bool {
return bux_socket_close(fd) == 0;
}
/* Get last socket error as a string. */
func Net_LastError() -> String {
return bux_socket_error();
}
/* Get last socket error as a string. */
func Net_LastError() -> String {
return bux_socket_error();
}
}
+52 -52
View File
@@ -1,61 +1,61 @@
module Std::Option {
import Std::Io::{PrintLine};
import Std::Io::{PrintLine};
extern func bux_exit(code: int);
extern func bux_exit(code: int);
enum Option {
Some(int),
None,
}
func Option_NewSome(value: int) -> Option {
let o: Option = Option { tag: Option_Some };
o.data.Some_0 = value;
return o;
}
func Option_NewNone() -> Option {
return Option { tag: Option_None };
}
func Option_IsSome(o: Option) -> bool {
return o.tag == Option_Some;
}
func Option_IsNone(o: Option) -> bool {
return o.tag == Option_None;
}
func Option_Unwrap(o: Option) -> int {
if o.tag != Option_Some {
PrintLine("panic: unwrap on None");
return 0;
enum Option {
Some(int),
None,
}
return o.data.Some_0;
}
func Option_UnwrapOr(o: Option, fallback: int) -> int {
if o.tag == Option_Some {
return o.data.Some_0;
}
return fallback;
}
/* Unwrap Some or panic with a custom message */
func Option_Expect(o: Option, msg: String) -> int {
if o.tag != Option_Some {
PrintLine(msg);
bux_exit(1);
}
return o.data.Some_0;
}
/* If o is Some return it, otherwise return other */
func Option_Or(o: Option, other: Option) -> Option {
if o.tag == Option_Some {
func Option_NewSome(value: int) -> Option {
let o: Option = Option { tag: Option_Some };
o.data.Some_0 = value;
return o;
}
return other;
}
func Option_NewNone() -> Option {
return Option { tag: Option_None };
}
func Option_IsSome(o: Option) -> bool {
return o.tag == Option_Some;
}
func Option_IsNone(o: Option) -> bool {
return o.tag == Option_None;
}
func Option_Unwrap(o: Option) -> int {
if o.tag != Option_Some {
PrintLine("panic: unwrap on None");
return 0;
}
return o.data.Some_0;
}
func Option_UnwrapOr(o: Option, fallback: int) -> int {
if o.tag == Option_Some {
return o.data.Some_0;
}
return fallback;
}
/* Unwrap Some or panic with a custom message */
func Option_Expect(o: Option, msg: String) -> int {
if o.tag != Option_Some {
PrintLine(msg);
bux_exit(1);
}
return o.data.Some_0;
}
/* If o is Some return it, otherwise return other */
func Option_Or(o: Option, other: Option) -> Option {
if o.tag == Option_Some {
return o;
}
return other;
}
}
+29 -29
View File
@@ -1,40 +1,40 @@
module Std::Os {
extern func bux_argc() -> int;
extern func bux_argv(index: int) -> String;
extern func bux_getenv(name: String) -> String;
extern func bux_setenv(name: String, value: String) -> int;
extern func bux_getcwd() -> String;
extern func bux_chdir(path: String) -> int;
extern func bux_exit(code: int);
extern func bux_argc() -> int;
extern func bux_argv(index: int) -> String;
extern func bux_getenv(name: String) -> String;
extern func bux_setenv(name: String, value: String) -> int;
extern func bux_getcwd() -> String;
extern func bux_chdir(path: String) -> int;
extern func bux_exit(code: int);
func Os_ArgsCount() -> int {
return bux_argc();
}
func Os_ArgsCount() -> int {
return bux_argc();
}
func Os_Args(index: int) -> String {
return bux_argv(index);
}
func Os_Args(index: int) -> String {
return bux_argv(index);
}
func Os_GetEnv(name: String) -> String {
return bux_getenv(name);
}
func Os_GetEnv(name: String) -> String {
return bux_getenv(name);
}
func Os_SetEnv(name: String, value: String) -> bool {
return bux_setenv(name, value) == 0;
}
func Os_SetEnv(name: String, value: String) -> bool {
return bux_setenv(name, value) == 0;
}
func Os_GetCwd() -> String {
return bux_getcwd();
}
func Os_GetCwd() -> String {
return bux_getcwd();
}
func Os_Chdir(path: String) -> bool {
return bux_chdir(path) == 0;
}
func Os_Chdir(path: String) -> bool {
return bux_chdir(path) == 0;
}
/* Terminate the process with the given exit code */
func Os_Exit(code: int) {
bux_exit(code);
}
/* Terminate the process with the given exit code */
func Os_Exit(code: int) {
bux_exit(code);
}
}
+15 -15
View File
@@ -1,19 +1,19 @@
module Std::Path {
extern func bux_path_join(a: String, b: String) -> String;
extern func bux_path_parent(path: String) -> String;
extern func bux_path_ext(path: String) -> String;
extern func bux_path_join(a: String, b: String) -> String;
extern func bux_path_parent(path: String) -> String;
extern func bux_path_ext(path: String) -> String;
func Path_Join(a: String, b: String) -> String {
return bux_path_join(a, b);
}
func Path_Parent(path: String) -> String {
return bux_path_parent(path);
}
func Path_Ext(path: String) -> String {
return bux_path_ext(path);
}
func Path_Join(a: String, b: String) -> String {
return bux_path_join(a, b);
}
func Path_Parent(path: String) -> String {
return bux_path_parent(path);
}
func Path_Ext(path: String) -> String {
return bux_path_ext(path);
}
}
+8 -8
View File
@@ -1,14 +1,14 @@
module Std::Process {
extern func bux_process_run(cmd: String) -> int;
extern func bux_process_output(cmd: String) -> String;
extern func bux_process_run(cmd: String) -> int;
extern func bux_process_output(cmd: String) -> String;
func Process_Run(cmd: String) -> int {
return bux_process_run(cmd);
}
func Process_Run(cmd: String) -> int {
return bux_process_run(cmd);
}
func Process_Output(cmd: String) -> String {
return bux_process_output(cmd);
}
func Process_Output(cmd: String) -> String {
return bux_process_output(cmd);
}
}
+63 -63
View File
@@ -1,72 +1,72 @@
module Std::Result {
import Std::Io::{PrintLine};
import Std::Io::{PrintLine};
extern func bux_exit(code: int);
extern func bux_exit(code: int);
enum Result {
Ok(int),
Err(String),
}
func Result_NewOk(value: int) -> Result {
let r: Result = Result { tag: Result_Ok };
r.data.Ok_0 = value;
return r;
}
func Result_NewErr(msg: String) -> Result {
let r: Result = Result { tag: Result_Err };
r.data.Err_0 = msg;
return r;
}
func Result_IsOk(r: Result) -> bool {
return r.tag == Result_Ok;
}
func Result_IsErr(r: Result) -> bool {
return r.tag == Result_Err;
}
func Result_Unwrap(r: Result) -> int {
if r.tag != Result_Ok {
PrintLine("panic: unwrap on Err");
return 0;
enum Result {
Ok(int),
Err(String),
}
return r.data.Ok_0;
}
func Result_UnwrapOr(r: Result, fallback: int) -> int {
if r.tag == Result_Ok {
return r.data.Ok_0;
}
return fallback;
}
/* Unwrap Ok or panic with a custom message */
func Result_Expect(r: Result, msg: String) -> int {
if r.tag != Result_Ok {
PrintLine(msg);
bux_exit(1);
}
return r.data.Ok_0;
}
/* Extract Err payload (panics if Ok) */
func Result_UnwrapErr(r: Result) -> String {
if r.tag != Result_Err {
PrintLine("panic: unwrap_err on Ok");
return "";
}
return r.data.Err_0;
}
/* If r is Ok return it, otherwise return other */
func Result_Or(r: Result, other: Result) -> Result {
if r.tag == Result_Ok {
func Result_NewOk(value: int) -> Result {
let r: Result = Result { tag: Result_Ok };
r.data.Ok_0 = value;
return r;
}
return other;
}
func Result_NewErr(msg: String) -> Result {
let r: Result = Result { tag: Result_Err };
r.data.Err_0 = msg;
return r;
}
func Result_IsOk(r: Result) -> bool {
return r.tag == Result_Ok;
}
func Result_IsErr(r: Result) -> bool {
return r.tag == Result_Err;
}
func Result_Unwrap(r: Result) -> int {
if r.tag != Result_Ok {
PrintLine("panic: unwrap on Err");
return 0;
}
return r.data.Ok_0;
}
func Result_UnwrapOr(r: Result, fallback: int) -> int {
if r.tag == Result_Ok {
return r.data.Ok_0;
}
return fallback;
}
/* Unwrap Ok or panic with a custom message */
func Result_Expect(r: Result, msg: String) -> int {
if r.tag != Result_Ok {
PrintLine(msg);
bux_exit(1);
}
return r.data.Ok_0;
}
/* Extract Err payload (panics if Ok) */
func Result_UnwrapErr(r: Result) -> String {
if r.tag != Result_Err {
PrintLine("panic: unwrap_err on Ok");
return "";
}
return r.data.Err_0;
}
/* If r is Ok return it, otherwise return other */
func Result_Or(r: Result, other: Result) -> Result {
if r.tag == Result_Ok {
return r;
}
return other;
}
}
+95 -91
View File
@@ -1,112 +1,116 @@
module Std::Set {
extern func bux_hash_bytes(ptr: *void, size: uint) -> uint;
extern func bux_mem_eq(a: *void, b: *void, size: uint) -> int;
extern func bux_alloc(size: uint) -> *void;
extern func bux_free(ptr: *void);
extern func bux_hash_bytes(ptr: *void, size: uint) -> uint;
extern func bux_mem_eq(a: *void, b: *void, size: uint) -> int;
extern func bux_alloc(size: uint) -> *void;
extern func bux_free(ptr: *void);
struct SetEntry<T> {
value: T,
occupied: bool,
}
struct Set<T> {
entries: *SetEntry<T>,
cap: uint,
len: uint,
}
func Set_New<T>(cap: uint) -> Set<T> {
let total: uint = cap * sizeof(SetEntry<T>);
let data: *SetEntry<T> = bux_alloc(total) as *SetEntry<T>;
var i: uint = 0;
while i < cap {
data[i].occupied = false;
i = i + 1;
struct SetEntry<T> {
value: T,
occupied: bool,
}
return Set<T> { entries: data, cap: cap, len: 0 };
}
func Set_Add<T>(s: *Set<T>, value: T) {
var valuePtr: *T = &value;
let hash: uint = bux_hash_bytes(valuePtr as *void, sizeof(T));
var idx: uint = hash % s.cap;
while s.entries[idx].occupied {
var entryPtr: *T = &s.entries[idx].value;
if bux_mem_eq(entryPtr as *void, valuePtr as *void, sizeof(T)) != 0 {
return;
struct Set<T> {
entries: *SetEntry<T>,
cap: uint,
len: uint,
}
func Set_New<T>(cap: uint) -> Set<T> {
let total: uint = cap * sizeof(SetEntry<T>);
let data: *SetEntry<T> = bux_alloc(total) as *SetEntry<T>;
var i: uint = 0;
while i < cap {
data[i].occupied = false;
i = i + 1;
}
idx = (idx + 1) % s.cap;
return Set<T> { entries: data, cap: cap, len: 0 };
}
s.entries[idx].value = value;
s.entries[idx].occupied = true;
s.len = s.len + 1;
}
func Set_Has<T>(s: *Set<T>, value: T) -> bool {
var valuePtr: *T = &value;
let hash: uint = bux_hash_bytes(valuePtr as *void, sizeof(T));
var idx: uint = hash % s.cap;
while s.entries[idx].occupied {
var entryPtr: *T = &s.entries[idx].value;
if bux_mem_eq(entryPtr as *void, valuePtr as *void, sizeof(T)) != 0 {
return true;
func Set_Add<T>(s: *Set<T>, value: T) {
var valuePtr: *T = &value;
let hash: uint = bux_hash_bytes(valuePtr as *void, sizeof(T));
var idx: uint = hash % s.cap;
while s.entries[idx].occupied {
var entryPtr: *T = &s.entries[idx].value;
if bux_mem_eq(entryPtr as *void, valuePtr as *void, sizeof(T)) != 0 {
return;
}
idx = (idx + 1) % s.cap;
}
idx = (idx + 1) % s.cap;
s.entries[idx].value = value;
s.entries[idx].occupied = true;
s.len = s.len + 1;
}
return false;
}
func Set_Len<T>(s: *Set<T>) -> uint {
return s.len;
}
func Set_IsEmpty<T>(s: *Set<T>) -> bool {
return s.len == 0;
}
/* Remove value if present. Rebuilds the table to keep open-addressing correct. */
func Set_Remove<T>(s: *Set<T>, value: T) -> bool {
if !Set_Has<T>(s, value) {
func Set_Has<T>(s: *Set<T>, value: T) -> bool {
var valuePtr: *T = &value;
let hash: uint = bux_hash_bytes(valuePtr as *void, sizeof(T));
var idx: uint = hash % s.cap;
while s.entries[idx].occupied {
var entryPtr: *T = &s.entries[idx].value;
if bux_mem_eq(entryPtr as *void, valuePtr as *void, sizeof(T)) != 0 {
return true;
}
idx = (idx + 1) % s.cap;
}
return false;
}
var fresh: Set<T> = Set_New<T>(s.cap);
var i: uint = 0;
while i < s.cap {
if s.entries[i].occupied {
var entryPtr: *T = &s.entries[i].value;
var valuePtr: *T = &value;
if bux_mem_eq(entryPtr as *void, valuePtr as *void, sizeof(T)) == 0 {
Set_Add<T>(&fresh, s.entries[i].value);
}
func Set_Len<T>(s: *Set<T>) -> uint {
return s.len;
}
func Set_IsEmpty<T>(s: *Set<T>) -> bool {
return s.len == 0;
}
/* Remove value if present. Rebuilds the table to keep open-addressing correct. */
func Set_Remove<T>(s: *Set<T>, value: T) -> bool {
if !Set_Has<T>(s, value) {
return false;
}
i = i + 1;
var fresh: Set<T> = Set_New<T>(s.cap);
var i: uint = 0;
while i < s.cap {
if s.entries[i].occupied {
var entryPtr: *T = &s.entries[i].value;
var valuePtr: *T = &value;
if bux_mem_eq(entryPtr as *void, valuePtr as *void, sizeof(T)) == 0 {
Set_Add<T>(&fresh, s.entries[i].value);
}
}
i = i + 1;
}
bux_free(s.entries as *void);
s.entries = fresh.entries;
s.cap = fresh.cap;
s.len = fresh.len;
// Ownership transferred to `s` — clear `fresh` so auto-Drop does not free twice
fresh.entries = null as *SetEntry<T>;
fresh.cap = 0;
fresh.len = 0;
return true;
}
bux_free(s.entries as *void);
s.entries = fresh.entries;
s.cap = fresh.cap;
s.len = fresh.len;
return true;
}
func Set_Clear<T>(s: *Set<T>) {
var i: uint = 0;
while i < s.cap {
s.entries[i].occupied = false;
i = i + 1;
func Set_Clear<T>(s: *Set<T>) {
var i: uint = 0;
while i < s.cap {
s.entries[i].occupied = false;
i = i + 1;
}
s.len = 0;
}
s.len = 0;
}
func Set_Free<T>(s: *Set<T>) {
bux_free(s.entries as *void);
s.entries = null as *SetEntry<T>;
s.cap = 0;
s.len = 0;
}
func Set_Free<T>(s: *Set<T>) {
bux_free(s.entries as *void);
s.entries = null as *SetEntry<T>;
s.cap = 0;
s.len = 0;
}
func Set_Drop<T>(s: *Set<T>) {
Set_Free<T>(s);
}
func Set_Drop<T>(s: *Set<T>) {
Set_Free<T>(s);
}
}
+28 -28
View File
@@ -1,39 +1,39 @@
module Std::Slice {
extern func bux_bounds_check(index: uint, len: uint);
extern func bux_bounds_check(index: uint, len: uint);
struct Slice<T> {
data: *T,
len: uint,
}
struct Slice<T> {
data: *T,
len: uint,
}
func Slice_FromArray<T>(arr: *Array<T>) -> Slice<T> {
var s: Slice<T>;
s.data = arr.data;
s.len = arr.len;
return s;
}
func Slice_FromArray<T>(arr: *Array<T>) -> Slice<T> {
var s: Slice<T>;
s.data = arr.data;
s.len = arr.len;
return s;
}
func Slice_Get<T>(self: *Slice<T>, idx: uint) -> T {
bux_bounds_check(idx, self.len);
return self.data[idx];
}
func Slice_Get<T>(self: *Slice<T>, idx: uint) -> T {
bux_bounds_check(idx, self.len);
return self.data[idx];
}
func Slice_Set<T>(self: *Slice<T>, idx: uint, value: T) {
bux_bounds_check(idx, self.len);
self.data[idx] = value;
}
func Slice_Set<T>(self: *Slice<T>, idx: uint, value: T) {
bux_bounds_check(idx, self.len);
self.data[idx] = value;
}
func Slice_Len<T>(self: *Slice<T>) -> uint {
return self.len;
}
func Slice_Len<T>(self: *Slice<T>) -> uint {
return self.len;
}
func Slice_operator_index_get<T>(self: *Slice<T>, idx: uint) -> T {
return Slice_Get<T>(self, idx);
}
func Slice_operator_index_get<T>(self: *Slice<T>, idx: uint) -> T {
return Slice_Get<T>(self, idx);
}
func Slice_operator_index_set<T>(self: *Slice<T>, idx: uint, value: T) {
Slice_Set<T>(self, idx, value);
}
func Slice_operator_index_set<T>(self: *Slice<T>, idx: uint, value: T) {
Slice_Set<T>(self, idx, value);
}
}
+270 -261
View File
@@ -1,288 +1,297 @@
module Std::String {
extern func bux_strlen(s: String) -> uint;
extern func bux_strcmp(a: String, b: String) -> int;
extern func bux_strncmp(a: String, b: String, n: uint) -> int;
extern func bux_strcpy(dest: *char8, src: String) -> *char8;
extern func bux_strcat(dest: *char8, src: String) -> *char8;
extern func bux_strncpy(dest: *char8, src: String, n: uint) -> *char8;
extern func bux_strstr(haystack: String, needle: String) -> String;
extern func bux_str_contains(haystack: String, needle: String) -> int;
extern func bux_str_offset(pos: String, base: String) -> uint;
extern func bux_str_is_null(s: String) -> int;
extern func bux_str_slice(s: String, start: uint, len: uint) -> String;
extern func bux_str_trim_left(s: String) -> String;
extern func bux_str_trim_right(s: String) -> String;
extern func bux_str_trim(s: String) -> String;
extern func bux_int_to_str(n: int64) -> String;
extern func bux_str_to_int(s: String) -> int64;
extern func bux_sb_new(initial_cap: uint) -> *void;
extern func bux_sb_append(sb: *void, s: String);
extern func bux_sb_append_int(sb: *void, n: int64);
extern func bux_sb_append_float(sb: *void, f: float64);
extern func bux_sb_append_char(sb: *void, c: char8);
extern func bux_sb_build(sb: *void) -> String;
extern func bux_sb_free(sb: *void);
extern func bux_str_split_count(s: String, delim: String) -> uint;
extern func bux_str_split_part(s: String, delim: String, index: uint) -> String;
extern func bux_str_join2(a: String, b: String, sep: String) -> String;
extern func bux_float_to_string(f: float64) -> String;
extern func bux_str_format(pattern: String, a0: String, a1: String, a2: String, a3: String, a4: String, a5: String, a6: String, a7: String) -> String;
extern func bux_strlen(s: String) -> uint;
extern func bux_strcmp(a: String, b: String) -> int;
extern func bux_strncmp(a: String, b: String, n: uint) -> int;
extern func bux_strcpy(dest: *char8, src: String) -> *char8;
extern func bux_strcat(dest: *char8, src: String) -> *char8;
extern func bux_strncpy(dest: *char8, src: String, n: uint) -> *char8;
extern func bux_strstr(haystack: String, needle: String) -> String;
extern func bux_str_contains(haystack: String, needle: String) -> int;
extern func bux_str_offset(pos: String, base: String) -> uint;
extern func bux_str_is_null(s: String) -> int;
extern func bux_str_slice(s: String, start: uint, len: uint) -> String;
extern func bux_str_trim_left(s: String) -> String;
extern func bux_str_trim_right(s: String) -> String;
extern func bux_str_trim(s: String) -> String;
extern func bux_int_to_str(n: int64) -> String;
extern func bux_str_to_int(s: String) -> int64;
extern func bux_sb_new(initial_cap: uint) -> *void;
extern func bux_sb_append(sb: *void, s: String);
extern func bux_sb_append_int(sb: *void, n: int64);
extern func bux_sb_append_float(sb: *void, f: float64);
extern func bux_sb_append_char(sb: *void, c: char8);
extern func bux_sb_build(sb: *void) -> String;
extern func bux_sb_free(sb: *void);
extern func bux_str_split_count(s: String, delim: String) -> uint;
extern func bux_str_split_part(s: String, delim: String, index: uint) -> String;
extern func bux_str_join2(a: String, b: String, sep: String) -> String;
extern func bux_float_to_string(f: float64) -> String;
extern func bux_str_format(pattern: String, a0: String, a1: String, a2: String, a3: String, a4: String, a5: String, a6: String, a7: String) -> String;
func String_Len(s: String) -> uint {
return bux_strlen(s);
}
func String_IsEmpty(s: String) -> bool {
return bux_strlen(s) == 0;
}
func String_IsNull(s: String) -> bool {
return bux_str_is_null(s) != 0;
}
func String_Eq(a: String, b: String) -> bool {
return bux_strcmp(a, b) == 0;
}
func String_Concat(a: String, b: String) -> String {
let len_a: uint = bux_strlen(a);
let len_b: uint = bux_strlen(b);
let total: uint = len_a + len_b + 1;
let buf: *char8 = bux_alloc(total) as *char8;
bux_strcpy(buf, a);
bux_strcat(buf, b);
return buf;
}
func String_Copy(s: String) -> String {
let len: uint = bux_strlen(s);
let buf: *char8 = bux_alloc(len + 1) as *char8;
bux_strcpy(buf, s);
return buf;
}
func String_StartsWith(s: String, prefix: String) -> bool {
let s_len: uint = bux_strlen(s);
let p_len: uint = bux_strlen(prefix);
if p_len > s_len {
return false;
/// Byte length of a C string (`strlen`).
func String_Len(s: String) -> uint {
return bux_strlen(s);
}
let r: int = bux_strncmp(s, prefix, p_len);
return r == 0;
}
func String_EndsWith(s: String, suffix: String) -> bool {
let s_len: uint = bux_strlen(s);
let suf_len: uint = bux_strlen(suffix);
if suf_len > s_len {
return false;
/// True if the string has zero length.
func String_IsEmpty(s: String) -> bool {
return bux_strlen(s) == 0;
}
let start: uint = s_len - suf_len;
let tail: String = bux_str_slice(s, start, suf_len);
let eq: bool = bux_strcmp(tail, suffix) == 0;
return eq;
}
func String_Contains(s: String, substr: String) -> bool {
let r: int = bux_str_contains(s, substr);
return r != 0;
}
/// True if the pointer is null.
func String_IsNull(s: String) -> bool {
return bux_str_is_null(s) != 0;
}
func String_Slice(s: String, start: uint, len: uint) -> String {
return bux_str_slice(s, start, len);
}
/// Lexicographic equality.
func String_Eq(a: String, b: String) -> bool {
return bux_strcmp(a, b) == 0;
}
func String_Trim(s: String) -> String {
return bux_str_trim(s);
}
/// Allocate and return `a` concatenated with `b`.
func String_Concat(a: String, b: String) -> String {
let len_a: uint = bux_strlen(a);
let len_b: uint = bux_strlen(b);
let total: uint = len_a + len_b + 1;
let buf: *char8 = bux_alloc(total) as *char8;
bux_strcpy(buf, a);
bux_strcat(buf, b);
return buf;
}
func String_TrimLeft(s: String) -> String {
return bux_str_trim_left(s);
}
/// Heap-copy of `s`.
func String_Copy(s: String) -> String {
let len: uint = bux_strlen(s);
let buf: *char8 = bux_alloc(len + 1) as *char8;
bux_strcpy(buf, s);
return buf;
}
func String_TrimRight(s: String) -> String {
return bux_str_trim_right(s);
}
func String_FromInt(n: int64) -> String {
return bux_int_to_str(n);
}
func String_ToInt(s: String) -> int64 {
return bux_str_to_int(s);
}
// String Builder — efficient string construction
struct StringBuilder {
handle: *void,
}
func StringBuilder_New() -> StringBuilder {
return StringBuilder { handle: bux_sb_new(64) };
}
func StringBuilder_NewCap(cap: uint) -> StringBuilder {
return StringBuilder { handle: bux_sb_new(cap) };
}
func StringBuilder_Append(sb: *StringBuilder, s: String) {
bux_sb_append(sb.handle, s);
}
func StringBuilder_AppendInt(sb: *StringBuilder, n: int64) {
bux_sb_append_int(sb.handle, n);
}
func StringBuilder_AppendFloat(sb: *StringBuilder, f: float64) {
bux_sb_append_float(sb.handle, f);
}
func StringBuilder_AppendChar(sb: *StringBuilder, c: char8) {
bux_sb_append_char(sb.handle, c);
}
func StringBuilder_Build(sb: *StringBuilder) -> String {
return bux_sb_build(sb.handle);
}
func StringBuilder_Free(sb: *StringBuilder) {
bux_sb_free(sb.handle);
}
/* True if empty or only whitespace (space, tab, CR, LF) */
func String_IsBlank(s: String) -> bool {
let n: uint = bux_strlen(s);
var i: uint = 0;
while i < n {
let ch: String = bux_str_slice(s, i, 1);
if !(String_Eq(ch, " ") || String_Eq(ch, "\t") || String_Eq(ch, "\n") || String_Eq(ch, "\r")) {
/// True if `s` begins with `prefix`.
func String_StartsWith(s: String, prefix: String) -> bool {
let s_len: uint = bux_strlen(s);
let p_len: uint = bux_strlen(prefix);
if p_len > s_len {
return false;
}
i = i + 1;
let r: int = bux_strncmp(s, prefix, p_len);
return r == 0;
}
return true;
}
/* Repeat s, count times (count==0 → empty string) */
func String_Repeat(s: String, count: uint) -> String {
if count == 0 {
return "";
}
if count == 1 {
return s;
}
var sb: StringBuilder = StringBuilder_New();
var i: uint = 0;
while i < count {
StringBuilder_Append(&sb, s);
i = i + 1;
}
let result: String = StringBuilder_Build(&sb);
StringBuilder_Free(&sb);
return result;
}
// ---------------------------------------------------------------------------
// String split/join
// ---------------------------------------------------------------------------
func String_SplitCount(s: String, delim: String) -> uint {
return bux_str_split_count(s, delim);
}
func String_SplitPart(s: String, delim: String, index: uint) -> String {
return bux_str_split_part(s, delim, index);
}
func String_Join2(a: String, b: String, sep: String) -> String {
return bux_str_join2(a, b, sep);
}
// ---------------------------------------------------------------------------
// String find/replace/format
// ---------------------------------------------------------------------------
// String_Chars — return single-character string at index (for iteration)
func String_Chars(s: String, index: uint) -> String {
return bux_str_slice(s, index, 1);
}
func String_Find(haystack: String, needle: String) -> String {
return bux_strstr(haystack, needle);
}
func String_Offset(pos: String, base: String) -> uint {
return bux_str_offset(pos, base);
}
func String_Replace(s: String, old: String, new: String) -> String {
let pos: String = bux_strstr(s, old);
if String_IsNull(pos) {
return s;
}
let oldLen: uint = bux_strlen(old);
let prefixLen: uint = String_Offset(pos, s);
let prefix: String = bux_str_slice(s, 0, prefixLen);
let suffix: String = bux_str_slice(s, prefixLen + oldLen, bux_strlen(s) - prefixLen - oldLen);
let temp: String = String_Concat(prefix, new);
let result: String = String_Concat(temp, suffix);
return result;
}
/* Replace every non-overlapping occurrence of old with new.
Empty old is a no-op (returns s unchanged). Safe if new contains old. */
func String_ReplaceAll(s: String, old: String, new: String) -> String {
let oldLen: uint = bux_strlen(old);
if oldLen == 0 {
return s;
}
var sb: StringBuilder = StringBuilder_New();
var remaining: String = s;
while true {
let pos: String = bux_strstr(remaining, old);
if String_IsNull(pos) {
StringBuilder_Append(&sb, remaining);
break;
/// True if `s` ends with `suffix`.
func String_EndsWith(s: String, suffix: String) -> bool {
let s_len: uint = bux_strlen(s);
let suf_len: uint = bux_strlen(suffix);
if suf_len > s_len {
return false;
}
let prefixLen: uint = String_Offset(pos, remaining);
let prefix: String = bux_str_slice(remaining, 0, prefixLen);
StringBuilder_Append(&sb, prefix);
StringBuilder_Append(&sb, new);
let remLen: uint = bux_strlen(remaining);
remaining = bux_str_slice(remaining, prefixLen + oldLen, remLen - prefixLen - oldLen);
let start: uint = s_len - suf_len;
let tail: String = bux_str_slice(s, start, suf_len);
let eq: bool = bux_strcmp(tail, suffix) == 0;
return eq;
}
let result: String = StringBuilder_Build(&sb);
StringBuilder_Free(&sb);
return result;
}
extern func bux_str_to_float(s: String) -> float64;
/// True if `substr` occurs anywhere in `s`.
func String_Contains(s: String, substr: String) -> bool {
let r: int = bux_str_contains(s, substr);
return r != 0;
}
func String_ToFloat(s: String) -> float64 {
return bux_str_to_float(s);
}
func String_Slice(s: String, start: uint, len: uint) -> String {
return bux_str_slice(s, start, len);
}
func String_FromBool(b: bool) -> String {
if b { return "true"; }
return "false";
}
func String_Trim(s: String) -> String {
return bux_str_trim(s);
}
func String_FromFloat(f: float64) -> String {
return bux_float_to_string(f);
}
func String_TrimLeft(s: String) -> String {
return bux_str_trim_left(s);
}
func String_Format1(pattern: String, a0: String) -> String {
return bux_str_format(pattern, a0, "", "", "", "", "", "", "");
}
func String_TrimRight(s: String) -> String {
return bux_str_trim_right(s);
}
func String_Format2(pattern: String, a0: String, a1: String) -> String {
return bux_str_format(pattern, a0, a1, "", "", "", "", "", "");
}
func String_FromInt(n: int64) -> String {
return bux_int_to_str(n);
}
func String_Format3(pattern: String, a0: String, a1: String, a2: String) -> String {
return bux_str_format(pattern, a0, a1, a2, "", "", "", "", "");
}
func String_ToInt(s: String) -> int64 {
return bux_str_to_int(s);
}
// String Builder — efficient string construction
struct StringBuilder {
handle: *void,
}
func StringBuilder_New() -> StringBuilder {
return StringBuilder { handle: bux_sb_new(64) };
}
func StringBuilder_NewCap(cap: uint) -> StringBuilder {
return StringBuilder { handle: bux_sb_new(cap) };
}
func StringBuilder_Append(sb: *StringBuilder, s: String) {
bux_sb_append(sb.handle, s);
}
func StringBuilder_AppendInt(sb: *StringBuilder, n: int64) {
bux_sb_append_int(sb.handle, n);
}
func StringBuilder_AppendFloat(sb: *StringBuilder, f: float64) {
bux_sb_append_float(sb.handle, f);
}
func StringBuilder_AppendChar(sb: *StringBuilder, c: char8) {
bux_sb_append_char(sb.handle, c);
}
func StringBuilder_Build(sb: *StringBuilder) -> String {
return bux_sb_build(sb.handle);
}
func StringBuilder_Free(sb: *StringBuilder) {
bux_sb_free(sb.handle);
}
/// True if empty or only whitespace (space, tab, CR, LF).
func String_IsBlank(s: String) -> bool {
let n: uint = bux_strlen(s);
var i: uint = 0;
while i < n {
let ch: String = bux_str_slice(s, i, 1);
if !(String_Eq(ch, " ") || String_Eq(ch, "\t") || String_Eq(ch, "\n") || String_Eq(ch, "\r")) {
return false;
}
i = i + 1;
}
return true;
}
/// Repeat `s`, `count` times (`count == 0` → empty string).
func String_Repeat(s: String, count: uint) -> String {
if count == 0 {
return "";
}
if count == 1 {
return s;
}
var sb: StringBuilder = StringBuilder_New();
var i: uint = 0;
while i < count {
StringBuilder_Append(&sb, s);
i = i + 1;
}
let result: String = StringBuilder_Build(&sb);
StringBuilder_Free(&sb);
return result;
}
// ---------------------------------------------------------------------------
// String split/join
// ---------------------------------------------------------------------------
func String_SplitCount(s: String, delim: String) -> uint {
return bux_str_split_count(s, delim);
}
func String_SplitPart(s: String, delim: String, index: uint) -> String {
return bux_str_split_part(s, delim, index);
}
func String_Join2(a: String, b: String, sep: String) -> String {
return bux_str_join2(a, b, sep);
}
// ---------------------------------------------------------------------------
// String find/replace/format
// ---------------------------------------------------------------------------
// String_Chars — return single-character string at index (for iteration)
func String_Chars(s: String, index: uint) -> String {
return bux_str_slice(s, index, 1);
}
func String_Find(haystack: String, needle: String) -> String {
return bux_strstr(haystack, needle);
}
func String_Offset(pos: String, base: String) -> uint {
return bux_str_offset(pos, base);
}
func String_Replace(s: String, old: String, new: String) -> String {
let pos: String = bux_strstr(s, old);
if String_IsNull(pos) {
return s;
}
let oldLen: uint = bux_strlen(old);
let prefixLen: uint = String_Offset(pos, s);
let prefix: String = bux_str_slice(s, 0, prefixLen);
let suffix: String = bux_str_slice(s, prefixLen + oldLen, bux_strlen(s) - prefixLen - oldLen);
let temp: String = String_Concat(prefix, new);
let result: String = String_Concat(temp, suffix);
return result;
}
/// Replace every non-overlapping occurrence of `old` with `new`.
/// Empty `old` is a no-op (returns `s` unchanged). Safe if `new` contains `old`.
func String_ReplaceAll(s: String, old: String, new: String) -> String {
let oldLen: uint = bux_strlen(old);
if oldLen == 0 {
return s;
}
var sb: StringBuilder = StringBuilder_New();
var remaining: String = s;
while true {
let pos: String = bux_strstr(remaining, old);
if String_IsNull(pos) {
StringBuilder_Append(&sb, remaining);
break;
}
let prefixLen: uint = String_Offset(pos, remaining);
let prefix: String = bux_str_slice(remaining, 0, prefixLen);
StringBuilder_Append(&sb, prefix);
StringBuilder_Append(&sb, new);
let remLen: uint = bux_strlen(remaining);
remaining = bux_str_slice(remaining, prefixLen + oldLen, remLen - prefixLen - oldLen);
}
let result: String = StringBuilder_Build(&sb);
StringBuilder_Free(&sb);
return result;
}
extern func bux_str_to_float(s: String) -> float64;
func String_ToFloat(s: String) -> float64 {
return bux_str_to_float(s);
}
func String_FromBool(b: bool) -> String {
if b { return "true"; }
return "false";
}
func String_FromFloat(f: float64) -> String {
return bux_float_to_string(f);
}
func String_Format1(pattern: String, a0: String) -> String {
return bux_str_format(pattern, a0, "", "", "", "", "", "", "");
}
func String_Format2(pattern: String, a0: String, a1: String) -> String {
return bux_str_format(pattern, a0, a1, "", "", "", "", "", "");
}
func String_Format3(pattern: String, a0: String, a1: String, a2: String) -> String {
return bux_str_format(pattern, a0, a1, a2, "", "", "", "", "");
}
}
+42 -42
View File
@@ -1,58 +1,58 @@
module Std::Sync {
extern func bux_mutex_new() -> *void;
extern func bux_mutex_lock(handle: *void);
extern func bux_mutex_unlock(handle: *void);
extern func bux_mutex_free(handle: *void);
extern func bux_mutex_new() -> *void;
extern func bux_mutex_lock(handle: *void);
extern func bux_mutex_unlock(handle: *void);
extern func bux_mutex_free(handle: *void);
extern func bux_rwlock_new() -> *void;
extern func bux_rwlock_rdlock(handle: *void);
extern func bux_rwlock_wrlock(handle: *void);
extern func bux_rwlock_unlock(handle: *void);
extern func bux_rwlock_free(handle: *void);
extern func bux_rwlock_new() -> *void;
extern func bux_rwlock_rdlock(handle: *void);
extern func bux_rwlock_wrlock(handle: *void);
extern func bux_rwlock_unlock(handle: *void);
extern func bux_rwlock_free(handle: *void);
struct Mutex {
handle: *void;
}
struct Mutex {
handle: *void;
}
struct RwLock {
handle: *void;
}
struct RwLock {
handle: *void;
}
func Mutex_New() -> Mutex {
return Mutex { handle: bux_mutex_new() };
}
func Mutex_New() -> Mutex {
return Mutex { handle: bux_mutex_new() };
}
func Mutex_Lock(m: *Mutex) {
bux_mutex_lock(m.handle);
}
func Mutex_Lock(m: *Mutex) {
bux_mutex_lock(m.handle);
}
func Mutex_Unlock(m: *Mutex) {
bux_mutex_unlock(m.handle);
}
func Mutex_Unlock(m: *Mutex) {
bux_mutex_unlock(m.handle);
}
func Mutex_Free(m: *Mutex) {
bux_mutex_free(m.handle);
}
func Mutex_Free(m: *Mutex) {
bux_mutex_free(m.handle);
}
func RwLock_New() -> RwLock {
return RwLock { handle: bux_rwlock_new() };
}
func RwLock_New() -> RwLock {
return RwLock { handle: bux_rwlock_new() };
}
func RwLock_ReadLock(rw: *RwLock) {
bux_rwlock_rdlock(rw.handle);
}
func RwLock_ReadLock(rw: *RwLock) {
bux_rwlock_rdlock(rw.handle);
}
func RwLock_WriteLock(rw: *RwLock) {
bux_rwlock_wrlock(rw.handle);
}
func RwLock_WriteLock(rw: *RwLock) {
bux_rwlock_wrlock(rw.handle);
}
func RwLock_Unlock(rw: *RwLock) {
bux_rwlock_unlock(rw.handle);
}
func RwLock_Unlock(rw: *RwLock) {
bux_rwlock_unlock(rw.handle);
}
func RwLock_Free(rw: *RwLock) {
bux_rwlock_free(rw.handle);
}
func RwLock_Free(rw: *RwLock) {
bux_rwlock_free(rw.handle);
}
}
+31 -31
View File
@@ -1,43 +1,43 @@
module Std::Task {
extern func bux_task_init(num_workers: int);
extern func bux_task_spawn(fn: *void, arg: *void) -> *void;
extern func bux_task_join(handle: *void);
extern func bux_task_sleep(ms: int64);
extern func bux_task_yield();
extern func bux_task_current_id() -> int;
extern func bux_task_shutdown();
extern func bux_task_init(num_workers: int);
extern func bux_task_spawn(fn: *void, arg: *void) -> *void;
extern func bux_task_join(handle: *void);
extern func bux_task_sleep(ms: int64);
extern func bux_task_yield();
extern func bux_task_current_id() -> int;
extern func bux_task_shutdown();
struct TaskHandle {
handle: *void;
}
struct TaskHandle {
handle: *void;
}
func Task_Init(num_workers: int) {
bux_task_init(num_workers);
}
func Task_Init(num_workers: int) {
bux_task_init(num_workers);
}
func Task_Spawn(fn: *void, arg: *void) -> TaskHandle {
return TaskHandle { handle: bux_task_spawn(fn, arg) };
}
func Task_Spawn(fn: *void, arg: *void) -> TaskHandle {
return TaskHandle { handle: bux_task_spawn(fn, arg) };
}
func Task_Wait(t: TaskHandle) {
bux_task_join(t.handle);
}
func Task_Wait(t: TaskHandle) {
bux_task_join(t.handle);
}
func Task_Sleep(ms: int64) {
bux_task_sleep(ms);
}
func Task_Sleep(ms: int64) {
bux_task_sleep(ms);
}
func Task_Yield() {
bux_task_yield();
}
func Task_Yield() {
bux_task_yield();
}
func Task_CurrentId() -> int {
return bux_task_current_id();
}
func Task_CurrentId() -> int {
return bux_task_current_id();
}
func Task_Shutdown() {
bux_task_shutdown();
}
func Task_Shutdown() {
bux_task_shutdown();
}
}
+75 -65
View File
@@ -1,76 +1,86 @@
module Std::Test {
import Std::Io::{PrintLine, PrintInt};
import Std::String::{String_Eq};
import Std::Io::{PrintLine, PrintInt};
import Std::String::{String_Eq};
extern func bux_exit(code: int);
extern func bux_assert(cond: int, file: String, line: int, expr: String);
extern func bux_exit(code: int);
extern func bux_assert(cond: int, file: String, line: int, expr: String);
func Test_Exit(code: int) {
bux_exit(code);
}
/// Exit the process with `code` (for test runners).
func Test_Exit(code: int) {
bux_exit(code);
}
func Test_Assert(cond: bool) {
bux_assert(cond as int, "", 0, "");
}
/// Assert `cond` is true; abort on failure.
func Test_Assert(cond: bool) {
bux_assert(cond as int, "", 0, "");
}
func Test_AssertEqInt(a: int, b: int) {
if a != b {
PrintLine("ASSERT_EQ_INT FAILED:");
PrintInt(a);
PrintLine(" != ");
PrintInt(b);
/// Assert two ints are equal; print both values and exit 1 on mismatch.
func Test_AssertEqInt(a: int, b: int) {
if a != b {
PrintLine("ASSERT_EQ_INT FAILED:");
PrintInt(a);
PrintLine(" != ");
PrintInt(b);
bux_exit(1);
}
}
/// Assert two ints differ.
func Test_AssertNeqInt(a: int, b: int) {
if a == b {
PrintLine("ASSERT_NEQ_INT FAILED: both are");
PrintInt(a);
bux_exit(1);
}
}
/// Assert two strings are equal (`String_Eq`).
func Test_AssertEqString(a: String, b: String) {
if !String_Eq(a, b) {
PrintLine("ASSERT_EQ_STRING FAILED:");
PrintLine(a);
PrintLine(" != ");
PrintLine(b);
bux_exit(1);
}
}
/// Assert two bools are equal.
func Test_AssertEqBool(a: bool, b: bool) {
if a != b {
PrintLine("ASSERT_EQ_BOOL FAILED");
bux_exit(1);
}
}
/// Assert `cond` is true.
func Test_AssertTrue(cond: bool) {
if !cond {
PrintLine("ASSERT_TRUE FAILED");
bux_exit(1);
}
}
/// Assert `cond` is false.
func Test_AssertFalse(cond: bool) {
if cond {
PrintLine("ASSERT_FALSE FAILED");
bux_exit(1);
}
}
/// Fail the test with a message and exit 1.
func Test_Fail(msg: String) {
PrintLine("FAIL:");
PrintLine(msg);
bux_exit(1);
}
}
func Test_AssertNeqInt(a: int, b: int) {
if a == b {
PrintLine("ASSERT_NEQ_INT FAILED: both are");
PrintInt(a);
bux_exit(1);
/// Print a PASS line (for human-readable runners / goldens).
func Test_Pass(msg: String) {
PrintLine("PASS:");
PrintLine(msg);
}
}
func Test_AssertEqString(a: String, b: String) {
if !String_Eq(a, b) {
PrintLine("ASSERT_EQ_STRING FAILED:");
PrintLine(a);
PrintLine(" != ");
PrintLine(b);
bux_exit(1);
}
}
func Test_AssertEqBool(a: bool, b: bool) {
if a != b {
PrintLine("ASSERT_EQ_BOOL FAILED");
bux_exit(1);
}
}
func Test_AssertTrue(cond: bool) {
if !cond {
PrintLine("ASSERT_TRUE FAILED");
bux_exit(1);
}
}
func Test_AssertFalse(cond: bool) {
if cond {
PrintLine("ASSERT_FALSE FAILED");
bux_exit(1);
}
}
func Test_Fail(msg: String) {
PrintLine("FAIL:");
PrintLine(msg);
bux_exit(1);
}
func Test_Pass(msg: String) {
PrintLine("PASS:");
PrintLine(msg);
}
}
+12 -12
View File
@@ -1,19 +1,19 @@
module Std::Time {
extern func bux_time_ms() -> int64;
extern func bux_time_us() -> int64;
extern func bux_sleep_ms(ms: int64);
extern func bux_time_ms() -> int64;
extern func bux_time_us() -> int64;
extern func bux_sleep_ms(ms: int64);
func Time_NowMs() -> int64 {
return bux_time_ms();
}
func Time_NowMs() -> int64 {
return bux_time_ms();
}
func Time_NowUs() -> int64 {
return bux_time_us();
}
func Time_NowUs() -> int64 {
return bux_time_us();
}
func Time_SleepMs(ms: int64) {
bux_sleep_ms(ms);
}
func Time_SleepMs(ms: int64) {
bux_sleep_ms(ms);
}
}
+49 -49
View File
@@ -3,65 +3,65 @@
// =============================================================================
module Std::Crypto::Aes {
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len};
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len};
extern func bux_random_bytes(buf: *void, len: int) -> int;
extern func bux_aes_256_cbc_encrypt(plain: String, plainlen: int, key: String, iv: String, outlen: *int) -> String;
extern func bux_aes_256_cbc_decrypt(cipher: String, cipherlen: int, key: String, iv: String, outlen: *int) -> String;
extern func bux_aes_256_gcm_encrypt(plain: String, plainlen: int, key: String, iv: String, tag: *void, outlen: *int) -> String;
extern func bux_aes_256_gcm_decrypt(cipher: String, cipherlen: int, key: String, iv: String, tag: String, outlen: *int) -> String;
extern func bux_random_bytes(buf: *void, len: int) -> int;
extern func bux_aes_256_cbc_encrypt(plain: String, plainlen: int, key: String, iv: String, outlen: *int) -> String;
extern func bux_aes_256_cbc_decrypt(cipher: String, cipherlen: int, key: String, iv: String, outlen: *int) -> String;
extern func bux_aes_256_gcm_encrypt(plain: String, plainlen: int, key: String, iv: String, tag: *void, outlen: *int) -> String;
extern func bux_aes_256_gcm_decrypt(cipher: String, cipherlen: int, key: String, iv: String, tag: String, outlen: *int) -> String;
// --- AES-256-CBC ---
// --- AES-256-CBC ---
const AES_KEY_SIZE: int = 32; // 256 bits
const AES_IV_SIZE: int = 16; // 128 bits
const AES_GCM_TAG_SIZE: int = 16;
const AES_KEY_SIZE: int = 32; // 256 bits
const AES_IV_SIZE: int = 16; // 128 bits
const AES_GCM_TAG_SIZE: int = 16;
// Generate a random 256-bit AES key (returns raw 32 bytes)
func Aes_GenerateKey() -> String {
let buf: *void = Alloc(AES_KEY_SIZE as uint);
if bux_random_bytes(buf, AES_KEY_SIZE) != 1 {
Free(buf);
return "";
// Generate a random 256-bit AES key (returns raw 32 bytes)
func Aes_GenerateKey() -> String {
let buf: *void = Alloc(AES_KEY_SIZE as uint);
if bux_random_bytes(buf, AES_KEY_SIZE) != 1 {
Free(buf);
return "";
}
return buf as String;
}
return buf as String;
}
// Generate a random 128-bit IV (returns raw 16 bytes)
func Aes_GenerateIV() -> String {
let buf: *void = Alloc(AES_IV_SIZE as uint);
if bux_random_bytes(buf, AES_IV_SIZE) != 1 {
Free(buf);
return "";
// Generate a random 128-bit IV (returns raw 16 bytes)
func Aes_GenerateIV() -> String {
let buf: *void = Alloc(AES_IV_SIZE as uint);
if bux_random_bytes(buf, AES_IV_SIZE) != 1 {
Free(buf);
return "";
}
return buf as String;
}
return buf as String;
}
// AES-256-CBC encrypt. plain and key are binary strings, iv is 16 bytes.
// Returns ciphertext (may be longer than plain due to PKCS#7 padding).
func Aes_CbcEncrypt(plain: String, key: String, iv: String) -> String {
let outlen: int = 0;
return bux_aes_256_cbc_encrypt(plain, String_Len(plain) as int, key, iv, &outlen);
}
// AES-256-CBC encrypt. plain and key are binary strings, iv is 16 bytes.
// Returns ciphertext (may be longer than plain due to PKCS#7 padding).
func Aes_CbcEncrypt(plain: String, key: String, iv: String) -> String {
let outlen: int = 0;
return bux_aes_256_cbc_encrypt(plain, String_Len(plain) as int, key, iv, &outlen);
}
// AES-256-CBC decrypt. Returns plaintext.
func Aes_CbcDecrypt(cipher: String, key: String, iv: String) -> String {
let outlen: int = 0;
return bux_aes_256_cbc_decrypt(cipher, String_Len(cipher) as int, key, iv, &outlen);
}
// AES-256-CBC decrypt. Returns plaintext.
func Aes_CbcDecrypt(cipher: String, key: String, iv: String) -> String {
let outlen: int = 0;
return bux_aes_256_cbc_decrypt(cipher, String_Len(cipher) as int, key, iv, &outlen);
}
// --- AES-256-GCM (Authenticated Encryption) ---
// --- AES-256-GCM (Authenticated Encryption) ---
// AES-256-GCM encrypt. Returns ciphertext. tag receives 16-byte authentication tag.
func Aes_GcmEncrypt(plain: String, key: String, iv: String, tag: *void) -> String {
let outlen: int = 0;
return bux_aes_256_gcm_encrypt(plain, String_Len(plain) as int, key, iv, tag, &outlen);
}
// AES-256-GCM encrypt. Returns ciphertext. tag receives 16-byte authentication tag.
func Aes_GcmEncrypt(plain: String, key: String, iv: String, tag: *void) -> String {
let outlen: int = 0;
return bux_aes_256_gcm_encrypt(plain, String_Len(plain) as int, key, iv, tag, &outlen);
}
// AES-256-GCM decrypt. Returns plaintext. tag must be the 16-byte auth tag from encryption.
func Aes_GcmDecrypt(cipher: String, key: String, iv: String, tag: String) -> String {
let outlen: int = 0;
return bux_aes_256_gcm_decrypt(cipher, String_Len(cipher) as int, key, iv, tag, &outlen);
}
// AES-256-GCM decrypt. Returns plaintext. tag must be the 16-byte auth tag from encryption.
func Aes_GcmDecrypt(cipher: String, key: String, iv: String, tag: String) -> String {
let outlen: int = 0;
return bux_aes_256_gcm_decrypt(cipher, String_Len(cipher) as int, key, iv, tag, &outlen);
}
}
+25 -25
View File
@@ -3,32 +3,32 @@
// =============================================================================
module Std::Crypto::Base64 {
import Std::String::{String_Len};
import Std::String::{String_Len};
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_base64_decode(data: String, len: int, outlen: *int) -> String;
extern func bux_base64url_encode(data: String, len: int) -> String;
extern func bux_base64url_decode(data: String, len: int, outlen: *int) -> String;
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_base64_decode(data: String, len: int, outlen: *int) -> String;
extern func bux_base64url_encode(data: String, len: int) -> String;
extern func bux_base64url_decode(data: String, len: int, outlen: *int) -> String;
// --- Standard Base64 ---
// --- Standard Base64 ---
func Base64_Encode(s: String) -> String {
return bux_base64_encode(s, String_Len(s) as int);
}
func Base64_Decode(s: String) -> String {
let outlen: int = 0;
return bux_base64_decode(s, String_Len(s) as int, &outlen);
}
// --- Base64URL (RFC 4648 §5, uses - and _ instead of + and /, no padding) ---
func Base64URL_Encode(s: String) -> String {
return bux_base64url_encode(s, String_Len(s) as int);
}
func Base64URL_Decode(s: String) -> String {
let outlen: int = 0;
return bux_base64url_decode(s, String_Len(s) as int, &outlen);
}
func Base64_Encode(s: String) -> String {
return bux_base64_encode(s, String_Len(s) as int);
}
func Base64_Decode(s: String) -> String {
let outlen: int = 0;
return bux_base64_decode(s, String_Len(s) as int, &outlen);
}
// --- Base64URL (RFC 4648 §5, uses - and _ instead of + and /, no padding) ---
func Base64URL_Encode(s: String) -> String {
return bux_base64url_encode(s, String_Len(s) as int);
}
func Base64URL_Decode(s: String) -> String {
let outlen: int = 0;
return bux_base64url_decode(s, String_Len(s) as int, &outlen);
}
}
+43 -43
View File
@@ -3,56 +3,56 @@
// =============================================================================
module Std::Crypto::Ecdsa {
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len};
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len};
// Extern declarations for the runtime C implementations
extern func bux_ecdsa_sign_p256(key: String, keylen: int, data: String, datalen: int, outlen: *int) -> String;
extern func bux_ecdsa_verify_p256(key: String, keylen: int, data: String, datalen: int, sig: String, siglen: int) -> int;
extern func bux_ecdsa_sign_p384(key: String, keylen: int, data: String, datalen: int, outlen: *int) -> String;
extern func bux_ecdsa_verify_p384(key: String, keylen: int, data: String, datalen: int, sig: String, siglen: int) -> int;
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_base64_decode(data: String, len: int, outlen: *int) -> String;
// Extern declarations for the runtime C implementations
extern func bux_ecdsa_sign_p256(key: String, keylen: int, data: String, datalen: int, outlen: *int) -> String;
extern func bux_ecdsa_verify_p256(key: String, keylen: int, data: String, datalen: int, sig: String, siglen: int) -> int;
extern func bux_ecdsa_sign_p384(key: String, keylen: int, data: String, datalen: int, outlen: *int) -> String;
extern func bux_ecdsa_verify_p384(key: String, keylen: int, data: String, datalen: int, sig: String, siglen: int) -> int;
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_base64_decode(data: String, len: int, outlen: *int) -> String;
func Ecdsa_SignP256(pemPrivateKey: String, data: String) -> String {
let siglen: int = 0;
return bux_ecdsa_sign_p256(pemPrivateKey, String_Len(pemPrivateKey) as int, data, String_Len(data) as int, &siglen);
}
func Ecdsa_SignP256(pemPrivateKey: String, data: String) -> String {
let siglen: int = 0;
return bux_ecdsa_sign_p256(pemPrivateKey, String_Len(pemPrivateKey) as int, data, String_Len(data) as int, &siglen);
}
func Ecdsa_SignP256Base64(pemPrivateKey: String, data: String) -> String {
let raw: String = Ecdsa_SignP256(pemPrivateKey, data);
return bux_base64_encode(raw, String_Len(raw) as int);
}
func Ecdsa_SignP256Base64(pemPrivateKey: String, data: String) -> String {
let raw: String = Ecdsa_SignP256(pemPrivateKey, data);
return bux_base64_encode(raw, String_Len(raw) as int);
}
func Ecdsa_VerifyP256(pemPublicKey: String, data: String, signature: String) -> bool {
let r: int = bux_ecdsa_verify_p256(pemPublicKey, String_Len(pemPublicKey) as int, data, String_Len(data) as int, signature, String_Len(signature) as int);
return r == 1;
}
func Ecdsa_VerifyP256(pemPublicKey: String, data: String, signature: String) -> bool {
let r: int = bux_ecdsa_verify_p256(pemPublicKey, String_Len(pemPublicKey) as int, data, String_Len(data) as int, signature, String_Len(signature) as int);
return r == 1;
}
func Ecdsa_VerifyP256Base64(pemPublicKey: String, data: String, signatureB64: String) -> bool {
let outlen: int = 0;
let sig: String = bux_base64_decode(signatureB64, String_Len(signatureB64) as int, &outlen);
return Ecdsa_VerifyP256(pemPublicKey, data, sig);
}
func Ecdsa_VerifyP256Base64(pemPublicKey: String, data: String, signatureB64: String) -> bool {
let outlen: int = 0;
let sig: String = bux_base64_decode(signatureB64, String_Len(signatureB64) as int, &outlen);
return Ecdsa_VerifyP256(pemPublicKey, data, sig);
}
func Ecdsa_SignP384(pemPrivateKey: String, data: String) -> String {
let siglen: int = 0;
return bux_ecdsa_sign_p384(pemPrivateKey, String_Len(pemPrivateKey) as int, data, String_Len(data) as int, &siglen);
}
func Ecdsa_SignP384(pemPrivateKey: String, data: String) -> String {
let siglen: int = 0;
return bux_ecdsa_sign_p384(pemPrivateKey, String_Len(pemPrivateKey) as int, data, String_Len(data) as int, &siglen);
}
func Ecdsa_SignP384Base64(pemPrivateKey: String, data: String) -> String {
let raw: String = Ecdsa_SignP384(pemPrivateKey, data);
return bux_base64_encode(raw, String_Len(raw) as int);
}
func Ecdsa_SignP384Base64(pemPrivateKey: String, data: String) -> String {
let raw: String = Ecdsa_SignP384(pemPrivateKey, data);
return bux_base64_encode(raw, String_Len(raw) as int);
}
func Ecdsa_VerifyP384(pemPublicKey: String, data: String, signature: String) -> bool {
let r: int = bux_ecdsa_verify_p384(pemPublicKey, String_Len(pemPublicKey) as int, data, String_Len(data) as int, signature, String_Len(signature) as int);
return r == 1;
}
func Ecdsa_VerifyP384(pemPublicKey: String, data: String, signature: String) -> bool {
let r: int = bux_ecdsa_verify_p384(pemPublicKey, String_Len(pemPublicKey) as int, data, String_Len(data) as int, signature, String_Len(signature) as int);
return r == 1;
}
func Ecdsa_VerifyP384Base64(pemPublicKey: String, data: String, signatureB64: String) -> bool {
let outlen: int = 0;
let sig: String = bux_base64_decode(signatureB64, String_Len(signatureB64) as int, &outlen);
return Ecdsa_VerifyP384(pemPublicKey, data, sig);
}
func Ecdsa_VerifyP384Base64(pemPublicKey: String, data: String, signatureB64: String) -> bool {
let outlen: int = 0;
let sig: String = bux_base64_decode(signatureB64, String_Len(signatureB64) as int, &outlen);
return Ecdsa_VerifyP384(pemPublicKey, data, sig);
}
}
+59 -59
View File
@@ -3,78 +3,78 @@
// =============================================================================
module Std::Crypto::Ed25519 {
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len, String_Concat};
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len, String_Concat};
extern func bux_ed25519_keypair(pubKey: *void, privKey: *void) -> int;
extern func bux_ed25519_sign(privKey: String, data: String, datalen: int, sig: *void) -> int;
extern func bux_ed25519_verify(pubKey: String, sig: String, data: String, datalen: int) -> int;
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_base64_decode(data: String, len: int, outlen: *int) -> String;
extern func bux_ed25519_keypair(pubKey: *void, privKey: *void) -> int;
extern func bux_ed25519_sign(privKey: String, data: String, datalen: int, sig: *void) -> int;
extern func bux_ed25519_verify(pubKey: String, sig: String, data: String, datalen: int) -> int;
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_base64_decode(data: String, len: int, outlen: *int) -> String;
const ED25519_PUBKEY_SIZE: int = 32;
const ED25519_PRIVKEY_SIZE: int = 32;
const ED25519_SIG_SIZE: int = 64;
const ED25519_PUBKEY_SIZE: int = 32;
const ED25519_PRIVKEY_SIZE: int = 32;
const ED25519_SIG_SIZE: int = 64;
// --- Key Generation ---
// --- Key Generation ---
// Ed25519_Keypair: generates a new keypair.
// Returns true on success. pubKey and privKey receive 32-byte raw keys.
func Ed25519_Keypair(pubKey: *void, privKey: *void) -> bool {
let r: int = bux_ed25519_keypair(pubKey, privKey);
return r == 1;
}
// Ed25519_Keypair: generates a new keypair.
// Returns true on success. pubKey and privKey receive 32-byte raw keys.
func Ed25519_Keypair(pubKey: *void, privKey: *void) -> bool {
let r: int = bux_ed25519_keypair(pubKey, privKey);
return r == 1;
}
// Convenience: generate and return base64-encoded keypair
func Ed25519_KeypairBase64() -> String {
let pubBuf: *void = Alloc(ED25519_PUBKEY_SIZE as uint);
let priv: *void = Alloc(ED25519_PRIVKEY_SIZE as uint);
if bux_ed25519_keypair(pubBuf, priv) != 1 {
// Convenience: generate and return base64-encoded keypair
func Ed25519_KeypairBase64() -> String {
let pubBuf: *void = Alloc(ED25519_PUBKEY_SIZE as uint);
let priv: *void = Alloc(ED25519_PRIVKEY_SIZE as uint);
if bux_ed25519_keypair(pubBuf, priv) != 1 {
Free(pubBuf);
Free(priv);
return "";
}
// Return "pub_b64:priv_b64"
let pubB64: String = bux_base64_encode(pubBuf as String, ED25519_PUBKEY_SIZE);
let privB64: String = bux_base64_encode(priv as String, ED25519_PRIVKEY_SIZE);
Free(pubBuf);
Free(priv);
return "";
let pair: String = String_Concat(pubB64, ":");
return String_Concat(pair, privB64);
}
// Return "pub_b64:priv_b64"
let pubB64: String = bux_base64_encode(pubBuf as String, ED25519_PUBKEY_SIZE);
let privB64: String = bux_base64_encode(priv as String, ED25519_PRIVKEY_SIZE);
Free(pubBuf);
Free(priv);
let pair: String = String_Concat(pubB64, ":");
return String_Concat(pair, privB64);
}
// --- Sign ---
// --- Sign ---
// Ed25519_Sign: sign data with 32-byte raw private key. Returns 64-byte raw signature.
func Ed25519_Sign(privKey: String, data: String) -> String {
let sig: *void = Alloc(ED25519_SIG_SIZE as uint);
if bux_ed25519_sign(privKey, data, String_Len(data) as int, sig) != 1 {
Free(sig);
return "";
// Ed25519_Sign: sign data with 32-byte raw private key. Returns 64-byte raw signature.
func Ed25519_Sign(privKey: String, data: String) -> String {
let sig: *void = Alloc(ED25519_SIG_SIZE as uint);
if bux_ed25519_sign(privKey, data, String_Len(data) as int, sig) != 1 {
Free(sig);
return "";
}
return sig as String;
}
return sig as String;
}
// Convenience: sign and return base64-encoded signature
func Ed25519_SignBase64(privKey: String, data: String) -> String {
let sig: String = Ed25519_Sign(privKey, data);
if String_Len(sig) == 0 { return ""; }
return bux_base64_encode(sig, ED25519_SIG_SIZE);
}
// Convenience: sign and return base64-encoded signature
func Ed25519_SignBase64(privKey: String, data: String) -> String {
let sig: String = Ed25519_Sign(privKey, data);
if String_Len(sig) == 0 { return ""; }
return bux_base64_encode(sig, ED25519_SIG_SIZE);
}
// --- Verify ---
// --- Verify ---
// Ed25519_Verify: verify a 64-byte raw signature against data with 32-byte public key.
func Ed25519_Verify(pubKey: String, signature: String, data: String) -> bool {
let r: int = bux_ed25519_verify(pubKey, signature, data, String_Len(data) as int);
return r == 1;
}
// Ed25519_Verify: verify a 64-byte raw signature against data with 32-byte public key.
func Ed25519_Verify(pubKey: String, signature: String, data: String) -> bool {
let r: int = bux_ed25519_verify(pubKey, signature, data, String_Len(data) as int);
return r == 1;
}
// Convenience: verify a base64-encoded signature
func Ed25519_VerifyBase64(pubKey: String, signatureB64: String, data: String) -> bool {
let outlen: int = 0;
let sig: String = bux_base64_decode(signatureB64, String_Len(signatureB64) as int, &outlen);
if outlen != ED25519_SIG_SIZE { return false; }
return Ed25519_Verify(pubKey, sig, data);
}
// Convenience: verify a base64-encoded signature
func Ed25519_VerifyBase64(pubKey: String, signatureB64: String, data: String) -> bool {
let outlen: int = 0;
let sig: String = bux_base64_decode(signatureB64, String_Len(signatureB64) as int, &outlen);
if outlen != ED25519_SIG_SIZE { return false; }
return Ed25519_Verify(pubKey, sig, data);
}
}
+64 -64
View File
@@ -3,71 +3,71 @@
// =============================================================================
module Std::Crypto::Hash {
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len};
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len};
extern func bux_sha1(data: String, len: int, out: *void);
extern func bux_sha256(data: String, len: int, out: *void);
extern func bux_sha384(data: String, len: int, out: *void);
extern func bux_sha512(data: String, len: int, out: *void);
extern func bux_bytes_to_hex(data: *void, len: int) -> String;
extern func bux_sha1(data: String, len: int, out: *void);
extern func bux_sha256(data: String, len: int, out: *void);
extern func bux_sha384(data: String, len: int, out: *void);
extern func bux_sha512(data: String, len: int, out: *void);
extern func bux_bytes_to_hex(data: *void, len: int) -> String;
// --- Convenience wrappers: hex output ---
// --- Convenience wrappers: hex output ---
func Hash_Sha1(data: String) -> String {
let len: int = String_Len(data) as int;
let buf: *void = Alloc(20);
bux_sha1(data, len, buf);
let result: String = bux_bytes_to_hex(buf, 20);
Free(buf);
return result;
}
func Hash_Sha256(data: String) -> String {
let len: int = String_Len(data) as int;
let buf: *void = Alloc(32);
bux_sha256(data, len, buf);
let result: String = bux_bytes_to_hex(buf, 32);
Free(buf);
return result;
}
func Hash_Sha384(data: String) -> String {
let len: int = String_Len(data) as int;
let buf: *void = Alloc(48);
bux_sha384(data, len, buf);
let result: String = bux_bytes_to_hex(buf, 48);
Free(buf);
return result;
}
func Hash_Sha512(data: String) -> String {
let len: int = String_Len(data) as int;
let buf: *void = Alloc(64);
bux_sha512(data, len, buf);
let result: String = bux_bytes_to_hex(buf, 64);
Free(buf);
return result;
}
// --- Raw binary output (caller must Alloc/Free) ---
func Hash_Sha256Raw(data: String, out: *void) {
bux_sha256(data, String_Len(data) as int, out);
}
func Hash_Sha384Raw(data: String, out: *void) {
bux_sha384(data, String_Len(data) as int, out);
}
func Hash_Sha512Raw(data: String, out: *void) {
bux_sha512(data, String_Len(data) as int, out);
}
// --- Digest sizes ---
func Hash_Sha1Size() -> int { return 20; }
func Hash_Sha256Size() -> int { return 32; }
func Hash_Sha384Size() -> int { return 48; }
func Hash_Sha512Size() -> int { return 64; }
func Hash_Sha1(data: String) -> String {
let len: int = String_Len(data) as int;
let buf: *void = Alloc(20);
bux_sha1(data, len, buf);
let result: String = bux_bytes_to_hex(buf, 20);
Free(buf);
return result;
}
func Hash_Sha256(data: String) -> String {
let len: int = String_Len(data) as int;
let buf: *void = Alloc(32);
bux_sha256(data, len, buf);
let result: String = bux_bytes_to_hex(buf, 32);
Free(buf);
return result;
}
func Hash_Sha384(data: String) -> String {
let len: int = String_Len(data) as int;
let buf: *void = Alloc(48);
bux_sha384(data, len, buf);
let result: String = bux_bytes_to_hex(buf, 48);
Free(buf);
return result;
}
func Hash_Sha512(data: String) -> String {
let len: int = String_Len(data) as int;
let buf: *void = Alloc(64);
bux_sha512(data, len, buf);
let result: String = bux_bytes_to_hex(buf, 64);
Free(buf);
return result;
}
// --- Raw binary output (caller must Alloc/Free) ---
func Hash_Sha256Raw(data: String, out: *void) {
bux_sha256(data, String_Len(data) as int, out);
}
func Hash_Sha384Raw(data: String, out: *void) {
bux_sha384(data, String_Len(data) as int, out);
}
func Hash_Sha512Raw(data: String, out: *void) {
bux_sha512(data, String_Len(data) as int, out);
}
// --- Digest sizes ---
func Hash_Sha1Size() -> int { return 20; }
func Hash_Sha256Size() -> int { return 32; }
func Hash_Sha384Size() -> int { return 48; }
func Hash_Sha512Size() -> int { return 64; }
}
+83 -83
View File
@@ -3,90 +3,90 @@
// =============================================================================
module Std::Crypto::Hmac {
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len};
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len};
extern func bux_hmac_sha256(key: String, keylen: int, msg: String, msglen: int, out: *void);
extern func bux_hmac_sha384(key: String, keylen: int, msg: String, msglen: int, out: *void);
extern func bux_hmac_sha512(key: String, keylen: int, msg: String, msglen: int, out: *void);
extern func bux_bytes_to_hex(data: *void, len: int) -> String;
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_hmac_sha256(key: String, keylen: int, msg: String, msglen: int, out: *void);
extern func bux_hmac_sha384(key: String, keylen: int, msg: String, msglen: int, out: *void);
extern func bux_hmac_sha512(key: String, keylen: int, msg: String, msglen: int, out: *void);
extern func bux_bytes_to_hex(data: *void, len: int) -> String;
extern func bux_base64_encode(data: String, len: int) -> String;
// --- HMAC-SHA256 ---
// --- HMAC-SHA256 ---
func Hmac_Sha256(key: String, message: String) -> String {
let kl: int = String_Len(key) as int;
let ml: int = String_Len(message) as int;
let buf: *void = Alloc(32);
bux_hmac_sha256(key, kl, message, ml, buf);
let result: String = bux_bytes_to_hex(buf, 32);
Free(buf);
return result;
}
func Hmac_Sha256Raw(key: String, message: String, out: *void) {
bux_hmac_sha256(key, String_Len(key) as int, message, String_Len(message) as int, out);
}
func Hmac_Sha256Base64(key: String, message: String) -> String {
let kl: int = String_Len(key) as int;
let ml: int = String_Len(message) as int;
let buf: *void = Alloc(32);
bux_hmac_sha256(key, kl, message, ml, buf);
let result: String = bux_base64_encode(buf as String, 32);
Free(buf);
return result;
}
// --- HMAC-SHA384 ---
func Hmac_Sha384(key: String, message: String) -> String {
let kl: int = String_Len(key) as int;
let ml: int = String_Len(message) as int;
let buf: *void = Alloc(48);
bux_hmac_sha384(key, kl, message, ml, buf);
let result: String = bux_bytes_to_hex(buf, 48);
Free(buf);
return result;
}
func Hmac_Sha384Raw(key: String, message: String, out: *void) {
bux_hmac_sha384(key, String_Len(key) as int, message, String_Len(message) as int, out);
}
func Hmac_Sha384Base64(key: String, message: String) -> String {
let kl: int = String_Len(key) as int;
let ml: int = String_Len(message) as int;
let buf: *void = Alloc(48);
bux_hmac_sha384(key, kl, message, ml, buf);
let result: String = bux_base64_encode(buf as String, 48);
Free(buf);
return result;
}
// --- HMAC-SHA512 ---
func Hmac_Sha512(key: String, message: String) -> String {
let kl: int = String_Len(key) as int;
let ml: int = String_Len(message) as int;
let buf: *void = Alloc(64);
bux_hmac_sha512(key, kl, message, ml, buf);
let result: String = bux_bytes_to_hex(buf, 64);
Free(buf);
return result;
}
func Hmac_Sha512Raw(key: String, message: String, out: *void) {
bux_hmac_sha512(key, String_Len(key) as int, message, String_Len(message) as int, out);
}
func Hmac_Sha512Base64(key: String, message: String) -> String {
let kl: int = String_Len(key) as int;
let ml: int = String_Len(message) as int;
let buf: *void = Alloc(64);
bux_hmac_sha512(key, kl, message, ml, buf);
let result: String = bux_base64_encode(buf as String, 64);
Free(buf);
return result;
}
func Hmac_Sha256(key: String, message: String) -> String {
let kl: int = String_Len(key) as int;
let ml: int = String_Len(message) as int;
let buf: *void = Alloc(32);
bux_hmac_sha256(key, kl, message, ml, buf);
let result: String = bux_bytes_to_hex(buf, 32);
Free(buf);
return result;
}
func Hmac_Sha256Raw(key: String, message: String, out: *void) {
bux_hmac_sha256(key, String_Len(key) as int, message, String_Len(message) as int, out);
}
func Hmac_Sha256Base64(key: String, message: String) -> String {
let kl: int = String_Len(key) as int;
let ml: int = String_Len(message) as int;
let buf: *void = Alloc(32);
bux_hmac_sha256(key, kl, message, ml, buf);
let result: String = bux_base64_encode(buf as String, 32);
Free(buf);
return result;
}
// --- HMAC-SHA384 ---
func Hmac_Sha384(key: String, message: String) -> String {
let kl: int = String_Len(key) as int;
let ml: int = String_Len(message) as int;
let buf: *void = Alloc(48);
bux_hmac_sha384(key, kl, message, ml, buf);
let result: String = bux_bytes_to_hex(buf, 48);
Free(buf);
return result;
}
func Hmac_Sha384Raw(key: String, message: String, out: *void) {
bux_hmac_sha384(key, String_Len(key) as int, message, String_Len(message) as int, out);
}
func Hmac_Sha384Base64(key: String, message: String) -> String {
let kl: int = String_Len(key) as int;
let ml: int = String_Len(message) as int;
let buf: *void = Alloc(48);
bux_hmac_sha384(key, kl, message, ml, buf);
let result: String = bux_base64_encode(buf as String, 48);
Free(buf);
return result;
}
// --- HMAC-SHA512 ---
func Hmac_Sha512(key: String, message: String) -> String {
let kl: int = String_Len(key) as int;
let ml: int = String_Len(message) as int;
let buf: *void = Alloc(64);
bux_hmac_sha512(key, kl, message, ml, buf);
let result: String = bux_bytes_to_hex(buf, 64);
Free(buf);
return result;
}
func Hmac_Sha512Raw(key: String, message: String, out: *void) {
bux_hmac_sha512(key, String_Len(key) as int, message, String_Len(message) as int, out);
}
func Hmac_Sha512Base64(key: String, message: String) -> String {
let kl: int = String_Len(key) as int;
let ml: int = String_Len(message) as int;
let buf: *void = Alloc(64);
bux_hmac_sha512(key, kl, message, ml, buf);
let result: String = bux_base64_encode(buf as String, 64);
Free(buf);
return result;
}
}
+206 -206
View File
@@ -4,233 +4,233 @@
// =============================================================================
module Std::Crypto::Jwt {
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len, String_Eq, String_StartsWith, String_Concat};
import Std::Crypto::Base64::{Base64URL_Encode, Base64URL_Decode};
import Std::Crypto::Hash::{Hash_Sha256Raw, Hash_Sha384Raw, Hash_Sha512Raw};
import Std::Crypto::Hmac::{Hmac_Sha256Raw, Hmac_Sha384Raw, Hmac_Sha512Raw};
import Std::Crypto::Rsa::{Rsa_SignSha256, Rsa_SignSha384, Rsa_SignSha512,
Rsa_VerifySha256, Rsa_VerifySha384, Rsa_VerifySha512};
import Std::Crypto::Ecdsa::{Ecdsa_SignP256, Ecdsa_SignP384, Ecdsa_VerifyP256, Ecdsa_VerifyP384};
import Std::Crypto::Ed25519::{Ed25519_Sign, Ed25519_Verify};
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len, String_Eq, String_StartsWith, String_Concat};
import Std::Crypto::Base64::{Base64URL_Encode, Base64URL_Decode};
import Std::Crypto::Hash::{Hash_Sha256Raw, Hash_Sha384Raw, Hash_Sha512Raw};
import Std::Crypto::Hmac::{Hmac_Sha256Raw, Hmac_Sha384Raw, Hmac_Sha512Raw};
import Std::Crypto::Rsa::{Rsa_SignSha256, Rsa_SignSha384, Rsa_SignSha512,
Rsa_VerifySha256, Rsa_VerifySha384, Rsa_VerifySha512};
import Std::Crypto::Ecdsa::{Ecdsa_SignP256, Ecdsa_SignP384, Ecdsa_VerifyP256, Ecdsa_VerifyP384};
import Std::Crypto::Ed25519::{Ed25519_Sign, Ed25519_Verify};
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_str_split_count(s: String, delim: String) -> uint;
extern func bux_str_split_part(s: String, delim: String, index: uint) -> String;
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_str_split_count(s: String, delim: String) -> uint;
extern func bux_str_split_part(s: String, delim: String, index: uint) -> String;
// --- JWT Algorithm enum ---
enum JwtAlg {
HS256,
HS384,
HS512,
RS256,
RS384,
RS512,
ES256,
ES384,
EdDSA,
}
// --- Header ---
// Jwt_MakeHeader: build the JWT header JSON string for the given algorithm
func Jwt_MakeHeader(alg: JwtAlg) -> String {
if alg.tag == JwtAlg_HS256 { return "{\"alg\":\"HS256\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_HS384 { return "{\"alg\":\"HS384\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_HS512 { return "{\"alg\":\"HS512\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_RS256 { return "{\"alg\":\"RS256\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_RS384 { return "{\"alg\":\"RS384\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_RS512 { return "{\"alg\":\"RS512\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_ES256 { return "{\"alg\":\"ES256\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_ES384 { return "{\"alg\":\"ES384\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_EdDSA { return "{\"alg\":\"EdDSA\",\"typ\":\"JWT\"}"; }
return "{\"alg\":\"none\",\"typ\":\"JWT\"}";
}
// --- Signing ---
// Sign the JWT signing input with the given algorithm
func Jwt_Sign(alg: JwtAlg, signingInput: String, key: String) -> String {
// --- HMAC algorithms ---
if alg.tag == JwtAlg_HS256 {
let buf: *void = Alloc(32);
Hmac_Sha256Raw(key, signingInput, buf);
let result: String = bux_base64_encode(buf as String, 32);
Free(buf);
return result;
}
if alg.tag == JwtAlg_HS384 {
let buf: *void = Alloc(48);
Hmac_Sha384Raw(key, signingInput, buf);
let result: String = bux_base64_encode(buf as String, 48);
Free(buf);
return result;
}
if alg.tag == JwtAlg_HS512 {
let buf: *void = Alloc(64);
Hmac_Sha512Raw(key, signingInput, buf);
let result: String = bux_base64_encode(buf as String, 64);
Free(buf);
return result;
// --- JWT Algorithm enum ---
enum JwtAlg {
HS256,
HS384,
HS512,
RS256,
RS384,
RS512,
ES256,
ES384,
EdDSA,
}
// --- RSA algorithms (key is PEM private key) ---
if alg.tag == JwtAlg_RS256 {
let raw: String = Rsa_SignSha256(key, signingInput);
return bux_base64_encode(raw, String_Len(raw) as int);
}
if alg.tag == JwtAlg_RS384 {
let raw: String = Rsa_SignSha384(key, signingInput);
return bux_base64_encode(raw, String_Len(raw) as int);
}
if alg.tag == JwtAlg_RS512 {
let raw: String = Rsa_SignSha512(key, signingInput);
return bux_base64_encode(raw, String_Len(raw) as int);
// --- Header ---
// Jwt_MakeHeader: build the JWT header JSON string for the given algorithm
func Jwt_MakeHeader(alg: JwtAlg) -> String {
if alg.tag == JwtAlg_HS256 { return "{\"alg\":\"HS256\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_HS384 { return "{\"alg\":\"HS384\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_HS512 { return "{\"alg\":\"HS512\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_RS256 { return "{\"alg\":\"RS256\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_RS384 { return "{\"alg\":\"RS384\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_RS512 { return "{\"alg\":\"RS512\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_ES256 { return "{\"alg\":\"ES256\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_ES384 { return "{\"alg\":\"ES384\",\"typ\":\"JWT\"}"; }
if alg.tag == JwtAlg_EdDSA { return "{\"alg\":\"EdDSA\",\"typ\":\"JWT\"}"; }
return "{\"alg\":\"none\",\"typ\":\"JWT\"}";
}
// --- ECDSA algorithms (key is PEM private key) ---
if alg.tag == JwtAlg_ES256 {
let raw: String = Ecdsa_SignP256(key, signingInput);
return bux_base64_encode(raw, String_Len(raw) as int);
}
if alg.tag == JwtAlg_ES384 {
let raw: String = Ecdsa_SignP384(key, signingInput);
return bux_base64_encode(raw, String_Len(raw) as int);
// --- Signing ---
// Sign the JWT signing input with the given algorithm
func Jwt_Sign(alg: JwtAlg, signingInput: String, key: String) -> String {
// --- HMAC algorithms ---
if alg.tag == JwtAlg_HS256 {
let buf: *void = Alloc(32);
Hmac_Sha256Raw(key, signingInput, buf);
let result: String = bux_base64_encode(buf as String, 32);
Free(buf);
return result;
}
if alg.tag == JwtAlg_HS384 {
let buf: *void = Alloc(48);
Hmac_Sha384Raw(key, signingInput, buf);
let result: String = bux_base64_encode(buf as String, 48);
Free(buf);
return result;
}
if alg.tag == JwtAlg_HS512 {
let buf: *void = Alloc(64);
Hmac_Sha512Raw(key, signingInput, buf);
let result: String = bux_base64_encode(buf as String, 64);
Free(buf);
return result;
}
// --- RSA algorithms (key is PEM private key) ---
if alg.tag == JwtAlg_RS256 {
let raw: String = Rsa_SignSha256(key, signingInput);
return bux_base64_encode(raw, String_Len(raw) as int);
}
if alg.tag == JwtAlg_RS384 {
let raw: String = Rsa_SignSha384(key, signingInput);
return bux_base64_encode(raw, String_Len(raw) as int);
}
if alg.tag == JwtAlg_RS512 {
let raw: String = Rsa_SignSha512(key, signingInput);
return bux_base64_encode(raw, String_Len(raw) as int);
}
// --- ECDSA algorithms (key is PEM private key) ---
if alg.tag == JwtAlg_ES256 {
let raw: String = Ecdsa_SignP256(key, signingInput);
return bux_base64_encode(raw, String_Len(raw) as int);
}
if alg.tag == JwtAlg_ES384 {
let raw: String = Ecdsa_SignP384(key, signingInput);
return bux_base64_encode(raw, String_Len(raw) as int);
}
// --- EdDSA (key is 32-byte raw private key) ---
if alg.tag == JwtAlg_EdDSA {
return Ed25519_Sign(key, signingInput);
}
return "";
}
// --- EdDSA (key is 32-byte raw private key) ---
if alg.tag == JwtAlg_EdDSA {
return Ed25519_Sign(key, signingInput);
}
// --- Verify ---
return "";
}
// Verify a JWT signature
func Jwt_Verify(alg: JwtAlg, signingInput: String, signatureB64: String, key: String) -> bool {
// --- HMAC algorithms ---
if alg.tag == JwtAlg_HS256 {
let expectBuf: *void = Alloc(32);
Hmac_Sha256Raw(key, signingInput, expectBuf);
let expectB64: String = bux_base64_encode(expectBuf as String, 32);
Free(expectBuf);
return String_Eq(expectB64, signatureB64);
}
if alg.tag == JwtAlg_HS384 {
let expectBuf: *void = Alloc(48);
Hmac_Sha384Raw(key, signingInput, expectBuf);
let expectB64: String = bux_base64_encode(expectBuf as String, 48);
Free(expectBuf);
return String_Eq(expectB64, signatureB64);
}
if alg.tag == JwtAlg_HS512 {
let expectBuf: *void = Alloc(64);
Hmac_Sha512Raw(key, signingInput, expectBuf);
let expectB64: String = bux_base64_encode(expectBuf as String, 64);
Free(expectBuf);
return String_Eq(expectB64, signatureB64);
}
// --- Verify ---
// --- RSA algorithms ---
if alg.tag == JwtAlg_RS256 { return Rsa_VerifySha256(key, signingInput, signatureB64); }
if alg.tag == JwtAlg_RS384 { return Rsa_VerifySha384(key, signingInput, signatureB64); }
if alg.tag == JwtAlg_RS512 { return Rsa_VerifySha512(key, signingInput, signatureB64); }
// Verify a JWT signature
func Jwt_Verify(alg: JwtAlg, signingInput: String, signatureB64: String, key: String) -> bool {
// --- HMAC algorithms ---
if alg.tag == JwtAlg_HS256 {
let expectBuf: *void = Alloc(32);
Hmac_Sha256Raw(key, signingInput, expectBuf);
let expectB64: String = bux_base64_encode(expectBuf as String, 32);
Free(expectBuf);
return String_Eq(expectB64, signatureB64);
}
if alg.tag == JwtAlg_HS384 {
let expectBuf: *void = Alloc(48);
Hmac_Sha384Raw(key, signingInput, expectBuf);
let expectB64: String = bux_base64_encode(expectBuf as String, 48);
Free(expectBuf);
return String_Eq(expectB64, signatureB64);
}
if alg.tag == JwtAlg_HS512 {
let expectBuf: *void = Alloc(64);
Hmac_Sha512Raw(key, signingInput, expectBuf);
let expectB64: String = bux_base64_encode(expectBuf as String, 64);
Free(expectBuf);
return String_Eq(expectB64, signatureB64);
}
// --- ECDSA algorithms ---
if alg.tag == JwtAlg_ES256 { return Ecdsa_VerifyP256(key, signingInput, signatureB64); }
if alg.tag == JwtAlg_ES384 { return Ecdsa_VerifyP384(key, signingInput, signatureB64); }
// --- RSA algorithms ---
if alg.tag == JwtAlg_RS256 { return Rsa_VerifySha256(key, signingInput, signatureB64); }
if alg.tag == JwtAlg_RS384 { return Rsa_VerifySha384(key, signingInput, signatureB64); }
if alg.tag == JwtAlg_RS512 { return Rsa_VerifySha512(key, signingInput, signatureB64); }
// --- EdDSA ---
if alg.tag == JwtAlg_EdDSA { return Ed25519_Verify(key, signatureB64, signingInput); }
// --- ECDSA algorithms ---
if alg.tag == JwtAlg_ES256 { return Ecdsa_VerifyP256(key, signingInput, signatureB64); }
if alg.tag == JwtAlg_ES384 { return Ecdsa_VerifyP384(key, signingInput, signatureB64); }
// --- EdDSA ---
if alg.tag == JwtAlg_EdDSA { return Ed25519_Verify(key, signatureB64, signingInput); }
return false;
}
// --- Encode ---
// Jwt_Encode: create a signed JWT
// headerJson — JSON header string (use Jwt_MakeHeader or custom)
// payloadJson — JSON payload/claims string
// alg — signing algorithm
// key — signing key (HMAC secret, RSA PEM, ECDSA PEM, or Ed25519 raw privkey)
// Returns the complete "header.payload.signature" JWT string
func Jwt_Encode(headerJson: String, payloadJson: String, alg: JwtAlg, key: String) -> String {
let headerB64: String = Base64URL_Encode(headerJson);
let payloadB64: String = Base64URL_Encode(payloadJson);
let signingInput: String = String_Concat(headerB64, ".");
let signingInputFull: String = String_Concat(signingInput, payloadB64);
let sigB64: String = Jwt_Sign(alg, signingInputFull, key);
let part1: String = String_Concat(signingInputFull, ".");
return String_Concat(part1, sigB64);
}
// --- Decode ---
// Jwt_Decode: decode and verify a JWT.
// token — the full "header.payload.signature" string
// alg — expected algorithm
// key — verification key
// headerOut — receives decoded header JSON
// payloadOut — receives decoded payload JSON
// Returns true if signature is valid.
func Jwt_Decode(token: String, alg: JwtAlg, key: String,
headerOut: *String, payloadOut: *String) -> bool {
// Split by "."
let partCount: uint = bux_str_split_count(token, ".");
if partCount != 3 { return false; }
let headerB64: String = bux_str_split_part(token, ".", 0);
let payloadB64: String = bux_str_split_part(token, ".", 1);
let sigB64: String = bux_str_split_part(token, ".", 2);
// Build signing input
let input: String = String_Concat(headerB64, ".");
let signingInput: String = String_Concat(input, payloadB64);
// Verify signature
if !Jwt_Verify(alg, signingInput, sigB64, key) {
return false;
}
// Decode
headerOut[0] = Base64URL_Decode(headerB64);
payloadOut[0] = Base64URL_Decode(payloadB64);
return true;
}
// --- Encode ---
// --- Convenience: Encode with standard header ---
// Jwt_Encode: create a signed JWT
// headerJson — JSON header string (use Jwt_MakeHeader or custom)
// payloadJson — JSON payload/claims string
// alg — signing algorithm
// key — signing key (HMAC secret, RSA PEM, ECDSA PEM, or Ed25519 raw privkey)
// Returns the complete "header.payload.signature" JWT string
func Jwt_Encode(headerJson: String, payloadJson: String, alg: JwtAlg, key: String) -> String {
let headerB64: String = Base64URL_Encode(headerJson);
let payloadB64: String = Base64URL_Encode(payloadJson);
let signingInput: String = String_Concat(headerB64, ".");
let signingInputFull: String = String_Concat(signingInput, payloadB64);
func Jwt_EncodeHS256(payloadJson: String, secret: String) -> String {
let header: String = Jwt_MakeHeader(JwtAlg { tag: JwtAlg_HS256 });
return Jwt_Encode(header, payloadJson, JwtAlg { tag: JwtAlg_HS256 }, secret);
}
let sigB64: String = Jwt_Sign(alg, signingInputFull, key);
func Jwt_EncodeHS384(payloadJson: String, secret: String) -> String {
let header: String = Jwt_MakeHeader(JwtAlg { tag: JwtAlg_HS384 });
return Jwt_Encode(header, payloadJson, JwtAlg { tag: JwtAlg_HS384 }, secret);
}
let part1: String = String_Concat(signingInputFull, ".");
return String_Concat(part1, sigB64);
}
func Jwt_EncodeHS512(payloadJson: String, secret: String) -> String {
let header: String = Jwt_MakeHeader(JwtAlg { tag: JwtAlg_HS512 });
return Jwt_Encode(header, payloadJson, JwtAlg { tag: JwtAlg_HS512 }, secret);
}
// --- Decode ---
func Jwt_EncodeRS256(payloadJson: String, pemPrivateKey: String) -> String {
let header: String = Jwt_MakeHeader(JwtAlg { tag: JwtAlg_RS256 });
return Jwt_Encode(header, payloadJson, JwtAlg { tag: JwtAlg_RS256 }, pemPrivateKey);
}
// Jwt_Decode: decode and verify a JWT.
// token — the full "header.payload.signature" string
// alg — expected algorithm
// key — verification key
// headerOut — receives decoded header JSON
// payloadOut — receives decoded payload JSON
// Returns true if signature is valid.
func Jwt_Decode(token: String, alg: JwtAlg, key: String,
headerOut: *String, payloadOut: *String) -> bool {
// Split by "."
let partCount: uint = bux_str_split_count(token, ".");
if partCount != 3 { return false; }
func Jwt_EncodeES256(payloadJson: String, pemPrivateKey: String) -> String {
let header: String = Jwt_MakeHeader(JwtAlg { tag: JwtAlg_ES256 });
return Jwt_Encode(header, payloadJson, JwtAlg { tag: JwtAlg_ES256 }, pemPrivateKey);
}
let headerB64: String = bux_str_split_part(token, ".", 0);
let payloadB64: String = bux_str_split_part(token, ".", 1);
let sigB64: String = bux_str_split_part(token, ".", 2);
func Jwt_EncodeEdDSA(payloadJson: String, rawPrivKey: String) -> String {
let header: String = Jwt_MakeHeader(JwtAlg { tag: JwtAlg_EdDSA });
return Jwt_Encode(header, payloadJson, JwtAlg { tag: JwtAlg_EdDSA }, rawPrivKey);
}
// Build signing input
let input: String = String_Concat(headerB64, ".");
let signingInput: String = String_Concat(input, payloadB64);
// Verify signature
if !Jwt_Verify(alg, signingInput, sigB64, key) {
return false;
}
// Decode
headerOut[0] = Base64URL_Decode(headerB64);
payloadOut[0] = Base64URL_Decode(payloadB64);
return true;
}
// --- Convenience: Encode with standard header ---
func Jwt_EncodeHS256(payloadJson: String, secret: String) -> String {
let header: String = Jwt_MakeHeader(JwtAlg { tag: JwtAlg_HS256 });
return Jwt_Encode(header, payloadJson, JwtAlg { tag: JwtAlg_HS256 }, secret);
}
func Jwt_EncodeHS384(payloadJson: String, secret: String) -> String {
let header: String = Jwt_MakeHeader(JwtAlg { tag: JwtAlg_HS384 });
return Jwt_Encode(header, payloadJson, JwtAlg { tag: JwtAlg_HS384 }, secret);
}
func Jwt_EncodeHS512(payloadJson: String, secret: String) -> String {
let header: String = Jwt_MakeHeader(JwtAlg { tag: JwtAlg_HS512 });
return Jwt_Encode(header, payloadJson, JwtAlg { tag: JwtAlg_HS512 }, secret);
}
func Jwt_EncodeRS256(payloadJson: String, pemPrivateKey: String) -> String {
let header: String = Jwt_MakeHeader(JwtAlg { tag: JwtAlg_RS256 });
return Jwt_Encode(header, payloadJson, JwtAlg { tag: JwtAlg_RS256 }, pemPrivateKey);
}
func Jwt_EncodeES256(payloadJson: String, pemPrivateKey: String) -> String {
let header: String = Jwt_MakeHeader(JwtAlg { tag: JwtAlg_ES256 });
return Jwt_Encode(header, payloadJson, JwtAlg { tag: JwtAlg_ES256 }, pemPrivateKey);
}
func Jwt_EncodeEdDSA(payloadJson: String, rawPrivKey: String) -> String {
let header: String = Jwt_MakeHeader(JwtAlg { tag: JwtAlg_EdDSA });
return Jwt_Encode(header, payloadJson, JwtAlg { tag: JwtAlg_EdDSA }, rawPrivKey);
}
}
+45 -45
View File
@@ -3,61 +3,61 @@
// =============================================================================
module Std::Crypto::Random {
import Std::Mem::{Alloc, Free};
import Std::Mem::{Alloc, Free};
extern func bux_random_bytes(buf: *void, len: int) -> int;
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_bytes_to_hex(data: *void, len: int) -> String;
extern func bux_random_bytes(buf: *void, len: int) -> int;
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_bytes_to_hex(data: *void, len: int) -> String;
// RandomBytes: returns n cryptographically secure random bytes as a raw string
func Random_Bytes(n: int) -> String {
if n <= 0 { return ""; }
let buf: *void = Alloc(n as uint);
if bux_random_bytes(buf, n) != 1 {
Free(buf);
return "";
// RandomBytes: returns n cryptographically secure random bytes as a raw string
func Random_Bytes(n: int) -> String {
if n <= 0 { return ""; }
let buf: *void = Alloc(n as uint);
if bux_random_bytes(buf, n) != 1 {
Free(buf);
return "";
}
// Return raw buffer as string (binary-safe)
return buf as String;
}
// Return raw buffer as string (binary-safe)
return buf as String;
}
// RandomHex: returns n random bytes as lowercase hex
func Random_Hex(n: int) -> String {
if n <= 0 { return ""; }
let buf: *void = Alloc(n as uint);
if bux_random_bytes(buf, n) != 1 {
// RandomHex: returns n random bytes as lowercase hex
func Random_Hex(n: int) -> String {
if n <= 0 { return ""; }
let buf: *void = Alloc(n as uint);
if bux_random_bytes(buf, n) != 1 {
Free(buf);
return "";
}
let result: String = bux_bytes_to_hex(buf, n);
Free(buf);
return "";
return result;
}
let result: String = bux_bytes_to_hex(buf, n);
Free(buf);
return result;
}
// RandomBase64: returns n random bytes as base64-encoded string
func Random_Base64(n: int) -> String {
if n <= 0 { return ""; }
let buf: *void = Alloc(n as uint);
if bux_random_bytes(buf, n) != 1 {
// RandomBase64: returns n random bytes as base64-encoded string
func Random_Base64(n: int) -> String {
if n <= 0 { return ""; }
let buf: *void = Alloc(n as uint);
if bux_random_bytes(buf, n) != 1 {
Free(buf);
return "";
}
let result: String = bux_base64_encode(buf as String, n);
Free(buf);
return "";
return result;
}
let result: String = bux_base64_encode(buf as String, n);
Free(buf);
return result;
}
// RandomUint32: returns a random 32-bit unsigned integer
func Random_Uint32() -> uint {
let buf: *void = Alloc(4);
if bux_random_bytes(buf, 4) != 1 {
// RandomUint32: returns a random 32-bit unsigned integer
func Random_Uint32() -> uint {
let buf: *void = Alloc(4);
if bux_random_bytes(buf, 4) != 1 {
Free(buf);
return 0;
}
// Interpret first 4 bytes as uint (native endian)
let ptr: *uint = buf as *uint;
let val: uint = *ptr;
Free(buf);
return 0;
return val;
}
// Interpret first 4 bytes as uint (native endian)
let ptr: *uint = buf as *uint;
let val: uint = *ptr;
Free(buf);
return val;
}
}
+81 -81
View File
@@ -3,88 +3,88 @@
// =============================================================================
module Std::Crypto::Rsa {
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len};
import Std::Mem::{Alloc, Free};
import Std::String::{String_Len};
// Extern declarations for the runtime C implementations
extern func bux_rsa_sign_sha256(key: String, keylen: int, data: String, datalen: int, outlen: *int) -> String;
extern func bux_rsa_sign_sha384(key: String, keylen: int, data: String, datalen: int, outlen: *int) -> String;
extern func bux_rsa_sign_sha512(key: String, keylen: int, data: String, datalen: int, outlen: *int) -> String;
extern func bux_rsa_verify_sha256(key: String, keylen: int, data: String, datalen: int, sig: String, siglen: int) -> int;
extern func bux_rsa_verify_sha384(key: String, keylen: int, data: String, datalen: int, sig: String, siglen: int) -> int;
extern func bux_rsa_verify_sha512(key: String, keylen: int, data: String, datalen: int, sig: String, siglen: int) -> int;
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_base64_decode(data: String, len: int, outlen: *int) -> String;
// Extern declarations for the runtime C implementations
extern func bux_rsa_sign_sha256(key: String, keylen: int, data: String, datalen: int, outlen: *int) -> String;
extern func bux_rsa_sign_sha384(key: String, keylen: int, data: String, datalen: int, outlen: *int) -> String;
extern func bux_rsa_sign_sha512(key: String, keylen: int, data: String, datalen: int, outlen: *int) -> String;
extern func bux_rsa_verify_sha256(key: String, keylen: int, data: String, datalen: int, sig: String, siglen: int) -> int;
extern func bux_rsa_verify_sha384(key: String, keylen: int, data: String, datalen: int, sig: String, siglen: int) -> int;
extern func bux_rsa_verify_sha512(key: String, keylen: int, data: String, datalen: int, sig: String, siglen: int) -> int;
extern func bux_base64_encode(data: String, len: int) -> String;
extern func bux_base64_decode(data: String, len: int, outlen: *int) -> String;
// --- RSA Sign ---
// --- RSA Sign ---
// Rsa_SignSha256: sign data with RSA private key (PEM format), returns raw signature
func Rsa_SignSha256(pemPrivateKey: String, data: String) -> String {
let siglen: int = 0;
return bux_rsa_sign_sha256(pemPrivateKey, String_Len(pemPrivateKey) as int, data, String_Len(data) as int, &siglen);
}
func Rsa_SignSha384(pemPrivateKey: String, data: String) -> String {
let siglen: int = 0;
return bux_rsa_sign_sha384(pemPrivateKey, String_Len(pemPrivateKey) as int, data, String_Len(data) as int, &siglen);
}
func Rsa_SignSha512(pemPrivateKey: String, data: String) -> String {
let siglen: int = 0;
return bux_rsa_sign_sha512(pemPrivateKey, String_Len(pemPrivateKey) as int, data, String_Len(data) as int, &siglen);
}
// Convenience: sign and return base64-encoded signature
func Rsa_SignSha256Base64(pemPrivateKey: String, data: String) -> String {
let raw: String = Rsa_SignSha256(pemPrivateKey, data);
return bux_base64_encode(raw, String_Len(raw) as int);
}
func Rsa_SignSha384Base64(pemPrivateKey: String, data: String) -> String {
let raw: String = Rsa_SignSha384(pemPrivateKey, data);
return bux_base64_encode(raw, String_Len(raw) as int);
}
func Rsa_SignSha512Base64(pemPrivateKey: String, data: String) -> String {
let raw: String = Rsa_SignSha512(pemPrivateKey, data);
return bux_base64_encode(raw, String_Len(raw) as int);
}
// --- RSA Verify ---
// Rsa_VerifySha256: verify raw signature against data with RSA public key (PEM)
// Returns true if signature is valid.
func Rsa_VerifySha256(pemPublicKey: String, data: String, signature: String) -> bool {
let r: int = bux_rsa_verify_sha256(pemPublicKey, String_Len(pemPublicKey) as int, data, String_Len(data) as int, signature, String_Len(signature) as int);
return r == 1;
}
func Rsa_VerifySha384(pemPublicKey: String, data: String, signature: String) -> bool {
let r: int = bux_rsa_verify_sha384(pemPublicKey, String_Len(pemPublicKey) as int, data, String_Len(data) as int, signature, String_Len(signature) as int);
return r == 1;
}
func Rsa_VerifySha512(pemPublicKey: String, data: String, signature: String) -> bool {
let r: int = bux_rsa_verify_sha512(pemPublicKey, String_Len(pemPublicKey) as int, data, String_Len(data) as int, signature, String_Len(signature) as int);
return r == 1;
}
// Convenience: verify base64-encoded signature
func Rsa_VerifySha256Base64(pemPublicKey: String, data: String, signatureB64: String) -> bool {
let outlen: int = 0;
let sig: String = bux_base64_decode(signatureB64, String_Len(signatureB64) as int, &outlen);
return Rsa_VerifySha256(pemPublicKey, data, sig);
}
func Rsa_VerifySha384Base64(pemPublicKey: String, data: String, signatureB64: String) -> bool {
let outlen: int = 0;
let sig: String = bux_base64_decode(signatureB64, String_Len(signatureB64) as int, &outlen);
return Rsa_VerifySha384(pemPublicKey, data, sig);
}
func Rsa_VerifySha512Base64(pemPublicKey: String, data: String, signatureB64: String) -> bool {
let outlen: int = 0;
let sig: String = bux_base64_decode(signatureB64, String_Len(signatureB64) as int, &outlen);
return Rsa_VerifySha512(pemPublicKey, data, sig);
}
// Rsa_SignSha256: sign data with RSA private key (PEM format), returns raw signature
func Rsa_SignSha256(pemPrivateKey: String, data: String) -> String {
let siglen: int = 0;
return bux_rsa_sign_sha256(pemPrivateKey, String_Len(pemPrivateKey) as int, data, String_Len(data) as int, &siglen);
}
func Rsa_SignSha384(pemPrivateKey: String, data: String) -> String {
let siglen: int = 0;
return bux_rsa_sign_sha384(pemPrivateKey, String_Len(pemPrivateKey) as int, data, String_Len(data) as int, &siglen);
}
func Rsa_SignSha512(pemPrivateKey: String, data: String) -> String {
let siglen: int = 0;
return bux_rsa_sign_sha512(pemPrivateKey, String_Len(pemPrivateKey) as int, data, String_Len(data) as int, &siglen);
}
// Convenience: sign and return base64-encoded signature
func Rsa_SignSha256Base64(pemPrivateKey: String, data: String) -> String {
let raw: String = Rsa_SignSha256(pemPrivateKey, data);
return bux_base64_encode(raw, String_Len(raw) as int);
}
func Rsa_SignSha384Base64(pemPrivateKey: String, data: String) -> String {
let raw: String = Rsa_SignSha384(pemPrivateKey, data);
return bux_base64_encode(raw, String_Len(raw) as int);
}
func Rsa_SignSha512Base64(pemPrivateKey: String, data: String) -> String {
let raw: String = Rsa_SignSha512(pemPrivateKey, data);
return bux_base64_encode(raw, String_Len(raw) as int);
}
// --- RSA Verify ---
// Rsa_VerifySha256: verify raw signature against data with RSA public key (PEM)
// Returns true if signature is valid.
func Rsa_VerifySha256(pemPublicKey: String, data: String, signature: String) -> bool {
let r: int = bux_rsa_verify_sha256(pemPublicKey, String_Len(pemPublicKey) as int, data, String_Len(data) as int, signature, String_Len(signature) as int);
return r == 1;
}
func Rsa_VerifySha384(pemPublicKey: String, data: String, signature: String) -> bool {
let r: int = bux_rsa_verify_sha384(pemPublicKey, String_Len(pemPublicKey) as int, data, String_Len(data) as int, signature, String_Len(signature) as int);
return r == 1;
}
func Rsa_VerifySha512(pemPublicKey: String, data: String, signature: String) -> bool {
let r: int = bux_rsa_verify_sha512(pemPublicKey, String_Len(pemPublicKey) as int, data, String_Len(data) as int, signature, String_Len(signature) as int);
return r == 1;
}
// Convenience: verify base64-encoded signature
func Rsa_VerifySha256Base64(pemPublicKey: String, data: String, signatureB64: String) -> bool {
let outlen: int = 0;
let sig: String = bux_base64_decode(signatureB64, String_Len(signatureB64) as int, &outlen);
return Rsa_VerifySha256(pemPublicKey, data, sig);
}
func Rsa_VerifySha384Base64(pemPublicKey: String, data: String, signatureB64: String) -> bool {
let outlen: int = 0;
let sig: String = bux_base64_decode(signatureB64, String_Len(signatureB64) as int, &outlen);
return Rsa_VerifySha384(pemPublicKey, data, sig);
}
func Rsa_VerifySha512Base64(pemPublicKey: String, data: String, signatureB64: String) -> bool {
let outlen: int = 0;
let sig: String = bux_base64_decode(signatureB64, String_Len(signatureB64) as int, &outlen);
return Rsa_VerifySha512(pemPublicKey, data, sig);
}
}
+15
View File
@@ -0,0 +1,15 @@
# greet
Demo package for the Bux registry (`config/registry.toml`).
```bash
bux add greet
bux install
```
```bux
func Main() -> int {
PrintLine(Greet_Hello("Bux"));
return 0;
}
```
+9
View File
@@ -0,0 +1,9 @@
[Package]
Name = "greet"
Version = "0.1.1"
Type = "lib"
Authors = ["Bux Core"]
License = "MIT"
[Build]
Output = "Lib"
+14
View File
@@ -0,0 +1,14 @@
// greet — demo registry package (E.1)
module Greet {
/// Return a greeting for `name`.
func Greet_Hello(name: String) -> String {
return String_Concat("Hello, ", String_Concat(name, "!"));
}
/// Return the package version string.
func Greet_Version() -> String {
return "0.1.1";
}
}
+16 -16
View File
@@ -1,23 +1,23 @@
// main.bux — Entry point for the Bux self-hosting compiler
module Main {
// C runtime for command-line args
extern func bux_argc() -> int;
extern func bux_argv(index: int) -> String;
extern func bux_alloc(size: uint) -> *void;
// C runtime for command-line args
extern func bux_argc() -> int;
extern func bux_argv(index: int) -> String;
extern func bux_alloc(size: uint) -> *void;
// Forward declaration from Cli module
func Cli_Run(args: *String, argCount: int) -> int;
// Forward declaration from Cli module
func Cli_Run(args: *String, argCount: int) -> int;
func Main() -> int {
let count: int = bux_argc();
// Allocate array of String pointers
let args: *String = bux_alloc(count as uint * 8) as *String;
var i: int = 0;
while i < count {
args[i] = bux_argv(i);
i = i + 1;
func Main() -> int {
let count: int = bux_argc();
// Allocate array of String pointers
let args: *String = bux_alloc(count as uint * 8) as *String;
var i: int = 0;
while i < count {
args[i] = bux_argv(i);
i = i + 1;
}
return Cli_Run(args, count);
}
return Cli_Run(args, count);
}
}
+434 -433
View File
@@ -1,437 +1,438 @@
// ast.bux — AST node types (Expr, Stmt, Decl, Pattern, TypeExpr)
module Ast {
// ---------------------------------------------------------------------------
// SourceLocation (inline for convenience)
// ---------------------------------------------------------------------------
struct SourceLoc {
line: uint32,
column: uint32,
}
// ---------------------------------------------------------------------------
// Token (lightweight inline)
// ---------------------------------------------------------------------------
struct AstToken {
kind: int,
text: String,
line: uint32,
column: uint32,
}
// ---------------------------------------------------------------------------
// TypeExpr — type expressions
// ---------------------------------------------------------------------------
const tekNamed: int = 0;
const tekPath: int = 1;
const tekSlice: int = 2;
const tekPointer: int = 3;
const tekRef: int = 7; // &T — shared reference
const tekMutRef: int = 8; // &mut T — mutable reference
const tekTuple: int = 4;
const tekSelf: int = 5;
const tekFunc: int = 6;
struct TypeExprList {
te: *TypeExpr,
next: *TypeExprList,
}
struct TypeExpr {
kind: int,
line: uint32,
column: uint32,
typeName: String, // for tekNamed / diagnostic name for tekFunc
pathStr: String, // for tekPath (segments joined with ::)
pathCount: int, // number of path segments
typeArgName0: String, // up to 2 type args
typeArgName1: String,
typeArgCount: int,
sliceElement: *TypeExpr, // for tekSlice
pointerPointee: *TypeExpr, // for tekPointer
funcParams: *TypeExprList, // for tekFunc
funcRet: *TypeExpr, // for tekFunc
funcParamCount: int, // for tekFunc
tupleElems: *TypeExprList, // for tekTuple
tupleCount: int, // for tekTuple
}
// ---------------------------------------------------------------------------
// Pattern — match patterns
// ---------------------------------------------------------------------------
const pkWildcard: int = 0;
const pkLiteral: int = 1;
const pkIdent: int = 2;
const pkRange: int = 3;
const pkEnum: int = 4;
const pkStruct: int = 5;
const pkTuple: int = 6;
const pkGuarded: int = 7; // `p if cond` — patChild1 = inner, patGuardExpr = condition
struct Pattern {
kind: int,
line: uint32,
column: uint32,
patIdent: String, // for pkIdent
patLitKind: int, // for pkLiteral (token kind)
patLitText: String, // for pkLiteral (token text)
patRangeInclusive: bool, // for pkRange
patEnumPath: String, // for pkEnum: "Enum::Variant"
patStructName: String, // for pkStruct (type name)
patFieldName: String, // for struct field entry: field name in Point { x: a }
patChild1: *Pattern, // range lo / nested / guarded inner
patChild2: *Pattern, // range hi / nested
patArgs: *Pattern, // pkEnum/pkTuple/pkStruct field list (head)
patNext: *Pattern, // next sibling in patArgs list
patGuardExpr: *Expr, // for pkGuarded: the `if` condition
}
// Match arm: pattern => body
struct MatchArm {
line: uint32,
column: uint32,
pattern: *Pattern,
body: *Expr,
next: *MatchArm,
}
// ---------------------------------------------------------------------------
// Expr — expressions (tagged union)
// ---------------------------------------------------------------------------
const ekLiteral: int = 0;
const ekIdent: int = 1;
const ekSelf: int = 2;
const ekPath: int = 3;
const ekSizeOf: int = 4;
const ekUnary: int = 5;
const ekPostfix: int = 6;
const ekBinary: int = 7;
const ekAssign: int = 8;
const ekTernary: int = 9;
const ekRange: int = 10;
const ekCall: int = 11;
const ekGenericCall: int = 12;
const ekIndex: int = 13;
const ekField: int = 14;
const ekStructInit: int = 15;
const ekSlice: int = 16;
const ekTuple: int = 17;
const ekCast: int = 18;
const ekIs: int = 19;
const ekTry: int = 20;
const ekUnwrap: int = 23;
const ekBlock: int = 21;
const ekMatch: int = 22;
const ekSpawn: int = 24;
const ekAwait: int = 25;
const ekStringInterp: int = 26;
const ekClosure: int = 27;
struct ExprList {
expr: *Expr,
next: *ExprList,
argName: String,
}
struct Expr {
kind: int,
line: uint32,
column: uint32,
// Common fields
strValue: String, // ident name, path segments, field name, callee
intValue: int, // operator kind, intrinsic kind
boolValue: bool, // range inclusive
tokKind: int, // literal token kind
tokText: String, // literal token text
// Children (up to 3 sub-expressions)
child1: *Expr, // left, operand, callee, cond, subject
child2: *Expr, // right, index, then, value
child3: *Expr, // else, third
// Extra references
refType: *TypeExpr, // cast type, is type, sizeof type
refBlock: *Block, // for ekBlock
// Generic call
genericCallee: String,
genericTypeArg0: String,
genericTypeArg1: String,
genericTypeArgCount: int,
// Struct init fields
structName: String,
structFieldCount: int,
// Closure params (for ekClosure)
closureParams: *Decl,
// Captures (for ekClosure)
captureCount: int,
captureName0: String,
captureName1: String,
captureName2: String,
captureName3: String,
captureName4: String,
captureName5: String,
captureName6: String,
captureName7: String,
captureType0: int,
captureType1: int,
captureType2: int,
captureType3: int,
captureType4: int,
captureType5: int,
captureType6: int,
captureType7: int,
// Call arguments (linked list for multi-arg support)
callArgs: *ExprList,
callArgCount: int,
// Match arms (for ekMatch)
matchArms: *MatchArm,
matchArmCount: int,
}
// ---------------------------------------------------------------------------
// Block — sequence of statements
// ---------------------------------------------------------------------------
struct Block {
line: uint32,
column: uint32,
stmtCount: int,
firstStmt: *Stmt,
lastStmt: *Stmt,
}
// ---------------------------------------------------------------------------
// Stmt — statements
// ---------------------------------------------------------------------------
const skExpr: int = 0;
const skLet: int = 1;
const skIf: int = 2;
const skWhile: int = 3;
const skDoWhile: int = 4;
const skLoop: int = 5;
const skFor: int = 6;
const skMatch: int = 7;
const skReturn: int = 8;
const skBreak: int = 9;
const skContinue: int = 10;
const skDecl: int = 11;
const skDefer: int = 12;
const skSwitch: int = 13;
struct ElseIf {
line: uint32;
column: uint32;
cond: *Expr;
block: *Block;
}
struct Stmt {
kind: int,
line: uint32,
column: uint32,
// Common fields
strValue: String, // let name, pattern ident, label, for var
boolValue: bool, // let mutable
// Children
child1: *Expr, // init expr, condition, iter expr, return value
child2: *Expr, // match subject
child3: *Expr, // extra
refStmtType: *TypeExpr, // let type annotation
refStmtPattern: *Pattern,// let pattern
refStmtDecl: *Decl, // for skDecl
refStmtBlock: *Block, // then/body block
refStmtElse: *Block, // else block
// Else-if chain
elseIfCount: int,
// Linked list
nextStmt: *Stmt,
}
// ---------------------------------------------------------------------------
// Decl — declarations
// ---------------------------------------------------------------------------
const dkFunc: int = 0;
const dkStruct: int = 1;
const dkEnum: int = 2;
const dkUnion: int = 3;
const dkInterface: int = 4;
const dkImpl: int = 5;
const dkModule: int = 6;
const dkUse: int = 7;
const dkConst: int = 8;
const dkTypeAlias: int = 9;
const dkExternFunc: int = 10;
const dkExternVar: int = 11;
struct Param {
line: uint32;
column: uint32;
name: String;
refParamType: *TypeExpr;
isVariadic: bool;
defaultExpr: *Expr;
}
struct StructField {
line: uint32;
column: uint32;
isPublic: bool;
name: String;
refFieldType: *TypeExpr;
}
struct EnumVariant {
line: uint32;
column: uint32;
name: String;
fieldCount: int;
fieldTypeName0: String;
fieldTypeName1: String;
}
struct Decl {
fieldCount: int,
fields: *StructField,
kind: int,
line: uint32,
column: uint32,
isPublic: bool,
isAsync: bool,
isChecked: int, // @[Checked] attribute (0/1)
isDrop: int, // @[Drop] attribute (0/1)
isRelease: int, // @[Release] attribute (0/1)
isConst: int, // const func (0/1)
// Names
strValue: String, // decl name
strValue2: String, // interface name, dll name, module path
// Type params (up to 2)
typeParam0: String,
typeParam1: String,
typeParamCount: int,
// Trait bounds for type params (e.g. <T: Comparable>)
typeParam0Bound: String,
typeParam1Bound: String,
// Params (for functions)
paramCount: int,
param0: Param,
param1: Param,
param2: Param,
param3: Param,
param4: Param,
param5: Param,
param6: Param,
param7: Param,
param8: Param,
retType: *TypeExpr,
// Body
refBody: *Block,
// Enum variants (up to 8)
variantCount: int,
variant0: EnumVariant,
variant1: EnumVariant,
variant2: EnumVariant,
variant3: EnumVariant,
variant4: EnumVariant,
variant5: EnumVariant,
variant6: EnumVariant,
variant7: EnumVariant,
variant8: EnumVariant,
// Impl methods (up to 4)
methodCount: int,
// Use/import
useKind: int,
usePath: String,
useNames: String, // joined names for multi-import
// Const
constType: *TypeExpr,
constValue: *Expr,
// Type alias
aliasType: *TypeExpr,
// Extern func
extFuncDll: String,
extFuncVariadic: bool,
extFuncRetType: *TypeExpr,
// Children
childDecl1: *Decl, // linked list of decls (for module items, impl methods)
childDecl2: *Decl,
// Struct fields (up to 256)
}
// ---------------------------------------------------------------------------
// Module — AST root
// ---------------------------------------------------------------------------
struct Module {
name: String,
path: String, // path segments joined
itemCount: int,
firstItem: *Decl,
}
// ---------------------------------------------------------------------------
// Constructor helpers
// ---------------------------------------------------------------------------
func Ast_MakeExpr(kind: int, line: uint32, col: uint32) -> Expr {
return Expr { kind: kind, line: line, column: col,
strValue: "", intValue: 0, boolValue: false,
tokKind: 0, tokText: "",
child1: null as *Expr, child2: null as *Expr, child3: null as *Expr,
refType: null as *TypeExpr, refBlock: null as *Block,
genericCallee: "", genericTypeArg0: "", genericTypeArg1: "", genericTypeArgCount: 0,
structName: "", structFieldCount: 0,
callArgs: null as *ExprList, callArgCount: 0 };
}
func Ast_MakeIdent(name: String, line: uint32, col: uint32) -> Expr {
var e: Expr = Ast_MakeExpr(ekIdent, line, col);
e.strValue = name;
return e;
}
func Ast_MakeLiteral(tokKind: int, text: String, line: uint32, col: uint32) -> Expr {
var e: Expr = Ast_MakeExpr(ekLiteral, line, col);
e.tokKind = tokKind;
e.tokText = text;
return e;
}
func Ast_MakeBinary(op: int, left: *Expr, right: *Expr, line: uint32, col: uint32) -> Expr {
var e: Expr = Ast_MakeExpr(ekBinary, line, col);
e.intValue = op;
e.child1 = left;
e.child2 = right;
return e;
}
func Ast_MakeCall(callee: *Expr, line: uint32, col: uint32) -> Expr {
var e: Expr = Ast_MakeExpr(ekCall, line, col);
e.child1 = callee;
return e;
}
func Ast_MakeStmt(kind: int, line: uint32, col: uint32) -> Stmt {
return Stmt { kind: kind, line: line, column: col,
strValue: "", boolValue: false,
child1: null as *Expr, child2: null as *Expr, child3: null as *Expr,
refStmtType: null as *TypeExpr, refStmtPattern: null as *Pattern,
refStmtDecl: null as *Decl, refStmtBlock: null as *Block, refStmtElse: null as *Block,
elseIfCount: 0 };
}
func Ast_MakeDecl(kind: int, line: uint32, col: uint32) -> Decl {
return Decl { kind: kind, line: line, column: col, isPublic: false,
isAsync: false, isChecked: 0, isDrop: 0, isRelease: 0, isConst: 0,
strValue: "", strValue2: "",
typeParam0: "", typeParam1: "", typeParamCount: 0,
typeParam0Bound: "", typeParam1Bound: ""
,
paramCount: 0,
retType: null as *TypeExpr,
refBody: null as *Block,
fieldCount: 0,
variantCount: 0,
methodCount: 0,
useKind: 0, usePath: "", useNames: "",
constType: null as *TypeExpr, constValue: null as *Expr,
aliasType: null as *TypeExpr,
extFuncDll: "", extFuncVariadic: false, extFuncRetType: null as *TypeExpr,
childDecl1: null as *Decl, childDecl2: null as *Decl };
}
// ---------------------------------------------------------------------------
// SourceLocation (inline for convenience)
// ---------------------------------------------------------------------------
struct SourceLoc {
line: uint32,
column: uint32,
}
// ---------------------------------------------------------------------------
// Token (lightweight inline)
// ---------------------------------------------------------------------------
struct AstToken {
kind: int,
text: String,
line: uint32,
column: uint32,
}
// ---------------------------------------------------------------------------
// TypeExpr — type expressions
// ---------------------------------------------------------------------------
const tekNamed: int = 0;
const tekPath: int = 1;
const tekSlice: int = 2;
const tekPointer: int = 3;
const tekRef: int = 7; // &T — shared reference
const tekMutRef: int = 8; // &mut T — mutable reference
const tekTuple: int = 4;
const tekSelf: int = 5;
const tekFunc: int = 6;
struct TypeExprList {
te: *TypeExpr,
next: *TypeExprList,
}
struct TypeExpr {
kind: int,
line: uint32,
column: uint32,
typeName: String, // for tekNamed / diagnostic name for tekFunc
pathStr: String, // for tekPath (segments joined with ::)
pathCount: int, // number of path segments
typeArgName0: String, // up to 2 type args
typeArgName1: String,
typeArgCount: int,
sliceElement: *TypeExpr, // for tekSlice
pointerPointee: *TypeExpr, // for tekPointer / tekRef / tekMutRef
refLifetime: String, // for tekRef / tekMutRef: "'a" or "" (elided)
funcParams: *TypeExprList, // for tekFunc
funcRet: *TypeExpr, // for tekFunc
funcParamCount: int, // for tekFunc
tupleElems: *TypeExprList, // for tekTuple
tupleCount: int, // for tekTuple
}
// ---------------------------------------------------------------------------
// Pattern — match patterns
// ---------------------------------------------------------------------------
const pkWildcard: int = 0;
const pkLiteral: int = 1;
const pkIdent: int = 2;
const pkRange: int = 3;
const pkEnum: int = 4;
const pkStruct: int = 5;
const pkTuple: int = 6;
const pkGuarded: int = 7; // `p if cond` — patChild1 = inner, patGuardExpr = condition
struct Pattern {
kind: int,
line: uint32,
column: uint32,
patIdent: String, // for pkIdent
patLitKind: int, // for pkLiteral (token kind)
patLitText: String, // for pkLiteral (token text)
patRangeInclusive: bool, // for pkRange
patEnumPath: String, // for pkEnum: "Enum::Variant"
patStructName: String, // for pkStruct (type name)
patFieldName: String, // for struct field entry: field name in Point { x: a }
patChild1: *Pattern, // range lo / nested / guarded inner
patChild2: *Pattern, // range hi / nested
patArgs: *Pattern, // pkEnum/pkTuple/pkStruct field list (head)
patNext: *Pattern, // next sibling in patArgs list
patGuardExpr: *Expr, // for pkGuarded: the `if` condition
}
// Match arm: pattern => body
struct MatchArm {
line: uint32,
column: uint32,
pattern: *Pattern,
body: *Expr,
next: *MatchArm,
}
// ---------------------------------------------------------------------------
// Expr — expressions (tagged union)
// ---------------------------------------------------------------------------
const ekLiteral: int = 0;
const ekIdent: int = 1;
const ekSelf: int = 2;
const ekPath: int = 3;
const ekSizeOf: int = 4;
const ekUnary: int = 5;
const ekPostfix: int = 6;
const ekBinary: int = 7;
const ekAssign: int = 8;
const ekTernary: int = 9;
const ekRange: int = 10;
const ekCall: int = 11;
const ekGenericCall: int = 12;
const ekIndex: int = 13;
const ekField: int = 14;
const ekStructInit: int = 15;
const ekSlice: int = 16;
const ekTuple: int = 17;
const ekCast: int = 18;
const ekIs: int = 19;
const ekTry: int = 20;
const ekUnwrap: int = 23;
const ekBlock: int = 21;
const ekMatch: int = 22;
const ekSpawn: int = 24;
const ekAwait: int = 25;
const ekStringInterp: int = 26;
const ekClosure: int = 27;
struct ExprList {
expr: *Expr,
next: *ExprList,
argName: String,
}
struct Expr {
kind: int,
line: uint32,
column: uint32,
// Common fields
strValue: String, // ident name, path segments, field name, callee
intValue: int, // operator kind, intrinsic kind
boolValue: bool, // range inclusive
tokKind: int, // literal token kind
tokText: String, // literal token text
// Children (up to 3 sub-expressions)
child1: *Expr, // left, operand, callee, cond, subject
child2: *Expr, // right, index, then, value
child3: *Expr, // else, third
// Extra references
refType: *TypeExpr, // cast type, is type, sizeof type
refBlock: *Block, // for ekBlock
// Generic call
genericCallee: String,
genericTypeArg0: String,
genericTypeArg1: String,
genericTypeArgCount: int,
// Struct init fields
structName: String,
structFieldCount: int,
// Closure params (for ekClosure)
closureParams: *Decl,
// Captures (for ekClosure)
captureCount: int,
captureName0: String,
captureName1: String,
captureName2: String,
captureName3: String,
captureName4: String,
captureName5: String,
captureName6: String,
captureName7: String,
captureType0: int,
captureType1: int,
captureType2: int,
captureType3: int,
captureType4: int,
captureType5: int,
captureType6: int,
captureType7: int,
// Call arguments (linked list for multi-arg support)
callArgs: *ExprList,
callArgCount: int,
// Match arms (for ekMatch)
matchArms: *MatchArm,
matchArmCount: int,
}
// ---------------------------------------------------------------------------
// Block — sequence of statements
// ---------------------------------------------------------------------------
struct Block {
line: uint32,
column: uint32,
stmtCount: int,
firstStmt: *Stmt,
lastStmt: *Stmt,
}
// ---------------------------------------------------------------------------
// Stmt — statements
// ---------------------------------------------------------------------------
const skExpr: int = 0;
const skLet: int = 1;
const skIf: int = 2;
const skWhile: int = 3;
const skDoWhile: int = 4;
const skLoop: int = 5;
const skFor: int = 6;
const skMatch: int = 7;
const skReturn: int = 8;
const skBreak: int = 9;
const skContinue: int = 10;
const skDecl: int = 11;
const skDefer: int = 12;
const skSwitch: int = 13;
struct ElseIf {
line: uint32;
column: uint32;
cond: *Expr;
block: *Block;
}
struct Stmt {
kind: int,
line: uint32,
column: uint32,
// Common fields
strValue: String, // let name, pattern ident, label, for var
boolValue: bool, // let mutable
// Children
child1: *Expr, // init expr, condition, iter expr, return value
child2: *Expr, // match subject
child3: *Expr, // extra
refStmtType: *TypeExpr, // let type annotation
refStmtPattern: *Pattern,// let pattern
refStmtDecl: *Decl, // for skDecl
refStmtBlock: *Block, // then/body block
refStmtElse: *Block, // else block
// Else-if chain
elseIfCount: int,
// Linked list
nextStmt: *Stmt,
}
// ---------------------------------------------------------------------------
// Decl — declarations
// ---------------------------------------------------------------------------
const dkFunc: int = 0;
const dkStruct: int = 1;
const dkEnum: int = 2;
const dkUnion: int = 3;
const dkInterface: int = 4;
const dkImpl: int = 5;
const dkModule: int = 6;
const dkUse: int = 7;
const dkConst: int = 8;
const dkTypeAlias: int = 9;
const dkExternFunc: int = 10;
const dkExternVar: int = 11;
struct Param {
line: uint32;
column: uint32;
name: String;
refParamType: *TypeExpr;
isVariadic: bool;
defaultExpr: *Expr;
}
struct StructField {
line: uint32;
column: uint32;
isPublic: bool;
name: String;
refFieldType: *TypeExpr;
}
struct EnumVariant {
line: uint32;
column: uint32;
name: String;
fieldCount: int;
fieldTypeName0: String;
fieldTypeName1: String;
}
struct Decl {
fieldCount: int,
fields: *StructField,
kind: int,
line: uint32,
column: uint32,
isPublic: bool,
isAsync: bool,
isChecked: int, // @[Checked] attribute (0/1)
isDrop: int, // @[Drop] attribute (0/1)
isRelease: int, // @[Release] attribute (0/1)
isConst: int, // const func (0/1)
// Names
strValue: String, // decl name
strValue2: String, // interface name, dll name, module path
// Type params (up to 2)
typeParam0: String,
typeParam1: String,
typeParamCount: int,
// Trait bounds for type params (e.g. <T: Comparable>)
typeParam0Bound: String,
typeParam1Bound: String,
// Params (for functions)
paramCount: int,
param0: Param,
param1: Param,
param2: Param,
param3: Param,
param4: Param,
param5: Param,
param6: Param,
param7: Param,
param8: Param,
retType: *TypeExpr,
// Body
refBody: *Block,
// Enum variants (up to 8)
variantCount: int,
variant0: EnumVariant,
variant1: EnumVariant,
variant2: EnumVariant,
variant3: EnumVariant,
variant4: EnumVariant,
variant5: EnumVariant,
variant6: EnumVariant,
variant7: EnumVariant,
variant8: EnumVariant,
// Impl methods (up to 4)
methodCount: int,
// Use/import
useKind: int,
usePath: String,
useNames: String, // joined names for multi-import
// Const
constType: *TypeExpr,
constValue: *Expr,
// Type alias
aliasType: *TypeExpr,
// Extern func
extFuncDll: String,
extFuncVariadic: bool,
extFuncRetType: *TypeExpr,
// Children
childDecl1: *Decl, // linked list of decls (for module items, impl methods)
childDecl2: *Decl,
// Struct fields (up to 256)
}
// ---------------------------------------------------------------------------
// Module — AST root
// ---------------------------------------------------------------------------
struct Module {
name: String,
path: String, // path segments joined
itemCount: int,
firstItem: *Decl,
}
// ---------------------------------------------------------------------------
// Constructor helpers
// ---------------------------------------------------------------------------
func Ast_MakeExpr(kind: int, line: uint32, col: uint32) -> Expr {
return Expr { kind: kind, line: line, column: col,
strValue: "", intValue: 0, boolValue: false,
tokKind: 0, tokText: "",
child1: null as *Expr, child2: null as *Expr, child3: null as *Expr,
refType: null as *TypeExpr, refBlock: null as *Block,
genericCallee: "", genericTypeArg0: "", genericTypeArg1: "", genericTypeArgCount: 0,
structName: "", structFieldCount: 0,
callArgs: null as *ExprList, callArgCount: 0 };
}
func Ast_MakeIdent(name: String, line: uint32, col: uint32) -> Expr {
var e: Expr = Ast_MakeExpr(ekIdent, line, col);
e.strValue = name;
return e;
}
func Ast_MakeLiteral(tokKind: int, text: String, line: uint32, col: uint32) -> Expr {
var e: Expr = Ast_MakeExpr(ekLiteral, line, col);
e.tokKind = tokKind;
e.tokText = text;
return e;
}
func Ast_MakeBinary(op: int, left: *Expr, right: *Expr, line: uint32, col: uint32) -> Expr {
var e: Expr = Ast_MakeExpr(ekBinary, line, col);
e.intValue = op;
e.child1 = left;
e.child2 = right;
return e;
}
func Ast_MakeCall(callee: *Expr, line: uint32, col: uint32) -> Expr {
var e: Expr = Ast_MakeExpr(ekCall, line, col);
e.child1 = callee;
return e;
}
func Ast_MakeStmt(kind: int, line: uint32, col: uint32) -> Stmt {
return Stmt { kind: kind, line: line, column: col,
strValue: "", boolValue: false,
child1: null as *Expr, child2: null as *Expr, child3: null as *Expr,
refStmtType: null as *TypeExpr, refStmtPattern: null as *Pattern,
refStmtDecl: null as *Decl, refStmtBlock: null as *Block, refStmtElse: null as *Block,
elseIfCount: 0 };
}
func Ast_MakeDecl(kind: int, line: uint32, col: uint32) -> Decl {
return Decl { kind: kind, line: line, column: col, isPublic: false,
isAsync: false, isChecked: 0, isDrop: 0, isRelease: 0, isConst: 0,
strValue: "", strValue2: "",
typeParam0: "", typeParam1: "", typeParamCount: 0,
typeParam0Bound: "", typeParam1Bound: ""
,
paramCount: 0,
retType: null as *TypeExpr,
refBody: null as *Block,
fieldCount: 0,
variantCount: 0,
methodCount: 0,
useKind: 0, usePath: "", useNames: "",
constType: null as *TypeExpr, constValue: null as *Expr,
aliasType: null as *TypeExpr,
extFuncDll: "", extFuncVariadic: false, extFuncRetType: null as *TypeExpr,
childDecl1: null as *Decl, childDecl2: null as *Decl };
}
}
+1534 -1534
View File
File diff suppressed because it is too large Load Diff
+463 -186
View File
@@ -2,144 +2,144 @@
// Wires together: Lexer → Parser → Sema → HirLower → CBackend
module Cli {
extern func PrintLine(s: String);
extern func Print(s: String);
extern func bux_read_file(path: String) -> String;
extern func bux_write_file(path: String, content: String) -> bool;
extern func bux_file_exists(path: String) -> int;
extern func bux_dir_exists(path: String) -> int;
extern func bux_getcwd() -> String;
extern func bux_path_join(a: String, b: String) -> String;
extern func bux_path_parent(path: String) -> String;
extern func bux_mkdir_if_needed(path: String) -> int;
extern func bux_run_nim(nim_file: String, out_bin: String) -> int;
extern func bux_list_dir(dir: String, ext: String, out_count: *int) -> *String;
extern func bux_system(cmd: String) -> int;
extern func bux_getenv(name: String) -> String;
extern func bux_setenv(name: String, value: String) -> int;
extern func bux_strlen(s: String) -> uint;
extern func bux_str_slice(s: String, start: uint, len: uint) -> String;
extern func PrintLine(s: String);
extern func Print(s: String);
extern func bux_read_file(path: String) -> String;
extern func bux_write_file(path: String, content: String) -> bool;
extern func bux_file_exists(path: String) -> int;
extern func bux_dir_exists(path: String) -> int;
extern func bux_getcwd() -> String;
extern func bux_path_join(a: String, b: String) -> String;
extern func bux_path_parent(path: String) -> String;
extern func bux_mkdir_if_needed(path: String) -> int;
extern func bux_run_nim(nim_file: String, out_bin: String) -> int;
extern func bux_list_dir(dir: String, ext: String, out_count: *int) -> *String;
extern func bux_system(cmd: String) -> int;
extern func bux_getenv(name: String) -> String;
extern func bux_setenv(name: String, value: String) -> int;
extern func bux_strlen(s: String) -> uint;
extern func bux_str_slice(s: String, start: uint, len: uint) -> String;
func ReadFile(path: String) -> String {
return bux_read_file(path);
}
func WriteFile(path: String, content: String) -> bool {
return bux_write_file(path, content);
}
func FileExists(path: String) -> bool {
return bux_file_exists(path) != 0;
}
func DirExists(path: String) -> bool {
return bux_dir_exists(path) != 0;
}
// ---------------------------------------------------------------------------
// Diagnostic formatting (Rust-style errors with snippets)
// ---------------------------------------------------------------------------
struct Diagnostic {
message: String;
line: uint32;
column: uint32;
severity: int;
}
/* Read a single line from a file (1-based). Returns "" on error or EOF. */
func Diagnostic_GetLine(path: String, lineNum: uint32) -> String {
let content: String = bux_read_file(path);
if String_Eq(content, "") { return ""; }
/* bux_str_split_part uses 0-based index */
return bux_str_split_part(content, "\n", lineNum - 1);
}
/* Simple substring check for help hints */
func Diagnostic_MsgContains(msg: String, needle: String) -> bool {
return bux_str_contains(msg, needle) != 0;
}
/* Actionable help for common error messages */
func Diagnostic_Hint(msg: String) -> String {
if Diagnostic_MsgContains(msg, "cannot assign") {
return "ensure the right-hand side type matches the left-hand side";
func ReadFile(path: String) -> String {
return bux_read_file(path);
}
if Diagnostic_MsgContains(msg, "undeclared identifier") {
return "check the spelling, or import the symbol from the right module";
}
if Diagnostic_MsgContains(msg, "too few arguments") {
return "compare the call with the function's parameter list";
}
if Diagnostic_MsgContains(msg, "too many arguments") {
return "compare the call with the function's parameter list";
}
if Diagnostic_MsgContains(msg, "use of moved value") {
return "the value was moved; clone it or restructure ownership";
}
if Diagnostic_MsgContains(msg, "expected expression") {
return "the previous statement may be incomplete (missing value or ';')";
}
if Diagnostic_MsgContains(msg, "duplicate symbol") {
return "rename one of the definitions or remove the duplicate";
}
return "";
}
/* Print a diagnostic in Rust-style format:
* error: <message>
* --> <path>:<line>:<col>
* |
* 42 | <source_line>
* | <spaces>^
* = help: <hint>
*/
func Diagnostic_Print(diag: *Diagnostic, sourcePath: String) {
/* Severity prefix */
if diag.severity == 0 {
Print("error: ");
} else if diag.severity == 1 {
Print("warning: ");
} else {
Print("note: ");
func WriteFile(path: String, content: String) -> bool {
return bux_write_file(path, content);
}
PrintLine(diag.message);
/* Location header */
Print(" --> ");
Print(sourcePath);
Print(":");
PrintInt(diag.line as int64);
Print(":");
PrintInt(diag.column as int64);
PrintLine("");
func FileExists(path: String) -> bool {
return bux_file_exists(path) != 0;
}
/* Source snippet */
let lineText: String = Diagnostic_GetLine(sourcePath, diag.line);
if !String_Eq(lineText, "") {
let lineNumStr: String = String_FromInt(diag.line as int64);
func DirExists(path: String) -> bool {
return bux_dir_exists(path) != 0;
}
Print(" |");
PrintLine("");
Print(" ");
Print(lineNumStr);
Print(" | ");
PrintLine(lineText);
// ---------------------------------------------------------------------------
// Diagnostic formatting (Rust-style errors with snippets)
// ---------------------------------------------------------------------------
/* Underline (multi-char for identifiers/string tokens) */
Print(" | ");
var i: uint32 = 0;
while i < diag.column - 1 && i < 120 {
Print(" ");
i = i + 1;
struct Diagnostic {
message: String;
line: uint32;
column: uint32;
severity: int;
}
/* Read a single line from a file (1-based). Returns "" on error or EOF. */
func Diagnostic_GetLine(path: String, lineNum: uint32) -> String {
let content: String = bux_read_file(path);
if String_Eq(content, "") { return ""; }
/* bux_str_split_part uses 0-based index */
return bux_str_split_part(content, "\n", lineNum - 1);
}
/* Simple substring check for help hints */
func Diagnostic_MsgContains(msg: String, needle: String) -> bool {
return bux_str_contains(msg, needle) != 0;
}
/* Actionable help for common error messages */
func Diagnostic_Hint(msg: String) -> String {
if Diagnostic_MsgContains(msg, "cannot assign") {
return "ensure the right-hand side type matches the left-hand side";
}
/* Estimate token length from the source line */
var ulen: uint = 1;
let col0: uint = diag.column - 1;
let lineLen: uint = String_Len(lineText);
if col0 < lineLen {
let first: String = String_Chars(lineText, col0);
if String_Eq(first, "\"") || String_Eq(first, "`") || String_Eq(first, "'") {
if Diagnostic_MsgContains(msg, "undeclared identifier") {
return "check the spelling, or import the symbol from the right module";
}
if Diagnostic_MsgContains(msg, "too few arguments") {
return "compare the call with the function's parameter list";
}
if Diagnostic_MsgContains(msg, "too many arguments") {
return "compare the call with the function's parameter list";
}
if Diagnostic_MsgContains(msg, "use of moved value") {
return "the value was moved; clone it or restructure ownership";
}
if Diagnostic_MsgContains(msg, "expected expression") {
return "the previous statement may be incomplete (missing value or ';')";
}
if Diagnostic_MsgContains(msg, "duplicate symbol") {
return "rename one of the definitions or remove the duplicate";
}
return "";
}
/* Print a diagnostic in Rust-style format:
* error: <message>
* --> <path>:<line>:<col>
* |
* 42 | <source_line>
* | <spaces>^
* = help: <hint>
*/
func Diagnostic_Print(diag: *Diagnostic, sourcePath: String) {
/* Severity prefix */
if diag.severity == 0 {
Print("error: ");
} else if diag.severity == 1 {
Print("warning: ");
} else {
Print("note: ");
}
PrintLine(diag.message);
/* Location header */
Print(" --> ");
Print(sourcePath);
Print(":");
PrintInt(diag.line as int64);
Print(":");
PrintInt(diag.column as int64);
PrintLine("");
/* Source snippet */
let lineText: String = Diagnostic_GetLine(sourcePath, diag.line);
if !String_Eq(lineText, "") {
let lineNumStr: String = String_FromInt(diag.line as int64);
Print(" |");
PrintLine("");
Print(" ");
Print(lineNumStr);
Print(" | ");
PrintLine(lineText);
/* Underline (multi-char for identifiers/string tokens) */
Print(" | ");
var i: uint32 = 0;
while i < diag.column - 1 && i < 120 {
Print(" ");
i = i + 1;
}
/* Estimate token length from the source line */
var ulen: uint = 1;
let col0: uint = diag.column - 1;
let lineLen: uint = String_Len(lineText);
if col0 < lineLen {
let first: String = String_Chars(lineText, col0);
if String_Eq(first, "\"") || String_Eq(first, "`") || String_Eq(first, "'") {
var j: uint = col0 + 1;
while j < lineLen {
let cj: String = String_Chars(lineText, j);
@@ -922,12 +922,184 @@ func Cli_Fetch() -> int {
}
// ---------------------------------------------------------------------------
// Fmt command — format source files
// Doc command — Markdown from /// comments (D.4)
// ---------------------------------------------------------------------------
func Cli_Fmt(dir: String) -> int {
// If dir is a file, format just that file
func Cli_DocIsDeclStart(line: String) -> bool {
if String_StartsWith(line, "func ") { return true; }
if String_StartsWith(line, "pub func ") { return true; }
if String_StartsWith(line, "extern func ") { return true; }
if String_StartsWith(line, "const func ") { return true; }
if String_StartsWith(line, "async func ") { return true; }
if String_StartsWith(line, "struct ") { return true; }
if String_StartsWith(line, "pub struct ") { return true; }
if String_StartsWith(line, "enum ") { return true; }
if String_StartsWith(line, "interface ") { return true; }
if String_StartsWith(line, "module ") { return true; }
if String_StartsWith(line, "type ") { return true; }
return false;
}
func Cli_DocExtractName(line: String) -> String {
// Skip leading keywords
var s: String = line;
if String_StartsWith(s, "pub ") { s = bux_str_slice(s, 4, bux_strlen(s) - 4); }
if String_StartsWith(s, "extern ") { s = bux_str_slice(s, 7, bux_strlen(s) - 7); }
if String_StartsWith(s, "const ") { s = bux_str_slice(s, 6, bux_strlen(s) - 6); }
if String_StartsWith(s, "async ") { s = bux_str_slice(s, 6, bux_strlen(s) - 6); }
if String_StartsWith(s, "func ") { s = bux_str_slice(s, 5, bux_strlen(s) - 5); }
else if String_StartsWith(s, "struct ") { s = bux_str_slice(s, 7, bux_strlen(s) - 7); }
else if String_StartsWith(s, "enum ") { s = bux_str_slice(s, 5, bux_strlen(s) - 5); }
else if String_StartsWith(s, "interface ") { s = bux_str_slice(s, 10, bux_strlen(s) - 10); }
else if String_StartsWith(s, "module ") { s = bux_str_slice(s, 7, bux_strlen(s) - 7); }
else if String_StartsWith(s, "type ") { s = bux_str_slice(s, 5, bux_strlen(s) - 5); }
// Take until space, <, (, {, :, ;
var i: uint = 0;
let n: uint = bux_strlen(s);
while i < n {
let ch: String = bux_str_slice(s, i, 1);
if String_Eq(ch, " ") || String_Eq(ch, "<") || String_Eq(ch, "(") ||
String_Eq(ch, "{") || String_Eq(ch, ":") || String_Eq(ch, ";") {
break;
}
i = i + 1;
}
if i == 0 { return s; }
return bux_str_slice(s, 0, i);
}
func Cli_DocProcessFile(path: String, outSb: *StringBuilder) -> int {
let source: String = ReadFile(path);
if source == null as String || String_Eq(source, "") { return 0; }
var itemCount: int = 0;
var pending: String = "";
var hasPending: bool = false;
let lineCount: uint = bux_str_split_count(source, "\n");
// Drop trailing empty split artifact
var nLines: uint = lineCount;
if nLines > 0 {
let last: String = bux_str_split_part(source, "\n", nLines - 1);
if String_Eq(last, "") { nLines = nLines - 1; }
}
var li: uint = 0;
var wroteHeader: bool = false;
while li < nLines {
let raw: String = bux_str_split_part(source, "\n", li);
let line: String = String_Trim(raw);
if String_StartsWith(line, "///") {
var body: String = bux_str_slice(line, 3, bux_strlen(line) - 3);
if String_StartsWith(body, " ") {
body = bux_str_slice(body, 1, bux_strlen(body) - 1);
}
if hasPending {
pending = String_Concat(pending, String_Concat("\n", body));
} else {
pending = body;
hasPending = true;
}
li = li + 1;
continue;
}
if String_Eq(line, "") || String_StartsWith(line, "@[") {
li = li + 1;
continue;
}
if hasPending && Cli_DocIsDeclStart(line) {
if !wroteHeader {
StringBuilder_Append(outSb, "## `");
StringBuilder_Append(outSb, Cli_FileNameFromPath(path));
StringBuilder_Append(outSb, "`\n\n");
StringBuilder_Append(outSb, "_Source: `");
StringBuilder_Append(outSb, path);
StringBuilder_Append(outSb, "`_\n\n");
wroteHeader = true;
}
let name: String = Cli_DocExtractName(line);
StringBuilder_Append(outSb, "### `");
StringBuilder_Append(outSb, name);
StringBuilder_Append(outSb, "`\n\n");
StringBuilder_Append(outSb, "```bux\n");
StringBuilder_Append(outSb, line);
StringBuilder_Append(outSb, "\n```\n\n");
StringBuilder_Append(outSb, pending);
StringBuilder_Append(outSb, "\n\n");
itemCount = itemCount + 1;
hasPending = false;
pending = "";
li = li + 1;
continue;
}
if String_StartsWith(line, "//") {
li = li + 1;
continue;
}
// Other code clears pending
hasPending = false;
pending = "";
li = li + 1;
}
return itemCount;
}
func Cli_Doc(dir: String, outPath: String) -> int {
var sb: StringBuilder = StringBuilder_NewCap(16384);
StringBuilder_Append(&sb, "# API Reference\n\n");
StringBuilder_Append(&sb, "Generated by `bux doc` from `///` documentation comments.\n\n");
var total: int = 0;
if FileExists(dir) {
total = total + Cli_DocProcessFile(dir, &sb);
} else if DirExists(dir) {
var fileCount: int = 0;
let files: *String = bux_list_dir(dir, ".bux", &fileCount);
var i: int = 0;
while i < fileCount {
total = total + Cli_DocProcessFile(files[i], &sb);
i = i + 1;
}
} else {
Print("Error: path not found: ");
PrintLine(dir);
StringBuilder_Free(&sb);
return 1;
}
let md: String = StringBuilder_Build(&sb);
StringBuilder_Free(&sb);
if String_Eq(outPath, "") {
Print(md);
} else {
if !WriteFile(outPath, md) {
Print("Error: cannot write ");
PrintLine(outPath);
return 1;
}
Print("Wrote ");
PrintInt(total as int64);
Print(" documented items → ");
PrintLine(outPath);
}
if total == 0 {
PrintLine("warning: no /// documented declarations found");
}
return 0;
}
// ---------------------------------------------------------------------------
// Fmt command — format source files (write or --check for CI)
// ---------------------------------------------------------------------------
func Cli_Fmt(dir: String, checkOnly: bool) -> int {
// If dir is a file, format/check just that file
if FileExists(dir) {
if checkOnly {
if Fmt_CheckFile(dir) == 0 {
Print(" ok ");
PrintLine(dir);
return 0;
}
Print(" would reformat ");
PrintLine(dir);
return 1;
}
Print("Formatting ");
PrintLine(dir);
if Fmt_FormatFile(dir) {
@@ -936,8 +1108,12 @@ func Cli_Fmt(dir: String) -> int {
}
return 1;
}
// Otherwise format all .bux files in directory
Print("Formatting ");
// Otherwise format/check all .bux files in directory
if checkOnly {
Print("Checking format in ");
} else {
Print("Formatting ");
}
PrintLine(dir);
var fileCount: int = 0;
let files: *String = bux_list_dir(dir, ".bux", &fileCount);
@@ -947,14 +1123,34 @@ func Cli_Fmt(dir: String) -> int {
}
var i: int = 0;
var okCount: int = 0;
var changeCount: int = 0;
while i < fileCount {
Print(" ");
PrintLine(files[i]);
if Fmt_FormatFile(files[i]) {
okCount = okCount + 1;
if checkOnly {
if Fmt_CheckFile(files[i]) == 0 {
okCount = okCount + 1;
} else {
Print(" would reformat ");
PrintLine(files[i]);
changeCount = changeCount + 1;
}
} else {
Print(" ");
PrintLine(files[i]);
if Fmt_FormatFile(files[i]) {
okCount = okCount + 1;
}
}
i = i + 1;
}
if checkOnly {
Print("fmt --check: ");
PrintInt(changeCount as int64);
Print(" would reformat, ");
PrintInt(okCount as int64);
PrintLine(" ok");
if changeCount > 0 { return 1; }
return 0;
}
Print("Formatted "); PrintInt(okCount as int64); Print("/"); PrintInt(fileCount as int64); PrintLine(" files");
return 0;
}
@@ -985,32 +1181,40 @@ func Cli_StripExtension(name: String) -> String {
return name;
}
func Cli_Test(projectDir: String) -> int {
func Cli_Test(projectDir: String, filter: String) -> int {
Print("Testing project: ");
PrintLine(projectDir);
// Build and run the project's own Main first
let mainRc: int = Cli_BuildProject(projectDir, "", false);
if mainRc != 0 {
PrintLine("Main test build failed");
return mainRc;
if !String_Eq(filter, "") {
Print("Filter: ");
PrintLine(filter);
}
let man: Manifest = Manifest_Load(bux_path_join(projectDir, "bux.toml"));
var mainName: String = man.name;
if String_Eq(mainName, "") { mainName = "bux_out"; }
let mainBin: String = bux_path_join(bux_path_join(projectDir, "build"), mainName);
if !FileExists(mainBin) {
Print("Error: test binary not found: ");
PrintLine(mainBin);
return 1;
// Without --filter, build and run the project's own Main first.
// With --filter, only run matching tests/*.bux files.
if String_Eq(filter, "") {
let mainRc: int = Cli_BuildProject(projectDir, "", false);
if mainRc != 0 {
PrintLine("Main test build failed");
return mainRc;
}
let man: Manifest = Manifest_Load(bux_path_join(projectDir, "bux.toml"));
var mainName: String = man.name;
if String_Eq(mainName, "") { mainName = "bux_out"; }
let mainBin: String = bux_path_join(bux_path_join(projectDir, "build"), mainName);
if !FileExists(mainBin) {
Print("Error: test binary not found: ");
PrintLine(mainBin);
return 1;
}
let mainResult: int = bux_system(mainBin);
if mainResult != 0 {
Print("Main tests failed (exit code ");
PrintInt(mainResult as int64);
PrintLine(")");
return mainResult;
}
PrintLine("Main tests passed");
}
let mainResult: int = bux_system(mainBin);
if mainResult != 0 {
Print("Main tests failed (exit code ");
PrintInt(mainResult as int64);
PrintLine(")");
return mainResult;
}
PrintLine("Main tests passed");
// Propagate the project's stdlib to temp test packages.
let stdlibDir: String = Cli_FindStdlibDir(projectDir);
@@ -1031,15 +1235,31 @@ func Cli_Test(projectDir: String) -> int {
return 0;
}
PrintLine("┌──────────────────────────────┬────────┐");
PrintLine("│ Test │ Status │");
PrintLine("├──────────────────────────────┼────────┤");
var passed: int = 0;
var failed: int = 0;
var skipped: int = 0;
var i: int = 0;
while i < testCount {
let testPath: String = testFiles[i];
let fileName: String = Cli_FileNameFromPath(testPath);
let testName: String = Cli_StripExtension(fileName);
Print(" Test: ");
// --filter: only run tests whose name contains the filter substring
if !String_Eq(filter, "") {
if !String_Contains(testName, filter) {
skipped = skipped + 1;
i = i + 1;
continue;
}
}
Print("│ ");
Print(testName);
// Pad status column roughly (name may be long)
Print(" ... ");
// Create temp package for this test file
@@ -1050,14 +1270,14 @@ func Cli_Test(projectDir: String) -> int {
let source: String = ReadFile(testPath);
if String_Eq(source, "") {
PrintLine("FAIL (cannot read test file)");
PrintLine("FAIL ");
failed = failed + 1;
i = i + 1;
continue;
}
let tmpMain: String = bux_path_join(tmpSrc, "Main.bux");
if !WriteFile(tmpMain, source) {
PrintLine("FAIL (cannot write temp Main.bux)");
PrintLine("FAIL ");
failed = failed + 1;
i = i + 1;
continue;
@@ -1066,7 +1286,7 @@ func Cli_Test(projectDir: String) -> int {
let tmpToml: String = bux_path_join(tmpDir, "bux.toml");
var tomlContent: String = "[Package]\nName = \"_test_tmp\"\nVersion = \"0.1.0\"\nType = \"bin\"\n\n[Build]\nOutput = \"Bin\"\n";
if !WriteFile(tmpToml, tomlContent) {
PrintLine("FAIL (cannot write temp bux.toml)");
PrintLine("FAIL ");
failed = failed + 1;
i = i + 1;
continue;
@@ -1091,36 +1311,48 @@ func Cli_Test(projectDir: String) -> int {
let buildRc: int = Cli_BuildProject(tmpDir, "", false);
if buildRc != 0 {
PrintLine("FAIL (build error)");
PrintLine("FAIL ");
failed = failed + 1;
i = i + 1;
continue;
}
let testBin: String = bux_path_join(bux_path_join(tmpDir, "build"), "_test_tmp");
if !FileExists(testBin) {
PrintLine("FAIL (test binary not found)");
PrintLine("FAIL ");
failed = failed + 1;
i = i + 1;
continue;
}
let runRc: int = bux_system(testBin);
if runRc == 0 {
PrintLine("PASS");
PrintLine("PASS");
passed = passed + 1;
} else {
Print("FAIL (exit ");
PrintInt(runRc as int64);
PrintLine(")");
Print("FAIL ");
PrintLine("");
failed = failed + 1;
}
i = i + 1;
}
Print("Tests: ");
PrintLine("└──────────────────────────────┴────────┘");
Print("Results: ");
PrintInt(passed as int64);
Print(" passed, ");
PrintInt(failed as int64);
PrintLine(" failed");
Print(" failed");
if skipped > 0 {
Print(", ");
PrintInt(skipped as int64);
Print(" skipped");
}
PrintLine("");
if !String_Eq(filter, "") && passed == 0 && failed == 0 {
Print("No tests matching filter '");
Print(filter);
PrintLine("'");
return 1;
}
if failed > 0 { return 1; }
return 0;
}
@@ -1493,7 +1725,10 @@ func Cli_Run(args: *String, argCount: int) -> int {
if argCount < 2 {
PrintLine("Bux Self-Hosting Compiler v0.2.0");
PrintLine("Usage: buxc <command> [args]");
PrintLine("Commands: build, check, new, init, add, remove, fetch, fmt, test, run, project, help, version");
PrintLine("Commands: build, check, new, init, add, remove, fetch, fmt, doc, test, run, project, help, version");
PrintLine(" test --filter <name> Only run tests/*.bux whose name contains <name>");
PrintLine(" fmt --check [path] Exit 1 if any file would be reformatted (CI)");
PrintLine(" doc --out file.md [path] API docs from /// comments");
return 0;
}
@@ -1506,14 +1741,16 @@ func Cli_Run(args: *String, argCount: int) -> int {
if String_Eq(cmd, "help") || String_Eq(cmd, "--help") || String_Eq(cmd, "-h") {
PrintLine("Bux Self-Hosting Compiler v0.2.0");
PrintLine("Usage: buxc <command> [args]");
PrintLine("Commands: build, check, new, init, add, remove, fetch, fmt, test, run, project, help, version");
PrintLine("Commands: build, check, new, init, add, remove, fetch, fmt, doc, test, run, project, help, version");
PrintLine(" test --filter <name> Only run tests/*.bux whose name contains <name>");
PrintLine(" fmt --check [path] Exit 1 if any file would be reformatted (CI)");
PrintLine(" doc --out file.md [path] API docs from /// comments");
PrintLine("Pipeline modules:");
PrintLine(" Lexer ✅ 695 lines");
PrintLine(" Parser ✅ 1004 lines");
PrintLine(" Sema ✅ 393 lines");
PrintLine(" HirLower ✅ 307 lines");
PrintLine(" CBackend ✅ 585 lines");
PrintLine(" Total: 3830 lines of Bux");
PrintLine(" Lexer ✅");
PrintLine(" Parser ✅");
PrintLine(" Sema ✅");
PrintLine(" HirLower ✅");
PrintLine(" CBackend ✅");
return 0;
}
@@ -1550,9 +1787,36 @@ func Cli_Run(args: *String, argCount: int) -> int {
}
if String_Eq(cmd, "fmt") {
let dir: String = ".";
if argCount >= 3 { dir = args[2]; }
return Cli_Fmt(dir);
var dir: String = ".";
var checkOnly: bool = false;
var fi: int = 2;
while fi < argCount {
if String_Eq(args[fi], "--check") {
checkOnly = true;
} else {
dir = args[fi];
}
fi = fi + 1;
}
return Cli_Fmt(dir, checkOnly);
}
if String_Eq(cmd, "doc") {
var dir: String = "lib";
var outPath: String = "";
var di: int = 2;
while di < argCount {
if String_Eq(args[di], "--out") && di + 1 < argCount {
outPath = args[di + 1];
di = di + 1;
} else if String_StartsWith(args[di], "--out=") {
outPath = bux_str_slice(args[di], 6, bux_strlen(args[di]) - 6);
} else if !String_StartsWith(args[di], "-") {
dir = args[di];
}
di = di + 1;
}
return Cli_Doc(dir, outPath);
}
if String_Eq(cmd, "check") {
@@ -1578,9 +1842,22 @@ func Cli_Run(args: *String, argCount: int) -> int {
}
if String_Eq(cmd, "test") {
let dir: String = ".";
if argCount >= 3 { dir = args[2]; }
return Cli_Test(dir);
var dir: String = ".";
var filter: String = "";
var ti: int = 2;
while ti < argCount {
if String_Eq(args[ti], "--filter") && ti + 1 < argCount {
filter = args[ti + 1];
ti = ti + 1;
} else if String_StartsWith(args[ti], "--filter=") {
// --filter=name form
filter = bux_str_slice(args[ti], 9, bux_strlen(args[ti]) - 9);
} else if !String_StartsWith(args[ti], "-") {
dir = args[ti];
}
ti = ti + 1;
}
return Cli_Test(dir, filter);
}
if String_Eq(cmd, "run") {
+153 -123
View File
@@ -1,138 +1,168 @@
// fmt.bux — Bux source code formatter (indentation-based, preserves line structure)
module Fmt {
extern func bux_read_file(path: String) -> String;
extern func bux_write_file(path: String, content: String) -> bool;
extern func bux_strlen(s: String) -> uint;
extern func bux_read_file(path: String) -> String;
extern func bux_write_file(path: String, content: String) -> bool;
extern func bux_strlen(s: String) -> uint;
// Count leading spaces on a line
func Fmt_CountLeadingSpaces(line: String) -> int {
var count: int = 0;
while count < 256 {
let c: int = line[count] as int;
if c == 0 { break; }
if c != 32 { break; } // space
count = count + 1;
}
return count;
}
// Skip whitespace from start of line, return rest
func Fmt_TrimLeft(line: String) -> String {
var i: int = 0;
while i < 256 {
let c: int = line[i] as int;
if c == 0 { break; }
if c != 32 && c != 9 { break; }
i = i + 1;
}
if i == 0 { return line; }
// Extract substring from i
var len: int = 0;
while len < 256 {
let c: int = line[len] as int;
if c == 0 { break; }
len = len + 1;
}
if i >= len { return ""; }
return bux_str_slice(line, i as uint, (len - i) as uint);
}
// Check if char at position is inside a string or comment (simplified)
func Fmt_IsInStringOrComment(line: String, pos: int) -> bool {
var inString: bool = false;
var inChar: bool = false;
var inComment: bool = false;
var i: int = 0;
while i < pos {
let c: int = line[i] as int;
let n: int = 0;
if i + 1 < 256 { n = line[i + 1] as int; }
if inComment { i = i + 1; continue; }
if c == 47 && n == 47 { inComment = true; i = i + 1; continue; } // //
if c == 34 && !inChar { inString = !inString; }
if c == 39 && !inString { inChar = !inChar; }
i = i + 1;
}
return inString || inChar || inComment;
}
// Count brace depth change on a line, skipping strings/comments
func Fmt_CountBraceDelta(line: String) -> int {
var delta: int = 0;
var i: int = 0;
while i < 256 {
let c: int = line[i] as int;
if c == 0 { break; }
if !Fmt_IsInStringOrComment(line, i) {
if c == 123 { delta = delta + 1; } // {
if c == 125 { delta = delta - 1; } // }
// Count leading spaces on a line
func Fmt_CountLeadingSpaces(line: String) -> int {
var count: int = 0;
while count < 256 {
let c: int = line[count] as int;
if c == 0 { break; }
if c != 32 { break; } // space
count = count + 1;
}
i = i + 1;
return count;
}
return delta;
}
func Fmt_FormatSource(source: String) -> String {
let sb: StringBuilder = StringBuilder_NewCap(8192);
var indent: int = 0;
var i: uint = 0;
let lineCount: uint = bux_str_split_count(source, "\n");
while i < lineCount {
let line: String = bux_str_split_part(source, "\n", i);
let trimmed: String = Fmt_TrimLeft(line);
// Skip empty lines
if String_Eq(trimmed, "") {
StringBuilder_Append(&sb, "\n");
// Skip whitespace from start of line, return rest
func Fmt_TrimLeft(line: String) -> String {
var i: int = 0;
while i < 256 {
let c: int = line[i] as int;
if c == 0 { break; }
if c != 32 && c != 9 { break; }
i = i + 1;
continue;
}
// Adjust indent for closing braces on this line
let delta: int = Fmt_CountBraceDelta(trimmed);
// If line starts with }, decrease indent before emitting
let firstChar: int = trimmed[0] as int;
if firstChar == 125 { // }
indent = indent - 1;
if indent < 0 { indent = 0; }
if i == 0 { return line; }
// Extract substring from i
var len: int = 0;
while len < 256 {
let c: int = line[len] as int;
if c == 0 { break; }
len = len + 1;
}
// Emit indentation
var si: int = 0;
while si < indent {
StringBuilder_Append(&sb, " ");
si = si + 1;
}
// Emit the trimmed line
StringBuilder_Append(&sb, trimmed);
StringBuilder_Append(&sb, "\n");
// Adjust indent for opening braces
if firstChar != 125 {
indent = indent + delta;
} else {
// For lines starting with }, apply the net delta after the initial decrease
indent = indent + delta + 1;
if indent < 0 { indent = 0; }
}
i = i + 1;
if i >= len { return ""; }
return bux_str_slice(line, i as uint, (len - i) as uint);
}
let result: String = StringBuilder_Build(&sb);
StringBuilder_Free(&sb);
return result;
}
// Check if char at position is inside a string or comment (simplified)
func Fmt_IsInStringOrComment(line: String, pos: int) -> bool {
var inString: bool = false;
var inChar: bool = false;
var inComment: bool = false;
var i: int = 0;
while i < pos {
let c: int = line[i] as int;
let n: int = 0;
if i + 1 < 256 { n = line[i + 1] as int; }
if inComment { i = i + 1; continue; }
if c == 47 && n == 47 { inComment = true; i = i + 1; continue; } // //
if c == 34 && !inChar { inString = !inString; }
if c == 39 && !inString { inChar = !inChar; }
i = i + 1;
}
return inString || inChar || inComment;
}
func Fmt_FormatFile(path: String) -> bool {
let source: String = bux_read_file(path);
if source == null as String || String_Eq(source, "") { return false; }
let formatted: String = Fmt_FormatSource(source);
if String_Eq(formatted, "") { return false; }
return bux_write_file(path, formatted);
}
// Count brace depth change on a line, skipping strings/comments
func Fmt_CountBraceDelta(line: String) -> int {
var delta: int = 0;
var i: int = 0;
while i < 256 {
let c: int = line[i] as int;
if c == 0 { break; }
if !Fmt_IsInStringOrComment(line, i) {
if c == 123 { delta = delta + 1; } // {
if c == 125 { delta = delta - 1; } // }
}
i = i + 1;
}
return delta;
}
func Fmt_FormatSource(source: String) -> String {
let sb: StringBuilder = StringBuilder_NewCap(8192);
var indent: int = 0;
var i: uint = 0;
var lineCount: uint = bux_str_split_count(source, "\n");
// Trailing "\n" yields a final empty part (split artifact). Drop it so
// re-formatting is idempotent and does not accumulate blank lines.
if lineCount > 0 {
let last: String = bux_str_split_part(source, "\n", lineCount - 1);
if String_Eq(last, "") {
lineCount = lineCount - 1;
}
}
while i < lineCount {
let line: String = bux_str_split_part(source, "\n", i);
let trimmed: String = Fmt_TrimLeft(line);
// Empty line (intentional blank) — keep a single newline
if String_Eq(trimmed, "") {
StringBuilder_Append(&sb, "\n");
i = i + 1;
continue;
}
// Adjust indent for closing braces on this line
let delta: int = Fmt_CountBraceDelta(trimmed);
// If line starts with }, decrease indent before emitting
let firstChar: int = trimmed[0] as int;
if firstChar == 125 { // }
indent = indent - 1;
if indent < 0 { indent = 0; }
}
// Emit indentation
var si: int = 0;
while si < indent {
StringBuilder_Append(&sb, " ");
si = si + 1;
}
// Emit the trimmed line
StringBuilder_Append(&sb, trimmed);
StringBuilder_Append(&sb, "\n");
// Adjust indent for opening braces
if firstChar != 125 {
indent = indent + delta;
} else {
// For lines starting with }, apply the net delta after the initial decrease
indent = indent + delta + 1;
if indent < 0 { indent = 0; }
}
i = i + 1;
}
let result: String = StringBuilder_Build(&sb);
StringBuilder_Free(&sb);
return result;
}
func Fmt_FormatFile(path: String) -> bool {
let source: String = bux_read_file(path);
if source == null as String || String_Eq(source, "") { return false; }
let formatted: String = Fmt_FormatSource(source);
if String_Eq(formatted, "") { return false; }
return bux_write_file(path, formatted);
}
// Returns true if formatting would change the file (CI --check).
func Fmt_WouldChange(path: String) -> bool {
let source: String = bux_read_file(path);
if source == null as String { return false; }
let formatted: String = Fmt_FormatSource(source);
return !String_Eq(formatted, source);
}
// Check a file without writing. Returns 0 if clean, 1 if would reformat / error.
func Fmt_CheckFile(path: String) -> int {
let source: String = bux_read_file(path);
if source == null as String {
return 1;
}
let formatted: String = Fmt_FormatSource(source);
if String_Eq(formatted, source) {
return 0;
}
return 1;
}
}
+250 -250
View File
@@ -1,256 +1,256 @@
// hir.bux — HIR (High-level Intermediate Representation) node types
module Hir {
// HIR node kinds
const hLit: int = 0;
const hVar: int = 1;
const hSelf: int = 2;
const hUnary: int = 3;
const hBinary: int = 4;
const hAssign: int = 5;
const hIf: int = 6;
const hWhile: int = 7;
const hLoop: int = 8;
const hBreak: int = 9;
const hContinue: int = 10;
const hReturn: int = 11;
const hAlloca: int = 12;
const hLoad: int = 13;
const hStore: int = 14;
const hFieldPtr: int = 15;
const hFieldAccess: int = 16;
const hArrowField: int = 17;
const hIndexPtr: int = 18;
const hCall: int = 32;
const hCallIndirect: int = 33;
const hCast: int = 34;
const hIs: int = 35;
const hSizeOf: int = 36;
const hBlock: int = 37;
const hStructInit: int = 38;
const hSliceInit: int = 39;
const hRange: int = 40;
const hTupleInit: int = 41;
const hMatch: int = 42;
const hSpawn: int = 43;
const hAwait: int = 44;
const hDefer: int = 45;
// HIR node kinds
const hLit: int = 0;
const hVar: int = 1;
const hSelf: int = 2;
const hUnary: int = 3;
const hBinary: int = 4;
const hAssign: int = 5;
const hIf: int = 6;
const hWhile: int = 7;
const hLoop: int = 8;
const hBreak: int = 9;
const hContinue: int = 10;
const hReturn: int = 11;
const hAlloca: int = 12;
const hLoad: int = 13;
const hStore: int = 14;
const hFieldPtr: int = 15;
const hFieldAccess: int = 16;
const hArrowField: int = 17;
const hIndexPtr: int = 18;
const hCall: int = 32;
const hCallIndirect: int = 33;
const hCast: int = 34;
const hIs: int = 35;
const hSizeOf: int = 36;
const hBlock: int = 37;
const hStructInit: int = 38;
const hSliceInit: int = 39;
const hRange: int = 40;
const hTupleInit: int = 41;
const hMatch: int = 42;
const hSpawn: int = 43;
const hAwait: int = 44;
const hDefer: int = 45;
// ---------------------------------------------------------------------------
// HirArgList — linked list for call arguments beyond 2
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// HirArgList — linked list for call arguments beyond 2
// ---------------------------------------------------------------------------
struct HirArgList {
node: *HirNode,
next: *HirArgList,
}
// ---------------------------------------------------------------------------
// HirNode — unified struct with tagged union pattern
// ---------------------------------------------------------------------------
struct HirNode {
kind: int;
line: uint32;
column: uint32;
typeKind: int;
typeName: String;
// Common fields (used by multiple kinds)
strValue: String; // var name; callee name; field name; label
intValue: int; // token kind (for lit; unary op; binary op)
boolValue: bool; // range inclusive; isScope
// Child nodes (up to 3)
child1: *HirNode; // left/operand/condition/base
child2: *HirNode; // right/value/then/body
child3: *HirNode; // else/third
// Extra data pointer (for children arrays, field lists, etc.)
extraData: *void;
extraCount: int;
}
// ---------------------------------------------------------------------------
// HirFunc
// ---------------------------------------------------------------------------
struct HirParam {
name: String;
typeKind: int;
typeName: String;
}
struct HirFunc {
name: String;
paramCount: int;
param0: *HirParam;
param1: *HirParam;
param2: *HirParam;
param3: *HirParam;
param4: *HirParam;
param5: *HirParam;
param6: *HirParam;
param7: *HirParam;
param8: *HirParam;
retTypeKind: int;
retTypeName: String;
body: *HirNode;
isPublic: bool;
// Closure capture metadata
captureCount: int;
captureName0: String;
captureName1: String;
captureName2: String;
captureName3: String;
captureName4: String;
captureName5: String;
captureName6: String;
captureName7: String;
captureType0: int;
captureType1: int;
captureType2: int;
captureType3: int;
captureType4: int;
captureType5: int;
captureType6: int;
captureType7: int;
envStructName: String;
envInstanceName: String;
checkedFunc: bool;
}
// ---------------------------------------------------------------------------
// HirEnumVariant
// ---------------------------------------------------------------------------
struct HirEnumVariant {
name: String;
fieldCount: int;
fieldType0: int;
fieldName0: String;
fieldTypeName0: String; // C type name (e.g. "int", "Tuple_int_int")
fieldType1: int;
fieldName1: String;
fieldTypeName1: String;
}
// ---------------------------------------------------------------------------
// HirModule
// ---------------------------------------------------------------------------
struct HirStructField {
name: String;
typeName: String;
}
struct HirStruct {
name: String;
fieldCount: int;
fields: *HirStructField;
}
struct HirConst {
name: String;
value: int;
}
struct HirEnum {
name: String;
variantCount: int;
variants: *HirEnumVariant;
}
struct HirModule {
funcCount: int;
funcs: *HirFunc;
externCount: int;
externFuncs: *HirFunc;
structCount: int;
structs: *HirStruct;
enumCount: int;
enums: *HirEnum;
constCount: int;
consts: *HirConst;
}
// ---------------------------------------------------------------------------
// Constructor helpers
// ---------------------------------------------------------------------------
func Hir_MakeNode(kind: int, line: uint32, column: uint32) -> HirNode {
return HirNode { kind: kind, line: line, column: column,
typeKind: 0, typeName: "",
strValue: "", intValue: 0, boolValue: false,
child1: null as *HirNode, child2: null as *HirNode, child3: null as *HirNode,
extraData: null as *void, extraCount: 0 };
}
func Hir_MakeLit(tokKind: int, tokText: String, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hLit, line, col);
n.intValue = tokKind;
n.strValue = tokText;
return n;
}
func Hir_MakeVar(name: String, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hVar, line, col);
n.strValue = name;
return n;
}
func Hir_MakeBinary(op: int, left: *HirNode, right: *HirNode, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hBinary, line, col);
n.intValue = op;
n.child1 = left;
n.child2 = right;
return n;
}
func Hir_MakeCall(callee: String, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hCall, line, col);
n.strValue = callee;
return n;
}
func Hir_MakeReturn(value: *HirNode, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hReturn, line, col);
n.child1 = value;
return n;
}
func Hir_MakeBlock(line: uint32, col: uint32) -> HirNode {
return Hir_MakeNode(hBlock, line, col);
}
func Hir_MakeIf(cond: *HirNode, thenBody: *HirNode, elseBody: *HirNode, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hIf, line, col);
n.child1 = cond;
n.child2 = thenBody;
n.child3 = elseBody;
return n;
}
func Hir_MakeWhile(cond: *HirNode, body: *HirNode, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hWhile, line, col);
n.child1 = cond;
n.child2 = body;
return n;
}
func Hir_MakeAlloca(name: String, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hAlloca, line, col);
n.strValue = name;
return n;
}
func Hir_MakeLoad(ptr: *HirNode, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hLoad, line, col);
n.child1 = ptr;
return n;
}
func Hir_MakeStore(ptr: *HirNode, value: *HirNode, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hStore, line, col);
n.child1 = ptr;
n.child2 = value;
return n;
}
struct HirArgList {
node: *HirNode,
next: *HirArgList,
}
// ---------------------------------------------------------------------------
// HirNode — unified struct with tagged union pattern
// ---------------------------------------------------------------------------
struct HirNode {
kind: int;
line: uint32;
column: uint32;
typeKind: int;
typeName: String;
// Common fields (used by multiple kinds)
strValue: String; // var name; callee name; field name; label
intValue: int; // token kind (for lit; unary op; binary op)
boolValue: bool; // range inclusive; isScope
// Child nodes (up to 3)
child1: *HirNode; // left/operand/condition/base
child2: *HirNode; // right/value/then/body
child3: *HirNode; // else/third
// Extra data pointer (for children arrays, field lists, etc.)
extraData: *void;
extraCount: int;
}
// ---------------------------------------------------------------------------
// HirFunc
// ---------------------------------------------------------------------------
struct HirParam {
name: String;
typeKind: int;
typeName: String;
}
struct HirFunc {
name: String;
paramCount: int;
param0: *HirParam;
param1: *HirParam;
param2: *HirParam;
param3: *HirParam;
param4: *HirParam;
param5: *HirParam;
param6: *HirParam;
param7: *HirParam;
param8: *HirParam;
retTypeKind: int;
retTypeName: String;
body: *HirNode;
isPublic: bool;
// Closure capture metadata
captureCount: int;
captureName0: String;
captureName1: String;
captureName2: String;
captureName3: String;
captureName4: String;
captureName5: String;
captureName6: String;
captureName7: String;
captureType0: int;
captureType1: int;
captureType2: int;
captureType3: int;
captureType4: int;
captureType5: int;
captureType6: int;
captureType7: int;
envStructName: String;
envInstanceName: String;
checkedFunc: bool;
}
// ---------------------------------------------------------------------------
// HirEnumVariant
// ---------------------------------------------------------------------------
struct HirEnumVariant {
name: String;
fieldCount: int;
fieldType0: int;
fieldName0: String;
fieldTypeName0: String; // C type name (e.g. "int", "Tuple_int_int")
fieldType1: int;
fieldName1: String;
fieldTypeName1: String;
}
// ---------------------------------------------------------------------------
// HirModule
// ---------------------------------------------------------------------------
struct HirStructField {
name: String;
typeName: String;
}
struct HirStruct {
name: String;
fieldCount: int;
fields: *HirStructField;
}
struct HirConst {
name: String;
value: int;
}
struct HirEnum {
name: String;
variantCount: int;
variants: *HirEnumVariant;
}
struct HirModule {
funcCount: int;
funcs: *HirFunc;
externCount: int;
externFuncs: *HirFunc;
structCount: int;
structs: *HirStruct;
enumCount: int;
enums: *HirEnum;
constCount: int;
consts: *HirConst;
}
// ---------------------------------------------------------------------------
// Constructor helpers
// ---------------------------------------------------------------------------
func Hir_MakeNode(kind: int, line: uint32, column: uint32) -> HirNode {
return HirNode { kind: kind, line: line, column: column,
typeKind: 0, typeName: "",
strValue: "", intValue: 0, boolValue: false,
child1: null as *HirNode, child2: null as *HirNode, child3: null as *HirNode,
extraData: null as *void, extraCount: 0 };
}
func Hir_MakeLit(tokKind: int, tokText: String, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hLit, line, col);
n.intValue = tokKind;
n.strValue = tokText;
return n;
}
func Hir_MakeVar(name: String, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hVar, line, col);
n.strValue = name;
return n;
}
func Hir_MakeBinary(op: int, left: *HirNode, right: *HirNode, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hBinary, line, col);
n.intValue = op;
n.child1 = left;
n.child2 = right;
return n;
}
func Hir_MakeCall(callee: String, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hCall, line, col);
n.strValue = callee;
return n;
}
func Hir_MakeReturn(value: *HirNode, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hReturn, line, col);
n.child1 = value;
return n;
}
func Hir_MakeBlock(line: uint32, col: uint32) -> HirNode {
return Hir_MakeNode(hBlock, line, col);
}
func Hir_MakeIf(cond: *HirNode, thenBody: *HirNode, elseBody: *HirNode, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hIf, line, col);
n.child1 = cond;
n.child2 = thenBody;
n.child3 = elseBody;
return n;
}
func Hir_MakeWhile(cond: *HirNode, body: *HirNode, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hWhile, line, col);
n.child1 = cond;
n.child2 = body;
return n;
}
func Hir_MakeAlloca(name: String, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hAlloca, line, col);
n.strValue = name;
return n;
}
func Hir_MakeLoad(ptr: *HirNode, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hLoad, line, col);
n.child1 = ptr;
return n;
}
func Hir_MakeStore(ptr: *HirNode, value: *HirNode, line: uint32, col: uint32) -> HirNode {
var n: HirNode = Hir_MakeNode(hStore, line, col);
n.child1 = ptr;
n.child2 = value;
return n;
}
}
+3960 -3960
View File
File diff suppressed because it is too large Load Diff
+725 -712
View File
File diff suppressed because it is too large Load Diff
+213 -213
View File
@@ -2,224 +2,137 @@
// Parses package metadata: name, version, type, build output.
module Manifest {
extern func bux_strlen(s: String) -> uint;
extern func bux_strlen(s: String) -> uint;
// ---------------------------------------------------------------------------
// Manifest struct
// ---------------------------------------------------------------------------
struct Manifest {
name: String;
version: String;
pkgType: String;
output: String;
depCount: int;
depName0: String;
depUrl0: String;
depName1: String;
depUrl1: String;
depName2: String;
depUrl2: String;
depName3: String;
depUrl3: String;
depName4: String;
depUrl4: String;
depName5: String;
depUrl5: String;
depName6: String;
depUrl6: String;
depName7: String;
depUrl7: String;
}
// ---------------------------------------------------------------------------
// Manifest struct
// ---------------------------------------------------------------------------
struct Manifest {
name: String;
version: String;
pkgType: String;
output: String;
depCount: int;
depName0: String;
depUrl0: String;
depName1: String;
depUrl1: String;
depName2: String;
depUrl2: String;
depName3: String;
depUrl3: String;
depName4: String;
depUrl4: String;
depName5: String;
depUrl5: String;
depName6: String;
depUrl6: String;
depName7: String;
depUrl7: String;
}
// ---------------------------------------------------------------------------
// Simple TOML parser (handles [Package] and [Build] sections)
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Simple TOML parser (handles [Package] and [Build] sections)
// ---------------------------------------------------------------------------
func Manifest_Parse(content: String) -> Manifest {
var m: Manifest;
m.name = "";
m.version = "0.1.0";
m.pkgType = "bin";
m.output = "Bin";
m.depCount = 0;
func Manifest_Parse(content: String) -> Manifest {
var m: Manifest;
m.name = "";
m.version = "0.1.0";
m.pkgType = "bin";
m.output = "Bin";
m.depCount = 0;
if String_Eq(content, "") { return m; }
if String_Eq(content, "") { return m; }
var currentSection: String = "";
let count: uint = String_SplitCount(content, "\n");
var i: uint = 0;
while i < count {
let line: String = String_SplitPart(content, "\n", i);
var currentSection: String = "";
let count: uint = String_SplitCount(content, "\n");
var i: uint = 0;
while i < count {
let line: String = String_SplitPart(content, "\n", i);
// Skip empty lines and comments
if String_Eq(line, "") { i = i + 1; continue; }
if String_StartsWith(line, "#") { i = i + 1; continue; }
// Skip empty lines and comments
if String_Eq(line, "") { i = i + 1; continue; }
if String_StartsWith(line, "#") { i = i + 1; continue; }
// Section header: [Section]
if String_StartsWith(line, "[") {
if String_StartsWith(line, "[Package]") {
currentSection = "Package";
} else if String_StartsWith(line, "[Build]") {
currentSection = "Build";
} else if String_StartsWith(line, "[dependencies]") {
currentSection = "dependencies";
} else {
currentSection = "";
// Section header: [Section]
if String_StartsWith(line, "[") {
if String_StartsWith(line, "[Package]") {
currentSection = "Package";
} else if String_StartsWith(line, "[Build]") {
currentSection = "Build";
} else if String_StartsWith(line, "[dependencies]") {
currentSection = "dependencies";
} else {
currentSection = "";
}
i = i + 1; continue;
}
i = i + 1; continue;
}
// Key = Value
let eqCount: uint = String_SplitCount(line, "=");
if eqCount >= 2 {
let key: String = String_Trim(String_SplitPart(line, "=", 0));
let rawVal: String = String_Trim(String_SplitPart(line, "=", 1));
// Key = Value
let eqCount: uint = String_SplitCount(line, "=");
if eqCount >= 2 {
let key: String = String_Trim(String_SplitPart(line, "=", 0));
let rawVal: String = String_Trim(String_SplitPart(line, "=", 1));
// Strip quotes from value
var val: String = rawVal;
if String_StartsWith(val, "\"") && String_EndsWith(val, "\"") {
let vlen: uint = bux_strlen(val);
if vlen >= 2 {
val = String_Slice(val, 1, vlen - 2);
}
}
if String_Eq(currentSection, "Package") {
if String_Eq(key, "Name") { m.name = val; }
if String_Eq(key, "Version") { m.version = val; }
if String_Eq(key, "Type") { m.pkgType = val; }
} else if String_Eq(currentSection, "Build") {
if String_Eq(key, "Output") { m.output = val; }
} else if String_Eq(currentSection, "dependencies") {
if m.depCount < 8 {
if m.depCount == 0 { m.depName0 = key; m.depUrl0 = val; }
else if m.depCount == 1 { m.depName1 = key; m.depUrl1 = val; }
else if m.depCount == 2 { m.depName2 = key; m.depUrl2 = val; }
else if m.depCount == 3 { m.depName3 = key; m.depUrl3 = val; }
else if m.depCount == 4 { m.depName4 = key; m.depUrl4 = val; }
else if m.depCount == 5 { m.depName5 = key; m.depUrl5 = val; }
else if m.depCount == 6 { m.depName6 = key; m.depUrl6 = val; }
else if m.depCount == 7 { m.depName7 = key; m.depUrl7 = val; }
m.depCount = m.depCount + 1;
// Strip quotes from value
var val: String = rawVal;
if String_StartsWith(val, "\"") && String_EndsWith(val, "\"") {
let vlen: uint = bux_strlen(val);
if vlen >= 2 {
val = String_Slice(val, 1, vlen - 2);
}
}
if String_Eq(currentSection, "Package") {
if String_Eq(key, "Name") { m.name = val; }
if String_Eq(key, "Version") { m.version = val; }
if String_Eq(key, "Type") { m.pkgType = val; }
} else if String_Eq(currentSection, "Build") {
if String_Eq(key, "Output") { m.output = val; }
} else if String_Eq(currentSection, "dependencies") {
if m.depCount < 8 {
if m.depCount == 0 { m.depName0 = key; m.depUrl0 = val; }
else if m.depCount == 1 { m.depName1 = key; m.depUrl1 = val; }
else if m.depCount == 2 { m.depName2 = key; m.depUrl2 = val; }
else if m.depCount == 3 { m.depName3 = key; m.depUrl3 = val; }
else if m.depCount == 4 { m.depName4 = key; m.depUrl4 = val; }
else if m.depCount == 5 { m.depName5 = key; m.depUrl5 = val; }
else if m.depCount == 6 { m.depName6 = key; m.depUrl6 = val; }
else if m.depCount == 7 { m.depName7 = key; m.depUrl7 = val; }
m.depCount = m.depCount + 1;
}
}
}
i = i + 1;
}
i = i + 1;
return m;
}
return m;
}
// ---------------------------------------------------------------------------
// Dependency helpers
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Dependency helpers
// ---------------------------------------------------------------------------
func Manifest_HasDep(m: Manifest, name: String) -> bool {
var i: int = 0;
while i < m.depCount {
var depName: String = "";
if i == 0 { depName = m.depName0; }
else if i == 1 { depName = m.depName1; }
else if i == 2 { depName = m.depName2; }
else if i == 3 { depName = m.depName3; }
else if i == 4 { depName = m.depName4; }
else if i == 5 { depName = m.depName5; }
else if i == 6 { depName = m.depName6; }
else if i == 7 { depName = m.depName7; }
if String_Eq(depName, name) { return true; }
i = i + 1;
func Manifest_HasDep(m: Manifest, name: String) -> bool {
var i: int = 0;
while i < m.depCount {
var depName: String = "";
if i == 0 { depName = m.depName0; }
else if i == 1 { depName = m.depName1; }
else if i == 2 { depName = m.depName2; }
else if i == 3 { depName = m.depName3; }
else if i == 4 { depName = m.depName4; }
else if i == 5 { depName = m.depName5; }
else if i == 6 { depName = m.depName6; }
else if i == 7 { depName = m.depName7; }
if String_Eq(depName, name) { return true; }
i = i + 1;
}
return false;
}
return false;
}
func Manifest_GetDepUrl(m: Manifest, name: String) -> String {
var i: int = 0;
while i < m.depCount {
var depName: String = "";
var depUrl: String = "";
if i == 0 { depName = m.depName0; depUrl = m.depUrl0; }
else if i == 1 { depName = m.depName1; depUrl = m.depUrl1; }
else if i == 2 { depName = m.depName2; depUrl = m.depUrl2; }
else if i == 3 { depName = m.depName3; depUrl = m.depUrl3; }
else if i == 4 { depName = m.depName4; depUrl = m.depUrl4; }
else if i == 5 { depName = m.depName5; depUrl = m.depUrl5; }
else if i == 6 { depName = m.depName6; depUrl = m.depUrl6; }
else if i == 7 { depName = m.depName7; depUrl = m.depUrl7; }
if String_Eq(depName, name) { return depUrl; }
i = i + 1;
}
return "";
}
func Manifest_AddDep(m: *Manifest, name: String, url: String) -> bool {
if Manifest_HasDep(*m, name) { return false; }
if m.depCount >= 8 { return false; }
if m.depCount == 0 { m.depName0 = name; m.depUrl0 = url; }
else if m.depCount == 1 { m.depName1 = name; m.depUrl1 = url; }
else if m.depCount == 2 { m.depName2 = name; m.depUrl2 = url; }
else if m.depCount == 3 { m.depName3 = name; m.depUrl3 = url; }
else if m.depCount == 4 { m.depName4 = name; m.depUrl4 = url; }
else if m.depCount == 5 { m.depName5 = name; m.depUrl5 = url; }
else if m.depCount == 6 { m.depName6 = name; m.depUrl6 = url; }
else if m.depCount == 7 { m.depName7 = name; m.depUrl7 = url; }
m.depCount = m.depCount + 1;
return true;
}
func Manifest_RemoveDep(m: *Manifest, name: String) -> bool {
var found: int = -1;
var i: int = 0;
while i < m.depCount {
var depName: String = "";
if i == 0 { depName = m.depName0; }
else if i == 1 { depName = m.depName1; }
else if i == 2 { depName = m.depName2; }
else if i == 3 { depName = m.depName3; }
else if i == 4 { depName = m.depName4; }
else if i == 5 { depName = m.depName5; }
else if i == 6 { depName = m.depName6; }
else if i == 7 { depName = m.depName7; }
if String_Eq(depName, name) { found = i; break; }
i = i + 1;
}
if found < 0 { return false; }
i = found;
while i < m.depCount - 1 {
if i == 0 { m.depName0 = m.depName1; m.depUrl0 = m.depUrl1; }
else if i == 1 { m.depName1 = m.depName2; m.depUrl1 = m.depUrl2; }
else if i == 2 { m.depName2 = m.depName3; m.depUrl2 = m.depUrl3; }
else if i == 3 { m.depName3 = m.depName4; m.depUrl3 = m.depUrl4; }
else if i == 4 { m.depName4 = m.depName5; m.depUrl4 = m.depUrl5; }
else if i == 5 { m.depName5 = m.depName6; m.depUrl5 = m.depUrl6; }
else if i == 6 { m.depName6 = m.depName7; m.depUrl6 = m.depUrl7; }
i = i + 1;
}
m.depCount = m.depCount - 1;
return true;
}
// ---------------------------------------------------------------------------
// Serialize manifest back to TOML
// ---------------------------------------------------------------------------
func Manifest_ToString(m: Manifest) -> String {
let sb: StringBuilder = StringBuilder_New();
StringBuilder_Append(&sb, "[Package]\n");
StringBuilder_Append(&sb, "Name = \"");
StringBuilder_Append(&sb, m.name);
StringBuilder_Append(&sb, "\"\n");
StringBuilder_Append(&sb, "Version = \"");
StringBuilder_Append(&sb, m.version);
StringBuilder_Append(&sb, "\"\n");
StringBuilder_Append(&sb, "Type = \"");
StringBuilder_Append(&sb, m.pkgType);
StringBuilder_Append(&sb, "\"\n\n");
StringBuilder_Append(&sb, "[Build]\n");
StringBuilder_Append(&sb, "Output = \"");
StringBuilder_Append(&sb, m.output);
StringBuilder_Append(&sb, "\"\n");
if m.depCount > 0 {
StringBuilder_Append(&sb, "\n[dependencies]\n");
func Manifest_GetDepUrl(m: Manifest, name: String) -> String {
var i: int = 0;
while i < m.depCount {
var depName: String = "";
@@ -232,22 +145,109 @@ func Manifest_ToString(m: Manifest) -> String {
else if i == 5 { depName = m.depName5; depUrl = m.depUrl5; }
else if i == 6 { depName = m.depName6; depUrl = m.depUrl6; }
else if i == 7 { depName = m.depName7; depUrl = m.depUrl7; }
StringBuilder_Append(&sb, depName);
StringBuilder_Append(&sb, " = \"");
StringBuilder_Append(&sb, depUrl);
StringBuilder_Append(&sb, "\"\n");
if String_Eq(depName, name) { return depUrl; }
i = i + 1;
}
return "";
}
return StringBuilder_Build(&sb);
}
// ---------------------------------------------------------------------------
// Load manifest from file
// ---------------------------------------------------------------------------
func Manifest_AddDep(m: *Manifest, name: String, url: String) -> bool {
if Manifest_HasDep(*m, name) { return false; }
if m.depCount >= 8 { return false; }
if m.depCount == 0 { m.depName0 = name; m.depUrl0 = url; }
else if m.depCount == 1 { m.depName1 = name; m.depUrl1 = url; }
else if m.depCount == 2 { m.depName2 = name; m.depUrl2 = url; }
else if m.depCount == 3 { m.depName3 = name; m.depUrl3 = url; }
else if m.depCount == 4 { m.depName4 = name; m.depUrl4 = url; }
else if m.depCount == 5 { m.depName5 = name; m.depUrl5 = url; }
else if m.depCount == 6 { m.depName6 = name; m.depUrl6 = url; }
else if m.depCount == 7 { m.depName7 = name; m.depUrl7 = url; }
m.depCount = m.depCount + 1;
return true;
}
func Manifest_Load(path: String) -> Manifest {
let content: String = ReadFile(path);
return Manifest_Parse(content);
}
func Manifest_RemoveDep(m: *Manifest, name: String) -> bool {
var found: int = -1;
var i: int = 0;
while i < m.depCount {
var depName: String = "";
if i == 0 { depName = m.depName0; }
else if i == 1 { depName = m.depName1; }
else if i == 2 { depName = m.depName2; }
else if i == 3 { depName = m.depName3; }
else if i == 4 { depName = m.depName4; }
else if i == 5 { depName = m.depName5; }
else if i == 6 { depName = m.depName6; }
else if i == 7 { depName = m.depName7; }
if String_Eq(depName, name) { found = i; break; }
i = i + 1;
}
if found < 0 { return false; }
i = found;
while i < m.depCount - 1 {
if i == 0 { m.depName0 = m.depName1; m.depUrl0 = m.depUrl1; }
else if i == 1 { m.depName1 = m.depName2; m.depUrl1 = m.depUrl2; }
else if i == 2 { m.depName2 = m.depName3; m.depUrl2 = m.depUrl3; }
else if i == 3 { m.depName3 = m.depName4; m.depUrl3 = m.depUrl4; }
else if i == 4 { m.depName4 = m.depName5; m.depUrl4 = m.depUrl5; }
else if i == 5 { m.depName5 = m.depName6; m.depUrl5 = m.depUrl6; }
else if i == 6 { m.depName6 = m.depName7; m.depUrl6 = m.depUrl7; }
i = i + 1;
}
m.depCount = m.depCount - 1;
return true;
}
// ---------------------------------------------------------------------------
// Serialize manifest back to TOML
// ---------------------------------------------------------------------------
func Manifest_ToString(m: Manifest) -> String {
let sb: StringBuilder = StringBuilder_New();
StringBuilder_Append(&sb, "[Package]\n");
StringBuilder_Append(&sb, "Name = \"");
StringBuilder_Append(&sb, m.name);
StringBuilder_Append(&sb, "\"\n");
StringBuilder_Append(&sb, "Version = \"");
StringBuilder_Append(&sb, m.version);
StringBuilder_Append(&sb, "\"\n");
StringBuilder_Append(&sb, "Type = \"");
StringBuilder_Append(&sb, m.pkgType);
StringBuilder_Append(&sb, "\"\n\n");
StringBuilder_Append(&sb, "[Build]\n");
StringBuilder_Append(&sb, "Output = \"");
StringBuilder_Append(&sb, m.output);
StringBuilder_Append(&sb, "\"\n");
if m.depCount > 0 {
StringBuilder_Append(&sb, "\n[dependencies]\n");
var i: int = 0;
while i < m.depCount {
var depName: String = "";
var depUrl: String = "";
if i == 0 { depName = m.depName0; depUrl = m.depUrl0; }
else if i == 1 { depName = m.depName1; depUrl = m.depUrl1; }
else if i == 2 { depName = m.depName2; depUrl = m.depUrl2; }
else if i == 3 { depName = m.depName3; depUrl = m.depUrl3; }
else if i == 4 { depName = m.depName4; depUrl = m.depUrl4; }
else if i == 5 { depName = m.depName5; depUrl = m.depUrl5; }
else if i == 6 { depName = m.depName6; depUrl = m.depUrl6; }
else if i == 7 { depName = m.depName7; depUrl = m.depUrl7; }
StringBuilder_Append(&sb, depName);
StringBuilder_Append(&sb, " = \"");
StringBuilder_Append(&sb, depUrl);
StringBuilder_Append(&sb, "\"\n");
i = i + 1;
}
}
return StringBuilder_Build(&sb);
}
// ---------------------------------------------------------------------------
// Load manifest from file
// ---------------------------------------------------------------------------
func Manifest_Load(path: String) -> Manifest {
let content: String = ReadFile(path);
return Manifest_Parse(content);
}
}
+2284 -2249
View File
File diff suppressed because it is too large Load Diff
+93 -93
View File
@@ -1,115 +1,115 @@
// scope.bux — Symbol table with parent-chain lookup
module Scope {
// Symbol kinds
const skVar: int = 0;
const skFunc: int = 1;
const skType: int = 2;
const skConst: int = 3;
const skModule: int = 4;
// Symbol kinds
const skVar: int = 0;
const skFunc: int = 1;
const skType: int = 2;
const skConst: int = 3;
const skModule: int = 4;
// Maximum symbols per scope
const maxSymbols: int = 8192;
// Maximum symbols per scope
const maxSymbols: int = 8192;
struct Symbol {
kind: int;
name: String;
typeKind: int;
typeName: String;
refType: *TypeExpr; // original type expression (for func types, etc.)
isMutable: bool;
isPublic: bool;
decl: *Decl; // associated declaration (for funcs, structs, enums)
}
struct Scope {
symbols: *Symbol;
count: int;
parent: *Scope;
}
// ---------------------------------------------------------------------------
// Scope operations
// ---------------------------------------------------------------------------
func Scope_New() -> Scope {
let sz: uint = maxSymbols as uint * sizeof(Symbol);
let data: *Symbol = bux_alloc(sz) as *Symbol;
return Scope { symbols: data, count: 0, parent: null as *Scope };
}
func Scope_NewChild(parent: *Scope) -> Scope {
let sz: uint = maxSymbols as uint * sizeof(Symbol);
let data: *Symbol = bux_alloc(sz) as *Symbol;
return Scope { symbols: data, count: 0, parent: parent };
}
func Scope_Define(scope: *Scope, sym: Symbol) -> bool {
// Check local scope for duplicates
var i: int = 0;
while i < scope.count {
if String_Eq(scope.symbols[i].name, sym.name) {
return false;
}
i = i + 1;
struct Symbol {
kind: int;
name: String;
typeKind: int;
typeName: String;
refType: *TypeExpr; // original type expression (for func types, etc.)
isMutable: bool;
isPublic: bool;
decl: *Decl; // associated declaration (for funcs, structs, enums)
}
if scope.count < maxSymbols {
scope.symbols[scope.count] = sym;
scope.count = scope.count + 1;
return true;
}
return false;
}
func Scope_Lookup(scope: *Scope, name: String) -> Symbol {
var cur: *Scope = scope;
while cur != null as *Scope {
struct Scope {
symbols: *Symbol;
count: int;
parent: *Scope;
}
// ---------------------------------------------------------------------------
// Scope operations
// ---------------------------------------------------------------------------
func Scope_New() -> Scope {
let sz: uint = maxSymbols as uint * sizeof(Symbol);
let data: *Symbol = bux_alloc(sz) as *Symbol;
return Scope { symbols: data, count: 0, parent: null as *Scope };
}
func Scope_NewChild(parent: *Scope) -> Scope {
let sz: uint = maxSymbols as uint * sizeof(Symbol);
let data: *Symbol = bux_alloc(sz) as *Symbol;
return Scope { symbols: data, count: 0, parent: parent };
}
func Scope_Define(scope: *Scope, sym: Symbol) -> bool {
// Check local scope for duplicates
var i: int = 0;
while i < cur.count {
if String_Eq(cur.symbols[i].name, name) {
return cur.symbols[i];
while i < scope.count {
if String_Eq(scope.symbols[i].name, sym.name) {
return false;
}
i = i + 1;
}
cur = cur.parent;
}
var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", refType: null as *TypeExpr, isMutable: false, isPublic: false, decl: null as *Decl };
return empty;
}
func Scope_LookupLocal(scope: *Scope, name: String) -> Symbol {
var i: int = 0;
while i < scope.count {
if String_Eq(scope.symbols[i].name, name) {
return scope.symbols[i];
if scope.count < maxSymbols {
scope.symbols[scope.count] = sym;
scope.count = scope.count + 1;
return true;
}
i = i + 1;
return false;
}
var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", refType: null as *TypeExpr, isMutable: false, isPublic: false, decl: null as *Decl };
return empty;
}
func Scope_LookupUpTo(scope: *Scope, name: String, limit: *Scope) -> Symbol {
var cur: *Scope = scope;
while cur != null as *Scope {
func Scope_Lookup(scope: *Scope, name: String) -> Symbol {
var cur: *Scope = scope;
while cur != null as *Scope {
var i: int = 0;
while i < cur.count {
if String_Eq(cur.symbols[i].name, name) {
return cur.symbols[i];
}
i = i + 1;
}
cur = cur.parent;
}
var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", refType: null as *TypeExpr, isMutable: false, isPublic: false, decl: null as *Decl };
return empty;
}
func Scope_LookupLocal(scope: *Scope, name: String) -> Symbol {
var i: int = 0;
while i < cur.count {
if String_Eq(cur.symbols[i].name, name) {
return cur.symbols[i];
while i < scope.count {
if String_Eq(scope.symbols[i].name, name) {
return scope.symbols[i];
}
i = i + 1;
}
if cur == limit {
break;
}
cur = cur.parent;
var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", refType: null as *TypeExpr, isMutable: false, isPublic: false, decl: null as *Decl };
return empty;
}
var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", refType: null as *TypeExpr, isMutable: false, isPublic: false, decl: null as *Decl };
return empty;
}
func Scope_Free(scope: *Scope) {
bux_free(scope.symbols as *void);
}
func Scope_LookupUpTo(scope: *Scope, name: String, limit: *Scope) -> Symbol {
var cur: *Scope = scope;
while cur != null as *Scope {
var i: int = 0;
while i < cur.count {
if String_Eq(cur.symbols[i].name, name) {
return cur.symbols[i];
}
i = i + 1;
}
if cur == limit {
break;
}
cur = cur.parent;
}
var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", refType: null as *TypeExpr, isMutable: false, isPublic: false, decl: null as *Decl };
return empty;
}
func Scope_Free(scope: *Scope) {
bux_free(scope.symbols as *void);
}
}
+2231 -1981
View File
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -1,13 +1,13 @@
// source_location.bux — Source position tracking
module SourceLocation {
struct SourceLocation {
line: uint32;
column: uint32;
offset: uint32;
}
struct SourceLocation {
line: uint32;
column: uint32;
offset: uint32;
}
func SourceLocation_New(line: uint32, column: uint32, offset: uint32) -> SourceLocation {
return SourceLocation { line: line, column: column, offset: offset };
}
func SourceLocation_New(line: uint32, column: uint32, offset: uint32) -> SourceLocation {
return SourceLocation { line: line, column: column, offset: offset };
}
}
+330 -326
View File
@@ -1,346 +1,350 @@
// token.bux — Token kinds and helpers
module Token {
// ---------------------------------------------------------------------------
// TokenKind enum
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// TokenKind enum
// ---------------------------------------------------------------------------
// Literals
const tkIntLiteral: int = 0;
const tkFloatLiteral: int = 1;
const tkStringLiteral: int = 2;
const tkCharLiteral: int = 3;
const tkBoolLiteral: int = 4;
// Literals
const tkIntLiteral: int = 0;
const tkFloatLiteral: int = 1;
const tkStringLiteral: int = 2;
const tkCharLiteral: int = 3;
const tkBoolLiteral: int = 4;
// Identifiers
const tkIdent: int = 5;
const tkUnderscore: int = 6;
// Identifiers
const tkIdent: int = 5;
const tkUnderscore: int = 6;
// Control flow keywords
const tkIf: int = 7;
const tkElse: int = 8;
const tkWhile: int = 9;
const tkDo: int = 10;
const tkLoop: int = 11;
const tkFor: int = 12;
const tkIn: int = 13;
const tkBreak: int = 14;
const tkContinue: int = 15;
const tkReturn: int = 16;
const tkMatch: int = 17;
// Control flow keywords
const tkIf: int = 7;
const tkElse: int = 8;
const tkWhile: int = 9;
const tkDo: int = 10;
const tkLoop: int = 11;
const tkFor: int = 12;
const tkIn: int = 13;
const tkBreak: int = 14;
const tkContinue: int = 15;
const tkReturn: int = 16;
const tkMatch: int = 17;
// Declaration keywords
const tkFunc: int = 18;
const tkLet: int = 19;
const tkVar: int = 20;
const tkConst: int = 21;
const tkType: int = 22;
const tkStruct: int = 23;
const tkEnum: int = 24;
const tkUnion: int = 25;
const tkInterface: int = 26;
const tkExtend: int = 27;
const tkModule: int = 28;
const tkImport: int = 29;
const tkPub: int = 30;
const tkExtern: int = 31;
// Declaration keywords
const tkFunc: int = 18;
const tkLet: int = 19;
const tkVar: int = 20;
const tkConst: int = 21;
const tkType: int = 22;
const tkStruct: int = 23;
const tkEnum: int = 24;
const tkUnion: int = 25;
const tkInterface: int = 26;
const tkExtend: int = 27;
const tkModule: int = 28;
const tkImport: int = 29;
const tkPub: int = 30;
const tkExtern: int = 31;
// Other keywords
const tkAs: int = 32;
const tkIs: int = 33;
const tkNull: int = 34;
const tkSelf: int = 35;
const tkSuper: int = 36;
const tkSizeOf: int = 37;
// Other keywords
const tkAs: int = 32;
const tkIs: int = 33;
const tkNull: int = 34;
const tkSelf: int = 35;
const tkSuper: int = 36;
const tkSizeOf: int = 37;
// Punctuation
const tkLParen: int = 38;
const tkRParen: int = 39;
const tkLBrace: int = 40;
const tkRBrace: int = 41;
const tkLBracket: int = 42;
const tkRBracket: int = 43;
const tkComma: int = 44;
const tkSemicolon: int = 45;
const tkColon: int = 46;
const tkColonColon: int = 47;
const tkDot: int = 48;
const tkDotDot: int = 49;
const tkDotDotDot: int = 50;
const tkDotDotEqual: int = 51;
const tkArrow: int = 52;
const tkFatArrow: int = 53;
const tkAt: int = 54;
const tkHash: int = 55;
const tkQuestion: int = 56;
// Punctuation
const tkLParen: int = 38;
const tkRParen: int = 39;
const tkLBrace: int = 40;
const tkRBrace: int = 41;
const tkLBracket: int = 42;
const tkRBracket: int = 43;
const tkComma: int = 44;
const tkSemicolon: int = 45;
const tkColon: int = 46;
const tkColonColon: int = 47;
const tkDot: int = 48;
const tkDotDot: int = 49;
const tkDotDotDot: int = 50;
const tkDotDotEqual: int = 51;
const tkArrow: int = 52;
const tkFatArrow: int = 53;
const tkAt: int = 54;
const tkHash: int = 55;
const tkQuestion: int = 56;
// Arithmetic operators
const tkPlus: int = 57;
const tkMinus: int = 58;
const tkStar: int = 59;
const tkSlash: int = 60;
const tkPercent: int = 61;
const tkStarStar: int = 62;
const tkPlusPlus: int = 63;
const tkMinusMinus: int = 64;
// Arithmetic operators
const tkPlus: int = 57;
const tkMinus: int = 58;
const tkStar: int = 59;
const tkSlash: int = 60;
const tkPercent: int = 61;
const tkStarStar: int = 62;
const tkPlusPlus: int = 63;
const tkMinusMinus: int = 64;
// Bitwise operators
const tkAmp: int = 65;
const tkPipe: int = 66;
const tkCaret: int = 67;
const tkTilde: int = 68;
const tkShl: int = 69;
const tkShr: int = 70;
// Bitwise operators
const tkAmp: int = 65;
const tkPipe: int = 66;
const tkCaret: int = 67;
const tkTilde: int = 68;
const tkShl: int = 69;
const tkShr: int = 70;
// Logical operators
const tkAmpAmp: int = 71;
const tkPipePipe: int = 72;
const tkBang: int = 73;
// Logical operators
const tkAmpAmp: int = 71;
const tkPipePipe: int = 72;
const tkBang: int = 73;
// Comparison operators
const tkEq: int = 74;
const tkNe: int = 75;
const tkLt: int = 76;
const tkLe: int = 77;
const tkGt: int = 78;
const tkGe: int = 79;
// Comparison operators
const tkEq: int = 74;
const tkNe: int = 75;
const tkLt: int = 76;
const tkLe: int = 77;
const tkGt: int = 78;
const tkGe: int = 79;
// Assignment operators
const tkAssign: int = 80;
const tkPlusAssign: int = 81;
const tkMinusAssign: int = 82;
const tkStarAssign: int = 83;
const tkSlashAssign: int = 84;
const tkPercentAssign: int = 85;
const tkAmpAssign: int = 86;
const tkPipeAssign: int = 87;
const tkCaretAssign: int = 88;
const tkShlAssign: int = 89;
const tkShrAssign: int = 90;
// Assignment operators
const tkAssign: int = 80;
const tkPlusAssign: int = 81;
const tkMinusAssign: int = 82;
const tkStarAssign: int = 83;
const tkSlashAssign: int = 84;
const tkPercentAssign: int = 85;
const tkAmpAssign: int = 86;
const tkPipeAssign: int = 87;
const tkCaretAssign: int = 88;
const tkShlAssign: int = 89;
const tkShrAssign: int = 90;
// Compile-time intrinsics
const tkHashLine: int = 91;
const tkHashColumn: int = 92;
const tkHashFile: int = 93;
const tkHashFunction: int = 94;
const tkHashDate: int = 95;
const tkHashTime: int = 96;
const tkHashModule: int = 97;
// Compile-time intrinsics
const tkHashLine: int = 91;
const tkHashColumn: int = 92;
const tkHashFile: int = 93;
const tkHashFunction: int = 94;
const tkHashDate: int = 95;
const tkHashTime: int = 96;
const tkHashModule: int = 97;
// Special
const tkOwn: int = 98;
const tkNewLine: int = 99;
const tkEndOfFile: int = 100;
const tkUnknown: int = 101;
// Special
const tkOwn: int = 98;
const tkNewLine: int = 99;
const tkEndOfFile: int = 100;
const tkUnknown: int = 101;
// Async / concurrency
const tkAsync: int = 102;
const tkAwait: int = 103;
const tkSpawn: int = 104;
const tkDiscard: int = 105;
const tkDefer: int = 106;
const tkSwitch: int = 107;
const tkCase: int = 108;
const tkDefault: int = 109;
const tkUnsafe: int = 110;
// Async / concurrency
const tkAsync: int = 102;
const tkAwait: int = 103;
const tkSpawn: int = 104;
const tkDiscard: int = 105;
const tkDefer: int = 106;
const tkSwitch: int = 107;
const tkCase: int = 108;
const tkDefault: int = 109;
const tkUnsafe: int = 110;
// ---------------------------------------------------------------------------
// Token struct
// ---------------------------------------------------------------------------
// Lifetime parameter token: 'a, 'b, ... (not a char literal)
const tkLifetime: int = 111;
struct Token {
kind: int;
text: String;
line: uint32;
column: uint32;
offset: uint32;
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
func Token_IsKeyword(kind: int) -> bool {
if kind >= tkIf && kind <= tkIn { return true; }
if kind >= tkBreak && kind <= tkMatch { return true; }
if kind >= tkFunc && kind <= tkExtern { return true; }
if kind >= tkAs && kind <= tkSuper { return true; }
if kind == tkSizeOf { return true; }
if kind >= tkDefer && kind <= tkUnsafe { return true; }
if kind >= tkAsync && kind <= tkSpawn { return true; }
return false;
}
func Token_IsLiteral(kind: int) -> bool {
if kind >= tkIntLiteral && kind <= tkBoolLiteral { return true; }
return false;
}
func Token_IsOperator(kind: int) -> bool {
if kind >= tkPlus && kind <= tkShrAssign { return true; }
return false;
}
func Token_IsEof(kind: int) -> bool {
return kind == tkEndOfFile;
}
func Token_KeywordKind(text: String) -> int {
if String_Eq(text, "func") { return tkFunc; }
if String_Eq(text, "let") { return tkLet; }
if String_Eq(text, "var") { return tkVar; }
if String_Eq(text, "const") { return tkConst; }
if String_Eq(text, "type") { return tkType; }
if String_Eq(text, "struct") { return tkStruct; }
if String_Eq(text, "enum") { return tkEnum; }
if String_Eq(text, "union") { return tkUnion; }
if String_Eq(text, "interface") { return tkInterface; }
if String_Eq(text, "extend") { return tkExtend; }
if String_Eq(text, "module") { return tkModule; }
if String_Eq(text, "import") { return tkImport; }
if String_Eq(text, "pub") { return tkPub; }
if String_Eq(text, "extern") { return tkExtern; }
if String_Eq(text, "if") { return tkIf; }
if String_Eq(text, "else") { return tkElse; }
if String_Eq(text, "while") { return tkWhile; }
if String_Eq(text, "do") { return tkDo; }
if String_Eq(text, "loop") { return tkLoop; }
if String_Eq(text, "for") { return tkFor; }
if String_Eq(text, "in") { return tkIn; }
if String_Eq(text, "break") { return tkBreak; }
if String_Eq(text, "continue") { return tkContinue; }
if String_Eq(text, "return") { return tkReturn; }
if String_Eq(text, "match") { return tkMatch; }
if String_Eq(text, "as") { return tkAs; }
if String_Eq(text, "is") { return tkIs; }
if String_Eq(text, "null") { return tkNull; }
if String_Eq(text, "self") { return tkSelf; }
if String_Eq(text, "super") { return tkSuper; }
if String_Eq(text, "sizeof") { return tkSizeOf; }
if String_Eq(text, "defer") { return tkDefer; }
if String_Eq(text, "switch") { return tkSwitch; }
if String_Eq(text, "case") { return tkCase; }
if String_Eq(text, "default") { return tkDefault; }
if String_Eq(text, "unsafe") { return tkUnsafe; }
if String_Eq(text, "discard") { return tkDiscard; }
if String_Eq(text, "async") { return tkAsync; }
if String_Eq(text, "await") { return tkAwait; }
if String_Eq(text, "spawn") { return tkSpawn; }
if String_Eq(text, "true") { return tkBoolLiteral; }
if String_Eq(text, "false") { return tkBoolLiteral; }
return tkIdent;
}
func Token_KindName(kind: int) -> String {
if kind == tkIntLiteral { return "integer literal"; }
if kind == tkFloatLiteral { return "float literal"; }
if kind == tkStringLiteral { return "string literal"; }
if kind == tkCharLiteral { return "char literal"; }
if kind == tkBoolLiteral { return "boolean literal"; }
if kind == tkIdent { return "identifier"; }
if kind == tkUnderscore { return "_"; }
if kind == tkSizeOf { return "sizeof"; }
if kind == tkIf { return "if"; }
if kind == tkElse { return "else"; }
if kind == tkWhile { return "while"; }
if kind == tkDo { return "do"; }
if kind == tkLoop { return "loop"; }
if kind == tkFor { return "for"; }
if kind == tkIn { return "in"; }
if kind == tkBreak { return "break"; }
if kind == tkContinue { return "continue"; }
if kind == tkReturn { return "return"; }
if kind == tkMatch { return "match"; }
if kind == tkFunc { return "func"; }
if kind == tkLet { return "let"; }
if kind == tkVar { return "var"; }
if kind == tkConst { return "const"; }
if kind == tkType { return "type"; }
if kind == tkStruct { return "struct"; }
if kind == tkEnum { return "enum"; }
if kind == tkUnion { return "union"; }
if kind == tkInterface { return "interface"; }
if kind == tkExtend { return "extend"; }
if kind == tkModule { return "module"; }
if kind == tkImport { return "import"; }
if kind == tkPub { return "pub"; }
if kind == tkExtern { return "extern"; }
if kind == tkAs { return "as"; }
if kind == tkIs { return "is"; }
if kind == tkNull { return "null"; }
if kind == tkSelf { return "self"; }
if kind == tkSuper { return "super"; }
if kind == tkUnsafe { return "unsafe"; }
if kind == tkDefer { return "defer"; }
if kind == tkSwitch { return "switch"; }
if kind == tkCase { return "case"; }
if kind == tkDefault { return "default"; }
if kind == tkAsync { return "async"; }
if kind == tkAwait { return "await"; }
if kind == tkSpawn { return "spawn"; }
if kind == tkDiscard { return "discard"; }
if kind == tkLParen { return "("; }
if kind == tkRParen { return ")"; }
if kind == tkLBrace { return "{"; }
if kind == tkRBrace { return "}"; }
if kind == tkLBracket { return "["; }
if kind == tkRBracket { return "]"; }
if kind == tkComma { return ","; }
if kind == tkSemicolon { return ";"; }
if kind == tkColon { return ":"; }
if kind == tkColonColon { return "::"; }
if kind == tkDot { return "."; }
if kind == tkDotDot { return ".."; }
if kind == tkDotDotDot { return "..."; }
if kind == tkDotDotEqual { return "..="; }
if kind == tkArrow { return "->"; }
if kind == tkFatArrow { return "=>"; }
if kind == tkAt { return "@"; }
if kind == tkHash { return "#"; }
if kind == tkQuestion { return "?"; }
if kind == tkPlus { return "+"; }
if kind == tkMinus { return "-"; }
if kind == tkStar { return "*"; }
if kind == tkSlash { return "/"; }
if kind == tkPercent { return "%"; }
if kind == tkStarStar { return "**"; }
if kind == tkPlusPlus { return "++"; }
if kind == tkMinusMinus { return "--"; }
if kind == tkAmp { return "&"; }
if kind == tkPipe { return "|"; }
if kind == tkCaret { return "^"; }
if kind == tkTilde { return "~"; }
if kind == tkShl { return "<<"; }
if kind == tkShr { return ">>"; }
if kind == tkAmpAmp { return "&&"; }
if kind == tkPipePipe { return "||"; }
if kind == tkBang { return "!"; }
if kind == tkEq { return "=="; }
if kind == tkNe { return "!="; }
if kind == tkLt { return "<"; }
if kind == tkLe { return "<="; }
if kind == tkGt { return ">"; }
if kind == tkGe { return ">="; }
if kind == tkAssign { return "="; }
if kind == tkPlusAssign { return "+="; }
if kind == tkMinusAssign { return "-="; }
if kind == tkStarAssign { return "*="; }
if kind == tkSlashAssign { return "/="; }
if kind == tkPercentAssign { return "%="; }
if kind == tkAmpAssign { return "&="; }
if kind == tkPipeAssign { return "|="; }
if kind == tkCaretAssign { return "^="; }
if kind == tkShlAssign { return "<<="; }
if kind == tkShrAssign { return ">>="; }
if kind == tkHashLine { return "#line"; }
if kind == tkHashColumn { return "#column"; }
if kind == tkHashFile { return "#file"; }
if kind == tkHashFunction { return "#function"; }
if kind == tkHashDate { return "#date"; }
if kind == tkHashTime { return "#time"; }
if kind == tkHashModule { return "#module"; }
if kind == tkNewLine { return "newline"; }
if kind == tkEndOfFile { return "end of file"; }
return "unknown token";
}
// ---------------------------------------------------------------------------
// Token struct
// ---------------------------------------------------------------------------
struct Token {
kind: int;
text: String;
line: uint32;
column: uint32;
offset: uint32;
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
func Token_IsKeyword(kind: int) -> bool {
if kind >= tkIf && kind <= tkIn { return true; }
if kind >= tkBreak && kind <= tkMatch { return true; }
if kind >= tkFunc && kind <= tkExtern { return true; }
if kind >= tkAs && kind <= tkSuper { return true; }
if kind == tkSizeOf { return true; }
if kind >= tkDefer && kind <= tkUnsafe { return true; }
if kind >= tkAsync && kind <= tkSpawn { return true; }
return false;
}
func Token_IsLiteral(kind: int) -> bool {
if kind >= tkIntLiteral && kind <= tkBoolLiteral { return true; }
return false;
}
func Token_IsOperator(kind: int) -> bool {
if kind >= tkPlus && kind <= tkShrAssign { return true; }
return false;
}
func Token_IsEof(kind: int) -> bool {
return kind == tkEndOfFile;
}
func Token_KeywordKind(text: String) -> int {
if String_Eq(text, "func") { return tkFunc; }
if String_Eq(text, "let") { return tkLet; }
if String_Eq(text, "var") { return tkVar; }
if String_Eq(text, "const") { return tkConst; }
if String_Eq(text, "type") { return tkType; }
if String_Eq(text, "struct") { return tkStruct; }
if String_Eq(text, "enum") { return tkEnum; }
if String_Eq(text, "union") { return tkUnion; }
if String_Eq(text, "interface") { return tkInterface; }
if String_Eq(text, "extend") { return tkExtend; }
if String_Eq(text, "module") { return tkModule; }
if String_Eq(text, "import") { return tkImport; }
if String_Eq(text, "pub") { return tkPub; }
if String_Eq(text, "extern") { return tkExtern; }
if String_Eq(text, "if") { return tkIf; }
if String_Eq(text, "else") { return tkElse; }
if String_Eq(text, "while") { return tkWhile; }
if String_Eq(text, "do") { return tkDo; }
if String_Eq(text, "loop") { return tkLoop; }
if String_Eq(text, "for") { return tkFor; }
if String_Eq(text, "in") { return tkIn; }
if String_Eq(text, "break") { return tkBreak; }
if String_Eq(text, "continue") { return tkContinue; }
if String_Eq(text, "return") { return tkReturn; }
if String_Eq(text, "match") { return tkMatch; }
if String_Eq(text, "as") { return tkAs; }
if String_Eq(text, "is") { return tkIs; }
if String_Eq(text, "null") { return tkNull; }
if String_Eq(text, "self") { return tkSelf; }
if String_Eq(text, "super") { return tkSuper; }
if String_Eq(text, "sizeof") { return tkSizeOf; }
if String_Eq(text, "defer") { return tkDefer; }
if String_Eq(text, "switch") { return tkSwitch; }
if String_Eq(text, "case") { return tkCase; }
if String_Eq(text, "default") { return tkDefault; }
if String_Eq(text, "unsafe") { return tkUnsafe; }
if String_Eq(text, "discard") { return tkDiscard; }
if String_Eq(text, "async") { return tkAsync; }
if String_Eq(text, "await") { return tkAwait; }
if String_Eq(text, "spawn") { return tkSpawn; }
if String_Eq(text, "true") { return tkBoolLiteral; }
if String_Eq(text, "false") { return tkBoolLiteral; }
return tkIdent;
}
func Token_KindName(kind: int) -> String {
if kind == tkIntLiteral { return "integer literal"; }
if kind == tkFloatLiteral { return "float literal"; }
if kind == tkStringLiteral { return "string literal"; }
if kind == tkCharLiteral { return "char literal"; }
if kind == tkBoolLiteral { return "boolean literal"; }
if kind == tkIdent { return "identifier"; }
if kind == tkUnderscore { return "_"; }
if kind == tkSizeOf { return "sizeof"; }
if kind == tkIf { return "if"; }
if kind == tkElse { return "else"; }
if kind == tkWhile { return "while"; }
if kind == tkDo { return "do"; }
if kind == tkLoop { return "loop"; }
if kind == tkFor { return "for"; }
if kind == tkIn { return "in"; }
if kind == tkBreak { return "break"; }
if kind == tkContinue { return "continue"; }
if kind == tkReturn { return "return"; }
if kind == tkMatch { return "match"; }
if kind == tkFunc { return "func"; }
if kind == tkLet { return "let"; }
if kind == tkVar { return "var"; }
if kind == tkConst { return "const"; }
if kind == tkType { return "type"; }
if kind == tkStruct { return "struct"; }
if kind == tkEnum { return "enum"; }
if kind == tkUnion { return "union"; }
if kind == tkInterface { return "interface"; }
if kind == tkExtend { return "extend"; }
if kind == tkModule { return "module"; }
if kind == tkImport { return "import"; }
if kind == tkPub { return "pub"; }
if kind == tkExtern { return "extern"; }
if kind == tkAs { return "as"; }
if kind == tkIs { return "is"; }
if kind == tkNull { return "null"; }
if kind == tkSelf { return "self"; }
if kind == tkSuper { return "super"; }
if kind == tkUnsafe { return "unsafe"; }
if kind == tkDefer { return "defer"; }
if kind == tkSwitch { return "switch"; }
if kind == tkCase { return "case"; }
if kind == tkDefault { return "default"; }
if kind == tkAsync { return "async"; }
if kind == tkAwait { return "await"; }
if kind == tkSpawn { return "spawn"; }
if kind == tkDiscard { return "discard"; }
if kind == tkLParen { return "("; }
if kind == tkRParen { return ")"; }
if kind == tkLBrace { return "{"; }
if kind == tkRBrace { return "}"; }
if kind == tkLBracket { return "["; }
if kind == tkRBracket { return "]"; }
if kind == tkComma { return ","; }
if kind == tkSemicolon { return ";"; }
if kind == tkColon { return ":"; }
if kind == tkColonColon { return "::"; }
if kind == tkDot { return "."; }
if kind == tkDotDot { return ".."; }
if kind == tkDotDotDot { return "..."; }
if kind == tkDotDotEqual { return "..="; }
if kind == tkArrow { return "->"; }
if kind == tkFatArrow { return "=>"; }
if kind == tkAt { return "@"; }
if kind == tkHash { return "#"; }
if kind == tkQuestion { return "?"; }
if kind == tkPlus { return "+"; }
if kind == tkMinus { return "-"; }
if kind == tkStar { return "*"; }
if kind == tkSlash { return "/"; }
if kind == tkPercent { return "%"; }
if kind == tkStarStar { return "**"; }
if kind == tkPlusPlus { return "++"; }
if kind == tkMinusMinus { return "--"; }
if kind == tkAmp { return "&"; }
if kind == tkPipe { return "|"; }
if kind == tkCaret { return "^"; }
if kind == tkTilde { return "~"; }
if kind == tkShl { return "<<"; }
if kind == tkShr { return ">>"; }
if kind == tkAmpAmp { return "&&"; }
if kind == tkPipePipe { return "||"; }
if kind == tkBang { return "!"; }
if kind == tkEq { return "=="; }
if kind == tkNe { return "!="; }
if kind == tkLt { return "<"; }
if kind == tkLe { return "<="; }
if kind == tkGt { return ">"; }
if kind == tkGe { return ">="; }
if kind == tkAssign { return "="; }
if kind == tkPlusAssign { return "+="; }
if kind == tkMinusAssign { return "-="; }
if kind == tkStarAssign { return "*="; }
if kind == tkSlashAssign { return "/="; }
if kind == tkPercentAssign { return "%="; }
if kind == tkAmpAssign { return "&="; }
if kind == tkPipeAssign { return "|="; }
if kind == tkCaretAssign { return "^="; }
if kind == tkShlAssign { return "<<="; }
if kind == tkShrAssign { return ">>="; }
if kind == tkHashLine { return "#line"; }
if kind == tkHashColumn { return "#column"; }
if kind == tkHashFile { return "#file"; }
if kind == tkHashFunction { return "#function"; }
if kind == tkHashDate { return "#date"; }
if kind == tkHashTime { return "#time"; }
if kind == tkHashModule { return "#module"; }
if kind == tkLifetime { return "lifetime"; }
if kind == tkNewLine { return "newline"; }
if kind == tkEndOfFile { return "end of file"; }
return "unknown token";
}
}

Some files were not shown because too many files have changed in this diff Show More