created a nice 'makeReactive' macro
This commit is contained in:
parent
cb2dd6331d
commit
4032bed306
1 changed files with 119 additions and 9 deletions
128
src/reactive.nim
128
src/reactive.nim
|
|
@ -1,4 +1,6 @@
|
||||||
|
|
||||||
|
import jdict
|
||||||
|
|
||||||
type
|
type
|
||||||
Message = enum
|
Message = enum
|
||||||
Unchanged
|
Unchanged
|
||||||
|
|
@ -14,9 +16,11 @@ type
|
||||||
phantom: bool
|
phantom: bool
|
||||||
|
|
||||||
type
|
type
|
||||||
|
SinkSeq = seq[proc(msg: Message, pos: int)]
|
||||||
ReactiveBase* = ref object of RootObj ## everything that is a "reactive"
|
ReactiveBase* = ref object of RootObj ## everything that is a "reactive"
|
||||||
## value derives from that
|
## value derives from that
|
||||||
sinks*: seq[proc(msg: Message, pos: int)]
|
sinks*: SinkSeq
|
||||||
|
dups: JDict[cstring, bool]
|
||||||
id: int
|
id: int
|
||||||
Reactive*[T] = ref object of ReactiveBase
|
Reactive*[T] = ref object of ReactiveBase
|
||||||
value*: T
|
value*: T
|
||||||
|
|
@ -33,10 +37,16 @@ type
|
||||||
|
|
||||||
var rid: int
|
var rid: int
|
||||||
|
|
||||||
|
proc addSink(x: ReactiveBase; key: cstring; sink: proc(msg: Message; pos: int)) =
|
||||||
|
if x.dups == nil: x.dups = newJDict[cstring, bool]()
|
||||||
|
if not x.dups.contains(key):
|
||||||
|
x.dups[key] = true
|
||||||
|
x.sinks.add sink
|
||||||
|
if x.id == 0:
|
||||||
|
inc rid
|
||||||
|
x.id = rid
|
||||||
|
|
||||||
proc addSink(x: ReactiveBase; sink: proc(msg: Message; pos: int)) =
|
proc addSink(x: ReactiveBase; sink: proc(msg: Message; pos: int)) =
|
||||||
#if x.sinks.len > 0:
|
|
||||||
# x.sinks[0] = sink
|
|
||||||
#else:
|
|
||||||
x.sinks.add sink
|
x.sinks.add sink
|
||||||
if x.id == 0:
|
if x.id == 0:
|
||||||
inc rid
|
inc rid
|
||||||
|
|
@ -78,6 +88,29 @@ template wrapObserver(f: untyped) =
|
||||||
#state.phantom = true
|
#state.phantom = true
|
||||||
helper
|
helper
|
||||||
|
|
||||||
|
template glitchFree(f: untyped) =
|
||||||
|
var state: State
|
||||||
|
proc helper(msg: Message; pos: int) =
|
||||||
|
case msg.kind:
|
||||||
|
of Changed, Unchanged:
|
||||||
|
state.outdated = state.outdated or (msg.kind == Changed)
|
||||||
|
dec state.stale
|
||||||
|
if state.stale == 0:
|
||||||
|
if state.outdated:
|
||||||
|
x.value = f
|
||||||
|
x.broadcast(msg.kind, pos)
|
||||||
|
state.outdated = false
|
||||||
|
else:
|
||||||
|
x.broadcast(Unchanged, pos)
|
||||||
|
of Mark:
|
||||||
|
if state.stale == 0: x.broadcast(msg, pos)
|
||||||
|
inc state.stale
|
||||||
|
of Inserted, Deleted, Replaced:
|
||||||
|
dec state.stale
|
||||||
|
if state.stale == 0:
|
||||||
|
x.broadcast(msg, pos)
|
||||||
|
helper
|
||||||
|
|
||||||
proc `:=`[T](x: Reactive[T], f: proc(): T) =
|
proc `:=`[T](x: Reactive[T], f: proc(): T) =
|
||||||
toTrack = wrapObserver(f())
|
toTrack = wrapObserver(f())
|
||||||
x.value = f()
|
x.value = f()
|
||||||
|
|
@ -186,17 +219,29 @@ proc map*[T, U](x: RSeq[T], f: proc(x: T): U): RSeq[U] =
|
||||||
|
|
||||||
import macros
|
import macros
|
||||||
|
|
||||||
template trackImpl(r: ReactiveBase; a, b: untyped) =
|
template protect(r: ReactiveBase; body: untyped) =
|
||||||
|
var tmp: seq[proc(msg: Message, pos: int)]
|
||||||
|
swap(r.sinks, tmp)
|
||||||
|
body
|
||||||
|
swap(r.sinks, tmp)
|
||||||
|
|
||||||
|
template trackImpl(r: ReactiveBase; key: cstring; a, b: untyped) =
|
||||||
when r is ReactiveBase:
|
when r is ReactiveBase:
|
||||||
addSink r, proc(m: Message; pos: int) =
|
addSink r, key, proc(m: Message; pos: int) =
|
||||||
if m == Changed: karax.runDiff(kxi, a, b)
|
if m == Changed:
|
||||||
|
protect r:
|
||||||
|
echo "runDiff A"
|
||||||
|
karax.runDiff(kxi, a, b)
|
||||||
|
|
||||||
template doTrack*(r: ReactiveBase; a, b: untyped) {.dirty.} =
|
template doTrack*(r: ReactiveBase; a, b: untyped) {.dirty.} =
|
||||||
bind addSink, Message, RSeq, Changed, Deleted, Inserted
|
bind addSink, Message, RSeq, Changed, Deleted, Inserted
|
||||||
addSink r, proc(m: Message; pos: int) =
|
addSink r, proc(m: Message; pos: int) =
|
||||||
#when r is RSeq:
|
#when r is RSeq:
|
||||||
#echo "Message: ", m, " ", pos
|
#echo "Message: ", m, " ", pos
|
||||||
if m == Changed: karax.runDiff(kxi, a, b)
|
if m == Changed:
|
||||||
|
protect r:
|
||||||
|
echo "runDiff B"
|
||||||
|
karax.runDiff(kxi, a, b)
|
||||||
|
|
||||||
template doTrackResize*(r: ReactiveBase; a, b: untyped) {.dirty.} =
|
template doTrackResize*(r: ReactiveBase; a, b: untyped) {.dirty.} =
|
||||||
bind addSink, Message, RSeq, Changed, Deleted, Inserted
|
bind addSink, Message, RSeq, Changed, Deleted, Inserted
|
||||||
|
|
@ -226,10 +271,75 @@ macro track*(procDef: untyped): untyped =
|
||||||
let param = x[i]
|
let param = x[i]
|
||||||
call.add(param)
|
call.add(param)
|
||||||
|
|
||||||
|
let key = lineInfo(procDef)
|
||||||
for j in 1..<call.len:
|
for j in 1..<call.len:
|
||||||
trackings.add getAst(trackImpl(call[j], ident"result", call))
|
trackings.add getAst(trackImpl(call[j], key, ident"result", call))
|
||||||
|
|
||||||
result = copyNimTree(procDef)
|
result = copyNimTree(procDef)
|
||||||
result.body = trackings
|
result.body = trackings
|
||||||
when defined(debugKaraxDsl):
|
when defined(debugKaraxDsl):
|
||||||
echo repr result
|
echo repr result
|
||||||
|
|
||||||
|
macro mut(t: typed): untyped =
|
||||||
|
if getType(t).typeKind == ntyRef:
|
||||||
|
result = t
|
||||||
|
else:
|
||||||
|
result = newTree(nnkVarTy, t)
|
||||||
|
|
||||||
|
proc generatePrivateAccessors(name, hidden, typ, fieldTyp: NimNode): NimNode =
|
||||||
|
template helper(name, hidden, typ, fieldTyp) {.dirty.} =
|
||||||
|
proc name(self: typ): fieldTyp = self.hidden
|
||||||
|
proc `name=`(self: mut typ; val: fieldTyp) =
|
||||||
|
self.hidden = val
|
||||||
|
notifyObservers(self)
|
||||||
|
result = getAst(helper(name, hidden, typ, fieldTyp))
|
||||||
|
|
||||||
|
proc generatePublicAccessors(name, hidden, typ, fieldTyp: NimNode): NimNode =
|
||||||
|
template helper(name, hidden, typ, fieldTyp) {.dirty.} =
|
||||||
|
proc name*(self: typ): fieldTyp = self.hidden
|
||||||
|
proc `name=`*(self: mut typ; val: fieldTyp) =
|
||||||
|
self.hidden = val
|
||||||
|
notifyObservers(self)
|
||||||
|
result = getAst(helper(name, hidden, typ, fieldTyp))
|
||||||
|
|
||||||
|
proc transform(n: NimNode; stmts, obj: NimNode): NimNode =
|
||||||
|
if n.kind == nnkIdentDefs and obj.kind == nnkIdent:
|
||||||
|
for i in 0..n.len-3:
|
||||||
|
let it = n[i]
|
||||||
|
let itB = if it.kind == nnkPostFix: it[1] else: it
|
||||||
|
let hidden = newIdentNode("raw" & $itB)
|
||||||
|
if it.kind == nnkPostFix:
|
||||||
|
stmts.add generatePublicAccessors(itB, hidden, obj, n[n.len-2])
|
||||||
|
else:
|
||||||
|
stmts.add generatePrivateAccessors(itB, hidden, obj, n[n.len-2])
|
||||||
|
n[i] = hidden
|
||||||
|
result = n
|
||||||
|
else:
|
||||||
|
var objB = if n.kind == nnkTypeDef: n[0] else: obj
|
||||||
|
result = copyNimNode(n)
|
||||||
|
for i in 0..<n.len:
|
||||||
|
if n.kind == nnkObjectTy and i == 1 and n[1].kind == nnkEmpty:
|
||||||
|
result.add newTree(nnkOfInherit, ident("ReactiveBase"))
|
||||||
|
else:
|
||||||
|
result.add transform(n[i], stmts, objB)
|
||||||
|
|
||||||
|
macro makeReactive*(n: untyped): untyped =
|
||||||
|
var a = newStmtList()
|
||||||
|
a.add newEmptyNode()
|
||||||
|
let t = transform(n, a, newEmptyNode())
|
||||||
|
a[0] = t
|
||||||
|
result = a
|
||||||
|
when defined(debugKaraxDsl):
|
||||||
|
echo repr result
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
makeReactive:
|
||||||
|
type
|
||||||
|
Foo = ref object of ReactiveBase
|
||||||
|
x, y: int
|
||||||
|
Bar = ref object of ReactiveBase
|
||||||
|
z: string
|
||||||
|
exported*: int
|
||||||
|
Another = ref object
|
||||||
|
a, b: cstring
|
||||||
|
c: bool
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue