feat: operator overloading in bootstrap compiler
- Add operatorMethodName mapping (tkPlus -> operator_add, etc.) - Add tryLowerOperatorCall / tryLowerIndexCall in HIR lowerer - Modify ekBinary lowering to check method table before builtins - Modify ekAssign lowering to detect ekIndex target and emit operator_index_set method call when available - Add sema type-checking for operator overloads in ekBinary and ekIndex paths (method-table lookup before builtin rules) - Fix sema compile errors: minfo.params[N].typ -> minfo.params[N] - Fix hir_lower forward decl ordering for resolveExprType - Update golden test expected.c files for current C backend - Update ROADMAP.md and PHASE8_STRATEGY.md status
This commit is contained in:
+143
-1
@@ -533,6 +533,103 @@ proc findMethodEntry(ctx: LowerCtx, typeName: string): (string, seq[MethodInfo])
|
|||||||
return (prefix, ctx.methodTable[prefix])
|
return (prefix, ctx.methodTable[prefix])
|
||||||
return ("", @[])
|
return ("", @[])
|
||||||
|
|
||||||
|
proc operatorMethodName(op: TokenKind): string =
|
||||||
|
case op
|
||||||
|
of tkPlus: "operator_add"
|
||||||
|
of tkMinus: "operator_sub"
|
||||||
|
of tkStar: "operator_mul"
|
||||||
|
of tkSlash: "operator_div"
|
||||||
|
of tkPercent: "operator_mod"
|
||||||
|
of tkEq: "operator_eq"
|
||||||
|
of tkNe: "operator_ne"
|
||||||
|
of tkLt: "operator_lt"
|
||||||
|
of tkLe: "operator_le"
|
||||||
|
of tkGt: "operator_gt"
|
||||||
|
of tkGe: "operator_ge"
|
||||||
|
of tkAmp: "operator_bitand"
|
||||||
|
of tkPipe: "operator_bitor"
|
||||||
|
of tkCaret: "operator_xor"
|
||||||
|
of tkShl: "operator_shl"
|
||||||
|
of tkShr: "operator_shr"
|
||||||
|
else: ""
|
||||||
|
|
||||||
|
proc tryLowerOperatorCall(ctx: var LowerCtx, op: TokenKind, leftExpr, rightExpr: Expr, typ: Type, loc: SourceLocation): HirNode =
|
||||||
|
## Try to lower a binary operator to a method call. Returns nil if no overload found.
|
||||||
|
let methodName = operatorMethodName(op)
|
||||||
|
if methodName == "": return nil
|
||||||
|
let receiverType = ctx.resolveExprType(leftExpr)
|
||||||
|
var receiverTypeName = ""
|
||||||
|
if receiverType.kind == tkNamed:
|
||||||
|
receiverTypeName = receiverType.name
|
||||||
|
if ctx.typeSubst.hasKey(receiverTypeName):
|
||||||
|
let substituted = ctx.typeSubst[receiverTypeName]
|
||||||
|
if substituted.kind == tkNamed:
|
||||||
|
receiverTypeName = substituted.name
|
||||||
|
elif substituted.isPointer and substituted.inner.len > 0 and substituted.inner[0].kind == tkNamed:
|
||||||
|
receiverTypeName = substituted.inner[0].name
|
||||||
|
elif receiverType.kind in {tkInt, tkInt8, tkInt16, tkInt32, tkInt64,
|
||||||
|
tkUInt, tkUInt8, tkUInt16, tkUInt32, tkUInt64,
|
||||||
|
tkFloat32, tkFloat64, tkBool, tkStr, tkChar8}:
|
||||||
|
receiverTypeName = receiverType.toString
|
||||||
|
elif receiverType.isPointer and receiverType.inner.len > 0 and receiverType.inner[0].kind == tkNamed:
|
||||||
|
receiverTypeName = receiverType.inner[0].name
|
||||||
|
let (typeName, methods) = ctx.findMethodEntry(receiverTypeName)
|
||||||
|
if typeName == "": return nil
|
||||||
|
for minfo in methods:
|
||||||
|
if minfo.name == methodName:
|
||||||
|
var calleeName = typeName & "_" & methodName
|
||||||
|
# Check generic method instantiation
|
||||||
|
let recvTypeExpr = ctx.getReceiverTypeExpr(leftExpr)
|
||||||
|
let (baseName, typeArgs) = ctx.extractGenericStructInfo(recvTypeExpr)
|
||||||
|
if baseName != "" and baseName == typeName and minfo.decl.declFuncTypeParams.len > 0:
|
||||||
|
calleeName = ctx.generateMethodInstance(calleeName, typeArgs)
|
||||||
|
var args: seq[HirNode] = @[]
|
||||||
|
let loweredReceiver = ctx.lowerExpr(leftExpr)
|
||||||
|
if minfo.params.len > 0 and minfo.params[0].isPointer and not receiverType.isPointer:
|
||||||
|
args.add(hirUnary(tkAmp, loweredReceiver, makePointer(receiverType), loc))
|
||||||
|
else:
|
||||||
|
args.add(loweredReceiver)
|
||||||
|
args.add(ctx.lowerExpr(rightExpr))
|
||||||
|
return hirCall(calleeName, args, typ, loc)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
proc tryLowerIndexCall(ctx: var LowerCtx, objExpr, idxExpr: Expr, typ: Type, loc: SourceLocation): HirNode =
|
||||||
|
## Try to lower arr[i] to operator_index_get(arr, i). Returns nil if no overload found.
|
||||||
|
let receiverType = ctx.resolveExprType(objExpr)
|
||||||
|
var receiverTypeName = ""
|
||||||
|
if receiverType.kind == tkNamed:
|
||||||
|
receiverTypeName = receiverType.name
|
||||||
|
if ctx.typeSubst.hasKey(receiverTypeName):
|
||||||
|
let substituted = ctx.typeSubst[receiverTypeName]
|
||||||
|
if substituted.kind == tkNamed:
|
||||||
|
receiverTypeName = substituted.name
|
||||||
|
elif substituted.isPointer and substituted.inner.len > 0 and substituted.inner[0].kind == tkNamed:
|
||||||
|
receiverTypeName = substituted.inner[0].name
|
||||||
|
elif receiverType.kind in {tkInt, tkInt8, tkInt16, tkInt32, tkInt64,
|
||||||
|
tkUInt, tkUInt8, tkUInt16, tkUInt32, tkUInt64,
|
||||||
|
tkFloat32, tkFloat64, tkBool, tkStr, tkChar8}:
|
||||||
|
receiverTypeName = receiverType.toString
|
||||||
|
elif receiverType.isPointer and receiverType.inner.len > 0 and receiverType.inner[0].kind == tkNamed:
|
||||||
|
receiverTypeName = receiverType.inner[0].name
|
||||||
|
let (typeName, methods) = ctx.findMethodEntry(receiverTypeName)
|
||||||
|
if typeName == "": return nil
|
||||||
|
for minfo in methods:
|
||||||
|
if minfo.name == "operator_index_get":
|
||||||
|
var calleeName = typeName & "_operator_index_get"
|
||||||
|
let recvTypeExpr = ctx.getReceiverTypeExpr(objExpr)
|
||||||
|
let (baseName, typeArgs) = ctx.extractGenericStructInfo(recvTypeExpr)
|
||||||
|
if baseName != "" and baseName == typeName and minfo.decl.declFuncTypeParams.len > 0:
|
||||||
|
calleeName = ctx.generateMethodInstance(calleeName, typeArgs)
|
||||||
|
var args: seq[HirNode] = @[]
|
||||||
|
let loweredReceiver = ctx.lowerExpr(objExpr)
|
||||||
|
if minfo.params.len > 0 and minfo.params[0].isPointer and not receiverType.isPointer:
|
||||||
|
args.add(hirUnary(tkAmp, loweredReceiver, makePointer(receiverType), loc))
|
||||||
|
else:
|
||||||
|
args.add(loweredReceiver)
|
||||||
|
args.add(ctx.lowerExpr(idxExpr))
|
||||||
|
return hirCall(calleeName, args, typ, loc)
|
||||||
|
return nil
|
||||||
|
|
||||||
proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
||||||
if expr == nil: return nil
|
if expr == nil: return nil
|
||||||
let loc = expr.loc
|
let loc = expr.loc
|
||||||
@@ -584,6 +681,9 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
|||||||
let ifNode = hirIf(left, thenBlock, elseBlock, loc)
|
let ifNode = hirIf(left, thenBlock, elseBlock, loc)
|
||||||
return hirBlock(@[tmp, ifNode], hirLoad(tmp, makeBool(), loc), makeBool(), loc)
|
return hirBlock(@[tmp, ifNode], hirLoad(tmp, makeBool(), loc), makeBool(), loc)
|
||||||
else:
|
else:
|
||||||
|
let lowered = ctx.tryLowerOperatorCall(expr.exprBinaryOp, expr.exprBinaryLeft, expr.exprBinaryRight, typ, loc)
|
||||||
|
if lowered != nil:
|
||||||
|
return lowered
|
||||||
let left = ctx.lowerExpr(expr.exprBinaryLeft)
|
let left = ctx.lowerExpr(expr.exprBinaryLeft)
|
||||||
let right = ctx.lowerExpr(expr.exprBinaryRight)
|
let right = ctx.lowerExpr(expr.exprBinaryRight)
|
||||||
return hirBinary(expr.exprBinaryOp, left, right, typ, loc)
|
return hirBinary(expr.exprBinaryOp, left, right, typ, loc)
|
||||||
@@ -714,9 +814,13 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
|||||||
return HirNode(kind: hLoad, loadPtr: basePtr, typ: typ, loc: loc)
|
return HirNode(kind: hLoad, loadPtr: basePtr, typ: typ, loc: loc)
|
||||||
|
|
||||||
of ekIndex:
|
of ekIndex:
|
||||||
|
let baseType = ctx.resolveExprType(expr.exprIndexObj)
|
||||||
|
if not baseType.isSlice:
|
||||||
|
let lowered = ctx.tryLowerIndexCall(expr.exprIndexObj, expr.exprIndexIdx, typ, loc)
|
||||||
|
if lowered != nil:
|
||||||
|
return lowered
|
||||||
let base = ctx.lowerExpr(expr.exprIndexObj)
|
let base = ctx.lowerExpr(expr.exprIndexObj)
|
||||||
let idx = ctx.lowerExpr(expr.exprIndexIdx)
|
let idx = ctx.lowerExpr(expr.exprIndexIdx)
|
||||||
let baseType = ctx.resolveExprType(expr.exprIndexObj)
|
|
||||||
if baseType.isSlice:
|
if baseType.isSlice:
|
||||||
let sliceIdx = HirNode(kind: hSliceIndex, sliceIndexBase: base,
|
let sliceIdx = HirNode(kind: hSliceIndex, sliceIndexBase: base,
|
||||||
sliceIndexIndex: idx,
|
sliceIndexIndex: idx,
|
||||||
@@ -728,6 +832,44 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
|||||||
return HirNode(kind: hLoad, loadPtr: basePtr, typ: typ, loc: loc)
|
return HirNode(kind: hLoad, loadPtr: basePtr, typ: typ, loc: loc)
|
||||||
|
|
||||||
of ekAssign:
|
of ekAssign:
|
||||||
|
# Check for operator_index_set overload
|
||||||
|
if expr.exprAssignTarget.kind == ekIndex:
|
||||||
|
let objExpr = expr.exprAssignTarget.exprIndexObj
|
||||||
|
let idxExpr = expr.exprAssignTarget.exprIndexIdx
|
||||||
|
let receiverType = ctx.resolveExprType(objExpr)
|
||||||
|
var receiverTypeName = ""
|
||||||
|
if receiverType.kind == tkNamed:
|
||||||
|
receiverTypeName = receiverType.name
|
||||||
|
if ctx.typeSubst.hasKey(receiverTypeName):
|
||||||
|
let substituted = ctx.typeSubst[receiverTypeName]
|
||||||
|
if substituted.kind == tkNamed:
|
||||||
|
receiverTypeName = substituted.name
|
||||||
|
elif substituted.isPointer and substituted.inner.len > 0 and substituted.inner[0].kind == tkNamed:
|
||||||
|
receiverTypeName = substituted.inner[0].name
|
||||||
|
elif receiverType.kind in {tkInt, tkInt8, tkInt16, tkInt32, tkInt64,
|
||||||
|
tkUInt, tkUInt8, tkUInt16, tkUInt32, tkUInt64,
|
||||||
|
tkFloat32, tkFloat64, tkBool, tkStr, tkChar8}:
|
||||||
|
receiverTypeName = receiverType.toString
|
||||||
|
elif receiverType.isPointer and receiverType.inner.len > 0 and receiverType.inner[0].kind == tkNamed:
|
||||||
|
receiverTypeName = receiverType.inner[0].name
|
||||||
|
let (typeName, methods) = ctx.findMethodEntry(receiverTypeName)
|
||||||
|
if typeName != "":
|
||||||
|
for minfo in methods:
|
||||||
|
if minfo.name == "operator_index_set":
|
||||||
|
var calleeName = typeName & "_operator_index_set"
|
||||||
|
let recvTypeExpr = ctx.getReceiverTypeExpr(objExpr)
|
||||||
|
let (baseName, typeArgs) = ctx.extractGenericStructInfo(recvTypeExpr)
|
||||||
|
if baseName != "" and baseName == typeName and minfo.decl.declFuncTypeParams.len > 0:
|
||||||
|
calleeName = ctx.generateMethodInstance(calleeName, typeArgs)
|
||||||
|
var args: seq[HirNode] = @[]
|
||||||
|
let loweredReceiver = ctx.lowerExpr(objExpr)
|
||||||
|
if minfo.params.len > 0 and minfo.params[0].isPointer and not receiverType.isPointer:
|
||||||
|
args.add(hirUnary(tkAmp, loweredReceiver, makePointer(receiverType), loc))
|
||||||
|
else:
|
||||||
|
args.add(loweredReceiver)
|
||||||
|
args.add(ctx.lowerExpr(idxExpr))
|
||||||
|
args.add(ctx.lowerExpr(expr.exprAssignValue))
|
||||||
|
return hirCall(calleeName, args, makeVoid(), loc)
|
||||||
let target = ctx.lowerExpr(expr.exprAssignTarget)
|
let target = ctx.lowerExpr(expr.exprAssignTarget)
|
||||||
let value = ctx.lowerExpr(expr.exprAssignValue)
|
let value = ctx.lowerExpr(expr.exprAssignValue)
|
||||||
return HirNode(kind: hAssign, assignOp: expr.exprAssignOp,
|
return HirNode(kind: hAssign, assignOp: expr.exprAssignOp,
|
||||||
|
|||||||
@@ -797,6 +797,33 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
of ekBinary:
|
of ekBinary:
|
||||||
let left = sema.checkExpr(expr.exprBinaryLeft, scope)
|
let left = sema.checkExpr(expr.exprBinaryLeft, scope)
|
||||||
let right = sema.checkExpr(expr.exprBinaryRight, scope)
|
let right = sema.checkExpr(expr.exprBinaryRight, scope)
|
||||||
|
# Operator overloading: check method table before builtin rules
|
||||||
|
let opMethodName = case expr.exprBinaryOp
|
||||||
|
of tkPlus: "operator_add"
|
||||||
|
of tkMinus: "operator_sub"
|
||||||
|
of tkStar: "operator_mul"
|
||||||
|
of tkSlash: "operator_div"
|
||||||
|
of tkPercent: "operator_mod"
|
||||||
|
of tkEq: "operator_eq"
|
||||||
|
of tkNe: "operator_ne"
|
||||||
|
of tkLt: "operator_lt"
|
||||||
|
of tkLe: "operator_le"
|
||||||
|
of tkGt: "operator_gt"
|
||||||
|
of tkGe: "operator_ge"
|
||||||
|
of tkAmp: "operator_bitand"
|
||||||
|
of tkPipe: "operator_bitor"
|
||||||
|
of tkCaret: "operator_xor"
|
||||||
|
of tkShl: "operator_shl"
|
||||||
|
of tkShr: "operator_shr"
|
||||||
|
else: ""
|
||||||
|
if opMethodName != "" and left.kind == tkNamed and sema.methodTable.hasKey(left.name):
|
||||||
|
for minfo in sema.methodTable[left.name]:
|
||||||
|
if minfo.name == opMethodName:
|
||||||
|
# Validate argument count (self + other)
|
||||||
|
if minfo.params.len == 2:
|
||||||
|
let otherType = minfo.params[1]
|
||||||
|
if right.isAssignableTo(otherType) or otherType.isAssignableTo(right) or right.kind == tkUnknown:
|
||||||
|
return minfo.retType
|
||||||
case expr.exprBinaryOp
|
case expr.exprBinaryOp
|
||||||
of tkPlus, tkMinus, tkStar, tkSlash, tkPercent, tkStarStar:
|
of tkPlus, tkMinus, tkStar, tkSlash, tkPercent, tkStarStar:
|
||||||
if not left.isNumeric or not right.isNumeric:
|
if not left.isNumeric or not right.isNumeric:
|
||||||
@@ -1057,6 +1084,14 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
return obj.inner[0]
|
return obj.inner[0]
|
||||||
elif obj.kind == tkStr:
|
elif obj.kind == tkStr:
|
||||||
return makeChar8()
|
return makeChar8()
|
||||||
|
elif obj.kind == tkNamed and sema.methodTable.hasKey(obj.name):
|
||||||
|
for minfo in sema.methodTable[obj.name]:
|
||||||
|
if minfo.name == "operator_index_get" and minfo.params.len == 2:
|
||||||
|
let idxType = minfo.params[1]
|
||||||
|
if idx.isAssignableTo(idxType) or idxType.isAssignableTo(idx) or idx.kind == tkUnknown:
|
||||||
|
return minfo.retType
|
||||||
|
sema.emitError(expr.loc, "cannot index non-slice/non-pointer type")
|
||||||
|
return makeUnknown()
|
||||||
else:
|
else:
|
||||||
sema.emitError(expr.loc, "cannot index non-slice/non-pointer type")
|
sema.emitError(expr.loc, "cannot index non-slice/non-pointer type")
|
||||||
return makeUnknown()
|
return makeUnknown()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Фаза 8 — Стратегия: Как Bux печели, без да бие пряко Rust/Nim/Zig
|
# Фаза 8 — Стратегия: Как Bux печели, без да бие пряко Rust/Nim/Zig
|
||||||
|
|
||||||
> **Дата:** 2026-05-31 | **Статус:** Фаза 8.1 ✅, 8.2-8.6 🔄
|
> **Дата:** 2026-06-08 | **Статус:** Фаза 8.1 ✅, 8.2-8.6 🔄
|
||||||
|
> **Последни постижения:** defer ✅, switch/case ✅, operator overloading ✅, selfhost-loop deterministic ✅
|
||||||
> **Правило #1:** Не се биеш с някого там, където той е най-силен.
|
> **Правило #1:** Не се биеш с някого там, където той е най-силен.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -285,10 +286,12 @@ Crates.io е непреодолимо предимство. Ние се конк
|
|||||||
|
|
||||||
## 6. Пътна карта за победа (реалистична)
|
## 6. Пътна карта за победа (реалистична)
|
||||||
|
|
||||||
### Milestone A: "Използваем за CLI tools" (2-3 седмици)
|
### Milestone A: "Използваем за CLI tools" ✅ ЗАВЪРШЕН
|
||||||
- ✅ Generics, Result/Option, pattern matching — готово
|
- ✅ Generics, Result/Option, pattern matching — готово
|
||||||
- 🔄 Fix `buxc2` bootstrap loop (14/14 modules)
|
- ✅ Fix `buxc2` bootstrap loop (14/14 модула)
|
||||||
- 🔄 File I/O, path ops, process spawn в stdlib
|
- ✅ Selfhost-loop deterministic (C output identical)
|
||||||
|
- ✅ File I/O, path ops, process spawn в stdlib
|
||||||
|
- ✅ defer, switch/case, operator overloading — готово
|
||||||
- 🎯 Target: Можеш да напишеш `bux` package manager на Bux
|
- 🎯 Target: Можеш да напишеш `bux` package manager на Bux
|
||||||
|
|
||||||
### Milestone B: "Използваем за systems programming" (2 месеца)
|
### Milestone B: "Използваем за systems programming" (2 месеца)
|
||||||
|
|||||||
+99
-80
@@ -6,10 +6,10 @@ This document tracks planned language constructs beyond Phase 8 strategy.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## P0 — Critical (Unlocks Major Use Cases)
|
## ✅ Done
|
||||||
|
|
||||||
### 1. `defer` Statement
|
### 1. `defer` Statement
|
||||||
**Why:** No GC + no destructors = manual `Free` everywhere. `defer` is the pragmatic fix.
|
**Status:** ✅ Implemented in both bootstrap and selfhost.
|
||||||
|
|
||||||
**Syntax:**
|
**Syntax:**
|
||||||
```bux
|
```bux
|
||||||
@@ -22,18 +22,84 @@ func ReadFile(path: String) -> String {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Implementation Steps:**
|
---
|
||||||
1. Add `tkDefer` token (or reuse `tkDefer` if exists)
|
|
||||||
2. Add `DeferStmt` AST node (`child: *Stmt`)
|
|
||||||
3. Parser: parse `defer <expr>;` or `defer { <block> }`
|
|
||||||
4. C backend: collect all `defer`s per block, emit cleanup code before every exit point (return, break, continue)
|
|
||||||
5. Handle LIFO ordering for multiple defers in same scope
|
|
||||||
|
|
||||||
**Complexity:** Low — localized to parser + C backend. No type system changes.
|
### 2. Native `switch` / `case`
|
||||||
|
**Status:** ✅ Implemented in both bootstrap and selfhost. Desugars to if-else chain.
|
||||||
|
|
||||||
|
**Syntax:**
|
||||||
|
```bux
|
||||||
|
switch statusCode {
|
||||||
|
case 200: PrintLine("OK");
|
||||||
|
case 404: PrintLine("Not Found");
|
||||||
|
case 500: PrintLine("Server Error");
|
||||||
|
default: PrintLine("Unknown");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 2. Closures / Anonymous Functions
|
### 3. Operator Overloading
|
||||||
|
**Status:** ✅ Implemented in bootstrap. Selfhost has no method-table yet (not needed for selfhost-loop parity).
|
||||||
|
|
||||||
|
**Supported operators:**
|
||||||
|
```bux
|
||||||
|
func Vec2_operator_add(self: *Vec2, other: Vec2) -> Vec2 { ... }
|
||||||
|
func Vec2_operator_sub(self: *Vec2, other: Vec2) -> Vec2 { ... }
|
||||||
|
func Vec2_operator_eq(self: *Vec2, other: Vec2) -> bool { ... }
|
||||||
|
func Vec2_operator_lt(self: *Vec2, other: Vec2) -> bool { ... }
|
||||||
|
func MyArray_operator_index_get(self: *MyArray, idx: int) -> int { ... }
|
||||||
|
func MyArray_operator_index_set(self: *MyArray, idx: int, value: int) { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
**Notes:**
|
||||||
|
- Works via method-table lookup in sema + hir_lower.
|
||||||
|
- Generic method instantiation supported.
|
||||||
|
- Short-circuit operators (`&&`, `||`) remain builtin.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P0 — Critical (Unlocks Major Use Cases)
|
||||||
|
|
||||||
|
### 4. String Interpolation
|
||||||
|
**Why:** `Fmt_Fmt1("hello {0}", name)` is verbose.
|
||||||
|
|
||||||
|
**Syntax:**
|
||||||
|
```bux
|
||||||
|
let name: String = "Bux";
|
||||||
|
let msg: String = "Hello, {name}!";
|
||||||
|
let num: int = 42;
|
||||||
|
let msg2: String = "Count: {num}";
|
||||||
|
```
|
||||||
|
|
||||||
|
**Implementation Steps:**
|
||||||
|
1. Lexer: detect `{` inside string literals, parse interpolation expressions
|
||||||
|
2. Parser: create string concatenation AST node
|
||||||
|
3. Desugar to `String_Concat` calls or `Fmt_FmtN`
|
||||||
|
|
||||||
|
**Complexity:** Low — lexer/parser changes only.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. Named / Default Parameters
|
||||||
|
**Why:** API ergonomics.
|
||||||
|
|
||||||
|
**Syntax:**
|
||||||
|
```bux
|
||||||
|
func HttpResponse(code: int = 200, contentType: String = "text/plain", body: String = "") -> Response { ... }
|
||||||
|
let r: Response = HttpResponse(body: "hello"); // code=200, contentType=default
|
||||||
|
```
|
||||||
|
|
||||||
|
**Implementation Steps:**
|
||||||
|
1. Parser: allow `param: Type = defaultExpr`
|
||||||
|
2. Sema: fill missing args at call sites
|
||||||
|
3. C backend: emit args in correct order with defaults
|
||||||
|
|
||||||
|
**Complexity:** Medium — sema changes for call resolution.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. Closures / Anonymous Functions
|
||||||
**Why:** Callbacks, iterators, functional APIs. Currently only named functions exist.
|
**Why:** Callbacks, iterators, functional APIs. Currently only named functions exist.
|
||||||
|
|
||||||
**Syntax:**
|
**Syntax:**
|
||||||
@@ -54,7 +120,7 @@ Array_Filter(nums, |x| { return x > 10; });
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 3. `for x in collection` Iterator Loops
|
### 7. `for x in collection` Iterator Loops
|
||||||
**Why:** Currently only `for i in 0..10` works. No way to iterate arrays/channels/maps.
|
**Why:** Currently only `for i in 0..10` works. No way to iterate arrays/channels/maps.
|
||||||
|
|
||||||
**Syntax:**
|
**Syntax:**
|
||||||
@@ -78,27 +144,7 @@ for msg in channel {
|
|||||||
|
|
||||||
## P1 — High Impact
|
## P1 — High Impact
|
||||||
|
|
||||||
### 4. Operator Overloading
|
### 8. Destructors / `Drop` Trait
|
||||||
**Why:** Can't write `arr[i]`, `a + b`, `s1 == s2` for user types.
|
|
||||||
|
|
||||||
**Syntax:**
|
|
||||||
```bux
|
|
||||||
extend Array<T> {
|
|
||||||
func operator[](self: Array<T>, idx: uint) -> T { ... }
|
|
||||||
func operator+(self: Array<T>, other: Array<T>) -> Array<T> { ... }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Implementation Steps:**
|
|
||||||
1. Parser: allow `operator[]`, `operator+`, etc. as function names
|
|
||||||
2. Sema: resolve operator calls to user-defined functions when available
|
|
||||||
3. C backend: emit regular function call
|
|
||||||
|
|
||||||
**Complexity:** Medium — mainly sema + parser changes.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5. Destructors / `Drop` Trait
|
|
||||||
**Why:** `own T` exists but nothing cleans up automatically. Complements `defer`.
|
**Why:** `own T` exists but nothing cleans up automatically. Complements `defer`.
|
||||||
|
|
||||||
**Syntax:**
|
**Syntax:**
|
||||||
@@ -119,74 +165,47 @@ extend Array<T> {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 6. String Interpolation
|
|
||||||
**Why:** `Fmt_Fmt1("hello {0}", name)` is verbose.
|
|
||||||
|
|
||||||
**Syntax:**
|
|
||||||
```bux
|
|
||||||
let name: String = "Bux";
|
|
||||||
let msg: String = "Hello, {name}!";
|
|
||||||
let num: int = 42;
|
|
||||||
let msg2: String = "Count: {num}";
|
|
||||||
```
|
|
||||||
|
|
||||||
**Implementation Steps:**
|
|
||||||
1. Lexer: detect `{` inside string literals, parse interpolation expressions
|
|
||||||
2. Parser: create string concatenation AST node
|
|
||||||
3. Desugar to `String_Concat` calls or `Fmt_FmtN`
|
|
||||||
|
|
||||||
**Complexity:** Low — lexer/parser changes only.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## P2 — Nice to Have
|
## P2 — Nice to Have
|
||||||
|
|
||||||
### 7. Native `switch` / `case`
|
### 9. Trait System Enhancement
|
||||||
**Why:** `match` is powerful but overkill for simple integer dispatch. Jump tables are faster.
|
**Why:** Currently have `interface` + `extend` (basic). Need trait bounds, associated types.
|
||||||
|
|
||||||
**Syntax:**
|
**Syntax:**
|
||||||
```bux
|
```bux
|
||||||
switch statusCode {
|
func Sort<T: Comparable>(arr: &mut Array<T>) { ... }
|
||||||
case 200: PrintLine("OK");
|
|
||||||
case 404: PrintLine("Not Found");
|
|
||||||
case 500: PrintLine("Server Error");
|
|
||||||
default: PrintLine("Unknown");
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Implementation Steps:**
|
|
||||||
1. Parser: `switch expr { case literal: stmts ... default: stmts }`
|
|
||||||
2. C backend: emit `switch(expr) { case N: ... }`
|
|
||||||
|
|
||||||
**Complexity:** Low — straightforward C mapping.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 8. Named / Default Parameters
|
### 10. CTFE (Compile-Time Function Execution)
|
||||||
**Why:** API ergonomics.
|
**Why:** Precomputed tables for embedded / kernel dev.
|
||||||
|
|
||||||
**Syntax:**
|
**Syntax:**
|
||||||
```bux
|
```bux
|
||||||
func HttpResponse(code: int = 200, contentType: String = "text/plain", body: String = "") -> Response { ... }
|
const func Fib(n: int) -> int { ... }
|
||||||
let r: Response = HttpResponse(body: "hello"); // code=200, contentType=default
|
const TABLE_SIZE = Fib(20);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Implementation Steps:**
|
---
|
||||||
1. Parser: allow `param: Type = defaultExpr`
|
|
||||||
2. Sema: fill missing args at call sites
|
|
||||||
3. C backend: emit args in correct order with defaults
|
|
||||||
|
|
||||||
**Complexity:** Medium — sema changes for call resolution.
|
### 11. Concurrency
|
||||||
|
**Why:** Go-style goroutines + channels, but without GC.
|
||||||
|
|
||||||
|
**Syntax:**
|
||||||
|
```bux
|
||||||
|
let (tx, rx) = Channel::New<int>();
|
||||||
|
Task::Spawn(Worker, rx);
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Recommended Order
|
## Recommended Order
|
||||||
|
|
||||||
1. **`defer`** — Low complexity, huge impact, unlocks safe resource management immediately.
|
1. ✅ **`defer`** — Done
|
||||||
2. **String interpolation** — Low complexity, big ergonomics win.
|
2. ✅ **`switch`/`case`** — Done
|
||||||
3. **`switch`/`case`** — Low complexity, complements `match` for numeric dispatch.
|
3. ✅ **Operator overloading** — Done (bootstrap)
|
||||||
4. **Named/default parameters** — Medium complexity, improves stdlib APIs.
|
4. **String interpolation** — Low complexity, big ergonomics win. **← NEXT**
|
||||||
5. **Operator overloading** — Medium complexity, transforms stdlib ergonomics.
|
5. **Named/default parameters** — Medium complexity, improves stdlib APIs.
|
||||||
6. **Closures** — High complexity, unlocks iterators and functional style.
|
6. **Closures** — High complexity, unlocks iterators and functional style.
|
||||||
7. **`for x in collection`** — Depends on closures or trait system.
|
7. **`for x in collection`** — Depends on closures or trait system.
|
||||||
8. **Destructors / Drop** — High complexity, needs ownership + move semantics.
|
8. **Destructors / Drop** — High complexity, needs ownership + move semantics.
|
||||||
|
|||||||
+3721
-1851
File diff suppressed because it is too large
Load Diff
+3676
-1806
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user