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
+26 -8
View File
@@ -402,16 +402,20 @@ proc initMacros*() =
innerBindings.add(name.items[j])
innerBindings.add(cljList(@[cljSymbol("nth"), tmpGs, cljInt(j)]))
let destructured = cljList(@[cljSymbol("let"), cljVector(innerBindings)] & body)
let recurForm = cljList(@[cljSymbol("recur"), cljList(@[cljSymbol("next"), seqGs])])
let loopBody = cljList(@[cljSymbol("when"), seqGs,
cljList(@[cljSymbol("let"), cljVector(@[tmpGs, cljList(@[cljSymbol("first"), seqGs])]), destructured])])
let recurBody = cljList(@[cljSymbol("recur"), cljList(@[cljSymbol("rest"), seqGs])])
let loopForm = cljList(@[cljSymbol("loop"), cljVector(@[seqGs, cljList(@[cljSymbol("seq"), gs])]), loopBody, recurBody])
result = cljList(@[cljSymbol("let"), cljVector(@[gs, coll]), loopForm])
cljList(@[cljSymbol("let"), cljVector(@[tmpGs, cljList(@[cljSymbol("first"), seqGs])]), destructured]),
recurForm])
let seqColl = cljList(@[cljSymbol("seq"), coll])
let loopForm = cljList(@[cljSymbol("loop"), cljVector(@[seqGs, seqColl]), loopBody])
result = loopForm
else:
let loopBody = cljList(@[cljSymbol("when"), name] & body)
let recurBody = cljList(@[cljSymbol("recur"), cljList(@[cljSymbol("first"), gs]), cljList(@[cljSymbol("rest"), gs])])
let loopForm = cljList(@[cljSymbol("loop"), cljVector(@[name, cljList(@[cljSymbol("first"), gs]), gs, cljList(@[cljSymbol("rest"), gs])]), loopBody, recurBody])
result = cljList(@[cljSymbol("let"), cljVector(@[gs, coll]), loopForm])
# Simple binding: skip the let, pass coll directly to loop
let seqColl = cljList(@[cljSymbol("seq"), coll])
let recurForm = cljList(@[cljSymbol("recur"), cljList(@[cljSymbol("first"), gs]), cljList(@[cljSymbol("next"), gs])])
let loopBody = cljList(@[cljSymbol("when"), name] & body & @[recurForm])
let loopForm = cljList(@[cljSymbol("loop"), cljVector(@[name, cljList(@[cljSymbol("first"), seqColl]), gs, cljList(@[cljSymbol("next"), seqColl])]), loopBody])
result = loopForm
else:
let name = bindings.items[0]
let coll = bindings.items[1]
@@ -498,6 +502,20 @@ proc initMacros*() =
cljList(@[cljSymbol("do")] & args[1..^1])
)
defineMacro("when-first", proc(args: seq[CljVal]): CljVal =
if args.len < 2: return cljNil()
let bindings = args[0]
if bindings.kind != ckVector or bindings.items.len != 2:
return cljNil()
let bindName = bindings.items[0]
let bindColl = bindings.items[1]
let body = args[1..^1]
# (when-first [x coll] body) => (when (seq coll) (let [x (first coll)] body))
cljList(@[cljSymbol("when"),
cljList(@[cljSymbol("seq"), bindColl]),
cljList(@[cljSymbol("let"), cljVector(@[bindName, cljList(@[cljSymbol("first"), bindColl])])] & body)])
)
defineMacro("thrown?", proc(args: seq[CljVal]): CljVal =
if args.len == 0: return cljNil()
let form = args[0]