2021-06-29 15:18:09 +00:00
|
|
|
# SPDX-License-Identifier: ISC
|
|
|
|
|
|
|
|
import std/[asyncdispatch, macros, options]
|
2021-08-27 20:03:02 +00:00
|
|
|
import preserves, preserves/records
|
2021-07-09 13:26:22 +00:00
|
|
|
import syndicate/[assertions, dataspaces, events, skeletons]
|
2021-06-29 15:18:09 +00:00
|
|
|
|
2021-07-12 10:17:13 +00:00
|
|
|
export preserves.`%`
|
2021-08-27 20:03:02 +00:00
|
|
|
export preserves.fromPreserve
|
|
|
|
export records.init
|
2021-06-30 11:30:56 +00:00
|
|
|
export assertions.`?_`
|
|
|
|
export assertions.`?*`
|
2021-06-29 15:18:09 +00:00
|
|
|
export assertions.Observe
|
|
|
|
export dataspaces.Facet
|
|
|
|
export dataspaces.FieldId
|
|
|
|
export dataspaces.Fields
|
2021-07-08 09:50:13 +00:00
|
|
|
export dataspaces.`==`
|
2021-06-29 15:18:09 +00:00
|
|
|
export dataspaces.addEndpoint
|
2021-06-30 10:18:57 +00:00
|
|
|
export dataspaces.addStartScript
|
|
|
|
export dataspaces.addStopScript
|
2021-07-09 09:38:53 +00:00
|
|
|
export dataspaces.beginExternalTask
|
2021-06-29 15:18:09 +00:00
|
|
|
export dataspaces.defineObservableProperty
|
2021-07-09 09:38:53 +00:00
|
|
|
export dataspaces.endExternalTask
|
2021-06-29 15:18:09 +00:00
|
|
|
export dataspaces.generateId
|
|
|
|
export dataspaces.hash
|
|
|
|
export dataspaces.recordDamage
|
|
|
|
export dataspaces.recordObservation
|
|
|
|
export dataspaces.scheduleScript
|
2021-07-08 09:50:13 +00:00
|
|
|
export dataspaces.stop
|
2021-06-29 15:18:09 +00:00
|
|
|
export events.EventKind
|
|
|
|
export skeletons.Analysis
|
|
|
|
|
|
|
|
export asyncdispatch.`callback=`
|
2021-08-27 20:03:02 +00:00
|
|
|
export options.get
|
2021-06-29 15:18:09 +00:00
|
|
|
|
2021-07-08 09:50:13 +00:00
|
|
|
proc getCurrentFacet*(): Facet = raiseAssert("must be called from within the DSL")
|
2021-06-29 15:18:09 +00:00
|
|
|
## Return the current `Facet` for this context.
|
|
|
|
|
|
|
|
template stopIf*(cond, body: untyped): untyped =
|
|
|
|
## Stop the current facet if `cond` is true and
|
|
|
|
## invoke `body` after the facet has stopped.
|
|
|
|
mixin getCurrentFacet
|
2021-07-09 10:38:10 +00:00
|
|
|
getCurrentFacet().addDataflow do (facet: Facet):
|
2021-06-29 15:18:09 +00:00
|
|
|
if cond:
|
|
|
|
facet.stop do (facet: Facet):
|
2021-06-30 08:47:54 +00:00
|
|
|
proc getCurrentFacet(): Facet {.inject, used.} = facet
|
2021-06-29 15:18:09 +00:00
|
|
|
body
|
|
|
|
|
2021-07-12 10:10:11 +00:00
|
|
|
template send*(msg: Preserve): untyped =
|
2021-06-29 15:18:09 +00:00
|
|
|
mixin getCurrentFacet
|
2021-07-12 10:10:11 +00:00
|
|
|
send(getCurrentFacet(), msg)
|
2021-06-30 11:30:56 +00:00
|
|
|
|
2021-07-08 09:50:13 +00:00
|
|
|
proc wrapDoHandler(pattern, handler: NimNode): NimNode =
|
2021-07-07 09:47:17 +00:00
|
|
|
## Generate a procedure that unpacks a `pattern` match to fit the
|
2021-06-30 11:30:56 +00:00
|
|
|
## parameters of `handler`, and calls the body of `handler`.
|
|
|
|
handler.expectKind nnkDo
|
2021-08-27 20:03:02 +00:00
|
|
|
var
|
2021-06-30 11:30:56 +00:00
|
|
|
formalArgs = handler[3]
|
2021-06-29 15:18:09 +00:00
|
|
|
cbFacetSym = genSym(nskParam, "facet")
|
|
|
|
scriptFacetSym = genSym(nskParam, "facet")
|
2021-07-07 09:47:17 +00:00
|
|
|
recSym = genSym(nskParam, "bindings")
|
2021-08-27 20:03:02 +00:00
|
|
|
varSection = newNimNode(nnkVarSection, handler)
|
|
|
|
conditional: NimNode
|
2021-07-08 09:50:13 +00:00
|
|
|
argCount: int
|
2021-06-29 15:18:09 +00:00
|
|
|
for i, arg in formalArgs:
|
|
|
|
if i > 0:
|
|
|
|
arg.expectKind nnkIdentDefs
|
|
|
|
if arg[0] == ident"_" or arg[0] == ident"*":
|
|
|
|
if arg[1].kind != nnkEmpty:
|
|
|
|
error("placeholders may not be typed", arg)
|
|
|
|
else:
|
|
|
|
if arg[1].kind == nnkEmpty:
|
|
|
|
error("type required for capture", arg)
|
2021-08-27 20:03:02 +00:00
|
|
|
var varDef = newNimNode(nnkIdentDefs, arg)
|
|
|
|
arg.copyChildrenTo varDef
|
|
|
|
varSection.add(varDef)
|
|
|
|
var conversion = newCall("fromPreserve", varDef[0],
|
|
|
|
newNimNode(nnkBracketExpr).add(recSym, newLit(pred i)))
|
|
|
|
if conditional.isNil:
|
|
|
|
conditional = conversion
|
|
|
|
else:
|
|
|
|
conditional = infix(conditional, "and", conversion)
|
2021-07-08 09:50:13 +00:00
|
|
|
inc(argCount)
|
2021-08-27 20:03:02 +00:00
|
|
|
var scriptBody = newStmtList()
|
|
|
|
if argCount > 0:
|
|
|
|
scriptBody.add(
|
|
|
|
varSection,
|
|
|
|
newNimNode(nnkIfStmt).add(
|
|
|
|
newNimNode(nnkElifBranch).add(
|
|
|
|
conditional, handler[6])))
|
|
|
|
else:
|
|
|
|
scriptBody.add(handler[6])
|
|
|
|
var
|
2021-07-08 09:50:13 +00:00
|
|
|
scriptSym = genSym(nskProc, "script")
|
|
|
|
handlerSym = genSym(nskProc, "handler")
|
|
|
|
litArgCount = newLit argCount
|
|
|
|
quote do:
|
|
|
|
proc `handlerSym`(`cbFacetSym`: Facet; `recSym`: seq[Preserve]) =
|
|
|
|
assert(`litArgCount` == captureCount(`pattern`), "pattern does not match handler")
|
|
|
|
# this should be a compile-time check
|
|
|
|
assert(
|
|
|
|
`litArgCount` == len(`recSym`),
|
2021-08-27 20:03:02 +00:00
|
|
|
"cannot unpack " & $`litArgCount` & " bindings from " & $(toPreserve `recSym`))
|
2021-07-08 09:50:13 +00:00
|
|
|
proc `scriptSym`(`scriptFacetSym`: Facet) =
|
|
|
|
proc getCurrentFacet(): Facet {.inject, used.} = `scriptFacetSym`
|
|
|
|
`scriptBody`
|
|
|
|
scheduleScript(`cbFacetSym`, `scriptSym`)
|
|
|
|
|
|
|
|
proc wrapHandler(pattern, handler: NimNode): NimNode =
|
|
|
|
case handler.kind
|
|
|
|
of nnkDo:
|
|
|
|
result = wrapDoHandler(pattern, handler)
|
|
|
|
of nnkStmtList:
|
|
|
|
let sym = genSym(nskProc, "handler")
|
|
|
|
result = quote do:
|
|
|
|
proc `sym`(facet: Facet; _: seq[Preserve]) =
|
|
|
|
proc getCurrentFacet(): Facet {.inject, used.} = facet
|
|
|
|
`handler`
|
|
|
|
else:
|
|
|
|
error("unhandled event handler", handler)
|
|
|
|
|
|
|
|
proc onEvent(event: EventKind, pattern, handler: NimNode): NimNode =
|
|
|
|
let
|
|
|
|
handler = wrapHandler(pattern, handler)
|
2021-06-29 15:18:09 +00:00
|
|
|
handlerSym = handler[0]
|
|
|
|
result = quote do:
|
|
|
|
mixin getCurrentFacet
|
2021-07-09 10:38:10 +00:00
|
|
|
getCurrentFacet().addEndpoint do (facet: Facet) -> EndpointSpec:
|
2021-07-08 09:50:13 +00:00
|
|
|
proc getCurrentFacet(): Facet {.inject, used.} = facet
|
|
|
|
`handler`
|
|
|
|
let a = `pattern`
|
2021-08-27 20:03:02 +00:00
|
|
|
result.assertion = Observe.init(a)
|
2021-07-08 09:50:13 +00:00
|
|
|
result.analysis = some(analyzeAssertion(a))
|
2021-07-09 10:38:10 +00:00
|
|
|
result.callback = wrap(facet, EventKind(`event`), `handlerSym`)
|
2021-06-29 15:18:09 +00:00
|
|
|
|
2021-06-30 11:30:56 +00:00
|
|
|
macro onAsserted*(pattern: Preserve; handler: untyped) =
|
|
|
|
onEvent(addedEvent, pattern, handler)
|
2021-06-29 15:18:09 +00:00
|
|
|
|
2021-06-30 11:30:56 +00:00
|
|
|
macro onRetracted*(pattern: Preserve; handler: untyped) =
|
|
|
|
onEvent(removedEvent, pattern, handler)
|
2021-06-29 15:18:09 +00:00
|
|
|
|
2021-06-30 11:30:56 +00:00
|
|
|
macro onMessage*(pattern: Preserve; doHandler: untyped) =
|
|
|
|
onEvent(messageEvent, pattern, doHandler)
|
2021-06-29 15:18:09 +00:00
|
|
|
|
2021-06-30 10:23:04 +00:00
|
|
|
template onStart*(body: untyped): untyped =
|
|
|
|
mixin getCurrentFacet
|
|
|
|
getCurrentFacet().addStartScript do (facet: Facet):
|
|
|
|
proc getCurrentFacet(): Facet {.inject, used.} = facet
|
|
|
|
body
|
|
|
|
|
|
|
|
template onStop*(body: untyped): untyped =
|
|
|
|
mixin getCurrentFacet
|
|
|
|
getCurrentFacet().addStopScript do (facet: Facet):
|
|
|
|
proc getCurrentFacet(): Facet {.inject, used.} = facet
|
|
|
|
body
|
|
|
|
|
2021-06-30 11:30:56 +00:00
|
|
|
template assert*(a: Preserve): untyped =
|
2021-06-29 15:18:09 +00:00
|
|
|
mixin getCurrentFacet
|
2021-07-09 10:38:10 +00:00
|
|
|
getCurrentFacet().addEndpoint do (_: Facet) -> EndpointSpec:
|
|
|
|
result.assertion = a
|
2021-06-29 15:18:09 +00:00
|
|
|
|
|
|
|
template field*(F: untyped; T: typedesc; initial: T): untyped =
|
|
|
|
## Declare a field. The identifier `F` shall be a value with
|
|
|
|
## `get` and `set` procs.
|
|
|
|
mixin getCurrentFacet
|
|
|
|
declareField(getCurrentFacet(), F, T, initial)
|
|
|
|
# use the template defined in dataspaces
|
|
|
|
|
2021-07-08 09:50:13 +00:00
|
|
|
template react*(body: untyped): untyped =
|
|
|
|
mixin getCurrentFacet
|
|
|
|
addChildFacet(getCurrentFacet()) do (facet: Facet):
|
|
|
|
proc getCurrentFacet(): Facet {.inject, used.} = facet
|
|
|
|
body
|
|
|
|
|
|
|
|
template stop*(body: untyped): untyped =
|
|
|
|
mixin getCurrentFacet
|
|
|
|
stop(getCurrentFacet()) do (facet: Facet):
|
|
|
|
proc getCurrentFacet(): Facet {.inject, used.} = facet
|
|
|
|
body
|
|
|
|
|
|
|
|
template stop*(): untyped =
|
|
|
|
mixin getCurrentFacet
|
|
|
|
stop(getCurrentFacet())
|
|
|
|
|
|
|
|
template during*(pattern: Preserve; handler: untyped) =
|
|
|
|
onAsserted(pattern):
|
|
|
|
react:
|
|
|
|
onAsserted(pattern, handler)
|
|
|
|
onRetracted(pattern): stop()
|
|
|
|
|
2021-06-29 15:18:09 +00:00
|
|
|
template spawn*(name: string; spawnBody: untyped): untyped =
|
|
|
|
mixin getCurrentFacet
|
|
|
|
spawn(getCurrentFacet(), name) do (spawnFacet: Facet):
|
2021-06-30 08:47:54 +00:00
|
|
|
proc getCurrentFacet(): Facet {.inject, used.} = spawnFacet
|
2021-06-29 15:18:09 +00:00
|
|
|
spawnBody
|
|
|
|
|
2021-07-12 10:17:13 +00:00
|
|
|
template withFacet*(f: Facet; body: untyped): untyped =
|
|
|
|
## Execute a Syndicate ``body`` using the ``Facet`` at ``f``.
|
|
|
|
runnableExamples:
|
|
|
|
import preserves, preserves/records
|
|
|
|
type Foo = ref object
|
|
|
|
facet: Facet
|
|
|
|
i: int
|
|
|
|
proc incAndAssert(foo: Foo) =
|
|
|
|
inc(foo.i)
|
|
|
|
withFacet foo.facet:
|
|
|
|
react: assert: initRecord("Foo", %foo.i)
|
|
|
|
proc getCurrentFacet(): Facet {.inject, used.} = f
|
|
|
|
body
|
|
|
|
|
2021-07-08 09:50:13 +00:00
|
|
|
template syndicate*(ident, dataspaceBody: untyped): untyped =
|
|
|
|
proc `ident`*(facet: Facet) =
|
|
|
|
proc getCurrentFacet(): Facet {.inject, used.} = facet
|
2021-06-29 15:18:09 +00:00
|
|
|
dataspaceBody
|
2021-07-08 09:50:13 +00:00
|
|
|
when isMainModule:
|
|
|
|
asyncCheck bootModule("", `ident`)
|
|
|
|
|
2021-07-12 10:17:13 +00:00
|
|
|
type BootProc* = proc (facet: Facet) {.gcsafe.}
|
|
|
|
|
|
|
|
template boot*(module: BootProc) =
|
2021-07-08 09:50:13 +00:00
|
|
|
mixin getCurrentFacet
|
|
|
|
module(getCurrentFacet())
|