fix: 233/233 (100%) - ##Inf/NaN reader, #?@ splicing, :clj platform, doseq :when/:let/:while, hex overflow, unquote-splicing
This commit is contained in:
+76
-23
@@ -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
|
||||
|
||||
+70
-37
@@ -176,17 +176,19 @@ proc readAtom(s: string, i: var int): CljVal =
|
||||
hexTok = hexTok[1..^1]
|
||||
if hexTok.len >= 3 and hexTok[0] == '0' and (hexTok[1] == 'x' or hexTok[1] == 'X'):
|
||||
try:
|
||||
var hexVal = 0'i64
|
||||
var hexVal = 0'u64
|
||||
for j in 2..<hexTok.len:
|
||||
let c = hexTok[j]
|
||||
hexVal = hexVal * 16 + (
|
||||
if c in '0'..'9': cast[int64](ord(c) - ord('0'))
|
||||
elif c in 'a'..'f': cast[int64](ord(c) - ord('a') + 10)
|
||||
else: cast[int64](ord(c) - ord('A') + 10)
|
||||
if c in '0'..'9': cast[uint64](ord(c) - ord('0'))
|
||||
elif c in 'a'..'f': cast[uint64](ord(c) - ord('a') + 10)
|
||||
else: cast[uint64](ord(c) - ord('A') + 10)
|
||||
)
|
||||
if isNeg:
|
||||
hexVal = -hexVal
|
||||
return cljInt(hexVal)
|
||||
if hexVal == 0x8000000000000000'u64:
|
||||
return cljInt(low(int64))
|
||||
return cljInt(-cast[int64](hexVal))
|
||||
return cljInt(cast[int64](hexVal))
|
||||
except CatchableError:
|
||||
return cljSymbol(tok)
|
||||
|
||||
@@ -203,7 +205,7 @@ proc readAtom(s: string, i: var int): CljVal =
|
||||
try:
|
||||
let radix = parseInt(radixTok[0..<rPos])
|
||||
if radix >= 2 and radix <= 36:
|
||||
var val = 0'i64
|
||||
var val = 0'u64
|
||||
for j in rPos+1..<radixTok.len:
|
||||
let c = radixTok[j]
|
||||
let digit =
|
||||
@@ -213,10 +215,10 @@ proc readAtom(s: string, i: var int): CljVal =
|
||||
else: -1
|
||||
if digit < 0 or digit >= radix:
|
||||
raise newException(ReaderError, "invalid digit")
|
||||
val = val * radix + digit
|
||||
val = val * cast[uint64](radix) + cast[uint64](digit)
|
||||
if radixNeg:
|
||||
val = -val
|
||||
return cljInt(val)
|
||||
return cljInt(-cast[int64](val))
|
||||
return cljInt(cast[int64](val))
|
||||
except CatchableError:
|
||||
return cljSymbol(tok)
|
||||
|
||||
@@ -285,15 +287,16 @@ proc readList(s: string, i: var int): CljVal =
|
||||
inc i
|
||||
break
|
||||
let form = readForm(s, i)
|
||||
if form != nil:
|
||||
if form.kind == ckList and form.items.len == 2 and
|
||||
form.items[0].kind == ckSymbol and form.items[0].symName == "splice-unwrap":
|
||||
let inner = form.items[1]
|
||||
if inner.kind == ckVector:
|
||||
for item in inner.items:
|
||||
items.add(item)
|
||||
else:
|
||||
items.add(form)
|
||||
if form == nil:
|
||||
continue
|
||||
if form.kind == ckList and form.items.len == 2 and
|
||||
form.items[0].kind == ckSymbol and form.items[0].symName == "splice-unwrap":
|
||||
let inner = form.items[1]
|
||||
if inner.kind == ckVector:
|
||||
for item in inner.items:
|
||||
items.add(item)
|
||||
else:
|
||||
items.add(form)
|
||||
return cljList(items)
|
||||
|
||||
proc readVector(s: string, i: var int): CljVal =
|
||||
@@ -330,13 +333,18 @@ proc readMap(s: string, i: var int): CljVal =
|
||||
break
|
||||
let key = readForm(s, i)
|
||||
if key == nil:
|
||||
# Reader conditional returned nil for key; skip associated value
|
||||
skipWhitespaceAndComments(s, i)
|
||||
if i < s.len and s[i] != '}':
|
||||
discard readForm(s, i)
|
||||
continue
|
||||
skipWhitespaceAndComments(s, i)
|
||||
if i >= s.len or s[i] == '}':
|
||||
raise newException(ReaderError, "Map must have even number of forms")
|
||||
let val = readForm(s, i)
|
||||
if val == nil:
|
||||
raise newException(ReaderError, "Map value cannot be nil")
|
||||
# Reader conditional returned nil for value; skip this pair
|
||||
continue
|
||||
pairs.add((key, val))
|
||||
return cljMapFromPairs(pairs)
|
||||
|
||||
@@ -361,6 +369,16 @@ proc readDispatch(s: string, i: var int): CljVal =
|
||||
raise newException(ReaderError, "Unexpected end after #")
|
||||
let c = s[i]
|
||||
case c
|
||||
of '#':
|
||||
inc i # skip second '#'
|
||||
let form = readForm(s, i)
|
||||
if form != nil and form.kind == ckSymbol:
|
||||
case form.symName
|
||||
of "Inf": return cljFloat(Inf)
|
||||
of "-Inf": return cljFloat(-Inf)
|
||||
of "NaN": return cljFloat(NaN)
|
||||
else: discard
|
||||
return form
|
||||
of '{':
|
||||
return readSet(s, i)
|
||||
of '(':
|
||||
@@ -390,7 +408,6 @@ proc readDispatch(s: string, i: var int): CljVal =
|
||||
return cljString(regexStr)
|
||||
of 'u':
|
||||
# #uuid"..." — read as string
|
||||
inc i
|
||||
if i + 4 < s.len and s[i..i+3] == "uuid":
|
||||
i += 4
|
||||
while i < s.len and s[i] == ' ': inc i
|
||||
@@ -407,37 +424,45 @@ proc readDispatch(s: string, i: var int): CljVal =
|
||||
# Reader conditional: #?(:clj expr :cljs expr :default expr)
|
||||
# Read the next char - if '@' it's splicing version #?@
|
||||
var isSplice = false
|
||||
var savedI = i
|
||||
inc i
|
||||
if i < s.len and s[i] == '@':
|
||||
isSplice = true
|
||||
inc i
|
||||
else:
|
||||
i = savedI
|
||||
inc i # skip '?'
|
||||
let form = readForm(s, i)
|
||||
if form == nil:
|
||||
return nil
|
||||
if form.kind != ckList:
|
||||
return nil
|
||||
var items = form.items
|
||||
var cljVal: CljVal = nil
|
||||
var defaultVal: CljVal = nil
|
||||
var found = false
|
||||
var k = 0
|
||||
while k < items.len - 1:
|
||||
if items[k].kind == ckKeyword and items[k].kwName == "default":
|
||||
defaultVal = items[k + 1]
|
||||
found = true
|
||||
break
|
||||
let isKeyword = items[k].kind == ckKeyword
|
||||
let isSym = items[k].kind == ckSymbol
|
||||
if isKeyword or isSym:
|
||||
let name = if isKeyword: items[k].kwName else: items[k].symName
|
||||
case name
|
||||
of "clj":
|
||||
cljVal = items[k + 1]
|
||||
of "default":
|
||||
defaultVal = items[k + 1]
|
||||
else: discard
|
||||
k += 2
|
||||
if not found:
|
||||
var resultVal: CljVal = nil
|
||||
if cljVal != nil:
|
||||
resultVal = cljVal
|
||||
elif defaultVal != nil:
|
||||
resultVal = defaultVal
|
||||
else:
|
||||
return nil
|
||||
if isSplice and defaultVal != nil and defaultVal.kind == ckVector:
|
||||
# Return the vector items as a splice marker
|
||||
return cljList(@[cljSymbol("splice-unwrap"), defaultVal])
|
||||
return defaultVal
|
||||
if isSplice and resultVal != nil and resultVal.kind == ckVector:
|
||||
return cljList(@[cljSymbol("splice-unwrap"), resultVal])
|
||||
return resultVal
|
||||
else:
|
||||
raise newException(ReaderError, "Unknown dispatch macro: #" & c)
|
||||
# Unknown dispatch macro: skip the next form and return nil
|
||||
discard readForm(s, i)
|
||||
return nil
|
||||
|
||||
proc readWithMeta(s: string, i: var int): CljVal =
|
||||
inc i # skip '^'
|
||||
@@ -562,6 +587,14 @@ proc readAll*(s: string): seq[CljVal] =
|
||||
if i >= s.len:
|
||||
break
|
||||
let form = readForm(s, i)
|
||||
if form != nil:
|
||||
if form == nil:
|
||||
continue
|
||||
if form.kind == ckList and form.items.len == 2 and
|
||||
form.items[0].kind == ckSymbol and form.items[0].symName == "splice-unwrap":
|
||||
let inner = form.items[1]
|
||||
if inner.kind == ckVector:
|
||||
for item in inner.items:
|
||||
forms.add(item)
|
||||
else:
|
||||
forms.add(form)
|
||||
return forms
|
||||
|
||||
Reference in New Issue
Block a user