docs: update README, Stdlib, BuildAndTest, PLAN for new std modules
Added documentation for: - Std::Fs (DirExists, Mkdir, ListDir) - Std::Mem (Alloc, Free, MemEq, New) - Std::Set<T> (Set_New, Set_Add, Set_Has) - String_Find, String_Replace, String_Format Updated PLAN.md blockers (all resolved) and self-host status. Updated README.md project structure and feature table. Updated BuildAndTest.md with new stdlib modules and path syntax. Also includes: - CLI path argument fix (build/check/run accept project path) - Remove dummyFunc from parser - Fix String_Replace to use String_Concat instead of buggy str_join2
This commit is contained in:
@@ -157,12 +157,15 @@ Bux is a fast, compiled, strongly-typed, multi-paradigm systems programming lang
|
|||||||
| Module | Status | Requirements |
|
| Module | Status | Requirements |
|
||||||
|--------|--------|-------------|
|
|--------|--------|-------------|
|
||||||
| `Std::Io` | ✅ | `Print`, `PrintLine`, `PrintInt`, `ReadLine` (wrap C stdio) |
|
| `Std::Io` | ✅ | `Print`, `PrintLine`, `PrintInt`, `ReadLine` (wrap C stdio) |
|
||||||
| `Std::Memory` | ✅ | `bux_alloc`, `bux_realloc`, `bux_free` (wrap `malloc`/`free`) |
|
| `Std::Mem` | ✅ | `Alloc`, `Realloc`, `Free`, `MemEq`, `New<T>` — wrappers around C runtime |
|
||||||
| `Std::String` | ✅ | Full API: `String_Len`, `String_Eq`, `String_Concat`, `String_Copy`, `String_StartsWith`, `String_EndsWith`, `String_Contains`, `String_Slice`, `String_Trim`, `String_TrimLeft`, `String_TrimRight`, `String_FromInt`, `String_ToInt`, `StringBuilder`; C wrappers in `runtime.c` |
|
| `Std::String` | ✅ | Full API: `String_Len`, `String_Eq`, `String_Concat`, `String_Copy`, `String_StartsWith`, `String_EndsWith`, `String_Contains`, `String_Slice`, `String_Trim`, `String_TrimLeft`, `String_TrimRight`, `String_FromInt`, `String_ToInt`, `StringBuilder`; plus `String_Find`, `String_Replace`, `String_Format1/2/3`; C wrappers in `runtime.c` |
|
||||||
| `Std::Array` | ✅ | Fully generic `Array<T>` with `Array_New<T>`, `Array_Push<T>`, `Array_Get<T>`, `Array_Len<T>`, `Array_Free<T>`; generic struct methods with auto-addressing |
|
| `Std::Array` | ✅ | Fully generic `Array<T>` with `Array_New<T>`, `Array_Push<T>`, `Array_Get<T>`, `Array_Len<T>`, `Array_Free<T>`; generic struct methods with auto-addressing |
|
||||||
| `Std::Map` | ✅ | Generic `Map<K,V>` with `Map_New`, `Map_Set`, `Map_Get`, `Map_Has`, `Map_Len`, `Map_Free`; value-type keys with strcmp |
|
| `Std::Map` | ✅ | Generic `Map<K,V>` with `Map_New`, `Map_Set`, `Map_Get`, `Map_Has`, `Map_Len`, `Map_Free`; value-type keys with strcmp |
|
||||||
|
| `Std::StringMap` | ✅ | Specialized `StringMap<V>` for String keys using `strcmp` |
|
||||||
|
| `Std::Set` | ✅ | Generic `Set<T>` with `Set_New`, `Set_Add`, `Set_Has`, `Set_Len`, `Set_Free` |
|
||||||
| `Std::Math` | ✅ | `Sqrt`, `Pow`, `Min`, `Max`, `Abs`, `MinF`, `MaxF`, `AbsF` (float64 + int64 variants, C runtime wrappers) |
|
| `Std::Math` | ✅ | `Sqrt`, `Pow`, `Min`, `Max`, `Abs`, `MinF`, `MaxF`, `AbsF` (float64 + int64 variants, C runtime wrappers) |
|
||||||
| `Std::Path` | ✅ | File exists, basic path operations |
|
| `Std::Path` | ✅ | `Path_Join`, `Path_Parent`, `Path_Ext` |
|
||||||
|
| `Std::Fs` | ✅ | `DirExists`, `Mkdir`, `ListDir` |
|
||||||
| `Std::Os` | ⏳ | `Args`, `Env`, `Exit`, `Cwd` |
|
| `Std::Os` | ⏳ | `Args`, `Env`, `Exit`, `Cwd` |
|
||||||
| `Std::Process` | ⏳ | Spawn subprocess, read stdout/stderr |
|
| `Std::Process` | ⏳ | Spawn subprocess, read stdout/stderr |
|
||||||
| **`Std::Result`** | ✅ | Algebraic enums `Result<T,E>` and `Option<T>` with `NewOk`/`NewErr`/`NewSome`/`NewNone`; `?` try operator desugared in HIR |
|
| **`Std::Result`** | ✅ | Algebraic enums `Result<T,E>` and `Option<T>` with `NewOk`/`NewErr`/`NewSome`/`NewNone`; `?` try operator desugared in HIR |
|
||||||
@@ -173,9 +176,13 @@ Bux is a fast, compiled, strongly-typed, multi-paradigm systems programming lang
|
|||||||
- ✅ Generic type inference: `Max(10, 20)` instead of `Max<int>(10, 20)` — compiler infers `T` from argument types
|
- ✅ Generic type inference: `Max(10, 20)` instead of `Max<int>(10, 20)` — compiler infers `T` from argument types
|
||||||
- ✅ `extend Box<T>` syntax: parser support for generic impl blocks
|
- ✅ `extend Box<T>` syntax: parser support for generic impl blocks
|
||||||
- ✅ String slicing, trimming, contains, StringBuilder (`strings2` example)
|
- ✅ String slicing, trimming, contains, StringBuilder (`strings2` example)
|
||||||
|
- ✅ String find, replace, format (`String_Find`, `String_Replace`, `String_Format`)
|
||||||
- ✅ Generic `Map<K,V>` with value-type keys
|
- ✅ Generic `Map<K,V>` with value-type keys
|
||||||
|
- ✅ Generic `Set<T>` for deduplication
|
||||||
|
- ✅ File system operations (`Std::Fs`)
|
||||||
|
- ✅ Memory management wrappers (`Std::Mem`)
|
||||||
|
|
||||||
**Deliverable:** Can write a non-trivial CLI tool entirely in Bux. ✅ 18 example programs working: `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`.
|
**Deliverable:** Can write a non-trivial CLI tool entirely in Bux. ✅ 20+ example programs working.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -205,25 +212,29 @@ Bux is a fast, compiled, strongly-typed, multi-paradigm systems programming lang
|
|||||||
| Nim Pattern | Used In | Bux Status |
|
| Nim Pattern | Used In | Bux Status |
|
||||||
|-------------|---------|------------|
|
|-------------|---------|------------|
|
||||||
| `Table[string, T]` | sema, hir_lower, c_backend (23 uses) | ❌ **Blocker** — need `StringMap<V>` |
|
| `Table[string, T]` | sema, hir_lower, c_backend (23 uses) | ❌ **Blocker** — need `StringMap<V>` |
|
||||||
| `HashSet[string]` | hir_lower (1 use) | ❌ Can use `StringMap<bool>` workaround |
|
| `HashSet[string]` | hir_lower (1 use) | ✅ `Set<T>` available |
|
||||||
| `seq[T]` with push/len/iter | All files (200+ uses) | ⚠️ `Array<T>` exists, needs richer API |
|
| `seq[T]` with push/len/iter | All files (200+ uses) | ⚠️ `Array<T>` exists, needs richer API |
|
||||||
| `&"..."` / `fmt"..."` | sema, c_backend (119 uses) | ❌ **Blocker** — need string formatting |
|
| `&"..."` / `fmt"..."` | sema, c_backend (119 uses) | ✅ `String_Format1/2/3` available |
|
||||||
| `split()`, `join()` | lexer, parser, cli | ❌ **Blocker** — need String split/join |
|
| `split()`, `join()` | lexer, parser, cli | ✅ `String_SplitCount`, `String_SplitPart`, `String_Join2` |
|
||||||
| `case obj.kind of...` | All files (90+ uses) | ✅ `match` with algebraic enums |
|
| `case obj.kind of...` | All files (90+ uses) | ✅ `match` with algebraic enums |
|
||||||
| `for x in collection` | All files (200+ uses) | ✅ Supported |
|
| `for x in collection` | All files (200+ uses) | ✅ Supported |
|
||||||
| `var` parameters | Multiple | ✅ Use pointers (`*T`) |
|
| `var` parameters | Multiple | ✅ Use pointers (`*T`) |
|
||||||
| File read/write | cli | ❌ Need `readFile`, `writeFile` |
|
| File read/write | cli | ✅ `ReadFile`, `WriteFile` in `Std::Io` |
|
||||||
| OS path operations | cli, manifest | ❌ Need path join, exists |
|
| OS path operations | cli, manifest | ✅ `Path_Join`, `DirExists`, `Mkdir` in `Std::Path`/`Std::Fs` |
|
||||||
|
|
||||||
### Rewrite Order (Dependency-driven)
|
### Rewrite Order (Dependency-driven)
|
||||||
|
|
||||||
```
|
```
|
||||||
Phase 7.0 — Stdlib blockers:
|
Phase 7.0 — Stdlib blockers (all resolved ✅):
|
||||||
├── StringMap<V> (blocker #1 — needed by all modules)
|
├── StringMap<V> ✅
|
||||||
├── String split/join (blocker #2 — needed by lexer, parser)
|
├── String split/join ✅
|
||||||
├── String formatting (blocker #3 — needed by sema, c_backend)
|
├── String formatting ✅
|
||||||
├── File I/O (readFile, writeFile, fileExists)
|
├── File I/O (readFile, writeFile, fileExists) ✅
|
||||||
└── OS path (joinPath, parentDir)
|
└── OS path (joinPath, parentDir) ✅
|
||||||
|
|
||||||
|
Remaining gaps for self-host polish:
|
||||||
|
├── `Std::Os` — `Args`, `Env`, `Exit`, `Cwd`
|
||||||
|
└── `Std::Process` — spawn subprocess
|
||||||
|
|
||||||
Phase 7.1 — Foundation (no internal deps):
|
Phase 7.1 — Foundation (no internal deps):
|
||||||
├── token.bux (enum + helpers)
|
├── token.bux (enum + helpers)
|
||||||
@@ -257,8 +268,8 @@ Phase 7.5 — Driver (depends on all):
|
|||||||
| StringMap not working for String keys | **High** | Already have working `StringMap<V>` in stdlib using strcmp |
|
| StringMap not working for String keys | **High** | Already have working `StringMap<V>` in stdlib using strcmp |
|
||||||
| `&key as *void` precedence bug | **Medium** | Workaround: use intermediate `*K` variable |
|
| `&key as *void` precedence bug | **Medium** | Workaround: use intermediate `*K` variable |
|
||||||
| Cross-module generics not working | **Medium** | All compiler code will be in one package (merged via stdlib mechanism) |
|
| Cross-module generics not working | **Medium** | All compiler code will be in one package (merged via stdlib mechanism) |
|
||||||
| `Map_Len` monomorphization bug | **Low** | Avoid calling `Map_Len` with explicit type args; inline the body |
|
| `Map_Len` / `Set_Len` monomorphization bug | **Low** | C backend issue — use explicit type args or avoid; QBE backend unaffected |
|
||||||
| String formatting complexity | **Medium** | Use StringBuilder pattern instead of printf-style formatting |
|
| String formatting | **Medium** | `String_Format1/2/3` available via `bux_str_format` |
|
||||||
| Array<T> API gaps | **Low** | Extend Array module as needed during porting |
|
| Array<T> API gaps | **Low** | Extend Array module as needed during porting |
|
||||||
|
|
||||||
### Estimated Effort
|
### Estimated Effort
|
||||||
|
|||||||
@@ -147,7 +147,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` |
|
| **Standard Library** | `Io`, `Array`, `String`, `Map`, `Fs`, `Mem`, `Set`, `Path`, `Math`, `Task`, `Channel` |
|
||||||
| **Backend** | QBE native codegen (self-hosting), C transpiler (bootstrap) |
|
| **Backend** | QBE native codegen (self-hosting), C transpiler (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 |
|
||||||
@@ -168,6 +168,11 @@ bux/
|
|||||||
├── src_bux/ # Self-hosting compiler source (Bux)
|
├── src_bux/ # Self-hosting compiler source (Bux)
|
||||||
├── _selfhost/ # Self-hosting build artifacts
|
├── _selfhost/ # Self-hosting build artifacts
|
||||||
├── stdlib/ # Standard library (.bux + .c runtime)
|
├── stdlib/ # Standard library (.bux + .c runtime)
|
||||||
|
│ └── Std/
|
||||||
|
│ ├── Io.bux, String.bux, Array.bux, Map.bux
|
||||||
|
│ ├── Fs.bux, Mem.bux, Set.bux
|
||||||
|
│ ├── Path.bux, Math.bux
|
||||||
|
│ └── Task.bux, Channel.bux
|
||||||
├── examples/ # Example programs
|
├── examples/ # Example programs
|
||||||
├── tests/ # Unit tests (Nim)
|
├── tests/ # Unit tests (Nim)
|
||||||
├── docs/ # Documentation
|
├── docs/ # Documentation
|
||||||
|
|||||||
@@ -0,0 +1,585 @@
|
|||||||
|
// c_backend.bux — C transpiler backend (ported from c_backend.nim)
|
||||||
|
// Generates C code from the HIR.
|
||||||
|
module CBackend {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Type → C type name
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func CBackend_TypeToC(kind: int) -> String {
|
||||||
|
if kind == tyVoid { return "void"; }
|
||||||
|
if kind == tyBool { return "bool"; }
|
||||||
|
if kind == tyBool8 { return "bool"; }
|
||||||
|
if kind == tyBool16 { return "bool"; }
|
||||||
|
if kind == tyBool32 { return "bool"; }
|
||||||
|
if kind == tyChar8 { return "char"; }
|
||||||
|
if kind == tyChar16 { return "uint16"; }
|
||||||
|
if kind == tyChar32 { return "uint32"; }
|
||||||
|
if kind == tyStr { return "String"; }
|
||||||
|
if kind == tyInt8 { return "int8"; }
|
||||||
|
if kind == tyInt16 { return "int16"; }
|
||||||
|
if kind == tyInt32 { return "int32"; }
|
||||||
|
if kind == tyInt64 { return "int64"; }
|
||||||
|
if kind == tyInt{ return "int"; }
|
||||||
|
if kind == tyUInt8 { return "uint8"; }
|
||||||
|
if kind == tyUInt16 { return "uint16"; }
|
||||||
|
if kind == tyUInt32 { return "uint32"; }
|
||||||
|
if kind == tyUInt64 { return "uint64"; }
|
||||||
|
if kind == tyUInt { return "uint"; }
|
||||||
|
if kind == tyFloat32 { return "float32"; }
|
||||||
|
if kind == tyFloat64 { return "float64"; }
|
||||||
|
if kind == tyPointer { return "void*"; }
|
||||||
|
if kind == tyNamed { return "int"; }
|
||||||
|
return "int";
|
||||||
|
}
|
||||||
|
|
||||||
|
func CBackend_OpToC(op: int) -> String {
|
||||||
|
if op == tkPlus { return "+"; }
|
||||||
|
if op == tkMinus { return "-"; }
|
||||||
|
if op == tkStar { return "*"; }
|
||||||
|
if op == tkSlash { return "/"; }
|
||||||
|
if op == tkPercent { return "%"; }
|
||||||
|
if op == tkEq { return "=="; }
|
||||||
|
if op == tkNe { return "!="; }
|
||||||
|
if op == tkLt { return "<"; }
|
||||||
|
if op == tkLe { return "<="; }
|
||||||
|
if op == tkGt { return ">"; }
|
||||||
|
if op == tkGe { return ">="; }
|
||||||
|
if op == tkAmpAmp { return "&&"; }
|
||||||
|
if op == tkPipePipe { return "||"; }
|
||||||
|
if op == tkBang { return "!"; }
|
||||||
|
if op == tkAmp { return "&"; }
|
||||||
|
if op == tkPipe { return "|"; }
|
||||||
|
if op == tkCaret { return "^"; }
|
||||||
|
if op == tkShl { return "<<"; }
|
||||||
|
if op == tkShr { return ">>"; }
|
||||||
|
if op == tkAssign { return "="; }
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// StringBuilder-based C emitter
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct CEmitter {
|
||||||
|
sb: StringBuilder,
|
||||||
|
indent: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
func CBE_Init() -> CEmitter {
|
||||||
|
var sb: StringBuilder = StringBuilder_NewCap(4096);
|
||||||
|
return CEmitter { sb: sb, indent: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
func CBE_Emit(cbe: *CEmitter, text: String) {
|
||||||
|
var i: int = 0;
|
||||||
|
while i < cbe.indent {
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
func CBE_EmitLine(cbe: *CEmitter, text: String) {
|
||||||
|
CBE_Emit(cbe, text);
|
||||||
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
func CBE_Build(cbe: *CEmitter) -> String {
|
||||||
|
return StringBuilder_Build(&cbe.sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Emit HIR node
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func CBE_EmitExpr(cbe: *CEmitter, node: *HirNode) {
|
||||||
|
if node == null as *HirNode { return; }
|
||||||
|
let kind: int = node.kind;
|
||||||
|
|
||||||
|
// Literal
|
||||||
|
if kind == hLit {
|
||||||
|
if node.intValue == tkNull {
|
||||||
|
StringBuilder_Append(&cbe.sb, "0");
|
||||||
|
} else {
|
||||||
|
StringBuilder_Append(&cbe.sb, node.strValue);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variable
|
||||||
|
if kind == hVar {
|
||||||
|
StringBuilder_Append(&cbe.sb, node.strValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary
|
||||||
|
if kind == hBinary {
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
StringBuilder_Append(&cbe.sb, CBackend_OpToC(node.intValue));
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
CBE_EmitExpr(cbe, node.child2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unary
|
||||||
|
if kind == hUnary {
|
||||||
|
StringBuilder_Append(&cbe.sb, CBackend_OpToC(node.intValue));
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call
|
||||||
|
if kind == hCall {
|
||||||
|
StringBuilder_Append(&cbe.sb, node.strValue);
|
||||||
|
StringBuilder_Append(&cbe.sb, "(");
|
||||||
|
if node.child1 != null as *HirNode {
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
}
|
||||||
|
if node.child2 != null as *HirNode {
|
||||||
|
StringBuilder_Append(&cbe.sb, ", ");
|
||||||
|
CBE_EmitExpr(cbe, node.child2);
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// spawn Callee(args)
|
||||||
|
if kind == hSpawn {
|
||||||
|
StringBuilder_Append(&cbe.sb, "bux_async_spawn(");
|
||||||
|
StringBuilder_Append(&cbe.sb, node.strValue);
|
||||||
|
if node.child1 != null as *HirNode {
|
||||||
|
StringBuilder_Append(&cbe.sb, ", (void*)");
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// await
|
||||||
|
if kind == hAwait {
|
||||||
|
StringBuilder_Append(&cbe.sb, "bux_async_await(");
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
StringBuilder_Append(&cbe.sb, ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return
|
||||||
|
if kind == hReturn {
|
||||||
|
StringBuilder_Append(&cbe.sb, "return");
|
||||||
|
if node.child1 != null as *HirNode {
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alloca
|
||||||
|
if kind == hAlloca {
|
||||||
|
if !String_Eq(node.typeName, "") {
|
||||||
|
StringBuilder_Append(&cbe.sb, node.typeName);
|
||||||
|
} else {
|
||||||
|
StringBuilder_Append(&cbe.sb, "int");
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
StringBuilder_Append(&cbe.sb, node.strValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store: combine alloca + value into single declaration
|
||||||
|
if kind == hStore {
|
||||||
|
if node.child1 != null as *HirNode && node.child1.kind == hAlloca {
|
||||||
|
// Declaration with initializer: Type x = value;
|
||||||
|
if !String_Eq(node.child1.typeName, "") {
|
||||||
|
StringBuilder_Append(&cbe.sb, node.child1.typeName);
|
||||||
|
} else {
|
||||||
|
StringBuilder_Append(&cbe.sb, CBackend_TypeToC(node.child1.intValue));
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
StringBuilder_Append(&cbe.sb, node.child1.strValue);
|
||||||
|
StringBuilder_Append(&cbe.sb, " = ");
|
||||||
|
CBE_EmitExpr(cbe, node.child2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Plain assignment
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
StringBuilder_Append(&cbe.sb, " = ");
|
||||||
|
CBE_EmitExpr(cbe, node.child2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If
|
||||||
|
if kind == hIf {
|
||||||
|
StringBuilder_Append(&cbe.sb, "if (");
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
StringBuilder_Append(&cbe.sb, ") {\n");
|
||||||
|
if node.child2 != null as *HirNode {
|
||||||
|
cbe.indent = cbe.indent + 1;
|
||||||
|
CBE_EmitExpr(cbe, node.child2);
|
||||||
|
cbe.indent = cbe.indent - 1;
|
||||||
|
}
|
||||||
|
var sp: int = 0;
|
||||||
|
while sp < cbe.indent {
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
sp = sp + 1;
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, "}");
|
||||||
|
let elseBlock: *HirNode = node.extraData as *HirNode;
|
||||||
|
if elseBlock != null as *HirNode {
|
||||||
|
StringBuilder_Append(&cbe.sb, " else {\n");
|
||||||
|
cbe.indent = cbe.indent + 1;
|
||||||
|
CBE_EmitExpr(cbe, elseBlock);
|
||||||
|
cbe.indent = cbe.indent - 1;
|
||||||
|
sp = 0;
|
||||||
|
while sp < cbe.indent {
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
sp = sp + 1;
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, "}\n");
|
||||||
|
} else {
|
||||||
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// While
|
||||||
|
if kind == hWhile {
|
||||||
|
StringBuilder_Append(&cbe.sb, "while (");
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
StringBuilder_Append(&cbe.sb, ") {\n");
|
||||||
|
if node.child2 != null as *HirNode {
|
||||||
|
cbe.indent = cbe.indent + 1;
|
||||||
|
CBE_EmitExpr(cbe, node.child2);
|
||||||
|
cbe.indent = cbe.indent - 1;
|
||||||
|
}
|
||||||
|
var sp: int = 0;
|
||||||
|
while sp < cbe.indent {
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
sp = sp + 1;
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, "}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop (infinite)
|
||||||
|
if kind == hLoop {
|
||||||
|
StringBuilder_Append(&cbe.sb, "while (1) {\n");
|
||||||
|
if node.child1 != null as *HirNode {
|
||||||
|
cbe.indent = cbe.indent + 1;
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
cbe.indent = cbe.indent - 1;
|
||||||
|
}
|
||||||
|
var sp: int = 0;
|
||||||
|
while sp < cbe.indent {
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
sp = sp + 1;
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, "}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field access: obj.field — emit as obj->field (since most are pointers)
|
||||||
|
if kind == hFieldPtr {
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
StringBuilder_Append(&cbe.sb, "->");
|
||||||
|
StringBuilder_Append(&cbe.sb, node.strValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sizeof: sizeof(Type) — emit as sizeof(Type)
|
||||||
|
if kind == hSizeOf {
|
||||||
|
StringBuilder_Append(&cbe.sb, "sizeof(");
|
||||||
|
if !String_Eq(node.typeName, "") {
|
||||||
|
StringBuilder_Append(&cbe.sb, node.typeName);
|
||||||
|
} else {
|
||||||
|
StringBuilder_Append(&cbe.sb, "int");
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index: arr[idx] — emit as arr[idx]
|
||||||
|
if kind == hIndexPtr {
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
StringBuilder_Append(&cbe.sb, "[");
|
||||||
|
CBE_EmitExpr(cbe, node.child2);
|
||||||
|
StringBuilder_Append(&cbe.sb, "]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load: *ptr — emit as *ptr (dereference)
|
||||||
|
if kind == hLoad {
|
||||||
|
StringBuilder_Append(&cbe.sb, "(*");
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
StringBuilder_Append(&cbe.sb, ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign: target = value
|
||||||
|
if kind == hAssign {
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
StringBuilder_Append(&cbe.sb, " = ");
|
||||||
|
CBE_EmitExpr(cbe, node.child2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cast
|
||||||
|
if kind == hCast {
|
||||||
|
StringBuilder_Append(&cbe.sb, "((");
|
||||||
|
if !String_Eq(node.typeName, "") {
|
||||||
|
StringBuilder_Append(&cbe.sb, node.typeName);
|
||||||
|
} else {
|
||||||
|
StringBuilder_Append(&cbe.sb, CBackend_TypeToC(node.typeKind));
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, ")");
|
||||||
|
CBE_EmitExpr(cbe, node.child1);
|
||||||
|
StringBuilder_Append(&cbe.sb, ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct init: ((TypeName){.field = value, ...})
|
||||||
|
if kind == hStructInit {
|
||||||
|
StringBuilder_Append(&cbe.sb, "((");
|
||||||
|
StringBuilder_Append(&cbe.sb, node.strValue);
|
||||||
|
StringBuilder_Append(&cbe.sb, "){");
|
||||||
|
// Emit fields (chained via child3)
|
||||||
|
var field: *HirNode = node.child1;
|
||||||
|
var first: bool = true;
|
||||||
|
while field != null as *HirNode {
|
||||||
|
if !first {
|
||||||
|
StringBuilder_Append(&cbe.sb, ", ");
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, ".");
|
||||||
|
StringBuilder_Append(&cbe.sb, field.strValue);
|
||||||
|
StringBuilder_Append(&cbe.sb, " = ");
|
||||||
|
CBE_EmitExpr(cbe, field.child1);
|
||||||
|
first = false;
|
||||||
|
field = field.child3;
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, "})");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block — emit each statement via child3 linked list
|
||||||
|
if kind == hBlock {
|
||||||
|
var child: *HirNode = node.child1;
|
||||||
|
while child != null as *HirNode {
|
||||||
|
// Indent
|
||||||
|
var sp: int = 0;
|
||||||
|
while sp < cbe.indent {
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
sp = sp + 1;
|
||||||
|
}
|
||||||
|
CBE_EmitExpr(cbe, child);
|
||||||
|
// Add semicolon for statements that need it
|
||||||
|
if child.kind != hBlock && child.kind != hIf && child.kind != hWhile && child.kind != hLoop {
|
||||||
|
StringBuilder_Append(&cbe.sb, ";");
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
child = child.child3;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Emit function declaration
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func CBE_EmitFuncDecl(cbe: *CEmitter, f: *HirFunc) {
|
||||||
|
// Return type
|
||||||
|
if String_Eq(f.retTypeName, "") || String_Eq(f.retTypeName, "void") {
|
||||||
|
StringBuilder_Append(&cbe.sb, "void ");
|
||||||
|
} else {
|
||||||
|
StringBuilder_Append(&cbe.sb, f.retTypeName);
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder_Append(&cbe.sb, f.name);
|
||||||
|
StringBuilder_Append(&cbe.sb, "(");
|
||||||
|
|
||||||
|
// Parameters
|
||||||
|
var i: int = 0;
|
||||||
|
while i < f.paramCount {
|
||||||
|
if i > 0 {
|
||||||
|
StringBuilder_Append(&cbe.sb, ", ");
|
||||||
|
}
|
||||||
|
// Use param type info
|
||||||
|
var pname: String = "";
|
||||||
|
var ptype: String = "";
|
||||||
|
if i == 0 { pname = f.param0.name; ptype = f.param0.typeName; }
|
||||||
|
if i == 1 { pname = f.param1.name; ptype = f.param1.typeName; }
|
||||||
|
if i == 2 { pname = f.param2.name; ptype = f.param2.typeName; }
|
||||||
|
if i == 3 { pname = f.param3.name; ptype = f.param3.typeName; }
|
||||||
|
if i == 4 { pname = f.param4.name; ptype = f.param4.typeName; }
|
||||||
|
if i == 5 { pname = f.param5.name; ptype = f.param5.typeName; }
|
||||||
|
// Emit type
|
||||||
|
if String_Eq(ptype, "String") || String_Eq(ptype, "str") {
|
||||||
|
StringBuilder_Append(&cbe.sb, "String ");
|
||||||
|
} else if String_Eq(ptype, "bool") {
|
||||||
|
StringBuilder_Append(&cbe.sb, "bool ");
|
||||||
|
} else if String_Eq(ptype, "int") {
|
||||||
|
StringBuilder_Append(&cbe.sb, "int ");
|
||||||
|
} else if String_Eq(ptype, "int64") {
|
||||||
|
StringBuilder_Append(&cbe.sb, "int64 ");
|
||||||
|
} else if String_Eq(ptype, "uint") {
|
||||||
|
StringBuilder_Append(&cbe.sb, "uint ");
|
||||||
|
} else if String_Eq(ptype, "float64") {
|
||||||
|
StringBuilder_Append(&cbe.sb, "float64 ");
|
||||||
|
} else if !String_Eq(ptype, "") {
|
||||||
|
StringBuilder_Append(&cbe.sb, ptype);
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
} else {
|
||||||
|
StringBuilder_Append(&cbe.sb, "int "); // fallback
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, pname);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder_Append(&cbe.sb, ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Generate complete C module
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func CBackend_Generate(mod: *HirModule) -> String {
|
||||||
|
let cbe: *CEmitter = bux_alloc(sizeof(CEmitter)) as *CEmitter;
|
||||||
|
cbe.sb = StringBuilder_NewCap(8192);
|
||||||
|
cbe.indent = 0;
|
||||||
|
|
||||||
|
// Header
|
||||||
|
StringBuilder_Append(&cbe.sb, "// Generated by Bux C Backend\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "#include <stdint.h>\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "#include <stdbool.h>\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "#include <string.h>\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "#include <stdio.h>\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "#include <stdlib.h>\n\n");
|
||||||
|
// Type aliases
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef const char* String;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef unsigned char uint8;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef unsigned short uint16;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef unsigned int uint32;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef unsigned long long uint64;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef signed char int8;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef short int16;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef long long int64;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef float float32;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef double float64;\n\n");
|
||||||
|
// Runtime declarations
|
||||||
|
StringBuilder_Append(&cbe.sb, "void* bux_alloc(unsigned int size);\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "void bux_free(void* ptr);\n\n");
|
||||||
|
|
||||||
|
// Forward declare all struct types
|
||||||
|
var si: int = 0;
|
||||||
|
while si < mod.structCount {
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef struct ");
|
||||||
|
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
||||||
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
|
si = si + 1;
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
|
||||||
|
// Struct definitions
|
||||||
|
si = 0;
|
||||||
|
while si < mod.structCount {
|
||||||
|
StringBuilder_Append(&cbe.sb, "typedef struct {\n");
|
||||||
|
var fi: int = 0;
|
||||||
|
while fi < mod.structs[si].fieldCount {
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
var ft: String = mod.structs[si].fields[fi].typeName;
|
||||||
|
if String_Eq(ft, "int") || String_Eq(ft, "") {
|
||||||
|
StringBuilder_Append(&cbe.sb, "int");
|
||||||
|
} else if String_Eq(ft, "String") {
|
||||||
|
StringBuilder_Append(&cbe.sb, "String");
|
||||||
|
} else if String_Eq(ft, "bool") {
|
||||||
|
StringBuilder_Append(&cbe.sb, "bool");
|
||||||
|
} else if String_Eq(ft, "uint32") {
|
||||||
|
StringBuilder_Append(&cbe.sb, "uint32");
|
||||||
|
} else {
|
||||||
|
// Use the type name directly (handles "Foo*", "Bar", etc.)
|
||||||
|
StringBuilder_Append(&cbe.sb, ft);
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, " ");
|
||||||
|
StringBuilder_Append(&cbe.sb, mod.structs[si].fields[fi].name);
|
||||||
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
|
fi = fi + 1;
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, "} ");
|
||||||
|
StringBuilder_Append(&cbe.sb, mod.structs[si].name);
|
||||||
|
StringBuilder_Append(&cbe.sb, ";\n\n");
|
||||||
|
si = si + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward declarations for all functions
|
||||||
|
var i: int = 0;
|
||||||
|
while i < mod.funcCount {
|
||||||
|
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
||||||
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
|
||||||
|
// Extern declarations
|
||||||
|
i = 0;
|
||||||
|
while i < mod.externCount {
|
||||||
|
CBE_EmitFuncDecl(cbe, &mod.externFuncs[i]);
|
||||||
|
StringBuilder_Append(&cbe.sb, ";\n");
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, "\n");
|
||||||
|
|
||||||
|
// Function definitions
|
||||||
|
var hasMain: bool = false;
|
||||||
|
i = 0;
|
||||||
|
while i < mod.funcCount {
|
||||||
|
if String_Eq(mod.funcs[i].name, "Main") { hasMain = true; }
|
||||||
|
// Skip forward declarations (functions without body)
|
||||||
|
let body: *HirNode = mod.funcs[i].body;
|
||||||
|
if body == null as *HirNode {
|
||||||
|
i = i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
CBE_EmitFuncDecl(cbe, &mod.funcs[i]);
|
||||||
|
StringBuilder_Append(&cbe.sb, " {\n");
|
||||||
|
// Body
|
||||||
|
var hasReturn: bool = false;
|
||||||
|
cbe.indent = 1;
|
||||||
|
CBE_EmitExpr(cbe, body);
|
||||||
|
cbe.indent = 0;
|
||||||
|
// Check if body has a return statement
|
||||||
|
var stmt: *HirNode = body.child1;
|
||||||
|
while stmt != null as *HirNode {
|
||||||
|
if stmt.kind == hReturn { hasReturn = true; }
|
||||||
|
stmt = stmt.child3;
|
||||||
|
}
|
||||||
|
// Add default return 0 only if function returns int and has no explicit return
|
||||||
|
if String_Eq(mod.funcs[i].retTypeName, "int") && !hasReturn {
|
||||||
|
StringBuilder_Append(&cbe.sb, " return 0;\n");
|
||||||
|
}
|
||||||
|
StringBuilder_Append(&cbe.sb, "}\n\n");
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate C main wrapper if Main function exists
|
||||||
|
if hasMain {
|
||||||
|
StringBuilder_Append(&cbe.sb, "extern int g_argc;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "extern char** g_argv;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "int main(int argc, char** argv) {\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, " g_argc = argc;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, " g_argv = argv;\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, " return Main();\n");
|
||||||
|
StringBuilder_Append(&cbe.sb, "}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return StringBuilder_Build(&cbe.sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
func CBackend_Free(cbe: *CEmitter) {
|
||||||
|
StringBuilder_Free(&cbe.sb);
|
||||||
|
bux_free(cbe as *void);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,812 @@
|
|||||||
|
// nim_backend.bux — Nim transpiler backend
|
||||||
|
// Generates Nim code from the HIR. Much simpler than C backend.
|
||||||
|
|
||||||
|
import Std::String;
|
||||||
|
import Std::Io::{Print, PrintLine};
|
||||||
|
|
||||||
|
module NimBackend {
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Type → Nim type name
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func NimBackend_TypeToNim(kind: int) -> String {
|
||||||
|
if kind == tyVoid { return "void"; }
|
||||||
|
if kind == tyBool || kind == tyBool8 || kind == tyBool16 || kind == tyBool32 { return "bool"; }
|
||||||
|
if kind == tyChar8 { return "char"; }
|
||||||
|
if kind == tyChar16 { return "uint16"; }
|
||||||
|
if kind == tyChar32 { return "uint32"; }
|
||||||
|
if kind == tyStr { return "string"; }
|
||||||
|
if kind == tyInt8 { return "int8"; }
|
||||||
|
if kind == tyInt16 { return "int16"; }
|
||||||
|
if kind == tyInt32 { return "int32"; }
|
||||||
|
if kind == tyInt64 { return "int64"; }
|
||||||
|
if kind == tyInt { return "int"; }
|
||||||
|
if kind == tyUInt8 { return "uint8"; }
|
||||||
|
if kind == tyUInt16 { return "uint16"; }
|
||||||
|
if kind == tyUInt32 { return "uint32"; }
|
||||||
|
if kind == tyUInt64 { return "uint64"; }
|
||||||
|
if kind == tyUInt { return "uint"; }
|
||||||
|
if kind == tyFloat32 { return "float32"; }
|
||||||
|
if kind == tyFloat64 { return "float64"; }
|
||||||
|
if kind == tyPointer { return "pointer"; }
|
||||||
|
return "int";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Operator → Nim
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func NimBackend_OpToNim(op: int) -> String {
|
||||||
|
if op == tkPlus { return "+"; }
|
||||||
|
if op == tkMinus { return "-"; }
|
||||||
|
if op == tkStar { return "*"; }
|
||||||
|
if op == tkSlash { return "/"; }
|
||||||
|
if op == tkPercent { return "%"; }
|
||||||
|
if op == tkEq { return "=="; }
|
||||||
|
if op == tkNe { return "!="; }
|
||||||
|
if op == tkLt { return "<"; }
|
||||||
|
if op == tkLe { return "<="; }
|
||||||
|
if op == tkGt { return ">"; }
|
||||||
|
if op == tkGe { return ">="; }
|
||||||
|
if op == tkAmpAmp { return "and"; }
|
||||||
|
if op == tkPipePipe { return "or"; }
|
||||||
|
if op == tkBang { return "not"; }
|
||||||
|
if op == tkAmp { return "and"; }
|
||||||
|
if op == tkPipe { return "or"; }
|
||||||
|
if op == tkCaret { return "xor"; }
|
||||||
|
if op == tkShl { return "shl"; }
|
||||||
|
if op == tkShr { return "shr"; }
|
||||||
|
if op == tkAssign { return "="; }
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Emitter
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct NBEmitter {
|
||||||
|
sb: StringBuilder;
|
||||||
|
indent: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
func NBE_Init() -> NBEmitter {
|
||||||
|
var sb: StringBuilder = StringBuilder_NewCap(4096);
|
||||||
|
return NBEmitter { sb: sb, indent: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
func NBE_EmitIndent(nbe: *NBEmitter) {
|
||||||
|
var i: int = 0;
|
||||||
|
while i < nbe.indent {
|
||||||
|
StringBuilder_Append(&nbe.sb, " ");
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NBE_Emit(nbe: *NBEmitter, text: String) {
|
||||||
|
NBE_EmitIndent(nbe);
|
||||||
|
StringBuilder_Append(&nbe.sb, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
func NBE_EmitLine(nbe: *NBEmitter, text: String) {
|
||||||
|
NBE_Emit(nbe, text);
|
||||||
|
StringBuilder_Append(&nbe.sb, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
func NBE_Build(nbe: *NBEmitter) -> String {
|
||||||
|
return StringBuilder_Build(&nbe.sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
func NBE_Append(nbe: *NBEmitter, text: String) {
|
||||||
|
StringBuilder_Append(&nbe.sb, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
func NBE_AppendLine(nbe: *NBEmitter, text: String) {
|
||||||
|
StringBuilder_Append(&nbe.sb, text);
|
||||||
|
StringBuilder_Append(&nbe.sb, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
func NBE_AppendChar(nbe: *NBEmitter, c: char8) {
|
||||||
|
var tmp: *char8 = bux_alloc(2) as *char8;
|
||||||
|
tmp[0] = c;
|
||||||
|
tmp[1] = 0 as char8;
|
||||||
|
StringBuilder_Append(&nbe.sb, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Expression emission
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func NBE_EmitExpr(nbe: *NBEmitter, node: *HirNode) {
|
||||||
|
if node == null as *HirNode { return; }
|
||||||
|
let kind: int = node.kind;
|
||||||
|
|
||||||
|
// Literal
|
||||||
|
if kind == hLit {
|
||||||
|
if node.intValue == tkNull {
|
||||||
|
NBE_Append(nbe, "nil");
|
||||||
|
} else if node.intValue == tkStringLiteral {
|
||||||
|
// Nim string: emit quoted with escapes
|
||||||
|
NBE_Append(nbe, "\"");
|
||||||
|
var s: String = node.strValue;
|
||||||
|
if s != null as String {
|
||||||
|
var slen: int = bux_strlen(s) as int;
|
||||||
|
var start: int = 0;
|
||||||
|
var end: int = slen;
|
||||||
|
// Strip outer quotes if present
|
||||||
|
if slen >= 2 {
|
||||||
|
var fc: char8 = s[0];
|
||||||
|
var lc: char8 = s[slen - 1];
|
||||||
|
if fc == 34 as char8 && lc == 34 as char8 {
|
||||||
|
start = 1;
|
||||||
|
end = slen - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var i: int = start;
|
||||||
|
while i < end {
|
||||||
|
var c: char8 = s[i];
|
||||||
|
if c == 10 as char8 { NBE_Append(nbe, "\\n"); } // \n
|
||||||
|
else if c == 13 as char8 { NBE_Append(nbe, "\\r"); } // \r
|
||||||
|
else if c == 9 as char8 { NBE_Append(nbe, "\\t"); } // \t
|
||||||
|
else if c == 34 as char8 { NBE_Append(nbe, "\\\""); } // \"
|
||||||
|
else if c == 92 as char8 { NBE_Append(nbe, "\\\\"); } // \\
|
||||||
|
else { NBE_AppendChar(nbe, c); }
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NBE_Append(nbe, "\"");
|
||||||
|
} else if node.intValue == tkBoolLiteral {
|
||||||
|
NBE_Append(nbe, node.strValue);
|
||||||
|
} else {
|
||||||
|
NBE_Append(nbe, node.strValue);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variable
|
||||||
|
if kind == hVar {
|
||||||
|
NBE_Append(nbe, NimBackend_EscapeIdent(node.strValue));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary
|
||||||
|
if kind == hBinary {
|
||||||
|
// Special case: String == null → check for empty string
|
||||||
|
if node.intValue == tkEq || node.intValue == tkNe {
|
||||||
|
var isNullCmp: bool = false;
|
||||||
|
var other: *HirNode = null as *HirNode;
|
||||||
|
if node.child1 != null as *HirNode && node.child1.kind == hLit && node.child1.intValue == tkNull {
|
||||||
|
isNullCmp = true;
|
||||||
|
other = node.child2;
|
||||||
|
}
|
||||||
|
if !isNullCmp && node.child2 != null as *HirNode && node.child2.kind == hLit && node.child2.intValue == tkNull {
|
||||||
|
isNullCmp = true;
|
||||||
|
other = node.child1;
|
||||||
|
}
|
||||||
|
if isNullCmp && other != null as *HirNode && other.typeKind == tyStr {
|
||||||
|
if node.intValue == tkEq {
|
||||||
|
NBE_EmitExpr(nbe, other);
|
||||||
|
NBE_Append(nbe, " == \"\"");
|
||||||
|
} else {
|
||||||
|
NBE_EmitExpr(nbe, other);
|
||||||
|
NBE_Append(nbe, " != \"\"");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.child1 != null as *HirNode {
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
NBE_Append(nbe, " ");
|
||||||
|
}
|
||||||
|
// Emit operator only if we have a left operand
|
||||||
|
if node.child1 != null as *HirNode {
|
||||||
|
NBE_Append(nbe, NimBackend_OpToNim(node.intValue));
|
||||||
|
NBE_Append(nbe, " ");
|
||||||
|
}
|
||||||
|
NBE_EmitExpr(nbe, node.child2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unary
|
||||||
|
if kind == hUnary {
|
||||||
|
// Skip address-of operator (&) — Nim uses automatic dereference
|
||||||
|
if node.intValue == tkAmp {
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NBE_Append(nbe, NimBackend_OpToNim(node.intValue));
|
||||||
|
NBE_Append(nbe, "(");
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
NBE_Append(nbe, ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call
|
||||||
|
if kind == hCall {
|
||||||
|
var fname: String = node.strValue;
|
||||||
|
// Translate StringBuilder calls to Nim native operations
|
||||||
|
if String_Eq(fname, "StringBuilder_Append") {
|
||||||
|
// sb = sb & text (string concatenation)
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
NBE_Append(nbe, " = ");
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
NBE_Append(nbe, " & ");
|
||||||
|
NBE_EmitExpr(nbe, node.child2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if String_Eq(fname, "StringBuilder_NewCap") {
|
||||||
|
// Just empty string
|
||||||
|
NBE_Append(nbe, "\"\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if String_Eq(fname, "StringBuilder_Build") {
|
||||||
|
// Just return the string field
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NBE_Append(nbe, NimBackend_EscapeIdent(fname));
|
||||||
|
NBE_Append(nbe, "(");
|
||||||
|
if node.child1 != null as *HirNode {
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
}
|
||||||
|
if node.child2 != null as *HirNode {
|
||||||
|
NBE_Append(nbe, ", ");
|
||||||
|
NBE_EmitExpr(nbe, node.child2);
|
||||||
|
}
|
||||||
|
NBE_Append(nbe, ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field access: obj.field → obj.field (Nim auto-dereferences)
|
||||||
|
if kind == hFieldPtr || kind == hArrowField {
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
NBE_Append(nbe, ".");
|
||||||
|
NBE_Append(nbe, node.strValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index: arr[idx]
|
||||||
|
if kind == hIndexPtr {
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
NBE_Append(nbe, "[");
|
||||||
|
NBE_EmitExpr(nbe, node.child2);
|
||||||
|
NBE_Append(nbe, "]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cast: expr as Type
|
||||||
|
if kind == hCast {
|
||||||
|
// Special case: null as Type → appropriate default
|
||||||
|
if node.child1 != null as *HirNode && node.child1.kind == hLit && node.child1.intValue == tkNull {
|
||||||
|
var typeName: String = node.typeName;
|
||||||
|
if !String_Eq(typeName, "") {
|
||||||
|
var converted: String = NimBackend_ConvertTypeName(typeName);
|
||||||
|
if String_Eq(converted, "string") {
|
||||||
|
NBE_Append(nbe, "\"\"");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if String_Eq(converted, "pointer") {
|
||||||
|
NBE_Append(nbe, "nil");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For pointer types (ending in * or "ptr "), emit nil
|
||||||
|
var tlen: int = bux_strlen(typeName) as int;
|
||||||
|
if tlen > 1 {
|
||||||
|
var lastCh: char8 = typeName[tlen - 1];
|
||||||
|
if lastCh == 42 as char8 { // '*'
|
||||||
|
NBE_Append(nbe, "nil");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NBE_Append(nbe, "cast[");
|
||||||
|
if !String_Eq(node.typeName, "") {
|
||||||
|
NBE_Append(nbe, NimBackend_ConvertTypeName(node.typeName));
|
||||||
|
} else {
|
||||||
|
NBE_Append(nbe, NimBackend_TypeToNim(node.typeKind));
|
||||||
|
}
|
||||||
|
NBE_Append(nbe, "](");
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
NBE_Append(nbe, ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is
|
||||||
|
if kind == hIs {
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
NBE_Append(nbe, " is ");
|
||||||
|
if !String_Eq(node.typeName, "") {
|
||||||
|
NBE_Append(nbe, node.typeName);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SizeOf
|
||||||
|
if kind == hSizeOf {
|
||||||
|
NBE_Append(nbe, "sizeof(");
|
||||||
|
if !String_Eq(node.typeName, "") {
|
||||||
|
NBE_Append(nbe, node.typeName);
|
||||||
|
} else {
|
||||||
|
NBE_Append(nbe, "int");
|
||||||
|
}
|
||||||
|
NBE_Append(nbe, ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alloca: variable declaration (used within Store for full decl)
|
||||||
|
if kind == hAlloca {
|
||||||
|
if !String_Eq(node.typeName, "") {
|
||||||
|
NBE_Append(nbe, NimBackend_ConvertTypeNameParam(node.typeName));
|
||||||
|
} else {
|
||||||
|
NBE_Append(nbe, NimBackend_TypeToNim(node.typeKind));
|
||||||
|
}
|
||||||
|
NBE_Append(nbe, " ");
|
||||||
|
NBE_Append(nbe, NimBackend_EscapeIdent(node.strValue));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign: target = value
|
||||||
|
if kind == hAssign {
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
NBE_Append(nbe, " = ");
|
||||||
|
NBE_EmitExpr(nbe, node.child2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store: assignment or declaration with init
|
||||||
|
if kind == hStore {
|
||||||
|
if node.child1 != null as *HirNode && node.child1.kind == hAlloca {
|
||||||
|
// Declaration: var x: Type = value
|
||||||
|
NBE_Append(nbe, "var ");
|
||||||
|
NBE_Append(nbe, NimBackend_EscapeIdent(node.child1.strValue));
|
||||||
|
if !String_Eq(node.child1.typeName, "") {
|
||||||
|
NBE_Append(nbe, ": ");
|
||||||
|
NBE_Append(nbe, NimBackend_ConvertTypeNameParam(node.child1.typeName));
|
||||||
|
}
|
||||||
|
if node.child2 != null as *HirNode {
|
||||||
|
NBE_Append(nbe, " = ");
|
||||||
|
NBE_EmitExpr(nbe, node.child2);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Plain assignment
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
NBE_Append(nbe, " = ");
|
||||||
|
NBE_EmitExpr(nbe, node.child2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If
|
||||||
|
if kind == hIf {
|
||||||
|
NBE_Append(nbe, "if ");
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
NBE_AppendLine(nbe, ":");
|
||||||
|
if node.child2 != null as *HirNode {
|
||||||
|
nbe.indent = nbe.indent + 1;
|
||||||
|
if node.child2.kind != hBlock { NBE_EmitIndent(nbe); }
|
||||||
|
NBE_EmitExpr(nbe, node.child2);
|
||||||
|
nbe.indent = nbe.indent - 1;
|
||||||
|
} else {
|
||||||
|
nbe.indent = nbe.indent + 1;
|
||||||
|
NBE_EmitLine(nbe, "discard");
|
||||||
|
nbe.indent = nbe.indent - 1;
|
||||||
|
}
|
||||||
|
if node.child3 != null as *HirNode {
|
||||||
|
NBE_EmitIndent(nbe); // indent else at same level as if
|
||||||
|
NBE_AppendLine(nbe, "else:");
|
||||||
|
nbe.indent = nbe.indent + 1;
|
||||||
|
if node.child3.kind != hBlock { NBE_EmitIndent(nbe); }
|
||||||
|
NBE_EmitExpr(nbe, node.child3);
|
||||||
|
nbe.indent = nbe.indent - 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// While
|
||||||
|
if kind == hWhile {
|
||||||
|
NBE_Append(nbe, "while ");
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
NBE_AppendLine(nbe, ":");
|
||||||
|
if node.child2 != null as *HirNode {
|
||||||
|
nbe.indent = nbe.indent + 1;
|
||||||
|
if node.child2.kind != hBlock { NBE_EmitIndent(nbe); }
|
||||||
|
NBE_EmitExpr(nbe, node.child2);
|
||||||
|
nbe.indent = nbe.indent - 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop (infinite)
|
||||||
|
if kind == hLoop {
|
||||||
|
NBE_AppendLine(nbe, "while true:");
|
||||||
|
if node.child1 != null as *HirNode {
|
||||||
|
nbe.indent = nbe.indent + 1;
|
||||||
|
if node.child1.kind != hBlock { NBE_EmitIndent(nbe); }
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
nbe.indent = nbe.indent - 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Break / Continue
|
||||||
|
if kind == hBreak {
|
||||||
|
NBE_AppendLine(nbe, "break");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if kind == hContinue {
|
||||||
|
NBE_AppendLine(nbe, "continue");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return
|
||||||
|
if kind == hReturn {
|
||||||
|
NBE_Append(nbe, "return ");
|
||||||
|
if node.child1 != null as *HirNode {
|
||||||
|
if node.child1.kind == hBinary {
|
||||||
|
NBE_Append(nbe, "(");
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
NBE_Append(nbe, ")");
|
||||||
|
} else {
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct init: TypeName { field: value, ... }
|
||||||
|
if kind == hStructInit {
|
||||||
|
NBE_Append(nbe, node.strValue);
|
||||||
|
NBE_Append(nbe, "(");
|
||||||
|
// Emit fields (chained via child3)
|
||||||
|
var field: *HirNode = node.child3;
|
||||||
|
var first: bool = true;
|
||||||
|
while field != null as *HirNode {
|
||||||
|
if !first { NBE_Append(nbe, ", "); }
|
||||||
|
NBE_Append(nbe, field.strValue);
|
||||||
|
NBE_Append(nbe, ": ");
|
||||||
|
NBE_EmitExpr(nbe, field.child1);
|
||||||
|
first = false;
|
||||||
|
field = field.child3;
|
||||||
|
}
|
||||||
|
NBE_Append(nbe, ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load: dereference — in Nim just emit the pointer expression
|
||||||
|
if kind == hLoad {
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block — sequence of statements
|
||||||
|
if kind == hBlock {
|
||||||
|
var child: *HirNode = node.child1;
|
||||||
|
if child == null as *HirNode {
|
||||||
|
NBE_EmitIndent(nbe);
|
||||||
|
NBE_AppendLine(nbe, "discard");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// First pass: emit statements, merging continuation lines
|
||||||
|
while child != null as *HirNode {
|
||||||
|
NBE_EmitIndent(nbe);
|
||||||
|
// Check if this is a return/binary that has continuation siblings
|
||||||
|
if child.kind == hReturn && child.child1 != null as *HirNode {
|
||||||
|
// Emit return and open paren before expression
|
||||||
|
NBE_Append(nbe, "return (");
|
||||||
|
NBE_EmitExpr(nbe, child.child1);
|
||||||
|
child = child.child3;
|
||||||
|
// Merge all continuation siblings
|
||||||
|
while child != null as *HirNode && child.kind == hBinary && child.child1 == null as *HirNode {
|
||||||
|
NBE_Append(nbe, " ");
|
||||||
|
NBE_EmitExpr(nbe, child);
|
||||||
|
child = child.child3;
|
||||||
|
}
|
||||||
|
NBE_AppendLine(nbe, ")");
|
||||||
|
} else {
|
||||||
|
// Wrap with discard for non-control-flow statements
|
||||||
|
if child.kind == hCall || child.kind == hAssign || child.kind == hStore {
|
||||||
|
NBE_Append(nbe, "discard (");
|
||||||
|
NBE_EmitExpr(nbe, child);
|
||||||
|
NBE_Append(nbe, ")");
|
||||||
|
} else {
|
||||||
|
NBE_EmitExpr(nbe, child);
|
||||||
|
}
|
||||||
|
child = child.child3;
|
||||||
|
// Merge continuation siblings for other statement types
|
||||||
|
while child != null as *HirNode && child.kind == hBinary && child.child1 == null as *HirNode {
|
||||||
|
NBE_Append(nbe, " ");
|
||||||
|
NBE_EmitExpr(nbe, child);
|
||||||
|
child = child.child3;
|
||||||
|
}
|
||||||
|
NBE_AppendLine(nbe, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match — simplified
|
||||||
|
if kind == hMatch {
|
||||||
|
NBE_EmitLine(nbe, "# match (simplified)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn
|
||||||
|
if kind == hSpawn {
|
||||||
|
NBE_Append(nbe, "spawn ");
|
||||||
|
NBE_Append(nbe, node.strValue);
|
||||||
|
NBE_Append(nbe, "(");
|
||||||
|
if node.child1 != null as *HirNode {
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
}
|
||||||
|
NBE_Append(nbe, ")");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Await
|
||||||
|
if kind == hAwait {
|
||||||
|
NBE_Append(nbe, "await ");
|
||||||
|
NBE_EmitExpr(nbe, node.child1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback
|
||||||
|
NBE_Append(nbe, "nil");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Function declaration emission
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func NBE_EmitFuncDecl(nbe: *NBEmitter, f: *HirFunc) {
|
||||||
|
// Return type
|
||||||
|
var retType: String = f.retTypeName;
|
||||||
|
if String_Eq(retType, "") || String_Eq(retType, "void") {
|
||||||
|
// No return type annotation needed
|
||||||
|
} else if String_Eq(retType, "int") {
|
||||||
|
NBE_Append(nbe, "int");
|
||||||
|
} else if String_Eq(retType, "bool") {
|
||||||
|
NBE_Append(nbe, "bool");
|
||||||
|
} else if String_Eq(retType, "string") || String_Eq(retType, "String") {
|
||||||
|
NBE_Append(nbe, "string");
|
||||||
|
} else {
|
||||||
|
NBE_Append(nbe, retType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Convert type name from Bux to Nim
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func NimBackend_EscapeIdent(name: String) -> String {
|
||||||
|
if name == null as String || String_Eq(name, "") { return "x"; }
|
||||||
|
// Nim keywords that need escaping
|
||||||
|
if String_Eq(name, "block") { return "`block`"; }
|
||||||
|
if String_Eq(name, "type") { return "`type`"; }
|
||||||
|
if String_Eq(name, "object") { return "`object`"; }
|
||||||
|
if String_Eq(name, "proc") { return "`proc`"; }
|
||||||
|
if String_Eq(name, "var") { return "`var`"; }
|
||||||
|
if String_Eq(name, "let") { return "`let`"; }
|
||||||
|
if String_Eq(name, "const") { return "`const`"; }
|
||||||
|
if String_Eq(name, "from") { return "`from`"; }
|
||||||
|
if String_Eq(name, "import") { return "`import`"; }
|
||||||
|
if String_Eq(name, "export") { return "`export`"; }
|
||||||
|
if String_Eq(name, "include") { return "`include`"; }
|
||||||
|
if String_Eq(name, "iterator") { return "`iterator`"; }
|
||||||
|
if String_Eq(name, "method") { return "`method`"; }
|
||||||
|
if String_Eq(name, "func") { return "`func`"; }
|
||||||
|
if String_Eq(name, "string") { return "`string`"; }
|
||||||
|
if String_Eq(name, "int") { return "`int`"; }
|
||||||
|
if String_Eq(name, "bool") { return "`bool`"; }
|
||||||
|
if String_Eq(name, "float") { return "`float`"; }
|
||||||
|
if String_Eq(name, "char") { return "`char`"; }
|
||||||
|
if String_Eq(name, "ptr") { return "`ptr`"; }
|
||||||
|
if String_Eq(name, "ref") { return "`ref`"; }
|
||||||
|
if String_Eq(name, "nil") { return "`nil`"; }
|
||||||
|
if String_Eq(name, "true") { return "`true`"; }
|
||||||
|
if String_Eq(name, "false") { return "`false`"; }
|
||||||
|
if String_Eq(name, "and") { return "`and`"; }
|
||||||
|
if String_Eq(name, "or") { return "`or`"; }
|
||||||
|
if String_Eq(name, "not") { return "`not`"; }
|
||||||
|
if String_Eq(name, "xor") { return "`xor`"; }
|
||||||
|
if String_Eq(name, "shl") { return "`shl`"; }
|
||||||
|
if String_Eq(name, "shr") { return "`shr`"; }
|
||||||
|
if String_Eq(name, "div") { return "`div`"; }
|
||||||
|
if String_Eq(name, "mod") { return "`mod`"; }
|
||||||
|
if String_Eq(name, "is") { return "`is`"; }
|
||||||
|
if String_Eq(name, "of") { return "`of`"; }
|
||||||
|
if String_Eq(name, "in") { return "`in`"; }
|
||||||
|
if String_Eq(name, "out") { return "`out`"; }
|
||||||
|
if String_Eq(name, "static") { return "`static`"; }
|
||||||
|
if String_Eq(name, "enum") { return "`enum`"; }
|
||||||
|
if String_Eq(name, "tuple") { return "`tuple`"; }
|
||||||
|
if String_Eq(name, "concept") { return "`concept`"; }
|
||||||
|
if String_Eq(name, "distinct") { return "`distinct`"; }
|
||||||
|
if String_Eq(name, "Result") { return "`Result`"; }
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
func NimBackend_ConvertTypeNameParam(tn: String) -> String {
|
||||||
|
// For function parameters: strip pointer indirection, Nim passes by ref
|
||||||
|
if tn == null as String || String_Eq(tn, "") { return "int"; }
|
||||||
|
if String_Eq(tn, "String") { return "string"; }
|
||||||
|
if String_Eq(tn, "bool") { return "bool"; }
|
||||||
|
if String_Eq(tn, "int") { return "int"; }
|
||||||
|
if String_Eq(tn, "int64") { return "int64"; }
|
||||||
|
if String_Eq(tn, "uint32") { return "uint32"; }
|
||||||
|
if String_Eq(tn, "uint") { return "uint"; }
|
||||||
|
if String_Eq(tn, "float64") { return "float64"; }
|
||||||
|
// Pointer types → ptr types in Nim
|
||||||
|
if !String_Eq(tn, "") {
|
||||||
|
var tlen: int = bux_strlen(tn) as int;
|
||||||
|
if tlen > 1 {
|
||||||
|
var lastCh: char8 = tn[tlen - 1];
|
||||||
|
if lastCh == 42 as char8 {
|
||||||
|
var base: String = String_Slice(tn, 0 as uint, (tlen - 1) as uint);
|
||||||
|
if String_Eq(base, "void") { return "pointer"; }
|
||||||
|
return String_Concat("ptr ", base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tn;
|
||||||
|
}
|
||||||
|
|
||||||
|
func NimBackend_ConvertTypeName(tn: String) -> String {
|
||||||
|
if tn == null as String || String_Eq(tn, "") { return "int"; }
|
||||||
|
if String_Eq(tn, "String") { return "string"; }
|
||||||
|
if String_Eq(tn, "bool") { return "bool"; }
|
||||||
|
if String_Eq(tn, "int") { return "int"; }
|
||||||
|
if String_Eq(tn, "int64") { return "int64"; }
|
||||||
|
if String_Eq(tn, "uint32") { return "uint32"; }
|
||||||
|
if String_Eq(tn, "uint") { return "uint"; }
|
||||||
|
if String_Eq(tn, "float64") { return "float64"; }
|
||||||
|
// Convert pointer types: "Foo*" → "ptr Foo"
|
||||||
|
if !String_Eq(tn, "") {
|
||||||
|
var tlen: int = bux_strlen(tn) as int;
|
||||||
|
if tlen > 1 {
|
||||||
|
var lastCh: char8 = tn[tlen - 1];
|
||||||
|
if lastCh == 42 as char8 { // '*'
|
||||||
|
// Extract base name (without *)
|
||||||
|
var base: String = String_Slice(tn, 0 as uint, (tlen - 1) as uint);
|
||||||
|
if String_Eq(base, "void") { return "pointer"; }
|
||||||
|
return String_Concat("ptr ", base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tn;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Generate complete Nim module
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func NimBackend_Generate(mod: *HirModule) -> String {
|
||||||
|
let nbe: *NBEmitter = bux_alloc(sizeof(NBEmitter)) as *NBEmitter;
|
||||||
|
nbe.sb = StringBuilder_NewCap(8192);
|
||||||
|
nbe.indent = 0;
|
||||||
|
|
||||||
|
// Header
|
||||||
|
NBE_AppendLine(nbe, "# Generated by Bux Nim Backend");
|
||||||
|
NBE_AppendLine(nbe, "");
|
||||||
|
NBE_AppendLine(nbe, "import std/strutils");
|
||||||
|
NBE_AppendLine(nbe, "import std/tables");
|
||||||
|
NBE_AppendLine(nbe, "import std/sequtils");
|
||||||
|
NBE_AppendLine(nbe, "import std/os");
|
||||||
|
NBE_AppendLine(nbe, "");
|
||||||
|
|
||||||
|
// Type definitions — skip builtins that Nim already has
|
||||||
|
NBE_AppendLine(nbe, "# Type aliases");
|
||||||
|
NBE_AppendLine(nbe, "type");
|
||||||
|
nbe.indent = nbe.indent + 1;
|
||||||
|
NBE_EmitLine(nbe, "String* = string");
|
||||||
|
NBE_EmitLine(nbe, "char8* = char");
|
||||||
|
// StringBuild is just a string in Nim
|
||||||
|
NBE_EmitLine(nbe, "StringBuilder* = string");
|
||||||
|
// Close type block
|
||||||
|
nbe.indent = nbe.indent - 1;
|
||||||
|
NBE_AppendLine(nbe, "");
|
||||||
|
|
||||||
|
NBE_AppendLine(nbe, "");
|
||||||
|
|
||||||
|
// Struct definitions
|
||||||
|
if mod.structCount > 0 {
|
||||||
|
NBE_AppendLine(nbe, "# Struct types");
|
||||||
|
NBE_AppendLine(nbe, "type");
|
||||||
|
var si: int = 0;
|
||||||
|
while si < mod.structCount {
|
||||||
|
NBE_Append(nbe, " ");
|
||||||
|
NBE_Append(nbe, NimBackend_EscapeIdent(mod.structs[si].name));
|
||||||
|
NBE_AppendLine(nbe, " = object");
|
||||||
|
var fi: int = 0;
|
||||||
|
while fi < mod.structs[si].fieldCount {
|
||||||
|
var fname: String = mod.structs[si].fields[fi].name;
|
||||||
|
if !String_Eq(fname, "") {
|
||||||
|
NBE_Append(nbe, " ");
|
||||||
|
NBE_Append(nbe, NimBackend_EscapeIdent(fname));
|
||||||
|
NBE_Append(nbe, ": ");
|
||||||
|
var ft: String = mod.structs[si].fields[fi].typeName;
|
||||||
|
NBE_Append(nbe, NimBackend_ConvertTypeName(ft));
|
||||||
|
NBE_AppendLine(nbe, "");
|
||||||
|
}
|
||||||
|
fi = fi + 1;
|
||||||
|
}
|
||||||
|
si = si + 1;
|
||||||
|
}
|
||||||
|
NBE_AppendLine(nbe, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Const definitions
|
||||||
|
if mod.constCount > 0 {
|
||||||
|
NBE_AppendLine(nbe, "# Constants");
|
||||||
|
var ci: int = 0;
|
||||||
|
while ci < mod.constCount {
|
||||||
|
NBE_Emit(nbe, "const ");
|
||||||
|
NBE_Append(nbe, mod.consts[ci].name);
|
||||||
|
NBE_Append(nbe, " = ");
|
||||||
|
NBE_Append(nbe, String_FromInt(mod.consts[ci].value as int64));
|
||||||
|
NBE_AppendLine(nbe, "");
|
||||||
|
ci = ci + 1;
|
||||||
|
}
|
||||||
|
NBE_AppendLine(nbe, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extern declarations — skip (Nim doesn't need them)
|
||||||
|
// Bux extern functions are C runtime functions that Nim can't directly call.
|
||||||
|
// Instead, we use Nim native equivalents (e.g., len() instead of bux_strlen).
|
||||||
|
|
||||||
|
// Function definitions (reverse order: dependencies first)
|
||||||
|
var i: int = mod.funcCount;
|
||||||
|
while i > 0 {
|
||||||
|
i = i - 1;
|
||||||
|
let f: *HirFunc = &mod.funcs[i];
|
||||||
|
if f.body == null as *HirNode { continue; }
|
||||||
|
|
||||||
|
NBE_Emit(nbe, "proc ");
|
||||||
|
NBE_Append(nbe, NimBackend_EscapeIdent(f.name));
|
||||||
|
NBE_Append(nbe, "(");
|
||||||
|
|
||||||
|
// Parameters
|
||||||
|
var pi: int = 0;
|
||||||
|
var pnames: *HirParam = &f.param0;
|
||||||
|
while pi < f.paramCount {
|
||||||
|
if pi > 0 { NBE_Append(nbe, ", "); }
|
||||||
|
NBE_Append(nbe, NimBackend_EscapeIdent(pnames[pi].name));
|
||||||
|
NBE_Append(nbe, ": ");
|
||||||
|
var pt: String = pnames[pi].typeName;
|
||||||
|
NBE_Append(nbe, NimBackend_ConvertTypeNameParam(pt));
|
||||||
|
pi = pi + 1;
|
||||||
|
}
|
||||||
|
NBE_Append(nbe, "): ");
|
||||||
|
// Return type
|
||||||
|
var rtn: String = NimBackend_ConvertTypeNameParam(f.retTypeName);
|
||||||
|
if String_Eq(rtn, "") || String_Eq(rtn, "void") {
|
||||||
|
NBE_AppendLine(nbe, "void =");
|
||||||
|
} else {
|
||||||
|
NBE_Append(nbe, rtn);
|
||||||
|
NBE_AppendLine(nbe, " =");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Body
|
||||||
|
nbe.indent = nbe.indent + 1;
|
||||||
|
if f.body != null as *HirNode && f.body.kind != hBlock {
|
||||||
|
NBE_EmitIndent(nbe);
|
||||||
|
}
|
||||||
|
NBE_EmitExpr(nbe, f.body);
|
||||||
|
nbe.indent = nbe.indent - 1;
|
||||||
|
NBE_AppendLine(nbe, "");
|
||||||
|
NBE_AppendLine(nbe, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main entry point
|
||||||
|
NBE_AppendLine(nbe, "# Entry point");
|
||||||
|
NBE_AppendLine(nbe, "when isMainModule:");
|
||||||
|
NBE_EmitLine(nbe, " var args: seq[string] = @[]");
|
||||||
|
NBE_EmitLine(nbe, " var argCount = paramCount()");
|
||||||
|
NBE_EmitLine(nbe, " discard Main()");
|
||||||
|
NBE_EmitLine(nbe, "");
|
||||||
|
|
||||||
|
let result: String = NBE_Build(nbe);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // module NimBackend
|
||||||
@@ -298,10 +298,6 @@ func parserParsePrimary(p: *Parser) -> *Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
func dummyFunc() -> int {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Postfix: call, index, field access, as, is, ?
|
// Postfix: call, index, field access, as, is, ?
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
+11
-1
@@ -71,12 +71,15 @@ Output = "Bin"
|
|||||||
```bash
|
```bash
|
||||||
# Type-check without building
|
# Type-check without building
|
||||||
./buxc check
|
./buxc check
|
||||||
|
./buxc check ./myproject
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
./buxc build
|
./buxc build
|
||||||
|
./buxc build ./myproject
|
||||||
|
|
||||||
# Build and run
|
# Build and run
|
||||||
./buxc run
|
./buxc run
|
||||||
|
./buxc run ./myproject
|
||||||
|
|
||||||
# Clean build artifacts
|
# Clean build artifacts
|
||||||
./buxc clean
|
./buxc clean
|
||||||
@@ -136,7 +139,14 @@ bux/
|
|||||||
│ │ ├── Io.bux
|
│ │ ├── Io.bux
|
||||||
│ │ ├── Array.bux
|
│ │ ├── Array.bux
|
||||||
│ │ ├── String.bux
|
│ │ ├── String.bux
|
||||||
│ │ └── Map.bux
|
│ │ ├── Map.bux
|
||||||
|
│ │ ├── Fs.bux
|
||||||
|
│ │ ├── Mem.bux
|
||||||
|
│ │ ├── Set.bux
|
||||||
|
│ │ ├── Path.bux
|
||||||
|
│ │ ├── Math.bux
|
||||||
|
│ │ ├── Task.bux
|
||||||
|
│ │ └── Channel.bux
|
||||||
│ ├── runtime.c # C runtime shim
|
│ ├── runtime.c # C runtime shim
|
||||||
│ └── io.c # C I/O functions
|
│ └── io.c # C I/O functions
|
||||||
├── examples/ # Example programs
|
├── examples/ # Example programs
|
||||||
|
|||||||
+142
-5
@@ -22,6 +22,33 @@ Basic I/O and file operations wrapping C stdio.
|
|||||||
| `WriteFile` | `func WriteFile(path: String, content: String) -> bool` | Write string to file |
|
| `WriteFile` | `func WriteFile(path: String, content: String) -> bool` | Write string to file |
|
||||||
| `FileExists` | `func FileExists(path: String) -> bool` | Check if file exists |
|
| `FileExists` | `func FileExists(path: String) -> bool` | Check if file exists |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Std::Fs
|
||||||
|
|
||||||
|
File system operations beyond basic I/O.
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|----------|-----------|-------------|
|
||||||
|
| `DirExists` | `func DirExists(path: String) -> bool` | Check if directory exists |
|
||||||
|
| `Mkdir` | `func Mkdir(path: String) -> bool` | Create directory (and parents if needed) |
|
||||||
|
| `ListDir` | `func ListDir(dir: String, ext: String, count: *int) -> *String` | List files matching extension |
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```bux
|
||||||
|
import Std::Fs::{DirExists, Mkdir, ListDir};
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
if DirExists("/tmp") {
|
||||||
|
PrintLine("/tmp exists");
|
||||||
|
}
|
||||||
|
Mkdir("build/output");
|
||||||
|
var count: int = 0;
|
||||||
|
let files: *String = ListDir("src", ".bux", &count);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
```bux
|
```bux
|
||||||
import Std::Io::{PrintLine, ReadFile, WriteFile};
|
import Std::Io::{PrintLine, ReadFile, WriteFile};
|
||||||
@@ -114,6 +141,35 @@ String manipulation utilities.
|
|||||||
| `String_FromInt` | `func String_FromInt(n: int64) -> String` | Int to string |
|
| `String_FromInt` | `func String_FromInt(n: int64) -> String` | Int to string |
|
||||||
| `String_ToInt` | `func String_ToInt(s: String) -> int64` | String to int |
|
| `String_ToInt` | `func String_ToInt(s: String) -> int64` | String to int |
|
||||||
|
|
||||||
|
### Find, Replace & Format
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|----------|-----------|-------------|
|
||||||
|
| `String_Find` | `func String_Find(haystack: String, needle: String) -> String` | Find substring (returns pointer; 0 = not found) |
|
||||||
|
| `String_Replace` | `func String_Replace(s: String, old: String, new: String) -> String` | Replace first occurrence |
|
||||||
|
| `String_Format1` | `func String_Format1(pattern: String, a0: String) -> String` | Format with 1 arg (`{0}`) |
|
||||||
|
| `String_Format2` | `func String_Format2(pattern: String, a0: String, a1: String) -> String` | Format with 2 args |
|
||||||
|
| `String_Format3` | `func String_Format3(pattern: String, a0: String, a1: String, a2: String) -> String` | Format with 3 args |
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```bux
|
||||||
|
import Std::String;
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let s: String = String_Replace("hello world", "world", "Bux");
|
||||||
|
PrintLine(s); // "hello Bux"
|
||||||
|
|
||||||
|
let fmt: String = String_Format2("{0} + {1} = magic", "Bux", "QBE");
|
||||||
|
PrintLine(fmt); // "Bux + QBE = magic"
|
||||||
|
|
||||||
|
let found: String = String_Find("hello world", "world");
|
||||||
|
if found as uint != 0 {
|
||||||
|
PrintLine("found!");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Std::StringBuilder
|
## Std::StringBuilder
|
||||||
@@ -157,6 +213,82 @@ func Main() -> int {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Std::Mem
|
||||||
|
|
||||||
|
Memory management wrappers around C runtime functions.
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|----------|-----------|-------------|
|
||||||
|
| `Alloc` | `func Alloc(size: uint) -> *void` | Allocate memory |
|
||||||
|
| `Realloc` | `func Realloc(ptr: *void, size: uint) -> *void` | Reallocate memory |
|
||||||
|
| `Free` | `func Free(ptr: *void)` | Free memory |
|
||||||
|
| `MemEq` | `func MemEq(a: *void, b: *void, size: uint) -> bool` | Byte-wise memory comparison |
|
||||||
|
| `New` | `func New<T>() -> *T` | Typed allocation (`sizeof(T)` bytes) |
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```bux
|
||||||
|
import Std::Mem;
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let p: *void = Alloc(64);
|
||||||
|
Free(p);
|
||||||
|
|
||||||
|
let n: *int = New<int>();
|
||||||
|
// use n...
|
||||||
|
Free(n as *void);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Std::Set
|
||||||
|
|
||||||
|
Generic hash set for deduplication and membership testing.
|
||||||
|
|
||||||
|
### Types
|
||||||
|
```bux
|
||||||
|
struct SetEntry<T> {
|
||||||
|
value: T,
|
||||||
|
occupied: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Set<T> {
|
||||||
|
entries: *SetEntry<T>,
|
||||||
|
cap: uint,
|
||||||
|
len: uint,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|----------|-----------|-------------|
|
||||||
|
| `Set_New<T>` | `func Set_New<T>(cap: uint) -> Set<T>` | Create set |
|
||||||
|
| `Set_Add<T>` | `func Set_Add<T>(s: *Set<T>, value: T)` | Insert element (ignores duplicates) |
|
||||||
|
| `Set_Has<T>` | `func Set_Has<T>(s: *Set<T>, value: T) -> bool` | Check membership |
|
||||||
|
| `Set_Len<T>` | `func Set_Len<T>(s: *Set<T>) -> uint` | Element count |
|
||||||
|
| `Set_Free<T>` | `func Set_Free<T>(s: *Set<T>)` | Free memory |
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```bux
|
||||||
|
import Std::Set;
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
var s: Set<int> = Set_New<int>(16);
|
||||||
|
Set_Add(&s, 10);
|
||||||
|
Set_Add(&s, 20);
|
||||||
|
Set_Add(&s, 10); // duplicate, ignored
|
||||||
|
if Set_Has(&s, 10) {
|
||||||
|
PrintLine("has 10");
|
||||||
|
}
|
||||||
|
Set_Free(&s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Std::Map
|
## Std::Map
|
||||||
|
|
||||||
Generic hash map `Map<K, V>` for value-type keys (int, float, etc.).
|
Generic hash map `Map<K, V>` for value-type keys (int, float, etc.).
|
||||||
@@ -317,8 +449,13 @@ func Main() -> int {
|
|||||||
|
|
||||||
- `Std::Result` — Shipped via algebraic enums ✅
|
- `Std::Result` — Shipped via algebraic enums ✅
|
||||||
- `Std::Option` — Shipped via algebraic enums ✅
|
- `Std::Option` — Shipped via algebraic enums ✅
|
||||||
- `Std::Math` — `Sqrt`, `Pow`, `Min`, `Max`, `Abs`
|
- `Std::Math` — `Sqrt`, `Pow`, `Min`, `Max`, `Abs` ✅
|
||||||
- `Std::Os` — `Args`, `Env`, `Exit`, `Cwd`
|
- `Std::Fs` — Directory operations ✅
|
||||||
- `Std::Fmt` — String formatting with interpolation
|
- `Std::Mem` — Memory wrappers ✅
|
||||||
- `Std::Iter` — Iterator trait and combinators
|
- `Std::Set` — Hash set ✅
|
||||||
- `Std::Task` / `Std::Channel` — Lightweight concurrency (pthread-based threads)
|
- `Std::Path` — Path manipulation ✅
|
||||||
|
- `Std::Os` — `Args`, `Env`, `Exit`, `Cwd` ⏳
|
||||||
|
- `Std::Process` — Spawn subprocess ⏳
|
||||||
|
- `Std::Fmt` — String formatting with interpolation ⏳
|
||||||
|
- `Std::Iter` — Iterator trait and combinators ⏳
|
||||||
|
- `Std::Task` / `Std::Channel` — Lightweight concurrency (pthread-based threads) ✅
|
||||||
+3
-3
@@ -258,7 +258,7 @@ proc collectDepDecls(lock: Lockfile, root: string, opts: GlobalOptions): seq[Dec
|
|||||||
|
|
||||||
proc cmdCheck*(args: seq[string], opts: GlobalOptions): int =
|
proc cmdCheck*(args: seq[string], opts: GlobalOptions): int =
|
||||||
let useColor = shouldUseColor(opts)
|
let useColor = shouldUseColor(opts)
|
||||||
let root = getCurrentDir()
|
let root = if args.len > 0: absolutePath(args[0]) else: getCurrentDir()
|
||||||
let manifestPath = root / "bux.toml"
|
let manifestPath = root / "bux.toml"
|
||||||
if not fileExists(manifestPath):
|
if not fileExists(manifestPath):
|
||||||
printError("no bux.toml found", useColor)
|
printError("no bux.toml found", useColor)
|
||||||
@@ -415,7 +415,7 @@ proc mergeDecls(stdlibDecls: seq[Decl], userDecls: seq[Decl]): seq[Decl] =
|
|||||||
|
|
||||||
proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
|
proc cmdBuild*(args: seq[string], opts: GlobalOptions): int =
|
||||||
let useColor = shouldUseColor(opts)
|
let useColor = shouldUseColor(opts)
|
||||||
let root = getCurrentDir()
|
let root = if args.len > 0: absolutePath(args[0]) else: getCurrentDir()
|
||||||
let manifestPath = root / "bux.toml"
|
let manifestPath = root / "bux.toml"
|
||||||
if not fileExists(manifestPath):
|
if not fileExists(manifestPath):
|
||||||
printError("no bux.toml found", useColor)
|
printError("no bux.toml found", useColor)
|
||||||
@@ -554,7 +554,7 @@ proc cmdRun*(args: seq[string], opts: GlobalOptions): int =
|
|||||||
let buildRes = cmdBuild(args, opts)
|
let buildRes = cmdBuild(args, opts)
|
||||||
if buildRes != 0:
|
if buildRes != 0:
|
||||||
return buildRes
|
return buildRes
|
||||||
let root = getCurrentDir()
|
let root = if args.len > 0: absolutePath(args[0]) else: getCurrentDir()
|
||||||
let man = loadManifest(root / "bux.toml")
|
let man = loadManifest(root / "bux.toml")
|
||||||
let outputName = if man.name != "": man.name else: "bux_out"
|
let outputName = if man.name != "": man.name else: "bux_out"
|
||||||
let outputFile = root / "build" / outputName
|
let outputFile = root / "build" / outputName
|
||||||
|
|||||||
@@ -298,10 +298,6 @@ func parserParsePrimary(p: *Parser) -> *Expr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
func dummyFunc() -> int {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// Postfix: call, index, field access, as, is, ?
|
// Postfix: call, index, field access, as, is, ?
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +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;
|
||||||
|
|
||||||
|
func DirExists(path: String) -> bool {
|
||||||
|
return bux_dir_exists(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +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;
|
||||||
|
|
||||||
|
func Alloc(size: uint) -> *void {
|
||||||
|
return bux_alloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
func Realloc(ptr: *void, size: uint) -> *void {
|
||||||
|
return bux_realloc(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 New<T>() -> *T {
|
||||||
|
let sz: uint = sizeof(T);
|
||||||
|
return bux_alloc(sz) as *T;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
idx = (idx + 1) % s.cap;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
idx = (idx + 1) % s.cap;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Set_Len<T>(s: *Set<T>) -> uint {
|
||||||
|
return s.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ extern func bux_sb_free(sb: *void);
|
|||||||
extern func bux_str_split_count(s: String, delim: String) -> uint;
|
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_split_part(s: String, delim: String, index: uint) -> String;
|
||||||
extern func bux_str_join2(a: String, b: String, sep: String) -> String;
|
extern func bux_str_join2(a: String, b: String, sep: String) -> 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 {
|
func String_Len(s: String) -> uint {
|
||||||
@@ -155,4 +156,38 @@ func String_Join2(a: String, b: String, sep: String) -> String {
|
|||||||
return bux_str_join2(a, b, sep);
|
return bux_str_join2(a, b, sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// String find/replace/format
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func String_Find(haystack: String, needle: String) -> String {
|
||||||
|
return bux_strstr(haystack, needle);
|
||||||
|
}
|
||||||
|
|
||||||
|
func String_Replace(s: String, old: String, new: String) -> String {
|
||||||
|
let pos: String = bux_strstr(s, old);
|
||||||
|
if pos as uint == 0 {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
let oldLen: uint = bux_strlen(old);
|
||||||
|
let prefixLen: uint = pos as uint - s as uint;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, "", "", "", "", "");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user