Parameterize Preserve type

This commit is contained in:
Emery Hemingway 2021-09-01 13:44:28 +02:00
parent cfd863fbf9
commit a5830a4a07
9 changed files with 72 additions and 51 deletions

View File

@ -1,14 +1,11 @@
# SPDX-License-Identifier: ISC # SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/[asyncdispatch, macros, options] import std/[asyncdispatch, macros, options]
import preserves, preserves/records import preserves, preserves/records
import syndicate/[assertions, dataspaces, events, skeletons] import syndicate/[assertions, dataspaces, events, skeletons]
export preserves.`%`
export preserves.fromPreserve export preserves.fromPreserve
export records.init
export assertions.`?_`
export assertions.`?*`
export assertions.Observe export assertions.Observe
export dataspaces.Facet export dataspaces.Facet
export dataspaces.FieldId export dataspaces.FieldId
@ -128,7 +125,7 @@ proc onEvent(event: EventKind, pattern, handler: NimNode): NimNode =
proc getCurrentFacet(): Facet {.inject, used.} = facet proc getCurrentFacet(): Facet {.inject, used.} = facet
`handler` `handler`
let a = `pattern` let a = `pattern`
result.assertion = Observe.init(a) result.assertion = observe(a)
result.analysis = some(analyzeAssertion(a)) result.analysis = some(analyzeAssertion(a))
result.callback = wrap(facet, EventKind(`event`), `handlerSym`) result.callback = wrap(facet, EventKind(`event`), `handlerSym`)
@ -197,13 +194,13 @@ template withFacet*(f: Facet; body: untyped): untyped =
## Execute a Syndicate ``body`` using the ``Facet`` at ``f``. ## Execute a Syndicate ``body`` using the ``Facet`` at ``f``.
runnableExamples: runnableExamples:
import preserves, preserves/records import preserves, preserves/records
type Foo = ref object type Foo {.record: "foo".} = ref object
facet: Facet facet: Facet
i: int i: int
proc incAndAssert(foo: Foo) = proc incAndAssert(foo: Foo) =
inc(foo.i) inc(foo.i)
withFacet foo.facet: withFacet foo.facet:
react: assert: initRecord("Foo", %foo.i) react: assert: foo
proc getCurrentFacet(): Facet {.inject, used.} = f proc getCurrentFacet(): Facet {.inject, used.} = f
body body
@ -219,3 +216,13 @@ type BootProc* = proc (facet: Facet) {.gcsafe.}
template boot*(module: BootProc) = template boot*(module: BootProc) =
mixin getCurrentFacet mixin getCurrentFacet
module(getCurrentFacet()) module(getCurrentFacet())
macro `?`*(x: untyped): untyped =
## Sugar for generating Syndicate patterns.
## `?_` is a pattern that matches but discards arbitrary
## values and `?` combined with any other identifier is
## a match and capture.
if eqIdent(x, "_"):
quote: toPreserve(Discard())
else:
quote: toPreserve(Capture())

View File

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

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: ISC # SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
# SPDX-License-Identifier: Unlicense
import ./bags, ./dataflow, ./events, ./skeletons import ./bags, ./dataflow, ./events, ./skeletons
import preserves import preserves
@ -70,7 +71,7 @@ type
Field* = object of RootObj Field* = object of RootObj
id*: FieldId id*: FieldId
Fields* = seq[Preserve] Fields* = seq[Value]
# TODO: compile-time tuples # TODO: compile-time tuples
Turn = object Turn = object
@ -403,7 +404,7 @@ proc initActivationAction(script: ActivationScript; name: string): Action =
ds.activations.add(script) ds.activations.add(script)
proc boot(root: Facet) = proc boot(root: Facet) =
root.addStartScript(script) root.addStartScript(script)
ds.addActor(name, boot, Preserve(kind: pkSet), actor) ds.addActor(name, boot, Value(kind: pkSet), actor)
Action(impl: impl, kind: activationAction) Action(impl: impl, kind: activationAction)
proc activate(facet; name: string; script: ActivationScript) = proc activate(facet; name: string; script: ActivationScript) =
@ -547,15 +548,15 @@ template declareField*(facet: Facet; F: untyped; T: typedesc; initial: T): untyp
let fieldOff = facet.fields.high let fieldOff = facet.fields.high
proc set(f: DistinctField; x: T) {.used.} = proc set(f: DistinctField; x: T) {.used.} =
facet.actor.dataspace.dataflow.recordDamage(f.id) facet.actor.dataspace.dataflow.recordDamage(f.id)
facet.fields[fieldOff] = toPreserve[T](x) facet.fields[fieldOff] = toPreserve(x)
proc set(f: DistinctField; x: Preserve) {.used.} = proc set(f: DistinctField; x: Value) {.used.} =
facet.actor.dataspace.dataflow.recordDamage(f.id) facet.actor.dataspace.dataflow.recordDamage(f.id)
facet.fields[fieldOff] = x facet.fields[fieldOff] = x
proc get(f: DistinctField): T {.used.} = proc get(f: DistinctField): T {.used.} =
facet.actor.dataspace.dataflow.recordObservation(f.id) facet.actor.dataspace.dataflow.recordObservation(f.id)
if not fromPreserve[T](result, facet.fields[fieldOff]): if not fromPreserve(result, facet.fields[fieldOff]):
raise newException(ValueError, "cannot convert field " & $F & " to " & $T) raise newException(ValueError, "cannot convert field " & $F & " to " & $T)
proc getPreserve(f: DistinctField): Preserve {.used.} = proc getPreserve(f: DistinctField): Value {.used.} =
facet.actor.dataspace.dataflow.recordObservation(f.id) facet.actor.dataspace.dataflow.recordObservation(f.id)
facet.fields[fieldOff] facet.fields[fieldOff]

