WiP! Value transition

This commit is contained in:
Emery Hemingway 2024-01-01 20:18:30 +02:00
parent a0355637d8
commit 1e107131d8
13 changed files with 132 additions and 111 deletions

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
/nim.cfg
/tests/*.run
*.check
*.run

View File

@ -13,6 +13,8 @@ import ./syndicate/protocols/dataspace
export actors, dataspaces, patterns
type Assertion* {.deprecated: "Assertion and Preserve[void] replaced by Value".} = Value
proc `!`*(typ: static typedesc): Pattern {.inline.} =
patterns.dropType(typ)
@ -33,9 +35,9 @@ proc `??`*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern {.inline.
type
Observe* = dataspace.Observe
PublishProc = proc (turn: var Turn; v: Assertion; h: Handle) {.closure, gcsafe.}
PublishProc = proc (turn: var Turn; v: Value; h: Handle) {.closure, gcsafe.}
RetractProc = proc (turn: var Turn; h: Handle) {.closure, gcsafe.}
MessageProc = proc (turn: var Turn; v: Assertion) {.closure, gcsafe.}
MessageProc = proc (turn: var Turn; v: Value) {.closure, gcsafe.}
ClosureEntity = ref object of Entity
publishImpl: PublishProc
retractImpl: RetractProc
@ -92,7 +94,7 @@ proc wrapPublishHandler(turn, handler: NimNode): NimNode =
handlerSym = genSym(nskProc, "publish")
bindingsSym = ident"bindings"
quote do:
proc `handlerSym`(`turn`: var Turn; `bindingsSym`: Assertion; `handleSym`: Handle) =
proc `handlerSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle) =
`varSection`
if fromPreserves(`valuesSym`, bindings):
`publishBody`
@ -104,7 +106,7 @@ proc wrapMessageHandler(turn, handler: NimNode): NimNode =
handlerSym = genSym(nskProc, "message")
bindingsSym = ident"bindings"
quote do:
proc `handlerSym`(`turn`: var Turn; `bindingsSym`: Assertion) =
proc `handlerSym`(`turn`: var Turn; `bindingsSym`: Value) =
`varSection`
if fromPreserves(`valuesSym`, bindings):
`body`
@ -118,13 +120,13 @@ proc wrapDuringHandler(turn, entryBody, exitBody: NimNode): NimNode =
duringSym = genSym(nskProc, "during")
if exitBody.isNil:
quote do:
proc `duringSym`(`turn`: var Turn; `bindingsSym`: Assertion; `handleSym`: Handle): TurnAction =
proc `duringSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle): TurnAction =
`varSection`
if fromPreserves(`valuesSym`, `bindingsSym`):
`publishBody`
else:
quote do:
proc `duringSym`(`turn`: var Turn; `bindingsSym`: Assertion; `handleSym`: Handle): TurnAction =
proc `duringSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle): TurnAction =
`varSection`
if fromPreserves(`valuesSym`, `bindingsSym`):
`publishBody`

View File

@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: ☭ Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/[asyncfutures, deques, hashes, monotimes, options, sets, tables, times]
import std/[asyncfutures, hashes, monotimes, options, sets, tables, times]
import preserves
import ../syndicate/protocols/[protocol, sturdy]
@ -27,7 +27,6 @@ generateIdType(TurnId)
type
Oid = sturdy.Oid
Assertion* = Value
Caveat = sturdy.Caveat
Attenuation = seq[Caveat]
Rewrite = sturdy.Rewrite
@ -40,7 +39,7 @@ type
Entity* = ref object of RootObj
oid*: Oid # oid is how Entities are identified over the wire
Cap* {.unpreservable.} = ref object
Cap* {.final.} = ref object of EmbeddedObj
relay*: Facet
target*: Entity
attenuation*: Attenuation
@ -166,7 +165,7 @@ proc enqueue(turn: var Turn; target: Facet; action: TurnAction) =
type Bindings = Table[Value, Value]
proc match(bindings: var Bindings; p: Pattern; v: Assertion): bool =
proc match(bindings: var Bindings; p: Pattern; v: Value): bool =
case p.orKind
of PatternKind.Pdiscard: result = true
of PatternKind.Patom:
@ -220,18 +219,19 @@ proc match(bindings: var Bindings; p: Pattern; v: Assertion): bool =
result = true
break
proc match(p: Pattern; v: Assertion): Option[Bindings] =
proc match(p: Pattern; v: Value): Option[Bindings] =
var b: Bindings
if match(b, p, v):
result = some b
proc instantiate(t: Template; bindings: Bindings): Assertion =
proc instantiate(t: Template; bindings: Bindings): Value =
case t.orKind
of TemplateKind.Tattenuate:
let v = instantiate(t.tattenuate.template, bindings)
if not v.isEmbedded:
let cap = v.unembed(Cap)
if cap.isNone:
raise newException(ValueError, "Attempt to attenuate non-capability")
result = embed(attenuate(v.embed, t.tattenuate.attenuation))
result = attenuate(get cap, t.tattenuate.attenuation).embed
of TemplateKind.TRef:
let n = $t.tref.binding.int
try: result = bindings[n.toPreserves]
@ -254,12 +254,12 @@ proc instantiate(t: Template; bindings: Bindings): Assertion =
for key, tt in t.tcompound.dict.entries:
result[key] = instantiate(tt, bindings)
proc rewrite(r: Rewrite; v: Assertion): Assertion =
proc rewrite(r: Rewrite; v: Value): Value =
let bindings = match(r.pattern, v)
if bindings.isSome:
result = instantiate(r.template, get bindings)
proc examineAlternatives(cav: Caveat; v: Assertion): Assertion =
proc examineAlternatives(cav: Caveat; v: Value): Value =
case cav.orKind
of CaveatKind.Rewrite:
result = rewrite(cav.rewrite, v)
@ -270,13 +270,13 @@ proc examineAlternatives(cav: Caveat; v: Assertion): Assertion =
of CaveatKind.Reject: discard
of CaveatKind.unknown: discard
proc runRewrites*(a: Attenuation; v: Assertion): Assertion =
proc runRewrites*(a: Attenuation; v: Value): Value =
result = v
for stage in a:
result = examineAlternatives(stage, result)
if result.isFalse: break
proc publish(turn: var Turn; r: Cap; v: Assertion; h: Handle) =
proc publish(turn: var Turn; r: Cap; v: Value; h: Handle) =
var a = runRewrites(r.attenuation, v)
if not a.isFalse:
let e = OutboundAssertion(
@ -298,7 +298,7 @@ proc publish(turn: var Turn; r: Cap; v: Assertion; h: Handle) =
turn.desc.actions.add act
proc publish*(turn: var Turn; r: Cap; a: Assertion): Handle =
proc publish*(turn: var Turn; r: Cap; a: Value): Handle =
result = turn.facet.nextHandle()
publish(turn, r, a, result)
@ -316,7 +316,7 @@ proc retract*(turn: var Turn; h: Handle) =
if turn.facet.outbound.pop(h, e):
turn.retract(e)
proc message*(turn: var Turn; r: Cap; v: Assertion) =
proc message*(turn: var Turn; r: Cap; v: Value) =
var a = runRewrites(r.attenuation, v)
if not a.isFalse:
enqueue(turn, r.relay) do (turn: var Turn):

View File

@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: ☭ 2022 Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/[hashes, tables]
import std/[hashes, options, tables]
import preserves
import ./actors, ./protocols/dataspace, ./skeletons
@ -18,18 +18,21 @@ type
method publish(ds: Dataspace; turn: var Turn; a: AssertionRef; h: Handle) {.gcsafe.} =
if add(ds.index, turn, a.value):
var obs: Observe
if obs.fromPreserves(a.value):
ds.index.add(turn, obs.pattern, obs.observer)
var obs = a.value.preservesTo(Observe)
if obs.isSome:
var cap = obs.get.observer.unembed(Cap)
if cap.isSome:
ds.index.add(turn, obs.get.pattern, cap.get)
ds.handleMap[h] = a.value
method retract(ds: Dataspace; turn: var Turn; h: Handle) {.gcsafe.} =
let v = ds.handleMap[h]
if remove(ds.index, turn, v):
ds.handleMap.del h
var obs: Observe
if obs.fromPreserves v:
ds.index.remove(turn, obs.pattern, obs.observer)
var obs = v.preservesTo(Observe)
if obs.isSome:
var cap = obs.get.observer.unembed(Cap)
ds.index.remove(turn, obs.get.pattern, cap.get)
method message(ds: Dataspace; turn: var Turn; a: AssertionRef) {.gcsafe.} =
ds.index.deliverMessage(turn, a.value)

View File

@ -12,7 +12,7 @@ type
Turn = actors.Turn
type
DuringProc* = proc (turn: var Turn; a: Assertion; h: Handle): TurnAction {.gcsafe.}
DuringProc* = proc (turn: var Turn; a: Value; h: Handle): TurnAction {.gcsafe.}
DuringActionKind = enum null, dead, act
DuringAction = object
case kind: DuringActionKind
@ -51,4 +51,4 @@ method retract(de: DuringEntity; turn: var Turn; h: Handle) =
proc during*(cb: DuringProc): DuringEntity = DuringEntity(cb: cb)
proc observe*(turn: var Turn; ds: Cap; pat: Pattern; e: Entity): Handle =
publish(turn, ds, Observe(pattern: pat, observer: newCap(turn, e)))
publish(turn, ds, Observe(pattern: pat, observer: newCap(turn, e).embed))

View File

@ -57,7 +57,7 @@ proc drop*(): Pattern {.inline.} = Pattern(orKind: PatternKind.DDiscard)
proc grab*(): Pattern {.inline.} = DBind(pattern: drop()).toPattern
## Create a pattern to capture any value.
proc grab*[T](pr: Value): Pattern =
proc grab*(pr: Value): Pattern =
## Convert a `Preserve` value to a `Pattern`.
runnableExamples:
from std/unittest import check
@ -66,42 +66,43 @@ proc grab*[T](pr: Value): Pattern =
$(grab parsePreserves"""<foo "bar" #"00" [0 1 2.0] {maybe: #t} <_>>""") ==
"""<rec foo [<lit "bar"> <lit #"00"> <arr [<lit 0> <lit 1> <lit 2.0>]> <dict {maybe: <lit #t>}> <_>]>"""
assert not pr.embedded
case pr.kind
of pkBoolean:
AnyAtom(orKind: AnyAtomKind.`bool`, bool: pr.bool).toPattern
of pkFloat:
AnyAtom(orKind: AnyAtomKind.`float`, float: pr.float).toPattern
of pkDouble:
AnyAtom(orKind: AnyAtomKind.`double`, double: pr.double).toPattern
of pkRegister:
AnyAtom(orKind: AnyAtomKind.`int`, int: pr.register).toPattern
of pkString:
AnyAtom(orKind: AnyAtomKind.`string`, string: pr.string).toPattern
of pkByteString:
AnyAtom(orKind: AnyAtomKind.`bytes`, bytes: pr.bytes).toPattern
of pkSymbol:
AnyAtom(orKind: AnyAtomKind.`symbol`, symbol: pr.symbol).toPattern
of pkRecord:
if (pr.isRecord("_") and pr.arity == 0) or (pr.isRecord("bind") and pr.arity == 1):
if pr.embedded: drop()
else:
case pr.kind
of pkBoolean:
AnyAtom(orKind: AnyAtomKind.`bool`, bool: pr.bool).toPattern
of pkFloat:
AnyAtom(orKind: AnyAtomKind.`float`, float: pr.float).toPattern
of pkDouble:
AnyAtom(orKind: AnyAtomKind.`double`, double: pr.double).toPattern
of pkRegister:
AnyAtom(orKind: AnyAtomKind.`int`, int: pr.register).toPattern
of pkString:
AnyAtom(orKind: AnyAtomKind.`string`, string: pr.string).toPattern
of pkByteString:
AnyAtom(orKind: AnyAtomKind.`bytes`, bytes: pr.bytes).toPattern
of pkSymbol:
AnyAtom(orKind: AnyAtomKind.`symbol`, symbol: pr.symbol).toPattern
of pkRecord:
if (pr.isRecord("_") and pr.arity == 0) or (pr.isRecord("bind") and pr.arity == 1):
drop()
else:
DCompoundRec(
label: pr.label,
fields: map[Value, Pattern](pr.fields, grab)).toPattern
of pkSequence:
DCompoundArr(items: map(pr.sequence, grab)).toPattern
of pkSet:
raiseAssert "cannot construct a pattern over a set literal"
of pkDictionary:
var dict = DCompoundDict()
for key, val in pr.pairs: dict.entries[key] = grab val
dict.toPattern
of pkEmbedded:
# TODO: can patterns be constructed over embedded literals?
drop()
else:
DCompoundRec(
label: pr.label,
fields: map[Value, Pattern](pr.fields, grab)).toPattern
of pkSequence:
DCompoundArr(items: map(pr.sequence, grab)).toPattern
of pkSet:
raiseAssert "cannot construct a pattern over a set literal"
of pkDictionary:
var dict = DCompoundDict()
for key, val in pr.pairs: dict.entries[key] = grab val
dict.toPattern
of pkEmbedded:
# TODO: can patterns be constructed over embedded literals?
drop()
else:
raise newException(ValueError, "cannot generate a pattern for unhandled Value type")
raise newException(ValueError, "cannot generate a pattern for unhandled Value type")
proc grab*[T](x: T): Pattern =
## Construct a `Pattern` from value of type `T`.
@ -382,12 +383,12 @@ func projectPaths*(v: Value; paths: Paths): Option[Captures] =
else: return
some res
func matches*(pat: Pattern; pr: Value): bool =
proc matches*(pat: Pattern; pr: Value): bool =
let analysis = analyse(pat)
assert analysis.constPaths.len == analysis.constValues.len
for i, path in analysis.constPaths:
let v = step(pr, path)
if v.isNone : return false
if v.isNone: return false
if analysis.constValues[i] != v.get: return false
for path in analysis.capturePaths:
if isNone step(pr, path): return false

View File

@ -5,7 +5,7 @@ import
type
Observe* {.preservesRecord: "Observe".} = object
`pattern`*: dataspacePatterns.Pattern
`observer`*: Value
`observer`* {.preservesEmbedded.}: Value
proc `$`*(x: Observe): string =
`$`(toPreserves(x))

View File

@ -5,7 +5,7 @@ import
type
Bind* {.preservesRecord: "bind".} = object
`description`*: Description
`target`*: Value
`target`* {.preservesEmbedded.}: Value
`observer`*: BindObserver
Route* {.preservesRecord: "route".} = object
@ -26,12 +26,12 @@ type
TransportConnection* {.preservesRecord: "connect-transport".} = object
`addr`*: Value
`control`*: TransportControl
`control`* {.preservesEmbedded.}: TransportControl
`resolved`*: Resolved
Step* = Value
ResolvedPathStep* {.preservesRecord: "path-step".} = object
`origin`*: Resolve
`origin`* {.preservesEmbedded.}: Resolve
`pathStep`*: PathStep
`resolved`*: Resolved
@ -57,12 +57,12 @@ type
Resolve* {.preservesRecord: "resolve".} = object
`step`*: Step
`observer`*: Resolved
`observer`* {.preservesEmbedded.}: Resolved
ResolvedKind* {.pure.} = enum
`accepted`, `Rejected`
ResolvedAccepted* {.preservesRecord: "accepted".} = object
`responderSession`*: Value
`responderSession`* {.preservesEmbedded.}: Value
`Resolved`* {.preservesOr.} = object
case orKind*: ResolvedKind
@ -77,7 +77,7 @@ type
ResolvePath* {.preservesRecord: "resolve-path".} = object
`route`*: Route
`addr`*: Value
`control`*: TransportControl
`control`* {.preservesEmbedded.}: TransportControl
`resolved`*: Resolved
PathStep* = Value

View File

@ -23,8 +23,8 @@ type
`message`*: string
StreamConnection* {.preservesRecord: "stream-connection".} = object
`source`*: Source
`sink`*: Sink
`source`* {.preservesEmbedded.}: Source
`sink`* {.preservesEmbedded.}: Sink
`spec`*: Value
`LineMode`* {.preservesOr, pure.} = enum
@ -32,7 +32,7 @@ type
SourceKind* {.pure.} = enum
`sink`, `StreamError`, `credit`
SourceSink* {.preservesRecord: "sink".} = object
`controller`*: Sink
`controller`* {.preservesEmbedded.}: Sink
SourceCredit* {.preservesRecord: "credit".} = object
`amount`*: CreditAmount
@ -53,7 +53,7 @@ type
SinkKind* {.pure.} = enum
`source`, `StreamError`, `data`, `eof`
SinkSource* {.preservesRecord: "source".} = object
`controller`*: Source
`controller`* {.preservesEmbedded.}: Source
SinkData* {.preservesRecord: "data".} = object
`payload`*: Value

View File

@ -8,7 +8,7 @@ type
`port`*: BiggestInt
TcpPeerInfo* {.preservesRecord: "tcp-peer".} = object
`handle`*: Value
`handle`* {.preservesEmbedded.}: Value
`local`*: TcpLocal
`remote`*: TcpRemote

View File

@ -39,20 +39,6 @@ proc classOf(p: Pattern): Class =
else:
Class(kind: classNone)
proc `$`(class: Class): string =
case class.kind
of classNone: result = "null"
of classRecord:
result.add($class.label)
result.add(':')
result.add($class.arity)
of classSequence:
result.add('[')
result.add($class.arity)
result.add(']')
of classDictionary:
result = "{…:…}"
type
EventKind = enum addedEvent, removedEvent, messageEvent
@ -78,9 +64,6 @@ func isEmpty(leaf: Leaf): bool =
func isEmpty(cont: Continuation): bool =
cont.cache.len == 0 and cont.leafMap.len == 0
proc `$`(x: Leaf|Continuation): string =
cast[uint](x[].unsafeAddr).toHex
type
ContinuationProc = proc (c: Continuation; v: Value) {.gcsafe.}
LeafProc = proc (l: Leaf; v: Value) {.gcsafe.}
@ -117,9 +100,6 @@ type
func isEmpty(node: Node): bool =
node.continuation.isEmpty and node.edges.len == 0
proc `$`(node: Node): string =
$(cast[uint](unsafeAddr node[]) and 0xffffff)
type TermStack = seq[Value]
proc push(stack: TermStack; val: Value): Termstack =
@ -288,7 +268,7 @@ proc adjustAssertion(index: var Index; turn: var Turn; outerValue: Value; delta:
c.cache.incl(v)
proc modLeaf(l: Leaf; v: Value) =
l.cache.incl(v)
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) {.gcsafe.} =
let change = group.cachedCaptures.change(vs, +1)
if change == cdAbsentToPresent:
for (observer, captureMap) in group.observers.pairs:
@ -301,20 +281,21 @@ proc adjustAssertion(index: var Index; turn: var Turn; outerValue: Value; delta:
c.cache.excl(v)
proc modLeaf(l: Leaf; v: Value) =
l.cache.excl(v)
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) {.gcsafe.} =
if group.cachedCaptures.change(vs, -1) == cdPresentToAbsent:
for (observer, captureMap) in group.observers.pairs:
retract(observer.target, turn, captureMap[vs])
captureMap.del(vs)
var h: Handle
if captureMap.take(vs, h):
retract(observer.target, turn, h)
modify(index.root, turn, outerValue, removedEvent, modContinuation, modLeaf, modObserver)
else: discard
proc continuationNoop(c: Continuation; v: Value) = discard
proc leafNoop(l: Leaf; v: Value) = discard
proc add*(index: var Index; turn: var Turn; v: Assertion): bool =
proc add*(index: var Index; turn: var Turn; v: Value): bool =
adjustAssertion(index, turn, v, +1)
proc remove*(index: var Index; turn: var Turn; v: Assertion): bool =
proc remove*(index: var Index; turn: var Turn; v: Value): bool =
adjustAssertion(index, turn, v, -1)
proc deliverMessage*(index: var Index; turn: var Turn; v: Value) =

View File

@ -1,6 +1,6 @@
# Package
version = "20231231"
version = "20240101"
author = "Emery Hemingway"
description = "Syndicated actors for conversational concurrency"
license = "Unlicense"

View File

@ -1,7 +1,7 @@
# SPDX-FileCopyrightText: ☭ Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/[tables, unittest]
import std/[options, tables, unittest]
import preserves, syndicate
@ -9,14 +9,14 @@ import ./test_schema
test "patterns":
let
observerPat = ?Observe(pattern: !Foo) ?? {0: grab()}
pat = ?Observe(pattern: !Foo) ?? {0: grab()}
text = """<rec Observe [<rec rec [<lit foo> <arr [<bind <_>> <_> <_>]>]> <_>]>"""
check($observerPat == text)
check($pat == text)
let
worte = @["alles", "in", "ordnung"]
observer = Observe(pattern: inject(?:Foo, { 0: ?worte })).toPreserves
have = capture(observerPat, observer).toPreserves.unpackLiterals
have = capture(pat, observer).toPreserves.unpackLiterals
want = [worte.toPreserves].toPreserves
check(have == want)
@ -52,7 +52,40 @@ type
test "literals":
const txt = """<rec request [<lit 3> <dict {artists: <lit "kyyyyym"> date: <lit "2023-10-14"> notes: <lit "Lots of stuff"> title: <lit "Domes show">}> <dict {front-cover: <dict {name: <lit "ADULT_TIME_Glielmi.jpg"> path: <lit "/tmp/652adad1b3d2b666dcc8d857.jpg"> size: <lit 255614> type: <lit "image/jpeg">}>}>]>"""
var pr = parsePreserves(txt, Cap)
var pr = parsePreserves(txt)
var capture: Literal[Request]
check capture.fromPreserves(pr)
suite "captures":
for txt in [
"#f",
"#t",
"0",
"-1",
"foo",
"<foo>",
"[0, 1, 2]",
]:
test txt:
let
pr = parsePreserves txt
pat = grab pr
checkpoint $pat
check pat.matches pr
suite "later-than":
let
obsA = parsePreserves"""<Observe <rec later-than [<lit 1704113731.419243>]> #!#f>"""
obsB = parsePreserves"""<Observe <rec Observe [<rec rec [<lit later-than> <arr [<rec lit [<bind <_>>]>]>]> <_>]> #!#f>"""
patA = """<rec later-than [<lit 1704113731.419243>]>""".parsePreserves.preservesTo(Pattern).get
patB = """<rec Observe [<rec rec [<lit later-than> <arr [<rec lit [<bind <_>>]>]>]> <_>]>""".parsePreserves.preservesTo(Pattern).get
patC = grab obsA
test $patC:
check patC.matches obsA
test $patB:
checkpoint $obsA
check patB.matches obsA