Compare commits
75 Commits
c06a94bcb9
...
760ce84a8c
Author | SHA1 | Date |
---|---|---|
Emery Hemingway | 760ce84a8c | |
Emery Hemingway | 2e027afc42 | |
Emery Hemingway | 686719273c | |
Emery Hemingway | fe2255bcc9 | |
Emery Hemingway | 2fa2276dfe | |
Emery Hemingway | a4ba81a481 | |
Emery Hemingway | 75d1e33bff | |
Emery Hemingway | 6b642645f9 | |
Emery Hemingway | 0e5637a6c3 | |
Emery Hemingway | 3e11884a91 | |
Emery Hemingway | 7721138bf4 | |
Emery Hemingway | 59ece65f3b | |
Emery Hemingway | 6d2a401a2b | |
Emery Hemingway | 19121e514c | |
Emery Hemingway | df52f72263 | |
Emery Hemingway | e48c62f448 | |
Emery Hemingway | 3cc3a48c82 | |
Emery Hemingway | c1c5333778 | |
Emery Hemingway | 1e107131d8 | |
Emery Hemingway | a0355637d8 | |
Emery Hemingway | c0cff79313 | |
Emery Hemingway | b5aa2a7b8f | |
Emery Hemingway | 8f6da89d69 | |
Emery Hemingway | 0954180321 | |
Emery Hemingway | 97d24aa971 | |
Emery Hemingway | 8a7cc884fe | |
Emery Hemingway | 23c69f63a5 | |
Emery Hemingway | 8bc0ee2ae5 | |
Emery Hemingway | 090b4d77ef | |
Emery Hemingway | 00609f3b6f | |
Emery Hemingway | 577490701a | |
Emery Hemingway | 843252ad61 | |
Emery Hemingway | ac2576f005 | |
Emery Hemingway | 311b614979 | |
Emery Hemingway | 5b373e3047 | |
Emery Hemingway | ec8e166099 | |
Emery Hemingway | a987f875a9 | |
Emery Hemingway | 57b99b20e7 | |
Emery Hemingway | 4a6e95bbce | |
Emery Hemingway | 3a04fc195b | |
Emery Hemingway | 6fcb76b1e9 | |
Emery Hemingway | 552e51899c | |
Emery Hemingway | b2994b6d05 | |
Emery Hemingway | d86ef24c01 | |
Emery Hemingway | dcd6bfe99b | |
Emery Hemingway | 35670b2727 | |
Emery Hemingway | 73d29da071 | |
Emery Hemingway | 66f435a279 | |
Emery Hemingway | 703bd7baea | |
Emery Hemingway | ce6d97c1d3 | |
Emery Hemingway | f78308765e | |
Emery Hemingway | ba2ea5d08b | |
Emery Hemingway | 9c5e26e8f1 | |
Emery Hemingway | 8fc9608199 | |
Emery Hemingway | ce8e800187 | |
Emery Hemingway | 4e0a36ef31 | |
Emery Hemingway | 16cc5aaf98 | |
Emery Hemingway | 7b2d59e4cd | |
Emery Hemingway | 7f903a14d7 | |
Emery Hemingway | 4b29fc009b | |
Emery Hemingway | 248d34ce69 | |
Emery Hemingway | 146b30ed42 | |
Emery Hemingway | ca12c1ae03 | |
Emery Hemingway | 9614955320 | |
Emery Hemingway | 7fec2d61ac | |
Emery Hemingway | 76d550602f | |
Emery Hemingway | 91a218f7fb | |
Emery Hemingway | b1b0477b8a | |
Emery Hemingway | 170f49693c | |
Emery Hemingway | 219286a84a | |
Emery Hemingway | 8bb9fb16d7 | |
Emery Hemingway | fc94fa39d8 | |
Emery Hemingway | 2f4552e7fe | |
Emery Hemingway | 0089e1f413 | |
Emery Hemingway | 7a36a6e8a4 |
|
@ -1 +1,3 @@
|
||||||
/.direnv
|
/nim.cfg
|
||||||
|
*.check
|
||||||
|
*.run
|
||||||
|
|
11
README.md
11
README.md
|
@ -49,12 +49,12 @@ assert present.fromPreserve(pr) == true
|
||||||
|
|
||||||
## The Syndicate DSL
|
## The Syndicate DSL
|
||||||
|
|
||||||
The Syndicate DSL can be entered using `bootDataspace` which calls a Nim body with a [dataspace](https://synit.org/book/glossary.html#dataspace) [Ref](https://synit.org/book/glossary.html#reference) and a [turn](https://synit.org/book/glossary.html#turn). The `Ref` is something that we can observe and publish assertions at, and a `Turn` is special type for temporal scoping and transactional semantics. Assertions can be published using the `Assertion` or equivalent `Preserve[Ref]` type, but using Nim types is preferred because they can be reliably consistent with schema.
|
The Syndicate DSL can be entered using `runActor` which calls a Nim body with a [dataspace](https://synit.org/book/glossary.html#dataspace) [Ref](https://synit.org/book/glossary.html#reference) and a [turn](https://synit.org/book/glossary.html#turn). The `Ref` is something that we can observe and publish assertions at, and a `Turn` is special type for temporal scoping and transactional semantics. Assertions can be published using the `Assertion` or equivalent `Preserve[Ref]` type, but using Nim types is preferred because they can be reliably consistent with schema.
|
||||||
|
|
||||||
### Publish
|
### Publish
|
||||||
|
|
||||||
``` nim
|
``` nim
|
||||||
bootDataspace("main") do (dataspace: Ref; turn: var Turn):
|
runActor("main") do (dataspace: Ref; turn: var Turn):
|
||||||
let presenceHandle = publish(turn, dataspace, Present(username: "Judy"))
|
let presenceHandle = publish(turn, dataspace, Present(username: "Judy"))
|
||||||
# publish <Present "Judy"> to the dataspace
|
# publish <Present "Judy"> to the dataspace
|
||||||
# the assertion can be later retracted by handle
|
# the assertion can be later retracted by handle
|
||||||
|
@ -67,7 +67,7 @@ bootDataspace("main") do (dataspace: Ref; turn: var Turn):
|
||||||
We can react to assertions and messages within dataspaces using [patterns](https://synit.org/book/glossary.html#dataspace-pattern). Patterns are constructed using a Nim type and the `?` operator. Again a Nim type is used rather than a raw Preserves for schema consistency.
|
We can react to assertions and messages within dataspaces using [patterns](https://synit.org/book/glossary.html#dataspace-pattern). Patterns are constructed using a Nim type and the `?` operator. Again a Nim type is used rather than a raw Preserves for schema consistency.
|
||||||
|
|
||||||
``` nim
|
``` nim
|
||||||
bootDataspace("main") do (dataspace: Ref; turn: var Turn):
|
runActor("main") do (dataspace: Ref; turn: var Turn):
|
||||||
during(turn, dataspace, ?Present) do (who: string):
|
during(turn, dataspace, ?Present) do (who: string):
|
||||||
# This body is active when the ?Present pattern is matched.
|
# This body is active when the ?Present pattern is matched.
|
||||||
# The Present type contains two atomic values that can be matched
|
# The Present type contains two atomic values that can be matched
|
||||||
|
@ -92,10 +92,7 @@ bootDataspace("main") do (dataspace: Ref; turn: var Turn):
|
||||||
### [test_chat](./tests/test_chat.nim)
|
### [test_chat](./tests/test_chat.nim)
|
||||||
Simple chat demo that is compatible with [chat.py](https://git.syndicate-lang.org/syndicate-lang/syndicate-py/src/branch/main/chat.py).
|
Simple chat demo that is compatible with [chat.py](https://git.syndicate-lang.org/syndicate-lang/syndicate-py/src/branch/main/chat.py).
|
||||||
```sh
|
```sh
|
||||||
nim c -r tests/test_chat.nim \
|
SYNDICATE_ROUTE='<route [<unix "/run/user/1000/dataspace">] [<ref {oid: "syndicate" sig: #x"69ca300c1dbfa08fba692102dd82311a"}>]>' nim c -r tests/test_chat.nim --user:fnord
|
||||||
--cap:'<ref {oid: "syndicate" sig: #x"69ca300c1dbfa08fba692102dd82311a"}>' \
|
|
||||||
--transport:'<tcp "127.0.0.1" 666>' \
|
|
||||||
--user:fnord
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### [xdg_open_ng](https://git.syndicate-lang.org/ehmry/xdg_open_ng)
|
### [xdg_open_ng](https://git.syndicate-lang.org/ehmry/xdg_open_ng)
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
include depends.tup
|
include depends.tup
|
||||||
|
NIM = $(DIRENV) $(NIM)
|
||||||
|
NIM_GROUPS += $(TUP_CWD)/<lock>
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
include ../preserves-nim/depends.tup
|
include ../preserves-nim/depends.tup
|
||||||
include ../taps/depends.tup
|
|
||||||
NIM_FLAGS += --path:$(TUP_CWD)/../nim
|
|
||||||
NIM_FLAGS += --path:$(TUP_CWD)/../preserves-nim/src
|
NIM_FLAGS += --path:$(TUP_CWD)/../preserves-nim/src
|
||||||
NIM_FLAGS += --path:$(TUP_CWD)/../taps/src
|
NIM_FLAGS += --path:$(TUP_CWD)/../noiseprotocol/src
|
||||||
NIM_FLAGS += --path:$(TUP_CWD)/../hashlib
|
|
||||||
NIM_GROUPS += $(TUP_CWD)/<protocol>
|
NIM_GROUPS += $(TUP_CWD)/<protocol>
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
{
|
||||||
|
"depends": [
|
||||||
|
{
|
||||||
|
"method": "fetchzip",
|
||||||
|
"packages": [
|
||||||
|
"bigints"
|
||||||
|
],
|
||||||
|
"path": "/nix/store/jvrm392g8adfsgf36prgwkbyd7vh5jsw-source",
|
||||||
|
"ref": "20231006",
|
||||||
|
"rev": "86ea14d31eea9275e1408ca34e6bfe9c99989a96",
|
||||||
|
"sha256": "15pcpmnk1bnw3k8769rjzcpg00nahyrypwbxs88jnwr4aczp99j4",
|
||||||
|
"srcDir": "src",
|
||||||
|
"url": "https://github.com/ehmry/nim-bigints/archive/86ea14d31eea9275e1408ca34e6bfe9c99989a96.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "fetchzip",
|
||||||
|
"packages": [
|
||||||
|
"hashlib"
|
||||||
|
],
|
||||||
|
"path": "/nix/store/fav82xdbicvlk34nmcbl89zx99lr3mbs-source",
|
||||||
|
"rev": "f9455d4be988e14e3dc7933eb7cc7d7c4820b7ac",
|
||||||
|
"sha256": "1sx6j952lj98629qfgr7ds5aipyw9d6lldcnnqs205wpj4pkcjb3",
|
||||||
|
"srcDir": "",
|
||||||
|
"url": "https://github.com/ehmry/hashlib/archive/f9455d4be988e14e3dc7933eb7cc7d7c4820b7ac.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "fetchzip",
|
||||||
|
"packages": [
|
||||||
|
"nimcrypto"
|
||||||
|
],
|
||||||
|
"path": "/nix/store/zyr8zwh7vaiycn1s4r8cxwc71f2k5l0h-source",
|
||||||
|
"ref": "traditional-api",
|
||||||
|
"rev": "602c5d20c69c76137201b5d41f788f72afb95aa8",
|
||||||
|
"sha256": "1dmdmgb6b9m5f8dyxk781nnd61dsk3hdxqks7idk9ncnpj9fng65",
|
||||||
|
"srcDir": "",
|
||||||
|
"url": "https://github.com/cheatfate/nimcrypto/archive/602c5d20c69c76137201b5d41f788f72afb95aa8.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "fetchzip",
|
||||||
|
"packages": [
|
||||||
|
"npeg"
|
||||||
|
],
|
||||||
|
"path": "/nix/store/ffkxmjmigfs7zhhiiqm0iw2c34smyciy-source",
|
||||||
|
"ref": "1.2.1",
|
||||||
|
"rev": "26d62fdc40feb84c6533956dc11d5ee9ea9b6c09",
|
||||||
|
"sha256": "0xpzifjkfp49w76qmaylan8q181bs45anmp46l4bwr3lkrr7bpwh",
|
||||||
|
"srcDir": "src",
|
||||||
|
"url": "https://github.com/zevv/npeg/archive/26d62fdc40feb84c6533956dc11d5ee9ea9b6c09.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "fetchzip",
|
||||||
|
"packages": [
|
||||||
|
"preserves"
|
||||||
|
],
|
||||||
|
"path": "/nix/store/fpkhfxnfbdcri6k7mac21r3byg738bs4-source",
|
||||||
|
"ref": "20240108",
|
||||||
|
"rev": "a01ba8c96d65f670862ba074bf82b50cbda6ed99",
|
||||||
|
"sha256": "0n8pghy2qfywx0psr54yzjvhdhi5av204150jyyzfxhigczd8sr4",
|
||||||
|
"srcDir": "src",
|
||||||
|
"url": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/a01ba8c96d65f670862ba074bf82b50cbda6ed99.tar.gz"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
11
shell.nix
11
shell.nix
|
@ -1,5 +1,6 @@
|
||||||
let
|
let pkgs = import <nixpkgs> { };
|
||||||
syndicate = builtins.getFlake "syndicate";
|
in pkgs.buildNimPackage {
|
||||||
pkgs =
|
name = "noiseprotocol";
|
||||||
import <nixpkgs> { overlays = builtins.attrValues syndicate.overlays; };
|
buildInputs = [ pkgs.noise-c ];
|
||||||
in pkgs.nimPackages.syndicate
|
lockFile = ./lock.json;
|
||||||
|
}
|
||||||
|
|
|
@ -6,42 +6,37 @@
|
||||||
import std/[asyncdispatch, macros, tables, typetraits]
|
import std/[asyncdispatch, macros, tables, typetraits]
|
||||||
|
|
||||||
import preserves
|
import preserves
|
||||||
export fromPreserve, toPreserve
|
export fromPreserves, toPreserves
|
||||||
|
|
||||||
import ./syndicate/[actors, dataspaces, durings, patterns]
|
import ./syndicate/[actors, dataspaces, durings, patterns]
|
||||||
import ./syndicate/protocols/dataspace
|
import ./syndicate/protocols/dataspace
|
||||||
|
|
||||||
when defined(posix):
|
export actors, dataspace, dataspaces, patterns
|
||||||
from ./syndicate/relays import Tcp, Unix, connect, connectStdio
|
|
||||||
export Tcp, Unix, connect, connectStdio
|
|
||||||
|
|
||||||
export patterns
|
type Assertion* {.deprecated: "Assertion and Preserve[void] replaced by Value".} = Value
|
||||||
|
|
||||||
export Actor, Assertion, Facet, Handle, Ref, Symbol, Turn, TurnAction,
|
|
||||||
`$`, addCallback, analyse, asyncCheck, bootDataspace,
|
|
||||||
facet, future, inFacet, message, newDataspace, onStop, publish,
|
|
||||||
retract, replace, run, stop, unembed
|
|
||||||
|
|
||||||
proc `!`*(typ: static typedesc): Pattern {.inline.} =
|
proc `!`*(typ: static typedesc): Pattern {.inline.} =
|
||||||
patterns.dropType(typ)
|
patterns.dropType(typ)
|
||||||
|
|
||||||
proc `?`*(typ: static typedesc): Pattern {.inline.} =
|
proc `?`*[T](val: T): Pattern {.inline.} =
|
||||||
|
patterns.grab[T](val)
|
||||||
|
|
||||||
|
proc `?:`*(typ: static typedesc): Pattern {.inline.} =
|
||||||
patterns.grabType(typ)
|
patterns.grabType(typ)
|
||||||
|
|
||||||
proc `?`*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern {.inline.} =
|
proc `?:`*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern {.inline.} =
|
||||||
|
patterns.grab(typ, bindings)
|
||||||
|
|
||||||
|
proc `?:`*(typ: static typedesc; bindings: sink openArray[(Value, Pattern)]): Pattern {.inline.} =
|
||||||
patterns.grab(typ, bindings)
|
patterns.grab(typ, bindings)
|
||||||
|
|
||||||
proc `??`*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern {.inline.} =
|
proc `??`*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern {.inline.} =
|
||||||
patterns.inject(pat, bindings)
|
patterns.inject(pat, bindings)
|
||||||
|
|
||||||
proc `?`*[T](val: T): Pattern {.inline.} =
|
|
||||||
patterns.grab[T](val)
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Observe* = dataspace.Observe[Ref]
|
PublishProc = proc (turn: var Turn; v: Value; h: Handle) {.closure, gcsafe.}
|
||||||
PublishProc = proc (turn: var Turn; v: Assertion; h: Handle) {.closure, gcsafe.}
|
|
||||||
RetractProc = proc (turn: var Turn; 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
|
ClosureEntity = ref object of Entity
|
||||||
publishImpl: PublishProc
|
publishImpl: PublishProc
|
||||||
retractImpl: RetractProc
|
retractImpl: RetractProc
|
||||||
|
@ -60,17 +55,20 @@ proc argumentCount(handler: NimNode): int =
|
||||||
handler.expectKind {nnkDo, nnkStmtList}
|
handler.expectKind {nnkDo, nnkStmtList}
|
||||||
if handler.kind == nnkDo: result = pred handler[3].len
|
if handler.kind == nnkDo: result = pred handler[3].len
|
||||||
|
|
||||||
proc wrapPublishHandler(handler: NimNode): NimNode =
|
type HandlerNodes = tuple
|
||||||
handler.expectKind {nnkDo, nnkStmtList}
|
valuesSym, varSection, body: NimNode
|
||||||
var innerProc = newNimNode(nnkProcDef)
|
|
||||||
handler.copyChildrenTo innerProc
|
proc generateHandlerNodes(handler: NimNode): HandlerNodes =
|
||||||
innerProc[0] = genSym(nskProc, "message")
|
handler.expectKind {nnkStmtList, nnkDo}
|
||||||
var
|
result.valuesSym = genSym(nskVar, "values")
|
||||||
valuesSym = genSym(nskVar, "values")
|
let valuesTuple = newNimNode(nnkTupleTy, handler)
|
||||||
valuesTuple = newNimNode(nnkTupleTy, handler)
|
case handler.kind
|
||||||
innerTuple = newNimNode(nnkVarTuple, handler)
|
of nnkStmtList:
|
||||||
varSectionInner = newNimNode(nnkVarSection, handler).add(innerTuple)
|
result.body = handler
|
||||||
if handler.kind == nnkDo:
|
of nnkDo:
|
||||||
|
let
|
||||||
|
innerTuple = newNimNode(nnkVarTuple, handler)
|
||||||
|
varSectionInner = newNimNode(nnkVarSection, handler).add(innerTuple)
|
||||||
for i, arg in handler[3]:
|
for i, arg in handler[3]:
|
||||||
if i > 0:
|
if i > 0:
|
||||||
arg.expectKind nnkIdentDefs
|
arg.expectKind nnkIdentDefs
|
||||||
|
@ -80,169 +78,125 @@ proc wrapPublishHandler(handler: NimNode): NimNode =
|
||||||
arg.copyChildrenTo def
|
arg.copyChildrenTo def
|
||||||
valuesTuple.add(def)
|
valuesTuple.add(def)
|
||||||
innerTuple.add(arg[0])
|
innerTuple.add(arg[0])
|
||||||
innerTuple.add(newEmptyNode(), valuesSym)
|
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 =
|
||||||
var
|
var
|
||||||
varSectionOuter = newNimNode(nnkVarSection, handler).add(
|
(valuesSym, varSection, publishBody) =
|
||||||
newIdentDefs(valuesSym, valuesTuple))
|
generateHandlerNodes(handler)
|
||||||
publishBody =
|
|
||||||
if handler.kind == nnkStmtList: handler
|
|
||||||
else: newStmtList(varSectionInner, handler[6])
|
|
||||||
turnSym = ident"turn"
|
|
||||||
handleSym = ident"handle"
|
handleSym = ident"handle"
|
||||||
handlerSym = genSym(nskProc, "publish")
|
handlerSym = genSym(nskProc, "publish")
|
||||||
|
bindingsSym = ident"bindings"
|
||||||
quote do:
|
quote do:
|
||||||
proc `handlerSym`(`turnSym`: var Turn; bindings: Assertion; `handleSym`: Handle) =
|
proc `handlerSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle) =
|
||||||
`varSectionOuter`
|
`varSection`
|
||||||
if fromPreserve(`valuesSym`, bindings):
|
if fromPreserves(`valuesSym`, bindings):
|
||||||
`publishBody`
|
`publishBody`
|
||||||
|
|
||||||
proc wrapMessageHandler(handler: NimNode): NimNode =
|
proc wrapMessageHandler(turn, handler: NimNode): NimNode =
|
||||||
handler.expectKind {nnkDo, nnkStmtList}
|
|
||||||
var innerProc = newNimNode(nnkProcDef)
|
|
||||||
handler.copyChildrenTo innerProc
|
|
||||||
innerProc[0] = genSym(nskProc, "message")
|
|
||||||
var
|
var
|
||||||
valuesSym = genSym(nskVar, "values")
|
(valuesSym, varSection, body) =
|
||||||
valuesTuple = newNimNode(nnkTupleTy, handler)
|
generateHandlerNodes(handler)
|
||||||
innerTuple = newNimNode(nnkVarTuple, handler)
|
|
||||||
varSectionInner = newNimNode(nnkVarSection, handler).add(innerTuple)
|
|
||||||
if handler.kind == nnkDo:
|
|
||||||
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])
|
|
||||||
innerTuple.add(newEmptyNode(), valuesSym)
|
|
||||||
var
|
|
||||||
varSectionOuter = newNimNode(nnkVarSection, handler).add(
|
|
||||||
newIdentDefs(valuesSym, valuesTuple))
|
|
||||||
body = newStmtList(varSectionInner, handler[6])
|
|
||||||
turnSym = ident"turn"
|
|
||||||
handlerSym = genSym(nskProc, "message")
|
handlerSym = genSym(nskProc, "message")
|
||||||
|
bindingsSym = ident"bindings"
|
||||||
quote do:
|
quote do:
|
||||||
proc `handlerSym`(`turnSym`: var Turn; bindings: Assertion) =
|
proc `handlerSym`(`turn`: var Turn; `bindingsSym`: Value) =
|
||||||
`varSectionOuter`
|
`varSection`
|
||||||
if fromPreserve(`valuesSym`, bindings):
|
if fromPreserves(`valuesSym`, bindings):
|
||||||
`body`
|
`body`
|
||||||
|
|
||||||
macro onPublish*(turn: Turn; ds: Ref; pattern: Pattern; handler: untyped) =
|
proc wrapDuringHandler(turn, entryBody, exitBody: NimNode): NimNode =
|
||||||
## Call `handler` when an assertion matching `pattern` is published at `ds`.
|
|
||||||
let
|
|
||||||
argCount = argumentCount(handler)
|
|
||||||
handlerProc = wrapPublishHandler(handler)
|
|
||||||
handlerSym = handlerProc[0]
|
|
||||||
result = quote do:
|
|
||||||
if `pattern`.analyse.capturePaths.len != `argCount`:
|
|
||||||
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments")
|
|
||||||
`handlerProc`
|
|
||||||
discard observe(`turn`, `ds`, `pattern`, ClosureEntity(publishImpl: `handlerSym`))
|
|
||||||
|
|
||||||
macro onMessage*(turn: Turn; ds: Ref; pattern: Pattern; handler: untyped) =
|
|
||||||
## Call `handler` when an message matching `pattern` is broadcasted at `ds`.
|
|
||||||
let
|
|
||||||
argCount = argumentCount(handler)
|
|
||||||
handlerProc = wrapMessageHandler(handler)
|
|
||||||
handlerSym = handlerProc[0]
|
|
||||||
result = quote do:
|
|
||||||
if `pattern`.analyse.capturePaths.len != `argCount`:
|
|
||||||
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments")
|
|
||||||
`handlerProc`
|
|
||||||
discard observe(`turn`, `ds`, `pattern`, ClosureEntity(messageImpl: `handlerSym`))
|
|
||||||
|
|
||||||
proc wrapDuringHandler(entryBody, exitBody: NimNode): NimNode =
|
|
||||||
entryBody.expectKind {nnkDo, nnkStmtList}
|
|
||||||
var innerProc = newNimNode(nnkProcDef)
|
|
||||||
entryBody.copyChildrenTo innerProc
|
|
||||||
innerProc[0] = genSym(nskProc, "during")
|
|
||||||
var
|
var
|
||||||
valuesSym = ident("rawValues")
|
(valuesSym, varSection, publishBody) =
|
||||||
valuesTuple = newNimNode(nnkTupleTy, entryBody)
|
generateHandlerNodes(entryBody)
|
||||||
innerTuple = newNimNode(nnkVarTuple, entryBody)
|
|
||||||
varSectionInner = newNimNode(nnkVarSection, entryBody).add(innerTuple)
|
|
||||||
if entryBody.kind == nnkDo:
|
|
||||||
for i, arg in entryBody[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])
|
|
||||||
innerTuple.add(newEmptyNode(), valuesSym)
|
|
||||||
var
|
|
||||||
varSectionOuter = newNimNode(nnkVarSection, entryBody).add(
|
|
||||||
newIdentDefs(valuesSym, valuesTuple))
|
|
||||||
publishBody =
|
|
||||||
if entryBody.kind == nnkStmtList: entryBody
|
|
||||||
else: newStmtList(varSectionInner, entryBody[6])
|
|
||||||
turnSym = ident"turn"
|
|
||||||
bindingsSym = ident"bindings"
|
bindingsSym = ident"bindings"
|
||||||
handleSym = ident"duringHandle"
|
handleSym = ident"duringHandle"
|
||||||
duringSym = genSym(nskProc, "during")
|
duringSym = genSym(nskProc, "during")
|
||||||
if exitBody.isNil:
|
if exitBody.isNil:
|
||||||
quote do:
|
quote do:
|
||||||
proc `duringSym`(`turnSym`: var Turn; `bindingsSym`: Assertion; `handleSym`: Handle): TurnAction =
|
proc `duringSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle): TurnAction =
|
||||||
`varSectionOuter`
|
`varSection`
|
||||||
if fromPreserve(`valuesSym`, `bindingsSym`):
|
if fromPreserves(`valuesSym`, `bindingsSym`):
|
||||||
`publishBody`
|
`publishBody`
|
||||||
else:
|
else:
|
||||||
quote do:
|
quote do:
|
||||||
proc `duringSym`(`turnSym`: var Turn; `bindingsSym`: Assertion; `handleSym`: Handle): TurnAction =
|
proc `duringSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle): TurnAction =
|
||||||
`varSectionOuter`
|
`varSection`
|
||||||
if fromPreserve(`valuesSym`, `bindingsSym`):
|
if fromPreserves(`valuesSym`, `bindingsSym`):
|
||||||
`publishBody`
|
`publishBody`
|
||||||
proc action(`turnSym`: var Turn) =
|
proc action(`turn`: var Turn) =
|
||||||
`exitBody`
|
`exitBody`
|
||||||
result = action
|
result = action
|
||||||
|
|
||||||
macro during*(turn: var Turn; ds: Ref; pattern: Pattern; publishBody, retractBody: untyped) =
|
macro onPublish*(turn: untyped; ds: Cap; pattern: Pattern; handler: untyped) =
|
||||||
|
## Call `handler` when an assertion matching `pattern` is published at `ds`.
|
||||||
|
let
|
||||||
|
argCount = argumentCount(handler)
|
||||||
|
handlerProc = wrapPublishHandler(turn, handler)
|
||||||
|
handlerSym = handlerProc[0]
|
||||||
|
result = quote do:
|
||||||
|
if `argCount` != 0 and `pattern`.analyse.capturePaths.len != `argCount`:
|
||||||
|
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments - " & $`pattern`)
|
||||||
|
`handlerProc`
|
||||||
|
discard observe(`turn`, `ds`, `pattern`, ClosureEntity(publishImpl: `handlerSym`))
|
||||||
|
|
||||||
|
macro onMessage*(turn: untyped; ds: Cap; pattern: Pattern; handler: untyped) =
|
||||||
|
## Call `handler` when an message matching `pattern` is broadcasted at `ds`.
|
||||||
|
let
|
||||||
|
argCount = argumentCount(handler)
|
||||||
|
handlerProc = wrapMessageHandler(turn, handler)
|
||||||
|
handlerSym = handlerProc[0]
|
||||||
|
result = quote do:
|
||||||
|
if `argCount` != 0 and `pattern`.analyse.capturePaths.len != `argCount`:
|
||||||
|
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments - " & $`pattern`)
|
||||||
|
`handlerProc`
|
||||||
|
discard observe(`turn`, `ds`, `pattern`, ClosureEntity(messageImpl: `handlerSym`))
|
||||||
|
|
||||||
|
macro during*(turn: untyped; ds: Cap; pattern: Pattern; publishBody, retractBody: untyped) =
|
||||||
## Call `publishBody` when an assertion matching `pattern` is published to `ds` and
|
## Call `publishBody` when an assertion matching `pattern` is published to `ds` and
|
||||||
## call `retractBody` on retraction. Assertions that match `pattern` but are not
|
## call `retractBody` on retraction. Assertions that match `pattern` but are not
|
||||||
## convertable to the arguments of `publishBody` are silently discarded.
|
## convertable to the arguments of `publishBody` are silently discarded.
|
||||||
##
|
##
|
||||||
## The following symbols are injected into the scope of both bodies:
|
## The following symbols are injected into the scope of both bodies:
|
||||||
## - `turn` - active turn at entry of `publishBody` and `retractBody`
|
|
||||||
## - `bindings` - raw Preserves sequence that matched `pattern`
|
## - `bindings` - raw Preserves sequence that matched `pattern`
|
||||||
## - `duringHandle` - dataspace handle of the assertion that triggered `publishBody`
|
## - `duringHandle` - dataspace handle of the assertion that triggered `publishBody`
|
||||||
let
|
let
|
||||||
argCount = argumentCount(publishBody)
|
argCount = argumentCount(publishBody)
|
||||||
callbackProc = wrapDuringHandler(publishBody, retractBody)
|
callbackProc = wrapDuringHandler(turn, publishBody, retractBody)
|
||||||
callbackSym = callbackProc[0]
|
callbackSym = callbackProc[0]
|
||||||
result = quote do:
|
result = quote do:
|
||||||
if `pattern`.analyse.capturePaths.len != `argCount`:
|
if `argCount` != 0 and `pattern`.analyse.capturePaths.len != `argCount`:
|
||||||
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments")
|
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments - " & $`pattern`)
|
||||||
`callbackProc`
|
`callbackProc`
|
||||||
discard observe(`turn`, `ds`, `pattern`, during(`callbackSym`))
|
discard observe(`turn`, `ds`, `pattern`, during(`callbackSym`))
|
||||||
|
|
||||||
macro during*(turn: var Turn; ds: Ref; pattern: Pattern; publishBody: untyped) =
|
macro during*(turn: untyped; ds: Cap; pattern: Pattern; publishBody: untyped) =
|
||||||
## Variant of `during` without a retract body.
|
## Variant of `during` without a retract body.
|
||||||
let
|
let
|
||||||
argCount = argumentCount(publishBody)
|
`argCount` = argumentCount(publishBody)
|
||||||
callbackProc = wrapDuringHandler(publishBody, nil)
|
callbackProc = wrapDuringHandler(turn, publishBody, nil)
|
||||||
callbackSym = callbackProc[0]
|
callbackSym = callbackProc[0]
|
||||||
result = quote do:
|
result = quote do:
|
||||||
if `pattern`.analyse.capturePaths.len != `argCount`:
|
if `argCount` != 0 and `pattern`.analyse.capturePaths.len != `argCount`:
|
||||||
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments")
|
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments - " & $`pattern`)
|
||||||
`callbackProc`
|
`callbackProc`
|
||||||
discard observe(`turn`, `ds`, `pattern`, during(`callbackSym`))
|
discard observe(`turn`, `ds`, `pattern`, during(`callbackSym`))
|
||||||
|
|
||||||
type BootProc = proc (ds: Ref; turn: var Turn) {.gcsafe.}
|
type BootProc = proc (turn: var Turn; ds: Cap) {.gcsafe.}
|
||||||
|
type DeprecatedBootProc = proc (ds: Cap; turn: var Turn) {.gcsafe.}
|
||||||
from std/os import getEnv
|
|
||||||
|
|
||||||
proc runActor*(name: string; bootProc: BootProc) =
|
proc runActor*(name: string; bootProc: BootProc) =
|
||||||
## Run an `Actor` to completion.
|
## Run an `Actor` to completion.
|
||||||
let actor = bootDataspace(name, bootProc)
|
let actor = bootDataspace(name, bootProc)
|
||||||
if getEnv"SYNDICATE_DEBUG" == "":
|
while actor.running:
|
||||||
while not actor.future.finished:
|
waitFor sleepAsync(500)
|
||||||
poll()
|
|
||||||
else:
|
proc runActor*(name: string; bootProc: DeprecatedBootProc) {.deprecated.} =
|
||||||
while not actor.future.finished:
|
## Run an `Actor` to completion.
|
||||||
stderr.writeLine("Polling ", name, " actor…")
|
runActor(name) do (turn: var Turn, ds: Cap):
|
||||||
poll()
|
bootProc(ds, turn)
|
||||||
read(actor.future)
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
include_rules
|
include_rules
|
||||||
NIM_FLAGS += --path:$(TUP_CWD)/..
|
NIM_FLAGS += --path:$(TUP_CWD)/..
|
||||||
: foreach *.nim |> !nim_check |>
|
: foreach *.nim |> !nim_check |>
|
||||||
: capabilities.nim |> !nim_bin |> $(BIN_DIR)/mint
|
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# 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 preserves
|
||||||
import ../syndicate/protocols/[protocol, sturdy]
|
import ../syndicate/protocols/[protocol, sturdy]
|
||||||
|
|
||||||
|
const tracing = defined(traceSyndicate)
|
||||||
|
|
||||||
|
when tracing:
|
||||||
|
import std/streams
|
||||||
|
from std/os import getEnv
|
||||||
|
import ./protocols/trace
|
||||||
|
|
||||||
export Handle
|
export Handle
|
||||||
|
|
||||||
template generateIdType(typ: untyped) =
|
template generateIdType(typ: untyped) =
|
||||||
|
@ -20,66 +27,91 @@ generateIdType(TurnId)
|
||||||
|
|
||||||
type
|
type
|
||||||
Oid = sturdy.Oid
|
Oid = sturdy.Oid
|
||||||
Assertion* = Preserve[Ref]
|
Caveat = sturdy.Caveat
|
||||||
Caveat = sturdy.Caveat[Ref]
|
|
||||||
Attenuation = seq[Caveat]
|
Attenuation = seq[Caveat]
|
||||||
Rewrite = sturdy.Rewrite[Ref]
|
Rewrite = sturdy.Rewrite
|
||||||
|
|
||||||
|
AssertionRef* = ref object
|
||||||
|
value*: Value
|
||||||
|
# if the Enity methods take a Value object then the generated
|
||||||
|
# C code has "redefinition of struct" problems when orc is enabled
|
||||||
|
|
||||||
Entity* = ref object of RootObj
|
Entity* = ref object of RootObj
|
||||||
oid*: Oid # oid is how Entities are identified over the wire
|
oid*: Oid # oid is how Entities are identified over the wire
|
||||||
|
|
||||||
Ref* {.unpreservable.} = ref object # TODO: rename
|
Cap* {.preservesEmbedded.} = ref object of EmbeddedObj
|
||||||
relay*: Facet
|
relay*: Facet
|
||||||
target*: Entity
|
target*: Entity
|
||||||
attenuation*: Attenuation
|
attenuation*: Attenuation
|
||||||
|
|
||||||
|
Ref* {.deprecated: "Ref was renamed to Cap".} = Cap
|
||||||
|
|
||||||
OutboundAssertion = ref object
|
OutboundAssertion = ref object
|
||||||
handle: Handle
|
handle: Handle
|
||||||
peer: Ref
|
peer: Cap
|
||||||
established: bool
|
established: bool
|
||||||
OutboundTable = Table[Handle, OutboundAssertion]
|
OutboundTable = Table[Handle, OutboundAssertion]
|
||||||
|
|
||||||
Actor* = ref object
|
Actor* = ref object
|
||||||
future: Future[void]
|
|
||||||
name: string
|
name: string
|
||||||
id: ActorId
|
handleAllocator: ref Handle
|
||||||
handleAllocator: Handle
|
# a fresh actor gets a new ref Handle and
|
||||||
|
# all actors spawned from it get the same ref.
|
||||||
root: Facet
|
root: Facet
|
||||||
exitReason: ref Exception
|
exitReason: ref Exception
|
||||||
exitHooks: seq[TurnAction]
|
exitHooks: seq[TurnAction]
|
||||||
exiting: bool
|
id: ActorId
|
||||||
|
exiting, exited: bool
|
||||||
|
when tracing:
|
||||||
|
turnIdAllocator: ref TurnId
|
||||||
|
traceStream: FileStream
|
||||||
|
|
||||||
TurnAction* = proc (t: var Turn) {.gcsafe.}
|
TurnAction* = proc (t: var Turn) {.gcsafe.}
|
||||||
|
|
||||||
Queues = TableRef[Facet, seq[TurnAction]]
|
Queues = TableRef[Facet, seq[TurnAction]]
|
||||||
|
|
||||||
Turn* = object # an object that should remain on the stack
|
Turn* = object # an object that should remain on the stack
|
||||||
id: TurnId
|
|
||||||
facet: Facet
|
facet: Facet
|
||||||
queues: Queues # a ref object that can outlive Turn
|
queues: Queues # a ref object that can outlive Turn
|
||||||
|
when tracing:
|
||||||
ParentFacet = Option[Facet]
|
desc: TurnDescription
|
||||||
|
|
||||||
Facet* = ref FacetObj
|
Facet* = ref FacetObj
|
||||||
FacetObj = object
|
FacetObj = object
|
||||||
id: FacetId
|
|
||||||
actor*: Actor
|
actor*: Actor
|
||||||
parent: ParentFacet
|
parent: Facet
|
||||||
children: HashSet[Facet]
|
children: HashSet[Facet]
|
||||||
outbound: OutboundTable
|
outbound: OutboundTable
|
||||||
shutdownActions: seq[TurnAction]
|
shutdownActions: seq[TurnAction]
|
||||||
inertCheckPreventers: int
|
inertCheckPreventers: int
|
||||||
|
id: FacetId
|
||||||
isAlive: bool
|
isAlive: bool
|
||||||
|
|
||||||
type AssertionRef* = ref object
|
when tracing:
|
||||||
value*: Preserve[Ref]
|
|
||||||
# if the Enity methods take a Preserve[Ref] object then the generated
|
proc nextTurnId(facet: Facet): TurnId =
|
||||||
# C code has "redefinition of struct" problems when orc is enabled
|
result = succ(facet.actor.turnIdAllocator[])
|
||||||
|
facet.actor.turnIdAllocator[] = result
|
||||||
|
|
||||||
|
proc trace(actor: Actor; act: ActorActivation) =
|
||||||
|
if not actor.traceStream.isNil:
|
||||||
|
var entry = TraceEntry(
|
||||||
|
timestamp: getTime().toUnixFloat(),
|
||||||
|
actor: initRecord("named", actor.name.toPreserves),
|
||||||
|
item: act)
|
||||||
|
actor.traceStream.writeText entry.toPreserves
|
||||||
|
actor.traceStream.writeLine()
|
||||||
|
|
||||||
|
proc path(facet: Facet): seq[trace.FacetId] =
|
||||||
|
var f = facet
|
||||||
|
while not f.isNil:
|
||||||
|
result.add f.id.toPreserves
|
||||||
|
f = f.parent
|
||||||
|
|
||||||
method publish*(e: Entity; turn: var Turn; v: AssertionRef; h: Handle) {.base, gcsafe.} = discard
|
method publish*(e: Entity; turn: var Turn; v: AssertionRef; h: Handle) {.base, gcsafe.} = discard
|
||||||
method retract*(e: Entity; turn: var Turn; h: Handle) {.base, gcsafe.} = discard
|
method retract*(e: Entity; turn: var Turn; h: Handle) {.base, gcsafe.} = discard
|
||||||
method message*(e: Entity; turn: var Turn; v: AssertionRef) {.base, gcsafe.} = discard
|
method message*(e: Entity; turn: var Turn; v: AssertionRef) {.base, gcsafe.} = discard
|
||||||
method sync*(e: Entity; turn: var Turn; peer: Ref) {.base, gcsafe.} = discard
|
method sync*(e: Entity; turn: var Turn; peer: Cap) {.base, gcsafe.} = discard
|
||||||
|
|
||||||
using
|
using
|
||||||
actor: Actor
|
actor: Actor
|
||||||
|
@ -90,25 +122,26 @@ using
|
||||||
proc labels(f: Facet): string =
|
proc labels(f: Facet): string =
|
||||||
proc catLabels(f: Facet; labels: var string) =
|
proc catLabels(f: Facet; labels: var string) =
|
||||||
labels.add ':'
|
labels.add ':'
|
||||||
if f.parent.isSome:
|
if not f.parent.isNil:
|
||||||
catLabels(f.parent.get, labels)
|
catLabels(f.parent, labels)
|
||||||
labels.add ':'
|
labels.add ':'
|
||||||
labels.add $f.id
|
when tracing:
|
||||||
|
labels.add $f.id
|
||||||
result.add f.actor.name
|
result.add f.actor.name
|
||||||
catLabels(f, result)
|
catLabels(f, result)
|
||||||
|
|
||||||
proc `$`*(f: Facet): string =
|
proc `$`*(f: Facet): string =
|
||||||
"<Facet:" & f.labels & ">"
|
"<Facet:" & f.labels & ">"
|
||||||
|
|
||||||
proc `$`*(r: Ref): string =
|
proc `$`*(r: Cap): string =
|
||||||
"<Ref:" & r.relay.labels & ">"
|
"<Ref:" & r.relay.labels & ">"
|
||||||
|
|
||||||
proc `$`*(actor: Actor): string =
|
proc `$`*(actor: Actor): string =
|
||||||
"<Actor:" & actor.name & ">" # TODO: ambigous
|
"<Actor:" & actor.name & ">" # TODO: ambigous
|
||||||
|
|
||||||
proc attenuate(r: Ref; a: Attenuation): Ref =
|
proc attenuate(r: Cap; a: Attenuation): Cap =
|
||||||
if a.len == 0: result = r
|
if a.len == 0: result = r
|
||||||
else: result = Ref(
|
else: result = Cap(
|
||||||
relay: r.relay,
|
relay: r.relay,
|
||||||
target: r.target,
|
target: r.target,
|
||||||
attenuation: a & r.attenuation)
|
attenuation: a & r.attenuation)
|
||||||
|
@ -116,11 +149,11 @@ proc attenuate(r: Ref; a: Attenuation): Ref =
|
||||||
proc hash*(facet): Hash =
|
proc hash*(facet): Hash =
|
||||||
facet.id.hash
|
facet.id.hash
|
||||||
|
|
||||||
proc hash*(r: Ref): Hash = !$(r.relay.hash !& r.target.unsafeAddr.hash)
|
proc hash*(r: Cap): Hash = !$(r.relay.hash !& r.target.unsafeAddr.hash)
|
||||||
|
|
||||||
proc nextHandle(facet: Facet): Handle =
|
proc nextHandle(facet: Facet): Handle =
|
||||||
inc facet.actor.handleAllocator
|
result = succ(facet.actor.handleAllocator[])
|
||||||
facet.actor.handleAllocator
|
facet.actor.handleAllocator[] = result
|
||||||
|
|
||||||
proc facet*(turn: var Turn): Facet = turn.facet
|
proc facet*(turn: var Turn): Facet = turn.facet
|
||||||
|
|
||||||
|
@ -130,9 +163,9 @@ proc enqueue(turn: var Turn; target: Facet; action: TurnAction) =
|
||||||
else:
|
else:
|
||||||
turn.queues[target] = @[action]
|
turn.queues[target] = @[action]
|
||||||
|
|
||||||
type Bindings = Table[Preserve[Ref], Preserve[Ref]]
|
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
|
case p.orKind
|
||||||
of PatternKind.Pdiscard: result = true
|
of PatternKind.Pdiscard: result = true
|
||||||
of PatternKind.Patom:
|
of PatternKind.Patom:
|
||||||
|
@ -148,7 +181,7 @@ proc match(bindings: var Bindings; p: Pattern; v: Assertion): bool =
|
||||||
result = v.isEmbedded
|
result = v.isEmbedded
|
||||||
of PatternKind.Pbind:
|
of PatternKind.Pbind:
|
||||||
if match(bindings, p.pbind.pattern, v):
|
if match(bindings, p.pbind.pattern, v):
|
||||||
bindings[toPreserve(p.pbind.pattern, Ref)] = v
|
bindings[p.pbind.pattern.toPreserves] = v
|
||||||
result = true
|
result = true
|
||||||
of PatternKind.Pand:
|
of PatternKind.Pand:
|
||||||
for pp in p.pand.patterns:
|
for pp in p.pand.patterns:
|
||||||
|
@ -186,21 +219,22 @@ proc match(bindings: var Bindings; p: Pattern; v: Assertion): bool =
|
||||||
result = true
|
result = true
|
||||||
break
|
break
|
||||||
|
|
||||||
proc match(p: Pattern; v: Assertion): Option[Bindings] =
|
proc match(p: Pattern; v: Value): Option[Bindings] =
|
||||||
var b: Bindings
|
var b: Bindings
|
||||||
if match(b, p, v):
|
if match(b, p, v):
|
||||||
result = some b
|
result = some b
|
||||||
|
|
||||||
proc instantiate(t: Template; bindings: Bindings): Assertion =
|
proc instantiate(t: Template; bindings: Bindings): Value =
|
||||||
case t.orKind
|
case t.orKind
|
||||||
of TemplateKind.Tattenuate:
|
of TemplateKind.Tattenuate:
|
||||||
let v = instantiate(t.tattenuate.template, bindings)
|
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")
|
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:
|
of TemplateKind.TRef:
|
||||||
let n = $t.tref.binding.int
|
let n = $t.tref.binding.int
|
||||||
try: result = bindings[toPreserve(n, Ref)]
|
try: result = bindings[n.toPreserves]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise newException(ValueError, "unbound reference: " & n)
|
raise newException(ValueError, "unbound reference: " & n)
|
||||||
of TemplateKind.Lit:
|
of TemplateKind.Lit:
|
||||||
|
@ -212,20 +246,20 @@ proc instantiate(t: Template; bindings: Bindings): Assertion =
|
||||||
for i, tt in t.tcompound.rec.fields:
|
for i, tt in t.tcompound.rec.fields:
|
||||||
result[i] = instantiate(tt, bindings)
|
result[i] = instantiate(tt, bindings)
|
||||||
of TCompoundKind.arr:
|
of TCompoundKind.arr:
|
||||||
result = initSequence(t.tcompound.arr.items.len, Ref)
|
result = initSequence(t.tcompound.arr.items.len)
|
||||||
for i, tt in t.tcompound.arr.items:
|
for i, tt in t.tcompound.arr.items:
|
||||||
result[i] = instantiate(tt, bindings)
|
result[i] = instantiate(tt, bindings)
|
||||||
of TCompoundKind.dict:
|
of TCompoundKind.dict:
|
||||||
result = initDictionary(Ref)
|
result = initDictionary()
|
||||||
for key, tt in t.tcompound.dict.entries:
|
for key, tt in t.tcompound.dict.entries:
|
||||||
result[key] = instantiate(tt, bindings)
|
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)
|
let bindings = match(r.pattern, v)
|
||||||
if bindings.isSome:
|
if bindings.isSome:
|
||||||
result = instantiate(r.template, get bindings)
|
result = instantiate(r.template, get bindings)
|
||||||
|
|
||||||
proc examineAlternatives(cav: Caveat; v: Assertion): Assertion =
|
proc examineAlternatives(cav: Caveat; v: Value): Value =
|
||||||
case cav.orKind
|
case cav.orKind
|
||||||
of CaveatKind.Rewrite:
|
of CaveatKind.Rewrite:
|
||||||
result = rewrite(cav.rewrite, v)
|
result = rewrite(cav.rewrite, v)
|
||||||
|
@ -236,13 +270,13 @@ proc examineAlternatives(cav: Caveat; v: Assertion): Assertion =
|
||||||
of CaveatKind.Reject: discard
|
of CaveatKind.Reject: discard
|
||||||
of CaveatKind.unknown: discard
|
of CaveatKind.unknown: discard
|
||||||
|
|
||||||
proc runRewrites*(a: Attenuation; v: Assertion): Assertion =
|
proc runRewrites*(a: Attenuation; v: Value): Value =
|
||||||
result = v
|
result = v
|
||||||
for stage in a:
|
for stage in a:
|
||||||
result = examineAlternatives(stage, result)
|
result = examineAlternatives(stage, result)
|
||||||
if result.isFalse: break
|
if result.isFalse: break
|
||||||
|
|
||||||
proc publish(turn: var Turn; r: Ref; v: Assertion; h: Handle) =
|
proc publish(turn: var Turn; r: Cap; v: Value; h: Handle) =
|
||||||
var a = runRewrites(r.attenuation, v)
|
var a = runRewrites(r.attenuation, v)
|
||||||
if not a.isFalse:
|
if not a.isFalse:
|
||||||
let e = OutboundAssertion(
|
let e = OutboundAssertion(
|
||||||
|
@ -251,13 +285,23 @@ proc publish(turn: var Turn; r: Ref; v: Assertion; h: Handle) =
|
||||||
enqueue(turn, r.relay) do (turn: var Turn):
|
enqueue(turn, r.relay) do (turn: var Turn):
|
||||||
e.established = true
|
e.established = true
|
||||||
publish(r.target, turn, AssertionRef(value: a), e.handle)
|
publish(r.target, turn, AssertionRef(value: a), e.handle)
|
||||||
|
when tracing:
|
||||||
|
var act = ActionDescription(orKind: ActionDescriptionKind.enqueue)
|
||||||
|
act.enqueue.event.target.actor = turn.facet.actor.id.toPreserves
|
||||||
|
act.enqueue.event.target.facet = turn.facet.id.toPreserves
|
||||||
|
act.enqueue.event.target.oid = r.target.oid.toPreserves
|
||||||
|
act.enqueue.event.detail = trace.TurnEvent(orKind: TurnEventKind.assert)
|
||||||
|
act.enqueue.event.detail.assert.assertion.value.value =
|
||||||
|
mapEmbeds(v) do (cap: Value) -> Value: discard
|
||||||
|
act.enqueue.event.detail.assert.handle = h
|
||||||
|
turn.desc.actions.add act
|
||||||
|
|
||||||
proc publish*(turn: var Turn; r: Ref; a: Assertion): Handle =
|
proc publish*(turn: var Turn; r: Cap; a: Value): Handle {.discardable.} =
|
||||||
result = turn.facet.nextHandle()
|
result = turn.facet.nextHandle()
|
||||||
publish(turn, r, a, result)
|
publish(turn, r, a, result)
|
||||||
|
|
||||||
proc publish*[T](turn: var Turn; r: Ref; a: T): Handle =
|
proc publish*[T](turn: var Turn; r: Cap; a: T): Handle {.discardable.} =
|
||||||
publish(turn, r, toPreserve(a, Ref))
|
publish(turn, r, a.toPreserves)
|
||||||
|
|
||||||
proc retract(turn: var Turn; e: OutboundAssertion) =
|
proc retract(turn: var Turn; e: OutboundAssertion) =
|
||||||
enqueue(turn, e.peer.relay) do (turn: var Turn):
|
enqueue(turn, e.peer.relay) do (turn: var Turn):
|
||||||
|
@ -270,31 +314,30 @@ proc retract*(turn: var Turn; h: Handle) =
|
||||||
if turn.facet.outbound.pop(h, e):
|
if turn.facet.outbound.pop(h, e):
|
||||||
turn.retract(e)
|
turn.retract(e)
|
||||||
|
|
||||||
proc message*(turn: var Turn; r: Ref; v: Assertion) =
|
proc message*(turn: var Turn; r: Cap; v: Value) =
|
||||||
var a = runRewrites(r.attenuation, v)
|
var a = runRewrites(r.attenuation, v)
|
||||||
if not a.isFalse:
|
if not a.isFalse:
|
||||||
enqueue(turn, r.relay) do (turn: var Turn):
|
enqueue(turn, r.relay) do (turn: var Turn):
|
||||||
r.target.message(turn, AssertionRef(value: a))
|
r.target.message(turn, AssertionRef(value: a))
|
||||||
|
|
||||||
proc message*[T](turn: var Turn; r: Ref; v: T) =
|
proc message*[T](turn: var Turn; r: Cap; v: T) =
|
||||||
message(turn, r, toPreserve(v, Ref))
|
message(turn, r, v.toPreserves)
|
||||||
|
|
||||||
proc sync(turn: var Turn; e: Entity; peer: Ref) =
|
proc sync(turn: var Turn; e: Entity; peer: Cap) =
|
||||||
e.sync(turn, peer)
|
e.sync(turn, peer)
|
||||||
# or turn.message(peer, true) ?
|
|
||||||
|
|
||||||
proc sync*(turn: var Turn; r, peer: Ref) =
|
proc sync*(turn: var Turn; r, peer: Cap) =
|
||||||
enqueue(turn, r.relay) do (turn: var Turn):
|
enqueue(turn, r.relay) do (turn: var Turn):
|
||||||
sync(turn, r.target, peer)
|
sync(turn, r.target, peer)
|
||||||
|
|
||||||
proc replace*[T](turn: var Turn; `ref`: Ref; h: Handle; v: T): Handle =
|
proc replace*[T](turn: var Turn; cap: Cap; h: Handle; v: T): Handle =
|
||||||
result = publish(turn, `ref`, v)
|
result = publish(turn, cap, v)
|
||||||
if h != default(Handle):
|
if h != default(Handle):
|
||||||
retract(turn, h)
|
retract(turn, h)
|
||||||
|
|
||||||
proc replace*[T](turn: var Turn; `ref`: Ref; h: var Handle; v: T): Handle {.discardable.} =
|
proc replace*[T](turn: var Turn; cap: Cap; h: var Handle; v: T): Handle {.discardable.} =
|
||||||
var old = h
|
var old = h
|
||||||
h = publish(turn, `ref`, v)
|
h = publish(turn, cap, v)
|
||||||
if old != default(Handle):
|
if old != default(Handle):
|
||||||
retract(turn, old)
|
retract(turn, old)
|
||||||
h
|
h
|
||||||
|
@ -303,22 +346,22 @@ proc stop*(turn: var Turn) {.gcsafe.}
|
||||||
|
|
||||||
proc run*(facet; action: TurnAction; zombieTurn = false) {.gcsafe.}
|
proc run*(facet; action: TurnAction; zombieTurn = false) {.gcsafe.}
|
||||||
|
|
||||||
proc newFacet(actor; parent: ParentFacet; initialAssertions: OutboundTable): Facet =
|
proc newFacet(actor; parent: Facet; initialAssertions: OutboundTable): Facet =
|
||||||
result = Facet(
|
result = Facet(
|
||||||
id: getMonoTime().ticks.FacetId,
|
id: getMonoTime().ticks.FacetId,
|
||||||
actor: actor,
|
actor: actor,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
outbound: initialAssertions,
|
outbound: initialAssertions,
|
||||||
isAlive: true)
|
isAlive: true)
|
||||||
if parent.isSome: parent.get.children.incl result
|
if not parent.isNil: parent.children.incl result
|
||||||
|
|
||||||
proc newFacet(actor; parent: ParentFacet): Facet =
|
proc newFacet(actor; parent: Facet): Facet =
|
||||||
var initialAssertions: OutboundTable
|
var initialAssertions: OutboundTable
|
||||||
newFacet(actor, parent, initialAssertions)
|
newFacet(actor, parent, initialAssertions)
|
||||||
|
|
||||||
proc isInert(facet): bool =
|
proc isInert(facet): bool =
|
||||||
result = facet.children.len == 0 and
|
result = facet.children.len == 0 and
|
||||||
(facet.outbound.len == 0 or facet.parent.isNone) and
|
(facet.outbound.len == 0 or facet.parent.isNil) and
|
||||||
facet.inertCheckPreventers == 0
|
facet.inertCheckPreventers == 0
|
||||||
|
|
||||||
proc preventInertCheck*(facet): (proc() {.gcsafe.}) {.discardable.} =
|
proc preventInertCheck*(facet): (proc() {.gcsafe.}) {.discardable.} =
|
||||||
|
@ -342,8 +385,8 @@ proc terminate(facet; turn: var Turn; orderly: bool) {.gcsafe.} =
|
||||||
if facet.isAlive:
|
if facet.isAlive:
|
||||||
facet.isAlive = false
|
facet.isAlive = false
|
||||||
let parent = facet.parent
|
let parent = facet.parent
|
||||||
if parent.isSome:
|
if not parent.isNil:
|
||||||
parent.get.children.excl facet
|
parent.children.excl facet
|
||||||
block:
|
block:
|
||||||
var turn = Turn(facet: facet, queues: turn.queues)
|
var turn = Turn(facet: facet, queues: turn.queues)
|
||||||
while facet.children.len > 0:
|
while facet.children.len > 0:
|
||||||
|
@ -353,55 +396,87 @@ proc terminate(facet; turn: var Turn; orderly: bool) {.gcsafe.} =
|
||||||
act(turn)
|
act(turn)
|
||||||
for a in facet.outbound.values: turn.retract(a)
|
for a in facet.outbound.values: turn.retract(a)
|
||||||
if orderly:
|
if orderly:
|
||||||
if parent.isSome:
|
if not parent.isNil:
|
||||||
if parent.get.isInert:
|
if parent.isInert:
|
||||||
parent.get.terminate(turn, true)
|
parent.terminate(turn, true)
|
||||||
else:
|
else:
|
||||||
terminate(facet.actor, turn, nil)
|
terminate(facet.actor, turn, nil)
|
||||||
|
when tracing:
|
||||||
|
var act = ActionDescription(orKind: ActionDescriptionKind.facetStop)
|
||||||
|
act.facetstop.path = facet.path
|
||||||
|
turn.desc.actions.add act
|
||||||
|
|
||||||
proc stopIfInertAfter(action: TurnAction): TurnAction =
|
proc stopIfInertAfter(action: TurnAction): TurnAction =
|
||||||
proc wrapper(turn: var Turn) =
|
proc wrapper(turn: var Turn) =
|
||||||
action(turn)
|
action(turn)
|
||||||
enqueue(turn, turn.facet) do (turn: var Turn):
|
enqueue(turn, turn.facet) do (turn: var Turn):
|
||||||
if (turn.facet.parent.isSome and
|
if (not turn.facet.parent.isNil and
|
||||||
(not turn.facet.parent.get.isAlive)) or
|
(not turn.facet.parent.isAlive)) or
|
||||||
turn.facet.isInert:
|
turn.facet.isInert:
|
||||||
stop(turn)
|
stop(turn)
|
||||||
wrapper
|
wrapper
|
||||||
|
|
||||||
|
proc newFacet*(turn: var Turn): Facet = newFacet(turn.facet.actor, turn.facet)
|
||||||
|
|
||||||
proc inFacet*(turn: var Turn; bootProc: TurnAction): Facet =
|
proc inFacet*(turn: var Turn; bootProc: TurnAction): Facet =
|
||||||
result = newFacet(turn.facet.actor, some turn.facet)
|
result = newFacet(turn)
|
||||||
|
when tracing:
|
||||||
|
var act = ActionDescription(orKind: ActionDescriptionKind.facetstart)
|
||||||
|
act.facetstart.path.add result.path
|
||||||
|
turn.desc.actions.add act
|
||||||
inFacet(turn, result, stopIfInertAfter(bootProc))
|
inFacet(turn, result, stopIfInertAfter(bootProc))
|
||||||
|
|
||||||
proc facet*(turn: var Turn; bootProc: TurnAction): Facet {.deprecated.} = inFacet(turn, bootProc)
|
proc facet*(turn: var Turn; bootProc: TurnAction): Facet {.deprecated.} = inFacet(turn, bootProc)
|
||||||
|
|
||||||
proc newActor(name: string; bootProc: TurnAction; initialAssertions: OutboundTable): Actor =
|
proc newActor(name: string; handleAlloc: ref Handle): Actor =
|
||||||
let
|
let
|
||||||
now = getTime()
|
now = getTime()
|
||||||
seed = now.toUnix * 1_000_000_000 + now.nanosecond
|
seed = now.toUnix * 1_000_000_000 + now.nanosecond
|
||||||
result = Actor(
|
result = Actor(
|
||||||
name: name,
|
name: name,
|
||||||
id: ActorId(seed))
|
id: ActorId(seed),
|
||||||
result.root = newFacet(result, none Facet)
|
handleAllocator: handleAlloc,
|
||||||
result.future = newFuture[void]($result)
|
)
|
||||||
run(
|
result.root = newFacet(result, nil)
|
||||||
newFacet(result, some result.root, initialAssertions),
|
when tracing:
|
||||||
stopIfInertAfter(bootProc))
|
var act = ActorActivation(orKind: ActorActivationKind.start)
|
||||||
|
act.start.actorName = Name(orKind: NameKind.named)
|
||||||
|
act.start.actorName.named.name = name.toPreserves
|
||||||
|
trace(result, act)
|
||||||
|
|
||||||
|
proc run(actor; bootProc: TurnAction; initialAssertions: OutboundTable) =
|
||||||
|
run(newFacet(actor, actor.root, initialAssertions), stopIfInertAfter(bootProc))
|
||||||
|
|
||||||
proc bootActor*(name: string; bootProc: TurnAction): Actor =
|
proc bootActor*(name: string; bootProc: TurnAction): Actor =
|
||||||
var initialAssertions: OutboundTable
|
var initialAssertions: OutboundTable
|
||||||
newActor(name, bootProc, initialAssertions)
|
result = newActor(name, new(ref Handle))
|
||||||
|
when tracing:
|
||||||
|
new result.turnIdAllocator
|
||||||
|
let path = getEnv("SYNDICATE_TRACE_FILE", "/tmp/" & name & ".trace.pr")
|
||||||
|
case path
|
||||||
|
of "": stderr.writeLine "$SYNDICATE_TRACE_FILE unset, not tracing actor ", name
|
||||||
|
of "-": result.traceStream = newFileStream(stderr)
|
||||||
|
else: result.traceStream = openFileStream(path, fmWrite)
|
||||||
|
run(result, bootProc, initialAssertions)
|
||||||
|
|
||||||
proc spawn*(name: string; turn: var Turn; bootProc: TurnAction; initialAssertions = initHashSet[Handle]()) =
|
proc spawn*(name: string; turn: var Turn; bootProc: TurnAction; initialAssertions = initHashSet[Handle]()): Actor {.discardable.} =
|
||||||
|
let actor = newActor(name, turn.facet.actor.handleAllocator)
|
||||||
enqueue(turn, turn.facet) do (turn: var Turn):
|
enqueue(turn, turn.facet) do (turn: var Turn):
|
||||||
var newOutBound: Table[Handle, OutboundAssertion]
|
var newOutBound: Table[Handle, OutboundAssertion]
|
||||||
for key in initialAssertions:
|
for key in initialAssertions:
|
||||||
discard turn.facet.outbound.pop(key, newOutbound[key])
|
discard turn.facet.outbound.pop(key, newOutbound[key])
|
||||||
discard newActor(name, bootProc, newOutBound)
|
when tracing:
|
||||||
|
actor.turnIdAllocator = turn.facet.actor.turnIdAllocator
|
||||||
|
actor.traceStream = turn.facet.actor.traceStream
|
||||||
|
var act = ActionDescription(orKind: ActionDescriptionKind.spawn)
|
||||||
|
act.spawn.id = actor.id.toPreserves
|
||||||
|
turn.desc.actions.add act
|
||||||
|
run(actor, bootProc, newOutBound)
|
||||||
|
actor
|
||||||
|
|
||||||
proc newInertRef*(): Ref =
|
proc newInertCap*(): Cap =
|
||||||
let a = bootActor("inert") do (turn: var Turn): turn.stop()
|
let a = bootActor("inert") do (turn: var Turn): turn.stop()
|
||||||
Ref(relay: a.root)
|
Cap(relay: a.root)
|
||||||
|
|
||||||
proc atExit*(actor; action) = actor.exitHooks.add action
|
proc atExit*(actor; action) = actor.exitHooks.add action
|
||||||
|
|
||||||
|
@ -409,13 +484,16 @@ proc terminate(actor; turn; reason: ref Exception) =
|
||||||
if not actor.exiting:
|
if not actor.exiting:
|
||||||
actor.exiting = true
|
actor.exiting = true
|
||||||
actor.exitReason = reason
|
actor.exitReason = reason
|
||||||
|
when tracing:
|
||||||
|
var act = ActorActivation(orKind: ActorActivationKind.stop)
|
||||||
|
if not reason.isNil:
|
||||||
|
act.stop.status = ExitStatus(orKind: ExitStatusKind.Error)
|
||||||
|
act.stop.status.error.message = reason.msg
|
||||||
|
trace(actor, act)
|
||||||
for hook in actor.exitHooks: hook(turn)
|
for hook in actor.exitHooks: hook(turn)
|
||||||
proc finish(turn: var Turn) =
|
proc finish(turn: var Turn) =
|
||||||
actor.root.terminate(turn, not reason.isNil)
|
actor.root.terminate(turn, reason.isNil)
|
||||||
if reason.isNil:
|
actor.exited = true
|
||||||
actor.future.complete()
|
|
||||||
else:
|
|
||||||
actor.future.fail reason
|
|
||||||
callSoon do ():
|
callSoon do ():
|
||||||
run(actor.root, finish, true)
|
run(actor.root, finish, true)
|
||||||
|
|
||||||
|
@ -437,28 +515,36 @@ template tryFacet(facet; body: untyped) =
|
||||||
except CatchableError as err: terminate(facet, err)
|
except CatchableError as err: terminate(facet, err)
|
||||||
|
|
||||||
proc run*(facet; action: TurnAction; zombieTurn = false) =
|
proc run*(facet; action: TurnAction; zombieTurn = false) =
|
||||||
if not zombieTurn:
|
if zombieTurn or (facet.actor.exitReason.isNil and facet.isAlive):
|
||||||
if not facet.actor.exitReason.isNil: return
|
tryFacet(facet):
|
||||||
if not facet.isAlive: return
|
var queues = newTable[Facet, seq[TurnAction]]()
|
||||||
# TODO: not Nim idiom
|
block:
|
||||||
tryFacet(facet):
|
var turn = Turn(facet: facet, queues: queues)
|
||||||
var queues = newTable[Facet, seq[TurnAction]]()
|
action(turn)
|
||||||
block:
|
when tracing:
|
||||||
var turn = Turn(facet: facet, queues: queues)
|
turn.desc.id = facet.nextTurnId.toPreserves
|
||||||
action(turn)
|
facet.actor.trace ActorActivation(
|
||||||
for facet, queue in queues:
|
orKind: ActorActivationKind.turn, turn: turn.desc)
|
||||||
for action in queue: run(facet, action)
|
for facet, queue in queues:
|
||||||
|
for action in queue: run(facet, action)
|
||||||
|
|
||||||
proc run*(`ref`: Ref; action: TurnAction) =
|
proc run*(cap: Cap; action: TurnAction) =
|
||||||
## Convenience proc to run a `TurnAction` in the scope of a `Ref`.
|
## Convenience proc to run a `TurnAction` in the scope of a `Cap`.
|
||||||
run(`ref`.relay, action)
|
run(cap.relay, action)
|
||||||
|
|
||||||
proc addCallback*(fut: FutureBase; facet: Facet; act: TurnAction) =
|
proc addCallback*(fut: FutureBase; facet: Facet; act: TurnAction) =
|
||||||
## Add a callback to a `Future` that will be called at a later `Turn`
|
## Add a callback to a `Future` that will be called at a later `Turn`
|
||||||
## within the context of `facet`.
|
## within the context of `facet`.
|
||||||
addCallback(fut) do ():
|
addCallback(fut) do ():
|
||||||
if fut.failed: terminate(facet, fut.error)
|
if fut.failed: terminate(facet, fut.error)
|
||||||
else: run(facet, act)
|
else:
|
||||||
|
when tracing:
|
||||||
|
run(facet) do (turn: var Turn):
|
||||||
|
turn.desc.cause = TurnCause(orKind: TurnCauseKind.external)
|
||||||
|
turn.desc.cause.external.description = "Future".toPreserves
|
||||||
|
act(turn)
|
||||||
|
else:
|
||||||
|
run(facet, act)
|
||||||
|
|
||||||
proc addCallback*(fut: FutureBase; turn: var Turn; act: TurnAction) =
|
proc addCallback*(fut: FutureBase; turn: var Turn; act: TurnAction) =
|
||||||
## Add a callback to a `Future` that will be called at a later `Turn`
|
## Add a callback to a `Future` that will be called at a later `Turn`
|
||||||
|
@ -470,9 +556,21 @@ proc addCallback*(fut: FutureBase; turn: var Turn; act: TurnAction) =
|
||||||
else:
|
else:
|
||||||
addCallback(fut, turn.facet, act)
|
addCallback(fut, turn.facet, act)
|
||||||
|
|
||||||
|
proc addCallback*[T](fut: Future[T]; turn: var Turn; act: proc (t: var Turn, x: T) {.gcsafe.}) =
|
||||||
|
addCallback(fut, turn) do (turn: var Turn):
|
||||||
|
if fut.failed: terminate(turn.facet, fut.error)
|
||||||
|
else:
|
||||||
|
when tracing:
|
||||||
|
turn.desc.cause = TurnCause(orKind: TurnCauseKind.external)
|
||||||
|
turn.desc.cause.external.description = "Future".toPreserves
|
||||||
|
act(turn, read fut)
|
||||||
|
|
||||||
proc stop*(turn: var Turn, facet: Facet) =
|
proc stop*(turn: var Turn, facet: Facet) =
|
||||||
enqueue(turn, facet.parent.get) do (turn: var Turn):
|
if facet.parent.isNil:
|
||||||
facet.terminate(turn, true)
|
facet.terminate(turn, true)
|
||||||
|
else:
|
||||||
|
enqueue(turn, facet.parent) do (turn: var Turn):
|
||||||
|
facet.terminate(turn, true)
|
||||||
|
|
||||||
proc stop*(turn: var Turn) =
|
proc stop*(turn: var Turn) =
|
||||||
stop(turn, turn.facet)
|
stop(turn, turn.facet)
|
||||||
|
@ -483,20 +581,32 @@ proc onStop*(facet: Facet; act: TurnAction) =
|
||||||
|
|
||||||
proc stopActor*(turn: var Turn) =
|
proc stopActor*(turn: var Turn) =
|
||||||
let actor = turn.facet.actor
|
let actor = turn.facet.actor
|
||||||
enqueue(turn, turn.facet.actor.root) do (turn: var Turn):
|
enqueue(turn, actor.root) do (turn: var Turn):
|
||||||
terminate(actor, turn, nil)
|
terminate(actor, turn, nil)
|
||||||
|
|
||||||
proc freshen*(turn: var Turn, act: TurnAction) =
|
proc freshen*(turn: var Turn, act: TurnAction) =
|
||||||
assert(turn.queues.len == 0, "Attempt to freshen a non-stale Turn")
|
assert(turn.queues.len == 0, "Attempt to freshen a non-stale Turn")
|
||||||
run(turn.facet, act)
|
run(turn.facet, act)
|
||||||
|
|
||||||
proc newRef*(relay: Facet; e: Entity): Ref =
|
proc newCap*(relay: Facet; e: Entity): Cap =
|
||||||
Ref(relay: relay, target: e)
|
Cap(relay: relay, target: e)
|
||||||
|
|
||||||
proc newRef*(turn; e: Entity): Ref =
|
proc newCap*(turn; e: Entity): Cap =
|
||||||
Ref(relay: turn.facet, target: e)
|
Cap(relay: turn.facet, target: e)
|
||||||
|
|
||||||
proc sync*(turn, refer: Ref, cb: proc(t: Turn) {.gcsafe.}) =
|
proc newCap*(e: Entity; turn): Cap =
|
||||||
discard # TODO
|
Cap(relay: turn.facet, target: e)
|
||||||
|
|
||||||
proc future*(actor): Future[void] = actor.future
|
type SyncContinuation {.final.} = ref object of Entity
|
||||||
|
action: TurnAction
|
||||||
|
|
||||||
|
method message(entity: SyncContinuation; turn: var Turn; v: AssertionRef) =
|
||||||
|
entity.action(turn)
|
||||||
|
|
||||||
|
proc sync*(turn: var Turn; refer: Cap; act: TurnAction) =
|
||||||
|
sync(turn, refer, newCap(turn, SyncContinuation(action: act)))
|
||||||
|
|
||||||
|
proc running*(actor): bool =
|
||||||
|
result = not actor.exited
|
||||||
|
if not (result or actor.exitReason.isNil):
|
||||||
|
raise actor.exitReason
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
include_rules
|
||||||
|
NIM_FLAGS += --path:$(TUP_CWD)/../..
|
||||||
|
: foreach *.nim |> !nim_check |>
|
|
@ -0,0 +1,35 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import std/[asyncdispatch, monotimes, times]
|
||||||
|
import preserves
|
||||||
|
import syndicate
|
||||||
|
|
||||||
|
import ../protocols/timer
|
||||||
|
from syndicate/protocols/dataspace import Observe
|
||||||
|
|
||||||
|
export timer
|
||||||
|
|
||||||
|
type Observe = dataspace.Observe
|
||||||
|
|
||||||
|
proc now: float64 = getTime().toUnixFloat()
|
||||||
|
|
||||||
|
proc spawnTimers*(turn: var Turn; ds: Cap): Actor {.discardable.} =
|
||||||
|
## Spawn a timer actor.
|
||||||
|
spawn("timer", turn) do (turn: var Turn):
|
||||||
|
|
||||||
|
during(turn, ds, inject(grab Observe(pattern: dropType LaterThan), {0: grabLit()})) do (seconds: float):
|
||||||
|
let period = seconds - now()
|
||||||
|
if period < 0.001:
|
||||||
|
discard publish(turn, ds, LaterThan(seconds: seconds))
|
||||||
|
else:
|
||||||
|
addCallback(sleepAsync(period * 1_000), turn) do (turn: var Turn):
|
||||||
|
discard publish(turn, ds, LaterThan(seconds: seconds))
|
||||||
|
|
||||||
|
proc after*(turn: var Turn; ds: Cap; dur: Duration; act: TurnAction) =
|
||||||
|
## Execute `act` after some duration of time.
|
||||||
|
let later = now() + dur.inMilliseconds.float64 * 1_000.0
|
||||||
|
onPublish(turn, ds, grab LaterThan(seconds: later)):
|
||||||
|
act(turn)
|
||||||
|
|
||||||
|
# TODO: periodic timer
|
|
@ -37,5 +37,12 @@ proc change*[T](bag: var Bag[T]; key: T; delta: int; clamp = false): ChangeDescr
|
||||||
if result in {cdAbsentToAbsent, cdPresentToAbsent}:
|
if result in {cdAbsentToAbsent, cdPresentToAbsent}:
|
||||||
bag.del(key)
|
bag.del(key)
|
||||||
|
|
||||||
iterator items*[T](bag: Bag[T]): (int, T) =
|
iterator items*[T](bag: Bag[T]): T =
|
||||||
for k, v in bag: yield(v, k)
|
for x in bag.keys: yield x
|
||||||
|
|
||||||
|
proc `$`*(bag: Bag): string =
|
||||||
|
result.add '{'
|
||||||
|
for x in bag.keys:
|
||||||
|
if result.len > 1: result.add ' '
|
||||||
|
result.add $x
|
||||||
|
result.add '}'
|
||||||
|
|
|
@ -6,64 +6,37 @@ runnableExamples:
|
||||||
let sturdy = mint()
|
let sturdy = mint()
|
||||||
check $sturdy == """<ref {oid: "syndicate" sig: #x"69ca300c1dbfa08fba692102dd82311a"}>"""
|
check $sturdy == """<ref {oid: "syndicate" sig: #x"69ca300c1dbfa08fba692102dd82311a"}>"""
|
||||||
|
|
||||||
import std/options
|
import std/[options, tables]
|
||||||
from std/sequtils import toSeq
|
from std/sequtils import toSeq
|
||||||
import hashlib/misc/blake2
|
import hashlib/misc/blake2
|
||||||
|
|
||||||
import preserves
|
import preserves
|
||||||
import ./protocols/sturdy
|
import ./protocols/sturdy
|
||||||
from ./actors import Ref
|
|
||||||
|
|
||||||
export `$`
|
export `$`
|
||||||
|
|
||||||
proc hmac(key, data: openarray[byte]): seq[byte] =
|
proc hmac(key, data: openarray[byte]): seq[byte] =
|
||||||
count[Hmac[BLAKE2S_256]](key, data).data[0..15].toSeq
|
count[Hmac[BLAKE2S_256]](key, data).data[0..15].toSeq
|
||||||
|
|
||||||
proc mint*[T](key: openarray[byte]; oid: Preserve[T]): SturdyRef[T] =
|
proc mint*(key: openarray[byte]; oid: Value): SturdyRef =
|
||||||
SturdyRef[T](parameters: {
|
result.parameters.oid = oid
|
||||||
"oid": oid,
|
result.parameters.sig = hmac(key, oid.encode)
|
||||||
"sig": hmac(key, encode(oid)).toPreserve(T),
|
|
||||||
}.toDictionary,
|
|
||||||
)
|
|
||||||
|
|
||||||
proc mint*(): SturdyRef[Ref] =
|
proc mint*(): SturdyRef =
|
||||||
var key: array[16, byte]
|
var key: array[16, byte]
|
||||||
mint(key, toPreserve("syndicate", Ref))
|
mint(key, "syndicate".toPreserves)
|
||||||
|
|
||||||
proc attenuate*[T](r: SturdyRef[T]; caveats: seq[Caveat]): SturdyRef[T] =
|
proc attenuate*(r: SturdyRef; caveats: seq[Caveat]): SturdyRef =
|
||||||
result = SturdyRef[T](
|
if r.parameters.caveats.isSome:
|
||||||
oid: r.oid,
|
result.parameters.caveats = some(r.parameters.caveats.get & caveats.toPreserves)
|
||||||
caveatChain: r.caveatChain,
|
result.parameters.oid = r.parameters.oid
|
||||||
sig: hmac(r.sig, encode caveats))
|
result.parameters.sig = hmac(r.parameters.sig, caveats.toPreserves.encode)
|
||||||
result.caveatChain.add caveats
|
|
||||||
|
|
||||||
proc validate*[T](key: openarray[byte]; sturdy: SturdyRef[T]): bool =
|
proc validate*(key: openarray[byte]; sturdy: SturdyRef): bool =
|
||||||
let oid = step(sturdy.parameters, Symbol"oid")
|
var sig = hmac(key, sturdy.parameters.oid.encode)
|
||||||
if oid.isSome:
|
if sturdy.parameters.caveats.isSome:
|
||||||
let ctrl = step(sturdy.parameters, Symbol"sig")
|
for cav in sturdy.parameters.caveats.get:
|
||||||
if ctrl.isSome:
|
sig = hmac(sig, encode cav)
|
||||||
var sig = hmac(key, oid.get.encode)
|
result = (sig == sturdy.parameters.sig)
|
||||||
let caveats = step(sturdy.parameters, Symbol"caveats")
|
|
||||||
if caveats.isSome and caveats.get.isSequence:
|
|
||||||
for cav in caveats.get.sequence:
|
|
||||||
sig = hmac(sig, encode cav)
|
|
||||||
result = (sig == ctrl.get.bytes)
|
|
||||||
|
|
||||||
when isMainModule:
|
# mint utility moved to syndicate_utils/src/mintsturdyref.nim
|
||||||
from os import commandLineParams
|
|
||||||
|
|
||||||
var key: array[16, byte]
|
|
||||||
case readBytes(stdin, key, 0, 16)
|
|
||||||
of 16: discard
|
|
||||||
of 0: stderr.writeLine "using null key"
|
|
||||||
else: quit "expected sixteen bytes of key from stdin"
|
|
||||||
|
|
||||||
var oids: seq[Preserve[void]]
|
|
||||||
for p in commandLineParams():
|
|
||||||
add(oids, parsePreserves p)
|
|
||||||
if oids.len == 0: oids.add(toPreserve "syndicate")
|
|
||||||
|
|
||||||
for oid in oids:
|
|
||||||
let sturdy = mint(key, oid)
|
|
||||||
doAssert validate(key, sturdy)
|
|
||||||
stdout.writeLine(sturdy)
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
# SPDX-FileCopyrightText: ☭ 2022 Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ 2022 Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[hashes, tables]
|
import std/[hashes, options, tables]
|
||||||
import preserves
|
import preserves
|
||||||
import ./actors, ./protocols/dataspace, ./skeletons
|
import ./actors, ./protocols/dataspace, ./skeletons
|
||||||
|
|
||||||
from ./protocols/protocol import Handle
|
from ./protocols/protocol import Handle
|
||||||
|
|
||||||
type
|
type
|
||||||
Assertion = Preserve[Ref]
|
Assertion = Value
|
||||||
Observe = dataspace.Observe[Ref]
|
Observe = dataspace.Observe
|
||||||
Turn = actors.Turn
|
Turn = actors.Turn
|
||||||
|
|
||||||
Dataspace {.final.} = ref object of Entity
|
Dataspace {.final.} = ref object of Entity
|
||||||
|
@ -18,30 +18,33 @@ type
|
||||||
|
|
||||||
method publish(ds: Dataspace; turn: var Turn; a: AssertionRef; h: Handle) {.gcsafe.} =
|
method publish(ds: Dataspace; turn: var Turn; a: AssertionRef; h: Handle) {.gcsafe.} =
|
||||||
if add(ds.index, turn, a.value):
|
if add(ds.index, turn, a.value):
|
||||||
var obs: Observe
|
var obs = a.value.preservesTo(Observe)
|
||||||
if obs.fromPreserve a.value:
|
if obs.isSome and obs.get.observer of Cap:
|
||||||
ds.index.add(turn, obs.pattern, obs.observer)
|
ds.index.add(turn, obs.get.pattern, Cap(obs.get.observer))
|
||||||
ds.handleMap[h] = a.value
|
ds.handleMap[h] = a.value
|
||||||
|
|
||||||
method retract(ds: Dataspace; turn: var Turn; h: Handle) {.gcsafe.} =
|
method retract(ds: Dataspace; turn: var Turn; h: Handle) {.gcsafe.} =
|
||||||
try:
|
let v = ds.handleMap[h]
|
||||||
let v = ds.handleMap[h]
|
if remove(ds.index, turn, v):
|
||||||
if remove(ds.index, turn, v):
|
ds.handleMap.del h
|
||||||
ds.handleMap.del h
|
var obs = v.preservesTo(Observe)
|
||||||
var obs: Observe
|
if obs.isSome and obs.get.observer of Cap:
|
||||||
if obs.fromPreserve v:
|
ds.index.remove(turn, obs.get.pattern, Cap(obs.get.observer))
|
||||||
ds.index.remove(turn, obs.pattern, obs.observer)
|
|
||||||
except KeyError: discard
|
|
||||||
|
|
||||||
method message(ds: Dataspace; turn: var Turn; a: AssertionRef) {.gcsafe.} =
|
method message(ds: Dataspace; turn: var Turn; a: AssertionRef) {.gcsafe.} =
|
||||||
ds.index.deliverMessage(turn, a.value)
|
ds.index.deliverMessage(turn, a.value)
|
||||||
|
|
||||||
proc newDataspace*(turn: var Turn): Ref =
|
proc newDataspace*(turn: var Turn): Cap =
|
||||||
newRef(turn, Dataspace(index: initIndex()))
|
newCap(turn, Dataspace(index: initIndex()))
|
||||||
|
|
||||||
type BootProc = proc (ds: Ref; turn: var Turn) {.gcsafe.}
|
type BootProc = proc (turn: var Turn; ds: Cap) {.gcsafe.}
|
||||||
|
type DeprecatedBootProc = proc (ds: Cap; turn: var Turn) {.gcsafe.}
|
||||||
|
|
||||||
proc bootDataspace*(name: string; bootProc: BootProc): Actor {.discardable.} =
|
proc bootDataspace*(name: string; bootProc: BootProc): Actor =
|
||||||
bootActor(name) do (turn: var Turn):
|
bootActor(name) do (turn: var Turn):
|
||||||
discard turn.facet.preventInertCheck()
|
discard turn.facet.preventInertCheck()
|
||||||
bootProc(newDataspace(turn), turn)
|
bootProc(turn, newDataspace(turn))
|
||||||
|
|
||||||
|
proc bootDataspace*(name: string; bootProc: DeprecatedBootProc): Actor {.deprecated.} =
|
||||||
|
bootDataspace(name) do (turn: var Turn, ds: Cap):
|
||||||
|
bootProc(ds, turn)
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
# SPDX-FileCopyrightText: 2021 ☭ Emery Hemingway
|
|
||||||
# SPDX-License-Identifier: Unlicense
|
|
||||||
|
|
||||||
import std/[asyncdispatch, monotimes, times]
|
|
||||||
import preserves, preserves/records
|
|
||||||
import syndicate, syndicate/assertions
|
|
||||||
import ../../syndicate/protocols/schemas/timer
|
|
||||||
|
|
||||||
syndicate timerDriver:
|
|
||||||
|
|
||||||
spawn "timer":
|
|
||||||
during(observe(laterThan(?msecs))) do (msecs: float64):
|
|
||||||
let
|
|
||||||
now = getTime().toUnixFloat() * 1_000.0
|
|
||||||
period = msecs - now
|
|
||||||
if period > 0:
|
|
||||||
getCurrentFacet().beginExternalTask()
|
|
||||||
addTimer(period.int, oneshot = true) do (fd: AsyncFD) -> bool:
|
|
||||||
react: publish: laterThan(deadline)
|
|
||||||
getCurrentFacet().endExternalTask()
|
|
||||||
true
|
|
||||||
else:
|
|
||||||
react: publish: prsTimeLaterThan(deadline)
|
|
|
@ -5,14 +5,8 @@ import std/[hashes, tables]
|
||||||
import preserves
|
import preserves
|
||||||
import ./actors, ./patterns, ./protocols/dataspace
|
import ./actors, ./patterns, ./protocols/dataspace
|
||||||
|
|
||||||
from ./protocols/protocol import Handle
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Observe = dataspace.Observe[Ref]
|
DuringProc* = proc (turn: var Turn; a: Value; h: Handle): TurnAction {.gcsafe.}
|
||||||
Turn = actors.Turn
|
|
||||||
|
|
||||||
type
|
|
||||||
DuringProc* = proc (turn: var Turn; a: Assertion; h: Handle): TurnAction {.gcsafe.}
|
|
||||||
DuringActionKind = enum null, dead, act
|
DuringActionKind = enum null, dead, act
|
||||||
DuringAction = object
|
DuringAction = object
|
||||||
case kind: DuringActionKind
|
case kind: DuringActionKind
|
||||||
|
@ -50,5 +44,5 @@ method retract(de: DuringEntity; turn: var Turn; h: Handle) =
|
||||||
|
|
||||||
proc during*(cb: DuringProc): DuringEntity = DuringEntity(cb: cb)
|
proc during*(cb: DuringProc): DuringEntity = DuringEntity(cb: cb)
|
||||||
|
|
||||||
proc observe*(turn: var Turn; ds: Ref; pat: Pattern; e: Entity): Handle =
|
proc observe*(turn: var Turn; ds: Cap; pat: Pattern; e: Entity): Handle =
|
||||||
publish(turn, ds, Observe(pattern: pat, observer: newRef(turn, e)))
|
publish(turn, ds, Observe(pattern: pat, observer: newCap(turn, e)))
|
||||||
|
|
|
@ -3,43 +3,43 @@
|
||||||
|
|
||||||
import std/[hashes, tables]
|
import std/[hashes, tables]
|
||||||
|
|
||||||
from ./actors import Ref, hash
|
from ./actors import Cap, hash
|
||||||
from ./protocols/sturdy import Oid
|
from ./protocols/sturdy import Oid
|
||||||
|
|
||||||
proc hash(r: Ref): Hash = !$(r.relay.hash !& r.target.unsafeAddr.hash)
|
proc hash(r: Cap): Hash = !$(r.relay.hash !& r.target.unsafeAddr.hash)
|
||||||
|
|
||||||
type
|
type
|
||||||
Membrane* = object
|
Membrane* = object
|
||||||
## Bidirectional mapping between `Oid` and `Ref` values.
|
## Bidirectional mapping between `Oid` and `Cap` values.
|
||||||
## https://synit.org/book/protocol.html#membranes
|
## https://synit.org/book/protocol.html#membranes
|
||||||
byOid: Table[Oid, WireSymbol]
|
byOid: Table[Oid, WireSymbol]
|
||||||
byRef: Table[Ref, WireSymbol]
|
byCap: Table[Cap, WireSymbol]
|
||||||
|
|
||||||
WireSymbol* = ref object
|
WireSymbol* = ref object
|
||||||
oid: Oid
|
oid: Oid
|
||||||
`ref`: Ref
|
cap: Cap
|
||||||
count: int
|
count: int
|
||||||
|
|
||||||
proc oid*(sym: WireSymbol): Oid = sym.oid
|
proc oid*(sym: WireSymbol): Oid = sym.oid
|
||||||
proc `ref`*(sym: WireSymbol): Ref = sym.ref
|
proc cap*(sym: WireSymbol): Cap = sym.cap
|
||||||
|
|
||||||
proc grab*(mem: Membrane; key: Oid): WireSymbol =
|
proc grab*(mem: Membrane; key: Oid): WireSymbol =
|
||||||
## Grab a `WireSymbol` from a `Membrane`.
|
## Grab a `WireSymbol` from a `Membrane`.
|
||||||
mem.byOid.getOrDefault(key)
|
mem.byOid.getOrDefault(key)
|
||||||
|
|
||||||
proc grab*(mem: Membrane; key: Ref): WireSymbol =
|
proc grab*(mem: Membrane; key: Cap): WireSymbol =
|
||||||
## Grab a `WireSymbol` from a `Membrane`.
|
## Grab a `WireSymbol` from a `Membrane`.
|
||||||
mem.byRef.getOrDefault(key)
|
mem.byCap.getOrDefault(key)
|
||||||
|
|
||||||
proc drop*(mem: var Membrane; sym: WireSymbol) =
|
proc drop*(mem: var Membrane; sym: WireSymbol) =
|
||||||
## Drop a `WireSymbol` from a `Membrane`.
|
## Drop a `WireSymbol` from a `Membrane`.
|
||||||
dec sym.count
|
dec sym.count
|
||||||
if sym.count < 1:
|
if sym.count < 1:
|
||||||
mem.byOid.del sym.oid
|
mem.byOid.del sym.oid
|
||||||
mem.byRef.del sym.`ref`
|
mem.byCap.del sym.cap
|
||||||
|
|
||||||
proc newWireSymbol*(mem: var Membrane; o: Oid; r: Ref): WireSymbol =
|
proc newWireSymbol*(mem: var Membrane; o: Oid; r: Cap): WireSymbol =
|
||||||
## Allocate a `WireSymbol` at a `Membrane`.
|
## Allocate a `WireSymbol` at a `Membrane`.
|
||||||
result = WireSymbol(oid: o, `ref`: r, count: 1)
|
result = WireSymbol(oid: o, cap: r, count: 1)
|
||||||
mem.byOid[result.oid] = result
|
mem.byOid[result.oid] = result
|
||||||
mem.byRef[result.`ref`] = result
|
mem.byCap[result.cap] = result
|
||||||
|
|
|
@ -1,23 +1,34 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[options, sequtils, tables, typetraits]
|
import std/[algorithm, options, sequtils, tables, typetraits]
|
||||||
|
|
||||||
import preserves
|
import preserves
|
||||||
import ./protocols/dataspacePatterns
|
import ./protocols/dataspacePatterns
|
||||||
from ./actors import Ref
|
from ./actors import Cap
|
||||||
|
|
||||||
export dataspacePatterns.`$`, PatternKind, DCompoundKind, AnyAtomKind
|
export dataspacePatterns.`$`, PatternKind, DCompoundKind, AnyAtomKind
|
||||||
|
|
||||||
type
|
type
|
||||||
AnyAtom = dataspacePatterns.AnyAtom[Ref]
|
AnyAtom = dataspacePatterns.AnyAtom
|
||||||
DBind = dataspacePatterns.DBind[Ref]
|
DBind = dataspacePatterns.DBind
|
||||||
DCompound = dataspacePatterns.DCompound[Ref]
|
DCompound = dataspacePatterns.DCompound
|
||||||
DCompoundArr = dataspacePatterns.DCompoundArr[Ref]
|
DCompoundArr = dataspacePatterns.DCompoundArr
|
||||||
DCompoundDict = dataspacePatterns.DCompoundDict[Ref]
|
DCompoundDict = dataspacePatterns.DCompoundDict
|
||||||
DCompoundRec = dataspacePatterns.DCompoundRec[Ref]
|
DCompoundRec = dataspacePatterns.DCompoundRec
|
||||||
DLit = dataspacePatterns.DLit[Ref]
|
DLit = dataspacePatterns.DLit
|
||||||
Pattern* = dataspacePatterns.Pattern[Ref]
|
Pattern* = dataspacePatterns.Pattern
|
||||||
|
|
||||||
|
iterator orderedEntries*(dict: DCompoundDict): (Value, Pattern) =
|
||||||
|
## Iterate a `DCompoundDict` in Preserves order.
|
||||||
|
## Values captured from a dictionary are represented as an
|
||||||
|
## array of values ordered by their former key, so using an
|
||||||
|
## ordered iterator is sometimes essential.
|
||||||
|
var keys = dict.entries.keys.toSeq
|
||||||
|
sort(keys, preserves.cmp)
|
||||||
|
for k in keys:
|
||||||
|
yield(k, dict.entries.getOrDefault(k))
|
||||||
|
# getOrDefault doesn't raise and we know the keys will match
|
||||||
|
|
||||||
proc toPattern(d: sink DBind): Pattern =
|
proc toPattern(d: sink DBind): Pattern =
|
||||||
Pattern(orKind: PatternKind.DBind, dbind: d)
|
Pattern(orKind: PatternKind.DBind, dbind: d)
|
||||||
|
@ -46,7 +57,7 @@ proc drop*(): Pattern {.inline.} = Pattern(orKind: PatternKind.DDiscard)
|
||||||
proc grab*(): Pattern {.inline.} = DBind(pattern: drop()).toPattern
|
proc grab*(): Pattern {.inline.} = DBind(pattern: drop()).toPattern
|
||||||
## Create a pattern to capture any value.
|
## Create a pattern to capture any value.
|
||||||
|
|
||||||
proc grab*[T](pr: Preserve[T]): Pattern =
|
proc grab*(pr: Value): Pattern =
|
||||||
## Convert a `Preserve` value to a `Pattern`.
|
## Convert a `Preserve` value to a `Pattern`.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
from std/unittest import check
|
from std/unittest import check
|
||||||
|
@ -55,7 +66,6 @@ proc grab*[T](pr: Preserve[T]): Pattern =
|
||||||
$(grab parsePreserves"""<foo "bar" #"00" [0 1 2.0] {maybe: #t} <_>>""") ==
|
$(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>}> <_>]>"""
|
"""<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
|
case pr.kind
|
||||||
of pkBoolean:
|
of pkBoolean:
|
||||||
AnyAtom(orKind: AnyAtomKind.`bool`, bool: pr.bool).toPattern
|
AnyAtom(orKind: AnyAtomKind.`bool`, bool: pr.bool).toPattern
|
||||||
|
@ -63,8 +73,8 @@ proc grab*[T](pr: Preserve[T]): Pattern =
|
||||||
AnyAtom(orKind: AnyAtomKind.`float`, float: pr.float).toPattern
|
AnyAtom(orKind: AnyAtomKind.`float`, float: pr.float).toPattern
|
||||||
of pkDouble:
|
of pkDouble:
|
||||||
AnyAtom(orKind: AnyAtomKind.`double`, double: pr.double).toPattern
|
AnyAtom(orKind: AnyAtomKind.`double`, double: pr.double).toPattern
|
||||||
of pkSignedInteger:
|
of pkRegister:
|
||||||
AnyAtom(orKind: AnyAtomKind.`int`, int: pr.int).toPattern
|
AnyAtom(orKind: AnyAtomKind.`int`, int: pr.register).toPattern
|
||||||
of pkString:
|
of pkString:
|
||||||
AnyAtom(orKind: AnyAtomKind.`string`, string: pr.string).toPattern
|
AnyAtom(orKind: AnyAtomKind.`string`, string: pr.string).toPattern
|
||||||
of pkByteString:
|
of pkByteString:
|
||||||
|
@ -76,21 +86,24 @@ proc grab*[T](pr: Preserve[T]): Pattern =
|
||||||
drop()
|
drop()
|
||||||
else:
|
else:
|
||||||
DCompoundRec(
|
DCompoundRec(
|
||||||
label: cast[Preserve[Ref]](pr.label), # TODO: don't cast like this
|
label: pr.label,
|
||||||
fields: map[Preserve[T], Pattern](pr.fields, grab)).toPattern
|
fields: map[Value, Pattern](pr.fields, grab)).toPattern
|
||||||
of pkSequence:
|
of pkSequence:
|
||||||
DCompoundArr(items: map(pr.sequence, grab)).toPattern
|
DCompoundArr(items: map(pr.sequence, grab)).toPattern
|
||||||
of pkSet: raise newException(
|
of pkSet:
|
||||||
ValueError, "cannot construct a pattern over a set literal")
|
raiseAssert "cannot construct a pattern over a set literal"
|
||||||
of pkDictionary:
|
of pkDictionary:
|
||||||
var dict = DCompoundDict()
|
var dict = DCompoundDict()
|
||||||
for key, val in pr.pairs: dict.entries[cast[Preserve[Ref]](key)] = grab val
|
for key, val in pr.pairs: dict.entries[key] = grab val
|
||||||
dict.toPattern
|
dict.toPattern
|
||||||
of pkEmbedded:
|
of pkEmbedded:
|
||||||
# TODO: can patterns be constructed over embedded literals?
|
if pr.embeddedRef.isNil: drop()
|
||||||
drop()
|
else:
|
||||||
|
AnyAtom(orKind: AnyAtomKind.`embedded`, embedded: pr.embeddedRef).toPattern
|
||||||
|
else:
|
||||||
|
raise newException(ValueError, "cannot generate a pattern for unhandled Value type")
|
||||||
|
|
||||||
proc grab*[T](val: T): Pattern =
|
proc grab*[T](x: T): Pattern =
|
||||||
## Construct a `Pattern` from value of type `T`.
|
## Construct a `Pattern` from value of type `T`.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
from std/unittest import check
|
from std/unittest import check
|
||||||
|
@ -98,34 +111,7 @@ proc grab*[T](val: T): Pattern =
|
||||||
$grab(true) == "<lit #t>"
|
$grab(true) == "<lit #t>"
|
||||||
$grab(3.14) == "<lit 3.14>"
|
$grab(3.14) == "<lit 3.14>"
|
||||||
$grab([0, 1, 2, 3]) == "<arr [<lit 0> <lit 1> <lit 2> <lit 3>]>"
|
$grab([0, 1, 2, 3]) == "<arr [<lit 0> <lit 1> <lit 2> <lit 3>]>"
|
||||||
grab (toPreserve(val, Ref))
|
grab(x.toPreserves)
|
||||||
|
|
||||||
proc patternOfType(typ: static typedesc; `bind`: static bool): Pattern =
|
|
||||||
when typ is ref:
|
|
||||||
patternOfType(pointerBase(typ), `bind`)
|
|
||||||
elif typ.hasPreservesRecordPragma:
|
|
||||||
var rec = DCompoundRec(label: typ.recordLabel.tosymbol(Ref))
|
|
||||||
for _, f in fieldPairs(default typ):
|
|
||||||
add(rec.fields, patternOfType(typeof f, `bind`))
|
|
||||||
result = rec.toPattern
|
|
||||||
elif typ.hasPreservesDictionaryPragma:
|
|
||||||
var dict = DCompoundDict()
|
|
||||||
for key, val in fieldPairs(default typ):
|
|
||||||
dict.entries[toSymbol(key, Ref)] = patternOfType(typeof val, `bind`)
|
|
||||||
dict.toPattern
|
|
||||||
elif typ is tuple:
|
|
||||||
var arr = DCompoundArr()
|
|
||||||
for _, f in fieldPairs(default typ):
|
|
||||||
add(arr.items, patternOfType(typeof f, `bind`))
|
|
||||||
arr.toPattern
|
|
||||||
elif typ is array:
|
|
||||||
var arr = DCompoundArr()
|
|
||||||
arr.items.setLen(len(typ))
|
|
||||||
for e in arr.items.mitems: e = grab()
|
|
||||||
arr.toPattern
|
|
||||||
else:
|
|
||||||
if `bind`: grab()
|
|
||||||
else: drop()
|
|
||||||
|
|
||||||
proc grabType*(typ: static typedesc): Pattern =
|
proc grabType*(typ: static typedesc): Pattern =
|
||||||
## Derive a `Pattern` from type `typ`.
|
## Derive a `Pattern` from type `typ`.
|
||||||
|
@ -148,43 +134,76 @@ proc grabType*(typ: static typedesc): Pattern =
|
||||||
"<rec rect [<arr [<bind <_>> <bind <_>>]> <arr [<bind <_>> <bind <_>>]>]>"
|
"<rec rect [<arr [<bind <_>> <bind <_>>]> <arr [<bind <_>> <bind <_>>]>]>"
|
||||||
$(grabType ColoredRect) ==
|
$(grabType ColoredRect) ==
|
||||||
"<dict {color: <bind <_>> rect: <rec rect [<arr [<bind <_>> <bind <_>>]> <arr [<bind <_>> <bind <_>>]>]>}>"
|
"<dict {color: <bind <_>> rect: <rec rect [<arr [<bind <_>> <bind <_>>]> <arr [<bind <_>> <bind <_>>]>]>}>"
|
||||||
patternOfType(typ, true)
|
when typ is ref:
|
||||||
|
grabType(pointerBase(typ))
|
||||||
proc dropType*(typ: static typedesc): Pattern =
|
elif typ.hasPreservesRecordPragma:
|
||||||
## Derive a `Pattern` from type `typ` without any bindings.
|
var rec = DCompoundRec(label: typ.recordLabel.toSymbol)
|
||||||
patternOfType(typ, false)
|
for _, f in fieldPairs(default typ):
|
||||||
|
add(rec.fields, grabType(typeof f))
|
||||||
|
result = rec.toPattern
|
||||||
|
elif typ.hasPreservesDictionaryPragma:
|
||||||
|
var dict = DCompoundDict()
|
||||||
|
for key, val in fieldPairs(default typ):
|
||||||
|
dict.entries[key.toSymbol] = grabType(typeof val)
|
||||||
|
dict.toPattern
|
||||||
|
elif typ is tuple:
|
||||||
|
var arr = DCompoundArr()
|
||||||
|
for _, f in fieldPairs(default typ):
|
||||||
|
add(arr.items, grabType(typeof f))
|
||||||
|
arr.toPattern
|
||||||
|
elif typ is array:
|
||||||
|
var arr = DCompoundArr()
|
||||||
|
arr.items.setLen(len(typ))
|
||||||
|
for e in arr.items.mitems: e = grab()
|
||||||
|
arr.toPattern
|
||||||
|
else:
|
||||||
|
grab()
|
||||||
|
|
||||||
proc fieldCount(T: typedesc): int =
|
proc fieldCount(T: typedesc): int =
|
||||||
for _, _ in fieldPairs(default T):
|
for _, _ in fieldPairs(default T):
|
||||||
inc result
|
inc result
|
||||||
|
|
||||||
proc match(bindings: sink openArray[(int, Pattern)]; i: int; pat: var Pattern): bool =
|
proc dropType*(typ: static typedesc): Pattern =
|
||||||
|
## Derive a `Pattern` from type `typ` without any bindings.
|
||||||
|
when typ is ref:
|
||||||
|
dropType(pointerBase(typ))
|
||||||
|
elif typ.hasPreservesRecordPragma:
|
||||||
|
var rec = DCompoundRec(label: typ.recordLabel.toSymbol)
|
||||||
|
rec.fields.setLen(fieldCount typ)
|
||||||
|
for i, _ in rec.fields:
|
||||||
|
rec.fields[i] = drop()
|
||||||
|
result = rec.toPattern
|
||||||
|
elif typ.hasPreservesDictionaryPragma:
|
||||||
|
DCompoundDict().toPattern
|
||||||
|
elif typ is tuple:
|
||||||
|
var arr = DCompoundArr()
|
||||||
|
arr.items.setLen(len typ)
|
||||||
|
for i, _ in arr.items:
|
||||||
|
arr.items[i] = drop()
|
||||||
|
arr.toPattern
|
||||||
|
elif typ is array:
|
||||||
|
var arr = DCompoundArr()
|
||||||
|
arr.items.setLen(len(typ))
|
||||||
|
for e in arr.items.mitems: e = drop()
|
||||||
|
arr.toPattern
|
||||||
|
else:
|
||||||
|
drop()
|
||||||
|
|
||||||
|
proc lookup(bindings: openArray[(int, Pattern)]; i: int): Pattern =
|
||||||
for (j, b) in bindings:
|
for (j, b) in bindings:
|
||||||
if i == j:
|
if i == j: return b
|
||||||
pat = b
|
return drop()
|
||||||
return true
|
|
||||||
|
|
||||||
proc grab*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern =
|
proc grab*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern =
|
||||||
## Construct a `Pattern` from type `typ` that selectively captures fields.
|
## Construct a `Pattern` from type `typ` with pattern `bindings` by integer offset.
|
||||||
runnableExamples:
|
when typ is ptr | ref:
|
||||||
import preserves
|
|
||||||
from std/unittest import check
|
|
||||||
type
|
|
||||||
Point = tuple[x: int; y: int]
|
|
||||||
Rect {.preservesRecord: "rect".} = tuple[a: Point; B: Point]
|
|
||||||
ColoredRect {.preservesDictionary.} = tuple[color: string; rect: Rect]
|
|
||||||
check:
|
|
||||||
$grab(Point, { 1: grab() }) == "<arr [<_> <bind <_>>]>"
|
|
||||||
$grab(Rect, { 0: grab() }) == "<rec rect [<bind <_>> <arr [<_> <_>]>]>"
|
|
||||||
when typ is ref:
|
|
||||||
grab(pointerBase(typ), bindings)
|
grab(pointerBase(typ), bindings)
|
||||||
elif typ.hasPreservesRecordPragma:
|
elif typ.hasPreservesRecordPragma:
|
||||||
var rec = DCompoundRec(label: typ.recordLabel.tosymbol(Ref))
|
var rec = DCompoundRec(label: typ.recordLabel.toSymbol)
|
||||||
rec.fields.setLen(fieldCount typ)
|
rec.fields.setLen(fieldCount typ)
|
||||||
var i: int
|
var i: int
|
||||||
for _, f in fieldPairs(default typ):
|
for _, f in fieldPairs(default typ):
|
||||||
if not match(bindings, i, rec.fields[i]):
|
rec.fields[i] = lookup(bindings, i)
|
||||||
rec.fields[i] = dropType(typeof f)
|
|
||||||
inc i
|
inc i
|
||||||
result = rec.toPattern
|
result = rec.toPattern
|
||||||
elif typ is tuple:
|
elif typ is tuple:
|
||||||
|
@ -192,27 +211,33 @@ proc grab*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Patt
|
||||||
arr.items.setLen(fieldCount typ)
|
arr.items.setLen(fieldCount typ)
|
||||||
var i: int
|
var i: int
|
||||||
for _, f in fieldPairs(default typ):
|
for _, f in fieldPairs(default typ):
|
||||||
if not match(bindings, i, arr.items[i]):
|
arr.items[i] = lookup(bindings, i)
|
||||||
arr.items[i] = dropType(typeof f)
|
|
||||||
inc i
|
inc i
|
||||||
result = arr.toPattern
|
result = arr.toPattern
|
||||||
else:
|
else:
|
||||||
{.error: "grab with bindings not implemented for " & $typ.}
|
{.error: "grab with indexed bindings not implemented for " & $typ.}
|
||||||
|
|
||||||
|
proc grab*(typ: static typedesc; bindings: sink openArray[(Value, Pattern)]): Pattern =
|
||||||
|
## Construct a `Pattern` from type `typ` with dictionary field `bindings`.
|
||||||
|
when typ.hasPreservesDictionaryPragma:
|
||||||
|
DCompoundDict(entries: bindings.toTable).toPattern
|
||||||
|
else:
|
||||||
|
{.error: "grab with dictionary bindings not implemented for " & $typ.}
|
||||||
|
|
||||||
proc grabLit*(): Pattern =
|
proc grabLit*(): Pattern =
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
from std/unittest import check
|
from std/unittest import check
|
||||||
check:
|
check:
|
||||||
$grabLit() == """<rec lit [<bind <_>>]>"""
|
$grabLit() == """<rec lit [<bind <_>>]>"""
|
||||||
grabType(dataspacePatterns.DLit[void])
|
grabType(dataspacePatterns.DLit)
|
||||||
|
|
||||||
proc grabDict*(): Pattern =
|
proc grabDict*(): Pattern =
|
||||||
grabType(dataspacePatterns.DCompoundDict[void])
|
grabType(dataspacePatterns.DCompoundDict)
|
||||||
|
|
||||||
proc unpackLiterals*[E](pr: Preserve[E]): Preserve[E] =
|
proc unpackLiterals*(pr: Value): Value =
|
||||||
result = pr
|
result = pr
|
||||||
apply(result) do (pr: var Preserve[E]):
|
apply(result) do (pr: var Value):
|
||||||
if pr.isRecord("lit", 1):
|
if pr.isRecord("lit", 1) or pr.isRecord("dict", 1) or pr.isRecord("arr", 1) or pr.isRecord("set", 1):
|
||||||
pr = pr.record[0]
|
pr = pr.record[0]
|
||||||
|
|
||||||
proc inject*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern =
|
proc inject*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern =
|
||||||
|
@ -257,27 +282,98 @@ proc inject*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern =
|
||||||
var offset = 0
|
var offset = 0
|
||||||
inject(pat, bindings, offset)
|
inject(pat, bindings, offset)
|
||||||
|
|
||||||
proc recordPattern*(label: Preserve[Ref], fields: varargs[Pattern]): Pattern =
|
proc inject*(pat: Pattern; bindings: openArray[(Value, Pattern)]): Pattern =
|
||||||
|
## Inject `bindings` into a dictionary pattern.
|
||||||
|
assert pat.orKind == PatternKind.DCompound
|
||||||
|
assert pat.dcompound.orKind == DCompoundKind.dict
|
||||||
|
result = pat
|
||||||
|
for (key, val) in bindings:
|
||||||
|
result.dcompound.dict.entries[key] = val
|
||||||
|
|
||||||
|
proc grabRecord*(label: Value, fields: varargs[Pattern]): Pattern =
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
from std/unittest import check
|
from std/unittest import check
|
||||||
import syndicate/actors, preserves
|
import syndicate/actors, preserves
|
||||||
check:
|
check:
|
||||||
$recordPattern("Says".toSymbol(Ref), grab(), grab()) ==
|
$grabRecord("Says".toSymbol, grab(), grab()) ==
|
||||||
"""<rec Says [<bind <_>> <bind <_>>]>"""
|
"""<rec Says [<bind <_>> <bind <_>>]>"""
|
||||||
DCompoundRec(label: label, fields: fields.toSeq).toPattern
|
DCompoundRec(label: label, fields: fields.toSeq).toPattern
|
||||||
|
|
||||||
|
proc grabRecord*(label: string, fields: varargs[Pattern]): Pattern =
|
||||||
|
## Sugar for creating record patterns.
|
||||||
|
## `label` is converted to a symbol value.
|
||||||
|
grabRecord(label.toSymbol, fields)
|
||||||
|
|
||||||
|
proc grabDictionary*(bindings: sink openArray[(Value, Pattern)]): Pattern =
|
||||||
|
## Construct a pattern that grabs some dictionary pairs.
|
||||||
|
DCompoundDict(entries: bindings.toTable).toPattern
|
||||||
|
|
||||||
|
proc grabDictionary*(bindings: sink openArray[(string, Pattern)]): Pattern =
|
||||||
|
## Construct a pattern that grabs some dictionary pairs.
|
||||||
|
## Keys are converted from strings to symbols.
|
||||||
|
result = DCompoundDict().toPattern
|
||||||
|
for (key, val) in bindings.items:
|
||||||
|
result.dcompound.dict.entries[key.toSymbol] = val
|
||||||
|
|
||||||
|
proc depattern(comp: DCompound; values: var seq[Value]; index: var int): Value {.gcsafe.}
|
||||||
|
|
||||||
|
proc depattern(pat: Pattern; values: var seq[Value]; index: var int): Value =
|
||||||
|
case pat.orKind
|
||||||
|
of PatternKind.DDiscard:
|
||||||
|
discard
|
||||||
|
of PatternKind.DBind:
|
||||||
|
if index < values.len:
|
||||||
|
result = move values[index]
|
||||||
|
inc index
|
||||||
|
of PatternKind.DLit:
|
||||||
|
result = pat.dlit.value.toPreserves
|
||||||
|
of PatternKind.DCompound:
|
||||||
|
result = depattern(pat.dcompound, values, index)
|
||||||
|
|
||||||
|
proc depattern(comp: DCompound; values: var seq[Value]; index: var int): Value {.gcsafe.} =
|
||||||
|
case comp.orKind
|
||||||
|
of DCompoundKind.rec:
|
||||||
|
result = initRecord(comp.rec.label, comp.rec.fields.len)
|
||||||
|
for i, f in comp.rec.fields:
|
||||||
|
result[i] = depattern(f, values, index)
|
||||||
|
of DCompoundKind.arr:
|
||||||
|
result = initSequence(comp.arr.items.len)
|
||||||
|
for i, e in comp.arr.items:
|
||||||
|
result[i] = depattern(e, values, index)
|
||||||
|
of DCompoundKind.dict:
|
||||||
|
result = initDictionary(Cap)
|
||||||
|
for key, val in comp.dict.entries:
|
||||||
|
result[key] = depattern(val, values, index)
|
||||||
|
|
||||||
|
proc depattern*(pat: Pattern; values: sink seq[Value]): Value =
|
||||||
|
## Convert a `Pattern` to a `Value` while replacing binds with `values`.
|
||||||
|
var index: int
|
||||||
|
depattern(pat, values, index)
|
||||||
|
|
||||||
|
type Literal*[T] = object
|
||||||
|
## A wrapper type to deserialize patterns to native values.
|
||||||
|
value*: T
|
||||||
|
|
||||||
|
proc fromPreservesHook*[T](lit: var Literal[T]; pr: Value): bool =
|
||||||
|
var pat: Pattern
|
||||||
|
pat.fromPreserves(pr) and lit.value.fromPreserves(depattern(pat, @[]))
|
||||||
|
|
||||||
|
proc toPreservesHook*[T](lit: Literal[T]): Value =
|
||||||
|
lit.value.grab.toPreserves
|
||||||
|
|
||||||
type
|
type
|
||||||
Value = Preserve[Ref]
|
Path* = seq[Value]
|
||||||
Path = seq[Value]
|
Paths* = seq[Path]
|
||||||
|
Captures* = seq[Value]
|
||||||
Analysis* = tuple
|
Analysis* = tuple
|
||||||
constPaths: seq[Path]
|
constPaths: Paths
|
||||||
constValues: seq[Value]
|
constValues: seq[Value]
|
||||||
capturePaths: seq[Path]
|
capturePaths: Paths
|
||||||
|
|
||||||
func walk(result: var Analysis; path: var Path; p: Pattern)
|
func walk(result: var Analysis; path: var Path; p: Pattern)
|
||||||
|
|
||||||
func walk(result: var Analysis; path: var Path; key: int|Value; pat: Pattern) =
|
func walk(result: var Analysis; path: var Path; key: int|Value; pat: Pattern) =
|
||||||
path.add(key.toPreserve(Ref))
|
path.add(key.toPreserves)
|
||||||
walk(result, path, pat)
|
walk(result, path, pat)
|
||||||
discard path.pop
|
discard path.pop
|
||||||
|
|
||||||
|
@ -290,51 +386,47 @@ func walk(result: var Analysis; path: var Path; p: Pattern) =
|
||||||
of DCompoundKind.arr:
|
of DCompoundKind.arr:
|
||||||
for k, e in p.dcompound.arr.items: walk(result, path, k, e)
|
for k, e in p.dcompound.arr.items: walk(result, path, k, e)
|
||||||
of DCompoundKind.dict:
|
of DCompoundKind.dict:
|
||||||
for k, e in p.dcompound.dict.entries: walk(result, path, k, e)
|
for k, e in p.dcompound.dict.orderedEntries: walk(result, path, k, e)
|
||||||
of PatternKind.DBind:
|
of PatternKind.DBind:
|
||||||
result.capturePaths.add(path)
|
result.capturePaths.add(path)
|
||||||
walk(result, path, p.dbind.pattern)
|
walk(result, path, p.dbind.pattern)
|
||||||
of PatternKind.DDiscard: discard
|
of PatternKind.DDiscard: discard
|
||||||
of PatternKind.DLit:
|
of PatternKind.DLit:
|
||||||
result.constPaths.add(path)
|
result.constPaths.add(path)
|
||||||
result.constValues.add(p.dlit.value.toPreserve(Ref))
|
result.constValues.add(p.dlit.value.toPreserves)
|
||||||
|
|
||||||
func analyse*(p: Pattern): Analysis =
|
func analyse*(p: Pattern): Analysis =
|
||||||
var path: Path
|
var path: Path
|
||||||
walk(result, path, p)
|
walk(result, path, p)
|
||||||
|
|
||||||
func projectPath*(v: Value; path: Path): Option[Value] =
|
func projectPaths*(v: Value; paths: Paths): Option[Captures] =
|
||||||
result = some(v)
|
var res = newSeq[Value](paths.len)
|
||||||
for index in path:
|
|
||||||
result = preserves.step(result.get, index)
|
|
||||||
if result.isNone: break
|
|
||||||
|
|
||||||
func projectPaths*(v: Value; paths: seq[Path]): seq[Value] =
|
|
||||||
result = newSeq[Value](paths.len)
|
|
||||||
for i, path in paths:
|
for i, path in paths:
|
||||||
var vv = projectPath(v, path)
|
var vv = step(v, path)
|
||||||
if vv.isSome: result[i] = get(vv)
|
if vv.isSome: res[i] = get(vv)
|
||||||
|
else: return
|
||||||
|
some res
|
||||||
|
|
||||||
func matches*(pat: Pattern; pr: Value): bool =
|
proc matches*(pat: Pattern; pr: Value): bool =
|
||||||
let analysis = analyse(pat)
|
let analysis = analyse(pat)
|
||||||
assert analysis.constPaths.len == analysis.constValues.len
|
assert analysis.constPaths.len == analysis.constValues.len
|
||||||
for i, path in analysis.constPaths:
|
for i, path in analysis.constPaths:
|
||||||
let v = projectPath(pr, path)
|
let v = step(pr, path)
|
||||||
if v.isNone : return false
|
if v.isNone: return false
|
||||||
if analysis.constValues[i] != v.get: return false
|
if analysis.constValues[i] != v.get: return false
|
||||||
for path in analysis.capturePaths:
|
for path in analysis.capturePaths:
|
||||||
if isNone projectPath(pr, path): return false
|
if isNone step(pr, path): return false
|
||||||
true
|
true
|
||||||
|
|
||||||
func capture*(pat: Pattern; pr: Value): seq[Value] =
|
func capture*(pat: Pattern; pr: Value): seq[Value] =
|
||||||
let analysis = analyse(pat)
|
let analysis = analyse(pat)
|
||||||
assert analysis.constPaths.len == analysis.constValues.len
|
assert analysis.constPaths.len == analysis.constValues.len
|
||||||
for i, path in analysis.constPaths:
|
for i, path in analysis.constPaths:
|
||||||
let v = projectPath(pr, path)
|
let v = step(pr, path)
|
||||||
if v.isNone : return @[]
|
if v.isNone : return @[]
|
||||||
if analysis.constValues[i] != v.get: return @[]
|
if analysis.constValues[i] != v.get: return @[]
|
||||||
for path in analysis.capturePaths:
|
for path in analysis.capturePaths:
|
||||||
let v = projectPath(pr, path)
|
let v = step(pr, path)
|
||||||
if v.isNone: return @[]
|
if v.isNone: return @[]
|
||||||
result.add(get v)
|
result.add(get v)
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,12 @@ import
|
||||||
preserves, dataspacePatterns
|
preserves, dataspacePatterns
|
||||||
|
|
||||||
type
|
type
|
||||||
Observe*[Cap] {.preservesRecord: "Observe".} = ref object
|
Observe* {.preservesRecord: "Observe".} = object
|
||||||
`pattern`*: dataspacePatterns.Pattern[Cap]
|
`pattern`*: dataspacePatterns.Pattern
|
||||||
`observer`*: Cap
|
`observer`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
proc `$`*[Cap](x: Observe[Cap]): string =
|
proc `$`*(x: Observe): string =
|
||||||
`$`(toPreserve(x, Cap))
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*[Cap](x: Observe[Cap]): seq[byte] =
|
proc encode*(x: Observe): seq[byte] =
|
||||||
encode(toPreserve(x, Cap))
|
encode(toPreserves(x))
|
||||||
|
|
|
@ -5,100 +5,85 @@ import
|
||||||
type
|
type
|
||||||
AnyAtomKind* {.pure.} = enum
|
AnyAtomKind* {.pure.} = enum
|
||||||
`bool`, `float`, `double`, `int`, `string`, `bytes`, `symbol`, `embedded`
|
`bool`, `float`, `double`, `int`, `string`, `bytes`, `symbol`, `embedded`
|
||||||
AnyAtomBool* = bool
|
`AnyAtom`* {.preservesOr.} = object
|
||||||
AnyAtomFloat* = float32
|
|
||||||
AnyAtomDouble* = float64
|
|
||||||
AnyAtomInt* = BiggestInt
|
|
||||||
AnyAtomString* = string
|
|
||||||
AnyAtomBytes* = seq[byte]
|
|
||||||
AnyAtomSymbol* = Symbol
|
|
||||||
AnyAtomEmbedded*[Cap] = Cap
|
|
||||||
`AnyAtom`*[Cap] {.preservesOr.} = object
|
|
||||||
case orKind*: AnyAtomKind
|
case orKind*: AnyAtomKind
|
||||||
of AnyAtomKind.`bool`:
|
of AnyAtomKind.`bool`:
|
||||||
`bool`*: AnyAtomBool
|
`bool`*: bool
|
||||||
|
|
||||||
of AnyAtomKind.`float`:
|
of AnyAtomKind.`float`:
|
||||||
`float`*: AnyAtomFloat
|
`float`*: float32
|
||||||
|
|
||||||
of AnyAtomKind.`double`:
|
of AnyAtomKind.`double`:
|
||||||
`double`*: AnyAtomDouble
|
`double`*: float64
|
||||||
|
|
||||||
of AnyAtomKind.`int`:
|
of AnyAtomKind.`int`:
|
||||||
`int`*: AnyAtomInt
|
`int`*: BiggestInt
|
||||||
|
|
||||||
of AnyAtomKind.`string`:
|
of AnyAtomKind.`string`:
|
||||||
`string`*: AnyAtomString
|
`string`*: string
|
||||||
|
|
||||||
of AnyAtomKind.`bytes`:
|
of AnyAtomKind.`bytes`:
|
||||||
`bytes`*: AnyAtomBytes
|
`bytes`*: seq[byte]
|
||||||
|
|
||||||
of AnyAtomKind.`symbol`:
|
of AnyAtomKind.`symbol`:
|
||||||
`symbol`*: AnyAtomSymbol
|
`symbol`*: Symbol
|
||||||
|
|
||||||
of AnyAtomKind.`embedded`:
|
of AnyAtomKind.`embedded`:
|
||||||
`embedded`*: AnyAtomEmbedded[Cap]
|
`embedded`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
|
|
||||||
DLit*[Cap] {.preservesRecord: "lit".} = object
|
DLit* {.preservesRecord: "lit".} = object
|
||||||
`value`*: AnyAtom[Cap]
|
`value`*: AnyAtom
|
||||||
|
|
||||||
DBind*[Cap] {.preservesRecord: "bind".} = ref object
|
DBind* {.preservesRecord: "bind".} = object
|
||||||
`pattern`*: Pattern[Cap]
|
`pattern`*: Pattern
|
||||||
|
|
||||||
DDiscard* {.preservesRecord: "_".} = object
|
DDiscard* {.preservesRecord: "_".} = object
|
||||||
|
|
||||||
DCompoundKind* {.pure.} = enum
|
DCompoundKind* {.pure.} = enum
|
||||||
`rec`, `arr`, `dict`
|
`rec`, `arr`, `dict`
|
||||||
DCompoundRec*[Cap] {.preservesRecord: "rec".} = ref object
|
DCompoundRec* {.preservesRecord: "rec".} = object
|
||||||
`label`*: Preserve[Cap]
|
`label`*: Value
|
||||||
`fields`*: seq[Pattern[Cap]]
|
`fields`*: seq[Pattern]
|
||||||
|
|
||||||
DCompoundArr*[Cap] {.preservesRecord: "arr".} = ref object
|
DCompoundArr* {.preservesRecord: "arr".} = object
|
||||||
`items`*: seq[Pattern[Cap]]
|
`items`*: seq[Pattern]
|
||||||
|
|
||||||
DCompoundDict*[Cap] {.preservesRecord: "dict".} = ref object
|
DCompoundDict* {.preservesRecord: "dict".} = object
|
||||||
`entries`*: Table[Preserve[Cap], Pattern[Cap]]
|
`entries`*: Table[Value, Pattern]
|
||||||
|
|
||||||
`DCompound`*[Cap] {.preservesOr.} = ref object
|
`DCompound`* {.preservesOr.} = object
|
||||||
case orKind*: DCompoundKind
|
case orKind*: DCompoundKind
|
||||||
of DCompoundKind.`rec`:
|
of DCompoundKind.`rec`:
|
||||||
`rec`*: DCompoundRec[Cap]
|
`rec`* {.preservesEmbedded.}: DCompoundRec
|
||||||
|
|
||||||
of DCompoundKind.`arr`:
|
of DCompoundKind.`arr`:
|
||||||
`arr`*: DCompoundArr[Cap]
|
`arr`* {.preservesEmbedded.}: DCompoundArr
|
||||||
|
|
||||||
of DCompoundKind.`dict`:
|
of DCompoundKind.`dict`:
|
||||||
`dict`*: DCompoundDict[Cap]
|
`dict`* {.preservesEmbedded.}: DCompoundDict
|
||||||
|
|
||||||
|
|
||||||
PatternKind* {.pure.} = enum
|
PatternKind* {.pure.} = enum
|
||||||
`DDiscard`, `DBind`, `DLit`, `DCompound`
|
`DDiscard`, `DBind`, `DLit`, `DCompound`
|
||||||
`Pattern`*[Cap] {.preservesOr.} = ref object
|
`Pattern`* {.acyclic, preservesOr.} = ref object
|
||||||
case orKind*: PatternKind
|
case orKind*: PatternKind
|
||||||
of PatternKind.`DDiscard`:
|
of PatternKind.`DDiscard`:
|
||||||
`ddiscard`*: DDiscard
|
`ddiscard`*: DDiscard
|
||||||
|
|
||||||
of PatternKind.`DBind`:
|
of PatternKind.`DBind`:
|
||||||
`dbind`*: DBind[Cap]
|
`dbind`* {.preservesEmbedded.}: DBind
|
||||||
|
|
||||||
of PatternKind.`DLit`:
|
of PatternKind.`DLit`:
|
||||||
`dlit`*: DLit[Cap]
|
`dlit`* {.preservesEmbedded.}: DLit
|
||||||
|
|
||||||
of PatternKind.`DCompound`:
|
of PatternKind.`DCompound`:
|
||||||
`dcompound`*: DCompound[Cap]
|
`dcompound`* {.preservesEmbedded.}: DCompound
|
||||||
|
|
||||||
|
|
||||||
proc `$`*[Cap](x: AnyAtom[Cap] | DLit[Cap] | DBind[Cap] | DCompound[Cap] |
|
proc `$`*(x: AnyAtom | DLit | DBind | DDiscard | DCompound | Pattern): string =
|
||||||
Pattern[Cap]): string =
|
`$`(toPreserves(x))
|
||||||
`$`(toPreserve(x, Cap))
|
|
||||||
|
|
||||||
proc encode*[Cap](x: AnyAtom[Cap] | DLit[Cap] | DBind[Cap] | DCompound[Cap] |
|
proc encode*(x: AnyAtom | DLit | DBind | DDiscard | DCompound | Pattern): seq[
|
||||||
Pattern[Cap]): seq[byte] =
|
byte] =
|
||||||
encode(toPreserve(x, Cap))
|
encode(toPreserves(x))
|
||||||
|
|
||||||
proc `$`*(x: DDiscard): string =
|
|
||||||
`$`(toPreserve(x))
|
|
||||||
|
|
||||||
proc encode*(x: DDiscard): seq[byte] =
|
|
||||||
encode(toPreserve(x))
|
|
||||||
|
|
|
@ -3,106 +3,101 @@ import
|
||||||
preserves
|
preserves
|
||||||
|
|
||||||
type
|
type
|
||||||
Bind*[Cap] {.preservesRecord: "bind".} = object
|
Bind* {.preservesRecord: "bind".} = object
|
||||||
`description`*: Description[Cap]
|
`description`*: Description
|
||||||
`target`*: Cap
|
`target`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
`observer`*: BindObserver[Cap]
|
`observer`*: BindObserver
|
||||||
|
|
||||||
Route*[Cap] {.preservesRecord: "route".} = object
|
Route* {.preservesRecord: "route".} = object
|
||||||
`transports`*: seq[Preserve[Cap]]
|
`transports`*: seq[Value]
|
||||||
`pathSteps`* {.preservesTupleTail.}: seq[PathStep[Cap]]
|
`pathSteps`* {.preservesTupleTail.}: seq[PathStep]
|
||||||
|
|
||||||
BindObserverKind* {.pure.} = enum
|
BindObserverKind* {.pure.} = enum
|
||||||
`present`, `absent`
|
`present`, `absent`
|
||||||
BindObserverPresent*[Cap] = Cap
|
`BindObserver`* {.preservesOr.} = object
|
||||||
`BindObserver`*[Cap] {.preservesOr.} = object
|
|
||||||
case orKind*: BindObserverKind
|
case orKind*: BindObserverKind
|
||||||
of BindObserverKind.`present`:
|
of BindObserverKind.`present`:
|
||||||
`present`*: BindObserverPresent[Cap]
|
`present`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
of BindObserverKind.`absent`:
|
of BindObserverKind.`absent`:
|
||||||
`absent`* {.preservesLiteral: "#f".}: bool
|
`absent`* {.preservesLiteral: "#f".}: bool
|
||||||
|
|
||||||
|
|
||||||
TransportConnection*[Cap] {.preservesRecord: "connect-transport".} = object
|
TransportConnection* {.preservesRecord: "connect-transport".} = object
|
||||||
`addr`*: Preserve[Cap]
|
`addr`*: Value
|
||||||
`control`*: Cap
|
`control`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
`resolved`*: Resolved[Cap]
|
`resolved`*: Resolved
|
||||||
|
|
||||||
Step*[Cap] = Preserve[Cap]
|
Step* = Value
|
||||||
ResolvedPathStep*[Cap] {.preservesRecord: "path-step".} = object
|
ResolvedPathStep* {.preservesRecord: "path-step".} = object
|
||||||
`origin`*: Cap
|
`origin`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
`pathStep`*: PathStep[Cap]
|
`pathStep`*: PathStep
|
||||||
`resolved`*: Resolved[Cap]
|
`resolved`*: Resolved
|
||||||
|
|
||||||
BoundKind* {.pure.} = enum
|
BoundKind* {.pure.} = enum
|
||||||
`bound`, `Rejected`
|
`bound`, `Rejected`
|
||||||
BoundBound*[Cap] {.preservesRecord: "bound".} = object
|
BoundBound* {.preservesRecord: "bound".} = object
|
||||||
`pathStep`*: PathStep[Cap]
|
`pathStep`*: PathStep
|
||||||
|
|
||||||
`Bound`*[Cap] {.preservesOr.} = object
|
`Bound`* {.preservesOr.} = object
|
||||||
case orKind*: BoundKind
|
case orKind*: BoundKind
|
||||||
of BoundKind.`bound`:
|
of BoundKind.`bound`:
|
||||||
`bound`*: BoundBound[Cap]
|
`bound`*: BoundBound
|
||||||
|
|
||||||
of BoundKind.`Rejected`:
|
of BoundKind.`Rejected`:
|
||||||
`rejected`*: Rejected[Cap]
|
`rejected`*: Rejected
|
||||||
|
|
||||||
|
|
||||||
ForceDisconnect* {.preservesRecord: "force-disconnect".} = object
|
ForceDisconnect* {.preservesRecord: "force-disconnect".} = object
|
||||||
|
|
||||||
Description*[Cap] = Preserve[Cap]
|
Description* = Value
|
||||||
Rejected*[Cap] {.preservesRecord: "rejected".} = object
|
Rejected* {.preservesRecord: "rejected".} = object
|
||||||
`detail`*: Preserve[Cap]
|
`detail`*: Value
|
||||||
|
|
||||||
Resolve*[Cap] {.preservesRecord: "resolve".} = object
|
Resolve* {.preservesRecord: "resolve".} = object
|
||||||
`step`*: Step[Cap]
|
`step`*: Step
|
||||||
`observer`*: Cap
|
`observer`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
ResolvedKind* {.pure.} = enum
|
ResolvedKind* {.pure.} = enum
|
||||||
`accepted`, `Rejected`
|
`accepted`, `Rejected`
|
||||||
ResolvedAccepted*[Cap] {.preservesRecord: "accepted".} = object
|
ResolvedAccepted* {.preservesRecord: "accepted".} = object
|
||||||
`responderSession`*: Cap
|
`responderSession`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
`Resolved`*[Cap] {.preservesOr.} = object
|
`Resolved`* {.preservesOr.} = object
|
||||||
case orKind*: ResolvedKind
|
case orKind*: ResolvedKind
|
||||||
of ResolvedKind.`accepted`:
|
of ResolvedKind.`accepted`:
|
||||||
`accepted`*: ResolvedAccepted[Cap]
|
`accepted`* {.preservesEmbedded.}: ResolvedAccepted
|
||||||
|
|
||||||
of ResolvedKind.`Rejected`:
|
of ResolvedKind.`Rejected`:
|
||||||
`rejected`*: Rejected[Cap]
|
`rejected`*: Rejected
|
||||||
|
|
||||||
|
|
||||||
TransportControl* = ForceDisconnect
|
TransportControl* = ForceDisconnect
|
||||||
ResolvePath*[Cap] {.preservesRecord: "resolve-path".} = object
|
ResolvePath* {.preservesRecord: "resolve-path".} = object
|
||||||
`route`*: Route[Cap]
|
`route`*: Route
|
||||||
`addr`*: Preserve[Cap]
|
`addr`*: Value
|
||||||
`control`*: Cap
|
`control`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
`resolved`*: Resolved[Cap]
|
`resolved`*: Resolved
|
||||||
|
|
||||||
PathStep*[Cap] = Preserve[Cap]
|
PathStep* = Value
|
||||||
proc `$`*[Cap](x: Bind[Cap] | Route[Cap] | BindObserver[Cap] |
|
proc `$`*(x: Bind | Route | BindObserver | TransportConnection |
|
||||||
TransportConnection[Cap] |
|
ResolvedPathStep |
|
||||||
ResolvedPathStep[Cap] |
|
Bound |
|
||||||
Bound[Cap] |
|
ForceDisconnect |
|
||||||
Rejected[Cap] |
|
Rejected |
|
||||||
Resolve[Cap] |
|
Resolve |
|
||||||
Resolved[Cap] |
|
Resolved |
|
||||||
ResolvePath[Cap]): string =
|
TransportControl |
|
||||||
`$`(toPreserve(x, Cap))
|
ResolvePath): string =
|
||||||
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*[Cap](x: Bind[Cap] | Route[Cap] | BindObserver[Cap] |
|
proc encode*(x: Bind | Route | BindObserver | TransportConnection |
|
||||||
TransportConnection[Cap] |
|
ResolvedPathStep |
|
||||||
ResolvedPathStep[Cap] |
|
Bound |
|
||||||
Bound[Cap] |
|
ForceDisconnect |
|
||||||
Rejected[Cap] |
|
Rejected |
|
||||||
Resolve[Cap] |
|
Resolve |
|
||||||
Resolved[Cap] |
|
Resolved |
|
||||||
ResolvePath[Cap]): seq[byte] =
|
TransportControl |
|
||||||
encode(toPreserve(x, Cap))
|
ResolvePath): seq[byte] =
|
||||||
|
encode(toPreserves(x))
|
||||||
proc `$`*(x: ForceDisconnect | TransportControl): string =
|
|
||||||
`$`(toPreserve(x))
|
|
||||||
|
|
||||||
proc encode*(x: ForceDisconnect | TransportControl): seq[byte] =
|
|
||||||
encode(toPreserve(x))
|
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
|
||||||
|
import
|
||||||
|
preserves, std/tables
|
||||||
|
|
||||||
|
type
|
||||||
|
HostPatternKind* {.pure.} = enum
|
||||||
|
`host`, `any`
|
||||||
|
`HostPattern`* {.preservesOr.} = object
|
||||||
|
case orKind*: HostPatternKind
|
||||||
|
of HostPatternKind.`host`:
|
||||||
|
`host`*: string
|
||||||
|
|
||||||
|
of HostPatternKind.`any`:
|
||||||
|
`any`* {.preservesLiteral: "#f".}: bool
|
||||||
|
|
||||||
|
|
||||||
|
HttpListener* {.preservesRecord: "http-listener".} = object
|
||||||
|
`port`*: BiggestInt
|
||||||
|
|
||||||
|
MethodPatternKind* {.pure.} = enum
|
||||||
|
`any`, `specific`
|
||||||
|
`MethodPattern`* {.preservesOr.} = object
|
||||||
|
case orKind*: MethodPatternKind
|
||||||
|
of MethodPatternKind.`any`:
|
||||||
|
`any`* {.preservesLiteral: "#f".}: bool
|
||||||
|
|
||||||
|
of MethodPatternKind.`specific`:
|
||||||
|
`specific`*: Symbol
|
||||||
|
|
||||||
|
|
||||||
|
MimeType* = Symbol
|
||||||
|
QueryValueKind* {.pure.} = enum
|
||||||
|
`string`, `file`
|
||||||
|
QueryValueFile* {.preservesRecord: "file".} = object
|
||||||
|
`filename`*: string
|
||||||
|
`headers`*: Headers
|
||||||
|
`body`*: seq[byte]
|
||||||
|
|
||||||
|
`QueryValue`* {.preservesOr.} = object
|
||||||
|
case orKind*: QueryValueKind
|
||||||
|
of QueryValueKind.`string`:
|
||||||
|
`string`*: string
|
||||||
|
|
||||||
|
of QueryValueKind.`file`:
|
||||||
|
`file`*: QueryValueFile
|
||||||
|
|
||||||
|
|
||||||
|
HttpRequest* {.preservesRecord: "http-request".} = object
|
||||||
|
`sequenceNumber`*: BiggestInt
|
||||||
|
`host`*: string
|
||||||
|
`port`*: BiggestInt
|
||||||
|
`method`*: Symbol
|
||||||
|
`path`*: seq[string]
|
||||||
|
`headers`*: Headers
|
||||||
|
`query`*: Table[Symbol, seq[QueryValue]]
|
||||||
|
`body`*: RequestBody
|
||||||
|
|
||||||
|
RequestBodyKind* {.pure.} = enum
|
||||||
|
`present`, `absent`
|
||||||
|
`RequestBody`* {.preservesOr.} = object
|
||||||
|
case orKind*: RequestBodyKind
|
||||||
|
of RequestBodyKind.`present`:
|
||||||
|
`present`*: seq[byte]
|
||||||
|
|
||||||
|
of RequestBodyKind.`absent`:
|
||||||
|
`absent`* {.preservesLiteral: "#f".}: bool
|
||||||
|
|
||||||
|
|
||||||
|
Headers* = Table[Symbol, string]
|
||||||
|
HttpResponseKind* {.pure.} = enum
|
||||||
|
`status`, `header`, `chunk`, `done`
|
||||||
|
HttpResponseStatus* {.preservesRecord: "status".} = object
|
||||||
|
`code`*: BiggestInt
|
||||||
|
`message`*: string
|
||||||
|
|
||||||
|
HttpResponseHeader* {.preservesRecord: "header".} = object
|
||||||
|
`name`*: Symbol
|
||||||
|
`value`*: string
|
||||||
|
|
||||||
|
HttpResponseChunk* {.preservesRecord: "chunk".} = object
|
||||||
|
`chunk`*: Chunk
|
||||||
|
|
||||||
|
HttpResponseDone* {.preservesRecord: "done".} = object
|
||||||
|
`chunk`*: Chunk
|
||||||
|
|
||||||
|
`HttpResponse`* {.preservesOr.} = object
|
||||||
|
case orKind*: HttpResponseKind
|
||||||
|
of HttpResponseKind.`status`:
|
||||||
|
`status`*: HttpResponseStatus
|
||||||
|
|
||||||
|
of HttpResponseKind.`header`:
|
||||||
|
`header`*: HttpResponseHeader
|
||||||
|
|
||||||
|
of HttpResponseKind.`chunk`:
|
||||||
|
`chunk`*: HttpResponseChunk
|
||||||
|
|
||||||
|
of HttpResponseKind.`done`:
|
||||||
|
`done`*: HttpResponseDone
|
||||||
|
|
||||||
|
|
||||||
|
HttpService* {.preservesRecord: "http-service".} = object
|
||||||
|
`host`*: HostPattern
|
||||||
|
`port`*: BiggestInt
|
||||||
|
`method`*: MethodPattern
|
||||||
|
`path`*: PathPattern
|
||||||
|
|
||||||
|
HttpBinding* {.preservesRecord: "http-bind".} = object
|
||||||
|
`host`*: HostPattern
|
||||||
|
`port`*: BiggestInt
|
||||||
|
`method`*: MethodPattern
|
||||||
|
`path`*: PathPattern
|
||||||
|
`handler`* {.preservesEmbedded.}: Value
|
||||||
|
|
||||||
|
HttpContext* {.preservesRecord: "request".} = object
|
||||||
|
`req`*: HttpRequest
|
||||||
|
`res`* {.preservesEmbedded.}: Value
|
||||||
|
|
||||||
|
PathPatternElementKind* {.pure.} = enum
|
||||||
|
`label`, `wildcard`, `rest`
|
||||||
|
`PathPatternElement`* {.preservesOr.} = object
|
||||||
|
case orKind*: PathPatternElementKind
|
||||||
|
of PathPatternElementKind.`label`:
|
||||||
|
`label`*: string
|
||||||
|
|
||||||
|
of PathPatternElementKind.`wildcard`:
|
||||||
|
`wildcard`* {.preservesLiteral: "_".}: bool
|
||||||
|
|
||||||
|
of PathPatternElementKind.`rest`:
|
||||||
|
`rest`* {.preservesLiteral: "|...|".}: bool
|
||||||
|
|
||||||
|
|
||||||
|
ChunkKind* {.pure.} = enum
|
||||||
|
`string`, `bytes`
|
||||||
|
`Chunk`* {.preservesOr.} = object
|
||||||
|
case orKind*: ChunkKind
|
||||||
|
of ChunkKind.`string`:
|
||||||
|
`string`*: string
|
||||||
|
|
||||||
|
of ChunkKind.`bytes`:
|
||||||
|
`bytes`*: seq[byte]
|
||||||
|
|
||||||
|
|
||||||
|
PathPattern* = seq[PathPatternElement]
|
||||||
|
proc `$`*(x: HostPattern | HttpListener | MethodPattern | MimeType | QueryValue |
|
||||||
|
HttpRequest |
|
||||||
|
RequestBody |
|
||||||
|
Headers |
|
||||||
|
HttpResponse |
|
||||||
|
HttpService |
|
||||||
|
HttpBinding |
|
||||||
|
HttpContext |
|
||||||
|
PathPatternElement |
|
||||||
|
Chunk |
|
||||||
|
PathPattern): string =
|
||||||
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
|
proc encode*(x: HostPattern | HttpListener | MethodPattern | MimeType |
|
||||||
|
QueryValue |
|
||||||
|
HttpRequest |
|
||||||
|
RequestBody |
|
||||||
|
Headers |
|
||||||
|
HttpResponse |
|
||||||
|
HttpService |
|
||||||
|
HttpBinding |
|
||||||
|
HttpContext |
|
||||||
|
PathPatternElement |
|
||||||
|
Chunk |
|
||||||
|
PathPattern): seq[byte] =
|
||||||
|
encode(toPreserves(x))
|
|
@ -0,0 +1,121 @@
|
||||||
|
|
||||||
|
import
|
||||||
|
preserves, std/options
|
||||||
|
|
||||||
|
type
|
||||||
|
NoiseDescriptionDetail* = NoiseServiceSpec
|
||||||
|
NoisePreSharedKeysKind* {.pure.} = enum
|
||||||
|
`present`, `invalid`, `absent`
|
||||||
|
NoisePreSharedKeysPresent* {.preservesDictionary.} = object
|
||||||
|
`preSharedKeys`*: seq[seq[byte]]
|
||||||
|
|
||||||
|
NoisePreSharedKeysInvalid* {.preservesDictionary.} = object
|
||||||
|
`preSharedKeys`*: Value
|
||||||
|
|
||||||
|
NoisePreSharedKeysAbsent* {.preservesDictionary.} = object
|
||||||
|
|
||||||
|
`NoisePreSharedKeys`* {.preservesOr.} = object
|
||||||
|
case orKind*: NoisePreSharedKeysKind
|
||||||
|
of NoisePreSharedKeysKind.`present`:
|
||||||
|
`present`*: NoisePreSharedKeysPresent
|
||||||
|
|
||||||
|
of NoisePreSharedKeysKind.`invalid`:
|
||||||
|
`invalid`*: NoisePreSharedKeysInvalid
|
||||||
|
|
||||||
|
of NoisePreSharedKeysKind.`absent`:
|
||||||
|
`absent`*: NoisePreSharedKeysAbsent
|
||||||
|
|
||||||
|
|
||||||
|
SecretKeyFieldKind* {.pure.} = enum
|
||||||
|
`present`, `invalid`, `absent`
|
||||||
|
SecretKeyFieldPresent* {.preservesDictionary.} = object
|
||||||
|
`secretKey`*: seq[byte]
|
||||||
|
|
||||||
|
SecretKeyFieldInvalid* {.preservesDictionary.} = object
|
||||||
|
`secretKey`*: Value
|
||||||
|
|
||||||
|
SecretKeyFieldAbsent* {.preservesDictionary.} = object
|
||||||
|
|
||||||
|
`SecretKeyField`* {.preservesOr.} = object
|
||||||
|
case orKind*: SecretKeyFieldKind
|
||||||
|
of SecretKeyFieldKind.`present`:
|
||||||
|
`present`*: SecretKeyFieldPresent
|
||||||
|
|
||||||
|
of SecretKeyFieldKind.`invalid`:
|
||||||
|
`invalid`*: SecretKeyFieldInvalid
|
||||||
|
|
||||||
|
of SecretKeyFieldKind.`absent`:
|
||||||
|
`absent`*: SecretKeyFieldAbsent
|
||||||
|
|
||||||
|
|
||||||
|
NoiseProtocolKind* {.pure.} = enum
|
||||||
|
`present`, `invalid`, `absent`
|
||||||
|
NoiseProtocolPresent* {.preservesDictionary.} = object
|
||||||
|
`protocol`*: string
|
||||||
|
|
||||||
|
NoiseProtocolInvalid* {.preservesDictionary.} = object
|
||||||
|
`protocol`*: Value
|
||||||
|
|
||||||
|
NoiseProtocolAbsent* {.preservesDictionary.} = object
|
||||||
|
|
||||||
|
`NoiseProtocol`* {.preservesOr.} = object
|
||||||
|
case orKind*: NoiseProtocolKind
|
||||||
|
of NoiseProtocolKind.`present`:
|
||||||
|
`present`*: NoiseProtocolPresent
|
||||||
|
|
||||||
|
of NoiseProtocolKind.`invalid`:
|
||||||
|
`invalid`*: NoiseProtocolInvalid
|
||||||
|
|
||||||
|
of NoiseProtocolKind.`absent`:
|
||||||
|
`absent`*: NoiseProtocolAbsent
|
||||||
|
|
||||||
|
|
||||||
|
NoisePathStepDetail* = NoiseSpec
|
||||||
|
NoiseServiceSpecKey* = seq[byte]
|
||||||
|
NoiseServiceSpecPreSharedKeys* = Option[Value]
|
||||||
|
NoiseServiceSpecProtocol* = Option[Value]
|
||||||
|
NoiseServiceSpecSecretKey* = Option[Value]
|
||||||
|
`NoiseServiceSpec`* {.preservesDictionary.} = object
|
||||||
|
`key`*: seq[byte]
|
||||||
|
`preSharedKeys`*: Option[Value]
|
||||||
|
`protocol`*: Option[Value]
|
||||||
|
`secretKey`*: Option[Value]
|
||||||
|
`service`*: ServiceSelector
|
||||||
|
|
||||||
|
ServiceSelector* = Value
|
||||||
|
NoiseStepDetail* = ServiceSelector
|
||||||
|
NoiseSpecKey* = seq[byte]
|
||||||
|
NoiseSpecPreSharedKeys* = Option[Value]
|
||||||
|
NoiseSpecProtocol* = Option[Value]
|
||||||
|
`NoiseSpec`* {.preservesDictionary.} = object
|
||||||
|
`key`*: seq[byte]
|
||||||
|
`preSharedKeys`*: Option[Value]
|
||||||
|
`protocol`*: Option[Value]
|
||||||
|
`service`*: ServiceSelector
|
||||||
|
|
||||||
|
PacketKind* {.pure.} = enum
|
||||||
|
`complete`, `fragmented`
|
||||||
|
`Packet`* {.preservesOr.} = object
|
||||||
|
case orKind*: PacketKind
|
||||||
|
of PacketKind.`complete`:
|
||||||
|
`complete`*: seq[byte]
|
||||||
|
|
||||||
|
of PacketKind.`fragmented`:
|
||||||
|
`fragmented`*: seq[seq[byte]]
|
||||||
|
|
||||||
|
|
||||||
|
proc `$`*(x: NoiseDescriptionDetail | NoisePreSharedKeys | SecretKeyField |
|
||||||
|
NoiseProtocol |
|
||||||
|
NoisePathStepDetail |
|
||||||
|
NoiseServiceSpec |
|
||||||
|
NoiseSpec |
|
||||||
|
Packet): string =
|
||||||
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
|
proc encode*(x: NoiseDescriptionDetail | NoisePreSharedKeys | SecretKeyField |
|
||||||
|
NoiseProtocol |
|
||||||
|
NoisePathStepDetail |
|
||||||
|
NoiseServiceSpec |
|
||||||
|
NoiseSpec |
|
||||||
|
Packet): seq[byte] =
|
||||||
|
encode(toPreserves(x))
|
|
@ -5,7 +5,7 @@ import
|
||||||
type
|
type
|
||||||
Error* {.preservesRecord: "error".} = object
|
Error* {.preservesRecord: "error".} = object
|
||||||
`message`*: string
|
`message`*: string
|
||||||
`detail`*: Preserve[void]
|
`detail`*: Value
|
||||||
|
|
||||||
Turn* = seq[TurnEvent]
|
Turn* = seq[TurnEvent]
|
||||||
Message* {.preservesRecord: "message".} = object
|
Message* {.preservesRecord: "message".} = object
|
||||||
|
@ -18,23 +18,23 @@ type
|
||||||
`assertion`*: Assertion
|
`assertion`*: Assertion
|
||||||
`handle`*: Handle
|
`handle`*: Handle
|
||||||
|
|
||||||
Extension* = Preserve[void]
|
Extension* = Value
|
||||||
Sync* {.preservesRecord: "sync".} = object
|
Sync* {.preservesRecord: "sync".} = object
|
||||||
`peer`* {.preservesLiteral: "#!<lit #t>".}: tuple[]
|
`peer`* {.preservesEmbedded.}: Value
|
||||||
|
|
||||||
TurnEvent* {.preservesTuple.} = object
|
TurnEvent* {.preservesTuple.} = object
|
||||||
`oid`*: Oid
|
`oid`*: Oid
|
||||||
`event`*: Event
|
`event`*: Event
|
||||||
|
|
||||||
Oid* = BiggestInt
|
Oid* = BiggestInt
|
||||||
Assertion* = Preserve[void]
|
Assertion* = Value
|
||||||
Handle* = BiggestInt
|
Handle* = BiggestInt
|
||||||
PacketKind* {.pure.} = enum
|
PacketKind* {.pure.} = enum
|
||||||
`Turn`, `Error`, `Extension`
|
`Turn`, `Error`, `Extension`
|
||||||
`Packet`* {.preservesOr.} = object
|
`Packet`* {.preservesOr.} = object
|
||||||
case orKind*: PacketKind
|
case orKind*: PacketKind
|
||||||
of PacketKind.`Turn`:
|
of PacketKind.`Turn`:
|
||||||
`turn`*: Turn
|
`turn`* {.preservesEmbedded.}: Turn
|
||||||
|
|
||||||
of PacketKind.`Error`:
|
of PacketKind.`Error`:
|
||||||
`error`*: Error
|
`error`*: Error
|
||||||
|
@ -57,18 +57,18 @@ type
|
||||||
`message`*: Message
|
`message`*: Message
|
||||||
|
|
||||||
of EventKind.`Sync`:
|
of EventKind.`Sync`:
|
||||||
`sync`*: Sync
|
`sync`* {.preservesEmbedded.}: Sync
|
||||||
|
|
||||||
|
|
||||||
proc `$`*(x: Error | Turn | Message | Retract | Assert | Sync | TurnEvent | Oid |
|
proc `$`*(x: Error | Turn | Message | Retract | Assert | Sync | TurnEvent | Oid |
|
||||||
Handle |
|
Handle |
|
||||||
Packet |
|
Packet |
|
||||||
Event): string =
|
Event): string =
|
||||||
`$`(toPreserve(x))
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*(x: Error | Turn | Message | Retract | Assert | Sync | TurnEvent |
|
proc encode*(x: Error | Turn | Message | Retract | Assert | Sync | TurnEvent |
|
||||||
Oid |
|
Oid |
|
||||||
Handle |
|
Handle |
|
||||||
Packet |
|
Packet |
|
||||||
Event): seq[byte] =
|
Event): seq[byte] =
|
||||||
encode(toPreserve(x))
|
encode(toPreserves(x))
|
||||||
|
|
|
@ -5,8 +5,7 @@ import
|
||||||
type
|
type
|
||||||
StateKind* {.pure.} = enum
|
StateKind* {.pure.} = enum
|
||||||
`started`, `ready`, `failed`, `complete`, `userDefined`
|
`started`, `ready`, `failed`, `complete`, `userDefined`
|
||||||
StateUserDefined*[Cap] = Preserve[Cap]
|
`State`* {.preservesOr.} = object
|
||||||
`State`*[Cap] {.preservesOr.} = object
|
|
||||||
case orKind*: StateKind
|
case orKind*: StateKind
|
||||||
of StateKind.`started`:
|
of StateKind.`started`:
|
||||||
`started`* {.preservesLiteral: "started".}: bool
|
`started`* {.preservesLiteral: "started".}: bool
|
||||||
|
@ -21,40 +20,38 @@ type
|
||||||
`complete`* {.preservesLiteral: "complete".}: bool
|
`complete`* {.preservesLiteral: "complete".}: bool
|
||||||
|
|
||||||
of StateKind.`userDefined`:
|
of StateKind.`userDefined`:
|
||||||
`userdefined`*: StateUserDefined[Cap]
|
`userdefined`*: Value
|
||||||
|
|
||||||
|
|
||||||
ServiceObject*[Cap] {.preservesRecord: "service-object".} = object
|
ServiceObject* {.preservesRecord: "service-object".} = object
|
||||||
`serviceName`*: Preserve[Cap]
|
`serviceName`*: Value
|
||||||
`object`*: Preserve[Cap]
|
`object`*: Value
|
||||||
|
|
||||||
RequireService*[Cap] {.preservesRecord: "require-service".} = object
|
RequireService* {.preservesRecord: "require-service".} = object
|
||||||
`serviceName`*: Preserve[Cap]
|
`serviceName`*: Value
|
||||||
|
|
||||||
RestartService*[Cap] {.preservesRecord: "restart-service".} = object
|
RestartService* {.preservesRecord: "restart-service".} = object
|
||||||
`serviceName`*: Preserve[Cap]
|
`serviceName`*: Value
|
||||||
|
|
||||||
RunService*[Cap] {.preservesRecord: "run-service".} = object
|
RunService* {.preservesRecord: "run-service".} = object
|
||||||
`serviceName`*: Preserve[Cap]
|
`serviceName`*: Value
|
||||||
|
|
||||||
ServiceState*[Cap] {.preservesRecord: "service-state".} = object
|
ServiceState* {.preservesRecord: "service-state".} = object
|
||||||
`serviceName`*: Preserve[Cap]
|
`serviceName`*: Value
|
||||||
`state`*: State[Cap]
|
`state`*: State
|
||||||
|
|
||||||
ServiceDependency*[Cap] {.preservesRecord: "depends-on".} = object
|
ServiceDependency* {.preservesRecord: "depends-on".} = object
|
||||||
`depender`*: Preserve[Cap]
|
`depender`*: Value
|
||||||
`dependee`*: ServiceState[Cap]
|
`dependee`*: ServiceState
|
||||||
|
|
||||||
proc `$`*[Cap](x: State[Cap] | ServiceObject[Cap] | RequireService[Cap] |
|
proc `$`*(x: State | ServiceObject | RequireService | RestartService |
|
||||||
RestartService[Cap] |
|
RunService |
|
||||||
RunService[Cap] |
|
ServiceState |
|
||||||
ServiceState[Cap] |
|
ServiceDependency): string =
|
||||||
ServiceDependency[Cap]): string =
|
`$`(toPreserves(x))
|
||||||
`$`(toPreserve(x, Cap))
|
|
||||||
|
|
||||||
proc encode*[Cap](x: State[Cap] | ServiceObject[Cap] | RequireService[Cap] |
|
proc encode*(x: State | ServiceObject | RequireService | RestartService |
|
||||||
RestartService[Cap] |
|
RunService |
|
||||||
RunService[Cap] |
|
ServiceState |
|
||||||
ServiceState[Cap] |
|
ServiceDependency): seq[byte] =
|
||||||
ServiceDependency[Cap]): seq[byte] =
|
encode(toPreserves(x))
|
||||||
encode(toPreserve(x, Cap))
|
|
||||||
|
|
|
@ -5,11 +5,10 @@ import
|
||||||
type
|
type
|
||||||
CreditAmountKind* {.pure.} = enum
|
CreditAmountKind* {.pure.} = enum
|
||||||
`count`, `unbounded`
|
`count`, `unbounded`
|
||||||
CreditAmountCount* = BiggestInt
|
|
||||||
`CreditAmount`* {.preservesOr.} = object
|
`CreditAmount`* {.preservesOr.} = object
|
||||||
case orKind*: CreditAmountKind
|
case orKind*: CreditAmountKind
|
||||||
of CreditAmountKind.`count`:
|
of CreditAmountKind.`count`:
|
||||||
`count`*: CreditAmountCount
|
`count`*: BiggestInt
|
||||||
|
|
||||||
of CreditAmountKind.`unbounded`:
|
of CreditAmountKind.`unbounded`:
|
||||||
`unbounded`* {.preservesLiteral: "unbounded".}: bool
|
`unbounded`* {.preservesLiteral: "unbounded".}: bool
|
||||||
|
@ -18,76 +17,76 @@ type
|
||||||
StreamError* {.preservesRecord: "error".} = object
|
StreamError* {.preservesRecord: "error".} = object
|
||||||
`message`*: string
|
`message`*: string
|
||||||
|
|
||||||
StreamListenerError*[Cap] {.preservesRecord: "stream-listener-error".} = object
|
StreamListenerError* {.preservesRecord: "stream-listener-error".} = object
|
||||||
`spec`*: Preserve[Cap]
|
`spec`*: Value
|
||||||
`message`*: string
|
`message`*: string
|
||||||
|
|
||||||
StreamConnection*[Cap] {.preservesRecord: "stream-connection".} = object
|
StreamConnection* {.preservesRecord: "stream-connection".} = object
|
||||||
`source`*: Cap
|
`source`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
`sink`*: Cap
|
`sink`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
`spec`*: Preserve[Cap]
|
`spec`*: Value
|
||||||
|
|
||||||
`LineMode`* {.preservesOr, pure.} = enum
|
`LineMode`* {.preservesOr, pure.} = enum
|
||||||
`lf`, `crlf`
|
`lf`, `crlf`
|
||||||
SourceKind* {.pure.} = enum
|
SourceKind* {.pure.} = enum
|
||||||
`sink`, `StreamError`, `credit`
|
`sink`, `StreamError`, `credit`
|
||||||
SourceSink*[Cap] {.preservesRecord: "sink".} = object
|
SourceSink* {.preservesRecord: "sink".} = object
|
||||||
`controller`*: Cap
|
`controller`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
SourceCredit*[Cap] {.preservesRecord: "credit".} = object
|
SourceCredit* {.preservesRecord: "credit".} = object
|
||||||
`amount`*: CreditAmount
|
`amount`*: CreditAmount
|
||||||
`mode`*: Mode[Cap]
|
`mode`*: Mode
|
||||||
|
|
||||||
`Source`*[Cap] {.preservesOr.} = object
|
`Source`* {.acyclic, preservesOr.} = ref object
|
||||||
case orKind*: SourceKind
|
case orKind*: SourceKind
|
||||||
of SourceKind.`sink`:
|
of SourceKind.`sink`:
|
||||||
`sink`*: SourceSink[Cap]
|
`sink`* {.preservesEmbedded.}: SourceSink
|
||||||
|
|
||||||
of SourceKind.`StreamError`:
|
of SourceKind.`StreamError`:
|
||||||
`streamerror`*: StreamError
|
`streamerror`*: StreamError
|
||||||
|
|
||||||
of SourceKind.`credit`:
|
of SourceKind.`credit`:
|
||||||
`credit`*: SourceCredit[Cap]
|
`credit`*: SourceCredit
|
||||||
|
|
||||||
|
|
||||||
SinkKind* {.pure.} = enum
|
SinkKind* {.pure.} = enum
|
||||||
`source`, `StreamError`, `data`, `eof`
|
`source`, `StreamError`, `data`, `eof`
|
||||||
SinkSource*[Cap] {.preservesRecord: "source".} = object
|
SinkSource* {.preservesRecord: "source".} = object
|
||||||
`controller`*: Cap
|
`controller`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
SinkData*[Cap] {.preservesRecord: "data".} = object
|
SinkData* {.preservesRecord: "data".} = object
|
||||||
`payload`*: Preserve[Cap]
|
`payload`*: Value
|
||||||
`mode`*: Mode[Cap]
|
`mode`*: Mode
|
||||||
|
|
||||||
SinkEof* {.preservesRecord: "eof".} = object
|
SinkEof* {.preservesRecord: "eof".} = object
|
||||||
|
|
||||||
`Sink`*[Cap] {.preservesOr.} = object
|
`Sink`* {.acyclic, preservesOr.} = ref object
|
||||||
case orKind*: SinkKind
|
case orKind*: SinkKind
|
||||||
of SinkKind.`source`:
|
of SinkKind.`source`:
|
||||||
`source`*: SinkSource[Cap]
|
`source`* {.preservesEmbedded.}: SinkSource
|
||||||
|
|
||||||
of SinkKind.`StreamError`:
|
of SinkKind.`StreamError`:
|
||||||
`streamerror`*: StreamError
|
`streamerror`*: StreamError
|
||||||
|
|
||||||
of SinkKind.`data`:
|
of SinkKind.`data`:
|
||||||
`data`*: SinkData[Cap]
|
`data`*: SinkData
|
||||||
|
|
||||||
of SinkKind.`eof`:
|
of SinkKind.`eof`:
|
||||||
`eof`*: SinkEof
|
`eof`*: SinkEof
|
||||||
|
|
||||||
|
|
||||||
StreamListenerReady*[Cap] {.preservesRecord: "stream-listener-ready".} = object
|
StreamListenerReady* {.preservesRecord: "stream-listener-ready".} = object
|
||||||
`spec`*: Preserve[Cap]
|
`spec`*: Value
|
||||||
|
|
||||||
ModeKind* {.pure.} = enum
|
ModeKind* {.pure.} = enum
|
||||||
`bytes`, `lines`, `packet`, `object`
|
`bytes`, `lines`, `packet`, `object`
|
||||||
ModePacket* {.preservesRecord: "packet".} = object
|
ModePacket* {.preservesRecord: "packet".} = object
|
||||||
`size`*: BiggestInt
|
`size`*: BiggestInt
|
||||||
|
|
||||||
ModeObject*[Cap] {.preservesRecord: "object".} = object
|
ModeObject* {.preservesRecord: "object".} = object
|
||||||
`description`*: Preserve[Cap]
|
`description`*: Value
|
||||||
|
|
||||||
`Mode`*[Cap] {.preservesOr.} = object
|
`Mode`* {.preservesOr.} = object
|
||||||
case orKind*: ModeKind
|
case orKind*: ModeKind
|
||||||
of ModeKind.`bytes`:
|
of ModeKind.`bytes`:
|
||||||
`bytes`* {.preservesLiteral: "bytes".}: bool
|
`bytes`* {.preservesLiteral: "bytes".}: bool
|
||||||
|
@ -99,24 +98,20 @@ type
|
||||||
`packet`*: ModePacket
|
`packet`*: ModePacket
|
||||||
|
|
||||||
of ModeKind.`object`:
|
of ModeKind.`object`:
|
||||||
`object`*: ModeObject[Cap]
|
`object`*: ModeObject
|
||||||
|
|
||||||
|
|
||||||
proc `$`*[Cap](x: StreamListenerError[Cap] | StreamConnection[Cap] | Source[Cap] |
|
proc `$`*(x: CreditAmount | StreamError | StreamListenerError | StreamConnection |
|
||||||
Sink[Cap] |
|
Source |
|
||||||
StreamListenerReady[Cap] |
|
Sink |
|
||||||
Mode[Cap]): string =
|
StreamListenerReady |
|
||||||
`$`(toPreserve(x, Cap))
|
Mode): string =
|
||||||
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*[Cap](x: StreamListenerError[Cap] | StreamConnection[Cap] |
|
proc encode*(x: CreditAmount | StreamError | StreamListenerError |
|
||||||
Source[Cap] |
|
StreamConnection |
|
||||||
Sink[Cap] |
|
Source |
|
||||||
StreamListenerReady[Cap] |
|
Sink |
|
||||||
Mode[Cap]): seq[byte] =
|
StreamListenerReady |
|
||||||
encode(toPreserve(x, Cap))
|
Mode): seq[byte] =
|
||||||
|
encode(toPreserves(x))
|
||||||
proc `$`*(x: CreditAmount | StreamError): string =
|
|
||||||
`$`(toPreserve(x))
|
|
||||||
|
|
||||||
proc encode*(x: CreditAmount | StreamError): seq[byte] =
|
|
||||||
encode(toPreserve(x))
|
|
||||||
|
|
|
@ -1,104 +1,111 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
preserves, std/tables
|
preserves, std/tables, std/options
|
||||||
|
|
||||||
type
|
type
|
||||||
PCompoundKind* {.pure.} = enum
|
PCompoundKind* {.pure.} = enum
|
||||||
`rec`, `arr`, `dict`
|
`rec`, `arr`, `dict`
|
||||||
PCompoundRec*[Cap] {.preservesRecord: "rec".} = ref object
|
PCompoundRec* {.preservesRecord: "rec".} = object
|
||||||
`label`*: Preserve[Cap]
|
`label`*: Value
|
||||||
`fields`*: seq[Pattern[Cap]]
|
`fields`*: seq[Pattern]
|
||||||
|
|
||||||
PCompoundArr*[Cap] {.preservesRecord: "arr".} = ref object
|
PCompoundArr* {.preservesRecord: "arr".} = object
|
||||||
`items`*: seq[Pattern[Cap]]
|
`items`*: seq[Pattern]
|
||||||
|
|
||||||
PCompoundDict*[Cap] {.preservesRecord: "dict".} = ref object
|
PCompoundDict* {.preservesRecord: "dict".} = object
|
||||||
`entries`*: Table[Preserve[Cap], Pattern[Cap]]
|
`entries`*: Table[Value, Pattern]
|
||||||
|
|
||||||
`PCompound`*[Cap] {.preservesOr.} = ref object
|
`PCompound`* {.preservesOr.} = object
|
||||||
case orKind*: PCompoundKind
|
case orKind*: PCompoundKind
|
||||||
of PCompoundKind.`rec`:
|
of PCompoundKind.`rec`:
|
||||||
`rec`*: PCompoundRec[Cap]
|
`rec`*: PCompoundRec
|
||||||
|
|
||||||
of PCompoundKind.`arr`:
|
of PCompoundKind.`arr`:
|
||||||
`arr`*: PCompoundArr[Cap]
|
`arr`*: PCompoundArr
|
||||||
|
|
||||||
of PCompoundKind.`dict`:
|
of PCompoundKind.`dict`:
|
||||||
`dict`*: PCompoundDict[Cap]
|
`dict`*: PCompoundDict
|
||||||
|
|
||||||
|
|
||||||
Reject*[Cap] {.preservesRecord: "reject".} = ref object
|
Reject* {.preservesRecord: "reject".} = object
|
||||||
`pattern`*: Pattern[Cap]
|
`pattern`*: Pattern
|
||||||
|
|
||||||
CaveatsFieldKind* {.pure.} = enum
|
CaveatsFieldKind* {.pure.} = enum
|
||||||
`present`, `invalid`, `absent`
|
`present`, `invalid`, `absent`
|
||||||
CaveatsFieldPresent*[Cap] {.preservesDictionary.} = ref object
|
CaveatsFieldPresent* {.preservesDictionary.} = object
|
||||||
`caveats`*: seq[Caveat[Cap]]
|
`caveats`*: seq[Caveat]
|
||||||
|
|
||||||
CaveatsFieldInvalid*[Cap] {.preservesDictionary.} = object
|
CaveatsFieldInvalid* {.preservesDictionary.} = object
|
||||||
`caveats`*: Preserve[Cap]
|
`caveats`*: Value
|
||||||
|
|
||||||
CaveatsFieldAbsent* {.preservesDictionary.} = object
|
CaveatsFieldAbsent* {.preservesDictionary.} = object
|
||||||
|
|
||||||
`CaveatsField`*[Cap] {.preservesOr.} = ref object
|
`CaveatsField`* {.preservesOr.} = object
|
||||||
case orKind*: CaveatsFieldKind
|
case orKind*: CaveatsFieldKind
|
||||||
of CaveatsFieldKind.`present`:
|
of CaveatsFieldKind.`present`:
|
||||||
`present`*: CaveatsFieldPresent[Cap]
|
`present`*: CaveatsFieldPresent
|
||||||
|
|
||||||
of CaveatsFieldKind.`invalid`:
|
of CaveatsFieldKind.`invalid`:
|
||||||
`invalid`*: CaveatsFieldInvalid[Cap]
|
`invalid`*: CaveatsFieldInvalid
|
||||||
|
|
||||||
of CaveatsFieldKind.`absent`:
|
of CaveatsFieldKind.`absent`:
|
||||||
`absent`*: CaveatsFieldAbsent
|
`absent`*: CaveatsFieldAbsent
|
||||||
|
|
||||||
|
|
||||||
SturdyDescriptionDetail*[Cap] {.preservesDictionary.} = object
|
SturdyDescriptionDetail* {.preservesDictionary.} = object
|
||||||
`oid`*: Preserve[Cap]
|
|
||||||
`key`*: seq[byte]
|
`key`*: seq[byte]
|
||||||
|
`oid`*: Value
|
||||||
|
|
||||||
PAnd*[Cap] {.preservesRecord: "and".} = ref object
|
PAnd* {.preservesRecord: "and".} = object
|
||||||
`patterns`*: seq[Pattern[Cap]]
|
`patterns`*: seq[Pattern]
|
||||||
|
|
||||||
SturdyStepDetail*[Cap] = Parameters[Cap]
|
SturdyStepDetail* = Parameters
|
||||||
Rewrite*[Cap] {.preservesRecord: "rewrite".} = ref object
|
Rewrite* {.preservesRecord: "rewrite".} = object
|
||||||
`pattern`*: Pattern[Cap]
|
`pattern`*: Pattern
|
||||||
`template`*: Template[Cap]
|
`template`*: Template
|
||||||
|
|
||||||
|
ParametersCaveats* = Option[Value]
|
||||||
|
ParametersOid* = Value
|
||||||
|
ParametersSig* = seq[byte]
|
||||||
|
`Parameters`* {.preservesDictionary.} = object
|
||||||
|
`caveats`*: Option[Value]
|
||||||
|
`oid`*: Value
|
||||||
|
`sig`*: seq[byte]
|
||||||
|
|
||||||
Parameters*[Cap] = Preserve[Cap]
|
|
||||||
TRef* {.preservesRecord: "ref".} = object
|
TRef* {.preservesRecord: "ref".} = object
|
||||||
`binding`*: BiggestInt
|
`binding`*: BiggestInt
|
||||||
|
|
||||||
PBind*[Cap] {.preservesRecord: "bind".} = ref object
|
PBind* {.preservesRecord: "bind".} = object
|
||||||
`pattern`*: Pattern[Cap]
|
`pattern`*: Pattern
|
||||||
|
|
||||||
Lit*[Cap] {.preservesRecord: "lit".} = object
|
Lit* {.preservesRecord: "lit".} = object
|
||||||
`value`*: Preserve[Cap]
|
`value`*: Value
|
||||||
|
|
||||||
TCompoundKind* {.pure.} = enum
|
TCompoundKind* {.pure.} = enum
|
||||||
`rec`, `arr`, `dict`
|
`rec`, `arr`, `dict`
|
||||||
TCompoundRec*[Cap] {.preservesRecord: "rec".} = ref object
|
TCompoundRec* {.preservesRecord: "rec".} = object
|
||||||
`label`*: Preserve[Cap]
|
`label`*: Value
|
||||||
`fields`*: seq[Template[Cap]]
|
`fields`*: seq[Template]
|
||||||
|
|
||||||
TCompoundArr*[Cap] {.preservesRecord: "arr".} = ref object
|
TCompoundArr* {.preservesRecord: "arr".} = object
|
||||||
`items`*: seq[Template[Cap]]
|
`items`*: seq[Template]
|
||||||
|
|
||||||
TCompoundDict*[Cap] {.preservesRecord: "dict".} = ref object
|
TCompoundDict* {.preservesRecord: "dict".} = object
|
||||||
`entries`*: Table[Preserve[Cap], Template[Cap]]
|
`entries`*: Table[Value, Template]
|
||||||
|
|
||||||
`TCompound`*[Cap] {.preservesOr.} = ref object
|
`TCompound`* {.preservesOr.} = object
|
||||||
case orKind*: TCompoundKind
|
case orKind*: TCompoundKind
|
||||||
of TCompoundKind.`rec`:
|
of TCompoundKind.`rec`:
|
||||||
`rec`*: TCompoundRec[Cap]
|
`rec`*: TCompoundRec
|
||||||
|
|
||||||
of TCompoundKind.`arr`:
|
of TCompoundKind.`arr`:
|
||||||
`arr`*: TCompoundArr[Cap]
|
`arr`*: TCompoundArr
|
||||||
|
|
||||||
of TCompoundKind.`dict`:
|
of TCompoundKind.`dict`:
|
||||||
`dict`*: TCompoundDict[Cap]
|
`dict`*: TCompoundDict
|
||||||
|
|
||||||
|
|
||||||
SturdyPathStepDetail*[Cap] = Parameters[Cap]
|
SturdyPathStepDetail* = Parameters
|
||||||
`PAtom`* {.preservesOr, pure.} = enum
|
`PAtom`* {.preservesOr, pure.} = enum
|
||||||
`Boolean`, `Float`, `Double`, `SignedInteger`, `String`, `ByteString`,
|
`Boolean`, `Float`, `Double`, `SignedInteger`, `String`, `ByteString`,
|
||||||
`Symbol`
|
`Symbol`
|
||||||
|
@ -106,44 +113,43 @@ type
|
||||||
|
|
||||||
TemplateKind* {.pure.} = enum
|
TemplateKind* {.pure.} = enum
|
||||||
`TAttenuate`, `TRef`, `Lit`, `TCompound`
|
`TAttenuate`, `TRef`, `Lit`, `TCompound`
|
||||||
`Template`*[Cap] {.preservesOr.} = ref object
|
`Template`* {.acyclic, preservesOr.} = ref object
|
||||||
case orKind*: TemplateKind
|
case orKind*: TemplateKind
|
||||||
of TemplateKind.`TAttenuate`:
|
of TemplateKind.`TAttenuate`:
|
||||||
`tattenuate`*: TAttenuate[Cap]
|
`tattenuate`*: TAttenuate
|
||||||
|
|
||||||
of TemplateKind.`TRef`:
|
of TemplateKind.`TRef`:
|
||||||
`tref`*: TRef
|
`tref`*: TRef
|
||||||
|
|
||||||
of TemplateKind.`Lit`:
|
of TemplateKind.`Lit`:
|
||||||
`lit`*: Lit[Cap]
|
`lit`*: Lit
|
||||||
|
|
||||||
of TemplateKind.`TCompound`:
|
of TemplateKind.`TCompound`:
|
||||||
`tcompound`*: TCompound[Cap]
|
`tcompound`*: TCompound
|
||||||
|
|
||||||
|
|
||||||
CaveatKind* {.pure.} = enum
|
CaveatKind* {.pure.} = enum
|
||||||
`Rewrite`, `Alts`, `Reject`, `unknown`
|
`Rewrite`, `Alts`, `Reject`, `unknown`
|
||||||
CaveatUnknown*[Cap] = Preserve[Cap]
|
`Caveat`* {.preservesOr.} = object
|
||||||
`Caveat`*[Cap] {.preservesOr.} = ref object
|
|
||||||
case orKind*: CaveatKind
|
case orKind*: CaveatKind
|
||||||
of CaveatKind.`Rewrite`:
|
of CaveatKind.`Rewrite`:
|
||||||
`rewrite`*: Rewrite[Cap]
|
`rewrite`*: Rewrite
|
||||||
|
|
||||||
of CaveatKind.`Alts`:
|
of CaveatKind.`Alts`:
|
||||||
`alts`*: Alts[Cap]
|
`alts`*: Alts
|
||||||
|
|
||||||
of CaveatKind.`Reject`:
|
of CaveatKind.`Reject`:
|
||||||
`reject`*: Reject[Cap]
|
`reject`*: Reject
|
||||||
|
|
||||||
of CaveatKind.`unknown`:
|
of CaveatKind.`unknown`:
|
||||||
`unknown`*: CaveatUnknown[Cap]
|
`unknown`*: Value
|
||||||
|
|
||||||
|
|
||||||
PNot*[Cap] {.preservesRecord: "not".} = ref object
|
PNot* {.preservesRecord: "not".} = object
|
||||||
`pattern`*: Pattern[Cap]
|
`pattern`*: Pattern
|
||||||
|
|
||||||
SturdyRef*[Cap] {.preservesRecord: "ref".} = ref object
|
SturdyRef* {.preservesRecord: "ref".} = object
|
||||||
`parameters`*: Parameters[Cap]
|
`parameters`*: Parameters
|
||||||
|
|
||||||
WireRefKind* {.pure.} = enum
|
WireRefKind* {.pure.} = enum
|
||||||
`mine`, `yours`
|
`mine`, `yours`
|
||||||
|
@ -151,32 +157,32 @@ type
|
||||||
`field0`* {.preservesLiteral: "0".}: tuple[]
|
`field0`* {.preservesLiteral: "0".}: tuple[]
|
||||||
`oid`*: Oid
|
`oid`*: Oid
|
||||||
|
|
||||||
WireRefYours*[Cap] {.preservesTuple.} = ref object
|
WireRefYours* {.preservesTuple.} = object
|
||||||
`field0`* {.preservesLiteral: "1".}: tuple[]
|
`field0`* {.preservesLiteral: "1".}: tuple[]
|
||||||
`oid`*: Oid
|
`oid`*: Oid
|
||||||
`attenuation`* {.preservesTupleTail.}: seq[Caveat[Cap]]
|
`attenuation`* {.preservesTupleTail.}: seq[Caveat]
|
||||||
|
|
||||||
`WireRef`*[Cap] {.preservesOr.} = ref object
|
`WireRef`* {.preservesOr.} = object
|
||||||
case orKind*: WireRefKind
|
case orKind*: WireRefKind
|
||||||
of WireRefKind.`mine`:
|
of WireRefKind.`mine`:
|
||||||
`mine`*: WireRefMine
|
`mine`*: WireRefMine
|
||||||
|
|
||||||
of WireRefKind.`yours`:
|
of WireRefKind.`yours`:
|
||||||
`yours`*: WireRefYours[Cap]
|
`yours`*: WireRefYours
|
||||||
|
|
||||||
|
|
||||||
TAttenuate*[Cap] {.preservesRecord: "attenuate".} = ref object
|
TAttenuate* {.preservesRecord: "attenuate".} = object
|
||||||
`template`*: Template[Cap]
|
`template`*: Template
|
||||||
`attenuation`*: seq[Caveat[Cap]]
|
`attenuation`*: seq[Caveat]
|
||||||
|
|
||||||
Oid* = BiggestInt
|
Oid* = BiggestInt
|
||||||
Alts*[Cap] {.preservesRecord: "or".} = ref object
|
Alts* {.preservesRecord: "or".} = object
|
||||||
`alternatives`*: seq[Rewrite[Cap]]
|
`alternatives`*: seq[Rewrite]
|
||||||
|
|
||||||
PatternKind* {.pure.} = enum
|
PatternKind* {.pure.} = enum
|
||||||
`PDiscard`, `PAtom`, `PEmbedded`, `PBind`, `PAnd`, `PNot`, `Lit`,
|
`PDiscard`, `PAtom`, `PEmbedded`, `PBind`, `PAnd`, `PNot`, `Lit`,
|
||||||
`PCompound`
|
`PCompound`
|
||||||
`Pattern`*[Cap] {.preservesOr.} = ref object
|
`Pattern`* {.acyclic, preservesOr.} = ref object
|
||||||
case orKind*: PatternKind
|
case orKind*: PatternKind
|
||||||
of PatternKind.`PDiscard`:
|
of PatternKind.`PDiscard`:
|
||||||
`pdiscard`*: PDiscard
|
`pdiscard`*: PDiscard
|
||||||
|
@ -188,57 +194,60 @@ type
|
||||||
`pembedded`* {.preservesLiteral: "Embedded".}: bool
|
`pembedded`* {.preservesLiteral: "Embedded".}: bool
|
||||||
|
|
||||||
of PatternKind.`PBind`:
|
of PatternKind.`PBind`:
|
||||||
`pbind`*: PBind[Cap]
|
`pbind`*: PBind
|
||||||
|
|
||||||
of PatternKind.`PAnd`:
|
of PatternKind.`PAnd`:
|
||||||
`pand`*: PAnd[Cap]
|
`pand`*: PAnd
|
||||||
|
|
||||||
of PatternKind.`PNot`:
|
of PatternKind.`PNot`:
|
||||||
`pnot`*: PNot[Cap]
|
`pnot`*: PNot
|
||||||
|
|
||||||
of PatternKind.`Lit`:
|
of PatternKind.`Lit`:
|
||||||
`lit`*: Lit[Cap]
|
`lit`*: Lit
|
||||||
|
|
||||||
of PatternKind.`PCompound`:
|
of PatternKind.`PCompound`:
|
||||||
`pcompound`*: PCompound[Cap]
|
`pcompound`*: PCompound
|
||||||
|
|
||||||
|
|
||||||
proc `$`*[Cap](x: PCompound[Cap] | Reject[Cap] | CaveatsField[Cap] |
|
proc `$`*(x: PCompound | Reject | CaveatsField | SturdyDescriptionDetail | PAnd |
|
||||||
SturdyDescriptionDetail[Cap] |
|
SturdyStepDetail |
|
||||||
PAnd[Cap] |
|
Rewrite |
|
||||||
Rewrite[Cap] |
|
Parameters |
|
||||||
PBind[Cap] |
|
TRef |
|
||||||
Lit[Cap] |
|
PBind |
|
||||||
TCompound[Cap] |
|
Lit |
|
||||||
Template[Cap] |
|
TCompound |
|
||||||
Caveat[Cap] |
|
SturdyPathStepDetail |
|
||||||
PNot[Cap] |
|
PDiscard |
|
||||||
SturdyRef[Cap] |
|
Template |
|
||||||
WireRef[Cap] |
|
Caveat |
|
||||||
TAttenuate[Cap] |
|
PNot |
|
||||||
Alts[Cap] |
|
SturdyRef |
|
||||||
Pattern[Cap]): string =
|
WireRef |
|
||||||
`$`(toPreserve(x, Cap))
|
TAttenuate |
|
||||||
|
Oid |
|
||||||
|
Alts |
|
||||||
|
Pattern): string =
|
||||||
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*[Cap](x: PCompound[Cap] | Reject[Cap] | CaveatsField[Cap] |
|
proc encode*(x: PCompound | Reject | CaveatsField | SturdyDescriptionDetail |
|
||||||
SturdyDescriptionDetail[Cap] |
|
PAnd |
|
||||||
PAnd[Cap] |
|
SturdyStepDetail |
|
||||||
Rewrite[Cap] |
|
Rewrite |
|
||||||
PBind[Cap] |
|
Parameters |
|
||||||
Lit[Cap] |
|
TRef |
|
||||||
TCompound[Cap] |
|
PBind |
|
||||||
Template[Cap] |
|
Lit |
|
||||||
Caveat[Cap] |
|
TCompound |
|
||||||
PNot[Cap] |
|
SturdyPathStepDetail |
|
||||||
SturdyRef[Cap] |
|
PDiscard |
|
||||||
WireRef[Cap] |
|
Template |
|
||||||
TAttenuate[Cap] |
|
Caveat |
|
||||||
Alts[Cap] |
|
PNot |
|
||||||
Pattern[Cap]): seq[byte] =
|
SturdyRef |
|
||||||
encode(toPreserve(x, Cap))
|
WireRef |
|
||||||
|
TAttenuate |
|
||||||
proc `$`*(x: TRef | PDiscard | Oid): string =
|
Oid |
|
||||||
`$`(toPreserve(x))
|
Alts |
|
||||||
|
Pattern): seq[byte] =
|
||||||
proc encode*(x: TRef | PDiscard | Oid): seq[byte] =
|
encode(toPreserves(x))
|
||||||
encode(toPreserve(x))
|
|
||||||
|
|
|
@ -7,8 +7,8 @@ type
|
||||||
`host`*: string
|
`host`*: string
|
||||||
`port`*: BiggestInt
|
`port`*: BiggestInt
|
||||||
|
|
||||||
TcpPeerInfo*[Cap] {.preservesRecord: "tcp-peer".} = object
|
TcpPeerInfo* {.preservesRecord: "tcp-peer".} = object
|
||||||
`handle`*: Cap
|
`handle`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
`local`*: TcpLocal
|
`local`*: TcpLocal
|
||||||
`remote`*: TcpRemote
|
`remote`*: TcpRemote
|
||||||
|
|
||||||
|
@ -16,14 +16,8 @@ type
|
||||||
`host`*: string
|
`host`*: string
|
||||||
`port`*: BiggestInt
|
`port`*: BiggestInt
|
||||||
|
|
||||||
proc `$`*[Cap](x: TcpPeerInfo[Cap]): string =
|
proc `$`*(x: TcpLocal | TcpPeerInfo | TcpRemote): string =
|
||||||
`$`(toPreserve(x, Cap))
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*[Cap](x: TcpPeerInfo[Cap]): seq[byte] =
|
proc encode*(x: TcpLocal | TcpPeerInfo | TcpRemote): seq[byte] =
|
||||||
encode(toPreserve(x, Cap))
|
encode(toPreserves(x))
|
||||||
|
|
||||||
proc `$`*(x: TcpLocal | TcpRemote): string =
|
|
||||||
`$`(toPreserve(x))
|
|
||||||
|
|
||||||
proc encode*(x: TcpLocal | TcpRemote): seq[byte] =
|
|
||||||
encode(toPreserve(x))
|
|
||||||
|
|
|
@ -4,11 +4,11 @@ import
|
||||||
|
|
||||||
type
|
type
|
||||||
TimerExpired* {.preservesRecord: "timer-expired".} = object
|
TimerExpired* {.preservesRecord: "timer-expired".} = object
|
||||||
`label`*: Preserve[void]
|
`label`*: Value
|
||||||
`seconds`*: float64
|
`seconds`*: float64
|
||||||
|
|
||||||
SetTimer* {.preservesRecord: "set-timer".} = object
|
SetTimer* {.preservesRecord: "set-timer".} = object
|
||||||
`label`*: Preserve[void]
|
`label`*: Value
|
||||||
`seconds`*: float64
|
`seconds`*: float64
|
||||||
`kind`*: TimerKind
|
`kind`*: TimerKind
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ type
|
||||||
`seconds`*: float64
|
`seconds`*: float64
|
||||||
|
|
||||||
proc `$`*(x: TimerExpired | SetTimer | LaterThan): string =
|
proc `$`*(x: TimerExpired | SetTimer | LaterThan): string =
|
||||||
`$`(toPreserve(x))
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*(x: TimerExpired | SetTimer | LaterThan): seq[byte] =
|
proc encode*(x: TimerExpired | SetTimer | LaterThan): seq[byte] =
|
||||||
encode(toPreserve(x))
|
encode(toPreserves(x))
|
||||||
|
|
|
@ -3,160 +3,160 @@ import
|
||||||
preserves, protocol
|
preserves, protocol
|
||||||
|
|
||||||
type
|
type
|
||||||
TargetedTurnEvent*[Cap] {.preservesRecord: "event".} = object
|
TargetedTurnEvent* {.preservesRecord: "event".} = object
|
||||||
`target`*: Target[Cap]
|
`target`*: Target
|
||||||
`detail`*: TurnEvent[Cap]
|
`detail`*: TurnEvent
|
||||||
|
|
||||||
`LinkedTaskReleaseReason`* {.preservesOr, pure.} = enum
|
`LinkedTaskReleaseReason`* {.preservesOr, pure.} = enum
|
||||||
`cancelled`, `normal`
|
`cancelled`, `normal`
|
||||||
TurnId*[Cap] = Preserve[Cap]
|
TurnId* = Value
|
||||||
AssertionDescriptionKind* {.pure.} = enum
|
AssertionDescriptionKind* {.pure.} = enum
|
||||||
`value`, `opaque`
|
`value`, `opaque`
|
||||||
AssertionDescriptionValue*[Cap] {.preservesRecord: "value".} = object
|
AssertionDescriptionValue* {.preservesRecord: "value".} = object
|
||||||
`value`*: Preserve[Cap]
|
`value`*: Value
|
||||||
|
|
||||||
AssertionDescriptionOpaque*[Cap] {.preservesRecord: "opaque".} = object
|
AssertionDescriptionOpaque* {.preservesRecord: "opaque".} = object
|
||||||
`description`*: Preserve[Cap]
|
`description`*: Value
|
||||||
|
|
||||||
`AssertionDescription`*[Cap] {.preservesOr.} = object
|
`AssertionDescription`* {.preservesOr.} = object
|
||||||
case orKind*: AssertionDescriptionKind
|
case orKind*: AssertionDescriptionKind
|
||||||
of AssertionDescriptionKind.`value`:
|
of AssertionDescriptionKind.`value`:
|
||||||
`value`*: AssertionDescriptionValue[Cap]
|
`value`*: AssertionDescriptionValue
|
||||||
|
|
||||||
of AssertionDescriptionKind.`opaque`:
|
of AssertionDescriptionKind.`opaque`:
|
||||||
`opaque`*: AssertionDescriptionOpaque[Cap]
|
`opaque`*: AssertionDescriptionOpaque
|
||||||
|
|
||||||
|
|
||||||
NameKind* {.pure.} = enum
|
NameKind* {.pure.} = enum
|
||||||
`anonymous`, `named`
|
`anonymous`, `named`
|
||||||
NameAnonymous* {.preservesRecord: "anonymous".} = object
|
NameAnonymous* {.preservesRecord: "anonymous".} = object
|
||||||
|
|
||||||
NameNamed*[Cap] {.preservesRecord: "named".} = object
|
NameNamed* {.preservesRecord: "named".} = object
|
||||||
`name`*: Preserve[Cap]
|
`name`*: Value
|
||||||
|
|
||||||
`Name`*[Cap] {.preservesOr.} = object
|
`Name`* {.preservesOr.} = object
|
||||||
case orKind*: NameKind
|
case orKind*: NameKind
|
||||||
of NameKind.`anonymous`:
|
of NameKind.`anonymous`:
|
||||||
`anonymous`*: NameAnonymous
|
`anonymous`*: NameAnonymous
|
||||||
|
|
||||||
of NameKind.`named`:
|
of NameKind.`named`:
|
||||||
`named`*: NameNamed[Cap]
|
`named`*: NameNamed
|
||||||
|
|
||||||
|
|
||||||
ActorId*[Cap] = Preserve[Cap]
|
ActorId* = Value
|
||||||
FacetId*[Cap] = Preserve[Cap]
|
FacetId* = Value
|
||||||
`FacetStopReason`* {.preservesOr, pure.} = enum
|
`FacetStopReason`* {.preservesOr, pure.} = enum
|
||||||
`explicitAction`, `inert`, `parentStopping`, `actorStopping`
|
`explicitAction`, `inert`, `parentStopping`, `actorStopping`
|
||||||
TaskId*[Cap] = Preserve[Cap]
|
TaskId* = Value
|
||||||
ActorActivationKind* {.pure.} = enum
|
ActorActivationKind* {.pure.} = enum
|
||||||
`start`, `turn`, `stop`
|
`start`, `turn`, `stop`
|
||||||
ActorActivationStart*[Cap] {.preservesRecord: "start".} = object
|
ActorActivationStart* {.preservesRecord: "start".} = object
|
||||||
`actorName`*: Name[Cap]
|
`actorName`*: Name
|
||||||
|
|
||||||
ActorActivationStop* {.preservesRecord: "stop".} = object
|
ActorActivationStop* {.preservesRecord: "stop".} = object
|
||||||
`status`*: ExitStatus
|
`status`*: ExitStatus
|
||||||
|
|
||||||
`ActorActivation`*[Cap] {.preservesOr.} = object
|
`ActorActivation`* {.preservesOr.} = object
|
||||||
case orKind*: ActorActivationKind
|
case orKind*: ActorActivationKind
|
||||||
of ActorActivationKind.`start`:
|
of ActorActivationKind.`start`:
|
||||||
`start`*: ActorActivationStart[Cap]
|
`start`*: ActorActivationStart
|
||||||
|
|
||||||
of ActorActivationKind.`turn`:
|
of ActorActivationKind.`turn`:
|
||||||
`turn`*: TurnDescription[Cap]
|
`turn`*: TurnDescription
|
||||||
|
|
||||||
of ActorActivationKind.`stop`:
|
of ActorActivationKind.`stop`:
|
||||||
`stop`*: ActorActivationStop
|
`stop`*: ActorActivationStop
|
||||||
|
|
||||||
|
|
||||||
Target*[Cap] {.preservesRecord: "entity".} = object
|
Target* {.preservesRecord: "entity".} = object
|
||||||
`actor`*: ActorId[Cap]
|
`actor`*: ActorId
|
||||||
`facet`*: FacetId[Cap]
|
`facet`*: FacetId
|
||||||
`oid`*: Oid[Cap]
|
`oid`*: Oid
|
||||||
|
|
||||||
TurnCauseKind* {.pure.} = enum
|
TurnCauseKind* {.pure.} = enum
|
||||||
`turn`, `cleanup`, `linkedTaskRelease`, `periodicActivation`, `delay`,
|
`turn`, `cleanup`, `linkedTaskRelease`, `periodicActivation`, `delay`,
|
||||||
`external`
|
`external`
|
||||||
TurnCauseTurn*[Cap] {.preservesRecord: "caused-by".} = object
|
TurnCauseTurn* {.preservesRecord: "caused-by".} = object
|
||||||
`id`*: TurnId[Cap]
|
`id`*: TurnId
|
||||||
|
|
||||||
TurnCauseCleanup* {.preservesRecord: "cleanup".} = object
|
TurnCauseCleanup* {.preservesRecord: "cleanup".} = object
|
||||||
|
|
||||||
TurnCauseLinkedTaskRelease*[Cap] {.preservesRecord: "linked-task-release".} = object
|
TurnCauseLinkedTaskRelease* {.preservesRecord: "linked-task-release".} = object
|
||||||
`id`*: TaskId[Cap]
|
`id`*: TaskId
|
||||||
`reason`*: LinkedTaskReleaseReason
|
`reason`*: LinkedTaskReleaseReason
|
||||||
|
|
||||||
TurnCausePeriodicActivation* {.preservesRecord: "periodic-activation".} = object
|
TurnCausePeriodicActivation* {.preservesRecord: "periodic-activation".} = object
|
||||||
`period`*: float64
|
`period`*: float64
|
||||||
|
|
||||||
TurnCauseDelay*[Cap] {.preservesRecord: "delay".} = object
|
TurnCauseDelay* {.preservesRecord: "delay".} = object
|
||||||
`causingTurn`*: TurnId[Cap]
|
`causingTurn`*: TurnId
|
||||||
`amount`*: float64
|
`amount`*: float64
|
||||||
|
|
||||||
TurnCauseExternal*[Cap] {.preservesRecord: "external".} = object
|
TurnCauseExternal* {.preservesRecord: "external".} = object
|
||||||
`description`*: Preserve[Cap]
|
`description`*: Value
|
||||||
|
|
||||||
`TurnCause`*[Cap] {.preservesOr.} = object
|
`TurnCause`* {.preservesOr.} = object
|
||||||
case orKind*: TurnCauseKind
|
case orKind*: TurnCauseKind
|
||||||
of TurnCauseKind.`turn`:
|
of TurnCauseKind.`turn`:
|
||||||
`turn`*: TurnCauseTurn[Cap]
|
`turn`*: TurnCauseTurn
|
||||||
|
|
||||||
of TurnCauseKind.`cleanup`:
|
of TurnCauseKind.`cleanup`:
|
||||||
`cleanup`*: TurnCauseCleanup
|
`cleanup`*: TurnCauseCleanup
|
||||||
|
|
||||||
of TurnCauseKind.`linkedTaskRelease`:
|
of TurnCauseKind.`linkedTaskRelease`:
|
||||||
`linkedtaskrelease`*: TurnCauseLinkedTaskRelease[Cap]
|
`linkedtaskrelease`*: TurnCauseLinkedTaskRelease
|
||||||
|
|
||||||
of TurnCauseKind.`periodicActivation`:
|
of TurnCauseKind.`periodicActivation`:
|
||||||
`periodicactivation`*: TurnCausePeriodicActivation
|
`periodicactivation`*: TurnCausePeriodicActivation
|
||||||
|
|
||||||
of TurnCauseKind.`delay`:
|
of TurnCauseKind.`delay`:
|
||||||
`delay`*: TurnCauseDelay[Cap]
|
`delay`*: TurnCauseDelay
|
||||||
|
|
||||||
of TurnCauseKind.`external`:
|
of TurnCauseKind.`external`:
|
||||||
`external`*: TurnCauseExternal[Cap]
|
`external`*: TurnCauseExternal
|
||||||
|
|
||||||
|
|
||||||
TurnEventKind* {.pure.} = enum
|
TurnEventKind* {.pure.} = enum
|
||||||
`assert`, `retract`, `message`, `sync`, `breakLink`
|
`assert`, `retract`, `message`, `sync`, `breakLink`
|
||||||
TurnEventAssert*[Cap] {.preservesRecord: "assert".} = object
|
TurnEventAssert* {.preservesRecord: "assert".} = object
|
||||||
`assertion`*: AssertionDescription[Cap]
|
`assertion`*: AssertionDescription
|
||||||
`handle`*: protocol.Handle
|
`handle`*: protocol.Handle
|
||||||
|
|
||||||
TurnEventRetract* {.preservesRecord: "retract".} = object
|
TurnEventRetract* {.preservesRecord: "retract".} = object
|
||||||
`handle`*: protocol.Handle
|
`handle`*: protocol.Handle
|
||||||
|
|
||||||
TurnEventMessage*[Cap] {.preservesRecord: "message".} = object
|
TurnEventMessage* {.preservesRecord: "message".} = object
|
||||||
`body`*: AssertionDescription[Cap]
|
`body`*: AssertionDescription
|
||||||
|
|
||||||
TurnEventSync*[Cap] {.preservesRecord: "sync".} = object
|
TurnEventSync* {.preservesRecord: "sync".} = object
|
||||||
`peer`*: Target[Cap]
|
`peer`*: Target
|
||||||
|
|
||||||
TurnEventBreakLink*[Cap] {.preservesRecord: "break-link".} = object
|
TurnEventBreakLink* {.preservesRecord: "break-link".} = object
|
||||||
`source`*: ActorId[Cap]
|
`source`*: ActorId
|
||||||
`handle`*: protocol.Handle
|
`handle`*: protocol.Handle
|
||||||
|
|
||||||
`TurnEvent`*[Cap] {.preservesOr.} = object
|
`TurnEvent`* {.preservesOr.} = object
|
||||||
case orKind*: TurnEventKind
|
case orKind*: TurnEventKind
|
||||||
of TurnEventKind.`assert`:
|
of TurnEventKind.`assert`:
|
||||||
`assert`*: TurnEventAssert[Cap]
|
`assert`*: TurnEventAssert
|
||||||
|
|
||||||
of TurnEventKind.`retract`:
|
of TurnEventKind.`retract`:
|
||||||
`retract`*: TurnEventRetract
|
`retract`*: TurnEventRetract
|
||||||
|
|
||||||
of TurnEventKind.`message`:
|
of TurnEventKind.`message`:
|
||||||
`message`*: TurnEventMessage[Cap]
|
`message`*: TurnEventMessage
|
||||||
|
|
||||||
of TurnEventKind.`sync`:
|
of TurnEventKind.`sync`:
|
||||||
`sync`*: TurnEventSync[Cap]
|
`sync`*: TurnEventSync
|
||||||
|
|
||||||
of TurnEventKind.`breakLink`:
|
of TurnEventKind.`breakLink`:
|
||||||
`breaklink`*: TurnEventBreakLink[Cap]
|
`breaklink`*: TurnEventBreakLink
|
||||||
|
|
||||||
|
|
||||||
TurnDescription*[Cap] {.preservesRecord: "turn".} = object
|
TurnDescription* {.preservesRecord: "turn".} = object
|
||||||
`id`*: TurnId[Cap]
|
`id`*: TurnId
|
||||||
`cause`*: TurnCause[Cap]
|
`cause`*: TurnCause
|
||||||
`actions`*: seq[ActionDescription[Cap]]
|
`actions`*: seq[ActionDescription]
|
||||||
|
|
||||||
ExitStatusKind* {.pure.} = enum
|
ExitStatusKind* {.pure.} = enum
|
||||||
`ok`, `Error`
|
`ok`, `Error`
|
||||||
|
@ -169,101 +169,95 @@ type
|
||||||
`error`*: protocol.Error
|
`error`*: protocol.Error
|
||||||
|
|
||||||
|
|
||||||
TraceEntry*[Cap] {.preservesRecord: "trace".} = object
|
TraceEntry* {.preservesRecord: "trace".} = object
|
||||||
`timestamp`*: float64
|
`timestamp`*: float64
|
||||||
`actor`*: ActorId[Cap]
|
`actor`*: ActorId
|
||||||
`item`*: ActorActivation[Cap]
|
`item`*: ActorActivation
|
||||||
|
|
||||||
Oid*[Cap] = Preserve[Cap]
|
Oid* = Value
|
||||||
ActionDescriptionKind* {.pure.} = enum
|
ActionDescriptionKind* {.pure.} = enum
|
||||||
`dequeue`, `enqueue`, `dequeueInternal`, `enqueueInternal`, `spawn`, `link`,
|
`dequeue`, `enqueue`, `dequeueInternal`, `enqueueInternal`, `spawn`, `link`,
|
||||||
`facetStart`, `facetStop`, `linkedTaskStart`
|
`facetStart`, `facetStop`, `linkedTaskStart`
|
||||||
ActionDescriptionDequeue*[Cap] {.preservesRecord: "dequeue".} = object
|
ActionDescriptionDequeue* {.preservesRecord: "dequeue".} = object
|
||||||
`event`*: TargetedTurnEvent[Cap]
|
`event`*: TargetedTurnEvent
|
||||||
|
|
||||||
ActionDescriptionEnqueue*[Cap] {.preservesRecord: "enqueue".} = object
|
ActionDescriptionEnqueue* {.preservesRecord: "enqueue".} = object
|
||||||
`event`*: TargetedTurnEvent[Cap]
|
`event`*: TargetedTurnEvent
|
||||||
|
|
||||||
ActionDescriptionDequeueInternal*[Cap] {.preservesRecord: "dequeue-internal".} = object
|
ActionDescriptionDequeueInternal* {.preservesRecord: "dequeue-internal".} = object
|
||||||
`event`*: TargetedTurnEvent[Cap]
|
`event`*: TargetedTurnEvent
|
||||||
|
|
||||||
ActionDescriptionEnqueueInternal*[Cap] {.preservesRecord: "enqueue-internal".} = object
|
ActionDescriptionEnqueueInternal* {.preservesRecord: "enqueue-internal".} = object
|
||||||
`event`*: TargetedTurnEvent[Cap]
|
`event`*: TargetedTurnEvent
|
||||||
|
|
||||||
ActionDescriptionSpawn*[Cap] {.preservesRecord: "spawn".} = object
|
ActionDescriptionSpawn* {.preservesRecord: "spawn".} = object
|
||||||
`link`*: bool
|
`link`*: bool
|
||||||
`id`*: ActorId[Cap]
|
`id`*: ActorId
|
||||||
|
|
||||||
ActionDescriptionLink*[Cap] {.preservesRecord: "link".} = object
|
ActionDescriptionLink* {.preservesRecord: "link".} = object
|
||||||
`parentActor`*: ActorId[Cap]
|
`parentActor`*: ActorId
|
||||||
`childToParent`*: protocol.Handle
|
`childToParent`*: protocol.Handle
|
||||||
`childActor`*: ActorId[Cap]
|
`childActor`*: ActorId
|
||||||
`parentToChild`*: protocol.Handle
|
`parentToChild`*: protocol.Handle
|
||||||
|
|
||||||
ActionDescriptionFacetStart*[Cap] {.preservesRecord: "facet-start".} = object
|
ActionDescriptionFacetStart* {.preservesRecord: "facet-start".} = object
|
||||||
`path`*: seq[FacetId[Cap]]
|
`path`*: seq[FacetId]
|
||||||
|
|
||||||
ActionDescriptionFacetStop*[Cap] {.preservesRecord: "facet-stop".} = object
|
ActionDescriptionFacetStop* {.preservesRecord: "facet-stop".} = object
|
||||||
`path`*: seq[FacetId[Cap]]
|
`path`*: seq[FacetId]
|
||||||
`reason`*: FacetStopReason
|
`reason`*: FacetStopReason
|
||||||
|
|
||||||
ActionDescriptionLinkedTaskStart*[Cap] {.preservesRecord: "linked-task-start".} = object
|
ActionDescriptionLinkedTaskStart* {.preservesRecord: "linked-task-start".} = object
|
||||||
`taskName`*: Name[Cap]
|
`taskName`*: Name
|
||||||
`id`*: TaskId[Cap]
|
`id`*: TaskId
|
||||||
|
|
||||||
`ActionDescription`*[Cap] {.preservesOr.} = object
|
`ActionDescription`* {.preservesOr.} = object
|
||||||
case orKind*: ActionDescriptionKind
|
case orKind*: ActionDescriptionKind
|
||||||
of ActionDescriptionKind.`dequeue`:
|
of ActionDescriptionKind.`dequeue`:
|
||||||
`dequeue`*: ActionDescriptionDequeue[Cap]
|
`dequeue`*: ActionDescriptionDequeue
|
||||||
|
|
||||||
of ActionDescriptionKind.`enqueue`:
|
of ActionDescriptionKind.`enqueue`:
|
||||||
`enqueue`*: ActionDescriptionEnqueue[Cap]
|
`enqueue`*: ActionDescriptionEnqueue
|
||||||
|
|
||||||
of ActionDescriptionKind.`dequeueInternal`:
|
of ActionDescriptionKind.`dequeueInternal`:
|
||||||
`dequeueinternal`*: ActionDescriptionDequeueInternal[Cap]
|
`dequeueinternal`*: ActionDescriptionDequeueInternal
|
||||||
|
|
||||||
of ActionDescriptionKind.`enqueueInternal`:
|
of ActionDescriptionKind.`enqueueInternal`:
|
||||||
`enqueueinternal`*: ActionDescriptionEnqueueInternal[Cap]
|
`enqueueinternal`*: ActionDescriptionEnqueueInternal
|
||||||
|
|
||||||
of ActionDescriptionKind.`spawn`:
|
of ActionDescriptionKind.`spawn`:
|
||||||
`spawn`*: ActionDescriptionSpawn[Cap]
|
`spawn`*: ActionDescriptionSpawn
|
||||||
|
|
||||||
of ActionDescriptionKind.`link`:
|
of ActionDescriptionKind.`link`:
|
||||||
`link`*: ActionDescriptionLink[Cap]
|
`link`*: ActionDescriptionLink
|
||||||
|
|
||||||
of ActionDescriptionKind.`facetStart`:
|
of ActionDescriptionKind.`facetStart`:
|
||||||
`facetstart`*: ActionDescriptionFacetStart[Cap]
|
`facetstart`*: ActionDescriptionFacetStart
|
||||||
|
|
||||||
of ActionDescriptionKind.`facetStop`:
|
of ActionDescriptionKind.`facetStop`:
|
||||||
`facetstop`*: ActionDescriptionFacetStop[Cap]
|
`facetstop`*: ActionDescriptionFacetStop
|
||||||
|
|
||||||
of ActionDescriptionKind.`linkedTaskStart`:
|
of ActionDescriptionKind.`linkedTaskStart`:
|
||||||
`linkedtaskstart`*: ActionDescriptionLinkedTaskStart[Cap]
|
`linkedtaskstart`*: ActionDescriptionLinkedTaskStart
|
||||||
|
|
||||||
|
|
||||||
proc `$`*[Cap](x: TargetedTurnEvent[Cap] | AssertionDescription[Cap] | Name[Cap] |
|
proc `$`*(x: TargetedTurnEvent | AssertionDescription | Name | ActorActivation |
|
||||||
ActorActivation[Cap] |
|
Target |
|
||||||
Target[Cap] |
|
TurnCause |
|
||||||
TurnCause[Cap] |
|
TurnEvent |
|
||||||
TurnEvent[Cap] |
|
TurnDescription |
|
||||||
TurnDescription[Cap] |
|
ExitStatus |
|
||||||
TraceEntry[Cap] |
|
TraceEntry |
|
||||||
ActionDescription[Cap]): string =
|
ActionDescription): string =
|
||||||
`$`(toPreserve(x, Cap))
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*[Cap](x: TargetedTurnEvent[Cap] | AssertionDescription[Cap] |
|
proc encode*(x: TargetedTurnEvent | AssertionDescription | Name |
|
||||||
Name[Cap] |
|
ActorActivation |
|
||||||
ActorActivation[Cap] |
|
Target |
|
||||||
Target[Cap] |
|
TurnCause |
|
||||||
TurnCause[Cap] |
|
TurnEvent |
|
||||||
TurnEvent[Cap] |
|
TurnDescription |
|
||||||
TurnDescription[Cap] |
|
ExitStatus |
|
||||||
TraceEntry[Cap] |
|
TraceEntry |
|
||||||
ActionDescription[Cap]): seq[byte] =
|
ActionDescription): seq[byte] =
|
||||||
encode(toPreserve(x, Cap))
|
encode(toPreserves(x))
|
||||||
|
|
||||||
proc `$`*(x: ExitStatus): string =
|
|
||||||
`$`(toPreserve(x))
|
|
||||||
|
|
||||||
proc encode*(x: ExitStatus): seq[byte] =
|
|
||||||
encode(toPreserve(x))
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ type
|
||||||
`port`*: BiggestInt
|
`port`*: BiggestInt
|
||||||
|
|
||||||
proc `$`*(x: WebSocket | Stdio | Unix | Tcp): string =
|
proc `$`*(x: WebSocket | Stdio | Unix | Tcp): string =
|
||||||
`$`(toPreserve(x))
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*(x: WebSocket | Stdio | Unix | Tcp): seq[byte] =
|
proc encode*(x: WebSocket | Stdio | Unix | Tcp): seq[byte] =
|
||||||
encode(toPreserve(x))
|
encode(toPreserves(x))
|
||||||
|
|
|
@ -3,12 +3,12 @@ import
|
||||||
preserves
|
preserves
|
||||||
|
|
||||||
type
|
type
|
||||||
Instance*[Cap] {.preservesRecord: "Instance".} = object
|
Instance* {.preservesRecord: "Instance".} = object
|
||||||
`name`*: string
|
`name`*: string
|
||||||
`argument`*: Preserve[Cap]
|
`argument`*: Value
|
||||||
|
|
||||||
proc `$`*[Cap](x: Instance[Cap]): string =
|
proc `$`*(x: Instance): string =
|
||||||
`$`(toPreserve(x, Cap))
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*[Cap](x: Instance[Cap]): seq[byte] =
|
proc encode*(x: Instance): seq[byte] =
|
||||||
encode(toPreserve(x, Cap))
|
encode(toPreserves(x))
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[asyncdispatch, options, streams, tables]
|
import std/[asyncdispatch, options, tables]
|
||||||
|
from std/os import getEnv, `/`
|
||||||
import preserves
|
import preserves
|
||||||
import ./actors, ./durings, ./membranes, ./protocols/[protocol, sturdy, transportAddress]
|
import ../syndicate, /capabilities, ./durings, ./membranes, ./protocols/[gatekeeper, protocol, sturdy, transportAddress]
|
||||||
|
|
||||||
when defined(traceSyndicate):
|
when defined(traceSyndicate):
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
|
@ -15,20 +16,21 @@ else:
|
||||||
|
|
||||||
export `$`
|
export `$`
|
||||||
|
|
||||||
type Oid = sturdy.Oid
|
type
|
||||||
|
Oid = sturdy.Oid
|
||||||
|
|
||||||
|
export Stdio, Tcp, WebSocket, Unix
|
||||||
|
|
||||||
type
|
type
|
||||||
Value = Preserve[void]
|
Assertion = Value
|
||||||
Assertion = Preserve[Ref]
|
WireRef = sturdy.WireRef
|
||||||
WireRef = sturdy.WireRef[void]
|
Turn = syndicate.Turn
|
||||||
|
Handle = actors.Handle
|
||||||
|
|
||||||
Turn = actors.Turn
|
PacketWriter = proc (turn: var Turn; buf: seq[byte]) {.closure, gcsafe.}
|
||||||
|
RelaySetup = proc (turn: var Turn; relay: Relay) {.closure, gcsafe.}
|
||||||
|
|
||||||
type
|
Relay* = ref object
|
||||||
PacketWriter = proc (pkt: sink Packet): Future[void] {.gcsafe.}
|
|
||||||
RelaySetup = proc (turn: var Turn; relay: Relay) {.gcsafe.}
|
|
||||||
|
|
||||||
Relay* = ref object of RootObj
|
|
||||||
facet: Facet
|
facet: Facet
|
||||||
inboundAssertions: Table[Handle,
|
inboundAssertions: Table[Handle,
|
||||||
tuple[localHandle: Handle, imported: seq[WireSymbol]]]
|
tuple[localHandle: Handle, imported: seq[WireSymbol]]]
|
||||||
|
@ -37,12 +39,13 @@ type
|
||||||
imported: Membrane
|
imported: Membrane
|
||||||
nextLocalOid: Oid
|
nextLocalOid: Oid
|
||||||
pendingTurn: protocol.Turn
|
pendingTurn: protocol.Turn
|
||||||
|
wireBuf: BufferedDecoder
|
||||||
packetWriter: PacketWriter
|
packetWriter: PacketWriter
|
||||||
untrusted: bool
|
peer: Cap
|
||||||
|
|
||||||
SyncPeerEntity = ref object of Entity
|
SyncPeerEntity = ref object of Entity
|
||||||
relay: Relay
|
relay: Relay
|
||||||
peer: Ref
|
peer: Cap
|
||||||
handleMap: Table[Handle, Handle]
|
handleMap: Table[Handle, Handle]
|
||||||
e: WireSymbol
|
e: WireSymbol
|
||||||
|
|
||||||
|
@ -51,7 +54,7 @@ type
|
||||||
label: string
|
label: string
|
||||||
relay: Relay
|
relay: Relay
|
||||||
|
|
||||||
proc releaseRefOut(r: Relay; e: WireSymbol) =
|
proc releaseCapOut(r: Relay; e: WireSymbol) =
|
||||||
r.exported.drop e
|
r.exported.drop e
|
||||||
|
|
||||||
method publish(spe: SyncPeerEntity; t: var Turn; a: AssertionRef; h: Handle) =
|
method publish(spe: SyncPeerEntity; t: var Turn; a: AssertionRef; h: Handle) =
|
||||||
|
@ -64,33 +67,35 @@ method retract(se: SyncPeerEntity; t: var Turn; h: Handle) =
|
||||||
|
|
||||||
method message(se: SyncPeerEntity; t: var Turn; a: AssertionRef) =
|
method message(se: SyncPeerEntity; t: var Turn; a: AssertionRef) =
|
||||||
if not se.e.isNil:
|
if not se.e.isNil:
|
||||||
se.relay.releaseRefOut(se.e)
|
se.relay.releaseCapOut(se.e)
|
||||||
message(t, se.peer, a.value)
|
message(t, se.peer, a.value)
|
||||||
|
|
||||||
method sync(se: SyncPeerEntity; t: var Turn; peer: Ref) =
|
method sync(se: SyncPeerEntity; t: var Turn; peer: Cap) =
|
||||||
sync(t, se.peer, peer)
|
sync(t, se.peer, peer)
|
||||||
|
|
||||||
proc newSyncPeerEntity(r: Relay; p: Ref): SyncPeerEntity =
|
proc newSyncPeerEntity(r: Relay; p: Cap): SyncPeerEntity =
|
||||||
SyncPeerEntity(relay: r, peer: p)
|
SyncPeerEntity(relay: r, peer: p)
|
||||||
|
|
||||||
proc rewriteRefOut(relay: Relay; `ref`: Ref; exported: var seq[WireSymbol]): WireRef =
|
proc rewriteCapOut(relay: Relay; cap: Cap; exported: var seq[WireSymbol]): WireRef =
|
||||||
if `ref`.target of RelayEntity and `ref`.target.RelayEntity.relay == relay and `ref`.attenuation.len == 0:
|
if cap.target of RelayEntity and cap.target.RelayEntity.relay == relay and cap.attenuation.len == 0:
|
||||||
WireRef(orKind: WireRefKind.yours, yours: WireRefYours[void](oid: `ref`.target.oid))
|
result = WireRef(orKind: WireRefKind.yours, yours: WireRefYours(oid: cap.target.oid))
|
||||||
else:
|
else:
|
||||||
var ws = grab(relay.exported, `ref`)
|
var ws = grab(relay.exported, cap)
|
||||||
if ws.isNil:
|
if ws.isNil:
|
||||||
ws = newWireSymbol(relay.exported, relay.nextLocalOid, `ref`)
|
ws = newWireSymbol(relay.exported, relay.nextLocalOid, cap)
|
||||||
inc relay.nextLocalOid
|
inc relay.nextLocalOid
|
||||||
exported.add ws
|
exported.add ws
|
||||||
WireRef(
|
result = WireRef(
|
||||||
orKind: WireRefKind.mine,
|
orKind: WireRefKind.mine,
|
||||||
mine: WireRefMine(oid: ws.oid))
|
mine: WireRefMine(oid: ws.oid))
|
||||||
|
|
||||||
proc rewriteOut(relay: Relay; v: Assertion):
|
proc rewriteOut(relay: Relay; v: Assertion):
|
||||||
tuple[rewritten: Value, exported: seq[WireSymbol]] {.gcsafe.} =
|
tuple[rewritten: Value, exported: seq[WireSymbol]] {.gcsafe.} =
|
||||||
var exported: seq[WireSymbol]
|
var exported: seq[WireSymbol]
|
||||||
result.rewritten = contract(v) do (r: Ref) -> Value:
|
result.rewritten = mapEmbeds(v) do (pr: Value) -> Value:
|
||||||
rewriteRefOut(relay, r, exported).toPreserve
|
let o = pr.unembed(Cap); if o.isSome:
|
||||||
|
rewriteCapOut(relay, o.get, exported).toPreserves
|
||||||
|
else: pr
|
||||||
result.exported = exported
|
result.exported = exported
|
||||||
|
|
||||||
proc register(relay: Relay; v: Assertion; h: Handle): tuple[rewritten: Value, exported: seq[WireSymbol]] =
|
proc register(relay: Relay; v: Assertion; h: Handle): tuple[rewritten: Value, exported: seq[WireSymbol]] =
|
||||||
|
@ -100,55 +105,52 @@ proc register(relay: Relay; v: Assertion; h: Handle): tuple[rewritten: Value, ex
|
||||||
proc deregister(relay: Relay; h: Handle) =
|
proc deregister(relay: Relay; h: Handle) =
|
||||||
var outbound: seq[WireSymbol]
|
var outbound: seq[WireSymbol]
|
||||||
if relay.outboundAssertions.pop(h, outbound):
|
if relay.outboundAssertions.pop(h, outbound):
|
||||||
for e in outbound: releaseRefOut(relay, e)
|
for e in outbound: releaseCapOut(relay, e)
|
||||||
|
|
||||||
proc send(r: Relay; pkt: sink Packet): Future[void] =
|
proc send(relay: Relay; turn: var Turn; rOid: protocol.Oid; m: Event) =
|
||||||
assert(not r.packetWriter.isNil, "missing packetWriter proc")
|
if relay.pendingTurn.len == 0:
|
||||||
r.packetWriter(pkt)
|
# If the pending queue is empty then schedule a packet
|
||||||
|
# to be sent after pending I/O is processed.
|
||||||
proc send(r: Relay; rOid: protocol.Oid; m: Event) =
|
|
||||||
if r.pendingTurn.len == 0:
|
|
||||||
# do not send a packet immediately,
|
|
||||||
# wait until the pending I/O is processed
|
|
||||||
callSoon do ():
|
callSoon do ():
|
||||||
r.facet.run do (turn: var Turn):
|
relay.facet.run do (turn: var Turn):
|
||||||
var pkt = Packet(
|
var pkt = Packet(
|
||||||
orKind: PacketKind.Turn,
|
orKind: PacketKind.Turn,
|
||||||
turn: move r.pendingTurn)
|
turn: move relay.pendingTurn)
|
||||||
trace "C: ", pkt
|
trace "C: ", pkt
|
||||||
asyncCheck(turn, r.send(pkt))
|
relay.packetWriter(turn, encode pkt)
|
||||||
r.pendingTurn.add TurnEvent(oid: rOid, event: m)
|
relay.pendingTurn.add TurnEvent(oid: rOid, event: m)
|
||||||
|
|
||||||
proc send(re: RelayEntity; ev: Event) =
|
proc send(re: RelayEntity; turn: var Turn; ev: Event) =
|
||||||
send(re.relay, protocol.Oid re.oid, ev)
|
send(re.relay, turn, protocol.Oid re.oid, ev)
|
||||||
|
|
||||||
method publish(re: RelayEntity; t: var Turn; a: AssertionRef; h: Handle) {.gcsafe.} =
|
method publish(re: RelayEntity; t: var Turn; a: AssertionRef; h: Handle) {.gcsafe.} =
|
||||||
re.send Event(
|
re.send(t, Event(
|
||||||
orKind: EventKind.Assert,
|
orKind: EventKind.Assert,
|
||||||
`assert`: protocol.Assert(
|
`assert`: protocol.Assert(
|
||||||
assertion: re.relay.register(a.value, h).rewritten,
|
assertion: re.relay.register(a.value, h).rewritten,
|
||||||
handle: h))
|
handle: h)))
|
||||||
|
|
||||||
method retract(re: RelayEntity; t: var Turn; h: Handle) {.gcsafe.} =
|
method retract(re: RelayEntity; t: var Turn; h: Handle) {.gcsafe.} =
|
||||||
re.relay.deregister h
|
re.relay.deregister h
|
||||||
re.send Event(
|
re.send(t, Event(
|
||||||
orKind: EventKind.Retract,
|
orKind: EventKind.Retract,
|
||||||
retract: Retract(handle: h))
|
retract: Retract(handle: h)))
|
||||||
|
|
||||||
method message(re: RelayEntity; turn: var Turn; msg: AssertionRef) {.gcsafe.} =
|
method message(re: RelayEntity; turn: var Turn; msg: AssertionRef) {.gcsafe.} =
|
||||||
var (value, exported) = rewriteOut(re.relay, msg.value)
|
var (value, exported) = rewriteOut(re.relay, msg.value)
|
||||||
assert(len(exported) == 0, "cannot send a reference in a message")
|
assert(len(exported) == 0, "cannot send a reference in a message")
|
||||||
if len(exported) == 0:
|
if len(exported) == 0:
|
||||||
re.send Event(orKind: EventKind.Message, message: Message(body: value))
|
re.send(turn, Event(orKind: EventKind.Message, message: Message(body: value)))
|
||||||
|
|
||||||
method sync(re: RelayEntity; turn: var Turn; peer: Ref) {.gcsafe.} =
|
method sync(re: RelayEntity; turn: var Turn; peer: Cap) {.gcsafe.} =
|
||||||
var
|
var
|
||||||
peerEntity = newSyncPeerEntity(re.relay, peer)
|
peerEntity = newSyncPeerEntity(re.relay, peer)
|
||||||
exported: seq[WireSymbol]
|
exported: seq[WireSymbol]
|
||||||
discard rewriteRefOut(re.relay, turn.newRef(peerEntity), exported)
|
wr = rewriteCapOut(re.relay, turn.newCap(peerEntity), exported)
|
||||||
# TODO: discard?
|
|
||||||
peerEntity.e = exported[0]
|
peerEntity.e = exported[0]
|
||||||
re.send Event(orKind: EventKind.Sync)
|
var ev = Event(orKind: EventKind.Sync)
|
||||||
|
ev.sync.peer = wr.toPreserves.embed
|
||||||
|
re.send(turn, ev)
|
||||||
|
|
||||||
proc newRelayEntity(label: string; r: Relay; o: Oid): RelayEntity =
|
proc newRelayEntity(label: string; r: Relay; o: Oid): RelayEntity =
|
||||||
RelayEntity(label: label, relay: r, oid: o)
|
RelayEntity(label: label, relay: r, oid: o)
|
||||||
|
@ -157,24 +159,26 @@ using
|
||||||
relay: Relay
|
relay: Relay
|
||||||
facet: Facet
|
facet: Facet
|
||||||
|
|
||||||
proc lookupLocal(relay; oid: Oid): Ref =
|
proc lookupLocal(relay; oid: Oid): Cap =
|
||||||
let sym = relay.exported.grab oid
|
let sym = relay.exported.grab oid
|
||||||
if sym.isNil: newInertRef()
|
if sym.isNil: newInertCap()
|
||||||
else: sym.`ref`
|
else: sym.cap
|
||||||
|
|
||||||
proc isInert(r: Ref): bool =
|
proc isInert(r: Cap): bool =
|
||||||
r.target.isNil
|
r.target.isNil
|
||||||
|
|
||||||
proc rewriteRefIn(relay; facet; n: WireRef, imported: var seq[WireSymbol]): Ref =
|
proc rewriteCapIn(relay; facet; n: WireRef, imported: var seq[WireSymbol]): Cap =
|
||||||
case n.orKind
|
case n.orKind
|
||||||
of WireRefKind.mine:
|
of WireRefKind.mine:
|
||||||
var e = relay.imported.grab(n.mine.oid)
|
var e = relay.imported.grab(n.mine.oid)
|
||||||
if e.isNil: e = newWireSymbol(
|
if e.isNil:
|
||||||
relay.imported,
|
e = newWireSymbol(
|
||||||
n.mine.oid,
|
relay.imported,
|
||||||
newRef(facet, newRelayEntity("rewriteRefIn", relay, n.mine.oid)))
|
n.mine.oid,
|
||||||
|
newCap(facet, newRelayEntity("rewriteCapIn", relay, n.mine.oid)),
|
||||||
|
)
|
||||||
imported.add e
|
imported.add e
|
||||||
result = e.`ref`
|
result = e.cap
|
||||||
of WireRefKind.yours:
|
of WireRefKind.yours:
|
||||||
let r = relay.lookupLocal(n.yours.oid)
|
let r = relay.lookupLocal(n.yours.oid)
|
||||||
if n.yours.attenuation.len == 0 or r.isInert: result = r
|
if n.yours.attenuation.len == 0 or r.isInert: result = r
|
||||||
|
@ -183,20 +187,20 @@ proc rewriteRefIn(relay; facet; n: WireRef, imported: var seq[WireSymbol]): Ref
|
||||||
proc rewriteIn(relay; facet; v: Value):
|
proc rewriteIn(relay; facet; v: Value):
|
||||||
tuple[rewritten: Assertion; imported: seq[WireSymbol]] {.gcsafe.} =
|
tuple[rewritten: Assertion; imported: seq[WireSymbol]] {.gcsafe.} =
|
||||||
var imported: seq[WireSymbol]
|
var imported: seq[WireSymbol]
|
||||||
result.rewritten = expand(v) do (pr: Value) -> Assertion:
|
result.rewritten = mapEmbeds(v) do (pr: Value) -> Value:
|
||||||
var wr: WireRef
|
let wr = pr.preservesTo WireRef; if wr.isSome:
|
||||||
if not fromPreserve(wr, pr):
|
result = rewriteCapIn(relay, facet, wr.get, imported).embed
|
||||||
raiseAssert "expansion of embedded value failed"
|
else:
|
||||||
rewriteRefIn(relay, facet, wr, imported).toPreserve(Ref)
|
result = pr
|
||||||
result.imported = imported
|
result.imported = imported
|
||||||
|
|
||||||
proc close(r: Relay) = discard
|
proc close(r: Relay) = discard
|
||||||
|
|
||||||
proc dispatch*(relay: Relay; turn: var Turn; `ref`: Ref; event: Event) {.gcsafe.} =
|
proc dispatch(relay: Relay; turn: var Turn; cap: Cap; event: Event) {.gcsafe.} =
|
||||||
case event.orKind
|
case event.orKind
|
||||||
of EventKind.Assert:
|
of EventKind.Assert:
|
||||||
let (a, imported) = rewriteIn(relay, turn.facet, event.assert.assertion)
|
let (a, imported) = rewriteIn(relay, turn.facet, event.assert.assertion)
|
||||||
relay.inboundAssertions[event.assert.handle] = (publish(turn, `ref`, a), imported,)
|
relay.inboundAssertions[event.assert.handle] = (publish(turn, cap, a), imported,)
|
||||||
|
|
||||||
of EventKind.Retract:
|
of EventKind.Retract:
|
||||||
let remoteHandle = event.retract.handle
|
let remoteHandle = event.retract.handle
|
||||||
|
@ -208,23 +212,23 @@ proc dispatch*(relay: Relay; turn: var Turn; `ref`: Ref; event: Event) {.gcsafe.
|
||||||
of EventKind.Message:
|
of EventKind.Message:
|
||||||
let (a, imported) = rewriteIn(relay, turn.facet, event.message.body)
|
let (a, imported) = rewriteIn(relay, turn.facet, event.message.body)
|
||||||
assert imported.len == 0, "Cannot receive transient reference"
|
assert imported.len == 0, "Cannot receive transient reference"
|
||||||
turn.message(`ref`, a)
|
turn.message(cap, a)
|
||||||
|
|
||||||
of EventKind.Sync:
|
of EventKind.Sync:
|
||||||
discard # TODO
|
discard # TODO
|
||||||
#[
|
#[
|
||||||
var imported: seq[WireSymbol]
|
var imported: seq[WireSymbol]
|
||||||
let k = relay.rewriteRefIn(turn, evenr.sync.peer, imported)
|
let k = relay.rewriteCapIn(turn, evenr.sync.peer, imported)
|
||||||
turn.sync(`ref`) do (turn: var Turn):
|
turn.sync(cap) do (turn: var Turn):
|
||||||
turn.message(k, true)
|
turn.message(k, true)
|
||||||
for e in imported: relay.imported.del e
|
for e in imported: relay.imported.del e
|
||||||
]#
|
]#
|
||||||
|
|
||||||
proc dispatch*(relay: Relay; v: Value) {.gcsafe.} =
|
proc dispatch(relay: Relay; v: Value) {.gcsafe.} =
|
||||||
trace "S: ", v
|
trace "S: ", v
|
||||||
run(relay.facet) do (t: var Turn):
|
run(relay.facet) do (t: var Turn):
|
||||||
var pkt: Packet
|
var pkt: Packet
|
||||||
if fromPreserve(pkt, v):
|
if pkt.fromPreserves(v):
|
||||||
case pkt.orKind
|
case pkt.orKind
|
||||||
of PacketKind.Turn:
|
of PacketKind.Turn:
|
||||||
# https://synit.org/book/protocol.html#turn-packets
|
# https://synit.org/book/protocol.html#turn-packets
|
||||||
|
@ -233,7 +237,7 @@ proc dispatch*(relay: Relay; v: Value) {.gcsafe.} =
|
||||||
if not r.isInert:
|
if not r.isInert:
|
||||||
dispatch(relay, t, r, te.event)
|
dispatch(relay, t, r, te.event)
|
||||||
else:
|
else:
|
||||||
stderr.writeLine("discarding event for unknown Ref; ", te.event)
|
stderr.writeLine("discarding event for unknown Cap; ", te.event)
|
||||||
of PacketKind.Error:
|
of PacketKind.Error:
|
||||||
# https://synit.org/book/protocol.html#error-packets
|
# https://synit.org/book/protocol.html#error-packets
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
|
@ -246,172 +250,451 @@ proc dispatch*(relay: Relay; v: Value) {.gcsafe.} =
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
stderr.writeLine("discarding undecoded packet ", v)
|
stderr.writeLine("discarding undecoded packet ", v)
|
||||||
|
|
||||||
|
proc recv(relay: Relay; buf: seq[byte]) =
|
||||||
|
feed(relay.wireBuf, buf)
|
||||||
|
var pr = decode(relay.wireBuf)
|
||||||
|
if pr.isSome: dispatch(relay, pr.get)
|
||||||
|
|
||||||
type
|
type
|
||||||
RelayOptions* = object of RootObj
|
RelayOptions* = object of RootObj
|
||||||
packetWriter*: PacketWriter
|
packetWriter*: PacketWriter
|
||||||
untrusted*: bool
|
|
||||||
RelayActorOptions* = object of RelayOptions
|
RelayActorOptions* = object of RelayOptions
|
||||||
initialOid*: Option[Oid]
|
initialOid*: Option[Oid]
|
||||||
initialRef*: Ref
|
initialCap*: Cap
|
||||||
nextLocalOid*: Option[Oid]
|
nextLocalOid*: Option[Oid]
|
||||||
|
|
||||||
proc newRelay(turn: var Turn; opts: RelayOptions; setup: RelaySetup): Relay =
|
proc spawnRelay(name: string; turn: var Turn; opts: RelayActorOptions; setup: RelaySetup) =
|
||||||
result = Relay(
|
|
||||||
facet: turn.facet,
|
|
||||||
packetWriter: opts.packetWriter,
|
|
||||||
untrusted: opts.untrusted)
|
|
||||||
discard result.facet.preventInertCheck()
|
|
||||||
setup(turn, result)
|
|
||||||
|
|
||||||
proc spawnRelay*(name: string; turn: var Turn; opts: RelayActorOptions; setup: RelaySetup): Future[Ref] =
|
|
||||||
var fut = newFuture[Ref]"spawnRelay"
|
|
||||||
spawn(name, turn) do (turn: var Turn):
|
spawn(name, turn) do (turn: var Turn):
|
||||||
let relay = newRelay(turn, opts, setup)
|
let relay = Relay(
|
||||||
if not opts.initialRef.isNil:
|
facet: turn.facet,
|
||||||
|
packetWriter: opts.packetWriter,
|
||||||
|
wireBuf: newBufferedDecoder(0),
|
||||||
|
)
|
||||||
|
discard relay.facet.preventInertCheck()
|
||||||
|
if not opts.initialCap.isNil:
|
||||||
var exported: seq[WireSymbol]
|
var exported: seq[WireSymbol]
|
||||||
discard rewriteRefOut(relay, opts.initialRef, exported)
|
discard rewriteCapOut(relay, opts.initialCap, exported)
|
||||||
if opts.initialOid.isSome:
|
|
||||||
var imported: seq[WireSymbol]
|
|
||||||
var wr = WireRef(
|
|
||||||
orKind: WireRefKind.mine,
|
|
||||||
mine: WireRefMine(oid: opts.initialOid.get))
|
|
||||||
fut.complete rewriteRefIn(relay, turn.facet, wr, imported)
|
|
||||||
else:
|
|
||||||
fut.complete(nil)
|
|
||||||
opts.nextLocalOid.map do (oid: Oid):
|
opts.nextLocalOid.map do (oid: Oid):
|
||||||
relay.nextLocalOid =
|
relay.nextLocalOid =
|
||||||
if oid == 0.Oid: 1.Oid
|
if oid == 0.Oid: 1.Oid
|
||||||
else: oid
|
else: oid
|
||||||
fut
|
assert opts.initialOid.isSome
|
||||||
|
if opts.initialOid.isSome:
|
||||||
|
var
|
||||||
|
imported: seq[WireSymbol]
|
||||||
|
wr = WireRef(
|
||||||
|
orKind: WireRefKind.mine,
|
||||||
|
mine: WireRefMine(oid: opts.initialOid.get))
|
||||||
|
relay.peer = rewriteCapIn(relay, turn.facet, wr, imported)
|
||||||
|
assert not relay.peer.isNil
|
||||||
|
setup(turn, relay)
|
||||||
|
|
||||||
|
proc rejected(detail: Value): Resolved =
|
||||||
|
result = Resolved(orKind: ResolvedKind.Rejected)
|
||||||
|
result.rejected.detail = detail
|
||||||
|
|
||||||
|
proc accepted(cap: Cap): Resolved =
|
||||||
|
result = Resolved(orKind: ResolvedKind.accepted)
|
||||||
|
result.accepted.responderSession = cap
|
||||||
|
|
||||||
|
import syndicate/protocols/noise
|
||||||
|
import noiseprotocol
|
||||||
|
|
||||||
|
type
|
||||||
|
NoiseTunnelEntity = ref object of Entity
|
||||||
|
handshake: HandshakeState
|
||||||
|
sendCipher, recvCipher: CipherState
|
||||||
|
peer: Cap
|
||||||
|
relay: Relay
|
||||||
|
action: ACTION
|
||||||
|
|
||||||
|
method message(tunnel: NoiseTunnelEntity; turn: var Turn; v: AssertionRef) =
|
||||||
|
doAssert(v.value.isByteString, $v.value)
|
||||||
|
stderr.writeLine "NoiseTunnelEntity: received message of ", v.value.bytes.len, " bytes"
|
||||||
|
|
||||||
|
proc protocolName(spec: NoiseSpec): string =
|
||||||
|
if spec.protocol.isNone: "Noise_NK_25519_ChaChaPoly_BLAKE2s"
|
||||||
|
else: spec.protocol.get.string
|
||||||
|
|
||||||
|
proc handshake(turn: var Turn; tunnel: NoiseTunnelEntity; spec: NoiseSpec; cont: TurnAction) =
|
||||||
|
stderr.writeLine "handshaking"
|
||||||
|
let handshake = spec.protocolName.newByName(INITIATOR)
|
||||||
|
stderr.writeLine "got handshake"
|
||||||
|
handshake.set_prologue(spec.service.encode)
|
||||||
|
stderr.writeLine "prologue set"
|
||||||
|
if spec.preSharedKeys.isSome:
|
||||||
|
stderr.writeLine "spec.preSharedKeys.isSome:"
|
||||||
|
var keys: seq[seq[byte]]
|
||||||
|
stderr.writeLine("get preSharedKeys")
|
||||||
|
if not keys.fromPreserves(get spec.preSharedKeys):
|
||||||
|
raise newException(ValueError, "invalid preSharedKeys in NoiseSpec")
|
||||||
|
for key in keys:
|
||||||
|
handshake.setPreSharedKey(key)
|
||||||
|
stderr.writeLine("get_local_keypair_dh")
|
||||||
|
let dh = handshake.get_remote_public_key_dh
|
||||||
|
if not dh.isNil:
|
||||||
|
dh.set_public_key(spec.key)
|
||||||
|
stderr.writeLine "start handshake"
|
||||||
|
start(handshake)
|
||||||
|
while true:
|
||||||
|
tunnel.action = get_action(handshake)
|
||||||
|
stderr.writeLine("tunnel.action is ", $tunnel.action)
|
||||||
|
case tunnel.action
|
||||||
|
of SPLIT:
|
||||||
|
(tunnel.sendCipher, tunnel.recvCipher) = split(handshake)
|
||||||
|
destroy(handshake)
|
||||||
|
cont(turn)
|
||||||
|
of WRITE_MESSAGE:
|
||||||
|
var buf = newSeqOfCap[byte](256)
|
||||||
|
write_message(handshake, buf, @[])
|
||||||
|
tunnel.peer.target.message(turn, AssertionRef(value: buf.toPreserves))
|
||||||
|
of READ_MESSAGE:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raiseAssert("action is " & $tunnel.action)
|
||||||
|
|
||||||
|
#[
|
||||||
|
type
|
||||||
|
NoiseTunnel = ref object
|
||||||
|
buffer: seq[byte]
|
||||||
|
handshake: HandshakeState
|
||||||
|
sendCipher, recvCipher: CipherState
|
||||||
|
|
||||||
|
proc noiseSend(state: NoiseTunnel; buf: seq[byte]) =
|
||||||
|
stderr.writeLine("noise send ", buf.len, " bytes")
|
||||||
|
var state = NoiseTunnel(tun)
|
||||||
|
if not state.handshake.isNil:
|
||||||
|
let action = get_action(state.handshake)
|
||||||
|
case action
|
||||||
|
of SPLIT:
|
||||||
|
(state.sendCipher, state.recvCipher) = split(state.handshake)
|
||||||
|
destroy(state.handshake)
|
||||||
|
send(state, buf)
|
||||||
|
of WRITE_MESSAGE:
|
||||||
|
doAssert(buf.len < MAX_PAYLOAD_LEN)
|
||||||
|
state.buffer.setLen(buf.len)
|
||||||
|
write_message(state.handshake, state.buffer, buf)
|
||||||
|
send(state.inner, state.buffer)
|
||||||
|
else:
|
||||||
|
raiseAssert("bad noise handshake state " & $action)
|
||||||
|
else:
|
||||||
|
doAssert(buf.len < MAX_PAYLOAD_LEN)
|
||||||
|
state.buffer.setLen(buf.len)
|
||||||
|
copyMem(state.buffer[0].addr, buf[0].addr, state.buffer.len)
|
||||||
|
encrypt(state.sendCipher, state.buffer)
|
||||||
|
send(state.inner, state.buffer)
|
||||||
|
|
||||||
|
proc noiseRecv(tun: Tunnel; buf: seq[byte]) =
|
||||||
|
stderr.writeLine("noise recv ", buf.len, " bytes")
|
||||||
|
var state = NoiseTunnel(tun)
|
||||||
|
if not state.handshake.isNil:
|
||||||
|
let action = get_action(state.handshake)
|
||||||
|
case action
|
||||||
|
of SPLIT:
|
||||||
|
(state.sendCipher, state.recvCipher) = split(state.handshake)
|
||||||
|
destroy(state.handshake)
|
||||||
|
send(state, buf)
|
||||||
|
of READ_MESSAGE:
|
||||||
|
doAssert(buf.len < MAX_PAYLOAD_LEN)
|
||||||
|
state.buffer.setLen(buf.len)
|
||||||
|
copyMem(state.buffer[0].addr, buf[0].addr, state.buffer.len)
|
||||||
|
read_message(state.handshake, buf, state.buffer)
|
||||||
|
recv(state.outer, state.buffer)
|
||||||
|
else:
|
||||||
|
raiseAssert("bad noise handshake state " & $action)
|
||||||
|
else:
|
||||||
|
doAssert(buf.len < MAX_PAYLOAD_LEN)
|
||||||
|
state.buffer.setLen(buf.len)
|
||||||
|
copyMem(state.buffer[0].addr, buf[0].addr, state.buffer.len)
|
||||||
|
decrypt(state.sendCipher, state.buffer)
|
||||||
|
recv(state.outer, state.buffer)
|
||||||
|
|
||||||
|
proc newNoiseTunnel(facet: Facet; spec: NoiseSpec): NoiseTunnel =
|
||||||
|
noiseprotocol.init()
|
||||||
|
let state = NoiseTunnel(
|
||||||
|
send: noiseSend,
|
||||||
|
recv: noiseRecv,
|
||||||
|
handshake: spec.protocolName.newByName(INITIATOR)
|
||||||
|
)
|
||||||
|
state.handshake.set_prologue(spec.service.encode)
|
||||||
|
if spec.preSharedKeys.isSome:
|
||||||
|
var keys: seq[seq[byte]]
|
||||||
|
if not keys.fromPreserves(get spec.preSharedKeys):
|
||||||
|
raise newException(ValueError, "invalid preSharedKeys in NoiseSpec")
|
||||||
|
for key in keys:
|
||||||
|
state.handshake.setPreSharedKey(key)
|
||||||
|
if spec.key.len > 0: # responder public key
|
||||||
|
state.handshake.get_local_keypair_dh.set_public_key(spec.key)
|
||||||
|
start(state.handshake)
|
||||||
|
]#
|
||||||
|
|
||||||
|
proc spawnNoiseRelay(turn: var Turn; ds, origin: Cap; spec: NoiseSpec) =
|
||||||
|
let tunnel = NoiseTunnelEntity(peer: origin)
|
||||||
|
proc noiseWriter(turn: var Turn; buf: seq[byte]) =
|
||||||
|
stderr.writeLine "NoiseTunnelEntity: need to send ", buf.len, " bytes"
|
||||||
|
var opts = RelayActorOptions(
|
||||||
|
packetWriter: noiseWriter,
|
||||||
|
initialCap: ds,
|
||||||
|
initialOid: 0.Oid.some,
|
||||||
|
)
|
||||||
|
spawnRelay("noise-relay", turn, opts) do (turn: var Turn; relay: Relay):
|
||||||
|
tunnel.relay = relay
|
||||||
|
#[
|
||||||
|
handshake(turn, tunnel, spec) do (turn: var Turn):
|
||||||
|
publish(turn, ds, ResolvedPathStep(
|
||||||
|
origin: origin,
|
||||||
|
pathStep: toRecord(toSymbol"noise", spec),
|
||||||
|
resolved: tunnel.relay.peer.accepted,
|
||||||
|
))
|
||||||
|
]#
|
||||||
|
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
import std/asyncnet
|
|
||||||
from std/nativesockets import AF_INET, AF_UNIX, IPPROTO_TCP, SOCK_STREAM, Protocol
|
|
||||||
|
|
||||||
import protocols/[gatekeeper, sturdy]
|
|
||||||
|
|
||||||
type ShutdownEntity* = ref object of Entity
|
|
||||||
|
|
||||||
method retract(e: ShutdownEntity; turn: var Turn; h: Handle) =
|
|
||||||
stopActor(turn)
|
|
||||||
|
|
||||||
type ConnectProc* = proc (turn: var Turn; ds: Ref) {.gcsafe.}
|
|
||||||
|
|
||||||
export Tcp
|
|
||||||
|
|
||||||
when defined(posix):
|
|
||||||
export Unix
|
|
||||||
|
|
||||||
proc connect*(turn: var Turn; socket: AsyncSocket; step: Preserve[Ref]; bootProc: ConnectProc) =
|
|
||||||
proc socketWriter(packet: sink Packet): Future[void] =
|
|
||||||
socket.send(cast[string](encode(packet)))
|
|
||||||
const recvSize = 0x2000
|
|
||||||
var shutdownRef: Ref
|
|
||||||
let
|
|
||||||
reenable = turn.facet.preventInertCheck()
|
|
||||||
connectionClosedRef = newRef(turn, ShutdownEntity())
|
|
||||||
fut = newFuture[void]"connect"
|
|
||||||
discard bootActor("socket") do (turn: var Turn):
|
|
||||||
var ops = RelayActorOptions(
|
|
||||||
packetWriter: socketWriter,
|
|
||||||
initialOid: 0.Oid.some)
|
|
||||||
let refFut = spawnRelay("socket", turn, ops) do (turn: var Turn; relay: Relay):
|
|
||||||
let facet = turn.facet
|
|
||||||
var wireBuf = newBufferedDecoder(0)
|
|
||||||
proc recvCb(pktFut: Future[string]) {.gcsafe.} =
|
|
||||||
if pktFut.failed:
|
|
||||||
run(facet) do (turn: var Turn): stopActor(turn)
|
|
||||||
else:
|
|
||||||
var buf = pktFut.read
|
|
||||||
if buf.len == 0:
|
|
||||||
run(facet) do (turn: var Turn): stopActor(turn)
|
|
||||||
else:
|
|
||||||
feed(wireBuf, buf)
|
|
||||||
var (success, pr) = decode(wireBuf)
|
|
||||||
if success:
|
|
||||||
dispatch(relay, pr)
|
|
||||||
socket.recv(recvSize).addCallback(recvCb)
|
|
||||||
socket.recv(recvSize).addCallback(recvCb)
|
|
||||||
turn.facet.actor.atExit do (turn: var Turn): close(socket)
|
|
||||||
discard publish(turn, connectionClosedRef, true)
|
|
||||||
shutdownRef = newRef(turn, ShutdownEntity())
|
|
||||||
addCallback(refFut) do ():
|
|
||||||
let gatekeeper = read refFut
|
|
||||||
run(gatekeeper.relay) do (turn: var Turn):
|
|
||||||
reenable()
|
|
||||||
discard publish(turn, shutdownRef, true)
|
|
||||||
proc duringCallback(turn: var Turn; a: Assertion; h: Handle): TurnAction =
|
|
||||||
let facet = inFacet(turn) do (turn: var Turn):
|
|
||||||
var
|
|
||||||
accepted: ResolvedAccepted[Ref]
|
|
||||||
rejected: Rejected[Ref]
|
|
||||||
if fromPreserve(accepted, a):
|
|
||||||
bootProc(turn, accepted.responderSession)
|
|
||||||
elif fromPreserve(rejected, a):
|
|
||||||
fail(fut, newException(CatchableError, $rejected.detail))
|
|
||||||
else:
|
|
||||||
fail(fut, newException(CatchableError, $a))
|
|
||||||
proc action(turn: var Turn) =
|
|
||||||
stop(turn, facet)
|
|
||||||
result = action
|
|
||||||
discard publish(turn, gatekeeper, Resolve[Ref](
|
|
||||||
step: step,
|
|
||||||
observer: newRef(turn, during(duringCallback)),
|
|
||||||
))
|
|
||||||
fut.complete()
|
|
||||||
asyncCheck(turn, fut)
|
|
||||||
|
|
||||||
proc connect*(turn: var Turn; transport: Tcp; step: Preserve[Ref]; bootProc: ConnectProc) =
|
|
||||||
let socket = newAsyncSocket(
|
|
||||||
domain = AF_INET,
|
|
||||||
sockType = SOCK_STREAM,
|
|
||||||
protocol = IPPROTO_TCP,
|
|
||||||
buffered = false)
|
|
||||||
let fut = connect(socket, transport.host, Port transport.port)
|
|
||||||
addCallback(fut, turn) do (turn: var Turn):
|
|
||||||
connect(turn, socket, step, bootProc)
|
|
||||||
|
|
||||||
proc connect*(turn: var Turn; transport: Unix; step: Preserve[Ref]; bootProc: ConnectProc) =
|
|
||||||
let socket = newAsyncSocket(
|
|
||||||
domain = AF_UNIX,
|
|
||||||
sockType = SOCK_STREAM,
|
|
||||||
protocol = cast[Protocol](0),
|
|
||||||
buffered = false)
|
|
||||||
let fut = connectUnix(socket, transport.path)
|
|
||||||
addCallback(fut, turn) do (turn: var Turn):
|
|
||||||
connect(turn, socket, step, bootProc)
|
|
||||||
|
|
||||||
import std/asyncfile
|
import std/asyncfile
|
||||||
|
export Unix
|
||||||
|
|
||||||
const stdinReadSize = 128
|
type StdioControlEntity = ref object of Entity
|
||||||
|
stdin: AsyncFile
|
||||||
|
|
||||||
proc connectStdio*(ds: Ref; turn: var Turn) =
|
method message(entity: StdioControlEntity; turn: var Turn; ass: AssertionRef) =
|
||||||
## Connect to an external dataspace over stdin and stdout.
|
if ass.value.preservesTo(ForceDisconnect).isSome:
|
||||||
proc stdoutWriter(packet: sink Packet): Future[void] {.async.} =
|
close(entity.stdin)
|
||||||
var buf = encode(packet)
|
close(stdout)
|
||||||
doAssert writeBytes(stdout, buf, 0, buf.len) == buf.len
|
|
||||||
|
proc connectTransport(turn: var Turn; ds: Cap; ta: transportAddress.Stdio) =
|
||||||
|
## Connect to an external dataspace over stdio.
|
||||||
|
proc stdoutWriter(turn: var Turn; buf: seq[byte]) =
|
||||||
|
## Blocking write to stdout.
|
||||||
|
let n = writeBytes(stdout, buf, 0, buf.len)
|
||||||
flushFile(stdout)
|
flushFile(stdout)
|
||||||
|
if n != buf.len:
|
||||||
|
stopActor(turn)
|
||||||
var opts = RelayActorOptions(
|
var opts = RelayActorOptions(
|
||||||
packetWriter: stdoutWriter,
|
packetWriter: stdoutWriter,
|
||||||
initialRef: ds,
|
initialCap: ds,
|
||||||
initialOid: 0.Oid.some)
|
initialOid: 0.Oid.some,
|
||||||
asyncCheck spawnRelay("stdio", turn, opts) do (turn: var Turn; relay: Relay):
|
)
|
||||||
|
spawnRelay("stdio", turn, opts) do (turn: var Turn; relay: Relay):
|
||||||
let
|
let
|
||||||
facet = turn.facet
|
facet = turn.facet
|
||||||
asyncStdin = openAsync("/dev/stdin") # this is universal now?
|
asyncStdin = openAsync("/dev/stdin") # this is universal now?
|
||||||
close(stdin)
|
publish(turn, ds, TransportConnection(
|
||||||
facet.actor.atExit do (turn: var Turn):
|
`addr`: ta.toPreserves,
|
||||||
close(asyncStdin)
|
control: StdioControlEntity(stdin: asyncStdin).newCap(turn),
|
||||||
var wireBuf = newBufferedDecoder()
|
resolved: relay.peer.accepted,
|
||||||
|
))
|
||||||
|
const stdinReadSize = 0x2000
|
||||||
proc readCb(pktFut: Future[string]) {.gcsafe.} =
|
proc readCb(pktFut: Future[string]) {.gcsafe.} =
|
||||||
if not pktFut.failed:
|
if not pktFut.failed:
|
||||||
var buf = pktFut.read
|
var buf = pktFut.read
|
||||||
if buf.len == 0:
|
if buf.len == 0:
|
||||||
run(facet) do (turn: var Turn): stopActor(turn)
|
run(facet) do (turn: var Turn): stopActor(turn)
|
||||||
else:
|
else:
|
||||||
feed(wireBuf, buf)
|
relay.recv(cast[seq[byte]](buf))
|
||||||
var (success, pr) = decode(wireBuf)
|
|
||||||
if success:
|
|
||||||
dispatch(relay, pr)
|
|
||||||
asyncStdin.read(stdinReadSize).addCallback(readCb)
|
asyncStdin.read(stdinReadSize).addCallback(readCb)
|
||||||
asyncStdin.read(stdinReadSize).addCallback(readCb)
|
asyncStdin.read(stdinReadSize).addCallback(readCb)
|
||||||
|
|
||||||
|
proc connectStdio*(turn: var Turn; ds: Cap) =
|
||||||
|
## Connect to an external dataspace over stdin and stdout.
|
||||||
|
connectTransport(turn, ds, transportAddress.Stdio())
|
||||||
|
|
||||||
|
import std/asyncnet
|
||||||
|
from std/nativesockets import AF_INET, AF_UNIX, IPPROTO_TCP, SOCK_STREAM, Protocol
|
||||||
|
|
||||||
|
type SocketControlEntity = ref object of Entity
|
||||||
|
socket: AsyncSocket
|
||||||
|
|
||||||
|
method message(entity: SocketControlEntity; turn: var Turn; ass: AssertionRef) =
|
||||||
|
if ass.value.preservesTo(ForceDisconnect).isSome:
|
||||||
|
close(entity.socket)
|
||||||
|
|
||||||
|
type ShutdownEntity* = ref object of Entity
|
||||||
|
method retract(e: ShutdownEntity; turn: var Turn; h: Handle) =
|
||||||
|
stopActor(turn)
|
||||||
|
|
||||||
|
proc connect(turn: var Turn; ds: Cap; transAddr: Value; socket: AsyncSocket) =
|
||||||
|
proc socketWriter(turn: var Turn; buf: seq[byte]) =
|
||||||
|
asyncCheck(turn, socket.send(cast[string](buf)))
|
||||||
|
var ops = RelayActorOptions(
|
||||||
|
packetWriter: socketWriter,
|
||||||
|
initialOid: 0.Oid.some,
|
||||||
|
)
|
||||||
|
spawnRelay("socket", turn, ops) do (turn: var Turn; relay: Relay):
|
||||||
|
let facet = turn.facet
|
||||||
|
facet.actor.atExit do (turn: var Turn): close(socket)
|
||||||
|
publish(turn, ds, TransportConnection(
|
||||||
|
`addr`: transAddr,
|
||||||
|
control: SocketControlEntity(socket: socket).newCap(turn),
|
||||||
|
resolved: relay.peer.accepted,
|
||||||
|
))
|
||||||
|
const recvSize = 0x4000
|
||||||
|
proc recvCb(pktFut: Future[string]) {.gcsafe.} =
|
||||||
|
if pktFut.failed or pktFut.read.len == 0:
|
||||||
|
run(facet) do (turn: var Turn): stopActor(turn)
|
||||||
|
else:
|
||||||
|
relay.recv(cast[seq[byte]](pktFut.read))
|
||||||
|
if not socket.isClosed:
|
||||||
|
socket.recv(recvSize).addCallback(recvCb)
|
||||||
|
socket.recv(recvSize).addCallback(recvCb)
|
||||||
|
|
||||||
|
proc connect(turn: var Turn; ds: Cap; ta: Value; socket: AsyncSocket; fut: Future[void]) =
|
||||||
|
let facet = turn.facet
|
||||||
|
fut.addCallback do ():
|
||||||
|
run(facet) do (turn: var Turn):
|
||||||
|
if fut.failed:
|
||||||
|
var ass = TransportConnection(
|
||||||
|
`addr`: ta,
|
||||||
|
resolved: Resolved(orKind: ResolvedKind.Rejected),
|
||||||
|
)
|
||||||
|
ass.resolved.rejected.detail = embed fut.error
|
||||||
|
publish(turn, ds, ass)
|
||||||
|
else:
|
||||||
|
connect(turn, ds, ta, socket)
|
||||||
|
|
||||||
|
proc connectTransport(turn: var Turn; ds: Cap; ta: transportAddress.Tcp) =
|
||||||
|
let
|
||||||
|
facet = turn.facet
|
||||||
|
socket = newAsyncSocket(
|
||||||
|
domain = AF_INET,
|
||||||
|
sockType = SOCK_STREAM,
|
||||||
|
protocol = IPPROTO_TCP,
|
||||||
|
buffered = false,
|
||||||
|
)
|
||||||
|
connect(turn, ds, ta.toPreserves, socket, connect(socket, ta.host, Port ta.port))
|
||||||
|
|
||||||
|
proc connectTransport(turn: var Turn; ds: Cap; ta: transportAddress.Unix) =
|
||||||
|
## Relay a dataspace over a UNIX socket.
|
||||||
|
let socket = newAsyncSocket(
|
||||||
|
domain = AF_UNIX,
|
||||||
|
sockType = SOCK_STREAM,
|
||||||
|
protocol = cast[Protocol](0),
|
||||||
|
buffered = false)
|
||||||
|
connect(turn, ds, ta.toPreserves, socket, connectUnix(socket, ta.path))
|
||||||
|
|
||||||
|
proc walk(turn: var Turn; ds, origin: Cap; route: Route; transOff, stepOff: int) {.gcsafe.} =
|
||||||
|
if stepOff < route.pathSteps.len:
|
||||||
|
let
|
||||||
|
step = route.pathSteps[stepOff]
|
||||||
|
rejectPat = ResolvedPathStep?:{
|
||||||
|
0: ?(origin.embed), 1: ?step, 2: ?:Rejected}
|
||||||
|
acceptPat = ResolvedPathStep?:{
|
||||||
|
0: ?(origin.embed), 1: ?step, 2: ?:ResolvedAccepted}
|
||||||
|
onPublish(turn, ds, rejectPat) do (detail: Value):
|
||||||
|
publish(turn, ds, ResolvePath(
|
||||||
|
route: route,
|
||||||
|
`addr`: route.transports[transOff],
|
||||||
|
resolved: detail.rejected,
|
||||||
|
))
|
||||||
|
during(turn, ds, acceptPat) do (next: Cap):
|
||||||
|
walk(turn, ds, next, route, transOff, stepOff.succ)
|
||||||
|
else:
|
||||||
|
publish(turn, ds, ResolvePath(
|
||||||
|
route: route,
|
||||||
|
`addr`: route.transports[transOff],
|
||||||
|
resolved: origin.accepted,
|
||||||
|
))
|
||||||
|
|
||||||
|
proc connectRoute(turn: var Turn; ds: Cap; route: Route; transOff: int) =
|
||||||
|
let rejectPat = TransportConnection ?: {
|
||||||
|
0: ?route.transports[transOff],
|
||||||
|
2: ?:Rejected,
|
||||||
|
}
|
||||||
|
during(turn, ds, rejectPat) do (detail: Value):
|
||||||
|
publish(turn, ds, ResolvePath(
|
||||||
|
route: route,
|
||||||
|
`addr`: route.transports[transOff],
|
||||||
|
resolved: detail.rejected,
|
||||||
|
))
|
||||||
|
let acceptPat = TransportConnection?:{
|
||||||
|
0: ?route.transports[transOff],
|
||||||
|
2: ?:ResolvedAccepted,
|
||||||
|
}
|
||||||
|
onPublish(turn, ds, acceptPat) do (origin: Cap):
|
||||||
|
walk(turn, ds, origin, route, transOff, 0)
|
||||||
|
|
||||||
|
proc spawnRelays*(turn: var Turn; ds: Cap) =
|
||||||
|
## Spawn actors that manage routes and appeasing gatekeepers.
|
||||||
|
spawn("transport-connector", turn) do (turn: var Turn):
|
||||||
|
let pat = ?Observe(pattern: !TransportConnection) ?? { 0: grab() }
|
||||||
|
# Use a generic pattern and type matching
|
||||||
|
# in the during handler because it is easy.
|
||||||
|
|
||||||
|
let stdioPat = ?Observe(pattern: TransportConnection?:{0: ?:Stdio})
|
||||||
|
during(turn, ds, stdioPat) do:
|
||||||
|
connectTransport(turn, ds, Stdio())
|
||||||
|
|
||||||
|
# TODO: tcp pattern
|
||||||
|
during(turn, ds, pat) do (ta: Literal[transportAddress.Tcp]):
|
||||||
|
connectTransport(turn, ds, ta.value)
|
||||||
|
|
||||||
|
# TODO: unix pattern
|
||||||
|
during(turn, ds, pat) do (ta: Literal[transportAddress.Unix]):
|
||||||
|
connectTransport(turn, ds, ta.value)
|
||||||
|
|
||||||
|
spawn("path-resolver", turn) do (turn: var Turn):
|
||||||
|
let pat = ?Observe(pattern: !ResolvePath) ?? {0: grab()}
|
||||||
|
during(turn, ds, pat) do (route: Literal[Route]):
|
||||||
|
for i, transAddr in route.value.transports:
|
||||||
|
connectRoute(turn, ds, route.value, i)
|
||||||
|
|
||||||
|
spawn("sturdyref-step", turn) do (turn: var Turn):
|
||||||
|
let pat = ?Observe(pattern: ResolvedPathStep?:{1: !SturdyRef}) ?? {0: grab(), 1: grab()}
|
||||||
|
during(turn, ds, pat) do (origin: Literal[Cap]; detail: Literal[sturdy.Parameters]):
|
||||||
|
let step = SturdyRef(parameters: detail.value).toPreserves
|
||||||
|
proc duringCallback(turn: var Turn; ass: Assertion; h: Handle): TurnAction =
|
||||||
|
let facet = inFacet(turn) do (turn: var Turn):
|
||||||
|
var res = ass.preservesTo Resolved
|
||||||
|
if res.isSome:
|
||||||
|
publish(turn, ds, ResolvedPathStep(
|
||||||
|
origin: origin.value,
|
||||||
|
pathStep: step,
|
||||||
|
resolved: res.get,
|
||||||
|
))
|
||||||
|
proc action(turn: var Turn) =
|
||||||
|
stop(turn, facet)
|
||||||
|
result = action
|
||||||
|
publish(turn, origin.value, Resolve(
|
||||||
|
step: step,
|
||||||
|
observer: newCap(turn, during(duringCallback)),
|
||||||
|
))
|
||||||
|
|
||||||
|
spawn("noise-step", turn) do (turn: var Turn):
|
||||||
|
let
|
||||||
|
stepPat = grabRecord(toSymbol"noise", grab())
|
||||||
|
pat = ?Observe(pattern: ResolvedPathStep?:{1: stepPat}) ?? {0: grab(), 1: grab()}
|
||||||
|
during(turn, ds, pat) do (origin: Literal[Cap]; detail: Literal[NoiseSpec]):
|
||||||
|
let step = toRecord(Symbol"noise", detail.value)
|
||||||
|
proc duringCallback(turn: var Turn; ass: Assertion; h: Handle): TurnAction =
|
||||||
|
# TODO: better during thing
|
||||||
|
let facet = inFacet(turn) do (turn: var Turn):
|
||||||
|
var res = ass.preservesTo Resolved
|
||||||
|
if res.isSome and res.get.orKind == ResolvedKind.accepted:
|
||||||
|
if res.get.accepted.responderSession of Cap:
|
||||||
|
spawnNoiseRelay(turn, ds, res.get.accepted.responderSession.Cap, detail.value)
|
||||||
|
proc action(turn: var Turn) =
|
||||||
|
stop(turn, facet)
|
||||||
|
result = action
|
||||||
|
publish(turn, origin.value, Resolve(
|
||||||
|
step: step,
|
||||||
|
observer: newCap(turn, during(duringCallback)),
|
||||||
|
))
|
||||||
|
|
||||||
|
type BootProc* = proc (turn: var Turn; ds: Cap) {.gcsafe.}
|
||||||
|
|
||||||
|
proc envRoute*: Route =
|
||||||
|
var text = getEnv("SYNDICATE_ROUTE")
|
||||||
|
if text == "":
|
||||||
|
var tx = (getEnv("XDG_RUNTIME_DIR", "/run/user/1000") / "dataspace").toPreserves
|
||||||
|
result.transports = @[initRecord("unix", tx)]
|
||||||
|
result.pathSteps = @[capabilities.mint().toPreserves]
|
||||||
|
else:
|
||||||
|
var pr = parsePreserves(text)
|
||||||
|
if not result.fromPreserves(pr):
|
||||||
|
raise newException(ValueError, "failed to parse $SYNDICATE_ROUTE " & $pr)
|
||||||
|
|
||||||
|
proc resolve*(turn: var Turn; ds: Cap; route: Route; bootProc: BootProc) =
|
||||||
|
during(turn, ds, ResolvePath ?: {0: ?route, 3: ?:ResolvedAccepted}) do (dst: Cap):
|
||||||
|
bootProc(turn, dst)
|
||||||
|
|
||||||
|
# TODO: define a runActor that comes preloaded with relaying
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[hashes, lists, options, sets, tables]
|
## https://git.syndicate-lang.org/syndicate-lang/syndicate-rkt/src/commit/90c4c60699069b496491b81ee63b5a45ffd638cb/syndicate/HOWITWORKS.md
|
||||||
|
|
||||||
|
import std/[hashes, options, sets, tables]
|
||||||
import preserves
|
import preserves
|
||||||
import ./actors, ./bags, ./patterns
|
import ./actors, ./bags, ./patterns
|
||||||
import ./protocols/dataspacePatterns
|
import ./protocols/dataspacePatterns
|
||||||
|
|
||||||
type
|
type
|
||||||
DCompound = dataspacePatterns.DCompound[Ref]
|
DCompound = dataspacePatterns.DCompound
|
||||||
Pattern = dataspacePatterns.Pattern[Ref]
|
Pattern = dataspacePatterns.Pattern
|
||||||
Value = Preserve[Ref]
|
|
||||||
Path = seq[Value]
|
Path = seq[Value]
|
||||||
ClassKind = enum classNone, classRecord, classSequence, classDictionary
|
ClassKind = enum classNone, classRecord, classSequence, classDictionary
|
||||||
Class = object
|
Class = object
|
||||||
|
@ -43,71 +44,106 @@ type
|
||||||
|
|
||||||
AssertionCache = HashSet[Value]
|
AssertionCache = HashSet[Value]
|
||||||
|
|
||||||
Paths = seq[Path]
|
|
||||||
|
|
||||||
ObserverGroup = ref object # Endpoints
|
ObserverGroup = ref object # Endpoints
|
||||||
cachedCaptures: Bag[seq[Value]]
|
cachedCaptures: Bag[Captures]
|
||||||
observers: Table[Ref, TableRef[seq[Value], Handle]]
|
observers: Table[Cap, TableRef[Captures, Handle]]
|
||||||
|
|
||||||
Leaf = ref object
|
Leaf = ref object
|
||||||
cachedAssertions: AssertionCache
|
cache: AssertionCache
|
||||||
observerGroups: Table[Paths, ObserverGroup]
|
observerGroups: Table[Paths, ObserverGroup]
|
||||||
|
|
||||||
|
LeafMap = TableRef[seq[Value], Leaf]
|
||||||
|
|
||||||
Continuation = ref object
|
Continuation = ref object
|
||||||
cachedAssertions: AssertionCache
|
cache: AssertionCache
|
||||||
leafMap: Table[Paths, TableRef[seq[Value], Leaf]]
|
leafMap: Table[Paths, LeafMap]
|
||||||
|
|
||||||
Selector = tuple[popCount: int; index: Value]
|
|
||||||
|
|
||||||
Node = ref object
|
|
||||||
edges: Table[Selector, TableRef[Class, Node]]
|
|
||||||
continuation: Continuation
|
|
||||||
|
|
||||||
func isEmpty(leaf: Leaf): bool =
|
func isEmpty(leaf: Leaf): bool =
|
||||||
leaf.cachedAssertions.len == 0 and leaf.observerGroups.len == 0
|
leaf.cache.len == 0 and leaf.observerGroups.len == 0
|
||||||
|
|
||||||
|
func isEmpty(cont: Continuation): bool =
|
||||||
|
cont.cache.len == 0 and cont.leafMap.len == 0
|
||||||
|
|
||||||
type
|
type
|
||||||
ContinuationProc = proc (c: Continuation; v: Value) {.gcsafe.}
|
ContinuationProc = proc (c: Continuation; v: Value) {.gcsafe.}
|
||||||
LeafProc = proc (l: Leaf; v: Value) {.gcsafe.}
|
LeafProc = proc (l: Leaf; v: Value) {.gcsafe.}
|
||||||
ObserverProc = proc (turn: var Turn; group: ObserverGroup; vs: seq[Value]) {.gcsafe.}
|
ObserverProc = proc (turn: var Turn; group: ObserverGroup; vs: seq[Value]) {.gcsafe.}
|
||||||
|
|
||||||
type TermStack = SinglyLinkedList[Value]
|
proc getLeaves(cont: Continuation; constPaths: Paths): LeafMap =
|
||||||
|
result = cont.leafMap.getOrDefault(constPaths)
|
||||||
|
if result.isNil:
|
||||||
|
new result
|
||||||
|
cont.leafMap[constPaths] = result
|
||||||
|
assert not cont.isEmpty
|
||||||
|
for ass in cont.cache:
|
||||||
|
let key = projectPaths(ass, constPaths)
|
||||||
|
if key.isSome:
|
||||||
|
var leaf = result.getOrDefault(get key)
|
||||||
|
if leaf.isNil:
|
||||||
|
new leaf
|
||||||
|
result[get key] = leaf
|
||||||
|
leaf.cache.incl(ass)
|
||||||
|
|
||||||
|
proc getLeaf(leafMap: LeafMap; constVals: seq[Value]): Leaf =
|
||||||
|
result = leafMap.getOrDefault(constVals)
|
||||||
|
if result.isNil:
|
||||||
|
new result
|
||||||
|
leafMap[constVals] = result
|
||||||
|
|
||||||
|
type
|
||||||
|
Selector = tuple[popCount: int; index: Value]
|
||||||
|
|
||||||
|
Node = ref object
|
||||||
|
continuation: Continuation
|
||||||
|
edges: Table[Selector, TableRef[Class, Node]]
|
||||||
|
|
||||||
|
func isEmpty(node: Node): bool =
|
||||||
|
node.continuation.isEmpty and node.edges.len == 0
|
||||||
|
|
||||||
|
type TermStack = seq[Value]
|
||||||
|
|
||||||
proc push(stack: TermStack; val: Value): Termstack =
|
proc push(stack: TermStack; val: Value): Termstack =
|
||||||
result = stack
|
result = stack
|
||||||
prepend(result, val)
|
add(result, val)
|
||||||
|
|
||||||
proc pop(stack: TermStack; n: int): TermStack =
|
proc pop(stack: TermStack; n: int): TermStack =
|
||||||
result = stack
|
assert n <= stack.len
|
||||||
var n = n
|
stack[stack.low..(stack.high-n)]
|
||||||
while n > 0:
|
|
||||||
result.remove(result.head)
|
|
||||||
assert not stack.head.isNil, "popped too far"
|
|
||||||
dec n
|
|
||||||
|
|
||||||
proc top(stack: TermStack): Value =
|
proc top(stack: TermStack): Value =
|
||||||
assert not stack.head.isNil, "stack is empty"
|
assert stack.len > 0
|
||||||
stack.head.value
|
stack[stack.high]
|
||||||
|
|
||||||
proc modify(node: Node; turn: var Turn; outerValue: Value; event: EventKind;
|
proc modify(node: Node; turn: var Turn; outerValue: Value; event: EventKind;
|
||||||
modCont: ContinuationProc; modLeaf: LeafProc; modObs: ObserverProc) =
|
modCont: ContinuationProc; modLeaf: LeafProc; modObs: ObserverProc) =
|
||||||
|
|
||||||
proc walk(turn: var Turn; cont: Continuation; outerValue: Value; event: EventKind) =
|
proc walk(cont: Continuation; turn: var Turn) =
|
||||||
modCont(cont, outerValue)
|
modCont(cont, outerValue)
|
||||||
for constPaths, constValMap in cont.leafMap.pairs:
|
for constPaths, constValMap in cont.leafMap.pairs:
|
||||||
let constVals = projectPaths(outerValue, constPaths)
|
let constVals = projectPaths(outerValue, constPaths)
|
||||||
var leaf = constValMap.getOrDefault(constVals)
|
if constVals.isSome:
|
||||||
if leaf.isNil and event == addedEvent:
|
case event
|
||||||
new leaf
|
of addedEvent, messageEvent:
|
||||||
constValMap[constVals] = leaf
|
let leaf = constValMap.getLeaf(get constVals)
|
||||||
if not leaf.isNil:
|
modLeaf(leaf, outerValue)
|
||||||
modLeaf(leaf, outerValue)
|
for capturePaths, observerGroup in leaf.observerGroups.pairs:
|
||||||
for capturePaths, observerGroup in leaf.observerGroups.pairs:
|
let captures = projectPaths(outerValue, capturePaths)
|
||||||
modObs(turn, observerGroup, projectPaths(outerValue, capturePaths))
|
if captures.isSome:
|
||||||
# TODO: cleanup dead leaves
|
modObs(turn, observerGroup, get captures)
|
||||||
|
of removedEvent:
|
||||||
|
let leaf = constValMap.getOrDefault(get constVals)
|
||||||
|
if not leaf.isNil:
|
||||||
|
modLeaf(leaf, outerValue)
|
||||||
|
for capturePaths, observerGroup in leaf.observerGroups.pairs:
|
||||||
|
let captures = projectPaths(outerValue, capturePaths)
|
||||||
|
if captures.isSome:
|
||||||
|
modObs(turn, observerGroup, get captures)
|
||||||
|
if leaf.isEmpty:
|
||||||
|
constValMap.del(get constVals)
|
||||||
|
|
||||||
proc walk(node: Node; turn: var Turn; outerValue: Value; event: EventKind; termStack: TermStack) =
|
|
||||||
walk(turn, node.continuation, outerValue, event)
|
proc walk(node: Node; turn: var Turn; termStack: TermStack) =
|
||||||
|
walk(node.continuation, turn)
|
||||||
for selector, table in node.edges:
|
for selector, table in node.edges:
|
||||||
let
|
let
|
||||||
nextStack = pop(termStack, selector.popCount)
|
nextStack = pop(termStack, selector.popCount)
|
||||||
|
@ -117,10 +153,11 @@ proc modify(node: Node; turn: var Turn; outerValue: Value; event: EventKind;
|
||||||
if nextClass.kind != classNone:
|
if nextClass.kind != classNone:
|
||||||
let nextNode = table.getOrDefault(nextClass)
|
let nextNode = table.getOrDefault(nextClass)
|
||||||
if not nextNode.isNil:
|
if not nextNode.isNil:
|
||||||
walk(nextNode, turn, outerValue, event, push(nextStack, get nextValue))
|
walk(nextNode, turn, push(nextStack, get nextValue))
|
||||||
|
if event == removedEvent and nextNode.isEmpty:
|
||||||
|
table.del(nextClass)
|
||||||
|
|
||||||
var stack: TermStack
|
walk(node, turn, @[@[outerValue].toPreserves])
|
||||||
walk(node, turn, outerValue, event, push(stack, @[outerValue].toPreserve(Ref)))
|
|
||||||
|
|
||||||
proc getOrNew[A, B, C](t: var Table[A, TableRef[B, C]], k: A): TableRef[B, C] =
|
proc getOrNew[A, B, C](t: var Table[A, TableRef[B, C]], k: A): TableRef[B, C] =
|
||||||
result = t.getOrDefault(k)
|
result = t.getOrDefault(k)
|
||||||
|
@ -132,10 +169,10 @@ iterator pairs(dc: DCompound): (Value, Pattern) =
|
||||||
case dc.orKind
|
case dc.orKind
|
||||||
of DCompoundKind.rec:
|
of DCompoundKind.rec:
|
||||||
for i, p in dc.rec.fields:
|
for i, p in dc.rec.fields:
|
||||||
yield (toPreserve(i, Ref), p,)
|
yield (i.toPreserves, p,)
|
||||||
of DCompoundKind.arr:
|
of DCompoundKind.arr:
|
||||||
for i, p in dc.arr.items:
|
for i, p in dc.arr.items:
|
||||||
yield (toPreserve(i, Ref), p,)
|
yield (i.toPreserves, p,)
|
||||||
of DCompoundKind.dict:
|
of DCompoundKind.dict:
|
||||||
for pair in dc.dict.entries.pairs:
|
for pair in dc.dict.entries.pairs:
|
||||||
yield pair
|
yield pair
|
||||||
|
@ -148,123 +185,117 @@ proc extendWalk(node: Node; popCount: Natural; stepIndex: Value; pat: Pattern; p
|
||||||
result = extendWalk(node, popCount, stepIndex, pat.dbind.pattern, path)
|
result = extendWalk(node, popCount, stepIndex, pat.dbind.pattern, path)
|
||||||
of PatternKind.DCompound:
|
of PatternKind.DCompound:
|
||||||
let
|
let
|
||||||
class = classOf pat
|
|
||||||
selector: Selector = (popCount, stepIndex,)
|
selector: Selector = (popCount, stepIndex,)
|
||||||
table = node.edges.getOrNew(selector)
|
table = node.edges.getOrNew(selector)
|
||||||
|
class = classOf pat
|
||||||
result.nextNode = table.getOrDefault(class)
|
result.nextNode = table.getOrDefault(class)
|
||||||
if result.nextNode.isNil:
|
if result.nextNode.isNil:
|
||||||
new result.nextNode
|
new result.nextNode
|
||||||
table[class] = result.nextNode
|
table[class] = result.nextNode
|
||||||
new result.nextNode.continuation
|
new result.nextNode.continuation
|
||||||
for a in node.continuation.cachedAssertions:
|
for a in node.continuation.cache:
|
||||||
var v = projectPath(a, path)
|
var v = step(a, path)
|
||||||
if v.isSome and class == classOf(get v):
|
if v.isSome and class == classOf(get v):
|
||||||
result.nextNode.continuation.cachedAssertions.incl a
|
result.nextNode.continuation.cache.incl a
|
||||||
for i, p in pat.dcompound.pairs:
|
result.popCount = 0
|
||||||
add(path, i)
|
for step, p in pat.dcompound.pairs:
|
||||||
result = extendWalk(result.nextNode, result.popCount, i, p, path)
|
add(path, step)
|
||||||
|
result = extendWalk(result.nextNode, result.popCount, step, p, path)
|
||||||
discard pop(path)
|
discard pop(path)
|
||||||
inc(result.popCount)
|
inc(result.popCount)
|
||||||
|
|
||||||
proc extend(node: var Node; pat: Pattern): Continuation =
|
proc extend(node: var Node; pat: Pattern): Continuation =
|
||||||
var path: Path
|
var path: Path
|
||||||
extendWalk(node, 0, toPreserve(0, Ref), pat, path).nextNode.continuation
|
extendWalk(node, 0, 0.toPreserves, pat, path).nextNode.continuation
|
||||||
|
|
||||||
type
|
type
|
||||||
Index* = object
|
Index* = object
|
||||||
allAssertions: Bag[Value]
|
allAssertions: Bag[Value]
|
||||||
root: Node
|
root: Node
|
||||||
observerCount: int
|
|
||||||
|
|
||||||
proc initIndex*(): Index =
|
proc initIndex*(): Index =
|
||||||
Index(root: Node(continuation: Continuation()))
|
Index(root: Node(continuation: Continuation()))
|
||||||
|
|
||||||
proc add*(index: var Index; turn: var Turn; pattern: Pattern; observer: Ref) =
|
proc getEndpoints(leaf: Leaf; capturePaths: Paths): ObserverGroup =
|
||||||
let
|
result = leaf.observerGroups.getOrDefault(capturePaths)
|
||||||
analysis = analyse pattern
|
if result.isNil:
|
||||||
continuation = index.root.extend pattern
|
new result
|
||||||
var constValMap = continuation.leafMap.getOrDefault(analysis.constPaths)
|
leaf.observerGroups[capturePaths] = result
|
||||||
if constValMap.isNil:
|
for term in leaf.cache:
|
||||||
new constValMap
|
# leaf.cache would be empty if observers come before assertions
|
||||||
for a in continuation.cachedAssertions:
|
let captures = projectPaths(term, capturePaths)
|
||||||
let key = projectPaths(a, analysis.constPaths)
|
if captures.isSome:
|
||||||
var leaf = constValMap.getOrDefault(key)
|
discard result.cachedCaptures.change(get captures, +1)
|
||||||
if leaf.isNil:
|
|
||||||
new leaf
|
|
||||||
constValMap[key] = leaf
|
|
||||||
leaf.cachedAssertions.incl(a)
|
|
||||||
continuation.leafMap[analysis.constPaths] = constValMap
|
|
||||||
var leaf = constValMap.getOrDefault(analysis.constValues)
|
|
||||||
if leaf.isNil:
|
|
||||||
new leaf
|
|
||||||
constValMap[analysis.constValues] = leaf
|
|
||||||
var observerGroup = leaf.observerGroups.getOrDefault(analysis.capturePaths)
|
|
||||||
if observerGroup.isNil:
|
|
||||||
new observerGroup
|
|
||||||
for a in leaf.cachedAssertions:
|
|
||||||
discard observerGroup.cachedCaptures.change(projectPaths(a, analysis.capturePaths), +1)
|
|
||||||
leaf.observerGroups[analysis.capturePaths] = observerGroup
|
|
||||||
var captureMap = newTable[seq[Value], Handle]()
|
|
||||||
for (count, captures) in observerGroup.cachedCaptures:
|
|
||||||
captureMap[captures] = publish(turn, observer, captures)
|
|
||||||
observerGroup.observers[observer] = captureMap
|
|
||||||
|
|
||||||
proc remove*(index: var Index; turn: var Turn; pattern: Pattern; observer: Ref) =
|
proc add*(index: var Index; turn: var Turn; pattern: Pattern; observer: Cap) =
|
||||||
var
|
let
|
||||||
|
cont = index.root.extend(pattern)
|
||||||
analysis = analyse pattern
|
analysis = analyse pattern
|
||||||
continuation = index.root.extend pattern
|
constValMap = cont.getLeaves(analysis.constPaths)
|
||||||
let constValMap = continuation.leafMap.getOrDefault(analysis.constPaths)
|
leaf = constValMap.getLeaf(analysis.constValues)
|
||||||
|
endpoints = leaf.getEndpoints(analysis.capturePaths)
|
||||||
|
# TODO if endpoints.cachedCaptures.len > 0:
|
||||||
|
var captureMap = newTable[seq[Value], Handle]()
|
||||||
|
for capture in endpoints.cachedCaptures.items:
|
||||||
|
captureMap[capture] = publish(turn, observer, capture)
|
||||||
|
endpoints.observers[observer] = captureMap
|
||||||
|
|
||||||
|
proc remove*(index: var Index; turn: var Turn; pattern: Pattern; observer: Cap) =
|
||||||
|
let
|
||||||
|
cont = index.root.extend(pattern)
|
||||||
|
analysis = analyse pattern
|
||||||
|
constValMap = cont.leafMap.getOrDefault(analysis.constPaths)
|
||||||
if not constValMap.isNil:
|
if not constValMap.isNil:
|
||||||
let leaf = constValMap.getOrDefault(analysis.constValues)
|
let leaf = constValMap.getOrDefault(analysis.constValues)
|
||||||
if not leaf.isNil:
|
if not leaf.isNil:
|
||||||
let observerGroup = leaf.observerGroups.getOrDefault(analysis.capturePaths)
|
let endpoints = leaf.observerGroups.getOrDefault(analysis.capturePaths)
|
||||||
if not observerGroup.isNil:
|
if not endpoints.isNil:
|
||||||
let captureMap = observerGroup.observers.getOrDefault(observer)
|
var captureMap: TableRef[seq[Value], Handle]
|
||||||
if not captureMap.isNil:
|
if endpoints.observers.pop(observer, captureMap):
|
||||||
for handle in captureMap.values: retract(observer.target, turn, handle)
|
for handle in captureMap.values: retract(turn, handle)
|
||||||
observerGroup.observers.del(observer)
|
if endpoints.observers.len == 0:
|
||||||
if observerGroup.observers.len == 0:
|
|
||||||
leaf.observerGroups.del(analysis.capturePaths)
|
leaf.observerGroups.del(analysis.capturePaths)
|
||||||
if leaf.isEmpty:
|
if leaf.observerGroups.len == 0:
|
||||||
constValMap.del(analysis.constValues)
|
constValMap.del(analysis.constValues)
|
||||||
if constValMap.len == 0:
|
if constValMap.len == 0:
|
||||||
continuation.leafMap.del(analysis.constPaths)
|
cont.leafMap.del(analysis.constPaths)
|
||||||
|
|
||||||
proc adjustAssertion*(index: var Index; turn: var Turn; outerValue: Value; delta: int): bool =
|
proc adjustAssertion(index: var Index; turn: var Turn; outerValue: Value; delta: int): bool =
|
||||||
case index.allAssertions.change(outerValue, delta)
|
case index.allAssertions.change(outerValue, delta)
|
||||||
of cdAbsentToPresent:
|
of cdAbsentToPresent:
|
||||||
result = true
|
result = true
|
||||||
proc modContinuation(c: Continuation; v: Value) =
|
proc modContinuation(c: Continuation; v: Value) =
|
||||||
c.cachedAssertions.incl(v)
|
c.cache.incl(v)
|
||||||
proc modLeaf(l: Leaf; v: Value) =
|
proc modLeaf(l: Leaf; v: Value) =
|
||||||
l.cachedAssertions.incl(v)
|
l.cache.incl(v)
|
||||||
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
|
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
|
||||||
if group.cachedCaptures.change(vs, +1) == cdAbsentToPresent:
|
let change = group.cachedCaptures.change(vs, +1)
|
||||||
|
if change == cdAbsentToPresent:
|
||||||
for (observer, captureMap) in group.observers.pairs:
|
for (observer, captureMap) in group.observers.pairs:
|
||||||
let a = vs.toPreserve(Ref)
|
captureMap[vs] = publish(turn, observer, vs.toPreserves)
|
||||||
captureMap[vs] = publish(turn, observer, a)
|
|
||||||
# TODO: this handle is coming from the facet?
|
# TODO: this handle is coming from the facet?
|
||||||
modify(index.root, turn, outerValue, addedEvent, modContinuation, modLeaf, modObserver)
|
modify(index.root, turn, outerValue, addedEvent, modContinuation, modLeaf, modObserver)
|
||||||
of cdPresentToAbsent:
|
of cdPresentToAbsent:
|
||||||
result = true
|
result = true
|
||||||
proc modContinuation(c: Continuation; v: Value) =
|
proc modContinuation(c: Continuation; v: Value) =
|
||||||
c.cachedAssertions.excl(v)
|
c.cache.excl(v)
|
||||||
proc modLeaf(l: Leaf; v: Value) =
|
proc modLeaf(l: Leaf; v: Value) =
|
||||||
l.cachedAssertions.excl(v)
|
l.cache.excl(v)
|
||||||
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
|
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
|
||||||
if group.cachedCaptures.change(vs, -1) == cdPresentToAbsent:
|
if group.cachedCaptures.change(vs, -1) == cdPresentToAbsent:
|
||||||
for (observer, captureMap) in group.observers.pairs:
|
for (observer, captureMap) in group.observers.pairs:
|
||||||
retract(observer.target, turn, captureMap[vs])
|
var h: Handle
|
||||||
captureMap.del(vs)
|
if captureMap.take(vs, h):
|
||||||
|
retract(observer.target, turn, h)
|
||||||
modify(index.root, turn, outerValue, removedEvent, modContinuation, modLeaf, modObserver)
|
modify(index.root, turn, outerValue, removedEvent, modContinuation, modLeaf, modObserver)
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc continuationNoop(c: Continuation; v: Value) = discard
|
proc continuationNoop(c: Continuation; v: Value) = discard
|
||||||
proc leafNoop(l: Leaf; 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)
|
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)
|
adjustAssertion(index, turn, v, -1)
|
||||||
|
|
||||||
proc deliverMessage*(index: var Index; turn: var Turn; v: Value) =
|
proc deliverMessage*(index: var Index; turn: var Turn; v: Value) =
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Package
|
# Package
|
||||||
|
|
||||||
version = "20230608"
|
version = "20240120"
|
||||||
author = "Emery Hemingway"
|
author = "Emery Hemingway"
|
||||||
description = "Syndicated actors for conversational concurrency"
|
description = "Syndicated actors for conversational concurrency"
|
||||||
license = "Unlicense"
|
license = "Unlicense"
|
||||||
|
@ -9,4 +9,4 @@ srcDir = "src"
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
requires "hashlib", "nim >= 1.4.8", "preserves >= 20230530", "taps >= 20221119"
|
requires "https://github.com/ehmry/hashlib.git#f9455d4be988e14e3dc7933eb7cc7d7c4820b7ac", "nim >= 2.0.0", "https://git.syndicate-lang.org/ehmry/preserves-nim.git >= 20240116"
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
include_rules
|
include_rules
|
||||||
: foreach test*.nim | $(SYNDICATE_PROTOCOL) |> !nim_run |>
|
: foreach *.prs |> !preserves_schema_nim |> | {schema}
|
||||||
|
: foreach t*.nim | ../../preserves-nim/<tests> {schema} $(SYNDICATE_PROTOCOL) |> !nim_run |> | ../<test>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[asyncdispatch, asyncfile, os, parseopt]
|
import std/[asyncdispatch, asyncfile, parseopt]
|
||||||
import preserves, syndicate, syndicate/protocols/transportAddress
|
import preserves, syndicate, syndicate/relays
|
||||||
|
|
||||||
type
|
type
|
||||||
Present {.preservesRecord: "Present".} = object
|
Present {.preservesRecord: "Present".} = object
|
||||||
|
@ -10,7 +10,7 @@ type
|
||||||
Says {.preservesRecord: "Says".} = object
|
Says {.preservesRecord: "Says".} = object
|
||||||
who, what: string
|
who, what: string
|
||||||
|
|
||||||
proc readStdin(facet: Facet; ds: Ref; username: string) =
|
proc readStdin(facet: Facet; ds: Cap; username: string) =
|
||||||
let file = openAsync("/dev/stdin")
|
let file = openAsync("/dev/stdin")
|
||||||
onStop(facet) do (turn: var Turn): close(file)
|
onStop(facet) do (turn: var Turn): close(file)
|
||||||
close(stdin)
|
close(stdin)
|
||||||
|
@ -18,49 +18,39 @@ proc readStdin(facet: Facet; ds: Ref; username: string) =
|
||||||
let future = readLine(file)
|
let future = readLine(file)
|
||||||
addCallback(future, facet) do (turn: var Turn):
|
addCallback(future, facet) do (turn: var Turn):
|
||||||
var msg = read(future)
|
var msg = read(future)
|
||||||
|
if msg == "": quit()
|
||||||
message(turn, ds, Says(who: username, what: msg))
|
message(turn, ds, Says(who: username, what: msg))
|
||||||
readLine()
|
readLine()
|
||||||
readLine()
|
readLine()
|
||||||
|
|
||||||
proc chat(turn: var Turn; ds: Ref; username: string) =
|
proc chat(turn: var Turn; ds: Cap; username: string) =
|
||||||
during(turn, ds, ?Present) do (who: string):
|
during(turn, ds, ?:Present) do (who: string):
|
||||||
echo who, " joined"
|
echo who, " joined"
|
||||||
do:
|
do:
|
||||||
echo who, " left"
|
echo who, " left"
|
||||||
|
|
||||||
onMessage(turn, ds, ?Says) do (who: string, what: string):
|
onMessage(turn, ds, ?:Says) do (who: string, what: string):
|
||||||
echo who, ": ", what
|
echo who, ": ", what
|
||||||
|
|
||||||
discard publish(turn, ds, Present(username: username))
|
discard publish(turn, ds, Present(username: username))
|
||||||
readStdin(turn.facet, ds, username)
|
readStdin(turn.facet, ds, username)
|
||||||
|
|
||||||
proc main =
|
proc main =
|
||||||
var
|
let route = envRoute()
|
||||||
transport: Preserve[void]
|
var username = ""
|
||||||
cap: Preserve[Ref]
|
|
||||||
username = getEnv("USER")
|
|
||||||
calledWithArguments = false
|
|
||||||
for kind, key, val in getopt():
|
for kind, key, val in getopt():
|
||||||
calledWithArguments = true
|
|
||||||
if kind == cmdLongOption:
|
if kind == cmdLongOption:
|
||||||
case key
|
case key
|
||||||
of "address", "transport":
|
|
||||||
transport = parsePreserves(val)
|
|
||||||
of "cap", "sturdy":
|
|
||||||
cap = parsePreserves(val, Ref)
|
|
||||||
of "user", "username":
|
of "user", "username":
|
||||||
username = val
|
username = val
|
||||||
|
|
||||||
if calledWithArguments:
|
if username == "":
|
||||||
runActor("chat") do (root: Ref; turn: var Turn):
|
stderr.writeLine "--user: unspecified"
|
||||||
var
|
else:
|
||||||
unixAddr: transportAddress.Unix
|
runActor("chat") do (turn: var Turn; root: Cap):
|
||||||
tcpAddr: transportAddress.Tcp
|
spawnRelays(turn, root)
|
||||||
if fromPreserve(unixAddr, transport):
|
resolve(turn, root, route) do (turn: var Turn; ds: Cap):
|
||||||
connect(turn, unixAddr, cap) do (turn: var Turn; ds: Ref):
|
chat(turn, ds, username)
|
||||||
chat(turn, ds, username)
|
|
||||||
elif fromPreserve(tcpAddr, transport):
|
|
||||||
connect(turn, tcpAddr, cap) do (turn: var Turn; ds: Ref):
|
|
||||||
chat(turn, ds, username)
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import std/[options, tables, unittest]
|
||||||
|
|
||||||
|
import preserves, syndicate, syndicate/protocols/gatekeeper
|
||||||
|
|
||||||
|
import ./test_schema
|
||||||
|
|
||||||
|
test "patterns":
|
||||||
|
let
|
||||||
|
pat = ?Observe(pattern: !Foo) ?? {0: grab()}
|
||||||
|
text = """<rec Observe [<rec rec [<lit foo> <arr [<bind <_>> <_> <_>]>]> <_>]>"""
|
||||||
|
check($pat == text)
|
||||||
|
|
||||||
|
let
|
||||||
|
worte = @["alles", "in", "ordnung"]
|
||||||
|
observer = Observe(pattern: inject(?:Foo, { 0: ?worte })).toPreserves
|
||||||
|
have = capture(pat, observer).toPreserves.unpackLiterals
|
||||||
|
want = [worte.toPreserves].toPreserves
|
||||||
|
check(have == want)
|
||||||
|
|
||||||
|
type Obj {.preservesDictionary.} = object
|
||||||
|
a, b, c: int
|
||||||
|
|
||||||
|
test "dictionaries":
|
||||||
|
let pat = ?:Obj
|
||||||
|
var source = initDictionary(Cap)
|
||||||
|
source["b".toSymbol] = 2.toPreserves
|
||||||
|
source["c".toSymbol] = 3.toPreserves
|
||||||
|
source["a".toSymbol] = 1.toPreserves
|
||||||
|
|
||||||
|
let values = capture(pat, source)
|
||||||
|
check values.len == 3
|
||||||
|
check values[0] == 1.toPreserves
|
||||||
|
check values[1] == 2.toPreserves
|
||||||
|
check values[2] == 3.toPreserves
|
||||||
|
|
||||||
|
type
|
||||||
|
File {.preservesDictionary.} = object
|
||||||
|
name: string
|
||||||
|
path: string
|
||||||
|
size: BiggestInt
|
||||||
|
`type`: string
|
||||||
|
Files = Table[Symbol, File]
|
||||||
|
Fields = Table[Symbol, string]
|
||||||
|
|
||||||
|
Request {.preservesRecord: "request".} = object
|
||||||
|
seq: BiggestInt
|
||||||
|
fields: Fields
|
||||||
|
files: Files
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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 "protocol":
|
||||||
|
test "Observe":
|
||||||
|
let pat = ?:Observe
|
||||||
|
const text = """<rec Observe [<bind <_>> <bind <_>>]>"""
|
||||||
|
check $pat == text
|
||||||
|
|
||||||
|
test "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
|
||||||
|
|
||||||
|
test "TransportConnection":
|
||||||
|
let
|
||||||
|
pat = TransportConnection ?: { 2: ?:Rejected}
|
||||||
|
text = """<rec connect-transport [<_> <_> <rec rejected [<bind <_>>]>]>"""
|
||||||
|
check $pat == text
|
|
@ -2,9 +2,9 @@ import std/[streams, strutils, unittest]
|
||||||
|
|
||||||
import preserves
|
import preserves
|
||||||
import syndicate/relays
|
import syndicate/relays
|
||||||
import syndicate/protocols/[protocol, sturdy]
|
import syndicate/protocols/sturdy
|
||||||
|
|
||||||
type WireRef = sturdy.WireRef[void]
|
type WireRef = sturdy.WireRef
|
||||||
|
|
||||||
suite "protocols":
|
suite "protocols":
|
||||||
test "PDiscard":
|
test "PDiscard":
|
||||||
|
@ -20,9 +20,9 @@ suite "protocols":
|
||||||
var pos = str.getPosition
|
var pos = str.getPosition
|
||||||
echo "decode position: ", pos
|
echo "decode position: ", pos
|
||||||
try:
|
try:
|
||||||
var a = decodePreserves(str, WireRef)
|
var a = decodePreserves(str)
|
||||||
echo a
|
echo a
|
||||||
except:
|
except CatchableError:
|
||||||
str.setPosition pos
|
str.setPosition pos
|
||||||
echo str.readAll.toHex
|
echo str.readAll.toHex
|
||||||
break
|
break
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
import
|
||||||
|
preserves
|
||||||
|
|
||||||
|
type
|
||||||
|
Foo* {.preservesRecord: "foo".} = object
|
||||||
|
`x`*: seq[string]
|
||||||
|
`y`*: BiggestInt
|
||||||
|
`z`*: BiggestInt
|
||||||
|
|
||||||
|
proc `$`*(x: Foo): string =
|
||||||
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
|
proc encode*(x: Foo): seq[byte] =
|
||||||
|
encode(toPreserves(x))
|
|
@ -0,0 +1,2 @@
|
||||||
|
version 1 .
|
||||||
|
Foo = <foo @x [string ...] @y int @z int> .
|
|
@ -0,0 +1,17 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import std/times
|
||||||
|
import syndicate, syndicate/actors/timers
|
||||||
|
|
||||||
|
proc now: float64 = getTime().toUnixFloat()
|
||||||
|
|
||||||
|
runActor("test_timers") do (ds: Cap; turn: var Turn):
|
||||||
|
onPublish(turn, ds, grab(LaterThan(seconds: now()+1.0))) do:
|
||||||
|
stderr.writeLine "slept one second once"
|
||||||
|
onPublish(turn, ds, grab(LaterThan(seconds: now()+1.0))) do:
|
||||||
|
stderr.writeLine "slept one second twice"
|
||||||
|
onPublish(turn, ds, grab(LaterThan(seconds: now()+1.0))) do:
|
||||||
|
stderr.writeLine "slept one second thrice"
|
||||||
|
quit()
|
||||||
|
spawnTimers(turn, ds)
|
Loading…
Reference in New Issue