syndicate-nim/src/syndicate/skeletons.nim

305 lines
11 KiB
Nim
Raw Normal View History

2023-07-20 17:31:02 +00:00
# SPDX-FileCopyrightText: ☭ Emery Hemingway
2021-09-01 11:44:28 +00:00
# SPDX-License-Identifier: Unlicense
2023-07-24 07:55:49 +00:00
## https://git.syndicate-lang.org/syndicate-lang/syndicate-rkt/src/commit/90c4c60699069b496491b81ee63b5a45ffd638cb/syndicate/HOWITWORKS.md
import std/[hashes, options, sets, tables]
2022-03-10 23:30:07 +00:00
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-12-31 17:15:06 +00:00
DCompound = dataspacePatterns.DCompound
Pattern = dataspacePatterns.Pattern
2022-03-10 23:30:07 +00:00
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]
ObserverGroup = ref object # Endpoints
2023-07-20 17:31:02 +00:00
cachedCaptures: Bag[Captures]
2023-07-24 15:13:36 +00:00
observers: Table[Cap, TableRef[Captures, Handle]]
2022-03-10 23:30:07 +00:00
Leaf = ref object
2023-07-20 17:31:02 +00:00
cache: AssertionCache
observerGroups: Table[Paths, ObserverGroup]
2023-07-20 17:31:02 +00:00
LeafMap = TableRef[seq[Value], Leaf]
Continuation = ref object
2023-07-20 17:31:02 +00:00
cache: AssertionCache
leafMap: Table[Paths, LeafMap]
2023-07-20 17:31:02 +00:00
func isEmpty(leaf: Leaf): bool =
leaf.cache.len == 0 and leaf.observerGroups.len == 0
2023-07-20 17:31:02 +00:00
func isEmpty(cont: Continuation): bool =
cont.cache.len == 0 and cont.leafMap.len == 0
type
ContinuationProc = proc (c: Continuation; v: Value) {.closure.}
LeafProc = proc (l: Leaf; v: Value) {.closure.}
2024-03-08 15:40:07 +00:00
ObserverProc = proc (turn: var Turn; group: ObserverGroup; vs: seq[Value]) {.closure.}
2022-03-11 20:29:46 +00:00
2023-07-20 17:31:02 +00:00
proc getLeaves(cont: Continuation; constPaths: Paths): LeafMap =
result = cont.leafMap.getOrDefault(constPaths)
if result.isNil:
new result
cont.leafMap[constPaths] = result
assert not cont.isEmpty
for ass in cont.cache:
let key = projectPaths(ass, constPaths)
if key.isSome:
var leaf = result.getOrDefault(get key)
if leaf.isNil:
new leaf
result[get key] = leaf
leaf.cache.incl(ass)
proc getLeaf(leafMap: LeafMap; constVals: seq[Value]): Leaf =
result = leafMap.getOrDefault(constVals)
if result.isNil:
new result
leafMap[constVals] = result
type
Selector = tuple[popCount: int; index: Value]
Node = ref object
continuation: Continuation
edges: Table[Selector, TableRef[Class, Node]]
func isEmpty(node: Node): bool =
node.continuation.isEmpty and node.edges.len == 0
type TermStack = seq[Value]
2022-03-11 20:29:46 +00:00
proc push(stack: TermStack; val: Value): Termstack =
result = stack
2023-07-20 17:31:02 +00:00
add(result, val)
2022-03-11 20:29:46 +00:00
proc pop(stack: TermStack; n: int): TermStack =
2023-07-20 17:31:02 +00:00
assert n <= stack.len
stack[stack.low..(stack.high-n)]
2022-03-11 20:29:46 +00:00
proc top(stack: TermStack): Value =
2023-07-20 17:31:02 +00:00
assert stack.len > 0
stack[stack.high]
2022-03-11 20:29:46 +00:00
2024-03-08 15:40:07 +00:00
proc modify(node: Node; turn: var Turn; outerValue: Value; event: EventKind;
2022-03-11 20:29:46 +00:00
modCont: ContinuationProc; modLeaf: LeafProc; modObs: ObserverProc) =
2024-03-08 15:40:07 +00:00
proc walk(cont: Continuation; turn: var Turn) =
2022-03-11 20:29:46 +00:00
modCont(cont, outerValue)
for constPaths, constValMap in cont.leafMap.pairs:
let constVals = projectPaths(outerValue, constPaths)
2023-07-20 17:31:02 +00:00
if constVals.isSome:
case event
of addedEvent, messageEvent:
let leaf = constValMap.getLeaf(get constVals)
modLeaf(leaf, outerValue)
for capturePaths, observerGroup in leaf.observerGroups.pairs:
let captures = projectPaths(outerValue, capturePaths)
if captures.isSome:
modObs(turn, observerGroup, get captures)
2023-07-24 07:55:49 +00:00
of removedEvent:
let leaf = constValMap.getOrDefault(get constVals)
if not leaf.isNil:
modLeaf(leaf, outerValue)
for capturePaths, observerGroup in leaf.observerGroups.pairs:
let captures = projectPaths(outerValue, capturePaths)
if captures.isSome:
modObs(turn, observerGroup, get captures)
if leaf.isEmpty:
constValMap.del(get constVals)
2023-07-20 17:31:02 +00:00
2024-03-08 15:40:07 +00:00
proc walk(node: Node; turn: var Turn; termStack: TermStack) =
2023-07-20 17:31:02 +00:00
walk(node.continuation, turn)
2022-03-11 20:29:46 +00:00
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:
2023-07-20 17:31:02 +00:00
walk(nextNode, turn, push(nextStack, get nextValue))
2023-07-24 07:55:49 +00:00
if event == removedEvent and nextNode.isEmpty:
table.del(nextClass)
2022-03-11 20:29:46 +00:00
2023-12-31 17:15:06 +00:00
walk(node, turn, @[@[outerValue].toPreserves])
2022-03-11 20:29:46 +00:00
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:
2023-12-31 17:15:06 +00:00
yield (i.toPreserves, p,)
of DCompoundKind.arr:
for i, p in dc.arr.items:
2023-12-31 17:15:06 +00:00
yield (i.toPreserves, 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
selector: Selector = (popCount, stepIndex,)
table = node.edges.getOrNew(selector)
2023-07-20 17:31:02 +00:00
class = classOf pat
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
2023-07-20 17:31:02 +00:00
for a in node.continuation.cache:
var v = step(a, path)
if v.isSome and class == classOf(get v):
2023-07-20 17:31:02 +00:00
result.nextNode.continuation.cache.incl a
result.popCount = 0
for step, p in pat.dcompound.pairs:
add(path, step)
result = extendWalk(result.nextNode, result.popCount, step, 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
2023-12-31 17:15:06 +00:00
extendWalk(node, 0, 0.toPreserves, pat, path).nextNode.continuation
type
Index* = object
allAssertions: Bag[Value]
root: Node
proc initIndex*(): Index =
2021-07-08 09:50:13 +00:00
Index(root: Node(continuation: Continuation()))
2023-07-20 17:31:02 +00:00
proc getEndpoints(leaf: Leaf; capturePaths: Paths): ObserverGroup =
result = leaf.observerGroups.getOrDefault(capturePaths)
if result.isNil:
new result
leaf.observerGroups[capturePaths] = result
for term in leaf.cache:
# leaf.cache would be empty if observers come before assertions
let captures = projectPaths(term, capturePaths)
if captures.isSome:
discard result.cachedCaptures.change(get captures, +1)
2024-03-08 15:40:07 +00:00
proc add*(index: var Index; turn: var Turn; pattern: Pattern; observer: Cap) =
let
2023-07-20 17:31:02 +00:00
cont = index.root.extend(pattern)
2022-03-10 23:30:07 +00:00
analysis = analyse pattern
2023-07-20 17:31:02 +00:00
constValMap = cont.getLeaves(analysis.constPaths)
leaf = constValMap.getLeaf(analysis.constValues)
endpoints = leaf.getEndpoints(analysis.capturePaths)
# TODO if endpoints.cachedCaptures.len > 0:
2022-03-10 23:30:07 +00:00
var captureMap = newTable[seq[Value], Handle]()
2023-07-20 17:31:02 +00:00
for capture in endpoints.cachedCaptures.items:
captureMap[capture] = publish(turn, observer, capture)
endpoints.observers[observer] = captureMap
2022-03-10 23:30:07 +00:00
2024-03-08 15:40:07 +00:00
proc remove*(index: var Index; turn: var Turn; pattern: Pattern; observer: Cap) =
2023-07-20 17:31:02 +00:00
let
cont = index.root.extend(pattern)
2022-03-10 23:30:07 +00:00
analysis = analyse pattern
2023-07-20 17:31:02 +00:00
constValMap = cont.leafMap.getOrDefault(analysis.constPaths)
2022-03-10 23:30:07 +00:00
if not constValMap.isNil:
let leaf = constValMap.getOrDefault(analysis.constValues)
if not leaf.isNil:
2023-07-20 17:31:02 +00:00
let endpoints = leaf.observerGroups.getOrDefault(analysis.capturePaths)
if not endpoints.isNil:
var captureMap: TableRef[seq[Value], Handle]
if endpoints.observers.pop(observer, captureMap):
for handle in captureMap.values: retract(turn, handle)
if endpoints.observers.len == 0:
2022-03-10 23:30:07 +00:00
leaf.observerGroups.del(analysis.capturePaths)
2023-07-20 17:31:02 +00:00
if leaf.observerGroups.len == 0:
constValMap.del(analysis.constValues)
if constValMap.len == 0:
cont.leafMap.del(analysis.constPaths)
2022-03-10 23:30:07 +00:00
2024-03-08 15:40:07 +00:00
proc adjustAssertion(index: var Index; turn: var Turn; outerValue: Value; delta: int): bool =
2022-03-10 23:30:07 +00:00
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) =
2023-07-20 17:31:02 +00:00
c.cache.incl(v)
2022-03-11 20:29:46 +00:00
proc modLeaf(l: Leaf; v: Value) =
2023-07-20 17:31:02 +00:00
l.cache.incl(v)
2024-03-08 15:40:07 +00:00
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
2023-07-20 17:31:02 +00:00
let change = group.cachedCaptures.change(vs, +1)
if change == cdAbsentToPresent:
2022-03-11 20:29:46 +00:00
for (observer, captureMap) in group.observers.pairs:
2023-12-31 17:15:06 +00:00
captureMap[vs] = publish(turn, observer, vs.toPreserves)
2022-03-11 20:29:46 +00:00
# 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) =
2023-07-20 17:31:02 +00:00
c.cache.excl(v)
2022-03-11 20:29:46 +00:00
proc modLeaf(l: Leaf; v: Value) =
2023-07-20 17:31:02 +00:00
l.cache.excl(v)
2024-03-08 15:40:07 +00:00
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
2022-03-11 20:29:46 +00:00
if group.cachedCaptures.change(vs, -1) == cdPresentToAbsent:
for (observer, captureMap) in group.observers.pairs:
2024-01-01 18:18:30 +00:00
var h: Handle
if captureMap.take(vs, h):
retract(observer.target, turn, h)
2022-03-11 20:29:46 +00:00
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
2024-03-08 15:40:07 +00:00
proc add*(index: var Index; turn: var Turn; v: Value): bool =
2022-03-10 23:30:07 +00:00
adjustAssertion(index, turn, v, +1)
2024-03-08 15:40:07 +00:00
proc remove*(index: var Index; turn: var Turn; v: Value): bool =
2022-03-10 23:30:07 +00:00
adjustAssertion(index, turn, v, -1)
2024-03-08 15:40:07 +00:00
proc deliverMessage*(index: var Index; turn: var Turn; v: Value) =
proc observersCb(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
2022-03-10 23:30:07 +00:00
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)