in midst of refactor
This commit is contained in:
674
Cargo.lock
generated
674
Cargo.lock
generated
@@ -14,92 +14,18 @@ dependencies = [
|
|||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aho-corasick"
|
|
||||||
version = "1.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "allocator-api2"
|
name = "allocator-api2"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstream"
|
|
||||||
version = "0.6.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540"
|
|
||||||
dependencies = [
|
|
||||||
"anstyle",
|
|
||||||
"anstyle-parse",
|
|
||||||
"anstyle-query",
|
|
||||||
"anstyle-wincon",
|
|
||||||
"colorchoice",
|
|
||||||
"utf8parse",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle"
|
|
||||||
version = "1.0.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle-parse"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
|
||||||
dependencies = [
|
|
||||||
"utf8parse",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle-query"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
|
||||||
dependencies = [
|
|
||||||
"windows-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "anstyle-wincon"
|
|
||||||
version = "3.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
|
||||||
dependencies = [
|
|
||||||
"anstyle",
|
|
||||||
"windows-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "atty"
|
|
||||||
version = "0.2.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
|
||||||
dependencies = [
|
|
||||||
"hermit-abi",
|
|
||||||
"libc",
|
|
||||||
"winapi 0.3.9",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "1.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
@@ -109,22 +35,6 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bound"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6021ae095f16f54aaae093f4c723700430e71eab731d3b0a07fc8fe258fd5110"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bstr"
|
|
||||||
version = "1.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -132,70 +42,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "convert_case"
|
||||||
version = "4.5.1"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
|
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||||
dependencies = [
|
|
||||||
"clap_builder",
|
|
||||||
"clap_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap_builder"
|
|
||||||
version = "4.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
|
|
||||||
dependencies = [
|
|
||||||
"anstream",
|
|
||||||
"anstyle",
|
|
||||||
"clap_lex",
|
|
||||||
"strsim",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap_derive"
|
|
||||||
version = "4.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
|
|
||||||
dependencies = [
|
|
||||||
"heck",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.50",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "clap_lex"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "colorchoice"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "const_format"
|
|
||||||
version = "0.2.32"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673"
|
|
||||||
dependencies = [
|
|
||||||
"const_format_proc_macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "const_format_proc_macros"
|
|
||||||
version = "0.2.32"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-xid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
@@ -206,31 +56,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-deque"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-epoch",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-epoch"
|
|
||||||
version = "0.9.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-utils"
|
|
||||||
version = "0.8.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
@@ -241,6 +66,65 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.20.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.20.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2 1.0.78",
|
||||||
|
"quote 1.0.35",
|
||||||
|
"strsim",
|
||||||
|
"syn 2.0.52",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.20.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote 1.0.35",
|
||||||
|
"syn 2.0.52",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_destructure"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc38e2e2eefd927b5804046cac648ab2e0a46d3f038a4b82f1e0aa149a1cbccd"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 0.4.30",
|
||||||
|
"quote 0.6.13",
|
||||||
|
"syn 0.15.44",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more"
|
||||||
|
version = "0.99.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||||
|
dependencies = [
|
||||||
|
"convert_case",
|
||||||
|
"proc-macro2 1.0.78",
|
||||||
|
"quote 1.0.35",
|
||||||
|
"rustc_version",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@@ -253,9 +137,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dyn-clone"
|
name = "dyn-clone"
|
||||||
version = "1.0.16"
|
version = "1.0.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d"
|
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
@@ -263,6 +147,12 @@ version = "1.10.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
@@ -273,19 +163,6 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "globset"
|
|
||||||
version = "0.4.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"bstr",
|
|
||||||
"log",
|
|
||||||
"regex-automata",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.3"
|
version = "0.14.3"
|
||||||
@@ -297,30 +174,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "ident_case"
|
||||||
version = "0.4.1"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hermit-abi"
|
|
||||||
version = "0.1.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "intern-all"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "20c9bf7d7b0572f7b4398fddc93ac1a200a92d1ba319a27dac04649b2223c0f6"
|
|
||||||
dependencies = [
|
|
||||||
"hashbrown",
|
|
||||||
"lazy_static",
|
|
||||||
"trait-set",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
@@ -331,16 +188,6 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "kernel32-sys"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
|
||||||
dependencies = [
|
|
||||||
"winapi 0.2.8",
|
|
||||||
"winapi-build",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -353,18 +200,6 @@ version = "0.2.153"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "log"
|
|
||||||
version = "0.4.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memchr"
|
|
||||||
version = "2.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "never"
|
name = "never"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -380,12 +215,6 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "numtoa"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
@@ -393,29 +222,74 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orchidlang"
|
name = "orchid-api"
|
||||||
version = "0.3.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bound",
|
"derive_more",
|
||||||
"clap",
|
"orchid-api-derive",
|
||||||
"const_format",
|
"orchid-api-traits",
|
||||||
|
"ordered-float",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "orchid-api-derive"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"itertools",
|
||||||
|
"orchid-api-traits",
|
||||||
|
"proc-macro2 1.0.78",
|
||||||
|
"quote 1.0.35",
|
||||||
|
"syn 2.0.52",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "orchid-api-traits"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ordered-float",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "orchid-base"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"derive_destructure",
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"intern-all",
|
|
||||||
"itertools",
|
"itertools",
|
||||||
|
"lazy_static",
|
||||||
"never",
|
"never",
|
||||||
"once_cell",
|
"orchid-api",
|
||||||
|
"orchid-api-derive",
|
||||||
|
"orchid-api-traits",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"paste",
|
|
||||||
"rayon",
|
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"substack",
|
"substack",
|
||||||
"take_mut",
|
|
||||||
"termsize",
|
|
||||||
"trait-set",
|
"trait-set",
|
||||||
"unicode-segmentation",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "orchid-extension"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"orchid-api",
|
||||||
|
"orchid-api-traits",
|
||||||
|
"orchid-base",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "orchid-host"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "orchid-std"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "orcx"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-float"
|
name = "ordered-float"
|
||||||
version = "4.2.0"
|
version = "4.2.0"
|
||||||
@@ -426,10 +300,13 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paste"
|
name = "proc-macro2"
|
||||||
version = "1.0.14"
|
version = "0.4.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
@@ -440,72 +317,29 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "0.6.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 0.4.30",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.35"
|
version = "1.0.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2 1.0.78",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon"
|
|
||||||
version = "1.8.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
"rayon-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon-core"
|
|
||||||
version = "1.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-deque",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.2.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_termios"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-automata"
|
|
||||||
version = "0.4.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.8.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed"
|
name = "rust-embed"
|
||||||
version = "8.2.0"
|
version = "8.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a82c0bbc10308ed323529fd3c1dce8badda635aa319a5ff0e6466f33b8101e3f"
|
checksum = "fb78f46d0066053d16d4ca7b898e9343bc3530f71c61d5ad84cd404ada068745"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rust-embed-impl",
|
"rust-embed-impl",
|
||||||
"rust-embed-utils",
|
"rust-embed-utils",
|
||||||
@@ -514,28 +348,36 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed-impl"
|
name = "rust-embed-impl"
|
||||||
version = "8.2.0"
|
version = "8.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6227c01b1783cdfee1bcf844eb44594cd16ec71c35305bf1c9fb5aade2735e16"
|
checksum = "b91ac2a3c6c0520a3fb3dd89321177c3c692937c4eb21893378219da10c44fc8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2 1.0.78",
|
||||||
"quote",
|
"quote 1.0.35",
|
||||||
"rust-embed-utils",
|
"rust-embed-utils",
|
||||||
"syn 2.0.50",
|
"syn 2.0.52",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed-utils"
|
name = "rust-embed-utils"
|
||||||
version = "8.2.0"
|
version = "8.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8cb0a25bfbb2d4b4402179c2cf030387d9990857ce08a32592c6238db9fa8665"
|
checksum = "86f69089032567ffff4eada41c573fc43ff466c7db7c5688b2e7969584345581"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"globset",
|
|
||||||
"sha2",
|
"sha2",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
@@ -546,24 +388,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "semver"
|
||||||
version = "1.0.197"
|
version = "1.0.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
|
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
|
||||||
dependencies = [
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_derive"
|
|
||||||
version = "1.0.197"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.50",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
@@ -576,11 +404,15 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stdio-perftest"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.11.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "substack"
|
name = "substack"
|
||||||
@@ -588,67 +420,47 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ffccc3d80f0a489de67aa74ff31ab852abb973e1c6dacf3704889e00ca544e7f"
|
checksum = "ffccc3d80f0a489de67aa74ff31ab852abb973e1c6dacf3704889e00ca544e7f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "0.15.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 0.4.30",
|
||||||
|
"quote 0.6.13",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2 1.0.78",
|
||||||
"quote",
|
"quote 1.0.35",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.50"
|
version = "2.0.52"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb"
|
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2 1.0.78",
|
||||||
"quote",
|
"quote 1.0.35",
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "take_mut"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "termion"
|
|
||||||
version = "1.5.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"numtoa",
|
|
||||||
"redox_syscall",
|
|
||||||
"redox_termios",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "termsize"
|
|
||||||
version = "0.1.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5e86d824a8e90f342ad3ef4bd51ef7119a9b681b0cc9f8ee7b2852f02ccd2517"
|
|
||||||
dependencies = [
|
|
||||||
"atty",
|
|
||||||
"kernel32-sys",
|
|
||||||
"libc",
|
|
||||||
"termion",
|
|
||||||
"winapi 0.2.8",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "trait-set"
|
name = "trait-set"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b79e2e9c9ab44c6d7c20d5976961b47e8f49ac199154daa514b77cd1ab536625"
|
checksum = "b79e2e9c9ab44c6d7c20d5976961b47e8f49ac199154daa514b77cd1ab536625"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2 1.0.78",
|
||||||
"quote",
|
"quote 1.0.35",
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -664,23 +476,11 @@ version = "1.0.12"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-segmentation"
|
|
||||||
version = "1.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.4"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "utf8parse"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
@@ -698,12 +498,6 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi"
|
|
||||||
version = "0.2.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
@@ -714,12 +508,6 @@ dependencies = [
|
|||||||
"winapi-x86_64-pc-windows-gnu",
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-build"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-i686-pc-windows-gnu"
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@@ -732,7 +520,7 @@ version = "0.1.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -741,72 +529,6 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.52.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-targets"
|
|
||||||
version = "0.52.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f"
|
|
||||||
dependencies = [
|
|
||||||
"windows_aarch64_gnullvm",
|
|
||||||
"windows_aarch64_msvc",
|
|
||||||
"windows_i686_gnu",
|
|
||||||
"windows_i686_msvc",
|
|
||||||
"windows_x86_64_gnu",
|
|
||||||
"windows_x86_64_gnullvm",
|
|
||||||
"windows_x86_64_msvc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_gnullvm"
|
|
||||||
version = "0.52.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_aarch64_msvc"
|
|
||||||
version = "0.52.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_gnu"
|
|
||||||
version = "0.52.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_i686_msvc"
|
|
||||||
version = "0.52.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnu"
|
|
||||||
version = "0.52.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_gnullvm"
|
|
||||||
version = "0.52.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows_x86_64_msvc"
|
|
||||||
version = "0.52.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.32"
|
version = "0.7.32"
|
||||||
@@ -822,7 +544,7 @@ version = "0.7.32"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2 1.0.78",
|
||||||
"quote",
|
"quote 1.0.35",
|
||||||
"syn 2.0.50",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|||||||
48
Cargo.toml
48
Cargo.toml
@@ -1,41 +1,9 @@
|
|||||||
[package]
|
[workspace]
|
||||||
name = "orchidlang"
|
resolver = "2"
|
||||||
version = "0.3.0"
|
|
||||||
edition = "2021"
|
|
||||||
license = "GPL-3.0"
|
|
||||||
repository = "https://github.com/lbfalvy/orchid"
|
|
||||||
description = """
|
|
||||||
An embeddable pure functional scripting language
|
|
||||||
"""
|
|
||||||
authors = ["Lawrence Bethlenfalvy <lbfalvy@protonmail.com>"]
|
|
||||||
|
|
||||||
[lib]
|
members = [
|
||||||
path = "src/lib.rs"
|
# "orchidlang",
|
||||||
|
"orcx",
|
||||||
[[bin]]
|
"orchid-api",
|
||||||
name = "orcx"
|
"orchid-std", "orchid-base", "orchid-api-derive", "orchid-api-traits", "stdio-perftest", "orchid-extension", "orchid-host",
|
||||||
path = "src/bin/orcx.rs"
|
]
|
||||||
doc = false
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
hashbrown = "0.14"
|
|
||||||
ordered-float = "4.2"
|
|
||||||
itertools = "0.12"
|
|
||||||
dyn-clone = "1.0"
|
|
||||||
trait-set = "0.3"
|
|
||||||
paste = "1.0"
|
|
||||||
rust-embed = { version = "8.2", features = ["include-exclude"] }
|
|
||||||
take_mut = "0.2"
|
|
||||||
unicode-segmentation = "1.11"
|
|
||||||
never = "0.1"
|
|
||||||
substack = "1.1"
|
|
||||||
intern-all = "0.4.1"
|
|
||||||
once_cell = "1.19"
|
|
||||||
const_format = "0.2"
|
|
||||||
bound = "0.5"
|
|
||||||
# Dependencies of orcx
|
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
|
||||||
rayon = "1.8"
|
|
||||||
termsize = "0.1"
|
|
||||||
|
|||||||
17
orchid-api-derive/Cargo.toml
Normal file
17
orchid-api-derive/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "orchid-api-derive"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
quote = "1.0.35"
|
||||||
|
syn = { version = "2.0.52" }
|
||||||
|
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||||
|
proc-macro2 = "1.0.78"
|
||||||
|
darling = "0.20.8"
|
||||||
|
itertools = "0.12.1"
|
||||||
30
orchid-api-derive/src/common.rs
Normal file
30
orchid-api-derive/src/common.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
use proc_macro2 as pm2;
|
||||||
|
use quote::ToTokens;
|
||||||
|
use syn::spanned::Spanned;
|
||||||
|
|
||||||
|
pub fn add_trait_bounds(mut generics: syn::Generics, bound: syn::TypeParamBound) -> syn::Generics {
|
||||||
|
for param in &mut generics.params {
|
||||||
|
if let syn::GenericParam::Type(ref mut type_param) = *param {
|
||||||
|
type_param.bounds.push(bound.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generics
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destructure(fields: &syn::Fields) -> Option<pm2::TokenStream> {
|
||||||
|
match fields {
|
||||||
|
syn::Fields::Unit => None,
|
||||||
|
syn::Fields::Named(_) => {
|
||||||
|
let field_list = fields.iter().map(|f| f.ident.as_ref().unwrap());
|
||||||
|
Some(quote! { { #(#field_list),* } })
|
||||||
|
},
|
||||||
|
syn::Fields::Unnamed(un) => {
|
||||||
|
let field_list = (0..fields.len()).map(|i| pos_field_name(i, un.span()));
|
||||||
|
Some(quote! { ( #(#field_list),* ) })
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pos_field_name(i: usize, span: pm2::Span) -> pm2::TokenStream {
|
||||||
|
syn::Ident::new(&format!("field_{i}"), span).to_token_stream()
|
||||||
|
}
|
||||||
56
orchid-api-derive/src/decode.rs
Normal file
56
orchid-api-derive/src/decode.rs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
use proc_macro::TokenStream;
|
||||||
|
use proc_macro2 as pm2;
|
||||||
|
|
||||||
|
use crate::common::add_trait_bounds;
|
||||||
|
|
||||||
|
pub fn derive(input: TokenStream) -> TokenStream {
|
||||||
|
// Parse the input tokens into a syntax tree
|
||||||
|
let input = parse_macro_input!(input as syn::DeriveInput);
|
||||||
|
let generics = add_trait_bounds(input.generics, parse_quote!(orchid_api_traits::Decode));
|
||||||
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
|
let name = input.ident;
|
||||||
|
let decode = decode_body(&input.data);
|
||||||
|
let expanded = quote! {
|
||||||
|
impl #impl_generics orchid_api_traits::Decode for #name #ty_generics #where_clause {
|
||||||
|
fn decode<R: std::io::Read>(read: &mut R) -> Self { #decode }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_fields(fields: &syn::Fields) -> pm2::TokenStream {
|
||||||
|
match fields {
|
||||||
|
syn::Fields::Unit => pm2::TokenStream::new(),
|
||||||
|
syn::Fields::Named(_) => {
|
||||||
|
let names = fields.iter().map(|f| f.ident.as_ref().unwrap());
|
||||||
|
quote! { { #( #names: orchid_api_traits::Decode::decode(read), )* } }
|
||||||
|
},
|
||||||
|
syn::Fields::Unnamed(_) => {
|
||||||
|
let exprs = fields.iter().map(|_| quote! { orchid_api_traits::Decode::decode(read), });
|
||||||
|
quote! { ( #( #exprs )* ) }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_body(data: &syn::Data) -> proc_macro2::TokenStream {
|
||||||
|
match data {
|
||||||
|
syn::Data::Union(_) => panic!("Unions can't be deserialized"),
|
||||||
|
syn::Data::Struct(str) => {
|
||||||
|
let fields = decode_fields(&str.fields);
|
||||||
|
quote! { Self #fields }
|
||||||
|
},
|
||||||
|
syn::Data::Enum(en) => {
|
||||||
|
let opts = en.variants.iter().enumerate().map(|(i, v @ syn::Variant { ident, .. })| {
|
||||||
|
let fields = decode_fields(&v.fields);
|
||||||
|
let id = i as u8;
|
||||||
|
quote! { #id => Self::#ident #fields, }
|
||||||
|
});
|
||||||
|
quote! {
|
||||||
|
match <u8 as orchid_api_traits::Decode>::decode(read) {
|
||||||
|
#(#opts)*
|
||||||
|
x => panic!("Unrecognized enum kind {x}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
68
orchid-api-derive/src/encode.rs
Normal file
68
orchid-api-derive/src/encode.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use proc_macro2 as pm2;
|
||||||
|
use quote::ToTokens;
|
||||||
|
use syn::spanned::Spanned;
|
||||||
|
|
||||||
|
use crate::common::{add_trait_bounds, destructure, pos_field_name};
|
||||||
|
|
||||||
|
pub fn derive(input: TokenStream) -> TokenStream {
|
||||||
|
// Parse the input tokens into a syntax tree
|
||||||
|
let input = parse_macro_input!(input as syn::DeriveInput);
|
||||||
|
let e_generics = add_trait_bounds(input.generics, parse_quote!(orchid_api_traits::Decode));
|
||||||
|
let (e_impl_generics, e_ty_generics, e_where_clause) = e_generics.split_for_impl();
|
||||||
|
let name = input.ident;
|
||||||
|
let encode = encode_body(&input.data);
|
||||||
|
let expanded = quote! {
|
||||||
|
impl #e_impl_generics orchid_api_traits::Encode for #name #e_ty_generics #e_where_clause {
|
||||||
|
fn encode<W: std::io::Write>(&self, write: &mut W) { #encode }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_body(data: &syn::Data) -> Option<pm2::TokenStream> {
|
||||||
|
match data {
|
||||||
|
syn::Data::Union(_) => panic!("Unions can't be deserialized"),
|
||||||
|
syn::Data::Struct(str) => {
|
||||||
|
let dest = destructure(&str.fields)?;
|
||||||
|
let body = encode_items(&str.fields);
|
||||||
|
Some(quote! {
|
||||||
|
let Self #dest = &self;
|
||||||
|
#body
|
||||||
|
})
|
||||||
|
},
|
||||||
|
syn::Data::Enum(en) => {
|
||||||
|
let options = en.variants.iter().enumerate().map(|(i, v @ syn::Variant { ident, .. })| {
|
||||||
|
let dest = destructure(&v.fields).unwrap_or_default();
|
||||||
|
let body = encode_items(&v.fields);
|
||||||
|
quote! {
|
||||||
|
Self::#ident #dest => {
|
||||||
|
(#i as u64).encode(write);
|
||||||
|
#body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Some(quote! {
|
||||||
|
match self {
|
||||||
|
#(#options)*
|
||||||
|
_ => unreachable!("Autogenerated encode impl for all possible variants"),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_names<T: ToTokens>(names: impl Iterator<Item = T>) -> pm2::TokenStream {
|
||||||
|
quote! { #( #names .encode(write); )* }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_items(fields: &syn::Fields) -> Option<pm2::TokenStream> {
|
||||||
|
match fields {
|
||||||
|
syn::Fields::Unit => None,
|
||||||
|
syn::Fields::Named(_) => Some(encode_names(fields.iter().map(|f| f.ident.as_ref().unwrap()))),
|
||||||
|
syn::Fields::Unnamed(un) =>
|
||||||
|
Some(encode_names((0..fields.len()).map(|i| pos_field_name(i, un.span())))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
127
orchid-api-derive/src/hierarchy.rs
Normal file
127
orchid-api-derive/src/hierarchy.rs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
use std::iter;
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
use pm2::TokenTree;
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use proc_macro2 as pm2;
|
||||||
|
use syn::DeriveInput;
|
||||||
|
|
||||||
|
pub fn derive(input: TokenStream) -> TokenStream {
|
||||||
|
// Parse the input tokens into a syntax tree
|
||||||
|
let input = parse_macro_input!(input as syn::DeriveInput);
|
||||||
|
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
|
let name = &input.ident;
|
||||||
|
let extendable = is_extendable(&input);
|
||||||
|
let is_leaf_val = if extendable { quote!(TLFalse) } else { quote!(TLTrue) };
|
||||||
|
match get_ancestry(&input) {
|
||||||
|
None => TokenStream::from(quote! {
|
||||||
|
impl #impl_generics orchid_api_traits::InHierarchy for #name #ty_generics #where_clause {
|
||||||
|
type IsRoot = orchid_api_traits::TLTrue;
|
||||||
|
type IsLeaf = orchid_api_traits:: #is_leaf_val ;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Some(ancestry) => {
|
||||||
|
let parent = ancestry[0].clone();
|
||||||
|
let casts = gen_casts(&ancestry[..], "e!(#name));
|
||||||
|
TokenStream::from(quote! {
|
||||||
|
#casts
|
||||||
|
impl #impl_generics orchid_api_traits::InHierarchy for #name #ty_generics #where_clause {
|
||||||
|
type IsRoot = orchid_api_traits::TLFalse;
|
||||||
|
type IsLeaf = orchid_api_traits:: #is_leaf_val ;
|
||||||
|
}
|
||||||
|
impl #impl_generics orchid_api_traits::Extends for #name #ty_generics #where_clause {
|
||||||
|
type Parent = #parent;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_casts(ancestry: &[pm2::TokenStream], this: &pm2::TokenStream) -> pm2::TokenStream {
|
||||||
|
let from_impls = iter::once(this).chain(ancestry.iter()).tuple_windows().map(|(prev, cur)| {
|
||||||
|
quote! {
|
||||||
|
impl From<#this> for #cur {
|
||||||
|
fn from(value: #this) -> Self {
|
||||||
|
#cur::#prev(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let try_from_impls = (1..=ancestry.len()).map(|len| {
|
||||||
|
let (orig, inter) = ancestry[..len].split_last().unwrap();
|
||||||
|
fn gen_chk(r: &[pm2::TokenStream], last: &pm2::TokenStream) -> pm2::TokenStream {
|
||||||
|
match r.split_last() {
|
||||||
|
None => quote! { #last (_) => true },
|
||||||
|
Some((ty, tail)) => {
|
||||||
|
let sub = gen_chk(tail, last);
|
||||||
|
quote! {
|
||||||
|
#ty ( value ) => match value {
|
||||||
|
#ty:: #sub ,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let chk = gen_chk(inter, this);
|
||||||
|
fn gen_unpk(r: &[pm2::TokenStream], last: &pm2::TokenStream) -> pm2::TokenStream {
|
||||||
|
match r.split_last() {
|
||||||
|
None => quote! { #last ( value ) => value },
|
||||||
|
Some((ty, tail)) => {
|
||||||
|
let sub = gen_unpk(tail, last);
|
||||||
|
quote! {
|
||||||
|
#ty ( value ) => match value {
|
||||||
|
#ty:: #sub ,
|
||||||
|
_ => unreachable!("Checked above!"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let unpk = gen_unpk(inter, this);
|
||||||
|
quote! {
|
||||||
|
impl TryFrom<#orig> for #this {
|
||||||
|
type Error = #orig;
|
||||||
|
fn try_from(value: #orig) -> Result<Self, Self::Error> {
|
||||||
|
let can_cast = match &value {
|
||||||
|
#orig:: #chk ,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
if !can_cast { return Err(value) }
|
||||||
|
Ok ( match value {
|
||||||
|
#orig:: #unpk ,
|
||||||
|
_ => unreachable!("Checked above!")
|
||||||
|
} )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
from_impls.chain(try_from_impls).flatten().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_ancestry(input: &DeriveInput) -> Option<Vec<pm2::TokenStream>> {
|
||||||
|
input.attrs.iter().find(|a| a.path().get_ident().is_some_and(|i| *i == "extends")).map(|attr| {
|
||||||
|
match &attr.meta {
|
||||||
|
syn::Meta::List(list) => (list.tokens.clone().into_iter())
|
||||||
|
.batching(|it| {
|
||||||
|
let grp: pm2::TokenStream = it
|
||||||
|
.take_while(|t| {
|
||||||
|
if let TokenTree::Punct(punct) = t { punct.as_char() != ',' } else { true }
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
(!grp.is_empty()).then_some(grp)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
_ => panic!("The correct format of the parent macro is #[parent(SomeParentType)]"),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_extendable(input: &DeriveInput) -> bool {
|
||||||
|
input.attrs.iter().any(|a| a.path().get_ident().is_some_and(|i| *i == "extendable"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_wtf() {
|
||||||
|
eprintln!("{}", gen_casts(&[quote!(ExtHostReq)], "e!(BogusReq)))
|
||||||
|
}
|
||||||
27
orchid-api-derive/src/lib.rs
Normal file
27
orchid-api-derive/src/lib.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
mod common;
|
||||||
|
mod decode;
|
||||||
|
mod encode;
|
||||||
|
mod hierarchy;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate quote;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate syn;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
use orchid_api_traits::Coding;
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
|
#[proc_macro_derive(Decode)]
|
||||||
|
pub fn decode(input: TokenStream) -> TokenStream { decode::derive(input) }
|
||||||
|
|
||||||
|
#[proc_macro_derive(Encode)]
|
||||||
|
pub fn encode(input: TokenStream) -> TokenStream { encode::derive(input) }
|
||||||
|
|
||||||
|
#[proc_macro_derive(Hierarchy, attributes(extends, extendable))]
|
||||||
|
pub fn hierarchy(input: TokenStream) -> TokenStream { hierarchy::derive(input) }
|
||||||
|
|
||||||
|
#[proc_macro_derive(Coding)]
|
||||||
|
pub fn coding(input: TokenStream) -> TokenStream {
|
||||||
|
decode(input.clone()).into_iter().chain(encode(input)).collect()
|
||||||
|
}
|
||||||
9
orchid-api-traits/Cargo.toml
Normal file
9
orchid-api-traits/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "orchid-api-traits"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ordered-float = "4.2"
|
||||||
266
orchid-api-traits/src/coding.rs
Normal file
266
orchid-api-traits/src/coding.rs
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::iter;
|
||||||
|
use std::ops::Range;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ordered_float::{FloatCore, NotNan};
|
||||||
|
|
||||||
|
use crate::encode_enum;
|
||||||
|
|
||||||
|
pub trait Decode {
|
||||||
|
/// Decode an instance from the beginning of the buffer. Return the decoded
|
||||||
|
/// data and the remaining buffer.
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self;
|
||||||
|
}
|
||||||
|
pub trait Encode {
|
||||||
|
/// Append an instance of the struct to the buffer
|
||||||
|
fn encode<W: Write>(&self, write: &mut W);
|
||||||
|
fn enc_vec(&self) -> Vec<u8> {
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
self.encode(&mut vec);
|
||||||
|
vec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub trait Coding: Encode + Decode {}
|
||||||
|
impl<T: Encode + Decode> Coding for T {}
|
||||||
|
|
||||||
|
macro_rules! num_impl {
|
||||||
|
($number:ty, $size:expr) => {
|
||||||
|
impl Decode for $number {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self {
|
||||||
|
let mut bytes = [0u8; $size];
|
||||||
|
read.read_exact(&mut bytes).unwrap();
|
||||||
|
<$number>::from_be_bytes(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Encode for $number {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) {
|
||||||
|
write.write_all(&self.to_be_bytes()).expect("Could not write number")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($number:ty) => {
|
||||||
|
num_impl!($number, (<$number>::BITS / 8) as usize);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
num_impl!(u128);
|
||||||
|
num_impl!(u64);
|
||||||
|
num_impl!(u32);
|
||||||
|
num_impl!(u16);
|
||||||
|
num_impl!(u8);
|
||||||
|
num_impl!(i128);
|
||||||
|
num_impl!(i64);
|
||||||
|
num_impl!(i32);
|
||||||
|
num_impl!(i16);
|
||||||
|
num_impl!(i8);
|
||||||
|
num_impl!(f64, 8);
|
||||||
|
num_impl!(f32, 4);
|
||||||
|
|
||||||
|
macro_rules! nonzero_impl {
|
||||||
|
($name:ty) => {
|
||||||
|
impl Decode for $name {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self { Self::new(Decode::decode(read)).unwrap() }
|
||||||
|
}
|
||||||
|
impl Encode for $name {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) { self.get().encode(write) }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
nonzero_impl!(std::num::NonZeroU8);
|
||||||
|
nonzero_impl!(std::num::NonZeroU16);
|
||||||
|
nonzero_impl!(std::num::NonZeroU32);
|
||||||
|
nonzero_impl!(std::num::NonZeroU64);
|
||||||
|
nonzero_impl!(std::num::NonZeroU128);
|
||||||
|
nonzero_impl!(std::num::NonZeroI8);
|
||||||
|
nonzero_impl!(std::num::NonZeroI16);
|
||||||
|
nonzero_impl!(std::num::NonZeroI32);
|
||||||
|
nonzero_impl!(std::num::NonZeroI64);
|
||||||
|
nonzero_impl!(std::num::NonZeroI128);
|
||||||
|
|
||||||
|
impl<'a, T: Encode> Encode for &'a T {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) { (**self).encode(write) }
|
||||||
|
}
|
||||||
|
impl<T: Decode + FloatCore> Decode for NotNan<T> {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self { NotNan::new(T::decode(read)).expect("Float was NaN") }
|
||||||
|
}
|
||||||
|
impl<T: Encode + FloatCore> Encode for NotNan<T> {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) { self.as_ref().encode(write) }
|
||||||
|
}
|
||||||
|
impl Decode for String {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self {
|
||||||
|
let len = u64::decode(read).try_into().unwrap();
|
||||||
|
let mut data = vec![0u8; len];
|
||||||
|
read.read_exact(&mut data).unwrap();
|
||||||
|
std::str::from_utf8(&data).expect("String invalid UTF-8").to_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Encode for String {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) {
|
||||||
|
u64::try_from(self.len()).unwrap().encode(write);
|
||||||
|
write.write_all(self.as_bytes()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Encode for str {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) {
|
||||||
|
u64::try_from(self.len()).unwrap().encode(write);
|
||||||
|
write.write_all(self.as_bytes()).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Decode> Decode for Vec<T> {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self {
|
||||||
|
let len = u64::decode(read).try_into().unwrap();
|
||||||
|
iter::repeat_with(|| T::decode(read)).take(len).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Encode> Encode for Vec<T> {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) {
|
||||||
|
u64::try_from(self.len()).unwrap().encode(write);
|
||||||
|
self.iter().for_each(|t| t.encode(write));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Encode> Encode for [T] {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) {
|
||||||
|
u64::try_from(self.len()).unwrap().encode(write);
|
||||||
|
self.iter().for_each(|t| t.encode(write));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Decode> Decode for Option<T> {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self {
|
||||||
|
match u8::decode(read) {
|
||||||
|
0 => None,
|
||||||
|
1 => Some(T::decode(read)),
|
||||||
|
x => panic!("{x} is not a valid option value"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Encode> Encode for Option<T> {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) {
|
||||||
|
let t = if let Some(t) = self { t } else { return 0u8.encode(write) };
|
||||||
|
1u8.encode(write);
|
||||||
|
t.encode(write);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Decode, E: Decode> Decode for Result<T, E> {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self {
|
||||||
|
match u8::decode(read) {
|
||||||
|
0 => Self::Ok(T::decode(read)),
|
||||||
|
1 => Self::Err(E::decode(read)),
|
||||||
|
x => panic!("Invalid Result tag {x}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Encode, E: Encode> Encode for Result<T, E> {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) {
|
||||||
|
match self {
|
||||||
|
Ok(t) => encode_enum(write, 0, |w| t.encode(w)),
|
||||||
|
Err(e) => encode_enum(write, 1, |w| e.encode(w)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<K: Decode + Eq + Hash, V: Decode> Decode for HashMap<K, V> {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self {
|
||||||
|
let len = u64::decode(read).try_into().unwrap();
|
||||||
|
iter::repeat_with(|| <(K, V)>::decode(read)).take(len).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<K: Encode + Eq + Hash, V: Encode> Encode for HashMap<K, V> {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) {
|
||||||
|
u64::try_from(self.len()).unwrap().encode(write);
|
||||||
|
self.iter().for_each(|pair| pair.encode(write));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
macro_rules! tuple {
|
||||||
|
(($($t:ident)*) ($($T:ident)*)) => {
|
||||||
|
impl<$($T: Decode),*> Decode for ($($T,)*) {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self { ($($T::decode(read),)*) }
|
||||||
|
}
|
||||||
|
impl<$($T: Encode),*> Encode for ($($T,)*) {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) {
|
||||||
|
let ($($t,)*) = self;
|
||||||
|
$( $t.encode(write); )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
tuple!((t)(T));
|
||||||
|
tuple!((t u) (T U));
|
||||||
|
tuple!((t u v) (T U V));
|
||||||
|
tuple!((t u v x) (T U V X)); // 4
|
||||||
|
tuple!((t u v x y) (T U V X Y));
|
||||||
|
tuple!((t u v x y z) (T U V X Y Z));
|
||||||
|
tuple!((t u v x y z a) (T U V X Y Z A));
|
||||||
|
tuple!((t u v x y z a b) (T U V X Y Z A B)); // 8
|
||||||
|
tuple!((t u v x y z a b c) (T U V X Y Z A B C));
|
||||||
|
tuple!((t u v x y z a b c d) (T U V X Y Z A B C D));
|
||||||
|
tuple!((t u v x y z a b c d e) (T U V X Y Z A B C D E));
|
||||||
|
tuple!((t u v x y z a b c d e f) (T U V X Y Z A B C D E F)); // 12
|
||||||
|
tuple!((t u v x y z a b c d e f g) (T U V X Y Z A B C D E F G));
|
||||||
|
tuple!((t u v x y z a b c d e f g h) (T U V X Y Z A B C D E F G H));
|
||||||
|
tuple!((t u v x y z a b c d e f g h i) (T U V X Y Z A B C D E F G H I));
|
||||||
|
tuple!((t u v x y z a b c d e f g h i j) (T U V X Y Z A B C D E F G H I J)); // 16
|
||||||
|
|
||||||
|
impl Decode for () {
|
||||||
|
fn decode<R: Read>(_: &mut R) -> Self {}
|
||||||
|
}
|
||||||
|
impl Encode for () {
|
||||||
|
fn encode<W: Write>(&self, _: &mut W) {}
|
||||||
|
}
|
||||||
|
impl Decode for bool {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self {
|
||||||
|
let mut buf = [0];
|
||||||
|
read.read_exact(&mut buf).unwrap();
|
||||||
|
buf[0] != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Encode for bool {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) {
|
||||||
|
write.write_all(&[if *self { 0xff } else { 0 }]).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Decode, const N: usize> Decode for [T; N] {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self {
|
||||||
|
// TODO: figure out how to do this in safe rust on the stack
|
||||||
|
((0..N).map(|_| T::decode(read)).collect::<Vec<_>>().try_into())
|
||||||
|
.unwrap_or_else(|_| unreachable!("The length of this iterator is statically known"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Encode, const N: usize> Encode for [T; N] {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) { self.iter().for_each(|t| t.encode(write)) }
|
||||||
|
}
|
||||||
|
impl<T: Decode> Decode for Range<T> {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self { T::decode(read)..T::decode(read) }
|
||||||
|
}
|
||||||
|
impl<T: Encode> Encode for Range<T> {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) {
|
||||||
|
self.start.encode(write);
|
||||||
|
self.end.encode(write);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! smart_ptr {
|
||||||
|
($name:tt) => {
|
||||||
|
impl<T: Decode> Decode for $name<T> {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self { $name::new(T::decode(read)) }
|
||||||
|
}
|
||||||
|
impl<T: Encode> Encode for $name<T> {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) { (**self).encode(write) }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
smart_ptr!(Arc);
|
||||||
|
smart_ptr!(Rc);
|
||||||
|
smart_ptr!(Box);
|
||||||
|
|
||||||
|
impl Decode for char {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self { char::from_u32(u32::decode(read)).unwrap() }
|
||||||
|
}
|
||||||
|
impl Encode for char {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) { (*self as u32).encode(write) }
|
||||||
|
}
|
||||||
18
orchid-api-traits/src/helpers.rs
Normal file
18
orchid-api-traits/src/helpers.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
use crate::Encode;
|
||||||
|
|
||||||
|
pub fn encode_enum<W: Write>(write: &mut W, id: u8, f: impl FnOnce(&mut W)) {
|
||||||
|
id.encode(write);
|
||||||
|
f(write)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_exact(write: &mut impl Write, bytes: &'static [u8]) {
|
||||||
|
write.write_all(bytes).expect("Failed to write exact bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_exact(read: &mut impl Read, bytes: &'static [u8]) {
|
||||||
|
let mut data = vec![0u8; bytes.len()];
|
||||||
|
read.read_exact(&mut data).expect("Failed to read bytes");
|
||||||
|
assert_eq!(&data, bytes, "Wrong bytes")
|
||||||
|
}
|
||||||
30
orchid-api-traits/src/hier2.rs
Normal file
30
orchid-api-traits/src/hier2.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
pub trait TBool {}
|
||||||
|
pub struct TTrue;
|
||||||
|
impl TBool for TTrue {}
|
||||||
|
pub struct TFalse;
|
||||||
|
impl TBool for TFalse {}
|
||||||
|
|
||||||
|
/// Implementation picker for a tree node
|
||||||
|
///
|
||||||
|
/// Note: This technically allows for the degenerate case
|
||||||
|
/// ```
|
||||||
|
/// struct MyType;
|
||||||
|
/// impl TreeRolePicker for MyType {
|
||||||
|
/// type IsLeaf = TTrue;
|
||||||
|
/// type IsRoot = TTrue;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// This isn't very useful because it describes a one element sealed hierarchy.
|
||||||
|
pub trait TreeRolePicker {
|
||||||
|
type IsLeaf: TBool;
|
||||||
|
type IsRoot: TBool;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Extends: TreeRolePicker<IsRoot = TFalse> {
|
||||||
|
type Parent: TreeRolePicker<IsLeaf = TFalse>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Inherits<T> {}
|
||||||
|
|
||||||
|
// impl<T> Inherits<T, 0> for T {}
|
||||||
|
impl<T: Extends, This> Inherits<T::Parent> for This where This: Inherits<T> {}
|
||||||
80
orchid-api-traits/src/hierarchy.rs
Normal file
80
orchid-api-traits/src/hierarchy.rs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
/// [Hierarchy] implementation key. The two implementors of this trait are
|
||||||
|
/// [Base] and [Subtype]. These types are assigned to [InHierarchy::Role] to
|
||||||
|
/// select the implementation of [Hierarchy].
|
||||||
|
pub trait HierarchyRole {}
|
||||||
|
|
||||||
|
/// A type-level boolean suitable to select conditional trait implementations.
|
||||||
|
/// Implementors are [True] and [False]
|
||||||
|
pub trait TLBool {}
|
||||||
|
/// [TLBool] value of `true`. The opposite is [False]
|
||||||
|
pub struct TLTrue;
|
||||||
|
impl TLBool for TLTrue {}
|
||||||
|
/// [TLBool] value of `false`. The opposite is [True]
|
||||||
|
pub struct TLFalse;
|
||||||
|
impl TLBool for TLFalse {}
|
||||||
|
|
||||||
|
/// Assign this type to [InHierarchy::Role] and implement [Descendant] to create
|
||||||
|
/// a subtype. These types can be upcast to their parent type, conditionally
|
||||||
|
/// downcast from it, and selected for [Descendant::Parent] by other types.
|
||||||
|
pub struct Subtype;
|
||||||
|
impl HierarchyRole for Subtype {}
|
||||||
|
/// Assign this type to [InHierarchy::Role] to create a base type. These types
|
||||||
|
/// are upcast only to themselves, but they can be selected in
|
||||||
|
/// [Descendant::Parent].
|
||||||
|
pub struct Base;
|
||||||
|
impl HierarchyRole for Base {}
|
||||||
|
|
||||||
|
/// A type that implements [Hierarchy]. Used to select implementations of traits
|
||||||
|
/// on the hierarchy
|
||||||
|
pub trait InHierarchy: Clone {
|
||||||
|
/// Indicates that this hierarchy element is a leaf. Leaves can never have
|
||||||
|
/// children
|
||||||
|
type IsLeaf: TLBool;
|
||||||
|
/// Indicates that this hierarchy element is a root. Roots can never have
|
||||||
|
/// parents
|
||||||
|
type IsRoot: TLBool;
|
||||||
|
}
|
||||||
|
/// A type that derives from a parent type.
|
||||||
|
pub trait Extends: InHierarchy<IsRoot = TLFalse> + Into<Self::Parent> {
|
||||||
|
/// Specify the immediate parent of this type. This guides the
|
||||||
|
type Parent: InHierarchy<IsLeaf = TLFalse>
|
||||||
|
+ TryInto<Self>
|
||||||
|
+ UnderRootImpl<<Self::Parent as InHierarchy>::IsRoot>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait UnderRootImpl<IsRoot: TLBool>: Sized {
|
||||||
|
type __Root: UnderRoot<IsRoot = TLTrue, Root = Self::__Root>;
|
||||||
|
fn __into_root(self) -> Self::__Root;
|
||||||
|
fn __try_from_root(root: Self::__Root) -> Result<Self, Self::__Root>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait UnderRoot: InHierarchy {
|
||||||
|
type Root: UnderRoot<IsRoot = TLTrue, Root = Self::Root>;
|
||||||
|
fn into_root(self) -> Self::Root;
|
||||||
|
fn try_from_root(root: Self::Root) -> Result<Self, Self::Root>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: InHierarchy + UnderRootImpl<T::IsRoot>> UnderRoot for T {
|
||||||
|
type Root = <Self as UnderRootImpl<<Self as InHierarchy>::IsRoot>>::__Root;
|
||||||
|
fn into_root(self) -> Self::Root { self.__into_root() }
|
||||||
|
fn try_from_root(root: Self::Root) -> Result<Self, Self::Root> { Self::__try_from_root(root) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: InHierarchy<IsRoot = TLTrue>> UnderRootImpl<TLTrue> for T {
|
||||||
|
type __Root = Self;
|
||||||
|
fn __into_root(self) -> Self::__Root { self }
|
||||||
|
fn __try_from_root(root: Self::__Root) -> Result<Self, Self::__Root> { Ok(root) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: InHierarchy<IsRoot = TLFalse> + Extends> UnderRootImpl<TLFalse> for T {
|
||||||
|
type __Root = <<Self as Extends>::Parent as UnderRootImpl<
|
||||||
|
<<Self as Extends>::Parent as InHierarchy>::IsRoot,
|
||||||
|
>>::__Root;
|
||||||
|
fn __into_root(self) -> Self::__Root {
|
||||||
|
<Self as Into<<Self as Extends>::Parent>>::into(self).into_root()
|
||||||
|
}
|
||||||
|
fn __try_from_root(root: Self::__Root) -> Result<Self, Self::__Root> {
|
||||||
|
let parent = <Self as Extends>::Parent::try_from_root(root)?;
|
||||||
|
parent.clone().try_into().map_err(|_| parent.into_root())
|
||||||
|
}
|
||||||
|
}
|
||||||
12
orchid-api-traits/src/lib.rs
Normal file
12
orchid-api-traits/src/lib.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
mod coding;
|
||||||
|
mod helpers;
|
||||||
|
mod hierarchy;
|
||||||
|
mod relations;
|
||||||
|
|
||||||
|
pub use coding::{Coding, Decode, Encode};
|
||||||
|
pub use helpers::{encode_enum, read_exact, write_exact};
|
||||||
|
pub use hierarchy::{
|
||||||
|
Base, Extends, HierarchyRole, InHierarchy, Subtype, TLBool, TLFalse, TLTrue, UnderRoot,
|
||||||
|
UnderRootImpl,
|
||||||
|
};
|
||||||
|
pub use relations::{MsgSet, Request};
|
||||||
13
orchid-api-traits/src/relations.rs
Normal file
13
orchid-api-traits/src/relations.rs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
use super::coding::{Coding, Encode};
|
||||||
|
|
||||||
|
pub trait Request: Coding + Sized + Send + 'static {
|
||||||
|
type Response: Coding + Send + 'static;
|
||||||
|
fn respond(&self, rep: Self::Response) -> Vec<u8> { rep.enc_vec() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait MsgSet {
|
||||||
|
type InReq: Coding + Sized + Send + 'static;
|
||||||
|
type InNot: Coding + Sized + Send + 'static;
|
||||||
|
type OutReq: Coding + Sized + Send + 'static;
|
||||||
|
type OutNot: Coding + Sized + Send + 'static;
|
||||||
|
}
|
||||||
12
orchid-api/Cargo.toml
Normal file
12
orchid-api/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "orchid-api"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ordered-float = "4.2.0"
|
||||||
|
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||||
|
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
|
||||||
|
derive_more = "0.99.17"
|
||||||
91
orchid-api/src/atom.rs
Normal file
91
orchid-api/src/atom.rs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
use orchid_api_derive::{Coding, Hierarchy};
|
||||||
|
use orchid_api_traits::Request;
|
||||||
|
|
||||||
|
use crate::expr::{Expr, ExprTicket};
|
||||||
|
use crate::proto::{ExtHostReq, HostExtNotif, HostExtReq};
|
||||||
|
use crate::system::SysId;
|
||||||
|
|
||||||
|
pub type AtomData = Vec<u8>;
|
||||||
|
|
||||||
|
/// An atom representation that can be serialized and sent around. Atoms
|
||||||
|
/// represent the smallest increment of work.
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub struct Atom {
|
||||||
|
/// Instance ID of the system that created the atom
|
||||||
|
pub owner: SysId,
|
||||||
|
/// Indicates whether the owner should be notified when this atom is dropped.
|
||||||
|
/// Construction is always explicit and atoms are never cloned.
|
||||||
|
///
|
||||||
|
/// Atoms with `drop == false` are also known as trivial, they can be
|
||||||
|
/// duplicated and stored with no regard to expression lifetimes. NOTICE
|
||||||
|
/// that this only applies to the atom. If it's referenced with an
|
||||||
|
/// [ExprTicket], the ticket itself can still expire.
|
||||||
|
///
|
||||||
|
/// Notice also that the atoms still expire when the system is dropped, and
|
||||||
|
/// are not portable across instances of the same system, so this doesn't
|
||||||
|
/// imply that the atom is serializable.
|
||||||
|
pub drop: bool,
|
||||||
|
/// Data stored in the atom. This could be a key into a map, or the raw data
|
||||||
|
/// of the atom if it isn't too big.
|
||||||
|
pub data: AtomData,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to apply an atom as a function to an expression
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(AtomReq, HostExtReq)]
|
||||||
|
pub struct CallRef(pub Atom, pub ExprTicket);
|
||||||
|
impl Request for CallRef {
|
||||||
|
type Response = Expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to apply an atom as a function, consuming the atom and enabling the
|
||||||
|
/// library to reuse its datastructures rather than duplicating them. This is an
|
||||||
|
/// optimization over [CallRef] followed by [AtomDrop].
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(AtomReq, HostExtReq)]
|
||||||
|
pub struct FinalCall(pub Atom, pub ExprTicket);
|
||||||
|
impl Request for FinalCall {
|
||||||
|
type Response = Expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determine whether two atoms are identical for the purposes of macro
|
||||||
|
/// application. If a given atom is never generated by macros or this relation
|
||||||
|
/// is difficult to define, the module can return false
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(AtomReq, HostExtReq)]
|
||||||
|
pub struct AtomSame(pub Atom, pub Atom);
|
||||||
|
impl Request for AtomSame {
|
||||||
|
type Response = bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A request blindly routed to the system that provides an atom.
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(AtomReq, HostExtReq)]
|
||||||
|
pub struct Fwded(pub Atom, pub Vec<u8>);
|
||||||
|
impl Request for Fwded {
|
||||||
|
type Response = Vec<u8>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(ExtHostReq)]
|
||||||
|
pub struct Fwd(pub Atom, pub Vec<u8>);
|
||||||
|
impl Request for Fwd {
|
||||||
|
type Response = Vec<u8>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notification that an atom is being dropped because its associated expression
|
||||||
|
/// isn't referenced anywhere. This should have no effect if the atom's `drop`
|
||||||
|
/// flag is false.
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(HostExtNotif)]
|
||||||
|
pub struct AtomDrop(pub Atom);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(HostExtReq)]
|
||||||
|
#[extendable]
|
||||||
|
pub enum AtomReq {
|
||||||
|
CallRef(CallRef),
|
||||||
|
FinalCall(FinalCall),
|
||||||
|
AtomSame(AtomSame),
|
||||||
|
Fwded(Fwded),
|
||||||
|
}
|
||||||
1
orchid-api/src/error.rs
Normal file
1
orchid-api/src/error.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub type ProjErrId = u16;
|
||||||
99
orchid-api/src/expr.rs
Normal file
99
orchid-api/src/expr.rs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
use orchid_api_derive::{Coding, Hierarchy};
|
||||||
|
use orchid_api_traits::Request;
|
||||||
|
|
||||||
|
use crate::atom::Atom;
|
||||||
|
use crate::intern::{TStr, TStrv};
|
||||||
|
use crate::location::Location;
|
||||||
|
use crate::proto::ExtHostReq;
|
||||||
|
use crate::system::SysId;
|
||||||
|
|
||||||
|
/// An arbitrary ID associated with an expression on the host side. Incoming
|
||||||
|
/// tickets always come with some lifetime guarantee, which can be extended with
|
||||||
|
/// [AcquireExpr].
|
||||||
|
///
|
||||||
|
/// The ID is globally unique within its lifetime, but may be reused.
|
||||||
|
pub type ExprTicket = u64;
|
||||||
|
|
||||||
|
/// Acquire a strong reference to an expression. This keeps it alive until a
|
||||||
|
/// corresponding [Release] is emitted. The number of times a system has
|
||||||
|
/// acquired an expression is counted, and it is the system's responsibility to
|
||||||
|
/// ensure that acquires and releases pair up. Behaviour in case of a
|
||||||
|
/// superfluous free is not defined.
|
||||||
|
///
|
||||||
|
/// Some contexts may specify that an ingress [ExprTicket] is owned, this means
|
||||||
|
/// that acquiring it is not necessary.
|
||||||
|
///
|
||||||
|
/// This can be called with a foreign system to signal that an owned reference
|
||||||
|
/// is being passed, though [Relocate] may be a better fit.
|
||||||
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub struct Acquire(pub SysId, pub ExprTicket);
|
||||||
|
|
||||||
|
/// Release a reference either previously acquired through either [Acquire]
|
||||||
|
/// or by receiving an owned reference. The number of times a system has
|
||||||
|
/// acquired an expression is counted, and it is the system's responsibility to
|
||||||
|
/// ensure that acquires and releases pair up. Behaviour in case of excessive
|
||||||
|
/// freeing is not defined.
|
||||||
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub struct Release(pub SysId, pub ExprTicket);
|
||||||
|
|
||||||
|
/// Decrement the reference count for one system and increment it for another,
|
||||||
|
/// to indicate passing an owned reference. Equivalent to [Acquire] followed by
|
||||||
|
/// [Release].
|
||||||
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub struct Relocate {
|
||||||
|
pub dec: SysId,
|
||||||
|
pub inc: SysId,
|
||||||
|
pub expr: ExprTicket,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A description of a new expression. It is used as the return value of
|
||||||
|
/// [crate::atom::Call] or [crate::atom::CallRef], or a constant in the
|
||||||
|
/// [crate::tree::Tree].
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub enum Clause {
|
||||||
|
/// Apply the lhs as a function to the rhs
|
||||||
|
Call(Box<Expr>, Box<Expr>),
|
||||||
|
/// Lambda function. The number operates as an argument name
|
||||||
|
Lambda(TStr, Box<Expr>),
|
||||||
|
/// Binds the argument passed to the lambda with the same ID in the same
|
||||||
|
/// template
|
||||||
|
Arg(TStr),
|
||||||
|
/// Insert the specified host-expression in the template here. When the clause
|
||||||
|
/// is used in the const tree, this variant is forbidden.
|
||||||
|
Slot(ExprTicket),
|
||||||
|
/// The lhs must be fully processed before the rhs can be processed.
|
||||||
|
/// Equivalent to Haskell's function of the same name
|
||||||
|
Seq(Box<Expr>, Box<Expr>),
|
||||||
|
/// Insert an atom in the tree. When the clause is used in the const tree, the
|
||||||
|
/// atom must be trivial.
|
||||||
|
Atom(Atom),
|
||||||
|
/// A reference to a constant
|
||||||
|
Const(TStrv),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub struct Expr {
|
||||||
|
pub clause: Clause,
|
||||||
|
pub location: Location
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(ExprReq, ExtHostReq)]
|
||||||
|
pub struct Inspect(pub ExprTicket);
|
||||||
|
impl Request for Inspect {
|
||||||
|
type Response = Clause;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(ExtHostReq)]
|
||||||
|
#[extendable]
|
||||||
|
pub enum ExprReq {
|
||||||
|
Inspect(Inspect),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub enum ExprNotif {
|
||||||
|
Acquire(Acquire),
|
||||||
|
Release(Release),
|
||||||
|
Relocate(Relocate),
|
||||||
|
}
|
||||||
16
orchid-api/src/fs.rs
Normal file
16
orchid-api/src/fs.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
use orchid_api_derive::Coding;
|
||||||
|
use orchid_api_traits::Request;
|
||||||
|
|
||||||
|
pub type FsId = u16;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Coding)]
|
||||||
|
pub enum Loaded {
|
||||||
|
Code(String),
|
||||||
|
Collection(Vec<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Coding)]
|
||||||
|
pub struct FsRead(pub Vec<String>);
|
||||||
|
impl Request for FsRead {
|
||||||
|
type Response = Result<Loaded, ()>;
|
||||||
|
}
|
||||||
91
orchid-api/src/intern.rs
Normal file
91
orchid-api/src/intern.rs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
use std::num::NonZeroU64;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use orchid_api_derive::{Coding, Hierarchy};
|
||||||
|
use orchid_api_traits::Request;
|
||||||
|
|
||||||
|
use crate::proto::{ExtHostReq, HostExtReq};
|
||||||
|
|
||||||
|
/// Intern requests sent by the replica to the master. These requests are
|
||||||
|
/// repeatable.
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(ExtHostReq)]
|
||||||
|
#[extendable]
|
||||||
|
pub enum IntReq {
|
||||||
|
InternStr(InternStr),
|
||||||
|
InternStrv(InternStrv),
|
||||||
|
ExternStr(ExternStr),
|
||||||
|
ExternStrv(ExternStrv),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// replica -> master to intern a string on the master.
|
||||||
|
///
|
||||||
|
/// Repeatable.
|
||||||
|
///
|
||||||
|
/// See [IntReq]
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(IntReq, ExtHostReq)]
|
||||||
|
pub struct InternStr(pub Arc<String>);
|
||||||
|
impl Request for InternStr {
|
||||||
|
type Response = TStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// replica -> master to find the interned string corresponding to a key.
|
||||||
|
///
|
||||||
|
/// Repeatable.
|
||||||
|
///
|
||||||
|
/// See [IntReq]
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(IntReq, ExtHostReq)]
|
||||||
|
pub struct ExternStr(pub TStr);
|
||||||
|
impl Request for ExternStr {
|
||||||
|
type Response = Arc<String>;
|
||||||
|
}
|
||||||
|
/// replica -> master to intern a vector of interned strings
|
||||||
|
///
|
||||||
|
/// Repeatable.
|
||||||
|
///
|
||||||
|
/// See [IntReq]
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(IntReq, ExtHostReq)]
|
||||||
|
pub struct InternStrv(pub Arc<Vec<TStr>>);
|
||||||
|
impl Request for InternStrv {
|
||||||
|
type Response = TStrv;
|
||||||
|
}
|
||||||
|
/// replica -> master to find the vector of interned strings corresponding to a
|
||||||
|
/// token
|
||||||
|
///
|
||||||
|
/// Repeatable.
|
||||||
|
///
|
||||||
|
/// See [IntReq]
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(IntReq, ExtHostReq)]
|
||||||
|
pub struct ExternStrv(pub TStrv);
|
||||||
|
impl Request for ExternStrv {
|
||||||
|
type Response = Arc<Vec<TStr>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A substitute for an interned string in serialized datastructures.
|
||||||
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
|
||||||
|
pub struct TStr(pub NonZeroU64);
|
||||||
|
|
||||||
|
/// A substitute for an interned string sequence in serialized datastructures.
|
||||||
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
|
||||||
|
pub struct TStrv(pub NonZeroU64);
|
||||||
|
|
||||||
|
/// A request to sweep the replica. The master will not be sweeped until all
|
||||||
|
/// replicas respond, as it must retain everything the replicas retained
|
||||||
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(HostExtReq)]
|
||||||
|
pub struct Sweep;
|
||||||
|
impl Request for Sweep {
|
||||||
|
type Response = Retained;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List of keys in this replica that couldn't be sweeped because local
|
||||||
|
/// datastructures reference their value.
|
||||||
|
#[derive(Clone, Debug, Coding)]
|
||||||
|
pub struct Retained {
|
||||||
|
pub strings: Vec<TStr>,
|
||||||
|
pub vecs: Vec<TStrv>,
|
||||||
|
}
|
||||||
10
orchid-api/src/lib.rs
Normal file
10
orchid-api/src/lib.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
pub mod atom;
|
||||||
|
pub mod error;
|
||||||
|
pub mod expr;
|
||||||
|
pub mod fs;
|
||||||
|
pub mod intern;
|
||||||
|
pub mod location;
|
||||||
|
pub mod proto;
|
||||||
|
pub mod system;
|
||||||
|
pub mod tree;
|
||||||
|
pub mod parser;
|
||||||
24
orchid-api/src/location.rs
Normal file
24
orchid-api/src/location.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
use orchid_api_derive::Coding;
|
||||||
|
|
||||||
|
use crate::intern::TStrv;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub enum Location {
|
||||||
|
None,
|
||||||
|
Gen(CodeGenInfo),
|
||||||
|
Range(SourceRange),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub struct SourceRange {
|
||||||
|
pub path: TStrv,
|
||||||
|
pub range: Range<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub struct CodeGenInfo {
|
||||||
|
pub generator: String,
|
||||||
|
pub details: String,
|
||||||
|
}
|
||||||
55
orchid-api/src/parser.rs
Normal file
55
orchid-api/src/parser.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
use orchid_api_derive::{Coding, Hierarchy};
|
||||||
|
use orchid_api_traits::Request;
|
||||||
|
|
||||||
|
use crate::intern::TStr;
|
||||||
|
use crate::proto::{ExtHostReq, HostExtReq};
|
||||||
|
use crate::system::SysId;
|
||||||
|
use crate::tree::TokenTree;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
|
#[extends(HostExtReq)]
|
||||||
|
#[extendable]
|
||||||
|
pub enum ParserReq {
|
||||||
|
MkLexer(MkLexer),
|
||||||
|
Lex(Lex),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type LexerId = u16;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
|
#[extends(ParserReq, HostExtReq)]
|
||||||
|
pub struct MkLexer(pub SysId, pub TStr);
|
||||||
|
impl Request for MkLexer {
|
||||||
|
type Response = LexerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
|
#[extends(ParserReq, HostExtReq)]
|
||||||
|
pub struct Lex {
|
||||||
|
pub parser: LexerId,
|
||||||
|
pub next: char,
|
||||||
|
pub pos: u32,
|
||||||
|
}
|
||||||
|
impl Request for Lex {
|
||||||
|
type Response = Option<LexResult>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Coding)]
|
||||||
|
pub struct LexResult {
|
||||||
|
pub consumed: u32,
|
||||||
|
pub data: TokenTree,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
|
#[extends(ExtHostReq)]
|
||||||
|
pub struct SubLex {
|
||||||
|
pub lexer: LexerId,
|
||||||
|
pub pos: u32,
|
||||||
|
}
|
||||||
|
impl Request for SubLex {
|
||||||
|
type Response = SubLex;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ParseLine {
|
||||||
|
|
||||||
|
}
|
||||||
125
orchid-api/src/proto.rs
Normal file
125
orchid-api/src/proto.rs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
//! Basic messages of the Orchid extension API.
|
||||||
|
//!
|
||||||
|
//! The protocol is defined over a byte stream, normally the stdin/stdout of the
|
||||||
|
//! extension. The implementations of [Coding] in this library are considered
|
||||||
|
//! normative. Any breaking change here or in the default implementations of
|
||||||
|
//! [Coding] must also increment the version number in the intro strings.
|
||||||
|
//!
|
||||||
|
//! 3 different kinds of messages are recognized; request, response, and
|
||||||
|
//! notification. There are no general ordering guarantees about these, multiple
|
||||||
|
//! requests, even requests of the same type may be sent concurrently, unless
|
||||||
|
//! otherwise specified in the request's definition.
|
||||||
|
//!
|
||||||
|
//! Each message begins with a u32 length, followed by that many bytes of
|
||||||
|
//! message content. The first byte of the content is a u64 combined request ID
|
||||||
|
//! and discriminator, D.
|
||||||
|
//!
|
||||||
|
//! - If D = 0, the rest of the content is a notification.
|
||||||
|
//! - If 0 < D < 2^63, it is a request with identifier D.
|
||||||
|
//! - If 2^63 <= D, it is a response to request identifier !D.
|
||||||
|
//!
|
||||||
|
//! The order of both notifications and requests sent from the same thread must
|
||||||
|
//! be preserved. Toolkits must ensure that the client code is able to observe
|
||||||
|
//! the ordering of messages.
|
||||||
|
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
use derive_more::{From, TryInto};
|
||||||
|
use orchid_api_derive::{Coding, Hierarchy};
|
||||||
|
use orchid_api_traits::{read_exact, write_exact, Decode, Encode, MsgSet, Request};
|
||||||
|
|
||||||
|
use crate::{atom, expr, intern, parser, system, tree};
|
||||||
|
|
||||||
|
static HOST_INTRO: &[u8] = b"Orchid host, binary API v0\n";
|
||||||
|
pub struct HostHeader;
|
||||||
|
impl Decode for HostHeader {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self {
|
||||||
|
read_exact(read, HOST_INTRO);
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Encode for HostHeader {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) { write_exact(write, HOST_INTRO) }
|
||||||
|
}
|
||||||
|
|
||||||
|
static EXT_INTRO: &[u8] = b"Orchid extension, binary API v0\n";
|
||||||
|
pub struct ExtensionHeader {
|
||||||
|
pub systems: Vec<system::SystemDecl>,
|
||||||
|
}
|
||||||
|
impl Decode for ExtensionHeader {
|
||||||
|
fn decode<R: Read>(read: &mut R) -> Self {
|
||||||
|
read_exact(read, EXT_INTRO);
|
||||||
|
Self { systems: Vec::decode(read) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Encode for ExtensionHeader {
|
||||||
|
fn encode<W: Write>(&self, write: &mut W) {
|
||||||
|
write_exact(write, EXT_INTRO);
|
||||||
|
self.systems.encode(write)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
|
||||||
|
pub struct Ping;
|
||||||
|
impl Request for Ping {
|
||||||
|
type Response = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests running from the extension to the host
|
||||||
|
#[derive(Clone, Coding, Hierarchy)]
|
||||||
|
#[extendable]
|
||||||
|
pub enum ExtHostReq {
|
||||||
|
Ping(Ping),
|
||||||
|
IntReq(intern::IntReq),
|
||||||
|
Fwd(atom::Fwd),
|
||||||
|
ExprReq(expr::ExprReq),
|
||||||
|
SubLex(parser::SubLex),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notifications sent from the extension to the host
|
||||||
|
#[derive(Coding, From, TryInto)]
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
pub enum ExtHostNotif {
|
||||||
|
Expr(expr::ExprNotif),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests running from the host to the extension
|
||||||
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
|
#[extendable]
|
||||||
|
pub enum HostExtReq {
|
||||||
|
Ping(Ping),
|
||||||
|
NewSystem(system::NewSystem),
|
||||||
|
Sweep(intern::Sweep),
|
||||||
|
AtomReq(atom::AtomReq),
|
||||||
|
ParserReq(parser::ParserReq),
|
||||||
|
GetConstTree(tree::GetConstTree),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notifications sent from the host to the extension
|
||||||
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
|
#[extendable]
|
||||||
|
pub enum HostExtNotif {
|
||||||
|
SystemDrop(system::SystemDrop),
|
||||||
|
AtomDrop(atom::AtomDrop),
|
||||||
|
/// The host can assume that after this notif is sent, a correctly written
|
||||||
|
/// extension will eventually exit.
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Message set viewed from the extension's perspective
|
||||||
|
pub struct ExtMsgSet;
|
||||||
|
impl MsgSet for ExtMsgSet {
|
||||||
|
type InNot = HostExtNotif;
|
||||||
|
type InReq = HostExtReq;
|
||||||
|
type OutNot = ExtHostNotif;
|
||||||
|
type OutReq = ExtHostReq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Message Set viewed from the host's perspective
|
||||||
|
pub struct HostMsgSet;
|
||||||
|
impl MsgSet for HostMsgSet {
|
||||||
|
type InNot = ExtHostNotif;
|
||||||
|
type InReq = ExtHostReq;
|
||||||
|
type OutNot = HostExtNotif;
|
||||||
|
type OutReq = HostExtReq;
|
||||||
|
}
|
||||||
54
orchid-api/src/system.rs
Normal file
54
orchid-api/src/system.rs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
use orchid_api_derive::{Coding, Hierarchy};
|
||||||
|
use orchid_api_traits::Request;
|
||||||
|
use ordered_float::NotNan;
|
||||||
|
|
||||||
|
use crate::proto::{HostExtNotif, HostExtReq};
|
||||||
|
|
||||||
|
/// ID of a system type
|
||||||
|
pub type SysDeclId = u16;
|
||||||
|
|
||||||
|
/// ID of a system instance
|
||||||
|
pub type SysId = u16;
|
||||||
|
|
||||||
|
/// Details about a system provided by this library
|
||||||
|
#[derive(Coding)]
|
||||||
|
pub struct SystemDecl {
|
||||||
|
/// ID of the system, unique within the library
|
||||||
|
pub id: SysDeclId,
|
||||||
|
/// This can be depended upon. Exactly one of each kind will be loaded
|
||||||
|
pub name: String,
|
||||||
|
/// If multiple instances of a system are found, the highest priority will be
|
||||||
|
/// used. This can be used for version counting, but also for fallbacks if a
|
||||||
|
/// negative number is found.
|
||||||
|
///
|
||||||
|
/// Systems cannot depend on specific versions and older versions of systems
|
||||||
|
/// are never loaded. Compatibility can be determined on a per-system basis
|
||||||
|
/// through an algorithm chosen by the provider.
|
||||||
|
pub priority: NotNan<f64>,
|
||||||
|
/// List of systems needed for this one to work correctly. These will be
|
||||||
|
/// looked up, and an error produced if they aren't found.
|
||||||
|
pub depends: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Host -> extension; instantiate a system according to its [SystemDecl].
|
||||||
|
/// Multiple instances of a system may exist in the same address space, so it's
|
||||||
|
/// essential that any resource associated with a system finds its system by the
|
||||||
|
/// ID in a global map.
|
||||||
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
|
#[extends(HostExtReq)]
|
||||||
|
pub struct NewSystem {
|
||||||
|
/// ID of the system
|
||||||
|
pub system: SysDeclId,
|
||||||
|
/// ID of the system instance, unique for the host
|
||||||
|
pub id: SysId,
|
||||||
|
/// Instance IDs for dependencies, in the order that the names appear in the
|
||||||
|
/// declaration
|
||||||
|
pub depends: Vec<SysId>,
|
||||||
|
}
|
||||||
|
impl Request for NewSystem {
|
||||||
|
type Response = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Coding, Hierarchy)]
|
||||||
|
#[extends(HostExtNotif)]
|
||||||
|
pub struct SystemDrop(pub SysId);
|
||||||
76
orchid-api/src/tree.rs
Normal file
76
orchid-api/src/tree.rs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use orchid_api_derive::{Coding, Hierarchy};
|
||||||
|
use orchid_api_traits::Request;
|
||||||
|
use ordered_float::NotNan;
|
||||||
|
|
||||||
|
use crate::atom::Atom;
|
||||||
|
use crate::expr::Expr;
|
||||||
|
use crate::intern::TStr;
|
||||||
|
use crate::location::SourceRange;
|
||||||
|
use crate::proto::HostExtReq;
|
||||||
|
use crate::system::SysId;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub struct TokenTree {
|
||||||
|
token: Token,
|
||||||
|
location: SourceRange,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub enum Token {
|
||||||
|
/// Lambda function. The number operates as an argument name
|
||||||
|
Lambda(TStr, Vec<TokenTree>),
|
||||||
|
Name(Vec<TStr>),
|
||||||
|
S(Paren, Vec<TokenTree>),
|
||||||
|
/// A placeholder in a macro. This variant is forbidden everywhere outside
|
||||||
|
/// line parser output
|
||||||
|
Ph(Placeholder),
|
||||||
|
Atom(Atom),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub struct Placeholder {
|
||||||
|
name: TStr,
|
||||||
|
kind: PlaceholderKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub enum PlaceholderKind {
|
||||||
|
Scalar,
|
||||||
|
Name,
|
||||||
|
Vector { nonzero: bool, priority: u8 },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Hash, PartialEq, Eq, Coding)]
|
||||||
|
pub enum Paren {
|
||||||
|
Round,
|
||||||
|
Square,
|
||||||
|
Curly,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Coding)]
|
||||||
|
pub struct MacroRule {
|
||||||
|
pub pattern: Vec<TokenTree>,
|
||||||
|
pub priority: NotNan<f64>,
|
||||||
|
pub template: Vec<TokenTree>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Coding)]
|
||||||
|
pub enum Tree {
|
||||||
|
Const(Expr),
|
||||||
|
Mod(TreeModule),
|
||||||
|
Rule(MacroRule),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Coding)]
|
||||||
|
pub struct TreeModule {
|
||||||
|
pub children: HashMap<String, Tree>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||||
|
#[extends(HostExtReq)]
|
||||||
|
pub struct GetConstTree(pub SysId);
|
||||||
|
impl Request for GetConstTree {
|
||||||
|
type Response = TreeModule;
|
||||||
|
}
|
||||||
21
orchid-base/Cargo.toml
Normal file
21
orchid-base/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "orchid-base"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
derive_destructure = "1.0.0"
|
||||||
|
dyn-clone = "1.0.17"
|
||||||
|
hashbrown = "0.14.3"
|
||||||
|
itertools = "0.12.1"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
never = "0.1.0"
|
||||||
|
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 = "4.2.0"
|
||||||
|
rust-embed = "8.3.0"
|
||||||
|
substack = "1.1.0"
|
||||||
|
trait-set = "0.3.0"
|
||||||
0
orchid-base/src/api_utils.rs
Normal file
0
orchid-base/src/api_utils.rs
Normal file
44
orchid-base/src/child.rs
Normal file
44
orchid-base/src/child.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::{mem, process};
|
||||||
|
|
||||||
|
pub struct SharedChild {
|
||||||
|
child: process::Child,
|
||||||
|
stdin: Mutex<process::ChildStdin>,
|
||||||
|
stdout: Mutex<process::ChildStdout>,
|
||||||
|
}
|
||||||
|
impl SharedChild {
|
||||||
|
pub fn new(cmd: &mut process::Command) -> io::Result<Self> {
|
||||||
|
let mut child = cmd.stdin(process::Stdio::piped()).stdout(process::Stdio::piped()).spawn()?;
|
||||||
|
let stdin = Mutex::new(child.stdin.take().expect("Piped stdin above"));
|
||||||
|
let stdout = Mutex::new(child.stdout.take().expect("Piped stdout above"));
|
||||||
|
Ok(Self { stdin, stdout, child })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_msg(&self, msg: &[u8]) -> io::Result<()> {
|
||||||
|
send_msg(&mut *self.stdin.lock().unwrap(), msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recv_msg(&self) -> io::Result<Vec<u8>> { recv_msg(&mut *self.stdout.lock().unwrap()) }
|
||||||
|
}
|
||||||
|
impl Drop for SharedChild {
|
||||||
|
fn drop(&mut self) { mem::drop(self.child.kill()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_msg(write: &mut impl Write, msg: &[u8]) -> io::Result<()> {
|
||||||
|
write.write_all(&(u32::try_from(msg.len()).unwrap()).to_be_bytes())?;
|
||||||
|
write.write_all(msg)?;
|
||||||
|
write.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recv_msg(read: &mut impl Read) -> io::Result<Vec<u8>> {
|
||||||
|
let mut len = [0u8; 4];
|
||||||
|
read.read_exact(&mut len)?;
|
||||||
|
let len = u32::from_be_bytes(len);
|
||||||
|
let mut msg = vec![0u8; len as usize];
|
||||||
|
read.read_exact(&mut msg)?;
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_parent_msg(msg: &[u8]) -> io::Result<()> { send_msg(&mut io::stdout().lock(), msg) }
|
||||||
|
pub fn recv_parent_msg() -> io::Result<Vec<u8>> { recv_msg(&mut io::stdin().lock()) }
|
||||||
9
orchid-base/src/clone.rs
Normal file
9
orchid-base/src/clone.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#[macro_export]
|
||||||
|
macro_rules! clone {
|
||||||
|
($($n:ident),+; $body:expr) => (
|
||||||
|
{
|
||||||
|
$( let $n = $n.clone(); )+
|
||||||
|
$body
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
63
orchid-base/src/event.rs
Normal file
63
orchid-base/src/event.rs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
//! Multiple-listener-single-delivery event system.
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
use std::sync::mpsc::{self, sync_channel};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
struct Reply<T, U> {
|
||||||
|
resub: bool,
|
||||||
|
outcome: Result<U, T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Listener<T, E> {
|
||||||
|
sink: mpsc::SyncSender<T>,
|
||||||
|
source: mpsc::Receiver<Reply<T, E>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Event<T, U> {
|
||||||
|
listeners: Mutex<Vec<Listener<T, U>>>,
|
||||||
|
}
|
||||||
|
impl<T, U> Event<T, U> {
|
||||||
|
pub const fn new() -> Self { Self { listeners: Mutex::new(Vec::new()) } }
|
||||||
|
|
||||||
|
pub fn dispatch(&self, mut ev: T) -> Option<U> {
|
||||||
|
let mut listeners = self.listeners.lock().unwrap();
|
||||||
|
let mut alt_list = Vec::with_capacity(listeners.len());
|
||||||
|
mem::swap(&mut *listeners, &mut alt_list);
|
||||||
|
let mut items = alt_list.into_iter();
|
||||||
|
while let Some(l) = items.next() {
|
||||||
|
l.sink.send(ev).unwrap();
|
||||||
|
let Reply { resub, outcome } = l.source.recv().unwrap();
|
||||||
|
if resub {
|
||||||
|
listeners.push(l);
|
||||||
|
}
|
||||||
|
match outcome {
|
||||||
|
Ok(res) => {
|
||||||
|
listeners.extend(items);
|
||||||
|
return Some(res);
|
||||||
|
},
|
||||||
|
Err(next) => {
|
||||||
|
ev = next;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_one<V>(&self, mut filter: impl FnMut(&T) -> bool, f: impl FnOnce(T) -> (U, V)) -> V {
|
||||||
|
let mut listeners = self.listeners.lock().unwrap();
|
||||||
|
let (sink, request) = sync_channel(0);
|
||||||
|
let (response, source) = sync_channel(0);
|
||||||
|
listeners.push(Listener { sink, source });
|
||||||
|
mem::drop(listeners);
|
||||||
|
loop {
|
||||||
|
let t = request.recv().unwrap();
|
||||||
|
if filter(&t) {
|
||||||
|
let (u, v) = f(t);
|
||||||
|
response.send(Reply { resub: false, outcome: Ok(u) }).unwrap();
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
response.send(Reply { resub: true, outcome: Err(t) }).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
orchid-base/src/gen/impls.rs
Normal file
67
orchid-base/src/gen/impls.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
use std::any::TypeId;
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
use orchid_api::expr::{Clause, Expr};
|
||||||
|
use orchid_api::location::Location;
|
||||||
|
|
||||||
|
use super::traits::{GenClause, Generable};
|
||||||
|
use crate::expr::RtExpr;
|
||||||
|
use crate::host::AtomHand;
|
||||||
|
use crate::intern::{deintern, intern};
|
||||||
|
|
||||||
|
fn safely_reinterpret<In: 'static, Out: 'static>(x: In) -> Result<Out, In> {
|
||||||
|
if TypeId::of::<In>() != TypeId::of::<Out>() {
|
||||||
|
return Err(x);
|
||||||
|
}
|
||||||
|
let bx = Box::new(x);
|
||||||
|
// SAFETY: type sameness asserted above, from_raw and into_raw pair up
|
||||||
|
Ok(*unsafe { Box::from_raw(Box::into_raw(bx) as *mut Out) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// impls of the gen traits for external types
|
||||||
|
|
||||||
|
impl GenClause for Expr {
|
||||||
|
fn generate<T: super::traits::Generable>(&self, ctx: T::Ctx<'_>, pop: &impl Fn() -> T) -> T {
|
||||||
|
match &self.clause {
|
||||||
|
Clause::Arg(arg) => T::arg(ctx, deintern(*arg).as_str()),
|
||||||
|
Clause::Atom(atom) => T::atom(ctx, AtomHand::from_api(atom.clone())),
|
||||||
|
Clause::Call(f, x) => T::apply(ctx, |c| f.generate(c, pop), |c| x.generate(c, pop)),
|
||||||
|
Clause::Lambda(arg, b) => T::lambda(ctx, deintern(*arg).as_str(), |ctx| b.generate(ctx, pop)),
|
||||||
|
Clause::Seq(n1, n2) => T::seq(ctx, |c| n1.generate(c, pop), |c| n2.generate(c, pop)),
|
||||||
|
Clause::Const(int) => T::constant(ctx, deintern(*int).iter().map(|t| t.as_str())),
|
||||||
|
Clause::Slot(expr) => {
|
||||||
|
let rte = RtExpr::resolve(*expr).expect("expired ticket");
|
||||||
|
safely_reinterpret(rte).expect("ticket slots make no sense for anything other than rte")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_expr(clause: Clause) -> Expr { Expr { clause, location: Location::None } }
|
||||||
|
|
||||||
|
impl Generable for Expr {
|
||||||
|
type Ctx<'a> = ();
|
||||||
|
|
||||||
|
fn arg(_ctx: Self::Ctx<'_>, name: &str) -> Self { to_expr(Clause::Arg(intern(name).marker())) }
|
||||||
|
fn apply(
|
||||||
|
ctx: Self::Ctx<'_>,
|
||||||
|
f: impl FnOnce(Self::Ctx<'_>) -> Self,
|
||||||
|
x: impl FnOnce(Self::Ctx<'_>) -> Self,
|
||||||
|
) -> Self {
|
||||||
|
to_expr(Clause::Call(Box::new(f(ctx)), Box::new(x(ctx))))
|
||||||
|
}
|
||||||
|
fn atom(_ctx: Self::Ctx<'_>, a: crate::host::AtomHand) -> Self { to_expr(Clause::Atom(a.api_ref())) }
|
||||||
|
fn constant<'a>(_ctx: Self::Ctx<'_>, name: impl IntoIterator<Item = &'a str>) -> Self {
|
||||||
|
to_expr(Clause::Const(intern(&name.into_iter().map(intern).collect_vec()[..]).marker()))
|
||||||
|
}
|
||||||
|
fn lambda(ctx: Self::Ctx<'_>, name: &str, body: impl FnOnce(Self::Ctx<'_>) -> Self) -> Self {
|
||||||
|
to_expr(Clause::Lambda(intern(name).marker(), Box::new(body(ctx))))
|
||||||
|
}
|
||||||
|
fn seq(
|
||||||
|
ctx: Self::Ctx<'_>,
|
||||||
|
a: impl FnOnce(Self::Ctx<'_>) -> Self,
|
||||||
|
b: impl FnOnce(Self::Ctx<'_>) -> Self,
|
||||||
|
) -> Self {
|
||||||
|
to_expr(Clause::Seq(Box::new(a(ctx)), Box::new(b(ctx))))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,3 +5,4 @@
|
|||||||
pub mod tpl;
|
pub mod tpl;
|
||||||
pub mod traits;
|
pub mod traits;
|
||||||
pub mod tree;
|
pub mod tree;
|
||||||
|
pub mod impls;
|
||||||
@@ -2,23 +2,14 @@
|
|||||||
//! [GenClause].
|
//! [GenClause].
|
||||||
|
|
||||||
use super::traits::{GenClause, Generable};
|
use super::traits::{GenClause, Generable};
|
||||||
use crate::foreign::atom::{Atom, AtomGenerator, Atomic};
|
use crate::host::AtomHand;
|
||||||
|
|
||||||
/// Atom, Embed a Rust value. See also [AnyAtom]
|
/// A trivial atom
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct V<A: Atomic + Clone>(pub A);
|
pub struct SysAtom(pub AtomHand);
|
||||||
impl<A: Atomic + Clone> GenClause for V<A> {
|
impl GenClause for SysAtom {
|
||||||
fn generate<T: Generable>(&self, ctx: T::Ctx<'_>, _: &impl Fn() -> T) -> T {
|
fn generate<T: Generable>(&self, ctx: T::Ctx<'_>, _: &impl Fn() -> T) -> T {
|
||||||
T::atom(ctx, Atom::new(self.0.clone()))
|
T::atom(ctx, self.0.clone())
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Atom, embed a Rust value of unspecified type. See also [V]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AnyAtom(pub AtomGenerator);
|
|
||||||
impl GenClause for AnyAtom {
|
|
||||||
fn generate<T: Generable>(&self, ctx: T::Ctx<'_>, _: &impl Fn() -> T) -> T {
|
|
||||||
T::atom(ctx, self.0.run())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5,15 +5,15 @@ use std::cell::RefCell;
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::foreign::atom::Atom;
|
use crate::host::AtomHand;
|
||||||
|
|
||||||
/// Representations of the Orchid expression tree that can describe basic
|
/// Representations of the Orchid expression tree that can describe basic
|
||||||
/// language elements.
|
/// language elements.
|
||||||
pub trait Generable: Sized {
|
pub trait Generable: Sized + 'static {
|
||||||
/// Context information defined by parents. Generators just forward this.
|
/// Context information defined by parents. Generators just forward this.
|
||||||
type Ctx<'a>: Sized;
|
type Ctx<'a>: Sized;
|
||||||
/// Wrap external data.
|
/// Wrap external data.
|
||||||
fn atom(ctx: Self::Ctx<'_>, a: Atom) -> Self;
|
fn atom(ctx: Self::Ctx<'_>, a: AtomHand) -> Self;
|
||||||
/// Generate a reference to a constant
|
/// Generate a reference to a constant
|
||||||
fn constant<'a>(ctx: Self::Ctx<'_>, name: impl IntoIterator<Item = &'a str>) -> Self;
|
fn constant<'a>(ctx: Self::Ctx<'_>, name: impl IntoIterator<Item = &'a str>) -> Self;
|
||||||
/// Generate a function call given the function and its argument
|
/// Generate a function call given the function and its argument
|
||||||
@@ -22,9 +22,16 @@ pub trait Generable: Sized {
|
|||||||
f: impl FnOnce(Self::Ctx<'_>) -> Self,
|
f: impl FnOnce(Self::Ctx<'_>) -> Self,
|
||||||
x: impl FnOnce(Self::Ctx<'_>) -> Self,
|
x: impl FnOnce(Self::Ctx<'_>) -> Self,
|
||||||
) -> Self;
|
) -> Self;
|
||||||
|
/// Generate a dependency between the top clause and the bottom clause
|
||||||
|
fn seq(
|
||||||
|
ctx: Self::Ctx<'_>,
|
||||||
|
a: impl FnOnce(Self::Ctx<'_>) -> Self,
|
||||||
|
b: impl FnOnce(Self::Ctx<'_>) -> Self,
|
||||||
|
) -> Self;
|
||||||
/// Generate a function. The argument name is only valid within the same
|
/// Generate a function. The argument name is only valid within the same
|
||||||
/// [Generable].
|
/// [Generable].
|
||||||
fn lambda(ctx: Self::Ctx<'_>, name: &str, body: impl FnOnce(Self::Ctx<'_>) -> Self) -> Self;
|
fn lambda(ctx: Self::Ctx<'_>, name: &str, body: impl FnOnce(Self::Ctx<'_>) -> Self) -> Self;
|
||||||
|
|
||||||
/// Generate a reference to a function argument. The argument name is only
|
/// Generate a reference to a function argument. The argument name is only
|
||||||
/// valid within the same [Generable].
|
/// valid within the same [Generable].
|
||||||
fn arg(ctx: Self::Ctx<'_>, name: &str) -> Self;
|
fn arg(ctx: Self::Ctx<'_>, name: &str) -> Self;
|
||||||
79
orchid-base/src/gen/tree.rs
Normal file
79
orchid-base/src/gen/tree.rs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
//! Components to build in-memory module trees that in Orchid. These modules
|
||||||
|
//! can only contain constants and other modules.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use dyn_clone::{clone_box, DynClone};
|
||||||
|
use orchid_api::expr::Expr;
|
||||||
|
use trait_set::trait_set;
|
||||||
|
|
||||||
|
use super::tpl;
|
||||||
|
use super::traits::{Gen, GenClause};
|
||||||
|
use crate::combine::Combine;
|
||||||
|
use crate::host::AtomHand;
|
||||||
|
use crate::tree::{ModEntry, ModMember, TreeConflict};
|
||||||
|
|
||||||
|
trait_set! {
|
||||||
|
trait TreeLeaf = Gen<Expr, [Expr; 0]> + DynClone + Send;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A leaf in the [ConstTree]
|
||||||
|
pub struct GenConst(Box<dyn TreeLeaf>);
|
||||||
|
impl GenConst {
|
||||||
|
fn c(data: impl GenClause + Send + Clone + 'static) -> Self { Self(Box::new(data)) }
|
||||||
|
}
|
||||||
|
impl fmt::Debug for GenConst {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.0) }
|
||||||
|
}
|
||||||
|
impl Clone for GenConst {
|
||||||
|
fn clone(&self) -> Self { Self(clone_box(&*self.0)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error condition when constant trees that define the the same constant are
|
||||||
|
/// merged. Produced during system loading if multiple modules define the
|
||||||
|
/// same constant
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ConflictingConsts;
|
||||||
|
|
||||||
|
impl Combine for GenConst {
|
||||||
|
type Error = ConflictingConsts;
|
||||||
|
fn combine(self, _: Self) -> Result<Self, Self::Error> { Err(ConflictingConsts) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A lightweight module tree that can be built declaratively by hand to
|
||||||
|
/// describe libraries of external functions in Rust. It implements [Combine]
|
||||||
|
/// for merging libraries.
|
||||||
|
pub type ConstTree = ModEntry<GenConst, (), ()>;
|
||||||
|
|
||||||
|
/// Describe a constant
|
||||||
|
#[must_use]
|
||||||
|
pub fn leaf(value: impl GenClause + Clone + Send + 'static) -> ConstTree {
|
||||||
|
ModEntry::wrap(ModMember::Item(GenConst::c(value)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describe a constant which appears in [ConstTree::tree].
|
||||||
|
///
|
||||||
|
/// The unarray tricks rustfmt into keeping this call as a single line even if
|
||||||
|
/// it chooses to break the argument into a block.
|
||||||
|
pub fn ent<K: AsRef<str>>(
|
||||||
|
key: K,
|
||||||
|
[g]: [impl GenClause + Clone + Send + 'static; 1],
|
||||||
|
) -> (K, ConstTree) {
|
||||||
|
(key, leaf(g))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Describe an [Atomic]
|
||||||
|
#[must_use]
|
||||||
|
pub fn atom_leaf(atom: AtomHand) -> ConstTree { leaf(tpl::SysAtom(atom)) }
|
||||||
|
|
||||||
|
/// Describe an [Atomic] which appears as an entry in a [ConstTree::tree]
|
||||||
|
///
|
||||||
|
/// The unarray is used to trick rustfmt into breaking the atom into a block
|
||||||
|
/// without breaking this call into a block
|
||||||
|
#[must_use]
|
||||||
|
pub fn atom_ent<K: AsRef<str>>(key: K, [atom]: [AtomHand; 1]) -> (K, ConstTree) {
|
||||||
|
(key, atom_leaf(atom))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Errors produced duriung the merger of constant trees
|
||||||
|
pub type ConstCombineErr = TreeConflict<GenConst, (), ()>;
|
||||||
303
orchid-base/src/intern.rs
Normal file
303
orchid-base/src/intern.rs
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
use std::borrow::Borrow;
|
||||||
|
use std::hash::BuildHasher as _;
|
||||||
|
use std::num::NonZeroU64;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::sync::{atomic, Arc, Mutex, MutexGuard};
|
||||||
|
use std::{fmt, hash};
|
||||||
|
|
||||||
|
use hashbrown::{HashMap, HashSet};
|
||||||
|
use itertools::Itertools as _;
|
||||||
|
use orchid_api::intern::{
|
||||||
|
ExternStr, ExternStrv, IntReq, InternStr, InternStrv, Retained, TStr, TStrv,
|
||||||
|
};
|
||||||
|
use orchid_api_traits::Request;
|
||||||
|
|
||||||
|
use crate::reqnot::{DynRequester, Requester};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Token<T: ?Sized + Interned> {
|
||||||
|
data: Arc<T>,
|
||||||
|
marker: T::Marker,
|
||||||
|
}
|
||||||
|
impl<T: Interned + ?Sized> Token<T> {
|
||||||
|
pub fn marker(&self) -> T::Marker { self.marker }
|
||||||
|
pub fn arc(&self) -> Arc<T> { self.data.clone() }
|
||||||
|
}
|
||||||
|
impl<T: Interned + ?Sized> Deref for Token<T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { self.data.as_ref() }
|
||||||
|
}
|
||||||
|
impl<T: Interned + ?Sized> Ord for Token<T> {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.marker().cmp(&other.marker()) }
|
||||||
|
}
|
||||||
|
impl<T: Interned + ?Sized> PartialOrd for Token<T> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(self.cmp(other)) }
|
||||||
|
}
|
||||||
|
impl<T: Interned + ?Sized> Eq for Token<T> {}
|
||||||
|
impl<T: Interned + ?Sized> PartialEq for Token<T> {
|
||||||
|
fn eq(&self, other: &Self) -> bool { self.cmp(other).is_eq() }
|
||||||
|
}
|
||||||
|
impl<T: Interned + ?Sized> hash::Hash for Token<T> {
|
||||||
|
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.marker().hash(state) }
|
||||||
|
}
|
||||||
|
impl<T: Interned + ?Sized + fmt::Display> fmt::Display for Token<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", &*self.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T: Interned + ?Sized + fmt::Debug> fmt::Debug for Token<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Token({} -> {:?})", self.marker().get_id(), self.data.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Interned: Eq + hash::Hash + Clone {
|
||||||
|
type Marker: InternMarker<Interned = Self>;
|
||||||
|
fn intern(self: Arc<Self>, req: &(impl DynRequester<Transfer = IntReq> + ?Sized))
|
||||||
|
-> Self::Marker;
|
||||||
|
fn bimap(interner: &mut Interner) -> &mut Bimap<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Internable {
|
||||||
|
type Interned: Interned;
|
||||||
|
fn get_owned(&self) -> Arc<Self::Interned>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait InternMarker: Copy + PartialEq + Eq + PartialOrd + Ord + hash::Hash {
|
||||||
|
type Interned: Interned<Marker = Self>;
|
||||||
|
fn resolve(self, req: &(impl DynRequester<Transfer = IntReq> + ?Sized)) -> Token<Self::Interned>;
|
||||||
|
fn get_id(self) -> NonZeroU64;
|
||||||
|
fn from_id(id: NonZeroU64) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interned for String {
|
||||||
|
type Marker = TStr;
|
||||||
|
fn intern(
|
||||||
|
self: Arc<Self>,
|
||||||
|
req: &(impl DynRequester<Transfer = IntReq> + ?Sized),
|
||||||
|
) -> Self::Marker {
|
||||||
|
req.request(InternStr(self))
|
||||||
|
}
|
||||||
|
fn bimap(interner: &mut Interner) -> &mut Bimap<Self> { &mut interner.strings }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InternMarker for TStr {
|
||||||
|
type Interned = String;
|
||||||
|
fn resolve(self, req: &(impl DynRequester<Transfer = IntReq> + ?Sized)) -> Token<Self::Interned> {
|
||||||
|
Token { marker: self, data: req.request(ExternStr(self)) }
|
||||||
|
}
|
||||||
|
fn get_id(self) -> NonZeroU64 { self.0 }
|
||||||
|
fn from_id(id: NonZeroU64) -> Self { Self(id) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Internable for str {
|
||||||
|
type Interned = String;
|
||||||
|
fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_string()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Internable for String {
|
||||||
|
type Interned = String;
|
||||||
|
fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_string()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interned for Vec<Token<String>> {
|
||||||
|
type Marker = TStrv;
|
||||||
|
fn intern(
|
||||||
|
self: Arc<Self>,
|
||||||
|
req: &(impl DynRequester<Transfer = IntReq> + ?Sized),
|
||||||
|
) -> Self::Marker {
|
||||||
|
req.request(InternStrv(Arc::new(self.iter().map(|t| t.marker()).collect())))
|
||||||
|
}
|
||||||
|
fn bimap(interner: &mut Interner) -> &mut Bimap<Self> { &mut interner.vecs }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InternMarker for TStrv {
|
||||||
|
type Interned = Vec<Token<String>>;
|
||||||
|
fn resolve(self, req: &(impl DynRequester<Transfer = IntReq> + ?Sized)) -> Token<Self::Interned> {
|
||||||
|
let data = Arc::new(req.request(ExternStrv(self)).iter().map(|m| deintern(*m)).collect_vec());
|
||||||
|
Token { marker: self, data }
|
||||||
|
}
|
||||||
|
fn get_id(self) -> NonZeroU64 { self.0 }
|
||||||
|
fn from_id(id: NonZeroU64) -> Self { Self(id) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Internable for [Token<String>] {
|
||||||
|
type Interned = Vec<Token<String>>;
|
||||||
|
fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_vec()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Internable for Vec<Token<String>> {
|
||||||
|
type Interned = Vec<Token<String>>;
|
||||||
|
fn get_owned(&self) -> Arc<Self::Interned> { Arc::new(self.to_vec()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Internable for Vec<TStr> {
|
||||||
|
type Interned = Vec<Token<String>>;
|
||||||
|
fn get_owned(&self) -> Arc<Self::Interned> {
|
||||||
|
Arc::new(self.iter().map(|ts| deintern(*ts)).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Internable for [TStr] {
|
||||||
|
type Interned = Vec<Token<String>>;
|
||||||
|
fn get_owned(&self) -> Arc<Self::Interned> {
|
||||||
|
Arc::new(self.iter().map(|ts| deintern(*ts)).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The number of references held to any token by the interner.
|
||||||
|
const BASE_RC: usize = 3;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn base_rc_correct() {
|
||||||
|
let tok = Token { marker: TStr(1.try_into().unwrap()), data: Arc::new("foo".to_string()) };
|
||||||
|
let mut bimap = Bimap::default();
|
||||||
|
bimap.insert(tok.clone());
|
||||||
|
assert_eq!(Arc::strong_count(&tok.data), BASE_RC + 1, "the bimap plus the current instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Bimap<T: Interned + ?Sized> {
|
||||||
|
intern: HashMap<Arc<T>, Token<T>>,
|
||||||
|
by_id: HashMap<T::Marker, Token<T>>,
|
||||||
|
}
|
||||||
|
impl<T: Interned + ?Sized> Bimap<T> {
|
||||||
|
pub fn insert(&mut self, token: Token<T>) {
|
||||||
|
self.intern.insert(token.data.clone(), token.clone());
|
||||||
|
self.by_id.insert(token.marker(), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn by_marker(&self, marker: T::Marker) -> Option<Token<T>> {
|
||||||
|
self.by_id.get(&marker).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn by_value<Q: Eq + hash::Hash>(&self, q: &Q) -> Option<Token<T>>
|
||||||
|
where T: Borrow<Q> {
|
||||||
|
(self.intern.raw_entry())
|
||||||
|
.from_hash(self.intern.hasher().hash_one(q), |k| k.as_ref().borrow() == q)
|
||||||
|
.map(|p| p.1.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sweep_replica(&mut self) -> Vec<T::Marker> {
|
||||||
|
(self.intern)
|
||||||
|
.extract_if(|k, _| Arc::strong_count(k) == BASE_RC)
|
||||||
|
.map(|(_, v)| {
|
||||||
|
self.by_id.remove(&v.marker());
|
||||||
|
v.marker()
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sweep_master(&mut self, retained: HashSet<T::Marker>) {
|
||||||
|
self.intern.retain(|k, v| BASE_RC < Arc::strong_count(k) || retained.contains(&v.marker()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Interned + ?Sized> Default for Bimap<T> {
|
||||||
|
fn default() -> Self { Self { by_id: HashMap::new(), intern: HashMap::new() } }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait UpComm {
|
||||||
|
fn up<R: Request>(&self, req: R) -> R::Response;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Interner {
|
||||||
|
strings: Bimap<String>,
|
||||||
|
vecs: Bimap<Vec<Token<String>>>,
|
||||||
|
master: Option<Box<dyn DynRequester<Transfer = IntReq>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
static ID: atomic::AtomicU64 = atomic::AtomicU64::new(1);
|
||||||
|
static INTERNER: Mutex<Option<Interner>> = Mutex::new(None);
|
||||||
|
|
||||||
|
pub fn interner() -> impl DerefMut<Target = Interner> {
|
||||||
|
struct G(MutexGuard<'static, Option<Interner>>);
|
||||||
|
impl Deref for G {
|
||||||
|
type Target = Interner;
|
||||||
|
fn deref(&self) -> &Self::Target { self.0.as_ref().expect("Guard pre-initialized") }
|
||||||
|
}
|
||||||
|
impl DerefMut for G {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
self.0.as_mut().expect("Guard pre-iniitialized")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut g = INTERNER.lock().unwrap();
|
||||||
|
g.get_or_insert_with(Interner::default);
|
||||||
|
G(g)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_replica(req: impl DynRequester<Transfer = IntReq> + 'static) {
|
||||||
|
let mut g = INTERNER.lock().unwrap();
|
||||||
|
assert!(g.is_none(), "Attempted to initialize replica interner after first use");
|
||||||
|
*g = Some(Interner {
|
||||||
|
strings: Bimap::default(),
|
||||||
|
vecs: Bimap::default(),
|
||||||
|
master: Some(Box::new(req)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intern<T: Interned>(t: &(impl Internable<Interned = T> + ?Sized)) -> Token<T> {
|
||||||
|
let mut g = interner();
|
||||||
|
let data = t.get_owned();
|
||||||
|
let marker = (g.master.as_mut()).map_or_else(
|
||||||
|
|| T::Marker::from_id(NonZeroU64::new(ID.fetch_add(1, atomic::Ordering::Relaxed)).unwrap()),
|
||||||
|
|c| data.clone().intern(&**c),
|
||||||
|
);
|
||||||
|
let tok = Token { marker, data };
|
||||||
|
T::bimap(&mut g).insert(tok.clone());
|
||||||
|
tok
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deintern<M: InternMarker>(marker: M) -> Token<M::Interned> {
|
||||||
|
let mut g = interner();
|
||||||
|
if let Some(tok) = M::Interned::bimap(&mut g).by_marker(marker) {
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
let master = g.master.as_mut().expect("ID not in local interner and this is master");
|
||||||
|
let token = marker.resolve(&**master);
|
||||||
|
M::Interned::bimap(&mut g).insert(token.clone());
|
||||||
|
token
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge_retained(into: &mut Retained, from: &Retained) {
|
||||||
|
into.strings = into.strings.iter().chain(&from.strings).copied().unique().collect();
|
||||||
|
into.vecs = into.vecs.iter().chain(&from.vecs).copied().unique().collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sweep_replica() -> Retained {
|
||||||
|
let mut g = interner();
|
||||||
|
assert!(g.master.is_some(), "Not a replica");
|
||||||
|
Retained { strings: g.strings.sweep_replica(), vecs: g.vecs.sweep_replica() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sweep_master(retained: Retained) {
|
||||||
|
let mut g = interner();
|
||||||
|
assert!(g.master.is_none(), "Not master");
|
||||||
|
g.strings.sweep_master(retained.strings.into_iter().collect());
|
||||||
|
g.vecs.sweep_master(retained.vecs.into_iter().collect());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a thread-local token instance and copy it. This ensures that the
|
||||||
|
/// interner will only be called the first time the expresion is executed,
|
||||||
|
/// and subsequent calls will just copy the token. Accepts a single static
|
||||||
|
/// expression (i.e. a literal).
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! intern {
|
||||||
|
($ty:ty : $expr:expr) => {{
|
||||||
|
thread_local! {
|
||||||
|
static VALUE: $crate::intern::Token<<$ty as $crate::intern::Internable>::Interned>
|
||||||
|
= $crate::intern::intern($expr as &$ty);
|
||||||
|
}
|
||||||
|
VALUE.with(|v| v.clone())
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn test_i() {
|
||||||
|
let _: Token<String> = intern!(str: "foo");
|
||||||
|
let _: Token<Vec<Token<String>>> = intern!([Token<String>]: &[
|
||||||
|
intern!(str: "bar"),
|
||||||
|
intern!(str: "baz")
|
||||||
|
]);
|
||||||
|
}
|
||||||
17
orchid-base/src/lib.rs
Normal file
17
orchid-base/src/lib.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
pub mod boxed_iter;
|
||||||
|
pub mod child;
|
||||||
|
pub mod clone;
|
||||||
|
pub mod combine;
|
||||||
|
pub mod event;
|
||||||
|
pub mod expr;
|
||||||
|
pub mod gen;
|
||||||
|
pub mod intern;
|
||||||
|
pub mod location;
|
||||||
|
pub mod name;
|
||||||
|
pub mod proj_error;
|
||||||
|
pub mod reqnot;
|
||||||
|
pub mod tree;
|
||||||
|
pub mod virt_fs;
|
||||||
|
pub mod join;
|
||||||
|
pub mod sequence;
|
||||||
|
pub mod api_utils;
|
||||||
@@ -7,7 +7,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::name::{NameLike, Sym};
|
use crate::name::Sym;
|
||||||
use crate::sym;
|
use crate::sym;
|
||||||
|
|
||||||
/// A full source code unit, such as a source file
|
/// A full source code unit, such as a source file
|
||||||
@@ -50,12 +50,11 @@ pub struct SourceRange {
|
|||||||
pub(crate) range: Range<usize>,
|
pub(crate) range: Range<usize>,
|
||||||
}
|
}
|
||||||
impl SourceRange {
|
impl SourceRange {
|
||||||
|
/// Create a new instance.
|
||||||
|
pub fn new(range: Range<usize>, code: SourceCode) -> Self { Self { code, range } }
|
||||||
/// Create a dud [SourceRange] for testing. Its value is unspecified and
|
/// Create a dud [SourceRange] for testing. Its value is unspecified and
|
||||||
/// volatile.
|
/// volatile.
|
||||||
pub fn mock() -> Self {
|
pub fn mock() -> Self { Self::new(0..1, SourceCode::new(sym!(test), Arc::new(String::new()))) }
|
||||||
let code = SourceCode { path: sym!(test), text: Arc::new(String::new()) };
|
|
||||||
SourceRange { range: 0..1, code }
|
|
||||||
}
|
|
||||||
/// Source code
|
/// Source code
|
||||||
pub fn code(&self) -> SourceCode { self.code.clone() }
|
pub fn code(&self) -> SourceCode { self.code.clone() }
|
||||||
/// Source text
|
/// Source text
|
||||||
@@ -64,11 +63,15 @@ impl SourceRange {
|
|||||||
pub fn path(&self) -> Sym { self.code.path.clone() }
|
pub fn path(&self) -> Sym { self.code.path.clone() }
|
||||||
/// Byte range
|
/// Byte range
|
||||||
pub fn range(&self) -> Range<usize> { self.range.clone() }
|
pub fn range(&self) -> Range<usize> { self.range.clone() }
|
||||||
|
/// 0-based index of first byte
|
||||||
|
pub fn start(&self) -> usize { self.range.start }
|
||||||
|
/// 0-based index of last byte + 1
|
||||||
|
pub fn end(&self) -> usize { self.range.end }
|
||||||
/// Syntactic location
|
/// Syntactic location
|
||||||
pub fn origin(&self) -> CodeOrigin { CodeOrigin::Source(self.clone()) }
|
pub fn origin(&self) -> CodeOrigin { CodeOrigin::Source(self.clone()) }
|
||||||
/// Transform the numeric byte range
|
/// Transform the numeric byte range
|
||||||
pub fn map_range(&self, map: impl FnOnce(Range<usize>) -> Range<usize>) -> Self {
|
pub fn map_range(&self, map: impl FnOnce(Range<usize>) -> Range<usize>) -> Self {
|
||||||
Self { code: self.code(), range: map(self.range()) }
|
Self::new(map(self.range()), self.code())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl fmt::Debug for SourceRange {
|
impl fmt::Debug for SourceRange {
|
||||||
488
orchid-base/src/name.rs
Normal file
488
orchid-base/src/name.rs
Normal file
@@ -0,0 +1,488 @@
|
|||||||
|
//! Various datatypes that all represent namespaced names.
|
||||||
|
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::iter::Cloned;
|
||||||
|
use std::num::{NonZeroU64, NonZeroUsize};
|
||||||
|
use std::ops::{Deref, Index};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::{fmt, slice, vec};
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
use trait_set::trait_set;
|
||||||
|
|
||||||
|
use crate::intern::{intern, InternMarker, Token};
|
||||||
|
|
||||||
|
trait_set! {
|
||||||
|
/// Traits that all name iterators should implement
|
||||||
|
pub trait NameIter = Iterator<Item = Token<String>> + DoubleEndedIterator + ExactSizeIterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A borrowed name fragment which can be empty. See [VPath] for the owned
|
||||||
|
/// variant.
|
||||||
|
#[derive(Hash, PartialEq, Eq)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct PathSlice([Token<String>]);
|
||||||
|
impl PathSlice {
|
||||||
|
/// Create a new [PathSlice]
|
||||||
|
pub fn new(slice: &[Token<String>]) -> &PathSlice {
|
||||||
|
// SAFETY: This is ok because PathSlice is #[repr(transparent)]
|
||||||
|
unsafe { &*(slice as *const [Token<String>] as *const PathSlice) }
|
||||||
|
}
|
||||||
|
/// Convert to an owned name fragment
|
||||||
|
pub fn to_vpath(&self) -> VPath { VPath(self.0.to_vec()) }
|
||||||
|
/// Iterate over the tokens
|
||||||
|
pub fn iter(&self) -> impl NameIter + '_ { self.into_iter() }
|
||||||
|
/// Iterate over the segments
|
||||||
|
pub fn str_iter(&self) -> impl Iterator<Item = &'_ str> {
|
||||||
|
Box::new(self.0.iter().map(|s| s.as_str()))
|
||||||
|
}
|
||||||
|
/// Find the longest shared prefix of this name and another sequence
|
||||||
|
pub fn coprefix<'a>(&'a self, other: &PathSlice) -> &'a PathSlice {
|
||||||
|
&self[0..self.iter().zip(other.iter()).take_while(|(l, r)| l == r).count()]
|
||||||
|
}
|
||||||
|
/// Find the longest shared suffix of this name and another sequence
|
||||||
|
pub fn cosuffix<'a>(&'a self, other: &PathSlice) -> &'a PathSlice {
|
||||||
|
&self[0..self.iter().zip(other.iter()).take_while(|(l, r)| l == r).count()]
|
||||||
|
}
|
||||||
|
/// Remove another
|
||||||
|
pub fn strip_prefix<'a>(&'a self, other: &PathSlice) -> Option<&'a PathSlice> {
|
||||||
|
let shared = self.coprefix(other).len();
|
||||||
|
(shared == other.len()).then_some(PathSlice::new(&self[shared..]))
|
||||||
|
}
|
||||||
|
/// Number of path segments
|
||||||
|
pub fn len(&self) -> usize { self.0.len() }
|
||||||
|
/// Whether there are any path segments. In other words, whether this is a
|
||||||
|
/// valid name
|
||||||
|
pub fn is_empty(&self) -> bool { self.len() == 0 }
|
||||||
|
/// Obtain a reference to the held slice. With all indexing traits shadowed,
|
||||||
|
/// this is better done explicitly
|
||||||
|
pub fn as_slice(&self) -> &[Token<String>] { self }
|
||||||
|
/// Global empty path slice
|
||||||
|
pub fn empty() -> &'static Self { PathSlice::new(&[]) }
|
||||||
|
}
|
||||||
|
impl fmt::Debug for PathSlice {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "VName({self})") }
|
||||||
|
}
|
||||||
|
impl fmt::Display for PathSlice {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.str_iter().join("::"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Borrow<[Token<String>]> for PathSlice {
|
||||||
|
fn borrow(&self) -> &[Token<String>] { &self.0 }
|
||||||
|
}
|
||||||
|
impl<'a> IntoIterator for &'a PathSlice {
|
||||||
|
type IntoIter = Cloned<slice::Iter<'a, Token<String>>>;
|
||||||
|
type Item = Token<String>;
|
||||||
|
fn into_iter(self) -> Self::IntoIter { self.0.iter().cloned() }
|
||||||
|
}
|
||||||
|
|
||||||
|
mod idx_impls {
|
||||||
|
use std::ops;
|
||||||
|
|
||||||
|
use super::PathSlice;
|
||||||
|
use crate::intern::Token;
|
||||||
|
|
||||||
|
impl ops::Index<usize> for PathSlice {
|
||||||
|
type Output = Token<String>;
|
||||||
|
fn index(&self, index: usize) -> &Self::Output { &self.0[index] }
|
||||||
|
}
|
||||||
|
macro_rules! impl_range_index_for_pathslice {
|
||||||
|
($range:ty) => {
|
||||||
|
impl ops::Index<$range> for PathSlice {
|
||||||
|
type Output = Self;
|
||||||
|
fn index(&self, index: $range) -> &Self::Output { Self::new(&self.0[index]) }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_range_index_for_pathslice!(ops::RangeFull);
|
||||||
|
impl_range_index_for_pathslice!(ops::RangeFrom<usize>);
|
||||||
|
impl_range_index_for_pathslice!(ops::RangeTo<usize>);
|
||||||
|
impl_range_index_for_pathslice!(ops::Range<usize>);
|
||||||
|
impl_range_index_for_pathslice!(ops::RangeInclusive<usize>);
|
||||||
|
impl_range_index_for_pathslice!(ops::RangeToInclusive<usize>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for PathSlice {
|
||||||
|
type Target = [Token<String>];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
|
}
|
||||||
|
impl Borrow<PathSlice> for [Token<String>] {
|
||||||
|
fn borrow(&self) -> &PathSlice { PathSlice::new(self) }
|
||||||
|
}
|
||||||
|
impl<const N: usize> Borrow<PathSlice> for [Token<String>; N] {
|
||||||
|
fn borrow(&self) -> &PathSlice { PathSlice::new(&self[..]) }
|
||||||
|
}
|
||||||
|
impl Borrow<PathSlice> for Vec<Token<String>> {
|
||||||
|
fn borrow(&self) -> &PathSlice { PathSlice::new(&self[..]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A token path which may be empty. [VName] is the non-empty,
|
||||||
|
/// [PathSlice] is the borrowed version
|
||||||
|
#[derive(Clone, Default, Hash, PartialEq, Eq)]
|
||||||
|
pub struct VPath(pub Vec<Token<String>>);
|
||||||
|
impl VPath {
|
||||||
|
/// Collect segments into a vector
|
||||||
|
pub fn new(items: impl IntoIterator<Item = Token<String>>) -> Self {
|
||||||
|
Self(items.into_iter().collect())
|
||||||
|
}
|
||||||
|
/// Number of path segments
|
||||||
|
pub fn len(&self) -> usize { self.0.len() }
|
||||||
|
/// Whether there are any path segments. In other words, whether this is a
|
||||||
|
/// valid name
|
||||||
|
pub fn is_empty(&self) -> bool { self.len() == 0 }
|
||||||
|
/// Prepend some tokens to the path
|
||||||
|
pub fn prefix(self, items: impl IntoIterator<Item = Token<String>>) -> Self {
|
||||||
|
Self(items.into_iter().chain(self.0).collect())
|
||||||
|
}
|
||||||
|
/// Append some tokens to the path
|
||||||
|
pub fn suffix(self, items: impl IntoIterator<Item = Token<String>>) -> Self {
|
||||||
|
Self(self.0.into_iter().chain(items).collect())
|
||||||
|
}
|
||||||
|
/// Partition the string by `::` namespace separators
|
||||||
|
pub fn parse(s: &str) -> Self {
|
||||||
|
Self(if s.is_empty() { vec![] } else { s.split("::").map(intern).collect() })
|
||||||
|
}
|
||||||
|
/// Walk over the segments
|
||||||
|
pub fn str_iter(&self) -> impl Iterator<Item = &'_ str> {
|
||||||
|
Box::new(self.0.iter().map(|s| s.as_str()))
|
||||||
|
}
|
||||||
|
/// Try to convert into non-empty version
|
||||||
|
pub fn into_name(self) -> Result<VName, EmptyNameError> { VName::new(self.0) }
|
||||||
|
/// Add a token to the path. Since now we know that it can't be empty, turn it
|
||||||
|
/// into a name.
|
||||||
|
pub fn name_with_prefix(self, name: Token<String>) -> VName {
|
||||||
|
VName(self.into_iter().chain([name]).collect())
|
||||||
|
}
|
||||||
|
/// Add a token to the beginning of the. Since now we know that it can't be
|
||||||
|
/// empty, turn it into a name.
|
||||||
|
pub fn name_with_suffix(self, name: Token<String>) -> VName {
|
||||||
|
VName([name].into_iter().chain(self).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a fs path to a vpath
|
||||||
|
pub fn from_path(path: &Path) -> Option<(Self, bool)> {
|
||||||
|
let to_vpath =
|
||||||
|
|p: &Path| p.iter().map(|c| c.to_str().map(intern)).collect::<Option<_>>().map(VPath);
|
||||||
|
match path.extension().map(|s| s.to_str()) {
|
||||||
|
Some(Some("orc")) => Some((to_vpath(&path.with_extension(""))?, true)),
|
||||||
|
None => Some((to_vpath(path)?, false)),
|
||||||
|
Some(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl fmt::Debug for VPath {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "VName({self})") }
|
||||||
|
}
|
||||||
|
impl fmt::Display for VPath {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.str_iter().join("::"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FromIterator<Token<String>> for VPath {
|
||||||
|
fn from_iter<T: IntoIterator<Item = Token<String>>>(iter: T) -> Self {
|
||||||
|
Self(iter.into_iter().collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl IntoIterator for VPath {
|
||||||
|
type Item = Token<String>;
|
||||||
|
type IntoIter = vec::IntoIter<Self::Item>;
|
||||||
|
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
|
||||||
|
}
|
||||||
|
impl Borrow<[Token<String>]> for VPath {
|
||||||
|
fn borrow(&self) -> &[Token<String>] { self.0.borrow() }
|
||||||
|
}
|
||||||
|
impl Borrow<PathSlice> for VPath {
|
||||||
|
fn borrow(&self) -> &PathSlice { PathSlice::new(&self.0[..]) }
|
||||||
|
}
|
||||||
|
impl Deref for VPath {
|
||||||
|
type Target = PathSlice;
|
||||||
|
fn deref(&self) -> &Self::Target { self.borrow() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Index<T> for VPath
|
||||||
|
where PathSlice: Index<T>
|
||||||
|
{
|
||||||
|
type Output = <PathSlice as Index<T>>::Output;
|
||||||
|
|
||||||
|
fn index(&self, index: T) -> &Self::Output { &Borrow::<PathSlice>::borrow(self)[index] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A mutable representation of a namespaced identifier of at least one segment.
|
||||||
|
///
|
||||||
|
/// These names may be relative or otherwise partially processed.
|
||||||
|
///
|
||||||
|
/// See also [Sym] for the immutable representation, and [VPath] for possibly
|
||||||
|
/// empty values
|
||||||
|
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct VName(Vec<Token<String>>);
|
||||||
|
impl VName {
|
||||||
|
/// Assert that the sequence isn't empty and wrap it in [VName] to represent
|
||||||
|
/// this invariant
|
||||||
|
pub fn new(items: impl IntoIterator<Item = Token<String>>) -> Result<Self, EmptyNameError> {
|
||||||
|
let data: Vec<_> = items.into_iter().collect();
|
||||||
|
if data.is_empty() { Err(EmptyNameError) } else { Ok(Self(data)) }
|
||||||
|
}
|
||||||
|
/// Unwrap the enclosed vector
|
||||||
|
pub fn into_vec(self) -> Vec<Token<String>> { self.0 }
|
||||||
|
/// Get a reference to the enclosed vector
|
||||||
|
pub fn vec(&self) -> &Vec<Token<String>> { &self.0 }
|
||||||
|
/// Mutable access to the underlying vector. To ensure correct results, this
|
||||||
|
/// must never be empty.
|
||||||
|
pub fn vec_mut(&mut self) -> &mut Vec<Token<String>> { &mut self.0 }
|
||||||
|
/// Intern the name and return a [Sym]
|
||||||
|
pub fn to_sym(&self) -> Sym { Sym(intern(&self.0[..])) }
|
||||||
|
/// If this name has only one segment, return it
|
||||||
|
pub fn as_root(&self) -> Option<Token<String>> { self.0.iter().exactly_one().ok().cloned() }
|
||||||
|
/// Prepend the segments to this name
|
||||||
|
#[must_use = "This is a pure function"]
|
||||||
|
pub fn prefix(self, items: impl IntoIterator<Item = Token<String>>) -> Self {
|
||||||
|
Self(items.into_iter().chain(self.0).collect())
|
||||||
|
}
|
||||||
|
/// Append the segments to this name
|
||||||
|
#[must_use = "This is a pure function"]
|
||||||
|
pub fn suffix(self, items: impl IntoIterator<Item = Token<String>>) -> Self {
|
||||||
|
Self(self.0.into_iter().chain(items).collect())
|
||||||
|
}
|
||||||
|
/// Read a `::` separated namespaced name
|
||||||
|
pub fn parse(s: &str) -> Result<Self, EmptyNameError> { Self::new(VPath::parse(s)) }
|
||||||
|
/// Obtain an iterator over the segments of the name
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = Token<String>> + '_ { self.0.iter().cloned() }
|
||||||
|
}
|
||||||
|
impl fmt::Debug for VName {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "VName({self})") }
|
||||||
|
}
|
||||||
|
impl fmt::Display for VName {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.str_iter().join("::"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl IntoIterator for VName {
|
||||||
|
type Item = Token<String>;
|
||||||
|
type IntoIter = vec::IntoIter<Self::Item>;
|
||||||
|
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
|
||||||
|
}
|
||||||
|
impl<T> Index<T> for VName
|
||||||
|
where PathSlice: Index<T>
|
||||||
|
{
|
||||||
|
type Output = <PathSlice as Index<T>>::Output;
|
||||||
|
|
||||||
|
fn index(&self, index: T) -> &Self::Output { &self.deref()[index] }
|
||||||
|
}
|
||||||
|
impl Borrow<[Token<String>]> for VName {
|
||||||
|
fn borrow(&self) -> &[Token<String>] { self.0.borrow() }
|
||||||
|
}
|
||||||
|
impl Borrow<PathSlice> for VName {
|
||||||
|
fn borrow(&self) -> &PathSlice { PathSlice::new(&self.0[..]) }
|
||||||
|
}
|
||||||
|
impl Deref for VName {
|
||||||
|
type Target = PathSlice;
|
||||||
|
fn deref(&self) -> &Self::Target { self.borrow() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error produced when a non-empty name [VName] or [Sym] is constructed with an
|
||||||
|
/// empty sequence
|
||||||
|
#[derive(Debug, Copy, Clone, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct EmptyNameError;
|
||||||
|
impl TryFrom<&[Token<String>]> for VName {
|
||||||
|
type Error = EmptyNameError;
|
||||||
|
fn try_from(value: &[Token<String>]) -> Result<Self, Self::Error> {
|
||||||
|
Self::new(value.iter().cloned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An interned representation of a namespaced identifier.
|
||||||
|
///
|
||||||
|
/// These names are always absolute.
|
||||||
|
///
|
||||||
|
/// See also [VName]
|
||||||
|
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct Sym(Token<Vec<Token<String>>>);
|
||||||
|
impl Sym {
|
||||||
|
/// Assert that the sequence isn't empty, intern it and wrap it in a [Sym] to
|
||||||
|
/// represent this invariant
|
||||||
|
pub fn new(v: impl IntoIterator<Item = Token<String>>) -> Result<Self, EmptyNameError> {
|
||||||
|
let items = v.into_iter().collect_vec();
|
||||||
|
Self::from_tok(intern(&items[..]))
|
||||||
|
}
|
||||||
|
/// Read a `::` separated namespaced name.
|
||||||
|
pub fn parse(s: &str) -> Result<Self, EmptyNameError> {
|
||||||
|
Ok(Sym(intern(&VName::parse(s)?.into_vec()[..])))
|
||||||
|
}
|
||||||
|
/// Assert that a token isn't empty, and wrap it in a [Sym]
|
||||||
|
pub fn from_tok(t: Token<Vec<Token<String>>>) -> Result<Self, EmptyNameError> {
|
||||||
|
if t.is_empty() { Err(EmptyNameError) } else { Ok(Self(t)) }
|
||||||
|
}
|
||||||
|
/// Grab the interner token
|
||||||
|
pub fn tok(&self) -> Token<Vec<Token<String>>> { self.0.clone() }
|
||||||
|
/// Get a number unique to this name suitable for arbitrary ordering.
|
||||||
|
pub fn id(&self) -> NonZeroU64 { self.0.marker().get_id() }
|
||||||
|
/// Extern the sym for editing
|
||||||
|
pub fn to_vname(&self) -> VName { VName(self[..].to_vec()) }
|
||||||
|
}
|
||||||
|
impl fmt::Debug for Sym {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Sym({self})") }
|
||||||
|
}
|
||||||
|
impl fmt::Display for Sym {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.str_iter().join("::"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> Index<T> for Sym
|
||||||
|
where PathSlice: Index<T>
|
||||||
|
{
|
||||||
|
type Output = <PathSlice as Index<T>>::Output;
|
||||||
|
|
||||||
|
fn index(&self, index: T) -> &Self::Output { &self.deref()[index] }
|
||||||
|
}
|
||||||
|
impl Borrow<[Token<String>]> for Sym {
|
||||||
|
fn borrow(&self) -> &[Token<String>] { &self.0[..] }
|
||||||
|
}
|
||||||
|
impl Borrow<PathSlice> for Sym {
|
||||||
|
fn borrow(&self) -> &PathSlice { PathSlice::new(&self.0[..]) }
|
||||||
|
}
|
||||||
|
impl Deref for Sym {
|
||||||
|
type Target = PathSlice;
|
||||||
|
fn deref(&self) -> &Self::Target { self.borrow() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An abstraction over tokenized vs non-tokenized names so that they can be
|
||||||
|
/// handled together in datastructures. The names can never be empty
|
||||||
|
#[allow(clippy::len_without_is_empty)] // never empty
|
||||||
|
pub trait NameLike:
|
||||||
|
'static + Clone + Eq + Hash + fmt::Debug + fmt::Display + Borrow<PathSlice>
|
||||||
|
{
|
||||||
|
/// Convert into held slice
|
||||||
|
fn as_slice(&self) -> &[Token<String>] { Borrow::<PathSlice>::borrow(self) }
|
||||||
|
/// Get iterator over tokens
|
||||||
|
fn iter(&self) -> impl NameIter + '_ { self.as_slice().iter().cloned() }
|
||||||
|
/// Get iterator over string segments
|
||||||
|
fn str_iter(&self) -> impl Iterator<Item = &'_ str> + '_ {
|
||||||
|
self.as_slice().iter().map(|t| t.as_str())
|
||||||
|
}
|
||||||
|
/// Fully resolve the name for printing
|
||||||
|
#[must_use]
|
||||||
|
fn to_strv(&self) -> Vec<String> { self.iter().map(|s| s.to_string()).collect() }
|
||||||
|
/// Format the name as an approximate filename
|
||||||
|
fn as_src_path(&self) -> String { format!("{}.orc", self.iter().join("/")) }
|
||||||
|
/// Return the number of segments in the name
|
||||||
|
fn len(&self) -> NonZeroUsize {
|
||||||
|
NonZeroUsize::try_from(self.iter().count()).expect("NameLike never empty")
|
||||||
|
}
|
||||||
|
/// Like slice's `split_first` except we know that it always returns Some
|
||||||
|
fn split_first(&self) -> (Token<String>, &PathSlice) {
|
||||||
|
let (foot, torso) = self.as_slice().split_last().expect("NameLike never empty");
|
||||||
|
(foot.clone(), PathSlice::new(torso))
|
||||||
|
}
|
||||||
|
/// Like slice's `split_last` except we know that it always returns Some
|
||||||
|
fn split_last(&self) -> (Token<String>, &PathSlice) {
|
||||||
|
let (foot, torso) = self.as_slice().split_last().expect("NameLike never empty");
|
||||||
|
(foot.clone(), PathSlice::new(torso))
|
||||||
|
}
|
||||||
|
/// Get the first element
|
||||||
|
fn first(&self) -> Token<String> { self.split_first().0 }
|
||||||
|
/// Get the last element
|
||||||
|
fn last(&self) -> Token<String> { self.split_last().0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NameLike for Sym {}
|
||||||
|
impl NameLike for VName {}
|
||||||
|
|
||||||
|
/// Create a [Sym] literal.
|
||||||
|
///
|
||||||
|
/// Both the name and its components will be cached in a thread-local static so
|
||||||
|
/// that subsequent executions of the expression only incur an Arc-clone for
|
||||||
|
/// cloning the token.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! sym {
|
||||||
|
($seg1:tt $( :: $seg:tt)*) => {
|
||||||
|
$crate::name::Sym::from_tok($crate::intern!([$crate::intern::Token<String>]: &[
|
||||||
|
$crate::intern!(str: stringify!($seg1))
|
||||||
|
$( , $crate::intern!(str: stringify!($seg)) )*
|
||||||
|
])).unwrap()
|
||||||
|
};
|
||||||
|
(@NAME $seg:tt) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [VName] literal.
|
||||||
|
///
|
||||||
|
/// The components are interned much like in [sym].
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vname {
|
||||||
|
($seg1:tt $( :: $seg:tt)*) => {
|
||||||
|
$crate::name::VName::new([
|
||||||
|
$crate::intern!(str: stringify!($seg1))
|
||||||
|
$( , $crate::intern!(str: stringify!($seg)) )*
|
||||||
|
]).unwrap()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [VPath] literal.
|
||||||
|
///
|
||||||
|
/// The components are interned much like in [sym].
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! vpath {
|
||||||
|
($seg1:tt $( :: $seg:tt)+) => {
|
||||||
|
$crate::name::VPath(vec![
|
||||||
|
$crate::intern!(str: stringify!($seg1))
|
||||||
|
$( , $crate::intern!(str: stringify!($seg)) )+
|
||||||
|
])
|
||||||
|
};
|
||||||
|
() => {
|
||||||
|
$crate::name::VPath(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a &[PathSlice] literal.
|
||||||
|
///
|
||||||
|
/// The components are interned much like in [sym]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! path_slice {
|
||||||
|
($seg1:tt $( :: $seg:tt)+) => {
|
||||||
|
$crate::name::PathSlice::new(&[
|
||||||
|
$crate::intern!(str: stringify!($seg1))
|
||||||
|
$( , $crate::intern!(str: stringify!($seg)) )+
|
||||||
|
])
|
||||||
|
};
|
||||||
|
() => {
|
||||||
|
$crate::name::PathSlice::new(&[])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
|
use super::{PathSlice, Sym, VName};
|
||||||
|
use crate::intern::{intern, Token};
|
||||||
|
use crate::name::VPath;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn recur() {
|
||||||
|
let myname = vname!(foo::bar);
|
||||||
|
let _borrowed_slice: &[Token<String>] = myname.borrow();
|
||||||
|
let _borrowed_pathslice: &PathSlice = myname.borrow();
|
||||||
|
let _deref_pathslice: &PathSlice = &myname;
|
||||||
|
let _as_slice_out: &[Token<String>] = myname.as_slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn literals() {
|
||||||
|
assert_eq!(
|
||||||
|
sym!(foo::bar::baz),
|
||||||
|
Sym::new([intern("foo"), intern("bar"), intern("baz")]).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
vname!(foo::bar::baz),
|
||||||
|
VName::new([intern("foo"), intern("bar"), intern("baz")]).unwrap()
|
||||||
|
);
|
||||||
|
assert_eq!(vpath!(foo::bar::baz), VPath::new([intern("foo"), intern("bar"), intern("baz")]));
|
||||||
|
assert_eq!(
|
||||||
|
path_slice!(foo::bar::baz),
|
||||||
|
PathSlice::new(&[intern("foo"), intern("bar"), intern("baz")])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ use dyn_clone::{clone_box, DynClone};
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::location::CodeOrigin;
|
use crate::location::CodeOrigin;
|
||||||
use crate::utils::boxed_iter::{box_once, BoxedIter};
|
use crate::boxed_iter::{box_once, BoxedIter};
|
||||||
#[allow(unused)] // for doc
|
#[allow(unused)] // for doc
|
||||||
use crate::virt_fs::CodeNotFound;
|
use crate::virt_fs::CodeNotFound;
|
||||||
|
|
||||||
222
orchid-base/src/reqnot.rs
Normal file
222
orchid-base/src/reqnot.rs
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
use std::mem;
|
||||||
|
use std::ops::{BitAnd, Deref};
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::mpsc::{sync_channel, SyncSender};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use dyn_clone::{clone_box, DynClone};
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use orchid_api_traits::{Coding, Decode, Encode, MsgSet, Request};
|
||||||
|
use trait_set::trait_set;
|
||||||
|
|
||||||
|
trait_set! {
|
||||||
|
pub trait SendFn<T: MsgSet> = for<'a> FnMut(&'a [u8], ReqNot<T>) + DynClone + Send + 'static;
|
||||||
|
pub trait ReqFn<T: MsgSet> = FnMut(RequestHandle<T>) + Send + 'static;
|
||||||
|
pub trait NotifFn<T: MsgSet> = for<'a> FnMut(T::InNot, ReqNot<T>) + Send + Sync + 'static;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_id(message: &[u8]) -> (u64, &[u8]) {
|
||||||
|
(u64::from_be_bytes(message[..8].to_vec().try_into().unwrap()), &message[8..])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RequestHandle<T: MsgSet> {
|
||||||
|
id: u64,
|
||||||
|
message: T::InReq,
|
||||||
|
send: Box<dyn SendFn<T>>,
|
||||||
|
parent: ReqNot<T>,
|
||||||
|
fulfilled: AtomicBool,
|
||||||
|
}
|
||||||
|
impl<MS: MsgSet> RequestHandle<MS> {
|
||||||
|
pub fn reqnot(&self) -> ReqNot<MS> { self.parent.clone() }
|
||||||
|
pub fn req(&self) -> &MS::InReq { &self.message }
|
||||||
|
fn respond(&self, response: &impl Encode) {
|
||||||
|
assert!(!self.fulfilled.swap(true, Ordering::Relaxed), "Already responded");
|
||||||
|
let mut buf = (!self.id).to_be_bytes().to_vec();
|
||||||
|
response.encode(&mut buf);
|
||||||
|
clone_box(&*self.send)(&buf, self.parent.clone());
|
||||||
|
}
|
||||||
|
pub fn handle<T: Request>(&self, _: &T, rep: &T::Response) { self.respond(rep) }
|
||||||
|
}
|
||||||
|
impl<MS: MsgSet> Drop for RequestHandle<MS> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
debug_assert!(self.fulfilled.load(Ordering::Relaxed), "Request dropped without response")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn respond_with<R: Request>(r: &R, f: impl FnOnce(&R) -> R::Response) -> Vec<u8> {
|
||||||
|
r.respond(f(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ReqNotData<T: MsgSet> {
|
||||||
|
id: u64,
|
||||||
|
send: Box<dyn SendFn<T>>,
|
||||||
|
notif: Box<dyn NotifFn<T>>,
|
||||||
|
req: Box<dyn ReqFn<T>>,
|
||||||
|
responses: HashMap<u64, SyncSender<Vec<u8>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RawReply(Vec<u8>);
|
||||||
|
impl Deref for RawReply {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &Self::Target { get_id(&self.0[..]).1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ReqNot<T: MsgSet>(Arc<Mutex<ReqNotData<T>>>);
|
||||||
|
impl<T: MsgSet> ReqNot<T> {
|
||||||
|
pub fn new(send: impl SendFn<T>, notif: impl NotifFn<T>, req: impl ReqFn<T>) -> Self {
|
||||||
|
Self(Arc::new(Mutex::new(ReqNotData {
|
||||||
|
id: 1,
|
||||||
|
send: Box::new(send),
|
||||||
|
notif: Box::new(notif),
|
||||||
|
req: Box::new(req),
|
||||||
|
responses: HashMap::new(),
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Can be called from a polling thread or dispatched in any other way
|
||||||
|
pub fn receive(&self, message: Vec<u8>) {
|
||||||
|
let mut g = self.0.lock().unwrap();
|
||||||
|
let (id, payload) = get_id(&message[..]);
|
||||||
|
if id == 0 {
|
||||||
|
(g.notif)(T::InNot::decode(&mut &payload[..]), self.clone())
|
||||||
|
} else if 0 < id.bitand(1 << 63) {
|
||||||
|
let sender = g.responses.remove(&!id).expect("Received response for invalid message");
|
||||||
|
sender.send(message).unwrap();
|
||||||
|
} else {
|
||||||
|
let send = clone_box(&*g.send);
|
||||||
|
let message = T::InReq::decode(&mut &payload[..]);
|
||||||
|
(g.req)(RequestHandle { id, message, send, fulfilled: false.into(), parent: self.clone() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn notify<N: Coding + Into<T::OutNot>>(&self, notif: N) {
|
||||||
|
let mut send = clone_box(&*self.0.lock().unwrap().send);
|
||||||
|
let mut buf = vec![0; 8];
|
||||||
|
let msg: T::OutNot = notif.into();
|
||||||
|
msg.encode(&mut buf);
|
||||||
|
send(&buf, self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MappedRequester<'a, T>(Box<dyn Fn(T) -> RawReply + Send + Sync + 'a>);
|
||||||
|
impl<'a, T> MappedRequester<'a, T> {
|
||||||
|
fn new<U: DynRequester + 'a>(req: U) -> Self
|
||||||
|
where T: Into<U::Transfer> {
|
||||||
|
MappedRequester(Box::new(move |t| req.raw_request(t.into())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> DynRequester for MappedRequester<'a, T> {
|
||||||
|
type Transfer = T;
|
||||||
|
fn raw_request(&self, data: Self::Transfer) -> RawReply { self.0(data) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: MsgSet> DynRequester for ReqNot<T> {
|
||||||
|
type Transfer = T::OutReq;
|
||||||
|
fn raw_request(&self, req: Self::Transfer) -> RawReply {
|
||||||
|
let mut g = self.0.lock().unwrap();
|
||||||
|
let id = g.id;
|
||||||
|
g.id += 1;
|
||||||
|
let mut buf = id.to_be_bytes().to_vec();
|
||||||
|
req.encode(&mut buf);
|
||||||
|
let (send, recv) = sync_channel(1);
|
||||||
|
g.responses.insert(id, send);
|
||||||
|
let mut send = clone_box(&*g.send);
|
||||||
|
mem::drop(g);
|
||||||
|
send(&buf, self.clone());
|
||||||
|
RawReply(recv.recv().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait DynRequester: Send + Sync {
|
||||||
|
type Transfer;
|
||||||
|
fn raw_request(&self, data: Self::Transfer) -> RawReply;
|
||||||
|
}
|
||||||
|
pub trait Requester: DynRequester {
|
||||||
|
#[must_use = "These types are subject to change with protocol versions. \
|
||||||
|
If you don't want to use the return value, At a minimum, force the type."]
|
||||||
|
fn request<R: Request + Into<Self::Transfer>>(&self, data: R) -> R::Response;
|
||||||
|
fn map<'a, U: Into<Self::Transfer>>(self) -> MappedRequester<'a, U>
|
||||||
|
where Self: Sized + 'a {
|
||||||
|
MappedRequester::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, This: DynRequester + ?Sized + 'a> Requester for This {
|
||||||
|
fn request<R: Request + Into<Self::Transfer>>(&self, data: R) -> R::Response {
|
||||||
|
R::Response::decode(&mut &self.raw_request(data.into())[..])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: MsgSet> Clone for ReqNot<T> {
|
||||||
|
fn clone(&self) -> Self { Self(self.0.clone()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use orchid_api_derive::Coding;
|
||||||
|
use orchid_api_traits::Request;
|
||||||
|
|
||||||
|
use super::{MsgSet, ReqNot};
|
||||||
|
use crate::{clone, reqnot::Requester as _};
|
||||||
|
|
||||||
|
#[derive(Coding, Debug, PartialEq)]
|
||||||
|
pub struct TestReq(u8);
|
||||||
|
impl Request for TestReq {
|
||||||
|
type Response = u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TestMsgSet;
|
||||||
|
impl MsgSet for TestMsgSet {
|
||||||
|
type InNot = u8;
|
||||||
|
type InReq = TestReq;
|
||||||
|
type OutNot = u8;
|
||||||
|
type OutReq = TestReq;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn notification() {
|
||||||
|
let received = Arc::new(Mutex::new(None));
|
||||||
|
let receiver = ReqNot::<TestMsgSet>::new(
|
||||||
|
|_, _| panic!("Should not send anything"),
|
||||||
|
clone!(received; move |notif, _| *received.lock().unwrap() = Some(notif)),
|
||||||
|
|_| panic!("Not receiving a request"),
|
||||||
|
);
|
||||||
|
let sender = ReqNot::<TestMsgSet>::new(
|
||||||
|
clone!(receiver; move |d, _| receiver.receive(d.to_vec())),
|
||||||
|
|_, _| panic!("Should not receive notif"),
|
||||||
|
|_| panic!("Should not receive request"),
|
||||||
|
);
|
||||||
|
sender.notify(3);
|
||||||
|
assert_eq!(*received.lock().unwrap(), Some(3));
|
||||||
|
sender.notify(4);
|
||||||
|
assert_eq!(*received.lock().unwrap(), Some(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn request() {
|
||||||
|
let receiver = Arc::new(Mutex::<Option<ReqNot<TestMsgSet>>>::new(None));
|
||||||
|
let sender = Arc::new(ReqNot::<TestMsgSet>::new(
|
||||||
|
{
|
||||||
|
let receiver = receiver.clone();
|
||||||
|
move |d, _| receiver.lock().unwrap().as_ref().unwrap().receive(d.to_vec())
|
||||||
|
},
|
||||||
|
|_, _| panic!("Should not receive notif"),
|
||||||
|
|_| panic!("Should not receive request"),
|
||||||
|
));
|
||||||
|
*receiver.lock().unwrap() = Some(ReqNot::new(
|
||||||
|
{
|
||||||
|
let sender = sender.clone();
|
||||||
|
move |d, _| sender.receive(d.to_vec())
|
||||||
|
},
|
||||||
|
|_, _| panic!("Not receiving notifs"),
|
||||||
|
|req| {
|
||||||
|
assert_eq!(req.req(), &TestReq(5));
|
||||||
|
req.respond(&6u8);
|
||||||
|
},
|
||||||
|
));
|
||||||
|
let response = sender.request(TestReq(5));
|
||||||
|
assert_eq!(response, 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,7 +45,7 @@ impl<'a> System<'a> {
|
|||||||
/// An error raised when a system fails to load a path. This usually means that
|
/// An error raised when a system fails to load a path. This usually means that
|
||||||
/// another system the current one depends on did not get loaded
|
/// another system the current one depends on did not get loaded
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MissingSystemCode {
|
struct MissingSystemCode {
|
||||||
path: VName,
|
path: VName,
|
||||||
system: Vec<String>,
|
system: Vec<String>,
|
||||||
referrer: VName,
|
referrer: VName,
|
||||||
630
orchid-base/src/tree.rs
Normal file
630
orchid-base/src/tree.rs
Normal file
@@ -0,0 +1,630 @@
|
|||||||
|
//! Generic module tree structure
|
||||||
|
//!
|
||||||
|
//! Used by various stages of the pipeline with different parameters
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use itertools::Itertools as _;
|
||||||
|
use never::Never;
|
||||||
|
use substack::Substack;
|
||||||
|
use trait_set::trait_set;
|
||||||
|
|
||||||
|
use crate::boxed_iter::BoxedIter;
|
||||||
|
use crate::combine::Combine;
|
||||||
|
use crate::intern::{intern, Token};
|
||||||
|
use crate::join::try_join_maps;
|
||||||
|
use crate::location::CodeOrigin;
|
||||||
|
use crate::name::{VName, VPath};
|
||||||
|
use crate::proj_error::{ProjectError, ProjectErrorObj};
|
||||||
|
use crate::sequence::Sequence;
|
||||||
|
|
||||||
|
/// An umbrella trait for operations you can carry out on any part of the tree
|
||||||
|
/// structure
|
||||||
|
pub trait TreeTransforms: Sized {
|
||||||
|
/// Data held at the leaves of the tree
|
||||||
|
type Item;
|
||||||
|
/// Data associated with modules
|
||||||
|
type XMod;
|
||||||
|
/// Data associated with entries inside modules
|
||||||
|
type XEnt;
|
||||||
|
/// Recursive type to enable [TreeTransforms::map_data] to transform the whole
|
||||||
|
/// tree
|
||||||
|
type SelfType<T, U, V>: TreeTransforms<Item = T, XMod = U, XEnt = V>;
|
||||||
|
|
||||||
|
/// Implementation for [TreeTransforms::map_data]
|
||||||
|
fn map_data_rec<T, U, V>(
|
||||||
|
self,
|
||||||
|
item: &mut impl FnMut(Substack<Token<String>>, Self::Item) -> T,
|
||||||
|
module: &mut impl FnMut(Substack<Token<String>>, Self::XMod) -> U,
|
||||||
|
entry: &mut impl FnMut(Substack<Token<String>>, Self::XEnt) -> V,
|
||||||
|
path: Substack<Token<String>>,
|
||||||
|
) -> Self::SelfType<T, U, V>;
|
||||||
|
|
||||||
|
/// Transform all the data in the tree without changing its structure
|
||||||
|
fn map_data<T, U, V>(
|
||||||
|
self,
|
||||||
|
mut item: impl FnMut(Substack<Token<String>>, Self::Item) -> T,
|
||||||
|
mut module: impl FnMut(Substack<Token<String>>, Self::XMod) -> U,
|
||||||
|
mut entry: impl FnMut(Substack<Token<String>>, Self::XEnt) -> V,
|
||||||
|
) -> Self::SelfType<T, U, V> {
|
||||||
|
self.map_data_rec(&mut item, &mut module, &mut entry, Substack::Bottom)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visit all elements in the tree. This is like [TreeTransforms::search] but
|
||||||
|
/// without the early exit
|
||||||
|
///
|
||||||
|
/// * init - can be used for reduce, otherwise pass `()`
|
||||||
|
/// * callback - a callback applied on every module.
|
||||||
|
/// * [`Substack<Token<String>>`] - the walked path
|
||||||
|
/// * [Module] - the current module
|
||||||
|
/// * `T` - data for reduce.
|
||||||
|
fn search_all<'a, T>(
|
||||||
|
&'a self,
|
||||||
|
init: T,
|
||||||
|
mut callback: impl FnMut(
|
||||||
|
Substack<Token<String>>,
|
||||||
|
ModMemberRef<'a, Self::Item, Self::XMod, Self::XEnt>,
|
||||||
|
T,
|
||||||
|
) -> T,
|
||||||
|
) -> T {
|
||||||
|
let res =
|
||||||
|
self.search(init, |stack, member, state| Ok::<T, Never>(callback(stack, member, state)));
|
||||||
|
res.unwrap_or_else(|e| match e {})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Visit elements in the tree depth first with the provided function
|
||||||
|
///
|
||||||
|
/// * init - can be used for reduce, otherwise pass `()`
|
||||||
|
/// * callback - a callback applied on every module. Can return [Err] to
|
||||||
|
/// short-circuit the walk
|
||||||
|
/// * [`Substack<Token<String>>`] - the walked path
|
||||||
|
/// * [Module] - the current module
|
||||||
|
/// * `T` - data for reduce.
|
||||||
|
fn search<'a, T, E>(
|
||||||
|
&'a self,
|
||||||
|
init: T,
|
||||||
|
mut callback: impl FnMut(
|
||||||
|
Substack<Token<String>>,
|
||||||
|
ModMemberRef<'a, Self::Item, Self::XMod, Self::XEnt>,
|
||||||
|
T,
|
||||||
|
) -> Result<T, E>,
|
||||||
|
) -> Result<T, E> {
|
||||||
|
self.search_rec(init, Substack::Bottom, &mut callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal version of [TreeTransforms::search_all]
|
||||||
|
fn search_rec<'a, T, E>(
|
||||||
|
&'a self,
|
||||||
|
state: T,
|
||||||
|
stack: Substack<Token<String>>,
|
||||||
|
callback: &mut impl FnMut(
|
||||||
|
Substack<Token<String>>,
|
||||||
|
ModMemberRef<'a, Self::Item, Self::XMod, Self::XEnt>,
|
||||||
|
T,
|
||||||
|
) -> Result<T, E>,
|
||||||
|
) -> Result<T, E>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The member in a [ModEntry] which is associated with a name in a [Module]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ModMember<Item, XMod, XEnt> {
|
||||||
|
/// Arbitrary data
|
||||||
|
Item(Item),
|
||||||
|
/// A child module
|
||||||
|
Sub(Module<Item, XMod, XEnt>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item, XMod, XEnt> TreeTransforms for ModMember<Item, XMod, XEnt> {
|
||||||
|
type Item = Item;
|
||||||
|
type XEnt = XEnt;
|
||||||
|
type XMod = XMod;
|
||||||
|
type SelfType<T, U, V> = ModMember<T, U, V>;
|
||||||
|
|
||||||
|
fn map_data_rec<T, U, V>(
|
||||||
|
self,
|
||||||
|
item: &mut impl FnMut(Substack<Token<String>>, Item) -> T,
|
||||||
|
module: &mut impl FnMut(Substack<Token<String>>, XMod) -> U,
|
||||||
|
entry: &mut impl FnMut(Substack<Token<String>>, XEnt) -> V,
|
||||||
|
path: Substack<Token<String>>,
|
||||||
|
) -> Self::SelfType<T, U, V> {
|
||||||
|
match self {
|
||||||
|
Self::Item(it) => ModMember::Item(item(path, it)),
|
||||||
|
Self::Sub(sub) => ModMember::Sub(sub.map_data_rec(item, module, entry, path)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_rec<'a, T, E>(
|
||||||
|
&'a self,
|
||||||
|
state: T,
|
||||||
|
stack: Substack<Token<String>>,
|
||||||
|
callback: &mut impl FnMut(
|
||||||
|
Substack<Token<String>>,
|
||||||
|
ModMemberRef<'a, Item, XMod, XEnt>,
|
||||||
|
T,
|
||||||
|
) -> Result<T, E>,
|
||||||
|
) -> Result<T, E> {
|
||||||
|
match self {
|
||||||
|
Self::Item(it) => callback(stack, ModMemberRef::Item(it), state),
|
||||||
|
Self::Sub(m) => m.search_rec(state, stack, callback),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reasons why merging trees might fail
|
||||||
|
pub enum ConflictKind<Item: Combine, XMod: Combine, XEnt: Combine> {
|
||||||
|
/// Error during the merging of items
|
||||||
|
Item(Item::Error),
|
||||||
|
/// Error during the merging of module metadata
|
||||||
|
Module(XMod::Error),
|
||||||
|
/// Error during the merging of entry metadata
|
||||||
|
XEnt(XEnt::Error),
|
||||||
|
/// An item appeared in one tree where the other contained a submodule
|
||||||
|
ItemModule,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_for_conflict {
|
||||||
|
($target:ty, ($($deps:tt)*), $for:ty, $body:tt) => {
|
||||||
|
impl<Item: Combine, XMod: Combine, XEnt: Combine> $target
|
||||||
|
for $for
|
||||||
|
where
|
||||||
|
Item::Error: $($deps)*,
|
||||||
|
XMod::Error: $($deps)*,
|
||||||
|
XEnt::Error: $($deps)*,
|
||||||
|
$body
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_for_conflict!(Clone, (Clone), ConflictKind<Item, XMod, XEnt>, {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
ConflictKind::Item(it_e) => ConflictKind::Item(it_e.clone()),
|
||||||
|
ConflictKind::Module(mod_e) => ConflictKind::Module(mod_e.clone()),
|
||||||
|
ConflictKind::XEnt(ent_e) => ConflictKind::XEnt(ent_e.clone()),
|
||||||
|
ConflictKind::ItemModule => ConflictKind::ItemModule,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
impl_for_conflict!(fmt::Debug, (fmt::Debug), ConflictKind<Item, XMod, XEnt>, {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ConflictKind::Item(it_e) =>
|
||||||
|
f.debug_tuple("TreeCombineErr::Item").field(it_e).finish(),
|
||||||
|
ConflictKind::Module(mod_e) =>
|
||||||
|
f.debug_tuple("TreeCombineErr::Module").field(mod_e).finish(),
|
||||||
|
ConflictKind::XEnt(ent_e) =>
|
||||||
|
f.debug_tuple("TreeCombineErr::XEnt").field(ent_e).finish(),
|
||||||
|
ConflictKind::ItemModule => write!(f, "TreeCombineErr::Item2Module"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Error produced when two trees cannot be merged
|
||||||
|
pub struct TreeConflict<Item: Combine, XMod: Combine, XEnt: Combine> {
|
||||||
|
/// Which subtree caused the failure
|
||||||
|
pub path: VPath,
|
||||||
|
/// What type of failure occurred
|
||||||
|
pub kind: ConflictKind<Item, XMod, XEnt>,
|
||||||
|
}
|
||||||
|
impl<Item: Combine, XMod: Combine, XEnt: Combine> TreeConflict<Item, XMod, XEnt> {
|
||||||
|
fn new(kind: ConflictKind<Item, XMod, XEnt>) -> Self { Self { path: VPath::new([]), kind } }
|
||||||
|
|
||||||
|
fn push(self, seg: Token<String>) -> Self {
|
||||||
|
Self { path: self.path.prefix([seg]), kind: self.kind }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_for_conflict!(Clone, (Clone), TreeConflict<Item, XMod, XEnt>, {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self { path: self.path.clone(), kind: self.kind.clone() }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
impl_for_conflict!(fmt::Debug, (fmt::Debug), TreeConflict<Item, XMod, XEnt>, {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("TreeConflict")
|
||||||
|
.field("path", &self.path)
|
||||||
|
.field("kind", &self.kind)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
impl<Item: Combine, XMod: Combine, XEnt: Combine> Combine for ModMember<Item, XMod, XEnt> {
|
||||||
|
type Error = TreeConflict<Item, XMod, XEnt>;
|
||||||
|
|
||||||
|
fn combine(self, other: Self) -> Result<Self, Self::Error> {
|
||||||
|
match (self, other) {
|
||||||
|
(Self::Item(i1), Self::Item(i2)) => match i1.combine(i2) {
|
||||||
|
Ok(i) => Ok(Self::Item(i)),
|
||||||
|
Err(e) => Err(TreeConflict::new(ConflictKind::Item(e))),
|
||||||
|
},
|
||||||
|
(Self::Sub(m1), Self::Sub(m2)) => m1.combine(m2).map(Self::Sub),
|
||||||
|
(..) => Err(TreeConflict::new(ConflictKind::ItemModule)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data about a name in a [Module]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ModEntry<Item, XMod, XEnt> {
|
||||||
|
/// The submodule or item
|
||||||
|
pub member: ModMember<Item, XMod, XEnt>,
|
||||||
|
/// Additional fields
|
||||||
|
pub x: XEnt,
|
||||||
|
}
|
||||||
|
impl<Item: Combine, XMod: Combine, XEnt: Combine> Combine for ModEntry<Item, XMod, XEnt> {
|
||||||
|
type Error = TreeConflict<Item, XMod, XEnt>;
|
||||||
|
fn combine(self, other: Self) -> Result<Self, Self::Error> {
|
||||||
|
match self.x.combine(other.x) {
|
||||||
|
Err(e) => Err(TreeConflict::new(ConflictKind::XEnt(e))),
|
||||||
|
Ok(x) => Ok(Self { x, member: self.member.combine(other.member)? }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<Item, XMod, XEnt> ModEntry<Item, XMod, XEnt> {
|
||||||
|
/// Returns the item in this entry if it contains one.
|
||||||
|
#[must_use]
|
||||||
|
pub fn item(&self) -> Option<&Item> {
|
||||||
|
match &self.member {
|
||||||
|
ModMember::Item(it) => Some(it),
|
||||||
|
ModMember::Sub(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item, XMod, XEnt> TreeTransforms for ModEntry<Item, XMod, XEnt> {
|
||||||
|
type Item = Item;
|
||||||
|
type XEnt = XEnt;
|
||||||
|
type XMod = XMod;
|
||||||
|
type SelfType<T, U, V> = ModEntry<T, U, V>;
|
||||||
|
|
||||||
|
fn map_data_rec<T, U, V>(
|
||||||
|
self,
|
||||||
|
item: &mut impl FnMut(Substack<Token<String>>, Item) -> T,
|
||||||
|
module: &mut impl FnMut(Substack<Token<String>>, XMod) -> U,
|
||||||
|
entry: &mut impl FnMut(Substack<Token<String>>, XEnt) -> V,
|
||||||
|
path: Substack<Token<String>>,
|
||||||
|
) -> Self::SelfType<T, U, V> {
|
||||||
|
ModEntry {
|
||||||
|
member: self.member.map_data_rec(item, module, entry, path.clone()),
|
||||||
|
x: entry(path, self.x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_rec<'a, T, E>(
|
||||||
|
&'a self,
|
||||||
|
state: T,
|
||||||
|
stack: Substack<Token<String>>,
|
||||||
|
callback: &mut impl FnMut(
|
||||||
|
Substack<Token<String>>,
|
||||||
|
ModMemberRef<'a, Item, XMod, XEnt>,
|
||||||
|
T,
|
||||||
|
) -> Result<T, E>,
|
||||||
|
) -> Result<T, E> {
|
||||||
|
self.member.search_rec(state, stack, callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<Item, XMod, XEnt: Default> ModEntry<Item, XMod, XEnt> {
|
||||||
|
/// Wrap a member directly with trivial metadata
|
||||||
|
pub fn wrap(member: ModMember<Item, XMod, XEnt>) -> Self { Self { member, x: XEnt::default() } }
|
||||||
|
/// Wrap an item directly with trivial metadata
|
||||||
|
pub fn leaf(item: Item) -> Self { Self::wrap(ModMember::Item(item)) }
|
||||||
|
}
|
||||||
|
impl<Item, XMod: Default, XEnt: Default> ModEntry<Item, XMod, XEnt> {
|
||||||
|
/// Create an empty submodule
|
||||||
|
pub fn empty() -> Self { Self::wrap(ModMember::Sub(Module::wrap([]))) }
|
||||||
|
|
||||||
|
/// Create a module
|
||||||
|
#[must_use]
|
||||||
|
pub fn tree<K: AsRef<str>>(arr: impl IntoIterator<Item = (K, Self)>) -> Self {
|
||||||
|
Self::wrap(ModMember::Sub(Module::wrap(arr.into_iter().map(|(k, v)| (intern(k.as_ref()), v)))))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a record in the list passed to [ModEntry#tree] which describes a
|
||||||
|
/// submodule. This mostly exists to deal with strange rustfmt block
|
||||||
|
/// breaking behaviour
|
||||||
|
pub fn tree_ent<K: AsRef<str>>(key: K, arr: impl IntoIterator<Item = (K, Self)>) -> (K, Self) {
|
||||||
|
(key, Self::tree(arr))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Namespace the tree with the list of names
|
||||||
|
///
|
||||||
|
/// The unarray is used to trick rustfmt into breaking the sub-item
|
||||||
|
/// into a block without breaking anything else.
|
||||||
|
#[must_use]
|
||||||
|
pub fn ns(name: impl AsRef<str>, [mut end]: [Self; 1]) -> Self {
|
||||||
|
let elements = name.as_ref().split("::").collect::<Vec<_>>();
|
||||||
|
for name in elements.into_iter().rev() {
|
||||||
|
end = Self::tree([(name, end)]);
|
||||||
|
}
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not_mod_panic<T>() -> T { panic!("Expected module but found leaf") }
|
||||||
|
|
||||||
|
/// Return the wrapped module. Panic if the entry wraps an item
|
||||||
|
pub fn unwrap_mod(self) -> Module<Item, XMod, XEnt> {
|
||||||
|
if let ModMember::Sub(m) = self.member { m } else { Self::not_mod_panic() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the wrapped module. Panic if the entry wraps an item
|
||||||
|
pub fn unwrap_mod_ref(&self) -> &Module<Item, XMod, XEnt> {
|
||||||
|
if let ModMember::Sub(m) = &self.member { m } else { Self::not_mod_panic() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A module, containing imports,
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Module<Item, XMod, XEnt> {
|
||||||
|
/// Submodules and items by name
|
||||||
|
pub entries: HashMap<Token<String>, ModEntry<Item, XMod, XEnt>>,
|
||||||
|
/// Additional fields
|
||||||
|
pub x: XMod,
|
||||||
|
}
|
||||||
|
|
||||||
|
trait_set! {
|
||||||
|
/// A filter applied to a module tree
|
||||||
|
pub trait Filter<'a, Item, XMod, XEnt> =
|
||||||
|
for<'b> Fn(&'b ModEntry<Item, XMod, XEnt>) -> bool + Clone + 'a
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A line in a [Module]
|
||||||
|
pub type Record<Item, XMod, XEnt> = (Token<String>, ModEntry<Item, XMod, XEnt>);
|
||||||
|
|
||||||
|
impl<Item, XMod, XEnt> Module<Item, XMod, XEnt> {
|
||||||
|
/// Returns child names for which the value matches a filter
|
||||||
|
#[must_use]
|
||||||
|
pub fn keys<'a>(
|
||||||
|
&'a self,
|
||||||
|
filter: impl for<'b> Fn(&'b ModEntry<Item, XMod, XEnt>) -> bool + 'a,
|
||||||
|
) -> BoxedIter<Token<String>> {
|
||||||
|
Box::new((self.entries.iter()).filter(move |(_, v)| filter(v)).map(|(k, _)| k.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the module at the end of the given path
|
||||||
|
pub fn walk_ref<'a: 'b, 'b>(
|
||||||
|
&'a self,
|
||||||
|
prefix: &'b [Token<String>],
|
||||||
|
path: &'b [Token<String>],
|
||||||
|
filter: impl Filter<'b, Item, XMod, XEnt>,
|
||||||
|
) -> Result<&'a Self, WalkError<'b>> {
|
||||||
|
let mut module = self;
|
||||||
|
for (pos, step) in path.iter().enumerate() {
|
||||||
|
let kind = match module.entries.get(step) {
|
||||||
|
None => ErrKind::Missing,
|
||||||
|
Some(ent) if !filter(ent) => ErrKind::Filtered,
|
||||||
|
Some(ModEntry { member: ModMember::Item(_), .. }) => ErrKind::NotModule,
|
||||||
|
Some(ModEntry { member: ModMember::Sub(next), .. }) => {
|
||||||
|
module = next;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let options = Sequence::new(move || module.keys(filter.clone()));
|
||||||
|
return Err(WalkError { kind, prefix, path, pos, options });
|
||||||
|
}
|
||||||
|
Ok(module)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the member at the end of the given path
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// if path is empty, since the reference cannot be forwarded that way
|
||||||
|
pub fn walk1_ref<'a: 'b, 'b>(
|
||||||
|
&'a self,
|
||||||
|
prefix: &'b [Token<String>],
|
||||||
|
path: &'b [Token<String>],
|
||||||
|
filter: impl Filter<'b, Item, XMod, XEnt>,
|
||||||
|
) -> Result<(&'a ModEntry<Item, XMod, XEnt>, &'a Self), WalkError<'b>> {
|
||||||
|
let (last, parent) = path.split_last().expect("Path cannot be empty");
|
||||||
|
let pos = path.len() - 1;
|
||||||
|
let module = self.walk_ref(prefix, parent, filter.clone())?;
|
||||||
|
let err_kind = match &module.entries.get(last) {
|
||||||
|
Some(entry) if filter(entry) => return Ok((entry, module)),
|
||||||
|
Some(_) => ErrKind::Filtered,
|
||||||
|
None => ErrKind::Missing,
|
||||||
|
};
|
||||||
|
let options = Sequence::new(move || module.keys(filter.clone()));
|
||||||
|
Err(WalkError { kind: err_kind, options, prefix, path, pos })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Walk from one node to another in a tree, asserting that the origin can see
|
||||||
|
/// the target.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If the target is the root node
|
||||||
|
pub fn inner_walk<'a: 'b, 'b>(
|
||||||
|
&'a self,
|
||||||
|
origin: &[Token<String>],
|
||||||
|
target: &'b [Token<String>],
|
||||||
|
is_exported: impl for<'c> Fn(&'c ModEntry<Item, XMod, XEnt>) -> bool + Clone + 'b,
|
||||||
|
) -> Result<(&'a ModEntry<Item, XMod, XEnt>, &'a Self), WalkError<'b>> {
|
||||||
|
let ignore_vis_len = 1 + origin.iter().zip(target).take_while(|(a, b)| a == b).count();
|
||||||
|
if target.len() <= ignore_vis_len {
|
||||||
|
return self.walk1_ref(&[], target, |_| true);
|
||||||
|
}
|
||||||
|
let (ignore_vis_path, hidden_path) = target.split_at(ignore_vis_len);
|
||||||
|
let first_divergence = self.walk_ref(&[], ignore_vis_path, |_| true)?;
|
||||||
|
first_divergence.walk1_ref(ignore_vis_path, hidden_path, is_exported)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrap entry table in a module with trivial metadata
|
||||||
|
pub fn wrap(entries: impl IntoIterator<Item = Record<Item, XMod, XEnt>>) -> Self
|
||||||
|
where XMod: Default {
|
||||||
|
Self { entries: entries.into_iter().collect(), x: XMod::default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item, XMod, XEnt> TreeTransforms for Module<Item, XMod, XEnt> {
|
||||||
|
type Item = Item;
|
||||||
|
type XEnt = XEnt;
|
||||||
|
type XMod = XMod;
|
||||||
|
type SelfType<T, U, V> = Module<T, U, V>;
|
||||||
|
|
||||||
|
fn map_data_rec<T, U, V>(
|
||||||
|
self,
|
||||||
|
item: &mut impl FnMut(Substack<Token<String>>, Item) -> T,
|
||||||
|
module: &mut impl FnMut(Substack<Token<String>>, XMod) -> U,
|
||||||
|
entry: &mut impl FnMut(Substack<Token<String>>, XEnt) -> V,
|
||||||
|
path: Substack<Token<String>>,
|
||||||
|
) -> Self::SelfType<T, U, V> {
|
||||||
|
Module {
|
||||||
|
x: module(path.clone(), self.x),
|
||||||
|
entries: (self.entries.into_iter())
|
||||||
|
.map(|(k, e)| (k.clone(), e.map_data_rec(item, module, entry, path.push(k))))
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_rec<'a, T, E>(
|
||||||
|
&'a self,
|
||||||
|
mut state: T,
|
||||||
|
stack: Substack<Token<String>>,
|
||||||
|
callback: &mut impl FnMut(
|
||||||
|
Substack<Token<String>>,
|
||||||
|
ModMemberRef<'a, Item, XMod, XEnt>,
|
||||||
|
T,
|
||||||
|
) -> Result<T, E>,
|
||||||
|
) -> Result<T, E> {
|
||||||
|
state = callback(stack.clone(), ModMemberRef::Mod(self), state)?;
|
||||||
|
for (key, value) in &self.entries {
|
||||||
|
state = value.search_rec(state, stack.push(key.clone()), callback)?;
|
||||||
|
}
|
||||||
|
Ok(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item: Combine, XMod: Combine, XEnt: Combine> Combine for Module<Item, XMod, XEnt> {
|
||||||
|
type Error = TreeConflict<Item, XMod, XEnt>;
|
||||||
|
fn combine(self, Self { entries, x }: Self) -> Result<Self, Self::Error> {
|
||||||
|
let entries =
|
||||||
|
try_join_maps(self.entries, entries, |k, l, r| l.combine(r).map_err(|e| e.push(k.clone())))?;
|
||||||
|
let x = (self.x.combine(x)).map_err(|e| TreeConflict::new(ConflictKind::Module(e)))?;
|
||||||
|
Ok(Self { x, entries })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item: fmt::Display, TExt: fmt::Display, XEnt: fmt::Display> fmt::Display
|
||||||
|
for Module<Item, TExt, XEnt>
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "module {{")?;
|
||||||
|
for (name, ModEntry { member, x: extra }) in &self.entries {
|
||||||
|
match member {
|
||||||
|
ModMember::Sub(module) => write!(f, "\n{name} {extra} = {module}"),
|
||||||
|
ModMember::Item(item) => write!(f, "\n{name} {extra} = {item}"),
|
||||||
|
}?;
|
||||||
|
}
|
||||||
|
write!(f, "\n\n{}\n}}", &self.x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A non-owning version of [ModMember]. Either an item-ref or a module-ref.
|
||||||
|
pub enum ModMemberRef<'a, Item, XMod, XEnt> {
|
||||||
|
/// Leaf
|
||||||
|
Item(&'a Item),
|
||||||
|
/// Node
|
||||||
|
Mod(&'a Module<Item, XMod, XEnt>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Possible causes why the path could not be walked
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum ErrKind {
|
||||||
|
/// `require_exported` was set to `true` and a module wasn't exported
|
||||||
|
Filtered,
|
||||||
|
/// A module was not found
|
||||||
|
Missing,
|
||||||
|
/// The path leads into a leaf node
|
||||||
|
NotModule,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
/// All details about a failed tree-walk
|
||||||
|
pub struct WalkError<'a> {
|
||||||
|
/// Failure mode
|
||||||
|
kind: ErrKind,
|
||||||
|
/// Path to the module where the walk started
|
||||||
|
prefix: &'a [Token<String>],
|
||||||
|
/// Planned walk path
|
||||||
|
path: &'a [Token<String>],
|
||||||
|
/// Index into walked path where the error occurred
|
||||||
|
pos: usize,
|
||||||
|
/// Alternatives to the failed steps
|
||||||
|
options: Sequence<'a, Token<String>>,
|
||||||
|
}
|
||||||
|
impl<'a> WalkError<'a> {
|
||||||
|
/// Total length of the path represented by this error
|
||||||
|
#[must_use]
|
||||||
|
pub fn depth(&self) -> usize { self.prefix.len() + self.pos + 1 }
|
||||||
|
|
||||||
|
/// Attach a location to the error and convert into trait object for reporting
|
||||||
|
#[must_use]
|
||||||
|
pub fn at(self, origin: &CodeOrigin) -> ProjectErrorObj {
|
||||||
|
let details = WalkErrorDetails {
|
||||||
|
origin: origin.clone(),
|
||||||
|
path: VName::new((self.prefix.iter()).chain(self.path.iter().take(self.pos + 1)).cloned())
|
||||||
|
.expect("empty paths don't cause an error"),
|
||||||
|
options: self.options.iter().collect(),
|
||||||
|
};
|
||||||
|
match self.kind {
|
||||||
|
ErrKind::Filtered => FilteredError(details).pack(),
|
||||||
|
ErrKind::Missing => MissingError(details).pack(),
|
||||||
|
ErrKind::NotModule => NotModuleError(details).pack(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Construct an error for the very last item in a slice. This is often done
|
||||||
|
/// outside [super::tree] so it gets a function rather than exposing the
|
||||||
|
/// fields of [WalkError]
|
||||||
|
pub fn last(
|
||||||
|
path: &'a [Token<String>],
|
||||||
|
kind: ErrKind,
|
||||||
|
options: Sequence<'a, Token<String>>,
|
||||||
|
) -> Self {
|
||||||
|
WalkError { kind, path, options, pos: path.len() - 1, prefix: &[] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> fmt::Debug for WalkError<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("WalkError")
|
||||||
|
.field("kind", &self.kind)
|
||||||
|
.field("prefix", &self.prefix)
|
||||||
|
.field("path", &self.path)
|
||||||
|
.field("pos", &self.pos)
|
||||||
|
.finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WalkErrorDetails {
|
||||||
|
path: VName,
|
||||||
|
options: Vec<Token<String>>,
|
||||||
|
origin: CodeOrigin,
|
||||||
|
}
|
||||||
|
impl WalkErrorDetails {
|
||||||
|
fn print_options(&self) -> String { format!("options are {}", self.options.iter().join(", ")) }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FilteredError(WalkErrorDetails);
|
||||||
|
impl ProjectError for FilteredError {
|
||||||
|
const DESCRIPTION: &'static str = "The path leads into a private module";
|
||||||
|
fn one_position(&self) -> CodeOrigin { self.0.origin.clone() }
|
||||||
|
fn message(&self) -> String { format!("{} is private, {}", self.0.path, self.0.print_options()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MissingError(WalkErrorDetails);
|
||||||
|
impl ProjectError for MissingError {
|
||||||
|
const DESCRIPTION: &'static str = "Nonexistent path";
|
||||||
|
fn one_position(&self) -> CodeOrigin { self.0.origin.clone() }
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("{} does not exist, {}", self.0.path, self.0.print_options())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NotModuleError(WalkErrorDetails);
|
||||||
|
impl ProjectError for NotModuleError {
|
||||||
|
const DESCRIPTION: &'static str = "The path leads into a leaf";
|
||||||
|
fn one_position(&self) -> CodeOrigin { self.0.origin.clone() }
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("{} is not a module, {}", self.0.path, self.0.print_options())
|
||||||
|
}
|
||||||
|
}
|
||||||
95
orchid-base/src/virt_fs/common.rs
Normal file
95
orchid-base/src/virt_fs/common.rs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::proj_error::{ErrorSansOrigin, ErrorSansOriginObj};
|
||||||
|
use crate::intern::Token;
|
||||||
|
use crate::name::{PathSlice, VPath};
|
||||||
|
|
||||||
|
/// Represents the result of loading code from a string-tree form such
|
||||||
|
/// as the file system. Cheap to clone.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Loaded {
|
||||||
|
/// Conceptually equivalent to a sourcefile
|
||||||
|
Code(Arc<String>),
|
||||||
|
/// Conceptually equivalent to the list of *.orc files in a folder, without
|
||||||
|
/// the extension
|
||||||
|
Collection(Arc<Vec<Token<String>>>),
|
||||||
|
}
|
||||||
|
impl Loaded {
|
||||||
|
/// Is the loaded item source code (not a collection)?
|
||||||
|
pub fn is_code(&self) -> bool { matches!(self, Loaded::Code(_)) }
|
||||||
|
/// Collect the elements in a collection rreport
|
||||||
|
pub fn collection(items: impl IntoIterator<Item = Token<String>>) -> Self {
|
||||||
|
Self::Collection(Arc::new(items.into_iter().collect()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returned by any source loading callback
|
||||||
|
pub type FSResult = Result<Loaded, ErrorSansOriginObj>;
|
||||||
|
|
||||||
|
/// Type that indicates the type of an entry without reading the contents
|
||||||
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||||
|
pub enum FSKind {
|
||||||
|
/// Invalid path or read error
|
||||||
|
None,
|
||||||
|
/// Source code
|
||||||
|
Code,
|
||||||
|
/// Internal tree node
|
||||||
|
Collection,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Distinguished error for missing code
|
||||||
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
|
pub struct CodeNotFound(pub VPath);
|
||||||
|
impl CodeNotFound {
|
||||||
|
/// Instantiate error
|
||||||
|
pub fn new(path: VPath) -> Self { Self(path) }
|
||||||
|
}
|
||||||
|
impl ErrorSansOrigin for CodeNotFound {
|
||||||
|
const DESCRIPTION: &'static str = "No source code for path";
|
||||||
|
fn message(&self) -> String { format!("{} not found", self.0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A simplified view of a file system for the purposes of source code loading.
|
||||||
|
/// This includes the real FS and source code, but also various in-memory
|
||||||
|
/// formats and other sources for libraries and dependencies.
|
||||||
|
pub trait VirtFS {
|
||||||
|
/// Implementation of [VirtFS::read]
|
||||||
|
fn get(&self, path: &[Token<String>], full_path: &PathSlice) -> FSResult;
|
||||||
|
/// Discover information about a path without reading it.
|
||||||
|
///
|
||||||
|
/// Implement this if your vfs backend can do expensive operations
|
||||||
|
fn kind(&self, path: &PathSlice) -> FSKind {
|
||||||
|
match self.read(path) {
|
||||||
|
Err(_) => FSKind::None,
|
||||||
|
Ok(Loaded::Code(_)) => FSKind::Code,
|
||||||
|
Ok(Loaded::Collection(_)) => FSKind::Collection,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Convert a path into a human-readable string that is meaningful in the
|
||||||
|
/// target context.
|
||||||
|
fn display(&self, path: &[Token<String>]) -> Option<String>;
|
||||||
|
/// Convert the FS handler into a type-erased version of itself for packing in
|
||||||
|
/// a tree.
|
||||||
|
fn rc(self) -> Rc<dyn VirtFS>
|
||||||
|
where Self: Sized + 'static {
|
||||||
|
Rc::new(self)
|
||||||
|
}
|
||||||
|
/// Read a path, returning either a text file, a directory listing or an
|
||||||
|
/// error. Wrapper for [VirtFS::get]
|
||||||
|
fn read(&self, path: &PathSlice) -> FSResult { self.get(path, path) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtFS for &dyn VirtFS {
|
||||||
|
fn get(&self, path: &[Token<String>], full_path: &PathSlice) -> FSResult {
|
||||||
|
(*self).get(path, full_path)
|
||||||
|
}
|
||||||
|
fn display(&self, path: &[Token<String>]) -> Option<String> { (*self).display(path) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: VirtFS + ?Sized> VirtFS for Rc<T> {
|
||||||
|
fn get(&self, path: &[Token<String>], full_path: &PathSlice) -> FSResult {
|
||||||
|
(**self).get(path, full_path)
|
||||||
|
}
|
||||||
|
fn display(&self, path: &[Token<String>]) -> Option<String> { (**self).display(path) }
|
||||||
|
}
|
||||||
73
orchid-base/src/virt_fs/decl.rs
Normal file
73
orchid-base/src/virt_fs/decl.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use super::common::CodeNotFound;
|
||||||
|
use super::{FSResult, Loaded, VirtFS};
|
||||||
|
use crate::intern::Token;
|
||||||
|
use crate::proj_error::ErrorSansOrigin;
|
||||||
|
use crate::name::PathSlice;
|
||||||
|
use crate::tree::{ModEntry, ModMember};
|
||||||
|
use crate::combine::Combine;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ConflictingTrees;
|
||||||
|
|
||||||
|
impl Combine for Rc<dyn VirtFS> {
|
||||||
|
type Error = ConflictingTrees;
|
||||||
|
fn combine(self, _: Self) -> Result<Self, Self::Error> { Err(ConflictingTrees) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Combine for Arc<dyn VirtFS> {
|
||||||
|
type Error = ConflictingTrees;
|
||||||
|
fn combine(self, _: Self) -> Result<Self, Self::Error> { Err(ConflictingTrees) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Combine for &'a dyn VirtFS {
|
||||||
|
type Error = ConflictingTrees;
|
||||||
|
fn combine(self, _: Self) -> Result<Self, Self::Error> { Err(ConflictingTrees) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A declarative in-memory tree with [VirtFS] objects for leaves. Paths are
|
||||||
|
/// followed to a leaf and the leftover handled by it.
|
||||||
|
pub type DeclTree = ModEntry<Rc<dyn VirtFS>, (), ()>;
|
||||||
|
|
||||||
|
impl VirtFS for DeclTree {
|
||||||
|
fn get(&self, path: &[Token<String>], full_path: &PathSlice) -> FSResult {
|
||||||
|
match &self.member {
|
||||||
|
ModMember::Item(it) => it.get(path, full_path),
|
||||||
|
ModMember::Sub(module) => match path.split_first() {
|
||||||
|
None => Ok(Loaded::collection(module.keys(|_| true))),
|
||||||
|
Some((head, tail)) => (module.entries.get(head))
|
||||||
|
.ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack())
|
||||||
|
.and_then(|ent| ent.get(tail, full_path)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display(&self, path: &[Token<String>]) -> Option<String> {
|
||||||
|
let (head, tail) = path.split_first()?;
|
||||||
|
match &self.member {
|
||||||
|
ModMember::Item(it) => it.display(path),
|
||||||
|
ModMember::Sub(module) => module.entries.get(head)?.display(tail),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtFS for String {
|
||||||
|
fn display(&self, _: &[Token<String>]) -> Option<String> { None }
|
||||||
|
fn get(&self, path: &[Token<String>], full_path: &PathSlice) -> FSResult {
|
||||||
|
(path.is_empty().then(|| Loaded::Code(Arc::new(self.as_str().to_string()))))
|
||||||
|
.ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> VirtFS for &'a str {
|
||||||
|
fn display(&self, _: &[Token<String>]) -> Option<String> { None }
|
||||||
|
fn get(&self, path: &[Token<String>], full_path: &PathSlice) -> FSResult {
|
||||||
|
(path.is_empty().then(|| Loaded::Code(Arc::new(self.to_string()))))
|
||||||
|
.ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert a file by cleartext contents in the [DeclTree].
|
||||||
|
pub fn decl_file(s: &str) -> DeclTree { DeclTree::leaf(Rc::new(s.to_string())) }
|
||||||
121
orchid-base/src/virt_fs/dir.rs
Normal file
121
orchid-base/src/virt_fs/dir.rs
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io;
|
||||||
|
use std::io::{ErrorKind, Read};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
|
use super::common::CodeNotFound;
|
||||||
|
use super::{FSResult, Loaded, VirtFS};
|
||||||
|
use crate::intern::{intern, Token};
|
||||||
|
use crate::proj_error::{ErrorSansOrigin, ErrorSansOriginObj};
|
||||||
|
use crate::name::PathSlice;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct OpenError {
|
||||||
|
file: Arc<Mutex<io::Error>>,
|
||||||
|
dir: Arc<Mutex<io::Error>>,
|
||||||
|
}
|
||||||
|
impl OpenError {
|
||||||
|
pub fn wrap(file: io::Error, dir: io::Error) -> ErrorSansOriginObj {
|
||||||
|
Self { dir: Arc::new(Mutex::new(dir)), file: Arc::new(Mutex::new(file)) }.pack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ErrorSansOrigin for OpenError {
|
||||||
|
const DESCRIPTION: &'static str = "A file system error occurred";
|
||||||
|
fn message(&self) -> String {
|
||||||
|
let Self { dir, file } = self;
|
||||||
|
format!(
|
||||||
|
"File system errors other than not found occurred\n\
|
||||||
|
as a file: {}\nas a directory: {}",
|
||||||
|
file.lock().unwrap(),
|
||||||
|
dir.lock().unwrap()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct IOError(Arc<Mutex<io::Error>>);
|
||||||
|
impl IOError {
|
||||||
|
pub fn wrap(inner: io::Error) -> ErrorSansOriginObj { Self(Arc::new(Mutex::new(inner))).pack() }
|
||||||
|
}
|
||||||
|
impl ErrorSansOrigin for IOError {
|
||||||
|
const DESCRIPTION: &'static str = "an I/O error occured";
|
||||||
|
fn message(&self) -> String { format!("File read error: {}", self.0.lock().unwrap()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct NotUtf8(PathBuf);
|
||||||
|
impl NotUtf8 {
|
||||||
|
pub fn wrap(path: &Path) -> ErrorSansOriginObj { Self(path.to_owned()).pack() }
|
||||||
|
}
|
||||||
|
impl ErrorSansOrigin for NotUtf8 {
|
||||||
|
const DESCRIPTION: &'static str = "Source files must be UTF-8";
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("{} is a source file but contains invalid UTF-8", self.0.display())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A real file system directory linked into the virtual FS
|
||||||
|
pub struct DirNode {
|
||||||
|
cached: RefCell<HashMap<PathBuf, FSResult>>,
|
||||||
|
root: PathBuf,
|
||||||
|
suffix: &'static str,
|
||||||
|
}
|
||||||
|
impl DirNode {
|
||||||
|
/// Reference a real file system directory in the virtual FS
|
||||||
|
pub fn new(root: PathBuf, suffix: &'static str) -> Self {
|
||||||
|
assert!(suffix.starts_with('.'), "Extension must begin with .");
|
||||||
|
Self { cached: RefCell::default(), root, suffix }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ext(&self) -> &str { self.suffix.strip_prefix('.').expect("Checked in constructor") }
|
||||||
|
|
||||||
|
fn load_file(&self, fpath: &Path, orig_path: &PathSlice) -> FSResult {
|
||||||
|
match fpath.read_dir() {
|
||||||
|
Err(dir_e) => {
|
||||||
|
let fpath = fpath.with_extension(self.ext());
|
||||||
|
let mut file =
|
||||||
|
File::open(&fpath).map_err(|file_e| match (dir_e.kind(), file_e.kind()) {
|
||||||
|
(ErrorKind::NotFound, ErrorKind::NotFound) =>
|
||||||
|
CodeNotFound::new(orig_path.to_vpath()).pack(),
|
||||||
|
_ => OpenError::wrap(file_e, dir_e),
|
||||||
|
})?;
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
file.read_to_end(&mut buf).map_err(IOError::wrap)?;
|
||||||
|
let text = String::from_utf8(buf).map_err(|_| NotUtf8::wrap(&fpath))?;
|
||||||
|
Ok(Loaded::Code(Arc::new(text)))
|
||||||
|
},
|
||||||
|
Ok(dir) => Ok(Loaded::collection(dir.filter_map(|ent_r| {
|
||||||
|
let ent = ent_r.ok()?;
|
||||||
|
let name = ent.file_name().into_string().ok()?;
|
||||||
|
match ent.metadata().ok()?.is_dir() {
|
||||||
|
false => Some(intern(name.strip_suffix(self.suffix)?)),
|
||||||
|
true => Some(intern(&name)),
|
||||||
|
}
|
||||||
|
}))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mk_pathbuf(&self, path: &[Token<String>]) -> PathBuf {
|
||||||
|
let mut fpath = self.root.clone();
|
||||||
|
path.iter().for_each(|seg| fpath.push(seg.as_str()));
|
||||||
|
fpath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl VirtFS for DirNode {
|
||||||
|
fn get(&self, path: &[Token<String>], full_path: &PathSlice) -> FSResult {
|
||||||
|
let fpath = self.mk_pathbuf(path);
|
||||||
|
let mut binding = self.cached.borrow_mut();
|
||||||
|
let (_, res) = (binding.raw_entry_mut().from_key(&fpath))
|
||||||
|
.or_insert_with(|| (fpath.clone(), self.load_file(&fpath, full_path)));
|
||||||
|
res.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn display(&self, path: &[Token<String>]) -> Option<String> {
|
||||||
|
let pathbuf = self.mk_pathbuf(path).with_extension(self.ext());
|
||||||
|
Some(pathbuf.to_string_lossy().to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
74
orchid-base/src/virt_fs/embed.rs
Normal file
74
orchid-base/src/virt_fs/embed.rs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use itertools::Itertools as _;
|
||||||
|
use rust_embed::RustEmbed;
|
||||||
|
|
||||||
|
use super::common::CodeNotFound;
|
||||||
|
use super::{FSResult, Loaded, VirtFS};
|
||||||
|
use crate::intern::{intern, Token};
|
||||||
|
use crate::proj_error::ErrorSansOrigin;
|
||||||
|
use crate::location::CodeGenInfo;
|
||||||
|
use crate::name::PathSlice;
|
||||||
|
use crate::tree::{ModEntry, ModMember, Module};
|
||||||
|
|
||||||
|
/// An in-memory FS tree for libraries managed internally by the interpreter
|
||||||
|
pub struct EmbeddedFS {
|
||||||
|
tree: Module<Arc<String>, (), ()>,
|
||||||
|
suffix: &'static str,
|
||||||
|
gen: CodeGenInfo,
|
||||||
|
}
|
||||||
|
impl EmbeddedFS {
|
||||||
|
/// Expose a directory embedded in a binary wiht [RustEmbed] to the
|
||||||
|
/// interpreter
|
||||||
|
pub fn new<T: RustEmbed>(suffix: &'static str, gen: CodeGenInfo) -> Self {
|
||||||
|
let mut tree = Module::wrap([]);
|
||||||
|
for path in T::iter() {
|
||||||
|
let data_buf = T::get(&path).expect("path from iterator").data.to_vec();
|
||||||
|
let data = String::from_utf8(data_buf).expect("embed must be utf8");
|
||||||
|
let mut cur_node = &mut tree;
|
||||||
|
let path_no_suffix = path.strip_suffix(suffix).expect("embed filtered for suffix");
|
||||||
|
let mut segments = path_no_suffix.split('/').map(intern);
|
||||||
|
let mut cur_seg = segments.next().expect("Embed is a directory");
|
||||||
|
for next_seg in segments {
|
||||||
|
if !cur_node.entries.contains_key(&cur_seg) {
|
||||||
|
let ent = ModEntry::wrap(ModMember::Sub(Module::wrap([])));
|
||||||
|
cur_node.entries.insert(cur_seg.clone(), ent);
|
||||||
|
}
|
||||||
|
let ent = cur_node.entries.get_mut(&cur_seg).expect("just constructed");
|
||||||
|
match &mut ent.member {
|
||||||
|
ModMember::Sub(submod) => cur_node = submod,
|
||||||
|
_ => panic!("Aliased file and folder"),
|
||||||
|
};
|
||||||
|
cur_seg = next_seg;
|
||||||
|
}
|
||||||
|
let data_ent = ModEntry::wrap(ModMember::Item(Arc::new(data)));
|
||||||
|
let prev = cur_node.entries.insert(cur_seg, data_ent);
|
||||||
|
debug_assert!(prev.is_none(), "file name unique");
|
||||||
|
}
|
||||||
|
// if gen.generator == "std" {
|
||||||
|
// panic!(
|
||||||
|
// "{:?}",
|
||||||
|
// tree.map_data(&|_, s| (), &|_, x| x, &|_, x| x, Substack::Bottom)
|
||||||
|
// );
|
||||||
|
// };
|
||||||
|
Self { gen, suffix, tree }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtFS for EmbeddedFS {
|
||||||
|
fn get(&self, path: &[Token<String>], full_path: &PathSlice) -> FSResult {
|
||||||
|
if path.is_empty() {
|
||||||
|
return Ok(Loaded::collection(self.tree.keys(|_| true)));
|
||||||
|
}
|
||||||
|
let entry = (self.tree.walk1_ref(&[], path, |_| true))
|
||||||
|
.map_err(|_| CodeNotFound::new(full_path.to_vpath()).pack())?;
|
||||||
|
Ok(match &entry.0.member {
|
||||||
|
ModMember::Item(text) => Loaded::Code(text.clone()),
|
||||||
|
ModMember::Sub(sub) => Loaded::collection(sub.keys(|_| true)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn display(&self, path: &[Token<String>]) -> Option<String> {
|
||||||
|
let Self { gen, suffix, .. } = self;
|
||||||
|
Some(format!("{}{suffix} in {gen}", path.iter().join("/")))
|
||||||
|
}
|
||||||
|
}
|
||||||
38
orchid-base/src/virt_fs/prefix.rs
Normal file
38
orchid-base/src/virt_fs/prefix.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use super::common::CodeNotFound;
|
||||||
|
use super::VirtFS;
|
||||||
|
use crate::intern::Token;
|
||||||
|
use crate::name::{PathSlice, VPath};
|
||||||
|
use crate::proj_error::ErrorSansOrigin;
|
||||||
|
|
||||||
|
/// Modify the prefix of a nested file tree
|
||||||
|
pub struct PrefixFS<'a> {
|
||||||
|
remove: VPath,
|
||||||
|
add: VPath,
|
||||||
|
wrapped: Box<dyn VirtFS + 'a>,
|
||||||
|
}
|
||||||
|
impl<'a> PrefixFS<'a> {
|
||||||
|
/// Modify the prefix of a file tree
|
||||||
|
pub fn new(wrapped: impl VirtFS + 'a, remove: impl AsRef<str>, add: impl AsRef<str>) -> Self {
|
||||||
|
Self {
|
||||||
|
wrapped: Box::new(wrapped),
|
||||||
|
remove: VPath::parse(remove.as_ref()),
|
||||||
|
add: VPath::parse(add.as_ref()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn proc_path(&self, path: &[Token<String>]) -> Option<Vec<Token<String>>> {
|
||||||
|
let path = path.strip_prefix(self.remove.as_slice())?;
|
||||||
|
Some(self.add.0.iter().chain(path).cloned().collect_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> VirtFS for PrefixFS<'a> {
|
||||||
|
fn get(&self, path: &[Token<String>], full_path: &PathSlice) -> super::FSResult {
|
||||||
|
let path =
|
||||||
|
self.proc_path(path).ok_or_else(|| CodeNotFound::new(full_path.to_vpath()).pack())?;
|
||||||
|
self.wrapped.get(&path, full_path)
|
||||||
|
}
|
||||||
|
fn display(&self, path: &[Token<String>]) -> Option<String> {
|
||||||
|
self.wrapped.display(&self.proc_path(path)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
11
orchid-extension/Cargo.toml
Normal file
11
orchid-extension/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "orchid-extension"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
||||||
|
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||||
|
orchid-base = { version = "0.1.0", path = "../orchid-base" }
|
||||||
35
orchid-extension/src/entrypoint.rs
Normal file
35
orchid-extension/src/entrypoint.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use orchid_api::proto::{ExtMsgSet, ExtensionHeader, HostExtNotif, HostExtReq, HostHeader};
|
||||||
|
use orchid_api_traits::{Decode, Encode};
|
||||||
|
use orchid_base::child::{recv_parent_msg, send_parent_msg};
|
||||||
|
use orchid_base::clone;
|
||||||
|
use orchid_base::intern::{init_replica, sweep_replica};
|
||||||
|
use orchid_base::reqnot::{ReqNot, Requester};
|
||||||
|
|
||||||
|
pub struct ExtensionData {}
|
||||||
|
|
||||||
|
pub fn main(data: &mut ExtensionData) {
|
||||||
|
HostHeader::decode(&mut &recv_parent_msg().unwrap()[..]);
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
ExtensionHeader { systems: vec![] }.encode(&mut buf);
|
||||||
|
send_parent_msg(&buf).unwrap();
|
||||||
|
let exiting = Arc::new(AtomicBool::new(false));
|
||||||
|
let rn = ReqNot::<ExtMsgSet>::new(
|
||||||
|
|a, _| send_parent_msg(a).unwrap(),
|
||||||
|
clone!(exiting; move |n, _| match n {
|
||||||
|
HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed),
|
||||||
|
_ => todo!(),
|
||||||
|
}),
|
||||||
|
|req| match req.req() {
|
||||||
|
HostExtReq::Ping(ping) => req.handle(ping, &()),
|
||||||
|
HostExtReq::Sweep(sweep) => req.handle(sweep, &sweep_replica()),
|
||||||
|
_ => todo!(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
init_replica(rn.clone().map());
|
||||||
|
while !exiting.load(Ordering::Relaxed) {
|
||||||
|
rn.receive(recv_parent_msg().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
30
orchid-extension/src/extension.rs
Normal file
30
orchid-extension/src/extension.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use orchid_api::proto::{ExtMsgSet, ExtensionHeader, HostExtNotif, HostExtReq, HostHeader};
|
||||||
|
use orchid_api_traits::{Decode, Encode};
|
||||||
|
use ordered_float::NotNan;
|
||||||
|
|
||||||
|
use crate::child::{recv_parent_msg, send_parent_msg};
|
||||||
|
use crate::clone;
|
||||||
|
use crate::intern::{init_replica, sweep_replica};
|
||||||
|
use crate::reqnot::{ReqNot, Requester as _};
|
||||||
|
|
||||||
|
pub struct SystemParams {
|
||||||
|
deps: Vec<SystemHandle>,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SystemCtor {
|
||||||
|
deps: Vec<String>,
|
||||||
|
make: Box<dyn FnMut(SystemParams) -> System>,
|
||||||
|
name: String,
|
||||||
|
prio: NotNan<f64>,
|
||||||
|
dependencies: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ExtensionData {
|
||||||
|
systems: Vec<SystemCtor>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
1
orchid-extension/src/lib.rs
Normal file
1
orchid-extension/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod entrypoint;
|
||||||
8
orchid-host/Cargo.toml
Normal file
8
orchid-host/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "orchid-host"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
41
orchid-host/src/expr.rs
Normal file
41
orchid-host/src/expr.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use orchid_api::expr::ExprTicket;
|
||||||
|
|
||||||
|
use crate::host::AtomHand;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct RtExpr {
|
||||||
|
is_canonical: Arc<AtomicBool>,
|
||||||
|
data: Arc<()>,
|
||||||
|
}
|
||||||
|
impl RtExpr {
|
||||||
|
pub fn as_atom(&self) -> Option<AtomHand> { todo!() }
|
||||||
|
pub fn strong_count(&self) -> usize { todo!() }
|
||||||
|
pub fn id(&self) -> u64 { self.data.as_ref() as *const () as usize as u64 }
|
||||||
|
pub fn canonicalize(&self) -> ExprTicket {
|
||||||
|
if !self.is_canonical.swap(true, Ordering::Relaxed) {
|
||||||
|
KNOWN_EXPRS.write().unwrap().entry(self.id()).or_insert_with(|| self.clone());
|
||||||
|
}
|
||||||
|
self.id()
|
||||||
|
}
|
||||||
|
pub fn resolve(tk: ExprTicket) -> Option<Self> { KNOWN_EXPRS.read().unwrap().get(&tk).cloned() }
|
||||||
|
}
|
||||||
|
impl Drop for RtExpr {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// If the only two references left are this and known, remove from known
|
||||||
|
if Arc::strong_count(&self.data) == 2 && self.is_canonical.load(Ordering::Relaxed) {
|
||||||
|
// if known is poisoned, a leak is preferable to a panicking destructor
|
||||||
|
if let Ok(mut w) = KNOWN_EXPRS.write() {
|
||||||
|
w.remove(&self.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static!{
|
||||||
|
static ref KNOWN_EXPRS: RwLock<HashMap<ExprTicket, RtExpr>> = RwLock::default();
|
||||||
|
}
|
||||||
234
orchid-host/src/lib.rs
Normal file
234
orchid-host/src/lib.rs
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
use std::io::Write as _;
|
||||||
|
use std::sync::atomic::{AtomicU16, AtomicU32, Ordering};
|
||||||
|
use std::sync::{Arc, Mutex, RwLock, Weak};
|
||||||
|
use std::{fmt, io, process, thread};
|
||||||
|
|
||||||
|
use derive_destructure::destructure;
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use orchid_api::atom::{Atom, AtomDrop, AtomSame, CallRef, FinalCall, Fwd, Fwded};
|
||||||
|
use orchid_api::expr::{Acquire, Expr, ExprNotif, ExprTicket, Release, Relocate};
|
||||||
|
use orchid_api::intern::IntReq;
|
||||||
|
use orchid_api::proto::{
|
||||||
|
ExtHostNotif, ExtHostReq, ExtensionHeader, HostExtNotif, HostHeader, HostMsgSet,
|
||||||
|
};
|
||||||
|
use orchid_api::system::{NewSystem, SysDeclId, SysId, SystemDecl, SystemDrop};
|
||||||
|
use orchid_api::tree::{GetConstTree, TreeModule};
|
||||||
|
use orchid_api_traits::{Decode, Encode};
|
||||||
|
use ordered_float::NotNan;
|
||||||
|
|
||||||
|
use crate::clone;
|
||||||
|
use crate::expr::RtExpr;
|
||||||
|
use crate::intern::{deintern, intern};
|
||||||
|
use crate::reqnot::{ReqNot, Requester as _};
|
||||||
|
|
||||||
|
#[derive(Debug, destructure)]
|
||||||
|
pub struct AtomData {
|
||||||
|
owner: System,
|
||||||
|
drop: bool,
|
||||||
|
data: Vec<u8>,
|
||||||
|
}
|
||||||
|
impl AtomData {
|
||||||
|
fn api(self) -> Atom {
|
||||||
|
let (owner, drop, data) = self.destructure();
|
||||||
|
Atom { data, drop, owner: owner.0.id }
|
||||||
|
}
|
||||||
|
fn api_ref(&self) -> Atom {
|
||||||
|
Atom { data: self.data.clone(), drop: self.drop, owner: self.owner.0.id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Drop for AtomData {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.owner.0.ext.0.reqnot.notify(AtomDrop(Atom {
|
||||||
|
owner: self.owner.0.id,
|
||||||
|
data: self.data.clone(),
|
||||||
|
drop: true,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct AtomHand(Arc<AtomData>);
|
||||||
|
impl AtomHand {
|
||||||
|
pub fn from_api(Atom { data, drop, owner }: Atom) -> Self {
|
||||||
|
let owner = System::resolve(owner).expect("Atom owned by non-existing system");
|
||||||
|
Self(Arc::new(AtomData { data, drop, owner }))
|
||||||
|
}
|
||||||
|
pub fn call(self, arg: RtExpr) -> Expr {
|
||||||
|
let owner_sys = self.0.owner.clone();
|
||||||
|
let ext = &owner_sys.0.ext;
|
||||||
|
let ticket = owner_sys.give_expr(arg.canonicalize(), || arg);
|
||||||
|
match Arc::try_unwrap(self.0) {
|
||||||
|
Ok(data) => ext.0.reqnot.request(FinalCall(data.api(), ticket)),
|
||||||
|
Err(hand) => ext.0.reqnot.request(CallRef(hand.api_ref(), ticket)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn same(&self, other: &AtomHand) -> bool {
|
||||||
|
let owner = self.0.owner.0.id;
|
||||||
|
if other.0.owner.0.id != owner {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.0.owner.0.ext.0.reqnot.request(AtomSame(self.0.api_ref(), other.0.api_ref()))
|
||||||
|
}
|
||||||
|
pub fn req(&self, req: Vec<u8>) -> Vec<u8> {
|
||||||
|
self.0.owner.0.ext.0.reqnot.request(Fwded(self.0.api_ref(), req))
|
||||||
|
}
|
||||||
|
pub fn api_ref(&self) -> Atom { self.0.api_ref() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Data held about an Extension. This is refcounted within [Extension]. It's
|
||||||
|
/// important to only ever access parts of this struct through the [Arc] because
|
||||||
|
/// the components reference each other through [Weak]s of it, and will panic if
|
||||||
|
/// upgrading fails.
|
||||||
|
#[derive(destructure)]
|
||||||
|
pub struct ExtensionData {
|
||||||
|
child: Mutex<process::Child>,
|
||||||
|
reqnot: ReqNot<HostMsgSet>,
|
||||||
|
systems: Vec<SystemCtor>,
|
||||||
|
}
|
||||||
|
impl Drop for ExtensionData {
|
||||||
|
fn drop(&mut self) { self.reqnot.notify(HostExtNotif::Exit) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn acq_expr(sys: SysId, extk: ExprTicket) {
|
||||||
|
(System::resolve(sys).expect("Expr acq'd by invalid system"))
|
||||||
|
.give_expr(extk, || RtExpr::resolve(extk).expect("Invalid expr acq'd"));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rel_expr(sys: SysId, extk: ExprTicket) {
|
||||||
|
let sys = System::resolve(sys).unwrap();
|
||||||
|
let mut exprs = sys.0.exprs.write().unwrap();
|
||||||
|
exprs.entry(extk).and_replace_entry_with(|_, (rc, rt)| {
|
||||||
|
(0 < rc.fetch_sub(1, Ordering::Relaxed)).then_some((rc, rt))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Extension(Arc<ExtensionData>);
|
||||||
|
impl Extension {
|
||||||
|
pub fn new(mut cmd: process::Command) -> io::Result<Self> {
|
||||||
|
let mut child = cmd.stdin(process::Stdio::piped()).stdout(process::Stdio::piped()).spawn()?;
|
||||||
|
HostHeader.encode(child.stdin.as_mut().unwrap());
|
||||||
|
let eh = ExtensionHeader::decode(child.stdout.as_mut().unwrap());
|
||||||
|
Ok(Self(Arc::new_cyclic(|weak| ExtensionData {
|
||||||
|
child: Mutex::new(child),
|
||||||
|
reqnot: ReqNot::new(
|
||||||
|
clone!(weak; move |sfn, _| {
|
||||||
|
let arc: Arc<ExtensionData> = weak.upgrade().unwrap();
|
||||||
|
let mut g = arc.child.lock().unwrap();
|
||||||
|
g.stdin.as_mut().unwrap().write_all(sfn).unwrap();
|
||||||
|
}),
|
||||||
|
|notif, _| match notif {
|
||||||
|
ExtHostNotif::Expr(expr) => match expr {
|
||||||
|
ExprNotif::Acquire(Acquire(sys, extk)) => acq_expr(sys, extk),
|
||||||
|
ExprNotif::Release(Release(sys, extk)) => rel_expr(sys, extk),
|
||||||
|
ExprNotif::Relocate(Relocate { inc, dec, expr }) => {
|
||||||
|
acq_expr(inc, expr);
|
||||||
|
rel_expr(dec, expr);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|req| match req.req() {
|
||||||
|
ExtHostReq::Ping(ping) => req.handle(ping, &()),
|
||||||
|
ExtHostReq::IntReq(intreq) => match intreq {
|
||||||
|
IntReq::InternStr(s) => req.handle(s, &intern(&**s.0).marker()),
|
||||||
|
IntReq::InternStrv(v) => req.handle(v, &intern(&*v.0).marker()),
|
||||||
|
IntReq::ExternStr(si) => req.handle(si, &deintern(si.0).arc()),
|
||||||
|
IntReq::ExternStrv(vi) =>
|
||||||
|
req.handle(vi, &Arc::new(deintern(vi.0).iter().map(|t| t.marker()).collect_vec())),
|
||||||
|
},
|
||||||
|
ExtHostReq::Fwd(fw @ Fwd(atom, _body)) => {
|
||||||
|
let sys = System::resolve(atom.owner).unwrap();
|
||||||
|
thread::spawn(clone!(fw; move || {
|
||||||
|
req.handle(&fw, &sys.0.ext.0.reqnot.request(Fwded(fw.0.clone(), fw.1.clone())))
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
_ => todo!(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
systems: eh.systems.into_iter().map(|decl| SystemCtor { decl, ext: weak.clone() }).collect(),
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
pub fn systems(&self) -> impl Iterator<Item = &SystemCtor> { self.0.systems.iter() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SystemCtor {
|
||||||
|
decl: SystemDecl,
|
||||||
|
ext: Weak<ExtensionData>,
|
||||||
|
}
|
||||||
|
impl SystemCtor {
|
||||||
|
pub fn name(&self) -> &str { &self.decl.name }
|
||||||
|
pub fn priority(&self) -> NotNan<f64> { self.decl.priority }
|
||||||
|
pub fn depends(&self) -> impl ExactSizeIterator<Item = &str> {
|
||||||
|
self.decl.depends.iter().map(|s| &**s)
|
||||||
|
}
|
||||||
|
pub fn run<'a>(&self, depends: impl IntoIterator<Item = &'a System>) -> System {
|
||||||
|
let mut inst_g = SYSTEM_INSTS.write().unwrap();
|
||||||
|
let depends = depends.into_iter().map(|si| si.0.id).collect_vec();
|
||||||
|
debug_assert_eq!(depends.len(), self.decl.depends.len(), "Wrong number of deps provided");
|
||||||
|
let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension");
|
||||||
|
static NEXT_ID: AtomicU16 = AtomicU16::new(0);
|
||||||
|
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
|
let () = ext.reqnot.request(NewSystem { depends, id, system: self.decl.id });
|
||||||
|
let data = System(Arc::new(SystemInstData {
|
||||||
|
decl_id: self.decl.id,
|
||||||
|
ext: Extension(ext),
|
||||||
|
exprs: RwLock::default(),
|
||||||
|
id,
|
||||||
|
}));
|
||||||
|
inst_g.insert(id, data.clone());
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref SYSTEM_INSTS: RwLock<HashMap<u16, System>> = RwLock::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(destructure)]
|
||||||
|
pub struct SystemInstData {
|
||||||
|
exprs: RwLock<HashMap<ExprTicket, (AtomicU32, RtExpr)>>,
|
||||||
|
ext: Extension,
|
||||||
|
decl_id: SysDeclId,
|
||||||
|
id: u16,
|
||||||
|
}
|
||||||
|
impl Drop for SystemInstData {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.ext.0.reqnot.notify(SystemDrop(self.id));
|
||||||
|
if let Ok(mut g) = SYSTEM_INSTS.write() {
|
||||||
|
g.remove(&self.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct System(Arc<SystemInstData>);
|
||||||
|
impl System {
|
||||||
|
fn resolve(id: u16) -> Option<System> { SYSTEM_INSTS.read().unwrap().get(&id).cloned() }
|
||||||
|
fn give_expr(&self, ticket: ExprTicket, get_expr: impl FnOnce() -> RtExpr) -> ExprTicket {
|
||||||
|
let mut exprs = self.0.exprs.write().unwrap();
|
||||||
|
exprs
|
||||||
|
.entry(ticket)
|
||||||
|
.and_modify(|(c, _)| {
|
||||||
|
c.fetch_add(1, Ordering::Relaxed);
|
||||||
|
})
|
||||||
|
.or_insert((AtomicU32::new(1), get_expr()));
|
||||||
|
ticket
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn const_tree(&self) -> TreeModule { self.0.ext.0.reqnot.request(GetConstTree(self.0.id)) }
|
||||||
|
}
|
||||||
|
impl fmt::Debug for System {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let ctor = (self.0.ext.0.systems.iter().find(|c| c.decl.id == self.0.decl_id))
|
||||||
|
.expect("System instance with no associated constructor");
|
||||||
|
write!(f, "System({} @ {} #{}, ", ctor.decl.name, ctor.decl.priority, self.0.id)?;
|
||||||
|
match self.0.exprs.read() {
|
||||||
|
Err(_) => write!(f, "expressions unavailable"),
|
||||||
|
Ok(r) => {
|
||||||
|
let rc: u32 = r.values().map(|v| v.0.load(Ordering::Relaxed)).sum();
|
||||||
|
write!(f, "{rc} refs to {} exprs", r.len())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
orchid-std/Cargo.toml
Normal file
8
orchid-std/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "orchid-std"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
12
orchid-std/src/lib.rs
Normal file
12
orchid-std/src/lib.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
pub fn add(left: usize, right: usize) -> usize { left + right }
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
let result = add(2, 2);
|
||||||
|
assert_eq!(result, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
1
orchidlang/.gitignore
vendored
Normal file
1
orchidlang/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
target
|
||||||
828
orchidlang/Cargo.lock
generated
Normal file
828
orchidlang/Cargo.lock
generated
Normal file
@@ -0,0 +1,828 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "allocator-api2"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bound"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6021ae095f16f54aaae093f4c723700430e71eab731d3b0a07fc8fe258fd5110"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.50",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const_format"
|
||||||
|
version = "0.2.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673"
|
||||||
|
dependencies = [
|
||||||
|
"const_format_proc_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const_format_proc_macros"
|
||||||
|
version = "0.2.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dyn-clone"
|
||||||
|
version = "1.0.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "globset"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"bstr",
|
||||||
|
"log",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
"allocator-api2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "intern-all"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20c9bf7d7b0572f7b4398fddc93ac1a200a92d1ba319a27dac04649b2223c0f6"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
"lazy_static",
|
||||||
|
"trait-set",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kernel32-sys"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.2.8",
|
||||||
|
"winapi-build",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.153"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "never"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "numtoa"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "orchidlang"
|
||||||
|
version = "0.3.0"
|
||||||
|
dependencies = [
|
||||||
|
"bound",
|
||||||
|
"clap",
|
||||||
|
"const_format",
|
||||||
|
"dyn-clone",
|
||||||
|
"hashbrown",
|
||||||
|
"intern-all",
|
||||||
|
"itertools",
|
||||||
|
"never",
|
||||||
|
"once_cell",
|
||||||
|
"ordered-float",
|
||||||
|
"paste",
|
||||||
|
"rayon",
|
||||||
|
"rust-embed",
|
||||||
|
"substack",
|
||||||
|
"take_mut",
|
||||||
|
"termsize",
|
||||||
|
"trait-set",
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ordered-float"
|
||||||
|
version = "4.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.78"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_termios"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed"
|
||||||
|
version = "8.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a82c0bbc10308ed323529fd3c1dce8badda635aa319a5ff0e6466f33b8101e3f"
|
||||||
|
dependencies = [
|
||||||
|
"rust-embed-impl",
|
||||||
|
"rust-embed-utils",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed-impl"
|
||||||
|
version = "8.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6227c01b1783cdfee1bcf844eb44594cd16ec71c35305bf1c9fb5aade2735e16"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rust-embed-utils",
|
||||||
|
"syn 2.0.50",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed-utils"
|
||||||
|
version = "8.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8cb0a25bfbb2d4b4402179c2cf030387d9990857ce08a32592c6238db9fa8665"
|
||||||
|
dependencies = [
|
||||||
|
"globset",
|
||||||
|
"sha2",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.197"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.197"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.50",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.10.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "substack"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffccc3d80f0a489de67aa74ff31ab852abb973e1c6dacf3704889e00ca544e7f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.50"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "take_mut"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termion"
|
||||||
|
version = "1.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"numtoa",
|
||||||
|
"redox_syscall",
|
||||||
|
"redox_termios",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termsize"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e86d824a8e90f342ad3ef4bd51ef7119a9b681b0cc9f8ee7b2852f02ccd2517"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"kernel32-sys",
|
||||||
|
"libc",
|
||||||
|
"termion",
|
||||||
|
"winapi 0.2.8",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "trait-set"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b79e2e9c9ab44c6d7c20d5976961b47e8f49ac199154daa514b77cd1ab536625"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-build"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.7.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.7.32"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.50",
|
||||||
|
]
|
||||||
41
orchidlang/Cargo.toml
Normal file
41
orchidlang/Cargo.toml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
[package]
|
||||||
|
name = "orchidlang"
|
||||||
|
version = "0.3.0"
|
||||||
|
edition = "2021"
|
||||||
|
license = "GPL-3.0"
|
||||||
|
repository = "https://github.com/lbfalvy/orchid"
|
||||||
|
description = """
|
||||||
|
An embeddable pure functional scripting language
|
||||||
|
"""
|
||||||
|
authors = ["Lawrence Bethlenfalvy <lbfalvy@protonmail.com>"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "orcx"
|
||||||
|
path = "src/bin/orcx.rs"
|
||||||
|
doc = false
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
hashbrown = "0.14"
|
||||||
|
ordered-float = "4.2"
|
||||||
|
itertools = "0.12"
|
||||||
|
dyn-clone = "1.0"
|
||||||
|
trait-set = "0.3"
|
||||||
|
paste = "1.0"
|
||||||
|
rust-embed = { version = "8.2", features = ["include-exclude"] }
|
||||||
|
take_mut = "0.2"
|
||||||
|
unicode-segmentation = "1.11"
|
||||||
|
never = "0.1"
|
||||||
|
substack = "1.1"
|
||||||
|
intern-all = "0.4.1"
|
||||||
|
once_cell = "1.19"
|
||||||
|
const_format = "0.2"
|
||||||
|
bound = "0.5"
|
||||||
|
# Dependencies of orcx
|
||||||
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
|
rayon = "1.8"
|
||||||
|
termsize = "0.1"
|
||||||
@@ -94,7 +94,7 @@ fn exprv_rec(
|
|||||||
}};
|
}};
|
||||||
let v_end = match v.back() {
|
let v_end = match v.back() {
|
||||||
None => return expr_rec(last, ctx),
|
None => return expr_rec(last, ctx),
|
||||||
Some(penultimate) => penultimate.range.range.end,
|
Some(penultimate) => penultimate.range.end(),
|
||||||
};
|
};
|
||||||
let f = exprv_rec(v, ctx.clone(), location.map_range(|r| r.start..v_end))?;
|
let f = exprv_rec(v, ctx.clone(), location.map_range(|r| r.start..v_end))?;
|
||||||
let x = expr_rec(last, ctx.clone())?;
|
let x = expr_rec(last, ctx.clone())?;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user