Syndicate with caps

This commit is contained in:
Emery Hemingway 2021-09-24 21:25:47 +02:00
parent 8447822243
commit 77a9ea380f
25 changed files with 1482 additions and 2044 deletions

@ -1 +1 @@
Subproject commit 1c4214537b4ed9b577bca3770baea3cb38ad0754
Subproject commit 29d7352a23be733a9892a55eb1503b3aaed5ab12

View File

@ -2,9 +2,14 @@
# SPDX-License-Identifier: Unlicense
import std/[asyncdispatch, macros, options]
import preserves, preserves/records
import preserves
import syndicate/[actors]
#[
import syndicate/[assertions, dataspaces, events, skeletons]
export preserves.fromPreserve
export assertions.Observe
export dataspaces.Facet
@ -226,3 +231,9 @@ macro `?`*(x: untyped): untyped =
quote: toPreserve(Discard())
else:
quote: toPreserve(Capture())
]#
proc startActorSystem*(name: string; bootProc: TurnAction): Future[void] =
# from python
let actor = newActor(name, bootProc)
result = actor.future

486
src/syndicate/actors.nim Normal file
View File

@ -0,0 +1,486 @@
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/[asyncdispatch, deques, hashes, monotimes, options, sets, tables, times]
import preserves, preserves/parse
import ../syndicate/protocols/[protocol, sturdy]
# proc `==`*(x, y: Handle): bool {.borrow.}
template generateIdType(T: untyped) =
type T* = distinct Natural
proc `==`*(x, y: T): bool {.borrow.}
proc `$`*(id: T): string {.borrow.}
generateIdType(ActorId)
generateIdType(FacetId)
generateIdType(EndpointId)
generateIdType(FieldId)
generateIdType(TurnId)
#[
proc genId(T: type): T =
getMonotime().ticks.T
]#
type
Attenuation = sturdy.Attenuation[Ref]
Oid = sturdy.Oid
Assertion* = protocol.Assertion[Ref]
Caveat = sturdy.Caveat[Ref]
Rewrite = sturdy.Rewrite[Ref]
Entity* = ref object of RootObj
oid*: Oid # oid is how Entities are identified over the wire
Ref* {.unpreservable.} = ref object # TODO: rename
relay*: Facet
target*: Entity
attenuation*: Attenuation
OutboundAssertion = ref object
handle: Handle
peer: Ref
established: bool
OutboundTable = Table[Handle, OutboundAssertion]
Actor = ref object
future: Future[void]
name: string
id: ActorId
handleAllocator: Handle
root: Facet
exitReason: ref Exception
exitHooks: seq[TurnAction]
exiting: bool
TurnAction* = proc (t: var Turn) {.gcsafe.}
Queues = TableRef[Facet, seq[TurnAction]]
Turn* = object # an object that should remain on the stack
id: TurnId
activeFacet*: Facet
queues: Queues # a ref object that can outlive Turn
ParentFacet = Option[Facet]
Facet* = ref FacetObj
FacetObj = object
id: FacetId
actor*: Actor
parent: ParentFacet
children: HashSet[Facet]
outbound: OutboundTable
shutdownActions: seq[TurnAction]
inertCheckPreventers: int
isAlive: bool
using
actor: Actor
facet: Facet
turn: var Turn
action: TurnAction
method publish(e: Entity; turn: var Turn; v: Assertion; h: Handle) {.base.} =
raiseAssert "Entity does not implement publish"
method retract(e: Entity; turn: var Turn; h: Handle) {.base.} =
raiseAssert "Entity does not implement retract"
method message(e: Entity; turn: var Turn; v: Assertion) {.base.} =
raiseAssert "Entity does not implement message"
method sync(e: Entity; turn: var Turn; peer: Ref) {.base.} =
raiseAssert "Entity does not implement sync"
proc labels(f: Facet): string =
proc catLabels(f: Facet; labels: var string) =
labels.add ':'
if f.parent.isSome:
catLabels(f.parent.get, labels)
labels.add ':'
labels.add $f.id
result.add f.actor.name
catLabels(f, result)
proc `$`*(f: Facet): string =
"<Facet:" & f.labels & ">"
proc `$`*(r: Ref): string =
"<Ref:" & r.relay.labels & ">"
proc `$`*(actor: Actor): string =
"<Actor:" & actor.name & ">" # TODO: ambigous
proc attenuate(r: Ref; a: Attenuation): Ref =
if a.len == 0: result = r
else: result = Ref(
relay: r.relay,
target: r.target,
attenuation: a & r.attenuation)
proc hash*(facet): Hash =
facet.id.hash
proc hash*(r: Ref): Hash =
!$(r.relay.hash !&
r.target.unsafeAddr.hash) # !&
# r.attenuation.toPreserve.hash)
# TODO: really convert for each hash?
proc nextHandle(facet: Facet): Handle =
inc facet.actor.handleAllocator
facet.actor.handleAllocator
proc enqueue(turn: var Turn; target: Facet; action: TurnAction) =
if target in turn.queues:
turn.queues[target].add action
else:
turn.queues[target] = @[action]
type Bindings = Table[Preserve[Ref], Preserve[Ref]]
proc match(p: Pattern; v: Assertion): Option[Bindings] =
proc walk(bindings: var Bindings; p: Pattern; v: Assertion): bool =
case p.orKind
of PatternKind.Pdiscard: result = true
of PatternKind.Patom:
result = case p.patom
of PAtom.Boolean: v.isBoolean
of PAtom.Float: v.isFloat
of PAtom.Double: v.isDouble
of PAtom.Signedinteger: v.isInteger
of PAtom.String: v.isString
of PAtom.Bytestring: v.isByteString
of PAtom.Symbol: v.isSymbol
of PatternKind.Pembedded:
result = v.isEmbedded
of PatternKind.Pbind:
if walk(bindings, p.pbind.pattern, v):
bindings[toPreserve(p.pbind.pattern, Ref)] = v
result = true
of PatternKind.Pand:
for pp in p.pand.patterns:
result = walk(bindings, pp, v)
if not result: break
of PatternKind.Pnot:
var b: Bindings
result = not walk(b, p.pnot.pattern, v)
of PatternKind.Lit:
result = p.lit.value == v
of PatternKind.Pcompound:
let
ctor = p.pcompound.ctor
case ctor.orKind
of ConstructorspecKind.Crec:
if v.isRecord and
ctor.crec.label == v.label and
ctor.crec.arity == v.arity:
for key, pp in p.pcompound.members:
if not key.isInteger:
result = false
else:
result = walk(bindings, pp, v.record[key.int])
if not result: break
of ConstructorspecKind.Carr:
if v.isSequence and ctor.carr.arity == v.sequence.len:
for key, pp in p.pcompound.members:
result =
if not key.isInteger: false
else: walk(bindings, pp, v.sequence[key.int])
if not result: break
of ConstructorspecKind.Cdict:
if v.isDictionary:
for key, pp in p.pcompound.members:
let vv = v[key]
result =
if vv.isFalse: false
else: walk(bindings, pp, vv)
if not result: break
var b: Bindings
if walk(b, p, v): result = some b
proc instantiate(t: Template; bindings: Bindings): Assertion =
proc walk(t: Template): Assertion =
case t.orKind
of TemplateKind.Tattenuate:
let v = walk(t.tattenuate.template)
if not v.isEmbedded:
raise newException(ValueError, "Attempt to attenuate non-capability: " & $v)
result = embed(attenuate(v.embed, t.tattenuate.attenuation))
of TemplateKind.Tref:
let n = $t.tref.binding
try: result = bindings[toPreserve(n, Ref)]
except KeyError:
raise newException(ValueError, "unbound reference: " & n)
of TemplateKind.Lit:
result = t.lit.value
of TemplateKind.Tcompound:
let ctor = t.tcompound.ctor
case ctor.orKind
of ConstructorspecKind.Crec:
result = initRecord(ctor.crec.label, ctor.crec.arity)
for key, tt in t.tcompound.members:
result.record[key.int] = walk(tt)
of ConstructorspecKind.Carr:
result = initSequence[Ref](ctor.carr.arity)
for key, tt in t.tcompound.members:
result.sequence[key.int] = walk(tt)
of ConstructorspecKind.Cdict:
result = initDictionary[Ref]()
for key, tt in t.tcompound.members:
result[key] = walk(tt)
walk(t)
proc rewrite(r: Rewrite; v: Assertion): Assertion =
let bindings = match(r.pattern, v)
if bindings.isSome:
result = instantiate(r.template, get bindings)
proc examineAlternatives(cav: Caveat; v: Assertion): Assertion =
case cav.orKind
of CaveatKind.`Rewrite`:
result = rewrite(cav.rewrite, v)
of CaveatKind.`Alts`:
for r in cav.alts.alternatives:
result = rewrite(r, v)
if not result.isFalse: break
proc runRewrites*(a: Attenuation; v: Assertion): Assertion =
result = v
for stage in a:
result = examineAlternatives(stage, result)
if result.isFalse: break
proc publish(turn: var Turn; r: Ref; v: Assertion; h: Handle) =
let a = runRewrites(r.attenuation, v)
if not a.isFalse:
let e = OutboundAssertion(
handle: h, peer: r, established: false)
turn.activeFacet.outbound[h] = e
enqueue(turn, r.relay) do (turn: var Turn):
e.established = true
publish(r.target, turn, a, e.handle)
proc publish*(turn: var Turn; r: Ref; a: Assertion): Handle =
result = turn.activeFacet.nextHandle()
publish(turn, r, a, result)
proc publish*[T](turn: var Turn; r: Ref; a: T): Handle =
publish(turn, r, toPreserve(a, Ref))
proc retract(turn: var Turn; e: OutboundAssertion) =
enqueue(turn, e.peer.relay) do (turn: var Turn):
if e.established:
e.established = false
e.peer.target.retract(turn, e.handle)
proc retract*(turn: var Turn; h: Handle) =
var e: OutboundAssertion
if turn.activeFacet.outbound.pop(h, e):
turn.retract(e)
proc message*(turn: var Turn; r: Ref; v: Assertion) =
let a = runRewrites(r.attenuation, v)
if not a.isFalse:
enqueue(turn, r.relay) do (turn: var Turn):
r.target.message(turn, a)
proc sync(turn: var Turn; e: Entity; peer: Ref) =
e.sync(turn, peer)
# or turn.message(peer, true) ?
proc sync*(turn: var Turn; r, peer: Ref) =
enqueue(turn, r.relay) do (turn: var Turn):
sync(turn, r.target, peer)
proc replace*[T](turn: var Turn; `ref`: Ref; h: Handle; v: T): Handle =
result = publish(turn, `ref`, v)
retract(turn, h)
proc stop*(turn: var Turn) {.gcsafe.}
proc run*(facet; action: TurnAction; zombieTurn = false) {.gcsafe.}
proc newFacet(actor; parent: ParentFacet; initialAssertions: OutboundTable): Facet =
result = Facet(
id: getMonoTime().ticks.FacetId,
actor: actor,
parent: parent,
outbound: initialAssertions,
isAlive: true)
if parent.isSome: parent.get.children.incl result
proc newFacet(actor; parent: ParentFacet): Facet =
var initialAssertions: OutboundTable
newFacet(actor, parent, initialAssertions)
proc onStop(facet; action) =
facet.shutdownActions.add action
proc isInert(facet): bool =
facet.inertCheckPreventers == 0 and facet.children.len == 0 and facet.outbound.len == 0
proc preventInertCheck*(facet): (proc() {.gcsafe.}) =
var armed = true
inc facet.inertCheckPreventers
proc disarm() =
if armed:
armed = false
dec facet.inertCheckPreventers
result = disarm
proc inFacet(turn: var Turn; facet; act: TurnAction) =
## Call an action with a facet using a temporary `Turn`
## that shares the `Queues` of the calling `Turn`.
var t = Turn(activeFacet: facet, queues: turn.queues)
act(t)
proc terminate(actor; turn; reason: ref Exception) {.gcsafe.}
proc terminate(facet; turn: var Turn; orderly: bool) {.gcsafe.} =
if facet.isAlive:
facet.isAlive = false
let parent = facet.parent
block:
var turn = Turn(activeFacet: facet, queues: turn.queues)
for child in facet.children:
child.terminate(turn, orderly)
if orderly:
for act in facet.shutdownActions:
act(turn)
for a in facet.outbound.values: turn.retract(a)
if orderly:
if parent.isSome:
if parent.get.isInert:
run(parent.get) do (turn: var Turn):
parent.get.terminate(turn, true) # TODO: is this the right turn?
else:
run(facet.actor.root) do (turn: var Turn):
terminate(facet.actor, turn, nil) # TODO: is this the right turn?
proc stopIfInertAfter(action: TurnAction): TurnAction =
proc wrapper(turn: var Turn) =
action(turn)
enqueue(turn, turn.activeFacet) do (turn: var Turn):
if (turn.activeFacet.parent.isSome and
(not turn.activeFacet.parent.get.isAlive)) or
turn.activeFacet.isInert:
stop(turn)
wrapper
proc facet*(turn: var Turn; bootProc: TurnAction): Facet =
result =newFacet(turn.activeFacet.actor, some turn.activeFacet)
inFacet(turn, result, stopIfInertAfter(bootProc))
proc newActor(name: string; bootProc: TurnAction; initialAssertions: OutboundTable): Actor =
let
now = getTime()
seed = now.toUnix * 1_000_000_000 + now.nanosecond
result = Actor(
name: name,
id: ActorId(seed))
result.root = newFacet(result, none Facet)
result.future = newFuture[void]($result)
run(
newFacet(result, some result.root, initialAssertions),
# stopIfInertAfter(bootProc) TODO
bootProc)
proc newActor*(name: string; bootProc: TurnAction): Actor =
var initialAssertions: OutboundTable
newActor(name, bootProc, initialAssertions)
proc spawn*(name: string; turn: var Turn; bootProc: TurnAction; initialAssertions = initHashSet[Handle]()) =
enqueue(turn, turn.activeFacet) do (turn: var Turn):
var newOutBound: Table[Handle, OutboundAssertion]
for key in initialAssertions:
discard turn.activeFacet.outbound.pop(key, newOutbound[key])
callSoon:
discard newActor(name, bootProc, newOutBound)
proc newInertRef*(): Ref =
# TODO: really create a new actor? Novy does this only once.
let a = newActor("") do (turn: var Turn): turn.stop()
Ref(relay: a.root)
proc atExit*(actor; action) = actor.exitHooks.add action
proc terminate(actor; turn; reason: ref Exception) =
if not actor.exiting:
actor.exiting = true
actor.exitReason = reason
for hook in actor.exitHooks: hook(turn)
proc finish(turn: var Turn) =
actor.root.terminate(turn, not reason.isNil)
if reason.isNil:
actor.future.complete()
else:
actor.future.fail reason
callSoon:
run(actor.root, finish, true)
proc terminate(facet; e: ref Exception) =
run(facet.actor.root) do (turn: var Turn):
facet.actor.terminate(turn, e)
proc asyncCheck*(turn; fut: FutureBase) =
let facet = turn.activeFacet
fut.addCallback do ():
if fut.failed: terminate(facet, fut.error)
template tryFacet(facet; body: untyped) =
body
# TODO
#try: body
#except: terminate(facet, getCurrentException())
proc run(queues: Queues) =
callSoon:
for facet, queue in queues:
for action in queue: run(facet, action)
proc run*(facet; action: TurnAction; zombieTurn = false) =
if not zombieTurn:
if not facet.actor.exitReason.isNil: return
if not facet.isAlive: return
# TODO: not Nim idiom
tryFacet(facet):
var turn = Turn(
activeFacet: facet,
queues: newTable[Facet, seq[TurnAction]]())
action(turn)
run(turn.queues)
proc stop*(turn: var Turn, facet: Facet) =
enqueue(turn, facet.parent.get) do (turn: var Turn):
facet.terminate(turn, true)
proc stop*(turn: var Turn) =
stop(turn, turn.activeFacet)
proc stopActor*(turn: var Turn) =
let actor = turn.activeFacet.actor
enqueue(turn, turn.activeFacet.actor.root) do (turn: var Turn):
terminate(actor, turn, nil)
proc freshen*(turn: var Turn, act: TurnAction) =
assert(turn.queues.len == 0, "Attempt to freshen a non-stale Turn")
run(turn.activeFacet, act)
proc newRef*(relay: Facet; e: Entity): Ref =
Ref(relay: relay, target: e)
proc newRef*(turn; e: Entity): Ref =
Ref(relay: turn.activeFacet, target: e)
proc sync*(turn, refer: Ref, cb: proc(t: Turn) {.gcsafe.}) =
discard # TODO
proc log*(f: Facet, args: varargs[string, `$`]) =
echo f, args
proc runActor*(name: string; bootProc: TurnAction): Future[void] =
let actor = newActor(name, bootProc)
result = actor.future

View File

@ -1,29 +0,0 @@
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/options
import preserves
type
Discard* {.record: "discard", pure.} = object
discard
Capture* {.record: "capture", pure.} = object
_: Discard
Observe* {.record: "observe", pure.} = object
pattern: Preserve
proc observe*[T](x: T): Preserve =
Observe(pattern: x.toPreserve).toPreserve
proc captureCount*(pattern: Preserve): int =
if pattern.preserveTo(Capture).isSome:
result = 1
else:
for e in pattern.items:
result.inc captureCount(e)
when isMainModule:
let a = observe(`?*`)
assert($toPreserve(a) == "<capture <discard>>")

View File

