stateful components now work

This commit is contained in:
Andreas Rumpf 2017-04-19 11:17:00 +02:00
commit be4159fc11
2 changed files with 55 additions and 40 deletions

View file

@ -6,7 +6,7 @@ var
proc carousel*(key: VKey): VNode {.component.} = proc carousel*(key: VKey): VNode {.component.} =
state: state:
var counter = 0 var counter: int = 0
proc next(ev: Event; n: VNode) = proc next(ev: Event; n: VNode) =
counter = (counter + 1) mod images.len counter = (counter + 1) mod images.len

View file

@ -4,8 +4,9 @@ import macros, jdict, dom, vdom, tables, strutils
type type
StateDict*[V] = ref object StateDict*[V] = ref object
defaultValue*: V
proc `[]`*[V](d: StateDict[V], k: VKey): V {.importcpp: "#[#]".} proc get[V](d: StateDict[V], k: VKey): V {.importcpp: "#[#]".}
proc put[V](d: StateDict[V], k: VKey, v: V) {.importcpp: "#[#] = #".} proc put[V](d: StateDict[V], k: VKey, v: V) {.importcpp: "#[#] = #".}
proc contains*[V](d: StateDict[V], k: VKey): bool {.importcpp: "#.hasOwnProperty(#)".} proc contains*[V](d: StateDict[V], k: VKey): bool {.importcpp: "#.hasOwnProperty(#)".}
@ -24,6 +25,10 @@ proc markDirty*(key: VKey) =
proc unmarkDirty*(key: VKey) = dirty.del key proc unmarkDirty*(key: VKey) = dirty.del key
proc isDirty*(key: VKey): bool = dirty.contains(key) proc isDirty*(key: VKey): bool = dirty.contains(key)
proc `[]`*[V](d: StateDict[V], k: VKey): V =
if d.contains(k): result = d.get(k)
else: result = d.defaultValue
proc `[]=`*[V](d: StateDict[V], k: VKey, v: V) = proc `[]=`*[V](d: StateDict[V], k: VKey, v: V) =
d.put(k, v) d.put(k, v)
markDirty(k) markDirty(k)
@ -56,50 +61,62 @@ proc addTags() {.compileTime.} =
static: static:
addTags() addTags()
proc stateDecl(n: NimNode; names: TableRef[string, bool]) =
template toState(x): untyped = newIdentNode("state" & x)
proc accessState(sv: NimNode): NimNode {.compileTime.} =
newTree(nnkBracketExpr, sv, newIdentNode("key"))
proc stateDecl(n: NimNode; names: TableRef[string, bool]; decl: NimNode) =
case n.kind case n.kind
of nnkVarSection, nnkLetSection: of nnkVarSection, nnkLetSection:
for c in n: for c in n:
expectKind c, nnkIdentDefs expectKind c, nnkIdentDefs
let typ = c[^2]
let val = c[^1]
let usedType = if typ.kind != nnkEmpty: typ else: newCall("type", val)
if usedType.kind == nnkEmpty:
error("cannot determine the variable's type", c)
for i in 0 .. c.len-3: for i in 0 .. c.len-3:
let v = $c[i] let v = $c[i]
let sv = toState v
decl.add quote do:
var `sv` = newStateDict[`usedType`]()
if val.kind != nnkEmpty:
decl.add newTree(nnkAsgn, newDotExpr(sv, newIdentNode"defaultValue"),
val)
else:
error("component state must have a primitive initializer")
names[v] = true names[v] = true
of nnkStmtList, nnkStmtListExpr: of nnkStmtList, nnkStmtListExpr:
for x in n: stateDecl(x, names) for x in n: stateDecl(x, names, decl)
of nnkDo: of nnkDo:
stateDecl(n.body, names) stateDecl(n.body, names, decl)
else: discard of nnkCommentStmt: discard
proc accessesState(n: NimNode; names: TableRef[string, bool]): bool =
case n.kind
of nnkSym, nnkIdent:
result = $n in names
of nnkBracketExpr, nnkDotExpr:
result = accessesState(n[0], names)
else: else:
for i in 0..<n.len: error("invalid 'state' declaration", n)
if accessesState(n[i], names): return true
proc doState(n: NimNode; names: TableRef[string, bool]; proc doState(n: NimNode; names: TableRef[string, bool];
outer: NimNode): NimNode = decl: NimNode): NimNode =
result = n
case n.kind case n.kind
of nnkCallKinds: of nnkCallKinds:
# handle 'state' declaration and move it to the outer block: # handle 'state' declaration and remove it from the AST:
if n.len == 2 and repr(n[0]) == "state": if n.len == 2 and repr(n[0]) == "state":
stateDecl(n[1], names) stateDecl(n[1], names, decl)
outer.add n[1] return nil #newTree(nnkEmpty)
result = newEmptyNode() of nnkSym, nnkIdent:
of nnkAsgn, nnkFastAsgn: let v = $n
if accessesState(n[0], names): if v in names:
result = newStmtList(n, newCall(bindSym"markDirty", newIdentNode"key")) let sv = toState v
else: return accessState(sv)
for i in 0..<n.len: else: discard
result[i] = doState(n[i], names, outer) result = copyNimNode(n)
for i in 0..<n.len:
let x = doState(n[i], names, decl)
if x != nil: result.add x
proc compBody(body, outer: NimNode): NimNode = proc compBody(body, decl: NimNode): NimNode =
var names = newTable[string, bool]() var names = newTable[string, bool]()
result = doState(body, names, outer) result = doState(body, names, decl)
proc unpack(symbolicType: NimNode; index: int): NimNode {.compileTime.} = proc unpack(symbolicType: NimNode; index: int): NimNode {.compileTime.} =
#let t = symbolicType.getTypeImpl #let t = symbolicType.getTypeImpl
@ -162,8 +179,8 @@ macro component*(prc: untyped): untyped =
unpackCall.add unpack(typ, counter) unpackCall.add unpack(typ, counter)
inc counter inc counter
let outer = newTree(nnkStmtList) let decl = newTree(nnkStmtList)
discard compBody(n.body, outer) let newBody = compBody(n, decl)
template vwrapper(pname, unpackCall) {.dirty.} = template vwrapper(pname, unpackCall) {.dirty.} =
proc pname(args: seq[VNode]): VNode = proc pname(args: seq[VNode]): VNode =
@ -181,15 +198,13 @@ macro component*(prc: untyped): untyped =
bind jdict.`[]=` bind jdict.`[]=`
`[]=`(dcomponents, cstring(key), val) `[]=`(dcomponents, cstring(key), val)
outer.add n result = newTree(nnkStmtList, decl, newBody)
#outer.add body
outer.add unpackCall
result = newTree(nnkStmtList)
if isvirtual == ComponentKind.VNode: if isvirtual == ComponentKind.VNode:
result.add getAst(vwrapper(newname name, outer)) result.add getAst(vwrapper(newname name, unpackCall))
result.add getAst(vregister(newLit(nn), realName)) result.add getAst(vregister(newLit(nn), realName))
else: else:
result.add getAst(dwrapper(newname name, outer)) result.add getAst(dwrapper(newname name, unpackCall))
result.add getAst(dregister(newLit(nn), realName)) result.add getAst(dregister(newLit(nn), realName))
allcomponents[nn] = isvirtual allcomponents[nn] = isvirtual
when defined(debugKaraxDsl): when defined(debugKaraxDsl):
@ -205,6 +220,6 @@ when isMainModule:
let cc = callback let cc = callback
proc private(x, y: int, b: bool; s: cstring): VNode {.component.} = when false:
discard proc private(x, y: int, b: bool; s: cstring): VNode {.component.} =
discard