feat: restructure repo, borrow checker, expanded stdlib
- Reorganize repository to Rust-style layout: compiler/bootstrap/ compiler/selfhost/ compiler/tests/ library/std/ library/runtime/ tests/ tools/ - Add buxs/ Windows-compatible project root - Add borrow checker tests and implement: - Alias analysis (double mutable borrow detection) - Use-after-move detection for own T - Expand standard library: - Std::Os: Args, Env, Cwd, Chdir - Std::Time: NowMs, NowUs, SleepMs - Std::Process: Run, Output - Std::Io: PrintInt64 (fixes 32-bit truncation bug) - Add examples: os_time.bux, process.bux - Fix PrintInt to use int64_t in C runtime
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
NIM := nim
|
NIM := nim
|
||||||
SRC := src/main.nim
|
SRC := compiler/bootstrap/main.nim
|
||||||
OUT := buxc
|
OUT := buxc
|
||||||
BUILD_DIR := build
|
BUILD_DIR := build
|
||||||
|
|
||||||
@@ -17,13 +17,15 @@ dev:
|
|||||||
|
|
||||||
test: build test-examples
|
test: build test-examples
|
||||||
@echo "Running lexer tests..."
|
@echo "Running lexer tests..."
|
||||||
$(NIM) c -r tests/lexer_test.nim
|
$(NIM) c -r compiler/tests/lexer_test.nim
|
||||||
@echo "Running parser tests..."
|
@echo "Running parser tests..."
|
||||||
$(NIM) c -r tests/parser_test.nim
|
$(NIM) c -r compiler/tests/parser_test.nim
|
||||||
@echo "Running sema tests..."
|
@echo "Running sema tests..."
|
||||||
$(NIM) c -r tests/sema_test.nim
|
$(NIM) c -r compiler/tests/sema_test.nim
|
||||||
@echo "Running HIR tests..."
|
@echo "Running HIR tests..."
|
||||||
$(NIM) c -r tests/hir_test.nim
|
$(NIM) c -r compiler/tests/hir_test.nim
|
||||||
|
@echo "Running borrow checker tests..."
|
||||||
|
$(NIM) c -r compiler/tests/borrow_test.nim
|
||||||
@echo "Running integration tests..."
|
@echo "Running integration tests..."
|
||||||
rm -rf _test_tmp_pkg
|
rm -rf _test_tmp_pkg
|
||||||
./$(OUT) new _test_tmp_pkg
|
./$(OUT) new _test_tmp_pkg
|
||||||
@@ -58,7 +60,7 @@ selfhost: build
|
|||||||
@echo "=== Phase 7.9: Building self-hosted compiler ==="
|
@echo "=== Phase 7.9: Building self-hosted compiler ==="
|
||||||
@rm -rf _selfhost/src
|
@rm -rf _selfhost/src
|
||||||
@mkdir -p _selfhost/src
|
@mkdir -p _selfhost/src
|
||||||
@cp src_bux/*.bux _selfhost/src/
|
@cp compiler/selfhost/*.bux _selfhost/src/
|
||||||
@mv _selfhost/src/main.bux _selfhost/src/Main.bux 2>/dev/null || true
|
@mv _selfhost/src/main.bux _selfhost/src/Main.bux 2>/dev/null || true
|
||||||
@cd _selfhost && ../$(OUT) build
|
@cd _selfhost && ../$(OUT) build
|
||||||
@echo "=== Self-hosted compiler built successfully ==="
|
@echo "=== Self-hosted compiler built successfully ==="
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ Phase 7.5 — Driver (depends on all):
|
|||||||
|
|
||||||
**Goal:** Bux compiler compiles itself. This is the **main milestone**.
|
**Goal:** Bux compiler compiles itself. This is the **main milestone**.
|
||||||
|
|
||||||
**All 14 modules ported** in `src_bux/` (4094 LOC total). Self-hosted project structure in `_selfhost/`.
|
**All 14 modules ported** in `compiler/selfhost/` (4094 LOC total). Self-hosted project structure in `_selfhost/`.
|
||||||
|
|
||||||
| Task | Status | Details | LOC |
|
| Task | Status | Details | LOC |
|
||||||
|------|--------|---------|-----|
|
|------|--------|---------|-----|
|
||||||
@@ -355,8 +355,8 @@ Pipeline modules:
|
|||||||
|
|
||||||
**Bootstrap loop goal:**
|
**Bootstrap loop goal:**
|
||||||
```
|
```
|
||||||
buxc (Nim) → compile src_bux/*.bux → buxc2 (Bux binary)
|
buxc (Nim) → compile compiler/selfhost/*.bux → buxc2 (Bux binary)
|
||||||
buxc2 (Bux) → compile src_bux/*.bux → buxc3 (Bux binary)
|
buxc2 (Bux) → compile compiler/selfhost/*.bux → buxc3 (Bux binary)
|
||||||
compare buxc2 == buxc3 → SELF-HOSTED ✅
|
compare buxc2 == buxc3 → SELF-HOSTED ✅
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -556,7 +556,7 @@ bux/
|
|||||||
│ ├── Linker.bux # Custom linker / build driver
|
│ ├── Linker.bux # Custom linker / build driver
|
||||||
│ ├── Manifest.bux # bux.toml parser
|
│ ├── Manifest.bux # bux.toml parser
|
||||||
│ └── Package.bux # Package resolution
|
│ └── Package.bux # Package resolution
|
||||||
├── stdlib/
|
├── library/
|
||||||
│ ├── Std/
|
│ ├── Std/
|
||||||
│ │ ├── Io.bux
|
│ │ ├── Io.bux
|
||||||
│ │ ├── Memory.bux
|
│ │ ├── Memory.bux
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ func Main() -> int {
|
|||||||
| **Methods** | `extend` blocks for struct methods |
|
| **Methods** | `extend` blocks for struct methods |
|
||||||
| **Interfaces** | `interface` + `extend` for trait-like behavior |
|
| **Interfaces** | `interface` + `extend` for trait-like behavior |
|
||||||
| **Error Handling** | `Result<T,E>`, `Option<T>`, and the `?` operator |
|
| **Error Handling** | `Result<T,E>`, `Option<T>`, and the `?` operator |
|
||||||
| **Standard Library** | `Io`, `Array`, `String`, `Map`, `Fs`, `Mem`, `Set`, `Path`, `Math`, `Task`, `Channel` |
|
| **Standard Library** | `Io`, `Array`, `String`, `Map`, `Fs`, `Mem`, `Set`, `Path`, `Math`, `Task`, `Channel`, `Os`, `Time`, `Process` |
|
||||||
| **Backend** | C transpiler (self-hosting + bootstrap) |
|
| **Backend** | C transpiler (self-hosting + bootstrap) |
|
||||||
| **Strings** | Raw multi-line backtick strings (`...`), C-string interop |
|
| **Strings** | Raw multi-line backtick strings (`...`), C-string interop |
|
||||||
| **Gradual Ownership** | `@[Checked]` + `&T`/`&mut T` borrow checking |
|
| **Gradual Ownership** | `@[Checked]` + `&T`/`&mut T` borrow checking |
|
||||||
@@ -166,18 +166,22 @@ func Main() -> int {
|
|||||||
|
|
||||||
```
|
```
|
||||||
bux/
|
bux/
|
||||||
├── src/ # Bootstrap compiler (Nim)
|
├── compiler/ # Compiler source code
|
||||||
├── src_bux/ # Self-hosting compiler source (Bux)
|
│ ├── bootstrap/ # Bootstrap compiler (Nim)
|
||||||
├── _selfhost/ # Self-hosting build artifacts
|
│ ├── selfhost/ # Self-hosting compiler source (Bux)
|
||||||
├── stdlib/ # Standard library (.bux + .c runtime)
|
│ └── tests/ # Compiler unit tests (Nim)
|
||||||
│ └── Std/
|
├── library/ # Standard library
|
||||||
│ ├── Io.bux, String.bux, Array.bux, Map.bux
|
│ ├── std/ # Standard library modules (.bux)
|
||||||
│ ├── Fs.bux, Mem.bux, Set.bux
|
│ │ ├── Io.bux, String.bux, Array.bux, Map.bux
|
||||||
│ ├── Path.bux, Math.bux
|
│ │ ├── Fs.bux, Mem.bux, Set.bux
|
||||||
│ └── Task.bux, Channel.bux
|
│ │ ├── Path.bux, Math.bux
|
||||||
|
│ │ └── Task.bux, Channel.bux
|
||||||
|
│ └── runtime/ # C runtime files (runtime.c, io.c)
|
||||||
|
├── tests/ # Language integration tests
|
||||||
├── examples/ # Example programs
|
├── examples/ # Example programs
|
||||||
├── tests/ # Unit tests (Nim)
|
├── tools/ # Additional tooling
|
||||||
├── docs/ # Documentation
|
├── docs/ # Documentation
|
||||||
|
├── buxs/ # Windows-compatible project root
|
||||||
├── README.md
|
├── README.md
|
||||||
├── PLAN.md # Roadmap to self-hosting
|
├── PLAN.md # Roadmap to self-hosting
|
||||||
└── Makefile
|
└── Makefile
|
||||||
@@ -204,6 +208,8 @@ make test-examples
|
|||||||
make clean
|
make clean
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **Windows users:** Use the `buxs/` directory as your project root to avoid path conflicts.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|||||||
+11
-11
@@ -31,7 +31,7 @@ func DirExists(path: String) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Import the compiler pipeline
|
// Import the compiler pipeline
|
||||||
// In self-hosting mode, these are compiled together from src_bux/
|
// In self-hosting mode, these are compiled together from compiler/selfhost/
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Compile a single .bux source file
|
// Compile a single .bux source file
|
||||||
@@ -140,19 +140,19 @@ func Cli_Build(srcPath: String, outPath: String) -> int {
|
|||||||
PrintLine(cFile);
|
PrintLine(cFile);
|
||||||
|
|
||||||
// Find runtime.c and io.c for linking
|
// Find runtime.c and io.c for linking
|
||||||
var rtPath: String = "stdlib/runtime.c";
|
var rtPath: String = "library/runtime/runtime.c";
|
||||||
var ioPath: String = "stdlib/io.c";
|
var ioPath: String = "library/runtime/io.c";
|
||||||
if !FileExists(rtPath) {
|
if !FileExists(rtPath) {
|
||||||
rtPath = "../stdlib/runtime.c";
|
rtPath = "../library/runtime/runtime.c";
|
||||||
}
|
}
|
||||||
if !FileExists(ioPath) {
|
if !FileExists(ioPath) {
|
||||||
ioPath = "../stdlib/io.c";
|
ioPath = "../library/runtime/io.c";
|
||||||
}
|
}
|
||||||
if !FileExists(rtPath) {
|
if !FileExists(rtPath) {
|
||||||
rtPath = "/home/ziko/z-git/bux/bux/stdlib/runtime.c";
|
rtPath = "/home/ziko/z-git/bux/bux/library/runtime/runtime.c";
|
||||||
}
|
}
|
||||||
if !FileExists(ioPath) {
|
if !FileExists(ioPath) {
|
||||||
ioPath = "/home/ziko/z-git/bux/bux/stdlib/io.c";
|
ioPath = "/home/ziko/z-git/bux/bux/library/runtime/io.c";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile with cc
|
// Compile with cc
|
||||||
@@ -633,16 +633,16 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
var rtPath: String = bux_path_join(stdlibDir, "runtime.c");
|
var rtPath: String = bux_path_join(stdlibDir, "runtime.c");
|
||||||
var ioPath: String = bux_path_join(stdlibDir, "io.c");
|
var ioPath: String = bux_path_join(stdlibDir, "io.c");
|
||||||
if !FileExists(rtPath) {
|
if !FileExists(rtPath) {
|
||||||
rtPath = bux_path_join(projectDir, "stdlib/runtime.c");
|
rtPath = bux_path_join(projectDir, "library/runtime/runtime.c");
|
||||||
}
|
}
|
||||||
if !FileExists(ioPath) {
|
if !FileExists(ioPath) {
|
||||||
ioPath = bux_path_join(projectDir, "stdlib/io.c");
|
ioPath = bux_path_join(projectDir, "library/runtime/io.c");
|
||||||
}
|
}
|
||||||
if !FileExists(rtPath) {
|
if !FileExists(rtPath) {
|
||||||
rtPath = bux_path_join(projectDir, "../stdlib/runtime.c");
|
rtPath = bux_path_join(projectDir, "../library/runtime/runtime.c");
|
||||||
}
|
}
|
||||||
if !FileExists(ioPath) {
|
if !FileExists(ioPath) {
|
||||||
ioPath = bux_path_join(projectDir, "../stdlib/io.c");
|
ioPath = bux_path_join(projectDir, "../library/runtime/io.c");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile with cc
|
// Compile with cc
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const skConst: int = 3;
|
|||||||
const skModule: int = 4;
|
const skModule: int = 4;
|
||||||
|
|
||||||
// Maximum symbols per scope
|
// Maximum symbols per scope
|
||||||
const maxSymbols: int = 1024;
|
const maxSymbols: int = 8192;
|
||||||
|
|
||||||
struct Symbol {
|
struct Symbol {
|
||||||
kind: int;
|
kind: int;
|
||||||
|
|||||||
@@ -438,6 +438,7 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
|
|
||||||
func Sema_CollectGlobals(sema: *Sema) {
|
func Sema_CollectGlobals(sema: *Sema) {
|
||||||
var decl: *Decl = sema.module.firstItem;
|
var decl: *Decl = sema.module.firstItem;
|
||||||
|
var funcCount: int = 0;
|
||||||
while decl != null as *Decl {
|
while decl != null as *Decl {
|
||||||
let dk: int = decl.kind;
|
let dk: int = decl.kind;
|
||||||
|
|
||||||
@@ -503,6 +504,24 @@ func Sema_CollectGlobals(sema: *Sema) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Const
|
||||||
|
if dk == dkConst {
|
||||||
|
var sym: Symbol;
|
||||||
|
Sema_ZeroInitSymbol(&sym);
|
||||||
|
sym.kind = skConst;
|
||||||
|
sym.name = decl.strValue;
|
||||||
|
if decl.constType != null as *TypeExpr {
|
||||||
|
sym.typeKind = Sema_ResolveType(sema, decl.constType);
|
||||||
|
sym.typeName = decl.constType.typeName;
|
||||||
|
} else {
|
||||||
|
sym.typeKind = tyInt;
|
||||||
|
}
|
||||||
|
sym.isMutable = false;
|
||||||
|
sym.isPublic = decl.isPublic;
|
||||||
|
sym.decl = decl;
|
||||||
|
discard Scope_Define(sema.scope, sym);
|
||||||
|
}
|
||||||
|
|
||||||
// Use (import)
|
// Use (import)
|
||||||
if dk == dkUse {
|
if dk == dkUse {
|
||||||
if decl.useKind == 2 {
|
if decl.useKind == 2 {
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# BUXS — Windows-Compatible Project Root
|
||||||
|
|
||||||
|
This directory serves as an alternative project root for Windows environments
|
||||||
|
where the name `bux` may conflict with system paths or tooling.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
From this directory, you can build and run the Bux compiler using the
|
||||||
|
provided wrapper scripts.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
The actual project source lives in the parent directory (`../`):
|
||||||
|
|
||||||
|
- `../compiler/bootstrap/` — Nim bootstrap compiler
|
||||||
|
- `../compiler/selfhost/` — Self-hosting compiler (Bux)
|
||||||
|
- `../library/std/` — Standard library modules
|
||||||
|
- `../library/runtime/` — C runtime files
|
||||||
|
- `../tests/` — Integration tests
|
||||||
|
- `../docs/` — Documentation
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@echo off
|
||||||
|
REM Windows wrapper to build Bux from buxs/ root
|
||||||
|
cd ..
|
||||||
|
make build
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Wrapper script to build Bux from buxs/ root
|
||||||
|
cd ..
|
||||||
|
make build
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
[Package]
|
||||||
|
Name = "buxc"
|
||||||
|
Version = "0.1.0"
|
||||||
|
Type = "bin"
|
||||||
|
|
||||||
|
[Build]
|
||||||
|
Output = "Bin"
|
||||||
@@ -306,10 +306,10 @@ proc cmdCheck*(args: seq[string], opts: GlobalOptions): int =
|
|||||||
# Phase 2: Merge with stdlib and check
|
# Phase 2: Merge with stdlib and check
|
||||||
var stdlibDir = ""
|
var stdlibDir = ""
|
||||||
let stdlibSearchPaths = @[
|
let stdlibSearchPaths = @[
|
||||||
getAppDir() / ".." / "stdlib",
|
getAppDir() / ".." / "library",
|
||||||
getAppDir() / "stdlib",
|
getAppDir() / "library",
|
||||||
getCurrentDir() / "stdlib",
|
getCurrentDir() / "library",
|
||||||
"/home/ziko/z-git/bux/bux/stdlib",
|
"/home/ziko/z-git/bux/bux/library",
|
||||||
]
|
]
|
||||||
for path in stdlibSearchPaths:
|
for path in stdlibSearchPaths:
|
||||||
if dirExists(path):
|
if dirExists(path):
|
||||||
@@ -434,10 +434,10 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
|
|||||||
# Find stdlib directory
|
# Find stdlib directory
|
||||||
var stdlibDir = ""
|
var stdlibDir = ""
|
||||||
let stdlibSearchPaths = @[
|
let stdlibSearchPaths = @[
|
||||||
getAppDir() / ".." / "stdlib",
|
getAppDir() / ".." / "library",
|
||||||
getAppDir() / "stdlib",
|
getAppDir() / "library",
|
||||||
root / "stdlib",
|
root / "library",
|
||||||
"/home/ziko/z-git/bux/bux/stdlib",
|
"/home/ziko/z-git/bux/bux/library",
|
||||||
]
|
]
|
||||||
for path in stdlibSearchPaths:
|
for path in stdlibSearchPaths:
|
||||||
if dirExists(path):
|
if dirExists(path):
|
||||||
@@ -518,19 +518,19 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
# Copy runtime.c
|
# Copy runtime.c
|
||||||
let runtimeSrc = stdlibDir / "runtime.c"
|
let runtimeSrc = stdlibDir / "runtime" / "runtime.c"
|
||||||
if fileExists(runtimeSrc):
|
if fileExists(runtimeSrc):
|
||||||
copyFile(runtimeSrc, runtimeDst)
|
copyFile(runtimeSrc, runtimeDst)
|
||||||
else:
|
else:
|
||||||
printError("runtime.c not found in stdlib", useColor)
|
printError("runtime.c not found in library/runtime/", useColor)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
# Copy io.c
|
# Copy io.c
|
||||||
let ioSrc = stdlibDir / "io.c"
|
let ioSrc = stdlibDir / "runtime" / "io.c"
|
||||||
if fileExists(ioSrc):
|
if fileExists(ioSrc):
|
||||||
copyFile(ioSrc, ioDst)
|
copyFile(ioSrc, ioDst)
|
||||||
else:
|
else:
|
||||||
printError("io.c not found in stdlib", useColor)
|
printError("io.c not found in library/runtime/", useColor)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
# Compile with cc
|
# Compile with cc
|
||||||
@@ -43,6 +43,7 @@ type
|
|||||||
# Borrow checker state
|
# Borrow checker state
|
||||||
checkedFunc*: bool ## true inside @[Checked] function
|
checkedFunc*: bool ## true inside @[Checked] function
|
||||||
currentFuncIsAsync*: bool ## true inside async func
|
currentFuncIsAsync*: bool ## true inside async func
|
||||||
|
movedVars*: seq[string] ## variables moved in current checked function
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Helpers
|
# Helpers
|
||||||
@@ -727,6 +728,9 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
of tkNull: return makePointer(makeUnknown())
|
of tkNull: return makePointer(makeUnknown())
|
||||||
else: return makeUnknown()
|
else: return makeUnknown()
|
||||||
of ekIdent:
|
of ekIdent:
|
||||||
|
if sema.checkedFunc and expr.exprIdent in sema.movedVars:
|
||||||
|
sema.emitError(expr.loc, &"use of moved value '{expr.exprIdent}'")
|
||||||
|
return makeUnknown()
|
||||||
let sym = scope.lookup(expr.exprIdent)
|
let sym = scope.lookup(expr.exprIdent)
|
||||||
if sym == nil:
|
if sym == nil:
|
||||||
sema.emitError(expr.loc, &"undeclared identifier '{expr.exprIdent}'")
|
sema.emitError(expr.loc, &"undeclared identifier '{expr.exprIdent}'")
|
||||||
@@ -931,16 +935,7 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
# Regular function call
|
# Regular function call
|
||||||
let calleeType = sema.checkExpr(expr.exprCallCallee, scope)
|
let calleeType = sema.checkExpr(expr.exprCallCallee, scope)
|
||||||
var argTypes = sema.checkExprList(expr.exprCallArgs, scope)
|
var argTypes = sema.checkExprList(expr.exprCallArgs, scope)
|
||||||
if calleeType.kind == tkFunc:
|
# Look up callee declaration early (needed for borrow checking)
|
||||||
let expectedParams = calleeType.inner[0..^2]
|
|
||||||
if argTypes.len != expectedParams.len:
|
|
||||||
sema.emitError(expr.loc, &"expected {expectedParams.len} arguments, got {argTypes.len}")
|
|
||||||
else:
|
|
||||||
for i in 0 ..< argTypes.len:
|
|
||||||
if not argTypes[i].isAssignableTo(expectedParams[i]) and not (argTypes[i].kind in {TypeKind.tkUnknown, TypeKind.tkNamed, TypeKind.tkTypeParam}):
|
|
||||||
sema.emitError(expr.loc, &"argument {i+1}: expected {expectedParams[i].toString}, got {argTypes[i].toString}")
|
|
||||||
|
|
||||||
# Check for inferred generic function call (no explicit type args)
|
|
||||||
var calleeDecl: Decl = nil
|
var calleeDecl: Decl = nil
|
||||||
case expr.exprCallCallee.kind
|
case expr.exprCallCallee.kind
|
||||||
of ekIdent:
|
of ekIdent:
|
||||||
@@ -951,6 +946,40 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
let sym = scope.lookup(fullName)
|
let sym = scope.lookup(fullName)
|
||||||
if sym != nil: calleeDecl = sym.decl
|
if sym != nil: calleeDecl = sym.decl
|
||||||
else: discard
|
else: discard
|
||||||
|
if calleeDecl != nil and calleeDecl.kind == dkFunc and calleeDecl.declFuncTypeParams.len > 0:
|
||||||
|
discard # will be handled later
|
||||||
|
if calleeType.kind == tkFunc:
|
||||||
|
let expectedParams = calleeType.inner[0..^2]
|
||||||
|
if argTypes.len != expectedParams.len:
|
||||||
|
sema.emitError(expr.loc, &"expected {expectedParams.len} arguments, got {argTypes.len}")
|
||||||
|
else:
|
||||||
|
for i in 0 ..< argTypes.len:
|
||||||
|
if not argTypes[i].isAssignableTo(expectedParams[i]) and not (argTypes[i].kind in {TypeKind.tkUnknown, TypeKind.tkNamed, TypeKind.tkTypeParam}):
|
||||||
|
sema.emitError(expr.loc, &"argument {i+1}: expected {expectedParams[i].toString}, got {argTypes[i].toString}")
|
||||||
|
|
||||||
|
# Borrow check: reject double mutable borrow (alias analysis)
|
||||||
|
if sema.checkedFunc:
|
||||||
|
var mutRefArgs: seq[tuple[idx: int, name: string]] = @[]
|
||||||
|
for i in 0 ..< argTypes.len:
|
||||||
|
if expectedParams[i].isMutRef and i < expr.exprCallArgs.len:
|
||||||
|
let arg = expr.exprCallArgs[i]
|
||||||
|
if arg.kind == ekUnary and arg.exprUnaryOp == tkAmp and arg.exprUnaryOperand.kind == ekIdent:
|
||||||
|
mutRefArgs.add((idx: i, name: arg.exprUnaryOperand.exprIdent))
|
||||||
|
for i in 0 ..< mutRefArgs.len:
|
||||||
|
for j in i+1 ..< mutRefArgs.len:
|
||||||
|
if mutRefArgs[i].name == mutRefArgs[j].name:
|
||||||
|
sema.emitError(expr.loc, &"mutable borrow conflict: arguments {mutRefArgs[i].idx+1} and {mutRefArgs[j].idx+1} both borrow '&mut {mutRefArgs[i].name}'")
|
||||||
|
|
||||||
|
# Borrow check: track moved variables (own T)
|
||||||
|
if calleeDecl != nil and calleeDecl.kind == dkFunc:
|
||||||
|
for i in 0 ..< argTypes.len:
|
||||||
|
if i < calleeDecl.declFuncParams.len and i < expr.exprCallArgs.len:
|
||||||
|
if calleeDecl.declFuncParams[i].ptype.kind == tekOwn:
|
||||||
|
let arg = expr.exprCallArgs[i]
|
||||||
|
if arg.kind == ekIdent:
|
||||||
|
sema.movedVars.add(arg.exprIdent)
|
||||||
|
|
||||||
|
# Check for inferred generic function call (no explicit type args)
|
||||||
|
|
||||||
if calleeDecl != nil and calleeDecl.kind == dkFunc and
|
if calleeDecl != nil and calleeDecl.kind == dkFunc and
|
||||||
calleeDecl.declFuncTypeParams.len > 0 and
|
calleeDecl.declFuncTypeParams.len > 0 and
|
||||||
@@ -1281,6 +1310,8 @@ proc checkFunc(sema: var Sema, decl: Decl) =
|
|||||||
let wasAsync = sema.currentFuncIsAsync
|
let wasAsync = sema.currentFuncIsAsync
|
||||||
sema.checkedFunc = "Checked" in decl.declAttrs
|
sema.checkedFunc = "Checked" in decl.declAttrs
|
||||||
sema.currentFuncIsAsync = decl.declFuncIsAsync
|
sema.currentFuncIsAsync = decl.declFuncIsAsync
|
||||||
|
if sema.checkedFunc:
|
||||||
|
sema.movedVars = @[]
|
||||||
var funcScope = newScope(sema.globalScope)
|
var funcScope = newScope(sema.globalScope)
|
||||||
# Add type parameters to type table for resolution
|
# Add type parameters to type table for resolution
|
||||||
var addedTypeParams: seq[string] = @[]
|
var addedTypeParams: seq[string] = @[]
|
||||||
@@ -31,7 +31,7 @@ func DirExists(path: String) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Import the compiler pipeline
|
// Import the compiler pipeline
|
||||||
// In self-hosting mode, these are compiled together from src_bux/
|
// In self-hosting mode, these are compiled together from compiler/selfhost/
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Compile a single .bux source file
|
// Compile a single .bux source file
|
||||||
@@ -140,19 +140,19 @@ func Cli_Build(srcPath: String, outPath: String) -> int {
|
|||||||
PrintLine(cFile);
|
PrintLine(cFile);
|
||||||
|
|
||||||
// Find runtime.c and io.c for linking
|
// Find runtime.c and io.c for linking
|
||||||
var rtPath: String = "stdlib/runtime.c";
|
var rtPath: String = "library/runtime/runtime.c";
|
||||||
var ioPath: String = "stdlib/io.c";
|
var ioPath: String = "library/runtime/io.c";
|
||||||
if !FileExists(rtPath) {
|
if !FileExists(rtPath) {
|
||||||
rtPath = "../stdlib/runtime.c";
|
rtPath = "../library/runtime/runtime.c";
|
||||||
}
|
}
|
||||||
if !FileExists(ioPath) {
|
if !FileExists(ioPath) {
|
||||||
ioPath = "../stdlib/io.c";
|
ioPath = "../library/runtime/io.c";
|
||||||
}
|
}
|
||||||
if !FileExists(rtPath) {
|
if !FileExists(rtPath) {
|
||||||
rtPath = "/home/ziko/z-git/bux/bux/stdlib/runtime.c";
|
rtPath = "/home/ziko/z-git/bux/bux/library/runtime/runtime.c";
|
||||||
}
|
}
|
||||||
if !FileExists(ioPath) {
|
if !FileExists(ioPath) {
|
||||||
ioPath = "/home/ziko/z-git/bux/bux/stdlib/io.c";
|
ioPath = "/home/ziko/z-git/bux/bux/library/runtime/io.c";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile with cc
|
// Compile with cc
|
||||||
@@ -633,16 +633,16 @@ func Cli_BuildProject(projectDir: String) -> int {
|
|||||||
var rtPath: String = bux_path_join(stdlibDir, "runtime.c");
|
var rtPath: String = bux_path_join(stdlibDir, "runtime.c");
|
||||||
var ioPath: String = bux_path_join(stdlibDir, "io.c");
|
var ioPath: String = bux_path_join(stdlibDir, "io.c");
|
||||||
if !FileExists(rtPath) {
|
if !FileExists(rtPath) {
|
||||||
rtPath = bux_path_join(projectDir, "stdlib/runtime.c");
|
rtPath = bux_path_join(projectDir, "library/runtime/runtime.c");
|
||||||
}
|
}
|
||||||
if !FileExists(ioPath) {
|
if !FileExists(ioPath) {
|
||||||
ioPath = bux_path_join(projectDir, "stdlib/io.c");
|
ioPath = bux_path_join(projectDir, "library/runtime/io.c");
|
||||||
}
|
}
|
||||||
if !FileExists(rtPath) {
|
if !FileExists(rtPath) {
|
||||||
rtPath = bux_path_join(projectDir, "../stdlib/runtime.c");
|
rtPath = bux_path_join(projectDir, "../library/runtime/runtime.c");
|
||||||
}
|
}
|
||||||
if !FileExists(ioPath) {
|
if !FileExists(ioPath) {
|
||||||
ioPath = bux_path_join(projectDir, "../stdlib/io.c");
|
ioPath = bux_path_join(projectDir, "../library/runtime/io.c");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile with cc
|
// Compile with cc
|
||||||
@@ -9,7 +9,7 @@ const skConst: int = 3;
|
|||||||
const skModule: int = 4;
|
const skModule: int = 4;
|
||||||
|
|
||||||
// Maximum symbols per scope
|
// Maximum symbols per scope
|
||||||
const maxSymbols: int = 1024;
|
const maxSymbols: int = 8192;
|
||||||
|
|
||||||
struct Symbol {
|
struct Symbol {
|
||||||
kind: int;
|
kind: int;
|
||||||
@@ -438,6 +438,7 @@ func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) {
|
|||||||
|
|
||||||
func Sema_CollectGlobals(sema: *Sema) {
|
func Sema_CollectGlobals(sema: *Sema) {
|
||||||
var decl: *Decl = sema.module.firstItem;
|
var decl: *Decl = sema.module.firstItem;
|
||||||
|
var funcCount: int = 0;
|
||||||
while decl != null as *Decl {
|
while decl != null as *Decl {
|
||||||
let dk: int = decl.kind;
|
let dk: int = decl.kind;
|
||||||
|
|
||||||
@@ -503,6 +504,24 @@ func Sema_CollectGlobals(sema: *Sema) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Const
|
||||||
|
if dk == dkConst {
|
||||||
|
var sym: Symbol;
|
||||||
|
Sema_ZeroInitSymbol(&sym);
|
||||||
|
sym.kind = skConst;
|
||||||
|
sym.name = decl.strValue;
|
||||||
|
if decl.constType != null as *TypeExpr {
|
||||||
|
sym.typeKind = Sema_ResolveType(sema, decl.constType);
|
||||||
|
sym.typeName = decl.constType.typeName;
|
||||||
|
} else {
|
||||||
|
sym.typeKind = tyInt;
|
||||||
|
}
|
||||||
|
sym.isMutable = false;
|
||||||
|
sym.isPublic = decl.isPublic;
|
||||||
|
sym.decl = decl;
|
||||||
|
discard Scope_Define(sema.scope, sym);
|
||||||
|
}
|
||||||
|
|
||||||
// Use (import)
|
// Use (import)
|
||||||
if dk == dkUse {
|
if dk == dkUse {
|
||||||
if decl.useKind == 2 {
|
if decl.useKind == 2 {
|
||||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,117 @@
|
|||||||
|
import std/[unittest, strutils]
|
||||||
|
import ../bootstrap/[lexer, parser, sema, types]
|
||||||
|
|
||||||
|
proc checkSource(source: string): SemaResult =
|
||||||
|
let lexRes = tokenize(source, "<test>")
|
||||||
|
check not lexRes.hasErrors
|
||||||
|
let parseRes = parse(lexRes.tokens, "<test>")
|
||||||
|
check parseRes.diagnostics.len == 0
|
||||||
|
result = analyze(parseRes.module)
|
||||||
|
|
||||||
|
suite "Borrow Checker":
|
||||||
|
|
||||||
|
test "@[Checked] function with &mut allows mutation":
|
||||||
|
let res = checkSource("""
|
||||||
|
func Mutate(val: &mut int) {
|
||||||
|
*val = 42;
|
||||||
|
}
|
||||||
|
func Main() -> int {
|
||||||
|
var x: int = 10;
|
||||||
|
Mutate(&x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
check(not res.hasErrors)
|
||||||
|
|
||||||
|
test "@[Checked] function rejects write through &T":
|
||||||
|
let res = checkSource("""
|
||||||
|
@[Checked]
|
||||||
|
func BadWrite(val: &int) {
|
||||||
|
*val = 42;
|
||||||
|
}
|
||||||
|
func Main() -> int {
|
||||||
|
var x: int = 10;
|
||||||
|
BadWrite(&x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
check(res.hasErrors)
|
||||||
|
check(res.diagnostics[0].message.contains("cannot assign through shared reference"))
|
||||||
|
|
||||||
|
test "unchecked function allows write through raw pointer":
|
||||||
|
let res = checkSource("""
|
||||||
|
func RawWrite(val: *int) {
|
||||||
|
*val = 42;
|
||||||
|
}
|
||||||
|
func Main() -> int {
|
||||||
|
var x: int = 10;
|
||||||
|
RawWrite(&x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
check(not res.hasErrors)
|
||||||
|
|
||||||
|
test "&T allows reading":
|
||||||
|
let res = checkSource("""
|
||||||
|
@[Checked]
|
||||||
|
func Read(val: &int) -> int {
|
||||||
|
return *val;
|
||||||
|
}
|
||||||
|
func Main() -> int {
|
||||||
|
var x: int = 10;
|
||||||
|
return Read(&x);
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
check(not res.hasErrors)
|
||||||
|
|
||||||
|
test "own T type parses and resolves":
|
||||||
|
let res = checkSource("""
|
||||||
|
struct Box {
|
||||||
|
value: int;
|
||||||
|
}
|
||||||
|
func TakeOwn(b: own Box) -> int {
|
||||||
|
return b.value;
|
||||||
|
}
|
||||||
|
func Main() -> int {
|
||||||
|
var b: Box = Box { value: 42 };
|
||||||
|
return TakeOwn(b);
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
check(not res.hasErrors)
|
||||||
|
|
||||||
|
test "@[Checked] rejects double mutable borrow in call":
|
||||||
|
let res = checkSource("""
|
||||||
|
@[Checked]
|
||||||
|
func Swap(a: &mut int, b: &mut int) {
|
||||||
|
let tmp = *a;
|
||||||
|
*a = *b;
|
||||||
|
*b = tmp;
|
||||||
|
}
|
||||||
|
@[Checked]
|
||||||
|
func Main() -> int {
|
||||||
|
var x: int = 10;
|
||||||
|
Swap(&x, &x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
check(res.hasErrors)
|
||||||
|
check(res.diagnostics[0].message.contains("mutable borrow"))
|
||||||
|
|
||||||
|
test "@[Checked] rejects use after move":
|
||||||
|
let res = checkSource("""
|
||||||
|
struct Box {
|
||||||
|
value: int;
|
||||||
|
}
|
||||||
|
@[Checked]
|
||||||
|
func Consume(b: own Box) -> int {
|
||||||
|
return b.value;
|
||||||
|
}
|
||||||
|
@[Checked]
|
||||||
|
func Main() -> int {
|
||||||
|
var b: Box = Box { value: 42 };
|
||||||
|
let x: int = Consume(b);
|
||||||
|
return b.value;
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
check(res.hasErrors)
|
||||||
|
check(res.diagnostics[0].message.contains("moved"))
|
||||||
Executable
BIN
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
import std/[unittest, strformat]
|
import std/[unittest, strformat]
|
||||||
import ../src/[lexer, parser, sema, hir, hir_lower, types, scope]
|
import ../bootstrap/[lexer, parser, sema, hir, hir_lower, types, scope]
|
||||||
|
|
||||||
proc lowerSource(source: string): HirModule =
|
proc lowerSource(source: string): HirModule =
|
||||||
let lexRes = tokenize(source, "<test>")
|
let lexRes = tokenize(source, "<test>")
|
||||||
Executable
BIN
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
import std/[unittest, os, strutils]
|
import std/[unittest, os, strutils]
|
||||||
import ../src/[lexer, token, source_location]
|
import ../bootstrap/[lexer, token, source_location]
|
||||||
|
|
||||||
proc tokenKinds(source: string): seq[TokenKind] =
|
proc tokenKinds(source: string): seq[TokenKind] =
|
||||||
let res = tokenize(source, "<test>")
|
let res = tokenize(source, "<test>")
|
||||||
Executable
BIN
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
import std/[unittest, os, strutils]
|
import std/[unittest, os, strutils]
|
||||||
import ../src/[lexer, parser, ast, token]
|
import ../bootstrap/[lexer, parser, ast, token]
|
||||||
|
|
||||||
proc parseSource(source: string): ParseResult =
|
proc parseSource(source: string): ParseResult =
|
||||||
let lexRes = tokenize(source, "<test>")
|
let lexRes = tokenize(source, "<test>")
|
||||||
Executable
BIN
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
import std/[unittest, strutils]
|
import std/[unittest, strutils]
|
||||||
import ../src/[lexer, parser, sema, types]
|
import ../bootstrap/[lexer, parser, sema, types]
|
||||||
|
|
||||||
proc checkSource(source: string): SemaResult =
|
proc checkSource(source: string): SemaResult =
|
||||||
let lexRes = tokenize(source, "<test>")
|
let lexRes = tokenize(source, "<test>")
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import "../src/lexer", "../src/parser", "../src/ast"
|
import "../bootstrap/lexer", "../bootstrap/parser", "../bootstrap/ast"
|
||||||
|
|
||||||
let source = readFile("../_selfhost/src/ast.bux")
|
let source = readFile("../_selfhost/src/ast.bux")
|
||||||
let lexRes = tokenize(source, "ast.bux")
|
let lexRes = tokenize(source, "ast.bux")
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import "../src/lexer", "../src/parser", "../src/ast", "../src/token"
|
import "../bootstrap/lexer", "../bootstrap/parser", "../bootstrap/ast", "../bootstrap/token"
|
||||||
import std/os
|
import std/os
|
||||||
|
|
||||||
let source = readFile("../_selfhost/src/ast.bux")
|
let source = readFile("../_selfhost/src/ast.bux")
|
||||||
@@ -34,7 +34,7 @@ make dev
|
|||||||
|
|
||||||
The output is a single binary: `buxc` (bootstrap compiler in Nim).
|
The output is a single binary: `buxc` (bootstrap compiler in Nim).
|
||||||
|
|
||||||
The self-hosted compiler `buxc2` is built from `src_bux/*.bux` sources via:
|
The self-hosted compiler `buxc2` is built from `compiler/selfhost/*.bux` sources via:
|
||||||
```bash
|
```bash
|
||||||
make selfhost
|
make selfhost
|
||||||
```
|
```
|
||||||
@@ -140,7 +140,7 @@ bux/
|
|||||||
│ ├── hir_lower.nim # AST → HIR lowering
|
│ ├── hir_lower.nim # AST → HIR lowering
|
||||||
│ ├── c_backend.nim # HIR → C code generation
|
│ ├── c_backend.nim # HIR → C code generation
|
||||||
│ └── ...
|
│ └── ...
|
||||||
├── stdlib/ # Standard library
|
├── library/ # Standard library
|
||||||
│ ├── Std/
|
│ ├── Std/
|
||||||
│ │ ├── Io.bux
|
│ │ ├── Io.bux
|
||||||
│ │ ├── Array.bux
|
│ │ ├── Array.bux
|
||||||
@@ -186,7 +186,7 @@ cat build/main.c
|
|||||||
|
|
||||||
| Error | Cause | Fix |
|
| Error | Cause | Fix |
|
||||||
|-------|-------|-----|
|
|-------|-------|-----|
|
||||||
| `stdlib directory not found` | `buxc` can't find `stdlib/` | Run from project root or set correct path |
|
| `stdlib directory not found` | `buxc` can't find `library/` | Run from project root or set correct path |
|
||||||
| `duplicate symbol 'bux_alloc'` | Multiple stdlib modules declare same extern | Only declare in one module |
|
| `duplicate symbol 'bux_alloc'` | Multiple stdlib modules declare same extern | Only declare in one module |
|
||||||
| `C compilation failed` | Generated C has errors | Check `build/main.c` for issues |
|
| `C compilation failed` | Generated C has errors | Check `build/main.c` for issues |
|
||||||
|
|
||||||
|
|||||||
+53
-2
@@ -454,8 +454,59 @@ func Main() -> int {
|
|||||||
- `Std::Mem` — Memory wrappers ✅
|
- `Std::Mem` — Memory wrappers ✅
|
||||||
- `Std::Set` — Hash set ✅
|
- `Std::Set` — Hash set ✅
|
||||||
- `Std::Path` — Path manipulation ✅
|
- `Std::Path` — Path manipulation ✅
|
||||||
- `Std::Os` — `Args`, `Env`, `Exit`, `Cwd` ⏳
|
- `Std::Os` — `Args`, `Env`, `Cwd`, `Chdir` ✅
|
||||||
- `Std::Process` — Spawn subprocess ⏳
|
- `Std::Time` — `NowMs`, `NowUs`, `SleepMs` ✅
|
||||||
|
- `Std::Process` — `Run`, `Output` ✅
|
||||||
- `Std::Fmt` — String formatting with interpolation ⏳
|
- `Std::Fmt` — String formatting with interpolation ⏳
|
||||||
- `Std::Iter` — Iterator trait and combinators ⏳
|
- `Std::Iter` — Iterator trait and combinators ⏳
|
||||||
- `Std::Task` / `Std::Channel` — Lightweight concurrency (pthread-based threads) ✅
|
- `Std::Task` / `Std::Channel` — Lightweight concurrency (pthread-based threads) ✅
|
||||||
|
---
|
||||||
|
|
||||||
|
## Std::Os
|
||||||
|
|
||||||
|
Operating system interface.
|
||||||
|
|
||||||
|
```bux
|
||||||
|
import Std::Os::{Os_ArgsCount, Os_Args, Os_GetEnv, Os_SetEnv, Os_GetCwd, Os_Chdir};
|
||||||
|
```
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|----------|-----------|-------------|
|
||||||
|
| `Os_ArgsCount` | `func Os_ArgsCount() -> int` | Number of command-line arguments |
|
||||||
|
| `Os_Args` | `func Os_Args(index: int) -> String` | Get argument at index |
|
||||||
|
| `Os_GetEnv` | `func Os_GetEnv(name: String) -> String` | Get environment variable |
|
||||||
|
| `Os_SetEnv` | `func Os_SetEnv(name: String, value: String) -> bool` | Set environment variable |
|
||||||
|
| `Os_GetCwd` | `func Os_GetCwd() -> String` | Get current working directory |
|
||||||
|
| `Os_Chdir` | `func Os_Chdir(path: String) -> bool` | Change directory |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Std::Time
|
||||||
|
|
||||||
|
Time utilities.
|
||||||
|
|
||||||
|
```bux
|
||||||
|
import Std::Time::{Time_NowMs, Time_NowUs, Time_SleepMs};
|
||||||
|
```
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|----------|-----------|-------------|
|
||||||
|
| `Time_NowMs` | `func Time_NowMs() -> int64` | Current time in milliseconds |
|
||||||
|
| `Time_NowUs` | `func Time_NowUs() -> int64` | Current time in microseconds |
|
||||||
|
| `Time_SleepMs` | `func Time_SleepMs(ms: int64)` | Sleep for N milliseconds |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Std::Process
|
||||||
|
|
||||||
|
Process spawning.
|
||||||
|
|
||||||
|
```bux
|
||||||
|
import Std::Process::{Process_Run, Process_Output};
|
||||||
|
```
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|----------|-----------|-------------|
|
||||||
|
| `Process_Run` | `func Process_Run(cmd: String) -> int` | Run command, return exit code |
|
||||||
|
| `Process_Output` | `func Process_Output(cmd: String) -> String` | Run command, capture stdout |
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Os & Time - System and time utilities
|
||||||
|
import Std::Io::{PrintLine, PrintInt, PrintInt64};
|
||||||
|
import Std::Os::{Os_ArgsCount, Os_Args, Os_GetEnv, Os_GetCwd};
|
||||||
|
import Std::Time::{Time_NowMs, Time_SleepMs};
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
PrintLine("=== OS Demo ===");
|
||||||
|
Print("Current directory: ");
|
||||||
|
PrintLine(Os_GetCwd());
|
||||||
|
|
||||||
|
Print("Command-line args: ");
|
||||||
|
PrintInt(Os_ArgsCount());
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
Print("HOME env: ");
|
||||||
|
PrintLine(Os_GetEnv("HOME"));
|
||||||
|
|
||||||
|
PrintLine("\n=== Time Demo ===");
|
||||||
|
let start: int64 = Time_NowMs();
|
||||||
|
Print("Start: ");
|
||||||
|
PrintInt64(start);
|
||||||
|
PrintLine(" ms");
|
||||||
|
|
||||||
|
PrintLine("Sleeping 150ms...");
|
||||||
|
Time_SleepMs(150);
|
||||||
|
|
||||||
|
let end: int64 = Time_NowMs();
|
||||||
|
Print("End: ");
|
||||||
|
PrintInt64(end);
|
||||||
|
PrintLine(" ms");
|
||||||
|
|
||||||
|
Print("Elapsed: ");
|
||||||
|
PrintInt64(end - start);
|
||||||
|
PrintLine(" ms");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// Process - Run external commands and capture output
|
||||||
|
import Std::Io::{PrintLine, PrintInt};
|
||||||
|
import Std::Process::{Process_Run, Process_Output};
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
PrintLine("Process demo:");
|
||||||
|
|
||||||
|
let output: String = Process_Output("echo Hello from subprocess");
|
||||||
|
Print("Captured: ");
|
||||||
|
PrintLine(output);
|
||||||
|
|
||||||
|
let rc: int = Process_Run("true");
|
||||||
|
Print("Exit code: ");
|
||||||
|
PrintInt(rc);
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -25,6 +25,11 @@ void PrintInt(int n) {
|
|||||||
printf("%d", n);
|
printf("%d", n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PrintInt64 - print 64-bit integer */
|
||||||
|
void PrintInt64(int64_t n) {
|
||||||
|
printf("%lld", (long long)n);
|
||||||
|
}
|
||||||
|
|
||||||
/* PrintFloat - print float */
|
/* PrintFloat - print float */
|
||||||
void PrintFloat(double f) {
|
void PrintFloat(double f) {
|
||||||
printf("%g", f);
|
printf("%g", f);
|
||||||
@@ -1061,3 +1061,97 @@ void* bux_async_result(void* handle) {
|
|||||||
bux_async_task_t* task = (bux_async_task_t*)handle;
|
bux_async_task_t* task = (bux_async_task_t*)handle;
|
||||||
return task->result;
|
return task->result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* OS primitives
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
const char* bux_getenv(const char* name) {
|
||||||
|
if (!name) return "";
|
||||||
|
const char* val = getenv(name);
|
||||||
|
return val ? val : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int bux_setenv(const char* name, const char* value) {
|
||||||
|
if (!name || !value) return -1;
|
||||||
|
return setenv(name, value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* bux_getcwd(void) {
|
||||||
|
static char buf[4096];
|
||||||
|
if (getcwd(buf, sizeof(buf))) {
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
int bux_chdir(const char* path) {
|
||||||
|
if (!path) return -1;
|
||||||
|
return chdir(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Time primitives
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
int64_t bux_time_ms(void) {
|
||||||
|
struct timespec ts;
|
||||||
|
if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
|
||||||
|
return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t bux_time_us(void) {
|
||||||
|
struct timespec ts;
|
||||||
|
if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
|
||||||
|
return (int64_t)ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bux_sleep_ms(int64_t ms) {
|
||||||
|
if (ms <= 0) return;
|
||||||
|
struct timespec ts;
|
||||||
|
ts.tv_sec = ms / 1000;
|
||||||
|
ts.tv_nsec = (ms % 1000) * 1000000;
|
||||||
|
nanosleep(&ts, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
* Process primitives
|
||||||
|
* ============================================================================ */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int bux_process_run(const char* cmd) {
|
||||||
|
if (!cmd) return -1;
|
||||||
|
return system(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* bux_process_output(const char* cmd) {
|
||||||
|
if (!cmd) return NULL;
|
||||||
|
FILE* pipe = popen(cmd, "r");
|
||||||
|
if (!pipe) return NULL;
|
||||||
|
|
||||||
|
size_t cap = 1024;
|
||||||
|
size_t len = 0;
|
||||||
|
char* buf = (char*)malloc(cap);
|
||||||
|
if (!buf) { pclose(pipe); return NULL; }
|
||||||
|
|
||||||
|
int c;
|
||||||
|
while ((c = fgetc(pipe)) != EOF) {
|
||||||
|
if (len + 1 >= cap) {
|
||||||
|
cap *= 2;
|
||||||
|
char* new_buf = (char*)realloc(buf, cap);
|
||||||
|
if (!new_buf) { free(buf); pclose(pipe); return NULL; }
|
||||||
|
buf = new_buf;
|
||||||
|
}
|
||||||
|
buf[len++] = (char)c;
|
||||||
|
}
|
||||||
|
buf[len] = '\0';
|
||||||
|
pclose(pipe);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ module Std::Io {
|
|||||||
extern func PrintLine(s: String);
|
extern func PrintLine(s: String);
|
||||||
extern func Print(s: String);
|
extern func Print(s: String);
|
||||||
extern func PrintInt(n: int);
|
extern func PrintInt(n: int);
|
||||||
|
extern func PrintInt64(n: int64);
|
||||||
extern func PrintFloat(f: float64);
|
extern func PrintFloat(f: float64);
|
||||||
extern func PrintBool(b: bool);
|
extern func PrintBool(b: bool);
|
||||||
extern func ReadLine() -> String;
|
extern func ReadLine() -> String;
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
func Os_ArgsCount() -> int {
|
||||||
|
return bux_argc();
|
||||||
|
}
|
||||||
|
|
||||||
|
func Os_Args(index: int) -> String {
|
||||||
|
return bux_argv(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_GetCwd() -> String {
|
||||||
|
return bux_getcwd();
|
||||||
|
}
|
||||||
|
|
||||||
|
func Os_Chdir(path: String) -> bool {
|
||||||
|
return bux_chdir(path) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
module Std::Process {
|
||||||
|
|
||||||
|
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_Output(cmd: String) -> String {
|
||||||
|
return bux_process_output(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
module Std::Time {
|
||||||
|
|
||||||
|
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_NowUs() -> int64 {
|
||||||
|
return bux_time_us();
|
||||||
|
}
|
||||||
|
|
||||||
|
func Time_SleepMs(ms: int64) {
|
||||||
|
bux_sleep_ms(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
BIN
Binary file not shown.
Reference in New Issue
Block a user