consolidated scheduler API in pursuit of a deadlock
This commit is contained in:
1 parent
f1493ad35d
commit
490ca6d8b1
102 files changed
+1058
-706
No files matched your search
@@ -2,6 +2,7 @@
|
||||
xtask = "run --quiet --package xtask --"
|
||||
orcx = "xtask orcx --"
|
||||
orcxdb = "xtask orcxdb --"
|
||||
build-orcx = "xtask build-orcx --"
|
||||
|
||||
[env]
|
||||
CARGO_WORKSPACE_DIR = { value = "", relative = true }
|
||||
|
||||
Vendored
+36
-3
@@ -8,9 +8,42 @@
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug",
|
||||
"program": "${workspaceFolder}/<executable file>",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
"cargo": {
|
||||
"cwd": "${workspaceFolder}",
|
||||
"problemMatcher": "$rustc",
|
||||
"args": [
|
||||
"build-orcx",
|
||||
],
|
||||
"filter": {
|
||||
"kind": "bin",
|
||||
"name": "orcx"
|
||||
}
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"env": {
|
||||
"ORCHID_EXTENSIONS": "target/debug/orchid_std",
|
||||
"ORCHID_DEFAULT_SYSTEMS": "orchid::std;orchid::macros",
|
||||
"ORCHID_LOG_BUFFERS": "true"
|
||||
},
|
||||
"args": [
|
||||
"--proj=examples/hello-world",
|
||||
"--log=stderr",
|
||||
"--log=msg:stderr",
|
||||
"exec",
|
||||
"--main='src::main::main'"
|
||||
]
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"id": "pickOrcxProcess",
|
||||
"type": "command",
|
||||
"command": "lldb.pickProcess",
|
||||
"args": {
|
||||
// orcx appears literally in the cargo and xtask commands,
|
||||
// so we use a / to search for a path which should only match the executable itself
|
||||
"filter": "/orcx"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Whitespace-only changes.
Whitespace-only changes.
Generated
+74
-72
@@ -138,8 +138,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -209,8 +209,8 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -243,7 +243,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"quote 1.0.46",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
@@ -261,9 +261,9 @@ checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.2.2"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
|
||||
checksum = "b4ce8d3bd5823c7504d3f579f13e7b2f3da252fcb938c594d5680ee508bf846f"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
@@ -306,9 +306,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.44"
|
||||
version = "0.4.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
@@ -347,8 +347,8 @@ checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -570,8 +570,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -677,9 +677,9 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.17.0"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
@@ -738,7 +738,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"quote 1.0.46",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -761,9 +761,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
@@ -1037,8 +1037,8 @@ dependencies = [
|
||||
"itertools",
|
||||
"orchid-api-traits",
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1065,13 +1065,14 @@ dependencies = [
|
||||
name = "orchid-base"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-event",
|
||||
"async-fn-stream",
|
||||
"async-once-cell",
|
||||
"bound",
|
||||
"derive_destructure",
|
||||
"dyn-clone",
|
||||
"futures",
|
||||
"hashbrown 0.17.0",
|
||||
"hashbrown 0.17.1",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"never",
|
||||
@@ -1087,6 +1088,7 @@ dependencies = [
|
||||
"rust-embed",
|
||||
"substack",
|
||||
"task-local",
|
||||
"tokio",
|
||||
"trait-set",
|
||||
"unsync-pipe",
|
||||
]
|
||||
@@ -1103,7 +1105,7 @@ dependencies = [
|
||||
"dyn-clone",
|
||||
"futures",
|
||||
"futures-locks",
|
||||
"hashbrown 0.17.0",
|
||||
"hashbrown 0.17.1",
|
||||
"include_dir",
|
||||
"itertools",
|
||||
"konst",
|
||||
@@ -1138,7 +1140,7 @@ dependencies = [
|
||||
"derive_destructure",
|
||||
"futures",
|
||||
"futures-locks",
|
||||
"hashbrown 0.17.0",
|
||||
"hashbrown 0.17.1",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"libloading",
|
||||
@@ -1169,7 +1171,7 @@ dependencies = [
|
||||
"async-once-cell",
|
||||
"chrono",
|
||||
"futures",
|
||||
"hashbrown 0.17.0",
|
||||
"hashbrown 0.17.1",
|
||||
"itertools",
|
||||
"never",
|
||||
"once_cell",
|
||||
@@ -1243,9 +1245,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pastey"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5a797f0e07bdf071d15742978fc3128ec6c22891c31a3a931513263904c982a"
|
||||
checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
@@ -1269,7 +1271,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"syn 2.0.117",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1331,7 +1333,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"quote 1.0.46",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
@@ -1346,9 +1348,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.45"
|
||||
version = "1.0.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
||||
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
]
|
||||
@@ -1433,9 +1435,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.3"
|
||||
version = "1.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
|
||||
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -1456,9 +1458,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
|
||||
[[package]]
|
||||
name = "rend"
|
||||
@@ -1494,7 +1496,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"quote 1.0.46",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
@@ -1516,9 +1518,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fa2c8c9e8711e10f9c4fd2d64317ef13feaab820a4c51541f1a8c8e2e851ab2"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"quote 1.0.46",
|
||||
"rust-embed-utils",
|
||||
"syn 2.0.117",
|
||||
"syn 2.0.118",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@@ -1534,9 +1536,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust_decimal"
|
||||
version = "1.41.0"
|
||||
version = "1.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ce901f9a19d251159075a4c37af514c3b8ef99c22e02dd8c19161cf397ee94a"
|
||||
checksum = "be2a24f50780bc85f09cc6ac299bdf1424302742d77221106859c9d8b102126a"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"borsh",
|
||||
@@ -1620,8 +1622,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1772,18 +1774,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"quote 1.0.46",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.117"
|
||||
version = "2.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"quote 1.0.46",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
@@ -1823,8 +1825,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b68fbf53af4a8dfdf0bd9192ab2f6fd42e5b59454bcde58fe1382a8677087732"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
"wasm-bindgen-test",
|
||||
]
|
||||
|
||||
@@ -1844,8 +1846,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1874,9 +1876,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.52.1"
|
||||
version = "1.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6"
|
||||
checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
@@ -1896,8 +1898,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1962,8 +1964,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2012,7 +2014,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b79e2e9c9ab44c6d7c20d5976961b47e8f49ac199154daa514b77cd1ab536625"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"quote 1.0.46",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
@@ -2036,9 +2038,9 @@ checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.13.2"
|
||||
version = "1.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
|
||||
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
@@ -2156,7 +2158,7 @@ version = "0.2.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
|
||||
dependencies = [
|
||||
"quote 1.0.45",
|
||||
"quote 1.0.46",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
@@ -2168,8 +2170,8 @@ checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@@ -2210,8 +2212,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7150335716dce6028bead2b848e72f47b45e7b9422f64cccdc23bedca89affc1"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2330,8 +2332,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2341,8 +2343,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2553,7 +2555,7 @@ dependencies = [
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn 2.0.117",
|
||||
"syn 2.0.118",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
@@ -2568,8 +2570,8 @@ dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
@@ -2644,6 +2646,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.106",
|
||||
"quote 1.0.45",
|
||||
"syn 2.0.117",
|
||||
"quote 1.0.46",
|
||||
"syn 2.0.118",
|
||||
]
|
||||
@@ -1,4 +1,4 @@
|
||||
let main = macros::resolve '(
|
||||
let main = (
|
||||
stdio::get_stdout (ref stdout) =>
|
||||
std::stream::write_str stdout "Hello, World!\n" (result::ok _) =>
|
||||
std::stream::flush stdout (result::ok _) =>
|
||||
|
||||
@@ -9,8 +9,8 @@ proc-macro = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0.45"
|
||||
syn = { version = "2.0.117" }
|
||||
quote = "1.0.46"
|
||||
syn = { version = "2.0.118" }
|
||||
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||
proc-macro2 = "1.0.106"
|
||||
itertools = "0.14.0"
|
||||
itertools = "0.15.0"
|
||||
@@ -6,8 +6,8 @@ edition = "2024"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.44"
|
||||
chrono = "0.4.45"
|
||||
futures = { version = "0.3.32", features = ["std"], default-features = false }
|
||||
itertools = "0.14.0"
|
||||
itertools = "0.15.0"
|
||||
never = "0.1.0"
|
||||
ordered-float = "5.3.0"
|
||||
@@ -10,7 +10,7 @@ ordered-float = "5.3.0"
|
||||
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
|
||||
futures = { version = "0.3.32", features = ["std"], default-features = false }
|
||||
itertools = "0.14.0"
|
||||
itertools = "0.15.0"
|
||||
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -41,7 +41,7 @@ pub struct FutureContextBin {
|
||||
/// ABI-stable `Poll<()>`
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub enum UnitPoll {
|
||||
pub enum UnitPollBin {
|
||||
Pending,
|
||||
Ready,
|
||||
}
|
||||
@@ -57,7 +57,20 @@ pub struct FutureBin {
|
||||
/// `self`
|
||||
pub drop: extern "C" fn(*const ()),
|
||||
/// `&mut self` Equivalent to [Future::poll]
|
||||
pub poll: extern "C" fn(*const (), FutureContextBin) -> UnitPoll,
|
||||
pub poll: extern "C" fn(*const (), FutureContextBin) -> UnitPollBin,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct TaskHandleBin {
|
||||
pub data: *const (),
|
||||
/// `self` The task continues
|
||||
pub drop: extern "C" fn(*const ()),
|
||||
/// `self` The future is never polled again.
|
||||
pub abort: extern "C" fn(*const ()),
|
||||
/// `self` Returns a future that completes when the task completes or when
|
||||
/// it's aborted
|
||||
pub join: extern "C" fn(*const ()) -> FutureBin,
|
||||
}
|
||||
|
||||
/// Handle for a runtime that allows its holder to spawn futures across dynamic
|
||||
@@ -68,10 +81,12 @@ pub struct SpawnerBin {
|
||||
pub data: *const (),
|
||||
/// `self`
|
||||
pub drop: extern "C" fn(*const ()),
|
||||
/// `&self` Add a future to this extension's task after a configurable delay
|
||||
/// measured in milliseconds. By itself, a pending timer never prevents
|
||||
/// extension shutdown.
|
||||
pub spawn: extern "C" fn(*const (), u64, FutureBin),
|
||||
/// `&self` Add a future to this extension's task. If the bool is true, this
|
||||
/// future will keep the extension alive, and only be dropped on a forced
|
||||
/// shutdown.
|
||||
pub spawn: extern "C" fn(*const (), bool, FutureBin) -> TaskHandleBin,
|
||||
/// `&self` wait until the specified time has passed before waking this future
|
||||
pub sleep: extern "C" fn(*const (), u64) -> FutureBin,
|
||||
}
|
||||
|
||||
/// Extension context.
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
use core::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::num::NonZeroU64;
|
||||
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
@@ -36,7 +39,7 @@ impl Request for InternStr {
|
||||
#[extends(IntReq, ExtHostReq)]
|
||||
pub struct ExternStr(pub TStr);
|
||||
impl Request for ExternStr {
|
||||
type Response = String;
|
||||
type Response = Result<String, StaleTokenError>;
|
||||
}
|
||||
/// replica -> master to intern a vector of interned strings
|
||||
///
|
||||
@@ -59,7 +62,7 @@ impl Request for InternStrv {
|
||||
#[extends(IntReq, ExtHostReq)]
|
||||
pub struct ExternStrv(pub TStrv);
|
||||
impl Request for ExternStrv {
|
||||
type Response = Vec<TStr>;
|
||||
type Response = Result<Vec<TStr>, StaleTokenError>;
|
||||
}
|
||||
|
||||
/// A substitute for an interned string in serialized datastructures.
|
||||
@@ -89,3 +92,23 @@ pub struct Sweeped {
|
||||
pub strings: Vec<TStr>,
|
||||
pub vecs: Vec<TStrv>,
|
||||
}
|
||||
|
||||
pub trait InternerToken:
|
||||
Clone + Copy + Debug + Hash + Eq + PartialOrd + Ord + orchid_api_traits::Coding + 'static
|
||||
{
|
||||
}
|
||||
impl<T: Clone + Copy + Debug + Hash + Eq + PartialOrd + Ord + orchid_api_traits::Coding + 'static>
|
||||
InternerToken for T
|
||||
{
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, Coding)]
|
||||
pub struct StaleTokenError(String);
|
||||
impl StaleTokenError {
|
||||
pub fn new<T: InternerToken>(tok: T) -> Self { Self(format!("{tok:?}")) }
|
||||
}
|
||||
impl fmt::Display for StaleTokenError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "The interned data associated with {} has already been freed", self.0)
|
||||
}
|
||||
}
|
||||
@@ -70,7 +70,7 @@ pub struct ReflexiveRead {
|
||||
pub path: TStrv,
|
||||
}
|
||||
impl Request for ReflexiveRead {
|
||||
type Response = TokenTree;
|
||||
type Response = OrcResult<TokenTree>;
|
||||
}
|
||||
|
||||
/// Invalidates a [crate::ReflexiveTreeId] to reclaim the associated resources
|
||||
|
||||
@@ -8,5 +8,5 @@ futures = { version = "0.3.32", default-features = false, features = [
|
||||
"std",
|
||||
"async-await",
|
||||
] }
|
||||
itertools = "0.14.0"
|
||||
itertools = "0.15.0"
|
||||
task-local = "0.1.1"
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::Display;
|
||||
use std::pin::{Pin, pin};
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll, Waker};
|
||||
@@ -44,7 +45,13 @@ impl Future for Pollable {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct JoinError;
|
||||
impl Display for JoinError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "The task panicked or was cancelled")
|
||||
}
|
||||
}
|
||||
|
||||
/// An object that can be used to inspect the state of the task
|
||||
pub struct Handle<T: 'static> {
|
||||
|
||||
@@ -6,6 +6,7 @@ edition = "2024"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-event = "0.2.1"
|
||||
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
|
||||
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
||||
orchid-async-utils = { version = "0.1.0", path = "../orchid-async-utils" }
|
||||
@@ -17,8 +18,8 @@ futures = { version = "0.3.32", default-features = false, features = [
|
||||
"std",
|
||||
"async-await",
|
||||
] }
|
||||
hashbrown = "0.17.0"
|
||||
itertools = "0.14.0"
|
||||
hashbrown = "0.17.1"
|
||||
itertools = "0.15.0"
|
||||
lazy_static = "1.5.0"
|
||||
never = "0.1.0"
|
||||
num-traits = "0.2.19"
|
||||
@@ -26,11 +27,16 @@ orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
||||
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
|
||||
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||
ordered-float = "5.3.0"
|
||||
regex = "1.12.3"
|
||||
regex = "1.12.4"
|
||||
rust-embed = "8.11.0"
|
||||
substack = "1.1.1"
|
||||
trait-set = "0.3.0"
|
||||
task-local = "0.1.1"
|
||||
tokio = { version = "1.52.3", optional = true }
|
||||
|
||||
[features]
|
||||
tokio = ["dep:tokio"]
|
||||
default = ["tokio"]
|
||||
|
||||
[dev-dependencies]
|
||||
futures = "0.3.32"
|
||||
|
||||
+63
-11
@@ -1,11 +1,15 @@
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
use std::mem;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||
|
||||
use crate::api;
|
||||
use futures::FutureExt;
|
||||
use futures::future::LocalBoxFuture;
|
||||
use orchid_api::binary::TaskHandleBin;
|
||||
|
||||
type WideBox = Box<dyn Future<Output = ()>>;
|
||||
use crate::{DynTaskHandle, api};
|
||||
|
||||
static OWNED_VTABLE: RawWakerVTable = RawWakerVTable::new(
|
||||
|data| {
|
||||
@@ -56,13 +60,19 @@ static BORROWED_VTABLE: RawWakerVTable = RawWakerVTable::new(
|
||||
/// dynamic library boundaries
|
||||
#[must_use]
|
||||
pub fn future_to_vt<Fut: Future<Output = ()> + 'static>(fut: Fut) -> api::binary::FutureBin {
|
||||
let wide_box = Box::new(fut) as WideBox;
|
||||
let wide_box = Box::new(fut) as Box<dyn Future<Output = Fut::Output>>;
|
||||
let data = Box::into_raw(Box::new(wide_box));
|
||||
extern "C" fn drop(raw: *const ()) {
|
||||
mem::drop(unsafe { Box::<WideBox>::from_raw(raw as *mut _) })
|
||||
mem::drop(unsafe { Box::<Box<dyn Future<Output = ()>>>::from_raw(raw as *mut _) })
|
||||
}
|
||||
extern "C" fn poll(raw: *const (), cx: api::binary::FutureContextBin) -> api::binary::UnitPoll {
|
||||
let mut this = unsafe { Pin::new_unchecked(&mut **(raw as *mut WideBox).as_mut().unwrap()) };
|
||||
extern "C" fn poll(
|
||||
raw: *const (),
|
||||
cx: api::binary::FutureContextBin,
|
||||
) -> api::binary::UnitPollBin {
|
||||
let mut this = unsafe {
|
||||
let ptr = raw as *mut Box<dyn Future<Output = ()>>;
|
||||
Pin::new_unchecked(&mut **ptr.as_mut().unwrap())
|
||||
};
|
||||
loop {
|
||||
let mut go_around = false;
|
||||
let borrowed_waker = unsafe {
|
||||
@@ -73,11 +83,11 @@ pub fn future_to_vt<Fut: Future<Output = ()> + 'static>(fut: Fut) -> api::binary
|
||||
};
|
||||
let mut ctx = Context::from_waker(&borrowed_waker);
|
||||
let result = this.as_mut().poll(&mut ctx);
|
||||
if matches!(result, Poll::Ready(())) {
|
||||
break api::binary::UnitPoll::Ready;
|
||||
if let Poll::Ready(()) = result {
|
||||
break api::binary::UnitPollBin::Ready;
|
||||
}
|
||||
if !go_around {
|
||||
break api::binary::UnitPoll::Pending;
|
||||
break api::binary::UnitPollBin::Pending;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,8 +118,8 @@ impl Future for VirtualFuture {
|
||||
let cx = api::binary::FutureContextBin { data: cx as *mut Context as *const (), waker };
|
||||
let result = (self.vt.poll)(self.vt.data, cx);
|
||||
match result {
|
||||
api::binary::UnitPoll::Pending => Poll::Pending,
|
||||
api::binary::UnitPoll::Ready => Poll::Ready(()),
|
||||
api::binary::UnitPollBin::Pending => Poll::Pending,
|
||||
api::binary::UnitPollBin::Ready => Poll::Ready(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,3 +130,45 @@ impl Drop for VirtualFuture {
|
||||
/// Receive a future sent across dynamic library boundaries and convert it into
|
||||
/// an owned object
|
||||
pub fn vt_to_future(vt: api::binary::FutureBin) -> impl Future<Output = ()> { VirtualFuture { vt } }
|
||||
|
||||
pub struct VirtualTaskHandle {
|
||||
dropped: bool,
|
||||
bin: api::binary::TaskHandleBin,
|
||||
data: Rc<RefCell<Option<Box<dyn Any>>>>,
|
||||
}
|
||||
impl DynTaskHandle for VirtualTaskHandle {
|
||||
fn abort(mut self: Box<Self>) {
|
||||
(self.bin.abort)(self.bin.data);
|
||||
self.dropped = true;
|
||||
}
|
||||
fn join(mut self: Box<Self>) -> LocalBoxFuture<'static, Box<dyn Any>> {
|
||||
Box::pin(async move {
|
||||
self.dropped = true;
|
||||
vt_to_future((self.bin.join)(self.bin.data)).await;
|
||||
self.data.take().expect("Task's future settled but return value has not been initialized")
|
||||
})
|
||||
}
|
||||
}
|
||||
impl Drop for VirtualTaskHandle {
|
||||
fn drop(&mut self) {
|
||||
if !self.dropped {
|
||||
(self.bin.drop)(self.bin.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FutureOutboxHandle {
|
||||
data: Rc<RefCell<Option<Box<dyn Any>>>>,
|
||||
}
|
||||
impl FutureOutboxHandle {
|
||||
pub fn into_task_handle(self, bin: TaskHandleBin) -> VirtualTaskHandle {
|
||||
VirtualTaskHandle { dropped: false, bin, data: self.data }
|
||||
}
|
||||
}
|
||||
pub fn future_to_outbox<Fut: Future<Output = Box<dyn Any>>>(
|
||||
fut: Fut,
|
||||
) -> (impl Future<Output = ()>, FutureOutboxHandle) {
|
||||
let data = Rc::new(RefCell::new(None));
|
||||
let hand = FutureOutboxHandle { data: data.clone() };
|
||||
(fut.map(move |o| *data.borrow_mut() = Some(o)), hand)
|
||||
}
|
||||
@@ -79,7 +79,9 @@ impl OrcErr {
|
||||
}
|
||||
async fn from_api(api: api::OrcError) -> Self {
|
||||
Self {
|
||||
description: es(api.description).await,
|
||||
description: es(api.description)
|
||||
.await
|
||||
.expect("Error token arrived after description was freed"),
|
||||
message: api.message,
|
||||
positions: join_all(api.locations.into_iter().map(ErrPos::from_api)).await,
|
||||
}
|
||||
@@ -268,7 +270,7 @@ pub async fn os_str_to_string<I: Into<ErrPos>>(
|
||||
match str.to_str() {
|
||||
Some(str) => Ok(str),
|
||||
None => Err(mk_errv(
|
||||
is("Non-unicode string").await,
|
||||
is!("Non-unicode string"),
|
||||
format!("{str:?} is not representable as unicode"),
|
||||
posv,
|
||||
)),
|
||||
|
||||
@@ -35,7 +35,11 @@ impl<T> IdStore<T> {
|
||||
*rec = Rec::Val(value);
|
||||
id
|
||||
}
|
||||
pub fn add_with(&mut self, cb: impl FnOnce(usize) -> T) -> usize { self.add(cb(self.first)) }
|
||||
pub fn add_with<R>(&mut self, cb: impl FnOnce(usize) -> (T, R)) -> R {
|
||||
let (t, r) = cb(self.first);
|
||||
self.add(t);
|
||||
r
|
||||
}
|
||||
pub fn remove(&mut self, id: usize) -> T {
|
||||
let Some(rec) = self.values.get_mut(id) else { panic!("Index out of bounds") };
|
||||
let Rec::Val(val) = std::mem::replace(rec, Rec::Next(self.first)) else {
|
||||
|
||||
+16
-10
@@ -105,11 +105,11 @@ pub trait InternerSrv {
|
||||
/// Intern a string
|
||||
fn is<'a>(&'a self, v: &'a str) -> LocalBoxFuture<'a, IStr>;
|
||||
/// Find an existing string by its key
|
||||
fn es(&self, t: api::TStr) -> LocalBoxFuture<'_, IStr>;
|
||||
fn es(&self, t: api::TStr) -> LocalBoxFuture<'_, Result<IStr, api::StaleTokenError>>;
|
||||
/// Intern a str vector
|
||||
fn iv<'a>(&'a self, v: &'a [IStr]) -> LocalBoxFuture<'a, IStrv>;
|
||||
/// Find an existing str vector by its key
|
||||
fn ev(&self, t: api::TStrv) -> LocalBoxFuture<'_, IStrv>;
|
||||
fn ev(&self, t: api::TStrv) -> LocalBoxFuture<'_, Result<IStrv, api::StaleTokenError>>;
|
||||
}
|
||||
|
||||
task_local! {
|
||||
@@ -128,6 +128,12 @@ fn get_interner() -> Rc<dyn InternerSrv> {
|
||||
|
||||
/// Intern a `String` (find its ID or assign it a new one)
|
||||
pub async fn is(v: &str) -> IStr { get_interner().is(v).await }
|
||||
#[macro_export]
|
||||
macro_rules! is {
|
||||
($str:literal) => {
|
||||
$crate::tl_cache!(async $crate::IStr: $crate::is($str).await)
|
||||
};
|
||||
}
|
||||
/// Intern a `Vec<IStr>` (find its ID or assign it a new one)
|
||||
pub async fn iv(v: &[IStr]) -> IStrv { get_interner().iv(v).await }
|
||||
/// Find a live [IStr] by its ID
|
||||
@@ -136,7 +142,7 @@ pub async fn iv(v: &[IStr]) -> IStrv { get_interner().iv(v).await }
|
||||
///
|
||||
/// This function may panic if there are no other references to the [IStr] we're
|
||||
/// searching for, as the interner is free to forget about unreferenced values
|
||||
pub async fn es(v: api::TStr) -> IStr { get_interner().es(v).await }
|
||||
pub async fn es(v: api::TStr) -> Result<IStr, api::StaleTokenError> { get_interner().es(v).await }
|
||||
/// Find a live [IStrv] by its ID
|
||||
///
|
||||
/// # Panics
|
||||
@@ -144,7 +150,7 @@ pub async fn es(v: api::TStr) -> IStr { get_interner().es(v).await }
|
||||
/// This function may panic if there are no other references to the [IStrv]
|
||||
/// we're searching for, as the interner is free to forget about unreferenced
|
||||
/// values
|
||||
pub async fn ev(v: api::TStrv) -> IStrv { get_interner().ev(v).await }
|
||||
pub async fn ev(v: api::TStrv) -> Result<IStrv, api::StaleTokenError> { get_interner().ev(v).await }
|
||||
|
||||
/// Basic engine for an interner that supports recovering if a token is not
|
||||
/// found locally.
|
||||
@@ -160,7 +166,6 @@ pub mod local_interner {
|
||||
use futures::future::LocalBoxFuture;
|
||||
use hashbrown::hash_table::{Entry, OccupiedEntry, VacantEntry};
|
||||
use hashbrown::{DefaultHashBuilder, HashTable};
|
||||
use orchid_api_traits::Coding;
|
||||
|
||||
use super::{IStr, IStrHandle, IStrv, IStrvHandle, InternerSrv};
|
||||
use crate::api;
|
||||
@@ -169,7 +174,7 @@ pub mod local_interner {
|
||||
/// vector interning
|
||||
pub trait InternableCard: 'static + Sized + Default + Debug {
|
||||
/// API representation of an interner key
|
||||
type Token: Clone + Copy + Debug + Hash + Eq + PartialOrd + Ord + Coding + 'static;
|
||||
type Token: api::InternerToken;
|
||||
/// Owned version of interned value physically held by `'static` interner
|
||||
/// and token
|
||||
type Data: 'static + Borrow<Self::Borrow> + Eq + Hash + Debug;
|
||||
@@ -348,6 +353,7 @@ pub mod local_interner {
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn to_stale_token(&self) -> api::StaleTokenError { api::StaleTokenError::new(self.token) }
|
||||
}
|
||||
impl<B: InternableCard> Debug for ExternError<'_, B> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
@@ -405,8 +411,8 @@ pub mod local_interner {
|
||||
Err(e) => with_new_id(|id| Box::pin(future::ready(e.set_if_empty(api::TStr(id))))),
|
||||
}
|
||||
}
|
||||
fn es(&self, t: api::TStr) -> LocalBoxFuture<'_, IStr> {
|
||||
Box::pin(future::ready(self.str.e(t).expect("Unrecognized token cannot be externed")))
|
||||
fn es(&self, t: api::TStr) -> LocalBoxFuture<'_, Result<IStr, api::StaleTokenError>> {
|
||||
Box::pin(future::ready(self.str.e(t).map_err(|e| e.to_stale_token())))
|
||||
}
|
||||
fn iv<'a>(&'a self, v: &'a [IStr]) -> LocalBoxFuture<'a, IStrv> {
|
||||
match self.strv.i(v) {
|
||||
@@ -414,8 +420,8 @@ pub mod local_interner {
|
||||
Err(e) => with_new_id(|id| Box::pin(future::ready(e.set_if_empty(api::TStrv(id))))),
|
||||
}
|
||||
}
|
||||
fn ev(&self, t: orchid_api::TStrv) -> LocalBoxFuture<'_, IStrv> {
|
||||
Box::pin(future::ready(self.strv.e(t).expect("Unrecognized token cannot be externed")))
|
||||
fn ev(&self, t: api::TStrv) -> LocalBoxFuture<'_, Result<IStrv, api::StaleTokenError>> {
|
||||
Box::pin(future::ready(self.strv.e(t).map_err(|e| e.to_stale_token())))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,12 @@ impl<'a, I: Iterator<Item = E> + Clone, E: fmt::Display> fmt::Display for PrintL
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for (pos, item) in self.0.clone().with_position() {
|
||||
match pos {
|
||||
Position::First | Position::Only => write!(f, "{item}")?,
|
||||
Position::Middle => write!(f, ", {item}")?,
|
||||
Position::Last => write!(f, ", {} {item}", self.1)?,
|
||||
// first or only item
|
||||
Position { is_first: true, .. } => write!(f, "{item}")?,
|
||||
// last but not only item
|
||||
Position { is_last: true, .. } => write!(f, ", {} {item}", self.1)?,
|
||||
// middle item
|
||||
_ => write!(f, ", {item}")?,
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
pub use async_once_cell;
|
||||
use orchid_api as api;
|
||||
|
||||
mod spawn;
|
||||
pub use spawn::*;
|
||||
mod on_drop;
|
||||
pub use on_drop::*;
|
||||
mod binary;
|
||||
|
||||
@@ -157,7 +157,10 @@ impl SrcRange {
|
||||
pub fn suffix(&self) -> Self { Self::zw(self.path(), self.end()) }
|
||||
/// Deserialize from a message
|
||||
pub async fn from_api(api: &api::SourceRange) -> Self {
|
||||
Self { path: Sym::from_api(api.path).await, range: api.range.clone() }
|
||||
Self {
|
||||
path: Sym::from_api(api.path).await.expect("Source path should be statically interned"),
|
||||
range: api.range.clone(),
|
||||
}
|
||||
}
|
||||
/// Serialize to a message
|
||||
pub fn to_api(&self) -> api::SourceRange {
|
||||
@@ -192,7 +195,7 @@ pub struct CodeGenInfo {
|
||||
}
|
||||
impl CodeGenInfo {
|
||||
/// A codegen marker with no user message and parameters
|
||||
pub async fn new_short(generator: Sym) -> Self { Self { generator, details: is("").await } }
|
||||
pub async fn new_short(generator: Sym) -> Self { Self { generator, details: is!("") } }
|
||||
/// A codegen marker with a user message or parameters
|
||||
pub async fn new_details(generator: Sym, details: impl AsRef<str>) -> Self {
|
||||
Self { generator, details: is(details.as_ref()).await }
|
||||
@@ -201,7 +204,12 @@ impl CodeGenInfo {
|
||||
pub fn pos(&self) -> Pos { Pos::Gen(self.clone()) }
|
||||
/// Deserialize from a message
|
||||
pub async fn from_api(api: &api::CodeGenInfo) -> Self {
|
||||
Self { generator: Sym::from_api(api.generator).await, details: es(api.details).await }
|
||||
Self {
|
||||
generator: Sym::from_api(api.generator)
|
||||
.await
|
||||
.expect("Generator path should be statically interned"),
|
||||
details: es(api.details).await.expect("Generator label should be statically interned"),
|
||||
}
|
||||
}
|
||||
/// Serialize to a message
|
||||
pub fn to_api(&self) -> api::CodeGenInfo {
|
||||
|
||||
+17
-18
@@ -117,7 +117,12 @@ impl VName {
|
||||
if data.is_empty() { Err(EmptyNameError) } else { Ok(Self(data)) }
|
||||
}
|
||||
pub async fn deintern(name: impl IntoIterator<Item = api::TStr>) -> Result<Self, EmptyNameError> {
|
||||
Self::new(join_all(name.into_iter().map(es)).await)
|
||||
Self::new(
|
||||
join_all(
|
||||
name.into_iter().map(|n| async move { es(n).await.expect("Name segment is stale") }),
|
||||
)
|
||||
.await,
|
||||
)
|
||||
}
|
||||
/// Unwrap the enclosed vector
|
||||
pub fn into_vec(self) -> Vec<IStr> { self.0 }
|
||||
@@ -212,8 +217,8 @@ impl Sym {
|
||||
/// Extern the sym for editing
|
||||
pub fn to_vname(&self) -> VName { VName(self[..].to_vec()) }
|
||||
/// Decode from a message
|
||||
pub async fn from_api(marker: api::TStrv) -> Sym {
|
||||
Self::from_tok(ev(marker).await).expect("Empty sequence found for serialized Sym")
|
||||
pub async fn from_api(marker: api::TStrv) -> Result<Sym, api::StaleTokenError> {
|
||||
Ok(Self::from_tok(ev(marker).await?).expect("Empty sequence found for serialized Sym"))
|
||||
}
|
||||
/// Encode into a message
|
||||
pub fn to_api(&self) -> api::TStrv { self.tok().to_api() }
|
||||
@@ -295,11 +300,11 @@ macro_rules! sym {
|
||||
($seg1:tt $( :: $seg:tt)*) => {
|
||||
$crate::tl_cache!(async $crate::Sym : {
|
||||
$crate::Sym::from_tok(
|
||||
$crate::iv(&[
|
||||
$crate::is($crate::sym!(@SEG $seg1)).await
|
||||
$( , $crate::is($crate::sym!(@SEG $seg)).await )*
|
||||
])
|
||||
.await
|
||||
$crate::iv(&[
|
||||
$crate::is($crate::sym!(@SEG $seg1)).await
|
||||
$( , $crate::is($crate::sym!(@SEG $seg)).await )*
|
||||
])
|
||||
.await
|
||||
).unwrap()
|
||||
})
|
||||
};
|
||||
@@ -369,20 +374,14 @@ mod test {
|
||||
spin_on(with_interner(local_interner(), async {
|
||||
assert_eq!(
|
||||
sym!(foo::bar::baz),
|
||||
Sym::new([is("foo").await, is("bar").await, is("baz").await]).await.unwrap()
|
||||
Sym::new([is!("foo"), is!("bar"), is!("baz")]).await.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
sym!(foo::bar::[|>]),
|
||||
Sym::new([is("foo").await, is("bar").await, is("|>").await]).await.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
vname!(foo::bar::baz),
|
||||
VName::new([is("foo").await, is("bar").await, is("baz").await]).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
{ vpath!(foo::bar::baz) },
|
||||
VPath::new([is("foo").await, is("bar").await, is("baz").await])
|
||||
Sym::new([is!("foo"), is!("bar"), is!("|>")]).await.unwrap()
|
||||
);
|
||||
assert_eq!(vname!(foo::bar::baz), VName::new([is!("foo"), is!("bar"), is!("baz")]).unwrap());
|
||||
assert_eq!({ vpath!(foo::bar::baz) }, VPath::new([is!("foo"), is!("bar"), is!("baz")]));
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ pub struct NumError {
|
||||
|
||||
pub async fn num_to_errv(NumError { kind, range }: NumError, offset: u32, source: &Sym) -> OrcErrv {
|
||||
mk_errv(
|
||||
is("Failed to parse number").await,
|
||||
is!("Failed to parse number"),
|
||||
match kind {
|
||||
NumErrorKind::NaN => "NaN emerged during parsing",
|
||||
NumErrorKind::InvalidDigit => "non-digit character encountered",
|
||||
|
||||
+15
-14
@@ -123,7 +123,10 @@ pub struct Comment {
|
||||
}
|
||||
impl Comment {
|
||||
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.expect("Comment text should be statically interned"),
|
||||
sr: SrcRange::new(c.range.clone(), &src),
|
||||
}
|
||||
}
|
||||
pub async fn from_tk(tk: &TokTree<impl ManagedStrat>) -> Option<Self> {
|
||||
match &tk.tok {
|
||||
@@ -182,21 +185,19 @@ pub async fn try_pop_no_fluff<'a, M: ManagedStrat>(
|
||||
) -> ParseRes<'a, &'a TokTree<M>, M> {
|
||||
match snip.skip_fluff().split_first() {
|
||||
Some((output, tail)) => Ok(Parsed { output, tail }),
|
||||
None =>
|
||||
Err(mk_errv(is("Unexpected end").await, "Line ends abruptly; more tokens were expected", [
|
||||
snip.sr(),
|
||||
])),
|
||||
None => Err(mk_errv(is!("Unexpected end"), "Line ends abruptly; more tokens were expected", [
|
||||
snip.sr(),
|
||||
])),
|
||||
}
|
||||
}
|
||||
|
||||
/// Fail if the snippet isn't empty
|
||||
pub async fn expect_end(snip: Snippet<'_, impl ManagedStrat>) -> OrcRes<()> {
|
||||
match snip.skip_fluff().get(0) {
|
||||
Some(surplus) => Err(mk_errv(
|
||||
is("Extra code after end of line").await,
|
||||
"Code found after the end of the line",
|
||||
[surplus.sr.pos()],
|
||||
)),
|
||||
Some(surplus) =>
|
||||
Err(mk_errv(is!("Extra code after end of line"), "Code found after the end of the line", [
|
||||
surplus.sr.pos(),
|
||||
])),
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -210,7 +211,7 @@ pub async fn expect_tok<'a, M: ManagedStrat>(
|
||||
match &head.tok {
|
||||
Token::Name(n) if *n == tok => Ok(Parsed { output: (), tail }),
|
||||
t => Err(mk_errv(
|
||||
is("Expected specific keyword").await,
|
||||
is!("Expected specific keyword"),
|
||||
format!("Expected {tok} but found {:?}", fmt(t).await),
|
||||
[head.sr()],
|
||||
)),
|
||||
@@ -243,7 +244,7 @@ pub async fn parse_multiname<'a, M: ManagedStrat>(
|
||||
) -> ParseRes<'a, Vec<Import>, M> {
|
||||
let Some((tt, tail)) = tail.skip_fluff().split_first() else {
|
||||
return Err(mk_errv(
|
||||
is("Expected token").await,
|
||||
is!("Expected token"),
|
||||
"Expected a name, a parenthesized list of names, or a globstar.",
|
||||
[tail.sr().pos()],
|
||||
));
|
||||
@@ -257,7 +258,7 @@ pub async fn parse_multiname<'a, M: ManagedStrat>(
|
||||
match &tt.tok {
|
||||
Token::NS(ns, body) => {
|
||||
if !ns.starts_with(name_start) {
|
||||
report(mk_errv(is("Unexpected name prefix").await, "Only names can precede ::", [ttpos]))
|
||||
report(mk_errv(is!("Unexpected name prefix"), "Only names can precede ::", [ttpos]))
|
||||
};
|
||||
let out = Box::pin(rec(body)).await?;
|
||||
Ok(out.into_iter().update(|i| i.0.push(ns.clone())).collect_vec())
|
||||
@@ -280,7 +281,7 @@ pub async fn parse_multiname<'a, M: ManagedStrat>(
|
||||
Ok(o)
|
||||
},
|
||||
t => Err(mk_errv(
|
||||
is("Unrecognized name end").await,
|
||||
is!("Unrecognized name end"),
|
||||
format!("Names cannot end with {:?} tokens", fmt(t).await),
|
||||
[ttpos],
|
||||
)),
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
use std::any::Any;
|
||||
#[cfg(feature = "tokio")]
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
use async_event::Event;
|
||||
use futures::future::LocalBoxFuture;
|
||||
use task_local::task_local;
|
||||
|
||||
use crate::on_drop;
|
||||
|
||||
/// Handle for a task that gets polled independently from the codepath that
|
||||
/// spawned it. Wrapper for a [DynTaskHandle]
|
||||
pub struct TaskHandle<T>(Box<dyn DynTaskHandle>, PhantomData<T>);
|
||||
impl<T: 'static> TaskHandle<T> {
|
||||
/// Immediately stop working on the task. Unlike in Tokio's abort, this is a
|
||||
/// guarantee
|
||||
pub fn abort(self) { self.0.abort(); }
|
||||
/// Stop working on the task and return the nested future. The distinction
|
||||
/// between this and waiting until the task is complete without reparenting it
|
||||
/// is significant for the purpose of [task_local] context
|
||||
pub async fn join(self) -> T { *self.0.join().await.downcast().unwrap() }
|
||||
}
|
||||
|
||||
/// Operations on tasks exposed by the underlying [SpawnSrv]. Wrapped by
|
||||
/// [TaskHandle]
|
||||
pub trait DynTaskHandle {
|
||||
/// It is guaranteed that the future will never be polled after this is called
|
||||
fn abort(self: Box<Self>);
|
||||
/// take the future out of the task. If the return value
|
||||
/// 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, Box<dyn Any>>;
|
||||
}
|
||||
|
||||
/// Trait exposed by the platform-specific scheduler for spawning futures and
|
||||
/// waiting. Use [SpawnExt] for an ergonomic interface
|
||||
pub trait SpawnSrv {
|
||||
/// Returns a future that will resolve after the specified time has elapsed.
|
||||
///
|
||||
/// The sleep future should not keep the interpreter alive.
|
||||
fn sleep_obj(&self, delay: Duration) -> LocalBoxFuture<'static, ()>;
|
||||
/// Spawn a future off-task, but in the same thread.
|
||||
///
|
||||
/// The implementor should not exit while there are pending tasks to allow
|
||||
/// external communication channels to cleanly shut down.
|
||||
fn spawn_obj(
|
||||
&self,
|
||||
keepalive: bool,
|
||||
fut: LocalBoxFuture<'static, Box<dyn Any>>,
|
||||
) -> Box<dyn DynTaskHandle>;
|
||||
}
|
||||
|
||||
/// Extension functions to [SpawnSrv] to conceal all the wrapping
|
||||
pub trait SpawnExt: SpawnSrv {
|
||||
/// Resolve after some time has elapsed
|
||||
fn sleep(&self, delay: Duration) -> impl Future<Output = ()> + 'static { self.sleep_obj(delay) }
|
||||
/// Start a new task on the same thread
|
||||
fn spawn<F: Future + 'static>(&self, fut: F) -> TaskHandle<F::Output> {
|
||||
let th_obj = self.spawn_obj(true, Box::pin(async { Box::new(fut.await) as _ }));
|
||||
TaskHandle(th_obj, PhantomData)
|
||||
}
|
||||
/// Start a new task on the same thread
|
||||
fn spawn_weak<F: Future + 'static>(&self, fut: F) -> TaskHandle<F::Output> {
|
||||
let th_obj = self.spawn_obj(false, Box::pin(async { Box::new(fut.await) as _ }));
|
||||
TaskHandle(th_obj, PhantomData)
|
||||
}
|
||||
}
|
||||
impl<T: ?Sized + SpawnSrv> SpawnExt for T {}
|
||||
|
||||
task_local! {
|
||||
static SPAWN: Rc<dyn SpawnSrv>;
|
||||
}
|
||||
|
||||
pub async fn with_spawn<F: Future>(spawn: Rc<dyn SpawnSrv>, fut: F) -> F::Output {
|
||||
SPAWN.scope(spawn, fut).await
|
||||
}
|
||||
pub fn get_spawn() -> Rc<dyn SpawnSrv> { SPAWN.with(|s| s.clone()) }
|
||||
|
||||
pub async fn sleep(delay: Duration) { get_spawn().sleep(delay).await }
|
||||
pub async fn spawn<F: Future + 'static>(fut: F) -> TaskHandle<F::Output> { get_spawn().spawn(fut) }
|
||||
pub async fn spawn_weak<F: Future + 'static>(fut: F) -> TaskHandle<F::Output> {
|
||||
get_spawn().spawn_weak(fut)
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
#[derive(Clone)]
|
||||
pub struct TokioSpawn {
|
||||
notifier: Rc<Event>,
|
||||
task_counter: Rc<RefCell<usize>>,
|
||||
}
|
||||
#[cfg(feature = "tokio")]
|
||||
impl TokioSpawn {
|
||||
pub fn new() -> Self {
|
||||
Self { notifier: Rc::new(Event::new()), task_counter: Rc::new(RefCell::new(0)) }
|
||||
}
|
||||
pub fn wait_for(&self) -> impl Future<Output = ()> {
|
||||
self.notifier.wait_until(|| (*self.task_counter.borrow() == 0).then_some(()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
impl Default for TokioSpawn {
|
||||
fn default() -> Self { Self::new() }
|
||||
}
|
||||
#[cfg(feature = "tokio")]
|
||||
impl SpawnSrv for TokioSpawn {
|
||||
fn sleep_obj(&self, delay: Duration) -> LocalBoxFuture<'static, ()> {
|
||||
Box::pin(tokio::time::sleep(delay))
|
||||
}
|
||||
fn spawn_obj(
|
||||
&self,
|
||||
keepalive: bool,
|
||||
fut: LocalBoxFuture<'static, Box<dyn Any>>,
|
||||
) -> Box<dyn DynTaskHandle> {
|
||||
let spawner = self.clone();
|
||||
|
||||
let on_task_drop = if keepalive {
|
||||
*self.task_counter.borrow_mut() += 1;
|
||||
self.notifier.notify_all();
|
||||
Some(on_drop(move || {
|
||||
*spawner.task_counter.borrow_mut() -= 1;
|
||||
spawner.notifier.notify_all();
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
struct TokioHandle(tokio::task::JoinHandle<Box<dyn Any>>);
|
||||
return Box::new(TokioHandle(tokio::task::spawn_local(async move {
|
||||
let r = fut.await;
|
||||
std::mem::drop(on_task_drop);
|
||||
r
|
||||
})));
|
||||
impl DynTaskHandle for TokioHandle {
|
||||
fn abort(self: Box<Self>) { self.0.abort(); }
|
||||
fn join(self: Box<Self>) -> LocalBoxFuture<'static, Box<dyn Any>> {
|
||||
Box::pin(async { self.0.await.expect("off-task panicked") })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,13 +154,13 @@ impl<M: ManagedStrat> TokTree<M> {
|
||||
let pos = SrcRange::new(tt.range, &cx.src);
|
||||
let tok = match_mapping!(tt.token, api::Token => Token::<M> {
|
||||
BR,
|
||||
NS(n => es(n).await,
|
||||
NS(n => es(n).await.expect("Namespace prefix is stale interned string"),
|
||||
b => Box::new(Self::from_api(*b, cx).boxed_local().await)),
|
||||
Bottom(e => OrcErrv::from_api(e).await),
|
||||
LambdaHead(arg => Box::new(Self::from_api(*arg, cx).boxed_local().await)),
|
||||
Name(n => es(n).await),
|
||||
Name(n => es(n).await.expect("Name is stale interned string")),
|
||||
S(par, b => ttv_from_api(b, cx).await),
|
||||
Comment(c => es(c).await),
|
||||
Comment(c => es(c).await.expect("Comment is stale interned string")),
|
||||
NewExpr(expr => M::Extra::from_api(expr, cx.extra, pos.clone()).await),
|
||||
Handle(tk => M::Expr::from_api(tk, cx.handle, pos.clone()).await),
|
||||
Reflexive(l => M::Flex::from_api(l, cx.lazy, pos.clone()).await)
|
||||
|
||||
@@ -9,7 +9,7 @@ edition = "2024"
|
||||
async-event = "0.2.1"
|
||||
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
||||
async-once-cell = "0.5.4"
|
||||
chrono = "0.4.44"
|
||||
chrono = "0.4.45"
|
||||
derive_destructure = "1.0.0"
|
||||
dyn-clone = "1.0.20"
|
||||
futures = { version = "0.3.32", default-features = false, features = [
|
||||
@@ -17,9 +17,9 @@ futures = { version = "0.3.32", default-features = false, features = [
|
||||
"async-await",
|
||||
] }
|
||||
futures-locks = "0.7.1"
|
||||
hashbrown = "0.17.0"
|
||||
hashbrown = "0.17.1"
|
||||
include_dir = { version = "0.7.4", optional = true }
|
||||
itertools = "0.14.0"
|
||||
itertools = "0.15.0"
|
||||
konst = "0.4.3"
|
||||
lazy_static = "1.5.0"
|
||||
memo-map = "0.3.3"
|
||||
@@ -29,17 +29,17 @@ orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
||||
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
|
||||
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", default-features = false }
|
||||
ordered-float = "5.3.0"
|
||||
pastey = "0.2.2"
|
||||
pastey = "0.2.3"
|
||||
substack = "1.1.1"
|
||||
task-local = "0.1.1"
|
||||
tokio = { version = "1.52.1", optional = true, features = ["io-std", "time"] }
|
||||
tokio = { version = "1.52.3", optional = true, features = ["io-std", "time"] }
|
||||
tokio-util = { version = "0.7.18", optional = true, features = ["compat"] }
|
||||
|
||||
trait-set = "0.3.0"
|
||||
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
|
||||
|
||||
[features]
|
||||
tokio = ["dep:tokio", "dep:tokio-util"]
|
||||
tokio = ["dep:tokio", "dep:tokio-util", "orchid-base/tokio"]
|
||||
default = ["tokio"]
|
||||
@@ -158,7 +158,7 @@ impl NotTypAtom {
|
||||
/// Convert to a generic Orchid error
|
||||
pub async fn mk_err(&self) -> OrcErrv {
|
||||
mk_errv(
|
||||
is("Not the expected type").await,
|
||||
is!("Not the expected type"),
|
||||
format!("The expression {} is not a {}", fmt(&self.expr).await, self.typ),
|
||||
[self.pos.clone()],
|
||||
)
|
||||
@@ -367,7 +367,7 @@ impl Format for AtomFactory {
|
||||
/// Error produced when an atom can not be applied to a value as a function
|
||||
pub async fn err_not_callable(unit: &FmtUnit) -> OrcErrv {
|
||||
mk_errv_floating(
|
||||
is("This atom is not callable").await,
|
||||
is!("This atom is not callable"),
|
||||
format!("Attempted to apply {} as function", take_first(unit, false)),
|
||||
)
|
||||
}
|
||||
@@ -375,13 +375,13 @@ pub async fn err_not_callable(unit: &FmtUnit) -> OrcErrv {
|
||||
/// Error produced when an atom can not be the final value of the program
|
||||
pub async fn err_not_command(unit: &FmtUnit) -> OrcErrv {
|
||||
mk_errv_floating(
|
||||
is("This atom is not a command").await,
|
||||
is!("This atom is not a command"),
|
||||
format!("Settled on {} which is an inactionable value", take_first(unit, false)),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) async fn err_exit_success_msg() -> IStr { is("Early successful exit").await }
|
||||
pub(crate) async fn err_exit_failure_msg() -> IStr { is("Early failure exit").await }
|
||||
pub(crate) async fn err_exit_success_msg() -> IStr { is!("Early successful exit") }
|
||||
pub(crate) async fn err_exit_failure_msg() -> IStr { is!("Early failure exit") }
|
||||
|
||||
/// Sentinel error returnable from [crate::OwnedAtom::command] or
|
||||
/// [crate::ThinAtom::command] to indicate that the program should exit with a
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::future::LocalBoxFuture;
|
||||
use orchid_base::future_to_vt;
|
||||
use orchid_base::{SpawnExt, SpawnSrv, future_to_outbox, future_to_vt, vt_to_future};
|
||||
|
||||
use crate::{ExtPort, ExtensionBuilder, api};
|
||||
|
||||
@@ -12,24 +12,32 @@ struct Spawner(api::binary::SpawnerBin);
|
||||
impl Drop for Spawner {
|
||||
fn drop(&mut self) { (self.0.drop)(self.0.data) }
|
||||
}
|
||||
impl Spawner {
|
||||
pub fn spawn(&self, delay: Duration, fut: LocalBoxFuture<'static, ()>) {
|
||||
(self.0.spawn)(self.0.data, delay.as_millis().try_into().unwrap(), future_to_vt(fut))
|
||||
impl SpawnSrv for Spawner {
|
||||
fn sleep_obj(&self, delay: Duration) -> LocalBoxFuture<'static, ()> {
|
||||
Box::pin(vt_to_future((self.0.sleep)(self.0.data, delay.as_millis().try_into().unwrap())))
|
||||
}
|
||||
fn spawn_obj(
|
||||
&self,
|
||||
keepalive: bool,
|
||||
fut: LocalBoxFuture<'static, Box<dyn std::any::Any>>,
|
||||
) -> Box<dyn orchid_base::DynTaskHandle> {
|
||||
let (future, outbox) = future_to_outbox(fut);
|
||||
let hand_bin = (self.0.spawn)(self.0.data, keepalive, future_to_vt(future));
|
||||
Box::new(outbox.into_task_handle(hand_bin))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn orchid_extension_main_body(cx: ExtCx, builder: ExtensionBuilder) {
|
||||
let spawner = Rc::new(Spawner(cx.spawner));
|
||||
let spawner2 = spawner.clone();
|
||||
spawner2.spawn(
|
||||
Duration::ZERO,
|
||||
Box::pin(builder.run(ExtPort {
|
||||
input: Box::pin(cx.input),
|
||||
output: Box::pin(cx.output),
|
||||
log: Box::pin(cx.log),
|
||||
spawn: Rc::new(move |delay, fut| spawner.spawn(delay, fut)),
|
||||
})),
|
||||
);
|
||||
let spawn = Rc::new(Spawner(cx.spawner));
|
||||
let spawn2 = spawn.clone();
|
||||
// TODO: define safe binary spawner interface and implement user side.
|
||||
// Take note that
|
||||
spawn2.spawn(builder.run(ExtPort {
|
||||
input: Box::pin(cx.input),
|
||||
output: Box::pin(cx.output),
|
||||
log: Box::pin(cx.log),
|
||||
spawn,
|
||||
}));
|
||||
}
|
||||
|
||||
/// Generate entrypoint for the dylib extension loader
|
||||
|
||||
@@ -32,7 +32,7 @@ impl<T: TryFromExpr, U: TryFromExpr> TryFromExpr for (T, U) {
|
||||
/// Error raised when a composite expression was assumed to be an
|
||||
/// [crate::Atomic], or if the expression was not evaluated yet
|
||||
async fn err_not_atom(pos: Pos, value: &impl Format) -> OrcErrv {
|
||||
mk_errv(is("Expected an atom").await, format!("{} is not an atom", fmt(value).await), [pos])
|
||||
mk_errv(is!("Expected an atom"), format!("{} is not an atom", fmt(value).await), [pos])
|
||||
}
|
||||
|
||||
impl TryFromExpr for ForeignAtom {
|
||||
|
||||
@@ -62,12 +62,12 @@ impl OwnedAtom for Replier {
|
||||
|
||||
/// A long-lived async context that can yield to the executor. The expression
|
||||
/// representing an in-progress exec block is not serializable.
|
||||
pub async fn exec<R: ToExpr>(f: impl for<'a> AsyncFnOnce(ExecHandle<'a>) -> R + 'static) -> GExpr {
|
||||
pub async fn exec<R: ToExpr, F: for<'a> AsyncFnOnce(ExecHandle<'a>) -> R + 'static>(f: F) -> GExpr {
|
||||
let (cmd_snd, cmd_recv) = channel(0);
|
||||
let halt =
|
||||
async { Command::Halt(f(ExecHandle(cmd_snd, PhantomData)).await.to_gen().await) }.into_stream();
|
||||
let coro = BuilderCoroutine(Rc::new(BuilderCoroutineData {
|
||||
name: type_name::<R>(),
|
||||
name: type_name::<F>(),
|
||||
receiver: Mutex::new(stream::select(halt, cmd_recv).boxed_local()),
|
||||
}));
|
||||
coro.run().await
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
use std::any::Any;
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::num::NonZero;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::future::{LocalBoxFuture, join_all, join3};
|
||||
use futures::{AsyncReadExt, AsyncWriteExt, StreamExt, stream};
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
use orchid_api_traits::{Decode, Encode, Request, UnderRoot, enc_vec};
|
||||
use orchid_async_utils::{Handle, JoinError, to_task};
|
||||
use orchid_base::{
|
||||
Client, ClientExt, CommCx, Comment, IoComm, MsgReader, MsgReaderExt, ReqHandleExt, ReqReaderExt,
|
||||
Snippet, Sym, TokFromApiCx, TokToApiCx, TokenVariant, Witness, char_filter_match,
|
||||
char_filter_union, es, io_comm, is, log, mk_char_filter, try_with_reporter, ttv_from_api,
|
||||
ttv_into_api, with_interner, with_logger, with_stash,
|
||||
ttv_into_api, with_interner, with_logger, with_reporter, with_spawn, with_stash,
|
||||
};
|
||||
use substack::Substack;
|
||||
use task_local::task_local;
|
||||
@@ -106,46 +102,6 @@ impl<F: AsyncFnOnce(LocalBoxFuture<'_, ()>) + 'static> ContextModifier for F {
|
||||
Box::pin((self)(fut))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait DynTaskHandle: 'static {
|
||||
fn abort(self: Box<Self>);
|
||||
fn join(self: Box<Self>) -> LocalBoxFuture<'static, Result<Box<dyn Any>, JoinError>>;
|
||||
}
|
||||
|
||||
task_local! {
|
||||
pub(crate) static SPAWN:
|
||||
Rc<dyn Fn(Duration, LocalBoxFuture<'static, Box<dyn Any>>) -> Box<dyn DynTaskHandle> + 'static>
|
||||
}
|
||||
|
||||
/// Handle for a task that is not associated with a particular pending request
|
||||
/// or past notification
|
||||
pub struct TaskHandle<T>(Box<dyn DynTaskHandle>, PhantomData<T>);
|
||||
impl<T: 'static> TaskHandle<T> {
|
||||
/// Immediately stop working on the task. Unlike in Tokio's abort, this is a
|
||||
/// guarantee
|
||||
pub fn abort(self) { self.0.abort(); }
|
||||
/// Stop working on the task and return the nested future. The distinction
|
||||
/// between this and waiting until the task is complete without reparenting it
|
||||
/// is significant for the purpose of [task_local] context
|
||||
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
|
||||
/// notification. Pending tasks are cancelled and dropped when the extension
|
||||
/// exits
|
||||
pub fn spawn<F: Future<Output: 'static> + 'static>(delay: Duration, f: F) -> TaskHandle<F::Output> {
|
||||
SPAWN.with(|spawn| {
|
||||
TaskHandle(spawn(delay, Box::pin(async { Box::new(f.await) as Box<dyn Any> })), PhantomData)
|
||||
})
|
||||
}
|
||||
|
||||
impl DynTaskHandle for Handle<Box<dyn Any>> {
|
||||
fn abort(self: Box<Self>) { Self::abort(&self); }
|
||||
fn join(self: Box<Self>) -> LocalBoxFuture<'static, Result<Box<dyn Any>, JoinError>> {
|
||||
Box::pin(Self::join(*self))
|
||||
}
|
||||
}
|
||||
|
||||
/// A new Orchid extension as specified in loaders. An extension is a unit of
|
||||
/// distribution and its name serves for debugging purposes primarily. In
|
||||
/// contrast, [SystemCtor] is a unit of features of which [ExtensionBuilder] may
|
||||
@@ -293,8 +249,8 @@ impl ExtensionBuilder {
|
||||
},
|
||||
api::HostExtReq::LexExpr(lex @ api::LexExpr { sys, src, text, pos, id }) =>
|
||||
with_sys_record(sys, async {
|
||||
let text = es(text).await;
|
||||
let src = Sym::from_api(src).await;
|
||||
let text = es(text).await.expect("Server did not retain lexable text");
|
||||
let src = Sym::from_api(src).await.expect("Server did not retain lexable path");
|
||||
let expr_store = BorrowedExprStore::new();
|
||||
let tail = &text[pos as usize..];
|
||||
let trigger_char = tail.chars().next().unwrap();
|
||||
@@ -317,7 +273,6 @@ impl ExtensionBuilder {
|
||||
})
|
||||
.await;
|
||||
let pos = (text.len() - s.len()) as u32;
|
||||
eprintln!("Lexation up to pos {pos} ({} - {})", text.len(), s.len());
|
||||
expr_store.dispose().await;
|
||||
return handle.reply(&lex, Some(Ok(api::LexedExpr { pos, expr }))).await;
|
||||
},
|
||||
@@ -333,7 +288,7 @@ impl ExtensionBuilder {
|
||||
let api::ParseLine { module, src, exported, comments, sys, line, idx } = pline;
|
||||
with_sys_record(sys, async {
|
||||
let parsers = dyn_cted().inst().dyn_parsers();
|
||||
let src = Sym::from_api(src).await;
|
||||
let src = Sym::from_api(src).await.expect("Server did not retain parsable path");
|
||||
let comments =
|
||||
join_all(comments.iter().map(|c| Comment::from_api(c, src.clone()))).await;
|
||||
let expr_store = BorrowedExprStore::new();
|
||||
@@ -342,7 +297,9 @@ impl ExtensionBuilder {
|
||||
let line: Vec<GenTokTree> = ttv_from_api(line, &mut cx).await;
|
||||
let snip = Snippet::new(line.first().expect("Empty line"), &line);
|
||||
let parser = parsers[idx as usize];
|
||||
let module = Sym::from_api(module).await;
|
||||
let module = Sym::from_api(module)
|
||||
.await
|
||||
.expect("Server did not retain enclosing module path for parsing");
|
||||
let pctx = ParsCtx::new(module);
|
||||
let o_line =
|
||||
match try_with_reporter(parser.parse(pctx, exported, comments, snip)).await {
|
||||
@@ -357,9 +314,18 @@ impl ExtensionBuilder {
|
||||
},
|
||||
api::HostExtReq::ReflexiveRead(ref rr @ api::ReflexiveRead { tree, path }) =>
|
||||
with_sys_record(tree.sys, async {
|
||||
let tt = read_flex_tree(tree.id, Sym::from_api(path).await).await;
|
||||
let mut cx = TokToApiCx { extra: &mut (), handle: &mut (), lazy: &mut () };
|
||||
handle.reply(rr, tt.into_api(&mut cx).await).await
|
||||
let path = Sym::from_api(path)
|
||||
.await
|
||||
.expect("Server did not retain path for flex tree query");
|
||||
handle
|
||||
.reply(rr, match with_reporter(read_flex_tree(tree.id, path)).await {
|
||||
Ok(tt) => {
|
||||
let mut cx = TokToApiCx { extra: &mut (), handle: &mut (), lazy: &mut () };
|
||||
Ok(tt.into_api(&mut cx).await)
|
||||
},
|
||||
Err(err) => Err(err.to_api()),
|
||||
})
|
||||
.await
|
||||
})
|
||||
.await,
|
||||
api::HostExtReq::ReflexiveDrop(ref rd @ api::ReflexiveDrop(tree)) =>
|
||||
@@ -393,7 +359,9 @@ impl ExtensionBuilder {
|
||||
let (mut req_in, req) = pipe(1024);
|
||||
let (rep, mut rep_out) = pipe(1024);
|
||||
let mut reply = Vec::new();
|
||||
let key = Sym::from_api(*key).await;
|
||||
let key = Sym::from_api(*key)
|
||||
.await
|
||||
.expect("Server did not retain forwarded request key");
|
||||
let (.., some) = join3(
|
||||
async { req_in.write_all(payload).await.expect("Ingress failed") },
|
||||
async { rep_out.read_to_end(&mut reply).await.expect("Egress failed") },
|
||||
@@ -456,12 +424,8 @@ impl ExtensionBuilder {
|
||||
with_comm(
|
||||
Rc::new(client),
|
||||
comm_ctx,
|
||||
SPAWN.scope(
|
||||
Rc::new(move |delay, fut| {
|
||||
let (poll, handle) = to_task(fut);
|
||||
spawn(delay, Box::pin(poll));
|
||||
Box::new(handle)
|
||||
}),
|
||||
with_spawn(
|
||||
spawn,
|
||||
(self.context.into_iter()).fold(
|
||||
Box::pin(async { extension_fut.await.unwrap() }) as LocalBoxFuture<()>,
|
||||
|fut, cx| cx.apply(fut),
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::{AsyncRead, AsyncWrite};
|
||||
use orchid_base::SpawnSrv;
|
||||
|
||||
pub struct ExtPort {
|
||||
pub input: Pin<Box<dyn AsyncRead>>,
|
||||
pub output: Pin<Box<dyn AsyncWrite>>,
|
||||
pub log: Pin<Box<dyn AsyncWrite>>,
|
||||
pub spawn: Rc<dyn Fn(Duration, LocalBoxFuture<'static, ()>)>,
|
||||
pub spawn: Rc<dyn SpawnSrv>,
|
||||
}
|
||||
@@ -145,7 +145,8 @@ impl OwnedAtom for Fun {
|
||||
self.args.clone()
|
||||
}
|
||||
async fn deserialize(mut ds_cx: impl DeserializeCtx, args: Self::Refs) -> Self {
|
||||
let path = Sym::from_api(ds_cx.decode().await).await;
|
||||
let path =
|
||||
Sym::from_api(ds_cx.decode().await).await.expect("Server does not have this function's path");
|
||||
let record = (FUNS_CTX.with(|funs| funs.borrow().get(&(sys_id(), path.clone())).cloned()))
|
||||
.expect("Function missing during deserialization")
|
||||
.clone();
|
||||
|
||||
@@ -141,7 +141,7 @@ pub fn flex_tok<F: AsyncFn(Sym) -> GenTokTree + 'static>(f: F) -> GenTok {
|
||||
pub async fn resolve_name(path: &Sym, local: &Sym) -> OrcRes<Sym> {
|
||||
match request(api::ResolveName { sys: sys_id(), path: path.to_api(), name: local.to_api() }).await
|
||||
{
|
||||
Ok(sym) => Ok(Sym::from_api(sym).await),
|
||||
Ok(sym) => Ok(Sym::from_api(sym).await.expect("Server should cache this local for the lifetime of the extension")),
|
||||
Err(e) => Err(OrcErrv::from_api(e).await),
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::future::{LocalBoxFuture, join_all, ready};
|
||||
use async_fn_stream::try_stream;
|
||||
use futures::TryStreamExt;
|
||||
use futures::future::{LocalBoxFuture, ready};
|
||||
use itertools::Itertools;
|
||||
use orchid_api::StaleTokenError;
|
||||
use orchid_base::local_interner::{Int, StrBranch, StrvBranch};
|
||||
use orchid_base::{IStr, IStrv, InternerSrv};
|
||||
|
||||
@@ -20,10 +23,11 @@ impl InternerSrv for ExtInterner {
|
||||
Box::pin(async { e.set_if_empty(mute_reply(request(api::InternStr(v.to_owned()))).await) }),
|
||||
}
|
||||
}
|
||||
fn es(&self, t: api::TStr) -> LocalBoxFuture<'_, IStr> {
|
||||
fn es(&self, t: api::TStr) -> LocalBoxFuture<'_, Result<IStr, api::StaleTokenError>> {
|
||||
match self.str.e(t) {
|
||||
Ok(i) => Box::pin(ready(i)),
|
||||
Err(e) => Box::pin(async move { e.set_if_empty(Rc::new(request(api::ExternStr(t)).await)) }),
|
||||
Ok(i) => Box::pin(ready(Ok(i))),
|
||||
Err(e) =>
|
||||
Box::pin(async move { Ok(e.set_if_empty(Rc::new(request(api::ExternStr(t)).await?))) }),
|
||||
}
|
||||
}
|
||||
fn iv<'a>(&'a self, v: &'a [IStr]) -> LocalBoxFuture<'a, IStrv> {
|
||||
@@ -34,12 +38,22 @@ impl InternerSrv for ExtInterner {
|
||||
}),
|
||||
}
|
||||
}
|
||||
fn ev(&self, t: orchid_api::TStrv) -> LocalBoxFuture<'_, IStrv> {
|
||||
fn ev(&self, t: api::TStrv) -> LocalBoxFuture<'_, Result<IStrv, StaleTokenError>> {
|
||||
match self.strv.e(t) {
|
||||
Ok(i) => Box::pin(ready(i)),
|
||||
Ok(i) => Box::pin(ready(Ok(i))),
|
||||
Err(e) => Box::pin(async move {
|
||||
let tstr_v = request(api::ExternStrv(t)).await;
|
||||
e.set_if_empty(Rc::new(join_all(tstr_v.into_iter().map(|t| self.es(t))).await))
|
||||
Ok(
|
||||
e.set_if_empty(Rc::new(
|
||||
try_stream(async |mut h| {
|
||||
for tstr in request(api::ExternStrv(t)).await? {
|
||||
h.emit(self.es(tstr).await?).await
|
||||
}
|
||||
Ok(h)
|
||||
})
|
||||
.try_collect()
|
||||
.await?,
|
||||
)),
|
||||
)
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::gen_tok::GenTokTree;
|
||||
use crate::{BorrowedExprStore, api, request};
|
||||
|
||||
pub(crate) async fn ekey_not_applicable() -> IStr {
|
||||
is("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await
|
||||
is!("Pseudo-error to communicate that the current branch in a dispatch doesn't apply")
|
||||
}
|
||||
const MSG_INTERNAL_ERROR: &str = "This error is a sentinel for the extension library.\
|
||||
it should not be emitted by the extension.";
|
||||
|
||||
@@ -59,7 +59,7 @@ impl ReflMod {
|
||||
Ok(details) => details,
|
||||
};
|
||||
for (k, v) in reply.members {
|
||||
let k = es(k).await;
|
||||
let k = es(k).await.expect("Stale token for the name of a member in the tree");
|
||||
let mem = match self.0.members.get(&k) {
|
||||
Some(mem) => mem,
|
||||
None => {
|
||||
|
||||
@@ -41,19 +41,22 @@ impl OwnedAtom for WriterAtom {
|
||||
}
|
||||
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 }) => {
|
||||
eprintln!("output req {req:?}");
|
||||
let reply = match &req {
|
||||
OutputReq::WriteReq(wr @ WriteReq { data }) => {
|
||||
self.0.lock().await.buf.extend(data);
|
||||
hand.reply(wr, Ok(())).await
|
||||
},
|
||||
OutputReq::FlushReq(ref fr @ FlushReq) => {
|
||||
OutputReq::FlushReq(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) =>
|
||||
OutputReq::CloseReq(cr @ CloseReq) =>
|
||||
hand.reply(cr, self.0.lock().await.writer.close().await.map_err(|e| e.into())).await,
|
||||
}
|
||||
};
|
||||
eprintln!("output rep {req:?}");
|
||||
reply
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ pub(crate) fn general_atoms() -> impl Iterator<Item = Option<Box<dyn AtomOps>>>
|
||||
Some(WriterAtom::ops()),
|
||||
Some(ReaderAtom::ops()),
|
||||
Some(IoError::ops()),
|
||||
Some(<()>::ops()),
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
@@ -10,48 +10,22 @@ use crate::{ExtPort, ExtensionBuilder};
|
||||
/// shutdown.
|
||||
#[cfg(feature = "tokio")]
|
||||
pub async fn __tokio_entrypoint(builder: ExtensionBuilder) {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use async_event::Event;
|
||||
use orchid_base::{SpawnExt, TokioSpawn};
|
||||
use tokio::io::{stderr, stdin, stdout};
|
||||
use tokio::task::{LocalSet, spawn_local};
|
||||
use tokio::task::LocalSet;
|
||||
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
|
||||
|
||||
let cc = Rc::new(Event::new());
|
||||
let c = Rc::new(RefCell::new(1));
|
||||
LocalSet::new()
|
||||
.run_until(async {
|
||||
let counter = (c.clone(), cc.clone());
|
||||
builder
|
||||
.run(ExtPort {
|
||||
input: Box::pin(stdin().compat()),
|
||||
output: Box::pin(stdout().compat_write()),
|
||||
log: Box::pin(stderr().compat_write()),
|
||||
spawn: Rc::new(move |delay, fut| {
|
||||
let (c, cc) = counter.clone();
|
||||
if delay.is_zero() {
|
||||
*c.borrow_mut() += 1;
|
||||
cc.notify_all();
|
||||
spawn_local(async move {
|
||||
fut.await;
|
||||
*c.borrow_mut() -= 1;
|
||||
cc.notify_all();
|
||||
});
|
||||
} else {
|
||||
let at = tokio::time::Instant::now() + delay;
|
||||
spawn_local(async move {
|
||||
tokio::time::sleep_until(at).await;
|
||||
*c.borrow_mut() += 1;
|
||||
cc.notify_all();
|
||||
fut.await;
|
||||
*c.borrow_mut() -= 1;
|
||||
cc.notify_all();
|
||||
});
|
||||
}
|
||||
}),
|
||||
})
|
||||
.await;
|
||||
cc.wait_until(|| (*c.borrow() == 0).then_some(())).await;
|
||||
let spawn = Rc::new(TokioSpawn::new());
|
||||
let builder_handle = spawn.spawn(builder.run(ExtPort {
|
||||
input: Box::pin(stdin().compat()),
|
||||
output: Box::pin(stdout().compat_write()),
|
||||
log: Box::pin(stderr().compat_write()),
|
||||
spawn: spawn.clone(),
|
||||
}));
|
||||
builder_handle.join().await;
|
||||
spawn.wait_for().await;
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ pub fn module(
|
||||
name: &str,
|
||||
mems: impl IntoIterator<Item = Vec<GenMember>>,
|
||||
) -> Vec<GenMember> {
|
||||
let (name, kind) = (name.to_string(), LazyMemKind::Mod(mems.into_iter().flatten().collect()));
|
||||
let (name, kind) = (name.to_string(), LazyMemKind::Mod(merge_trivial(mems)));
|
||||
vec![GenMember { name, kind, public, comments: vec![] }]
|
||||
}
|
||||
/// A Rust function that is passed to Orchid via [Fun]
|
||||
@@ -72,25 +72,24 @@ pub fn comments<'a>(
|
||||
|
||||
/// Trivially merge a gen tree. Behaviours were chosen to make this simple.
|
||||
///
|
||||
/// - Comments on imports are discarded
|
||||
/// - Comments on exports and submodules are combined
|
||||
/// - Duplicate constants result in an error
|
||||
/// - A combination of lazy and anything results in an error
|
||||
pub fn merge_trivial(trees: impl IntoIterator<Item = Vec<GenMember>>) -> Vec<GenMember> {
|
||||
let mut all_members = HashMap::<String, (LazyMemKind, Vec<String>)>::new();
|
||||
let mut all_members = HashMap::<String, (bool, LazyMemKind, Vec<String>)>::new();
|
||||
for mem in trees.into_iter().flatten() {
|
||||
assert!(mem.public, "Non-trivial merge in {}", mem.name);
|
||||
match mem.kind {
|
||||
unit @ (LazyMemKind::Const(_) | LazyMemKind::Lazy(_)) => {
|
||||
let prev = all_members.insert(mem.name.clone(), (unit, mem.comments.into_iter().collect()));
|
||||
let prev = all_members
|
||||
.insert(mem.name.clone(), (mem.public, unit, mem.comments.into_iter().collect()));
|
||||
assert!(prev.is_none(), "Conflict in trivial tree merge on {}", mem.name);
|
||||
},
|
||||
LazyMemKind::Mod(members) => match all_members.entry(mem.name.clone()) {
|
||||
hashbrown::hash_map::Entry::Vacant(slot) => {
|
||||
slot.insert((LazyMemKind::Mod(members), mem.comments.into_iter().collect()));
|
||||
slot.insert((mem.public, LazyMemKind::Mod(members), mem.comments.into_iter().collect()));
|
||||
},
|
||||
hashbrown::hash_map::Entry::Occupied(mut old) => match old.get_mut() {
|
||||
(LazyMemKind::Mod(old_items), old_cmts) => {
|
||||
(old_pub, LazyMemKind::Mod(old_items), old_cmts) if *old_pub == mem.public => {
|
||||
let mut swap = vec![];
|
||||
std::mem::swap(&mut swap, old_items);
|
||||
*old_items = merge_trivial([swap, members]);
|
||||
@@ -103,7 +102,7 @@ pub fn merge_trivial(trees: impl IntoIterator<Item = Vec<GenMember>>) -> Vec<Gen
|
||||
}
|
||||
|
||||
(all_members.into_iter())
|
||||
.map(|(name, (kind, comments))| GenMember { comments, kind, name, public: true })
|
||||
.map(|(name, (public, kind, comments))| GenMember { comments, kind, name, public })
|
||||
.collect_vec()
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,12 @@ async-event = "0.2.1"
|
||||
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
||||
async-once-cell = "0.5.4"
|
||||
bound = "0.6.0"
|
||||
chrono = "0.4.44"
|
||||
chrono = "0.4.45"
|
||||
derive_destructure = "1.0.0"
|
||||
futures = { version = "0.3.32", features = ["std"], default-features = false }
|
||||
futures-locks = "0.7.1"
|
||||
hashbrown = "0.17.0"
|
||||
itertools = "0.14.0"
|
||||
hashbrown = "0.17.1"
|
||||
itertools = "0.15.0"
|
||||
lazy_static = "1.5.0"
|
||||
libloading = { version = "0.9.0", optional = true }
|
||||
memo-map = "0.3.3"
|
||||
@@ -24,17 +24,17 @@ num-traits = "0.2.19"
|
||||
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
||||
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", default-features = false }
|
||||
orchid-extension = { version = "0.1.0", path = "../orchid-extension", optional = true }
|
||||
ordered-float = "5.3.0"
|
||||
pastey = "0.2.2"
|
||||
pastey = "0.2.3"
|
||||
substack = "1.1.1"
|
||||
task-local = "0.1.1"
|
||||
tokio = { version = "1.52.1", features = ["process"], optional = true }
|
||||
tokio = { version = "1.52.3", features = ["process"], optional = true }
|
||||
tokio-util = { version = "0.7.18", features = ["compat"], optional = true }
|
||||
trait-set = "0.3.0"
|
||||
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
|
||||
|
||||
[features]
|
||||
tokio = ["dep:tokio", "dep:tokio-util", "dep:libloading"]
|
||||
tokio = ["dep:tokio", "dep:tokio-util", "dep:libloading", "orchid-base/tokio"]
|
||||
orchid-extension = ["dep:orchid-extension"]
|
||||
@@ -272,7 +272,7 @@ impl ox::System for CmdSystemInst {
|
||||
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,
|
||||
is!("Not an instant"),
|
||||
format!("{} is not an instant", fmt(&until).await),
|
||||
[get_arg(0).pos().await],
|
||||
));
|
||||
@@ -289,7 +289,7 @@ impl ox::System for CmdSystemInst {
|
||||
}),
|
||||
])
|
||||
}
|
||||
async fn prelude(&self) -> Vec<Sym> { vec![sym!("orchid")] }
|
||||
async fn prelude(&self) -> Vec<Sym> { vec![sym!(orchid)] }
|
||||
fn lexers(&self) -> Vec<ox::LexerObj> { vec![] }
|
||||
fn parsers(&self) -> Vec<ox::ParserObj> { vec![] }
|
||||
async fn request(&self, _hand: Box<dyn ReqHandle>, req: ox::ReqForSystem<Self>) -> Receipt {
|
||||
|
||||
+8
-42
@@ -4,9 +4,9 @@ use std::rc::{Rc, Weak};
|
||||
use std::time::Duration;
|
||||
use std::{fmt, ops};
|
||||
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures_locks::RwLock;
|
||||
use hashbrown::HashMap;
|
||||
use orchid_base::{SpawnExt, SpawnSrv};
|
||||
|
||||
use crate::api;
|
||||
use crate::expr_store::ExprStore;
|
||||
@@ -14,30 +14,8 @@ use crate::logger::LoggerImpl;
|
||||
use crate::system::{System, WeakSystem};
|
||||
use crate::tree::WeakRoot;
|
||||
|
||||
pub trait JoinHandle {
|
||||
/// It is guaranteed that the future will never be polled after this is called
|
||||
fn abort(&self);
|
||||
/// take the future out of the task. If the return value
|
||||
/// 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, ()>;
|
||||
}
|
||||
|
||||
pub trait Spawner {
|
||||
/// Spawn a future off-task, but in the same thread. If all objects associated
|
||||
/// with the owning [cx] are dropped (eg. expressions, extensions, systems)
|
||||
/// and no extensions create permanent ref loops, then all tasks will
|
||||
/// eventually settle, therefore the implementor of this interface should not
|
||||
/// exit while there are pending tasks to allow external communication
|
||||
/// channels to cleanly shut down.
|
||||
fn spawn_obj(&self, delay: Duration, fut: LocalBoxFuture<'static, ()>) -> Box<dyn JoinHandle>;
|
||||
fn sleep(&self, delay: Duration) -> LocalBoxFuture<'static, ()> {
|
||||
self.spawn_obj(delay, Box::pin(async move {})).join()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CxData {
|
||||
spawner: Rc<dyn Spawner>,
|
||||
spawner: Rc<dyn SpawnSrv>,
|
||||
pub systems: RwLock<HashMap<api::SysId, WeakSystem>>,
|
||||
pub system_id: RefCell<NonZeroU16>,
|
||||
pub exprs: ExprStore,
|
||||
@@ -51,8 +29,8 @@ impl ops::Deref for Cx {
|
||||
fn deref(&self) -> &Self::Target { &self.0 }
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct Weakcx(Weak<CxData>);
|
||||
impl Weakcx {
|
||||
pub struct WeakCx(Weak<CxData>);
|
||||
impl WeakCx {
|
||||
#[must_use]
|
||||
pub fn try_upgrade(&self) -> Option<Cx> { Some(Cx(self.0.upgrade()?)) }
|
||||
#[must_use]
|
||||
@@ -60,9 +38,9 @@ impl Weakcx {
|
||||
}
|
||||
impl Cx {
|
||||
#[must_use]
|
||||
pub fn new(spawner: impl Spawner + 'static, logger: LoggerImpl) -> Self {
|
||||
pub fn new(spawner: Rc<dyn SpawnSrv>, logger: LoggerImpl) -> Self {
|
||||
Self(Rc::new(CxData {
|
||||
spawner: Rc::new(spawner),
|
||||
spawner,
|
||||
systems: RwLock::default(),
|
||||
system_id: RefCell::new(NonZero::new(1).unwrap()),
|
||||
exprs: ExprStore::default(),
|
||||
@@ -70,19 +48,7 @@ impl Cx {
|
||||
logger,
|
||||
}))
|
||||
}
|
||||
/// Spawn a parallel future that you can join at any later time.
|
||||
///
|
||||
/// Don't use this for async Drop, use [orchid_base::stash] instead.
|
||||
/// If you use this for an actor object, make sure to actually join the
|
||||
/// handle.
|
||||
#[must_use]
|
||||
pub fn spawn(
|
||||
&self,
|
||||
delay: Duration,
|
||||
fut: impl Future<Output = ()> + 'static,
|
||||
) -> Box<dyn JoinHandle> {
|
||||
self.spawner.spawn_obj(delay, Box::pin(fut))
|
||||
}
|
||||
pub fn spawn(&self) -> Rc<dyn SpawnSrv> { self.spawner.clone() }
|
||||
/// Return after an amount of time has passed
|
||||
#[must_use]
|
||||
pub async fn sleep(&self, delay: Duration) { self.spawner.sleep(delay).await }
|
||||
@@ -97,7 +63,7 @@ impl Cx {
|
||||
api::SysId(*g)
|
||||
}
|
||||
#[must_use]
|
||||
pub fn downgrade(&self) -> Weakcx { Weakcx(Rc::downgrade(&self.0)) }
|
||||
pub fn downgrade(&self) -> WeakCx { WeakCx(Rc::downgrade(&self.0)) }
|
||||
}
|
||||
impl fmt::Debug for Cx {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
||||
@@ -16,16 +16,14 @@ impl AbsPathError {
|
||||
pub async fn err_obj(self, pos: Pos, path: &str) -> OrcErrv {
|
||||
let (descr, msg) = match self {
|
||||
AbsPathError::RootPath => (
|
||||
is("Path ends on root module").await,
|
||||
is!("Path ends on root module"),
|
||||
format!(
|
||||
"{path} is equal to the empty path. You cannot directly reference the root. \
|
||||
Use one fewer 'super::' or add more segments to make it valid."
|
||||
),
|
||||
),
|
||||
AbsPathError::TooManySupers => (
|
||||
is("Too many 'super::' steps in path").await,
|
||||
format!("{path} is leading outside the root."),
|
||||
),
|
||||
AbsPathError::TooManySupers =>
|
||||
(is!("Too many 'super::' steps in path"), format!("{path} is leading outside the root.")),
|
||||
};
|
||||
mk_errv(descr, msg, [pos])
|
||||
}
|
||||
@@ -39,8 +37,8 @@ impl AbsPathError {
|
||||
/// if the relative path contains as many or more `super` segments than the
|
||||
/// length of the absolute path.
|
||||
pub async fn absolute_path(mut cwd: &[IStr], mut rel: &[IStr]) -> Result<VName, AbsPathError> {
|
||||
let i_self = is("self").await;
|
||||
let i_super = is("super").await;
|
||||
let i_self = is!("self");
|
||||
let i_super = is!("super");
|
||||
let mut relative = false;
|
||||
if let Some((_, tail)) = rel.split_first().filter(|(h, _)| **h == i_self) {
|
||||
rel = tail;
|
||||
|
||||
+65
-10
@@ -7,13 +7,13 @@ use futures::io::BufReader;
|
||||
use futures::{AsyncBufReadExt, StreamExt};
|
||||
use hashbrown::HashMap;
|
||||
use libloading::{Library, Symbol};
|
||||
use orchid_base::{log, on_drop, vt_to_future};
|
||||
use orchid_base::{SpawnExt, future_to_vt, log, on_drop, vt_to_future};
|
||||
use unsync_pipe::pipe;
|
||||
|
||||
use crate::api;
|
||||
use crate::cx::Cx;
|
||||
use crate::extension::ExtPort;
|
||||
use crate::task_set::TaskSet;
|
||||
use crate::task_set::{TaskSet, WeakTaskSet};
|
||||
|
||||
static DYNAMIC_LIBRARIES: Mutex<Option<HashMap<PathBuf, Arc<Library>>>> = Mutex::new(None);
|
||||
fn load_dylib(path: &Path) -> Result<Arc<Library>, libloading::Error> {
|
||||
@@ -33,7 +33,7 @@ pub async fn ext_dylib(path: &Path, cx: Cx) -> Result<ExtPort, libloading::Error
|
||||
let (output, read_output) = pipe(1024);
|
||||
let (write_log, read_log) = pipe(1024);
|
||||
let log_path = path.to_string_lossy().to_string();
|
||||
let _ = cx.spawn(Duration::ZERO, async move {
|
||||
let _ = cx.spawn().spawn(async move {
|
||||
let mut lines = BufReader::new(read_log).lines();
|
||||
while let Some(line) = lines.next().await {
|
||||
match line {
|
||||
@@ -51,7 +51,7 @@ pub async fn ext_dylib(path: &Path, cx: Cx) -> Result<ExtPort, libloading::Error
|
||||
unsafe { library.get("orchid_extension_main") }?;
|
||||
|
||||
let data = Box::into_raw(Box::new(SpawnerState { cx, tasks: tasks.clone() })) as *const ();
|
||||
let spawner = api::binary::SpawnerBin { data, drop, spawn };
|
||||
let spawner = api::binary::SpawnerBin { data, drop: drop_spawner, spawn, sleep };
|
||||
let cx = api::binary::ExtensionContext { input, output, log: write_log, spawner };
|
||||
unsafe { (entrypoint)(cx) };
|
||||
Ok(ExtPort {
|
||||
@@ -65,23 +65,78 @@ struct SpawnerState {
|
||||
cx: Cx,
|
||||
tasks: TaskSet,
|
||||
}
|
||||
extern "C" fn drop(data: *const ()) {
|
||||
extern "C" fn drop_spawner(data: *const ()) {
|
||||
let state = unsafe { Box::from_raw(data as *mut SpawnerState) };
|
||||
state.tasks.abort_all();
|
||||
}
|
||||
extern "C" fn spawn(data: *const (), delay: u64, vt: api::binary::FutureBin) {
|
||||
extern "C" fn sleep(data: *const (), millis: u64) -> api::binary::FutureBin {
|
||||
// SAFETY: this is technically a Box but it can be used directly as a &mut
|
||||
let state = unsafe { (data as *mut SpawnerState).as_mut() }.unwrap();
|
||||
future_to_vt(state.cx.sleep(Duration::from_millis(millis)))
|
||||
}
|
||||
extern "C" fn spawn(
|
||||
data: *const (),
|
||||
keepalive: bool,
|
||||
vt: api::binary::FutureBin,
|
||||
) -> api::binary::TaskHandleBin {
|
||||
// TODO: a new DynTaskHandle implementor that uses these IDs
|
||||
let future = vt_to_future(vt);
|
||||
// SAFETY: this is technically a Box but it can be used directly as a &mut
|
||||
let state = unsafe { (data as *mut SpawnerState).as_mut() }.unwrap();
|
||||
state.tasks.with(|store| {
|
||||
let id = state.tasks.with(|store| {
|
||||
store.add_with(|id| {
|
||||
state.cx.spawn(Duration::from_millis(delay), async move {
|
||||
let fut = async move {
|
||||
future.await;
|
||||
// SAFETY: We know this is live because the drop handle that frees it also
|
||||
// aborts the future
|
||||
let state = unsafe { (data as *mut SpawnerState).as_mut() }.unwrap();
|
||||
state.tasks.with(|store| store.remove(id));
|
||||
})
|
||||
});
|
||||
};
|
||||
let spawner = state.cx.spawn();
|
||||
let handle = if keepalive { spawner.spawn(fut) } else { spawner.spawn_weak(fut) };
|
||||
(handle, id as u64)
|
||||
})
|
||||
});
|
||||
let handle_data = if let Some(id) = id {
|
||||
let handle = TaskHandle { id, tasks: state.tasks.downgrade() };
|
||||
Box::into_raw(Box::new(handle)) as *const ()
|
||||
} else {
|
||||
std::ptr::null()
|
||||
};
|
||||
api::binary::TaskHandleBin { data: handle_data, drop: drop_task, abort, join }
|
||||
}
|
||||
|
||||
struct TaskHandle {
|
||||
id: u64,
|
||||
tasks: WeakTaskSet,
|
||||
}
|
||||
|
||||
extern "C" fn drop_task(data: *const ()) {
|
||||
if !data.is_null() {
|
||||
// SAFETY: this type is constructed in spawn, and the pointer is not null
|
||||
unsafe { std::mem::drop(Box::<TaskHandle>::from_raw(data as *mut _)) };
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn abort(data: *const ()) {
|
||||
if data.is_null() {
|
||||
return;
|
||||
}
|
||||
// SAFETY: this type is constructed in spawn, and the pointer is not null
|
||||
let handle = unsafe { Box::<TaskHandle>::from_raw(data as *mut _) };
|
||||
let Some(tasks) = handle.tasks.upgrade() else { return };
|
||||
tasks.with(|store| store.remove(handle.id as usize).abort());
|
||||
}
|
||||
|
||||
extern "C" fn join(data: *const ()) -> api::binary::FutureBin {
|
||||
if data.is_null() {
|
||||
return future_to_vt(futures::future::pending());
|
||||
}
|
||||
// SAFETY: this type is constructed in spawn, and the pointer is not null
|
||||
let handle = unsafe { Box::<TaskHandle>::from_raw(data as *mut _) };
|
||||
let Some(tasks) = handle.tasks.upgrade() else { return future_to_vt(futures::future::pending()) };
|
||||
let Some(task) = tasks.with(|store| store.remove(handle.id as usize)) else {
|
||||
return future_to_vt(futures::future::pending());
|
||||
};
|
||||
future_to_vt(task.join())
|
||||
}
|
||||
@@ -76,7 +76,8 @@ impl Expr {
|
||||
Expr::from_api(*x, rpsb, cx).boxed_local().await,
|
||||
)
|
||||
},
|
||||
api::ExpressionKind::Const(name) => ExprKind::Const(Sym::from_api(name).await),
|
||||
api::ExpressionKind::Const(name) =>
|
||||
ExprKind::Const(Sym::from_api(name).await.expect("Const name freed before const received")),
|
||||
api::ExpressionKind::Lambda(x, body) => {
|
||||
let lbuilder = psb.lambda(&x);
|
||||
let body = Expr::from_api(*body, lbuilder.stack(), cx).boxed_local().await;
|
||||
|
||||
@@ -5,7 +5,6 @@ use std::io;
|
||||
use std::num::NonZeroU64;
|
||||
use std::pin::Pin;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::time::Duration;
|
||||
|
||||
use derive_destructure::destructure;
|
||||
use futures::channel::mpsc::{Sender, channel};
|
||||
@@ -17,12 +16,13 @@ use itertools::Itertools;
|
||||
use orchid_api_traits::{Decode, Encode, Request};
|
||||
use orchid_base::{
|
||||
AtomRepr, Client, ClientExt, CommCx, FmtCxImpl, Format, IStr, IStrv, IoComm, MsgReaderExt, Pos,
|
||||
ReqHandleExt, ReqReaderExt, Sym, Witness, es, ev, io_comm, is, iv, log, stash, with_stash,
|
||||
ReqHandleExt, ReqReaderExt, SpawnExt, Sym, TaskHandle, Witness, es, ev, io_comm, is, iv, log,
|
||||
stash, with_stash,
|
||||
};
|
||||
|
||||
use crate::api;
|
||||
use crate::atom::AtomHand;
|
||||
use crate::cx::{Cx, JoinHandle};
|
||||
use crate::cx::Cx;
|
||||
use crate::dealias::{ChildError, ChildErrorKind, walk};
|
||||
use crate::expr::{Expr, ExprFromApiCx, PathSetBuilder};
|
||||
use crate::system::SystemCtor;
|
||||
@@ -51,7 +51,7 @@ pub struct ExtensionData {
|
||||
name: String,
|
||||
cx: Cx,
|
||||
comm_cx: Option<CommCx>,
|
||||
join_ext: Option<Box<dyn JoinHandle>>,
|
||||
join_ext: Option<TaskHandle<()>>,
|
||||
client: Rc<dyn Client>,
|
||||
systems: Vec<SystemCtor>,
|
||||
next_pars: RefCell<NonZeroU64>,
|
||||
@@ -90,7 +90,7 @@ impl Extension {
|
||||
let weak2 = weak;
|
||||
let weak = weak.clone();
|
||||
let cx2 = cx.clone();
|
||||
let join_ext = cx.clone().spawn(Duration::ZERO, async move {
|
||||
let join_ext = cx.clone().spawn().spawn(async move {
|
||||
srv
|
||||
.listen(
|
||||
async |reader| {
|
||||
@@ -110,12 +110,22 @@ impl Extension {
|
||||
this.0.cx.exprs.take_expr(rel.1);
|
||||
},
|
||||
api::ExtHostNotif::Log(api::Log { category, message }) =>
|
||||
write!(log(&es(category).await), "{message}").await,
|
||||
write!(log(&es(category).await.expect("Log category stale")), "{message}").await,
|
||||
api::ExtHostNotif::Sweeped(data) => {
|
||||
for i in join_all(data.strings.into_iter().map(es)).await {
|
||||
for i in
|
||||
join_all(data.strings.into_iter().map(async |s| {
|
||||
es(s).await.expect("Sweeped token must have existed before")
|
||||
}))
|
||||
.await
|
||||
{
|
||||
this.0.itable.borrow_mut().strings.remove(&i);
|
||||
}
|
||||
for i in join_all(data.vecs.into_iter().map(ev)).await {
|
||||
for i in
|
||||
join_all(data.vecs.into_iter().map(async |s| {
|
||||
ev(s).await.expect("Sweeped token must have existed before")
|
||||
}))
|
||||
.await
|
||||
{
|
||||
this.0.itable.borrow_mut().string_vecs.remove(&i);
|
||||
}
|
||||
},
|
||||
@@ -145,23 +155,32 @@ impl Extension {
|
||||
handle.reply(&s, i.to_api()).await
|
||||
},
|
||||
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| async { es(*m).await.expect("Intern strv of stale strings") }),
|
||||
)
|
||||
.await;
|
||||
this.0.itable.borrow_mut().strings.extend(tokens.iter().cloned());
|
||||
let i = iv(&tokens).await;
|
||||
this.0.itable.borrow_mut().string_vecs.insert(i.clone());
|
||||
handle.reply(&v, i.to_api()).await
|
||||
},
|
||||
api::IntReq::ExternStr(si) => {
|
||||
let i = es(si.0).await;
|
||||
this.0.itable.borrow_mut().strings.insert(i.clone());
|
||||
handle.reply(&si, i.to_string()).await
|
||||
api::IntReq::ExternStr(si) => match es(si.0).await {
|
||||
Err(e) => handle.reply(&si, Err(e)).await,
|
||||
Ok(i) => {
|
||||
this.0.itable.borrow_mut().strings.insert(i.clone());
|
||||
handle.reply(&si, Ok(i.to_string())).await
|
||||
},
|
||||
},
|
||||
api::IntReq::ExternStrv(vi) => {
|
||||
let i = ev(vi.0).await;
|
||||
this.0.itable.borrow_mut().strings.extend(i.iter().cloned());
|
||||
this.0.itable.borrow_mut().string_vecs.insert(i.clone());
|
||||
let markerv = i.iter().map(|t| t.to_api()).collect_vec();
|
||||
handle.reply(&vi, markerv).await
|
||||
api::IntReq::ExternStrv(vi) => match ev(vi.0).await {
|
||||
Err(e) => handle.reply(&vi, Err(e)).await,
|
||||
Ok(i) => {
|
||||
this.0.itable.borrow_mut().strings.extend(i.iter().cloned());
|
||||
this.0.itable.borrow_mut().string_vecs.insert(i.clone());
|
||||
let markerv = i.iter().map(|t| t.to_api()).collect_vec();
|
||||
handle.reply(&vi, Ok(markerv)).await
|
||||
},
|
||||
},
|
||||
},
|
||||
api::ExtHostReq::Fwd(ref fw @ api::Fwd { ref target, ref method, ref body }) => {
|
||||
@@ -223,7 +242,7 @@ impl Extension {
|
||||
api::ExtHostReq::LsModule(ref ls @ api::LsModule(_sys, path)) => {
|
||||
let reply: <api::LsModule as Request>::Response =
|
||||
'reply: {
|
||||
let path = ev(path).await;
|
||||
let path = ev(path).await.expect("LsModule issued for stale path");
|
||||
let root = (cx.root.read().await.upgrade())
|
||||
.expect("LSModule called when root isn't in context");
|
||||
let root_data = &*root.0.read().await;
|
||||
@@ -261,8 +280,12 @@ impl Extension {
|
||||
let result = root
|
||||
.resolve_name(
|
||||
cx.clone(),
|
||||
Sym::from_api(*path).await,
|
||||
Sym::from_api(*name).await,
|
||||
Sym::from_api(*path)
|
||||
.await
|
||||
.expect("Extension dropped path from ResolveName query"),
|
||||
Sym::from_api(*name)
|
||||
.await
|
||||
.expect("Extension dropped name from ResolveName query"),
|
||||
)
|
||||
.await;
|
||||
let rep = match result {
|
||||
@@ -271,7 +294,11 @@ impl Extension {
|
||||
.extend(e.iter().map(|e| e.description.clone()));
|
||||
Err(e.to_api())
|
||||
},
|
||||
Ok(name) => Ok(name.to_sym().await.to_api()),
|
||||
Ok(name) => {
|
||||
let sym = name.to_sym().await;
|
||||
this.0.itable.borrow_mut().string_vecs.insert(sym.tok());
|
||||
Ok(sym.to_api())
|
||||
},
|
||||
};
|
||||
handle.reply(rn, rep).await
|
||||
},
|
||||
@@ -351,13 +378,10 @@ impl Extension {
|
||||
}
|
||||
pub fn system_drop(&self, id: api::SysId) {
|
||||
let rc = self.clone();
|
||||
let _ = self.cx().spawn(
|
||||
Duration::ZERO,
|
||||
with_stash(async move {
|
||||
rc.client().request(api::SystemDrop(id)).await.unwrap();
|
||||
rc.cx().systems.write().await.remove(&id);
|
||||
}),
|
||||
);
|
||||
let _ = self.cx().spawn().spawn(with_stash(async move {
|
||||
rc.client().request(api::SystemDrop(id)).await.unwrap();
|
||||
rc.cx().systems.write().await.remove(&id);
|
||||
}));
|
||||
}
|
||||
pub fn itable(&self) -> &RefCell<InternedTable> { &self.0.itable }
|
||||
#[must_use]
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use std::io;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::io::BufReader;
|
||||
use futures::{AsyncBufReadExt, StreamExt};
|
||||
use orchid_base::{log, on_drop};
|
||||
use orchid_base::{SpawnExt, log, on_drop};
|
||||
use orchid_extension as ox;
|
||||
use unsync_pipe::pipe;
|
||||
|
||||
@@ -19,7 +17,7 @@ pub async fn ext_inline(builder: ox::ExtensionBuilder, cx: Cx) -> ExtPort {
|
||||
|
||||
let name = builder.name;
|
||||
|
||||
std::mem::drop(cx.spawn(Duration::ZERO, async move {
|
||||
std::mem::drop(cx.spawn().spawn(async move {
|
||||
let mut lines = BufReader::new(out_stderr).lines();
|
||||
while let Some(line) = lines.next().await {
|
||||
match line {
|
||||
@@ -35,25 +33,14 @@ pub async fn ext_inline(builder: ox::ExtensionBuilder, cx: Cx) -> ExtPort {
|
||||
let task_set = TaskSet::default();
|
||||
let task_set1 = task_set.clone();
|
||||
|
||||
std::mem::drop(cx.clone().spawn(Duration::ZERO, async move {
|
||||
std::mem::drop(cx.spawn().spawn(async move {
|
||||
let task_set2 = task_set1.clone();
|
||||
builder
|
||||
.run(ox::ExtPort {
|
||||
input: Box::pin(out_stdin),
|
||||
output: Box::pin(in_stdout),
|
||||
log: Box::pin(in_stderr),
|
||||
spawn: Rc::new(move |delay, fut| {
|
||||
let cx1 = cx.clone();
|
||||
let task_set2 = task_set1.clone();
|
||||
task_set1.with(move |store| {
|
||||
store.add_with(move |id| {
|
||||
cx1.spawn(delay, async move {
|
||||
fut.await;
|
||||
task_set2.with(|store| store.remove(id));
|
||||
})
|
||||
})
|
||||
});
|
||||
}),
|
||||
spawn: cx.spawn(),
|
||||
})
|
||||
.await;
|
||||
task_set2.abort_all();
|
||||
|
||||
@@ -121,7 +121,7 @@ pub async fn lex_once(cx: &mut LexCx<'_>, exactly_one: bool) -> OrcRes<bool> {
|
||||
let mut sub_cx = cx.sub(cx.pos_from(tail), &mut produced);
|
||||
if !lex_once(&mut sub_cx, exactly_one).boxed_local().await? {
|
||||
return Err(mk_errv(
|
||||
is("Unexpected end of source text").await,
|
||||
is!("Unexpected end of source text"),
|
||||
":: cannot be the last token",
|
||||
[SrcRange::new(start..cx.get_pos(), &cx.pcx.src())],
|
||||
));
|
||||
@@ -138,7 +138,7 @@ pub async fn lex_once(cx: &mut LexCx<'_>, exactly_one: bool) -> OrcRes<bool> {
|
||||
} else if cx.strip_prefix("--[") {
|
||||
let Some((cmt, tail)) = cx.tail.split_once("]--") else {
|
||||
return Err(mk_errv(
|
||||
is("Unterminated block comment").await,
|
||||
is!("Unterminated block comment"),
|
||||
"This block comment has no ending ]--",
|
||||
[SrcRange::new(start..start + 3, &cx.pcx.src())],
|
||||
));
|
||||
@@ -155,7 +155,7 @@ pub async fn lex_once(cx: &mut LexCx<'_>, exactly_one: bool) -> OrcRes<bool> {
|
||||
let mut sub_cx = cx.sub(cx.pos_from(tail), &mut produced);
|
||||
if !lex_once(&mut sub_cx, exactly_one).boxed_local().await? {
|
||||
return Err(mk_errv(
|
||||
is("Unexpected end of file").await,
|
||||
is!("Unexpected end of file"),
|
||||
"Expected a lambda argument and body",
|
||||
[SrcRange::new(start..cx.get_pos(), &cx.pcx.src())],
|
||||
));
|
||||
@@ -176,7 +176,7 @@ pub async fn lex_once(cx: &mut LexCx<'_>, exactly_one: bool) -> OrcRes<bool> {
|
||||
let mut sub_cx = cx.sub(cx.get_pos(), &mut body);
|
||||
if !lex_once(&mut sub_cx, false).boxed_local().await? {
|
||||
return Err(mk_errv(
|
||||
is("unclosed paren").await,
|
||||
is!("unclosed paren"),
|
||||
format!("this {lp} has no matching {rp}"),
|
||||
[SrcRange::new(start..start + 1, &cx.pcx.src())],
|
||||
));
|
||||
@@ -223,7 +223,7 @@ pub async fn lex_once(cx: &mut LexCx<'_>, exactly_one: bool) -> OrcRes<bool> {
|
||||
return Ok(true);
|
||||
} else {
|
||||
return Err(mk_errv(
|
||||
is("Unrecognized character").await,
|
||||
is!("Unrecognized character"),
|
||||
"The following syntax is meaningless.",
|
||||
[SrcRange::new(start..start + 1, &cx.pcx.src())],
|
||||
));
|
||||
@@ -251,11 +251,10 @@ pub async fn sys_lex(cx: &mut LexCx<'_>) -> Option<OrcRes<Vec<ParsTokTree>>> {
|
||||
let mut sub_cx = cx_g.sub(pos, &mut produced);
|
||||
let lex_res = lex_once(&mut sub_cx, true).boxed_local().await;
|
||||
let pos1 = sub_cx.get_pos();
|
||||
eprintln!("Setting sublex pos {pos1}");
|
||||
cx_g.set_pos(pos1);
|
||||
match lex_res {
|
||||
Ok(false) => {
|
||||
let errv = mk_errv(is("End of file").await, "Unexpected end of source text", [
|
||||
let errv = mk_errv(is!("End of file"), "Unexpected end of source text", [
|
||||
cx_g.sr_to(pos),
|
||||
]);
|
||||
Err(errv.to_api())
|
||||
@@ -282,7 +281,6 @@ pub async fn sys_lex(cx: &mut LexCx<'_>) -> Option<OrcRes<Vec<ParsTokTree>>> {
|
||||
return Some(Err(OrcErrv::from_api(e).await));
|
||||
},
|
||||
Ok(Some(lexed)) => {
|
||||
eprintln!("Lexer plugin ended at {}", lexed.pos);
|
||||
cx.set_pos(lexed.pos);
|
||||
let mut stable_trees = Vec::new();
|
||||
for tok in lexed.expr {
|
||||
|
||||
+12
-10
@@ -99,16 +99,16 @@ pub async fn parse_item(
|
||||
) -> OrcRes<Vec<Item>> {
|
||||
match item.split_first() {
|
||||
Some((TokTree { tok: Token::Name(n), .. }, postdisc)) => match n {
|
||||
n if *n == is("export").await => match try_pop_no_fluff(postdisc).await? {
|
||||
n if *n == is!("export") => match try_pop_no_fluff(postdisc).await? {
|
||||
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } =>
|
||||
parse_exportable_item(cx, path, comments, true, n.clone(), tail).await,
|
||||
Parsed { output, tail: _ } => Err(mk_errv(
|
||||
is("Malformed export").await,
|
||||
is!("Malformed export"),
|
||||
"`export` can either prefix other lines or list names inside ( )",
|
||||
[output.sr()],
|
||||
)),
|
||||
},
|
||||
n if *n == is("import").await => {
|
||||
n if *n == is!("import") => {
|
||||
let imports = parse_import(postdisc).await?;
|
||||
Ok(Vec::from_iter(imports.into_iter().map(|t| Item {
|
||||
comments: comments.clone(),
|
||||
@@ -119,7 +119,7 @@ pub async fn parse_item(
|
||||
n => parse_exportable_item(cx, path, comments, false, n.clone(), postdisc).await,
|
||||
},
|
||||
Some(_) =>
|
||||
Err(mk_errv(is("Expected a line type").await, "All lines must begin with a keyword", [
|
||||
Err(mk_errv(is!("Expected a line type"), "All lines must begin with a keyword", [
|
||||
item.sr()
|
||||
])),
|
||||
None => unreachable!("These lines are filtered and aggregated in earlier stages"),
|
||||
@@ -140,7 +140,7 @@ pub async fn parse_exportable_item<'a>(
|
||||
discr: IStr,
|
||||
tail: ParsSnippet<'a>,
|
||||
) -> OrcRes<Vec<Item>> {
|
||||
let kind = if discr == is("mod").await {
|
||||
let kind = if discr == is!("mod") {
|
||||
let (name, body) = parse_module(cx, path, tail).await?;
|
||||
ItemKind::Member(ParsedMember { name, exported, kind: ParsedMemKind::Mod(body) })
|
||||
} else if let Some(parser) = cx.systems().iter().find_map(|s| s.get_parser(discr.clone())) {
|
||||
@@ -153,7 +153,7 @@ pub async fn parse_exportable_item<'a>(
|
||||
} else {
|
||||
let ext_lines = cx.systems().iter().flat_map(System::line_types).join(", ");
|
||||
return Err(mk_errv(
|
||||
is("Unrecognized line type").await,
|
||||
is!("Unrecognized line type"),
|
||||
format!("Line types are: mod, {ext_lines}"),
|
||||
[tail.prev().sr()],
|
||||
));
|
||||
@@ -170,7 +170,7 @@ pub async fn parse_module<'a>(
|
||||
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } => (n.clone(), tail),
|
||||
Parsed { output, .. } => {
|
||||
return Err(mk_errv(
|
||||
is("Missing module name").await,
|
||||
is!("Missing module name"),
|
||||
format!("A name was expected, {} was found", fmt(output).await),
|
||||
[output.sr()],
|
||||
));
|
||||
@@ -180,7 +180,7 @@ pub async fn parse_module<'a>(
|
||||
expect_end(surplus).await?;
|
||||
let Some(body) = output.as_s(Paren::Round) else {
|
||||
return Err(mk_errv(
|
||||
is("Expected module body").await,
|
||||
is!("Expected module body"),
|
||||
format!("A ( block ) was expected, {} was found", fmt(output).await),
|
||||
[output.sr()],
|
||||
));
|
||||
@@ -259,8 +259,10 @@ pub async fn lower(
|
||||
ParsTok::Bottom(err) => ExprKind::Bottom(err.clone()).at(tt.sr.pos()),
|
||||
ParsTok::Handle(expr) | ParsTok::NewExpr(expr) => expr.clone(),
|
||||
ParsTok::Reflexive(flex) => {
|
||||
let flex_tt = flex.get(&mut *cx.flex_repo().lock().await, &path, &cx.src()).await;
|
||||
lower(&flex_tt, builder, path, cx).boxed_local().await
|
||||
match flex.get(&mut *cx.flex_repo().lock().await, &path, &cx.src()).await {
|
||||
Ok(flex_tt) => lower(&flex_tt, builder, path, cx).boxed_local().await,
|
||||
Err(e) => ExprKind::Bottom(e).at(tt.sr.pos()),
|
||||
}
|
||||
},
|
||||
ParsTok::NS(..) | ParsTok::Name(_) => {
|
||||
let Ok(local_name) = tt.as_multiname() else {
|
||||
|
||||
+24
-17
@@ -7,8 +7,9 @@ use futures::future::{LocalBoxFuture, join_all};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use itertools::Itertools;
|
||||
use orchid_base::{
|
||||
ClientExt, Comment, FmtCx, FmtUnit, Format, IStr, IStrv, Import, ManagedStrat, Snippet, SrcRange,
|
||||
Sym, TokFromApiCx, TokToApiCx, TokTree, Token, TokenVariant, Variants, stash, tl_cache, ttv_fmt,
|
||||
ClientExt, Comment, FmtCx, FmtUnit, Format, IStr, IStrv, Import, ManagedStrat, OrcErrv, OrcRes,
|
||||
Snippet, SrcRange, Sym, TokFromApiCx, TokToApiCx, TokTree, Token, TokenVariant, Variants, stash,
|
||||
tl_cache, ttv_fmt,
|
||||
};
|
||||
|
||||
use crate::api;
|
||||
@@ -26,7 +27,7 @@ impl FlexTreeHandle {
|
||||
pub fn to_api(&self) -> api::ReflexiveTree {
|
||||
api::ReflexiveTree { sys: self.sys.id(), id: self.id }
|
||||
}
|
||||
pub async fn read(&self, path: &Sym) -> api::TokenTree {
|
||||
pub async fn read(&self, path: &Sym) -> api::OrcResult<api::TokenTree> {
|
||||
(self.sys.client())
|
||||
.request(api::ReflexiveRead { path: path.to_api(), tree: self.to_api() })
|
||||
.await
|
||||
@@ -43,22 +44,28 @@ impl Drop for FlexTreeHandle {
|
||||
}
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct FlexTree(Rc<FlexTreeHandle>);
|
||||
pub struct FlexTree {
|
||||
hand: Rc<FlexTreeHandle>,
|
||||
}
|
||||
impl FlexTree {
|
||||
pub async fn get(&self, flex: &mut FlexTreeRepo, path: &Sym, src: &Sym) -> ParsTokTree {
|
||||
let tt = self.0.read(path).await;
|
||||
ParsTokTree::from_api(tt, &mut TokFromApiCx {
|
||||
extra: &mut &self.0.sys.clone(),
|
||||
handle: &mut ExprStore::default(),
|
||||
src: src.clone(),
|
||||
lazy: &mut (self.0.sys.clone(), flex),
|
||||
})
|
||||
.await
|
||||
pub async fn get(&self, flex: &mut FlexTreeRepo, path: &Sym, src: &Sym) -> OrcRes<ParsTokTree> {
|
||||
match self.hand.read(path).await {
|
||||
Err(e) => Err(OrcErrv::from_api(e).await),
|
||||
Ok(tt) => Ok(
|
||||
ParsTokTree::from_api(tt, &mut TokFromApiCx {
|
||||
extra: &mut &self.hand.sys.clone(),
|
||||
handle: &mut ExprStore::default(),
|
||||
src: src.clone(),
|
||||
lazy: &mut (self.hand.sys.clone(), flex),
|
||||
})
|
||||
.await,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Debug for FlexTree {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}/flextree/{:?}", self.0.id, &self.0.sys)
|
||||
write!(f, "{:?}/flextree/{:?}", self.hand.id, &self.hand.sys)
|
||||
}
|
||||
}
|
||||
impl Format for FlexTree {
|
||||
@@ -79,13 +86,13 @@ impl TokenVariant<api::ReflexiveTree> for FlexTree {
|
||||
tree.clone()
|
||||
} else {
|
||||
assert_eq!(sys.id(), api.sys, "Newly constructed flextree for wrong system");
|
||||
FlexTree(Rc::new(FlexTreeHandle { sys: sys.clone(), id: api.id }))
|
||||
FlexTree { hand: Rc::new(FlexTreeHandle { sys: sys.clone(), id: api.id }) }
|
||||
}
|
||||
}
|
||||
type ToApiCx<'a> = FlexTreeRepo;
|
||||
async fn into_api(self, ctx: &mut Self::ToApiCx<'_>) -> api::ReflexiveTree {
|
||||
ctx.0.entry(self.0.to_api()).or_insert_with(|| self.clone());
|
||||
self.0.to_api()
|
||||
ctx.0.entry(self.hand.to_api()).or_insert_with(|| self.clone());
|
||||
self.hand.to_api()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::io::BufReader;
|
||||
use futures::{self, AsyncBufReadExt, StreamExt};
|
||||
use orchid_base::log;
|
||||
use orchid_base::{SpawnExt, log};
|
||||
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
|
||||
|
||||
use crate::cx::Cx;
|
||||
@@ -19,7 +17,7 @@ pub async fn ext_command(cmd: std::process::Command, cx: Cx) -> std::io::Result<
|
||||
let stdin = child.stdin.take().unwrap();
|
||||
let stdout = child.stdout.take().unwrap();
|
||||
let child_stderr = child.stderr.take().unwrap();
|
||||
std::mem::drop(cx.spawn(Duration::ZERO, async move {
|
||||
std::mem::drop(cx.spawn().spawn(async move {
|
||||
let _ = child;
|
||||
let mut lines = BufReader::new(child_stderr.compat()).lines();
|
||||
while let Some(line) = lines.next().await {
|
||||
|
||||
@@ -103,7 +103,7 @@ async fn conv(
|
||||
continue;
|
||||
},
|
||||
};
|
||||
let name = es(name).await;
|
||||
let name = es(name).await.expect("Member name stale before parsed tree fully received");
|
||||
let mem_path = module.push(name.clone());
|
||||
let mkind = match kind {
|
||||
api::ParsedMemberKind::Module { lines, use_prelude } => {
|
||||
|
||||
@@ -2,14 +2,15 @@ use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::time::Duration;
|
||||
|
||||
use derive_destructure::destructure;
|
||||
use futures::future::join_all;
|
||||
use futures_locks::RwLock;
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
use orchid_base::{Client, ClientExt, FmtCx, FmtUnit, Format, IStr, Sym, char_filter_match, es};
|
||||
use orchid_base::{
|
||||
Client, ClientExt, FmtCx, FmtUnit, Format, IStr, SpawnExt, Sym, char_filter_match, es,
|
||||
};
|
||||
use ordered_float::NotNan;
|
||||
use substack::{Stackframe, Substack};
|
||||
|
||||
@@ -114,7 +115,7 @@ impl System {
|
||||
}
|
||||
pub(crate) fn drop_atom(&self, dropped_atom_id: api::AtomId) {
|
||||
let this = self.0.clone();
|
||||
let _ = self.0.cx.spawn(Duration::ZERO, async move {
|
||||
let _ = self.0.cx.spawn().spawn(async move {
|
||||
this.ext.client().request(api::AtomDrop(this.id, dropped_atom_id)).await.unwrap();
|
||||
this.owned_atoms.write().await.remove(&dropped_atom_id);
|
||||
});
|
||||
@@ -175,9 +176,18 @@ impl SystemCtor {
|
||||
ext: ext.clone(),
|
||||
cx: ext.cx().clone(),
|
||||
lex_filter: sys_inst.lex_filter,
|
||||
line_types: join_all(sys_inst.line_types.iter().map(|m| es(*m))).await,
|
||||
line_types: join_all(
|
||||
sys_inst
|
||||
.line_types
|
||||
.iter()
|
||||
.map(async |m| es(*m).await.expect("line_types should be statically interned")),
|
||||
)
|
||||
.await,
|
||||
id,
|
||||
prelude: join_all(sys_inst.prelude.iter().map(|tok| Sym::from_api(*tok))).await,
|
||||
prelude: join_all(sys_inst.prelude.iter().map(async |tok| {
|
||||
Sym::from_api(*tok).await.expect("prelude items should be statically interned")
|
||||
}))
|
||||
.await,
|
||||
owned_atoms: RwLock::new(HashMap::new()),
|
||||
}));
|
||||
let api_module_root = api::Module {
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use orchid_base::IdStore;
|
||||
use orchid_base::{IdStore, TaskHandle};
|
||||
|
||||
use crate::cx::JoinHandle;
|
||||
pub struct WeakTaskSet {
|
||||
pub tasks: Weak<RefCell<Option<IdStore<TaskHandle<()>>>>>,
|
||||
}
|
||||
impl WeakTaskSet {
|
||||
pub fn upgrade(&self) -> Option<TaskSet> { Some(TaskSet { tasks: self.tasks.upgrade()? }) }
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TaskSet {
|
||||
pub tasks: Rc<RefCell<Option<IdStore<Box<dyn JoinHandle>>>>>,
|
||||
pub tasks: Rc<RefCell<Option<IdStore<TaskHandle<()>>>>>,
|
||||
}
|
||||
impl TaskSet {
|
||||
pub fn with<R>(&self, f: impl FnOnce(&mut IdStore<Box<dyn JoinHandle>>) -> R) -> Option<R> {
|
||||
pub fn downgrade(&self) -> WeakTaskSet { WeakTaskSet { tasks: Rc::downgrade(&self.tasks) } }
|
||||
pub fn with<R>(&self, f: impl FnOnce(&mut IdStore<TaskHandle<()>>) -> R) -> Option<R> {
|
||||
self.tasks.borrow_mut().as_mut().map(f)
|
||||
}
|
||||
pub fn abort_all(&self) {
|
||||
|
||||
@@ -105,7 +105,7 @@ impl Root {
|
||||
}
|
||||
match module {
|
||||
Ok(_) => Err(mk_errv(
|
||||
is("module used as constant").await,
|
||||
is!("module used as constant"),
|
||||
format!("{name} is a module, not a constant"),
|
||||
[pos],
|
||||
)),
|
||||
@@ -113,7 +113,7 @@ impl Root {
|
||||
ChildErrorKind::Private => panic!("public_only is false"),
|
||||
ChildErrorKind::Constant => panic!("Tree refers to constant not in table"),
|
||||
ChildErrorKind::Missing => Err(mk_errv(
|
||||
is("Constant does not exist").await,
|
||||
is!("Constant does not exist"),
|
||||
format!("{name} does not refer to a constant"),
|
||||
[pos],
|
||||
)),
|
||||
@@ -138,7 +138,7 @@ impl Root {
|
||||
Some(Ok(dest)) => return Ok(dest.target.to_vname().suffix(tail.iter().cloned())),
|
||||
Some(Err(dests)) => {
|
||||
return Err(mk_errv_floating(
|
||||
is("Ambiguous name").await,
|
||||
is!("Ambiguous name"),
|
||||
format!("{selector} could refer to {}", dests.iter().map(|ri| &ri.target).display("or")),
|
||||
));
|
||||
},
|
||||
@@ -153,7 +153,7 @@ impl Root {
|
||||
return Ok(VPath::new(cwd.iter().cloned()).name_with_suffix(selector.clone()));
|
||||
}
|
||||
Err(mk_errv_floating(
|
||||
is("Invalid name").await,
|
||||
is!("Invalid name"),
|
||||
format!(
|
||||
"{selector} doesn't refer to a module from {orig}, valid options are {}",
|
||||
options.iter().join(", ")
|
||||
@@ -203,7 +203,7 @@ impl Mod {
|
||||
pub async fn from_api(api: api::Module, cx: &mut TreeFromApicx<'_>) -> Self {
|
||||
let mut members = HashMap::new();
|
||||
for mem in api.members {
|
||||
let mem_name = es(mem.name).await;
|
||||
let mem_name = es(mem.name).await.expect("env tree items should be statically interned");
|
||||
let vname = VPath::new(cx.path.iter().cloned()).name_with_suffix(mem_name.clone());
|
||||
let name = vname.to_sym().await;
|
||||
let (lazy, kind) = match mem.kind {
|
||||
@@ -284,7 +284,7 @@ impl Mod {
|
||||
}
|
||||
}
|
||||
}
|
||||
let conflicting_imports_msg = is("Conflicting imports").await;
|
||||
let conflicting_imports_msg = is!("Conflicting imports");
|
||||
for (key, values) in imports_by_name {
|
||||
if values.len() == 1 {
|
||||
let import = values.into_iter().next().unwrap();
|
||||
@@ -323,7 +323,7 @@ impl Mod {
|
||||
imports.insert(key, if values.len() == 1 { Ok(values[0].clone()) } else { Err(values) });
|
||||
}
|
||||
}
|
||||
let self_referential_msg = is("Self-referential import").await;
|
||||
let self_referential_msg = is!("Self-referential import");
|
||||
for (key, value) in imports.iter() {
|
||||
let Ok(import) = value else { continue };
|
||||
if import.target.strip_prefix(&path[..]).is_some_and(|t| t.starts_with(slice::from_ref(key)))
|
||||
|
||||
@@ -16,10 +16,10 @@ path = "src/lib.rs"
|
||||
async-event = "0.2.1"
|
||||
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
||||
async-once-cell = "0.5.4"
|
||||
chrono = "0.4.44"
|
||||
chrono = "0.4.45"
|
||||
futures = { version = "0.3.32", features = ["std"], default-features = false }
|
||||
hashbrown = "0.17.0"
|
||||
itertools = "0.14.0"
|
||||
hashbrown = "0.17.1"
|
||||
itertools = "0.15.0"
|
||||
never = "0.1.0"
|
||||
once_cell = "1.21.4"
|
||||
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
||||
@@ -30,12 +30,12 @@ orchid-extension = { version = "0.1.0", path = "../orchid-extension", features =
|
||||
"tokio",
|
||||
] }
|
||||
ordered-float = "5.3.0"
|
||||
pastey = "0.2.2"
|
||||
rust_decimal = "1.41.0"
|
||||
pastey = "0.2.3"
|
||||
rust_decimal = "1.42.1"
|
||||
subslice-offset = "0.1.1"
|
||||
substack = "1.1.1"
|
||||
tokio = { version = "1.52.1", features = ["full"] }
|
||||
unicode-segmentation = "1.13.2"
|
||||
tokio = { version = "1.52.3", features = ["full"] }
|
||||
unicode-segmentation = "1.13.3"
|
||||
|
||||
[dev-dependencies]
|
||||
test_executors = "0.4.1"
|
||||
@@ -28,7 +28,7 @@ impl Parser for LetLine {
|
||||
});
|
||||
return Err(err.await);
|
||||
};
|
||||
let Parsed { tail, .. } = expect_tok(tail, is("=").await).await?;
|
||||
let Parsed { tail, .. } = expect_tok(tail, is!("=")).await?;
|
||||
// Parse a mactree that cannot contain placeholders and push all reflexive
|
||||
// trees onto a vector to be returned separately and applied during execution
|
||||
pub struct LetParsStrat(Vec<(api::ReflexiveTree, SrcRange)>);
|
||||
|
||||
@@ -16,22 +16,22 @@ pub async fn lower(mt: &MacTree, args: Substack<'_, Sym>) -> GExpr {
|
||||
MacTok::Resolved(inner) => lower(inner, args).boxed_local().await,
|
||||
MacTok::Bottom(b) => bot(b.clone()),
|
||||
MacTok::Slot(_) => bot(mk_errv(
|
||||
is("Lowering intermediary slotted mactree").await,
|
||||
is!("Lowering intermediary slotted mactree"),
|
||||
"Found a Slot during lowering, which should only exist in temporary templates",
|
||||
[mt.pos()],
|
||||
)),
|
||||
MacTok::Ph(ph) => bot(mk_errv(
|
||||
is("Lowering placeholder").await,
|
||||
is!("Lowering placeholder"),
|
||||
format!("Found {ph} during lowering. Placeholders should only exist in macro patterns"),
|
||||
[mt.pos()],
|
||||
)),
|
||||
MacTok::S(Paren::Curly | Paren::Square, _) => bot(mk_errv(
|
||||
is("Lowering {...} or [...]").await,
|
||||
is!("Lowering {...} or [...]"),
|
||||
format!("Cannot lower this syntax, probable incorrect macro call: {}", fmt(mt).await),
|
||||
[mt.pos()],
|
||||
)),
|
||||
MacTok::S(Paren::Round, b) if b.items.is_empty() => bot(mk_errv(
|
||||
is("Cannot lower empty ()").await,
|
||||
is!("Cannot lower empty ()"),
|
||||
"Attempted to lower empty (). All expressions must have a value.",
|
||||
[mt.pos()],
|
||||
)),
|
||||
@@ -39,7 +39,7 @@ pub async fn lower(mt: &MacTree, args: Substack<'_, Sym>) -> GExpr {
|
||||
let MacTok::Name(n) = arg.tok() else {
|
||||
return {
|
||||
bot(mk_errv(
|
||||
is("Lowering lambda with complex param").await,
|
||||
is!("Lowering lambda with complex param"),
|
||||
format!(
|
||||
"In code after macros, lambda params can only be names, not {}",
|
||||
fmt(arg).await
|
||||
@@ -51,7 +51,7 @@ pub async fn lower(mt: &MacTree, args: Substack<'_, Sym>) -> GExpr {
|
||||
if body.items.is_empty() {
|
||||
return {
|
||||
bot(mk_errv(
|
||||
is("Lowering lambda with empty body").await,
|
||||
is!("Lowering lambda with empty body"),
|
||||
"Lambdas without a body are invalid, all expressions in Orchid must have a value",
|
||||
[mt.pos()],
|
||||
))
|
||||
|
||||
@@ -33,7 +33,7 @@ impl Parser for MacroLine {
|
||||
) -> OrcRes<Vec<ParsedLine>> {
|
||||
if exported {
|
||||
return Err(mk_errv(
|
||||
is("macros are always exported").await,
|
||||
is!("macros are always exported"),
|
||||
"The export keyword is forbidden here to avoid confusion\n\
|
||||
because macros are exported by default",
|
||||
[line.sr()],
|
||||
@@ -74,7 +74,7 @@ impl Parser for MacroLine {
|
||||
let lines = line_items(Snippet::new(prio_or_body, body)).await;
|
||||
let Some((kw_line, rule_lines)) = lines.split_first() else { return Ok(Vec::new()) };
|
||||
let mut keywords = Vec::new();
|
||||
let Parsed { tail: kw_tail, .. } = expect_tok(kw_line.tail, is("keywords").await).await?;
|
||||
let Parsed { tail: kw_tail, .. } = expect_tok(kw_line.tail, is!("keywords")).await?;
|
||||
for kw_tok in kw_tail.iter().filter(|kw| !kw.is_fluff()) {
|
||||
match kw_tok.as_name() {
|
||||
Some(kw) => {
|
||||
@@ -90,7 +90,7 @@ impl Parser for MacroLine {
|
||||
}
|
||||
let Some((macro_name, _)) = keywords.first().cloned() else {
|
||||
return Err(mk_errv(
|
||||
is("macro with no keywords").await,
|
||||
is!("macro with no keywords"),
|
||||
"Macros must define at least one macro of their own.",
|
||||
[kw_line.tail.sr()],
|
||||
));
|
||||
@@ -100,11 +100,11 @@ impl Parser for MacroLine {
|
||||
for (idx, line) in rule_lines.iter().enumerate().map(|(n, v)| (n as u32, v)) {
|
||||
let sr = line.tail.sr();
|
||||
let name = is(&format!("rule::{}::{}", macro_name, idx)).await;
|
||||
let Parsed { tail, .. } = expect_tok(line.tail, is("rule").await).await?;
|
||||
let arrow_token = is("=>").await;
|
||||
let Parsed { tail, .. } = expect_tok(line.tail, is!("rule")).await?;
|
||||
let arrow_token = is!("=>");
|
||||
let Some((pattern, body)) = tail.split_once(|tok| tok.is_kw(arrow_token.clone())) else {
|
||||
report(mk_errv(
|
||||
is("Missing => in rule").await,
|
||||
is!("Missing => in rule"),
|
||||
"Rule lines are of the form `rule ...pattern => ...body`",
|
||||
[line.tail.sr()],
|
||||
));
|
||||
@@ -119,7 +119,7 @@ impl Parser for MacroLine {
|
||||
sr: SrcRange,
|
||||
) -> Result<crate::MacTree, Self::Err> {
|
||||
Err(mk_errv(
|
||||
is("Unsupported token in macro pattern").await,
|
||||
is!("Unsupported token in macro pattern"),
|
||||
"Patterns don't support atoms and other complex token types",
|
||||
[sr],
|
||||
))
|
||||
@@ -162,7 +162,7 @@ impl Parser for MacroLine {
|
||||
comments: line.output.clone(),
|
||||
sr: sr.clone(),
|
||||
kind: ParsedLineKind::Rec(
|
||||
[Token::Name(is("let").await), Token::Name(name.clone()), Token::Name(is("=").await)]
|
||||
[Token::Name(is!("let")), Token::Name(name.clone()), Token::Name(is!("="))]
|
||||
.into_iter()
|
||||
.map(|t| t.at(sr.clone()))
|
||||
.chain(body_tt)
|
||||
|
||||
@@ -65,6 +65,7 @@ impl System for MacroSystemInst {
|
||||
sym!(pattern::ref),
|
||||
sym!(pattern::=>),
|
||||
sym!(std::fn::[|>]),
|
||||
sym!(std::fn::[$]),
|
||||
]
|
||||
}
|
||||
fn lexers(&self) -> Vec<LexerObj> { vec![&MacTreeLexer, &PhLexer] }
|
||||
|
||||
@@ -239,7 +239,7 @@ impl MacTok {
|
||||
impl Format for MacTok {
|
||||
async fn print<'a>(&'a self, c: &'a (impl FmtCx + ?Sized + 'a)) -> FmtUnit {
|
||||
match self {
|
||||
Self::Value(v) => tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("$({0b})")))
|
||||
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()
|
||||
// .unbounded("\\{0} {1l}")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::borrow::Cow;
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use itertools::{Itertools};
|
||||
use itertools::Itertools;
|
||||
use never::Never;
|
||||
use orchid_base::{OrcRes, Paren, clone, token_errv};
|
||||
use orchid_extension::gen_expr::new_atom;
|
||||
@@ -20,7 +20,6 @@ pub struct MacTreeLexer;
|
||||
impl Lexer for MacTreeLexer {
|
||||
const CHAR_FILTER: &'static [RangeInclusive<char>] = &['\''..='\''];
|
||||
async fn lex<'a>(tail: &'a str, lctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree)> {
|
||||
eprintln!("Lexing toktree");
|
||||
let Some(tail2) = tail.strip_prefix('\'') else {
|
||||
return Err(err_not_applicable().await);
|
||||
};
|
||||
@@ -53,8 +52,8 @@ impl Lexer for MacTreeLexer {
|
||||
flex: api::ReflexiveTree,
|
||||
sr: orchid_base::SrcRange,
|
||||
) -> Result<MacTree, Self::Err> {
|
||||
self.0.push(Cow::Owned(GenTok::Reflexive(FlexTree::Existing(flex)).at(sr)));
|
||||
todo!("Review the todo in macro_line and replicate it here")
|
||||
self.0.push(Cow::Owned(GenTok::Reflexive(FlexTree::Existing(flex)).at(sr.clone())));
|
||||
Ok(MacTok::Slot(SlotFillStrategy::Value).at(sr))
|
||||
}
|
||||
}
|
||||
let mut strat = Strat(Vec::new());
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::future::join_all;
|
||||
use futures::{StreamExt, stream};
|
||||
use itertools::Itertools;
|
||||
use never::Never;
|
||||
use orchid_base::{OrcRes, Sym, fmt, is, mk_errv};
|
||||
use orchid_base::{OrcRes, Sym, Variants, fmt, is, mk_errv, tl_cache};
|
||||
use orchid_extension::gen_expr::{bot, call, call_v, lam, new_atom};
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
use orchid_extension::{Atomic, ExecHandle, Expr, OwnedAtom, OwnedVariant, TAtom, ToExpr, exec};
|
||||
@@ -27,6 +29,13 @@ impl Atomic for MatcherAtom {
|
||||
impl OwnedAtom for MatcherAtom {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> std::borrow::Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
async fn print_atom<'a>(
|
||||
&'a self,
|
||||
c: &'a (impl orchid_base::FmtCx + ?Sized + 'a),
|
||||
) -> orchid_base::FmtUnit {
|
||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("Matcher[{0b}]({1b})")))
|
||||
.units([self.keys.iter().join(", ").into(), self.matcher.print_atom(c).await])
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn match_one(
|
||||
@@ -48,7 +57,10 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
||||
}),
|
||||
fun(true, "matcher", async |names: HomoTpl<TAtom<SymAtom>>, matcher: TAtom<MacTree>| {
|
||||
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.expect("SymAtom should keep the sym from going stale")
|
||||
}))
|
||||
.await,
|
||||
matcher: matcher.own().await,
|
||||
})
|
||||
}),
|
||||
@@ -61,7 +73,7 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
||||
},
|
||||
)
|
||||
.finish(),
|
||||
build_macro(None, ["match", "match_rule", "_row", "=>"])
|
||||
build_macro(None, ["match", "match_rule", "_row"])
|
||||
.rule(mactreev!("pattern::match" { "..$" rules 0 }), async |mut cx, [rules]| {
|
||||
let desugared = mactree!("l" "pattern::implicit_value" (
|
||||
"pattern::match" "pattern::implicit_value" {
|
||||
@@ -81,9 +93,10 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
||||
cx.recur_call(mactree!("pattern::_row" "push" line_mac.own().await)).await?;
|
||||
rule_atoms.push((matcher, body));
|
||||
}
|
||||
eprintln!("Before base case created");
|
||||
let base_case = lam(async |_| {
|
||||
bot(mk_errv(
|
||||
is("No branches match").await,
|
||||
is!("No branches match"),
|
||||
"None of the patterns matches this value",
|
||||
[rules.pos()],
|
||||
))
|
||||
@@ -91,11 +104,12 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
||||
.await
|
||||
.create()
|
||||
.await;
|
||||
eprintln!("after base case created");
|
||||
let match_chain = stream::iter(rule_atoms.into_iter().rev())
|
||||
.fold(mactree!("Val" base_case), async |tail, (mat, body)| {
|
||||
mactree!("l" "pattern::x" (
|
||||
"pattern::match_one"
|
||||
"Val" mat.ex();
|
||||
("push" mat.own().await.matcher)
|
||||
"pattern::x"
|
||||
"push" body.own().await;
|
||||
("push" tail;
|
||||
@@ -103,6 +117,7 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
||||
))
|
||||
})
|
||||
.await;
|
||||
eprintln!("after match chain created");
|
||||
Ok(mactree!("push" match_chain; "push" value))
|
||||
},
|
||||
)
|
||||
@@ -123,7 +138,7 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
||||
cx.recur_call(mactree!("pattern::match_rule" "push" pattern.clone())).await
|
||||
else {
|
||||
return Err(mk_errv(
|
||||
is("Invalid pattern").await,
|
||||
is!("Invalid pattern"),
|
||||
format!("Could not parse {} as a match pattern", fmt(&pattern).await),
|
||||
[pattern.pos()],
|
||||
));
|
||||
@@ -140,7 +155,7 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
|
||||
.rule(mactreev!("pattern::match_rule"("pattern::ref" "$" name)), async |_cx, [name]| {
|
||||
let MacTok::Name(name) = name.tok() else {
|
||||
return Err(mk_errv(
|
||||
is("pattern 'ref' requires a name to bind to").await,
|
||||
is!("pattern 'ref' requires a name to bind to"),
|
||||
format!(
|
||||
"'ref' was interpreted as a binding matcher, \
|
||||
but it was followed by {} instead of a name",
|
||||
|
||||
@@ -50,7 +50,7 @@ impl Lexer for PhLexer {
|
||||
match prio_s.parse::<u8>() {
|
||||
Ok(priority) => (tail, PhKind::Vector { at_least_one, priority }),
|
||||
Err(e) =>
|
||||
return Err(mk_errv(is("Invalid placeholder priority").await, e.to_string(), [
|
||||
return Err(mk_errv(is!("Invalid placeholder priority"), e.to_string(), [
|
||||
ctx.pos_tt(line, tail)
|
||||
])),
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ async fn resolve_seq(
|
||||
Err((lran, rran))
|
||||
}
|
||||
});
|
||||
let mac_conflict_tk = is("Macro conflict").await;
|
||||
let mac_conflict_tk = is!("Macro conflict");
|
||||
let error = conflict_sets
|
||||
.filter(|r| 1 < r.len())
|
||||
.map(|set| {
|
||||
|
||||
@@ -122,7 +122,7 @@ async fn mk_scalar(pattern: &MacTree) -> OrcRes<ScalMatcher> {
|
||||
MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(&body.items).boxed_local().await?)),
|
||||
MacTok::Lambda(..) => {
|
||||
return Err(mk_errv(
|
||||
is("Lambda in matcher").await,
|
||||
is!("Lambda in matcher"),
|
||||
"Lambdas can't be matched for, only generated in templates",
|
||||
[pattern.pos()],
|
||||
));
|
||||
@@ -150,7 +150,7 @@ mod test {
|
||||
let pattern = vec![
|
||||
ex(MacTok::Ph(Ph {
|
||||
kind: PhKind::Vector { priority: 0, at_least_one: false },
|
||||
name: is("::prefix").await,
|
||||
name: is!("::prefix"),
|
||||
}))
|
||||
.await,
|
||||
ex(MacTok::Name(sym!(prelude::do))).await,
|
||||
@@ -159,13 +159,13 @@ mod test {
|
||||
MacTreeSeq::new([
|
||||
ex(MacTok::Ph(Ph {
|
||||
kind: PhKind::Vector { priority: 0, at_least_one: false },
|
||||
name: is("expr").await,
|
||||
name: is!("expr"),
|
||||
}))
|
||||
.await,
|
||||
ex(MacTok::Name(sym!(prelude::;))).await,
|
||||
ex(MacTok::Ph(Ph {
|
||||
kind: PhKind::Vector { priority: 1, at_least_one: false },
|
||||
name: is("rest").await,
|
||||
name: is!("rest"),
|
||||
}))
|
||||
.await,
|
||||
]),
|
||||
@@ -173,7 +173,7 @@ mod test {
|
||||
.await,
|
||||
ex(MacTok::Ph(Ph {
|
||||
kind: PhKind::Vector { priority: 0, at_least_one: false },
|
||||
name: is("::suffix").await,
|
||||
name: is!("::suffix"),
|
||||
}))
|
||||
.await,
|
||||
];
|
||||
|
||||
@@ -22,12 +22,12 @@ impl Matcher {
|
||||
let first = pattern.first().expect("Empty pattern is not allowed");
|
||||
if vec_attrs(first).is_none() {
|
||||
let pos = first.pos();
|
||||
pattern.insert(0, MacTok::Ph(Ph { name: is("::before").await, kind }).at(pos));
|
||||
pattern.insert(0, MacTok::Ph(Ph { name: is!("::before"), kind }).at(pos));
|
||||
}
|
||||
let last = pattern.last().expect("first returned Some above");
|
||||
if vec_attrs(last).is_none() {
|
||||
let pos = last.pos();
|
||||
pattern.insert(0, MacTok::Ph(Ph { name: is("::after").await, kind }).at(pos));
|
||||
pattern.insert(0, MacTok::Ph(Ph { name: is!("::after"), kind }).at(pos));
|
||||
}
|
||||
Ok(Matcher { inner: mk_any(&pattern).await? })
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ pub async fn gen_functional_macro_lib() -> Vec<GenMember> {
|
||||
prefix("std::fn", [
|
||||
build_macro(Some(50), ["|>"])
|
||||
.rule(
|
||||
mactreev!("...$" lhs 0 "std::fn::|>" "$" fun "...$" rhs 0),
|
||||
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)
|
||||
},
|
||||
|
||||
@@ -26,11 +26,11 @@ pub async fn gen_result_macro_lib() -> Vec<GenMember> {
|
||||
mactreev!("pattern::match_rule" ( "std::result::ok" "...$" sub_pattern 0)),
|
||||
async |mut cx, [sub]| {
|
||||
let sub: TAtom<MatcherAtom> =
|
||||
cx.recur_call(mactree!("pattern::match_rule" "push" sub;)).await?;
|
||||
cx.recur_call(mactree!("pattern::match_rule" "push" sub)).await?;
|
||||
let sub = sub.own().await;
|
||||
let matcher = new_atom(MatcherAtom {
|
||||
keys: sub.keys,
|
||||
matcher: mactree!("std::option::is_some_body" "push" sub.matcher),
|
||||
matcher: mactree!("std::result::is_ok_body" "push" sub.matcher),
|
||||
});
|
||||
Ok(mactree!("Val" matcher))
|
||||
},
|
||||
@@ -39,11 +39,11 @@ pub async fn gen_result_macro_lib() -> Vec<GenMember> {
|
||||
mactreev!("pattern::match_rule" ( "std::result::err" "...$" sub_pattern 0)),
|
||||
async |mut cx, [sub]| {
|
||||
let sub: TAtom<MatcherAtom> =
|
||||
cx.recur_call(mactree!("pattern::match_rule" "push" sub;)).await?;
|
||||
cx.recur_call(mactree!("pattern::match_rule" "push" sub)).await?;
|
||||
let sub = sub.own().await;
|
||||
let matcher = new_atom(MatcherAtom {
|
||||
keys: sub.keys,
|
||||
matcher: mactree!("std::option::is_some_body" "push" sub.matcher),
|
||||
matcher: mactree!("std::result::is_err_body" "push" sub.matcher),
|
||||
});
|
||||
Ok(mactree!("Val" matcher))
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::any::type_name_of_val;
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
|
||||
@@ -40,7 +41,7 @@ impl OwnedAtom for MacroBodyArgCollector {
|
||||
if !self.args.is_empty() {
|
||||
eprintln!("This is an intermediary value. It should never be copied");
|
||||
}
|
||||
self.clone().call(arg).await
|
||||
self.clone().call(arg).await.to_gen().await
|
||||
}
|
||||
async fn call(mut self, arg: orchid_extension::Expr) -> GExpr {
|
||||
let atom = (TAtom::<MacTree>::downcast(arg.handle()).await).unwrap_or_else(|_| {
|
||||
@@ -124,7 +125,12 @@ impl MacroBuilder {
|
||||
let arr =
|
||||
argv.into_iter().collect_array::<N>().expect("argc should enforce the length");
|
||||
let body = body.clone();
|
||||
Box::pin(async move { body(rec, arr).await.map(new_atom).to_gen().await })
|
||||
Box::pin(async move {
|
||||
eprintln!("Entering {}", type_name_of_val(&body));
|
||||
let ret = body(rec, arr).await.map(new_atom).to_gen().await;
|
||||
eprintln!("exiting {}", type_name_of_val(&body));
|
||||
ret
|
||||
})
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
@@ -15,7 +15,7 @@ async fn bounds_error(
|
||||
args: impl IntoIterator<Item = usize>,
|
||||
) -> OrcErrv {
|
||||
mk_errv(
|
||||
is("Index out of bounds").await,
|
||||
is!("Index out of bounds"),
|
||||
format!("Selected {expected} from blob of len {}", blob.0.len()),
|
||||
get_arg_posv(args).await,
|
||||
)
|
||||
@@ -98,7 +98,7 @@ pub fn gen_binary_lib() -> Vec<GenMember> {
|
||||
}
|
||||
if 8 < len {
|
||||
return Err(mk_errv(
|
||||
is("Too many bytes for int conversion").await,
|
||||
is!("Too many bytes for int conversion"),
|
||||
format!("At most 8 bytes fit into an Int, requested {len}"),
|
||||
get_arg_posv(3..4).await,
|
||||
));
|
||||
@@ -124,7 +124,7 @@ pub fn gen_binary_lib() -> Vec<GenMember> {
|
||||
fun(true, "from_num", async |len: Int, le: Bool, val: Int| {
|
||||
if 8 < len.0 {
|
||||
return Err(mk_errv(
|
||||
is("Too many bytes for int conversion").await,
|
||||
is!("Too many bytes for int conversion"),
|
||||
format!("Ints are 8 bytes, attempted to write {} byte buffer", len.0),
|
||||
get_arg_posv(0..1).await,
|
||||
));
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::OrcString;
|
||||
pub fn gen_bottom_lib() -> Vec<GenMember> {
|
||||
prefix("std", [
|
||||
fun(true, "panic", async |msg: OrcString| {
|
||||
bot(mk_errv(is("Panic!").await, &*msg.get_string().await, get_arg_posv([0, 1]).await))
|
||||
bot(mk_errv(is!("Panic!"), &*msg.get_string().await, get_arg_posv([0, 1]).await))
|
||||
}),
|
||||
fun(true, "seq", async |precond: Expr, value: Expr| seq(precond, value)),
|
||||
])
|
||||
|
||||
@@ -49,7 +49,7 @@ impl Supports<ProtocolMethod> for Int {
|
||||
match req {
|
||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, sym!(std::number::Int).to_api()).await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||
let name = Sym::from_api(key).await;
|
||||
let name = Sym::from_api(key).await.expect("Protocol name should be statically interned");
|
||||
let val = if name == sym!(std::ops::add) {
|
||||
sym!(std::number::add)
|
||||
} else if name == sym!(std::ops::sub) {
|
||||
@@ -108,7 +108,7 @@ impl Supports<ProtocolMethod> for Float {
|
||||
match req {
|
||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, sym!(std::number::Float).to_api()).await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||
let name = Sym::from_api(key).await;
|
||||
let name = Sym::from_api(key).await.expect("Protocol name should be statically interned");
|
||||
let val = if name == sym!(std::ops::add) {
|
||||
sym!(std::number::add)
|
||||
} else if name == sym!(std::ops::sub) {
|
||||
|
||||
@@ -45,7 +45,7 @@ pub fn gen_num_lib() -> Vec<GenMember> {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return Err(mk_errv(
|
||||
is("Float out of range").await,
|
||||
is!("Float out of range"),
|
||||
format!("{f} is not representable as an integer"),
|
||||
[get_arg(0).pos().await],
|
||||
));
|
||||
|
||||
@@ -19,7 +19,7 @@ impl Lexer for SubscriptLexer {
|
||||
};
|
||||
let new_tail = &tail[name_len + 1..];
|
||||
Ok((new_tail, [
|
||||
GenTok::Name(is(".").await).at(lctx.pos_lt(1, &tail[1..])),
|
||||
GenTok::Name(is!(".")).at(lctx.pos_lt(1, &tail[1..])),
|
||||
GenTok::NewExpr(new_atom(IntStrAtom(is(&tail[1..name_len + 1]).await)))
|
||||
.at(lctx.pos_tt(&tail[1..], new_tail)),
|
||||
]))
|
||||
|
||||
@@ -62,7 +62,7 @@ pub fn gen_option_lib() -> Vec<GenMember> {
|
||||
fun(true, "expect", async |opt: OrcOpt<Expr>, msg: OrcString| match opt {
|
||||
OrcOpt(Some(ex)) => Ok::<Expr, _>(ex),
|
||||
OrcOpt(None) => Err(mk_errv(
|
||||
is("Unwrapped std::option::none").await,
|
||||
is!("Unwrapped std::option::none"),
|
||||
msg.get_string().await.as_str(),
|
||||
get_arg_posv([0]).await,
|
||||
)),
|
||||
|
||||
@@ -16,7 +16,7 @@ pub async fn parse_impls(
|
||||
Token::S(Paren::Round, body) => line_items(Snippet::new(body_tt, body)).await,
|
||||
Token::S(ptyp, _) => {
|
||||
return Err(mk_errv(
|
||||
is("Incorrect paren type").await,
|
||||
is!("Incorrect paren type"),
|
||||
format!("Expected () block, found {ptyp}"),
|
||||
[body_tt.sr().pos()],
|
||||
));
|
||||
@@ -29,7 +29,7 @@ pub async fn parse_impls(
|
||||
},
|
||||
};
|
||||
for Parsed { tail: line, output: comments } in body {
|
||||
if let Ok(Parsed { tail, .. }) = expect_tok(line, is("impl").await).await {
|
||||
if let Ok(Parsed { tail, .. }) = expect_tok(line, is!("impl")).await {
|
||||
let Parsed { tail, output: name_tt } = parse_multiname(tail).await?;
|
||||
let (name, name_sr) = match name_tt.into_iter().at_most_one() {
|
||||
Ok(None) => panic!("multiname is always at least one name"),
|
||||
@@ -37,38 +37,34 @@ pub async fn parse_impls(
|
||||
(n.clone().mspath().to_sym().await, sr.clone()),
|
||||
Ok(Some(Import { name: None, sr, .. })) => {
|
||||
return Err(mk_errv(
|
||||
is("impl line with globstar").await,
|
||||
is!("impl line with globstar"),
|
||||
"::* is not permitted in a protocol impl",
|
||||
[sr.pos()],
|
||||
));
|
||||
},
|
||||
Err(e) => {
|
||||
return Err(mk_errv(
|
||||
is("Impl line with multiple protocol names").await,
|
||||
is!("Impl line with multiple protocol names"),
|
||||
"::() is not permitted in a protocol impl",
|
||||
e.map(|i| i.sr.pos()),
|
||||
));
|
||||
},
|
||||
};
|
||||
let Parsed { tail, .. } = expect_tok(tail, is("as").await).await?;
|
||||
let Parsed { tail, .. } = expect_tok(tail, is!("as")).await?;
|
||||
let cnst_name = is(&format!("{}{}", lines.len(), name.iter().join("__"))).await;
|
||||
lines.push(ParsedLine {
|
||||
comments,
|
||||
sr: line.sr(),
|
||||
kind: ParsedLineKind::Rec(Vec::from_iter(chain![
|
||||
[Token::Name(is("let").await).at(line.sr())],
|
||||
[Token::Name(is!("let")).at(line.sr())],
|
||||
[Token::Name(cnst_name.clone()).at(name_sr)],
|
||||
[Token::Name(is("=").await).at(line.sr())],
|
||||
[Token::Name(is!("=")).at(line.sr())],
|
||||
tail.iter().cloned(),
|
||||
])),
|
||||
});
|
||||
impls.push((name, cnst_name));
|
||||
} else {
|
||||
lines.push(ParsedLine {
|
||||
sr: line.sr(),
|
||||
comments,
|
||||
kind: ParsedLineKind::Rec(line.to_vec()),
|
||||
});
|
||||
lines.push(ParsedLine { sr: line.sr(), comments, kind: ParsedLineKind::Rec(line.to_vec()) });
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -23,7 +23,7 @@ impl Parser for AsProtoParser {
|
||||
expect_end(tail).await?;
|
||||
if exported {
|
||||
return Err(mk_errv(
|
||||
is("Exported internal line").await,
|
||||
is!("Exported internal line"),
|
||||
"as_proto cannot be exported, the type shares the enclosing module's visibility",
|
||||
[line.sr().pos()],
|
||||
));
|
||||
@@ -32,7 +32,7 @@ impl Parser for AsProtoParser {
|
||||
let mut impls = Vec::new();
|
||||
parse_impls(&pcx, &mut lines, &mut impls, body_tt).await?;
|
||||
let id = pcx.module();
|
||||
let proto_tag_name = is("__protocol_tag__").await;
|
||||
let proto_tag_name = is!("__protocol_tag__");
|
||||
let proto_tag_path = id.suffix([proto_tag_name.clone()]).await;
|
||||
let mut new_impls = HashMap::new();
|
||||
for (k, v) in impls {
|
||||
@@ -41,7 +41,7 @@ impl Parser for AsProtoParser {
|
||||
lines.push(ParsedLine::cnst(&line.sr(), &cmts, true, proto_tag_name, vec![
|
||||
x_tok(new_atom(Tag { id, impls: Rc::new(new_impls) })).await.at(line.sr()),
|
||||
]));
|
||||
lines.push(ParsedLine::cnst(&line.sr(), [], false, is("resolve").await, vec![
|
||||
lines.push(ParsedLine::cnst(&line.sr(), [], false, is!("resolve"), vec![
|
||||
x_tok(call(sym!(std::protocol::resolve), proto_tag_path)).await.at(line.sr()),
|
||||
]));
|
||||
Ok(lines)
|
||||
@@ -60,7 +60,7 @@ impl Parser for ProtoParser {
|
||||
) -> OrcRes<Vec<ParsedLine>> {
|
||||
let Parsed { output: name_tt, tail } = try_pop_no_fluff(line).await?;
|
||||
let Token::Name(name) = &name_tt.tok else {
|
||||
return Err(mk_errv(is("missing name for type").await, "A type needs a name", [name_tt
|
||||
return Err(mk_errv(is!("missing name for type"), "A type needs a name", [name_tt
|
||||
.sr()
|
||||
.pos()]));
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ impl Parser for AsTypeParser {
|
||||
expect_end(tail).await?;
|
||||
if exported {
|
||||
return Err(mk_errv(
|
||||
is("Exported internal line").await,
|
||||
is!("Exported internal line"),
|
||||
"as_type cannot be exported, the type shares the enclosing module's visibility",
|
||||
[line.sr().pos()],
|
||||
));
|
||||
@@ -32,7 +32,7 @@ impl Parser for AsTypeParser {
|
||||
let mut impls = Vec::new();
|
||||
parse_impls(&ctx, &mut lines, &mut impls, body_tt).await?;
|
||||
let id = ctx.module();
|
||||
let type_tag_name = is("__type_tag__").await;
|
||||
let type_tag_name = is!("__type_tag__");
|
||||
let type_tag_path = id.suffix([type_tag_name.clone()]).await;
|
||||
let mut new_impls = HashMap::new();
|
||||
for (k, v) in impls {
|
||||
@@ -43,11 +43,11 @@ impl Parser for AsTypeParser {
|
||||
x_tok(new_atom(Tag { id, impls: Rc::new(new_impls) })).await.at(line.sr()),
|
||||
]));
|
||||
let type_tag_path_1 = type_tag_path.clone();
|
||||
lines.push(ParsedLine::cnst(&line.sr(), [], false, is("wrap").await, vec![
|
||||
lines.push(ParsedLine::cnst(&line.sr(), [], false, is!("wrap"), vec![
|
||||
x_tok(call(sym!(std::protocol::wrap), type_tag_path_1)).await.at(line.sr()),
|
||||
]));
|
||||
let type_tag_path_1 = type_tag_path.clone();
|
||||
lines.push(ParsedLine::cnst(&line.sr(), [], false, is("unwrap").await, vec![
|
||||
lines.push(ParsedLine::cnst(&line.sr(), [], false, is!("unwrap"), vec![
|
||||
x_tok(call(sym!(std::protocol::unwrap), type_tag_path_1)).await.at(line.sr()),
|
||||
]));
|
||||
Ok(lines)
|
||||
@@ -66,7 +66,7 @@ impl Parser for TypeParser {
|
||||
) -> OrcRes<Vec<ParsedLine>> {
|
||||
let Parsed { output: name_tt, tail } = try_pop_no_fluff(line).await?;
|
||||
let Token::Name(name) = &name_tt.tok else {
|
||||
return Err(mk_errv(is("missing name for type").await, "A type needs a name", [name_tt
|
||||
return Err(mk_errv(is!("missing name for type"), "A type needs a name", [name_tt
|
||||
.sr()
|
||||
.pos()]));
|
||||
};
|
||||
|
||||
@@ -43,10 +43,10 @@ impl Supports<ProtocolMethod> for Tag {
|
||||
) -> std::io::Result<orchid_base::Receipt> {
|
||||
match req {
|
||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, self.id.to_api()).await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) =>
|
||||
hand
|
||||
.reply(req, self.impls.get(&Sym::from_api(key).await).map(|expr| expr.handle().ticket()))
|
||||
.await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||
let key = Sym::from_api(key).await.expect("All impl keys should be statically interned");
|
||||
hand.reply(req, self.impls.get(&key).map(|expr| expr.handle().ticket())).await
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,14 +99,14 @@ impl Supports<ProtocolMethod> for Tagged {
|
||||
pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes<Expr> {
|
||||
let Some(proto_id) = proto.call(GetTagId).await else {
|
||||
return Err(mk_errv(
|
||||
is("Not a protocol").await,
|
||||
is!("Not a protocol"),
|
||||
format!("Protocol ({}) does not have a tag ID", fmt(&proto).await),
|
||||
[proto.pos()],
|
||||
));
|
||||
};
|
||||
let Some(impl_val_opt) = receiver.call(GetImpl(proto_id)).await else {
|
||||
return Err(mk_errv(
|
||||
is("Receiver not tagged").await,
|
||||
is!("Receiver not tagged"),
|
||||
format!("The receiver ({}) does not have a type tag", fmt(&receiver).await),
|
||||
[receiver.pos()],
|
||||
));
|
||||
@@ -116,14 +116,14 @@ pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes<Expr>
|
||||
}
|
||||
let Some(type_id) = receiver.call(GetTagId).await else {
|
||||
return Err(mk_errv(
|
||||
is("Incorrect protocols implementation in extension").await,
|
||||
is!("Incorrect protocols implementation in extension"),
|
||||
format!("The receiver ({}) provides an impl table but no tag ID", fmt(&receiver).await),
|
||||
[receiver.pos()],
|
||||
));
|
||||
};
|
||||
let Some(impl_val_opt) = proto.call(GetImpl(type_id)).await else {
|
||||
return Err(mk_errv(
|
||||
is("Incorrect protocols implementation in extension").await,
|
||||
is!("Incorrect protocols implementation in extension"),
|
||||
format!("Protocol ({}) provides a tag ID but no impl table", fmt(&proto).await),
|
||||
[receiver.pos()],
|
||||
));
|
||||
@@ -131,11 +131,11 @@ pub async fn get_impl(receiver: ForeignAtom, proto: ForeignAtom) -> OrcRes<Expr>
|
||||
if let Some(impl_val) = impl_val_opt {
|
||||
return Ok(Expr::deserialize(impl_val).await);
|
||||
}
|
||||
return Err(mk_errv(
|
||||
is("Implementation not found").await,
|
||||
format!("Protocol {} is not implemented for {}", ev(proto_id).await, ev(type_id).await),
|
||||
Err(mk_errv(
|
||||
is!("Implementation not found"),
|
||||
format!("Protocol {:?} is not implemented for {:?}", ev(proto_id).await, ev(type_id).await),
|
||||
[receiver.pos(), proto.pos()],
|
||||
));
|
||||
))
|
||||
}
|
||||
|
||||
pub fn gen_protocol_lib() -> Vec<GenMember> {
|
||||
@@ -153,7 +153,7 @@ pub fn gen_protocol_lib() -> Vec<GenMember> {
|
||||
Ok(own_val.value)
|
||||
} else {
|
||||
Err(mk_errv(
|
||||
is("Type mismatch").await,
|
||||
is!("Type mismatch"),
|
||||
format!("{} has type {}, expected {}", fmt(&value).await, own_val.tag.id, own_tag.id),
|
||||
[value.pos()],
|
||||
))
|
||||
@@ -254,7 +254,7 @@ pub fn resolver_for(proto: VName) -> impl AsyncFn(ForeignAtom) -> GExpr + Clone
|
||||
Some(val) => val,
|
||||
None => {
|
||||
let proto: ForeignAtom =
|
||||
h.exec(proto.clone().suffix([is("__protocol_tag__").await]).to_sym().await).await?;
|
||||
h.exec(proto.clone().suffix([is!("__protocol_tag__")]).to_sym().await).await?;
|
||||
*proto_cache.borrow_mut() = Some(proto.clone());
|
||||
proto
|
||||
},
|
||||
|
||||
@@ -32,8 +32,14 @@ impl OwnedAtom for RecordAtom {
|
||||
values
|
||||
}
|
||||
async fn deserialize(mut dctx: impl DeserializeCtx, refs: Self::Refs) -> Self {
|
||||
let keys =
|
||||
join_all(dctx.decode::<Vec<api::TStr>>().await.iter().map(|t| async { es(*t).await })).await;
|
||||
let keys = join_all(
|
||||
dctx
|
||||
.decode::<Vec<api::TStr>>()
|
||||
.await
|
||||
.iter()
|
||||
.map(|t| async { es(*t).await.expect("Server lost interned keys during serialization") }),
|
||||
)
|
||||
.await;
|
||||
RecordAtom(Rc::new(keys.into_iter().zip(refs).collect()))
|
||||
}
|
||||
|
||||
@@ -44,7 +50,7 @@ impl Supports<ProtocolMethod> for RecordAtom {
|
||||
match req {
|
||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, sym!(std::record::Record).to_api()).await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||
let name = Sym::from_api(key).await;
|
||||
let name = Sym::from_api(key).await.expect("Protocol name should be statically interned");
|
||||
let val = if name == sym!(std::ops::get) {
|
||||
sym!(std::record::get)
|
||||
} else if name == sym!(std::ops::set) {
|
||||
|
||||
@@ -23,7 +23,7 @@ pub fn gen_record_lib() -> Vec<GenMember> {
|
||||
match record.0.get(&key.0) {
|
||||
Some(val) => Ok(val.clone()),
|
||||
None => Err(mk_errv(
|
||||
is("Key not found in record").await,
|
||||
is!("Key not found in record"),
|
||||
format!("{} is not in this record, valid keys are {}", key.0, record.0.keys().join(", ")),
|
||||
get_arg_posv([0, 1]).await,
|
||||
)),
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::borrow::Cow;
|
||||
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
use orchid_api_traits::Request;
|
||||
use orchid_base::{NameLike, ReqHandleExt, Sym, es, is, mk_errv};
|
||||
use orchid_base::{NameLike, ReqHandleExt, Sym, is, mk_errv};
|
||||
use orchid_extension::gen_expr::new_atom;
|
||||
use orchid_extension::tree::{GenMember, fun, prefix};
|
||||
use orchid_extension::{
|
||||
@@ -52,10 +52,10 @@ pub async fn sym_expr(sym: Sym) -> Expr {
|
||||
pub async fn gen_sym_lib() -> Vec<GenMember> {
|
||||
prefix("std::refl::sym", [
|
||||
fun(true, "from_str", async move |str: TAtom<IntStrAtom>| {
|
||||
match Sym::parse(&es(*str).await).await {
|
||||
match Sym::parse(&str.e().await).await {
|
||||
Ok(sym) => Ok(new_atom(SymAtom(sym))),
|
||||
Err(_) => Err(mk_errv(
|
||||
is("Cannot parse sym from empty string").await,
|
||||
is!("Cannot parse sym from empty string"),
|
||||
"Empty string passed to std::refl::sym::from_str",
|
||||
[str.pos()],
|
||||
)),
|
||||
|
||||
@@ -69,7 +69,7 @@ pub fn gen_result_lib() -> Vec<GenMember> {
|
||||
fun(true, "expect", async |res: OrcResObj<Expr, Expr>, msg: OrcString| match res {
|
||||
OrcResObj(Ok(ex)) => Ok::<Expr, _>(ex),
|
||||
OrcResObj(Err(ex)) => Err(mk_errv(
|
||||
is("Unwrapped std::result::err").await,
|
||||
is!("Unwrapped std::result::err"),
|
||||
format!("Unexpected error {}: {}", fmt(&ex).await, msg.get_string().await.as_str()),
|
||||
get_arg_posv([0]).await,
|
||||
)),
|
||||
|
||||
@@ -97,25 +97,29 @@ impl System for StdSystem {
|
||||
xreq.reply(req, tk).await.unwrap()
|
||||
},
|
||||
StdReq::CreateRecord(ref req @ CreateRecord(ref items)) => {
|
||||
let values =
|
||||
join_all(items.iter().map(async |(k, v)| (es(*k).await, Expr::deserialize(*v).await)))
|
||||
.await;
|
||||
let values = join_all(items.iter().map(async |(k, v)| {
|
||||
(es(*k).await.expect("Dropped keys while CreateRecord"), Expr::deserialize(*v).await)
|
||||
}))
|
||||
.await;
|
||||
let rec = RecordAtom(Rc::new(values.into_iter().collect()));
|
||||
let tk = new_atom(rec).to_expr().await.serialize().await;
|
||||
xreq.reply(req, tk).await.unwrap()
|
||||
},
|
||||
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.expect("Dropped sym to be turned into SymAtom"));
|
||||
xreq.reply(req, new_atom(sym_atom).to_expr().await.serialize().await).await.unwrap()
|
||||
},
|
||||
StdReq::CreateTag(ref req @ CreateTag { name, ref impls }) => {
|
||||
let tag_atom = Tag {
|
||||
id: Sym::from_api(name).await,
|
||||
id: Sym::from_api(name).await.expect("Dropped type tag during CreateTag"),
|
||||
impls: Rc::new(
|
||||
join_all(
|
||||
(impls.iter())
|
||||
.map(|(k, v)| async { (Sym::from_api(*k).await, Expr::deserialize(*v).await) }),
|
||||
)
|
||||
join_all((impls.iter()).map(|(k, v)| async {
|
||||
(
|
||||
Sym::from_api(*k).await.expect("Dropped impl target tag during CreateTag"),
|
||||
Expr::deserialize(*v).await,
|
||||
)
|
||||
}))
|
||||
.await
|
||||
.into_iter()
|
||||
.collect(),
|
||||
|
||||
@@ -37,7 +37,7 @@ impl Supports<StartCommand> for ReadStreamCmd {
|
||||
let ret = 'ret: {
|
||||
let Some(read_res) = self.hand.call(ReadReq { limit: self.limit.clone() }).await else {
|
||||
break 'ret Err(mk_errv(
|
||||
is("Atom is not readable").await,
|
||||
is!("Atom is not readable"),
|
||||
format!("Expected a readable stream handle, found {}", fmt(&self.hand).await),
|
||||
[self.hand.pos()],
|
||||
));
|
||||
@@ -90,7 +90,7 @@ impl Supports<StartCommand> for WriteStreamCmd {
|
||||
};
|
||||
let cont = match result {
|
||||
None => bot(mk_errv(
|
||||
is("Not a writer").await,
|
||||
is!("Not a writer"),
|
||||
format!("{} cannot be written to", fmt(&self.hand).await),
|
||||
[self.hand.pos()],
|
||||
)),
|
||||
|
||||
@@ -27,7 +27,7 @@ pub fn gen_stream_lib() -> Vec<GenMember> {
|
||||
fun(true, "read_until", async |hand: ForeignAtom, delim: Int, cont: Expr| {
|
||||
let Ok(end) = delim.0.try_into() else {
|
||||
return Err(mk_errv(
|
||||
is("Byte out of range").await,
|
||||
is!("Byte out of range"),
|
||||
format!(
|
||||
"{} doesn't fit into a byte and cannot be used as a delimiter for reading",
|
||||
delim.0
|
||||
@@ -47,7 +47,7 @@ pub fn gen_stream_lib() -> Vec<GenMember> {
|
||||
Ok(None) =>
|
||||
Ok(call(cont, OrcResObj::<_, Never>(Ok(new_atom(BlobAtom(Rc::default()))))).await),
|
||||
Err(_) => Err(mk_errv(
|
||||
is("Length cannot be negative").await,
|
||||
is!("Length cannot be negative"),
|
||||
format!("{} is negative and cannot be used as a length", count.0),
|
||||
[get_arg(1).pos().await],
|
||||
)),
|
||||
|
||||
@@ -74,7 +74,7 @@ impl Supports<ProtocolMethod> for StrAtom {
|
||||
match req {
|
||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, sym!(std::string::StrAtom).to_api()).await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||
let name = Sym::from_api(key).await;
|
||||
let name = Sym::from_api(key).await.expect("Protocol name should be statically interned");
|
||||
let val = if name == sym!(std::ops::add) {
|
||||
sym!(std::string::concat)
|
||||
} else {
|
||||
@@ -86,11 +86,19 @@ impl Supports<ProtocolMethod> for StrAtom {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Coding)]
|
||||
pub struct AtomicIntStr(pub orchid_api::TStr);
|
||||
impl AtomicIntStr {
|
||||
pub async fn e(&self) -> IStr {
|
||||
es(self.0).await.expect("IntStrAtom should keep alive the string")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IntStrAtom(pub(crate) IStr);
|
||||
impl Atomic for IntStrAtom {
|
||||
type Variant = OwnedVariant;
|
||||
type Data = orchid_api::TStr;
|
||||
type Data = AtomicIntStr;
|
||||
fn reg_methods() -> MethodSetBuilder<Self> {
|
||||
MethodSetBuilder::new().handle::<ProtocolMethod>().handle::<ToStringMethod>()
|
||||
}
|
||||
@@ -100,7 +108,7 @@ impl From<IStr> for IntStrAtom {
|
||||
}
|
||||
impl OwnedAtom for IntStrAtom {
|
||||
type Refs = ();
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.0.to_api()) }
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(AtomicIntStr(self.0.to_api())) }
|
||||
async fn print_atom<'a>(&'a self, _: &'a (impl FmtCx + ?Sized + 'a)) -> FmtUnit {
|
||||
format!("{:?}i", &*self.0).into()
|
||||
}
|
||||
@@ -114,7 +122,7 @@ impl OwnedAtom for IntStrAtom {
|
||||
}
|
||||
impl TryFromExpr for IntStrAtom {
|
||||
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
|
||||
Ok(IntStrAtom(es(TAtom::<IntStrAtom>::try_from_expr(expr).await?.value).await))
|
||||
Ok(IntStrAtom(TAtom::<IntStrAtom>::try_from_expr(expr).await?.value.e().await))
|
||||
}
|
||||
}
|
||||
impl Supports<ToStringMethod> for IntStrAtom {
|
||||
@@ -128,7 +136,7 @@ impl Supports<ProtocolMethod> for IntStrAtom {
|
||||
ProtocolMethod::GetTagId(req) =>
|
||||
hand.reply(&req, sym!(std::string::IntStrAtom).to_api()).await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||
let name = Sym::from_api(key).await;
|
||||
let name = Sym::from_api(key).await.expect("Protocol name should be statically interned");
|
||||
let val = if name == sym!(std::ops::add) {
|
||||
sym!(std::string::concat)
|
||||
} else {
|
||||
@@ -152,7 +160,7 @@ pub enum OrcStringKind {
|
||||
impl OrcString {
|
||||
pub async fn get_string(&self) -> Rc<String> {
|
||||
match &self.kind {
|
||||
OrcStringKind::Int(tok) => es(**tok).await.rc(),
|
||||
OrcStringKind::Int(tok) => tok.e().await.rc(),
|
||||
OrcStringKind::Val(atom) => atom.call(StringGetValMethod).await,
|
||||
}
|
||||
}
|
||||
@@ -165,7 +173,7 @@ impl TryFromExpr for OrcString {
|
||||
}
|
||||
match TAtom::<IntStrAtom>::try_from_expr(expr).await {
|
||||
Ok(t) => Ok(OrcString { kind: OrcStringKind::Int(t) }),
|
||||
Err(e) => Err(mk_errv(is("A string was expected").await, "", e.pos_iter())),
|
||||
Err(e) => Err(mk_errv(is!("A string was expected"), "", e.pos_iter())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ impl StringError {
|
||||
pub async fn into_proj(self, path: &Sym, pos: u32) -> OrcErrv {
|
||||
let start = pos + self.pos;
|
||||
mk_errv(
|
||||
is("Failed to parse string").await,
|
||||
is!("Failed to parse string"),
|
||||
match self.kind {
|
||||
StringErrorKind::NotHex => "Expected a hex digit",
|
||||
StringErrorKind::BadCodePoint => "The specified number is not a Unicode code point",
|
||||
@@ -121,7 +121,6 @@ impl Lexer for StringLexer {
|
||||
};
|
||||
loop {
|
||||
if let Some(rest) = tail.strip_prefix('"') {
|
||||
eprintln!("From {} remaining tokens are {}", all.len(), rest.len());
|
||||
return Ok((
|
||||
rest,
|
||||
add_frag(ret, str_to_gen(&mut cur, tail, &mut errors, lctx).await).await,
|
||||
@@ -148,7 +147,7 @@ impl Lexer for StringLexer {
|
||||
tail = ch.as_str();
|
||||
} else {
|
||||
let range = lctx.pos(all)..lctx.pos("");
|
||||
return Err(mk_errv(is("No string end").await, "String never terminated with \"", [
|
||||
return Err(mk_errv(is!("No string end"), "String never terminated with \"", [
|
||||
SrcRange::new(range.clone(), lctx.src()),
|
||||
]));
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ pub fn gen_str_lib() -> Vec<GenMember> {
|
||||
let Some(s) = substr_iter.next() else {
|
||||
let str_len = str.graphemes(true).count();
|
||||
return Err(mk_errv(
|
||||
is("Index out of bounds").await,
|
||||
is!("Index out of bounds"),
|
||||
format!("Tried to select grapheme {start}+{len} from string that only has {str_len}"),
|
||||
[get_arg(0).pos().await, get_arg(1).pos().await, get_arg(2).pos().await],
|
||||
));
|
||||
@@ -108,7 +108,7 @@ pub fn gen_str_lib() -> Vec<GenMember> {
|
||||
let Some((i, _)) = str.grapheme_indices(true).nth(i.0.try_into().unwrap()) else {
|
||||
let len = str.graphemes(true).count();
|
||||
return Err(mk_errv(
|
||||
is("Index out of bounds").await,
|
||||
is!("Index out of bounds"),
|
||||
format!("Tried to split string at {}, it only has {} graphemes", i.0, len),
|
||||
[get_arg(0).pos().await, get_arg(1).pos().await],
|
||||
));
|
||||
@@ -127,7 +127,7 @@ pub fn gen_str_lib() -> Vec<GenMember> {
|
||||
let Some(s) = str.graphemes(true).nth(i.0.try_into().unwrap()) else {
|
||||
let len = str.graphemes(true).count();
|
||||
return Err(mk_errv(
|
||||
is("Index out of bounds").await,
|
||||
is!("Index out of bounds"),
|
||||
format!("Tried to read grapheme {} from string, it only has {}", i.0, len),
|
||||
[get_arg(0).pos().await, get_arg(1).pos().await],
|
||||
));
|
||||
|
||||
@@ -56,7 +56,7 @@ impl Supports<ProtocolMethod> for Tuple {
|
||||
match req {
|
||||
ProtocolMethod::GetTagId(req) => hand.reply(&req, sym!(std::tuple).to_api()).await,
|
||||
ProtocolMethod::GetImpl(ref req @ GetImpl(key)) => {
|
||||
let name = Sym::from_api(key).await;
|
||||
let name = Sym::from_api(key).await.expect("Protocol name should be statically interned");
|
||||
let val = if name == sym!(std::ops::get) {
|
||||
sym!(std::tuple::get)
|
||||
} else if name == sym!(std::ops::set) {
|
||||
@@ -116,11 +116,11 @@ pub fn gen_tuple_lib() -> Vec<GenMember> {
|
||||
{
|
||||
return Ok(val.clone());
|
||||
}
|
||||
return Err(mk_errv(
|
||||
is("Tuple index out of bounds").await,
|
||||
Err(mk_errv(
|
||||
is!("Tuple index out of bounds"),
|
||||
format!("{} is out of bounds for Tuple{}", idx.0, tup.len()),
|
||||
[idx.pos()],
|
||||
));
|
||||
))
|
||||
}),
|
||||
fun(true, "set", async |tup: TAtom<Tuple>, idx: TAtom<Int>, val: Expr| {
|
||||
if let Ok(idx) = usize::try_from(idx.0) {
|
||||
@@ -130,11 +130,11 @@ pub fn gen_tuple_lib() -> Vec<GenMember> {
|
||||
return Ok(new_atom(Tuple(Rc::new(new_vec))));
|
||||
}
|
||||
}
|
||||
return Err(mk_errv(
|
||||
is("Tuple index out of bounds").await,
|
||||
Err(mk_errv(
|
||||
is!("Tuple index out of bounds"),
|
||||
format!("{} is out of bounds for Tuple{}", idx.0, tup.len()),
|
||||
[idx.pos()],
|
||||
));
|
||||
))
|
||||
}),
|
||||
fun(true, "len", async |tup: TAtom<Tuple>| {
|
||||
Int(tup.len().try_into().expect("Tuple was created with an Int length"))
|
||||
@@ -182,7 +182,7 @@ mod tpl_impls {
|
||||
let tpl = UntypedTuple::try_from_expr(expr.clone()).await?;
|
||||
let Some([$( [< $t:lower >], )*]) = tpl.0.iter().cloned().collect_array() else {
|
||||
return Err(mk_errv(
|
||||
is("Tuple arity mismatch").await,
|
||||
is!("Tuple arity mismatch"),
|
||||
format!("Expected a {}-ary tuple, found {}-ary", $len, tpl.0.len()),
|
||||
[expr.data().await.pos.clone()]
|
||||
));
|
||||
|
||||
+3
-3
@@ -8,11 +8,11 @@ authors = ["Lawrence Bethlenfalvy <lbfalvy@protonmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
||||
camino = "1.2.2"
|
||||
camino = "1.2.3"
|
||||
clap = { version = "4.6.1", features = ["derive", "env", "cargo"] }
|
||||
ctrlc = "3.5.2"
|
||||
futures = "0.3.32"
|
||||
itertools = "0.14.0"
|
||||
itertools = "0.15.0"
|
||||
never = "0.1.0"
|
||||
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
||||
orchid-base = { version = "0.1.0", path = "../orchid-base" }
|
||||
@@ -23,5 +23,5 @@ orchid-host = { version = "0.1.0", path = "../orchid-host", features = [
|
||||
] }
|
||||
stacker = "0.1.24"
|
||||
substack = "1.1.1"
|
||||
tokio = { version = "1.52.1", features = ["full"] }
|
||||
tokio = { version = "1.52.3", features = ["full"] }
|
||||
tokio-util = { version = "0.7.18", features = ["compat"] }
|
||||
+17
-27
@@ -1,5 +1,5 @@
|
||||
use never::Never;
|
||||
use orchid_base::{IStr, Logger, Receipt, ReqHandle};
|
||||
use orchid_base::{IStr, Logger, Receipt, ReqHandle, TokioSpawn};
|
||||
use orchid_extension::{self as ox, OrcReader, OrcWriter};
|
||||
use orchid_host::cmd_system::{CmdEvent, CmdRunner};
|
||||
use orchid_host::dylib::ext_dylib;
|
||||
@@ -23,15 +23,14 @@ use std::time::Duration;
|
||||
use async_fn_stream::try_stream;
|
||||
use camino::{Utf8Path, Utf8PathBuf};
|
||||
use clap::{Parser, Subcommand};
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::{FutureExt, Stream, TryStreamExt, io};
|
||||
use futures::{Stream, TryStreamExt, io};
|
||||
use itertools::Itertools;
|
||||
use orchid_base::local_interner::local_interner;
|
||||
use orchid_base::{
|
||||
FmtCxImpl, Snippet, SrcRange, Token, VPath, fmt, fmt_v, is, log, sym, take_first,
|
||||
try_with_reporter, ttv_fmt, with_interner, with_logger, with_reporter, with_stash,
|
||||
};
|
||||
use orchid_host::cx::{Cx, JoinHandle, Spawner};
|
||||
use orchid_host::cx::Cx;
|
||||
use orchid_host::execute::{ExecCx, ExecResult};
|
||||
use orchid_host::expr::ExprKind;
|
||||
use orchid_host::extension::Extension;
|
||||
@@ -42,7 +41,7 @@ use orchid_host::parsed::{ParsTokTree, ParsedModule};
|
||||
use orchid_host::subprocess::ext_command;
|
||||
use orchid_host::system::init_systems;
|
||||
use substack::Substack;
|
||||
use tokio::task::{LocalSet, spawn_local};
|
||||
use tokio::task::LocalSet;
|
||||
|
||||
use crate::parse_folder::parse_folder;
|
||||
use crate::repl::repl;
|
||||
@@ -102,7 +101,7 @@ pub struct Args {
|
||||
next_line_help = true,
|
||||
verbatim_doc_comment
|
||||
)]
|
||||
logs: Vec<String>,
|
||||
log: Vec<String>,
|
||||
/// Measure and report the timings of various events
|
||||
#[arg(long, action)]
|
||||
time: bool,
|
||||
@@ -238,7 +237,7 @@ fn parse_log_dest(dest: &str) -> orchid_api::LogStrategy {
|
||||
|
||||
fn get_logger(args: &Args) -> LoggerImpl {
|
||||
let mut logger = LoggerImpl::default();
|
||||
for cmd in &args.logs {
|
||||
for cmd in &args.log {
|
||||
match cmd.split_once(":") {
|
||||
None => {
|
||||
let strat = parse_log_dest(cmd);
|
||||
@@ -262,21 +261,6 @@ fn get_logger(args: &Args) -> LoggerImpl {
|
||||
logger
|
||||
}
|
||||
|
||||
struct JoinHandleImpl(tokio::task::JoinHandle<()>);
|
||||
impl JoinHandle for JoinHandleImpl {
|
||||
fn abort(&self) { self.0.abort() }
|
||||
fn join(self: Box<Self>) -> LocalBoxFuture<'static, ()> {
|
||||
Box::pin(async { self.0.await.unwrap() })
|
||||
}
|
||||
}
|
||||
|
||||
struct SpawnerImpl;
|
||||
impl Spawner for SpawnerImpl {
|
||||
fn spawn_obj(&self, delay: Duration, fut: LocalBoxFuture<'static, ()>) -> Box<dyn JoinHandle> {
|
||||
Box::new(JoinHandleImpl(spawn_local(tokio::time::sleep(delay).then(|()| fut))))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct StdIoSystem;
|
||||
impl ox::SystemCard for StdIoSystem {
|
||||
@@ -307,7 +291,11 @@ impl ox::System for StdIoSystem {
|
||||
}),
|
||||
ox::tree::fun(true, "get_stdout", async |cb: ox::Expr| {
|
||||
ox::cmd(async move || {
|
||||
Some(ox::gen_expr::call(cb.clone(), OrcWriter(tokio::io::stdout().compat_write())))
|
||||
eprintln!("get_output req");
|
||||
let rep =
|
||||
Some(ox::gen_expr::call(cb.clone(), OrcWriter(tokio::io::stdout().compat_write())));
|
||||
eprintln!("get_output rep");
|
||||
rep
|
||||
})
|
||||
}),
|
||||
ox::tree::fun(true, "get_stderr", async |cb: ox::Expr| {
|
||||
@@ -317,6 +305,7 @@ impl ox::System for StdIoSystem {
|
||||
}),
|
||||
])
|
||||
}
|
||||
async fn prelude(&self) -> Vec<orchid_base::Sym> { vec![sym!(stdio)] }
|
||||
}
|
||||
|
||||
async fn load_proj_if_set(
|
||||
@@ -340,7 +329,7 @@ async fn add_const_at(
|
||||
let prefix_sr = SrcRange::zw(pcx.src(), 0);
|
||||
let mut lexemes = lex(value.clone(), pcx).await.map_err(|e| e.to_string())?;
|
||||
writeln!(log("debug"), "lexed: {}", fmt_v::<ParsTokTree>(lexemes.iter()).await.join(" ")).await;
|
||||
let prefix = [is("export").await, is("let").await, name, is("=").await];
|
||||
let prefix = [is!("export"), is!("let"), name, is!("=")];
|
||||
lexemes.splice(0..0, prefix.map(|n| Token::Name(n).at(prefix_sr.clone())));
|
||||
let snippet = Snippet::new(&lexemes[0], &lexemes);
|
||||
let items = try_with_reporter(parse_item(pcx, Substack::Bottom, vec![], snippet))
|
||||
@@ -363,7 +352,8 @@ fn main() -> io::Result<ExitCode> {
|
||||
let logger = get_logger(&args);
|
||||
let logger2 = logger.clone();
|
||||
unsafe { STARTUP = Some(Instant::now()) };
|
||||
let cx = Cx::new(SpawnerImpl, logger2);
|
||||
let spawner = Rc::new(TokioSpawn::new());
|
||||
let cx = Cx::new(spawner.clone(), logger2);
|
||||
let (signal_end_main, on_end_main) = futures::channel::oneshot::channel();
|
||||
let cx1 = cx.clone();
|
||||
local_set.spawn_local(async move {
|
||||
@@ -430,7 +420,7 @@ fn main() -> io::Result<ExitCode> {
|
||||
add_const_at(
|
||||
&mut root,
|
||||
&pcx.with_src(sym!(usercode)),
|
||||
is("entrypoint").await,
|
||||
is!("entrypoint"),
|
||||
is(code).await,
|
||||
)
|
||||
.await?;
|
||||
@@ -465,7 +455,7 @@ fn main() -> io::Result<ExitCode> {
|
||||
add_const_at(
|
||||
&mut root,
|
||||
&pcx.with_src(sym!(usercode)),
|
||||
is("entrypoint").await,
|
||||
is!("entrypoint"),
|
||||
is(main).await,
|
||||
)
|
||||
.await?;
|
||||
|
||||
+4
-4
@@ -71,7 +71,7 @@ pub async fn repl(args: &Args, extensions: &[Extension], cx: Cx) -> Result<(), S
|
||||
let add_imports = |items: &mut Vec<Item>, imports: &[Import]| {
|
||||
items.extend(imports.iter().map(|import| Item::new(import.sr.clone(), import.clone())));
|
||||
};
|
||||
if discr.is_kw(is("import").await) {
|
||||
if discr.is_kw(is!("import")) {
|
||||
let import_lines = process_lexemes(&lexemes).await?;
|
||||
imports.extend(import_lines.into_iter().map(|it| match it.kind {
|
||||
ItemKind::Import(imp) => imp,
|
||||
@@ -79,9 +79,9 @@ pub async fn repl(args: &Args, extensions: &[Extension], cx: Cx) -> Result<(), S
|
||||
}));
|
||||
continue;
|
||||
}
|
||||
let mut prefix = vec![is("export").await];
|
||||
if !discr.is_kw(is("let").await) {
|
||||
prefix.extend([is("let").await, name.clone(), is("=").await]);
|
||||
let mut prefix = vec![is!("export")];
|
||||
if !discr.is_kw(is!("let")) {
|
||||
prefix.extend([is!("let"), name.clone(), is!("=")]);
|
||||
}
|
||||
lexemes.splice(0..0, prefix.into_iter().map(|n| Token::Name(n).at(prefix_sr.clone())));
|
||||
let mut new_lines = process_lexemes(&lexemes).await?;
|
||||
|
||||
@@ -8,13 +8,13 @@ repository = "https://git.lbfalvy.com/Orchid/orchid"
|
||||
homepage = "https://git.lbfalvy.com/Orchid/orchid"
|
||||
|
||||
[dev-dependencies]
|
||||
futures = "0.3.31"
|
||||
itertools = "0.14.0"
|
||||
futures = "0.3.32"
|
||||
itertools = "0.15.0"
|
||||
rand = "0.10.1"
|
||||
rand_chacha = "0.10.0"
|
||||
test_executors = "0.4.1"
|
||||
orchid-async-utils = { version = "0.1.0", path = "../orchid-async-utils" }
|
||||
|
||||
[dependencies]
|
||||
futures-io = "0.3.31"
|
||||
itertools = "0.14.0"
|
||||
futures-io = "0.3.32"
|
||||
itertools = "0.15.0"
|
||||
@@ -7,6 +7,7 @@ use std::path::Path;
|
||||
use crate::Args;
|
||||
|
||||
pub fn check_api_refs(_args: &Args) -> io::Result<()> {
|
||||
let mut any_failed = false;
|
||||
walk_wsp(&mut |_| Ok(true), &mut |file| {
|
||||
if file.path().extension() == Some(OsStr::new("rs")) && file.file_name() != "lib.rs" {
|
||||
let mut contents = String::new();
|
||||
@@ -20,10 +21,15 @@ pub fn check_api_refs(_args: &Args) -> io::Result<()> {
|
||||
continue;
|
||||
}
|
||||
let dname = file.path().to_string_lossy().to_string();
|
||||
eprintln!("orchid_api imported in {dname}:{}:{}", l + 1, c + 1)
|
||||
eprintln!("orchid_api imported in {dname}:{}:{}", l + 1, c + 1);
|
||||
any_failed |= true;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
if any_failed {
|
||||
Err(io::Error::other("orchid_api imports deep in submodules were found"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Loaded 100 of 102 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user