chore: emitter/runtime improvements from previous session

This commit is contained in:
2026-05-11 01:49:48 +03:00
parent 075e5f6df0
commit e5897125fd
2 changed files with 523 additions and 84 deletions
+195 -3
View File
@@ -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 =