2023-04-10 21:55:39 +00:00
|
|
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
2021-09-01 11:44:28 +00:00
|
|
|
# SPDX-License-Identifier: Unlicense
|
2021-06-29 15:18:09 +00:00
|
|
|
|
2022-04-23 21:05:48 +00:00
|
|
|
## This module implements the `Syndicate DSL <https://syndicate-lang.org/doc/syndicate/>`_.
|
|
|
|
|
2023-05-06 19:09:45 +00:00
|
|
|
import std/[asyncdispatch, macros, tables, typetraits]
|
2023-05-18 10:20:44 +00:00
|
|
|
|
2022-04-24 01:01:30 +00:00
|
|
|
import preserves
|
2023-12-31 17:15:06 +00:00
|
|
|
export fromPreserves, toPreserves
|
2023-05-18 10:20:44 +00:00
|
|
|
|
2022-03-12 16:35:03 +00:00
|
|
|
import ./syndicate/[actors, dataspaces, durings, patterns]
|
2022-08-29 19:47:12 +00:00
|
|
|
import ./syndicate/protocols/dataspace
|
2022-03-12 16:35:03 +00:00
|
|
|
|
2024-01-06 11:55:48 +00:00
|
|
|
export actors, dataspace, dataspaces, patterns
|
2022-04-24 01:01:30 +00:00
|
|
|
|
2024-01-01 18:18:30 +00:00
|
|
|
type Assertion* {.deprecated: "Assertion and Preserve[void] replaced by Value".} = Value
|
|
|
|
|
2023-03-23 19:06:44 +00:00
|
|
|
proc `!`*(typ: static typedesc): Pattern {.inline.} =
|
|
|
|
patterns.dropType(typ)
|
|
|
|
|
2023-12-29 19:38:03 +00:00
|
|
|
proc `?`*[T](val: T): Pattern {.inline.} =
|
|
|
|
patterns.grab[T](val)
|
|
|
|
|
|
|
|
proc `?:`*(typ: static typedesc): Pattern {.inline.} =
|
2023-03-23 19:06:44 +00:00
|
|
|
patterns.grabType(typ)
|
|
|
|
|
2023-12-29 19:38:03 +00:00
|
|
|
proc `?:`*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern {.inline.} =
|
2023-03-23 19:06:44 +00:00
|
|
|
patterns.grab(typ, bindings)
|
|
|
|
|
2023-12-29 19:38:03 +00:00
|
|
|
proc `?:`*(typ: static typedesc; bindings: sink openArray[(Value, Pattern)]): Pattern {.inline.} =
|
2023-07-26 10:01:47 +00:00
|
|
|
patterns.grab(typ, bindings)
|
|
|
|
|
2023-03-23 19:06:44 +00:00
|
|
|
proc `??`*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern {.inline.} =
|
|
|
|
patterns.inject(pat, bindings)
|
|
|
|
|
2022-03-12 16:08:22 +00:00
|
|
|
type
|
2024-01-01 18:18:30 +00:00
|
|
|
PublishProc = proc (turn: var Turn; v: Value; h: Handle) {.closure, gcsafe.}
|
2022-12-22 04:59:16 +00:00
|
|
|
RetractProc = proc (turn: var Turn; h: Handle) {.closure, gcsafe.}
|
2024-01-01 18:18:30 +00:00
|
|
|
MessageProc = proc (turn: var Turn; v: Value) {.closure, gcsafe.}
|
2022-03-12 16:08:22 +00:00
|
|
|
ClosureEntity = ref object of Entity
|
|
|
|
publishImpl: PublishProc
|
|
|
|
retractImpl: RetractProc
|
|
|
|
messageImpl: MessageProc
|
|
|
|
|
2022-12-22 04:59:16 +00:00
|
|
|
method publish(e: ClosureEntity; turn: var Turn; a: AssertionRef; h: Handle) {.gcsafe.} =
|
|
|
|
if not e.publishImpl.isNil: e.publishImpl(turn, a.value, h)
|
2022-03-12 16:08:22 +00:00
|
|
|
|
2022-12-22 04:59:16 +00:00
|
|
|
method retract(e: ClosureEntity; turn: var Turn; h: Handle) {.gcsafe.} =
|
2022-03-12 16:08:22 +00:00
|
|
|
if not e.retractImpl.isNil: e.retractImpl(turn, h)
|
|
|
|
|
2022-12-22 04:59:16 +00:00
|
|
|
method message(e: ClosureEntity; turn: var Turn; a: AssertionRef) {.gcsafe.} =
|
|
|
|
if not e.messageImpl.isNil: e.messageImpl(turn, a.value)
|
2021-11-03 18:21:52 +00:00
|
|
|
|
2022-08-29 14:09:07 +00:00
|
|
|
proc argumentCount(handler: NimNode): int =
|
|
|
|
handler.expectKind {nnkDo, nnkStmtList}
|
|
|
|
if handler.kind == nnkDo: result = pred handler[3].len
|
|
|
|
|
2023-07-21 11:00:04 +00:00
|
|
|
type HandlerNodes = tuple
|
|
|
|
valuesSym, varSection, body: NimNode
|
|
|
|
|
|
|
|
proc generateHandlerNodes(handler: NimNode): HandlerNodes =
|
|
|
|
handler.expectKind {nnkStmtList, nnkDo}
|
|
|
|
result.valuesSym = genSym(nskVar, "values")
|
|
|
|
let valuesTuple = newNimNode(nnkTupleTy, handler)
|
|
|
|
case handler.kind
|
|
|
|
of nnkStmtList:
|
|
|
|
result.body = handler
|
|
|
|
of nnkDo:
|
|
|
|
let
|
|
|
|
innerTuple = newNimNode(nnkVarTuple, handler)
|
|
|
|
varSectionInner = newNimNode(nnkVarSection, handler).add(innerTuple)
|
2022-06-12 19:09:34 +00:00
|
|
|
for i, arg in handler[3]:
|
|
|
|
if i > 0:
|
|
|
|
arg.expectKind nnkIdentDefs
|
|
|
|
if arg[1].kind == nnkEmpty:
|
|
|
|
error("type required for capture", arg)
|
|
|
|
var def = newNimNode(nnkIdentDefs, arg)
|
|
|
|
arg.copyChildrenTo def
|
|
|
|
valuesTuple.add(def)
|
|
|
|
innerTuple.add(arg[0])
|
2023-07-21 11:00:04 +00:00
|
|
|
innerTuple.add(newEmptyNode(), result.valuesSym)
|
|
|
|
result.body = newStmtList(varSectionInner, handler[6])
|
|
|
|
else:
|
|
|
|
discard # caught earlier by expectKind
|
|
|
|
result.varSection = newNimNode(nnkVarSection, handler).
|
|
|
|
add(newIdentDefs(result.valuesSym, valuesTuple))
|
|
|
|
|
|
|
|
proc wrapPublishHandler(turn, handler: NimNode): NimNode =
|
2021-08-27 20:03:02 +00:00
|
|
|
var
|
2023-07-21 11:00:04 +00:00
|
|
|
(valuesSym, varSection, publishBody) =
|
|
|
|
generateHandlerNodes(handler)
|
2021-10-29 14:55:49 +00:00
|
|
|
handleSym = ident"handle"
|
|
|
|
handlerSym = genSym(nskProc, "publish")
|
2023-08-01 10:03:47 +00:00
|
|
|
bindingsSym = ident"bindings"
|
2021-07-08 09:50:13 +00:00
|
|
|
quote do:
|
2024-01-01 18:18:30 +00:00
|
|
|
proc `handlerSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle) =
|
2023-07-21 11:00:04 +00:00
|
|
|
`varSection`
|
2023-12-31 17:15:06 +00:00
|
|
|
if fromPreserves(`valuesSym`, bindings):
|
2021-11-02 12:02:20 +00:00
|
|
|
`publishBody`
|
2021-07-08 09:50:13 +00:00
|
|
|
|
2023-06-30 09:05:42 +00:00
|
|
|
proc wrapMessageHandler(turn, handler: NimNode): NimNode =
|
2021-10-29 14:55:49 +00:00
|
|
|
var
|
2023-07-21 11:00:04 +00:00
|
|
|
(valuesSym, varSection, body) =
|
|
|
|
generateHandlerNodes(handler)
|
2021-10-29 14:55:49 +00:00
|
|
|
handlerSym = genSym(nskProc, "message")
|
2023-08-01 10:03:47 +00:00
|
|
|
bindingsSym = ident"bindings"
|
2021-10-29 14:55:49 +00:00
|
|
|
quote do:
|
2024-01-01 18:18:30 +00:00
|
|
|
proc `handlerSym`(`turn`: var Turn; `bindingsSym`: Value) =
|
2023-07-21 11:00:04 +00:00
|
|
|
`varSection`
|
2023-12-31 17:15:06 +00:00
|
|
|
if fromPreserves(`valuesSym`, bindings):
|
2021-10-29 14:55:49 +00:00
|
|
|
`body`
|
2021-07-08 09:50:13 +00:00
|
|
|
|
2023-07-21 11:00:04 +00:00
|
|
|
proc wrapDuringHandler(turn, entryBody, exitBody: NimNode): NimNode =
|
|
|
|
var
|
|
|
|
(valuesSym, varSection, publishBody) =
|
|
|
|
generateHandlerNodes(entryBody)
|
|
|
|
bindingsSym = ident"bindings"
|
|
|
|
handleSym = ident"duringHandle"
|
|
|
|
duringSym = genSym(nskProc, "during")
|
|
|
|
if exitBody.isNil:
|
|
|
|
quote do:
|
2024-01-01 18:18:30 +00:00
|
|
|
proc `duringSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle): TurnAction =
|
2023-07-21 11:00:04 +00:00
|
|
|
`varSection`
|
2023-12-31 17:15:06 +00:00
|
|
|
if fromPreserves(`valuesSym`, `bindingsSym`):
|
2023-07-21 11:00:04 +00:00
|
|
|
`publishBody`
|
|
|
|
else:
|
|
|
|
quote do:
|
2024-01-01 18:18:30 +00:00
|
|
|
proc `duringSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle): TurnAction =
|
2023-07-21 11:00:04 +00:00
|
|
|
`varSection`
|
2023-12-31 17:15:06 +00:00
|
|
|
if fromPreserves(`valuesSym`, `bindingsSym`):
|
2023-07-21 11:00:04 +00:00
|
|
|
`publishBody`
|
|
|
|
proc action(`turn`: var Turn) =
|
|
|
|
`exitBody`
|
|
|
|
result = action
|
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
macro onPublish*(turn: untyped; ds: Cap; pattern: Pattern; handler: untyped) =
|
2022-04-23 23:31:34 +00:00
|
|
|
## Call `handler` when an assertion matching `pattern` is published at `ds`.
|
2021-07-08 09:50:13 +00:00
|
|
|
let
|
2022-08-29 14:09:07 +00:00
|
|
|
argCount = argumentCount(handler)
|
2023-06-30 09:05:42 +00:00
|
|
|
handlerProc = wrapPublishHandler(turn, handler)
|
2021-10-29 14:55:49 +00:00
|
|
|
handlerSym = handlerProc[0]
|
2021-06-29 15:18:09 +00:00
|
|
|
result = quote do:
|
2023-08-01 10:03:47 +00:00
|
|
|
if `argCount` != 0 and `pattern`.analyse.capturePaths.len != `argCount`:
|
2023-10-20 12:29:51 +00:00
|
|
|
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments - " & $`pattern`)
|
2021-10-29 14:55:49 +00:00
|
|
|
`handlerProc`
|
2022-03-12 16:08:22 +00:00
|
|
|
discard observe(`turn`, `ds`, `pattern`, ClosureEntity(publishImpl: `handlerSym`))
|
2021-06-29 15:18:09 +00:00
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
macro onMessage*(turn: untyped; ds: Cap; pattern: Pattern; handler: untyped) =
|
2022-04-23 23:31:34 +00:00
|
|
|
## Call `handler` when an message matching `pattern` is broadcasted at `ds`.
|
2021-10-29 14:55:49 +00:00
|
|
|
let
|
2022-08-29 14:09:07 +00:00
|
|
|
argCount = argumentCount(handler)
|
2023-06-30 09:05:42 +00:00
|
|
|
handlerProc = wrapMessageHandler(turn, handler)
|
2021-10-29 14:55:49 +00:00
|
|
|
handlerSym = handlerProc[0]
|
|
|
|
result = quote do:
|
2023-08-01 10:03:47 +00:00
|
|
|
if `argCount` != 0 and `pattern`.analyse.capturePaths.len != `argCount`:
|
2023-10-20 12:29:51 +00:00
|
|
|
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments - " & $`pattern`)
|
2021-10-29 14:55:49 +00:00
|
|
|
`handlerProc`
|
2022-03-12 16:08:22 +00:00
|
|
|
discard observe(`turn`, `ds`, `pattern`, ClosureEntity(messageImpl: `handlerSym`))
|
2022-04-23 21:05:48 +00:00
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
macro during*(turn: untyped; ds: Cap; pattern: Pattern; publishBody, retractBody: untyped) =
|
2022-04-23 21:05:48 +00:00
|
|
|
## Call `publishBody` when an assertion matching `pattern` is published to `ds` and
|
|
|
|
## call `retractBody` on retraction. Assertions that match `pattern` but are not
|
|
|
|
## convertable to the arguments of `publishBody` are silently discarded.
|
|
|
|
##
|
|
|
|
## The following symbols are injected into the scope of both bodies:
|
|
|
|
## - `bindings` - raw Preserves sequence that matched `pattern`
|
|
|
|
## - `duringHandle` - dataspace handle of the assertion that triggered `publishBody`
|
|
|
|
let
|
2022-08-29 14:09:07 +00:00
|
|
|
argCount = argumentCount(publishBody)
|
2023-06-30 09:05:42 +00:00
|
|
|
callbackProc = wrapDuringHandler(turn, publishBody, retractBody)
|
2022-04-23 21:05:48 +00:00
|
|
|
callbackSym = callbackProc[0]
|
|
|
|
result = quote do:
|
2023-08-01 10:03:47 +00:00
|
|
|
if `argCount` != 0 and `pattern`.analyse.capturePaths.len != `argCount`:
|
2023-10-20 12:29:51 +00:00
|
|
|
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments - " & $`pattern`)
|
2022-04-23 21:05:48 +00:00
|
|
|
`callbackProc`
|
|
|
|
discard observe(`turn`, `ds`, `pattern`, during(`callbackSym`))
|
|
|
|
|
2023-07-24 15:13:36 +00:00
|
|
|
macro during*(turn: untyped; ds: Cap; pattern: Pattern; publishBody: untyped) =
|
2022-04-23 21:05:48 +00:00
|
|
|
## Variant of `during` without a retract body.
|
|
|
|
let
|
2023-08-01 10:03:47 +00:00
|
|
|
`argCount` = argumentCount(publishBody)
|
2023-06-30 09:05:42 +00:00
|
|
|
callbackProc = wrapDuringHandler(turn, publishBody, nil)
|
2022-04-23 21:05:48 +00:00
|
|
|
callbackSym = callbackProc[0]
|
|
|
|
result = quote do:
|
2023-08-01 10:03:47 +00:00
|
|
|
if `argCount` != 0 and `pattern`.analyse.capturePaths.len != `argCount`:
|
2023-11-02 13:53:59 +00:00
|
|
|
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments - " & $`pattern`)
|
2022-04-23 21:05:48 +00:00
|
|
|
`callbackProc`
|
|
|
|
discard observe(`turn`, `ds`, `pattern`, during(`callbackSym`))
|
2023-05-06 19:09:45 +00:00
|
|
|
|
2023-10-21 16:38:28 +00:00
|
|
|
type BootProc = proc (turn: var Turn; ds: Cap) {.gcsafe.}
|
|
|
|
type DeprecatedBootProc = proc (ds: Cap; turn: var Turn) {.gcsafe.}
|
2023-05-06 19:09:45 +00:00
|
|
|
|
|
|
|
proc runActor*(name: string; bootProc: BootProc) =
|
|
|
|
## Run an `Actor` to completion.
|
|
|
|
let actor = bootDataspace(name, bootProc)
|
2023-10-26 12:13:03 +00:00
|
|
|
while actor.running:
|
2023-07-23 07:29:45 +00:00
|
|
|
waitFor sleepAsync(500)
|
2023-10-21 16:38:28 +00:00
|
|
|
|
|
|
|
proc runActor*(name: string; bootProc: DeprecatedBootProc) {.deprecated.} =
|
|
|
|
## Run an `Actor` to completion.
|
|
|
|
runActor(name) do (turn: var Turn, ds: Cap):
|
|
|
|
bootProc(ds, turn)
|