syndicate-nim/src/syndicate/skeletons.nim

274 lines
9.9 KiB
Nim
Raw Normal View History

2021-09-01 11:44:28 +00:00
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
# SPDX-License-Identifier: Unlicense
2022-03-10 23:30:07 +00:00
import std/[hashes, lists, options, sets, tables]
import preserves
import ./actors, ./bags, ./patterns
2023-03-23 19:06:44 +00:00
import ./protocols/dataspacePatterns
2022-03-10 23:30:07 +00:00
type
2023-03-23 19:06:44 +00:00
DCompound = dataspacePatterns.DCompound[Ref]
Pattern = dataspacePatterns.Pattern[Ref]
2022-03-10 23:30:07 +00:00
Value = Preserve[Ref]
Path = seq[Value]
ClassKind = enum classNone, classRecord, classSequence, classDictionary
Class = object
kind: ClassKind
label: Value
arity: int
func classOf(v: Value): Class =
case v.kind
of pkRecord: Class(kind: classRecord, label: v.label, arity: v.arity)
of pkSequence: Class(kind: classSequence, arity: v.len)
of pkDictionary: Class(kind: classDictionary)
else: Class(kind: classNone)
proc classOf(p: Pattern): Class =
2022-03-10 23:30:07 +00:00
if p.orKind == PatternKind.DCompound:
case p.dcompound.orKind
of DCompoundKind.rec:
Class(kind: classRecord,
label: p.dcompound.rec.label,
arity: p.dcompound.rec.fields.len)
2022-03-10 23:30:07 +00:00
of DCompoundKind.arr:
Class(kind: classSequence, arity: p.dcompound.arr.items.len)
2022-03-10 23:30:07 +00:00
of DCompoundKind.dict:
Class(kind: classDictionary)
else:
Class(kind: classNone)
type
2022-03-10 23:30:07 +00:00
EventKind = enum addedEvent, removedEvent, messageEvent
2021-07-09 13:08:02 +00:00
AssertionCache = HashSet[Value]
Paths = seq[Path]
ObserverGroup = ref object # Endpoints
2022-03-10 23:30:07 +00:00
cachedCaptures: Bag[seq[Value]]
observers: Table[Ref, TableRef[seq[Value], Handle]]
Leaf = ref object
2021-07-09 13:08:02 +00:00
cachedAssertions: AssertionCache
observerGroups: Table[Paths, ObserverGroup]
Continuation = ref object
2021-07-09 13:08:02 +00:00
cachedAssertions: AssertionCache
leafMap: Table[Paths, TableRef[seq[Value], Leaf]]
2022-03-10 23:30:07 +00:00
Selector = tuple[popCount: int; index: Value]
Node = ref object
2022-03-10 23:30:07 +00:00
edges: Table[Selector, TableRef[Class, Node]]
continuation: Continuation
2022-03-11 20:29:46 +00:00
func isEmpty(leaf: Leaf): bool =
2022-03-10 23:30:07 +00:00
leaf.cachedAssertions.len == 0 and leaf.observerGroups.len == 0
type
ContinuationProc = proc (c: Continuation; v: Value) {.gcsafe.}
LeafProc = proc (l: Leaf; v: Value) {.gcsafe.}
ObserverProc = proc (turn: var Turn; group: ObserverGroup; vs: seq[Value]) {.gcsafe.}
2022-03-11 20:29:46 +00:00
type TermStack = SinglyLinkedList[Value]
proc push(stack: TermStack; val: Value): Termstack =
result = stack
prepend(result, val)
proc pop(stack: TermStack; n: int): TermStack =
result = stack
var n = n
while n > 0:
result.remove(result.head)
assert not stack.head.isNil, "popped too far"
dec n
proc top(stack: TermStack): Value =
assert not stack.head.isNil, "stack is empty"
stack.head.value
proc modify(node: Node; turn: var Turn; outerValue: Value; event: EventKind;
modCont: ContinuationProc; modLeaf: LeafProc; modObs: ObserverProc) =
proc walk(turn: var Turn; cont: Continuation; outerValue: Value; event: EventKind) =
modCont(cont, outerValue)
for constPaths, constValMap in cont.leafMap.pairs:
let constVals = projectPaths(outerValue, constPaths)
var leaf = constValMap.getOrDefault(constVals)
if leaf.isNil and event == addedEvent:
2022-03-10 23:30:07 +00:00
new leaf
2022-03-11 20:29:46 +00:00
constValMap[constVals] = leaf
2022-03-10 23:30:07 +00:00
if not leaf.isNil:
2022-03-11 20:29:46 +00:00
modLeaf(leaf, outerValue)
for capturePaths, observerGroup in leaf.observerGroups.pairs:
modObs(turn, observerGroup, projectPaths(outerValue, capturePaths))
# TODO: cleanup dead leaves
proc walk(node: Node; turn: var Turn; outerValue: Value; event: EventKind; termStack: TermStack) =
walk(turn, node.continuation, outerValue, event)
for selector, table in node.edges:
2022-03-10 23:30:07 +00:00
let
2022-03-11 20:29:46 +00:00
nextStack = pop(termStack, selector.popCount)
nextValue = step(nextStack.top, selector.index)
if nextValue.isSome:
let nextClass = classOf(get nextValue)
if nextClass.kind != classNone:
let nextNode = table.getOrDefault(nextClass)
if not nextNode.isNil:
walk(nextNode, turn, outerValue, event, push(nextStack, get nextValue))
2022-03-11 20:29:46 +00:00
var stack: TermStack
walk(node, turn, outerValue, event, push(stack, @[outerValue].toPreserve(Ref)))
proc getOrNew[A, B, C](t: var Table[A, TableRef[B, C]], k: A): TableRef[B, C] =
result = t.getOrDefault(k)
if result.isNil:
result = newTable[B, C]()
t[k] = result
iterator pairs(dc: DCompound): (Value, Pattern) =
case dc.orKind
of DCompoundKind.rec:
for i, p in dc.rec.fields:
yield (toPreserve(i, Ref), p,)
of DCompoundKind.arr:
for i, p in dc.arr.items:
yield (toPreserve(i, Ref), p,)
of DCompoundKind.dict:
for pair in dc.dict.entries.pairs:
yield pair
proc extendWalk(node: Node; popCount: Natural; stepIndex: Value; pat: Pattern; path: var Path): tuple[popCount: Natural, nextNode: Node] =
2022-03-11 20:29:46 +00:00
case pat.orKind
of PatternKind.DDiscard, PatternKind.DLit:
result = (popCount, node)
of PatternKind.DBind:
result = extendWalk(node, popCount, stepIndex, pat.dbind.pattern, path)
2022-03-11 20:29:46 +00:00
of PatternKind.DCompound:
let
class = classOf pat
selector: Selector = (popCount, stepIndex,)
table = node.edges.getOrNew(selector)
2022-03-11 20:29:46 +00:00
result.nextNode = table.getOrDefault(class)
if result.nextNode.isNil:
new result.nextNode
table[class] = result.nextNode
new result.nextNode.continuation
2022-03-11 20:29:46 +00:00
for a in node.continuation.cachedAssertions:
var v = projectPath(a, path)
if v.isSome and class == classOf(get v):
2022-03-11 20:29:46 +00:00
result.nextNode.continuation.cachedAssertions.incl a
for i, p in pat.dcompound.pairs:
add(path, i)
result = extendWalk(result.nextNode, result.popCount, i, p, path)
discard pop(path)
inc(result.popCount)
2021-07-09 13:08:02 +00:00
2022-03-11 20:29:46 +00:00
proc extend(node: var Node; pat: Pattern): Continuation =
2022-03-10 23:30:07 +00:00
var path: Path
extendWalk(node, 0, toPreserve(0, Ref), pat, path).nextNode.continuation
type
Index* = object
allAssertions: Bag[Value]
root: Node
observerCount: int
proc initIndex*(): Index =
2021-07-08 09:50:13 +00:00
Index(root: Node(continuation: Continuation()))
2022-03-10 23:30:07 +00:00
proc add*(index: var Index; turn: var Turn; pattern: Pattern; observer: Ref) =
let
2022-03-10 23:30:07 +00:00
analysis = analyse pattern
continuation = index.root.extend pattern
var constValMap = continuation.leafMap.getOrDefault(analysis.constPaths)
if constValMap.isNil:
2022-03-10 23:30:07 +00:00
new constValMap
for a in continuation.cachedAssertions:
2022-03-10 23:30:07 +00:00
let key = projectPaths(a, analysis.constPaths)
2021-07-09 13:08:02 +00:00
var leaf = constValMap.getOrDefault(key)
if leaf.isNil:
new leaf
2021-07-09 13:08:02 +00:00
constValMap[key] = leaf
leaf.cachedAssertions.incl(a)
2022-03-10 23:30:07 +00:00
continuation.leafMap[analysis.constPaths] = constValMap
var leaf = constValMap.getOrDefault(analysis.constValues)
if leaf.isNil:
new leaf
2022-03-10 23:30:07 +00:00
constValMap[analysis.constValues] = leaf
var observerGroup = leaf.observerGroups.getOrDefault(analysis.capturePaths)
if observerGroup.isNil:
new observerGroup
for a in leaf.cachedAssertions:
2022-03-10 23:30:07 +00:00
discard observerGroup.cachedCaptures.change(projectPaths(a, analysis.capturePaths), +1)
leaf.observerGroups[analysis.capturePaths] = observerGroup
var captureMap = newTable[seq[Value], Handle]()
for (count, captures) in observerGroup.cachedCaptures:
captureMap[captures] = publish(turn, observer, captures)
observerGroup.observers[observer] = captureMap
proc remove*(index: var Index; turn: var Turn; pattern: Pattern; observer: Ref) =
var
analysis = analyse pattern
continuation = index.root.extend pattern
let constValMap = continuation.leafMap.getOrDefault(analysis.constPaths)
if not constValMap.isNil:
let leaf = constValMap.getOrDefault(analysis.constValues)
if not leaf.isNil:
let observerGroup = leaf.observerGroups.getOrDefault(analysis.capturePaths)
if not observerGroup.isNil:
let captureMap = observerGroup.observers.getOrDefault(observer)
if not captureMap.isNil:
for handle in captureMap.values: retract(observer.target, turn, handle)
observerGroup.observers.del(observer)
if observerGroup.observers.len == 0:
leaf.observerGroups.del(analysis.capturePaths)
if leaf.isEmpty:
constValMap.del(analysis.constValues)
if constValMap.len == 0:
continuation.leafMap.del(analysis.constPaths)
proc adjustAssertion*(index: var Index; turn: var Turn; outerValue: Value; delta: int): bool =
case index.allAssertions.change(outerValue, delta)
of cdAbsentToPresent:
2022-03-10 23:30:07 +00:00
result = true
2022-03-11 20:29:46 +00:00
proc modContinuation(c: Continuation; v: Value) =
c.cachedAssertions.incl(v)
proc modLeaf(l: Leaf; v: Value) =
l.cachedAssertions.incl(v)
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
if group.cachedCaptures.change(vs, +1) == cdAbsentToPresent:
for (observer, captureMap) in group.observers.pairs:
let a = vs.toPreserve(Ref)
captureMap[vs] = publish(turn, observer, a)
# TODO: this handle is coming from the facet?
modify(index.root, turn, outerValue, addedEvent, modContinuation, modLeaf, modObserver)
of cdPresentToAbsent:
2022-03-10 23:30:07 +00:00
result = true
2022-03-11 20:29:46 +00:00
proc modContinuation(c: Continuation; v: Value) =
c.cachedAssertions.excl(v)
proc modLeaf(l: Leaf; v: Value) =
l.cachedAssertions.excl(v)
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
if group.cachedCaptures.change(vs, -1) == cdPresentToAbsent:
for (observer, captureMap) in group.observers.pairs:
retract(observer.target, turn, captureMap[vs])
captureMap.del(vs)
modify(index.root, turn, outerValue, removedEvent, modContinuation, modLeaf, modObserver)
2022-03-10 23:30:07 +00:00
else: discard
proc continuationNoop(c: Continuation; v: Value) = discard
proc leafNoop(l: Leaf; v: Value) = discard
2022-03-10 23:30:07 +00:00
proc add*(index: var Index; turn: var Turn; v: Assertion): bool =
adjustAssertion(index, turn, v, +1)
proc remove*(index: var Index; turn: var Turn; v: Assertion): bool =
adjustAssertion(index, turn, v, -1)
2022-03-11 20:29:46 +00:00
proc deliverMessage*(index: var Index; turn: var Turn; v: Value) =
2022-03-10 23:30:07 +00:00
proc observersCb(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
for observer in group.observers.keys: message(turn, observer, vs)
2022-03-11 20:29:46 +00:00
index.root.modify(turn, v, messageEvent, continuationNoop, leafNoop, observersCb)