Compare commits
51 Commits
Author | SHA1 | Date |
---|---|---|
Emery Hemingway | a2c723914b | |
Emery Hemingway | 99e4d760cf | |
Emery Hemingway | c21fdb5003 | |
Emery Hemingway | cdc59b7641 | |
Emery Hemingway | 85522a4c3c | |
Emery Hemingway | b6f7e90b95 | |
Emery Hemingway | 8dca5d4663 | |
Emery Hemingway | 42b7c9544a | |
Emery Hemingway | a4c816ef66 | |
Emery Hemingway | 7bbcdb7e77 | |
Emery Hemingway | 18dd022830 | |
Emery Hemingway | 730ce695de | |
Emery Hemingway | 48410aa94a | |
Emery Hemingway | 1bc4f872a9 | |
Emery Hemingway | f4e1e053c5 | |
Emery Hemingway | 9b70fb9b36 | |
Emery Hemingway | 347e1b5766 | |
Emery Hemingway | 24364187f2 | |
Emery Hemingway | 2901d3dbd8 | |
Emery Hemingway | 39dbe6fb63 | |
Emery Hemingway | cef56c78d6 | |
Emery Hemingway | 86be600ccf | |
Emery Hemingway | 3a8e729e89 | |
Emery Hemingway | aa7e224298 | |
Emery Hemingway | 49741ade4f | |
Emery Hemingway | 5646c10efa | |
Emery Hemingway | e08c7f3d37 | |
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 |
14
README.md
14
README.md
|
@ -54,7 +54,8 @@ The Syndicate DSL can be entered using `runActor` which calls a Nim body with a
|
||||||
### Publish
|
### Publish
|
||||||
|
|
||||||
``` nim
|
``` nim
|
||||||
runActor("main") do (dataspace: Ref; turn: Turn):
|
runActor("main") do (turn: Turn):
|
||||||
|
let dataspace = newDataspace()
|
||||||
let presenceHandle = publish(turn, dataspace, Present(username: "Judy"))
|
let presenceHandle = publish(turn, dataspace, Present(username: "Judy"))
|
||||||
# publish <Present "Judy"> to the dataspace
|
# publish <Present "Judy"> to the dataspace
|
||||||
# the assertion can be later retracted by handle
|
# the assertion can be later retracted by handle
|
||||||
|
@ -67,7 +68,8 @@ runActor("main") do (dataspace: Ref; turn: Turn):
|
||||||
We can react to assertions and messages within dataspaces using [patterns](https://synit.org/book/glossary.html#dataspace-pattern). Patterns are constructed using a Nim type and the `?` operator. Again a Nim type is used rather than a raw Preserves for schema consistency.
|
We can react to assertions and messages within dataspaces using [patterns](https://synit.org/book/glossary.html#dataspace-pattern). Patterns are constructed using a Nim type and the `?` operator. Again a Nim type is used rather than a raw Preserves for schema consistency.
|
||||||
|
|
||||||
``` nim
|
``` nim
|
||||||
runActor("main") do (dataspace: Ref; turn: Turn):
|
runActor("main") do (turn: Turn):
|
||||||
|
let dataspace = newDataspace()
|
||||||
during(turn, dataspace, ?Present) do (who: string):
|
during(turn, dataspace, ?Present) do (who: string):
|
||||||
# This body is active when the ?Present pattern is matched.
|
# This body is active when the ?Present pattern is matched.
|
||||||
# The Present type contains two atomic values that can be matched
|
# The Present type contains two atomic values that can be matched
|
||||||
|
@ -89,17 +91,15 @@ runActor("main") do (dataspace: Ref; turn: Turn):
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### [test_chat](./tests/test_chat.nim)
|
### [test_chat](./tests/test_chat.nim)
|
||||||
Simple chat demo that is compatible with [chat.py](https://git.syndicate-lang.org/syndicate-lang/syndicate-py/src/branch/main/chat.py).
|
Simple chat demo that is compatible with [chat.py](https://git.syndicate-lang.org/syndicate-lang/syndicate-py/src/branch/main/chat.py).
|
||||||
```sh
|
```sh
|
||||||
SYNDICATE_ROUTE='<route [<unix "/run/user/1000/dataspace">] [<ref {oid: "syndicate" sig: #x"69ca300c1dbfa08fba692102dd82311a"}>]>' nim c -r tests/test_chat.nim --user:fnord
|
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)
|
||||||
### [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.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
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/).
|
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](https://nlnet.nl/logo/banner.svg)](https://nlnet.nl/)
|
[![NLnet](./nlnet.svg)](https://nlnet.nl/)
|
||||||
|
|
3
Tupfile
3
Tupfile
|
@ -1,3 +1,2 @@
|
||||||
include_rules
|
include_rules
|
||||||
: |> !nim_lk |> {lockfile}
|
: sbom.json |> !sbom-to-nix |> | ./<lock>
|
||||||
: {lockfile} |> !nim_cfg |> | ./<lock>
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
include depends.tup
|
PROJECT_DIR = $(TUP_CWD)
|
||||||
NIM_GROUPS += $(TUP_CWD)/<lock>
|
NIM_GROUPS += $(TUP_CWD)/<lock>
|
||||||
NIM_FLAGS += --path:$(TUP_CWD)/../cps
|
|
||||||
NIM_FLAGS += --path:$(TUP_CWD)/../solo5_dispatcher/pkg
|
include depends.tup
|
||||||
NIM_FLAGS += --path:$(TUP_CWD)/../taps/src
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
include ../preserves-nim/depends.tup
|
include ../preserves-nim/depends.tup
|
||||||
NIM_FLAGS += --path:$(TUP_CWD)/../preserves-nim/src
|
NIM = $(DIRENV) $(NIM)
|
||||||
|
NIM_FLAGS += --path:$(TUP_CWD)/src
|
||||||
NIM_GROUPS += $(TUP_CWD)/<protocol>
|
NIM_GROUPS += $(TUP_CWD)/<protocol>
|
||||||
|
NIM_GROUPS += $(TUP_CWD)/<schema>
|
||||||
|
NIM_LOCK_EXCLUDES += "syndicate"
|
||||||
|
|
175
lock.json
175
lock.json
|
@ -1,175 +0,0 @@
|
||||||
{
|
|
||||||
"depends": [
|
|
||||||
{
|
|
||||||
"method": "fetchzip",
|
|
||||||
"packages": [
|
|
||||||
"bigints"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/jvrm392g8adfsgf36prgwkbyd7vh5jsw-source",
|
|
||||||
"rev": "86ea14d31eea9275e1408ca34e6bfe9c99989a96",
|
|
||||||
"sha256": "15pcpmnk1bnw3k8769rjzcpg00nahyrypwbxs88jnwr4aczp99j4",
|
|
||||||
"srcDir": "src",
|
|
||||||
"url": "https://github.com/ehmry/nim-bigints/archive/86ea14d31eea9275e1408ca34e6bfe9c99989a96.tar.gz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "fetchzip",
|
|
||||||
"packages": [
|
|
||||||
"cps"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/8gbhwni0akqskdb3qhn5nfgv6gkdz0vz-source",
|
|
||||||
"rev": "c90530ac57f98a842b7be969115c6ef08bdcc564",
|
|
||||||
"sha256": "0h8ghs2fqg68j3jdcg7grnxssmllmgg99kym2w0a3vlwca1zvr62",
|
|
||||||
"srcDir": "",
|
|
||||||
"url": "https://github.com/ehmry/cps/archive/c90530ac57f98a842b7be969115c6ef08bdcc564.tar.gz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "fetchzip",
|
|
||||||
"packages": [
|
|
||||||
"getdns"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/x9xmn7w4k6jg8nv5bnx148ibhnsfh362-source",
|
|
||||||
"rev": "c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6",
|
|
||||||
"sha256": "1sbgx2x51szr22i72n7c8jglnfmr8m7y7ga0v85d58fwadiv7g6b",
|
|
||||||
"srcDir": "src",
|
|
||||||
"url": "https://git.sr.ht/~ehmry/getdns-nim/archive/c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6.tar.gz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "fetchzip",
|
|
||||||
"packages": [
|
|
||||||
"getdns"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/x9xmn7w4k6jg8nv5bnx148ibhnsfh362-source",
|
|
||||||
"rev": "c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6",
|
|
||||||
"sha256": "1sbgx2x51szr22i72n7c8jglnfmr8m7y7ga0v85d58fwadiv7g6b",
|
|
||||||
"srcDir": "src",
|
|
||||||
"url": "https://git.sr.ht/~ehmry/getdns-nim/archive/c73cbe288d9f9480586b8fa87f6d794ffb6a6ce6.tar.gz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "fetchzip",
|
|
||||||
"packages": [
|
|
||||||
"hashlib"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/fav82xdbicvlk34nmcbl89zx99lr3mbs-source",
|
|
||||||
"rev": "f9455d4be988e14e3dc7933eb7cc7d7c4820b7ac",
|
|
||||||
"sha256": "1sx6j952lj98629qfgr7ds5aipyw9d6lldcnnqs205wpj4pkcjb3",
|
|
||||||
"srcDir": "",
|
|
||||||
"url": "https://github.com/ehmry/hashlib/archive/f9455d4be988e14e3dc7933eb7cc7d7c4820b7ac.tar.gz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "fetchzip",
|
|
||||||
"packages": [
|
|
||||||
"nimcrypto"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/fkrcpp8lzj2yi21na79xm63xk0ggnqsp-source",
|
|
||||||
"rev": "f147d30c69bc1c9bcf0e37f7699bcf0fbaab97b5",
|
|
||||||
"sha256": "1h3dzdbc9kacwpi10mj73yjglvn7kbizj1x8qc9099ax091cj5xn",
|
|
||||||
"srcDir": "",
|
|
||||||
"url": "https://github.com/cheatfate/nimcrypto/archive/f147d30c69bc1c9bcf0e37f7699bcf0fbaab97b5.tar.gz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "fetchzip",
|
|
||||||
"packages": [
|
|
||||||
"npeg"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/xpn694ibgipj8xak3j4bky6b3k0vp7hh-source",
|
|
||||||
"rev": "ec0cc6e64ea4c62d2aa382b176a4838474238f8d",
|
|
||||||
"sha256": "1fi9ls3xl20bmv1ikillxywl96i9al6zmmxrbffx448gbrxs86kg",
|
|
||||||
"srcDir": "src",
|
|
||||||
"url": "https://github.com/zevv/npeg/archive/ec0cc6e64ea4c62d2aa382b176a4838474238f8d.tar.gz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "fetchzip",
|
|
||||||
"packages": [
|
|
||||||
"preserves"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/2hy124xgabz134dxj3wji7mp47fdwy3w-source",
|
|
||||||
"rev": "9ae435a83c6d5028405538af5d24a023af625b6e",
|
|
||||||
"sha256": "1k7ywcp1a53x2fpc6wc2b0qzb264dkifash0s1wcp66rw3lx15k2",
|
|
||||||
"srcDir": "src",
|
|
||||||
"url": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/9ae435a83c6d5028405538af5d24a023af625b6e.tar.gz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "fetchzip",
|
|
||||||
"packages": [
|
|
||||||
"stew"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/mqg8qzsbcc8xqabq2yzvlhvcyqypk72c-source",
|
|
||||||
"rev": "3c91b8694e15137a81ec7db37c6c58194ec94a6a",
|
|
||||||
"sha256": "17lfhfxp5nxvld78xa83p258y80ks5jb4n53152cdr57xk86y07w",
|
|
||||||
"srcDir": "",
|
|
||||||
"url": "https://github.com/status-im/nim-stew/archive/3c91b8694e15137a81ec7db37c6c58194ec94a6a.tar.gz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "fetchzip",
|
|
||||||
"packages": [
|
|
||||||
"sys"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/syhxsjlsdqfap0hk4qp3s6kayk8cqknd-source",
|
|
||||||
"rev": "4ef3b624db86e331ba334e705c1aa235d55b05e1",
|
|
||||||
"sha256": "1q4qgw4an4mmmcbx48l6xk1jig1vc8p9cq9dbx39kpnb0890j32q",
|
|
||||||
"srcDir": "src",
|
|
||||||
"url": "https://github.com/ehmry/nim-sys/archive/4ef3b624db86e331ba334e705c1aa235d55b05e1.tar.gz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "fetchzip",
|
|
||||||
"packages": [
|
|
||||||
"sys"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/vf9ls2wip6d8xhsi3rjh0dqsqg597i6b-source",
|
|
||||||
"rev": "c117ee60542f084525f254e6ade590675a6a2ed6",
|
|
||||||
"sha256": "12qzx2lnh84xqfgypy0pka8nflq0y8n1izfwx8mb4zya5nzawmyf",
|
|
||||||
"srcDir": "src",
|
|
||||||
"url": "https://github.com/alaviss/nim-sys/archive/c117ee60542f084525f254e6ade590675a6a2ed6.tar.gz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "fetchzip",
|
|
||||||
"packages": [
|
|
||||||
"taps"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/n86g5fw60z1k53bn35zvrwlwmyk3ixdn-source",
|
|
||||||
"rev": "756cb07b4f874181ad34c370cad6082ee65f646d",
|
|
||||||
"sha256": "0dp7ml3kj2fi6isvjkkxf02hwj0gshx6qra0ghnk2cbfykbcgfp8",
|
|
||||||
"srcDir": "src",
|
|
||||||
"url": "https://git.sr.ht/~ehmry/nim_taps/archive/756cb07b4f874181ad34c370cad6082ee65f646d.tar.gz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2024-04-02T15:38:57+01:00",
|
|
||||||
"deepClone": false,
|
|
||||||
"fetchLFS": false,
|
|
||||||
"fetchSubmodules": true,
|
|
||||||
"hash": "sha256-iZb9aAgYr4FGkqfIg49QWiCqeizIi047kFhugHiP8o0=",
|
|
||||||
"leaveDotGit": false,
|
|
||||||
"method": "git",
|
|
||||||
"packages": [
|
|
||||||
"solo5_dispatcher"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/sf5dgj2ljvahcm6my7d61ibda51vnrii-solo5_dispatcher",
|
|
||||||
"rev": "a7a894a96a2221284012800e6fd32923d83d20bd",
|
|
||||||
"sha256": "13gjixw80vjqj0xlx2y85ixal82sa27q7j57j9383bqq11lgv5l9",
|
|
||||||
"srcDir": "pkg",
|
|
||||||
"url": "https://git.sr.ht/~ehmry/solo5_dispatcher"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "fetchzip",
|
|
||||||
"packages": [
|
|
||||||
"cps"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/phdf6siqbhj7vx4qq507lzla81si60iz-source",
|
|
||||||
"rev": "58772ff9ddb38a4b2ec52da142d8532ba2fe7039",
|
|
||||||
"sha256": "1lph7v27nqwgm3a0ssi8q348gjrkjwgqc50agw38j7xif6wj80cw",
|
|
||||||
"srcDir": "",
|
|
||||||
"url": "https://github.com/ehmry/cps/archive/58772ff9ddb38a4b2ec52da142d8532ba2fe7039.tar.gz"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"method": "fetchzip",
|
|
||||||
"packages": [
|
|
||||||
"stew"
|
|
||||||
],
|
|
||||||
"path": "/nix/store/mqg8qzsbcc8xqabq2yzvlhvcyqypk72c-source",
|
|
||||||
"rev": "3c91b8694e15137a81ec7db37c6c58194ec94a6a",
|
|
||||||
"sha256": "17lfhfxp5nxvld78xa83p258y80ks5jb4n53152cdr57xk86y07w",
|
|
||||||
"srcDir": "",
|
|
||||||
"url": "https://github.com/status-im/nim-stew/archive/3c91b8694e15137a81ec7db37c6c58194ec94a6a.tar.gz"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -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": "20240628",
|
||||||
|
"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": "20240610",
|
||||||
|
"externalReferences": [
|
||||||
|
{
|
||||||
|
"url": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/560a6417a30a2dff63f24b62498e9fcac2de8354.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/0sszsmz84ppwqsgda8cmli4lfh2mjmin-source"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:rev",
|
||||||
|
"value": "560a6417a30a2dff63f24b62498e9fcac2de8354"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:sha256",
|
||||||
|
"value": "19r983fy7m54mlaj0adxdp8pxi1x8dp6phkcnr8rz5y5cwndfjx2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:url",
|
||||||
|
"value": "https://git.syndicate-lang.org/ehmry/preserves-nim/archive/560a6417a30a2dff63f24b62498e9fcac2de8354.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nix:fod:ref",
|
||||||
|
"value": "20240610"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
pkgs.buildNimPackage {
|
pkgs.buildNimPackage {
|
||||||
name = "dummy";
|
name = "dummy";
|
||||||
lockFile = ./lock.json;
|
|
||||||
buildInputs = builtins.attrValues { inherit (pkgs) getdns solo5; };
|
buildInputs = builtins.attrValues { inherit (pkgs) getdns solo5; };
|
||||||
nativeBuildInputs = builtins.attrValues { inherit (pkgs) pkg-config solo5; };
|
nativeBuildInputs = builtins.attrValues { inherit (pkgs) pkg-config solo5; };
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
include_rules
|
include_rules
|
||||||
: foreach *.nim |> !nim_check |>
|
: foreach *.nim |> !nim-doc |>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import std/[macros, tables, typetraits]
|
import std/[macros, tables, typetraits]
|
||||||
|
|
||||||
import preserves
|
import pkg/preserves
|
||||||
export fromPreserves, toPreserves
|
export fromPreserves, toPreserves
|
||||||
|
|
||||||
import ./syndicate/[actors, dataspaces, durings, patterns]
|
import ./syndicate/[actors, dataspaces, durings, patterns]
|
||||||
|
@ -16,13 +16,13 @@ export actors, dataspace, dataspaces, patterns
|
||||||
type Assertion* {.deprecated: "Assertion and Preserve[void] replaced by Value".} = Value
|
type Assertion* {.deprecated: "Assertion and Preserve[void] replaced by Value".} = Value
|
||||||
|
|
||||||
proc `!`*(typ: static typedesc): Pattern {.inline.} =
|
proc `!`*(typ: static typedesc): Pattern {.inline.} =
|
||||||
patterns.dropType(typ)
|
patterns.matchType(typ)
|
||||||
|
|
||||||
proc `?`*[T](val: T): Pattern {.inline.} =
|
proc `?`*[T](val: T): Pattern {.inline.} =
|
||||||
patterns.grab[T](val)
|
patterns.drop[T](val)
|
||||||
|
|
||||||
proc `?:`*(typ: static typedesc): Pattern {.inline.} =
|
proc `?:`*(typ: static typedesc): Pattern {.inline.} =
|
||||||
patterns.grabType(typ)
|
patterns.grabTypeFlat(typ)
|
||||||
|
|
||||||
proc `?:`*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern {.inline.} =
|
proc `?:`*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern {.inline.} =
|
||||||
patterns.grab(typ, bindings)
|
patterns.grab(typ, bindings)
|
||||||
|
@ -30,25 +30,22 @@ proc `?:`*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Patt
|
||||||
proc `?:`*(typ: static typedesc; bindings: sink openArray[(Value, Pattern)]): Pattern {.inline.} =
|
proc `?:`*(typ: static typedesc; bindings: sink openArray[(Value, Pattern)]): Pattern {.inline.} =
|
||||||
patterns.grab(typ, bindings)
|
patterns.grab(typ, bindings)
|
||||||
|
|
||||||
proc `??`*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern {.inline.} =
|
|
||||||
patterns.inject(pat, bindings)
|
|
||||||
|
|
||||||
type
|
type
|
||||||
PublishProc = proc (turn: var Turn; v: Value; h: Handle) {.closure.}
|
PublishProc = proc (turn: Turn; v: Value; h: Handle) {.closure.}
|
||||||
RetractProc = proc (turn: var Turn; h: Handle) {.closure.}
|
RetractProc = proc (turn: Turn; h: Handle) {.closure.}
|
||||||
MessageProc = proc (turn: var Turn; v: Value) {.closure.}
|
MessageProc = proc (turn: Turn; v: Value) {.closure.}
|
||||||
ClosureEntity = ref object of Entity
|
ClosureEntity = ref object of Entity
|
||||||
publishImpl*: PublishProc
|
publishImpl*: PublishProc
|
||||||
retractImpl*: RetractProc
|
retractImpl*: RetractProc
|
||||||
messageImpl*: MessageProc
|
messageImpl*: MessageProc
|
||||||
|
|
||||||
method publish(e: ClosureEntity; turn: var Turn; a: AssertionRef; h: Handle) =
|
method publish(e: ClosureEntity; turn: Turn; a: AssertionRef; h: Handle) =
|
||||||
if not e.publishImpl.isNil: e.publishImpl(turn, a.value, h)
|
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)
|
if not e.retractImpl.isNil: e.retractImpl(turn, h)
|
||||||
|
|
||||||
method message(e: ClosureEntity; turn: var Turn; a: AssertionRef) =
|
method message(e: ClosureEntity; turn: Turn; a: AssertionRef) =
|
||||||
if not e.messageImpl.isNil: e.messageImpl(turn, a.value)
|
if not e.messageImpl.isNil: e.messageImpl(turn, a.value)
|
||||||
|
|
||||||
proc argumentCount(handler: NimNode): int =
|
proc argumentCount(handler: NimNode): int =
|
||||||
|
@ -93,7 +90,7 @@ proc wrapPublishHandler(turn, handler: NimNode): NimNode =
|
||||||
handlerSym = genSym(nskProc, "publish")
|
handlerSym = genSym(nskProc, "publish")
|
||||||
bindingsSym = ident"bindings"
|
bindingsSym = ident"bindings"
|
||||||
quote do:
|
quote do:
|
||||||
proc `handlerSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle) =
|
proc `handlerSym`(`turn`: Turn; `bindingsSym`: Value; `handleSym`: Handle) =
|
||||||
`varSection`
|
`varSection`
|
||||||
if fromPreserves(`valuesSym`, bindings):
|
if fromPreserves(`valuesSym`, bindings):
|
||||||
`publishBody`
|
`publishBody`
|
||||||
|
@ -105,7 +102,7 @@ proc wrapMessageHandler(turn, handler: NimNode): NimNode =
|
||||||
handlerSym = genSym(nskProc, "message")
|
handlerSym = genSym(nskProc, "message")
|
||||||
bindingsSym = ident"bindings"
|
bindingsSym = ident"bindings"
|
||||||
quote do:
|
quote do:
|
||||||
proc `handlerSym`(`turn`: var Turn; `bindingsSym`: Value) =
|
proc `handlerSym`(`turn`: Turn; `bindingsSym`: Value) =
|
||||||
`varSection`
|
`varSection`
|
||||||
if fromPreserves(`valuesSym`, bindings):
|
if fromPreserves(`valuesSym`, bindings):
|
||||||
`body`
|
`body`
|
||||||
|
@ -119,17 +116,17 @@ proc wrapDuringHandler(turn, entryBody, exitBody: NimNode): NimNode =
|
||||||
duringSym = genSym(nskProc, "during")
|
duringSym = genSym(nskProc, "during")
|
||||||
if exitBody.isNil:
|
if exitBody.isNil:
|
||||||
quote do:
|
quote do:
|
||||||
proc `duringSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle): TurnAction =
|
proc `duringSym`(`turn`: Turn; `bindingsSym`: Value; `handleSym`: Handle): TurnAction =
|
||||||
`varSection`
|
`varSection`
|
||||||
if fromPreserves(`valuesSym`, `bindingsSym`):
|
if fromPreserves(`valuesSym`, `bindingsSym`):
|
||||||
`publishBody`
|
`publishBody`
|
||||||
else:
|
else:
|
||||||
quote do:
|
quote do:
|
||||||
proc `duringSym`(`turn`: var Turn; `bindingsSym`: Value; `handleSym`: Handle): TurnAction =
|
proc `duringSym`(`turn`: Turn; `bindingsSym`: Value; `handleSym`: Handle): TurnAction =
|
||||||
`varSection`
|
`varSection`
|
||||||
if fromPreserves(`valuesSym`, `bindingsSym`):
|
if fromPreserves(`valuesSym`, `bindingsSym`):
|
||||||
`publishBody`
|
`publishBody`
|
||||||
proc action(`turn`: var Turn) =
|
proc action(`turn`: Turn) =
|
||||||
`exitBody`
|
`exitBody`
|
||||||
result = action
|
result = action
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
include_rules
|
include_rules
|
||||||
NIM_FLAGS += --path:$(TUP_CWD)/..
|
NIM_FLAGS += --path:$(TUP_CWD)/..
|
||||||
: foreach *.nim |> !nim_check |>
|
: patterns.nim |> !nim_check |>
|
||||||
|
: patterns.nim |> !nim_bin |>
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[assertions, deques, hashes, monotimes, options, sets, tables, times]
|
import
|
||||||
import cps
|
std/[assertions, deques, hashes, monotimes, options, sets, tables, times],
|
||||||
|
pkg/cps,
|
||||||
import preserves
|
pkg/preserves,
|
||||||
import ../syndicate/protocols/[protocol, sturdy]
|
../syndicate/protocols/[protocol, sturdy, trace]
|
||||||
import ../syndicate/protocols/trace
|
|
||||||
|
|
||||||
when defined(solo5):
|
when defined(solo5):
|
||||||
import solo5_dispatcher
|
import solo5_dispatcher
|
||||||
|
@ -21,6 +20,7 @@ when tracing:
|
||||||
from std/os import getEnv
|
from std/os import getEnv
|
||||||
|
|
||||||
export Handle
|
export Handle
|
||||||
|
export sturdy.Caveat
|
||||||
|
|
||||||
type
|
type
|
||||||
Oid = sturdy.Oid
|
Oid = sturdy.Oid
|
||||||
|
@ -41,7 +41,7 @@ type
|
||||||
target*: Entity
|
target*: Entity
|
||||||
relay*: Facet
|
relay*: Facet
|
||||||
# Entity has facet but a Cap is also scoped to a relay Facet
|
# Entity has facet but a Cap is also scoped to a relay Facet
|
||||||
attenuation*: Attenuation
|
caveats*: seq[Caveat]
|
||||||
|
|
||||||
Ref* {.deprecated: "Ref was renamed to Cap".} = Cap
|
Ref* {.deprecated: "Ref was renamed to Cap".} = Cap
|
||||||
|
|
||||||
|
@ -64,12 +64,13 @@ type
|
||||||
facetIdAllocator: uint
|
facetIdAllocator: uint
|
||||||
exiting, exited: bool
|
exiting, exited: bool
|
||||||
|
|
||||||
TurnAction* = proc (t: var Turn) {.closure.}
|
TurnAction* = proc (t: Turn) {.closure.}
|
||||||
|
|
||||||
Turn* = ref object
|
Turn* = var TurnRef
|
||||||
|
TurnRef* = ref object
|
||||||
facet: Facet # active facet that may change during a turn
|
facet: Facet # active facet that may change during a turn
|
||||||
work: Deque[tuple[facet: Facet, act: TurnAction]]
|
work: Deque[tuple[facet: Facet, act: TurnAction]]
|
||||||
effects: Table[Actor, Turn]
|
effects: Table[Actor, TurnRef]
|
||||||
when tracing:
|
when tracing:
|
||||||
desc: TurnDescription
|
desc: TurnDescription
|
||||||
|
|
||||||
|
@ -84,7 +85,9 @@ type
|
||||||
id: FacetId
|
id: FacetId
|
||||||
isAlive: bool
|
isAlive: bool
|
||||||
|
|
||||||
var turnQueue {.threadvar.}: Deque[Turn]
|
var
|
||||||
|
rootActors {.threadvar.}: HashSet[Actor]
|
||||||
|
turnQueue {.threadvar.}: Deque[TurnRef]
|
||||||
|
|
||||||
when tracing:
|
when tracing:
|
||||||
when defined(solo5):
|
when defined(solo5):
|
||||||
|
@ -144,11 +147,6 @@ when tracing:
|
||||||
result.facet = cap.target.facet.id
|
result.facet = cap.target.facet.id
|
||||||
result.oid = cap.target.oid.toPreserves
|
result.oid = cap.target.oid.toPreserves
|
||||||
|
|
||||||
method publish*(e: Entity; turn: var Turn; v: AssertionRef; h: Handle) {.base.} = discard
|
|
||||||
method retract*(e: Entity; turn: var Turn; h: Handle) {.base.} = discard
|
|
||||||
method message*(e: Entity; turn: var Turn; v: AssertionRef) {.base.} = discard
|
|
||||||
method sync*(e: Entity; turn: var Turn; peer: Cap) {.base.} = discard
|
|
||||||
|
|
||||||
converter toActor(f: Facet): Actor = f.actor
|
converter toActor(f: Facet): Actor = f.actor
|
||||||
converter toActor(t: Turn): Actor = t.facet.actor
|
converter toActor(t: Turn): Actor = t.facet.actor
|
||||||
converter toFacet(a: Actor): Facet = a.root
|
converter toFacet(a: Actor): Facet = a.root
|
||||||
|
@ -157,7 +155,7 @@ converter toFacet(t: Turn): Facet = t.facet
|
||||||
using
|
using
|
||||||
actor: Actor
|
actor: Actor
|
||||||
facet: Facet
|
facet: Facet
|
||||||
turn: var Turn
|
turn: Turn
|
||||||
action: TurnAction
|
action: TurnAction
|
||||||
|
|
||||||
proc labels(f: Facet): string =
|
proc labels(f: Facet): string =
|
||||||
|
@ -185,12 +183,28 @@ when tracing:
|
||||||
proc `$`*(t: Turn): string =
|
proc `$`*(t: Turn): string =
|
||||||
"<Turn:" & $t.desc.id & ">"
|
"<Turn:" & $t.desc.id & ">"
|
||||||
|
|
||||||
proc attenuate*(r: Cap; a: Attenuation): Cap =
|
proc attenuate*(cap: Cap; caveats: openarray[Caveat]): Cap =
|
||||||
if a.len == 0: result = r
|
## Create a new `Cap` attenuated by a `caveats`.
|
||||||
else: result = Cap(
|
## These caveats are applied before those already present at `cap`.
|
||||||
target: r.target,
|
if caveats.len == 0: result = cap
|
||||||
relay: r.relay,
|
else:
|
||||||
attenuation: a & r.attenuation)
|
var att = newSeqOfCap[Caveat](caveats.len + cap.caveats.len)
|
||||||
|
att.add cap.caveats
|
||||||
|
att.add caveats
|
||||||
|
result = Cap(
|
||||||
|
target: cap.target,
|
||||||
|
relay: cap.relay,
|
||||||
|
caveats: att,
|
||||||
|
)
|
||||||
|
|
||||||
|
proc attenuate*(cap: Cap; cav: Caveat): Cap =
|
||||||
|
## Create a new `Cap` attenuated by a `Caveat`.
|
||||||
|
result = Cap(
|
||||||
|
target: cap.target,
|
||||||
|
relay: cap.relay,
|
||||||
|
caveats: cap.caveats,
|
||||||
|
)
|
||||||
|
result.caveats.add cav
|
||||||
|
|
||||||
proc hash*(actor): Hash =
|
proc hash*(actor): Hash =
|
||||||
result = actor[].unsafeAddr.hash
|
result = actor[].unsafeAddr.hash
|
||||||
|
@ -206,27 +220,27 @@ proc nextHandle(facet: Facet): Handle =
|
||||||
result = succ(facet.actor.handleAllocator[])
|
result = succ(facet.actor.handleAllocator[])
|
||||||
facet.actor.handleAllocator[] = result
|
facet.actor.handleAllocator[] = result
|
||||||
|
|
||||||
template recallFacet(turn: var Turn; body: untyped): untyped =
|
template recallFacet(turn: Turn; body: untyped): untyped =
|
||||||
let facet = turn.facet
|
let facet = turn.facet
|
||||||
block:
|
block:
|
||||||
body
|
body
|
||||||
assert facet.actor == turn.facet.actor
|
assert facet.actor == turn.facet.actor
|
||||||
turn.facet = facet
|
turn.facet = facet
|
||||||
|
|
||||||
proc queueWork*(turn: var Turn; facet: Facet; act: TurnAction) =
|
proc queueWork*(turn: Turn; facet: Facet; act: TurnAction) =
|
||||||
assert not facet.isNil
|
assert not facet.isNil
|
||||||
turn.work.addLast((facet, act,))
|
turn.work.addLast((facet, act,))
|
||||||
|
|
||||||
proc queueTurn*(facet: Facet; act: TurnAction) =
|
proc queueTurn*(facet: Facet; act: TurnAction) =
|
||||||
var turn = Turn(facet: facet)
|
var turn = TurnRef(facet: facet)
|
||||||
assert not facet.isNil
|
assert not facet.isNil
|
||||||
turn.work.addLast((facet, act,))
|
turn.work.addLast((facet, act,))
|
||||||
when tracing:
|
when tracing:
|
||||||
turn.desc.id = nextTurnId()
|
turn.desc.id = nextTurnId()
|
||||||
turnQueue.addLast(turn)
|
turnQueue.addLast(turn)
|
||||||
|
|
||||||
proc queueTurn*(prev: var Turn; facet: Facet; act: TurnAction) =
|
proc queueTurn*(prev: Turn; facet: Facet; act: TurnAction) =
|
||||||
var next = Turn(facet: facet)
|
var next = TurnRef(facet: facet)
|
||||||
assert not facet.isNil
|
assert not facet.isNil
|
||||||
next.work.addLast((facet, act,))
|
next.work.addLast((facet, act,))
|
||||||
when tracing:
|
when tracing:
|
||||||
|
@ -238,16 +252,19 @@ proc queueTurn*(prev: var Turn; facet: Facet; act: TurnAction) =
|
||||||
proc run*(facet: Facet; action: TurnAction) = queueTurn(facet, action)
|
proc run*(facet: Facet; action: TurnAction) = queueTurn(facet, action)
|
||||||
## Alias to queueTurn_.
|
## Alias to queueTurn_.
|
||||||
|
|
||||||
|
proc run*(cap: Cap; action: TurnAction) = queueTurn(cap.relay, action)
|
||||||
|
## Run `action` with a new `Turn` at the `Facet` that relays for `cap`.
|
||||||
|
|
||||||
proc facet*(turn: Turn): Facet = turn.facet
|
proc facet*(turn: Turn): Facet = turn.facet
|
||||||
|
|
||||||
proc queueEffect*(turn: var Turn; target: Facet; act: TurnAction) =
|
proc queueEffect*(turn: Turn; target: Facet; act: TurnAction) =
|
||||||
let fremd = target.actor
|
let fremd = target.actor
|
||||||
if fremd == turn.facet.actor:
|
if fremd == turn.facet.actor:
|
||||||
turn.work.addLast((target, act,))
|
turn.work.addLast((target, act,))
|
||||||
else:
|
else:
|
||||||
var fremdTurn = turn.effects.getOrDefault(fremd)
|
var fremdTurn = turn.effects.getOrDefault(fremd)
|
||||||
if fremdTurn.isNil:
|
if fremdTurn.isNil:
|
||||||
fremdTurn = Turn(facet: target)
|
fremdTurn = TurnRef(facet: target)
|
||||||
turn.effects[fremd] = fremdTurn
|
turn.effects[fremd] = fremdTurn
|
||||||
when tracing:
|
when tracing:
|
||||||
fremdTurn.desc.id = nextTurnId()
|
fremdTurn.desc.id = nextTurnId()
|
||||||
|
@ -341,7 +358,7 @@ proc instantiate(t: Template; bindings: Bindings): Value =
|
||||||
for i, tt in t.tcompound.arr.items:
|
for i, tt in t.tcompound.arr.items:
|
||||||
result[i] = instantiate(tt, bindings)
|
result[i] = instantiate(tt, bindings)
|
||||||
of TCompoundKind.dict:
|
of TCompoundKind.dict:
|
||||||
result = initDictionary()
|
result = initDictionary(t.tcompound.dict.entries.len)
|
||||||
for key, tt in t.tcompound.dict.entries:
|
for key, tt in t.tcompound.dict.entries:
|
||||||
result[key] = instantiate(tt, bindings)
|
result[key] = instantiate(tt, bindings)
|
||||||
|
|
||||||
|
@ -350,7 +367,19 @@ proc rewrite(r: Rewrite; v: Value): Value =
|
||||||
if bindings.isSome:
|
if bindings.isSome:
|
||||||
result = instantiate(r.template, get bindings)
|
result = instantiate(r.template, get bindings)
|
||||||
|
|
||||||
proc examineAlternatives(cav: Caveat; v: Value): Value =
|
proc attenuate*(v: Value; cav: Caveat): Value =
|
||||||
|
## Attenuate a `Value` by applying a `Caveat` function.
|
||||||
|
runnableExamples:
|
||||||
|
import pkg/preserves
|
||||||
|
|
||||||
|
proc parseCaveat(s: string): Caveat =
|
||||||
|
doAssert result.fromPreserves(parsePreserves s)
|
||||||
|
|
||||||
|
let val = parsePreserves"""<foo { x: [ #t "bar" ] }> """
|
||||||
|
doAssert val.attenuate(parseCaveat"""<reject <_>>""").isFalse
|
||||||
|
doAssert val.attenuate(parseCaveat"""<reject <not <_>>>""") == val
|
||||||
|
doAssert val.attenuate(parseCaveat"""<reject <not <rec foo [<_>]>>>""") == val
|
||||||
|
|
||||||
case cav.orKind
|
case cav.orKind
|
||||||
of CaveatKind.Rewrite:
|
of CaveatKind.Rewrite:
|
||||||
result = rewrite(cav.rewrite, v)
|
result = rewrite(cav.rewrite, v)
|
||||||
|
@ -358,17 +387,24 @@ proc examineAlternatives(cav: Caveat; v: Value): Value =
|
||||||
for r in cav.alts.alternatives:
|
for r in cav.alts.alternatives:
|
||||||
result = rewrite(r, v)
|
result = rewrite(r, v)
|
||||||
if not result.isFalse: break
|
if not result.isFalse: break
|
||||||
of CaveatKind.Reject: discard
|
of CaveatKind.Reject:
|
||||||
|
if cav.reject.pattern.match(v).isNone:
|
||||||
|
result = v
|
||||||
of CaveatKind.unknown: discard
|
of CaveatKind.unknown: discard
|
||||||
|
|
||||||
proc runRewrites*(a: Attenuation; v: Value): Value =
|
proc attenuate*(v: Value; caveats: openarray[Caveat]): Value =
|
||||||
|
## Attenuate a `Value` by applying `caveats`.
|
||||||
|
## Application is in reverse order.
|
||||||
result = v
|
result = v
|
||||||
for stage in a:
|
var i = caveats.high
|
||||||
result = examineAlternatives(stage, result)
|
while i > -1 and not result.isFalse:
|
||||||
if result.isFalse: break
|
result = result.attenuate(caveats[i])
|
||||||
|
dec i
|
||||||
|
|
||||||
proc publish(turn: var Turn; cap: Cap; v: Value; h: Handle) =
|
method publish*(e: Entity; turn: Turn; v: AssertionRef; h: Handle) {.base.} = discard
|
||||||
var a = runRewrites(cap.attenuation, v)
|
|
||||||
|
proc publish(turn: Turn; cap: Cap; v: Value; h: Handle) =
|
||||||
|
var a = v.attenuate(cap.caveats)
|
||||||
if not a.isFalse:
|
if not a.isFalse:
|
||||||
let e = OutboundAssertion(handle: h, peer: cap)
|
let e = OutboundAssertion(handle: h, peer: cap)
|
||||||
turn.facet.outbound[h] = e
|
turn.facet.outbound[h] = e
|
||||||
|
@ -379,81 +415,92 @@ proc publish(turn: var Turn; cap: Cap; v: Value; h: Handle) =
|
||||||
act.enqueue.event.target.oid = cap.target.oid.toPreserves
|
act.enqueue.event.target.oid = cap.target.oid.toPreserves
|
||||||
act.enqueue.event.detail = trace.TurnEvent(orKind: trace.TurnEventKind.assert)
|
act.enqueue.event.detail = trace.TurnEvent(orKind: trace.TurnEventKind.assert)
|
||||||
act.enqueue.event.detail.assert.assertion.value.value =
|
act.enqueue.event.detail.assert.assertion.value.value =
|
||||||
mapEmbeds(v) do (cap: Value) -> Value: discard
|
mapEmbeds(v) do (cap: Value) -> Value:
|
||||||
|
result.embedded = true # #:#f
|
||||||
act.enqueue.event.detail.assert.handle = h
|
act.enqueue.event.detail.assert.handle = h
|
||||||
turn.desc.actions.add act
|
turn.desc.actions.add act
|
||||||
queueEffect(turn, cap.relay) do (turn: var Turn):
|
queueEffect(turn, cap.relay) do (turn: Turn):
|
||||||
e.established = true
|
e.established = true
|
||||||
when tracing:
|
when tracing:
|
||||||
turn.desc.actions.add act.toDequeue
|
turn.desc.actions.add act.toDequeue
|
||||||
publish(cap.target, turn, AssertionRef(value: a), e.handle)
|
publish(cap.target, turn, AssertionRef(value: a), e.handle)
|
||||||
|
|
||||||
proc publish*(turn: var Turn; r: Cap; a: Value): Handle {.discardable.} =
|
proc publish*(turn: Turn; r: Cap; a: Value): Handle {.discardable.} =
|
||||||
result = turn.facet.nextHandle()
|
result = turn.facet.nextHandle()
|
||||||
publish(turn, r, a, result)
|
publish(turn, r, a, result)
|
||||||
|
|
||||||
proc publish*[T](turn: var Turn; r: Cap; a: T): Handle {.discardable.} =
|
proc publish*[T](turn: Turn; r: Cap; a: T): Handle {.discardable.} =
|
||||||
publish(turn, r, a.toPreserves)
|
publish(turn, r, a.toPreserves)
|
||||||
|
|
||||||
proc retract(turn: var Turn; e: OutboundAssertion) =
|
method retract*(e: Entity; turn: Turn; h: Handle) {.base.} = discard
|
||||||
|
|
||||||
|
proc retract(turn: Turn; e: OutboundAssertion) =
|
||||||
when tracing:
|
when tracing:
|
||||||
var act = initEnqueue(turn, e.peer)
|
var act = initEnqueue(turn, e.peer)
|
||||||
act.enqueue.event.detail = trace.TurnEvent(orKind: TurnEventKind.retract)
|
act.enqueue.event.detail = trace.TurnEvent(orKind: TurnEventKind.retract)
|
||||||
act.enqueue.event.detail.retract.handle = e.handle
|
act.enqueue.event.detail.retract.handle = e.handle
|
||||||
turn.desc.actions.add act
|
turn.desc.actions.add act
|
||||||
queueEffect(turn, e.peer.relay) do (turn: var Turn):
|
queueEffect(turn, e.peer.relay) do (turn: Turn):
|
||||||
when tracing:
|
when tracing:
|
||||||
turn.desc.actions.add act.toDequeue
|
turn.desc.actions.add act.toDequeue
|
||||||
if e.established:
|
if e.established:
|
||||||
e.established = false
|
e.established = false
|
||||||
e.peer.target.retract(turn, e.handle)
|
e.peer.target.retract(turn, e.handle)
|
||||||
|
|
||||||
proc retract*(turn: var Turn; h: Handle) =
|
proc retract*(turn: Turn; h: Handle) =
|
||||||
var e: OutboundAssertion
|
var e: OutboundAssertion
|
||||||
if turn.facet.outbound.pop(h, e):
|
if turn.facet.outbound.pop(h, e):
|
||||||
turn.retract(e)
|
turn.retract(e)
|
||||||
|
|
||||||
proc message*(turn: var Turn; r: Cap; v: Value) =
|
method message*(e: Entity; turn: Turn; v: AssertionRef) {.base.} = discard
|
||||||
var a = runRewrites(r.attenuation, v)
|
|
||||||
|
proc message*(turn: Turn; r: Cap; v: Value) =
|
||||||
|
var a = v.attenuate(r.caveats)
|
||||||
if not a.isFalse:
|
if not a.isFalse:
|
||||||
when tracing:
|
when tracing:
|
||||||
var act = initEnqueue(turn, r)
|
var act = initEnqueue(turn, r)
|
||||||
act.enqueue.event.detail = trace.TurnEvent(orKind: TurnEventKind.message)
|
act.enqueue.event.detail = trace.TurnEvent(orKind: TurnEventKind.message)
|
||||||
act.enqueue.event.detail.message.body.value.value =
|
act.enqueue.event.detail.message.body.value.value =
|
||||||
mapEmbeds(a) do (cap: Value) -> Value: discard
|
mapEmbeds(a) do (cap: Value) -> Value:
|
||||||
|
result.embedded = true # #:#f
|
||||||
turn.desc.actions.add act
|
turn.desc.actions.add act
|
||||||
queueEffect(turn, r.relay) do (turn: var Turn):
|
queueEffect(turn, r.relay) do (turn: Turn):
|
||||||
when tracing:
|
when tracing:
|
||||||
turn.desc.actions.add act.toDequeue
|
turn.desc.actions.add act.toDequeue
|
||||||
r.target.message(turn, AssertionRef(value: a))
|
r.target.message(turn, AssertionRef(value: a))
|
||||||
|
|
||||||
proc message*[T](turn: var Turn; r: Cap; v: T) =
|
proc message*[T](turn: Turn; r: Cap; v: T) =
|
||||||
message(turn, r, v.toPreserves)
|
message(turn, r, v.toPreserves)
|
||||||
|
|
||||||
proc sync*(turn: var Turn; r, peer: Cap) =
|
method sync*(e: Entity; turn: Turn; peer: Cap) {.base.} =
|
||||||
|
queueTurn(e.facet) do (turn: Turn):
|
||||||
|
message(turn, peer, true.toPreserves)
|
||||||
|
# complete sync on a later turn
|
||||||
|
|
||||||
|
proc sync*(turn: Turn; r, peer: Cap) =
|
||||||
when tracing:
|
when tracing:
|
||||||
var act = initEnqueue(turn, peer)
|
var act = initEnqueue(turn, peer)
|
||||||
act.enqueue.event.detail = trace.TurnEvent(orKind: TurnEventKind.sync)
|
act.enqueue.event.detail = trace.TurnEvent(orKind: TurnEventKind.sync)
|
||||||
act.enqueue.event.detail.sync.peer = peer.toTraceTarget
|
act.enqueue.event.detail.sync.peer = peer.toTraceTarget
|
||||||
turn.desc.actions.add act
|
turn.desc.actions.add act
|
||||||
queueEffect(turn, r.relay) do (turn: var Turn):
|
queueEffect(turn, r.relay) do (turn: Turn):
|
||||||
when tracing:
|
when tracing:
|
||||||
turn.desc.actions.add act.toDequeue
|
turn.desc.actions.add act.toDequeue
|
||||||
r.target.sync(turn, peer)
|
r.target.sync(turn, peer)
|
||||||
|
|
||||||
proc replace*[T](turn: var Turn; cap: Cap; h: Handle; v: T): Handle =
|
proc replace*[T](turn: Turn; cap: Cap; h: Handle; v: T): Handle =
|
||||||
result = publish(turn, cap, v)
|
result = publish(turn, cap, v)
|
||||||
if h != default(Handle):
|
if h != default(Handle):
|
||||||
retract(turn, h)
|
retract(turn, h)
|
||||||
|
|
||||||
proc replace*[T](turn: var Turn; cap: Cap; h: var Handle; v: T): Handle {.discardable.} =
|
proc replace*[T](turn: Turn; cap: Cap; h: var Handle; v: T): Handle {.discardable.} =
|
||||||
var old = h
|
var old = h
|
||||||
h = publish(turn, cap, v)
|
h = publish(turn, cap, v)
|
||||||
if old != default(Handle):
|
if old != default(Handle):
|
||||||
retract(turn, old)
|
retract(turn, old)
|
||||||
h
|
h
|
||||||
|
|
||||||
proc stop*(turn: var Turn)
|
proc stop*(turn: Turn)
|
||||||
|
|
||||||
proc newFacet(actor; parent: Facet; initialAssertions: OutboundTable): Facet =
|
proc newFacet(actor; parent: Facet; initialAssertions: OutboundTable): Facet =
|
||||||
inc actor.facetIdAllocator
|
inc actor.facetIdAllocator
|
||||||
|
@ -482,7 +529,7 @@ proc preventInertCheck*(turn: Turn) =
|
||||||
|
|
||||||
proc terminateActor(turn; reason: ref Exception)
|
proc terminateActor(turn; reason: ref Exception)
|
||||||
|
|
||||||
proc terminateFacetOrderly(turn: var Turn) =
|
proc terminateFacetOrderly(turn: Turn) =
|
||||||
let facet = turn.facet
|
let facet = turn.facet
|
||||||
if facet.isAlive:
|
if facet.isAlive:
|
||||||
facet.isAlive = false
|
facet.isAlive = false
|
||||||
|
@ -495,7 +542,7 @@ proc terminateFacetOrderly(turn: var Turn) =
|
||||||
retract(turn, e)
|
retract(turn, e)
|
||||||
clear facet.outbound
|
clear facet.outbound
|
||||||
|
|
||||||
proc inertCheck(turn: var Turn) =
|
proc inertCheck(turn: Turn) =
|
||||||
if (not turn.facet.parent.isNil and
|
if (not turn.facet.parent.isNil and
|
||||||
(not turn.facet.parent.isAlive)) or
|
(not turn.facet.parent.isAlive)) or
|
||||||
turn.facet.isInert:
|
turn.facet.isInert:
|
||||||
|
@ -506,7 +553,7 @@ proc inertCheck(turn: var Turn) =
|
||||||
turn.desc.actions.add act
|
turn.desc.actions.add act
|
||||||
stop(turn)
|
stop(turn)
|
||||||
|
|
||||||
proc terminateFacet(turn: var Turn) =
|
proc terminateFacet(turn: Turn) =
|
||||||
let facet = turn.facet
|
let facet = turn.facet
|
||||||
for child in facet.children:
|
for child in facet.children:
|
||||||
queueWork(turn, child, terminateFacetOrderly)
|
queueWork(turn, child, terminateFacetOrderly)
|
||||||
|
@ -517,14 +564,14 @@ proc terminateFacet(turn: var Turn) =
|
||||||
# self-termination
|
# self-termination
|
||||||
|
|
||||||
proc stopIfInertAfter(action: TurnAction): TurnAction =
|
proc stopIfInertAfter(action: TurnAction): TurnAction =
|
||||||
proc work(turn: var Turn) =
|
proc work(turn: Turn) =
|
||||||
queueEffect(turn, turn.facet, inertCheck)
|
queueEffect(turn, turn.facet, inertCheck)
|
||||||
action(turn)
|
action(turn)
|
||||||
work
|
work
|
||||||
|
|
||||||
proc newFacet(turn: var Turn): Facet = newFacet(turn.facet.actor, turn.facet)
|
proc newFacet(turn: Turn): Facet = newFacet(turn.facet.actor, turn.facet)
|
||||||
|
|
||||||
proc inFacet*(turn: var Turn; bootProc: TurnAction): Facet {.discardable.} =
|
proc inFacet*(turn: Turn; bootProc: TurnAction): Facet {.discardable.} =
|
||||||
result = newFacet(turn)
|
result = newFacet(turn)
|
||||||
recallFacet turn:
|
recallFacet turn:
|
||||||
turn.facet = result
|
turn.facet = result
|
||||||
|
@ -557,7 +604,7 @@ proc bootActor*(name: string; bootProc: TurnAction): Actor {.discardable.} =
|
||||||
## Boot a top-level actor.
|
## Boot a top-level actor.
|
||||||
result = newActor(name, nil)
|
result = newActor(name, nil)
|
||||||
new result.handleAllocator
|
new result.handleAllocator
|
||||||
var turn = Turn(facet: result.root)
|
var turn = TurnRef(facet: result.root)
|
||||||
assert not result.root.isNil
|
assert not result.root.isNil
|
||||||
turn.work.addLast((result.root, bootProc,))
|
turn.work.addLast((result.root, bootProc,))
|
||||||
when tracing:
|
when tracing:
|
||||||
|
@ -566,9 +613,9 @@ proc bootActor*(name: string; bootProc: TurnAction): Actor {.discardable.} =
|
||||||
turn.desc.cause.external.description = "bootActor".toPreserves
|
turn.desc.cause.external.description = "bootActor".toPreserves
|
||||||
turnQueue.addLast turn
|
turnQueue.addLast turn
|
||||||
|
|
||||||
proc spawnActor*(turn: var Turn; name: string; bootProc: TurnAction; initialAssertions = initHashSet[Handle]()): Actor {.discardable.} =
|
proc spawnActor*(turn: Turn; name: string; bootProc: TurnAction; initialAssertions = initHashSet[Handle]()): Actor {.discardable.} =
|
||||||
let actor = newActor(name, turn.facet)
|
let actor = newActor(name, turn.facet)
|
||||||
queueEffect(turn, actor.root) do (turn: var Turn):
|
queueEffect(turn, actor.root) do (turn: Turn):
|
||||||
var newOutBound: Table[Handle, OutboundAssertion]
|
var newOutBound: Table[Handle, OutboundAssertion]
|
||||||
for key in initialAssertions:
|
for key in initialAssertions:
|
||||||
discard turn.facet.outbound.pop(key, newOutbound[key])
|
discard turn.facet.outbound.pop(key, newOutbound[key])
|
||||||
|
@ -579,12 +626,12 @@ proc spawnActor*(turn: var Turn; name: string; bootProc: TurnAction; initialAsse
|
||||||
run(actor, bootProc, newOutBound)
|
run(actor, bootProc, newOutBound)
|
||||||
actor
|
actor
|
||||||
|
|
||||||
proc spawn*(name: string; turn: var Turn; bootProc: TurnAction; initialAssertions = initHashSet[Handle]()): Actor {.discardable.} =
|
proc spawn*(name: string; turn: Turn; bootProc: TurnAction; initialAssertions = initHashSet[Handle]()): Actor {.discardable.} =
|
||||||
spawnActor(turn, name, bootProc, initialAssertions)
|
spawnActor(turn, name, bootProc, initialAssertions)
|
||||||
|
|
||||||
type StopOnRetract = ref object of Entity
|
type StopOnRetract = ref object of Entity
|
||||||
|
|
||||||
method retract*(e: StopOnRetract; turn: var Turn; h: Handle) =
|
method retract*(e: StopOnRetract; turn: Turn; h: Handle) =
|
||||||
stop(turn)
|
stop(turn)
|
||||||
|
|
||||||
proc halfLink(facet, other: Facet) =
|
proc halfLink(facet, other: Facet) =
|
||||||
|
@ -595,7 +642,7 @@ proc halfLink(facet, other: Facet) =
|
||||||
established: true,
|
established: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
proc linkActor*(turn: var Turn; name: string; bootProc: TurnAction; initialAssertions = initHashSet[Handle]()): Actor {.discardable.} =
|
proc linkActor*(turn: Turn; name: string; bootProc: TurnAction; initialAssertions = initHashSet[Handle]()): Actor {.discardable.} =
|
||||||
result = spawnActor(turn, name, bootProc, initialAssertions)
|
result = spawnActor(turn, name, bootProc, initialAssertions)
|
||||||
halfLink(turn.facet, result.root)
|
halfLink(turn.facet, result.root)
|
||||||
halfLink(result.root, turn.facet)
|
halfLink(result.root, turn.facet)
|
||||||
|
@ -604,7 +651,7 @@ var inertActor {.threadvar.}: Actor
|
||||||
|
|
||||||
proc newInertCap*(): Cap =
|
proc newInertCap*(): Cap =
|
||||||
if inertActor.isNil:
|
if inertActor.isNil:
|
||||||
inertActor = bootActor("inert") do (turn: var Turn): turn.stop()
|
inertActor = bootActor("inert") do (turn: Turn): turn.stop()
|
||||||
Cap(relay: inertActor.root)
|
Cap(relay: inertActor.root)
|
||||||
|
|
||||||
proc atExit*(actor; action) = actor.exitHooks.add action
|
proc atExit*(actor; action) = actor.exitHooks.add action
|
||||||
|
@ -627,7 +674,7 @@ proc terminateActor(turn; reason: ref Exception) =
|
||||||
if reason.isNil:
|
if reason.isNil:
|
||||||
terminateActor(turn, err)
|
terminateActor(turn, err)
|
||||||
return
|
return
|
||||||
proc finish(turn: var Turn) =
|
proc finish(turn: Turn) =
|
||||||
assert not actor.root.isNil, actor.name
|
assert not actor.root.isNil, actor.name
|
||||||
terminateFacet(turn)
|
terminateFacet(turn)
|
||||||
actor.root = nil
|
actor.root = nil
|
||||||
|
@ -635,14 +682,14 @@ proc terminateActor(turn; reason: ref Exception) =
|
||||||
queueTurn(actor.root, finish)
|
queueTurn(actor.root, finish)
|
||||||
|
|
||||||
proc terminateFacet*(facet; e: ref Exception) =
|
proc terminateFacet*(facet; e: ref Exception) =
|
||||||
run(facet.actor.root) do (turn: var Turn):
|
run(facet.actor.root) do (turn: Turn):
|
||||||
terminateActor(turn, e)
|
terminateActor(turn, e)
|
||||||
|
|
||||||
proc terminate*(turn: var Turn; e: ref Exception) =
|
proc terminate*(turn: Turn; e: ref Exception) =
|
||||||
terminateActor(turn, e)
|
terminateActor(turn, e)
|
||||||
|
|
||||||
proc stop*(turn: var Turn, facet: Facet) =
|
proc stop*(turn: Turn, facet: Facet) =
|
||||||
queueEffect(turn, facet) do (turn: var Turn):
|
queueEffect(turn, facet) do (turn: Turn):
|
||||||
when tracing:
|
when tracing:
|
||||||
var act = ActionDescription(orKind: ActionDescriptionKind.facetStop)
|
var act = ActionDescription(orKind: ActionDescriptionKind.facetStop)
|
||||||
act.facetstop.path = facet.path
|
act.facetstop.path = facet.path
|
||||||
|
@ -650,17 +697,20 @@ proc stop*(turn: var Turn, facet: Facet) =
|
||||||
turn.desc.actions.add act
|
turn.desc.actions.add act
|
||||||
terminateFacet(turn)
|
terminateFacet(turn)
|
||||||
|
|
||||||
proc stop*(turn: var Turn) =
|
proc stopFacet*(turn: Turn) = stop(turn, turn.facet)
|
||||||
|
## Stop the `Facet` currently active for this `Turn`.
|
||||||
|
|
||||||
|
proc stop*(turn: Turn) {.deprecated:"use stopFacet(turn)".} =
|
||||||
stop(turn, turn.facet)
|
stop(turn, turn.facet)
|
||||||
|
|
||||||
proc stop*(facet: Facet) =
|
proc stop*(facet: Facet) =
|
||||||
run(facet, stop)
|
run(facet, stop)
|
||||||
|
|
||||||
proc onStop*(facet: Facet; act: TurnAction) =
|
proc onStop*(facet: Facet; act: TurnAction) =
|
||||||
## Add a `proc (turn: var Turn)` action to `facet` to be called as it stops.
|
## Add a `proc (turn: Turn)` action to `facet` to be called as it stops.
|
||||||
add(facet.shutdownActions, act)
|
add(facet.shutdownActions, act)
|
||||||
|
|
||||||
proc onStop*(turn: var Turn; act: TurnAction) =
|
proc onStop*(turn: Turn; act: TurnAction) =
|
||||||
onStop(turn.facet, act)
|
onStop(turn.facet, act)
|
||||||
|
|
||||||
proc isAlive(actor): bool =
|
proc isAlive(actor): bool =
|
||||||
|
@ -668,7 +718,7 @@ proc isAlive(actor): bool =
|
||||||
|
|
||||||
proc stop*(actor: Actor) =
|
proc stop*(actor: Actor) =
|
||||||
if actor.isAlive:
|
if actor.isAlive:
|
||||||
queueTurn(actor.root) do (turn: var Turn):
|
queueTurn(actor.root) do (turn: Turn):
|
||||||
assert(not turn.facet.isNil)
|
assert(not turn.facet.isNil)
|
||||||
when tracing:
|
when tracing:
|
||||||
var act = ActionDescription(orKind: ActionDescriptionKind.facetStop)
|
var act = ActionDescription(orKind: ActionDescriptionKind.facetStop)
|
||||||
|
@ -680,10 +730,10 @@ proc stop*(actor: Actor) =
|
||||||
proc stopActor*(facet: Facet) =
|
proc stopActor*(facet: Facet) =
|
||||||
stop(facet.actor)
|
stop(facet.actor)
|
||||||
|
|
||||||
proc stopActor*(turn: var Turn) =
|
proc stopActor*(turn: Turn) =
|
||||||
stop(turn, turn.facet.actor.root)
|
stop(turn, turn.facet.actor.root)
|
||||||
|
|
||||||
proc freshen*(turn: var Turn, act: TurnAction) {.deprecated.} =
|
proc freshen*(turn: Turn, act: TurnAction) {.deprecated.} =
|
||||||
run(turn.facet, act)
|
run(turn.facet, act)
|
||||||
|
|
||||||
proc newCap*(relay: Facet; entity: Entity): Cap =
|
proc newCap*(relay: Facet; entity: Entity): Cap =
|
||||||
|
@ -700,10 +750,10 @@ proc newCap*(e: Entity; turn): Cap =
|
||||||
type SyncContinuation {.final.} = ref object of Entity
|
type SyncContinuation {.final.} = ref object of Entity
|
||||||
action: TurnAction
|
action: TurnAction
|
||||||
|
|
||||||
method message(entity: SyncContinuation; turn: var Turn; v: AssertionRef) =
|
method message(entity: SyncContinuation; turn: Turn; v: AssertionRef) =
|
||||||
entity.action(turn)
|
entity.action(turn)
|
||||||
|
|
||||||
proc sync*(turn: var Turn; refer: Cap; act: TurnAction) =
|
proc sync*(turn: Turn; refer: Cap; act: TurnAction) =
|
||||||
sync(turn, refer, newCap(turn, SyncContinuation(action: act)))
|
sync(turn, refer, newCap(turn, SyncContinuation(action: act)))
|
||||||
|
|
||||||
proc running*(actor): bool =
|
proc running*(actor): bool =
|
||||||
|
@ -711,7 +761,9 @@ proc running*(actor): bool =
|
||||||
if not (result or actor.exitReason.isNil):
|
if not (result or actor.exitReason.isNil):
|
||||||
raise actor.exitReason
|
raise actor.exitReason
|
||||||
|
|
||||||
proc run(turn: var Turn) =
|
proc facet*(actor): Facet = actor.root
|
||||||
|
|
||||||
|
proc run(turn: Turn) =
|
||||||
while turn.work.len > 0:
|
while turn.work.len > 0:
|
||||||
var (facet, act) = turn.work.popFirst()
|
var (facet, act) = turn.work.popFirst()
|
||||||
assert not act.isNil
|
assert not act.isNil
|
||||||
|
@ -736,8 +788,24 @@ proc runPendingTurns* =
|
||||||
terminateActor(turn, err)
|
terminateActor(turn, err)
|
||||||
raise err
|
raise err
|
||||||
|
|
||||||
|
proc runOnce*(timeout = none(Duration)): bool {.discardable.} =
|
||||||
|
## Run pending turns if there are any, otherwise
|
||||||
|
## poll for external events and run any resulting turns.
|
||||||
|
## Return true if any turns have been processed.
|
||||||
|
if turnQueue.len == 0:
|
||||||
|
when defined(solo5):
|
||||||
|
discard solo5_dispatcher.runOnce(timeout)
|
||||||
|
else:
|
||||||
|
var ready: seq[Continuation]
|
||||||
|
ioqueue.poll(ready, timeout)
|
||||||
|
while ready.len > 0:
|
||||||
|
discard trampoline:
|
||||||
|
ready.pop()
|
||||||
|
result = turnQueue.len > 0
|
||||||
|
runPendingTurns()
|
||||||
|
|
||||||
proc run* =
|
proc run* =
|
||||||
## Run actors to completion
|
## Run actors to completion.
|
||||||
when defined(solo5):
|
when defined(solo5):
|
||||||
while turnQueue.len > 0 or solo5_dispatcher.runOnce():
|
while turnQueue.len > 0 or solo5_dispatcher.runOnce():
|
||||||
runPendingTurns()
|
runPendingTurns()
|
||||||
|
@ -745,7 +813,8 @@ proc run* =
|
||||||
var ready: seq[Continuation]
|
var ready: seq[Continuation]
|
||||||
while true:
|
while true:
|
||||||
runPendingTurns()
|
runPendingTurns()
|
||||||
ioqueue.poll(ready)
|
try: ioqueue.poll(ready)
|
||||||
|
except OSError: discard
|
||||||
if ready.len == 0: break
|
if ready.len == 0: break
|
||||||
while ready.len > 0:
|
while ready.len > 0:
|
||||||
discard trampoline:
|
discard trampoline:
|
||||||
|
@ -756,6 +825,9 @@ proc runActor*(name: string; bootProc: TurnAction) =
|
||||||
let actor = bootActor(name, bootProc)
|
let actor = bootActor(name, bootProc)
|
||||||
if not actor.exitReason.isNil:
|
if not actor.exitReason.isNil:
|
||||||
raise actor.exitReason
|
raise actor.exitReason
|
||||||
|
actor.atExit do (turn: Turn):
|
||||||
|
rootActors.excl actor
|
||||||
|
rootActors.incl actor
|
||||||
when defined(solo5):
|
when defined(solo5):
|
||||||
runPendingTurns()
|
runPendingTurns()
|
||||||
while (actor.isAlive and solo5_dispatcher.runOnce()) or turnQueue.len > 0:
|
while (actor.isAlive and solo5_dispatcher.runOnce()) or turnQueue.len > 0:
|
||||||
|
@ -765,6 +837,22 @@ proc runActor*(name: string; bootProc: TurnAction) =
|
||||||
if not actor.exitReason.isNil:
|
if not actor.exitReason.isNil:
|
||||||
raise actor.exitReason
|
raise actor.exitReason
|
||||||
|
|
||||||
|
when defined(posix):
|
||||||
|
proc stopActorsHook() {.noconv.} =
|
||||||
|
stderr.writeLine " stopping actors "
|
||||||
|
if rootActors.len == 0: quit(-1) # Ctrl-C double-tap, scram
|
||||||
|
let act: TurnAction = stopActor
|
||||||
|
for actor in rootActors:
|
||||||
|
let turn = TurnRef(facet: actor.root)
|
||||||
|
turn.work.addLast((actor.root, act,))
|
||||||
|
when tracing:
|
||||||
|
turn.desc.id = nextTurnId()
|
||||||
|
turnQueue.addLast(turn)
|
||||||
|
rootActors.clear()
|
||||||
|
# dump all actors
|
||||||
|
|
||||||
|
setControlCHook(stopActorsHook)
|
||||||
|
|
||||||
type FacetGuard* = object
|
type FacetGuard* = object
|
||||||
facet: Facet
|
facet: Facet
|
||||||
|
|
||||||
|
|
|
@ -6,17 +6,21 @@ runnableExamples:
|
||||||
let sturdy = mint()
|
let sturdy = mint()
|
||||||
check $sturdy == """<ref {oid: "syndicate" sig: #x"69ca300c1dbfa08fba692102dd82311a"}>"""
|
check $sturdy == """<ref {oid: "syndicate" sig: #x"69ca300c1dbfa08fba692102dd82311a"}>"""
|
||||||
|
|
||||||
import std/[options, tables]
|
import
|
||||||
from std/sequtils import toSeq
|
std/[options, tables],
|
||||||
import hashlib/misc/blake2
|
pkg/nimcrypto/[blake2, hmac],
|
||||||
|
pkg/preserves,
|
||||||
import preserves
|
./protocols/sturdy
|
||||||
import ./protocols/sturdy
|
|
||||||
|
|
||||||
export `$`
|
export `$`
|
||||||
|
|
||||||
proc hmac(key, data: openarray[byte]): seq[byte] =
|
proc hmac(key, data: openarray[byte]): seq[byte] =
|
||||||
count[Hmac[BLAKE2S_256]](key, data).data[0..15].toSeq
|
result = newSeq[byte](32)
|
||||||
|
var ctx: HMAC[blake2_256]
|
||||||
|
ctx.init key
|
||||||
|
ctx.update data
|
||||||
|
discard ctx.finish result
|
||||||
|
result.setLen 16
|
||||||
|
|
||||||
proc mint*(key: openarray[byte]; oid: Value): SturdyRef =
|
proc mint*(key: openarray[byte]; oid: Value): SturdyRef =
|
||||||
result.parameters.oid = oid
|
result.parameters.oid = oid
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
# SPDX-FileCopyrightText: 2021 ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import preserves
|
import
|
||||||
import options, sets, tables
|
std/[options, sets, tables],
|
||||||
|
pkg/preserves
|
||||||
|
|
||||||
type
|
type
|
||||||
Set = HashSet
|
Set = HashSet
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
# SPDX-FileCopyrightText: ☭ 2022 Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[hashes, options, tables]
|
import
|
||||||
import preserves
|
std/[hashes, options, tables],
|
||||||
import ./actors, ./protocols/dataspace, ./skeletons
|
pkg/preserves,
|
||||||
|
./actors, ./protocols/dataspace, ./skeletons
|
||||||
|
|
||||||
from ./protocols/protocol import Handle
|
from ./protocols/protocol import Handle
|
||||||
|
|
||||||
|
@ -16,14 +17,14 @@ type
|
||||||
index: Index
|
index: Index
|
||||||
handleMap: Table[Handle, Assertion]
|
handleMap: Table[Handle, Assertion]
|
||||||
|
|
||||||
method publish(ds: Dataspace; turn: var Turn; a: AssertionRef; h: Handle) =
|
method publish(ds: Dataspace; turn: Turn; a: AssertionRef; h: Handle) =
|
||||||
if add(ds.index, turn, a.value):
|
if add(ds.index, turn, a.value):
|
||||||
var obs = a.value.preservesTo(Observe)
|
var obs = a.value.preservesTo(Observe)
|
||||||
if obs.isSome and obs.get.observer of Cap:
|
if obs.isSome and obs.get.observer of Cap:
|
||||||
ds.index.add(turn, obs.get.pattern, Cap(obs.get.observer))
|
ds.index.add(turn, obs.get.pattern, Cap(obs.get.observer))
|
||||||
ds.handleMap[h] = a.value
|
ds.handleMap[h] = a.value
|
||||||
|
|
||||||
method retract(ds: Dataspace; turn: var Turn; h: Handle) =
|
method retract(ds: Dataspace; turn: Turn; h: Handle) =
|
||||||
let v = ds.handleMap[h]
|
let v = ds.handleMap[h]
|
||||||
if remove(ds.index, turn, v):
|
if remove(ds.index, turn, v):
|
||||||
ds.handleMap.del h
|
ds.handleMap.del h
|
||||||
|
@ -31,20 +32,20 @@ method retract(ds: Dataspace; turn: var Turn; h: Handle) =
|
||||||
if obs.isSome and obs.get.observer of Cap:
|
if obs.isSome and obs.get.observer of Cap:
|
||||||
ds.index.remove(turn, obs.get.pattern, Cap(obs.get.observer))
|
ds.index.remove(turn, obs.get.pattern, Cap(obs.get.observer))
|
||||||
|
|
||||||
method message(ds: Dataspace; turn: var Turn; a: AssertionRef) =
|
method message(ds: Dataspace; turn: Turn; a: AssertionRef) =
|
||||||
ds.index.deliverMessage(turn, a.value)
|
ds.index.deliverMessage(turn, a.value)
|
||||||
|
|
||||||
proc newDataspace*(turn: var Turn): Cap =
|
proc newDataspace*(turn: Turn): Cap =
|
||||||
newCap(turn, Dataspace(index: initIndex()))
|
newCap(turn, Dataspace(index: initIndex()))
|
||||||
|
|
||||||
type BootProc = proc (turn: var Turn; ds: Cap) {.closure.}
|
type BootProc = proc (turn: Turn; ds: Cap) {.closure.}
|
||||||
type DeprecatedBootProc = proc (ds: Cap; turn: var Turn) {.closure.}
|
type DeprecatedBootProc = proc (ds: Cap; turn: Turn) {.closure.}
|
||||||
|
|
||||||
proc bootDataspace*(name: string; bootProc: BootProc): Actor =
|
proc bootDataspace*(name: string; bootProc: BootProc): Actor =
|
||||||
bootActor(name) do (turn: var Turn):
|
bootActor(name) do (turn: Turn):
|
||||||
turn.preventInertCheck()
|
turn.preventInertCheck()
|
||||||
bootProc(turn, newDataspace(turn))
|
bootProc(turn, newDataspace(turn))
|
||||||
|
|
||||||
proc bootDataspace*(name: string; bootProc: DeprecatedBootProc): Actor {.deprecated.} =
|
proc bootDataspace*(name: string; bootProc: DeprecatedBootProc): Actor {.deprecated.} =
|
||||||
bootDataspace(name) do (turn: var Turn, ds: Cap):
|
bootDataspace(name) do (turn: Turn, ds: Cap):
|
||||||
bootProc(ds, turn)
|
bootProc(ds, turn)
|
||||||
|
|
|
@ -0,0 +1,398 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import
|
||||||
|
std/[httpcore, options, parseutils, sets, streams, strutils, tables, times, uri],
|
||||||
|
pkg/taps,
|
||||||
|
pkg/preserves,
|
||||||
|
../../syndicate, ../bags, ./timers, ../protocols/http
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
type
|
||||||
|
Driver = ref object
|
||||||
|
facet: Facet
|
||||||
|
ds, timers: Cap
|
||||||
|
sequenceNumber: BiggestInt
|
||||||
|
Session = ref object
|
||||||
|
facet: Facet
|
||||||
|
driver: Driver
|
||||||
|
conn: Connection
|
||||||
|
exch: Exchange
|
||||||
|
pendingLen: int
|
||||||
|
port: Port
|
||||||
|
Exchange = ref object of Entity
|
||||||
|
cap: Cap
|
||||||
|
ses: Session
|
||||||
|
# TODO: store what we need fom Session locally.
|
||||||
|
req: HttpRequest
|
||||||
|
binding: Option[HttpBinding]
|
||||||
|
stream: StringStream
|
||||||
|
contentType: string
|
||||||
|
contentLen: int
|
||||||
|
mode: HttpResponseKind
|
||||||
|
active: bool
|
||||||
|
|
||||||
|
proc badRequest(conn: Connection; msg: string) =
|
||||||
|
conn.send(SupportedVersion & " " & 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; exch: Exchange; text: string): int =
|
||||||
|
## Parse an `HttpRequest` request out of a `text` from a `Connection`.
|
||||||
|
var
|
||||||
|
token: string
|
||||||
|
off: int
|
||||||
|
|
||||||
|
template advanceSp =
|
||||||
|
let n = skipWhile(text, SP, off)
|
||||||
|
if n < 1:
|
||||||
|
badRequest(conn, "400 invalid request")
|
||||||
|
return
|
||||||
|
inc(off, n)
|
||||||
|
|
||||||
|
# method
|
||||||
|
off.inc parseUntil(text, token, SP, off)
|
||||||
|
exch.req.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, "400 version not supported")
|
||||||
|
return
|
||||||
|
|
||||||
|
exch.req.query = extractQuery(token)
|
||||||
|
|
||||||
|
if token != "":
|
||||||
|
exch.req.path = split(token, '/')
|
||||||
|
for p in exch.req.path.mitems:
|
||||||
|
# normalize the path
|
||||||
|
for i, c in p:
|
||||||
|
if c in {'A'..'Z'}:
|
||||||
|
p[i] = char c.ord + 0x20
|
||||||
|
|
||||||
|
exch.req.host = RequestHost(orKind: RequestHostKind.absent)
|
||||||
|
|
||||||
|
template advanceLine =
|
||||||
|
inc off, skipWhile(text, {'\x0d'}, off)
|
||||||
|
if text.high < off or text[off] != '\x0a':
|
||||||
|
badRequest(conn, "400 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
|
||||||
|
v = exch.req.headers.getOrDefault(cast[Symbol](k))
|
||||||
|
for e in vals.mitems:
|
||||||
|
var e = e.move.toLowerAscii
|
||||||
|
case k
|
||||||
|
of "host":
|
||||||
|
exch.req.host = RequestHost(orKind: RequestHostKind.`present`, present: v)
|
||||||
|
of "content-length":
|
||||||
|
discard parseInt(e, exch.contentLen)
|
||||||
|
if exch.contentLen > (1 shl 23):
|
||||||
|
badRequest(conn, "413 Content Too Large")
|
||||||
|
if exch.contentLen > 0:
|
||||||
|
exch.req.body = Value(
|
||||||
|
kind: pkByteString, bytes: newSeqOfCap[byte](exch.contentLen))
|
||||||
|
of "content-type":
|
||||||
|
exch.contentType = e
|
||||||
|
if v == "":
|
||||||
|
v = e.toLowerAscii
|
||||||
|
else:
|
||||||
|
v.add ", "
|
||||||
|
v.add e.toLowerAscii
|
||||||
|
exch.req.headers[cast[Symbol](k)] = v
|
||||||
|
advanceLine()
|
||||||
|
result = 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
|
||||||
|
|
||||||
|
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 isTrue(v: Value): bool = v.kind == pkBoolean and v.bool
|
||||||
|
|
||||||
|
proc dispatch(exch: Exchange; turn: Turn; res: HttpResponse) =
|
||||||
|
case res.orKind
|
||||||
|
of HttpResponseKind.status:
|
||||||
|
if exch.mode == res.orKind:
|
||||||
|
exch.active = true
|
||||||
|
exch.ses.conn.startBatch()
|
||||||
|
exch.stream.write(
|
||||||
|
SupportedVersion, " ", res.status.code, " ", res.status.message, CRLF &
|
||||||
|
# "connection: close" & CRLF &
|
||||||
|
# RFC9112 says we SHOULD support persistent connections.
|
||||||
|
"date: ", now().format(IMF), CRLF
|
||||||
|
# add Date header automatically - RFC 9110 Section 6.6.1.
|
||||||
|
)
|
||||||
|
exch.mode = HttpResponseKind.header
|
||||||
|
|
||||||
|
of HttpResponseKind.header:
|
||||||
|
if exch.mode == res.orKind:
|
||||||
|
exch.stream.write(res.header.name, ": ", res.header.value, CRLF)
|
||||||
|
|
||||||
|
of HttpResponseKind.chunk:
|
||||||
|
if res.chunk.chunk.len > 0:
|
||||||
|
if exch.mode == HttpResponseKind.header:
|
||||||
|
exch.stream.write("transfer-encoding: chunked" & CRLF & CRLF)
|
||||||
|
exch.ses.send(move exch.stream.data)
|
||||||
|
exch.mode = res.orKind
|
||||||
|
if exch.mode == res.orKind:
|
||||||
|
exch.ses.send(res.chunk.chunk.lenLine)
|
||||||
|
exch.ses.send(res.chunk.chunk)
|
||||||
|
exch.ses.send(CRLF)
|
||||||
|
|
||||||
|
of HttpResponseKind.done:
|
||||||
|
if exch.mode in {HttpResponseKind.header, HttpResponseKind.chunk}:
|
||||||
|
if exch.mode == HttpResponseKind.header:
|
||||||
|
exch.stream.write("content-length: ", $res.done.chunk.len & CRLF & CRLF)
|
||||||
|
exch.ses.send(move exch.stream.data)
|
||||||
|
if res.done.chunk.len > 0:
|
||||||
|
exch.ses.send(res.done.chunk)
|
||||||
|
elif exch.mode == HttpResponseKind.chunk:
|
||||||
|
exch.ses.send(res.done.chunk.lenLine)
|
||||||
|
if res.done.chunk.len > 0:
|
||||||
|
exch.ses.send(res.done.chunk)
|
||||||
|
exch.ses.send(CRLF & "0" & CRLF & CRLF)
|
||||||
|
exch.mode = res.orKind
|
||||||
|
exch.ses.conn.endBatch()
|
||||||
|
if exch.req.headers.getOrDefault(Symbol"connection") == "close":
|
||||||
|
exch.ses.conn.close()
|
||||||
|
stop(turn)
|
||||||
|
# stop the facet scoped to the exchange
|
||||||
|
# so that the response capability is withdrawn
|
||||||
|
|
||||||
|
proc scheduleTimeout(exch: Exchange; turn: Turn) =
|
||||||
|
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"
|
||||||
|
exch.dispatch(turn, res)
|
||||||
|
res = HttpResponse(orKind: HttpResponseKind.done)
|
||||||
|
exch.dispatch(turn, res)
|
||||||
|
# Use a HttpResponse to reuse logic.
|
||||||
|
|
||||||
|
method message(exch: Exchange; turn: Turn; a: AssertionRef) =
|
||||||
|
# Send responses back into a connection.
|
||||||
|
if a.value.isTrue:
|
||||||
|
# Sync message from dataspace.
|
||||||
|
exch.scheduleTimeout(turn)
|
||||||
|
if exch.binding.isSome:
|
||||||
|
# The best binding in the dataspace has been selected.
|
||||||
|
let handler = exch.binding.get.handler.unembed(Cap)
|
||||||
|
if handler.isSome:
|
||||||
|
# Send the handler the exchange capability.
|
||||||
|
if exch.contentType.startsWith "application/json":
|
||||||
|
if exch.req.body.isByteString:
|
||||||
|
var bodyBytes = exch.req.body.bytes.move
|
||||||
|
exch.req.body = cast[string](bodyBytes).parsePreserves
|
||||||
|
discard publish(turn, handler.get, HttpContext(
|
||||||
|
req: exch.req,
|
||||||
|
res: exch.cap.embed,
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
exch.binding.reset()
|
||||||
|
else:
|
||||||
|
var res: HttpResponse
|
||||||
|
if exch.mode != HttpResponseKind.done and res.fromPreserves a.value:
|
||||||
|
exch.dispatch(turn, res)
|
||||||
|
|
||||||
|
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 service(turn: Turn; exch: Exchange) =
|
||||||
|
## Service an HTTP message exchange.
|
||||||
|
let pat = grab(HttpBinding?:{
|
||||||
|
0: drop(), # match all hosts
|
||||||
|
1: ?exch.req.port,
|
||||||
|
2: drop(), # match all methods
|
||||||
|
3: drop(), # match all paths
|
||||||
|
4: drop(),
|
||||||
|
})
|
||||||
|
onPublish(turn, exch.ses.driver.ds, pat) do (b: HttpBinding):
|
||||||
|
if b.match exch.req:
|
||||||
|
if exch.binding.isNone or b.strongerThan exch.binding.get:
|
||||||
|
exch.binding = some b
|
||||||
|
# found a better binding
|
||||||
|
exch.cap = turn.newCap(exch)
|
||||||
|
sync(turn, exch.ses.driver.ds, exch.cap)
|
||||||
|
# When the sync returns we have seen all the bindings.
|
||||||
|
|
||||||
|
proc exchange(ses: Session) =
|
||||||
|
## Detach the current Exchange from the session
|
||||||
|
## and pass it into Syndicate.
|
||||||
|
ses.pendingLen = 0
|
||||||
|
var exch = move ses.exch
|
||||||
|
exch.ses = ses
|
||||||
|
ses.facet.run do (turn: Turn):
|
||||||
|
inFacet(turn) do (turn: Turn):
|
||||||
|
# start a new facet for this message exchange
|
||||||
|
preventInertCheck(turn)
|
||||||
|
exch.facet = turn.facet
|
||||||
|
exch.stream = newStringStream()
|
||||||
|
exch.mode = HttpResponseKind.status
|
||||||
|
turn.service(exch)
|
||||||
|
|
||||||
|
proc service(ses: Session) =
|
||||||
|
## Service a connection to an HTTP client.
|
||||||
|
const oneMiB = 1 shl 20
|
||||||
|
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):
|
||||||
|
if ses.pendingLen == 0:
|
||||||
|
if ses.exch.isNil:
|
||||||
|
new ses.exch
|
||||||
|
let off = parseRequest(ses.conn, ses.exch, cast[string](data))
|
||||||
|
if off > 0:
|
||||||
|
assert not ses.exch.isNil
|
||||||
|
inc(ses.driver.sequenceNumber)
|
||||||
|
ses.exch.req.sequenceNumber = ses.driver.sequenceNumber
|
||||||
|
ses.exch.req.port = BiggestInt ses.port
|
||||||
|
ses.pendingLen = ses.exch.contentLen
|
||||||
|
if off < data.len:
|
||||||
|
let n = min(data.len - off, ses.pendingLen)
|
||||||
|
ses.exch.req.body.bytes.add data[off .. off+n.pred]
|
||||||
|
ses.pendingLen.dec n
|
||||||
|
else:
|
||||||
|
let n = min(data.len, ses.pendingLen)
|
||||||
|
ses.exch.req.body.bytes.add data[0..n.pred]
|
||||||
|
ses.pendingLen.dec n
|
||||||
|
assert ses.pendingLen >= 0, $ses.pendingLen
|
||||||
|
if ses.pendingLen == 0:
|
||||||
|
ses.exchange()
|
||||||
|
ses.conn.receive(maxLength = oneMiB)
|
||||||
|
else:
|
||||||
|
ses.conn.receive(maxLength=ses.pendingLen)
|
||||||
|
ses.conn.receive(maxLength =oneMiB)
|
||||||
|
|
||||||
|
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
|
||||||
|
echo "listening for HTTP on port ", port
|
||||||
|
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, driver.ds, HttpBinding?:{ 1: grab() }) do (port: BiggestInt):
|
||||||
|
publish(turn, ds, HttpListener(port: port))
|
||||||
|
|
||||||
|
# TODO: publish <http-service …>.
|
||||||
|
|
||||||
|
during(turn, driver.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,9 +1,10 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/times
|
import
|
||||||
import preserves
|
std/[options, tables, times],
|
||||||
import ../../syndicate, ../bags, ../protocols/[timer, dataspace]
|
pkg/preserves,
|
||||||
|
../../syndicate, ../protocols/timer
|
||||||
|
|
||||||
when defined(solo5):
|
when defined(solo5):
|
||||||
import solo5_dispatcher
|
import solo5_dispatcher
|
||||||
|
@ -12,9 +13,6 @@ else:
|
||||||
|
|
||||||
export timer
|
export timer
|
||||||
|
|
||||||
type
|
|
||||||
Observe = dataspace.Observe
|
|
||||||
|
|
||||||
when defined(solo5):
|
when defined(solo5):
|
||||||
import solo5, solo5_dispatcher
|
import solo5, solo5_dispatcher
|
||||||
|
|
||||||
|
@ -27,7 +25,7 @@ when defined(solo5):
|
||||||
## Owning facet of driver.
|
## Owning facet of driver.
|
||||||
target: Cap
|
target: Cap
|
||||||
## Destination for LaterThan assertions.
|
## Destination for LaterThan assertions.
|
||||||
deadlines: Bag[float]
|
deadlines: Table[float, Facet]
|
||||||
## Deadlines that other actors are observing.
|
## Deadlines that other actors are observing.
|
||||||
|
|
||||||
proc spawnTimerDriver(facet: Facet; cap: Cap): TimerDriver =
|
proc spawnTimerDriver(facet: Facet; cap: Cap): TimerDriver =
|
||||||
|
@ -35,11 +33,12 @@ when defined(solo5):
|
||||||
|
|
||||||
proc await(driver: TimerDriver; deadline: float) {.solo5dispatch.} =
|
proc await(driver: TimerDriver; deadline: float) {.solo5dispatch.} =
|
||||||
yieldUntil(deadline)
|
yieldUntil(deadline)
|
||||||
if deadline in driver.deadlines:
|
let facet = driver.deadlines.getOrDefault(deadline)
|
||||||
|
if not facet.isNil:
|
||||||
# check if the deadline is still observed
|
# check if the deadline is still observed
|
||||||
proc turnWork(turn: var Turn) =
|
proc turnWork(turn: Turn) =
|
||||||
discard publish(turn, driver.target, LaterThan(seconds: deadline))
|
discard publish(turn, driver.target, LaterThan(seconds: deadline))
|
||||||
run(driver.facet, turnWork)
|
run(facet, turnWork)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
import std/[oserrors, posix, sets]
|
import std/[oserrors, posix, sets]
|
||||||
|
@ -50,21 +49,12 @@ else:
|
||||||
proc timerfd_create(clock_id: ClockId, flags: cint): cint {.timerfd.}
|
proc timerfd_create(clock_id: ClockId, flags: cint): cint {.timerfd.}
|
||||||
proc timerfd_settime(ufd: cint, flags: cint,
|
proc timerfd_settime(ufd: cint, flags: cint,
|
||||||
utmr: var Itimerspec, otmr: var Itimerspec): cint {.timerfd.}
|
utmr: var Itimerspec, otmr: var Itimerspec): cint {.timerfd.}
|
||||||
proc timerfd_gettime(ufd: cint, curr: var Itimerspec): cint {.timerfd.}
|
|
||||||
|
|
||||||
var
|
var
|
||||||
TFD_NONBLOCK {.timerfd.}: cint
|
TFD_NONBLOCK {.timerfd.}: cint
|
||||||
TFD_CLOEXEC {.timerfd.}: cint
|
TFD_CLOEXEC {.timerfd.}: cint
|
||||||
TFD_TIMER_ABSTIME {.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 =
|
func toFloat(ts: Timespec): float =
|
||||||
ts.tv_sec.float + ts.tv_nsec.float / 1_000_000_000
|
ts.tv_sec.float + ts.tv_nsec.float / 1_000_000_000
|
||||||
|
|
||||||
|
@ -84,26 +74,19 @@ else:
|
||||||
## Owning facet of driver.
|
## Owning facet of driver.
|
||||||
target: Cap
|
target: Cap
|
||||||
## Destination for LaterThan assertions.
|
## Destination for LaterThan assertions.
|
||||||
deadlines: Bag[float]
|
deadlines: Table[float, Facet]
|
||||||
## Deadlines that other actors are observing.
|
## Deadlines that other actors are observing.
|
||||||
timers: HashSet[cint]
|
timers: HashSet[cint]
|
||||||
# TODO: use a single timer descriptor
|
# TODO: use a single timer descriptor
|
||||||
|
|
||||||
proc spawnTimerDriver(facet: Facet; cap: Cap): TimerDriver =
|
proc spawnTimerDriver(facet: Facet; cap: Cap): TimerDriver =
|
||||||
let driver = TimerDriver(facet: facet, target: cap)
|
let driver = TimerDriver(facet: facet, target: cap)
|
||||||
facet.onStop do (turn: var Turn):
|
facet.onStop do (turn: Turn):
|
||||||
for fd in driver.timers:
|
for fd in driver.timers:
|
||||||
unregister(FD fd)
|
unregister(FD fd)
|
||||||
discard close(fd)
|
discard close(fd)
|
||||||
driver
|
driver
|
||||||
|
|
||||||
proc earliestFloat(driver: TimerDriver): float =
|
|
||||||
assert driver.deadlines.len > 0
|
|
||||||
result = high float
|
|
||||||
for deadline in driver.deadlines:
|
|
||||||
if deadline < result:
|
|
||||||
result = deadline
|
|
||||||
|
|
||||||
proc await(driver: TimerDriver; deadline: float) {.asyncio.} =
|
proc await(driver: TimerDriver; deadline: float) {.asyncio.} =
|
||||||
## Run timer driver concurrently with actor.
|
## Run timer driver concurrently with actor.
|
||||||
let fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK or TFD_CLOEXEC)
|
let fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK or TFD_CLOEXEC)
|
||||||
|
@ -119,29 +102,76 @@ else:
|
||||||
# Check if the timer is expired which
|
# Check if the timer is expired which
|
||||||
# could happen before waiting.
|
# could happen before waiting.
|
||||||
wait(FD fd, Read)
|
wait(FD fd, Read)
|
||||||
if deadline in driver.deadlines:
|
let facet = driver.deadlines.getOrDefault(deadline)
|
||||||
|
if not facet.isNil:
|
||||||
# Check if the deadline is still observed.
|
# Check if the deadline is still observed.
|
||||||
proc turnWork(turn: var Turn) =
|
proc turnWork(turn: Turn) =
|
||||||
discard publish(turn, driver.target, LaterThan(seconds: deadline))
|
discard publish(turn, driver.target, LaterThan(seconds: deadline))
|
||||||
run(driver.facet, turnWork)
|
run(facet, turnWork)
|
||||||
discard close(fd)
|
discard close(fd)
|
||||||
driver.timers.excl(fd)
|
driver.timers.excl(fd)
|
||||||
|
|
||||||
proc spawnTimerActor*(turn: var Turn; ds: Cap): Actor {.discardable.} =
|
proc runTimer(driver: TimerDriver; peer: Cap; label: Value; start, deadline: float) {.asyncio.} =
|
||||||
|
## Run timer driver concurrently with actor.
|
||||||
|
assert start < deadline
|
||||||
|
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)
|
||||||
|
var now = wallFloat()
|
||||||
|
while now < deadline:
|
||||||
|
# Check if the timer is expired which
|
||||||
|
# could happen before waiting.
|
||||||
|
wait(FD fd, Read)
|
||||||
|
now = wallFloat()
|
||||||
|
let facet = driver.deadlines.getOrDefault(deadline)
|
||||||
|
if not facet.isNil:
|
||||||
|
# Check if the deadline is still observed.
|
||||||
|
proc turnWork(turn: Turn) =
|
||||||
|
message(turn, peer, TimerExpired(label: label, seconds: now - start))
|
||||||
|
run(facet, turnWork)
|
||||||
|
discard close(fd)
|
||||||
|
driver.timers.excl(fd)
|
||||||
|
driver.deadlines.del deadline
|
||||||
|
|
||||||
|
proc spawnTimerDriver*(turn: Turn; ds: Cap): Actor {.discardable.} =
|
||||||
## Spawn a timer actor that responds to
|
## Spawn a timer actor that responds to
|
||||||
## dataspace observations of timeouts on `ds`.
|
## dataspace observations of timeouts on `ds`.
|
||||||
linkActor(turn, "timers") do (turn: var Turn):
|
linkActor(turn, "timers") do (turn: Turn):
|
||||||
let driver = spawnTimerDriver(turn.facet, ds)
|
let driver = spawnTimerDriver(turn.facet, ds)
|
||||||
let pat = inject(grab Observe(pattern: dropType LaterThan), {0: grabLit()})
|
|
||||||
during(turn, ds, pat) do (deadline: float):
|
|
||||||
if change(driver.deadlines, deadline, +1) == cdAbsentToPresent:
|
|
||||||
discard trampoline(whelp await(driver, deadline))
|
|
||||||
do:
|
|
||||||
discard change(driver.deadlines, deadline, -1, clamp = true)
|
|
||||||
# TODO: retract assertions that are unobserved.
|
|
||||||
|
|
||||||
proc after*(turn: var Turn; ds: Cap; dur: Duration; act: TurnAction) =
|
let laterThanPat = observePattern(!LaterThan, {@[0.toPreserves]: grabLit()})
|
||||||
|
during(turn, ds, laterThanPat) do (deadline: float):
|
||||||
|
driver.deadlines[deadline] = turn.facet
|
||||||
|
discard trampoline:
|
||||||
|
whelp await(driver, deadline)
|
||||||
|
do:
|
||||||
|
driver.deadlines.del deadline
|
||||||
|
|
||||||
|
during(turn, ds, SetTimer.grabType) do (req: SetTimer):
|
||||||
|
var
|
||||||
|
now = wallFloat()
|
||||||
|
deadline = req.seconds
|
||||||
|
let peer = req.peer.unembed Cap
|
||||||
|
if peer.isSome:
|
||||||
|
if req.kind == TimerKind.relative:
|
||||||
|
deadline = deadline + now
|
||||||
|
if deadline <= now:
|
||||||
|
message(turn, peer.get, TimerExpired(label: req.label))
|
||||||
|
else:
|
||||||
|
driver.deadlines[deadline] = turn.facet
|
||||||
|
discard trampoline:
|
||||||
|
whelp driver.runTimer(peer.get, req.label, now, deadline)
|
||||||
|
do:
|
||||||
|
driver.deadlines.del deadline
|
||||||
|
|
||||||
|
proc after*(turn: Turn; ds: Cap; dur: Duration; act: TurnAction) =
|
||||||
## Execute `act` after some duration of time.
|
## Execute `act` after some duration of time.
|
||||||
var later = wallFloat() + dur.inMilliseconds.float / 1_000.0
|
var later = wallFloat() + dur.inMilliseconds.float / 1_000.0
|
||||||
onPublish(turn, ds, grab LaterThan(seconds: later)):
|
onPublish(turn, ds, ?LaterThan(seconds: later)):
|
||||||
act(turn)
|
act(turn)
|
|
@ -1,12 +1,13 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[hashes, tables]
|
import
|
||||||
import preserves
|
std/[hashes, tables],
|
||||||
import ./actors, ./patterns, ./protocols/dataspace
|
pkg/preserves,
|
||||||
|
./actors, ./patterns, ./protocols/dataspace
|
||||||
|
|
||||||
type
|
type
|
||||||
DuringProc* = proc (turn: var Turn; a: Value; h: Handle): TurnAction
|
DuringProc* = proc (turn: Turn; a: Value; h: Handle): TurnAction
|
||||||
DuringActionKind = enum null, dead, act
|
DuringActionKind = enum null, dead, act
|
||||||
DuringAction = object
|
DuringAction = object
|
||||||
case kind: DuringActionKind
|
case kind: DuringActionKind
|
||||||
|
@ -17,8 +18,8 @@ type
|
||||||
cb: DuringProc
|
cb: DuringProc
|
||||||
assertionMap: Table[Handle, DuringAction]
|
assertionMap: Table[Handle, DuringAction]
|
||||||
|
|
||||||
method publish(de: DuringEntity; turn: var Turn; a: AssertionRef; h: Handle) =
|
method publish(de: DuringEntity; turn: Turn; a: AssertionRef; h: Handle) =
|
||||||
discard inFacet(turn) do (turn: var Turn):
|
discard inFacet(turn) do (turn: Turn):
|
||||||
let action = de.cb(turn, a.value, h)
|
let action = de.cb(turn, a.value, h)
|
||||||
# assert(not action.isNil "should have put in a no-op action")
|
# assert(not action.isNil "should have put in a no-op action")
|
||||||
let g = de.assertionMap.getOrDefault h
|
let g = de.assertionMap.getOrDefault h
|
||||||
|
@ -27,11 +28,12 @@ method publish(de: DuringEntity; turn: var Turn; a: AssertionRef; h: Handle) =
|
||||||
de.assertionMap[h] = DuringAction(kind: act, action: action)
|
de.assertionMap[h] = DuringAction(kind: act, action: action)
|
||||||
of dead:
|
of dead:
|
||||||
de.assertionMap.del h
|
de.assertionMap.del h
|
||||||
action(turn)
|
if not action.isNil:
|
||||||
|
action(turn)
|
||||||
of act:
|
of act:
|
||||||
raiseAssert("during: duplicate handle in publish: " & $h)
|
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
|
let g = de.assertionMap.getOrDefault h
|
||||||
case g.kind
|
case g.kind
|
||||||
of null:
|
of null:
|
||||||
|
@ -45,5 +47,5 @@ method retract(de: DuringEntity; turn: var Turn; h: Handle) =
|
||||||
|
|
||||||
proc during*(cb: DuringProc): DuringEntity = DuringEntity(cb: cb)
|
proc during*(cb: DuringProc): DuringEntity = DuringEntity(cb: cb)
|
||||||
|
|
||||||
proc observe*(turn: var Turn; ds: Cap; pat: Pattern; e: Entity): Handle =
|
proc observe*(turn: Turn; ds: Cap; pat: Pattern; e: Entity): Handle =
|
||||||
publish(turn, ds, Observe(pattern: pat, observer: newCap(turn, e)))
|
publish(turn, ds, Observe(pattern: pat, observer: newCap(turn, e)))
|
||||||
|
|
|
@ -1,70 +1,47 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[algorithm, assertions, options, sequtils, tables, typetraits]
|
import
|
||||||
|
std/[assertions, options, sequtils, tables, typetraits],
|
||||||
|
pkg/preserves,
|
||||||
|
./protocols/[dataspacePatterns, dataspace]
|
||||||
|
|
||||||
import preserves
|
|
||||||
import ./protocols/dataspacePatterns
|
|
||||||
from ./actors import Cap
|
from ./actors import Cap
|
||||||
|
|
||||||
export dataspacePatterns.`$`, PatternKind, DCompoundKind, AnyAtomKind
|
export dataspacePatterns.`$`, AnyAtomKind, GroupTypeKind, PatternKind
|
||||||
|
|
||||||
type
|
type
|
||||||
AnyAtom = dataspacePatterns.AnyAtom
|
|
||||||
DBind = dataspacePatterns.DBind
|
|
||||||
DCompound = dataspacePatterns.DCompound
|
|
||||||
DCompoundArr = dataspacePatterns.DCompoundArr
|
|
||||||
DCompoundDict = dataspacePatterns.DCompoundDict
|
|
||||||
DCompoundRec = dataspacePatterns.DCompoundRec
|
|
||||||
DLit = dataspacePatterns.DLit
|
|
||||||
Pattern* = dataspacePatterns.Pattern
|
Pattern* = dataspacePatterns.Pattern
|
||||||
|
|
||||||
iterator orderedEntries*(dict: DCompoundDict): (Value, Pattern) =
|
proc toPattern(b: sink PatternBind): Pattern =
|
||||||
## Iterate a `DCompoundDict` in Preserves order.
|
Pattern(orKind: PatternKind.`bind`, `bind`: b)
|
||||||
## Values captured from a dictionary are represented as an
|
|
||||||
## array of values ordered by their former key, so using an
|
|
||||||
## ordered iterator is sometimes essential.
|
|
||||||
var keys = dict.entries.keys.toSeq
|
|
||||||
sort(keys, preserves.cmp)
|
|
||||||
for k in keys:
|
|
||||||
yield(k, dict.entries.getOrDefault(k))
|
|
||||||
# getOrDefault doesn't raise and we know the keys will match
|
|
||||||
|
|
||||||
proc toPattern(d: sink DBind): Pattern =
|
proc toPattern(l: sink PatternLit): Pattern =
|
||||||
Pattern(orKind: PatternKind.DBind, dbind: d)
|
Pattern(orKind: PatternKind.`lit`, lit: l)
|
||||||
|
|
||||||
proc toPattern(d: sink DLit): Pattern =
|
proc toPattern(g: sink PatternGroup): Pattern =
|
||||||
Pattern(orKind: PatternKind.DLit, dlit: d)
|
Pattern(orKind: PatternKind.`group`, group: g)
|
||||||
|
|
||||||
proc toPattern(aa: sink AnyAtom): Pattern =
|
proc toPattern(a: sink AnyAtom): Pattern =
|
||||||
DLit(value: aa).toPattern
|
PatternLit(value: a).toPattern
|
||||||
|
|
||||||
proc toPattern(d: sink DCompound): Pattern =
|
proc grab*(p: sink Pattern): Pattern =
|
||||||
Pattern(orKind: PatternKind.DCompound, dcompound: d)
|
PatternBind(pattern: p).toPattern
|
||||||
|
|
||||||
proc toPattern(d: sink DCompoundRec): Pattern =
|
proc drop*(): Pattern = Pattern(orKind: PatternKind.`discard`)
|
||||||
DCompound(orKind: DCompoundKind.rec, rec: d).toPattern
|
|
||||||
|
|
||||||
proc toPattern(d: sink DCompoundArr): Pattern =
|
|
||||||
DCompound(orKind: DCompoundKind.arr, arr: d).toPattern
|
|
||||||
|
|
||||||
proc toPattern(d: sink DCompoundDict): Pattern =
|
|
||||||
DCompound(orKind: DCompoundKind.dict, dict: d).toPattern
|
|
||||||
|
|
||||||
proc drop*(): Pattern {.inline.} = Pattern(orKind: PatternKind.DDiscard)
|
|
||||||
## Create a pattern to match any value without capture.
|
## Create a pattern to match any value without capture.
|
||||||
|
|
||||||
proc grab*(): Pattern {.inline.} = DBind(pattern: drop()).toPattern
|
proc grab*(): Pattern = drop().grab()
|
||||||
## Create a pattern to capture any value.
|
## Create a pattern to capture any value.
|
||||||
|
|
||||||
proc grab*(pr: Value): Pattern =
|
proc drop*(pr: Value): Pattern =
|
||||||
## Convert a `Preserve` value to a `Pattern`.
|
## Convert a `Preserve` value to a `Pattern`.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
from std/unittest import check
|
from std/unittest import check
|
||||||
import preserves
|
import pkg/preserves
|
||||||
check:
|
check:
|
||||||
$(grab parsePreserves"""<foo "bar" #"00" [0 1 2.0] {maybe: #t} <_>>""") ==
|
$("""<foo "bar" #"00" [0 1 2.0] {maybe: #t} <_>>""".parsePreserves.drop) ==
|
||||||
"""<rec foo [<lit "bar"> <lit #"00"> <arr [<lit 0> <lit 1> <lit 2.0>]> <dict {maybe: <lit #t>}> <_>]>"""
|
"""<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: <_>}>"""
|
||||||
|
|
||||||
case pr.kind
|
case pr.kind
|
||||||
of pkBoolean:
|
of pkBoolean:
|
||||||
|
@ -73,87 +50,110 @@ proc grab*(pr: Value): Pattern =
|
||||||
AnyAtom(orKind: AnyAtomKind.`double`, double: pr.float).toPattern
|
AnyAtom(orKind: AnyAtomKind.`double`, double: pr.float).toPattern
|
||||||
of pkRegister:
|
of pkRegister:
|
||||||
AnyAtom(orKind: AnyAtomKind.`int`, int: pr.register).toPattern
|
AnyAtom(orKind: AnyAtomKind.`int`, int: pr.register).toPattern
|
||||||
|
of pkBigInt:
|
||||||
|
raiseAssert "cannot make a pattern over a big integer"
|
||||||
of pkString:
|
of pkString:
|
||||||
AnyAtom(orKind: AnyAtomKind.`string`, string: pr.string).toPattern
|
AnyAtom(orKind: AnyAtomKind.`string`, string: pr.string).toPattern
|
||||||
of pkByteString:
|
of pkByteString:
|
||||||
AnyAtom(orKind: AnyAtomKind.`bytes`, bytes: pr.bytes).toPattern
|
AnyAtom(orKind: AnyAtomKind.`bytes`, bytes: pr.bytes).toPattern
|
||||||
of pkSymbol:
|
of pkSymbol:
|
||||||
AnyAtom(orKind: AnyAtomKind.`symbol`, symbol: pr.symbol).toPattern
|
AnyAtom(orKind: AnyAtomKind.`symbol`, symbol: pr.symbol).toPattern
|
||||||
|
|
||||||
of pkRecord:
|
of pkRecord:
|
||||||
if (pr.isRecord("_") and pr.arity == 0) or (pr.isRecord("bind") and pr.arity == 1):
|
if pr.isRecord("_", 0):
|
||||||
drop()
|
drop()
|
||||||
|
elif pr.isRecord("bind", 1):
|
||||||
|
pr.fields[0].drop
|
||||||
else:
|
else:
|
||||||
DCompoundRec(
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.rec))
|
||||||
label: pr.label,
|
group.`type`.rec.label = pr.label
|
||||||
fields: map[Value, Pattern](pr.fields, grab)).toPattern
|
var i: int
|
||||||
|
for v in pr.fields:
|
||||||
|
group.entries[toPreserves i] = drop v
|
||||||
|
inc i
|
||||||
|
group.toPattern
|
||||||
|
|
||||||
of pkSequence:
|
of pkSequence:
|
||||||
DCompoundArr(items: map(pr.sequence, grab)).toPattern
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.arr))
|
||||||
|
for i, v in pr.sequence:
|
||||||
|
group.entries[toPreserves i] = drop v
|
||||||
|
group.toPattern
|
||||||
|
|
||||||
of pkSet:
|
of pkSet:
|
||||||
raiseAssert "cannot construct a pattern over a set literal"
|
raiseAssert "cannot construct a pattern over a set literal"
|
||||||
|
|
||||||
of pkDictionary:
|
of pkDictionary:
|
||||||
var dict = DCompoundDict()
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.dict))
|
||||||
for key, val in pr.pairs: dict.entries[key] = grab val
|
for key, val in pr.pairs:
|
||||||
dict.toPattern
|
group.entries[key] = drop val
|
||||||
|
group.toPattern
|
||||||
|
|
||||||
of pkEmbedded:
|
of pkEmbedded:
|
||||||
if pr.embeddedRef.isNil: drop()
|
if pr.embeddedRef.isNil: drop()
|
||||||
else:
|
else:
|
||||||
AnyAtom(orKind: AnyAtomKind.`embedded`, embedded: pr.embeddedRef).toPattern
|
AnyAtom(orKind: AnyAtomKind.`embedded`, embedded: pr.embeddedRef).toPattern
|
||||||
else:
|
#else:
|
||||||
raise newException(ValueError, "cannot generate a pattern for unhandled Value type")
|
# raise newException(ValueError, "cannot generate a pattern for unhandled Value type")
|
||||||
|
|
||||||
proc grab*[T](x: T): Pattern =
|
proc drop*[T](x: T): Pattern =
|
||||||
## Construct a `Pattern` from value of type `T`.
|
## Construct a `Pattern` from value of type `T`.
|
||||||
|
## This proc is called `drop` because the value `x` is matched but discarded.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
from std/unittest import check
|
from std/unittest import check
|
||||||
check:
|
check:
|
||||||
$grab(true) == "<lit #t>"
|
$drop(true) == "<lit #t>"
|
||||||
$grab(3.14) == "<lit 3.14>"
|
$drop(3.14) == "<lit 3.14>"
|
||||||
$grab([0, 1, 2, 3]) == "<arr [<lit 0> <lit 1> <lit 2> <lit 3>]>"
|
$drop([0, 1, 2, 3]) == "<group <arr> {0: <lit 0> 1: <lit 1> 2: <lit 2> 3: <lit 3>}>"
|
||||||
grab(x.toPreserves)
|
drop(x.toPreserves)
|
||||||
|
|
||||||
proc grabType*(typ: static typedesc): Pattern =
|
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`.
|
## Derive a `Pattern` from type `typ`.
|
||||||
## This works for `tuple` and `object` types but in the
|
## This works for `tuple` and `object` types but in the
|
||||||
## general case will return a wildcard binding.
|
## general case will return a wildcard binding.
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
import preserves
|
import pkg/preserves
|
||||||
from std/unittest import check
|
from std/unittest import check
|
||||||
check:
|
check:
|
||||||
$grabType(array[3, int]) ==
|
$grabTypeFlat(array[3, int]) ==
|
||||||
"""<arr [<bind <_>> <bind <_>> <bind <_>>]>"""
|
"""<group <arr> {0: <bind <_>> 1: <bind <_>> 2: <bind <_>> 3: <bind <_>>}>"""
|
||||||
type
|
type
|
||||||
Point = tuple[x: int; y: int]
|
Point = tuple[x: int; y: int]
|
||||||
Rect {.preservesRecord: "rect".} = tuple[a: Point; B: Point]
|
Rect {.preservesRecord: "rect".} = tuple[a: Point; B: Point]
|
||||||
ColoredRect {.preservesDictionary.} = tuple[color: string; rect: Rect]
|
ColoredRect {.preservesDictionary.} = tuple[color: string; rect: Rect]
|
||||||
check:
|
check:
|
||||||
$(grabType Point) ==
|
$(grabTypeFlat Point) ==
|
||||||
"<arr [<bind <_>> <bind <_>>]>"
|
"<group <arr> {0: <bind <_>> 1: <bind <_>>}>"
|
||||||
$(grabType Rect) ==
|
$(grabTypeFlat Rect) ==
|
||||||
"<rec rect [<arr [<bind <_>> <bind <_>>]> <arr [<bind <_>> <bind <_>>]>]>"
|
"<group <rec rect> {0: <group <arr> {0: <bind <_>> 1: <bind <_>>}> 1: <group <arr> {0: <bind <_>> 1: <bind <_>>}>}>"
|
||||||
$(grabType ColoredRect) ==
|
$(grabTypeFlat ColoredRect) ==
|
||||||
"<dict {color: <bind <_>> rect: <rec rect [<arr [<bind <_>> <bind <_>>]> <arr [<bind <_>> <bind <_>>]>]>}>"
|
"<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:
|
when typ is ref:
|
||||||
grabType(pointerBase(typ))
|
grabTypeFlat(pointerBase(typ))
|
||||||
elif typ.hasPreservesRecordPragma:
|
elif typ.hasPreservesRecordPragma:
|
||||||
var rec = DCompoundRec(label: typ.recordLabel.toSymbol)
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||||
|
group.`type`.rec.label = typ.recordLabel.toSymbol
|
||||||
for _, f in fieldPairs(default typ):
|
for _, f in fieldPairs(default typ):
|
||||||
add(rec.fields, grabType(typeof f))
|
group.entries[group.entries.len.toPreserves] = grabTypeFlat(typeof f)
|
||||||
result = rec.toPattern
|
group.toPattern
|
||||||
elif typ.hasPreservesDictionaryPragma:
|
elif typ.hasPreservesDictionaryPragma:
|
||||||
var dict = DCompoundDict()
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||||
for key, val in fieldPairs(default typ):
|
for key, val in fieldPairs(default typ):
|
||||||
dict.entries[key.toSymbol] = grabType(typeof val)
|
group.entries[key.toSymbol] = grabTypeFlat(typeof val)
|
||||||
dict.toPattern
|
group.toPattern
|
||||||
elif typ is tuple:
|
elif typ is tuple:
|
||||||
var arr = DCompoundArr()
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||||
for _, f in fieldPairs(default typ):
|
for _, f in fieldPairs(default typ):
|
||||||
add(arr.items, grabType(typeof f))
|
group.entries[group.entries.len.toPreserves] = grabTypeFlat(typeof f)
|
||||||
arr.toPattern
|
group.toPattern
|
||||||
elif typ is array:
|
elif typ is array:
|
||||||
var arr = DCompoundArr()
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||||
arr.items.setLen(len(typ))
|
for i in 0..len(typ):
|
||||||
for e in arr.items.mitems: e = grab()
|
group.entries[toPreserves i] = grab()
|
||||||
arr.toPattern
|
group.toPattern
|
||||||
else:
|
else:
|
||||||
grab()
|
grab()
|
||||||
|
|
||||||
|
@ -161,64 +161,76 @@ proc fieldCount(T: typedesc): int =
|
||||||
for _, _ in fieldPairs(default T):
|
for _, _ in fieldPairs(default T):
|
||||||
inc result
|
inc result
|
||||||
|
|
||||||
proc dropType*(typ: static typedesc): Pattern =
|
proc matchType*(typ: static typedesc): Pattern =
|
||||||
## Derive a `Pattern` from type `typ` without any bindings.
|
## Derive a `Pattern` from type `typ` without any bindings.
|
||||||
when typ is ref:
|
when typ is ref:
|
||||||
dropType(pointerBase(typ))
|
matchType(pointerBase(typ))
|
||||||
elif typ.hasPreservesRecordPragma:
|
elif typ.hasPreservesRecordPragma:
|
||||||
var rec = DCompoundRec(label: typ.recordLabel.toSymbol)
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||||
rec.fields.setLen(fieldCount typ)
|
group.`type`.rec.label = typ.recordLabel.toSymbol
|
||||||
for i, _ in rec.fields:
|
for _, f in fieldPairs(default typ):
|
||||||
rec.fields[i] = drop()
|
group.entries[group.entries.len.toPreserves] = matchType(typeof f)
|
||||||
result = rec.toPattern
|
group.toPattern
|
||||||
elif typ.hasPreservesDictionaryPragma:
|
elif typ.hasPreservesDictionaryPragma:
|
||||||
DCompoundDict().toPattern
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||||
|
for key, val in fieldPairs(default typ):
|
||||||
|
group.entries[key.toSymbol] = matchType(typeof val)
|
||||||
|
group.toPattern
|
||||||
elif typ is tuple:
|
elif typ is tuple:
|
||||||
var arr = DCompoundArr()
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||||
arr.items.setLen(len typ)
|
for _, f in fieldPairs(default typ):
|
||||||
for i, _ in arr.items:
|
group.entries[group.entries.len.toPreserves] = matchType(typeof f)
|
||||||
arr.items[i] = drop()
|
group.toPattern
|
||||||
arr.toPattern
|
|
||||||
elif typ is array:
|
elif typ is array:
|
||||||
var arr = DCompoundArr()
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||||
arr.items.setLen(len(typ))
|
let elemPat = typ.default.elementType
|
||||||
for e in arr.items.mitems: e = drop()
|
for i in 0..typ.high:
|
||||||
arr.toPattern
|
group.entries[i.toPreserves] = elemPat
|
||||||
|
group.toPattern
|
||||||
else:
|
else:
|
||||||
drop()
|
drop()
|
||||||
|
|
||||||
proc lookup(bindings: openArray[(int, Pattern)]; i: int): Pattern =
|
proc grabType*(typ: static typedesc): Pattern =
|
||||||
for (j, b) in bindings:
|
PatternBind(pattern: typ.matchType).toPattern
|
||||||
if i == j: return b
|
|
||||||
return drop()
|
proc grabWithin*(T: static typedesc): Pattern =
|
||||||
|
## Construct a `Pattern` that binds the fields within type `T`.
|
||||||
|
result = matchType(T)
|
||||||
|
for entry in result.group.entries.mvalues:
|
||||||
|
case entry.orKind
|
||||||
|
of `discard`: entry = grab()
|
||||||
|
of `bind`, `lit`: discard
|
||||||
|
of `group`: entry = grab(entry)
|
||||||
|
|
||||||
|
proc grabWithinType*(T: static typedesc): Pattern {.
|
||||||
|
deprecated: "use grabWithin".} = grabWithin(T)
|
||||||
|
|
||||||
|
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 =
|
proc grab*(typ: static typedesc; bindings: sink openArray[(int, Pattern)]): Pattern =
|
||||||
## Construct a `Pattern` from type `typ` with pattern `bindings` by integer offset.
|
## Construct a `Pattern` from type `typ` with pattern `bindings` by integer offset.
|
||||||
when typ is ptr | ref:
|
when typ is ptr | ref:
|
||||||
grab(pointerBase(typ), bindings)
|
grab(pointerBase(typ), bindings)
|
||||||
elif typ.hasPreservesRecordPragma:
|
elif typ.hasPreservesRecordPragma:
|
||||||
var rec = DCompoundRec(label: typ.recordLabel.toSymbol)
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`rec`))
|
||||||
rec.fields.setLen(fieldCount typ)
|
group.`type`.rec.label = typ.recordLabel.toSymbol
|
||||||
var i: int
|
bindEntries(group, bindings)
|
||||||
for _, f in fieldPairs(default typ):
|
group.toPattern
|
||||||
rec.fields[i] = lookup(bindings, i)
|
|
||||||
inc i
|
|
||||||
result = rec.toPattern
|
|
||||||
elif typ is tuple:
|
elif typ is tuple:
|
||||||
var arr = DCompoundArr()
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`arr`))
|
||||||
arr.items.setLen(fieldCount typ)
|
bindEntries(group, bindings)
|
||||||
var i: int
|
group.toPattern
|
||||||
for _, f in fieldPairs(default typ):
|
|
||||||
arr.items[i] = lookup(bindings, i)
|
|
||||||
inc i
|
|
||||||
result = arr.toPattern
|
|
||||||
else:
|
else:
|
||||||
{.error: "grab with indexed bindings not implemented for " & $typ.}
|
{.error: "grab with indexed bindings not implemented for " & $typ.}
|
||||||
|
|
||||||
proc grab*(typ: static typedesc; bindings: sink openArray[(Value, Pattern)]): Pattern =
|
proc grab*(typ: static typedesc; bindings: sink openArray[(Value, Pattern)]): Pattern =
|
||||||
## Construct a `Pattern` from type `typ` with dictionary field `bindings`.
|
## Construct a `Pattern` from type `typ` with dictionary field `bindings`.
|
||||||
when typ.hasPreservesDictionaryPragma:
|
when typ.hasPreservesDictionaryPragma:
|
||||||
DCompoundDict(entries: bindings.toTable).toPattern
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||||
|
for key, val in bindinds: group.entries[key] = val
|
||||||
|
group.toPattern
|
||||||
else:
|
else:
|
||||||
{.error: "grab with dictionary bindings not implemented for " & $typ.}
|
{.error: "grab with dictionary bindings not implemented for " & $typ.}
|
||||||
|
|
||||||
|
@ -226,11 +238,11 @@ proc grabLit*(): Pattern =
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
from std/unittest import check
|
from std/unittest import check
|
||||||
check:
|
check:
|
||||||
$grabLit() == """<rec lit [<bind <_>>]>"""
|
$grabLit() == """<group <rec lit> {0: <bind <_>>}>"""
|
||||||
grabType(dataspacePatterns.DLit)
|
grabTypeFlat(dataspacePatterns.PatternLit)
|
||||||
|
|
||||||
proc grabDict*(): Pattern =
|
proc grabDict*(): Pattern =
|
||||||
grabType(dataspacePatterns.DCompoundDict)
|
grabTypeFlat(dataspacePatterns.GroupTypeDict)
|
||||||
|
|
||||||
proc unpackLiterals*(pr: Value): Value =
|
proc unpackLiterals*(pr: Value): Value =
|
||||||
result = pr
|
result = pr
|
||||||
|
@ -238,113 +250,117 @@ proc unpackLiterals*(pr: Value): Value =
|
||||||
if pr.isRecord("lit", 1) or pr.isRecord("dict", 1) or pr.isRecord("arr", 1) or pr.isRecord("set", 1):
|
if pr.isRecord("lit", 1) or pr.isRecord("dict", 1) or pr.isRecord("arr", 1) or pr.isRecord("set", 1):
|
||||||
pr = pr.record[0]
|
pr = pr.record[0]
|
||||||
|
|
||||||
proc inject*(pat: Pattern; bindings: openArray[(int, Pattern)]): Pattern =
|
proc inject*(pattern: sink Pattern; p: Pattern; path: varargs[Value, toPreserves]): Pattern =
|
||||||
## Construct a `Pattern` from `pat` with injected overrides from `bindings`.
|
## Inject `p` inside `pattern` at `path`.
|
||||||
## Injects are made at offsets indexed by the discard (`<_>`) patterns in `pat`.
|
## Injects are made at offsets indexed by the discard (`<_>`) patterns in `pat`.
|
||||||
proc inject(pat: Pattern; bindings: openArray[(int, Pattern)]; offset: var int): Pattern =
|
proc inject(pat: var Pattern; path: openarray[Value]) =
|
||||||
case pat.orKind
|
if len(path) == 0:
|
||||||
of PatternKind.DDiscard:
|
pat = p
|
||||||
result = pat
|
elif pat.orKind != PatternKind.`group`:
|
||||||
for (off, injection) in bindings:
|
raise newException(ValueError, "cannot inject along specified path")
|
||||||
if off == offset:
|
else:
|
||||||
result = injection
|
inject(pat.group.entries[path[0]], path[1..path.high])
|
||||||
break
|
result = pattern
|
||||||
inc offset
|
inject(result, path)
|
||||||
of PatternKind.DBind:
|
|
||||||
let bindOff = offset
|
|
||||||
result = pat
|
|
||||||
result.dbind.pattern = inject(pat.dbind.pattern, bindings, offset)
|
|
||||||
if result.orKind == PatternKind.DBind:
|
|
||||||
for (off, injection) in bindings:
|
|
||||||
if (off == bindOff) and (result.dbind.pattern == injection):
|
|
||||||
result = result.dbind.pattern
|
|
||||||
break # promote the injected pattern over the bind
|
|
||||||
of PatternKind.DLit:
|
|
||||||
result = pat
|
|
||||||
of PatternKind.DCompound:
|
|
||||||
result = pat
|
|
||||||
case pat.dcompound.orKind
|
|
||||||
of DCompoundKind.rec:
|
|
||||||
var fields = mapIt(pat.dcompound.rec.fields):
|
|
||||||
inject(it, bindings, offset)
|
|
||||||
result = DCompoundRec(label: result.dcompound.rec.label, fields: fields).toPattern
|
|
||||||
of DCompoundKind.arr:
|
|
||||||
var items = mapIt(pat.dcompound.arr.items):
|
|
||||||
inject(it, bindings, offset)
|
|
||||||
result = DCompoundArr(items: items).toPattern
|
|
||||||
of DCompoundKind.dict:
|
|
||||||
var dict = DCompoundDict()
|
|
||||||
for key, val in pat.dcompound.dict.entries:
|
|
||||||
dict.entries[key] = inject(val, bindings, offset)
|
|
||||||
result = toPattern(dict)
|
|
||||||
var offset = 0
|
|
||||||
inject(pat, bindings, offset)
|
|
||||||
|
|
||||||
proc inject*(pat: Pattern; bindings: openArray[(Value, Pattern)]): Pattern =
|
proc matchRecord*(label: Value, fields: varargs[Pattern]): Pattern =
|
||||||
## Inject `bindings` into a dictionary pattern.
|
runnableExamples:
|
||||||
assert pat.orKind == PatternKind.DCompound
|
from std/unittest import check
|
||||||
assert pat.dcompound.orKind == DCompoundKind.dict
|
import pkg/preserves
|
||||||
result = pat
|
check:
|
||||||
for (key, val) in bindings:
|
$matchRecord("Says".toSymbol, grab(), grab()) ==
|
||||||
result.dcompound.dict.entries[key] = val
|
"""<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
|
||||||
|
|
||||||
|
proc matchRecord*(label: string; fields: varargs[Pattern]): Pattern =
|
||||||
|
matchRecord(label.toSymbol, fields)
|
||||||
|
|
||||||
proc grabRecord*(label: Value, fields: varargs[Pattern]): Pattern =
|
proc grabRecord*(label: Value, fields: varargs[Pattern]): Pattern =
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
from std/unittest import check
|
from std/unittest import check
|
||||||
import syndicate/actors, preserves
|
import pkg/preserves
|
||||||
check:
|
check:
|
||||||
$grabRecord("Says".toSymbol, grab(), grab()) ==
|
$grabRecord("Says".toSymbol, grab(), grab()) ==
|
||||||
"""<rec Says [<bind <_>> <bind <_>>]>"""
|
"""<bind <group <rec Says> {0: <bind <_>> 1: <bind <_>>}>>"""
|
||||||
DCompoundRec(label: label, fields: fields.toSeq).toPattern
|
grab(matchRecord(label, fields))
|
||||||
|
|
||||||
|
proc grabRecord*(label: Value, fields: sink openArray[(int, Pattern)]): Pattern =
|
||||||
|
runnableExamples:
|
||||||
|
from std/unittest import check
|
||||||
|
import pkg/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
|
||||||
|
|
||||||
proc grabRecord*(label: string, fields: varargs[Pattern]): Pattern =
|
proc grabRecord*(label: string, fields: varargs[Pattern]): Pattern =
|
||||||
## Sugar for creating record patterns.
|
## Sugar for creating record patterns.
|
||||||
## `label` is converted to a symbol value.
|
## `label` is converted to a symbol value.
|
||||||
grabRecord(label.toSymbol, fields)
|
grabRecord(label.toSymbol, fields)
|
||||||
|
|
||||||
proc grabDictionary*(bindings: sink openArray[(Value, Pattern)]): Pattern =
|
proc matchDictionary*(bindings: sink openArray[(Value, Pattern)]): Pattern =
|
||||||
## Construct a pattern that grabs some dictionary pairs.
|
## Construct a pattern that grabs some dictionary pairs.
|
||||||
DCompoundDict(entries: bindings.toTable).toPattern
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||||
|
for (key, val) in bindings: group.entries[key] = val
|
||||||
|
group.toPattern
|
||||||
|
|
||||||
proc grabDictionary*(bindings: sink openArray[(string, Pattern)]): Pattern =
|
proc matchDictionary*(bindings: sink openArray[(string, Pattern)]): Pattern =
|
||||||
## Construct a pattern that grabs some dictionary pairs.
|
## Construct a pattern that grabs some dictionary pairs.
|
||||||
## Keys are converted from strings to symbols.
|
## Keys are converted from strings to symbols.
|
||||||
result = DCompoundDict().toPattern
|
var group = PatternGroup(`type`: GroupType(orKind: GroupTypeKind.`dict`))
|
||||||
for (key, val) in bindings.items:
|
for (key, val) in bindings: group.entries[toSymbol key] = val
|
||||||
result.dcompound.dict.entries[key.toSymbol] = val
|
group.toPattern
|
||||||
|
|
||||||
proc depattern(comp: DCompound; values: var seq[Value]; index: var int): Value
|
proc depattern(group: PatternGroup; values: var seq[Value]; index: var int): Value
|
||||||
|
|
||||||
proc depattern(pat: Pattern; values: var seq[Value]; index: var int): Value =
|
proc depattern(pat: Pattern; values: var seq[Value]; index: var int): Value =
|
||||||
case pat.orKind
|
case pat.orKind
|
||||||
of PatternKind.DDiscard:
|
of PatternKind.`discard`:
|
||||||
discard
|
discard
|
||||||
of PatternKind.DBind:
|
of PatternKind.`bind`:
|
||||||
if index < values.len:
|
if index < values.len:
|
||||||
result = move values[index]
|
result = move values[index]
|
||||||
inc index
|
inc index
|
||||||
of PatternKind.DLit:
|
of PatternKind.`lit`:
|
||||||
result = pat.dlit.value.toPreserves
|
result = pat.`lit`.value.toPreserves
|
||||||
of PatternKind.DCompound:
|
of PatternKind.`group`:
|
||||||
result = depattern(pat.dcompound, values, index)
|
result = depattern(pat.group, values, index)
|
||||||
|
|
||||||
proc depattern(comp: DCompound; values: var seq[Value]; index: var int): Value =
|
proc depattern(group: PatternGroup; values: var seq[Value]; index: var int): Value =
|
||||||
case comp.orKind
|
case group.`type`.orKind
|
||||||
of DCompoundKind.rec:
|
of GroupTypeKind.rec:
|
||||||
result = initRecord(comp.rec.label, comp.rec.fields.len)
|
result = initRecord(group.`type`.rec.label, group.entries.len)
|
||||||
for i, f in comp.rec.fields:
|
var i: int
|
||||||
result[i] = depattern(f, values, index)
|
for key, val in group.entries:
|
||||||
of DCompoundKind.arr:
|
if i.fromPreserves key:
|
||||||
result = initSequence(comp.arr.items.len)
|
result[i] = depattern(val, values, index)
|
||||||
for i, e in comp.arr.items:
|
of GroupTypeKind.arr:
|
||||||
result[i] = depattern(e, values, index)
|
result = initSequence(group.entries.len)
|
||||||
of DCompoundKind.dict:
|
var i: int
|
||||||
result = initDictionary(Cap)
|
for key, val in group.entries:
|
||||||
for key, val in comp.dict.entries:
|
if i.fromPreserves key:
|
||||||
|
result[i] = depattern(val, values, index)
|
||||||
|
of GroupTypeKind.dict:
|
||||||
|
result = initDictionary(group.entries.len)
|
||||||
|
for key, val in group.entries:
|
||||||
result[key] = depattern(val, values, index)
|
result[key] = depattern(val, values, index)
|
||||||
|
|
||||||
proc depattern*(pat: Pattern; values: sink seq[Value]): Value =
|
proc depattern*(pat: Pattern; values: sink seq[Value]): Value =
|
||||||
## Convert a `Pattern` to a `Value` while replacing binds with `values`.
|
## Convert a `Pattern` to a `Value` while replacing binds with `values`.
|
||||||
|
runnableExamples:
|
||||||
|
from std/unittest import check
|
||||||
|
import pkg/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
|
var index: int
|
||||||
depattern(pat, values, index)
|
depattern(pat, values, index)
|
||||||
|
|
||||||
|
@ -359,44 +375,99 @@ proc fromPreservesHook*[T](lit: var Literal[T]; pr: Value): bool =
|
||||||
proc toPreservesHook*[T](lit: Literal[T]): Value =
|
proc toPreservesHook*[T](lit: Literal[T]): Value =
|
||||||
lit.value.grab.toPreserves
|
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:
|
||||||
|
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 observePattern*(pat: Pattern): Pattern =
|
||||||
|
## Construct a pattern that matches an `Observe` assertion for `pat`.
|
||||||
|
runnableExamples:
|
||||||
|
import pkg/preserves
|
||||||
|
let
|
||||||
|
sample = parsePreserves"<sample>"
|
||||||
|
observation = sample.drop.observePattern
|
||||||
|
assert $observation ==
|
||||||
|
"<group <rec Observe> {0: <group <rec group> {0: <group <rec rec> {0: <lit sample>}> 1: <group <dict> {}>}> 1: <_>}>"
|
||||||
|
result = matchType Observe
|
||||||
|
result.group.entries[0.toPreserves] = pat.toPreserves.drop
|
||||||
|
|
||||||
|
proc observePattern*(pat: Pattern; injects: openarray[(seq[Value], Pattern)]): Pattern =
|
||||||
|
## Construct a pattern that matches an `Observe` assertion for `pat`
|
||||||
|
## and inject patterns along paths corresponding to the source pattern `pat`.
|
||||||
|
runnableExamples:
|
||||||
|
import pkg/preserves
|
||||||
|
let
|
||||||
|
v = parsePreserves"[ {} {a: #f b: #t } ]"
|
||||||
|
observation = v.drop.observePattern { @[1.toPreserves, "b".toSymbol]: grab() }
|
||||||
|
assert $observation ==
|
||||||
|
"<group <rec Observe> {0: <group <rec group> {0: <group <rec arr> {}> 1: <group <dict> {0: <group <rec group> {0: <group <rec dict> {}> 1: <group <dict> {}>}> 1: <group <rec group> {0: <group <rec dict> {}> 1: <group <dict> {a: <group <rec lit> {0: <lit #f>}> b: <bind <_>>}>}>}>}> 1: <_>}>"
|
||||||
|
result = matchType Observe
|
||||||
|
var meta = pat.toPreserves.drop
|
||||||
|
for (path, pat) in injects:
|
||||||
|
metaApply(meta, pat, path, 0)
|
||||||
|
result.group.entries[0.toPreserves] = meta
|
||||||
|
|
||||||
|
proc observePattern*(pat: Pattern; injects: openarray[(int, Pattern)]): Pattern =
|
||||||
|
## Sugar for observing patterns using record field offsets.
|
||||||
|
pat.observePattern injects.map do (pair: (int, Pattern)) -> (seq[Value], Pattern):
|
||||||
|
(@[pair[0].toPreserves], pair[1])
|
||||||
|
|
||||||
type
|
type
|
||||||
Path* = seq[Value]
|
Path* = seq[Value]
|
||||||
Paths* = seq[Path]
|
Paths* = seq[Path]
|
||||||
Captures* = seq[Value]
|
Captures* = seq[Value]
|
||||||
Analysis* = tuple
|
Analysis* = tuple
|
||||||
|
presentPaths: Paths
|
||||||
constPaths: Paths
|
constPaths: Paths
|
||||||
constValues: seq[Value]
|
constValues: seq[Value]
|
||||||
capturePaths: Paths
|
capturePaths: Paths
|
||||||
|
|
||||||
func walk(result: var Analysis; path: var Path; p: Pattern)
|
func walk(result: var Analysis; path: var Path; p: Pattern)
|
||||||
|
|
||||||
func walk(result: var Analysis; path: var Path; key: int|Value; pat: Pattern) =
|
func walk(result: var Analysis; path: var Path; key: Value; pat: Pattern) =
|
||||||
path.add(key.toPreserves)
|
path.add(key)
|
||||||
walk(result, path, pat)
|
walk(result, path, pat)
|
||||||
discard path.pop
|
discard path.pop
|
||||||
|
|
||||||
func walk(result: var Analysis; path: var Path; p: Pattern) =
|
func walk(result: var Analysis; path: var Path; p: Pattern) =
|
||||||
case p.orKind
|
case p.orKind
|
||||||
of PatternKind.DCompound:
|
of PatternKind.group:
|
||||||
case p.dcompound.orKind
|
for k, v in p.group.entries: walk(result, path, k, v)
|
||||||
of DCompoundKind.rec:
|
of PatternKind.`bind`:
|
||||||
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.orderedEntries: walk(result, path, k, e)
|
|
||||||
of PatternKind.DBind:
|
|
||||||
result.capturePaths.add(path)
|
result.capturePaths.add(path)
|
||||||
walk(result, path, p.dbind.pattern)
|
walk(result, path, p.`bind`.pattern)
|
||||||
of PatternKind.DDiscard: discard
|
of PatternKind.`discard`:
|
||||||
of PatternKind.DLit:
|
result.presentPaths.add(path)
|
||||||
|
of PatternKind.`lit`:
|
||||||
result.constPaths.add(path)
|
result.constPaths.add(path)
|
||||||
result.constValues.add(p.dlit.value.toPreserves)
|
result.constValues.add(p.`lit`.value.toPreserves)
|
||||||
|
|
||||||
func analyse*(p: Pattern): Analysis =
|
func analyse*(p: Pattern): Analysis =
|
||||||
var path: Path
|
var path: Path
|
||||||
walk(result, path, p)
|
walk(result, path, p)
|
||||||
|
|
||||||
|
func 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] =
|
func projectPaths*(v: Value; paths: Paths): Option[Captures] =
|
||||||
var res = newSeq[Value](paths.len)
|
var res = newSeq[Value](paths.len)
|
||||||
for i, path in paths:
|
for i, path in paths:
|
||||||
|
@ -408,30 +479,27 @@ func projectPaths*(v: Value; paths: Paths): Option[Captures] =
|
||||||
proc matches*(pat: Pattern; pr: Value): bool =
|
proc matches*(pat: Pattern; pr: Value): bool =
|
||||||
let analysis = analyse(pat)
|
let analysis = analyse(pat)
|
||||||
assert analysis.constPaths.len == analysis.constValues.len
|
assert analysis.constPaths.len == analysis.constValues.len
|
||||||
for i, path in analysis.constPaths:
|
result = checkPresence(pr, analysis.presentPaths)
|
||||||
let v = step(pr, path)
|
if result:
|
||||||
if v.isNone: return false
|
for i, path in analysis.constPaths:
|
||||||
if analysis.constValues[i] != v.get: return false
|
let v = step(pr, path)
|
||||||
for path in analysis.capturePaths:
|
if v.isNone: return false
|
||||||
if isNone step(pr, path): return false
|
if analysis.constValues[i] != v.get: return false
|
||||||
true
|
for path in analysis.capturePaths:
|
||||||
|
if step(pr, path).isNone: return false
|
||||||
|
|
||||||
func capture*(pat: Pattern; pr: Value): seq[Value] =
|
proc capture*(pat: Pattern; pr: Value): seq[Value] =
|
||||||
let analysis = analyse(pat)
|
let analysis = analyse(pat)
|
||||||
assert analysis.constPaths.len == analysis.constValues.len
|
assert analysis.constPaths.len == analysis.constValues.len
|
||||||
for i, path in analysis.constPaths:
|
if checkPresence(pr, analysis.presentPaths):
|
||||||
let v = step(pr, path)
|
for i, path in analysis.constPaths:
|
||||||
if v.isNone : return @[]
|
let v = step(pr, path)
|
||||||
if analysis.constValues[i] != v.get: return @[]
|
if v.isNone : return @[]
|
||||||
for path in analysis.capturePaths:
|
if analysis.constValues[i] != v.get: return @[]
|
||||||
let v = step(pr, path)
|
for path in analysis.capturePaths:
|
||||||
if v.isNone: return @[]
|
let v = step(pr, path)
|
||||||
result.add(get v)
|
if v.isNone: return @[]
|
||||||
|
result.add(get v)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
let txt = readAll stdin
|
stdout.writeLine stdin.readAll.parsePreserves.grab
|
||||||
if txt != "":
|
|
||||||
let
|
|
||||||
v = parsePreserves(txt)
|
|
||||||
pat = grab v
|
|
||||||
stdout.writeLine(pat)
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ modules += gatekeeper.nim
|
||||||
modules += http.nim
|
modules += http.nim
|
||||||
modules += noise.nim
|
modules += noise.nim
|
||||||
modules += protocol.nim
|
modules += protocol.nim
|
||||||
|
modules += rpc.nim
|
||||||
modules += service.nim
|
modules += service.nim
|
||||||
modules += stdenv.nim
|
modules += stdenv.nim
|
||||||
modules += stream.nim
|
modules += stream.nim
|
||||||
|
@ -16,7 +17,7 @@ modules += transportAddress.nim
|
||||||
modules += worker.nim
|
modules += worker.nim
|
||||||
|
|
||||||
: ../../../../syndicate-protocols/schema-bundle.bin \
|
: ../../../../syndicate-protocols/schema-bundle.bin \
|
||||||
|> !preserves_schema_nim \
|
|> !preserves-schema-nim \
|
||||||
|> $(modules) | $(SYNDICATE_PROTOCOL)
|
|> $(modules) | $(SYNDICATE_PROTOCOL)
|
||||||
|
|
||||||
: foreach $(modules) | $(modules) |> !nim_check |>
|
: foreach $(modules) | $(modules) |> !nim_check |>
|
||||||
|
|
|
@ -29,58 +29,58 @@ type
|
||||||
`embedded`* {.preservesEmbedded.}: EmbeddedRef
|
`embedded`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
|
|
||||||
DLit* {.preservesRecord: "lit".} = object
|
GroupTypeKind* {.pure.} = enum
|
||||||
`value`*: AnyAtom
|
|
||||||
|
|
||||||
DBind* {.preservesRecord: "bind".} = object
|
|
||||||
`pattern`*: Pattern
|
|
||||||
|
|
||||||
DDiscard* {.preservesRecord: "_".} = object
|
|
||||||
|
|
||||||
DCompoundKind* {.pure.} = enum
|
|
||||||
`rec`, `arr`, `dict`
|
`rec`, `arr`, `dict`
|
||||||
DCompoundRec* {.preservesRecord: "rec".} = object
|
GroupTypeRec* {.preservesRecord: "rec".} = object
|
||||||
`label`*: Value
|
`label`*: Value
|
||||||
`fields`*: seq[Pattern]
|
|
||||||
|
|
||||||
DCompoundArr* {.preservesRecord: "arr".} = object
|
GroupTypeArr* {.preservesRecord: "arr".} = object
|
||||||
`items`*: seq[Pattern]
|
|
||||||
|
|
||||||
DCompoundDict* {.preservesRecord: "dict".} = object
|
GroupTypeDict* {.preservesRecord: "dict".} = object
|
||||||
`entries`*: Table[Value, Pattern]
|
|
||||||
|
|
||||||
`DCompound`* {.preservesOr.} = object
|
`GroupType`* {.preservesOr.} = object
|
||||||
case orKind*: DCompoundKind
|
case orKind*: GroupTypeKind
|
||||||
of DCompoundKind.`rec`:
|
of GroupTypeKind.`rec`:
|
||||||
`rec`* {.preservesEmbedded.}: DCompoundRec
|
`rec`*: GroupTypeRec
|
||||||
|
|
||||||
of DCompoundKind.`arr`:
|
of GroupTypeKind.`arr`:
|
||||||
`arr`* {.preservesEmbedded.}: DCompoundArr
|
`arr`*: GroupTypeArr
|
||||||
|
|
||||||
of DCompoundKind.`dict`:
|
of GroupTypeKind.`dict`:
|
||||||
`dict`* {.preservesEmbedded.}: DCompoundDict
|
`dict`*: GroupTypeDict
|
||||||
|
|
||||||
|
|
||||||
PatternKind* {.pure.} = enum
|
PatternKind* {.pure.} = enum
|
||||||
`DDiscard`, `DBind`, `DLit`, `DCompound`
|
`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
|
`Pattern`* {.acyclic, preservesOr.} = ref object
|
||||||
case orKind*: PatternKind
|
case orKind*: PatternKind
|
||||||
of PatternKind.`DDiscard`:
|
of PatternKind.`discard`:
|
||||||
`ddiscard`*: DDiscard
|
`discard`*: PatternDiscard
|
||||||
|
|
||||||
of PatternKind.`DBind`:
|
of PatternKind.`bind`:
|
||||||
`dbind`* {.preservesEmbedded.}: DBind
|
`bind`* {.preservesEmbedded.}: PatternBind
|
||||||
|
|
||||||
of PatternKind.`DLit`:
|
of PatternKind.`lit`:
|
||||||
`dlit`* {.preservesEmbedded.}: DLit
|
`lit`* {.preservesEmbedded.}: PatternLit
|
||||||
|
|
||||||
of PatternKind.`DCompound`:
|
of PatternKind.`group`:
|
||||||
`dcompound`* {.preservesEmbedded.}: DCompound
|
`group`* {.preservesEmbedded.}: PatternGroup
|
||||||
|
|
||||||
|
|
||||||
proc `$`*(x: AnyAtom | DLit | DBind | DDiscard | DCompound | Pattern): string =
|
proc `$`*(x: AnyAtom | GroupType | Pattern): string =
|
||||||
`$`(toPreserves(x))
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*(x: AnyAtom | DLit | DBind | DDiscard | DCompound | Pattern): seq[
|
proc encode*(x: AnyAtom | GroupType | Pattern): seq[byte] =
|
||||||
byte] =
|
|
||||||
encode(toPreserves(x))
|
encode(toPreserves(x))
|
||||||
|
|
|
@ -13,15 +13,15 @@ type
|
||||||
`pathSteps`* {.preservesTupleTail.}: seq[PathStep]
|
`pathSteps`* {.preservesTupleTail.}: seq[PathStep]
|
||||||
|
|
||||||
BindObserverKind* {.pure.} = enum
|
BindObserverKind* {.pure.} = enum
|
||||||
`present`, `absent`
|
`absent`, `present`
|
||||||
`BindObserver`* {.preservesOr.} = object
|
`BindObserver`* {.preservesOr.} = object
|
||||||
case orKind*: BindObserverKind
|
case orKind*: BindObserverKind
|
||||||
of BindObserverKind.`present`:
|
|
||||||
`present`* {.preservesEmbedded.}: EmbeddedRef
|
|
||||||
|
|
||||||
of BindObserverKind.`absent`:
|
of BindObserverKind.`absent`:
|
||||||
`absent`* {.preservesLiteral: "#f".}: bool
|
`absent`* {.preservesLiteral: "#f".}: bool
|
||||||
|
|
||||||
|
of BindObserverKind.`present`:
|
||||||
|
`present`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
|
|
||||||
TransportConnection* {.preservesRecord: "connect-transport".} = object
|
TransportConnection* {.preservesRecord: "connect-transport".} = object
|
||||||
`addr`*: Value
|
`addr`*: Value
|
||||||
|
@ -35,18 +35,18 @@ type
|
||||||
`resolved`*: Resolved
|
`resolved`*: Resolved
|
||||||
|
|
||||||
BoundKind* {.pure.} = enum
|
BoundKind* {.pure.} = enum
|
||||||
`bound`, `Rejected`
|
`Rejected`, `bound`
|
||||||
BoundBound* {.preservesRecord: "bound".} = object
|
BoundBound* {.preservesRecord: "bound".} = object
|
||||||
`pathStep`*: PathStep
|
`pathStep`*: PathStep
|
||||||
|
|
||||||
`Bound`* {.preservesOr.} = object
|
`Bound`* {.preservesOr.} = object
|
||||||
case orKind*: BoundKind
|
case orKind*: BoundKind
|
||||||
of BoundKind.`bound`:
|
|
||||||
`bound`*: BoundBound
|
|
||||||
|
|
||||||
of BoundKind.`Rejected`:
|
of BoundKind.`Rejected`:
|
||||||
`rejected`*: Rejected
|
`rejected`*: Rejected
|
||||||
|
|
||||||
|
of BoundKind.`bound`:
|
||||||
|
`bound`*: BoundBound
|
||||||
|
|
||||||
|
|
||||||
ForceDisconnect* {.preservesRecord: "force-disconnect".} = object
|
ForceDisconnect* {.preservesRecord: "force-disconnect".} = object
|
||||||
|
|
||||||
|
@ -59,18 +59,18 @@ type
|
||||||
`observer`* {.preservesEmbedded.}: EmbeddedRef
|
`observer`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
ResolvedKind* {.pure.} = enum
|
ResolvedKind* {.pure.} = enum
|
||||||
`accepted`, `Rejected`
|
`Rejected`, `accepted`
|
||||||
ResolvedAccepted* {.preservesRecord: "accepted".} = object
|
ResolvedAccepted* {.preservesRecord: "accepted".} = object
|
||||||
`responderSession`* {.preservesEmbedded.}: EmbeddedRef
|
`responderSession`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
`Resolved`* {.preservesOr.} = object
|
`Resolved`* {.preservesOr.} = object
|
||||||
case orKind*: ResolvedKind
|
case orKind*: ResolvedKind
|
||||||
of ResolvedKind.`accepted`:
|
|
||||||
`accepted`* {.preservesEmbedded.}: ResolvedAccepted
|
|
||||||
|
|
||||||
of ResolvedKind.`Rejected`:
|
of ResolvedKind.`Rejected`:
|
||||||
`rejected`*: Rejected
|
`rejected`*: Rejected
|
||||||
|
|
||||||
|
of ResolvedKind.`accepted`:
|
||||||
|
`accepted`* {.preservesEmbedded.}: ResolvedAccepted
|
||||||
|
|
||||||
|
|
||||||
TransportControl* = ForceDisconnect
|
TransportControl* = ForceDisconnect
|
||||||
ResolvePath* {.preservesRecord: "resolve-path".} = object
|
ResolvePath* {.preservesRecord: "resolve-path".} = object
|
||||||
|
|
|
@ -47,24 +47,13 @@ type
|
||||||
|
|
||||||
HttpRequest* {.preservesRecord: "http-request".} = object
|
HttpRequest* {.preservesRecord: "http-request".} = object
|
||||||
`sequenceNumber`*: BiggestInt
|
`sequenceNumber`*: BiggestInt
|
||||||
`host`*: string
|
`host`*: RequestHost
|
||||||
`port`*: BiggestInt
|
`port`*: BiggestInt
|
||||||
`method`*: Symbol
|
`method`*: Symbol
|
||||||
`path`*: seq[string]
|
`path`*: seq[string]
|
||||||
`headers`*: Headers
|
`headers`*: Headers
|
||||||
`query`*: Table[Symbol, seq[QueryValue]]
|
`query`*: Table[Symbol, seq[QueryValue]]
|
||||||
`body`*: RequestBody
|
`body`*: Value
|
||||||
|
|
||||||
RequestBodyKind* {.pure.} = enum
|
|
||||||
`present`, `absent`
|
|
||||||
`RequestBody`* {.preservesOr.} = object
|
|
||||||
case orKind*: RequestBodyKind
|
|
||||||
of RequestBodyKind.`present`:
|
|
||||||
`present`*: seq[byte]
|
|
||||||
|
|
||||||
of RequestBodyKind.`absent`:
|
|
||||||
`absent`* {.preservesLiteral: "#f".}: bool
|
|
||||||
|
|
||||||
|
|
||||||
Headers* = Table[Symbol, string]
|
Headers* = Table[Symbol, string]
|
||||||
HttpResponseKind* {.pure.} = enum
|
HttpResponseKind* {.pure.} = enum
|
||||||
|
@ -115,6 +104,17 @@ type
|
||||||
`req`*: HttpRequest
|
`req`*: HttpRequest
|
||||||
`res`* {.preservesEmbedded.}: Value
|
`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
|
PathPatternElementKind* {.pure.} = enum
|
||||||
`label`, `wildcard`, `rest`
|
`label`, `wildcard`, `rest`
|
||||||
`PathPatternElement`* {.preservesOr.} = object
|
`PathPatternElement`* {.preservesOr.} = object
|
||||||
|
@ -143,12 +143,12 @@ type
|
||||||
PathPattern* = seq[PathPatternElement]
|
PathPattern* = seq[PathPatternElement]
|
||||||
proc `$`*(x: HostPattern | HttpListener | MethodPattern | MimeType | QueryValue |
|
proc `$`*(x: HostPattern | HttpListener | MethodPattern | MimeType | QueryValue |
|
||||||
HttpRequest |
|
HttpRequest |
|
||||||
RequestBody |
|
|
||||||
Headers |
|
Headers |
|
||||||
HttpResponse |
|
HttpResponse |
|
||||||
HttpService |
|
HttpService |
|
||||||
HttpBinding |
|
HttpBinding |
|
||||||
HttpContext |
|
HttpContext |
|
||||||
|
RequestHost |
|
||||||
PathPatternElement |
|
PathPatternElement |
|
||||||
Chunk |
|
Chunk |
|
||||||
PathPattern): string =
|
PathPattern): string =
|
||||||
|
@ -157,12 +157,12 @@ proc `$`*(x: HostPattern | HttpListener | MethodPattern | MimeType | QueryValue
|
||||||
proc encode*(x: HostPattern | HttpListener | MethodPattern | MimeType |
|
proc encode*(x: HostPattern | HttpListener | MethodPattern | MimeType |
|
||||||
QueryValue |
|
QueryValue |
|
||||||
HttpRequest |
|
HttpRequest |
|
||||||
RequestBody |
|
|
||||||
Headers |
|
Headers |
|
||||||
HttpResponse |
|
HttpResponse |
|
||||||
HttpService |
|
HttpService |
|
||||||
HttpBinding |
|
HttpBinding |
|
||||||
HttpContext |
|
HttpContext |
|
||||||
|
RequestHost |
|
||||||
PathPatternElement |
|
PathPatternElement |
|
||||||
Chunk |
|
Chunk |
|
||||||
PathPattern): seq[byte] =
|
PathPattern): seq[byte] =
|
||||||
|
|
|
@ -48,6 +48,17 @@ type
|
||||||
`absent`*: SecretKeyFieldAbsent
|
`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
|
NoiseProtocolKind* {.pure.} = enum
|
||||||
`present`, `invalid`, `absent`
|
`present`, `invalid`, `absent`
|
||||||
NoiseProtocolPresent* {.preservesDictionary.} = object
|
NoiseProtocolPresent* {.preservesDictionary.} = object
|
||||||
|
@ -83,6 +94,9 @@ type
|
||||||
`service`*: ServiceSelector
|
`service`*: ServiceSelector
|
||||||
|
|
||||||
ServiceSelector* = Value
|
ServiceSelector* = Value
|
||||||
|
Initiator* {.preservesRecord: "initiator".} = object
|
||||||
|
`initiatorSession`* {.preservesEmbedded.}: EmbeddedRef
|
||||||
|
|
||||||
NoiseStepDetail* = ServiceSelector
|
NoiseStepDetail* = ServiceSelector
|
||||||
NoiseSpecKey* = seq[byte]
|
NoiseSpecKey* = seq[byte]
|
||||||
NoiseSpecPreSharedKeys* = Option[Value]
|
NoiseSpecPreSharedKeys* = Option[Value]
|
||||||
|
@ -105,17 +119,21 @@ type
|
||||||
|
|
||||||
|
|
||||||
proc `$`*(x: NoiseDescriptionDetail | NoisePreSharedKeys | SecretKeyField |
|
proc `$`*(x: NoiseDescriptionDetail | NoisePreSharedKeys | SecretKeyField |
|
||||||
|
SessionItem |
|
||||||
NoiseProtocol |
|
NoiseProtocol |
|
||||||
NoisePathStepDetail |
|
NoisePathStepDetail |
|
||||||
NoiseServiceSpec |
|
NoiseServiceSpec |
|
||||||
|
Initiator |
|
||||||
NoiseSpec |
|
NoiseSpec |
|
||||||
Packet): string =
|
Packet): string =
|
||||||
`$`(toPreserves(x))
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
proc encode*(x: NoiseDescriptionDetail | NoisePreSharedKeys | SecretKeyField |
|
proc encode*(x: NoiseDescriptionDetail | NoisePreSharedKeys | SecretKeyField |
|
||||||
|
SessionItem |
|
||||||
NoiseProtocol |
|
NoiseProtocol |
|
||||||
NoisePathStepDetail |
|
NoisePathStepDetail |
|
||||||
NoiseServiceSpec |
|
NoiseServiceSpec |
|
||||||
|
Initiator |
|
||||||
NoiseSpec |
|
NoiseSpec |
|
||||||
Packet): seq[byte] =
|
Packet): seq[byte] =
|
||||||
encode(toPreserves(x))
|
encode(toPreserves(x))
|
||||||
|
|
|
@ -30,9 +30,12 @@ type
|
||||||
Assertion* = Value
|
Assertion* = Value
|
||||||
Handle* = BiggestInt
|
Handle* = BiggestInt
|
||||||
PacketKind* {.pure.} = enum
|
PacketKind* {.pure.} = enum
|
||||||
`Turn`, `Error`, `Extension`
|
`Nop`, `Turn`, `Error`, `Extension`
|
||||||
`Packet`* {.preservesOr.} = object
|
`Packet`* {.preservesOr.} = object
|
||||||
case orKind*: PacketKind
|
case orKind*: PacketKind
|
||||||
|
of PacketKind.`Nop`:
|
||||||
|
`nop`* {.preservesLiteral: "#f".}: bool
|
||||||
|
|
||||||
of PacketKind.`Turn`:
|
of PacketKind.`Turn`:
|
||||||
`turn`* {.preservesEmbedded.}: Turn
|
`turn`* {.preservesEmbedded.}: Turn
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
|
||||||
|
import
|
||||||
|
preserves
|
||||||
|
|
||||||
|
type
|
||||||
|
Question* {.preservesRecord: "q".} = object
|
||||||
|
`request`*: Value
|
||||||
|
|
||||||
|
Answer* {.preservesRecord: "a".} = object
|
||||||
|
`request`*: Value
|
||||||
|
`response`*: Value
|
||||||
|
|
||||||
|
ResultKind* {.pure.} = enum
|
||||||
|
`ok`, `error`
|
||||||
|
ResultOk* {.preservesRecord: "ok".} = object
|
||||||
|
`value`*: Value
|
||||||
|
|
||||||
|
ResultError* {.preservesRecord: "error".} = object
|
||||||
|
`error`*: Value
|
||||||
|
|
||||||
|
`Result`* {.preservesOr.} = object
|
||||||
|
case orKind*: ResultKind
|
||||||
|
of ResultKind.`ok`:
|
||||||
|
`ok`*: ResultOk
|
||||||
|
|
||||||
|
of ResultKind.`error`:
|
||||||
|
`error`*: ResultError
|
||||||
|
|
||||||
|
|
||||||
|
proc `$`*(x: Question | Answer | Result): string =
|
||||||
|
`$`(toPreserves(x))
|
||||||
|
|
||||||
|
proc encode*(x: Question | Answer | Result): seq[byte] =
|
||||||
|
encode(toPreserves(x))
|
|
@ -11,6 +11,7 @@ type
|
||||||
`label`*: Value
|
`label`*: Value
|
||||||
`seconds`*: float
|
`seconds`*: float
|
||||||
`kind`*: TimerKind
|
`kind`*: TimerKind
|
||||||
|
`peer`* {.preservesEmbedded.}: Value
|
||||||
|
|
||||||
`TimerKind`* {.preservesOr, pure.} = enum
|
`TimerKind`* {.preservesOr, pure.} = enum
|
||||||
`relative`, `absolute`, `clear`
|
`relative`, `absolute`, `clear`
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[options, tables]
|
import
|
||||||
import preserves
|
std/[options, sets, tables],
|
||||||
import ../syndicate, ./durings, ./membranes, ./protocols/[gatekeeper, protocol, sturdy, transportAddress]
|
pkg/preserves,
|
||||||
|
../syndicate, ./durings, ./membranes, ./protocols/[gatekeeper, protocol, sturdy, transportAddress]
|
||||||
|
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
import ./capabilities
|
import ./capabilities
|
||||||
|
@ -29,8 +30,8 @@ type
|
||||||
Turn = syndicate.Turn
|
Turn = syndicate.Turn
|
||||||
WireRef = sturdy.WireRef
|
WireRef = sturdy.WireRef
|
||||||
|
|
||||||
PacketWriter = proc (turn: var Turn; buf: seq[byte]) {.closure.}
|
PacketWriter = proc (turn: Turn; buf: seq[byte]) {.closure.}
|
||||||
RelaySetup = proc (turn: var Turn; relay: Relay) {.closure.}
|
RelaySetup = proc (turn: Turn; relay: Relay) {.closure.}
|
||||||
|
|
||||||
Relay* = ref object
|
Relay* = ref object
|
||||||
facet: Facet
|
facet: Facet
|
||||||
|
@ -59,27 +60,27 @@ type
|
||||||
proc releaseCapOut(r: Relay; e: WireSymbol) =
|
proc releaseCapOut(r: Relay; e: WireSymbol) =
|
||||||
r.exported.drop e
|
r.exported.drop e
|
||||||
|
|
||||||
method publish(spe: SyncPeerEntity; t: var Turn; a: AssertionRef; h: Handle) =
|
method publish(spe: SyncPeerEntity; t: Turn; a: AssertionRef; h: Handle) =
|
||||||
spe.handleMap[h] = publish(t, spe.peer, a.value)
|
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
|
var other: Handle
|
||||||
if se.handleMap.pop(h, other):
|
if se.handleMap.pop(h, other):
|
||||||
retract(t, other)
|
retract(t, other)
|
||||||
|
|
||||||
method message(se: SyncPeerEntity; t: var Turn; a: AssertionRef) =
|
method message(se: SyncPeerEntity; t: Turn; a: AssertionRef) =
|
||||||
if not se.e.isNil:
|
if not se.e.isNil:
|
||||||
se.relay.releaseCapOut(se.e)
|
se.relay.releaseCapOut(se.e)
|
||||||
message(t, se.peer, a.value)
|
message(t, se.peer, a.value)
|
||||||
|
|
||||||
method sync(se: SyncPeerEntity; t: var Turn; peer: Cap) =
|
method sync(se: SyncPeerEntity; t: Turn; peer: Cap) =
|
||||||
sync(t, se.peer, peer)
|
sync(t, se.peer, peer)
|
||||||
|
|
||||||
proc newSyncPeerEntity(r: Relay; p: Cap): SyncPeerEntity =
|
proc newSyncPeerEntity(r: Relay; p: Cap): SyncPeerEntity =
|
||||||
SyncPeerEntity(relay: r, peer: p)
|
SyncPeerEntity(relay: r, peer: p)
|
||||||
|
|
||||||
proc rewriteCapOut(relay: Relay; cap: Cap; exported: var seq[WireSymbol]): WireRef =
|
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:
|
if cap.target of RelayEntity and cap.target.RelayEntity.relay == relay and cap.caveats.len == 0:
|
||||||
result = WireRef(orKind: WireRefKind.yours, yours: WireRefYours(oid: cap.target.oid))
|
result = WireRef(orKind: WireRefKind.yours, yours: WireRefYours(oid: cap.target.oid))
|
||||||
else:
|
else:
|
||||||
var ws = grab(relay.exported, cap)
|
var ws = grab(relay.exported, cap)
|
||||||
|
@ -96,8 +97,9 @@ proc rewriteOut(relay: Relay; v: Assertion):
|
||||||
var exported: seq[WireSymbol]
|
var exported: seq[WireSymbol]
|
||||||
result.rewritten = mapEmbeds(v) do (pr: Value) -> Value:
|
result.rewritten = mapEmbeds(v) do (pr: Value) -> Value:
|
||||||
let o = pr.unembed(Cap); if o.isSome:
|
let o = pr.unembed(Cap); if o.isSome:
|
||||||
rewriteCapOut(relay, o.get, exported).toPreserves
|
result = rewriteCapOut(relay, o.get, exported).toPreserves
|
||||||
else: pr
|
result.embedded = true
|
||||||
|
else: result = pr
|
||||||
result.exported = exported
|
result.exported = exported
|
||||||
|
|
||||||
proc register(relay: Relay; v: Assertion; h: Handle): tuple[rewritten: Value, exported: seq[WireSymbol]] =
|
proc register(relay: Relay; v: Assertion; h: Handle): tuple[rewritten: Value, exported: seq[WireSymbol]] =
|
||||||
|
@ -109,10 +111,10 @@ proc deregister(relay: Relay; h: Handle) =
|
||||||
if relay.outboundAssertions.pop(h, outbound):
|
if relay.outboundAssertions.pop(h, outbound):
|
||||||
for e in outbound: releaseCapOut(relay, e)
|
for e in outbound: releaseCapOut(relay, e)
|
||||||
|
|
||||||
proc send(relay: Relay; turn: var Turn; rOid: protocol.Oid; m: Event) =
|
proc send(relay: Relay; turn: Turn; rOid: protocol.Oid; m: Event) =
|
||||||
# TODO: don't send right away.
|
# TODO: don't send right away.
|
||||||
relay.pendingTurn.add TurnEvent(oid: rOid, event: m)
|
relay.pendingTurn.add TurnEvent(oid: rOid, event: m)
|
||||||
queueEffect(turn, relay.facet) do (turn: var Turn):
|
queueEffect(turn, relay.facet) do (turn: Turn):
|
||||||
if relay.pendingTurn.len > 0:
|
if relay.pendingTurn.len > 0:
|
||||||
var pkt = Packet(
|
var pkt = Packet(
|
||||||
orKind: PacketKind.Turn,
|
orKind: PacketKind.Turn,
|
||||||
|
@ -120,29 +122,29 @@ proc send(relay: Relay; turn: var Turn; rOid: protocol.Oid; m: Event) =
|
||||||
trace "C: ", pkt
|
trace "C: ", pkt
|
||||||
relay.packetWriter(turn, encode pkt)
|
relay.packetWriter(turn, encode pkt)
|
||||||
|
|
||||||
proc send(re: RelayEntity; turn: var Turn; ev: Event) =
|
proc send(re: RelayEntity; turn: Turn; ev: Event) =
|
||||||
send(re.relay, turn, protocol.Oid re.oid, ev)
|
send(re.relay, turn, protocol.Oid re.oid, ev)
|
||||||
|
|
||||||
method publish(re: RelayEntity; t: var Turn; a: AssertionRef; h: Handle) =
|
method publish(re: RelayEntity; t: Turn; a: AssertionRef; h: Handle) =
|
||||||
re.send(t, Event(
|
re.send(t, Event(
|
||||||
orKind: EventKind.Assert,
|
orKind: EventKind.Assert,
|
||||||
`assert`: protocol.Assert(
|
`assert`: protocol.Assert(
|
||||||
assertion: re.relay.register(a.value, h).rewritten,
|
assertion: re.relay.register(a.value, h).rewritten,
|
||||||
handle: h)))
|
handle: h)))
|
||||||
|
|
||||||
method retract(re: RelayEntity; t: var Turn; h: Handle) =
|
method retract(re: RelayEntity; t: Turn; h: Handle) =
|
||||||
re.relay.deregister h
|
re.relay.deregister h
|
||||||
re.send(t, Event(
|
re.send(t, Event(
|
||||||
orKind: EventKind.Retract,
|
orKind: EventKind.Retract,
|
||||||
retract: Retract(handle: h)))
|
retract: Retract(handle: h)))
|
||||||
|
|
||||||
method message(re: RelayEntity; turn: var Turn; msg: AssertionRef) =
|
method message(re: RelayEntity; turn: Turn; msg: AssertionRef) =
|
||||||
var (value, exported) = rewriteOut(re.relay, msg.value)
|
var (value, exported) = rewriteOut(re.relay, msg.value)
|
||||||
assert(len(exported) == 0, "cannot send a reference in a message")
|
assert(len(exported) == 0, "cannot send a reference in a message")
|
||||||
if len(exported) == 0:
|
if len(exported) == 0:
|
||||||
re.send(turn, Event(orKind: EventKind.Message, message: Message(body: value)))
|
re.send(turn, Event(orKind: EventKind.Message, message: Message(body: value)))
|
||||||
|
|
||||||
method sync(re: RelayEntity; turn: var Turn; peer: Cap) =
|
method sync(re: RelayEntity; turn: Turn; peer: Cap) =
|
||||||
var
|
var
|
||||||
peerEntity = newSyncPeerEntity(re.relay, peer)
|
peerEntity = newSyncPeerEntity(re.relay, peer)
|
||||||
exported: seq[WireSymbol]
|
exported: seq[WireSymbol]
|
||||||
|
@ -195,7 +197,7 @@ proc rewriteIn(relay; facet; v: Value):
|
||||||
|
|
||||||
proc close(r: Relay) = discard
|
proc close(r: Relay) = discard
|
||||||
|
|
||||||
proc dispatch(relay: Relay; turn: var Turn; cap: Cap; event: Event) =
|
proc dispatch(relay: Relay; turn: Turn; cap: Cap; event: Event) =
|
||||||
case event.orKind
|
case event.orKind
|
||||||
of EventKind.Assert:
|
of EventKind.Assert:
|
||||||
let (a, imported) = rewriteIn(relay, turn.facet, event.assert.assertion)
|
let (a, imported) = rewriteIn(relay, turn.facet, event.assert.assertion)
|
||||||
|
@ -214,7 +216,7 @@ proc dispatch(relay: Relay; turn: var Turn; cap: Cap; event: Event) =
|
||||||
turn.message(cap, a)
|
turn.message(cap, a)
|
||||||
|
|
||||||
of EventKind.Sync:
|
of EventKind.Sync:
|
||||||
turn.sync(cap) do (turn: var Turn):
|
turn.sync(cap) do (turn: Turn):
|
||||||
var
|
var
|
||||||
(v, imported) = rewriteIn(relay, turn.facet, event.sync.peer)
|
(v, imported) = rewriteIn(relay, turn.facet, event.sync.peer)
|
||||||
peer = unembed(v, Cap)
|
peer = unembed(v, Cap)
|
||||||
|
@ -224,7 +226,7 @@ proc dispatch(relay: Relay; turn: var Turn; cap: Cap; event: Event) =
|
||||||
|
|
||||||
proc dispatch(relay: Relay; v: Value) =
|
proc dispatch(relay: Relay; v: Value) =
|
||||||
trace "S: ", v
|
trace "S: ", v
|
||||||
run(relay.facet) do (t: var Turn):
|
run(relay.facet) do (t: Turn):
|
||||||
var pkt: Packet
|
var pkt: Packet
|
||||||
if pkt.fromPreserves(v):
|
if pkt.fromPreserves(v):
|
||||||
case pkt.orKind
|
case pkt.orKind
|
||||||
|
@ -242,6 +244,8 @@ proc dispatch(relay: Relay; v: Value) =
|
||||||
of PacketKind.Extension:
|
of PacketKind.Extension:
|
||||||
# https://synit.org/book/protocol.html#extension-packets
|
# https://synit.org/book/protocol.html#extension-packets
|
||||||
discard
|
discard
|
||||||
|
of PacketKind.Nop:
|
||||||
|
discard
|
||||||
else:
|
else:
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
stderr.writeLine("discarding undecoded packet ", v)
|
stderr.writeLine("discarding undecoded packet ", v)
|
||||||
|
@ -251,7 +255,7 @@ proc recv(relay: Relay; buf: openarray[byte]; slice: Slice[int]) =
|
||||||
var pr = decode(relay.wireBuf)
|
var pr = decode(relay.wireBuf)
|
||||||
if pr.isSome: dispatch(relay, pr.get)
|
if pr.isSome: dispatch(relay, pr.get)
|
||||||
|
|
||||||
proc recv(relay: Relay; buf: openarray[byte]) =
|
proc recv(relay: Relay; buf: openarray[byte]) {.used.} =
|
||||||
feed(relay.wireBuf, buf)
|
feed(relay.wireBuf, buf)
|
||||||
var pr = decode(relay.wireBuf)
|
var pr = decode(relay.wireBuf)
|
||||||
if pr.isSome: dispatch(relay, pr.get)
|
if pr.isSome: dispatch(relay, pr.get)
|
||||||
|
@ -265,8 +269,8 @@ type
|
||||||
initialCap*: Cap
|
initialCap*: Cap
|
||||||
nextLocalOid*: Option[Oid]
|
nextLocalOid*: Option[Oid]
|
||||||
|
|
||||||
proc spawnRelay(name: string; turn: var Turn; opts: RelayActorOptions; setup: RelaySetup) =
|
proc spawnRelay(name: string; turn: Turn; opts: RelayActorOptions; setup: RelaySetup) =
|
||||||
linkActor(turn, name) do (turn: var Turn):
|
linkActor(turn, name) do (turn: Turn):
|
||||||
turn.preventInertCheck()
|
turn.preventInertCheck()
|
||||||
let relay = Relay(
|
let relay = Relay(
|
||||||
facet: turn.facet,
|
facet: turn.facet,
|
||||||
|
@ -300,10 +304,10 @@ proc accepted(cap: Cap): Resolved =
|
||||||
result.accepted.responderSession = cap
|
result.accepted.responderSession = cap
|
||||||
|
|
||||||
type ShutdownEntity = ref object of Entity
|
type ShutdownEntity = ref object of Entity
|
||||||
method retract(e: ShutdownEntity; turn: var Turn; h: Handle) =
|
method retract(e: ShutdownEntity; turn: Turn; h: Handle) =
|
||||||
stopActor(e.facet)
|
stopActor(e.facet)
|
||||||
|
|
||||||
when defined(posix):
|
when defined(posix) and not defined(nimdoc):
|
||||||
|
|
||||||
import std/[oserrors, posix]
|
import std/[oserrors, posix]
|
||||||
import pkg/sys/[files, handles, ioqueue, sockets]
|
import pkg/sys/[files, handles, ioqueue, sockets]
|
||||||
|
@ -314,7 +318,7 @@ when defined(posix):
|
||||||
stdin: AsyncFile
|
stdin: AsyncFile
|
||||||
alive: bool
|
alive: bool
|
||||||
|
|
||||||
method message(entity: StdioEntity; turn: var Turn; ass: AssertionRef) =
|
method message(entity: StdioEntity; turn: Turn; ass: AssertionRef) =
|
||||||
if ass.value.preservesTo(ForceDisconnect).isSome:
|
if ass.value.preservesTo(ForceDisconnect).isSome:
|
||||||
entity.alive = false
|
entity.alive = false
|
||||||
|
|
||||||
|
@ -324,15 +328,17 @@ when defined(posix):
|
||||||
while entity.alive:
|
while entity.alive:
|
||||||
buf[].setLen(0x1000)
|
buf[].setLen(0x1000)
|
||||||
let n = read(entity.stdin, buf)
|
let n = read(entity.stdin, buf)
|
||||||
if n == 0:
|
if n > 0:
|
||||||
stopActor(entity.facet)
|
|
||||||
else:
|
|
||||||
entity.relay.recv(buf[], 0..<n)
|
entity.relay.recv(buf[], 0..<n)
|
||||||
|
else:
|
||||||
|
entity.alive = false
|
||||||
|
if n < 0: raiseOSError(osLastError())
|
||||||
|
stopActor(entity.facet)
|
||||||
|
|
||||||
proc connectTransport(turn: var Turn; ds: Cap; ta: transportAddress.Stdio) =
|
proc connectTransport(turn: Turn; ds: Cap; ta: transportAddress.Stdio) =
|
||||||
## Connect to an external dataspace over stdio.
|
## Connect to an external dataspace over stdio.
|
||||||
let localDataspace = newDataspace(turn)
|
let localDataspace = newDataspace(turn)
|
||||||
proc stdoutWriter(turn: var Turn; buf: seq[byte]) =
|
proc stdoutWriter(turn: Turn; buf: seq[byte]) =
|
||||||
## Blocking write to stdout.
|
## Blocking write to stdout.
|
||||||
let n = writeBytes(stdout, buf, 0, buf.len)
|
let n = writeBytes(stdout, buf, 0, buf.len)
|
||||||
flushFile(stdout)
|
flushFile(stdout)
|
||||||
|
@ -343,7 +349,7 @@ when defined(posix):
|
||||||
initialCap: localDataspace,
|
initialCap: localDataspace,
|
||||||
initialOid: 0.Oid.some,
|
initialOid: 0.Oid.some,
|
||||||
)
|
)
|
||||||
spawnRelay("stdio", turn, opts) do (turn: var Turn; relay: Relay):
|
spawnRelay("stdio", turn, opts) do (turn: Turn; relay: Relay):
|
||||||
let
|
let
|
||||||
facet = turn.facet
|
facet = turn.facet
|
||||||
fd = stdin.getOsFileHandle()
|
fd = stdin.getOsFileHandle()
|
||||||
|
@ -353,7 +359,7 @@ when defined(posix):
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
let entity = StdioEntity(
|
let entity = StdioEntity(
|
||||||
facet: turn.facet, relay: relay, stdin: newAsyncFile(FD fd))
|
facet: turn.facet, relay: relay, stdin: newAsyncFile(FD fd))
|
||||||
onStop(entity.facet) do (turn: var Turn):
|
onStop(entity.facet) do (turn: Turn):
|
||||||
entity.alive = false
|
entity.alive = false
|
||||||
close(entity.stdin)
|
close(entity.stdin)
|
||||||
# Close stdin to remove it from the ioqueue
|
# Close stdin to remove it from the ioqueue
|
||||||
|
@ -365,7 +371,7 @@ when defined(posix):
|
||||||
resolved: localDataspace.accepted,
|
resolved: localDataspace.accepted,
|
||||||
))
|
))
|
||||||
|
|
||||||
proc connectStdio*(turn: var Turn; ds: Cap) =
|
proc connectStdio*(turn: Turn; ds: Cap) =
|
||||||
## Connect to an external dataspace over stdin and stdout.
|
## Connect to an external dataspace over stdin and stdout.
|
||||||
connectTransport(turn, ds, transportAddress.Stdio())
|
connectTransport(turn, ds, transportAddress.Stdio())
|
||||||
|
|
||||||
|
@ -382,33 +388,35 @@ when defined(posix):
|
||||||
|
|
||||||
SocketEntity = TcpEntity | UnixEntity
|
SocketEntity = TcpEntity | UnixEntity
|
||||||
|
|
||||||
method message(entity: SocketEntity; turn: var Turn; ass: AssertionRef) =
|
method message(entity: SocketEntity; turn: Turn; ass: AssertionRef) =
|
||||||
if ass.value.preservesTo(ForceDisconnect).isSome:
|
if ass.value.preservesTo(ForceDisconnect).isSome:
|
||||||
entity.alive = false
|
entity.alive = false
|
||||||
|
|
||||||
template bootSocketEntity() {.dirty.} =
|
template bootSocketEntity() {.dirty.} =
|
||||||
proc setup(turn: var Turn) {.closure.} =
|
proc setup(turn: Turn) {.closure.} =
|
||||||
proc kill(turn: var Turn) =
|
proc kill(turn: Turn) =
|
||||||
if entity.alive:
|
if entity.alive:
|
||||||
entity.alive = false
|
entity.alive = false
|
||||||
close(entity.sock)
|
close(entity.sock)
|
||||||
onStop(turn, kill)
|
onStop(turn, kill)
|
||||||
publish(turn, ds, TransportConnection(
|
var ass = TransportConnection(
|
||||||
`addr`: ta.toPreserves,
|
`addr`: ta.toPreserves,
|
||||||
control: newCap(entity, turn),
|
control: newCap(entity, turn),
|
||||||
resolved: entity.relay.peer.accepted,
|
resolved: entity.relay.peer.accepted,
|
||||||
))
|
)
|
||||||
|
publish(turn, ds, ass)
|
||||||
run(entity.relay.facet, setup)
|
run(entity.relay.facet, setup)
|
||||||
let buf = new seq[byte]
|
let buf = new seq[byte]
|
||||||
entity.alive = true
|
entity.alive = true
|
||||||
while entity.alive:
|
while entity.alive:
|
||||||
buf[].setLen(0x1000)
|
buf[].setLen(0x1000)
|
||||||
let n = read(entity.sock, buf)
|
let n = read(entity.sock, buf)
|
||||||
if n < 0: raiseOSError(osLastError())
|
if n > 0:
|
||||||
elif n == 0:
|
|
||||||
stopActor(entity.facet)
|
|
||||||
else:
|
|
||||||
entity.relay.recv(buf[], 0..<n)
|
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
|
# the socket closes when the actor is stopped
|
||||||
|
|
||||||
proc boot(entity: TcpEntity; ta: transportAddress.Tcp; ds: Cap) {.asyncio.} =
|
proc boot(entity: TcpEntity; ta: transportAddress.Tcp; ds: Cap) {.asyncio.} =
|
||||||
|
@ -420,24 +428,25 @@ when defined(posix):
|
||||||
bootSocketEntity()
|
bootSocketEntity()
|
||||||
|
|
||||||
template spawnSocketRelay() {.dirty.} =
|
template spawnSocketRelay() {.dirty.} =
|
||||||
proc writeConn(turn: var Turn; buf: seq[byte]) =
|
proc writeConn(turn: Turn; buf: seq[byte]) =
|
||||||
discard trampoline:
|
if entity.alive:
|
||||||
whelp write(entity.sock, buf)
|
discard trampoline:
|
||||||
|
whelp write(entity.sock, buf)
|
||||||
var ops = RelayActorOptions(
|
var ops = RelayActorOptions(
|
||||||
packetWriter: writeConn,
|
packetWriter: writeConn,
|
||||||
initialOid: 0.Oid.some,
|
initialOid: 0.Oid.some,
|
||||||
)
|
)
|
||||||
spawnRelay("socket", turn, ops) do (turn: var Turn; relay: Relay):
|
spawnRelay("socket", turn, ops) do (turn: Turn; relay: Relay):
|
||||||
entity.facet = turn.facet
|
entity.facet = turn.facet
|
||||||
entity.relay = relay
|
entity.relay = relay
|
||||||
discard trampoline:
|
discard trampoline:
|
||||||
whelp boot(entity, ta, ds)
|
whelp boot(entity, ta, ds)
|
||||||
|
|
||||||
proc connectTransport(turn: var Turn; ds: Cap; ta: transportAddress.Tcp) =
|
proc connectTransport(turn: Turn; ds: Cap; ta: transportAddress.Tcp) =
|
||||||
let entity = TcpEntity()
|
let entity = TcpEntity()
|
||||||
spawnSocketRelay()
|
spawnSocketRelay()
|
||||||
|
|
||||||
proc connectTransport(turn: var Turn; ds: Cap; ta: transportAddress.Unix) =
|
proc connectTransport(turn: Turn; ds: Cap; ta: transportAddress.Unix) =
|
||||||
let entity = UnixEntity()
|
let entity = UnixEntity()
|
||||||
spawnSocketRelay()
|
spawnSocketRelay()
|
||||||
|
|
||||||
|
@ -452,14 +461,14 @@ elif defined(solo5):
|
||||||
conn: Connection
|
conn: Connection
|
||||||
decoder: BufferedDecoder
|
decoder: BufferedDecoder
|
||||||
|
|
||||||
method message(entity: TcpEntity; turn: var Turn; ass: AssertionRef) =
|
method message(entity: TcpEntity; turn: Turn; ass: AssertionRef) =
|
||||||
if ass.value.preservesTo(ForceDisconnect).isSome:
|
if ass.value.preservesTo(ForceDisconnect).isSome:
|
||||||
entity.conn.abort()
|
entity.conn.abort()
|
||||||
|
|
||||||
proc connectTransport(turn: var Turn; ds: Cap; ta: transportAddress.Tcp) =
|
proc connectTransport(turn: Turn; ds: Cap; ta: transportAddress.Tcp) =
|
||||||
let entity = TcpEntity(facet: turn.facet)
|
let entity = TcpEntity(facet: turn.facet)
|
||||||
|
|
||||||
proc writeConn(turn: var Turn; buf: seq[byte]) =
|
proc writeConn(turn: Turn; buf: seq[byte]) =
|
||||||
assert not entity.conn.isNil
|
assert not entity.conn.isNil
|
||||||
entity.conn.batch:
|
entity.conn.batch:
|
||||||
entity.conn.send(buf)
|
entity.conn.send(buf)
|
||||||
|
@ -467,7 +476,7 @@ elif defined(solo5):
|
||||||
packetWriter: writeConn,
|
packetWriter: writeConn,
|
||||||
initialOid: 0.Oid.some,
|
initialOid: 0.Oid.some,
|
||||||
)
|
)
|
||||||
spawnRelay("socket", turn, ops) do (turn: var Turn; relay: Relay):
|
spawnRelay("socket", turn, ops) do (turn: Turn; relay: Relay):
|
||||||
entity.facet = turn.facet
|
entity.facet = turn.facet
|
||||||
entity.relay = relay
|
entity.relay = relay
|
||||||
|
|
||||||
|
@ -486,10 +495,10 @@ elif defined(solo5):
|
||||||
var preconn = newPreconnection(
|
var preconn = newPreconnection(
|
||||||
remote=[ep], transport=tp.some)
|
remote=[ep], transport=tp.some)
|
||||||
entity.conn = preconn.initiate()
|
entity.conn = preconn.initiate()
|
||||||
entity.facet.onStop do (turn: var Turn):
|
entity.facet.onStop do (turn: Turn):
|
||||||
entity.conn.close()
|
entity.conn.close()
|
||||||
entity.conn.onConnectionError do (err: ref Exception):
|
entity.conn.onConnectionError do (err: ref Exception):
|
||||||
run(entity.facet) do (turn: var Turn):
|
run(entity.facet) do (turn: Turn):
|
||||||
terminate(turn, err)
|
terminate(turn, err)
|
||||||
entity.conn.onClosed():
|
entity.conn.onClosed():
|
||||||
stop(entity.facet)
|
stop(entity.facet)
|
||||||
|
@ -500,7 +509,7 @@ elif defined(solo5):
|
||||||
else:
|
else:
|
||||||
entity.conn.receive()
|
entity.conn.receive()
|
||||||
entity.conn.onReady do ():
|
entity.conn.onReady do ():
|
||||||
entity.facet.run do (turn: var Turn):
|
entity.facet.run do (turn: Turn):
|
||||||
publish(turn, ds, TransportConnection(
|
publish(turn, ds, TransportConnection(
|
||||||
`addr`: ta.toPreserves,
|
`addr`: ta.toPreserves,
|
||||||
control: newCap(entity, turn),
|
control: newCap(entity, turn),
|
||||||
|
@ -508,7 +517,7 @@ elif defined(solo5):
|
||||||
))
|
))
|
||||||
entity.conn.receive()
|
entity.conn.receive()
|
||||||
|
|
||||||
proc walk(turn: var Turn; ds, origin: Cap; route: Route; transOff, stepOff: int) =
|
proc walk(turn: Turn; ds, origin: Cap; route: Route; transOff, stepOff: int) =
|
||||||
if stepOff < route.pathSteps.len:
|
if stepOff < route.pathSteps.len:
|
||||||
let
|
let
|
||||||
step = route.pathSteps[stepOff]
|
step = route.pathSteps[stepOff]
|
||||||
|
@ -531,7 +540,7 @@ proc walk(turn: var Turn; ds, origin: Cap; route: Route; transOff, stepOff: int)
|
||||||
resolved: origin.accepted,
|
resolved: origin.accepted,
|
||||||
))
|
))
|
||||||
|
|
||||||
proc connectRoute(turn: var Turn; ds: Cap; route: Route; transOff: int) =
|
proc connectRoute(turn: Turn; ds: Cap; route: Route; transOff: int) =
|
||||||
let rejectPat = TransportConnection ?: {
|
let rejectPat = TransportConnection ?: {
|
||||||
0: ?route.transports[transOff],
|
0: ?route.transports[transOff],
|
||||||
2: ?:Rejected,
|
2: ?:Rejected,
|
||||||
|
@ -547,37 +556,36 @@ proc connectRoute(turn: var Turn; ds: Cap; route: Route; transOff: int) =
|
||||||
2: ?:ResolvedAccepted,
|
2: ?:ResolvedAccepted,
|
||||||
}
|
}
|
||||||
onPublish(turn, ds, acceptPat) do (origin: Cap):
|
onPublish(turn, ds, acceptPat) do (origin: Cap):
|
||||||
walk(turn, ds, origin, route, transOff, 0)
|
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: var Turn; step: Value; origin, next: Cap) {.closure.}
|
type StepCallback = proc (turn: Turn; step: Value; origin: Cap; res: Resolved) {.closure.}
|
||||||
|
|
||||||
proc spawnStepResolver(turn: var Turn; ds: Cap; stepType: Value; cb: StepCallback) =
|
proc spawnStepResolver(turn: Turn; ds: Cap; stepType: Value; cb: StepCallback) =
|
||||||
let stepPat = grabRecord(stepType, grab())
|
let pat = observePattern(
|
||||||
let pat = ?Observe(pattern: ResolvedPathStep?:{1: stepPat}) ?? {0: grabLit(), 1: grab()}
|
ResolvedPathStep?:{1: grabRecord(stepType)},
|
||||||
|
{ @[0.toPreserves]: grabLit(), @[1.toPreserves]: grab() },
|
||||||
|
)
|
||||||
during(turn, ds, pat) do (origin: Cap; stepDetail: Literal[Value]):
|
during(turn, ds, pat) do (origin: Cap; stepDetail: Literal[Value]):
|
||||||
let step = toRecord(stepType, stepDetail.value)
|
proc duringCallback(turn: Turn; ass: Value; h: Handle): TurnAction =
|
||||||
proc duringCallback(turn: var Turn; ass: Value; h: Handle): TurnAction =
|
var res: Resolved
|
||||||
var res = ass.preservesTo Resolved
|
if res.fromPreserves ass:
|
||||||
if res.isSome:
|
cb(turn, stepDetail.value, origin, res)
|
||||||
if res.get.orKind == ResolvedKind.accepted and
|
proc action(turn: Turn) =
|
||||||
res.get.accepted.responderSession of Cap:
|
|
||||||
cb(turn, step, origin, res.get.accepted.responderSession.Cap)
|
|
||||||
else:
|
|
||||||
publish(turn, ds, ResolvedPathStep(
|
|
||||||
origin: origin, pathStep: step, resolved: res.get))
|
|
||||||
proc action(turn: var Turn) =
|
|
||||||
stop(turn)
|
stop(turn)
|
||||||
result = action
|
result = action
|
||||||
publish(turn, origin, Resolve(
|
publish(turn, origin, Resolve(
|
||||||
step: step, observer: newCap(turn, during(duringCallback))))
|
step: stepDetail.value, observer: newCap(turn, during(duringCallback))))
|
||||||
|
|
||||||
proc spawnRelays*(turn: var Turn; ds: Cap) =
|
proc spawnRelays*(turn: Turn; ds: Cap) =
|
||||||
## Spawn actors that manage routes and appeasing gatekeepers.
|
## Spawn actors that manage routes and appease gatekeepers.
|
||||||
let transPat = ?Observe(pattern: !TransportConnection) ?? { 0: grab() }
|
|
||||||
|
let transPat = observePattern(!TransportConnection, { @[0.toPreserves]: grab() })
|
||||||
# Use a generic pattern and type matching
|
# Use a generic pattern and type matching
|
||||||
# in the during handler because it is easy.
|
# in the during handler because it is easy.
|
||||||
|
|
||||||
when defined(posix):
|
when defined(posix) and not defined(nimdoc):
|
||||||
let stdioPat = ?Observe(pattern: TransportConnection?:{0: ?:Stdio})
|
let stdioPat = ?Observe(pattern: TransportConnection?:{0: ?:Stdio})
|
||||||
during(turn, ds, stdioPat) do:
|
during(turn, ds, stdioPat) do:
|
||||||
connectTransport(turn, ds, Stdio())
|
connectTransport(turn, ds, Stdio())
|
||||||
|
@ -596,26 +604,56 @@ proc spawnRelays*(turn: var Turn; ds: Cap) =
|
||||||
try: connectTransport(turn, ds, ta.value)
|
try: connectTransport(turn, ds, ta.value)
|
||||||
except exceptions.IOError as e:
|
except exceptions.IOError as e:
|
||||||
publish(turn, ds, TransportConnection(
|
publish(turn, ds, TransportConnection(
|
||||||
`addr`: ta.toPreserve,
|
`addr`: ta.toPreserves,
|
||||||
resolved: rejected(embed e),
|
resolved: rejected(embed e),
|
||||||
))
|
))
|
||||||
|
|
||||||
let resolvePat = ?Observe(pattern: !ResolvePath) ?? {0: grab()}
|
let resolvePat = observePattern(!ResolvePath, {@[0.toPreserves]: grab()})
|
||||||
during(turn, ds, resolvePat) do (route: Literal[Route]):
|
during(turn, ds, resolvePat) do (route: Literal[Route]):
|
||||||
for i, transAddr in route.value.transports:
|
for i, transAddr in route.value.transports:
|
||||||
connectRoute(turn, ds, route.value, i)
|
connectRoute(turn, ds, route.value, i)
|
||||||
|
|
||||||
spawnStepResolver(turn, ds, "ref".toSymbol) do (
|
spawnStepResolver(turn, ds, "ref".toSymbol) do (
|
||||||
turn: var Turn, step: Value, origin: Cap, next: Cap):
|
turn: Turn, step: Value, origin: Cap, res: Resolved):
|
||||||
publish(turn, ds, ResolvedPathStep(
|
publish(turn, ds, ResolvedPathStep(
|
||||||
origin: origin, pathStep: step, resolved: next.accepted))
|
origin: origin, pathStep: step, resolved: res))
|
||||||
|
|
||||||
type BootProc* = proc (turn: var Turn; ds: Cap) {.closure.}
|
type
|
||||||
|
BootProc* = proc (turn: Turn; ds: Cap) {.closure.}
|
||||||
|
|
||||||
proc resolve*(turn: var Turn; ds: Cap; route: Route; bootProc: BootProc) =
|
proc resolve*(turn: Turn; ds: Cap; route: Route; bootProc: BootProc) =
|
||||||
## Resolve `route` within `ds` and call `bootProc` with resolved capabilities.
|
## Resolve `route` within `ds` and call `bootProc` with resolved capabilities.
|
||||||
|
## If a resolved route is retracted then `bootProc` will be called again with
|
||||||
|
## the next available route.
|
||||||
|
var
|
||||||
|
destinations: OrderedSet[Cap]
|
||||||
|
activeDest: Cap
|
||||||
|
activeActor: Actor
|
||||||
|
proc bootActor(turn: Turn) =
|
||||||
|
assert activeActor.isNil
|
||||||
|
assert activeDest.isNil
|
||||||
|
for d in destinations:
|
||||||
|
activeDest = d
|
||||||
|
break
|
||||||
|
if not activeDest.isNil:
|
||||||
|
activeActor = linkActor(turn, "resolver") do (turn: Turn):
|
||||||
|
onStop(turn) do (turn: Turn):
|
||||||
|
activeActor = nil
|
||||||
|
activeDest = nil
|
||||||
|
bootActor(turn)
|
||||||
|
bootProc(turn, activeDest)
|
||||||
during(turn, ds, ResolvePath ?: {0: ?route, 3: ?:ResolvedAccepted}) do (dst: Cap):
|
during(turn, ds, ResolvePath ?: {0: ?route, 3: ?:ResolvedAccepted}) do (dst: Cap):
|
||||||
bootProc(turn, dst)
|
destinations.incl dst
|
||||||
|
if activeDest.isNil:
|
||||||
|
bootActor(turn)
|
||||||
|
do:
|
||||||
|
destinations.excl dst
|
||||||
|
|
||||||
|
proc resolve*(turn: Turn; route: Route; bootProc: BootProc) =
|
||||||
|
## Resolve `route` and call `bootProc` with resolved capability.
|
||||||
|
let ds = turn.newDataspace()
|
||||||
|
resolve(turn, ds, route, bootProc)
|
||||||
|
spawnRelays(turn, ds)
|
||||||
|
|
||||||
when defined(posix):
|
when defined(posix):
|
||||||
const defaultRoute* = "<route [<stdio>]>"
|
const defaultRoute* = "<route [<stdio>]>"
|
||||||
|
@ -626,28 +664,14 @@ when defined(posix):
|
||||||
## fallack to a defaultRoute_.
|
## fallack to a defaultRoute_.
|
||||||
## See https://git.syndicate-lang.org/syndicate-lang/syndicate-protocols/raw/branch/main/schemas/gatekeeper.prs.
|
## See https://git.syndicate-lang.org/syndicate-lang/syndicate-protocols/raw/branch/main/schemas/gatekeeper.prs.
|
||||||
var text = getEnv("SYNDICATE_ROUTE", defaultRoute)
|
var text = getEnv("SYNDICATE_ROUTE", defaultRoute)
|
||||||
if text == "":
|
try:
|
||||||
var tx = (getEnv("XDG_RUNTIME_DIR", "/run/user/1000") / "dataspace").toPreserves
|
if result.fromPreserves text.parsePreserves: return
|
||||||
result.transports = @[initRecord("unix", tx)]
|
except ValueError: discard
|
||||||
result.pathSteps = @[capabilities.mint().toPreserves]
|
raise newException(ValueError, "failed to parse $SYNDICATE_ROUTE " & $text)
|
||||||
else:
|
|
||||||
var pr = parsePreserves(text)
|
|
||||||
if not result.fromPreserves(pr):
|
|
||||||
raise newException(ValueError, "failed to parse $SYNDICATE_ROUTE " & $pr)
|
|
||||||
|
|
||||||
proc resolveEnvironment*(turn: var Turn; bootProc: BootProc) =
|
proc resolveEnvironment*(turn: Turn; bootProc: BootProc) =
|
||||||
## Resolve a capability from the calling environment
|
## Resolve a capability from the calling environment
|
||||||
## and call `bootProc`. See envRoute_.
|
## and call `bootProc`. See envRoute_.
|
||||||
var resolved = false
|
resolve(turn, envRoute(), bootProc)
|
||||||
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
|
# TODO: define a runActor that comes preloaded with relaying
|
||||||
|
|
|
@ -3,38 +3,35 @@
|
||||||
|
|
||||||
## https://git.syndicate-lang.org/syndicate-lang/syndicate-rkt/src/commit/90c4c60699069b496491b81ee63b5a45ffd638cb/syndicate/HOWITWORKS.md
|
## https://git.syndicate-lang.org/syndicate-lang/syndicate-rkt/src/commit/90c4c60699069b496491b81ee63b5a45ffd638cb/syndicate/HOWITWORKS.md
|
||||||
|
|
||||||
import std/[assertions, hashes, options, sets, tables]
|
import
|
||||||
import preserves
|
std/[assertions, hashes, options, sets, tables],
|
||||||
import ./actors, ./bags, ./patterns
|
pkg/preserves,
|
||||||
import ./protocols/dataspacePatterns
|
./actors, ./bags, ./patterns,
|
||||||
|
./protocols/dataspacePatterns
|
||||||
|
|
||||||
type
|
type
|
||||||
DCompound = dataspacePatterns.DCompound
|
|
||||||
Pattern = dataspacePatterns.Pattern
|
Pattern = dataspacePatterns.Pattern
|
||||||
Path = seq[Value]
|
Path = seq[Value]
|
||||||
ClassKind = enum classNone, classRecord, classSequence, classDictionary
|
ClassKind = enum classNone, classRecord, classSequence, classDictionary
|
||||||
Class = object
|
Class = object
|
||||||
kind: ClassKind
|
kind: ClassKind
|
||||||
label: Value
|
label: Value
|
||||||
arity: int
|
|
||||||
|
|
||||||
func classOf(v: Value): Class =
|
func classOf(v: Value): Class =
|
||||||
case v.kind
|
case v.kind
|
||||||
of pkRecord: Class(kind: classRecord, label: v.label, arity: v.arity)
|
of pkRecord: Class(kind: classRecord, label: v.label)
|
||||||
of pkSequence: Class(kind: classSequence, arity: v.len)
|
of pkSequence: Class(kind: classSequence)
|
||||||
of pkDictionary: Class(kind: classDictionary)
|
of pkDictionary: Class(kind: classDictionary)
|
||||||
else: Class(kind: classNone)
|
else: Class(kind: classNone)
|
||||||
|
|
||||||
proc classOf(p: Pattern): Class =
|
proc classOf(p: Pattern): Class =
|
||||||
if p.orKind == PatternKind.DCompound:
|
if p.orKind == PatternKind.group:
|
||||||
case p.dcompound.orKind
|
case p.group.type.orKind
|
||||||
of DCompoundKind.rec:
|
of GroupTypeKind.rec:
|
||||||
Class(kind: classRecord,
|
Class(kind: classRecord, label: p.group.`type`.rec.label)
|
||||||
label: p.dcompound.rec.label,
|
of GroupTypeKind.arr:
|
||||||
arity: p.dcompound.rec.fields.len)
|
Class(kind: classSequence)
|
||||||
of DCompoundKind.arr:
|
of GroupTypeKind.dict:
|
||||||
Class(kind: classSequence, arity: p.dcompound.arr.items.len)
|
|
||||||
of DCompoundKind.dict:
|
|
||||||
Class(kind: classDictionary)
|
Class(kind: classDictionary)
|
||||||
else:
|
else:
|
||||||
Class(kind: classNone)
|
Class(kind: classNone)
|
||||||
|
@ -67,15 +64,16 @@ func isEmpty(cont: Continuation): bool =
|
||||||
type
|
type
|
||||||
ContinuationProc = proc (c: Continuation; v: Value) {.closure.}
|
ContinuationProc = proc (c: Continuation; v: Value) {.closure.}
|
||||||
LeafProc = proc (l: Leaf; v: Value) {.closure.}
|
LeafProc = proc (l: Leaf; v: Value) {.closure.}
|
||||||
ObserverProc = proc (turn: var Turn; group: ObserverGroup; vs: seq[Value]) {.closure.}
|
ObserverProc = proc (turn: Turn; group: ObserverGroup; vs: seq[Value]) {.closure.}
|
||||||
|
|
||||||
proc getLeaves(cont: Continuation; constPaths: Paths): LeafMap =
|
proc getLeaves(cont: Continuation; presentPaths, constPaths: Paths): LeafMap =
|
||||||
result = cont.leafMap.getOrDefault(constPaths)
|
result = cont.leafMap.getOrDefault(constPaths)
|
||||||
if result.isNil:
|
if result.isNil:
|
||||||
new result
|
new result
|
||||||
cont.leafMap[constPaths] = result
|
cont.leafMap[constPaths] = result
|
||||||
assert not cont.isEmpty
|
assert not cont.isEmpty
|
||||||
for ass in cont.cache:
|
for ass in cont.cache:
|
||||||
|
# TODO: check presence
|
||||||
let key = projectPaths(ass, constPaths)
|
let key = projectPaths(ass, constPaths)
|
||||||
if key.isSome:
|
if key.isSome:
|
||||||
var leaf = result.getOrDefault(get key)
|
var leaf = result.getOrDefault(get key)
|
||||||
|
@ -114,10 +112,10 @@ proc top(stack: TermStack): Value =
|
||||||
assert stack.len > 0
|
assert stack.len > 0
|
||||||
stack[stack.high]
|
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) =
|
modCont: ContinuationProc; modLeaf: LeafProc; modObs: ObserverProc) =
|
||||||
|
|
||||||
proc walk(cont: Continuation; turn: var Turn) =
|
proc walk(cont: Continuation; turn: Turn) =
|
||||||
modCont(cont, outerValue)
|
modCont(cont, outerValue)
|
||||||
for constPaths, constValMap in cont.leafMap.pairs:
|
for constPaths, constValMap in cont.leafMap.pairs:
|
||||||
let constVals = projectPaths(outerValue, constPaths)
|
let constVals = projectPaths(outerValue, constPaths)
|
||||||
|
@ -142,7 +140,7 @@ proc modify(node: Node; turn: var Turn; outerValue: Value; event: EventKind;
|
||||||
constValMap.del(get constVals)
|
constValMap.del(get constVals)
|
||||||
|
|
||||||
|
|
||||||
proc walk(node: Node; turn: var Turn; termStack: TermStack) =
|
proc walk(node: Node; turn: Turn; termStack: TermStack) =
|
||||||
walk(node.continuation, turn)
|
walk(node.continuation, turn)
|
||||||
for selector, table in node.edges:
|
for selector, table in node.edges:
|
||||||
let
|
let
|
||||||
|
@ -165,25 +163,13 @@ proc getOrNew[A, B, C](t: var Table[A, TableRef[B, C]], k: A): TableRef[B, C] =
|
||||||
result = newTable[B, C]()
|
result = newTable[B, C]()
|
||||||
t[k] = result
|
t[k] = result
|
||||||
|
|
||||||
iterator pairs(dc: DCompound): (Value, Pattern) =
|
|
||||||
case dc.orKind
|
|
||||||
of DCompoundKind.rec:
|
|
||||||
for i, p in dc.rec.fields:
|
|
||||||
yield (i.toPreserves, p,)
|
|
||||||
of DCompoundKind.arr:
|
|
||||||
for i, p in dc.arr.items:
|
|
||||||
yield (i.toPreserves, p,)
|
|
||||||
of DCompoundKind.dict:
|
|
||||||
for pair in dc.dict.entries.pairs:
|
|
||||||
yield pair
|
|
||||||
|
|
||||||
proc extendWalk(node: Node; popCount: Natural; stepIndex: Value; pat: Pattern; path: var Path): tuple[popCount: Natural, nextNode: Node] =
|
proc extendWalk(node: Node; popCount: Natural; stepIndex: Value; pat: Pattern; path: var Path): tuple[popCount: Natural, nextNode: Node] =
|
||||||
case pat.orKind
|
case pat.orKind
|
||||||
of PatternKind.DDiscard, PatternKind.DLit:
|
of PatternKind.`discard`, PatternKind.lit:
|
||||||
result = (popCount, node)
|
result = (popCount, node)
|
||||||
of PatternKind.DBind:
|
of PatternKind.`bind`:
|
||||||
result = extendWalk(node, popCount, stepIndex, pat.dbind.pattern, path)
|
result = extendWalk(node, popCount, stepIndex, pat.`bind`.pattern, path)
|
||||||
of PatternKind.DCompound:
|
of PatternKind.`group`:
|
||||||
let
|
let
|
||||||
selector: Selector = (popCount, stepIndex,)
|
selector: Selector = (popCount, stepIndex,)
|
||||||
table = node.edges.getOrNew(selector)
|
table = node.edges.getOrNew(selector)
|
||||||
|
@ -198,7 +184,7 @@ proc extendWalk(node: Node; popCount: Natural; stepIndex: Value; pat: Pattern; p
|
||||||
if v.isSome and class == classOf(get v):
|
if v.isSome and class == classOf(get v):
|
||||||
result.nextNode.continuation.cache.incl a
|
result.nextNode.continuation.cache.incl a
|
||||||
result.popCount = 0
|
result.popCount = 0
|
||||||
for step, p in pat.dcompound.pairs:
|
for step, p in pat.group.entries:
|
||||||
add(path, step)
|
add(path, step)
|
||||||
result = extendWalk(result.nextNode, result.popCount, step, p, path)
|
result = extendWalk(result.nextNode, result.popCount, step, p, path)
|
||||||
discard pop(path)
|
discard pop(path)
|
||||||
|
@ -227,11 +213,11 @@ proc getEndpoints(leaf: Leaf; capturePaths: Paths): ObserverGroup =
|
||||||
if captures.isSome:
|
if captures.isSome:
|
||||||
discard result.cachedCaptures.change(get captures, +1)
|
discard result.cachedCaptures.change(get captures, +1)
|
||||||
|
|
||||||
proc add*(index: var Index; turn: var Turn; pattern: Pattern; observer: Cap) =
|
proc add*(index: var Index; turn: Turn; pattern: Pattern; observer: Cap) =
|
||||||
let
|
let
|
||||||
cont = index.root.extend(pattern)
|
cont = index.root.extend(pattern)
|
||||||
analysis = analyse pattern
|
analysis = analyse pattern
|
||||||
constValMap = cont.getLeaves(analysis.constPaths)
|
constValMap = cont.getLeaves(analysis.presentPaths, analysis.constPaths)
|
||||||
leaf = constValMap.getLeaf(analysis.constValues)
|
leaf = constValMap.getLeaf(analysis.constValues)
|
||||||
endpoints = leaf.getEndpoints(analysis.capturePaths)
|
endpoints = leaf.getEndpoints(analysis.capturePaths)
|
||||||
# TODO if endpoints.cachedCaptures.len > 0:
|
# TODO if endpoints.cachedCaptures.len > 0:
|
||||||
|
@ -240,7 +226,7 @@ proc add*(index: var Index; turn: var Turn; pattern: Pattern; observer: Cap) =
|
||||||
captureMap[capture] = publish(turn, observer, capture)
|
captureMap[capture] = publish(turn, observer, capture)
|
||||||
endpoints.observers[observer] = captureMap
|
endpoints.observers[observer] = captureMap
|
||||||
|
|
||||||
proc remove*(index: var Index; turn: var Turn; pattern: Pattern; observer: Cap) =
|
proc remove*(index: var Index; turn: Turn; pattern: Pattern; observer: Cap) =
|
||||||
let
|
let
|
||||||
cont = index.root.extend(pattern)
|
cont = index.root.extend(pattern)
|
||||||
analysis = analyse pattern
|
analysis = analyse pattern
|
||||||
|
@ -260,7 +246,7 @@ proc remove*(index: var Index; turn: var Turn; pattern: Pattern; observer: Cap)
|
||||||
if constValMap.len == 0:
|
if constValMap.len == 0:
|
||||||
cont.leafMap.del(analysis.constPaths)
|
cont.leafMap.del(analysis.constPaths)
|
||||||
|
|
||||||
proc adjustAssertion(index: var Index; turn: var Turn; outerValue: Value; delta: int): bool =
|
proc adjustAssertion(index: var Index; turn: Turn; outerValue: Value; delta: int): bool =
|
||||||
case index.allAssertions.change(outerValue, delta)
|
case index.allAssertions.change(outerValue, delta)
|
||||||
of cdAbsentToPresent:
|
of cdAbsentToPresent:
|
||||||
result = true
|
result = true
|
||||||
|
@ -268,7 +254,7 @@ proc adjustAssertion(index: var Index; turn: var Turn; outerValue: Value; delta:
|
||||||
c.cache.incl(v)
|
c.cache.incl(v)
|
||||||
proc modLeaf(l: Leaf; v: Value) =
|
proc modLeaf(l: Leaf; v: Value) =
|
||||||
l.cache.incl(v)
|
l.cache.incl(v)
|
||||||
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
|
proc modObserver(turn: Turn; group: ObserverGroup; vs: seq[Value]) =
|
||||||
let change = group.cachedCaptures.change(vs, +1)
|
let change = group.cachedCaptures.change(vs, +1)
|
||||||
if change == cdAbsentToPresent:
|
if change == cdAbsentToPresent:
|
||||||
for (observer, captureMap) in group.observers.pairs:
|
for (observer, captureMap) in group.observers.pairs:
|
||||||
|
@ -281,7 +267,7 @@ proc adjustAssertion(index: var Index; turn: var Turn; outerValue: Value; delta:
|
||||||
c.cache.excl(v)
|
c.cache.excl(v)
|
||||||
proc modLeaf(l: Leaf; v: Value) =
|
proc modLeaf(l: Leaf; v: Value) =
|
||||||
l.cache.excl(v)
|
l.cache.excl(v)
|
||||||
proc modObserver(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
|
proc modObserver(turn: Turn; group: ObserverGroup; vs: seq[Value]) =
|
||||||
if group.cachedCaptures.change(vs, -1) == cdPresentToAbsent:
|
if group.cachedCaptures.change(vs, -1) == cdPresentToAbsent:
|
||||||
for (observer, captureMap) in group.observers.pairs:
|
for (observer, captureMap) in group.observers.pairs:
|
||||||
var h: Handle
|
var h: Handle
|
||||||
|
@ -293,12 +279,12 @@ proc adjustAssertion(index: var Index; turn: var Turn; outerValue: Value; delta:
|
||||||
proc continuationNoop(c: Continuation; v: Value) = discard
|
proc continuationNoop(c: Continuation; v: Value) = discard
|
||||||
proc leafNoop(l: Leaf; v: Value) = discard
|
proc leafNoop(l: Leaf; v: Value) = discard
|
||||||
|
|
||||||
proc add*(index: var Index; turn: var Turn; v: Value): bool =
|
proc add*(index: var Index; turn: Turn; v: Value): bool =
|
||||||
adjustAssertion(index, turn, v, +1)
|
adjustAssertion(index, turn, v, +1)
|
||||||
proc remove*(index: var Index; turn: var Turn; v: Value): bool =
|
proc remove*(index: var Index; turn: Turn; v: Value): bool =
|
||||||
adjustAssertion(index, turn, v, -1)
|
adjustAssertion(index, turn, v, -1)
|
||||||
|
|
||||||
proc deliverMessage*(index: var Index; turn: var Turn; v: Value) =
|
proc deliverMessage*(index: var Index; turn: Turn; v: Value) =
|
||||||
proc observersCb(turn: var Turn; group: ObserverGroup; vs: seq[Value]) =
|
proc observersCb(turn: Turn; group: ObserverGroup; vs: seq[Value]) =
|
||||||
for observer in group.observers.keys: message(turn, observer, vs)
|
for observer in group.observers.keys: message(turn, observer, vs)
|
||||||
index.root.modify(turn, v, messageEvent, continuationNoop, leafNoop, observersCb)
|
index.root.modify(turn, v, messageEvent, continuationNoop, leafNoop, observersCb)
|
||||||
|
|
|
@ -1,12 +1,61 @@
|
||||||
# Package
|
# Emulate Nimble from CycloneDX data at sbom.json.
|
||||||
|
|
||||||
version = "20240402"
|
import std/json
|
||||||
author = "Emery Hemingway"
|
|
||||||
description = "Syndicated actors for conversational concurrency"
|
|
||||||
license = "Unlicense"
|
|
||||||
srcDir = "src"
|
|
||||||
|
|
||||||
|
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 "https://github.com/ehmry/hashlib.git >= 20231130", "nim >= 2.0.0", "https://git.syndicate-lang.org/ehmry/preserves-nim.git >= 20240312", "https://github.com/ehmry/nim-sys.git#4ef3b624db86e331ba334e705c1aa235d55b05e1", "https://git.sr.ht/~ehmry/nim_taps >= 20240402"
|
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
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
include_rules
|
include_rules
|
||||||
: foreach *.prs |> !preserves_schema_nim |> | {schema}
|
: foreach *.prs |> !preserves-schema-nim |> | {schema}
|
||||||
: foreach t*.nim | ../../preserves-nim/<tests> {schema} $(SYNDICATE_PROTOCOL) |> !nim_run |> | ../<test>
|
: 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>
|
# : foreach solo5*.nim | ../../taps/<sources> ../../preserves-nim/<tests> {schema} $(SYNDICATE_PROTOCOL) |> !nim_solo5_spt |> | ../<test>
|
||||||
|
|
||||||
|
: | $(NIM_GROUPS) |> $(NIM) $(NIM_FLAGS) dump > %o |> nim.dump
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
|
import
|
||||||
|
std/solo5,
|
||||||
|
pkg/taps,
|
||||||
|
pkg/preserves,
|
||||||
|
syndicate, syndicate/relays
|
||||||
|
|
||||||
|
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"
|
|
@ -2,8 +2,10 @@
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[oserrors, parseopt, posix, strutils]
|
import std/[oserrors, parseopt, posix, strutils]
|
||||||
import pkg/sys/[files, handles, ioqueue]
|
import
|
||||||
import preserves, syndicate, syndicate/relays
|
pkg/sys/[files, handles, ioqueue],
|
||||||
|
pkg/preserves,
|
||||||
|
syndicate, syndicate/relays
|
||||||
|
|
||||||
type
|
type
|
||||||
Present {.preservesRecord: "Present".} = object
|
Present {.preservesRecord: "Present".} = object
|
||||||
|
@ -14,7 +16,7 @@ type
|
||||||
proc syncAndStop(facet: Facet; cap: Cap) =
|
proc syncAndStop(facet: Facet; cap: Cap) =
|
||||||
## Stop the actor responsible for `facet` after
|
## Stop the actor responsible for `facet` after
|
||||||
## synchronizing with `cap`.
|
## synchronizing with `cap`.
|
||||||
run(facet) do (turn: var Turn):
|
run(facet) do (turn: Turn):
|
||||||
sync(turn, cap, stopActor)
|
sync(turn, cap, stopActor)
|
||||||
|
|
||||||
proc readStdin(facet: Facet; ds: Cap; username: string) {.asyncio.} =
|
proc readStdin(facet: Facet; ds: Cap; username: string) {.asyncio.} =
|
||||||
|
@ -37,11 +39,11 @@ proc readStdin(facet: Facet; ds: Cap; username: string) {.asyncio.} =
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
var msg = buf[][0..<n].strip
|
var msg = buf[][0..<n].strip
|
||||||
proc send(turn: var Turn) =
|
proc send(turn: Turn) =
|
||||||
message(turn, ds, Says(who: username, what: msg))
|
message(turn, ds, Says(who: username, what: msg))
|
||||||
run(facet, send)
|
run(facet, send)
|
||||||
|
|
||||||
proc chat(turn: var Turn; ds: Cap; username: string) =
|
proc chat(turn: Turn; ds: Cap; username: string) =
|
||||||
during(turn, ds, ?:Present) do (who: string):
|
during(turn, ds, ?:Present) do (who: string):
|
||||||
echo who, " joined"
|
echo who, " joined"
|
||||||
do:
|
do:
|
||||||
|
@ -67,8 +69,8 @@ proc main =
|
||||||
if username == "":
|
if username == "":
|
||||||
stderr.writeLine "--user: unspecified"
|
stderr.writeLine "--user: unspecified"
|
||||||
else:
|
else:
|
||||||
runActor("chat") do (turn: var Turn):
|
runActor("chat") do (turn: Turn):
|
||||||
resolveEnvironment(turn) do (turn: var Turn; ds: Cap):
|
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
||||||
chat(turn, ds, username)
|
chat(turn, ds, username)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -1,103 +1,85 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/[options, tables, unittest]
|
import
|
||||||
|
std/[options, tables, unittest],
|
||||||
|
pkg/preserves, syndicate, syndicate/protocols/timer
|
||||||
|
|
||||||
import preserves, syndicate, syndicate/protocols/gatekeeper
|
suite "example":
|
||||||
|
var pat: Pattern
|
||||||
|
check pat.fromPreserves parsePreserves"""
|
||||||
|
<group <arr> {
|
||||||
|
0: <lit 1>
|
||||||
|
1: <bind <group <arr> {
|
||||||
|
0: <bind <_>>
|
||||||
|
1: <_>
|
||||||
|
}>>
|
||||||
|
2: <_>
|
||||||
|
}>
|
||||||
|
"""
|
||||||
|
|
||||||
import ./test_schema
|
const A = "[1 2 3]"
|
||||||
|
test A:
|
||||||
|
let v = parsePreserves A
|
||||||
|
check:
|
||||||
|
not pat.matches(v)
|
||||||
|
|
||||||
test "patterns":
|
const B = "[1 [2 3] 4]"
|
||||||
let
|
test B:
|
||||||
pat = ?Observe(pattern: !Foo) ?? {0: grab()}
|
|
||||||
text = """<rec Observe [<rec rec [<lit foo> <arr [<bind <_>> <_> <_>]>]> <_>]>"""
|
|
||||||
check($pat == text)
|
|
||||||
|
|
||||||
let
|
|
||||||
worte = @["alles", "in", "ordnung"]
|
|
||||||
observer = Observe(pattern: inject(?:Foo, { 0: ?worte })).toPreserves
|
|
||||||
have = capture(pat, observer).toPreserves.unpackLiterals
|
|
||||||
want = [worte.toPreserves].toPreserves
|
|
||||||
check(have == want)
|
|
||||||
|
|
||||||
type Obj {.preservesDictionary.} = object
|
|
||||||
a, b, c: int
|
|
||||||
|
|
||||||
test "dictionaries":
|
|
||||||
let pat = ?:Obj
|
|
||||||
var source = initDictionary(Cap)
|
|
||||||
source["b".toSymbol] = 2.toPreserves
|
|
||||||
source["c".toSymbol] = 3.toPreserves
|
|
||||||
source["a".toSymbol] = 1.toPreserves
|
|
||||||
|
|
||||||
let values = capture(pat, source)
|
|
||||||
check values.len == 3
|
|
||||||
check values[0] == 1.toPreserves
|
|
||||||
check values[1] == 2.toPreserves
|
|
||||||
check values[2] == 3.toPreserves
|
|
||||||
|
|
||||||
type
|
|
||||||
File {.preservesDictionary.} = object
|
|
||||||
name: string
|
|
||||||
path: string
|
|
||||||
size: BiggestInt
|
|
||||||
`type`: string
|
|
||||||
Files = Table[Symbol, File]
|
|
||||||
Fields = Table[Symbol, string]
|
|
||||||
|
|
||||||
Request {.preservesRecord: "request".} = object
|
|
||||||
seq: BiggestInt
|
|
||||||
fields: Fields
|
|
||||||
files: Files
|
|
||||||
|
|
||||||
test "literals":
|
|
||||||
const txt = """<rec request [<lit 3> <dict {artists: <lit "kyyyyym"> date: <lit "2023-10-14"> notes: <lit "Lots of stuff"> title: <lit "Domes show">}> <dict {front-cover: <dict {name: <lit "ADULT_TIME_Glielmi.jpg"> path: <lit "/tmp/652adad1b3d2b666dcc8d857.jpg"> size: <lit 255614> type: <lit "image/jpeg">}>}>]>"""
|
|
||||||
var pr = parsePreserves(txt)
|
|
||||||
|
|
||||||
var capture: Literal[Request]
|
|
||||||
check capture.fromPreserves(pr)
|
|
||||||
|
|
||||||
suite "captures":
|
|
||||||
for txt in [
|
|
||||||
"#f",
|
|
||||||
"#t",
|
|
||||||
"0",
|
|
||||||
"-1",
|
|
||||||
"foo",
|
|
||||||
"<foo>",
|
|
||||||
"[0, 1, 2]",
|
|
||||||
]:
|
|
||||||
test txt:
|
|
||||||
let
|
|
||||||
pr = parsePreserves txt
|
|
||||||
pat = grab pr
|
|
||||||
checkpoint $pat
|
|
||||||
check pat.matches pr
|
|
||||||
|
|
||||||
suite "protocol":
|
|
||||||
test "Observe":
|
|
||||||
let pat = ?:Observe
|
|
||||||
const text = """<rec Observe [<bind <_>> <bind <_>>]>"""
|
|
||||||
check $pat == text
|
|
||||||
|
|
||||||
test "later-than":
|
|
||||||
let
|
let
|
||||||
obsA = parsePreserves"""<Observe <rec later-than [<lit 1704113731.419243>]> #f>"""
|
v = parsePreserves B
|
||||||
obsB = parsePreserves"""<Observe <rec Observe [<rec rec [<lit later-than> <arr [<rec lit [<bind <_>>]>]>]> <_>]> #f>"""
|
c = parsePreserves "[[2 3] 2]"
|
||||||
patA = """<rec later-than [<lit 1704113731.419243>]>""".parsePreserves.preservesTo(Pattern).get
|
check pat.matches(v)
|
||||||
patB = """<rec Observe [<rec rec [<lit later-than> <arr [<rec lit [<bind <_>>]>]>]> <_>]>""".parsePreserves.preservesTo(Pattern).get
|
check pat.capture(v).toPreserves == c
|
||||||
|
|
||||||
patC = grab obsA
|
const C = "[1 [2] 5]"
|
||||||
|
test C:
|
||||||
|
let v = parsePreserves C
|
||||||
|
check:
|
||||||
|
not pat.matches(v)
|
||||||
|
|
||||||
test $patC:
|
const D = "[1 [2 3 4] 5]"
|
||||||
check patC.matches obsA
|
test D:
|
||||||
|
|
||||||
test $patB:
|
|
||||||
checkpoint $obsA
|
|
||||||
check patB.matches obsA
|
|
||||||
|
|
||||||
test "TransportConnection":
|
|
||||||
let
|
let
|
||||||
pat = TransportConnection ?: { 2: ?:Rejected}
|
v = parsePreserves D
|
||||||
text = """<rec connect-transport [<_> <_> <rec rejected [<bind <_>>]>]>"""
|
c = parsePreserves "[[2 3 4] 2]"
|
||||||
check $pat == text
|
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 = matchRecord("foo".toSymbol, matchDictionary({666.toPreserves: drop()}))
|
||||||
|
meta = pat.toPreserves.drop()
|
||||||
|
check $meta == "<group <rec group> {0: <group <rec rec> {0: <lit foo>}> 1: <group <dict> {0: <group <rec group> {0: <group <rec dict> {}> 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]"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import std/[streams, strutils, unittest]
|
import
|
||||||
|
std/[streams, strutils, unittest],
|
||||||
import preserves
|
pkg/preserves,
|
||||||
import syndicate/relays
|
syndicate/relays,
|
||||||
import syndicate/protocols/sturdy
|
syndicate/protocols/sturdy
|
||||||
|
|
||||||
type WireRef = sturdy.WireRef
|
type WireRef = sturdy.WireRef
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,34 @@
|
||||||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||||
# SPDX-License-Identifier: Unlicense
|
# SPDX-License-Identifier: Unlicense
|
||||||
|
|
||||||
import std/times
|
import
|
||||||
import syndicate, syndicate/actors/timers
|
std/times,
|
||||||
|
pkg/preserves,
|
||||||
|
syndicate, syndicate/drivers/timers
|
||||||
|
|
||||||
runActor("timer-test") do (turn: var Turn):
|
var passCount = 0
|
||||||
|
|
||||||
|
runActor("timer-test") do (turn: Turn):
|
||||||
let timers = newDataspace(turn)
|
let timers = newDataspace(turn)
|
||||||
spawnTimerActor(turn, timers)
|
spawnTimerDriver(turn, timers)
|
||||||
|
|
||||||
onPublish(turn, timers, ?LaterThan(seconds: 1356100000)):
|
onPublish(turn, timers, ?LaterThan(seconds: 1356100000)):
|
||||||
echo "now in 13th bʼakʼtun"
|
echo "now in 13th bʼakʼtun"
|
||||||
|
inc passCount
|
||||||
|
|
||||||
after(turn, timers, initDuration(seconds = 3)) do (turn: var Turn):
|
after(turn, timers, initDuration(seconds = 3)) do (turn: Turn):
|
||||||
echo "third timer expired"
|
echo "third timer expired"
|
||||||
stopActor(turn)
|
assert passCount == 3
|
||||||
|
inc passCount
|
||||||
|
|
||||||
after(turn, timers, initDuration(seconds = 1)) do (turn: var Turn):
|
after(turn, timers, initDuration(seconds = 1)) do (turn: Turn):
|
||||||
echo "first timer expired"
|
echo "first timer expired"
|
||||||
|
assert passCount == 1
|
||||||
|
inc passCount
|
||||||
|
|
||||||
after(turn, timers, initDuration(seconds = 2)) do (turn: var Turn):
|
after(turn, timers, initDuration(seconds = 2)) do (turn: Turn):
|
||||||
echo "second timer expired"
|
echo "second timer expired"
|
||||||
|
assert passCount == 2
|
||||||
|
inc passCount
|
||||||
|
|
||||||
|
doAssert passCount == 4, $passCount
|
||||||
|
|
Loading…
Reference in New Issue