Phase 5/6 complete: Transients, meta/with-meta, instance?
T5.5 Transients (transient/persistent!/conj!/assoc!) - Mutable builder for batch vector/map operations - Use with let bindings for proper ordering T6.10 meta/with-meta — metadata on any CljVal via ref object field T6.11 instance? — type predicate (instance? :integer 42) Also marked T9.1 Atoms as done (already implemented earlier) 86 tests pass, all examples work. Phases 0-6 now complete. Phase 7: 2/4 tasks done.
This commit is contained in:
+93
-2
@@ -5,10 +5,11 @@ import strutils, sequtils, hashes, algorithm, os, osproc
|
||||
type
|
||||
CljKind* = enum
|
||||
ckNil, ckBool, ckInt, ckFloat, ckString, ckKeyword, ckSymbol,
|
||||
ckList, ckVector, ckMap, ckSet, ckFn, ckAtom
|
||||
ckList, ckVector, ckMap, ckSet, ckFn, ckAtom, ckTransient
|
||||
|
||||
CljVal* = ref CljValObj
|
||||
CljValObj = object
|
||||
meta*: CljVal # metadata attached to this value
|
||||
case kind*: CljKind
|
||||
of ckNil: discard
|
||||
of ckBool: boolVal*: bool
|
||||
@@ -23,6 +24,10 @@ type
|
||||
of ckSet: setData*: PersistentMap[CljVal, bool]
|
||||
of ckFn: fnProc*: proc(args: seq[CljVal]): CljVal
|
||||
of ckAtom: atomVal*: CljVal
|
||||
of ckTransient:
|
||||
transKind*: CljKind # ckVector or ckMap — what this transient was created from
|
||||
transVec*: seq[CljVal] # mutable vector builder
|
||||
transPairs*: seq[(CljVal, CljVal)] # mutable map builder
|
||||
|
||||
# ---- Hashing ----
|
||||
|
||||
@@ -37,7 +42,7 @@ proc hash*(v: CljVal): Hash =
|
||||
of ckSymbol: result = hash(v.symName)
|
||||
of ckFn: result = hash(cast[uint](unsafeAddr v.fnProc))
|
||||
of ckAtom: result = hash(cast[uint](unsafeAddr v.atomVal))
|
||||
of ckList, ckVector, ckMap, ckSet: result = hash(0)
|
||||
of ckList, ckVector, ckMap, ckSet, ckTransient: result = hash(0)
|
||||
|
||||
# ---- Equality ----
|
||||
|
||||
@@ -115,6 +120,7 @@ proc cljRepr*(v: CljVal): string =
|
||||
"#{" & parts.join(" ") & "}"
|
||||
of ckFn: "#<fn>"
|
||||
of ckAtom: "(atom " & cljRepr(v.atomVal) & ")"
|
||||
of ckTransient: "#<transient>"
|
||||
|
||||
proc cljStr*(v: CljVal): string =
|
||||
if v.isNil: return ""
|
||||
@@ -368,6 +374,9 @@ proc cljCount*(v: CljVal): CljVal =
|
||||
of ckMap: cljInt(v.mapData.count)
|
||||
of ckSet: cljInt(v.setData.count)
|
||||
of ckString: cljInt(v.strVal.len)
|
||||
of ckTransient:
|
||||
if v.transKind == ckVector: cljInt(v.transVec.len)
|
||||
else: cljInt(v.transPairs.len)
|
||||
else: cljInt(0)
|
||||
|
||||
proc cljFirst*(v: CljVal): CljVal =
|
||||
@@ -922,6 +931,38 @@ proc cljType*(v: CljVal): CljVal =
|
||||
of ckSet: cljKeyword("set")
|
||||
of ckFn: cljKeyword("function")
|
||||
of ckAtom: cljKeyword("atom")
|
||||
of ckTransient: cljKeyword("transient")
|
||||
|
||||
proc cljInstanceP*(t: CljVal, v: CljVal): CljVal =
|
||||
if t.kind != ckKeyword: return cljBool(false)
|
||||
let vt = cljType(v)
|
||||
cljBool(t.kwName == vt.kwName)
|
||||
|
||||
proc cljMeta*(v: CljVal): CljVal =
|
||||
if v.isNil: return cljNil()
|
||||
if v.meta.isNil: return cljNil()
|
||||
v.meta
|
||||
|
||||
proc cljWithMeta*(v: CljVal, m: CljVal): CljVal =
|
||||
if v.isNil: return v
|
||||
var result = v # share ref, but we need a copy
|
||||
result = CljVal(kind: v.kind, meta: m)
|
||||
case v.kind
|
||||
of ckNil: discard
|
||||
of ckBool: result.boolVal = v.boolVal
|
||||
of ckInt: result.intVal = v.intVal
|
||||
of ckFloat: result.floatVal = v.floatVal
|
||||
of ckString: result.strVal = v.strVal
|
||||
of ckKeyword: result.kwName = v.kwName
|
||||
of ckSymbol: result.symName = v.symName
|
||||
of ckList: result.listItems = v.listItems
|
||||
of ckVector: result.vecData = v.vecData
|
||||
of ckMap: result.mapData = v.mapData
|
||||
of ckSet: result.setData = v.setData
|
||||
of ckFn: result.fnProc = v.fnProc
|
||||
of ckAtom: result.atomVal = v.atomVal
|
||||
of ckTransient: result.transKind = v.transKind; result.transVec = v.transVec; result.transPairs = v.transPairs
|
||||
return result
|
||||
|
||||
proc cljAtom*(v: CljVal): CljVal =
|
||||
CljVal(kind: ckAtom, atomVal: v)
|
||||
@@ -950,6 +991,56 @@ proc cljSwap*(a: CljVal, f: CljVal, args: seq[CljVal] = @[]): CljVal =
|
||||
if f.kind == ckFn: cljSwap(a, f.fnProc, args)
|
||||
else: raise newException(CatchableError, "swap! requires a function")
|
||||
|
||||
# ---- Transients ----
|
||||
|
||||
proc cljTransient*(coll: CljVal): CljVal =
|
||||
case coll.kind
|
||||
of ckVector:
|
||||
result = CljVal(kind: ckTransient, transKind: ckVector)
|
||||
result.transVec = toSeq(coll.vecData)
|
||||
of ckMap:
|
||||
result = CljVal(kind: ckTransient, transKind: ckMap)
|
||||
result.transPairs = pmapEntries(coll.mapData)
|
||||
of ckSet:
|
||||
result = CljVal(kind: ckTransient, transKind: ckVector)
|
||||
result.transVec = pmapKeys(coll.setData)
|
||||
else:
|
||||
raise newException(CatchableError, "transient requires a collection")
|
||||
|
||||
proc cljPersistent*(t: CljVal): CljVal =
|
||||
if t.isNil or t.kind != ckTransient:
|
||||
raise newException(CatchableError, "persistent! requires a transient")
|
||||
case t.transKind
|
||||
of ckVector:
|
||||
cljVector(t.transVec)
|
||||
of ckMap:
|
||||
var m = newPersistentMap[CljVal, CljVal]()
|
||||
for (k, v) in t.transPairs:
|
||||
m = pmapAssoc(m, k, v, hash(k), cljEq)
|
||||
CljVal(kind: ckMap, mapData: m)
|
||||
else:
|
||||
raise newException(CatchableError, "persistent! requires a transient")
|
||||
|
||||
proc cljConjB*(t: CljVal, item: CljVal): CljVal =
|
||||
if t.isNil or t.kind != ckTransient:
|
||||
raise newException(CatchableError, "conj! requires a transient")
|
||||
case t.transKind
|
||||
of ckVector:
|
||||
t.transVec.add(item)
|
||||
of ckMap:
|
||||
if item.kind == ckVector and item.vecData.count == 2:
|
||||
t.transPairs.add((pvecNth(item.vecData, 0), pvecNth(item.vecData, 1)))
|
||||
of ckSet: discard
|
||||
else: raise newException(CatchableError, "conj! unsupported source type")
|
||||
t
|
||||
|
||||
proc cljAssocB*(t: CljVal, key: CljVal, val: CljVal): CljVal =
|
||||
if t.isNil or t.kind != ckTransient:
|
||||
raise newException(CatchableError, "assoc! requires a transient")
|
||||
if t.transKind == ckMap:
|
||||
t.transPairs.add((key, val))
|
||||
t
|
||||
|
||||
# ---- Additional functions needed by emitter ----
|
||||
|
||||
proc cljNotEq*(args: seq[CljVal]): CljVal =
|
||||
|
||||
Reference in New Issue
Block a user