diff --git a/Makefile b/Makefile index 9ae2cca..0823d3e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ NIM := nim -SRC := compiler/bootstrap/main.nim +SRC := bootstrap/main.nim OUT := buxc BUILD_DIR := build @@ -21,15 +21,15 @@ debug: dev test: build test-examples @echo "Running lexer tests..." - $(NIM) c -r compiler/tests/lexer_test.nim + $(NIM) c -r tests/lexer_test.nim @echo "Running parser tests..." - $(NIM) c -r compiler/tests/parser_test.nim + $(NIM) c -r tests/parser_test.nim @echo "Running sema tests..." - $(NIM) c -r compiler/tests/sema_test.nim + $(NIM) c -r tests/sema_test.nim @echo "Running HIR tests..." - $(NIM) c -r compiler/tests/hir_test.nim + $(NIM) c -r tests/hir_test.nim @echo "Running borrow checker tests..." - $(NIM) c -r compiler/tests/borrow_test.nim + $(NIM) c -r tests/borrow_test.nim @echo "Running integration tests..." rm -rf _test_tmp_pkg ./$(OUT) new _test_tmp_pkg @@ -62,14 +62,15 @@ clean: rm -rf _test_cast _test_cast2 _test_cast3 _test_channel clean-all: clean - rm -rf _selfhost/src _selfhost/build + rm -rf build/selfhost selfhost: build @echo "=== Building self-hosted compiler ===" - @rm -rf _selfhost/src _selfhost/build - @mkdir -p _selfhost/src - @cp compiler/selfhost/*.bux _selfhost/src/ - @mv _selfhost/src/main.bux _selfhost/src/Main.bux 2>/dev/null || true - @cd _selfhost && ../$(OUT) build - @strip _selfhost/build/buxc2 + @rm -rf build/selfhost + @mkdir -p build/selfhost/src + @cp src/*.bux build/selfhost/src/ + @cp src/bux.toml build/selfhost/ + @mv build/selfhost/src/main.bux build/selfhost/src/Main.bux 2>/dev/null || true + @cd build/selfhost && ../../$(OUT) build + @strip build/selfhost/build/buxc2 2>/dev/null || true @echo "=== Self-hosted compiler built successfully ===" diff --git a/PLAN.md b/PLAN.md index becc8fc..7fca0fa 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1,16 +1,20 @@ -# Bux Programming Language — Roadmap to Self-Hosting +# Bux Programming Language — Roadmap to v1.0.0 -> **Bootstrap Implementation:** Nim -> **Target:** Bux compiler written in Bux (self-hosting) +> **Version:** 0.3.0 (2026-06-06) +> **Bootstrap:** Nim (`bootstrap/`) — compiles `src/` → `buxc` +> **Self-host:** Bux (`src/`) — compiles via `buxc` → `buxc2` +> **Target:** Bux v1.0.0 — fully self-hosting, gradual ownership, tooling --- ## Overview -Bux is a fast, compiled, strongly-typed, multi-paradigm systems programming language. The strategy is **bootstrap via Nim** — we build the first Bux compiler in Nim, then progressively rewrite it in Bux until it compiles itself. +Bux is a fast, compiled, strongly-typed, multi-paradigm systems programming language. The strategy is **bootstrap via Nim** — we built the first Bux compiler in Nim, then rewrote it in Bux. The Nim bootstrap now exists only as a build scaffold. **Core philosophy:** Systems-level control with modern ergonomics. No hidden costs, no hidden allocations, no hidden control flow. +**Killer feature:** Gradual ownership — write fast like C, add safety like Rust, but only where you choose (`@[Checked]`). + --- ## Language Design Goals (Bux vs Rust vs Nim vs Zig) @@ -286,11 +290,11 @@ Phase 7.5 — Driver (depends on all): --- -## Phase 7 — Self-Hosting: The Great Rewrite 🔄 (In Progress) +## Phase 7 — Self-Hosting: The Great Rewrite ✅ (Complete) **Goal:** Bux compiler compiles itself. This is the **main milestone**. -**All 14 modules ported** in `compiler/selfhost/` (4094 LOC total). Self-hosted project structure in `_selfhost/`. +**All 14 modules ported** in `src/` (4094 LOC total). Built via `make selfhost`. | Task | Status | Details | LOC | |------|--------|---------|-----| @@ -342,7 +346,7 @@ Phase 7.5 — Driver (depends on all): **Self-hosted compiler stats:** ``` -$ _selfhost/build/buxc2 version +$ src/build/buxc2 version Bux 0.2.0 (self-hosting bootstrap) Pipeline modules: Lexer ✅ 695 lines @@ -355,8 +359,8 @@ Pipeline modules: **Bootstrap loop goal:** ``` -buxc (Nim) → compile compiler/selfhost/*.bux → buxc2 (Bux binary) -buxc2 (Bux) → compile compiler/selfhost/*.bux → buxc3 (Bux binary) +buxc (Nim) → compile src/*.bux → buxc2 (Bux binary) +buxc2 (Bux) → compile src/*.bux → buxc3 (Bux binary) compare buxc2 == buxc3 → SELF-HOSTED ✅ ``` @@ -534,60 +538,187 @@ const TABLE_SIZE = Factorial(10); // Computed at compile time --- -## File Structure (Target) +## File Structure (v0.3.0 — Current) ``` bux/ -├── bux.toml # Compiler package manifest +├── bux.toml # Bootstrap compiler manifest (v0.3.0) ├── README.md -├── PLAN.md +├── PLAN.md # This file ├── Makefile # build, test, selfhost -├── src/ -│ ├── Main.bux # CLI entry point -│ ├── Lexer.bux -│ ├── Parser.bux -│ ├── Ast.bux -│ ├── Sema.bux -│ ├── Type.bux -│ ├── Hir.bux -│ ├── Lir.bux -│ ├── CBackend.bux # C transpiler (primary backend) -│ ├── X64Backend.bux # Native x86-64 backend (optional) -│ ├── Linker.bux # Custom linker / build driver -│ ├── Manifest.bux # bux.toml parser -│ └── Package.bux # Package resolution -├── library/ -│ ├── Std/ -│ │ ├── Io.bux -│ │ ├── Memory.bux -│ │ ├── String.bux -│ │ ├── Array.bux -│ │ ├── Map.bux -│ │ ├── Math.bux -│ │ ├── Os.bux -│ │ ├── Path.bux -│ │ ├── Process.bux -│ │ ├── Result.bux # Result and Option -│ │ ├── Iter.bux # Iterator trait and combinators -│ │ ├── Fmt.bux # String formatting -│ │ ├── Task.bux # Lightweight concurrency -│ │ ├── Channel.bux # Message passing -│ │ └── Sync.bux # Mutex, RwLock, atomic -│ └── Runtime.c # C runtime shim -├── tests/ -│ ├── Lexer/ -│ ├── Parser/ -│ ├── Sema/ -│ ├── Codegen/ -│ └── Integration/ -└── docs/ - ├── LanguageRef.md - ├── Ownership.md - └── Concurrency.md +├── src/ # 🎯 CANONICAL: Bux compiler source +│ ├── bux.toml # Self-host compiler manifest +│ ├── main.bux # Entry point (renamed → Main.bux at build) +│ ├── lexer.bux # Tokenizer (UTF-8 state machine, 697 LOC) +│ ├── parser.bux # Pratt parser (1004 LOC) +│ ├── ast.bux # AST node types (algebraic enums) +│ ├── sema.bux # Type checker / semantic analysis (393 LOC) +│ ├── types.bux # Type factories +│ ├── scope.bux # Symbol table +│ ├── hir.bux # High-level IR definitions +│ ├── hir_lower.bux # AST → HIR lowering (307 LOC) +│ ├── c_backend.bux # HIR → C code generator (264 LOC) +│ ├── cli.bux # CLI command dispatch +│ ├── manifest.bux # bux.toml / TOML parser +│ ├── token.bux # Token kind definitions +│ └── source_location.bux # Source location tracking +├── bootstrap/ # 🔧 Nim bootstrap (build scaffold only) +│ ├── main.nim # Entry point +│ ├── cli.nim # CLI commands + build driver +│ └── ... # (mirrors src/ structure) +├── lib/ # 📦 Standard library (23 modules) +│ ├── Io.bux # Print, ReadFile, WriteFile +│ ├── String.bux # Full string API (len, concat, split, format...) +│ ├── Array.bux # Generic Array +│ ├── Map.bux # Generic Map + StringMap +│ ├── Set.bux # Generic Set +│ ├── Math.bux # Sqrt, Pow, Min, Max, Abs +│ ├── Mem.bux # Alloc, Realloc, Free +│ ├── Path.bux # Path_Join, Path_Parent, Path_Ext +│ ├── Fs.bux # DirExists, Mkdir, ListDir +│ ├── Task.bux # Lightweight tasks (spawn/await) +│ ├── Channel.bux # Producer/consumer channels +│ ├── Sync.bux # Mutex, RwLock +│ ├── Result.bux # Result + Option + ? operator +│ ├── Iter.bux # Iterator trait +│ ├── Fmt.bux # String formatting +│ ├── Os.bux # Args, Env, Exit, Cwd +│ ├── Time.bux # Time measurement +│ ├── Process.bux # Subprocess spawning +│ ├── Net.bux # TCP client/server +│ ├── Crypto.bux # SHA256, HMAC, Base64 +│ ├── Json.bux # JSON parse/stringify +│ └── Test.bux # Test framework +├── rt/ # ⚙️ C runtime +│ ├── runtime.c # Memory, string, path helpers +│ └── io.c # File I/O wrappers +├── tests/ # 🧪 Unit tests (Nim) +│ ├── lexer_test.nim +│ ├── parser_test.nim +│ ├── sema_test.nim +│ ├── hir_test.nim +│ ├── borrow_test.nim +│ └── testdata/ +├── examples/ # Example programs +├── docs/ # Documentation +│ ├── LanguageRef.md +│ ├── BuildAndTest.md +│ ├── STRATEGY.md +│ ├── PHASE8_STRATEGY.md +│ └── Packages.md +├── build/ # Build artifacts (gitignored) +│ └── selfhost/ # Self-host compiler build dir +└── vendor/ # Vendored dependencies ``` --- +## Phase 10 🔄 — Path to v1.0.0 (In Progress) + +### 10.0 — v0.3.0 Restructuring ✅ (Completed 2026-06-06) + +| Task | Status | Details | +|------|--------|---------| +| Directory restructure | ✅ | `compiler/selfhost/` → `src/`, `compiler/bootstrap/` → `bootstrap/`, `library/std/` → `lib/`, `library/runtime/` → `rt/`, `compiler/tests/` → `tests/` | +| Path updates | ✅ | Updated Makefile, cli.nim, cli.bux, test files, docs | +| Selfhost fix | ✅ | Build via `build/selfhost/` (project wrapper) | +| Push to GitHub | ✅ | `ac969b3` — v0.3.0 restructure | + +### 10.1 — Selfhost Loop 🔄 (v0.4.0 target) + +**Goal:** `buxc2` can compile itself producing a binary-identical `buxc3`. + +``` +buxc (Nim) → src/*.bux → buxc2 ✅ (already works) +buxc2 → src/*.bux → buxc3 🔄 (needs verification) +buxc2 == buxc3 🔄 (deterministic codegen) +``` + +| Task | Status | Details | +|------|--------|---------| +| `10.1.1` Verify buxc2 can build src/ | 🔄 | Run `buxc2 build` on the selfhost project | +| `10.1.2` Deterministic C codegen | ⏳ | Remove timestamps, random IDs, non-deterministic ordering | +| `10.1.3` Binary-identical loop | ⏳ | `make selfhost-loop` — 2-pass bootstrap verification | +| `10.1.4` Remove hardcoded paths | ⏳ | No `/home/ziko/...` paths in cli.bux | +| `10.1.5` Selfhost test in CI | ⏳ | Add to `make test` | + +### 10.2 — Gradual Ownership 🔄 (v0.5.0 target) ⭐ Killer Feature + +**Goal:** `@[Checked]` functions get full borrow checking. Without this, Bux is "C with modern syntax." + +| Task | Status | Details | +|------|--------|---------| +| `10.2.1` `@[Checked]` attribute gate | ⏳ | Enable/disable borrow checker per function | +| `10.2.2` `&T` shared reference check | ⏳ | No mutation through shared refs | +| `10.2.3` `&mut T` exclusive mutable check | ⏳ | No aliasing of mutable refs | +| `10.2.4` Bounds checking on slices | ⏳ | Buffer overflow prevention | +| `10.2.5` Lifetime elision (simple rules) | ⏳ | 80% of cases without annotations | +| `10.2.6` Explicit lifetimes `'a` | ⏳ | Only for complex cases | + +### 10.3 — Compiler Architecture Upgrade (v0.6.0 target) + +**Goal:** Proper module structure instead of flat file mirror of Nim bootstrap. + +``` +src/ +├── main.bux +├── frontend/ +│ ├── lexer.bux +│ ├── parser.bux +│ └── ast.bux +├── analysis/ +│ ├── sema.bux +│ ├── types.bux +│ ├── scope.bux +│ └── borrow.bux # NEW — borrow checker +├── lowering/ +│ ├── hir.bux +│ └── hir_lower.bux +├── backend/ +│ └── c_backend.bux +└── driver/ + ├── cli.bux + ├── manifest.bux + ├── token.bux + └── source_location.bux +``` + +### 10.4 — Stdlib Completion (v0.7.0 target) + +| Module | Status | Priority | +|--------|--------|----------| +| `Std::Os` — Args, Env, Exit, Cwd | ⏳ | P0 | +| `Std::Process` — spawn subprocess | ⏳ | P0 | +| `Std::Iter` — map, filter, fold | ⏳ | P1 | +| `Std::Fmt` — string interpolation | ⏳ | P1 | + +### 10.5 — Tooling (v0.8.0 target) + +| Tool | Status | Priority | +|------|--------|----------| +| `bux test` — test runner | ⏳ | P0 | +| `bux fmt` — code formatter | ⏳ | P1 | +| `bux doc` — doc generator | ⏳ | P2 | + +### 10.6 — Native Backend (v0.9.0 target) + +| Task | Status | +|------|--------| +| Direct x86-64 codegen (no C) | ⏳ | +| ELF64 output | ⏳ | +| System V AMD64 ABI | ⏳ | + +### 10.7 — v1.0.0 Release Criteria + +- [ ] Compiler self-hosts binary-identically +- [ ] `@[Checked]` borrow checker works on real code +- [ ] Stdlib complete enough for CLI tools +- [ ] `bux test`, `bux fmt`, `bux doc` exist +- [ ] Documentation + 30+ examples +- [ ] CI/CD pipeline (build + test on push) + +--- + ## Language Design Decisions (Bux Improvements) ### What Bux learns from Rust diff --git a/_selfhost/bux.toml b/_selfhost/bux.toml deleted file mode 100644 index efd3c8b..0000000 --- a/_selfhost/bux.toml +++ /dev/null @@ -1,7 +0,0 @@ -[Package] -Name = "buxc2" -Version = "0.2.0" -Type = "bin" - -[Build] -Output = "Bin" \ No newline at end of file diff --git a/_selfhost/src/Main.bux b/_selfhost/src/Main.bux deleted file mode 100644 index 88940f3..0000000 --- a/_selfhost/src/Main.bux +++ /dev/null @@ -1,23 +0,0 @@ -// main.bux — Entry point for the Bux self-hosting compiler -module Main { - -// C runtime for command-line args -extern func bux_argc() -> int; -extern func bux_argv(index: int) -> String; -extern func bux_alloc(size: uint) -> *void; - -// Forward declaration from Cli module -func Cli_Run(args: *String, argCount: int) -> int; - -func Main() -> int { - let count: int = bux_argc(); - // Allocate array of String pointers - let args: *String = bux_alloc(count as uint * 8) as *String; - var i: int = 0; - while i < count { - args[i] = bux_argv(i); - i = i + 1; - } - return Cli_Run(args, count); -} -} diff --git a/_selfhost/src/ast.bux b/_selfhost/src/ast.bux deleted file mode 100644 index c8369d7..0000000 --- a/_selfhost/src/ast.bux +++ /dev/null @@ -1,431 +0,0 @@ -// ast.bux — AST node types (Expr, Stmt, Decl, Pattern, TypeExpr) -module Ast { - -// --------------------------------------------------------------------------- -// SourceLocation (inline for convenience) -// --------------------------------------------------------------------------- -struct SourceLoc { - line: uint32, - column: uint32, -} - -// --------------------------------------------------------------------------- -// Token (lightweight inline) -// --------------------------------------------------------------------------- -struct AstToken { - kind: int, - text: String, - line: uint32, - column: uint32, -} - -// --------------------------------------------------------------------------- -// TypeExpr — type expressions -// --------------------------------------------------------------------------- -const tekNamed: int = 0; -const tekPath: int = 1; -const tekSlice: int = 2; -const tekPointer: int = 3; -const tekTuple: int = 4; -const tekSelf: int = 5; - -struct TypeExpr { - kind: int, - line: uint32, - column: uint32, - typeName: String, // for tekNamed - pathStr: String, // for tekPath (segments joined with ::) - pathCount: int, // number of path segments - typeArgName0: String, // up to 2 type args - typeArgName1: String, - typeArgCount: int, - sliceElement: *TypeExpr, // for tekSlice - pointerPointee: *TypeExpr, // for tekPointer -} - -// --------------------------------------------------------------------------- -// Pattern — match patterns -// --------------------------------------------------------------------------- -const pkWildcard: int = 0; -const pkLiteral: int = 1; -const pkIdent: int = 2; -const pkRange: int = 3; -const pkEnum: int = 4; -const pkStruct: int = 5; -const pkTuple: int = 6; - -struct Pattern { - kind: int, - line: uint32, - column: uint32, - patIdent: String, // for pkIdent - patLitKind: int, // for pkLiteral (token kind) - patLitText: String, // for pkLiteral (token text) - patRangeInclusive: bool, // for pkRange - patEnumPath: String, // for pkEnum (path joined) - patStructName: String, // for pkStruct -} - -// --------------------------------------------------------------------------- -// Expr — expressions (tagged union) -// --------------------------------------------------------------------------- -const ekLiteral: int = 0; -const ekIdent: int = 1; -const ekSelf: int = 2; -const ekPath: int = 3; -const ekSizeOf: int = 4; -const ekUnary: int = 5; -const ekPostfix: int = 6; -const ekBinary: int = 7; -const ekAssign: int = 8; -const ekTernary: int = 9; -const ekRange: int = 10; -const ekCall: int = 11; -const ekGenericCall: int = 12; -const ekIndex: int = 13; -const ekField: int = 14; -const ekStructInit: int = 15; -const ekSlice: int = 16; -const ekTuple: int = 17; -const ekCast: int = 18; -const ekIs: int = 19; -const ekTry: int = 20; -const ekUnwrap: int = 23; -const ekBlock: int = 21; -const ekMatch: int = 22; -const ekSpawn: int = 24; -const ekAwait: int = 25; - -struct ExprList { - expr: *Expr, - next: *ExprList, -} - -struct Expr { - kind: int, - line: uint32, - column: uint32, - // Common fields - strValue: String, // ident name, path segments, field name, callee - intValue: int, // operator kind, intrinsic kind - boolValue: bool, // range inclusive - tokKind: int, // literal token kind - tokText: String, // literal token text - // Children (up to 3 sub-expressions) - child1: *Expr, // left, operand, callee, cond, subject - child2: *Expr, // right, index, then, value - child3: *Expr, // else, third - // Extra references - refType: *TypeExpr, // cast type, is type, sizeof type - refBlock: *Block, // for ekBlock - // Generic call - genericCallee: String, - genericTypeArg0: String, - genericTypeArg1: String, - genericTypeArgCount: int, - // Struct init fields - structName: String, - structFieldCount: int, - // Call arguments (linked list for multi-arg support) - callArgs: *ExprList, - callArgCount: int, -} - -// --------------------------------------------------------------------------- -// Block — sequence of statements -// --------------------------------------------------------------------------- -struct Block { - line: uint32, - column: uint32, - stmtCount: int, - firstStmt: *Stmt, - lastStmt: *Stmt, -} - -// --------------------------------------------------------------------------- -// Stmt — statements -// --------------------------------------------------------------------------- -const skExpr: int = 0; -const skLet: int = 1; -const skIf: int = 2; -const skWhile: int = 3; -const skDoWhile: int = 4; -const skLoop: int = 5; -const skFor: int = 6; -const skMatch: int = 7; -const skReturn: int = 8; -const skBreak: int = 9; -const skContinue: int = 10; -const skDecl: int = 11; - -struct ElseIf { - line: uint32; - column: uint32; - cond: *Expr; - block: *Block; -} - -struct Stmt { - kind: int, - line: uint32, - column: uint32, - // Common fields - strValue: String, // let name, pattern ident, label, for var - boolValue: bool, // let mutable - // Children - child1: *Expr, // init expr, condition, iter expr, return value - child2: *Expr, // match subject - child3: *Expr, // extra - refStmtType: *TypeExpr, // let type annotation - refStmtPattern: *Pattern,// let pattern - refStmtDecl: *Decl, // for skDecl - refStmtBlock: *Block, // then/body block - refStmtElse: *Block, // else block - // Else-if chain - elseIfCount: int, - // Linked list - nextStmt: *Stmt, -} - -// --------------------------------------------------------------------------- -// Decl — declarations -// --------------------------------------------------------------------------- -const dkFunc: int = 0; -const dkStruct: int = 1; -const dkEnum: int = 2; -const dkUnion: int = 3; -const dkInterface: int = 4; -const dkImpl: int = 5; -const dkModule: int = 6; -const dkUse: int = 7; -const dkConst: int = 8; -const dkTypeAlias: int = 9; -const dkExternFunc: int = 10; -const dkExternVar: int = 11; - -struct Param { - line: uint32; - column: uint32; - name: String; - refParamType: *TypeExpr; - isVariadic: bool; -} - -struct StructField { - line: uint32; - column: uint32; - isPublic: bool; - name: String; - refFieldType: *TypeExpr; -} - -struct EnumVariant { - line: uint32; - column: uint32; - name: String; - fieldCount: int; - fieldTypeName0: String; - fieldTypeName1: String; -} - -struct Decl { - kind: int, - line: uint32, - column: uint32, - isPublic: bool, - isAsync: bool, - // Names - strValue: String, // decl name - strValue2: String, // interface name, dll name, module path - // Type params (up to 2) - typeParam0: String, - typeParam1: String, - typeParamCount: int, - // Params (for functions) - paramCount: int, - param0: Param, - param1: Param, - param2: Param, - param3: Param, - param4: Param, - param5: Param, - param6: Param, - param7: Param, - param8: Param, - retType: *TypeExpr, - // Body - refBody: *Block, - // Struct fields (up to 64) - fieldCount: int, - field0: StructField, - field1: StructField, - field2: StructField, - field3: StructField, - field4: StructField, - field5: StructField, - field6: StructField, - field7: StructField, - field8: StructField, - field9: StructField, - field10: StructField, - field11: StructField, - field12: StructField, - field13: StructField, - field14: StructField, - field15: StructField, - field16: StructField, - field17: StructField, - field18: StructField, - field19: StructField, - field20: StructField, - field21: StructField, - field22: StructField, - field23: StructField, - field24: StructField, - field25: StructField, - field26: StructField, - field27: StructField, - field28: StructField, - field29: StructField, - field30: StructField, - field31: StructField, - field32: StructField, - field33: StructField, - field34: StructField, - field35: StructField, - field36: StructField, - field37: StructField, - field38: StructField, - field39: StructField, - field40: StructField, - field41: StructField, - field42: StructField, - field43: StructField, - field44: StructField, - field45: StructField, - field46: StructField, - field47: StructField, - field48: StructField, - field49: StructField, - field50: StructField, - field51: StructField, - field52: StructField, - field53: StructField, - field54: StructField, - field55: StructField, - field56: StructField, - field57: StructField, - field58: StructField, - field59: StructField, - field60: StructField, - field61: StructField, - field62: StructField, - field63: StructField, - // Enum variants (up to 8) - variantCount: int, - variant0: EnumVariant, - variant1: EnumVariant, - variant2: EnumVariant, - variant3: EnumVariant, - variant4: EnumVariant, - variant5: EnumVariant, - variant6: EnumVariant, - variant7: EnumVariant, - // Impl methods (up to 4) - methodCount: int, - // Use/import - useKind: int, - usePath: String, - useNames: String, // joined names for multi-import - // Const - constType: *TypeExpr, - constValue: *Expr, - // Type alias - aliasType: *TypeExpr, - // Extern func - extFuncDll: String, - extFuncVariadic: bool, - extFuncRetType: *TypeExpr, - // Children - childDecl1: *Decl, // linked list of decls (for module items, impl methods) - childDecl2: *Decl, -} - -// --------------------------------------------------------------------------- -// Module — AST root -// --------------------------------------------------------------------------- -struct Module { - name: String, - path: String, // path segments joined - itemCount: int, - firstItem: *Decl, -} - -// --------------------------------------------------------------------------- -// Constructor helpers -// --------------------------------------------------------------------------- - -func Ast_MakeExpr(kind: int, line: uint32, col: uint32) -> Expr { - return Expr { kind: kind, line: line, column: col, - strValue: "", intValue: 0, boolValue: false, - tokKind: 0, tokText: "", - child1: null as *Expr, child2: null as *Expr, child3: null as *Expr, - refType: null as *TypeExpr, refBlock: null as *Block, - genericCallee: "", genericTypeArg0: "", genericTypeArg1: "", genericTypeArgCount: 0, - structName: "", structFieldCount: 0, - callArgs: null as *ExprList, callArgCount: 0 }; -} - -func Ast_MakeIdent(name: String, line: uint32, col: uint32) -> Expr { - var e: Expr = Ast_MakeExpr(ekIdent, line, col); - e.strValue = name; - return e; -} - -func Ast_MakeLiteral(tokKind: int, text: String, line: uint32, col: uint32) -> Expr { - var e: Expr = Ast_MakeExpr(ekLiteral, line, col); - e.tokKind = tokKind; - e.tokText = text; - return e; -} - -func Ast_MakeBinary(op: int, left: *Expr, right: *Expr, line: uint32, col: uint32) -> Expr { - var e: Expr = Ast_MakeExpr(ekBinary, line, col); - e.intValue = op; - e.child1 = left; - e.child2 = right; - return e; -} - -func Ast_MakeCall(callee: *Expr, line: uint32, col: uint32) -> Expr { - var e: Expr = Ast_MakeExpr(ekCall, line, col); - e.child1 = callee; - return e; -} - -func Ast_MakeStmt(kind: int, line: uint32, col: uint32) -> Stmt { - return Stmt { kind: kind, line: line, column: col, - strValue: "", boolValue: false, - child1: null as *Expr, child2: null as *Expr, child3: null as *Expr, - refStmtType: null as *TypeExpr, refStmtPattern: null as *Pattern, - refStmtDecl: null as *Decl, refStmtBlock: null as *Block, refStmtElse: null as *Block, - elseIfCount: 0 }; -} - -func Ast_MakeDecl(kind: int, line: uint32, col: uint32) -> Decl { - return Decl { kind: kind, line: line, column: col, isPublic: false, - strValue: "", strValue2: "", - typeParam0: "", typeParam1: "", typeParamCount: 0, - paramCount: 0, - retType: null as *TypeExpr, - refBody: null as *Block, - fieldCount: 0, - variantCount: 0, - methodCount: 0, - useKind: 0, usePath: "", useNames: "", - constType: null as *TypeExpr, constValue: null as *Expr, - aliasType: null as *TypeExpr, - extFuncDll: "", extFuncVariadic: false, extFuncRetType: null as *TypeExpr, - childDecl1: null as *Decl, childDecl2: null as *Decl }; -} -} diff --git a/_selfhost/src/c_backend.bux b/_selfhost/src/c_backend.bux deleted file mode 100644 index 958d8b8..0000000 --- a/_selfhost/src/c_backend.bux +++ /dev/null @@ -1,689 +0,0 @@ -// 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, "("); - var needsComma: bool = false; - if node.child1 != null as *HirNode { - CBE_EmitExpr(cbe, node.child1); - needsComma = true; - } - if node.child2 != null as *HirNode { - if needsComma { - StringBuilder_Append(&cbe.sb, ", "); - } - CBE_EmitExpr(cbe, node.child2); - needsComma = true; - } - // Emit extra args from linked list - var ai: int = 0; - var curExtra: *HirArgList = node.extraData as *HirArgList; - while ai < node.extraCount { - if needsComma { - StringBuilder_Append(&cbe.sb, ", "); - } - CBE_EmitExpr(cbe, curExtra.node); - needsComma = true; - curExtra = curExtra.next; - ai = ai + 1; - } - 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; } - if i == 6 { pname = f.param6.name; ptype = f.param6.typeName; } - if i == 7 { pname = f.param7.name; ptype = f.param7.typeName; } - if i == 8 { pname = f.param8.name; ptype = f.param8.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, ")"); -} - -// --------------------------------------------------------------------------- -// Helpers for detecting generic declarations (not yet monomorphized) -// --------------------------------------------------------------------------- - -func CBE_IsGenericTypeName(name: String) -> bool { - if String_Eq(name, "T") || String_Eq(name, "K") || String_Eq(name, "V") { return true; } - if String_Eq(name, "T*") || String_Eq(name, "K*") || String_Eq(name, "V*") { return true; } - // Generic container structs and their pointer variants - if String_Eq(name, "Array") || String_Eq(name, "Array*") { return true; } - if String_Eq(name, "SetEntry") || String_Eq(name, "SetEntry*") { return true; } - if String_Eq(name, "StringMapEntry") || String_Eq(name, "StringMapEntry*") { return true; } - if String_Eq(name, "MapEntry") || String_Eq(name, "MapEntry*") { return true; } - return false; -} - -func CBE_StructHasGeneric(st: *HirStruct) -> bool { - var fi: int = 0; - while fi < st.fieldCount { - if CBE_IsGenericTypeName(st.fields[fi].typeName) { return true; } - fi = fi + 1; - } - return false; -} - -func CBE_FuncHasGeneric(f: *HirFunc) -> bool { - var pi: int = 0; - while pi < f.paramCount { - var ptype: String = ""; - if pi == 0 { ptype = f.param0.typeName; } - if pi == 1 { ptype = f.param1.typeName; } - if pi == 2 { ptype = f.param2.typeName; } - if pi == 3 { ptype = f.param3.typeName; } - if pi == 4 { ptype = f.param4.typeName; } - if pi == 5 { ptype = f.param5.typeName; } - if pi == 6 { ptype = f.param6.typeName; } - if pi == 7 { ptype = f.param7.typeName; } - if pi == 8 { ptype = f.param8.typeName; } - if CBE_IsGenericTypeName(ptype) { return true; } - pi = pi + 1; - } - if CBE_IsGenericTypeName(f.retTypeName) { return true; } - return false; -} - -// --------------------------------------------------------------------------- -// 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 \n"); - StringBuilder_Append(&cbe.sb, "#include \n"); - StringBuilder_Append(&cbe.sb, "#include \n"); - StringBuilder_Append(&cbe.sb, "#include \n"); - StringBuilder_Append(&cbe.sb, "#include \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"); - StringBuilder_Append(&cbe.sb, "typedef char char8;\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 (skip generics) - var si: int = 0; - while si < mod.structCount { - if !CBE_StructHasGeneric(&mod.structs[si]) { - 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"); - - // Enum definitions (typedef to int) - var ei: int = 0; - while ei < mod.enumCount { - StringBuilder_Append(&cbe.sb, "typedef int "); - StringBuilder_Append(&cbe.sb, mod.enums[ei].name); - StringBuilder_Append(&cbe.sb, ";\n"); - ei = ei + 1; - } - if mod.enumCount > 0 { - StringBuilder_Append(&cbe.sb, "\n"); - } - - // Constant definitions - var ci: int = 0; - while ci < mod.constCount { - StringBuilder_Append(&cbe.sb, "#define "); - StringBuilder_Append(&cbe.sb, mod.consts[ci].name); - StringBuilder_Append(&cbe.sb, " "); - StringBuilder_Append(&cbe.sb, String_FromInt(mod.consts[ci].value as int64)); - StringBuilder_Append(&cbe.sb, "\n"); - ci = ci + 1; - } - if mod.constCount > 0 { - StringBuilder_Append(&cbe.sb, "\n"); - } - - // Struct definitions (skip generics) - si = 0; - while si < mod.structCount { - if CBE_StructHasGeneric(&mod.structs[si]) { - si = si + 1; - continue; - } - StringBuilder_Append(&cbe.sb, "struct "); - StringBuilder_Append(&cbe.sb, mod.structs[si].name); - StringBuilder_Append(&cbe.sb, " {\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, "};\n\n"); - si = si + 1; - } - - // Forward declarations for all functions (skip generics) - var i: int = 0; - while i < mod.funcCount { - if !CBE_FuncHasGeneric(&mod.funcs[i]) { - 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 (skip generics) - var hasMain: bool = false; - i = 0; - while i < mod.funcCount { - if String_Eq(mod.funcs[i].name, "Main") { hasMain = true; } - // Skip generic functions - if CBE_FuncHasGeneric(&mod.funcs[i]) { - i = i + 1; - continue; - } - // 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); -} -} diff --git a/_selfhost/src/cli.bux b/_selfhost/src/cli.bux deleted file mode 100644 index f4ac134..0000000 --- a/_selfhost/src/cli.bux +++ /dev/null @@ -1,761 +0,0 @@ -// cli.bux — CLI driver for the Bux self-hosting compiler -// Wires together: Lexer → Parser → Sema → HirLower → CBackend -module Cli { - -extern func PrintLine(s: String); -extern func Print(s: String); -extern func bux_read_file(path: String) -> String; -extern func bux_write_file(path: String, content: String) -> bool; -extern func bux_file_exists(path: String) -> int; -extern func bux_dir_exists(path: String) -> int; -extern func bux_path_join(a: String, b: String) -> String; -extern func bux_mkdir_if_needed(path: String) -> int; -extern func bux_run_nim(nim_file: String, out_bin: String) -> int; -extern func bux_list_dir(dir: String, ext: String, out_count: *int) -> *String; -extern func bux_system(cmd: String) -> int; - -func ReadFile(path: String) -> String { - return bux_read_file(path); -} - -func WriteFile(path: String, content: String) -> bool { - return bux_write_file(path, content); -} - -func FileExists(path: String) -> bool { - return bux_file_exists(path) != 0; -} - -func DirExists(path: String) -> bool { - return bux_dir_exists(path) != 0; -} - -// Import the compiler pipeline -// In self-hosting mode, these are compiled together from compiler/selfhost/ - -// --------------------------------------------------------------------------- -// Compile a single .bux source file -// --------------------------------------------------------------------------- - -func Cli_Compile(source: String, sourceName: String) -> String { - // Phase 1: Lex - PrintLine(" Lexing..."); - let lex: *Lexer = Lexer_Tokenize(source); - PrintLine(" Lex done"); - if Lexer_DiagCount(lex) > 0 { - PrintLine("Lex errors:"); - var i: int = 0; - while i < Lexer_DiagCount(lex) { - Print(" "); - PrintLine(lex.diags[i].message); - i = i + 1; - } - return ""; - } - - // Phase 2: Parse - PrintLine(" Parsing..."); - let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); - if mod == null as *Module { - PrintLine("Parse failed"); - return ""; - } - PrintLine(" Parse done"); - - // Flatten module wrappers: find module decl and hoist its children - var decl: *Decl = mod.firstItem; - while decl != null as *Decl { - if decl.kind == dkModule && decl.childDecl1 != null as *Decl { - mod.firstItem = decl.childDecl1; - break; - } - decl = decl.childDecl2; - } - - // Phase 3: Semantic analysis - PrintLine(" Sema..."); - let sema: *Sema = Sema_Analyze(mod); - if Sema_HasError(sema) { - PrintLine("Sema errors:"); - var i: int = 0; - while i < Sema_DiagCount(sema) { - Print(" "); - PrintLine(sema.diags[i].message); - i = i + 1; - } - return ""; - } - PrintLine(" Sema done"); - - // Phase 4: HIR lowering - PrintLine(" HIR lowering..."); - let hirMod: *HirModule = HirLower_LowerModule(mod, sema); - if hirMod == null as *HirModule { - PrintLine("HIR lowering failed"); - return ""; - } - Print("HIR funcCount="); - PrintInt(hirMod.funcCount); - - // Phase 5: C code generation - let cCode: String = CBackend_Generate(hirMod); - - // Cleanup - Lexer_Free(lex); - Sema_Free(sema); - - return cCode; -} - -// --------------------------------------------------------------------------- -// Build command -// --------------------------------------------------------------------------- - -func Cli_Build(srcPath: String, outPath: String) -> int { - let source: String = ReadFile(srcPath); - if source == null as String || String_Eq(source, "") || !FileExists(srcPath) { - Print("Error: cannot read source file: "); - PrintLine(srcPath); - return 1; - } - - Print("Compiling "); - PrintLine(srcPath); - - let cCode: String = Cli_Compile(source, srcPath); - - if String_Eq(cCode, "") { - PrintLine("Compilation failed"); - return 1; - } - - // Write C output - let cFile: String = String_Concat(outPath, ".c"); - let ok: bool = WriteFile(cFile, cCode); - if !ok { - PrintLine("Error: cannot write output file"); - return 1; - } - Print(" → C written to "); - PrintLine(cFile); - - // Find runtime.c and io.c for linking - var rtPath: String = "library/runtime/runtime.c"; - var ioPath: String = "library/runtime/io.c"; - if !FileExists(rtPath) { - rtPath = "../library/runtime/runtime.c"; - } - if !FileExists(ioPath) { - ioPath = "../library/runtime/io.c"; - } - if !FileExists(rtPath) { - rtPath = "/home/ziko/z-git/bux/bux/library/runtime/runtime.c"; - } - if !FileExists(ioPath) { - ioPath = "/home/ziko/z-git/bux/bux/library/runtime/io.c"; - } - - // Compile with cc - PrintLine("Compiling C..."); - var cmdBuf: StringBuilder = StringBuilder_NewCap(512); - StringBuilder_Append(&cmdBuf, "cc -O2 -pthread -o "); - StringBuilder_Append(&cmdBuf, outPath); - StringBuilder_Append(&cmdBuf, " "); - StringBuilder_Append(&cmdBuf, cFile); - StringBuilder_Append(&cmdBuf, " "); - if FileExists(rtPath) { - StringBuilder_Append(&cmdBuf, rtPath); - StringBuilder_Append(&cmdBuf, " "); - } - if FileExists(ioPath) { - StringBuilder_Append(&cmdBuf, ioPath); - StringBuilder_Append(&cmdBuf, " "); - } - StringBuilder_Append(&cmdBuf, "-lm"); - let ccRc: int = bux_system(StringBuilder_Build(&cmdBuf)); - if ccRc != 0 { - PrintLine("Error: C compilation failed"); - return 1; - } - Print(" → Binary: "); - PrintLine(outPath); - return 0; -} - -// --------------------------------------------------------------------------- -// Check command (compile only, no output) -// --------------------------------------------------------------------------- - -func Cli_Check(srcPath: String) -> int { - Print("Check: "); - PrintLine(srcPath); - let source: String = ReadFile(srcPath); - if source == null as String || String_Eq(source, "") { - PrintLine("Error: cannot read source file"); - return 1; - } - PrintLine(" File read OK"); - - // Phase 1: Lex - PrintLine(" Lexing..."); - let lex: *Lexer = Lexer_Tokenize(source); - PrintLine(" Lex done"); - if Lexer_DiagCount(lex) > 0 { - PrintLine("Lex errors found"); - return 1; - } - - // Phase 2: Parse - let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); - if mod == null as *Module { - PrintLine("Parse failed"); - return 1; - } - PrintLine(" Parse done"); - - // Flatten module wrappers - var decl2: *Decl = mod.firstItem; - while decl2 != null as *Decl { - if decl2.kind == dkModule && decl2.childDecl1 != null as *Decl { - mod.firstItem = decl2.childDecl1; - break; - } - decl2 = decl2.childDecl2; - } - - // Phase 3: Sema - let sema: *Sema = Sema_Analyze(mod); - if Sema_HasError(sema) { - PrintLine("Sema errors found"); - var i: int = 0; - while i < Sema_DiagCount(sema) { - Print(" line "); - PrintInt(sema.diags[i].line as int64); - Print(": "); - PrintLine(sema.diags[i].message); - i = i + 1; - } - return 1; - } - - // Phase 4: HIR lowering - let hirMod: *HirModule = HirLower_LowerModule(mod, sema); - - var dc: *Decl = mod.firstItem; - while dc != null as *Decl { - if dc.kind == dkFunc { - Print("check func "); - PrintLine(dc.strValue); - } - dc = dc.childDecl2; - } - PrintLine("Check passed"); - return 0; -} - -// --------------------------------------------------------------------------- -// Project build — compile all .bux files in src/ directory -// --------------------------------------------------------------------------- - -func Cli_CompileSource(source: String, sourceName: String) -> *HirModule { - // Phase 1: Lex - PrintLine(" Lexing..."); - let lex: *Lexer = Lexer_Tokenize(source); - PrintLine(" Lex done"); - if Lexer_DiagCount(lex) > 0 { - Print("Lex errors in "); - PrintLine(sourceName); - return null as *HirModule; - } - - // Phase 2: Parse - let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); - if mod == null as *Module { - Print("Parse failed for "); - PrintLine(sourceName); - return null as *HirModule; - } - - // Phase 3: Semantic analysis - let sema: *Sema = Sema_Analyze(mod); - if Sema_HasError(sema) { - Print("Sema errors in "); - PrintLine(sourceName); - return null as *HirModule; - } - - // Phase 4: HIR lowering - let hirMod: *HirModule = HirLower_LowerModule(mod, sema); - return hirMod; -} - -// --------------------------------------------------------------------------- -// Stdlib discovery and merging -// --------------------------------------------------------------------------- - -func Cli_FindStdlibDir(projectDir: String) -> String { - var path: String = bux_path_join(projectDir, "stdlib"); - if DirExists(path) { return path; } - path = bux_path_join(projectDir, "../stdlib"); - if DirExists(path) { return path; } - path = "/home/ziko/z-git/bux/bux/stdlib"; - if DirExists(path) { return path; } - return ""; -} - -func Cli_DeclName(decl: *Decl) -> String { - if decl == null as *Decl { return ""; } - return decl.strValue; -} - -func Cli_NameInList(name: String, names: *String, count: int) -> bool { - if String_Eq(name, "") { return false; } - var i: int = 0; - while i < count { - if String_Eq(names[i], name) { - return true; - } - i = i + 1; - } - return false; -} - -func Cli_CollectNames(mod: *Module, names: *String, maxCount: int) -> int { - if mod == null as *Module { return 0; } - var count: int = 0; - var decl: *Decl = mod.firstItem; - while decl != null as *Decl && count < maxCount { - let next: *Decl = decl.childDecl2; - if decl.kind == dkModule { - var inner: *Decl = decl.childDecl1; - while inner != null as *Decl && count < maxCount { - let iname: String = Cli_DeclName(inner); - if !String_Eq(iname, "") { - names[count] = iname; - count = count + 1; - } - inner = inner.childDecl2; - } - } else { - let dname: String = Cli_DeclName(decl); - if !String_Eq(dname, "") { - names[count] = dname; - count = count + 1; - } - } - decl = next; - } - return count; -} - -// Helper: convert single char int to String -func String_FromChar(c: int) -> String { - var buf: *char8 = bux_alloc(2) as *char8; - buf[0] = c as char8; - buf[1] = 0 as char8; - return buf as String; -} - -// Collect stdlib module paths imported by user code. -// Returns number of unique stdlib files to merge (paths written into outPaths). -func Cli_CollectStdlibImports(mod: *Module, outPaths: *String, maxCount: int, stdlibDir: String) -> int { - if mod == null as *Module { return 0; } - var count: int = 0; - var decl: *Decl = mod.firstItem; - while decl != null as *Decl && count < maxCount { - if decl.kind == dkUse { - let path: String = decl.usePath; - // Only handle Std::* imports - if String_StartsWith(path, "Std::") { - // Extract module name after Std:: (e.g., "Std::Io" -> "Io") - var j: int = 5; // skip "Std::" - var modName: String = ""; - while j < 100 { - let c: int = path[j] as int; - if c == 0 || c == 58 { break; } // null or ':' - modName = String_Concat(modName, String_FromChar(c)); - j = j + 1; - } - if !String_Eq(modName, "") { - let filePath: String = bux_path_join(bux_path_join(stdlibDir, "Std"), String_Concat(modName, ".bux")); - // Check for duplicates - var dup: bool = false; - var di: int = 0; - while di < count { - if String_Eq(outPaths[di], filePath) { dup = true; } - di = di + 1; - } - if !dup { - outPaths[count] = filePath; - count = count + 1; - } - } - } - } - decl = decl.childDecl2; - } - return count; -} - -func Cli_MergeFileInto(target: *Module, path: String, skipNames: *String, skipCount: int) -> int { - if !FileExists(path) { - Print("Error: stdlib file not found: "); - PrintLine(path); - return 0; - } - let source: String = ReadFile(path); - if source == null as String || String_Eq(source, "") { return 0; } - let lex: *Lexer = Lexer_Tokenize(source); - if Lexer_DiagCount(lex) > 0 { return 0; } - let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); - if mod == null as *Module { return 0; } - - var added: int = 0; - var decl: *Decl = mod.firstItem; - while decl != null as *Decl { - let next: *Decl = decl.childDecl2; - decl.childDecl2 = null as *Decl; - if decl.kind == dkModule { - var inner: *Decl = decl.childDecl1; - while inner != null as *Decl { - let innerNext: *Decl = inner.childDecl2; - let iname: String = Cli_DeclName(inner); - if !String_Eq(iname, "") && Cli_NameInList(iname, skipNames, skipCount) { - // Skip shadowed stdlib decl - } else { - inner.childDecl2 = target.firstItem; - target.firstItem = inner; - target.itemCount = target.itemCount + 1; - added = added + 1; - } - inner = innerNext; - } - } else { - let dname: String = Cli_DeclName(decl); - if !String_Eq(dname, "") && Cli_NameInList(dname, skipNames, skipCount) { - // Skip shadowed stdlib decl - } else { - decl.childDecl2 = target.firstItem; - target.firstItem = decl; - target.itemCount = target.itemCount + 1; - added = added + 1; - } - } - decl = next; - } - return added; -} - -func Cli_CopyModuleDecls(target: *Module, source: *Module) { - if source == null as *Module || source.firstItem == null as *Decl { return; } - var decl: *Decl = source.firstItem; - var limit: int = 0; - while decl != null as *Decl && limit < 10000 { - let next: *Decl = decl.childDecl2; - decl.childDecl2 = target.firstItem; - target.firstItem = decl; - target.itemCount = target.itemCount + 1; - decl = next; - limit = limit + 1; - } - source.firstItem = null as *Decl; - source.itemCount = 0; -} - -func Cli_BuildProject(projectDir: String) -> int { - let man: Manifest = Manifest_Load(bux_path_join(projectDir, "bux.toml")); - var outName: String = man.name; - if String_Eq(outName, "") { - outName = "bux_out"; - } - let srcDir: String = bux_path_join(projectDir, "src"); - if !DirExists(srcDir) { - Print("Error: no src/ directory in "); - PrintLine(projectDir); - return 1; - } - - Print("Scanning "); - PrintLine(srcDir); - - // List all .bux files in src/ - var fileCount: int = 0; - let files: *String = bux_list_dir(srcDir, ".bux", &fileCount); - if fileCount == 0 { - PrintLine("Error: no .bux files found in src/"); - return 1; - } - - Print("Found "); - PrintInt(fileCount); - PrintLine(" source files"); - - // Create user merged module (collect user decls first) - let userMerged: *Module = bux_alloc(sizeof(Module)) as *Module; - userMerged.name = "main"; - userMerged.path = ""; - userMerged.itemCount = 0; - userMerged.firstItem = null as *Decl; - - // Parse each file and merge declarations into userMerged module - var i: int = 0; - while i < fileCount { - let path: String = files[i]; - Print(" Parsing "); - PrintLine(path); - let source: String = ReadFile(path); - if String_Eq(source, "") { - Print(" Error: cannot read "); - PrintLine(path); - return 1; - } - let lex: *Lexer = Lexer_Tokenize(source); - if Lexer_DiagCount(lex) > 0 { - Print(" Lex errors in "); - PrintLine(path); - return 1; - } - let mod: *Module = Parser_Parse(lex.tokens, lex.tokenCount); - if mod == null as *Module { - Print(" Parse failed for "); - PrintLine(path); - return 1; - } - // Merge declarations from this module into userMerged - var decl: *Decl = mod.firstItem; - while decl != null as *Decl { - let next: *Decl = decl.childDecl2; - decl.childDecl2 = null as *Decl; - // Flatten module declarations - if decl.kind == dkModule { - var inner: *Decl = decl.childDecl1; - while inner != null as *Decl { - let innerNext: *Decl = inner.childDecl2; - inner.childDecl2 = userMerged.firstItem; - userMerged.firstItem = inner; - userMerged.itemCount = userMerged.itemCount + 1; - inner = innerNext; - } - } else { - decl.childDecl2 = userMerged.firstItem; - userMerged.firstItem = decl; - userMerged.itemCount = userMerged.itemCount + 1; - } - decl = next; - } - i = i + 1; - } - - // Collect user declaration names for shadow detection - let maxNames: int = 2048; - let userNames: *String = bux_alloc(maxNames * 8) as *String; - let userNameCount: int = Cli_CollectNames(userMerged, userNames, maxNames); - - // Create final merged module - let merged: *Module = bux_alloc(sizeof(Module)) as *Module; - merged.name = "main"; - merged.path = ""; - merged.itemCount = 0; - merged.firstItem = null as *Decl; - - // Find and merge stdlib declarations (only imported modules) - let stdlibDir: String = Cli_FindStdlibDir(projectDir); - if !String_Eq(stdlibDir, "") { - Print("Stdlib found: "); - PrintLine(stdlibDir); - // Collect imported stdlib modules from user code - let maxImports: int = 64; - let importPaths: *String = bux_alloc(maxImports * 8) as *String; - let importCount: int = Cli_CollectStdlibImports(userMerged, importPaths, maxImports, stdlibDir); - Print("Imported stdlib modules: "); - PrintInt(importCount); - PrintLine(""); - if importCount > 0 { - var si: int = 0; - var stdAdded: int = 0; - while si < importCount { - Print(" Merging "); - PrintLine(importPaths[si]); - let added: int = Cli_MergeFileInto(merged, importPaths[si], userNames, userNameCount); - stdAdded = stdAdded + added; - si = si + 1; - } - Print("Stdlib declarations added: "); - PrintInt(stdAdded); - } - } - - // Copy user declarations into merged (user shadows stdlib) - Cli_CopyModuleDecls(merged, userMerged); - - Print("Merged "); - PrintInt(merged.itemCount); - PrintLine(" declarations"); - - // Semantic analysis - PrintLine("Running sema..."); - let sema: *Sema = Sema_Analyze(merged); - if Sema_HasError(sema) { - PrintLine("Sema errors found"); - var i: int = 0; - while i < Sema_DiagCount(sema) { - Print(" line "); - PrintInt(sema.diags[i].line as int64); - Print(": "); - PrintLine(sema.diags[i].message); - i = i + 1; - } - return 1; - } - - // HIR lowering - PrintLine("Lowering to HIR..."); - let hirMod: *HirModule = HirLower_LowerModule(merged, sema); - - // C code generation - PrintLine("Generating C code..."); - let cCode: String = CBackend_Generate(hirMod); - - // Create build directory - let buildDir: String = bux_path_join(projectDir, "build"); - discard bux_mkdir_if_needed(buildDir); - - // Write C output - let cFile: String = bux_path_join(buildDir, "main.c"); - if !WriteFile(cFile, cCode) { - PrintLine("Error: cannot write main.c"); - return 1; - } - Print("C code written to "); - PrintLine(cFile); - - // Find runtime.c and io.c (use stdlibDir if available) - var rtPath: String = bux_path_join(stdlibDir, "runtime.c"); - var ioPath: String = bux_path_join(stdlibDir, "io.c"); - if !FileExists(rtPath) { - rtPath = bux_path_join(projectDir, "library/runtime/runtime.c"); - } - if !FileExists(ioPath) { - ioPath = bux_path_join(projectDir, "library/runtime/io.c"); - } - if !FileExists(rtPath) { - rtPath = bux_path_join(projectDir, "../library/runtime/runtime.c"); - } - if !FileExists(ioPath) { - ioPath = bux_path_join(projectDir, "../library/runtime/io.c"); - } - - // Compile with cc - PrintLine("Compiling C..."); - let outBin: String = bux_path_join(buildDir, outName); - var ccBuf: StringBuilder = StringBuilder_NewCap(512); - StringBuilder_Append(&ccBuf, "cc -O2 -pthread -o "); - StringBuilder_Append(&ccBuf, outBin); - StringBuilder_Append(&ccBuf, " "); - StringBuilder_Append(&ccBuf, cFile); - StringBuilder_Append(&ccBuf, " "); - if FileExists(rtPath) { - StringBuilder_Append(&ccBuf, rtPath); - StringBuilder_Append(&ccBuf, " "); - } - if FileExists(ioPath) { - StringBuilder_Append(&ccBuf, ioPath); - StringBuilder_Append(&ccBuf, " "); - } - StringBuilder_Append(&ccBuf, "-lm"); - let ccRc: int = bux_system(StringBuilder_Build(&ccBuf)); - if ccRc != 0 { - PrintLine("Error: C compilation failed"); - return 1; - } - - Print("Build successful: "); - PrintLine(outBin); - return 0; -} - -// --------------------------------------------------------------------------- -// Run command — build project and execute -// --------------------------------------------------------------------------- - -func Cli_RunProject(projectDir: String) -> int { - let rc: int = Cli_BuildProject(projectDir); - if rc != 0 { - return rc; - } - let man: Manifest = Manifest_Load(bux_path_join(projectDir, "bux.toml")); - var outName: String = man.name; - if String_Eq(outName, "") { - outName = "bux_out"; - } - let outBin: String = bux_path_join(bux_path_join(projectDir, "build"), outName); - Print("Running: "); - PrintLine(outBin); - return bux_system(outBin); -} - -// --------------------------------------------------------------------------- -// Main entry — dispatch based on args -// --------------------------------------------------------------------------- - -func Cli_Run(args: *String, argCount: int) -> int { - if argCount < 2 { - PrintLine("Bux Self-Hosting Compiler v0.2.0"); - PrintLine("Usage: buxc [args]"); - PrintLine("Commands: build, check, run, project, help, version"); - return 0; - } - - let cmd: String = args[1]; - if String_Eq(cmd, "version") || String_Eq(cmd, "--version") || String_Eq(cmd, "-v") { - PrintLine("Bux 0.2.0 (self-hosting bootstrap)"); - return 0; - } - - if String_Eq(cmd, "help") || String_Eq(cmd, "--help") || String_Eq(cmd, "-h") { - PrintLine("Bux Self-Hosting Compiler v0.2.0"); - PrintLine("Usage: buxc [args]"); - PrintLine("Commands: build, check, run, project, help, version"); - PrintLine("Pipeline modules:"); - PrintLine(" Lexer ✅ 695 lines"); - PrintLine(" Parser ✅ 1004 lines"); - PrintLine(" Sema ✅ 393 lines"); - PrintLine(" HirLower ✅ 307 lines"); - PrintLine(" CBackend ✅ 585 lines"); - PrintLine(" Total: 3830 lines of Bux"); - return 0; - } - - if String_Eq(cmd, "check") { - if argCount < 3 { - PrintLine("Usage: buxc check "); - return 1; - } - return Cli_Check(args[2]); - } - - if String_Eq(cmd, "build") { - let src: String = "src/Main.bux"; - let out: String = "build/main"; - if argCount >= 3 { src = args[2]; } - if argCount >= 4 { out = args[3]; } - return Cli_Build(src, out); - } - - if String_Eq(cmd, "project") { - let dir: String = "."; - if argCount >= 3 { dir = args[2]; } - return Cli_BuildProject(dir); - } - - if String_Eq(cmd, "run") { - let dir: String = "."; - if argCount >= 3 { dir = args[2]; } - return Cli_RunProject(dir); - } - - Print("Unknown command: "); - PrintLine(cmd); - return 1; -} -} diff --git a/_selfhost/src/hir.bux b/_selfhost/src/hir.bux deleted file mode 100644 index 2f37e06..0000000 --- a/_selfhost/src/hir.bux +++ /dev/null @@ -1,229 +0,0 @@ -// hir.bux — HIR (High-level Intermediate Representation) node types -module Hir { - -// HIR node kinds -const hLit: int = 0; -const hVar: int = 1; -const hSelf: int = 2; -const hUnary: int = 3; -const hBinary: int = 4; -const hAssign: int = 5; -const hIf: int = 6; -const hWhile: int = 7; -const hLoop: int = 8; -const hBreak: int = 9; -const hContinue: int = 10; -const hReturn: int = 11; -const hAlloca: int = 12; -const hLoad: int = 13; -const hStore: int = 14; -const hFieldPtr: int = 15; -const hArrowField: int = 16; -const hIndexPtr: int = 17; -const hCall: int = 18; -const hCallIndirect: int = 19; -const hCast: int = 20; -const hIs: int = 21; -const hSizeOf: int = 22; -const hBlock: int = 23; -const hStructInit: int = 24; -const hSliceInit: int = 25; -const hRange: int = 26; -const hTupleInit: int = 27; -const hMatch: int = 28; -const hSpawn: int = 29; -const hAwait: int = 30; - -// --------------------------------------------------------------------------- -// HirArgList — linked list for call arguments beyond 2 -// --------------------------------------------------------------------------- - -struct HirArgList { - node: *HirNode, - next: *HirArgList, -} - -// --------------------------------------------------------------------------- -// HirNode — unified struct with tagged union pattern -// --------------------------------------------------------------------------- - -struct HirNode { - kind: int; - line: uint32; - column: uint32; - typeKind: int; - typeName: String; - // Common fields (used by multiple kinds) - strValue: String; // var name; callee name; field name; label - intValue: int; // token kind (for lit; unary op; binary op) - boolValue: bool; // range inclusive; isScope - // Child nodes (up to 3) - child1: *HirNode; // left/operand/condition/base - child2: *HirNode; // right/value/then/body - child3: *HirNode; // else/third - // Extra data pointer (for children arrays, field lists, etc.) - extraData: *void; - extraCount: int; -} - -// --------------------------------------------------------------------------- -// HirFunc -// --------------------------------------------------------------------------- - -struct HirParam { - name: String; - typeKind: int; - typeName: String; -} - -struct HirFunc { - name: String; - paramCount: int; - param0: HirParam; - param1: HirParam; - param2: HirParam; - param3: HirParam; - param4: HirParam; - param5: HirParam; - param6: HirParam; - param7: HirParam; - param8: HirParam; - retTypeKind: int; - retTypeName: String; - body: *HirNode; - isPublic: bool; -} - -// --------------------------------------------------------------------------- -// HirEnumVariant -// --------------------------------------------------------------------------- - -struct HirEnumVariant { - name: String; - fieldCount: int; - fieldType0: int; - fieldName0: String; - fieldType1: int; - fieldName1: String; -} - -// --------------------------------------------------------------------------- -// HirModule -// --------------------------------------------------------------------------- - -struct HirStructField { - name: String; - typeName: String; -} - -struct HirStruct { - name: String; - fieldCount: int; - fields: *HirStructField; -} - -struct HirConst { - name: String; - value: int; -} - -struct HirEnum { - name: String; -} - -struct HirModule { - funcCount: int; - funcs: *HirFunc; - externCount: int; - externFuncs: *HirFunc; - structCount: int; - structs: *HirStruct; - enumCount: int; - enums: *HirEnum; - constCount: int; - consts: *HirConst; -} - -// --------------------------------------------------------------------------- -// Constructor helpers -// --------------------------------------------------------------------------- - -func Hir_MakeNode(kind: int, line: uint32, column: uint32) -> HirNode { - return HirNode { kind: kind, line: line, column: column, - typeKind: 0, typeName: "", - strValue: "", intValue: 0, boolValue: false, - child1: null as *HirNode, child2: null as *HirNode, child3: null as *HirNode, - extraData: null as *void, extraCount: 0 }; -} - -func Hir_MakeLit(tokKind: int, tokText: String, line: uint32, col: uint32) -> HirNode { - var n: HirNode = Hir_MakeNode(hLit, line, col); - n.intValue = tokKind; - n.strValue = tokText; - return n; -} - -func Hir_MakeVar(name: String, line: uint32, col: uint32) -> HirNode { - var n: HirNode = Hir_MakeNode(hVar, line, col); - n.strValue = name; - return n; -} - -func Hir_MakeBinary(op: int, left: *HirNode, right: *HirNode, line: uint32, col: uint32) -> HirNode { - var n: HirNode = Hir_MakeNode(hBinary, line, col); - n.intValue = op; - n.child1 = left; - n.child2 = right; - return n; -} - -func Hir_MakeCall(callee: String, line: uint32, col: uint32) -> HirNode { - var n: HirNode = Hir_MakeNode(hCall, line, col); - n.strValue = callee; - return n; -} - -func Hir_MakeReturn(value: *HirNode, line: uint32, col: uint32) -> HirNode { - var n: HirNode = Hir_MakeNode(hReturn, line, col); - n.child1 = value; - return n; -} - -func Hir_MakeBlock(line: uint32, col: uint32) -> HirNode { - return Hir_MakeNode(hBlock, line, col); -} - -func Hir_MakeIf(cond: *HirNode, thenBody: *HirNode, elseBody: *HirNode, line: uint32, col: uint32) -> HirNode { - var n: HirNode = Hir_MakeNode(hIf, line, col); - n.child1 = cond; - n.child2 = thenBody; - n.child3 = elseBody; - return n; -} - -func Hir_MakeWhile(cond: *HirNode, body: *HirNode, line: uint32, col: uint32) -> HirNode { - var n: HirNode = Hir_MakeNode(hWhile, line, col); - n.child1 = cond; - n.child2 = body; - return n; -} - -func Hir_MakeAlloca(name: String, line: uint32, col: uint32) -> HirNode { - var n: HirNode = Hir_MakeNode(hAlloca, line, col); - n.strValue = name; - return n; -} - -func Hir_MakeLoad(ptr: *HirNode, line: uint32, col: uint32) -> HirNode { - var n: HirNode = Hir_MakeNode(hLoad, line, col); - n.child1 = ptr; - return n; -} - -func Hir_MakeStore(ptr: *HirNode, value: *HirNode, line: uint32, col: uint32) -> HirNode { - var n: HirNode = Hir_MakeNode(hStore, line, col); - n.child1 = ptr; - n.child2 = value; - return n; -} -} diff --git a/_selfhost/src/hir_lower.bux b/_selfhost/src/hir_lower.bux deleted file mode 100644 index d783a37..0000000 --- a/_selfhost/src/hir_lower.bux +++ /dev/null @@ -1,740 +0,0 @@ -// hir_lower.bux — HIR lowering: AST → HIR transformation (ported from hir_lower.nim) -// Transforms the typed AST into a lower-level IR suitable for code generation. -module HirLower { - -// --------------------------------------------------------------------------- -// Lowering context -// --------------------------------------------------------------------------- -struct LowerCtx { - module: *Module, - scope: *Scope, - funcs: *HirFunc, - funcCount: int, - externFuncs: *HirFunc, - externCount: int, - varCounter: int, - tryCounter: int, -} - -// --------------------------------------------------------------------------- -// TypeExpr.kind → Type.kind resolver -// TypeExpr.kind values (0-5) overlap with Type.kind values — this -// resolves the correct Type.kind for codegen. -// --------------------------------------------------------------------------- - -func Lcx_ResolveTypeKind(te: *TypeExpr) -> int { - if te == null as *TypeExpr { return tyUnknown; } - let name: String = te.typeName; - - if te.kind == tekPointer { return tyPointer; } - if te.kind == tekSlice { return tySlice; } - if te.kind == tekTuple { return tyTuple; } - - // Named types: resolve by name - if String_Eq(name, "void") { return tyVoid; } - if String_Eq(name, "bool") { return tyBool; } - if String_Eq(name, "bool8") { return tyBool8; } - if String_Eq(name, "bool16") { return tyBool16; } - if String_Eq(name, "bool32") { return tyBool32; } - if String_Eq(name, "char8") { return tyChar8; } - if String_Eq(name, "char16") { return tyChar16; } - if String_Eq(name, "char32") { return tyChar32; } - if String_Eq(name, "String") { return tyStr; } - if String_Eq(name, "str") { return tyStr; } - if String_Eq(name, "int8") { return tyInt8; } - if String_Eq(name, "int16") { return tyInt16; } - if String_Eq(name, "int32") { return tyInt32; } - if String_Eq(name, "int64") { return tyInt64; } - if String_Eq(name, "int") { return tyInt; } - if String_Eq(name, "uint8") { return tyUInt8; } - if String_Eq(name, "uint16") { return tyUInt16; } - if String_Eq(name, "uint32") { return tyUInt32; } - if String_Eq(name, "uint64") { return tyUInt64; } - if String_Eq(name, "uint") { return tyUInt; } - if String_Eq(name, "float32") { return tyFloat32; } - if String_Eq(name, "float64") { return tyFloat64; } - if String_Eq(name, "float") { return tyFloat64; } - - return tyNamed; -} - -// --------------------------------------------------------------------------- -// Fresh name generation -// --------------------------------------------------------------------------- - -func Lcx_FreshName(ctx: *LowerCtx) -> String { - ctx.varCounter = ctx.varCounter + 1; - return String_FromInt(ctx.varCounter as int64); -} - -// --------------------------------------------------------------------------- -// Type → HIR type -// --------------------------------------------------------------------------- - -func Lcx_LowerType(typeKind: int, typeName: String) -> int { - return typeKind; -} - -// --------------------------------------------------------------------------- -// Expression lowering -// --------------------------------------------------------------------------- - -func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode { - if expr == null as *Expr { return null as *HirNode; } - - let line: uint32 = expr.line; - let col: uint32 = expr.column; - let n: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; - n.kind = hBlock; - n.line = line; - n.column = col; - - let kind: int = expr.kind; - - // Literal - if kind == ekLiteral { - n.kind = hLit; - n.intValue = expr.tokKind; - n.strValue = expr.tokText; - return n; - } - - // Identifier → variable reference - if kind == ekIdent { - n.kind = hVar; - n.strValue = expr.strValue; - let sym: Symbol = Scope_Lookup(ctx.scope, expr.strValue); - n.typeKind = sym.typeKind; - if expr.refType != null as *TypeExpr { - n.typeName = expr.refType.typeName; - } - if sym.typeName != null as String && !String_Eq(sym.typeName, "") { - n.typeName = sym.typeName; - } - return n; - } - - // self → variable reference named "self" - if kind == ekSelf { - n.kind = hVar; - n.strValue = "self"; - let sym: Symbol = Scope_Lookup(ctx.scope, "self"); - n.typeKind = sym.typeKind; - if sym.typeName != null as String && !String_Eq(sym.typeName, "") { - n.typeName = sym.typeName; - } - return n; - } - - // Binary - if kind == ekBinary { - // Assignment operator → use hAssign - if expr.intValue == tkAssign { - n.kind = hAssign; - n.child1 = Lcx_LowerExpr(ctx, expr.child1); - n.child2 = Lcx_LowerExpr(ctx, expr.child2); - return n; - } - n.kind = hBinary; - n.intValue = expr.intValue; // operator - n.child1 = Lcx_LowerExpr(ctx, expr.child1); - n.child2 = Lcx_LowerExpr(ctx, expr.child2); - return n; - } - - // Unary - if kind == ekUnary { - n.kind = hUnary; - n.intValue = expr.intValue; - n.child1 = Lcx_LowerExpr(ctx, expr.child1); - return n; - } - - // Call - if kind == ekCall { - n.kind = hCall; - // Method call desugaring: obj.method(args) → Type_method(obj, args) - if expr.child1 != null as *Expr && expr.child1.kind == ekField { - let methodName: String = expr.child1.strValue; - var receiverTypeName: String = ""; - if expr.child1.child1 != null as *Expr && expr.child1.child1.kind == ekIdent { - let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.child1.strValue); - receiverTypeName = sym.typeName; - } - if String_Eq(receiverTypeName, "") && expr.child1.child1 != null as *Expr && expr.child1.child1.refType != null as *TypeExpr { - receiverTypeName = expr.child1.child1.refType.typeName; - } - if !String_Eq(receiverTypeName, "") { - // Strip trailing '*' from pointer type names (e.g. "Box*" → "Box") - var baseName: String = receiverTypeName; - let len: int = bux_strlen(baseName) as int; - if len > 0 { - let lastChar: String = bux_str_slice(baseName, (len - 1) as uint, 1); - if String_Eq(lastChar, "*") { - baseName = bux_str_slice(baseName, 0, (len - 1) as uint); - } - } - n.strValue = String_Concat(baseName, "_"); - n.strValue = String_Concat(n.strValue, methodName); - } - // Lower receiver as first argument - let recv: *HirNode = Lcx_LowerExpr(ctx, expr.child1.child1); - n.child1 = recv; - // Lower remaining arguments from linked list - var arg: *ExprList = expr.callArgs; - var argIdx: int = 0; - while arg != null as *ExprList { - let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr); - if argIdx == 0 { - n.child2 = lowered; - } else if argIdx == 1 { - // Third argument — start linked list - let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; - firstExtra.node = lowered; - firstExtra.next = null as *HirArgList; - n.extraData = firstExtra as *void; - n.extraCount = 1; - } else { - // Additional args — append to linked list - var cur: *HirArgList = n.extraData as *HirArgList; - while cur.next != null as *HirArgList { - cur = cur.next; - } - let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; - newNode.node = lowered; - newNode.next = null as *HirArgList; - cur.next = newNode; - n.extraCount = n.extraCount + 1; - } - arg = arg.next; - argIdx = argIdx + 1; - } - return n; - } - // Get callee name - if expr.child1 != null as *Expr && expr.child1.kind == ekIdent { - n.strValue = expr.child1.strValue; - } - // Lower arguments from linked list - var arg: *ExprList = expr.callArgs; - var argIdx: int = 0; - while arg != null as *ExprList { - let lowered: *HirNode = Lcx_LowerExpr(ctx, arg.expr); - if argIdx == 0 { - n.child1 = lowered; - } else if argIdx == 1 { - n.child2 = lowered; - } else if argIdx == 2 { - // Third argument — start linked list - let firstExtra: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; - firstExtra.node = lowered; - firstExtra.next = null as *HirArgList; - n.extraData = firstExtra as *void; - n.extraCount = 1; - } else { - // Additional args — append to linked list - var cur: *HirArgList = n.extraData as *HirArgList; - while cur.next != null as *HirArgList { - cur = cur.next; - } - let newNode: *HirArgList = bux_alloc(sizeof(HirArgList)) as *HirArgList; - newNode.node = lowered; - newNode.next = null as *HirArgList; - cur.next = newNode; - n.extraCount = n.extraCount + 1; - } - arg = arg.next; - argIdx = argIdx + 1; - } - return n; - } - - // Sizeof - if kind == ekSizeOf { - n.kind = hSizeOf; - if expr.refType != null as *TypeExpr { - n.typeName = expr.refType.typeName; - } - return n; - } - - // Field access - if kind == ekField { - // Check if this is enum variant access: Color::Green - if expr.child1 != null as *Expr && expr.child1.kind == ekIdent { - let sym: Symbol = Scope_Lookup(ctx.scope, expr.child1.strValue); - if sym.decl != null as *Decl && sym.decl.kind == dkEnum { - // Emit as variable reference: Color_Green - n.kind = hVar; - n.strValue = String_Concat(String_Concat(expr.child1.strValue, "_"), expr.strValue); - return n; - } - } - n.kind = hFieldPtr; - n.child1 = Lcx_LowerExpr(ctx, expr.child1); - n.strValue = expr.strValue; - // Get struct type from base expr refType - if expr.child1 != null as *Expr && expr.child1.refType != null as *TypeExpr { - n.typeName = expr.child1.refType.typeName; - } - return n; - } - - // spawn Callee(args) - if kind == ekSpawn { - n.kind = hSpawn; - if expr.child1 != null as *Expr && expr.child1.kind == ekIdent { - n.strValue = expr.child1.strValue; - } - if expr.child2 != null as *Expr { - n.child1 = Lcx_LowerExpr(ctx, expr.child2); - } - return n; - } - - // expr.await - if kind == ekAwait { - n.kind = hAwait; - n.child1 = Lcx_LowerExpr(ctx, expr.child1); - return n; - } - - // Index: arr[idx] - if kind == ekIndex { - n.kind = hIndexPtr; - n.child1 = Lcx_LowerExpr(ctx, expr.child1); - n.child2 = Lcx_LowerExpr(ctx, expr.child2); - return n; - } - - // Assign: target = value - if kind == ekAssign { - n.kind = hAssign; - n.child1 = Lcx_LowerExpr(ctx, expr.child1); // target - n.child2 = Lcx_LowerExpr(ctx, expr.child2); // value - return n; - } - - // Cast - if kind == ekCast { - n.kind = hCast; - n.child1 = Lcx_LowerExpr(ctx, expr.child1); - if expr.refType != null as *TypeExpr { - let resolvedKind: int = Lcx_ResolveTypeKind(expr.refType); - n.typeKind = resolvedKind; - // For pointer types, construct "PointeeType*" - if expr.refType.kind == tekPointer && expr.refType.pointerPointee != null as *TypeExpr { - n.typeName = String_Concat(expr.refType.pointerPointee.typeName, "*"); - } else if !String_Eq(expr.refType.typeName, "") { - n.typeName = expr.refType.typeName; - } - } - return n; - } - - // Struct init: TypeName { field: value, ... } - if kind == ekStructInit { - n.kind = hStructInit; - n.strValue = expr.structName; - // Lower each field (fields are chained via child3 on synthetic ekField exprs) - var field: *Expr = expr.child1; - var firstField: *HirNode = null as *HirNode; - var lastField: *HirNode = null as *HirNode; - while field != null as *Expr { - let fNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; - fNode.kind = hBlock; // placeholder, field is identified by name+value - fNode.line = expr.line; - fNode.column = expr.column; - fNode.strValue = field.strValue; // field name - fNode.child1 = Lcx_LowerExpr(ctx, field.child1); // field value - if firstField == null as *HirNode { - firstField = fNode; - lastField = fNode; - } else { - lastField.child3 = fNode; - lastField = fNode; - } - field = field.child3; - } - n.child1 = firstField; - return n; - } - - return n; -} - -// --------------------------------------------------------------------------- -// Statement lowering -// --------------------------------------------------------------------------- - -func Lcx_LowerStmt(ctx: *LowerCtx, stmt: *Stmt) -> *HirNode { - if stmt == null as *Stmt { return null as *HirNode; } - - let line: uint32 = stmt.line; - let col: uint32 = stmt.column; - let n: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; - n.kind = hBlock; - n.line = line; - n.column = col; - - let kind: int = stmt.kind; - - // Let/var → alloca + store - if kind == skLet { - let init: *HirNode = Lcx_LowerExpr(ctx, stmt.child1); - // alloca for the variable - let alloca: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; - alloca.kind = hAlloca; - alloca.line = line; - alloca.column = col; - alloca.strValue = stmt.strValue; - // Set type from the declared type expression - alloca.typeName = ""; - if stmt.refStmtType != null as *TypeExpr { - alloca.intValue = stmt.refStmtType.kind; - alloca.typeKind = Lcx_ResolveTypeKind(stmt.refStmtType); - // For pointer types, construct "PointeeType*" - if stmt.refStmtType.kind == tekPointer && stmt.refStmtType.pointerPointee != null as *TypeExpr { - alloca.typeName = String_Concat(stmt.refStmtType.pointerPointee.typeName, "*"); - } else if !String_Eq(stmt.refStmtType.typeName, "") { - alloca.typeName = stmt.refStmtType.typeName; - } - } - // Add to scope for field offset lookups - var sym: Symbol; - sym.kind = skVar; - sym.name = stmt.strValue; - sym.typeKind = alloca.typeKind; - sym.typeName = alloca.typeName; - sym.isMutable = false; - sym.isPublic = false; - sym.decl = null as *Decl; - discard Scope_Define(ctx.scope, sym); - // store the init value - n.kind = hStore; - n.child1 = alloca; - n.child2 = init; - return n; - } - - // Return - if kind == skReturn { - n.kind = hReturn; - if stmt.child1 != null as *Expr { - n.child1 = Lcx_LowerExpr(ctx, stmt.child1); - } - return n; - } - - // Expression statement - if kind == skExpr && stmt.child1 != null as *Expr { - return Lcx_LowerExpr(ctx, stmt.child1); - } - - // If - if kind == skIf { - n.kind = hIf; - n.child1 = Lcx_LowerExpr(ctx, stmt.child1); // condition - if stmt.refStmtBlock != null as *Block { - n.child2 = Lcx_LowerBlock(ctx, stmt.refStmtBlock, -1); - } - if stmt.refStmtElse != null as *Block { - // Store else block in extraData (child3 is reserved for block chaining) - n.extraData = Lcx_LowerBlock(ctx, stmt.refStmtElse, -1) as *void; - } - return n; - } - - // While - if kind == skWhile { - n.kind = hWhile; - n.child1 = Lcx_LowerExpr(ctx, stmt.child1); - if stmt.refStmtBlock != null as *Block { - n.child2 = Lcx_LowerBlock(ctx, stmt.refStmtBlock, -1); - } - return n; - } - - // Loop - if kind == skLoop { - n.kind = hLoop; - if stmt.refStmtBlock != null as *Block { - n.child1 = Lcx_LowerBlock(ctx, stmt.refStmtBlock, -1); - } - return n; - } - - return n; -} - -// --------------------------------------------------------------------------- -// Block lowering -// --------------------------------------------------------------------------- - -func Lcx_LowerBlock(ctx: *LowerCtx, block: *Block, retTypeKind: int) -> *HirNode { - if block == null as *Block { return null as *HirNode; } - if block.stmtCount == 0 { return null as *HirNode; } - - // Build a linked list of HirNodes via child3: - // node1 (stmt1) → child3 → node2 (stmt2) → child3 → node3 (stmt3) → null - // child3 is safe for chaining because: - // hStore: child1=alloca, child2=value, child3 unused - // hReturn: child1=value, child2/child3 unused - // hCall: child1=arg1, child2=arg2, child3 unused - var firstNode: *HirNode = null as *HirNode; - var prevNode: *HirNode = null as *HirNode; - var stmt: *Stmt = block.firstStmt; - while stmt != null as *Stmt { - let lowered: *HirNode = Lcx_LowerStmt(ctx, stmt); - if lowered != null as *HirNode { - if firstNode == null as *HirNode { - firstNode = lowered; - prevNode = lowered; - } else { - prevNode.child3 = lowered; - prevNode = lowered; - } - } - stmt = stmt.nextStmt; - } - - // Wrap in an hBlock node with child1 = first statement in chain - let n: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; - n.kind = hBlock; - n.line = block.line; - n.column = block.column; - n.boolValue = true; - n.child1 = firstNode; - return n; -} - -// --------------------------------------------------------------------------- -// Param → HirParam conversion -// --------------------------------------------------------------------------- - -func Lcx_LowerParam(out: *HirParam, p: *Param) { - out.name = p.name; - if p.refParamType != null as *TypeExpr { - out.typeKind = Lcx_ResolveTypeKind(p.refParamType); - // Use typeName directly if set (parser may have constructed "String*") - if !String_Eq(p.refParamType.typeName, "") { - out.typeName = p.refParamType.typeName; - } else if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr { - out.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*"); - } else { - out.typeName = ""; - } - } else { - out.typeKind = 0; - out.typeName = ""; - } -} - -// --------------------------------------------------------------------------- -// Function lowering -// --------------------------------------------------------------------------- - -func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc { - let f: *HirFunc = bux_alloc(sizeof(HirFunc)) as *HirFunc; - f.name = decl.strValue; - f.isPublic = decl.isPublic; - f.paramCount = decl.paramCount; - Lcx_LowerParam(&f.param0, &decl.param0); - Lcx_LowerParam(&f.param1, &decl.param1); - Lcx_LowerParam(&f.param2, &decl.param2); - Lcx_LowerParam(&f.param3, &decl.param3); - Lcx_LowerParam(&f.param4, &decl.param4); - Lcx_LowerParam(&f.param5, &decl.param5); - Lcx_LowerParam(&f.param6, &decl.param6); - Lcx_LowerParam(&f.param7, &decl.param7); - Lcx_LowerParam(&f.param8, &decl.param8); - - if decl.retType != null as *TypeExpr { - f.retTypeName = decl.retType.typeName; - f.retTypeKind = Lcx_ResolveTypeKind(decl.retType); - } else { - f.retTypeName = ""; - f.retTypeKind = 0; - } - - // Add parameters to hir_lower scope for field offset lookups - var pi: int = 0; - while pi < decl.paramCount { - var p: *Param = null as *Param; - if pi == 0 { p = &decl.param0; } - else if pi == 1 { p = &decl.param1; } - else if pi == 2 { p = &decl.param2; } - else if pi == 3 { p = &decl.param3; } - else if pi == 4 { p = &decl.param4; } - else if pi == 5 { p = &decl.param5; } - else if pi == 6 { p = &decl.param6; } - else if pi == 7 { p = &decl.param7; } - else if pi == 8 { p = &decl.param8; } - if p != null as *Param && p.refParamType != null as *TypeExpr { - var sym: Symbol; - sym.kind = skVar; - sym.name = p.name; - sym.typeKind = Lcx_ResolveTypeKind(p.refParamType); - sym.typeName = p.refParamType.typeName; - sym.isMutable = false; - sym.isPublic = false; - sym.decl = null as *Decl; - discard Scope_Define(ctx.scope, sym); - } - pi = pi + 1; - } - - if decl.refBody != null as *Block { - f.body = Lcx_LowerBlock(ctx, decl.refBody, -1); - } else { - f.body = null as *HirNode; - } - - return f; -} - -// --------------------------------------------------------------------------- -// Module lowering — main entry point -// --------------------------------------------------------------------------- - -func HirLower_LowerModule(mod: *Module, sema: *Sema) -> *HirModule { - let ctx: *LowerCtx = bux_alloc(sizeof(LowerCtx)) as *LowerCtx; - ctx.module = mod; - ctx.scope = sema.scope; - ctx.funcs = bux_alloc(256 as uint * sizeof(HirFunc)) as *HirFunc; - ctx.funcCount = 0; - ctx.externFuncs = bux_alloc(256 as uint * sizeof(HirFunc)) as *HirFunc; - ctx.externCount = 0; - ctx.varCounter = 0; - - let hm: *HirModule = bux_alloc(sizeof(HirModule)) as *HirModule; - hm.funcCount = 0; - hm.funcs = ctx.funcs; - hm.structCount = 0; - hm.structs = bux_alloc(64 as uint * sizeof(HirStruct)) as *HirStruct; - hm.enumCount = 0; - hm.enums = bux_alloc(64 as uint * sizeof(HirEnum)) as *HirEnum; - hm.constCount = 0; - hm.consts = bux_alloc(512 as uint * sizeof(HirConst)) as *HirConst; - - // First pass: count structs (to allocate field arrays later) - // Second pass: actually collect them - // For simplicity, do single pass with pre-allocated field arrays - - // Iterate declarations - var decl: *Decl = mod.firstItem; - while decl != null as *Decl { - if decl.kind == dkFunc { - let f: *HirFunc = Lcx_LowerFunc(ctx, decl); - ctx.funcs[ctx.funcCount] = *f; - ctx.funcCount = ctx.funcCount + 1; - } - if decl.kind == dkImpl { - let implTypeName: String = decl.strValue; - var implDecl: *Decl = decl.childDecl1; - while implDecl != null as *Decl { - if implDecl.kind == dkFunc { - let mangled: String = String_Concat(implTypeName, "_"); - implDecl.strValue = String_Concat(mangled, implDecl.strValue); - let f: *HirFunc = Lcx_LowerFunc(ctx, implDecl); - ctx.funcs[ctx.funcCount] = *f; - ctx.funcCount = ctx.funcCount + 1; - } - implDecl = implDecl.childDecl2; - } - } - if decl.kind == dkExternFunc { - let f: *HirFunc = Lcx_LowerFunc(ctx, decl); - ctx.externFuncs[ctx.externCount] = *f; - ctx.externCount = ctx.externCount + 1; - } - if decl.kind == dkStruct { - // Collect struct definition for C codegen - let si: int = hm.structCount; - hm.structs[si].name = decl.strValue; - hm.structs[si].fieldCount = decl.fieldCount; - hm.structs[si].fields = bux_alloc(decl.fieldCount as uint * sizeof(HirStructField)) as *HirStructField; - var fi: int = 0; - while fi < decl.fieldCount { - var fname: String = ""; - var ftype: *TypeExpr = null as *TypeExpr; - if fi == 0 { fname = decl.field0.name; ftype = decl.field0.refFieldType; } - else if fi == 1 { fname = decl.field1.name; ftype = decl.field1.refFieldType; } - else if fi == 2 { fname = decl.field2.name; ftype = decl.field2.refFieldType; } - else if fi == 3 { fname = decl.field3.name; ftype = decl.field3.refFieldType; } - else if fi == 4 { fname = decl.field4.name; ftype = decl.field4.refFieldType; } - else if fi == 5 { fname = decl.field5.name; ftype = decl.field5.refFieldType; } - else if fi == 6 { fname = decl.field6.name; ftype = decl.field6.refFieldType; } - else if fi == 7 { fname = decl.field7.name; ftype = decl.field7.refFieldType; } - // Skip empty field names - if String_Eq(fname, "") { - fi = fi + 1; - continue; - } - hm.structs[si].fields[fi].name = fname; - if ftype != null as *TypeExpr { - if ftype.kind == tekPointer && ftype.pointerPointee != null as *TypeExpr { - // Pointer type: emit "TypeName*" - if !String_Eq(ftype.pointerPointee.typeName, "") { - hm.structs[si].fields[fi].typeName = String_Concat(ftype.pointerPointee.typeName, "*"); - } - } else if !String_Eq(ftype.typeName, "") { - hm.structs[si].fields[fi].typeName = ftype.typeName; - } - } - fi = fi + 1; - } - hm.structCount = hm.structCount + 1; - } - if decl.kind == dkConst && hm.constCount < 512 { - let ci: int = hm.constCount; - hm.consts[ci].name = decl.strValue; - var val: int = 0; - if decl.constValue != null as *Expr { - if decl.constValue.kind == ekLiteral { - val = decl.constValue.intValue; - } - } - hm.consts[ci].value = val; - hm.constCount = hm.constCount + 1; - } - if decl.kind == dkEnum { - let ei: int = hm.enumCount; - hm.enums[ei].name = decl.strValue; - hm.enumCount = hm.enumCount + 1; - // Emit enum variants as constants: EnumName_VariantName = value - var vi: int = 0; - while vi < decl.variantCount && hm.constCount < 512 { - var vname: String = ""; - if vi == 0 { vname = decl.variant0.name; } - if vi == 1 { vname = decl.variant1.name; } - if vi == 2 { vname = decl.variant2.name; } - if vi == 3 { vname = decl.variant3.name; } - if !String_Eq(vname, "") { - let ci: int = hm.constCount; - hm.consts[ci].name = String_Concat(String_Concat(decl.strValue, "_"), vname); - hm.consts[ci].value = vi; - hm.constCount = hm.constCount + 1; - } - vi = vi + 1; - } - } - decl = decl.childDecl2; - } - - - hm.funcCount = ctx.funcCount; - hm.funcs = ctx.funcs; - hm.externCount = ctx.externCount; - hm.externFuncs = ctx.externFuncs; - - - return hm; -} - -func HirLower_Free(ctx: *LowerCtx) { - bux_free(ctx.funcs as *void); - bux_free(ctx.externFuncs as *void); - bux_free(ctx as *void); -} -} diff --git a/_selfhost/src/lexer.bux b/_selfhost/src/lexer.bux deleted file mode 100644 index 91e02f1..0000000 --- a/_selfhost/src/lexer.bux +++ /dev/null @@ -1,816 +0,0 @@ -// lexer.bux — Lexer state machine (ported from lexer.nim) -// Tokenizes Bux source into a stream of tokens. -module Lexer { - -extern func bux_strlen(s: String) -> uint; - -// --------------------------------------------------------------------------- -// Character helpers (wrap C ctype) -// --------------------------------------------------------------------------- - -func Lex_IsDigit(c: uint32) -> bool { - return c >= 48 && c <= 57; // '0'..'9' -} - -func Lex_IsHexDigit(c: uint32) -> bool { - if c >= 48 && c <= 57 { return true; } // 0-9 - if c >= 65 && c <= 70 { return true; } // A-F - if c >= 97 && c <= 102 { return true; } // a-f - return false; -} - -func Lex_IsBinDigit(c: uint32) -> bool { - return c == 48 || c == 49; // '0' or '1' -} - -func Lex_IsOctDigit(c: uint32) -> bool { - return c >= 48 && c <= 55; // '0'..'7' -} - -func Lex_IsIdentStart(c: uint32) -> bool { - if c >= 97 && c <= 122 { return true; } // a-z - if c >= 65 && c <= 90 { return true; } // A-Z - return c == 95; // '_' -} - -func Lex_IsIdentChar(c: uint32) -> bool { - if Lex_IsIdentStart(c) { return true; } - return Lex_IsDigit(c); -} - -// --------------------------------------------------------------------------- -// Lexer state -// --------------------------------------------------------------------------- - -const maxTokens: int = 32768; -const maxDiags: int = 128; - -struct LexerDiag { - line: uint32; - column: uint32; - message: String; -} - -struct Lexer { - source: String; - sourceLen: int; - pos: int; - line: uint32; - column: uint32; - startLine: uint32; - startColumn: uint32; - startPos: int; - tokenCount: int; - tokens: *LexToken; - diagCount: int; - diags: *LexerDiag; -} - -struct LexToken { - kind: int; - text: String; - line: uint32; - column: uint32; -} - -// --------------------------------------------------------------------------- -// Init -// --------------------------------------------------------------------------- - -func Lexer_Init(source: String) -> Lexer { - let len: uint = bux_strlen(source); - let tokBuf: *LexToken = bux_alloc(maxTokens as uint * sizeof(LexToken)) as *LexToken; - let diagBuf: *LexerDiag = bux_alloc(maxDiags as uint * sizeof(LexerDiag)) as *LexerDiag; - return Lexer { - source: source, sourceLen: len as int, - pos: 0, line: 1, column: 1, - startLine: 0, startColumn: 0, startPos: 0, - tokenCount: 0, tokens: tokBuf, - diagCount: 0, diags: diagBuf - }; -} - -// --------------------------------------------------------------------------- -// Core primitives -// --------------------------------------------------------------------------- - -func lexIsAtEnd(lex: *Lexer) -> bool { - return lex.pos >= lex.sourceLen; -} - -func lexPeek(lex: *Lexer, ahead: int) -> uint32 { - let i: int = lex.pos + ahead; - if i < lex.sourceLen { - return (lex.source[i] as uint32) & 255; - } - return 0; -} - -func lexAdvance(lex: *Lexer) -> uint32 { - let c: uint32 = lexPeek(lex, 0); - if !lexIsAtEnd(lex) { - lex.pos = lex.pos + 1; - if c == 10 { // '\n' - lex.line = lex.line + 1; - lex.column = 1; - } else { - lex.column = lex.column + 1; - } - } - return c; -} - -func lexMatch(lex: *Lexer, expected: uint32) -> bool { - if lexIsAtEnd(lex) { return false; } - if lexPeek(lex, 0) != expected { return false; } - discard lexAdvance(lex); - return true; -} - -func lexMatchStr(lex: *Lexer, s: String) -> bool { - let len: uint = bux_strlen(s); - var i: int = 0; - while i < (len as int) { - if lexPeek(lex, i) != (s[i] as uint32) { - return false; - } - i = i + 1; - } - i = 0; - while i < (len as int) { - discard lexAdvance(lex); - i = i + 1; - } - return true; -} - -// --------------------------------------------------------------------------- -// Token emission -// --------------------------------------------------------------------------- - -func lexMarkStart(lex: *Lexer) { - lex.startLine = lex.line; - lex.startColumn = lex.column; - lex.startPos = lex.pos; -} - -func lexMakeToken(lex: *Lexer, kind: int) -> LexToken { - var text: String = ""; - let endPos: int = lex.pos; - let startPos: int = lex.startPos; - if endPos > startPos { - let len: int = endPos - startPos; - let buf: *char8 = bux_alloc((len + 1) as uint) as *char8; - var i: int = 0; - while i < len { - buf[i] = lex.source[startPos + i] as char8; - i = i + 1; - } - buf[len] = 0 as char8; - text = buf; - } else { - text = ""; - } - return LexToken { - kind: kind, text: text, - line: lex.startLine, column: lex.startColumn - }; -} - -func lexEmitToken(lex: *Lexer, kind: int) { - let tok: LexToken = lexMakeToken(lex, kind); - if lex.tokenCount < maxTokens { - lex.tokens[lex.tokenCount] = tok; - lex.tokenCount = lex.tokenCount + 1; - } -} - -func lexSetLastTokenText(lex: *Lexer, text: String) { - if lex.tokenCount > 0 { - lex.tokens[lex.tokenCount - 1].text = text; - } -} - -func lexEmitDiag(lex: *Lexer, msg: String) { - if lex.diagCount < maxDiags { - lex.diags[lex.diagCount] = LexerDiag { - line: lex.line, column: lex.column, message: msg - }; - lex.diagCount = lex.diagCount + 1; - } -} - -// --------------------------------------------------------------------------- -// Whitespace / comments -// --------------------------------------------------------------------------- - -func lexSkipLineComment(lex: *Lexer) { - while !lexIsAtEnd(lex) && lexPeek(lex, 0) != 10 { // '\n' - discard lexAdvance(lex); - } -} - -func lexSkipBlockComment(lex: *Lexer) { - var depth: int = 1; - while !lexIsAtEnd(lex) && depth > 0 { - if lexPeek(lex, 0) == 47 && lexPeek(lex, 1) == 42 { // /* - discard lexAdvance(lex); - discard lexAdvance(lex); - depth = depth + 1; - } else if lexPeek(lex, 0) == 42 && lexPeek(lex, 1) == 47 { // */ - discard lexAdvance(lex); - discard lexAdvance(lex); - depth = depth - 1; - } else { - discard lexAdvance(lex); - } - } - if depth > 0 { - lexEmitDiag(lex, "unterminated block comment"); - } -} - -func lexSkipWhitespace(lex: *Lexer) { - while !lexIsAtEnd(lex) { - let c: uint32 = lexPeek(lex, 0); - if c == 32 || c == 9 || c == 13 { - discard lexAdvance(lex); - } else { - if c == 47 && lexPeek(lex, 1) == 47 { - lexSkipLineComment(lex); - } else { - if c == 47 && lexPeek(lex, 1) == 42 { - discard lexAdvance(lex); - discard lexAdvance(lex); - lexSkipBlockComment(lex); - } else { - break; - } - } - } - } -} - -// --------------------------------------------------------------------------- -// Identifiers -// --------------------------------------------------------------------------- - -func lexIsIdentKind(tok: int) -> bool { - return tok == tkFunc || tok == tkLet || tok == tkVar || tok == tkConst - || tok == tkType || tok == tkStruct || tok == tkEnum || tok == tkUnion - || tok == tkInterface || tok == tkExtend || tok == tkModule || tok == tkImport - || tok == tkPub || tok == tkExtern || tok == tkIf || tok == tkElse - || tok == tkWhile || tok == tkDo || tok == tkLoop || tok == tkFor - || tok == tkIn || tok == tkBreak || tok == tkContinue || tok == tkReturn - || tok == tkMatch || tok == tkAs || tok == tkIs || tok == tkNull - || tok == tkSelf || tok == tkSuper; -} - -func lexKeywordKind(text: String) -> int { - if String_Eq(text, "true") { return tkBoolLiteral; } - if String_Eq(text, "false") { return tkBoolLiteral; } - if String_Eq(text, "func") { return tkFunc; } - if String_Eq(text, "let") { return tkLet; } - if String_Eq(text, "var") { return tkVar; } - if String_Eq(text, "const") { return tkConst; } - if String_Eq(text, "type") { return tkType; } - if String_Eq(text, "struct") { return tkStruct; } - if String_Eq(text, "enum") { return tkEnum; } - if String_Eq(text, "union") { return tkUnion; } - if String_Eq(text, "interface") { return tkInterface; } - if String_Eq(text, "extend") { return tkExtend; } - if String_Eq(text, "module") { return tkModule; } - if String_Eq(text, "import") { return tkImport; } - if String_Eq(text, "pub") { return tkPub; } - if String_Eq(text, "extern") { return tkExtern; } - if String_Eq(text, "if") { return tkIf; } - if String_Eq(text, "else") { return tkElse; } - if String_Eq(text, "while") { return tkWhile; } - if String_Eq(text, "do") { return tkDo; } - if String_Eq(text, "loop") { return tkLoop; } - if String_Eq(text, "for") { return tkFor; } - if String_Eq(text, "in") { return tkIn; } - if String_Eq(text, "break") { return tkBreak; } - if String_Eq(text, "continue") { return tkContinue; } - if String_Eq(text, "return") { return tkReturn; } - if String_Eq(text, "match") { return tkMatch; } - if String_Eq(text, "as") { return tkAs; } - if String_Eq(text, "is") { return tkIs; } - if String_Eq(text, "null") { return tkNull; } - if String_Eq(text, "self") { return tkSelf; } - if String_Eq(text, "super") { return tkSuper; } - if String_Eq(text, "sizeof") { return tkSizeOf; } - if String_Eq(text, "discard") { return tkDiscard; } - if String_Eq(text, "async") { return tkAsync; } - if String_Eq(text, "await") { return tkAwait; } - if String_Eq(text, "spawn") { return tkSpawn; } - return tkIdent; -} - -func lexScanIdent(lex: *Lexer) { - lexMarkStart(lex); - while !lexIsAtEnd(lex) && Lex_IsIdentChar(lexPeek(lex, 0)) { - discard lexAdvance(lex); - } - let tok: LexToken = lexMakeToken(lex, tkIdent); - let kind: int = lexKeywordKind(tok.text); - tok.kind = kind; - if lex.tokenCount < maxTokens { - lex.tokens[lex.tokenCount] = tok; - lex.tokenCount = lex.tokenCount + 1; - } -} - -// --------------------------------------------------------------------------- -// Numbers -// --------------------------------------------------------------------------- - -func lexScanDigits(lex: *Lexer) { - while !lexIsAtEnd(lex) && Lex_IsDigit(lexPeek(lex, 0)) { - discard lexAdvance(lex); - } -} - -func lexScanHexDigits(lex: *Lexer) { - while !lexIsAtEnd(lex) && Lex_IsHexDigit(lexPeek(lex, 0)) { - discard lexAdvance(lex); - } -} - -func lexScanNumber(lex: *Lexer) { - lexMarkStart(lex); - var isFloat: bool = false; - - if lexPeek(lex, 0) == 48 { // '0' - let p1: uint32 = lexPeek(lex, 1); - if p1 == 120 || p1 == 88 || p1 == 98 || p1 == 66 || p1 == 111 || p1 == 79 { // x X b B o O - discard lexAdvance(lex); // 0 - discard lexAdvance(lex); // prefix - if p1 == 120 || p1 == 88 { lexScanHexDigits(lex); } - else if p1 == 98 || p1 == 66 { - while !lexIsAtEnd(lex) && Lex_IsBinDigit(lexPeek(lex, 0)) { - discard lexAdvance(lex); - } - } - else { - while !lexIsAtEnd(lex) && Lex_IsOctDigit(lexPeek(lex, 0)) { - discard lexAdvance(lex); - } - } - lexEmitToken(lex, tkIntLiteral); - return; - } - } - - lexScanDigits(lex); - - if lexPeek(lex, 0) == 46 && Lex_IsDigit(lexPeek(lex, 1)) { // . digit - isFloat = true; - discard lexAdvance(lex); // . - lexScanDigits(lex); - } - - let e: uint32 = lexPeek(lex, 0); - if e == 101 || e == 69 { // e E - isFloat = true; - discard lexAdvance(lex); - let sign: uint32 = lexPeek(lex, 0); - if sign == 43 || sign == 45 { discard lexAdvance(lex); } // + - - lexScanDigits(lex); - } - - if isFloat { - lexEmitToken(lex, tkFloatLiteral); - } else { - lexEmitToken(lex, tkIntLiteral); - } -} - -// --------------------------------------------------------------------------- -// Strings and chars -// --------------------------------------------------------------------------- - -func lexScanBacktickString(lex: *Lexer) { - lexMarkStart(lex); - if lexPeek(lex, 0) == 96 { discard lexAdvance(lex); } // opening backtick - while !lexIsAtEnd(lex) && lexPeek(lex, 0) != 96 { - discard lexAdvance(lex); - } - if lexIsAtEnd(lex) { - lexEmitDiag(lex, "unterminated backtick string literal"); - } else { - discard lexAdvance(lex); // closing backtick - } - lexEmitToken(lex, tkStringLiteral); -} - -func lexScanString(lex: *Lexer) { - lexMarkStart(lex); - // Collect the prefix (before opening quote) for the token text - var prefix: String = ""; - var prefixLen: int = 0; - if lex.pos > lex.startPos { - let plen: int = lex.pos - lex.startPos; - let pbuf: *char8 = bux_alloc((plen + 1) as uint) as *char8; - var pi: int = 0; - while pi < plen { - pbuf[pi] = lex.source[lex.startPos + pi] as char8; - pi = pi + 1; - } - pbuf[plen] = 0 as char8; - prefix = pbuf; - prefixLen = plen; - } - // Allocate buffer for resolved content (max = remaining source length) - let maxLen: int = 4096; - let resolved: *char8 = bux_alloc(maxLen as uint) as *char8; - var rpos: int = 0; - if lexPeek(lex, 0) == 34 { discard lexAdvance(lex); } // opening " - while !lexIsAtEnd(lex) && lexPeek(lex, 0) != 34 { // closing " - if lexPeek(lex, 0) == 10 { // \n - lexEmitDiag(lex, "unterminated string literal"); - break; - } - if lexPeek(lex, 0) == 92 { // backslash - discard lexAdvance(lex); - if !lexIsAtEnd(lex) { - let ec: uint32 = lexAdvance(lex); - var rc: char8 = ec as char8; - if ec == 110 { rc = 10 as char8; } // \n - else if ec == 114 { rc = 13 as char8; } // \r - else if ec == 116 { rc = 9 as char8; } // \t - else if ec == 48 { rc = 0 as char8; } // \0 - else if ec == 92 { rc = 92 as char8; } // \\ - else if ec == 34 { rc = 34 as char8; } // \" - else if ec == 39 { rc = 39 as char8; } // \' - resolved[rpos] = rc; - rpos = rpos + 1; - } - } else { - let c: uint32 = lexAdvance(lex); - resolved[rpos] = c as char8; - rpos = rpos + 1; - } - } - resolved[rpos] = 0 as char8; - if lexIsAtEnd(lex) { - lexEmitDiag(lex, "unterminated string literal"); - } else { - discard lexAdvance(lex); // closing " - } - lexEmitToken(lex, tkStringLiteral); - // Replace token text with resolved version: prefix + " + resolved + " - let totalLen: int = prefixLen + 1 + rpos + 1; - let finalBuf: *char8 = bux_alloc((totalLen + 1) as uint) as *char8; - var fi: int = 0; - var pi2: int = 0; - while pi2 < prefixLen { finalBuf[fi] = prefix[pi2] as char8; fi = fi + 1; pi2 = pi2 + 1; } - finalBuf[fi] = 34 as char8; fi = fi + 1; // opening " - var ri: int = 0; - while ri < rpos { finalBuf[fi] = resolved[ri]; fi = fi + 1; ri = ri + 1; } - finalBuf[fi] = 34 as char8; fi = fi + 1; // closing " - finalBuf[fi] = 0 as char8; - lexSetLastTokenText(lex, finalBuf); -} - -func lexScanChar(lex: *Lexer) { - lexMarkStart(lex); - // Collect the prefix for the token text - var prefix: String = ""; - var prefixLen: int = 0; - if lex.pos > lex.startPos { - let plen: int = lex.pos - lex.startPos; - let pbuf: *char8 = bux_alloc((plen + 1) as uint) as *char8; - var pi: int = 0; - while pi < plen { - pbuf[pi] = lex.source[lex.startPos + pi] as char8; - pi = pi + 1; - } - pbuf[plen] = 0 as char8; - prefix = pbuf; - prefixLen = plen; - } - var resolved: char8 = 0 as char8; - if lexPeek(lex, 0) == 39 { discard lexAdvance(lex); } // opening ' - if lexIsAtEnd(lex) { - lexEmitDiag(lex, "unterminated char literal"); - lexEmitToken(lex, tkCharLiteral); - return; - } - if lexPeek(lex, 0) == 10 { // \n - lexEmitDiag(lex, "newline in char literal"); - } else if lexPeek(lex, 0) == 92 { // backslash - discard lexAdvance(lex); - if !lexIsAtEnd(lex) { - let ec: uint32 = lexAdvance(lex); - if ec == 110 { resolved = 10 as char8; } // \n - else if ec == 114 { resolved = 13 as char8; } // \r - else if ec == 116 { resolved = 9 as char8; } // \t - else if ec == 48 { resolved = 0 as char8; } // \0 - else if ec == 92 { resolved = 92 as char8; } // \\ - else if ec == 34 { resolved = 34 as char8; } // \" - else if ec == 39 { resolved = 39 as char8; } // \' - else { resolved = ec as char8; } - } - } else { - resolved = lexAdvance(lex) as char8; - } - if lexIsAtEnd(lex) || lexPeek(lex, 0) != 39 { - lexEmitDiag(lex, "expected closing ' for char literal"); - } else { - discard lexAdvance(lex); // closing ' - } - lexEmitToken(lex, tkCharLiteral); - // Replace token text with resolved version - let totalLen: int = prefixLen + 1 + 1 + 1; - let finalBuf: *char8 = bux_alloc((totalLen + 1) as uint) as *char8; - var fi: int = 0; - var pi2: int = 0; - while pi2 < prefixLen { finalBuf[fi] = prefix[pi2] as char8; fi = fi + 1; pi2 = pi2 + 1; } - finalBuf[fi] = 39 as char8; fi = fi + 1; // opening ' - finalBuf[fi] = resolved; fi = fi + 1; // resolved char - finalBuf[fi] = 39 as char8; fi = fi + 1; // closing ' - finalBuf[fi] = 0 as char8; - lexSetLastTokenText(lex, finalBuf); -} - -// --------------------------------------------------------------------------- -// Symbols / operators -// --------------------------------------------------------------------------- - -func lexScanSymbol(lex: *Lexer) { - lexMarkStart(lex); - let c: uint32 = lexAdvance(lex); - - // Single-char tokens - if c == 40 { lexEmitToken(lex, tkLParen); return; } - if c == 41 { lexEmitToken(lex, tkRParen); return; } - if c == 123 { lexEmitToken(lex, tkLBrace); return; } - if c == 125 { lexEmitToken(lex, tkRBrace); return; } - if c == 91 { lexEmitToken(lex, tkLBracket); return; } - if c == 93 { lexEmitToken(lex, tkRBracket); return; } - if c == 44 { lexEmitToken(lex, tkComma); return; } - if c == 59 { lexEmitToken(lex, tkSemicolon); return; } - if c == 64 { lexEmitToken(lex, tkAt); return; } - if c == 63 { lexEmitToken(lex, tkQuestion); return; } - if c == 126 { lexEmitToken(lex, tkTilde); return; } - - // : :: - if c == 58 { - if lexMatch(lex, 58) { lexEmitToken(lex, tkColonColon); } - else { lexEmitToken(lex, tkColon); } - return; - } - - // . .. ... ..= - if c == 46 { - if lexPeek(lex, 0) == 46 && lexPeek(lex, 1) == 46 { - discard lexAdvance(lex); discard lexAdvance(lex); - lexEmitToken(lex, tkDotDotDot); return; - } - if lexPeek(lex, 0) == 46 && lexPeek(lex, 1) == 61 { - discard lexAdvance(lex); discard lexAdvance(lex); - lexEmitToken(lex, tkDotDotEqual); return; - } - if lexMatch(lex, 46) { lexEmitToken(lex, tkDotDot); return; } - lexEmitToken(lex, tkDot); return; - } - - // - -> -- -= - if c == 45 { - if lexMatch(lex, 62) { lexEmitToken(lex, tkArrow); return; } - if lexMatch(lex, 45) { lexEmitToken(lex, tkMinusMinus); return; } - if lexMatch(lex, 61) { lexEmitToken(lex, tkMinusAssign); return; } - lexEmitToken(lex, tkMinus); return; - } - - // + ++ += - if c == 43 { - if lexMatch(lex, 43) { lexEmitToken(lex, tkPlusPlus); return; } - if lexMatch(lex, 61) { lexEmitToken(lex, tkPlusAssign); return; } - lexEmitToken(lex, tkPlus); return; - } - - // * ** *= - if c == 42 { - if lexMatch(lex, 42) { lexEmitToken(lex, tkStarStar); return; } - if lexMatch(lex, 61) { lexEmitToken(lex, tkStarAssign); return; } - lexEmitToken(lex, tkStar); return; - } - - // / /= - if c == 47 { - if lexMatch(lex, 61) { lexEmitToken(lex, tkSlashAssign); return; } - lexEmitToken(lex, tkSlash); return; - } - - // % %= - if c == 37 { - if lexMatch(lex, 61) { lexEmitToken(lex, tkPercentAssign); return; } - lexEmitToken(lex, tkPercent); return; - } - - // = == => - if c == 61 { - if lexMatch(lex, 61) { lexEmitToken(lex, tkEq); return; } - if lexMatch(lex, 62) { lexEmitToken(lex, tkFatArrow); return; } - lexEmitToken(lex, tkAssign); return; - } - - // ! != - if c == 33 { - if lexMatch(lex, 61) { lexEmitToken(lex, tkNe); return; } - lexEmitToken(lex, tkBang); return; - } - - // < <= << <<= - if c == 60 { - if lexMatch(lex, 61) { lexEmitToken(lex, tkLe); return; } - if lexMatch(lex, 60) { - if lexMatch(lex, 61) { lexEmitToken(lex, tkShlAssign); return; } - lexEmitToken(lex, tkShl); return; - } - lexEmitToken(lex, tkLt); return; - } - - // > >= >> >>= - if c == 62 { - if lexMatch(lex, 61) { lexEmitToken(lex, tkGe); return; } - if lexMatch(lex, 62) { - if lexMatch(lex, 61) { lexEmitToken(lex, tkShrAssign); return; } - lexEmitToken(lex, tkShr); return; - } - lexEmitToken(lex, tkGt); return; - } - - // & && &= - if c == 38 { - if lexMatch(lex, 38) { lexEmitToken(lex, tkAmpAmp); return; } - if lexMatch(lex, 61) { lexEmitToken(lex, tkAmpAssign); return; } - lexEmitToken(lex, tkAmp); return; - } - - // | || |= - if c == 124 { - if lexMatch(lex, 124) { lexEmitToken(lex, tkPipePipe); return; } - if lexMatch(lex, 61) { lexEmitToken(lex, tkPipeAssign); return; } - lexEmitToken(lex, tkPipe); return; - } - - // ^ ^= - if c == 94 { - if lexMatch(lex, 61) { lexEmitToken(lex, tkCaretAssign); return; } - lexEmitToken(lex, tkCaret); return; - } - - // # intrinsics - if c == 35 { - if lexMatchStr(lex, "line") { lexEmitToken(lex, tkHashLine); return; } - if lexMatchStr(lex, "column") { lexEmitToken(lex, tkHashColumn); return; } - if lexMatchStr(lex, "file") { lexEmitToken(lex, tkHashFile); return; } - if lexMatchStr(lex, "function") { lexEmitToken(lex, tkHashFunction); return; } - if lexMatchStr(lex, "date") { lexEmitToken(lex, tkHashDate); return; } - if lexMatchStr(lex, "time") { lexEmitToken(lex, tkHashTime); return; } - if lexMatchStr(lex, "module") { lexEmitToken(lex, tkHashModule); return; } - lexEmitToken(lex, tkHash); return; - } - - lexEmitDiag(lex, "unexpected character"); - lexEmitToken(lex, tkUnknown); -} - -// --------------------------------------------------------------------------- -// Next token -// --------------------------------------------------------------------------- - -func lexNextToken(lex: *Lexer) { - lexSkipWhitespace(lex); - - if lexIsAtEnd(lex) { - lexMarkStart(lex); - lexEmitToken(lex, tkEndOfFile); - return; - } - let c: uint32 = lexPeek(lex, 0); - - if c == 10 { // \n - lexMarkStart(lex); - discard lexAdvance(lex); - lexEmitToken(lex, tkNewLine); - return; - } - - // String prefixes: c8" c16" c32" - if c == 99 { // 'c' - let d: uint32 = lexPeek(lex, 1); - if d == 56 && lexPeek(lex, 2) == 34 { // c8" - discard lexAdvance(lex); discard lexAdvance(lex); // c 8 - lexScanString(lex); return; - } - if d == 49 && lexPeek(lex, 2) == 54 && lexPeek(lex, 3) == 34 { // c16" - discard lexAdvance(lex); discard lexAdvance(lex); discard lexAdvance(lex); - lexScanString(lex); return; - } - if d == 51 && lexPeek(lex, 2) == 50 && lexPeek(lex, 3) == 34 { // c32" - discard lexAdvance(lex); discard lexAdvance(lex); discard lexAdvance(lex); - lexScanString(lex); return; - } - } - - if c == 34 { // " - lexScanString(lex); return; - } - - // Backtick raw string - if c == 96 { // backtick - lexScanBacktickString(lex); return; - } - - // Char prefixes: c8' c16' c32' - if c == 99 { // 'c' - let d: uint32 = lexPeek(lex, 1); - if d == 56 && lexPeek(lex, 2) == 39 { // c8' - discard lexAdvance(lex); discard lexAdvance(lex); - lexScanChar(lex); return; - } - if d == 49 && lexPeek(lex, 2) == 54 && lexPeek(lex, 3) == 39 { // c16' - discard lexAdvance(lex); discard lexAdvance(lex); discard lexAdvance(lex); - lexScanChar(lex); return; - } - if d == 51 && lexPeek(lex, 2) == 50 && lexPeek(lex, 3) == 39 { // c32' - discard lexAdvance(lex); discard lexAdvance(lex); discard lexAdvance(lex); - lexScanChar(lex); return; - } - } - - if c == 39 { // ' - lexScanChar(lex); return; - } - - if Lex_IsIdentStart(c) { - lexScanIdent(lex); return; - } - - if Lex_IsDigit(c) { - lexScanNumber(lex); return; - } - - lexScanSymbol(lex); -} - -// --------------------------------------------------------------------------- -// Tokenize — main entry point -// --------------------------------------------------------------------------- - -func Lexer_Tokenize(source: String) -> *Lexer { - let lex: *Lexer = bux_alloc(sizeof(Lexer)) as *Lexer; - lex.source = source; - lex.sourceLen = bux_strlen(source) as int; - lex.pos = 0; - lex.line = 1; - lex.column = 1; - lex.startLine = 0; - lex.startColumn = 0; - lex.startPos = 0; - let tokBuf: *LexToken = bux_alloc(maxTokens as uint * sizeof(LexToken)) as *LexToken; - let diagBuf: *LexerDiag = bux_alloc(maxDiags as uint * sizeof(LexerDiag)) as *LexerDiag; - lex.tokens = tokBuf; - lex.diags = diagBuf; - lex.tokenCount = 0; - lex.diagCount = 0; - - while true { - lexNextToken(lex); - if lex.tokenCount >= maxTokens { - lexEmitDiag(lex, "too many tokens"); - break; - } - if lex.tokens[lex.tokenCount - 1].kind == tkEndOfFile { - break; - } - } - return lex; -} - -func Lexer_TokenCount(lex: *Lexer) -> int { - return lex.tokenCount; -} - -func Lexer_GetToken(lex: *Lexer, index: int) -> LexToken { - if index >= 0 && index < lex.tokenCount { - return lex.tokens[index]; - } - var empty: LexToken = LexToken { kind: tkEndOfFile, text: "", line: 0, column: 0 }; - return empty; -} - -func Lexer_DiagCount(lex: *Lexer) -> int { - return lex.diagCount; -} - -func Lexer_Free(lex: *Lexer) { - bux_free(lex.tokens as *void); - bux_free(lex.diags as *void); - bux_free(lex as *void); -} -} diff --git a/_selfhost/src/manifest.bux b/_selfhost/src/manifest.bux deleted file mode 100644 index 47df46d..0000000 --- a/_selfhost/src/manifest.bux +++ /dev/null @@ -1,89 +0,0 @@ -// manifest.bux — Manifest parser for bux.toml files -// Parses package metadata: name, version, type, build output. -module Manifest { - -extern func bux_strlen(s: String) -> uint; - -// --------------------------------------------------------------------------- -// Manifest struct -// --------------------------------------------------------------------------- -struct Manifest { - name: String; - version: String; - pkgType: String; - output: String; -} - -// --------------------------------------------------------------------------- -// Simple TOML parser (handles [Package] and [Build] sections) -// --------------------------------------------------------------------------- - -func Manifest_Parse(content: String) -> Manifest { - var m: Manifest; - m.name = ""; - m.version = "0.1.0"; - m.pkgType = "bin"; - m.output = "Bin"; - - if String_Eq(content, "") { return m; } - - var currentSection: String = ""; - let count: uint = String_SplitCount(content, "\n"); - var i: uint = 0; - while i < count { - let line: String = String_SplitPart(content, "\n", i); - - // Skip empty lines and comments - if String_Eq(line, "") { i = i + 1; continue; } - if String_StartsWith(line, "#") { i = i + 1; continue; } - - // Section header: [Section] - if String_StartsWith(line, "[") { - if String_StartsWith(line, "[Package]") { - currentSection = "Package"; - } else if String_StartsWith(line, "[Build]") { - currentSection = "Build"; - } else { - currentSection = ""; - } - i = i + 1; continue; - } - - // Key = Value - let eqCount: uint = String_SplitCount(line, "="); - if eqCount >= 2 { - let key: String = String_Trim(String_SplitPart(line, "=", 0)); - let rawVal: String = String_Trim(String_SplitPart(line, "=", 1)); - - // Strip quotes from value - var val: String = rawVal; - if String_StartsWith(val, "\"") && String_EndsWith(val, "\"") { - let vlen: uint = bux_strlen(val); - if vlen >= 2 { - val = String_Slice(val, 1, vlen - 2); - } - } - - if String_Eq(currentSection, "Package") { - if String_Eq(key, "Name") { m.name = val; } - if String_Eq(key, "Version") { m.version = val; } - if String_Eq(key, "Type") { m.pkgType = val; } - } else if String_Eq(currentSection, "Build") { - if String_Eq(key, "Output") { m.output = val; } - } - } - i = i + 1; - } - - return m; -} - -// --------------------------------------------------------------------------- -// Load manifest from file -// --------------------------------------------------------------------------- - -func Manifest_Load(path: String) -> Manifest { - let content: String = ReadFile(path); - return Manifest_Parse(content); -} -} diff --git a/_selfhost/src/parser.bux b/_selfhost/src/parser.bux deleted file mode 100644 index ce699a8..0000000 --- a/_selfhost/src/parser.bux +++ /dev/null @@ -1,1532 +0,0 @@ -// parser.bux — Recursive descent parser (ported from parser.nim) -// Parses Bux source tokens into an AST. -module Parser { - -extern func bux_strlen(s: String) -> uint; - -// --------------------------------------------------------------------------- -// Parser state -// --------------------------------------------------------------------------- -struct Parser { - tokens: *LexToken, - tokenCount: int, - pos: int, - diagCount: int, - diags: *ParserDiag, - structInitAllowed: bool, -} - -struct ParserDiag { - line: uint32, - column: uint32, - message: String, -} - -// --------------------------------------------------------------------------- -// Token helpers -// --------------------------------------------------------------------------- - -func parserCurToken(p: *Parser) -> LexToken { - if p.pos < p.tokenCount { - return p.tokens[p.pos]; - } - var eof: LexToken; - eof.kind = tkEndOfFile; - eof.text = ""; - return eof; -} - -func parserPeek(p: *Parser, ahead: int) -> int { - let i: int = p.pos + ahead; - if i >= 0 && i < p.tokenCount { - return p.tokens[i].kind; - } - return tkEndOfFile; -} - -// Lookahead to determine if '<' starts a type argument list. -func parserIsTypeArgListAhead(p: *Parser) -> bool { - if !parserCheck(p, tkLt) { return false; } - var depth: int = 0; - var ahead: int = 0; - while true { - let kind: int = parserPeek(p, ahead); - if kind == tkEndOfFile || kind == tkLBrace || kind == tkSemicolon { - return false; - } - if kind == tkLt { - depth = depth + 1; - } else if kind == tkGt { - depth = depth - 1; - if depth == 0 { - return true; - } - } - ahead = ahead + 1; - } - return false; -} - -func parserAdvance(p: *Parser) -> LexToken { - let tok: LexToken = parserCurToken(p); - if p.pos < p.tokenCount { - p.pos = p.pos + 1; - } - return tok; -} - -func parserCheck(p: *Parser, kind: int) -> bool { - return parserPeek(p, 0) == kind; -} - -func parserMatch(p: *Parser, kind: int) -> bool { - if parserCheck(p, kind) { - discard parserAdvance(p); - return true; - } - return false; -} - -func parserPrevious(p: *Parser) -> LexToken { - if p.pos > 0 && p.pos <= p.tokenCount { - return p.tokens[p.pos - 1]; - } - var eof: LexToken; - eof.kind = tkEndOfFile; - return eof; -} - -func parserExpect(p: *Parser, kind: int, msg: String) -> LexToken { - if parserCheck(p, kind) { - return parserAdvance(p); - } - let tok: LexToken = parserCurToken(p); - if p.diagCount < 256 { - p.diags[p.diagCount] = ParserDiag { - line: tok.line, column: tok.column, message: msg - }; - p.diagCount = p.diagCount + 1; - } - return tok; -} - -func parserEmitDiag(p: *Parser, line: uint32, col: uint32, msg: String) { - p.diags[p.diagCount] = ParserDiag { - line: line, column: col, message: msg - }; - p.diagCount = p.diagCount + 1; -} - -func parserIsKeyword(kind: int) -> bool { - if kind >= tkIf && kind <= tkSuper { return true; } - if kind == tkSizeOf { return true; } - return false; -} - -func parserExpectIdentOrKeyword(p: *Parser, msg: String) -> LexToken { - let tok: LexToken = parserCurToken(p); - if tok.kind == tkIdent || parserIsKeyword(tok.kind) { - return parserAdvance(p); - } - if p.diagCount < 256 { - p.diags[p.diagCount] = ParserDiag { - line: tok.line, column: tok.column, message: msg - }; - p.diagCount = p.diagCount + 1; - } - return tok; -} - -// --------------------------------------------------------------------------- -// Type parsing -// --------------------------------------------------------------------------- - -func parserParseType(p: *Parser) -> *TypeExpr { - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - let kindTok: int = parserPeek(p, 0); - - // *T (pointer) - if kindTok == tkStar { - discard parserAdvance(p); - let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; - te.kind = tekPointer; - te.line = line; - te.column = col; - te.pointerPointee = parserParseType(p); - // Set typeName to "Pointee*" - if te.pointerPointee != null as *TypeExpr { - te.typeName = String_Concat(te.pointerPointee.typeName, "*"); - } - return te; - } - - // name - let nameTok: LexToken = parserExpect(p, tkIdent, "expected type name"); - let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; - te.kind = tekNamed; - te.line = nameTok.line; - te.column = nameTok.column; - te.typeName = nameTok.text; - - // Optional type args - if parserCheck(p, tkLt) { - discard parserAdvance(p); // < - let arg0: LexToken = parserExpect(p, tkIdent, "expected type argument"); - te.typeArgName0 = arg0.text; - te.typeArgCount = 1; - if parserMatch(p, tkComma) { - let arg1: LexToken = parserExpect(p, tkIdent, "expected type argument"); - te.typeArgName1 = arg1.text; - te.typeArgCount = 2; - } - discard parserExpect(p, tkGt, "expected '>' to close type arguments"); - } - return te; -} - -// --------------------------------------------------------------------------- -// Forward declarations and helpers -// --------------------------------------------------------------------------- - -func parserParseExpr(p: *Parser) -> *Expr; -func parserParseStmt(p: *Parser) -> *Stmt; -func parserParseBlock(p: *Parser) -> *Block; - -func parserMakeExpr(kind: int, line: uint32, col: uint32) -> *Expr { - let e: *Expr = bux_alloc(sizeof(Expr)) as *Expr; - e.kind = kind; - e.line = line; - e.column = col; - e.strValue = ""; - e.intValue = 0; - e.boolValue = false; - e.tokKind = 0; - e.tokText = ""; - e.child1 = null as *Expr; - e.child2 = null as *Expr; - e.child3 = null as *Expr; - e.refType = null as *TypeExpr; - e.refBlock = null as *Block; - e.genericCallee = ""; - e.genericTypeArg0 = ""; - e.genericTypeArg1 = ""; - e.genericTypeArgCount = 0; - e.structName = ""; - e.structFieldCount = 0; - e.callArgs = null as *ExprList; - e.callArgCount = 0; - return e; -} - -// --------------------------------------------------------------------------- -// Primary expressions -// --------------------------------------------------------------------------- - -func parserParsePrimary(p: *Parser) -> *Expr { - let tok: LexToken = parserCurToken(p); - let line: uint32 = tok.line; - let col: uint32 = tok.column; - let kind: int = tok.kind; - - // Literals - if kind == tkIntLiteral || kind == tkFloatLiteral || kind == tkStringLiteral - || kind == tkCharLiteral || kind == tkBoolLiteral { - discard parserAdvance(p); - let e: *Expr = parserMakeExpr(ekLiteral, line, col); - e.tokKind = kind; - e.tokText = tok.text; - return e; - } - - // Identifier - if kind == tkIdent { - discard parserAdvance(p); - let e: *Expr = parserMakeExpr(ekIdent, line, col); - e.strValue = tok.text; - return e; - } - - // self - if kind == tkSelf { - discard parserAdvance(p); - return parserMakeExpr(ekSelf, line, col); - } - - // null - if kind == tkNull { - discard parserAdvance(p); - let e: *Expr = parserMakeExpr(ekLiteral, line, col); - e.tokKind = tkNull; - return e; - } - - // sizeof(Type) - if kind == tkSizeOf { - discard parserAdvance(p); - discard parserExpect(p, tkLParen, "expected '(' after sizeof"); - let e: *Expr = parserMakeExpr(ekSizeOf, line, col); - e.refType = parserParseType(p); - discard parserExpect(p, tkRParen, "expected ')' after sizeof type"); - return e; - } - - // spawn Callee(args) - if kind == tkSpawn { - discard parserAdvance(p); - let e: *Expr = parserMakeExpr(ekSpawn, line, col); - e.child1 = parserParsePrimary(p); - // Optional call arguments - if parserCheck(p, tkLParen) { - discard parserAdvance(p); - if !parserCheck(p, tkRParen) { - e.child2 = parserParseExpr(p); - if parserMatch(p, tkComma) { - e.child3 = parserParseExpr(p); - while parserMatch(p, tkComma) { - discard parserParseExpr(p); - } - } - } - discard parserExpect(p, tkRParen, "expected ')' after spawn arguments"); - } - return e; - } - - // #intrinsics - if kind >= tkHashLine && kind <= tkHashModule { - discard parserAdvance(p); - let e: *Expr = parserMakeExpr(ekLiteral, line, col); - e.intValue = kind; - return e; - } - - // ( expr ) - if kind == tkLParen { - discard parserAdvance(p); - let e: *Expr = parserParseExpr(p); - discard parserExpect(p, tkRParen, "expected ')'"); - return e; - } - - // -expr, !expr, *expr, &expr - if kind == tkMinus || kind == tkBang || kind == tkStar || kind == tkAmp { - discard parserAdvance(p); - let e: *Expr = parserMakeExpr(ekUnary, line, col); - e.intValue = kind; - e.child1 = parserParsePrimary(p); - return e; - } - - parserEmitDiag(p, line, col, "expected expression"); - return parserMakeExpr(ekLiteral, line, col); -} - -// --------------------------------------------------------------------------- -// Postfix: call, index, field access, as, is, ? -// --------------------------------------------------------------------------- - -func parserParsePostfix(p: *Parser) -> *Expr { - var left: *Expr = parserParsePrimary(p); - - while true { - let kind: int = parserPeek(p, 0); - - // Call: expr(args) - if kind == tkLParen { - discard parserAdvance(p); - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - let e: *Expr = parserMakeExpr(ekCall, line, col); - e.child1 = left; - // Parse all arguments into linked list - var argCount: int = 0; - var firstArg: *ExprList = null as *ExprList; - var lastArg: *ExprList = null as *ExprList; - while !parserCheck(p, tkRParen) { - let argExpr: *Expr = parserParseExpr(p); - let argNode: *ExprList = bux_alloc(sizeof(ExprList)) as *ExprList; - argNode.expr = argExpr; - argNode.next = null as *ExprList; - if firstArg == null as *ExprList { - firstArg = argNode; - lastArg = argNode; - } else { - lastArg.next = argNode; - lastArg = argNode; - } - argCount = argCount + 1; - if !parserMatch(p, tkComma) { break; } - } - e.callArgs = firstArg; - e.callArgCount = argCount; - discard parserExpect(p, tkRParen, "expected ')'"); - left = e; - continue; - } - - // Generic call: Func(args) or Type { ... } - if kind == tkLt { - if left.kind == ekIdent && parserIsTypeArgListAhead(p) { - discard parserAdvance(p); // < - let ta0: LexToken = parserExpect(p, tkIdent, "expected type argument"); - left.genericCallee = left.strValue; - left.genericTypeArg0 = ta0.text; - left.genericTypeArgCount = 1; - if parserMatch(p, tkComma) { - let ta1: LexToken = parserExpect(p, tkIdent, "expected type argument"); - left.genericTypeArg1 = ta1.text; - left.genericTypeArgCount = 2; - } - discard parserExpect(p, tkGt, "expected '>' to close type arguments"); - // After generic args, continue loop to handle call or struct init - continue; - } - } - - // Index: expr[expr] - if kind == tkLBracket { - discard parserAdvance(p); - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - let e: *Expr = parserMakeExpr(ekIndex, line, col); - e.child1 = left; - e.child2 = parserParseExpr(p); - discard parserExpect(p, tkRBracket, "expected ']'"); - left = e; - continue; - } - - // .await - if kind == tkDot { - if parserPeek(p, 1) == tkAwait { - discard parserAdvance(p); // . - discard parserAdvance(p); // await - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - let e: *Expr = parserMakeExpr(ekAwait, line, col); - e.child1 = left; - left = e; - continue; - } - } - - // Field: expr.name - if kind == tkDot { - discard parserAdvance(p); - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - let name: LexToken = parserExpect(p, tkIdent, "expected field name"); - let e: *Expr = parserMakeExpr(ekField, line, col); - e.child1 = left; - e.strValue = name.text; - left = e; - continue; - } - - // Path: expr::name - if kind == tkColonColon { - discard parserAdvance(p); - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - let name: LexToken = parserExpect(p, tkIdent, "expected path segment"); - let e: *Expr = parserMakeExpr(ekField, line, col); - e.child1 = left; - e.strValue = name.text; - left = e; - continue; - } - - // as, is - if kind == tkAs || kind == tkIs { - discard parserAdvance(p); - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - let ek: int = 0; - if kind == tkAs { ek = ekCast; } else { ek = ekIs; } - let e: *Expr = parserMakeExpr(ek, line, col); - e.child1 = left; - e.refType = parserParseType(p); - left = e; - continue; - } - - // ? (try operator) - if kind == tkQuestion { - discard parserAdvance(p); - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - let e: *Expr = parserMakeExpr(ekTry, line, col); - e.child1 = left; - left = e; - continue; - } - - // ! (unwrap operator) - if kind == tkBang { - discard parserAdvance(p); - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - let e: *Expr = parserMakeExpr(ekUnwrap, line, col); - e.child1 = left; - left = e; - continue; - } - - // ++, -- - if kind == tkPlusPlus || kind == tkMinusMinus { - discard parserAdvance(p); - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - let e: *Expr = parserMakeExpr(ekPostfix, line, col); - e.child1 = left; - e.intValue = kind; - left = e; - continue; - } - - // Struct init: TypeName { field: value, ... } - if kind == tkLBrace { - if p.structInitAllowed && left.kind == ekIdent { - discard parserAdvance(p); // consume { - let siLine: uint32 = parserCurToken(p).line; - let siCol: uint32 = parserCurToken(p).column; - let typeName: String = left.strValue; - var fieldCount: int = 0; - var firstField: *Expr = null as *Expr; - var lastField: *Expr = null as *Expr; - while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile { - while parserCheck(p, tkNewLine) { discard parserAdvance(p); } - if parserCheck(p, tkRBrace) || parserPeek(p, 0) == tkEndOfFile { break; } - let sip: int = p.pos; - let fName: LexToken = parserExpectIdentOrKeyword(p, "expected field name"); - discard parserExpect(p, tkColon, "expected ':'"); - let fValue: *Expr = parserParseExpr(p); - let fExpr: *Expr = parserMakeExpr(ekField, fName.line, fName.column); - fExpr.strValue = fName.text; - fExpr.child1 = fValue; - if firstField == null as *Expr { - firstField = fExpr; - lastField = fExpr; - } else { - lastField.child3 = fExpr; - lastField = fExpr; - } - fieldCount = fieldCount + 1; - parserMatch(p, tkComma); - if p.pos == sip { discard parserAdvance(p); } - } - discard parserExpect(p, tkRBrace, "expected '}'"); - let e: *Expr = parserMakeExpr(ekStructInit, siLine, siCol); - e.structName = typeName; - e.structFieldCount = fieldCount; - e.child1 = firstField; - left = e; - continue; - } else { - break; - } - } - - break; - } - return left; -} - -// --------------------------------------------------------------------------- -// Binary expression (precedence climbing) -// All binary operators: arithmetic, comparison, logical, bitwise, assignment -// --------------------------------------------------------------------------- - -func parserIsBinaryOp(kind: int) -> bool { - if kind >= tkPlus && kind <= tkStarStar { return true; } // arithmetic - if kind == tkAmp || kind == tkPipe || kind == tkCaret { return true; } // bitwise (no &|) - if kind >= tkShl && kind <= tkShrAssign { return true; } // shift + assign - if kind >= tkEq && kind <= tkGe { return true; } // comparison - if kind == tkAmpAmp || kind == tkPipePipe { return true; } // logical - if kind >= tkAssign && kind <= tkShrAssign { return true; } // all assigns - if kind == tkDotDot || kind == tkDotDotEqual { return true; } // range - return false; -} - -func parserPrecedence(op: int) -> int { - if op == tkAssign || op == tkPlusAssign || op == tkMinusAssign || op == tkStarAssign || op == tkSlashAssign || op == tkPercentAssign || op == tkAmpAssign || op == tkPipeAssign || op == tkCaretAssign || op == tkShlAssign || op == tkShrAssign { return 1; } - if op == tkPipePipe { return 2; } - if op == tkAmpAmp { return 3; } - if op == tkPipe { return 4; } - if op == tkCaret { return 5; } - if op == tkAmp { return 6; } - if op == tkEq || op == tkNe { return 7; } - if op == tkLt || op == tkLe || op == tkGt || op == tkGe { return 8; } - if op == tkShl || op == tkShr { return 9; } - if op == tkPlus || op == tkMinus { return 10; } - if op == tkStar || op == tkSlash || op == tkPercent { return 11; } - if op == tkStarStar { return 12; } - return 0; -} - -func parserParseBinaryPrec(p: *Parser, minPrec: int) -> *Expr { - var left: *Expr = parserParsePostfix(p); - while true { - let op: int = parserPeek(p, 0); - let prec: int = parserPrecedence(op); - if prec < minPrec { break; } - let opTok: LexToken = parserAdvance(p); - let line: uint32 = opTok.line; - let col: uint32 = opTok.column; - let nextMinPrec: int = prec + 1; - let right: *Expr = parserParseBinaryPrec(p, nextMinPrec); - let e: *Expr = parserMakeExpr(ekBinary, line, col); - e.intValue = opTok.kind; - e.child1 = left; - e.child2 = right; - left = e; - } - return left; -} - -func parserParseBinary(p: *Parser) -> *Expr { - return parserParseBinaryPrec(p, 1); -} - -// --------------------------------------------------------------------------- -// Ternary: cond ? then : else -// --------------------------------------------------------------------------- - -func parserParseTernary(p: *Parser) -> *Expr { - var left: *Expr = parserParseBinary(p); - if parserMatch(p, tkQuestion) { - let thenExpr: *Expr = parserParseExpr(p); - discard parserExpect(p, tkColon, "expected ':' in ternary"); - let elseExpr: *Expr = parserParseExpr(p); - let e: *Expr = parserMakeExpr(ekTernary, left.line, left.column); - e.child1 = left; - e.child2 = thenExpr; - e.child3 = elseExpr; - return e; - } - return left; -} - -// --------------------------------------------------------------------------- -// Top-level expression -// --------------------------------------------------------------------------- - -func parserParseExpr(p: *Parser) -> *Expr { - return parserParseTernary(p); -} - -// --------------------------------------------------------------------------- -// Statements -// --------------------------------------------------------------------------- - -func parserParseStmt(p: *Parser) -> *Stmt { - let tok: LexToken = parserCurToken(p); - let line: uint32 = tok.line; - let col: uint32 = tok.column; - let kind: int = tok.kind; - - // let / var - if kind == tkLet || kind == tkVar { - let isVar: bool = (kind == tkVar); - discard parserAdvance(p); - let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected variable name"); - var typeExpr: *TypeExpr = null as *TypeExpr; - if parserMatch(p, tkColon) { - typeExpr = parserParseType(p); - } - discard parserExpect(p, tkAssign, "expected '=' in let/var statement"); - let init: *Expr = parserParseExpr(p); - parserMatch(p, tkSemicolon); // optional ; - - let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; - s.kind = skLet; - s.line = line; - s.column = col; - s.strValue = nameTok.text; - s.boolValue = isVar; - s.child1 = init; - s.refStmtType = typeExpr; - return s; - } - - // if - if kind == tkIf { - discard parserAdvance(p); - p.structInitAllowed = false; - let cond: *Expr = parserParseExpr(p); - p.structInitAllowed = true; - let thenBlock: *Block = parserParseBlock(p); - var elseBlock: *Block = null as *Block; - if parserMatch(p, tkElse) { - if parserCheck(p, tkIf) { - // else if → parse the if statement, wrap in a synthetic block - let innerIf: *Stmt = parserParseStmt(p); - elseBlock = bux_alloc(sizeof(Block)) as *Block; - elseBlock.line = innerIf.line; - elseBlock.column = innerIf.column; - elseBlock.stmtCount = 1; - elseBlock.firstStmt = innerIf; - elseBlock.lastStmt = innerIf; - innerIf.nextStmt = null as *Stmt; - } else { - elseBlock = parserParseBlock(p); - } - } - let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; - s.kind = skIf; - s.line = line; - s.column = col; - s.child1 = cond; - s.refStmtBlock = thenBlock; - s.refStmtElse = elseBlock; - return s; - } - - // while - if kind == tkWhile { - discard parserAdvance(p); - p.structInitAllowed = false; - let cond: *Expr = parserParseExpr(p); - p.structInitAllowed = true; - let body: *Block = parserParseBlock(p); - let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; - s.kind = skWhile; - s.line = line; - s.column = col; - s.child1 = cond; - s.refStmtBlock = body; - return s; - } - - // loop - if kind == tkLoop { - discard parserAdvance(p); - let body: *Block = parserParseBlock(p); - let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; - s.kind = skLoop; - s.line = line; - s.column = col; - s.refStmtBlock = body; - return s; - } - - // for - if kind == tkFor { - discard parserAdvance(p); - let varName: LexToken = parserExpect(p, tkIdent, "expected loop variable"); - discard parserExpect(p, tkIn, "expected 'in'"); - p.structInitAllowed = false; - let iter: *Expr = parserParseExpr(p); - p.structInitAllowed = true; - let body: *Block = parserParseBlock(p); - let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; - s.kind = skFor; - s.line = line; - s.column = col; - s.strValue = varName.text; - s.child1 = iter; - s.refStmtBlock = body; - return s; - } - - // return - if kind == tkReturn { - discard parserAdvance(p); - var value: *Expr = null as *Expr; - if !parserCheck(p, tkSemicolon) && !parserCheck(p, tkNewLine) && !parserCheck(p, tkRBrace) { - value = parserParseExpr(p); - } - parserMatch(p, tkSemicolon); - let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; - s.kind = skReturn; - s.line = line; - s.column = col; - s.child1 = value; - return s; - } - - // break / continue - if kind == tkBreak || kind == tkContinue { - let sk: int = 0; - if kind == tkBreak { sk = skBreak; } else { sk = skContinue; } - discard parserAdvance(p); - parserMatch(p, tkSemicolon); - let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; - s.kind = sk; - s.line = line; - s.column = col; - return s; - } - - // match - if kind == tkMatch { - discard parserAdvance(p); - p.structInitAllowed = false; - let subject: *Expr = parserParseExpr(p); - p.structInitAllowed = true; - discard parserExpect(p, tkLBrace, "expected '{' to start match body"); - // Skip match body (simplified — just parse arms as empty) - while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile { - if parserCheck(p, tkNewLine) { discard parserAdvance(p); continue; } - let mp: int = p.pos; - discard parserParseExpr(p); // pattern - if parserMatch(p, tkFatArrow) { - discard parserParseExpr(p); // body - } - if p.pos == mp { discard parserAdvance(p); } - } - discard parserExpect(p, tkRBrace, "expected '}' to close match"); - let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; - s.kind = skMatch; - s.line = line; - s.column = col; - s.child1 = subject; - return s; - } - - // Expression statement - if kind == tkNewLine || kind == tkSemicolon { - discard parserAdvance(p); - return null as *Stmt; - } - - let expr: *Expr = parserParseExpr(p); - parserMatch(p, tkSemicolon); - let s: *Stmt = bux_alloc(sizeof(Stmt)) as *Stmt; - s.kind = skExpr; - s.line = line; - s.column = col; - s.child1 = expr; - return s; -} - -// --------------------------------------------------------------------------- -// Block: { stmt* } -// --------------------------------------------------------------------------- - -func parserParseBlock(p: *Parser) -> *Block { - discard parserExpect(p, tkLBrace, "expected '{'"); - let b: *Block = bux_alloc(sizeof(Block)) as *Block; - b.line = parserCurToken(p).line; - b.column = parserCurToken(p).column; - b.stmtCount = 0; - b.firstStmt = null as *Stmt; - b.lastStmt = null as *Stmt; - - while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile { - if parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) { - discard parserAdvance(p); - continue; - } - let beforePos: int = p.pos; - let s: *Stmt = parserParseStmt(p); - // Infinite-loop safeguard: if no progress, advance past current token - if p.pos == beforePos { - discard parserAdvance(p); - continue; - } - if s != null as *Stmt { - s.nextStmt = null as *Stmt; - if b.firstStmt == null as *Stmt { - b.firstStmt = s; - b.lastStmt = s; - } else { - b.lastStmt.nextStmt = s; - b.lastStmt = s; - } - b.stmtCount = b.stmtCount + 1; - } - } - discard parserExpect(p, tkRBrace, "expected '}'"); - return b; -} - -// --------------------------------------------------------------------------- -// Function parameters -// --------------------------------------------------------------------------- - -func parserParseParamList(p: *Parser) -> *Decl { - let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; - d.kind = dkFunc; - d.paramCount = 0; - - discard parserExpect(p, tkLParen, "expected '('"); - while !parserCheck(p, tkRParen) && parserPeek(p, 0) != tkEndOfFile { - if d.paramCount >= 9 { break; } - let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected parameter name"); - discard parserExpect(p, tkColon, "expected ':' in parameter"); - let ptype: *TypeExpr = parserParseType(p); - - if d.paramCount == 0 { - d.param0.name = nameTok.text; - d.param0.refParamType = ptype; - } else if d.paramCount == 1 { - d.param1.name = nameTok.text; - d.param1.refParamType = ptype; - } else if d.paramCount == 2 { - d.param2.name = nameTok.text; - d.param2.refParamType = ptype; - } else if d.paramCount == 3 { - d.param3.name = nameTok.text; - d.param3.refParamType = ptype; - } else if d.paramCount == 4 { - d.param4.name = nameTok.text; - d.param4.refParamType = ptype; - } else if d.paramCount == 5 { - d.param5.name = nameTok.text; - d.param5.refParamType = ptype; - } else if d.paramCount == 6 { - d.param6.name = nameTok.text; - d.param6.refParamType = ptype; - } else if d.paramCount == 7 { - d.param7.name = nameTok.text; - d.param7.refParamType = ptype; - } else if d.paramCount == 8 { - d.param8.name = nameTok.text; - d.param8.refParamType = ptype; - } - d.paramCount = d.paramCount + 1; - - if parserMatch(p, tkComma) { continue; } - break; - } - discard parserExpect(p, tkRParen, "expected ')'"); - return d; -} - -// --------------------------------------------------------------------------- -// Declarations -// --------------------------------------------------------------------------- - -func parserParseFuncDecl(p: *Parser, isPublic: bool, isExtern: bool, isAsync: bool) -> *Decl { - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - discard parserExpect(p, tkFunc, "expected 'func'"); - - let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected function name"); - let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; - d.kind = dkFunc; - d.line = line; - d.column = col; - d.isPublic = isPublic; - d.isAsync = isAsync; - d.strValue = nameTok.text; - - // Type params - if parserCheck(p, tkLt) { - discard parserAdvance(p); - let tp0: LexToken = parserExpect(p, tkIdent, "expected type param"); - d.typeParam0 = tp0.text; - d.typeParamCount = 1; - if parserMatch(p, tkComma) { - let tp1: LexToken = parserExpect(p, tkIdent, "expected type param"); - d.typeParam1 = tp1.text; - d.typeParamCount = 2; - } - discard parserExpect(p, tkGt, "expected '>'"); - } - - // Params - let params: *Decl = parserParseParamList(p); - d.paramCount = params.paramCount; - d.param0 = params.param0; - d.param1 = params.param1; - d.param2 = params.param2; - d.param3 = params.param3; - d.param4 = params.param4; - d.param5 = params.param5; - d.param6 = params.param6; - d.param7 = params.param7; - d.param8 = params.param8; - - // Return type - if parserMatch(p, tkArrow) { - d.retType = parserParseType(p); - } - - // Body - if !isExtern && parserCheck(p, tkLBrace) { - d.refBody = parserParseBlock(p); - } else { - d.refBody = null as *Block; - } - - return d; -} - -func parserParseStructDecl(p: *Parser, isPublic: bool) -> *Decl { - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - discard parserExpect(p, tkStruct, "expected 'struct'"); - let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected struct name"); - - let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; - d.kind = dkStruct; - d.line = line; - d.column = col; - d.isPublic = isPublic; - d.strValue = nameTok.text; - - // Type params - if parserCheck(p, tkLt) { - discard parserAdvance(p); - let tp0: LexToken = parserExpect(p, tkIdent, "expected type param"); - d.typeParam0 = tp0.text; - d.typeParamCount = 1; - if parserMatch(p, tkComma) { - let tp1: LexToken = parserExpect(p, tkIdent, "expected type param"); - d.typeParam1 = tp1.text; - d.typeParamCount = 2; - } - discard parserExpect(p, tkGt, "expected '>'"); - } - - discard parserExpect(p, tkLBrace, "expected '{'"); - while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile { - if d.fieldCount >= 64 { break; } - if parserCheck(p, tkNewLine) { discard parserAdvance(p); continue; } - if parserCheck(p, tkSemicolon) { discard parserAdvance(p); continue; } - let beforePos: int = p.pos; - let fName: LexToken = parserExpectIdentOrKeyword(p, "expected field name"); - discard parserExpect(p, tkColon, "expected ':' in struct field"); - let fType: *TypeExpr = parserParseType(p); - parserMatch(p, tkSemicolon); - // Infinite-loop safeguard - if p.pos == beforePos { - discard parserAdvance(p); - continue; - } - - if d.fieldCount == 0 { - d.field0.name = fName.text; - d.field0.refFieldType = fType; - } else if d.fieldCount == 1 { - d.field1.name = fName.text; - d.field1.refFieldType = fType; - } else if d.fieldCount == 2 { - d.field2.name = fName.text; - d.field2.refFieldType = fType; - } else if d.fieldCount == 3 { - d.field3.name = fName.text; - d.field3.refFieldType = fType; - } else if d.fieldCount == 4 { - d.field4.name = fName.text; - d.field4.refFieldType = fType; - } else if d.fieldCount == 5 { - d.field5.name = fName.text; - d.field5.refFieldType = fType; - } else if d.fieldCount == 6 { - d.field6.name = fName.text; - d.field6.refFieldType = fType; - } else if d.fieldCount == 7 { - d.field7.name = fName.text; - d.field7.refFieldType = fType; - } else if d.fieldCount == 8 { - d.field8.name = fName.text; - d.field8.refFieldType = fType; - } else if d.fieldCount == 9 { - d.field9.name = fName.text; - d.field9.refFieldType = fType; - } else if d.fieldCount == 10 { - d.field10.name = fName.text; - d.field10.refFieldType = fType; - } else if d.fieldCount == 11 { - d.field11.name = fName.text; - d.field11.refFieldType = fType; - } else if d.fieldCount == 12 { - d.field12.name = fName.text; - d.field12.refFieldType = fType; - } else if d.fieldCount == 13 { - d.field13.name = fName.text; - d.field13.refFieldType = fType; - } else if d.fieldCount == 14 { - d.field14.name = fName.text; - d.field14.refFieldType = fType; - } else if d.fieldCount == 15 { - d.field15.name = fName.text; - d.field15.refFieldType = fType; - } else if d.fieldCount == 16 { - d.field16.name = fName.text; - d.field16.refFieldType = fType; - } else if d.fieldCount == 17 { - d.field17.name = fName.text; - d.field17.refFieldType = fType; - } else if d.fieldCount == 18 { - d.field18.name = fName.text; - d.field18.refFieldType = fType; - } else if d.fieldCount == 19 { - d.field19.name = fName.text; - d.field19.refFieldType = fType; - } else if d.fieldCount == 20 { - d.field20.name = fName.text; - d.field20.refFieldType = fType; - } else if d.fieldCount == 21 { - d.field21.name = fName.text; - d.field21.refFieldType = fType; - } else if d.fieldCount == 22 { - d.field22.name = fName.text; - d.field22.refFieldType = fType; - } else if d.fieldCount == 23 { - d.field23.name = fName.text; - d.field23.refFieldType = fType; - } else if d.fieldCount == 24 { - d.field24.name = fName.text; - d.field24.refFieldType = fType; - } else if d.fieldCount == 25 { - d.field25.name = fName.text; - d.field25.refFieldType = fType; - } else if d.fieldCount == 26 { - d.field26.name = fName.text; - d.field26.refFieldType = fType; - } else if d.fieldCount == 27 { - d.field27.name = fName.text; - d.field27.refFieldType = fType; - } else if d.fieldCount == 28 { - d.field28.name = fName.text; - d.field28.refFieldType = fType; - } else if d.fieldCount == 29 { - d.field29.name = fName.text; - d.field29.refFieldType = fType; - } else if d.fieldCount == 30 { - d.field30.name = fName.text; - d.field30.refFieldType = fType; - } else if d.fieldCount == 31 { - d.field31.name = fName.text; - d.field31.refFieldType = fType; - } else if d.fieldCount == 32 { - d.field32.name = fName.text; - d.field32.refFieldType = fType; - } else if d.fieldCount == 33 { - d.field33.name = fName.text; - d.field33.refFieldType = fType; - } else if d.fieldCount == 34 { - d.field34.name = fName.text; - d.field34.refFieldType = fType; - } else if d.fieldCount == 35 { - d.field35.name = fName.text; - d.field35.refFieldType = fType; - } else if d.fieldCount == 36 { - d.field36.name = fName.text; - d.field36.refFieldType = fType; - } else if d.fieldCount == 37 { - d.field37.name = fName.text; - d.field37.refFieldType = fType; - } else if d.fieldCount == 38 { - d.field38.name = fName.text; - d.field38.refFieldType = fType; - } else if d.fieldCount == 39 { - d.field39.name = fName.text; - d.field39.refFieldType = fType; - } else if d.fieldCount == 40 { - d.field40.name = fName.text; - d.field40.refFieldType = fType; - } else if d.fieldCount == 41 { - d.field41.name = fName.text; - d.field41.refFieldType = fType; - } else if d.fieldCount == 42 { - d.field42.name = fName.text; - d.field42.refFieldType = fType; - } else if d.fieldCount == 43 { - d.field43.name = fName.text; - d.field43.refFieldType = fType; - } else if d.fieldCount == 44 { - d.field44.name = fName.text; - d.field44.refFieldType = fType; - } else if d.fieldCount == 45 { - d.field45.name = fName.text; - d.field45.refFieldType = fType; - } else if d.fieldCount == 46 { - d.field46.name = fName.text; - d.field46.refFieldType = fType; - } else if d.fieldCount == 47 { - d.field47.name = fName.text; - d.field47.refFieldType = fType; - } else if d.fieldCount == 48 { - d.field48.name = fName.text; - d.field48.refFieldType = fType; - } else if d.fieldCount == 49 { - d.field49.name = fName.text; - d.field49.refFieldType = fType; - } else if d.fieldCount == 50 { - d.field50.name = fName.text; - d.field50.refFieldType = fType; - } else if d.fieldCount == 51 { - d.field51.name = fName.text; - d.field51.refFieldType = fType; - } else if d.fieldCount == 52 { - d.field52.name = fName.text; - d.field52.refFieldType = fType; - } else if d.fieldCount == 53 { - d.field53.name = fName.text; - d.field53.refFieldType = fType; - } else if d.fieldCount == 54 { - d.field54.name = fName.text; - d.field54.refFieldType = fType; - } else if d.fieldCount == 55 { - d.field55.name = fName.text; - d.field55.refFieldType = fType; - } else if d.fieldCount == 56 { - d.field56.name = fName.text; - d.field56.refFieldType = fType; - } else if d.fieldCount == 57 { - d.field57.name = fName.text; - d.field57.refFieldType = fType; - } else if d.fieldCount == 58 { - d.field58.name = fName.text; - d.field58.refFieldType = fType; - } else if d.fieldCount == 59 { - d.field59.name = fName.text; - d.field59.refFieldType = fType; - } else if d.fieldCount == 60 { - d.field60.name = fName.text; - d.field60.refFieldType = fType; - } else if d.fieldCount == 61 { - d.field61.name = fName.text; - d.field61.refFieldType = fType; - } else if d.fieldCount == 62 { - d.field62.name = fName.text; - d.field62.refFieldType = fType; - } else if d.fieldCount == 63 { - d.field63.name = fName.text; - d.field63.refFieldType = fType; - } - d.fieldCount = d.fieldCount + 1; - } - discard parserExpect(p, tkRBrace, "expected '}'"); - return d; -} - -func parserParseEnumDecl(p: *Parser, isPublic: bool) -> *Decl { - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - discard parserExpect(p, tkEnum, "expected 'enum'"); - let nameTok: LexToken = parserExpect(p, tkIdent, "expected enum name"); - - let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; - d.kind = dkEnum; - d.line = line; - d.column = col; - d.isPublic = isPublic; - d.strValue = nameTok.text; - - discard parserExpect(p, tkLBrace, "expected '{'"); - while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile { - if d.variantCount >= 8 { break; } - if parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) { discard parserAdvance(p); continue; } - let vName: LexToken = parserExpect(p, tkIdent, "expected variant name"); - - var v: EnumVariant; - v.name = vName.text; - - // Optional (Type, Type) data - if parserMatch(p, tkLParen) { - let t0: LexToken = parserExpect(p, tkIdent, "expected data type"); - v.fieldTypeName0 = t0.text; - v.fieldCount = 1; - if parserMatch(p, tkComma) { - let t1: LexToken = parserExpect(p, tkIdent, "expected data type"); - v.fieldTypeName1 = t1.text; - v.fieldCount = 2; - } - discard parserExpect(p, tkRParen, "expected ')'"); - } - - if d.variantCount == 0 { d.variant0 = v; } - else if d.variantCount == 1 { d.variant1 = v; } - else if d.variantCount == 2 { d.variant2 = v; } - else if d.variantCount == 3 { d.variant3 = v; } - d.variantCount = d.variantCount + 1; - - parserMatch(p, tkComma); - if parserCheck(p, tkNewLine) { discard parserAdvance(p); } - } - discard parserExpect(p, tkRBrace, "expected '}'"); - return d; -} - -func parserParseImportDecl(p: *Parser, isPublic: bool) -> *Decl { - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - discard parserExpect(p, tkImport, "expected 'import'"); - - let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; - d.kind = dkUse; - d.line = line; - d.column = col; - d.isPublic = isPublic; - - // Parse path: Std::Io::PrintLine - var pathStr: String = ""; - var segCount: int = 0; - while parserCheck(p, tkIdent) || (segCount > 0 && parserCheck(p, tkColonColon) && parserPeek(p, 1) != tkLBrace) { - if segCount > 0 { - discard parserAdvance(p); // :: - if String_Len(pathStr) > 0 { - let tmp: *char8 = bux_alloc(256) as *char8; - // Append to path string (simplified) - pathStr = String_Concat(pathStr, "::"); - } - } - let seg: LexToken = parserExpect(p, tkIdent, "expected module path segment"); - pathStr = String_Concat(pathStr, seg.text); - segCount = segCount + 1; - } - d.usePath = pathStr; - - // Optional ::{name1, name2} - if parserMatch(p, tkColonColon) && parserCheck(p, tkLBrace) { - discard parserAdvance(p); // { - var names: String = ""; - while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile { - let n: LexToken = parserExpect(p, tkIdent, "expected import name"); - names = String_Concat(names, n.text); - names = String_Concat(names, ","); - if !parserMatch(p, tkComma) { break; } - } - discard parserExpect(p, tkRBrace, "expected '}'"); - d.useNames = names; - d.useKind = 2; // ukMulti - } else { - d.useKind = 0; // ukSingle - } - - parserMatch(p, tkSemicolon); - return d; -} - -func parserParseExternDecl(p: *Parser, isPublic: bool) -> *Decl { - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - discard parserExpect(p, tkExtern, "expected 'extern'"); - - if parserCheck(p, tkFunc) { - let d: *Decl = parserParseFuncDecl(p, isPublic, true, false); - d.kind = dkExternFunc; - return d; - } - - let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; - d.kind = dkExternFunc; - d.line = line; - d.column = col; - d.isPublic = isPublic; - return d; -} - -// --------------------------------------------------------------------------- -// Top-level declaration -// --------------------------------------------------------------------------- - -func parserParseDecl(p: *Parser) -> *Decl { - let isPublic: bool = parserMatch(p, tkPub); - let kind: int = parserPeek(p, 0); - - if kind == tkAsync && parserPeek(p, 1) == tkFunc { - discard parserAdvance(p); // async - return parserParseFuncDecl(p, isPublic, false, true); - } - if kind == tkFunc { - return parserParseFuncDecl(p, isPublic, false, false); - } - if kind == tkStruct { return parserParseStructDecl(p, isPublic); } - if kind == tkEnum { return parserParseEnumDecl(p, isPublic); } - if kind == tkImport { return parserParseImportDecl(p, isPublic); } - if kind == tkExtern { return parserParseExternDecl(p, isPublic); } - - if kind == tkExtend { - discard parserAdvance(p); - let line: uint32 = parserCurToken(p).line; - let col: uint32 = parserCurToken(p).column; - let typeName: LexToken = parserExpect(p, tkIdent, "expected type name"); - - let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; - d.kind = dkImpl; - d.line = line; - d.column = col; - d.isPublic = isPublic; - d.strValue = typeName.text; - - // Optional - if parserCheck(p, tkLt) { - discard parserAdvance(p); - let tp0: LexToken = parserExpect(p, tkIdent, "expected type param"); - d.typeParam0 = tp0.text; - d.typeParamCount = 1; - discard parserExpect(p, tkGt, "expected '>'"); - } - - discard parserExpect(p, tkLBrace, "expected '{'"); - while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile { - if parserCheck(p, tkNewLine) { discard parserAdvance(p); continue; } - if parserCheck(p, tkFunc) { - let m: *Decl = parserParseFuncDecl(p, false, false, false); - // Store method in linked list - if d.methodCount == 0 { - d.childDecl1 = m; - } else if d.methodCount == 1 { - d.childDecl1.childDecl2 = m; - } - d.methodCount = d.methodCount + 1; - } else { - break; - } - } - discard parserExpect(p, tkRBrace, "expected '}'"); - return d; - } - - if kind == tkModule { - discard parserAdvance(p); - let name: LexToken = parserExpect(p, tkIdent, "expected module name"); - // Parse optional path segments (::Name) - while parserCheck(p, tkColonColon) { - discard parserAdvance(p); - discard parserExpect(p, tkIdent, "expected module path segment"); - } - let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; - d.kind = dkModule; - d.strValue = name.text; - // Parse module body with braces - if parserCheck(p, tkLBrace) { - discard parserAdvance(p); // consume { - var items: *Decl = null as *Decl; - var lastItem: *Decl = null as *Decl; - while !parserCheck(p, tkRBrace) && parserPeek(p, 0) != tkEndOfFile { - if parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) { - discard parserAdvance(p); - continue; - } - let beforePos: int = p.pos; - let item: *Decl = parserParseDecl(p); - if item != null as *Decl { - item.childDecl2 = null as *Decl; - if items == null as *Decl { - items = item; - lastItem = item; - } else { - lastItem.childDecl2 = item; - lastItem = item; - } - } - // Infinite-loop safeguard: if no progress, skip token - if p.pos == beforePos { - discard parserAdvance(p); - } - } - parserMatch(p, tkRBrace); // consume } - d.childDecl1 = items; // first item of module - } else { - parserMatch(p, tkSemicolon); - } - return d; - } - - if kind == tkConst { - discard parserAdvance(p); - let name: LexToken = parserExpect(p, tkIdent, "expected const name"); - discard parserExpect(p, tkColon, "expected ':'"); - let ct: *TypeExpr = parserParseType(p); - discard parserExpect(p, tkAssign, "expected '='"); - let val: *Expr = parserParseExpr(p); - parserMatch(p, tkSemicolon); - let d: *Decl = bux_alloc(sizeof(Decl)) as *Decl; - d.kind = dkConst; - d.strValue = name.text; - d.constType = ct; - d.constValue = val; - return d; - } - - // Skip unknown declarations - while parserPeek(p, 0) != tkEndOfFile && parserPeek(p, 0) != tkNewLine && parserPeek(p, 0) != tkRBrace { - discard parserAdvance(p); - } - return null as *Decl; -} - -// --------------------------------------------------------------------------- -// Module parsing -// --------------------------------------------------------------------------- - -func Parser_Parse(tokens: *LexToken, tokenCount: int) -> *Module { - let p: *Parser = bux_alloc(sizeof(Parser)) as *Parser; - p.tokens = tokens; - p.tokenCount = tokenCount; - p.pos = 0; - p.structInitAllowed = true; - let diagBuf: *ParserDiag = bux_alloc(256 as uint * sizeof(ParserDiag)) as *ParserDiag; - p.diags = diagBuf; - p.diagCount = 0; - - let mod: *Module = bux_alloc(sizeof(Module)) as *Module; - mod.name = ""; - mod.itemCount = 0; - mod.firstItem = null as *Decl; - - // Parse declarations until EOF - while parserPeek(p, 0) != tkEndOfFile { - if parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) { - discard parserAdvance(p); - continue; - } - let beforePos: int = p.pos; - let decl: *Decl = parserParseDecl(p); - if decl != null as *Decl { - decl.childDecl2 = mod.firstItem; // push front - mod.firstItem = decl; - mod.itemCount = mod.itemCount + 1; - } - // Infinite-loop safeguard: if no progress, skip token - if p.pos == beforePos { - discard parserAdvance(p); - } - } - - return mod; -} - -func Parser_DiagCount(p: *Parser) -> int { - return p.diagCount; -} - -func Parser_Free(p: *Parser) { - bux_free(p.diags as *void); - bux_free(p as *void); -} - -// --------------------------------------------------------------------------- -// Module merging — merge multiple parsed modules into one -// --------------------------------------------------------------------------- - -// Add a single declaration to the merged module (push front to linked list) -func parserMergeAddDecl(mod: *Module, decl: *Decl) { - if decl == null as *Decl { return; } - // Flatten module declarations: extract inner items - if decl.kind == dkModule { - var inner: *Decl = decl.childDecl1; - while inner != null as *Decl { - let next: *Decl = inner.childDecl2; - inner.childDecl2 = mod.firstItem; - mod.firstItem = inner; - mod.itemCount = mod.itemCount + 1; - inner = next; - } - } else { - decl.childDecl2 = mod.firstItem; - mod.firstItem = decl; - mod.itemCount = mod.itemCount + 1; - } -} - -func Parser_MergeModules(modules: *void, count: int) -> *Module { - // This function is kept for future use but not called by Cli_BuildProject - // which does inline merging for simplicity - let merged: *Module = bux_alloc(sizeof(Module)) as *Module; - merged.name = "main"; - merged.path = ""; - merged.itemCount = 0; - merged.firstItem = null as *Decl; - return merged; -} -} diff --git a/_selfhost/src/scope.bux b/_selfhost/src/scope.bux deleted file mode 100644 index d08d90d..0000000 --- a/_selfhost/src/scope.bux +++ /dev/null @@ -1,95 +0,0 @@ -// scope.bux — Symbol table with parent-chain lookup -module Scope { - -// Symbol kinds -const skVar: int = 0; -const skFunc: int = 1; -const skType: int = 2; -const skConst: int = 3; -const skModule: int = 4; - -// Maximum symbols per scope -const maxSymbols: int = 8192; - -struct Symbol { - kind: int; - name: String; - typeKind: int; - typeName: String; - isMutable: bool; - isPublic: bool; - decl: *Decl; // associated declaration (for funcs, structs, enums) -} - -struct Scope { - symbols: *Symbol; - count: int; - parent: *Scope; -} - -// --------------------------------------------------------------------------- -// Scope operations -// --------------------------------------------------------------------------- - -func Scope_New() -> Scope { - let sz: uint = maxSymbols as uint * sizeof(Symbol); - let data: *Symbol = bux_alloc(sz) as *Symbol; - return Scope { symbols: data, count: 0, parent: null as *Scope }; -} - -func Scope_NewChild(parent: *Scope) -> Scope { - let sz: uint = maxSymbols as uint * sizeof(Symbol); - let data: *Symbol = bux_alloc(sz) as *Symbol; - return Scope { symbols: data, count: 0, parent: parent }; -} - -func Scope_Define(scope: *Scope, sym: Symbol) -> bool { - // Check local scope for duplicates - var i: int = 0; - while i < scope.count { - if String_Eq(scope.symbols[i].name, sym.name) { - return false; - } - i = i + 1; - } - if scope.count < maxSymbols { - scope.symbols[scope.count] = sym; - scope.count = scope.count + 1; - return true; - } - return false; -} - -func Scope_Lookup(scope: *Scope, name: String) -> Symbol { - var cur: *Scope = scope; - while cur != null as *Scope { - var i: int = 0; - while i < cur.count { - if String_Eq(cur.symbols[i].name, name) { - return cur.symbols[i]; - } - i = i + 1; - } - cur = cur.parent; - } - var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", isMutable: false, isPublic: false, decl: null as *Decl }; - return empty; -} - -func Scope_LookupLocal(scope: *Scope, name: String) -> Symbol { - var i: int = 0; - while i < scope.count { - if String_Eq(scope.symbols[i].name, name) { - return scope.symbols[i]; - } - i = i + 1; - } - var empty: Symbol = Symbol { kind: 0, name: "", typeKind: 0, typeName: "", isMutable: false, isPublic: false, decl: null as *Decl }; - return empty; -} - -func Scope_Free(scope: *Scope) { - bux_free(scope.symbols as *void); -} - -} diff --git a/_selfhost/src/sema.bux b/_selfhost/src/sema.bux deleted file mode 100644 index df37759..0000000 --- a/_selfhost/src/sema.bux +++ /dev/null @@ -1,753 +0,0 @@ -// sema.bux — Semantic analysis (type checker, ported from sema.nim) -// Validates types, resolves identifiers, checks function calls. -module Sema { - -// --------------------------------------------------------------------------- -// Sema context -// --------------------------------------------------------------------------- -struct Sema { - module: *Module; - scope: *Scope; - typeTable: *void; - methodTable: *void; - diagCount: int; - diags: *SemaDiag; - hasError: bool; - currentRetType: int; // return type of the function being checked -} - -struct SemaDiag { - line: uint32; - column: uint32; - message: String; -} - -// --------------------------------------------------------------------------- -// Diagnostics -// --------------------------------------------------------------------------- - -func Sema_EmitError(sema: *Sema, line: uint32, col: uint32, msg: String) { - if sema.diagCount < 256 { - sema.diags[sema.diagCount] = SemaDiag { line: line, column: col, message: msg }; - sema.diagCount = sema.diagCount + 1; - } - sema.hasError = true; -} - -// --------------------------------------------------------------------------- -// Symbol zero-init helper (bootstrap C backend does not zero-init structs) -// --------------------------------------------------------------------------- - -func Sema_ZeroInitSymbol(sym: *Symbol) { - sym.kind = 0; - sym.name = ""; - sym.typeKind = 0; - sym.typeName = ""; - sym.isMutable = false; - sym.isPublic = false; - sym.decl = null as *Decl; -} - -// --------------------------------------------------------------------------- -// Type resolution from TypeExpr → Type constants -// --------------------------------------------------------------------------- - -func Sema_ResolveType(sema: *Sema, te: *TypeExpr) -> int { - if te == null as *TypeExpr { return tyUnknown; } - let name: String = te.typeName; - - if te.kind == tekPointer { - return tyPointer; - } - - if String_Eq(name, "void") { return tyVoid; } - if String_Eq(name, "bool") { return tyBool; } - if String_Eq(name, "bool8") { return tyBool8; } - if String_Eq(name, "bool16") { return tyBool16; } - if String_Eq(name, "bool32") { return tyBool32; } - if String_Eq(name, "char8") { return tyChar8; } - if String_Eq(name, "char16") { return tyChar16; } - if String_Eq(name, "char32") { return tyChar32; } - if String_Eq(name, "String") { return tyStr; } - if String_Eq(name, "str") { return tyStr; } - if String_Eq(name, "int8") { return tyInt8; } - if String_Eq(name, "int16") { return tyInt16; } - if String_Eq(name, "int32") { return tyInt32; } - if String_Eq(name, "int64") { return tyInt64; } - if String_Eq(name, "int") { return tyInt; } - if String_Eq(name, "uint8") { return tyUInt8; } - if String_Eq(name, "uint16") { return tyUInt16; } - if String_Eq(name, "uint32") { return tyUInt32; } - if String_Eq(name, "uint64") { return tyUInt64; } - if String_Eq(name, "uint") { return tyUInt; } - if String_Eq(name, "float32") { return tyFloat32; } - if String_Eq(name, "float64") { return tyFloat64; } - if String_Eq(name, "float") { return tyFloat64; } - - // Check type table for user-defined types (StringMap not yet supported) - // TODO: re-enable when StringMap generic is available - // if sema.typeTable != null as *void { ... } - return tyNamed; // assume named type -} - -// --------------------------------------------------------------------------- -// Type predicates -// --------------------------------------------------------------------------- - -func Sema_IsNumeric(kind: int) -> bool { - if kind == tyUnknown || kind == tyNamed || kind == tyTypeParam { return true; } - if kind == tyInt8 || kind == tyInt16 || kind == tyInt32 || kind == tyInt64 || kind == tyInt { return true; } - if kind == tyUInt8 || kind == tyUInt16 || kind == tyUInt32 || kind == tyUInt64 || kind == tyUInt { return true; } - if kind == tyFloat32 || kind == tyFloat64 { return true; } - return false; -} - -func Sema_IsBool(kind: int) -> bool { - return kind == tyBool || kind == tyBool8 || kind == tyBool16 || kind == tyBool32; -} - -func Sema_TypeName(kind: int) -> String { - if kind == tyUnknown { return "?"; } - if kind == tyVoid { return "void"; } - if kind == tyBool { return "bool"; } - if kind == tyInt{ return "int"; } - if kind == tyInt64 { return "int64"; } - if kind == tyUInt { return "uint"; } - if kind == tyFloat64 { return "float64"; } - if kind == tyStr { return "String"; } - if kind == tyPointer { return "*ptr"; } - if kind == tyNamed { return "user-type"; } - if kind == tyTypeParam { return "type-param"; } - return "?"; -} - -// --------------------------------------------------------------------------- -// Block checking helper -// --------------------------------------------------------------------------- - -func Sema_CheckBlock(sema: *Sema, block: *Block) { - if block == null as *Block { return; } - var blockScope: Scope = Scope_NewChild(sema.scope); - let prevScope: *Scope = sema.scope; - sema.scope = &blockScope; - var stmt: *Stmt = block.firstStmt; - while stmt != null as *Stmt { - Sema_CheckStmt(sema, stmt); - stmt = stmt.nextStmt; - } - sema.scope = prevScope; -} - -// --------------------------------------------------------------------------- -// Expression type checking -// --------------------------------------------------------------------------- - -func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int { - if expr == null as *Expr { return tyUnknown; } - let kind: int = expr.kind; - // Debug: print expr kind - // Print("Sema_CheckExpr kind="); - // PrintInt(kind as int64); - // PrintLine(""); - - // Literal - if kind == ekLiteral { - let tk: int = expr.tokKind; - if tk == tkIntLiteral { return tyInt; } - if tk == tkFloatLiteral { return tyFloat64; } - if tk == tkStringLiteral { return tyStr; } - if tk == tkBoolLiteral { return tyBool; } - if tk == tkCharLiteral { return tyChar32; } - if tk == tkNull { return tyPointer; } - return tyUnknown; - } - - // Identifier — look up in scope - if kind == ekIdent { - let sym: Symbol = Scope_Lookup(sema.scope, expr.strValue); - if sym.kind == 0 && !String_Eq(sym.name, expr.strValue) { - Sema_EmitError(sema, expr.line, expr.column, "undeclared identifier"); - return tyUnknown; - } - if sym.typeName != null as String && !String_Eq(sym.typeName, "") { - let te: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; - te.kind = tekNamed; - te.typeName = sym.typeName; - expr.refType = te; - } - return sym.typeKind; - } - - // self - if kind == ekSelf { - let sym: Symbol = Scope_Lookup(sema.scope, "self"); - if sym.kind == 0 && !String_Eq(sym.name, "self") { - Sema_EmitError(sema, expr.line, expr.column, "self outside method"); - return tyUnknown; - } - return sym.typeKind; - } - - // Binary - if kind == ekBinary { - let left: int = Sema_CheckExpr(sema, expr.child1); - let right: int = Sema_CheckExpr(sema, expr.child2); - let op: int = expr.intValue; - - // Assignment operators return target type - if op == tkAssign || (op >= tkPlusAssign && op <= tkShrAssign) { - return left; - } - // Comparison operators return bool - if op >= tkEq && op <= tkGe { return tyBool; } - // Logical operators return bool - if op == tkAmpAmp || op == tkPipePipe || op == tkBang { return tyBool; } - // Arithmetic returns wider type - if !Sema_IsNumeric(left) || !Sema_IsNumeric(right) { - Sema_EmitError(sema, expr.line, expr.column, "arithmetic requires numeric operands"); - } - if left == tyFloat64 || right == tyFloat64 { return tyFloat64; } - return tyInt; - } - - // Unary - if kind == ekUnary { - let operand: int = Sema_CheckExpr(sema, expr.child1); - let op: int = expr.intValue; - if op == tkBang { return tyBool; } - if op == tkStar { return tyUnknown; } // dereference — resolve pointee - if op == tkAmp { return tyPointer; } - return operand; - } - - // Assign - if kind == ekAssign { - let target: int = Sema_CheckExpr(sema, expr.child1); - let value: int = Sema_CheckExpr(sema, expr.child2); - // Basic type compatibility (permissive for now) - return target; - } - - // Call - if kind == ekCall { - discard Sema_CheckExpr(sema, expr.child1); - var arg: *ExprList = expr.callArgs; - while arg != null as *ExprList { - discard Sema_CheckExpr(sema, arg.expr); - arg = arg.next; - } - // Try to resolve return type from function declaration - if expr.child1.kind == ekIdent { - let sym: Symbol = Scope_Lookup(sema.scope, expr.child1.strValue); - if sym.kind == skFunc && sym.decl != null as *Decl && sym.decl.retType != null as *TypeExpr { - return Sema_ResolveType(sema, sym.decl.retType); - } - } - return tyUnknown; - } - - // Ternary - if kind == ekTernary { - return Sema_CheckExpr(sema, expr.child2); // then type - } - - // Cast — return target type - if kind == ekCast { - if expr.refType != null as *TypeExpr { - return Sema_ResolveType(sema, expr.refType); - } - return tyUnknown; - } - - // Try (?) - if kind == ekTry { - let inner: int = Sema_CheckExpr(sema, expr.child1); - return inner; // simplified - } - - // Struct init: TypeName { field: value, ... } - if kind == ekStructInit { - return tyNamed; - } - - // Field access - if kind == ekField { - discard Sema_CheckExpr(sema, expr.child1); - return tyUnknown; - } - - // Index - if kind == ekIndex { - let obj: int = Sema_CheckExpr(sema, expr.child1); - let idx: int = Sema_CheckExpr(sema, expr.child2); - if !Sema_IsNumeric(idx) && idx != tyUnknown { - Sema_EmitError(sema, expr.line, expr.column, "index must be integer"); - } - return tyUnknown; - } - - // Block expression - if kind == ekBlock { - if expr.refBlock != null as *Block { - Sema_CheckBlock(sema, expr.refBlock); - } - return tyVoid; - } - - return tyUnknown; -} - -// --------------------------------------------------------------------------- -// Statement checking -// --------------------------------------------------------------------------- - -func Sema_CheckStmt(sema: *Sema, stmt: *Stmt) { - if stmt == null as *Stmt { return; } - let kind: int = stmt.kind; - - // Let/var - if kind == skLet { - let initType: int = Sema_CheckExpr(sema, stmt.child1); - // Register variable in scope - var sym: Symbol; - sym.kind = skVar; - sym.name = stmt.strValue; - sym.typeKind = initType; - sym.typeName = ""; - if stmt.refStmtType != null as *TypeExpr { - sym.typeName = stmt.refStmtType.typeName; - } - sym.isMutable = stmt.boolValue; - sym.isPublic = false; - sym.decl = null as *Decl; - discard Scope_Define(sema.scope, sym); - return; - } - - // Return - if kind == skReturn { - if stmt.child1 != null as *Expr { - let retType: int = Sema_CheckExpr(sema, stmt.child1); - if sema.currentRetType != tyUnknown && retType != tyUnknown { - if retType != sema.currentRetType { - // Be permissive: allow numeric widening, pointer compatibility - if !Sema_IsNumeric(retType) || !Sema_IsNumeric(sema.currentRetType) { - // Allow pointer-type compatibility (String <-> *T, *T <-> *U) - let retIsPtr: bool = retType == tyPointer || retType == tyStr; - let expectedIsPtr: bool = sema.currentRetType == tyPointer || sema.currentRetType == tyStr; - if !retIsPtr || !expectedIsPtr { - Sema_EmitError(sema, stmt.line, stmt.column, "return type mismatch"); - } - } - } - } - } else { - if sema.currentRetType != tyVoid && sema.currentRetType != tyUnknown { - Sema_EmitError(sema, stmt.line, stmt.column, "missing return value"); - } - } - return; - } - - // If - if kind == skIf { - let condType: int = Sema_CheckExpr(sema, stmt.child1); - if !Sema_IsBool(condType) && condType != tyUnknown { - Sema_EmitError(sema, stmt.line, stmt.column, "if condition must be bool"); - } - Sema_CheckBlock(sema, stmt.refStmtBlock); - Sema_CheckBlock(sema, stmt.refStmtElse); - return; - } - - // While - if kind == skWhile { - let condType: int = Sema_CheckExpr(sema, stmt.child1); - if !Sema_IsBool(condType) && condType != tyUnknown { - Sema_EmitError(sema, stmt.line, stmt.column, "while condition must be bool"); - } - Sema_CheckBlock(sema, stmt.refStmtBlock); - return; - } - - // Do-while - if kind == skDoWhile { - Sema_CheckBlock(sema, stmt.refStmtBlock); - let condType: int = Sema_CheckExpr(sema, stmt.child1); - if !Sema_IsBool(condType) && condType != tyUnknown { - Sema_EmitError(sema, stmt.line, stmt.column, "do-while condition must be bool"); - } - return; - } - - // Loop - if kind == skLoop { - Sema_CheckBlock(sema, stmt.refStmtBlock); - return; - } - - // For - if kind == skFor { - discard Sema_CheckExpr(sema, stmt.child1); - var forScope: Scope = Scope_NewChild(sema.scope); - var loopSym: Symbol; - loopSym.kind = skVar; - loopSym.name = stmt.strValue; - loopSym.typeKind = tyUnknown; - loopSym.typeName = ""; - loopSym.isMutable = true; - loopSym.isPublic = false; - loopSym.decl = null as *Decl; - discard Scope_Define(&forScope, loopSym); - let prevScope: *Scope = sema.scope; - sema.scope = &forScope; - Sema_CheckBlock(sema, stmt.refStmtBlock); - sema.scope = prevScope; - return; - } - - // Match - if kind == skMatch { - discard Sema_CheckExpr(sema, stmt.child1); - return; - } - - // Break / Continue - if kind == skBreak || kind == skContinue { - return; - } - - // Expression statement - if kind == skExpr && stmt.child1 != null as *Expr { - discard Sema_CheckExpr(sema, stmt.child1); - return; - } - - // Decl (nested) - if kind == skDecl { - if stmt.refStmtDecl != null as *Decl && stmt.refStmtDecl.kind == dkFunc { - Sema_EmitError(sema, stmt.line, stmt.column, "nested functions not yet supported"); - } - return; - } -} - -// --------------------------------------------------------------------------- -// Collect globals (register functions, structs, enums in scope) -// --------------------------------------------------------------------------- - -func Sema_CollectGlobals(sema: *Sema) { - var decl: *Decl = sema.module.firstItem; - var funcCount: int = 0; - while decl != null as *Decl { - let dk: int = decl.kind; - - // Function - if dk == dkFunc { - var sym: Symbol; - Sema_ZeroInitSymbol(&sym); - sym.kind = skFunc; - sym.name = decl.strValue; - sym.typeKind = tyFunc; - sym.isPublic = decl.isPublic; - sym.decl = decl; - discard Scope_Define(sema.scope, sym); - } - - // Struct - if dk == dkStruct { - var sym: Symbol; - Sema_ZeroInitSymbol(&sym); - sym.kind = skType; - sym.name = decl.strValue; - sym.typeKind = tyNamed; - sym.isPublic = decl.isPublic; - sym.decl = decl; - discard Scope_Define(sema.scope, sym); - } - - // Enum - if dk == dkEnum { - var sym: Symbol; - Sema_ZeroInitSymbol(&sym); - sym.kind = skType; - sym.name = decl.strValue; - sym.typeKind = tyNamed; - sym.isPublic = decl.isPublic; - sym.decl = decl; - discard Scope_Define(sema.scope, sym); - - // Register enum variants as constants - var vi: int = 0; - while vi < decl.variantCount && vi < 8 { - var v: EnumVariant; - if vi == 0 { v = decl.variant0; } - else if vi == 1 { v = decl.variant1; } - else if vi == 2 { v = decl.variant2; } - else if vi == 3 { v = decl.variant3; } - else if vi == 4 { v = decl.variant4; } - else if vi == 5 { v = decl.variant5; } - else if vi == 6 { v = decl.variant6; } - else if vi == 7 { v = decl.variant7; } - let variantName: String = String_Concat(decl.strValue, "_"); - let variantName2: String = String_Concat(variantName, v.name); - var vSym: Symbol; - vSym.kind = skConst; - vSym.name = variantName2; - vSym.typeKind = tyNamed; - vSym.typeName = String_Concat(decl.strValue, "_Tag"); - vSym.isMutable = false; - vSym.isPublic = decl.isPublic; - vSym.decl = decl; - discard Scope_Define(sema.scope, vSym); - vi = vi + 1; - } - } - - // 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) - if dk == dkUse { - if decl.useKind == 2 { - // Multi-import: names are comma-separated in useNames - // For now, register the whole useNames string as a single func name - // (permissive fallback — real fix needs string split) - let namesStr: String = decl.useNames; - if !String_Eq(namesStr, "") { - // Simple split by comma using available string functions - var start: uint = 0; - var pos: uint = 0; - let totalLen: uint = String_Len(namesStr); - while pos <= totalLen { - let atEnd: bool = pos == totalLen; - let isComma: bool = false; - if pos < totalLen { - // Check if character at pos is comma - let chStr: String = bux_str_slice(namesStr, pos, 1); - isComma = String_Eq(chStr, ","); - } - if atEnd || isComma { - let nameLen: uint = pos - start; - if nameLen > 0 { - let name: String = bux_str_slice(namesStr, start, nameLen); - var sym: Symbol; - sym.kind = skFunc; - sym.name = name; - sym.typeKind = tyUnknown; - sym.typeName = ""; - sym.isMutable = false; - sym.isPublic = true; - sym.decl = null as *Decl; - discard Scope_Define(sema.scope, sym); - } - start = pos + 1; - } - pos = pos + 1; - } - } - } else { - // Single import or glob — add last path segment - let path: String = decl.usePath; - if !String_Eq(path, "") { - // Find last :: segment - var lastSeg: String = path; - let containsColons: int = bux_str_contains(path, "::"); - if containsColons != 0 { - // Try to get last segment by finding last :: - // Use a simple heuristic: slice from various positions - let pathLen: uint = String_Len(path); - var tryPos: uint = 0; - while tryPos < pathLen { - let slice: String = bux_str_slice(path, tryPos, pathLen - tryPos); - if String_StartsWith(slice, "::") { - lastSeg = bux_str_slice(slice, 2, String_Len(slice) - 2); - } - tryPos = tryPos + 1; - } - } - var sym: Symbol; - sym.kind = skFunc; - sym.name = lastSeg; - sym.typeKind = tyUnknown; - sym.typeName = ""; - sym.isMutable = false; - sym.isPublic = true; - sym.decl = null as *Decl; - discard Scope_Define(sema.scope, sym); - } - } - } - - // Const - if dk == dkConst { - var sym: Symbol; - Sema_ZeroInitSymbol(&sym); - sym.kind = skConst; - sym.name = decl.strValue; - sym.typeKind = tyUnknown; - sym.isPublic = decl.isPublic; - sym.decl = decl; - discard Scope_Define(sema.scope, sym); - } - - // Extern function - if dk == dkExternFunc { - var sym: Symbol; - Sema_ZeroInitSymbol(&sym); - sym.kind = skFunc; - sym.name = decl.strValue; - sym.typeKind = tyFunc; - sym.isPublic = true; - sym.decl = decl; - discard Scope_Define(sema.scope, sym); - } - - decl = decl.childDecl2; - } -} - -// --------------------------------------------------------------------------- -// Analyze — main entry point -// --------------------------------------------------------------------------- - -func Sema_Analyze(mod: *Module) -> *Sema { - let s: *Sema = bux_alloc(sizeof(Sema)) as *Sema; - s.module = mod; - s.scope = bux_alloc(sizeof(Scope)) as *Scope; - s.scope.symbols = bux_alloc(1024 as uint * sizeof(Symbol)) as *Symbol; - s.scope.count = 0; - s.scope.parent = null as *Scope; - s.hasError = false; - s.diagCount = 0; - s.diags = bux_alloc(256 as uint * sizeof(SemaDiag)) as *SemaDiag; - s.typeTable = null as *void; - s.methodTable = null as *void; - s.currentRetType = tyVoid; - - // First pass: collect globals - Sema_CollectGlobals(s); - - // Second pass: check function bodies - var decl: *Decl = mod.firstItem; - while decl != null as *Decl { - if decl.kind == dkFunc && decl.refBody != null as *Block { - // Create function scope (child of global) - var funcScope: Scope = Scope_NewChild(s.scope); - - // Add type params to scope - if decl.typeParamCount >= 1 { - var tpSym: Symbol; - Sema_ZeroInitSymbol(&tpSym); - tpSym.kind = skType; - tpSym.name = decl.typeParam0; - tpSym.typeKind = tyTypeParam; - tpSym.decl = null as *Decl; - discard Scope_Define(&funcScope, tpSym); - } - if decl.typeParamCount >= 2 { - var tpSym2: Symbol; - Sema_ZeroInitSymbol(&tpSym2); - tpSym2.kind = skType; - tpSym2.name = decl.typeParam1; - tpSym2.typeKind = tyTypeParam; - tpSym2.decl = null as *Decl; - discard Scope_Define(&funcScope, tpSym2); - } - - // Add parameters to scope - var i: int = 0; - while i < decl.paramCount { - var p: *Param = null as *Param; - if i == 0 { p = &decl.param0; } - else if i == 1 { p = &decl.param1; } - else if i == 2 { p = &decl.param2; } - else if i == 3 { p = &decl.param3; } - else if i == 4 { p = &decl.param4; } - else if i == 5 { p = &decl.param5; } - else if i == 6 { p = &decl.param6; } - else if i == 7 { p = &decl.param7; } - else if i == 8 { p = &decl.param8; } - var pSym: Symbol; - Sema_ZeroInitSymbol(&pSym); - pSym.kind = skVar; - if p != null as *Param && p.refParamType != null as *TypeExpr { - pSym.typeKind = Sema_ResolveType(s, p.refParamType); - pSym.typeName = p.refParamType.typeName; - } else { - pSym.typeKind = tyInt; - pSym.typeName = ""; - } - pSym.isMutable = false; - pSym.isPublic = false; - pSym.decl = null as *Decl; - if i == 0 { pSym.name = decl.param0.name; } - else if i == 1 { pSym.name = decl.param1.name; } - else if i == 2 { pSym.name = decl.param2.name; } - else if i == 3 { pSym.name = decl.param3.name; } - else if i == 4 { pSym.name = decl.param4.name; } - else if i == 5 { pSym.name = decl.param5.name; } - else if i == 6 { pSym.name = decl.param6.name; } - else if i == 7 { pSym.name = decl.param7.name; } - else if i == 8 { pSym.name = decl.param8.name; } - discard Scope_Define(&funcScope, pSym); - i = i + 1; - } - - // Switch to function scope and check body statements - let prevScope: *Scope = s.scope; - s.scope = &funcScope; - - // Set current function return type - if decl.retType != null as *TypeExpr { - s.currentRetType = Sema_ResolveType(s, decl.retType); - } else { - s.currentRetType = tyVoid; - } - - // Check body statements - var stmt: *Stmt = decl.refBody.firstStmt; - while stmt != null as *Stmt { - Sema_CheckStmt(s, stmt); - stmt = stmt.nextStmt; - } - - s.scope = prevScope; - } - decl = decl.childDecl2; - } - - return s; -} - -func Sema_HasError(sema: *Sema) -> bool { - return sema.hasError; -} - -func Sema_DiagCount(sema: *Sema) -> int { - return sema.diagCount; -} - -func Sema_Free(sema: *Sema) { - bux_free(sema.scope.symbols as *void); - bux_free(sema.scope as *void); - bux_free(sema.diags as *void); - bux_free(sema as *void); -} - -} diff --git a/_selfhost/src/source_location.bux b/_selfhost/src/source_location.bux deleted file mode 100644 index 3d01e5e..0000000 --- a/_selfhost/src/source_location.bux +++ /dev/null @@ -1,13 +0,0 @@ -// source_location.bux — Source position tracking -module SourceLocation { - -struct SourceLocation { - line: uint32; - column: uint32; - offset: uint32; -} - -func SourceLocation_New(line: uint32, column: uint32, offset: uint32) -> SourceLocation { - return SourceLocation { line: line, column: column, offset: offset }; -} -} diff --git a/_selfhost/src/token.bux b/_selfhost/src/token.bux deleted file mode 100644 index f20fd7e..0000000 --- a/_selfhost/src/token.bux +++ /dev/null @@ -1,322 +0,0 @@ -// token.bux — Token kinds and helpers -module Token { - -// --------------------------------------------------------------------------- -// TokenKind enum -// --------------------------------------------------------------------------- - -// Literals -const tkIntLiteral: int = 0; -const tkFloatLiteral: int = 1; -const tkStringLiteral: int = 2; -const tkCharLiteral: int = 3; -const tkBoolLiteral: int = 4; - -// Identifiers -const tkIdent: int = 5; -const tkUnderscore: int = 6; - -// Control flow keywords -const tkIf: int = 7; -const tkElse: int = 8; -const tkWhile: int = 9; -const tkDo: int = 10; -const tkLoop: int = 11; -const tkFor: int = 12; -const tkIn: int = 13; -const tkBreak: int = 14; -const tkContinue: int = 15; -const tkReturn: int = 16; -const tkMatch: int = 17; - -// Declaration keywords -const tkFunc: int = 18; -const tkLet: int = 19; -const tkVar: int = 20; -const tkConst: int = 21; -const tkType: int = 22; -const tkStruct: int = 23; -const tkEnum: int = 24; -const tkUnion: int = 25; -const tkInterface: int = 26; -const tkExtend: int = 27; -const tkModule: int = 28; -const tkImport: int = 29; -const tkPub: int = 30; -const tkExtern: int = 31; - -// Other keywords -const tkAs: int = 32; -const tkIs: int = 33; -const tkNull: int = 34; -const tkSelf: int = 35; -const tkSuper: int = 36; -const tkSizeOf: int = 37; - -// Punctuation -const tkLParen: int = 38; -const tkRParen: int = 39; -const tkLBrace: int = 40; -const tkRBrace: int = 41; -const tkLBracket: int = 42; -const tkRBracket: int = 43; -const tkComma: int = 44; -const tkSemicolon: int = 45; -const tkColon: int = 46; -const tkColonColon: int = 47; -const tkDot: int = 48; -const tkDotDot: int = 49; -const tkDotDotDot: int = 50; -const tkDotDotEqual: int = 51; -const tkArrow: int = 52; -const tkFatArrow: int = 53; -const tkAt: int = 54; -const tkHash: int = 55; -const tkQuestion: int = 56; - -// Arithmetic operators -const tkPlus: int = 57; -const tkMinus: int = 58; -const tkStar: int = 59; -const tkSlash: int = 60; -const tkPercent: int = 61; -const tkStarStar: int = 62; -const tkPlusPlus: int = 63; -const tkMinusMinus: int = 64; - -// Bitwise operators -const tkAmp: int = 65; -const tkPipe: int = 66; -const tkCaret: int = 67; -const tkTilde: int = 68; -const tkShl: int = 69; -const tkShr: int = 70; - -// Logical operators -const tkAmpAmp: int = 71; -const tkPipePipe: int = 72; -const tkBang: int = 73; - -// Comparison operators -const tkEq: int = 74; -const tkNe: int = 75; -const tkLt: int = 76; -const tkLe: int = 77; -const tkGt: int = 78; -const tkGe: int = 79; - -// Assignment operators -const tkAssign: int = 80; -const tkPlusAssign: int = 81; -const tkMinusAssign: int = 82; -const tkStarAssign: int = 83; -const tkSlashAssign: int = 84; -const tkPercentAssign: int = 85; -const tkAmpAssign: int = 86; -const tkPipeAssign: int = 87; -const tkCaretAssign: int = 88; -const tkShlAssign: int = 89; -const tkShrAssign: int = 90; - -// Compile-time intrinsics -const tkHashLine: int = 91; -const tkHashColumn: int = 92; -const tkHashFile: int = 93; -const tkHashFunction: int = 94; -const tkHashDate: int = 95; -const tkHashTime: int = 96; -const tkHashModule: int = 97; - -// Special -const tkOwn: int = 98; -const tkNewLine: int = 99; -const tkEndOfFile: int = 100; -const tkUnknown: int = 101; - -// Async / concurrency -const tkAsync: int = 102; -const tkAwait: int = 103; -const tkSpawn: int = 104; -const tkDiscard: int = 105; - -// --------------------------------------------------------------------------- -// Token struct -// --------------------------------------------------------------------------- - -struct Token { - kind: int; - text: String; - line: uint32; - column: uint32; - offset: uint32; -} - -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - -func Token_IsKeyword(kind: int) -> bool { - if kind >= tkIf && kind <= tkIn { return true; } - if kind >= tkBreak && kind <= tkMatch { return true; } - if kind >= tkFunc && kind <= tkExtern { return true; } - if kind >= tkAs && kind <= tkSuper { return true; } - if kind == tkSizeOf { return true; } - if kind >= tkAsync && kind <= tkSpawn { return true; } - return false; -} - -func Token_IsLiteral(kind: int) -> bool { - if kind >= tkIntLiteral && kind <= tkBoolLiteral { return true; } - return false; -} - -func Token_IsOperator(kind: int) -> bool { - if kind >= tkPlus && kind <= tkShrAssign { return true; } - return false; -} - -func Token_IsEof(kind: int) -> bool { - return kind == tkEndOfFile; -} - -func Token_KeywordKind(text: String) -> int { - if String_Eq(text, "func") { return tkFunc; } - if String_Eq(text, "let") { return tkLet; } - if String_Eq(text, "var") { return tkVar; } - if String_Eq(text, "const") { return tkConst; } - if String_Eq(text, "type") { return tkType; } - if String_Eq(text, "struct") { return tkStruct; } - if String_Eq(text, "enum") { return tkEnum; } - if String_Eq(text, "union") { return tkUnion; } - if String_Eq(text, "interface") { return tkInterface; } - if String_Eq(text, "extend") { return tkExtend; } - if String_Eq(text, "module") { return tkModule; } - if String_Eq(text, "import") { return tkImport; } - if String_Eq(text, "pub") { return tkPub; } - if String_Eq(text, "extern") { return tkExtern; } - if String_Eq(text, "if") { return tkIf; } - if String_Eq(text, "else") { return tkElse; } - if String_Eq(text, "while") { return tkWhile; } - if String_Eq(text, "do") { return tkDo; } - if String_Eq(text, "loop") { return tkLoop; } - if String_Eq(text, "for") { return tkFor; } - if String_Eq(text, "in") { return tkIn; } - if String_Eq(text, "break") { return tkBreak; } - if String_Eq(text, "continue") { return tkContinue; } - if String_Eq(text, "return") { return tkReturn; } - if String_Eq(text, "match") { return tkMatch; } - if String_Eq(text, "as") { return tkAs; } - if String_Eq(text, "is") { return tkIs; } - if String_Eq(text, "null") { return tkNull; } - if String_Eq(text, "self") { return tkSelf; } - if String_Eq(text, "super") { return tkSuper; } - if String_Eq(text, "sizeof") { return tkSizeOf; } - if String_Eq(text, "true") { return tkBoolLiteral; } - if String_Eq(text, "false") { return tkBoolLiteral; } - return tkIdent; -} - -func Token_KindName(kind: int) -> String { - if kind == tkIntLiteral { return "integer literal"; } - if kind == tkFloatLiteral { return "float literal"; } - if kind == tkStringLiteral { return "string literal"; } - if kind == tkCharLiteral { return "char literal"; } - if kind == tkBoolLiteral { return "boolean literal"; } - if kind == tkIdent { return "identifier"; } - if kind == tkUnderscore { return "_"; } - if kind == tkSizeOf { return "sizeof"; } - if kind == tkIf { return "if"; } - if kind == tkElse { return "else"; } - if kind == tkWhile { return "while"; } - if kind == tkDo { return "do"; } - if kind == tkLoop { return "loop"; } - if kind == tkFor { return "for"; } - if kind == tkIn { return "in"; } - if kind == tkBreak { return "break"; } - if kind == tkContinue { return "continue"; } - if kind == tkReturn { return "return"; } - if kind == tkMatch { return "match"; } - if kind == tkFunc { return "func"; } - if kind == tkLet { return "let"; } - if kind == tkVar { return "var"; } - if kind == tkConst { return "const"; } - if kind == tkType { return "type"; } - if kind == tkStruct { return "struct"; } - if kind == tkEnum { return "enum"; } - if kind == tkUnion { return "union"; } - if kind == tkInterface { return "interface"; } - if kind == tkExtend { return "extend"; } - if kind == tkModule { return "module"; } - if kind == tkImport { return "import"; } - if kind == tkPub { return "pub"; } - if kind == tkExtern { return "extern"; } - if kind == tkAs { return "as"; } - if kind == tkIs { return "is"; } - if kind == tkNull { return "null"; } - if kind == tkSelf { return "self"; } - if kind == tkSuper { return "super"; } - if kind == tkLParen { return "("; } - if kind == tkRParen { return ")"; } - if kind == tkLBrace { return "{"; } - if kind == tkRBrace { return "}"; } - if kind == tkLBracket { return "["; } - if kind == tkRBracket { return "]"; } - if kind == tkComma { return ","; } - if kind == tkSemicolon { return ";"; } - if kind == tkColon { return ":"; } - if kind == tkColonColon { return "::"; } - if kind == tkDot { return "."; } - if kind == tkDotDot { return ".."; } - if kind == tkDotDotDot { return "..."; } - if kind == tkDotDotEqual { return "..="; } - if kind == tkArrow { return "->"; } - if kind == tkFatArrow { return "=>"; } - if kind == tkAt { return "@"; } - if kind == tkHash { return "#"; } - if kind == tkQuestion { return "?"; } - if kind == tkPlus { return "+"; } - if kind == tkMinus { return "-"; } - if kind == tkStar { return "*"; } - if kind == tkSlash { return "/"; } - if kind == tkPercent { return "%"; } - if kind == tkStarStar { return "**"; } - if kind == tkPlusPlus { return "++"; } - if kind == tkMinusMinus { return "--"; } - if kind == tkAmp { return "&"; } - if kind == tkPipe { return "|"; } - if kind == tkCaret { return "^"; } - if kind == tkTilde { return "~"; } - if kind == tkShl { return "<<"; } - if kind == tkShr { return ">>"; } - if kind == tkAmpAmp { return "&&"; } - if kind == tkPipePipe { return "||"; } - if kind == tkBang { return "!"; } - if kind == tkEq { return "=="; } - if kind == tkNe { return "!="; } - if kind == tkLt { return "<"; } - if kind == tkLe { return "<="; } - if kind == tkGt { return ">"; } - if kind == tkGe { return ">="; } - if kind == tkAssign { return "="; } - if kind == tkPlusAssign { return "+="; } - if kind == tkMinusAssign { return "-="; } - if kind == tkStarAssign { return "*="; } - if kind == tkSlashAssign { return "/="; } - if kind == tkPercentAssign { return "%="; } - if kind == tkAmpAssign { return "&="; } - if kind == tkPipeAssign { return "|="; } - if kind == tkCaretAssign { return "^="; } - if kind == tkShlAssign { return "<<="; } - if kind == tkShrAssign { return ">>="; } - if kind == tkHashLine { return "#line"; } - if kind == tkHashColumn { return "#column"; } - if kind == tkHashFile { return "#file"; } - if kind == tkHashFunction { return "#function"; } - if kind == tkHashDate { return "#date"; } - if kind == tkHashTime { return "#time"; } - if kind == tkHashModule { return "#module"; } - if kind == tkNewLine { return "newline"; } - if kind == tkEndOfFile { return "end of file"; } - return "unknown token"; -} -} diff --git a/_selfhost/src/types.bux b/_selfhost/src/types.bux deleted file mode 100644 index f3bc6de..0000000 --- a/_selfhost/src/types.bux +++ /dev/null @@ -1,188 +0,0 @@ -// types.bux — Type system definitions and factories -module Types { - -// --------------------------------------------------------------------------- -// TypeKind constants -// --------------------------------------------------------------------------- - -const tyUnknown: int = 0; -const tyVoid: int = 1; -const tyBool: int = 2; -const tyBool8: int = 3; -const tyBool16: int = 4; -const tyBool32: int = 5; -const tyChar8: int = 6; -const tyChar16: int = 7; -const tyChar32: int = 8; -const tyStr: int = 9; -const tyInt8: int = 10; -const tyInt16: int = 11; -const tyInt32: int = 12; -const tyInt64: int = 13; -const tyInt: int = 14; -const tyUInt8: int = 15; -const tyUInt16: int = 16; -const tyUInt32: int = 17; -const tyUInt64: int = 18; -const tyUInt: int = 19; -const tyFloat32: int = 20; -const tyFloat64: int = 21; -const tyPointer: int = 22; -const tySlice: int = 23; -const tyRange: int = 24; -const tyTuple: int = 25; -const tyNamed: int = 26; -const tyTypeParam: int = 27; -const tyFunc: int = 28; - -// --------------------------------------------------------------------------- -// Type struct -// --------------------------------------------------------------------------- - -struct Type { - kind: int; - name: String; - // inner types stored as array of pointers (simplified) - innerKind1: int; - innerName1: String; - innerKind2: int; - innerName2: String; - innerKind3: int; - innerName3: String; - innerCount: int; -} - -// --------------------------------------------------------------------------- -// Factories -// --------------------------------------------------------------------------- - -func Type_MakeUnknown() -> Type { - return Type { kind: tyUnknown, name: "", innerCount: 0, - innerKind1: 0, innerName1: "", innerKind2: 0, innerName2: "", - innerKind3: 0, innerName3: "" }; -} - -func Type_MakeVoid() -> Type { - return Type { kind: tyVoid, name: "void", innerCount: 0, - innerKind1: 0, innerName1: "", innerKind2: 0, innerName2: "", - innerKind3: 0, innerName3: "" }; -} - -func Type_MakeBool() -> Type { - return Type { kind: tyBool, name: "bool", innerCount: 0, - innerKind1: 0, innerName1: "", innerKind2: 0, innerName2: "", - innerKind3: 0, innerName3: "" }; -} - -func Type_MakeInt() -> Type { - return Type { kind: tyInt, name: "int", innerCount: 0, - innerKind1: 0, innerName1: "", innerKind2: 0, innerName2: "", - innerKind3: 0, innerName3: "" }; -} - -func Type_MakeInt64() -> Type { - return Type { kind: tyInt64, name: "int64", innerCount: 0, - innerKind1: 0, innerName1: "", innerKind2: 0, innerName2: "", - innerKind3: 0, innerName3: "" }; -} - -func Type_MakeUInt() -> Type { - return Type { kind: tyUInt, name: "uint", innerCount: 0, - innerKind1: 0, innerName1: "", innerKind2: 0, innerName2: "", - innerKind3: 0, innerName3: "" }; -} - -func Type_MakeFloat64() -> Type { - return Type { kind: tyFloat64, name: "float64", innerCount: 0, - innerKind1: 0, innerName1: "", innerKind2: 0, innerName2: "", - innerKind3: 0, innerName3: "" }; -} - -func Type_MakeStr() -> Type { - return Type { kind: tyStr, name: "String", innerCount: 0, - innerKind1: 0, innerName1: "", innerKind2: 0, innerName2: "", - innerKind3: 0, innerName3: "" }; -} - -func Type_MakePointer(pointee: Type) -> Type { - return Type { kind: tyPointer, name: "", innerCount: 1, - innerKind1: pointee.kind, innerName1: pointee.name, - innerKind2: 0, innerName2: "", innerKind3: 0, innerName3: "" }; -} - -func Type_MakeNamed(name: String) -> Type { - return Type { kind: tyNamed, name: name, innerCount: 0, - innerKind1: 0, innerName1: "", innerKind2: 0, innerName2: "", - innerKind3: 0, innerName3: "" }; -} - -func Type_MakeTypeParam(name: String) -> Type { - return Type { kind: tyTypeParam, name: name, innerCount: 0, - innerKind1: 0, innerName1: "", innerKind2: 0, innerName2: "", - innerKind3: 0, innerName3: "" }; -} - -// --------------------------------------------------------------------------- -// Predicates -// --------------------------------------------------------------------------- - -func Type_IsNumeric(t: Type) -> bool { - let k: int = t.kind; - if k == tyInt8 || k == tyInt16 || k == tyInt32 || k == tyInt64 || k == tyInt { return true; } - if k == tyUInt8 || k == tyUInt16 || k == tyUInt32 || k == tyUInt64 || k == tyUInt { return true; } - if k == tyFloat32 || k == tyFloat64 { return true; } - if k == tyUnknown || k == tyNamed || k == tyTypeParam { return true; } - return false; -} - -func Type_IsInteger(t: Type) -> bool { - let k: int = t.kind; - if k == tyInt8 || k == tyInt16 || k == tyInt32 || k == tyInt64 || k == tyInt { return true; } - if k == tyUInt8 || k == tyUInt16 || k == tyUInt32 || k == tyUInt64 || k == tyUInt { return true; } - if k == tyUnknown || k == tyNamed || k == tyTypeParam { return true; } - return false; -} - -func Type_IsBool(t: Type) -> bool { - let k: int = t.kind; - return k == tyBool || k == tyBool8 || k == tyBool16 || k == tyBool32; -} - -func Type_IsPointer(t: Type) -> bool { - return t.kind == tyPointer; -} - -func Type_IsSlice(t: Type) -> bool { - return t.kind == tySlice; -} - -// --------------------------------------------------------------------------- -// Comparison (structural, limited to kind + name for simplicity) -// --------------------------------------------------------------------------- - -func Type_Eq(a: Type, b: Type) -> bool { - if a.kind != b.kind { return false; } - if a.kind == tyNamed || a.kind == tyTypeParam { - return String_Eq(a.name, b.name); - } - return true; -} - -// --------------------------------------------------------------------------- -// toString -// --------------------------------------------------------------------------- - -func Type_ToString(t: Type) -> String { - if t.kind == tyVoid { return "void"; } - if t.kind == tyBool { return "bool"; } - if t.kind == tyStr { return "String"; } - if t.kind == tyInt { return "int"; } - if t.kind == tyInt64 { return "int64"; } - if t.kind == tyUInt { return "uint"; } - if t.kind == tyFloat64 { return "float64"; } - if t.kind == tyNamed { return t.name; } - if t.kind == tyTypeParam { return t.name; } - if t.kind == tyPointer { return String_Concat("*", t.innerName1); } - return "?"; -} -} diff --git a/bootstrap/cli.nim b/bootstrap/cli.nim index fa90d4a..16cba1a 100644 --- a/bootstrap/cli.nim +++ b/bootstrap/cli.nim @@ -259,9 +259,9 @@ proc collectDepDecls(lock: Lockfile, root: string, opts: GlobalOptions): seq[Dec proc findStdlibDir(root: string): string = let searchPaths = @[ - getAppDir() / ".." / "library", - getAppDir() / "library", - root / "library", + getAppDir() / ".." / "lib", + getAppDir() / "lib", + root / "lib", ] for path in searchPaths: if dirExists(path): @@ -461,26 +461,27 @@ proc cmdBuild*(args: seq[string], opts: GlobalOptions): int = let cFile = buildDir / "main.c" writeFile(cFile, allCCode) - # Copy runtime and stdlib files + # Copy runtime files (rt/ is sibling of lib/) let stdlibDir = pctx.stdlibDir let runtimeDst = buildDir / "runtime.c" let ioDst = buildDir / "io.c" if stdlibDir == "": printError("stdlib directory not found", useColor) return 1 - - let runtimeSrc = stdlibDir / "runtime" / "runtime.c" + + let baseDir = stdlibDir.parentDir() + let runtimeSrc = baseDir / "rt" / "runtime.c" if fileExists(runtimeSrc): copyFile(runtimeSrc, runtimeDst) else: - printError("runtime.c not found in library/runtime/", useColor) + printError("runtime.c not found in rt/", useColor) return 1 - - let ioSrc = stdlibDir / "runtime" / "io.c" + + let ioSrc = baseDir / "rt" / "io.c" if fileExists(ioSrc): copyFile(ioSrc, ioDst) else: - printError("io.c not found in library/runtime/", useColor) + printError("io.c not found in rt/", useColor) return 1 # Compile with cc diff --git a/bux.toml b/bux.toml index 8eee9db..1418e5e 100644 --- a/bux.toml +++ b/bux.toml @@ -1,6 +1,6 @@ [Package] Name = "buxc" -Version = "0.1.0" +Version = "0.3.0" Type = "bin" [Build] diff --git a/compiler/tests/borrow_test b/compiler/tests/borrow_test deleted file mode 100755 index 5993056..0000000 Binary files a/compiler/tests/borrow_test and /dev/null differ diff --git a/compiler/tests/hir_test b/compiler/tests/hir_test deleted file mode 100755 index e82e323..0000000 Binary files a/compiler/tests/hir_test and /dev/null differ diff --git a/compiler/tests/lexer_test b/compiler/tests/lexer_test deleted file mode 100755 index c15b203..0000000 Binary files a/compiler/tests/lexer_test and /dev/null differ diff --git a/compiler/tests/parser_test b/compiler/tests/parser_test deleted file mode 100755 index e459771..0000000 Binary files a/compiler/tests/parser_test and /dev/null differ diff --git a/compiler/tests/sema_test b/compiler/tests/sema_test deleted file mode 100755 index 4ab91b2..0000000 Binary files a/compiler/tests/sema_test and /dev/null differ diff --git a/docs/BuildAndTest.md b/docs/BuildAndTest.md index 3cf194e..1e387f7 100644 --- a/docs/BuildAndTest.md +++ b/docs/BuildAndTest.md @@ -36,7 +36,7 @@ make dev The output is a single binary: `buxc` (bootstrap compiler in Nim). -The self-hosted compiler `buxc2` is built from `compiler/selfhost/*.bux` sources via: +The self-hosted compiler `buxc2` is built from `src/*.bux` sources via: ```bash make selfhost ``` @@ -135,31 +135,39 @@ cd examples_pkg/hello && ../../buxc run ``` bux/ -├── src/ # Bootstrap compiler source (Nim) -│ ├── main.nim # Entry point -│ ├── cli.nim # CLI commands -│ ├── lexer.nim # Tokenizer -│ ├── parser.nim # Parser -│ ├── sema.nim # Semantic analysis -│ ├── hir.nim # High-level IR -│ ├── hir_lower.nim # AST → HIR lowering -│ ├── c_backend.nim # HIR → C code generation +├── src/ # Self-hosted compiler source (Bux) +│ ├── Main.bux # Entry point +│ ├── Cli.bux # CLI commands +│ ├── Lexer.bux # Tokenizer +│ ├── Parser.bux # Parser +│ ├── Ast.bux # AST definitions +│ ├── Sema.bux # Semantic analysis +│ ├── Types.bux # Type system +│ ├── Scope.bux # Symbol table +│ ├── Hir.bux # High-level IR +│ ├── HirLower.bux # AST → HIR lowering +│ ├── CBackend.bux # HIR → C code generation +│ ├── Manifest.bux # bux.toml parser +│ └── Token.bux # Token definitions +├── bootstrap/ # Bootstrap compiler (Nim) — compiles src/ → buxc +│ ├── main.nim +│ ├── cli.nim │ └── ... -├── library/ # Standard library -│ ├── Std/ -│ │ ├── Io.bux -│ │ ├── Array.bux -│ │ ├── String.bux -│ │ ├── Map.bux -│ │ ├── Fs.bux -│ │ ├── Mem.bux -│ │ ├── Set.bux -│ │ ├── Path.bux -│ │ ├── Math.bux -│ │ ├── Task.bux -│ │ └── Channel.bux -│ ├── runtime.c # C runtime shim -│ └── io.c # C I/O functions +├── lib/ # Standard library (Bux) +│ ├── Io.bux +│ ├── Array.bux +│ ├── String.bux +│ ├── Map.bux +│ ├── Fs.bux +│ ├── Mem.bux +│ ├── Set.bux +│ ├── Path.bux +│ ├── Math.bux +│ ├── Task.bux +│ └── Channel.bux +├── rt/ # C runtime +│ ├── runtime.c +│ └── io.c ├── examples/ # Example programs ├── tests/ # Unit tests (Nim) ├── docs/ # Documentation @@ -183,7 +191,7 @@ cat build/main.c ### Inspecting Generated C (self-hosted) ```bash -cd _selfhost && ../buxc build +cd src && ../buxc build cat build/main.c ``` @@ -191,7 +199,7 @@ cat build/main.c | Error | Cause | Fix | |-------|-------|-----| -| `stdlib directory not found` | `buxc` can't find `library/` | Run from project root or set correct path | +| `stdlib directory not found` | `buxc` can't find `lib/` | Run from project root or set correct path | | `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 | diff --git a/src/cli.bux b/src/cli.bux index f4ac134..6920fc0 100644 --- a/src/cli.bux +++ b/src/cli.bux @@ -31,7 +31,7 @@ func DirExists(path: String) -> bool { } // Import the compiler pipeline -// In self-hosting mode, these are compiled together from compiler/selfhost/ +// In self-hosting mode, these are compiled together from src/ // --------------------------------------------------------------------------- // Compile a single .bux source file @@ -139,20 +139,20 @@ func Cli_Build(srcPath: String, outPath: String) -> int { Print(" → C written to "); PrintLine(cFile); - // Find runtime.c and io.c for linking - var rtPath: String = "library/runtime/runtime.c"; - var ioPath: String = "library/runtime/io.c"; + // Find runtime.c and io.c for linking (rt/ directory) + var rtPath: String = "rt/runtime.c"; + var ioPath: String = "rt/io.c"; if !FileExists(rtPath) { - rtPath = "../library/runtime/runtime.c"; + rtPath = "../rt/runtime.c"; } if !FileExists(ioPath) { - ioPath = "../library/runtime/io.c"; + ioPath = "../rt/io.c"; } if !FileExists(rtPath) { - rtPath = "/home/ziko/z-git/bux/bux/library/runtime/runtime.c"; + rtPath = "/home/ziko/z-git/bux/bux/rt/runtime.c"; } if !FileExists(ioPath) { - ioPath = "/home/ziko/z-git/bux/bux/library/runtime/io.c"; + ioPath = "/home/ziko/z-git/bux/bux/rt/io.c"; } // Compile with cc @@ -294,11 +294,11 @@ func Cli_CompileSource(source: String, sourceName: String) -> *HirModule { // --------------------------------------------------------------------------- func Cli_FindStdlibDir(projectDir: String) -> String { - var path: String = bux_path_join(projectDir, "stdlib"); + var path: String = bux_path_join(projectDir, "lib"); if DirExists(path) { return path; } - path = bux_path_join(projectDir, "../stdlib"); + path = bux_path_join(projectDir, "../lib"); if DirExists(path) { return path; } - path = "/home/ziko/z-git/bux/bux/stdlib"; + path = "/home/ziko/z-git/bux/bux/lib"; if DirExists(path) { return path; } return ""; } @@ -377,7 +377,7 @@ func Cli_CollectStdlibImports(mod: *Module, outPaths: *String, maxCount: int, st j = j + 1; } if !String_Eq(modName, "") { - let filePath: String = bux_path_join(bux_path_join(stdlibDir, "Std"), String_Concat(modName, ".bux")); + let filePath: String = bux_path_join(stdlibDir, String_Concat(modName, ".bux")); // Check for duplicates var dup: bool = false; var di: int = 0; @@ -558,31 +558,28 @@ func Cli_BuildProject(projectDir: String) -> int { merged.itemCount = 0; merged.firstItem = null as *Decl; - // Find and merge stdlib declarations (only imported modules) + // Find and merge ALL stdlib declarations let stdlibDir: String = Cli_FindStdlibDir(projectDir); if !String_Eq(stdlibDir, "") { Print("Stdlib found: "); PrintLine(stdlibDir); - // Collect imported stdlib modules from user code - let maxImports: int = 64; - let importPaths: *String = bux_alloc(maxImports * 8) as *String; - let importCount: int = Cli_CollectStdlibImports(userMerged, importPaths, maxImports, stdlibDir); - Print("Imported stdlib modules: "); - PrintInt(importCount); - PrintLine(""); - if importCount > 0 { + // List all .bux files in lib/ + var libCount: int = 0; + let libFiles: *String = bux_list_dir(stdlibDir, ".bux", &libCount); + var stdAdded: int = 0; + if libCount > 0 { var si: int = 0; - var stdAdded: int = 0; - while si < importCount { + while si < libCount { Print(" Merging "); - PrintLine(importPaths[si]); - let added: int = Cli_MergeFileInto(merged, importPaths[si], userNames, userNameCount); + PrintLine(libFiles[si]); + let added: int = Cli_MergeFileInto(merged, libFiles[si], userNames, userNameCount); stdAdded = stdAdded + added; si = si + 1; } - Print("Stdlib declarations added: "); - PrintInt(stdAdded); } + Print("Stdlib declarations added: "); + PrintInt(stdAdded); + PrintLine(""); } // Copy user declarations into merged (user shadows stdlib) @@ -629,20 +626,20 @@ func Cli_BuildProject(projectDir: String) -> int { Print("C code written to "); PrintLine(cFile); - // Find runtime.c and io.c (use stdlibDir if available) - var rtPath: String = bux_path_join(stdlibDir, "runtime.c"); - var ioPath: String = bux_path_join(stdlibDir, "io.c"); + // Find runtime.c and io.c (look in rt/ directory) + var rtPath: String = bux_path_join(projectDir, "rt/runtime.c"); + var ioPath: String = bux_path_join(projectDir, "rt/io.c"); if !FileExists(rtPath) { - rtPath = bux_path_join(projectDir, "library/runtime/runtime.c"); + rtPath = bux_path_join(projectDir, "../rt/runtime.c"); } if !FileExists(ioPath) { - ioPath = bux_path_join(projectDir, "library/runtime/io.c"); + ioPath = bux_path_join(projectDir, "../rt/io.c"); } if !FileExists(rtPath) { - rtPath = bux_path_join(projectDir, "../library/runtime/runtime.c"); + rtPath = bux_path_join(projectDir, "../../rt/runtime.c"); } if !FileExists(ioPath) { - ioPath = bux_path_join(projectDir, "../library/runtime/io.c"); + ioPath = bux_path_join(projectDir, "../../rt/io.c"); } // Compile with cc diff --git a/src/main.bux b/src/main.bux deleted file mode 100644 index 88940f3..0000000 --- a/src/main.bux +++ /dev/null @@ -1,23 +0,0 @@ -// main.bux — Entry point for the Bux self-hosting compiler -module Main { - -// C runtime for command-line args -extern func bux_argc() -> int; -extern func bux_argv(index: int) -> String; -extern func bux_alloc(size: uint) -> *void; - -// Forward declaration from Cli module -func Cli_Run(args: *String, argCount: int) -> int; - -func Main() -> int { - let count: int = bux_argc(); - // Allocate array of String pointers - let args: *String = bux_alloc(count as uint * 8) as *String; - var i: int = 0; - while i < count { - args[i] = bux_argv(i); - i = i + 1; - } - return Cli_Run(args, count); -} -} diff --git a/tests/test_ast_fields.nim b/tests/test_ast_fields.nim index 278d10b..d485fa5 100644 --- a/tests/test_ast_fields.nim +++ b/tests/test_ast_fields.nim @@ -1,6 +1,6 @@ import "../bootstrap/lexer", "../bootstrap/parser", "../bootstrap/ast" -let source = readFile("../_selfhost/src/ast.bux") +let source = readFile("../src/ast.bux") let lexRes = tokenize(source, "ast.bux") let res = parse(lexRes.tokens, "ast.bux") if res.module != nil: diff --git a/tests/test_parse_ast.nim b/tests/test_parse_ast.nim index 32841f4..2cb013e 100644 --- a/tests/test_parse_ast.nim +++ b/tests/test_parse_ast.nim @@ -1,7 +1,7 @@ import "../bootstrap/lexer", "../bootstrap/parser", "../bootstrap/ast", "../bootstrap/token" import std/os -let source = readFile("../_selfhost/src/ast.bux") +let source = readFile("../src/ast.bux") let lexRes = tokenize(source, "ast.bux") echo "Tokens: ", lexRes.tokens.len echo "Errors: ", lexRes.hasErrors