fix: threading macros, REPL stability, and CI coverage
- fix(emitter): emitFnWrapper now uses runtimeName for builtin operators - fix(emitter): map/reduce handlers support variadic runtime functions (+, -, etc.) - fix(emitter): variadic runtime functions list extracted to variadicRuntimeFns constant - fix(macros): restore correct thread-first behavior (->) and fix result shadowing - fix(macros): and/or/as->/some->/some->> macro result variable fixes - fix(repl): add macroexpand step before interpreter eval - fix(runtime): cljReduceSeq handles nil init (uses first coll element) - fix(eval): add macroexpand/macroexpand-1 special forms to interpreter - fix(eval): add macros module import for macroexpand access - fix(warnings): eliminate ResultShadowed and UnusedImport warnings - chore(.gitignore): remove artifact entries (-o, =0.4.0), unignore lib/bring_http.nim - chore(ci): run all examples + threading macro + macroexpand REPL smoke tests
This commit is contained in:
+70
-47
@@ -1,5 +1,5 @@
|
||||
# Bara Lang → Nim Emitter
|
||||
import strutils, sets, algorithm, sequtils, tables
|
||||
import strutils, sets, sequtils, tables
|
||||
import types
|
||||
import macros
|
||||
|
||||
@@ -13,16 +13,52 @@ 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
|
||||
|
||||
# Runtime functions that accept seq[CljVal] (variadic wrapper signature)
|
||||
let variadicRuntimeFns* = ["+", "-", "*", "/", "=", ">", "<", ">=", "<=", "not=",
|
||||
"println", "prn", "print", "str", "pr-str",
|
||||
"atom", "concat", "min", "max", "merge", "interleave",
|
||||
"zipmap", "hash-map", "hash-set", "sorted-map", "sorted-map-by", "sorted-set", "sorted-set-by", "cons", "use-fixtures", "vswap!", "swap!", "tap>", "add-tap", "remove-tap", "add-watch", "remove-watch", "alter-var-root",
|
||||
"array-map", "inf", "nan", "list",
|
||||
"float", "int", "double", "long", "short", "byte",
|
||||
"boolean", "num", "number",
|
||||
"make-hierarchy", "derive", "underive", "ancestors",
|
||||
"descendants", "parents", "isa?", "promise", "create-ns", "future",
|
||||
"delay",
|
||||
"aclone", "alength", "aget", "int-array", "identical?", "empty", "identity",
|
||||
"conj",
|
||||
"drop-last", "shuffle", "repeatedly", "fnil", "intern",
|
||||
"println-str", "prn-str", "binding", "aset",
|
||||
"volatile!", "deliver", "doall", "dorun",
|
||||
"to-array", "into-array", "vector", "rand", "rand-int",
|
||||
"rand-nth", "random-sample",
|
||||
"assoc", "dissoc", "get", "get-in", "update", "assoc-in",
|
||||
"contains?", "select-keys", "keys", "vals",
|
||||
"disj", "peek", "pop",
|
||||
"transduce", "ex-info",
|
||||
"compare", "subvec",
|
||||
"require", "eval", "resolve", "random-uuid",
|
||||
"vreset!", "restart-agent", "with-out-str",
|
||||
"System/getProperty",
|
||||
"dosync", "alter"]
|
||||
|
||||
proc registerGlobal*(name: string) =
|
||||
definedGlobals.incl(name)
|
||||
|
||||
proc mangleName*(name: string): string
|
||||
proc runtimeName(op: 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 rtName = runtimeName(name)
|
||||
if rtName.len > 0:
|
||||
# Built-in runtime function — emit variadic wrapper if applicable
|
||||
if name in variadicRuntimeFns:
|
||||
return "cljFn(" & rtName & ")"
|
||||
else:
|
||||
return "cljFn(proc(args: seq[CljVal]): CljVal = " & rtName & "(args[0]))"
|
||||
let mangled = mangleName(name)
|
||||
if name in multiArityFns:
|
||||
# Multi-arity / rest params already take seq[CljVal]
|
||||
@@ -137,15 +173,15 @@ proc indentStr(indent: int): string =
|
||||
proc indentCode(code: string, extra: int): string =
|
||||
if extra <= 0: return code
|
||||
let prefix = indentStr(extra)
|
||||
var result = ""
|
||||
var res = ""
|
||||
var lines = code.split("\n")
|
||||
for i, line in lines:
|
||||
if i > 0: result.add("\n")
|
||||
if i > 0: res.add("\n")
|
||||
if line.len > 0:
|
||||
result.add(prefix & line)
|
||||
res.add(prefix & line)
|
||||
else:
|
||||
result.add(line)
|
||||
return result
|
||||
res.add(line)
|
||||
return res
|
||||
|
||||
proc runtimeName(op: string): string =
|
||||
case op
|
||||
@@ -440,26 +476,26 @@ proc letDestructuredBindings(bindings: CljVal): CljVal =
|
||||
# Expand let bindings with vector/map destructuring into simple symbol bindings
|
||||
if bindings.kind != ckVector:
|
||||
return bindings
|
||||
var result: seq[CljVal] = @[]
|
||||
var res: seq[CljVal] = @[]
|
||||
var i = 0
|
||||
while i < bindings.items.len:
|
||||
let bname = bindings.items[i]
|
||||
let bval = bindings.items[i+1]
|
||||
if bname.kind == ckSymbol:
|
||||
result.add(bname)
|
||||
result.add(bval)
|
||||
res.add(bname)
|
||||
res.add(bval)
|
||||
elif bname.kind == ckVector:
|
||||
let tmp = cljSymbol("ds_" & $i)
|
||||
result.add(tmp)
|
||||
result.add(bval)
|
||||
res.add(tmp)
|
||||
res.add(bval)
|
||||
for j in 0..<bname.items.len:
|
||||
if bname.items[j].kind == ckSymbol:
|
||||
result.add(bname.items[j])
|
||||
result.add(cljList(@[cljSymbol("nth"), tmp, cljInt(j)]))
|
||||
res.add(bname.items[j])
|
||||
res.add(cljList(@[cljSymbol("nth"), tmp, cljInt(j)]))
|
||||
elif bname.kind == ckMap:
|
||||
let tmp = cljSymbol("ds_" & $i)
|
||||
result.add(tmp)
|
||||
result.add(bval)
|
||||
res.add(tmp)
|
||||
res.add(bval)
|
||||
var hasAs = false
|
||||
var asName: CljVal = nil
|
||||
var j = 0
|
||||
@@ -469,17 +505,17 @@ proc letDestructuredBindings(bindings: CljVal): CljVal =
|
||||
if key.kind == ckKeyword and key.kwName == "keys" and val.kind == ckVector:
|
||||
for k in 0..<val.items.len:
|
||||
if val.items[k].kind == ckSymbol:
|
||||
result.add(val.items[k])
|
||||
result.add(cljList(@[cljSymbol("get"), tmp, cljKeyword(val.items[k].symName)]))
|
||||
res.add(val.items[k])
|
||||
res.add(cljList(@[cljSymbol("get"), tmp, cljKeyword(val.items[k].symName)]))
|
||||
elif key.kind == ckKeyword and key.kwName == "as":
|
||||
hasAs = true
|
||||
asName = val
|
||||
j += 1
|
||||
if hasAs and asName != nil and asName.kind == ckSymbol:
|
||||
result.add(asName)
|
||||
result.add(tmp)
|
||||
res.add(asName)
|
||||
res.add(tmp)
|
||||
i += 2
|
||||
return cljVector(result)
|
||||
return cljVector(res)
|
||||
|
||||
proc emitSpecialForm(items: seq[CljVal], indent: int, needsValue: bool = false): string =
|
||||
let sp = indentStr(indent)
|
||||
@@ -1501,7 +1537,11 @@ proc emitSpecialForm(items: seq[CljVal], indent: int, needsValue: bool = false):
|
||||
if fnArg.kind == ckSymbol:
|
||||
let rn = runtimeName(fnArg.symName)
|
||||
if rn.len > 0:
|
||||
return sp & "cljMap(cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args[0])), " & collArg & ")"
|
||||
let isVar = fnArg.symName in variadicRuntimeFns
|
||||
if isVar:
|
||||
return sp & "cljMap(cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args)), " & collArg & ")"
|
||||
else:
|
||||
return sp & "cljMap(cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args[0])), " & collArg & ")"
|
||||
return sp & "cljMap(cljFn(proc(args: seq[CljVal]): CljVal = " & mangleName(fnArg.symName) & "(args[0])), " & collArg & ")"
|
||||
elif fnArg.kind == ckList and fnArg.items.len > 0 and fnArg.items[0].kind == ckSymbol and fnArg.items[0].symName in ["fn", "fn*"]:
|
||||
let fnParams = fnArg.items[1]
|
||||
@@ -1574,6 +1614,10 @@ proc emitSpecialForm(items: seq[CljVal], indent: int, needsValue: bool = false):
|
||||
let fnProc = "proc(args: seq[CljVal]): CljVal = (let " & pNames[0] & " = args[0]; let " & pNames[1] & " = args[1]; " & bodyExpr & ")"
|
||||
return sp & "cljReduce(cljFn(" & fnProc & "), cljNil(), " & collArg & ")"
|
||||
elif isSymbol:
|
||||
let rn = runtimeName(fnArg.symName)
|
||||
if rn.len > 0:
|
||||
let wrapper = "proc(args: seq[CljVal]): CljVal = " & rn & "(args)"
|
||||
return sp & "cljReduce(cljFn(" & wrapper & "), cljNil(), " & collArg & ")"
|
||||
let mangled = mangleName(fnArg.symName)
|
||||
let wrapper = "proc(args: seq[CljVal]): CljVal = " & mangled & "(args[0], args[1])"
|
||||
return sp & "cljReduce(cljFn(" & wrapper & "), cljNil(), " & collArg & ")"
|
||||
@@ -1603,6 +1647,10 @@ proc emitSpecialForm(items: seq[CljVal], indent: int, needsValue: bool = false):
|
||||
let fnProc = "proc(args: seq[CljVal]): CljVal = (let " & pNames[0] & " = args[0]; let " & pNames[1] & " = args[1]; " & bodyExpr & ")"
|
||||
return sp & "cljReduce(cljFn(" & fnProc & "), " & initArg & ", " & collArg & ")"
|
||||
elif isSymbol:
|
||||
let rn = runtimeName(fnArg.symName)
|
||||
if rn.len > 0:
|
||||
let wrapper = "proc(args: seq[CljVal]): CljVal = " & rn & "(args)"
|
||||
return sp & "cljReduce(cljFn(" & wrapper & "), " & initArg & ", " & collArg & ")"
|
||||
let mangled = mangleName(fnArg.symName)
|
||||
let wrapper = "proc(args: seq[CljVal]): CljVal = " & mangled & "(args[0], args[1])"
|
||||
return sp & "cljReduce(cljFn(" & wrapper & "), " & initArg & ", " & collArg & ")"
|
||||
@@ -2202,32 +2250,7 @@ proc emitExpr*(v: CljVal, indent: int = 0, needsValue: bool = false): string =
|
||||
let rn = runtimeName(symName)
|
||||
if rn.len > 0 and not isLocalVar(symName):
|
||||
# Check if the runtime function is variadic (takes seq[CljVal])
|
||||
let variadic = symName in ["+", "-", "*", "/", "=", ">", "<", ">=", "<=", "not=",
|
||||
"println", "prn", "print", "str", "pr-str",
|
||||
"atom", "concat", "min", "max", "merge", "interleave",
|
||||
"zipmap", "hash-map", "hash-set", "sorted-map", "sorted-map-by", "sorted-set", "sorted-set-by", "cons", "use-fixtures", "vswap!", "swap!", "tap>", "add-tap", "remove-tap", "add-watch", "remove-watch", "alter-var-root",
|
||||
"array-map", "inf", "nan", "list",
|
||||
"float", "int", "double", "long", "short", "byte",
|
||||
"boolean", "num", "number",
|
||||
"make-hierarchy", "derive", "underive", "ancestors",
|
||||
"descendants", "parents", "isa?", "promise", "create-ns", "future",
|
||||
"delay",
|
||||
"aclone", "alength", "aget", "int-array", "identical?", "empty", "identity",
|
||||
"conj",
|
||||
"drop-last", "shuffle", "repeatedly", "fnil", "intern",
|
||||
"println-str", "prn-str", "binding", "aset",
|
||||
"volatile!", "deliver", "doall", "dorun",
|
||||
"to-array", "into-array", "vector", "rand", "rand-int",
|
||||
"rand-nth", "random-sample",
|
||||
"assoc", "dissoc", "get", "get-in", "update", "assoc-in",
|
||||
"contains?", "select-keys", "keys", "vals",
|
||||
"disj", "peek", "pop",
|
||||
"transduce", "ex-info",
|
||||
"compare", "subvec",
|
||||
"require", "eval", "resolve", "random-uuid",
|
||||
"vreset!", "restart-agent", "with-out-str",
|
||||
"System/getProperty",
|
||||
"dosync", "alter"]
|
||||
let variadic = symName in variadicRuntimeFns
|
||||
if variadic:
|
||||
return sp & "cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args))"
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user