chore: emitter/runtime improvements from previous session
This commit is contained in:
+195
-3
@@ -11,6 +11,9 @@ type
|
||||
fn: CljVal
|
||||
args: seq[CljVal]
|
||||
|
||||
ExInfo* = object of CatchableError
|
||||
exData*: CljVal
|
||||
|
||||
CljVal* = ref CljValObj
|
||||
CljValObj = object
|
||||
meta*: CljVal
|
||||
@@ -110,6 +113,24 @@ proc cljSet*(items: seq[CljVal]): CljVal =
|
||||
s = pmapAssoc(s, item, true, hash(item), cljEq)
|
||||
CljVal(kind: ckSet, setData: s)
|
||||
|
||||
proc cljSet*(coll: CljVal): CljVal =
|
||||
var s = newPersistentMap[CljVal, bool]()
|
||||
if coll.isNil: return CljVal(kind: ckSet, setData: s)
|
||||
case coll.kind
|
||||
of ckList:
|
||||
for item in coll.listItems:
|
||||
s = pmapAssoc(s, item, true, hash(item), cljEq)
|
||||
of ckVector:
|
||||
for item in toSeq(coll.vecData):
|
||||
s = pmapAssoc(s, item, true, hash(item), cljEq)
|
||||
of ckSet:
|
||||
return coll
|
||||
of ckMap:
|
||||
for key in pmapKeys(coll.mapData):
|
||||
s = pmapAssoc(s, key, true, hash(key), cljEq)
|
||||
else: discard
|
||||
CljVal(kind: ckSet, setData: s)
|
||||
|
||||
proc cljHashMap*(args: seq[CljVal]): CljVal =
|
||||
if args.len mod 2 != 0:
|
||||
raise newException(CatchableError, "hash-map requires even number of arguments")
|
||||
@@ -185,6 +206,47 @@ proc cljIsColl*(v: CljVal): CljVal = cljBool(not cljIsNil(v) and v.kind in {ckLi
|
||||
proc cljIsSequential*(v: CljVal): CljVal = cljBool(not cljIsNil(v) and v.kind in {ckList, ckVector})
|
||||
proc cljIsFn*(v: CljVal): CljVal = cljBool(not cljIsNil(v) and v.kind == ckFn)
|
||||
proc cljIsVar*(v: CljVal): CljVal = cljBool(false)
|
||||
|
||||
proc cljParseUuid*(s: CljVal): CljVal =
|
||||
if s.kind != ckString:
|
||||
raise newException(CatchableError, "Invalid UUID string")
|
||||
let str = s.strVal
|
||||
if str.len == 0:
|
||||
return cljNil()
|
||||
# Simple UUID validation: 8-4-4-4-12 hex digits
|
||||
var parts: seq[string] = @[]
|
||||
var current = ""
|
||||
for c in str:
|
||||
if c == '-':
|
||||
parts.add(current)
|
||||
current = ""
|
||||
else:
|
||||
current.add(c)
|
||||
parts.add(current)
|
||||
if parts.len == 5 and
|
||||
parts[0].len == 8 and parts[1].len == 4 and parts[2].len == 4 and
|
||||
parts[3].len == 4 and parts[4].len == 12:
|
||||
for p in parts:
|
||||
for c in p:
|
||||
if c notin {'0'..'9', 'a'..'f', 'A'..'F'}:
|
||||
return cljNil()
|
||||
return cljString(str.toLowerAscii())
|
||||
return cljNil()
|
||||
|
||||
proc cljIsUuid*(v: CljVal): CljVal =
|
||||
if v.kind != ckString:
|
||||
return cljBool(false)
|
||||
let str = v.strVal
|
||||
var parts = str.split('-')
|
||||
if parts.len == 5 and
|
||||
parts[0].len == 8 and parts[1].len == 4 and parts[2].len == 4 and
|
||||
parts[3].len == 4 and parts[4].len == 12:
|
||||
for p in parts:
|
||||
for c in p:
|
||||
if c notin {'0'..'9', 'a'..'f', 'A'..'F'}:
|
||||
return cljBool(false)
|
||||
return cljBool(true)
|
||||
return cljBool(false)
|
||||
proc cljIsIfn*(v: CljVal): CljVal = cljBool(not cljIsNil(v) and v.kind in {ckFn, ckKeyword, ckSymbol, ckMap, ckSet, ckVector})
|
||||
proc cljIsBool*(v: CljVal): CljVal = cljBool(not cljIsNil(v) and v.kind == ckBool)
|
||||
proc cljIsTrueP*(v: CljVal): CljVal = cljBool(cljIsTrue(v))
|
||||
@@ -772,10 +834,20 @@ proc cljSortedMap*(args: seq[CljVal]): CljVal =
|
||||
# Fallback to regular hash-map
|
||||
cljHashMap(args)
|
||||
|
||||
proc cljSortedMapBy*(args: seq[CljVal]): CljVal =
|
||||
# Fallback to regular hash-map (ignores comparator)
|
||||
if args.len < 1: return cljMap(@[], @[])
|
||||
cljHashMap(args[1..^1])
|
||||
|
||||
proc cljSortedSet*(args: seq[CljVal]): CljVal =
|
||||
# Fallback to regular hash-set
|
||||
cljHashSet(args)
|
||||
|
||||
proc cljSortedSetBy*(args: seq[CljVal]): CljVal =
|
||||
# Fallback to regular hash-set (ignores comparator)
|
||||
if args.len < 1: return cljSet(@[])
|
||||
cljHashSet(args[1..^1])
|
||||
|
||||
proc cljArrayMap*(args: seq[CljVal]): CljVal =
|
||||
# Fallback to regular hash-map
|
||||
cljHashMap(args)
|
||||
@@ -845,6 +917,14 @@ proc cljToArray*(args: seq[CljVal]): CljVal =
|
||||
for c in v.strVal: items.add(cljString($c))
|
||||
return cljVector(items)
|
||||
else: return cljVector(@[v])
|
||||
|
||||
proc cljIntoArray*(args: seq[CljVal]): CljVal =
|
||||
if args.len == 0: return cljVector(@[])
|
||||
if args.len == 1:
|
||||
return cljToArray(@[args[0]])
|
||||
# Ignore type argument, convert last collection argument to vector
|
||||
return cljToArray(@[args[^1]])
|
||||
|
||||
proc cljVolatileBang*(args: seq[CljVal]): CljVal =
|
||||
if args.len == 0: return cljNil()
|
||||
# volatile! creates a mutable holder - stub as atom
|
||||
@@ -948,6 +1028,19 @@ proc cljDescendants*(args: seq[CljVal]): CljVal =
|
||||
if args.len < 1: return cljNil()
|
||||
cljSet(@[])
|
||||
|
||||
proc cljUseFixtures*(args: seq[CljVal]): CljVal =
|
||||
cljNil()
|
||||
|
||||
proc cljAddWatch*(args: seq[CljVal]): CljVal =
|
||||
cljNil()
|
||||
|
||||
proc cljRemoveWatch*(args: seq[CljVal]): CljVal =
|
||||
cljNil()
|
||||
|
||||
proc cljAlterVarRoot*(args: seq[CljVal]): CljVal =
|
||||
if args.len >= 2: return args[1]
|
||||
cljNil()
|
||||
|
||||
proc cljParents*(args: seq[CljVal]): CljVal =
|
||||
if args.len < 1: return cljNil()
|
||||
cljSet(@[])
|
||||
@@ -1254,6 +1347,42 @@ proc cljGetDefault*(m: CljVal, key: CljVal, default: CljVal): CljVal =
|
||||
else: default
|
||||
else: default
|
||||
|
||||
proc cljExInfo*(msg: CljVal, data: CljVal): CljVal =
|
||||
var ex = newException(ExInfo, cljStr(msg))
|
||||
ex.exData = data
|
||||
raise ex
|
||||
|
||||
proc cljExData*(ex: CljVal): CljVal =
|
||||
if ex.isNil: return cljNil()
|
||||
case ex.kind
|
||||
of ckMap:
|
||||
let data = cljGet(ex, cljKeyword("data"))
|
||||
if data.kind == ckNil:
|
||||
return ex
|
||||
return data
|
||||
else: return cljNil()
|
||||
|
||||
proc cljCall*(f: CljVal, args: seq[CljVal]): CljVal =
|
||||
if f.isNil: return cljNil()
|
||||
case f.kind
|
||||
of ckFn:
|
||||
if args.len > 0: return f.fnProc(args)
|
||||
else: return f.fnProc(@[])
|
||||
of ckMap:
|
||||
if args.len > 0: return cljGet(f, args[0])
|
||||
else: return cljNil()
|
||||
of ckSet:
|
||||
if args.len > 0: return cljBool(pmapContains(f.setData, args[0], hash(args[0]), cljEq))
|
||||
else: return cljBool(false)
|
||||
of ckVector:
|
||||
if args.len > 0: return cljNth(f, args[0])
|
||||
else: return cljNil()
|
||||
of ckKeyword:
|
||||
if args.len > 0: return cljGet(args[0], f)
|
||||
else: return cljNil()
|
||||
else:
|
||||
raise newException(CatchableError, "Cannot call value of type " & $f.kind)
|
||||
|
||||
proc cljAssoc*(m: CljVal, key: CljVal, val: CljVal): CljVal =
|
||||
if m.isNil or m.kind == ckNil:
|
||||
return CljVal(kind: ckMap, mapData: pmapAssoc(newPersistentMap[CljVal, CljVal](), key, val, hash(key), cljEq))
|
||||
@@ -1340,6 +1469,29 @@ proc cljMap*(f: CljVal, coll: CljVal): CljVal =
|
||||
if f.kind == ckFn: cljMap(f.fnProc, coll)
|
||||
else: raise newException(CatchableError, "map requires a function")
|
||||
|
||||
proc cljMapN*(f: CljVal, colls: seq[CljVal]): CljVal =
|
||||
if f.kind != ckFn: raise newException(CatchableError, "map requires a function")
|
||||
let fn = f.fnProc
|
||||
var seqs: seq[seq[CljVal]] = newSeq[seq[CljVal]]()
|
||||
for coll in colls:
|
||||
if coll.isNil:
|
||||
seqs.add(@[])
|
||||
else:
|
||||
case coll.kind
|
||||
of ckList: seqs.add(coll.listItems)
|
||||
of ckVector: seqs.add(toSeq(coll.vecData))
|
||||
else: seqs.add(@[])
|
||||
if seqs.len == 0: return cljList(@[])
|
||||
var res = newSeq[CljVal]()
|
||||
var i = 0
|
||||
while true:
|
||||
var args = newSeq[CljVal]()
|
||||
for s in seqs:
|
||||
if i >= s.len: return cljList(res)
|
||||
args.add(s[i])
|
||||
res.add(fn(args))
|
||||
i += 1
|
||||
|
||||
proc cljFilter*(f: proc(args: seq[CljVal]): CljVal, coll: CljVal): CljVal =
|
||||
if coll.isNil: return cljList(@[])
|
||||
case coll.kind
|
||||
@@ -1362,6 +1514,15 @@ proc cljReduce*(f: CljVal, init: CljVal, coll: CljVal): CljVal =
|
||||
if f.kind == ckFn: cljReduce(f.fnProc, init, coll)
|
||||
else: raise newException(CatchableError, "reduce requires a function")
|
||||
|
||||
proc cljTransduce*(xform: CljVal, f: CljVal, init: CljVal, coll: CljVal): CljVal =
|
||||
# Stub: apply xform to f and reduce
|
||||
if xform.kind == ckFn and f.kind == ckFn:
|
||||
let rf = xform.fnProc(@[f])
|
||||
if rf.kind == ckFn:
|
||||
return cljReduce(rf, init, coll)
|
||||
# Fallback: just return init
|
||||
return init
|
||||
|
||||
proc cljMapv*(f: proc(args: seq[CljVal]): CljVal, coll: CljVal): CljVal =
|
||||
if coll.isNil: return cljVector(@[])
|
||||
case coll.kind
|
||||
@@ -1590,9 +1751,32 @@ proc cljAtom*(args: seq[CljVal]): CljVal =
|
||||
i += 2
|
||||
a
|
||||
|
||||
proc cljPromise*(): CljVal =
|
||||
cljAtom(cljNil())
|
||||
|
||||
proc cljPromise*(args: seq[CljVal]): CljVal =
|
||||
cljAtom(cljNil())
|
||||
|
||||
proc cljForce*(v: CljVal): CljVal =
|
||||
if v.kind == ckAtom: v.atomVal
|
||||
else: v
|
||||
|
||||
proc cljSleep*(ms: CljVal): CljVal =
|
||||
cljNil()
|
||||
|
||||
proc cljTap*(args: seq[CljVal]): CljVal =
|
||||
if args.len >= 1: return args[0]
|
||||
cljNil()
|
||||
|
||||
proc cljFutureCancel*(v: CljVal): CljVal =
|
||||
cljNil()
|
||||
|
||||
proc cljAddTap*(args: seq[CljVal]): CljVal =
|
||||
cljNil()
|
||||
|
||||
proc cljRemoveTap*(args: seq[CljVal]): CljVal =
|
||||
cljNil()
|
||||
|
||||
proc cljFuture*(args: seq[CljVal]): CljVal =
|
||||
if args.len == 0: return cljAtom(cljNil())
|
||||
let f = args[0]
|
||||
@@ -1621,7 +1805,7 @@ proc cljReset*(a: CljVal, v: CljVal): CljVal =
|
||||
else:
|
||||
raise newException(CatchableError, "reset! requires an atom")
|
||||
|
||||
proc cljSwap*(a: CljVal, f: proc(args: seq[CljVal]): CljVal, args: seq[CljVal]): CljVal =
|
||||
proc cljSwapImpl(a: CljVal, f: proc(args: seq[CljVal]): CljVal, args: seq[CljVal]): CljVal =
|
||||
if a.kind == ckAtom:
|
||||
var fargs = @[a.atomVal]
|
||||
fargs.add(args)
|
||||
@@ -1630,10 +1814,18 @@ proc cljSwap*(a: CljVal, f: proc(args: seq[CljVal]): CljVal, args: seq[CljVal]):
|
||||
else:
|
||||
raise newException(CatchableError, "swap! requires an atom")
|
||||
|
||||
proc cljSwap*(a: CljVal, f: CljVal, args: seq[CljVal] = @[]): CljVal =
|
||||
if f.kind == ckFn: cljSwap(a, f.fnProc, args)
|
||||
proc cljSwap*(args: seq[CljVal]): CljVal =
|
||||
if args.len < 2: raise newException(CatchableError, "swap! requires at least 2 arguments")
|
||||
let a = args[0]
|
||||
let f = args[1]
|
||||
let rest = if args.len > 2: args[2..^1] else: @[]
|
||||
if f.kind == ckFn: cljSwapImpl(a, f.fnProc, rest)
|
||||
else: raise newException(CatchableError, "swap! requires a function")
|
||||
|
||||
proc cljVswap*(args: seq[CljVal]): CljVal =
|
||||
# Treat volatile like atom for now
|
||||
cljSwap(args)
|
||||
|
||||
# ---- Agents ----
|
||||
|
||||
proc cljAgent*(v: CljVal): CljVal =
|
||||
|
||||
Reference in New Issue
Block a user