forked from Orchid/orchid
partway towards commands
I got very confused and started mucking about with "spawn" when in fact all I needed was the "inline" extension type in orcx that allows the interpreter to expose custom constants.
This commit is contained in:
619
Cargo.lock
generated
619
Cargo.lock
generated
@@ -28,6 +28,15 @@ version = "0.2.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.18"
|
version = "0.6.18"
|
||||||
@@ -77,12 +86,28 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.102"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-event"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1222afd3d2bce3995035054046a279ae7aa154d70d0766cea050073f3fd7ddf"
|
||||||
|
dependencies = [
|
||||||
|
"loom",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-fn-stream"
|
name = "async-fn-stream"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -259,6 +284,30 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chacha20"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures 0.3.0",
|
||||||
|
"rand_core 0.10.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.43"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
|
||||||
|
dependencies = [
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-traits",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.54"
|
version = "4.5.54"
|
||||||
@@ -326,6 +375,12 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
@@ -335,6 +390,15 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
@@ -413,6 +477,12 @@ version = "0.1.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff"
|
checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foldhash"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foldhash"
|
name = "foldhash"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -525,6 +595,19 @@ dependencies = [
|
|||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generator"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"rustversion",
|
||||||
|
"windows",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
@@ -554,10 +637,24 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"r-efi",
|
"r-efi 5.3.0",
|
||||||
"wasip2",
|
"wasip2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi 6.0.0",
|
||||||
|
"rand_core 0.10.0",
|
||||||
|
"wasip2",
|
||||||
|
"wasip3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@@ -567,6 +664,15 @@ dependencies = [
|
|||||||
"ahash",
|
"ahash",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||||
|
dependencies = [
|
||||||
|
"foldhash 0.1.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.16.1"
|
version = "0.16.1"
|
||||||
@@ -575,7 +681,7 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"foldhash",
|
"foldhash 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -584,6 +690,36 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.65"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "id-arena"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "include_dir"
|
name = "include_dir"
|
||||||
version = "0.7.4"
|
version = "0.7.4"
|
||||||
@@ -611,6 +747,8 @@ checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -667,6 +805,12 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "leb128fmt"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.175"
|
version = "0.2.175"
|
||||||
@@ -699,6 +843,12 @@ dependencies = [
|
|||||||
"scopeguard",
|
"scopeguard",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "logwise"
|
name = "logwise"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
@@ -737,6 +887,28 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c980f57d231d31d90700b8a9440f42f237f09d8ad02e82636140e3d760b2768"
|
checksum = "1c980f57d231d31d90700b8a9440f42f237f09d8ad02e82636140e3d760b2768"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "loom"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"generator",
|
||||||
|
"scoped-tls",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchers"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
||||||
|
dependencies = [
|
||||||
|
"regex-automata",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.4"
|
||||||
@@ -862,12 +1034,22 @@ dependencies = [
|
|||||||
name = "orchid-api-traits"
|
name = "orchid-api-traits"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
"futures",
|
"futures",
|
||||||
"itertools",
|
"itertools",
|
||||||
"never",
|
"never",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "orchid-async-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"futures",
|
||||||
|
"itertools",
|
||||||
|
"task-local",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orchid-base"
|
name = "orchid-base"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -886,7 +1068,10 @@ dependencies = [
|
|||||||
"orchid-api",
|
"orchid-api",
|
||||||
"orchid-api-derive",
|
"orchid-api-derive",
|
||||||
"orchid-api-traits",
|
"orchid-api-traits",
|
||||||
|
"orchid-async-utils",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
|
"rand 0.10.0",
|
||||||
|
"rand_chacha 0.10.0",
|
||||||
"regex",
|
"regex",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"substack",
|
"substack",
|
||||||
@@ -899,9 +1084,9 @@ dependencies = [
|
|||||||
name = "orchid-extension"
|
name = "orchid-extension"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-event",
|
||||||
"async-fn-stream",
|
"async-fn-stream",
|
||||||
"async-once-cell",
|
"async-once-cell",
|
||||||
"bound",
|
|
||||||
"derive_destructure",
|
"derive_destructure",
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"futures",
|
"futures",
|
||||||
@@ -917,6 +1102,7 @@ dependencies = [
|
|||||||
"orchid-api",
|
"orchid-api",
|
||||||
"orchid-api-derive",
|
"orchid-api-derive",
|
||||||
"orchid-api-traits",
|
"orchid-api-traits",
|
||||||
|
"orchid-async-utils",
|
||||||
"orchid-base",
|
"orchid-base",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"pastey",
|
"pastey",
|
||||||
@@ -932,6 +1118,7 @@ dependencies = [
|
|||||||
name = "orchid-host"
|
name = "orchid-host"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-event",
|
||||||
"async-fn-stream",
|
"async-fn-stream",
|
||||||
"async-once-cell",
|
"async-once-cell",
|
||||||
"bound",
|
"bound",
|
||||||
@@ -962,8 +1149,10 @@ dependencies = [
|
|||||||
name = "orchid-std"
|
name = "orchid-std"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-event",
|
||||||
"async-fn-stream",
|
"async-fn-stream",
|
||||||
"async-once-cell",
|
"async-once-cell",
|
||||||
|
"chrono",
|
||||||
"futures",
|
"futures",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
"itertools",
|
"itertools",
|
||||||
@@ -1030,7 +1219,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-targets",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1060,6 +1249,16 @@ dependencies = [
|
|||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prettyplease"
|
||||||
|
version = "0.2.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.104",
|
||||||
|
"syn 2.0.112",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "priority"
|
name = "priority"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -1081,7 +1280,7 @@ version = "0.4.30"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1137,6 +1336,12 @@ version = "5.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "radium"
|
name = "radium"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@@ -1164,6 +1369,17 @@ dependencies = [
|
|||||||
"rand_core 0.9.3",
|
"rand_core 0.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
|
||||||
|
dependencies = [
|
||||||
|
"chacha20",
|
||||||
|
"getrandom 0.4.2",
|
||||||
|
"rand_core 0.10.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_chacha"
|
name = "rand_chacha"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -1184,6 +1400,16 @@ dependencies = [
|
|||||||
"rand_core 0.9.3",
|
"rand_core 0.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3e6af7f3e25ded52c41df4e0b1af2d047e45896c2f3281792ed68a1c243daedb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core 0.10.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
@@ -1202,6 +1428,12 @@ dependencies = [
|
|||||||
"getrandom 0.3.4",
|
"getrandom 0.3.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
@@ -1349,6 +1581,12 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped-tls"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@@ -1361,6 +1599,12 @@ version = "4.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.228"
|
version = "1.0.228"
|
||||||
@@ -1410,10 +1654,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures 0.2.16",
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@@ -1507,7 +1760,7 @@ checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 0.4.30",
|
"proc-macro2 0.4.30",
|
||||||
"quote 0.6.13",
|
"quote 0.6.13",
|
||||||
"unicode-xid",
|
"unicode-xid 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1593,6 +1846,15 @@ dependencies = [
|
|||||||
"syn 2.0.112",
|
"syn 2.0.112",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
@@ -1680,6 +1942,67 @@ dependencies = [
|
|||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project-lite",
|
||||||
|
"tracing-attributes",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-attributes"
|
||||||
|
version = "0.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.104",
|
||||||
|
"quote 1.0.42",
|
||||||
|
"syn 2.0.112",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-core"
|
||||||
|
version = "0.1.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
|
||||||
|
dependencies = [
|
||||||
|
"matchers",
|
||||||
|
"nu-ansi-term",
|
||||||
|
"once_cell",
|
||||||
|
"regex-automata",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "trait-set"
|
name = "trait-set"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@@ -1721,6 +2044,12 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unsync-pipe"
|
name = "unsync-pipe"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -1745,6 +2074,12 @@ version = "1.11.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
|
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
@@ -1773,7 +2108,16 @@ version = "1.0.1+wasi-0.2.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"wit-bindgen",
|
"wit-bindgen 0.46.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasip3"
|
||||||
|
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen 0.51.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1866,6 +2210,28 @@ dependencies = [
|
|||||||
"syn 2.0.112",
|
"syn 2.0.112",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-encoder"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||||
|
dependencies = [
|
||||||
|
"leb128fmt",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-metadata"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"indexmap",
|
||||||
|
"wasm-encoder",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm_safe_mutex"
|
name = "wasm_safe_mutex"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -1890,6 +2256,18 @@ dependencies = [
|
|||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasmparser"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"hashbrown 0.15.5",
|
||||||
|
"indexmap",
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.83"
|
version = "0.3.83"
|
||||||
@@ -1919,19 +2297,81 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.62.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-implement",
|
||||||
|
"windows-interface",
|
||||||
|
"windows-link",
|
||||||
|
"windows-result",
|
||||||
|
"windows-strings",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-implement"
|
||||||
|
version = "0.60.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.104",
|
||||||
|
"quote 1.0.42",
|
||||||
|
"syn 2.0.112",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-interface"
|
||||||
|
version = "0.59.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.104",
|
||||||
|
"quote 1.0.42",
|
||||||
|
"syn 2.0.112",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-link"
|
name = "windows-link"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-targets",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1940,7 +2380,7 @@ version = "0.59.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-targets",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1952,34 +2392,67 @@ dependencies = [
|
|||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.48.5",
|
||||||
|
"windows_aarch64_msvc 0.48.5",
|
||||||
|
"windows_i686_gnu 0.48.5",
|
||||||
|
"windows_i686_msvc 0.48.5",
|
||||||
|
"windows_x86_64_gnu 0.48.5",
|
||||||
|
"windows_x86_64_gnullvm 0.48.5",
|
||||||
|
"windows_x86_64_msvc 0.48.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm 0.52.6",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc 0.52.6",
|
||||||
"windows_i686_gnu",
|
"windows_i686_gnu 0.52.6",
|
||||||
"windows_i686_gnullvm",
|
"windows_i686_gnullvm",
|
||||||
"windows_i686_msvc",
|
"windows_i686_msvc 0.52.6",
|
||||||
"windows_x86_64_gnu",
|
"windows_x86_64_gnu 0.52.6",
|
||||||
"windows_x86_64_gnullvm",
|
"windows_x86_64_gnullvm 0.52.6",
|
||||||
"windows_x86_64_msvc",
|
"windows_x86_64_msvc 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -1992,24 +2465,48 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2031,6 +2528,94 @@ version = "0.46.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen-rust-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-core"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"heck",
|
||||||
|
"wit-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rust"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"heck",
|
||||||
|
"indexmap",
|
||||||
|
"prettyplease",
|
||||||
|
"syn 2.0.112",
|
||||||
|
"wasm-metadata",
|
||||||
|
"wit-bindgen-core",
|
||||||
|
"wit-component",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rust-macro"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"prettyplease",
|
||||||
|
"proc-macro2 1.0.104",
|
||||||
|
"quote 1.0.42",
|
||||||
|
"syn 2.0.112",
|
||||||
|
"wit-bindgen-core",
|
||||||
|
"wit-bindgen-rust",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-component"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bitflags",
|
||||||
|
"indexmap",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"wasm-encoder",
|
||||||
|
"wasm-metadata",
|
||||||
|
"wasmparser",
|
||||||
|
"wit-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-parser"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"id-arena",
|
||||||
|
"indexmap",
|
||||||
|
"log",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"unicode-xid 0.2.6",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wyz"
|
name = "wyz"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
|||||||
@@ -14,4 +14,5 @@ members = [
|
|||||||
"xtask",
|
"xtask",
|
||||||
"async-fn-stream",
|
"async-fn-stream",
|
||||||
"unsync-pipe",
|
"unsync-pipe",
|
||||||
|
"orchid-async-utils",
|
||||||
]
|
]
|
||||||
|
|||||||
21
notes/commands.md
Normal file
21
notes/commands.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Foreword
|
||||||
|
|
||||||
|
Commands exist to allow programs to choose a course of action rather than merely respond to external queries with data. In a sense, commands are data that describes the totality of what a program will do.
|
||||||
|
|
||||||
|
Commands are not equivalent to the instructions of an imperative programming language. If that were the case, they would describe a transition from a previous state to a new state in which additional instructions may be attempted, but commands do not have an "outcome". To the extent that they allow the continuation to be selected, they must encode that within themselves.
|
||||||
|
|
||||||
|
## Are commands unique
|
||||||
|
|
||||||
|
Yes. Since they own the entire program, and since all expressions are younger than all their subexpressions, they cannot be exactly identical to any other subexpression.
|
||||||
|
|
||||||
|
## Are commands exclusive
|
||||||
|
|
||||||
|
Not really. What control flow primitives exist between commands is up to the environment / interpreter. It may make sense to introduce a "parallel" primitive depending on the nature of the commands. In the abstract, we cannot talk about "the current command".
|
||||||
|
|
||||||
|
# Extensions
|
||||||
|
|
||||||
|
The orchid embedder and extension API mean something different by command than any particular programmer or embedder does, and something different still from what Orcx and systems programmers do. The Orchid extension API should not assume any capability that may make an embedder's job unduly difficult.
|
||||||
|
|
||||||
|
## Continuation
|
||||||
|
|
||||||
|
Since commands are expected to be composed into arbitrarily deep TC structures,to avoid a memory leak, commands should not remain passively present in the system; they must be able to express certain outcomes as plain data and return. The most obvious of such outcomes is the single continuation wherein a subexpression evaluating to another command from the same set will eventually run, but other ones may potentially exist. Are there any that cannot be emulated by continuing with a call to an environment constant which expresses the outcome in terms of its parameters?
|
||||||
@@ -6,6 +6,7 @@ edition = "2024"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
chrono = "0.4.43"
|
||||||
futures = { version = "0.3.31", features = ["std"], default-features = false }
|
futures = { version = "0.3.31", features = ["std"], default-features = false }
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
never = "0.1.0"
|
never = "0.1.0"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use std::ops::{Range, RangeInclusive};
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||||
use never::Never;
|
use never::Never;
|
||||||
@@ -352,3 +353,26 @@ impl Encode for char {
|
|||||||
(*self as u32).encode(write).await
|
(*self as u32).encode(write).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Decode for Duration {
|
||||||
|
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||||
|
Ok(Self::new(u64::decode(read.as_mut()).await?, u32::decode(read).await?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Encode for Duration {
|
||||||
|
async fn encode<W: AsyncWrite + ?Sized>(&self, mut write: Pin<&mut W>) -> io::Result<()> {
|
||||||
|
self.as_secs().encode(write.as_mut()).await?;
|
||||||
|
self.subsec_nanos().encode(write).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Decode for chrono::TimeDelta {
|
||||||
|
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> io::Result<Self> {
|
||||||
|
Ok(Self::new(i64::decode(read.as_mut()).await?, u32::decode(read).await?).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Encode for chrono::TimeDelta {
|
||||||
|
async fn encode<W: AsyncWrite + ?Sized>(&self, mut write: Pin<&mut W>) -> io::Result<()> {
|
||||||
|
self.num_seconds().encode(write.as_mut()).await?;
|
||||||
|
self.subsec_nanos().encode(write).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ impl TLBool for TLTrue {}
|
|||||||
pub struct TLFalse;
|
pub struct TLFalse;
|
||||||
impl TLBool for TLFalse {}
|
impl TLBool for TLFalse {}
|
||||||
|
|
||||||
/// A type that implements [Hierarchy]. Used to select implementations of traits
|
/// A type that implements [Extends] or a root. Used to select implementations
|
||||||
/// on the hierarchy
|
/// of traits on the hierarchy
|
||||||
pub trait InHierarchy: Clone {
|
pub trait InHierarchy: Clone {
|
||||||
/// Indicates that this hierarchy element is a leaf. Leaves can never have
|
/// Indicates that this hierarchy element is a leaf. Leaves can never have
|
||||||
/// children
|
/// children
|
||||||
|
|||||||
@@ -49,16 +49,16 @@ pub struct Atom {
|
|||||||
/// Construction is always explicit and atoms are never cloned.
|
/// Construction is always explicit and atoms are never cloned.
|
||||||
///
|
///
|
||||||
/// Atoms with `drop == None` are also known as trivial, they can be
|
/// Atoms with `drop == None` are also known as trivial, they can be
|
||||||
/// duplicated and stored with no regard to expression lifetimes. NOTICE
|
/// duplicated and stored with no regard to expression lifetimes. Note
|
||||||
/// that this only applies to the atom. If it's referenced with an
|
/// that this only applies to the atom. If it's referenced with an
|
||||||
/// [ExprTicket], the ticket itself can still expire.
|
/// [ExprTicket], the ticket itself can still expire.
|
||||||
///
|
///
|
||||||
/// Notice also that the atoms still expire when the system is dropped, and
|
/// Note also that the atoms still expire when the system is dropped, and
|
||||||
/// are not portable across instances of the same system, so this doesn't
|
/// are not portable across instances of the same system, so this doesn't
|
||||||
/// imply that the atom is serializable.
|
/// imply that the atom is serializable.
|
||||||
pub drop: Option<AtomId>,
|
pub drop: Option<AtomId>,
|
||||||
/// Data stored in the atom. This could be a key into a map, or the raw data
|
/// Data stored in the atom. This could be a key into a map, the raw data
|
||||||
/// of the atom if it isn't too big.
|
/// of the atom if it isn't too big, or even a pointer.
|
||||||
pub data: AtomData,
|
pub data: AtomData,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +91,7 @@ impl Request for SerializeAtom {
|
|||||||
#[extends(HostExtReq)]
|
#[extends(HostExtReq)]
|
||||||
pub struct DeserAtom(pub SysId, pub Vec<u8>, pub Vec<ExprTicket>);
|
pub struct DeserAtom(pub SysId, pub Vec<u8>, pub Vec<ExprTicket>);
|
||||||
impl Request for DeserAtom {
|
impl Request for DeserAtom {
|
||||||
type Response = Atom;
|
type Response = LocalAtom;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A request blindly routed to the system that provides an atom.
|
/// A request blindly routed to the system that provides an atom.
|
||||||
@@ -109,10 +109,24 @@ impl Request for Fwd {
|
|||||||
type Response = Option<Vec<u8>>;
|
type Response = Option<Vec<u8>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// What to do after a command has finished executing
|
||||||
#[derive(Clone, Debug, Coding)]
|
#[derive(Clone, Debug, Coding)]
|
||||||
pub enum NextStep {
|
pub enum NextStep {
|
||||||
Continue(Expression),
|
/// Add more work. Parallel work is fairly executed in parallel, so different
|
||||||
Halt,
|
/// command chains can block on each other. When the command queue is empty,
|
||||||
|
/// the interpreter may exit without waiting for timers.
|
||||||
|
Continue {
|
||||||
|
/// Run these commands immediately. Since timers don't keep the interpreter
|
||||||
|
/// alive, this should usually be non-empty, but this is not required.
|
||||||
|
immediate: Vec<Expression>,
|
||||||
|
/// Schedule these commands after the specified number of milliseconds, if
|
||||||
|
/// the interpreter had not exited by then.
|
||||||
|
delayed: Vec<(NonZeroU64, Expression)>,
|
||||||
|
},
|
||||||
|
/// Discard the rest of the queue and exit. It is possible to fail the program
|
||||||
|
/// without raising an error because the convention on most OSes is to
|
||||||
|
/// separate error reporting from a failure exit.
|
||||||
|
Exit { success: bool },
|
||||||
}
|
}
|
||||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
#[extends(AtomReq, HostExtReq)]
|
#[extends(AtomReq, HostExtReq)]
|
||||||
|
|||||||
@@ -69,8 +69,10 @@ pub struct SpawnerBin {
|
|||||||
pub data: *const (),
|
pub data: *const (),
|
||||||
/// `self`
|
/// `self`
|
||||||
pub drop: extern "C" fn(*const ()),
|
pub drop: extern "C" fn(*const ()),
|
||||||
/// `&self` Add a future to this extension's task
|
/// `&self` Add a future to this extension's task after a configurable delay
|
||||||
pub spawn: extern "C" fn(*const (), FutureBin),
|
/// measured in milliseconds. By itself, a pending timer never prevents
|
||||||
|
/// extension shutdown.
|
||||||
|
pub spawn: extern "C" fn(*const (), u64, FutureBin),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extension context.
|
/// Extension context.
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::num::NonZeroU16;
|
use std::num::NonZeroU16;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use orchid_api_derive::Coding;
|
use orchid_api_derive::Coding;
|
||||||
@@ -28,7 +29,7 @@ pub struct OrcError {
|
|||||||
pub description: TStr,
|
pub description: TStr,
|
||||||
/// Specific information about the exact error, preferably containing concrete
|
/// Specific information about the exact error, preferably containing concrete
|
||||||
/// values.
|
/// values.
|
||||||
pub message: Arc<String>,
|
pub message: Rc<String>,
|
||||||
/// Specific code fragments that have contributed to the emergence of the
|
/// Specific code fragments that have contributed to the emergence of the
|
||||||
/// error.
|
/// error.
|
||||||
pub locations: Vec<ErrLocation>,
|
pub locations: Vec<ErrLocation>,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::num::NonZeroU64;
|
|||||||
use orchid_api_derive::{Coding, Hierarchy};
|
use orchid_api_derive::{Coding, Hierarchy};
|
||||||
use orchid_api_traits::Request;
|
use orchid_api_traits::Request;
|
||||||
|
|
||||||
use crate::{Atom, ExtHostNotif, ExtHostReq, Location, OrcError, SysId, TStrv};
|
use crate::{Atom, ExtHostNotif, ExtHostReq, LocalAtom, Location, OrcError, SysId, TStrv};
|
||||||
|
|
||||||
/// An arbitrary ID associated with an expression on the host side. Incoming
|
/// An arbitrary ID associated with an expression on the host side. Incoming
|
||||||
/// tickets always come with some lifetime guarantee, which can be extended with
|
/// tickets always come with some lifetime guarantee, which can be extended with
|
||||||
@@ -65,8 +65,7 @@ pub enum ExpressionKind {
|
|||||||
/// Insert a new atom in the tree. When the clause is used in the const tree,
|
/// Insert a new atom in the tree. When the clause is used in the const tree,
|
||||||
/// the atom must be trivial. This is always a newly constructed atom, if you
|
/// the atom must be trivial. This is always a newly constructed atom, if you
|
||||||
/// want to reference an existing atom, use the corresponding [ExprTicket].
|
/// want to reference an existing atom, use the corresponding [ExprTicket].
|
||||||
/// Because the atom is newly constructed, it also must belong to this system.
|
NewAtom(LocalAtom),
|
||||||
NewAtom(Atom),
|
|
||||||
/// A reference to a constant
|
/// A reference to a constant
|
||||||
Const(TStrv),
|
Const(TStrv),
|
||||||
/// A static runtime error.
|
/// A static runtime error.
|
||||||
@@ -123,7 +122,7 @@ pub enum ExprNotif {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
#[extends(ExprReq, ExtHostReq)]
|
#[extends(ExprReq, ExtHostReq)]
|
||||||
pub struct Create(pub Expression);
|
pub struct Create(pub SysId, pub Expression);
|
||||||
impl Request for Create {
|
impl Request for Create {
|
||||||
type Response = ExprTicket;
|
type Response = ExprTicket;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ use futures::{AsyncRead, AsyncWrite, AsyncWriteExt};
|
|||||||
use orchid_api_derive::{Coding, Hierarchy};
|
use orchid_api_derive::{Coding, Hierarchy};
|
||||||
use orchid_api_traits::{Channel, Decode, Encode, MsgSet, Request, read_exact};
|
use orchid_api_traits::{Channel, Decode, Encode, MsgSet, Request, read_exact};
|
||||||
|
|
||||||
use crate::{Sweeped, atom, expr, interner, lexer, logging, parser, system, tree};
|
use crate::{atom, expr, interner, lexer, logging, parser, system, tree};
|
||||||
|
|
||||||
static HOST_INTRO: &[u8] = b"Orchid host, binary API v0\n";
|
static HOST_INTRO: &[u8] = b"Orchid host, binary API v0\n";
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -97,7 +97,7 @@ pub enum ExtHostReq {
|
|||||||
pub enum ExtHostNotif {
|
pub enum ExtHostNotif {
|
||||||
ExprNotif(expr::ExprNotif),
|
ExprNotif(expr::ExprNotif),
|
||||||
Log(logging::Log),
|
Log(logging::Log),
|
||||||
Sweeped(Sweeped),
|
Sweeped(interner::Sweeped),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ExtHostChannel;
|
pub struct ExtHostChannel;
|
||||||
|
|||||||
12
orchid-async-utils/Cargo.toml
Normal file
12
orchid-async-utils/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "orchid-async-utils"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
futures = { version = "0.3.31", default-features = false, features = [
|
||||||
|
"std",
|
||||||
|
"async-await",
|
||||||
|
] }
|
||||||
|
itertools = "0.14.0"
|
||||||
|
task-local = "0.1.0"
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
//! Note that these utilities are safe and simple in order to facilitate
|
||||||
|
//! debugging without adding more points of failure, but they're not efficient;
|
||||||
|
//! they may perform heap allocations, I/O and other expensive operations, or
|
||||||
|
//! even block the thread altogether waiting for input whenever they receive
|
||||||
|
//! control
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::pin::pin;
|
use std::pin::pin;
|
||||||
@@ -19,10 +25,7 @@ impl<F: Fn() + 'static> Wake for OnPollWaker<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attach a callback to the [Future] protocol for testing and debugging. Note
|
/// Attach a callback to the [Future] protocol for testing and debugging.
|
||||||
/// that this function is safe and simple in order to facilitate debugging
|
|
||||||
/// without adding more points of failure, but it's not fast; it performs a heap
|
|
||||||
/// allocation on each poll of the returned future.
|
|
||||||
pub async fn on_wake<F: Future>(
|
pub async fn on_wake<F: Future>(
|
||||||
f: F,
|
f: F,
|
||||||
wake: impl Fn() + Clone + Send + Sync + 'static,
|
wake: impl Fn() + Clone + Send + Sync + 'static,
|
||||||
@@ -56,6 +59,8 @@ pub async fn wrap_poll<Fut: Future>(
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Respond to [Stream::poll_next] with a callback. The semantics of the
|
||||||
|
/// callback are identical to that in [wrap_poll]
|
||||||
pub fn wrap_poll_next<'a, S: Stream + 'a>(
|
pub fn wrap_poll_next<'a, S: Stream + 'a>(
|
||||||
s: S,
|
s: S,
|
||||||
mut cb: impl FnMut(Box<dyn FnOnce() -> bool + '_>) + 'a,
|
mut cb: impl FnMut(Box<dyn FnOnce() -> bool + '_>) + 'a,
|
||||||
@@ -73,6 +78,7 @@ pub fn wrap_poll_next<'a, S: Stream + 'a>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attach a callback to the [Stream] protocol for testing and debugging.
|
||||||
pub fn on_stream_wake<'a, S: Stream + 'a>(
|
pub fn on_stream_wake<'a, S: Stream + 'a>(
|
||||||
s: S,
|
s: S,
|
||||||
wake: impl Fn() + Clone + Send + Sync + 'static,
|
wake: impl Fn() + Clone + Send + Sync + 'static,
|
||||||
@@ -88,21 +94,26 @@ task_local! {
|
|||||||
static LABEL_STATE: Vec<Rc<String>>
|
static LABEL_STATE: Vec<Rc<String>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a label to the "label stack" for the duration of a future that helps you
|
||||||
|
/// efficiently visualize important aspects of the call stack during logging
|
||||||
pub async fn with_label<Fut: Future>(label: &str, f: Fut) -> Fut::Output {
|
pub async fn with_label<Fut: Future>(label: &str, f: Fut) -> Fut::Output {
|
||||||
let mut new_lbl = LABEL_STATE.try_with(|lbl| lbl.clone()).unwrap_or_default();
|
let mut new_lbl = LABEL_STATE.try_with(|lbl| lbl.clone()).unwrap_or_default();
|
||||||
new_lbl.push(Rc::new(label.to_string()));
|
new_lbl.push(Rc::new(label.to_string()));
|
||||||
LABEL_STATE.scope(new_lbl, f).await
|
LABEL_STATE.scope(new_lbl, f).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allows to print the label stack
|
||||||
pub fn label() -> impl Display + Clone + Send + Sync + 'static {
|
pub fn label() -> impl Display + Clone + Send + Sync + 'static {
|
||||||
LABEL_STATE.try_with(|lbl| lbl.iter().join("/")).unwrap_or("".to_string())
|
LABEL_STATE.try_with(|lbl| lbl.iter().join("/")).unwrap_or("".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Displays the label stack when printed
|
||||||
pub struct Label;
|
pub struct Label;
|
||||||
impl Display for Label {
|
impl Display for Label {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", label()) }
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", label()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attaches generic eprintln handlers to a future
|
||||||
pub async fn eprint_events<Fut: Future>(note: &str, f: Fut) -> Fut::Output {
|
pub async fn eprint_events<Fut: Future>(note: &str, f: Fut) -> Fut::Output {
|
||||||
let label = label();
|
let label = label();
|
||||||
let note1 = note.to_string();
|
let note1 = note.to_string();
|
||||||
@@ -116,6 +127,7 @@ pub async fn eprint_events<Fut: Future>(note: &str, f: Fut) -> Fut::Output {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attaches generic eprintln handlers to a stream
|
||||||
pub fn eprint_stream_events<'a, S: Stream + 'a>(
|
pub fn eprint_stream_events<'a, S: Stream + 'a>(
|
||||||
note: &'a str,
|
note: &'a str,
|
||||||
s: S,
|
s: S,
|
||||||
@@ -137,12 +149,12 @@ impl Wake for SpinWaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A dumb executor that keeps synchronously re-running the future as long as it
|
/// A dumb executor that keeps synchronously re-running the future as long as it
|
||||||
/// keeps synchronously waking itself.
|
/// keeps synchronously waking itself. This is useful for deterministic tests
|
||||||
|
/// that don't contain side effects or threading.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// If the future doesn't wake itself and doesn't settle. This is useful for
|
/// If the future doesn't wake itself and doesn't settle.
|
||||||
/// deterministic tests that don't contain side effects or threading.
|
|
||||||
pub fn spin_on<Fut: Future>(f: Fut) -> Fut::Output {
|
pub fn spin_on<Fut: Future>(f: Fut) -> Fut::Output {
|
||||||
let repeat = Arc::new(SpinWaker(AtomicBool::new(false)));
|
let repeat = Arc::new(SpinWaker(AtomicBool::new(false)));
|
||||||
let mut f = pin!(f);
|
let mut f = pin!(f);
|
||||||
@@ -161,8 +173,11 @@ pub fn spin_on<Fut: Future>(f: Fut) -> Fut::Output {
|
|||||||
/// called once the particular constraint preventing a drop has passed
|
/// called once the particular constraint preventing a drop has passed
|
||||||
pub fn assert_no_drop(msg: &'static str) -> PanicOnDrop { PanicOnDrop(true, msg) }
|
pub fn assert_no_drop(msg: &'static str) -> PanicOnDrop { PanicOnDrop(true, msg) }
|
||||||
|
|
||||||
|
/// This object will panic if dropped. Call [Self::defuse] when dropping is safe
|
||||||
|
/// again
|
||||||
pub struct PanicOnDrop(bool, &'static str);
|
pub struct PanicOnDrop(bool, &'static str);
|
||||||
impl PanicOnDrop {
|
impl PanicOnDrop {
|
||||||
|
/// Allow dropping the object without causing a panic
|
||||||
pub fn defuse(mut self) { self.0 = false; }
|
pub fn defuse(mut self) { self.0 = false; }
|
||||||
}
|
}
|
||||||
impl Drop for PanicOnDrop {
|
impl Drop for PanicOnDrop {
|
||||||
5
orchid-async-utils/src/lib.rs
Normal file
5
orchid-async-utils/src/lib.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod debug;
|
||||||
|
mod localset;
|
||||||
|
pub use localset::*;
|
||||||
|
mod task_future;
|
||||||
|
pub use task_future::*;
|
||||||
92
orchid-async-utils/src/task_future.rs
Normal file
92
orchid-async-utils/src/task_future.rs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
use std::any::Any;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::task::{Context, Poll, Waker};
|
||||||
|
|
||||||
|
use futures::future::{FusedFuture, LocalBoxFuture};
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
work: Option<LocalBoxFuture<'static, Box<dyn Any>>>,
|
||||||
|
result: Option<Box<dyn Any>>,
|
||||||
|
waker: Waker,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A fused future that can be passed to a non-polymorphic executor that doesn't
|
||||||
|
/// process results and doesn't return handles
|
||||||
|
pub struct Pollable(Rc<RefCell<State>>);
|
||||||
|
impl FusedFuture for Pollable {
|
||||||
|
fn is_terminated(&self) -> bool {
|
||||||
|
let g = self.0.borrow();
|
||||||
|
g.work.is_none() || g.result.is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Future for Pollable {
|
||||||
|
type Output = ();
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let mut g = self.0.borrow_mut();
|
||||||
|
match &mut *g {
|
||||||
|
State { result: Some(_), .. } | State { work: None, .. } => Poll::Ready(()),
|
||||||
|
State { work: Some(work), waker, result } => match work.as_mut().poll(cx) {
|
||||||
|
Poll::Pending => {
|
||||||
|
waker.clone_from(cx.waker());
|
||||||
|
Poll::Pending
|
||||||
|
},
|
||||||
|
Poll::Ready(val) => {
|
||||||
|
*result = Some(val);
|
||||||
|
g.work = None;
|
||||||
|
Poll::Ready(())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An object that can be used to inspect the state of the task
|
||||||
|
pub struct Handle<T: 'static>(Rc<RefCell<State>>, PhantomData<T>);
|
||||||
|
impl<T: 'static> Handle<T> {
|
||||||
|
/// Immediately stop working on this task, and return the result if it has
|
||||||
|
/// already finished
|
||||||
|
pub fn abort(&self) -> Option<T> {
|
||||||
|
let mut g = self.0.borrow_mut();
|
||||||
|
g.work.take();
|
||||||
|
match g.result.take() {
|
||||||
|
Some(val) => Some(*val.downcast().expect("Mismatch between type of future and handle")),
|
||||||
|
None => {
|
||||||
|
g.waker.wake_by_ref();
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Determine if there's any more work to do on this task
|
||||||
|
pub fn is_finished(&self) -> bool {
|
||||||
|
let g = self.0.borrow();
|
||||||
|
g.result.is_some() || g.work.is_none()
|
||||||
|
}
|
||||||
|
/// "finish" the freestanding task, and return the future instead
|
||||||
|
pub async fn join(self) -> T {
|
||||||
|
let work = {
|
||||||
|
let mut g = self.0.borrow_mut();
|
||||||
|
if let Some(val) = g.result.take() {
|
||||||
|
return *val.downcast().expect("Mistmatch between type of future and handle");
|
||||||
|
}
|
||||||
|
g.waker.wake_by_ref();
|
||||||
|
g.work.take().expect("Attempted to join task that was already aborted")
|
||||||
|
};
|
||||||
|
*work.await.downcast().expect("Mismatch between type of future and handle")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Split a future into an object that can be polled and one that returns
|
||||||
|
/// information on its progress and its result. The first one can be passed to
|
||||||
|
/// an executor or localset, the second can be used to manage it
|
||||||
|
pub fn to_task<F: Future<Output: 'static> + 'static>(f: F) -> (Pollable, Handle<F::Output>) {
|
||||||
|
let dyn_future = Box::pin(async { Box::new(f.await) as Box<dyn Any> });
|
||||||
|
let state = Rc::new(RefCell::new(State {
|
||||||
|
result: None,
|
||||||
|
work: Some(dyn_future),
|
||||||
|
waker: Waker::noop().clone(),
|
||||||
|
}));
|
||||||
|
(Pollable(state.clone()), Handle(state, PhantomData))
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
|
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
|
||||||
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
||||||
|
orchid-async-utils = { version = "0.1.0", path = "../orchid-async-utils" }
|
||||||
async-once-cell = "0.5.4"
|
async-once-cell = "0.5.4"
|
||||||
bound = "0.6.0"
|
bound = "0.6.0"
|
||||||
derive_destructure = "1.0.0"
|
derive_destructure = "1.0.0"
|
||||||
@@ -33,3 +34,5 @@ task-local = "0.1.0"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
futures = "0.3.31"
|
futures = "0.3.31"
|
||||||
|
rand = "0.10.0"
|
||||||
|
rand_chacha = "0.10.0"
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ static BORROWED_VTABLE: RawWakerVTable = RawWakerVTable::new(
|
|||||||
|
|
||||||
/// Convert a future to a binary-compatible format that can be sent across
|
/// Convert a future to a binary-compatible format that can be sent across
|
||||||
/// dynamic library boundaries
|
/// dynamic library boundaries
|
||||||
|
#[must_use]
|
||||||
pub fn future_to_vt<Fut: Future<Output = ()> + 'static>(fut: Fut) -> api::binary::FutureBin {
|
pub fn future_to_vt<Fut: Future<Output = ()> + 'static>(fut: Fut) -> api::binary::FutureBin {
|
||||||
let wide_box = Box::new(fut) as WideBox;
|
let wide_box = Box::new(fut) as WideBox;
|
||||||
let data = Box::into_raw(Box::new(wide_box));
|
let data = Box::into_raw(Box::new(wide_box));
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
use std::borrow::Borrow;
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub enum ArcCow<'a, T: ?Sized + ToOwned> {
|
|
||||||
Borrowed(&'a T),
|
|
||||||
Owned(Arc<T::Owned>),
|
|
||||||
}
|
|
||||||
impl<T: ?Sized + ToOwned> ArcCow<'_, T> {
|
|
||||||
pub fn owned(value: T::Owned) -> Self { Self::Owned(Arc::new(value)) }
|
|
||||||
}
|
|
||||||
impl<T: ?Sized + ToOwned> Clone for ArcCow<'_, T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
match self {
|
|
||||||
Self::Borrowed(r) => Self::Borrowed(r),
|
|
||||||
Self::Owned(b) => Self::Owned(b.clone()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized + ToOwned> Deref for ArcCow<'_, T> {
|
|
||||||
type Target = T;
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
match self {
|
|
||||||
Self::Borrowed(t) => t,
|
|
||||||
Self::Owned(b) => b.as_ref().borrow(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,20 +3,22 @@
|
|||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
/// A trait object of [Iterator] to be assigned to variables that may be
|
/// A trait object of [Iterator] to be assigned to variables that may be
|
||||||
/// initialized form multiple iterators of incompatible types
|
/// initialized form iterators of multiple or unknown types
|
||||||
pub type BoxedIter<'a, T> = Box<dyn Iterator<Item = T> + 'a>;
|
pub type BoxedIter<'a, T> = Box<dyn Iterator<Item = T> + 'a>;
|
||||||
/// creates a [BoxedIter] of a single element
|
/// creates a [BoxedIter] of a single element
|
||||||
|
#[must_use]
|
||||||
pub fn box_once<'a, T: 'a>(t: T) -> BoxedIter<'a, T> { Box::new(iter::once(t)) }
|
pub fn box_once<'a, T: 'a>(t: T) -> BoxedIter<'a, T> { Box::new(iter::once(t)) }
|
||||||
/// creates an empty [BoxedIter]
|
/// creates an empty [BoxedIter]
|
||||||
|
#[must_use]
|
||||||
pub fn box_empty<'a, T: 'a>() -> BoxedIter<'a, T> { Box::new(iter::empty()) }
|
pub fn box_empty<'a, T: 'a>() -> BoxedIter<'a, T> { Box::new(iter::empty()) }
|
||||||
|
|
||||||
/// Chain various iterators into a [BoxedIter]
|
/// Chain various iterators into a [BoxedIter]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! box_chain {
|
macro_rules! box_chain {
|
||||||
($curr:expr) => {
|
($curr:expr) => {
|
||||||
Box::new($curr) as $crate::boxed_iter::BoxedIter<_>
|
Box::new($curr) as $crate::BoxedIter<_>
|
||||||
};
|
};
|
||||||
($curr:expr, $($rest:expr),*) => {
|
($curr:expr, $($rest:expr),*) => {
|
||||||
Box::new($curr$(.chain($rest))*) as $crate::boxed_iter::BoxedIter<_>
|
Box::new($curr$(.chain($rest))*) as $crate::BoxedIter<_>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ use itertools::Itertools;
|
|||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
|
|
||||||
pub type CRange = RangeInclusive<char>;
|
/// A fast character filter to avoid superfluous extension calls in the lexer.
|
||||||
|
|
||||||
pub trait ICFilter: fmt::Debug {
|
pub trait ICFilter: fmt::Debug {
|
||||||
|
/// Returns an ordered set of character ranges
|
||||||
fn ranges(&self) -> &[RangeInclusive<char>];
|
fn ranges(&self) -> &[RangeInclusive<char>];
|
||||||
}
|
}
|
||||||
impl ICFilter for [RangeInclusive<char>] {
|
impl ICFilter for [RangeInclusive<char>] {
|
||||||
@@ -17,7 +17,10 @@ impl ICFilter for api::CharFilter {
|
|||||||
fn ranges(&self) -> &[RangeInclusive<char>] { &self.0 }
|
fn ranges(&self) -> &[RangeInclusive<char>] { &self.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_merge_char_ranges(left: CRange, right: CRange) -> Result<CRange, (CRange, CRange)> {
|
fn try_merge_char_ranges(
|
||||||
|
left: RangeInclusive<char>,
|
||||||
|
right: RangeInclusive<char>,
|
||||||
|
) -> Result<RangeInclusive<char>, (RangeInclusive<char>, RangeInclusive<char>)> {
|
||||||
match *left.end() as u32 + 1 < *right.start() as u32 {
|
match *left.end() as u32 + 1 < *right.start() as u32 {
|
||||||
true => Err((left, right)),
|
true => Err((left, right)),
|
||||||
false => Ok(*left.start()..=*right.end()),
|
false => Ok(*left.start()..=*right.end()),
|
||||||
@@ -25,8 +28,9 @@ fn try_merge_char_ranges(left: CRange, right: CRange) -> Result<CRange, (CRange,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Process the character ranges to make them adhere to the structural
|
/// Process the character ranges to make them adhere to the structural
|
||||||
/// requirements of [CharFilter]
|
/// requirements of [api::CharFilter]
|
||||||
pub fn mk_char_filter(items: impl IntoIterator<Item = CRange>) -> api::CharFilter {
|
#[must_use]
|
||||||
|
pub fn mk_char_filter(items: impl IntoIterator<Item = RangeInclusive<char>>) -> api::CharFilter {
|
||||||
api::CharFilter(
|
api::CharFilter(
|
||||||
(items.into_iter())
|
(items.into_iter())
|
||||||
.filter(|r| *r.start() as u32 <= *r.end() as u32)
|
.filter(|r| *r.start() as u32 <= *r.end() as u32)
|
||||||
@@ -37,6 +41,7 @@ pub fn mk_char_filter(items: impl IntoIterator<Item = CRange>) -> api::CharFilte
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Decide whether a char filter matches a character via binary search
|
/// Decide whether a char filter matches a character via binary search
|
||||||
|
#[must_use]
|
||||||
pub fn char_filter_match(cf: &(impl ICFilter + ?Sized), c: char) -> bool {
|
pub fn char_filter_match(cf: &(impl ICFilter + ?Sized), c: char) -> bool {
|
||||||
match cf.ranges().binary_search_by_key(&c, |l| *l.end()) {
|
match cf.ranges().binary_search_by_key(&c, |l| *l.end()) {
|
||||||
Ok(_) => true, // c is the end of a range
|
Ok(_) => true, // c is the end of a range
|
||||||
@@ -48,6 +53,7 @@ pub fn char_filter_match(cf: &(impl ICFilter + ?Sized), c: char) -> bool {
|
|||||||
|
|
||||||
/// Merge two char filters into a filter that matches if either of the
|
/// Merge two char filters into a filter that matches if either of the
|
||||||
/// constituents would match.
|
/// constituents would match.
|
||||||
|
#[must_use]
|
||||||
pub fn char_filter_union(
|
pub fn char_filter_union(
|
||||||
l: &(impl ICFilter + ?Sized),
|
l: &(impl ICFilter + ?Sized),
|
||||||
r: &(impl ICFilter + ?Sized),
|
r: &(impl ICFilter + ?Sized),
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
//! The concept of a fallible merger
|
|
||||||
|
|
||||||
use never::Never;
|
|
||||||
|
|
||||||
/// Fallible, type-preserving variant of [std::ops::Add] implemented by a
|
|
||||||
/// variety of types for different purposes. Very broadly, if the operation
|
|
||||||
/// succeeds, the result should represent _both_ inputs.
|
|
||||||
pub trait Combine: Sized {
|
|
||||||
/// Information about the failure
|
|
||||||
type Error;
|
|
||||||
|
|
||||||
/// Merge two values into a value that represents both, if this is possible.
|
|
||||||
fn combine(self, other: Self) -> Result<Self, Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Combine for Never {
|
|
||||||
type Error = Never;
|
|
||||||
fn combine(self, _: Self) -> Result<Self, Self::Error> { match self {} }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Combine for () {
|
|
||||||
type Error = Never;
|
|
||||||
fn combine(self, (): Self) -> Result<Self, Self::Error> { Ok(()) }
|
|
||||||
}
|
|
||||||
@@ -16,9 +16,8 @@ use futures::{
|
|||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use orchid_api_traits::{Decode, Encode, Request, UnderRoot};
|
use orchid_api_traits::{Decode, Encode, Request, UnderRoot};
|
||||||
|
use orchid_async_utils::LocalSet;
|
||||||
use crate::future_debug::{PanicOnDrop, assert_no_drop};
|
use orchid_async_utils::debug::{PanicOnDrop, assert_no_drop};
|
||||||
use crate::localset::LocalSet;
|
|
||||||
|
|
||||||
#[must_use = "Receipts indicate that a required action has been performed within a function. \
|
#[must_use = "Receipts indicate that a required action has been performed within a function. \
|
||||||
Most likely this should be returned somewhere."]
|
Most likely this should be returned somewhere."]
|
||||||
@@ -445,10 +444,10 @@ mod test {
|
|||||||
use futures::{SinkExt, StreamExt, join};
|
use futures::{SinkExt, StreamExt, join};
|
||||||
use orchid_api_derive::{Coding, Hierarchy};
|
use orchid_api_derive::{Coding, Hierarchy};
|
||||||
use orchid_api_traits::Request;
|
use orchid_api_traits::Request;
|
||||||
|
use orchid_async_utils::debug::spin_on;
|
||||||
use unsync_pipe::pipe;
|
use unsync_pipe::pipe;
|
||||||
|
|
||||||
use crate::future_debug::spin_on;
|
use crate::comm::{ClientExt, MsgReaderExt, ReqReaderExt, io_comm};
|
||||||
use crate::reqnot::{ClientExt, MsgReaderExt, ReqReaderExt, io_comm};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Coding, Hierarchy)]
|
#[derive(Clone, Debug, PartialEq, Coding, Hierarchy)]
|
||||||
#[extendable]
|
#[extendable]
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::num::{NonZero, NonZeroUsize};
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -10,9 +11,7 @@ use futures::future::join_all;
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use task_local::task_local;
|
use task_local::task_local;
|
||||||
|
|
||||||
use crate::api;
|
use crate::{IStr, Pos, api, es, is};
|
||||||
use crate::interner::{IStr, es, is};
|
|
||||||
use crate::location::Pos;
|
|
||||||
|
|
||||||
/// A point of interest in resolving the error, such as the point where
|
/// A point of interest in resolving the error, such as the point where
|
||||||
/// processing got stuck, a command that is likely to be incorrect
|
/// processing got stuck, a command that is likely to be incorrect
|
||||||
@@ -24,12 +23,15 @@ pub struct ErrPos {
|
|||||||
pub message: Option<Arc<String>>,
|
pub message: Option<Arc<String>>,
|
||||||
}
|
}
|
||||||
impl ErrPos {
|
impl ErrPos {
|
||||||
|
/// Create from a position with a position-specific message. If there's no
|
||||||
|
/// message, use `Pos::into`
|
||||||
|
#[must_use]
|
||||||
pub fn new(msg: &str, position: Pos) -> Self {
|
pub fn new(msg: &str, position: Pos) -> Self {
|
||||||
Self { message: Some(Arc::new(msg.to_string())), position }
|
Self { message: Some(Arc::new(msg.to_string())), position }
|
||||||
}
|
}
|
||||||
async fn from_api(api: &api::ErrLocation) -> Self {
|
async fn from_api(api: api::ErrLocation) -> Self {
|
||||||
Self {
|
Self {
|
||||||
message: Some(api.message.clone()).filter(|s| !s.is_empty()),
|
message: Some(api.message).filter(|s| !s.is_empty()),
|
||||||
position: Pos::from_api(&api.location).await,
|
position: Pos::from_api(&api.location).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,10 +54,16 @@ impl fmt::Display for ErrPos {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An error that occurred in Orchid code, whether at startup or runtime
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct OrcErr {
|
pub struct OrcErr {
|
||||||
|
/// A generic error message used in categorizing errors
|
||||||
|
/// You can also equality-compare atoms with these message tokens
|
||||||
pub description: IStr,
|
pub description: IStr,
|
||||||
pub message: Arc<String>,
|
/// A specific error message that may include values relevant in resolving the
|
||||||
|
/// error
|
||||||
|
pub message: Rc<String>,
|
||||||
|
/// Various locations in code that may be useful in resolving the error
|
||||||
pub positions: Vec<ErrPos>,
|
pub positions: Vec<ErrPos>,
|
||||||
}
|
}
|
||||||
impl OrcErr {
|
impl OrcErr {
|
||||||
@@ -66,11 +74,11 @@ impl OrcErr {
|
|||||||
locations: self.positions.iter().map(ErrPos::to_api).collect(),
|
locations: self.positions.iter().map(ErrPos::to_api).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn from_api(api: &api::OrcError) -> Self {
|
async fn from_api(api: api::OrcError) -> Self {
|
||||||
Self {
|
Self {
|
||||||
description: es(api.description).await,
|
description: es(api.description).await,
|
||||||
message: api.message.clone(),
|
message: api.message,
|
||||||
positions: join_all(api.locations.iter().map(ErrPos::from_api)).await,
|
positions: join_all(api.locations.into_iter().map(ErrPos::from_api)).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,6 +95,8 @@ impl fmt::Display for OrcErr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Rust error produced when an Orchid error condition arises but no
|
||||||
|
/// specific errors are listed. This is always a Rust programmer error.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct EmptyErrv;
|
pub struct EmptyErrv;
|
||||||
impl fmt::Display for EmptyErrv {
|
impl fmt::Display for EmptyErrv {
|
||||||
@@ -95,39 +105,57 @@ impl fmt::Display for EmptyErrv {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A container for one or more errors. Code that supports error recovery should
|
||||||
|
/// use these instead of plain [OrcErr] objects.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct OrcErrv(Vec<OrcErr>);
|
pub struct OrcErrv(Vec<OrcErr>);
|
||||||
impl OrcErrv {
|
impl OrcErrv {
|
||||||
|
/// Create from individual errors. If you have exactly one initial error, see
|
||||||
|
/// [mk_errv]
|
||||||
pub fn new(errors: impl IntoIterator<Item = OrcErr>) -> Result<Self, EmptyErrv> {
|
pub fn new(errors: impl IntoIterator<Item = OrcErr>) -> Result<Self, EmptyErrv> {
|
||||||
let v = errors.into_iter().collect_vec();
|
let v = errors.into_iter().collect_vec();
|
||||||
if v.is_empty() { Err(EmptyErrv) } else { Ok(Self(v)) }
|
if v.is_empty() { Err(EmptyErrv) } else { Ok(Self(v)) }
|
||||||
}
|
}
|
||||||
|
/// Add additional errors to this container. Since `OrcErrv` also implements
|
||||||
|
/// [IntoIterator], this can take `(Self, Self)`
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn extended<T>(mut self, errors: impl IntoIterator<Item = T>) -> Self
|
pub fn extended<T>(mut self, errors: impl IntoIterator<Item = T>) -> Self
|
||||||
where Self: Extend<T> {
|
where Self: Extend<T> {
|
||||||
self.extend(errors);
|
self.extend(errors);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
/// Determine how many distinct errors there are in the container
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn len(&self) -> usize { self.0.len() }
|
pub fn len(&self) -> NonZeroUsize { NonZero::new(self.0.len()).expect("OrcErrv cannot be empty") }
|
||||||
#[must_use]
|
/// See if any errors match a particular filter criteria. This is useful for
|
||||||
pub fn is_empty(&self) -> bool { self.len() == 0 }
|
/// sentinel errors whch are produced by user code to trigger unique
|
||||||
|
/// behaviours such as a lexer mismatch
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn any(&self, f: impl FnMut(&OrcErr) -> bool) -> bool { self.0.iter().any(f) }
|
pub fn any(&self, f: impl FnMut(&OrcErr) -> bool) -> bool { self.0.iter().any(f) }
|
||||||
|
/// Remove all errors that don't match a filter criterion. If no errors match,
|
||||||
|
/// nothing is returned
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn keep_only(self, f: impl FnMut(&OrcErr) -> bool) -> Option<Self> {
|
pub fn keep_only(self, f: impl FnMut(&OrcErr) -> bool) -> Option<Self> {
|
||||||
let v = self.0.into_iter().filter(f).collect_vec();
|
let v = self.0.into_iter().filter(f).collect_vec();
|
||||||
if v.is_empty() { None } else { Some(Self(v)) }
|
if v.is_empty() { None } else { Some(Self(v)) }
|
||||||
}
|
}
|
||||||
|
/// If there is exactly one error, return it. Mostly used for simplified
|
||||||
|
/// printing
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn one(&self) -> Option<&OrcErr> { (self.0.len() == 1).then(|| &self.0[9]) }
|
pub fn one(&self) -> Option<&OrcErr> { (self.0.len() == 1).then(|| &self.0[9]) }
|
||||||
|
/// Iterate over all positions of all errors
|
||||||
pub fn pos_iter(&self) -> impl Iterator<Item = ErrPos> + '_ {
|
pub fn pos_iter(&self) -> impl Iterator<Item = ErrPos> + '_ {
|
||||||
self.0.iter().flat_map(|e| e.positions.iter().cloned())
|
self.0.iter().flat_map(|e| e.positions.iter().cloned())
|
||||||
}
|
}
|
||||||
|
/// Serialize for transmission
|
||||||
|
#[must_use]
|
||||||
pub fn to_api(&self) -> Vec<api::OrcError> { self.0.iter().map(OrcErr::to_api).collect() }
|
pub fn to_api(&self) -> Vec<api::OrcError> { self.0.iter().map(OrcErr::to_api).collect() }
|
||||||
pub async fn from_api<'a>(api: impl IntoIterator<Item = &'a api::OrcError>) -> Self {
|
/// Deserialize from transmission
|
||||||
|
#[must_use]
|
||||||
|
pub async fn from_api(api: impl IntoIterator<Item = api::OrcError>) -> Self {
|
||||||
Self(join_all(api.into_iter().map(OrcErr::from_api)).await)
|
Self(join_all(api.into_iter().map(OrcErr::from_api)).await)
|
||||||
}
|
}
|
||||||
|
/// Iterate over the errors without consuming the collection
|
||||||
pub fn iter(&self) -> impl Iterator<Item = OrcErr> + '_ { self.0.iter().cloned() }
|
pub fn iter(&self) -> impl Iterator<Item = OrcErr> + '_ { self.0.iter().cloned() }
|
||||||
}
|
}
|
||||||
impl From<OrcErr> for OrcErrv {
|
impl From<OrcErr> for OrcErrv {
|
||||||
@@ -156,8 +184,11 @@ impl fmt::Display for OrcErrv {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A result from a function that may return multiple errors.
|
||||||
pub type OrcRes<T> = Result<T, OrcErrv>;
|
pub type OrcRes<T> = Result<T, OrcErrv>;
|
||||||
|
|
||||||
|
/// If two fallible values both succeed return both values, otherwise return
|
||||||
|
/// all errors.
|
||||||
pub fn join_ok<T, U>(left: OrcRes<T>, right: OrcRes<U>) -> OrcRes<(T, U)> {
|
pub fn join_ok<T, U>(left: OrcRes<T>, right: OrcRes<U>) -> OrcRes<(T, U)> {
|
||||||
match (left, right) {
|
match (left, right) {
|
||||||
(Ok(t), Ok(u)) => Ok((t, u)),
|
(Ok(t), Ok(u)) => Ok((t, u)),
|
||||||
@@ -187,15 +218,22 @@ macro_rules! join_ok {
|
|||||||
};
|
};
|
||||||
(@TYPES) => { () };
|
(@TYPES) => { () };
|
||||||
(@VALUES $name:ident $(: $ty:ty)? = $val:expr ; $($names:ident $(: $tys:ty)? = $vals:expr;)*) => {
|
(@VALUES $name:ident $(: $ty:ty)? = $val:expr ; $($names:ident $(: $tys:ty)? = $vals:expr;)*) => {
|
||||||
$crate::error::join_ok($val, $crate::join_ok!(@VALUES $($names $(: $tys)? = $vals;)*))
|
$crate::join_ok($val, $crate::join_ok!(@VALUES $($names $(: $tys)? = $vals;)*))
|
||||||
};
|
};
|
||||||
(@VALUES) => { Ok(()) };
|
(@VALUES) => { Ok(()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an errv without an associated position, as opposed to [mk_errv].
|
||||||
|
/// While this is technically legal and sometimes needed in library code, all
|
||||||
|
/// errors that are technically possible to associate with at least one position
|
||||||
|
/// should be.
|
||||||
|
#[must_use]
|
||||||
pub fn mk_errv_floating(description: IStr, message: impl AsRef<str>) -> OrcErrv {
|
pub fn mk_errv_floating(description: IStr, message: impl AsRef<str>) -> OrcErrv {
|
||||||
mk_errv::<Pos>(description, message, [])
|
mk_errv::<Pos>(description, message, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an errv. The third argument can be an iterable of [ErrPos] or [Pos].
|
||||||
|
#[must_use]
|
||||||
pub fn mk_errv<I: Into<ErrPos>>(
|
pub fn mk_errv<I: Into<ErrPos>>(
|
||||||
description: IStr,
|
description: IStr,
|
||||||
message: impl AsRef<str>,
|
message: impl AsRef<str>,
|
||||||
@@ -203,12 +241,14 @@ pub fn mk_errv<I: Into<ErrPos>>(
|
|||||||
) -> OrcErrv {
|
) -> OrcErrv {
|
||||||
OrcErr {
|
OrcErr {
|
||||||
description,
|
description,
|
||||||
message: Arc::new(message.as_ref().to_string()),
|
message: Rc::new(message.as_ref().to_string()),
|
||||||
positions: posv.into_iter().map_into().collect(),
|
positions: posv.into_iter().map_into().collect(),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert a standard IO error into an Orchid error
|
||||||
|
#[must_use]
|
||||||
pub async fn async_io_err<I: Into<ErrPos>>(
|
pub async fn async_io_err<I: Into<ErrPos>>(
|
||||||
err: std::io::Error,
|
err: std::io::Error,
|
||||||
posv: impl IntoIterator<Item = I>,
|
posv: impl IntoIterator<Item = I>,
|
||||||
@@ -216,6 +256,8 @@ pub async fn async_io_err<I: Into<ErrPos>>(
|
|||||||
mk_errv(is(&err.kind().to_string()).await, err.to_string(), posv)
|
mk_errv(is(&err.kind().to_string()).await, err.to_string(), posv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decode an Unicode string, or produce a common error related to Unicode
|
||||||
|
/// decoding
|
||||||
pub async fn os_str_to_string<I: Into<ErrPos>>(
|
pub async fn os_str_to_string<I: Into<ErrPos>>(
|
||||||
str: &OsStr,
|
str: &OsStr,
|
||||||
posv: impl IntoIterator<Item = I>,
|
posv: impl IntoIterator<Item = I>,
|
||||||
@@ -262,6 +304,9 @@ pub async fn try_with_reporter<T>(fut: impl Future<Output = OrcRes<T>>) -> OrcRe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determine if there are pending errors or if this overarching procedure has a
|
||||||
|
/// chance to succeed
|
||||||
|
#[must_use]
|
||||||
pub async fn is_erroring() -> bool {
|
pub async fn is_erroring() -> bool {
|
||||||
(REPORTER.try_with(|r| !r.errors.borrow().is_empty()))
|
(REPORTER.try_with(|r| !r.errors.borrow().is_empty()))
|
||||||
.expect("Sidechannel errors must be caught by a reporter")
|
.expect("Sidechannel errors must be caught by a reporter")
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
//! Multiple-listener-single-delivery event system.
|
|
||||||
|
|
||||||
use std::mem;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
use std::sync::mpsc::{self, sync_channel};
|
|
||||||
|
|
||||||
struct Reply<T, U> {
|
|
||||||
resub: bool,
|
|
||||||
outcome: Result<U, T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Listener<T, E> {
|
|
||||||
sink: mpsc::SyncSender<T>,
|
|
||||||
source: mpsc::Receiver<Reply<T, E>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Event<T, U> {
|
|
||||||
listeners: Mutex<Vec<Listener<T, U>>>,
|
|
||||||
}
|
|
||||||
impl<T, U> Event<T, U> {
|
|
||||||
pub const fn new() -> Self { Self { listeners: Mutex::new(Vec::new()) } }
|
|
||||||
|
|
||||||
pub fn dispatch(&self, mut ev: T) -> Option<U> {
|
|
||||||
let mut listeners = self.listeners.lock().unwrap();
|
|
||||||
let mut alt_list = Vec::with_capacity(listeners.len());
|
|
||||||
mem::swap(&mut *listeners, &mut alt_list);
|
|
||||||
let mut items = alt_list.into_iter();
|
|
||||||
while let Some(l) = items.next() {
|
|
||||||
l.sink.send(ev).unwrap();
|
|
||||||
let Reply { resub, outcome } = l.source.recv().unwrap();
|
|
||||||
if resub {
|
|
||||||
listeners.push(l);
|
|
||||||
}
|
|
||||||
match outcome {
|
|
||||||
Ok(res) => {
|
|
||||||
listeners.extend(items);
|
|
||||||
return Some(res);
|
|
||||||
},
|
|
||||||
Err(next) => {
|
|
||||||
ev = next;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_one<V>(&self, mut filter: impl FnMut(&T) -> bool, f: impl FnOnce(T) -> (U, V)) -> V {
|
|
||||||
let mut listeners = self.listeners.lock().unwrap();
|
|
||||||
let (sink, request) = sync_channel(0);
|
|
||||||
let (response, source) = sync_channel(0);
|
|
||||||
listeners.push(Listener { sink, source });
|
|
||||||
mem::drop(listeners);
|
|
||||||
loop {
|
|
||||||
let t = request.recv().unwrap();
|
|
||||||
if filter(&t) {
|
|
||||||
let (u, v) = f(t);
|
|
||||||
response.send(Reply { resub: false, outcome: Ok(u) }).unwrap();
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
response.send(Reply { resub: true, outcome: Err(t) }).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, U> Default for Event<T, U> {
|
|
||||||
fn default() -> Self { Self::new() }
|
|
||||||
}
|
|
||||||
@@ -14,21 +14,27 @@ use regex::Regex;
|
|||||||
|
|
||||||
use crate::{api, match_mapping};
|
use crate::{api, match_mapping};
|
||||||
|
|
||||||
|
/// A unit of formattable text where the formatter must make a single choice
|
||||||
|
/// Converting from various types via [Into::into] keeps strings intact, but
|
||||||
|
/// [str::parse] resolves escape sequences
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct FmtUnit {
|
pub struct FmtUnit {
|
||||||
|
/// Sub-units
|
||||||
pub subs: Vec<FmtUnit>,
|
pub subs: Vec<FmtUnit>,
|
||||||
|
/// Parsed text templates for how to render this text
|
||||||
pub variants: Rc<Variants>,
|
pub variants: Rc<Variants>,
|
||||||
}
|
}
|
||||||
impl FmtUnit {
|
impl FmtUnit {
|
||||||
pub fn new(variants: Rc<Variants>, subs: impl IntoIterator<Item = FmtUnit>) -> Self {
|
pub fn new(variants: Rc<Variants>, subs: impl IntoIterator<Item = FmtUnit>) -> Self {
|
||||||
Self { subs: subs.into_iter().collect(), variants }
|
Self { subs: subs.into_iter().collect(), variants }
|
||||||
}
|
}
|
||||||
|
/// Deserialize from message
|
||||||
pub fn from_api(api: &api::FormattingUnit) -> Self {
|
pub fn from_api(api: &api::FormattingUnit) -> Self {
|
||||||
Self {
|
Self {
|
||||||
subs: api.subs.iter().map(Self::from_api).collect(),
|
subs: api.subs.iter().map(Self::from_api).collect(),
|
||||||
variants: Rc::new(Variants(
|
variants: Rc::new(Variants(
|
||||||
(api.variants.iter().map(|var| Variant {
|
(api.variants.iter().map(|var| FmtVariant {
|
||||||
bounded: var.bounded,
|
bounded: var.bounded,
|
||||||
elements: var.elements.iter().map(FmtElement::from_api).collect(),
|
elements: var.elements.iter().map(FmtElement::from_api).collect(),
|
||||||
}))
|
}))
|
||||||
@@ -36,6 +42,8 @@ impl FmtUnit {
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Serialize into message. String interner IDs used in the structure must
|
||||||
|
/// remain valid.
|
||||||
pub fn to_api(&self) -> api::FormattingUnit {
|
pub fn to_api(&self) -> api::FormattingUnit {
|
||||||
api::FormattingUnit {
|
api::FormattingUnit {
|
||||||
subs: self.subs.iter().map(Self::to_api).collect(),
|
subs: self.subs.iter().map(Self::to_api).collect(),
|
||||||
@@ -46,11 +54,13 @@ impl FmtUnit {
|
|||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Shorthand for a variable-length list that can be formatted in exactly one
|
||||||
|
/// way
|
||||||
pub fn sequence(
|
pub fn sequence(
|
||||||
head: &str,
|
head: &str,
|
||||||
delim: &str,
|
delim: &str,
|
||||||
tail: &str,
|
tail: &str,
|
||||||
seq_bnd: Option<bool>,
|
seq_bnd: bool,
|
||||||
seq: impl IntoIterator<Item = FmtUnit>,
|
seq: impl IntoIterator<Item = FmtUnit>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let items = seq.into_iter().collect_vec();
|
let items = seq.into_iter().collect_vec();
|
||||||
@@ -69,18 +79,37 @@ impl FromStr for FmtUnit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A single element of a format string. Composes into [FmtVariant]
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub enum FmtElement {
|
pub enum FmtElement {
|
||||||
Sub { slot: u32, bounded: Option<bool> },
|
/// a reference to an interpolable subunit in the enclosing [FmtUnit]
|
||||||
|
Sub {
|
||||||
|
/// Index into [FmtUnit::subs]
|
||||||
|
slot: u32,
|
||||||
|
/// Whether the subunit can use an unbounded (`Some(false)`) [FmtVariant],
|
||||||
|
/// it is restricted to bounded (`Some(true)`) [FmtVariant], or it should
|
||||||
|
/// inherit this information from the enclosing unit, meaning that the slot
|
||||||
|
/// is at the very end of the format string
|
||||||
|
bounded: Option<bool>,
|
||||||
|
},
|
||||||
|
/// a string snippet
|
||||||
String(Rc<String>),
|
String(Rc<String>),
|
||||||
|
/// an indented block
|
||||||
Indent(Vec<FmtElement>),
|
Indent(Vec<FmtElement>),
|
||||||
}
|
}
|
||||||
impl FmtElement {
|
impl FmtElement {
|
||||||
|
/// Create a plain string snippet
|
||||||
pub fn str(s: &'_ str) -> Self { Self::String(Rc::new(s.to_string())) }
|
pub fn str(s: &'_ str) -> Self { Self::String(Rc::new(s.to_string())) }
|
||||||
|
/// Create a slot for a subunit
|
||||||
pub fn sub(slot: u32, bounded: Option<bool>) -> Self { Self::Sub { slot, bounded } }
|
pub fn sub(slot: u32, bounded: Option<bool>) -> Self { Self::Sub { slot, bounded } }
|
||||||
|
/// Create a slot for a subunit's bounded representation
|
||||||
pub fn bounded(i: u32) -> Self { Self::sub(i, Some(true)) }
|
pub fn bounded(i: u32) -> Self { Self::sub(i, Some(true)) }
|
||||||
|
/// Create a slot for any representation of a subunit
|
||||||
pub fn unbounded(i: u32) -> Self { Self::sub(i, Some(false)) }
|
pub fn unbounded(i: u32) -> Self { Self::sub(i, Some(false)) }
|
||||||
|
/// Create an end slot bounded by the enclosing unit if that is bounded
|
||||||
pub fn last(i: u32) -> Self { Self::sub(i, None) }
|
pub fn last(i: u32) -> Self { Self::sub(i, None) }
|
||||||
|
/// Create a sequence of `len` unbounded slots capped by a slot of the
|
||||||
|
/// specified boundedness
|
||||||
pub fn sequence(len: usize, bounded: Option<bool>) -> Vec<Self> {
|
pub fn sequence(len: usize, bounded: Option<bool>) -> Vec<Self> {
|
||||||
match len.try_into().unwrap() {
|
match len.try_into().unwrap() {
|
||||||
0u32 => vec![],
|
0u32 => vec![],
|
||||||
@@ -88,6 +117,7 @@ impl FmtElement {
|
|||||||
n => (0..n - 1).map(FmtElement::unbounded).chain([FmtElement::sub(n - 1, bounded)]).collect(),
|
n => (0..n - 1).map(FmtElement::unbounded).chain([FmtElement::sub(n - 1, bounded)]).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Decode from a message
|
||||||
pub fn from_api(api: &api::FormattingElement) -> Self {
|
pub fn from_api(api: &api::FormattingElement) -> Self {
|
||||||
match_mapping!(api, api::FormattingElement => FmtElement {
|
match_mapping!(api, api::FormattingElement => FmtElement {
|
||||||
Indent(v => v.iter().map(FmtElement::from_api).collect()),
|
Indent(v => v.iter().map(FmtElement::from_api).collect()),
|
||||||
@@ -95,6 +125,7 @@ impl FmtElement {
|
|||||||
Sub{ *slot, *bounded },
|
Sub{ *slot, *bounded },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
/// Encode to message
|
||||||
pub fn to_api(&self) -> api::FormattingElement {
|
pub fn to_api(&self) -> api::FormattingElement {
|
||||||
match_mapping!(self, FmtElement => api::FormattingElement {
|
match_mapping!(self, FmtElement => api::FormattingElement {
|
||||||
Indent(v => v.iter().map(FmtElement::to_api).collect()),
|
Indent(v => v.iter().map(FmtElement::to_api).collect()),
|
||||||
@@ -104,39 +135,16 @@ impl FmtElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A particular way in which a value may be formatted in text.
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct Variant {
|
pub struct FmtVariant {
|
||||||
|
/// Whether this representation has an intrinsic end marker or it needs the
|
||||||
|
/// parent to provide one
|
||||||
pub bounded: bool,
|
pub bounded: bool,
|
||||||
|
/// Template string syntax elements
|
||||||
pub elements: Vec<FmtElement>,
|
pub elements: Vec<FmtElement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn variants_parse_test() {
|
|
||||||
let vars = Rc::new(Variants::default().bounded("({{{0}}})"));
|
|
||||||
let expected_vars = Rc::new(Variants(vec![Variant {
|
|
||||||
bounded: true,
|
|
||||||
elements: vec![
|
|
||||||
FmtElement::String(Rc::new("({".to_string())),
|
|
||||||
FmtElement::Sub { bounded: Some(false), slot: 0 },
|
|
||||||
FmtElement::String(Rc::new("})".to_string())),
|
|
||||||
],
|
|
||||||
}]));
|
|
||||||
assert_eq!(vars.as_ref(), expected_vars.as_ref());
|
|
||||||
let unit = vars.units(["1".into()]);
|
|
||||||
assert_eq!(unit, FmtUnit {
|
|
||||||
subs: vec![FmtUnit {
|
|
||||||
subs: vec![],
|
|
||||||
variants: Rc::new(Variants(vec![Variant {
|
|
||||||
bounded: true,
|
|
||||||
elements: vec![FmtElement::String(Rc::new("1".to_string()))]
|
|
||||||
}]))
|
|
||||||
}],
|
|
||||||
variants: expected_vars
|
|
||||||
});
|
|
||||||
let str = take_first(&unit, true);
|
|
||||||
assert_eq!(str, "({1})");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a collection of formatting strings for the same set of parameters
|
/// Represents a collection of formatting strings for the same set of parameters
|
||||||
/// from which the formatter can choose within their associated constraints.
|
/// from which the formatter can choose within their associated constraints.
|
||||||
///
|
///
|
||||||
@@ -145,7 +153,7 @@ fn variants_parse_test() {
|
|||||||
/// - {0l} causes the current end restriction to be applied to the parameter.
|
/// - {0l} causes the current end restriction to be applied to the parameter.
|
||||||
/// This is to be used if the parameter is at the very end of the variant.
|
/// This is to be used if the parameter is at the very end of the variant.
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, Default)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Default)]
|
||||||
pub struct Variants(pub Vec<Variant>);
|
pub struct Variants(pub Vec<FmtVariant>);
|
||||||
impl Variants {
|
impl Variants {
|
||||||
fn parse_phs(s: &'_ str) -> Vec<FmtElement> {
|
fn parse_phs(s: &'_ str) -> Vec<FmtElement> {
|
||||||
let re = Regex::new(r"(?<tpl>\{\d+?[bl]?\})|(\{\{)|(\}\})").unwrap();
|
let re = Regex::new(r"(?<tpl>\{\d+?[bl]?\})|(\{\{)|(\}\})").unwrap();
|
||||||
@@ -216,7 +224,7 @@ impl Variants {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn add(&mut self, bounded: bool, s: &'_ str) {
|
fn add(&mut self, bounded: bool, s: &'_ str) {
|
||||||
self.0.push(Variant { bounded, elements: Self::parse(s) })
|
self.0.push(FmtVariant { bounded, elements: Self::parse(s) })
|
||||||
}
|
}
|
||||||
/// This option is available in all positions.
|
/// This option is available in all positions.
|
||||||
/// See [Variants] for a description of the format strings
|
/// See [Variants] for a description of the format strings
|
||||||
@@ -231,35 +239,42 @@ impl Variants {
|
|||||||
self.add(false, s);
|
self.add(false, s);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
/// Produces formatting options for `len` parameters separated by `delim`.
|
||||||
|
/// `seq_bnd` indicates whether `delim` and `tail` can unambiguously indicate
|
||||||
|
/// the end of a subsequence. For consistency, the stricter of the two is
|
||||||
|
/// expected to be used
|
||||||
pub fn sequence(
|
pub fn sequence(
|
||||||
mut self,
|
mut self,
|
||||||
len: usize,
|
len: usize,
|
||||||
head: &str,
|
head: &str,
|
||||||
delim: &str,
|
delim: &str,
|
||||||
tail: &str,
|
tail: &str,
|
||||||
seq_bnd: Option<bool>,
|
seq_bnd: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let seq = chain!(
|
let seq = chain!(
|
||||||
[FmtElement::str(head)],
|
[FmtElement::str(head)],
|
||||||
Itertools::intersperse(
|
Itertools::intersperse(
|
||||||
FmtElement::sequence(len, seq_bnd).into_iter(),
|
FmtElement::sequence(len, Some(seq_bnd)).into_iter(),
|
||||||
FmtElement::str(delim),
|
FmtElement::str(delim),
|
||||||
),
|
),
|
||||||
[FmtElement::str(tail)],
|
[FmtElement::str(tail)],
|
||||||
);
|
);
|
||||||
self.0.push(Variant { bounded: true, elements: seq.collect_vec() });
|
self.0.push(FmtVariant { bounded: true, elements: seq.collect_vec() });
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
/// Pair the slots with subunits to produce a [FmtUnit]
|
||||||
pub fn units_own(self, subs: impl IntoIterator<Item = FmtUnit>) -> FmtUnit {
|
pub fn units_own(self, subs: impl IntoIterator<Item = FmtUnit>) -> FmtUnit {
|
||||||
FmtUnit::new(Rc::new(self), subs)
|
FmtUnit::new(Rc::new(self), subs)
|
||||||
}
|
}
|
||||||
|
/// Pair the slots with subunits to produce a [FmtUnit] by reference. These
|
||||||
|
/// objects should preferably be thread-locally cached whenever possible.
|
||||||
pub fn units(self: &Rc<Self>, subs: impl IntoIterator<Item = FmtUnit>) -> FmtUnit {
|
pub fn units(self: &Rc<Self>, subs: impl IntoIterator<Item = FmtUnit>) -> FmtUnit {
|
||||||
FmtUnit::new(self.clone(), subs)
|
FmtUnit::new(self.clone(), subs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<Rc<String>> for Variants {
|
impl From<Rc<String>> for Variants {
|
||||||
fn from(value: Rc<String>) -> Self {
|
fn from(value: Rc<String>) -> Self {
|
||||||
Self(vec![Variant { elements: vec![FmtElement::String(value)], bounded: true }])
|
Self(vec![FmtVariant { elements: vec![FmtElement::String(value)], bounded: true }])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<String> for Variants {
|
impl From<String> for Variants {
|
||||||
@@ -304,23 +319,18 @@ pub async fn take_first_fmt(v: &(impl Format + ?Sized)) -> String {
|
|||||||
take_first(&v.print(&FmtCtxImpl { _foo: PhantomData }).await, false)
|
take_first(&v.print(&FmtCtxImpl { _foo: PhantomData }).await, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// [Default] this if you need one
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FmtCtxImpl<'a> {
|
pub struct FmtCtxImpl<'a> {
|
||||||
_foo: PhantomData<&'a ()>,
|
_foo: PhantomData<&'a ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FmtCtx {
|
/// Additional settings to the formatter. Implemented by [FmtCtxImpl]. Currently
|
||||||
// fn print_as(&self, p: &(impl Format + ?Sized)) -> impl Future<Output =
|
/// not in use
|
||||||
// String> where Self: Sized {
|
pub trait FmtCtx {}
|
||||||
// async {
|
|
||||||
// // for now, always take the first option which is probably the one-line
|
|
||||||
// form let variants = p.print(self).await;
|
|
||||||
// take_first(&variants, true)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
impl FmtCtx for FmtCtxImpl<'_> {}
|
impl FmtCtx for FmtCtxImpl<'_> {}
|
||||||
|
|
||||||
|
/// A value that can be formatted into a string with multiple possible forms
|
||||||
pub trait Format {
|
pub trait Format {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> impl Future<Output = FmtUnit> + 'a;
|
fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> impl Future<Output = FmtUnit> + 'a;
|
||||||
@@ -337,3 +347,37 @@ pub async fn fmt_v<F: Format + ?Sized>(
|
|||||||
) -> impl Iterator<Item = String> {
|
) -> impl Iterator<Item = String> {
|
||||||
join_all(v.into_iter().map(|f| async move { take_first_fmt(f.borrow()).await })).await.into_iter()
|
join_all(v.into_iter().map(|f| async move { take_first_fmt(f.borrow()).await })).await.into_iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::format::{FmtElement, FmtUnit, FmtVariant, Variants, take_first};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn variants_parse_test() {
|
||||||
|
let vars = Rc::new(Variants::default().bounded("({{{0}}})"));
|
||||||
|
let expected_vars = Rc::new(Variants(vec![FmtVariant {
|
||||||
|
bounded: true,
|
||||||
|
elements: vec![
|
||||||
|
FmtElement::String(Rc::new("({".to_string())),
|
||||||
|
FmtElement::Sub { bounded: Some(false), slot: 0 },
|
||||||
|
FmtElement::String(Rc::new("})".to_string())),
|
||||||
|
],
|
||||||
|
}]));
|
||||||
|
assert_eq!(vars.as_ref(), expected_vars.as_ref());
|
||||||
|
let unit = vars.units(["1".into()]);
|
||||||
|
assert_eq!(unit, FmtUnit {
|
||||||
|
subs: vec![FmtUnit {
|
||||||
|
subs: vec![],
|
||||||
|
variants: Rc::new(Variants(vec![FmtVariant {
|
||||||
|
bounded: true,
|
||||||
|
elements: vec![FmtElement::String(Rc::new("1".to_string()))]
|
||||||
|
}]))
|
||||||
|
}],
|
||||||
|
variants: expected_vars
|
||||||
|
});
|
||||||
|
let str = take_first(&unit, true);
|
||||||
|
assert_eq!(str, "({1})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
//! Impure functions that can be triggered by Orchid code when a command
|
|
||||||
//! evaluates to an atom representing a command
|
|
||||||
|
|
||||||
use std::any::{Any, TypeId};
|
|
||||||
use std::cell::RefCell;
|
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
|
||||||
use trait_set::trait_set;
|
|
||||||
|
|
||||||
use super::nort::Expr;
|
|
||||||
use crate::foreign::atom::Atomic;
|
|
||||||
use crate::foreign::error::RTResult;
|
|
||||||
use crate::foreign::to_clause::ToClause;
|
|
||||||
use crate::location::CodeLocation;
|
|
||||||
|
|
||||||
trait_set! {
|
|
||||||
trait Handler = for<'a> Fn(&'a dyn Any, CodeLocation) -> Expr;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum HTEntry<'a> {
|
|
||||||
Handler(Box<dyn Handler + 'a>),
|
|
||||||
Forward(&'a (dyn Handler + 'a)),
|
|
||||||
}
|
|
||||||
impl<'a> AsRef<dyn Handler + 'a> for HTEntry<'a> {
|
|
||||||
fn as_ref(&self) -> &(dyn Handler + 'a) {
|
|
||||||
match self {
|
|
||||||
HTEntry::Handler(h) => &**h,
|
|
||||||
HTEntry::Forward(h) => *h,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A table of impure command handlers exposed to Orchid
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct HandlerTable<'a> {
|
|
||||||
handlers: HashMap<TypeId, HTEntry<'a>>,
|
|
||||||
}
|
|
||||||
impl<'a> HandlerTable<'a> {
|
|
||||||
/// Create a new [HandlerTable]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new() -> Self { Self { handlers: HashMap::new() } }
|
|
||||||
|
|
||||||
/// Add a handler function to interpret a command and select the continuation.
|
|
||||||
/// See [HandlerTable#with] for a declarative option.
|
|
||||||
pub fn register<T: 'static, R: ToClause>(&mut self, f: impl for<'b> FnMut(&'b T) -> R + 'a) {
|
|
||||||
let cell = RefCell::new(f);
|
|
||||||
let cb = move |a: &dyn Any, loc: CodeLocation| {
|
|
||||||
cell.borrow_mut()(a.downcast_ref().expect("found by TypeId")).to_expr(loc)
|
|
||||||
};
|
|
||||||
let prev = self.handlers.insert(TypeId::of::<T>(), HTEntry::Handler(Box::new(cb)));
|
|
||||||
assert!(prev.is_none(), "A handler for this type is already registered");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a handler function to interpret a command and select the continuation.
|
|
||||||
/// See [HandlerTable#register] for a procedural option.
|
|
||||||
pub fn with<T: 'static>(mut self, f: impl FnMut(&T) -> RTResult<Expr> + 'a) -> Self {
|
|
||||||
self.register(f);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find and execute the corresponding handler for this type
|
|
||||||
pub fn dispatch(&self, arg: &dyn Atomic, loc: CodeLocation) -> Option<Expr> {
|
|
||||||
(self.handlers.get(&arg.as_any_ref().type_id())).map(|ent| ent.as_ref()(arg.as_any_ref(), loc))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Combine two non-overlapping handler sets
|
|
||||||
#[must_use]
|
|
||||||
pub fn combine(mut self, other: Self) -> Self {
|
|
||||||
for (key, value) in other.handlers {
|
|
||||||
let prev = self.handlers.insert(key, value);
|
|
||||||
assert!(prev.is_none(), "Duplicate handlers")
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add entries that forward requests to a borrowed non-overlapping handler
|
|
||||||
/// set
|
|
||||||
pub fn link<'b: 'a>(mut self, other: &'b HandlerTable<'b>) -> Self {
|
|
||||||
for (key, value) in other.handlers.iter() {
|
|
||||||
let prev = self.handlers.insert(*key, HTEntry::Forward(value.as_ref()));
|
|
||||||
assert!(prev.is_none(), "Duplicate handlers")
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[allow(unconditional_recursion)]
|
|
||||||
#[allow(clippy::ptr_arg)]
|
|
||||||
mod test {
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use super::HandlerTable;
|
|
||||||
|
|
||||||
/// Ensure that the method I use to verify covariance actually passes with
|
|
||||||
/// covariant and fails with invariant
|
|
||||||
///
|
|
||||||
/// The failing case:
|
|
||||||
/// ```
|
|
||||||
/// struct Cov2<'a>(PhantomData<&'a mut &'a ()>);
|
|
||||||
/// fn fail<'a>(_c: &Cov2<'a>, _s: &'a String) { fail(_c, &String::new()) }
|
|
||||||
/// ```
|
|
||||||
#[allow(unused)]
|
|
||||||
fn covariant_control() {
|
|
||||||
struct Cov<'a>(PhantomData<&'a ()>);
|
|
||||||
fn pass<'a>(_c: &Cov<'a>, _s: &'a String) { pass(_c, &String::new()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The &mut ensures that 'a in the two functions must be disjoint, and that
|
|
||||||
/// ht must outlive both. For this to compile, Rust has to cast ht to the
|
|
||||||
/// shorter lifetimes, ensuring covariance
|
|
||||||
#[allow(unused)]
|
|
||||||
fn assert_covariant() {
|
|
||||||
fn pass<'a>(_ht: HandlerTable<'a>, _s: &'a String) { pass(_ht, &String::new()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +1,120 @@
|
|||||||
use std::num::NonZeroU64;
|
use std::ops::{Index, IndexMut};
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
|
||||||
use std::sync::{Mutex, MutexGuard, OnceLock};
|
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
enum Rec<T> {
|
||||||
|
Val(T),
|
||||||
|
Next(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A simple and very fast store that assigns small stable integer IDs to
|
||||||
|
/// objects. It uses a free-list for O(1) insertion, deletion and retrieval.
|
||||||
pub struct IdStore<T> {
|
pub struct IdStore<T> {
|
||||||
table: OnceLock<Mutex<HashMap<NonZeroU64, T>>>,
|
first: usize,
|
||||||
id: AtomicU64,
|
values: Vec<Rec<T>>,
|
||||||
}
|
}
|
||||||
impl<T> IdStore<T> {
|
impl<T> IdStore<T> {
|
||||||
pub const fn new() -> Self { Self { table: OnceLock::new(), id: AtomicU64::new(1) } }
|
pub fn new() -> Self { IdStore { first: 0, values: Vec::new() } }
|
||||||
pub fn add(&self, t: T) -> IdRecord<'_, T> {
|
pub fn add(&mut self, value: T) -> usize {
|
||||||
let tbl = self.table.get_or_init(Mutex::default);
|
if self.first == 0 && self.values.is_empty() {
|
||||||
let mut tbl_g = tbl.lock().unwrap();
|
self.first = 1;
|
||||||
let id: NonZeroU64 = self.id.fetch_add(1, Ordering::Relaxed).try_into().unwrap();
|
self.values.push(Rec::Val(value));
|
||||||
assert!(tbl_g.insert(id, t).is_none(), "atom ID wraparound");
|
return 0;
|
||||||
IdRecord(id, tbl_g)
|
}
|
||||||
|
if self.first == self.values.len() {
|
||||||
|
let len = self.values.len();
|
||||||
|
self.values.extend((len..len * 2).map(|i| Rec::Next(i + 1)));
|
||||||
|
}
|
||||||
|
let Some(rec) = self.values.get_mut(self.first) else {
|
||||||
|
panic!("Bounds check and growth above")
|
||||||
|
};
|
||||||
|
let Rec::Next(next) = rec else {
|
||||||
|
panic!("first should always point to an empty space or one past the length")
|
||||||
|
};
|
||||||
|
let id = std::mem::replace(&mut self.first, *next);
|
||||||
|
*rec = Rec::Val(value);
|
||||||
|
id
|
||||||
}
|
}
|
||||||
pub fn get(&self, id: impl Into<NonZeroU64>) -> Option<IdRecord<'_, T>> {
|
pub fn add_with(&mut self, cb: impl FnOnce(usize) -> T) -> usize { self.add(cb(self.first)) }
|
||||||
let tbl = self.table.get_or_init(Mutex::default);
|
pub fn remove(&mut self, id: usize) -> T {
|
||||||
let tbl_g = tbl.lock().unwrap();
|
let Some(rec) = self.values.get_mut(id) else { panic!("Index out of bounds") };
|
||||||
let id64 = id.into();
|
let Rec::Val(val) = std::mem::replace(rec, Rec::Next(self.first)) else {
|
||||||
if tbl_g.contains_key(&id64) { Some(IdRecord(id64, tbl_g)) } else { None }
|
panic!("Index vacated")
|
||||||
|
};
|
||||||
|
self.first = id;
|
||||||
|
val
|
||||||
|
}
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = (usize, &T)> {
|
||||||
|
(self.values.iter().enumerate())
|
||||||
|
.filter_map(|(i, rec)| if let Rec::Val(val) = rec { Some((i, val)) } else { None })
|
||||||
|
}
|
||||||
|
pub fn iter_mut(&mut self) -> impl Iterator<Item = (usize, &mut T)> {
|
||||||
|
(self.values.iter_mut().enumerate())
|
||||||
|
.filter_map(|(i, rec)| if let Rec::Val(val) = rec { Some((i, val)) } else { None })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[allow(clippy::type_complexity, reason = "This is verbose enough as it is")]
|
||||||
|
pub struct IntoIter<T>(
|
||||||
|
std::iter::FilterMap<
|
||||||
|
std::iter::Enumerate<std::vec::IntoIter<Rec<T>>>,
|
||||||
|
fn((usize, Rec<T>)) -> Option<(usize, T)>,
|
||||||
|
>,
|
||||||
|
);
|
||||||
|
impl<T> Iterator for IntoIter<T> {
|
||||||
|
type Item = (usize, T);
|
||||||
|
fn next(&mut self) -> Option<Self::Item> { self.0.next() }
|
||||||
|
fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
|
||||||
|
}
|
||||||
|
impl<T> IntoIterator for IdStore<T> {
|
||||||
|
type Item = (usize, T);
|
||||||
|
type IntoIter = IntoIter<T>;
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
IntoIter(
|
||||||
|
(self.values.into_iter().enumerate())
|
||||||
|
.filter_map(|(i, rec)| if let Rec::Val(val) = rec { Some((i, val)) } else { None }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> Index<usize> for IdStore<T> {
|
||||||
|
type Output = T;
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
match self.values.get(index) {
|
||||||
|
Some(Rec::Val(val)) => val,
|
||||||
|
_ => panic!("Invalid or stale index"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> IndexMut<usize> for IdStore<T> {
|
||||||
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
|
match self.values.get_mut(index) {
|
||||||
|
Some(Rec::Val(val)) => val,
|
||||||
|
_ => panic!("Invalid or stale index"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn is_empty(&self) -> bool { self.len() == 0 }
|
|
||||||
pub fn len(&self) -> usize { self.table.get().map(|t| t.lock().unwrap().len()).unwrap_or(0) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for IdStore<T> {
|
impl<T> Default for IdStore<T> {
|
||||||
fn default() -> Self { Self::new() }
|
fn default() -> Self { Self::new() }
|
||||||
}
|
}
|
||||||
|
impl<A> FromIterator<A> for IdStore<A> {
|
||||||
|
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
|
||||||
|
let values = iter.into_iter().map(|a| Rec::Val(a)).collect_vec();
|
||||||
|
Self { first: values.len(), values }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct IdRecord<'a, T>(NonZeroU64, MutexGuard<'a, HashMap<NonZeroU64, T>>);
|
#[cfg(test)]
|
||||||
impl<T> IdRecord<'_, T> {
|
mod test {
|
||||||
pub fn id(&self) -> NonZeroU64 { self.0 }
|
use super::*;
|
||||||
pub fn remove(mut self) -> T { self.1.remove(&self.0).unwrap() }
|
|
||||||
}
|
#[test]
|
||||||
impl<T> Deref for IdRecord<'_, T> {
|
fn add_and_retrieve() {
|
||||||
type Target = T;
|
let mut store = IdStore::new();
|
||||||
fn deref(&self) -> &Self::Target {
|
let key1 = store.add(14);
|
||||||
self.1.get(&self.0).expect("Existence checked on construction")
|
let key2 = store.add(34);
|
||||||
}
|
assert_eq!(store[key1], 14);
|
||||||
}
|
assert_eq!(store[key2], 34);
|
||||||
impl<T> DerefMut for IdRecord<'_, T> {
|
assert_eq!(store.remove(key1), 14);
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
assert_eq!(store.iter().collect_vec(), vec![(key2, &34)]);
|
||||||
self.1.get_mut(&self.0).expect("Existence checked on construction")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,22 +10,27 @@ use task_local::task_local;
|
|||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
|
|
||||||
|
/// Implementation-specific backing object for an interned string.
|
||||||
pub trait IStrHandle: AsRef<str> {
|
pub trait IStrHandle: AsRef<str> {
|
||||||
fn rc(&self) -> Rc<String>;
|
fn rc(&self) -> Rc<String>;
|
||||||
}
|
}
|
||||||
|
/// Implementation-specific backing object for an interned sequence of interned
|
||||||
|
/// strings.
|
||||||
pub trait IStrvHandle: AsRef<[IStr]> {
|
pub trait IStrvHandle: AsRef<[IStr]> {
|
||||||
fn rc(&self) -> Rc<Vec<IStr>>;
|
fn rc(&self) -> Rc<Vec<IStr>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interned string created with [is] or [es]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct IStr(pub api::TStr, pub Rc<dyn IStrHandle>);
|
pub struct IStr(pub api::TStr, pub Rc<dyn IStrHandle>);
|
||||||
impl IStr {
|
impl IStr {
|
||||||
/// Obtain a unique ID for this interned data.
|
/// Obtain a unique ID for this interned data
|
||||||
///
|
///
|
||||||
/// NOTICE: the ID is guaranteed to be the same for any interned instance of
|
/// NOTICE: the ID is guaranteed to be the same for any interned instance of
|
||||||
/// the same value only as long as at least one instance exists. If a value is
|
/// the same value only as long as at least one instance exists. If a value is
|
||||||
/// no longer interned, the interner is free to forget about it.
|
/// no longer interned, the interner is free to forget about it
|
||||||
pub fn to_api(&self) -> api::TStr { self.0 }
|
pub fn to_api(&self) -> api::TStr { self.0 }
|
||||||
|
/// Owned reference to a shared instance of the interned string
|
||||||
pub fn rc(&self) -> Rc<String> { self.1.rc() }
|
pub fn rc(&self) -> Rc<String> { self.1.rc() }
|
||||||
}
|
}
|
||||||
impl Deref for IStr {
|
impl Deref for IStr {
|
||||||
@@ -45,15 +50,18 @@ impl Display for IStr {
|
|||||||
impl Debug for IStr {
|
impl Debug for IStr {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "IStr({self}") }
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "IStr({self}") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interned string sequence
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct IStrv(pub api::TStrv, pub Rc<dyn IStrvHandle>);
|
pub struct IStrv(pub api::TStrv, pub Rc<dyn IStrvHandle>);
|
||||||
impl IStrv {
|
impl IStrv {
|
||||||
/// Obtain a unique ID for this interned data.
|
/// Obtain a unique ID for this interned data
|
||||||
///
|
///
|
||||||
/// NOTICE: the ID is guaranteed to be the same for any interned instance of
|
/// NOTICE: the ID is guaranteed to be the same for any interned instance of
|
||||||
/// the same value only as long as at least one instance exists. If a value is
|
/// the same value only as long as at least one instance exists. If a value is
|
||||||
/// no longer interned, the interner is free to forget about it.
|
/// no longer interned, the interner is free to forget about it
|
||||||
pub fn to_api(&self) -> api::TStrv { self.0 }
|
pub fn to_api(&self) -> api::TStrv { self.0 }
|
||||||
|
/// Owned reference to a shared instance of the interned sequence
|
||||||
pub fn rc(&self) -> Rc<Vec<IStr>> { self.1.rc() }
|
pub fn rc(&self) -> Rc<Vec<IStr>> { self.1.rc() }
|
||||||
}
|
}
|
||||||
impl Deref for IStrv {
|
impl Deref for IStrv {
|
||||||
@@ -84,10 +92,23 @@ impl Debug for IStrv {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "IStrv({self})") }
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "IStrv({self})") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Injectable interner interface
|
||||||
|
///
|
||||||
|
/// [Self::is] and [Self::iv] return an existing ID if any [IStrHandle] or
|
||||||
|
/// [IStrvHandle] for the same value is still live, and any ID currently not
|
||||||
|
/// used with the same type otherwise
|
||||||
|
///
|
||||||
|
/// [Self::es] and [Self::ev] find an existing value by its key if any
|
||||||
|
/// [IStrHandle] or [IStrvHandle] for the same ID is still live. If all objects
|
||||||
|
/// are gone the functions may work or panic.
|
||||||
pub trait InternerSrv {
|
pub trait InternerSrv {
|
||||||
|
/// Intern a string
|
||||||
fn is<'a>(&'a self, v: &'a str) -> LocalBoxFuture<'a, IStr>;
|
fn is<'a>(&'a self, v: &'a str) -> LocalBoxFuture<'a, IStr>;
|
||||||
|
/// Find an existing string by its key
|
||||||
fn es(&self, t: api::TStr) -> LocalBoxFuture<'_, IStr>;
|
fn es(&self, t: api::TStr) -> LocalBoxFuture<'_, IStr>;
|
||||||
|
/// Intern a str vector
|
||||||
fn iv<'a>(&'a self, v: &'a [IStr]) -> LocalBoxFuture<'a, IStrv>;
|
fn iv<'a>(&'a self, v: &'a [IStr]) -> LocalBoxFuture<'a, IStrv>;
|
||||||
|
/// Find an existing str vector by its key
|
||||||
fn ev(&self, t: api::TStrv) -> LocalBoxFuture<'_, IStrv>;
|
fn ev(&self, t: api::TStrv) -> LocalBoxFuture<'_, IStrv>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +116,8 @@ task_local! {
|
|||||||
static INTERNER: Rc<dyn InternerSrv>;
|
static INTERNER: Rc<dyn InternerSrv>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Install a global interner. Within this future, the global [is], [iv], [es]
|
||||||
|
/// and [ev] functions call the provided [InternerSrv]
|
||||||
pub async fn with_interner<F: Future>(val: Rc<dyn InternerSrv>, fut: F) -> F::Output {
|
pub async fn with_interner<F: Future>(val: Rc<dyn InternerSrv>, fut: F) -> F::Output {
|
||||||
INTERNER.scope(val, fut).await
|
INTERNER.scope(val, fut).await
|
||||||
}
|
}
|
||||||
@@ -103,11 +126,28 @@ fn get_interner() -> Rc<dyn InternerSrv> {
|
|||||||
INTERNER.try_with(|i| i.clone()).expect("Interner not initialized")
|
INTERNER.try_with(|i| i.clone()).expect("Interner not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Intern a `String` (find its ID or assign it a new one)
|
||||||
pub async fn is(v: &str) -> IStr { get_interner().is(v).await }
|
pub async fn is(v: &str) -> IStr { get_interner().is(v).await }
|
||||||
|
/// Intern a `Vec<IStr>` (find its ID or assign it a new one)
|
||||||
pub async fn iv(v: &[IStr]) -> IStrv { get_interner().iv(v).await }
|
pub async fn iv(v: &[IStr]) -> IStrv { get_interner().iv(v).await }
|
||||||
|
/// Find a live [IStr] by its ID
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This function may panic if there are no other references to the [IStr] we're
|
||||||
|
/// searching for, as the interner is free to forget about unreferenced values
|
||||||
pub async fn es(v: api::TStr) -> IStr { get_interner().es(v).await }
|
pub async fn es(v: api::TStr) -> IStr { get_interner().es(v).await }
|
||||||
|
/// Find a live [IStrv] by its ID
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This function may panic if there are no other references to the [IStrv]
|
||||||
|
/// we're searching for, as the interner is free to forget about unreferenced
|
||||||
|
/// values
|
||||||
pub async fn ev(v: api::TStrv) -> IStrv { get_interner().ev(v).await }
|
pub async fn ev(v: api::TStrv) -> IStrv { get_interner().ev(v).await }
|
||||||
|
|
||||||
|
/// Basic engine for an interner that supports recovering if a token is not
|
||||||
|
/// found locally.
|
||||||
pub mod local_interner {
|
pub mod local_interner {
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
@@ -144,6 +184,7 @@ pub mod local_interner {
|
|||||||
fn new_interned(token: Self::Token, handle: Rc<Handle<Self>>) -> Self::Interned;
|
fn new_interned(token: Self::Token, handle: Rc<Handle<Self>>) -> Self::Interned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// String-specific values for [InternableCard]
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct StrBranch;
|
pub struct StrBranch;
|
||||||
impl InternableCard for StrBranch {
|
impl InternableCard for StrBranch {
|
||||||
@@ -154,6 +195,7 @@ pub mod local_interner {
|
|||||||
fn new_interned(t: Self::Token, h: Rc<Handle<Self>>) -> Self::Interned { IStr(t, h) }
|
fn new_interned(t: Self::Token, h: Rc<Handle<Self>>) -> Self::Interned { IStr(t, h) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Vector-specific values for [InternableCard]
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct StrvBranch;
|
pub struct StrvBranch;
|
||||||
impl InternableCard for StrvBranch {
|
impl InternableCard for StrvBranch {
|
||||||
@@ -208,8 +250,8 @@ pub mod local_interner {
|
|||||||
/// Information retained about an interned token indexed both by key and
|
/// Information retained about an interned token indexed both by key and
|
||||||
/// value.
|
/// value.
|
||||||
struct Rec<B: InternableCard> {
|
struct Rec<B: InternableCard> {
|
||||||
/// This reference is weak, but the [Drop] handler of [Handle] removes all
|
/// This reference is weak, but the [Drop] handler of [Handle] removes the
|
||||||
/// [Rec]s from the interner so it is guaranteed to be live.
|
/// [Rec] from the interner so it is guaranteed to be live.
|
||||||
handle: Weak<Handle<B>>,
|
handle: Weak<Handle<B>>,
|
||||||
/// Keys for indexing from either table
|
/// Keys for indexing from either table
|
||||||
data: Data<B>,
|
data: Data<B>,
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ impl<'a, I: Iterator<Item = E> + Clone, E: fmt::Display> fmt::Display for PrintL
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait IteratorPrint: Iterator<Item: fmt::Display> + Clone {
|
pub trait IteratorPrint: Iterator<Item: fmt::Display> + Clone {
|
||||||
|
/// Pretty-print a list with a comma-separated enumeration and an operator
|
||||||
|
/// word (such as "and" or "or") inserted before the last
|
||||||
fn display<'a>(self, operator: &'a str) -> PrintList<'a, Self, Self::Item> {
|
fn display<'a>(self, operator: &'a str) -> PrintList<'a, Self, Self::Item> {
|
||||||
PrintList(self, operator)
|
PrintList(self, operator)
|
||||||
}
|
}
|
||||||
@@ -1,33 +1,44 @@
|
|||||||
pub use async_once_cell;
|
pub use async_once_cell;
|
||||||
use orchid_api as api;
|
use orchid_api as api;
|
||||||
|
|
||||||
pub mod binary;
|
mod on_drop;
|
||||||
pub mod box_cow;
|
pub use on_drop::*;
|
||||||
pub mod boxed_iter;
|
mod binary;
|
||||||
pub mod char_filter;
|
pub use binary::*;
|
||||||
|
mod id_store;
|
||||||
|
pub use id_store::*;
|
||||||
|
mod boxed_iter;
|
||||||
|
pub use boxed_iter::*;
|
||||||
|
mod char_filter;
|
||||||
|
pub use char_filter::*;
|
||||||
pub mod clone;
|
pub mod clone;
|
||||||
pub mod combine;
|
mod error;
|
||||||
pub mod error;
|
pub use error::*;
|
||||||
pub mod event;
|
mod format;
|
||||||
pub mod format;
|
pub use format::*;
|
||||||
pub mod future_debug;
|
mod interner;
|
||||||
pub mod id_store;
|
pub use interner::*;
|
||||||
pub mod interner;
|
mod iter_print;
|
||||||
pub mod iter_utils;
|
pub use iter_print::*;
|
||||||
pub mod join;
|
mod join;
|
||||||
mod localset;
|
pub use join::*;
|
||||||
pub mod location;
|
mod location;
|
||||||
pub mod logging;
|
pub use location::*;
|
||||||
|
mod logging;
|
||||||
|
pub use logging::*;
|
||||||
mod match_mapping;
|
mod match_mapping;
|
||||||
pub mod msg;
|
mod name;
|
||||||
pub mod name;
|
pub use name::*;
|
||||||
pub mod number;
|
mod number;
|
||||||
pub mod parse;
|
pub use number::*;
|
||||||
pub mod pure_seq;
|
mod parse;
|
||||||
pub mod reqnot;
|
pub use parse::*;
|
||||||
pub mod sequence;
|
mod comm;
|
||||||
pub mod side;
|
pub use comm::*;
|
||||||
pub mod stash;
|
mod side;
|
||||||
pub mod tl_cache;
|
pub use side::*;
|
||||||
pub mod tokens;
|
mod stash;
|
||||||
pub mod tree;
|
pub use stash::*;
|
||||||
|
mod tl_cache;
|
||||||
|
mod tree;
|
||||||
|
pub use tree::*;
|
||||||
|
|||||||
@@ -7,25 +7,34 @@ use std::ops::{Add, AddAssign, Range};
|
|||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
use crate::error::ErrPos;
|
use crate::{ErrPos, IStr, IteratorPrint, Sym, api, es, is, match_mapping, sym};
|
||||||
use crate::interner::{IStr, es, is};
|
|
||||||
use crate::name::Sym;
|
|
||||||
use crate::{api, match_mapping, sym};
|
|
||||||
|
|
||||||
trait_set! {
|
trait_set! {
|
||||||
pub trait GetSrc = FnMut(&Sym) -> IStr;
|
pub trait GetSrc = FnMut(&Sym) -> IStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// One or more positions in code that are associated with an event, value, or
|
||||||
|
/// other consequence of that code
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Pos {
|
pub enum Pos {
|
||||||
|
/// Location not known, for example because the expression was decoded from a
|
||||||
|
/// source that doesn't have a meaningful location attached, from a format
|
||||||
|
/// that does not encode location data
|
||||||
None,
|
None,
|
||||||
|
/// If the expression in question is a slot, it receives the substituted
|
||||||
|
/// expression's position. If the expression is being placed into a slot, this
|
||||||
|
/// is discarded. In all other cases, it is a conflict and an error
|
||||||
SlotTarget,
|
SlotTarget,
|
||||||
/// Used in functions to denote the generated code that carries on the
|
/// Used in functions to denote the generated code that carries on the
|
||||||
/// location of the call. Not allowed in the const tree.
|
/// location of the call. Not allowed in the const tree
|
||||||
Inherit,
|
Inherit,
|
||||||
|
/// ID and parameters of a generator (such as an extension)
|
||||||
Gen(CodeGenInfo),
|
Gen(CodeGenInfo),
|
||||||
/// Range and file
|
/// Range and file
|
||||||
SrcRange(SrcRange),
|
SrcRange(SrcRange),
|
||||||
|
/// More than one positions. This vec should not contain another [Pos::Multi]
|
||||||
|
/// and should be `>=2` long. To ensure this, use `+` and `+=` to combine
|
||||||
|
/// positions and do not construct this directly.
|
||||||
Multi(Vec<Pos>),
|
Multi(Vec<Pos>),
|
||||||
}
|
}
|
||||||
impl Pos {
|
impl Pos {
|
||||||
@@ -33,6 +42,7 @@ impl Pos {
|
|||||||
match self {
|
match self {
|
||||||
Self::Gen(g) => g.to_string(),
|
Self::Gen(g) => g.to_string(),
|
||||||
Self::SrcRange(sr) => sr.pretty_print(&get_src(&sr.path)),
|
Self::SrcRange(sr) => sr.pretty_print(&get_src(&sr.path)),
|
||||||
|
Self::Multi(posv) => posv.iter().display("and").to_string(),
|
||||||
// Can't pretty print partial and meta-location
|
// Can't pretty print partial and meta-location
|
||||||
other => format!("{other:?}"),
|
other => format!("{other:?}"),
|
||||||
}
|
}
|
||||||
@@ -106,7 +116,7 @@ impl SrcRange {
|
|||||||
pub fn new(range: Range<u32>, path: &Sym) -> Self {
|
pub fn new(range: Range<u32>, path: &Sym) -> Self {
|
||||||
Self { range: range.clone(), path: path.clone() }
|
Self { range: range.clone(), path: path.clone() }
|
||||||
}
|
}
|
||||||
/// Create a dud [SourceRange] for testing. Its value is unspecified and
|
/// Create a dud [SrcRange] for testing. Its value is unspecified and
|
||||||
/// volatile.
|
/// volatile.
|
||||||
pub async fn mock() -> Self { Self { range: 0..1, path: sym!(test) } }
|
pub async fn mock() -> Self { Self { range: 0..1, path: sym!(test) } }
|
||||||
/// Path the source text was loaded from
|
/// Path the source text was loaded from
|
||||||
@@ -123,6 +133,10 @@ impl SrcRange {
|
|||||||
pub fn map_range(&self, map: impl FnOnce(Range<u32>) -> Range<u32>) -> Self {
|
pub fn map_range(&self, map: impl FnOnce(Range<u32>) -> Range<u32>) -> Self {
|
||||||
Self { range: map(self.range()), path: self.path() }
|
Self { range: map(self.range()), path: self.path() }
|
||||||
}
|
}
|
||||||
|
/// Format the range in a way that VSCode can convert to a link and is
|
||||||
|
/// human-readable. For this operation we need the source code text, but
|
||||||
|
/// holding it in the position object is a bit heavy so instead we take it as
|
||||||
|
/// an argument
|
||||||
pub fn pretty_print(&self, src: &str) -> String {
|
pub fn pretty_print(&self, src: &str) -> String {
|
||||||
let (sl, sc) = pos2lc(src, self.range.start);
|
let (sl, sc) = pos2lc(src, self.range.start);
|
||||||
let (el, ec) = pos2lc(src, self.range.end);
|
let (el, ec) = pos2lc(src, self.range.end);
|
||||||
@@ -132,13 +146,21 @@ impl SrcRange {
|
|||||||
(false, _) => format!("{sl}:{sc}..{el}:{ec}"),
|
(false, _) => format!("{sl}:{sc}..{el}:{ec}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Zero-width position at a certain offset
|
||||||
pub fn zw(path: Sym, pos: u32) -> Self { Self { path, range: pos..pos } }
|
pub fn zw(path: Sym, pos: u32) -> Self { Self { path, range: pos..pos } }
|
||||||
|
/// Deserialize from a message
|
||||||
pub async fn from_api(api: &api::SourceRange) -> Self {
|
pub async fn from_api(api: &api::SourceRange) -> Self {
|
||||||
Self { path: Sym::from_api(api.path).await, range: api.range.clone() }
|
Self { path: Sym::from_api(api.path).await, range: api.range.clone() }
|
||||||
}
|
}
|
||||||
|
/// Serialize to a message
|
||||||
pub fn to_api(&self) -> api::SourceRange {
|
pub fn to_api(&self) -> api::SourceRange {
|
||||||
api::SourceRange { path: self.path.to_api(), range: self.range.clone() }
|
api::SourceRange { path: self.path.to_api(), range: self.range.clone() }
|
||||||
}
|
}
|
||||||
|
/// Connect two ranges into one
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// if the ranges are not from the same file
|
||||||
pub fn to(&self, rhs: &Self) -> Self {
|
pub fn to(&self, rhs: &Self) -> Self {
|
||||||
assert_eq!(self.path, rhs.path, "Range continues across files");
|
assert_eq!(self.path, rhs.path, "Range continues across files");
|
||||||
Self { path: self.path(), range: self.start().min(rhs.start())..self.end().max(rhs.end()) }
|
Self { path: self.path(), range: self.start().min(rhs.start())..self.end().max(rhs.end()) }
|
||||||
@@ -173,9 +195,11 @@ impl CodeGenInfo {
|
|||||||
}
|
}
|
||||||
/// Syntactic location
|
/// Syntactic location
|
||||||
pub fn pos(&self) -> Pos { Pos::Gen(self.clone()) }
|
pub fn pos(&self) -> Pos { Pos::Gen(self.clone()) }
|
||||||
|
/// Deserialize from a message
|
||||||
pub async fn from_api(api: &api::CodeGenInfo) -> Self {
|
pub async fn from_api(api: &api::CodeGenInfo) -> Self {
|
||||||
Self { generator: Sym::from_api(api.generator).await, details: es(api.details).await }
|
Self { generator: Sym::from_api(api.generator).await, details: es(api.details).await }
|
||||||
}
|
}
|
||||||
|
/// Serialize to a message
|
||||||
pub fn to_api(&self) -> api::CodeGenInfo {
|
pub fn to_api(&self) -> api::CodeGenInfo {
|
||||||
api::CodeGenInfo { generator: self.generator.to_api(), details: self.details.to_api() }
|
api::CodeGenInfo { generator: self.generator.to_api(), details: self.details.to_api() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,27 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::fmt::Arguments;
|
use std::fmt::Arguments;
|
||||||
use std::io::Write;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use task_local::task_local;
|
use task_local::task_local;
|
||||||
|
|
||||||
use crate::api;
|
use crate::{api, clone};
|
||||||
|
|
||||||
task_local! {
|
|
||||||
static DEFAULT_WRITER: RefCell<Box<dyn Write>>
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the stream used for [api::LogStrategy::Default]. If not set,
|
|
||||||
/// [std::io::stderr] will be used.
|
|
||||||
pub async fn with_default_stream<F: Future>(stderr: impl Write + 'static, fut: F) -> F::Output {
|
|
||||||
DEFAULT_WRITER.scope(RefCell::new(Box::new(stderr)), fut).await
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// A first argument to [write!] and [writeln!] that causes them to return a
|
||||||
|
/// future.
|
||||||
pub trait LogWriter {
|
pub trait LogWriter {
|
||||||
fn write_fmt<'a>(&'a self, fmt: Arguments<'a>) -> LocalBoxFuture<'a, ()>;
|
fn write_fmt<'a>(&'a self, fmt: Arguments<'a>) -> LocalBoxFuture<'a, ()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Injectable logging service passed to [with_logger]
|
||||||
pub trait Logger: Any {
|
pub trait Logger: Any {
|
||||||
|
/// Obtain a writer that processes the message according to the given category
|
||||||
fn writer(&self, category: &str) -> Rc<dyn LogWriter>;
|
fn writer(&self, category: &str) -> Rc<dyn LogWriter>;
|
||||||
|
/// Obtain a serializable limited description of what will eventually happen
|
||||||
|
/// to the message
|
||||||
fn strat(&self, category: &str) -> api::LogStrategy;
|
fn strat(&self, category: &str) -> api::LogStrategy;
|
||||||
|
/// Determine whether a certain category would get processed at all. This is a
|
||||||
|
/// shortcut
|
||||||
fn is_active(&self, category: &str) -> bool {
|
fn is_active(&self, category: &str) -> bool {
|
||||||
!matches!(self.strat(category), api::LogStrategy::Discard)
|
!matches!(self.strat(category), api::LogStrategy::Discard)
|
||||||
}
|
}
|
||||||
@@ -35,43 +31,46 @@ task_local! {
|
|||||||
static LOGGER: Rc<dyn Logger>;
|
static LOGGER: Rc<dyn Logger>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Within the future passed to this function the freestanding [log] and
|
||||||
|
/// [get_logger] functions can be used
|
||||||
pub async fn with_logger<F: Future>(logger: impl Logger + 'static, fut: F) -> F::Output {
|
pub async fn with_logger<F: Future>(logger: impl Logger + 'static, fut: F) -> F::Output {
|
||||||
LOGGER.scope(Rc::new(logger), fut).await
|
LOGGER.scope(Rc::new(logger), fut).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Obtain an async log writer
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use orchid_base::log;
|
||||||
|
/// async {
|
||||||
|
/// let user = "lorentz";
|
||||||
|
/// writeln!(log("info"), "Hello from {user}").await
|
||||||
|
/// };
|
||||||
|
/// ```
|
||||||
pub fn log(category: &str) -> Rc<dyn LogWriter> {
|
pub fn log(category: &str) -> Rc<dyn LogWriter> {
|
||||||
LOGGER.try_with(|l| l.writer(category)).expect("Logger not set!")
|
LOGGER.try_with(|l| l.writer(category)).expect("Logger not set!")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Obtain a reference to the current [Logger]. This is mostly useful for
|
||||||
|
/// [Logger::is_active]-based optimizations
|
||||||
pub fn get_logger() -> Rc<dyn Logger> { LOGGER.try_with(|l| l.clone()).expect("Logger not set!") }
|
pub fn get_logger() -> Rc<dyn Logger> { LOGGER.try_with(|l| l.clone()).expect("Logger not set!") }
|
||||||
|
|
||||||
pub mod test {
|
#[derive(Clone)]
|
||||||
use std::fmt::Arguments;
|
pub struct TestLogger(Rc<dyn Fn(String) -> LocalBoxFuture<'static, ()>>);
|
||||||
use std::rc::Rc;
|
impl LogWriter for TestLogger {
|
||||||
|
fn write_fmt<'a>(&'a self, fmt: Arguments<'a>) -> LocalBoxFuture<'a, ()> {
|
||||||
use futures::future::LocalBoxFuture;
|
(self.0)(fmt.to_string())
|
||||||
|
|
||||||
use crate::clone;
|
|
||||||
use crate::logging::{LogWriter, Logger};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TestLogger(Rc<dyn Fn(String) -> LocalBoxFuture<'static, ()>>);
|
|
||||||
impl LogWriter for TestLogger {
|
|
||||||
fn write_fmt<'a>(&'a self, fmt: Arguments<'a>) -> LocalBoxFuture<'a, ()> {
|
|
||||||
(self.0)(fmt.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Logger for TestLogger {
|
|
||||||
fn strat(&self, _category: &str) -> orchid_api::LogStrategy { orchid_api::LogStrategy::Default }
|
|
||||||
fn writer(&self, _category: &str) -> std::rc::Rc<dyn LogWriter> { Rc::new(self.clone()) }
|
|
||||||
}
|
|
||||||
impl TestLogger {
|
|
||||||
pub fn new(f: impl AsyncFn(String) + 'static) -> Self {
|
|
||||||
let f = Rc::new(f);
|
|
||||||
Self(Rc::new(move |s| clone!(f; Box::pin(async move { f(s).await }))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Default for TestLogger {
|
|
||||||
fn default() -> Self { TestLogger::new(async |s| eprint!("{s}")) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Logger for TestLogger {
|
||||||
|
fn strat(&self, _category: &str) -> orchid_api::LogStrategy { orchid_api::LogStrategy::Default }
|
||||||
|
fn writer(&self, _category: &str) -> std::rc::Rc<dyn LogWriter> { Rc::new(self.clone()) }
|
||||||
|
}
|
||||||
|
impl TestLogger {
|
||||||
|
pub fn new(f: impl AsyncFn(String) + 'static) -> Self {
|
||||||
|
let f = Rc::new(f);
|
||||||
|
Self(Rc::new(move |s| clone!(f; Box::pin(async move { f(s).await }))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Default for TestLogger {
|
||||||
|
fn default() -> Self { TestLogger::new(async |s| eprint!("{s}")) }
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
use std::io;
|
|
||||||
use std::pin::Pin;
|
|
||||||
|
|
||||||
use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
|
||||||
use orchid_api_traits::{Decode, Encode};
|
|
||||||
|
|
||||||
pub async fn send_msg(mut write: Pin<&mut impl AsyncWrite>, msg: &[u8]) -> io::Result<()> {
|
|
||||||
let mut len_buf = vec![];
|
|
||||||
let len_prefix = u32::try_from(msg.len()).expect("Message over 4GB not permitted on channel");
|
|
||||||
len_prefix.encode_vec(&mut len_buf);
|
|
||||||
write.write_all(&len_buf).await?;
|
|
||||||
write.write_all(msg).await?;
|
|
||||||
write.flush().await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn recv_msg(mut read: Pin<&mut impl AsyncRead>) -> io::Result<Vec<u8>> {
|
|
||||||
let mut len_buf = [0u8; (u32::BITS / 8) as usize];
|
|
||||||
read.read_exact(&mut len_buf).await?;
|
|
||||||
let len = u32::decode(Pin::new(&mut &len_buf[..])).await?;
|
|
||||||
let mut msg = vec![0u8; len as usize];
|
|
||||||
read.read_exact(&mut msg).await?;
|
|
||||||
Ok(msg)
|
|
||||||
}
|
|
||||||
@@ -11,8 +11,7 @@ use futures::future::{OptionFuture, join_all};
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
use crate::api;
|
use crate::{IStr, IStrv, api, es, ev, is, iv};
|
||||||
use crate::interner::{IStr, IStrv, es, ev, is, iv};
|
|
||||||
|
|
||||||
trait_set! {
|
trait_set! {
|
||||||
/// Traits that all name iterators should implement
|
/// Traits that all name iterators should implement
|
||||||
@@ -143,7 +142,6 @@ impl VName {
|
|||||||
}
|
}
|
||||||
/// Read a `::` separated namespaced name
|
/// Read a `::` separated namespaced name
|
||||||
pub async fn parse(s: &str) -> Result<Self, EmptyNameError> { Self::new(VPath::parse(s).await) }
|
pub async fn parse(s: &str) -> Result<Self, EmptyNameError> { Self::new(VPath::parse(s).await) }
|
||||||
pub async fn literal(s: &'static str) -> Self { Self::parse(s).await.expect("empty literal !?") }
|
|
||||||
/// Obtain an iterator over the segments of the name
|
/// Obtain an iterator over the segments of the name
|
||||||
pub fn iter(&self) -> impl Iterator<Item = IStr> + '_ { self.0.iter().cloned() }
|
pub fn iter(&self) -> impl Iterator<Item = IStr> + '_ { self.0.iter().cloned() }
|
||||||
}
|
}
|
||||||
@@ -213,10 +211,13 @@ impl Sym {
|
|||||||
pub fn id(&self) -> NonZeroU64 { self.0.to_api().0 }
|
pub fn id(&self) -> NonZeroU64 { self.0.to_api().0 }
|
||||||
/// Extern the sym for editing
|
/// Extern the sym for editing
|
||||||
pub fn to_vname(&self) -> VName { VName(self[..].to_vec()) }
|
pub fn to_vname(&self) -> VName { VName(self[..].to_vec()) }
|
||||||
|
/// Decode from a message
|
||||||
pub async fn from_api(marker: api::TStrv) -> Sym {
|
pub async fn from_api(marker: api::TStrv) -> Sym {
|
||||||
Self::from_tok(ev(marker).await).expect("Empty sequence found for serialized Sym")
|
Self::from_tok(ev(marker).await).expect("Empty sequence found for serialized Sym")
|
||||||
}
|
}
|
||||||
|
/// Encode into a message
|
||||||
pub fn to_api(&self) -> api::TStrv { self.tok().to_api() }
|
pub fn to_api(&self) -> api::TStrv { self.tok().to_api() }
|
||||||
|
/// Copy the symbol and extend it with a suffix
|
||||||
pub async fn suffix(&self, tokv: impl IntoIterator<Item = IStr>) -> Sym {
|
pub async fn suffix(&self, tokv: impl IntoIterator<Item = IStr>) -> Sym {
|
||||||
Self::new(self.0.iter().cloned().chain(tokv)).await.unwrap()
|
Self::new(self.0.iter().cloned().chain(tokv)).await.unwrap()
|
||||||
}
|
}
|
||||||
@@ -246,7 +247,7 @@ impl Deref for Sym {
|
|||||||
|
|
||||||
/// An abstraction over tokenized vs non-tokenized names so that they can be
|
/// An abstraction over tokenized vs non-tokenized names so that they can be
|
||||||
/// handled together in datastructures. The names can never be empty
|
/// handled together in datastructures. The names can never be empty
|
||||||
#[allow(clippy::len_without_is_empty)] // never empty
|
#[allow(clippy::len_without_is_empty, reason = "never empty")]
|
||||||
pub trait NameLike:
|
pub trait NameLike:
|
||||||
'static + Clone + Eq + Hash + fmt::Debug + fmt::Display + Borrow<[IStr]>
|
'static + Clone + Eq + Hash + fmt::Debug + fmt::Display + Borrow<[IStr]>
|
||||||
{
|
{
|
||||||
@@ -292,11 +293,11 @@ impl NameLike for VName {}
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! sym {
|
macro_rules! sym {
|
||||||
($seg1:tt $( :: $seg:tt)*) => {
|
($seg1:tt $( :: $seg:tt)*) => {
|
||||||
$crate::tl_cache!(async $crate::name::Sym : {
|
$crate::tl_cache!(async $crate::Sym : {
|
||||||
$crate::name::Sym::from_tok(
|
$crate::Sym::from_tok(
|
||||||
$crate::interner::iv(&[
|
$crate::iv(&[
|
||||||
$crate::interner::is($crate::sym!(@SEG $seg1)).await
|
$crate::is($crate::sym!(@SEG $seg1)).await
|
||||||
$( , $crate::interner::is($crate::sym!(@SEG $seg)).await )*
|
$( , $crate::is($crate::sym!(@SEG $seg)).await )*
|
||||||
])
|
])
|
||||||
.await
|
.await
|
||||||
).unwrap()
|
).unwrap()
|
||||||
@@ -316,10 +317,10 @@ macro_rules! sym {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! vname {
|
macro_rules! vname {
|
||||||
($seg1:tt $( :: $seg:tt)*) => {
|
($seg1:tt $( :: $seg:tt)*) => {
|
||||||
$crate::tl_cache!(async $crate::name::VName : {
|
$crate::tl_cache!(async $crate::VName : {
|
||||||
$crate::name::VName::new([
|
$crate::VName::new([
|
||||||
$crate::interner::is(stringify!($seg1)).await
|
$crate::is(stringify!($seg1)).await
|
||||||
$( , $crate::interner::is(stringify!($seg)).await )*
|
$( , $crate::is(stringify!($seg)).await )*
|
||||||
]).unwrap()
|
]).unwrap()
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
@@ -331,28 +332,26 @@ macro_rules! vname {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! vpath {
|
macro_rules! vpath {
|
||||||
($seg1:tt $( :: $seg:tt)*) => {
|
($seg1:tt $( :: $seg:tt)*) => {
|
||||||
$crate::tl_cache!(async $crate::name::VPath : {
|
$crate::tl_cache!(async $crate::VPath : {
|
||||||
$crate::name::VPath(vec![
|
$crate::VPath::new(vec![
|
||||||
$crate::interner::is(stringify!($seg1)).await
|
$crate::is(stringify!($seg1)).await
|
||||||
$( , $crate::interner::is(stringify!($seg)).await )*
|
$( , $crate::is(stringify!($seg)).await )*
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
() => {
|
() => {
|
||||||
$crate::name::VPath(vec![])
|
$crate::VPath::new(vec![])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
mod test {
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
use orchid_api_traits::spin_on;
|
use orchid_api_traits::spin_on;
|
||||||
|
|
||||||
use super::{NameLike, Sym, VName};
|
use crate::local_interner::local_interner;
|
||||||
use crate::interner::local_interner::local_interner;
|
use crate::{IStr, NameLike, Sym, VName, VPath, is, with_interner};
|
||||||
use crate::interner::{IStr, is, with_interner};
|
|
||||||
use crate::name::VPath;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn recur() {
|
pub fn recur() {
|
||||||
|
|||||||
@@ -1,283 +0,0 @@
|
|||||||
//! The NORT (Normal Order Referencing Tree) is the interpreter's runtime
|
|
||||||
//! representation of Orchid programs.
|
|
||||||
//!
|
|
||||||
//! It uses a locator tree to find bound variables in lambda functions, which
|
|
||||||
//! necessitates a normal reduction order because modifying the body by reducing
|
|
||||||
//! expressions would invalidate any locators in enclosing lambdas.
|
|
||||||
//!
|
|
||||||
//! Clauses are held in a mutable `Arc<Mutex<_>>`, so that after substitution
|
|
||||||
//! the instances of the argument remain linked and a reduction step applied to
|
|
||||||
//! any instance transforms all of them.
|
|
||||||
//!
|
|
||||||
//! To improve locality and make the tree less deep and locators shorter,
|
|
||||||
//! function calls store multiple arguments in a deque.
|
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::fmt;
|
|
||||||
use std::ops::DerefMut;
|
|
||||||
use std::sync::{Arc, Mutex, MutexGuard, TryLockError};
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use super::path_set::PathSet;
|
|
||||||
use crate::foreign::atom::Atom;
|
|
||||||
#[allow(unused)] // for doc
|
|
||||||
use crate::foreign::atom::Atomic;
|
|
||||||
use crate::foreign::error::{RTErrorObj, RTResult};
|
|
||||||
use crate::foreign::try_from_expr::TryFromExpr;
|
|
||||||
use crate::location::CodeLocation;
|
|
||||||
use crate::name::Sym;
|
|
||||||
#[allow(unused)] // for doc
|
|
||||||
use crate::parse::parsed;
|
|
||||||
use crate::utils::ddispatch::request;
|
|
||||||
|
|
||||||
/// Kinda like [AsMut] except it supports a guard
|
|
||||||
pub(crate) trait AsDerefMut<T> {
|
|
||||||
fn as_deref_mut(&mut self) -> impl DerefMut<Target = T> + '_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An expression with metadata
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Expr {
|
|
||||||
/// The actual value
|
|
||||||
pub clause: ClauseInst,
|
|
||||||
/// Information about the code that produced this value
|
|
||||||
pub location: CodeLocation,
|
|
||||||
}
|
|
||||||
impl Expr {
|
|
||||||
/// Constructor
|
|
||||||
pub fn new(clause: ClauseInst, location: CodeLocation) -> Self { Self { clause, location } }
|
|
||||||
/// Obtain the location of the expression
|
|
||||||
pub fn location(&self) -> CodeLocation { self.location.clone() }
|
|
||||||
|
|
||||||
/// Convert into any type that implements [TryFromExpr]. Calls to this
|
|
||||||
/// function are generated wherever a conversion is elided in an extern
|
|
||||||
/// function.
|
|
||||||
pub fn downcast<T: TryFromExpr>(self) -> RTResult<T> {
|
|
||||||
let Expr { mut clause, location } = self;
|
|
||||||
loop {
|
|
||||||
let cls_deref = clause.cls_mut();
|
|
||||||
match &*cls_deref {
|
|
||||||
Clause::Identity(alt) => {
|
|
||||||
let temp = alt.clone();
|
|
||||||
drop(cls_deref);
|
|
||||||
clause = temp;
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
drop(cls_deref);
|
|
||||||
return T::from_expr(Expr { clause, location });
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Visit all expressions in the tree. The search can be exited early by
|
|
||||||
/// returning [Some]
|
|
||||||
///
|
|
||||||
/// See also [parsed::Expr::search_all]
|
|
||||||
pub fn search_all<T>(&self, predicate: &mut impl FnMut(&Self) -> Option<T>) -> Option<T> {
|
|
||||||
if let Some(t) = predicate(self) {
|
|
||||||
return Some(t);
|
|
||||||
}
|
|
||||||
self.clause.inspect(|c| match c {
|
|
||||||
Clause::Identity(_alt) => unreachable!("Handled by inspect"),
|
|
||||||
Clause::Apply { f, x } =>
|
|
||||||
(f.search_all(predicate)).or_else(|| x.iter().find_map(|x| x.search_all(predicate))),
|
|
||||||
Clause::Lambda { body, .. } => body.search_all(predicate),
|
|
||||||
Clause::Constant(_) | Clause::LambdaArg | Clause::Atom(_) | Clause::Bottom(_) => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clone the refcounted [ClauseInst] out of the expression
|
|
||||||
#[must_use]
|
|
||||||
pub fn clsi(&self) -> ClauseInst { self.clause.clone() }
|
|
||||||
|
|
||||||
/// Read-Write access to the [Clause]
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// if the clause is already borrowed
|
|
||||||
pub fn cls_mut(&self) -> MutexGuard<'_, Clause> { self.clause.cls_mut() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Expr {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "{:?}@{}", self.clause, self.location)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Expr {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.clause) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsDerefMut<Clause> for Expr {
|
|
||||||
fn as_deref_mut(&mut self) -> impl DerefMut<Target = Clause> + '_ { self.clause.cls_mut() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A wrapper around expressions to handle their multiple occurences in
|
|
||||||
/// the tree together
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ClauseInst(pub Arc<Mutex<Clause>>);
|
|
||||||
impl ClauseInst {
|
|
||||||
/// Wrap a [Clause] in a shared container so that normalization steps are
|
|
||||||
/// applied to all references
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(cls: Clause) -> Self { Self(Arc::new(Mutex::new(cls))) }
|
|
||||||
|
|
||||||
/// Take the [Clause] out of this container if it's the last reference to it,
|
|
||||||
/// or return self.
|
|
||||||
pub fn try_unwrap(self) -> Result<Clause, ClauseInst> {
|
|
||||||
Arc::try_unwrap(self.0).map(|c| c.into_inner().unwrap()).map_err(Self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read-Write access to the shared clause instance
|
|
||||||
///
|
|
||||||
/// if the clause is already borrowed, this will block until it is released.
|
|
||||||
pub fn cls_mut(&self) -> MutexGuard<'_, Clause> { self.0.lock().unwrap() }
|
|
||||||
|
|
||||||
/// Call a predicate on the clause, returning whatever the
|
|
||||||
/// predicate returns. This is a convenience function for reaching
|
|
||||||
/// through the [Mutex]. The clause will never be [Clause::Identity].
|
|
||||||
#[must_use]
|
|
||||||
pub fn inspect<T>(&self, predicate: impl FnOnce(&Clause) -> T) -> T {
|
|
||||||
match &*self.cls_mut() {
|
|
||||||
Clause::Identity(sub) => sub.inspect(predicate),
|
|
||||||
x => predicate(x),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If this expression is an [Atomic], request an object of the given type.
|
|
||||||
/// If it's not an atomic, fail the request automatically.
|
|
||||||
#[must_use = "your request might not have succeeded"]
|
|
||||||
pub fn request<T: 'static>(&self) -> Option<T> {
|
|
||||||
match &*self.cls_mut() {
|
|
||||||
Clause::Atom(a) => request(&*a.0),
|
|
||||||
Clause::Identity(alt) => alt.request(),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Associate a location with this clause
|
|
||||||
pub fn into_expr(self, location: CodeLocation) -> Expr {
|
|
||||||
Expr { clause: self.clone(), location: location.clone() }
|
|
||||||
}
|
|
||||||
/// Check ahead-of-time if this clause contains an atom. Calls
|
|
||||||
/// [ClauseInst#cls] for read access.
|
|
||||||
///
|
|
||||||
/// Since atoms cannot become normalizable, if this is true and previous
|
|
||||||
/// normalization failed, the atom is known to be in normal form.
|
|
||||||
pub fn is_atom(&self) -> bool { matches!(&*self.cls_mut(), Clause::Atom(_)) }
|
|
||||||
|
|
||||||
/// Tries to unwrap the [Arc]. If that fails, clones it field by field.
|
|
||||||
/// If it's a [Clause::Atom] which cannot be cloned, wraps it in a
|
|
||||||
/// [Clause::Identity].
|
|
||||||
///
|
|
||||||
/// Implementation of [crate::foreign::to_clause::ToClause::to_clause]. The
|
|
||||||
/// trait is more general so it requires a location which this one doesn't.
|
|
||||||
pub fn into_cls(self) -> Clause {
|
|
||||||
self.try_unwrap().unwrap_or_else(|clsi| match &*clsi.cls_mut() {
|
|
||||||
Clause::Apply { f, x } => Clause::Apply { f: f.clone(), x: x.clone() },
|
|
||||||
Clause::Atom(_) => Clause::Identity(clsi.clone()),
|
|
||||||
Clause::Bottom(e) => Clause::Bottom(e.clone()),
|
|
||||||
Clause::Constant(c) => Clause::Constant(c.clone()),
|
|
||||||
Clause::Identity(sub) => Clause::Identity(sub.clone()),
|
|
||||||
Clause::Lambda { args, body } => Clause::Lambda { args: args.clone(), body: body.clone() },
|
|
||||||
Clause::LambdaArg => Clause::LambdaArg,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decides if this clause is the exact same instance as another. Most useful
|
|
||||||
/// to detect potential deadlocks.
|
|
||||||
pub fn is_same(&self, other: &Self) -> bool { Arc::ptr_eq(&self.0, &other.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for ClauseInst {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self.0.try_lock() {
|
|
||||||
Ok(expr) => write!(f, "{expr:?}"),
|
|
||||||
Err(TryLockError::Poisoned(_)) => write!(f, "<poisoned>"),
|
|
||||||
Err(TryLockError::WouldBlock) => write!(f, "<locked>"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for ClauseInst {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self.0.try_lock() {
|
|
||||||
Ok(expr) => write!(f, "{expr}"),
|
|
||||||
Err(TryLockError::Poisoned(_)) => write!(f, "<poisoned>"),
|
|
||||||
Err(TryLockError::WouldBlock) => write!(f, "<locked>"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsDerefMut<Clause> for ClauseInst {
|
|
||||||
fn as_deref_mut(&mut self) -> impl DerefMut<Target = Clause> + '_ { self.cls_mut() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Distinct types of expressions recognized by the interpreter
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Clause {
|
|
||||||
/// An expression that causes an error
|
|
||||||
Bottom(RTErrorObj),
|
|
||||||
/// Indicates that this [ClauseInst] has the same value as the other
|
|
||||||
/// [ClauseInst]. This has two benefits;
|
|
||||||
///
|
|
||||||
/// - [Clause] and therefore [Atomic] doesn't have to be [Clone] which saves
|
|
||||||
/// many synchronization primitives and reference counters in usercode
|
|
||||||
/// - it enforces on the type level that all copies are normalized together,
|
|
||||||
/// so accidental inefficiency in the interpreter is rarer.
|
|
||||||
///
|
|
||||||
/// That being said, it's still arbitrary many indirections, so when possible
|
|
||||||
/// APIs should be usable with a [ClauseInst] directly.
|
|
||||||
Identity(ClauseInst),
|
|
||||||
/// An opaque non-callable value, eg. a file handle
|
|
||||||
Atom(Atom),
|
|
||||||
/// A function application
|
|
||||||
Apply {
|
|
||||||
/// Function to be applied
|
|
||||||
f: Expr,
|
|
||||||
/// Argument to be substituted in the function
|
|
||||||
x: VecDeque<Expr>,
|
|
||||||
},
|
|
||||||
/// A name to be looked up in the interpreter's symbol table
|
|
||||||
Constant(Sym),
|
|
||||||
/// A function
|
|
||||||
Lambda {
|
|
||||||
/// A collection of (zero or more) paths to placeholders belonging to this
|
|
||||||
/// function
|
|
||||||
args: Option<PathSet>,
|
|
||||||
/// The tree produced by this function, with placeholders where the
|
|
||||||
/// argument will go
|
|
||||||
body: Expr,
|
|
||||||
},
|
|
||||||
/// A placeholder within a function that will be replaced upon application
|
|
||||||
LambdaArg,
|
|
||||||
}
|
|
||||||
impl Clause {
|
|
||||||
/// Wrap a clause in a refcounted lock
|
|
||||||
pub fn into_inst(self) -> ClauseInst { ClauseInst::new(self) }
|
|
||||||
/// Wrap a clause in an expression.
|
|
||||||
pub fn into_expr(self, location: CodeLocation) -> Expr { self.into_inst().into_expr(location) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Clause {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
Clause::Atom(a) => write!(f, "{a:?}"),
|
|
||||||
Clause::Bottom(err) => write!(f, "bottom({err})"),
|
|
||||||
Clause::LambdaArg => write!(f, "arg"),
|
|
||||||
Clause::Apply { f: fun, x } => write!(f, "({fun} {})", x.iter().join(" ")),
|
|
||||||
Clause::Lambda { args, body } => match args {
|
|
||||||
Some(path) => write!(f, "[\\{path}.{body}]"),
|
|
||||||
None => write!(f, "[\\_.{body}]"),
|
|
||||||
},
|
|
||||||
Clause::Constant(t) => write!(f, "{t}"),
|
|
||||||
Clause::Identity(other) => write!(f, "{{{other}}}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsDerefMut<Clause> for Clause {
|
|
||||||
fn as_deref_mut(&mut self) -> impl DerefMut<Target = Clause> + '_ { self }
|
|
||||||
}
|
|
||||||
@@ -3,10 +3,7 @@ use std::ops::Range;
|
|||||||
|
|
||||||
use ordered_float::NotNan;
|
use ordered_float::NotNan;
|
||||||
|
|
||||||
use crate::error::{OrcErrv, mk_errv};
|
use crate::{OrcErrv, SrcRange, Sym, is, mk_errv};
|
||||||
use crate::interner::is;
|
|
||||||
use crate::location::SrcRange;
|
|
||||||
use crate::name::Sym;
|
|
||||||
|
|
||||||
/// A number, either floating point or unsigned int, parsed by Orchid.
|
/// A number, either floating point or unsigned int, parsed by Orchid.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
|||||||
@@ -1,261 +0,0 @@
|
|||||||
//! Adaptor trait to embed Rust values in Orchid expressions
|
|
||||||
|
|
||||||
use std::any::Any;
|
|
||||||
use std::fmt;
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use never::Never;
|
|
||||||
|
|
||||||
use super::error::{RTError, RTResult};
|
|
||||||
use crate::interpreter::context::{RunEnv, RunParams};
|
|
||||||
use crate::interpreter::nort;
|
|
||||||
use crate::location::{CodeLocation, SourceRange};
|
|
||||||
use crate::name::NameLike;
|
|
||||||
use crate::parse::lexer::Lexeme;
|
|
||||||
use crate::parse::parsed;
|
|
||||||
use crate::utils::ddispatch::{request, Request, Responder};
|
|
||||||
|
|
||||||
/// Information returned by [Atomic::run].
|
|
||||||
pub enum AtomicReturn {
|
|
||||||
/// No work was done. If the atom takes an argument, it can be provided now
|
|
||||||
Inert(Atom),
|
|
||||||
/// Work was done, returns new clause and consumed gas. 1 gas is already
|
|
||||||
/// consumed by the virtual call, so nonzero values indicate expensive
|
|
||||||
/// operations.
|
|
||||||
Change(usize, nort::Clause),
|
|
||||||
}
|
|
||||||
impl AtomicReturn {
|
|
||||||
/// Report indicating that the value is inert. The result here is always [Ok],
|
|
||||||
/// it's meant to match the return type of [Atomic::run]
|
|
||||||
#[allow(clippy::unnecessary_wraps)]
|
|
||||||
pub fn inert<T: Atomic, E>(this: T) -> Result<Self, E> { Ok(Self::Inert(Atom::new(this))) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returned by [Atomic::run]
|
|
||||||
pub type AtomicResult = RTResult<AtomicReturn>;
|
|
||||||
|
|
||||||
/// General error produced when a non-function [Atom] is applied to something as
|
|
||||||
/// a function.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct NotAFunction(pub nort::Expr);
|
|
||||||
impl RTError for NotAFunction {}
|
|
||||||
impl fmt::Display for NotAFunction {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "{:?} is not a function", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information about a function call presented to an external function
|
|
||||||
pub struct CallData<'a, 'b> {
|
|
||||||
/// Location of the function expression
|
|
||||||
pub location: CodeLocation,
|
|
||||||
/// The argument the function was called on. Functions are curried
|
|
||||||
pub arg: nort::Expr,
|
|
||||||
/// Globally available information such as the list of all constants
|
|
||||||
pub env: &'a RunEnv<'b>,
|
|
||||||
/// Resource limits and other details specific to this interpreter run
|
|
||||||
pub params: &'a mut RunParams,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information about a normalization run presented to an atom
|
|
||||||
pub struct RunData<'a, 'b> {
|
|
||||||
/// Location of the atom
|
|
||||||
pub location: CodeLocation,
|
|
||||||
/// Globally available information such as the list of all constants
|
|
||||||
pub env: &'a RunEnv<'b>,
|
|
||||||
/// Resource limits and other details specific to this interpreter run
|
|
||||||
pub params: &'a mut RunParams,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Functionality the interpreter needs to handle a value
|
|
||||||
///
|
|
||||||
/// # Lifecycle methods
|
|
||||||
///
|
|
||||||
/// Atomics expose the methods [Atomic::redirect], [Atomic::run],
|
|
||||||
/// [Atomic::apply] and [Atomic::apply_mut] to interact with the interpreter.
|
|
||||||
/// The interpreter first tries to call `redirect` to find a subexpression to
|
|
||||||
/// normalize. If it returns `None` or the subexpression is inert, `run` is
|
|
||||||
/// called. `run` takes ownership of the value and returns a new one.
|
|
||||||
///
|
|
||||||
/// If `run` indicated in its return value that the result is inert and the atom
|
|
||||||
/// is in the position of a function, `apply` or `apply_mut` is called depending
|
|
||||||
/// upon whether the atom is referenced elsewhere. `apply` falls back to
|
|
||||||
/// `apply_mut` so implementing it is considered an optimization to avoid
|
|
||||||
/// excessive copying.
|
|
||||||
///
|
|
||||||
/// Atoms don't generally have to be copyable because clauses are refcounted in
|
|
||||||
/// the interpreter, but Orchid code is always free to duplicate the references
|
|
||||||
/// and apply them as functions to multiple different arguments so atoms that
|
|
||||||
/// represent functions have to support application by-ref without consuming the
|
|
||||||
/// function itself.
|
|
||||||
pub trait Atomic: Any + fmt::Debug + Responder + Send
|
|
||||||
where Self: 'static
|
|
||||||
{
|
|
||||||
/// Casts this value to [Any] so that its original value can be salvaged
|
|
||||||
/// during introspection by other external code.
|
|
||||||
///
|
|
||||||
/// This function should be implemented in exactly one way:
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// fn as_any(self: Box<Self>) -> Box<dyn Any> { self }
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
|
||||||
fn as_any(self: Box<Self>) -> Box<dyn Any>;
|
|
||||||
/// See [Atomic::as_any], exactly the same but for references
|
|
||||||
#[must_use]
|
|
||||||
fn as_any_ref(&self) -> &dyn Any;
|
|
||||||
/// Print the atom's type name. Should only ever be implemented as
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// fn type_name(&self) -> &'static str { std::any::type_name::<Self>() }
|
|
||||||
/// ```
|
|
||||||
fn type_name(&self) -> &'static str;
|
|
||||||
|
|
||||||
/// Returns a reference to a possible expression held inside the atom which
|
|
||||||
/// can be reduced. For an overview of the lifecycle see [Atomic]
|
|
||||||
fn redirect(&mut self) -> Option<&mut nort::Expr>;
|
|
||||||
|
|
||||||
/// Attempt to normalize this value. If it wraps a value, this should report
|
|
||||||
/// inert. If it wraps a computation, it should execute one logical step of
|
|
||||||
/// the computation and return a structure representing the next.
|
|
||||||
///
|
|
||||||
/// For an overview of the lifecycle see [Atomic]
|
|
||||||
fn run(self: Box<Self>, run: RunData) -> AtomicResult;
|
|
||||||
|
|
||||||
/// Combine the function with an argument to produce a new clause. Falls back
|
|
||||||
/// to [Atomic::apply_mut] by default.
|
|
||||||
///
|
|
||||||
/// For an overview of the lifecycle see [Atomic]
|
|
||||||
fn apply(mut self: Box<Self>, call: CallData) -> RTResult<nort::Clause> { self.apply_mut(call) }
|
|
||||||
|
|
||||||
/// Combine the function with an argument to produce a new clause
|
|
||||||
///
|
|
||||||
/// For an overview of the lifecycle see [Atomic]
|
|
||||||
fn apply_mut(&mut self, call: CallData) -> RTResult<nort::Clause>;
|
|
||||||
|
|
||||||
/// Must return true for atoms parsed from identical source.
|
|
||||||
/// If the atom cannot be parsed from source, it can safely be ignored
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn parser_eq(&self, other: &dyn Atomic) -> bool { false }
|
|
||||||
|
|
||||||
/// Wrap the atom in a clause to be placed in an [AtomicResult].
|
|
||||||
#[must_use]
|
|
||||||
fn atom_cls(self) -> nort::Clause
|
|
||||||
where Self: Sized {
|
|
||||||
nort::Clause::Atom(Atom(Box::new(self)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shorthand for `self.atom_cls().to_inst()`
|
|
||||||
fn atom_clsi(self) -> nort::ClauseInst
|
|
||||||
where Self: Sized {
|
|
||||||
self.atom_cls().into_inst()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrap the atom in a new expression instance to be placed in a tree
|
|
||||||
#[must_use]
|
|
||||||
fn atom_expr(self, location: CodeLocation) -> nort::Expr
|
|
||||||
where Self: Sized {
|
|
||||||
self.atom_clsi().into_expr(location)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrap the atom in a clause to be placed in a
|
|
||||||
/// [crate::parse::parsed::SourceLine].
|
|
||||||
#[must_use]
|
|
||||||
fn ast_cls(self) -> parsed::Clause
|
|
||||||
where Self: Sized + Clone {
|
|
||||||
parsed::Clause::Atom(AtomGenerator::cloner(self))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrap the atom in an expression to be placed in a
|
|
||||||
/// [crate::parse::parsed::SourceLine].
|
|
||||||
#[must_use]
|
|
||||||
fn ast_exp<N: NameLike>(self, range: SourceRange) -> parsed::Expr
|
|
||||||
where Self: Sized + Clone {
|
|
||||||
self.ast_cls().into_expr(range)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrap this atomic value in a lexeme. This means that the atom will
|
|
||||||
/// participate in macro reproject, so it must implement [Atomic::parser_eq].
|
|
||||||
fn lexeme(self) -> Lexeme
|
|
||||||
where Self: Sized + Clone {
|
|
||||||
Lexeme::Atom(AtomGenerator::cloner(self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A struct for generating any number of [Atom]s. Since atoms aren't Clone,
|
|
||||||
/// this represents the ability to create any number of instances of an atom
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct AtomGenerator(Arc<dyn Fn() -> Atom + Send + Sync>);
|
|
||||||
impl AtomGenerator {
|
|
||||||
/// Use a factory function to create any number of atoms
|
|
||||||
pub fn new(f: impl Fn() -> Atom + Send + Sync + 'static) -> Self { Self(Arc::new(f)) }
|
|
||||||
/// Clone a representative atom when called
|
|
||||||
pub fn cloner(atom: impl Atomic + Clone) -> Self {
|
|
||||||
let lock = Mutex::new(atom);
|
|
||||||
Self::new(move || Atom::new(lock.lock().unwrap().clone()))
|
|
||||||
}
|
|
||||||
/// Generate an atom
|
|
||||||
pub fn run(&self) -> Atom { self.0() }
|
|
||||||
}
|
|
||||||
impl fmt::Debug for AtomGenerator {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.run()) }
|
|
||||||
}
|
|
||||||
impl PartialEq for AtomGenerator {
|
|
||||||
fn eq(&self, other: &Self) -> bool { self.run().0.parser_eq(&*other.run().0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents a black box unit of data with its own normalization steps.
|
|
||||||
/// Typically Rust functions integrated with [super::fn_bridge::xfn] will
|
|
||||||
/// produce and consume [Atom]s to represent both raw data, pending
|
|
||||||
/// computational tasks, and curried partial calls awaiting their next argument.
|
|
||||||
pub struct Atom(pub Box<dyn Atomic>);
|
|
||||||
impl Atom {
|
|
||||||
/// Wrap an [Atomic] in a type-erased box
|
|
||||||
#[must_use]
|
|
||||||
pub fn new<T: 'static + Atomic>(data: T) -> Self { Self(Box::new(data) as Box<dyn Atomic>) }
|
|
||||||
/// Get the contained data
|
|
||||||
#[must_use]
|
|
||||||
pub fn data(&self) -> &dyn Atomic { self.0.as_ref() as &dyn Atomic }
|
|
||||||
/// Test the type of the contained data without downcasting
|
|
||||||
#[must_use]
|
|
||||||
pub fn is<T: Atomic>(&self) -> bool { self.data().as_any_ref().is::<T>() }
|
|
||||||
/// Downcast contained data, panic if it isn't the specified type
|
|
||||||
#[must_use]
|
|
||||||
pub fn downcast<T: Atomic>(self) -> T {
|
|
||||||
*self.0.as_any().downcast().expect("Type mismatch on Atom::cast")
|
|
||||||
}
|
|
||||||
/// Normalize the contained data
|
|
||||||
pub fn run(self, run: RunData<'_, '_>) -> AtomicResult { self.0.run(run) }
|
|
||||||
/// Request a delegate from the encapsulated data
|
|
||||||
pub fn request<T: 'static>(&self) -> Option<T> { request(self.0.as_ref()) }
|
|
||||||
/// Downcast the atom to a concrete atomic type, or return the original atom
|
|
||||||
/// if it is not the specified type
|
|
||||||
pub fn try_downcast<T: Atomic>(self) -> Result<T, Self> {
|
|
||||||
match self.0.as_any_ref().is::<T>() {
|
|
||||||
true => Ok(*self.0.as_any().downcast().expect("checked just above")),
|
|
||||||
false => Err(self),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Downcast an atom by reference
|
|
||||||
pub fn downcast_ref<T: Atomic>(&self) -> Option<&T> { self.0.as_any_ref().downcast_ref() }
|
|
||||||
/// Combine the function with an argument to produce a new clause
|
|
||||||
pub fn apply(self, call: CallData) -> RTResult<nort::Clause> { self.0.apply(call) }
|
|
||||||
/// Combine the function with an argument to produce a new clause
|
|
||||||
pub fn apply_mut(&mut self, call: CallData) -> RTResult<nort::Clause> { self.0.apply_mut(call) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Atom {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.data()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Responder for Never {
|
|
||||||
fn respond(&self, _request: Request) { match *self {} }
|
|
||||||
}
|
|
||||||
impl Atomic for Never {
|
|
||||||
fn as_any(self: Box<Self>) -> Box<dyn Any> { match *self {} }
|
|
||||||
fn as_any_ref(&self) -> &dyn Any { match *self {} }
|
|
||||||
fn type_name(&self) -> &'static str { match *self {} }
|
|
||||||
fn redirect(&mut self) -> Option<&mut nort::Expr> { match *self {} }
|
|
||||||
fn run(self: Box<Self>, _: RunData) -> AtomicResult { match *self {} }
|
|
||||||
fn apply_mut(&mut self, _: CallData) -> RTResult<nort::Clause> { match *self {} }
|
|
||||||
}
|
|
||||||
5
orchid-base/src/on_drop.rs
Normal file
5
orchid-base/src/on_drop.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
pub struct OnDrop<F: FnOnce()>(Option<F>);
|
||||||
|
impl<F: FnOnce()> Drop for OnDrop<F> {
|
||||||
|
fn drop(&mut self) { (self.0.take().unwrap())() }
|
||||||
|
}
|
||||||
|
pub fn on_drop<F: FnOnce()>(f: F) -> OnDrop<F> { OnDrop(Some(f)) }
|
||||||
@@ -6,17 +6,19 @@ use futures::FutureExt;
|
|||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::api;
|
use crate::{
|
||||||
use crate::error::{OrcErrv, OrcRes, mk_errv, report};
|
ExprRepr, ExtraTok, FmtCtx, FmtUnit, Format, IStr, OrcErrv, OrcRes, Paren, SrcRange, Sym,
|
||||||
use crate::format::{FmtCtx, FmtUnit, Format, fmt};
|
TokTree, Token, VName, VPath, api, es, fmt, is, mk_errv, report, ttv_fmt, ttv_range,
|
||||||
use crate::interner::{IStr, es, is};
|
};
|
||||||
use crate::location::SrcRange;
|
|
||||||
use crate::name::{Sym, VName, VPath};
|
|
||||||
use crate::tree::{ExprRepr, ExtraTok, Paren, TokTree, Token, ttv_fmt, ttv_range};
|
|
||||||
|
|
||||||
|
/// A character that can appear at the start of a name; `[a-zA-Z_]`
|
||||||
pub fn name_start(c: char) -> bool { c.is_alphabetic() || c == '_' }
|
pub fn name_start(c: char) -> bool { c.is_alphabetic() || c == '_' }
|
||||||
|
/// A character that can appear inside a name after the start `[a-zA-Z0-9_]`
|
||||||
pub fn name_char(c: char) -> bool { name_start(c) || c.is_numeric() }
|
pub fn name_char(c: char) -> bool { name_start(c) || c.is_numeric() }
|
||||||
pub fn op_char(c: char) -> bool { !name_char(c) && !unrep_space(c) && !"()[]{}\\".contains(c) }
|
/// A character that can appear in an operator. Anything except
|
||||||
|
/// `a-zA-Z0-9_()[]{}\` or whitespace
|
||||||
|
pub fn op_char(c: char) -> bool { !name_char(c) && !c.is_whitespace() && !"()[]{}\\".contains(c) }
|
||||||
|
/// Any whitespace except a line break
|
||||||
pub fn unrep_space(c: char) -> bool { c.is_whitespace() && !"\r\n".contains(c) }
|
pub fn unrep_space(c: char) -> bool { c.is_whitespace() && !"\r\n".contains(c) }
|
||||||
|
|
||||||
/// A cheaply copiable subsection of a document that holds onto context data and
|
/// A cheaply copiable subsection of a document that holds onto context data and
|
||||||
@@ -31,7 +33,10 @@ where
|
|||||||
A: ExprRepr,
|
A: ExprRepr,
|
||||||
X: ExtraTok,
|
X: ExtraTok,
|
||||||
{
|
{
|
||||||
|
/// Create a snippet from a fallback token for position tracking and a range
|
||||||
|
/// of tokens
|
||||||
pub fn new(prev: &'a TokTree<A, X>, cur: &'a [TokTree<A, X>]) -> Self { Self { prev, cur } }
|
pub fn new(prev: &'a TokTree<A, X>, cur: &'a [TokTree<A, X>]) -> Self { Self { prev, cur } }
|
||||||
|
/// Split snippet at index
|
||||||
pub fn split_at(self, pos: u32) -> (Self, Self) {
|
pub fn split_at(self, pos: u32) -> (Self, Self) {
|
||||||
let Self { prev, cur } = self;
|
let Self { prev, cur } = self;
|
||||||
let fst = Self { prev, cur: &cur[..pos as usize] };
|
let fst = Self { prev, cur: &cur[..pos as usize] };
|
||||||
@@ -39,23 +44,35 @@ where
|
|||||||
let snd = Self { prev: new_prev, cur: &self.cur[pos as usize..] };
|
let snd = Self { prev: new_prev, cur: &self.cur[pos as usize..] };
|
||||||
(fst, snd)
|
(fst, snd)
|
||||||
}
|
}
|
||||||
|
/// Find the first index that matches a condition
|
||||||
pub fn find_idx(self, mut f: impl FnMut(&Token<A, X>) -> bool) -> Option<u32> {
|
pub fn find_idx(self, mut f: impl FnMut(&Token<A, X>) -> bool) -> Option<u32> {
|
||||||
self.cur.iter().position(|t| f(&t.tok)).map(|t| t as u32)
|
self.cur.iter().position(|t| f(&t.tok)).map(|t| t as u32)
|
||||||
}
|
}
|
||||||
|
/// Get the n-th token
|
||||||
pub fn get(self, idx: u32) -> Option<&'a TokTree<A, X>> { self.cur.get(idx as usize) }
|
pub fn get(self, idx: u32) -> Option<&'a TokTree<A, X>> { self.cur.get(idx as usize) }
|
||||||
|
/// Count how many tokens long the current sequence is. Parenthesized
|
||||||
|
/// subsequences count as 1
|
||||||
pub fn len(self) -> u32 { self.cur.len() as u32 }
|
pub fn len(self) -> u32 { self.cur.len() as u32 }
|
||||||
|
/// The fallback token that can be used for error reporting if this snippet is
|
||||||
|
/// unexpectedly empty
|
||||||
pub fn prev(self) -> &'a TokTree<A, X> { self.prev }
|
pub fn prev(self) -> &'a TokTree<A, X> { self.prev }
|
||||||
|
/// Create a position that describes all tokens in this snippet
|
||||||
pub fn sr(self) -> SrcRange { ttv_range(self.cur).unwrap_or_else(|| self.prev.sr.clone()) }
|
pub fn sr(self) -> SrcRange { ttv_range(self.cur).unwrap_or_else(|| self.prev.sr.clone()) }
|
||||||
pub fn pop_front(self) -> Option<(&'a TokTree<A, X>, Self)> {
|
/// Split the first token
|
||||||
|
pub fn split_first(self) -> Option<(&'a TokTree<A, X>, Self)> {
|
||||||
self.cur.first().map(|r| (r, self.split_at(1).1))
|
self.cur.first().map(|r| (r, self.split_at(1).1))
|
||||||
}
|
}
|
||||||
pub fn pop_back(self) -> Option<(Self, &'a TokTree<A, X>)> {
|
/// Split the last token
|
||||||
|
pub fn split_last(self) -> Option<(Self, &'a TokTree<A, X>)> {
|
||||||
self.cur.last().map(|r| (self.split_at(self.len() - 1).0, r))
|
self.cur.last().map(|r| (self.split_at(self.len() - 1).0, r))
|
||||||
}
|
}
|
||||||
|
/// Split the snippet at the first token that matches the predicate
|
||||||
pub fn split_once(self, f: impl FnMut(&Token<A, X>) -> bool) -> Option<(Self, Self)> {
|
pub fn split_once(self, f: impl FnMut(&Token<A, X>) -> bool) -> Option<(Self, Self)> {
|
||||||
let idx = self.find_idx(f)?;
|
let idx = self.find_idx(f)?;
|
||||||
Some((self.split_at(idx).0, self.split_at(idx + 1).1))
|
Some((self.split_at(idx).0, self.split_at(idx + 1).1))
|
||||||
}
|
}
|
||||||
|
/// Split the snippet at each occurrence of a delimiter matched by the
|
||||||
|
/// predicate
|
||||||
pub fn split(mut self, mut f: impl FnMut(&Token<A, X>) -> bool) -> impl Iterator<Item = Self> {
|
pub fn split(mut self, mut f: impl FnMut(&Token<A, X>) -> bool) -> impl Iterator<Item = Self> {
|
||||||
iter::from_fn(move || {
|
iter::from_fn(move || {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
@@ -66,7 +83,10 @@ where
|
|||||||
Some(ret)
|
Some(ret)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn is_empty(self) -> bool { self.len() == 0 }
|
/// Returns true if the snippet contains no tokens. Note that thanks to the
|
||||||
|
/// fallback, an empty snippet still has a queriable position
|
||||||
|
pub fn is_empty(self) -> bool { self.cur.is_empty() }
|
||||||
|
/// Skip tokens that are not meant to be significant inside expressions
|
||||||
pub fn skip_fluff(self) -> Self {
|
pub fn skip_fluff(self) -> Self {
|
||||||
let non_fluff_start = self.find_idx(|t| !matches!(t, Token::BR | Token::Comment(_)));
|
let non_fluff_start = self.find_idx(|t| !matches!(t, Token::BR | Token::Comment(_)));
|
||||||
self.split_at(non_fluff_start.unwrap_or(self.len())).1
|
self.split_at(non_fluff_start.unwrap_or(self.len())).1
|
||||||
@@ -86,6 +106,7 @@ impl<A: ExprRepr, X: ExtraTok> Format for Snippet<'_, A, X> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A comment as parsed from code
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Comment {
|
pub struct Comment {
|
||||||
pub text: IStr,
|
pub text: IStr,
|
||||||
@@ -114,6 +135,15 @@ impl fmt::Display for Comment {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "--[{}]--", self.text) }
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "--[{}]--", self.text) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Split a snippet into items by line breaks outside parentheses, unwrap lines
|
||||||
|
/// that are entirely wrapped in a single set of round parens for multiline
|
||||||
|
/// items, and associate all comments with the next non-comment line
|
||||||
|
///
|
||||||
|
/// The parse result's [Parsed::output] is the comments, the line's contents are
|
||||||
|
/// held in [Parsed::tail] so semantically your line parser "continues" for each
|
||||||
|
/// line separately
|
||||||
|
///
|
||||||
|
/// This is the procedure for module parsing
|
||||||
pub async fn line_items<'a, A: ExprRepr, X: ExtraTok>(
|
pub async fn line_items<'a, A: ExprRepr, X: ExtraTok>(
|
||||||
snip: Snippet<'a, A, X>,
|
snip: Snippet<'a, A, X>,
|
||||||
) -> Vec<Parsed<'a, Vec<Comment>, A, X>> {
|
) -> Vec<Parsed<'a, Vec<Comment>, A, X>> {
|
||||||
@@ -141,10 +171,11 @@ pub async fn line_items<'a, A: ExprRepr, X: ExtraTok>(
|
|||||||
items
|
items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pop the next token that isn't a comment or line break
|
||||||
pub async fn try_pop_no_fluff<'a, A: ExprRepr, X: ExtraTok>(
|
pub async fn try_pop_no_fluff<'a, A: ExprRepr, X: ExtraTok>(
|
||||||
snip: Snippet<'a, A, X>,
|
snip: Snippet<'a, A, X>,
|
||||||
) -> ParseRes<'a, &'a TokTree<A, X>, A, X> {
|
) -> ParseRes<'a, &'a TokTree<A, X>, A, X> {
|
||||||
match snip.skip_fluff().pop_front() {
|
match snip.skip_fluff().split_first() {
|
||||||
Some((output, tail)) => Ok(Parsed { output, tail }),
|
Some((output, tail)) => Ok(Parsed { output, tail }),
|
||||||
None =>
|
None =>
|
||||||
Err(mk_errv(is("Unexpected end").await, "Line ends abruptly; more tokens were expected", [
|
Err(mk_errv(is("Unexpected end").await, "Line ends abruptly; more tokens were expected", [
|
||||||
@@ -153,6 +184,7 @@ pub async fn try_pop_no_fluff<'a, A: ExprRepr, X: ExtraTok>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fail if the snippet isn't empty
|
||||||
pub async fn expect_end(snip: Snippet<'_, impl ExprRepr, impl ExtraTok>) -> OrcRes<()> {
|
pub async fn expect_end(snip: Snippet<'_, impl ExprRepr, impl ExtraTok>) -> OrcRes<()> {
|
||||||
match snip.skip_fluff().get(0) {
|
match snip.skip_fluff().get(0) {
|
||||||
Some(surplus) => Err(mk_errv(
|
Some(surplus) => Err(mk_errv(
|
||||||
@@ -164,6 +196,7 @@ pub async fn expect_end(snip: Snippet<'_, impl ExprRepr, impl ExtraTok>) -> OrcR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read a token and ensure that it matches the specified keyword
|
||||||
pub async fn expect_tok<'a, A: ExprRepr, X: ExtraTok>(
|
pub async fn expect_tok<'a, A: ExprRepr, X: ExtraTok>(
|
||||||
snip: Snippet<'a, A, X>,
|
snip: Snippet<'a, A, X>,
|
||||||
tok: IStr,
|
tok: IStr,
|
||||||
@@ -179,6 +212,8 @@ pub async fn expect_tok<'a, A: ExprRepr, X: ExtraTok>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Report an error related to a token that can conveniently use the token's
|
||||||
|
/// text representation in the long message
|
||||||
pub async fn token_errv<A: ExprRepr, X: ExtraTok>(
|
pub async fn token_errv<A: ExprRepr, X: ExtraTok>(
|
||||||
tok: &TokTree<A, X>,
|
tok: &TokTree<A, X>,
|
||||||
description: &'static str,
|
description: &'static str,
|
||||||
@@ -187,17 +222,21 @@ pub async fn token_errv<A: ExprRepr, X: ExtraTok>(
|
|||||||
mk_errv(is(description).await, message(&fmt(tok).await), [tok.sr.pos()])
|
mk_errv(is(description).await, message(&fmt(tok).await), [tok.sr.pos()])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Success output of parsers
|
||||||
pub struct Parsed<'a, T, H: ExprRepr, X: ExtraTok> {
|
pub struct Parsed<'a, T, H: ExprRepr, X: ExtraTok> {
|
||||||
|
/// Information obtained from consumed tokens
|
||||||
pub output: T,
|
pub output: T,
|
||||||
|
/// Input to next parser
|
||||||
pub tail: Snippet<'a, H, X>,
|
pub tail: Snippet<'a, H, X>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ParseRes<'a, T, H, X> = OrcRes<Parsed<'a, T, H, X>>;
|
pub type ParseRes<'a, T, H, X> = OrcRes<Parsed<'a, T, H, X>>;
|
||||||
|
|
||||||
|
/// Parse a `namespaced::name` or a `namespaced::(multi name)`
|
||||||
pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
|
pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
|
||||||
tail: Snippet<'a, A, X>,
|
tail: Snippet<'a, A, X>,
|
||||||
) -> ParseRes<'a, Vec<Import>, A, X> {
|
) -> ParseRes<'a, Vec<Import>, A, X> {
|
||||||
let Some((tt, tail)) = tail.skip_fluff().pop_front() else {
|
let Some((tt, tail)) = tail.skip_fluff().split_first() else {
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
is("Expected token").await,
|
is("Expected token").await,
|
||||||
"Expected a name, a parenthesized list of names, or a globstar.",
|
"Expected a name, a parenthesized list of names, or a globstar.",
|
||||||
@@ -226,7 +265,7 @@ pub async fn parse_multiname<'a, A: ExprRepr, X: ExtraTok>(
|
|||||||
Token::S(Paren::Round, b) => {
|
Token::S(Paren::Round, b) => {
|
||||||
let mut o = Vec::new();
|
let mut o = Vec::new();
|
||||||
let mut body = Snippet::new(tt, b);
|
let mut body = Snippet::new(tt, b);
|
||||||
while let Some((output, tail)) = body.pop_front() {
|
while let Some((output, tail)) = body.split_first() {
|
||||||
match rec(output).boxed_local().await {
|
match rec(output).boxed_local().await {
|
||||||
Ok(names) => o.extend(names),
|
Ok(names) => o.extend(names),
|
||||||
Err(e) => report(e),
|
Err(e) => report(e),
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
//! Methods to operate on Rust vectors in a declarative manner
|
|
||||||
|
|
||||||
use std::iter;
|
|
||||||
|
|
||||||
/// Pure version of [Vec::push]
|
|
||||||
///
|
|
||||||
/// Create a new vector consisting of the provided vector with the
|
|
||||||
/// element appended. See [pushed_ref] to use it with a slice
|
|
||||||
pub fn pushed<I: IntoIterator, C: FromIterator<I::Item>>(vec: I, t: I::Item) -> C {
|
|
||||||
vec.into_iter().chain(iter::once(t)).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pure version of [Vec::push]
|
|
||||||
///
|
|
||||||
/// Create a new vector consisting of the provided slice with the
|
|
||||||
/// element appended. See [pushed] for the owned version
|
|
||||||
pub fn pushed_ref<'a, T: Clone + 'a, C: FromIterator<T>>(
|
|
||||||
vec: impl IntoIterator<Item = &'a T>,
|
|
||||||
t: T,
|
|
||||||
) -> C {
|
|
||||||
vec.into_iter().cloned().chain(iter::once(t)).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Push an element on the adhoc stack, pass it to the callback, then pop the
|
|
||||||
/// element out again.
|
|
||||||
pub fn with_pushed<T, U>(
|
|
||||||
vec: &mut Vec<T>,
|
|
||||||
item: T,
|
|
||||||
cb: impl for<'a> FnOnce(&'a mut Vec<T>) -> U,
|
|
||||||
) -> (T, U) {
|
|
||||||
vec.push(item);
|
|
||||||
let out = cb(vec);
|
|
||||||
let item = vec.pop().expect("top element stolen by callback");
|
|
||||||
(item, out)
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//! An alternative to `Iterable` in many languages, a [Fn] that returns an
|
|
||||||
//! iterator.
|
|
||||||
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use trait_set::trait_set;
|
|
||||||
|
|
||||||
use super::boxed_iter::BoxedIter;
|
|
||||||
|
|
||||||
trait_set! {
|
|
||||||
trait Payload<'a, T> = Fn() -> BoxedIter<'a, T> + 'a;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dynamic iterator building callback. Given how many trait objects this
|
|
||||||
/// involves, it may actually be slower than C#.
|
|
||||||
pub struct Sequence<'a, T: 'a>(Rc<dyn Payload<'a, T>>);
|
|
||||||
impl<'a, T: 'a> Sequence<'a, T> {
|
|
||||||
/// Construct from a concrete function returning a concrete iterator
|
|
||||||
pub fn new<I: IntoIterator<Item = T> + 'a>(f: impl Fn() -> I + 'a) -> Self {
|
|
||||||
Self(Rc::new(move || Box::new(f().into_iter())))
|
|
||||||
}
|
|
||||||
/// Get an iterator from the function
|
|
||||||
pub fn iter(&self) -> BoxedIter<'_, T> { (self.0)() }
|
|
||||||
}
|
|
||||||
impl<'a, T: 'a> Clone for Sequence<'a, T> {
|
|
||||||
fn clone(&self) -> Self { Self(self.0.clone()) }
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::ops::Not;
|
use std::ops::Not;
|
||||||
|
|
||||||
use crate::boxed_iter::BoxedIter;
|
use itertools::Either;
|
||||||
|
|
||||||
/// A primitive for encoding the two sides Left and Right. While booleans
|
/// A primitive for encoding the two sides Left and Right. While booleans
|
||||||
/// are technically usable for this purpose, they're very easy to confuse
|
/// are technically usable for this purpose, they're very easy to confuse
|
||||||
@@ -67,10 +67,13 @@ impl Side {
|
|||||||
}
|
}
|
||||||
/// Walk a double ended iterator (assumed to be left-to-right) in this
|
/// Walk a double ended iterator (assumed to be left-to-right) in this
|
||||||
/// direction
|
/// direction
|
||||||
pub fn walk<'a, I: DoubleEndedIterator + 'a>(&self, iter: I) -> BoxedIter<'a, I::Item> {
|
pub fn walk<'a, I: DoubleEndedIterator + 'a>(
|
||||||
|
&self,
|
||||||
|
iter: I,
|
||||||
|
) -> impl Iterator<Item = I::Item> + 'a {
|
||||||
match self {
|
match self {
|
||||||
Side::Right => Box::new(iter) as BoxedIter<I::Item>,
|
Side::Right => Either::Right(iter),
|
||||||
Side::Left => Box::new(iter.rev()),
|
Side::Left => Either::Left(iter.rev()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,18 @@
|
|||||||
|
/// Cache a value in a [thread_local!]. Supports synchronous and asynchronous
|
||||||
|
/// initializers
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #[macro_use]
|
||||||
|
/// use orchid_base::tl_cache;
|
||||||
|
///
|
||||||
|
/// // simple synchronous case
|
||||||
|
/// let foo = tl_cache!(Rc<Vec<usize>>: vec![0; 1024]);
|
||||||
|
/// async {
|
||||||
|
/// async fn complex_operation(x: usize) -> usize { x + 1 }
|
||||||
|
/// // async case
|
||||||
|
/// let bar = tl_cache!(async usize: complex_operation(0).await)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! tl_cache {
|
macro_rules! tl_cache {
|
||||||
($ty:ty : $expr:expr) => {{
|
($ty:ty : $expr:expr) => {{
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
pub use api::Paren;
|
|
||||||
|
|
||||||
use crate::api;
|
|
||||||
|
|
||||||
pub const PARENS: &[(char, char, Paren)] =
|
|
||||||
&[('(', ')', Paren::Round), ('[', ']', Paren::Square), ('{', '}', Paren::Curly)];
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
use std::borrow::Borrow;
|
|
||||||
use std::fmt::{self, Debug, Display};
|
use std::fmt::{self, Debug, Display};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
@@ -12,62 +11,67 @@ use never::Never;
|
|||||||
use orchid_api_traits::Coding;
|
use orchid_api_traits::Coding;
|
||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
use crate::error::OrcErrv;
|
use crate::{
|
||||||
use crate::format::{FmtCtx, FmtUnit, Format, Variants};
|
FmtCtx, FmtUnit, Format, IStr, OrcErrv, Pos, Snippet, SrcRange, Sym, VName, VPath, Variants, api,
|
||||||
use crate::interner::{IStr, es};
|
es, match_mapping, tl_cache,
|
||||||
use crate::location::{Pos, SrcRange};
|
};
|
||||||
use crate::name::{Sym, VName, VPath};
|
|
||||||
use crate::parse::Snippet;
|
|
||||||
use crate::{api, match_mapping, tl_cache};
|
|
||||||
|
|
||||||
|
/// The 3 types of parentheses Orchid's lexer recognizes as intrinsic groups in
|
||||||
|
/// the S-tree
|
||||||
|
pub type Paren = api::Paren;
|
||||||
|
|
||||||
|
/// Helper table with different kinds of parentheses recognized by the language.
|
||||||
|
/// opening, closing, variant name
|
||||||
|
pub const PARENS: &[(char, char, Paren)] =
|
||||||
|
&[('(', ')', Paren::Round), ('[', ']', Paren::Square), ('{', '}', Paren::Curly)];
|
||||||
|
|
||||||
|
/// Extension interface for embedded expressions and expression construction
|
||||||
|
/// commands inside token trees
|
||||||
pub trait TokenVariant<ApiEquiv: Clone + Debug + Coding>: Format + Clone + fmt::Debug {
|
pub trait TokenVariant<ApiEquiv: Clone + Debug + Coding>: Format + Clone + fmt::Debug {
|
||||||
|
/// Additional arguments to the deserializer. If deserialization of a token
|
||||||
|
/// type is impossible, set this to a sentinel unit type that describes why.
|
||||||
|
/// If you set this to [Never], your token tree type can never be
|
||||||
|
/// deserialized.
|
||||||
type FromApiCtx<'a>;
|
type FromApiCtx<'a>;
|
||||||
|
/// Additional arguments to the serializer. If serialization of a token type
|
||||||
|
/// is forbidden, set this to a sentinel unit type that describes how to avoid
|
||||||
|
/// it.
|
||||||
|
/// If you set this to [Never], your token tree type can never be serialized.
|
||||||
type ToApiCtx<'a>;
|
type ToApiCtx<'a>;
|
||||||
|
/// Deserializer
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn from_api(
|
fn from_api(
|
||||||
api: &ApiEquiv,
|
api: ApiEquiv,
|
||||||
ctx: &mut Self::FromApiCtx<'_>,
|
ctx: &mut Self::FromApiCtx<'_>,
|
||||||
pos: SrcRange,
|
pos: SrcRange,
|
||||||
) -> impl Future<Output = Self>;
|
) -> impl Future<Output = Self>;
|
||||||
|
/// Serializer
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn into_api(self, ctx: &mut Self::ToApiCtx<'_>) -> impl Future<Output = ApiEquiv>;
|
fn into_api(self, ctx: &mut Self::ToApiCtx<'_>) -> impl Future<Output = ApiEquiv>;
|
||||||
}
|
}
|
||||||
impl<T: Clone + Debug + Coding> TokenVariant<T> for Never {
|
impl<T: Clone + Debug + Coding> TokenVariant<T> for Never {
|
||||||
type FromApiCtx<'a> = ();
|
type FromApiCtx<'a> = ();
|
||||||
type ToApiCtx<'a> = ();
|
type ToApiCtx<'a> = ();
|
||||||
async fn from_api(_: &T, _: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
|
async fn from_api(_: T, _: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
|
||||||
panic!("Cannot deserialize Never")
|
panic!("Cannot deserialize Never")
|
||||||
}
|
}
|
||||||
async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> T { match self {} }
|
async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> T { match self {} }
|
||||||
}
|
}
|
||||||
|
|
||||||
trait_set! {
|
trait_set! {
|
||||||
// TokenHandle
|
/// [api::Token::Handle] variant
|
||||||
pub trait ExprRepr = TokenVariant<api::ExprTicket>;
|
pub trait ExprRepr = TokenVariant<api::ExprTicket>;
|
||||||
// TokenExpr
|
/// [api::Token::NewExpr] variant
|
||||||
pub trait ExtraTok = TokenVariant<api::Expression>;
|
pub trait ExtraTok = TokenVariant<api::Expression>;
|
||||||
}
|
}
|
||||||
|
|
||||||
trait_set! {
|
trait_set! {
|
||||||
|
/// Callback to callback to [recur].
|
||||||
pub trait RecurCB<H: ExprRepr, X: ExtraTok> = Fn(TokTree<H, X>) -> TokTree<H, X>;
|
pub trait RecurCB<H: ExprRepr, X: ExtraTok> = Fn(TokTree<H, X>) -> TokTree<H, X>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recur<H: ExprRepr, X: ExtraTok>(
|
/// An atom that can be passed through the API boundary as part of an
|
||||||
tt: TokTree<H, X>,
|
/// expression. In particular, atoms created by extensions use this form.
|
||||||
f: &impl Fn(TokTree<H, X>, &dyn RecurCB<H, X>) -> TokTree<H, X>,
|
|
||||||
) -> TokTree<H, X> {
|
|
||||||
f(tt, &|TokTree { sr: range, tok }| {
|
|
||||||
let tok = match tok {
|
|
||||||
tok @ (Token::BR | Token::Bottom(_) | Token::Comment(_) | Token::Name(_)) => tok,
|
|
||||||
tok @ (Token::Handle(_) | Token::NewExpr(_)) => tok,
|
|
||||||
Token::NS(n, b) => Token::NS(n, Box::new(recur(*b, f))),
|
|
||||||
Token::LambdaHead(arg) => Token::LambdaHead(Box::new(recur(*arg, f))),
|
|
||||||
Token::S(p, b) => Token::S(p, b.into_iter().map(|tt| recur(tt, f)).collect_vec()),
|
|
||||||
};
|
|
||||||
TokTree { sr: range, tok }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AtomRepr: Clone + Format {
|
pub trait AtomRepr: Clone + Format {
|
||||||
type Ctx: ?Sized;
|
type Ctx: ?Sized;
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@@ -93,6 +97,7 @@ impl Display for TokHandle<'_> {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Handle({})", self.0.0) }
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Handle({})", self.0.0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Lexer output
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TokTree<H: ExprRepr, X: ExtraTok> {
|
pub struct TokTree<H: ExprRepr, X: ExtraTok> {
|
||||||
pub tok: Token<H, X>,
|
pub tok: Token<H, X>,
|
||||||
@@ -102,22 +107,37 @@ pub struct TokTree<H: ExprRepr, X: ExtraTok> {
|
|||||||
pub sr: SrcRange,
|
pub sr: SrcRange,
|
||||||
}
|
}
|
||||||
impl<H: ExprRepr, X: ExtraTok> TokTree<H, X> {
|
impl<H: ExprRepr, X: ExtraTok> TokTree<H, X> {
|
||||||
|
/// Visit all tokens, modify them at will, and optionally recurse into them by
|
||||||
|
/// calling the callback passed to your callback
|
||||||
|
pub fn recur(self, f: &impl Fn(Self, &dyn RecurCB<H, X>) -> Self) -> Self {
|
||||||
|
f(self, &|TokTree { sr: range, tok }| {
|
||||||
|
let tok = match tok {
|
||||||
|
tok @ (Token::BR | Token::Bottom(_) | Token::Comment(_) | Token::Name(_)) => tok,
|
||||||
|
tok @ (Token::Handle(_) | Token::NewExpr(_)) => tok,
|
||||||
|
Token::NS(n, b) => Token::NS(n, Box::new(b.recur(f))),
|
||||||
|
Token::LambdaHead(arg) => Token::LambdaHead(Box::new(arg.recur(f))),
|
||||||
|
Token::S(p, b) => Token::S(p, b.into_iter().map(|tt| tt.recur(f)).collect_vec()),
|
||||||
|
};
|
||||||
|
TokTree { sr: range, tok }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn from_api(
|
pub async fn from_api(
|
||||||
tt: &api::TokenTree,
|
tt: api::TokenTree,
|
||||||
hctx: &mut H::FromApiCtx<'_>,
|
hctx: &mut H::FromApiCtx<'_>,
|
||||||
xctx: &mut X::FromApiCtx<'_>,
|
xctx: &mut X::FromApiCtx<'_>,
|
||||||
src: &Sym,
|
src: &Sym,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let pos = SrcRange::new(tt.range.clone(), src);
|
let pos = SrcRange::new(tt.range, src);
|
||||||
let tok = match_mapping!(&tt.token, api::Token => Token::<H, X> {
|
let tok = match_mapping!(tt.token, api::Token => Token::<H, X> {
|
||||||
BR,
|
BR,
|
||||||
NS(n => es(*n).await,
|
NS(n => es(n).await,
|
||||||
b => Box::new(Self::from_api(b, hctx, xctx, src).boxed_local().await)),
|
b => Box::new(Self::from_api(*b, hctx, xctx, src).boxed_local().await)),
|
||||||
Bottom(e => OrcErrv::from_api(e).await),
|
Bottom(e => OrcErrv::from_api(e).await),
|
||||||
LambdaHead(arg => Box::new(Self::from_api(arg, hctx, xctx, src).boxed_local().await)),
|
LambdaHead(arg => Box::new(Self::from_api(*arg, hctx, xctx, src).boxed_local().await)),
|
||||||
Name(n => es(*n).await),
|
Name(n => es(n).await),
|
||||||
S(*par, b => ttv_from_api(b, hctx, xctx, src).await),
|
S(par, b => ttv_from_api(b, hctx, xctx, src).await),
|
||||||
Comment(c => es(*c).await),
|
Comment(c => es(c).await),
|
||||||
NewExpr(expr => X::from_api(expr, xctx, pos.clone()).await),
|
NewExpr(expr => X::from_api(expr, xctx, pos.clone()).await),
|
||||||
Handle(tk => H::from_api(tk, hctx, pos.clone()).await)
|
Handle(tk => H::from_api(tk, hctx, pos.clone()).await)
|
||||||
});
|
});
|
||||||
@@ -186,21 +206,22 @@ impl<H: ExprRepr, X: ExtraTok> Format for TokTree<H, X> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Receive a token sequence from API
|
||||||
pub async fn ttv_from_api<H: ExprRepr, X: ExtraTok>(
|
pub async fn ttv_from_api<H: ExprRepr, X: ExtraTok>(
|
||||||
tokv: impl IntoIterator<Item: Borrow<api::TokenTree>>,
|
tokv: impl IntoIterator<Item = api::TokenTree>,
|
||||||
hctx: &mut H::FromApiCtx<'_>,
|
hctx: &mut H::FromApiCtx<'_>,
|
||||||
xctx: &mut X::FromApiCtx<'_>,
|
xctx: &mut X::FromApiCtx<'_>,
|
||||||
src: &Sym,
|
src: &Sym,
|
||||||
) -> Vec<TokTree<H, X>> {
|
) -> Vec<TokTree<H, X>> {
|
||||||
stream(async |mut cx| {
|
stream(async |mut cx| {
|
||||||
for tok in tokv {
|
for tok in tokv {
|
||||||
cx.emit(TokTree::<H, X>::from_api(tok.borrow(), hctx, xctx, src).boxed_local().await).await
|
cx.emit(TokTree::<H, X>::from_api(tok, hctx, xctx, src).boxed_local().await).await
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
/// Encode a token sequence for sending
|
||||||
pub async fn ttv_into_api<H: ExprRepr, X: ExtraTok>(
|
pub async fn ttv_into_api<H: ExprRepr, X: ExtraTok>(
|
||||||
tokv: impl IntoIterator<Item = TokTree<H, X>>,
|
tokv: impl IntoIterator<Item = TokTree<H, X>>,
|
||||||
hctx: &mut H::ToApiCtx<'_>,
|
hctx: &mut H::ToApiCtx<'_>,
|
||||||
@@ -215,6 +236,7 @@ pub async fn ttv_into_api<H: ExprRepr, X: ExtraTok>(
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enclose the tokens in `()` if there is more than one
|
||||||
pub fn wrap_tokv<H: ExprRepr, X: ExtraTok>(
|
pub fn wrap_tokv<H: ExprRepr, X: ExtraTok>(
|
||||||
items: impl IntoIterator<Item = TokTree<H, X>>,
|
items: impl IntoIterator<Item = TokTree<H, X>>,
|
||||||
) -> TokTree<H, X> {
|
) -> TokTree<H, X> {
|
||||||
@@ -229,8 +251,6 @@ pub fn wrap_tokv<H: ExprRepr, X: ExtraTok>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use api::Paren;
|
|
||||||
|
|
||||||
/// Lexer output variant
|
/// Lexer output variant
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Token<H: ExprRepr, X: ExtraTok> {
|
pub enum Token<H: ExprRepr, X: ExtraTok> {
|
||||||
@@ -272,8 +292,10 @@ impl<H: ExprRepr, X: ExtraTok> Format for Token<H, X> {
|
|||||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
match self {
|
match self {
|
||||||
Self::BR => "\n".to_string().into(),
|
Self::BR => "\n".to_string().into(),
|
||||||
Self::Bottom(err) if err.len() == 1 => format!("Bottom({}) ", err.one().unwrap()).into(),
|
Self::Bottom(err) => match err.one() {
|
||||||
Self::Bottom(err) => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
|
Some(err) => format!("Bottom({err}) ").into(),
|
||||||
|
None => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
|
||||||
|
},
|
||||||
Self::Comment(c) => format!("--[{c}]--").into(),
|
Self::Comment(c) => format!("--[{c}]--").into(),
|
||||||
Self::LambdaHead(arg) =>
|
Self::LambdaHead(arg) =>
|
||||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0b}.")))
|
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0b}.")))
|
||||||
@@ -295,16 +317,20 @@ impl<H: ExprRepr, X: ExtraTok> Format for Token<H, X> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find the location that best describes a sequence of tokens if the sequence
|
||||||
|
/// isn't empty
|
||||||
pub fn ttv_range<'a>(ttv: &[TokTree<impl ExprRepr + 'a, impl ExtraTok + 'a>]) -> Option<SrcRange> {
|
pub fn ttv_range<'a>(ttv: &[TokTree<impl ExprRepr + 'a, impl ExtraTok + 'a>]) -> Option<SrcRange> {
|
||||||
let range = ttv.first()?.sr.range.start..ttv.last().unwrap().sr.range.end;
|
let range = ttv.first()?.sr.range.start..ttv.last().unwrap().sr.range.end;
|
||||||
Some(SrcRange { path: ttv.first().unwrap().sr.path(), range })
|
Some(SrcRange { path: ttv.first().unwrap().sr.path(), range })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pretty-print a token sequence
|
||||||
pub async fn ttv_fmt<'a: 'b, 'b>(
|
pub async fn ttv_fmt<'a: 'b, 'b>(
|
||||||
ttv: impl IntoIterator<Item = &'b TokTree<impl ExprRepr + 'a, impl ExtraTok + 'a>>,
|
ttv: impl IntoIterator<Item = &'b TokTree<impl ExprRepr + 'a, impl ExtraTok + 'a>>,
|
||||||
c: &(impl FmtCtx + ?Sized),
|
c: &(impl FmtCtx + ?Sized),
|
||||||
) -> FmtUnit {
|
) -> FmtUnit {
|
||||||
FmtUnit::sequence("", " ", "", None, join_all(ttv.into_iter().map(|t| t.print(c))).await)
|
FmtUnit::sequence("", " ", "", true, join_all(ttv.into_iter().map(|t| t.print(c))).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Indent a string by two spaces
|
||||||
pub fn indent(s: &str) -> String { s.replace("\n", "\n ") }
|
pub fn indent(s: &str) -> String { s.replace("\n", "\n ") }
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ edition = "2024"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
async-event = "0.2.1"
|
||||||
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
||||||
async-once-cell = "0.5.4"
|
async-once-cell = "0.5.4"
|
||||||
bound = "0.6.0"
|
|
||||||
derive_destructure = "1.0.0"
|
derive_destructure = "1.0.0"
|
||||||
dyn-clone = "1.0.20"
|
dyn-clone = "1.0.20"
|
||||||
futures = { version = "0.3.31", default-features = false, features = [
|
futures = { version = "0.3.31", default-features = false, features = [
|
||||||
@@ -27,6 +27,7 @@ once_cell = "1.21.3"
|
|||||||
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
||||||
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
|
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
|
||||||
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||||
|
orchid-async-utils = { version = "0.1.0", path = "../orchid-async-utils" }
|
||||||
orchid-base = { version = "0.1.0", path = "../orchid-base" }
|
orchid-base = { version = "0.1.0", path = "../orchid-base" }
|
||||||
ordered-float = "5.1.0"
|
ordered-float = "5.1.0"
|
||||||
pastey = "0.2.1"
|
pastey = "0.2.1"
|
||||||
|
|||||||
@@ -4,87 +4,82 @@ use std::fmt::{self, Debug};
|
|||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::num::NonZeroU32;
|
use std::num::{NonZero, NonZeroU32, NonZeroU64};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use dyn_clone::{DynClone, clone_box};
|
use dyn_clone::{DynClone, clone_box};
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
|
use futures::stream::LocalBoxStream;
|
||||||
use futures::{AsyncWrite, FutureExt, StreamExt, stream};
|
use futures::{AsyncWrite, FutureExt, StreamExt, stream};
|
||||||
use orchid_api_derive::Coding;
|
use orchid_api_derive::Coding;
|
||||||
use orchid_api_traits::{Coding, Decode, InHierarchy, Request, UnderRoot, enc_vec};
|
use orchid_api_traits::{Coding, Decode, InHierarchy, Request, UnderRoot, enc_vec};
|
||||||
use orchid_base::error::{OrcErrv, OrcRes, mk_errv, mk_errv_floating};
|
use orchid_base::{
|
||||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, fmt, take_first};
|
FmtCtx, FmtUnit, Format, IStr, OrcErrv, Pos, Receipt, ReqHandle, ReqReader, ReqReaderExt, Sym,
|
||||||
use orchid_base::interner::is;
|
fmt, is, mk_errv, mk_errv_floating, take_first,
|
||||||
use orchid_base::location::Pos;
|
};
|
||||||
use orchid_base::name::Sym;
|
|
||||||
use orchid_base::reqnot::{Receipt, ReqHandle, ReqReader, ReqReaderExt};
|
|
||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
|
use crate::atom_owned::{OwnedAtom, get_obj_store};
|
||||||
use crate::conv::ToExpr;
|
use crate::conv::ToExpr;
|
||||||
use crate::entrypoint::request;
|
use crate::entrypoint::request;
|
||||||
// use crate::error::{ProjectError, ProjectResult};
|
|
||||||
use crate::expr::{Expr, ExprData, ExprHandle, ExprKind};
|
use crate::expr::{Expr, ExprData, ExprHandle, ExprKind};
|
||||||
use crate::gen_expr::GExpr;
|
use crate::gen_expr::{GExpr, IntoGExprStream};
|
||||||
use crate::system::{DynSystemCard, atom_by_idx, atom_info_for, cted, downcast_atom};
|
use crate::system::{DynSystemCardExt, cted, sys_id};
|
||||||
|
|
||||||
|
/// Every atom managed via this system starts with an ID into the type table
|
||||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
|
||||||
pub struct AtomTypeId(pub NonZeroU32);
|
pub struct AtomTypeId(pub NonZeroU32);
|
||||||
|
|
||||||
pub trait AtomCard: 'static + Sized {
|
|
||||||
type Data: Clone + Coding + Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AtomicVariant {}
|
pub trait AtomicVariant {}
|
||||||
|
|
||||||
|
/// A value managed by Orchid. The type should also be registered in the
|
||||||
|
/// [crate::SystemCard] through [AtomicFeatures::ops] which is provided
|
||||||
|
/// indirectly by either [crate::OwnedAtom] or [crate::ThinAtom]
|
||||||
pub trait Atomic: 'static + Sized {
|
pub trait Atomic: 'static + Sized {
|
||||||
|
/// Either [crate::OwnedVariant] or [crate::ThinVariant] depending on whether
|
||||||
|
/// the value implements [crate::OwnedAtom] or [crate::ThinAtom]
|
||||||
type Variant: AtomicVariant;
|
type Variant: AtomicVariant;
|
||||||
|
/// Serializable data that gets sent inside the atom to other systems that
|
||||||
|
/// depend on this system. Methods on this value are directly accessible
|
||||||
|
/// through [TAtom], and this data can also be used for optimized public
|
||||||
|
/// functions. The serialized form should have a reasonable length to avoid
|
||||||
|
/// overburdening the protocol.
|
||||||
type Data: Clone + Coding + Sized + 'static;
|
type Data: Clone + Coding + Sized + 'static;
|
||||||
/// Register handlers for IPC calls. If this atom implements [Supports], you
|
/// Register handlers for IPC calls. If this atom implements [Supports], you
|
||||||
/// should register your implementations here. If this atom doesn't
|
/// should register your implementations here. If this atom doesn't
|
||||||
/// participate in IPC at all, the default implementation is fine
|
/// participate in IPC at all, the default implementation is fine
|
||||||
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new() }
|
fn reg_methods() -> MethodSetBuilder<Self> { MethodSetBuilder::new() }
|
||||||
}
|
|
||||||
impl<A: Atomic> AtomCard for A {
|
|
||||||
type Data = <Self as Atomic>::Data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shared interface of all atom types created in this library for use by the
|
||||||
|
/// library that defines them. This is provided by [Atomic] and either
|
||||||
|
/// [crate::OwnedAtom] or [crate::ThinAtom]
|
||||||
pub trait AtomicFeatures: Atomic {
|
pub trait AtomicFeatures: Atomic {
|
||||||
|
/// Convert a value of this atom inside the defining system into a function
|
||||||
|
/// that will perform registrations and serialization
|
||||||
|
#[allow(private_interfaces)]
|
||||||
fn factory(self) -> AtomFactory;
|
fn factory(self) -> AtomFactory;
|
||||||
type Info: AtomDynfo;
|
/// Expose all operations that can be performed on an instance of this type in
|
||||||
fn info() -> Self::Info;
|
/// an instanceless vtable. This vtable must be registered by the
|
||||||
fn dynfo() -> Box<dyn AtomDynfo>;
|
/// [crate::System].
|
||||||
|
fn ops() -> Box<dyn AtomOps>;
|
||||||
}
|
}
|
||||||
pub trait ToAtom {
|
pub(crate) trait AtomicFeaturesImpl<Variant: AtomicVariant> {
|
||||||
fn to_atom_factory(self) -> AtomFactory;
|
|
||||||
}
|
|
||||||
impl<A: AtomicFeatures> ToAtom for A {
|
|
||||||
fn to_atom_factory(self) -> AtomFactory { self.factory() }
|
|
||||||
}
|
|
||||||
impl ToAtom for AtomFactory {
|
|
||||||
fn to_atom_factory(self) -> AtomFactory { self }
|
|
||||||
}
|
|
||||||
pub trait AtomicFeaturesImpl<Variant: AtomicVariant> {
|
|
||||||
fn _factory(self) -> AtomFactory;
|
fn _factory(self) -> AtomFactory;
|
||||||
type _Info: AtomDynfo;
|
type _Info: AtomOps;
|
||||||
fn _info() -> Self::_Info;
|
fn _info() -> Self::_Info;
|
||||||
}
|
}
|
||||||
impl<A: Atomic + AtomicFeaturesImpl<A::Variant>> AtomicFeatures for A {
|
impl<A: Atomic + AtomicFeaturesImpl<A::Variant>> AtomicFeatures for A {
|
||||||
|
#[allow(private_interfaces)]
|
||||||
fn factory(self) -> AtomFactory { self._factory() }
|
fn factory(self) -> AtomFactory { self._factory() }
|
||||||
type Info = <Self as AtomicFeaturesImpl<A::Variant>>::_Info;
|
fn ops() -> Box<dyn AtomOps> { Box::new(Self::_info()) }
|
||||||
fn info() -> Self::Info { Self::_info() }
|
|
||||||
fn dynfo() -> Box<dyn AtomDynfo> { Box::new(Self::info()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_info<A: AtomCard>(
|
|
||||||
sys: &(impl DynSystemCard + ?Sized),
|
|
||||||
) -> (AtomTypeId, Box<dyn AtomDynfo>) {
|
|
||||||
atom_info_for(sys, TypeId::of::<A>()).unwrap_or_else(|| {
|
|
||||||
panic!("Atom {} not associated with system {}", type_name::<A>(), sys.name())
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A reference to a value of some [Atomic] type. This owns an [Expr]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ForeignAtom {
|
pub struct ForeignAtom {
|
||||||
pub(crate) expr: Rc<ExprHandle>,
|
pub(crate) expr: Rc<ExprHandle>,
|
||||||
@@ -92,7 +87,9 @@ pub struct ForeignAtom {
|
|||||||
pub(crate) pos: Pos,
|
pub(crate) pos: Pos,
|
||||||
}
|
}
|
||||||
impl ForeignAtom {
|
impl ForeignAtom {
|
||||||
|
/// Obtain the position in code of the expression
|
||||||
pub fn pos(&self) -> Pos { self.pos.clone() }
|
pub fn pos(&self) -> Pos { self.pos.clone() }
|
||||||
|
/// Obtain the [Expr]
|
||||||
pub fn ex(self) -> Expr {
|
pub fn ex(self) -> Expr {
|
||||||
let (handle, pos) = (self.expr.clone(), self.pos.clone());
|
let (handle, pos) = (self.expr.clone(), self.pos.clone());
|
||||||
let data = ExprData { pos, kind: ExprKind::Atom(ForeignAtom { ..self }) };
|
let data = ExprData { pos, kind: ExprKind::Atom(ForeignAtom { ..self }) };
|
||||||
@@ -101,10 +98,9 @@ impl ForeignAtom {
|
|||||||
pub(crate) fn new(handle: Rc<ExprHandle>, atom: api::Atom, pos: Pos) -> Self {
|
pub(crate) fn new(handle: Rc<ExprHandle>, atom: api::Atom, pos: Pos) -> Self {
|
||||||
ForeignAtom { atom, expr: handle, pos }
|
ForeignAtom { atom, expr: handle, pos }
|
||||||
}
|
}
|
||||||
pub async fn request<R: Request + UnderRoot<Root: AtomMethod>>(
|
/// Call an IPC method. If the type does not support the given method type,
|
||||||
&self,
|
/// this function returns [None]
|
||||||
r: R,
|
pub async fn call<R: Request + UnderRoot<Root: AtomMethod>>(&self, r: R) -> Option<R::Response> {
|
||||||
) -> Option<R::Response> {
|
|
||||||
let rep = (request(api::Fwd(
|
let rep = (request(api::Fwd(
|
||||||
self.atom.clone(),
|
self.atom.clone(),
|
||||||
Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
|
Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
|
||||||
@@ -113,8 +109,33 @@ impl ForeignAtom {
|
|||||||
.await?;
|
.await?;
|
||||||
Some(R::Response::decode_slice(&mut &rep[..]))
|
Some(R::Response::decode_slice(&mut &rep[..]))
|
||||||
}
|
}
|
||||||
pub async fn downcast<T: AtomicFeatures>(self) -> Result<TAtom<T>, NotTypAtom> {
|
/// Attempt to downcast this value to a concrete atom type
|
||||||
TAtom::downcast(self.ex().handle()).await
|
pub fn downcast<A: Atomic>(self) -> Result<TAtom<A>, NotTypAtom> {
|
||||||
|
let mut data = &self.atom.data.0[..];
|
||||||
|
let value = AtomTypeId::decode_slice(&mut data);
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
let cted = cted();
|
||||||
|
let own_inst = cted.inst();
|
||||||
|
let owner_id = self.atom.owner;
|
||||||
|
let typ = type_name::<A>();
|
||||||
|
let owner = if sys_id() == owner_id {
|
||||||
|
own_inst.card()
|
||||||
|
} else {
|
||||||
|
(cted.deps().find(|s| s.id() == self.atom.owner))
|
||||||
|
.ok_or_else(|| NotTypAtom { expr: self.clone().ex(), pos: self.pos(), typ })?
|
||||||
|
.get_card()
|
||||||
|
};
|
||||||
|
let Some(ops) = owner.ops_by_atid(value) else {
|
||||||
|
panic!("{value:?} does not refer to an atom in {owner_id:?} when downcasting {typ}");
|
||||||
|
};
|
||||||
|
if ops.tid() != TypeId::of::<A>() {
|
||||||
|
panic!(
|
||||||
|
"{value:?} of {owner_id:?} refers to a type other than {typ}. System version mismatch?"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let value = A::Data::decode_slice(&mut data);
|
||||||
|
Ok(TAtom { value, untyped: self })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl fmt::Display for ForeignAtom {
|
impl fmt::Display for ForeignAtom {
|
||||||
@@ -139,13 +160,14 @@ impl ToExpr for ForeignAtom {
|
|||||||
pub struct NotTypAtom {
|
pub struct NotTypAtom {
|
||||||
pub pos: Pos,
|
pub pos: Pos,
|
||||||
pub expr: Expr,
|
pub expr: Expr,
|
||||||
pub typ: Box<dyn AtomDynfo>,
|
pub typ: &'static str,
|
||||||
}
|
}
|
||||||
impl NotTypAtom {
|
impl NotTypAtom {
|
||||||
|
/// Convert to a generic Orchid error
|
||||||
pub async fn mk_err(&self) -> OrcErrv {
|
pub async fn mk_err(&self) -> OrcErrv {
|
||||||
mk_errv(
|
mk_errv(
|
||||||
is("Not the expected type").await,
|
is("Not the expected type").await,
|
||||||
format!("The expression {} is not a {}", fmt(&self.expr).await, self.typ.name()),
|
format!("The expression {} is not a {}", fmt(&self.expr).await, self.typ),
|
||||||
[self.pos.clone()],
|
[self.pos.clone()],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -155,15 +177,21 @@ impl Debug for NotTypAtom {
|
|||||||
f.debug_struct("NotTypAtom")
|
f.debug_struct("NotTypAtom")
|
||||||
.field("pos", &self.pos)
|
.field("pos", &self.pos)
|
||||||
.field("expr", &self.expr)
|
.field("expr", &self.expr)
|
||||||
.field("typ.name", &self.typ.name())
|
.field("typ", &self.typ)
|
||||||
.finish_non_exhaustive()
|
.finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An IPC request associated with an atom. This type should either implement
|
||||||
|
/// [Request] or be the root of a [orchid_api_derive::Hierarchy] the leaves of
|
||||||
|
/// which implement [Request].
|
||||||
pub trait AtomMethod: Coding + InHierarchy {
|
pub trait AtomMethod: Coding + InHierarchy {
|
||||||
const NAME: &str;
|
const NAME: &str;
|
||||||
}
|
}
|
||||||
pub trait Supports<M: AtomMethod>: AtomCard {
|
|
||||||
|
/// A handler for an [AtomMethod] on an [Atomic]. The [AtomMethod] must also be
|
||||||
|
/// registered in [Atomic::reg_methods]
|
||||||
|
pub trait Supports<M: AtomMethod>: Atomic {
|
||||||
fn handle<'a>(
|
fn handle<'a>(
|
||||||
&self,
|
&self,
|
||||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||||
@@ -192,12 +220,17 @@ impl<M: AtomMethod, A: Supports<M>> HandleAtomMethod<A> for AtomMethodHandler<M,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MethodSetBuilder<A: AtomCard> {
|
/// A collection of [Supports] impls for an [Atomic]. If a [Supports]
|
||||||
|
/// impl is not added to the method set, it will not be recognized. Note that
|
||||||
|
/// the [Supports] implementors must be registered, which are not necessarily
|
||||||
|
/// the same as the [Request] implementors
|
||||||
|
pub struct MethodSetBuilder<A: Atomic> {
|
||||||
handlers: Vec<(&'static str, Rc<dyn HandleAtomMethod<A>>)>,
|
handlers: Vec<(&'static str, Rc<dyn HandleAtomMethod<A>>)>,
|
||||||
}
|
}
|
||||||
impl<A: AtomCard> MethodSetBuilder<A> {
|
impl<A: Atomic> MethodSetBuilder<A> {
|
||||||
pub fn new() -> Self { Self { handlers: vec![] } }
|
pub fn new() -> Self { Self { handlers: vec![] } }
|
||||||
|
|
||||||
|
/// Add an [AtomMethod]
|
||||||
pub fn handle<M: AtomMethod>(mut self) -> Self
|
pub fn handle<M: AtomMethod>(mut self) -> Self
|
||||||
where A: Supports<M> {
|
where A: Supports<M> {
|
||||||
assert!(!M::NAME.is_empty(), "AtomMethod::NAME cannoot be empty");
|
assert!(!M::NAME.is_empty(), "AtomMethod::NAME cannoot be empty");
|
||||||
@@ -205,7 +238,7 @@ impl<A: AtomCard> MethodSetBuilder<A> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn pack(&self) -> MethodSet<A> {
|
pub(crate) async fn pack(&self) -> MethodSet<A> {
|
||||||
MethodSet {
|
MethodSet {
|
||||||
handlers: stream::iter(self.handlers.iter())
|
handlers: stream::iter(self.handlers.iter())
|
||||||
.then(async |(k, v)| (Sym::parse(k).await.unwrap(), v.clone()))
|
.then(async |(k, v)| (Sym::parse(k).await.unwrap(), v.clone()))
|
||||||
@@ -215,10 +248,10 @@ impl<A: AtomCard> MethodSetBuilder<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MethodSet<A: AtomCard> {
|
pub(crate) struct MethodSet<A: Atomic> {
|
||||||
handlers: HashMap<Sym, Rc<dyn HandleAtomMethod<A>>>,
|
handlers: HashMap<Sym, Rc<dyn HandleAtomMethod<A>>>,
|
||||||
}
|
}
|
||||||
impl<A: AtomCard> MethodSet<A> {
|
impl<A: Atomic> MethodSet<A> {
|
||||||
pub(crate) async fn dispatch<'a>(
|
pub(crate) async fn dispatch<'a>(
|
||||||
&self,
|
&self,
|
||||||
atom: &'_ A,
|
atom: &'_ A,
|
||||||
@@ -235,29 +268,45 @@ impl<A: AtomCard> MethodSet<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: AtomCard> Default for MethodSetBuilder<A> {
|
impl<A: Atomic> Default for MethodSetBuilder<A> {
|
||||||
fn default() -> Self { Self::new() }
|
fn default() -> Self { Self::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A handle to a value defined by this or another system. This owns an [Expr]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TAtom<A: AtomicFeatures> {
|
pub struct TAtom<A: Atomic> {
|
||||||
pub untyped: ForeignAtom,
|
pub untyped: ForeignAtom,
|
||||||
pub value: A::Data,
|
pub value: A::Data,
|
||||||
}
|
}
|
||||||
impl<A: AtomicFeatures> TAtom<A> {
|
impl<A: Atomic> TAtom<A> {
|
||||||
|
/// Obtain the underlying [Expr]
|
||||||
pub fn ex(&self) -> Expr { self.untyped.clone().ex() }
|
pub fn ex(&self) -> Expr { self.untyped.clone().ex() }
|
||||||
|
/// Obtain the position in code associated with the atom
|
||||||
pub fn pos(&self) -> Pos { self.untyped.pos() }
|
pub fn pos(&self) -> Pos { self.untyped.pos() }
|
||||||
|
/// Produce from an [ExprHandle] directly
|
||||||
pub async fn downcast(expr: Rc<ExprHandle>) -> Result<Self, NotTypAtom> {
|
pub async fn downcast(expr: Rc<ExprHandle>) -> Result<Self, NotTypAtom> {
|
||||||
match Expr::from_handle(expr).atom().await {
|
match Expr::from_handle(expr).atom().await {
|
||||||
Err(expr) =>
|
Err(expr) =>
|
||||||
Err(NotTypAtom { pos: expr.data().await.pos.clone(), expr, typ: Box::new(A::info()) }),
|
Err(NotTypAtom { pos: expr.data().await.pos.clone(), expr, typ: type_name::<A>() }),
|
||||||
Ok(atm) => match downcast_atom::<A>(atm).await {
|
Ok(atm) => atm.downcast(),
|
||||||
Ok(tatom) => Ok(tatom),
|
|
||||||
Err(fa) => Err(NotTypAtom { pos: fa.pos.clone(), expr: fa.ex(), typ: Box::new(A::info()) }),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn request<R: Request + UnderRoot<Root: AtomMethod>>(&self, req: R) -> R::Response
|
/// Find the instance associated with a [TAtom] that we own
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// if we don't actually own this atom
|
||||||
|
pub async fn own(&self) -> A
|
||||||
|
where A: OwnedAtom {
|
||||||
|
let g = get_obj_store().objects.read().await;
|
||||||
|
let atom_id = self.untyped.atom.drop.expect("Owned atoms always have a drop ID");
|
||||||
|
let dyn_atom =
|
||||||
|
g.get(&atom_id).expect("Atom ID invalid; atom type probably not owned by this crate");
|
||||||
|
dyn_atom.as_any_ref().downcast_ref().cloned().expect("The ID should imply a type as well")
|
||||||
|
}
|
||||||
|
/// Call an IPC method on the value. Since we know the type, unlike
|
||||||
|
/// [ForeignAtom::call], we can ensure that the callee recognizes this method
|
||||||
|
pub async fn call<R: Request + UnderRoot<Root: AtomMethod>>(&self, req: R) -> R::Response
|
||||||
where A: Supports<<R as UnderRoot>::Root> {
|
where A: Supports<<R as UnderRoot>::Root> {
|
||||||
R::Response::decode_slice(
|
R::Response::decode_slice(
|
||||||
&mut &(request(api::Fwd(
|
&mut &(request(api::Fwd(
|
||||||
@@ -283,9 +332,82 @@ impl<A: AtomicFeatures> Format for TAtom<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AtomCtx<'a>(pub &'a [u8], pub Option<api::AtomId>);
|
pub(crate) struct AtomCtx<'a>(pub &'a [u8], pub Option<api::AtomId>);
|
||||||
|
|
||||||
pub trait AtomDynfo: 'static {
|
pub enum Next {
|
||||||
|
ExitSuccess,
|
||||||
|
Continue(Continuation),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Continuation {
|
||||||
|
immediate: Vec<LocalBoxStream<'static, GExpr>>,
|
||||||
|
delayed: Vec<(NonZeroU64, LocalBoxStream<'static, GExpr>)>,
|
||||||
|
}
|
||||||
|
impl Continuation {
|
||||||
|
pub fn immediate<T: IntoGExprStream + 'static>(mut self, expr: T) -> Self {
|
||||||
|
self.immediate.push(Box::pin(expr.into_gexpr_stream()));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn schedule<T: IntoGExprStream + 'static>(mut self, delay: Duration, expr: T) -> Self {
|
||||||
|
let delay = delay.as_millis().try_into().unwrap();
|
||||||
|
let Some(nzdelay) = NonZero::new(delay) else { return self.immediate(expr) };
|
||||||
|
self.delayed.push((nzdelay, Box::pin(expr.into_gexpr_stream())));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Continuation> for Next {
|
||||||
|
fn from(value: Continuation) -> Self { Self::Continue(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Continuation> for CmdResult {
|
||||||
|
fn from(value: Continuation) -> Self { Ok(Next::Continue(value)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum CmdError {
|
||||||
|
Orc(OrcErrv),
|
||||||
|
FatalError,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct FatalError;
|
||||||
|
impl From<FatalError> for CmdError {
|
||||||
|
fn from(FatalError: FatalError) -> Self { Self::FatalError }
|
||||||
|
}
|
||||||
|
impl From<OrcErrv> for CmdError {
|
||||||
|
fn from(value: OrcErrv) -> Self { Self::Orc(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn encode_command_result(
|
||||||
|
result: Result<Next, CmdError>,
|
||||||
|
) -> api::OrcResult<api::NextStep> {
|
||||||
|
match result {
|
||||||
|
Ok(Next::ExitSuccess) => Ok(api::NextStep::Exit { success: true }),
|
||||||
|
Err(CmdError::FatalError) => Ok(api::NextStep::Exit { success: false }),
|
||||||
|
Err(CmdError::Orc(errv)) => Err(errv.to_api()),
|
||||||
|
Ok(Next::Continue(Continuation { immediate, delayed })) => Ok(api::NextStep::Continue {
|
||||||
|
immediate: (stream::iter(immediate).flatten())
|
||||||
|
.then(async |x| x.serialize().await)
|
||||||
|
.collect()
|
||||||
|
.await,
|
||||||
|
delayed: stream::iter(delayed.into_iter().map(|(delay, expr)| {
|
||||||
|
expr.then(move |expr| async move { (delay, expr.serialize().await) })
|
||||||
|
}))
|
||||||
|
.flatten()
|
||||||
|
.collect()
|
||||||
|
.await,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type CmdResult = Result<Next, CmdError>;
|
||||||
|
|
||||||
|
/// A vtable-like type that collects operations defined by an [Atomic] without
|
||||||
|
/// associating with an instance of that type. This must be registered in
|
||||||
|
/// [crate::SystemCard]
|
||||||
|
#[allow(private_interfaces)]
|
||||||
|
pub trait AtomOps: 'static {
|
||||||
fn tid(&self) -> TypeId;
|
fn tid(&self) -> TypeId;
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
fn decode<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>>;
|
fn decode<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>>;
|
||||||
@@ -298,25 +420,29 @@ pub trait AtomDynfo: 'static {
|
|||||||
key: Sym,
|
key: Sym,
|
||||||
req: Box<dyn ReqReader<'a> + 'a>,
|
req: Box<dyn ReqReader<'a> + 'a>,
|
||||||
) -> LocalBoxFuture<'a, bool>;
|
) -> LocalBoxFuture<'a, bool>;
|
||||||
fn command<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, OrcRes<Option<GExpr>>>;
|
fn command<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, CmdResult>;
|
||||||
fn serialize<'a, 'b: 'a>(
|
fn serialize<'a, 'b: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
ctx: AtomCtx<'a>,
|
ctx: AtomCtx<'a>,
|
||||||
write: Pin<&'b mut dyn AsyncWrite>,
|
write: Pin<&'b mut dyn AsyncWrite>,
|
||||||
) -> LocalBoxFuture<'a, Option<Vec<Expr>>>;
|
) -> LocalBoxFuture<'a, Option<Vec<Expr>>>;
|
||||||
fn deserialize<'a>(&'a self, data: &'a [u8], refs: &'a [Expr]) -> LocalBoxFuture<'a, api::Atom>;
|
fn deserialize<'a>(
|
||||||
|
&'a self,
|
||||||
|
data: &'a [u8],
|
||||||
|
refs: &'a [Expr],
|
||||||
|
) -> LocalBoxFuture<'a, api::LocalAtom>;
|
||||||
fn drop<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, ()>;
|
fn drop<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, ()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
trait_set! {
|
trait_set! {
|
||||||
pub trait AtomFactoryFn = FnOnce() -> LocalBoxFuture<'static, api::Atom> + DynClone;
|
pub trait AtomFactoryFn = FnOnce() -> LocalBoxFuture<'static, api::LocalAtom> + DynClone;
|
||||||
}
|
}
|
||||||
pub struct AtomFactory(Box<dyn AtomFactoryFn>);
|
pub(crate) struct AtomFactory(Box<dyn AtomFactoryFn>);
|
||||||
impl AtomFactory {
|
impl AtomFactory {
|
||||||
pub fn new(f: impl AsyncFnOnce() -> api::Atom + Clone + 'static) -> Self {
|
pub fn new(f: impl AsyncFnOnce() -> api::LocalAtom + Clone + 'static) -> Self {
|
||||||
Self(Box::new(|| f().boxed_local()))
|
Self(Box::new(|| f().boxed_local()))
|
||||||
}
|
}
|
||||||
pub async fn build(self) -> api::Atom { (self.0)().await }
|
pub async fn build(self) -> api::LocalAtom { (self.0)().await }
|
||||||
}
|
}
|
||||||
impl Clone for AtomFactory {
|
impl Clone for AtomFactory {
|
||||||
fn clone(&self) -> Self { AtomFactory(clone_box(&*self.0)) }
|
fn clone(&self) -> Self { AtomFactory(clone_box(&*self.0)) }
|
||||||
@@ -333,6 +459,7 @@ impl Format for AtomFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error produced when an atom can not be applied to a value as a function
|
||||||
pub async fn err_not_callable(unit: &FmtUnit) -> OrcErrv {
|
pub async fn err_not_callable(unit: &FmtUnit) -> OrcErrv {
|
||||||
mk_errv_floating(
|
mk_errv_floating(
|
||||||
is("This atom is not callable").await,
|
is("This atom is not callable").await,
|
||||||
@@ -340,6 +467,7 @@ pub async fn err_not_callable(unit: &FmtUnit) -> OrcErrv {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error produced when an atom can not be the final value of the program
|
||||||
pub async fn err_not_command(unit: &FmtUnit) -> OrcErrv {
|
pub async fn err_not_command(unit: &FmtUnit) -> OrcErrv {
|
||||||
mk_errv_floating(
|
mk_errv_floating(
|
||||||
is("This atom is not a command").await,
|
is("This atom is not a command").await,
|
||||||
@@ -347,11 +475,35 @@ pub async fn err_not_command(unit: &FmtUnit) -> OrcErrv {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn err_exit_success_msg() -> IStr { is("Early successful exit").await }
|
||||||
|
pub(crate) async fn err_exit_failure_msg() -> IStr { is("Early failure exit").await }
|
||||||
|
|
||||||
|
/// Sentinel error returnable from [crate::OwnedAtom::command] or
|
||||||
|
/// [crate::ThinAtom::command] to indicate that the program should exit with a
|
||||||
|
/// success
|
||||||
|
pub async fn err_exit_success() -> OrcErrv {
|
||||||
|
mk_errv_floating(
|
||||||
|
err_exit_success_msg().await,
|
||||||
|
"Sentinel error indicating that the program should exit with a success.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sentinel error returnable from [crate::OwnedAtom::command] or
|
||||||
|
/// [crate::ThinAtom::command] to indicate that the program should exit with a
|
||||||
|
/// failure
|
||||||
|
pub async fn err_exit_failure() -> OrcErrv {
|
||||||
|
mk_errv_floating(
|
||||||
|
err_exit_failure_msg().await,
|
||||||
|
"Sentinel error indicating that the program should exit with a failure \
|
||||||
|
but without raising an error.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Read the type ID prefix from an atom, return type information and the rest
|
/// Read the type ID prefix from an atom, return type information and the rest
|
||||||
/// of the data
|
/// of the data
|
||||||
pub(crate) fn resolve_atom_type(atom: &api::Atom) -> (Box<dyn AtomDynfo>, AtomTypeId, &[u8]) {
|
pub(crate) fn resolve_atom_type(atom: &api::Atom) -> (Box<dyn AtomOps>, AtomTypeId, &[u8]) {
|
||||||
let mut data = &atom.data.0[..];
|
let mut data = &atom.data.0[..];
|
||||||
let tid = AtomTypeId::decode_slice(&mut data);
|
let atid = AtomTypeId::decode_slice(&mut data);
|
||||||
let atom_record = atom_by_idx(cted().inst().card(), tid).expect("Unrecognized atom type ID");
|
let atom_record = cted().inst().card().ops_by_atid(atid).expect("Unrecognized atom type ID");
|
||||||
(atom_record, tid, data)
|
(atom_record, atid, data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,21 +17,20 @@ use itertools::Itertools;
|
|||||||
use memo_map::MemoMap;
|
use memo_map::MemoMap;
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_api_traits::{Decode, Encode, enc_vec};
|
use orchid_api_traits::{Decode, Encode, enc_vec};
|
||||||
use orchid_base::error::OrcRes;
|
use orchid_base::{FmtCtx, FmtCtxImpl, FmtUnit, Format, Sym, log, take_first};
|
||||||
use orchid_base::format::{FmtCtx, FmtCtxImpl, FmtUnit, Format, take_first};
|
|
||||||
use orchid_base::logging::log;
|
|
||||||
use orchid_base::name::Sym;
|
|
||||||
use task_local::task_local;
|
use task_local::task_local;
|
||||||
|
|
||||||
use crate::api;
|
|
||||||
use crate::atom::{
|
use crate::atom::{
|
||||||
AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
|
AtomCtx, AtomFactory, AtomOps, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
|
||||||
MethodSetBuilder, TAtom, err_not_callable, err_not_command, get_info,
|
MethodSetBuilder, err_not_callable, err_not_command,
|
||||||
};
|
};
|
||||||
|
use crate::conv::ToExpr;
|
||||||
use crate::expr::Expr;
|
use crate::expr::Expr;
|
||||||
use crate::gen_expr::{GExpr, bot};
|
use crate::gen_expr::{GExpr, bot};
|
||||||
use crate::system::{cted, sys_id};
|
use crate::system::{DynSystemCardExt, cted};
|
||||||
|
use crate::{CmdError, CmdResult, api};
|
||||||
|
|
||||||
|
/// Value of [Atomic::Variant] for a type that implements [OwnedAtom]
|
||||||
pub struct OwnedVariant;
|
pub struct OwnedVariant;
|
||||||
impl AtomicVariant for OwnedVariant {}
|
impl AtomicVariant for OwnedVariant {}
|
||||||
impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVariant> for A {
|
impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVariant> for A {
|
||||||
@@ -43,15 +42,15 @@ impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVari
|
|||||||
*id += 1;
|
*id += 1;
|
||||||
api::AtomId(NonZero::new(*id + 1).unwrap())
|
api::AtomId(NonZero::new(*id + 1).unwrap())
|
||||||
};
|
};
|
||||||
let (typ_id, _) = get_info::<A>(cted().inst().card());
|
let (typ_id, _) = cted().inst().card().ops::<A>();
|
||||||
let mut data = enc_vec(&typ_id);
|
let mut data = enc_vec(&typ_id);
|
||||||
self.encode(Pin::<&mut Vec<u8>>::new(&mut data)).await;
|
self.encode(Pin::<&mut Vec<u8>>::new(&mut data)).await;
|
||||||
obj_store.objects.read().await.insert(atom_id, Box::new(self));
|
obj_store.objects.read().await.insert(atom_id, Box::new(self));
|
||||||
api::Atom { drop: Some(atom_id), data: api::AtomData(data), owner: sys_id() }
|
api::LocalAtom { drop: Some(atom_id), data: api::AtomData(data) }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn _info() -> Self::_Info { OwnedAtomDynfo { msbuild: A::reg_reqs(), ms: OnceCell::new() } }
|
fn _info() -> Self::_Info { OwnedAtomOps { msbuild: A::reg_methods(), ms: OnceCell::new() } }
|
||||||
type _Info = OwnedAtomDynfo<A>;
|
type _Info = OwnedAtomOps<A>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// While an atom read guard is held, no atom can be removed.
|
/// While an atom read guard is held, no atom can be removed.
|
||||||
@@ -80,17 +79,15 @@ pub(crate) async fn take_atom(id: api::AtomId) -> Box<dyn DynOwnedAtom> {
|
|||||||
g.remove(&id).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0))
|
g.remove(&id).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OwnedAtomDynfo<T: OwnedAtom> {
|
pub(crate) struct OwnedAtomOps<T: OwnedAtom> {
|
||||||
msbuild: MethodSetBuilder<T>,
|
msbuild: MethodSetBuilder<T>,
|
||||||
ms: OnceCell<MethodSet<T>>,
|
ms: OnceCell<MethodSet<T>>,
|
||||||
}
|
}
|
||||||
impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
|
impl<A: OwnedAtom> AtomOps for OwnedAtomOps<A> {
|
||||||
fn tid(&self) -> TypeId { TypeId::of::<T>() }
|
fn tid(&self) -> TypeId { TypeId::of::<A>() }
|
||||||
fn name(&self) -> &'static str { type_name::<T>() }
|
fn name(&self) -> &'static str { type_name::<A>() }
|
||||||
fn decode<'a>(&'a self, AtomCtx(data, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>> {
|
fn decode<'a>(&'a self, AtomCtx(data, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>> {
|
||||||
Box::pin(async {
|
Box::pin(async { Box::new(<A as Atomic>::Data::decode_slice(&mut &data[..])) as Box<dyn Any> })
|
||||||
Box::new(<T as AtomCard>::Data::decode_slice(&mut &data[..])) as Box<dyn Any>
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
fn call(&self, AtomCtx(_, id): AtomCtx, arg: Expr) -> LocalBoxFuture<'_, GExpr> {
|
fn call(&self, AtomCtx(_, id): AtomCtx, arg: Expr) -> LocalBoxFuture<'_, GExpr> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
@@ -123,7 +120,7 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
|
|||||||
&'a self,
|
&'a self,
|
||||||
AtomCtx(_, id): AtomCtx<'a>,
|
AtomCtx(_, id): AtomCtx<'a>,
|
||||||
key: Sym,
|
key: Sym,
|
||||||
req: Box<dyn orchid_base::reqnot::ReqReader<'a> + 'a>,
|
req: Box<dyn orchid_base::ReqReader<'a> + 'a>,
|
||||||
) -> LocalBoxFuture<'a, bool> {
|
) -> LocalBoxFuture<'a, bool> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let a = AtomReadGuard::new(id.unwrap()).await;
|
let a = AtomReadGuard::new(id.unwrap()).await;
|
||||||
@@ -131,10 +128,7 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
|
|||||||
ms.dispatch(a.as_any_ref().downcast_ref().unwrap(), key, req).await
|
ms.dispatch(a.as_any_ref().downcast_ref().unwrap(), key, req).await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn command<'a>(
|
fn command<'a>(&'a self, AtomCtx(_, id): AtomCtx<'a>) -> LocalBoxFuture<'a, CmdResult> {
|
||||||
&'a self,
|
|
||||||
AtomCtx(_, id): AtomCtx<'a>,
|
|
||||||
) -> LocalBoxFuture<'a, OrcRes<Option<GExpr>>> {
|
|
||||||
Box::pin(async move { take_atom(id.unwrap()).await.dyn_command().await })
|
Box::pin(async move { take_atom(id.unwrap()).await.dyn_command().await })
|
||||||
}
|
}
|
||||||
fn drop(&self, AtomCtx(_, id): AtomCtx) -> LocalBoxFuture<'_, ()> {
|
fn drop(&self, AtomCtx(_, id): AtomCtx) -> LocalBoxFuture<'_, ()> {
|
||||||
@@ -151,19 +145,34 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
|
|||||||
AtomReadGuard::new(id).await.dyn_serialize(write).await
|
AtomReadGuard::new(id).await.dyn_serialize(write).await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn deserialize<'a>(&'a self, data: &'a [u8], refs: &'a [Expr]) -> LocalBoxFuture<'a, api::Atom> {
|
fn deserialize<'a>(
|
||||||
|
&'a self,
|
||||||
|
data: &'a [u8],
|
||||||
|
refs: &'a [Expr],
|
||||||
|
) -> LocalBoxFuture<'a, api::LocalAtom> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let refs = T::Refs::from_iter(refs.iter().cloned());
|
let refs = A::Refs::from_iter(refs.iter().cloned());
|
||||||
let obj = T::deserialize(DeserCtxImpl(data), refs).await;
|
let obj = A::deserialize(DeserCtxImpl(data), refs).await;
|
||||||
obj._factory().build().await
|
obj._factory().build().await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read from the buffer populated by a previous call to [OwnedAtom::serialize]
|
||||||
pub trait DeserializeCtx: Sized {
|
pub trait DeserializeCtx: Sized {
|
||||||
|
/// Read a value from the head of the buffer
|
||||||
fn read<T: Decode>(&mut self) -> impl Future<Output = T>;
|
fn read<T: Decode>(&mut self) -> impl Future<Output = T>;
|
||||||
|
/// Check if the buffer is empty
|
||||||
fn is_empty(&self) -> bool;
|
fn is_empty(&self) -> bool;
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// if the buffer isn't empty
|
||||||
fn assert_empty(&self) { assert!(self.is_empty(), "Bytes found after decoding") }
|
fn assert_empty(&self) { assert!(self.is_empty(), "Bytes found after decoding") }
|
||||||
|
/// Decode the only value in the buffer
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// if the buffer has more data after the value was read
|
||||||
fn decode<T: Decode>(&mut self) -> impl Future<Output = T> {
|
fn decode<T: Decode>(&mut self) -> impl Future<Output = T> {
|
||||||
async {
|
async {
|
||||||
let t = self.read().await;
|
let t = self.read().await;
|
||||||
@@ -179,6 +188,8 @@ impl DeserializeCtx for DeserCtxImpl<'_> {
|
|||||||
fn is_empty(&self) -> bool { self.0.is_empty() }
|
fn is_empty(&self) -> bool { self.0.is_empty() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Various collections of expr's that distinguish how many references the type
|
||||||
|
/// holds. See [OwnedAtom::Refs] for the list of permitted values
|
||||||
pub trait RefSet {
|
pub trait RefSet {
|
||||||
fn from_iter<I: Iterator<Item = Expr> + ExactSizeIterator>(refs: I) -> Self;
|
fn from_iter<I: Iterator<Item = Expr> + ExactSizeIterator>(refs: I) -> Self;
|
||||||
fn to_vec(self) -> Vec<Expr>;
|
fn to_vec(self) -> Vec<Expr>;
|
||||||
@@ -211,10 +222,12 @@ impl<const N: usize> RefSet for [Expr; N] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Atoms that have a [Drop]
|
/// Atoms that have a [Drop]. Internal mutability is allowed for optimization
|
||||||
|
/// purposes, but new references to [Expr] should not be added to avoid
|
||||||
|
/// reference loops
|
||||||
pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
|
pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
|
||||||
/// If serializable, the collection that best stores subexpression references
|
/// If serializable, the collection that best stores subexpression references
|
||||||
/// for this atom.
|
/// for this type.
|
||||||
///
|
///
|
||||||
/// - `()` for no subexppressions,
|
/// - `()` for no subexppressions,
|
||||||
/// - `[Expr; N]` for a static number of subexpressions
|
/// - `[Expr; N]` for a static number of subexpressions
|
||||||
@@ -222,30 +235,38 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
|
|||||||
/// - `Never` if not serializable
|
/// - `Never` if not serializable
|
||||||
///
|
///
|
||||||
/// If this isn't `Never`, you must override the default, panicking
|
/// If this isn't `Never`, you must override the default, panicking
|
||||||
/// `serialize` and `deserialize` implementation
|
/// [Self::serialize] and [Self::deserialize] implementation
|
||||||
type Refs: RefSet;
|
type Refs: RefSet;
|
||||||
|
/// Obtain serializable representation
|
||||||
fn val(&self) -> impl Future<Output = Cow<'_, Self::Data>>;
|
fn val(&self) -> impl Future<Output = Cow<'_, Self::Data>>;
|
||||||
|
/// Apply as a function while a different reference to the value exists.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn call_ref(&self, arg: Expr) -> impl Future<Output = GExpr> {
|
fn call_ref(&self, arg: Expr) -> impl Future<Output: ToExpr> {
|
||||||
async move { bot(err_not_callable(&self.dyn_print().await).await) }
|
async move { bot(err_not_callable(&self.dyn_print().await).await) }
|
||||||
}
|
}
|
||||||
fn call(self, arg: Expr) -> impl Future<Output = GExpr> {
|
/// Apply as a function and consume
|
||||||
|
fn call(self, arg: Expr) -> impl Future<Output: ToExpr> {
|
||||||
async {
|
async {
|
||||||
let gcl = self.call_ref(arg).await;
|
let gcl = self.call_ref(arg).await.to_gen().await;
|
||||||
self.free().await;
|
self.free().await;
|
||||||
gcl
|
gcl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[allow(unused_variables)]
|
/// Called when this is the final value of the program
|
||||||
fn command(self) -> impl Future<Output = OrcRes<Option<GExpr>>> {
|
fn command(self) -> impl Future<Output = CmdResult> {
|
||||||
async move { Err(err_not_command(&self.dyn_print().await).await) }
|
async move { Err(CmdError::Orc(err_not_command(&self.dyn_print().await).await)) }
|
||||||
}
|
}
|
||||||
#[allow(unused_variables)]
|
/// Drop and perform any cleanup. Unlike Rust's [Drop::drop], this is
|
||||||
|
/// guaranteed to be called
|
||||||
fn free(self) -> impl Future<Output = ()> { async {} }
|
fn free(self) -> impl Future<Output = ()> { async {} }
|
||||||
|
/// Debug-print. This is the final fallback for Orchid's
|
||||||
|
/// `std::string::to_str`.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn print_atom<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> impl Future<Output = FmtUnit> {
|
fn print_atom<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> impl Future<Output = FmtUnit> {
|
||||||
async { format!("OwnedAtom({})", type_name::<Self>()).into() }
|
async { format!("OwnedAtom({})", type_name::<Self>()).into() }
|
||||||
}
|
}
|
||||||
|
/// Serialize this object. If the object is serializable you must override
|
||||||
|
/// this function, otherwise set [Self::Refs] to [Never].
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn serialize(
|
fn serialize(
|
||||||
&self,
|
&self,
|
||||||
@@ -254,6 +275,8 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
|
|||||||
assert_serializable::<Self>();
|
assert_serializable::<Self>();
|
||||||
async { panic!("Either implement serialize or set Refs to Never for {}", type_name::<Self>()) }
|
async { panic!("Either implement serialize or set Refs to Never for {}", type_name::<Self>()) }
|
||||||
}
|
}
|
||||||
|
/// Deserialize this object. If the object is serializable you must override
|
||||||
|
/// this function, otherwise set [Self::Refs] to [Never]
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn deserialize(dctx: impl DeserializeCtx, refs: Self::Refs) -> impl Future<Output = Self> {
|
fn deserialize(dctx: impl DeserializeCtx, refs: Self::Refs) -> impl Future<Output = Self> {
|
||||||
assert_serializable::<Self>();
|
assert_serializable::<Self>();
|
||||||
@@ -263,18 +286,18 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Debug-assert that the object opted in to serialization
|
||||||
fn assert_serializable<T: OwnedAtom>() {
|
fn assert_serializable<T: OwnedAtom>() {
|
||||||
static MSG: &str = "The extension scaffold is broken, Never Refs should prevent serialization";
|
static MSG: &str = "The extension scaffold is broken, Never Refs should prevent serialization";
|
||||||
assert_ne!(TypeId::of::<T::Refs>(), TypeId::of::<Never>(), "{MSG}");
|
debug_assert_ne!(TypeId::of::<T::Refs>(), TypeId::of::<Never>(), "{MSG}");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DynOwnedAtom: DynClone + 'static {
|
pub(crate) trait DynOwnedAtom: DynClone + 'static {
|
||||||
fn atom_tid(&self) -> TypeId;
|
|
||||||
fn as_any_ref(&self) -> &dyn Any;
|
fn as_any_ref(&self) -> &dyn Any;
|
||||||
fn encode<'a>(&'a self, buffer: Pin<&'a mut dyn AsyncWrite>) -> LocalBoxFuture<'a, ()>;
|
fn encode<'a>(&'a self, buffer: Pin<&'a mut dyn AsyncWrite>) -> LocalBoxFuture<'a, ()>;
|
||||||
fn dyn_call_ref(&self, arg: Expr) -> LocalBoxFuture<'_, GExpr>;
|
fn dyn_call_ref(&self, arg: Expr) -> LocalBoxFuture<'_, GExpr>;
|
||||||
fn dyn_call(self: Box<Self>, arg: Expr) -> LocalBoxFuture<'static, GExpr>;
|
fn dyn_call(self: Box<Self>, arg: Expr) -> LocalBoxFuture<'static, GExpr>;
|
||||||
fn dyn_command(self: Box<Self>) -> LocalBoxFuture<'static, OrcRes<Option<GExpr>>>;
|
fn dyn_command(self: Box<Self>) -> LocalBoxFuture<'static, CmdResult>;
|
||||||
fn dyn_free(self: Box<Self>) -> LocalBoxFuture<'static, ()>;
|
fn dyn_free(self: Box<Self>) -> LocalBoxFuture<'static, ()>;
|
||||||
fn dyn_print(&self) -> LocalBoxFuture<'_, FmtUnit>;
|
fn dyn_print(&self) -> LocalBoxFuture<'_, FmtUnit>;
|
||||||
fn dyn_serialize<'a>(
|
fn dyn_serialize<'a>(
|
||||||
@@ -283,20 +306,17 @@ pub trait DynOwnedAtom: DynClone + 'static {
|
|||||||
) -> LocalBoxFuture<'a, Option<Vec<Expr>>>;
|
) -> LocalBoxFuture<'a, Option<Vec<Expr>>>;
|
||||||
}
|
}
|
||||||
impl<T: OwnedAtom> DynOwnedAtom for T {
|
impl<T: OwnedAtom> DynOwnedAtom for T {
|
||||||
fn atom_tid(&self) -> TypeId { TypeId::of::<T>() }
|
|
||||||
fn as_any_ref(&self) -> &dyn Any { self }
|
fn as_any_ref(&self) -> &dyn Any { self }
|
||||||
fn encode<'a>(&'a self, buffer: Pin<&'a mut dyn AsyncWrite>) -> LocalBoxFuture<'a, ()> {
|
fn encode<'a>(&'a self, buffer: Pin<&'a mut dyn AsyncWrite>) -> LocalBoxFuture<'a, ()> {
|
||||||
async { self.val().await.as_ref().encode(buffer).await.unwrap() }.boxed_local()
|
async { self.val().await.as_ref().encode(buffer).await.unwrap() }.boxed_local()
|
||||||
}
|
}
|
||||||
fn dyn_call_ref(&self, arg: Expr) -> LocalBoxFuture<'_, GExpr> {
|
fn dyn_call_ref(&self, arg: Expr) -> LocalBoxFuture<'_, GExpr> {
|
||||||
self.call_ref(arg).boxed_local()
|
async { self.call_ref(arg).await.to_gen().await }.boxed_local()
|
||||||
}
|
}
|
||||||
fn dyn_call(self: Box<Self>, arg: Expr) -> LocalBoxFuture<'static, GExpr> {
|
fn dyn_call(self: Box<Self>, arg: Expr) -> LocalBoxFuture<'static, GExpr> {
|
||||||
self.call(arg).boxed_local()
|
async { self.call(arg).await.to_gen().await }.boxed_local()
|
||||||
}
|
|
||||||
fn dyn_command(self: Box<Self>) -> LocalBoxFuture<'static, OrcRes<Option<GExpr>>> {
|
|
||||||
self.command().boxed_local()
|
|
||||||
}
|
}
|
||||||
|
fn dyn_command(self: Box<Self>) -> LocalBoxFuture<'static, CmdResult> { Box::pin(self.command()) }
|
||||||
fn dyn_free(self: Box<Self>) -> LocalBoxFuture<'static, ()> { self.free().boxed_local() }
|
fn dyn_free(self: Box<Self>) -> LocalBoxFuture<'static, ()> { self.free().boxed_local() }
|
||||||
fn dyn_print(&self) -> LocalBoxFuture<'_, FmtUnit> {
|
fn dyn_print(&self) -> LocalBoxFuture<'_, FmtUnit> {
|
||||||
async move { self.print_atom(&FmtCtxImpl::default()).await }.boxed_local()
|
async move { self.print_atom(&FmtCtxImpl::default()).await }.boxed_local()
|
||||||
@@ -330,14 +350,8 @@ pub(crate) fn get_obj_store() -> Rc<ObjStore> {
|
|||||||
OBJ_STORE.try_with(|store| store.clone()).expect("Owned atom store not initialized")
|
OBJ_STORE.try_with(|store| store.clone()).expect("Owned atom store not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn own<A: OwnedAtom>(typ: &TAtom<A>) -> A {
|
/// Debug-print the entire object store. Most useful if the interpreter refuses
|
||||||
let g = get_obj_store().objects.read().await;
|
/// to shut down due to apparent refloops
|
||||||
let atom_id = typ.untyped.atom.drop.expect("Owned atoms always have a drop ID");
|
|
||||||
let dyn_atom =
|
|
||||||
g.get(&atom_id).expect("Atom ID invalid; atom type probably not owned by this crate");
|
|
||||||
dyn_atom.as_any_ref().downcast_ref().cloned().expect("The ID should imply a type as well")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn debug_print_obj_store(show_atoms: bool) {
|
pub async fn debug_print_obj_store(show_atoms: bool) {
|
||||||
let store = get_obj_store();
|
let store = get_obj_store();
|
||||||
let keys = store.objects.read().await.keys().cloned().collect_vec();
|
let keys = store.objects.read().await.keys().cloned().collect_vec();
|
||||||
|
|||||||
@@ -6,40 +6,38 @@ use async_once_cell::OnceCell;
|
|||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use futures::{AsyncWrite, FutureExt};
|
use futures::{AsyncWrite, FutureExt};
|
||||||
use orchid_api_traits::{Coding, enc_vec};
|
use orchid_api_traits::{Coding, enc_vec};
|
||||||
use orchid_base::error::OrcRes;
|
use orchid_base::{FmtUnit, Sym, log};
|
||||||
use orchid_base::format::FmtUnit;
|
|
||||||
use orchid_base::logging::log;
|
|
||||||
use orchid_base::name::Sym;
|
|
||||||
|
|
||||||
use crate::api;
|
|
||||||
use crate::atom::{
|
use crate::atom::{
|
||||||
AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
|
AtomCtx, AtomFactory, AtomOps, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
|
||||||
MethodSetBuilder, err_not_callable, err_not_command, get_info,
|
MethodSetBuilder, err_not_callable, err_not_command,
|
||||||
};
|
};
|
||||||
use crate::expr::Expr;
|
use crate::expr::Expr;
|
||||||
use crate::gen_expr::{GExpr, bot};
|
use crate::gen_expr::{GExpr, bot};
|
||||||
use crate::system::{cted, sys_id};
|
use crate::system::{DynSystemCardExt, cted};
|
||||||
|
use crate::{CmdResult, api};
|
||||||
|
|
||||||
|
/// Value of [Atomic::Variant] for a type that implements [ThinAtom]
|
||||||
pub struct ThinVariant;
|
pub struct ThinVariant;
|
||||||
impl AtomicVariant for ThinVariant {}
|
impl AtomicVariant for ThinVariant {}
|
||||||
impl<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant> for A {
|
impl<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant> for A {
|
||||||
fn _factory(self) -> AtomFactory {
|
fn _factory(self) -> AtomFactory {
|
||||||
AtomFactory::new(async move || {
|
AtomFactory::new(async move || {
|
||||||
let (id, _) = get_info::<A>(cted().inst().card());
|
let (id, _) = cted().inst().card().ops::<A>();
|
||||||
let mut buf = enc_vec(&id);
|
let mut buf = enc_vec(&id);
|
||||||
self.encode_vec(&mut buf);
|
self.encode_vec(&mut buf);
|
||||||
api::Atom { drop: None, data: api::AtomData(buf), owner: sys_id() }
|
api::LocalAtom { drop: None, data: api::AtomData(buf) }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn _info() -> Self::_Info { ThinAtomDynfo { msbuild: Self::reg_reqs(), ms: OnceCell::new() } }
|
fn _info() -> Self::_Info { ThinAtomOps { msbuild: Self::reg_methods(), ms: OnceCell::new() } }
|
||||||
type _Info = ThinAtomDynfo<Self>;
|
type _Info = ThinAtomOps<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ThinAtomDynfo<T: ThinAtom> {
|
pub(crate) struct ThinAtomOps<T: ThinAtom> {
|
||||||
msbuild: MethodSetBuilder<T>,
|
msbuild: MethodSetBuilder<T>,
|
||||||
ms: OnceCell<MethodSet<T>>,
|
ms: OnceCell<MethodSet<T>>,
|
||||||
}
|
}
|
||||||
impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
|
impl<T: ThinAtom> AtomOps for ThinAtomOps<T> {
|
||||||
fn print<'a>(&self, AtomCtx(buf, _): AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit> {
|
fn print<'a>(&self, AtomCtx(buf, _): AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit> {
|
||||||
Box::pin(async move { T::decode_slice(&mut &buf[..]).print().await })
|
Box::pin(async move { T::decode_slice(&mut &buf[..]).print().await })
|
||||||
}
|
}
|
||||||
@@ -58,17 +56,14 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
|
|||||||
&'a self,
|
&'a self,
|
||||||
AtomCtx(buf, ..): AtomCtx<'a>,
|
AtomCtx(buf, ..): AtomCtx<'a>,
|
||||||
key: Sym,
|
key: Sym,
|
||||||
req: Box<dyn orchid_base::reqnot::ReqReader<'a> + 'a>,
|
req: Box<dyn orchid_base::ReqReader<'a> + 'a>,
|
||||||
) -> LocalBoxFuture<'a, bool> {
|
) -> LocalBoxFuture<'a, bool> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let ms = self.ms.get_or_init(self.msbuild.pack()).await;
|
let ms = self.ms.get_or_init(self.msbuild.pack()).await;
|
||||||
ms.dispatch(&T::decode_slice(&mut &buf[..]), key, req).await
|
ms.dispatch(&T::decode_slice(&mut &buf[..]), key, req).await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn command<'a>(
|
fn command<'a>(&'a self, AtomCtx(buf, _): AtomCtx<'a>) -> LocalBoxFuture<'a, CmdResult> {
|
||||||
&'a self,
|
|
||||||
AtomCtx(buf, _): AtomCtx<'a>,
|
|
||||||
) -> LocalBoxFuture<'a, OrcRes<Option<GExpr>>> {
|
|
||||||
async move { T::decode_slice(&mut &buf[..]).command().await }.boxed_local()
|
async move { T::decode_slice(&mut &buf[..]).command().await }.boxed_local()
|
||||||
}
|
}
|
||||||
fn serialize<'a, 'b: 'a>(
|
fn serialize<'a, 'b: 'a>(
|
||||||
@@ -81,7 +76,11 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
|
|||||||
Some(Vec::new())
|
Some(Vec::new())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn deserialize<'a>(&'a self, data: &'a [u8], refs: &'a [Expr]) -> LocalBoxFuture<'a, api::Atom> {
|
fn deserialize<'a>(
|
||||||
|
&'a self,
|
||||||
|
data: &'a [u8],
|
||||||
|
refs: &'a [Expr],
|
||||||
|
) -> LocalBoxFuture<'a, api::LocalAtom> {
|
||||||
assert!(refs.is_empty(), "Refs found when deserializing thin atom");
|
assert!(refs.is_empty(), "Refs found when deserializing thin atom");
|
||||||
Box::pin(async { T::decode_slice(&mut &data[..])._factory().build().await })
|
Box::pin(async { T::decode_slice(&mut &data[..])._factory().build().await })
|
||||||
}
|
}
|
||||||
@@ -93,16 +92,15 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ThinAtom:
|
/// A simple value that is serializable and does not reference any other values
|
||||||
AtomCard<Data = Self> + Atomic<Variant = ThinVariant> + Coding + Send + Sync + 'static
|
pub trait ThinAtom: Atomic<Data = Self> + Atomic<Variant = ThinVariant> + Coding + 'static {
|
||||||
{
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn call(&self, arg: Expr) -> impl Future<Output = GExpr> {
|
fn call(&self, arg: Expr) -> impl Future<Output = GExpr> {
|
||||||
async move { bot(err_not_callable(&self.print().await).await) }
|
async move { bot(err_not_callable(&self.print().await).await) }
|
||||||
}
|
}
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn command(&self) -> impl Future<Output = OrcRes<Option<GExpr>>> {
|
fn command(&self) -> impl Future<Output = CmdResult> {
|
||||||
async move { Err(err_not_command(&self.print().await).await) }
|
async move { Err(err_not_command(&self.print().await).await.into()) }
|
||||||
}
|
}
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn print(&self) -> impl Future<Output = FmtUnit> {
|
fn print(&self) -> impl Future<Output = FmtUnit> {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use orchid_base::binary::future_to_vt;
|
use orchid_base::future_to_vt;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::entrypoint::ExtensionBuilder;
|
use crate::entrypoint::ExtensionBuilder;
|
||||||
@@ -14,19 +15,23 @@ impl Drop for Spawner {
|
|||||||
fn drop(&mut self) { (self.0.drop)(self.0.data) }
|
fn drop(&mut self) { (self.0.drop)(self.0.data) }
|
||||||
}
|
}
|
||||||
impl Spawner {
|
impl Spawner {
|
||||||
pub fn spawn(&self, fut: LocalBoxFuture<'static, ()>) {
|
pub fn spawn(&self, delay: Duration, fut: LocalBoxFuture<'static, ()>) {
|
||||||
(self.0.spawn)(self.0.data, future_to_vt(fut))
|
(self.0.spawn)(self.0.data, delay.as_millis().try_into().unwrap(), future_to_vt(fut))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn orchid_extension_main_body(cx: ExtCx, builder: ExtensionBuilder) {
|
pub fn orchid_extension_main_body(cx: ExtCx, builder: ExtensionBuilder) {
|
||||||
let spawner = Spawner(cx.spawner);
|
let spawner = Rc::new(Spawner(cx.spawner));
|
||||||
builder.build(ExtPort {
|
let spawner2 = spawner.clone();
|
||||||
input: Box::pin(cx.input),
|
spawner2.spawn(
|
||||||
output: Box::pin(cx.output),
|
Duration::ZERO,
|
||||||
log: Box::pin(cx.log),
|
Box::pin(builder.run(ExtPort {
|
||||||
spawn: Rc::new(move |fut| spawner.spawn(fut)),
|
input: Box::pin(cx.input),
|
||||||
});
|
output: Box::pin(cx.output),
|
||||||
|
log: Box::pin(cx.log),
|
||||||
|
spawn: Rc::new(move |delay, fut| spawner.spawn(delay, fut)),
|
||||||
|
})),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate entrypoint for the dylib extension loader
|
/// Generate entrypoint for the dylib extension loader
|
||||||
|
|||||||
@@ -3,16 +3,17 @@ use std::pin::Pin;
|
|||||||
|
|
||||||
use dyn_clone::DynClone;
|
use dyn_clone::DynClone;
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_base::error::{OrcErrv, OrcRes, mk_errv};
|
use orchid_base::{Format, OrcErrv, OrcRes, Pos, fmt, is, mk_errv};
|
||||||
use orchid_base::format::{Format, fmt};
|
|
||||||
use orchid_base::interner::is;
|
|
||||||
use orchid_base::location::Pos;
|
|
||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
use crate::atom::{AtomicFeatures, ForeignAtom, TAtom};
|
use crate::atom::{AtomicFeatures, ForeignAtom, TAtom};
|
||||||
use crate::expr::{Expr, ExprKind};
|
use crate::expr::{Expr, ExprKind};
|
||||||
use crate::gen_expr::{GExpr, bot};
|
use crate::gen_expr::{GExpr, bot};
|
||||||
|
|
||||||
|
/// Attempt to cast a generic Orchid expression reference to a concrete value.
|
||||||
|
/// Note that this cannot evaluate the expression, and if it is not already
|
||||||
|
/// evaluated, it will simply fail. Use [crate::ExecHandle::exec] inside
|
||||||
|
/// [crate::exec] to wait for an expression to be evaluated
|
||||||
pub trait TryFromExpr: Sized {
|
pub trait TryFromExpr: Sized {
|
||||||
fn try_from_expr(expr: Expr) -> impl Future<Output = OrcRes<Self>>;
|
fn try_from_expr(expr: Expr) -> impl Future<Output = OrcRes<Self>>;
|
||||||
}
|
}
|
||||||
@@ -27,6 +28,8 @@ impl<T: TryFromExpr, U: TryFromExpr> TryFromExpr for (T, U) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Error raised when a composite expression was assumed to be an
|
||||||
|
/// [crate::Atomic], or if the expression was not evaluated yet
|
||||||
async fn err_not_atom(pos: Pos, value: &impl Format) -> OrcErrv {
|
async fn err_not_atom(pos: Pos, value: &impl Format) -> OrcErrv {
|
||||||
mk_errv(is("Expected an atom").await, format!("{} is not an atom", fmt(value).await), [pos])
|
mk_errv(is("Expected an atom").await, format!("{} is not an atom", fmt(value).await), [pos])
|
||||||
}
|
}
|
||||||
@@ -46,21 +49,43 @@ impl TryFromExpr for ForeignAtom {
|
|||||||
impl<A: AtomicFeatures> TryFromExpr for TAtom<A> {
|
impl<A: AtomicFeatures> TryFromExpr for TAtom<A> {
|
||||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||||
let f = ForeignAtom::try_from_expr(expr).await?;
|
let f = ForeignAtom::try_from_expr(expr).await?;
|
||||||
match f.clone().downcast::<A>().await {
|
match f.clone().downcast::<A>() {
|
||||||
Ok(a) => Ok(a),
|
Ok(a) => Ok(a),
|
||||||
Err(e) => Err(e.mk_err().await),
|
Err(e) => Err(e.mk_err().await),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Values that are convertible to an Orchid expression. This could mean that
|
||||||
|
/// the value owns an [Expr] or it may involve more complex operations
|
||||||
pub trait ToExpr {
|
pub trait ToExpr {
|
||||||
|
/// Inline the value in an expression returned from a function or included in
|
||||||
|
/// the const tree returned by [crate::System::env]
|
||||||
fn to_gen(self) -> impl Future<Output = GExpr>;
|
fn to_gen(self) -> impl Future<Output = GExpr>;
|
||||||
|
/// Convert the value into a freestanding expression
|
||||||
fn to_expr(self) -> impl Future<Output = Expr>
|
fn to_expr(self) -> impl Future<Output = Expr>
|
||||||
where Self: Sized {
|
where Self: Sized {
|
||||||
async { self.to_gen().await.create().await }
|
async { self.to_gen().await.create().await }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ToExprFuture<F>(pub F);
|
||||||
|
|
||||||
|
impl<F: Future<Output: ToExpr>> ToExpr for ToExprFuture<F> {
|
||||||
|
async fn to_gen(self) -> GExpr { self.0.await.to_gen().await }
|
||||||
|
async fn to_expr(self) -> Expr
|
||||||
|
where Self: Sized {
|
||||||
|
self.0.await.to_expr().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<F: Future> Future for ToExprFuture<F> {
|
||||||
|
type Output = F::Output;
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
|
||||||
|
unsafe { self.map_unchecked_mut(|this| &mut this.0) }.poll(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type-erased [ToExpr]
|
||||||
pub trait ToExprDyn {
|
pub trait ToExprDyn {
|
||||||
fn to_gen_dyn<'a>(self: Box<Self>) -> Pin<Box<dyn Future<Output = GExpr> + 'a>>
|
fn to_gen_dyn<'a>(self: Box<Self>) -> Pin<Box<dyn Future<Output = GExpr> + 'a>>
|
||||||
where Self: 'a;
|
where Self: 'a;
|
||||||
@@ -79,6 +104,8 @@ impl<T: ToExpr> ToExprDyn for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
trait_set! {
|
trait_set! {
|
||||||
|
/// type-erased [ToExpr] and [Clone]. Needed for a value to be
|
||||||
|
/// included in [crate::System::env]
|
||||||
pub trait ClonableToExprDyn = ToExprDyn + DynClone;
|
pub trait ClonableToExprDyn = ToExprDyn + DynClone;
|
||||||
}
|
}
|
||||||
impl ToExpr for Box<dyn ToExprDyn> {
|
impl ToExpr for Box<dyn ToExprDyn> {
|
||||||
|
|||||||
@@ -7,17 +7,16 @@ use futures::lock::Mutex;
|
|||||||
use futures::stream::{self, LocalBoxStream};
|
use futures::stream::{self, LocalBoxStream};
|
||||||
use futures::{FutureExt, SinkExt, StreamExt};
|
use futures::{FutureExt, SinkExt, StreamExt};
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_base::error::OrcRes;
|
use orchid_base::OrcRes;
|
||||||
|
|
||||||
use crate::atom::Atomic;
|
use crate::atom::Atomic;
|
||||||
use crate::atom_owned::{OwnedAtom, OwnedVariant};
|
use crate::atom_owned::{OwnedAtom, OwnedVariant};
|
||||||
use crate::conv::{ToExpr, TryFromExpr};
|
use crate::conv::{ToExpr, TryFromExpr};
|
||||||
use crate::expr::Expr;
|
use crate::expr::Expr;
|
||||||
use crate::gen_expr::{GExpr, arg, call, lambda, new_atom, seq};
|
use crate::gen_expr::{GExpr, arg, call, lam, new_atom, seq};
|
||||||
|
|
||||||
enum Command {
|
enum Command {
|
||||||
Execute(GExpr, Sender<Expr>),
|
Execute(GExpr, Sender<Expr>),
|
||||||
Register(GExpr, Sender<Expr>),
|
|
||||||
Halt(GExpr),
|
Halt(GExpr),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,12 +32,9 @@ impl BuilderCoroutine {
|
|||||||
match cmd {
|
match cmd {
|
||||||
None => panic!("Before the stream ends, we should have gotten a Halt"),
|
None => panic!("Before the stream ends, we should have gotten a Halt"),
|
||||||
Some(Command::Halt(expr)) => expr,
|
Some(Command::Halt(expr)) => expr,
|
||||||
Some(Command::Execute(expr, reply)) => call(
|
Some(Command::Execute(expr, reply)) =>
|
||||||
lambda(0, [seq([arg(0)], call(new_atom(Replier { reply, builder: self }), [arg(0)]))]),
|
call(lam::<0>(seq(arg(0), call(new_atom(Replier { reply, builder: self }), arg(0)))), expr)
|
||||||
[expr],
|
.await,
|
||||||
),
|
|
||||||
Some(Command::Register(expr, reply)) =>
|
|
||||||
call(new_atom(Replier { reply, builder: self }), [expr]),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,7 +51,7 @@ impl Atomic for Replier {
|
|||||||
impl OwnedAtom for Replier {
|
impl OwnedAtom for Replier {
|
||||||
type Refs = Never;
|
type Refs = Never;
|
||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
async fn call(mut self, arg: Expr) -> GExpr {
|
async fn call(mut self, arg: Expr) -> impl ToExpr {
|
||||||
self.reply.send(arg).await.expect("What the heck");
|
self.reply.send(arg).await.expect("What the heck");
|
||||||
std::mem::drop(self.reply);
|
std::mem::drop(self.reply);
|
||||||
self.builder.run().await
|
self.builder.run().await
|
||||||
@@ -81,9 +77,4 @@ impl ExecHandle<'_> {
|
|||||||
self.0.send(Command::Execute(val.to_gen().await, reply_snd)).await.expect(WEIRD_DROP_ERR);
|
self.0.send(Command::Execute(val.to_gen().await, reply_snd)).await.expect(WEIRD_DROP_ERR);
|
||||||
T::try_from_expr(reply_recv.next().await.expect(WEIRD_DROP_ERR)).await
|
T::try_from_expr(reply_recv.next().await.expect(WEIRD_DROP_ERR)).await
|
||||||
}
|
}
|
||||||
pub async fn register(&mut self, val: impl ToExpr) -> Expr {
|
|
||||||
let (reply_snd, mut reply_recv) = channel(1);
|
|
||||||
self.0.send(Command::Register(val.to_gen().await, reply_snd)).await.expect(WEIRD_DROP_ERR);
|
|
||||||
reply_recv.next().await.expect(WEIRD_DROP_ERR)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,27 @@
|
|||||||
|
use std::any::Any;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::num::NonZero;
|
use std::num::NonZero;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures::future::{LocalBoxFuture, join_all};
|
use futures::future::{LocalBoxFuture, join_all};
|
||||||
use futures::{AsyncWriteExt, StreamExt, stream};
|
use futures::{AsyncWriteExt, StreamExt, stream};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_api_traits::{Decode, Encode, Request, UnderRoot, enc_vec};
|
use orchid_api_traits::{Decode, Encode, Request, UnderRoot, enc_vec};
|
||||||
use orchid_base::char_filter::{char_filter_match, char_filter_union, mk_char_filter};
|
use orchid_async_utils::{Handle, to_task};
|
||||||
use orchid_base::error::try_with_reporter;
|
use orchid_base::{
|
||||||
use orchid_base::interner::{es, is, with_interner};
|
Client, ClientExt, CommCtx, Comment, MsgReader, MsgReaderExt, ReqHandleExt, ReqReaderExt,
|
||||||
use orchid_base::logging::{log, with_logger};
|
Snippet, Sym, TokenVariant, Witness, char_filter_match, char_filter_union, es, io_comm, is, log,
|
||||||
use orchid_base::name::Sym;
|
mk_char_filter, try_with_reporter, ttv_from_api, with_interner, with_logger, with_stash,
|
||||||
use orchid_base::parse::{Comment, Snippet};
|
|
||||||
use orchid_base::reqnot::{
|
|
||||||
Client, ClientExt, CommCtx, MsgReader, MsgReaderExt, ReqHandleExt, ReqReaderExt, Witness, io_comm,
|
|
||||||
};
|
};
|
||||||
use orchid_base::stash::with_stash;
|
|
||||||
use orchid_base::tree::{TokenVariant, ttv_from_api};
|
|
||||||
use substack::Substack;
|
use substack::Substack;
|
||||||
use task_local::task_local;
|
use task_local::task_local;
|
||||||
|
|
||||||
use crate::api;
|
|
||||||
use crate::atom::{AtomCtx, AtomTypeId, resolve_atom_type};
|
use crate::atom::{AtomCtx, AtomTypeId, resolve_atom_type};
|
||||||
use crate::atom_owned::{take_atom, with_obj_store};
|
use crate::atom_owned::{take_atom, with_obj_store};
|
||||||
use crate::expr::{BorrowedExprStore, Expr, ExprHandle};
|
use crate::expr::{BorrowedExprStore, Expr, ExprHandle};
|
||||||
@@ -35,10 +32,11 @@ use crate::lexer::{LexContext, ekey_cascade, ekey_not_applicable};
|
|||||||
use crate::logger::LoggerImpl;
|
use crate::logger::LoggerImpl;
|
||||||
use crate::parser::{PTokTree, ParsCtx, get_const, linev_into_api, with_parsed_const_ctx};
|
use crate::parser::{PTokTree, ParsCtx, get_const, linev_into_api, with_parsed_const_ctx};
|
||||||
use crate::reflection::with_refl_roots;
|
use crate::reflection::with_refl_roots;
|
||||||
use crate::system::{SysCtx, atom_by_idx, cted, with_sys};
|
use crate::system::{DynSystemCardExt, SysCtx, cted, with_sys};
|
||||||
use crate::system_ctor::{CtedObj, DynSystemCtor, SystemCtor};
|
use crate::system_ctor::{CtedObj, DynSystemCtor, SystemCtor};
|
||||||
use crate::tree::{TreeIntoApiCtxImpl, get_lazy, with_lazy_member_store};
|
use crate::tree::{TreeIntoApiCtxImpl, get_lazy, with_lazy_member_store};
|
||||||
use crate::trivial_req::TrivialReqCycle;
|
use crate::trivial_req::TrivialReqCycle;
|
||||||
|
use crate::{api, encode_command_result};
|
||||||
|
|
||||||
task_local::task_local! {
|
task_local::task_local! {
|
||||||
static CLIENT: Rc<dyn Client>;
|
static CLIENT: Rc<dyn Client>;
|
||||||
@@ -100,6 +98,33 @@ impl<F: AsyncFnOnce(LocalBoxFuture<'_, ()>) + 'static> ContextModifier for F {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) trait DynTaskHandle: 'static {
|
||||||
|
fn abort(self: Box<Self>);
|
||||||
|
fn join(self: Box<Self>) -> LocalBoxFuture<'static, Box<dyn Any>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
task_local! {
|
||||||
|
pub(crate) static SPAWN:
|
||||||
|
Rc<dyn Fn(Duration, LocalBoxFuture<'static, Box<dyn Any>>) -> Box<dyn DynTaskHandle> + 'static>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TaskHandle<T>(Box<dyn DynTaskHandle>, PhantomData<T>);
|
||||||
|
impl<T: 'static> TaskHandle<T> {
|
||||||
|
pub fn abort(self) { self.0.abort(); }
|
||||||
|
pub async fn join(self) -> T { *self.0.join().await.downcast().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn spawn<F: Future<Output: 'static> + 'static>(delay: Duration, f: F) -> TaskHandle<F::Output> {
|
||||||
|
SPAWN.with(|spawn| {
|
||||||
|
TaskHandle(spawn(delay, Box::pin(async { Box::new(f.await) as Box<dyn Any> })), PhantomData)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynTaskHandle for Handle<Box<dyn Any>> {
|
||||||
|
fn abort(self: Box<Self>) { Self::abort(&self); }
|
||||||
|
fn join(self: Box<Self>) -> LocalBoxFuture<'static, Box<dyn Any>> { Box::pin(Self::join(*self)) }
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ExtensionBuilder {
|
pub struct ExtensionBuilder {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
pub systems: Vec<Box<dyn DynSystemCtor>>,
|
pub systems: Vec<Box<dyn DynSystemCtor>>,
|
||||||
@@ -118,275 +143,272 @@ impl ExtensionBuilder {
|
|||||||
self.add_context(fun);
|
self.add_context(fun);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn build(mut self, mut ctx: ExtPort) {
|
pub async fn run(mut self, mut ctx: ExtPort) {
|
||||||
self.add_context(with_funs_ctx);
|
self.add_context(with_funs_ctx);
|
||||||
self.add_context(with_parsed_const_ctx);
|
self.add_context(with_parsed_const_ctx);
|
||||||
self.add_context(with_obj_store);
|
self.add_context(with_obj_store);
|
||||||
self.add_context(with_lazy_member_store);
|
self.add_context(with_lazy_member_store);
|
||||||
self.add_context(with_refl_roots);
|
self.add_context(with_refl_roots);
|
||||||
(ctx.spawn)(Box::pin(async move {
|
let spawn = ctx.spawn.clone();
|
||||||
let host_header = api::HostHeader::decode(ctx.input.as_mut()).await.unwrap();
|
let host_header = api::HostHeader::decode(ctx.input.as_mut()).await.unwrap();
|
||||||
let decls = (self.systems.iter().enumerate())
|
let decls = (self.systems.iter().enumerate())
|
||||||
.map(|(id, sys)| (u16::try_from(id).expect("more than u16max system ctors"), sys))
|
.map(|(id, sys)| (u16::try_from(id).expect("more than u16max system ctors"), sys))
|
||||||
.map(|(id, sys)| sys.decl(api::SysDeclId(NonZero::new(id + 1).unwrap())))
|
.map(|(id, sys)| sys.decl(api::SysDeclId(NonZero::new(id + 1).unwrap())))
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
api::ExtensionHeader { name: self.name.to_string(), systems: decls.clone() }
|
api::ExtensionHeader { name: self.name.to_string(), systems: decls.clone() }
|
||||||
.encode(ctx.output.as_mut())
|
.encode(ctx.output.as_mut())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ctx.output.as_mut().flush().await.unwrap();
|
ctx.output.as_mut().flush().await.unwrap();
|
||||||
let logger1 = LoggerImpl::from_api(&host_header.logger);
|
let logger1 = LoggerImpl::from_api(&host_header.logger);
|
||||||
let logger2 = logger1.clone();
|
let logger2 = logger1.clone();
|
||||||
let (client, comm_ctx, extension_srv) = io_comm(ctx.output, ctx.input);
|
let (client, comm_ctx, extension_srv) = io_comm(ctx.output, ctx.input);
|
||||||
let extension_fut = extension_srv.listen(
|
// this future will be ready once the extension cleanly exits
|
||||||
async |n: Box<dyn MsgReader<'_>>| {
|
let extension_fut = extension_srv.listen(
|
||||||
let notif = n.read().await.unwrap();
|
async |n: Box<dyn MsgReader<'_>>| {
|
||||||
match notif {
|
let notif = n.read().await.unwrap();
|
||||||
api::HostExtNotif::Exit => exit().await,
|
match notif {
|
||||||
|
api::HostExtNotif::Exit => exit().await,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
async |mut reader| {
|
||||||
|
with_stash(async {
|
||||||
|
let req = reader.read_req().await.unwrap();
|
||||||
|
let handle = reader.finish().await;
|
||||||
|
// Atom printing is never reported because it generates too much
|
||||||
|
// noise
|
||||||
|
if !matches!(req, api::HostExtReq::AtomReq(api::AtomReq::AtomPrint(_))) {
|
||||||
|
writeln!(log("msg"), "{} extension received request {req:?}", self.name).await;
|
||||||
}
|
}
|
||||||
Ok(())
|
match req {
|
||||||
},
|
api::HostExtReq::SystemDrop(sys_drop) => {
|
||||||
async |mut reader| {
|
SYSTEM_TABLE.with(|l| l.borrow_mut().remove(&sys_drop.0));
|
||||||
with_stash(async {
|
handle.reply(&sys_drop, &()).await
|
||||||
let req = reader.read_req().await.unwrap();
|
},
|
||||||
let handle = reader.finish().await;
|
api::HostExtReq::AtomDrop(atom_drop @ api::AtomDrop(sys_id, atom)) =>
|
||||||
// Atom printing is never reported because it generates too much
|
with_sys_record(sys_id, async {
|
||||||
// noise
|
take_atom(atom).await.dyn_free().await;
|
||||||
if !matches!(req, api::HostExtReq::AtomReq(api::AtomReq::AtomPrint(_))) {
|
handle.reply(&atom_drop, &()).await
|
||||||
writeln!(log("msg"), "{} extension received request {req:?}", self.name).await;
|
})
|
||||||
}
|
.await,
|
||||||
match req {
|
api::HostExtReq::Ping(ping @ api::Ping) => handle.reply(&ping, &()).await,
|
||||||
api::HostExtReq::SystemDrop(sys_drop) => {
|
api::HostExtReq::Sweep(api::Sweep) => todo!(),
|
||||||
SYSTEM_TABLE.with(|l| l.borrow_mut().remove(&sys_drop.0));
|
api::HostExtReq::SysReq(api::SysReq::NewSystem(new_sys)) => {
|
||||||
handle.reply(&sys_drop, &()).await
|
let (ctor_idx, _) = (decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system))
|
||||||
},
|
.expect("NewSystem call received for invalid system");
|
||||||
api::HostExtReq::AtomDrop(atom_drop @ api::AtomDrop(sys_id, atom)) =>
|
let cted = self.systems[ctor_idx].new_system(&new_sys);
|
||||||
with_sys_record(sys_id, async {
|
let record = Rc::new(SystemRecord { cted: cted.clone() });
|
||||||
take_atom(atom).await.dyn_free().await;
|
SYSTEM_TABLE.with(|tbl| {
|
||||||
handle.reply(&atom_drop, &()).await
|
let mut g = tbl.borrow_mut();
|
||||||
})
|
g.insert(new_sys.id, record);
|
||||||
.await,
|
});
|
||||||
api::HostExtReq::Ping(ping @ api::Ping) => handle.reply(&ping, &()).await,
|
with_sys_record(new_sys.id, async {
|
||||||
api::HostExtReq::Sweep(api::Sweep) => todo!(),
|
let lex_filter =
|
||||||
api::HostExtReq::SysReq(api::SysReq::NewSystem(new_sys)) => {
|
cted.inst().dyn_lexers().iter().fold(api::CharFilter(vec![]), |cf, lx| {
|
||||||
let (ctor_idx, _) =
|
char_filter_union(&cf, &mk_char_filter(lx.char_filter().iter().cloned()))
|
||||||
(decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system))
|
});
|
||||||
.expect("NewSystem call received for invalid system");
|
let const_root = stream::iter(cted.inst().dyn_env().await)
|
||||||
let cted = self.systems[ctor_idx].new_system(&new_sys);
|
.then(async |mem| {
|
||||||
let record = Rc::new(SystemRecord { cted: cted.clone() });
|
let name = is(&mem.name).await;
|
||||||
SYSTEM_TABLE.with(|tbl| {
|
let mut tia_ctx = TreeIntoApiCtxImpl {
|
||||||
let mut g = tbl.borrow_mut();
|
basepath: &[],
|
||||||
g.insert(new_sys.id, record);
|
path: Substack::Bottom.push(name.clone()),
|
||||||
});
|
};
|
||||||
with_sys_record(new_sys.id, async {
|
(name.to_api(), mem.kind.into_api(&mut tia_ctx).await)
|
||||||
let lex_filter =
|
})
|
||||||
cted.inst().dyn_lexers().iter().fold(api::CharFilter(vec![]), |cf, lx| {
|
.collect()
|
||||||
char_filter_union(&cf, &mk_char_filter(lx.char_filter().iter().cloned()))
|
|
||||||
});
|
|
||||||
let const_root = stream::iter(cted.inst().dyn_env().await)
|
|
||||||
.then(async |mem| {
|
|
||||||
let name = is(&mem.name).await;
|
|
||||||
let mut tia_ctx = TreeIntoApiCtxImpl {
|
|
||||||
basepath: &[],
|
|
||||||
path: Substack::Bottom.push(name.clone()),
|
|
||||||
};
|
|
||||||
(name.to_api(), mem.kind.into_api(&mut tia_ctx).await)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
.await;
|
|
||||||
let prelude =
|
|
||||||
cted.inst().dyn_prelude().await.iter().map(|sym| sym.to_api()).collect();
|
|
||||||
let line_types = join_all(
|
|
||||||
(cted.inst().dyn_parsers().iter())
|
|
||||||
.map(async |p| is(p.line_head()).await.to_api()),
|
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
let response =
|
let prelude =
|
||||||
api::NewSystemResponse { lex_filter, const_root, line_types, prelude };
|
cted.inst().dyn_prelude().await.iter().map(|sym| sym.to_api()).collect();
|
||||||
handle.reply(&new_sys, &response).await
|
let line_types = join_all(
|
||||||
})
|
(cted.inst().dyn_parsers().iter())
|
||||||
.await
|
.map(async |p| is(p.line_head()).await.to_api()),
|
||||||
},
|
)
|
||||||
api::HostExtReq::GetMember(get_tree @ api::GetMember(sys_id, tree_id)) =>
|
.await;
|
||||||
with_sys_record(sys_id, async {
|
let response =
|
||||||
let (path, tree) = get_lazy(tree_id).await;
|
api::NewSystemResponse { lex_filter, const_root, line_types, prelude };
|
||||||
let mut tia_ctx =
|
handle.reply(&new_sys, &response).await
|
||||||
TreeIntoApiCtxImpl { path: Substack::Bottom, basepath: &path[..] };
|
})
|
||||||
handle.reply(&get_tree, &tree.into_api(&mut tia_ctx).await).await
|
.await
|
||||||
})
|
},
|
||||||
.await,
|
api::HostExtReq::GetMember(get_tree @ api::GetMember(sys_id, tree_id)) =>
|
||||||
api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => {
|
with_sys_record(sys_id, async {
|
||||||
let fwd_tok = Witness::of(&fwd);
|
let (path, tree) = get_lazy(tree_id).await;
|
||||||
let api::SysFwded(sys_id, payload) = fwd;
|
let mut tia_ctx =
|
||||||
with_sys_record(sys_id, async {
|
TreeIntoApiCtxImpl { path: Substack::Bottom, basepath: &path[..] };
|
||||||
let mut reply = Vec::new();
|
handle.reply(&get_tree, &tree.into_api(&mut tia_ctx).await).await
|
||||||
let req = TrivialReqCycle { req: &payload, rep: &mut reply };
|
})
|
||||||
let _ = cted().inst().dyn_request(Box::new(req)).await;
|
.await,
|
||||||
handle.reply(fwd_tok, &reply).await
|
api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => {
|
||||||
})
|
let fwd_tok = Witness::of(&fwd);
|
||||||
.await
|
let api::SysFwded(sys_id, payload) = fwd;
|
||||||
},
|
with_sys_record(sys_id, async {
|
||||||
api::HostExtReq::LexExpr(lex @ api::LexExpr { sys, src, text, pos, id }) =>
|
let mut reply = Vec::new();
|
||||||
with_sys_record(sys, async {
|
let req = TrivialReqCycle { req: &payload, rep: &mut reply };
|
||||||
let text = es(text).await;
|
let _ = cted().inst().dyn_request(Box::new(req)).await;
|
||||||
let src = Sym::from_api(src).await;
|
handle.reply(fwd_tok, &reply).await
|
||||||
let expr_store = BorrowedExprStore::new();
|
})
|
||||||
let tail = &text[pos as usize..];
|
.await
|
||||||
let trigger_char = tail.chars().next().unwrap();
|
},
|
||||||
let ekey_na = ekey_not_applicable().await;
|
api::HostExtReq::LexExpr(lex @ api::LexExpr { sys, src, text, pos, id }) =>
|
||||||
let ekey_cascade = ekey_cascade().await;
|
with_sys_record(sys, async {
|
||||||
let lexers = cted().inst().dyn_lexers();
|
let text = es(text).await;
|
||||||
for lx in
|
let src = Sym::from_api(src).await;
|
||||||
lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char))
|
let expr_store = BorrowedExprStore::new();
|
||||||
{
|
let tail = &text[pos as usize..];
|
||||||
let ctx = LexContext::new(&expr_store, &text, id, pos, src.clone());
|
let trigger_char = tail.chars().next().unwrap();
|
||||||
match try_with_reporter(lx.lex(tail, &ctx)).await {
|
let ekey_na = ekey_not_applicable().await;
|
||||||
Err(e) if e.any(|e| *e == ekey_na) => continue,
|
let ekey_cascade = ekey_cascade().await;
|
||||||
Err(e) => {
|
let lexers = cted().inst().dyn_lexers();
|
||||||
let eopt = e.keep_only(|e| *e != ekey_cascade).map(|e| Err(e.to_api()));
|
for lx in lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char))
|
||||||
expr_store.dispose().await;
|
{
|
||||||
return handle.reply(&lex, &eopt).await;
|
let ctx = LexContext::new(&expr_store, &text, id, pos, src.clone());
|
||||||
},
|
match try_with_reporter(lx.lex(tail, &ctx)).await {
|
||||||
Ok((s, expr)) => {
|
Err(e) if e.any(|e| *e == ekey_na) => continue,
|
||||||
let expr = join_all(
|
Err(e) => {
|
||||||
(expr.into_iter())
|
let eopt = e.keep_only(|e| *e != ekey_cascade).map(|e| Err(e.to_api()));
|
||||||
.map(|tok| async { tok.into_api(&mut (), &mut ()).await }),
|
expr_store.dispose().await;
|
||||||
)
|
return handle.reply(&lex, &eopt).await;
|
||||||
.await;
|
},
|
||||||
let pos = (text.len() - s.len()) as u32;
|
Ok((s, expr)) => {
|
||||||
expr_store.dispose().await;
|
let expr = join_all(
|
||||||
return handle.reply(&lex, &Some(Ok(api::LexedExpr { pos, expr }))).await;
|
(expr.into_iter())
|
||||||
|
.map(|tok| async { tok.into_api(&mut (), &mut ()).await }),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let pos = (text.len() - s.len()) as u32;
|
||||||
|
expr_store.dispose().await;
|
||||||
|
return handle.reply(&lex, &Some(Ok(api::LexedExpr { pos, expr }))).await;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeln!(log("warn"), "Got notified about n/a character '{trigger_char}'").await;
|
||||||
|
expr_store.dispose().await;
|
||||||
|
handle.reply(&lex, &None).await
|
||||||
|
})
|
||||||
|
.await,
|
||||||
|
api::HostExtReq::ParseLine(pline) => {
|
||||||
|
let req = Witness::of(&pline);
|
||||||
|
let api::ParseLine { module, src, exported, comments, sys, line, idx } = pline;
|
||||||
|
with_sys_record(sys, async {
|
||||||
|
let parsers = cted().inst().dyn_parsers();
|
||||||
|
let src = Sym::from_api(src).await;
|
||||||
|
let comments =
|
||||||
|
join_all(comments.iter().map(|c| Comment::from_api(c, src.clone()))).await;
|
||||||
|
let expr_store = BorrowedExprStore::new();
|
||||||
|
let line: Vec<PTokTree> = ttv_from_api(line, &mut &expr_store, &mut (), &src).await;
|
||||||
|
let snip = Snippet::new(line.first().expect("Empty line"), &line);
|
||||||
|
let parser = parsers[idx as usize];
|
||||||
|
let module = Sym::from_api(module).await;
|
||||||
|
let pctx = ParsCtx::new(module);
|
||||||
|
let o_line =
|
||||||
|
match try_with_reporter(parser.parse(pctx, exported, comments, snip)).await {
|
||||||
|
Err(e) => Err(e.to_api()),
|
||||||
|
Ok(t) => Ok(linev_into_api(t).await),
|
||||||
|
};
|
||||||
|
mem::drop(line);
|
||||||
|
expr_store.dispose().await;
|
||||||
|
handle.reply(req, &o_line).await
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
},
|
||||||
|
api::HostExtReq::FetchParsedConst(ref fpc @ api::FetchParsedConst(sys, id)) =>
|
||||||
|
with_sys_record(sys, async {
|
||||||
|
let cnst = get_const(id).await;
|
||||||
|
handle.reply(fpc, &cnst.serialize().await).await
|
||||||
|
})
|
||||||
|
.await,
|
||||||
|
api::HostExtReq::AtomReq(atom_req) => {
|
||||||
|
let atom = atom_req.get_atom();
|
||||||
|
with_sys_record(atom.owner, async {
|
||||||
|
let (nfo, id, buf) = resolve_atom_type(atom);
|
||||||
|
let actx = AtomCtx(buf, atom.drop);
|
||||||
|
match &atom_req {
|
||||||
|
api::AtomReq::SerializeAtom(ser) => {
|
||||||
|
let mut buf = enc_vec(&id);
|
||||||
|
match nfo.serialize(actx, Pin::<&mut Vec<_>>::new(&mut buf)).await {
|
||||||
|
None => handle.reply(ser, &None).await,
|
||||||
|
Some(refs) => {
|
||||||
|
let refs =
|
||||||
|
join_all(refs.into_iter().map(async |ex| ex.into_api(&mut ()).await))
|
||||||
|
.await;
|
||||||
|
handle.reply(ser, &Some((buf, refs))).await
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
writeln!(log("warn"), "Got notified about n/a character '{trigger_char}'").await;
|
api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) =>
|
||||||
expr_store.dispose().await;
|
handle.reply(print, &nfo.print(actx).await.to_api()).await,
|
||||||
handle.reply(&lex, &None).await
|
api::AtomReq::Fwded(fwded) => {
|
||||||
})
|
let api::Fwded(_, key, payload) = &fwded;
|
||||||
.await,
|
let mut reply = Vec::new();
|
||||||
api::HostExtReq::ParseLine(pline) => {
|
let key = Sym::from_api(*key).await;
|
||||||
let api::ParseLine { module, src, exported, comments, sys, line, idx } = &pline;
|
let req = TrivialReqCycle { req: payload, rep: &mut reply };
|
||||||
with_sys_record(*sys, async {
|
let some = nfo.handle_req(actx, key, Box::new(req)).await;
|
||||||
let parsers = cted().inst().dyn_parsers();
|
handle.reply(fwded, &some.then_some(reply)).await
|
||||||
let src = Sym::from_api(*src).await;
|
},
|
||||||
let comments =
|
api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => {
|
||||||
join_all(comments.iter().map(|c| Comment::from_api(c, src.clone()))).await;
|
let expr_store = BorrowedExprStore::new();
|
||||||
let expr_store = BorrowedExprStore::new();
|
let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
|
||||||
let line: Vec<PTokTree> =
|
let ret = nfo.call_ref(actx, Expr::from_handle(expr_handle.clone())).await;
|
||||||
ttv_from_api(line, &mut &expr_store, &mut (), &src).await;
|
let api_expr = ret.serialize().await;
|
||||||
let snip = Snippet::new(line.first().expect("Empty line"), &line);
|
mem::drop(expr_handle);
|
||||||
let parser = parsers[*idx as usize];
|
expr_store.dispose().await;
|
||||||
let module = Sym::from_api(*module).await;
|
handle.reply(call, &api_expr).await
|
||||||
let pctx = ParsCtx::new(module);
|
},
|
||||||
let o_line =
|
api::AtomReq::FinalCall(call @ api::FinalCall(_, arg)) => {
|
||||||
match try_with_reporter(parser.parse(pctx, *exported, comments, snip)).await {
|
let expr_store = BorrowedExprStore::new();
|
||||||
Err(e) => Err(e.to_api()),
|
let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
|
||||||
Ok(t) => Ok(linev_into_api(t).await),
|
let ret = nfo.call(actx, Expr::from_handle(expr_handle.clone())).await;
|
||||||
};
|
let api_expr = ret.serialize().await;
|
||||||
mem::drop(line);
|
mem::drop(expr_handle);
|
||||||
expr_store.dispose().await;
|
expr_store.dispose().await;
|
||||||
handle.reply(&pline, &o_line).await
|
handle.reply(call, &api_expr).await
|
||||||
})
|
},
|
||||||
.await
|
api::AtomReq::Command(cmd @ api::Command(_)) =>
|
||||||
},
|
handle.reply(cmd, &encode_command_result(nfo.command(actx).await).await).await,
|
||||||
api::HostExtReq::FetchParsedConst(ref fpc @ api::FetchParsedConst(sys, id)) =>
|
}
|
||||||
with_sys_record(sys, async {
|
})
|
||||||
let cnst = get_const(id).await;
|
.await
|
||||||
handle.reply(fpc, &cnst.serialize().await).await
|
},
|
||||||
})
|
api::HostExtReq::DeserAtom(deser) => {
|
||||||
.await,
|
let api::DeserAtom(sys, buf, refs) = &deser;
|
||||||
api::HostExtReq::AtomReq(atom_req) => {
|
let read = &mut &buf[..];
|
||||||
let atom = atom_req.get_atom();
|
with_sys_record(*sys, async {
|
||||||
with_sys_record(atom.owner, async {
|
// SAFETY: deserialization implicitly grants ownership to previously owned exprs
|
||||||
let (nfo, id, buf) = resolve_atom_type(atom);
|
let refs = (refs.iter())
|
||||||
let actx = AtomCtx(buf, atom.drop);
|
.map(|tk| Expr::from_handle(ExprHandle::deserialize(*tk)))
|
||||||
match &atom_req {
|
.collect_vec();
|
||||||
api::AtomReq::SerializeAtom(ser) => {
|
let id = AtomTypeId::decode_slice(read);
|
||||||
let mut buf = enc_vec(&id);
|
let nfo = (cted().inst().card().ops_by_atid(id))
|
||||||
match nfo.serialize(actx, Pin::<&mut Vec<_>>::new(&mut buf)).await {
|
.expect("Deserializing atom with invalid ID");
|
||||||
None => handle.reply(ser, &None).await,
|
handle.reply(&deser, &nfo.deserialize(read, &refs).await).await
|
||||||
Some(refs) => {
|
})
|
||||||
let refs =
|
.await
|
||||||
join_all(refs.into_iter().map(async |ex| ex.into_api(&mut ()).await))
|
},
|
||||||
.await;
|
}
|
||||||
handle.reply(ser, &Some((buf, refs))).await
|
})
|
||||||
},
|
.await
|
||||||
}
|
},
|
||||||
},
|
);
|
||||||
api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) =>
|
// add essential services to the very tail, then fold all context into the run
|
||||||
handle.reply(print, &nfo.print(actx).await.to_api()).await,
|
// future
|
||||||
api::AtomReq::Fwded(fwded) => {
|
SYSTEM_TABLE
|
||||||
let api::Fwded(_, key, payload) = &fwded;
|
.scope(
|
||||||
let mut reply = Vec::new();
|
RefCell::default(),
|
||||||
let key = Sym::from_api(*key).await;
|
with_interner(
|
||||||
let req = TrivialReqCycle { req: payload, rep: &mut reply };
|
new_interner(),
|
||||||
let some = nfo.handle_req(actx, key, Box::new(req)).await;
|
with_logger(
|
||||||
handle.reply(fwded, &some.then_some(reply)).await
|
logger2,
|
||||||
},
|
with_comm(
|
||||||
api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => {
|
Rc::new(client),
|
||||||
let expr_store = BorrowedExprStore::new();
|
comm_ctx,
|
||||||
let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
|
SPAWN.scope(
|
||||||
let ret = nfo.call_ref(actx, Expr::from_handle(expr_handle.clone())).await;
|
Rc::new(move |delay, fut| {
|
||||||
let api_expr = ret.serialize().await;
|
let (poll, handle) = to_task(fut);
|
||||||
mem::drop(expr_handle);
|
spawn(delay, Box::pin(poll));
|
||||||
expr_store.dispose().await;
|
Box::new(handle)
|
||||||
handle.reply(call, &api_expr).await
|
}),
|
||||||
},
|
|
||||||
api::AtomReq::FinalCall(call @ api::FinalCall(_, arg)) => {
|
|
||||||
let expr_store = BorrowedExprStore::new();
|
|
||||||
let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
|
|
||||||
let ret = nfo.call(actx, Expr::from_handle(expr_handle.clone())).await;
|
|
||||||
let api_expr = ret.serialize().await;
|
|
||||||
mem::drop(expr_handle);
|
|
||||||
expr_store.dispose().await;
|
|
||||||
handle.reply(call, &api_expr).await
|
|
||||||
},
|
|
||||||
api::AtomReq::Command(cmd @ api::Command(_)) => match nfo.command(actx).await {
|
|
||||||
Err(e) => handle.reply(cmd, &Err(e.to_api())).await,
|
|
||||||
Ok(opt) => match opt {
|
|
||||||
None => handle.reply(cmd, &Ok(api::NextStep::Halt)).await,
|
|
||||||
Some(cont) => {
|
|
||||||
let cont = cont.serialize().await;
|
|
||||||
handle.reply(cmd, &Ok(api::NextStep::Continue(cont))).await
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
},
|
|
||||||
api::HostExtReq::DeserAtom(deser) => {
|
|
||||||
let api::DeserAtom(sys, buf, refs) = &deser;
|
|
||||||
let read = &mut &buf[..];
|
|
||||||
with_sys_record(*sys, async {
|
|
||||||
// SAFETY: deserialization implicitly grants ownership to previously owned exprs
|
|
||||||
let refs = (refs.iter())
|
|
||||||
.map(|tk| Expr::from_handle(ExprHandle::deserialize(*tk)))
|
|
||||||
.collect_vec();
|
|
||||||
let id = AtomTypeId::decode_slice(read);
|
|
||||||
let nfo = atom_by_idx(cted().inst().card(), id)
|
|
||||||
.expect("Deserializing atom with invalid ID");
|
|
||||||
handle.reply(&deser, &nfo.deserialize(read, &refs).await).await
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
},
|
|
||||||
);
|
|
||||||
// add essential services to the very tail, then fold all context into the run
|
|
||||||
// future
|
|
||||||
SYSTEM_TABLE
|
|
||||||
.scope(
|
|
||||||
RefCell::default(),
|
|
||||||
with_interner(
|
|
||||||
new_interner(),
|
|
||||||
with_logger(
|
|
||||||
logger2,
|
|
||||||
with_comm(
|
|
||||||
Rc::new(client),
|
|
||||||
comm_ctx,
|
|
||||||
(self.context.into_iter()).fold(
|
(self.context.into_iter()).fold(
|
||||||
Box::pin(async { extension_fut.await.unwrap() }) as LocalBoxFuture<()>,
|
Box::pin(async { extension_fut.await.unwrap() }) as LocalBoxFuture<()>,
|
||||||
|fut, cx| cx.apply(fut),
|
|fut, cx| cx.apply(fut),
|
||||||
@@ -394,8 +416,8 @@ impl ExtensionBuilder {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
.await;
|
)
|
||||||
}) as Pin<Box<_>>);
|
.await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,310 +0,0 @@
|
|||||||
use std::any::Any;
|
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::sync::{Arc, OnceLock};
|
|
||||||
use std::{fmt, iter};
|
|
||||||
|
|
||||||
use dyn_clone::{clone_box, DynClone};
|
|
||||||
use itertools::Itertools;
|
|
||||||
use orchid_base::boxed_iter::{box_once, BoxedIter};
|
|
||||||
use orchid_base::clone;
|
|
||||||
use orchid_base::error::{ErrPos, OrcError};
|
|
||||||
use orchid_base::interner::{deintern, intern};
|
|
||||||
use orchid_base::location::{GetSrc, Pos};
|
|
||||||
use orchid_base::reqnot::{ReqNot, Requester};
|
|
||||||
|
|
||||||
use crate::api;
|
|
||||||
|
|
||||||
/// Errors addressed to the developer which are to be resolved with
|
|
||||||
/// code changes
|
|
||||||
pub trait ProjectError: Sized + Send + Sync + 'static {
|
|
||||||
/// A general description of this type of error
|
|
||||||
const DESCRIPTION: &'static str;
|
|
||||||
/// A formatted message that includes specific parameters
|
|
||||||
#[must_use]
|
|
||||||
fn message(&self) -> String { self.description().to_string() }
|
|
||||||
/// Code positions relevant to this error. If you don't implement this, you
|
|
||||||
/// must implement [ProjectError::one_position]
|
|
||||||
#[must_use]
|
|
||||||
fn positions(&self) -> impl IntoIterator<Item = ErrPos> + '_ {
|
|
||||||
box_once(ErrPos { position: self.one_position(), message: None })
|
|
||||||
}
|
|
||||||
/// Short way to provide a single origin. If you don't implement this, you
|
|
||||||
/// must implement [ProjectError::positions]
|
|
||||||
#[must_use]
|
|
||||||
fn one_position(&self) -> Pos {
|
|
||||||
unimplemented!("Error type did not implement either positions or one_position")
|
|
||||||
}
|
|
||||||
/// Convert the error into an `Arc<dyn DynProjectError>` to be able to
|
|
||||||
/// handle various errors together
|
|
||||||
#[must_use]
|
|
||||||
fn pack(self) -> ProjectErrorObj { Arc::new(self) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Object-safe version of [ProjectError]. Implement that instead of this.
|
|
||||||
pub trait DynProjectError: Send + Sync + 'static {
|
|
||||||
/// Access type information about this error
|
|
||||||
#[must_use]
|
|
||||||
fn as_any_ref(&self) -> &dyn Any;
|
|
||||||
/// Pack the error into a trait object, or leave it as-is if it's already a
|
|
||||||
/// trait object
|
|
||||||
#[must_use]
|
|
||||||
fn into_packed(self: Arc<Self>) -> ProjectErrorObj;
|
|
||||||
/// A general description of this type of error
|
|
||||||
#[must_use]
|
|
||||||
fn description(&self) -> Cow<'_, str>;
|
|
||||||
/// A formatted message that includes specific parameters
|
|
||||||
#[must_use]
|
|
||||||
fn message(&self) -> String { self.description().to_string() }
|
|
||||||
/// Code positions relevant to this error.
|
|
||||||
#[must_use]
|
|
||||||
fn positions(&self) -> BoxedIter<'_, ErrPos>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> DynProjectError for T
|
|
||||||
where T: ProjectError
|
|
||||||
{
|
|
||||||
fn as_any_ref(&self) -> &dyn Any { self }
|
|
||||||
fn into_packed(self: Arc<Self>) -> ProjectErrorObj { self }
|
|
||||||
fn description(&self) -> Cow<'_, str> { Cow::Borrowed(T::DESCRIPTION) }
|
|
||||||
fn message(&self) -> String { ProjectError::message(self) }
|
|
||||||
fn positions(&self) -> BoxedIter<ErrPos> { Box::new(ProjectError::positions(self).into_iter()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pretty_print(err: &dyn DynProjectError, get_src: &mut impl GetSrc) -> String {
|
|
||||||
let description = err.description();
|
|
||||||
let message = err.message();
|
|
||||||
let positions = err.positions().collect::<Vec<_>>();
|
|
||||||
let head = format!("Project error: {description}\n{message}");
|
|
||||||
if positions.is_empty() {
|
|
||||||
head + "No origins specified"
|
|
||||||
} else {
|
|
||||||
iter::once(head)
|
|
||||||
.chain(positions.iter().map(|ErrPos { position: origin, message }| match message {
|
|
||||||
None => format!("@{}", origin.pretty_print(get_src)),
|
|
||||||
Some(msg) => format!("@{}: {msg}", origin.pretty_print(get_src)),
|
|
||||||
}))
|
|
||||||
.join("\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DynProjectError for ProjectErrorObj {
|
|
||||||
fn as_any_ref(&self) -> &dyn Any { (**self).as_any_ref() }
|
|
||||||
fn description(&self) -> Cow<'_, str> { (**self).description() }
|
|
||||||
fn into_packed(self: Arc<Self>) -> ProjectErrorObj { (*self).clone() }
|
|
||||||
fn message(&self) -> String { (**self).message() }
|
|
||||||
fn positions(&self) -> BoxedIter<'_, ErrPos> { (**self).positions() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type-erased [ProjectError] implementor through the [DynProjectError]
|
|
||||||
/// object-trait
|
|
||||||
pub type ProjectErrorObj = Arc<dyn DynProjectError>;
|
|
||||||
/// Alias for a result with an error of [ProjectErrorObj]. This is the type of
|
|
||||||
/// result most commonly returned by pre-runtime operations.
|
|
||||||
pub type ProjectResult<T> = Result<T, ProjectErrorObj>;
|
|
||||||
|
|
||||||
/// A trait for error types that are only missing an origin. Do not depend on
|
|
||||||
/// this trait, refer to [DynErrorSansOrigin] instead.
|
|
||||||
pub trait ErrorSansOrigin: Clone + Sized + Send + Sync + 'static {
|
|
||||||
/// General description of the error condition
|
|
||||||
const DESCRIPTION: &'static str;
|
|
||||||
/// Specific description of the error including code fragments or concrete
|
|
||||||
/// data if possible
|
|
||||||
fn message(&self) -> String { Self::DESCRIPTION.to_string() }
|
|
||||||
/// Convert the error to a type-erased structure for handling on shared
|
|
||||||
/// channels
|
|
||||||
fn pack(self) -> ErrorSansOriginObj { Box::new(self) }
|
|
||||||
/// A shortcut to streamline switching code between [ErrorSansOriginObj] and
|
|
||||||
/// concrete types
|
|
||||||
fn bundle(self, origin: &Pos) -> ProjectErrorObj { self.pack().bundle(origin) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Object-safe equivalent to [ErrorSansOrigin]. Implement that one instead of
|
|
||||||
/// this. Typically found as [ErrorSansOriginObj]
|
|
||||||
pub trait DynErrorSansOrigin: Any + Send + Sync + DynClone {
|
|
||||||
/// Allow to downcast the base object to distinguish between various errors.
|
|
||||||
/// The main intended purpose is to trigger a fallback when [CodeNotFound] is
|
|
||||||
/// encountered, but the possibilities are not limited to that.
|
|
||||||
fn as_any_ref(&self) -> &dyn Any;
|
|
||||||
/// Regularize the type
|
|
||||||
fn into_packed(self: Box<Self>) -> ErrorSansOriginObj;
|
|
||||||
/// Generic description of the error condition
|
|
||||||
fn description(&self) -> Cow<'_, str>;
|
|
||||||
/// Specific description of this particular error
|
|
||||||
fn message(&self) -> String;
|
|
||||||
/// Add an origin
|
|
||||||
fn bundle(self: Box<Self>, origin: &Pos) -> ProjectErrorObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type-erased [ErrorSansOrigin] implementor through the object-trait
|
|
||||||
/// [DynErrorSansOrigin]. This can be turned into a [ProjectErrorObj] with
|
|
||||||
/// [ErrorSansOriginObj::bundle].
|
|
||||||
pub type ErrorSansOriginObj = Box<dyn DynErrorSansOrigin>;
|
|
||||||
/// A generic project result without origin
|
|
||||||
pub type ResultSansOrigin<T> = Result<T, ErrorSansOriginObj>;
|
|
||||||
|
|
||||||
impl<T: ErrorSansOrigin + 'static> DynErrorSansOrigin for T {
|
|
||||||
fn description(&self) -> Cow<'_, str> { Cow::Borrowed(Self::DESCRIPTION) }
|
|
||||||
fn message(&self) -> String { (*self).message() }
|
|
||||||
fn as_any_ref(&self) -> &dyn Any { self }
|
|
||||||
fn into_packed(self: Box<Self>) -> ErrorSansOriginObj { (*self).pack() }
|
|
||||||
fn bundle(self: Box<Self>, origin: &Pos) -> ProjectErrorObj {
|
|
||||||
Arc::new(OriginBundle(origin.clone(), *self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Clone for ErrorSansOriginObj {
|
|
||||||
fn clone(&self) -> Self { clone_box(&**self) }
|
|
||||||
}
|
|
||||||
impl DynErrorSansOrigin for ErrorSansOriginObj {
|
|
||||||
fn description(&self) -> Cow<'_, str> { (**self).description() }
|
|
||||||
fn message(&self) -> String { (**self).message() }
|
|
||||||
fn as_any_ref(&self) -> &dyn Any { (**self).as_any_ref() }
|
|
||||||
fn into_packed(self: Box<Self>) -> ErrorSansOriginObj { *self }
|
|
||||||
fn bundle(self: Box<Self>, origin: &Pos) -> ProjectErrorObj { (*self).bundle(origin) }
|
|
||||||
}
|
|
||||||
impl fmt::Display for ErrorSansOriginObj {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
writeln!(f, "{}\nOrigin missing from error", self.message())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl fmt::Debug for ErrorSansOriginObj {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self}") }
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OriginBundle<T: ErrorSansOrigin>(Pos, T);
|
|
||||||
impl<T: ErrorSansOrigin> DynProjectError for OriginBundle<T> {
|
|
||||||
fn as_any_ref(&self) -> &dyn Any { self.1.as_any_ref() }
|
|
||||||
fn into_packed(self: Arc<Self>) -> ProjectErrorObj { self }
|
|
||||||
fn description(&self) -> Cow<'_, str> { self.1.description() }
|
|
||||||
fn message(&self) -> String { self.1.message() }
|
|
||||||
fn positions(&self) -> BoxedIter<ErrPos> {
|
|
||||||
box_once(ErrPos { position: self.0.clone(), message: None })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A collection for tracking fatal errors without halting. Participating
|
|
||||||
/// functions return [ProjectResult] even if they only ever construct [Ok]. When
|
|
||||||
/// they call other participating functions, instead of directly forwarding
|
|
||||||
/// errors with `?` they should prefer constructing a fallback value with
|
|
||||||
/// [Reporter::fallback]. If any error is added to a [Reporter] in a function,
|
|
||||||
/// the return value is valid but its meaning need not be related in any way to
|
|
||||||
/// the inputs.
|
|
||||||
///
|
|
||||||
/// Returning [Err] from a function that accepts `&mut Reporter` indicates not
|
|
||||||
/// that there was a fatal error but that it wasn't possible to construct a
|
|
||||||
/// fallback, so if it can, the caller should construct one.
|
|
||||||
pub struct Reporter(RefCell<Vec<ProjectErrorObj>>);
|
|
||||||
impl Reporter {
|
|
||||||
/// Create a new error reporter
|
|
||||||
pub fn new() -> Self { Self(RefCell::new(Vec::new())) }
|
|
||||||
/// Returns true if any errors were regorded. If this ever returns true, it
|
|
||||||
/// will always return true in the future.
|
|
||||||
pub fn failing(&self) -> bool { !self.0.borrow().is_empty() }
|
|
||||||
/// Report a fatal error
|
|
||||||
pub fn report(&self, error: ProjectErrorObj) {
|
|
||||||
match error.as_any_ref().downcast_ref::<MultiError>() {
|
|
||||||
None => self.0.borrow_mut().push(error),
|
|
||||||
Some(me) =>
|
|
||||||
for err in me.0.iter() {
|
|
||||||
self.report(err.clone())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Catch a fatal error, report it, and substitute the value
|
|
||||||
pub fn fallback<T>(&self, res: ProjectResult<T>, cb: impl FnOnce(ProjectErrorObj) -> T) -> T {
|
|
||||||
res.inspect_err(|e| self.report(e.clone())).unwrap_or_else(cb)
|
|
||||||
}
|
|
||||||
/// Take the errors out of the reporter
|
|
||||||
#[must_use]
|
|
||||||
pub fn into_errors(self) -> Option<Vec<ProjectErrorObj>> {
|
|
||||||
let v = self.0.into_inner();
|
|
||||||
if v.is_empty() { None } else { Some(v) }
|
|
||||||
}
|
|
||||||
/// Raise an error if the reporter contains any errors
|
|
||||||
pub fn bind(self) -> ProjectResult<()> {
|
|
||||||
match self.into_errors() {
|
|
||||||
None => Ok(()),
|
|
||||||
Some(v) if v.len() == 1 => Err(v.into_iter().next().unwrap()),
|
|
||||||
Some(v) => Err(MultiError(v).pack()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Reporter {
|
|
||||||
fn default() -> Self { Self::new() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unpack_into(err: impl DynProjectError, res: &mut Vec<ProjectErrorObj>) {
|
|
||||||
match err.as_any_ref().downcast_ref::<MultiError>() {
|
|
||||||
Some(multi) => multi.0.iter().for_each(|e| unpack_into(e.clone(), res)),
|
|
||||||
None => res.push(Arc::new(err).into_packed()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unpack_err(err: ProjectErrorObj) -> Vec<ProjectErrorObj> {
|
|
||||||
let mut out = Vec::new();
|
|
||||||
unpack_into(err, &mut out);
|
|
||||||
out
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pack_err<E: DynProjectError>(iter: impl IntoIterator<Item = E>) -> ProjectErrorObj {
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
iter.into_iter().for_each(|e| unpack_into(e, &mut errors));
|
|
||||||
if errors.len() == 1 { errors.into_iter().next().unwrap() } else { MultiError(errors).pack() }
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MultiError(Vec<ProjectErrorObj>);
|
|
||||||
impl ProjectError for MultiError {
|
|
||||||
const DESCRIPTION: &'static str = "Multiple errors occurred";
|
|
||||||
fn message(&self) -> String { format!("{} errors occurred", self.0.len()) }
|
|
||||||
fn positions(&self) -> impl IntoIterator<Item = ErrPos> + '_ {
|
|
||||||
self.0.iter().flat_map(|e| {
|
|
||||||
e.positions().map(|pos| {
|
|
||||||
let emsg = e.message();
|
|
||||||
let msg = match pos.message {
|
|
||||||
None => emsg,
|
|
||||||
Some(s) if s.is_empty() => emsg,
|
|
||||||
Some(pmsg) => format!("{emsg}: {pmsg}"),
|
|
||||||
};
|
|
||||||
ErrPos { position: pos.position, message: Some(Arc::new(msg)) }
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn err_to_api(err: ProjectErrorObj) -> api::OrcErr {
|
|
||||||
api::OrcErr {
|
|
||||||
description: intern(&*err.description()).marker(),
|
|
||||||
message: Arc::new(err.message()),
|
|
||||||
locations: err.positions().map(|e| e.to_api()).collect_vec(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RelayedError {
|
|
||||||
pub id: Option<api::ErrId>,
|
|
||||||
pub reqnot: ReqNot<api::ExtMsgSet>,
|
|
||||||
pub details: OnceLock<OrcError>,
|
|
||||||
}
|
|
||||||
impl RelayedError {
|
|
||||||
fn details(&self) -> &OrcError {
|
|
||||||
let Self { id, reqnot, details: data } = self;
|
|
||||||
data.get_or_init(clone!(reqnot; move || {
|
|
||||||
let id = id.expect("Either data or ID must be initialized");
|
|
||||||
let projerr = reqnot.request(api::GetErrorDetails(id));
|
|
||||||
OrcError {
|
|
||||||
description: deintern(projerr.description),
|
|
||||||
message: projerr.message,
|
|
||||||
positions: projerr.locations.iter().map(ErrPos::from_api).collect_vec(),
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl DynProjectError for RelayedError {
|
|
||||||
fn description(&self) -> Cow<'_, str> { Cow::Borrowed(self.details().description.as_str()) }
|
|
||||||
fn message(&self) -> String { self.details().message.to_string() }
|
|
||||||
fn as_any_ref(&self) -> &dyn std::any::Any { self }
|
|
||||||
fn into_packed(self: Arc<Self>) -> ProjectErrorObj { self }
|
|
||||||
fn positions(&self) -> BoxedIter<'_, ErrPos> {
|
|
||||||
Box::new(self.details().positions.iter().cloned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::hash::Hash;
|
use std::hash::{Hash, Hasher};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::thread::panicking;
|
use std::thread::panicking;
|
||||||
|
|
||||||
use async_once_cell::OnceCell;
|
use async_once_cell::OnceCell;
|
||||||
use derive_destructure::destructure;
|
use derive_destructure::destructure;
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use orchid_base::error::OrcErrv;
|
use orchid_base::{FmtCtx, FmtUnit, Format, OrcErrv, Pos, stash};
|
||||||
use orchid_base::format::{FmtCtx, FmtUnit, Format};
|
|
||||||
use orchid_base::location::Pos;
|
|
||||||
use orchid_base::stash::stash;
|
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::atom::ForeignAtom;
|
use crate::atom::ForeignAtom;
|
||||||
@@ -123,7 +120,7 @@ impl Expr {
|
|||||||
let kind = match details.kind {
|
let kind = match details.kind {
|
||||||
api::InspectedKind::Atom(a) =>
|
api::InspectedKind::Atom(a) =>
|
||||||
ExprKind::Atom(ForeignAtom::new(self.handle.clone(), a, pos.clone())),
|
ExprKind::Atom(ForeignAtom::new(self.handle.clone(), a, pos.clone())),
|
||||||
api::InspectedKind::Bottom(b) => ExprKind::Bottom(OrcErrv::from_api(&b).await),
|
api::InspectedKind::Bottom(b) => ExprKind::Bottom(OrcErrv::from_api(b).await),
|
||||||
api::InspectedKind::Opaque => ExprKind::Opaque,
|
api::InspectedKind::Opaque => ExprKind::Opaque,
|
||||||
};
|
};
|
||||||
ExprData { pos, kind }
|
ExprData { pos, kind }
|
||||||
@@ -156,6 +153,13 @@ impl Format for Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Eq for Expr {}
|
||||||
|
impl PartialEq for Expr {
|
||||||
|
fn eq(&self, other: &Self) -> bool { self.handle == other.handle }
|
||||||
|
}
|
||||||
|
impl Hash for Expr {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) { self.handle.hash(state); }
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ExprData {
|
pub struct ExprData {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use futures::{AsyncRead, AsyncWrite};
|
use futures::{AsyncRead, AsyncWrite};
|
||||||
@@ -8,5 +9,5 @@ pub struct ExtPort {
|
|||||||
pub input: Pin<Box<dyn AsyncRead>>,
|
pub input: Pin<Box<dyn AsyncRead>>,
|
||||||
pub output: Pin<Box<dyn AsyncWrite>>,
|
pub output: Pin<Box<dyn AsyncWrite>>,
|
||||||
pub log: Pin<Box<dyn AsyncWrite>>,
|
pub log: Pin<Box<dyn AsyncWrite>>,
|
||||||
pub spawn: Rc<dyn Fn(LocalBoxFuture<'static, ()>)>,
|
pub spawn: Rc<dyn Fn(Duration, LocalBoxFuture<'static, ()>)>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,7 @@ use futures::{AsyncWrite, FutureExt};
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_api_traits::Encode;
|
use orchid_api_traits::Encode;
|
||||||
use orchid_base::clone;
|
use orchid_base::{FmtCtx, FmtUnit, OrcRes, Pos, Sym, clone};
|
||||||
use orchid_base::error::OrcRes;
|
|
||||||
use orchid_base::format::{FmtCtx, FmtUnit};
|
|
||||||
use orchid_base::location::Pos;
|
|
||||||
use orchid_base::name::Sym;
|
|
||||||
use task_local::task_local;
|
use task_local::task_local;
|
||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
@@ -129,7 +125,7 @@ impl Atomic for Fun {
|
|||||||
impl OwnedAtom for Fun {
|
impl OwnedAtom for Fun {
|
||||||
type Refs = Vec<Expr>;
|
type Refs = Vec<Expr>;
|
||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
async fn call_ref(&self, arg: Expr) -> GExpr {
|
async fn call_ref(&self, arg: Expr) -> impl ToExpr {
|
||||||
let new_args = self.args.iter().cloned().chain([arg]).collect_vec();
|
let new_args = self.args.iter().cloned().chain([arg]).collect_vec();
|
||||||
if new_args.len() == self.record.argtyps.len() {
|
if new_args.len() == self.record.argtyps.len() {
|
||||||
(self.record.fun)(new_args).await.to_gen().await
|
(self.record.fun)(new_args).await.to_gen().await
|
||||||
@@ -137,7 +133,6 @@ impl OwnedAtom for Fun {
|
|||||||
new_atom(Self { args: new_args, record: self.record.clone(), path: self.path.clone() })
|
new_atom(Self { args: new_args, record: self.record.clone(), path: self.path.clone() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
|
|
||||||
async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs {
|
async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs {
|
||||||
self.path.to_api().encode(write).await.unwrap();
|
self.path.to_api().encode(write).await.unwrap();
|
||||||
self.args.clone()
|
self.args.clone()
|
||||||
@@ -175,7 +170,7 @@ impl Atomic for Lambda {
|
|||||||
impl OwnedAtom for Lambda {
|
impl OwnedAtom for Lambda {
|
||||||
type Refs = Never;
|
type Refs = Never;
|
||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
async fn call_ref(&self, arg: Expr) -> GExpr {
|
async fn call_ref(&self, arg: Expr) -> impl ToExpr {
|
||||||
let new_args = self.args.iter().cloned().chain([arg]).collect_vec();
|
let new_args = self.args.iter().cloned().chain([arg]).collect_vec();
|
||||||
if new_args.len() == self.record.argtyps.len() {
|
if new_args.len() == self.record.argtyps.len() {
|
||||||
(self.record.fun)(new_args).await.to_gen().await
|
(self.record.fun)(new_args).await.to_gen().await
|
||||||
@@ -183,14 +178,13 @@ impl OwnedAtom for Lambda {
|
|||||||
new_atom(Self { args: new_args, record: self.record.clone() })
|
new_atom(Self { args: new_args, record: self.record.clone() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod expr_func_derives {
|
mod expr_func_derives {
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use orchid_base::error::OrcRes;
|
use orchid_base::OrcRes;
|
||||||
|
|
||||||
use super::{ARGV, ExprFunc};
|
use super::{ARGV, ExprFunc};
|
||||||
use crate::conv::{ToExpr, TryFromExpr};
|
use crate::conv::{ToExpr, TryFromExpr};
|
||||||
@@ -224,7 +218,7 @@ mod expr_func_derives {
|
|||||||
expr_func_derive!(A, B, C);
|
expr_func_derive!(A, B, C);
|
||||||
expr_func_derive!(A, B, C, D);
|
expr_func_derive!(A, B, C, D);
|
||||||
expr_func_derive!(A, B, C, D, E);
|
expr_func_derive!(A, B, C, D, E);
|
||||||
// expr_func_derive!(A, B, C, D, E, F);
|
expr_func_derive!(A, B, C, D, E, F);
|
||||||
// expr_func_derive!(A, B, C, D, E, F, G);
|
// expr_func_derive!(A, B, C, D, E, F, G);
|
||||||
// expr_func_derive!(A, B, C, D, E, F, G, H);
|
// expr_func_derive!(A, B, C, D, E, F, G, H);
|
||||||
// expr_func_derive!(A, B, C, D, E, F, G, H, I);
|
// expr_func_derive!(A, B, C, D, E, F, G, H, I);
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::pin::{Pin, pin};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use futures::FutureExt;
|
use futures::{FutureExt, Stream, StreamExt, stream};
|
||||||
use orchid_base::error::{OrcErr, OrcErrv};
|
use orchid_base::{
|
||||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
|
FmtCtx, FmtUnit, Format, OrcErr, OrcErrv, Pos, Sym, Variants, match_mapping, tl_cache,
|
||||||
use orchid_base::location::Pos;
|
};
|
||||||
use orchid_base::name::Sym;
|
|
||||||
use orchid_base::{match_mapping, tl_cache};
|
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::atom::{AtomFactory, ToAtom};
|
use crate::atom::{AtomFactory, AtomicFeatures};
|
||||||
|
use crate::conv::{ToExpr, ToExprFuture};
|
||||||
use crate::entrypoint::request;
|
use crate::entrypoint::request;
|
||||||
use crate::expr::Expr;
|
use crate::expr::Expr;
|
||||||
|
use crate::system::sys_id;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GExpr {
|
pub struct GExpr {
|
||||||
@@ -39,7 +40,7 @@ impl GExpr {
|
|||||||
}
|
}
|
||||||
pub fn at(self, pos: Pos) -> Self { GExpr { pos, kind: self.kind } }
|
pub fn at(self, pos: Pos) -> Self { GExpr { pos, kind: self.kind } }
|
||||||
pub async fn create(self) -> Expr {
|
pub async fn create(self) -> Expr {
|
||||||
Expr::deserialize(request(api::Create(self.serialize().await)).await).await
|
Expr::deserialize(request(api::Create(sys_id(), self.serialize().await)).await).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Format for GExpr {
|
impl Format for GExpr {
|
||||||
@@ -55,6 +56,7 @@ pub enum GExprKind {
|
|||||||
Arg(u64),
|
Arg(u64),
|
||||||
Seq(Box<GExpr>, Box<GExpr>),
|
Seq(Box<GExpr>, Box<GExpr>),
|
||||||
Const(Sym),
|
Const(Sym),
|
||||||
|
#[allow(private_interfaces)]
|
||||||
NewAtom(AtomFactory),
|
NewAtom(AtomFactory),
|
||||||
Slot(Expr),
|
Slot(Expr),
|
||||||
Bottom(OrcErrv),
|
Bottom(OrcErrv),
|
||||||
@@ -105,27 +107,105 @@ impl Format for GExprKind {
|
|||||||
|
|
||||||
fn inherit(kind: GExprKind) -> GExpr { GExpr { pos: Pos::Inherit, kind } }
|
fn inherit(kind: GExprKind) -> GExpr { GExpr { pos: Pos::Inherit, kind } }
|
||||||
|
|
||||||
pub fn sym_ref(path: Sym) -> GExpr { inherit(GExprKind::Const(path)) }
|
impl ToExpr for Sym {
|
||||||
/// Creates an expression from a new atom that we own.
|
async fn to_expr(self) -> Expr
|
||||||
pub fn new_atom<A: ToAtom>(atom: A) -> GExpr { inherit(GExprKind::NewAtom(atom.to_atom_factory())) }
|
where Self: Sized {
|
||||||
|
self.to_gen().await.create().await
|
||||||
pub fn seq(deps: impl IntoIterator<Item = GExpr>, val: GExpr) -> GExpr {
|
|
||||||
fn recur(mut ops: impl Iterator<Item = GExpr>) -> Option<GExpr> {
|
|
||||||
let op = ops.next()?;
|
|
||||||
Some(match recur(ops) {
|
|
||||||
None => op,
|
|
||||||
Some(rec) => inherit(GExprKind::Seq(Box::new(op), Box::new(rec))),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
recur(deps.into_iter().chain([val])).expect("Empty list provided to seq!")
|
async fn to_gen(self) -> GExpr { inherit(GExprKind::Const(self)) }
|
||||||
|
}
|
||||||
|
/// Creates an expression from a new atom that we own.
|
||||||
|
pub fn new_atom<A: AtomicFeatures>(atom: A) -> GExpr { inherit(GExprKind::NewAtom(atom.factory())) }
|
||||||
|
|
||||||
|
pub fn seq(
|
||||||
|
deps: impl IntoGExprStream,
|
||||||
|
val: impl ToExpr,
|
||||||
|
) -> ToExprFuture<impl Future<Output = GExpr>> {
|
||||||
|
ToExprFuture(async {
|
||||||
|
async fn recur(mut ops: Pin<&mut impl Stream<Item = GExpr>>) -> Option<GExpr> {
|
||||||
|
let op = ops.next().await?.to_gen().await;
|
||||||
|
Some(match recur(ops).boxed_local().await {
|
||||||
|
None => op,
|
||||||
|
Some(rec) => inherit(GExprKind::Seq(Box::new(op), Box::new(rec))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
recur(pin!(deps.into_gexpr_stream().chain(stream::iter([val.to_gen().await]))))
|
||||||
|
.await
|
||||||
|
.expect("Empty list provided to seq!")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arg(n: u64) -> GExpr { inherit(GExprKind::Arg(n)) }
|
pub fn arg(n: u64) -> GExpr { inherit(GExprKind::Arg(n)) }
|
||||||
|
|
||||||
pub fn lambda(n: u64, [b]: [GExpr; 1]) -> GExpr { inherit(GExprKind::Lambda(n, Box::new(b))) }
|
pub fn lam<const N: u64>(b: impl ToExpr) -> ToExprFuture<impl Future<Output = GExpr>> {
|
||||||
|
dyn_lambda(N, b)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn call(f: GExpr, argv: impl IntoIterator<Item = GExpr>) -> GExpr {
|
pub fn dyn_lambda(n: u64, b: impl ToExpr) -> ToExprFuture<impl Future<Output = GExpr>> {
|
||||||
(argv.into_iter()).fold(f, |f, x| inherit(GExprKind::Call(Box::new(f), Box::new(x))))
|
ToExprFuture(async move { inherit(GExprKind::Lambda(n, Box::new(b.to_gen().await))) })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IntoGExprStream {
|
||||||
|
fn into_gexpr_stream(self) -> impl Stream<Item = GExpr>;
|
||||||
|
}
|
||||||
|
impl<T: ToExpr> IntoGExprStream for T {
|
||||||
|
fn into_gexpr_stream(self) -> impl Stream<Item = GExpr> { (self,).into_gexpr_stream() }
|
||||||
|
}
|
||||||
|
impl IntoGExprStream for Vec<GExpr> {
|
||||||
|
fn into_gexpr_stream(self) -> impl Stream<Item = GExpr> { stream::iter(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
mod tuple_impls {
|
||||||
|
use futures::{Stream, StreamExt, stream};
|
||||||
|
|
||||||
|
use super::IntoGExprStream;
|
||||||
|
use crate::conv::ToExpr;
|
||||||
|
use crate::gen_expr::GExpr;
|
||||||
|
|
||||||
|
macro_rules! tuple_impl {
|
||||||
|
($($T:ident)*) => {
|
||||||
|
pastey::paste!{
|
||||||
|
impl<$($T: ToExpr),*> IntoGExprStream for ($($T,)*) {
|
||||||
|
fn into_gexpr_stream(self) -> impl Stream<Item = GExpr> {
|
||||||
|
let ($([< $T:snake >],)*) = self;
|
||||||
|
stream::once(async { stream::iter([$([< $T:snake >].to_gen().await,)*]) }).flatten()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
tuple_impl!();
|
||||||
|
tuple_impl!(A);
|
||||||
|
tuple_impl!(A B);
|
||||||
|
tuple_impl!(A B C);
|
||||||
|
tuple_impl!(A B C D);
|
||||||
|
tuple_impl!(A B C D E);
|
||||||
|
tuple_impl!(A B C D E F);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call(
|
||||||
|
f: impl ToExpr,
|
||||||
|
argv: impl IntoGExprStream,
|
||||||
|
) -> ToExprFuture<impl Future<Output = GExpr>> {
|
||||||
|
ToExprFuture(async {
|
||||||
|
(argv.into_gexpr_stream())
|
||||||
|
.fold(f.to_gen().await, async |f, x| inherit(GExprKind::Call(Box::new(f), Box::new(x))))
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call_v(
|
||||||
|
f: impl ToExpr,
|
||||||
|
argv: impl IntoIterator<Item: ToExpr>,
|
||||||
|
) -> ToExprFuture<impl Future<Output = GExpr>> {
|
||||||
|
ToExprFuture(async {
|
||||||
|
stream::iter(argv)
|
||||||
|
.fold(f.to_gen().await, async |f, x| {
|
||||||
|
inherit(GExprKind::Call(Box::new(f), Box::new(x.to_gen().await)))
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bot(ev: impl IntoIterator<Item = OrcErr>) -> GExpr {
|
pub fn bot(ev: impl IntoIterator<Item = OrcErr>) -> GExpr {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use futures::future::{LocalBoxFuture, join_all, ready};
|
use futures::future::{LocalBoxFuture, join_all, ready};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::interner::local_interner::{Int, StrBranch, StrvBranch};
|
use orchid_base::local_interner::{Int, StrBranch, StrvBranch};
|
||||||
use orchid_base::interner::{IStr, IStrv, InternerSrv};
|
use orchid_base::{IStr, IStrv, InternerSrv};
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::entrypoint::{MUTE_REPLY, request};
|
use crate::entrypoint::{MUTE_REPLY, request};
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ use std::ops::RangeInclusive;
|
|||||||
|
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use orchid_base::error::{OrcErrv, OrcRes, mk_errv};
|
use orchid_base::{IStr, OrcErrv, OrcRes, Pos, SrcRange, Sym, is, mk_errv};
|
||||||
use orchid_base::interner::{IStr, is};
|
|
||||||
use orchid_base::location::{Pos, SrcRange};
|
|
||||||
use orchid_base::name::Sym;
|
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::entrypoint::request;
|
use crate::entrypoint::request;
|
||||||
@@ -50,7 +47,7 @@ impl<'a> LexContext<'a> {
|
|||||||
}
|
}
|
||||||
pub fn src(&self) -> &Sym { &self.src }
|
pub fn src(&self) -> &Sym { &self.src }
|
||||||
/// This function returns [PTokTree] because it can never return
|
/// This function returns [PTokTree] because it can never return
|
||||||
/// [orchid_base::tree::Token::NewExpr]. You can use
|
/// [orchid_base::Token::NewExpr]. You can use
|
||||||
/// [crate::parser::p_tree2gen] to convert this to [crate::tree::GenTokTree]
|
/// [crate::parser::p_tree2gen] to convert this to [crate::tree::GenTokTree]
|
||||||
/// for embedding in the return value.
|
/// for embedding in the return value.
|
||||||
pub async fn recurse(&self, tail: &'a str) -> OrcRes<(&'a str, PTokTree)> {
|
pub async fn recurse(&self, tail: &'a str) -> OrcRes<(&'a str, PTokTree)> {
|
||||||
@@ -58,7 +55,7 @@ impl<'a> LexContext<'a> {
|
|||||||
let Some(lx) = request(api::SubLex { pos: start, id: self.id }).await else {
|
let Some(lx) = request(api::SubLex { pos: start, id: self.id }).await else {
|
||||||
return Err(err_cascade().await);
|
return Err(err_cascade().await);
|
||||||
};
|
};
|
||||||
let tree = PTokTree::from_api(&lx.tree, &mut { self.exprs }, &mut (), &self.src).await;
|
let tree = PTokTree::from_api(lx.tree, &mut { self.exprs }, &mut (), &self.src).await;
|
||||||
Ok((&self.text[lx.pos as usize..], tree))
|
Ok((&self.text[lx.pos as usize..], tree))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
|
#![allow(refining_impl_trait, reason = "Has various false-positives around lints")]
|
||||||
use orchid_api as api;
|
use orchid_api as api;
|
||||||
|
|
||||||
pub mod atom;
|
mod atom;
|
||||||
pub mod atom_owned;
|
pub use atom::*;
|
||||||
pub mod atom_thin;
|
mod atom_owned;
|
||||||
|
pub use atom_owned::*;
|
||||||
|
mod atom_thin;
|
||||||
|
pub use atom_thin::*;
|
||||||
pub mod binary;
|
pub mod binary;
|
||||||
pub mod conv;
|
pub mod conv;
|
||||||
pub mod coroutine_exec;
|
pub mod coroutine_exec;
|
||||||
|
|||||||
@@ -5,8 +5,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use orchid_base::interner::is;
|
use orchid_base::{LogWriter, Logger, is};
|
||||||
use orchid_base::logging::{LogWriter, Logger};
|
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::entrypoint::notify;
|
use crate::entrypoint::notify;
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
use std::pin::pin;
|
|
||||||
|
|
||||||
use async_once_cell::OnceCell;
|
|
||||||
use futures::lock::Mutex;
|
|
||||||
use orchid_base::msg::{recv_msg, send_msg};
|
|
||||||
|
|
||||||
pub async fn send_parent_msg(msg: &[u8]) -> io::Result<()> {
|
|
||||||
let stdout_lk = STDOUT.get_or_init(async { Mutex::new(io::stdout()) }).await;
|
|
||||||
let mut stdout_g = stdout_lk.lock().await;
|
|
||||||
send_msg(pin!(&mut *stdout_g), msg).await
|
|
||||||
}
|
|
||||||
pub async fn recv_parent_msg() -> io::Result<Vec<u8>> { recv_msg(pin!(io::stdin())).await }
|
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::num::NonZero;
|
||||||
|
|
||||||
use async_fn_stream::stream;
|
use async_fn_stream::stream;
|
||||||
use futures::future::{LocalBoxFuture, join_all};
|
use futures::future::{LocalBoxFuture, join_all};
|
||||||
use futures::{FutureExt, Stream, StreamExt};
|
use futures::{FutureExt, Stream, StreamExt};
|
||||||
|
use hashbrown::HashMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_base::error::{OrcErrv, OrcRes};
|
use orchid_base::{
|
||||||
use orchid_base::id_store::IdStore;
|
Comment, IStr, OrcErrv, OrcRes, Snippet, SrcRange, Sym, TokTree, Token, match_mapping,
|
||||||
use orchid_base::interner::IStr;
|
ttv_into_api,
|
||||||
use orchid_base::location::SrcRange;
|
};
|
||||||
use orchid_base::match_mapping;
|
|
||||||
use orchid_base::name::Sym;
|
|
||||||
use orchid_base::parse::{Comment, Snippet};
|
|
||||||
use orchid_base::tree::{TokTree, Token, ttv_into_api};
|
|
||||||
use task_local::task_local;
|
use task_local::task_local;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
@@ -92,11 +91,13 @@ impl<'a> ParsCtx<'a> {
|
|||||||
type BoxConstCallback = Box<dyn FnOnce(ConstCtx) -> LocalBoxFuture<'static, GExpr>>;
|
type BoxConstCallback = Box<dyn FnOnce(ConstCtx) -> LocalBoxFuture<'static, GExpr>>;
|
||||||
|
|
||||||
task_local! {
|
task_local! {
|
||||||
static PARSED_CONST_CTX: IdStore<BoxConstCallback>
|
static CONST_TBL: RefCell<HashMap<NonZero<u64>, BoxConstCallback>>;
|
||||||
|
static NEXT_CONST_ID: RefCell<NonZero<u64>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn with_parsed_const_ctx<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> {
|
pub(crate) fn with_parsed_const_ctx<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> {
|
||||||
Box::pin(PARSED_CONST_CTX.scope(IdStore::default(), fut))
|
let id_cell = RefCell::new(NonZero::new(1).unwrap());
|
||||||
|
Box::pin(CONST_TBL.scope(RefCell::default(), NEXT_CONST_ID.scope(id_cell, fut)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ParsedLine {
|
pub struct ParsedLine {
|
||||||
@@ -139,9 +140,11 @@ impl ParsedLine {
|
|||||||
name: mem.name.to_api(),
|
name: mem.name.to_api(),
|
||||||
exported: mem.exported,
|
exported: mem.exported,
|
||||||
kind: match mem.kind {
|
kind: match mem.kind {
|
||||||
ParsedMemKind::Const(cb) => api::ParsedMemberKind::Constant(api::ParsedConstId(
|
ParsedMemKind::Const(cb) => {
|
||||||
PARSED_CONST_CTX.with(|consts| consts.add(cb).id()),
|
let id = NEXT_CONST_ID.with(|c| c.replace_with(|id| id.checked_add(1).unwrap()));
|
||||||
)),
|
CONST_TBL.with(|consts| consts.borrow_mut().insert(id, cb));
|
||||||
|
api::ParsedMemberKind::Constant(api::ParsedConstId(id))
|
||||||
|
},
|
||||||
ParsedMemKind::Mod { lines, use_prelude } => api::ParsedMemberKind::Module {
|
ParsedMemKind::Mod { lines, use_prelude } => api::ParsedMemberKind::Module {
|
||||||
lines: linev_into_api(lines).boxed_local().await,
|
lines: linev_into_api(lines).boxed_local().await,
|
||||||
use_prelude,
|
use_prelude,
|
||||||
@@ -192,7 +195,7 @@ impl ConstCtx {
|
|||||||
let resolve_names = api::ResolveNames { constid: self.constid, sys: sys_id(), names };
|
let resolve_names = api::ResolveNames { constid: self.constid, sys: sys_id(), names };
|
||||||
for name_opt in request(resolve_names).await {
|
for name_opt in request(resolve_names).await {
|
||||||
cx.emit(match name_opt {
|
cx.emit(match name_opt {
|
||||||
Err(e) => Err(OrcErrv::from_api(&e).await),
|
Err(e) => Err(OrcErrv::from_api(e).await),
|
||||||
Ok(name) => Ok(Sym::from_api(name).await),
|
Ok(name) => Ok(Sym::from_api(name).await),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@@ -205,7 +208,7 @@ impl ConstCtx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_const(id: api::ParsedConstId) -> GExpr {
|
pub(crate) async fn get_const(id: api::ParsedConstId) -> GExpr {
|
||||||
let cb = PARSED_CONST_CTX
|
let cb = CONST_TBL
|
||||||
.with(|ent| ent.get(id.0).expect("Bad ID or double read of parsed const").remove());
|
.with(|ent| ent.borrow_mut().remove(&id.0).expect("Bad ID or double read of parsed const"));
|
||||||
cb(ConstCtx { constid: id }).await
|
cb(ConstCtx { constid: id }).await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ use futures::future::LocalBoxFuture;
|
|||||||
use futures::lock::Mutex;
|
use futures::lock::Mutex;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use memo_map::MemoMap;
|
use memo_map::MemoMap;
|
||||||
use orchid_base::interner::{IStr, es, iv};
|
use orchid_base::{IStr, NameLike, VPath, es, iv};
|
||||||
use orchid_base::name::{NameLike, VPath};
|
|
||||||
use task_local::task_local;
|
use task_local::task_local;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::num::NonZero;
|
||||||
|
|
||||||
use orchid_api_derive::{Coding, Hierarchy};
|
use orchid_api_derive::{Coding, Hierarchy};
|
||||||
use orchid_api_traits::Request;
|
use orchid_api_traits::Request;
|
||||||
|
|
||||||
@@ -5,13 +7,23 @@ use crate::atom::AtomMethod;
|
|||||||
|
|
||||||
/// Represents [std::io::ErrorKind] values that are produced while operating on
|
/// Represents [std::io::ErrorKind] values that are produced while operating on
|
||||||
/// already-opened files
|
/// already-opened files
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
pub enum IoErrorKind {
|
pub enum IoErrorKind {
|
||||||
BrokenPipe,
|
BrokenPipe,
|
||||||
UnexpectedEof,
|
UnexpectedEof,
|
||||||
ConnectionAborted,
|
ConnectionAborted,
|
||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
impl IoErrorKind {
|
||||||
|
pub fn message(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
IoErrorKind::Other => "Failed to read from stream",
|
||||||
|
IoErrorKind::BrokenPipe => "Broken pipe",
|
||||||
|
IoErrorKind::UnexpectedEof => "Unexpected EOF",
|
||||||
|
IoErrorKind::ConnectionAborted => "Connection aborted",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents [std::io::Error] values that are produced while operating on
|
/// Represents [std::io::Error] values that are produced while operating on
|
||||||
/// already-opened files
|
/// already-opened files
|
||||||
@@ -21,10 +33,17 @@ pub struct IoError {
|
|||||||
pub kind: IoErrorKind,
|
pub kind: IoErrorKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read at most the specified number of bytes, but at least one byte, from a
|
#[derive(Clone, Debug, Coding)]
|
||||||
/// stream. If the returned vector is empty, the stream has reached its end.
|
pub enum ReadLimit {
|
||||||
|
End,
|
||||||
|
Delimiter(u8),
|
||||||
|
Length(NonZero<u64>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read all available data from a stream. If the returned vector is empty, the
|
||||||
|
/// stream has reached its end.
|
||||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
pub struct ReadReq(pub u64);
|
pub struct ReadReq(pub ReadLimit);
|
||||||
impl Request for ReadReq {
|
impl Request for ReadReq {
|
||||||
type Response = Result<Vec<u8>, IoError>;
|
type Response = Result<Vec<u8>, IoError>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
use std::any::{Any, TypeId};
|
use std::any::{Any, TypeId, type_name};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::num::NonZero;
|
use std::num::NonZero;
|
||||||
use std::pin::Pin;
|
|
||||||
|
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use orchid_api_traits::{Coding, Decode, Encode, Request};
|
use orchid_api_traits::{Coding, Decode, Encode, Request};
|
||||||
use orchid_base::boxed_iter::BoxedIter;
|
use orchid_base::{BoxedIter, Receipt, ReqHandle, ReqReader, ReqReaderExt, Sym};
|
||||||
use orchid_base::name::Sym;
|
|
||||||
use orchid_base::reqnot::{Receipt, ReqHandle, ReqReader, ReqReaderExt};
|
|
||||||
use task_local::task_local;
|
use task_local::task_local;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId, AtomicFeatures, ForeignAtom, TAtom, get_info};
|
use crate::atom::{AtomOps, AtomTypeId, Atomic, AtomicFeatures};
|
||||||
use crate::coroutine_exec::Replier;
|
use crate::coroutine_exec::Replier;
|
||||||
use crate::entrypoint::request;
|
use crate::entrypoint::request;
|
||||||
use crate::func_atom::{Fun, Lambda};
|
use crate::func_atom::{Fun, Lambda};
|
||||||
@@ -22,96 +19,95 @@ use crate::parser::ParserObj;
|
|||||||
use crate::system_ctor::{CtedObj, SystemCtor};
|
use crate::system_ctor::{CtedObj, SystemCtor};
|
||||||
use crate::tree::GenMember;
|
use crate::tree::GenMember;
|
||||||
|
|
||||||
/// System as consumed by foreign code
|
/// Description of a system. This is a distinct object because [SystemCtor]
|
||||||
pub trait SystemCard: Debug + Default + Send + Sync + 'static {
|
/// isn't [Default]
|
||||||
|
pub trait SystemCard: Debug + Default + 'static {
|
||||||
type Ctor: SystemCtor;
|
type Ctor: SystemCtor;
|
||||||
type Req: Coding;
|
type Req: Coding;
|
||||||
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>>;
|
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomOps>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DynSystemCard: Send + Sync + Any + 'static {
|
/// Type-erased [SystemCard]
|
||||||
|
pub trait DynSystemCard: Any + 'static {
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
/// Atoms explicitly defined by the system card. Do not rely on this for
|
/// Atoms explicitly defined by the system card. Do not rely on this for
|
||||||
/// querying atoms as it doesn't include the general atom types
|
/// querying atoms as it doesn't include the general atom types
|
||||||
fn atoms(&'_ self) -> BoxedIter<'_, Option<Box<dyn AtomDynfo>>>;
|
fn atoms(&'_ self) -> BoxedIter<'_, Option<Box<dyn AtomOps>>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: DynSystemCard + ?Sized> DynSystemCardExt for T {}
|
||||||
|
pub(crate) trait DynSystemCardExt: DynSystemCard {
|
||||||
|
fn ops_by_tid(&self, tid: TypeId) -> Option<(AtomTypeId, Box<dyn AtomOps>)> {
|
||||||
|
(self.atoms().enumerate().map(|(i, o)| (NonZero::new(i as u32 + 1).unwrap(), o)))
|
||||||
|
.chain(general_atoms().enumerate().map(|(i, o)| (NonZero::new(!(i as u32)).unwrap(), o)))
|
||||||
|
.filter_map(|(i, o)| o.map(|a| (AtomTypeId(i), a)))
|
||||||
|
.find(|ent| ent.1.tid() == tid)
|
||||||
|
}
|
||||||
|
fn ops_by_atid(&self, tid: AtomTypeId) -> Option<Box<dyn AtomOps>> {
|
||||||
|
if (u32::from(tid.0) >> (u32::BITS - 1)) & 1 == 1 {
|
||||||
|
general_atoms().nth(!u32::from(tid.0) as usize).unwrap()
|
||||||
|
} else {
|
||||||
|
self.atoms().nth(u32::from(tid.0) as usize - 1).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn ops<A: Atomic>(&self) -> (AtomTypeId, Box<dyn AtomOps>) {
|
||||||
|
self
|
||||||
|
.ops_by_tid(TypeId::of::<A>())
|
||||||
|
.unwrap_or_else(|| panic!("{} is not an atom in {}", type_name::<A>(), self.name()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Atoms supported by this package which may appear in all extensions.
|
/// Atoms supported by this package which may appear in all extensions.
|
||||||
/// The indices of these are bitwise negated, such that the MSB of an atom index
|
/// The indices of these are bitwise negated, such that the MSB of an atom index
|
||||||
/// marks whether it belongs to this package (0) or the importer (1)
|
/// marks whether it belongs to this package (0) or the importer (1)
|
||||||
fn general_atoms() -> impl Iterator<Item = Option<Box<dyn AtomDynfo>>> {
|
fn general_atoms() -> impl Iterator<Item = Option<Box<dyn AtomOps>>> {
|
||||||
[Some(Fun::dynfo()), Some(Lambda::dynfo()), Some(Replier::dynfo())].into_iter()
|
[Some(Fun::ops()), Some(Lambda::ops()), Some(Replier::ops())].into_iter()
|
||||||
}
|
|
||||||
|
|
||||||
pub fn atom_info_for(
|
|
||||||
sys: &(impl DynSystemCard + ?Sized),
|
|
||||||
tid: TypeId,
|
|
||||||
) -> Option<(AtomTypeId, Box<dyn AtomDynfo>)> {
|
|
||||||
(sys.atoms().enumerate().map(|(i, o)| (NonZero::new(i as u32 + 1).unwrap(), o)))
|
|
||||||
.chain(general_atoms().enumerate().map(|(i, o)| (NonZero::new(!(i as u32)).unwrap(), o)))
|
|
||||||
.filter_map(|(i, o)| o.map(|a| (AtomTypeId(i), a)))
|
|
||||||
.find(|ent| ent.1.tid() == tid)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn atom_by_idx(
|
|
||||||
sys: &(impl DynSystemCard + ?Sized),
|
|
||||||
tid: AtomTypeId,
|
|
||||||
) -> Option<Box<dyn AtomDynfo>> {
|
|
||||||
if (u32::from(tid.0) >> (u32::BITS - 1)) & 1 == 1 {
|
|
||||||
general_atoms().nth(!u32::from(tid.0) as usize).unwrap()
|
|
||||||
} else {
|
|
||||||
sys.atoms().nth(u32::from(tid.0) as usize - 1).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn resolv_atom(
|
|
||||||
sys: &(impl DynSystemCard + ?Sized),
|
|
||||||
atom: &api::Atom,
|
|
||||||
) -> Box<dyn AtomDynfo> {
|
|
||||||
let tid = AtomTypeId::decode(Pin::new(&mut &atom.data.0[..])).await.unwrap();
|
|
||||||
atom_by_idx(sys, tid).expect("Value of nonexistent type found")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: SystemCard> DynSystemCard for T {
|
impl<T: SystemCard> DynSystemCard for T {
|
||||||
fn name(&self) -> &'static str { T::Ctor::NAME }
|
fn name(&self) -> &'static str { T::Ctor::NAME }
|
||||||
fn atoms(&'_ self) -> BoxedIter<'_, Option<Box<dyn AtomDynfo>>> {
|
fn atoms(&'_ self) -> BoxedIter<'_, Option<Box<dyn AtomOps>>> {
|
||||||
Box::new(Self::atoms().into_iter())
|
Box::new(Self::atoms().into_iter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// System as defined by author
|
/// System as defined by author
|
||||||
pub trait System: Send + Sync + SystemCard + 'static {
|
pub trait System: SystemCard + 'static {
|
||||||
fn prelude() -> impl Future<Output = Vec<Sym>>;
|
fn prelude(&self) -> impl Future<Output = Vec<Sym>>;
|
||||||
fn env() -> impl Future<Output = Vec<GenMember>>;
|
fn env(&self) -> impl Future<Output = Vec<GenMember>>;
|
||||||
fn lexers() -> Vec<LexerObj>;
|
fn lexers(&self) -> Vec<LexerObj>;
|
||||||
fn parsers() -> Vec<ParserObj>;
|
fn parsers(&self) -> Vec<ParserObj>;
|
||||||
fn request<'a>(
|
fn request<'a>(
|
||||||
|
&self,
|
||||||
hand: Box<dyn ReqHandle<'a> + 'a>,
|
hand: Box<dyn ReqHandle<'a> + 'a>,
|
||||||
req: Self::Req,
|
req: Self::Req,
|
||||||
) -> impl Future<Output = Receipt<'a>>;
|
) -> impl Future<Output = Receipt<'a>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DynSystem: Send + Sync + DynSystemCard + 'static {
|
pub trait DynSystem: DynSystemCard + 'static {
|
||||||
fn dyn_prelude(&self) -> LocalBoxFuture<'_, Vec<Sym>>;
|
fn dyn_prelude(&self) -> LocalBoxFuture<'_, Vec<Sym>>;
|
||||||
fn dyn_env(&self) -> LocalBoxFuture<'_, Vec<GenMember>>;
|
fn dyn_env(&self) -> LocalBoxFuture<'_, Vec<GenMember>>;
|
||||||
fn dyn_lexers(&self) -> Vec<LexerObj>;
|
fn dyn_lexers(&self) -> Vec<LexerObj>;
|
||||||
fn dyn_parsers(&self) -> Vec<ParserObj>;
|
fn dyn_parsers(&self) -> Vec<ParserObj>;
|
||||||
fn dyn_request<'a>(&self, hand: Box<dyn ReqReader<'a> + 'a>) -> LocalBoxFuture<'a, Receipt<'a>>;
|
fn dyn_request<'a, 'b: 'a>(
|
||||||
|
&'a self,
|
||||||
|
hand: Box<dyn ReqReader<'b> + 'b>,
|
||||||
|
) -> LocalBoxFuture<'a, Receipt<'b>>;
|
||||||
fn card(&self) -> &dyn DynSystemCard;
|
fn card(&self) -> &dyn DynSystemCard;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: System> DynSystem for T {
|
impl<T: System> DynSystem for T {
|
||||||
fn dyn_prelude(&self) -> LocalBoxFuture<'_, Vec<Sym>> { Box::pin(Self::prelude()) }
|
fn dyn_prelude(&self) -> LocalBoxFuture<'_, Vec<Sym>> { Box::pin(self.prelude()) }
|
||||||
fn dyn_env(&self) -> LocalBoxFuture<'_, Vec<GenMember>> { Self::env().boxed_local() }
|
fn dyn_env(&self) -> LocalBoxFuture<'_, Vec<GenMember>> { self.env().boxed_local() }
|
||||||
fn dyn_lexers(&self) -> Vec<LexerObj> { Self::lexers() }
|
fn dyn_lexers(&self) -> Vec<LexerObj> { self.lexers() }
|
||||||
fn dyn_parsers(&self) -> Vec<ParserObj> { Self::parsers() }
|
fn dyn_parsers(&self) -> Vec<ParserObj> { self.parsers() }
|
||||||
fn dyn_request<'a>(
|
fn dyn_request<'a, 'b: 'a>(
|
||||||
&self,
|
&'a self,
|
||||||
mut hand: Box<dyn ReqReader<'a> + 'a>,
|
mut hand: Box<dyn ReqReader<'b> + 'b>,
|
||||||
) -> LocalBoxFuture<'a, Receipt<'a>> {
|
) -> LocalBoxFuture<'a, Receipt<'b>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let value = hand.read_req::<<Self as SystemCard>::Req>().await.unwrap();
|
let value = hand.read_req::<<Self as SystemCard>::Req>().await.unwrap();
|
||||||
Self::request(hand.finish().await, value).await
|
self.request(hand.finish().await, value).await
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn card(&self) -> &dyn DynSystemCard { self }
|
fn card(&self) -> &dyn DynSystemCard { self }
|
||||||
@@ -130,30 +126,6 @@ pub(crate) async fn with_sys<F: Future>(sys: SysCtx, fut: F) -> F::Output {
|
|||||||
|
|
||||||
pub fn sys_id() -> api::SysId { SYS_CTX.with(|cx| cx.0) }
|
pub fn sys_id() -> api::SysId { SYS_CTX.with(|cx| cx.0) }
|
||||||
pub fn cted() -> CtedObj { SYS_CTX.with(|cx| cx.1.clone()) }
|
pub fn cted() -> CtedObj { SYS_CTX.with(|cx| cx.1.clone()) }
|
||||||
pub async fn downcast_atom<A>(foreign: ForeignAtom) -> Result<TAtom<A>, ForeignAtom>
|
|
||||||
where A: AtomicFeatures {
|
|
||||||
let mut data = &foreign.atom.data.0[..];
|
|
||||||
let value = AtomTypeId::decode_slice(&mut data);
|
|
||||||
let cted = cted();
|
|
||||||
let own_inst = cted.inst();
|
|
||||||
let owner = if sys_id() == foreign.atom.owner {
|
|
||||||
own_inst.card()
|
|
||||||
} else {
|
|
||||||
cted.deps().find(|s| s.id() == foreign.atom.owner).ok_or_else(|| foreign.clone())?.get_card()
|
|
||||||
};
|
|
||||||
if owner.atoms().flatten().all(|dynfo| dynfo.tid() != TypeId::of::<A>()) {
|
|
||||||
return Err(foreign);
|
|
||||||
}
|
|
||||||
let (typ_id, dynfo) = get_info::<A>(owner);
|
|
||||||
if value != typ_id {
|
|
||||||
return Err(foreign);
|
|
||||||
}
|
|
||||||
let val = dynfo.decode(AtomCtx(data, foreign.atom.drop)).await;
|
|
||||||
let Ok(value) = val.downcast::<A::Data>() else {
|
|
||||||
panic!("decode of {} returned wrong type.", dynfo.name());
|
|
||||||
};
|
|
||||||
Ok(TAtom { value: *value, untyped: foreign })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make a global request to a system that supports this request type. The
|
/// Make a global request to a system that supports this request type. The
|
||||||
/// target system must either be the system in which this function is called, or
|
/// target system must either be the system in which this function is called, or
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::any::Any;
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use orchid_base::boxed_iter::{BoxedIter, box_empty, box_once};
|
use orchid_base::{BoxedIter, box_empty, box_once};
|
||||||
use ordered_float::NotNan;
|
use ordered_float::NotNan;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
@@ -17,7 +17,7 @@ pub struct Cted<Ctor: SystemCtor + ?Sized> {
|
|||||||
impl<C: SystemCtor + ?Sized> Clone for Cted<C> {
|
impl<C: SystemCtor + ?Sized> Clone for Cted<C> {
|
||||||
fn clone(&self) -> Self { Self { deps: self.deps.clone(), inst: self.inst.clone() } }
|
fn clone(&self) -> Self { Self { deps: self.deps.clone(), inst: self.inst.clone() } }
|
||||||
}
|
}
|
||||||
pub trait DynCted: Debug + Send + Sync + 'static {
|
pub trait DynCted: Debug + 'static {
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
fn deps<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)>;
|
fn deps<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)>;
|
||||||
fn inst(&self) -> Arc<dyn DynSystem>;
|
fn inst(&self) -> Arc<dyn DynSystem>;
|
||||||
@@ -29,7 +29,7 @@ impl<C: SystemCtor + ?Sized> DynCted for Cted<C> {
|
|||||||
}
|
}
|
||||||
pub type CtedObj = Arc<dyn DynCted>;
|
pub type CtedObj = Arc<dyn DynCted>;
|
||||||
|
|
||||||
pub trait DepSat: Debug + Clone + Send + Sync + 'static {
|
pub trait DepSat: Debug + Clone + 'static {
|
||||||
fn iter<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)>;
|
fn iter<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ impl DepDef for () {
|
|||||||
fn report(_: &mut impl FnMut(&'static str)) {}
|
fn report(_: &mut impl FnMut(&'static str)) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SystemCtor: Debug + Send + Sync + 'static {
|
pub trait SystemCtor: Debug + 'static {
|
||||||
type Deps: DepDef;
|
type Deps: DepDef;
|
||||||
type Instance: System;
|
type Instance: System;
|
||||||
const NAME: &'static str;
|
const NAME: &'static str;
|
||||||
@@ -68,7 +68,7 @@ pub trait SystemCtor: Debug + Send + Sync + 'static {
|
|||||||
fn inst(&self, deps: <Self::Deps as DepDef>::Sat) -> Self::Instance;
|
fn inst(&self, deps: <Self::Deps as DepDef>::Sat) -> Self::Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DynSystemCtor: Debug + Send + Sync + 'static {
|
pub trait DynSystemCtor: Debug + 'static {
|
||||||
fn decl(&self, id: api::SysDeclId) -> api::SystemDecl;
|
fn decl(&self, id: api::SysDeclId) -> api::SystemDecl;
|
||||||
fn new_system(&self, new: &api::NewSystem) -> CtedObj;
|
fn new_system(&self, new: &api::NewSystem) -> CtedObj;
|
||||||
}
|
}
|
||||||
@@ -91,8 +91,7 @@ impl<T: SystemCtor> DynSystemCtor for T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod dep_set_tuple_impls {
|
mod dep_set_tuple_impls {
|
||||||
use orchid_base::box_chain;
|
use orchid_base::{BoxedIter, box_chain};
|
||||||
use orchid_base::boxed_iter::BoxedIter;
|
|
||||||
use pastey::paste;
|
use pastey::paste;
|
||||||
|
|
||||||
use super::{DepDef, DepSat};
|
use super::{DepDef, DepSat};
|
||||||
|
|||||||
@@ -11,22 +11,50 @@ use crate::ext_port::ExtPort;
|
|||||||
/// shutdown.
|
/// shutdown.
|
||||||
#[cfg(feature = "tokio")]
|
#[cfg(feature = "tokio")]
|
||||||
pub async fn tokio_entrypoint(builder: ExtensionBuilder) {
|
pub async fn tokio_entrypoint(builder: ExtensionBuilder) {
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use async_event::Event;
|
||||||
use tokio::io::{stderr, stdin, stdout};
|
use tokio::io::{stderr, stdin, stdout};
|
||||||
use tokio::task::{LocalSet, spawn_local};
|
use tokio::task::{LocalSet, spawn_local};
|
||||||
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
|
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
|
||||||
|
|
||||||
let local_set = LocalSet::new();
|
let cc = Rc::new(Event::new());
|
||||||
local_set.spawn_local(async {
|
let c = Rc::new(RefCell::new(1));
|
||||||
builder.build(ExtPort {
|
LocalSet::new()
|
||||||
input: Box::pin(stdin().compat()),
|
.run_until(async {
|
||||||
output: Box::pin(stdout().compat_write()),
|
let counter = (c.clone(), cc.clone());
|
||||||
log: Box::pin(stderr().compat_write()),
|
builder
|
||||||
spawn: Rc::new(|fut| {
|
.run(ExtPort {
|
||||||
spawn_local(fut);
|
input: Box::pin(stdin().compat()),
|
||||||
}),
|
output: Box::pin(stdout().compat_write()),
|
||||||
});
|
log: Box::pin(stderr().compat_write()),
|
||||||
});
|
spawn: Rc::new(move |delay, fut| {
|
||||||
local_set.await;
|
let (c, cc) = counter.clone();
|
||||||
|
if delay.is_zero() {
|
||||||
|
*c.borrow_mut() += 1;
|
||||||
|
cc.notify_all();
|
||||||
|
spawn_local(async move {
|
||||||
|
fut.await;
|
||||||
|
*c.borrow_mut() -= 1;
|
||||||
|
cc.notify_all();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let at = tokio::time::Instant::now() + delay;
|
||||||
|
spawn_local(async move {
|
||||||
|
tokio::time::sleep_until(at).await;
|
||||||
|
*c.borrow_mut() += 1;
|
||||||
|
cc.notify_all();
|
||||||
|
fut.await;
|
||||||
|
*c.borrow_mut() -= 1;
|
||||||
|
cc.notify_all();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
cc.wait_until(|| (*c.borrow() == 0).then_some(())).await;
|
||||||
|
})
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ use futures::future::{LocalBoxFuture, join_all};
|
|||||||
use futures::{FutureExt, StreamExt};
|
use futures::{FutureExt, StreamExt};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::interner::{IStr, is};
|
use orchid_base::{IStr, SrcRange, Sym, TokTree, Token, TokenVariant, is};
|
||||||
use orchid_base::location::SrcRange;
|
|
||||||
use orchid_base::name::Sym;
|
|
||||||
use orchid_base::tree::{TokTree, Token, TokenVariant};
|
|
||||||
use substack::Substack;
|
use substack::Substack;
|
||||||
use task_local::task_local;
|
use task_local::task_local;
|
||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
@@ -20,7 +17,7 @@ use crate::api;
|
|||||||
use crate::conv::ToExpr;
|
use crate::conv::ToExpr;
|
||||||
use crate::expr::{BorrowedExprStore, Expr, ExprHandle};
|
use crate::expr::{BorrowedExprStore, Expr, ExprHandle};
|
||||||
use crate::func_atom::{ExprFunc, Fun};
|
use crate::func_atom::{ExprFunc, Fun};
|
||||||
use crate::gen_expr::{GExpr, new_atom, sym_ref};
|
use crate::gen_expr::{GExpr, new_atom};
|
||||||
|
|
||||||
pub type GenTokTree = TokTree<Expr, GExpr>;
|
pub type GenTokTree = TokTree<Expr, GExpr>;
|
||||||
pub type GenTok = Token<Expr, GExpr>;
|
pub type GenTok = Token<Expr, GExpr>;
|
||||||
@@ -28,7 +25,7 @@ pub type GenTok = Token<Expr, GExpr>;
|
|||||||
impl TokenVariant<api::Expression> for GExpr {
|
impl TokenVariant<api::Expression> for GExpr {
|
||||||
type FromApiCtx<'a> = ();
|
type FromApiCtx<'a> = ();
|
||||||
type ToApiCtx<'a> = ();
|
type ToApiCtx<'a> = ();
|
||||||
async fn from_api(_: &api::Expression, _: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
|
async fn from_api(_: api::Expression, _: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
|
||||||
panic!("Received new expression from host")
|
panic!("Received new expression from host")
|
||||||
}
|
}
|
||||||
async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> api::Expression { self.serialize().await }
|
async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> api::Expression { self.serialize().await }
|
||||||
@@ -36,16 +33,16 @@ impl TokenVariant<api::Expression> for GExpr {
|
|||||||
|
|
||||||
impl TokenVariant<api::ExprTicket> for Expr {
|
impl TokenVariant<api::ExprTicket> for Expr {
|
||||||
type FromApiCtx<'a> = &'a BorrowedExprStore;
|
type FromApiCtx<'a> = &'a BorrowedExprStore;
|
||||||
async fn from_api(api: &api::ExprTicket, exprs: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
|
async fn from_api(api: api::ExprTicket, exprs: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
|
||||||
// SAFETY: receiving trees from sublexers implies borrowing
|
// SAFETY: receiving trees from sublexers implies borrowing
|
||||||
Expr::from_handle(ExprHandle::borrowed(*api, exprs))
|
Expr::from_handle(ExprHandle::borrowed(api, exprs))
|
||||||
}
|
}
|
||||||
type ToApiCtx<'a> = ();
|
type ToApiCtx<'a> = ();
|
||||||
async fn into_api(self, (): &mut Self::ToApiCtx<'_>) -> api::ExprTicket { self.handle().ticket() }
|
async fn into_api(self, (): &mut Self::ToApiCtx<'_>) -> api::ExprTicket { self.handle().ticket() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn x_tok(x: impl ToExpr) -> GenTok { GenTok::NewExpr(x.to_gen().await) }
|
pub async fn x_tok(x: impl ToExpr) -> GenTok { GenTok::NewExpr(x.to_gen().await) }
|
||||||
pub async fn ref_tok(path: Sym) -> GenTok { GenTok::NewExpr(sym_ref(path)) }
|
pub async fn ref_tok(path: Sym) -> GenTok { GenTok::NewExpr(path.to_gen().await) }
|
||||||
|
|
||||||
pub fn lazy(
|
pub fn lazy(
|
||||||
public: bool,
|
public: bool,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::pin::Pin;
|
|||||||
|
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use futures::{AsyncRead, AsyncWrite};
|
use futures::{AsyncRead, AsyncWrite};
|
||||||
use orchid_base::reqnot::{Receipt, RepWriter, ReqHandle, ReqReader};
|
use orchid_base::{Receipt, RepWriter, ReqHandle, ReqReader};
|
||||||
|
|
||||||
pub struct TrivialReqCycle<'a> {
|
pub struct TrivialReqCycle<'a> {
|
||||||
pub req: &'a [u8],
|
pub req: &'a [u8],
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ edition = "2024"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
async-event = "0.2.1"
|
||||||
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
||||||
async-once-cell = "0.5.4"
|
async-once-cell = "0.5.4"
|
||||||
bound = "0.6.0"
|
bound = "0.6.0"
|
||||||
|
|||||||
@@ -5,16 +5,13 @@ use async_once_cell::OnceCell;
|
|||||||
use derive_destructure::destructure;
|
use derive_destructure::destructure;
|
||||||
#[cfg(feature = "orchid-extension")]
|
#[cfg(feature = "orchid-extension")]
|
||||||
use orchid_api_traits::{Request, UnderRoot};
|
use orchid_api_traits::{Request, UnderRoot};
|
||||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, take_first_fmt};
|
use orchid_base::{AtomRepr, ClientExt, FmtCtx, FmtUnit, Format, Pos, take_first_fmt};
|
||||||
use orchid_base::location::Pos;
|
|
||||||
use orchid_base::reqnot::ClientExt;
|
|
||||||
use orchid_base::tree::AtomRepr;
|
|
||||||
#[cfg(feature = "orchid-extension")]
|
#[cfg(feature = "orchid-extension")]
|
||||||
use orchid_extension::atom::AtomMethod;
|
use orchid_extension::AtomMethod;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::ctx::Ctx;
|
use crate::ctx::Ctx;
|
||||||
use crate::expr::{Expr, PathSetBuilder};
|
use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder};
|
||||||
use crate::extension::Extension;
|
use crate::extension::Extension;
|
||||||
use crate::system::System;
|
use crate::system::System;
|
||||||
|
|
||||||
@@ -66,7 +63,7 @@ impl AtomHand {
|
|||||||
method: M,
|
method: M,
|
||||||
) -> Option<M::Response> {
|
) -> Option<M::Response> {
|
||||||
use orchid_api_traits::{Decode, Encode};
|
use orchid_api_traits::{Decode, Encode};
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::Sym;
|
||||||
|
|
||||||
let name = Sym::parse(<M as UnderRoot>::Root::NAME).await.unwrap();
|
let name = Sym::parse(<M as UnderRoot>::Root::NAME).await.unwrap();
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
@@ -76,15 +73,19 @@ impl AtomHand {
|
|||||||
}
|
}
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub async fn call(self, arg: Expr) -> Expr {
|
pub async fn call(self, arg: Expr) -> Expr {
|
||||||
let owner_sys = self.0.owner.clone();
|
let owner = self.0.owner.clone();
|
||||||
let ctx = owner_sys.ctx();
|
let ctx = owner.ctx();
|
||||||
let client = owner_sys.client();
|
let client = owner.client();
|
||||||
ctx.exprs.give_expr(arg.clone());
|
ctx.exprs.give_expr(arg.clone());
|
||||||
let ret = match Rc::try_unwrap(self.0) {
|
let ret = match Rc::try_unwrap(self.0) {
|
||||||
Ok(data) => client.request(api::FinalCall(data.api(), arg.id())).await.unwrap(),
|
Ok(data) => client.request(api::FinalCall(data.api(), arg.id())).await.unwrap(),
|
||||||
Err(hand) => client.request(api::CallRef(hand.api_ref(), arg.id())).await.unwrap(),
|
Err(hand) => client.request(api::CallRef(hand.api_ref(), arg.id())).await.unwrap(),
|
||||||
};
|
};
|
||||||
let val = Expr::from_api(&ret, PathSetBuilder::new(), ctx.clone()).await;
|
let val = Expr::from_api(ret, PathSetBuilder::new(), ExprFromApiCtx {
|
||||||
|
sys: owner.id(),
|
||||||
|
ctx: ctx.clone(),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
ctx.exprs.take_expr(arg.id());
|
ctx.exprs.take_expr(arg.id());
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::num::{NonZero, NonZeroU16};
|
use std::num::{NonZero, NonZeroU16};
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
use std::time::Duration;
|
||||||
use std::{fmt, ops};
|
use std::{fmt, ops};
|
||||||
|
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
@@ -14,12 +15,21 @@ use crate::system::{System, WeakSystem};
|
|||||||
use crate::tree::WeakRoot;
|
use crate::tree::WeakRoot;
|
||||||
|
|
||||||
pub trait JoinHandle {
|
pub trait JoinHandle {
|
||||||
|
/// It is guaranteed that the future will never be polled after this is called
|
||||||
fn abort(&self);
|
fn abort(&self);
|
||||||
|
/// take the future out of the task. If the return value
|
||||||
|
/// is dropped, the spawned future is also dropped
|
||||||
fn join(self: Box<Self>) -> LocalBoxFuture<'static, ()>;
|
fn join(self: Box<Self>) -> LocalBoxFuture<'static, ()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Spawner {
|
pub trait Spawner {
|
||||||
fn spawn_obj(&self, fut: LocalBoxFuture<'static, ()>) -> Box<dyn JoinHandle>;
|
/// Spawn a future off-task, but in the same thread. If all objects associated
|
||||||
|
/// with the owning [Ctx] are dropped (eg. expressions, extensions, systems)
|
||||||
|
/// and no extensions create permanent ref loops, then all tasks will
|
||||||
|
/// eventually settle, therefore the implementor of this interface should not
|
||||||
|
/// exit while there are pending tasks to allow external communication
|
||||||
|
/// channels to cleanly shut down.
|
||||||
|
fn spawn_obj(&self, delay: Duration, fut: LocalBoxFuture<'static, ()>) -> Box<dyn JoinHandle>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CtxData {
|
pub struct CtxData {
|
||||||
@@ -58,12 +68,16 @@ impl Ctx {
|
|||||||
}
|
}
|
||||||
/// Spawn a parallel future that you can join at any later time.
|
/// Spawn a parallel future that you can join at any later time.
|
||||||
///
|
///
|
||||||
/// Don't use this for async Drop, use [orchid_base::stash::stash] instead.
|
/// Don't use this for async Drop, use [orchid_base::stash] instead.
|
||||||
/// If you use this for an actor object, make sure to actually join the
|
/// If you use this for an actor object, make sure to actually join the
|
||||||
/// handle.
|
/// handle.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn spawn(&self, fut: impl Future<Output = ()> + 'static) -> Box<dyn JoinHandle> {
|
pub fn spawn(
|
||||||
self.spawner.spawn_obj(Box::pin(fut))
|
&self,
|
||||||
|
delay: Duration,
|
||||||
|
fut: impl Future<Output = ()> + 'static,
|
||||||
|
) -> Box<dyn JoinHandle> {
|
||||||
|
self.spawner.spawn_obj(delay, Box::pin(fut))
|
||||||
}
|
}
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) async fn system_inst(&self, id: api::SysId) -> Option<System> {
|
pub(crate) async fn system_inst(&self, id: api::SysId) -> Option<System> {
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::error::{OrcErrv, OrcRes, mk_errv};
|
use orchid_base::{IStr, OrcErrv, OrcRes, Pos, VName, is, mk_errv};
|
||||||
use orchid_base::interner::{IStr, is};
|
|
||||||
use orchid_base::location::Pos;
|
|
||||||
use orchid_base::name::VName;
|
|
||||||
|
|
||||||
/// Errors produced by absolute_path
|
/// Errors produced by absolute_path
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures::io::BufReader;
|
use futures::io::BufReader;
|
||||||
use futures::{AsyncBufReadExt, StreamExt};
|
use futures::{AsyncBufReadExt, StreamExt};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use libloading::{Library, Symbol};
|
use libloading::{Library, Symbol};
|
||||||
use orchid_base::binary::vt_to_future;
|
use orchid_base::{log, on_drop, vt_to_future};
|
||||||
use orchid_base::logging::log;
|
|
||||||
use unsync_pipe::pipe;
|
use unsync_pipe::pipe;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::ctx::Ctx;
|
use crate::ctx::Ctx;
|
||||||
use crate::extension::ExtPort;
|
use crate::extension::ExtPort;
|
||||||
|
use crate::task_set::TaskSet;
|
||||||
|
|
||||||
static DYNAMIC_LIBRARIES: Mutex<Option<HashMap<PathBuf, Arc<Library>>>> = Mutex::new(None);
|
static DYNAMIC_LIBRARIES: Mutex<Option<HashMap<PathBuf, Arc<Library>>>> = Mutex::new(None);
|
||||||
fn load_dylib(path: &Path) -> Result<Arc<Library>, libloading::Error> {
|
fn load_dylib(path: &Path) -> Result<Arc<Library>, libloading::Error> {
|
||||||
@@ -32,7 +33,7 @@ pub async fn ext_dylib(path: &Path, ctx: Ctx) -> Result<ExtPort, libloading::Err
|
|||||||
let (output, read_output) = pipe(1024);
|
let (output, read_output) = pipe(1024);
|
||||||
let (write_log, read_log) = pipe(1024);
|
let (write_log, read_log) = pipe(1024);
|
||||||
let log_path = path.to_string_lossy().to_string();
|
let log_path = path.to_string_lossy().to_string();
|
||||||
let _ = ctx.spawn(async move {
|
let _ = ctx.spawn(Duration::ZERO, async move {
|
||||||
let mut lines = BufReader::new(read_log).lines();
|
let mut lines = BufReader::new(read_log).lines();
|
||||||
while let Some(line) = lines.next().await {
|
while let Some(line) = lines.next().await {
|
||||||
match line {
|
match line {
|
||||||
@@ -44,16 +45,43 @@ pub async fn ext_dylib(path: &Path, ctx: Ctx) -> Result<ExtPort, libloading::Err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
let tasks = TaskSet::default();
|
||||||
let library = load_dylib(path)?;
|
let library = load_dylib(path)?;
|
||||||
let entrypoint: Symbol<unsafe extern "C" fn(api::binary::ExtensionContext)> =
|
let entrypoint: Symbol<unsafe extern "C" fn(api::binary::ExtensionContext)> =
|
||||||
unsafe { library.get("orchid_extension_main") }?;
|
unsafe { library.get("orchid_extension_main") }?;
|
||||||
let data = Box::into_raw(Box::new(ctx)) as *const ();
|
|
||||||
extern "C" fn drop(data: *const ()) { std::mem::drop(unsafe { Box::from_raw(data as *mut Ctx) }) }
|
let data = Box::into_raw(Box::new(SpawnerState { ctx, tasks: tasks.clone() })) as *const ();
|
||||||
extern "C" fn spawn(data: *const (), vt: api::binary::FutureBin) {
|
|
||||||
let _ = unsafe { (data as *mut Ctx).as_mut().unwrap().spawn(vt_to_future(vt)) };
|
|
||||||
}
|
|
||||||
let spawner = api::binary::SpawnerBin { data, drop, spawn };
|
let spawner = api::binary::SpawnerBin { data, drop, spawn };
|
||||||
let cx = api::binary::ExtensionContext { input, output, log: write_log, spawner };
|
let cx = api::binary::ExtensionContext { input, output, log: write_log, spawner };
|
||||||
unsafe { (entrypoint)(cx) };
|
unsafe { (entrypoint)(cx) };
|
||||||
Ok(ExtPort { input: Box::pin(write_input), output: Box::pin(read_output) })
|
Ok(ExtPort {
|
||||||
|
input: Box::pin(write_input),
|
||||||
|
output: Box::pin(read_output),
|
||||||
|
drop_trigger: Box::new(on_drop(move || tasks.abort_all())),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SpawnerState {
|
||||||
|
ctx: Ctx,
|
||||||
|
tasks: TaskSet,
|
||||||
|
}
|
||||||
|
extern "C" fn drop(data: *const ()) {
|
||||||
|
let state = unsafe { Box::from_raw(data as *mut SpawnerState) };
|
||||||
|
state.tasks.abort_all();
|
||||||
|
}
|
||||||
|
extern "C" fn spawn(data: *const (), delay: u64, vt: api::binary::FutureBin) {
|
||||||
|
let future = vt_to_future(vt);
|
||||||
|
// SAFETY: this is technically a Box but it can be used directly as a &mut
|
||||||
|
let state = unsafe { (data as *mut SpawnerState).as_mut() }.unwrap();
|
||||||
|
state.tasks.with(|store| {
|
||||||
|
store.add_with(|id| {
|
||||||
|
state.ctx.spawn(Duration::from_millis(delay), async move {
|
||||||
|
future.await;
|
||||||
|
// SAFETY: We know this is live because the drop handle that frees it also
|
||||||
|
// aborts the future
|
||||||
|
let state = unsafe { (data as *mut SpawnerState).as_mut() }.unwrap();
|
||||||
|
state.tasks.with(|store| store.remove(id));
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ use std::mem;
|
|||||||
use bound::Bound;
|
use bound::Bound;
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use futures_locks::{RwLockWriteGuard, TryLockError};
|
use futures_locks::{RwLockWriteGuard, TryLockError};
|
||||||
use orchid_base::error::OrcErrv;
|
use orchid_base::{OrcErrv, Pos, fmt, log};
|
||||||
use orchid_base::format::fmt;
|
|
||||||
use orchid_base::location::Pos;
|
|
||||||
use orchid_base::logging::log;
|
|
||||||
|
|
||||||
use crate::expr::{Expr, ExprKind, PathSet, Step};
|
use crate::expr::{Expr, ExprKind, PathSet, Step};
|
||||||
use crate::tree::Root;
|
use crate::tree::Root;
|
||||||
|
|||||||
@@ -7,12 +7,10 @@ use std::{fmt, mem};
|
|||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use futures_locks::RwLock;
|
use futures_locks::RwLock;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::error::OrcErrv;
|
use orchid_base::{
|
||||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
|
AtomRepr, FmtCtx, FmtUnit, Format, OrcErrv, Pos, SrcRange, Sym, TokenVariant, Variants, indent,
|
||||||
use orchid_base::location::{Pos, SrcRange};
|
tl_cache,
|
||||||
use orchid_base::name::Sym;
|
};
|
||||||
use orchid_base::tl_cache;
|
|
||||||
use orchid_base::tree::{AtomRepr, TokenVariant, indent};
|
|
||||||
use substack::Substack;
|
use substack::Substack;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
@@ -26,6 +24,12 @@ pub struct ExprData {
|
|||||||
kind: RwLock<ExprKind>,
|
kind: RwLock<ExprKind>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ExprFromApiCtx {
|
||||||
|
pub sys: api::SysId,
|
||||||
|
pub ctx: Ctx,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Expr(Rc<ExprData>);
|
pub struct Expr(Rc<ExprData>);
|
||||||
impl Expr {
|
impl Expr {
|
||||||
@@ -54,35 +58,40 @@ impl Expr {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub async fn from_api(api: &api::Expression, psb: PathSetBuilder<'_, u64>, ctx: Ctx) -> Self {
|
pub async fn from_api(
|
||||||
|
api: api::Expression,
|
||||||
|
psb: PathSetBuilder<'_, u64>,
|
||||||
|
ctx: ExprFromApiCtx,
|
||||||
|
) -> Self {
|
||||||
let pos = Pos::from_api(&api.location).await;
|
let pos = Pos::from_api(&api.location).await;
|
||||||
let kind = match &api.kind {
|
let kind = match api.kind {
|
||||||
api::ExpressionKind::Arg(n) => {
|
api::ExpressionKind::Arg(n) => {
|
||||||
assert!(psb.register_arg(n), "Arguments must be enclosed in a matching lambda");
|
assert!(psb.register_arg(&n), "Arguments must be enclosed in a matching lambda");
|
||||||
ExprKind::Arg
|
ExprKind::Arg
|
||||||
},
|
},
|
||||||
api::ExpressionKind::Bottom(bot) => ExprKind::Bottom(OrcErrv::from_api(bot).await),
|
api::ExpressionKind::Bottom(bot) => ExprKind::Bottom(OrcErrv::from_api(bot).await),
|
||||||
api::ExpressionKind::Call(f, x) => {
|
api::ExpressionKind::Call(f, x) => {
|
||||||
let (lpsb, rpsb) = psb.split();
|
let (lpsb, rpsb) = psb.split();
|
||||||
ExprKind::Call(
|
ExprKind::Call(
|
||||||
Expr::from_api(f, lpsb, ctx.clone()).boxed_local().await,
|
Expr::from_api(*f, lpsb, ctx.clone()).boxed_local().await,
|
||||||
Expr::from_api(x, rpsb, ctx).boxed_local().await,
|
Expr::from_api(*x, rpsb, ctx).boxed_local().await,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
api::ExpressionKind::Const(name) => ExprKind::Const(Sym::from_api(*name).await),
|
api::ExpressionKind::Const(name) => ExprKind::Const(Sym::from_api(name).await),
|
||||||
api::ExpressionKind::Lambda(x, body) => {
|
api::ExpressionKind::Lambda(x, body) => {
|
||||||
let lbuilder = psb.lambda(x);
|
let lbuilder = psb.lambda(&x);
|
||||||
let body = Expr::from_api(body, lbuilder.stack(), ctx).boxed_local().await;
|
let body = Expr::from_api(*body, lbuilder.stack(), ctx).boxed_local().await;
|
||||||
ExprKind::Lambda(lbuilder.collect(), body)
|
ExprKind::Lambda(lbuilder.collect(), body)
|
||||||
},
|
},
|
||||||
api::ExpressionKind::NewAtom(a) =>
|
api::ExpressionKind::NewAtom(a) => ExprKind::Atom(
|
||||||
ExprKind::Atom(AtomHand::from_api(a, pos.clone(), &mut ctx.clone()).await),
|
AtomHand::from_api(&a.associate(ctx.sys), pos.clone(), &mut ctx.ctx.clone()).await,
|
||||||
api::ExpressionKind::Slot(tk) => return ctx.exprs.take_expr(*tk).expect("Invalid slot"),
|
),
|
||||||
|
api::ExpressionKind::Slot(tk) => return ctx.ctx.exprs.take_expr(tk).expect("Invalid slot"),
|
||||||
api::ExpressionKind::Seq(a, b) => {
|
api::ExpressionKind::Seq(a, b) => {
|
||||||
let (apsb, bpsb) = psb.split();
|
let (apsb, bpsb) = psb.split();
|
||||||
ExprKind::Seq(
|
ExprKind::Seq(
|
||||||
Expr::from_api(a, apsb, ctx.clone()).boxed_local().await,
|
Expr::from_api(*a, apsb, ctx.clone()).boxed_local().await,
|
||||||
Expr::from_api(b, bpsb, ctx).boxed_local().await,
|
Expr::from_api(*b, bpsb, ctx).boxed_local().await,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -159,8 +168,10 @@ async fn print_exprkind<'a>(
|
|||||||
panic!("This variant is swapped into write guards, so a read can never see it")
|
panic!("This variant is swapped into write guards, so a read can never see it")
|
||||||
},
|
},
|
||||||
ExprKind::Atom(a) => a.print(c).await,
|
ExprKind::Atom(a) => a.print(c).await,
|
||||||
ExprKind::Bottom(e) if e.len() == 1 => format!("Bottom({e})").into(),
|
ExprKind::Bottom(e) => match e.one() {
|
||||||
ExprKind::Bottom(e) => format!("Bottom(\n\t{}\n)", indent(&e.to_string())).into(),
|
Some(e) => format!("Bottom({e})").into(),
|
||||||
|
None => format!("Bottom(\n\t{}\n)", indent(&e.to_string())).into(),
|
||||||
|
},
|
||||||
ExprKind::Call(f, x) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
ExprKind::Call(f, x) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||||
.unbounded("{0b} {1l}")
|
.unbounded("{0b} {1l}")
|
||||||
.bounded("({0b} {1})")))
|
.bounded("({0b} {1})")))
|
||||||
@@ -333,8 +344,8 @@ impl WeakExpr {
|
|||||||
|
|
||||||
impl TokenVariant<api::ExprTicket> for Expr {
|
impl TokenVariant<api::ExprTicket> for Expr {
|
||||||
type FromApiCtx<'a> = ExprStore;
|
type FromApiCtx<'a> = ExprStore;
|
||||||
async fn from_api(api: &api::ExprTicket, ctx: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
|
async fn from_api(api: api::ExprTicket, ctx: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
|
||||||
ctx.get_expr(*api).expect("Invalid ticket")
|
ctx.get_expr(api).expect("Invalid ticket")
|
||||||
}
|
}
|
||||||
type ToApiCtx<'a> = ExprStore;
|
type ToApiCtx<'a> = ExprStore;
|
||||||
async fn into_api(self, ctx: &mut Self::ToApiCtx<'_>) -> api::ExprTicket {
|
async fn into_api(self, ctx: &mut Self::ToApiCtx<'_>) -> api::ExprTicket {
|
||||||
@@ -349,8 +360,8 @@ impl TokenVariant<api::ExprTicket> for Expr {
|
|||||||
pub struct ExprWillPanic;
|
pub struct ExprWillPanic;
|
||||||
|
|
||||||
impl TokenVariant<api::Expression> for Expr {
|
impl TokenVariant<api::Expression> for Expr {
|
||||||
type FromApiCtx<'a> = Ctx;
|
type FromApiCtx<'a> = ExprFromApiCtx;
|
||||||
async fn from_api(api: &api::Expression, ctx: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
|
async fn from_api(api: api::Expression, ctx: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
|
||||||
Self::from_api(api, PathSetBuilder::new(), ctx.clone()).await
|
Self::from_api(api, PathSetBuilder::new(), ctx.clone()).await
|
||||||
}
|
}
|
||||||
type ToApiCtx<'a> = ExprWillPanic;
|
type ToApiCtx<'a> = ExprWillPanic;
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
use std::any::Any;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::num::NonZeroU64;
|
use std::num::NonZeroU64;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use async_fn_stream::stream;
|
use async_fn_stream::stream;
|
||||||
use derive_destructure::destructure;
|
use derive_destructure::destructure;
|
||||||
@@ -14,28 +16,23 @@ use futures::{AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt};
|
|||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_api_traits::{Decode, Encode, Request};
|
use orchid_api_traits::{Decode, Encode, Request};
|
||||||
use orchid_base::format::{FmtCtxImpl, Format};
|
use orchid_base::{
|
||||||
use orchid_base::interner::{IStr, IStrv, es, ev, is, iv};
|
AtomRepr, Client, ClientExt, CommCtx, FmtCtxImpl, Format, IStr, IStrv, MsgReaderExt, Pos,
|
||||||
use orchid_base::location::Pos;
|
ReqHandleExt, ReqReaderExt, Sym, Witness, es, ev, io_comm, is, iv, log, stash, with_stash,
|
||||||
use orchid_base::logging::log;
|
|
||||||
use orchid_base::name::Sym;
|
|
||||||
use orchid_base::reqnot::{
|
|
||||||
Client, ClientExt, CommCtx, MsgReaderExt, ReqHandleExt, ReqReaderExt, io_comm,
|
|
||||||
};
|
};
|
||||||
use orchid_base::stash::{stash, with_stash};
|
|
||||||
use orchid_base::tree::AtomRepr;
|
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::atom::AtomHand;
|
use crate::atom::AtomHand;
|
||||||
use crate::ctx::{Ctx, JoinHandle};
|
use crate::ctx::{Ctx, JoinHandle};
|
||||||
use crate::dealias::{ChildError, ChildErrorKind, walk};
|
use crate::dealias::{ChildError, ChildErrorKind, walk};
|
||||||
use crate::expr::{Expr, PathSetBuilder};
|
use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder};
|
||||||
use crate::system::SystemCtor;
|
use crate::system::SystemCtor;
|
||||||
use crate::tree::MemberKind;
|
use crate::tree::MemberKind;
|
||||||
|
|
||||||
pub struct ExtPort {
|
pub struct ExtPort {
|
||||||
pub input: Pin<Box<dyn AsyncWrite>>,
|
pub input: Pin<Box<dyn AsyncWrite>>,
|
||||||
pub output: Pin<Box<dyn AsyncRead>>,
|
pub output: Pin<Box<dyn AsyncRead>>,
|
||||||
|
pub drop_trigger: Box<dyn Any>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ReqPair<R: Request>(R, Sender<R::Response>);
|
pub struct ReqPair<R: Request>(R, Sender<R::Response>);
|
||||||
@@ -56,6 +53,8 @@ pub struct ExtensionData {
|
|||||||
lex_recur: Mutex<HashMap<api::ParsId, Sender<ReqPair<api::SubLex>>>>,
|
lex_recur: Mutex<HashMap<api::ParsId, Sender<ReqPair<api::SubLex>>>>,
|
||||||
strings: RefCell<HashSet<IStr>>,
|
strings: RefCell<HashSet<IStr>>,
|
||||||
string_vecs: RefCell<HashSet<IStrv>>,
|
string_vecs: RefCell<HashSet<IStrv>>,
|
||||||
|
/// Moved over from [ExtPort] to allow hooking to the extension's drop
|
||||||
|
_drop_trigger: Box<dyn Any>,
|
||||||
}
|
}
|
||||||
impl Drop for ExtensionData {
|
impl Drop for ExtensionData {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
@@ -85,7 +84,7 @@ impl Extension {
|
|||||||
let weak2 = weak;
|
let weak2 = weak;
|
||||||
let weak = weak.clone();
|
let weak = weak.clone();
|
||||||
let ctx2 = ctx.clone();
|
let ctx2 = ctx.clone();
|
||||||
let join_ext = ctx.clone().spawn(async move {
|
let join_ext = ctx.clone().spawn(Duration::ZERO, async move {
|
||||||
comm
|
comm
|
||||||
.listen(
|
.listen(
|
||||||
async |reader| {
|
async |reader| {
|
||||||
@@ -192,11 +191,17 @@ impl Extension {
|
|||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
},
|
},
|
||||||
api::ExprReq::Create(ref cre @ api::Create(ref expr)) => {
|
api::ExprReq::Create(cre) => {
|
||||||
let expr = Expr::from_api(expr, PathSetBuilder::new(), ctx.clone()).await;
|
let req = Witness::of(&cre);
|
||||||
|
let api::Create(sys, expr) = cre;
|
||||||
|
let expr = Expr::from_api(expr, PathSetBuilder::new(), ExprFromApiCtx {
|
||||||
|
ctx: ctx.clone(),
|
||||||
|
sys,
|
||||||
|
})
|
||||||
|
.await;
|
||||||
let expr_id = expr.id();
|
let expr_id = expr.id();
|
||||||
ctx.exprs.give_expr(expr);
|
ctx.exprs.give_expr(expr);
|
||||||
handle.reply(cre, &expr_id).await
|
handle.reply(req, &expr_id).await
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
api::ExtHostReq::LsModule(ref ls @ api::LsModule(_sys, path)) => {
|
api::ExtHostReq::LsModule(ref ls @ api::LsModule(_sys, path)) => {
|
||||||
@@ -286,6 +291,7 @@ impl Extension {
|
|||||||
client: Rc::new(client),
|
client: Rc::new(client),
|
||||||
strings: RefCell::default(),
|
strings: RefCell::default(),
|
||||||
string_vecs: RefCell::default(),
|
string_vecs: RefCell::default(),
|
||||||
|
_drop_trigger: init.drop_trigger,
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
@@ -336,10 +342,13 @@ impl Extension {
|
|||||||
}
|
}
|
||||||
pub fn system_drop(&self, id: api::SysId) {
|
pub fn system_drop(&self, id: api::SysId) {
|
||||||
let rc = self.clone();
|
let rc = self.clone();
|
||||||
let _ = self.ctx().spawn(with_stash(async move {
|
let _ = self.ctx().spawn(
|
||||||
rc.client().request(api::SystemDrop(id)).await.unwrap();
|
Duration::ZERO,
|
||||||
rc.ctx().systems.write().await.remove(&id);
|
with_stash(async move {
|
||||||
}));
|
rc.client().request(api::SystemDrop(id)).await.unwrap();
|
||||||
|
rc.ctx().systems.write().await.remove(&id);
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn downgrade(&self) -> WeakExtension { WeakExtension(Rc::downgrade(&self.0)) }
|
pub fn downgrade(&self) -> WeakExtension { WeakExtension(Rc::downgrade(&self.0)) }
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
#[cfg(feature = "orchid-extension")]
|
#[cfg(feature = "orchid-extension")]
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[cfg(feature = "orchid-extension")]
|
||||||
|
use orchid_base::on_drop;
|
||||||
|
#[cfg(feature = "orchid-extension")]
|
||||||
use orchid_extension as ox;
|
use orchid_extension as ox;
|
||||||
|
|
||||||
#[cfg(feature = "orchid-extension")]
|
#[cfg(feature = "orchid-extension")]
|
||||||
use crate::ctx::Ctx;
|
use crate::ctx::Ctx;
|
||||||
#[cfg(feature = "orchid-extension")]
|
#[cfg(feature = "orchid-extension")]
|
||||||
use crate::extension::ExtPort;
|
use crate::extension::ExtPort;
|
||||||
|
#[cfg(feature = "orchid-extension")]
|
||||||
|
use crate::task_set::TaskSet;
|
||||||
|
|
||||||
#[cfg(feature = "orchid-extension")]
|
#[cfg(feature = "orchid-extension")]
|
||||||
pub async fn ext_inline(builder: ox::entrypoint::ExtensionBuilder, ctx: Ctx) -> ExtPort {
|
pub async fn ext_inline(builder: ox::entrypoint::ExtensionBuilder, ctx: Ctx) -> ExtPort {
|
||||||
@@ -13,7 +20,7 @@ pub async fn ext_inline(builder: ox::entrypoint::ExtensionBuilder, ctx: Ctx) ->
|
|||||||
|
|
||||||
use futures::io::BufReader;
|
use futures::io::BufReader;
|
||||||
use futures::{AsyncBufReadExt, StreamExt};
|
use futures::{AsyncBufReadExt, StreamExt};
|
||||||
use orchid_base::logging::log;
|
use orchid_base::log;
|
||||||
use unsync_pipe::pipe;
|
use unsync_pipe::pipe;
|
||||||
|
|
||||||
let (in_stdin, out_stdin) = pipe(1024);
|
let (in_stdin, out_stdin) = pipe(1024);
|
||||||
@@ -22,7 +29,7 @@ pub async fn ext_inline(builder: ox::entrypoint::ExtensionBuilder, ctx: Ctx) ->
|
|||||||
|
|
||||||
let name = builder.name;
|
let name = builder.name;
|
||||||
|
|
||||||
std::mem::drop(ctx.spawn(async move {
|
std::mem::drop(ctx.spawn(Duration::ZERO, async move {
|
||||||
let mut lines = BufReader::new(out_stderr).lines();
|
let mut lines = BufReader::new(out_stderr).lines();
|
||||||
while let Some(line) = lines.next().await {
|
while let Some(line) = lines.next().await {
|
||||||
match line {
|
match line {
|
||||||
@@ -35,11 +42,35 @@ pub async fn ext_inline(builder: ox::entrypoint::ExtensionBuilder, ctx: Ctx) ->
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
builder.build(ox::ext_port::ExtPort {
|
let task_set = TaskSet::default();
|
||||||
input: Box::pin(out_stdin),
|
let task_set1 = task_set.clone();
|
||||||
output: Box::pin(in_stdout),
|
|
||||||
log: Box::pin(in_stderr),
|
std::mem::drop(ctx.clone().spawn(Duration::ZERO, async move {
|
||||||
spawn: Rc::new(move |fut| std::mem::drop(ctx.spawn(fut))),
|
let task_set2 = task_set1.clone();
|
||||||
});
|
builder
|
||||||
ExtPort { input: Box::pin(in_stdin), output: Box::pin(out_stdout) }
|
.run(ox::ext_port::ExtPort {
|
||||||
|
input: Box::pin(out_stdin),
|
||||||
|
output: Box::pin(in_stdout),
|
||||||
|
log: Box::pin(in_stderr),
|
||||||
|
spawn: Rc::new(move |delay, fut| {
|
||||||
|
let ctx1 = ctx.clone();
|
||||||
|
let task_set2 = task_set1.clone();
|
||||||
|
task_set1.with(move |store| {
|
||||||
|
store.add_with(move |id| {
|
||||||
|
ctx1.spawn(delay, async move {
|
||||||
|
fut.await;
|
||||||
|
task_set2.with(|store| store.remove(id));
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
task_set2.abort_all();
|
||||||
|
}));
|
||||||
|
ExtPort {
|
||||||
|
input: Box::pin(in_stdin),
|
||||||
|
output: Box::pin(out_stdout),
|
||||||
|
drop_trigger: Box::new(on_drop(move || task_set.abort_all())),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,14 @@ use std::ops::Range;
|
|||||||
|
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use futures::lock::Mutex;
|
use futures::lock::Mutex;
|
||||||
use orchid_base::clone;
|
use orchid_base::{
|
||||||
use orchid_base::error::{OrcErrv, OrcRes, mk_errv, report};
|
IStr, OrcErrv, OrcRes, PARENS, SrcRange, Sym, clone, is, mk_errv, name_char, name_start, op_char,
|
||||||
use orchid_base::interner::{IStr, is};
|
report, unrep_space,
|
||||||
use orchid_base::location::SrcRange;
|
};
|
||||||
use orchid_base::name::Sym;
|
|
||||||
use orchid_base::parse::{name_char, name_start, op_char, unrep_space};
|
|
||||||
use orchid_base::tokens::PARENS;
|
|
||||||
use orchid_base::tree::recur;
|
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::ctx::Ctx;
|
use crate::ctx::Ctx;
|
||||||
use crate::expr::Expr;
|
use crate::expr::{Expr, ExprFromApiCtx};
|
||||||
use crate::expr_store::ExprStore;
|
use crate::expr_store::ExprStore;
|
||||||
use crate::parsed::{ParsTok, ParsTokTree, tt_to_api};
|
use crate::parsed::{ParsTok, ParsTokTree, tt_to_api};
|
||||||
use crate::system::System;
|
use crate::system::System;
|
||||||
@@ -63,8 +59,14 @@ impl<'a> LexCtx<'a> {
|
|||||||
tt_to_api(&mut { exprs }, subtree).await
|
tt_to_api(&mut { exprs }, subtree).await
|
||||||
}
|
}
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub async fn des_subtree(&mut self, tree: &api::TokenTree, exprs: ExprStore) -> ParsTokTree {
|
pub async fn des_subtree(
|
||||||
ParsTokTree::from_api(tree, &mut { exprs }, &mut self.ctx.clone(), self.path).await
|
&mut self,
|
||||||
|
tree: api::TokenTree,
|
||||||
|
sys: api::SysId,
|
||||||
|
exprs: ExprStore,
|
||||||
|
) -> ParsTokTree {
|
||||||
|
let mut cx = ExprFromApiCtx { ctx: self.ctx.clone(), sys };
|
||||||
|
ParsTokTree::from_api(tree, &mut { exprs }, &mut cx, self.path).await
|
||||||
}
|
}
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn strip_char(&mut self, tgt: char) -> bool {
|
pub fn strip_char(&mut self, tgt: char) -> bool {
|
||||||
@@ -261,12 +263,13 @@ pub async fn sys_lex(ctx: &mut LexCtx<'_>) -> Option<OrcRes<Vec<ParsTokTree>>> {
|
|||||||
.await;
|
.await;
|
||||||
match lx {
|
match lx {
|
||||||
Err(e) =>
|
Err(e) =>
|
||||||
return Some(Err(errors.into_iter().fold(OrcErrv::from_api(&e).await, |a, b| a + b))),
|
return Some(Err(errors.into_iter().fold(OrcErrv::from_api(e).await, |a, b| a + b))),
|
||||||
Ok(Some(lexed)) => {
|
Ok(Some(lexed)) => {
|
||||||
ctx.set_pos(lexed.pos);
|
ctx.set_pos(lexed.pos);
|
||||||
let mut stable_trees = Vec::new();
|
let mut stable_trees = Vec::new();
|
||||||
for tok in lexed.expr {
|
for tok in lexed.expr {
|
||||||
stable_trees.push(recur(ctx.des_subtree(&tok, temp_store.clone()).await, &|tt, r| {
|
let tree = ctx.des_subtree(tok, sys.id(), temp_store.clone()).await;
|
||||||
|
stable_trees.push(tree.recur(&|tt, r| {
|
||||||
if let ParsTok::NewExpr(expr) = tt.tok {
|
if let ParsTok::NewExpr(expr) = tt.tok {
|
||||||
return ParsTok::Handle(expr).at(tt.sr);
|
return ParsTok::Handle(expr).at(tt.sr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,4 +18,5 @@ pub mod parsed;
|
|||||||
pub mod subprocess;
|
pub mod subprocess;
|
||||||
mod sys_parser;
|
mod sys_parser;
|
||||||
pub mod system;
|
pub mod system;
|
||||||
|
mod task_set;
|
||||||
pub mod tree;
|
pub mod tree;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use std::rc::Rc;
|
|||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::logging::{LogWriter, Logger};
|
use orchid_base::{LogWriter, Logger};
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::error::{OrcRes, mk_errv, report};
|
use orchid_base::{
|
||||||
use orchid_base::format::fmt;
|
Comment, IStr, Import, OrcRes, Paren, Parsed, Snippet, Sym, TokTree, Token, expect_end, fmt, is,
|
||||||
use orchid_base::interner::{IStr, is};
|
line_items, mk_errv, parse_multiname, report, try_pop_no_fluff,
|
||||||
use orchid_base::name::Sym;
|
|
||||||
use orchid_base::parse::{
|
|
||||||
Comment, Import, Parsed, Snippet, expect_end, line_items, parse_multiname, try_pop_no_fluff,
|
|
||||||
};
|
};
|
||||||
use orchid_base::tree::{Paren, TokTree, Token};
|
|
||||||
use substack::Substack;
|
use substack::Substack;
|
||||||
|
|
||||||
use crate::ctx::Ctx;
|
use crate::ctx::Ctx;
|
||||||
@@ -60,7 +56,7 @@ pub async fn parse_item(
|
|||||||
comments: Vec<Comment>,
|
comments: Vec<Comment>,
|
||||||
item: ParsSnippet<'_>,
|
item: ParsSnippet<'_>,
|
||||||
) -> OrcRes<Vec<Item>> {
|
) -> OrcRes<Vec<Item>> {
|
||||||
match item.pop_front() {
|
match item.split_first() {
|
||||||
Some((TokTree { tok: Token::Name(n), .. }, postdisc)) => match n {
|
Some((TokTree { tok: Token::Name(n), .. }, postdisc)) => match n {
|
||||||
n if *n == is("export").await => match try_pop_no_fluff(postdisc).await? {
|
n if *n == is("export").await => match try_pop_no_fluff(postdisc).await? {
|
||||||
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } =>
|
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } =>
|
||||||
|
|||||||
@@ -5,12 +5,10 @@ use futures::FutureExt;
|
|||||||
use futures::future::{LocalBoxFuture, join_all};
|
use futures::future::{LocalBoxFuture, join_all};
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
|
use orchid_base::{
|
||||||
use orchid_base::interner::{IStr, IStrv};
|
Comment, FmtCtx, FmtUnit, Format, IStr, IStrv, Import, SrcRange, TokTree, Token, Variants,
|
||||||
use orchid_base::location::SrcRange;
|
tl_cache,
|
||||||
use orchid_base::parse::{Comment, Import};
|
};
|
||||||
use orchid_base::tl_cache;
|
|
||||||
use orchid_base::tree::{TokTree, Token, recur};
|
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::dealias::{ChildErrorKind, ChildResult, Tree};
|
use crate::dealias::{ChildErrorKind, ChildResult, Tree};
|
||||||
@@ -182,7 +180,7 @@ impl Tree for ParsedModule {
|
|||||||
impl Format for ParsedModule {
|
impl Format for ParsedModule {
|
||||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
let head_str = format!("export ::({})\n", self.exports.iter().join(", "));
|
let head_str = format!("export ::({})\n", self.exports.iter().join(", "));
|
||||||
Variants::default().sequence(self.items.len() + 1, "", "\n", "", None).units_own(
|
Variants::default().sequence(self.items.len() + 1, "", "\n", "", false).units_own(
|
||||||
[head_str.into()].into_iter().chain(join_all(self.items.iter().map(|i| i.print(c))).await),
|
[head_str.into()].into_iter().chain(join_all(self.items.iter().map(|i| i.print(c))).await),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -202,7 +200,7 @@ impl ConstPath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn tt_to_api(exprs: &mut ExprStore, subtree: ParsTokTree) -> api::TokenTree {
|
pub async fn tt_to_api(exprs: &mut ExprStore, subtree: ParsTokTree) -> api::TokenTree {
|
||||||
let without_new_expr = recur(subtree, &|tt, r| {
|
let without_new_expr = subtree.recur(&|tt, r| {
|
||||||
if let ParsTok::NewExpr(expr) = tt.tok {
|
if let ParsTok::NewExpr(expr) = tt.tok {
|
||||||
return ParsTok::Handle(expr).at(tt.sr);
|
return ParsTok::Handle(expr).at(tt.sr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,40 @@
|
|||||||
use std::{io, process};
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures::io::BufReader;
|
use futures::io::BufReader;
|
||||||
use futures::{self, AsyncBufReadExt, StreamExt};
|
use futures::{self, AsyncBufReadExt, StreamExt};
|
||||||
use orchid_base::logging::log;
|
use orchid_base::log;
|
||||||
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
|
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
|
||||||
|
|
||||||
use crate::ctx::Ctx;
|
use crate::ctx::Ctx;
|
||||||
use crate::extension::ExtPort;
|
use crate::extension::ExtPort;
|
||||||
|
|
||||||
pub async fn ext_command(cmd: process::Command, ctx: Ctx) -> io::Result<ExtPort> {
|
pub async fn ext_command(cmd: std::process::Command, ctx: Ctx) -> std::io::Result<ExtPort> {
|
||||||
let name = cmd.get_program().to_string_lossy().to_string();
|
let name = cmd.get_program().to_string_lossy().to_string();
|
||||||
let mut child = tokio::process::Command::from(cmd)
|
let mut child = tokio::process::Command::from(cmd)
|
||||||
.stdin(process::Stdio::piped())
|
.stdin(std::process::Stdio::piped())
|
||||||
.stdout(process::Stdio::piped())
|
.stdout(std::process::Stdio::piped())
|
||||||
.stderr(process::Stdio::piped())
|
.stderr(std::process::Stdio::piped())
|
||||||
.spawn()?;
|
.spawn()?;
|
||||||
std::thread::spawn(|| {});
|
std::thread::spawn(|| {});
|
||||||
let stdin = child.stdin.take().unwrap();
|
let stdin = child.stdin.take().unwrap();
|
||||||
let stdout = child.stdout.take().unwrap();
|
let stdout = child.stdout.take().unwrap();
|
||||||
let child_stderr = child.stderr.take().unwrap();
|
let child_stderr = child.stderr.take().unwrap();
|
||||||
let _ = ctx.spawn(Box::pin(async move {
|
std::mem::drop(ctx.spawn(Duration::ZERO, async move {
|
||||||
let _ = child;
|
let _ = child;
|
||||||
let mut lines = BufReader::new(child_stderr.compat()).lines();
|
let mut lines = BufReader::new(child_stderr.compat()).lines();
|
||||||
while let Some(line) = lines.next().await {
|
while let Some(line) = lines.next().await {
|
||||||
match line {
|
match line {
|
||||||
Ok(line) => writeln!(log("stderr"), "subproc {name} err> {line}").await,
|
Ok(line) => writeln!(log("stderr"), "subproc {name} err> {line}").await,
|
||||||
Err(e) => match e.kind() {
|
Err(e) => match e.kind() {
|
||||||
io::ErrorKind::BrokenPipe | io::ErrorKind::UnexpectedEof => break,
|
std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::UnexpectedEof => break,
|
||||||
_ => panic!("Error while reading stderr {e}"),
|
_ => panic!("Error while reading stderr {e}"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
Ok(ExtPort { input: Box::pin(stdin.compat_write()), output: Box::pin(stdout.compat()) })
|
Ok(ExtPort {
|
||||||
|
input: Box::pin(stdin.compat_write()),
|
||||||
|
output: Box::pin(stdout.compat()),
|
||||||
|
drop_trigger: Box::new(()),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::error::{OrcErrv, OrcRes};
|
use orchid_base::{ClientExt, Comment, IStr, OrcErrv, OrcRes, SrcRange, Sym, es, ttv_from_api};
|
||||||
use orchid_base::interner::{IStr, es};
|
|
||||||
use orchid_base::location::SrcRange;
|
|
||||||
use orchid_base::name::Sym;
|
|
||||||
use orchid_base::parse::Comment;
|
|
||||||
use orchid_base::reqnot::ClientExt;
|
|
||||||
use orchid_base::tree::ttv_from_api;
|
|
||||||
use substack::Substack;
|
use substack::Substack;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
|
use crate::expr::ExprFromApiCtx;
|
||||||
use crate::expr_store::ExprStore;
|
use crate::expr_store::ExprStore;
|
||||||
use crate::parse::HostParseCtx;
|
use crate::parse::HostParseCtx;
|
||||||
use crate::parsed::{
|
use crate::parsed::{
|
||||||
@@ -59,7 +54,7 @@ impl Parser {
|
|||||||
sys: &self.system,
|
sys: &self.system,
|
||||||
})
|
})
|
||||||
.await,
|
.await,
|
||||||
Err(e) => Err(OrcErrv::from_api(&e).await),
|
Err(e) => Err(OrcErrv::from_api(e).await),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,8 +77,8 @@ async fn conv(
|
|||||||
api::ParsedLineKind::Member(api::ParsedMember { name, exported, kind }) =>
|
api::ParsedLineKind::Member(api::ParsedMember { name, exported, kind }) =>
|
||||||
(name, exported, kind),
|
(name, exported, kind),
|
||||||
api::ParsedLineKind::Recursive(rec) => {
|
api::ParsedLineKind::Recursive(rec) => {
|
||||||
let tokens =
|
let mut cx = ExprFromApiCtx { ctx: ctx.sys.ctx().clone(), sys: ctx.sys.id() };
|
||||||
ttv_from_api(rec, ctx.ext_exprs, &mut ctx.sys.ctx().clone(), ctx.src_path).await;
|
let tokens = ttv_from_api(rec, ctx.ext_exprs, &mut cx, ctx.src_path).await;
|
||||||
items.extend(callback(module.clone(), tokens).await?);
|
items.extend(callback(module.clone(), tokens).await?);
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use std::collections::VecDeque;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use derive_destructure::destructure;
|
use derive_destructure::destructure;
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
@@ -9,15 +10,10 @@ use futures_locks::RwLock;
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use memo_map::MemoMap;
|
use memo_map::MemoMap;
|
||||||
use orchid_base::char_filter::char_filter_match;
|
use orchid_base::{
|
||||||
use orchid_base::error::{OrcRes, mk_errv_floating};
|
Client, ClientExt, FmtCtx, FmtUnit, Format, IStr, IteratorPrint, NameLike, OrcRes, Sym, VName,
|
||||||
use orchid_base::format::{FmtCtx, FmtUnit, Format};
|
VPath, char_filter_match, es, is, log, mk_errv_floating, stash,
|
||||||
use orchid_base::interner::{IStr, es, is};
|
};
|
||||||
use orchid_base::iter_utils::IteratorPrint;
|
|
||||||
use orchid_base::logging::log;
|
|
||||||
use orchid_base::name::{NameLike, Sym, VName, VPath};
|
|
||||||
use orchid_base::reqnot::{Client, ClientExt};
|
|
||||||
use orchid_base::stash::stash;
|
|
||||||
use ordered_float::NotNan;
|
use ordered_float::NotNan;
|
||||||
use substack::{Stackframe, Substack};
|
use substack::{Stackframe, Substack};
|
||||||
|
|
||||||
@@ -129,10 +125,10 @@ impl System {
|
|||||||
}
|
}
|
||||||
pub(crate) fn drop_atom(&self, dropped_atom_id: api::AtomId) {
|
pub(crate) fn drop_atom(&self, dropped_atom_id: api::AtomId) {
|
||||||
let this = self.0.clone();
|
let this = self.0.clone();
|
||||||
let _ = self.0.ctx.spawn(Box::pin(async move {
|
let _ = self.0.ctx.spawn(Duration::ZERO, async move {
|
||||||
this.ext.client().request(api::AtomDrop(this.id, dropped_atom_id)).await.unwrap();
|
this.ext.client().request(api::AtomDrop(this.id, dropped_atom_id)).await.unwrap();
|
||||||
this.owned_atoms.write().await.remove(&dropped_atom_id);
|
this.owned_atoms.write().await.remove(&dropped_atom_id);
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn downgrade(&self) -> WeakSystem {
|
pub fn downgrade(&self) -> WeakSystem {
|
||||||
|
|||||||
23
orchid-host/src/task_set.rs
Normal file
23
orchid-host/src/task_set.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use orchid_base::IdStore;
|
||||||
|
|
||||||
|
use crate::ctx::JoinHandle;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TaskSet {
|
||||||
|
pub tasks: Rc<RefCell<Option<IdStore<Box<dyn JoinHandle>>>>>,
|
||||||
|
}
|
||||||
|
impl TaskSet {
|
||||||
|
pub fn with<R>(&self, f: impl FnOnce(&mut IdStore<Box<dyn JoinHandle>>) -> R) -> Option<R> {
|
||||||
|
self.tasks.borrow_mut().as_mut().map(f)
|
||||||
|
}
|
||||||
|
pub fn abort_all(&self) {
|
||||||
|
let Some(tasks) = self.tasks.borrow_mut().take() else { return };
|
||||||
|
tasks.into_iter().for_each(|(_, task)| task.abort());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Default for TaskSet {
|
||||||
|
fn default() -> Self { Self { tasks: Rc::new(RefCell::new(Some(IdStore::default()))) } }
|
||||||
|
}
|
||||||
@@ -12,17 +12,15 @@ use hashbrown::HashMap;
|
|||||||
use hashbrown::hash_map::Entry;
|
use hashbrown::hash_map::Entry;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use memo_map::MemoMap;
|
use memo_map::MemoMap;
|
||||||
use orchid_base::clone;
|
use orchid_base::{
|
||||||
use orchid_base::error::{OrcRes, mk_errv, report};
|
ClientExt, CodeGenInfo, IStr, IStrv, NameLike, OrcRes, Pos, Sym, VPath, clone, es, is, iv,
|
||||||
use orchid_base::interner::{IStr, IStrv, es, is, iv};
|
mk_errv, report,
|
||||||
use orchid_base::location::{CodeGenInfo, Pos};
|
};
|
||||||
use orchid_base::name::{NameLike, Sym, VPath};
|
|
||||||
use orchid_base::reqnot::ClientExt;
|
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::ctx::Ctx;
|
use crate::ctx::Ctx;
|
||||||
use crate::dealias::{ChildErrorKind, Tree, absolute_path, resolv_glob, walk};
|
use crate::dealias::{ChildErrorKind, Tree, absolute_path, resolv_glob, walk};
|
||||||
use crate::expr::{Expr, PathSetBuilder};
|
use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder};
|
||||||
use crate::parsed::{ItemKind, ParsedMemberKind, ParsedModule};
|
use crate::parsed::{ItemKind, ParsedMemberKind, ParsedModule};
|
||||||
use crate::system::System;
|
use crate::system::System;
|
||||||
|
|
||||||
@@ -89,7 +87,8 @@ impl Root {
|
|||||||
for (path, sys_id, pc_id) in deferred_consts {
|
for (path, sys_id, pc_id) in deferred_consts {
|
||||||
let sys = this.ctx.system_inst(sys_id).await.expect("System dropped since parsing");
|
let sys = this.ctx.system_inst(sys_id).await.expect("System dropped since parsing");
|
||||||
let api_expr = sys.client().request(api::FetchParsedConst(sys.id(), pc_id)).await.unwrap();
|
let api_expr = sys.client().request(api::FetchParsedConst(sys.id(), pc_id)).await.unwrap();
|
||||||
let expr = Expr::from_api(&api_expr, PathSetBuilder::new(), this.ctx.clone()).await;
|
let cx = ExprFromApiCtx { sys: sys_id, ctx: this.ctx.clone() };
|
||||||
|
let expr = Expr::from_api(api_expr, PathSetBuilder::new(), cx).await;
|
||||||
new.0.write().await.consts.insert(path, expr);
|
new.0.write().await.consts.insert(path, expr);
|
||||||
}
|
}
|
||||||
new
|
new
|
||||||
@@ -176,7 +175,8 @@ impl Module {
|
|||||||
api::MemberKind::Lazy(id) =>
|
api::MemberKind::Lazy(id) =>
|
||||||
(Some(LazyMemberHandle { id, sys: ctx.sys.id(), path: name.clone() }), None),
|
(Some(LazyMemberHandle { id, sys: ctx.sys.id(), path: name.clone() }), None),
|
||||||
api::MemberKind::Const(val) => {
|
api::MemberKind::Const(val) => {
|
||||||
let expr = Expr::from_api(&val, PathSetBuilder::new(), ctx.sys.ctx().clone()).await;
|
let cx = ExprFromApiCtx { ctx: ctx.sys.ctx().clone(), sys: ctx.sys.id() };
|
||||||
|
let expr = Expr::from_api(val, PathSetBuilder::new(), cx).await;
|
||||||
ctx.consts.insert(name.clone(), expr);
|
ctx.consts.insert(name.clone(), expr);
|
||||||
(None, Some(MemberKind::Const))
|
(None, Some(MemberKind::Const))
|
||||||
},
|
},
|
||||||
@@ -456,7 +456,8 @@ impl LazyMemberHandle {
|
|||||||
let sys = ctx.system_inst(self.sys).await.expect("Missing system for lazy member");
|
let sys = ctx.system_inst(self.sys).await.expect("Missing system for lazy member");
|
||||||
match sys.get_tree(self.id).await {
|
match sys.get_tree(self.id).await {
|
||||||
api::MemberKind::Const(c) => {
|
api::MemberKind::Const(c) => {
|
||||||
let expr = Expr::from_api(&c, PathSetBuilder::new(), ctx.clone()).await;
|
let ctx = ExprFromApiCtx { sys: sys.id(), ctx: ctx.clone() };
|
||||||
|
let expr = Expr::from_api(c, PathSetBuilder::new(), ctx).await;
|
||||||
let (.., path) = self.destructure();
|
let (.., path) = self.destructure();
|
||||||
consts.insert(path, expr);
|
consts.insert(path, expr);
|
||||||
MemberKind::Const
|
MemberKind::Const
|
||||||
|
|||||||
@@ -13,8 +13,10 @@ name = "orchid_std"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
async-event = "0.2.1"
|
||||||
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
||||||
async-once-cell = "0.5.4"
|
async-once-cell = "0.5.4"
|
||||||
|
chrono = "0.4.43"
|
||||||
futures = { version = "0.3.31", features = ["std"], default-features = false }
|
futures = { version = "0.3.31", features = ["std"], default-features = false }
|
||||||
hashbrown = "0.16.1"
|
hashbrown = "0.16.1"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use orchid_extension::dylib_main;
|
|||||||
use orchid_extension::entrypoint::ExtensionBuilder;
|
use orchid_extension::entrypoint::ExtensionBuilder;
|
||||||
|
|
||||||
pub fn builder() -> ExtensionBuilder {
|
pub fn builder() -> ExtensionBuilder {
|
||||||
ExtensionBuilder::new("orchid-std::main").system(StdSystem).system(MacroSystem)
|
ExtensionBuilder::new("orchid-std::main").system(StdSystem::default()).system(MacroSystem)
|
||||||
}
|
}
|
||||||
|
|
||||||
dylib_main! { builder() }
|
dylib_main! { builder() }
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_base::format::fmt;
|
use orchid_base::fmt;
|
||||||
use orchid_extension::atom::{Atomic, TAtom};
|
|
||||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant, own};
|
|
||||||
use orchid_extension::conv::ToExpr;
|
use orchid_extension::conv::ToExpr;
|
||||||
use orchid_extension::coroutine_exec::exec;
|
use orchid_extension::coroutine_exec::exec;
|
||||||
use orchid_extension::expr::Expr;
|
use orchid_extension::expr::Expr;
|
||||||
use orchid_extension::gen_expr::{GExpr, new_atom};
|
use orchid_extension::gen_expr::new_atom;
|
||||||
|
use orchid_extension::{Atomic, OwnedAtom, OwnedVariant, TAtom};
|
||||||
|
|
||||||
use crate::macros::mactree::{MacTok, MacTree};
|
use crate::macros::mactree::{MacTok, MacTree};
|
||||||
|
|
||||||
@@ -25,7 +24,7 @@ impl OwnedAtom for InstantiateTplCall {
|
|||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
type Refs = Never;
|
type Refs = Never;
|
||||||
// Technically must be supported but shouldn't actually ever be called
|
// Technically must be supported but shouldn't actually ever be called
|
||||||
async fn call_ref(&self, arg: Expr) -> GExpr {
|
async fn call_ref(&self, arg: Expr) -> impl ToExpr {
|
||||||
if !self.argv.is_empty() {
|
if !self.argv.is_empty() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Copying partially applied instantiate_tpl call. This is an internal value.\
|
"Copying partially applied instantiate_tpl call. This is an internal value.\
|
||||||
@@ -34,11 +33,11 @@ impl OwnedAtom for InstantiateTplCall {
|
|||||||
}
|
}
|
||||||
self.clone().call(arg).await
|
self.clone().call(arg).await
|
||||||
}
|
}
|
||||||
async fn call(mut self, arg: Expr) -> GExpr {
|
async fn call(mut self, arg: Expr) -> impl ToExpr {
|
||||||
exec(async move |mut h| {
|
exec(async move |mut h| {
|
||||||
match h.exec::<TAtom<MacTree>>(arg.clone()).await {
|
match h.exec::<TAtom<MacTree>>(arg.clone()).await {
|
||||||
Err(_) => panic!("Expected a macro param, found {}", fmt(&arg).await),
|
Err(_) => panic!("Expected a macro param, found {}", fmt(&arg).await),
|
||||||
Ok(t) => self.argv.push(own(&t).await),
|
Ok(t) => self.argv.push(t.own().await),
|
||||||
};
|
};
|
||||||
if self.argv.len() < self.argc {
|
if self.argv.len() < self.argc {
|
||||||
return new_atom(self);
|
return new_atom(self);
|
||||||
@@ -52,7 +51,5 @@ impl OwnedAtom for InstantiateTplCall {
|
|||||||
new_atom(ret)
|
new_atom(ret)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.to_gen()
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,13 @@ use std::pin::pin;
|
|||||||
use futures::{FutureExt, StreamExt, stream};
|
use futures::{FutureExt, StreamExt, stream};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::error::{OrcRes, report, with_reporter};
|
use orchid_base::{
|
||||||
use orchid_base::interner::is;
|
Comment, OrcRes, Paren, Parsed, Snippet, Sym, expect_tok, is, report, sym, token_errv,
|
||||||
use orchid_base::name::Sym;
|
try_pop_no_fluff, with_reporter,
|
||||||
use orchid_base::parse::{Comment, Parsed, Snippet, expect_tok, token_errv, try_pop_no_fluff};
|
};
|
||||||
use orchid_base::sym;
|
use orchid_extension::TAtom;
|
||||||
use orchid_base::tree::Paren;
|
|
||||||
use orchid_extension::atom::TAtom;
|
|
||||||
use orchid_extension::conv::TryFromExpr;
|
use orchid_extension::conv::TryFromExpr;
|
||||||
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
|
use orchid_extension::gen_expr::{call, new_atom};
|
||||||
use orchid_extension::parser::{ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser};
|
use orchid_extension::parser::{ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser};
|
||||||
|
|
||||||
use crate::macros::mactree::{MacTok, MacTree, MacTreeSeq};
|
use crate::macros::mactree::{MacTok, MacTree, MacTreeSeq};
|
||||||
@@ -40,7 +38,7 @@ impl Parser for LetLine {
|
|||||||
Ok(vec![ParsedLine::cnst(&line.sr(), &comments, exported, name, async move |ctx| {
|
Ok(vec![ParsedLine::cnst(&line.sr(), &comments, exported, name, async move |ctx| {
|
||||||
let macro_input =
|
let macro_input =
|
||||||
MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&aliased, &ctx)).await?).at(sr.pos());
|
MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&aliased, &ctx)).await?).at(sr.pos());
|
||||||
Ok(call(sym_ref(sym!(macros::resolve)), [new_atom(macro_input)]))
|
Ok(call(sym!(macros::resolve), new_atom(macro_input)))
|
||||||
})])
|
})])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +64,7 @@ pub async fn dealias_mac_v(aliased: &MacTreeSeq, ctx: &ConstCtx) -> MacTreeSeq {
|
|||||||
pub async fn parse_tokv(line: PSnippet<'_>) -> MacTreeSeq {
|
pub async fn parse_tokv(line: PSnippet<'_>) -> MacTreeSeq {
|
||||||
if let Some((idx, arg)) = line.iter().enumerate().find_map(|(i, x)| Some((i, x.as_lambda()?))) {
|
if let Some((idx, arg)) = line.iter().enumerate().find_map(|(i, x)| Some((i, x.as_lambda()?))) {
|
||||||
let (head, lambda) = line.split_at(idx as u32);
|
let (head, lambda) = line.split_at(idx as u32);
|
||||||
let (_, body) = lambda.pop_front().unwrap();
|
let (_, body) = lambda.split_first().unwrap();
|
||||||
let body = parse_tokv(body).boxed_local().await;
|
let body = parse_tokv(body).boxed_local().await;
|
||||||
let mut all = parse_tokv_no_lambdas(&head).await;
|
let mut all = parse_tokv_no_lambdas(&head).await;
|
||||||
match parse_tok(arg).await {
|
match parse_tok(arg).await {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use orchid_base::sym;
|
use orchid_base::sym;
|
||||||
use orchid_extension::atom::TAtom;
|
use orchid_extension::TAtom;
|
||||||
use orchid_extension::atom_owned::own;
|
|
||||||
use orchid_extension::coroutine_exec::exec;
|
use orchid_extension::coroutine_exec::exec;
|
||||||
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
|
use orchid_extension::gen_expr::{call, new_atom};
|
||||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||||
|
|
||||||
use crate::macros::mactree::MacTree;
|
use crate::macros::mactree::MacTree;
|
||||||
@@ -12,37 +11,37 @@ use crate::{HomoTpl, UntypedTuple};
|
|||||||
|
|
||||||
pub async fn gen_macro_lib() -> Vec<GenMember> {
|
pub async fn gen_macro_lib() -> Vec<GenMember> {
|
||||||
prefix("macros", [
|
prefix("macros", [
|
||||||
fun(true, "resolve", async |tpl: TAtom<MacTree>| resolve(own(&tpl).await).await),
|
fun(true, "resolve", async |tpl: TAtom<MacTree>| resolve(tpl.own().await).await),
|
||||||
prefix("common", [
|
prefix("common", [
|
||||||
build_macro(None, ["..", "_", "="]).finish(),
|
build_macro(None, ["..", "_", "="]).finish(),
|
||||||
build_macro(Some(1), ["+"])
|
build_macro(Some(1), ["+"])
|
||||||
.rule(mactreev!("...$" lhs 1 macros::common::+ "...$" rhs 0), [async |[lhs, rhs]| {
|
.rule(mactreev!("...$" lhs 1 macros::common::+ "...$" rhs 0), [async |[lhs, rhs]| {
|
||||||
call(sym_ref(sym!(std::ops::add::resolve)), [resolve(lhs).await, resolve(rhs).await])
|
call(sym!(std::ops::add::resolve), (resolve(lhs).await, resolve(rhs).await)).await
|
||||||
}])
|
}])
|
||||||
.finish(),
|
.finish(),
|
||||||
build_macro(Some(1), ["-"])
|
build_macro(Some(1), ["-"])
|
||||||
.rule(mactreev!("...$" lhs 1 macros::common::- "...$" rhs 0), [async |[lhs, rhs]| {
|
.rule(mactreev!("...$" lhs 1 macros::common::- "...$" rhs 0), [async |[lhs, rhs]| {
|
||||||
call(sym_ref(sym!(std::ops::sub::resolve)), [resolve(lhs).await, resolve(rhs).await])
|
call(sym!(std::ops::sub::resolve), (resolve(lhs).await, resolve(rhs).await)).await
|
||||||
}])
|
}])
|
||||||
.finish(),
|
.finish(),
|
||||||
build_macro(Some(2), ["*"])
|
build_macro(Some(2), ["*"])
|
||||||
.rule(mactreev!("...$" lhs 1 macros::common::* "...$" rhs 0), [async |[lhs, rhs]| {
|
.rule(mactreev!("...$" lhs 1 macros::common::* "...$" rhs 0), [async |[lhs, rhs]| {
|
||||||
call(sym_ref(sym!(std::ops::mul::resolve)), [resolve(lhs).await, resolve(rhs).await])
|
call(sym!(std::ops::mul::resolve), (resolve(lhs).await, resolve(rhs).await)).await
|
||||||
}])
|
}])
|
||||||
.finish(),
|
.finish(),
|
||||||
build_macro(Some(2), ["/"])
|
build_macro(Some(2), ["/"])
|
||||||
.rule(mactreev!("...$" lhs 1 macros::common::/ "...$" rhs 0), [async |[lhs, rhs]| {
|
.rule(mactreev!("...$" lhs 1 macros::common::/ "...$" rhs 0), [async |[lhs, rhs]| {
|
||||||
call(sym_ref(sym!(std::ops::div::resolve)), [resolve(lhs).await, resolve(rhs).await])
|
call(sym!(std::ops::div::resolve), (resolve(lhs).await, resolve(rhs).await)).await
|
||||||
}])
|
}])
|
||||||
.finish(),
|
.finish(),
|
||||||
build_macro(Some(2), ["%"])
|
build_macro(Some(2), ["%"])
|
||||||
.rule(mactreev!("...$" lhs 1 macros::common::% "...$" rhs 0), [async |[lhs, rhs]| {
|
.rule(mactreev!("...$" lhs 1 macros::common::% "...$" rhs 0), [async |[lhs, rhs]| {
|
||||||
call(sym_ref(sym!(std::ops::mod::resolve)), [resolve(lhs).await, resolve(rhs).await])
|
call(sym!(std::ops::mod::resolve), (resolve(lhs).await, resolve(rhs).await)).await
|
||||||
}])
|
}])
|
||||||
.finish(),
|
.finish(),
|
||||||
build_macro(Some(3), ["."])
|
build_macro(Some(3), ["."])
|
||||||
.rule(mactreev!("...$" lhs 1 macros::common::. "...$" rhs 0), [async |[lhs, rhs]| {
|
.rule(mactreev!("...$" lhs 1 macros::common::. "...$" rhs 0), [async |[lhs, rhs]| {
|
||||||
call(sym_ref(sym!(std::ops::get::resolve)), [resolve(lhs).await, resolve(rhs).await])
|
call(sym!(std::ops::get::resolve), (resolve(lhs).await, resolve(rhs).await)).await
|
||||||
}])
|
}])
|
||||||
.finish(),
|
.finish(),
|
||||||
build_macro(None, ["comma_list", ","])
|
build_macro(None, ["comma_list", ","])
|
||||||
|
|||||||
@@ -4,16 +4,13 @@ use async_fn_stream::stream;
|
|||||||
use async_once_cell::OnceCell;
|
use async_once_cell::OnceCell;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::error::{OrcRes, mk_errv, report, with_reporter};
|
use orchid_base::{
|
||||||
use orchid_base::interner::is;
|
Comment, OrcRes, Paren, Parsed, Snippet, Token, clone, expect_end, expect_tok, is, line_items,
|
||||||
use orchid_base::parse::{
|
mk_errv, report, sym, token_errv, try_pop_no_fluff, with_reporter,
|
||||||
Comment, Parsed, Snippet, expect_end, expect_tok, line_items, token_errv, try_pop_no_fluff,
|
|
||||||
};
|
};
|
||||||
use orchid_base::tree::{Paren, Token};
|
use orchid_extension::TAtom;
|
||||||
use orchid_base::{clone, sym};
|
|
||||||
use orchid_extension::atom::TAtom;
|
|
||||||
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
use orchid_extension::conv::{ToExpr, TryFromExpr};
|
||||||
use orchid_extension::gen_expr::{call, new_atom, sym_ref};
|
use orchid_extension::gen_expr::{call, new_atom};
|
||||||
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
|
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
|
||||||
|
|
||||||
use crate::macros::let_line::{dealias_mac_v, parse_tokv};
|
use crate::macros::let_line::{dealias_mac_v, parse_tokv};
|
||||||
@@ -133,7 +130,7 @@ impl Parser for MacroLine {
|
|||||||
let macro_input =
|
let macro_input =
|
||||||
MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&body_mactree, &ctx)).await?)
|
MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&body_mactree, &ctx)).await?)
|
||||||
.at(body_sr.pos());
|
.at(body_sr.pos());
|
||||||
Ok(call(sym_ref(sym!(macros::resolve)), [new_atom(macro_input)]))
|
Ok(call(sym!(macros::resolve), new_atom(macro_input)))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
let mac_cell = Rc::new(OnceCell::new());
|
let mac_cell = Rc::new(OnceCell::new());
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::{Receipt, ReqHandle, Sym, sym};
|
||||||
use orchid_base::reqnot::{Receipt, ReqHandle};
|
use orchid_extension::{AtomOps, AtomicFeatures};
|
||||||
use orchid_base::sym;
|
|
||||||
use orchid_extension::atom::{AtomDynfo, AtomicFeatures};
|
|
||||||
use orchid_extension::lexer::LexerObj;
|
use orchid_extension::lexer::LexerObj;
|
||||||
use orchid_extension::other_system::SystemHandle;
|
use orchid_extension::other_system::SystemHandle;
|
||||||
use orchid_extension::parser::ParserObj;
|
use orchid_extension::parser::ParserObj;
|
||||||
@@ -34,20 +32,22 @@ impl SystemCtor for MacroSystem {
|
|||||||
impl SystemCard for MacroSystem {
|
impl SystemCard for MacroSystem {
|
||||||
type Ctor = Self;
|
type Ctor = Self;
|
||||||
type Req = Never;
|
type Req = Never;
|
||||||
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>> {
|
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomOps>>> {
|
||||||
[
|
[
|
||||||
Some(InstantiateTplCall::dynfo()),
|
Some(InstantiateTplCall::ops()),
|
||||||
Some(MacTree::dynfo()),
|
Some(MacTree::ops()),
|
||||||
Some(Macro::dynfo()),
|
Some(Macro::ops()),
|
||||||
Some(PhAtom::dynfo()),
|
Some(PhAtom::ops()),
|
||||||
Some(MacroBodyArgCollector::dynfo()),
|
Some(MacroBodyArgCollector::ops()),
|
||||||
Some(MatcherAtom::dynfo()),
|
Some(MatcherAtom::ops()),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl System for MacroSystem {
|
impl System for MacroSystem {
|
||||||
async fn request<'a>(_: Box<dyn ReqHandle<'a> + 'a>, req: Never) -> Receipt<'a> { match req {} }
|
async fn request<'a>(&self, _: Box<dyn ReqHandle<'a> + 'a>, req: Never) -> Receipt<'a> {
|
||||||
async fn prelude() -> Vec<Sym> {
|
match req {}
|
||||||
|
}
|
||||||
|
async fn prelude(&self) -> Vec<Sym> {
|
||||||
vec![
|
vec![
|
||||||
sym!(macros::common::+),
|
sym!(macros::common::+),
|
||||||
sym!(macros::common::*),
|
sym!(macros::common::*),
|
||||||
@@ -65,9 +65,9 @@ impl System for MacroSystem {
|
|||||||
sym!(std::fn::[|>]),
|
sym!(std::fn::[|>]),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
fn lexers() -> Vec<LexerObj> { vec![&MacTreeLexer, &PhLexer] }
|
fn lexers(&self) -> Vec<LexerObj> { vec![&MacTreeLexer, &PhLexer] }
|
||||||
fn parsers() -> Vec<ParserObj> { vec![&LetLine, &MacroLine] }
|
fn parsers(&self) -> Vec<ParserObj> { vec![&LetLine, &MacroLine] }
|
||||||
async fn env() -> Vec<GenMember> {
|
async fn env(&self) -> Vec<GenMember> {
|
||||||
merge_trivial([gen_macro_lib().await, gen_std_macro_lib().await, gen_match_macro_lib().await])
|
merge_trivial([gen_macro_lib().await, gen_std_macro_lib().await, gen_match_macro_lib().await])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ use std::borrow::Cow;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_base::interner::IStr;
|
use orchid_base::IStr;
|
||||||
use orchid_base::name::Sym;
|
use orchid_base::Sym;
|
||||||
use orchid_extension::atom::Atomic;
|
use orchid_extension::Atomic;
|
||||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant};
|
use orchid_extension::{OwnedAtom, OwnedVariant};
|
||||||
|
|
||||||
use crate::macros::mactree::MacTreeSeq;
|
use crate::macros::mactree::MacTreeSeq;
|
||||||
use crate::macros::rule::matcher::Matcher;
|
use crate::macros::rule::matcher::Matcher;
|
||||||
|
|||||||
@@ -6,16 +6,12 @@ use futures::FutureExt;
|
|||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use orchid_api_derive::Coding;
|
use orchid_api_derive::Coding;
|
||||||
use orchid_base::error::OrcErrv;
|
use orchid_base::{
|
||||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
|
FmtCtx, FmtUnit, Format, IStr, OrcErrv, Paren, Pos, Sym, Variants, indent, tl_cache,
|
||||||
use orchid_base::interner::IStr;
|
};
|
||||||
use orchid_base::location::Pos;
|
use orchid_extension::Atomic;
|
||||||
use orchid_base::name::Sym;
|
|
||||||
use orchid_base::tl_cache;
|
|
||||||
use orchid_base::tree::{Paren, indent};
|
|
||||||
use orchid_extension::atom::Atomic;
|
|
||||||
use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant};
|
|
||||||
use orchid_extension::expr::Expr;
|
use orchid_extension::expr::Expr;
|
||||||
|
use orchid_extension::{OwnedAtom, OwnedVariant};
|
||||||
|
|
||||||
fn union_rc_sets(seq: impl IntoIterator<Item = Rc<HashSet<Sym>>>) -> Rc<HashSet<Sym>> {
|
fn union_rc_sets(seq: impl IntoIterator<Item = Rc<HashSet<Sym>>>) -> Rc<HashSet<Sym>> {
|
||||||
let mut acc = Rc::<HashSet<Sym>>::default();
|
let mut acc = Rc::<HashSet<Sym>>::default();
|
||||||
@@ -190,8 +186,10 @@ impl Format for MacTok {
|
|||||||
}
|
}
|
||||||
.units([body.print(c).await]),
|
.units([body.print(c).await]),
|
||||||
Self::Slot => "$SLOT".into(),
|
Self::Slot => "$SLOT".into(),
|
||||||
Self::Bottom(err) if err.len() == 1 => format!("Bottom({}) ", err.one().unwrap()).into(),
|
Self::Bottom(err) => match err.one() {
|
||||||
Self::Bottom(err) => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
|
Some(err) => format!("Bottom({err}) ").into(),
|
||||||
|
None => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,7 +198,7 @@ pub async fn mtreev_fmt<'b>(
|
|||||||
v: impl IntoIterator<Item = &'b MacTree>,
|
v: impl IntoIterator<Item = &'b MacTree>,
|
||||||
c: &(impl FmtCtx + ?Sized),
|
c: &(impl FmtCtx + ?Sized),
|
||||||
) -> FmtUnit {
|
) -> FmtUnit {
|
||||||
FmtUnit::sequence("", " ", "", None, join_all(v.into_iter().map(|t| t.print(c))).await)
|
FmtUnit::sequence("", " ", "", true, join_all(v.into_iter().map(|t| t.print(c))).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user