Pass only a single preserves value to assert
This commit is contained in:
parent
3de97c5cf1
commit
86b2dfbdab
|
@ -1,11 +1,11 @@
|
|||
# SPDX-License-Identifier: ISC
|
||||
|
||||
import std/[asyncdispatch, macros, options]
|
||||
import preserves, preserves/records
|
||||
import preserves
|
||||
import syndicate/[assertions, dataspaces, events, skeletons]
|
||||
|
||||
export assertions.Capture
|
||||
export assertions.Discard
|
||||
export assertions.`?_`
|
||||
export assertions.`?*`
|
||||
export assertions.Observe
|
||||
export dataspaces.Facet
|
||||
export dataspaces.FieldId
|
||||
|
@ -29,9 +29,8 @@ proc `==`*(x, y: FieldId): bool {.borrow.}
|
|||
proc newLit(p: pointer): NimNode = ident"nil"
|
||||
## Hack to make `newLit` work on `Presevere`.
|
||||
|
||||
proc getCurrentFacet*(): Facet =
|
||||
proc getCurrentFacet*(): Facet {.error.}
|
||||
## Return the current `Facet` for this context.
|
||||
raiseAssert "getCurrentFacet called outside of a Syndicate context"
|
||||
|
||||
template stopIf*(cond, body: untyped): untyped =
|
||||
## Stop the current facet if `cond` is true and
|
||||
|
@ -43,41 +42,23 @@ template stopIf*(cond, body: untyped): untyped =
|
|||
proc getCurrentFacet(): Facet {.inject, used.} = facet
|
||||
body
|
||||
|
||||
template send*(class: RecordClass; fields: varargs[Preserve,
|
||||
toPreserve]): untyped =
|
||||
template sendMessage*(msg: untyped): untyped =
|
||||
mixin getCurrentFacet
|
||||
send(getCurrentFacet(), init(class, fields))
|
||||
send(getCurrentFacet(), toPreserve(msg))
|
||||
|
||||
proc assertionForRecord(class: RecordClass; doHandler: NimNode): NimNode =
|
||||
## Generate an assertion that captures or discards the items of record `class`
|
||||
## according to the parameters of `doHandler`. `_` parameters are discarded.
|
||||
let formalArgs = doHandler[3]
|
||||
if formalArgs.len.pred != class.arity:
|
||||
error($formalArgs.repr & " does not match record class " & $class, doHandler)
|
||||
result = newCall("init", newLit(class))
|
||||
for i, arg in formalArgs:
|
||||
if i > 0:
|
||||
arg.expectKind nnkIdentDefs
|
||||
if arg[0] == ident"_":
|
||||
result.add newCall("init", ident"Discard")
|
||||
else:
|
||||
result.add newCall("init", ident"Capture", newCall("init",
|
||||
ident"Discard"))
|
||||
|
||||
proc callbackForEvent(event: EventKind; class: RecordClass; doHandler: NimNode): NimNode =
|
||||
## Generate a procedure that checks an event kind, unpacks the fields of `class` to match the
|
||||
## parameters of `doHandler`, and calls the body of `doHandler`.
|
||||
let formalArgs = doHandler[3]
|
||||
if formalArgs.len.pred != class.arity:
|
||||
error($formalArgs.repr & " does not match record class " & $class, doHandler)
|
||||
doHandler.expectKind nnkDo
|
||||
proc callbackForEvent(event: EventKind; pattern, handler: NimNode): NimNode =
|
||||
## Generate a procedure that checks an event kind, unpacks `pattern` match to fit the
|
||||
## parameters of `handler`, and calls the body of `handler`.
|
||||
# TODO: compile time analysis of pattern to count number of captures
|
||||
handler.expectKind nnkDo
|
||||
let
|
||||
formalArgs = handler[3]
|
||||
cbFacetSym = genSym(nskParam, "facet")
|
||||
scriptFacetSym = genSym(nskParam, "facet")
|
||||
eventSym = genSym(nskParam, "event")
|
||||
recSym = genSym(nskParam, "record")
|
||||
var
|
||||
letSection = newNimNode(nnkLetSection, doHandler)
|
||||
letSection = newNimNode(nnkLetSection, handler)
|
||||
captureCount: int
|
||||
for i, arg in formalArgs:
|
||||
if i > 0:
|
||||
|
@ -111,7 +92,7 @@ proc callbackForEvent(event: EventKind; class: RecordClass; doHandler: NimNode):
|
|||
body = scriptFacetSym,
|
||||
pragmas = newNimNode(nnkPragma).add(ident"inject").add(ident"used")),
|
||||
letSection,
|
||||
doHandler[6]
|
||||
handler[6]
|
||||
)
|
||||
)
|
||||
newProc(
|
||||
|
@ -133,28 +114,27 @@ proc callbackForEvent(event: EventKind; class: RecordClass; doHandler: NimNode):
|
|||
# TODO: this proc just checks the event type and then schedules a script,
|
||||
# should the event check be done in skeletons instead?
|
||||
|
||||
proc onEvent(event: EventKind; class: RecordClass; doHandler: NimNode): NimNode =
|
||||
proc onEvent(event: EventKind; pattern, handler: NimNode): NimNode =
|
||||
let
|
||||
assertion = assertionForRecord(class, doHandler)
|
||||
handler = callbackForEvent(event, class, doHandler)
|
||||
handler = callbackForEvent(event, pattern, handler)
|
||||
handlerSym = handler[0]
|
||||
result = quote do:
|
||||
`handler`
|
||||
mixin getCurrentFacet
|
||||
discard getCurrentFacet().addEndpoint do (facet: Facet) -> EndpointSpec:
|
||||
let a = `assertion`
|
||||
result.assertion = some(init(Observe, a))
|
||||
let a = `pattern`
|
||||
result.assertion = some(toPreserve(observe(a)))
|
||||
result.analysis = some(analyzeAssertion(a))
|
||||
result.analysis.get.callback = wrap(facet, `handlerSym`)
|
||||
|
||||
macro onAsserted*(class: static[RecordClass]; doHandler: untyped) =
|
||||
onEvent(addedEvent, class, doHandler)
|
||||
macro onAsserted*(pattern: Preserve; handler: untyped) =
|
||||
onEvent(addedEvent, pattern, handler)
|
||||
|
||||
macro onRetracted*(class: static[RecordClass]; doHandler: untyped) =
|
||||
onEvent(removedEvent, class, doHandler)
|
||||
macro onRetracted*(pattern: Preserve; handler: untyped) =
|
||||
onEvent(removedEvent, pattern, handler)
|
||||
|
||||
macro onMessage*(class: static[RecordClass]; doHandler: untyped) =
|
||||
onEvent(messageEvent, class, doHandler)
|
||||
macro onMessage*(pattern: Preserve; doHandler: untyped) =
|
||||
onEvent(messageEvent, pattern, doHandler)
|
||||
|
||||
template onStart*(body: untyped): untyped =
|
||||
mixin getCurrentFacet
|
||||
|
@ -168,11 +148,10 @@ template onStop*(body: untyped): untyped =
|
|||
proc getCurrentFacet(): Facet {.inject, used.} = facet
|
||||
body
|
||||
|
||||
template assert*(class: RecordClass; fields: varargs[Preserve, toPreserve]): untyped =
|
||||
template assert*(a: Preserve): untyped =
|
||||
mixin getCurrentFacet
|
||||
let facet = getCurrentFacet()
|
||||
discard facet.addEndpoint do (_: Facet) -> EndpointSpec:
|
||||
let a = init(class, fields)
|
||||
result.assertion = some(a)
|
||||
|
||||
template field*(F: untyped; T: typedesc; initial: T): untyped =
|
||||
|
|
|
@ -2,10 +2,30 @@
|
|||
|
||||
import preserves, preserves/records
|
||||
|
||||
type
|
||||
Discard* {.record: "discard".} = tuple[]
|
||||
Capture* {.record: "capture".} = tuple[pattern: Preserve]
|
||||
Observe* {.record: "observe".} = tuple[pattern: Preserve]
|
||||
#Inbound* = RecordClass(label: symbol"inbound", arity: 1)
|
||||
#Outbound* = RecordClass(label: symbol"outbound", arity: 1)
|
||||
#Instance* = RecordClass(label: symbol"instance", arity: 1)
|
||||
|
||||
const
|
||||
Discard* = RecordClass(label: symbol"discard", arity: 0)
|
||||
Capture* = RecordClass(label: symbol"capture", arity: 1)
|
||||
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")
|
||||
`?*`* = initRecord("capture", `?_`)
|
||||
|
||||
proc capture*(pattern: Preserve): Capture {.inline.} = (pattern,)
|
||||
proc capture*[T](pattern: T): Capture {.inline.} = (pattern.toPreserve,)
|
||||
|
||||
proc pattern*(class: RecordClass; fields: varargs[Preserve, toPreserve]): Preserve {.inline.} =
|
||||
## Build a pattern for capturing some fields of a tuple type.
|
||||
assert(class.arity == fields.len)
|
||||
init(class, fields)
|
||||
|
||||
proc pattern*(T: typedesc[tuple]; fields: varargs[Preserve, toPreserve]): Preserve {.inline.} =
|
||||
## Build a pattern for capturing some fields of a tuple type.
|
||||
# TODO: pattern type?
|
||||
pattern(classOf(T), fields)
|
||||
|
||||
proc observe*(pattern: Preserve): Observe {.inline.} = (pattern,)
|
||||
proc observe*[T](pattern: T): Observe {.inline.} = (pattern.toPreserve,)
|
||||
|
|
|
@ -555,7 +555,7 @@ proc bootModule*(name: string; bootProc: ActivationScript): Future[void] =
|
|||
|
||||
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 = object of Field
|
||||
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)
|
||||
|
|
|
@ -48,11 +48,14 @@ proc `$`(a: Analysis): string =
|
|||
proc analyzeAssertion*(a: Value): Analysis =
|
||||
var path: Path
|
||||
proc walk(analysis: var Analysis; a: Value): Skeleton[Shape] =
|
||||
if Capture.isClassOf a:
|
||||
const
|
||||
discardClass = classOf Discard
|
||||
captureClass = classOf Capture
|
||||
if discardClass.isClassOf a:
|
||||
discard
|
||||
elif captureClass.isClassOf a:
|
||||
analysis.capturePaths.add(path)
|
||||
result = walk(analysis, a.fields[0])
|
||||
elif Discard.isClassOf a:
|
||||
discard
|
||||
else:
|
||||
if a.kind == pkRecord:
|
||||
let class = classOf(a)
|
||||
|
@ -274,6 +277,7 @@ proc adjustAssertion*(index: var Index; outerValue: Value; delta: int): ChangeDe
|
|||
(proc (l: Leaf; v: Value) = l.cachedAssertions.incl(v)),
|
||||
(proc (h: Handler; vs: seq[Value]) =
|
||||
if h.cachedCaptures.change(vs, +1) == cdAbsentToPresent:
|
||||
#debugEcho " assertion of ", outerValue
|
||||
for cb in h.callbacks: cb(addedEvent, vs)))
|
||||
of cdPresentToAbsent:
|
||||
index.root.modify(
|
||||
|
@ -283,6 +287,7 @@ proc adjustAssertion*(index: var Index; outerValue: Value; delta: int): ChangeDe
|
|||
(proc (l: Leaf; v: Value) = l.cachedAssertions.excl(v)),
|
||||
(proc (h: Handler; vs: seq[Value]) =
|
||||
if h.cachedCaptures.change(vs, -1) == cdPresentToAbsent:
|
||||
#debugEcho "retraction of ", outerValue
|
||||
for cb in h.callbacks: cb(removedEvent, vs)))
|
||||
else:
|
||||
discard
|
||||
|
|
|
@ -7,8 +7,6 @@ import asyncdispatch, tables, options
|
|||
const N = 100000
|
||||
|
||||
const
|
||||
`?_` = init(Discard)
|
||||
`?$` = init(Capture, `?_`)
|
||||
BoxState = RecordClass(label: symbol"BoxState", arity: 1)
|
||||
SetBox = RecordClass(label: symbol"SetBox", arity: 1)
|
||||
|
||||
|
@ -30,21 +28,21 @@ proc boot(facet: Facet) =
|
|||
echo "terminated box root facet"
|
||||
|
||||
discard facet.addEndpoint do (facet: Facet) -> EndpointSpec:
|
||||
const a = SetBox.init(`?$`)
|
||||
const a = SetBox.init(`?*`)
|
||||
result.analysis = some analyzeAssertion(a)
|
||||
proc cb(facet: Facet; evt: EventKind; vs: seq[Value]) =
|
||||
if evt == messageEvent:
|
||||
facet.scheduleScript do (facet: Facet):
|
||||
value.set(vs[0])
|
||||
# echo "box updated value ", vs[0]
|
||||
echo "box updated value ", vs[0]
|
||||
result.analysis.get.callback = facet.wrap cb
|
||||
const o = Observe.init(SetBox.init(`?$`))
|
||||
const o = observe(SetBox.init(`?*`)).toPreserve
|
||||
result.assertion = some o
|
||||
|
||||
facet.spawn("client") do (facet: Facet):
|
||||
|
||||
discard facet.addEndpoint do (facet: Facet) -> EndpointSpec:
|
||||
const a = BoxState.init(`?$`)
|
||||
const a = BoxState.init(`?*`)
|
||||
result.analysis = some analyzeAssertion(a)
|
||||
proc cb(facet: Facet; evt: EventKind; vs: seq[Value]) =
|
||||
if evt == addedEvent:
|
||||
|
@ -53,7 +51,7 @@ proc boot(facet: Facet) =
|
|||
# echo "client sending ", v
|
||||
facet.send(v)
|
||||
result.analysis.get.callback = facet.wrap cb
|
||||
const o = Observe.init(BoxState.init(`?$`))
|
||||
const o = observe(BoxState.init(`?*`)).toPreserve
|
||||
result.assertion = some o
|
||||
|
||||
discard facet.addEndpoint do (facet: Facet) -> EndpointSpec:
|
||||
|
@ -64,7 +62,7 @@ proc boot(facet: Facet) =
|
|||
facet.scheduleScript do (facet: Facet):
|
||||
echo "box gone"
|
||||
result.analysis.get.callback = facet.wrap cb
|
||||
const o = Observe.init(BoxState.init(`?_`))
|
||||
const o = observe(BoxState.init(`?_`)).toPreserve
|
||||
result.assertion = some o
|
||||
|
||||
facet.actor.dataspace.ground.addStopHandler do (_: Dataspace):
|
||||
|
|
|
@ -7,16 +7,15 @@ import syndicate
|
|||
const
|
||||
BoxState = RecordClass(label: symbol"box-state", arity: 1)
|
||||
SetBox = RecordClass(label: symbol"set-box", arity: 1)
|
||||
# TODO: hide RecordClass behind Assertion and Message types
|
||||
|
||||
syndicate "test_dsl":
|
||||
|
||||
spawn "box":
|
||||
field(currentValue, int, 0)
|
||||
assert(BoxState, currentValue.get)
|
||||
assert(BoxState.init currentValue.get)
|
||||
stopIf currentValue.get == 10:
|
||||
echo "box: terminating"
|
||||
onMessage(SetBox) do (newValue: int):
|
||||
onMessage(SetBox % `?*`) do (newValue: int):
|
||||
# The SetBox message is unpacked to `newValue: int`
|
||||
echo "box: taking on new value ", newValue
|
||||
currentValue.set(newValue)
|
||||
|
@ -24,10 +23,10 @@ syndicate "test_dsl":
|
|||
spawn "client":
|
||||
#stopIf retracted(observe(SetBox, _)):
|
||||
# echo "client: box has gone"
|
||||
onAsserted(BoxState) do (v: int):
|
||||
onAsserted(BoxState % `?*`) do (v: int):
|
||||
echo "client: learned that box's value is now ", v
|
||||
send(SetBox, v+1)
|
||||
onRetracted(BoxState) do (_):
|
||||
sendMessage(SetBox % v.succ)
|
||||
onRetracted(BoxState % `?_`) do (_):
|
||||
echo "client: box state disappeared"
|
||||
onStop:
|
||||
quit(0) # Quit explicitly rather than let the dispatcher run empty.
|
||||
|
|
Loading…
Reference in New Issue