@ -1,577 +1,104 @@
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
# SPDX-License-Identifier: Unlicense
import ./bags, ./dataflow, ./events, ./skeletons
import std/[asyncdispatch, hashes, macros, options, tables]
import preserves
import std/[asyncdispatch, deques, hashes, macros, options, sets, tables]
import ../syndicate/protocols/[dataspace, dataspacePatterns]
import ./actors, ./bags
export dataflow.defineObservableProperty
export dataflow.recordObservation
export dataflow.recordDamage
template generateIdType(T: untyped) =
type T* = distinct Natural
proc `==`*(x, y: T): bool {.borrow.}
proc `$`*(id: T): string {.borrow.}
generateIdType(ActorId)
generateIdType(FacetId)
generateIdType(EndpointId)
generateIdType(FieldId)
from ../syndicate/protocols/protocol import Handle
type
Value* = Preserve
Bag = bags.Bag[Value]
Task[T] = proc (): T
Script[T] = proc (facet: Facet): T
ActivationScript* = Script[void]
ActionKind = enum
patchAction, messageAction, spawnAction, quitAction, deferredTurnAction, activationAction
Action = object
impl: proc (action: Action; ds: Dataspace; actor: Option[Actor]) {.gcsafe.}
case kind: ActionKind
of patchAction:
changes: Bag
else:
discard
Priority = enum
pQueryHigh = 0,
pQuery,
pQueryHandler,
pNormal,
pGC,
pIdle,
len
Actor = ref object
id: ActorId
name: string
dataspace*: Dataspace
rootFacet: ParentFacet
pendingTasks: array[Priority.len, Deque[Task[void]]]
pendingActions: seq[Action]
adhocAssertions: Bag
cleanupChanges: Bag
parentId: ActorId
EndpointSpec* = tuple
callback: HandlerCallback
assertion: Value
analysis: Option[Analysis]
Endpoint = ref object
id: EndpointId
facet: Facet
updateProc: Script[EndpointSpec]
spec: EndpointSpec
Field* = object of RootObj
id*: FieldId
Fields* = seq[Value]
# TODO: compile-time tuples
Turn = object
actions: seq[Action]
actor: Option[Actor]
Dataspace* = ref object
ground*: Ground
index: Index
dataflow*: Graph[Endpoint, FieldId]
runnable: seq[Actor]
pendingTurns: seq[Turn]
actors: Table[ActorId, Actor]
activations: seq[ActivationScript]
nextId: Natural
StopHandler = proc (ds: Dataspace) {.gcsafe.}
Ground = ref object
dataspace: Dataspace
stopHandlers: seq[StopHandler]
future: Future[void]
externalTaskCount: int
stepScheduled: bool
ParentFacet = Option[Facet]
Facet* = ref FacetObj
FacetObj = object
id: FacetId
actor*: Actor
parent: ParentFacet
endpoints: Table[EndpointId, Endpoint]
stopScripts: seq[Script[void]]
children: Table[FacetId, Facet]
fields*: Fields
isLive, inScript: bool
# FacetImpl[Fields] = ref FacetImplObj[Fields]
# FacetImplObj[Fields] {.final.} = object of FacetBaseObj
using
dataspace: Dataspace
actor: Actor
facet: Facet
proc hash*(ep: Endpoint): Hash =
!$(hash(ep.id) !& hash(ep.facet.id))
proc generateId*(ds: Dataspace): Natural =
# TODO: used by declareField, but should be hidden.
inc(ds.nextId)
ds.nextId
proc newActor(ds: Dataspace; name: string; initialAssertions: Value; parentId: ActorId): Actor =
assert(initialAssertions.kind == pkSet)
result = Actor(
id: ds.generateId.ActorId,
name: name,
dataspace: ds,
parentId: parentId)
for v in initialAssertions.set:
discard result.adhocAssertions.change(v, 1)
ds.actors[result.id] = result
proc applyPatch(ds: Dataspace; actor: Option[Actor]; changes: Bag) =
type Pair = tuple[val: Value; count: int]
var removals: seq[Pair]
for a, count in changes.pairs:
if count > 0:
# debugEcho "applyPatch +", a
discard ds.index.adjustAssertion(a, count)
else:
removals.add((a, count))
actor.map do (ac: Actor):
discard ac.cleanupChanges.change(a, -count)
for (a, count) in removals:
# debugEcho "applyPatch -", a
discard ds.index.adjustAssertion(a, count)
proc initPatch(): Action =
proc impl(patch: Action; ds: Dataspace; actor: Option[Actor]) {.gcsafe.} =
ds.applyPatch(actor, patch.changes)
Action(impl: impl, kind: patchAction)
proc pendingPatch(actor): var Action =
for a in actor.pendingActions.mitems:
if a.kind == patchAction: return a
actor.pendingActions.add(initPatch())
actor.pendingActions[actor.pendingActions.high]
proc adjust(patch: var Action; v: Value; delta: int) =
discard patch.changes.change(v, delta)
proc assert(actor; a: Value) = actor.pendingPatch.adjust(a, +1)
proc retract(actor; a: Value) = actor.pendingPatch.adjust(a, -1)
proc install(ep: Endpoint; spec: EndpointSpec) =
ep.spec = spec
if not ep.spec.assertion.isFalse:
ep.facet.actor.assert(ep.spec.assertion)
ep.spec.analysis.map do (a: Analysis):
assert(not ep.spec.callback.isNil)
ep.facet.actor.dataspace.index.addHandler(a, ep.spec.callback)
proc isRunnable(actor): bool =
for tasks in actor.pendingTasks:
if tasks.len > 0: return true
proc scheduleTask(actor; prio: Priority; task: Task[void]) =
if not actor.isRunnable:
actor.dataspace.runnable.add(actor)
actor.pendingTasks[prio].addLast(task)
proc scheduleTask(actor; task: Task[void]) =
scheduleTask(actor, pNormal, task)
proc abandonQueuedWork(actor) =
reset actor.pendingActions
for q in actor.pendingTasks.mitems: clear(q)
proc uninstall(ep: Endpoint; emitPatches: bool) =
if emitPatches:
if not ep.spec.assertion.isFalse:
ep.facet.actor.retract(ep.spec.assertion)
ep.spec.analysis.map do (a: Analysis):
assert(not ep.spec.callback.isNil)
ep.facet.actor.dataspace.index.removeHandler(a, ep.spec.callback)
proc destroy(ep: Endpoint; emitPatches: bool) =
ep.facet.actor.dataspace.dataflow.forgetSubject(ep)
ep.uninstall(emitPatches)
ep.facet.actor.scheduleTask(pGC) do ():
ep.facet.endpoints.del(ep.id)
# TODO: cannot remove from ep.facet.endpoints during
# its iteration, defering remove is probably unecessary
# because the facet is going down.
proc retractAssertionsAndSubscriptions(facet; emitPatches: bool) =
facet.actor.scheduleTask do ():
for ep in facet.endpoints.values:
ep.destroy(emitPatches)
clear(facet.endpoints)
proc abort(facet; emitPatches: bool) =
facet.isLive = false
for child in facet.children.values:
child.abort(emitPatches)
facet.retractAssertionsAndSubscriptions(emitPatches)
for s in facet.stopScripts: s(facet)
# call stopScripts immediately
proc enqueueScriptAction(actor; action: Action) =
actor.pendingActions.add(action)
proc enqueueScriptAction(facet; action: Action) =
enqueueScriptAction(facet.actor, action)
proc initQuitAction(): Action =
proc impl(action: Action; ds: Dataspace; actor: Option[Actor]) =
assert(actor.isSome)
ds.applyPatch(actor, actor.get.cleanupChanges)
ds.actors.del(actor.get.id)
Action(impl: impl, kind: quitAction)
proc terminate(actor; emitPatches: bool) =
if emitPatches:
actor.scheduleTask do ():
for a in actor.adhocAssertions.keys:
actor.retract(a)
actor.rootFacet.map do (root: Facet):
root.abort(emitPatches)
actor.scheduleTask do ():
actor.enqueueScriptAction(initQuitAction())
proc invokeScript(facet; script: Script[void]) =
try: script(facet)
except:
let e = getCurrentException()
# TODO: install an error handling callback at the facet?
facet.actor.abandonQueuedWork()
facet.actor.terminate(false)
raise e
func isInert(facet): bool =
facet.endpoints.len == 0 and facet.children.len == 0
proc terminate(facet) =
if facet.isLive:
let
actor = facet.actor
parent = facet.parent
if parent.isNone:
reset actor.rootFacet
facet.isLive = false
for child in facet.children.values:
child.terminate()
reset facet.children
actor.scheduleTask do ():
facet.invokeScript do (facet: Facet):
for s in facet.stopScripts:
s(facet)
facet.retractAssertionsAndSubscriptions(true)
actor.scheduleTask(pGC) do ():
if parent.isSome:
if parent.get.isInert:
parent.get.terminate()
else:
actor.terminate(true)
template withNonScriptContext(facet; body: untyped) =
let inScriptPrev = facet.inScript
facet.inScript = false
try: body
finally: facet.inScript = inScriptPrev
proc ensureFacetSetup(facet; s: string) =
assert(not facet.inScript, "Cannot " & s & " ouside facet setup")
proc ensureNonFacetSetup(facet; s: string) =
assert(facet.inScript, "Cannot " & s & " during facet setup")
proc wrap(facet; script: Script[void]): Task[void] =
proc task() = facet.invokeScript(script)
task
proc scheduleScript*(facet; prio: Priority; script: Script[void]) =
facet.actor.scheduleTask(prio, facet.wrap(script))
proc scheduleScript*(facet; script: Script[void]) =
facet.actor.scheduleTask(pNormal, facet.wrap(script))
proc addStartScript*(facet; s: Script[void]) =
facet.ensureFacetSetup("onStart")
facet.scheduleScript(pNormal, s)
proc addStopScript*(facet; s: Script[void]) =
facet.stopScripts.add(s)
proc addFacet(actor; parentFacet: Option[Facet]; bootScript: Script[void]; checkInScript = false) =
if checkInScript and parentFacet.isSome:
assert parentFacet.get.inScript
let f = Facet(
id: actor.dataspace.generateId.FacetId,
actor: actor,
parent: parentFacet,
isLive: true,
inScript: true)
if parentFacet.isSome:
parentFacet.get.children[f.id] = f
f.fields = parentFacet.get.fields
# inherit scope by copying fields of the parent
else:
actor.rootFacet = some f
f.invokeScript do (facet: Facet):
facet.withNonScriptContext:
bootScript(facet)
actor.scheduleTask do ():
if ((parentFacet.isSome) and (not parentFacet.get.isLive)) or f.isInert:
f.terminate()
proc addChildFacet*(facet; bootProc: Script[void]) =
facet.actor.addFacet(some facet, bootProc, true)
proc deliverMessage(ds: Dataspace; msg: Value; ac: Option[Actor]) =
ds.index.deliverMessage(msg)
proc adhocRetract(actor; a: Value) =
if actor.adhocAssertions.change(a, -1, true) == cdPresentToAbsent:
actor.retract(a)
proc refresh(ep: Endpoint) =
let newSpec = ep.updateProc(ep.facet)
if newSpec.assertion != ep.spec.assertion:
ep.uninstall(true)
ep.install(newSpec)
proc refreshAssertions(ds: Dataspace) =
ds.dataflow.repairDamage do (ep: Endpoint):
let facet = ep.facet
assert(facet.isLive)
facet.invokeScript do (f: Facet):
f.withNonScriptContext:
refresh(ep)
proc addActor(ds: Dataspace; name: string; bootProc: Script[void]; initialAssertions: Value; parent: Option[Actor]) =
var parentId: ActorId
parent.map do (p: Actor): parentId = p.id
let ac = newActor(ds, name, initialAssertions, parentId)
ds.applyPatch(some ac, ac.adhocAssertions)
ac.addFacet(none Facet) do (systemFacet: Facet):
# Root facet is a dummy "system" facet that exists to hold
# one-or-more "user" "root" facets.
ac.addFacet(some systemFacet, bootProc)
# ^ The "true root", user-visible facet.
for a in initialAssertions.set:
ac.adhocRetract(a)
proc send*(facet; body: Value) =
## Send a message into the dataspace.
facet.ensureNonFacetSetup("send")
proc impl(_: Action; ds: Dataspace; actor: Option[Actor]) =
ds.deliverMessage(body, actor)
facet.enqueueScriptAction(Action(impl: impl, kind: messageAction))
proc initSpawnAction(name: string; bootProc: Script[void], initialAssertions: Value): Action =
proc impl(action: Action; ds: Dataspace; actor: Option[Actor]) =
ds.addActor(name, bootProc, initialAssertions, actor)
Action(impl: impl, kind: spawnAction)
proc spawn*(facet; name: string; bootProc: Script[void], initialAssertions: Value) =
facet.ensureNonFacetSetup("spawn")
facet.enqueueScriptAction(initSpawnAction(name, bootProc, initialAssertions))
proc spawn*(facet; name: string; bootProc: Script[void]) =
spawn(facet, name, bootProc, Value(kind: pkSet))
Value = Preserve[Ref]
Pattern* = dataspacePatterns.Pattern[Ref]
Observe = dataspace.Observe[Ref]
Turn = actors.Turn
#[
template spawn*(facet; name: string; fields: untyped; bootProc: Script[void]): untyped =
type Fields = typeof(fields)
spawn[Fields](facet, name, bootProc, Value(kind: pkSet))
DataspaceEntity = ref object of Entity
assertions: Bag[Assertion]
subscriptions: Table[Assertion, TableRef[Ref, TableRef[Assertion, Handle]]]
handleMap: Table[Handle, Assertion] # breaks toPreserve(Observe[Ref]())
method publish(ds: DataspaceEntity; turn: var Turn; rec: Assertion; h: Handle) =
if rec.isRecord:
ds.handleMap[h] = rec
if ds.assertions.change(rec, +1) == cdAbsentToPresent:
var obs: Observe
if fromPreserve(obs, rec):
var seen = newTable[Assertion, Handle]()
for prev, count in ds.assertions.pairs:
if prev == rec.label:
seen[prev] = publish(turn, obs.observer.unembed, prev)
var patternSubs = ds.subscriptions.getOrDefault(rec.label)
if patternSubs.isNil:
patternSubs = newTable[Ref, TableRef[Value, Handle]]()
ds.subscriptions[rec.label] = patternSubs
patternSubs[obs.observer.unembed] = move seen
for peer, seen in ds.subscriptions[rec.label]:
if rec notin seen:
seen[rec] = publish(turn, peer, rec)
method retract(ds: DataspaceEntity; turn: var Turn; upstreamHandle: Handle) =
let rec = ds.handleMap.getOrDefault(upstreamHandle)
if rec.isRecord:
ds.handleMap.del upstreamHandle
if ds.assertions.change(rec, -1) == cdPresentToAbsent:
for peer, seen in ds.subscriptions[rec.label]:
var h: Handle
if pop(seen, rec, h): retract(turn, h)
preserveTo(rec, Observe).map do (obs: Observe):
let peerMap = ds.subscriptions[rec.label]
peerMap.del(obs.observer.unembed)
if peerMap.len == 0:
ds.subscriptions.del(rec.label)
method message(ds: DataspaceEntity; turn: var Turn; msg: Assertion) =
if msg.isRecord:
for peer, seen in ds.subscriptions[msg.label].pairs:
message(turn, peer, msg)
]#
proc initActivationAction(script: ActivationScript; name: string): Action =
proc impl(action: Action; ds: Dataspace; actor: Option[Actor]) =
for s in ds.activations:
if s == script: return
ds.activations.add(script)
proc boot(root: Facet) =
root.addStartScript(script)
ds.addActor(name, boot, Value(kind: pkSet), actor)
Action(impl: impl, kind: activationAction)
type DuringProc* = proc (turn: var Turn; a: Assertion): TurnAction {.gcsafe.}
proc activate(facet; name: string; script: ActivationScript) =
facet.ensureNonFacetSetup "`activate`"
facet.enqueueScriptAction(initActivationAction(script, name))
type
DuringActionKind = enum null, dead, act
DuringAction = object
case kind: DuringActionKind
of null, dead: discard
of act:
action: TurnAction
proc newDataspace(ground: Ground; name: string; bootProc: ActivationScript): Dataspace =
let turn = Turn(actions: @[initSpawnAction(name, bootProc, Value(kind: pkSet))])
Dataspace(ground: ground, index: initIndex(), pendingTurns: @[turn])
type DuringEntity = ref object of Entity
assertionMap: Table[Handle, DuringAction]
cb: DuringProc
proc addEndpoint*(facet; updateScript: Script[EndpointSpec], isDynamic = true) =
facet.ensureFacetSetup("addEndpoint")
let
actor = facet.actor
dataspace = actor.dataspace
ep = Endpoint(
id: dataspace.generateId.EndpointId,
facet: facet,
updateProc: updateScript)
dataspace.dataflow.addSubject(ep)
let
dyn = if isDynamic: some ep else: none Endpoint
initialSpec = dataspace.dataflow.withSubject(dyn) do () -> EndpointSpec:
updateScript(facet)
assert:
(initialSpec.analysis.isNone and initialSpec.callback.isNil) or
(initialSpec.analysis.isSome and (not initialSpec.callback.isNil))
ep.install(initialSpec)
facet.endpoints[ep.id] = ep
method publish(de: DuringEntity; turn: var Turn; a: Assertion; h: Handle) =
let action = de.cb(turn, a)
# assert(not action.isNil "should have put in a no-op action")
let g = de.assertionMap.getOrDefault h
case g.kind
of null:
de.assertionMap[h] = DuringAction(kind: act, action: action)
of dead:
de.assertionMap.del h
freshen(turn, action)
of act:
raiseAssert("during: duplicate handle in publish: " & $h)
proc addDataflow*(facet; prio: Priority; subjectProc: Script[void]) =
facet.addEndpoint do (fa: Facet) -> EndpointSpec:
let subjectId = facet.actor.dataspace.dataflow.currentSubjectId
facet.scheduleScript(prio) do (fa: Facet):
if facet.isLive:
facet.actor.dataspace.dataflow.withSubject(subjectId):
subjectProc(facet)
method retract(de: DuringEntity; turn: var Turn; h: Handle) =
let g = de.assertionMap.getOrDefault h
case g.kind
of null:
de.assertionMap[h] = DuringAction(kind: dead)
of dead:
raiseAssert("during: duplicate handle in retract: " & $h)
of act:
de.assertionMap.del h
g.action(turn)
proc addDataflow*(facet; subjectProc: Script[void]) =
addDataflow(facet, pNormal, subjectProc)
proc during*(cb: DuringProc): DuringEntity =
result = DuringEntity(cb: cb)
proc commitActions(dataspace; actor; pending: seq[Action]) =
dataspace.pendingTurns.add(Turn(actor: some actor, actions: pending))
proc runPendingTask(actor): bool =
for deque in actor.pendingTasks.mitems:
if deque.len > 0:
let task = deque.popFirst()
task()
actor.dataspace.refreshAssertions()
return true
proc runPendingTasks(actor) =
while actor.runPendingTask(): discard
if actor.pendingActions.len > 0:
var pending = move actor.pendingActions
actor.dataspace.commitActions(actor, pending)
proc runPendingTasks(ds: Dataspace) =
var runnable = move ds.runnable
for actor in runnable:
runPendingTasks(actor)
proc performPendingActions(ds: Dataspace) =
var turns = move ds.pendingTurns
for turn in turns:
for action in turn.actions:
action.impl(action, ds, turn.actor)
runPendingTasks(ds)
proc runTasks(ds: Dataspace): bool =
ds.runPendingTasks()
ds.performPendingActions()
result = ds.runnable.len > 0 or ds.pendingTurns.len > 0
proc stop*(facet; continuation: Script[void] = nil) =
facet.parent.map do (parent: Facet):
parent.invokeScript do (_: Facet):
facet.actor.scheduleTask do ():
facet.terminate()
if not continuation.isNil:
parent.scheduleScript do (parent: Facet):
continuation(parent)
# ^ TODO: is this the correct scope to use??
proc addStopHandler*(g: Ground; h: StopHandler) =
g.stopHandlers.add(h)
proc step(g: Ground) {.gcsafe.}
proc scheduleStep(g: Ground) =
if not g.stepScheduled:
g.stepScheduled = true
asyncdispatch.callSoon: step(g)
proc beginExternalTask*(facet) =
## Inform the ``Ground`` dataspace of a pending external task.
## The dataspace will continue to operate until all internal
## and external tasks have completed. See ``endExternalTask``.
inc facet.actor.dataspace.ground.externalTaskCount
proc endExternalTask*(facet) =
## Inform the ``Ground`` dataspace that an external task has completed.
# TODO: automatically do this when the facet stops?
let g = facet.actor.dataspace.ground
dec g.externalTaskCount
scheduleStep g
proc step(g: Ground) =
# TODO: backgroundtasks
g.stepScheduled = false
if g.dataspace.runTasks():
scheduleStep g
else:
if g.externalTaskCount < 1:
for actor in g.dataspace.actors.values:
terminate(actor, false)
for sh in g.stopHandlers:
sh(g.dataspace)
reset g.stopHandlers
complete(g.future)
proc bootModule*(name: string; bootProc: ActivationScript): Future[void] =
# TODO: better integration with the async dispatcher
let g = Ground(future: newFuture[void]"bootModule")
g.dataspace = newDataspace(g, name) do (rootFacet: Facet):
rootFacet.addStartScript do (rootFacet: Facet):
rootFacet.activate(name, bootProc)
addTimer(1, true) do (fd: AsyncFD) -> bool:
step(g)
true
return g.future
template declareField*(facet: Facet; F: untyped; T: typedesc; initial: T): untyped =
## Declare getter and setter procs for field `F` of type `T` initalized with `initial`.
type DistinctField {.final, unpreservable.} = object of Field
discard
let `F` {.inject.} = DistinctField(id: facet.actor.dataspace.generateId.FieldId)
facet.actor.dataspace.dataflow.defineObservableProperty(`F`.id)
facet.fields.add(toPreserve(initial))
let fieldOff = facet.fields.high
proc set(f: DistinctField; x: T) {.used.} =
facet.actor.dataspace.dataflow.recordDamage(f.id)
facet.fields[fieldOff] = toPreserve(x)
proc set(f: DistinctField; x: Value) {.used.} =
facet.actor.dataspace.dataflow.recordDamage(f.id)
facet.fields[fieldOff] = x
proc get(f: DistinctField): T {.used.} =
facet.actor.dataspace.dataflow.recordObservation(f.id)
if not fromPreserve(result, facet.fields[fieldOff]):
raise newException(ValueError, "cannot convert field " & $F & " to " & $T)
proc getPreserve(f: DistinctField): Value {.used.} =
facet.actor.dataspace.dataflow.recordObservation(f.id)
facet.fields[fieldOff]
template stopIf*(facet: Facet; cond: untyped; continuation: Script[void]): untyped =
## Stop the current facet if `cond` is true and
## invoke `body` after the facet has stopped.
discard facet.addDataflow do (facet: Facet):
if cond: facet.stop(continuation)
type EventHandler* = proc (facet: Facet; bindings: seq[Value]) {.gcsafe.}
proc wrap*(facet: Facet; onEvent: EventKind; cb: EventHandler): HandlerCallback =
proc wrapper(event: EventKind; bindings: seq[Value]) =
facet.invokeScript do (facet: Facet):
if event == onEvent:
facet.scheduleScript do (facet: Facet):
cb(facet, bindings)
wrapper
proc observe*(turn: var Turn; ds: Ref; pat: Pattern; e: Entity): Handle =
publish(turn, ds, Observe(pattern: pat, observer: embed newRef(turn, e)))

