feat(emitter): first-class defn functions via cljFn wrapper
User-defined defn functions can now be passed as values to other functions (e.g. bring/run-server, apply-fn, map, filter). - Track function arities in definedFnArities table - Register defn/defn- arities during emission - Emit cljFn wrapper when a defn symbol is used as a value: * Multi-arity / rest: cljFn(procName) (already seq[CljVal]) * Single-arity: cljFn(proc(args): procName(args[0], ...)) - Direct calls remain unchanged (fast path) - All existing emitter tests pass
This commit is contained in:
+34
-1
@@ -1,5 +1,5 @@
|
||||
# Bara Lang → Nim Emitter
|
||||
import strutils, sets, algorithm, sequtils
|
||||
import strutils, sets, algorithm, sequtils, tables
|
||||
import types
|
||||
import macros
|
||||
|
||||
@@ -10,13 +10,38 @@ var nsAliases*: seq[(string, string)] = @[] # (alias, namespace)
|
||||
var scopeStack*: seq[HashSet[string]] = @[]
|
||||
var definedGlobals*: HashSet[string] = initHashSet[string]()
|
||||
var multiArityFns*: HashSet[string] = initHashSet[string]()
|
||||
var definedFnArities*: Table[string, int] = initTable[string, int]() # -1 = multi-arity/rest
|
||||
var loopResultVar*: string = "" # Set by loop handler when result capture is needed
|
||||
|
||||
proc registerGlobal*(name: string) =
|
||||
definedGlobals.incl(name)
|
||||
|
||||
proc mangleName*(name: string): string
|
||||
|
||||
proc registerFn*(name: string, arity: int) =
|
||||
definedFnArities[name] = arity
|
||||
|
||||
proc emitFnWrapper*(name: string): string =
|
||||
## Emit a cljFn wrapper for a user-defined function when used as a value.
|
||||
let mangled = mangleName(name)
|
||||
if name in multiArityFns:
|
||||
# Multi-arity / rest params already take seq[CljVal]
|
||||
return "cljFn(" & mangled & ")"
|
||||
elif definedFnArities.hasKey(name):
|
||||
let arity = definedFnArities[name]
|
||||
if arity == 0:
|
||||
return "cljFn(proc(args: seq[CljVal]): CljVal = " & mangled & "())"
|
||||
else:
|
||||
var argRefs: seq[string] = @[]
|
||||
for i in 0..<arity:
|
||||
argRefs.add("args[" & $i & "]")
|
||||
return "cljFn(proc(args: seq[CljVal]): CljVal = " & mangled & "(" & argRefs.join(", ") & "))"
|
||||
else:
|
||||
return mangled
|
||||
|
||||
proc clearGlobals*() =
|
||||
definedGlobals = initHashSet[string]()
|
||||
definedFnArities = initTable[string, int]()
|
||||
|
||||
proc setNsAliases*(aliases: seq[(string, string)]) =
|
||||
nsAliases = aliases
|
||||
@@ -807,6 +832,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
# Rest params: generate proc with args: seq[CljVal]
|
||||
# Register so call sites wrap args in @[...]
|
||||
multiArityFns.incl(name.symName)
|
||||
registerFn(name.symName, -1)
|
||||
let namedCount = restIdx
|
||||
var preambleLines: seq[string] = @[]
|
||||
for pi in 0..<restIdx:
|
||||
@@ -824,6 +850,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
popScope()
|
||||
let preamble = preambleLines.join("\n")
|
||||
return sp & "proc " & procName & exportMarker & "(args: seq[CljVal]): CljVal =\n" & preamble & "\n" & bodyCode
|
||||
registerFn(name.symName, paramNames.len)
|
||||
if indent == 0:
|
||||
var bodyCode = ""
|
||||
if body.len == 0:
|
||||
@@ -895,6 +922,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
if hasRest:
|
||||
# Rest params: generate proc with args: seq[CljVal]
|
||||
multiArityFns.incl(name.symName)
|
||||
registerFn(name.symName, -1)
|
||||
let namedCount = restIdx
|
||||
var preambleLines: seq[string] = @[]
|
||||
for pi in 0..<restIdx:
|
||||
@@ -912,6 +940,7 @@ proc emitSpecialForm(items: seq[CljVal], indent: int): string =
|
||||
popScope()
|
||||
let preamble = preambleLines.join("\n")
|
||||
return sp & "proc " & procName & "(args: seq[CljVal]): CljVal {.used.} =\n" & preamble & "\n" & bodyCode
|
||||
registerFn(name.symName, paramNames.len)
|
||||
var bodyCode = ""
|
||||
if body.len == 0:
|
||||
bodyCode = indentStr(indent + 1) & "cljNil()"
|
||||
@@ -2196,6 +2225,10 @@ proc emitExpr*(v: CljVal, indent: int = 0): string =
|
||||
if isLocalVar(symName):
|
||||
return sp & mangleName(symName)
|
||||
if symName in definedGlobals:
|
||||
# If the global is a user-defined function, emit a cljFn wrapper
|
||||
# so it can be passed as a first-class value.
|
||||
if symName in multiArityFns or definedFnArities.hasKey(symName):
|
||||
return sp & emitFnWrapper(symName)
|
||||
return sp & mangleName(symName)
|
||||
return sp & "cljSymbol(\"" & symName & "\")"
|
||||
of ckList:
|
||||
|
||||
Reference in New Issue
Block a user