feat(stdlib): generic Iter_Map/Filter/Fold with func monomorphization
Add Iter_Map<T,U>, Filter, Fold, Any, All, ForEach over fat function pointers; keep Iter_MapInt and friends as thin aliases. Compiler fixes required for non-int returns and capturing closures: - bootstrap: resolve fat-func call return type under mono typeSubst - bootstrap: type-check args of Foo<T>(...) so closures capture correctly - selfhost: substitute type params inside tekFunc (BuxFn_U_T → concrete) - selfhost: emit cstr fat typedefs with #ifndef redefinition guards Example: examples/iter_generic.bux (int↔String map, fold, closures). Selfhost-loop remains binary-identical.
This commit is contained in:
@@ -3,7 +3,7 @@ SRC := bootstrap/main.nim
|
||||
OUT := buxc
|
||||
BUILD_DIR := build
|
||||
|
||||
EXAMPLES := hello fibonacci factorial structs enums methods algebraic_enums generics generics_struct generic_infer generic_infer2 extend_generic pattern_matching strings strings2 map result_option try_operator ownership ctfe async concurrency os_time process json iter trait_bounds channel sync jwt stdlib_ergonomics tuples func_ptr map_remove array_iter_extra string_extra multi_closure iter_hof closure_control match_let string_interp
|
||||
EXAMPLES := hello fibonacci factorial structs enums methods algebraic_enums generics generics_struct generic_infer generic_infer2 extend_generic pattern_matching strings strings2 map result_option try_operator ownership ctfe async concurrency os_time process json iter trait_bounds channel sync jwt stdlib_ergonomics tuples func_ptr map_remove array_iter_extra string_extra multi_closure iter_hof closure_control match_let string_interp iter_generic
|
||||
|
||||
.PHONY: all build dev debug test clean clean-all test-examples selfhost test-golden test-errors selfhost-loop lsp
|
||||
|
||||
|
||||
@@ -548,9 +548,15 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
||||
return makeUnknown()
|
||||
else: return ctx.resolveExprType(expr.exprUnaryOperand)
|
||||
of ekCall:
|
||||
# Local / param fat-func values (after monomorphization typeSubst) — e.g. f: func(T)->U
|
||||
# Must run before the global-only lookup so generic HOFs get the correct return type.
|
||||
if expr.exprCallCallee.kind in {ekIdent, ekPath}:
|
||||
let calType = ctx.resolveExprType(expr.exprCallCallee)
|
||||
if calType != nil and calType.kind == tkFunc and calType.inner.len > 0:
|
||||
return calType.inner[^1]
|
||||
if expr.exprCallCallee.kind == ekIdent:
|
||||
let sym = ctx.globalScope.lookup(expr.exprCallCallee.exprIdent)
|
||||
if sym != nil and sym.typ != nil and sym.typ.kind == tkFunc:
|
||||
if sym != nil and sym.typ != nil and sym.typ.kind == tkFunc and sym.typ.inner.len > 0:
|
||||
return sym.typ.inner[^1]
|
||||
if expr.exprCallCallee.kind == ekField:
|
||||
let recvType = ctx.resolveExprType(expr.exprCallCallee.exprFieldObj)
|
||||
|
||||
+13
-10
@@ -1063,31 +1063,34 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
||||
sema.emitError(expr.loc, "internal error: nil callee in call expression")
|
||||
return makeUnknown()
|
||||
|
||||
# Check for generic function call: Max<int>(10, 20)
|
||||
# Check for generic function call: Max<int>(10, 20) or Iter_Map<int, String>(…)
|
||||
if expr.exprCallCallee.kind == ekGenericCall:
|
||||
let sym = scope.lookup(expr.exprCallCallee.exprGenericCallee)
|
||||
if sym == nil:
|
||||
sema.emitError(expr.loc, &"undeclared identifier '{expr.exprCallCallee.exprGenericCallee}'")
|
||||
return makeUnknown()
|
||||
if sym.typ != nil and sym.typ.kind == tkFunc:
|
||||
let retType = sym.typ.inner[^1]
|
||||
# Still type-check args (closures need capture analysis, etc.)
|
||||
# Bind type params while checking so `func(T)->U` params resolve.
|
||||
let sym2 = sema.globalScope.lookup(expr.exprCallCallee.exprGenericCallee)
|
||||
if sym2 != nil and sym2.decl != nil and sym2.decl.kind == dkFunc and
|
||||
sym2.decl.declFuncTypeParams.len > 0 and
|
||||
sym2.decl.declFuncReturnType != nil:
|
||||
let typeParams = sym2.decl.declFuncTypeParams
|
||||
var added: seq[string] = @[]
|
||||
if sym2 != nil and sym2.decl != nil and sym2.decl.kind == dkFunc and
|
||||
sym2.decl.declFuncTypeParams.len > 0:
|
||||
let typeParams = sym2.decl.declFuncTypeParams
|
||||
for i, tp in typeParams:
|
||||
if i < expr.exprCallCallee.exprGenericTypeArgs.len:
|
||||
let concrete = sema.resolveType(expr.exprCallCallee.exprGenericTypeArgs[i])
|
||||
sema.typeTable[tp.name] = concrete
|
||||
added.add(tp.name)
|
||||
let resolvedRet = sema.resolveType(sym2.decl.declFuncReturnType)
|
||||
discard sema.checkExprList(expr.exprCallArgs, scope)
|
||||
var resolvedRet = makeUnknown()
|
||||
if sym2 != nil and sym2.decl != nil and sym2.decl.kind == dkFunc and
|
||||
sym2.decl.declFuncReturnType != nil:
|
||||
resolvedRet = sema.resolveType(sym2.decl.declFuncReturnType)
|
||||
elif sym.typ != nil and sym.typ.kind == tkFunc and sym.typ.inner.len > 0:
|
||||
resolvedRet = sym.typ.inner[^1]
|
||||
for tp in added:
|
||||
sema.typeTable.del(tp)
|
||||
return resolvedRet
|
||||
return retType
|
||||
return makeUnknown()
|
||||
|
||||
# Check for method call: obj.method(args)
|
||||
if expr.exprCallCallee.kind == ekField:
|
||||
|
||||
+20
-6
@@ -48,7 +48,7 @@
|
||||
| A.2 | String: IsEmpty, ReplaceAll | Чести операции; само first-replace досега | ✅ (тази сесия) |
|
||||
| A.3 | Os_Exit + Test_AssertEqString / richer asserts | Тестове и CLI без raw `bux_exit` | ✅ (тази сесия) |
|
||||
| A.4 | Map_Remove / Set polish | Completeness на колекциите | ✅ (тази сесия) |
|
||||
| A.5 | Iter: map/filter/fold върху closures | Higher-order без boilerplate | ✅ Iter_Map/Filter/FoldInt |
|
||||
| A.5 | Iter: map/filter/fold върху closures | Higher-order без boilerplate | ✅ generic `Iter_Map`/`Filter`/`Fold` + Int aliases |
|
||||
| A.6 | Result helpers: Expect, UnwrapErr, Or | По-малко match boilerplate | ✅ (тази сесия) |
|
||||
|
||||
### B — Compiler Correctness (P0)
|
||||
@@ -246,10 +246,24 @@ A (stdlib ergonomics) → B (compiler holes) → C (ownership depth)
|
||||
|
||||
---
|
||||
|
||||
## Сесия 14 (generic Iter map/filter/fold)
|
||||
|
||||
1. **`Iter_Map<T,U>` / `Filter<T>` / `Fold<T,Acc>` / `Any` / `All` / `ForEach`** — fat `func` params + monomorphization
|
||||
2. **Int aliases** keep working: `Iter_MapInt` → `Iter_Map<int,int>`, …
|
||||
3. **Bootstrap fixes:**
|
||||
- call return type for local fat-func (`f: func(T)->U`) after mono (was always `int` → String map truncated pointers)
|
||||
- generic call `Foo<T>(…)` now type-checks args (closures get capture analysis)
|
||||
4. **Selfhost fixes:**
|
||||
- `Lcx_SubstituteType` recurses into `tekFunc` (was leaving `BuxFn_U_T`)
|
||||
- fat typedef emit covers cstr shapes + `#ifndef` guards
|
||||
5. Example: `examples/iter_generic.bux` (int↔String map, filter, fold, closures)
|
||||
6. Verified: bootstrap + **buxc2** + selfhost-loop IDENTICAL ✓
|
||||
|
||||
---
|
||||
|
||||
## Следващи стъпки
|
||||
|
||||
1. **Generic Iter map** (не само int), ако monomorphization с `func` params е стабилна
|
||||
2. Struct/tuple patterns (`Point { x, y }`, `(a, b)`) + nested bindings
|
||||
3. Match arm multi-stmt bodies (beyond single expr)
|
||||
4. LSP: wire hover types from real sema (replace lightweight index where possible)
|
||||
|
||||
1. Struct/tuple patterns (`Point { x, y }`, `(a, b)`) + nested bindings
|
||||
2. Match arm multi-stmt bodies (beyond single expr)
|
||||
3. LSP: wire hover types from real sema (replace lightweight index where possible)
|
||||
4. Generic type inference for `Iter_Map` without explicit `<T,U>`
|
||||
|
||||
+9
-8
@@ -143,18 +143,19 @@ struct Iter<T> {
|
||||
| `Iter_AllEq<T>` | `func Iter_AllEq<T>(it: *Iter<T>, value: T) -> bool` | True if all remaining equal value |
|
||||
| `Iter_Collect<T>` | `func Iter_Collect<T>(it: *Iter<T>) -> Array<T>` | Collect remaining into a new Array |
|
||||
|
||||
### Higher-order (int-specialized)
|
||||
### Higher-order (generic + int aliases)
|
||||
|
||||
Take fat function pointers / closures (`func(int) -> int`, `func(int) -> bool`, …).
|
||||
Take fat function pointers / closures. Prefer the generic forms; `*Int` aliases remain for compatibility.
|
||||
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `Iter_MapInt` | `func Iter_MapInt(it: *Iter<int>, f: func(int) -> int) -> Array<int>` | Map each element |
|
||||
| `Iter_FilterInt` | `func Iter_FilterInt(it: *Iter<int>, pred: func(int) -> bool) -> Array<int>` | Keep matching elements |
|
||||
| `Iter_FoldInt` | `func Iter_FoldInt(it: *Iter<int>, init: int, f: func(int, int) -> int) -> int` | Left fold |
|
||||
| `Iter_ForEachInt` | `func Iter_ForEachInt(it: *Iter<int>, f: func(int) -> int)` | Side-effect per element |
|
||||
| `Iter_AnyInt` | `func Iter_AnyInt(it: *Iter<int>, pred: func(int) -> bool) -> bool` | Any matches pred |
|
||||
| `Iter_AllInt` | `func Iter_AllInt(it: *Iter<int>, pred: func(int) -> bool) -> bool` | All match pred |
|
||||
| `Iter_Map<T,U>` | `func Iter_Map<T,U>(it: *Iter<T>, f: func(T) -> U) -> Array<U>` | Map `T → U` |
|
||||
| `Iter_Filter<T>` | `func Iter_Filter<T>(it: *Iter<T>, pred: func(T) -> bool) -> Array<T>` | Keep matching |
|
||||
| `Iter_Fold<T,Acc>` | `func Iter_Fold<T,Acc>(it: *Iter<T>, init: Acc, f: func(Acc, T) -> Acc) -> Acc` | Left fold |
|
||||
| `Iter_ForEach<T>` | `func Iter_ForEach<T>(it: *Iter<T>, f: func(T) -> int)` | Side-effect per element |
|
||||
| `Iter_Any<T>` | `func Iter_Any<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool` | Any matches pred |
|
||||
| `Iter_All<T>` | `func Iter_All<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool` | All match pred |
|
||||
| `Iter_MapInt` … | wrappers → `Iter_Map<int,int>` etc. | Back-compat |
|
||||
| `Iter_SumInt` | `func Iter_SumInt(it: *Iter<int>) -> int` | Sum remaining ints |
|
||||
|
||||
### Example
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// Generic Iter_Map / Filter / Fold / Any / All (not just int)
|
||||
import Std::Io::{PrintLine, PrintInt};
|
||||
import Std::Array::{
|
||||
Array, Array_New, Array_Push, Array_Get, Array_Len, Array_Free
|
||||
};
|
||||
import Std::String::{String_FromInt, String_Len, String_Eq};
|
||||
import Std::Iter::{
|
||||
Array_Iter, Iter,
|
||||
Iter_Map, Iter_Filter, Iter_Fold, Iter_Any, Iter_All,
|
||||
Iter_MapInt, Iter_SumInt
|
||||
};
|
||||
import Std::Test::{
|
||||
Test_AssertEqInt, Test_AssertTrue, Test_AssertFalse, Test_AssertEqString, Test_Pass
|
||||
};
|
||||
|
||||
func Double(x: int) -> int {
|
||||
return x * 2;
|
||||
}
|
||||
|
||||
func IsEven(x: int) -> bool {
|
||||
return (x % 2) == 0;
|
||||
}
|
||||
|
||||
func IntToString(x: int) -> String {
|
||||
return String_FromInt(x);
|
||||
}
|
||||
|
||||
func StringLenAsInt(s: String) -> int {
|
||||
return String_Len(s) as int;
|
||||
}
|
||||
|
||||
func AddLens(acc: int, s: String) -> int {
|
||||
return acc + (String_Len(s) as int);
|
||||
}
|
||||
|
||||
func IsNonEmpty(s: String) -> bool {
|
||||
return String_Len(s) > 0;
|
||||
}
|
||||
|
||||
func Main() -> int {
|
||||
var nums: Array<int> = Array_New<int>(8);
|
||||
Array_Push<int>(&nums, 1);
|
||||
Array_Push<int>(&nums, 2);
|
||||
Array_Push<int>(&nums, 3);
|
||||
Array_Push<int>(&nums, 4);
|
||||
Array_Push<int>(&nums, 5);
|
||||
|
||||
// Generic Map int → int
|
||||
let itA: Iter<int> = Array_Iter<int>(&nums);
|
||||
var doubled: Array<int> = Iter_Map<int, int>(&itA, Double);
|
||||
Test_AssertEqInt(Array_Get<int>(&doubled, 0), 2);
|
||||
Test_AssertEqInt(Array_Get<int>(&doubled, 4), 10);
|
||||
|
||||
// Generic Map int → String
|
||||
let itB: Iter<int> = Array_Iter<int>(&nums);
|
||||
var asStr: Array<String> = Iter_Map<int, String>(&itB, IntToString);
|
||||
Test_AssertEqInt(Array_Len<String>(&asStr) as int, 5);
|
||||
Test_AssertEqString(Array_Get<String>(&asStr, 0), "1");
|
||||
Test_AssertEqString(Array_Get<String>(&asStr, 4), "5");
|
||||
|
||||
// Map String → int (lengths)
|
||||
let itC: Iter<String> = Array_Iter<String>(&asStr);
|
||||
var lens: Array<int> = Iter_Map<String, int>(&itC, StringLenAsInt);
|
||||
Test_AssertEqInt(Array_Get<int>(&lens, 0), 1);
|
||||
Test_AssertEqInt(Array_Get<int>(&lens, 4), 1);
|
||||
|
||||
// Filter generic
|
||||
let itD: Iter<int> = Array_Iter<int>(&nums);
|
||||
var evens: Array<int> = Iter_Filter<int>(&itD, IsEven);
|
||||
Test_AssertEqInt(Array_Len<int>(&evens) as int, 2);
|
||||
Test_AssertEqInt(Array_Get<int>(&evens, 0), 2);
|
||||
Test_AssertEqInt(Array_Get<int>(&evens, 1), 4);
|
||||
|
||||
// Fold String lengths
|
||||
let itE: Iter<String> = Array_Iter<String>(&asStr);
|
||||
let totalChars: int = Iter_Fold<String, int>(&itE, 0, AddLens);
|
||||
Test_AssertEqInt(totalChars, 5); // "1"+"2"+"3"+"4"+"5"
|
||||
|
||||
// Any / All on String
|
||||
let itF: Iter<String> = Array_Iter<String>(&asStr);
|
||||
Test_AssertTrue(Iter_Any<String>(&itF, IsNonEmpty));
|
||||
let itG: Iter<String> = Array_Iter<String>(&asStr);
|
||||
Test_AssertTrue(Iter_All<String>(&itG, IsNonEmpty));
|
||||
|
||||
// Closures with generic Map
|
||||
let scale: int = 100;
|
||||
let itH: Iter<int> = Array_Iter<int>(&nums);
|
||||
var scaled: Array<int> = Iter_Map<int, int>(&itH, |x: int| -> int {
|
||||
return x * scale;
|
||||
});
|
||||
Test_AssertEqInt(Array_Get<int>(&scaled, 0), 100);
|
||||
Test_AssertEqInt(Array_Get<int>(&scaled, 2), 300);
|
||||
|
||||
// Int aliases still work
|
||||
let itI: Iter<int> = Array_Iter<int>(&nums);
|
||||
var d2: Array<int> = Iter_MapInt(&itI, Double);
|
||||
Test_AssertEqInt(Array_Get<int>(&d2, 1), 4);
|
||||
let itJ: Iter<int> = Array_Iter<int>(&nums);
|
||||
Test_AssertEqInt(Iter_SumInt(&itJ), 15);
|
||||
|
||||
PrintInt(totalChars);
|
||||
PrintLine("");
|
||||
Test_Pass("iter_generic");
|
||||
|
||||
Array_Free<int>(&nums);
|
||||
Array_Free<int>(&doubled);
|
||||
Array_Free<String>(&asStr);
|
||||
Array_Free<int>(&lens);
|
||||
Array_Free<int>(&evens);
|
||||
Array_Free<int>(&scaled);
|
||||
Array_Free<int>(&d2);
|
||||
return 0;
|
||||
}
|
||||
+46
-18
@@ -108,48 +108,48 @@ func Iter_Collect<T>(it: *Iter<T>) -> Array<T> {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Higher-order helpers (int-specialized; take fat func pointers / closures)
|
||||
// Higher-order helpers (generic; fat func pointers / closures)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/* Map each remaining int through f, collect into a new Array */
|
||||
func Iter_MapInt(it: *Iter<int>, f: func(int) -> int) -> Array<int> {
|
||||
/* Map each remaining element through f: T → U, collect into Array<U> */
|
||||
func Iter_Map<T, U>(it: *Iter<T>, f: func(T) -> U) -> Array<U> {
|
||||
let remaining: uint = it.len - it.pos;
|
||||
var cap: uint = remaining;
|
||||
if cap == 0 {
|
||||
cap = 1;
|
||||
}
|
||||
var out: Array<int> = Array_New<int>(cap);
|
||||
var out: Array<U> = Array_New<U>(cap);
|
||||
var i: uint = it.pos;
|
||||
while i < it.len {
|
||||
let mapped: int = f(it.data[i]);
|
||||
Array_Push<int>(&out, mapped);
|
||||
let mapped: U = f(it.data[i]);
|
||||
Array_Push<U>(&out, mapped);
|
||||
i = i + 1;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Keep remaining ints for which pred returns true */
|
||||
func Iter_FilterInt(it: *Iter<int>, pred: func(int) -> bool) -> Array<int> {
|
||||
/* Keep remaining elements for which pred returns true */
|
||||
func Iter_Filter<T>(it: *Iter<T>, pred: func(T) -> bool) -> Array<T> {
|
||||
let remaining: uint = it.len - it.pos;
|
||||
var cap: uint = remaining;
|
||||
if cap == 0 {
|
||||
cap = 1;
|
||||
}
|
||||
var out: Array<int> = Array_New<int>(cap);
|
||||
var out: Array<T> = Array_New<T>(cap);
|
||||
var i: uint = it.pos;
|
||||
while i < it.len {
|
||||
let v: int = it.data[i];
|
||||
let v: T = it.data[i];
|
||||
if pred(v) {
|
||||
Array_Push<int>(&out, v);
|
||||
Array_Push<T>(&out, v);
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Left-fold remaining ints: f(f(...f(init, x0), x1), ...) */
|
||||
func Iter_FoldInt(it: *Iter<int>, init: int, f: func(int, int) -> int) -> int {
|
||||
var acc: int = init;
|
||||
/* Left-fold: f(f(...f(init, x0), x1), ...) */
|
||||
func Iter_Fold<T, Acc>(it: *Iter<T>, init: Acc, f: func(Acc, T) -> Acc) -> Acc {
|
||||
var acc: Acc = init;
|
||||
var i: uint = it.pos;
|
||||
while i < it.len {
|
||||
acc = f(acc, it.data[i]);
|
||||
@@ -158,8 +158,8 @@ func Iter_FoldInt(it: *Iter<int>, init: int, f: func(int, int) -> int) -> int {
|
||||
return acc;
|
||||
}
|
||||
|
||||
/* Call f for each remaining int (side effects; f's return is ignored) */
|
||||
func Iter_ForEachInt(it: *Iter<int>, f: func(int) -> int) {
|
||||
/* Call f for each remaining element (return value of f is ignored) */
|
||||
func Iter_ForEach<T>(it: *Iter<T>, f: func(T) -> int) {
|
||||
var i: uint = it.pos;
|
||||
while i < it.len {
|
||||
let _ignored: int = f(it.data[i]);
|
||||
@@ -168,7 +168,7 @@ func Iter_ForEachInt(it: *Iter<int>, f: func(int) -> int) {
|
||||
}
|
||||
|
||||
/* True if any remaining element satisfies pred */
|
||||
func Iter_AnyInt(it: *Iter<int>, pred: func(int) -> bool) -> bool {
|
||||
func Iter_Any<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool {
|
||||
var i: uint = it.pos;
|
||||
while i < it.len {
|
||||
if pred(it.data[i]) {
|
||||
@@ -180,7 +180,7 @@ func Iter_AnyInt(it: *Iter<int>, pred: func(int) -> bool) -> bool {
|
||||
}
|
||||
|
||||
/* True if all remaining elements satisfy pred (true if empty) */
|
||||
func Iter_AllInt(it: *Iter<int>, pred: func(int) -> bool) -> bool {
|
||||
func Iter_All<T>(it: *Iter<T>, pred: func(T) -> bool) -> bool {
|
||||
var i: uint = it.pos;
|
||||
while i < it.len {
|
||||
if !pred(it.data[i]) {
|
||||
@@ -202,4 +202,32 @@ func Iter_SumInt(it: *Iter<int>) -> int {
|
||||
return total;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Int-specialized aliases (backward compatible with earlier examples)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func Iter_MapInt(it: *Iter<int>, f: func(int) -> int) -> Array<int> {
|
||||
return Iter_Map<int, int>(it, f);
|
||||
}
|
||||
|
||||
func Iter_FilterInt(it: *Iter<int>, pred: func(int) -> bool) -> Array<int> {
|
||||
return Iter_Filter<int>(it, pred);
|
||||
}
|
||||
|
||||
func Iter_FoldInt(it: *Iter<int>, init: int, f: func(int, int) -> int) -> int {
|
||||
return Iter_Fold<int, int>(it, init, f);
|
||||
}
|
||||
|
||||
func Iter_ForEachInt(it: *Iter<int>, f: func(int) -> int) {
|
||||
Iter_ForEach<int>(it, f);
|
||||
}
|
||||
|
||||
func Iter_AnyInt(it: *Iter<int>, pred: func(int) -> bool) -> bool {
|
||||
return Iter_Any<int>(it, pred);
|
||||
}
|
||||
|
||||
func Iter_AllInt(it: *Iter<int>, pred: func(int) -> bool) -> bool {
|
||||
return Iter_All<int>(it, pred);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+19
-33
@@ -774,32 +774,17 @@ func CBE_FatPartToC(part: String) -> String {
|
||||
// Emit typedefs for common BuxFn_* shapes (fat function pointers)
|
||||
func CBE_EmitFatFuncTypedefs(cbe: *CEmitter, mod: *HirModule) {
|
||||
StringBuilder_Append(&cbe.sb, "/* Fat function pointer types (code + env) */\n");
|
||||
// (int)->int
|
||||
StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_int_int {\n");
|
||||
StringBuilder_Append(&cbe.sb, " int (*code)(void* env, int a0);\n");
|
||||
StringBuilder_Append(&cbe.sb, " void* env;\n");
|
||||
StringBuilder_Append(&cbe.sb, "} BuxFn_int_int;\n");
|
||||
// (int,int)->int
|
||||
StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_int_int_int {\n");
|
||||
StringBuilder_Append(&cbe.sb, " int (*code)(void* env, int a0, int a1);\n");
|
||||
StringBuilder_Append(&cbe.sb, " void* env;\n");
|
||||
StringBuilder_Append(&cbe.sb, "} BuxFn_int_int_int;\n");
|
||||
// (int)->bool
|
||||
StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_bool_int {\n");
|
||||
StringBuilder_Append(&cbe.sb, " bool (*code)(void* env, int a0);\n");
|
||||
StringBuilder_Append(&cbe.sb, " void* env;\n");
|
||||
StringBuilder_Append(&cbe.sb, "} BuxFn_bool_int;\n");
|
||||
// ()->void
|
||||
StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_void_void {\n");
|
||||
StringBuilder_Append(&cbe.sb, " void (*code)(void* env);\n");
|
||||
StringBuilder_Append(&cbe.sb, " void* env;\n");
|
||||
StringBuilder_Append(&cbe.sb, "} BuxFn_void_void;\n");
|
||||
// ()->int
|
||||
StringBuilder_Append(&cbe.sb, "typedef struct BuxFn_int_void {\n");
|
||||
StringBuilder_Append(&cbe.sb, " int (*code)(void* env);\n");
|
||||
StringBuilder_Append(&cbe.sb, " void* env;\n");
|
||||
StringBuilder_Append(&cbe.sb, "} BuxFn_int_void;\n");
|
||||
// Scan module for any other BuxFn_* names
|
||||
// Always emit core shapes
|
||||
CBE_EmitOneFatTypedef(cbe, "BuxFn_int_int");
|
||||
CBE_EmitOneFatTypedef(cbe, "BuxFn_int_int_int");
|
||||
CBE_EmitOneFatTypedef(cbe, "BuxFn_bool_int");
|
||||
CBE_EmitOneFatTypedef(cbe, "BuxFn_void_void");
|
||||
CBE_EmitOneFatTypedef(cbe, "BuxFn_int_void");
|
||||
CBE_EmitOneFatTypedef(cbe, "BuxFn_cstr_int");
|
||||
CBE_EmitOneFatTypedef(cbe, "BuxFn_int_cstr");
|
||||
CBE_EmitOneFatTypedef(cbe, "BuxFn_bool_cstr");
|
||||
CBE_EmitOneFatTypedef(cbe, "BuxFn_int_int_cstr");
|
||||
// Scan module for any other BuxFn_* names (deduped via #ifndef in EmitOne)
|
||||
var i: int = 0;
|
||||
while i < mod.funcCount {
|
||||
CBE_MaybeEmitExtraFat(cbe, mod.funcs[i].retTypeName);
|
||||
@@ -826,12 +811,6 @@ func CBE_EmitFatFuncTypedefs(cbe: *CEmitter, mod: *HirModule) {
|
||||
func CBE_MaybeEmitExtraFat(cbe: *CEmitter, name: String) {
|
||||
if String_Eq(name, "") { return; }
|
||||
if !String_StartsWith(name, "BuxFn_") { return; }
|
||||
// Skip ones we already emit as built-ins
|
||||
if String_Eq(name, "BuxFn_int_int") { return; }
|
||||
if String_Eq(name, "BuxFn_int_int_int") { return; }
|
||||
if String_Eq(name, "BuxFn_bool_int") { return; }
|
||||
if String_Eq(name, "BuxFn_void_void") { return; }
|
||||
if String_Eq(name, "BuxFn_int_void") { return; }
|
||||
CBE_EmitOneFatTypedef(cbe, name);
|
||||
}
|
||||
|
||||
@@ -859,6 +838,13 @@ func CBE_EmitOneFatTypedef(cbe: *CEmitter, fatName: String) {
|
||||
let retPart: String = String_SplitPart(rest, "_", 0);
|
||||
let retC: String = CBE_FatPartToC(retPart);
|
||||
|
||||
// Guard against redefinition if the same name is emitted twice
|
||||
StringBuilder_Append(&cbe.sb, "#ifndef ");
|
||||
StringBuilder_Append(&cbe.sb, fatName);
|
||||
StringBuilder_Append(&cbe.sb, "_DEFINED\n#define ");
|
||||
StringBuilder_Append(&cbe.sb, fatName);
|
||||
StringBuilder_Append(&cbe.sb, "_DEFINED\n");
|
||||
|
||||
StringBuilder_Append(&cbe.sb, "typedef struct ");
|
||||
StringBuilder_Append(&cbe.sb, fatName);
|
||||
StringBuilder_Append(&cbe.sb, " {\n ");
|
||||
@@ -876,7 +862,7 @@ func CBE_EmitOneFatTypedef(cbe: *CEmitter, fatName: String) {
|
||||
}
|
||||
StringBuilder_Append(&cbe.sb, ");\n void* env;\n} ");
|
||||
StringBuilder_Append(&cbe.sb, fatName);
|
||||
StringBuilder_Append(&cbe.sb, ";\n");
|
||||
StringBuilder_Append(&cbe.sb, ";\n#endif\n");
|
||||
}
|
||||
|
||||
func CBE_EmitMakerDecl(cbe: *CEmitter, f: *HirFunc) {
|
||||
|
||||
@@ -152,6 +152,35 @@ func Lcx_SubstituteType(ctx: *LowerCtx, te: *TypeExpr) -> *TypeExpr {
|
||||
return r;
|
||||
}
|
||||
|
||||
// Fat function type: func(T)->U — substitute params and return
|
||||
if te.kind == tekFunc {
|
||||
let r: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||
r.kind = tekFunc;
|
||||
r.line = te.line;
|
||||
r.column = te.column;
|
||||
r.funcParamCount = te.funcParamCount;
|
||||
r.funcRet = Lcx_SubstituteType(ctx, te.funcRet);
|
||||
var head: *TypeExprList = null as *TypeExprList;
|
||||
var tail: *TypeExprList = null as *TypeExprList;
|
||||
var cur: *TypeExprList = te.funcParams;
|
||||
while cur != null as *TypeExprList {
|
||||
let node: *TypeExprList = bux_alloc(sizeof(TypeExprList)) as *TypeExprList;
|
||||
node.te = Lcx_SubstituteType(ctx, cur.te);
|
||||
node.next = null as *TypeExprList;
|
||||
if head == null as *TypeExprList {
|
||||
head = node;
|
||||
tail = node;
|
||||
} else {
|
||||
tail.next = node;
|
||||
tail = node;
|
||||
}
|
||||
cur = cur.next;
|
||||
}
|
||||
r.funcParams = head;
|
||||
r.typeName = Lcx_BuildFuncTypeName(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
return te;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user