Compare commits
10 Commits
0909524dee
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 883d56143f | |||
| 759497ee70 | |||
| 0bc7097c88 | |||
| 7f8c247d97 | |||
| 60c96964d9 | |||
| 6eed6b9831 | |||
| 286040c3ec | |||
| 23180b66e3 | |||
| b44f3c1832 | |||
| 9b4c7fa7d7 |
@@ -13,3 +13,12 @@ RUST_BACKTRACE = "1"
|
|||||||
|
|
||||||
[build]
|
[build]
|
||||||
# rustflags = ["-Znext-solver"]
|
# rustflags = ["-Znext-solver"]
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 0
|
||||||
|
debug = 2
|
||||||
|
strip = 'none'
|
||||||
|
debug-assertions = true
|
||||||
|
overflow-checks = true
|
||||||
|
lto = false
|
||||||
|
panic = 'abort'
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
name: Rust
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "main" ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ "main" ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Install rust toolchain
|
|
||||||
run: curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly
|
|
||||||
- name: Build
|
|
||||||
run: $HOME/.cargo/bin/cargo build --verbose
|
|
||||||
- name: Run tests
|
|
||||||
run: $HOME/.cargo/bin/cargo test --verbose
|
|
||||||
- name: Clippy
|
|
||||||
run: $HOME/.cargo/bin/cargo clippy
|
|
||||||
- name: Formatting
|
|
||||||
run: $HOME/.cargo/bin/cargo +nightly fmt --check
|
|
||||||
- name: No unqualified imports from orchid_api
|
|
||||||
run: $HOME/.cargo/bin/cargo xtask check-api-refs
|
|
||||||
436
Cargo.lock
generated
436
Cargo.lock
generated
@@ -39,9 +39,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.18"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"anstyle-parse",
|
"anstyle-parse",
|
||||||
@@ -54,15 +54,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.10"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-parse"
|
name = "anstyle-parse"
|
||||||
version = "0.2.6"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
]
|
]
|
||||||
@@ -92,6 +92,15 @@ version = "1.0.102"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ar_archive_writer"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b"
|
||||||
|
dependencies = [
|
||||||
|
"object",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
@@ -113,7 +122,7 @@ name = "async-fn-stream"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"test_executors",
|
"orchid-async-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -128,9 +137,9 @@ version = "0.1.89"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
|
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -199,9 +208,9 @@ checksum = "c2593a3b8b938bd68373196c9832f516be11fa487ef4ae745eb282e6a56a7244"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -233,8 +242,8 @@ version = "0.6.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
|
checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -297,9 +306,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.43"
|
version = "0.4.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118"
|
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
@@ -310,9 +319,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.54"
|
version = "4.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394"
|
checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -320,9 +329,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.54"
|
version = "4.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00"
|
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -332,21 +341,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.49"
|
version = "4.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
|
checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.7.4"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
|
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
@@ -411,9 +420,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctrlc"
|
name = "ctrlc"
|
||||||
version = "3.5.1"
|
version = "3.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73736a89c4aff73035ba2ed2e565061954da00d4970fc9ac25dcc85a2a20d790"
|
checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dispatch2",
|
"dispatch2",
|
||||||
"nix",
|
"nix",
|
||||||
@@ -497,9 +506,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.31"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@@ -512,9 +521,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.31"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
@@ -522,15 +531,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.31"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-executor"
|
name = "futures-executor"
|
||||||
version = "0.3.31"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
@@ -539,9 +548,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.31"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-locks"
|
name = "futures-locks"
|
||||||
@@ -556,32 +565,32 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-macro"
|
name = "futures-macro"
|
||||||
version = "0.3.31"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.31"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.31"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-util"
|
name = "futures-util"
|
||||||
version = "0.3.31"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@@ -591,7 +600,6 @@ dependencies = [
|
|||||||
"futures-task",
|
"futures-task",
|
||||||
"memchr",
|
"memchr",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -629,18 +637,6 @@ dependencies = [
|
|||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getrandom"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"r-efi 5.3.0",
|
|
||||||
"wasip2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
@@ -649,7 +645,7 @@ checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"r-efi 6.0.0",
|
"r-efi",
|
||||||
"rand_core 0.10.0",
|
"rand_core 0.10.0",
|
||||||
"wasip2",
|
"wasip2",
|
||||||
"wasip3",
|
"wasip3",
|
||||||
@@ -678,6 +674,12 @@ name = "hashbrown"
|
|||||||
version = "0.16.1"
|
version = "0.16.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
"equivalent",
|
"equivalent",
|
||||||
@@ -735,8 +737,8 @@ version = "0.7.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
|
checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -813,9 +815,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.175"
|
version = "0.2.186"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
@@ -933,13 +935,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "1.0.3"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -950,9 +952,9 @@ checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.30.1"
|
version = "0.31.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
|
checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@@ -995,10 +997,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
|
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "object"
|
||||||
version = "1.21.3"
|
version = "0.37.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oorandom"
|
name = "oorandom"
|
||||||
@@ -1014,8 +1025,8 @@ dependencies = [
|
|||||||
"itertools",
|
"itertools",
|
||||||
"orchid-api-derive",
|
"orchid-api-derive",
|
||||||
"orchid-api-traits",
|
"orchid-api-traits",
|
||||||
|
"orchid-async-utils",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"test_executors",
|
|
||||||
"unsync-pipe",
|
"unsync-pipe",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1025,9 +1036,9 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools",
|
"itertools",
|
||||||
"orchid-api-traits",
|
"orchid-api-traits",
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1060,7 +1071,7 @@ dependencies = [
|
|||||||
"derive_destructure",
|
"derive_destructure",
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"futures",
|
"futures",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.17.0",
|
||||||
"itertools",
|
"itertools",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"never",
|
"never",
|
||||||
@@ -1070,7 +1081,7 @@ dependencies = [
|
|||||||
"orchid-api-traits",
|
"orchid-api-traits",
|
||||||
"orchid-async-utils",
|
"orchid-async-utils",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"rand 0.10.0",
|
"rand 0.10.1",
|
||||||
"rand_chacha 0.10.0",
|
"rand_chacha 0.10.0",
|
||||||
"regex",
|
"regex",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
@@ -1087,11 +1098,12 @@ dependencies = [
|
|||||||
"async-event",
|
"async-event",
|
||||||
"async-fn-stream",
|
"async-fn-stream",
|
||||||
"async-once-cell",
|
"async-once-cell",
|
||||||
|
"chrono",
|
||||||
"derive_destructure",
|
"derive_destructure",
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-locks",
|
"futures-locks",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.17.0",
|
||||||
"include_dir",
|
"include_dir",
|
||||||
"itertools",
|
"itertools",
|
||||||
"konst",
|
"konst",
|
||||||
@@ -1122,10 +1134,11 @@ dependencies = [
|
|||||||
"async-fn-stream",
|
"async-fn-stream",
|
||||||
"async-once-cell",
|
"async-once-cell",
|
||||||
"bound",
|
"bound",
|
||||||
|
"chrono",
|
||||||
"derive_destructure",
|
"derive_destructure",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-locks",
|
"futures-locks",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.17.0",
|
||||||
"itertools",
|
"itertools",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libloading",
|
"libloading",
|
||||||
@@ -1134,6 +1147,7 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
"orchid-api",
|
"orchid-api",
|
||||||
"orchid-api-traits",
|
"orchid-api-traits",
|
||||||
|
"orchid-async-utils",
|
||||||
"orchid-base",
|
"orchid-base",
|
||||||
"orchid-extension",
|
"orchid-extension",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
@@ -1155,7 +1169,7 @@ dependencies = [
|
|||||||
"async-once-cell",
|
"async-once-cell",
|
||||||
"chrono",
|
"chrono",
|
||||||
"futures",
|
"futures",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.17.0",
|
||||||
"itertools",
|
"itertools",
|
||||||
"never",
|
"never",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -1184,18 +1198,22 @@ dependencies = [
|
|||||||
"ctrlc",
|
"ctrlc",
|
||||||
"futures",
|
"futures",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
"never",
|
||||||
"orchid-api",
|
"orchid-api",
|
||||||
"orchid-base",
|
"orchid-base",
|
||||||
|
"orchid-extension",
|
||||||
"orchid-host",
|
"orchid-host",
|
||||||
|
"stacker",
|
||||||
"substack",
|
"substack",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-float"
|
name = "ordered-float"
|
||||||
version = "5.1.0"
|
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 = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d"
|
checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
@@ -1225,9 +1243,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pastey"
|
name = "pastey"
|
||||||
version = "0.2.1"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b867cad97c0791bbd3aaa6472142568c6c9e8f71937e98379f584cfb0cf35bec"
|
checksum = "c5a797f0e07bdf071d15742978fc3128ec6c22891c31a3a931513263904c982a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
@@ -1235,12 +1253,6 @@ version = "0.2.16"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-utils"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.20"
|
version = "0.2.20"
|
||||||
@@ -1256,8 +1268,8 @@ version = "0.2.37"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1286,13 +1298,23 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.104"
|
version = "1.0.106"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0"
|
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "psm"
|
||||||
|
version = "0.1.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8"
|
||||||
|
dependencies = [
|
||||||
|
"ar_archive_writer",
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ptr_meta"
|
name = "ptr_meta"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
@@ -1308,8 +1330,8 @@ version = "0.1.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
|
checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1324,19 +1346,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.42"
|
version = "1.0.45"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "r-efi"
|
|
||||||
version = "5.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "r-efi"
|
name = "r-efi"
|
||||||
version = "6.0.0"
|
version = "6.0.0"
|
||||||
@@ -1362,19 +1378,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.9.2"
|
version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
|
||||||
dependencies = [
|
|
||||||
"rand_chacha 0.9.0",
|
|
||||||
"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 = [
|
dependencies = [
|
||||||
"chacha20",
|
"chacha20",
|
||||||
"getrandom 0.4.2",
|
"getrandom 0.4.2",
|
||||||
@@ -1391,16 +1397,6 @@ dependencies = [
|
|||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_chacha"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
|
||||||
dependencies = [
|
|
||||||
"ppv-lite86",
|
|
||||||
"rand_core 0.9.3",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_chacha"
|
name = "rand_chacha"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
@@ -1420,15 +1416,6 @@ dependencies = [
|
|||||||
"getrandom 0.2.15",
|
"getrandom 0.2.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.9.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom 0.3.4",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
@@ -1446,9 +1433,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.12.2"
|
version = "1.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -1484,9 +1471,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rkyv"
|
name = "rkyv"
|
||||||
version = "0.7.45"
|
version = "0.7.46"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b"
|
checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitvec",
|
"bitvec",
|
||||||
"bytecheck",
|
"bytecheck",
|
||||||
@@ -1502,20 +1489,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rkyv_derive"
|
name = "rkyv_derive"
|
||||||
version = "0.7.45"
|
version = "0.7.46"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0"
|
checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed"
|
name = "rust-embed"
|
||||||
version = "8.9.0"
|
version = "8.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca"
|
checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rust-embed-impl",
|
"rust-embed-impl",
|
||||||
"rust-embed-utils",
|
"rust-embed-utils",
|
||||||
@@ -1528,10 +1515,10 @@ version = "8.9.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5fa2c8c9e8711e10f9c4fd2d64317ef13feaab820a4c51541f1a8c8e2e851ab2"
|
checksum = "5fa2c8c9e8711e10f9c4fd2d64317ef13feaab820a4c51541f1a8c8e2e851ab2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"rust-embed-utils",
|
"rust-embed-utils",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1547,9 +1534,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust_decimal"
|
name = "rust_decimal"
|
||||||
version = "1.39.0"
|
version = "1.41.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "35affe401787a9bd846712274d97654355d21b2a2c092a3139aabe31e9022282"
|
checksum = "2ce901f9a19d251159075a4c37af514c3b8ef99c22e02dd8c19161cf397ee94a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"borsh",
|
"borsh",
|
||||||
@@ -1559,6 +1546,7 @@ dependencies = [
|
|||||||
"rkyv",
|
"rkyv",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1631,9 +1619,9 @@ version = "1.0.228"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1706,12 +1694,12 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.6.0"
|
version = "0.6.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
|
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1731,6 +1719,19 @@ dependencies = [
|
|||||||
"web-time",
|
"web-time",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stacker"
|
||||||
|
version = "0.1.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"psm",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stdio-perftest"
|
name = "stdio-perftest"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -1770,19 +1771,19 @@ version = "1.0.109"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.112"
|
version = "2.0.117"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4"
|
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1794,9 +1795,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "task-local"
|
name = "task-local"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b2c821daee0efdf6414970c8185a1c22e259a7ed87b2fd9f7d3c5f5503fd2863"
|
checksum = "2972044a9e5e448a506a7ff6f0d03b566d8ef4cd6918a58fc59835a0f8666626"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
@@ -1822,8 +1823,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b68fbf53af4a8dfdf0bd9192ab2f6fd42e5b59454bcde58fe1382a8677087732"
|
checksum = "b68fbf53af4a8dfdf0bd9192ab2f6fd42e5b59454bcde58fe1382a8677087732"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
"wasm-bindgen-test",
|
"wasm-bindgen-test",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1842,9 +1843,9 @@ version = "2.0.16"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
|
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1873,9 +1874,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.49.0"
|
version = "1.52.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
|
checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -1890,13 +1891,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "2.6.0"
|
version = "2.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1960,9 +1961,9 @@ version = "0.1.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2010,8 +2011,8 @@ version = "0.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b79e2e9c9ab44c6d7c20d5976961b47e8f49ac199154daa514b77cd1ab536625"
|
checksum = "b79e2e9c9ab44c6d7c20d5976961b47e8f49ac199154daa514b77cd1ab536625"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2035,9 +2036,9 @@ checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
version = "1.12.0"
|
version = "1.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
@@ -2058,8 +2059,9 @@ dependencies = [
|
|||||||
"futures",
|
"futures",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"itertools",
|
"itertools",
|
||||||
"rand 0.9.2",
|
"orchid-async-utils",
|
||||||
"rand_chacha 0.9.0",
|
"rand 0.10.1",
|
||||||
|
"rand_chacha 0.10.0",
|
||||||
"test_executors",
|
"test_executors",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2130,6 +2132,7 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustversion",
|
"rustversion",
|
||||||
|
"serde",
|
||||||
"wasm-bindgen-macro",
|
"wasm-bindgen-macro",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
@@ -2153,7 +2156,7 @@ version = "0.2.106"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
|
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"wasm-bindgen-macro-support",
|
"wasm-bindgen-macro-support",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2164,9 +2167,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
|
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2206,9 +2209,9 @@ version = "0.3.56"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7150335716dce6028bead2b848e72f47b45e7b9422f64cccdc23bedca89affc1"
|
checksum = "7150335716dce6028bead2b848e72f47b45e7b9422f64cccdc23bedca89affc1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2326,9 +2329,9 @@ version = "0.60.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2337,9 +2340,9 @@ version = "0.59.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2366,15 +2369,6 @@ dependencies = [
|
|||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.52.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.59.0"
|
version = "0.59.0"
|
||||||
@@ -2559,7 +2553,7 @@ dependencies = [
|
|||||||
"heck",
|
"heck",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
"wasm-metadata",
|
"wasm-metadata",
|
||||||
"wit-bindgen-core",
|
"wit-bindgen-core",
|
||||||
"wit-component",
|
"wit-component",
|
||||||
@@ -2573,9 +2567,9 @@ checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
"wit-bindgen-core",
|
"wit-bindgen-core",
|
||||||
"wit-bindgen-rust",
|
"wit-bindgen-rust",
|
||||||
]
|
]
|
||||||
@@ -2649,7 +2643,7 @@ version = "0.7.35"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 1.0.104",
|
"proc-macro2 1.0.106",
|
||||||
"quote 1.0.42",
|
"quote 1.0.45",
|
||||||
"syn 2.0.112",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures = { version = "0.3.31", features = ["std"], default-features = false }
|
futures = { version = "0.3.32", features = ["std"], default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
test_executors = "0.4.1"
|
orchid-async-utils = { path = "../orchid-async-utils" }
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ mod test {
|
|||||||
|
|
||||||
use futures::channel::mpsc::channel;
|
use futures::channel::mpsc::channel;
|
||||||
use futures::{Stream, StreamExt, TryStreamExt};
|
use futures::{Stream, StreamExt, TryStreamExt};
|
||||||
use test_executors::spin_on;
|
use orchid_async_utils::debug::spin_on;
|
||||||
|
|
||||||
use crate::{stream, try_stream};
|
use crate::{stream, try_stream};
|
||||||
|
|
||||||
|
|||||||
@@ -2,3 +2,14 @@ let user = r[ "foo" 1, "bar" t[3, 4] ]
|
|||||||
let _main = user.bar.1
|
let _main = user.bar.1
|
||||||
|
|
||||||
let main = "foo" + string::slice "hello" 1 3 + "bar"
|
let main = "foo" + string::slice "hello" 1 3 + "bar"
|
||||||
|
|
||||||
|
let io_main = (
|
||||||
|
stdio::get_stdout \stdout
|
||||||
|
std::stream::write_str stdout "Hello, World!\n"
|
||||||
|
(std::stream::flush stdout
|
||||||
|
(std::stream::close stdout
|
||||||
|
orchid::cmd::exit
|
||||||
|
\e e)
|
||||||
|
\e e)
|
||||||
|
\e e
|
||||||
|
)
|
||||||
2
orchid-api-derive/.cargo/config.toml
Normal file
2
orchid-api-derive/.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[profile.dev]
|
||||||
|
panic = 'unwind'
|
||||||
@@ -9,8 +9,8 @@ proc-macro = true
|
|||||||
# 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]
|
||||||
quote = "1.0.42"
|
quote = "1.0.45"
|
||||||
syn = { version = "2.0.112" }
|
syn = { version = "2.0.117" }
|
||||||
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||||
proc-macro2 = "1.0.104"
|
proc-macro2 = "1.0.106"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ 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"
|
chrono = "0.4.44"
|
||||||
futures = { version = "0.3.31", features = ["std"], default-features = false }
|
futures = { version = "0.3.32", features = ["std"], default-features = false }
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
never = "0.1.0"
|
never = "0.1.0"
|
||||||
ordered-float = "5.1.0"
|
ordered-float = "5.3.0"
|
||||||
|
|||||||
@@ -376,3 +376,13 @@ impl Encode for chrono::TimeDelta {
|
|||||||
self.subsec_nanos().encode(write).await
|
self.subsec_nanos().encode(write).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Decode for chrono::DateTime<chrono::Utc> {
|
||||||
|
async fn decode<R: AsyncRead + ?Sized>(read: Pin<&mut R>) -> io::Result<Self> {
|
||||||
|
Ok(Self::from_timestamp_micros(i64::decode(read).await?).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Encode for chrono::DateTime<chrono::Utc> {
|
||||||
|
async fn encode<W: AsyncWrite + ?Sized>(&self, write: Pin<&mut W>) -> io::Result<()> {
|
||||||
|
self.timestamp_micros().encode(write).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ 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]
|
||||||
ordered-float = "5.1.0"
|
ordered-float = "5.3.0"
|
||||||
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||||
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
|
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
|
||||||
futures = { version = "0.3.31", features = ["std"], default-features = false }
|
futures = { version = "0.3.32", features = ["std"], default-features = false }
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
|
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
test_executors = "0.4.1"
|
orchid-async-utils = { path = "../orchid-async-utils" }
|
||||||
|
|||||||
@@ -95,14 +95,18 @@ impl Request for DeserAtom {
|
|||||||
/// A request blindly routed to the system that provides an atom.
|
/// A request blindly routed to the system that provides an atom.
|
||||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
#[extends(AtomReq, HostExtReq)]
|
#[extends(AtomReq, HostExtReq)]
|
||||||
pub struct FinalFwded(pub Atom, pub TStrv, pub Vec<u8>);
|
pub struct Fwded(pub Atom, pub TStrv, pub Vec<u8>);
|
||||||
impl Request for FinalFwded {
|
impl Request for Fwded {
|
||||||
type Response = Option<Vec<u8>>;
|
type Response = Option<Vec<u8>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
#[extends(ExtHostReq)]
|
#[extends(ExtHostReq)]
|
||||||
pub struct Fwd(pub Atom, pub TStrv, pub Vec<u8>);
|
pub struct Fwd {
|
||||||
|
pub target: Atom,
|
||||||
|
pub method: TStrv,
|
||||||
|
pub body: Vec<u8>,
|
||||||
|
}
|
||||||
impl Request for Fwd {
|
impl Request for Fwd {
|
||||||
type Response = Option<Vec<u8>>;
|
type Response = Option<Vec<u8>>;
|
||||||
}
|
}
|
||||||
@@ -138,8 +142,7 @@ impl Request for ExtAtomPrint {
|
|||||||
pub enum AtomReq {
|
pub enum AtomReq {
|
||||||
CallRef(CallRef),
|
CallRef(CallRef),
|
||||||
FinalCall(FinalCall),
|
FinalCall(FinalCall),
|
||||||
FwdedRef(FinalFwded),
|
Fwded(Fwded),
|
||||||
FinalFwded(FinalFwded),
|
|
||||||
AtomPrint(AtomPrint),
|
AtomPrint(AtomPrint),
|
||||||
SerializeAtom(SerializeAtom),
|
SerializeAtom(SerializeAtom),
|
||||||
}
|
}
|
||||||
@@ -150,8 +153,7 @@ impl AtomReq {
|
|||||||
match self {
|
match self {
|
||||||
Self::CallRef(CallRef(a, ..))
|
Self::CallRef(CallRef(a, ..))
|
||||||
| Self::FinalCall(FinalCall(a, ..))
|
| Self::FinalCall(FinalCall(a, ..))
|
||||||
| Self::FwdedRef(FinalFwded(a, ..))
|
| Self::Fwded(Fwded(a, ..))
|
||||||
| Self::FinalFwded(FinalFwded(a, ..))
|
|
||||||
| Self::AtomPrint(AtomPrint(a))
|
| Self::AtomPrint(AtomPrint(a))
|
||||||
| Self::SerializeAtom(SerializeAtom(a)) => a,
|
| Self::SerializeAtom(SerializeAtom(a)) => a,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
//! the channel with the same protocol outlined in [crate::proto]
|
//! the channel with the same protocol outlined in [crate::proto]
|
||||||
|
|
||||||
use unsync_pipe::{Reader, Writer};
|
use unsync_pipe::{Reader, Writer};
|
||||||
|
|
||||||
/// !Send !Sync owned waker
|
/// !Send !Sync owned waker
|
||||||
///
|
///
|
||||||
/// This object is [Clone] for convenience but it has `drop` and no `clone` so
|
/// This object is [Clone] for convenience but it has `drop` and no `clone` so
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ 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, LocalAtom, Location, OrcError, SysId, TStrv};
|
use crate::{
|
||||||
|
Atom, ExtHostNotif, ExtHostReq, FormattingUnit, 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
|
||||||
@@ -104,11 +106,21 @@ impl Request for Inspect {
|
|||||||
type Response = Inspected;
|
type Response = Inspected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(ExprReq, ExtHostReq)]
|
||||||
|
pub struct ExprPrint {
|
||||||
|
pub target: ExprTicket,
|
||||||
|
}
|
||||||
|
impl Request for ExprPrint {
|
||||||
|
type Response = FormattingUnit;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
#[extends(ExtHostReq)]
|
#[extends(ExtHostReq)]
|
||||||
#[extendable]
|
#[extendable]
|
||||||
pub enum ExprReq {
|
pub enum ExprReq {
|
||||||
Inspect(Inspect),
|
Inspect(Inspect),
|
||||||
|
ExprPrint(ExprPrint),
|
||||||
Create(Create),
|
Create(Create),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -157,8 +157,8 @@ mod tests {
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use orchid_api_traits::enc_vec;
|
use orchid_api_traits::enc_vec;
|
||||||
|
use orchid_async_utils::debug::spin_on;
|
||||||
use ordered_float::NotNan;
|
use ordered_float::NotNan;
|
||||||
use test_executors::spin_on;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Logger;
|
use crate::Logger;
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures = { version = "0.3.31", default-features = false, features = [
|
futures = { version = "0.3.32", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"async-await",
|
"async-await",
|
||||||
] }
|
] }
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
task-local = "0.1.0"
|
task-local = "0.1.1"
|
||||||
|
|||||||
87
orchid-async-utils/src/cancel_cleanup.rs
Normal file
87
orchid-async-utils/src/cancel_cleanup.rs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
|
/// Future returned by [cancel_cleanup]
|
||||||
|
pub struct CancelCleanup<Fut: Future + Unpin, Fun: FnOnce(Fut)> {
|
||||||
|
/// Set to None when Ready
|
||||||
|
fut: Option<Fut>,
|
||||||
|
/// Only set to None in Drop
|
||||||
|
on_drop: Option<Fun>,
|
||||||
|
}
|
||||||
|
impl<Fut: Future + Unpin, Fun: FnOnce(Fut)> Future for CancelCleanup<Fut, Fun> {
|
||||||
|
type Output = Fut::Output;
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let Self { fut, .. } = unsafe { self.get_unchecked_mut() };
|
||||||
|
if let Some(future) = fut {
|
||||||
|
let future = unsafe { Pin::new_unchecked(future) };
|
||||||
|
let poll = future.poll(cx);
|
||||||
|
if poll.is_ready() {
|
||||||
|
*fut = None;
|
||||||
|
}
|
||||||
|
poll
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<Fut: Future + Unpin, Fun: FnOnce(Fut)> Drop for CancelCleanup<Fut, Fun> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(fut) = self.fut.take() {
|
||||||
|
(self.on_drop.take().unwrap())(fut)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle a Future's Drop. The callback is only called if the future has not
|
||||||
|
/// yet returned and would be cancelled, and it receives the future as an
|
||||||
|
/// argument
|
||||||
|
pub fn cancel_cleanup<Fut: Future + Unpin, Fun: FnOnce(Fut)>(
|
||||||
|
fut: Fut,
|
||||||
|
on_drop: Fun,
|
||||||
|
) -> CancelCleanup<Fut, Fun> {
|
||||||
|
CancelCleanup { fut: Some(fut), on_drop: Some(on_drop) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::pin::pin;
|
||||||
|
|
||||||
|
use futures::channel::mpsc;
|
||||||
|
use futures::future::join;
|
||||||
|
use futures::{SinkExt, StreamExt};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::debug::spin_on;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn called_on_drop() {
|
||||||
|
let mut called = false;
|
||||||
|
cancel_cleanup(pin!(async {}), |_| called = true);
|
||||||
|
assert!(called, "cleanup was called when the future was dropped");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn not_called_if_finished() {
|
||||||
|
spin_on(async {
|
||||||
|
let (mut req_in, mut req_out) = mpsc::channel(0);
|
||||||
|
let (mut rep_in, mut rep_out) = mpsc::channel(0);
|
||||||
|
join(
|
||||||
|
async {
|
||||||
|
req_out.next().await.unwrap();
|
||||||
|
rep_in.send(()).await.unwrap();
|
||||||
|
},
|
||||||
|
async {
|
||||||
|
cancel_cleanup(
|
||||||
|
pin!(async {
|
||||||
|
req_in.send(()).await.unwrap();
|
||||||
|
rep_out.next().await.unwrap();
|
||||||
|
}),
|
||||||
|
|_| panic!("Callback called on drop even though the future was finished"),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -143,27 +143,48 @@ pub fn eprint_stream_events<'a, S: Stream + 'a>(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SpinWaker(AtomicBool);
|
thread_local! {
|
||||||
|
static WAKE_LODUD: RefCell<bool> = const { RefCell::new(false) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Equivalent to [spin_on], but also logs on wake
|
||||||
|
pub fn spin_on_loud<Fut: Future>(fut: Fut) -> Fut::Output {
|
||||||
|
let prev = WAKE_LODUD.replace(true);
|
||||||
|
let ret = spin_on(fut);
|
||||||
|
WAKE_LODUD.set(prev);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SpinWaker {
|
||||||
|
repeat: AtomicBool,
|
||||||
|
}
|
||||||
impl Wake for SpinWaker {
|
impl Wake for SpinWaker {
|
||||||
fn wake(self: Arc<Self>) { self.0.store(true, Ordering::Relaxed); }
|
fn wake(self: Arc<Self>) {
|
||||||
|
self.repeat.store(true, Ordering::SeqCst);
|
||||||
|
if WAKE_LODUD.with_borrow(|k| *k) {
|
||||||
|
eprintln!("{Label} Triggered repeat for spin_on")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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. This is useful for deterministic tests
|
/// keeps synchronously waking itself. This is useful for deterministic tests
|
||||||
/// that don't contain side effects or threading.
|
/// that don't contain side effects or threading.
|
||||||
///
|
///
|
||||||
|
/// Use [spin_on_loud] to get messages on wake for debugging
|
||||||
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// If the future doesn't wake itself and doesn't settle.
|
/// If the future doesn't wake itself and doesn't settle.
|
||||||
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 spin_waker = Arc::new(SpinWaker { repeat: AtomicBool::new(false) });
|
||||||
let mut f = pin!(f);
|
let mut f = pin!(f);
|
||||||
let waker = repeat.clone().into();
|
let waker = spin_waker.clone().into();
|
||||||
let mut cx = Context::from_waker(&waker);
|
let mut cx = Context::from_waker(&waker);
|
||||||
loop {
|
loop {
|
||||||
match f.as_mut().poll(&mut cx) {
|
match f.as_mut().poll(&mut cx) {
|
||||||
Poll::Ready(t) => break t,
|
Poll::Ready(t) => break t,
|
||||||
Poll::Pending if repeat.0.swap(false, Ordering::Relaxed) => (),
|
Poll::Pending if spin_waker.repeat.swap(false, Ordering::SeqCst) => (),
|
||||||
Poll::Pending => panic!("The future did not exit and did not call its waker."),
|
Poll::Pending => panic!("The future did not exit and did not call its waker."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
pub mod debug;
|
pub mod debug;
|
||||||
|
mod cancel_cleanup;
|
||||||
|
pub use cancel_cleanup::*;
|
||||||
mod localset;
|
mod localset;
|
||||||
pub use localset::*;
|
pub use localset::*;
|
||||||
mod task_future;
|
mod task_future;
|
||||||
|
|||||||
@@ -1,48 +1,67 @@
|
|||||||
use std::collections::VecDeque;
|
use std::cell::RefCell;
|
||||||
|
use std::fmt;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::Poll;
|
use std::rc::Rc;
|
||||||
|
use std::task::{Poll, Waker};
|
||||||
|
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender, unbounded};
|
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
|
use futures::stream::FuturesUnordered;
|
||||||
|
|
||||||
pub struct LocalSet<'a, E> {
|
pub struct SpawnError<T>(T);
|
||||||
receiver: UnboundedReceiver<LocalBoxFuture<'a, Result<(), E>>>,
|
impl<T> fmt::Debug for SpawnError<T> {
|
||||||
pending: VecDeque<LocalBoxFuture<'a, Result<(), E>>>,
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("SpawnError") }
|
||||||
}
|
}
|
||||||
impl<'a, E> LocalSet<'a, E> {
|
|
||||||
pub fn new() -> (UnboundedSender<LocalBoxFuture<'a, Result<(), E>>>, Self) {
|
pub struct LocalSetState<'a, E> {
|
||||||
let (sender, receiver) = unbounded();
|
waker: Waker,
|
||||||
(sender, Self { receiver, pending: VecDeque::new() })
|
futures: FuturesUnordered<LocalBoxFuture<'a, Result<(), E>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LocalSetController<'a, E>(Rc<RefCell<LocalSetState<'a, E>>>);
|
||||||
|
impl<'a, E> LocalSetController<'a, E> {
|
||||||
|
pub fn is_empty(&self) -> bool { self.len() == 0 }
|
||||||
|
pub fn len(&self) -> usize { self.0.borrow().futures.len() }
|
||||||
|
pub fn spawn<F: Future<Output = Result<(), E>> + 'a>(&self, fut: F) {
|
||||||
|
self.try_spawn(fut).unwrap()
|
||||||
|
}
|
||||||
|
pub fn try_spawn<F: Future<Output = Result<(), E>> + 'a>(
|
||||||
|
&self,
|
||||||
|
fut: F,
|
||||||
|
) -> Result<(), SpawnError<F>> {
|
||||||
|
if Rc::strong_count(&self.0) == 1 {
|
||||||
|
return Err(SpawnError(fut));
|
||||||
|
}
|
||||||
|
let g = self.0.borrow_mut();
|
||||||
|
g.futures.push(Box::pin(fut));
|
||||||
|
g.waker.wake_by_ref();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn local_set<'a, E: 'a>() -> (LocalSetController<'a, E>, LocalSet<'a, E>) {
|
||||||
|
let state = Rc::new(RefCell::new(LocalSetState {
|
||||||
|
waker: Waker::noop().clone(),
|
||||||
|
futures: FuturesUnordered::new(),
|
||||||
|
}));
|
||||||
|
let controller = LocalSetController(state.clone());
|
||||||
|
let set = LocalSet(state);
|
||||||
|
(controller, set)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LocalSet<'a, E>(Rc<RefCell<LocalSetState<'a, E>>>);
|
||||||
impl<E> Future for LocalSet<'_, E> {
|
impl<E> Future for LocalSet<'_, E> {
|
||||||
type Output = Result<(), E>;
|
type Output = Result<(), E>;
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
|
||||||
let this = self.get_mut();
|
let ctl_dropped = 1 == Rc::strong_count(&self.0);
|
||||||
let mut any_pending = false;
|
let mut this = self.0.borrow_mut();
|
||||||
loop {
|
if !ctl_dropped {
|
||||||
match this.receiver.poll_next_unpin(cx) {
|
this.waker.clone_from(cx.waker());
|
||||||
Poll::Ready(Some(fut)) => this.pending.push_back(fut),
|
|
||||||
Poll::Ready(None) => break,
|
|
||||||
Poll::Pending => {
|
|
||||||
any_pending = true;
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let count = this.pending.len();
|
match this.futures.poll_next_unpin(cx) {
|
||||||
for _ in 0..count {
|
Poll::Ready(Some(Err(e))) => Poll::Ready(Err(e)),
|
||||||
let mut req = this.pending.pop_front().unwrap();
|
Poll::Ready(None) if ctl_dropped => Poll::Ready(Ok(())),
|
||||||
match req.as_mut().poll(cx) {
|
_ => Poll::Pending,
|
||||||
Poll::Ready(Ok(())) => (),
|
|
||||||
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
|
|
||||||
Poll::Pending => {
|
|
||||||
any_pending = true;
|
|
||||||
this.pending.push_back(req)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if any_pending { Poll::Pending } else { Poll::Ready(Ok(())) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::marker::PhantomData;
|
use std::pin::{Pin, pin};
|
||||||
use std::pin::Pin;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::task::{Context, Poll, Waker};
|
use std::task::{Context, Poll, Waker};
|
||||||
|
|
||||||
|
use futures::FutureExt;
|
||||||
|
use futures::channel::oneshot::{self, Canceled};
|
||||||
use futures::future::{FusedFuture, LocalBoxFuture};
|
use futures::future::{FusedFuture, LocalBoxFuture};
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
@@ -43,50 +44,56 @@ impl Future for Pollable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct JoinError;
|
||||||
|
|
||||||
/// An object that can be used to inspect the state of the task
|
/// An object that can be used to inspect the state of the task
|
||||||
pub struct Handle<T: 'static>(Rc<RefCell<State>>, PhantomData<T>);
|
pub struct Handle<T: 'static> {
|
||||||
|
send_abort: RefCell<Option<oneshot::Sender<()>>>,
|
||||||
|
ready: Rc<RefCell<bool>>,
|
||||||
|
recv_output: RefCell<oneshot::Receiver<T>>,
|
||||||
|
}
|
||||||
impl<T: 'static> Handle<T> {
|
impl<T: 'static> Handle<T> {
|
||||||
/// Immediately stop working on this task, and return the result if it has
|
/// Immediately stop working on this task, and return the result if it has
|
||||||
/// already finished
|
/// already finished
|
||||||
pub fn abort(&self) -> Option<T> {
|
pub fn abort(&self) -> Option<T> {
|
||||||
let mut g = self.0.borrow_mut();
|
if let Some(abort) = self.send_abort.take() {
|
||||||
g.work.take();
|
let _ = abort.send(());
|
||||||
match g.result.take() {
|
|
||||||
Some(val) => Some(*val.downcast().expect("Mismatch between type of future and handle")),
|
|
||||||
None => {
|
|
||||||
g.waker.wake_by_ref();
|
|
||||||
None
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
self.recv_output.borrow_mut().try_recv().ok().flatten()
|
||||||
}
|
}
|
||||||
/// Determine if there's any more work to do on this task
|
/// Determine if there's any more work to do on this task
|
||||||
pub fn is_finished(&self) -> bool {
|
pub fn is_finished(&self) -> bool { *self.ready.borrow() }
|
||||||
let g = self.0.borrow();
|
|
||||||
g.result.is_some() || g.work.is_none()
|
|
||||||
}
|
|
||||||
/// "finish" the freestanding task, and return the future instead
|
/// "finish" the freestanding task, and return the future instead
|
||||||
pub async fn join(self) -> T {
|
pub async fn join(self) -> Result<T, JoinError> {
|
||||||
let work = {
|
self.recv_output.into_inner().await.map_err(|Canceled| JoinError)
|
||||||
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
|
/// 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
|
/// 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
|
/// 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>) {
|
pub fn to_task<'a, F: Future<Output: 'a> + 'a>(
|
||||||
let dyn_future = Box::pin(async { Box::new(f.await) as Box<dyn Any> });
|
f: F,
|
||||||
let state = Rc::new(RefCell::new(State {
|
) -> (impl Future<Output = ()> + 'a, Handle<F::Output>) {
|
||||||
result: None,
|
let (send_abort, mut on_abort) = oneshot::channel();
|
||||||
work: Some(dyn_future),
|
let (send_output, on_output) = oneshot::channel();
|
||||||
waker: Waker::noop().clone(),
|
let ready = Rc::new(RefCell::new(false));
|
||||||
}));
|
let ready2 = ready.clone();
|
||||||
(Pollable(state.clone()), Handle(state, PhantomData))
|
let fut = async move {
|
||||||
|
let mut fut = pin!(f.fuse());
|
||||||
|
let output = futures::select_biased! {
|
||||||
|
res = on_abort => match res {
|
||||||
|
Ok(()) => return,
|
||||||
|
Err(_) => fut.await,
|
||||||
|
},
|
||||||
|
output = fut => output,
|
||||||
|
};
|
||||||
|
ready2.replace(true);
|
||||||
|
let _: Result<_, _> = send_output.send(output);
|
||||||
|
};
|
||||||
|
(fut, Handle {
|
||||||
|
ready,
|
||||||
|
recv_output: RefCell::new(on_output),
|
||||||
|
send_abort: RefCell::new(Some(send_abort)),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ async-once-cell = "0.5.4"
|
|||||||
bound = "0.6.0"
|
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.32", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"async-await",
|
"async-await",
|
||||||
] }
|
] }
|
||||||
hashbrown = "0.16.1"
|
hashbrown = "0.17.0"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
never = "0.1.0"
|
never = "0.1.0"
|
||||||
@@ -25,14 +25,14 @@ num-traits = "0.2.19"
|
|||||||
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" }
|
||||||
ordered-float = "5.1.0"
|
ordered-float = "5.3.0"
|
||||||
regex = "1.12.2"
|
regex = "1.12.3"
|
||||||
rust-embed = "8.9.0"
|
rust-embed = "8.11.0"
|
||||||
substack = "1.1.1"
|
substack = "1.1.1"
|
||||||
trait-set = "0.3.0"
|
trait-set = "0.3.0"
|
||||||
task-local = "0.1.0"
|
task-local = "0.1.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
futures = "0.3.31"
|
futures = "0.3.32"
|
||||||
rand = "0.10.0"
|
rand = "0.10.1"
|
||||||
rand_chacha = "0.10.0"
|
rand_chacha = "0.10.0"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -142,7 +142,7 @@ impl OrcErrv {
|
|||||||
/// If there is exactly one error, return it. Mostly used for simplified
|
/// If there is exactly one error, return it. Mostly used for simplified
|
||||||
/// printing
|
/// 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.iter().exactly_one().ok() }
|
||||||
/// Iterate over all positions of all errors
|
/// 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())
|
||||||
|
|||||||
@@ -113,7 +113,6 @@ pub struct Comment {
|
|||||||
pub sr: SrcRange,
|
pub sr: SrcRange,
|
||||||
}
|
}
|
||||||
impl Comment {
|
impl Comment {
|
||||||
// XXX: which of these four are actually used?
|
|
||||||
pub async fn from_api(c: &api::Comment, src: Sym) -> Self {
|
pub async fn from_api(c: &api::Comment, src: Sym) -> Self {
|
||||||
Self { text: es(c.text).await, sr: SrcRange::new(c.range.clone(), &src) }
|
Self { text: es(c.text).await, sr: SrcRange::new(c.range.clone(), &src) }
|
||||||
}
|
}
|
||||||
@@ -123,9 +122,6 @@ impl Comment {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn to_tk<R: ExprRepr, X: ExtraTok>(&self) -> TokTree<R, X> {
|
|
||||||
TokTree { tok: Token::Comment(self.text.clone()), sr: self.sr.clone() }
|
|
||||||
}
|
|
||||||
pub fn to_api(&self) -> api::Comment {
|
pub fn to_api(&self) -> api::Comment {
|
||||||
api::Comment { range: self.sr.range(), text: self.text.to_api() }
|
api::Comment { range: self.sr.range(), text: self.text.to_api() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,181 @@
|
|||||||
//! A pattern for running async code from sync destructors and other
|
//! A pattern for running async code from sync destructors and other
|
||||||
//! unfortunately sync callbacks
|
//! unfortunately sync callbacks, and for ensuring that these futures finish in
|
||||||
|
//! a timely fashion
|
||||||
//!
|
//!
|
||||||
//! We create a task_local vecdeque which is moved into a thread_local whenever
|
//! We create a task_local vecdeque which is moved into a thread_local whenever
|
||||||
//! the task is being polled. A call to [stash] pushes the future onto this
|
//! the task is being polled. A call to [stash] pushes the future onto this
|
||||||
//! deque. Before [with_stash] returns, it pops everything from the deque
|
//! deque. Before [with_stash] returns, it awaits everything stashed up to that
|
||||||
//! individually and awaits each of them, pushing any additionally stashed
|
//! point or inside the stashed futures.
|
||||||
//! futures onto the back of the same deque.
|
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
use task_local::task_local;
|
use futures::StreamExt;
|
||||||
|
use futures::future::LocalBoxFuture;
|
||||||
|
use futures::stream::FuturesUnordered;
|
||||||
|
use orchid_async_utils::cancel_cleanup;
|
||||||
|
|
||||||
#[derive(Default)]
|
thread_local! {
|
||||||
struct StashedFutures {
|
/// # Invariant
|
||||||
queue: RefCell<VecDeque<Pin<Box<dyn Future<Output = ()>>>>>,
|
///
|
||||||
}
|
/// Any function that changes the value of this thread_local must restore it before returning
|
||||||
|
static CURRENT_STASH: RefCell<Option<Vec<LocalBoxFuture<'static, ()>>>> = RefCell::default();
|
||||||
task_local! {
|
|
||||||
static STASHED_FUTURES: StashedFutures;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete the argument future, and any futures spawned from it via [stash].
|
/// Complete the argument future, and any futures spawned from it via [stash].
|
||||||
/// This is useful mostly to guarantee that messaging destructors have run.
|
/// This is useful mostly to guarantee that messaging destructors have run.
|
||||||
pub async fn with_stash<F: Future>(fut: F) -> F::Output {
|
///
|
||||||
STASHED_FUTURES
|
/// # Cancellation
|
||||||
.scope(StashedFutures::default(), async {
|
///
|
||||||
let val = fut.await;
|
/// To ensure that stashed futures run, the returned future re-stashes them a
|
||||||
while let Some(fut) = STASHED_FUTURES.with(|sf| sf.queue.borrow_mut().pop_front()) {
|
/// layer above when dropped. Therefore cancelling `with_stash` is only safe
|
||||||
fut.await;
|
/// within an enclosing `with_stash` outside of a panic.
|
||||||
}
|
pub fn with_stash<F: Future>(fut: F) -> impl Future<Output = F::Output> {
|
||||||
val
|
WithStash { stash: FuturesUnordered::new(), state: WithStashState::Main(fut) }
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Schedule a future to be run before the next [with_stash] guard ends. This is
|
/// Schedule a future to be run before the next [with_stash] guard ends. This is
|
||||||
/// most useful for sending messages from destructors.
|
/// most useful for sending messages from destructors.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If no enclosing stash is found, this function panics, unless we are already
|
||||||
|
/// panicking. The assumption is that a panic is a vis-major where proper
|
||||||
|
/// cleanup is secondary to avoiding an abort.
|
||||||
pub fn stash<F: Future<Output = ()> + 'static>(fut: F) {
|
pub fn stash<F: Future<Output = ()> + 'static>(fut: F) {
|
||||||
(STASHED_FUTURES.try_with(|sf| sf.queue.borrow_mut().push_back(Box::pin(fut))))
|
CURRENT_STASH.with(|stash| {
|
||||||
.expect("No stash! Timely completion cannot be guaranteed")
|
let mut g = stash.borrow_mut();
|
||||||
|
let Some(stash) = g.as_mut() else {
|
||||||
|
if !std::thread::panicking() {
|
||||||
|
panic!("No stash! Timely completion cannot be guaranteed");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
stash.push(Box::pin(fut))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish_or_stash<F: Future + Unpin + 'static>(
|
||||||
|
fut: F,
|
||||||
|
) -> impl Future<Output = F::Output> + Unpin + 'static {
|
||||||
|
cancel_cleanup(fut, |fut| {
|
||||||
|
stash(async {
|
||||||
|
fut.await;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
enum WithStashState<F: Future> {
|
||||||
|
Main(F),
|
||||||
|
Stash {
|
||||||
|
/// Optional to simplify state management, but only ever null on a very
|
||||||
|
/// short stretch
|
||||||
|
output: Option<F::Output>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WithStash<F: Future> {
|
||||||
|
stash: FuturesUnordered<LocalBoxFuture<'static, ()>>,
|
||||||
|
state: WithStashState<F>,
|
||||||
|
}
|
||||||
|
impl<F: Future> Future for WithStash<F> {
|
||||||
|
type Output = F::Output;
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
// SAFETY: the only non-Unpin item is Main#main, and it's pinned right back
|
||||||
|
let Self { state, stash } = unsafe { Pin::get_unchecked_mut(self) };
|
||||||
|
if let WithStashState::Main(main) = state {
|
||||||
|
// SAFETY: this comes from the pin we break on the line above
|
||||||
|
let main = unsafe { Pin::new_unchecked(main) };
|
||||||
|
let prev = CURRENT_STASH.with_borrow_mut(|key| key.replace(Vec::new()));
|
||||||
|
let poll = main.poll(cx);
|
||||||
|
let stash_init = CURRENT_STASH
|
||||||
|
.with_borrow_mut(|key| std::mem::replace(key, prev))
|
||||||
|
.expect("We put a Some() in here and CURRENT_STASH demands restoration");
|
||||||
|
stash.extend(stash_init);
|
||||||
|
if let Poll::Ready(o) = poll {
|
||||||
|
// skip this branch from this point onwards
|
||||||
|
*state = WithStashState::Stash { output: Some(o) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match state {
|
||||||
|
WithStashState::Main(_) | WithStashState::Stash { output: None, .. } => Poll::Pending,
|
||||||
|
WithStashState::Stash { output: output @ Some(_) } => loop {
|
||||||
|
// if the queue has new elements, poll_next_unpin has to be called in the next
|
||||||
|
// loop to ensure that wake-ups are triggered for them too, and if
|
||||||
|
// poll_next_unpin is called, the queue may get yet more elements synchronously,
|
||||||
|
// hence the loop
|
||||||
|
let prev = CURRENT_STASH.with_borrow_mut(|key| key.replace(Vec::new()));
|
||||||
|
let poll = stash.poll_next_unpin(cx);
|
||||||
|
let stash_new = CURRENT_STASH
|
||||||
|
.with_borrow_mut(|key| std::mem::replace(key, prev))
|
||||||
|
.expect("We put a Some() in here and CURRENT_STASH demands restoration");
|
||||||
|
stash.extend(stash_new);
|
||||||
|
match poll {
|
||||||
|
Poll::Ready(None) if stash.is_empty() => {
|
||||||
|
let output = output.take().expect("Checked in branching");
|
||||||
|
break Poll::Ready(output);
|
||||||
|
},
|
||||||
|
Poll::Pending => {
|
||||||
|
break Poll::Pending;
|
||||||
|
},
|
||||||
|
Poll::Ready(_) => continue,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<F: Future> Drop for WithStash<F> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if std::thread::panicking() {
|
||||||
|
eprintln!("Panicking through with_stash may silently drop stashed cleanup work")
|
||||||
|
}
|
||||||
|
for future in std::mem::take(&mut self.stash) {
|
||||||
|
stash(future);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use futures::SinkExt;
|
||||||
|
use futures::channel::mpsc;
|
||||||
|
use futures::future::join;
|
||||||
|
use orchid_async_utils::debug::spin_on;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn run_stashed_future() {
|
||||||
|
let (mut send, recv) = mpsc::channel(0);
|
||||||
|
spin_on(join(
|
||||||
|
with_stash(async {
|
||||||
|
let mut send1 = send.clone();
|
||||||
|
stash(async move {
|
||||||
|
send1.send(1).await.unwrap();
|
||||||
|
});
|
||||||
|
let mut send1 = send.clone();
|
||||||
|
stash(async move {
|
||||||
|
let mut send2 = send1.clone();
|
||||||
|
stash(async move {
|
||||||
|
send2.send(2).await.unwrap();
|
||||||
|
});
|
||||||
|
send1.send(3).await.unwrap();
|
||||||
|
stash(async move {
|
||||||
|
send1.send(4).await.unwrap();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let mut send1 = send.clone();
|
||||||
|
stash(async move {
|
||||||
|
send1.send(5).await.unwrap();
|
||||||
|
});
|
||||||
|
send.send(6).await.unwrap();
|
||||||
|
}),
|
||||||
|
async {
|
||||||
|
let mut results = recv.take(6).collect::<Vec<_>>().await;
|
||||||
|
results.sort();
|
||||||
|
assert_eq!(&results, &[1, 2, 3, 4, 5, 6], "all variations completed in unspecified order");
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,23 @@
|
|||||||
/// Cache a value in a [thread_local!]. Supports synchronous and asynchronous
|
/// Cache a value in a [thread_local!]. Supports synchronous and asynchronous
|
||||||
/// initializers
|
/// initializers
|
||||||
///
|
///
|
||||||
|
/// Synchronous use-case:
|
||||||
|
/// ```
|
||||||
|
/// use std::rc::Rc;
|
||||||
|
/// #[macro_use]
|
||||||
|
/// use orchid_base::tl_cache;
|
||||||
|
/// let foo = tl_cache!(Rc<Vec<usize>>: Rc::new(vec![0; 1024]));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Asynchronous use-case:
|
||||||
/// ```
|
/// ```
|
||||||
/// #[macro_use]
|
/// #[macro_use]
|
||||||
/// use orchid_base::tl_cache;
|
/// use orchid_base::tl_cache;
|
||||||
///
|
///
|
||||||
/// // simple synchronous case
|
/// async fn complex_operation(x: usize) -> usize { x + 1 }
|
||||||
/// let foo = tl_cache!(Rc<Vec<usize>>: vec![0; 1024]);
|
|
||||||
/// async {
|
/// async {
|
||||||
/// async fn complex_operation(x: usize) -> usize { x + 1 }
|
/// let bar = tl_cache!(async usize: complex_operation(0).await);
|
||||||
/// // async case
|
/// };
|
||||||
/// let bar = tl_cache!(async usize: complex_operation(0).await)
|
|
||||||
/// }
|
|
||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! tl_cache {
|
macro_rules! tl_cache {
|
||||||
|
|||||||
@@ -332,5 +332,12 @@ pub async fn ttv_fmt<'a: 'b, 'b>(
|
|||||||
FmtUnit::sequence("", " ", "", true, 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FmtTTV<'a, H: ExprRepr, X: ExtraTok>(pub &'a [TokTree<H, X>]);
|
||||||
|
impl<'b, H: ExprRepr, X: ExtraTok> Format for FmtTTV<'b, H, X> {
|
||||||
|
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
|
ttv_fmt(self.0, c).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Indent a string by two spaces
|
/// 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 ") }
|
||||||
|
|||||||
@@ -9,32 +9,33 @@ edition = "2024"
|
|||||||
async-event = "0.2.1"
|
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.44"
|
||||||
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.32", default-features = false, features = [
|
||||||
"std",
|
"std",
|
||||||
"async-await",
|
"async-await",
|
||||||
] }
|
] }
|
||||||
futures-locks = "0.7.1"
|
futures-locks = "0.7.1"
|
||||||
hashbrown = "0.16.1"
|
hashbrown = "0.17.0"
|
||||||
include_dir = { version = "0.7.4", optional = true }
|
include_dir = { version = "0.7.4", optional = true }
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
konst = "0.4.3"
|
konst = "0.4.3"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
memo-map = "0.3.3"
|
memo-map = "0.3.3"
|
||||||
never = "0.1.0"
|
never = "0.1.0"
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.4"
|
||||||
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-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.3.0"
|
||||||
pastey = "0.2.1"
|
pastey = "0.2.2"
|
||||||
substack = "1.1.1"
|
substack = "1.1.1"
|
||||||
task-local = "0.1.0"
|
task-local = "0.1.1"
|
||||||
tokio = { version = "1.49.0", optional = true, features = [] }
|
tokio = { version = "1.52.1", optional = true, features = ["io-std", "time"] }
|
||||||
tokio-util = { version = "0.7.17", optional = true, features = ["compat"] }
|
tokio-util = { version = "0.7.18", optional = true, features = ["compat"] }
|
||||||
|
|
||||||
trait-set = "0.3.0"
|
trait-set = "0.3.0"
|
||||||
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
|
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use std::any::{Any, TypeId, type_name};
|
use std::any::{Any, TypeId, type_name};
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
@@ -19,7 +18,6 @@ use orchid_base::{
|
|||||||
FmtCtx, FmtUnit, Format, IStr, OrcErrv, Pos, Receipt, ReqHandle, ReqReader, ReqReaderExt, Sym,
|
FmtCtx, FmtUnit, Format, IStr, OrcErrv, Pos, Receipt, ReqHandle, ReqReader, ReqReaderExt, Sym,
|
||||||
fmt, is, mk_errv, mk_errv_floating, take_first,
|
fmt, is, mk_errv, mk_errv_floating, take_first,
|
||||||
};
|
};
|
||||||
use task_local::task_local;
|
|
||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
use crate::gen_expr::GExpr;
|
use crate::gen_expr::GExpr;
|
||||||
@@ -99,11 +97,11 @@ impl ForeignAtom {
|
|||||||
/// Call an IPC method. If the type does not support the given method type,
|
/// Call an IPC method. If the type does not support the given method type,
|
||||||
/// this function returns [None]
|
/// this function returns [None]
|
||||||
pub async fn call<R: Request + UnderRoot<Root: AtomMethod>>(&self, r: R) -> Option<R::Response> {
|
pub async fn call<R: Request + UnderRoot<Root: AtomMethod>>(&self, r: R) -> Option<R::Response> {
|
||||||
let rep = (request(api::Fwd(
|
let rep = (request(api::Fwd {
|
||||||
self.atom.clone(),
|
target: self.atom.clone(),
|
||||||
Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
|
method: Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
|
||||||
enc_vec(&r.into_root()),
|
body: enc_vec(&r.into_root()),
|
||||||
)))
|
}))
|
||||||
.await?;
|
.await?;
|
||||||
Some(R::Response::decode_slice(&mut &rep[..]))
|
Some(R::Response::decode_slice(&mut &rep[..]))
|
||||||
}
|
}
|
||||||
@@ -111,26 +109,22 @@ impl ForeignAtom {
|
|||||||
pub fn downcast<A: Atomic>(self) -> Result<TAtom<A>, NotTypAtom> {
|
pub fn downcast<A: Atomic>(self) -> Result<TAtom<A>, NotTypAtom> {
|
||||||
let mut data = &self.atom.data.0[..];
|
let mut data = &self.atom.data.0[..];
|
||||||
let value = AtomTypeId::decode_slice(&mut data);
|
let value = AtomTypeId::decode_slice(&mut data);
|
||||||
if cfg!(debug_assertions) {
|
let cted = dyn_cted();
|
||||||
let cted = dyn_cted();
|
let own_inst = cted.inst();
|
||||||
let own_inst = cted.inst();
|
let owner_id = self.atom.owner;
|
||||||
let owner_id = self.atom.owner;
|
let typ = type_name::<A>();
|
||||||
let typ = type_name::<A>();
|
let owner = if sys_id() == owner_id {
|
||||||
let owner = if sys_id() == owner_id {
|
own_inst.card()
|
||||||
own_inst.card()
|
} else {
|
||||||
} else {
|
(cted.deps().find(|s| s.id() == self.atom.owner))
|
||||||
(cted.deps().find(|s| s.id() == self.atom.owner))
|
.ok_or_else(|| NotTypAtom { expr: self.clone().ex(), pos: self.pos(), typ })?
|
||||||
.ok_or_else(|| NotTypAtom { expr: self.clone().ex(), pos: self.pos(), typ })?
|
.get_card()
|
||||||
.get_card()
|
};
|
||||||
};
|
let Some(ops) = owner.ops_by_atid(value) else {
|
||||||
let Some(ops) = owner.ops_by_atid(value) else {
|
panic!("{value:?} does not refer to an atom in {owner_id:?} when downcasting {typ}");
|
||||||
panic!("{value:?} does not refer to an atom in {owner_id:?} when downcasting {typ}");
|
};
|
||||||
};
|
if ops.tid() != TypeId::of::<A>() {
|
||||||
if ops.tid() != TypeId::of::<A>() {
|
return Err(NotTypAtom { pos: self.pos.clone(), expr: self.ex(), typ });
|
||||||
panic!(
|
|
||||||
"{value:?} of {owner_id:?} refers to a type other than {typ}. System version mismatch?"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let value = A::Data::decode_slice(&mut data);
|
let value = A::Data::decode_slice(&mut data);
|
||||||
Ok(TAtom { value, untyped: self })
|
Ok(TAtom { value, untyped: self })
|
||||||
@@ -187,67 +181,23 @@ pub trait AtomMethod: Coding + InHierarchy {
|
|||||||
const NAME: &str;
|
const NAME: &str;
|
||||||
}
|
}
|
||||||
|
|
||||||
task_local! {
|
|
||||||
pub(crate) static ATOM_WITHOUT_HANDLE_FINAL_IMPL: Rc<RefCell<Option<Box<dyn Any>>>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A handler for an [AtomMethod] on an [Atomic]. The [AtomMethod] must also be
|
/// A handler for an [AtomMethod] on an [Atomic]. The [AtomMethod] must also be
|
||||||
/// registered in [Atomic::reg_methods]
|
/// registered in [Atomic::reg_methods]
|
||||||
pub trait Supports<M: AtomMethod>: Atomic {
|
pub trait Supports<M: AtomMethod>: Atomic {
|
||||||
fn handle<'a>(
|
fn handle(&self, hand: Box<dyn ReqHandle>, req: M) -> impl Future<Output = io::Result<Receipt>>;
|
||||||
&self,
|
|
||||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
|
||||||
req: M,
|
|
||||||
) -> impl Future<Output = io::Result<Receipt<'a>>>;
|
|
||||||
fn handle_final<'a>(
|
|
||||||
self,
|
|
||||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
|
||||||
req: M,
|
|
||||||
) -> impl Future<Output = io::Result<Receipt<'a>>> {
|
|
||||||
async move {
|
|
||||||
let rcpt = self.handle(hand, req).await;
|
|
||||||
let _ = ATOM_WITHOUT_HANDLE_FINAL_IMPL.try_with(|cell| cell.replace(Some(Box::new(self))));
|
|
||||||
rcpt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: default-implement the above somehow while calling OwnedAtom::free if
|
|
||||||
// necessary
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trait HandleAtomMethod<A> {
|
trait HandleAtomMethod<A> {
|
||||||
fn handle<'a, 'b: 'a>(
|
fn handle<'a>(&'a self, atom: &'a A, reader: Box<dyn ReqReader>) -> LocalBoxFuture<'a, ()>;
|
||||||
&'a self,
|
|
||||||
atom: &'a A,
|
|
||||||
reader: Box<dyn ReqReader<'b> + 'a>,
|
|
||||||
) -> LocalBoxFuture<'a, ()>;
|
|
||||||
fn handle_final<'a, 'b: 'a>(
|
|
||||||
&'a self,
|
|
||||||
atom: A,
|
|
||||||
reader: Box<dyn ReqReader<'b> + 'a>,
|
|
||||||
) -> LocalBoxFuture<'a, ()>;
|
|
||||||
}
|
}
|
||||||
struct AtomMethodHandler<M, A>(PhantomData<M>, PhantomData<A>);
|
struct AtomMethodHandler<M, A>(PhantomData<M>, PhantomData<A>);
|
||||||
impl<M: AtomMethod, A: Supports<M>> HandleAtomMethod<A> for AtomMethodHandler<M, A> {
|
impl<M: AtomMethod, A: Supports<M>> HandleAtomMethod<A> for AtomMethodHandler<M, A> {
|
||||||
fn handle<'a, 'b: 'a>(
|
fn handle<'a>(&'a self, atom: &'a A, mut reader: Box<dyn ReqReader>) -> LocalBoxFuture<'a, ()> {
|
||||||
&'a self,
|
|
||||||
atom: &'a A,
|
|
||||||
mut reader: Box<dyn ReqReader<'b> + 'a>,
|
|
||||||
) -> LocalBoxFuture<'a, ()> {
|
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
let req = reader.read_req::<M>().await.unwrap();
|
let req = reader.read_req::<M>().await.unwrap();
|
||||||
let _ = Supports::<M>::handle(atom, reader.finish().await, req).await.unwrap();
|
let _ = Supports::<M>::handle(atom, reader.finish().await, req).await.unwrap();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn handle_final<'a, 'b: 'a>(
|
|
||||||
&'a self,
|
|
||||||
atom: A,
|
|
||||||
mut reader: Box<dyn ReqReader<'b> + 'a>,
|
|
||||||
) -> LocalBoxFuture<'a, ()> {
|
|
||||||
Box::pin(async {
|
|
||||||
let req = reader.read_req::<M>().await.unwrap();
|
|
||||||
let _ = Supports::<M>::handle_final(atom, reader.finish().await, req).await.unwrap();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A collection of [Supports] impls for an [Atomic]. If a [Supports]
|
/// A collection of [Supports] impls for an [Atomic]. If a [Supports]
|
||||||
@@ -282,26 +232,7 @@ pub(crate) struct MethodSet<A: Atomic> {
|
|||||||
handlers: HashMap<Sym, Rc<dyn HandleAtomMethod<A>>>,
|
handlers: HashMap<Sym, Rc<dyn HandleAtomMethod<A>>>,
|
||||||
}
|
}
|
||||||
impl<A: Atomic> MethodSet<A> {
|
impl<A: Atomic> MethodSet<A> {
|
||||||
pub(crate) async fn final_dispatch<'a>(
|
pub(crate) async fn dispatch(&self, atom: &A, key: Sym, req: Box<dyn ReqReader>) -> bool {
|
||||||
&self,
|
|
||||||
atom: A,
|
|
||||||
key: Sym,
|
|
||||||
req: Box<dyn ReqReader<'a> + 'a>,
|
|
||||||
) -> bool {
|
|
||||||
match self.handlers.get(&key) {
|
|
||||||
None => false,
|
|
||||||
Some(handler) => {
|
|
||||||
handler.handle_final(atom, req).await;
|
|
||||||
true
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub(crate) async fn dispatch<'a>(
|
|
||||||
&self,
|
|
||||||
atom: &'_ A,
|
|
||||||
key: Sym,
|
|
||||||
req: Box<dyn ReqReader<'a> + 'a>,
|
|
||||||
) -> bool {
|
|
||||||
match self.handlers.get(&key) {
|
match self.handlers.get(&key) {
|
||||||
None => false,
|
None => false,
|
||||||
Some(handler) => {
|
Some(handler) => {
|
||||||
@@ -353,11 +284,11 @@ impl<A: Atomic> TAtom<A> {
|
|||||||
pub async fn call<R: Request + UnderRoot<Root: AtomMethod>>(&self, req: R) -> R::Response
|
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 {
|
||||||
self.untyped.atom.clone(),
|
target: self.untyped.atom.clone(),
|
||||||
Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
|
method: Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
|
||||||
enc_vec(&req.into_root()),
|
body: enc_vec(&req.into_root()),
|
||||||
)))
|
}))
|
||||||
.await
|
.await
|
||||||
.unwrap()[..],
|
.unwrap()[..],
|
||||||
)
|
)
|
||||||
@@ -389,17 +320,11 @@ pub trait AtomOps: 'static {
|
|||||||
fn call<'a>(&'a self, ctx: AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr>;
|
fn call<'a>(&'a self, ctx: AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr>;
|
||||||
fn call_ref<'a>(&'a self, ctx: AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr>;
|
fn call_ref<'a>(&'a self, ctx: AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr>;
|
||||||
fn print<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit>;
|
fn print<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit>;
|
||||||
fn handle_req<'a>(
|
|
||||||
&'a self,
|
|
||||||
ctx: AtomCtx<'a>,
|
|
||||||
key: Sym,
|
|
||||||
req: Box<dyn ReqReader<'a> + 'a>,
|
|
||||||
) -> LocalBoxFuture<'a, bool>;
|
|
||||||
fn handle_req_ref<'a>(
|
fn handle_req_ref<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
ctx: AtomCtx<'a>,
|
ctx: AtomCtx<'a>,
|
||||||
key: Sym,
|
key: Sym,
|
||||||
req: Box<dyn ReqReader<'a> + 'a>,
|
req: Box<dyn ReqReader>,
|
||||||
) -> LocalBoxFuture<'a, bool>;
|
) -> LocalBoxFuture<'a, bool>;
|
||||||
fn serialize<'a, 'b: 'a>(
|
fn serialize<'a, 'b: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
@@ -417,25 +342,25 @@ pub trait AtomOps: 'static {
|
|||||||
trait_set! {
|
trait_set! {
|
||||||
pub trait AtomFactoryFn = FnOnce() -> LocalBoxFuture<'static, api::LocalAtom> + DynClone;
|
pub trait AtomFactoryFn = FnOnce() -> LocalBoxFuture<'static, api::LocalAtom> + DynClone;
|
||||||
}
|
}
|
||||||
pub(crate) struct AtomFactory(Box<dyn AtomFactoryFn>);
|
pub(crate) struct AtomFactory(Box<dyn AtomFactoryFn>, String);
|
||||||
impl AtomFactory {
|
impl AtomFactory {
|
||||||
pub fn new(f: impl AsyncFnOnce() -> api::LocalAtom + Clone + 'static) -> Self {
|
pub fn new(name: String, f: impl AsyncFnOnce() -> api::LocalAtom + Clone + 'static) -> Self {
|
||||||
Self(Box::new(|| f().boxed_local()))
|
Self(Box::new(|| f().boxed_local()), name)
|
||||||
}
|
}
|
||||||
pub async fn build(self) -> api::LocalAtom { (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), self.1.clone()) }
|
||||||
}
|
}
|
||||||
impl fmt::Debug for AtomFactory {
|
impl fmt::Debug for AtomFactory {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory") }
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory<{}>", self.1) }
|
||||||
}
|
}
|
||||||
impl fmt::Display for AtomFactory {
|
impl fmt::Display for AtomFactory {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory") }
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self:?}") }
|
||||||
}
|
}
|
||||||
impl Format for AtomFactory {
|
impl Format for AtomFactory {
|
||||||
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 {
|
||||||
"AtomFactory".to_string().into()
|
self.to_string().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,8 @@ use task_local::task_local;
|
|||||||
|
|
||||||
use crate::gen_expr::{GExpr, bot};
|
use crate::gen_expr::{GExpr, bot};
|
||||||
use crate::{
|
use crate::{
|
||||||
ATOM_WITHOUT_HANDLE_FINAL_IMPL, AtomCtx, AtomFactory, AtomOps, Atomic, AtomicFeaturesImpl,
|
AtomCtx, AtomFactory, AtomOps, Atomic, AtomicFeaturesImpl, AtomicVariant, DynSystemCardExt, Expr,
|
||||||
AtomicVariant, DynSystemCardExt, Expr, MethodSet, MethodSetBuilder, ToExpr, api, dyn_cted,
|
MethodSet, MethodSetBuilder, ToExpr, api, dyn_cted, err_not_callable,
|
||||||
err_not_callable,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Value of [Atomic::Variant] for a type that implements [OwnedAtom]
|
/// Value of [Atomic::Variant] for a type that implements [OwnedAtom]
|
||||||
@@ -32,7 +31,7 @@ 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 {
|
||||||
fn _factory(self) -> AtomFactory {
|
fn _factory(self) -> AtomFactory {
|
||||||
AtomFactory::new(async move || {
|
AtomFactory::new(type_name::<A>().to_string(), async move || {
|
||||||
let obj_store = get_obj_store();
|
let obj_store = get_obj_store();
|
||||||
let atom_id = {
|
let atom_id = {
|
||||||
let mut id = obj_store.next_id.borrow_mut();
|
let mut id = obj_store.next_id.borrow_mut();
|
||||||
@@ -73,7 +72,10 @@ impl Deref for AtomReadGuard<'_> {
|
|||||||
/// Remove an atom from the store
|
/// Remove an atom from the store
|
||||||
pub(crate) async fn take_atom(id: api::AtomId) -> Box<dyn DynOwnedAtom> {
|
pub(crate) async fn take_atom(id: api::AtomId) -> Box<dyn DynOwnedAtom> {
|
||||||
let mut g = get_obj_store().objects.write().await;
|
let mut g = get_obj_store().objects.write().await;
|
||||||
g.remove(&id).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0))
|
g.remove(&id).unwrap_or_else(|| {
|
||||||
|
let name = dyn_cted().inst().card().name();
|
||||||
|
panic!("{name} received invalid atom ID: {}", id.0)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct OwnedAtomOps<T: OwnedAtom> {
|
pub(crate) struct OwnedAtomOps<T: OwnedAtom> {
|
||||||
@@ -113,30 +115,11 @@ impl<A: OwnedAtom> AtomOps for OwnedAtomOps<A> {
|
|||||||
fn print(&self, AtomCtx(_, id): AtomCtx<'_>) -> LocalBoxFuture<'_, FmtUnit> {
|
fn print(&self, AtomCtx(_, id): AtomCtx<'_>) -> LocalBoxFuture<'_, FmtUnit> {
|
||||||
Box::pin(async move { AtomReadGuard::new(id.unwrap()).await.dyn_print().await })
|
Box::pin(async move { AtomReadGuard::new(id.unwrap()).await.dyn_print().await })
|
||||||
}
|
}
|
||||||
fn handle_req<'a>(
|
|
||||||
&'a self,
|
|
||||||
AtomCtx(_, id): AtomCtx<'a>,
|
|
||||||
key: Sym,
|
|
||||||
req: Box<dyn orchid_base::ReqReader<'a> + 'a>,
|
|
||||||
) -> LocalBoxFuture<'a, bool> {
|
|
||||||
Box::pin(async move {
|
|
||||||
let a = take_atom(id.unwrap()).await;
|
|
||||||
let ms = self.ms.get_or_init(self.msbuild.pack()).await;
|
|
||||||
let cell = Rc::new(RefCell::new(None));
|
|
||||||
let matched = ATOM_WITHOUT_HANDLE_FINAL_IMPL
|
|
||||||
.scope(cell.clone(), ms.final_dispatch(*a.as_any().downcast().unwrap(), key, req))
|
|
||||||
.await;
|
|
||||||
if let Some(val) = cell.take() {
|
|
||||||
val.downcast::<A>().unwrap().free().await
|
|
||||||
}
|
|
||||||
matched
|
|
||||||
})
|
|
||||||
}
|
|
||||||
fn handle_req_ref<'a>(
|
fn handle_req_ref<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
AtomCtx(_, id): AtomCtx<'a>,
|
AtomCtx(_, id): AtomCtx<'a>,
|
||||||
key: Sym,
|
key: Sym,
|
||||||
req: Box<dyn orchid_base::ReqReader<'a> + 'a>,
|
req: Box<dyn orchid_base::ReqReader>,
|
||||||
) -> 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;
|
||||||
@@ -303,7 +286,6 @@ fn assert_serializable<T: OwnedAtom>() {
|
|||||||
|
|
||||||
pub(crate) trait DynOwnedAtom: DynClone + 'static {
|
pub(crate) trait DynOwnedAtom: DynClone + 'static {
|
||||||
fn as_any_ref(&self) -> &dyn Any;
|
fn as_any_ref(&self) -> &dyn Any;
|
||||||
fn as_any(self: Box<Self>) -> Box<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>;
|
||||||
@@ -316,7 +298,6 @@ pub(crate) trait DynOwnedAtom: DynClone + 'static {
|
|||||||
}
|
}
|
||||||
impl<T: OwnedAtom> DynOwnedAtom for T {
|
impl<T: OwnedAtom> DynOwnedAtom for T {
|
||||||
fn as_any_ref(&self) -> &dyn Any { self }
|
fn as_any_ref(&self) -> &dyn Any { self }
|
||||||
fn as_any(self: Box<Self>) -> Box<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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ 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(type_name::<A>().to_string(), async move || {
|
||||||
let (id, _) = dyn_cted().inst().card().ops::<A>();
|
let (id, _) = dyn_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);
|
||||||
@@ -49,25 +49,17 @@ impl<T: ThinAtom> AtomOps for ThinAtomOps<T> {
|
|||||||
fn call_ref<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> {
|
fn call_ref<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> {
|
||||||
Box::pin(async move { T::decode_slice(&mut &buf[..]).call(arg).await })
|
Box::pin(async move { T::decode_slice(&mut &buf[..]).call(arg).await })
|
||||||
}
|
}
|
||||||
fn handle_req<'a>(
|
fn handle_req_ref<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
AtomCtx(buf, ..): AtomCtx<'a>,
|
AtomCtx(buf, ..): AtomCtx<'a>,
|
||||||
key: Sym,
|
key: Sym,
|
||||||
req: Box<dyn orchid_base::ReqReader<'a> + 'a>,
|
req: Box<dyn orchid_base::ReqReader>,
|
||||||
) -> 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 handle_req_ref<'a>(
|
|
||||||
&'a self,
|
|
||||||
ctx: AtomCtx<'a>,
|
|
||||||
key: Sym,
|
|
||||||
req: Box<dyn orchid_base::ReqReader<'a> + 'a>,
|
|
||||||
) -> LocalBoxFuture<'a, bool> {
|
|
||||||
self.handle_req(ctx, key, req)
|
|
||||||
}
|
|
||||||
fn serialize<'a, 'b: 'a>(
|
fn serialize<'a, 'b: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
ctx: AtomCtx<'a>,
|
ctx: AtomCtx<'a>,
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ pub fn orchid_extension_main_body(cx: ExtCx, builder: ExtensionBuilder) {
|
|||||||
/// ```
|
/// ```
|
||||||
/// #[macro_use]
|
/// #[macro_use]
|
||||||
/// use orchid_extension::dylib_main;
|
/// use orchid_extension::dylib_main;
|
||||||
/// use orchid_extension::entrypoint::ExtensionBuilder;
|
/// use orchid_extension::ExtensionBuilder;
|
||||||
///
|
///
|
||||||
/// dylib_main! {
|
/// dylib_main! {
|
||||||
/// ExtensionBuilder::new("orchid-std::main")
|
/// ExtensionBuilder::new("orchid-std::main")
|
||||||
|
|||||||
@@ -1,45 +1,34 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use dyn_clone::DynClone;
|
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_base::{Receipt, ReqHandle, ReqHandleExt};
|
use orchid_base::{Receipt, ReqHandle, ReqHandleExt};
|
||||||
use trait_set::trait_set;
|
|
||||||
|
|
||||||
use crate::gen_expr::{GExpr, new_atom};
|
use crate::gen_expr::{GExpr, new_atom, serialize};
|
||||||
use crate::std_reqs::RunCommand;
|
use crate::std_reqs::StartCommand;
|
||||||
use crate::{Atomic, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, ToExpr};
|
use crate::{Atomic, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, ToExpr};
|
||||||
|
|
||||||
trait_set! {
|
pub trait AsyncFnDyn {
|
||||||
pub trait ClonableAsyncFnOnceDyn = FnOnce() -> LocalBoxFuture<'static, Option<GExpr>> + DynClone;
|
fn call<'a>(&'a self) -> LocalBoxFuture<'a, Option<GExpr>>;
|
||||||
|
}
|
||||||
|
impl<T: AsyncFn() -> Option<GExpr>> AsyncFnDyn for T {
|
||||||
|
fn call<'a>(&'a self) -> LocalBoxFuture<'a, Option<GExpr>> { Box::pin(async { (self)().await }) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CmdAtom(Box<dyn ClonableAsyncFnOnceDyn>);
|
#[derive(Clone)]
|
||||||
impl Clone for CmdAtom {
|
pub struct CmdAtom(Rc<dyn AsyncFnDyn>);
|
||||||
fn clone(&self) -> Self { Self(dyn_clone::clone_box(&*self.0)) }
|
|
||||||
}
|
|
||||||
impl Atomic for CmdAtom {
|
impl Atomic for CmdAtom {
|
||||||
type Data = ();
|
type Data = ();
|
||||||
type Variant = OwnedVariant;
|
type Variant = OwnedVariant;
|
||||||
fn reg_methods() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<RunCommand>() }
|
fn reg_methods() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<StartCommand>() }
|
||||||
}
|
}
|
||||||
impl Supports<RunCommand> for CmdAtom {
|
impl Supports<StartCommand> for CmdAtom {
|
||||||
async fn handle<'a>(
|
async fn handle(&self, hand: Box<dyn ReqHandle>, req: StartCommand) -> std::io::Result<Receipt> {
|
||||||
&self,
|
let reply = self.0.call().await;
|
||||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
|
||||||
req: RunCommand,
|
|
||||||
) -> std::io::Result<Receipt<'a>> {
|
|
||||||
Self(dyn_clone::clone_box(&*self.0)).handle_final(hand, req).await
|
|
||||||
}
|
|
||||||
async fn handle_final<'a>(
|
|
||||||
self,
|
|
||||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
|
||||||
req: RunCommand,
|
|
||||||
) -> std::io::Result<Receipt<'a>> {
|
|
||||||
let reply = (self.0)().await;
|
|
||||||
match reply {
|
match reply {
|
||||||
None => hand.reply(&req, &None).await,
|
None => hand.reply(&req, None).await,
|
||||||
Some(next) => hand.reply(&req, &Some(next.serialize().await)).await,
|
Some(next) => hand.reply(&req, Some(serialize(next).await)).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,13 +37,9 @@ impl OwnedAtom for CmdAtom {
|
|||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cmd<R: ToExpr>(f: impl AsyncFnOnce() -> Option<R> + Clone + 'static) -> GExpr {
|
pub fn cmd<R: ToExpr>(f: impl AsyncFn() -> Option<R> + Clone + 'static) -> GExpr {
|
||||||
new_atom(CmdAtom(Box::new(|| {
|
new_atom(CmdAtom(Rc::new(async move || match f().await {
|
||||||
Box::pin(async {
|
None => None,
|
||||||
match f().await {
|
Some(r) => Some(r.to_gen().await),
|
||||||
None => None,
|
|
||||||
Some(r) => Some(r.to_gen().await),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,14 @@ pub trait ToExpr {
|
|||||||
where Self: Sized {
|
where Self: Sized {
|
||||||
async { self.to_gen().await.create().await }
|
async { self.to_gen().await.create().await }
|
||||||
}
|
}
|
||||||
|
fn boxed<'a>(self) -> Box<dyn ToExprDyn + 'a>
|
||||||
|
where Self: Sized + 'a {
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
fn clonable_boxed<'a>(self) -> Box<dyn ClonableToExprDyn + 'a>
|
||||||
|
where Self: Clone + Sized + 'a {
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper for a future that implements [ToExpr]
|
/// A wrapper for a future that implements [ToExpr]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::any::type_name;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@@ -7,9 +8,9 @@ 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::OrcRes;
|
use orchid_base::{FmtCtx, FmtUnit, OrcRes};
|
||||||
|
|
||||||
use crate::gen_expr::{GExpr, arg, call, lam, new_atom, seq};
|
use crate::gen_expr::{GExpr, call, lam, new_atom, seq};
|
||||||
use crate::{Atomic, Expr, OwnedAtom, OwnedVariant, ToExpr, TryFromExpr};
|
use crate::{Atomic, Expr, OwnedAtom, OwnedVariant, ToExpr, TryFromExpr};
|
||||||
|
|
||||||
enum Command {
|
enum Command {
|
||||||
@@ -18,6 +19,7 @@ enum Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct BuilderCoroutineData {
|
struct BuilderCoroutineData {
|
||||||
|
name: &'static str,
|
||||||
receiver: Mutex<LocalBoxStream<'static, Command>>,
|
receiver: Mutex<LocalBoxStream<'static, Command>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +32,7 @@ impl BuilderCoroutine {
|
|||||||
None => panic!("Exec handle dropped and coroutine blocked instead of returning"),
|
None => panic!("Exec handle dropped and coroutine blocked instead of returning"),
|
||||||
Some(Command::Halt(expr)) => expr,
|
Some(Command::Halt(expr)) => expr,
|
||||||
Some(Command::Execute(expr, reply)) =>
|
Some(Command::Execute(expr, reply)) =>
|
||||||
call(lam::<0>(seq(arg(0), call(new_atom(Replier { reply, builder: self }), arg(0)))), expr)
|
call(lam(async |x| seq(x, call(new_atom(Replier { reply, builder: self }), x)).await), expr)
|
||||||
.await,
|
.await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,6 +55,9 @@ impl OwnedAtom for Replier {
|
|||||||
std::mem::drop(self.reply);
|
std::mem::drop(self.reply);
|
||||||
self.builder.run().await
|
self.builder.run().await
|
||||||
}
|
}
|
||||||
|
async fn print_atom<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||||
|
format!("Replier<{}>", self.builder.0.name).into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A long-lived async context that can yield to the executor. The expression
|
/// A long-lived async context that can yield to the executor. The expression
|
||||||
@@ -62,6 +67,7 @@ pub async fn exec<R: ToExpr>(f: impl for<'a> AsyncFnOnce(ExecHandle<'a>) -> R +
|
|||||||
let halt =
|
let halt =
|
||||||
async { Command::Halt(f(ExecHandle(cmd_snd, PhantomData)).await.to_gen().await) }.into_stream();
|
async { Command::Halt(f(ExecHandle(cmd_snd, PhantomData)).await.to_gen().await) }.into_stream();
|
||||||
let coro = BuilderCoroutine(Rc::new(BuilderCoroutineData {
|
let coro = BuilderCoroutine(Rc::new(BuilderCoroutineData {
|
||||||
|
name: type_name::<R>(),
|
||||||
receiver: Mutex::new(stream::select(halt, cmd_recv).boxed_local()),
|
receiver: Mutex::new(stream::select(halt, cmd_recv).boxed_local()),
|
||||||
}));
|
}));
|
||||||
coro.run().await
|
coro.run().await
|
||||||
|
|||||||
@@ -8,20 +8,22 @@ use std::pin::Pin;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures::future::{LocalBoxFuture, join_all};
|
use futures::future::{LocalBoxFuture, join_all, join3};
|
||||||
use futures::{AsyncWriteExt, StreamExt, stream};
|
use futures::{AsyncReadExt, 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_async_utils::{Handle, to_task};
|
use orchid_async_utils::{Handle, JoinError, to_task};
|
||||||
use orchid_base::{
|
use orchid_base::{
|
||||||
Client, ClientExt, CommCtx, Comment, MsgReader, MsgReaderExt, ReqHandleExt, ReqReaderExt,
|
Client, ClientExt, CommCx, Comment, IoComm, MsgReader, MsgReaderExt, ReqHandleExt, ReqReaderExt,
|
||||||
Snippet, Sym, TokenVariant, Witness, char_filter_match, char_filter_union, es, io_comm, is, log,
|
Snippet, Sym, TokenVariant, Witness, char_filter_match, char_filter_union, es, io_comm, is, log,
|
||||||
mk_char_filter, try_with_reporter, ttv_from_api, with_interner, with_logger, with_stash,
|
mk_char_filter, try_with_reporter, ttv_from_api, with_interner, with_logger, with_stash,
|
||||||
};
|
};
|
||||||
use substack::Substack;
|
use substack::Substack;
|
||||||
use task_local::task_local;
|
use task_local::task_local;
|
||||||
|
use unsync_pipe::pipe;
|
||||||
|
|
||||||
|
use crate::gen_expr::serialize;
|
||||||
use crate::interner::new_interner;
|
use crate::interner::new_interner;
|
||||||
use crate::logger::LoggerImpl;
|
use crate::logger::LoggerImpl;
|
||||||
use crate::tree::{TreeIntoApiCtxImpl, get_lazy, with_lazy_member_store};
|
use crate::tree::{TreeIntoApiCtxImpl, get_lazy, with_lazy_member_store};
|
||||||
@@ -35,7 +37,7 @@ use crate::{
|
|||||||
|
|
||||||
task_local::task_local! {
|
task_local::task_local! {
|
||||||
static CLIENT: Rc<dyn Client>;
|
static CLIENT: Rc<dyn Client>;
|
||||||
static CTX: Rc<RefCell<Option<CommCtx>>>;
|
static CTX: Rc<RefCell<Option<CommCx>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_client() -> Rc<dyn Client> { CLIENT.get() }
|
fn get_client() -> Rc<dyn Client> { CLIENT.get() }
|
||||||
@@ -49,7 +51,7 @@ pub async fn exit() {
|
|||||||
|
|
||||||
/// Set the client used for global [request] and [notify] functions within the
|
/// Set the client used for global [request] and [notify] functions within the
|
||||||
/// runtime of this future
|
/// runtime of this future
|
||||||
pub async fn with_comm<F: Future>(c: Rc<dyn Client>, ctx: CommCtx, fut: F) -> F::Output {
|
pub async fn with_comm<F: Future>(c: Rc<dyn Client>, ctx: CommCx, fut: F) -> F::Output {
|
||||||
CLIENT.scope(c, CTX.scope(Rc::new(RefCell::new(Some(ctx))), fut)).await
|
CLIENT.scope(c, CTX.scope(Rc::new(RefCell::new(Some(ctx))), fut)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,15 +65,17 @@ pub async fn mute_reply<F: Future>(f: F) -> F::Output { MUTE_REPLY.scope((), f).
|
|||||||
|
|
||||||
/// Send a request through the global client's [ClientExt::request]
|
/// Send a request through the global client's [ClientExt::request]
|
||||||
pub async fn request<T: Request + UnderRoot<Root = api::ExtHostReq>>(t: T) -> T::Response {
|
pub async fn request<T: Request + UnderRoot<Root = api::ExtHostReq>>(t: T) -> T::Response {
|
||||||
|
let req_str = if MUTE_REPLY.try_with(|b| *b).is_err() { format!("{t:?}") } else { String::new() };
|
||||||
let response = get_client().request(t).await.unwrap();
|
let response = get_client().request(t).await.unwrap();
|
||||||
if MUTE_REPLY.try_with(|b| *b).is_err() {
|
if MUTE_REPLY.try_with(|b| *b).is_err() {
|
||||||
writeln!(log("msg"), "Got response {response:?}").await;
|
let ext = dyn_cted().inst().card().name();
|
||||||
|
writeln!(log("msg"), "{ext} {req_str} got response {response:?}").await;
|
||||||
}
|
}
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a notification through the global client's [ClientExt::notify]
|
/// Send a notification through the global client's [ClientExt::notify]
|
||||||
pub async fn notify<T: UnderRoot<Root = api::ExtHostNotif>>(t: T) {
|
pub async fn notify<T: UnderRoot<Root = api::ExtHostNotif> + 'static>(t: T) {
|
||||||
get_client().notify(t).await.unwrap()
|
get_client().notify(t).await.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +107,7 @@ impl<F: AsyncFnOnce(LocalBoxFuture<'_, ()>) + 'static> ContextModifier for F {
|
|||||||
|
|
||||||
pub(crate) trait DynTaskHandle: 'static {
|
pub(crate) trait DynTaskHandle: 'static {
|
||||||
fn abort(self: Box<Self>);
|
fn abort(self: Box<Self>);
|
||||||
fn join(self: Box<Self>) -> LocalBoxFuture<'static, Box<dyn Any>>;
|
fn join(self: Box<Self>) -> LocalBoxFuture<'static, Result<Box<dyn Any>, JoinError>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
task_local! {
|
task_local! {
|
||||||
@@ -121,7 +125,7 @@ impl<T: 'static> TaskHandle<T> {
|
|||||||
/// Stop working on the task and return the nested future. The distinction
|
/// Stop working on the task and return the nested future. The distinction
|
||||||
/// between this and waiting until the task is complete without reparenting it
|
/// between this and waiting until the task is complete without reparenting it
|
||||||
/// is significant for the purpose of [task_local] context
|
/// is significant for the purpose of [task_local] context
|
||||||
pub async fn join(self) -> T { *self.0.join().await.downcast().unwrap() }
|
pub async fn join(self) -> Result<T, JoinError> { Ok(*self.0.join().await?.downcast().unwrap()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawn a future that is not associated with a pending request or a past
|
/// Spawn a future that is not associated with a pending request or a past
|
||||||
@@ -135,7 +139,9 @@ pub fn spawn<F: Future<Output: 'static> + 'static>(delay: Duration, f: F) -> Tas
|
|||||||
|
|
||||||
impl DynTaskHandle for Handle<Box<dyn Any>> {
|
impl DynTaskHandle for Handle<Box<dyn Any>> {
|
||||||
fn abort(self: Box<Self>) { Self::abort(&self); }
|
fn abort(self: Box<Self>) { Self::abort(&self); }
|
||||||
fn join(self: Box<Self>) -> LocalBoxFuture<'static, Box<dyn Any>> { Box::pin(Self::join(*self)) }
|
fn join(self: Box<Self>) -> LocalBoxFuture<'static, Result<Box<dyn Any>, JoinError>> {
|
||||||
|
Box::pin(Self::join(*self))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A new Orchid extension as specified in loaders. An extension is a unit of
|
/// A new Orchid extension as specified in loaders. An extension is a unit of
|
||||||
@@ -188,9 +194,9 @@ impl ExtensionBuilder {
|
|||||||
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 IoComm { client, cx: comm_ctx, srv } = io_comm(ctx.output, ctx.input);
|
||||||
// this future will be ready once the extension cleanly exits
|
// this future will be ready once the extension cleanly exits
|
||||||
let extension_fut = extension_srv.listen(
|
let extension_fut = srv.listen(
|
||||||
async |n: Box<dyn MsgReader<'_>>| {
|
async |n: Box<dyn MsgReader<'_>>| {
|
||||||
let notif = n.read().await.unwrap();
|
let notif = n.read().await.unwrap();
|
||||||
match notif {
|
match notif {
|
||||||
@@ -210,15 +216,15 @@ impl ExtensionBuilder {
|
|||||||
match req {
|
match req {
|
||||||
api::HostExtReq::SystemDrop(sys_drop) => {
|
api::HostExtReq::SystemDrop(sys_drop) => {
|
||||||
SYSTEM_TABLE.with(|l| l.borrow_mut().remove(&sys_drop.0));
|
SYSTEM_TABLE.with(|l| l.borrow_mut().remove(&sys_drop.0));
|
||||||
handle.reply(&sys_drop, &()).await
|
handle.reply(&sys_drop, ()).await
|
||||||
},
|
},
|
||||||
api::HostExtReq::AtomDrop(atom_drop @ api::AtomDrop(sys_id, atom)) =>
|
api::HostExtReq::AtomDrop(atom_drop @ api::AtomDrop(sys_id, atom)) =>
|
||||||
with_sys_record(sys_id, async {
|
with_sys_record(sys_id, async {
|
||||||
take_atom(atom).await.dyn_free().await;
|
take_atom(atom).await.dyn_free().await;
|
||||||
handle.reply(&atom_drop, &()).await
|
handle.reply(&atom_drop, ()).await
|
||||||
})
|
})
|
||||||
.await,
|
.await,
|
||||||
api::HostExtReq::Ping(ping @ api::Ping) => handle.reply(&ping, &()).await,
|
api::HostExtReq::Ping(ping @ api::Ping) => handle.reply(&ping, ()).await,
|
||||||
api::HostExtReq::Sweep(api::Sweep) => todo!(),
|
api::HostExtReq::Sweep(api::Sweep) => todo!(),
|
||||||
api::HostExtReq::SysReq(api::SysReq::NewSystem(new_sys)) => {
|
api::HostExtReq::SysReq(api::SysReq::NewSystem(new_sys)) => {
|
||||||
let (ctor_idx, _) = (decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system))
|
let (ctor_idx, _) = (decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system))
|
||||||
@@ -254,7 +260,7 @@ impl ExtensionBuilder {
|
|||||||
.await;
|
.await;
|
||||||
let response =
|
let response =
|
||||||
api::NewSystemResponse { lex_filter, const_root, line_types, prelude };
|
api::NewSystemResponse { lex_filter, const_root, line_types, prelude };
|
||||||
handle.reply(&new_sys, &response).await
|
handle.reply(&new_sys, response).await
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
},
|
},
|
||||||
@@ -263,17 +269,23 @@ impl ExtensionBuilder {
|
|||||||
let (path, tree) = get_lazy(tree_id).await;
|
let (path, tree) = get_lazy(tree_id).await;
|
||||||
let mut tia_ctx =
|
let mut tia_ctx =
|
||||||
TreeIntoApiCtxImpl { path: Substack::Bottom, basepath: &path[..] };
|
TreeIntoApiCtxImpl { path: Substack::Bottom, basepath: &path[..] };
|
||||||
handle.reply(&get_tree, &tree.into_api(&mut tia_ctx).await).await
|
handle.reply(&get_tree, tree.into_api(&mut tia_ctx).await).await
|
||||||
})
|
})
|
||||||
.await,
|
.await,
|
||||||
api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => {
|
api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => {
|
||||||
let fwd_tok = Witness::of(&fwd);
|
let fwd_tok = Witness::of(&fwd);
|
||||||
let api::SysFwded(sys_id, payload) = fwd;
|
let api::SysFwded(sys_id, payload) = fwd;
|
||||||
with_sys_record(sys_id, async {
|
with_sys_record(sys_id, async {
|
||||||
|
let (mut req_in, req) = pipe(1024);
|
||||||
|
let (rep, mut rep_out) = pipe(1024);
|
||||||
let mut reply = Vec::new();
|
let mut reply = Vec::new();
|
||||||
let req = TrivialReqCycle { req: &payload, rep: &mut reply };
|
let (..) = join3(
|
||||||
let _ = dyn_cted().inst().dyn_request(Box::new(req)).await;
|
async { req_in.write_all(&payload).await.expect("Ingress failed") },
|
||||||
handle.reply(fwd_tok, &reply).await
|
async { rep_out.read_to_end(&mut reply).await.expect("Egress failed") },
|
||||||
|
dyn_cted().inst().dyn_request(Box::new(TrivialReqCycle { req, rep })),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
handle.reply(fwd_tok, reply).await
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
},
|
},
|
||||||
@@ -295,7 +307,7 @@ impl ExtensionBuilder {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
let eopt = e.keep_only(|e| *e != ekey_cascade).map(|e| Err(e.to_api()));
|
let eopt = e.keep_only(|e| *e != ekey_cascade).map(|e| Err(e.to_api()));
|
||||||
expr_store.dispose().await;
|
expr_store.dispose().await;
|
||||||
return handle.reply(&lex, &eopt).await;
|
return handle.reply(&lex, eopt).await;
|
||||||
},
|
},
|
||||||
Ok((s, expr)) => {
|
Ok((s, expr)) => {
|
||||||
let expr = join_all(
|
let expr = join_all(
|
||||||
@@ -305,13 +317,13 @@ impl ExtensionBuilder {
|
|||||||
.await;
|
.await;
|
||||||
let pos = (text.len() - s.len()) as u32;
|
let pos = (text.len() - s.len()) as u32;
|
||||||
expr_store.dispose().await;
|
expr_store.dispose().await;
|
||||||
return handle.reply(&lex, &Some(Ok(api::LexedExpr { pos, expr }))).await;
|
return handle.reply(&lex, Some(Ok(api::LexedExpr { pos, expr }))).await;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writeln!(log("warn"), "Got notified about n/a character '{trigger_char}'").await;
|
writeln!(log("warn"), "Got notified about n/a character '{trigger_char}'").await;
|
||||||
expr_store.dispose().await;
|
expr_store.dispose().await;
|
||||||
handle.reply(&lex, &None).await
|
handle.reply(&lex, None).await
|
||||||
})
|
})
|
||||||
.await,
|
.await,
|
||||||
api::HostExtReq::ParseLine(pline) => {
|
api::HostExtReq::ParseLine(pline) => {
|
||||||
@@ -335,14 +347,14 @@ impl ExtensionBuilder {
|
|||||||
};
|
};
|
||||||
mem::drop(line);
|
mem::drop(line);
|
||||||
expr_store.dispose().await;
|
expr_store.dispose().await;
|
||||||
handle.reply(req, &o_line).await
|
handle.reply(req, o_line).await
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
},
|
},
|
||||||
api::HostExtReq::FetchParsedConst(ref fpc @ api::FetchParsedConst(sys, id)) =>
|
api::HostExtReq::FetchParsedConst(ref fpc @ api::FetchParsedConst(sys, id)) =>
|
||||||
with_sys_record(sys, async {
|
with_sys_record(sys, async {
|
||||||
let cnst = get_const(id).await;
|
let cnst = get_const(id).await;
|
||||||
handle.reply(fpc, &cnst.serialize().await).await
|
handle.reply(fpc, serialize(cnst).await).await
|
||||||
})
|
})
|
||||||
.await,
|
.await,
|
||||||
api::HostExtReq::AtomReq(atom_req) => {
|
api::HostExtReq::AtomReq(atom_req) => {
|
||||||
@@ -354,50 +366,48 @@ impl ExtensionBuilder {
|
|||||||
api::AtomReq::SerializeAtom(ser) => {
|
api::AtomReq::SerializeAtom(ser) => {
|
||||||
let mut buf = enc_vec(&id);
|
let mut buf = enc_vec(&id);
|
||||||
match nfo.serialize(actx, Pin::<&mut Vec<_>>::new(&mut buf)).await {
|
match nfo.serialize(actx, Pin::<&mut Vec<_>>::new(&mut buf)).await {
|
||||||
None => handle.reply(ser, &None).await,
|
None => handle.reply(ser, None).await,
|
||||||
Some(refs) => {
|
Some(refs) => {
|
||||||
let refs =
|
let refs =
|
||||||
join_all(refs.into_iter().map(async |ex| ex.into_api(&mut ()).await))
|
join_all(refs.into_iter().map(async |ex| ex.into_api(&mut ()).await))
|
||||||
.await;
|
.await;
|
||||||
handle.reply(ser, &Some((buf, refs))).await
|
handle.reply(ser, Some((buf, refs))).await
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) =>
|
api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) =>
|
||||||
handle.reply(print, &nfo.print(actx).await.to_api()).await,
|
handle.reply(print, nfo.print(actx).await.to_api()).await,
|
||||||
api::AtomReq::FinalFwded(fwded) => {
|
api::AtomReq::Fwded(fwded) => {
|
||||||
let api::FinalFwded(_, key, payload) = &fwded;
|
let api::Fwded(_, key, payload) = &fwded;
|
||||||
|
let (mut req_in, req) = pipe(1024);
|
||||||
|
let (rep, mut rep_out) = pipe(1024);
|
||||||
let mut reply = Vec::new();
|
let mut reply = Vec::new();
|
||||||
let key = Sym::from_api(*key).await;
|
let key = Sym::from_api(*key).await;
|
||||||
let req = TrivialReqCycle { req: payload, rep: &mut reply };
|
let (.., some) = join3(
|
||||||
let some = nfo.handle_req(actx, key, Box::new(req)).await;
|
async { req_in.write_all(payload).await.expect("Ingress failed") },
|
||||||
handle.reply(fwded, &some.then_some(reply)).await
|
async { rep_out.read_to_end(&mut reply).await.expect("Egress failed") },
|
||||||
},
|
nfo.handle_req_ref(actx, key, Box::new(TrivialReqCycle { req, rep })),
|
||||||
api::AtomReq::FwdedRef(fwded) => {
|
)
|
||||||
let api::FinalFwded(_, key, payload) = &fwded;
|
.await;
|
||||||
let mut reply = Vec::new();
|
handle.reply(fwded, some.then_some(reply)).await
|
||||||
let key = Sym::from_api(*key).await;
|
|
||||||
let req = TrivialReqCycle { req: payload, rep: &mut reply };
|
|
||||||
let some = nfo.handle_req_ref(actx, key, Box::new(req)).await;
|
|
||||||
handle.reply(fwded, &some.then_some(reply)).await
|
|
||||||
},
|
},
|
||||||
api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => {
|
api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => {
|
||||||
let expr_store = BorrowedExprStore::new();
|
let expr_store = BorrowedExprStore::new();
|
||||||
let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
|
let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
|
||||||
let ret = nfo.call_ref(actx, Expr::from_handle(expr_handle.clone())).await;
|
let ret = nfo.call_ref(actx, Expr::from_handle(expr_handle.clone())).await;
|
||||||
let api_expr = ret.serialize().await;
|
let api_expr = serialize(ret).await;
|
||||||
mem::drop(expr_handle);
|
mem::drop(expr_handle);
|
||||||
expr_store.dispose().await;
|
expr_store.dispose().await;
|
||||||
handle.reply(call, &api_expr).await
|
handle.reply(call, api_expr).await
|
||||||
},
|
},
|
||||||
api::AtomReq::FinalCall(call @ api::FinalCall(_, arg)) => {
|
api::AtomReq::FinalCall(call @ api::FinalCall(_, arg)) => {
|
||||||
let expr_store = BorrowedExprStore::new();
|
let expr_store = BorrowedExprStore::new();
|
||||||
let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
|
let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
|
||||||
let ret = nfo.call(actx, Expr::from_handle(expr_handle.clone())).await;
|
let ret = nfo.call(actx, Expr::from_handle(expr_handle.clone())).await;
|
||||||
let api_expr = ret.serialize().await;
|
let api_expr = serialize(ret).await;
|
||||||
mem::drop(expr_handle);
|
mem::drop(expr_handle);
|
||||||
expr_store.dispose().await;
|
expr_store.dispose().await;
|
||||||
handle.reply(call, &api_expr).await
|
handle.reply(call, api_expr).await
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -414,7 +424,7 @@ impl ExtensionBuilder {
|
|||||||
let id = AtomTypeId::decode_slice(read);
|
let id = AtomTypeId::decode_slice(read);
|
||||||
let nfo = (dyn_cted().inst().card().ops_by_atid(id))
|
let nfo = (dyn_cted().inst().card().ops_by_atid(id))
|
||||||
.expect("Deserializing atom with invalid ID");
|
.expect("Deserializing atom with invalid ID");
|
||||||
handle.reply(&deser, &nfo.deserialize(read, &refs).await).await
|
handle.reply(&deser, nfo.deserialize(read, &refs).await).await
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use futures::future::join_all;
|
|||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use orchid_base::{FmtCtx, FmtUnit, Format, OrcErrv, Pos, stash};
|
use orchid_base::{FmtCtx, FmtUnit, Format, OrcErrv, Pos, stash};
|
||||||
|
|
||||||
use crate::gen_expr::{GExpr, GExprKind};
|
use crate::gen_expr::{GExpr, slot};
|
||||||
use crate::{ForeignAtom, api, notify, request, sys_id};
|
use crate::{ForeignAtom, api, notify, request, sys_id};
|
||||||
|
|
||||||
/// Handle for a lifetime associated with an [ExprHandle], such as a function
|
/// Handle for a lifetime associated with an [ExprHandle], such as a function
|
||||||
@@ -158,9 +158,7 @@ impl Expr {
|
|||||||
pub fn handle(&self) -> Rc<ExprHandle> { self.handle.clone() }
|
pub fn handle(&self) -> Rc<ExprHandle> { self.handle.clone() }
|
||||||
/// Wrap this expression in a [GExpr] synchronously as an escape hatch.
|
/// Wrap this expression in a [GExpr] synchronously as an escape hatch.
|
||||||
/// Otherwise identical to this type's [crate::ToExpr] impl
|
/// Otherwise identical to this type's [crate::ToExpr] impl
|
||||||
pub fn slot(&self) -> GExpr {
|
pub fn slot(&self) -> GExpr { slot(self.clone()) }
|
||||||
GExpr { pos: Pos::SlotTarget, kind: GExprKind::Slot(self.clone()) }
|
|
||||||
}
|
|
||||||
/// Increments the refcount to ensure that the ticket remains valid even if
|
/// Increments the refcount to ensure that the ticket remains valid even if
|
||||||
/// the handle is freed. To avoid a leak, [Expr::deserialize] must eventually
|
/// the handle is freed. To avoid a leak, [Expr::deserialize] must eventually
|
||||||
/// be called.
|
/// be called.
|
||||||
@@ -168,11 +166,7 @@ impl Expr {
|
|||||||
}
|
}
|
||||||
impl Format for Expr {
|
impl Format for Expr {
|
||||||
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.data().await.kind {
|
FmtUnit::from_api(&request(api::ExprPrint { target: self.handle.0 }).await)
|
||||||
ExprKind::Opaque => "OPAQUE".to_string().into(),
|
|
||||||
ExprKind::Bottom(b) => format!("Bottom({b})").into(),
|
|
||||||
ExprKind::Atom(a) => FmtUnit::from_api(&request(api::ExtAtomPrint(a.atom.clone())).await),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Eq for Expr {}
|
impl Eq for Expr {}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::pin::{Pin, pin};
|
use std::pin::{Pin, pin};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@@ -6,22 +8,38 @@ use futures::{FutureExt, Stream, StreamExt, stream};
|
|||||||
use orchid_base::{
|
use orchid_base::{
|
||||||
FmtCtx, FmtUnit, Format, OrcErr, OrcErrv, Pos, Sym, Variants, match_mapping, tl_cache,
|
FmtCtx, FmtUnit, Format, OrcErr, OrcErrv, Pos, Sym, Variants, match_mapping, tl_cache,
|
||||||
};
|
};
|
||||||
|
use substack::Substack;
|
||||||
|
use task_local::task_local;
|
||||||
|
|
||||||
use crate::{AtomFactory, AtomicFeatures, Expr, ToExpr, ToExprFuture, api, request, sys_id};
|
use crate::{AtomFactory, AtomicFeatures, Expr, ToExpr, ToExprFuture, api, request, sys_id};
|
||||||
|
|
||||||
/// Newly generated AST. Values of this type should not typically be constructed
|
#[derive(Clone, Copy, Debug)]
|
||||||
/// manually but through the helpers in this module
|
struct ExprSerializeCx<'a> {
|
||||||
|
closures: Substack<'a, u64>,
|
||||||
|
lambda_counter: &'a RefCell<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Release notifications will not be sent for the slots. Use this with
|
||||||
|
/// messages that imply ownership transfer
|
||||||
|
pub async fn serialize(expr: GExpr) -> api::Expression {
|
||||||
|
let cx = ExprSerializeCx { closures: Substack::Bottom, lambda_counter: &RefCell::new(0) };
|
||||||
|
expr.serialize(cx).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Smart object representing AST not-yet-sent to the interpreter. This type can
|
||||||
|
/// be cloned and persisted, and it must not have unbound arguments. The helper
|
||||||
|
/// functions in this module let you build trees of [ToExpr] implementors which
|
||||||
|
/// represent lambdas and their arguments separately, and then convert them into
|
||||||
|
/// [GExpr] in one pass.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct GExpr {
|
pub struct GExpr {
|
||||||
/// AST node type
|
/// AST node type
|
||||||
pub kind: GExprKind,
|
kind: GExprKind,
|
||||||
/// Code location associated with the expression for debugging purposes
|
/// Code location associated with the expression for debugging purposes
|
||||||
pub pos: Pos,
|
pos: Pos,
|
||||||
}
|
}
|
||||||
impl GExpr {
|
impl GExpr {
|
||||||
/// Release notifications will not be sent for the slots. Use this with
|
async fn serialize(self, cx: ExprSerializeCx<'_>) -> api::Expression {
|
||||||
/// messages that imply ownership transfer
|
|
||||||
pub async fn serialize(self) -> api::Expression {
|
|
||||||
if let GExprKind::Slot(ex) = self.kind {
|
if let GExprKind::Slot(ex) = self.kind {
|
||||||
let hand = ex.handle();
|
let hand = ex.handle();
|
||||||
mem::drop(ex);
|
mem::drop(ex);
|
||||||
@@ -32,8 +50,8 @@ impl GExpr {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
api::Expression {
|
api::Expression {
|
||||||
location: api::Location::Inherit,
|
location: self.pos.to_api(),
|
||||||
kind: self.kind.serialize().boxed_local().await,
|
kind: self.kind.serialize(cx).boxed_local().await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,7 +60,7 @@ impl GExpr {
|
|||||||
/// Send the expression to the interpreter to be compiled and to become
|
/// Send the expression to the interpreter to be compiled and to become
|
||||||
/// shareable across extensions
|
/// shareable across extensions
|
||||||
pub async fn create(self) -> Expr {
|
pub async fn create(self) -> Expr {
|
||||||
Expr::deserialize(request(api::Create(sys_id(), self.serialize().await)).await).await
|
Expr::deserialize(request(api::Create(sys_id(), serialize(self).await)).await).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Format for GExpr {
|
impl Format for GExpr {
|
||||||
@@ -56,8 +74,8 @@ impl Format for GExpr {
|
|||||||
pub enum GExprKind {
|
pub enum GExprKind {
|
||||||
/// Function call
|
/// Function call
|
||||||
Call(Box<GExpr>, Box<GExpr>),
|
Call(Box<GExpr>, Box<GExpr>),
|
||||||
/// Lambda expression. Argument numbers are matched when equal
|
/// Lambda expression. Argument must be the same for slot
|
||||||
Lambda(u64, Box<GExpr>),
|
Lambda(Box<GExpr>),
|
||||||
/// Slot for a lambda argument
|
/// Slot for a lambda argument
|
||||||
Arg(u64),
|
Arg(u64),
|
||||||
/// The second expression is only valid after the first one had already been
|
/// The second expression is only valid after the first one had already been
|
||||||
@@ -80,23 +98,40 @@ pub enum GExprKind {
|
|||||||
Bottom(OrcErrv),
|
Bottom(OrcErrv),
|
||||||
}
|
}
|
||||||
impl GExprKind {
|
impl GExprKind {
|
||||||
async fn serialize(self) -> api::ExpressionKind {
|
pub fn at(self, pos: Pos) -> GExpr { GExpr { kind: self, pos } }
|
||||||
|
async fn serialize(self, cx: ExprSerializeCx<'_>) -> api::ExpressionKind {
|
||||||
match_mapping!(self, Self => api::ExpressionKind {
|
match_mapping!(self, Self => api::ExpressionKind {
|
||||||
Call(
|
Call(
|
||||||
f => Box::new(f.serialize().await),
|
f => Box::new(f.serialize(cx).await),
|
||||||
x => Box::new(x.serialize().await)
|
x => Box::new(x.serialize(cx).await)
|
||||||
),
|
),
|
||||||
Seq(
|
Seq(
|
||||||
a => Box::new(a.serialize().await),
|
a => Box::new(a.serialize(cx).await),
|
||||||
b => Box::new(b.serialize().await)
|
b => Box::new(b.serialize(cx).await)
|
||||||
),
|
),
|
||||||
Lambda(arg, body => Box::new(body.serialize().await)),
|
|
||||||
Arg(arg),
|
|
||||||
Const(name.to_api()),
|
Const(name.to_api()),
|
||||||
Bottom(err.to_api()),
|
Bottom(err.to_api()),
|
||||||
NewAtom(fac.clone().build().await),
|
NewAtom(fac.clone().build().await),
|
||||||
} {
|
} {
|
||||||
Self::Slot(_) => panic!("processed elsewhere")
|
Self::Slot(_) => panic!("processed elsewhere"),
|
||||||
|
Self::Lambda(body) => {
|
||||||
|
let id: u64;
|
||||||
|
{
|
||||||
|
let mut g = cx.lambda_counter.borrow_mut();
|
||||||
|
id = *g;
|
||||||
|
*g += 1;
|
||||||
|
};
|
||||||
|
let cx = ExprSerializeCx {
|
||||||
|
lambda_counter: cx.lambda_counter,
|
||||||
|
closures: cx.closures.push(id)
|
||||||
|
};
|
||||||
|
api::ExpressionKind::Lambda(id,
|
||||||
|
Box::new(body.serialize(cx).await)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Self::Arg(arg) => {
|
||||||
|
api::ExpressionKind::Arg(*cx.closures.iter().nth(arg as usize).expect("Unbound arg"))
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,9 +141,9 @@ impl Format for GExprKind {
|
|||||||
GExprKind::Call(f, x) =>
|
GExprKind::Call(f, x) =>
|
||||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0} ({1})")))
|
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0} ({1})")))
|
||||||
.units([f.print(c).await, x.print(c).await]),
|
.units([f.print(c).await, x.print(c).await]),
|
||||||
GExprKind::Lambda(arg, body) =>
|
GExprKind::Lambda(body) =>
|
||||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0}.{1}")))
|
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{1}")))
|
||||||
.units([arg.to_string().into(), body.print(c).await]),
|
.units([body.print(c).await]),
|
||||||
GExprKind::Arg(arg) => arg.to_string().into(),
|
GExprKind::Arg(arg) => arg.to_string().into(),
|
||||||
GExprKind::Seq(a, b) =>
|
GExprKind::Seq(a, b) =>
|
||||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("[{0}] {1}")))
|
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("[{0}] {1}")))
|
||||||
@@ -123,7 +158,11 @@ impl Format for GExprKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inherit(kind: GExprKind) -> GExpr { GExpr { pos: Pos::Inherit, kind } }
|
pub fn inherit(kind: GExprKind) -> GExpr { GExpr { pos: Pos::Inherit, kind } }
|
||||||
|
|
||||||
|
task_local! {
|
||||||
|
pub static CLOSURE_DEPTH: u64;
|
||||||
|
}
|
||||||
|
|
||||||
impl ToExpr for Sym {
|
impl ToExpr for Sym {
|
||||||
async fn to_expr(self) -> Expr
|
async fn to_expr(self) -> Expr
|
||||||
@@ -135,6 +174,8 @@ impl ToExpr for Sym {
|
|||||||
/// Creates an expression from a new atom that we own.
|
/// 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 new_atom<A: AtomicFeatures>(atom: A) -> GExpr { inherit(GExprKind::NewAtom(atom.factory())) }
|
||||||
|
|
||||||
|
pub fn slot(expr: Expr) -> GExpr { GExpr { pos: Pos::SlotTarget, kind: GExprKind::Slot(expr) } }
|
||||||
|
|
||||||
/// An expression which is only valid if a number of dependencies had already
|
/// An expression which is only valid if a number of dependencies had already
|
||||||
/// been normalized
|
/// been normalized
|
||||||
pub fn seq(
|
pub fn seq(
|
||||||
@@ -155,17 +196,49 @@ pub fn seq(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Argument bound by an enclosing [lam] or [dyn_lambda]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub fn arg(n: u64) -> GExpr { inherit(GExprKind::Arg(n)) }
|
pub enum ArgState {
|
||||||
|
Building,
|
||||||
/// A lambda expression. The difference from [dyn_lambda] is purely aesthetic
|
Serializing { depth: u64 },
|
||||||
pub fn lam<const N: u64>(b: impl ToExpr) -> ToExprFuture<impl Future<Output = GExpr>> {
|
Ready,
|
||||||
dyn_lambda(N, b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A lambda expression. The difference from [lam] is purely aesthetic
|
/// Argument bound by an enclosing [lam] or [dyn_lambda]
|
||||||
pub fn dyn_lambda(n: u64, b: impl ToExpr) -> ToExprFuture<impl Future<Output = GExpr>> {
|
#[derive(Debug, Clone, Copy)]
|
||||||
ToExprFuture(async move { inherit(GExprKind::Lambda(n, Box::new(b.to_gen().await))) })
|
pub struct GenArg<'a>(*const RefCell<ArgState>, PhantomData<&'a ()>);
|
||||||
|
impl ToExpr for GenArg<'_> {
|
||||||
|
async fn to_gen(self) -> GExpr {
|
||||||
|
// SAFETY: Created from a Rc that lives as long as the lifetime arg, see [lam]
|
||||||
|
let state = unsafe { self.0.as_ref().unwrap() };
|
||||||
|
match (*state.borrow(), CLOSURE_DEPTH.try_with(|r| *r)) {
|
||||||
|
(ArgState::Serializing { .. }, Err(_)) =>
|
||||||
|
panic!("Lambda should have cleared up argstate alongside CLOSURE_DEPTH"),
|
||||||
|
(ArgState::Serializing { depth }, Ok(total)) => inherit(GExprKind::Arg(total - depth)),
|
||||||
|
(ArgState::Building, _) =>
|
||||||
|
panic!("Argument serialized before lambda. Likely an over-eager ToExpr impl"),
|
||||||
|
(ArgState::Ready, _) =>
|
||||||
|
unreachable!("The arg should never be available this long, the GenArg is a convenience"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A lambda expression.
|
||||||
|
pub fn lam<'a>(
|
||||||
|
cb: impl for<'b> AsyncFnOnce(GenArg<'b>) -> GExpr + 'a,
|
||||||
|
) -> ToExprFuture<impl Future<Output = GExpr> + 'a> {
|
||||||
|
let state = Rc::new(RefCell::new(ArgState::Building));
|
||||||
|
ToExprFuture(async move {
|
||||||
|
let rank = CLOSURE_DEPTH.try_with(|r| *r + 1).unwrap_or(0);
|
||||||
|
match *state.borrow_mut() {
|
||||||
|
ref mut state @ ArgState::Building => *state = ArgState::Serializing { depth: rank },
|
||||||
|
ArgState::Serializing { .. } => panic!("Lambda serialized twice, found interrupted"),
|
||||||
|
ArgState::Ready => panic!("Lambda serialized twice"),
|
||||||
|
}
|
||||||
|
let gen_arg = GenArg(Rc::as_ptr(&state), PhantomData);
|
||||||
|
let ret = CLOSURE_DEPTH.scope(rank, async { cb(gen_arg).await.to_gen().await }).await;
|
||||||
|
mem::drop(state);
|
||||||
|
inherit(GExprKind::Lambda(Box::new(ret)))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// one or more items that are convertible to expressions. In practice, a
|
/// one or more items that are convertible to expressions. In practice, a
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#![allow(refining_impl_trait, reason = "Has various false-positives around lints")]
|
#![allow(refining_impl_trait, reason = "Has various false-positives around lints")]
|
||||||
use orchid_api as api;
|
use orchid_api as api;
|
||||||
|
|
||||||
|
mod streams;
|
||||||
|
pub use streams::*;
|
||||||
mod atom;
|
mod atom;
|
||||||
pub use atom::*;
|
pub use atom::*;
|
||||||
mod cmd_atom;
|
mod cmd_atom;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use task_local::task_local;
|
|||||||
|
|
||||||
use crate::gen_expr::GExpr;
|
use crate::gen_expr::GExpr;
|
||||||
use crate::tree::{GenTok, GenTokTree};
|
use crate::tree::{GenTok, GenTokTree};
|
||||||
use crate::{Expr, ToExpr, api, request, sys_id};
|
use crate::{ExecHandle, Expr, ToExpr, TryFromExpr, api, exec, request, sys_id};
|
||||||
|
|
||||||
/// [PTokTree] without [orchid_base::Pos] metadata
|
/// [PTokTree] without [orchid_base::Pos] metadata
|
||||||
pub type PTok = Token<Expr, Never>;
|
pub type PTok = Token<Expr, Never>;
|
||||||
@@ -118,7 +118,7 @@ impl<'a> ParsCtx<'a> {
|
|||||||
pub fn module(&self) -> Sym { self.module.clone() }
|
pub fn module(&self) -> Sym { self.module.clone() }
|
||||||
}
|
}
|
||||||
|
|
||||||
type BoxConstCallback = Box<dyn FnOnce(ConstCtx) -> LocalBoxFuture<'static, GExpr>>;
|
type BoxConstCallback = Box<dyn for<'a> FnOnce(ConstCtx<'a>) -> LocalBoxFuture<'a, GExpr>>;
|
||||||
|
|
||||||
task_local! {
|
task_local! {
|
||||||
static CONST_TBL: RefCell<HashMap<NonZero<u64>, BoxConstCallback>>;
|
static CONST_TBL: RefCell<HashMap<NonZero<u64>, BoxConstCallback>>;
|
||||||
@@ -143,14 +143,15 @@ impl ParsedLine {
|
|||||||
/// Define a constant. The callback is only called later if the constant is
|
/// Define a constant. The callback is only called later if the constant is
|
||||||
/// referenced, and it can call [crate::refl] to base its value on the module
|
/// referenced, and it can call [crate::refl] to base its value on the module
|
||||||
/// tree or use its argument for context-specific queries
|
/// tree or use its argument for context-specific queries
|
||||||
pub fn cnst<'a, R: ToExpr + 'static, F: AsyncFnOnce(ConstCtx) -> R + 'static>(
|
pub fn cnst<'a, R: ToExpr + 'static, F: for<'b> AsyncFnOnce(ConstCtx<'b>) -> R + 'static>(
|
||||||
sr: &SrcRange,
|
sr: &SrcRange,
|
||||||
comments: impl IntoIterator<Item = &'a Comment>,
|
comments: impl IntoIterator<Item = &'a Comment>,
|
||||||
exported: bool,
|
exported: bool,
|
||||||
name: IStr,
|
name: IStr,
|
||||||
f: F,
|
f: F,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let cb = Box::new(|ctx| async move { f(ctx).await.to_gen().await }.boxed_local());
|
let cb: BoxConstCallback =
|
||||||
|
Box::new(|ctx| async move { f(ctx).await.to_gen().await }.boxed_local());
|
||||||
let kind = ParsedLineKind::Mem(ParsedMem { name, exported, kind: ParsedMemKind::Const(cb) });
|
let kind = ParsedLineKind::Mem(ParsedMem { name, exported, kind: ParsedMemKind::Const(cb) });
|
||||||
let comments = comments.into_iter().cloned().collect();
|
let comments = comments.into_iter().cloned().collect();
|
||||||
ParsedLine { comments, sr: sr.clone(), kind }
|
ParsedLine { comments, sr: sr.clone(), kind }
|
||||||
@@ -241,11 +242,11 @@ pub enum ParsedMemKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Enable a generated constant to query about its environment
|
/// Enable a generated constant to query about its environment
|
||||||
#[derive(Clone)]
|
pub struct ConstCtx<'a> {
|
||||||
pub struct ConstCtx {
|
|
||||||
constid: api::ParsedConstId,
|
constid: api::ParsedConstId,
|
||||||
|
exec: ExecHandle<'a>,
|
||||||
}
|
}
|
||||||
impl ConstCtx {
|
impl<'a> ConstCtx<'a> {
|
||||||
/// Resolve a set of local names into the full names they would point to if
|
/// Resolve a set of local names into the full names they would point to if
|
||||||
/// they were globally bound. Errors produced at this stage are soft, as the
|
/// they were globally bound. Errors produced at this stage are soft, as the
|
||||||
/// names may still be found to be locally bound within the expression
|
/// names may still be found to be locally bound within the expression
|
||||||
@@ -272,10 +273,14 @@ impl ConstCtx {
|
|||||||
pub async fn names_n<const N: usize>(&self, names: [&Sym; N]) -> [OrcRes<Sym>; N] {
|
pub async fn names_n<const N: usize>(&self, names: [&Sym; N]) -> [OrcRes<Sym>; N] {
|
||||||
self.names(names).collect::<Vec<_>>().await.try_into().expect("Lengths must match")
|
self.names(names).collect::<Vec<_>>().await.try_into().expect("Lengths must match")
|
||||||
}
|
}
|
||||||
|
pub async fn exec<R: TryFromExpr>(&mut self, val: impl ToExpr) -> OrcRes<R> {
|
||||||
|
self.exec.exec(val).await
|
||||||
|
}
|
||||||
|
pub fn handle(&mut self) -> &mut ExecHandle<'a> { &mut self.exec }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_const(id: api::ParsedConstId) -> GExpr {
|
pub(crate) async fn get_const(id: api::ParsedConstId) -> GExpr {
|
||||||
let cb = CONST_TBL
|
let cb = CONST_TBL
|
||||||
.with(|ent| ent.borrow_mut().remove(&id.0).expect("Bad ID or double read of parsed const"));
|
.with(|ent| ent.borrow_mut().remove(&id.0).expect("Bad ID or double read of parsed const"));
|
||||||
cb(ConstCtx { constid: id }).await
|
exec(async move |exec| cb(ConstCtx { constid: id, exec }).await).await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
use std::io;
|
||||||
use std::num::NonZero;
|
use std::num::NonZero;
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
use orchid_api_derive::{Coding, Hierarchy};
|
use orchid_api_derive::{Coding, Hierarchy};
|
||||||
use orchid_api_traits::Request;
|
use orchid_api_traits::Request;
|
||||||
|
|
||||||
@@ -15,20 +16,20 @@ impl Request for Spawn {
|
|||||||
|
|
||||||
/// Execute the atom as a command.
|
/// Execute the atom as a command.
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
pub struct RunCommand;
|
pub struct StartCommand;
|
||||||
impl Request for RunCommand {
|
impl Request for StartCommand {
|
||||||
type Response = Option<api::Expression>;
|
type Response = Option<api::Expression>;
|
||||||
}
|
}
|
||||||
impl AtomMethod for RunCommand {
|
impl AtomMethod for StartCommand {
|
||||||
const NAME: &str = "orchid::cmd::run";
|
const NAME: &str = "orchid::cmd::run";
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
pub struct AsDuration;
|
pub struct AsInstant;
|
||||||
impl Request for AsDuration {
|
impl Request for AsInstant {
|
||||||
type Response = Duration;
|
type Response = DateTime<Utc>;
|
||||||
}
|
}
|
||||||
impl AtomMethod for AsDuration {
|
impl AtomMethod for AsInstant {
|
||||||
const NAME: &str = "orchid::time::as_duration";
|
const NAME: &str = "orchid::time::as_duration";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +59,20 @@ pub struct IoError {
|
|||||||
pub message: String,
|
pub message: String,
|
||||||
pub kind: IoErrorKind,
|
pub kind: IoErrorKind,
|
||||||
}
|
}
|
||||||
|
impl From<io::Error> for IoError {
|
||||||
|
fn from(value: io::Error) -> Self {
|
||||||
|
Self {
|
||||||
|
message: value.to_string(),
|
||||||
|
kind: match value.kind() {
|
||||||
|
io::ErrorKind::Interrupted
|
||||||
|
| io::ErrorKind::BrokenPipe
|
||||||
|
| io::ErrorKind::NetworkDown
|
||||||
|
| io::ErrorKind::ConnectionReset => IoErrorKind::Interrupted,
|
||||||
|
_ => IoErrorKind::Other,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Coding)]
|
#[derive(Clone, Debug, Coding)]
|
||||||
pub enum ReadLimit {
|
pub enum ReadLimit {
|
||||||
@@ -69,7 +84,9 @@ pub enum ReadLimit {
|
|||||||
/// Read all available data from a stream. If the returned vector is empty, the
|
/// Read all available data from a stream. If the returned vector is empty, the
|
||||||
/// stream has reached its end.
|
/// stream has reached its end.
|
||||||
#[derive(Clone, Debug, Coding, Hierarchy)]
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
pub struct ReadReq(pub ReadLimit);
|
pub struct ReadReq {
|
||||||
|
pub limit: ReadLimit,
|
||||||
|
}
|
||||||
impl Request for ReadReq {
|
impl Request for ReadReq {
|
||||||
type Response = Result<Vec<u8>, IoError>;
|
type Response = Result<Vec<u8>, IoError>;
|
||||||
}
|
}
|
||||||
|
|||||||
95
orchid-extension/src/streams.rs
Normal file
95
orchid-extension/src/streams.rs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
use std::io::Result;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use futures::io::BufReader;
|
||||||
|
use futures::lock::Mutex;
|
||||||
|
use futures::{AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||||
|
use never::Never;
|
||||||
|
use orchid_base::{Receipt, ReqHandle, ReqHandleExt};
|
||||||
|
|
||||||
|
use crate::gen_expr::{GExpr, new_atom};
|
||||||
|
use crate::std_reqs::{CloseReq, FlushReq, OutputReq, ReadLimit, ReadReq, WriteReq};
|
||||||
|
use crate::{Atomic, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, ToExpr};
|
||||||
|
|
||||||
|
struct WriterState {
|
||||||
|
buf: Vec<u8>,
|
||||||
|
writer: Pin<Box<dyn AsyncWrite>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OrcWriter<T: AsyncWrite + 'static>(pub T);
|
||||||
|
impl<T: AsyncWrite + 'static> ToExpr for OrcWriter<T> {
|
||||||
|
async fn to_gen(self) -> GExpr {
|
||||||
|
new_atom(WriterAtom(Rc::new(Mutex::new(WriterState {
|
||||||
|
buf: Vec::new(),
|
||||||
|
writer: Box::pin(self.0),
|
||||||
|
}))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct WriterAtom(Rc<Mutex<WriterState>>);
|
||||||
|
impl Atomic for WriterAtom {
|
||||||
|
type Variant = OwnedVariant;
|
||||||
|
type Data = ();
|
||||||
|
fn reg_methods() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<OutputReq>() }
|
||||||
|
}
|
||||||
|
impl OwnedAtom for WriterAtom {
|
||||||
|
type Refs = Never;
|
||||||
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
|
}
|
||||||
|
impl Supports<OutputReq> for WriterAtom {
|
||||||
|
async fn handle(&self, hand: Box<dyn ReqHandle>, req: OutputReq) -> Result<Receipt> {
|
||||||
|
match req {
|
||||||
|
OutputReq::WriteReq(ref wr @ WriteReq { ref data }) => {
|
||||||
|
self.0.lock().await.buf.extend(data);
|
||||||
|
hand.reply(wr, Ok(())).await
|
||||||
|
},
|
||||||
|
OutputReq::FlushReq(ref fr @ FlushReq) => {
|
||||||
|
let mut g = self.0.lock().await;
|
||||||
|
let WriterState { buf, writer } = &mut *g;
|
||||||
|
hand.reply(fr, writer.write_all(&buf[..]).await.map_err(|e| e.into())).await
|
||||||
|
},
|
||||||
|
OutputReq::CloseReq(ref cr @ CloseReq) =>
|
||||||
|
hand.reply(cr, self.0.lock().await.writer.close().await.map_err(|e| e.into())).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OrcReader<T: AsyncRead + 'static>(pub T);
|
||||||
|
impl<T: AsyncRead + 'static> ToExpr for OrcReader<T> {
|
||||||
|
async fn to_gen(self) -> GExpr {
|
||||||
|
new_atom(ReaderAtom(Rc::new(Mutex::new(BufReader::new(Box::pin(self.0))))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ReaderAtom(Rc<Mutex<BufReader<Pin<Box<dyn AsyncRead>>>>>);
|
||||||
|
impl Atomic for ReaderAtom {
|
||||||
|
type Variant = OwnedVariant;
|
||||||
|
type Data = ();
|
||||||
|
fn reg_methods() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<ReadReq>() }
|
||||||
|
}
|
||||||
|
impl OwnedAtom for ReaderAtom {
|
||||||
|
type Refs = Never;
|
||||||
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
|
}
|
||||||
|
impl Supports<ReadReq> for ReaderAtom {
|
||||||
|
async fn handle(&self, hand: Box<dyn ReqHandle>, req: ReadReq) -> Result<Receipt> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let mut reader = self.0.lock().await;
|
||||||
|
let rep = match match req.limit {
|
||||||
|
ReadLimit::End => reader.read_to_end(&mut buf).await.map(|_| ()),
|
||||||
|
ReadLimit::Delimiter(b) => reader.read_until(b, &mut buf).await.map(|_| ()),
|
||||||
|
ReadLimit::Length(n) => {
|
||||||
|
buf = vec![0u8; n.get() as usize];
|
||||||
|
reader.read_exact(&mut buf).await
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
Ok(()) => Ok(buf),
|
||||||
|
};
|
||||||
|
hand.reply(&req, rep).await
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,15 +16,15 @@ pub type ReqForSystem<T> = <CardForSystem<T> as SystemCard>::Req;
|
|||||||
/// System as defined by author
|
/// System as defined by author
|
||||||
pub trait System: Debug + 'static {
|
pub trait System: Debug + 'static {
|
||||||
type Ctor: SystemCtor<Instance = Self>;
|
type Ctor: SystemCtor<Instance = Self>;
|
||||||
fn prelude(&self) -> impl Future<Output = Vec<Sym>>;
|
fn prelude(&self) -> impl Future<Output = Vec<Sym>> { futures::future::ready(Vec::new()) }
|
||||||
fn env(&self) -> impl Future<Output = Vec<GenMember>>;
|
fn env(&self) -> impl Future<Output = Vec<GenMember>> { futures::future::ready(Vec::new()) }
|
||||||
fn lexers(&self) -> Vec<LexerObj>;
|
fn lexers(&self) -> Vec<LexerObj> { Vec::new() }
|
||||||
fn parsers(&self) -> Vec<ParserObj>;
|
fn parsers(&self) -> Vec<ParserObj> { Vec::new() }
|
||||||
fn request<'a>(
|
fn request(
|
||||||
&self,
|
&self,
|
||||||
hand: Box<dyn ReqHandle<'a> + 'a>,
|
hand: Box<dyn ReqHandle>,
|
||||||
req: ReqForSystem<Self>,
|
req: ReqForSystem<Self>,
|
||||||
) -> impl Future<Output = Receipt<'a>>;
|
) -> impl Future<Output = Receipt>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DynSystem: Debug + 'static {
|
pub trait DynSystem: Debug + 'static {
|
||||||
@@ -32,10 +32,7 @@ pub trait DynSystem: Debug + 'static {
|
|||||||
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, 'b: 'a>(
|
fn dyn_request<'a, 'b: 'a>(&'a self, hand: Box<dyn ReqReader>) -> LocalBoxFuture<'a, Receipt>;
|
||||||
&'a self,
|
|
||||||
hand: Box<dyn ReqReader<'b> + 'b>,
|
|
||||||
) -> LocalBoxFuture<'a, Receipt<'b>>;
|
|
||||||
fn card(&self) -> Box<dyn DynSystemCard>;
|
fn card(&self) -> Box<dyn DynSystemCard>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,8 +43,8 @@ impl<T: System> DynSystem for T {
|
|||||||
fn dyn_parsers(&self) -> Vec<ParserObj> { self.parsers() }
|
fn dyn_parsers(&self) -> Vec<ParserObj> { self.parsers() }
|
||||||
fn dyn_request<'a, 'b: 'a>(
|
fn dyn_request<'a, 'b: 'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
mut hand: Box<dyn ReqReader<'b> + 'b>,
|
mut hand: Box<dyn ReqReader>,
|
||||||
) -> LocalBoxFuture<'a, Receipt<'b>> {
|
) -> LocalBoxFuture<'a, Receipt> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let value = hand.read_req().await.unwrap();
|
let value = hand.read_req().await.unwrap();
|
||||||
self.request(hand.finish().await, value).await
|
self.request(hand.finish().await, value).await
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ use std::num::NonZero;
|
|||||||
use orchid_api_traits::Coding;
|
use orchid_api_traits::Coding;
|
||||||
use orchid_base::BoxedIter;
|
use orchid_base::BoxedIter;
|
||||||
|
|
||||||
use crate::{AtomOps, AtomTypeId, Atomic, AtomicFeatures, Fun, Lambda, Replier, SystemCtor};
|
use crate::{
|
||||||
|
AtomOps, AtomTypeId, Atomic, AtomicFeatures, CmdAtom, Fun, Lambda, ReaderAtom, Replier,
|
||||||
|
SystemCtor, WriterAtom,
|
||||||
|
};
|
||||||
|
|
||||||
/// Description of a system. This is intended to be a ZST storing the static
|
/// Description of a system. This is intended to be a ZST storing the static
|
||||||
/// properties of a [SystemCtor] which should be known to foreign systems
|
/// properties of a [SystemCtor] which should be known to foreign systems
|
||||||
@@ -56,5 +59,13 @@ pub(crate) trait DynSystemCardExt: DynSystemCard {
|
|||||||
/// 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)
|
||||||
pub(crate) fn general_atoms() -> impl Iterator<Item = Option<Box<dyn AtomOps>>> {
|
pub(crate) fn general_atoms() -> impl Iterator<Item = Option<Box<dyn AtomOps>>> {
|
||||||
[Some(Fun::ops()), Some(Lambda::ops()), Some(Replier::ops())].into_iter()
|
[
|
||||||
|
Some(Fun::ops()),
|
||||||
|
Some(Lambda::ops()),
|
||||||
|
Some(Replier::ops()),
|
||||||
|
Some(CmdAtom::ops()),
|
||||||
|
Some(WriterAtom::ops()),
|
||||||
|
Some(ReaderAtom::ops()),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use substack::Substack;
|
|||||||
use task_local::task_local;
|
use task_local::task_local;
|
||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
use crate::gen_expr::{GExpr, new_atom};
|
use crate::gen_expr::{GExpr, new_atom, serialize};
|
||||||
use crate::{BorrowedExprStore, Expr, ExprFunc, ExprHandle, Fun, ToExpr, api};
|
use crate::{BorrowedExprStore, Expr, ExprFunc, ExprHandle, Fun, ToExpr, api};
|
||||||
|
|
||||||
/// Tokens generated by lexers and parsers
|
/// Tokens generated by lexers and parsers
|
||||||
@@ -31,7 +31,7 @@ impl TokenVariant<api::Expression> for GExpr {
|
|||||||
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 { serialize(self).await }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenVariant<api::ExprTicket> for Expr {
|
impl TokenVariant<api::ExprTicket> for Expr {
|
||||||
@@ -193,7 +193,7 @@ impl LazyMemKind {
|
|||||||
pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind {
|
pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind {
|
||||||
match self {
|
match self {
|
||||||
Self::Lazy(lazy) => api::MemberKind::Lazy(add_lazy(ctx, lazy)),
|
Self::Lazy(lazy) => api::MemberKind::Lazy(add_lazy(ctx, lazy)),
|
||||||
Self::Const(c) => api::MemberKind::Const(c.serialize().await),
|
Self::Const(c) => api::MemberKind::Const(serialize(c).await),
|
||||||
Self::Mod(members) => api::MemberKind::Module(api::Module {
|
Self::Mod(members) => api::MemberKind::Module(api::Module {
|
||||||
members: stream(async |mut cx| {
|
members: stream(async |mut cx| {
|
||||||
for m in members {
|
for m in members {
|
||||||
|
|||||||
@@ -4,25 +4,26 @@ use std::pin::Pin;
|
|||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use futures::{AsyncRead, AsyncWrite};
|
use futures::{AsyncRead, AsyncWrite};
|
||||||
use orchid_base::{Receipt, RepWriter, ReqHandle, ReqReader};
|
use orchid_base::{Receipt, RepWriter, ReqHandle, ReqReader};
|
||||||
|
use unsync_pipe::{Reader, Writer};
|
||||||
|
|
||||||
pub struct TrivialReqCycle<'a> {
|
pub struct TrivialReqCycle {
|
||||||
pub req: &'a [u8],
|
pub req: Reader,
|
||||||
pub rep: &'a mut Vec<u8>,
|
pub rep: Writer,
|
||||||
}
|
}
|
||||||
impl<'a> ReqReader<'a> for TrivialReqCycle<'a> {
|
impl ReqReader for TrivialReqCycle {
|
||||||
fn reader(&mut self) -> Pin<&mut dyn AsyncRead> { Pin::new(&mut self.req) as Pin<&mut _> }
|
fn reader(&mut self) -> Pin<&mut dyn AsyncRead> { Pin::new(&mut self.req) as Pin<&mut _> }
|
||||||
fn finish(self: Box<Self>) -> LocalBoxFuture<'a, Box<dyn ReqHandle<'a> + 'a>> {
|
fn finish(self: Box<Self>) -> LocalBoxFuture<'static, Box<dyn ReqHandle>> {
|
||||||
Box::pin(async { self as Box<_> })
|
Box::pin(async { self as Box<_> })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> ReqHandle<'a> for TrivialReqCycle<'a> {
|
impl ReqHandle for TrivialReqCycle {
|
||||||
fn start_reply(self: Box<Self>) -> LocalBoxFuture<'a, io::Result<Box<dyn RepWriter<'a> + 'a>>> {
|
fn start_reply(self: Box<Self>) -> LocalBoxFuture<'static, io::Result<Box<dyn RepWriter>>> {
|
||||||
Box::pin(async { Ok(self as Box<_>) })
|
Box::pin(async { Ok(self as Box<_>) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> RepWriter<'a> for TrivialReqCycle<'a> {
|
impl RepWriter for TrivialReqCycle {
|
||||||
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { Pin::new(&mut self.rep) as Pin<&mut _> }
|
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { Pin::new(&mut self.rep) as Pin<&mut _> }
|
||||||
fn finish(self: Box<Self>) -> LocalBoxFuture<'a, io::Result<Receipt<'a>>> {
|
fn finish(self: Box<Self>) -> LocalBoxFuture<'static, io::Result<Receipt>> {
|
||||||
Box::pin(async { Ok(Receipt::_new()) })
|
Box::pin(async { Ok(Receipt::_new()) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,10 +10,11 @@ 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"
|
||||||
|
chrono = "0.4.44"
|
||||||
derive_destructure = "1.0.0"
|
derive_destructure = "1.0.0"
|
||||||
futures = { version = "0.3.31", features = ["std"], default-features = false }
|
futures = { version = "0.3.32", features = ["std"], default-features = false }
|
||||||
futures-locks = "0.7.1"
|
futures-locks = "0.7.1"
|
||||||
hashbrown = "0.16.1"
|
hashbrown = "0.17.0"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
libloading = { version = "0.9.0", optional = true }
|
libloading = { version = "0.9.0", optional = true }
|
||||||
@@ -22,13 +23,14 @@ never = "0.1.0"
|
|||||||
num-traits = "0.2.19"
|
num-traits = "0.2.19"
|
||||||
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
||||||
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" }
|
||||||
orchid-extension = { version = "0.1.0", path = "../orchid-extension", optional = true }
|
orchid-extension = { version = "0.1.0", path = "../orchid-extension", optional = true }
|
||||||
ordered-float = "5.1.0"
|
ordered-float = "5.3.0"
|
||||||
pastey = "0.2.1"
|
pastey = "0.2.2"
|
||||||
substack = "1.1.1"
|
substack = "1.1.1"
|
||||||
task-local = "0.1.0"
|
task-local = "0.1.1"
|
||||||
tokio = { version = "1.49.0", features = ["process"], optional = true }
|
tokio = { version = "1.52.1", features = ["process"], optional = true }
|
||||||
tokio-util = { version = "0.7.18", features = ["compat"], optional = true }
|
tokio-util = { version = "0.7.18", features = ["compat"], optional = true }
|
||||||
trait-set = "0.3.0"
|
trait-set = "0.3.0"
|
||||||
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
|
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ impl AtomHand {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn ext(&self) -> &Extension { self.sys().ext() }
|
pub fn ext(&self) -> &Extension { self.sys().ext() }
|
||||||
pub async fn req(&self, key: api::TStrv, req: Vec<u8>) -> Option<Vec<u8>> {
|
pub async fn req(&self, key: api::TStrv, req: Vec<u8>) -> Option<Vec<u8>> {
|
||||||
self.0.owner.client().request(api::FinalFwded(self.0.api_ref(), key, req)).await.unwrap()
|
self.0.owner.client().request(api::Fwded(self.0.api_ref(), key, req)).await.unwrap()
|
||||||
}
|
}
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn api_ref(&self) -> api::Atom { self.0.api_ref() }
|
pub fn api_ref(&self) -> api::Atom { self.0.api_ref() }
|
||||||
|
|||||||
@@ -1,46 +1,59 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt::Debug;
|
use std::fmt::{self, Debug};
|
||||||
use std::pin::pin;
|
use std::pin::pin;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use async_event::Event;
|
use async_event::Event;
|
||||||
use async_fn_stream::stream;
|
use async_fn_stream::stream;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
use futures::channel::mpsc;
|
use futures::channel::mpsc;
|
||||||
use futures::future::LocalBoxFuture;
|
use futures::stream::select;
|
||||||
use futures::stream::FuturesUnordered;
|
use futures::{FutureExt, SinkExt, StreamExt};
|
||||||
use futures::{SinkExt, StreamExt, select};
|
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_base::{OrcErrv, Receipt, ReqHandle, Sym};
|
use orchid_async_utils::{LocalSet, LocalSetController, local_set};
|
||||||
use orchid_extension::{self as ox, AtomicFeatures as _};
|
use orchid_base::{OrcErrv, Receipt, ReqHandle, Sym, fmt, is, log, mk_errv, sym};
|
||||||
|
use orchid_extension::{self as ox, AtomicFeatures as _, get_arg};
|
||||||
|
|
||||||
use crate::ctx::Ctx;
|
use crate::ctx::Ctx;
|
||||||
use crate::execute::{ExecCtx, ExecResult};
|
use crate::execute::{ExecCtx, ExecResult};
|
||||||
use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder};
|
use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder};
|
||||||
|
use crate::extension::Extension;
|
||||||
|
use crate::inline::ext_inline;
|
||||||
|
use crate::system::System;
|
||||||
use crate::tree::Root;
|
use crate::tree::Root;
|
||||||
|
|
||||||
|
/// Events internally recognized by this system sent through [CommandQueue]
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Task {
|
||||||
|
RunCommand(Expr),
|
||||||
|
Sleep(DateTime<Utc>, Expr),
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
struct CommandQueueState {
|
struct CommandQueueState {
|
||||||
new: VecDeque<Expr>,
|
new: VecDeque<Task>,
|
||||||
added: Rc<Event>,
|
added: Rc<Event>,
|
||||||
wants_exit: bool,
|
|
||||||
ctx: Ctx,
|
ctx: Ctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shared object serving as a communication point between the extension
|
||||||
|
/// [CmdSystemCtor] and the host toolkit [CmdRunner]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct CommandQueue(Rc<RefCell<CommandQueueState>>);
|
struct CommandQueue(Rc<RefCell<CommandQueueState>>);
|
||||||
impl CommandQueue {
|
impl CommandQueue {
|
||||||
fn new(ctx: Ctx, init: impl IntoIterator<Item = Expr>) -> Self {
|
fn new(ctx: Ctx, init: impl IntoIterator<Item = Expr>) -> Self {
|
||||||
Self(Rc::new(RefCell::new(CommandQueueState {
|
Self(Rc::new(RefCell::new(CommandQueueState {
|
||||||
new: init.into_iter().collect(),
|
new: init.into_iter().map(Task::RunCommand).collect(),
|
||||||
added: Rc::default(),
|
added: Rc::default(),
|
||||||
wants_exit: false,
|
|
||||||
ctx,
|
ctx,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
pub fn push(&self, expr: Expr) {
|
pub fn push(&self, task: Task) {
|
||||||
let was_empty = {
|
let was_empty = {
|
||||||
let mut g = self.0.borrow_mut();
|
let mut g = self.0.borrow_mut();
|
||||||
g.new.push_back(expr);
|
g.new.push_back(task);
|
||||||
g.new.len() == 1
|
g.new.len() == 1
|
||||||
};
|
};
|
||||||
if was_empty {
|
if was_empty {
|
||||||
@@ -48,7 +61,7 @@ impl CommandQueue {
|
|||||||
added.notify_one();
|
added.notify_one();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn get_new(&self) -> Expr {
|
pub async fn get_new(&self) -> Task {
|
||||||
let added = {
|
let added = {
|
||||||
let mut g = self.0.borrow_mut();
|
let mut g = self.0.borrow_mut();
|
||||||
if let Some(waiting) = g.new.pop_front() {
|
if let Some(waiting) = g.new.pop_front() {
|
||||||
@@ -60,15 +73,18 @@ impl CommandQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Debug for CommandQueue {
|
impl Debug for CommandQueue {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("CommandQueue").finish_non_exhaustive()
|
f.debug_struct("CommandQueue").finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum CmdResult {
|
/// Events the embedder may want to be notified about
|
||||||
/// All command sequences settled
|
pub enum CmdEvent {
|
||||||
|
/// All commands finished and there's nothing else to do
|
||||||
Settled,
|
Settled,
|
||||||
/// Exit was requested explicitly by usercode
|
/// Exit was requested explicitly by usercode. This request means that all
|
||||||
|
/// internal system state should be discarded, so if it is returned by the
|
||||||
|
/// [CmdRunner], no further commands are permitted
|
||||||
Exit,
|
Exit,
|
||||||
/// Ran out of gas
|
/// Ran out of gas
|
||||||
Gas,
|
Gas,
|
||||||
@@ -84,97 +100,120 @@ pub enum CmdResult {
|
|||||||
Err(OrcErrv),
|
Err(OrcErrv),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum TaskOutcome {
|
||||||
|
/// We need this so that we get an opportunity to exit when the last tasks
|
||||||
|
/// exited
|
||||||
|
None,
|
||||||
|
Next(Expr),
|
||||||
|
NonCommand(Expr),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct CmdRunner {
|
pub struct CmdRunner {
|
||||||
root: Root,
|
|
||||||
queue: CommandQueue,
|
queue: CommandQueue,
|
||||||
gas: Option<u64>,
|
gas: Option<u64>,
|
||||||
interrupted: Option<ExecCtx>,
|
interrupted: Option<ExecCtx>,
|
||||||
futures: FuturesUnordered<LocalBoxFuture<'static, Option<CmdResult>>>,
|
system: System,
|
||||||
|
recv: mpsc::Receiver<TaskOutcome>,
|
||||||
|
send: mpsc::Sender<TaskOutcome>,
|
||||||
|
ctl: LocalSetController<'static, Never>,
|
||||||
|
local_set: LocalSet<'static, Never>,
|
||||||
}
|
}
|
||||||
impl CmdRunner {
|
impl CmdRunner {
|
||||||
pub async fn new(root: Root, ctx: Ctx, init: impl IntoIterator<Item = Expr>) -> Self {
|
pub async fn new(root: &mut Root, ctx: Ctx, init: impl IntoIterator<Item = Expr>) -> Self {
|
||||||
Self {
|
let queue = CommandQueue::new(ctx.clone(), init);
|
||||||
futures: FuturesUnordered::new(),
|
let ext_builder = ox::ExtensionBuilder::new("orchid::cmd").system(CmdSystemCtor(queue.clone()));
|
||||||
gas: None,
|
let extension = (Extension::new(ext_inline(ext_builder, ctx.clone()).await, ctx).await)
|
||||||
root,
|
.expect("IO error on in-memory pipe");
|
||||||
interrupted: None,
|
let system_ctor = (extension.system_ctors().find(|ctor| ctor.decl.name == "orchid::cmd"))
|
||||||
queue: CommandQueue::new(ctx, init),
|
.expect("Missing command system ctor");
|
||||||
}
|
let (cmd_root, system) = system_ctor.run(vec![]).await;
|
||||||
|
*root = root.merge(&cmd_root).await.expect("Could not merge command system into tree");
|
||||||
|
let (send, recv) = mpsc::channel(0);
|
||||||
|
let (ctl, local_set) = local_set();
|
||||||
|
Self { send, recv, ctl, local_set, gas: None, interrupted: None, queue, system }
|
||||||
}
|
}
|
||||||
|
pub fn push(&self, expr: Expr) { self.queue.push(Task::RunCommand(expr)); }
|
||||||
|
#[must_use]
|
||||||
|
pub fn sys(&self) -> &System { &self.system }
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_gas(&self) -> u64 { self.gas.expect("queried gas but no gas was set") }
|
pub fn get_gas(&self) -> u64 { self.gas.expect("queried gas but no gas was set") }
|
||||||
pub fn set_gas(&mut self, gas: u64) { self.gas = Some(gas) }
|
pub fn set_gas(&mut self, gas: u64) { self.gas = Some(gas) }
|
||||||
pub fn disable_gas(&mut self) { self.gas = None }
|
pub fn disable_gas(&mut self) { self.gas = None }
|
||||||
pub async fn execute(&mut self) -> CmdResult {
|
pub async fn execute(&mut self, root: &Root) -> CmdEvent {
|
||||||
let waiting_on_queue = RefCell::new(false);
|
#[derive(Debug)]
|
||||||
let (mut spawn, mut on_spawn) = mpsc::channel(1);
|
enum Event {
|
||||||
let mut normalize_stream = pin!(
|
Finished(TaskOutcome),
|
||||||
stream(async |mut h| {
|
Request(Task),
|
||||||
loop {
|
}
|
||||||
if self.queue.0.borrow().wants_exit {
|
let queue_stream = pin!(stream(async |mut h| loop {
|
||||||
h.emit(CmdResult::Exit).await;
|
h.emit(self.queue.get_new().await).await
|
||||||
break;
|
}));
|
||||||
}
|
let mut event_stream = pin!(select(
|
||||||
waiting_on_queue.replace(false);
|
select(queue_stream.map(Event::Request), self.recv.by_ref().map(Event::Finished)),
|
||||||
let mut xctx = match self.interrupted.take() {
|
((&mut self.local_set).into_stream())
|
||||||
None => ExecCtx::new(self.root.clone(), self.queue.get_new().await).await,
|
.map(|_| panic!("self has the ctl so this should never exit cleanly"))
|
||||||
Some(xctx) => xctx,
|
));
|
||||||
};
|
while let Some(ev) = event_stream.next().await {
|
||||||
waiting_on_queue.replace(true);
|
match ev {
|
||||||
|
Event::Finished(TaskOutcome::None) if self.ctl.is_empty() => return CmdEvent::Settled,
|
||||||
|
Event::Finished(TaskOutcome::None) => continue,
|
||||||
|
Event::Finished(TaskOutcome::Next(expr)) => self.queue.push(Task::RunCommand(expr)),
|
||||||
|
Event::Finished(TaskOutcome::NonCommand(val)) => return CmdEvent::NonCommand(val),
|
||||||
|
Event::Request(Task::Exit) => return CmdEvent::Exit,
|
||||||
|
Event::Request(Task::Sleep(until, expr)) => {
|
||||||
|
let queue = self.queue.clone();
|
||||||
|
let ctx = queue.0.borrow_mut().ctx.clone();
|
||||||
|
self.ctl.spawn(async move {
|
||||||
|
let delta = until - Utc::now();
|
||||||
|
match delta.to_std() {
|
||||||
|
Err(_) =>
|
||||||
|
writeln!(log("debug"), "Negative sleep found ({delta}), requeuing as instant").await,
|
||||||
|
Ok(delay) => ctx.sleep(delay).await,
|
||||||
|
};
|
||||||
|
queue.push(Task::RunCommand(expr));
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Event::Request(Task::RunCommand(expr)) => {
|
||||||
|
let mut xctx = ExecCtx::new(root.clone(), expr).await;
|
||||||
xctx.set_gas(self.gas);
|
xctx.set_gas(self.gas);
|
||||||
let res = xctx.execute().await;
|
let norm = match xctx.execute().await {
|
||||||
match res {
|
ExecResult::Err(err, gas) => {
|
||||||
ExecResult::Err(e, gas) => {
|
|
||||||
self.gas = gas;
|
self.gas = gas;
|
||||||
h.emit(CmdResult::Err(e)).await;
|
return CmdEvent::Err(err);
|
||||||
},
|
|
||||||
ExecResult::Gas(exec) => {
|
|
||||||
self.interrupted = Some(exec);
|
|
||||||
h.emit(CmdResult::Gas).await;
|
|
||||||
},
|
},
|
||||||
ExecResult::Value(val, gas) => {
|
ExecResult::Value(val, gas) => {
|
||||||
self.gas = gas;
|
self.gas = gas;
|
||||||
let Some(atom) = val.as_atom().await else {
|
val
|
||||||
h.emit(CmdResult::NonCommand(val)).await;
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let queue = self.queue.clone();
|
|
||||||
let ctx = queue.0.borrow_mut().ctx.clone();
|
|
||||||
spawn
|
|
||||||
.send(Box::pin(async move {
|
|
||||||
match atom.ipc(ox::std_reqs::RunCommand).await {
|
|
||||||
None => Some(CmdResult::NonCommand(val)),
|
|
||||||
Some(None) => None,
|
|
||||||
Some(Some(expr)) => {
|
|
||||||
let from_api_cx = ExprFromApiCtx { ctx, sys: atom.api_ref().owner };
|
|
||||||
queue.push(Expr::from_api(expr, PathSetBuilder::new(), from_api_cx).await);
|
|
||||||
None
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
.await
|
|
||||||
.expect("Receiver is owned by the layer that polls this");
|
|
||||||
},
|
},
|
||||||
}
|
ExecResult::Gas(xctx) => {
|
||||||
}
|
self.interrupted = Some(xctx);
|
||||||
})
|
return CmdEvent::Gas;
|
||||||
.fuse()
|
},
|
||||||
);
|
};
|
||||||
loop {
|
let Some(atom) = norm.as_atom().await else {
|
||||||
if self.queue.0.borrow().wants_exit {
|
return CmdEvent::NonCommand(norm);
|
||||||
break CmdResult::Exit;
|
};
|
||||||
}
|
let queue = self.queue.clone();
|
||||||
let task = select!(
|
let ctx = queue.0.borrow_mut().ctx.clone();
|
||||||
r_opt = self.futures.by_ref().next() => match r_opt {
|
let mut send = self.send.clone();
|
||||||
Some(Some(r)) => break r,
|
self.ctl.spawn(async move {
|
||||||
None if *waiting_on_queue.borrow() => break CmdResult::Settled,
|
match atom.ipc(ox::std_reqs::StartCommand).await {
|
||||||
None | Some(None) => continue,
|
None => send.send(TaskOutcome::NonCommand(norm)).await.unwrap(),
|
||||||
|
Some(None) => send.send(TaskOutcome::None).await.unwrap(),
|
||||||
|
Some(Some(expr)) => {
|
||||||
|
let from_api_cx = ExprFromApiCtx { ctx, sys: atom.api_ref().owner };
|
||||||
|
let expr = Expr::from_api(expr, PathSetBuilder::new(), from_api_cx).await;
|
||||||
|
send.send(TaskOutcome::Next(expr)).await.unwrap();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
},
|
},
|
||||||
r = normalize_stream.by_ref().next() => break r.expect("infinite stream"),
|
}
|
||||||
task = on_spawn.by_ref().next() => task.expect("sender moved into infinite stream"),
|
|
||||||
);
|
|
||||||
self.futures.push(task)
|
|
||||||
}
|
}
|
||||||
|
unreachable!("Since the localset cannot exit, the function cannot exit either")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,9 +228,7 @@ impl ox::SystemCard for CmdSystemCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CmdSystemCtor {
|
pub struct CmdSystemCtor(CommandQueue);
|
||||||
queue: CommandQueue,
|
|
||||||
}
|
|
||||||
impl ox::SystemCtor for CmdSystemCtor {
|
impl ox::SystemCtor for CmdSystemCtor {
|
||||||
const NAME: &'static str = "orchid::cmd";
|
const NAME: &'static str = "orchid::cmd";
|
||||||
const VERSION: f64 = 0.1;
|
const VERSION: f64 = 0.1;
|
||||||
@@ -199,7 +236,7 @@ impl ox::SystemCtor for CmdSystemCtor {
|
|||||||
type Deps = ();
|
type Deps = ();
|
||||||
type Instance = CmdSystemInst;
|
type Instance = CmdSystemInst;
|
||||||
fn inst(&self, _: <Self::Deps as orchid_extension::DepDef>::Sat) -> Self::Instance {
|
fn inst(&self, _: <Self::Deps as orchid_extension::DepDef>::Sat) -> Self::Instance {
|
||||||
CmdSystemInst { queue: self.queue.clone() }
|
CmdSystemInst { queue: self.0.clone() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,24 +254,47 @@ impl ox::System for CmdSystemInst {
|
|||||||
ox::tree::fun(true, "spawn", async |side: ox::Expr, cont: ox::Expr| {
|
ox::tree::fun(true, "spawn", async |side: ox::Expr, cont: ox::Expr| {
|
||||||
ox::cmd(async move || {
|
ox::cmd(async move || {
|
||||||
let queue = ox_get_queue();
|
let queue = ox_get_queue();
|
||||||
let side_xtk = side.serialize().await;
|
let side_xtk = side.clone().serialize().await;
|
||||||
let mut g = queue.0.borrow_mut();
|
let mut g = queue.0.borrow_mut();
|
||||||
let host_ex =
|
let host_ex =
|
||||||
g.ctx.exprs.take_expr(side_xtk).expect("Host could not locate leaked expr by ID ");
|
g.ctx.exprs.take_expr(side_xtk).expect("Host could not locate leaked expr by ID ");
|
||||||
g.new.push_back(host_ex);
|
g.new.push_back(Task::RunCommand(host_ex));
|
||||||
Some(cont)
|
Some(cont.clone())
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
ox::tree::cnst(true, "yield", ox::cmd(async || None::<Never>)),
|
||||||
|
ox::tree::cnst(
|
||||||
|
true,
|
||||||
|
"exit",
|
||||||
|
ox::cmd(async || {
|
||||||
|
ox_get_queue().push(Task::Exit);
|
||||||
|
None::<Never>
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
ox::tree::fun(true, "sleep", async |until: ox::ForeignAtom, cont: ox::Expr| {
|
||||||
|
let Some(until) = until.call(ox::std_reqs::AsInstant).await else {
|
||||||
|
return ox::gen_expr::bot(mk_errv(
|
||||||
|
is("Not an instant").await,
|
||||||
|
format!("{} is not an instant", fmt(&until).await),
|
||||||
|
[get_arg(0).pos().await],
|
||||||
|
));
|
||||||
|
};
|
||||||
|
ox::cmd(async move || {
|
||||||
|
let queue = ox_get_queue();
|
||||||
|
let cont_xtk = cont.clone().serialize().await;
|
||||||
|
let mut g = queue.0.borrow_mut();
|
||||||
|
let host_ex =
|
||||||
|
g.ctx.exprs.take_expr(cont_xtk).expect("Host could not locate leaked expr by ID ");
|
||||||
|
g.new.push_back(Task::Sleep(until, host_ex));
|
||||||
|
None::<Never>
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
async fn prelude(&self) -> Vec<Sym> { vec![] }
|
async fn prelude(&self) -> Vec<Sym> { vec![sym!("orchid")] }
|
||||||
fn lexers(&self) -> Vec<ox::LexerObj> { vec![] }
|
fn lexers(&self) -> Vec<ox::LexerObj> { vec![] }
|
||||||
fn parsers(&self) -> Vec<ox::ParserObj> { vec![] }
|
fn parsers(&self) -> Vec<ox::ParserObj> { vec![] }
|
||||||
async fn request<'a>(
|
async fn request(&self, _hand: Box<dyn ReqHandle>, req: ox::ReqForSystem<Self>) -> Receipt {
|
||||||
&self,
|
|
||||||
_hand: Box<dyn ReqHandle<'a> + 'a>,
|
|
||||||
req: ox::ReqForSystem<Self>,
|
|
||||||
) -> Receipt<'a> {
|
|
||||||
match req {}
|
match req {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ pub trait JoinHandle {
|
|||||||
/// It is guaranteed that the future will never be polled after this is called
|
/// 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
|
/// take the future out of the task. If the return value
|
||||||
/// is dropped, the spawned future is also dropped
|
/// is dropped, the spawned future is also dropped. The returned boxed future
|
||||||
|
/// will finish any sleep requested at spawn.
|
||||||
fn join(self: Box<Self>) -> LocalBoxFuture<'static, ()>;
|
fn join(self: Box<Self>) -> LocalBoxFuture<'static, ()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,6 +31,9 @@ pub trait Spawner {
|
|||||||
/// exit while there are pending tasks to allow external communication
|
/// exit while there are pending tasks to allow external communication
|
||||||
/// channels to cleanly shut down.
|
/// channels to cleanly shut down.
|
||||||
fn spawn_obj(&self, delay: Duration, fut: LocalBoxFuture<'static, ()>) -> Box<dyn JoinHandle>;
|
fn spawn_obj(&self, delay: Duration, fut: LocalBoxFuture<'static, ()>) -> Box<dyn JoinHandle>;
|
||||||
|
fn sleep(&self, delay: Duration) -> LocalBoxFuture<'static, ()> {
|
||||||
|
self.spawn_obj(delay, Box::pin(async move {})).join()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CtxData {
|
pub struct CtxData {
|
||||||
@@ -79,6 +83,9 @@ impl Ctx {
|
|||||||
) -> Box<dyn JoinHandle> {
|
) -> Box<dyn JoinHandle> {
|
||||||
self.spawner.spawn_obj(delay, Box::pin(fut))
|
self.spawner.spawn_obj(delay, Box::pin(fut))
|
||||||
}
|
}
|
||||||
|
/// Return after an amount of time has passed
|
||||||
|
#[must_use]
|
||||||
|
pub async fn sleep(&self, delay: Duration) { self.spawner.sleep(delay).await }
|
||||||
#[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> {
|
||||||
self.systems.read().await.get(&id).and_then(WeakSystem::upgrade)
|
self.systems.read().await.get(&id).and_then(WeakSystem::upgrade)
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ async fn print_exprkind<'a>(
|
|||||||
None => format!("Bottom(\n\t{}\n)", indent(&e.to_string())).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})")))
|
||||||
.units([print_expr(f, c, visited, id_only).await, print_expr(x, c, visited, id_only).await]),
|
.units([print_expr(f, c, visited, id_only).await, print_expr(x, c, visited, id_only).await]),
|
||||||
ExprKind::Identity(id) =>
|
ExprKind::Identity(id) =>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ 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::{
|
use orchid_base::{
|
||||||
AtomRepr, Client, ClientExt, CommCtx, FmtCtxImpl, Format, IStr, IStrv, MsgReaderExt, Pos,
|
AtomRepr, Client, ClientExt, CommCx, FmtCtxImpl, Format, IStr, IStrv, IoComm, MsgReaderExt, Pos,
|
||||||
ReqHandleExt, ReqReaderExt, Sym, Witness, es, ev, io_comm, is, iv, log, stash, with_stash,
|
ReqHandleExt, ReqReaderExt, Sym, Witness, es, ev, io_comm, is, iv, log, stash, with_stash,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ use crate::ctx::{Ctx, JoinHandle};
|
|||||||
use crate::dealias::{ChildError, ChildErrorKind, walk};
|
use crate::dealias::{ChildError, ChildErrorKind, walk};
|
||||||
use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder};
|
use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder};
|
||||||
use crate::system::SystemCtor;
|
use crate::system::SystemCtor;
|
||||||
use crate::tree::MemberKind;
|
use crate::tree::MemKind;
|
||||||
|
|
||||||
pub struct ExtPort {
|
pub struct ExtPort {
|
||||||
pub input: Pin<Box<dyn AsyncWrite>>,
|
pub input: Pin<Box<dyn AsyncWrite>>,
|
||||||
@@ -45,7 +45,7 @@ pub struct ReqPair<R: Request>(R, Sender<R::Response>);
|
|||||||
pub struct ExtensionData {
|
pub struct ExtensionData {
|
||||||
name: String,
|
name: String,
|
||||||
ctx: Ctx,
|
ctx: Ctx,
|
||||||
comm_cx: Option<CommCtx>,
|
comm_cx: Option<CommCx>,
|
||||||
join_ext: Option<Box<dyn JoinHandle>>,
|
join_ext: Option<Box<dyn JoinHandle>>,
|
||||||
client: Rc<dyn Client>,
|
client: Rc<dyn Client>,
|
||||||
systems: Vec<SystemCtor>,
|
systems: Vec<SystemCtor>,
|
||||||
@@ -54,17 +54,19 @@ pub struct ExtensionData {
|
|||||||
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
|
/// Moved over from [ExtPort] to allow hooking to the extension's drop
|
||||||
_drop_trigger: Box<dyn Any>,
|
drop_trigger: Box<dyn Any>,
|
||||||
}
|
}
|
||||||
impl Drop for ExtensionData {
|
impl Drop for ExtensionData {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let client = self.client.clone();
|
let client = self.client.clone();
|
||||||
let join_ext = self.join_ext.take().expect("Only called once in Drop");
|
let join_ext = self.join_ext.take().expect("Only called once in Drop");
|
||||||
let comm_cx = self.comm_cx.take().expect("Only used here");
|
let comm_cx = self.comm_cx.take().expect("Only used here");
|
||||||
|
let drop_trigger = std::mem::replace(&mut self.drop_trigger, Box::new(()));
|
||||||
stash(async move {
|
stash(async move {
|
||||||
client.notify(api::HostExtNotif::Exit).await.unwrap();
|
client.notify(api::HostExtNotif::Exit).await.unwrap();
|
||||||
comm_cx.exit().await.unwrap();
|
comm_cx.exit().await.unwrap();
|
||||||
join_ext.join().await;
|
join_ext.join().await;
|
||||||
|
std::mem::drop(drop_trigger);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,12 +82,12 @@ impl Extension {
|
|||||||
let header2 = header.clone();
|
let header2 = header.clone();
|
||||||
Ok(Self(Rc::new_cyclic(|weak: &Weak<ExtensionData>| {
|
Ok(Self(Rc::new_cyclic(|weak: &Weak<ExtensionData>| {
|
||||||
// context not needed because exit is extension-initiated
|
// context not needed because exit is extension-initiated
|
||||||
let (client, comm_cx, comm) = io_comm(init.input, init.output);
|
let IoComm { client, cx: comm_cx, srv } = io_comm(init.input, init.output);
|
||||||
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(Duration::ZERO, async move {
|
let join_ext = ctx.clone().spawn(Duration::ZERO, async move {
|
||||||
comm
|
srv
|
||||||
.listen(
|
.listen(
|
||||||
async |reader| {
|
async |reader| {
|
||||||
with_stash(async {
|
with_stash(async {
|
||||||
@@ -131,46 +133,46 @@ impl Extension {
|
|||||||
}
|
}
|
||||||
let this = Self(weak.upgrade().unwrap());
|
let this = Self(weak.upgrade().unwrap());
|
||||||
match req {
|
match req {
|
||||||
api::ExtHostReq::Ping(ping) => handle.reply(&ping, &()).await,
|
api::ExtHostReq::Ping(ping) => handle.reply(&ping, ()).await,
|
||||||
api::ExtHostReq::IntReq(intreq) => match intreq {
|
api::ExtHostReq::IntReq(intreq) => match intreq {
|
||||||
api::IntReq::InternStr(s) => {
|
api::IntReq::InternStr(s) => {
|
||||||
let i = is(&s.0).await;
|
let i = is(&s.0).await;
|
||||||
this.0.strings.borrow_mut().insert(i.clone());
|
this.0.strings.borrow_mut().insert(i.clone());
|
||||||
handle.reply(&s, &i.to_api()).await
|
handle.reply(&s, i.to_api()).await
|
||||||
},
|
},
|
||||||
api::IntReq::InternStrv(v) => {
|
api::IntReq::InternStrv(v) => {
|
||||||
let tokens = join_all(v.0.iter().map(|m| es(*m))).await;
|
let tokens = join_all(v.0.iter().map(|m| es(*m))).await;
|
||||||
this.0.strings.borrow_mut().extend(tokens.iter().cloned());
|
this.0.strings.borrow_mut().extend(tokens.iter().cloned());
|
||||||
let i = iv(&tokens).await;
|
let i = iv(&tokens).await;
|
||||||
this.0.string_vecs.borrow_mut().insert(i.clone());
|
this.0.string_vecs.borrow_mut().insert(i.clone());
|
||||||
handle.reply(&v, &i.to_api()).await
|
handle.reply(&v, i.to_api()).await
|
||||||
},
|
},
|
||||||
api::IntReq::ExternStr(si) => {
|
api::IntReq::ExternStr(si) => {
|
||||||
let i = es(si.0).await;
|
let i = es(si.0).await;
|
||||||
this.0.strings.borrow_mut().insert(i.clone());
|
this.0.strings.borrow_mut().insert(i.clone());
|
||||||
handle.reply(&si, &i.to_string()).await
|
handle.reply(&si, i.to_string()).await
|
||||||
},
|
},
|
||||||
api::IntReq::ExternStrv(vi) => {
|
api::IntReq::ExternStrv(vi) => {
|
||||||
let i = ev(vi.0).await;
|
let i = ev(vi.0).await;
|
||||||
this.0.strings.borrow_mut().extend(i.iter().cloned());
|
this.0.strings.borrow_mut().extend(i.iter().cloned());
|
||||||
this.0.string_vecs.borrow_mut().insert(i.clone());
|
this.0.string_vecs.borrow_mut().insert(i.clone());
|
||||||
let markerv = i.iter().map(|t| t.to_api()).collect_vec();
|
let markerv = i.iter().map(|t| t.to_api()).collect_vec();
|
||||||
handle.reply(&vi, &markerv).await
|
handle.reply(&vi, markerv).await
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
api::ExtHostReq::Fwd(ref fw @ api::Fwd(ref atom, ref key, ref body)) => {
|
api::ExtHostReq::Fwd(ref fw @ api::Fwd { ref target, ref method, ref body }) => {
|
||||||
let sys =
|
let sys =
|
||||||
ctx.system_inst(atom.owner).await.expect("owner of live atom dropped");
|
ctx.system_inst(target.owner).await.expect("owner of live atom dropped");
|
||||||
let client = sys.client();
|
let client = sys.client();
|
||||||
let reply = client
|
let reply = client
|
||||||
.request(api::FinalFwded(fw.0.clone(), *key, body.clone()))
|
.request(api::Fwded(target.clone(), *method, body.clone()))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
handle.reply(fw, &reply).await
|
handle.reply(fw, reply).await
|
||||||
},
|
},
|
||||||
api::ExtHostReq::SysFwd(ref fw @ api::SysFwd(id, ref body)) => {
|
api::ExtHostReq::SysFwd(ref fw @ api::SysFwd(id, ref body)) => {
|
||||||
let sys = ctx.system_inst(id).await.unwrap();
|
let sys = ctx.system_inst(id).await.unwrap();
|
||||||
handle.reply(fw, &sys.request(body.clone()).await).await
|
handle.reply(fw, sys.request(body.clone()).await).await
|
||||||
},
|
},
|
||||||
api::ExtHostReq::SubLex(sl) => {
|
api::ExtHostReq::SubLex(sl) => {
|
||||||
let (rep_in, mut rep_out) = channel(0);
|
let (rep_in, mut rep_out) = channel(0);
|
||||||
@@ -180,19 +182,27 @@ impl Extension {
|
|||||||
lex_g.get(&sl.id).cloned().expect("Sublex for nonexistent lexid");
|
lex_g.get(&sl.id).cloned().expect("Sublex for nonexistent lexid");
|
||||||
req_in.send(ReqPair(sl.clone(), rep_in)).await.unwrap();
|
req_in.send(ReqPair(sl.clone(), rep_in)).await.unwrap();
|
||||||
}
|
}
|
||||||
handle.reply(&sl, &rep_out.next().await.unwrap()).await
|
handle.reply(&sl, rep_out.next().await.unwrap()).await
|
||||||
},
|
},
|
||||||
api::ExtHostReq::ExprReq(expr_req) => match expr_req {
|
api::ExtHostReq::ExprReq(expr_req) => match expr_req {
|
||||||
api::ExprReq::Inspect(ins @ api::Inspect { target }) => {
|
api::ExprReq::Inspect(ins @ api::Inspect { target }) => {
|
||||||
let expr = ctx.exprs.get_expr(target).expect("Invalid ticket");
|
let expr = ctx.exprs.get_expr(target).expect("Invalid ticket");
|
||||||
handle
|
handle
|
||||||
.reply(&ins, &api::Inspected {
|
.reply(&ins, api::Inspected {
|
||||||
refcount: expr.strong_count() as u32,
|
refcount: expr.strong_count() as u32,
|
||||||
location: expr.pos().to_api(),
|
location: expr.pos().to_api(),
|
||||||
kind: expr.to_api().await,
|
kind: expr.to_api().await,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
},
|
},
|
||||||
|
api::ExprReq::ExprPrint(prt @ api::ExprPrint { target }) => {
|
||||||
|
let msg = match ctx.exprs.get_expr(target) {
|
||||||
|
None => "EXPIRED_TICKET".into(),
|
||||||
|
Some(expr) => expr.print(&FmtCtxImpl::default()).await,
|
||||||
|
}
|
||||||
|
.to_api();
|
||||||
|
handle.reply(&prt, msg).await
|
||||||
|
},
|
||||||
api::ExprReq::Create(cre) => {
|
api::ExprReq::Create(cre) => {
|
||||||
let req = Witness::of(&cre);
|
let req = Witness::of(&cre);
|
||||||
let api::Create(sys, expr) = cre;
|
let api::Create(sys, expr) = cre;
|
||||||
@@ -203,7 +213,7 @@ impl Extension {
|
|||||||
.await;
|
.await;
|
||||||
let expr_id = expr.id();
|
let expr_id = expr.id();
|
||||||
ctx.exprs.give_expr(expr);
|
ctx.exprs.give_expr(expr);
|
||||||
handle.reply(req, &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)) => {
|
||||||
@@ -229,14 +239,14 @@ impl Extension {
|
|||||||
let mut members = std::collections::HashMap::new();
|
let mut members = std::collections::HashMap::new();
|
||||||
for (k, v) in &module.members {
|
for (k, v) in &module.members {
|
||||||
let kind = match v.kind(ctx.clone(), &root_data.consts).await {
|
let kind = match v.kind(ctx.clone(), &root_data.consts).await {
|
||||||
MemberKind::Const => api::MemberInfoKind::Constant,
|
MemKind::Const => api::MemberInfoKind::Constant,
|
||||||
MemberKind::Module(_) => api::MemberInfoKind::Module,
|
MemKind::Module(_) => api::MemberInfoKind::Module,
|
||||||
};
|
};
|
||||||
members.insert(k.to_api(), api::MemberInfo { public: v.public, kind });
|
members.insert(k.to_api(), api::MemberInfo { public: v.public, kind });
|
||||||
}
|
}
|
||||||
Ok(api::ModuleInfo { members })
|
Ok(api::ModuleInfo { members })
|
||||||
};
|
};
|
||||||
handle.reply(ls, &reply).await
|
handle.reply(ls, reply).await
|
||||||
},
|
},
|
||||||
api::ExtHostReq::ResolveNames(ref rn) => {
|
api::ExtHostReq::ResolveNames(ref rn) => {
|
||||||
let api::ResolveNames { constid, names, sys } = rn;
|
let api::ResolveNames { constid, names, sys } = rn;
|
||||||
@@ -265,12 +275,12 @@ impl Extension {
|
|||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
.await;
|
.await;
|
||||||
handle.reply(rn, &responses).await
|
handle.reply(rn, responses).await
|
||||||
},
|
},
|
||||||
api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) => {
|
api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) => {
|
||||||
let atom = AtomHand::from_api(atom, Pos::None, &mut ctx.clone()).await;
|
let atom = AtomHand::from_api(atom, Pos::None, &mut ctx.clone()).await;
|
||||||
let unit = atom.print(&FmtCtxImpl::default()).await;
|
let unit = atom.print(&FmtCtxImpl::default()).await;
|
||||||
handle.reply(eap, &unit.to_api()).await
|
handle.reply(eap, unit.to_api()).await
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -293,7 +303,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,
|
drop_trigger: init.drop_trigger,
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,8 +262,9 @@ 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();
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use orchid_api as api;
|
use orchid_api as api;
|
||||||
|
|
||||||
pub mod atom;
|
pub mod atom;
|
||||||
pub mod ctx;
|
|
||||||
#[cfg(feature = "orchid-extension")]
|
#[cfg(feature = "orchid-extension")]
|
||||||
pub mod cmd_system;
|
pub mod cmd_system;
|
||||||
|
pub mod ctx;
|
||||||
pub mod dealias;
|
pub mod dealias;
|
||||||
#[cfg(feature = "tokio")]
|
#[cfg(feature = "tokio")]
|
||||||
pub mod dylib;
|
pub mod dylib;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use substack::Substack;
|
|||||||
|
|
||||||
use crate::ctx::Ctx;
|
use crate::ctx::Ctx;
|
||||||
use crate::expr::Expr;
|
use crate::expr::Expr;
|
||||||
use crate::parsed::{Item, ItemKind, ParsedMember, ParsedMemberKind, ParsedModule};
|
use crate::parsed::{Item, ItemKind, ParsedMemKind, ParsedMember, ParsedModule};
|
||||||
use crate::system::System;
|
use crate::system::System;
|
||||||
|
|
||||||
type ParsSnippet<'a> = Snippet<'a, Expr, Expr>;
|
type ParsSnippet<'a> = Snippet<'a, Expr, Expr>;
|
||||||
@@ -101,7 +101,7 @@ pub async fn parse_exportable_item<'a>(
|
|||||||
) -> OrcRes<Vec<Item>> {
|
) -> OrcRes<Vec<Item>> {
|
||||||
let kind = if discr == is("mod").await {
|
let kind = if discr == is("mod").await {
|
||||||
let (name, body) = parse_module(ctx, path, tail).await?;
|
let (name, body) = parse_module(ctx, path, tail).await?;
|
||||||
ItemKind::Member(ParsedMember { name, exported, kind: ParsedMemberKind::Mod(body) })
|
ItemKind::Member(ParsedMember { name, exported, kind: ParsedMemKind::Mod(body) })
|
||||||
} else if let Some(parser) = ctx.systems().find_map(|s| s.get_parser(discr.clone())) {
|
} else if let Some(parser) = ctx.systems().find_map(|s| s.get_parser(discr.clone())) {
|
||||||
return parser
|
return parser
|
||||||
.parse(ctx, path, tail.to_vec(), exported, comments, &mut async |stack, lines| {
|
.parse(ctx, path, tail.to_vec(), exported, comments, &mut async |stack, lines| {
|
||||||
|
|||||||
@@ -53,10 +53,10 @@ impl Format for Item {
|
|||||||
let item_text = match &self.kind {
|
let item_text = match &self.kind {
|
||||||
ItemKind::Import(i) => format!("import {i}").into(),
|
ItemKind::Import(i) => format!("import {i}").into(),
|
||||||
ItemKind::Member(mem) => match &mem.kind {
|
ItemKind::Member(mem) => match &mem.kind {
|
||||||
ParsedMemberKind::Const(_, sys) =>
|
ParsedMemKind::Const(_, sys) =>
|
||||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("const {0} via {1}")))
|
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("const {0} via {1}")))
|
||||||
.units([mem.name.to_string().into(), sys.print(c).await]),
|
.units([mem.name.to_string().into(), sys.print(c).await]),
|
||||||
ParsedMemberKind::Mod(module) =>
|
ParsedMemKind::Mod(module) =>
|
||||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("module {0} {{\n\t{1}\n}}")))
|
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("module {0} {{\n\t{1}\n}}")))
|
||||||
.units([mem.name.to_string().into(), module.print(c).boxed_local().await]),
|
.units([mem.name.to_string().into(), module.print(c).boxed_local().await]),
|
||||||
},
|
},
|
||||||
@@ -69,12 +69,12 @@ impl Format for Item {
|
|||||||
pub struct ParsedMember {
|
pub struct ParsedMember {
|
||||||
pub name: IStr,
|
pub name: IStr,
|
||||||
pub exported: bool,
|
pub exported: bool,
|
||||||
pub kind: ParsedMemberKind,
|
pub kind: ParsedMemKind,
|
||||||
}
|
}
|
||||||
impl ParsedMember {
|
impl ParsedMember {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn name(&self) -> IStr { self.name.clone() }
|
pub fn name(&self) -> IStr { self.name.clone() }
|
||||||
pub fn new(exported: bool, name: IStr, kind: impl Into<ParsedMemberKind>) -> Self {
|
pub fn new(exported: bool, name: IStr, kind: impl Into<ParsedMemKind>) -> Self {
|
||||||
Self { exported, name, kind: kind.into() }
|
Self { exported, name, kind: kind.into() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,11 +101,11 @@ impl fmt::Debug for ParsedExpr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ParsedMemberKind {
|
pub enum ParsedMemKind {
|
||||||
Const(api::ParsedConstId, System),
|
Const(api::ParsedConstId, System),
|
||||||
Mod(ParsedModule),
|
Mod(ParsedModule),
|
||||||
}
|
}
|
||||||
impl From<ParsedModule> for ParsedMemberKind {
|
impl From<ParsedModule> for ParsedMemKind {
|
||||||
fn from(value: ParsedModule) -> Self { Self::Mod(value) }
|
fn from(value: ParsedModule) -> Self { Self::Mod(value) }
|
||||||
}
|
}
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@@ -137,7 +137,7 @@ impl ParsedModule {
|
|||||||
.filter_map(|it| if let ItemKind::Import(i) = &it.kind { Some(i) } else { None })
|
.filter_map(|it| if let ItemKind::Import(i) = &it.kind { Some(i) } else { None })
|
||||||
}
|
}
|
||||||
pub fn default_item(self, name: IStr, sr: SrcRange) -> Item {
|
pub fn default_item(self, name: IStr, sr: SrcRange) -> Item {
|
||||||
let mem = ParsedMember { exported: true, name, kind: ParsedMemberKind::Mod(self) };
|
let mem = ParsedMember { exported: true, name, kind: ParsedMemKind::Mod(self) };
|
||||||
Item { comments: vec![], sr, kind: ItemKind::Member(mem) }
|
Item { comments: vec![], sr, kind: ItemKind::Member(mem) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -157,8 +157,8 @@ impl Tree for ParsedModule {
|
|||||||
.find(|m| m.name == key)
|
.find(|m| m.name == key)
|
||||||
{
|
{
|
||||||
match &member.kind {
|
match &member.kind {
|
||||||
ParsedMemberKind::Const(..) => return ChildResult::Err(ChildErrorKind::Constant),
|
ParsedMemKind::Const(..) => return ChildResult::Err(ChildErrorKind::Constant),
|
||||||
ParsedMemberKind::Mod(m) => return ChildResult::Ok(m),
|
ParsedMemKind::Mod(m) => return ChildResult::Ok(m),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ChildResult::Err(ChildErrorKind::Missing)
|
ChildResult::Err(ChildErrorKind::Missing)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ 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::{
|
||||||
Item, ItemKind, ParsTokTree, ParsedMember, ParsedMemberKind, ParsedModule, tt_to_api,
|
Item, ItemKind, ParsTokTree, ParsedMemKind, ParsedMember, ParsedModule, tt_to_api,
|
||||||
};
|
};
|
||||||
use crate::system::System;
|
use crate::system::System;
|
||||||
|
|
||||||
@@ -88,11 +88,11 @@ async fn conv(
|
|||||||
let mkind = match kind {
|
let mkind = match kind {
|
||||||
api::ParsedMemberKind::Module { lines, use_prelude } => {
|
api::ParsedMemberKind::Module { lines, use_prelude } => {
|
||||||
let items = conv(lines, mem_path, callback, ctx).boxed_local().await?;
|
let items = conv(lines, mem_path, callback, ctx).boxed_local().await?;
|
||||||
ParsedMemberKind::Mod(ParsedModule::new(use_prelude, items))
|
ParsedMemKind::Mod(ParsedModule::new(use_prelude, items))
|
||||||
},
|
},
|
||||||
api::ParsedMemberKind::Constant(cid) => {
|
api::ParsedMemberKind::Constant(cid) => {
|
||||||
ctx.sys.0.const_paths.insert(cid, ctx.mod_path.suffix(mem_path.unreverse()).await);
|
ctx.sys.0.const_paths.insert(cid, ctx.mod_path.suffix(mem_path.unreverse()).await);
|
||||||
ParsedMemberKind::Const(cid, ctx.sys.clone())
|
ParsedMemKind::Const(cid, ctx.sys.clone())
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
items.push(Item {
|
items.push(Item {
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ 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, ExprFromApiCtx, PathSetBuilder};
|
use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder};
|
||||||
use crate::parsed::{ItemKind, ParsedMemberKind, ParsedModule};
|
use crate::parsed::{ItemKind, ParsedMemKind, ParsedModule};
|
||||||
use crate::system::System;
|
use crate::system::System;
|
||||||
|
|
||||||
pub struct RootData {
|
pub struct RootData {
|
||||||
pub root: Module,
|
pub root: Mod,
|
||||||
pub consts: MemoMap<Sym, Expr>,
|
pub consts: MemoMap<Sym, Expr>,
|
||||||
pub ctx: Ctx,
|
pub ctx: Ctx,
|
||||||
}
|
}
|
||||||
@@ -34,17 +34,13 @@ pub struct Root(pub Rc<RwLock<RootData>>);
|
|||||||
impl Root {
|
impl Root {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(ctx: Ctx) -> Self {
|
pub fn new(ctx: Ctx) -> Self {
|
||||||
Root(Rc::new(RwLock::new(RootData {
|
Root(Rc::new(RwLock::new(RootData { root: Mod::default(), consts: MemoMap::default(), ctx })))
|
||||||
root: Module::default(),
|
|
||||||
consts: MemoMap::default(),
|
|
||||||
ctx,
|
|
||||||
})))
|
|
||||||
}
|
}
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub async fn from_api(api: api::Module, sys: &System) -> Self {
|
pub async fn from_api(api: api::Module, sys: &System) -> Self {
|
||||||
let consts = MemoMap::new();
|
let consts = MemoMap::new();
|
||||||
let mut tfac = TreeFromApiCtx { consts: &consts, path: iv(&[][..]).await, sys };
|
let mut tfac = TreeFromApiCtx { consts: &consts, path: iv(&[][..]).await, sys };
|
||||||
let root = Module::from_api(api, &mut tfac).await;
|
let root = Mod::from_api(api, &mut tfac).await;
|
||||||
Root(Rc::new(RwLock::new(RootData { root, consts, ctx: sys.ctx().clone() })))
|
Root(Rc::new(RwLock::new(RootData { root, consts, ctx: sys.ctx().clone() })))
|
||||||
}
|
}
|
||||||
pub async fn merge(&self, new: &Root) -> Result<Self, MergeErr> {
|
pub async fn merge(&self, new: &Root) -> Result<Self, MergeErr> {
|
||||||
@@ -71,14 +67,14 @@ impl Root {
|
|||||||
root: &this.root,
|
root: &this.root,
|
||||||
ctx: &this.ctx,
|
ctx: &this.ctx,
|
||||||
};
|
};
|
||||||
let mut module = Module::from_parsed(parsed, pars_prefix.clone(), &mut tfpctx).await;
|
let mut module = Mod::from_parsed(parsed, pars_prefix.clone(), &mut tfpctx).await;
|
||||||
for step in pars_prefix.iter().rev() {
|
for step in pars_prefix.iter().rev() {
|
||||||
let kind = OnceCell::from(MemberKind::Module(module));
|
let kind = OnceCell::from(MemKind::Module(module));
|
||||||
let members = HashMap::from([(
|
let members = HashMap::from([(
|
||||||
step.clone(),
|
step.clone(),
|
||||||
Rc::new(Member { public: true, lazy: RefCell::new(None), kind }),
|
Rc::new(Member { public: true, lazy: RefCell::new(None), kind }),
|
||||||
)]);
|
)]);
|
||||||
module = Module { imports: HashMap::new(), members }
|
module = Mod { imports: HashMap::new(), members }
|
||||||
}
|
}
|
||||||
let root = (this.root.merge(&module, this.ctx.clone(), &consts).await)
|
let root = (this.root.merge(&module, this.ctx.clone(), &consts).await)
|
||||||
.expect("Merge conflict between parsed and existing module");
|
.expect("Merge conflict between parsed and existing module");
|
||||||
@@ -159,11 +155,11 @@ pub struct ResolvedImport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct Module {
|
pub struct Mod {
|
||||||
pub imports: HashMap<IStr, Result<ResolvedImport, Vec<ResolvedImport>>>,
|
pub imports: HashMap<IStr, Result<ResolvedImport, Vec<ResolvedImport>>>,
|
||||||
pub members: HashMap<IStr, Rc<Member>>,
|
pub members: HashMap<IStr, Rc<Member>>,
|
||||||
}
|
}
|
||||||
impl Module {
|
impl Mod {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub async fn from_api(api: api::Module, ctx: &mut TreeFromApiCtx<'_>) -> Self {
|
pub async fn from_api(api: api::Module, ctx: &mut TreeFromApiCtx<'_>) -> Self {
|
||||||
let mut members = HashMap::new();
|
let mut members = HashMap::new();
|
||||||
@@ -178,11 +174,11 @@ impl Module {
|
|||||||
let cx = ExprFromApiCtx { ctx: ctx.sys.ctx().clone(), sys: ctx.sys.id() };
|
let cx = ExprFromApiCtx { ctx: ctx.sys.ctx().clone(), sys: ctx.sys.id() };
|
||||||
let expr = Expr::from_api(val, PathSetBuilder::new(), cx).await;
|
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(MemKind::Const))
|
||||||
},
|
},
|
||||||
api::MemberKind::Module(m) => {
|
api::MemberKind::Module(m) => {
|
||||||
let m = Self::from_api(m, &mut ctx.push(mem_name.clone()).await).boxed_local().await;
|
let m = Self::from_api(m, &mut ctx.push(mem_name.clone()).await).boxed_local().await;
|
||||||
(None, Some(MemberKind::Module(m)))
|
(None, Some(MemKind::Module(m)))
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
members.insert(
|
members.insert(
|
||||||
@@ -305,7 +301,7 @@ impl Module {
|
|||||||
match &item.kind {
|
match &item.kind {
|
||||||
ItemKind::Member(mem) => {
|
ItemKind::Member(mem) => {
|
||||||
let path = path.to_vname().suffix([mem.name.clone()]).to_sym().await;
|
let path = path.to_vname().suffix([mem.name.clone()]).to_sym().await;
|
||||||
let kind = OnceCell::from(MemberKind::from_parsed(&mem.kind, path.clone(), ctx).await);
|
let kind = OnceCell::from(MemKind::from_parsed(&mem.kind, path.clone(), ctx).await);
|
||||||
members.insert(
|
members.insert(
|
||||||
mem.name.clone(),
|
mem.name.clone(),
|
||||||
Rc::new(Member { kind, lazy: RefCell::default(), public: mem.exported }),
|
Rc::new(Member { kind, lazy: RefCell::default(), public: mem.exported }),
|
||||||
@@ -314,14 +310,14 @@ impl Module {
|
|||||||
ItemKind::Import(_) => (),
|
ItemKind::Import(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Module { imports, members }
|
Mod { imports, members }
|
||||||
}
|
}
|
||||||
pub async fn merge(
|
pub async fn merge(
|
||||||
&self,
|
&self,
|
||||||
other: &Module,
|
other: &Mod,
|
||||||
ctx: Ctx,
|
ctx: Ctx,
|
||||||
consts: &MemoMap<Sym, Expr>,
|
consts: &MemoMap<Sym, Expr>,
|
||||||
) -> Result<Module, MergeErr> {
|
) -> Result<Mod, MergeErr> {
|
||||||
if !self.imports.is_empty() || !other.imports.is_empty() {
|
if !self.imports.is_empty() || !other.imports.is_empty() {
|
||||||
return Err(MergeErr { path: VPath::new([]), kind: MergeErrKind::Imports });
|
return Err(MergeErr { path: VPath::new([]), kind: MergeErrKind::Imports });
|
||||||
}
|
}
|
||||||
@@ -335,7 +331,7 @@ impl Module {
|
|||||||
return Err(MergeErr { path: VPath::new([]), kind: MergeErrKind::Visibility });
|
return Err(MergeErr { path: VPath::new([]), kind: MergeErrKind::Visibility });
|
||||||
}
|
}
|
||||||
match (own.kind(ctx.clone(), consts).await, mem.kind(ctx.clone(), consts).await) {
|
match (own.kind(ctx.clone(), consts).await, mem.kind(ctx.clone(), consts).await) {
|
||||||
(MemberKind::Module(own_sub), MemberKind::Module(sub)) => {
|
(MemKind::Module(own_sub), MemKind::Module(sub)) => {
|
||||||
match own_sub.merge(sub, ctx.clone(), consts).boxed_local().await {
|
match own_sub.merge(sub, ctx.clone(), consts).boxed_local().await {
|
||||||
Ok(module) => {
|
Ok(module) => {
|
||||||
members.insert(
|
members.insert(
|
||||||
@@ -343,7 +339,7 @@ impl Module {
|
|||||||
Rc::new(Member {
|
Rc::new(Member {
|
||||||
lazy: RefCell::new(None),
|
lazy: RefCell::new(None),
|
||||||
public: own.public,
|
public: own.public,
|
||||||
kind: OnceCell::from(MemberKind::Module(module)),
|
kind: OnceCell::from(MemKind::Module(module)),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -361,7 +357,7 @@ impl Module {
|
|||||||
slot.insert(mem.clone());
|
slot.insert(mem.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Module { imports: HashMap::new(), members })
|
Ok(Mod { imports: HashMap::new(), members })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,13 +376,13 @@ pub enum MergeErrKind {
|
|||||||
pub struct FromParsedCtx<'a> {
|
pub struct FromParsedCtx<'a> {
|
||||||
pars_prefix: Sym,
|
pars_prefix: Sym,
|
||||||
pars_root: &'a ParsedModule,
|
pars_root: &'a ParsedModule,
|
||||||
root: &'a Module,
|
root: &'a Mod,
|
||||||
ctx: &'a Ctx,
|
ctx: &'a Ctx,
|
||||||
consts: &'a MemoMap<Sym, Expr>,
|
consts: &'a MemoMap<Sym, Expr>,
|
||||||
deferred_consts: &'a mut Vec<(Sym, api::SysId, api::ParsedConstId)>,
|
deferred_consts: &'a mut Vec<(Sym, api::SysId, api::ParsedConstId)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tree for Module {
|
impl Tree for Mod {
|
||||||
type Ctx<'a> = (Ctx, &'a MemoMap<Sym, Expr>);
|
type Ctx<'a> = (Ctx, &'a MemoMap<Sym, Expr>);
|
||||||
async fn child(
|
async fn child(
|
||||||
&self,
|
&self,
|
||||||
@@ -401,8 +397,8 @@ impl Tree for Module {
|
|||||||
return Err(ChildErrorKind::Private);
|
return Err(ChildErrorKind::Private);
|
||||||
}
|
}
|
||||||
match &member.kind(ctx.clone(), consts).await {
|
match &member.kind(ctx.clone(), consts).await {
|
||||||
MemberKind::Module(m) => Ok(m),
|
MemKind::Module(m) => Ok(m),
|
||||||
MemberKind::Const => Err(ChildErrorKind::Constant),
|
MemKind::Const => Err(ChildErrorKind::Constant),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn children(&self, public_only: bool) -> hashbrown::HashSet<IStr> {
|
fn children(&self, public_only: bool) -> hashbrown::HashSet<IStr> {
|
||||||
@@ -413,11 +409,11 @@ impl Tree for Module {
|
|||||||
pub struct Member {
|
pub struct Member {
|
||||||
pub public: bool,
|
pub public: bool,
|
||||||
pub lazy: RefCell<Option<LazyMemberHandle>>,
|
pub lazy: RefCell<Option<LazyMemberHandle>>,
|
||||||
pub kind: OnceCell<MemberKind>,
|
pub kind: OnceCell<MemKind>,
|
||||||
}
|
}
|
||||||
impl Member {
|
impl Member {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub async fn kind<'a>(&'a self, ctx: Ctx, consts: &MemoMap<Sym, Expr>) -> &'a MemberKind {
|
pub async fn kind<'a>(&'a self, ctx: Ctx, consts: &MemoMap<Sym, Expr>) -> &'a MemKind {
|
||||||
(self.kind.get_or_init(async {
|
(self.kind.get_or_init(async {
|
||||||
let handle = self.lazy.borrow_mut().take().expect("If kind is uninit, lazy must be Some");
|
let handle = self.lazy.borrow_mut().take().expect("If kind is uninit, lazy must be Some");
|
||||||
handle.run(ctx, consts).await
|
handle.run(ctx, consts).await
|
||||||
@@ -426,20 +422,20 @@ impl Member {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum MemberKind {
|
pub enum MemKind {
|
||||||
Const,
|
Const,
|
||||||
Module(Module),
|
Module(Mod),
|
||||||
}
|
}
|
||||||
impl MemberKind {
|
impl MemKind {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
async fn from_parsed(parsed: &ParsedMemberKind, path: Sym, ctx: &mut FromParsedCtx<'_>) -> Self {
|
async fn from_parsed(parsed: &ParsedMemKind, path: Sym, ctx: &mut FromParsedCtx<'_>) -> Self {
|
||||||
match parsed {
|
match parsed {
|
||||||
ParsedMemberKind::Const(id, sys) => {
|
ParsedMemKind::Const(id, sys) => {
|
||||||
ctx.deferred_consts.push((path, sys.id(), *id));
|
ctx.deferred_consts.push((path, sys.id(), *id));
|
||||||
MemberKind::Const
|
MemKind::Const
|
||||||
},
|
},
|
||||||
ParsedMemberKind::Mod(m) =>
|
ParsedMemKind::Mod(m) =>
|
||||||
MemberKind::Module(Module::from_parsed(m, path, ctx).boxed_local().await),
|
MemKind::Module(Mod::from_parsed(m, path, ctx).boxed_local().await),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -452,7 +448,7 @@ pub struct LazyMemberHandle {
|
|||||||
}
|
}
|
||||||
impl LazyMemberHandle {
|
impl LazyMemberHandle {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub async fn run(mut self, ctx: Ctx, consts: &MemoMap<Sym, Expr>) -> MemberKind {
|
pub async fn run(mut self, ctx: Ctx, consts: &MemoMap<Sym, Expr>) -> MemKind {
|
||||||
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) => {
|
||||||
@@ -460,12 +456,12 @@ impl LazyMemberHandle {
|
|||||||
let expr = Expr::from_api(c, PathSetBuilder::new(), ctx).await;
|
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
|
MemKind::Const
|
||||||
},
|
},
|
||||||
api::MemberKind::Module(m) => {
|
api::MemberKind::Module(m) => {
|
||||||
let (.., path) = self.destructure();
|
let (.., path) = self.destructure();
|
||||||
MemberKind::Module(
|
MemKind::Module(
|
||||||
Module::from_api(m, &mut TreeFromApiCtx { sys: &sys, consts, path: path.tok() }).await,
|
Mod::from_api(m, &mut TreeFromApiCtx { sys: &sys, consts, path: path.tok() }).await,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
api::MemberKind::Lazy(id) => {
|
api::MemberKind::Lazy(id) => {
|
||||||
|
|||||||
@@ -16,26 +16,26 @@ path = "src/lib.rs"
|
|||||||
async-event = "0.2.1"
|
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"
|
chrono = "0.4.44"
|
||||||
futures = { version = "0.3.31", features = ["std"], default-features = false }
|
futures = { version = "0.3.32", features = ["std"], default-features = false }
|
||||||
hashbrown = "0.16.1"
|
hashbrown = "0.17.0"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
never = "0.1.0"
|
never = "0.1.0"
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.4"
|
||||||
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-base = { version = "0.1.0", path = "../orchid-base" }
|
orchid-base = { version = "0.1.0", path = "../orchid-base" }
|
||||||
orchid-extension = { version = "0.1.0", path = "../orchid-extension", features = [
|
orchid-extension = { version = "0.1.0", path = "../orchid-extension", features = [
|
||||||
"tokio",
|
"tokio",
|
||||||
] }
|
] }
|
||||||
ordered-float = "5.1.0"
|
ordered-float = "5.3.0"
|
||||||
pastey = "0.2.1"
|
pastey = "0.2.2"
|
||||||
rust_decimal = "1.39.0"
|
rust_decimal = "1.41.0"
|
||||||
subslice-offset = "0.1.1"
|
subslice-offset = "0.1.1"
|
||||||
substack = "1.1.1"
|
substack = "1.1.1"
|
||||||
tokio = { version = "1.49.0", features = ["full"] }
|
tokio = { version = "1.52.1", features = ["full"] }
|
||||||
unicode-segmentation = "1.12.0"
|
unicode-segmentation = "1.13.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
test_executors = "0.4.1"
|
test_executors = "0.4.1"
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ use std::borrow::Cow;
|
|||||||
|
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_base::fmt;
|
use orchid_base::fmt;
|
||||||
use orchid_extension::Expr;
|
|
||||||
use orchid_extension::gen_expr::new_atom;
|
use orchid_extension::gen_expr::new_atom;
|
||||||
use orchid_extension::{Atomic, OwnedAtom, OwnedVariant, TAtom, ToExpr, exec};
|
use orchid_extension::{Atomic, Expr, OwnedAtom, OwnedVariant, TAtom, ToExpr, exec};
|
||||||
|
|
||||||
use crate::macros::mactree::{MacTok, MacTree};
|
use crate::macros::mactree::{MacTok, MacTree};
|
||||||
|
|
||||||
|
|||||||
@@ -4,16 +4,17 @@ use futures::{FutureExt, StreamExt, stream};
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::{
|
use orchid_base::{
|
||||||
Comment, OrcRes, Paren, Parsed, Snippet, Sym, expect_tok, is, report, sym, token_errv,
|
Comment, OrcRes, Paren, Parsed, Snippet, Sym, expect_tok, is, report, token_errv,
|
||||||
try_pop_no_fluff, with_reporter,
|
try_pop_no_fluff, with_reporter,
|
||||||
};
|
};
|
||||||
use orchid_extension::gen_expr::{call, new_atom};
|
|
||||||
use orchid_extension::{
|
use orchid_extension::{
|
||||||
ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser, TAtom, TryFromExpr,
|
ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser, TAtom, TryFromExpr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::macros::lower::lower;
|
||||||
use crate::macros::mactree::{MacTok, MacTree, MacTreeSeq};
|
use crate::macros::mactree::{MacTok, MacTree, MacTreeSeq};
|
||||||
use crate::macros::ph_lexer::PhAtom;
|
use crate::macros::ph_lexer::PhAtom;
|
||||||
|
use crate::macros::resolve::resolve;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct LetLine;
|
pub struct LetLine;
|
||||||
@@ -35,15 +36,22 @@ impl Parser for LetLine {
|
|||||||
};
|
};
|
||||||
let Parsed { tail, .. } = expect_tok(tail, is("=").await).await?;
|
let Parsed { tail, .. } = expect_tok(tail, is("=").await).await?;
|
||||||
let aliased = parse_tokv(tail).await;
|
let aliased = parse_tokv(tail).await;
|
||||||
Ok(vec![ParsedLine::cnst(&line.sr(), &comments, exported, name, async move |ctx| {
|
Ok(vec![ParsedLine::cnst(
|
||||||
let macro_input =
|
&line.sr(),
|
||||||
MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&aliased, &ctx)).await?).at(sr.pos());
|
&comments,
|
||||||
Ok(call(sym!(macros::resolve), new_atom(macro_input)))
|
exported,
|
||||||
})])
|
name.clone(),
|
||||||
|
async move |mut ctx| {
|
||||||
|
let macro_input =
|
||||||
|
MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&aliased, &ctx)).await?).at(sr.pos());
|
||||||
|
let resolved = resolve(ctx.handle(), macro_input).await;
|
||||||
|
Ok(lower(&resolved, substack::Substack::Bottom).await)
|
||||||
|
},
|
||||||
|
)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn dealias_mac_v(aliased: &MacTreeSeq, ctx: &ConstCtx) -> MacTreeSeq {
|
pub async fn dealias_mac_v(aliased: &MacTreeSeq, ctx: &ConstCtx<'_>) -> MacTreeSeq {
|
||||||
let keys = aliased.glossary().iter().cloned().collect_vec();
|
let keys = aliased.glossary().iter().cloned().collect_vec();
|
||||||
let mut names: HashMap<_, _> = HashMap::new();
|
let mut names: HashMap<_, _> = HashMap::new();
|
||||||
let mut stream = pin!(ctx.names(&keys).zip(stream::iter(&keys)));
|
let mut stream = pin!(ctx.names(&keys).zip(stream::iter(&keys)));
|
||||||
|
|||||||
81
orchid-std/src/macros/lower.rs
Normal file
81
orchid-std/src/macros/lower.rs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use async_fn_stream::stream;
|
||||||
|
use futures::{FutureExt, StreamExt, stream};
|
||||||
|
use itertools::Itertools;
|
||||||
|
use orchid_base::{OrcErrv, Paren, Sym, fmt, is, mk_errv, report};
|
||||||
|
use orchid_extension::ToExpr;
|
||||||
|
use orchid_extension::gen_expr::{GExpr, GExprKind, bot, call};
|
||||||
|
use substack::Substack;
|
||||||
|
|
||||||
|
use crate::macros::mactree::MacTreeSeq;
|
||||||
|
use crate::{MacTok, MacTree};
|
||||||
|
|
||||||
|
fn on_err(e: OrcErrv) -> GExpr {
|
||||||
|
report(e.clone());
|
||||||
|
bot(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn lower(mt: &MacTree, args: Substack<'_, Sym>) -> GExpr {
|
||||||
|
match mt.tok() {
|
||||||
|
MacTok::Resolved(inner) => lower(inner, args).boxed_local().await,
|
||||||
|
MacTok::Bottom(b) => on_err(b.clone()),
|
||||||
|
MacTok::Slot => on_err(mk_errv(
|
||||||
|
is("Lowering intermediary slotted mactree").await,
|
||||||
|
"Found a Slot during lowering, which should only exist in temporary templates",
|
||||||
|
[mt.pos()],
|
||||||
|
)),
|
||||||
|
MacTok::Ph(ph) => on_err(mk_errv(
|
||||||
|
is("Lowering placeholder").await,
|
||||||
|
format!("Found {ph} during lowering. Placeholders should only exist in macro patterns"),
|
||||||
|
[mt.pos()],
|
||||||
|
)),
|
||||||
|
MacTok::S(Paren::Curly | Paren::Square, _) => on_err(mk_errv(
|
||||||
|
is("Lowering {...} or [...]").await,
|
||||||
|
format!("Cannot lower this syntax, probable incorrect macro call: {}", fmt(mt).await),
|
||||||
|
[mt.pos()],
|
||||||
|
)),
|
||||||
|
MacTok::S(Paren::Round, b) if b.items.is_empty() => on_err(mk_errv(
|
||||||
|
is("Cannot lower empty ()").await,
|
||||||
|
"Attempted to lower empty (). All expressions must have a value.",
|
||||||
|
[mt.pos()],
|
||||||
|
)),
|
||||||
|
MacTok::Lambda(arg, body) => {
|
||||||
|
let MacTok::Name(n) = arg.tok() else {
|
||||||
|
return on_err(mk_errv(
|
||||||
|
is("Lowering lambda with complex param").await,
|
||||||
|
format!("In code after macros, lambda params can only be names, not {}", fmt(arg).await),
|
||||||
|
[arg.pos()],
|
||||||
|
));
|
||||||
|
};
|
||||||
|
if body.items.is_empty() {
|
||||||
|
return on_err(mk_errv(
|
||||||
|
is("Lowering lambda with empty body").await,
|
||||||
|
"Lambdas without a body are invalid, all expressions in Orchid must have a value",
|
||||||
|
[mt.pos()],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let body = lower_seq(body, args.push(n.clone())).await;
|
||||||
|
GExprKind::Lambda(Box::new(body)).at(mt.pos())
|
||||||
|
},
|
||||||
|
MacTok::S(Paren::Round, body) => lower_seq(body, args).await,
|
||||||
|
MacTok::Name(n) => match args.iter().find_position(|b| *b == n) {
|
||||||
|
Some((i, _)) => GExprKind::Arg(i.try_into().unwrap()).at(mt.pos()),
|
||||||
|
None => n.clone().to_gen().await,
|
||||||
|
},
|
||||||
|
MacTok::Value(expr) => expr.clone().to_gen().await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn lower_seq(mtv: &MacTreeSeq, args: Substack<'_, Sym>) -> GExpr {
|
||||||
|
let mut exprs = stream(async |mut h| {
|
||||||
|
for mt in mtv.items.iter() {
|
||||||
|
h.emit(lower(mt, args.clone()).await).await
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<VecDeque<_>>()
|
||||||
|
.boxed_local()
|
||||||
|
.await;
|
||||||
|
let first = exprs.pop_front().expect("We checked first that it isn't empty");
|
||||||
|
stream::iter(exprs).fold(first, async |f, x| call(f, x).await).await
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
use orchid_base::sym;
|
use orchid_extension::gen_expr::new_atom;
|
||||||
use orchid_extension::gen_expr::{call, new_atom};
|
|
||||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||||
use orchid_extension::{TAtom, exec};
|
use orchid_extension::{TAtom, exec};
|
||||||
|
use substack::Substack;
|
||||||
|
|
||||||
|
use crate::macros::lower::lower;
|
||||||
use crate::macros::mactree::MacTree;
|
use crate::macros::mactree::MacTree;
|
||||||
use crate::macros::resolve::resolve;
|
use crate::macros::resolve::resolve;
|
||||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||||
@@ -10,74 +11,111 @@ 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(tpl.own().await).await),
|
fun(true, "resolve", async |tpl: TAtom<MacTree>| {
|
||||||
|
exec(async move |mut h| new_atom(resolve(&mut h, tpl.own().await).await)).await
|
||||||
|
}),
|
||||||
|
fun(true, "lower", async |mt: TAtom<MacTree>| lower(&mt.own().await, Substack::Bottom).await),
|
||||||
prefix("common", [
|
prefix("common", [
|
||||||
build_macro(None, ["..", "_", "="]).finish(),
|
build_macro(None, ["..", "_", "="]).finish(),
|
||||||
build_macro(Some(1), ["+"])
|
build_macro(Some(30), ["+"])
|
||||||
.rule(mactreev!("...$" lhs 1 macros::common::+ "...$" rhs 0), [async |[lhs, rhs]| {
|
.rule(
|
||||||
call(sym!(std::ops::add::resolve), (resolve(lhs).await, resolve(rhs).await)).await
|
mactreev!("...$" lhs 1 "macros::common::+" "...$" rhs 0),
|
||||||
}])
|
async |mut cx, [lhs, rhs]| {
|
||||||
|
Ok(mactree!("std::ops::add::resolve"
|
||||||
|
"push" cx.recur(lhs).await;
|
||||||
|
"push" cx.recur(rhs).await;))
|
||||||
|
},
|
||||||
|
)
|
||||||
.finish(),
|
.finish(),
|
||||||
build_macro(Some(1), ["-"])
|
build_macro(Some(30), ["-"])
|
||||||
.rule(mactreev!("...$" lhs 1 macros::common::- "...$" rhs 0), [async |[lhs, rhs]| {
|
.rule(
|
||||||
call(sym!(std::ops::sub::resolve), (resolve(lhs).await, resolve(rhs).await)).await
|
mactreev!("...$" lhs 1 "macros::common::-" "...$" rhs 0),
|
||||||
}])
|
async |mut cx, [lhs, rhs]| {
|
||||||
|
Ok(mactree!("std::ops::sub::resolve"
|
||||||
|
"push" cx.recur(lhs).await;
|
||||||
|
"push" cx.recur(rhs).await;))
|
||||||
|
},
|
||||||
|
)
|
||||||
.finish(),
|
.finish(),
|
||||||
build_macro(Some(2), ["*"])
|
build_macro(Some(20), ["*"])
|
||||||
.rule(mactreev!("...$" lhs 1 macros::common::* "...$" rhs 0), [async |[lhs, rhs]| {
|
.rule(
|
||||||
call(sym!(std::ops::mul::resolve), (resolve(lhs).await, resolve(rhs).await)).await
|
mactreev!("...$" lhs 1 "macros::common::*" "...$" rhs 0),
|
||||||
}])
|
async |mut cx, [lhs, rhs]| {
|
||||||
|
Ok(mactree!("std::ops::mul::resolve"
|
||||||
|
"push" cx.recur(lhs).await;
|
||||||
|
"push" cx.recur(rhs).await;))
|
||||||
|
},
|
||||||
|
)
|
||||||
.finish(),
|
.finish(),
|
||||||
build_macro(Some(2), ["/"])
|
build_macro(Some(20), ["/"])
|
||||||
.rule(mactreev!("...$" lhs 1 macros::common::/ "...$" rhs 0), [async |[lhs, rhs]| {
|
.rule(
|
||||||
call(sym!(std::ops::div::resolve), (resolve(lhs).await, resolve(rhs).await)).await
|
mactreev!("...$" lhs 1 "macros::common::/" "...$" rhs 0),
|
||||||
}])
|
async |mut cx, [lhs, rhs]| {
|
||||||
|
Ok(mactree!("std::ops::div::resolve"
|
||||||
|
"push" cx.recur(lhs).await;
|
||||||
|
"push" cx.recur(rhs).await;))
|
||||||
|
},
|
||||||
|
)
|
||||||
.finish(),
|
.finish(),
|
||||||
build_macro(Some(2), ["%"])
|
build_macro(Some(20), ["%"])
|
||||||
.rule(mactreev!("...$" lhs 1 macros::common::% "...$" rhs 0), [async |[lhs, rhs]| {
|
.rule(
|
||||||
call(sym!(std::ops::mod::resolve), (resolve(lhs).await, resolve(rhs).await)).await
|
mactreev!("...$" lhs 1 "macros::common::%" "...$" rhs 0),
|
||||||
}])
|
async |mut cx, [lhs, rhs]| {
|
||||||
|
Ok(mactree!("std::ops::mod::resolve"
|
||||||
|
"push" cx.recur(lhs).await;
|
||||||
|
"push" cx.recur(rhs).await;))
|
||||||
|
},
|
||||||
|
)
|
||||||
.finish(),
|
.finish(),
|
||||||
build_macro(Some(3), ["."])
|
build_macro(Some(10), ["."])
|
||||||
.rule(mactreev!("...$" lhs 1 macros::common::. "...$" rhs 0), [async |[lhs, rhs]| {
|
.rule(
|
||||||
call(sym!(std::ops::get::resolve), (resolve(lhs).await, resolve(rhs).await)).await
|
mactreev!("...$" lhs 1 "macros::common::." "...$" rhs 0),
|
||||||
}])
|
async |mut cx, [lhs, rhs]| {
|
||||||
|
Ok(mactree!("std::ops::get::resolve"
|
||||||
|
"push" cx.recur(lhs).await;
|
||||||
|
"push" cx.recur(rhs).await;))
|
||||||
|
},
|
||||||
|
)
|
||||||
.finish(),
|
.finish(),
|
||||||
build_macro(None, ["comma_list", ","])
|
build_macro(None, ["comma_list", ","])
|
||||||
.rule(
|
.rule(
|
||||||
mactreev!(macros::common::comma_list ( "...$" head 0 macros::common::, "...$" tail 1)),
|
mactreev!("macros::common::comma_list" (
|
||||||
[async |[head, tail]| {
|
"...$" head 0 "macros::common::," "...$" tail 1
|
||||||
exec(async |mut h| {
|
)),
|
||||||
let recur = resolve(mactree!(macros::common::comma_list "push" tail ;)).await;
|
async |mut cx, [head, tail]| {
|
||||||
let mut tail = h.exec::<HomoTpl<TAtom<MacTree>>>(recur).await?;
|
let mut tail: HomoTpl<TAtom<MacTree>> =
|
||||||
tail.0.insert(0, h.exec(new_atom(head)).await?);
|
cx.recur_call(mactree!("macros::common::comma_list" "push" tail ;)).await?;
|
||||||
Ok(tail)
|
tail.0.insert(0, cx.exec(new_atom(head)).await?);
|
||||||
})
|
Ok(mactree!("Val" tail;))
|
||||||
.await
|
},
|
||||||
}],
|
|
||||||
)
|
)
|
||||||
.rule(mactreev!(macros::common::comma_list ( "...$" final_tail 0 )), [async |[tail]| {
|
.rule(
|
||||||
HomoTpl(vec![new_atom(tail)])
|
mactreev!("macros::common::comma_list" ( "...$" final_tail 0 )),
|
||||||
}])
|
async |_cx, [tail]| Ok(mactree!("Val" HomoTpl(vec![new_atom(tail)]);)),
|
||||||
.rule(mactreev!(macros::common::comma_list()), [async |[]| UntypedTuple(Vec::new())])
|
)
|
||||||
|
.rule(mactreev!("macros::common::comma_list"()), async |_cx, []| {
|
||||||
|
Ok(mactree!("Val" UntypedTuple(Vec::new());))
|
||||||
|
})
|
||||||
.finish(),
|
.finish(),
|
||||||
build_macro(None, ["semi_list", ";"])
|
build_macro(None, ["semi_list", ";"])
|
||||||
.rule(
|
.rule(
|
||||||
mactreev!(macros::common::semi_list ( "...$" head 0 macros::common::; "...$" tail 1)),
|
mactreev!("macros::common::semi_list" (
|
||||||
[async |[head, tail]| {
|
"...$" head 0 "macros::common::;" "...$" tail 1
|
||||||
exec(async |mut h| {
|
)),
|
||||||
let recur = resolve(mactree!(macros::common::semi_list "push" tail ;)).await;
|
async |mut cx, [head, tail]| {
|
||||||
let mut tail = h.exec::<HomoTpl<TAtom<MacTree>>>(recur).await?;
|
let mut tail: HomoTpl<TAtom<MacTree>> =
|
||||||
tail.0.insert(0, h.exec(new_atom(head)).await?);
|
cx.recur_call(mactree!("macros::common::semi_list" "push" tail ;)).await?;
|
||||||
Ok(tail)
|
tail.0.insert(0, cx.exec(new_atom(head)).await?);
|
||||||
})
|
Ok(mactree!("Val" tail))
|
||||||
.await
|
},
|
||||||
}],
|
|
||||||
)
|
)
|
||||||
.rule(mactreev!(macros::common::semi_list ( "...$" final_tail 0 )), [async |[tail]| {
|
.rule(
|
||||||
HomoTpl(vec![new_atom(tail)])
|
mactreev!("macros::common::semi_list" ( "...$" final_tail 0 )),
|
||||||
}])
|
async |_cx, [tail]| Ok(mactree!("Val" HomoTpl(vec![new_atom(tail)]))),
|
||||||
.rule(mactreev!(macros::common::semi_list()), [async |[]| UntypedTuple(Vec::new())])
|
)
|
||||||
|
.rule(mactreev!("macros::common::semi_list"()), async |_cx, []| {
|
||||||
|
Ok(mactree!("Val" UntypedTuple(Vec::new())))
|
||||||
|
})
|
||||||
.finish(),
|
.finish(),
|
||||||
]),
|
]),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -8,10 +8,8 @@ use orchid_base::{
|
|||||||
Comment, OrcRes, Paren, Parsed, Snippet, Token, clone, expect_end, expect_tok, is, line_items,
|
Comment, OrcRes, Paren, Parsed, Snippet, Token, clone, expect_end, expect_tok, is, line_items,
|
||||||
mk_errv, report, sym, token_errv, try_pop_no_fluff, with_reporter,
|
mk_errv, report, sym, token_errv, try_pop_no_fluff, with_reporter,
|
||||||
};
|
};
|
||||||
use orchid_extension::TAtom;
|
|
||||||
use orchid_extension::{ToExpr, TryFromExpr};
|
|
||||||
use orchid_extension::gen_expr::{call, new_atom};
|
use orchid_extension::gen_expr::{call, new_atom};
|
||||||
use orchid_extension::{PSnippet, ParsCtx, ParsedLine, Parser};
|
use orchid_extension::{PSnippet, ParsCtx, ParsedLine, Parser, TAtom, ToExpr, TryFromExpr};
|
||||||
|
|
||||||
use crate::macros::let_line::{dealias_mac_v, parse_tokv};
|
use crate::macros::let_line::{dealias_mac_v, parse_tokv};
|
||||||
use crate::macros::macro_value::{Macro, MacroData, Rule};
|
use crate::macros::macro_value::{Macro, MacroData, Rule};
|
||||||
@@ -130,7 +128,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!(macros::resolve), new_atom(macro_input)))
|
Ok(call(sym!(macros::lower), call(sym!(macros::resolve), new_atom(macro_input))))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
let mac_cell = Rc::new(OnceCell::new());
|
let mac_cell = Rc::new(OnceCell::new());
|
||||||
|
|||||||
@@ -48,9 +48,7 @@ pub struct MacroSystemInst {
|
|||||||
}
|
}
|
||||||
impl System for MacroSystemInst {
|
impl System for MacroSystemInst {
|
||||||
type Ctor = MacroSystem;
|
type Ctor = MacroSystem;
|
||||||
async fn request<'a>(&self, _: Box<dyn ReqHandle<'a> + 'a>, req: Never) -> Receipt<'a> {
|
async fn request(&self, _: Box<dyn ReqHandle>, req: Never) -> Receipt { match req {} }
|
||||||
match req {}
|
|
||||||
}
|
|
||||||
async fn prelude(&self) -> Vec<Sym> {
|
async fn prelude(&self) -> Vec<Sym> {
|
||||||
vec![
|
vec![
|
||||||
sym!(macros::common::+),
|
sym!(macros::common::+),
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ use std::borrow::Cow;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_base::IStr;
|
use orchid_base::{IStr, Sym};
|
||||||
use orchid_base::Sym;
|
use orchid_extension::{Atomic, OwnedAtom, OwnedVariant};
|
||||||
use orchid_extension::Atomic;
|
|
||||||
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;
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ use orchid_api_derive::Coding;
|
|||||||
use orchid_base::{
|
use orchid_base::{
|
||||||
FmtCtx, FmtUnit, Format, IStr, OrcErrv, Paren, Pos, Sym, Variants, indent, tl_cache,
|
FmtCtx, FmtUnit, Format, IStr, OrcErrv, Paren, Pos, Sym, Variants, indent, tl_cache,
|
||||||
};
|
};
|
||||||
use orchid_extension::Atomic;
|
use orchid_extension::{Atomic, Expr, OwnedAtom, OwnedVariant};
|
||||||
use orchid_extension::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();
|
||||||
@@ -116,6 +114,7 @@ impl MacTree {
|
|||||||
MacTok::Lambda(arg, body) =>
|
MacTok::Lambda(arg, body) =>
|
||||||
MacTok::Lambda(ro(changed, |changed| arg.map(changed, map)), body.map(changed, map)),
|
MacTok::Lambda(ro(changed, |changed| arg.map(changed, map)), body.map(changed, map)),
|
||||||
MacTok::Name(_) | MacTok::Value(_) => return self.clone(),
|
MacTok::Name(_) | MacTok::Value(_) => return self.clone(),
|
||||||
|
MacTok::Resolved(inner) => return inner.map(changed, map),
|
||||||
MacTok::Slot | MacTok::Ph(_) | MacTok::Bottom(_) => return self.clone(),
|
MacTok::Slot | MacTok::Ph(_) | MacTok::Bottom(_) => return self.clone(),
|
||||||
MacTok::S(p, body) => MacTok::S(*p, body.map(changed, map)),
|
MacTok::S(p, body) => MacTok::S(*p, body.map(changed, map)),
|
||||||
},
|
},
|
||||||
@@ -154,11 +153,23 @@ pub enum MacTok {
|
|||||||
/// never accessed as variables by usercode
|
/// never accessed as variables by usercode
|
||||||
Ph(Ph),
|
Ph(Ph),
|
||||||
Bottom(OrcErrv),
|
Bottom(OrcErrv),
|
||||||
|
/// This node type cannot be manually constructed in Orchid and should not be
|
||||||
|
/// manually constructed in Rust.
|
||||||
|
///
|
||||||
|
/// - can only match placeholders
|
||||||
|
/// - skipped by Resolve and glossary
|
||||||
|
/// - transparent for all other purposes
|
||||||
|
///
|
||||||
|
/// It is used to wrap macro return values to prevent double-resolve
|
||||||
|
///
|
||||||
|
/// TODO: consider including optional metadata about the match
|
||||||
|
Resolved(MacTree),
|
||||||
}
|
}
|
||||||
impl MacTok {
|
impl MacTok {
|
||||||
pub fn build_glossary(&self) -> Rc<HashSet<Sym>> {
|
pub fn build_glossary(&self) -> Rc<HashSet<Sym>> {
|
||||||
match self {
|
match self {
|
||||||
MacTok::Bottom(_) | MacTok::Ph(_) | MacTok::Slot | MacTok::Value(_) => Rc::default(),
|
MacTok::Bottom(_) | MacTok::Ph(_) | MacTok::Slot | MacTok::Value(_) | MacTok::Resolved(_) =>
|
||||||
|
Rc::default(),
|
||||||
MacTok::Name(sym) => Rc::new(HashSet::from([sym.clone()])),
|
MacTok::Name(sym) => Rc::new(HashSet::from([sym.clone()])),
|
||||||
MacTok::S(_, body) => union_rc_sets(body.items.iter().map(|mt| mt.glossary.clone())),
|
MacTok::S(_, body) => union_rc_sets(body.items.iter().map(|mt| mt.glossary.clone())),
|
||||||
MacTok::Lambda(arg, body) =>
|
MacTok::Lambda(arg, body) =>
|
||||||
@@ -172,9 +183,10 @@ impl MacTok {
|
|||||||
impl Format for MacTok {
|
impl Format for MacTok {
|
||||||
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::Value(v) => v.print(c).await,
|
Self::Value(v) => tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("$({0b})")))
|
||||||
|
.units([v.print(c).await]),
|
||||||
Self::Lambda(arg, b) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
Self::Lambda(arg, b) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||||
.unbounded("\\{0} {1l}")
|
// .unbounded("\\{0} {1l}")
|
||||||
.bounded("(\\{0} {1b})")))
|
.bounded("(\\{0} {1b})")))
|
||||||
.units([arg.print(c).boxed_local().await, b.print(c).await]),
|
.units([arg.print(c).boxed_local().await, b.print(c).await]),
|
||||||
Self::Name(n) => format!("{n}").into(),
|
Self::Name(n) => format!("{n}").into(),
|
||||||
@@ -186,6 +198,7 @@ impl Format for MacTok {
|
|||||||
}
|
}
|
||||||
.units([body.print(c).await]),
|
.units([body.print(c).await]),
|
||||||
Self::Slot => "$SLOT".into(),
|
Self::Slot => "$SLOT".into(),
|
||||||
|
Self::Resolved(res) => res.print(c).boxed_local().await,
|
||||||
Self::Bottom(err) => match err.one() {
|
Self::Bottom(err) => match err.one() {
|
||||||
Some(err) => format!("Bottom({err}) ").into(),
|
Some(err) => format!("Bottom({err}) ").into(),
|
||||||
None => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
|
None => format!("Botttom(\n{}) ", indent(&err.to_string())).into(),
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ use std::ops::RangeInclusive;
|
|||||||
|
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use itertools::chain;
|
use itertools::chain;
|
||||||
use orchid_base::Paren;
|
use orchid_base::{OrcRes, PARENS, Paren, is, mk_errv};
|
||||||
use orchid_base::{OrcRes, PARENS, is, mk_errv};
|
|
||||||
use orchid_extension::gen_expr::new_atom;
|
use orchid_extension::gen_expr::new_atom;
|
||||||
use orchid_extension::{LexContext, Lexer, err_not_applicable};
|
|
||||||
use orchid_extension::p_tree2gen;
|
|
||||||
use orchid_extension::tree::{GenTok, GenTokTree, x_tok};
|
use orchid_extension::tree::{GenTok, GenTokTree, x_tok};
|
||||||
|
use orchid_extension::{LexContext, Lexer, err_not_applicable, p_tree2gen};
|
||||||
|
|
||||||
use crate::macros::instantiate_tpl::InstantiateTplCall;
|
use crate::macros::instantiate_tpl::InstantiateTplCall;
|
||||||
use crate::macros::let_line::parse_tok;
|
use crate::macros::let_line::parse_tok;
|
||||||
|
|||||||
@@ -1,160 +1,126 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use async_fn_stream::stream;
|
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use futures::{Stream, StreamExt, stream};
|
use futures::{StreamExt, stream};
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_api_derive::Coding;
|
use orchid_base::{OrcRes, Sym, fmt, is, mk_errv};
|
||||||
use orchid_base::{OrcRes, Sym, fmt, is, mk_errv, sym};
|
use orchid_extension::gen_expr::{bot, call, call_v, lam, new_atom};
|
||||||
use orchid_extension::ToExpr;
|
|
||||||
use orchid_extension::{ExecHandle, exec};
|
|
||||||
use orchid_extension::{Expr, ExprHandle};
|
|
||||||
use orchid_extension::gen_expr::{GExpr, arg, bot, call, call_v, lam, new_atom};
|
|
||||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||||
use orchid_extension::{Atomic, OwnedAtom, OwnedVariant, TAtom};
|
use orchid_extension::{Atomic, ExecHandle, Expr, OwnedAtom, OwnedVariant, TAtom, ToExpr, exec};
|
||||||
|
|
||||||
use crate::macros::resolve::resolve;
|
|
||||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||||
use crate::std::reflection::sym_atom::SymAtom;
|
use crate::std::reflection::sym_atom::SymAtom;
|
||||||
use crate::{HomoTpl, MacTok, MacTree, OrcOpt, Tpl, UntypedTuple, api};
|
use crate::{HomoTpl, MacTok, MacTree, OrcOpt, Tpl, UntypedTuple};
|
||||||
|
|
||||||
#[derive(Clone, Coding)]
|
|
||||||
pub struct MatcherData {
|
|
||||||
keys: Vec<api::TStrv>,
|
|
||||||
matcher: api::ExprTicket,
|
|
||||||
}
|
|
||||||
impl MatcherData {
|
|
||||||
async fn matcher(&self) -> Expr { Expr::from_handle(ExprHandle::from_ticket(self.matcher).await) }
|
|
||||||
pub async fn run_matcher(
|
|
||||||
&self,
|
|
||||||
h: &mut ExecHandle<'_>,
|
|
||||||
val: impl ToExpr,
|
|
||||||
) -> OrcRes<OrcOpt<HomoTpl<Expr>>> {
|
|
||||||
h.exec::<OrcOpt<HomoTpl<Expr>>>(call(self.matcher().await, val)).await
|
|
||||||
}
|
|
||||||
pub fn keys(&self) -> impl Stream<Item = Sym> {
|
|
||||||
stream(async |mut h| {
|
|
||||||
for tk in &self.keys {
|
|
||||||
h.emit(Sym::from_api(*tk).await).await
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MatcherAtom {
|
pub struct MatcherAtom {
|
||||||
/// The names that subresults may be bound to
|
/// The names that subresults may be bound to
|
||||||
pub(super) keys: Vec<Sym>,
|
pub(super) keys: Vec<Sym>,
|
||||||
/// Takes the value-to-be-matched, returns an `option (tuple T1..TN)` of the
|
/// Takes the value-to-be-matched, returns an `option (tuple T1..TN)` of the
|
||||||
/// subresults to be bound to the names returned by [Self::keys]
|
/// subresults to be bound to the names returned by [Self::keys]
|
||||||
pub(super) matcher: Expr,
|
pub(super) matcher: MacTree,
|
||||||
}
|
}
|
||||||
impl Atomic for MatcherAtom {
|
impl Atomic for MatcherAtom {
|
||||||
type Data = MatcherData;
|
type Data = ();
|
||||||
type Variant = OwnedVariant;
|
type Variant = OwnedVariant;
|
||||||
}
|
}
|
||||||
impl OwnedAtom for MatcherAtom {
|
impl OwnedAtom for MatcherAtom {
|
||||||
type Refs = Never;
|
type Refs = Never;
|
||||||
async fn val(&self) -> std::borrow::Cow<'_, Self::Data> {
|
async fn val(&self) -> std::borrow::Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
Cow::Owned(MatcherData {
|
}
|
||||||
keys: self.keys.iter().map(|t| t.to_api()).collect(),
|
|
||||||
matcher: self.matcher.handle().ticket(),
|
pub async fn match_one(
|
||||||
})
|
h: &mut ExecHandle<'_>,
|
||||||
}
|
mat: impl ToExpr,
|
||||||
|
value: impl ToExpr,
|
||||||
|
) -> OrcRes<OrcOpt<HomoTpl<Expr>>> {
|
||||||
|
h.exec::<OrcOpt<HomoTpl<Expr>>>(call(mat, value)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
||||||
prefix("pattern", [
|
prefix("pattern", [
|
||||||
fun(
|
fun(true, "match_one", async |mat: Expr, value: Expr, then: Expr, default: Expr| {
|
||||||
true,
|
exec(async move |mut h| match match_one(&mut h, mat, value).await? {
|
||||||
"match_one",
|
OrcOpt(Some(values)) => Ok(call_v(then, values.0).await),
|
||||||
async |mat: TAtom<MatcherAtom>, value: Expr, then: Expr, default: Expr| {
|
OrcOpt(None) => Ok(default.to_gen().await),
|
||||||
exec(async move |mut h| match mat.run_matcher(&mut h, value).await? {
|
})
|
||||||
OrcOpt(Some(values)) => Ok(call_v(then, values.0).await),
|
.await
|
||||||
OrcOpt(None) => Ok(default.to_gen().await),
|
}),
|
||||||
})
|
fun(true, "matcher", async |names: HomoTpl<TAtom<SymAtom>>, matcher: TAtom<MacTree>| {
|
||||||
.await
|
|
||||||
},
|
|
||||||
),
|
|
||||||
fun(true, "matcher", async |names: HomoTpl<TAtom<SymAtom>>, matcher: Expr| {
|
|
||||||
new_atom(MatcherAtom {
|
new_atom(MatcherAtom {
|
||||||
keys: join_all(names.0.iter().map(async |atm| Sym::from_api(atm.0).await)).await,
|
keys: join_all(names.0.iter().map(async |atm| Sym::from_api(atm.0).await)).await,
|
||||||
matcher,
|
matcher: matcher.own().await,
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
build_macro(None, ["match", "match_rule", "_row", "=>"])
|
build_macro(None, ["match", "match_rule", "_row", "=>"])
|
||||||
.rule(mactreev!("pattern::match" "...$" value 0 { "..$" rules 0 }), [
|
.rule(
|
||||||
async |[value, rules]| {
|
mactreev!("pattern::match" "...$" value 0 { "..$" rules 0 }),
|
||||||
exec(async move |mut h| {
|
async |mut cx, [value, rules]| {
|
||||||
let rule_lines = h
|
let rule_lines: HomoTpl<TAtom<MacTree>> =
|
||||||
.exec::<HomoTpl<TAtom<MacTree>>>(call(
|
cx.recur_call(mactree!("macros::common::semi_list" "push" rules.clone())).await?;
|
||||||
sym!(macros::resolve),
|
let mut rule_atoms = Vec::<(TAtom<MatcherAtom>, TAtom<MacTree>)>::new();
|
||||||
new_atom(mactree!(macros::common::semi_list "push" rules.clone();)),
|
for line_mac in rule_lines.0.iter() {
|
||||||
))
|
let Tpl((matcher, body)) =
|
||||||
.await?;
|
cx.recur_call(mactree!("pattern::_row" "push" line_mac.own().await)).await?;
|
||||||
let mut rule_atoms = Vec::<(TAtom<MatcherAtom>, Expr)>::new();
|
rule_atoms.push((matcher, body));
|
||||||
for line_mac in rule_lines.0.iter() {
|
}
|
||||||
let Tpl((matcher, body)) = h
|
let base_case = lam(async |_| {
|
||||||
.exec(call(
|
bot(mk_errv(
|
||||||
sym!(macros::resolve),
|
|
||||||
new_atom(mactree!(pattern::_row "push" line_mac.own().await ;)),
|
|
||||||
))
|
|
||||||
.await?;
|
|
||||||
rule_atoms.push((matcher, body));
|
|
||||||
}
|
|
||||||
let base_case = lam::<0>(bot(mk_errv(
|
|
||||||
is("No branches match").await,
|
is("No branches match").await,
|
||||||
"None of the patterns matches this value",
|
"None of the patterns matches this value",
|
||||||
[rules.pos()],
|
[rules.pos()],
|
||||||
)))
|
))
|
||||||
.await;
|
|
||||||
let match_expr = stream::iter(rule_atoms.into_iter().rev())
|
|
||||||
.fold(base_case, async |tail, (mat, body)| {
|
|
||||||
lam::<0>(call(sym!(pattern::match_one), (mat, arg(0), body, call(tail, arg(0)))))
|
|
||||||
.await
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
Ok(call(match_expr, resolve(value).await))
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
},
|
.create()
|
||||||
])
|
.await;
|
||||||
.rule(mactreev!(pattern::match_rule (( "...$" pattern 0 ))), [async |[pattern]| {
|
let match_chain = stream::iter(rule_atoms.into_iter().rev())
|
||||||
resolve(mactree!(pattern::match_rule "push" pattern; )).await
|
.fold(mactree!("Val" base_case;), async |tail, (mat, body)| {
|
||||||
}])
|
mactree!("l" "pattern::x" (
|
||||||
.rule(mactreev!(pattern::match_rule ( macros::common::_ )), [async |[]| {
|
"pattern::match_one"
|
||||||
Ok(new_atom(MatcherAtom {
|
"Val" mat.ex();
|
||||||
keys: Vec::new(),
|
"pattern::x"
|
||||||
matcher: lam::<0>(OrcOpt(Some(Tpl(())))).await.create().await,
|
"push" body.own().await;
|
||||||
}))
|
("push" tail;
|
||||||
}])
|
"pattern::x")
|
||||||
.rule(mactreev!(pattern::_row ( "...$" pattern 0 pattern::=> "...$" value 1 )), [
|
|
||||||
async |[pattern, mut value]| {
|
|
||||||
exec(async move |mut h| -> OrcRes<Tpl<(TAtom<MatcherAtom>, GExpr)>> {
|
|
||||||
let Ok(pat) = h
|
|
||||||
.exec::<TAtom<MatcherAtom>>(call(
|
|
||||||
sym!(macros::resolve),
|
|
||||||
new_atom(mactree!(pattern::match_rule "push" pattern.clone();)),
|
|
||||||
))
|
))
|
||||||
.await
|
})
|
||||||
else {
|
.await;
|
||||||
return Err(mk_errv(
|
Ok(mactree!("push" match_chain; "push" value))
|
||||||
is("Invalid pattern").await,
|
|
||||||
format!("Could not parse {} as a match pattern", fmt(&pattern).await),
|
|
||||||
[pattern.pos()],
|
|
||||||
));
|
|
||||||
};
|
|
||||||
value = (pat.keys())
|
|
||||||
.fold(value, async |value, name| mactree!("l_" name; ( "push" value ; )))
|
|
||||||
.await;
|
|
||||||
Ok(Tpl((pat, resolve(value).await)))
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
},
|
},
|
||||||
])
|
)
|
||||||
|
.rule(mactreev!("pattern::match_rule" (( "...$" pattern 0 ))), async |mut cx, [pattern]| {
|
||||||
|
Ok(cx.recur(mactree!("pattern::match_rule" "push" pattern)).await)
|
||||||
|
})
|
||||||
|
.rule(mactreev!("pattern::match_rule"("macros::common::_")), async |_cx, []| {
|
||||||
|
let matcher = new_atom(MatcherAtom {
|
||||||
|
keys: Vec::new(),
|
||||||
|
matcher: mactree!("l" "pattern::unused" ("Val" OrcOpt(Some(Tpl(()))))),
|
||||||
|
});
|
||||||
|
Ok(mactree!("Val" matcher))
|
||||||
|
})
|
||||||
|
.rule(
|
||||||
|
mactreev!("pattern::_row" ( "...$" pattern 0 "pattern::=>" "...$" value 1 )),
|
||||||
|
async |mut cx, [pattern, mut value]| {
|
||||||
|
let Ok(pat): OrcRes<TAtom<MatcherAtom>> =
|
||||||
|
cx.recur_call(mactree!("pattern::match_rule" "push" pattern.clone())).await
|
||||||
|
else {
|
||||||
|
return Err(mk_errv(
|
||||||
|
is("Invalid pattern").await,
|
||||||
|
format!("Could not parse {} as a match pattern", fmt(&pattern).await),
|
||||||
|
[pattern.pos()],
|
||||||
|
));
|
||||||
|
};
|
||||||
|
value = stream::iter(pat.own().await.keys.iter().cloned())
|
||||||
|
.fold(value, async |value, name| mactree!("l_" name; ( "push" value )))
|
||||||
|
.await;
|
||||||
|
Ok(mactree!("Val" Tpl((pat, new_atom(cx.recur(value).await)))))
|
||||||
|
},
|
||||||
|
)
|
||||||
.finish(),
|
.finish(),
|
||||||
fun(true, "ref_body", async |val| OrcOpt(Some(UntypedTuple(vec![val])))),
|
fun(true, "ref_body", async |val| OrcOpt(Some(UntypedTuple(vec![val])))),
|
||||||
build_macro(None, ["ref"])
|
build_macro(None, ["ref"])
|
||||||
.rule(mactreev!(pattern::match_rule(pattern::ref "$" name)), [async |[name]| {
|
.rule(mactreev!("pattern::match_rule"("pattern::ref" "$" name)), async |_cx, [name]| {
|
||||||
let MacTok::Name(name) = name.tok() else {
|
let MacTok::Name(name) = name.tok() else {
|
||||||
return Err(mk_errv(
|
return Err(mk_errv(
|
||||||
is("pattern 'ref' requires a name to bind to").await,
|
is("pattern 'ref' requires a name to bind to").await,
|
||||||
@@ -166,11 +132,12 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
|||||||
[name.pos()],
|
[name.pos()],
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
Ok(new_atom(MatcherAtom {
|
let atom = new_atom(MatcherAtom {
|
||||||
keys: vec![name.clone()],
|
keys: vec![name.clone()],
|
||||||
matcher: sym!(pattern::ref_body).to_expr().await,
|
matcher: mactree!("pattern::ref_body"),
|
||||||
}))
|
});
|
||||||
}])
|
Ok(mactree!("Val" atom))
|
||||||
|
})
|
||||||
.finish(),
|
.finish(),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
mod instantiate_tpl;
|
mod instantiate_tpl;
|
||||||
mod let_line;
|
mod let_line;
|
||||||
|
pub mod lower;
|
||||||
mod macro_lib;
|
mod macro_lib;
|
||||||
mod macro_line;
|
mod macro_line;
|
||||||
pub mod macro_system;
|
pub mod macro_system;
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
use orchid_api_derive::Coding;
|
use orchid_api_derive::Coding;
|
||||||
use orchid_base::{FmtUnit, OrcRes, es, is, mk_errv, name_char, name_start};
|
use orchid_base::{FmtUnit, OrcRes, es, is, mk_errv, name_char, name_start};
|
||||||
use orchid_extension::Atomic;
|
|
||||||
use orchid_extension::gen_expr::new_atom;
|
use orchid_extension::gen_expr::new_atom;
|
||||||
use orchid_extension::{LexContext, Lexer, err_not_applicable};
|
|
||||||
use orchid_extension::tree::{GenTokTree, x_tok};
|
use orchid_extension::tree::{GenTokTree, x_tok};
|
||||||
use orchid_extension::{ThinAtom, ThinVariant};
|
use orchid_extension::{Atomic, LexContext, Lexer, ThinAtom, ThinVariant, err_not_applicable};
|
||||||
|
|
||||||
use crate::macros::mactree::{Ph, PhKind};
|
use crate::macros::mactree::{Ph, PhKind};
|
||||||
|
|
||||||
|
|||||||
@@ -1,71 +1,60 @@
|
|||||||
use std::collections::VecDeque;
|
|
||||||
use std::ops::{Add, Range};
|
use std::ops::{Add, Range};
|
||||||
|
|
||||||
use async_fn_stream::stream;
|
use futures::FutureExt;
|
||||||
use futures::{FutureExt, StreamExt, stream};
|
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::{NameLike, Paren, Pos, Sym, VPath, fmt, is, log, mk_errv};
|
use orchid_base::{NameLike, Paren, Pos, VPath, fmt, is, log, mk_errv, report};
|
||||||
use orchid_extension::gen_expr::{GExpr, arg, bot, call, call_v, dyn_lambda, new_atom};
|
use orchid_extension::gen_expr::{call_v, new_atom};
|
||||||
use orchid_extension::{ReflMemKind, TAtom, ToExpr, exec, refl};
|
use orchid_extension::{ExecHandle, ReflMemKind, TAtom, refl};
|
||||||
use subslice_offset::SubsliceOffset;
|
use subslice_offset::SubsliceOffset;
|
||||||
use substack::Substack;
|
|
||||||
|
|
||||||
use crate::macros::macro_value::{Macro, Rule};
|
use crate::macros::macro_value::{Macro, Rule};
|
||||||
use crate::macros::mactree::MacTreeSeq;
|
use crate::macros::mactree::MacTreeSeq;
|
||||||
use crate::macros::rule::state::{MatchState, StateEntry};
|
use crate::macros::rule::state::{MatchState, StateEntry};
|
||||||
use crate::{MacTok, MacTree};
|
use crate::{MacTok, MacTree};
|
||||||
|
|
||||||
pub async fn resolve(val: MacTree) -> GExpr {
|
pub async fn resolve(h: &mut ExecHandle<'_>, val: MacTree) -> MacTree {
|
||||||
exec(async move |mut h| {
|
writeln!(log("debug"), "Macro-resolving {}", fmt(&val).await).await;
|
||||||
writeln!(log("debug"), "Macro-resolving {}", fmt(&val).await).await;
|
let root = refl();
|
||||||
let root = refl();
|
let mut macros = HashMap::new();
|
||||||
let mut macros = HashMap::new();
|
for n in val.glossary() {
|
||||||
for n in val.glossary() {
|
let (foot, body) = n.split_last_seg();
|
||||||
let (foot, body) = n.split_last_seg();
|
let new_name = VPath::new(body.iter().cloned())
|
||||||
let new_name = VPath::new(body.iter().cloned())
|
.name_with_suffix(is(&format!("__macro__{foot}")).await)
|
||||||
.name_with_suffix(is(&format!("__macro__{foot}")).await)
|
.to_sym()
|
||||||
.to_sym()
|
.await;
|
||||||
.await;
|
if let Ok(ReflMemKind::Const) = root.get_by_path(&new_name).await.map(|m| m.kind()) {
|
||||||
if let Ok(ReflMemKind::Const) = root.get_by_path(&new_name).await.map(|m| m.kind()) {
|
let Ok(mac) = h.exec::<TAtom<Macro>>(new_name).await else { continue };
|
||||||
let Ok(mac) = h.exec::<TAtom<Macro>>(new_name).await else { continue };
|
let mac = mac.own().await;
|
||||||
let mac = mac.own().await;
|
macros.entry(mac.0.canonical_name.clone()).or_insert(mac);
|
||||||
macros.entry(mac.0.canonical_name.clone()).or_insert(mac);
|
}
|
||||||
|
}
|
||||||
|
let mut exclusive = Vec::new();
|
||||||
|
let mut prios = Vec::<u64>::new();
|
||||||
|
let mut priod = Vec::<FilteredMacroRecord>::new();
|
||||||
|
for (_, mac) in macros.iter() {
|
||||||
|
let mut record = FilteredMacroRecord { mac, rules: Vec::new() };
|
||||||
|
for (rule_i, rule) in mac.0.rules.iter().enumerate() {
|
||||||
|
if rule.pattern.glossary.is_subset(val.glossary()) {
|
||||||
|
record.rules.push(rule_i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut exclusive = Vec::new();
|
if !record.rules.is_empty() {
|
||||||
let mut prios = Vec::<u64>::new();
|
match mac.0.prio {
|
||||||
let mut priod = Vec::<FilteredMacroRecord>::new();
|
None => exclusive.push(record),
|
||||||
for (_, mac) in macros.iter() {
|
Some(prio) => {
|
||||||
let mut record = FilteredMacroRecord { mac, rules: Vec::new() };
|
let i = prios.partition_point(|p| *p > prio);
|
||||||
for (rule_i, rule) in mac.0.rules.iter().enumerate() {
|
prios.insert(i, prio);
|
||||||
if rule.pattern.glossary.is_subset(val.glossary()) {
|
priod.insert(i, record);
|
||||||
record.rules.push(rule_i);
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
if !record.rules.is_empty() {
|
|
||||||
match mac.0.prio {
|
|
||||||
None => exclusive.push(record),
|
|
||||||
Some(prio) => {
|
|
||||||
let i = prios.partition_point(|p| *p > prio);
|
|
||||||
prios.insert(i, prio);
|
|
||||||
priod.insert(i, record);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut rctx = ResolveCtx { exclusive, priod };
|
}
|
||||||
let gex = resolve_one(&mut rctx, Substack::Bottom, &val).await;
|
let mut rctx = ResolveCtx { exclusive, priod, h: &mut *h };
|
||||||
writeln!(
|
let gex = resolve_one(&mut rctx, &val).await.unwrap_or(val.clone());
|
||||||
log("debug"),
|
writeln!(log("debug"), "Macro-resolution over {} yielded {}", fmt(&val).await, fmt(&gex).await)
|
||||||
"Macro-resolution over {}\nreturned {}",
|
|
||||||
fmt(&val).await,
|
|
||||||
fmt(&gex).await
|
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
gex
|
gex
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rules belonging to one macro that passed a particular filter
|
/// Rules belonging to one macro that passed a particular filter
|
||||||
@@ -75,45 +64,33 @@ pub struct FilteredMacroRecord<'a> {
|
|||||||
rules: Vec<usize>,
|
rules: Vec<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ResolveCtx<'a> {
|
struct ResolveCtx<'a, 'b> {
|
||||||
/// If these overlap, that's a compile-time error
|
/// If these overlap, that's a compile-time error
|
||||||
pub exclusive: Vec<FilteredMacroRecord<'a>>,
|
pub exclusive: Vec<FilteredMacroRecord<'a>>,
|
||||||
/// If these overlap, the priorities decide the order. In case of a tie, the
|
/// If these overlap, the priorities decide the order. In case of a tie, the
|
||||||
/// order is unspecified
|
/// order is unspecified
|
||||||
pub priod: Vec<FilteredMacroRecord<'a>>,
|
pub priod: Vec<FilteredMacroRecord<'a>>,
|
||||||
|
pub h: &'a mut ExecHandle<'b>,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn resolve_one(
|
async fn resolve_one(ctx: &mut ResolveCtx<'_, '_>, value: &MacTree) -> Option<MacTree> {
|
||||||
ctx: &mut ResolveCtx<'_>,
|
|
||||||
arg_stk: Substack<'_, Sym>,
|
|
||||||
value: &MacTree,
|
|
||||||
) -> GExpr {
|
|
||||||
match value.tok() {
|
match value.tok() {
|
||||||
MacTok::Ph(_) | MacTok::Slot => panic!("Forbidden element in value mactree"),
|
MacTok::Ph(_) | MacTok::Slot => panic!("Forbidden element in value mactree"),
|
||||||
MacTok::Bottom(err) => bot(err.clone()),
|
MacTok::Bottom(_) | MacTok::Value(_) | MacTok::Name(_) | MacTok::Resolved(_) => None,
|
||||||
MacTok::Value(v) => v.clone().to_gen().await,
|
|
||||||
MacTok::Name(n) => match arg_stk.iter().position(|arg| arg == n) {
|
|
||||||
Some(de_bruijn) => arg((arg_stk.len() - 1 - de_bruijn).try_into().unwrap()),
|
|
||||||
None => n.clone().to_gen().await,
|
|
||||||
},
|
|
||||||
MacTok::Lambda(arg, body) => {
|
MacTok::Lambda(arg, body) => {
|
||||||
let MacTok::Name(name) = &*arg.tok else {
|
let new_arg = resolve_one(ctx, arg).boxed_local().await;
|
||||||
return bot(mk_errv(
|
let new_body = resolve_seq(ctx, body, value.pos()).boxed_local().await;
|
||||||
is("Syntax error after macros").await,
|
if new_arg.is_none() && new_body.is_none() {
|
||||||
"This token ends up as a binding, consider replacing it with a name",
|
return None;
|
||||||
[arg.pos()],
|
}
|
||||||
));
|
let tok = MacTok::Lambda(
|
||||||
};
|
new_arg.unwrap_or_else(|| arg.clone()),
|
||||||
let arg_pos = arg_stk.len() as u64;
|
new_body.unwrap_or_else(|| body.clone()),
|
||||||
let arg_stk = arg_stk.push(name.clone());
|
);
|
||||||
dyn_lambda(arg_pos, resolve_seq(ctx, arg_stk, body.clone(), value.pos()).await).await
|
Some(tok.at(value.pos()))
|
||||||
},
|
},
|
||||||
MacTok::S(Paren::Round, body) => resolve_seq(ctx, arg_stk, body.clone(), value.pos()).await,
|
MacTok::S(pty, body) => (resolve_seq(ctx, body, value.pos()).boxed_local().await)
|
||||||
MacTok::S(..) => bot(mk_errv(
|
.map(|body| MacTok::S(*pty, body).at(value.pos())),
|
||||||
is("Leftover [] or {} not matched by macro").await,
|
|
||||||
format!("{} was not matched by any macro", fmt(value).await),
|
|
||||||
[value.pos()],
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,18 +109,12 @@ fn subsection<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn resolve_seq(
|
async fn resolve_seq(
|
||||||
ctx: &mut ResolveCtx<'_>,
|
ctx: &mut ResolveCtx<'_, '_>,
|
||||||
arg_stk: Substack<'_, Sym>,
|
val: &MacTreeSeq,
|
||||||
val: MacTreeSeq,
|
|
||||||
fallback_pos: Pos,
|
fallback_pos: Pos,
|
||||||
) -> GExpr {
|
) -> Option<MacTreeSeq> {
|
||||||
if val.items.is_empty() {
|
if val.items.is_empty() {
|
||||||
return bot(mk_errv(
|
return None;
|
||||||
is("Empty sequence").await,
|
|
||||||
"() or (\\arg ) left after macro execution. \
|
|
||||||
This is usually caused by an incomplete call to a macro with bad error detection",
|
|
||||||
[fallback_pos],
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
// A sorted collection of overlapping but non-nested matches to exclusive
|
// A sorted collection of overlapping but non-nested matches to exclusive
|
||||||
// macros
|
// macros
|
||||||
@@ -196,6 +167,7 @@ async fn resolve_seq(
|
|||||||
x_matches.splice(lt_start..lt_start + lt_range.len(), [new_r]);
|
x_matches.splice(lt_start..lt_start + lt_range.len(), [new_r]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut any_match = !x_matches.is_empty();
|
||||||
// apply exclusive matches
|
// apply exclusive matches
|
||||||
if !x_matches.is_empty() {
|
if !x_matches.is_empty() {
|
||||||
// ranges of indices into x_matches which setwise conflict with each other.
|
// ranges of indices into x_matches which setwise conflict with each other.
|
||||||
@@ -226,18 +198,19 @@ async fn resolve_seq(
|
|||||||
})
|
})
|
||||||
.reduce(|l, r| l + r);
|
.reduce(|l, r| l + r);
|
||||||
if let Some(error) = error {
|
if let Some(error) = error {
|
||||||
return bot(error);
|
report(error.clone());
|
||||||
|
return Some(MacTreeSeq::new([MacTok::Bottom(error).at(fallback_pos)]));
|
||||||
}
|
}
|
||||||
// no conflicts, apply all exclusive matches
|
// no conflicts, apply all exclusive matches
|
||||||
for (range, mac, rule, state) in x_matches.into_iter().rev() {
|
for (range, mac, rule, state) in x_matches.into_iter().rev() {
|
||||||
// backwards so that the non-overlapping ranges remain valid
|
// backwards so that the non-overlapping ranges remain valid
|
||||||
let pos = (state.names().flat_map(|r| r.1).cloned().reduce(Pos::add))
|
let pos = (state.names().flat_map(|r| r.1).cloned().reduce(Pos::add))
|
||||||
.expect("All macro rules must contain at least one locally defined name");
|
.expect("All macro rules must contain at least one locally defined name");
|
||||||
let subex = mk_body_call(mac, rule, &state, pos.clone()).await.to_expr().await;
|
let subex = call_body(ctx.h, mac, rule, &state, pos.clone()).await;
|
||||||
new_val.splice(range, [MacTok::Value(subex).at(pos)]);
|
new_val.splice(range, [subex]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Does this glossary refresh actually pay off?
|
// TODO: Does this glossary refresh actually pay off?
|
||||||
let top_glossary = (new_val.iter())
|
let top_glossary = (new_val.iter())
|
||||||
.flat_map(|t| if let MacTok::Name(t) = t.tok() { Some(t.clone()) } else { None })
|
.flat_map(|t| if let MacTok::Name(t) = t.tok() { Some(t.clone()) } else { None })
|
||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
@@ -248,29 +221,30 @@ async fn resolve_seq(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let Some((pre, state, suf)) = rule.matcher.apply(&new_val, &|_| true).await else { continue };
|
let Some((pre, state, suf)) = rule.matcher.apply(&new_val, &|_| true).await else { continue };
|
||||||
|
any_match = true;
|
||||||
let range = pre.len()..new_val.len() - suf.len();
|
let range = pre.len()..new_val.len() - suf.len();
|
||||||
let pos = (state.names().flat_map(|pair| pair.1).cloned().reduce(Pos::add))
|
let pos = (state.names().flat_map(|pair| pair.1).cloned().reduce(Pos::add))
|
||||||
.expect("All macro rules must contain at least one locally defined name");
|
.expect("All macro rules must contain at least one locally defined name");
|
||||||
let subex = mk_body_call(mac, rule, &state, pos.clone()).await.to_expr().await;
|
let subex = call_body(ctx.h, mac, rule, &state, pos.clone()).await;
|
||||||
std::mem::drop(state);
|
std::mem::drop(state);
|
||||||
new_val.splice(range, [MacTok::Value(subex).at(pos)]);
|
new_val.splice(range, [subex]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut exprs = stream(async |mut h| {
|
for item in new_val.iter_mut() {
|
||||||
for mt in new_val {
|
let Some(new) = resolve_one(ctx, item).await else { continue };
|
||||||
h.emit(resolve_one(ctx, arg_stk.clone(), &mt).await).await
|
*item = new;
|
||||||
}
|
any_match = true;
|
||||||
})
|
}
|
||||||
.collect::<VecDeque<_>>()
|
any_match.then_some(MacTreeSeq::new(new_val))
|
||||||
.boxed_local()
|
|
||||||
.await;
|
|
||||||
let first = exprs.pop_front().expect(
|
|
||||||
"We checked first that it isn't empty, and named macros get replaced with their results",
|
|
||||||
);
|
|
||||||
stream::iter(exprs).fold(first, async |f, x| call(f, x).await).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn mk_body_call(mac: &Macro, rule: &Rule, state: &MatchState<'_>, pos: Pos) -> GExpr {
|
async fn call_body(
|
||||||
|
h: &mut ExecHandle<'_>,
|
||||||
|
mac: &Macro,
|
||||||
|
rule: &Rule,
|
||||||
|
state: &MatchState<'_>,
|
||||||
|
pos: Pos,
|
||||||
|
) -> MacTree {
|
||||||
let mut call_args = vec![];
|
let mut call_args = vec![];
|
||||||
for name in rule.ph_names.iter() {
|
for name in rule.ph_names.iter() {
|
||||||
call_args.push(match state.get(name).expect("Missing state entry for placeholder") {
|
call_args.push(match state.get(name).expect("Missing state entry for placeholder") {
|
||||||
@@ -279,5 +253,9 @@ async fn mk_body_call(mac: &Macro, rule: &Rule, state: &MatchState<'_>, pos: Pos
|
|||||||
new_atom(MacTok::S(Paren::Round, MacTreeSeq::new(vec.iter().cloned())).at(Pos::None)),
|
new_atom(MacTok::S(Paren::Round, MacTreeSeq::new(vec.iter().cloned())).at(Pos::None)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
call_v(mac.0.module.suffix([rule.body.clone()]).await, call_args).await.at(pos.clone())
|
let f_name = mac.0.module.suffix([rule.body.clone()]).await;
|
||||||
|
match h.exec::<TAtom<MacTree>>(call_v(f_name, call_args)).await {
|
||||||
|
Err(e) => MacTok::Bottom(e).at(pos),
|
||||||
|
Ok(mt) => MacTok::Resolved(mt.own().await).at(mt.pos()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ async fn mk_scalar(pattern: &MacTree) -> OrcRes<ScalMatcher> {
|
|||||||
[pattern.pos()],
|
[pattern.pos()],
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
|
MacTok::Resolved(_) => panic!("Can only appear in macro output, not in matcher"),
|
||||||
MacTok::Value(_) | MacTok::Slot => panic!("Only used for templating"),
|
MacTok::Value(_) | MacTok::Slot => panic!("Only used for templating"),
|
||||||
MacTok::Bottom(errv) => return Err(errv.clone()),
|
MacTok::Bottom(errv) => return Err(errv.clone()),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use orchid_base::Sym;
|
use orchid_base::{OrcRes, Sym, is};
|
||||||
use orchid_base::{OrcRes, is};
|
|
||||||
|
|
||||||
use super::any_match::any_match;
|
use super::any_match::any_match;
|
||||||
use super::build::mk_any;
|
use super::build::mk_any;
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::{PARENS, Paren};
|
use orchid_base::{IStr, PARENS, Paren, Side, Sym};
|
||||||
use orchid_base::{IStr, Side, Sym};
|
|
||||||
|
|
||||||
pub enum ScalMatcher {
|
pub enum ScalMatcher {
|
||||||
Name(Sym),
|
Name(Sym),
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use orchid_base::Pos;
|
use orchid_base::{IStr, Pos, Sym, join_maps, match_mapping};
|
||||||
use orchid_base::Sym;
|
|
||||||
use orchid_base::{IStr, join_maps, match_mapping};
|
|
||||||
|
|
||||||
use crate::macros::MacTree;
|
use crate::macros::MacTree;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
use orchid_extension::tree::{GenMember, prefix};
|
use orchid_extension::tree::{GenMember, prefix};
|
||||||
|
|
||||||
use crate::macros::resolve::resolve;
|
|
||||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||||
|
|
||||||
pub async fn gen_functional_macro_lib() -> Vec<GenMember> {
|
pub async fn gen_functional_macro_lib() -> Vec<GenMember> {
|
||||||
prefix("std::fn", [build_macro(Some(4), ["|>"])
|
prefix("std::fn", [build_macro(Some(4), ["|>"])
|
||||||
.rule(mactreev!("...$" lhs 0 "std::fn::|>" "$" fun "...$" rhs 0), [async |[lhs, fun, rhs]| {
|
.rule(
|
||||||
resolve(mactree!(("push" fun ; "push" lhs ;) "pushv" rhs ;)).await
|
mactreev!("...$" lhs 0 "std::fn::|>" "$" fun "...$" rhs 0),
|
||||||
}])
|
async |mut cx, [lhs, fun, rhs]| {
|
||||||
|
Ok(cx.recur(mactree!("push" fun ; "push" lhs ; "pushv" rhs)).await)
|
||||||
|
},
|
||||||
|
)
|
||||||
.finish()])
|
.finish()])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,17 @@
|
|||||||
use futures::StreamExt;
|
use orchid_extension::gen_expr::new_atom;
|
||||||
use orchid_base::sym;
|
|
||||||
use orchid_extension::gen_expr::{call, new_atom};
|
|
||||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||||
use orchid_extension::{Expr, TAtom, ToExpr, exec};
|
use orchid_extension::{Expr, TAtom, exec};
|
||||||
|
|
||||||
use crate::macros::match_macros::MatcherAtom;
|
use crate::macros::match_macros::{MatcherAtom, match_one};
|
||||||
use crate::macros::resolve::resolve;
|
|
||||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||||
use crate::{OrcOpt, Tpl};
|
use crate::{OrcOpt, Tpl};
|
||||||
|
|
||||||
pub async fn gen_option_macro_lib() -> Vec<GenMember> {
|
pub async fn gen_option_macro_lib() -> Vec<GenMember> {
|
||||||
prefix("std::option", [
|
prefix("std::option", [
|
||||||
fun(false, "is_some_body", |sub: TAtom<MatcherAtom>, val: OrcOpt<Expr>| {
|
fun(false, "is_some_body", |sub: Expr, val: OrcOpt<Expr>| {
|
||||||
exec(async move |mut h| {
|
exec(async move |mut h| {
|
||||||
let Some(sub_val) = val.0 else { return Ok(OrcOpt(None)) };
|
let Some(sub_val) = val.0 else { return Ok(OrcOpt(None)) };
|
||||||
sub.run_matcher(&mut h, sub_val).await
|
match_one(&mut h, sub, sub_val).await
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
fun(
|
fun(
|
||||||
@@ -25,25 +22,25 @@ pub async fn gen_option_macro_lib() -> Vec<GenMember> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
build_macro(None, ["some", "none"])
|
build_macro(None, ["some", "none"])
|
||||||
.rule(mactreev!(pattern::match_rule ( std::option::some "...$" sub_pattern 0)), [
|
.rule(
|
||||||
|[sub]: [_; _]| {
|
mactreev!("pattern::match_rule" ( "std::option::some" "...$" sub_pattern 0)),
|
||||||
exec(async move |mut h| {
|
async |mut cx, [sub]| {
|
||||||
let sub = h
|
let sub: TAtom<MatcherAtom> =
|
||||||
.exec::<TAtom<MatcherAtom>>(resolve(mactree!(pattern::match_rule "push" sub;)).await)
|
cx.recur_call(mactree!("pattern::match_rule" "push" sub;)).await?;
|
||||||
.await?;
|
let sub = sub.own().await;
|
||||||
Ok(new_atom(MatcherAtom {
|
let matcher = new_atom(MatcherAtom {
|
||||||
keys: sub.keys().collect().await,
|
keys: sub.keys,
|
||||||
matcher: call(sym!(std::option::is_some_body), sub).await.create().await,
|
matcher: mactree!("std::option::is_some_body" "push" sub.matcher),
|
||||||
}))
|
});
|
||||||
})
|
Ok(mactree!("Val" matcher))
|
||||||
},
|
},
|
||||||
])
|
)
|
||||||
.rule(mactreev!(pattern::match_rule(std::option::none)), [async |[]: [_; _]| {
|
.rule(mactreev!("pattern::match_rule"("std::option::none")), async |_cx, []| {
|
||||||
new_atom(MatcherAtom {
|
Ok(mactree!("Val" new_atom(MatcherAtom {
|
||||||
keys: vec![],
|
keys: vec![],
|
||||||
matcher: sym!(std::option::is_none_body).to_expr().await,
|
matcher: mactree!("std::option::is_none_body"),
|
||||||
})
|
})))
|
||||||
}])
|
})
|
||||||
.finish(),
|
.finish(),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,31 @@
|
|||||||
use orchid_base::sym;
|
|
||||||
use orchid_extension::TAtom;
|
use orchid_extension::TAtom;
|
||||||
use orchid_extension::ToExpr;
|
use orchid_extension::gen_expr::new_atom;
|
||||||
use orchid_extension::exec;
|
|
||||||
use orchid_extension::Expr;
|
|
||||||
use orchid_extension::gen_expr::{call, new_atom};
|
|
||||||
use orchid_extension::tree::{GenMember, prefix};
|
use orchid_extension::tree::{GenMember, prefix};
|
||||||
|
|
||||||
use crate::macros::resolve::resolve;
|
|
||||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
use crate::macros::utils::{build_macro, mactree, mactreev};
|
||||||
use crate::std::string::str_atom::IntStrAtom;
|
|
||||||
use crate::{HomoTpl, MacTree, Tpl};
|
use crate::{HomoTpl, MacTree, Tpl};
|
||||||
|
|
||||||
pub async fn gen_record_macro_lib() -> Vec<GenMember> {
|
pub async fn gen_record_macro_lib() -> Vec<GenMember> {
|
||||||
prefix("std::record", [build_macro(None, ["r", "_row"])
|
prefix("std::record", [build_macro(None, ["r", "_row"])
|
||||||
.rule(mactreev!(std::record::r[ "...$" elements 0 ]), [async |[elements]: [_; _]| {
|
.rule(mactreev!("std::record::r"[ "...$" elements 0 ]), async |mut cx, [elements]| {
|
||||||
exec(async move |mut h| {
|
let tup: HomoTpl<TAtom<MacTree>> =
|
||||||
let tup = h
|
cx.recur_call(mactree!(("macros::common::comma_list" "push" elements))).await?;
|
||||||
.exec::<HomoTpl<TAtom<MacTree>>>(call(
|
let mut record = mactree!("std::record::empty");
|
||||||
sym!(macros::resolve),
|
for item_exprh in tup.0 {
|
||||||
new_atom(mactree!((macros::common::comma_list "push" elements ;))),
|
let Tpl((key, value)): Tpl<(TAtom<MacTree>, TAtom<MacTree>)> =
|
||||||
))
|
cx.recur_call(mactree!("std::record::_row" "push" item_exprh.own().await)).await?;
|
||||||
.await?;
|
record = mactree!("std::record::set"
|
||||||
let mut record = sym!(std::record::empty).to_gen().await;
|
"push" record;
|
||||||
for item_exprh in tup.0 {
|
"push" key.own().await;
|
||||||
let Tpl((key, value)) = h
|
"push" value.own().await);
|
||||||
.exec::<Tpl<(TAtom<IntStrAtom>, Expr)>>(
|
}
|
||||||
resolve(mactree!(std::record::_row "push" item_exprh.own().await ;)).await,
|
Ok(record)
|
||||||
)
|
})
|
||||||
.await?;
|
.rule(
|
||||||
record = call(sym!(std::record::set), (record, key, value)).await;
|
mactreev!("std::record::_row" ( "$" name "...$" value 1 )),
|
||||||
}
|
async |mut cx, [name, value]| {
|
||||||
Ok(record)
|
Ok(mactree!("Val" Tpl((new_atom(cx.recur(name).await), new_atom(cx.recur(value).await)))))
|
||||||
})
|
},
|
||||||
.await
|
)
|
||||||
}])
|
|
||||||
.rule(mactreev!(std::record::_row ( "$" name "...$" value 1 )), [async |[name, value]| {
|
|
||||||
Ok(Tpl((resolve(name).await, resolve(value).await)))
|
|
||||||
}])
|
|
||||||
.finish()])
|
.finish()])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,102 +1,93 @@
|
|||||||
|
use async_fn_stream::stream;
|
||||||
use futures::{StreamExt, stream};
|
use futures::{StreamExt, stream};
|
||||||
use orchid_base::{OrcRes, sym};
|
use orchid_base::{OrcRes, sym};
|
||||||
use orchid_extension::TAtom;
|
|
||||||
use orchid_extension::ToExpr;
|
|
||||||
use orchid_extension::exec;
|
|
||||||
use orchid_extension::Expr;
|
|
||||||
use orchid_extension::gen_expr::{GExpr, call, new_atom};
|
use orchid_extension::gen_expr::{GExpr, call, new_atom};
|
||||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||||
|
use orchid_extension::{Expr, TAtom, ToExpr, exec};
|
||||||
|
|
||||||
use crate::macros::match_macros::MatcherAtom;
|
use crate::macros::match_macros::{MatcherAtom, match_one};
|
||||||
use crate::macros::utils::{build_macro, mactree, mactreev};
|
use crate::macros::utils::{RuleCtx, build_macro, mactree, mactreev};
|
||||||
use crate::{HomoTpl, MacTree, OrcOpt};
|
use crate::{HomoTpl, MacTree, OrcOpt};
|
||||||
|
|
||||||
pub async fn gen_tuple_macro_lib() -> Vec<GenMember> {
|
pub async fn gen_tuple_macro_lib() -> Vec<GenMember> {
|
||||||
prefix("std::tuple", [
|
prefix("std::tuple", [
|
||||||
build_macro(None, ["t"])
|
build_macro(None, ["t"])
|
||||||
.rule(mactreev!(std::tuple::t [ "...$" elements 0 ]), [|[elements]: [_; _]| {
|
.rule(mactreev!("std::tuple::t" [ "...$" elements 0 ]), async |mut cx, [elements]| {
|
||||||
exec(async move |mut h| {
|
let tup: HomoTpl<TAtom<MacTree>> =
|
||||||
let tup = h
|
cx.recur_call(mactree!("macros::common::comma_list" "push" elements)).await?;
|
||||||
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym!(macros::resolve),
|
let val = stream(async |mut h| {
|
||||||
new_atom(mactree!((macros::common::comma_list "push" elements ;))),
|
for item in &tup.0[..] {
|
||||||
))
|
h.emit(cx.recur(item.own().await).await).await
|
||||||
.await?;
|
}
|
||||||
let val = stream::iter(&tup.0[..])
|
})
|
||||||
.fold(sym!(std::tuple::empty).to_gen().await, async |head, new| {
|
.fold(mactree!("std::tuple::empty"), async |head, new| {
|
||||||
call(sym!(std::tuple::cat), (
|
mactree!(
|
||||||
head,
|
"std::tuple::cat"
|
||||||
call(sym!(std::tuple::one), call(
|
"push" head;
|
||||||
sym!(macros::resolve),
|
("std::tuple::one"
|
||||||
new.clone(),
|
"push" new)
|
||||||
)),
|
)
|
||||||
)).await
|
})
|
||||||
})
|
.await;
|
||||||
.await;
|
Ok(val)
|
||||||
Ok(val)
|
})
|
||||||
})
|
.rule(
|
||||||
}])
|
mactreev!("pattern::match_rule"(
|
||||||
.rule(
|
"std::tuple::t"[ "...$" elements 0 "macros::common::," "macros::common::.."]
|
||||||
mactreev!(pattern::match_rule(std::tuple::t[ "...$" elements 0 macros::common::, macros::common::..])),
|
)),
|
||||||
[async |[elements]: [_; _]| parse_tpl(elements, Some(mactree!(macros::common::_))).await],
|
async |cx, [elements]| parse_tpl(cx, elements, Some(mactree!("macros::common::_"))).await,
|
||||||
)
|
)
|
||||||
.rule(
|
.rule(
|
||||||
mactreev!(pattern::match_rule(
|
mactreev!("pattern::match_rule"(
|
||||||
std::tuple::t[ "...$" elements 1 macros::common::, macros::common::.. "...$" tail 0]
|
"std::tuple::t"[ "...$" elements 1 "macros::common::," "macros::common::.." "...$" tail 0]
|
||||||
)),
|
)),
|
||||||
[async |[elements, tail]: [_; _]| parse_tpl(elements, Some(tail)).await],
|
async |cx, [elements, tail]| parse_tpl(cx, elements, Some(tail)).await,
|
||||||
)
|
)
|
||||||
.rule(mactreev!(pattern::match_rule(std::tuple::t[ "...$" elements 0])), [
|
.rule(
|
||||||
|[elements]: [_; _]| parse_tpl(elements, None),
|
mactreev!("pattern::match_rule"("std::tuple::t"[ "...$" elements 0])),
|
||||||
])
|
async |cx, [elements]| parse_tpl(cx, elements, None).await,
|
||||||
.finish(),
|
)
|
||||||
fun(false, "matcher_body", tuple_matcher_body),
|
.finish(),
|
||||||
])
|
fun(false, "matcher_body", tuple_matcher_body),
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_tpl(elements: MacTree, tail_matcher: Option<MacTree>) -> impl Future<Output = GExpr> {
|
async fn parse_tpl(
|
||||||
exec(async move |mut h| -> OrcRes<GExpr> {
|
mut cx: RuleCtx<'_>,
|
||||||
let tup = h
|
elements: MacTree,
|
||||||
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym!(macros::resolve), new_atom(
|
tail_matcher: Option<MacTree>,
|
||||||
mactree!((macros::common::comma_list "push" elements ;)),
|
) -> OrcRes<MacTree> {
|
||||||
)))
|
let tup: HomoTpl<TAtom<MacTree>> =
|
||||||
.await?;
|
cx.recur_call(mactree!("macros::common::comma_list" "push" elements)).await?;
|
||||||
let mut subs = Vec::with_capacity(tup.0.len());
|
let mut keys = Vec::new();
|
||||||
for mac_a in &tup.0[..] {
|
let mut sub_matchers = mactree!("std::tuple::empty");
|
||||||
let sub = h
|
for mac_a in &tup.0[..] {
|
||||||
.exec::<TAtom<MatcherAtom>>(call(sym!(macros::resolve), new_atom(
|
let sub: TAtom<MatcherAtom> =
|
||||||
mactree!(pattern::match_rule ("push" mac_a.own().await ;)),
|
cx.recur_call(mactree!("pattern::match_rule" ("push" mac_a.own().await))).await?;
|
||||||
)))
|
let owned = sub.own().await;
|
||||||
.await?;
|
keys.extend(owned.keys);
|
||||||
subs.push(sub);
|
sub_matchers =
|
||||||
}
|
mactree!("std::tuple::cat" "push" sub_matchers; ("std::tuple::one" "push" owned.matcher));
|
||||||
let tail_matcher = match tail_matcher {
|
}
|
||||||
Some(mac) => Some(
|
let tail_matcher = match tail_matcher {
|
||||||
h.exec::<TAtom<MatcherAtom>>(call(sym!(macros::resolve), new_atom(
|
Some(mac) => {
|
||||||
mactree!(pattern::match_rule "push" mac ;),
|
let atm: TAtom<MatcherAtom> =
|
||||||
)))
|
cx.recur_call(mactree!("pattern::match_rule" "push" mac)).await?;
|
||||||
.await?,
|
let owned = atm.own().await;
|
||||||
),
|
keys.extend(owned.keys);
|
||||||
None => None,
|
mactree!("std::option::some" "push" owned.matcher)
|
||||||
};
|
},
|
||||||
Ok(new_atom(MatcherAtom {
|
None => mactree!("std::option::none"),
|
||||||
keys: stream::iter(&subs[..])
|
};
|
||||||
.flat_map(|t| t.keys())
|
Ok(mactree!("Val" new_atom(MatcherAtom {
|
||||||
.chain(stream::iter(&tail_matcher).flat_map(|mat| mat.keys()))
|
keys,
|
||||||
.collect()
|
matcher: mactree!("std::tuple::matcher_body" "push" sub_matchers; "push" tail_matcher),
|
||||||
.await,
|
})))
|
||||||
matcher: call(sym!(std::tuple::matcher_body), (
|
|
||||||
HomoTpl(subs),
|
|
||||||
OrcOpt(tail_matcher),
|
|
||||||
))
|
|
||||||
.to_expr()
|
|
||||||
.await,
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tuple_matcher_body(
|
fn tuple_matcher_body(
|
||||||
children: HomoTpl<TAtom<MatcherAtom>>,
|
children: HomoTpl<Expr>,
|
||||||
tail: OrcOpt<TAtom<MatcherAtom>>,
|
tail: OrcOpt<Expr>,
|
||||||
value: HomoTpl<Expr>,
|
value: HomoTpl<Expr>,
|
||||||
) -> impl Future<Output = GExpr> {
|
) -> impl Future<Output = GExpr> {
|
||||||
exec(async move |mut h| -> OrcRes<OrcOpt<HomoTpl<Expr>>> {
|
exec(async move |mut h| -> OrcRes<OrcOpt<HomoTpl<Expr>>> {
|
||||||
@@ -105,7 +96,7 @@ fn tuple_matcher_body(
|
|||||||
}
|
}
|
||||||
let mut binds = Vec::new();
|
let mut binds = Vec::new();
|
||||||
for (sub_mat, sub_val) in children.0.iter().zip(&value.0) {
|
for (sub_mat, sub_val) in children.0.iter().zip(&value.0) {
|
||||||
match sub_mat.run_matcher(&mut h, sub_val.clone()).await? {
|
match match_one(&mut h, sub_mat.clone(), sub_val.clone()).await? {
|
||||||
OrcOpt(None) => return Ok(OrcOpt(None)),
|
OrcOpt(None) => return Ok(OrcOpt(None)),
|
||||||
OrcOpt(Some(subres)) => binds.extend(subres.0),
|
OrcOpt(Some(subres)) => binds.extend(subres.0),
|
||||||
}
|
}
|
||||||
@@ -119,7 +110,7 @@ fn tuple_matcher_body(
|
|||||||
call(sym!(std::tuple::cat), (prefix, new.clone())).await
|
call(sym!(std::tuple::cat), (prefix, new.clone())).await
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
match tail_mat.run_matcher(&mut h, tail_tpl).await? {
|
match match_one(&mut h, tail_mat, tail_tpl).await? {
|
||||||
OrcOpt(Some(tail_binds)) => binds.extend(tail_binds.0),
|
OrcOpt(Some(tail_binds)) => binds.extend(tail_binds.0),
|
||||||
OrcOpt(None) => return Ok(OrcOpt(None)),
|
OrcOpt(None) => return Ok(OrcOpt(None)),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,18 @@ use futures::StreamExt;
|
|||||||
use futures::future::LocalBoxFuture;
|
use futures::future::LocalBoxFuture;
|
||||||
use itertools::{Itertools, chain};
|
use itertools::{Itertools, chain};
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_base::{NameLike, Sym, VPath, is};
|
use orchid_base::{NameLike, OrcRes, Sym, VPath, is};
|
||||||
use orchid_extension::ToExpr;
|
|
||||||
use orchid_extension::gen_expr::{GExpr, new_atom};
|
use orchid_extension::gen_expr::{GExpr, new_atom};
|
||||||
use orchid_extension::tree::{GenMember, MemKind, cnst, lazy};
|
use orchid_extension::tree::{GenMember, MemKind, lazy};
|
||||||
use orchid_extension::{Atomic, OwnedAtom, OwnedVariant, TAtom};
|
use orchid_extension::{
|
||||||
|
Atomic, ExecHandle, OwnedAtom, OwnedVariant, TAtom, ToExpr, TryFromExpr, exec,
|
||||||
|
};
|
||||||
|
use substack::Substack;
|
||||||
|
|
||||||
|
use crate::macros::lower::lower;
|
||||||
use crate::macros::macro_value::{Macro, MacroData, Rule};
|
use crate::macros::macro_value::{Macro, MacroData, Rule};
|
||||||
use crate::macros::mactree::MacTreeSeq;
|
use crate::macros::mactree::MacTreeSeq;
|
||||||
|
use crate::macros::resolve::resolve;
|
||||||
use crate::macros::rule::matcher::Matcher;
|
use crate::macros::rule::matcher::Matcher;
|
||||||
use crate::{MacTok, MacTree};
|
use crate::{MacTok, MacTree};
|
||||||
|
|
||||||
@@ -23,7 +27,7 @@ pub type Args = Vec<MacTree>;
|
|||||||
pub struct MacroBodyArgCollector {
|
pub struct MacroBodyArgCollector {
|
||||||
argc: usize,
|
argc: usize,
|
||||||
args: Args,
|
args: Args,
|
||||||
cb: Rc<dyn Fn(Args) -> LocalBoxFuture<'static, GExpr>>,
|
cb: Rc<dyn for<'a> Fn(RuleCtx<'a>, Args) -> LocalBoxFuture<'a, GExpr>>,
|
||||||
}
|
}
|
||||||
impl Atomic for MacroBodyArgCollector {
|
impl Atomic for MacroBodyArgCollector {
|
||||||
type Data = ();
|
type Data = ();
|
||||||
@@ -44,7 +48,11 @@ impl OwnedAtom for MacroBodyArgCollector {
|
|||||||
});
|
});
|
||||||
self.args.push(atom.own().await);
|
self.args.push(atom.own().await);
|
||||||
if self.argc == self.args.len() {
|
if self.argc == self.args.len() {
|
||||||
(self.cb)(self.args).await.to_gen().await
|
exec(async move |handle| {
|
||||||
|
let rule_ctx = RuleCtx { handle };
|
||||||
|
(self.cb)(rule_ctx, self.args).await
|
||||||
|
})
|
||||||
|
.await
|
||||||
} else {
|
} else {
|
||||||
new_atom(self)
|
new_atom(self)
|
||||||
}
|
}
|
||||||
@@ -53,6 +61,29 @@ impl OwnedAtom for MacroBodyArgCollector {
|
|||||||
|
|
||||||
fn body_name(name: &str, counter: usize) -> String { format!("({name})::{counter}") }
|
fn body_name(name: &str, counter: usize) -> String { format!("({name})::{counter}") }
|
||||||
|
|
||||||
|
pub struct RuleCtx<'a> {
|
||||||
|
handle: ExecHandle<'a>,
|
||||||
|
}
|
||||||
|
impl RuleCtx<'_> {
|
||||||
|
/// Recursively resolve a subexpression
|
||||||
|
pub async fn recur(&mut self, mt: MacTree) -> MacTree { resolve(&mut self.handle, mt).await }
|
||||||
|
/// Recursively resolve a value from a delegate macro which is expected to
|
||||||
|
/// match only keywords and return any subexpressions in a datastructure
|
||||||
|
///
|
||||||
|
/// If this is used with syntax that matches an expression macro, names bound
|
||||||
|
/// in the enclosing scope will not be correctly matched and the conversion at
|
||||||
|
/// the end will fail.
|
||||||
|
pub async fn recur_call<T: TryFromExpr>(&mut self, mt: MacTree) -> OrcRes<T> {
|
||||||
|
let resolved = self.recur(mt).await;
|
||||||
|
let lowered = lower(&resolved, Substack::Bottom).await;
|
||||||
|
self.exec(lowered).await
|
||||||
|
}
|
||||||
|
/// Normalize a value, run an expression to completion.
|
||||||
|
pub async fn exec<T: TryFromExpr>(&mut self, val: impl ToExpr) -> OrcRes<T> {
|
||||||
|
self.handle.exec(val).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn build_macro(
|
pub(crate) fn build_macro(
|
||||||
prio: Option<u64>,
|
prio: Option<u64>,
|
||||||
own_kws: impl IntoIterator<Item = &'static str>,
|
own_kws: impl IntoIterator<Item = &'static str>,
|
||||||
@@ -71,34 +102,33 @@ pub(crate) struct MacroBuilder {
|
|||||||
body_consts: Vec<GenMember>,
|
body_consts: Vec<GenMember>,
|
||||||
}
|
}
|
||||||
impl MacroBuilder {
|
impl MacroBuilder {
|
||||||
pub(crate) fn rule<const N: usize, R: ToExpr>(
|
pub(crate) fn rule<const N: usize>(
|
||||||
mut self,
|
mut self,
|
||||||
pat: MacTreeSeq,
|
pat: MacTreeSeq,
|
||||||
body: [impl AsyncFn([MacTree; N]) -> R + 'static; 1],
|
body: impl AsyncFn(RuleCtx, [MacTree; N]) -> OrcRes<MacTree> + 'static,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let [body] = body;
|
|
||||||
let body = Rc::new(body);
|
let body = Rc::new(body);
|
||||||
let name = &body_name(self.own_kws[0], self.body_consts.len());
|
let name = &body_name(self.own_kws[0], self.body_consts.len());
|
||||||
self.body_consts.extend(match N {
|
self.body_consts.extend(lazy(true, name, async |_| {
|
||||||
0 => lazy(true, name, async move |_| {
|
MemKind::Const(if N == 0 {
|
||||||
let argv = [].into_iter().collect_array().expect("N is 0");
|
exec(async move |handle| {
|
||||||
MemKind::Const(body(argv).await.to_gen().await)
|
let empty = std::iter::empty().collect_array::<N>().unwrap();
|
||||||
}),
|
Ok(new_atom(body(RuleCtx { handle }, empty).await?))
|
||||||
1.. => cnst(
|
})
|
||||||
true,
|
.await
|
||||||
name,
|
} else {
|
||||||
new_atom(MacroBodyArgCollector {
|
new_atom(MacroBodyArgCollector {
|
||||||
argc: N,
|
argc: N,
|
||||||
args: Vec::new(),
|
args: Vec::new(),
|
||||||
cb: Rc::new(move |argv| {
|
cb: Rc::new(move |rec, argv| {
|
||||||
let arr =
|
let arr =
|
||||||
argv.into_iter().collect_array::<N>().expect("argc should enforce the length");
|
argv.into_iter().collect_array::<N>().expect("argc should enforce the length");
|
||||||
let body = body.clone();
|
let body = body.clone();
|
||||||
Box::pin(async move { body(arr).await.to_gen().await })
|
Box::pin(async move { body(rec, arr).await.map(new_atom).to_gen().await })
|
||||||
}),
|
}),
|
||||||
}),
|
})
|
||||||
),
|
})
|
||||||
});
|
}));
|
||||||
self.patterns.push(pat);
|
self.patterns.push(pat);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -176,17 +206,26 @@ macro_rules! mactreev_impl {
|
|||||||
}).at(orchid_base::Pos::Inherit));
|
}).at(orchid_base::Pos::Inherit));
|
||||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||||
};
|
};
|
||||||
|
(@RECUR $ret:ident "Val" $arg:expr) => {
|
||||||
|
$crate::macros::utils::mactreev_impl!(@RECUR $ret "Val" $arg ;)
|
||||||
|
};
|
||||||
(@RECUR $ret:ident "Val" $arg:expr ; $($tail:tt)*) => {
|
(@RECUR $ret:ident "Val" $arg:expr ; $($tail:tt)*) => {
|
||||||
$ret.push(
|
$ret.push(
|
||||||
$crate::macros::mactree::MacTok::Value($arg)
|
$crate::macros::mactree::MacTok::Value(orchid_extension::ToExpr::to_expr($arg).await)
|
||||||
.at(orchid_base::Pos::Inherit)
|
.at(orchid_base::Pos::Inherit)
|
||||||
);
|
);
|
||||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||||
};
|
};
|
||||||
|
(@RECUR $ret:ident "push" $arg:expr) => {
|
||||||
|
$crate::macros::utils::mactreev_impl!(@RECUR $ret "push" $arg ;)
|
||||||
|
};
|
||||||
(@RECUR $ret:ident "push" $arg:expr ; $($tail:tt)*) => {
|
(@RECUR $ret:ident "push" $arg:expr ; $($tail:tt)*) => {
|
||||||
$ret.push($arg);
|
$ret.push($arg);
|
||||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||||
};
|
};
|
||||||
|
(@RECUR $ret:ident "pushv" $arg:expr) => {
|
||||||
|
$crate::macros::utils::mactreev_impl!(@RECUR $ret "pushv" $arg ;)
|
||||||
|
};
|
||||||
(@RECUR $ret:ident "pushv" $arg:expr ; $($tail:tt)*) => {
|
(@RECUR $ret:ident "pushv" $arg:expr ; $($tail:tt)*) => {
|
||||||
let $crate::macros::mactree::MacTok::S(_, body) = $arg.tok() else {
|
let $crate::macros::mactree::MacTok::S(_, body) = $arg.tok() else {
|
||||||
panic!("pushv used with non-vec value")
|
panic!("pushv used with non-vec value")
|
||||||
@@ -203,9 +242,15 @@ macro_rules! mactreev_impl {
|
|||||||
).at(orchid_base::Pos::Inherit));
|
).at(orchid_base::Pos::Inherit));
|
||||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||||
};
|
};
|
||||||
(@RECUR $ret:ident "l" $argh:tt $(:: $arg:tt)+ ($($body:tt)*) $($tail:tt)*) => {
|
(@RECUR $ret:ident "l" $arg:literal ($($body:tt)*) $($tail:tt)*) => {
|
||||||
|
assert!(
|
||||||
|
$arg.contains("::"),
|
||||||
|
"{} was treated as a name, but it doesn't have a namespace prefix",
|
||||||
|
$arg
|
||||||
|
);
|
||||||
|
let sym = orchid_base::Sym::parse($arg).await.expect("Empty string in sym literal in Rust");
|
||||||
$ret.push(MacTok::Lambda(
|
$ret.push(MacTok::Lambda(
|
||||||
MacTok::Name(sym!($argh $(:: $arg)+).await).at(orchid_base::Pos::Inherit),
|
MacTok::Name(sym).at(orchid_base::Pos::Inherit),
|
||||||
$crate::macros::utils::mactreev!($($body)*)
|
$crate::macros::utils::mactreev!($($body)*)
|
||||||
).at(orchid_base::Pos::Inherit));
|
).at(orchid_base::Pos::Inherit));
|
||||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||||
@@ -216,12 +261,9 @@ macro_rules! mactreev_impl {
|
|||||||
"{} was treated as a name, but it doesn't have a namespace prefix",
|
"{} was treated as a name, but it doesn't have a namespace prefix",
|
||||||
$name
|
$name
|
||||||
);
|
);
|
||||||
let sym = orchid_base::Sym::parse(
|
let sym = orchid_base::Sym::parse($name).await.expect("Empty string in sym literal in Rust");
|
||||||
$name
|
|
||||||
).await.expect("Empty string in sym literal in Rust");
|
|
||||||
$ret.push(
|
$ret.push(
|
||||||
$crate::macros::mactree::MacTok::Name(sym)
|
$crate::macros::mactree::MacTok::Name(sym).at(orchid_base::Pos::Inherit)
|
||||||
.at(orchid_base::Pos::Inherit)
|
|
||||||
);
|
);
|
||||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||||
};
|
};
|
||||||
@@ -255,20 +297,6 @@ macro_rules! mactreev_impl {
|
|||||||
);
|
);
|
||||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
||||||
};
|
};
|
||||||
(@RECUR $ret:ident $ns:ident :: $nhead:tt $($tail:tt)*) => {
|
|
||||||
$crate::macros::utils::mactreev_impl!(@NAME_MUNCHER $ret ($ns :: $nhead) $($tail)*)
|
|
||||||
};
|
|
||||||
(@NAME_MUNCHER $ret:ident ($($munched:tt)*) :: $name:tt $($tail:tt)*) => {
|
|
||||||
$crate::macros::utils::mactreev_impl!(@NAME_MUNCHER $ret ($($munched)* :: $name) $($tail)*)
|
|
||||||
};
|
|
||||||
(@NAME_MUNCHER $ret:ident ($($munched:tt)*) $($tail:tt)*) => {
|
|
||||||
let sym = orchid_base::sym!($($munched)*);
|
|
||||||
$ret.push(
|
|
||||||
$crate::macros::mactree::MacTok::Name(sym)
|
|
||||||
.at(orchid_base::Pos::Inherit)
|
|
||||||
);
|
|
||||||
$crate::macros::utils::mactreev_impl!(@RECUR $ret $($tail)*);
|
|
||||||
};
|
|
||||||
() => { Vec::new() };
|
() => { Vec::new() };
|
||||||
}
|
}
|
||||||
macro_rules! mactreev {
|
macro_rules! mactreev {
|
||||||
@@ -282,4 +310,6 @@ macro_rules! mactreev {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use {mactree, mactreev, mactreev_impl};
|
pub(crate) use mactree;
|
||||||
|
pub(crate) use mactreev;
|
||||||
|
pub(crate) use mactreev_impl;
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use futures::AsyncWrite;
|
use futures::AsyncWrite;
|
||||||
use orchid_api_traits::Encode;
|
use orchid_api_traits::Encode;
|
||||||
use orchid_extension::Atomic;
|
use orchid_extension::{Atomic, DeserializeCtx, OwnedAtom, OwnedVariant};
|
||||||
use orchid_extension::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BlobAtom(pub(crate) Rc<Vec<u8>>);
|
pub struct BlobAtom(pub(crate) Rc<Vec<u8>>);
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
use orchid_api_derive::Coding;
|
use orchid_api_derive::Coding;
|
||||||
use orchid_base::{FmtUnit, OrcRes, sym};
|
use orchid_base::{FmtUnit, OrcRes, sym};
|
||||||
use orchid_extension::{ToExpr, TryFromExpr};
|
|
||||||
use orchid_extension::Expr;
|
|
||||||
use orchid_extension::gen_expr::GExpr;
|
use orchid_extension::gen_expr::GExpr;
|
||||||
use orchid_extension::tree::{GenMember, cnst, comments, fun, prefix};
|
use orchid_extension::tree::{GenMember, cnst, comments, fun, prefix};
|
||||||
use orchid_extension::{Atomic, TAtom, ThinAtom, ThinVariant};
|
use orchid_extension::{Atomic, Expr, TAtom, ThinAtom, ThinVariant, ToExpr, TryFromExpr};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)]
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
pub struct Bool(pub bool);
|
pub struct Bool(pub bool);
|
||||||
|
|||||||
@@ -1,349 +0,0 @@
|
|||||||
use std::borrow::Cow;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::cmp::{Ordering, Reverse};
|
|
||||||
use std::collections::{BinaryHeap, VecDeque};
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::mem;
|
|
||||||
use std::num::NonZeroU64;
|
|
||||||
use std::pin::Pin;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::task::{Context, Poll, Waker};
|
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
use async_event::Event;
|
|
||||||
use chrono::TimeDelta;
|
|
||||||
use futures::channel::{mpsc, oneshot};
|
|
||||||
use futures::{FutureExt, select};
|
|
||||||
use hashbrown::HashMap;
|
|
||||||
use never::Never;
|
|
||||||
use orchid_api_derive::{Coding, Hierarchy};
|
|
||||||
use orchid_api_traits::Request;
|
|
||||||
use orchid_base::{FmtCtxImpl, OrcRes};
|
|
||||||
use orchid_extension::ToExpr;
|
|
||||||
use orchid_extension::entrypoint::spawn;
|
|
||||||
use orchid_extension::Expr;
|
|
||||||
use orchid_extension::gen_expr::{GExpr, IntoGExprStream, call, new_atom};
|
|
||||||
use orchid_extension::system::cted;
|
|
||||||
use orchid_extension::tree::{GenMember, cnst, comments, fun, prefix};
|
|
||||||
use orchid_extension::{
|
|
||||||
Atomic, ForeignAtom, OwnedAtom, OwnedVariant, err_not_callable, err_not_command,
|
|
||||||
};
|
|
||||||
use rust_decimal::prelude::Zero;
|
|
||||||
use tokio::task::{JoinHandle, spawn_local};
|
|
||||||
use tokio::time::sleep;
|
|
||||||
|
|
||||||
use crate::std::std_system::StdReq;
|
|
||||||
use crate::std::time::OrcDT;
|
|
||||||
use crate::{StdSystem, api};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Coding, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct AsyncTaskId(NonZeroU64);
|
|
||||||
|
|
||||||
/// Signals to the scheduler that some async work is in progress, and to take
|
|
||||||
/// ownership of this expression representing the progress of that work. This
|
|
||||||
/// doesn't have to be called before [FinishAsyncWork] if keeping the work and
|
|
||||||
/// thus the requesting system alive is not necessary
|
|
||||||
#[derive(Debug, Clone, Coding, Hierarchy)]
|
|
||||||
#[extends(FutureReq, StdReq)]
|
|
||||||
pub struct AddAsyncWork(pub api::ExprTicket);
|
|
||||||
impl Request for AddAsyncWork {
|
|
||||||
type Response = AsyncTaskId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Signals to the scheduler that some async work has been finished, and to
|
|
||||||
/// return this expression from a future `std::future::yield` call.
|
|
||||||
/// If [AddAsyncWork] was called before this, include the [AsyncTaskId] you
|
|
||||||
/// received to unlink the work from the scheduler so that cleanup is not
|
|
||||||
/// blocked.
|
|
||||||
#[derive(Debug, Clone, Coding, Hierarchy)]
|
|
||||||
#[extends(FutureReq, StdReq)]
|
|
||||||
pub struct FinishAsyncWork(pub Option<AsyncTaskId>, pub api::ExprTicket);
|
|
||||||
impl Request for FinishAsyncWork {
|
|
||||||
type Response = Result<(), SchedulerError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Coding)]
|
|
||||||
pub struct SchedulerError;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Coding, Hierarchy)]
|
|
||||||
#[extendable]
|
|
||||||
#[extends(StdReq)]
|
|
||||||
pub enum FutureReq {
|
|
||||||
AddAsyncWork(AddAsyncWork),
|
|
||||||
FinishAsyncWork(FinishAsyncWork),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct Timer {
|
|
||||||
set_at: Instant,
|
|
||||||
delay: TimeDelta,
|
|
||||||
repetition: Option<u64>,
|
|
||||||
cancelled: Rc<Event>,
|
|
||||||
action: Expr,
|
|
||||||
}
|
|
||||||
impl Timer {
|
|
||||||
pub fn next_occurrence(&self) -> Instant {
|
|
||||||
let delay_mult = i32::try_from(self.repetition.unwrap_or(0) + 1).unwrap();
|
|
||||||
self.set_at + (self.delay * delay_mult).to_std().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl PartialEq for Timer {
|
|
||||||
fn eq(&self, other: &Self) -> bool { self.next_occurrence().eq(&other.next_occurrence()) }
|
|
||||||
}
|
|
||||||
impl Eq for Timer {}
|
|
||||||
impl PartialOrd for Timer {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
|
||||||
}
|
|
||||||
impl Ord for Timer {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering { self.next_occurrence().cmp(&other.next_occurrence()) }
|
|
||||||
}
|
|
||||||
impl Atomic for Timer {
|
|
||||||
type Variant = OwnedVariant;
|
|
||||||
type Data = ();
|
|
||||||
}
|
|
||||||
impl OwnedAtom for Timer {
|
|
||||||
type Refs = Never;
|
|
||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
|
||||||
async fn command(mut self) -> CmdResult {
|
|
||||||
let sleep_until =
|
|
||||||
self.set_at + (self.delay * self.repetition.unwrap_or(1) as i32).to_std().unwrap();
|
|
||||||
let (timer_ready, on_timer_ready) = oneshot::channel();
|
|
||||||
let task = spawn(self.delay.to_std().unwrap(), async move { mem::drop(timer_ready.send(())) });
|
|
||||||
let res =
|
|
||||||
self.cancelled.wait_until_or_timeout(|| Some(()), on_timer_ready.map(mem::drop)).await;
|
|
||||||
task.abort();
|
|
||||||
// cancelled
|
|
||||||
if let Some(()) = res {
|
|
||||||
return Continuation::default().into();
|
|
||||||
}
|
|
||||||
// TODO: add binary API for sleep and
|
|
||||||
let mut ret = Continuation::default().into();
|
|
||||||
let mut ret = vec![self.action.to_gen().await];
|
|
||||||
if let Some(rep) = self.repetition.as_mut() {
|
|
||||||
*rep = *rep + 1;
|
|
||||||
ret.push(new_atom(self));
|
|
||||||
}
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SchedulerState {
|
|
||||||
/// Waker to call when async work finishes
|
|
||||||
finish_waker: Waker,
|
|
||||||
timer_task: Option<(Instant, JoinHandle<()>)>,
|
|
||||||
id: NonZeroU64,
|
|
||||||
background: HashMap<AsyncTaskId, Expr>,
|
|
||||||
ready: VecDeque<Expr>,
|
|
||||||
timers: BinaryHeap<Reverse<Timer>>,
|
|
||||||
}
|
|
||||||
impl SchedulerState {
|
|
||||||
fn activate_timers(&mut self, now: Instant) {
|
|
||||||
while let Some(t) = self.timers.peek()
|
|
||||||
&& t.0.next_occurrence() < now
|
|
||||||
{
|
|
||||||
let mut timer = self.timers.pop().unwrap().0;
|
|
||||||
let work = timer.action.clone();
|
|
||||||
self.ready.push_back(work);
|
|
||||||
if let Some(count) = timer.repetition {
|
|
||||||
timer.repetition = Some(count + 1);
|
|
||||||
self.timers.push(Reverse(timer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Debug for SchedulerState {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("SchedulerState").finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Default for SchedulerState {
|
|
||||||
fn default() -> Self {
|
|
||||||
SchedulerState {
|
|
||||||
background: HashMap::new(),
|
|
||||||
finish_waker: Waker::noop().clone(),
|
|
||||||
id: NonZeroU64::MIN,
|
|
||||||
timer_task: None,
|
|
||||||
ready: VecDeque::new(),
|
|
||||||
timers: BinaryHeap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
pub struct Scheduler(Rc<RefCell<SchedulerState>>);
|
|
||||||
impl Scheduler {
|
|
||||||
pub(crate) async fn add(&self, req: &AddAsyncWork) -> <AddAsyncWork as Request>::Response {
|
|
||||||
let expr = Expr::deserialize(req.0).await;
|
|
||||||
let mut this = self.0.borrow_mut();
|
|
||||||
let id = AsyncTaskId(this.id);
|
|
||||||
this.background.insert(id, expr);
|
|
||||||
this.id = this.id.checked_add(1).unwrap();
|
|
||||||
id
|
|
||||||
}
|
|
||||||
pub(crate) async fn finish(
|
|
||||||
&self,
|
|
||||||
req: &FinishAsyncWork,
|
|
||||||
) -> <FinishAsyncWork as Request>::Response {
|
|
||||||
let expr = Expr::deserialize(req.1).await;
|
|
||||||
let mut g = self.0.borrow_mut();
|
|
||||||
if let Some(id) = req.0 {
|
|
||||||
g.background.remove(&id);
|
|
||||||
}
|
|
||||||
g.ready.push_back(expr);
|
|
||||||
g.finish_waker.wake_by_ref();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct Yield;
|
|
||||||
impl Atomic for Yield {
|
|
||||||
type Variant = OwnedVariant;
|
|
||||||
type Data = ();
|
|
||||||
}
|
|
||||||
impl OwnedAtom for Yield {
|
|
||||||
type Refs = Never;
|
|
||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
|
||||||
async fn command(self) -> OrcRes<()> { Ok(()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct Spawn(ForeignAtom, ForeignAtom);
|
|
||||||
impl Atomic for Spawn {
|
|
||||||
type Variant = OwnedVariant;
|
|
||||||
type Data = [api::ExprTicket; 2];
|
|
||||||
}
|
|
||||||
impl OwnedAtom for Spawn {
|
|
||||||
type Refs = Never;
|
|
||||||
async fn val(&self) -> Cow<'_, Self::Data> {
|
|
||||||
Cow::Owned([self.0.clone().ex().handle().ticket(), self.1.clone().ex().handle().ticket()])
|
|
||||||
}
|
|
||||||
async fn command(self) -> OrcRes<impl IntoGExprStream> { Ok((self.1, self.0)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct Canceller {
|
|
||||||
cont: Option<Expr>,
|
|
||||||
cancel: Rc<RefCell<Option<oneshot::Sender<()>>>>,
|
|
||||||
}
|
|
||||||
impl Atomic for Canceller {
|
|
||||||
type Variant = OwnedVariant;
|
|
||||||
type Data = ();
|
|
||||||
}
|
|
||||||
impl OwnedAtom for Canceller {
|
|
||||||
type Refs = Never;
|
|
||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
|
||||||
async fn call_ref(&self, arg: Expr) -> impl ToExpr {
|
|
||||||
match &self.cont {
|
|
||||||
Some(_) => Err(err_not_callable(&self.print_atom(&FmtCtxImpl::default()).await).await),
|
|
||||||
None => Ok(new_atom(Self { cont: Some(arg), cancel: self.cancel.clone() })),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async fn command(self) -> OrcRes<impl IntoGExprStream> {
|
|
||||||
let Some(cont) = self.cont else {
|
|
||||||
return Err(err_not_command(&self.print_atom(&FmtCtxImpl::default()).await).await);
|
|
||||||
};
|
|
||||||
if let Some(canceller) = self.cancel.borrow_mut().take() {
|
|
||||||
canceller.send(());
|
|
||||||
}
|
|
||||||
Ok(cont)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct SetTimer {
|
|
||||||
delay: TimeDelta,
|
|
||||||
recurring: bool,
|
|
||||||
action: Expr,
|
|
||||||
cont: Expr,
|
|
||||||
}
|
|
||||||
impl Atomic for SetTimer {
|
|
||||||
type Variant = OwnedVariant;
|
|
||||||
type Data = ();
|
|
||||||
}
|
|
||||||
impl OwnedAtom for SetTimer {
|
|
||||||
type Refs = Never;
|
|
||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
|
||||||
async fn command(self) -> OrcRes<impl IntoGExprStream> {
|
|
||||||
let (send, recv) = oneshot::channel();
|
|
||||||
Ok((
|
|
||||||
new_atom(Timer {
|
|
||||||
set_at: Instant::now(),
|
|
||||||
delay: self.delay,
|
|
||||||
cancelled: Rc::new(recv),
|
|
||||||
repetition: self.recurring.then_some(1),
|
|
||||||
action: self.action,
|
|
||||||
}),
|
|
||||||
call(
|
|
||||||
self.cont,
|
|
||||||
new_atom(Canceller { cont: None, cancel: Rc::new(RefCell::new(Some(send))) }),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gen_future_lib() -> Vec<GenMember> {
|
|
||||||
prefix("std", [comments(
|
|
||||||
[
|
|
||||||
"This library exposes a futures executor, and tools for timing and cooperative multitasking. \
|
|
||||||
The use of these tools is only possible in a command trampoline, i.e. a caller that always \
|
|
||||||
defers to the command implementation of an atom.",
|
|
||||||
"Any command that correctly integrates with this library should return `std::future::yield` \
|
|
||||||
as its final value on all codepaths, which is the (re)entry point of the trampoline. \
|
|
||||||
Returning any other command, especially the ones in `std::exit_code` causes the program to \
|
|
||||||
immediately exit.",
|
|
||||||
"Cancellers take a continuation, stop whatever process they are associated with from \
|
|
||||||
proceeding, and call the continuation with information about the cancelled work.",
|
|
||||||
"|type canceller: \\T ((T -> cmd) -> cmd)|",
|
|
||||||
],
|
|
||||||
prefix("future", [
|
|
||||||
comments(
|
|
||||||
[
|
|
||||||
"A command without a continuation that defers control to the queued set of commands.",
|
|
||||||
"|type: cmd|",
|
|
||||||
],
|
|
||||||
cnst(true, "yield", new_atom(Yield)),
|
|
||||||
),
|
|
||||||
comments(
|
|
||||||
[
|
|
||||||
"Takes two commands and queues both to be executed one after the other.",
|
|
||||||
"|type: cmd -> cmd -> cmd|",
|
|
||||||
],
|
|
||||||
fun(true, "spawn", async |left: ForeignAtom, right: ForeignAtom| {
|
|
||||||
new_atom(Spawn(left, right))
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
comments(
|
|
||||||
[
|
|
||||||
"Takes a time amount to wait, the command to perform after waiting, and a continuation, \
|
|
||||||
and returns a command that sets a single-fire timeout. The continuation will be \
|
|
||||||
called with a canceller, which reports true if the task has not yet run.",
|
|
||||||
"|type: Duration -> cmd -> (canceller bool -> cmd) -> cmd|",
|
|
||||||
],
|
|
||||||
fun(true, "timeout", async |OrcDT(delay): OrcDT, action: Expr, cont: Expr| {
|
|
||||||
new_atom(SetTimer { delay, action, cont, recurring: false })
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
comments(
|
|
||||||
[
|
|
||||||
"Takes a time amount to wait between repetitions, the command to perform periodically, \
|
|
||||||
and a continuation, and returns a command. The continuation will be called with a \
|
|
||||||
canceller, which reports how many times the interval has run.",
|
|
||||||
"|type: Duration -> cmd -> (canceller Int -> cmd) -> cmd|",
|
|
||||||
],
|
|
||||||
fun(true, "interval", async |OrcDT(delay): OrcDT, action: Expr, cont: Expr| {
|
|
||||||
new_atom(SetTimer { delay, action, cont, recurring: true })
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
)])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_scheduler() -> Scheduler {
|
|
||||||
let cted = cted();
|
|
||||||
let std = cted.as_any().downcast_ref::<StdSystem>().unwrap();
|
|
||||||
let sched = std.sched.get_or_init(Scheduler::default);
|
|
||||||
sched.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AsyncTaskAtom {}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
// pub mod future_lib;
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
pub mod binary;
|
pub mod binary;
|
||||||
pub mod boolean;
|
pub mod boolean;
|
||||||
pub mod future;
|
|
||||||
pub mod number;
|
pub mod number;
|
||||||
pub mod ops;
|
pub mod ops;
|
||||||
pub mod option;
|
pub mod option;
|
||||||
|
|||||||
@@ -45,13 +45,9 @@ impl ToExpr for Int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Supports<ProtocolMethod> for Int {
|
impl Supports<ProtocolMethod> for Int {
|
||||||
async fn handle<'a>(
|
async fn handle(&self, hand: Box<dyn ReqHandle>, req: ProtocolMethod) -> io::Result<Receipt> {
|
||||||
&self,
|
|
||||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
|
||||||
req: ProtocolMethod,
|
|
||||||
) -> io::Result<Receipt<'a>> {
|
|
||||||
match req {
|
match req {
|
||||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, &sym!(std::number::Int).to_api()).await,
|
ProtocolMethod::GetTagId(req) => hand.reply(&req, sym!(std::number::Int).to_api()).await,
|
||||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||||
let name = Sym::from_api(key).await;
|
let name = Sym::from_api(key).await;
|
||||||
let val = if name == sym!(std::ops::add) {
|
let val = if name == sym!(std::ops::add) {
|
||||||
@@ -65,20 +61,16 @@ impl Supports<ProtocolMethod> for Int {
|
|||||||
} else if name == sym!(std::ops::mod) {
|
} else if name == sym!(std::ops::mod) {
|
||||||
sym!(std::number::imod)
|
sym!(std::number::imod)
|
||||||
} else {
|
} else {
|
||||||
return hand.reply(req, &None).await;
|
return hand.reply(req, None).await;
|
||||||
};
|
};
|
||||||
hand.reply(req, &Some(val.to_expr().await.serialize().await)).await
|
hand.reply(req, Some(val.to_expr().await.serialize().await)).await
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Supports<ToStringMethod> for Int {
|
impl Supports<ToStringMethod> for Int {
|
||||||
async fn handle<'a>(
|
async fn handle(&self, hand: Box<dyn ReqHandle>, req: ToStringMethod) -> io::Result<Receipt> {
|
||||||
&self,
|
hand.reply(&req, self.0.to_string()).await
|
||||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
|
||||||
req: ToStringMethod,
|
|
||||||
) -> io::Result<Receipt<'a>> {
|
|
||||||
hand.reply(&req, &self.0.to_string()).await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,13 +104,9 @@ impl ToExpr for Float {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Supports<ProtocolMethod> for Float {
|
impl Supports<ProtocolMethod> for Float {
|
||||||
async fn handle<'a>(
|
async fn handle(&self, hand: Box<dyn ReqHandle>, req: ProtocolMethod) -> io::Result<Receipt> {
|
||||||
&self,
|
|
||||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
|
||||||
req: ProtocolMethod,
|
|
||||||
) -> io::Result<Receipt<'a>> {
|
|
||||||
match req {
|
match req {
|
||||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, &sym!(std::number::Float).to_api()).await,
|
ProtocolMethod::GetTagId(req) => hand.reply(&req, sym!(std::number::Float).to_api()).await,
|
||||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||||
let name = Sym::from_api(key).await;
|
let name = Sym::from_api(key).await;
|
||||||
let val = if name == sym!(std::ops::add) {
|
let val = if name == sym!(std::ops::add) {
|
||||||
@@ -132,20 +120,16 @@ impl Supports<ProtocolMethod> for Float {
|
|||||||
} else if name == sym!(std::ops::mod) {
|
} else if name == sym!(std::ops::mod) {
|
||||||
sym!(std::number::fmod)
|
sym!(std::number::fmod)
|
||||||
} else {
|
} else {
|
||||||
return hand.reply(req, &None).await;
|
return hand.reply(req, None).await;
|
||||||
};
|
};
|
||||||
hand.reply(req, &Some(val.to_expr().await.serialize().await)).await
|
hand.reply(req, Some(val.to_expr().await.serialize().await)).await
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Supports<ToStringMethod> for Float {
|
impl Supports<ToStringMethod> for Float {
|
||||||
async fn handle<'a>(
|
async fn handle(&self, hand: Box<dyn ReqHandle>, req: ToStringMethod) -> io::Result<Receipt> {
|
||||||
&self,
|
hand.reply(&req, self.0.to_string()).await
|
||||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
|
||||||
req: ToStringMethod,
|
|
||||||
) -> io::Result<Receipt<'a>> {
|
|
||||||
hand.reply(&req, &self.0.to_string()).await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use orchid_base::{name_char, name_start};
|
use orchid_base::{OrcRes, is, name_char, name_start};
|
||||||
use orchid_base::{OrcRes, is};
|
|
||||||
use orchid_extension::gen_expr::new_atom;
|
use orchid_extension::gen_expr::new_atom;
|
||||||
use orchid_extension::{LexContext, LexedData, Lexer, err_not_applicable};
|
|
||||||
use orchid_extension::tree::GenTok;
|
use orchid_extension::tree::GenTok;
|
||||||
|
use orchid_extension::{LexContext, LexedData, Lexer, err_not_applicable};
|
||||||
|
|
||||||
use crate::std::string::str_atom::IntStrAtom;
|
use crate::std::string::str_atom::IntStrAtom;
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ use std::pin::Pin;
|
|||||||
use futures::AsyncWrite;
|
use futures::AsyncWrite;
|
||||||
use orchid_api_traits::Encode;
|
use orchid_api_traits::Encode;
|
||||||
use orchid_base::{is, mk_errv, sym};
|
use orchid_base::{is, mk_errv, sym};
|
||||||
use orchid_extension::{ToExpr, TryFromExpr};
|
|
||||||
use orchid_extension::{Expr, ExprHandle};
|
|
||||||
use orchid_extension::gen_expr::{call, new_atom};
|
use orchid_extension::gen_expr::{call, new_atom};
|
||||||
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
|
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
|
||||||
use orchid_extension::{Atomic, DeserializeCtx, ForeignAtom, OwnedAtom, OwnedVariant, TAtom};
|
use orchid_extension::{
|
||||||
|
Atomic, DeserializeCtx, Expr, ExprHandle, ForeignAtom, OwnedAtom, OwnedVariant, TAtom, ToExpr,
|
||||||
|
TryFromExpr,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{OrcString, api};
|
use crate::{OrcString, api};
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
use itertools::{Itertools, chain};
|
use itertools::{Itertools, chain};
|
||||||
use orchid_base::Sym;
|
|
||||||
use orchid_base::{
|
use orchid_base::{
|
||||||
Import, Parsed, Snippet, expect_tok, line_items, parse_multiname, token_errv,
|
IStr, Import, OrcRes, Paren, Parsed, Snippet, Sym, Token, expect_tok, is, line_items, mk_errv,
|
||||||
};
|
parse_multiname, token_errv,
|
||||||
use orchid_base::{Paren, Token};
|
|
||||||
use orchid_base::{IStr, OrcRes, is, mk_errv};
|
|
||||||
use orchid_extension::{
|
|
||||||
PTokTree, ParsCtx, ParsedLine, ParsedLineKind, p_tree2gen, p_v2gen,
|
|
||||||
};
|
};
|
||||||
|
use orchid_extension::{PTokTree, ParsCtx, ParsedLine, ParsedLineKind, p_tree2gen, p_v2gen};
|
||||||
|
|
||||||
pub async fn parse_impls(
|
pub async fn parse_impls(
|
||||||
_: &ParsCtx<'_>,
|
_: &ParsCtx<'_>,
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use orchid_base::{Comment, OrcRes, Parsed, Token, expect_end, is, mk_errv, sym, try_pop_no_fluff};
|
use orchid_base::{Comment, OrcRes, Parsed, Token, expect_end, is, mk_errv, sym, try_pop_no_fluff};
|
||||||
use orchid_extension::ToExpr;
|
|
||||||
use orchid_extension::gen_expr::{call, new_atom};
|
use orchid_extension::gen_expr::{call, new_atom};
|
||||||
use orchid_extension::{PSnippet, ParsCtx, ParsedLine, Parser};
|
use orchid_extension::{PSnippet, ParsCtx, ParsedLine, Parser, ToExpr};
|
||||||
|
|
||||||
use crate::std::protocol::parse_impls::parse_impls;
|
use crate::std::protocol::parse_impls::parse_impls;
|
||||||
use crate::std::protocol::types::Tag;
|
use crate::std::protocol::types::Tag;
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use orchid_base::{Comment, OrcRes, Parsed, Token, expect_end, is, mk_errv, sym, try_pop_no_fluff};
|
use orchid_base::{Comment, OrcRes, Parsed, Token, expect_end, is, mk_errv, sym, try_pop_no_fluff};
|
||||||
use orchid_extension::ToExpr;
|
|
||||||
use orchid_extension::gen_expr::{call, new_atom};
|
use orchid_extension::gen_expr::{call, new_atom};
|
||||||
use orchid_extension::{PSnippet, ParsCtx, ParsedLine, Parser};
|
use orchid_extension::{PSnippet, ParsCtx, ParsedLine, Parser, ToExpr};
|
||||||
|
|
||||||
use crate::std::protocol::parse_impls::parse_impls;
|
use crate::std::protocol::parse_impls::parse_impls;
|
||||||
use crate::std::protocol::types::Tag;
|
use crate::std::protocol::types::Tag;
|
||||||
|
|||||||
@@ -36,16 +36,16 @@ impl OwnedAtom for Tag {
|
|||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.id.to_api()) }
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.id.to_api()) }
|
||||||
}
|
}
|
||||||
impl Supports<ProtocolMethod> for Tag {
|
impl Supports<ProtocolMethod> for Tag {
|
||||||
async fn handle<'a>(
|
async fn handle(
|
||||||
&self,
|
&self,
|
||||||
hand: Box<dyn orchid_base::ReqHandle<'a> + '_>,
|
hand: Box<dyn orchid_base::ReqHandle>,
|
||||||
req: ProtocolMethod,
|
req: ProtocolMethod,
|
||||||
) -> std::io::Result<orchid_base::Receipt<'a>> {
|
) -> std::io::Result<orchid_base::Receipt> {
|
||||||
match req {
|
match req {
|
||||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, &self.id.to_api()).await,
|
ProtocolMethod::GetTagId(req) => hand.reply(&req, self.id.to_api()).await,
|
||||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) =>
|
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) =>
|
||||||
hand
|
hand
|
||||||
.reply(req, &self.impls.get(&Sym::from_api(key).await).map(|expr| expr.handle().ticket()))
|
.reply(req, self.impls.get(&Sym::from_api(key).await).map(|expr| expr.handle().ticket()))
|
||||||
.await,
|
.await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,11 +87,11 @@ impl OwnedAtom for Tagged {
|
|||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.tag.id.to_api()) }
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.tag.id.to_api()) }
|
||||||
}
|
}
|
||||||
impl Supports<ProtocolMethod> for Tagged {
|
impl Supports<ProtocolMethod> for Tagged {
|
||||||
async fn handle<'a>(
|
async fn handle(
|
||||||
&self,
|
&self,
|
||||||
hand: Box<dyn orchid_base::ReqHandle<'a> + '_>,
|
hand: Box<dyn orchid_base::ReqHandle>,
|
||||||
req: ProtocolMethod,
|
req: ProtocolMethod,
|
||||||
) -> io::Result<orchid_base::Receipt<'a>> {
|
) -> io::Result<orchid_base::Receipt> {
|
||||||
self.tag.handle(hand, req).await
|
self.tag.handle(hand, req).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,8 @@ use hashbrown::HashMap;
|
|||||||
use orchid_api_derive::Coding;
|
use orchid_api_derive::Coding;
|
||||||
use orchid_api_traits::{Encode, Request};
|
use orchid_api_traits::{Encode, Request};
|
||||||
use orchid_base::{IStr, Receipt, ReqHandle, ReqHandleExt, Sym, es, sym};
|
use orchid_base::{IStr, Receipt, ReqHandle, ReqHandleExt, Sym, es, sym};
|
||||||
use orchid_extension::ToExpr;
|
|
||||||
use orchid_extension::Expr;
|
|
||||||
use orchid_extension::{
|
use orchid_extension::{
|
||||||
Atomic, DeserializeCtx, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports,
|
Atomic, DeserializeCtx, Expr, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, ToExpr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
@@ -42,13 +40,9 @@ impl OwnedAtom for RecordAtom {
|
|||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
}
|
}
|
||||||
impl Supports<ProtocolMethod> for RecordAtom {
|
impl Supports<ProtocolMethod> for RecordAtom {
|
||||||
async fn handle<'a>(
|
async fn handle(&self, hand: Box<dyn ReqHandle>, req: ProtocolMethod) -> io::Result<Receipt> {
|
||||||
&self,
|
|
||||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
|
||||||
req: ProtocolMethod,
|
|
||||||
) -> io::Result<Receipt<'a>> {
|
|
||||||
match req {
|
match req {
|
||||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, &sym!(std::record::Record).to_api()).await,
|
ProtocolMethod::GetTagId(req) => hand.reply(&req, sym!(std::record::Record).to_api()).await,
|
||||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||||
let name = Sym::from_api(key).await;
|
let name = Sym::from_api(key).await;
|
||||||
let val = if name == sym!(std::ops::get) {
|
let val = if name == sym!(std::ops::get) {
|
||||||
@@ -56,9 +50,9 @@ impl Supports<ProtocolMethod> for RecordAtom {
|
|||||||
} else if name == sym!(std::ops::set) {
|
} else if name == sym!(std::ops::set) {
|
||||||
sym!(std::record::set)
|
sym!(std::record::set)
|
||||||
} else {
|
} else {
|
||||||
return hand.reply(req, &None).await;
|
return hand.reply(req, None).await;
|
||||||
};
|
};
|
||||||
return hand.reply(req, &Some(val.to_expr().await.serialize().await)).await;
|
return hand.reply(req, Some(val.to_expr().await.serialize().await)).await;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ use std::rc::Rc;
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchid_base::{is, mk_errv};
|
use orchid_base::{is, mk_errv};
|
||||||
use orchid_extension::TAtom;
|
use orchid_extension::gen_expr::new_atom;
|
||||||
use orchid_extension::Expr;
|
|
||||||
use orchid_extension::gen_expr::{arg, new_atom};
|
|
||||||
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
|
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
|
||||||
|
use orchid_extension::{Expr, TAtom, get_arg_posv};
|
||||||
|
|
||||||
use crate::std::record::record_atom::RecordAtom;
|
use crate::std::record::record_atom::RecordAtom;
|
||||||
use crate::std::string::str_atom::IntStrAtom;
|
use crate::std::string::str_atom::IntStrAtom;
|
||||||
@@ -26,7 +25,7 @@ pub fn gen_record_lib() -> Vec<GenMember> {
|
|||||||
None => Err(mk_errv(
|
None => Err(mk_errv(
|
||||||
is("Key not found in record").await,
|
is("Key not found in record").await,
|
||||||
format!("{} is not in this record, valid keys are {}", key.0, record.0.keys().join(", ")),
|
format!("{} is not in this record, valid keys are {}", key.0, record.0.keys().join(", ")),
|
||||||
[arg(0).pos.clone(), arg(1).pos.clone()],
|
get_arg_posv([0, 1]).await,
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ impl OwnedAtom for SymAtom {
|
|||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(SymAtomData(self.0.tok().to_api())) }
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(SymAtomData(self.0.tok().to_api())) }
|
||||||
}
|
}
|
||||||
impl Supports<ToStringMethod> for SymAtom {
|
impl Supports<ToStringMethod> for SymAtom {
|
||||||
async fn handle<'a>(
|
async fn handle(
|
||||||
&self,
|
&self,
|
||||||
hand: Box<dyn orchid_base::ReqHandle<'a> + '_>,
|
hand: Box<dyn orchid_base::ReqHandle>,
|
||||||
req: ToStringMethod,
|
req: ToStringMethod,
|
||||||
) -> std::io::Result<orchid_base::Receipt<'a>> {
|
) -> std::io::Result<orchid_base::Receipt> {
|
||||||
hand.reply(&req, &self.0.to_string()).await
|
hand.reply(&req, self.0.to_string()).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ use futures::future::join_all;
|
|||||||
use orchid_api_derive::{Coding, Hierarchy};
|
use orchid_api_derive::{Coding, Hierarchy};
|
||||||
use orchid_base::{Receipt, ReqHandle, ReqHandleExt, Sym, es, sym};
|
use orchid_base::{Receipt, ReqHandle, ReqHandleExt, Sym, es, sym};
|
||||||
use orchid_extension::gen_expr::new_atom;
|
use orchid_extension::gen_expr::new_atom;
|
||||||
use orchid_extension::ParserObj;
|
|
||||||
use orchid_extension::tree::{GenMember, merge_trivial};
|
use orchid_extension::tree::{GenMember, merge_trivial};
|
||||||
use orchid_extension::{
|
use orchid_extension::{
|
||||||
AtomOps, AtomicFeatures, Expr, LexerObj, ReqForSystem, System, SystemCard, SystemCtor, ToExpr,
|
AtomOps, AtomicFeatures, Expr, LexerObj, ParserObj, ReqForSystem, System, SystemCard, SystemCtor,
|
||||||
|
ToExpr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::number::num_lib::gen_num_lib;
|
use super::number::num_lib::gen_num_lib;
|
||||||
@@ -27,6 +27,7 @@ use crate::std::protocol::types::{CreateTag, Tag, Tagged, gen_protocol_lib};
|
|||||||
use crate::std::record::record_atom::{CreateRecord, RecordAtom};
|
use crate::std::record::record_atom::{CreateRecord, RecordAtom};
|
||||||
use crate::std::record::record_lib::gen_record_lib;
|
use crate::std::record::record_lib::gen_record_lib;
|
||||||
use crate::std::reflection::sym_atom::{CreateSymAtom, SymAtom, gen_sym_lib};
|
use crate::std::reflection::sym_atom::{CreateSymAtom, SymAtom, gen_sym_lib};
|
||||||
|
use crate::std::stream::stream_cmds::{ReadStreamCmd, WriteStreamCmd};
|
||||||
use crate::std::stream::stream_lib::gen_stream_lib;
|
use crate::std::stream::stream_lib::gen_stream_lib;
|
||||||
use crate::std::string::str_lexer::StringLexer;
|
use crate::std::string::str_lexer::StringLexer;
|
||||||
use crate::std::time::{CreateDT, gen_time_lib};
|
use crate::std::time::{CreateDT, gen_time_lib};
|
||||||
@@ -72,27 +73,25 @@ impl SystemCard for StdSystem {
|
|||||||
Some(TupleBuilder::ops()),
|
Some(TupleBuilder::ops()),
|
||||||
Some(Tag::ops()),
|
Some(Tag::ops()),
|
||||||
Some(Tagged::ops()),
|
Some(Tagged::ops()),
|
||||||
|
Some(ReadStreamCmd::ops()),
|
||||||
|
Some(WriteStreamCmd::ops()),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl System for StdSystem {
|
impl System for StdSystem {
|
||||||
type Ctor = Self;
|
type Ctor = Self;
|
||||||
async fn request<'a>(
|
async fn request(&self, xreq: Box<dyn ReqHandle>, req: ReqForSystem<Self>) -> Receipt {
|
||||||
&self,
|
|
||||||
xreq: Box<dyn ReqHandle<'a> + 'a>,
|
|
||||||
req: ReqForSystem<Self>,
|
|
||||||
) -> Receipt<'a> {
|
|
||||||
match req {
|
match req {
|
||||||
StdReq::CreateInt(ref req @ CreateInt(int)) =>
|
StdReq::CreateInt(ref req @ CreateInt(int)) =>
|
||||||
xreq.reply(req, &new_atom(int).to_expr().await.serialize().await).await.unwrap(),
|
xreq.reply(req, new_atom(int).to_expr().await.serialize().await).await.unwrap(),
|
||||||
StdReq::CreateFloat(ref req @ CreateFloat(float)) =>
|
StdReq::CreateFloat(ref req @ CreateFloat(float)) =>
|
||||||
xreq.reply(req, &new_atom(float).to_expr().await.serialize().await).await.unwrap(),
|
xreq.reply(req, new_atom(float).to_expr().await.serialize().await).await.unwrap(),
|
||||||
StdReq::CreateDT(ref req @ CreateDT(dt)) =>
|
StdReq::CreateDT(ref req @ CreateDT(dt)) =>
|
||||||
xreq.reply(req, &new_atom(dt).to_expr().await.serialize().await).await.unwrap(),
|
xreq.reply(req, new_atom(dt).to_expr().await.serialize().await).await.unwrap(),
|
||||||
StdReq::CreateTuple(ref req @ CreateTuple(ref items)) => {
|
StdReq::CreateTuple(ref req @ CreateTuple(ref items)) => {
|
||||||
let tpl = Tuple(Rc::new(join_all(items.iter().copied().map(Expr::deserialize)).await));
|
let tpl = Tuple(Rc::new(join_all(items.iter().copied().map(Expr::deserialize)).await));
|
||||||
let tk = new_atom(tpl).to_expr().await.serialize().await;
|
let tk = new_atom(tpl).to_expr().await.serialize().await;
|
||||||
xreq.reply(req, &tk).await.unwrap()
|
xreq.reply(req, tk).await.unwrap()
|
||||||
},
|
},
|
||||||
StdReq::CreateRecord(ref req @ CreateRecord(ref items)) => {
|
StdReq::CreateRecord(ref req @ CreateRecord(ref items)) => {
|
||||||
let values =
|
let values =
|
||||||
@@ -100,11 +99,11 @@ impl System for StdSystem {
|
|||||||
.await;
|
.await;
|
||||||
let rec = RecordAtom(Rc::new(values.into_iter().collect()));
|
let rec = RecordAtom(Rc::new(values.into_iter().collect()));
|
||||||
let tk = new_atom(rec).to_expr().await.serialize().await;
|
let tk = new_atom(rec).to_expr().await.serialize().await;
|
||||||
xreq.reply(req, &tk).await.unwrap()
|
xreq.reply(req, tk).await.unwrap()
|
||||||
},
|
},
|
||||||
StdReq::CreateSymAtom(ref req @ CreateSymAtom(sym_tok)) => {
|
StdReq::CreateSymAtom(ref req @ CreateSymAtom(sym_tok)) => {
|
||||||
let sym_atom = SymAtom(Sym::from_api(sym_tok).await);
|
let sym_atom = SymAtom(Sym::from_api(sym_tok).await);
|
||||||
xreq.reply(req, &new_atom(sym_atom).to_expr().await.serialize().await).await.unwrap()
|
xreq.reply(req, new_atom(sym_atom).to_expr().await.serialize().await).await.unwrap()
|
||||||
},
|
},
|
||||||
StdReq::CreateTag(ref req @ CreateTag { name, ref impls }) => {
|
StdReq::CreateTag(ref req @ CreateTag { name, ref impls }) => {
|
||||||
let tag_atom = Tag {
|
let tag_atom = Tag {
|
||||||
@@ -119,7 +118,7 @@ impl System for StdSystem {
|
|||||||
.collect(),
|
.collect(),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
xreq.reply(req, &new_atom(tag_atom).to_expr().await.serialize().await).await.unwrap()
|
xreq.reply(req, new_atom(tag_atom).to_expr().await.serialize().await).await.unwrap()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,15 @@ use std::io;
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use never::Never;
|
use never::Never;
|
||||||
use orchid_base::{ReqHandleExt, fmt, is, mk_errv};
|
use orchid_base::{Receipt, ReqHandle, ReqHandleExt, fmt, is, mk_errv};
|
||||||
use orchid_extension::gen_expr::{bot, call, new_atom};
|
use orchid_extension::gen_expr::{bot, call, new_atom, serialize};
|
||||||
use orchid_extension::std_reqs::{ReadLimit, ReadReq, RunCommand};
|
use orchid_extension::std_reqs::{CloseReq, FlushReq, ReadLimit, ReadReq, StartCommand, WriteReq};
|
||||||
use orchid_extension::{Atomic, Expr, ForeignAtom, OwnedAtom, OwnedVariant, Supports, ToExpr};
|
use orchid_extension::{
|
||||||
|
Atomic, Expr, ForeignAtom, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, ToExpr,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::std::binary::binary_atom::BlobAtom;
|
use crate::std::binary::binary_atom::BlobAtom;
|
||||||
|
use crate::std::string::str_atom::StrAtom;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ReadStreamCmd {
|
pub struct ReadStreamCmd {
|
||||||
@@ -16,40 +19,99 @@ pub struct ReadStreamCmd {
|
|||||||
pub limit: ReadLimit,
|
pub limit: ReadLimit,
|
||||||
pub succ: Expr,
|
pub succ: Expr,
|
||||||
pub fail: Expr,
|
pub fail: Expr,
|
||||||
|
pub as_str: bool,
|
||||||
}
|
}
|
||||||
impl Atomic for ReadStreamCmd {
|
impl Atomic for ReadStreamCmd {
|
||||||
type Variant = OwnedVariant;
|
type Variant = OwnedVariant;
|
||||||
type Data = ();
|
type Data = ();
|
||||||
|
fn reg_methods() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<StartCommand>() }
|
||||||
}
|
}
|
||||||
impl OwnedAtom for ReadStreamCmd {
|
impl OwnedAtom for ReadStreamCmd {
|
||||||
type Refs = Never;
|
type Refs = Never;
|
||||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
}
|
}
|
||||||
impl Supports<RunCommand> for ReadStreamCmd {
|
impl Supports<StartCommand> for ReadStreamCmd {
|
||||||
async fn handle<'a>(
|
async fn handle(&self, hand: Box<dyn ReqHandle>, req: StartCommand) -> io::Result<Receipt> {
|
||||||
&self,
|
let ret = 'ret: {
|
||||||
hand: Box<dyn orchid_base::ReqHandle<'a> + '_>,
|
let Some(read_res) = self.hand.call(ReadReq { limit: self.limit.clone() }).await else {
|
||||||
req: RunCommand,
|
break 'ret Err(mk_errv(
|
||||||
) -> io::Result<orchid_base::Receipt<'a>> {
|
is("Atom is not readable").await,
|
||||||
let ret = match self.hand.call(ReadReq(self.limit.clone())).await {
|
format!("Expected a readable stream handle, found {}", fmt(&self.hand).await),
|
||||||
None => Err(mk_errv(
|
[self.hand.pos()],
|
||||||
is("Atom is not readable").await,
|
));
|
||||||
format!("Expected a readable stream handle, found {}", fmt(&self.hand).await),
|
};
|
||||||
[self.hand.pos()],
|
let res = match read_res {
|
||||||
|
Err(e) => Err(mk_errv(
|
||||||
|
is(e.kind.message()).await,
|
||||||
|
format!("An error occurred while reading: {}", e.message),
|
||||||
|
[self.hand.pos(), self.succ.pos().await],
|
||||||
|
)),
|
||||||
|
Ok(v) if !self.as_str => Ok(new_atom(BlobAtom(Rc::new(v)))),
|
||||||
|
Ok(v) => match String::from_utf8(v) {
|
||||||
|
Ok(s) => Ok(new_atom(StrAtom(Rc::new(s)))),
|
||||||
|
Err(e) => Err(mk_errv(is("Invalid utf8 in input string").await, e.to_string(), [
|
||||||
|
self.hand.pos(),
|
||||||
|
self.succ.pos().await,
|
||||||
|
])),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(match res {
|
||||||
|
Err(e) => call(self.fail.clone(), bot(e)),
|
||||||
|
Ok(gex) => call(self.succ.clone(), gex),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
hand.reply(&req, Some(serialize(ret.to_gen().await).await)).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum WriteAction {
|
||||||
|
Write(Rc<Vec<u8>>),
|
||||||
|
Flush,
|
||||||
|
Close,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct WriteStreamCmd {
|
||||||
|
pub hand: ForeignAtom,
|
||||||
|
pub action: WriteAction,
|
||||||
|
pub succ: Expr,
|
||||||
|
pub fail: Expr,
|
||||||
|
}
|
||||||
|
impl Atomic for WriteStreamCmd {
|
||||||
|
type Variant = OwnedVariant;
|
||||||
|
type Data = ();
|
||||||
|
fn reg_methods() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<StartCommand>() }
|
||||||
|
}
|
||||||
|
impl OwnedAtom for WriteStreamCmd {
|
||||||
|
type Refs = Never;
|
||||||
|
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||||
|
}
|
||||||
|
impl Supports<StartCommand> for WriteStreamCmd {
|
||||||
|
async fn handle(&self, hand: Box<dyn ReqHandle>, req: StartCommand) -> io::Result<Receipt> {
|
||||||
|
let result = match &self.action {
|
||||||
|
WriteAction::Write(bin) => self.hand.call(WriteReq { data: bin.to_vec() }).await,
|
||||||
|
WriteAction::Flush => self.hand.call(FlushReq).await,
|
||||||
|
WriteAction::Close => self.hand.call(CloseReq).await,
|
||||||
|
};
|
||||||
|
let cont = match result {
|
||||||
|
None => bot(mk_errv(
|
||||||
|
is("Not a writer").await,
|
||||||
|
format!("{} cannot be written to", fmt(&self.hand).await),
|
||||||
|
[self.hand.pos(), self.succ.pos().await],
|
||||||
)),
|
)),
|
||||||
Some(Err(e)) => Ok(
|
Some(Err(e)) =>
|
||||||
call(
|
call(
|
||||||
self.fail.clone(),
|
self.fail.clone(),
|
||||||
bot(mk_errv(
|
bot(mk_errv(
|
||||||
is(e.kind.message()).await,
|
is(e.kind.message()).await,
|
||||||
format!("An error occurred while reading: {}", e.message),
|
format!("An error occurred while writing: {}", e.message),
|
||||||
[self.hand.pos(), self.succ.pos().await],
|
[self.hand.pos(), self.succ.pos().await],
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
),
|
Some(Ok(())) => self.succ.clone().to_gen().await,
|
||||||
Some(Ok(v)) => Ok(call(self.succ.clone(), new_atom(BlobAtom(Rc::new(v)))).await),
|
|
||||||
};
|
};
|
||||||
hand.reply(&req, &Some(ret.to_gen().await.serialize().await)).await
|
hand.reply(&req, Some(serialize(cont.to_gen().await).await)).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,26 @@
|
|||||||
use std::num::NonZero;
|
use std::num::NonZero;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
use orchid_base::{is, mk_errv};
|
use orchid_base::{is, mk_errv};
|
||||||
use orchid_extension::gen_expr::{call, new_atom};
|
use orchid_extension::gen_expr::{call, new_atom};
|
||||||
use orchid_extension::std_reqs::ReadLimit;
|
use orchid_extension::std_reqs::ReadLimit;
|
||||||
use orchid_extension::tree::{GenMember, comments, fun, prefix};
|
use orchid_extension::tree::{GenMember, comments, fun, prefix};
|
||||||
use orchid_extension::{Expr, ForeignAtom, get_arg};
|
use orchid_extension::{Expr, ForeignAtom, TAtom, get_arg};
|
||||||
|
|
||||||
use crate::Int;
|
|
||||||
use crate::std::binary::binary_atom::BlobAtom;
|
use crate::std::binary::binary_atom::BlobAtom;
|
||||||
use crate::std::stream::stream_cmds::ReadStreamCmd;
|
use crate::std::stream::stream_cmds::{ReadStreamCmd, WriteAction, WriteStreamCmd};
|
||||||
|
use crate::{Int, OrcString};
|
||||||
|
|
||||||
pub fn gen_stream_lib() -> Vec<GenMember> {
|
pub fn gen_stream_lib() -> Vec<GenMember> {
|
||||||
prefix("std", [comments(
|
prefix("std", [comments(
|
||||||
["Read from and write to byte streams"],
|
["Read from and write to byte streams"],
|
||||||
prefix("stream", [
|
prefix("stream", [
|
||||||
fun(true, "read_bin", async |hand: ForeignAtom, succ: Expr, fail: Expr| {
|
fun(true, "read_bin", async |hand: ForeignAtom, succ: Expr, fail: Expr| {
|
||||||
new_atom(ReadStreamCmd { hand, succ, fail, limit: ReadLimit::End })
|
new_atom(ReadStreamCmd { hand, succ, fail, as_str: false, limit: ReadLimit::End })
|
||||||
|
}),
|
||||||
|
fun(true, "read_str", async |hand: ForeignAtom, succ: Expr, fail: Expr| {
|
||||||
|
new_atom(ReadStreamCmd { hand, succ, fail, as_str: true, limit: ReadLimit::End })
|
||||||
}),
|
}),
|
||||||
fun(true, "read_until", async |hand: ForeignAtom, delim: Int, succ: Expr, fail: Expr| {
|
fun(true, "read_until", async |hand: ForeignAtom, delim: Int, succ: Expr, fail: Expr| {
|
||||||
let Ok(end) = delim.0.try_into() else {
|
let Ok(end) = delim.0.try_into() else {
|
||||||
@@ -29,12 +33,15 @@ pub fn gen_stream_lib() -> Vec<GenMember> {
|
|||||||
[get_arg(1).pos().await],
|
[get_arg(1).pos().await],
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
Ok(new_atom(ReadStreamCmd { hand, succ, fail, limit: ReadLimit::Delimiter(end) }))
|
let limit = ReadLimit::Delimiter(end);
|
||||||
|
Ok(new_atom(ReadStreamCmd { hand, succ, fail, as_str: false, limit }))
|
||||||
}),
|
}),
|
||||||
fun(true, "read_bytes", async |hand: ForeignAtom, count: Int, succ: Expr, fail: Expr| {
|
fun(true, "read_bytes", async |hand: ForeignAtom, count: Int, succ: Expr, fail: Expr| {
|
||||||
match count.0.try_into().map(NonZero::new) {
|
match count.0.try_into().map(NonZero::new) {
|
||||||
Ok(Some(nzlen)) =>
|
Ok(Some(nzlen)) => {
|
||||||
Ok(new_atom(ReadStreamCmd { hand, succ, fail, limit: ReadLimit::Length(nzlen) })),
|
let limit = ReadLimit::Length(nzlen);
|
||||||
|
Ok(new_atom(ReadStreamCmd { hand, succ, fail, as_str: false, limit }))
|
||||||
|
},
|
||||||
Ok(None) => Ok(call(succ, new_atom(BlobAtom(Rc::default()))).await),
|
Ok(None) => Ok(call(succ, new_atom(BlobAtom(Rc::default()))).await),
|
||||||
Err(_) => Err(mk_errv(
|
Err(_) => Err(mk_errv(
|
||||||
is("Length cannot be negative").await,
|
is("Length cannot be negative").await,
|
||||||
@@ -43,6 +50,28 @@ pub fn gen_stream_lib() -> Vec<GenMember> {
|
|||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
fun(true, "read_line", async |hand: ForeignAtom, succ: Expr, fail: Expr| {
|
||||||
|
const LIMIT_BR: ReadLimit = ReadLimit::Delimiter(b'\n');
|
||||||
|
new_atom(ReadStreamCmd { hand, succ, fail, as_str: true, limit: LIMIT_BR })
|
||||||
|
}),
|
||||||
|
fun(true, "write_str", async |hand: ForeignAtom, str: OrcString, succ: Expr, fail: Expr| {
|
||||||
|
let action = WriteAction::Write(Rc::new(str.get_string().await.bytes().collect_vec()));
|
||||||
|
new_atom(WriteStreamCmd { hand, action, succ, fail })
|
||||||
|
}),
|
||||||
|
fun(
|
||||||
|
true,
|
||||||
|
"write_bin",
|
||||||
|
async |hand: ForeignAtom, bin: TAtom<BlobAtom>, succ: Expr, fail: Expr| {
|
||||||
|
let action = WriteAction::Write(bin.own().await.0.clone());
|
||||||
|
new_atom(WriteStreamCmd { hand, action, succ, fail })
|
||||||
|
},
|
||||||
|
),
|
||||||
|
fun(true, "flush", async |hand: ForeignAtom, succ: Expr, fail: Expr| {
|
||||||
|
new_atom(WriteStreamCmd { hand, action: WriteAction::Flush, succ, fail })
|
||||||
|
}),
|
||||||
|
fun(true, "close", async |hand: ForeignAtom, succ: Expr, fail: Expr| {
|
||||||
|
new_atom(WriteStreamCmd { hand, action: WriteAction::Close, succ, fail })
|
||||||
|
}),
|
||||||
]),
|
]),
|
||||||
)])
|
)])
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user