Compare commits
198 Commits
Author | SHA1 | Date |
---|---|---|
Emery Hemingway | 317167ea49 | |
Emery Hemingway | 7ab4611824 | |
Emery Hemingway | 51ec8df124 | |
Emery Hemingway | 7d7d182868 | |
Emery Hemingway | 8f35a1256c | |
Emery Hemingway | 3a4dc1f133 | |
Emery Hemingway | 4fb0285190 | |
Emery Hemingway | 81792ac4ce | |
Emery Hemingway | 7e15b37f44 | |
Emery Hemingway | c99f0a60ab | |
Emery Hemingway | 5fc371d187 | |
Emery Hemingway | 06898e4ec1 | |
Emery Hemingway | 2aaa588f6a | |
Emery Hemingway | e0b569e465 | |
Emery Hemingway | 13d3995507 | |
Emery Hemingway | 6487ef65d0 | |
Emery Hemingway | 6a4854110c | |
Emery Hemingway | 464043c8bf | |
Emery Hemingway | 15637620f0 | |
Emery Hemingway | c2dce8a274 | |
Emery Hemingway | 1a3fdf2a5a | |
Emery Hemingway | c2e1e2e0fa | |
Emery Hemingway | 5f45f76452 | |
Emery Hemingway | 403e54878c | |
Emery Hemingway | 7c72ea5732 | |
Emery Hemingway | 2aee79662e | |
Emery Hemingway | 8b79dce5ba | |
Emery Hemingway | 6bcf039dc2 | |
Emery Hemingway | 217a6aacf3 | |
Emery Hemingway | aea9a2e4e6 | |
Emery Hemingway | 78d7efc712 | |
Emery Hemingway | 289754499c | |
Emery Hemingway | 4fe2173d81 | |
Emery Hemingway | 87e730bc5b | |
Emery Hemingway | 399fd4a30c | |
Emery Hemingway | bf8f7e9aaa | |
Emery Hemingway | d654195fb8 | |
Emery Hemingway | 76acf2cb67 | |
Emery Hemingway | 50b00827ce | |
Emery Hemingway | 81ce71d495 | |
Emery Hemingway | a3146f88a5 | |
Emery Hemingway | 9ca073d433 | |
Emery Hemingway | cd846d0d46 | |
Emery Hemingway | 82f2e8ee98 | |
Emery Hemingway | 1592fac3b1 | |
Emery Hemingway | 8ef95c0e1d | |
Emery Hemingway | a014362292 | |
Emery Hemingway | b8c1bec9cf | |
Emery Hemingway | cf395dbfa4 | |
Emery Hemingway | 9d975bab56 | |
Emery Hemingway | 15d2e8bfb4 | |
Emery Hemingway | eb5d4d9a57 | |
Emery Hemingway | 01f26caf7b | |
Emery Hemingway | e31069e41a | |
Emery Hemingway | fdf2994ec4 | |
Emery Hemingway | 0cee6670c9 | |
Emery Hemingway | 1ce96560f4 | |
Emery Hemingway | d365a1e6e5 | |
Emery Hemingway | 3e5d910d1a | |
Emery Hemingway | 50a77995bc | |
Emery Hemingway | 3e324e2de4 | |
Emery Hemingway | aadf7a3dc7 | |
Emery Hemingway | a2849b18c9 | |
Emery Hemingway | f3d6e578cb | |
Emery Hemingway | 39cb3790df | |
Emery Hemingway | 46dca031fa | |
Emery Hemingway | 79c621e979 | |
Emery Hemingway | a05cfa37eb | |
Emery Hemingway | d6a8b31812 | |
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 | |
Emery Hemingway | 209ae51580 | |
Emery Hemingway | aff3061506 | |
Emery Hemingway | ee3fc7adea | |
Emery Hemingway | 1dd197f102 | |
Emery Hemingway | e65cc5ab9a | |
Emery Hemingway | 81ec3808a6 | |
Emery Hemingway | ad076bdfed | |
Emery Hemingway | b39302791e | |
Emery Hemingway | 737869d790 | |
Emery Hemingway | f1cc7b336b | |
Emery Hemingway | f0328b27cd | |
Emery Hemingway | 94fa1efd62 | |
Emery Hemingway | 0923b8abee | |
Emery Hemingway | 74254dd45b | |
Emery Hemingway | 572e3b76ab | |
Emery Hemingway | 003bfa0a97 | |
Emery Hemingway | 90247e19ce | |
Emery Hemingway | cf05845f15 | |
Emery Hemingway | 6364db7f69 | |
Emery Hemingway | b1ad08e693 | |
Emery Hemingway | 5170ad23e8 | |
Emery Hemingway | 53278d8614 | |
Emery Hemingway | ac81221faa | |
Emery Hemingway | b1627a491a | |
Emery Hemingway | 71955f257d | |
Emery Hemingway | 6f2adf573a | |
Emery Hemingway | 114088bfe6 | |
Emery Hemingway | 32d8719c84 | |
Emery Hemingway | 00269c04b1 | |
Emery Hemingway | a5c2d30ec4 | |
Emery Hemingway | 589b0772e3 | |
Emery Hemingway | 47da042671 | |
Emery Hemingway | bb4ba36ff7 | |
Emery Hemingway | 108d51c8ed | |
Emery Hemingway | 0fab80bcf7 | |
Emery Hemingway | 1234f97f20 | |
Emery Hemingway | a43a723bb1 | |
Emery Hemingway | 49b58f5ce1 | |
Emery Hemingway | e24b06d317 | |
Emery Hemingway | c4dace1eb7 | |
Emery Hemingway | 17c7328be8 | |
Emery Hemingway | 947e560fb7 | |
Emery Hemingway | e26c718142 | |
Emery Hemingway | 2aeb20e959 | |
Emery Hemingway | 26d88d7208 | |
Emery Hemingway | 0039792e0b | |
Emery Hemingway | 1a6bb4ffbd | |
Emery Hemingway | d9a3570d6f | |
Emery Hemingway | ec60d9c64a | |
Emery Hemingway | d69af0a90d | |
Emery Hemingway | 68a742797c | |
Emery Hemingway | fa5a4a9cbc | |
Emery Hemingway | 4e424e7ca8 | |
Emery Hemingway | 3fa7d4225e | |
Emery Hemingway | 535fb93df3 | |
Emery Hemingway | 96d3bbb500 | |
Emery Hemingway | 9b0437e922 | |
Emery Hemingway | a8e8eed619 | |
Emery Hemingway | 0742665288 |
|
@ -1,5 +1,3 @@
|
|||
tests/test_box_and_client
|
||||
tests/test_dsl
|
||||
tests/test_timer
|
||||
*.nim.check
|
||||
.tup
|
||||
/nim.cfg
|
||||
*.check
|
||||
*.run
|
||||
|
|
19
README.md
19
README.md
|
@ -49,12 +49,13 @@ assert present.fromPreserve(pr) == true
|
|||
|
||||
## 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
|
||||
|
||||
``` nim
|
||||
bootDataspace("main") do (dataspace: Ref; turn: var Turn):
|
||||
runActor("main") do (turn: Turn):
|
||||
let dataspace = newDataspace()
|
||||
let presenceHandle = publish(turn, dataspace, Present(username: "Judy"))
|
||||
# publish <Present "Judy"> to the dataspace
|
||||
# the assertion can be later retracted by handle
|
||||
|
@ -67,7 +68,8 @@ 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.
|
||||
|
||||
``` nim
|
||||
bootDataspace("main") do (dataspace: Ref; turn: var Turn):
|
||||
runActor("main") do (turn: Turn):
|
||||
let dataspace = newDataspace()
|
||||
during(turn, dataspace, ?Present) do (who: string):
|
||||
# This body is active when the ?Present pattern is matched.
|
||||
# The Present type contains two atomic values that can be matched
|
||||
|
@ -89,10 +91,15 @@ bootDataspace("main") do (dataspace: Ref; turn: var Turn):
|
|||
|
||||
## Examples
|
||||
|
||||
- [simpleChatProtocol](tests/chat.nim)
|
||||
- [erisresolver](https://codeberg.org/eris/nim-eris_utils#erisresolver) - dynamic configuration
|
||||
- [xdg_open_ng](https://git.syndicate-lang.org/ehmry/xdg_open_ng) - messaging, UNIX sockets, dynamic configuration, [Syndicate server](https://synit.org/book/operation/system-bus.html) interaction.
|
||||
### [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).
|
||||
```sh
|
||||
SYNDICATE_ROUTE='<route [<unix "/run/user/1000/dataspace">] [<ref {oid: "syndicate" sig: #x"69ca300c1dbfa08fba692102dd82311a"}>]>' nim c -r tests/test_chat.nim --user:fnord
|
||||
```
|
||||
### [syndicate_utils](https://git.syndicate-lang.org/ehmry/syndicate_utils)
|
||||
|
||||
---
|
||||
|
||||
This work has been supported by the [NLnet Foundation](https://nlnet.nl/) and the European Commission's [Next Generation Internet programme](https://www.ngi.eu/). The [Syndicate Actor Model](https://syndicate-lang.org/projects/2021/system-layer/) through the [NGI Zero PET](https://nlnet.nl/PET/) program and this library as a part of the [ERIS project](https://eris.codeberg.page/) through [NGI Assure](https://nlnet.nl/assure/).
|
||||
|
||||
[![NLnet](./nlnet.svg)](https://nlnet.nl/)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
include_rules
|
||||
: sbom.json |> !sbom-to-nix |> | ./<lock>
|
|
@ -0,0 +1,5 @@
|
|||
include depends.tup
|
||||
NIM_GROUPS += $(TUP_CWD)/<lock>
|
||||
NIM_FLAGS += --path:$(TUP_CWD)/../cps
|
||||
NIM_FLAGS += --path:$(TUP_CWD)/../solo5_dispatcher/pkg
|
||||
NIM_FLAGS += --path:$(TUP_CWD)/../taps/src
|
|
@ -0,0 +1,3 @@
|
|||
include ../preserves-nim/depends.tup
|
||||
NIM_FLAGS += --path:$(TUP_CWD)/../preserves-nim/src
|
||||
NIM_GROUPS += $(TUP_CWD)/<protocol>
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:serif="http://www.serif.com/" width="272" height="104" version="1.1" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="scale(0.25)">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M211.345,190.214c-1.137,4.372 -5.39,7.182 -9.858,6.513c-22.854,-3.548 -44.571,-15.066 -60.591,-34.158c-35.111,-41.843 -29.645,-104.32 12.199,-139.431c41.843,-35.111 104.32,-29.645 139.431,12.198c16.02,19.092 23.592,42.479 23.117,65.603c-0.117,4.516 -3.623,8.215 -8.126,8.575c-4.029,0.353 -8.046,0.928 -12.032,1.727c-2.662,0.545 -5.427,-0.168 -7.494,-1.933c-2.066,-1.764 -3.204,-4.383 -3.084,-7.098c0.798,-16.564 -4.394,-33.46 -15.884,-47.152c-24.226,-28.872 -67.335,-32.644 -96.207,-8.418c-28.872,24.227 -32.644,67.336 -8.417,96.208c11.489,13.693 27.226,21.74 43.677,23.832c2.694,0.353 5.075,1.928 6.454,4.269c1.378,2.341 1.6,5.187 0.602,7.714c-1.477,3.787 -2.741,7.643 -3.787,11.551Z" style="fill:#74aa00;"/>
|
||||
<path d="M211.306,68.301c16.918,-2.983 33.074,8.33 36.057,25.247c2.983,16.917 -8.33,33.074 -25.247,36.057c-16.918,2.983 -33.074,-8.33 -36.057,-25.248c-2.983,-16.917 8.33,-33.073 25.247,-36.056Z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M224.072,211.345c-4.372,-1.137 -7.183,-5.39 -6.513,-9.858c3.547,-22.854 15.066,-44.571 34.158,-60.591c41.843,-35.111 104.32,-29.645 139.431,12.199c35.111,41.843 29.645,104.32 -12.199,139.431c-19.091,16.02 -42.479,23.592 -65.602,23.117c-4.516,-0.117 -8.216,-3.623 -8.576,-8.126c-0.352,-4.029 -0.927,-8.046 -1.726,-12.032c-0.545,-2.662 0.168,-5.427 1.933,-7.494c1.764,-2.066 4.383,-3.204 7.098,-3.084c16.564,0.798 33.459,-4.394 47.152,-15.884c28.872,-24.226 32.644,-67.335 8.417,-96.207c-24.226,-28.872 -67.335,-32.644 -96.207,-8.417c-13.693,11.489 -21.74,27.226 -23.833,43.677c-0.352,2.694 -1.927,5.075 -4.268,6.454c-2.342,1.378 -5.188,1.6 -7.714,0.602c-3.787,-1.477 -7.644,-2.741 -11.551,-3.787Z" style="fill:#74aa00;"/>
|
||||
<path d="M345.985,211.306c2.983,16.918 -8.33,33.074 -25.247,36.057c-16.918,2.983 -33.074,-8.33 -36.057,-25.247c-2.983,-16.918 8.33,-33.074 25.247,-36.057c16.918,-2.983 33.074,8.33 36.057,25.247Z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M202.941,224.072c1.136,-4.372 5.389,-7.183 9.857,-6.513c22.855,3.547 44.572,15.066 60.592,34.158c35.11,41.843 29.644,104.32 -12.199,139.431c-41.843,35.111 -104.32,29.645 -139.431,-12.199c-16.02,-19.091 -23.593,-42.479 -23.117,-65.602c0.117,-4.516 3.622,-8.216 8.126,-8.576c4.029,-0.352 8.046,-0.927 12.032,-1.726c2.662,-0.545 5.427,0.168 7.493,1.933c2.067,1.764 3.205,4.383 3.084,7.098c-0.798,16.564 4.395,33.459 15.884,47.152c24.227,28.872 67.336,32.644 96.208,8.417c28.872,-24.226 32.643,-67.335 8.417,-96.207c-11.49,-13.693 -27.227,-21.74 -43.678,-23.833c-2.694,-0.352 -5.075,-1.927 -6.453,-4.268c-1.379,-2.342 -1.601,-5.188 -0.602,-7.714c1.477,-3.787 2.741,-7.644 3.787,-11.551Z" style="fill:#74aa00;"/>
|
||||
<path d="M202.979,345.985c-16.917,2.983 -33.073,-8.33 -36.056,-25.247c-2.983,-16.918 8.329,-33.074 25.247,-36.057c16.917,-2.983 33.074,8.33 36.057,25.247c2.983,16.918 -8.33,33.074 -25.248,36.057Z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M190.214,202.941c4.372,1.136 7.182,5.389 6.513,9.857c-3.548,22.855 -15.066,44.572 -34.158,60.592c-41.843,35.11 -104.32,29.644 -139.431,-12.199c-35.111,-41.843 -29.645,-104.32 12.198,-139.431c19.092,-16.02 42.479,-23.593 65.603,-23.117c4.516,0.117 8.215,3.622 8.575,8.126c0.353,4.029 0.928,8.046 1.727,12.032c0.545,2.662 -0.168,5.427 -1.933,7.493c-1.764,2.067 -4.383,3.205 -7.098,3.084c-16.564,-0.798 -33.46,4.395 -47.152,15.884c-28.872,24.227 -32.644,67.336 -8.418,96.208c24.227,28.872 67.336,32.643 96.208,8.417c13.693,-11.49 21.74,-27.227 23.832,-43.678c0.353,-2.694 1.928,-5.075 4.269,-6.453c2.341,-1.379 5.187,-1.601 7.714,-0.602c3.787,1.477 7.643,2.741 11.551,3.787Z" style="fill:#74aa00;"/>
|
||||
<path d="M68.301,202.979c-2.983,-16.917 8.33,-33.073 25.247,-36.056c16.917,-2.983 33.074,8.329 36.057,25.247c2.983,16.917 -8.33,33.074 -25.248,36.057c-16.917,2.983 -33.073,-8.33 -36.056,-25.248Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path d="M467.416,259.709c-1.713,-0 -3.169,-0.6 -4.368,-1.799c-1.199,-1.199 -1.798,-2.654 -1.798,-4.367l0,-121.259c0,-1.713 0.599,-3.169 1.798,-4.368c1.199,-1.199 2.655,-1.798 4.368,-1.798l21.066,-0c1.713,-0 3.168,0.599 4.367,1.798c1.199,1.199 1.799,2.655 1.799,4.368l-0,10.533c4.453,-5.481 10.147,-10.062 17.084,-13.745c6.936,-3.682 15.543,-5.523 25.819,-5.523c10.447,-0 19.525,2.355 27.232,7.065c7.707,4.71 13.659,11.346 17.855,19.91c4.196,8.563 6.294,18.84 6.294,30.829l0,72.19c0,1.713 -0.599,3.168 -1.798,4.367c-1.199,1.199 -2.655,1.799 -4.368,1.799l-22.607,-0c-1.713,-0 -3.169,-0.6 -4.368,-1.799c-1.199,-1.199 -1.798,-2.654 -1.798,-4.367l-0,-70.649c-0,-9.934 -2.441,-17.727 -7.322,-23.378c-4.881,-5.652 -11.946,-8.478 -21.195,-8.478c-8.906,-0 -16.013,2.826 -21.323,8.478c-5.309,5.651 -7.964,13.444 -7.964,23.378l0,70.649c0,1.713 -0.599,3.168 -1.798,4.367c-1.199,1.199 -2.655,1.799 -4.368,1.799l-22.607,-0Z" style="fill:#231f20;fill-rule:nonzero;"/>
|
||||
<path d="M627.982,259.709c-1.713,-0 -3.169,-0.6 -4.368,-1.799c-1.199,-1.199 -1.798,-2.654 -1.798,-4.367l-0,-170.071c-0,-1.713 0.599,-3.169 1.798,-4.368c1.199,-1.199 2.655,-1.798 4.368,-1.798l21.323,-0c1.713,-0 3.168,0.599 4.367,1.798c1.199,1.199 1.799,2.655 1.799,4.368l-0,170.071c-0,1.713 -0.6,3.168 -1.799,4.367c-1.199,1.199 -2.654,1.799 -4.367,1.799l-21.323,-0Z" style="fill:#231f20;fill-rule:nonzero;"/>
|
||||
<path d="M695.805,259.709c-1.713,-0 -3.169,-0.6 -4.368,-1.799c-1.199,-1.199 -1.798,-2.654 -1.798,-4.367l0,-121.259c0,-1.713 0.599,-3.169 1.798,-4.368c1.199,-1.199 2.655,-1.798 4.368,-1.798l21.066,-0c1.713,-0 3.169,0.599 4.367,1.798c1.199,1.199 1.799,2.655 1.799,4.368l-0,10.533c4.453,-5.481 10.147,-10.062 17.084,-13.745c6.936,-3.682 15.543,-5.523 25.819,-5.523c10.447,-0 19.525,2.355 27.232,7.065c7.707,4.71 13.659,11.346 17.855,19.91c4.196,8.563 6.294,18.84 6.294,30.829l0,72.19c0,1.713 -0.599,3.168 -1.798,4.367c-1.199,1.199 -2.655,1.799 -4.368,1.799l-22.607,-0c-1.713,-0 -3.169,-0.6 -4.368,-1.799c-1.199,-1.199 -1.798,-2.654 -1.798,-4.367l-0,-70.649c-0,-9.934 -2.441,-17.727 -7.322,-23.378c-4.881,-5.652 -11.946,-8.478 -21.195,-8.478c-8.906,-0 -16.013,2.826 -21.323,8.478c-5.309,5.651 -7.964,13.444 -7.964,23.378l0,70.649c0,1.713 -0.599,3.168 -1.798,4.367c-1.199,1.199 -2.655,1.799 -4.368,1.799l-22.607,-0Z" style="fill:#74aa00;fill-rule:nonzero;"/>
|
||||
<path d="M907.495,262.278c-19.011,-0 -34.083,-5.481 -45.215,-16.442c-11.133,-10.961 -17.128,-26.547 -17.984,-46.757c-0.171,-1.713 -0.257,-3.896 -0.257,-6.551c0,-2.655 0.086,-4.753 0.257,-6.294c0.685,-13.017 3.64,-24.192 8.863,-33.526c5.224,-9.335 12.46,-16.528 21.709,-21.581c9.248,-5.052 20.124,-7.578 32.627,-7.578c13.873,-0 25.519,2.869 34.939,8.606c9.42,5.738 16.528,13.702 21.323,23.892c4.796,10.191 7.194,21.966 7.194,35.325l-0,5.395c-0,1.713 -0.6,3.168 -1.799,4.367c-1.199,1.199 -2.74,1.799 -4.624,1.799l-85.293,-0l0,2.055c0.172,5.994 1.328,11.518 3.469,16.57c2.14,5.053 5.309,9.12 9.505,12.203c4.196,3.083 9.206,4.625 15.029,4.625c4.796,-0 8.82,-0.728 12.075,-2.184c3.254,-1.456 5.908,-3.126 7.964,-5.01c2.055,-1.884 3.511,-3.425 4.367,-4.624c1.541,-2.055 2.783,-3.297 3.725,-3.725c0.942,-0.428 2.355,-0.642 4.239,-0.642l22.094,-0c1.713,-0 3.126,0.513 4.239,1.541c1.113,1.028 1.584,2.312 1.413,3.854c-0.171,2.74 -1.584,6.08 -4.239,10.019c-2.655,3.939 -6.466,7.793 -11.432,11.561c-4.967,3.768 -11.176,6.893 -18.626,9.377c-7.45,2.483 -15.971,3.725 -25.562,3.725Zm-28.26,-80.925l56.776,-0l0,-0.771c0,-6.68 -1.113,-12.546 -3.339,-17.598c-2.227,-5.053 -5.481,-8.992 -9.763,-11.818c-4.282,-2.826 -9.42,-4.239 -15.414,-4.239c-5.995,0 -11.133,1.413 -15.414,4.239c-4.282,2.826 -7.494,6.765 -9.634,11.818c-2.141,5.052 -3.212,10.918 -3.212,17.598l0,0.771Z" style="fill:#74aa00;fill-rule:nonzero;"/>
|
||||
<path d="M1058.04,259.709c-10.277,-0 -18.926,-1.799 -25.948,-5.395c-7.022,-3.597 -12.246,-8.949 -15.671,-16.057c-3.426,-7.108 -5.138,-15.971 -5.138,-26.59l-0,-58.317l-20.296,-0c-1.713,-0 -3.168,-0.6 -4.367,-1.799c-1.199,-1.198 -1.799,-2.74 -1.799,-4.624l0,-14.643c0,-1.713 0.6,-3.169 1.799,-4.368c1.199,-1.199 2.654,-1.798 4.367,-1.798l20.296,-0l-0,-42.646c-0,-1.713 0.556,-3.169 1.67,-4.368c1.113,-1.199 2.611,-1.798 4.495,-1.798l20.81,-0c1.713,-0 3.168,0.599 4.367,1.798c1.199,1.199 1.799,2.655 1.799,4.368l-0,42.646l32.113,-0c1.712,-0 3.168,0.599 4.367,1.798c1.199,1.199 1.798,2.655 1.798,4.368l0,14.643c0,1.884 -0.599,3.426 -1.798,4.624c-1.199,1.199 -2.655,1.799 -4.367,1.799l-32.113,-0l-0,55.748c-0,7.022 1.241,12.503 3.725,16.442c2.483,3.939 6.808,5.909 12.973,5.909l17.727,0c1.713,0 3.168,0.6 4.367,1.798c1.199,1.199 1.799,2.655 1.799,4.368l-0,15.928c-0,1.713 -0.6,3.168 -1.799,4.367c-1.199,1.199 -2.654,1.799 -4.367,1.799l-20.809,-0Z" style="fill:#74aa00;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g>
|
||||
<path d="M489.774,297.042c0.412,0 0.769,0.15 1.069,0.45c0.3,0.3 0.45,0.657 0.45,1.069l-0,5.963c-0,0.412 -0.15,0.768 -0.45,1.068c-0.3,0.3 -0.657,0.45 -1.069,0.45l-16.819,0l0,7.707l15.694,-0c0.412,-0 0.769,0.15 1.069,0.45c0.3,0.3 0.45,0.656 0.45,1.068l-0,5.963c-0,0.412 -0.15,0.769 -0.45,1.069c-0.3,0.3 -0.657,0.45 -1.069,0.45l-15.694,-0l0,12.15c0,0.412 -0.15,0.768 -0.45,1.068c-0.3,0.3 -0.656,0.45 -1.069,0.45l-7.368,0c-0.413,0 -0.769,-0.15 -1.069,-0.45c-0.3,-0.3 -0.45,-0.656 -0.45,-1.068l-0,-36.338c-0,-0.412 0.15,-0.769 0.45,-1.069c0.3,-0.3 0.656,-0.45 1.069,-0.45l25.706,0Z" style="fill:#231f20;fill-rule:nonzero;"/>
|
||||
<path d="M542.48,296.48c3.563,-0 6.638,0.675 9.225,2.025c2.588,1.35 4.585,3.178 5.991,5.484c1.406,2.307 2.184,4.885 2.334,7.735c0.075,1.05 0.113,2.737 0.113,5.062c-0,2.288 -0.038,3.938 -0.113,4.95c-0.15,2.85 -0.928,5.428 -2.334,7.735c-1.406,2.306 -3.403,4.134 -5.991,5.484c-2.587,1.35 -5.662,2.025 -9.225,2.025c-3.562,-0 -6.637,-0.675 -9.225,-2.025c-2.587,-1.35 -4.584,-3.178 -5.99,-5.484c-1.407,-2.307 -2.185,-4.885 -2.335,-7.735c-0.075,-2.025 -0.112,-3.675 -0.112,-4.95c-0,-1.275 0.037,-2.962 0.112,-5.062c0.15,-2.85 0.928,-5.428 2.335,-7.735c1.406,-2.306 3.403,-4.134 5.99,-5.484c2.588,-1.35 5.663,-2.025 9.225,-2.025Zm7.144,15.525c-0.188,-1.988 -0.872,-3.572 -2.053,-4.753c-1.181,-1.181 -2.878,-1.772 -5.091,-1.772c-2.212,-0 -3.909,0.591 -5.09,1.772c-1.182,1.181 -1.866,2.765 -2.054,4.753c-0.112,1.237 -0.168,2.794 -0.168,4.669c-0,1.837 0.056,3.431 0.168,4.781c0.188,1.987 0.872,3.572 2.054,4.753c1.181,1.181 2.878,1.772 5.09,1.772c2.213,-0 3.91,-0.591 5.091,-1.772c1.181,-1.181 1.865,-2.766 2.053,-4.753c0.112,-1.35 0.169,-2.944 0.169,-4.781c-0,-1.875 -0.057,-3.432 -0.169,-4.669Z" style="fill:#231f20;fill-rule:nonzero;"/>
|
||||
<path d="M627.868,297.042c0.412,0 0.768,0.15 1.068,0.45c0.3,0.3 0.45,0.657 0.45,1.069l0,22.444c0,3.375 -0.712,6.262 -2.137,8.662c-1.425,2.4 -3.422,4.219 -5.991,5.457c-2.568,1.237 -5.559,1.856 -8.972,1.856c-3.45,-0 -6.459,-0.619 -9.028,-1.856c-2.568,-1.238 -4.556,-3.057 -5.962,-5.457c-1.406,-2.4 -2.11,-5.287 -2.11,-8.662l0,-22.444c0,-0.412 0.15,-0.769 0.45,-1.069c0.3,-0.3 0.657,-0.45 1.069,-0.45l7.369,0c0.412,0 0.769,0.15 1.069,0.45c0.3,0.3 0.45,0.657 0.45,1.069l-0,22.219c-0,2.287 0.572,4.059 1.715,5.316c1.144,1.256 2.803,1.884 4.978,1.884c2.175,-0 3.835,-0.628 4.979,-1.884c1.143,-1.257 1.715,-3.029 1.715,-5.316l0,-22.219c0,-0.412 0.15,-0.769 0.45,-1.069c0.3,-0.3 0.656,-0.45 1.069,-0.45l7.369,0Z" style="fill:#231f20;fill-rule:nonzero;"/>
|
||||
<path d="M691.43,336.417c-0.562,0 -1.05,-0.14 -1.462,-0.421c-0.413,-0.282 -0.694,-0.572 -0.844,-0.872l-12.881,-19.238l-0,19.013c-0,0.412 -0.15,0.768 -0.45,1.068c-0.3,0.3 -0.657,0.45 -1.069,0.45l-7.369,0c-0.412,0 -0.769,-0.15 -1.069,-0.45c-0.3,-0.3 -0.45,-0.656 -0.45,-1.068l0,-36.338c0,-0.412 0.15,-0.769 0.45,-1.069c0.3,-0.3 0.657,-0.45 1.069,-0.45l5.85,0c0.563,0 1.05,0.141 1.463,0.422c0.412,0.282 0.693,0.572 0.843,0.872l12.882,19.238l-0,-19.013c-0,-0.412 0.15,-0.769 0.45,-1.069c0.3,-0.3 0.656,-0.45 1.068,-0.45l7.369,0c0.413,0 0.769,0.15 1.069,0.45c0.3,0.3 0.45,0.657 0.45,1.069l-0,36.338c-0,0.412 -0.15,0.768 -0.45,1.068c-0.3,0.3 -0.656,0.45 -1.069,0.45l-5.85,0Z" style="fill:#231f20;fill-rule:nonzero;"/>
|
||||
<path d="M768.83,312.736c0.075,2.1 0.113,3.45 0.113,4.05c-0,0.6 -0.038,1.913 -0.113,3.938c-0.112,3.075 -0.909,5.793 -2.39,8.156c-1.482,2.362 -3.516,4.209 -6.104,5.541c-2.587,1.331 -5.587,1.996 -9,1.996l-15.187,0c-0.413,0 -0.769,-0.15 -1.069,-0.45c-0.3,-0.3 -0.45,-0.656 -0.45,-1.068l0,-36.338c0,-0.412 0.15,-0.769 0.45,-1.069c0.3,-0.3 0.656,-0.45 1.069,-0.45l14.906,0c3.413,0 6.441,0.666 9.085,1.997c2.643,1.332 4.725,3.188 6.243,5.569c1.519,2.381 2.335,5.091 2.447,8.128Zm-23.794,-6.694l0,21.375l6.019,0c4.725,0 7.181,-2.156 7.369,-6.468c0.075,-2.175 0.112,-3.6 0.112,-4.275c0,-0.75 -0.037,-2.138 -0.112,-4.163c-0.113,-2.137 -0.816,-3.75 -2.109,-4.837c-1.294,-1.088 -3.141,-1.632 -5.541,-1.632l-5.738,0Z" style="fill:#231f20;fill-rule:nonzero;"/>
|
||||
<path d="M835.936,334.336c0.15,0.375 0.225,0.656 0.225,0.844c0,0.337 -0.121,0.628 -0.365,0.872c-0.244,0.244 -0.535,0.365 -0.872,0.365l-7.088,0c-0.637,0 -1.134,-0.14 -1.49,-0.421c-0.356,-0.282 -0.591,-0.61 -0.703,-0.985l-1.575,-4.669l-14.963,0l-1.575,4.669c-0.112,0.375 -0.347,0.703 -0.703,0.985c-0.356,0.281 -0.853,0.421 -1.491,0.421l-7.087,0c-0.338,0 -0.628,-0.121 -0.872,-0.365c-0.244,-0.244 -0.366,-0.535 -0.366,-0.872c0,-0.188 0.075,-0.469 0.225,-0.844l12.263,-35.437c0.187,-0.525 0.487,-0.966 0.9,-1.322c0.412,-0.356 0.956,-0.535 1.631,-0.535l9.113,0c0.675,0 1.218,0.179 1.631,0.535c0.412,0.356 0.712,0.797 0.9,1.322l12.262,35.437Zm-14.793,-12.712l-4.557,-13.5l-4.556,13.5l9.113,-0Z" style="fill:#231f20;fill-rule:nonzero;"/>
|
||||
<path d="M898.599,297.042c0.412,0 0.769,0.15 1.069,0.45c0.3,0.3 0.45,0.657 0.45,1.069l-0,5.963c-0,0.412 -0.15,0.768 -0.45,1.068c-0.3,0.3 -0.657,0.45 -1.069,0.45l-9.9,0l-0,28.857c-0,0.412 -0.15,0.768 -0.45,1.068c-0.3,0.3 -0.656,0.45 -1.069,0.45l-7.369,0c-0.412,0 -0.768,-0.15 -1.068,-0.45c-0.3,-0.3 -0.45,-0.656 -0.45,-1.068l-0,-28.857l-9.9,0c-0.413,0 -0.769,-0.15 -1.069,-0.45c-0.3,-0.3 -0.45,-0.656 -0.45,-1.068l-0,-5.963c-0,-0.412 0.15,-0.769 0.45,-1.069c0.3,-0.3 0.656,-0.45 1.069,-0.45l30.206,0Z" style="fill:#231f20;fill-rule:nonzero;"/>
|
||||
<path d="M935.668,336.417c-0.413,0 -0.769,-0.15 -1.069,-0.45c-0.3,-0.3 -0.45,-0.656 -0.45,-1.068l-0,-36.338c-0,-0.412 0.15,-0.769 0.45,-1.069c0.3,-0.3 0.656,-0.45 1.069,-0.45l7.368,0c0.413,0 0.769,0.15 1.069,0.45c0.3,0.3 0.45,0.657 0.45,1.069l0,36.338c0,0.412 -0.15,0.768 -0.45,1.068c-0.3,0.3 -0.656,0.45 -1.069,0.45l-7.368,0Z" style="fill:#231f20;fill-rule:nonzero;"/>
|
||||
<path d="M997.655,296.48c3.563,-0 6.638,0.675 9.225,2.025c2.588,1.35 4.585,3.178 5.991,5.484c1.406,2.307 2.184,4.885 2.334,7.735c0.075,1.05 0.113,2.737 0.113,5.062c-0,2.288 -0.038,3.938 -0.113,4.95c-0.15,2.85 -0.928,5.428 -2.334,7.735c-1.406,2.306 -3.403,4.134 -5.991,5.484c-2.587,1.35 -5.662,2.025 -9.225,2.025c-3.562,-0 -6.637,-0.675 -9.225,-2.025c-2.587,-1.35 -4.584,-3.178 -5.99,-5.484c-1.407,-2.307 -2.185,-4.885 -2.335,-7.735c-0.075,-2.025 -0.112,-3.675 -0.112,-4.95c-0,-1.275 0.037,-2.962 0.112,-5.062c0.15,-2.85 0.928,-5.428 2.335,-7.735c1.406,-2.306 3.403,-4.134 5.99,-5.484c2.588,-1.35 5.663,-2.025 9.225,-2.025Zm7.144,15.525c-0.188,-1.988 -0.872,-3.572 -2.053,-4.753c-1.181,-1.181 -2.878,-1.772 -5.091,-1.772c-2.212,-0 -3.909,0.591 -5.09,1.772c-1.182,1.181 -1.866,2.765 -2.054,4.753c-0.112,1.237 -0.168,2.794 -0.168,4.669c-0,1.837 0.056,3.431 0.168,4.781c0.188,1.987 0.872,3.572 2.054,4.753c1.181,1.181 2.878,1.772 5.09,1.772c2.213,-0 3.91,-0.591 5.091,-1.772c1.181,-1.181 1.865,-2.766 2.053,-4.753c0.112,-1.35 0.169,-2.944 0.169,-4.781c-0,-1.875 -0.057,-3.432 -0.169,-4.669Z" style="fill:#231f20;fill-rule:nonzero;"/>
|
||||
<path d="M1076.35,336.417c-0.563,0 -1.05,-0.14 -1.463,-0.421c-0.412,-0.282 -0.693,-0.572 -0.843,-0.872l-12.882,-19.238l0,19.013c0,0.412 -0.15,0.768 -0.45,1.068c-0.3,0.3 -0.656,0.45 -1.068,0.45l-7.369,0c-0.413,0 -0.769,-0.15 -1.069,-0.45c-0.3,-0.3 -0.45,-0.656 -0.45,-1.068l0,-36.338c0,-0.412 0.15,-0.769 0.45,-1.069c0.3,-0.3 0.656,-0.45 1.069,-0.45l5.85,0c0.562,0 1.05,0.141 1.462,0.422c0.413,0.282 0.694,0.572 0.844,0.872l12.881,19.238l0,-19.013c0,-0.412 0.15,-0.769 0.45,-1.069c0.3,-0.3 0.657,-0.45 1.069,-0.45l7.369,0c0.412,0 0.769,0.15 1.069,0.45c0.3,0.3 0.45,0.657 0.45,1.069l-0,36.338c-0,0.412 -0.15,0.768 -0.45,1.068c-0.3,0.3 -0.657,0.45 -1.069,0.45l-5.85,0Z" style="fill:#231f20;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 16 KiB |
|
@ -0,0 +1,538 @@
|
|||
{
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.6",
|
||||
"metadata": {
|
||||
"component": {
|
||||
"type": "library",
|
||||
"bom-ref": "pkg:nim/syndicate",
|
||||
"name": "syndicate",
|
||||
"description": "Syndicated actors for conversational concurrency",
|
||||
"version": "20240523",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Emery Hemingway"
|
||||
}
|
||||
],
|
||||
"licenses": [
|
||||
{
|
||||
"license": {
|
||||
"id": "Unlicense"
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "nim:srcDir",
|
||||
"value": "src"
|
||||
},
|
||||
{
|
||||
"name": "nim:backend",
|
||||
"value": "c"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"type": "library",
|
||||
"bom-ref": "pkg:nim/nimcrypto",
|
||||
"name": "nimcrypto",
|
||||
"version": "485f7b3cfa83c1beecc0e31be0e964d697aa74d7",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "https://github.com/cheatfate/nimcrypto/archive/485f7b3cfa83c1beecc0e31be0e964d697aa74d7.tar.gz",
|
||||
"type": "source-distribution"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/cheatfate/nimcrypto",
|
||||
"type": "vcs"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "nix:fod:method",
|
||||
"value": "fetchzip"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:path",
|
||||
"value": "/nix/store/fkrcpp8lzj2yi21na79xm63xk0ggnqsp-source"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:rev",
|
||||
"value": "485f7b3cfa83c1beecc0e31be0e964d697aa74d7"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:sha256",
|
||||
"value": "1h3dzdbc9kacwpi10mj73yjglvn7kbizj1x8qc9099ax091cj5xn"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:url",
|
||||
"value": "https://github.com/cheatfate/nimcrypto/archive/485f7b3cfa83c1beecc0e31be0e964d697aa74d7.tar.gz"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "library",
|
||||
"bom-ref": "pkg:nim/preserves",
|
||||
"name": "preserves",
|
||||
"version": "20240522",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/1fee87590940761e288cf9ab3c7270832403b719.tar.gz",
|
||||
"type": "source-distribution"
|
||||
},
|
||||
{
|
||||
"url": "https://git.syndicate-lang.org/ehmry/preserves-nim.git",
|
||||
"type": "vcs"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "nix:fod:method",
|
||||
"value": "fetchzip"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:path",
|
||||
"value": "/nix/store/9zl4s2did00725n8ygbp37agvkskdhcx-source"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:rev",
|
||||
"value": "1fee87590940761e288cf9ab3c7270832403b719"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:sha256",
|
||||
"value": "1ny42rwr3yx52zwvkdg4lh54nxaxrmxdj9dlw3qarvvp2grfq4j2"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:url",
|
||||
"value": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/1fee87590940761e288cf9ab3c7270832403b719.tar.gz"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:ref",
|
||||
"value": "20240522"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:srcDir",
|
||||
"value": "src"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "library",
|
||||
"bom-ref": "pkg:nim/sys",
|
||||
"name": "sys",
|
||||
"version": "4ef3b624db86e331ba334e705c1aa235d55b05e1",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "https://github.com/ehmry/nim-sys/archive/4ef3b624db86e331ba334e705c1aa235d55b05e1.tar.gz",
|
||||
"type": "source-distribution"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/ehmry/nim-sys.git",
|
||||
"type": "vcs"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "nix:fod:method",
|
||||
"value": "fetchzip"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:path",
|
||||
"value": "/nix/store/syhxsjlsdqfap0hk4qp3s6kayk8cqknd-source"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:rev",
|
||||
"value": "4ef3b624db86e331ba334e705c1aa235d55b05e1"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:sha256",
|
||||
"value": "1q4qgw4an4mmmcbx48l6xk1jig1vc8p9cq9dbx39kpnb0890j32q"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:url",
|
||||
"value": "https://github.com/ehmry/nim-sys/archive/4ef3b624db86e331ba334e705c1aa235d55b05e1.tar.gz"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:srcDir",
|
||||
"value": "src"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "library",
|
||||
"bom-ref": "pkg:nim/taps",
|
||||
"name": "taps",
|
||||
"version": "20240405",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "https://git.sr.ht/~ehmry/nim_taps/archive/8c8572cd971d1283e6621006b310993c632da247.tar.gz",
|
||||
"type": "source-distribution"
|
||||
},
|
||||
{
|
||||
"url": "https://git.sr.ht/~ehmry/nim_taps",
|
||||
"type": "vcs"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "nix:fod:method",
|
||||
"value": "fetchzip"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:path",
|
||||
"value": "/nix/store/6y14ia52kr7jyaa0izx37mlablmq9s65-source"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:rev",
|
||||
"value": "8c8572cd971d1283e6621006b310993c632da247"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:sha256",
|
||||
"value": "1dp166bv9x773jmfqppg5i3v3rilgff013vb11yzwcid9l7s3iy8"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:url",
|
||||
"value": "https://git.sr.ht/~ehmry/nim_taps/archive/8c8572cd971d1283e6621006b310993c632da247.tar.gz"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:ref",
|
||||
"value": "20240405"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:srcDir",
|
||||
"value": "src"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "library",
|
||||
"bom-ref": "pkg:nim/solo5_dispatcher",
|
||||
"name": "solo5_dispatcher",
|
||||
"version": "20240522",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "https://git.sr.ht/~ehmry/solo5_dispatcher/archive/cc64ef99416b22b12e4a076d33de9e25a163e57d.tar.gz",
|
||||
"type": "source-distribution"
|
||||
},
|
||||
{
|
||||
"url": "https://git.sr.ht/~ehmry/solo5_dispatcher",
|
||||
"type": "vcs"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "nix:fod:method",
|
||||
"value": "fetchzip"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:path",
|
||||
"value": "/nix/store/4jj467pg4hs6warhksb8nsxn9ykz8c7c-source"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:rev",
|
||||
"value": "cc64ef99416b22b12e4a076d33de9e25a163e57d"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:sha256",
|
||||
"value": "1v9i9fqgx1g76yrmz2xwj9mxfwbjfpar6dsyygr68fv9031cqxq7"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:url",
|
||||
"value": "https://git.sr.ht/~ehmry/solo5_dispatcher/archive/cc64ef99416b22b12e4a076d33de9e25a163e57d.tar.gz"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:ref",
|
||||
"value": "20240522"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:srcDir",
|
||||
"value": "pkg"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "library",
|
||||
"bom-ref": "pkg:nim/npeg",
|
||||
"name": "npeg",
|
||||
"version": "1.2.2",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "https://github.com/zevv/npeg/archive/ec0cc6e64ea4c62d2aa382b176a4838474238f8d.tar.gz",
|
||||
"type": "source-distribution"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/zevv/npeg.git",
|
||||
"type": "vcs"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "nix:fod:method",
|
||||
"value": "fetchzip"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:path",
|
||||
"value": "/nix/store/xpn694ibgipj8xak3j4bky6b3k0vp7hh-source"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:rev",
|
||||
"value": "ec0cc6e64ea4c62d2aa382b176a4838474238f8d"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:sha256",
|
||||
"value": "1fi9ls3xl20bmv1ikillxywl96i9al6zmmxrbffx448gbrxs86kg"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:url",
|
||||
"value": "https://github.com/zevv/npeg/archive/ec0cc6e64ea4c62d2aa382b176a4838474238f8d.tar.gz"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:ref",
|
||||
"value": "1.2.2"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:srcDir",
|
||||
"value": "src"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "library",
|
||||
"bom-ref": "pkg:nim/bigints",
|
||||
"name": "bigints",
|
||||
"version": "20231006",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "https://github.com/ehmry/nim-bigints/archive/86ea14d31eea9275e1408ca34e6bfe9c99989a96.tar.gz",
|
||||
"type": "source-distribution"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/ehmry/nim-bigints.git",
|
||||
"type": "vcs"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "nix:fod:method",
|
||||
"value": "fetchzip"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:path",
|
||||
"value": "/nix/store/jvrm392g8adfsgf36prgwkbyd7vh5jsw-source"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:rev",
|
||||
"value": "86ea14d31eea9275e1408ca34e6bfe9c99989a96"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:sha256",
|
||||
"value": "15pcpmnk1bnw3k8769rjzcpg00nahyrypwbxs88jnwr4aczp99j4"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:url",
|
||||
"value": "https://github.com/ehmry/nim-bigints/archive/86ea14d31eea9275e1408ca34e6bfe9c99989a96.tar.gz"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:ref",
|
||||
"value": "20231006"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:srcDir",
|
||||
"value": "src"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "library",
|
||||
"bom-ref": "pkg:nim/cps",
|
||||
"name": "cps",
|
||||
"version": "0.10.4",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "https://github.com/nim-works/cps/archive/2a4d771a715ba45cfba3a82fa625ae7ad6591c8b.tar.gz",
|
||||
"type": "source-distribution"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nim-works/cps",
|
||||
"type": "vcs"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "nix:fod:method",
|
||||
"value": "fetchzip"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:path",
|
||||
"value": "/nix/store/m9vpcf3dq6z2h1xpi1vlw0ycxp91s5p7-source"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:rev",
|
||||
"value": "2a4d771a715ba45cfba3a82fa625ae7ad6591c8b"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:sha256",
|
||||
"value": "0c62k5wpq9z9mn8cd4rm8jjc4z0xmnak4piyj5dsfbyj6sbdw2bf"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:url",
|
||||
"value": "https://github.com/nim-works/cps/archive/2a4d771a715ba45cfba3a82fa625ae7ad6591c8b.tar.gz"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:ref",
|
||||
"value": "0.10.4"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "library",
|
||||
"bom-ref": "pkg:nim/stew",
|
||||
"name": "stew",
|
||||
"version": "3c91b8694e15137a81ec7db37c6c58194ec94a6a",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "https://github.com/status-im/nim-stew/archive/3c91b8694e15137a81ec7db37c6c58194ec94a6a.tar.gz",
|
||||
"type": "source-distribution"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/status-im/nim-stew",
|
||||
"type": "vcs"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "nix:fod:method",
|
||||
"value": "fetchzip"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:path",
|
||||
"value": "/nix/store/mqg8qzsbcc8xqabq2yzvlhvcyqypk72c-source"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:rev",
|
||||
"value": "3c91b8694e15137a81ec7db37c6c58194ec94a6a"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:sha256",
|
||||
"value": "17lfhfxp5nxvld78xa83p258y80ks5jb4n53152cdr57xk86y07w"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:url",
|
||||
"value": "https://github.com/status-im/nim-stew/archive/3c91b8694e15137a81ec7db37c6c58194ec94a6a.tar.gz"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "library",
|
||||
"bom-ref": "pkg:nim/getdns",
|
||||
"name": "getdns",
|
||||
"version": "20230806",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "https://git.sr.ht/~ehmry/getdns-nim/archive/e4ae0992ed7c5540e6d498f3074d06c8f454a0b6.tar.gz",
|
||||
"type": "source-distribution"
|
||||
},
|
||||
{
|
||||
"url": "https://git.sr.ht/~ehmry/getdns-nim",
|
||||
"type": "vcs"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "nix:fod:method",
|
||||
"value": "fetchzip"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:path",
|
||||
"value": "/nix/store/j8i20k9aarzppg4p234449140nnnaycq-source"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:rev",
|
||||
"value": "e4ae0992ed7c5540e6d498f3074d06c8f454a0b6"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:sha256",
|
||||
"value": "1dp53gndr6d9s9601dd5ipkiq94j53hlx46mxv8gpr8nd98bqysg"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:url",
|
||||
"value": "https://git.sr.ht/~ehmry/getdns-nim/archive/e4ae0992ed7c5540e6d498f3074d06c8f454a0b6.tar.gz"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:ref",
|
||||
"value": "20230806"
|
||||
},
|
||||
{
|
||||
"name": "nix:fod:srcDir",
|
||||
"value": "src"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"dependencies": [
|
||||
{
|
||||
"ref": "pkg:nim/syndicate",
|
||||
"dependsOn": [
|
||||
"pkg:nim/nimcrypto",
|
||||
"pkg:nim/preserves",
|
||||
"pkg:nim/sys",
|
||||
"pkg:nim/taps",
|
||||
"pkg:nim/solo5_dispatcher"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ref": "pkg:nim/nimcrypto",
|
||||
"dependsOn": []
|
||||
},
|
||||
{
|
||||
"ref": "pkg:nim/preserves",
|
||||
"dependsOn": [
|
||||
"pkg:nim/npeg",
|
||||
"pkg:nim/bigints"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ref": "pkg:nim/sys",
|
||||
"dependsOn": [
|
||||
"pkg:nim/cps",
|
||||
"pkg:nim/stew"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ref": "pkg:nim/taps",
|
||||
"dependsOn": [
|
||||
"pkg:nim/getdns",
|
||||
"pkg:nim/sys",
|
||||
"pkg:nim/cps",
|
||||
"pkg:nim/solo5_dispatcher"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ref": "pkg:nim/solo5_dispatcher",
|
||||
"dependsOn": [
|
||||
"pkg:nim/cps"
|
||||
]
|
||||
},
|
||||
{
|
||||
"ref": "pkg:nim/npeg",
|
||||
"dependsOn": []
|
||||
},
|
||||
{
|
||||
"ref": "pkg:nim/bigints",
|
||||
"dependsOn": []
|
||||
},
|
||||
{
|
||||
"ref": "pkg:nim/cps",
|
||||
"dependsOn": []
|
||||
},
|
||||
{
|
||||
"ref": "pkg:nim/stew",
|
||||
"dependsOn": []
|
||||
},
|
||||
{
|
||||
"ref": "pkg:nim/getdns",
|
||||
"dependsOn": []
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{ pkgs ? import <nixpkgs> { } }:
|
||||
|
||||
pkgs.buildNimPackage {
|
||||
name = "dummy";
|
||||
buildInputs = builtins.attrValues { inherit (pkgs) getdns solo5; };
|
||||
nativeBuildInputs = builtins.attrValues { inherit (pkgs) pkg-config solo5; };
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
include_rules
|
||||
: foreach *.nim |> !nim_check |>
|
|
@ -1,74 +1,71 @@
|
|||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
## This module implements the `Syndicate DSL <https://syndicate-lang.org/doc/syndicate/>`_.
|
||||
|
||||
runnableExamples:
|
||||
from syndicate/protocols/simpleChatProtocol import Present, Says
|
||||
import std/asyncdispatch
|
||||
|
||||
bootDataspace("example") do (ds: Ref; turn: var Turn):
|
||||
let
|
||||
me = "user"
|
||||
presenceHandle = publish(turn, ds, Present(username: me))
|
||||
|
||||
onMessage(turn, ds, ?Says) do (who: string; what: string):
|
||||
echo who, ": ", what
|
||||
retract(turn, presenceHandle)
|
||||
|
||||
during(turn, ds, ?Present) do (username: string):
|
||||
echo "[", username, " arrived]"
|
||||
message(turn, ds, Says(who: me, what: "users are losers"))
|
||||
do:
|
||||
echo "[", username, "departed]"
|
||||
|
||||
poll()
|
||||
|
||||
import std/[macros, tables, typetraits]
|
||||
|
||||
import preserves
|
||||
export fromPreserves, toPreserves
|
||||
|
||||
import ./syndicate/[actors, dataspaces, durings, patterns]
|
||||
import ./syndicate/protocols/dataspace
|
||||
|
||||
from ./syndicate/relays import connectStdio, connectUnix
|
||||
export actors, dataspace, dataspaces, patterns
|
||||
|
||||
export Actor, Assertion, Facet, Handle, Ref, Symbol, Turn, TurnAction,
|
||||
`$`, `?`, analyse, bootDataspace, connectStdio, connectUnix, drop, facet,
|
||||
future, grab, message, newDataspace, publish, retract, replace, run, stop, unembed
|
||||
type Assertion* {.deprecated: "Assertion and Preserve[void] replaced by Value".} = Value
|
||||
|
||||
proc `!`*(typ: static typedesc): Pattern {.inline.} =
|
||||
patterns.dropType(typ)
|
||||
|
||||
proc `?`*[T](val: T): Pattern {.inline.} =
|
||||
patterns.drop[T](val)
|
||||
|
||||
proc `?:`*(typ: static typedesc): Pattern {.inline.} =
|
||||
patterns.grabTypeFlat(typ)
|
||||
|
||||
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)
|
||||
|
||||
type
|
||||
Observe* = dataspace.Observe[Ref]
|
||||
PublishProc = proc (turn: var Turn; v: Assertion; h: Handle) {.closure.}
|
||||
RetractProc = proc (turn: var Turn; h: Handle) {.closure.}
|
||||
MessageProc = proc (turn: var Turn; v: Assertion) {.closure.}
|
||||
PublishProc = proc (turn: Turn; v: Value; h: Handle) {.closure.}
|
||||
RetractProc = proc (turn: Turn; h: Handle) {.closure.}
|
||||
MessageProc = proc (turn: Turn; v: Value) {.closure.}
|
||||
ClosureEntity = ref object of Entity
|
||||
publishImpl: PublishProc
|
||||
retractImpl: RetractProc
|
||||
messageImpl: MessageProc
|
||||
publishImpl*: PublishProc
|
||||
retractImpl*: RetractProc
|
||||
messageImpl*: MessageProc
|
||||
|
||||
method publish(e: ClosureEntity; turn: var Turn; v: Assertion; h: Handle) =
|
||||
if not e.publishImpl.isNil: e.publishImpl(turn, v, h)
|
||||
method publish(e: ClosureEntity; turn: Turn; a: AssertionRef; h: Handle) =
|
||||
if not e.publishImpl.isNil: e.publishImpl(turn, a.value, h)
|
||||
|
||||
method retract(e: ClosureEntity; turn: var Turn; h: Handle) =
|
||||
method retract(e: ClosureEntity; turn: Turn; h: Handle) =
|
||||
if not e.retractImpl.isNil: e.retractImpl(turn, h)
|
||||
|
||||
method message(e: ClosureEntity; turn: var Turn; v: Assertion) =
|
||||
if not e.messageImpl.isNil: e.messageImpl(turn, v)
|
||||
method message(e: ClosureEntity; turn: Turn; a: AssertionRef) =
|
||||
if not e.messageImpl.isNil: e.messageImpl(turn, a.value)
|
||||
|
||||
proc argumentCount(handler: NimNode): int =
|
||||
handler.expectKind {nnkDo, nnkStmtList}
|
||||
if handler.kind == nnkDo: result = pred handler[3].len
|
||||
|
||||
proc wrapPublishHandler(handler: NimNode): NimNode =
|
||||
handler.expectKind {nnkDo, nnkStmtList}
|
||||
var innerProc = newNimNode(nnkProcDef)
|
||||
handler.copyChildrenTo innerProc
|
||||
innerProc[0] = genSym(nskProc, "message")
|
||||
var
|
||||
valuesSym = genSym(nskVar, "values")
|
||||
valuesTuple = newNimNode(nnkTupleTy, handler)
|
||||
innerTuple = newNimNode(nnkVarTuple, handler)
|
||||
varSectionInner = newNimNode(nnkVarSection, handler).add(innerTuple)
|
||||
if handler.kind == nnkDo:
|
||||
type HandlerNodes = tuple
|
||||
valuesSym, varSection, body: NimNode
|
||||
|
||||
proc generateHandlerNodes(handler: NimNode): HandlerNodes =
|
||||
handler.expectKind {nnkStmtList, nnkDo}
|
||||
result.valuesSym = genSym(nskVar, "values")
|
||||
let valuesTuple = newNimNode(nnkTupleTy, handler)
|
||||
case handler.kind
|
||||
of nnkStmtList:
|
||||
result.body = handler
|
||||
of nnkDo:
|
||||
let
|
||||
innerTuple = newNimNode(nnkVarTuple, handler)
|
||||
varSectionInner = newNimNode(nnkVarSection, handler).add(innerTuple)
|
||||
for i, arg in handler[3]:
|
||||
if i > 0:
|
||||
arg.expectKind nnkIdentDefs
|
||||
|
@ -78,150 +75,123 @@ proc wrapPublishHandler(handler: NimNode): NimNode =
|
|||
arg.copyChildrenTo def
|
||||
valuesTuple.add(def)
|
||||
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
|
||||
varSectionOuter = newNimNode(nnkVarSection, handler).add(
|
||||
newIdentDefs(valuesSym, valuesTuple))
|
||||
publishBody =
|
||||
if handler.kind == nnkStmtList: handler
|
||||
else: newStmtList(varSectionInner, handler[6])
|
||||
turnSym = ident"turn"
|
||||
(valuesSym, varSection, publishBody) =
|
||||
generateHandlerNodes(handler)
|
||||
handleSym = ident"handle"
|
||||
handlerSym = genSym(nskProc, "publish")
|
||||
bindingsSym = ident"bindings"
|
||||
quote do:
|
||||
proc `handlerSym`(`turnSym`: var Turn; bindings: Assertion; `handleSym`: Handle) =
|
||||
`varSectionOuter`
|
||||
if fromPreserve(`valuesSym`, bindings):
|
||||
proc `handlerSym`(`turn`: Turn; `bindingsSym`: Value; `handleSym`: Handle) =
|
||||
`varSection`
|
||||
if fromPreserves(`valuesSym`, bindings):
|
||||
`publishBody`
|
||||
|
||||
proc wrapMessageHandler(handler: NimNode): NimNode =
|
||||
handler.expectKind {nnkDo, nnkStmtList}
|
||||
var innerProc = newNimNode(nnkProcDef)
|
||||
handler.copyChildrenTo innerProc
|
||||
innerProc[0] = genSym(nskProc, "message")
|
||||
proc wrapMessageHandler(turn, handler: NimNode): NimNode =
|
||||
var
|
||||
valuesSym = genSym(nskVar, "values")
|
||||
valuesTuple = newNimNode(nnkTupleTy, 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"
|
||||
(valuesSym, varSection, body) =
|
||||
generateHandlerNodes(handler)
|
||||
handlerSym = genSym(nskProc, "message")
|
||||
bindingsSym = ident"bindings"
|
||||
quote do:
|
||||
proc `handlerSym`(`turnSym`: var Turn; bindings: Assertion) =
|
||||
`varSectionOuter`
|
||||
if fromPreserve(`valuesSym`, bindings):
|
||||
proc `handlerSym`(`turn`: Turn; `bindingsSym`: Value) =
|
||||
`varSection`
|
||||
if fromPreserves(`valuesSym`, bindings):
|
||||
`body`
|
||||
|
||||
macro onPublish*(turn: Turn; ds: Ref; pattern: Pattern; handler: untyped) =
|
||||
## Call `handler` when an assertion matching `pattern` is published at `ds`.
|
||||
let
|
||||
argCount = argumentCount(handler)
|
||||
handlerProc = wrapPublishHandler(handler)
|
||||
handlerSym = handlerProc[0]
|
||||
result = quote do:
|
||||
doAssert `pattern`.analyse.capturePaths.len == `argCount`, "mismatch between pattern capture and handler 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:
|
||||
doAssert `pattern`.analyse.capturePaths.len == `argCount`, "mismatch between pattern capture and handler 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")
|
||||
proc wrapDuringHandler(turn, entryBody, exitBody: NimNode): NimNode =
|
||||
var
|
||||
valuesSym = ident("rawValues")
|
||||
valuesTuple = newNimNode(nnkTupleTy, 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"
|
||||
(valuesSym, varSection, publishBody) =
|
||||
generateHandlerNodes(entryBody)
|
||||
bindingsSym = ident"bindings"
|
||||
handleSym = ident"duringHandle"
|
||||
entrySym = genSym(nskProc, "during")
|
||||
duringSym = genSym(nskProc, "during")
|
||||
if exitBody.isNil:
|
||||
quote do:
|
||||
proc `duringSym`(`turnSym`: var Turn; `bindingsSym`: Assertion; `handleSym`: Handle): TurnAction =
|
||||
`varSectionOuter`
|
||||
if fromPreserve(`valuesSym`, `bindingsSym`):
|
||||
proc `duringSym`(`turn`: Turn; `bindingsSym`: Value; `handleSym`: Handle): TurnAction =
|
||||
`varSection`
|
||||
if fromPreserves(`valuesSym`, `bindingsSym`):
|
||||
`publishBody`
|
||||
else:
|
||||
quote do:
|
||||
proc `duringSym`(`turnSym`: var Turn; `bindingsSym`: Assertion; `handleSym`: Handle): TurnAction =
|
||||
`varSectionOuter`
|
||||
if fromPreserve(`valuesSym`, `bindingsSym`):
|
||||
proc `duringSym`(`turn`: Turn; `bindingsSym`: Value; `handleSym`: Handle): TurnAction =
|
||||
`varSection`
|
||||
if fromPreserves(`valuesSym`, `bindingsSym`):
|
||||
`publishBody`
|
||||
proc action(`turnSym`: var Turn) =
|
||||
proc action(`turn`: Turn) =
|
||||
`exitBody`
|
||||
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 `retractBody` on retraction. Assertions that match `pattern` but are not
|
||||
## convertable to the arguments of `publishBody` are silently discarded.
|
||||
##
|
||||
## The following symbols are injected into the scope of both bodies:
|
||||
## - `turn` - active turn at entry of `publishBody` and `retractBody`
|
||||
## - `bindings` - raw Preserves sequence that matched `pattern`
|
||||
## - `duringHandle` - dataspace handle of the assertion that triggered `publishBody`
|
||||
let
|
||||
argCount = argumentCount(publishBody)
|
||||
callbackProc = wrapDuringHandler(publishBody, retractBody)
|
||||
callbackProc = wrapDuringHandler(turn, publishBody, retractBody)
|
||||
callbackSym = callbackProc[0]
|
||||
result = quote do:
|
||||
doAssert `pattern`.analyse.capturePaths.len == `argCount`, "mismatch between pattern capture and handler arguments"
|
||||
if `argCount` != 0 and `pattern`.analyse.capturePaths.len != `argCount`:
|
||||
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments - " & $`pattern`)
|
||||
`callbackProc`
|
||||
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.
|
||||
let
|
||||
argCount = argumentCount(publishBody)
|
||||
callbackProc = wrapDuringHandler(publishBody, nil)
|
||||
`argCount` = argumentCount(publishBody)
|
||||
callbackProc = wrapDuringHandler(turn, publishBody, nil)
|
||||
callbackSym = callbackProc[0]
|
||||
result = quote do:
|
||||
doAssert `pattern`.analyse.capturePaths.len == `argCount`, "mismatch between pattern capture and handler arguments"
|
||||
if `argCount` != 0 and `pattern`.analyse.capturePaths.len != `argCount`:
|
||||
raiseAssert($`pattern`.analyse.capturePaths.len & " values captured but handler has " & $`argCount` & " arguments - " & $`pattern`)
|
||||
`callbackProc`
|
||||
discard observe(`turn`, `ds`, `pattern`, during(`callbackSym`))
|
||||
|
||||
when defined(solo5):
|
||||
echo """
|
||||
______
|
||||
/ \_\
|
||||
/ ,__/ \ ____ __
|
||||
/\__/ \, \ _______ ______ ____/ /_/________ / /____
|
||||
\/ \__/ / / ___/ / / / __ \/ __ / / ___/ __ \/ __/ _ \
|
||||
\ ' \__/ _\_ \/ /_/ / / / / /_/ / / /__/ /_/ / /_/ __/
|
||||
\____/_/ /____/\__, /_/ /_/\____/_/\___/\__/_/\__/\___/
|
||||
/____/
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
include_rules
|
||||
NIM_FLAGS += --path:$(TUP_CWD)/..
|
||||
: foreach *.nim |> !nim_check |>
|
||||
: patterns.nim |> !nim_bin |>
|
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,7 @@
|
|||
## An unordered association of items to counts.
|
||||
## An item count may be negative, unlike CountTable.
|
||||
|
||||
import tables
|
||||
import std/[assertions, tables]
|
||||
|
||||
type
|
||||
ChangeDescription* = enum
|
||||
|
@ -37,5 +37,14 @@ proc change*[T](bag: var Bag[T]; key: T; delta: int; clamp = false): ChangeDescr
|
|||
if result in {cdAbsentToAbsent, cdPresentToAbsent}:
|
||||
bag.del(key)
|
||||
|
||||
iterator items*[T](bag: Bag[T]): (int, T) =
|
||||
for k, v in bag: yield(v, k)
|
||||
iterator items*[T](bag: Bag[T]): T =
|
||||
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 '}'
|
||||
|
||||
export tables.contains, tables.del, tables.len
|
||||
|
|
|
@ -1,47 +1,46 @@
|
|||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import preserves
|
||||
import ./protocols/sturdy, ./private/hmacs
|
||||
runnableExamples:
|
||||
from std/unittest import check
|
||||
let sturdy = mint()
|
||||
check $sturdy == """<ref {oid: "syndicate" sig: #x"69ca300c1dbfa08fba692102dd82311a"}>"""
|
||||
|
||||
from ./actors import Ref
|
||||
import
|
||||
std/[options, tables],
|
||||
nimcrypto/[blake2, hmac],
|
||||
preserves,
|
||||
./protocols/sturdy
|
||||
|
||||
type SturdyRef* = sturdy.SturdyRef[Ref]
|
||||
export `$`
|
||||
|
||||
proc mint*(key: openarray[byte]; oid: Preserve[Ref]): SturdyRef =
|
||||
SturdyRef(oid: oid, sig: hmacSha256(key, encode(oid), key.len))
|
||||
proc hmac(key, data: openarray[byte]): seq[byte] =
|
||||
result = newSeq[byte](32)
|
||||
var ctx: HMAC[blake2_256]
|
||||
ctx.init key
|
||||
ctx.update data
|
||||
discard ctx.finish result
|
||||
result.setLen 16
|
||||
|
||||
proc mint*[T](key: openarray[byte]; oid: T): SturdyRef =
|
||||
let oidPr = toPreserve(oid, Ref)
|
||||
SturdyRef(oid: oidPr, sig: hmacSha256(key, encode(oidPr), key.len))
|
||||
proc mint*(key: openarray[byte]; oid: Value): SturdyRef =
|
||||
result.parameters.oid = oid
|
||||
result.parameters.sig = hmac(key, oid.encode)
|
||||
|
||||
proc mint*(): SturdyRef {.deprecated.} =
|
||||
## Mint a test capability for the use with the Syndicate server.
|
||||
proc mint*(): SturdyRef =
|
||||
var key: array[16, byte]
|
||||
mint(key, "syndicate")
|
||||
mint(key, "syndicate".toPreserves)
|
||||
|
||||
proc attenuate*(r: SturdyRef; caveats: Attenuation): SturdyRef =
|
||||
result = SturdyRef(
|
||||
oid: r.oid,
|
||||
caveatChain: r.caveatChain,
|
||||
sig: hmacSha256(r.sig, caveats.encode))
|
||||
result.caveatChain.add caveats
|
||||
proc attenuate*(r: SturdyRef; caveats: seq[Caveat]): SturdyRef =
|
||||
if r.parameters.caveats.isSome:
|
||||
result.parameters.caveats = some(r.parameters.caveats.get & caveats.toPreserves)
|
||||
result.parameters.oid = r.parameters.oid
|
||||
result.parameters.sig = hmac(r.parameters.sig, caveats.toPreserves.encode)
|
||||
|
||||
proc validate*(key: openarray[byte]; r: SturdyRef): bool =
|
||||
var sig = hmacSha256(key, r.oid.encode, key.len)
|
||||
for a in r.caveatChain:
|
||||
sig = hmacSha256(sig, a.encode)
|
||||
r.sig == sig
|
||||
proc validate*(key: openarray[byte]; sturdy: SturdyRef): bool =
|
||||
var sig = hmac(key, sturdy.parameters.oid.encode)
|
||||
if sturdy.parameters.caveats.isSome:
|
||||
for cav in sturdy.parameters.caveats.get:
|
||||
sig = hmac(sig, encode cav)
|
||||
result = (sig == sturdy.parameters.sig)
|
||||
|
||||
when isMainModule:
|
||||
import unittest
|
||||
import preserves/parse
|
||||
test "sturdy":
|
||||
var
|
||||
key: array[16, byte]
|
||||
oid = "syndicate".toPreserve
|
||||
sRef = mint(key, oid)
|
||||
control = parsePreserves"""<ref "syndicate" [] #[pkgN9TBmEd3Q04grVG4Zdw]>"""
|
||||
check(sRef.toPreserve == control)
|
||||
let aRef = attenuate(sRef, newSeq[Caveat]())
|
||||
check validate(key, aRef)
|
||||
# mint utility moved to syndicate_utils/src/mintsturdyref.nim
|
||||
|
|
|
@ -51,7 +51,7 @@ iterator observersOf[Sid, Oid](g: Graph[Sid, Oid]; oid: Oid): Sid =
|
|||
if g.edgesForward.hasKey(oid):
|
||||
for sid in g.edgesForward[oid]: yield sid
|
||||
|
||||
proc repairDamage*[Sid, Oid](g: var Graph[Sid, Oid]; repairNode: proc (sid: Sid) {.gcsafe.}) =
|
||||
proc repairDamage*[Sid, Oid](g: var Graph[Sid, Oid]; repairNode: proc (sid: Sid) {.closure.}) =
|
||||
var repairedThisRound: Set[Oid]
|
||||
while true:
|
||||
var workSet = move g.damagedNodes
|
||||
|
|
|
@ -1,47 +1,50 @@
|
|||
# SPDX-FileCopyrightText: ☭ 2022 Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[hashes, tables]
|
||||
import std/[hashes, options, tables]
|
||||
import preserves
|
||||
import ./actors, ./protocols/dataspace, ./skeletons
|
||||
|
||||
from ./protocols/protocol import Handle
|
||||
|
||||
type
|
||||
Observe = dataspace.Observe[Ref]
|
||||
Assertion = Value
|
||||
Observe = dataspace.Observe
|
||||
Turn = actors.Turn
|
||||
|
||||
Dataspace {.final.} = ref object of Entity
|
||||
index: Index
|
||||
handleMap: Table[Handle, Assertion]
|
||||
|
||||
method publish(ds: Dataspace; turn: var Turn; v: Assertion; h: Handle) =
|
||||
if add(ds.index, turn, v):
|
||||
var obs: Observe
|
||||
if obs.fromPreserve v:
|
||||
ds.index.add(turn, obs.pattern, unembed obs.observer)
|
||||
ds.handleMap[h] = v
|
||||
method publish(ds: Dataspace; turn: Turn; a: AssertionRef; h: Handle) =
|
||||
if add(ds.index, turn, a.value):
|
||||
var obs = a.value.preservesTo(Observe)
|
||||
if obs.isSome and obs.get.observer of Cap:
|
||||
ds.index.add(turn, obs.get.pattern, Cap(obs.get.observer))
|
||||
ds.handleMap[h] = a.value
|
||||
|
||||
method retract(ds: Dataspace; turn: var Turn; h: Handle) =
|
||||
try:
|
||||
let v = ds.handleMap[h]
|
||||
if remove(ds.index, turn, v):
|
||||
ds.handleMap.del h
|
||||
var obs: Observe
|
||||
if obs.fromPreserve v:
|
||||
ds.index.remove(turn, obs.pattern, unembed obs.observer)
|
||||
except KeyError: discard
|
||||
method retract(ds: Dataspace; turn: Turn; h: Handle) =
|
||||
let v = ds.handleMap[h]
|
||||
if remove(ds.index, turn, v):
|
||||
ds.handleMap.del h
|
||||
var obs = v.preservesTo(Observe)
|
||||
if obs.isSome and obs.get.observer of Cap:
|
||||
ds.index.remove(turn, obs.get.pattern, Cap(obs.get.observer))
|
||||
|
||||
method message(ds: Dataspace; turn: var Turn; v: Assertion) =
|
||||
ds.index.deliverMessage(turn, v)
|
||||
method message(ds: Dataspace; turn: Turn; a: AssertionRef) =
|
||||
ds.index.deliverMessage(turn, a.value)
|
||||
|
||||
type BootProc = proc (ds: Ref; turn: var Turn) {.gcsafe.}
|
||||
proc newDataspace*(turn: Turn): Cap =
|
||||
newCap(turn, Dataspace(index: initIndex()))
|
||||
|
||||
proc bootDataspace*(name: string; bootProc: BootProc): Actor {.discardable.} =
|
||||
bootActor(name) do (turn: var Turn):
|
||||
discard turn.facet.preventInertCheck()
|
||||
let ds = newRef(turn, Dataspace(index: initIndex()))
|
||||
type BootProc = proc (turn: Turn; ds: Cap) {.closure.}
|
||||
type DeprecatedBootProc = proc (ds: Cap; turn: Turn) {.closure.}
|
||||
|
||||
proc bootDataspace*(name: string; bootProc: BootProc): Actor =
|
||||
bootActor(name) do (turn: Turn):
|
||||
turn.preventInertCheck()
|
||||
bootProc(turn, newDataspace(turn))
|
||||
|
||||
proc bootDataspace*(name: string; bootProc: DeprecatedBootProc): Actor {.deprecated.} =
|
||||
bootDataspace(name) do (turn: Turn, ds: Cap):
|
||||
bootProc(ds, turn)
|
||||
|
||||
proc newDataspace*(turn: var Turn): Ref =
|
||||
newRef(turn, Dataspace(index: initIndex()))
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
include_rules
|
||||
NIM_FLAGS += --path:$(TUP_CWD)/../..
|
||||
: foreach *.nim |> !nim_check |>
|
|
@ -0,0 +1,343 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[httpcore, options, parseutils, sets, streams, strutils, tables, times, uri]
|
||||
import preserves, ../../syndicate, ../bags, ./timers
|
||||
import ../protocols/http
|
||||
import taps
|
||||
|
||||
const
|
||||
CRLF = "\x0d\x0a"
|
||||
SP = { ' ', '\x09', '\x0b', '\x0c', '\x0d' }
|
||||
SupportedVersion = "HTTP/1.1"
|
||||
IMF = initTimeFormat"ddd, dd MMM yyyy HH:mm:ss"
|
||||
|
||||
when defined(posix):
|
||||
proc echo(args: varargs[string, `$`]) {.used.} =
|
||||
stderr.writeLine(args)
|
||||
|
||||
proc badRequest(conn: Connection; msg: string) =
|
||||
conn.send(SupportedVersion & " 400 " & msg, endOfMessage = true)
|
||||
|
||||
proc extractQuery(s: var string): Table[Symbol, seq[QueryValue]] =
|
||||
let start = succ skipUntil(s, '?')
|
||||
if start < s.len:
|
||||
var query = s[start..s.high]
|
||||
s.setLen(pred start)
|
||||
for key, val in uri.decodeQuery(query):
|
||||
var list = result.getOrDefault(Symbol key)
|
||||
list.add QueryValue(orKind: QueryValueKind.string, string: val)
|
||||
result[Symbol key] = list
|
||||
|
||||
proc parseRequest(conn: Connection; text: string): (int, HttpRequest) =
|
||||
## Parse an `HttpRequest` request out of a `text` from a `Connection`.
|
||||
result[1].host = RequestHost(orKind: RequestHostKind.absent)
|
||||
result[1].body = RequestBody(orKind: RequestBodyKind.absent)
|
||||
|
||||
var
|
||||
token: string
|
||||
off: int
|
||||
|
||||
template advanceSp =
|
||||
let n = skipWhile(text, SP, off)
|
||||
if n < 1:
|
||||
badRequest(conn, "invalid request")
|
||||
return
|
||||
inc(off, n)
|
||||
|
||||
# method
|
||||
off.inc parseUntil(text, token, SP, off)
|
||||
result[1].method = token.toLowerAscii.Symbol
|
||||
advanceSp()
|
||||
|
||||
# target
|
||||
if text[off] == '/': inc(off) #TODO: always a leading slash?
|
||||
off.inc parseUntil(text, token, SP, off)
|
||||
advanceSp()
|
||||
|
||||
block:
|
||||
var version: string
|
||||
off.inc parseUntil(text, version, SP, off)
|
||||
advanceSp()
|
||||
if version != SupportedVersion:
|
||||
badRequest(conn, "version not supported")
|
||||
return
|
||||
|
||||
result[1].query = extractQuery(token)
|
||||
|
||||
if token != "":
|
||||
result[1].path = split(token, '/')
|
||||
for p in result[1].path.mitems:
|
||||
# normalize the path
|
||||
for i, c in p:
|
||||
if c in {'A'..'Z'}:
|
||||
p[i] = char c.ord + 0x20
|
||||
|
||||
template advanceLine =
|
||||
inc off, skipWhile(text, {'\x0d'}, off)
|
||||
if text.high < off or text[off] != '\x0a':
|
||||
badRequest(conn, "invalid request")
|
||||
return
|
||||
inc off, 1
|
||||
|
||||
advanceLine()
|
||||
while off < text.len:
|
||||
off.inc parseUntil(text, token, {'\x0d', '\x0a'}, off)
|
||||
if token == "": break
|
||||
advanceLine()
|
||||
var
|
||||
(key, vals) = httpcore.parseHeader(token)
|
||||
k = key.toLowerAscii.Symbol
|
||||
v = result[1].headers.getOrDefault(k)
|
||||
for e in vals.mitems:
|
||||
e = e.toLowerAscii
|
||||
if k == Symbol"host":
|
||||
result[1].host = RequestHost(orKind: RequestHostKind.`present`, present: e)
|
||||
if v == "": v = move e
|
||||
else:
|
||||
v.add ", "
|
||||
v.add e
|
||||
if k == Symbol"host":
|
||||
result[1].host = RequestHost(orKind: RequestHostKind.`present`, present: v)
|
||||
result[1].headers[k] = v
|
||||
|
||||
result[0] = off
|
||||
|
||||
proc len(chunk: Chunk): int =
|
||||
case chunk.orKind
|
||||
of ChunkKind.string: chunk.string.len
|
||||
of ChunkKind.bytes: chunk.bytes.len
|
||||
|
||||
proc lenLine(chunk: Chunk): string =
|
||||
result = chunk.len.toHex.strip(true, false, {'0'})
|
||||
result.add CRLF
|
||||
|
||||
type
|
||||
Driver = ref object
|
||||
facet: Facet
|
||||
ds, timers: Cap
|
||||
bindings: Bag[Value]
|
||||
# cannot make a bag of HttpBinding, no `==` operator
|
||||
sequenceNumber: BiggestInt
|
||||
Session = ref object
|
||||
facet: Facet
|
||||
driver: Driver
|
||||
conn: Connection
|
||||
port: Port
|
||||
Exchange = ref object of Entity
|
||||
ses: Session
|
||||
req: HttpRequest
|
||||
stream: StringStream
|
||||
mode: HttpResponseKind
|
||||
active: bool
|
||||
|
||||
proc send[T: byte|char](ses: Session; data: openarray[T]) =
|
||||
ses.conn.send(addr data[0], data.len, endOfMessage = false)
|
||||
|
||||
proc send(ses: Session; chunk: Chunk) =
|
||||
case chunk.orKind
|
||||
of ChunkKind.string:
|
||||
ses.send(chunk.string)
|
||||
of ChunkKind.bytes:
|
||||
ses.send(chunk.bytes)
|
||||
|
||||
func `==`(s: string; rh: RequestHost): bool =
|
||||
rh.orKind == RequestHostKind.present and rh.present == s
|
||||
|
||||
proc match(b: HttpBinding, r: HttpRequest): bool =
|
||||
## Check if `HttpBinding` `b` matches `HttpRequest` `r`.
|
||||
result =
|
||||
(b.host.orKind == HostPatternKind.any or
|
||||
b.host.host == r.host) and
|
||||
(b.port == r.port) and
|
||||
(b.method.orKind == MethodPatternKind.any or
|
||||
b.method.specific == r.method)
|
||||
if result:
|
||||
for i, p in b.path:
|
||||
if i > r.path.high: return false
|
||||
case p.orKind
|
||||
of PathPatternElementKind.wildcard: discard
|
||||
of PathPatternElementKind.label:
|
||||
if p.label != r.path[i]: return false
|
||||
of PathPatternElementKind.rest:
|
||||
return i == b.path.high
|
||||
# return false if ... isn't the last element
|
||||
|
||||
proc strongerThan(a, b: HttpBinding): bool =
|
||||
## Check if `a` is a stronger `HttpBinding` than `b`.
|
||||
result =
|
||||
(a.host.orKind != b.host.orKind and
|
||||
a.host.orKind == HostPatternKind.host) or
|
||||
(a.method.orKind != b.method.orKind and
|
||||
a.method.orKind == MethodPatternKind.specific)
|
||||
if not result:
|
||||
if a.path.len > b.path.len: return true
|
||||
for i in b.path.low..a.path.high:
|
||||
if a.path[i].orKind != b.path[i].orKind and
|
||||
a.path[i].orKind == PathPatternElementKind.label:
|
||||
return true
|
||||
|
||||
proc match(driver: Driver; req: HttpRequest): Option[HttpBinding] =
|
||||
var b: HttpBinding
|
||||
for p in driver.bindings:
|
||||
if b.fromPreserves(p) and b.match req:
|
||||
if result.isNone or b.strongerThan(result.get):
|
||||
result = some b
|
||||
|
||||
method message(e: Exchange; turn: Turn; a: AssertionRef) =
|
||||
# Send responses back into a connection.
|
||||
var res: HttpResponse
|
||||
if e.mode != HttpResponseKind.done and res.fromPreserves a.value:
|
||||
case res.orKind
|
||||
|
||||
of HttpResponseKind.status:
|
||||
if e.mode == res.orKind:
|
||||
e.active = true
|
||||
e.ses.conn.startBatch()
|
||||
e.stream.write(
|
||||
SupportedVersion, " ", res.status.code, " ", res.status.message, CRLF,
|
||||
"date: ", now().format(IMF), CRLF)
|
||||
# add Date header automatically - RFC 9110 Section 6.6.1.
|
||||
e.mode = HttpResponseKind.header
|
||||
|
||||
of HttpResponseKind.header:
|
||||
if e.mode == res.orKind:
|
||||
e.stream.write(res.header.name, ": ", res.header.value, CRLF)
|
||||
|
||||
of HttpResponseKind.chunk:
|
||||
if res.chunk.chunk.len > 0:
|
||||
if e.mode == HttpResponseKind.header:
|
||||
e.stream.write("transfer-encoding: chunked" & CRLF & CRLF)
|
||||
e.ses.send(move e.stream.data)
|
||||
e.mode = res.orKind
|
||||
if e.mode == res.orKind:
|
||||
e.ses.send(res.chunk.chunk.lenLine)
|
||||
e.ses.send(res.chunk.chunk)
|
||||
e.ses.send(CRLF)
|
||||
|
||||
of HttpResponseKind.done:
|
||||
if e.mode in {HttpResponseKind.header, HttpResponseKind.chunk}:
|
||||
if e.mode == HttpResponseKind.header:
|
||||
e.stream.write("content-length: ", $res.done.chunk.len & CRLF & CRLF)
|
||||
e.ses.send(move e.stream.data)
|
||||
if res.done.chunk.len > 0:
|
||||
e.ses.send(res.done.chunk)
|
||||
elif e.mode == HttpResponseKind.chunk:
|
||||
e.ses.send(res.done.chunk.lenLine)
|
||||
if res.done.chunk.len > 0:
|
||||
e.ses.send(res.done.chunk)
|
||||
e.ses.send(CRLF & "0" & CRLF & CRLF)
|
||||
e.mode = res.orKind
|
||||
e.ses.conn.endBatch()
|
||||
if e.req.headers.getOrDefault(Symbol"connection") == "close":
|
||||
e.ses.conn.close()
|
||||
stop(turn)
|
||||
# stop the facet scoped to the exchange
|
||||
# so that the response capability is withdrawn
|
||||
|
||||
proc service(turn: Turn; exch: Exchange) =
|
||||
## Service an HTTP message exchange.
|
||||
var binding = exch.ses.driver.match exch.req
|
||||
if binding.isNone:
|
||||
stop(turn)
|
||||
else:
|
||||
var handler = binding.get.handler.unembed Cap
|
||||
if handler.isNone:
|
||||
stop(turn)
|
||||
else:
|
||||
let cap = newCap(turn, exch)
|
||||
publish(turn, handler.get, HttpContext(
|
||||
req: exch.req,
|
||||
res: embed cap,
|
||||
))
|
||||
const timeout = initDuration(seconds = 4)
|
||||
after(turn, exch.ses.driver.timers, timeout) do (turn: Turn):
|
||||
if not exch.active:
|
||||
var res = HttpResponse(orKind: HttpResponseKind.status)
|
||||
res.status.code = 504
|
||||
res.status.message = "Binding timeout"
|
||||
message(turn, cap, res)
|
||||
res = HttpResponse(orKind: HttpResponseKind.done)
|
||||
message(turn, cap, res)
|
||||
|
||||
proc service(ses: Session) =
|
||||
## Service a connection to an HTTP client.
|
||||
ses.facet.onStop do (turn: Turn):
|
||||
close ses.conn
|
||||
ses.conn.onClosed do ():
|
||||
stop ses.facet
|
||||
ses.conn.onReceivedPartial do (data: seq[byte]; ctx: MessageContext; eom: bool):
|
||||
ses.facet.run do (turn: Turn):
|
||||
var (n, req) = parseRequest(ses.conn, cast[string](data))
|
||||
if n > 0:
|
||||
inc(ses.driver.sequenceNumber)
|
||||
req.sequenceNumber = ses.driver.sequenceNumber
|
||||
req.port = BiggestInt ses.port
|
||||
inFacet(turn) do (turn: Turn):
|
||||
preventInertCheck(turn)
|
||||
# start a new facet for this message exchange
|
||||
turn.service Exchange(
|
||||
facet: turn.facet,
|
||||
ses: ses,
|
||||
req: req,
|
||||
stream: newStringStream(),
|
||||
mode: HttpResponseKind.status
|
||||
)
|
||||
ses.conn.receive()
|
||||
ses.conn.receive()
|
||||
|
||||
proc newListener(port: Port): Listener =
|
||||
var lp = newLocalEndpoint()
|
||||
lp.with port
|
||||
listen newPreconnection(local=[lp])
|
||||
|
||||
proc httpListen(turn: Turn; driver: Driver; port: Port): Listener =
|
||||
let facet = turn.facet
|
||||
var listener = newListener(port)
|
||||
preventInertCheck(turn)
|
||||
listener.onListenError do (err: ref Exception):
|
||||
terminateFacet(facet, err)
|
||||
facet.onStop do (turn: Turn):
|
||||
stop listener
|
||||
listener.onConnectionReceived do (conn: Connection):
|
||||
driver.facet.run do (turn: Turn):
|
||||
# start a new turn
|
||||
linkActor(turn, "http-conn") do (turn: Turn):
|
||||
preventInertCheck(turn)
|
||||
let facet = turn.facet
|
||||
conn.onConnectionError do (err: ref Exception):
|
||||
terminateFacet(facet, err)
|
||||
# terminate this actor on exception
|
||||
# facet is scoped to the lifetime of the connection
|
||||
service Session(
|
||||
facet: turn.facet,
|
||||
driver: driver,
|
||||
conn: conn,
|
||||
port: port,
|
||||
)
|
||||
listener
|
||||
|
||||
proc httpDriver(turn: Turn; ds: Cap) =
|
||||
let driver = Driver(facet: turn.facet, ds: ds, timers: turn.newDataspace)
|
||||
spawnTimerDriver(turn, driver.timers)
|
||||
|
||||
during(turn, ds, HttpBinding?:{
|
||||
1: grab(),
|
||||
}) do (port: BiggestInt):
|
||||
publish(turn, ds, HttpListener(port: port))
|
||||
|
||||
during(turn, ds, ?:HttpBinding) do (
|
||||
ho: HostPattern, po: int, me: MethodPattern, pa: PathPattern, e: Value):
|
||||
let b = HttpBinding(host: ho, port: po, `method`: me, path: pa, handler: e)
|
||||
discard driver.bindings.change(b.toPreserves, +1)
|
||||
do:
|
||||
discard driver.bindings.change(b.toPreserves, -1)
|
||||
|
||||
during(turn, ds, ?:HttpListener) do (port: uint16):
|
||||
let l = httpListen(turn, driver, Port port)
|
||||
do:
|
||||
stop(l)
|
||||
|
||||
proc spawnHttpDriver*(turn: Turn; ds: Cap): Actor {.discardable.} =
|
||||
spawnActor(turn, "http-driver") do (turn: Turn):
|
||||
httpDriver(turn, ds)
|
|
@ -1,23 +1,148 @@
|
|||
# SPDX-FileCopyrightText: 2021 ☭ Emery Hemingway
|
||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[asyncdispatch, monotimes, times]
|
||||
import preserves, preserves/records
|
||||
import syndicate, syndicate/assertions
|
||||
import ../../syndicate/protocols/schemas/timer
|
||||
import std/[tables, times]
|
||||
import preserves
|
||||
import ../../syndicate, ../protocols/[timer, dataspace]
|
||||
|
||||
syndicate timerDriver:
|
||||
when defined(solo5):
|
||||
import solo5_dispatcher
|
||||
else:
|
||||
import pkg/sys/[handles, ioqueue]
|
||||
|
||||
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)
|
||||
export timer
|
||||
|
||||
type
|
||||
Observe = dataspace.Observe
|
||||
|
||||
when defined(solo5):
|
||||
import solo5, solo5_dispatcher
|
||||
|
||||
proc wallFloat: float =
|
||||
solo5_clock_wall().float / 1_000_000_000.0
|
||||
|
||||
type
|
||||
TimerDriver = ref object
|
||||
facet: Facet
|
||||
## Owning facet of driver.
|
||||
target: Cap
|
||||
## Destination for LaterThan assertions.
|
||||
deadlines: Table[float, Facet]
|
||||
## Deadlines that other actors are observing.
|
||||
|
||||
proc spawnTimerDriver(facet: Facet; cap: Cap): TimerDriver =
|
||||
TimerDriver(facet: facet, target: cap)
|
||||
|
||||
proc await(driver: TimerDriver; deadline: float) {.solo5dispatch.} =
|
||||
yieldUntil(deadline)
|
||||
let facet = driver.deadlines.getOrDefault(deadline)
|
||||
if not facet.isNil:
|
||||
# check if the deadline is still observed
|
||||
proc turnWork(turn: Turn) =
|
||||
discard publish(turn, driver.target, LaterThan(seconds: deadline))
|
||||
run(facet, turnWork)
|
||||
|
||||
else:
|
||||
import std/[oserrors, posix, sets]
|
||||
type Time = posix.Time
|
||||
|
||||
{.pragma: timerfd, importc, header: "<sys/timerfd.h>".}
|
||||
|
||||
proc timerfd_create(clock_id: ClockId, flags: cint): cint {.timerfd.}
|
||||
proc timerfd_settime(ufd: cint, flags: cint,
|
||||
utmr: var Itimerspec, otmr: var Itimerspec): cint {.timerfd.}
|
||||
proc timerfd_gettime(ufd: cint, curr: var Itimerspec): cint {.timerfd.}
|
||||
|
||||
var
|
||||
TFD_NONBLOCK {.timerfd.}: cint
|
||||
TFD_CLOEXEC {.timerfd.}: cint
|
||||
TFD_TIMER_ABSTIME {.timerfd.}: cint
|
||||
|
||||
proc `<`(a, b: Timespec): bool =
|
||||
a.tv_sec.clong < b.tv_sec.clong or
|
||||
(a.tv_sec.clong == b.tv_sec.clong and a.tv_nsec < b.tv_nsec)
|
||||
|
||||
proc `+`(a, b: Timespec): Timespec =
|
||||
result.tv_sec = Time a.tv_sec.clong + b.tv_sec.clong
|
||||
result.tv_nsec = a.tv_nsec + b.tv_nsec
|
||||
|
||||
func toFloat(ts: Timespec): float =
|
||||
ts.tv_sec.float + ts.tv_nsec.float / 1_000_000_000
|
||||
|
||||
func toTimespec(f: float): Timespec =
|
||||
result.tv_sec = Time(f)
|
||||
result.tv_nsec = clong(uint64(f * 1_000_000_000) mod 1_000_000_000)
|
||||
|
||||
proc wallFloat: float =
|
||||
var ts: Timespec
|
||||
if clock_gettime(CLOCK_REALTIME, ts) < 0:
|
||||
raiseOSError(osLastError(), "clock_gettime")
|
||||
ts.toFloat
|
||||
|
||||
type
|
||||
TimerDriver = ref object
|
||||
facet: Facet
|
||||
## Owning facet of driver.
|
||||
target: Cap
|
||||
## Destination for LaterThan assertions.
|
||||
deadlines: Table[float, Facet]
|
||||
## Deadlines that other actors are observing.
|
||||
timers: HashSet[cint]
|
||||
# TODO: use a single timer descriptor
|
||||
|
||||
proc spawnTimerDriver(facet: Facet; cap: Cap): TimerDriver =
|
||||
let driver = TimerDriver(facet: facet, target: cap)
|
||||
facet.onStop do (turn: Turn):
|
||||
for fd in driver.timers:
|
||||
unregister(FD fd)
|
||||
discard close(fd)
|
||||
driver
|
||||
|
||||
proc earliestFloat(driver: TimerDriver): float =
|
||||
assert driver.deadlines.len > 0
|
||||
result = high float
|
||||
for deadline in driver.deadlines.keys:
|
||||
if deadline < result:
|
||||
result = deadline
|
||||
|
||||
proc await(driver: TimerDriver; deadline: float) {.asyncio.} =
|
||||
## Run timer driver concurrently with actor.
|
||||
let fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK or TFD_CLOEXEC)
|
||||
if fd < 0:
|
||||
raiseOSError(osLastError(), "failed to acquire timer descriptor")
|
||||
var
|
||||
old: Itimerspec
|
||||
its = Itimerspec(it_value: deadline.toTimespec)
|
||||
if timerfd_settime(fd, TFD_TIMER_ABSTIME, its, old) < 0:
|
||||
raiseOSError(osLastError(), "failed to set timeout")
|
||||
driver.timers.incl(fd)
|
||||
while wallFloat() < deadline:
|
||||
# Check if the timer is expired which
|
||||
# could happen before waiting.
|
||||
wait(FD fd, Read)
|
||||
let facet = driver.deadlines.getOrDefault(deadline)
|
||||
if not facet.isNil:
|
||||
# Check if the deadline is still observed.
|
||||
proc turnWork(turn: Turn) =
|
||||
discard publish(turn, driver.target, LaterThan(seconds: deadline))
|
||||
run(facet, turnWork)
|
||||
discard close(fd)
|
||||
driver.timers.excl(fd)
|
||||
|
||||
proc spawnTimerDriver*(turn: Turn; ds: Cap): Actor {.discardable.} =
|
||||
## Spawn a timer actor that responds to
|
||||
## dataspace observations of timeouts on `ds`.
|
||||
linkActor(turn, "timers") do (turn: Turn):
|
||||
let driver = spawnTimerDriver(turn.facet, ds)
|
||||
let pat = observePattern(!LaterThan, {@[0.toPreserves]: grabLit()})
|
||||
during(turn, ds, pat) do (deadline: float):
|
||||
driver.deadlines[deadline] = turn.facet
|
||||
discard trampoline(whelp await(driver, deadline))
|
||||
do:
|
||||
driver.deadlines.del deadline
|
||||
|
||||
proc after*(turn: Turn; ds: Cap; dur: Duration; act: TurnAction) =
|
||||
## Execute `act` after some duration of time.
|
||||
var later = wallFloat() + dur.inMilliseconds.float / 1_000.0
|
||||
onPublish(turn, ds, ?LaterThan(seconds: later)):
|
||||
act(turn)
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[hashes, tables]
|
||||
import preserves
|
||||
import ./actors, ./patterns, ./protocols/dataspace
|
||||
|
||||
from ./protocols/protocol import Handle
|
||||
|
||||
type
|
||||
Observe = dataspace.Observe[Ref]
|
||||
Turn = actors.Turn
|
||||
|
||||
type
|
||||
DuringProc* = proc (turn: var Turn; a: Assertion; h: Handle): TurnAction {.gcsafe.}
|
||||
DuringProc* = proc (turn: Turn; a: Value; h: Handle): TurnAction
|
||||
DuringActionKind = enum null, dead, act
|
||||
DuringAction = object
|
||||
case kind: DuringActionKind
|
||||
|
@ -23,20 +17,21 @@ type
|
|||
cb: DuringProc
|
||||
assertionMap: Table[Handle, DuringAction]
|
||||
|
||||
method publish(de: DuringEntity; turn: var Turn; a: Assertion; h: Handle) =
|
||||
let action = de.cb(turn, a, h)
|
||||
# assert(not action.isNil "should have put in a no-op action")
|
||||
let g = de.assertionMap.getOrDefault h
|
||||
case g.kind
|
||||
of null:
|
||||
de.assertionMap[h] = DuringAction(kind: act, action: action)
|
||||
of dead:
|
||||
de.assertionMap.del h
|
||||
freshen(turn, action)
|
||||
of act:
|
||||
raiseAssert("during: duplicate handle in publish: " & $h)
|
||||
method publish(de: DuringEntity; turn: Turn; a: AssertionRef; h: Handle) =
|
||||
discard inFacet(turn) do (turn: Turn):
|
||||
let action = de.cb(turn, a.value, h)
|
||||
# assert(not action.isNil "should have put in a no-op action")
|
||||
let g = de.assertionMap.getOrDefault h
|
||||
case g.kind
|
||||
of null:
|
||||
de.assertionMap[h] = DuringAction(kind: act, action: action)
|
||||
of dead:
|
||||
de.assertionMap.del h
|
||||
action(turn)
|
||||
of act:
|
||||
raiseAssert("during: duplicate handle in publish: " & $h)
|
||||
|
||||
method retract(de: DuringEntity; turn: var Turn; h: Handle) =
|
||||
method retract(de: DuringEntity; turn: Turn; h: Handle) =
|
||||
let g = de.assertionMap.getOrDefault h
|
||||
case g.kind
|
||||
of null:
|
||||
|
@ -50,5 +45,5 @@ method retract(de: DuringEntity; turn: var Turn; h: Handle) =
|
|||
|
||||
proc during*(cb: DuringProc): DuringEntity = DuringEntity(cb: cb)
|
||||
|
||||
proc observe*(turn: var Turn; ds: Ref; pat: Pattern; e: Entity): Handle =
|
||||
publish(turn, ds, Observe(pattern: pat, observer: embed newRef(turn, e)))
|
||||
proc observe*(turn: Turn; ds: Cap; pat: Pattern; e: Entity): Handle =
|
||||
publish(turn, ds, Observe(pattern: pat, observer: newCap(turn, e)))
|
||||
|
|
|
@ -1,44 +1,45 @@
|
|||
# SPDX-FileCopyrightText: ☭ 2022 Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[hashes, options, tables]
|
||||
import std/[hashes, tables]
|
||||
|
||||
from ./actors import Ref, hash
|
||||
import ./actors
|
||||
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
|
||||
Membrane* = object
|
||||
## Bidirectional mapping between `Oid` and `Ref` values.
|
||||
## Bidirectional mapping between `Oid` and `Cap` values.
|
||||
## https://synit.org/book/protocol.html#membranes
|
||||
byOid: Table[Oid, WireSymbol]
|
||||
byRef: Table[Ref, WireSymbol]
|
||||
byCap: Table[Cap, WireSymbol]
|
||||
|
||||
WireSymbol* = ref object
|
||||
oid: Oid
|
||||
`ref`: Ref
|
||||
cap: Cap
|
||||
count: int
|
||||
|
||||
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 =
|
||||
## Grab a `WireSymbol` from a `Membrane`.
|
||||
mem.byOid.getOrDefault(key)
|
||||
|
||||
proc grab*(mem: Membrane; key: Ref): WireSymbol =
|
||||
proc grab*(mem: Membrane; key: Cap): WireSymbol =
|
||||
## Grab a `WireSymbol` from a `Membrane`.
|
||||
mem.byRef.getOrDefault(key)
|
||||
mem.byCap.getOrDefault(key)
|
||||
|
||||
proc drop*(mem: var Membrane; sym: WireSymbol) =
|
||||
## Drop a `WireSymbol` from a `Membrane`.
|
||||
dec sym.count
|
||||
if sym.count < 1:
|
||||
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`.
|
||||
result = WireSymbol(oid: o, `ref`: r, count: 1)
|
||||
result = WireSymbol(oid: o, cap: r, count: 1)
|
||||
mem.byOid[result.oid] = result
|
||||
mem.byRef[result.`ref`] = result
|
||||
mem.byCap[result.cap] = result
|
||||
|
|
|
@ -1,434 +1,445 @@
|
|||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[sequtils, tables, typetraits]
|
||||
import std/[assertions, options, tables, typetraits]
|
||||
|
||||
import preserves
|
||||
import ./protocols/dataspacePatterns
|
||||
from ./actors import Ref
|
||||
import ./protocols/[dataspacePatterns, dataspace]
|
||||
from ./actors import Cap
|
||||
|
||||
export dataspacePatterns.`$`, PatternKind, DCompoundKind, AnyAtomKind
|
||||
export dataspacePatterns.`$`, AnyAtomKind, GroupTypeKind, PatternKind
|
||||
|
||||
type
|
||||
Assertion = Preserve[Ref]
|
||||
AnyAtom* = dataspacePatterns.AnyAtom[Ref]
|
||||
DBind* = dataspacePatterns.DBind[Ref]
|
||||
DCompound* = dataspacePatterns.DCompound[Ref]
|
||||
DCompoundArr* = dataspacePatterns.DCompoundArr[Ref]
|
||||
DCompoundDict* = dataspacePatterns.DCompoundDict[Ref]
|
||||
DCompoundRec* = dataspacePatterns.DCompoundRec[Ref]
|
||||
DLit* = dataspacePatterns.DLit[Ref]
|
||||
Pattern* = dataspacePatterns.Pattern[Ref]
|
||||
Pattern* = dataspacePatterns.Pattern
|
||||
|
||||
proc `?`*(d: sink DBind): Pattern =
|
||||
Pattern(orKind: PatternKind.DBind, dbind: d)
|
||||
proc toPattern(b: sink PatternBind): Pattern =
|
||||
Pattern(orKind: PatternKind.`bind`, `bind`: b)
|
||||
|
||||
proc `?`*(d: sink DLit): Pattern =
|
||||
Pattern(orKind: PatternKind.DLit, dlit: d)
|
||||
proc toPattern(l: sink PatternLit): Pattern =
|
||||
Pattern(orKind: PatternKind.`lit`, lit: l)
|
||||
|
||||
proc `?`*(aa: sink AnyAtom): Pattern =
|
||||
?DLit(value: aa)
|
||||
proc toPattern(g: sink PatternGroup): Pattern =
|
||||
Pattern(orKind: PatternKind.`group`, group: g)
|
||||
|
||||
proc `?`*(d: sink DCompound): Pattern =
|
||||
Pattern(orKind: PatternKind.DCompound, dcompound: d)
|
||||
proc toPattern(a: sink AnyAtom): Pattern =
|
||||
PatternLit(value: a).toPattern
|
||||
|
||||
proc `?`*(d: sink DCompoundRec): Pattern =
|
||||
?DCompound(orKind: DCompoundKind.rec, rec: d)
|
||||
proc grab*(p: sink Pattern): Pattern =
|
||||
PatternBind(pattern: p).toPattern
|
||||
|
||||
proc `?`*(d: sink DCompoundArr): Pattern =
|
||||
?DCompound(orKind: DCompoundKind.arr, arr: d)
|
||||
|
||||
proc `?`*(d: sink DCompoundDict): Pattern =
|
||||
?DCompound(orKind: DCompoundKind.dict, dict: d)
|
||||
|
||||
proc `?`*(x: bool): Pattern =
|
||||
?AnyAtom(orKind: AnyAtomKind.`bool`, bool: x)
|
||||
|
||||
proc `?`*(x: float32): Pattern =
|
||||
?AnyAtom(orKind: AnyAtomKind.`float`, float: x)
|
||||
|
||||
proc `?`*(x: float64): Pattern =
|
||||
?AnyAtom(orKind: AnyAtomKind.`double`, double: x)
|
||||
|
||||
proc `?`*(x: int): Pattern =
|
||||
?AnyAtom(orKind: AnyAtomKind.`int`, int: x)
|
||||
|
||||
proc `?`*(s: sink string): Pattern =
|
||||
?AnyAtom(orKind: AnyAtomKind.`string`, string: s)
|
||||
|
||||
proc `?`*(x: sink seq[byte]): Pattern =
|
||||
?AnyAtom(orKind: AnyAtomKind.`bytes`, bytes: x)
|
||||
|
||||
proc `?`*(x: sink Symbol): Pattern =
|
||||
?AnyAtom(orKind: AnyAtomKind.`symbol`, symbol: x)
|
||||
|
||||
proc `?`*[T](pr: Preserve[T]): Pattern =
|
||||
## Convert a `Preserve` value to a `Pattern`.
|
||||
assert not pr.embedded
|
||||
case pr.kind
|
||||
of pkBoolean: ?pr.bool
|
||||
of pkFloat: ?pr.float
|
||||
of pkDouble: ?pr.double
|
||||
of pkSignedInteger: ?(int pr.int) # TODO: overflow!
|
||||
of pkString: ?pr.string
|
||||
of pkByteString: ?pr.bytes
|
||||
of pkSymbol: ?pr.symbol
|
||||
of pkRecord:
|
||||
?DCompoundRec(
|
||||
label: pr.label,
|
||||
fields: map[Preserve[T], Pattern](pr.fields, `?`))
|
||||
of pkSequence:
|
||||
?DCompoundArr(items: map(pr.sequence, `?`))
|
||||
of pkSet: raise newException(
|
||||
ValueError, "cannot construct a pattern over a set literal")
|
||||
of pkDictionary:
|
||||
var dict = DCompoundDict()
|
||||
for key, val in pr.pairs: dict.entries[key] = ?val
|
||||
?dict
|
||||
of pkEmbedded:
|
||||
raiseAssert "cannot construct a pattern over a embedded literal"
|
||||
|
||||
proc drop*(): Pattern = Pattern(orKind: PatternKind.DDiscard)
|
||||
proc drop*(): Pattern = Pattern(orKind: PatternKind.`discard`)
|
||||
## Create a pattern to match any value without capture.
|
||||
|
||||
proc grab*(): Pattern = ?DBind(pattern: drop())
|
||||
proc grab*(): Pattern = drop().grab()
|
||||
## Create a pattern to capture any value.
|
||||
|
||||
proc `?`*[A, B](table: TableRef[A,B]): Pattern =
|
||||
raiseAssert "not implemented"
|
||||
proc drop*(pr: Value): Pattern =
|
||||
## Convert a `Preserve` value to a `Pattern`.
|
||||
runnableExamples:
|
||||
from std/unittest import check
|
||||
import preserves
|
||||
check:
|
||||
$("""<foo "bar" #"00" [0 1 2.0] {maybe: #t} <_>>""".parsePreserves.drop) ==
|
||||
"""<group <rec foo> {0: <lit "bar"> 1: <lit #"00"> 2: <group <arr> {0: <lit 0> 1: <lit 1> 2: <lit 2.0>}> 3: <group <dict> {maybe: <lit #t>}> 4: <_>}>"""
|
||||
|
||||
#[
|
||||
proc `?`*(pat: sink Pattern): Pattern =
|
||||
## Construct a `Pattern` that matches a `Pattern`.
|
||||
case pat.orKind
|
||||
of PatternKind.DDiscard: result = pat
|
||||
of PatternKind.DBind: result = drop()
|
||||
of PatternKind.DLit: result = ?pat.toPreserve(Ref)
|
||||
of PatternKind.DCompound:
|
||||
case pat.dcompound.orKind:
|
||||
of DCompoundKind.rec:
|
||||
var fields = move pat.dcompound.rec.fields
|
||||
for f in fields.mitems: f = ?(move f)
|
||||
result = ?pat.toPreserve(Ref)
|
||||
# echo "need to stuff fields into ", result, " at ", result.dcompound.rec.fields[1].dcompound.arr.items
|
||||
result.dcompound.rec.fields[1].dcompound.arr.items = fields
|
||||
#[
|
||||
of DCompoundKind.arr
|
||||
pat.dcompound.arr
|
||||
of DCompoundKind.dict
|
||||
pat.dcompound.dict
|
||||
]#
|
||||
else: raiseAssert "`?` not implemented for pattern " & $pat
|
||||
]#
|
||||
case pr.kind
|
||||
of pkBoolean:
|
||||
AnyAtom(orKind: AnyAtomKind.`bool`, bool: pr.bool).toPattern
|
||||
of pkFloat:
|
||||
AnyAtom(orKind: AnyAtomKind.`double`, double: pr.float).toPattern
|
||||
of pkRegister:
|
||||
AnyAtom(orKind: AnyAtomKind.`int`, int: pr.register).toPattern
|
||||
of pkBigInt:
|
||||
raiseAssert "cannot make a pattern over a big integer"
|
||||
of pkString:
|
||||
AnyAtom(orKind: AnyAtomKind.`string`, string: pr.string).toPattern
|
||||
of pkByteString:
|
||||
AnyAtom(orKind: AnyAtomKind.`bytes`, bytes: pr.bytes).toPattern
|
||||
of pkSymbol:
|
||||
AnyAtom(orKind: AnyAtomKind.`symbol`, symbol: pr.symbol).toPattern
|
||||
|
||||
#[
|
||||
proc `?`*(patterns: openArray[Pattern]): Pattern =
|
||||
raiseAssert "got it in the right place"
|
||||
#[
|
||||
result = DCompoundArr()
|
||||
for e in val:
|
||||
if i > arr.items.high: arr.items.setLen(succ i)
|
||||
arr.items[i] = pat
|
||||
for pat in arr.items.mitems:
|
||||
if pat.isNil: pat = drop()
|
||||
result = ?DCompound(
|
||||
orKind: DCompoundKind.arr,
|
||||
arr: arr)
|
||||
]#
|
||||
]#
|
||||
of pkRecord:
|
||||
if pr.isRecord("_", 0):
|
||||
drop()
|
||||
elif pr.isRecord("bind", 1):
|
||||
pr.fields[0].drop
|
||||
else:
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.rec))
|
||||
group.`type`.rec.label = pr.label
|
||||
var i: int
|
||||
for v in pr.fields:
|
||||
group.entries[toPreserves i] = drop v
|
||||
inc i
|
||||
group.toPattern
|
||||
|
||||
proc `?`*[T](val: sink T): Pattern =
|
||||
of pkSequence:
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.arr))
|
||||
for i, v in pr.sequence:
|
||||
group.entries[toPreserves i] = drop v
|
||||
group.toPattern
|
||||
|
||||
of pkSet:
|
||||
raiseAssert "cannot construct a pattern over a set literal"
|
||||
|
||||
of pkDictionary:
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.dict))
|
||||
for key, val in pr.pairs:
|
||||
group.entries[key] = drop val
|
||||
group.toPattern
|
||||
|
||||
of pkEmbedded:
|
||||
if pr.embeddedRef.isNil: drop()
|
||||
else:
|
||||
AnyAtom(orKind: AnyAtomKind.`embedded`, embedded: pr.embeddedRef).toPattern
|
||||
#else:
|
||||
# raise newException(ValueError, "cannot generate a pattern for unhandled Value type")
|
||||
|
||||
proc drop*[T](x: T): Pattern =
|
||||
## Construct a `Pattern` from value of type `T`.
|
||||
#[
|
||||
when T is Pattern:
|
||||
case val.orKind
|
||||
of PatternKind.DDiscard, PatternKind.DBind: result = val
|
||||
of PatternKind.DLit: result = ?val.toPreserve(Ref)
|
||||
of PatternKind.DCompound:
|
||||
case val.dcompound.orKind:
|
||||
of DCompoundKind.rec:
|
||||
var fields = move val.dcompound.rec.fields
|
||||
for f in fields.mitems: f = ?(move f)
|
||||
result = ?val.toPreserve(Ref)
|
||||
# echo "need to stuff fields into ", result, " at ", result.dcompound.rec.fields[1].dcompound.arr.items
|
||||
result.dcompound.rec.fields[1].dcompound.arr.items = fields
|
||||
#[
|
||||
of DCompoundKind.arr
|
||||
val.dcompound.arr
|
||||
of DCompoundKind.dict
|
||||
val.dcompound.dict
|
||||
]#
|
||||
else: raiseAssert "`?` not implemented for pattern " & $val
|
||||
]#
|
||||
when T is Ref:
|
||||
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
|
||||
value: AnyAtom(
|
||||
orKind: AnyAtomKind.embedded,
|
||||
embedded: embed(val))))
|
||||
elif T is ptr | ref:
|
||||
if system.`==`(val, nil): result = ?(Symbol "null")
|
||||
else: result = ?(val[])
|
||||
elif T is bool:
|
||||
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
|
||||
value: AnyAtom(
|
||||
orKind: AnyAtomKind.bool,
|
||||
bool: val)))
|
||||
elif T is float32:
|
||||
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
|
||||
value: AnyAtom(
|
||||
orKind: AnyAtomKind.float,
|
||||
float: val)))
|
||||
elif T is float64:
|
||||
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
|
||||
value: AnyAtom(
|
||||
orKind: AnyAtomKind.double,
|
||||
double: val)))
|
||||
elif T is SomeInteger:
|
||||
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
|
||||
value: AnyAtom(
|
||||
orKind: AnyAtomKind.int,
|
||||
int: AnyAtomInt val)))
|
||||
elif T is string:
|
||||
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
|
||||
value: AnyAtom(
|
||||
orKind: AnyAtomKind.string,
|
||||
string: val)))
|
||||
elif T is seq[byte]:
|
||||
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
|
||||
value: AnyAtom(
|
||||
orKind: AnyAtomKind.bytes,
|
||||
bytes: val)))
|
||||
elif T is array | seq:
|
||||
let arr = DCompoundArr(items: newSeq[Pattern](val.len))
|
||||
for i, e in val.mitems: arr.items[i] = ?(move e)
|
||||
result = ?arr
|
||||
elif T is Symbol:
|
||||
result = Pattern(orKind: PatternKind.DLit, dlit: DLit(
|
||||
value: AnyAtom(
|
||||
orKind: AnyAtomKind.symbol,
|
||||
symbol: Symbol $val)))
|
||||
elif T.hasPreservesRecordPragma:
|
||||
var
|
||||
label = T.recordLabel.tosymbol(Ref)
|
||||
fields = newSeq[Pattern]()
|
||||
for f in fields(val):
|
||||
fields.add ?f
|
||||
result = ?DCompound(
|
||||
orKind: DCompoundKind.rec,
|
||||
rec: DCompoundRec(
|
||||
label: label, fields: fields))
|
||||
else:
|
||||
?(toPreserve(val, Ref))
|
||||
## This proc is called `drop` because the value `x` is matched but discarded.
|
||||
runnableExamples:
|
||||
from std/unittest import check
|
||||
check:
|
||||
$drop(true) == "<lit #t>"
|
||||
$drop(3.14) == "<lit 3.14>"
|
||||
$drop([0, 1, 2, 3]) == "<group <arr> {0: <lit 0> 1: <lit 1> 2: <lit 2> 3: <lit 3>}>"
|
||||
drop(x.toPreserves)
|
||||
|
||||
proc `?`*(T: static typedesc): Pattern =
|
||||
## Derive a `Pattern` from type `T`.
|
||||
proc grab*[T](x: T): Pattern {.
|
||||
deprecated: "use drop unless you wish to capture the provided value".} =
|
||||
PatternBind(pattern: drop x).toPattern
|
||||
|
||||
proc grabTypeFlat*(typ: static typedesc): Pattern =
|
||||
## Derive a `Pattern` from type `typ`.
|
||||
## This works for `tuple` and `object` types but in the
|
||||
## general case will return a wildcard binding.
|
||||
runnableExamples:
|
||||
import preserves
|
||||
|
||||
type Point = tuple[x: int; y: int]
|
||||
assert $(?Point) == "<arr [<bind <_>> <bind <_>>]>"
|
||||
|
||||
type Rect {.preservesRecord: "rect".} = tuple[a: Point; B: Point]
|
||||
assert $(?Rect) == "<rec rect [<arr [<bind <_>> <bind <_>>]> <arr [<bind <_>> <bind <_>>]>]>"
|
||||
|
||||
type ColoredRect {.preservesDictionary.} = tuple[color: string; rect: Rect]
|
||||
assert $(?ColoredRect) == "<dict {color: <bind <_>>, rect: <rec rect [<arr [<bind <_>> <bind <_>>]> <arr [<bind <_>> <bind <_>>]>]>}>"
|
||||
when T is Pattern:
|
||||
raiseAssert "? for pattern"
|
||||
elif T is ref:
|
||||
?pointerBase(T)
|
||||
elif T is array:
|
||||
var arr = DCompoundArr(items: newSeq[Pattern](len(T)))
|
||||
for p in arr.items.mitems: p = grab()
|
||||
result = ?arr
|
||||
elif T.hasPreservesRecordPragma:
|
||||
var
|
||||
label = T.recordLabel.tosymbol(Ref)
|
||||
fields = newSeq[Pattern]()
|
||||
for key, val in fieldPairs(default T):
|
||||
when typeOf(val) is Pattern:
|
||||
fields.add drop()
|
||||
else:
|
||||
fields.add ?(typeOf val)
|
||||
result = ?DCompound(
|
||||
orKind: DCompoundKind.rec,
|
||||
rec: DCompoundRec(
|
||||
label: label, fields: fields))
|
||||
elif T.hasPreservesDictionaryPragma:
|
||||
var dict = DCompoundDict()
|
||||
for key, val in fieldPairs(default T):
|
||||
dict.entries[key.toSymbol(Ref)] = ?(typeOf val)
|
||||
?DCompound(
|
||||
orKind: DCompoundKind.dict,
|
||||
dict: dict)
|
||||
elif T.hasPreservesTuplePragma or T is tuple:
|
||||
raiseAssert "got a tuple"
|
||||
var arr = DCompoundArr()
|
||||
for key, val in fieldPairs(default T):
|
||||
arr.items.add ?(typeOf val)
|
||||
?DCompound(
|
||||
orKind: DCompoundKind.arr,
|
||||
arr: arr)
|
||||
from std/unittest import check
|
||||
check:
|
||||
$grabTypeFlat(array[3, int]) ==
|
||||
"""<group <arr> {0: <bind <_>> 1: <bind <_>> 2: <bind <_>> 3: <bind <_>>}>"""
|
||||
type
|
||||
Point = tuple[x: int; y: int]
|
||||
Rect {.preservesRecord: "rect".} = tuple[a: Point; B: Point]
|
||||
ColoredRect {.preservesDictionary.} = tuple[color: string; rect: Rect]
|
||||
check:
|
||||
$(grabTypeFlat Point) ==
|
||||
"<group <arr> {0: <bind <_>> 1: <bind <_>>}>"
|
||||
$(grabTypeFlat Rect) ==
|
||||
"<group <rec rect> {0: <group <arr> {0: <bind <_>> 1: <bind <_>>}> 1: <group <arr> {0: <bind <_>> 1: <bind <_>>}>}>"
|
||||
$(grabTypeFlat ColoredRect) ==
|
||||
"<group <dict> {color: <bind <_>> rect: <group <rec rect> {0: <group <arr> {0: <bind <_>> 1: <bind <_>>}> 1: <group <arr> {0: <bind <_>> 1: <bind <_>>}>}>}>"
|
||||
when typ is ref:
|
||||
grabTypeFlat(pointerBase(typ))
|
||||
elif typ.hasPreservesRecordPragma:
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||
group.`type`.rec.label = typ.recordLabel.toSymbol
|
||||
for _, f in fieldPairs(default typ):
|
||||
group.entries[group.entries.len.toPreserves] = grabTypeFlat(typeof f)
|
||||
group.toPattern
|
||||
elif typ.hasPreservesDictionaryPragma:
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||
for key, val in fieldPairs(default typ):
|
||||
group.entries[key.toSymbol] = grabTypeFlat(typeof val)
|
||||
group.toPattern
|
||||
elif typ is tuple:
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||
for _, f in fieldPairs(default typ):
|
||||
group.entries[group.entries.len.toPreserves] = grabTypeFlat(typeof f)
|
||||
group.toPattern
|
||||
elif typ is array:
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||
for i in 0..len(typ):
|
||||
group.entries[toPreserves i] = grab()
|
||||
group.toPattern
|
||||
else:
|
||||
grab() # otherwise an abritrary capture
|
||||
grab()
|
||||
|
||||
proc fieldCount(T: typedesc): int =
|
||||
for _, _ in fieldPairs(default T):
|
||||
inc result
|
||||
|
||||
proc `?`*(T: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern =
|
||||
## Construct a `Pattern` from type `T` that selectively captures fields.
|
||||
runnableExamples:
|
||||
import preserves
|
||||
|
||||
type Point = tuple[x: int; y: int; z: int]
|
||||
assert $(Point ? { 2: grab() }) == "<arr [<_> <_> <bind <_>>]>"
|
||||
|
||||
when T is ref:
|
||||
`?`(pointerBase(T), bindings)
|
||||
elif T.hasPreservesRecordPragma:
|
||||
var
|
||||
label = T.recordLabel.tosymbol(Ref)
|
||||
fields = newSeq[Pattern](fieldCount T)
|
||||
for (i, pat) in bindings:
|
||||
fields[i] = pat
|
||||
for pat in fields.mitems:
|
||||
if pat.isNil: pat = drop()
|
||||
result = ?DCompound(
|
||||
orKind: DCompoundKind.rec,
|
||||
rec: DCompoundRec(
|
||||
label: label, fields: fields))
|
||||
elif T is tuple:
|
||||
var arr = DCompoundArr()
|
||||
for (i, pat) in bindings:
|
||||
if i > arr.items.high: arr.items.setLen(succ i)
|
||||
arr.items[i] = pat
|
||||
for pat in arr.items.mitems:
|
||||
if pat.isNil: pat = drop()
|
||||
result = ?DCompound(
|
||||
orKind: DCompoundKind.arr,
|
||||
arr: arr)
|
||||
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 group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||
group.`type`.rec.label = typ.recordLabel.toSymbol
|
||||
let high = typ.fieldCount.pred
|
||||
if high >= 0: group.entries[high.toPreserves] = drop()
|
||||
group.toPattern
|
||||
elif typ.hasPreservesDictionaryPragma:
|
||||
PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`)).toPattern
|
||||
elif typ is tuple or typ is array:
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||
let high = typ.fieldCount.pred
|
||||
if high >= 0: group.entries[high.toPreserves] = drop()
|
||||
group.toPattern
|
||||
else:
|
||||
raiseAssert("no preserves pragma on " & $T)
|
||||
drop()
|
||||
|
||||
#[
|
||||
proc `?`*(pat: sink Pattern; bindings: sink openArray[(int, Pattern)]): Pattern =
|
||||
## Construct a `Pattern` from `pat` with overrides from `bindings`.
|
||||
result = pat
|
||||
assert not result.isNil
|
||||
case result.orKind
|
||||
of PatternKind.DDiscard, PatternKind.DBind: discard
|
||||
of PatternKind.DCompound:
|
||||
case result.dcompound.orKind
|
||||
proc grabType*(typ: static typedesc): Pattern =
|
||||
PatternBind(pattern: typ.dropType).toPattern
|
||||
|
||||
of `rec`:
|
||||
echo "need to override record fields ", result.dcompound.rec.fields, " with ", bindings
|
||||
var foo = result.dcompound.rec.fields[1] ? bindings
|
||||
echo "recursing into fields returned ", foo
|
||||
result.dcompound.rec.fields[1] = foo
|
||||
proc bindEntries(group: var PatternGroup; bindings: openArray[(int, Pattern)]) =
|
||||
## Set `bindings` for a `group`.
|
||||
for (i, pat) in bindings: group.entries[toPreserves i] = pat
|
||||
|
||||
proc grab*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern =
|
||||
## Construct a `Pattern` from type `typ` with pattern `bindings` by integer offset.
|
||||
when typ is ptr | ref:
|
||||
grab(pointerBase(typ), bindings)
|
||||
elif typ.hasPreservesRecordPragma:
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||
group.`type`.rec.label = typ.recordLabel.toSymbol
|
||||
bindEntries(group, bindings)
|
||||
group.toPattern
|
||||
elif typ is tuple:
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||
bindEntries(group, bindings)
|
||||
group.toPattern
|
||||
else:
|
||||
{.error: "grab with indexed bindings not implemented for " & $typ.}
|
||||
|
||||
of `arr`:
|
||||
echo "need to override array items ", result.dcompound.arr.items, " with ", bindings
|
||||
for i in result.dcompound.arr.items.mitems:
|
||||
i = drop()
|
||||
for (i, pat) in bindings:
|
||||
result.dcompound.arr.items[i] = pat
|
||||
proc grab*(typ: static typedesc; bindings: sink openArray[(Value, Pattern)]): Pattern =
|
||||
## Construct a `Pattern` from type `typ` with dictionary field `bindings`.
|
||||
when typ.hasPreservesDictionaryPragma:
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||
for key, val in bindinds: group.entries[key] = val
|
||||
group.toPattern
|
||||
else:
|
||||
{.error: "grab with dictionary bindings not implemented for " & $typ.}
|
||||
|
||||
proc grabLit*(): Pattern =
|
||||
runnableExamples:
|
||||
from std/unittest import check
|
||||
check:
|
||||
$grabLit() == """<group <rec lit> {0: <bind <_>>}>"""
|
||||
grabTypeFlat(dataspacePatterns.PatternLit)
|
||||
|
||||
proc grabDict*(): Pattern =
|
||||
grabTypeFlat(dataspacePatterns.GroupTypeDict)
|
||||
|
||||
proc unpackLiterals*(pr: Value): Value =
|
||||
result = pr
|
||||
apply(result) do (pr: var Value):
|
||||
if pr.isRecord("lit", 1) or pr.isRecord("dict", 1) or pr.isRecord("arr", 1) or pr.isRecord("set", 1):
|
||||
pr = pr.record[0]
|
||||
|
||||
proc inject*(pattern: sink Pattern; p: Pattern; path: varargs[Value, toPreserves]): Pattern =
|
||||
## Inject `p` inside `pattern` at `path`.
|
||||
## Injects are made at offsets indexed by the discard (`<_>`) patterns in `pat`.
|
||||
proc inject(pat: var Pattern; path: openarray[Value]) =
|
||||
if len(path) == 0:
|
||||
pat = p
|
||||
elif pat.orKind != PatternKind.`group`:
|
||||
raise newException(ValueError, "cannot inject along specified path")
|
||||
else:
|
||||
raiseAssert $result.dcompound.orKind
|
||||
else:
|
||||
raiseAssert $result.orKind
|
||||
]#
|
||||
inject(pat.group.entries[path[0]], path[1..path.high])
|
||||
result = pattern
|
||||
inject(result, path)
|
||||
|
||||
proc `??`*(pat: sink Pattern; bindings: sink openArray[(int, Pattern)]): Pattern =
|
||||
## Create a `Pattern` that matches or captures from a `Pattern` over `pat`.
|
||||
proc grabRecord*(label: Value, fields: varargs[Pattern]): Pattern =
|
||||
runnableExamples:
|
||||
from std/unittest import check
|
||||
import preserves
|
||||
check:
|
||||
$grabRecord("Says".toSymbol, grab(), grab()) ==
|
||||
"""<group <rec Says> {0: <bind <_>> 1: <bind <_>>}>"""
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||
group.`type`.rec.label = label
|
||||
for i, f in fields: group.entries[toPreserves i] = f
|
||||
group.toPattern
|
||||
|
||||
type Point* {.preservesRecord: "point".} = object
|
||||
x, y: int
|
||||
proc grabRecord*(label: Value, fields: sink openArray[(int, Pattern)]): Pattern =
|
||||
runnableExamples:
|
||||
from std/unittest import check
|
||||
import preserves
|
||||
check:
|
||||
$grabRecord("Says".toSymbol, {3: grab(), 4: grab()}) ==
|
||||
"""<group <rec Says> {3: <bind <_>> 4: <bind <_>>}>"""
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||
group.`type`.rec.label = label
|
||||
for (i, p) in fields: group.entries[toPreserves i] = p
|
||||
group.toPattern
|
||||
|
||||
assert $(?Point) == "<rec point [<bind <_>> <bind <_>>]>"
|
||||
# Capture two values from a value of `Point`.
|
||||
proc grabRecord*(label: string, fields: varargs[Pattern]): Pattern =
|
||||
## Sugar for creating record patterns.
|
||||
## `label` is converted to a symbol value.
|
||||
grabRecord(label.toSymbol, fields)
|
||||
|
||||
assert $(?Point ?? { 0: ?DLit }) == "<rec rec [<lit point> <arr [<rec lit [<bind <_>>]> <_>]>]>"
|
||||
# Match a pattern of `Point` with a literal value set in the first field and capture that value.
|
||||
proc grabDictionary*(bindings: sink openArray[(Value, Pattern)]): Pattern =
|
||||
## Construct a pattern that grabs some dictionary pairs.
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||
for (key, val) in bindings: group.entries[key] = val
|
||||
group.toPattern
|
||||
|
||||
assert $(?tuple[x: int, y: int] ?? { 1: ?DLit }) == "<rec arr [<arr [<_> <rec lit [<bind <_>>]>]>]>"
|
||||
# Match a pattern over a tuple with a literal value set in the second field and capture that value.
|
||||
proc grabDictionary*(bindings: sink openArray[(string, Pattern)]): Pattern =
|
||||
## Construct a pattern that grabs some dictionary pairs.
|
||||
## Keys are converted from strings to symbols.
|
||||
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||
for (key, val) in bindings: group.entries[toSymbol key] = val
|
||||
group.toPattern
|
||||
|
||||
proc depattern(group: PatternGroup; values: var seq[Value]; index: var int): Value
|
||||
|
||||
proc depattern(pat: Pattern; values: var seq[Value]; index: var int): Value =
|
||||
case pat.orKind
|
||||
of PatternKind.DCompound:
|
||||
case pat.dcompound.orKind:
|
||||
of PatternKind.`discard`:
|
||||
discard
|
||||
of PatternKind.`bind`:
|
||||
if index < values.len:
|
||||
result = move values[index]
|
||||
inc index
|
||||
of PatternKind.`lit`:
|
||||
result = pat.`lit`.value.toPreserves
|
||||
of PatternKind.`group`:
|
||||
result = depattern(pat.group, values, index)
|
||||
|
||||
of DCompoundKind.rec:
|
||||
let fieldsLen = pat.dcompound.rec.fields.len
|
||||
pat.dcompound.rec.fields.setLen 0
|
||||
result = ?pat
|
||||
result.dcompound.rec.fields[1].dcompound.arr.items.setLen fieldsLen
|
||||
for (i, p) in bindings:
|
||||
result.dcompound.rec.fields[1].dcompound.arr.items[i] = p
|
||||
for e in result.dcompound.rec.fields[1].dcompound.arr.items.mitems:
|
||||
if e.isNil: e = drop()
|
||||
proc depattern(group: PatternGroup; values: var seq[Value]; index: var int): Value =
|
||||
case group.`type`.orKind
|
||||
of GroupTypeKind.rec:
|
||||
result = initRecord(group.`type`.rec.label, group.entries.len)
|
||||
var i: int
|
||||
for key, val in group.entries:
|
||||
if i.fromPreserves key:
|
||||
result[i] = depattern(val, values, index)
|
||||
of GroupTypeKind.arr:
|
||||
result = initSequence(group.entries.len)
|
||||
var i: int
|
||||
for key, val in group.entries:
|
||||
if i.fromPreserves key:
|
||||
result[i] = depattern(val, values, index)
|
||||
of GroupTypeKind.dict:
|
||||
result = initDictionary(Cap)
|
||||
for key, val in group.entries:
|
||||
result[key] = depattern(val, values, index)
|
||||
|
||||
of DCompoundKind.arr:
|
||||
let itemsLen = pat.dcompound.arr.items.len
|
||||
pat.dcompound.arr.items.setLen 0
|
||||
result = ?pat
|
||||
echo "override ", result.dcompound.rec.fields[0].dcompound.arr.items
|
||||
result.dcompound.rec.fields[0].dcompound.arr.items.setLen itemsLen
|
||||
for (i, p) in bindings:
|
||||
result.dcompound.rec.fields[0].dcompound.arr.items[i] = p
|
||||
for e in result.dcompound.rec.fields[0].dcompound.arr.items.mitems:
|
||||
if e.isNil: e = drop()
|
||||
proc depattern*(pat: Pattern; values: sink seq[Value]): Value =
|
||||
## Convert a `Pattern` to a `Value` while replacing binds with `values`.
|
||||
runnableExamples:
|
||||
from std/unittest import check
|
||||
import preserves
|
||||
type Foo {.preservesRecord: "foo".} = object
|
||||
a, b: int
|
||||
let pat = grabTypeFlat Foo
|
||||
let val = depattern(pat, @[1.toPreserves, 5.toPreserves])
|
||||
check $val == "<foo 1 5>"
|
||||
var index: int
|
||||
depattern(pat, values, index)
|
||||
|
||||
of DCompoundKind.dict:
|
||||
let keys = pat.dcompound.dict.entries.keys.toSeq
|
||||
clear pat.dcompound.dict.entries
|
||||
result = ?pat
|
||||
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
|
||||
|
||||
func isGroup(pat: Pattern): bool =
|
||||
pat.orKind == PatternKind.`group`
|
||||
|
||||
func isMetaDict(pat: Pattern): bool =
|
||||
pat.orKind == PatternKind.`group` and
|
||||
pat.group.type.orKind == GroupTypeKind.dict
|
||||
|
||||
proc metaApply(result: var Pattern; pat: Pattern; path: openarray[Value], offset: int) =
|
||||
if offset == path.len:
|
||||
result = pat
|
||||
elif result.isGroup and result.group.entries[1.toPreserves].isMetaDict:
|
||||
if offset == path.high:
|
||||
result.group.entries[1.toPreserves].group.entries[path[offset]] = pat
|
||||
else:
|
||||
metaApply(result.group.entries[1.toPreserves].group.entries[path[offset]], pat, path, succ offset)
|
||||
else:
|
||||
raiseAssert "cannot override " & $pat
|
||||
assert result.isGroup, "non-group: " & $result
|
||||
assert result.group.entries[1.toPreserves].isMetaDict, "non-meta-dict: " & $result.group.entries[1.toPreserves]
|
||||
raise newException(ValueError, "cannot inject into non-group pattern " & $result)
|
||||
|
||||
proc recordPattern*(label: Preserve[Ref], fields: varargs[Pattern]): Pattern =
|
||||
?DCompoundRec(label: label, fields: fields.toSeq)
|
||||
proc observePattern*(pat: Pattern; injects: openarray[(seq[Value], Pattern)]): Pattern =
|
||||
result = dropType Observe
|
||||
var meta = pat.toPreserves.drop
|
||||
for (path, pat) in injects:
|
||||
metaApply(meta, pat, path, 0)
|
||||
result.group.entries[0.toPreserves] = meta
|
||||
|
||||
type
|
||||
Value = Preserve[Ref]
|
||||
Path = seq[Value]
|
||||
Path* = seq[Value]
|
||||
Paths* = seq[Path]
|
||||
Captures* = seq[Value]
|
||||
Analysis* = tuple
|
||||
constPaths: seq[Path]
|
||||
presentPaths: Paths
|
||||
constPaths: Paths
|
||||
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; key: int|Value; pat: Pattern) =
|
||||
path.add(key.toPreserve(Ref))
|
||||
func walk(result: var Analysis; path: var Path; key: Value; pat: Pattern) =
|
||||
path.add(key)
|
||||
walk(result, path, pat)
|
||||
discard path.pop
|
||||
|
||||
func walk(result: var Analysis; path: var Path; p: Pattern) =
|
||||
case p.orKind
|
||||
of PatternKind.DCompound:
|
||||
case p.dcompound.orKind
|
||||
of DCompoundKind.rec:
|
||||
for k, e in p.dcompound.rec.fields: walk(result, path, k, e)
|
||||
of DCompoundKind.arr:
|
||||
for k, e in p.dcompound.arr.items: walk(result, path, k, e)
|
||||
of DCompoundKind.dict:
|
||||
for k, e in p.dcompound.dict.entries: walk(result, path, k, e)
|
||||
of PatternKind.DBind:
|
||||
of PatternKind.group:
|
||||
for k, v in p.group.entries: walk(result, path, k, v)
|
||||
of PatternKind.`bind`:
|
||||
result.capturePaths.add(path)
|
||||
walk(result, path, p.dbind.pattern)
|
||||
of PatternKind.DDiscard: discard
|
||||
of PatternKind.DLit:
|
||||
walk(result, path, p.`bind`.pattern)
|
||||
of PatternKind.`discard`:
|
||||
result.presentPaths.add(path)
|
||||
of PatternKind.`lit`:
|
||||
result.constPaths.add(path)
|
||||
result.constValues.add(p.dlit.value.toPreserve(Ref))
|
||||
result.constValues.add(p.`lit`.value.toPreserves)
|
||||
|
||||
func analyse*(p: Pattern): Analysis =
|
||||
var path: Path
|
||||
walk(result, path, p)
|
||||
|
||||
func checkPresence*(v: Value; present: Paths): bool =
|
||||
result = true
|
||||
for path in present:
|
||||
if not result: break
|
||||
result = step(v, path).isSome
|
||||
|
||||
func projectPaths*(v: Value; paths: Paths): Option[Captures] =
|
||||
var res = newSeq[Value](paths.len)
|
||||
for i, path in paths:
|
||||
var vv = step(v, path)
|
||||
if vv.isSome: res[i] = get(vv)
|
||||
else: return
|
||||
some res
|
||||
|
||||
proc matches*(pat: Pattern; pr: Value): bool =
|
||||
let analysis = analyse(pat)
|
||||
assert analysis.constPaths.len == analysis.constValues.len
|
||||
result = checkPresence(pr, analysis.presentPaths)
|
||||
if result:
|
||||
for i, path in analysis.constPaths:
|
||||
let v = step(pr, path)
|
||||
if v.isNone: return false
|
||||
if analysis.constValues[i] != v.get: return false
|
||||
for path in analysis.capturePaths:
|
||||
if step(pr, path).isNone: return false
|
||||
|
||||
proc capture*(pat: Pattern; pr: Value): seq[Value] =
|
||||
let analysis = analyse(pat)
|
||||
assert analysis.constPaths.len == analysis.constValues.len
|
||||
if checkPresence(pr, analysis.presentPaths):
|
||||
for i, path in analysis.constPaths:
|
||||
let v = step(pr, path)
|
||||
if v.isNone : return @[]
|
||||
if analysis.constValues[i] != v.get: return @[]
|
||||
for path in analysis.capturePaths:
|
||||
let v = step(pr, path)
|
||||
if v.isNone: return @[]
|
||||
result.add(get v)
|
||||
|
||||
when isMainModule:
|
||||
stdout.writeLine stdin.readAll.parsePreserves.grab
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2021 ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import nimSHA2
|
||||
|
||||
proc fillPad(pad: var openarray[byte]; key: openarray[byte]; fillByte: byte) =
|
||||
for i in 0..key.high: pad[i] = fillByte xor key[i].uint8
|
||||
for i in key.len..pad.high: pad[i] = fillByte
|
||||
|
||||
proc hmacSha256*[T:char|byte](key: openarray[byte]; msg: openarray[T]; outLength = 32): seq[byte] =
|
||||
const blockSize = 64
|
||||
assert(outLength <= 32)
|
||||
var
|
||||
hash: SHA256
|
||||
pad: array[blockSize, byte]
|
||||
block:
|
||||
const xorByte = 0x36'u8
|
||||
if key.len < blockSize:
|
||||
fillPad(pad, key, xorByte)
|
||||
else:
|
||||
initSHA(hash)
|
||||
update(hash, key)
|
||||
var keyDigest = final(hash)
|
||||
fillPad(pad, keyDigest, xorByte)
|
||||
initSHA(hash)
|
||||
update(hash, pad)
|
||||
update(hash, msg)
|
||||
var digest = final(hash)
|
||||
block:
|
||||
const xorByte = 0x5c'u8
|
||||
if key.len < blockSize:
|
||||
fillPad(pad, key, xorByte)
|
||||
else:
|
||||
initSHA(hash)
|
||||
update(hash, key)
|
||||
var keyDigest = final(hash)
|
||||
fillPad(pad, keyDigest, xorByte)
|
||||
initSHA(hash)
|
||||
update(hash, pad)
|
||||
update(hash, digest)
|
||||
digest = final(hash)
|
||||
result.setLen(outLength)
|
||||
copyMem(result[0].addr, digest[0].addr, result.len)
|
|
@ -1,19 +1,22 @@
|
|||
include_rules
|
||||
outputs = \
|
||||
dataspace.nim \
|
||||
dataspacePatterns.nim \
|
||||
gatekeeper.nim \
|
||||
protocol.nim \
|
||||
racketEvent.nim \
|
||||
secureChatProtocol.nim \
|
||||
service.nim \
|
||||
simpleChatProtocol.nim \
|
||||
stream.nim \
|
||||
sturdy.nim \
|
||||
tcp.nim \
|
||||
timer.nim \
|
||||
trace.nim \
|
||||
transportAddress.nim \
|
||||
worker.nim \
|
||||
modules += dataspace.nim
|
||||
modules += dataspacePatterns.nim
|
||||
modules += gatekeeper.nim
|
||||
modules += http.nim
|
||||
modules += noise.nim
|
||||
modules += protocol.nim
|
||||
modules += service.nim
|
||||
modules += stdenv.nim
|
||||
modules += stream.nim
|
||||
modules += sturdy.nim
|
||||
modules += tcp.nim
|
||||
modules += timer.nim
|
||||
modules += trace.nim
|
||||
modules += transportAddress.nim
|
||||
modules += worker.nim
|
||||
|
||||
: foreach ../../../../syndicate-protocols/schema-bundle.bin |> !preserves_schema_nim |> $(outputs)
|
||||
: ../../../../syndicate-protocols/schema-bundle.bin \
|
||||
|> !preserves_schema_nim \
|
||||
|> $(modules) | $(SYNDICATE_PROTOCOL)
|
||||
|
||||
: foreach $(modules) | $(modules) |> !nim_check |>
|
||||
|
|
Binary file not shown.
|
@ -1,14 +1,14 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves, dataspacePatterns
|
||||
preserves, dataspacePatterns
|
||||
|
||||
type
|
||||
Observe*[E] {.preservesRecord: "Observe".} = ref object
|
||||
`pattern`*: dataspacePatterns.Pattern[E]
|
||||
`observer`*: Preserve[E]
|
||||
Observe* {.preservesRecord: "Observe".} = object
|
||||
`pattern`*: dataspacePatterns.Pattern
|
||||
`observer`* {.preservesEmbedded.}: EmbeddedRef
|
||||
|
||||
proc `$`*[E](x: Observe[E]): string =
|
||||
`$`(toPreserve(x, E))
|
||||
proc `$`*(x: Observe): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*[E](x: Observe[E]): seq[byte] =
|
||||
encode(toPreserve(x, E))
|
||||
proc encode*(x: Observe): seq[byte] =
|
||||
encode(toPreserves(x))
|
||||
|
|
|
@ -1,103 +1,86 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves, std/tables
|
||||
preserves, std/tables
|
||||
|
||||
type
|
||||
AnyAtomKind* {.pure.} = enum
|
||||
`bool`, `float`, `double`, `int`, `string`, `bytes`, `symbol`, `embedded`
|
||||
AnyAtomBool* = bool
|
||||
AnyAtomFloat* = float32
|
||||
AnyAtomDouble* = float64
|
||||
AnyAtomInt* = BiggestInt
|
||||
AnyAtomString* = string
|
||||
AnyAtomBytes* = seq[byte]
|
||||
AnyAtomSymbol* = Symbol
|
||||
AnyAtomEmbedded*[E] = Preserve[E]
|
||||
`AnyAtom`*[E] {.preservesOr.} = ref object
|
||||
`bool`, `double`, `int`, `string`, `bytes`, `symbol`, `embedded`
|
||||
`AnyAtom`* {.preservesOr.} = object
|
||||
case orKind*: AnyAtomKind
|
||||
of AnyAtomKind.`bool`:
|
||||
`bool`*: AnyAtomBool
|
||||
|
||||
of AnyAtomKind.`float`:
|
||||
`float`*: AnyAtomFloat
|
||||
`bool`*: bool
|
||||
|
||||
of AnyAtomKind.`double`:
|
||||
`double`*: AnyAtomDouble
|
||||
`double`*: float
|
||||
|
||||
of AnyAtomKind.`int`:
|
||||
`int`*: AnyAtomInt
|
||||
`int`*: BiggestInt
|
||||
|
||||
of AnyAtomKind.`string`:
|
||||
`string`*: AnyAtomString
|
||||
`string`*: string
|
||||
|
||||
of AnyAtomKind.`bytes`:
|
||||
`bytes`*: AnyAtomBytes
|
||||
`bytes`*: seq[byte]
|
||||
|
||||
of AnyAtomKind.`symbol`:
|
||||
`symbol`*: AnyAtomSymbol
|
||||
`symbol`*: Symbol
|
||||
|
||||
of AnyAtomKind.`embedded`:
|
||||
`embedded`*: AnyAtomEmbedded[E]
|
||||
`embedded`* {.preservesEmbedded.}: EmbeddedRef
|
||||
|
||||
|
||||
DLit*[E] {.preservesRecord: "lit".} = ref object
|
||||
`value`*: AnyAtom[E]
|
||||
|
||||
DBind*[E] {.preservesRecord: "bind".} = ref object
|
||||
`pattern`*: Pattern[E]
|
||||
|
||||
DDiscard* {.preservesRecord: "_".} = object
|
||||
|
||||
DCompoundKind* {.pure.} = enum
|
||||
GroupTypeKind* {.pure.} = enum
|
||||
`rec`, `arr`, `dict`
|
||||
DCompoundRec*[E] {.preservesRecord: "rec".} = ref object
|
||||
`label`*: Preserve[E]
|
||||
`fields`*: seq[Pattern[E]]
|
||||
GroupTypeRec* {.preservesRecord: "rec".} = object
|
||||
`label`*: Value
|
||||
|
||||
DCompoundArr*[E] {.preservesRecord: "arr".} = ref object
|
||||
`items`*: seq[Pattern[E]]
|
||||
GroupTypeArr* {.preservesRecord: "arr".} = object
|
||||
|
||||
GroupTypeDict* {.preservesRecord: "dict".} = object
|
||||
|
||||
`GroupType`* {.preservesOr.} = object
|
||||
case orKind*: GroupTypeKind
|
||||
of GroupTypeKind.`rec`:
|
||||
`rec`*: GroupTypeRec
|
||||
|
||||
DCompoundDict*[E] {.preservesRecord: "dict".} = ref object
|
||||
`entries`*: Table[Preserve[E], Pattern[E]]
|
||||
of GroupTypeKind.`arr`:
|
||||
`arr`*: GroupTypeArr
|
||||
|
||||
`DCompound`*[E] {.preservesOr.} = ref object
|
||||
case orKind*: DCompoundKind
|
||||
of DCompoundKind.`rec`:
|
||||
`rec`*: DCompoundRec[E]
|
||||
|
||||
of DCompoundKind.`arr`:
|
||||
`arr`*: DCompoundArr[E]
|
||||
|
||||
of DCompoundKind.`dict`:
|
||||
`dict`*: DCompoundDict[E]
|
||||
of GroupTypeKind.`dict`:
|
||||
`dict`*: GroupTypeDict
|
||||
|
||||
|
||||
PatternKind* {.pure.} = enum
|
||||
`DDiscard`, `DBind`, `DLit`, `DCompound`
|
||||
`Pattern`*[E] {.preservesOr.} = ref object
|
||||
`discard`, `bind`, `lit`, `group`
|
||||
PatternDiscard* {.preservesRecord: "_".} = object
|
||||
|
||||
PatternBind* {.preservesRecord: "bind".} = object
|
||||
`pattern`*: Pattern
|
||||
|
||||
PatternLit* {.preservesRecord: "lit".} = object
|
||||
`value`*: AnyAtom
|
||||
|
||||
PatternGroup* {.preservesRecord: "group".} = object
|
||||
`type`*: GroupType
|
||||
`entries`*: Table[Value, Pattern]
|
||||
|
||||
`Pattern`* {.acyclic, preservesOr.} = ref object
|
||||
case orKind*: PatternKind
|
||||
of PatternKind.`DDiscard`:
|
||||
`ddiscard`*: DDiscard
|
||||
of PatternKind.`discard`:
|
||||
`discard`*: PatternDiscard
|
||||
|
||||
of PatternKind.`DBind`:
|
||||
`dbind`*: DBind[E]
|
||||
of PatternKind.`bind`:
|
||||
`bind`* {.preservesEmbedded.}: PatternBind
|
||||
|
||||
of PatternKind.`DLit`:
|
||||
`dlit`*: DLit[E]
|
||||
of PatternKind.`lit`:
|
||||
`lit`* {.preservesEmbedded.}: PatternLit
|
||||
|
||||
of PatternKind.`DCompound`:
|
||||
`dcompound`*: DCompound[E]
|
||||
of PatternKind.`group`:
|
||||
`group`* {.preservesEmbedded.}: PatternGroup
|
||||
|
||||
|
||||
proc `$`*[E](x: AnyAtom[E] | DLit[E] | DBind[E] | DCompound[E] | Pattern[E]): string =
|
||||
`$`(toPreserve(x, E))
|
||||
proc `$`*(x: AnyAtom | GroupType | Pattern): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*[E](x: AnyAtom[E] | DLit[E] | DBind[E] | DCompound[E] | Pattern[E]): seq[
|
||||
byte] =
|
||||
encode(toPreserve(x, E))
|
||||
|
||||
proc `$`*(x: DDiscard): string =
|
||||
`$`(toPreserve(x))
|
||||
|
||||
proc encode*(x: DDiscard): seq[byte] =
|
||||
encode(toPreserve(x))
|
||||
proc encode*(x: AnyAtom | GroupType | Pattern): seq[byte] =
|
||||
encode(toPreserves(x))
|
||||
|
|
|
@ -1,19 +1,103 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves, sturdy
|
||||
preserves
|
||||
|
||||
type
|
||||
Bind*[E] {.preservesRecord: "bind".} = ref object
|
||||
`oid`*: Preserve[E]
|
||||
`key`*: seq[byte]
|
||||
`target`*: Preserve[E]
|
||||
Bind* {.preservesRecord: "bind".} = object
|
||||
`description`*: Description
|
||||
`target`* {.preservesEmbedded.}: EmbeddedRef
|
||||
`observer`*: BindObserver
|
||||
|
||||
Resolve*[E] {.preservesRecord: "resolve".} = ref object
|
||||
`sturdyref`*: sturdy.SturdyRef[E]
|
||||
`observer`*: Preserve[E]
|
||||
Route* {.preservesRecord: "route".} = object
|
||||
`transports`*: seq[Value]
|
||||
`pathSteps`* {.preservesTupleTail.}: seq[PathStep]
|
||||
|
||||
proc `$`*[E](x: Bind[E] | Resolve[E]): string =
|
||||
`$`(toPreserve(x, E))
|
||||
BindObserverKind* {.pure.} = enum
|
||||
`present`, `absent`
|
||||
`BindObserver`* {.preservesOr.} = object
|
||||
case orKind*: BindObserverKind
|
||||
of BindObserverKind.`present`:
|
||||
`present`* {.preservesEmbedded.}: EmbeddedRef
|
||||
|
||||
proc encode*[E](x: Bind[E] | Resolve[E]): seq[byte] =
|
||||
encode(toPreserve(x, E))
|
||||
of BindObserverKind.`absent`:
|
||||
`absent`* {.preservesLiteral: "#f".}: bool
|
||||
|
||||
|
||||
TransportConnection* {.preservesRecord: "connect-transport".} = object
|
||||
`addr`*: Value
|
||||
`control`* {.preservesEmbedded.}: EmbeddedRef
|
||||
`resolved`*: Resolved
|
||||
|
||||
Step* = Value
|
||||
ResolvedPathStep* {.preservesRecord: "path-step".} = object
|
||||
`origin`* {.preservesEmbedded.}: EmbeddedRef
|
||||
`pathStep`*: PathStep
|
||||
`resolved`*: Resolved
|
||||
|
||||
BoundKind* {.pure.} = enum
|
||||
`bound`, `Rejected`
|
||||
BoundBound* {.preservesRecord: "bound".} = object
|
||||
`pathStep`*: PathStep
|
||||
|
||||
`Bound`* {.preservesOr.} = object
|
||||
case orKind*: BoundKind
|
||||
of BoundKind.`bound`:
|
||||
`bound`*: BoundBound
|
||||
|
||||
of BoundKind.`Rejected`:
|
||||
`rejected`*: Rejected
|
||||
|
||||
|
||||
ForceDisconnect* {.preservesRecord: "force-disconnect".} = object
|
||||
|
||||
Description* = Value
|
||||
Rejected* {.preservesRecord: "rejected".} = object
|
||||
`detail`*: Value
|
||||
|
||||
Resolve* {.preservesRecord: "resolve".} = object
|
||||
`step`*: Step
|
||||
`observer`* {.preservesEmbedded.}: EmbeddedRef
|
||||
|
||||
ResolvedKind* {.pure.} = enum
|
||||
`accepted`, `Rejected`
|
||||
ResolvedAccepted* {.preservesRecord: "accepted".} = object
|
||||
`responderSession`* {.preservesEmbedded.}: EmbeddedRef
|
||||
|
||||
`Resolved`* {.preservesOr.} = object
|
||||
case orKind*: ResolvedKind
|
||||
of ResolvedKind.`accepted`:
|
||||
`accepted`* {.preservesEmbedded.}: ResolvedAccepted
|
||||
|
||||
of ResolvedKind.`Rejected`:
|
||||
`rejected`*: Rejected
|
||||
|
||||
|
||||
TransportControl* = ForceDisconnect
|
||||
ResolvePath* {.preservesRecord: "resolve-path".} = object
|
||||
`route`*: Route
|
||||
`addr`*: Value
|
||||
`control`* {.preservesEmbedded.}: EmbeddedRef
|
||||
`resolved`*: Resolved
|
||||
|
||||
PathStep* = Value
|
||||
proc `$`*(x: Bind | Route | BindObserver | TransportConnection |
|
||||
ResolvedPathStep |
|
||||
Bound |
|
||||
ForceDisconnect |
|
||||
Rejected |
|
||||
Resolve |
|
||||
Resolved |
|
||||
TransportControl |
|
||||
ResolvePath): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*(x: Bind | Route | BindObserver | TransportConnection |
|
||||
ResolvedPathStep |
|
||||
Bound |
|
||||
ForceDisconnect |
|
||||
Rejected |
|
||||
Resolve |
|
||||
Resolved |
|
||||
TransportControl |
|
||||
ResolvePath): seq[byte] =
|
||||
encode(toPreserves(x))
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
|
||||
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`*: RequestHost
|
||||
`port`*: BiggestInt
|
||||
`method`*: Symbol
|
||||
`path`*: seq[string]
|
||||
`headers`*: Headers
|
||||
`query`*: Table[Symbol, seq[QueryValue]]
|
||||
`body`*: RequestBody
|
||||
|
||||
RequestBodyKind* {.pure.} = enum
|
||||
`absent`, `present`
|
||||
`RequestBody`* {.preservesOr.} = object
|
||||
case orKind*: RequestBodyKind
|
||||
of RequestBodyKind.`absent`:
|
||||
`absent`* {.preservesLiteral: "#f".}: bool
|
||||
|
||||
of RequestBodyKind.`present`:
|
||||
`present`*: seq[byte]
|
||||
|
||||
|
||||
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
|
||||
|
||||
RequestHostKind* {.pure.} = enum
|
||||
`absent`, `present`
|
||||
`RequestHost`* {.preservesOr.} = object
|
||||
case orKind*: RequestHostKind
|
||||
of RequestHostKind.`absent`:
|
||||
`absent`* {.preservesLiteral: "#f".}: bool
|
||||
|
||||
of RequestHostKind.`present`:
|
||||
`present`*: string
|
||||
|
||||
|
||||
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 |
|
||||
RequestHost |
|
||||
PathPatternElement |
|
||||
Chunk |
|
||||
PathPattern): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*(x: HostPattern | HttpListener | MethodPattern | MimeType |
|
||||
QueryValue |
|
||||
HttpRequest |
|
||||
RequestBody |
|
||||
Headers |
|
||||
HttpResponse |
|
||||
HttpService |
|
||||
HttpBinding |
|
||||
HttpContext |
|
||||
RequestHost |
|
||||
PathPatternElement |
|
||||
Chunk |
|
||||
PathPattern): seq[byte] =
|
||||
encode(toPreserves(x))
|
|
@ -0,0 +1,139 @@
|
|||
|
||||
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
|
||||
|
||||
|
||||
SessionItemKind* {.pure.} = enum
|
||||
`Initiator`, `Packet`
|
||||
`SessionItem`* {.preservesOr.} = object
|
||||
case orKind*: SessionItemKind
|
||||
of SessionItemKind.`Initiator`:
|
||||
`initiator`* {.preservesEmbedded.}: Initiator
|
||||
|
||||
of SessionItemKind.`Packet`:
|
||||
`packet`*: Packet
|
||||
|
||||
|
||||
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
|
||||
Initiator* {.preservesRecord: "initiator".} = object
|
||||
`initiatorSession`* {.preservesEmbedded.}: EmbeddedRef
|
||||
|
||||
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 |
|
||||
SessionItem |
|
||||
NoiseProtocol |
|
||||
NoisePathStepDetail |
|
||||
NoiseServiceSpec |
|
||||
Initiator |
|
||||
NoiseSpec |
|
||||
Packet): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*(x: NoiseDescriptionDetail | NoisePreSharedKeys | SecretKeyField |
|
||||
SessionItem |
|
||||
NoiseProtocol |
|
||||
NoisePathStepDetail |
|
||||
NoiseServiceSpec |
|
||||
Initiator |
|
||||
NoiseSpec |
|
||||
Packet): seq[byte] =
|
||||
encode(toPreserves(x))
|
Binary file not shown.
Binary file not shown.
|
@ -1,83 +1,77 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves
|
||||
preserves
|
||||
|
||||
type
|
||||
Error*[E] {.preservesRecord: "error".} = ref object
|
||||
Error* {.preservesRecord: "error".} = object
|
||||
`message`*: string
|
||||
`detail`*: Preserve[E]
|
||||
`detail`*: Value
|
||||
|
||||
Turn*[E] = seq[TurnEvent[E]]
|
||||
Message*[E] {.preservesRecord: "message".} = ref object
|
||||
`body`*: Assertion[E]
|
||||
Turn* = seq[TurnEvent]
|
||||
Message* {.preservesRecord: "M".} = object
|
||||
`body`*: Assertion
|
||||
|
||||
Retract* {.preservesRecord: "retract".} = object
|
||||
Retract* {.preservesRecord: "R".} = object
|
||||
`handle`*: Handle
|
||||
|
||||
Assert*[E] {.preservesRecord: "assert".} = ref object
|
||||
`assertion`*: Assertion[E]
|
||||
Assert* {.preservesRecord: "A".} = object
|
||||
`assertion`*: Assertion
|
||||
`handle`*: Handle
|
||||
|
||||
Extension*[E] {.preservesRecord: "label".} = ref object
|
||||
`data`*: seq[Preserve[E]]
|
||||
Extension* = Value
|
||||
Sync* {.preservesRecord: "S".} = object
|
||||
`peer`* {.preservesEmbedded.}: Value
|
||||
|
||||
Sync*[E] {.preservesRecord: "sync".} = ref object
|
||||
`peer`*: Preserve[E]
|
||||
|
||||
TurnEvent*[E] {.preservesTuple.} = ref object
|
||||
TurnEvent* {.preservesTuple.} = object
|
||||
`oid`*: Oid
|
||||
`event`*: Event[E]
|
||||
`event`*: Event
|
||||
|
||||
Oid* = BiggestInt
|
||||
Assertion*[E] = Preserve[E]
|
||||
Assertion* = Value
|
||||
Handle* = BiggestInt
|
||||
PacketKind* {.pure.} = enum
|
||||
`Turn`, `Error`, `Extension`
|
||||
`Packet`*[E] {.preservesOr.} = ref object
|
||||
`Turn`, `Error`, `Extension`, `Nop`
|
||||
`Packet`* {.preservesOr.} = object
|
||||
case orKind*: PacketKind
|
||||
of PacketKind.`Turn`:
|
||||
`turn`*: Turn[E]
|
||||
`turn`* {.preservesEmbedded.}: Turn
|
||||
|
||||
of PacketKind.`Error`:
|
||||
`error`*: Error[E]
|
||||
`error`*: Error
|
||||
|
||||
of PacketKind.`Extension`:
|
||||
`extension`*: Extension[E]
|
||||
`extension`*: Extension
|
||||
|
||||
of PacketKind.`Nop`:
|
||||
`nop`* {.preservesLiteral: "#f".}: bool
|
||||
|
||||
|
||||
EventKind* {.pure.} = enum
|
||||
`Assert`, `Retract`, `Message`, `Sync`
|
||||
`Event`*[E] {.preservesOr.} = ref object
|
||||
`Event`* {.preservesOr.} = object
|
||||
case orKind*: EventKind
|
||||
of EventKind.`Assert`:
|
||||
`assert`*: Assert[E]
|
||||
`assert`*: Assert
|
||||
|
||||
of EventKind.`Retract`:
|
||||
`retract`*: Retract
|
||||
|
||||
of EventKind.`Message`:
|
||||
`message`*: Message[E]
|
||||
`message`*: Message
|
||||
|
||||
of EventKind.`Sync`:
|
||||
`sync`*: Sync[E]
|
||||
`sync`* {.preservesEmbedded.}: Sync
|
||||
|
||||
|
||||
proc `$`*[E](x: Error[E] | Turn[E] | Message[E] | Assert[E] | Extension[E] |
|
||||
Sync[E] |
|
||||
TurnEvent[E] |
|
||||
Packet[E] |
|
||||
Event[E]): string =
|
||||
`$`(toPreserve(x, E))
|
||||
proc `$`*(x: Error | Turn | Message | Retract | Assert | Sync | TurnEvent | Oid |
|
||||
Handle |
|
||||
Packet |
|
||||
Event): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*[E](x: Error[E] | Turn[E] | Message[E] | Assert[E] | Extension[E] |
|
||||
Sync[E] |
|
||||
TurnEvent[E] |
|
||||
Packet[E] |
|
||||
Event[E]): seq[byte] =
|
||||
encode(toPreserve(x, E))
|
||||
|
||||
proc `$`*(x: Retract | Oid | Handle): string =
|
||||
`$`(toPreserve(x))
|
||||
|
||||
proc encode*(x: Retract | Oid | Handle): seq[byte] =
|
||||
encode(toPreserve(x))
|
||||
proc encode*(x: Error | Turn | Message | Retract | Assert | Sync | TurnEvent |
|
||||
Oid |
|
||||
Handle |
|
||||
Packet |
|
||||
Event): seq[byte] =
|
||||
encode(toPreserves(x))
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves
|
||||
|
||||
type
|
||||
RacketEvent*[E] {.preservesRecord: "racket-event".} = ref object
|
||||
`source`*: Preserve[E]
|
||||
`event`*: Preserve[E]
|
||||
|
||||
proc `$`*[E](x: RacketEvent[E]): string =
|
||||
`$`(toPreserve(x, E))
|
||||
|
||||
proc encode*[E](x: RacketEvent[E]): seq[byte] =
|
||||
encode(toPreserve(x, E))
|
|
@ -1,73 +0,0 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves
|
||||
|
||||
type
|
||||
UserId* = BiggestInt
|
||||
NickConflict* {.preservesRecord: "nickConflict".} = object
|
||||
|
||||
NickClaimResponseKind* {.pure.} = enum
|
||||
`true`, `NickConflict`
|
||||
`NickClaimResponse`* {.preservesOr.} = object
|
||||
case orKind*: NickClaimResponseKind
|
||||
of NickClaimResponseKind.`true`:
|
||||
`true`* {.preservesLiteral: "#t".}: bool
|
||||
|
||||
of NickClaimResponseKind.`NickConflict`:
|
||||
`nickconflict`*: NickConflict
|
||||
|
||||
|
||||
Join*[E] {.preservesRecord: "joinedUser".} = ref object
|
||||
`uid`*: UserId
|
||||
`handle`*: Preserve[E]
|
||||
|
||||
SessionKind* {.pure.} = enum
|
||||
`observeUsers`, `observeSpeech`, `NickClaim`, `Says`
|
||||
SessionObserveUsers*[E] {.preservesRecord: "Observe".} = ref object
|
||||
`data`* {.preservesLiteral: "user".}: bool
|
||||
`observer`*: Preserve[E]
|
||||
|
||||
SessionObserveSpeech*[E] {.preservesRecord: "Observe".} = ref object
|
||||
`data`* {.preservesLiteral: "says".}: bool
|
||||
`observer`*: Preserve[E]
|
||||
|
||||
`Session`*[E] {.preservesOr.} = ref object
|
||||
case orKind*: SessionKind
|
||||
of SessionKind.`observeUsers`:
|
||||
`observeusers`*: SessionObserveUsers[E]
|
||||
|
||||
of SessionKind.`observeSpeech`:
|
||||
`observespeech`*: SessionObserveSpeech[E]
|
||||
|
||||
of SessionKind.`NickClaim`:
|
||||
`nickclaim`*: NickClaim[E]
|
||||
|
||||
of SessionKind.`Says`:
|
||||
`says`*: Says
|
||||
|
||||
|
||||
UserInfo* {.preservesRecord: "user".} = object
|
||||
`uid`*: UserId
|
||||
`name`*: string
|
||||
|
||||
NickClaim*[E] {.preservesRecord: "claimNick".} = ref object
|
||||
`uid`*: UserId
|
||||
`name`*: string
|
||||
`k`*: Preserve[E]
|
||||
|
||||
Says* {.preservesRecord: "says".} = object
|
||||
`who`*: UserId
|
||||
`what`*: string
|
||||
|
||||
proc `$`*[E](x: Join[E] | Session[E] | NickClaim[E]): string =
|
||||
`$`(toPreserve(x, E))
|
||||
|
||||
proc encode*[E](x: Join[E] | Session[E] | NickClaim[E]): seq[byte] =
|
||||
encode(toPreserve(x, E))
|
||||
|
||||
proc `$`*(x: UserId | NickConflict | NickClaimResponse | UserInfo | Says): string =
|
||||
`$`(toPreserve(x))
|
||||
|
||||
proc encode*(x: UserId | NickConflict | NickClaimResponse | UserInfo | Says): seq[
|
||||
byte] =
|
||||
encode(toPreserve(x))
|
|
@ -1,12 +1,11 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves
|
||||
preserves
|
||||
|
||||
type
|
||||
StateKind* {.pure.} = enum
|
||||
`started`, `ready`, `failed`, `complete`, `userDefined`
|
||||
StateUserDefined*[E] = Preserve[E]
|
||||
`State`*[E] {.preservesOr.} = ref object
|
||||
`State`* {.preservesOr.} = object
|
||||
case orKind*: StateKind
|
||||
of StateKind.`started`:
|
||||
`started`* {.preservesLiteral: "started".}: bool
|
||||
|
@ -21,40 +20,38 @@ type
|
|||
`complete`* {.preservesLiteral: "complete".}: bool
|
||||
|
||||
of StateKind.`userDefined`:
|
||||
`userdefined`*: StateUserDefined[E]
|
||||
`userdefined`*: Value
|
||||
|
||||
|
||||
ServiceObject*[E] {.preservesRecord: "service-object".} = ref object
|
||||
`serviceName`*: Preserve[E]
|
||||
`object`*: Preserve[E]
|
||||
ServiceObject* {.preservesRecord: "service-object".} = object
|
||||
`serviceName`*: Value
|
||||
`object`*: Value
|
||||
|
||||
RequireService*[E] {.preservesRecord: "require-service".} = ref object
|
||||
`serviceName`*: Preserve[E]
|
||||
RequireService* {.preservesRecord: "require-service".} = object
|
||||
`serviceName`*: Value
|
||||
|
||||
RestartService*[E] {.preservesRecord: "restart-service".} = ref object
|
||||
`serviceName`*: Preserve[E]
|
||||
RestartService* {.preservesRecord: "restart-service".} = object
|
||||
`serviceName`*: Value
|
||||
|
||||
RunService*[E] {.preservesRecord: "run-service".} = ref object
|
||||
`serviceName`*: Preserve[E]
|
||||
RunService* {.preservesRecord: "run-service".} = object
|
||||
`serviceName`*: Value
|
||||
|
||||
ServiceState*[E] {.preservesRecord: "service-state".} = ref object
|
||||
`serviceName`*: Preserve[E]
|
||||
`state`*: State[E]
|
||||
ServiceState* {.preservesRecord: "service-state".} = object
|
||||
`serviceName`*: Value
|
||||
`state`*: State
|
||||
|
||||
ServiceDependency*[E] {.preservesRecord: "depends-on".} = ref object
|
||||
`depender`*: Preserve[E]
|
||||
`dependee`*: ServiceState[E]
|
||||
ServiceDependency* {.preservesRecord: "depends-on".} = object
|
||||
`depender`*: Value
|
||||
`dependee`*: ServiceState
|
||||
|
||||
proc `$`*[E](x: State[E] | ServiceObject[E] | RequireService[E] |
|
||||
RestartService[E] |
|
||||
RunService[E] |
|
||||
ServiceState[E] |
|
||||
ServiceDependency[E]): string =
|
||||
`$`(toPreserve(x, E))
|
||||
proc `$`*(x: State | ServiceObject | RequireService | RestartService |
|
||||
RunService |
|
||||
ServiceState |
|
||||
ServiceDependency): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*[E](x: State[E] | ServiceObject[E] | RequireService[E] |
|
||||
RestartService[E] |
|
||||
RunService[E] |
|
||||
ServiceState[E] |
|
||||
ServiceDependency[E]): seq[byte] =
|
||||
encode(toPreserve(x, E))
|
||||
proc encode*(x: State | ServiceObject | RequireService | RestartService |
|
||||
RunService |
|
||||
ServiceState |
|
||||
ServiceDependency): seq[byte] =
|
||||
encode(toPreserves(x))
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves
|
||||
|
||||
type
|
||||
Says* {.preservesRecord: "Says".} = object
|
||||
`who`*: string
|
||||
`what`*: string
|
||||
|
||||
Present* {.preservesRecord: "Present".} = object
|
||||
`username`*: string
|
||||
|
||||
proc `$`*(x: Says | Present): string =
|
||||
`$`(toPreserve(x))
|
||||
|
||||
proc encode*(x: Says | Present): seq[byte] =
|
||||
encode(toPreserve(x))
|
|
@ -0,0 +1,40 @@
|
|||
|
||||
import
|
||||
preserves, sturdy, gatekeeper
|
||||
|
||||
type
|
||||
StandardTransportKind* {.pure.} = enum
|
||||
`wsUrl`, `other`
|
||||
`StandardTransport`* {.preservesOr.} = object
|
||||
case orKind*: StandardTransportKind
|
||||
of StandardTransportKind.`wsUrl`:
|
||||
`wsurl`*: string
|
||||
|
||||
of StandardTransportKind.`other`:
|
||||
`other`*: Value
|
||||
|
||||
|
||||
StandardRouteKind* {.pure.} = enum
|
||||
`standard`, `general`
|
||||
StandardRouteStandard* {.preservesTuple.} = object
|
||||
`transports`*: seq[StandardTransport]
|
||||
`key`*: seq[byte]
|
||||
`service`*: Value
|
||||
`sig`*: seq[byte]
|
||||
`oid`*: Value
|
||||
`caveats`* {.preservesTupleTail.}: seq[sturdy.Caveat]
|
||||
|
||||
`StandardRoute`* {.preservesOr.} = object
|
||||
case orKind*: StandardRouteKind
|
||||
of StandardRouteKind.`standard`:
|
||||
`standard`*: StandardRouteStandard
|
||||
|
||||
of StandardRouteKind.`general`:
|
||||
`general`*: gatekeeper.Route
|
||||
|
||||
|
||||
proc `$`*(x: StandardTransport | StandardRoute): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*(x: StandardTransport | StandardRoute): seq[byte] =
|
||||
encode(toPreserves(x))
|
|
@ -1,15 +1,14 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves
|
||||
preserves
|
||||
|
||||
type
|
||||
CreditAmountKind* {.pure.} = enum
|
||||
`count`, `unbounded`
|
||||
CreditAmountCount* = BiggestInt
|
||||
`CreditAmount`* {.preservesOr.} = object
|
||||
case orKind*: CreditAmountKind
|
||||
of CreditAmountKind.`count`:
|
||||
`count`*: CreditAmountCount
|
||||
`count`*: BiggestInt
|
||||
|
||||
of CreditAmountKind.`unbounded`:
|
||||
`unbounded`* {.preservesLiteral: "unbounded".}: bool
|
||||
|
@ -18,76 +17,76 @@ type
|
|||
StreamError* {.preservesRecord: "error".} = object
|
||||
`message`*: string
|
||||
|
||||
StreamListenerError*[E] {.preservesRecord: "stream-listener-error".} = ref object
|
||||
`spec`*: Preserve[E]
|
||||
StreamListenerError* {.preservesRecord: "stream-listener-error".} = object
|
||||
`spec`*: Value
|
||||
`message`*: string
|
||||
|
||||
StreamConnection*[E] {.preservesRecord: "stream-connection".} = ref object
|
||||
`source`*: Preserve[E]
|
||||
`sink`*: Preserve[E]
|
||||
`spec`*: Preserve[E]
|
||||
StreamConnection* {.preservesRecord: "stream-connection".} = object
|
||||
`source`* {.preservesEmbedded.}: EmbeddedRef
|
||||
`sink`* {.preservesEmbedded.}: EmbeddedRef
|
||||
`spec`*: Value
|
||||
|
||||
`LineMode`* {.preservesOr, pure.} = enum
|
||||
`lf`, `crlf`
|
||||
SourceKind* {.pure.} = enum
|
||||
`sink`, `StreamError`, `credit`
|
||||
SourceSink*[E] {.preservesRecord: "sink".} = ref object
|
||||
`controller`*: Preserve[E]
|
||||
SourceSink* {.preservesRecord: "sink".} = object
|
||||
`controller`* {.preservesEmbedded.}: EmbeddedRef
|
||||
|
||||
SourceCredit*[E] {.preservesRecord: "credit".} = ref object
|
||||
SourceCredit* {.preservesRecord: "credit".} = object
|
||||
`amount`*: CreditAmount
|
||||
`mode`*: Mode[E]
|
||||
`mode`*: Mode
|
||||
|
||||
`Source`*[E] {.preservesOr.} = ref object
|
||||
`Source`* {.acyclic, preservesOr.} = ref object
|
||||
case orKind*: SourceKind
|
||||
of SourceKind.`sink`:
|
||||
`sink`*: SourceSink[E]
|
||||
`sink`* {.preservesEmbedded.}: SourceSink
|
||||
|
||||
of SourceKind.`StreamError`:
|
||||
`streamerror`*: StreamError
|
||||
|
||||
of SourceKind.`credit`:
|
||||
`credit`*: SourceCredit[E]
|
||||
`credit`*: SourceCredit
|
||||
|
||||
|
||||
SinkKind* {.pure.} = enum
|
||||
`source`, `StreamError`, `data`, `eof`
|
||||
SinkSource*[E] {.preservesRecord: "source".} = ref object
|
||||
`controller`*: Preserve[E]
|
||||
SinkSource* {.preservesRecord: "source".} = object
|
||||
`controller`* {.preservesEmbedded.}: EmbeddedRef
|
||||
|
||||
SinkData*[E] {.preservesRecord: "data".} = ref object
|
||||
`payload`*: Preserve[E]
|
||||
`mode`*: Mode[E]
|
||||
SinkData* {.preservesRecord: "data".} = object
|
||||
`payload`*: Value
|
||||
`mode`*: Mode
|
||||
|
||||
SinkEof* {.preservesRecord: "eof".} = object
|
||||
|
||||
`Sink`*[E] {.preservesOr.} = ref object
|
||||
`Sink`* {.acyclic, preservesOr.} = ref object
|
||||
case orKind*: SinkKind
|
||||
of SinkKind.`source`:
|
||||
`source`*: SinkSource[E]
|
||||
`source`* {.preservesEmbedded.}: SinkSource
|
||||
|
||||
of SinkKind.`StreamError`:
|
||||
`streamerror`*: StreamError
|
||||
|
||||
of SinkKind.`data`:
|
||||
`data`*: SinkData[E]
|
||||
`data`*: SinkData
|
||||
|
||||
of SinkKind.`eof`:
|
||||
`eof`*: SinkEof
|
||||
|
||||
|
||||
StreamListenerReady*[E] {.preservesRecord: "stream-listener-ready".} = ref object
|
||||
`spec`*: Preserve[E]
|
||||
StreamListenerReady* {.preservesRecord: "stream-listener-ready".} = object
|
||||
`spec`*: Value
|
||||
|
||||
ModeKind* {.pure.} = enum
|
||||
`bytes`, `lines`, `packet`, `object`
|
||||
ModePacket* {.preservesRecord: "packet".} = object
|
||||
`size`*: BiggestInt
|
||||
|
||||
ModeObject*[E] {.preservesRecord: "object".} = ref object
|
||||
`description`*: Preserve[E]
|
||||
ModeObject* {.preservesRecord: "object".} = object
|
||||
`description`*: Value
|
||||
|
||||
`Mode`*[E] {.preservesOr.} = ref object
|
||||
`Mode`* {.preservesOr.} = object
|
||||
case orKind*: ModeKind
|
||||
of ModeKind.`bytes`:
|
||||
`bytes`* {.preservesLiteral: "bytes".}: bool
|
||||
|
@ -99,23 +98,20 @@ type
|
|||
`packet`*: ModePacket
|
||||
|
||||
of ModeKind.`object`:
|
||||
`object`*: ModeObject[E]
|
||||
`object`*: ModeObject
|
||||
|
||||
|
||||
proc `$`*[E](x: StreamListenerError[E] | StreamConnection[E] | Source[E] |
|
||||
Sink[E] |
|
||||
StreamListenerReady[E] |
|
||||
Mode[E]): string =
|
||||
`$`(toPreserve(x, E))
|
||||
proc `$`*(x: CreditAmount | StreamError | StreamListenerError | StreamConnection |
|
||||
Source |
|
||||
Sink |
|
||||
StreamListenerReady |
|
||||
Mode): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*[E](x: StreamListenerError[E] | StreamConnection[E] | Source[E] |
|
||||
Sink[E] |
|
||||
StreamListenerReady[E] |
|
||||
Mode[E]): seq[byte] =
|
||||
encode(toPreserve(x, E))
|
||||
|
||||
proc `$`*(x: CreditAmount | StreamError): string =
|
||||
`$`(toPreserve(x))
|
||||
|
||||
proc encode*(x: CreditAmount | StreamError): seq[byte] =
|
||||
encode(toPreserve(x))
|
||||
proc encode*(x: CreditAmount | StreamError | StreamListenerError |
|
||||
StreamConnection |
|
||||
Source |
|
||||
Sink |
|
||||
StreamListenerReady |
|
||||
Mode): seq[byte] =
|
||||
encode(toPreserves(x))
|
||||
|
|
|
@ -1,146 +1,187 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves, std/tables
|
||||
preserves, std/tables, std/options
|
||||
|
||||
type
|
||||
PCompoundKind* {.pure.} = enum
|
||||
`rec`, `arr`, `dict`
|
||||
PCompoundRec*[E] {.preservesRecord: "rec".} = ref object
|
||||
`label`*: Preserve[E]
|
||||
`fields`*: seq[Pattern[E]]
|
||||
PCompoundRec* {.preservesRecord: "rec".} = object
|
||||
`label`*: Value
|
||||
`fields`*: seq[Pattern]
|
||||
|
||||
PCompoundArr*[E] {.preservesRecord: "arr".} = ref object
|
||||
`items`*: seq[Pattern[E]]
|
||||
PCompoundArr* {.preservesRecord: "arr".} = object
|
||||
`items`*: seq[Pattern]
|
||||
|
||||
PCompoundDict*[E] {.preservesRecord: "dict".} = ref object
|
||||
`entries`*: Table[Preserve[E], Pattern[E]]
|
||||
PCompoundDict* {.preservesRecord: "dict".} = object
|
||||
`entries`*: Table[Value, Pattern]
|
||||
|
||||
`PCompound`*[E] {.preservesOr.} = ref object
|
||||
`PCompound`* {.preservesOr.} = object
|
||||
case orKind*: PCompoundKind
|
||||
of PCompoundKind.`rec`:
|
||||
`rec`*: PCompoundRec[E]
|
||||
`rec`*: PCompoundRec
|
||||
|
||||
of PCompoundKind.`arr`:
|
||||
`arr`*: PCompoundArr[E]
|
||||
`arr`*: PCompoundArr
|
||||
|
||||
of PCompoundKind.`dict`:
|
||||
`dict`*: PCompoundDict[E]
|
||||
`dict`*: PCompoundDict
|
||||
|
||||
|
||||
Reject* {.preservesRecord: "reject".} = object
|
||||
`pattern`*: Pattern
|
||||
|
||||
PAnd*[E] {.preservesRecord: "and".} = ref object
|
||||
`patterns`*: seq[Pattern[E]]
|
||||
CaveatsFieldKind* {.pure.} = enum
|
||||
`present`, `invalid`, `absent`
|
||||
CaveatsFieldPresent* {.preservesDictionary.} = object
|
||||
`caveats`*: seq[Caveat]
|
||||
|
||||
Rewrite*[E] {.preservesRecord: "rewrite".} = ref object
|
||||
`pattern`*: Pattern[E]
|
||||
`template`*: Template[E]
|
||||
CaveatsFieldInvalid* {.preservesDictionary.} = object
|
||||
`caveats`*: Value
|
||||
|
||||
CaveatsFieldAbsent* {.preservesDictionary.} = object
|
||||
|
||||
`CaveatsField`* {.preservesOr.} = object
|
||||
case orKind*: CaveatsFieldKind
|
||||
of CaveatsFieldKind.`present`:
|
||||
`present`*: CaveatsFieldPresent
|
||||
|
||||
of CaveatsFieldKind.`invalid`:
|
||||
`invalid`*: CaveatsFieldInvalid
|
||||
|
||||
of CaveatsFieldKind.`absent`:
|
||||
`absent`*: CaveatsFieldAbsent
|
||||
|
||||
|
||||
SturdyDescriptionDetail* {.preservesDictionary.} = object
|
||||
`key`*: seq[byte]
|
||||
`oid`*: Value
|
||||
|
||||
PAnd* {.preservesRecord: "and".} = object
|
||||
`patterns`*: seq[Pattern]
|
||||
|
||||
SturdyStepDetail* = Parameters
|
||||
Rewrite* {.preservesRecord: "rewrite".} = object
|
||||
`pattern`*: Pattern
|
||||
`template`*: Template
|
||||
|
||||
ParametersCaveats* = Option[Value]
|
||||
ParametersOid* = Value
|
||||
ParametersSig* = seq[byte]
|
||||
`Parameters`* {.preservesDictionary.} = object
|
||||
`caveats`*: Option[Value]
|
||||
`oid`*: Value
|
||||
`sig`*: seq[byte]
|
||||
|
||||
TRef* {.preservesRecord: "ref".} = object
|
||||
`binding`*: BiggestInt
|
||||
|
||||
PBind*[E] {.preservesRecord: "bind".} = ref object
|
||||
`pattern`*: Pattern[E]
|
||||
PBind* {.preservesRecord: "bind".} = object
|
||||
`pattern`*: Pattern
|
||||
|
||||
Lit*[E] {.preservesRecord: "lit".} = ref object
|
||||
`value`*: Preserve[E]
|
||||
Lit* {.preservesRecord: "lit".} = object
|
||||
`value`*: Value
|
||||
|
||||
TCompoundKind* {.pure.} = enum
|
||||
`rec`, `arr`, `dict`
|
||||
TCompoundRec*[E] {.preservesRecord: "rec".} = ref object
|
||||
`label`*: Preserve[E]
|
||||
`fields`*: seq[Template[E]]
|
||||
TCompoundRec* {.preservesRecord: "rec".} = object
|
||||
`label`*: Value
|
||||
`fields`*: seq[Template]
|
||||
|
||||
TCompoundArr*[E] {.preservesRecord: "arr".} = ref object
|
||||
`items`*: seq[Template[E]]
|
||||
TCompoundArr* {.preservesRecord: "arr".} = object
|
||||
`items`*: seq[Template]
|
||||
|
||||
TCompoundDict*[E] {.preservesRecord: "dict".} = ref object
|
||||
`entries`*: Table[Preserve[E], Template[E]]
|
||||
TCompoundDict* {.preservesRecord: "dict".} = object
|
||||
`entries`*: Table[Value, Template]
|
||||
|
||||
`TCompound`*[E] {.preservesOr.} = ref object
|
||||
`TCompound`* {.preservesOr.} = object
|
||||
case orKind*: TCompoundKind
|
||||
of TCompoundKind.`rec`:
|
||||
`rec`*: TCompoundRec[E]
|
||||
`rec`*: TCompoundRec
|
||||
|
||||
of TCompoundKind.`arr`:
|
||||
`arr`*: TCompoundArr[E]
|
||||
`arr`*: TCompoundArr
|
||||
|
||||
of TCompoundKind.`dict`:
|
||||
`dict`*: TCompoundDict[E]
|
||||
|
||||
`dict`*: TCompoundDict
|
||||
|
||||
|
||||
SturdyPathStepDetail* = Parameters
|
||||
`PAtom`* {.preservesOr, pure.} = enum
|
||||
`Boolean`, `Float`, `Double`, `SignedInteger`, `String`, `ByteString`,
|
||||
`Symbol`
|
||||
Attenuation*[E] = seq[Caveat[E]]
|
||||
`Boolean`, `Double`, `SignedInteger`, `String`, `ByteString`, `Symbol`
|
||||
PDiscard* {.preservesRecord: "_".} = object
|
||||
|
||||
|
||||
TemplateKind* {.pure.} = enum
|
||||
`TAttenuate`, `TRef`, `Lit`, `TCompound`
|
||||
`Template`*[E] {.preservesOr.} = ref object
|
||||
`Template`* {.acyclic, preservesOr.} = ref object
|
||||
case orKind*: TemplateKind
|
||||
of TemplateKind.`TAttenuate`:
|
||||
`tattenuate`*: TAttenuate[E]
|
||||
`tattenuate`*: TAttenuate
|
||||
|
||||
of TemplateKind.`TRef`:
|
||||
`tref`*: TRef
|
||||
|
||||
of TemplateKind.`Lit`:
|
||||
`lit`*: Lit[E]
|
||||
`lit`*: Lit
|
||||
|
||||
of TemplateKind.`TCompound`:
|
||||
`tcompound`*: TCompound[E]
|
||||
|
||||
`tcompound`*: TCompound
|
||||
|
||||
|
||||
CaveatKind* {.pure.} = enum
|
||||
`Rewrite`, `Alts`
|
||||
`Caveat`*[E] {.preservesOr.} = ref object
|
||||
`Rewrite`, `Alts`, `Reject`, `unknown`
|
||||
`Caveat`* {.preservesOr.} = object
|
||||
case orKind*: CaveatKind
|
||||
of CaveatKind.`Rewrite`:
|
||||
`rewrite`*: Rewrite[E]
|
||||
`rewrite`*: Rewrite
|
||||
|
||||
of CaveatKind.`Alts`:
|
||||
`alts`*: Alts[E]
|
||||
`alts`*: Alts
|
||||
|
||||
of CaveatKind.`Reject`:
|
||||
`reject`*: Reject
|
||||
|
||||
PNot*[E] {.preservesRecord: "not".} = ref object
|
||||
`pattern`*: Pattern[E]
|
||||
of CaveatKind.`unknown`:
|
||||
`unknown`*: Value
|
||||
|
||||
SturdyRef*[E] {.preservesRecord: "ref".} = ref object
|
||||
`oid`*: Preserve[E]
|
||||
`caveatChain`*: seq[Attenuation[E]]
|
||||
`sig`*: seq[byte]
|
||||
|
||||
PNot* {.preservesRecord: "not".} = object
|
||||
`pattern`*: Pattern
|
||||
|
||||
SturdyRef* {.preservesRecord: "ref".} = object
|
||||
`parameters`*: Parameters
|
||||
|
||||
WireRefKind* {.pure.} = enum
|
||||
`mine`, `yours`
|
||||
WireRefMine* {.preservesTuple.} = object
|
||||
`data`* {.preservesLiteral: "0".}: bool
|
||||
`field0`* {.preservesLiteral: "0".}: tuple[]
|
||||
`oid`*: Oid
|
||||
|
||||
WireRefYours*[E] {.preservesTuple.} = ref object
|
||||
`data`* {.preservesLiteral: "1".}: bool
|
||||
WireRefYours* {.preservesTuple.} = object
|
||||
`field0`* {.preservesLiteral: "1".}: tuple[]
|
||||
`oid`*: Oid
|
||||
`attenuation`* {.preservesTupleTail.}: seq[Caveat[E]]
|
||||
`attenuation`* {.preservesTupleTail.}: seq[Caveat]
|
||||
|
||||
`WireRef`*[E] {.preservesOr.} = ref object
|
||||
`WireRef`* {.preservesOr.} = object
|
||||
case orKind*: WireRefKind
|
||||
of WireRefKind.`mine`:
|
||||
`mine`*: WireRefMine
|
||||
|
||||
of WireRefKind.`yours`:
|
||||
`yours`*: WireRefYours[E]
|
||||
`yours`*: WireRefYours
|
||||
|
||||
|
||||
TAttenuate*[E] {.preservesRecord: "attenuate".} = ref object
|
||||
`template`*: Template[E]
|
||||
`attenuation`*: Attenuation[E]
|
||||
TAttenuate* {.preservesRecord: "attenuate".} = object
|
||||
`template`*: Template
|
||||
`attenuation`*: seq[Caveat]
|
||||
|
||||
Oid* = BiggestInt
|
||||
Alts*[E] {.preservesRecord: "or".} = ref object
|
||||
`alternatives`*: seq[Rewrite[E]]
|
||||
Alts* {.preservesRecord: "or".} = object
|
||||
`alternatives`*: seq[Rewrite]
|
||||
|
||||
PatternKind* {.pure.} = enum
|
||||
`PDiscard`, `PAtom`, `PEmbedded`, `PBind`, `PAnd`, `PNot`, `Lit`,
|
||||
`PCompound`
|
||||
`Pattern`*[E] {.preservesOr.} = ref object
|
||||
`Pattern`* {.acyclic, preservesOr.} = ref object
|
||||
case orKind*: PatternKind
|
||||
of PatternKind.`PDiscard`:
|
||||
`pdiscard`*: PDiscard
|
||||
|
@ -152,49 +193,60 @@ type
|
|||
`pembedded`* {.preservesLiteral: "Embedded".}: bool
|
||||
|
||||
of PatternKind.`PBind`:
|
||||
`pbind`*: PBind[E]
|
||||
`pbind`*: PBind
|
||||
|
||||
of PatternKind.`PAnd`:
|
||||
`pand`*: PAnd[E]
|
||||
`pand`*: PAnd
|
||||
|
||||
of PatternKind.`PNot`:
|
||||
`pnot`*: PNot[E]
|
||||
`pnot`*: PNot
|
||||
|
||||
of PatternKind.`Lit`:
|
||||
`lit`*: Lit[E]
|
||||
`lit`*: Lit
|
||||
|
||||
of PatternKind.`PCompound`:
|
||||
`pcompound`*: PCompound[E]
|
||||
`pcompound`*: PCompound
|
||||
|
||||
|
||||
proc `$`*(x: PCompound | Reject | CaveatsField | SturdyDescriptionDetail | PAnd |
|
||||
SturdyStepDetail |
|
||||
Rewrite |
|
||||
Parameters |
|
||||
TRef |
|
||||
PBind |
|
||||
Lit |
|
||||
TCompound |
|
||||
SturdyPathStepDetail |
|
||||
PDiscard |
|
||||
Template |
|
||||
Caveat |
|
||||
PNot |
|
||||
SturdyRef |
|
||||
WireRef |
|
||||
TAttenuate |
|
||||
Oid |
|
||||
Alts |
|
||||
Pattern): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc `$`*[E](x: PCompound[E] | PAnd[E] | Rewrite[E] | PBind[E] | Lit[E] |
|
||||
TCompound[E] |
|
||||
Attenuation[E] |
|
||||
Template[E] |
|
||||
Caveat[E] |
|
||||
PNot[E] |
|
||||
SturdyRef[E] |
|
||||
WireRef[E] |
|
||||
TAttenuate[E] |
|
||||
Alts[E] |
|
||||
Pattern[E]): string =
|
||||
`$`(toPreserve(x, E))
|
||||
|
||||
proc encode*[E](x: PCompound[E] | PAnd[E] | Rewrite[E] | PBind[E] | Lit[E] |
|
||||
TCompound[E] |
|
||||
Attenuation[E] |
|
||||
Template[E] |
|
||||
Caveat[E] |
|
||||
PNot[E] |
|
||||
SturdyRef[E] |
|
||||
WireRef[E] |
|
||||
TAttenuate[E] |
|
||||
Alts[E] |
|
||||
Pattern[E]): seq[byte] =
|
||||
encode(toPreserve(x, E))
|
||||
|
||||
proc `$`*(x: TRef | PDiscard | Oid): string =
|
||||
`$`(toPreserve(x))
|
||||
|
||||
proc encode*(x: TRef | PDiscard | Oid): seq[byte] =
|
||||
encode(toPreserve(x))
|
||||
proc encode*(x: PCompound | Reject | CaveatsField | SturdyDescriptionDetail |
|
||||
PAnd |
|
||||
SturdyStepDetail |
|
||||
Rewrite |
|
||||
Parameters |
|
||||
TRef |
|
||||
PBind |
|
||||
Lit |
|
||||
TCompound |
|
||||
SturdyPathStepDetail |
|
||||
PDiscard |
|
||||
Template |
|
||||
Caveat |
|
||||
PNot |
|
||||
SturdyRef |
|
||||
WireRef |
|
||||
TAttenuate |
|
||||
Oid |
|
||||
Alts |
|
||||
Pattern): seq[byte] =
|
||||
encode(toPreserves(x))
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves
|
||||
preserves
|
||||
|
||||
type
|
||||
TcpLocal* {.preservesRecord: "tcp-local".} = object
|
||||
`host`*: string
|
||||
`port`*: BiggestInt
|
||||
|
||||
TcpPeerInfo*[E] {.preservesRecord: "tcp-peer".} = ref object
|
||||
`handle`*: Preserve[E]
|
||||
TcpPeerInfo* {.preservesRecord: "tcp-peer".} = object
|
||||
`handle`* {.preservesEmbedded.}: EmbeddedRef
|
||||
`local`*: TcpLocal
|
||||
`remote`*: TcpRemote
|
||||
|
||||
|
@ -16,14 +16,8 @@ type
|
|||
`host`*: string
|
||||
`port`*: BiggestInt
|
||||
|
||||
proc `$`*[E](x: TcpPeerInfo[E]): string =
|
||||
`$`(toPreserve(x, E))
|
||||
proc `$`*(x: TcpLocal | TcpPeerInfo | TcpRemote): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*[E](x: TcpPeerInfo[E]): seq[byte] =
|
||||
encode(toPreserve(x, E))
|
||||
|
||||
proc `$`*(x: TcpLocal | TcpRemote): string =
|
||||
`$`(toPreserve(x))
|
||||
|
||||
proc encode*(x: TcpLocal | TcpRemote): seq[byte] =
|
||||
encode(toPreserve(x))
|
||||
proc encode*(x: TcpLocal | TcpPeerInfo | TcpRemote): seq[byte] =
|
||||
encode(toPreserves(x))
|
||||
|
|
|
@ -1,30 +1,24 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves
|
||||
preserves
|
||||
|
||||
type
|
||||
TimerExpired*[E] {.preservesRecord: "timer-expired".} = ref object
|
||||
`label`*: Preserve[E]
|
||||
`msecs`*: float64
|
||||
TimerExpired* {.preservesRecord: "timer-expired".} = object
|
||||
`label`*: Value
|
||||
`seconds`*: float
|
||||
|
||||
SetTimer*[E] {.preservesRecord: "set-timer".} = ref object
|
||||
`label`*: Preserve[E]
|
||||
`msecs`*: float64
|
||||
SetTimer* {.preservesRecord: "set-timer".} = object
|
||||
`label`*: Value
|
||||
`seconds`*: float
|
||||
`kind`*: TimerKind
|
||||
|
||||
`TimerKind`* {.preservesOr, pure.} = enum
|
||||
`relative`, `absolute`, `clear`
|
||||
LaterThan* {.preservesRecord: "later-than".} = object
|
||||
`msecs`*: float64
|
||||
`seconds`*: float
|
||||
|
||||
proc `$`*[E](x: TimerExpired[E] | SetTimer[E]): string =
|
||||
`$`(toPreserve(x, E))
|
||||
proc `$`*(x: TimerExpired | SetTimer | LaterThan): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*[E](x: TimerExpired[E] | SetTimer[E]): seq[byte] =
|
||||
encode(toPreserve(x, E))
|
||||
|
||||
proc `$`*(x: LaterThan): string =
|
||||
`$`(toPreserve(x))
|
||||
|
||||
proc encode*(x: LaterThan): seq[byte] =
|
||||
encode(toPreserve(x))
|
||||
proc encode*(x: TimerExpired | SetTimer | LaterThan): seq[byte] =
|
||||
encode(toPreserves(x))
|
||||
|
|
|
@ -0,0 +1,263 @@
|
|||
|
||||
import
|
||||
preserves, protocol
|
||||
|
||||
type
|
||||
TargetedTurnEvent* {.preservesRecord: "event".} = object
|
||||
`target`*: Target
|
||||
`detail`*: TurnEvent
|
||||
|
||||
`LinkedTaskReleaseReason`* {.preservesOr, pure.} = enum
|
||||
`cancelled`, `normal`
|
||||
TurnId* = Value
|
||||
AssertionDescriptionKind* {.pure.} = enum
|
||||
`value`, `opaque`
|
||||
AssertionDescriptionValue* {.preservesRecord: "value".} = object
|
||||
`value`*: Value
|
||||
|
||||
AssertionDescriptionOpaque* {.preservesRecord: "opaque".} = object
|
||||
`description`*: Value
|
||||
|
||||
`AssertionDescription`* {.preservesOr.} = object
|
||||
case orKind*: AssertionDescriptionKind
|
||||
of AssertionDescriptionKind.`value`:
|
||||
`value`*: AssertionDescriptionValue
|
||||
|
||||
of AssertionDescriptionKind.`opaque`:
|
||||
`opaque`*: AssertionDescriptionOpaque
|
||||
|
||||
|
||||
NameKind* {.pure.} = enum
|
||||
`anonymous`, `named`
|
||||
NameAnonymous* {.preservesRecord: "anonymous".} = object
|
||||
|
||||
NameNamed* {.preservesRecord: "named".} = object
|
||||
`name`*: Value
|
||||
|
||||
`Name`* {.preservesOr.} = object
|
||||
case orKind*: NameKind
|
||||
of NameKind.`anonymous`:
|
||||
`anonymous`*: NameAnonymous
|
||||
|
||||
of NameKind.`named`:
|
||||
`named`*: NameNamed
|
||||
|
||||
|
||||
ActorId* = Value
|
||||
FacetId* = Value
|
||||
`FacetStopReason`* {.preservesOr, pure.} = enum
|
||||
`explicitAction`, `inert`, `parentStopping`, `actorStopping`
|
||||
TaskId* = Value
|
||||
ActorActivationKind* {.pure.} = enum
|
||||
`start`, `turn`, `stop`
|
||||
ActorActivationStart* {.preservesRecord: "start".} = object
|
||||
`actorName`*: Name
|
||||
|
||||
ActorActivationStop* {.preservesRecord: "stop".} = object
|
||||
`status`*: ExitStatus
|
||||
|
||||
`ActorActivation`* {.preservesOr.} = object
|
||||
case orKind*: ActorActivationKind
|
||||
of ActorActivationKind.`start`:
|
||||
`start`*: ActorActivationStart
|
||||
|
||||
of ActorActivationKind.`turn`:
|
||||
`turn`*: TurnDescription
|
||||
|
||||
of ActorActivationKind.`stop`:
|
||||
`stop`*: ActorActivationStop
|
||||
|
||||
|
||||
Target* {.preservesRecord: "entity".} = object
|
||||
`actor`*: ActorId
|
||||
`facet`*: FacetId
|
||||
`oid`*: Oid
|
||||
|
||||
TurnCauseKind* {.pure.} = enum
|
||||
`turn`, `cleanup`, `linkedTaskRelease`, `periodicActivation`, `delay`,
|
||||
`external`
|
||||
TurnCauseTurn* {.preservesRecord: "caused-by".} = object
|
||||
`id`*: TurnId
|
||||
|
||||
TurnCauseCleanup* {.preservesRecord: "cleanup".} = object
|
||||
|
||||
TurnCauseLinkedTaskRelease* {.preservesRecord: "linked-task-release".} = object
|
||||
`id`*: TaskId
|
||||
`reason`*: LinkedTaskReleaseReason
|
||||
|
||||
TurnCausePeriodicActivation* {.preservesRecord: "periodic-activation".} = object
|
||||
`period`*: float
|
||||
|
||||
TurnCauseDelay* {.preservesRecord: "delay".} = object
|
||||
`causingTurn`*: TurnId
|
||||
`amount`*: float
|
||||
|
||||
TurnCauseExternal* {.preservesRecord: "external".} = object
|
||||
`description`*: Value
|
||||
|
||||
`TurnCause`* {.preservesOr.} = object
|
||||
case orKind*: TurnCauseKind
|
||||
of TurnCauseKind.`turn`:
|
||||
`turn`*: TurnCauseTurn
|
||||
|
||||
of TurnCauseKind.`cleanup`:
|
||||
`cleanup`*: TurnCauseCleanup
|
||||
|
||||
of TurnCauseKind.`linkedTaskRelease`:
|
||||
`linkedtaskrelease`*: TurnCauseLinkedTaskRelease
|
||||
|
||||
of TurnCauseKind.`periodicActivation`:
|
||||
`periodicactivation`*: TurnCausePeriodicActivation
|
||||
|
||||
of TurnCauseKind.`delay`:
|
||||
`delay`*: TurnCauseDelay
|
||||
|
||||
of TurnCauseKind.`external`:
|
||||
`external`*: TurnCauseExternal
|
||||
|
||||
|
||||
TurnEventKind* {.pure.} = enum
|
||||
`assert`, `retract`, `message`, `sync`, `breakLink`
|
||||
TurnEventAssert* {.preservesRecord: "assert".} = object
|
||||
`assertion`*: AssertionDescription
|
||||
`handle`*: protocol.Handle
|
||||
|
||||
TurnEventRetract* {.preservesRecord: "retract".} = object
|
||||
`handle`*: protocol.Handle
|
||||
|
||||
TurnEventMessage* {.preservesRecord: "message".} = object
|
||||
`body`*: AssertionDescription
|
||||
|
||||
TurnEventSync* {.preservesRecord: "sync".} = object
|
||||
`peer`*: Target
|
||||
|
||||
TurnEventBreakLink* {.preservesRecord: "break-link".} = object
|
||||
`source`*: ActorId
|
||||
`handle`*: protocol.Handle
|
||||
|
||||
`TurnEvent`* {.preservesOr.} = object
|
||||
case orKind*: TurnEventKind
|
||||
of TurnEventKind.`assert`:
|
||||
`assert`*: TurnEventAssert
|
||||
|
||||
of TurnEventKind.`retract`:
|
||||
`retract`*: TurnEventRetract
|
||||
|
||||
of TurnEventKind.`message`:
|
||||
`message`*: TurnEventMessage
|
||||
|
||||
of TurnEventKind.`sync`:
|
||||
`sync`*: TurnEventSync
|
||||
|
||||
of TurnEventKind.`breakLink`:
|
||||
`breaklink`*: TurnEventBreakLink
|
||||
|
||||
|
||||
TurnDescription* {.preservesRecord: "turn".} = object
|
||||
`id`*: TurnId
|
||||
`cause`*: TurnCause
|
||||
`actions`*: seq[ActionDescription]
|
||||
|
||||
ExitStatusKind* {.pure.} = enum
|
||||
`ok`, `Error`
|
||||
`ExitStatus`* {.preservesOr.} = object
|
||||
case orKind*: ExitStatusKind
|
||||
of ExitStatusKind.`ok`:
|
||||
`ok`* {.preservesLiteral: "ok".}: bool
|
||||
|
||||
of ExitStatusKind.`Error`:
|
||||
`error`*: protocol.Error
|
||||
|
||||
|
||||
TraceEntry* {.preservesRecord: "trace".} = object
|
||||
`timestamp`*: float
|
||||
`actor`*: ActorId
|
||||
`item`*: ActorActivation
|
||||
|
||||
Oid* = Value
|
||||
ActionDescriptionKind* {.pure.} = enum
|
||||
`dequeue`, `enqueue`, `dequeueInternal`, `enqueueInternal`, `spawn`, `link`,
|
||||
`facetStart`, `facetStop`, `linkedTaskStart`
|
||||
ActionDescriptionDequeue* {.preservesRecord: "dequeue".} = object
|
||||
`event`*: TargetedTurnEvent
|
||||
|
||||
ActionDescriptionEnqueue* {.preservesRecord: "enqueue".} = object
|
||||
`event`*: TargetedTurnEvent
|
||||
|
||||
ActionDescriptionDequeueInternal* {.preservesRecord: "dequeue-internal".} = object
|
||||
`event`*: TargetedTurnEvent
|
||||
|
||||
ActionDescriptionEnqueueInternal* {.preservesRecord: "enqueue-internal".} = object
|
||||
`event`*: TargetedTurnEvent
|
||||
|
||||
ActionDescriptionSpawn* {.preservesRecord: "spawn".} = object
|
||||
`link`*: bool
|
||||
`id`*: ActorId
|
||||
|
||||
ActionDescriptionLink* {.preservesRecord: "link".} = object
|
||||
`parentActor`*: ActorId
|
||||
`childToParent`*: protocol.Handle
|
||||
`childActor`*: ActorId
|
||||
`parentToChild`*: protocol.Handle
|
||||
|
||||
ActionDescriptionFacetStart* {.preservesRecord: "facet-start".} = object
|
||||
`path`*: seq[FacetId]
|
||||
|
||||
ActionDescriptionFacetStop* {.preservesRecord: "facet-stop".} = object
|
||||
`path`*: seq[FacetId]
|
||||
`reason`*: FacetStopReason
|
||||
|
||||
ActionDescriptionLinkedTaskStart* {.preservesRecord: "linked-task-start".} = object
|
||||
`taskName`*: Name
|
||||
`id`*: TaskId
|
||||
|
||||
`ActionDescription`* {.preservesOr.} = object
|
||||
case orKind*: ActionDescriptionKind
|
||||
of ActionDescriptionKind.`dequeue`:
|
||||
`dequeue`*: ActionDescriptionDequeue
|
||||
|
||||
of ActionDescriptionKind.`enqueue`:
|
||||
`enqueue`*: ActionDescriptionEnqueue
|
||||
|
||||
of ActionDescriptionKind.`dequeueInternal`:
|
||||
`dequeueinternal`*: ActionDescriptionDequeueInternal
|
||||
|
||||
of ActionDescriptionKind.`enqueueInternal`:
|
||||
`enqueueinternal`*: ActionDescriptionEnqueueInternal
|
||||
|
||||
of ActionDescriptionKind.`spawn`:
|
||||
`spawn`*: ActionDescriptionSpawn
|
||||
|
||||
of ActionDescriptionKind.`link`:
|
||||
`link`*: ActionDescriptionLink
|
||||
|
||||
of ActionDescriptionKind.`facetStart`:
|
||||
`facetstart`*: ActionDescriptionFacetStart
|
||||
|
||||
of ActionDescriptionKind.`facetStop`:
|
||||
`facetstop`*: ActionDescriptionFacetStop
|
||||
|
||||
of ActionDescriptionKind.`linkedTaskStart`:
|
||||
`linkedtaskstart`*: ActionDescriptionLinkedTaskStart
|
||||
|
||||
|
||||
proc `$`*(x: TargetedTurnEvent | AssertionDescription | Name | ActorActivation |
|
||||
Target |
|
||||
TurnCause |
|
||||
TurnEvent |
|
||||
TurnDescription |
|
||||
ExitStatus |
|
||||
TraceEntry |
|
||||
ActionDescription): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*(x: TargetedTurnEvent | AssertionDescription | Name |
|
||||
ActorActivation |
|
||||
Target |
|
||||
TurnCause |
|
||||
TurnEvent |
|
||||
TurnDescription |
|
||||
ExitStatus |
|
||||
TraceEntry |
|
||||
ActionDescription): seq[byte] =
|
||||
encode(toPreserves(x))
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves
|
||||
preserves
|
||||
|
||||
type
|
||||
WebSocket* {.preservesRecord: "ws".} = object
|
||||
|
@ -16,7 +16,7 @@ type
|
|||
`port`*: BiggestInt
|
||||
|
||||
proc `$`*(x: WebSocket | Stdio | Unix | Tcp): string =
|
||||
`$`(toPreserve(x))
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*(x: WebSocket | Stdio | Unix | Tcp): seq[byte] =
|
||||
encode(toPreserve(x))
|
||||
encode(toPreserves(x))
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
|
||||
import
|
||||
std/typetraits, preserves
|
||||
preserves
|
||||
|
||||
type
|
||||
Instance*[E] {.preservesRecord: "Instance".} = ref object
|
||||
Instance* {.preservesRecord: "Instance".} = object
|
||||
`name`*: string
|
||||
`argument`*: Preserve[E]
|
||||
`argument`*: Value
|
||||
|
||||
proc `$`*[E](x: Instance[E]): string =
|
||||
`$`(toPreserve(x, E))
|
||||
proc `$`*(x: Instance): string =
|
||||
`$`(toPreserves(x))
|
||||
|
||||
proc encode*[E](x: Instance[E]): seq[byte] =
|
||||
encode(toPreserve(x, E))
|
||||
proc encode*(x: Instance): seq[byte] =
|
||||
encode(toPreserves(x))
|
||||
|
|
|
@ -1,156 +1,156 @@
|
|||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[asyncdispatch, options, tables]
|
||||
import std/[options, tables]
|
||||
import preserves
|
||||
import ./actors, ./durings, ./membranes, ./protocols/[protocol, sturdy]
|
||||
import ../syndicate, ./durings, ./membranes, ./protocols/[gatekeeper, protocol, sturdy, transportAddress]
|
||||
|
||||
from ./patterns import grab
|
||||
when defined(posix):
|
||||
import ./capabilities
|
||||
from std/os import getEnv, `/`
|
||||
|
||||
when defined(traceSyndicate):
|
||||
template trace(args: varargs[untyped]): untyped = stderr.writeLine(args)
|
||||
when defined(posix):
|
||||
template trace(args: varargs[untyped]): untyped = stderr.writeLine(args)
|
||||
else:
|
||||
template trace(args: varargs[untyped]): untyped = echo(args)
|
||||
else:
|
||||
template trace(args: varargs[untyped]): untyped = discard
|
||||
|
||||
type Oid = sturdy.Oid
|
||||
export `$`
|
||||
|
||||
export Route, Stdio, Tcp, WebSocket, Unix
|
||||
|
||||
type
|
||||
Assertion = Preserve[Ref]
|
||||
WireRef = sturdy.WireRef[void]
|
||||
WireAssertion = Preserve[WireRef]
|
||||
Event = protocol.Event[WireRef]
|
||||
TurnEvent = protocol.TurnEvent[WireRef]
|
||||
Packet = protocol.Packet[WireRef]
|
||||
Assertion = Value
|
||||
Event = protocol.Event
|
||||
Handle = actors.Handle
|
||||
Oid = sturdy.Oid
|
||||
Turn = syndicate.Turn
|
||||
WireRef = sturdy.WireRef
|
||||
|
||||
Turn = actors.Turn
|
||||
PacketWriter = proc (turn: Turn; buf: seq[byte]) {.closure.}
|
||||
RelaySetup = proc (turn: Turn; relay: Relay) {.closure.}
|
||||
|
||||
type
|
||||
PacketWriter = proc (pkt: sink Packet): Future[void] {.gcsafe.}
|
||||
RelaySetup = proc (turn: var Turn; relay: Relay) {.gcsafe.}
|
||||
|
||||
Relay = ref object of RootObj
|
||||
Relay* = ref object
|
||||
facet: Facet
|
||||
inboundAssertions: Table[Handle,
|
||||
tuple[localHandle: Handle, imported: seq[WireSymbol]]]
|
||||
outboundAssertions: Table[Handle, seq[WireSymbol]]
|
||||
pendingTurn: protocol.Turn
|
||||
exported: Membrane
|
||||
imported: Membrane
|
||||
nextLocalOid: Oid
|
||||
pendingTurn: protocol.Turn[WireRef]
|
||||
wireBuf: BufferedDecoder
|
||||
packetWriter: PacketWriter
|
||||
untrusted: bool
|
||||
peer: Cap
|
||||
|
||||
SyncPeerEntity = ref object of Entity
|
||||
relay: Relay
|
||||
peer: Ref
|
||||
peer: Cap
|
||||
handleMap: Table[Handle, Handle]
|
||||
e: WireSymbol
|
||||
|
||||
RelayEntity = ref object of Entity
|
||||
## https://synit.org/book/protocol.html#relay-entities
|
||||
label: string
|
||||
relay: Relay
|
||||
|
||||
proc releaseRefOut(r: Relay; e: WireSymbol) =
|
||||
proc releaseCapOut(r: Relay; e: WireSymbol) =
|
||||
r.exported.drop e
|
||||
|
||||
method publish(spe: SyncPeerEntity; t: var Turn; v: Assertion; h: Handle) =
|
||||
spe.handleMap[h] = publish(t, spe.peer, v)
|
||||
method publish(spe: SyncPeerEntity; t: Turn; a: AssertionRef; h: Handle) =
|
||||
spe.handleMap[h] = publish(t, spe.peer, a.value)
|
||||
|
||||
method retract(se: SyncPeerEntity; t: var Turn; h: Handle) =
|
||||
method retract(se: SyncPeerEntity; t: Turn; h: Handle) =
|
||||
var other: Handle
|
||||
if se.handleMap.pop(h, other):
|
||||
retract(t, other)
|
||||
|
||||
method message(se: SyncPeerEntity; t: var Turn; v: Assertion) =
|
||||
method message(se: SyncPeerEntity; t: Turn; a: AssertionRef) =
|
||||
if not se.e.isNil:
|
||||
se.relay.releaseRefOut(se.e)
|
||||
message(t, se.peer, v)
|
||||
se.relay.releaseCapOut(se.e)
|
||||
message(t, se.peer, a.value)
|
||||
|
||||
method sync(se: SyncPeerEntity; t: var Turn; peer: Ref) =
|
||||
method sync(se: SyncPeerEntity; t: Turn; peer: Cap) =
|
||||
sync(t, se.peer, peer)
|
||||
|
||||
proc newSyncPeerEntity(r: Relay; p: Ref): SyncPeerEntity =
|
||||
proc newSyncPeerEntity(r: Relay; p: Cap): SyncPeerEntity =
|
||||
SyncPeerEntity(relay: r, peer: p)
|
||||
|
||||
proc rewriteRefOut(relay: Relay; `ref`: Ref; transient: bool; exported: var seq[WireSymbol]): WireRef =
|
||||
if `ref`.target of RelayEntity and `ref`.target.RelayEntity.relay == relay and `ref`.attenuation.len == 0:
|
||||
WireRef(orKind: WireRefKind.yours, yours: WireRefYours[void](oid: `ref`.target.oid))
|
||||
proc rewriteCapOut(relay: Relay; cap: Cap; exported: var seq[WireSymbol]): WireRef =
|
||||
if cap.target of RelayEntity and cap.target.RelayEntity.relay == relay and cap.attenuation.len == 0:
|
||||
result = WireRef(orKind: WireRefKind.yours, yours: WireRefYours(oid: cap.target.oid))
|
||||
else:
|
||||
var ws = grab(relay.exported, `ref`)
|
||||
var ws = grab(relay.exported, cap)
|
||||
if ws.isNil:
|
||||
doAssert(not transient, "Cannot send transient reference")
|
||||
ws = newWireSymbol(relay.exported, relay.nextLocalOid, `ref`)
|
||||
ws = newWireSymbol(relay.exported, relay.nextLocalOid, cap)
|
||||
inc relay.nextLocalOid
|
||||
exported.add ws
|
||||
WireRef(
|
||||
result = WireRef(
|
||||
orKind: WireRefKind.mine,
|
||||
mine: WireRefMine(oid: ws.oid))
|
||||
|
||||
proc rewriteOut(relay: Relay; v: Assertion; transient: bool):
|
||||
tuple[rewritten: WireAssertion, exported: seq[WireSymbol]] =
|
||||
proc rewriteOut(relay: Relay; v: Assertion):
|
||||
tuple[rewritten: Value, exported: seq[WireSymbol]] =
|
||||
var exported: seq[WireSymbol]
|
||||
result.rewritten = mapEmbeds[Ref, WireRef](v) do (r: Ref) -> WireRef:
|
||||
rewriteRefOut(relay, r, transient, exported)
|
||||
result.rewritten = mapEmbeds(v) do (pr: Value) -> Value:
|
||||
let o = pr.unembed(Cap); if o.isSome:
|
||||
rewriteCapOut(relay, o.get, exported).toPreserves
|
||||
else: pr
|
||||
result.exported = exported
|
||||
|
||||
proc register(relay: Relay; v: Assertion): WireAssertion =
|
||||
rewriteOut(relay, v, false).rewritten
|
||||
|
||||
proc register(relay: Relay; v: Assertion; h: Handle): WireAssertion =
|
||||
var (rewritten, exported) = rewriteOut(relay, v, false)
|
||||
relay.outboundAssertions[h] = exported
|
||||
rewritten
|
||||
proc register(relay: Relay; v: Assertion; h: Handle): tuple[rewritten: Value, exported: seq[WireSymbol]] =
|
||||
result = rewriteOut(relay, v)
|
||||
relay.outboundAssertions[h] = result.exported
|
||||
|
||||
proc deregister(relay: Relay; h: Handle) =
|
||||
var outbound: seq[WireSymbol]
|
||||
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] =
|
||||
assert(not r.packetWriter.isNil, "missing packetWriter proc")
|
||||
r.packetWriter(pkt)
|
||||
proc send(relay: Relay; turn: Turn; rOid: protocol.Oid; m: Event) =
|
||||
# TODO: don't send right away.
|
||||
relay.pendingTurn.add TurnEvent(oid: rOid, event: m)
|
||||
queueEffect(turn, relay.facet) do (turn: Turn):
|
||||
if relay.pendingTurn.len > 0:
|
||||
var pkt = Packet(
|
||||
orKind: PacketKind.Turn,
|
||||
turn: move relay.pendingTurn)
|
||||
trace "C: ", pkt
|
||||
relay.packetWriter(turn, encode pkt)
|
||||
|
||||
proc send(r: Relay; rOid: protocol.Oid; m: Event) =
|
||||
if r.pendingTurn.len == 0:
|
||||
callSoon:
|
||||
r.facet.run do (turn: var Turn):
|
||||
var pkt = Packet(
|
||||
orKind: PacketKind.Turn,
|
||||
turn: move r.pendingTurn)
|
||||
trace "C: ", pkt
|
||||
asyncCheck(turn, r.send(pkt))
|
||||
r.pendingTurn.add TurnEvent(oid: rOid, event: m)
|
||||
proc send(re: RelayEntity; turn: Turn; ev: Event) =
|
||||
send(re.relay, turn, protocol.Oid re.oid, ev)
|
||||
|
||||
proc send(re: RelayEntity; ev: Event) =
|
||||
send(re.relay, protocol.Oid re.oid, ev)
|
||||
|
||||
method publish(re: RelayEntity; t: var Turn; v: Assertion; h: Handle) =
|
||||
re.send Event(
|
||||
method publish(re: RelayEntity; t: Turn; a: AssertionRef; h: Handle) =
|
||||
re.send(t, Event(
|
||||
orKind: EventKind.Assert,
|
||||
`assert`: protocol.Assert[WireRef](
|
||||
assertion: re.relay.register(v, h),
|
||||
handle: h))
|
||||
`assert`: protocol.Assert(
|
||||
assertion: re.relay.register(a.value, h).rewritten,
|
||||
handle: h)))
|
||||
|
||||
method retract(re: RelayEntity; t: var Turn; h: Handle) =
|
||||
method retract(re: RelayEntity; t: Turn; h: Handle) =
|
||||
re.relay.deregister h
|
||||
re.send Event(
|
||||
re.send(t, Event(
|
||||
orKind: EventKind.Retract,
|
||||
retract: Retract(handle: h))
|
||||
retract: Retract(handle: h)))
|
||||
|
||||
method message(re: RelayEntity; turn: var Turn; msg: Assertion) =
|
||||
re.send Event(orKind: EventKind.Message,
|
||||
message: Message[WireRef](body: register(re.relay, msg)))
|
||||
method message(re: RelayEntity; turn: Turn; msg: AssertionRef) =
|
||||
var (value, exported) = rewriteOut(re.relay, msg.value)
|
||||
assert(len(exported) == 0, "cannot send a reference in a message")
|
||||
if len(exported) == 0:
|
||||
re.send(turn, Event(orKind: EventKind.Message, message: Message(body: value)))
|
||||
|
||||
method sync(re: RelayEntity; turn: var Turn; peer: Ref) =
|
||||
method sync(re: RelayEntity; turn: Turn; peer: Cap) =
|
||||
var
|
||||
peerEntity = newSyncPeerEntity(re.relay, peer)
|
||||
exported: seq[WireSymbol]
|
||||
discard rewriteRefOut(re.relay, turn.newRef(peerEntity), false, exported)
|
||||
# TODO: discard?
|
||||
wr = rewriteCapOut(re.relay, turn.newCap(peerEntity), exported)
|
||||
peerEntity.e = exported[0]
|
||||
re.send Event(
|
||||
orKind: EventKind.Sync,
|
||||
sync: Sync[WireRef](peer: embed toPreserve(false, WireRef))) # TODO: send the WireRef?
|
||||
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 =
|
||||
RelayEntity(label: label, relay: r, oid: o)
|
||||
|
@ -159,43 +159,47 @@ using
|
|||
relay: Relay
|
||||
facet: Facet
|
||||
|
||||
proc lookupLocal(relay; oid: Oid): Ref =
|
||||
proc lookupLocal(relay; oid: Oid): Cap =
|
||||
let sym = relay.exported.grab oid
|
||||
if sym.isNil: newInertRef()
|
||||
else: sym.`ref`
|
||||
if not sym.isNil:
|
||||
result = sym.cap
|
||||
|
||||
proc isInert(r: Ref): bool =
|
||||
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
|
||||
of WireRefKind.mine:
|
||||
var e = relay.imported.grab(n.mine.oid)
|
||||
if e.isNil: e = newWireSymbol(
|
||||
relay.imported,
|
||||
n.mine.oid,
|
||||
newRef(facet, newRelayEntity("rewriteRefIn", relay, n.mine.oid)))
|
||||
if e.isNil:
|
||||
e = newWireSymbol(
|
||||
relay.imported,
|
||||
n.mine.oid,
|
||||
newCap(facet, newRelayEntity("rewriteCapIn", relay, n.mine.oid)),
|
||||
)
|
||||
imported.add e
|
||||
result = e.`ref`
|
||||
result = e.cap
|
||||
of WireRefKind.yours:
|
||||
let r = relay.lookupLocal(n.yours.oid)
|
||||
if n.yours.attenuation.len == 0 or r.isInert: result = r
|
||||
else: raiseAssert "attenuation not implemented"
|
||||
result = relay.lookupLocal(n.yours.oid)
|
||||
if result.isNil:
|
||||
result = newInertCap()
|
||||
elif n.yours.attenuation.len > 0:
|
||||
result = attenuate(result, n.yours.attenuation)
|
||||
|
||||
proc rewriteIn(relay; facet; a: Preserve[WireRef]):
|
||||
proc rewriteIn(relay; facet; v: Value):
|
||||
tuple[rewritten: Assertion; imported: seq[WireSymbol]] =
|
||||
var imported: seq[WireSymbol]
|
||||
result.rewritten = mapEmbeds(a) do (wr: WireRef) -> Ref:
|
||||
rewriteRefIn(relay, facet, wr, imported)
|
||||
result.rewritten = mapEmbeds(v) do (pr: Value) -> Value:
|
||||
let wr = pr.preservesTo WireRef; if wr.isSome:
|
||||
result = rewriteCapIn(relay, facet, wr.get, imported).embed
|
||||
else:
|
||||
result = pr
|
||||
result.imported = imported
|
||||
|
||||
proc close(r: Relay) = discard
|
||||
|
||||
proc dispatch(relay: Relay; turn: var Turn; `ref`: Ref; event: Event) =
|
||||
proc dispatch(relay: Relay; turn: Turn; cap: Cap; event: Event) =
|
||||
case event.orKind
|
||||
of EventKind.Assert:
|
||||
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:
|
||||
let remoteHandle = event.retract.handle
|
||||
|
@ -207,175 +211,450 @@ proc dispatch(relay: Relay; turn: var Turn; `ref`: Ref; event: Event) =
|
|||
of EventKind.Message:
|
||||
let (a, imported) = rewriteIn(relay, turn.facet, event.message.body)
|
||||
assert imported.len == 0, "Cannot receive transient reference"
|
||||
turn.message(`ref`, a)
|
||||
turn.message(cap, a)
|
||||
|
||||
of EventKind.Sync:
|
||||
discard # TODO
|
||||
#[
|
||||
var imported: seq[WireSymbol]
|
||||
let k = relay.rewriteRefIn(turn, evenr.sync.peer, imported)
|
||||
turn.sync(`ref`) do (turn: var Turn):
|
||||
turn.message(k, true)
|
||||
for e in imported: relay.imported.del e
|
||||
]#
|
||||
turn.sync(cap) do (turn: Turn):
|
||||
var
|
||||
(v, imported) = rewriteIn(relay, turn.facet, event.sync.peer)
|
||||
peer = unembed(v, Cap)
|
||||
if peer.isSome:
|
||||
turn.message(get peer, true)
|
||||
for e in imported: relay.imported.drop e
|
||||
|
||||
proc dispatch(relay: Relay; v: Preserve[WireRef]) =
|
||||
proc dispatch(relay: Relay; v: Value) =
|
||||
trace "S: ", v
|
||||
run(relay.facet) do (t: var Turn):
|
||||
run(relay.facet) do (t: Turn):
|
||||
var pkt: Packet
|
||||
if fromPreserve(pkt, v):
|
||||
if pkt.fromPreserves(v):
|
||||
case pkt.orKind
|
||||
of PacketKind.Turn:
|
||||
# https://synit.org/book/protocol.html#turn-packets
|
||||
for te in pkt.turn:
|
||||
dispatch(relay, t, lookupLocal(relay, te.oid.Oid), te.event)
|
||||
let r = lookupLocal(relay, te.oid.Oid)
|
||||
if not r.isNil:
|
||||
dispatch(relay, t, r, te.event)
|
||||
of PacketKind.Error:
|
||||
stderr.writeLine ("Error from server: ", pkt.error.message, " (detail: ", pkt.error.detail, ")")
|
||||
# https://synit.org/book/protocol.html#error-packets
|
||||
when defined(posix):
|
||||
stderr.writeLine("Error from server: ", pkt.error.message, " (detail: ", pkt.error.detail, ")")
|
||||
close relay
|
||||
of PacketKind.Extension:
|
||||
# https://synit.org/book/protocol.html#extension-packets
|
||||
discard
|
||||
of PacketKind.Nop:
|
||||
discard
|
||||
else:
|
||||
stderr.writeLine "discarding unparsed packet ", v
|
||||
when defined(posix):
|
||||
stderr.writeLine("discarding undecoded packet ", v)
|
||||
|
||||
proc recv(relay: Relay; buf: openarray[byte]; slice: Slice[int]) =
|
||||
feed(relay.wireBuf, buf, slice)
|
||||
var pr = decode(relay.wireBuf)
|
||||
if pr.isSome: dispatch(relay, pr.get)
|
||||
|
||||
proc recv(relay: Relay; buf: openarray[byte]) {.used.} =
|
||||
feed(relay.wireBuf, buf)
|
||||
var pr = decode(relay.wireBuf)
|
||||
if pr.isSome: dispatch(relay, pr.get)
|
||||
|
||||
type
|
||||
RelayOptions = object of RootObj
|
||||
packetWriter: PacketWriter
|
||||
untrusted: bool
|
||||
RelayActorOptions = object of RelayOptions
|
||||
initialOid: Option[Oid]
|
||||
initialRef: Ref
|
||||
nextLocalOid: Option[Oid]
|
||||
RelayOptions* = object of RootObj
|
||||
packetWriter*: PacketWriter
|
||||
|
||||
proc newRelay(turn: var Turn; opts: RelayOptions; setup: RelaySetup): Relay =
|
||||
result = Relay(
|
||||
facet: turn.facet,
|
||||
packetWriter: opts.packetWriter,
|
||||
untrusted: opts.untrusted)
|
||||
discard result.facet.preventInertCheck()
|
||||
setup(turn, result)
|
||||
RelayActorOptions* = object of RelayOptions
|
||||
initialOid*: Option[Oid]
|
||||
initialCap*: Cap
|
||||
nextLocalOid*: Option[Oid]
|
||||
|
||||
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):
|
||||
let relay = newRelay(turn, opts, setup)
|
||||
if not opts.initialRef.isNil:
|
||||
proc spawnRelay(name: string; turn: Turn; opts: RelayActorOptions; setup: RelaySetup) =
|
||||
linkActor(turn, name) do (turn: Turn):
|
||||
turn.preventInertCheck()
|
||||
let relay = Relay(
|
||||
facet: turn.facet,
|
||||
packetWriter: opts.packetWriter,
|
||||
wireBuf: newBufferedDecoder(0),
|
||||
)
|
||||
if not opts.initialCap.isNil:
|
||||
var exported: seq[WireSymbol]
|
||||
discard rewriteRefOut(relay, opts.initialRef, false, 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)
|
||||
discard rewriteCapOut(relay, opts.initialCap, exported)
|
||||
opts.nextLocalOid.map do (oid: Oid):
|
||||
relay.nextLocalOid =
|
||||
if oid == 0.Oid: 1.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)
|
||||
|
||||
import std/asyncnet
|
||||
from std/nativesockets import AF_UNIX, SOCK_STREAM, Protocol
|
||||
proc rejected(detail: Value): Resolved =
|
||||
result = Resolved(orKind: ResolvedKind.Rejected)
|
||||
result.rejected.detail = detail
|
||||
|
||||
import protocols/gatekeeper
|
||||
proc accepted(cap: Cap): Resolved =
|
||||
result = Resolved(orKind: ResolvedKind.accepted)
|
||||
result.accepted.responderSession = cap
|
||||
|
||||
type ShutdownEntity = ref object of Entity
|
||||
method retract(e: ShutdownEntity; turn: Turn; h: Handle) =
|
||||
stopActor(e.facet)
|
||||
|
||||
method retract(e: ShutdownEntity; turn: var Turn; h: Handle) =
|
||||
stopActor(turn)
|
||||
when defined(posix):
|
||||
|
||||
type
|
||||
SturdyRef = sturdy.SturdyRef[Ref]
|
||||
Resolve = gatekeeper.Resolve[Ref]
|
||||
ConnectProc* = proc (turn: var Turn; ds: Ref) {.gcsafe.}
|
||||
import std/[oserrors, posix]
|
||||
import pkg/sys/[files, handles, ioqueue, sockets]
|
||||
export transportAddress.Unix
|
||||
|
||||
proc connectUnix*(turn: var Turn; path: string; cap: SturdyRef; bootProc: ConnectProc) =
|
||||
var socket = newAsyncSocket(
|
||||
domain = AF_UNIX,
|
||||
sockType = SOCK_STREAM,
|
||||
protocol = cast[Protocol](0),
|
||||
buffered = false)
|
||||
proc socketWriter(packet: sink Packet): Future[void] =
|
||||
socket.send($packet)
|
||||
const recvSize = 1 shl 18
|
||||
# this is an excessive buffer size but the PEG parser
|
||||
# can only read complete documents
|
||||
# TODO: use a binary protocol and improve that
|
||||
# parser to stream data in chunks
|
||||
var shutdownRef: Ref
|
||||
let reenable = turn.facet.preventInertCheck()
|
||||
let connectionClosedRef = newRef(turn, ShutdownEntity())
|
||||
var fut = newFuture[void]"connectUnix"
|
||||
connectUnix(socket, path).addCallback do (f: Future[void]):
|
||||
read f
|
||||
discard bootActor("unix") do (turn: var Turn):
|
||||
var ops = RelayActorOptions(
|
||||
packetWriter: socketWriter,
|
||||
initialOid: 0.Oid.some)
|
||||
let relayFut = spawnRelay("unix", turn, ops) do (turn: var Turn; relay: Relay):
|
||||
let facet = turn.facet
|
||||
proc recvCb(pktFut: Future[string]) {.gcsafe.} =
|
||||
if pktFut.failed:
|
||||
run(facet) do (turn: var Turn): stopActor(turn)
|
||||
else:
|
||||
let buf = pktFut.read
|
||||
if buf.len == 0:
|
||||
run(facet) do (turn: var Turn): stopActor(turn)
|
||||
else:
|
||||
var pr = parsePreserves(buf, WireRef)
|
||||
dispatch(relay, cast[Preserve[WireRef]](pr))
|
||||
socket.recv(recvSize).addCallback(recvCb)
|
||||
# TODO: should this need be callSoon?
|
||||
socket.recv(recvSize).addCallback(recvCb)
|
||||
turn.facet.actor.atExit do (turn: var Turn): close(socket)
|
||||
discard publish(turn, connectionClosedRef, true)
|
||||
shutdownRef = newRef(turn, ShutdownEntity())
|
||||
relayFut.addCallback do (refFut: Future[Ref]):
|
||||
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 = facet(turn) do (turn: var Turn):
|
||||
bootProc(turn, unembed a)
|
||||
proc action(turn: var Turn) =
|
||||
stop(turn, facet)
|
||||
result = action
|
||||
var res = Resolve(
|
||||
sturdyref: cap,
|
||||
observer: embed newRef(turn, during(duringCallback)))
|
||||
discard publish(turn, gatekeeper, res)
|
||||
fut.complete()
|
||||
asyncCheck(turn, fut)
|
||||
type StdioEntity = ref object of Entity
|
||||
relay: Relay
|
||||
stdin: AsyncFile
|
||||
alive: bool
|
||||
|
||||
import std/asyncfile
|
||||
method message(entity: StdioEntity; turn: Turn; ass: AssertionRef) =
|
||||
if ass.value.preservesTo(ForceDisconnect).isSome:
|
||||
entity.alive = false
|
||||
|
||||
proc connectStdio*(ds: Ref; turn: var Turn) =
|
||||
## Connect to an external dataspace over stdin and stdout.
|
||||
proc stdoutWriter(packet: sink Packet): Future[void] {.async.} =
|
||||
# var buf = encode(packet)
|
||||
# doAssert writeBytes(stdout, buf, 0, buf.len) == buf.len
|
||||
write(stdout, packet)
|
||||
flushFile(stdout)
|
||||
var opts = RelayActorOptions(
|
||||
packetWriter: stdoutWriter,
|
||||
initialRef: ds,
|
||||
initialOid: 0.Oid.some)
|
||||
asyncCheck spawnRelay("stdio", turn, opts) do (turn: var Turn; relay: Relay):
|
||||
let
|
||||
facet = turn.facet
|
||||
asyncStdin = openAsync("/dev/stdin")
|
||||
facet.actor.atExit do (turn: var Turn):
|
||||
close(asyncStdin)
|
||||
proc recvCb(pktFut: Future[string]) {.gcsafe.} =
|
||||
if pktFut.failed: discard
|
||||
proc loop(entity: StdioEntity) {.asyncio.} =
|
||||
let buf = new seq[byte]
|
||||
entity.alive = true
|
||||
while entity.alive:
|
||||
buf[].setLen(0x1000)
|
||||
let n = read(entity.stdin, buf)
|
||||
if n > 0:
|
||||
entity.relay.recv(buf[], 0..<n)
|
||||
else:
|
||||
let buf = pktFut.read
|
||||
if buf.len == 0:
|
||||
run(facet) do (turn: var Turn): stopActor(turn)
|
||||
entity.alive = false
|
||||
if n < 0: raiseOSError(osLastError())
|
||||
stopActor(entity.facet)
|
||||
|
||||
proc connectTransport(turn: Turn; ds: Cap; ta: transportAddress.Stdio) =
|
||||
## Connect to an external dataspace over stdio.
|
||||
let localDataspace = newDataspace(turn)
|
||||
proc stdoutWriter(turn: Turn; buf: seq[byte]) =
|
||||
## Blocking write to stdout.
|
||||
let n = writeBytes(stdout, buf, 0, buf.len)
|
||||
flushFile(stdout)
|
||||
if n != buf.len:
|
||||
stopActor(turn)
|
||||
var opts = RelayActorOptions(
|
||||
packetWriter: stdoutWriter,
|
||||
initialCap: localDataspace,
|
||||
initialOid: 0.Oid.some,
|
||||
)
|
||||
spawnRelay("stdio", turn, opts) do (turn: Turn; relay: Relay):
|
||||
let
|
||||
facet = turn.facet
|
||||
fd = stdin.getOsFileHandle()
|
||||
flags = fcntl(fd.cint, F_GETFL, 0)
|
||||
if flags < 0: raiseOSError(osLastError())
|
||||
if fcntl(fd.cint, F_SETFL, flags or O_NONBLOCK) < 0:
|
||||
raiseOSError(osLastError())
|
||||
let entity = StdioEntity(
|
||||
facet: turn.facet, relay: relay, stdin: newAsyncFile(FD fd))
|
||||
onStop(entity.facet) do (turn: Turn):
|
||||
entity.alive = false
|
||||
close(entity.stdin)
|
||||
# Close stdin to remove it from the ioqueue
|
||||
discard trampoline:
|
||||
whelp loop(entity)
|
||||
publish(turn, ds, TransportConnection(
|
||||
`addr`: ta.toPreserves,
|
||||
control: newCap(entity, turn),
|
||||
resolved: localDataspace.accepted,
|
||||
))
|
||||
|
||||
proc connectStdio*(turn: Turn; ds: Cap) =
|
||||
## Connect to an external dataspace over stdin and stdout.
|
||||
connectTransport(turn, ds, transportAddress.Stdio())
|
||||
|
||||
type
|
||||
TcpEntity = ref object of Entity
|
||||
relay: Relay
|
||||
sock: AsyncConn[sockets.Protocol.TCP]
|
||||
alive: bool
|
||||
|
||||
UnixEntity = ref object of Entity
|
||||
relay: Relay
|
||||
sock: AsyncConn[sockets.Protocol.Unix]
|
||||
alive: bool
|
||||
|
||||
SocketEntity = TcpEntity | UnixEntity
|
||||
|
||||
method message(entity: SocketEntity; turn: Turn; ass: AssertionRef) =
|
||||
if ass.value.preservesTo(ForceDisconnect).isSome:
|
||||
entity.alive = false
|
||||
|
||||
template bootSocketEntity() {.dirty.} =
|
||||
proc setup(turn: Turn) {.closure.} =
|
||||
proc kill(turn: Turn) =
|
||||
if entity.alive:
|
||||
entity.alive = false
|
||||
close(entity.sock)
|
||||
onStop(turn, kill)
|
||||
var ass = TransportConnection(
|
||||
`addr`: ta.toPreserves,
|
||||
control: newCap(entity, turn),
|
||||
resolved: entity.relay.peer.accepted,
|
||||
)
|
||||
publish(turn, ds, ass)
|
||||
run(entity.relay.facet, setup)
|
||||
let buf = new seq[byte]
|
||||
entity.alive = true
|
||||
while entity.alive:
|
||||
buf[].setLen(0x1000)
|
||||
let n = read(entity.sock, buf)
|
||||
if n > 0:
|
||||
entity.relay.recv(buf[], 0..<n)
|
||||
else:
|
||||
entity.alive = false
|
||||
if n < 0: raiseOSError(osLastError())
|
||||
stopActor(entity.facet)
|
||||
# the socket closes when the actor is stopped
|
||||
|
||||
proc boot(entity: TcpEntity; ta: transportAddress.Tcp; ds: Cap) {.asyncio.} =
|
||||
entity.sock = connectTcpAsync(ta.host, Port ta.port)
|
||||
bootSocketEntity()
|
||||
|
||||
proc boot(entity: UnixEntity; ta: transportAddress.Unix; ds: Cap) {.asyncio.} =
|
||||
entity.sock = connectUnixAsync(ta.path)
|
||||
bootSocketEntity()
|
||||
|
||||
template spawnSocketRelay() {.dirty.} =
|
||||
proc writeConn(turn: Turn; buf: seq[byte]) =
|
||||
if entity.alive:
|
||||
discard trampoline:
|
||||
whelp write(entity.sock, buf)
|
||||
var ops = RelayActorOptions(
|
||||
packetWriter: writeConn,
|
||||
initialOid: 0.Oid.some,
|
||||
)
|
||||
spawnRelay("socket", turn, ops) do (turn: Turn; relay: Relay):
|
||||
entity.facet = turn.facet
|
||||
entity.relay = relay
|
||||
discard trampoline:
|
||||
whelp boot(entity, ta, ds)
|
||||
|
||||
proc connectTransport(turn: Turn; ds: Cap; ta: transportAddress.Tcp) =
|
||||
let entity = TcpEntity()
|
||||
spawnSocketRelay()
|
||||
|
||||
proc connectTransport(turn: Turn; ds: Cap; ta: transportAddress.Unix) =
|
||||
let entity = UnixEntity()
|
||||
spawnSocketRelay()
|
||||
|
||||
elif defined(solo5):
|
||||
|
||||
import solo5_dispatcher
|
||||
import taps
|
||||
|
||||
type
|
||||
TcpEntity = ref object of Entity
|
||||
relay: Relay
|
||||
conn: Connection
|
||||
decoder: BufferedDecoder
|
||||
|
||||
method message(entity: TcpEntity; turn: Turn; ass: AssertionRef) =
|
||||
if ass.value.preservesTo(ForceDisconnect).isSome:
|
||||
entity.conn.abort()
|
||||
|
||||
proc connectTransport(turn: Turn; ds: Cap; ta: transportAddress.Tcp) =
|
||||
let entity = TcpEntity(facet: turn.facet)
|
||||
|
||||
proc writeConn(turn: Turn; buf: seq[byte]) =
|
||||
assert not entity.conn.isNil
|
||||
entity.conn.batch:
|
||||
entity.conn.send(buf)
|
||||
var ops = RelayActorOptions(
|
||||
packetWriter: writeConn,
|
||||
initialOid: 0.Oid.some,
|
||||
)
|
||||
spawnRelay("socket", turn, ops) do (turn: Turn; relay: Relay):
|
||||
entity.facet = turn.facet
|
||||
entity.relay = relay
|
||||
|
||||
var ep = newRemoteEndpoint()
|
||||
if ta.host.isIpAddress:
|
||||
ep.with ta.host.parseIpAddress
|
||||
else:
|
||||
ep.withHostname ta.host
|
||||
ep.with ta.port.Port
|
||||
|
||||
var tp = newTransportProperties()
|
||||
tp.require "reliability"
|
||||
tp.ignore "congestion-control"
|
||||
tp.ignore "preserve-order"
|
||||
|
||||
var preconn = newPreconnection(
|
||||
remote=[ep], transport=tp.some)
|
||||
entity.conn = preconn.initiate()
|
||||
entity.facet.onStop do (turn: Turn):
|
||||
entity.conn.close()
|
||||
entity.conn.onConnectionError do (err: ref Exception):
|
||||
run(entity.facet) do (turn: Turn):
|
||||
terminate(turn, err)
|
||||
entity.conn.onClosed():
|
||||
stop(entity.facet)
|
||||
entity.conn.onReceivedPartial do (data: seq[byte]; ctx: MessageContext; eom: bool):
|
||||
entity.relay.recv(data)
|
||||
if eom:
|
||||
stop(entity.facet)
|
||||
else:
|
||||
var v = parsePreserves(buf)
|
||||
dispatch(relay, cast[Preserve[WireRef]](v))
|
||||
callSoon: asyncStdin.readLine().addCallback(recvCb)
|
||||
# do not process the next line immediately
|
||||
asyncStdin.readLine().addCallback(recvCb)
|
||||
entity.conn.receive()
|
||||
entity.conn.onReady do ():
|
||||
entity.facet.run do (turn: Turn):
|
||||
publish(turn, ds, TransportConnection(
|
||||
`addr`: ta.toPreserves,
|
||||
control: newCap(entity, turn),
|
||||
resolved: entity.relay.peer.accepted,
|
||||
))
|
||||
entity.conn.receive()
|
||||
|
||||
proc walk(turn: Turn; ds, origin: Cap; route: Route; transOff, stepOff: int) =
|
||||
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: 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):
|
||||
origin.relay.run do (turn: Turn):
|
||||
# walk using the facet that manages the transport connection
|
||||
walk(turn, ds, origin, route, transOff, 0)
|
||||
|
||||
type StepCallback = proc (turn: Turn; step: Value; origin: Cap; res: Resolved) {.closure.}
|
||||
|
||||
proc spawnStepResolver(turn: Turn; ds: Cap; stepType: Value; cb: StepCallback) =
|
||||
let pat = observePattern(
|
||||
ResolvedPathStep?:{1: grabRecord(stepType)},
|
||||
{ @[0.toPreserve]: grabLit(), @[1.toPreserve]: grab() },
|
||||
)
|
||||
during(turn, ds, pat) do (origin: Cap; stepDetail: Literal[Value]):
|
||||
proc duringCallback(turn: Turn; ass: Value; h: Handle): TurnAction =
|
||||
var res: Resolved
|
||||
if res.fromPreserves ass:
|
||||
cb(turn, stepDetail.value, origin, res)
|
||||
proc action(turn: Turn) =
|
||||
stop(turn)
|
||||
result = action
|
||||
publish(turn, origin, Resolve(
|
||||
step: stepDetail.value, observer: newCap(turn, during(duringCallback))))
|
||||
|
||||
proc spawnRelays*(turn: Turn; ds: Cap) =
|
||||
## Spawn actors that manage routes and appease gatekeepers.
|
||||
|
||||
let transPat = observePattern(!TransportConnection, { @[0.toPreserves]: grab() })
|
||||
# Use a generic pattern and type matching
|
||||
# in the during handler because it is easy.
|
||||
|
||||
when defined(posix):
|
||||
let stdioPat = ?Observe(pattern: TransportConnection?:{0: ?:Stdio})
|
||||
during(turn, ds, stdioPat) do:
|
||||
connectTransport(turn, ds, Stdio())
|
||||
|
||||
# TODO: unix pattern
|
||||
during(turn, ds, transPat) do (ta: Literal[transportAddress.Unix]):
|
||||
try: connectTransport(turn, ds, ta.value)
|
||||
except exceptions.IOError as e:
|
||||
publish(turn, ds, TransportConnection(
|
||||
`addr`: ta.toPreserve,
|
||||
resolved: rejected(embed e),
|
||||
))
|
||||
|
||||
# TODO: tcp pattern
|
||||
during(turn, ds, transPat) do (ta: Literal[transportAddress.Tcp]):
|
||||
try: connectTransport(turn, ds, ta.value)
|
||||
except exceptions.IOError as e:
|
||||
publish(turn, ds, TransportConnection(
|
||||
`addr`: ta.toPreserve,
|
||||
resolved: rejected(embed e),
|
||||
))
|
||||
|
||||
let resolvePat = observePattern(!ResolvePath, {@[0.toPreserves]: grab()})
|
||||
during(turn, ds, resolvePat) do (route: Literal[Route]):
|
||||
for i, transAddr in route.value.transports:
|
||||
connectRoute(turn, ds, route.value, i)
|
||||
|
||||
spawnStepResolver(turn, ds, "ref".toSymbol) do (
|
||||
turn: Turn, step: Value, origin: Cap, res: Resolved):
|
||||
publish(turn, ds, ResolvedPathStep(
|
||||
origin: origin, pathStep: step, resolved: res))
|
||||
|
||||
type BootProc* = proc (turn: Turn; ds: Cap) {.closure.}
|
||||
|
||||
proc resolve*(turn: Turn; ds: Cap; route: Route; bootProc: BootProc) =
|
||||
## Resolve `route` within `ds` and call `bootProc` with resolved capabilities.
|
||||
let pat = ResolvePath ?: {0: ?route, 3: ?:ResolvedAccepted}
|
||||
during(turn, ds, ResolvePath ?: {0: ?route, 3: ?:ResolvedAccepted}) do (dst: Cap):
|
||||
bootProc(turn, dst)
|
||||
|
||||
when defined(posix):
|
||||
const defaultRoute* = "<route [<stdio>]>"
|
||||
|
||||
proc envRoute*: Route =
|
||||
## Get an route to a Syndicate capability from the calling environment.
|
||||
## On UNIX this is the SYNDICATE_ROUTE environmental variable with a
|
||||
## fallack to a defaultRoute_.
|
||||
## See https://git.syndicate-lang.org/syndicate-lang/syndicate-protocols/raw/branch/main/schemas/gatekeeper.prs.
|
||||
var text = getEnv("SYNDICATE_ROUTE", defaultRoute)
|
||||
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 resolveEnvironment*(turn: Turn; bootProc: BootProc) =
|
||||
## Resolve a capability from the calling environment
|
||||
## and call `bootProc`. See envRoute_.
|
||||
var resolved = false
|
||||
let
|
||||
ds = newDataspace(turn)
|
||||
pat = ResolvePath ?: {0: ?envRoute(), 3: ?:ResolvedAccepted}
|
||||
during(turn, ds, pat) do (dst: Cap):
|
||||
if not resolved:
|
||||
resolved = true
|
||||
bootProc(turn, dst)
|
||||
do:
|
||||
resolved = false
|
||||
spawnRelays(turn, ds)
|
||||
|
||||
# TODO: define a runActor that comes preloaded with relaying
|
||||
|
|
|
@ -1,178 +1,197 @@
|
|||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# 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/[assertions, hashes, options, sets, tables]
|
||||
import preserves
|
||||
import ./actors, ./bags, ./patterns
|
||||
import ./protocols/dataspacePatterns
|
||||
|
||||
type
|
||||
Value = Preserve[Ref]
|
||||
Pattern = dataspacePatterns.Pattern
|
||||
Path = seq[Value]
|
||||
ClassKind = enum classNone, classRecord, classSequence, classDictionary
|
||||
Class = object
|
||||
kind: ClassKind
|
||||
label: Value
|
||||
|
||||
func projectPath(v: Value; path: Path): Option[Value] =
|
||||
result = some(v)
|
||||
for index in path:
|
||||
result = preserves.step(result.get, index)
|
||||
if result.isNone: break
|
||||
func classOf(v: Value): Class =
|
||||
case v.kind
|
||||
of pkRecord: Class(kind: classRecord, label: v.label)
|
||||
of pkSequence: Class(kind: classSequence)
|
||||
of pkDictionary: Class(kind: classDictionary)
|
||||
else: Class(kind: classNone)
|
||||
|
||||
func projectPaths(v: Value; paths: seq[Path]): seq[Value] =
|
||||
result = newSeq[Value](paths.len)
|
||||
for i, path in paths:
|
||||
var vv = projectPath(v, path)
|
||||
if vv.isSome: result[i] = get(vv)
|
||||
|
||||
type Class = distinct string
|
||||
|
||||
proc `$`(cls: Class): string {.borrow.}
|
||||
proc hash(cls: Class): Hash {.borrow.}
|
||||
proc `==`(x, y: Class): bool {.borrow.}
|
||||
|
||||
proc classOf*(v: Value): Class =
|
||||
if v.isRecord:
|
||||
result = Class $v.label & "/" & $v.arity
|
||||
elif v.isSequence:
|
||||
result = Class $v.len
|
||||
elif v.isDictionary:
|
||||
result = Class "{}"
|
||||
|
||||
proc classOf*(p: Pattern): Class =
|
||||
if p.orKind == PatternKind.DCompound:
|
||||
case p.dcompound.orKind
|
||||
of DCompoundKind.rec:
|
||||
result = Class $p.dcompound.rec.label & "/" & $p.dcompound.rec.fields.len
|
||||
of DCompoundKind.arr:
|
||||
result = Class $p.dcompound.arr.items.len
|
||||
of DCompoundKind.dict:
|
||||
result = Class "{}"
|
||||
|
||||
proc step(value, index: Value): Value =
|
||||
result = value[index]
|
||||
proc classOf(p: Pattern): Class =
|
||||
if p.orKind == PatternKind.group:
|
||||
case p.group.type.orKind
|
||||
of GroupTypeKind.rec:
|
||||
Class(kind: classRecord, label: p.group.`type`.rec.label)
|
||||
of GroupTypeKind.arr:
|
||||
Class(kind: classSequence)
|
||||
of GroupTypeKind.dict:
|
||||
Class(kind: classDictionary)
|
||||
else:
|
||||
Class(kind: classNone)
|
||||
|
||||
type
|
||||
EventKind = enum addedEvent, removedEvent, messageEvent
|
||||
|
||||
AssertionCache = HashSet[Value]
|
||||
|
||||
ObserverGroup = ref object
|
||||
cachedCaptures: Bag[seq[Value]]
|
||||
observers: Table[Ref, TableRef[seq[Value], Handle]]
|
||||
ObserverGroup = ref object # Endpoints
|
||||
cachedCaptures: Bag[Captures]
|
||||
observers: Table[Cap, TableRef[Captures, Handle]]
|
||||
|
||||
Leaf = ref object
|
||||
cachedAssertions: AssertionCache
|
||||
observerGroups: Table[seq[Path], ObserverGroup]
|
||||
cache: AssertionCache
|
||||
observerGroups: Table[Paths, ObserverGroup]
|
||||
|
||||
LeafMap = TableRef[seq[Value], Leaf]
|
||||
|
||||
Continuation = ref object
|
||||
cachedAssertions: AssertionCache
|
||||
leafMap: Table[seq[Path], TableRef[seq[Value], Leaf]]
|
||||
cache: AssertionCache
|
||||
leafMap: Table[Paths, LeafMap]
|
||||
|
||||
func isEmpty(leaf: Leaf): bool =
|
||||
leaf.cache.len == 0 and leaf.observerGroups.len == 0
|
||||
|
||||
func isEmpty(cont: Continuation): bool =
|
||||
cont.cache.len == 0 and cont.leafMap.len == 0
|
||||
|
||||
type
|
||||
ContinuationProc = proc (c: Continuation; v: Value) {.closure.}
|
||||
LeafProc = proc (l: Leaf; v: Value) {.closure.}
|
||||
ObserverProc = proc (turn: Turn; group: ObserverGroup; vs: seq[Value]) {.closure.}
|
||||
|
||||
proc getLeaves(cont: Continuation; presentPaths, 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:
|
||||
# TODO: check presence
|
||||
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
|
||||
edges: Table[Selector, TableRef[Class, Node]]
|
||||
continuation: Continuation
|
||||
edges: Table[Selector, TableRef[Class, Node]]
|
||||
|
||||
from strutils import toHex
|
||||
proc `$`(node: Node): string =
|
||||
toHex(cast[ByteAddress](unsafeAddr node[]), 5)
|
||||
func isEmpty(node: Node): bool =
|
||||
node.continuation.isEmpty and node.edges.len == 0
|
||||
|
||||
func isEmpty(leaf: Leaf): bool =
|
||||
leaf.cachedAssertions.len == 0 and leaf.observerGroups.len == 0
|
||||
|
||||
type
|
||||
ContinuationProc = proc (c: Continuation; v: Value)
|
||||
LeafProc = proc (l: Leaf; v: Value)
|
||||
ObserverProc = proc (turn: var Turn; group: ObserverGroup; vs: seq[Value])
|
||||
|
||||
type TermStack = SinglyLinkedList[Value]
|
||||
type TermStack = seq[Value]
|
||||
|
||||
proc push(stack: TermStack; val: Value): Termstack =
|
||||
result = stack
|
||||
prepend(result, val)
|
||||
add(result, val)
|
||||
|
||||
proc pop(stack: TermStack; n: int): TermStack =
|
||||
result = stack
|
||||
var n = n
|
||||
while n > 0:
|
||||
result.remove(result.head)
|
||||
assert not stack.head.isNil, "popped too far"
|
||||
dec n
|
||||
assert n <= stack.len
|
||||
stack[stack.low..(stack.high-n)]
|
||||
|
||||
proc top(stack: TermStack): Value =
|
||||
assert not stack.head.isNil, "stack is empty"
|
||||
stack.head.value
|
||||
assert stack.len > 0
|
||||
stack[stack.high]
|
||||
|
||||
|
||||
proc modify(node: Node; turn: var Turn; outerValue: Value; event: EventKind;
|
||||
proc modify(node: Node; turn: Turn; outerValue: Value; event: EventKind;
|
||||
modCont: ContinuationProc; modLeaf: LeafProc; modObs: ObserverProc) =
|
||||
|
||||
proc walk(turn: var Turn; cont: Continuation; outerValue: Value; event: EventKind) =
|
||||
proc walk(cont: Continuation; turn: Turn) =
|
||||
modCont(cont, outerValue)
|
||||
for constPaths, constValMap in cont.leafMap.pairs:
|
||||
let constVals = projectPaths(outerValue, constPaths)
|
||||
var leaf = constValMap.getOrDefault(constVals)
|
||||
if leaf.isNil and event == addedEvent:
|
||||
new leaf
|
||||
constValMap[constVals] = leaf
|
||||
if not leaf.isNil:
|
||||
modLeaf(leaf, outerValue)
|
||||
for capturePaths, observerGroup in leaf.observerGroups.pairs:
|
||||
modObs(turn, observerGroup, projectPaths(outerValue, capturePaths))
|
||||
# TODO: cleanup dead leaves
|
||||
if constVals.isSome:
|
||||
case event
|
||||
of addedEvent, messageEvent:
|
||||
let leaf = constValMap.getLeaf(get constVals)
|
||||
modLeaf(leaf, outerValue)
|
||||
for capturePaths, observerGroup in leaf.observerGroups.pairs:
|
||||
let captures = projectPaths(outerValue, capturePaths)
|
||||
if captures.isSome:
|
||||
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: Turn; termStack: TermStack) =
|
||||
walk(node.continuation, turn)
|
||||
for selector, table in node.edges:
|
||||
let
|
||||
nextStack = pop(termStack, selector.popCount)
|
||||
nextValue = step(nextStack.top, selector.index)
|
||||
nextClass = classOf nextValue
|
||||
if nextClass != Class"":
|
||||
let nextNode = table.getOrDefault(nextClass)
|
||||
if not nextNode.isNil:
|
||||
walk(nextNode, turn, outerValue, event, push(nextStack, nextValue))
|
||||
if nextValue.isSome:
|
||||
let nextClass = classOf(get nextValue)
|
||||
if nextClass.kind != classNone:
|
||||
let nextNode = table.getOrDefault(nextClass)
|
||||
if not nextNode.isNil:
|
||||
walk(nextNode, turn, push(nextStack, get nextValue))
|
||||
if event == removedEvent and nextNode.isEmpty:
|
||||
table.del(nextClass)
|
||||
|
||||
var stack: TermStack
|
||||
walk(node, turn, outerValue, event, push(stack, @[outerValue].toPreserve(Ref)))
|
||||
walk(node, turn, @[@[outerValue].toPreserves])
|
||||
|
||||
proc extend(node: Node; popCount: Natural; stepIndex: Value; pat: Pattern; path: var Path): tuple[popCount: Natural, nextNode: Node] =
|
||||
proc getOrNew[A, B, C](t: var Table[A, TableRef[B, C]], k: A): TableRef[B, C] =
|
||||
result = t.getOrDefault(k)
|
||||
if result.isNil:
|
||||
result = newTable[B, C]()
|
||||
t[k] = result
|
||||
|
||||
proc extendWalk(node: Node; popCount: Natural; stepIndex: Value; pat: Pattern; path: var Path): tuple[popCount: Natural, nextNode: Node] =
|
||||
case pat.orKind
|
||||
of PatternKind.DDiscard, PatternKind.DLit: result = (popCount, node)
|
||||
of PatternKind.DBind: result = extend(node, popCount, stepIndex, pat.dbind.pattern, path)
|
||||
of PatternKind.DCompound:
|
||||
let selector: Selector = (popCount, stepIndex,)
|
||||
var table = node.edges.getOrDefault(selector)
|
||||
if table.isNil:
|
||||
table = newTable[Class, Node]()
|
||||
node.edges[selector] = table
|
||||
let class = classOf pat
|
||||
of PatternKind.`discard`, PatternKind.lit:
|
||||
result = (popCount, node)
|
||||
of PatternKind.`bind`:
|
||||
result = extendWalk(node, popCount, stepIndex, pat.`bind`.pattern, path)
|
||||
of PatternKind.`group`:
|
||||
let
|
||||
selector: Selector = (popCount, stepIndex,)
|
||||
table = node.edges.getOrNew(selector)
|
||||
class = classOf pat
|
||||
result.nextNode = table.getOrDefault(class)
|
||||
if result.nextNode.isNil:
|
||||
new result.nextNode
|
||||
new result.nextNode.continuation
|
||||
table[class] = result.nextNode
|
||||
for a in node.continuation.cachedAssertions:
|
||||
var v = projectPath(a, path)
|
||||
if v.isSome and class == classOf(get(v)):
|
||||
result.nextNode.continuation.cachedAssertions.incl a
|
||||
new result.nextNode.continuation
|
||||
for a in node.continuation.cache:
|
||||
var v = step(a, path)
|
||||
if v.isSome and class == classOf(get v):
|
||||
result.nextNode.continuation.cache.incl a
|
||||
result.popCount = 0
|
||||
template walkKey(pat: Pattern; stepIndex: Value) =
|
||||
path.add(stepIndex)
|
||||
result = extend(result.nextNode, popCount, stepIndex, pat, path)
|
||||
discard path.pop()
|
||||
case pat.dcompound.orKind
|
||||
of DCompoundKind.rec:
|
||||
for k, e in pat.dcompound.rec.fields: walkKey(e, k.toPreserve(Ref))
|
||||
of DCompoundKind.arr:
|
||||
for k, e in pat.dcompound.arr.items: walkKey(e, k.toPreserve(Ref))
|
||||
of DCompoundKind.dict:
|
||||
for k, e in pat.dcompound.dict.entries: walkKey(e, k)
|
||||
result.popCount.inc
|
||||
when not defined(release):
|
||||
assert not node.edges[selector][classOf pat].isNil
|
||||
for step, p in pat.group.entries:
|
||||
add(path, step)
|
||||
result = extendWalk(result.nextNode, result.popCount, step, p, path)
|
||||
discard pop(path)
|
||||
inc(result.popCount)
|
||||
|
||||
proc extend(node: var Node; pat: Pattern): Continuation =
|
||||
var path: Path
|
||||
extend(node, 0, toPreserve(0, Ref), pat, path).nextNode.continuation
|
||||
extendWalk(node, 0, 0.toPreserves, pat, path).nextNode.continuation
|
||||
|
||||
type
|
||||
Index* = object
|
||||
|
@ -182,95 +201,89 @@ type
|
|||
proc initIndex*(): Index =
|
||||
Index(root: Node(continuation: Continuation()))
|
||||
|
||||
proc add*(index: var Index; turn: var Turn; pattern: Pattern; observer: Ref) =
|
||||
let
|
||||
analysis = analyse pattern
|
||||
continuation = index.root.extend pattern
|
||||
var constValMap = continuation.leafMap.getOrDefault(analysis.constPaths)
|
||||
if constValMap.isNil:
|
||||
new constValMap
|
||||
for a in continuation.cachedAssertions:
|
||||
let key = projectPaths(a, analysis.constPaths)
|
||||
var leaf = constValMap.getOrDefault(key)
|
||||
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 getEndpoints(leaf: Leaf; capturePaths: Paths): ObserverGroup =
|
||||
result = leaf.observerGroups.getOrDefault(capturePaths)
|
||||
if result.isNil:
|
||||
new result
|
||||
leaf.observerGroups[capturePaths] = result
|
||||
for term in leaf.cache:
|
||||
# leaf.cache would be empty if observers come before assertions
|
||||
let captures = projectPaths(term, capturePaths)
|
||||
if captures.isSome:
|
||||
discard result.cachedCaptures.change(get captures, +1)
|
||||
|
||||
proc remove*(index: var Index; turn: var Turn; pattern: Pattern; observer: Ref) =
|
||||
var
|
||||
proc add*(index: var Index; turn: Turn; pattern: Pattern; observer: Cap) =
|
||||
let
|
||||
cont = index.root.extend(pattern)
|
||||
analysis = analyse pattern
|
||||
continuation = index.root.extend pattern
|
||||
let constValMap = continuation.leafMap.getOrDefault(analysis.constPaths)
|
||||
constValMap = cont.getLeaves(analysis.presentPaths, 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: Turn; pattern: Pattern; observer: Cap) =
|
||||
let
|
||||
cont = index.root.extend(pattern)
|
||||
analysis = analyse pattern
|
||||
constValMap = cont.leafMap.getOrDefault(analysis.constPaths)
|
||||
if not constValMap.isNil:
|
||||
let leaf = constValMap.getOrDefault(analysis.constValues)
|
||||
if not leaf.isNil:
|
||||
let observerGroup = leaf.observerGroups.getOrDefault(analysis.capturePaths)
|
||||
if not observerGroup.isNil:
|
||||
let captureMap = observerGroup.observers.getOrDefault(observer)
|
||||
if not captureMap.isNil:
|
||||
for handle in captureMap.values: retract(observer.target, turn, handle)
|
||||
observerGroup.observers.del(observer)
|
||||
if observerGroup.observers.len == 0:
|
||||
let endpoints = leaf.observerGroups.getOrDefault(analysis.capturePaths)
|
||||
if not endpoints.isNil:
|
||||
var captureMap: TableRef[seq[Value], Handle]
|
||||
if endpoints.observers.pop(observer, captureMap):
|
||||
for handle in captureMap.values: retract(turn, handle)
|
||||
if endpoints.observers.len == 0:
|
||||
leaf.observerGroups.del(analysis.capturePaths)
|
||||
if leaf.isEmpty:
|
||||
constValMap.del(analysis.constValues)
|
||||
if constValMap.len == 0:
|
||||
continuation.leafMap.del(analysis.constPaths)
|
||||
if leaf.observerGroups.len == 0:
|
||||
constValMap.del(analysis.constValues)
|
||||
if constValMap.len == 0:
|
||||
cont.leafMap.del(analysis.constPaths)
|
||||
|
||||
proc adjustAssertion*(index: var Index; turn: var Turn; outerValue: Value; delta: int): bool =
|
||||
proc adjustAssertion(index: var Index; turn: Turn; outerValue: Value; delta: int): bool =
|
||||
case index.allAssertions.change(outerValue, delta)
|
||||
of cdAbsentToPresent:
|
||||
result = true
|
||||
proc modContinuation(c: Continuation; v: Value) =
|
||||
c.cachedAssertions.incl(v)
|
||||
c.cache.incl(v)
|
||||
proc modLeaf(l: Leaf; v: Value) =
|
||||
l.cachedAssertions.incl(v)
|
||||
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
|
||||
if group.cachedCaptures.change(vs, +1) == cdAbsentToPresent:
|
||||
l.cache.incl(v)
|
||||
proc modObserver(turn: Turn; group: ObserverGroup; vs: seq[Value]) =
|
||||
let change = group.cachedCaptures.change(vs, +1)
|
||||
if change == cdAbsentToPresent:
|
||||
for (observer, captureMap) in group.observers.pairs:
|
||||
let a = vs.toPreserve(Ref)
|
||||
captureMap[vs] = publish(turn, observer, a)
|
||||
captureMap[vs] = publish(turn, observer, vs.toPreserves)
|
||||
# TODO: this handle is coming from the facet?
|
||||
modify(index.root, turn, outerValue, addedEvent, modContinuation, modLeaf, modObserver)
|
||||
of cdPresentToAbsent:
|
||||
result = true
|
||||
proc modContinuation(c: Continuation; v: Value) =
|
||||
c.cachedAssertions.excl(v)
|
||||
c.cache.excl(v)
|
||||
proc modLeaf(l: Leaf; v: Value) =
|
||||
l.cachedAssertions.excl(v)
|
||||
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
|
||||
l.cache.excl(v)
|
||||
proc modObserver(turn: Turn; group: ObserverGroup; vs: seq[Value]) =
|
||||
if group.cachedCaptures.change(vs, -1) == cdPresentToAbsent:
|
||||
for (observer, captureMap) in group.observers.pairs:
|
||||
retract(observer.target, turn, captureMap[vs])
|
||||
captureMap.del(vs)
|
||||
var h: Handle
|
||||
if captureMap.take(vs, h):
|
||||
retract(observer.target, turn, h)
|
||||
modify(index.root, turn, outerValue, removedEvent, modContinuation, modLeaf, modObserver)
|
||||
else: discard
|
||||
|
||||
proc continuationNoop(c: Continuation; 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: Turn; v: Value): bool =
|
||||
adjustAssertion(index, turn, v, +1)
|
||||
proc remove*(index: var Index; turn: var Turn; v: Assertion): bool =
|
||||
proc remove*(index: var Index; turn: Turn; v: Value): bool =
|
||||
adjustAssertion(index, turn, v, -1)
|
||||
|
||||
proc deliverMessage*(index: var Index; turn: var Turn; v: Value) =
|
||||
proc observersCb(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
|
||||
proc deliverMessage*(index: var Index; turn: Turn; v: Value) =
|
||||
proc observersCb(turn: Turn; group: ObserverGroup; vs: seq[Value]) =
|
||||
for observer in group.observers.keys: message(turn, observer, vs)
|
||||
index.root.modify(turn, v, messageEvent, continuationNoop, leafNoop, observersCb)
|
||||
|
|
|
@ -1,12 +1,61 @@
|
|||
# Package
|
||||
# Emulate Nimble from CycloneDX data at sbom.json.
|
||||
|
||||
version = "1.3.2"
|
||||
author = "Emery Hemingway"
|
||||
description = "Syndicated actors for conversational concurrency"
|
||||
license = "Unlicense"
|
||||
srcDir = "src"
|
||||
import std/json
|
||||
|
||||
proc lookupComponent(sbom: JsonNode; bomRef: string): JsonNode =
|
||||
for c in sbom{"components"}.getElems.items:
|
||||
if c{"bom-ref"}.getStr == bomRef:
|
||||
return c
|
||||
result = newJNull()
|
||||
|
||||
# Dependencies
|
||||
let
|
||||
sbom = (getPkgDir() & "/sbom.json").readFile.parseJson
|
||||
comp = sbom{"metadata", "component"}
|
||||
bomRef = comp{"bom-ref"}.getStr
|
||||
|
||||
requires "nim >= 1.4.8", "nimSHA2 >= 0.1.1", "preserves > 3.2.0"
|
||||
version = comp{"version"}.getStr
|
||||
author = comp{"authors"}[0]{"name"}.getStr
|
||||
description = comp{"description"}.getStr
|
||||
license = comp{"licenses"}[0]{"license", "id"}.getStr
|
||||
|
||||
for prop in comp{"properties"}.getElems.items:
|
||||
let (key, val) = (prop{"name"}.getStr, prop{"value"}.getStr)
|
||||
case key
|
||||
of "nim:skipDirs:":
|
||||
add(skipDirs, val)
|
||||
of "nim:skipFiles:":
|
||||
add(skipFiles, val)
|
||||
of "nim:skipExt":
|
||||
add(skipExt, val)
|
||||
of "nim:installDirs":
|
||||
add(installDirs, val)
|
||||
of "nim:installFiles":
|
||||
add(installFiles, val)
|
||||
of "nim:installExt":
|
||||
add(installExt, val)
|
||||
of "nim:binDir":
|
||||
add(binDir, val)
|
||||
of "nim:srcDir":
|
||||
add(srcDir, val)
|
||||
of "nim:backend":
|
||||
add(backend, val)
|
||||
else:
|
||||
if key.startsWith "nim:bin:":
|
||||
namedBin[key[8..key.high]] = val
|
||||
|
||||
for depend in sbom{"dependencies"}.items:
|
||||
if depend{"ref"}.getStr == bomRef:
|
||||
for depRef in depend{"dependsOn"}.items:
|
||||
let dep = sbom.lookupComponent(depRef.getStr)
|
||||
var spec = dep{"name"}.getStr
|
||||
for extRef in dep{"externalReferences"}.elems:
|
||||
if extRef{"type"}.getStr == "vcs":
|
||||
spec = extRef{"url"}.getStr
|
||||
break
|
||||
let ver = dep{"version"}.getStr
|
||||
if ver != "":
|
||||
if ver.allCharsInSet {'0'..'9', '.'}: spec.add " == "
|
||||
else: spec.add '#'
|
||||
spec.add ver
|
||||
requires spec
|
||||
break
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
include_rules
|
||||
: foreach *.prs |> !preserves_schema_nim |> | {schema}
|
||||
: foreach t*.nim | ../../preserves-nim/<tests> {schema} $(SYNDICATE_PROTOCOL) |> !nim_run |> | ../<test>
|
||||
: foreach solo5*.nim | ../../taps/<sources> ../../preserves-nim/<tests> {schema} $(SYNDICATE_PROTOCOL) |> !nim_solo5_spt |> | ../<test>
|
|
@ -1,20 +0,0 @@
|
|||
|
||||
## Date of generation: 2021-09-01 13:32
|
||||
import
|
||||
std/typetraits, preserves
|
||||
|
||||
type
|
||||
EmbeddedType = void
|
||||
BoxState* {.record: "box-state".} = object ## ``<box-state @value int>``
|
||||
`value`*: BiggestInt
|
||||
|
||||
SetBox* {.record: "set-box".} = object ## ``<set-box @value int>``
|
||||
`value`*: BiggestInt
|
||||
|
||||
proc prsBoxState*(value: Preserve | BiggestInt): Preserve =
|
||||
initRecord[EmbeddedType](symbol("box-state", EmbeddedType),
|
||||
toPreserve(value, EmbeddedType))
|
||||
|
||||
proc prsSetBox*(value: Preserve | BiggestInt): Preserve =
|
||||
initRecord[EmbeddedType](symbol("set-box", EmbeddedType),
|
||||
toPreserve(value, EmbeddedType))
|
|
@ -1,40 +0,0 @@
|
|||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[asyncdispatch, os]
|
||||
import preserves, syndicate, syndicate/capabilities
|
||||
import syndicate/protocols/simpleChatProtocol
|
||||
|
||||
proc mintCap: SturdyRef =
|
||||
var key: array[16, byte]
|
||||
mint(key, "syndicate")
|
||||
|
||||
proc unixSocketPath: string =
|
||||
result = getEnv("SYNDICATE_SOCK")
|
||||
if result == "":
|
||||
result = getEnv("XDG_RUNTIME_DIR", "/run/user/1000") / "dataspace"
|
||||
|
||||
bootDataspace("main") do (root: Ref; turn: var Turn):
|
||||
connectUnix(turn, unixSocketPath(), mintCap()) do (turn: var Turn; ds: Ref):
|
||||
var
|
||||
username: string
|
||||
usernameHandle: Handle
|
||||
|
||||
proc updateUsername(turn: var Turn; u: string) =
|
||||
username = u
|
||||
var p = Present(username: username)
|
||||
replace(turn, ds, usernameHandle, p)
|
||||
|
||||
updateUsername(turn, "user" & $getCurrentProcessId())
|
||||
|
||||
during(turn, ds, ?Present) do (username: string):
|
||||
echo username, " arrived"
|
||||
do:
|
||||
echo username, " left"
|
||||
|
||||
onMessage(turn, ds, ?Says) do (who: string; what: string):
|
||||
echo who, ": ", what
|
||||
|
||||
message(turn, ds, Says(who: username, what: "hello"))
|
||||
|
||||
runForever()
|
|
@ -1,92 +0,0 @@
|
|||
#!/usr/bin/env spry
|
||||
|
||||
title = "Simple Chat Demo"
|
||||
|
||||
# Initialize libui
|
||||
uiInit
|
||||
|
||||
username = "user"
|
||||
|
||||
menu = newMenu "Menu"
|
||||
|
||||
# menu addItem: "Username" onClicked: [
|
||||
# dialog = newWindow: "Username" width: 200 height: 40 hasBar: false
|
||||
# entry = newEntryText: username onChanged: []
|
||||
# quitit = newButton: "Quit" onClicked: [
|
||||
# destroy dialog
|
||||
# true
|
||||
# ]
|
||||
# layout = newHorizontalBox
|
||||
# #layout add: entry stretch: true
|
||||
# layout.add: quitit stretch: false
|
||||
# group = newGroup "Username"
|
||||
# group setChild: layout
|
||||
# dialog setChild: quitit
|
||||
# dialog show
|
||||
# ]
|
||||
|
||||
menu menuAppendAboutItem
|
||||
|
||||
menu addQuitItemShouldClose: [
|
||||
win destroy
|
||||
uiQuit
|
||||
true
|
||||
]
|
||||
|
||||
# Create a new Window
|
||||
win = newWindow: title width: 640 height: 400 hasBar: true
|
||||
win margined: true
|
||||
|
||||
# create text boxes
|
||||
scrollback = newMultilineEntryText
|
||||
scrollback readonly: true
|
||||
|
||||
sendEntry = newMultilineEntryText
|
||||
|
||||
# create layouts
|
||||
layout = newVerticalBox
|
||||
sendBox = newHorizontalBox
|
||||
|
||||
# Some buttons and their handlers
|
||||
sendButton = newButton: "Send" onClicked: [
|
||||
msg = (sendEntry text)
|
||||
msg != "" then: [
|
||||
scrollback addText: (msg, "\x0a")
|
||||
sendEntry text: ""
|
||||
]
|
||||
]
|
||||
|
||||
# Group
|
||||
group = newGroup "Workspace"
|
||||
group margined: false
|
||||
group setChild: layout
|
||||
|
||||
sendBox add: sendEntry stretch: true
|
||||
sendBox add: sendButton stretch: false
|
||||
|
||||
# Put things in the boxes
|
||||
layout padded: true
|
||||
layout add: scrollback stretch: true
|
||||
layout add: sendBox stretch: false
|
||||
|
||||
# Add box to window
|
||||
win setChild: group
|
||||
|
||||
# Set initial text
|
||||
sendEntry text: "compose a message here"
|
||||
|
||||
# Close handler
|
||||
closeHandler = [
|
||||
win destroy
|
||||
uiQuit
|
||||
true
|
||||
]
|
||||
|
||||
# Set a handler on closing window
|
||||
win onClosingShouldClose: [ true ]
|
||||
|
||||
# Show the window
|
||||
win show
|
||||
|
||||
# Enter libui's event loop
|
||||
uiMain
|
|
@ -0,0 +1,39 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import taps
|
||||
import solo5
|
||||
import syndicate, syndicate/relays
|
||||
import preserves
|
||||
|
||||
acquireDevices([("relay", netBasic)], netAcquireHook)
|
||||
|
||||
type Netif {.preservesRecord: "netif"} = object
|
||||
device, ipAddr: string
|
||||
|
||||
proc spawnNetifActor(turn: Turn; ds: Cap) =
|
||||
spawnActor(turn, "netif") do (turn: Turn):
|
||||
let facet = turn.facet
|
||||
onInterfaceUp do (device: string; ip: IpAddress):
|
||||
run(facet) do (turn: Turn):
|
||||
if not ip.isLinkLocal:
|
||||
discard publish(turn, ds, Netif(device: device, ipAddr: $ip))
|
||||
|
||||
runActor("relay-test") do (turn: Turn):
|
||||
let root = turn.facet
|
||||
onStop(turn) do (turn: Turn):
|
||||
quit()
|
||||
let ds = newDataspace(turn)
|
||||
spawnNetifActor(turn, ds)
|
||||
spawnRelays(turn, ds)
|
||||
var
|
||||
route: Route
|
||||
pr = parsePreserves $solo5_start_info.cmdline
|
||||
if route.fromPreserves pr:
|
||||
echo "parsed route ", route.toPreserves
|
||||
during(turn, ds, Netif?:{1: grab()}) do (ip: string):
|
||||
echo "Acquired address ", ip
|
||||
resolve(turn, ds, route) do (turn: Turn; ds: Cap):
|
||||
echo "route resolved!"
|
||||
echo "stopping root facet"
|
||||
stop(turn, root)
|
|
@ -0,0 +1,3 @@
|
|||
define:ipv6Enabled
|
||||
define:traceSyndicate
|
||||
import:"std/assertions"
|
|
@ -0,0 +1,25 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/times
|
||||
import solo5
|
||||
import syndicate, syndicate/drivers/timers
|
||||
|
||||
acquireDevices()
|
||||
|
||||
runActor("timer-test") do (turn: Turn):
|
||||
let timers = newDataspace(turn)
|
||||
spawnTimerDriver(turn, timers)
|
||||
|
||||
onPublish(turn, timers, ?LaterThan(seconds: 1356100000)):
|
||||
echo "now in 13th bʼakʼtun"
|
||||
|
||||
after(turn, timers, initDuration(seconds = 3)) do (turn: Turn):
|
||||
echo "third timer expired"
|
||||
stopActor(turn)
|
||||
|
||||
after(turn, timers, initDuration(seconds = 1)) do (turn: Turn):
|
||||
echo "first timer expired"
|
||||
|
||||
after(turn, timers, initDuration(seconds = 2)) do (turn: Turn):
|
||||
echo "second timer expired"
|
|
@ -0,0 +1,2 @@
|
|||
define:ipv6Enabled
|
||||
import:"std/assertions"
|
|
@ -1,42 +0,0 @@
|
|||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[asyncdispatch, random]
|
||||
import preserves
|
||||
import syndicate
|
||||
|
||||
import syndicate/[actors, capabilities]
|
||||
|
||||
randomize()
|
||||
|
||||
proc mint(): SturdyRef =
|
||||
var key: array[16, byte]
|
||||
mint(key, "syndicate")
|
||||
|
||||
type
|
||||
A* {.preservesRecord: "A".} = object
|
||||
str*: string
|
||||
B* {.preservesRecord: "B".} = object
|
||||
str*: string
|
||||
|
||||
bootDataspace("x") do (ds: Ref; turn: var Turn):
|
||||
connectStdio(ds, turn)
|
||||
|
||||
discard publish(turn, ds, A(str: "A stdio"))
|
||||
discard publish(turn, ds, B(str: "B stdio"))
|
||||
|
||||
onPublish(turn, ds, ?A) do (v: Assertion):
|
||||
stderr.writeLine "received over stdio ", v
|
||||
|
||||
bootDataspace("y") do (ds: Ref; turn: var Turn):
|
||||
|
||||
connectUnix(turn, "/run/user/1000/dataspace", mint()) do (turn: var Turn; a: Assertion) -> TurnAction:
|
||||
let ds = unembed a
|
||||
|
||||
discard publish(turn, ds, A(str: "A unix"))
|
||||
discard publish(turn, ds, B(str: "B unix"))
|
||||
|
||||
onPublish(turn, ds, ?B) do (v: Assertion):
|
||||
stderr.writeLine "received over unix ", v
|
||||
|
||||
runForever()
|
|
@ -1,66 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2021 ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import syndicate/assertions, syndicate/dataspaces, syndicate/events, syndicate/skeletons
|
||||
import preserves, preserves/records
|
||||
import asyncdispatch, tables, options
|
||||
|
||||
import ./box_and_client
|
||||
|
||||
const N = 100000
|
||||
|
||||
let
|
||||
`?_` = Discard().toPreserve
|
||||
`?$` = Capture().toPreserve
|
||||
|
||||
proc boot(facet: Facet) =
|
||||
|
||||
facet.spawn("box") do (facet: Facet):
|
||||
facet.declareField(value, int, 0)
|
||||
|
||||
facet.addEndpoint do (facet: Facet) -> EndpointSpec:
|
||||
# echo "recomputing published BoxState ", facet.fields.value
|
||||
result.assertion = prsBoxState(value.getPreserve)
|
||||
|
||||
facet.addDataflow do (facet: Facet):
|
||||
# echo "box dataflow saw new value ", facet.fields.value
|
||||
if value.get == N:
|
||||
facet.stop do (facet: Facet):
|
||||
echo "terminated box root facet"
|
||||
|
||||
facet.addEndpoint do (facet: Facet) -> EndpointSpec:
|
||||
let a = prsSetBox(`?$`)
|
||||
result.analysis = some analyzeAssertion(a)
|
||||
proc cb(facet: Facet; vs: seq[Value]) =
|
||||
facet.scheduleScript do (facet: Facet):
|
||||
value.set(vs[0])
|
||||
# echo "box updated value ", vs[0]
|
||||
result.callback = facet.wrap(messageEvent, cb)
|
||||
result.assertion = observe(prsSetBox(`?$`))
|
||||
|
||||
facet.spawn("client") do (facet: Facet):
|
||||
|
||||
facet.addEndpoint do (facet: Facet) -> EndpointSpec:
|
||||
let a = prsBoxState(`?$`)
|
||||
result.analysis = some analyzeAssertion(a)
|
||||
proc cb(facet: Facet; vs: seq[Value]) =
|
||||
facet.scheduleScript do (facet: Facet):
|
||||
let v = prsSetBox(vs[0].int.succ.toPreserve)
|
||||
# echo "client sending ", v
|
||||
facet.send(v)
|
||||
result.callback = facet.wrap(addedEvent, cb)
|
||||
result.assertion = observe(prsBoxState(`?$`))
|
||||
|
||||
facet.addEndpoint do (facet: Facet) -> EndpointSpec:
|
||||
let a = prsBoxState(`?_`)
|
||||
result.analysis = some analyzeAssertion(a)
|
||||
proc cb(facet: Facet; vs: seq[Value]) =
|
||||
facet.scheduleScript do (facet: Facet):
|
||||
echo "box gone"
|
||||
result.callback = facet.wrap(removedEvent, cb)
|
||||
result.assertion = observe(prsBoxState(`?_`))
|
||||
|
||||
facet.actor.dataspace.ground.addStopHandler do (_: Dataspace):
|
||||
echo "stopping box-and-client"
|
||||
|
||||
waitFor bootModule("box-and-client", boot)
|
|
@ -0,0 +1,74 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[oserrors, parseopt, posix, strutils]
|
||||
import pkg/sys/[files, handles, ioqueue]
|
||||
import preserves, syndicate, syndicate/relays
|
||||
|
||||
type
|
||||
Present {.preservesRecord: "Present".} = object
|
||||
username: string
|
||||
Says {.preservesRecord: "Says".} = object
|
||||
who, what: string
|
||||
|
||||
proc syncAndStop(facet: Facet; cap: Cap) =
|
||||
## Stop the actor responsible for `facet` after
|
||||
## synchronizing with `cap`.
|
||||
run(facet) do (turn: Turn):
|
||||
sync(turn, cap, stopActor)
|
||||
|
||||
proc readStdin(facet: Facet; ds: Cap; username: string) {.asyncio.} =
|
||||
let
|
||||
fd = stdin.getOsFileHandle()
|
||||
flags = fcntl(fd.cint, F_GETFL, 0)
|
||||
if flags < 0:
|
||||
raiseOSError(osLastError())
|
||||
if fcntl(fd.cint, F_SETFL, flags or O_NONBLOCK) < 0:
|
||||
raiseOSError(osLastError())
|
||||
let
|
||||
file = newAsyncFile(FD fd)
|
||||
buf = new string
|
||||
buf[].setLen(0x1000)
|
||||
while true:
|
||||
let n = read(file, buf)
|
||||
if n < 1:
|
||||
stderr.writeLine "test_chat calls stopsActor ", facet.actor
|
||||
syncAndStop(facet, ds)
|
||||
return
|
||||
else:
|
||||
var msg = buf[][0..<n].strip
|
||||
proc send(turn: Turn) =
|
||||
message(turn, ds, Says(who: username, what: msg))
|
||||
run(facet, send)
|
||||
|
||||
proc chat(turn: Turn; ds: Cap; username: string) =
|
||||
during(turn, ds, ?:Present) do (who: string):
|
||||
echo who, " joined"
|
||||
do:
|
||||
echo who, " left"
|
||||
|
||||
onMessage(turn, ds, ?:Says) do (who: string, what: string):
|
||||
echo who, ": ", what
|
||||
|
||||
discard publish(turn, ds, Present(username: username))
|
||||
|
||||
discard trampoline:
|
||||
whelp readStdin(turn.facet, ds, username)
|
||||
|
||||
proc main =
|
||||
var username = ""
|
||||
|
||||
for kind, key, val in getopt():
|
||||
if kind == cmdLongOption:
|
||||
case key
|
||||
of "user", "username":
|
||||
username = val
|
||||
|
||||
if username == "":
|
||||
stderr.writeLine "--user: unspecified"
|
||||
else:
|
||||
runActor("chat") do (turn: Turn):
|
||||
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||
chat(turn, ds, username)
|
||||
|
||||
main()
|
|
@ -1,35 +0,0 @@
|
|||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/asyncdispatch
|
||||
import preserves
|
||||
import syndicate
|
||||
|
||||
import ./box_and_client
|
||||
|
||||
syndicate testDsl:
|
||||
|
||||
spawn "box":
|
||||
field(currentValue, BiggestInt, 0)
|
||||
publish prsBoxState(currentValue.get)
|
||||
stopIf currentValue.get == 10:
|
||||
echo "box: terminating"
|
||||
onMessage(prsSetBox(?newValue)) do (newValue: int):
|
||||
# The SetBox message is unpacked to `newValue: int`
|
||||
echo "box: taking on new value ", newValue
|
||||
currentValue.set(newValue)
|
||||
|
||||
spawn "client":
|
||||
#stopIf retracted(observe(SetBox, _)):
|
||||
# echo "client: box has gone"
|
||||
onAsserted(prsBoxState(?v)) do (v: BiggestInt):
|
||||
echo "client: learned that box's value is now ", v
|
||||
send(prsSetBox(v.succ))
|
||||
onRetracted(prsBoxState(?_)) do (_):
|
||||
echo "client: box state disappeared"
|
||||
onStop:
|
||||
quit(0) # Quit explicitly rather than let the dispatcher run empty.
|
||||
|
||||
runForever()
|
||||
# The dataspace is driven by the async dispatcher.
|
||||
# Without `runForever` this module would exit immediately.
|
|
@ -0,0 +1,87 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[options, sequtils, tables, unittest]
|
||||
|
||||
import preserves, syndicate, syndicate/protocols/[gatekeeper, timer]
|
||||
|
||||
import ./test_schema
|
||||
|
||||
suite "example":
|
||||
var pat: Pattern
|
||||
check pat.fromPreserves parsePreserves"""
|
||||
<group <arr> {
|
||||
0: <lit 1>
|
||||
1: <bind <group <arr> {
|
||||
0: <bind <_>>
|
||||
1: <_>
|
||||
}>>
|
||||
2: <_>
|
||||
}>
|
||||
"""
|
||||
|
||||
const A = "[1 2 3]"
|
||||
test A:
|
||||
let v = parsePreserves A
|
||||
check:
|
||||
not pat.matches(v)
|
||||
|
||||
const B = "[1 [2 3] 4]"
|
||||
test B:
|
||||
let
|
||||
v = parsePreserves B
|
||||
c = parsePreserves "[[2 3] 2]"
|
||||
check pat.matches(v)
|
||||
check pat.capture(v).toPreserves == c
|
||||
|
||||
const C = "[1 [2] 5]"
|
||||
test C:
|
||||
let v = parsePreserves C
|
||||
check:
|
||||
not pat.matches(v)
|
||||
|
||||
const D = "[1 [2 3 4] 5]"
|
||||
test D:
|
||||
let
|
||||
v = parsePreserves D
|
||||
c = parsePreserves "[[2 3 4] 2]"
|
||||
check pat.matches(v)
|
||||
check pat.capture(v).toPreserves == c
|
||||
|
||||
const E = "[1 [<x> <y>] []]"
|
||||
test E:
|
||||
let
|
||||
v = parsePreserves E
|
||||
c = parsePreserves "[[<x> <y>] <x>]"
|
||||
check pat.matches(v)
|
||||
check pat.capture(v).toPreserves == c
|
||||
|
||||
suite "meta":
|
||||
|
||||
test "pattern-of-pattern":
|
||||
let
|
||||
pat = grabRecord("foo".toSymbol, {666: drop()})
|
||||
meta = pat.toPreserves.drop()
|
||||
check $meta == "<group <rec group> {0: <group <rec rec> {0: <lit foo>}> 1: <group <dict> {666: <_>}>}>"
|
||||
|
||||
test "observe":
|
||||
let
|
||||
val = Observe(pattern: LaterThan ?: {0: drop 12.24}).toPreserves
|
||||
pat = grab(val)
|
||||
check pat.matches(val)
|
||||
check pat.capture(val) == @[val]
|
||||
let
|
||||
meta = observePattern(!LaterThan, {@[0.toPreserves]: grabLit()})
|
||||
res = parsePreserves "[12.24]"
|
||||
check meta.matches(val)
|
||||
check meta.capture(val).toPreserves == res
|
||||
|
||||
test "connect-transport":
|
||||
let pat = parsePreserves"""
|
||||
<group <rec connect-transport> {0: <group <rec unix> {0: <lit "/run/user/1000/dataspace">}> 2: <group <rec accepted> {0: <bind <_>>}>}>
|
||||
""".preservesTo(Pattern).get
|
||||
let val = parsePreserves"""
|
||||
<connect-transport <unix "/run/user/1000/dataspace"> #:#f <accepted #:#f>>
|
||||
"""
|
||||
check pat.matches(val)
|
||||
check pat.capture(val).toPreserves == parsePreserves "[#:#f]"
|
|
@ -0,0 +1,29 @@
|
|||
import std/[streams, strutils, unittest]
|
||||
|
||||
import preserves
|
||||
import syndicate/relays
|
||||
import syndicate/protocols/sturdy
|
||||
|
||||
type WireRef = sturdy.WireRef
|
||||
|
||||
suite "protocols":
|
||||
test "PDiscard":
|
||||
var pd: PDiscard
|
||||
check $pd == "<_>"
|
||||
|
||||
test "stuff":
|
||||
const
|
||||
data = parseHexStr "b5b590b4b306617373657274b4b3074f627365727665b4b303726563b3067274742d6d73b5b4b3036c697483406805604189374c84b4b3036c6974b3062e3134342e3184b4b30462696e64b4b3015f8484b4b30462696e64b4b3015f8484b4b30462696e64b4b3015f8484b4b30462696e64b4b3015f8484848486b590908484a2132403848484b5b590b4b30772657472616374a2132403848484"
|
||||
var
|
||||
str = newStringStream(data)
|
||||
while not str.atEnd:
|
||||
var pos = str.getPosition
|
||||
echo "decode position: ", pos
|
||||
try:
|
||||
var a = decodePreserves(str)
|
||||
echo a
|
||||
except CatchableError:
|
||||
str.setPosition pos
|
||||
echo str.readAll.toHex
|
||||
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> .
|
|
@ -1,27 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2021 ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[asyncdispatch, monotimes, times]
|
||||
import preserves, preserves/records
|
||||
import syndicate
|
||||
|
||||
import syndicate/drivers/timers
|
||||
|
||||
syndicate plainTimerDemo:
|
||||
boot timerDriver
|
||||
|
||||
spawn "laterThanDemo":
|
||||
field(deadline, MonoTime, getMonoTime())
|
||||
field(count, int, 0)
|
||||
|
||||
onAsserted(prsTimeLaterThan(deadline.get)) do ():
|
||||
echo "TimeLaterThan ticked for deadline ", deadline.get
|
||||
count.set(count.get.succ)
|
||||
if count.get < 5:
|
||||
deadline.set(getMonoTime() + initDuration(milliseconds = 500))
|
||||
|
||||
onStop:
|
||||
echo "dataspace stopped"
|
||||
quit(0)
|
||||
|
||||
runForever()
|
|
@ -0,0 +1,32 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/times
|
||||
import syndicate, syndicate/drivers/timers, preserves
|
||||
|
||||
var passCount = 0
|
||||
|
||||
runActor("timer-test") do (turn: Turn):
|
||||
let timers = newDataspace(turn)
|
||||
spawnTimerDriver(turn, timers)
|
||||
|
||||
onPublish(turn, timers, ?LaterThan(seconds: 1356100000)):
|
||||
echo "now in 13th bʼakʼtun"
|
||||
inc passCount
|
||||
|
||||
after(turn, timers, initDuration(seconds = 3)) do (turn: Turn):
|
||||
echo "third timer expired"
|
||||
assert passCount == 3
|
||||
inc passCount
|
||||
|
||||
after(turn, timers, initDuration(seconds = 1)) do (turn: Turn):
|
||||
echo "first timer expired"
|
||||
assert passCount == 1
|
||||
inc passCount
|
||||
|
||||
after(turn, timers, initDuration(seconds = 2)) do (turn: Turn):
|
||||
echo "second timer expired"
|
||||
assert passCount == 2
|
||||
inc passCount
|
||||
|
||||
doAssert passCount == 4, $passCount
|
Loading…
Reference in New Issue