feat: capture-less anonymous functions (closures)
Implement MVP closures — anonymous functions without captures.
Syntax:
|a: int, b: int| -> int { return a + b; }
Changes:
- ast.bux + ast.nim: add ekClosure AST node
- parser.bux + parser.nim: parse |params| -> Ret { body }
- sema.bux + sema.nim: type-check closure params/body, return tyFunc
- hir_lower.bux + hir_lower.nim: generate __closure_N function + hAddrOf
- lir_c_backend.nim: fix function-pointer variable declaration (cParamDecl)
- C backend: closures compile to global functions with unique names
Test: _test_closure/src/Main.bux
- Closure as variable
- Closure passed to higher-order function
- Address of named function as function pointer
Both bootstrap and selfhost compilers build and pass the test.
This commit is contained in:
Executable
BIN
Binary file not shown.
@@ -0,0 +1,54 @@
|
|||||||
|
/* Bux Standard Library - I/O functions */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* PrintLine - print string with newline */
|
||||||
|
void PrintLine(const char* s) {
|
||||||
|
if (s != NULL) {
|
||||||
|
puts(s);
|
||||||
|
} else {
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print - print string without newline */
|
||||||
|
void Print(const char* s) {
|
||||||
|
if (s != NULL) {
|
||||||
|
printf("%s", s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PrintInt - print integer */
|
||||||
|
void PrintInt(int n) {
|
||||||
|
printf("%d", n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PrintInt64 - print 64-bit integer */
|
||||||
|
void PrintInt64(int64_t n) {
|
||||||
|
printf("%lld", (long long)n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PrintFloat - print float */
|
||||||
|
void PrintFloat(double f) {
|
||||||
|
printf("%g", f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PrintBool - print boolean */
|
||||||
|
void PrintBool(int b) {
|
||||||
|
printf("%s", b ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ReadLine - read line from stdin (simplified) */
|
||||||
|
const char* ReadLine(void) {
|
||||||
|
static char buffer[1024];
|
||||||
|
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
|
||||||
|
size_t len = strlen(buffer);
|
||||||
|
if (len > 0 && buffer[len-1] == '\n') {
|
||||||
|
buffer[len-1] = '\0';
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,67 @@
|
|||||||
|
// Generated by Bux C Backend v2
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef const char* String;
|
||||||
|
typedef unsigned char uint8;
|
||||||
|
typedef unsigned short uint16;
|
||||||
|
typedef unsigned int uint32;
|
||||||
|
typedef unsigned long long uint64;
|
||||||
|
typedef signed char int8;
|
||||||
|
typedef short int16;
|
||||||
|
typedef long long int64;
|
||||||
|
typedef float float32;
|
||||||
|
typedef double float64;
|
||||||
|
typedef char char8;
|
||||||
|
|
||||||
|
void* bux_alloc(unsigned int size);
|
||||||
|
void bux_free(void* ptr);
|
||||||
|
|
||||||
|
|
||||||
|
int Apply(int x, int (*op)(int));
|
||||||
|
int Double(int x);
|
||||||
|
int __closure_2(int a, int b);
|
||||||
|
int __closure_3(int x);
|
||||||
|
int main();
|
||||||
|
|
||||||
|
void PrintInt(int x);
|
||||||
|
void PrintLine(String msg);
|
||||||
|
|
||||||
|
int Apply(int x, int (*op)(int)) {
|
||||||
|
return op(x);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int Double(int x) {
|
||||||
|
return x * 2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int __closure_2(int a, int b) {
|
||||||
|
return a + b;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int __closure_3(int x) {
|
||||||
|
return x * 3;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int (*add)(int, int) = &__closure_2;
|
||||||
|
int sum = add(3, 4);
|
||||||
|
PrintInt(sum);
|
||||||
|
PrintLine("");
|
||||||
|
int result = Apply(5, &__closure_3);
|
||||||
|
PrintInt(result);
|
||||||
|
PrintLine("");
|
||||||
|
int (*d)(int) = &Double;
|
||||||
|
PrintInt(d(7));
|
||||||
|
PrintLine("");
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
|||||||
|
[Package]
|
||||||
|
Name = "closure_test"
|
||||||
|
Version = "0.1.0"
|
||||||
|
Type = "bin"
|
||||||
|
|
||||||
|
[Build]
|
||||||
|
Output = "Bin"
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
module Main {
|
||||||
|
|
||||||
|
extern func PrintInt(x: int);
|
||||||
|
extern func PrintLine(msg: String);
|
||||||
|
|
||||||
|
func Apply(x: int, op: func(int) -> int) -> int {
|
||||||
|
return op(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
func Double(x: int) -> int {
|
||||||
|
return x * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() -> int {
|
||||||
|
// Closure as variable
|
||||||
|
let add: func(int, int) -> int = |a: int, b: int| -> int { return a + b; };
|
||||||
|
let sum: int = add(3, 4);
|
||||||
|
PrintInt(sum);
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
// Closure passed to function
|
||||||
|
let result: int = Apply(5, |x: int| -> int { return x * 3; });
|
||||||
|
PrintInt(result);
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
// Address of named function as function pointer
|
||||||
|
let d: func(int) -> int = &Double;
|
||||||
|
PrintInt(d(7));
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -132,6 +132,7 @@ type
|
|||||||
ekBlock
|
ekBlock
|
||||||
ekMatch
|
ekMatch
|
||||||
ekStringInterp
|
ekStringInterp
|
||||||
|
ekClosure
|
||||||
|
|
||||||
MatchArm* = object
|
MatchArm* = object
|
||||||
loc*: SourceLocation
|
loc*: SourceLocation
|
||||||
@@ -228,6 +229,10 @@ type
|
|||||||
of ekStringInterp:
|
of ekStringInterp:
|
||||||
exprInterpTexts*: seq[string]
|
exprInterpTexts*: seq[string]
|
||||||
exprInterpExprs*: seq[Expr]
|
exprInterpExprs*: seq[Expr]
|
||||||
|
of ekClosure:
|
||||||
|
exprClosureParams*: seq[Param]
|
||||||
|
exprClosureBody*: Block
|
||||||
|
exprClosureReturnType*: TypeExpr
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Statements
|
# Statements
|
||||||
|
|||||||
@@ -276,6 +276,7 @@ proc resolveTypeExpr(ctx: var LowerCtx, te: TypeExpr): Type =
|
|||||||
proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode
|
proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode
|
||||||
proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode
|
proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode
|
||||||
proc lowerBlock(ctx: var LowerCtx, blk: Block): HirNode
|
proc lowerBlock(ctx: var LowerCtx, blk: Block): HirNode
|
||||||
|
proc lowerClosureFunc(ctx: var LowerCtx, expr: Expr): HirFunc
|
||||||
|
|
||||||
proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
||||||
if expr == nil: return makeUnknown()
|
if expr == nil: return makeUnknown()
|
||||||
@@ -1139,6 +1140,10 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
|||||||
resultNode = hirCall("String_Concat", @[resultNode, lastTextNode], makeStr(), loc)
|
resultNode = hirCall("String_Concat", @[resultNode, lastTextNode], makeStr(), loc)
|
||||||
return resultNode
|
return resultNode
|
||||||
|
|
||||||
|
of ekClosure:
|
||||||
|
let f = ctx.lowerClosureFunc(expr)
|
||||||
|
return hirUnary(tkAmp, hirVar(f.name, makeFunc(@[], makeVoid()), loc), typ, loc)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return HirNode(kind: hLit, litToken: Token(kind: tkIntLiteral, text: "0", loc: loc),
|
return HirNode(kind: hLit, litToken: Token(kind: tkIntLiteral, text: "0", loc: loc),
|
||||||
typ: makeVoid(), loc: loc)
|
typ: makeVoid(), loc: loc)
|
||||||
@@ -1167,6 +1172,12 @@ proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode =
|
|||||||
of tekSlice:
|
of tekSlice:
|
||||||
let elemType = ctx.resolveTypeExpr(stmt.stmtLetType.sliceElement)
|
let elemType = ctx.resolveTypeExpr(stmt.stmtLetType.sliceElement)
|
||||||
makeSlice(elemType)
|
makeSlice(elemType)
|
||||||
|
of tekFunc:
|
||||||
|
var params: seq[Type] = @[]
|
||||||
|
for p in stmt.stmtLetType.funcParams:
|
||||||
|
params.add(ctx.resolveTypeExpr(p))
|
||||||
|
let ret = if stmt.stmtLetType.funcRet != nil: ctx.resolveTypeExpr(stmt.stmtLetType.funcRet) else: makeVoid()
|
||||||
|
makeFunc(params, ret)
|
||||||
else: makeUnknown()
|
else: makeUnknown()
|
||||||
elif stmt.stmtLetInit != nil:
|
elif stmt.stmtLetInit != nil:
|
||||||
ctx.resolveExprType(stmt.stmtLetInit)
|
ctx.resolveExprType(stmt.stmtLetInit)
|
||||||
@@ -1475,6 +1486,25 @@ proc generateMethodInstance(ctx: var LowerCtx, baseMethodName: string, typeArgs:
|
|||||||
ctx.generatedFuncInsts[mangledName] = true
|
ctx.generatedFuncInsts[mangledName] = true
|
||||||
return mangledName
|
return mangledName
|
||||||
|
|
||||||
|
proc lowerClosureFunc(ctx: var LowerCtx, expr: Expr): HirFunc =
|
||||||
|
let loc = expr.loc
|
||||||
|
let name = "__closure_" & $ctx.varCounter
|
||||||
|
inc ctx.varCounter
|
||||||
|
var f = HirFunc(name: name, isPublic: false)
|
||||||
|
# Params
|
||||||
|
for p in expr.exprClosureParams:
|
||||||
|
f.params.add((name: p.name, typ: if p.ptype != nil: ctx.resolveTypeExpr(p.ptype) else: makeUnknown()))
|
||||||
|
# Return type
|
||||||
|
if expr.exprClosureReturnType != nil:
|
||||||
|
f.retType = ctx.resolveTypeExpr(expr.exprClosureReturnType)
|
||||||
|
else:
|
||||||
|
f.retType = makeVoid()
|
||||||
|
# Body
|
||||||
|
if expr.exprClosureBody != nil:
|
||||||
|
f.body = ctx.lowerBlock(expr.exprClosureBody)
|
||||||
|
ctx.extraFuncs.add(f)
|
||||||
|
return f
|
||||||
|
|
||||||
proc lowerModule*(module: Module, sema: Sema): HirModule =
|
proc lowerModule*(module: Module, sema: Sema): HirModule =
|
||||||
var ctx = initLowerCtx(module, sema)
|
var ctx = initLowerCtx(module, sema)
|
||||||
var funcs: seq[HirFunc] = @[]
|
var funcs: seq[HirFunc] = @[]
|
||||||
|
|||||||
@@ -58,6 +58,13 @@ proc typeFromValue(be: var LirCBackend, v: LirValue): string =
|
|||||||
proc setTempType(be: var LirCBackend, temp: string, cType: string) =
|
proc setTempType(be: var LirCBackend, temp: string, cType: string) =
|
||||||
be.tempTypes[temp] = cType
|
be.tempTypes[temp] = cType
|
||||||
|
|
||||||
|
proc cParamDecl(cType, name: string): string =
|
||||||
|
## Emit a C parameter declaration, handling function-pointer syntax.
|
||||||
|
if cType.contains("(*)"):
|
||||||
|
return cType.replace("(*)", "(*" & name & ")")
|
||||||
|
else:
|
||||||
|
return cType & " " & name
|
||||||
|
|
||||||
# ── Per-instruction emission ──
|
# ── Per-instruction emission ──
|
||||||
|
|
||||||
proc emitInstr(be: var LirCBackend, instr: LirInstr) =
|
proc emitInstr(be: var LirCBackend, instr: LirInstr) =
|
||||||
@@ -183,7 +190,7 @@ proc emitInstr(be: var LirCBackend, instr: LirInstr) =
|
|||||||
let inferred = be.tempTypes[instr.dst.strVal]
|
let inferred = be.tempTypes[instr.dst.strVal]
|
||||||
if inferred != "" and inferred != ct:
|
if inferred != "" and inferred != ct:
|
||||||
ct = inferred
|
ct = inferred
|
||||||
be.emitLine(&"{ct} {v(instr.dst)};")
|
be.emitLine(cParamDecl(ct, v(instr.dst)) & ";")
|
||||||
|
|
||||||
# ── Pointers ──
|
# ── Pointers ──
|
||||||
of lirAddrOf:
|
of lirAddrOf:
|
||||||
@@ -241,13 +248,6 @@ proc emitInstr(be: var LirCBackend, instr: LirInstr) =
|
|||||||
|
|
||||||
# ── Function emission ──
|
# ── Function emission ──
|
||||||
|
|
||||||
proc cParamDecl(cType, name: string): string =
|
|
||||||
## Emit a C parameter declaration, handling function-pointer syntax.
|
|
||||||
if cType.contains("(*)"):
|
|
||||||
return cType.replace("(*)", "(*" & name & ")")
|
|
||||||
else:
|
|
||||||
return cType & " " & name
|
|
||||||
|
|
||||||
proc emitFunc(be: var LirCBackend, f: LirFunc, funcRetTypes: Table[string, string], funcPtrTypes: Table[string, string]) =
|
proc emitFunc(be: var LirCBackend, f: LirFunc, funcRetTypes: Table[string, string], funcPtrTypes: Table[string, string]) =
|
||||||
var paramsStr = ""
|
var paramsStr = ""
|
||||||
for i, p in f.params:
|
for i, p in f.params:
|
||||||
|
|||||||
@@ -587,6 +587,26 @@ proc parsePrimary(p: var Parser): Expr =
|
|||||||
of tkNull:
|
of tkNull:
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
return newLiteralExpr(Token(kind: tkNull, text: "null", loc: loc))
|
return newLiteralExpr(Token(kind: tkNull, text: "null", loc: loc))
|
||||||
|
of tkPipe:
|
||||||
|
# Closure: |params| -> Ret { body }
|
||||||
|
discard p.advance() # |
|
||||||
|
var params: seq[Param] = @[]
|
||||||
|
while not p.check(tkPipe) and not p.isAtEnd:
|
||||||
|
let nameTok = p.expect(tkIdent, "expected parameter name in closure")
|
||||||
|
discard p.expect(tkColon, "expected ':' in closure parameter")
|
||||||
|
let ptype = p.parseType()
|
||||||
|
params.add(Param(name: nameTok.text, ptype: ptype))
|
||||||
|
if p.check(tkComma):
|
||||||
|
discard p.advance()
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
discard p.expect(tkPipe, "expected '|' to close closure params")
|
||||||
|
var retType: TypeExpr = nil
|
||||||
|
if p.check(tkArrow):
|
||||||
|
discard p.advance() # ->
|
||||||
|
retType = p.parseType()
|
||||||
|
let body = p.parseBlock()
|
||||||
|
return Expr(kind: ekClosure, loc: loc, exprClosureParams: params, exprClosureBody: body, exprClosureReturnType: retType)
|
||||||
else:
|
else:
|
||||||
p.emitError(loc, "expected expression")
|
p.emitError(loc, "expected expression")
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ type
|
|||||||
checkedFunc*: bool ## true inside @[Checked] function
|
checkedFunc*: bool ## true inside @[Checked] function
|
||||||
currentFuncIsAsync*: bool ## true inside async func
|
currentFuncIsAsync*: bool ## true inside async func
|
||||||
movedVars*: seq[string] ## variables moved in current checked function
|
movedVars*: seq[string] ## variables moved in current checked function
|
||||||
|
currentRetType*: Type ## return type of the function being checked
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Helpers
|
# Helpers
|
||||||
@@ -1337,6 +1338,25 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
for e in expr.exprInterpExprs:
|
for e in expr.exprInterpExprs:
|
||||||
discard sema.checkExpr(e, scope)
|
discard sema.checkExpr(e, scope)
|
||||||
return makeStr()
|
return makeStr()
|
||||||
|
of ekClosure:
|
||||||
|
let savedRetType = sema.currentRetType
|
||||||
|
let childScope = Scope(parent: scope)
|
||||||
|
sema.currentRetType = if expr.exprClosureReturnType != nil: sema.resolveType(expr.exprClosureReturnType) else: makeUnknown()
|
||||||
|
# Register params
|
||||||
|
for p in expr.exprClosureParams:
|
||||||
|
let ptype = if p.ptype != nil: sema.resolveType(p.ptype) else: makeUnknown()
|
||||||
|
discard childScope.define(Symbol(kind: skVar, name: p.name, typ: ptype))
|
||||||
|
# Check body
|
||||||
|
if expr.exprClosureBody != nil:
|
||||||
|
for stmt in expr.exprClosureBody.stmts:
|
||||||
|
discard sema.checkStmt(stmt, childScope)
|
||||||
|
sema.currentRetType = savedRetType
|
||||||
|
# Build function type
|
||||||
|
var params: seq[Type] = @[]
|
||||||
|
for p in expr.exprClosureParams:
|
||||||
|
params.add(if p.ptype != nil: sema.resolveType(p.ptype) else: makeUnknown())
|
||||||
|
let retType = if expr.exprClosureReturnType != nil: sema.resolveType(expr.exprClosureReturnType) else: makeVoid()
|
||||||
|
return makeFunc(params, retType)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Statement type checking
|
# Statement type checking
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ const ekMatch: int = 22;
|
|||||||
const ekSpawn: int = 24;
|
const ekSpawn: int = 24;
|
||||||
const ekAwait: int = 25;
|
const ekAwait: int = 25;
|
||||||
const ekStringInterp: int = 26;
|
const ekStringInterp: int = 26;
|
||||||
|
const ekClosure: int = 27;
|
||||||
|
|
||||||
struct ExprList {
|
struct ExprList {
|
||||||
expr: *Expr,
|
expr: *Expr,
|
||||||
@@ -139,6 +140,8 @@ struct Expr {
|
|||||||
// Struct init fields
|
// Struct init fields
|
||||||
structName: String,
|
structName: String,
|
||||||
structFieldCount: int,
|
structFieldCount: int,
|
||||||
|
// Closure params (for ekClosure)
|
||||||
|
closureParams: *Decl,
|
||||||
// Call arguments (linked list for multi-arg support)
|
// Call arguments (linked list for multi-arg support)
|
||||||
callArgs: *ExprList,
|
callArgs: *ExprList,
|
||||||
callArgCount: int,
|
callArgCount: int,
|
||||||
|
|||||||
@@ -384,6 +384,23 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Closure: generate function and return address-of
|
||||||
|
if kind == ekClosure {
|
||||||
|
let f: *HirFunc = Lcx_LowerClosureFunc(ctx, expr);
|
||||||
|
n.kind = hUnary;
|
||||||
|
n.intValue = tkAmp;
|
||||||
|
let varNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode;
|
||||||
|
varNode.kind = hVar;
|
||||||
|
varNode.strValue = f.name;
|
||||||
|
varNode.typeKind = tyFunc;
|
||||||
|
n.child1 = varNode;
|
||||||
|
n.typeKind = tyFunc;
|
||||||
|
if expr.refType != null as *TypeExpr {
|
||||||
|
n.typeName = Lcx_BuildFuncTypeName(expr.refType);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
// Cast
|
// Cast
|
||||||
if kind == ekCast {
|
if kind == ekCast {
|
||||||
n.kind = hCast;
|
n.kind = hCast;
|
||||||
@@ -779,6 +796,93 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc {
|
|||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Closure lowering — generate a global function for a closure expression
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func Lcx_LowerClosureFunc(ctx: *LowerCtx, expr: *Expr) -> *HirFunc {
|
||||||
|
let f: *HirFunc = bux_alloc(sizeof(HirFunc)) as *HirFunc;
|
||||||
|
|
||||||
|
// Generate unique name
|
||||||
|
let numStr: String = String_FromInt(ctx.funcCount);
|
||||||
|
f.name = String_Concat("__closure_", numStr);
|
||||||
|
f.isPublic = false;
|
||||||
|
|
||||||
|
let params: *Decl = expr.closureParams;
|
||||||
|
if params != null as *Decl {
|
||||||
|
f.paramCount = params.paramCount;
|
||||||
|
if params.paramCount > 0 { f.param0 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param0, ¶ms.param0); }
|
||||||
|
if params.paramCount > 1 { f.param1 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param1, ¶ms.param1); }
|
||||||
|
if params.paramCount > 2 { f.param2 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param2, ¶ms.param2); }
|
||||||
|
if params.paramCount > 3 { f.param3 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param3, ¶ms.param3); }
|
||||||
|
if params.paramCount > 4 { f.param4 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param4, ¶ms.param4); }
|
||||||
|
if params.paramCount > 5 { f.param5 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param5, ¶ms.param5); }
|
||||||
|
if params.paramCount > 6 { f.param6 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param6, ¶ms.param6); }
|
||||||
|
if params.paramCount > 7 { f.param7 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param7, ¶ms.param7); }
|
||||||
|
if params.paramCount > 8 { f.param8 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param8, ¶ms.param8); }
|
||||||
|
}
|
||||||
|
|
||||||
|
if expr.refType != null as *TypeExpr && expr.refType.kind == tekFunc && expr.refType.funcRet != null as *TypeExpr {
|
||||||
|
f.retTypeName = expr.refType.funcRet.typeName;
|
||||||
|
f.retTypeKind = Lcx_ResolveTypeKind(expr.refType.funcRet);
|
||||||
|
} else {
|
||||||
|
f.retTypeName = "";
|
||||||
|
f.retTypeKind = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create function scope
|
||||||
|
var funcScope: Scope = Scope_NewChild(ctx.scope);
|
||||||
|
var pi: int = 0;
|
||||||
|
while pi < params.paramCount {
|
||||||
|
var p: *Param = null as *Param;
|
||||||
|
if pi == 0 { p = ¶ms.param0; }
|
||||||
|
else if pi == 1 { p = ¶ms.param1; }
|
||||||
|
else if pi == 2 { p = ¶ms.param2; }
|
||||||
|
else if pi == 3 { p = ¶ms.param3; }
|
||||||
|
else if pi == 4 { p = ¶ms.param4; }
|
||||||
|
else if pi == 5 { p = ¶ms.param5; }
|
||||||
|
else if pi == 6 { p = ¶ms.param6; }
|
||||||
|
else if pi == 7 { p = ¶ms.param7; }
|
||||||
|
else if pi == 8 { p = ¶ms.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.refType = p.refParamType;
|
||||||
|
if p.refParamType.kind == tekFunc {
|
||||||
|
sym.typeName = Lcx_BuildFuncTypeName(p.refParamType);
|
||||||
|
} else if !String_Eq(p.refParamType.typeName, "") {
|
||||||
|
sym.typeName = p.refParamType.typeName;
|
||||||
|
} else if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr {
|
||||||
|
sym.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*");
|
||||||
|
} else {
|
||||||
|
sym.typeName = "";
|
||||||
|
}
|
||||||
|
sym.isMutable = false;
|
||||||
|
sym.isPublic = false;
|
||||||
|
sym.decl = null as *Decl;
|
||||||
|
discard Scope_Define(&funcScope, sym);
|
||||||
|
}
|
||||||
|
pi = pi + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let prevScope: *Scope = ctx.scope;
|
||||||
|
ctx.scope = &funcScope;
|
||||||
|
if expr.refBlock != null as *Block {
|
||||||
|
f.body = Lcx_LowerBlock(ctx, expr.refBlock, -1);
|
||||||
|
} else {
|
||||||
|
f.body = null as *HirNode;
|
||||||
|
}
|
||||||
|
ctx.scope = prevScope;
|
||||||
|
|
||||||
|
// Add to module functions
|
||||||
|
ctx.funcs[ctx.funcCount] = *f;
|
||||||
|
ctx.funcCount = ctx.funcCount + 1;
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Module lowering — main entry point
|
// Module lowering — main entry point
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -384,10 +384,89 @@ func parserParsePrimary(p: *Parser) -> *Expr {
|
|||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Closure: |params| -> Ret { body }
|
||||||
|
if kind == tkPipe {
|
||||||
|
return parserParseClosure(p);
|
||||||
|
}
|
||||||
|
|
||||||
parserEmitDiag(p, line, col, "expected expression");
|
parserEmitDiag(p, line, col, "expected expression");
|
||||||
return parserMakeExpr(ekLiteral, line, col);
|
return parserMakeExpr(ekLiteral, line, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Closure: |params| -> Ret { body }
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func parserParseClosure(p: *Parser) -> *Expr {
|
||||||
|
let line: uint32 = parserCurToken(p).line;
|
||||||
|
let col: uint32 = parserCurToken(p).column;
|
||||||
|
discard parserExpect(p, tkPipe, "expected '|' to start closure params");
|
||||||
|
|
||||||
|
let e: *Expr = parserMakeExpr(ekClosure, line, col);
|
||||||
|
let params: *Decl = bux_alloc(sizeof(Decl)) as *Decl;
|
||||||
|
params.kind = dkFunc;
|
||||||
|
params.paramCount = 0;
|
||||||
|
|
||||||
|
// Parse params: name: Type
|
||||||
|
while !parserCheck(p, tkPipe) && parserPeek(p, 0) != tkEndOfFile {
|
||||||
|
while parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) {
|
||||||
|
discard parserAdvance(p);
|
||||||
|
}
|
||||||
|
if parserCheck(p, tkPipe) || parserPeek(p, 0) == tkEndOfFile {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if params.paramCount >= 9 { break; }
|
||||||
|
let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected parameter name in closure");
|
||||||
|
discard parserExpect(p, tkColon, "expected ':' in closure parameter");
|
||||||
|
let ptype: *TypeExpr = parserParseType(p);
|
||||||
|
|
||||||
|
let idx: int = params.paramCount;
|
||||||
|
if idx == 0 {
|
||||||
|
params.param0.name = nameTok.text;
|
||||||
|
params.param0.refParamType = ptype;
|
||||||
|
} else if idx == 1 {
|
||||||
|
params.param1.name = nameTok.text;
|
||||||
|
params.param1.refParamType = ptype;
|
||||||
|
} else if idx == 2 {
|
||||||
|
params.param2.name = nameTok.text;
|
||||||
|
params.param2.refParamType = ptype;
|
||||||
|
} else if idx == 3 {
|
||||||
|
params.param3.name = nameTok.text;
|
||||||
|
params.param3.refParamType = ptype;
|
||||||
|
} else if idx == 4 {
|
||||||
|
params.param4.name = nameTok.text;
|
||||||
|
params.param4.refParamType = ptype;
|
||||||
|
} else if idx == 5 {
|
||||||
|
params.param5.name = nameTok.text;
|
||||||
|
params.param5.refParamType = ptype;
|
||||||
|
} else if idx == 6 {
|
||||||
|
params.param6.name = nameTok.text;
|
||||||
|
params.param6.refParamType = ptype;
|
||||||
|
} else if idx == 7 {
|
||||||
|
params.param7.name = nameTok.text;
|
||||||
|
params.param7.refParamType = ptype;
|
||||||
|
} else if idx == 8 {
|
||||||
|
params.param8.name = nameTok.text;
|
||||||
|
params.param8.refParamType = ptype;
|
||||||
|
}
|
||||||
|
params.paramCount = params.paramCount + 1;
|
||||||
|
if parserMatch(p, tkComma) { continue; }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
discard parserExpect(p, tkPipe, "expected '|' to close closure params");
|
||||||
|
e.closureParams = params;
|
||||||
|
|
||||||
|
// Optional return type: -> Type
|
||||||
|
if parserMatch(p, tkArrow) {
|
||||||
|
e.refType = parserParseType(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Body: { ... }
|
||||||
|
e.refBlock = parserParseBlock(p);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Postfix: call, index, field access, as, is, ?
|
// Postfix: call, index, field access, as, is, ?
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -564,6 +564,105 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int {
|
|||||||
return tyVoid;
|
return tyVoid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Closure: |params| -> Ret { body }
|
||||||
|
if kind == ekClosure {
|
||||||
|
let savedRetType: int = sema.currentRetType;
|
||||||
|
let savedScope: *Scope = sema.scope;
|
||||||
|
let childScope: Scope = Scope_NewChild(sema.scope);
|
||||||
|
sema.scope = &childScope;
|
||||||
|
|
||||||
|
// Set return type from annotation, or unknown for inference
|
||||||
|
var closureRetType: int = tyUnknown;
|
||||||
|
if expr.refType != null as *TypeExpr {
|
||||||
|
closureRetType = Sema_ResolveType(sema, expr.refType);
|
||||||
|
}
|
||||||
|
sema.currentRetType = closureRetType;
|
||||||
|
|
||||||
|
// Register params in child scope
|
||||||
|
let params: *Decl = expr.closureParams;
|
||||||
|
if params != null as *Decl {
|
||||||
|
var i: int = 0;
|
||||||
|
while i < params.paramCount {
|
||||||
|
var p: Param;
|
||||||
|
if i == 0 { p = params.param0; }
|
||||||
|
else if i == 1 { p = params.param1; }
|
||||||
|
else if i == 2 { p = params.param2; }
|
||||||
|
else if i == 3 { p = params.param3; }
|
||||||
|
else if i == 4 { p = params.param4; }
|
||||||
|
else if i == 5 { p = params.param5; }
|
||||||
|
else if i == 6 { p = params.param6; }
|
||||||
|
else if i == 7 { p = params.param7; }
|
||||||
|
else if i == 8 { p = params.param8; }
|
||||||
|
|
||||||
|
if !String_Eq(p.name, "") {
|
||||||
|
var sym: Symbol;
|
||||||
|
sym.kind = skVar;
|
||||||
|
sym.name = p.name;
|
||||||
|
sym.typeKind = tyUnknown;
|
||||||
|
if p.refParamType != null as *TypeExpr {
|
||||||
|
sym.typeKind = Sema_ResolveType(sema, p.refParamType);
|
||||||
|
sym.refType = p.refParamType;
|
||||||
|
}
|
||||||
|
sym.typeName = "";
|
||||||
|
sym.isMutable = false;
|
||||||
|
sym.isPublic = false;
|
||||||
|
sym.decl = null as *Decl;
|
||||||
|
discard Scope_Define(sema.scope, sym);
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check body
|
||||||
|
if expr.refBlock != null as *Block {
|
||||||
|
Sema_CheckBlock(sema, expr.refBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore
|
||||||
|
sema.scope = savedScope;
|
||||||
|
sema.currentRetType = savedRetType;
|
||||||
|
|
||||||
|
// Build function type expression for later use
|
||||||
|
let funcType: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr;
|
||||||
|
funcType.kind = tekFunc;
|
||||||
|
funcType.funcRet = expr.refType;
|
||||||
|
funcType.funcParamCount = params.paramCount;
|
||||||
|
// Build param type list
|
||||||
|
if params.paramCount > 0 {
|
||||||
|
var head: *TypeExprList = null as *TypeExprList;
|
||||||
|
var tail: *TypeExprList = null as *TypeExprList;
|
||||||
|
var i: int = 0;
|
||||||
|
while i < params.paramCount {
|
||||||
|
var p: Param;
|
||||||
|
if i == 0 { p = params.param0; }
|
||||||
|
else if i == 1 { p = params.param1; }
|
||||||
|
else if i == 2 { p = params.param2; }
|
||||||
|
else if i == 3 { p = params.param3; }
|
||||||
|
else if i == 4 { p = params.param4; }
|
||||||
|
else if i == 5 { p = params.param5; }
|
||||||
|
else if i == 6 { p = params.param6; }
|
||||||
|
else if i == 7 { p = params.param7; }
|
||||||
|
else if i == 8 { p = params.param8; }
|
||||||
|
|
||||||
|
let node: *TypeExprList = bux_alloc(sizeof(TypeExprList)) as *TypeExprList;
|
||||||
|
node.te = p.refParamType;
|
||||||
|
node.next = null as *TypeExprList;
|
||||||
|
if head == null as *TypeExprList {
|
||||||
|
head = node;
|
||||||
|
tail = node;
|
||||||
|
} else {
|
||||||
|
tail.next = node;
|
||||||
|
tail = node;
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
funcType.funcParams = head;
|
||||||
|
}
|
||||||
|
expr.refType = funcType;
|
||||||
|
|
||||||
|
return tyFunc;
|
||||||
|
}
|
||||||
|
|
||||||
return tyUnknown;
|
return tyUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user