View File

@ -4,32 +4,19 @@
import std/[asyncdispatch, monotimes, times]
import preserves, preserves/records
import syndicate, syndicate/assertions
type TimeLaterThan* {.record: "TimeLaterThan".} = object
`deadline`*: Monotime
proc prsTimeLaterThan*(deadline: Preserve | Monotime): Preserve =
initRecord(symbol("TimeLaterThan"), deadline)
proc toPreserveHook*(time: Monotime): Preserve =
time.ticks.toPreserve
proc fromPreserveHook*(mt: var Monotime; p: Preserve): bool =
if p.kind == pkSignedInteger:
mt = cast[MonoTime]((p.int.int64,))
result = true
import ../../syndicate/protocols/schemas/timer
syndicate timerDriver:
spawn "timer":
during(observe(prsTimeLaterThan(?deadline))) do (deadline: MonoTime):
during(observe(laterThan(?msecs))) do (msecs: float64):
let
now = getMonoTime()
period = inMilliseconds(deadline - now)
now = getTime().toUnixFloat() * 1_000.0
period = msecs - now
if period > 0:
getCurrentFacet().beginExternalTask()
addTimer(period.int, oneshot = true) do (fd: AsyncFD) -> bool:
react: publish: prsTimeLaterThan(deadline)
react: publish: laterThan(deadline)
getCurrentFacet().endExternalTask()
true
else:

View File