View File

@ -2,7 +2,7 @@
import std/[asyncdispatch, monotimes, times] import std/[asyncdispatch, monotimes, times]
import preserves, preserves/records import preserves, preserves/records
import syndicate import syndicate, syndicate/assertions
type TimeLaterThan* {.record: "TimeLaterThan".} = object type TimeLaterThan* {.record: "TimeLaterThan".} = object
`deadline`*: Monotime `deadline`*: Monotime
@ -21,7 +21,7 @@ proc fromPreserveHook*(mt: var Monotime; p: Preserve): bool =
syndicate timerDriver: syndicate timerDriver:
spawn "timer": spawn "timer":
during(Observe % (prsTimeLaterThan(`?*`))) do (deadline: MonoTime): during(observe(prsTimeLaterThan(?deadline))) do (deadline: MonoTime):
let let
now = getMonoTime() now = getMonoTime()
period = inMilliseconds(deadline - now) period = inMilliseconds(deadline - now)

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: ISC # SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
# SPDX-License-Identifier: Unlicense
import ./assertions, ./bags, ./events import ./assertions, ./bags, ./events
import preserves, preserves/records import preserves, preserves/records
@ -36,9 +37,9 @@ proc projectPaths(v: Value; paths: seq[Path]): seq[Value] =
proc analyzeAssertion*(a: Value): Analysis = proc analyzeAssertion*(a: Value): Analysis =
var path: Path var path: Path
proc walk(analysis: var Analysis; a: Value): Skeleton[Shape] = proc walk(analysis: var Analysis; a: Value): Skeleton[Shape] =
if Discard.isClassOf a: if a.preserveTo(Discard).isSome:
discard discard
elif Capture.isClassOf a: elif a.preserveTo(Capture).isSome:
analysis.capturePaths.add(path) analysis.capturePaths.add(path)
result = walk(analysis, a.fields[0]) result = walk(analysis, a.fields[0])
else: else:

View File

@ -9,4 +9,4 @@ srcDir = "src"
# Dependencies # Dependencies
requires "nim >= 1.4.8", "preserves >= 0.3.0" requires "nim >= 1.4.8", "preserves >= 1.0.0"

View File

@ -1,9 +1,10 @@
## Date of generation: 2021-08-28 10:14 ## Date of generation: 2021-09-01 13:32
import import
std/typetraits, preserves std/typetraits, preserves
type type
EmbeddedType = void
BoxState* {.record: "box-state".} = object ## ``<box-state @value int>`` BoxState* {.record: "box-state".} = object ## ``<box-state @value int>``
`value`*: BiggestInt `value`*: BiggestInt
@ -11,7 +12,9 @@ type
`value`*: BiggestInt `value`*: BiggestInt
proc prsBoxState*(value: Preserve | BiggestInt): Preserve = proc prsBoxState*(value: Preserve | BiggestInt): Preserve =
initRecord(symbol("box-state"), value) initRecord[EmbeddedType](symbol("box-state", EmbeddedType),
toPreserve(value, EmbeddedType))
proc prsSetBox*(value: Preserve | BiggestInt): Preserve = proc prsSetBox*(value: Preserve | BiggestInt): Preserve =
initRecord(symbol("set-box"), value) initRecord[EmbeddedType](symbol("set-box", EmbeddedType),
toPreserve(value, EmbeddedType))

View File

