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:
@@ -17,8 +17,6 @@ test_ai_assist
|
|||||||
|
|
||||||
|
|
||||||
# Artifacts / temp files
|
# Artifacts / temp files
|
||||||
-o
|
|
||||||
=0.4.0
|
|
||||||
__pycache__/
|
__pycache__/
|
||||||
test_single_debug.py
|
test_single_debug.py
|
||||||
|
|
||||||
@@ -26,4 +24,3 @@ test_single_debug.py
|
|||||||
src/tui_config
|
src/tui_config
|
||||||
experiments/native-lib/test_client/test_client
|
experiments/native-lib/test_client/test_client
|
||||||
experiments/wasm/tools/zig/
|
experiments/wasm/tools/zig/
|
||||||
lib/bring_http.nim
|
|
||||||
|
|||||||
@@ -14,8 +14,12 @@ test:
|
|||||||
- echo "=== math ===" && ./cljnim run examples/math.clj
|
- echo "=== math ===" && ./cljnim run examples/math.clj
|
||||||
- echo "=== macros ===" && ./cljnim run examples/macros.clj
|
- echo "=== macros ===" && ./cljnim run examples/macros.clj
|
||||||
- echo "=== ai_tools ===" && ./cljnim run examples/ai_tools.clj || true
|
- echo "=== ai_tools ===" && ./cljnim run examples/ai_tools.clj || true
|
||||||
|
- echo "=== ai_features ===" && ./cljnim run examples/ai_features.clj || true
|
||||||
- echo "=== ffi ===" && ./cljnim run examples/ffi.clj
|
- echo "=== ffi ===" && ./cljnim run examples/ffi.clj
|
||||||
- echo "=== interop ===" && ./cljnim run examples/interop.clj
|
- echo "=== interop ===" && ./cljnim run examples/interop.clj
|
||||||
|
- echo "=== fizzbuzz ===" && ./cljnim run examples/fizzbuzz.clj
|
||||||
|
- echo "=== jsonista ===" && ./cljnim run examples/jsonista.clj
|
||||||
|
- echo "=== wordfreq ===" && ./cljnim run examples/wordfreq.clj
|
||||||
# Smoke test REPL (interpreter path)
|
# Smoke test REPL (interpreter path)
|
||||||
- printf '{"op":"eval","form":"(+ 1 2 3)"}\n{"op":"quit"}\n' | timeout 5 ./cljnim repl --json | head -3
|
- printf '{"op":"eval","form":"(+ 1 2 3)"}\n{"op":"quit"}\n' | timeout 5 ./cljnim repl --json | head -3
|
||||||
# Smoke test REPL (tool-call format)
|
# Smoke test REPL (tool-call format)
|
||||||
@@ -27,6 +31,10 @@ test:
|
|||||||
- printf '{"op":"eval","form":"(let [c (chan 3)] (>! c 42) (<! c))"}\n{"op":"quit"}\n' | timeout 5 ./cljnim repl --json | head -3
|
- printf '{"op":"eval","form":"(let [c (chan 3)] (>! c 42) (<! c))"}\n{"op":"quit"}\n' | timeout 5 ./cljnim repl --json | head -3
|
||||||
# Test agents in REPL
|
# Test agents in REPL
|
||||||
- printf '{"op":"eval","form":"(do (def a (agent 0)) (send a inc) (deref a))"}\n{"op":"quit"}\n' | timeout 5 ./cljnim repl --json | head -4
|
- printf '{"op":"eval","form":"(do (def a (agent 0)) (send a inc) (deref a))"}\n{"op":"quit"}\n' | timeout 5 ./cljnim repl --json | head -4
|
||||||
|
# Test threading macros in REPL
|
||||||
|
- printf '{"op":"eval","form":"(-> [1 2 3] (map inc))"}\n{"op":"quit"}\n' | timeout 5 ./cljnim repl --json | head -3
|
||||||
|
- printf '{"op":"eval","form":"(->> [1 2 3] (map inc) (reduce +))"}\n{"op":"quit"}\n' | timeout 5 ./cljnim repl --json | head -3
|
||||||
|
- printf '{"op":"eval","form":"(macroexpand (quote (-> [1 2 3] (map inc))))"}\n{"op":"quit"}\n' | timeout 5 ./cljnim repl --json | head -3
|
||||||
# Test compiled path
|
# Test compiled path
|
||||||
- ./cljnim -e '(+ 10 20)'
|
- ./cljnim -e '(+ 10 20)'
|
||||||
- ./cljnim -e '(quot 17 3)'
|
- ./cljnim -e '(quot 17 3)'
|
||||||
|
|||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
/home/ziko/bara-eco/bring/lib/bring_http.nim
|
||||||
+10
-2
@@ -1476,9 +1476,17 @@ proc cljFilterSeq*(f: proc(args: seq[CljVal]): CljVal, coll: seq[CljVal]): seq[C
|
|||||||
result.add(item)
|
result.add(item)
|
||||||
|
|
||||||
proc cljReduceSeq*(f: proc(args: seq[CljVal]): CljVal, init: CljVal, coll: seq[CljVal]): CljVal =
|
proc cljReduceSeq*(f: proc(args: seq[CljVal]): CljVal, init: CljVal, coll: seq[CljVal]): CljVal =
|
||||||
|
if coll.len == 0:
|
||||||
|
return init
|
||||||
|
var i = 0
|
||||||
|
if init.isNil or init.kind == ckNil:
|
||||||
|
result = coll[0]
|
||||||
|
i = 1
|
||||||
|
else:
|
||||||
result = init
|
result = init
|
||||||
for item in coll:
|
while i < coll.len:
|
||||||
result = f(@[result, item])
|
result = f(@[result, coll[i]])
|
||||||
|
i += 1
|
||||||
|
|
||||||
proc cljMap*(f: proc(args: seq[CljVal]): CljVal, coll: CljVal): CljVal =
|
proc cljMap*(f: proc(args: seq[CljVal]): CljVal, coll: CljVal): CljVal =
|
||||||
if coll.isNil: return cljList(@[])
|
if coll.isNil: return cljList(@[])
|
||||||
|
|||||||
+69
-46
@@ -1,5 +1,5 @@
|
|||||||
# Bara Lang → Nim Emitter
|
# Bara Lang → Nim Emitter
|
||||||
import strutils, sets, algorithm, sequtils, tables
|
import strutils, sets, sequtils, tables
|
||||||
import types
|
import types
|
||||||
import macros
|
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 definedFnArities*: Table[string, int] = initTable[string, int]() # -1 = multi-arity/rest
|
||||||
var loopResultVar*: string = "" # Set by loop handler when result capture is needed
|
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) =
|
proc registerGlobal*(name: string) =
|
||||||
definedGlobals.incl(name)
|
definedGlobals.incl(name)
|
||||||
|
|
||||||
proc mangleName*(name: string): string
|
proc mangleName*(name: string): string
|
||||||
|
proc runtimeName(op: string): string
|
||||||
|
|
||||||
proc registerFn*(name: string, arity: int) =
|
proc registerFn*(name: string, arity: int) =
|
||||||
definedFnArities[name] = arity
|
definedFnArities[name] = arity
|
||||||
|
|
||||||
proc emitFnWrapper*(name: string): string =
|
proc emitFnWrapper*(name: string): string =
|
||||||
## Emit a cljFn wrapper for a user-defined function when used as a value.
|
## 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)
|
let mangled = mangleName(name)
|
||||||
if name in multiArityFns:
|
if name in multiArityFns:
|
||||||
# Multi-arity / rest params already take seq[CljVal]
|
# Multi-arity / rest params already take seq[CljVal]
|
||||||
@@ -137,15 +173,15 @@ proc indentStr(indent: int): string =
|
|||||||
proc indentCode(code: string, extra: int): string =
|
proc indentCode(code: string, extra: int): string =
|
||||||
if extra <= 0: return code
|
if extra <= 0: return code
|
||||||
let prefix = indentStr(extra)
|
let prefix = indentStr(extra)
|
||||||
var result = ""
|
var res = ""
|
||||||
var lines = code.split("\n")
|
var lines = code.split("\n")
|
||||||
for i, line in lines:
|
for i, line in lines:
|
||||||
if i > 0: result.add("\n")
|
if i > 0: res.add("\n")
|
||||||
if line.len > 0:
|
if line.len > 0:
|
||||||
result.add(prefix & line)
|
res.add(prefix & line)
|
||||||
else:
|
else:
|
||||||
result.add(line)
|
res.add(line)
|
||||||
return result
|
return res
|
||||||
|
|
||||||
proc runtimeName(op: string): string =
|
proc runtimeName(op: string): string =
|
||||||
case op
|
case op
|
||||||
@@ -440,26 +476,26 @@ proc letDestructuredBindings(bindings: CljVal): CljVal =
|
|||||||
# Expand let bindings with vector/map destructuring into simple symbol bindings
|
# Expand let bindings with vector/map destructuring into simple symbol bindings
|
||||||
if bindings.kind != ckVector:
|
if bindings.kind != ckVector:
|
||||||
return bindings
|
return bindings
|
||||||
var result: seq[CljVal] = @[]
|
var res: seq[CljVal] = @[]
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < bindings.items.len:
|
while i < bindings.items.len:
|
||||||
let bname = bindings.items[i]
|
let bname = bindings.items[i]
|
||||||
let bval = bindings.items[i+1]
|
let bval = bindings.items[i+1]
|
||||||
if bname.kind == ckSymbol:
|
if bname.kind == ckSymbol:
|
||||||
result.add(bname)
|
res.add(bname)
|
||||||
result.add(bval)
|
res.add(bval)
|
||||||
elif bname.kind == ckVector:
|
elif bname.kind == ckVector:
|
||||||
let tmp = cljSymbol("ds_" & $i)
|
let tmp = cljSymbol("ds_" & $i)
|
||||||
result.add(tmp)
|
res.add(tmp)
|
||||||
result.add(bval)
|
res.add(bval)
|
||||||
for j in 0..<bname.items.len:
|
for j in 0..<bname.items.len:
|
||||||
if bname.items[j].kind == ckSymbol:
|
if bname.items[j].kind == ckSymbol:
|
||||||
result.add(bname.items[j])
|
res.add(bname.items[j])
|
||||||
result.add(cljList(@[cljSymbol("nth"), tmp, cljInt(j)]))
|
res.add(cljList(@[cljSymbol("nth"), tmp, cljInt(j)]))
|
||||||
elif bname.kind == ckMap:
|
elif bname.kind == ckMap:
|
||||||
let tmp = cljSymbol("ds_" & $i)
|
let tmp = cljSymbol("ds_" & $i)
|
||||||
result.add(tmp)
|
res.add(tmp)
|
||||||
result.add(bval)
|
res.add(bval)
|
||||||
var hasAs = false
|
var hasAs = false
|
||||||
var asName: CljVal = nil
|
var asName: CljVal = nil
|
||||||
var j = 0
|
var j = 0
|
||||||
@@ -469,17 +505,17 @@ proc letDestructuredBindings(bindings: CljVal): CljVal =
|
|||||||
if key.kind == ckKeyword and key.kwName == "keys" and val.kind == ckVector:
|
if key.kind == ckKeyword and key.kwName == "keys" and val.kind == ckVector:
|
||||||
for k in 0..<val.items.len:
|
for k in 0..<val.items.len:
|
||||||
if val.items[k].kind == ckSymbol:
|
if val.items[k].kind == ckSymbol:
|
||||||
result.add(val.items[k])
|
res.add(val.items[k])
|
||||||
result.add(cljList(@[cljSymbol("get"), tmp, cljKeyword(val.items[k].symName)]))
|
res.add(cljList(@[cljSymbol("get"), tmp, cljKeyword(val.items[k].symName)]))
|
||||||
elif key.kind == ckKeyword and key.kwName == "as":
|
elif key.kind == ckKeyword and key.kwName == "as":
|
||||||
hasAs = true
|
hasAs = true
|
||||||
asName = val
|
asName = val
|
||||||
j += 1
|
j += 1
|
||||||
if hasAs and asName != nil and asName.kind == ckSymbol:
|
if hasAs and asName != nil and asName.kind == ckSymbol:
|
||||||
result.add(asName)
|
res.add(asName)
|
||||||
result.add(tmp)
|
res.add(tmp)
|
||||||
i += 2
|
i += 2
|
||||||
return cljVector(result)
|
return cljVector(res)
|
||||||
|
|
||||||
proc emitSpecialForm(items: seq[CljVal], indent: int, needsValue: bool = false): string =
|
proc emitSpecialForm(items: seq[CljVal], indent: int, needsValue: bool = false): string =
|
||||||
let sp = indentStr(indent)
|
let sp = indentStr(indent)
|
||||||
@@ -1501,6 +1537,10 @@ proc emitSpecialForm(items: seq[CljVal], indent: int, needsValue: bool = false):
|
|||||||
if fnArg.kind == ckSymbol:
|
if fnArg.kind == ckSymbol:
|
||||||
let rn = runtimeName(fnArg.symName)
|
let rn = runtimeName(fnArg.symName)
|
||||||
if rn.len > 0:
|
if rn.len > 0:
|
||||||
|
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 = " & rn & "(args[0])), " & collArg & ")"
|
||||||
return sp & "cljMap(cljFn(proc(args: seq[CljVal]): CljVal = " & mangleName(fnArg.symName) & "(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*"]:
|
elif fnArg.kind == ckList and fnArg.items.len > 0 and fnArg.items[0].kind == ckSymbol and fnArg.items[0].symName in ["fn", "fn*"]:
|
||||||
@@ -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 & ")"
|
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 & ")"
|
return sp & "cljReduce(cljFn(" & fnProc & "), cljNil(), " & collArg & ")"
|
||||||
elif isSymbol:
|
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 mangled = mangleName(fnArg.symName)
|
||||||
let wrapper = "proc(args: seq[CljVal]): CljVal = " & mangled & "(args[0], args[1])"
|
let wrapper = "proc(args: seq[CljVal]): CljVal = " & mangled & "(args[0], args[1])"
|
||||||
return sp & "cljReduce(cljFn(" & wrapper & "), cljNil(), " & collArg & ")"
|
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 & ")"
|
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 & ")"
|
return sp & "cljReduce(cljFn(" & fnProc & "), " & initArg & ", " & collArg & ")"
|
||||||
elif isSymbol:
|
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 mangled = mangleName(fnArg.symName)
|
||||||
let wrapper = "proc(args: seq[CljVal]): CljVal = " & mangled & "(args[0], args[1])"
|
let wrapper = "proc(args: seq[CljVal]): CljVal = " & mangled & "(args[0], args[1])"
|
||||||
return sp & "cljReduce(cljFn(" & wrapper & "), " & initArg & ", " & collArg & ")"
|
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)
|
let rn = runtimeName(symName)
|
||||||
if rn.len > 0 and not isLocalVar(symName):
|
if rn.len > 0 and not isLocalVar(symName):
|
||||||
# Check if the runtime function is variadic (takes seq[CljVal])
|
# Check if the runtime function is variadic (takes seq[CljVal])
|
||||||
let variadic = symName in ["+", "-", "*", "/", "=", ">", "<", ">=", "<=", "not=",
|
let variadic = symName in variadicRuntimeFns
|
||||||
"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"]
|
|
||||||
if variadic:
|
if variadic:
|
||||||
return sp & "cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args))"
|
return sp & "cljFn(proc(args: seq[CljVal]): CljVal = " & rn & "(args))"
|
||||||
else:
|
else:
|
||||||
|
|||||||
+15
-1
@@ -1,7 +1,7 @@
|
|||||||
# Tree-walking interpreter for fast REPL evaluation
|
# Tree-walking interpreter for fast REPL evaluation
|
||||||
# Handles common cases without spawning nim c
|
# Handles common cases without spawning nim c
|
||||||
import strutils, sequtils, tables, algorithm, times, deques
|
import strutils, sequtils, tables, algorithm, times, deques
|
||||||
import types, reader, ai_assist
|
import types, reader, macros, ai_assist
|
||||||
|
|
||||||
var agentRegistry* = initTable[string, CljVal]()
|
var agentRegistry* = initTable[string, CljVal]()
|
||||||
var agentCounter*: int64 = 0
|
var agentCounter*: int64 = 0
|
||||||
@@ -228,6 +228,20 @@ proc evalList(items: seq[CljVal], env: Env): EvalResult =
|
|||||||
return EvalResult(ok: true, value: res.value)
|
return EvalResult(ok: true, value: res.value)
|
||||||
return EvalResult(ok: true, value: cljNil())
|
return EvalResult(ok: true, value: cljNil())
|
||||||
|
|
||||||
|
of "macroexpand":
|
||||||
|
if items.len < 2:
|
||||||
|
return EvalResult(ok: false, error: "macroexpand requires an expression")
|
||||||
|
let argRes = evalAst(items[1], env)
|
||||||
|
if not argRes.ok: return argRes
|
||||||
|
return EvalResult(ok: true, value: macroexpand(argRes.value))
|
||||||
|
|
||||||
|
of "macroexpand-1":
|
||||||
|
if items.len < 2:
|
||||||
|
return EvalResult(ok: false, error: "macroexpand-1 requires an expression")
|
||||||
|
let argRes = evalAst(items[1], env)
|
||||||
|
if not argRes.ok: return argRes
|
||||||
|
return EvalResult(ok: true, value: macroexpand1(argRes.value))
|
||||||
|
|
||||||
of "ai/debug", "ai.debug":
|
of "ai/debug", "ai.debug":
|
||||||
if items.len < 2:
|
if items.len < 2:
|
||||||
return EvalResult(ok: false, error: "ai/debug requires an expression")
|
return EvalResult(ok: false, error: "ai/debug requires an expression")
|
||||||
|
|||||||
+28
-28
@@ -149,20 +149,20 @@ proc initMacros*() =
|
|||||||
defineMacro("and", proc(args: seq[CljVal]): CljVal =
|
defineMacro("and", proc(args: seq[CljVal]): CljVal =
|
||||||
if args.len == 0: return cljBool(true)
|
if args.len == 0: return cljBool(true)
|
||||||
if args.len == 1: return args[0]
|
if args.len == 1: return args[0]
|
||||||
var result = args[^1]
|
var acc = args[^1]
|
||||||
for i in countdown(args.len - 2, 0):
|
for i in countdown(args.len - 2, 0):
|
||||||
result = cljList(@[cljSymbol("if"), args[i], result, cljBool(false)])
|
acc = cljList(@[cljSymbol("if"), args[i], acc, cljBool(false)])
|
||||||
return result
|
return acc
|
||||||
)
|
)
|
||||||
|
|
||||||
# or
|
# or
|
||||||
defineMacro("or", proc(args: seq[CljVal]): CljVal =
|
defineMacro("or", proc(args: seq[CljVal]): CljVal =
|
||||||
if args.len == 0: return cljNil()
|
if args.len == 0: return cljNil()
|
||||||
if args.len == 1: return args[0]
|
if args.len == 1: return args[0]
|
||||||
var result = args[^1]
|
var acc = args[^1]
|
||||||
for i in countdown(args.len - 2, 0):
|
for i in countdown(args.len - 2, 0):
|
||||||
result = cljList(@[cljSymbol("if"), args[i], args[i], result])
|
acc = cljList(@[cljSymbol("if"), args[i], args[i], acc])
|
||||||
return result
|
return acc
|
||||||
)
|
)
|
||||||
|
|
||||||
# when-let
|
# when-let
|
||||||
@@ -242,22 +242,22 @@ proc initMacros*() =
|
|||||||
let clauses = args[1..^1]
|
let clauses = args[1..^1]
|
||||||
if clauses.len mod 2 != 0:
|
if clauses.len mod 2 != 0:
|
||||||
raise newException(CatchableError, "cond-> requires pairs of test/expr")
|
raise newException(CatchableError, "cond-> requires pairs of test/expr")
|
||||||
var result = init
|
var acc = init
|
||||||
for i in 0..<(clauses.len div 2):
|
for i in 0..<(clauses.len div 2):
|
||||||
let test = clauses[i * 2]
|
let test = clauses[i * 2]
|
||||||
let expr = clauses[i * 2 + 1]
|
let expr = clauses[i * 2 + 1]
|
||||||
let gs = cljSymbol(gensymName("ct_"))
|
let gs = cljSymbol(gensymName("ct_"))
|
||||||
if expr.kind == ckList and expr.items.len > 0:
|
if expr.kind == ckList and expr.items.len > 0:
|
||||||
result = cljList(@[cljSymbol("let"), cljVector(@[gs, result]),
|
acc = cljList(@[cljSymbol("let"), cljVector(@[gs, acc]),
|
||||||
cljList(@[cljSymbol("if"), test,
|
cljList(@[cljSymbol("if"), test,
|
||||||
cljList(@[expr.items[0], gs] & expr.items[1..^1]),
|
cljList(@[expr.items[0], gs] & expr.items[1..^1]),
|
||||||
gs])])
|
gs])])
|
||||||
else:
|
else:
|
||||||
result = cljList(@[cljSymbol("let"), cljVector(@[gs, result]),
|
acc = cljList(@[cljSymbol("let"), cljVector(@[gs, acc]),
|
||||||
cljList(@[cljSymbol("if"), test,
|
cljList(@[cljSymbol("if"), test,
|
||||||
cljList(@[expr, gs]),
|
cljList(@[expr, gs]),
|
||||||
gs])])
|
gs])])
|
||||||
return result
|
return acc
|
||||||
)
|
)
|
||||||
|
|
||||||
# cond->>
|
# cond->>
|
||||||
@@ -268,22 +268,22 @@ proc initMacros*() =
|
|||||||
let clauses = args[1..^1]
|
let clauses = args[1..^1]
|
||||||
if clauses.len mod 2 != 0:
|
if clauses.len mod 2 != 0:
|
||||||
raise newException(CatchableError, "cond->> requires pairs of test/expr")
|
raise newException(CatchableError, "cond->> requires pairs of test/expr")
|
||||||
var result = init
|
var acc = init
|
||||||
for i in 0..<(clauses.len div 2):
|
for i in 0..<(clauses.len div 2):
|
||||||
let test = clauses[i * 2]
|
let test = clauses[i * 2]
|
||||||
let expr = clauses[i * 2 + 1]
|
let expr = clauses[i * 2 + 1]
|
||||||
let gs = cljSymbol(gensymName("ct_"))
|
let gs = cljSymbol(gensymName("ct_"))
|
||||||
if expr.kind == ckList and expr.items.len > 0:
|
if expr.kind == ckList and expr.items.len > 0:
|
||||||
result = cljList(@[cljSymbol("let"), cljVector(@[gs, result]),
|
acc = cljList(@[cljSymbol("let"), cljVector(@[gs, acc]),
|
||||||
cljList(@[cljSymbol("if"), test,
|
cljList(@[cljSymbol("if"), test,
|
||||||
cljList(expr.items & @[gs]),
|
cljList(expr.items & @[gs]),
|
||||||
gs])])
|
gs])])
|
||||||
else:
|
else:
|
||||||
result = cljList(@[cljSymbol("let"), cljVector(@[gs, result]),
|
acc = cljList(@[cljSymbol("let"), cljVector(@[gs, acc]),
|
||||||
cljList(@[cljSymbol("if"), test,
|
cljList(@[cljSymbol("if"), test,
|
||||||
cljList(@[expr, gs]),
|
cljList(@[expr, gs]),
|
||||||
gs])])
|
gs])])
|
||||||
return result
|
return acc
|
||||||
)
|
)
|
||||||
|
|
||||||
# doto
|
# doto
|
||||||
@@ -312,10 +312,10 @@ proc initMacros*() =
|
|||||||
let init = args[0]
|
let init = args[0]
|
||||||
let name = args[1]
|
let name = args[1]
|
||||||
let body = args[2..^1]
|
let body = args[2..^1]
|
||||||
var result = init
|
var acc = init
|
||||||
for form in body:
|
for form in body:
|
||||||
result = cljList(@[cljSymbol("let"), cljVector(@[name, result]), form])
|
acc = cljList(@[cljSymbol("let"), cljVector(@[name, acc]), form])
|
||||||
return result
|
return acc
|
||||||
)
|
)
|
||||||
|
|
||||||
# some->
|
# some->
|
||||||
@@ -324,21 +324,21 @@ proc initMacros*() =
|
|||||||
raise newException(CatchableError, "some-> requires initial value and body")
|
raise newException(CatchableError, "some-> requires initial value and body")
|
||||||
let init = args[0]
|
let init = args[0]
|
||||||
let body = args[1..^1]
|
let body = args[1..^1]
|
||||||
var result = init
|
var acc = init
|
||||||
for form in body:
|
for form in body:
|
||||||
let gs = cljSymbol(gensymName("st_"))
|
let gs = cljSymbol(gensymName("st_"))
|
||||||
let nilCheck = cljList(@[cljSymbol("nil?"), gs])
|
let nilCheck = cljList(@[cljSymbol("nil?"), gs])
|
||||||
if form.kind == ckList and form.items.len > 0:
|
if form.kind == ckList and form.items.len > 0:
|
||||||
result = cljList(@[cljSymbol("let"), cljVector(@[gs, result]),
|
acc = cljList(@[cljSymbol("let"), cljVector(@[gs, acc]),
|
||||||
cljList(@[cljSymbol("if"), nilCheck,
|
cljList(@[cljSymbol("if"), nilCheck,
|
||||||
cljNil(),
|
cljNil(),
|
||||||
cljList(@[form.items[0], gs] & form.items[1..^1])])])
|
cljList(@[form.items[0], gs] & form.items[1..^1])])])
|
||||||
else:
|
else:
|
||||||
result = cljList(@[cljSymbol("let"), cljVector(@[gs, result]),
|
acc = cljList(@[cljSymbol("let"), cljVector(@[gs, acc]),
|
||||||
cljList(@[cljSymbol("if"), nilCheck,
|
cljList(@[cljSymbol("if"), nilCheck,
|
||||||
cljNil(),
|
cljNil(),
|
||||||
cljList(@[form, gs])])])
|
cljList(@[form, gs])])])
|
||||||
return result
|
return acc
|
||||||
)
|
)
|
||||||
|
|
||||||
# some->>
|
# some->>
|
||||||
@@ -347,20 +347,20 @@ proc initMacros*() =
|
|||||||
raise newException(CatchableError, "some->> requires initial value and body")
|
raise newException(CatchableError, "some->> requires initial value and body")
|
||||||
let init = args[0]
|
let init = args[0]
|
||||||
let body = args[1..^1]
|
let body = args[1..^1]
|
||||||
var result = init
|
var acc = init
|
||||||
for form in body:
|
for form in body:
|
||||||
let gs = cljSymbol(gensymName("st_"))
|
let gs = cljSymbol(gensymName("st_"))
|
||||||
if form.kind == ckList and form.items.len > 0:
|
if form.kind == ckList and form.items.len > 0:
|
||||||
result = cljList(@[cljSymbol("let"), cljVector(@[gs, result]),
|
acc = cljList(@[cljSymbol("let"), cljVector(@[gs, acc]),
|
||||||
cljList(@[cljSymbol("if"), gs,
|
cljList(@[cljSymbol("if"), gs,
|
||||||
cljList(form.items & @[gs]),
|
cljList(form.items & @[gs]),
|
||||||
cljNil()])])
|
cljNil()])])
|
||||||
else:
|
else:
|
||||||
result = cljList(@[cljSymbol("let"), cljVector(@[gs, result]),
|
acc = cljList(@[cljSymbol("let"), cljVector(@[gs, acc]),
|
||||||
cljList(@[cljSymbol("if"), gs,
|
cljList(@[cljSymbol("if"), gs,
|
||||||
cljList(@[form, gs]),
|
cljList(@[form, gs]),
|
||||||
cljNil()])])
|
cljNil()])])
|
||||||
return result
|
return acc
|
||||||
)
|
)
|
||||||
|
|
||||||
# for
|
# for
|
||||||
@@ -465,11 +465,11 @@ proc initMacros*() =
|
|||||||
let loopBody = cljList(@[cljSymbol("when"), name] & innerBody & @[recurForm])
|
let loopBody = cljList(@[cljSymbol("when"), name] & innerBody & @[recurForm])
|
||||||
return cljList(@[cljSymbol("loop"), cljVector(@[name, cljList(@[cljSymbol("first"), seqColl]), gs, cljList(@[cljSymbol("next"), seqColl])]), loopBody])
|
return cljList(@[cljSymbol("loop"), cljVector(@[name, cljList(@[cljSymbol("first"), seqColl]), gs, cljList(@[cljSymbol("next"), seqColl])]), loopBody])
|
||||||
|
|
||||||
var result = makeLoop(pairs[^1][0], pairs[^1][1], inner)
|
var acc = makeLoop(pairs[^1][0], pairs[^1][1], inner)
|
||||||
for j in countdown(pairs.len - 2, 0):
|
for j in countdown(pairs.len - 2, 0):
|
||||||
let innerSeq = @[result]
|
let innerSeq = @[result]
|
||||||
result = makeLoop(pairs[j][0], pairs[j][1], innerSeq)
|
acc = makeLoop(pairs[j][0], pairs[j][1], innerSeq)
|
||||||
return result
|
return acc
|
||||||
)
|
)
|
||||||
|
|
||||||
# dotimes
|
# dotimes
|
||||||
|
|||||||
+3
-1
@@ -155,8 +155,10 @@ proc evalForm*(state: var ReplState, formStr: string): JsonNode =
|
|||||||
parsed.items[0].kind == ckSymbol and
|
parsed.items[0].kind == ckSymbol and
|
||||||
parsed.items[0].symName in ["def", "defn"]
|
parsed.items[0].symName in ["def", "defn"]
|
||||||
|
|
||||||
|
# Macro-expand before evaluating
|
||||||
|
let expanded = macroexpand(parsed)
|
||||||
# Try in-memory evaluation first (fast path)
|
# Try in-memory evaluation first (fast path)
|
||||||
let evalResult = evalAst(parsed, state.evalEnv)
|
let evalResult = evalAst(expanded, state.evalEnv)
|
||||||
if evalResult.ok:
|
if evalResult.ok:
|
||||||
let elapsed = (epochTime() - startTime) * 1000
|
let elapsed = (epochTime() - startTime) * 1000
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user