@ -1,7 +1,25 @@
preserves_schema_nim = ../../../preserves/preserves_schema_nim
include_rules
!preserves_schema_nim = | $(preserves_schema_nim) |> $(preserves_schema_nim) %f |> %B.nim
: ../../../preserves/src/preserves/preserves_schema_nim.nim |> !nim |> preserves_schema_nim
: foreach ../../../protocols/schemas/*.prs |> !preserves_schema_nim |> {gen}
!preserves_schema_nim = | ./preserves_schema_nim |> ./preserves_schema_nim %f |> %B.nim
: foreach {gen} | {gen} |> nim check %f; touch %o |> .%B.nim.check {gen}
schemas = \
dataspace.nim \
dataspacePatterns.nim \
gatekeeper.nim \
protocol.nim \
racketEvent.nim \
secureChatProtocol.nim \
service.nim \
simpleChatProtocol.nim \
stream.nim \
sturdy.nim \
tcp.nim \
timer.nim \
transportAddress.nim \
worker.nim \
: ../../../protocols/schema-bundle.bin |> !preserves_schema_nim |> $(schemas)
: foreach $(schemas) | $(schemas) |> nim check %f; touch %o |> .%B.nim.check

View File

@ -3,22 +3,12 @@ import
std/typetraits, preserves, dataspacePatterns
type
Observe*[E = void] {.record: "Observe".} = ref object ## ``<Observe @pattern dataspacePatterns.Pattern @observer #!any>``
`pattern`*: dataspacePatterns.Pattern
Observe*[E] {.preservesRecord: "Observe".} = ref object
`pattern`*: dataspacePatterns.Pattern[E]
`observer`*: Preserve[E]
proc `observe`*[E = void](`pattern`: dataspacePatterns.Pattern | Preserve[E];
`observer`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``Observe``.
initRecord[E](symbol[E]("Observe"), toPreserve(`pattern`, E),
toPreserve(`observer`, E))
proc toPreserveHook*(`observe`: Observe; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("Observe"), toPreserve(`observe`.`pattern`, E),
toPreserve(`observe`.`observer`, E))
proc `$`*[E](x: Observe[E]): string =
`$`(toPreserve(x, E))
proc `encode`*[E](x: Observe[E]): seq[byte] =
proc encode*[E](x: Observe[E]): seq[byte] =
encode(toPreserve(x, E))

View File

@ -3,164 +3,75 @@ import
std/typetraits, preserves, std/tables, std/tables, std/tables
type
PatternKind* {.pure.} = enum
`Ddiscard`, `Dbind`, `Dlit`, `Dcompound`
Pattern*[E = void] = ref object ## ``/ DDiscard / DBind / DLit / DCompound``
case kind*: PatternKind
of PatternKind.`Ddiscard`:
`ddiscard`*: DDiscard
of PatternKind.`Dbind`:
`dbind`*: DBind[E]
of PatternKind.`Dlit`:
`dlit`*: DLit[E]
of PatternKind.`Dcompound`:
`dcompound`*: DCompound[E]
DDiscard* {.record: "_".} = object ## ``<_>``
discard
DBind*[E = void] {.record: "bind".} = ref object ## ``<bind @pattern Pattern>``
`pattern`*: Pattern[E]
DLit*[E = void] {.record: "lit".} = ref object ## ``<lit @value any>``
`value`*: Preserve[E]
DcompoundKind* {.pure.} = enum
`rec`, `arr`, `dict`
DCompoundrec*[E = void] {.record: "compound".} = ref object
`ctor`*: CRec[E]
`members`*: TableRef[BiggestInt, Pattern[E]]
DCompoundarr*[E = void] {.record: "compound".} = ref object
`ctor`*: CArr
`members`*: TableRef[BiggestInt, Pattern[E]]
DCompounddict*[E = void] {.record: "compound".} = ref object
`ctor`*: CDict
`members`*: TableRef[Preserve[E], Pattern[E]]
DCompound*[E = void] = ref object ## ``/ <compound @ctor CRec @members {int : Pattern ...:...}> / <compound @ctor CArr @members {int : Pattern ...:...}> / <compound @ctor CDict @members {any : Pattern ...:...}>``
case kind*: DcompoundKind
of DcompoundKind.`rec`:
`rec`*: DCompoundrec[E]
of DcompoundKind.`arr`:
`arr`*: DCompoundarr[E]
of DcompoundKind.`dict`:
`dict`*: DCompounddict[E]
CRec*[E = void] {.record: "rec".} = ref object ## ``<rec @label any @arity int>``
CRec*[E] {.preservesRecord: "rec".} = ref object
`label`*: Preserve[E]
`arity`*: BiggestInt
CArr* {.record: "arr".} = ref object ## ``<arr @arity int>``
DLit*[E] {.preservesRecord: "lit".} = ref object
`value`*: Preserve[E]
DBind*[E] {.preservesRecord: "bind".} = ref object
`pattern`*: Pattern[E]
DDiscard* {.preservesRecord: "_".} = object
CArr* {.preservesRecord: "arr".} = object
`arity`*: BiggestInt
CDict* {.record: "dict".} = object ## ``<dict>``
discard
DCompoundKind* {.pure.} = enum
`rec`, `arr`, `dict`
DCompoundRec*[E] {.preservesRecord: "compound".} = ref object
`ctor`*: CRec[E]
`members`*: Table[BiggestInt, Pattern[E]]
proc toPreserveHook*(v: Pattern; E: typedesc): Preserve[E] =
case v.kind
of PatternKind.`Ddiscard`:
toPreserve(v.`ddiscard`, E)
of PatternKind.`Dbind`:
toPreserve(v.`dbind`, E)
of PatternKind.`Dlit`:
toPreserve(v.`dlit`, E)
of PatternKind.`Dcompound`:
toPreserve(v.`dcompound`, E)
DCompoundArr*[E] {.preservesRecord: "compound".} = ref object
`ctor`*: CArr
`members`*: Table[BiggestInt, Pattern[E]]
proc fromPreserveHook*[E](v: var Pattern; pr: Preserve[E]): bool =
if isRecord(pr) and pr.label.isSymbol("DDiscard"):
v = Pattern(kind: PatternKind.`Ddiscard`)
result = fromPreserve(v.`ddiscard`, pr)
elif isRecord(pr) and pr.label.isSymbol("DBind"):
v = Pattern(kind: PatternKind.`Dbind`)
result = fromPreserve(v.`dbind`, pr)
elif isRecord(pr) and pr.label.isSymbol("DLit"):
v = Pattern(kind: PatternKind.`Dlit`)
result = fromPreserve(v.`dlit`, pr)
elif false: ## snkOr - / <compound @ctor CRec @members {int : Pattern ...:...}> / <compound @ctor CArr @members {int : Pattern ...:...}> / <compound @ctor CDict @members {any : Pattern ...:...}>
discard
DCompoundDict*[E] {.preservesRecord: "compound".} = ref object
`ctor`*: CDict
`members`*: Table[Preserve[E], Pattern[E]]
proc `dDiscard`*[E = void](): Preserve[E] =
## Preserves constructor for ``DDiscard``.
initRecord[E](symbol[E]("_"))
`DCompound`*[E] {.preservesOr.} = ref object
case orKind*: DCompoundKind
of DCompoundKind.`rec`:
`rec`*: DCompoundRec[E]
proc toPreserveHook*(`ddiscard`: DDiscard; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("_"))
of DCompoundKind.`arr`:
`arr`*: DCompoundArr[E]
proc `dBind`*[E = void](`pattern`: Pattern | Preserve[E]): Preserve[E] =
## Preserves constructor for ``DBind``.
initRecord[E](symbol[E]("bind"), toPreserve(`pattern`, E))
of DCompoundKind.`dict`:
`dict`*: DCompoundDict[E]
proc toPreserveHook*(`dbind`: DBind; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("bind"), toPreserve(`dbind`.`pattern`, E))
CDict* {.preservesRecord: "dict".} = object
PatternKind* {.pure.} = enum
`DDiscard`, `DBind`, `DLit`, `DCompound`
`Pattern`*[E] {.preservesOr.} = ref object
case orKind*: PatternKind
of PatternKind.`DDiscard`:
`ddiscard`*: DDiscard
proc `dLit`*[E = void](`value`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``DLit``.
initRecord[E](symbol[E]("lit"), toPreserve(`value`, E))
of PatternKind.`DBind`:
`dbind`*: DBind[E]
proc toPreserveHook*(`dlit`: DLit; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("lit"), toPreserve(`dlit`.`value`, E))
of PatternKind.`DLit`:
`dlit`*: DLit[E]
proc toPreserveHook*(v: DCompound; E: typedesc): Preserve[E] =
case v.kind
of DCompoundKind.`rec`:
toPreserve(v.`rec`, E)
of DCompoundKind.`arr`:
toPreserve(v.`arr`, E)
of DCompoundKind.`dict`:
toPreserve(v.`dict`, E)
of PatternKind.`DCompound`:
`dcompound`*: DCompound[E]
proc fromPreserveHook*[E](v: var DCompound; pr: Preserve[E]): bool =
if isRecord(pr) and pr.label.isSymbol("rec"):
v = DCompound(kind: DCompoundKind.`rec`)
result = fromPreserve(v.`rec`, pr)
elif isRecord(pr) and pr.label.isSymbol("arr"):
v = DCompound(kind: DCompoundKind.`arr`)
result = fromPreserve(v.`arr`, pr)
elif isRecord(pr) and pr.label.isSymbol("dict"):
v = DCompound(kind: DCompoundKind.`dict`)
result = fromPreserve(v.`dict`, pr)
proc `cRec`*[E = void](`label`: Preserve[E]; `arity`: BiggestInt | Preserve[E]): Preserve[
E] =
## Preserves constructor for ``CRec``.
initRecord[E](symbol[E]("rec"), toPreserve(`label`, E), toPreserve(`arity`, E))
proc toPreserveHook*(`crec`: CRec; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("rec"), toPreserve(`crec`.`label`, E),
toPreserve(`crec`.`arity`, E))
proc `cArr`*[E = void](`arity`: BiggestInt | Preserve[E]): Preserve[E] =
## Preserves constructor for ``CArr``.
initRecord[E](symbol[E]("arr"), toPreserve(`arity`, E))
proc toPreserveHook*(`carr`: CArr; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("arr"), toPreserve(`carr`.`arity`, E))
proc `cDict`*[E = void](): Preserve[E] =
## Preserves constructor for ``CDict``.
initRecord[E](symbol[E]("dict"))
proc toPreserveHook*(`cdict`: CDict; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("dict"))
proc `$`*[E](x: Pattern[E] | DDiscard | DBind[E] | DLit[E] | DCompound[E] |
CRec[E] |
CArr |
CDict): string =
proc `$`*[E](x: CRec[E] | DLit[E] | DBind[E] | DCompound[E] | Pattern[E]): string =
`$`(toPreserve(x, E))
proc `encode`*[E](x: Pattern[E] | DDiscard | DBind[E] | DLit[E] | DCompound[E] |
CRec[E] |
CArr |
CDict): seq[byte] =
proc encode*[E](x: CRec[E] | DLit[E] | DBind[E] | DCompound[E] | Pattern[E]): seq[
byte] =
encode(toPreserve(x, E))
proc `$`*(x: DDiscard | CArr | CDict): string =
`$`(toPreserve(x))
proc encode*(x: DDiscard | CArr | CDict): seq[byte] =
encode(toPreserve(x))

View File

@ -3,37 +3,17 @@ import
std/typetraits, preserves, sturdy
type
Resolve*[E = void] {.record: "resolve".} = ref object ## ``<resolve @sturdyref sturdy.SturdyRef @observer #!#!any>``
`sturdyref`*: sturdy.SturdyRef
`observer`*: Preserve[E]
Bind*[E = void] {.record: "bind".} = ref object ## ``<bind @oid any @key bytes @target #!any>``
Bind*[E] {.preservesRecord: "bind".} = ref object
`oid`*: Preserve[E]
`key`*: seq[byte]
`target`*: Preserve[E]
proc `resolve`*[E = void](`sturdyref`: sturdy.SturdyRef | Preserve[E];
`observer`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``Resolve``.
initRecord[E](symbol[E]("resolve"), toPreserve(`sturdyref`, E),
toPreserve(`observer`, E))
Resolve*[E] {.preservesRecord: "resolve".} = ref object
`sturdyref`*: sturdy.SturdyRef[E]
`observer`*: Preserve[E]
proc toPreserveHook*(`resolve`: Resolve; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("resolve"), toPreserve(`resolve`.`sturdyref`, E),
toPreserve(`resolve`.`observer`, E))
proc `bind`*[E = void](`oid`: Preserve[E]; `key`: seq[byte] | Preserve[E];
`target`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``Bind``.
initRecord[E](symbol[E]("bind"), toPreserve(`oid`, E), toPreserve(`key`, E),
toPreserve(`target`, E))
proc toPreserveHook*(`bind`: Bind; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("bind"), toPreserve(`bind`.`oid`, E),
toPreserve(`bind`.`key`, E), toPreserve(`bind`.`target`, E))
proc `$`*[E](x: Resolve[E] | Bind[E]): string =
proc `$`*[E](x: Bind[E] | Resolve[E]): string =
`$`(toPreserve(x, E))
proc `encode`*[E](x: Resolve[E] | Bind[E]): seq[byte] =
proc encode*[E](x: Bind[E] | Resolve[E]): seq[byte] =
encode(toPreserve(x, E))

Binary file not shown.

View File

@ -3,10 +3,35 @@ import
std/typetraits, preserves
type
Error*[E] {.preservesRecord: "error".} = ref object
`message`*: string
`detail`*: Preserve[E]
Turn*[E] = seq[TurnEvent[E]]
Message*[E] {.preservesRecord: "message".} = ref object
`body`*: Assertion[E]
Retract* {.preservesRecord: "retract".} = object
`handle`*: Handle
Assert*[E] {.preservesRecord: "assert".} = ref object
`assertion`*: Assertion[E]
`handle`*: Handle
Sync*[E] {.preservesRecord: "sync".} = ref object
`peer`*: Preserve[E]
TurnEvent*[E] {.preservesTuple.} = ref object
`oid`*: Oid
`event`*: Event[E]
Oid* = BiggestInt
Assertion*[E] = Preserve[E]
Handle* = BiggestInt
PacketKind* {.pure.} = enum
`Turn`, `Error`
Packet*[E = void] = ref object ## ``/ Turn / Error``
case kind*: PacketKind
`Packet`*[E] {.preservesOr.} = ref object
case orKind*: PacketKind
of PacketKind.`Turn`:
`turn`*: Turn[E]
@ -14,16 +39,10 @@ type
`error`*: Error[E]
Error*[E = void] {.record: "error".} = ref object ## ``<error @message string @detail any>``
`message`*: string
`detail`*: Preserve[E]
Assertion*[E = void] = Preserve[E] ## ``any``
Handle* = distinct BiggestInt ## ``int``
EventKind* {.pure.} = enum
`Assert`, `Retract`, `Message`, `Sync`
Event*[E = void] = ref object ## ``/ Assert / Retract / Message / Sync``
case kind*: EventKind
`Event`*[E] {.preservesOr.} = ref object
case orKind*: EventKind
of EventKind.`Assert`:
`assert`*: Assert[E]
@ -34,124 +53,23 @@ type
`message`*: Message[E]
of EventKind.`Sync`:
`sync`*: Sync
`sync`*: Sync[E]
Oid* = distinct BiggestInt ## ``int``
Turn*[E = void] = seq[TurnEvent[E]] ## ``[ TurnEvent ... ]``
TurnEvent*[E = void] = tuple[`oid`: Oid, `event`: Event[E]] ## ``[@oid Oid @event Event]``
Assert*[E = void] {.record: "assert".} = ref object ## ``<assert @assertion Assertion @handle Handle>``
`assertion`*: Assertion[E]
`handle`*: Handle
Retract* {.record: "retract".} = ref object ## ``<retract @handle Handle>``
`handle`*: Handle
Message*[E = void] {.record: "message".} = ref object ## ``<message @body Assertion>``
`body`*: Assertion[E]
Sync* {.record: "sync".} = ref object ## ``<sync @peer #!#t>``
`peer`*: bool
proc toPreserveHook*(v: Packet; E: typedesc): Preserve[E] =
case v.kind
of PacketKind.`Turn`:
toPreserve(v.`turn`, E)
of PacketKind.`Error`:
toPreserve(v.`error`, E)
proc fromPreserveHook*[E](v: var Packet; pr: Preserve[E]): bool =
if false:
discard
elif isRecord(pr) and pr.label.isSymbol("Error"):
v = Packet(kind: PacketKind.`Error`)
result = fromPreserve(v.`error`, pr)
proc `error`*[E = void](`message`: string | Preserve[E]; `detail`: Preserve[E]): Preserve[
E] =
## Preserves constructor for ``Error``.
initRecord[E](symbol[E]("error"), toPreserve(`message`, E),
toPreserve(`detail`, E))
proc toPreserveHook*(`error`: Error; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("error"), toPreserve(`error`.`message`, E),
toPreserve(`error`.`detail`, E))
proc toPreserveHook*(v: Event; E: typedesc): Preserve[E] =
case v.kind
of EventKind.`Assert`:
toPreserve(v.`assert`, E)
of EventKind.`Retract`:
toPreserve(v.`retract`, E)
of EventKind.`Message`:
toPreserve(v.`message`, E)
of EventKind.`Sync`:
toPreserve(v.`sync`, E)
proc fromPreserveHook*[E](v: var Event; pr: Preserve[E]): bool =
if isRecord(pr) and pr.label.isSymbol("Assert"):
v = Event(kind: EventKind.`Assert`)
result = fromPreserve(v.`assert`, pr)
elif isRecord(pr) and pr.label.isSymbol("Retract"):
v = Event(kind: EventKind.`Retract`)
result = fromPreserve(v.`retract`, pr)
elif isRecord(pr) and pr.label.isSymbol("Message"):
v = Event(kind: EventKind.`Message`)
result = fromPreserve(v.`message`, pr)
elif isRecord(pr) and pr.label.isSymbol("Sync"):
v = Event(kind: EventKind.`Sync`)
result = fromPreserve(v.`sync`, pr)
proc toPreserveHook*(`turnevent`: TurnEvent; E: typedesc): Preserve[E] =
Preserve[E](kind: pkSequence, sequence: @[toPreserve(`turnevent`.`oid`, E),
toPreserve(`turnevent`.`event`, E)])
proc `assert`*[E = void](`assertion`: Assertion | Preserve[E];
`handle`: Handle | Preserve[E]): Preserve[E] =
## Preserves constructor for ``Assert``.
initRecord[E](symbol[E]("assert"), toPreserve(`assertion`, E),
toPreserve(`handle`, E))
proc toPreserveHook*(`assert`: Assert; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("assert"), toPreserve(`assert`.`assertion`, E),
toPreserve(`assert`.`handle`, E))
proc `retract`*[E = void](`handle`: Handle | Preserve[E]): Preserve[E] =
## Preserves constructor for ``Retract``.
initRecord[E](symbol[E]("retract"), toPreserve(`handle`, E))
proc toPreserveHook*(`retract`: Retract; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("retract"), toPreserve(`retract`.`handle`, E))
proc `message`*[E = void](`body`: Assertion | Preserve[E]): Preserve[E] =
## Preserves constructor for ``Message``.
initRecord[E](symbol[E]("message"), toPreserve(`body`, E))
proc toPreserveHook*(`message`: Message; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("message"), toPreserve(`message`.`body`, E))
proc `sync`*[E = void](`peer`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``Sync``.
initRecord[E](symbol[E]("sync"), toPreserve(`peer`, E))
proc toPreserveHook*(`sync`: Sync; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("sync"), toPreserve(`sync`.`peer`, E))
proc `$`*[E](x: Packet[E] | Error[E] | Assertion[E] | Handle | Event[E] | Oid |
Turn[E] |
proc `$`*[E](x: Error[E] | Turn[E] | Message[E] | Assert[E] | Sync[E] |
TurnEvent[E] |
Assert[E] |
Retract |
Message[E] |
Sync): string =
Packet[E] |
Event[E]): string =
`$`(toPreserve(x, E))
proc `encode`*[E](x: Packet[E] | Error[E] | Assertion[E] | Handle | Event[E] |
Oid |
Turn[E] |
proc encode*[E](x: Error[E] | Turn[E] | Message[E] | Assert[E] | Sync[E] |
TurnEvent[E] |
Assert[E] |
Retract |
Message[E] |
Sync): seq[byte] =
Packet[E] |
Event[E]): seq[byte] =
encode(toPreserve(x, E))
proc `$`*(x: Retract | Oid | Handle): string =
`$`(toPreserve(x))
proc encode*(x: Retract | Oid | Handle): seq[byte] =
encode(toPreserve(x))

View File

@ -3,23 +3,12 @@ import
std/typetraits, preserves
type
RacketEvent*[E = void] {.record: "racket-event".} = ref object ## ``<racket-event @source #!any @event #!any>``
RacketEvent*[E] {.preservesRecord: "racket-event".} = ref object
`source`*: Preserve[E]
`event`*: Preserve[E]
proc `racketEvent`*[E = void](`source`: Preserve[E]; `event`: Preserve[E]): Preserve[
E] =
## Preserves constructor for ``RacketEvent``.
initRecord[E](symbol[E]("racket-event"), toPreserve(`source`, E),
toPreserve(`event`, E))
proc toPreserveHook*(`racketevent`: RacketEvent; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("racket-event"),
toPreserve(`racketevent`.`source`, E),
toPreserve(`racketevent`.`event`, E))
proc `$`*[E](x: RacketEvent[E]): string =
`$`(toPreserve(x, E))
proc `encode`*[E](x: RacketEvent[E]): seq[byte] =
proc encode*[E](x: RacketEvent[E]): seq[byte] =
encode(toPreserve(x, E))

View File

@ -3,159 +3,71 @@ import
std/typetraits, preserves
type
UserId* = distinct BiggestInt ## ``int``
Join* {.record: "joinedUser".} = ref object ## ``<joinedUser @uid UserId @handle #!Session>``
UserId* = BiggestInt
NickConflict* {.preservesRecord: "nickConflict".} = object
NickClaimResponseKind* {.pure.} = enum
`true`, `NickConflict`
`NickClaimResponse`* {.preservesOr.} = object
case orKind*: NickClaimResponseKind
of NickClaimResponseKind.`true`:
`true`* {.preservesLiteral: "#t".}: bool
of NickClaimResponseKind.`NickConflict`:
`nickconflict`*: NickConflict
Join*[E] {.preservesRecord: "joinedUser".} = ref object
`uid`*: UserId
`handle`*: Session
`handle`*: Preserve[E]
SessionKind* {.pure.} = enum
`observeusers`, `observespeech`, `Nickclaim`, `Says`
Sessionobserveusers* {.record: "Observe".} = ref object
`user`*: string
`observer`*: UserInfo
`observeUsers`, `observeSpeech`, `NickClaim`, `Says`
SessionObserveUsers*[E] {.preservesRecord: "Observe".} = ref object
`data`* {.preservesLiteral: "user".}: bool
`observer`*: Preserve[E]
Sessionobservespeech* {.record: "Observe".} = ref object
`says`*: string
`observer`*: Says
SessionObserveSpeech*[E] {.preservesRecord: "Observe".} = ref object
`data`* {.preservesLiteral: "says".}: bool
`observer`*: Preserve[E]
Session* = ref object ## ``/ <Observe <<lit>user> @observer #!UserInfo> / <Observe <<lit>says> @observer #!Says> / NickClaim / Says``
case kind*: SessionKind
of SessionKind.`observeusers`:
`observeusers`*: Sessionobserveusers
`Session`*[E] {.preservesOr.} = ref object
case orKind*: SessionKind
of SessionKind.`observeUsers`:
`observeusers`*: SessionObserveUsers[E]
of SessionKind.`observespeech`:
`observespeech`*: Sessionobservespeech
of SessionKind.`observeSpeech`:
`observespeech`*: SessionObserveSpeech[E]
of SessionKind.`Nickclaim`:
`nickclaim`*: NickClaim
of SessionKind.`NickClaim`:
`nickclaim`*: NickClaim[E]
of SessionKind.`Says`:
`says`*: Says
NickClaim* {.record: "claimNick".} = ref object ## ``<claimNick @uid UserId @name string @k #!NickClaimResponse>``
`uid`*: UserId
`name`*: string
`k`*: NickClaimResponse
NickclaimresponseKind* {.pure.} = enum
`true`, `Nickconflict`
NickClaimResponsetrue* = bool
NickClaimResponse* = ref object ## ``/ =#t / NickConflict``
case kind*: NickclaimresponseKind
of NickclaimresponseKind.`true`:
discard
of NickclaimresponseKind.`Nickconflict`:
`nickconflict`*: NickConflict
UserInfo* {.record: "user".} = ref object ## ``<user @uid UserId @name string>``
UserInfo* {.preservesRecord: "user".} = object
`uid`*: UserId
`name`*: string
Says* {.record: "says".} = ref object ## ``<says @who UserId @what string>``
NickClaim*[E] {.preservesRecord: "claimNick".} = ref object
`uid`*: UserId
`name`*: string
`k`*: Preserve[E]
Says* {.preservesRecord: "says".} = object
`who`*: UserId
`what`*: string
NickConflict* {.record: "nickConflict".} = object ## ``<nickConflict>``
discard
proc `join`*[E = void](`uid`: UserId | Preserve[E]; `handle`: Preserve[E]): Preserve[
E] =
## Preserves constructor for ``Join``.
initRecord[E](symbol[E]("joinedUser"), toPreserve(`uid`, E),
toPreserve(`handle`, E))
proc toPreserveHook*(`join`: Join; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("joinedUser"), toPreserve(`join`.`uid`, E),
toPreserve(`join`.`handle`, E))
proc toPreserveHook*(v: Session; E: typedesc): Preserve[E] =
case v.kind
of SessionKind.`observeusers`:
toPreserve(v.`observeusers`, E)
of SessionKind.`observespeech`:
toPreserve(v.`observespeech`, E)
of SessionKind.`Nickclaim`:
toPreserve(v.`nickclaim`, E)
of SessionKind.`Says`:
toPreserve(v.`says`, E)
proc fromPreserveHook*[E](v: var Session; pr: Preserve[E]): bool =
if isRecord(pr) and pr.label.isSymbol("observeUsers"):
v = Session(kind: SessionKind.`observeusers`)
result = fromPreserve(v.`observeusers`, pr)
elif isRecord(pr) and pr.label.isSymbol("observeSpeech"):
v = Session(kind: SessionKind.`observespeech`)
result = fromPreserve(v.`observespeech`, pr)
elif isRecord(pr) and pr.label.isSymbol("NickClaim"):
v = Session(kind: SessionKind.`Nickclaim`)
result = fromPreserve(v.`nickclaim`, pr)
elif isRecord(pr) and pr.label.isSymbol("Says"):
v = Session(kind: SessionKind.`Says`)
result = fromPreserve(v.`says`, pr)
proc `nickClaim`*[E = void](`uid`: UserId | Preserve[E];
`name`: string | Preserve[E]; `k`: Preserve[E]): Preserve[
E] =
## Preserves constructor for ``NickClaim``.
initRecord[E](symbol[E]("claimNick"), toPreserve(`uid`, E),
toPreserve(`name`, E), toPreserve(`k`, E))
proc toPreserveHook*(`nickclaim`: NickClaim; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("claimNick"), toPreserve(`nickclaim`.`uid`, E),
toPreserve(`nickclaim`.`name`, E),
toPreserve(`nickclaim`.`k`, E))
proc toPreserveHook*(v: NickClaimResponse; E: typedesc): Preserve[E] =
case v.kind
of NickClaimResponseKind.`true`:
Preserve[E](kind: pkBoolean, bool: true)
of NickClaimResponseKind.`Nickconflict`:
toPreserve(v.`nickconflict`, E)
proc fromPreserveHook*[E](v: var NickClaimResponse; pr: Preserve[E]): bool =
if pr.kind == pkBoolean and pr.bool == true:
v = NickClaimResponse(kind: NickClaimResponseKind.`true`)
result = true
elif isRecord(pr) and pr.label.isSymbol("NickConflict"):
v = NickClaimResponse(kind: NickClaimResponseKind.`Nickconflict`)
result = fromPreserve(v.`nickconflict`, pr)
proc `userInfo`*[E = void](`uid`: UserId | Preserve[E];
`name`: string | Preserve[E]): Preserve[E] =
## Preserves constructor for ``UserInfo``.
initRecord[E](symbol[E]("user"), toPreserve(`uid`, E), toPreserve(`name`, E))
proc toPreserveHook*(`userinfo`: UserInfo; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("user"), toPreserve(`userinfo`.`uid`, E),
toPreserve(`userinfo`.`name`, E))
proc `says`*[E = void](`who`: UserId | Preserve[E]; `what`: string | Preserve[E]): Preserve[
E] =
## Preserves constructor for ``Says``.
initRecord[E](symbol[E]("says"), toPreserve(`who`, E), toPreserve(`what`, E))
proc toPreserveHook*(`says`: Says; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("says"), toPreserve(`says`.`who`, E),
toPreserve(`says`.`what`, E))
proc `nickConflict`*[E = void](): Preserve[E] =
## Preserves constructor for ``NickConflict``.
initRecord[E](symbol[E]("nickConflict"))
proc toPreserveHook*(`nickconflict`: NickConflict; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("nickConflict"))
proc `$`*[E](x: UserId | Join | Session | NickClaim | NickClaimResponse |
UserInfo |
Says |
NickConflict): string =
proc `$`*[E](x: Join[E] | Session[E] | NickClaim[E]): string =
`$`(toPreserve(x, E))
proc `encode`*[E](x: UserId | Join | Session | NickClaim | NickClaimResponse |
UserInfo |
Says |
NickConflict): seq[byte] =
proc encode*[E](x: Join[E] | Session[E] | NickClaim[E]): seq[byte] =
encode(toPreserve(x, E))
proc `$`*(x: UserId | NickConflict | NickClaimResponse | UserInfo | Says): string =
`$`(toPreserve(x))
proc encode*(x: UserId | NickConflict | NickClaimResponse | UserInfo | Says): seq[
byte] =
encode(toPreserve(x))

View File

@ -3,119 +3,47 @@ import
std/typetraits, preserves
type
RequireService*[E = void] {.record: "require-service".} = ref object ## ``<require-service @serviceName any>``
ServiceStarted*[E] {.preservesRecord: "service-started".} = ref object
`serviceName`*: Preserve[E]
RunService*[E = void] {.record: "run-service".} = ref object ## ``<run-service @serviceName any>``
`serviceName`*: Preserve[E]
ServiceStarted*[E = void] {.record: "service-started".} = ref object ## ``<service-started @serviceName any>``
`serviceName`*: Preserve[E]
ServiceRunning*[E = void] {.record: "service-running".} = ref object ## ``<service-running @serviceName any>``
`serviceName`*: Preserve[E]
ServiceDependency*[E = void] {.record: "depends-on".} = ref object ## ``<depends-on @depender any @dependee Dependee>``
`depender`*: Preserve[E]
`dependee`*: Dependee[E]
DependeeKind* {.pure.} = enum
`Servicestarted`, `Servicerunning`
Dependee*[E = void] = ref object ## ``/ ServiceStarted / ServiceRunning``
case kind*: DependeeKind
of DependeeKind.`Servicestarted`:
`servicestarted`*: ServiceStarted[E]
of DependeeKind.`Servicerunning`:
`servicerunning`*: ServiceRunning[E]
ServiceMilestone*[E = void] {.record: "service-milestone".} = ref object ## ``<service-milestone @serviceName any @milestone any>``
ServiceMilestone*[E] {.preservesRecord: "service-milestone".} = ref object
`serviceName`*: Preserve[E]
`milestone`*: Preserve[E]
proc `requireService`*[E = void](`serviceName`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``RequireService``.
initRecord[E](symbol[E]("require-service"), toPreserve(`serviceName`, E))
RequireService*[E] {.preservesRecord: "require-service".} = ref object
`serviceName`*: Preserve[E]
proc toPreserveHook*(`requireservice`: RequireService; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("require-service"),
toPreserve(`requireservice`.`serviceName`, E))
DependeeKind* {.pure.} = enum
`ServiceStarted`, `ServiceRunning`
`Dependee`*[E] {.preservesOr.} = ref object
case orKind*: DependeeKind
of DependeeKind.`ServiceStarted`:
`servicestarted`*: ServiceStarted[E]
proc `runService`*[E = void](`serviceName`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``RunService``.
initRecord[E](symbol[E]("run-service"), toPreserve(`serviceName`, E))
of DependeeKind.`ServiceRunning`:
`servicerunning`*: ServiceRunning[E]
proc toPreserveHook*(`runservice`: RunService; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("run-service"),
toPreserve(`runservice`.`serviceName`, E))
RunService*[E] {.preservesRecord: "run-service".} = ref object
`serviceName`*: Preserve[E]
proc `serviceStarted`*[E = void](`serviceName`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``ServiceStarted``.
initRecord[E](symbol[E]("service-started"), toPreserve(`serviceName`, E))
ServiceRunning*[E] {.preservesRecord: "service-running".} = ref object
`serviceName`*: Preserve[E]
proc toPreserveHook*(`servicestarted`: ServiceStarted; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("service-started"),
toPreserve(`servicestarted`.`serviceName`, E))
ServiceDependency*[E] {.preservesRecord: "depends-on".} = ref object
`depender`*: Preserve[E]
`dependee`*: Dependee[E]
proc `serviceRunning`*[E = void](`serviceName`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``ServiceRunning``.
initRecord[E](symbol[E]("service-running"), toPreserve(`serviceName`, E))
proc toPreserveHook*(`servicerunning`: ServiceRunning; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("service-running"),
toPreserve(`servicerunning`.`serviceName`, E))
proc `serviceDependency`*[E = void](`depender`: Preserve[E];
`dependee`: Dependee | Preserve[E]): Preserve[
E] =
## Preserves constructor for ``ServiceDependency``.
initRecord[E](symbol[E]("depends-on"), toPreserve(`depender`, E),
toPreserve(`dependee`, E))
proc toPreserveHook*(`servicedependency`: ServiceDependency; E: typedesc): Preserve[
E] =
initRecord[E](symbol[E]("depends-on"),
toPreserve(`servicedependency`.`depender`, E),
toPreserve(`servicedependency`.`dependee`, E))
proc toPreserveHook*(v: Dependee; E: typedesc): Preserve[E] =
case v.kind
of DependeeKind.`Servicestarted`:
toPreserve(v.`servicestarted`, E)
of DependeeKind.`Servicerunning`:
toPreserve(v.`servicerunning`, E)
proc fromPreserveHook*[E](v: var Dependee; pr: Preserve[E]): bool =
if isRecord(pr) and pr.label.isSymbol("ServiceStarted"):
v = Dependee(kind: DependeeKind.`Servicestarted`)
result = fromPreserve(v.`servicestarted`, pr)
elif isRecord(pr) and pr.label.isSymbol("ServiceRunning"):
v = Dependee(kind: DependeeKind.`Servicerunning`)
result = fromPreserve(v.`servicerunning`, pr)
proc `serviceMilestone`*[E = void](`serviceName`: Preserve[E];
`milestone`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``ServiceMilestone``.
initRecord[E](symbol[E]("service-milestone"), toPreserve(`serviceName`, E),
toPreserve(`milestone`, E))
proc toPreserveHook*(`servicemilestone`: ServiceMilestone; E: typedesc): Preserve[
E] =
initRecord[E](symbol[E]("service-milestone"),
toPreserve(`servicemilestone`.`serviceName`, E),
toPreserve(`servicemilestone`.`milestone`, E))
proc `$`*[E](x: RequireService[E] | RunService[E] | ServiceStarted[E] |
ServiceRunning[E] |
ServiceDependency[E] |
proc `$`*[E](x: ServiceStarted[E] | ServiceMilestone[E] | RequireService[E] |
Dependee[E] |
ServiceMilestone[E]): string =
RunService[E] |
ServiceRunning[E] |
ServiceDependency[E]): string =
`$`(toPreserve(x, E))
proc `encode`*[E](x: RequireService[E] | RunService[E] | ServiceStarted[E] |
ServiceRunning[E] |
ServiceDependency[E] |
proc encode*[E](x: ServiceStarted[E] | ServiceMilestone[E] | RequireService[E] |
Dependee[E] |
ServiceMilestone[E]): seq[byte] =
RunService[E] |
ServiceRunning[E] |
ServiceDependency[E]): seq[byte] =
encode(toPreserve(x, E))

View File

@ -3,31 +3,15 @@ import
std/typetraits, preserves
type
Present* {.record: "Present".} = ref object ## ``<Present @username string>``
`username`*: string
Says* {.record: "Says".} = ref object ## ``<Says @who string @what string>``
Says* {.preservesRecord: "Says".} = object
`who`*: string
`what`*: string
proc `present`*[E = void](`username`: string | Preserve[E]): Preserve[E] =
## Preserves constructor for ``Present``.
initRecord[E](symbol[E]("Present"), toPreserve(`username`, E))
Present* {.preservesRecord: "Present".} = object
`username`*: string
proc toPreserveHook*(`present`: Present; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("Present"), toPreserve(`present`.`username`, E))
proc `$`*(x: Says | Present): string =
`$`(toPreserve(x))
proc `says`*[E = void](`who`: string | Preserve[E]; `what`: string | Preserve[E]): Preserve[
E] =
## Preserves constructor for ``Says``.
initRecord[E](symbol[E]("Says"), toPreserve(`who`, E), toPreserve(`what`, E))
proc toPreserveHook*(`says`: Says; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("Says"), toPreserve(`says`.`who`, E),
toPreserve(`says`.`what`, E))
proc `$`*[E](x: Present | Says): string =
`$`(toPreserve(x, E))
proc `encode`*[E](x: Present | Says): seq[byte] =
encode(toPreserve(x, E))
proc encode*(x: Says | Present): seq[byte] =
encode(toPreserve(x))

View File

@ -3,266 +3,119 @@ import
std/typetraits, preserves
type
StreamConnection*[E = void] {.record: "stream-connection".} = ref object ## ``<stream-connection @source #!Source @sink #!Sink @spec any>``
`source`*: Source[E]
`sink`*: Sink[E]
`spec`*: Preserve[E]
CreditAmountKind* {.pure.} = enum
`count`, `unbounded`
CreditAmountCount* = BiggestInt
`CreditAmount`* {.preservesOr.} = object
case orKind*: CreditAmountKind
of CreditAmountKind.`count`:
`count`*: CreditAmountCount
StreamListenerReady*[E = void] {.record: "stream-listener-ready".} = ref object ## ``<stream-listener-ready @spec any>``
`spec`*: Preserve[E]
of CreditAmountKind.`unbounded`:
`unbounded`* {.preservesLiteral: "unbounded".}: bool
StreamListenerError*[E = void] {.record: "stream-listener-error".} = ref object ## ``<stream-listener-error @spec any @message string>``
StreamError* {.preservesRecord: "error".} = object
`message`*: string
StreamListenerError*[E] {.preservesRecord: "stream-listener-error".} = ref object
`spec`*: Preserve[E]
`message`*: string
StreamError* {.record: "error".} = ref object ## ``<error @message string>``
`message`*: string
StreamConnection*[E] {.preservesRecord: "stream-connection".} = ref object
`source`*: Preserve[E]
`sink`*: Preserve[E]
`spec`*: Preserve[E]
`LineMode`* {.preservesOr.} = enum
`lf`, `crlf`
SourceKind* {.pure.} = enum
`sink`, `Streamerror`, `credit`
Sourcesink*[E = void] {.record: "sink".} = ref object
`controller`*: Sink[E]
`sink`, `StreamError`, `credit`
SourceSink*[E] {.preservesRecord: "sink".} = ref object
`controller`*: Preserve[E]
Sourcecredit*[E = void] {.record: "credit".} = ref object
SourceCredit*[E] {.preservesRecord: "credit".} = ref object
`amount`*: CreditAmount
`mode`*: Mode[E]
Source*[E = void] = ref object ## ``/ <sink @controller #!Sink> / StreamError / <credit @amount CreditAmount @mode Mode>``
case kind*: SourceKind
`Source`*[E] {.preservesOr.} = ref object
case orKind*: SourceKind
of SourceKind.`sink`:
`sink`*: Preserve[E]
`sink`*: SourceSink[E]
of SourceKind.`Streamerror`:
of SourceKind.`StreamError`:
`streamerror`*: StreamError
of SourceKind.`credit`:
`credit`*: Sourcecredit[E]
`credit`*: SourceCredit[E]
SinkKind* {.pure.} = enum
`source`, `Streamerror`, `data`, `eof`
Sinksource*[E = void] {.record: "source".} = ref object
`controller`*: Source[E]
`source`, `StreamError`, `data`, `eof`
SinkSource*[E] {.preservesRecord: "source".} = ref object
`controller`*: Preserve[E]
Sinkdata*[E = void] {.record: "data".} = ref object
SinkData*[E] {.preservesRecord: "data".} = ref object
`payload`*: Preserve[E]
`mode`*: Mode[E]
Sinkeof* {.record: "eof".} = object
discard
Sink*[E = void] = ref object ## ``/ <source @controller #!Source> / StreamError / <data @payload any @mode Mode> / <eof>``
case kind*: SinkKind
SinkEof* {.preservesRecord: "eof".} = object
`Sink`*[E] {.preservesOr.} = ref object
case orKind*: SinkKind
of SinkKind.`source`:
`source`*: Preserve[E]
`source`*: SinkSource[E]
of SinkKind.`Streamerror`:
of SinkKind.`StreamError`:
`streamerror`*: StreamError
of SinkKind.`data`:
`data`*: Sinkdata[E]
`data`*: SinkData[E]
of SinkKind.`eof`:
`eof`*: Sinkeof
`eof`*: SinkEof
CreditamountKind* {.pure.} = enum
`count`, `unbounded`
CreditAmountcount* = BiggestInt
CreditAmountunbounded* = string
CreditAmount* = ref object ## ``/ @count int / =<<lit>unbounded>``
case kind*: CreditamountKind
of CreditamountKind.`count`:
`count`*: CreditAmountcount
StreamListenerReady*[E] {.preservesRecord: "stream-listener-ready".} = ref object
`spec`*: Preserve[E]
of CreditamountKind.`unbounded`:
discard
ModeKind* {.pure.} = enum
`bytes`, `lines`, `packet`, `object`
Modebytes* = string
Modepacket* {.record: "packet".} = ref object
ModePacket* {.preservesRecord: "packet".} = object
`size`*: BiggestInt
Modeobject*[E = void] {.record: "object".} = ref object
ModeObject*[E] {.preservesRecord: "object".} = ref object
`description`*: Preserve[E]
Mode*[E = void] = ref object ## ``/ =<<lit>bytes> / LineMode / <packet @size int> / <object @description any>``
case kind*: ModeKind
`Mode`*[E] {.preservesOr.} = ref object
case orKind*: ModeKind
of ModeKind.`bytes`:
discard
`bytes`* {.preservesLiteral: "bytes".}: bool
of ModeKind.`lines`:
`lines`*: LineMode
of ModeKind.`packet`:
`packet`*: BiggestInt
`packet`*: ModePacket
of ModeKind.`object`:
`object`*: Preserve[E]
`object`*: ModeObject[E]
LineMode* {.pure.} = enum ## ``/ =<<lit>lf> / =<<lit>crlf>``
`lf`, `crlf`
proc `streamConnection`*[E = void](`source`: Preserve[E]; `sink`: Preserve[E];
`spec`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``StreamConnection``.
initRecord[E](symbol[E]("stream-connection"), toPreserve(`source`, E),
toPreserve(`sink`, E), toPreserve(`spec`, E))
proc toPreserveHook*(`streamconnection`: StreamConnection; E: typedesc): Preserve[
E] =
initRecord[E](symbol[E]("stream-connection"),
toPreserve(`streamconnection`.`source`, E),
toPreserve(`streamconnection`.`sink`, E),
toPreserve(`streamconnection`.`spec`, E))
proc `streamListenerReady`*[E = void](`spec`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``StreamListenerReady``.
initRecord[E](symbol[E]("stream-listener-ready"), toPreserve(`spec`, E))
proc toPreserveHook*(`streamlistenerready`: StreamListenerReady; E: typedesc): Preserve[
E] =
initRecord[E](symbol[E]("stream-listener-ready"),
toPreserve(`streamlistenerready`.`spec`, E))
proc `streamListenerError`*[E = void](`spec`: Preserve[E];
`message`: string | Preserve[E]): Preserve[
E] =
## Preserves constructor for ``StreamListenerError``.
initRecord[E](symbol[E]("stream-listener-error"), toPreserve(`spec`, E),
toPreserve(`message`, E))
proc toPreserveHook*(`streamlistenererror`: StreamListenerError; E: typedesc): Preserve[
E] =
initRecord[E](symbol[E]("stream-listener-error"),
toPreserve(`streamlistenererror`.`spec`, E),
toPreserve(`streamlistenererror`.`message`, E))
proc `streamError`*[E = void](`message`: string | Preserve[E]): Preserve[E] =
## Preserves constructor for ``StreamError``.
initRecord[E](symbol[E]("error"), toPreserve(`message`, E))
proc toPreserveHook*(`streamerror`: StreamError; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("error"), toPreserve(`streamerror`.`message`, E))
proc toPreserveHook*(v: Source; E: typedesc): Preserve[E] =
case v.kind
of SourceKind.`sink`:
toPreserve(v.`sink`, E)
of SourceKind.`Streamerror`:
toPreserve(v.`streamerror`, E)
of SourceKind.`credit`:
toPreserve(v.`credit`, E)
proc fromPreserveHook*[E](v: var Source; pr: Preserve[E]): bool =
if isRecord(pr) and pr.label.isSymbol("sink"):
v = Source(kind: SourceKind.`sink`)
result = fromPreserve(v.`sink`, pr)
elif isRecord(pr) and pr.label.isSymbol("StreamError"):
v = Source(kind: SourceKind.`Streamerror`)
result = fromPreserve(v.`streamerror`, pr)
elif isRecord(pr) and pr.label.isSymbol("credit"):
v = Source(kind: SourceKind.`credit`)
result = fromPreserve(v.`credit`, pr)
proc toPreserveHook*(v: Sink; E: typedesc): Preserve[E] =
case v.kind
of SinkKind.`source`:
toPreserve(v.`source`, E)
of SinkKind.`Streamerror`:
toPreserve(v.`streamerror`, E)
of SinkKind.`data`:
toPreserve(v.`data`, E)
of SinkKind.`eof`:
toPreserve(v.`eof`, E)
proc fromPreserveHook*[E](v: var Sink; pr: Preserve[E]): bool =
if isRecord(pr) and pr.label.isSymbol("source"):
v = Sink(kind: SinkKind.`source`)
result = fromPreserve(v.`source`, pr)
elif isRecord(pr) and pr.label.isSymbol("StreamError"):
v = Sink(kind: SinkKind.`Streamerror`)
result = fromPreserve(v.`streamerror`, pr)
elif isRecord(pr) and pr.label.isSymbol("data"):
v = Sink(kind: SinkKind.`data`)
result = fromPreserve(v.`data`, pr)
elif isRecord(pr) and pr.label.isSymbol("eof"):
v = Sink(kind: SinkKind.`eof`)
result = fromPreserve(v.`eof`, pr)
proc toPreserveHook*(v: CreditAmount; E: typedesc): Preserve[E] =
case v.kind
of CreditAmountKind.`count`:
toPreserve(v.`count`, E)
of CreditAmountKind.`unbounded`:
Preserve[E](kind: pkSymbol, symbol: "unbounded")
proc fromPreserveHook*[E](v: var CreditAmount; pr: Preserve[E]): bool =
if false:
discard
elif pr.kind == pkSymbol and pr.symbol == "unbounded":
v = CreditAmount(kind: CreditAmountKind.`unbounded`)
result = true
proc toPreserveHook*(v: Mode; E: typedesc): Preserve[E] =
case v.kind
of ModeKind.`bytes`:
Preserve[E](kind: pkSymbol, symbol: "bytes")
of ModeKind.`lines`:
toPreserve(v.`lines`, E)
of ModeKind.`packet`:
toPreserve(v.`packet`, E)
of ModeKind.`object`:
toPreserve(v.`object`, E)
proc fromPreserveHook*[E](v: var Mode; pr: Preserve[E]): bool =
if pr.kind == pkSymbol and pr.symbol == "bytes":
v = Mode(kind: ModeKind.`bytes`)
result = true
elif false: ## snkOr - / =<<lit>lf> / =<<lit>crlf>
discard
elif isRecord(pr) and pr.label.isSymbol("packet"):
v = Mode(kind: ModeKind.`packet`)
result = fromPreserve(v.`packet`, pr)
elif isRecord(pr) and pr.label.isSymbol("object"):
v = Mode(kind: ModeKind.`object`)
result = fromPreserve(v.`object`, pr)
proc toPreserveHook*(v: LineMode; E: typedesc): Preserve[E] =
case v
of LineMode.`lf`:
symbol[E]("lf")
of LineMode.`crlf`:
symbol[E]("crlf")
proc fromPreserveHook*[E](v: var LineMode; pr: Preserve[E]): bool =
if isSymbol(pr):
case pr.symbol
of "lf":
v = LineMode.`lf`
result = true
of "crlf":
v = LineMode.`crlf`
result = true
proc `$`*[E](x: StreamConnection[E] | StreamListenerReady[E] |
StreamListenerError[E] |
StreamError |
Source[E] |
proc `$`*[E](x: StreamListenerError[E] | StreamConnection[E] | Source[E] |
Sink[E] |
CreditAmount |
Mode[E] |
LineMode): string =
StreamListenerReady[E] |
Mode[E]): string =
`$`(toPreserve(x, E))
proc `encode`*[E](x: StreamConnection[E] | StreamListenerReady[E] |
StreamListenerError[E] |
StreamError |
Source[E] |
proc encode*[E](x: StreamListenerError[E] | StreamConnection[E] | Source[E] |
Sink[E] |
CreditAmount |
Mode[E] |
LineMode): seq[byte] =
StreamListenerReady[E] |
Mode[E]): seq[byte] =
encode(toPreserve(x, E))
proc `$`*(x: CreditAmount | StreamError): string =
`$`(toPreserve(x))
proc encode*(x: CreditAmount | StreamError): seq[byte] =
encode(toPreserve(x))

View File

@ -3,16 +3,76 @@ import
std/typetraits, preserves, std/tables, std/tables
type
SturdyRef*[E = void] {.record: "ref".} = ref object ## ``<ref @oid any @caveatChain [ Attenuation ... ] @sig bytes>``
`oid`*: Preserve[E]
`caveatChain`*: seq[Attenuation[E]]
`sig`*: seq[byte]
CRec*[E] {.preservesRecord: "rec".} = ref object
`label`*: Preserve[E]
`arity`*: BiggestInt
Attenuation*[E = void] = seq[Caveat[E]] ## ``[ Caveat ... ]``
PCompound*[E] {.preservesRecord: "compound".} = ref object
`ctor`*: ConstructorSpec[E]
`members`*: PCompoundMembers[E]
ConstructorSpecKind* {.pure.} = enum
`CRec`, `CArr`, `CDict`
`ConstructorSpec`*[E] {.preservesOr.} = ref object
case orKind*: ConstructorSpecKind
of ConstructorSpecKind.`CRec`:
`crec`*: CRec[E]
of ConstructorSpecKind.`CArr`:
`carr`*: CArr
of ConstructorSpecKind.`CDict`:
`cdict`*: CDict
PAnd*[E] {.preservesRecord: "and".} = ref object
`patterns`*: seq[Pattern[E]]
Rewrite*[E] {.preservesRecord: "rewrite".} = ref object
`pattern`*: Pattern[E]
`template`*: Template[E]
TCompoundMembers*[E] = Table[Preserve[E], Template[E]]
TRef* {.preservesRecord: "ref".} = object
`binding`*: BiggestInt
PBind*[E] {.preservesRecord: "bind".} = ref object
`pattern`*: Pattern[E]
Lit*[E] {.preservesRecord: "lit".} = ref object
`value`*: Preserve[E]
TCompound*[E] {.preservesRecord: "compound".} = ref object
`ctor`*: ConstructorSpec[E]
`members`*: TCompoundMembers[E]
`PAtom`* {.preservesOr.} = enum
`Boolean`, `Float`, `Double`, `SignedInteger`, `String`, `ByteString`,
`Symbol`
Attenuation*[E] = seq[Caveat[E]]
PDiscard* {.preservesRecord: "_".} = object
TemplateKind* {.pure.} = enum
`TAttenuate`, `TRef`, `Lit`, `TCompound`
`Template`*[E] {.preservesOr.} = ref object
case orKind*: TemplateKind
of TemplateKind.`TAttenuate`:
`tattenuate`*: TAttenuate[E]
of TemplateKind.`TRef`:
`tref`*: TRef
of TemplateKind.`Lit`:
`lit`*: Lit[E]
of TemplateKind.`TCompound`:
`tcompound`*: TCompound[E]
CaveatKind* {.pure.} = enum
`Rewrite`, `Alts`
Caveat*[E = void] = ref object ## ``/ Rewrite / Alts``
case kind*: CaveatKind
`Caveat`*[E] {.preservesOr.} = ref object
case orKind*: CaveatKind
of CaveatKind.`Rewrite`:
`rewrite`*: Rewrite[E]
@ -20,475 +80,116 @@ type
`alts`*: Alts[E]
Rewrite*[E = void] {.record: "rewrite".} = ref object ## ``<rewrite @pattern Pattern @template Template>``
`pattern`*: Pattern[E]
`template`*: Template[E]
CArr* {.preservesRecord: "arr".} = object
`arity`*: BiggestInt
Alts*[E = void] {.record: "or".} = ref object ## ``<or @alternatives [ Rewrite ... ]>``
PCompoundMembers*[E] = Table[Preserve[E], Pattern[E]]
PNot*[E] {.preservesRecord: "not".} = ref object
`pattern`*: Pattern[E]
SturdyRef*[E] {.preservesRecord: "ref".} = ref object
`oid`*: Preserve[E]
`caveatChain`*: seq[Attenuation[E]]
`sig`*: seq[byte]
WireRefKind* {.pure.} = enum
`mine`, `yours`
WireRefMine* {.preservesTuple.} = object
`data`* {.preservesLiteral: "0".}: bool
`oid`*: Oid
WireRefYours*[E] {.preservesTuple.} = ref object
`data`* {.preservesLiteral: "1".}: bool
`oid`*: Oid
`attenuation`* {.preservesTupleTail.}: seq[Caveat[E]]
`WireRef`*[E] {.preservesOr.} = ref object
case orKind*: WireRefKind
of WireRefKind.`mine`:
`mine`*: WireRefMine
of WireRefKind.`yours`:
`yours`*: WireRefYours[E]
TAttenuate*[E] {.preservesRecord: "attenuate".} = ref object
`template`*: Template[E]
`attenuation`*: Attenuation[E]
Oid* = BiggestInt
Alts*[E] {.preservesRecord: "or".} = ref object
`alternatives`*: seq[Rewrite[E]]
Oid* = distinct BiggestInt ## ``int``
WirerefKind* {.pure.} = enum
`mine`, `yours`
WireRefmine* = tuple[`0`: BiggestInt, `oid`: Oid]
WireRefyours*[E = void] = tuple[`1`: BiggestInt, `oid`: Oid,
`attenuation`: seq[Caveat[E]]]
WireRef*[E = void] = ref object ## ``/ @mine [0 @oid Oid] / @yours [1 @oid Oid @attenuation Caveat ...]``
case kind*: WirerefKind
of WirerefKind.`mine`:
`mine`*: WireRefmine
of WirerefKind.`yours`:
`yours`*: WireRefyours[E]
CDict* {.preservesRecord: "dict".} = object
ConstructorspecKind* {.pure.} = enum
`Crec`, `Carr`, `Cdict`
ConstructorSpec*[E = void] = ref object ## ``/ CRec / CArr / CDict``
case kind*: ConstructorspecKind
of ConstructorspecKind.`Crec`:
`crec`*: CRec[E]
of ConstructorspecKind.`Carr`:
`carr`*: CArr
of ConstructorspecKind.`Cdict`:
`cdict`*: CDict
CRec*[E = void] {.record: "rec".} = ref object ## ``<rec @label any @arity int>``
`label`*: Preserve[E]
`arity`*: BiggestInt
CArr* {.record: "arr".} = ref object ## ``<arr @arity int>``
`arity`*: BiggestInt
CDict* {.record: "dict".} = object ## ``<dict>``
discard
Lit*[E = void] {.record: "lit".} = ref object ## ``<lit @value any>``
`value`*: Preserve[E]
PatternKind* {.pure.} = enum
`Pdiscard`, `Patom`, `Pembedded`, `Pbind`, `Pand`, `Pnot`, `Lit`,
`Pcompound`
Pattern*[E = void] = ref object ## ``/ PDiscard / PAtom / PEmbedded / PBind / PAnd / PNot / Lit / PCompound``
case kind*: PatternKind
of PatternKind.`Pdiscard`:
`PDiscard`, `PAtom`, `PEmbedded`, `PBind`, `PAnd`, `PNot`, `Lit`,
`PCompound`
`Pattern`*[E] {.preservesOr.} = ref object
case orKind*: PatternKind
of PatternKind.`PDiscard`:
`pdiscard`*: PDiscard
of PatternKind.`Patom`:
of PatternKind.`PAtom`:
`patom`*: PAtom
of PatternKind.`Pembedded`:
discard
of PatternKind.`PEmbedded`:
`pembedded`* {.preservesLiteral: "Embedded".}: bool
of PatternKind.`Pbind`:
of PatternKind.`PBind`:
`pbind`*: PBind[E]
of PatternKind.`Pand`:
of PatternKind.`PAnd`:
`pand`*: PAnd[E]
of PatternKind.`Pnot`:
of PatternKind.`PNot`:
`pnot`*: PNot[E]
of PatternKind.`Lit`:
`lit`*: Lit[E]
of PatternKind.`Pcompound`:
of PatternKind.`PCompound`:
`pcompound`*: PCompound[E]
PDiscard* {.record: "_".} = object ## ``<_>``
discard
PAtom* {.pure.} = enum ## ``/ =<<lit>Boolean> / =<<lit>Float> / =<<lit>Double> / =<<lit>SignedInteger> / =<<lit>String> / =<<lit>ByteString> / =<<lit>Symbol>``
`Boolean`, `Float`, `Double`, `Signedinteger`, `String`, `Bytestring`,
`Symbol`
PBind*[E = void] {.record: "bind".} = ref object ## ``<bind @pattern Pattern>``
`pattern`*: Pattern[E]
PAnd*[E = void] {.record: "and".} = ref object ## ``<and @patterns [ Pattern ... ]>``
`patterns`*: seq[Pattern[E]]
PNot*[E = void] {.record: "not".} = ref object ## ``<not @pattern Pattern>``
`pattern`*: Pattern[E]
PCompound*[E = void] {.record: "compound".} = ref object ## ``<compound @ctor ConstructorSpec @members PCompoundMembers>``
`ctor`*: ConstructorSpec[E]
`members`*: PCompoundMembers[E]
PCompoundMembers*[E = void] = TableRef[Preserve[E], Pattern[E]] ## ``{any : Pattern ...:...}``
TemplateKind* {.pure.} = enum
`Tattenuate`, `Tref`, `Lit`, `Tcompound`
Template*[E = void] = ref object ## ``/ TAttenuate / TRef / Lit / TCompound``
case kind*: TemplateKind
of TemplateKind.`Tattenuate`:
`tattenuate`*: TAttenuate[E]
of TemplateKind.`Tref`:
`tref`*: TRef
of TemplateKind.`Lit`:
`lit`*: Lit[E]
of TemplateKind.`Tcompound`:
`tcompound`*: TCompound[E]
TAttenuate*[E = void] {.record: "attenuate".} = ref object ## ``<attenuate @template Template @attenuation Attenuation>``
`template`*: Template[E]
`attenuation`*: Attenuation[E]
TRef* {.record: "ref".} = ref object ## ``<ref @binding int>``
`binding`*: BiggestInt
TCompound*[E = void] {.record: "compound".} = ref object ## ``<compound @ctor ConstructorSpec @members TCompoundMembers>``
`ctor`*: ConstructorSpec[E]
`members`*: TCompoundMembers[E]
TCompoundMembers*[E = void] = TableRef[Preserve[E], Template[E]] ## ``{any : Template ...:...}``
proc `sturdyRef`*[E = void](`oid`: Preserve[E]; `caveatChain`: Preserve[E];
`sig`: seq[byte] | Preserve[E]): Preserve[E] =
## Preserves constructor for ``SturdyRef``.
initRecord[E](symbol[E]("ref"), toPreserve(`oid`, E),
toPreserve(`caveatChain`, E), toPreserve(`sig`, E))
proc toPreserveHook*(`sturdyref`: SturdyRef; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("ref"), toPreserve(`sturdyref`.`oid`, E),
toPreserve(`sturdyref`.`caveatChain`, E),
toPreserve(`sturdyref`.`sig`, E))
proc toPreserveHook*(v: Caveat; E: typedesc): Preserve[E] =
case v.kind
of CaveatKind.`Rewrite`:
toPreserve(v.`rewrite`, E)
of CaveatKind.`Alts`:
toPreserve(v.`alts`, E)
proc fromPreserveHook*[E](v: var Caveat; pr: Preserve[E]): bool =
if isRecord(pr) and pr.label.isSymbol("Rewrite"):
v = Caveat(kind: CaveatKind.`Rewrite`)
result = fromPreserve(v.`rewrite`, pr)
elif isRecord(pr) and pr.label.isSymbol("Alts"):
v = Caveat(kind: CaveatKind.`Alts`)
result = fromPreserve(v.`alts`, pr)
proc `rewrite`*[E = void](`pattern`: Pattern | Preserve[E];
`template`: Template | Preserve[E]): Preserve[E] =
## Preserves constructor for ``Rewrite``.
initRecord[E](symbol[E]("rewrite"), toPreserve(`pattern`, E),
toPreserve(`template`, E))
proc toPreserveHook*(`rewrite`: Rewrite; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("rewrite"), toPreserve(`rewrite`.`pattern`, E),
toPreserve(`rewrite`.`template`, E))
proc `alts`*[E = void](`alternatives`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``Alts``.
initRecord[E](symbol[E]("or"), toPreserve(`alternatives`, E))
proc toPreserveHook*(`alts`: Alts; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("or"), toPreserve(`alts`.`alternatives`, E))
proc toPreserveHook*(v: WireRef; E: typedesc): Preserve[E] =
case v.kind
of WireRefKind.`mine`:
Preserve[E](kind: pkSequence, sequence: @[
Preserve[E](kind: pkSignedInteger, int: 0'i64),
toPreserve(v.`mine`.`oid`, E)])
of WireRefKind.`yours`:
Preserve[E](kind: pkSequence, sequence: @[
Preserve[E](kind: pkSignedInteger, int: 1'i64),
toPreserve(v.`yours`.`oid`, E)] &
toPreserve(v.`yours`.`attenuation`, E).sequence)
proc fromPreserveHook*[E](v: var WireRef; pr: Preserve[E]): bool =
if isSequence(pr) and len(pr) == 2 and
(pr[0].kind == pkSignedInteger and pr[0].int == 0'i64):
v = WireRef(kind: WireRefKind.`mine`)
result = fromPreserve(v.`mine`, pr)
elif isSequence(pr) and len(pr) >= 2 and
(pr[0].kind == pkSignedInteger and pr[0].int == 1'i64):
v = WireRef(kind: WireRefKind.`yours`)
result = fromPreserve(v.`yours`, pr)
proc toPreserveHook*(v: ConstructorSpec; E: typedesc): Preserve[E] =
case v.kind
of ConstructorSpecKind.`Crec`:
toPreserve(v.`crec`, E)
of ConstructorSpecKind.`Carr`:
toPreserve(v.`carr`, E)
of ConstructorSpecKind.`Cdict`:
toPreserve(v.`cdict`, E)
proc fromPreserveHook*[E](v: var ConstructorSpec; pr: Preserve[E]): bool =
if isRecord(pr) and pr.label.isSymbol("CRec"):
v = ConstructorSpec(kind: ConstructorSpecKind.`Crec`)
result = fromPreserve(v.`crec`, pr)
elif isRecord(pr) and pr.label.isSymbol("CArr"):
v = ConstructorSpec(kind: ConstructorSpecKind.`Carr`)
result = fromPreserve(v.`carr`, pr)
elif isRecord(pr) and pr.label.isSymbol("CDict"):
v = ConstructorSpec(kind: ConstructorSpecKind.`Cdict`)
result = fromPreserve(v.`cdict`, pr)
proc `cRec`*[E = void](`label`: Preserve[E]; `arity`: BiggestInt | Preserve[E]): Preserve[
E] =
## Preserves constructor for ``CRec``.
initRecord[E](symbol[E]("rec"), toPreserve(`label`, E), toPreserve(`arity`, E))
proc toPreserveHook*(`crec`: CRec; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("rec"), toPreserve(`crec`.`label`, E),
toPreserve(`crec`.`arity`, E))
proc `cArr`*[E = void](`arity`: BiggestInt | Preserve[E]): Preserve[E] =
## Preserves constructor for ``CArr``.
initRecord[E](symbol[E]("arr"), toPreserve(`arity`, E))
proc toPreserveHook*(`carr`: CArr; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("arr"), toPreserve(`carr`.`arity`, E))
proc `cDict`*[E = void](): Preserve[E] =
## Preserves constructor for ``CDict``.
initRecord[E](symbol[E]("dict"))
proc toPreserveHook*(`cdict`: CDict; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("dict"))
proc `lit`*[E = void](`value`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``Lit``.
initRecord[E](symbol[E]("lit"), toPreserve(`value`, E))
proc toPreserveHook*(`lit`: Lit; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("lit"), toPreserve(`lit`.`value`, E))
proc toPreserveHook*(v: Pattern; E: typedesc): Preserve[E] =
case v.kind
of PatternKind.`Pdiscard`:
toPreserve(v.`pdiscard`, E)
of PatternKind.`Patom`:
toPreserve(v.`patom`, E)
of PatternKind.`Pembedded`:
Preserve[E](kind: pkSymbol, symbol: "Embedded")
of PatternKind.`Pbind`:
toPreserve(v.`pbind`, E)
of PatternKind.`Pand`:
toPreserve(v.`pand`, E)
of PatternKind.`Pnot`:
toPreserve(v.`pnot`, E)
of PatternKind.`Lit`:
toPreserve(v.`lit`, E)
of PatternKind.`Pcompound`:
toPreserve(v.`pcompound`, E)
proc fromPreserveHook*[E](v: var Pattern; pr: Preserve[E]): bool =
if isRecord(pr) and pr.label.isSymbol("PDiscard"):
v = Pattern(kind: PatternKind.`Pdiscard`)
result = fromPreserve(v.`pdiscard`, pr)
elif false: ## snkOr - / =<<lit>Boolean> / =<<lit>Float> / =<<lit>Double> / =<<lit>SignedInteger> / =<<lit>String> / =<<lit>ByteString> / =<<lit>Symbol>
discard
elif pr.kind == pkSymbol and pr.symbol == "Embedded":
v = Pattern(kind: PatternKind.`Pembedded`)
result = true
elif isRecord(pr) and pr.label.isSymbol("PBind"):
v = Pattern(kind: PatternKind.`Pbind`)
result = fromPreserve(v.`pbind`, pr)
elif isRecord(pr) and pr.label.isSymbol("PAnd"):
v = Pattern(kind: PatternKind.`Pand`)
result = fromPreserve(v.`pand`, pr)
elif isRecord(pr) and pr.label.isSymbol("PNot"):
v = Pattern(kind: PatternKind.`Pnot`)
result = fromPreserve(v.`pnot`, pr)
elif isRecord(pr) and pr.label.isSymbol("Lit"):
v = Pattern(kind: PatternKind.`Lit`)
result = fromPreserve(v.`lit`, pr)
elif isRecord(pr) and pr.label.isSymbol("PCompound"):
v = Pattern(kind: PatternKind.`Pcompound`)
result = fromPreserve(v.`pcompound`, pr)
proc `pDiscard`*[E = void](): Preserve[E] =
## Preserves constructor for ``PDiscard``.
initRecord[E](symbol[E]("_"))
proc toPreserveHook*(`pdiscard`: PDiscard; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("_"))
proc toPreserveHook*(v: PAtom; E: typedesc): Preserve[E] =
case v
of PAtom.`Boolean`:
symbol[E]("Boolean")
of PAtom.`Float`:
symbol[E]("Float")
of PAtom.`Double`:
symbol[E]("Double")
of PAtom.`Signedinteger`:
symbol[E]("SignedInteger")
of PAtom.`String`:
symbol[E]("String")
of PAtom.`Bytestring`:
symbol[E]("ByteString")
of PAtom.`Symbol`:
symbol[E]("Symbol")
proc fromPreserveHook*[E](v: var PAtom; pr: Preserve[E]): bool =
if isSymbol(pr):
case pr.symbol
of "Boolean":
v = PAtom.`Boolean`
result = true
of "Float":
v = PAtom.`Float`
result = true
of "Double":
v = PAtom.`Double`
result = true
of "SignedInteger":
v = PAtom.`Signedinteger`
result = true
of "String":
v = PAtom.`String`
result = true
of "ByteString":
v = PAtom.`Bytestring`
result = true
of "Symbol":
v = PAtom.`Symbol`
result = true
proc pEmbedded*[E = void](): Preserve[E] =
## ``<<lit>Embedded>``
symbol[E]("Embedded")
proc `pBind`*[E = void](`pattern`: Pattern | Preserve[E]): Preserve[E] =
## Preserves constructor for ``PBind``.
initRecord[E](symbol[E]("bind"), toPreserve(`pattern`, E))
proc toPreserveHook*(`pbind`: PBind; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("bind"), toPreserve(`pbind`.`pattern`, E))
proc `pAnd`*[E = void](`patterns`: Preserve[E]): Preserve[E] =
## Preserves constructor for ``PAnd``.
initRecord[E](symbol[E]("and"), toPreserve(`patterns`, E))
proc toPreserveHook*(`pand`: PAnd; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("and"), toPreserve(`pand`.`patterns`, E))
proc `pNot`*[E = void](`pattern`: Pattern | Preserve[E]): Preserve[E] =
## Preserves constructor for ``PNot``.
initRecord[E](symbol[E]("not"), toPreserve(`pattern`, E))
proc toPreserveHook*(`pnot`: PNot; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("not"), toPreserve(`pnot`.`pattern`, E))
proc `pCompound`*[E = void](`ctor`: ConstructorSpec | Preserve[E];
`members`: PCompoundMembers | Preserve[E]): Preserve[
E] =
## Preserves constructor for ``PCompound``.
initRecord[E](symbol[E]("compound"), toPreserve(`ctor`, E),
toPreserve(`members`, E))
proc toPreserveHook*(`pcompound`: PCompound; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("compound"), toPreserve(`pcompound`.`ctor`, E),
toPreserve(`pcompound`.`members`, E))
proc toPreserveHook*(v: Template; E: typedesc): Preserve[E] =
case v.kind
of TemplateKind.`Tattenuate`:
toPreserve(v.`tattenuate`, E)
of TemplateKind.`Tref`:
toPreserve(v.`tref`, E)
of TemplateKind.`Lit`:
toPreserve(v.`lit`, E)
of TemplateKind.`Tcompound`:
toPreserve(v.`tcompound`, E)
proc fromPreserveHook*[E](v: var Template; pr: Preserve[E]): bool =
if isRecord(pr) and pr.label.isSymbol("TAttenuate"):
v = Template(kind: TemplateKind.`Tattenuate`)
result = fromPreserve(v.`tattenuate`, pr)
elif isRecord(pr) and pr.label.isSymbol("TRef"):
v = Template(kind: TemplateKind.`Tref`)
result = fromPreserve(v.`tref`, pr)
elif isRecord(pr) and pr.label.isSymbol("Lit"):
v = Template(kind: TemplateKind.`Lit`)
result = fromPreserve(v.`lit`, pr)
elif isRecord(pr) and pr.label.isSymbol("TCompound"):
v = Template(kind: TemplateKind.`Tcompound`)
result = fromPreserve(v.`tcompound`, pr)
proc `tAttenuate`*[E = void](`template`: Template | Preserve[E];
`attenuation`: Attenuation | Preserve[E]): Preserve[
E] =
## Preserves constructor for ``TAttenuate``.
initRecord[E](symbol[E]("attenuate"), toPreserve(`template`, E),
toPreserve(`attenuation`, E))
proc toPreserveHook*(`tattenuate`: TAttenuate; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("attenuate"), toPreserve(`tattenuate`.`template`, E),
toPreserve(`tattenuate`.`attenuation`, E))
proc `tRef`*[E = void](`binding`: BiggestInt | Preserve[E]): Preserve[E] =
## Preserves constructor for ``TRef``.
initRecord[E](symbol[E]("ref"), toPreserve(`binding`, E))
proc toPreserveHook*(`tref`: TRef; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("ref"), toPreserve(`tref`.`binding`, E))
proc `tCompound`*[E = void](`ctor`: ConstructorSpec | Preserve[E];
`members`: TCompoundMembers | Preserve[E]): Preserve[
E] =
## Preserves constructor for ``TCompound``.
initRecord[E](symbol[E]("compound"), toPreserve(`ctor`, E),
toPreserve(`members`, E))
proc toPreserveHook*(`tcompound`: TCompound; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("compound"), toPreserve(`tcompound`.`ctor`, E),
toPreserve(`tcompound`.`members`, E))
proc `$`*[E](x: SturdyRef[E] | Attenuation[E] | Caveat[E] | Rewrite[E] | Alts[E] |
Oid |
WireRef[E] |
ConstructorSpec[E] |
CRec[E] |
CArr |
CDict |
Lit[E] |
Pattern[E] |
PDiscard |
PAtom |
proc `$`*[E](x: CRec[E] | PCompound[E] | ConstructorSpec[E] | PAnd[E] |
Rewrite[E] |
TCompoundMembers[E] |
PBind[E] |
PAnd[E] |
PNot[E] |
PCompound[E] |
PCompoundMembers[E] |
Template[E] |
TAttenuate[E] |
TRef |
Lit[E] |
TCompound[E] |
TCompoundMembers[E]): string =
Attenuation[E] |
Template[E] |
Caveat[E] |
PCompoundMembers[E] |
PNot[E] |
SturdyRef[E] |
WireRef[E] |
TAttenuate[E] |
Alts[E] |
Pattern[E]): string =
`$`(toPreserve(x, E))
proc `encode`*[E](x: SturdyRef[E] | Attenuation[E] | Caveat[E] | Rewrite[E] |
Alts[E] |
Oid |
WireRef[E] |
ConstructorSpec[E] |
CRec[E] |
CArr |
CDict |
Lit[E] |
Pattern[E] |
PDiscard |
PAtom |
proc encode*[E](x: CRec[E] | PCompound[E] | ConstructorSpec[E] | PAnd[E] |
Rewrite[E] |
TCompoundMembers[E] |
PBind[E] |
PAnd[E] |
PNot[E] |
PCompound[E] |
PCompoundMembers[E] |
Template[E] |
TAttenuate[E] |
TRef |
Lit[E] |
TCompound[E] |
TCompoundMembers[E]): seq[byte] =
Attenuation[E] |
Template[E] |
Caveat[E] |
PCompoundMembers[E] |
PNot[E] |
SturdyRef[E] |
WireRef[E] |
TAttenuate[E] |
Alts[E] |
Pattern[E]): seq[byte] =
encode(toPreserve(x, E))
proc `$`*(x: TRef | PDiscard | CArr | Oid | CDict): string =
`$`(toPreserve(x))
proc encode*(x: TRef | PDiscard | CArr | Oid | CDict): seq[byte] =
encode(toPreserve(x))

View File

@ -3,53 +3,27 @@ import
std/typetraits, preserves
type
TcpRemote* {.record: "tcp-remote".} = ref object ## ``<tcp-remote @host string @port int>``
TcpLocal* {.preservesRecord: "tcp-local".} = object
`host`*: string
`port`*: BiggestInt
TcpLocal* {.record: "tcp-local".} = ref object ## ``<tcp-local @host string @port int>``
`host`*: string
`port`*: BiggestInt
TcpPeerInfo*[E = void] {.record: "tcp-peer".} = ref object ## ``<tcp-peer @handle #!any @local TcpLocal @remote TcpRemote>``
TcpPeerInfo*[E] {.preservesRecord: "tcp-peer".} = ref object
`handle`*: Preserve[E]
`local`*: TcpLocal
`remote`*: TcpRemote
proc `tcpRemote`*[E = void](`host`: string | Preserve[E];
`port`: BiggestInt | Preserve[E]): Preserve[E] =
## Preserves constructor for ``TcpRemote``.
initRecord[E](symbol[E]("tcp-remote"), toPreserve(`host`, E),
toPreserve(`port`, E))
TcpRemote* {.preservesRecord: "tcp-remote".} = object
`host`*: string
`port`*: BiggestInt
proc toPreserveHook*(`tcpremote`: TcpRemote; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("tcp-remote"), toPreserve(`tcpremote`.`host`, E),
toPreserve(`tcpremote`.`port`, E))
proc `tcpLocal`*[E = void](`host`: string | Preserve[E];
`port`: BiggestInt | Preserve[E]): Preserve[E] =
## Preserves constructor for ``TcpLocal``.
initRecord[E](symbol[E]("tcp-local"), toPreserve(`host`, E),
toPreserve(`port`, E))
proc toPreserveHook*(`tcplocal`: TcpLocal; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("tcp-local"), toPreserve(`tcplocal`.`host`, E),
toPreserve(`tcplocal`.`port`, E))
proc `tcpPeerInfo`*[E = void](`handle`: Preserve[E];
`local`: TcpLocal | Preserve[E];
`remote`: TcpRemote | Preserve[E]): Preserve[E] =
## Preserves constructor for ``TcpPeerInfo``.
initRecord[E](symbol[E]("tcp-peer"), toPreserve(`handle`, E),
toPreserve(`local`, E), toPreserve(`remote`, E))
proc toPreserveHook*(`tcppeerinfo`: TcpPeerInfo; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("tcp-peer"), toPreserve(`tcppeerinfo`.`handle`, E),
toPreserve(`tcppeerinfo`.`local`, E),
toPreserve(`tcppeerinfo`.`remote`, E))
proc `$`*[E](x: TcpRemote | TcpLocal | TcpPeerInfo[E]): string =
proc `$`*[E](x: TcpPeerInfo[E]): string =
`$`(toPreserve(x, E))
proc `encode`*[E](x: TcpRemote | TcpLocal | TcpPeerInfo[E]): seq[byte] =
proc encode*[E](x: TcpPeerInfo[E]): seq[byte] =
encode(toPreserve(x, E))
proc `$`*(x: TcpLocal | TcpRemote): string =
`$`(toPreserve(x))
proc encode*(x: TcpLocal | TcpRemote): seq[byte] =
encode(toPreserve(x))

View File

@ -3,74 +3,28 @@ import
std/typetraits, preserves
type
SetTimer*[E = void] {.record: "set-timer".} = ref object ## ``<set-timer @label any @msecs double @kind TimerKind>``
TimerExpired*[E] {.preservesRecord: "timer-expired".} = ref object
`label`*: Preserve[E]
`msecs`*: float64
SetTimer*[E] {.preservesRecord: "set-timer".} = ref object
`label`*: Preserve[E]
`msecs`*: float64
`kind`*: TimerKind
TimerExpired*[E = void] {.record: "timer-expired".} = ref object ## ``<timer-expired @label any @msecs double>``
`label`*: Preserve[E]
`msecs`*: float64
TimerKind* {.pure.} = enum ## ``/ =<<lit>relative> / =<<lit>absolute> / =<<lit>clear>``
`TimerKind`* {.preservesOr.} = enum
`relative`, `absolute`, `clear`
LaterThan* {.record: "later-than".} = ref object ## ``<later-than @msecs double>``
LaterThan* {.preservesRecord: "later-than".} = object
`msecs`*: float64
proc `setTimer`*[E = void](`label`: Preserve[E]; `msecs`: float64 | Preserve[E];
`kind`: TimerKind | Preserve[E]): Preserve[E] =
## Preserves constructor for ``SetTimer``.
initRecord[E](symbol[E]("set-timer"), toPreserve(`label`, E),
toPreserve(`msecs`, E), toPreserve(`kind`, E))
proc toPreserveHook*(`settimer`: SetTimer; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("set-timer"), toPreserve(`settimer`.`label`, E),
toPreserve(`settimer`.`msecs`, E),
toPreserve(`settimer`.`kind`, E))
proc `timerExpired`*[E = void](`label`: Preserve[E];
`msecs`: float64 | Preserve[E]): Preserve[E] =
## Preserves constructor for ``TimerExpired``.
initRecord[E](symbol[E]("timer-expired"), toPreserve(`label`, E),
toPreserve(`msecs`, E))
proc toPreserveHook*(`timerexpired`: TimerExpired; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("timer-expired"),
toPreserve(`timerexpired`.`label`, E),
toPreserve(`timerexpired`.`msecs`, E))
proc toPreserveHook*(v: TimerKind; E: typedesc): Preserve[E] =
case v
of TimerKind.`relative`:
symbol[E]("relative")
of TimerKind.`absolute`:
symbol[E]("absolute")
of TimerKind.`clear`:
symbol[E]("clear")
proc fromPreserveHook*[E](v: var TimerKind; pr: Preserve[E]): bool =
if isSymbol(pr):
case pr.symbol
of "relative":
v = TimerKind.`relative`
result = true
of "absolute":
v = TimerKind.`absolute`
result = true
of "clear":
v = TimerKind.`clear`
result = true
proc `laterThan`*[E = void](`msecs`: float64 | Preserve[E]): Preserve[E] =
## Preserves constructor for ``LaterThan``.
initRecord[E](symbol[E]("later-than"), toPreserve(`msecs`, E))
proc toPreserveHook*(`laterthan`: LaterThan; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("later-than"), toPreserve(`laterthan`.`msecs`, E))
proc `$`*[E](x: SetTimer[E] | TimerExpired[E] | TimerKind | LaterThan): string =
proc `$`*[E](x: TimerExpired[E] | SetTimer[E]): string =
`$`(toPreserve(x, E))
proc `encode`*[E](x: SetTimer[E] | TimerExpired[E] | TimerKind | LaterThan): seq[
byte] =
proc encode*[E](x: TimerExpired[E] | SetTimer[E]): seq[byte] =
encode(toPreserve(x, E))
proc `$`*(x: LaterThan): string =
`$`(toPreserve(x))
proc encode*(x: LaterThan): seq[byte] =
encode(toPreserve(x))

View File

@ -3,51 +3,20 @@ import
std/typetraits, preserves
type
Tcp* {.record: "tcp".} = ref object ## ``<tcp @host string @port int>``
WebSocket* {.preservesRecord: "ws".} = object
`url`*: string
Stdio* {.preservesRecord: "stdio".} = object
Unix* {.preservesRecord: "unix".} = object
`path`*: string
Tcp* {.preservesRecord: "tcp".} = object
`host`*: string
`port`*: BiggestInt
Unix* {.record: "unix".} = ref object ## ``<unix @path string>``
`path`*: string
proc `$`*(x: WebSocket | Stdio | Unix | Tcp): string =
`$`(toPreserve(x))
WebSocket* {.record: "ws".} = ref object ## ``<ws @url string>``
`url`*: string
Stdio* {.record: "stdio".} = object ## ``<stdio>``
discard
proc `tcp`*[E = void](`host`: string | Preserve[E];
`port`: BiggestInt | Preserve[E]): Preserve[E] =
## Preserves constructor for ``Tcp``.
initRecord[E](symbol[E]("tcp"), toPreserve(`host`, E), toPreserve(`port`, E))
proc toPreserveHook*(`tcp`: Tcp; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("tcp"), toPreserve(`tcp`.`host`, E),
toPreserve(`tcp`.`port`, E))
proc `unix`*[E = void](`path`: string | Preserve[E]): Preserve[E] =
## Preserves constructor for ``Unix``.
initRecord[E](symbol[E]("unix"), toPreserve(`path`, E))
proc toPreserveHook*(`unix`: Unix; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("unix"), toPreserve(`unix`.`path`, E))
proc `webSocket`*[E = void](`url`: string | Preserve[E]): Preserve[E] =
## Preserves constructor for ``WebSocket``.
initRecord[E](symbol[E]("ws"), toPreserve(`url`, E))
proc toPreserveHook*(`websocket`: WebSocket; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("ws"), toPreserve(`websocket`.`url`, E))
proc `stdio`*[E = void](): Preserve[E] =
## Preserves constructor for ``Stdio``.
initRecord[E](symbol[E]("stdio"))
proc toPreserveHook*(`stdio`: Stdio; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("stdio"))
proc `$`*[E](x: Tcp | Unix | WebSocket | Stdio): string =
`$`(toPreserve(x, E))
proc `encode`*[E](x: Tcp | Unix | WebSocket | Stdio): seq[byte] =
encode(toPreserve(x, E))
proc encode*(x: WebSocket | Stdio | Unix | Tcp): seq[byte] =
encode(toPreserve(x))

View File

@ -3,22 +3,12 @@ import
std/typetraits, preserves
type
Instance*[E = void] {.record: "Instance".} = ref object ## ``<Instance @name string @argument any>``
Instance*[E] {.preservesRecord: "Instance".} = ref object
`name`*: string
`argument`*: Preserve[E]
proc `instance`*[E = void](`name`: string | Preserve[E]; `argument`: Preserve[E]): Preserve[
E] =
## Preserves constructor for ``Instance``.
initRecord[E](symbol[E]("Instance"), toPreserve(`name`, E),
toPreserve(`argument`, E))
proc toPreserveHook*(`instance`: Instance; E: typedesc): Preserve[E] =
initRecord[E](symbol[E]("Instance"), toPreserve(`instance`.`name`, E),
toPreserve(`instance`.`argument`, E))
proc `$`*[E](x: Instance[E]): string =
`$`(toPreserve(x, E))
proc `encode`*[E](x: Instance[E]): seq[byte] =
proc encode*[E](x: Instance[E]): seq[byte] =
encode(toPreserve(x, E))

390
src/syndicate/relay.nim Normal file
View File

@ -0,0 +1,390 @@
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/[asyncdispatch, options, tables]
import preserves, preserves/parse
import ../syndicate/protocols/[protocol, sturdy]
import ./actors, ./dataspaces
type Oid = sturdy.Oid
type
Assertion = Preserve[Ref]
WireAssertion = Preserve[WireRef]
WireRef = sturdy.WireRef[Ref]
Packet = protocol.Packet[WireRef]
Turn = actors.Turn
WireSymbol = ref object
oid: Oid
`ref`: Ref
membrane: Membrane
count: int
Membrane = object
byOid: Table[Oid, WireSymbol]
byRef: Table[Ref, WireSymbol]
#[
proc `$`(ws: WireSymbol): string =
"<ws:" & $ws.oid & "/" & $ws.count & "/" & $ws.`ref` &>"
]#
proc grab(mb: var Membrane; key: Oid|Ref; transient: bool; alloc: proc(): WireSymbol {.gcsafe.}): WireSymbol {.deprecated: "not idomatic Nim".} =
when key is Oid:
result = mb.byOid.getOrDefault(key)
elif key is ref:
result = mb.byRef.getOrDefault(key)
if result.isNil:
result = alloc()
mb.byOid[result.oid] = result
mb.byRef[result.`ref`] = result
if not transient: inc result.count
proc drop(mb: var Membrane; ws: WireSymbol) =
dec ws.count
if ws.count < 1:
mb.byOid.del ws.oid
mb.byRef.del ws.`ref`
type
PacketWriter = proc (bs: seq[byte]): Future[void] {.gcsafe.}
RelaySetup = proc (turn: var Turn; relay: Relay) {.gcsafe.}
Relay = ref object of RootObj
facet: Facet
inboundAssertions: Table[Handle,
tuple[localHandle: Handle, imported: seq[WireSymbol]]]
outboundAssertions: Table[Handle, seq[WireSymbol]]
exported: Membrane
imported: Membrane
nextLocalOid: Oid
pendingTurn: protocol.Turn[WireRef]
packetWriter: PacketWriter
untrusted: bool
SyncPeerEntity = ref object of Entity
relay: Relay
peer: Ref
handleMap: Table[Handle, Handle]
e: WireSymbol
RelayEntity = ref object of Entity
label: string
relay: Relay
#[
proc newSyncPeerEntity(r: Relay; p: Ref): SyncPeerEntity =
SyncPeerEntity(relay: r, peer: p)
]#
proc releaseRefOut(r: Relay; e: WireSymbol) =
r.exported.drop e
method publish(se: SyncPeerEntity; t: var Turn; v: Assertion; h: Handle) =
se.handleMap[h] = publish(t, se.peer, v)
method retract(se: SyncPeerEntity; t: var Turn; h: Handle) =
var other: Handle
if se.handleMap.pop(h, other):
retract(t, other)
method message(se: SyncPeerEntity; t: var Turn; v: Assertion) =
if not se.e.isNil:
se.relay.releaseRefOut(se.e)
message(t, se.peer, v)
method sync(se: SyncPeerEntity; t: var Turn; peer: Ref) =
sync(t, se.peer, peer)
proc newRelayEntity(label: string; r: Relay; o: Oid): RelayEntity =
RelayEntity(label: label, relay: r, oid: o)
#[
proc `$`(ws: WireSymbol): string =
"<ws:" & $ws.oid & "/" & $ws.count & "/" & $ws.`ref` &>"
]#
proc `$`(re: RelayEntity): string =
"<Relay:" & re.label & ":" & $re.oid & ">"
proc rewriteRefOut(relay: Relay; `ref`: Ref; transient: bool; exported: var seq[WireSymbol]): WireRef =
#[
if not relay.untrusted:
result = WireRef(
orKind: WirerefKind.yours,
yours: WireRefYours[Ref](
oid: `ref`.target.oid,
attenuation: `ref`.attenuation))
]#
let e = grab(relay.exported, `ref`, transient) do () -> WireSymbol:
assert(not transient, "Cannot send transient reference")
result = WireSymbol( oid: relay.nextLocalOid, `ref`: `ref`)
inc relay.nextLocalOid
exported.add e
WireRef(
orKind: WireRefKind.mine,
mine: WireRefMine(oid: e.oid))
proc rewriteOut(relay: Relay; v: Assertion; transient: bool):
tuple[rewritten: WireAssertion, exported: seq[WireSymbol]] =
var exported: seq[WireSymbol]
var rewritten = mapEmbeds[Ref, WireRef](v) do (r: Ref) -> WireRef:
result = rewriteRefOut(relay, r, transient, exported)
(rewritten, exported)
proc register(relay: Relay; v: Assertion; h: Handle): WireAssertion =
var (rewritten, exported) = rewriteOut(relay, v, false)
relay.outboundAssertions[h] = exported
rewritten
proc deregister(relay: Relay; h: Handle) =
var outbound: seq[WireSymbol]
if relay.outboundAssertions.pop(h, outbound):
for e in outbound: releaseRefOut(relay, e)
proc send(r: Relay; msg: seq[byte]): Future[void] =
assert(not r.packetWriter.isNil, "missing packetWriter proc")
r.packetWriter(msg)
proc send(r: Relay; rOid: protocol.Oid; m: Event[WireRef]) =
if r.pendingTurn.len == 0:
callSoon:
r.facet.run do (turn: var Turn):
var pkt = $Packet(
orKind: PacketKind.Turn,
turn: move r.pendingTurn)
echo "C: ", pkt
#asyncCheck(turn, r.send(encode pkt))
asyncCheck(turn, r.send(cast[seq[byte]](pkt)))
r.pendingTurn.add TurnEvent[WireRef](oid: rOid, event: m)
proc send(re: RelayEntity; ev: Event) =
send(re.relay, protocol.Oid re.oid, ev)
method publish(re: RelayEntity; t: var Turn; v: Assertion; h: Handle) =
var ev = protocol.Event[WireRef](
orKind: EventKind.Assert,
`assert`: protocol.Assert[WireRef](
assertion: re.relay.register(v, h),
handle: h))
re.send ev
method retract(re: RelayEntity; t: var Turn; h: Handle) =
re.relay.deregister h
re.send Event[WireRef](
orKind: EventKind.Retract,
retract: Retract(handle: h))
method message(re: RelayEntity; turn: var Turn; msg: Assertion) =
var ev = Event[WireRef](orKind: EventKind.Message)
var (body, _) = rewriteOut(re.relay, msg, true)
ev.message.body = body
re.send ev
method sync(re: RelayEntity; turn: var Turn; peer: Ref) =
var
peerEntity = SyncPeerEntity(relay: re.relay, peer: peer)
exported: seq[WireSymbol]
discard rewriteRefOut(re.relay, turn.newRef(peerEntity), false, exported)
# TODO: discard?
peerEntity.e = exported[0]
re.send Event[WireRef](
orKind: EventKind.Sync,
sync: Sync[WireRef](peer: embed toPreserve(false, WireRef))) # TODO: send the WireRef?
using
relay: Relay
facet: Facet
proc lookupLocal(relay; oid: Oid): Ref =
try: relay.exported.byOid[oid].`ref`
except KeyError: newInertRef()
proc isInert(r: Ref): bool =
r.target.isNil
proc rewriteRefIn(relay; facet; n: WireRef, imported: var seq[WireSymbol]): Ref =
case n.orKind
of WireRefKind.mine:
let e = relay.imported.grab(n.mine.oid, false) do () -> WireSymbol:
WireSymbol(
oid: n.mine.oid,
`ref`: newRef(facet, newRelayEntity("rewriteRefIn", relay, n.mine.oid)))
imported.add e
result = e.`ref`
of WireRefKind.yours:
let r = relay.lookupLocal(n.yours.oid)
if n.yours.attenuation.len == 0 or r.isInert:
result = r
else:
raiseAssert "attenuation not implemented"
proc rewriteIn(relay; facet; a: Preserve[WireRef]):
tuple[rewritten: Assertion; imported: seq[WireSymbol]] =
var
imported: seq[WireSymbol]
rewritten = mapEmbeds(a) do (wr: WireRef) -> Ref:
rewriteRefIn(relay, facet, wr, imported)
(rewritten, imported)
proc close(r: Relay) = discard
proc dispatch(relay: Relay; turn: var Turn; `ref`: Ref; event: Event[WireRef]) =
case event.orKind
of EventKind.Assert:
let (a, imported) = rewriteIn(relay, turn.activeFacet, event.assert.assertion)
relay.inboundAssertions[event.assert.handle] =
(turn.publish(`ref`, a), imported,)
of EventKind.Retract:
let remoteHandle = event.retract.handle
var outbound: tuple[localHandle: Handle, imported: seq[WireSymbol]]
if relay.inboundAssertions.pop(remoteHandle, outbound):
for e in outbound.imported: relay.imported.drop e
turn.retract(outbound.localHandle)
of EventKind.Message:
let (a, imported) = rewriteIn(relay, turn.activeFacet, event.message.body)
assert imported.len == 0, "Cannot receive transient reference"
turn.message(`ref`, a)
of EventKind.Sync:
discard # TODO
#[
var imported: seq[WireSymbol]
let k = relay.rewriteRefIn(turn, evenr.sync.peer, imported)
turn.sync(`ref`) do (turn: var Turn):
turn.message(k, true)
for e in imported: relay.imported.del e
]#
proc dispatch(relay: Relay; v: Preserve[WireRef]) =
run(relay.facet) do (t: var Turn):
var pkt: Packet
if fromPreserve(pkt, v):
case pkt.orKind
of PacketKind.Turn:
for te in pkt.turn:
dispatch(relay, t, lookupLocal(relay, te.oid.Oid), te.event)
of PacketKind.Error:
relay.facet.log("Error from server: ", pkt.error.message, " (detail: ", pkt.error.detail, ")")
close relay
proc recv(relay: Relay; buf: seq[byte]) =
# var pkt = decodePreserves(buf, WireRef)
var pkt = cast[Preserve[WireRef]](
parsePreserves(cast[string](buf), sturdy.WireRef[void]))
# the compiler cannot convert `Preserve[void]` to `Preserve[WireRef[Ref]]`
# so convert to `Preserve[WireRef[void]]` and cast.
echo "S: ", pkt
dispatch(relay, pkt)
type
RelayOptions = object of RootObj
packetWriter: PacketWriter
setup: RelaySetup
untrusted: bool
RelayActorOptions = object of RelayOptions
initialOid: Option[Oid]
initialRef: Ref
nextLocalOid: Option[Oid]
proc newRelay(turn: var Turn; opts: RelayOptions): Relay =
result = Relay(
facet: turn.activeFacet,
packetWriter: opts.packetWriter,
untrusted: opts.untrusted)
discard result.facet.preventInertCheck()
opts.setup(turn, result)
proc spawnRelay(name: string; turn: var Turn; opts: RelayActorOptions): Future[Ref] =
var fut = newFuture[Ref]"spawnRelay"
spawn(name, turn) do (turn: var Turn):
let relay = newRelay(turn, opts)
if not opts.initialRef.isNil:
var exported: seq[WireSymbol]
discard rewriteRefOut(relay, opts.initialRef, false, exported)
if opts.initialOid.isSome:
var imported: seq[WireSymbol]
var wr = WireRef(
orKind: WireRefKind.mine,
mine: WireRefMine(oid: opts.initialOid.get))
fut.complete rewriteRefIn(relay, turn.activeFacet, wr, imported)
else:
fut.complete(nil)
opts.nextLocalOid.map do (oid: Oid):
relay.nextLocalOid =
if oid == 0.Oid: 1.Oid
else: oid
fut
import std/[asyncdispatch, asyncnet]
from std/nativesockets import AF_UNIX, SOCK_STREAM, Protocol
import protocols/gatekeeper
type ShutdownEntity = ref object of Entity
method publish(e: ShutdownEntity; t: var Turn; v: Assertion; h: Handle) = discard
method retract(e: ShutdownEntity; turn: var Turn; h: Handle) =
stopActor(turn)
type
SturdyRef = sturdy.SturdyRef[Ref]
Resolve = gatekeeper.Resolve[Ref]
proc connectUnix*(turn: var Turn; path: string; cap: SturdyRef; bootProc: DuringProc) =
var socket = newAsyncSocket(
domain = AF_UNIX,
sockType = SOCK_STREAM,
protocol = cast[Protocol](0),
buffered = false)
proc socketWriter(packet: seq[byte]): Future[void] =
socket.send cast[string](packet)
const recvSize = 4096
var shutdownRef: Ref
let reenable = turn.activeFacet.preventInertCheck()
let connectionClosedRef = newRef(turn, ShutdownEntity())
proc setup(turn: var Turn; relay: Relay) =
let facet = turn.activeFacet
proc recvCb(pktFut: Future[string]) {.gcsafe.} =
let buf = cast[seq[byte]](pktFut.read)
if buf.len == 0:
run(facet) do (turn: var Turn): stopActor(turn)
else:
relay.recv(buf )
socket.recv(recvSize).addCallback(recvCb)
# TODO: should this need be callSoon?
socket.recv(recvSize).addCallback(recvCb)
turn.activeFacet.actor.atExit do (turn: var Turn): close(socket)
discard publish(turn, connectionClosedRef, true)
shutdownRef = newRef(turn, ShutdownEntity())
var fut = newFuture[void]"connectUnix"
connectUnix(socket, path).addCallback do (f: Future[void]):
read f
discard newActor("unix") do (turn: var Turn):
let relayFut = spawnRelay("unix", turn, RelayActorOptions(
packetWriter: socketWriter,
setup: setup,
initialOid: 0.Oid.some))
relayFut.addCallback do (refFut: Future[Ref]):
let gatekeeper = read refFut
run(gatekeeper.relay) do (turn: var Turn):
reenable()
discard publish(turn, shutdownRef, true)
proc duringCallback(turn: var Turn; ds: Preserve[Ref]): TurnAction =
let facet = facet(turn) do (turn: var Turn):
discard bootProc(turn, ds) # TODO: what to do with this?
proc action(turn: var Turn) =
stop(turn, facet)
result = action
var res = Resolve(
sturdyref: cap,
observer: embed newRef(turn, during(duringCallback)))
discard publish(turn, gatekeeper, res)
fut.complete()
asyncCheck(turn, fut)

View File

@ -1,50 +1,45 @@
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/[asyncdispatch, asyncfile, posix, random, strutils]
import preserves
import syndicate, syndicate/protocols/schemas/simpleChatProtocol, syndicate/sturdy
import std/[asyncdispatch, asyncfile, random, strutils]
import preserves, preserves/parse
import syndicate/protocols/[simpleChatProtocol, sturdy]
import syndicate/[actors, dataspaces, patterns, relay]
from syndicate/protocols/protocol import Handle
from os import getCurrentProcessId
randomize()
syndicate chat:
const capStr = """<ref "syndicate" [] #x"a6480df5306611ddd0d3882b546e1977">"""
let me = "user_" & $rand(range[10..1000])
proc noOp(turn: var Turn) = discard
spawn "debug":
onAsserted(?s) do (s: Preserve):
echo " asserted ", s
onRetracted(?s) do (s: Preserve):
echo " retracted ", s
onMessage(?s) do (s: Preserve):
echo " message ", s
waitFor runActor("chat") do (turn: var Turn):
spawn "log":
during(present(?who)) do (who: string):
echo who, " joined"
onStop:
echo who, " left"
onMessage(says(?who, ?what)) do (who: string; what: string):
echo who, " says ", what
var cap: SturdyRef[Ref]
doAssert fromPreserve(cap, parsePreserves(capStr, Ref))
spawn "chat":
publish present(me)
during (present(me)):
let
inputFacet = getCurrentFacet()
af = newAsyncFile(AsyncFD STDIN_FILENO)
inputFacet.beginExternalTask()
proc readStdin() =
readline(af).addCallback do (f: Future[string]):
if f.failed:
inputFacet.endExternalTask()
else:
callSoon:
readStdin()
let line = read f
if line.len > 0:
let a = says(me, strip line)
send a
readStdin()
connectUnix(turn, "/run/syndicate/ds", cap) do (turn: var Turn; a: Assertion) -> TurnAction:
let ds = unembed a
var
username: string
usernameHandle: Handle
waitFor chat()
proc updateUsername(turn: var Turn; u: string) =
username = u
usernameHandle = replace(turn, ds,
usernameHandle, Present(username: username))
updateUsername(turn, "user" & $getCurrentProcessId())
echo "username updated?"
proc duringPresent(turn: var Turn; a: Assertion): TurnAction =
echo "observed ", a
noOp
discard observe(turn, ds, toPattern(Present), during(duringPresent))
echo "post-observe"
echo "actor completed"