@ -8,9 +8,9 @@ import ./box_and_client
const N = 100000 const N = 100000
const let
`?_` = init(Discard) `?_` = Discard().toPreserve
`?$` = init(Capture, `?_`) `?$` = Capture().toPreserve
proc boot(facet: Facet) = proc boot(facet: Facet) =
@ -28,19 +28,19 @@ proc boot(facet: Facet) =
echo "terminated box root facet" echo "terminated box root facet"
facet.addEndpoint do (facet: Facet) -> EndpointSpec: facet.addEndpoint do (facet: Facet) -> EndpointSpec:
const a = prsSetBox(`?$`) let a = prsSetBox(`?$`)
result.analysis = some analyzeAssertion(a) result.analysis = some analyzeAssertion(a)
proc cb(facet: Facet; vs: seq[Value]) = proc cb(facet: Facet; vs: seq[Value]) =
facet.scheduleScript do (facet: Facet): facet.scheduleScript do (facet: Facet):
value.set(vs[0]) value.set(vs[0])
# echo "box updated value ", vs[0] # echo "box updated value ", vs[0]
result.callback = facet.wrap(messageEvent, cb) result.callback = facet.wrap(messageEvent, cb)
result.assertion = Observe.init(prsSetBox(`?$`)) result.assertion = observe(prsSetBox(`?$`))
facet.spawn("client") do (facet: Facet): facet.spawn("client") do (facet: Facet):
facet.addEndpoint do (facet: Facet) -> EndpointSpec: facet.addEndpoint do (facet: Facet) -> EndpointSpec:
const a = prsBoxState(`?$`) let a = prsBoxState(`?$`)
result.analysis = some analyzeAssertion(a) result.analysis = some analyzeAssertion(a)
proc cb(facet: Facet; vs: seq[Value]) = proc cb(facet: Facet; vs: seq[Value]) =
facet.scheduleScript do (facet: Facet): facet.scheduleScript do (facet: Facet):
@ -48,16 +48,16 @@ proc boot(facet: Facet) =
# echo "client sending ", v # echo "client sending ", v
facet.send(v) facet.send(v)
result.callback = facet.wrap(addedEvent, cb) result.callback = facet.wrap(addedEvent, cb)
result.assertion = Observe.init(prsBoxState(`?$`)) result.assertion = observe(prsBoxState(`?$`))
facet.addEndpoint do (facet: Facet) -> EndpointSpec: facet.addEndpoint do (facet: Facet) -> EndpointSpec:
const a = prsBoxState(`?_`) let a = prsBoxState(`?_`)
result.analysis = some analyzeAssertion(a) result.analysis = some analyzeAssertion(a)
proc cb(facet: Facet; vs: seq[Value]) = proc cb(facet: Facet; vs: seq[Value]) =
facet.scheduleScript do (facet: Facet): facet.scheduleScript do (facet: Facet):
echo "box gone" echo "box gone"
result.callback = facet.wrap(removedEvent, cb) result.callback = facet.wrap(removedEvent, cb)
result.assertion = Observe.init(prsBoxState(`?_`)) result.assertion = observe(prsBoxState(`?_`))
facet.actor.dataspace.ground.addStopHandler do (_: Dataspace): facet.actor.dataspace.ground.addStopHandler do (_: Dataspace):
echo "stopping box-and-client" echo "stopping box-and-client"

View File

@ -1,7 +1,8 @@
# SPDX-License-Identifier: ISC # SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
# SPDX-License-Identifier: Unlicense
import asyncdispatch import std/asyncdispatch
import preserves, preserves/records import preserves
import syndicate import syndicate
import ./box_and_client import ./box_and_client
@ -13,7 +14,7 @@ syndicate testDsl:
asserting prsBoxState(currentValue.get) asserting prsBoxState(currentValue.get)
stopIf currentValue.get == 10: stopIf currentValue.get == 10:
echo "box: terminating" echo "box: terminating"
onMessage(prsSetBox(`?*`)) do (newValue: int): onMessage(prsSetBox(?newValue)) do (newValue: int):
# The SetBox message is unpacked to `newValue: int` # The SetBox message is unpacked to `newValue: int`
echo "box: taking on new value ", newValue echo "box: taking on new value ", newValue
currentValue.set(newValue) currentValue.set(newValue)
@ -21,10 +22,10 @@ syndicate testDsl:
spawn "client": spawn "client":
#stopIf retracted(observe(SetBox, _)): #stopIf retracted(observe(SetBox, _)):
# echo "client: box has gone" # echo "client: box has gone"
onAsserted(prsBoxState(`?*`)) do (v: BiggestInt): onAsserted(prsBoxState(?v)) do (v: BiggestInt):
echo "client: learned that box's value is now ", v echo "client: learned that box's value is now ", v
send(prsSetBox(v.succ)) send(prsSetBox(v.succ))
onRetracted(prsBoxState(`?_`)) do (_): onRetracted(prsBoxState(?_)) do (_):
echo "client: box state disappeared" echo "client: box state disappeared"
onStop: onStop:
quit(0) # Quit explicitly rather than let the dispatcher run empty. quit(0) # Quit explicitly rather than let the dispatcher run empty.