fix: 233/233 (100%) - ##Inf/NaN reader, #?@ splicing, :clj platform, doseq :when/:let/:while, hex overflow, unquote-splicing

This commit is contained in:
2026-05-11 01:49:42 +03:00
parent 549e04f24d
commit 075e5f6df0
4 changed files with 165 additions and 178 deletions
+76 -23
View File
@@ -44,7 +44,9 @@ proc expandSyntaxQuote*(form: CljVal): CljVal =
raise newException(CatchableError, "unquote requires exactly 1 argument")
return form.items[1]
if head.symName == "unquote-splicing":
raise newException(CatchableError, "unquote-splicing can only be used inside a collection")
if form.items.len != 2:
raise newException(CatchableError, "unquote-splicing requires exactly 1 argument")
return form.items[1]
# Process each element
var parts: seq[CljVal] = @[]
for item in form.items:
@@ -389,39 +391,90 @@ proc initMacros*() =
let body = args[1..^1]
if bindings.kind != ckVector:
raise newException(CatchableError, "doseq bindings must be a vector")
let bindingPairs = bindings.items.len div 2
if bindingPairs == 1:
let name = bindings.items[0]
let coll = bindings.items[1]
let gs = cljSymbol(gensymName("ds_"))
# Parse bindings: collect [name coll] pairs and :when/:let/:while modifiers
var pairs: seq[(CljVal, CljVal)] = @[]
var whenExpr: CljVal = nil
var letBinds: seq[CljVal] = @[]
var whileExpr: CljVal = nil
var i = 0
while i < bindings.items.len:
let item = bindings.items[i]
if item.kind == ckKeyword:
let kw = item.kwName
if kw in ["when", "let", "while"] and i + 1 < bindings.items.len:
case kw
of "when":
whenExpr = bindings.items[i + 1]
of "let":
let lb = bindings.items[i + 1]
if lb.kind == ckVector:
for it in lb.items:
letBinds.add(it)
else:
letBinds.add(lb)
of "while":
whileExpr = bindings.items[i + 1]
else: discard
i += 2
else:
i += 1
else:
if i + 1 < bindings.items.len:
pairs.add((item, bindings.items[i + 1]))
i += 2
if pairs.len == 0:
var b = body
if letBinds.len > 0:
b = @[cljList(@[cljSymbol("let"), cljVector(letBinds)] & b)]
if whenExpr != nil:
b = @[cljList(@[cljSymbol("when"), whenExpr] & b)]
if whileExpr != nil:
let gs = cljSymbol(gensymName("dw_"))
b = @[cljList(@[cljSymbol("loop"), cljVector(@[gs, cljBool(true)]),
cljList(@[cljSymbol("when"), whileExpr] & b & @[cljList(@[cljSymbol("recur"), gs])]),
cljList(@[cljSymbol("when"), cljBool(false), cljNil()])])]
return cljList(@[cljSymbol("do")] & b)
# Build nested doseq for multiple pairs, innermost gets the body with modifiers
var inner: seq[CljVal] = body
if letBinds.len > 0:
inner = @[cljList(@[cljSymbol("let"), cljVector(letBinds)] & inner)]
if whenExpr != nil:
inner = @[cljList(@[cljSymbol("when"), whenExpr] & inner)]
if whileExpr != nil:
let gs = cljSymbol(gensymName("dw_"))
inner = @[cljList(@[cljSymbol("loop"), cljVector(@[gs, cljBool(true)]),
cljList(@[cljSymbol("when"), whileExpr] & inner & @[cljList(@[cljSymbol("recur"), gs])]),
cljList(@[cljSymbol("when"), cljBool(false), cljNil()])])]
proc makeLoop(name: CljVal, coll: CljVal, innerBody: seq[CljVal]): CljVal =
if name.kind == ckVector:
let seqGs = cljSymbol(gensymName("sq_"))
let tmpGs = cljSymbol(gensymName("tmp_"))
var innerBindings: seq[CljVal] = @[]
for j in 0..<name.items.len:
innerBindings.add(name.items[j])
innerBindings.add(cljList(@[cljSymbol("nth"), tmpGs, cljInt(j)]))
let destructured = cljList(@[cljSymbol("let"), cljVector(innerBindings)] & body)
for jn in 0..<name.items.len:
innerBindings.add(name.items[jn])
innerBindings.add(cljList(@[cljSymbol("nth"), tmpGs, cljInt(jn)]))
let destructured = cljList(@[cljSymbol("let"), cljVector(innerBindings)] & innerBody)
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]),
recurForm])
let seqColl = cljList(@[cljSymbol("seq"), coll])
let loopForm = cljList(@[cljSymbol("loop"), cljVector(@[seqGs, seqColl]), loopBody])
result = loopForm
return cljList(@[cljSymbol("loop"), cljVector(@[seqGs, cljList(@[cljSymbol("seq"), coll])]), loopBody])
else:
# Simple binding: skip the let, pass coll directly to loop
let seqColl = cljList(@[cljSymbol("seq"), coll])
let gs = cljSymbol(gensymName("ds_"))
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]
let restBindings = cljVector(bindings.items[2..^1])
let innerDoseq = cljList(@[cljSymbol("doseq"), restBindings] & body)
result = cljList(@[cljSymbol("doseq"), cljVector(@[name, coll]), innerDoseq])
let loopBody = cljList(@[cljSymbol("when"), name] & innerBody & @[recurForm])
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)
for j in countdown(pairs.len - 2, 0):
let innerSeq = @[result]
result = makeLoop(pairs[j][0], pairs[j][1], innerSeq)
return result
)
# dotimes