feat: +12 tests (210/233, 90%) — fn wrapping, doseq, when-first, NaN?, delay, loop scope

Major changes:
- fn handler wraps with cljFn for higher-order functions (rand, rand_int, rand_nth)
- def handler emits raw proc for (def f (fn ...)) context
- Local fn calls use .fnProc for cljFn values
- Added when-first macro, lazy-seq special form, NaN? predicate
- Added delay, rseq, listEmpty runtime functions
- doseq macro uses next instead of rest, recur inside when body
- loop handler pushScope/popScope for proper variable scoping
- let handler: var/while/proc statement checks, block last-line discard
- when handler: proper statement detection for body forms
- defn/defn- allow empty body
This commit is contained in:
2026-05-09 23:17:02 +03:00
parent e072cde757
commit 549e04f24d
4 changed files with 230 additions and 114 deletions
+29
View File
@@ -81,6 +81,10 @@ proc cljInt*(v: int): CljVal = CljVal(kind: ckInt, intVal: v.int64)
proc cljFloat*(v: float64): CljVal = CljVal(kind: ckFloat, floatVal: v)
proc cljInf*(args: seq[CljVal]): CljVal = CljVal(kind: ckFloat, floatVal: Inf)
proc cljNaN*(args: seq[CljVal]): CljVal = CljVal(kind: ckFloat, floatVal: NaN)
proc cljNaNQ*(v: CljVal): CljVal =
if v.kind == ckFloat: cljBool(v.floatVal.isNaN)
else: cljBool(false)
proc cljString*(v: string): CljVal = CljVal(kind: ckString, strVal: v)
proc cljKeyword*(v: string): CljVal = CljVal(kind: ckKeyword, kwName: v)
proc cljSymbol*(v: string): CljVal = CljVal(kind: ckSymbol, symName: v)
@@ -779,6 +783,23 @@ proc cljArrayMap*(args: seq[CljVal]): CljVal =
proc cljSortedQ*(v: CljVal): CljVal =
cljBool(false)
proc cljRseq*(v: CljVal): CljVal =
case v.kind
of ckVector:
var items: seq[CljVal] = @[]
for i in countdown(v.vecData.count - 1, 0):
items.add(v.vecData[i])
cljList(items)
of ckList:
var items: seq[CljVal] = @[]
for i in countdown(v.listItems.len - 1, 0):
items.add(v.listItems[i])
cljList(items)
else: cljNil()
proc cljListEmpty*(args: seq[CljVal]): CljVal =
cljList(args)
# Missing function stubs for test suite compatibility
proc cljToFloat*(args: seq[CljVal]): CljVal =
if args.len == 0: return cljFloat(0.0)
@@ -1580,6 +1601,14 @@ proc cljFuture*(args: seq[CljVal]): CljVal =
else:
cljAtom(cljNil())
proc cljDelay*(args: seq[CljVal]): CljVal =
if args.len == 0: return cljAtom(cljNil())
let f = args[0]
if f.kind == ckFn:
cljAtom(f.fnProc(@[]))
else:
cljAtom(f)
proc cljDeref*(a: CljVal): CljVal =
if a.kind == ckAtom: a.atomVal
elif a.kind == ckAgent: a.agentVal