forked from Orchid/orchid
Almost Alpha
Massive improvements across the board. One day I'll adopt incremental commits.
This commit is contained in:
416
Cargo.lock
generated
416
Cargo.lock
generated
@@ -4,20 +4,21 @@ version = 3
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ahash"
|
name = "ahash"
|
||||||
version = "0.8.3"
|
version = "0.8.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
|
checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"version_check",
|
"version_check",
|
||||||
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.7.20"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
@@ -30,9 +31,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.4"
|
version = "0.6.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
|
checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"anstyle-parse",
|
"anstyle-parse",
|
||||||
@@ -44,33 +45,33 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.0"
|
version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
|
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-parse"
|
name = "anstyle-parse"
|
||||||
version = "0.2.0"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
|
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-query"
|
name = "anstyle-query"
|
||||||
version = "1.0.0"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-wincon"
|
name = "anstyle-wincon"
|
||||||
version = "3.0.1"
|
version = "3.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
|
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
@@ -99,12 +100,6 @@ version = "1.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "2.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
@@ -115,21 +110,21 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bound"
|
||||||
version = "1.5.0"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a246e68bb43f6cd9db24bea052a53e40405417c5fb372e3d1a8a7f770a564ef5"
|
checksum = "6021ae095f16f54aaae093f4c723700430e71eab731d3b0a07fc8fe258fd5110"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cc"
|
|
||||||
version = "1.0.79"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -138,9 +133,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.4.7"
|
version = "4.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
|
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -148,9 +143,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.4.7"
|
version = "4.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
|
checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -160,21 +155,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.4.7"
|
version = "4.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.13",
|
"syn 2.0.50",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
@@ -182,15 +177,6 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "concurrent-queue"
|
|
||||||
version = "2.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const_format"
|
name = "const_format"
|
||||||
version = "0.2.32"
|
version = "0.2.32"
|
||||||
@@ -213,45 +199,37 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.7"
|
version = "0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
|
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-deque"
|
name = "crossbeam-deque"
|
||||||
version = "0.8.3"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
|
||||||
"crossbeam-epoch",
|
"crossbeam-epoch",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-epoch"
|
name = "crossbeam-epoch"
|
||||||
version = "0.9.15"
|
version = "0.9.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
|
||||||
"cfg-if",
|
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
"memoffset",
|
|
||||||
"scopeguard",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.16"
|
version = "0.8.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
@@ -275,42 +253,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dyn-clone"
|
name = "dyn-clone"
|
||||||
version = "1.0.11"
|
version = "1.0.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
|
checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.8.1"
|
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 = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "errno"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
|
|
||||||
dependencies = [
|
|
||||||
"errno-dragonfly",
|
|
||||||
"libc",
|
|
||||||
"windows-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "errno-dragonfly"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[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"
|
||||||
@@ -324,22 +275,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "globset"
|
name = "globset"
|
||||||
version = "0.4.10"
|
version = "0.4.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc"
|
checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"bstr",
|
"bstr",
|
||||||
"fnv",
|
|
||||||
"log",
|
"log",
|
||||||
"regex",
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.2"
|
version = "0.14.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
|
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
@@ -362,19 +313,20 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "intern-all"
|
name = "intern-all"
|
||||||
version = "0.2.0"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d79d55e732e243f6762e0fc7b245bfd9fa0e0246356ed6cfdba62d9c707e36c1"
|
checksum = "20c9bf7d7b0572f7b4398fddc93ac1a200a92d1ba319a27dac04649b2223c0f6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"trait-set",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.12.0"
|
version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
@@ -397,48 +349,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.148"
|
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 = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
|
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "linux-raw-sys"
|
|
||||||
version = "0.4.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.17"
|
version = "0.4.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.5.0"
|
version = "2.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memoffset"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "memorize"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6b505dbd3a88b64417e29469500c32af2b538ba5f703100761f657540a1c442d"
|
|
||||||
dependencies = [
|
|
||||||
"hashbrown",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "never"
|
name = "never"
|
||||||
@@ -448,9 +373,9 @@ checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.15"
|
version = "0.2.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
@@ -469,20 +394,19 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orchidlang"
|
name = "orchidlang"
|
||||||
version = "0.2.2"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bound",
|
||||||
"clap",
|
"clap",
|
||||||
"const_format",
|
"const_format",
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"intern-all",
|
"intern-all",
|
||||||
"itertools",
|
"itertools",
|
||||||
"memorize",
|
|
||||||
"never",
|
"never",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"paste",
|
"paste",
|
||||||
"polling",
|
|
||||||
"rayon",
|
"rayon",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"substack",
|
"substack",
|
||||||
@@ -494,62 +418,42 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-float"
|
name = "ordered-float"
|
||||||
version = "4.1.0"
|
version = "4.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3a540f3e3b3d7929c884e46d093d344e4e5bdeed54d08bf007df50c93cc85d5"
|
checksum = "a76df7075c7d4d01fdcb46c912dd17fba5b60c78ea480b475f2b6ab6f666584e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paste"
|
name = "paste"
|
||||||
version = "1.0.12"
|
version = "1.0.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
|
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project-lite"
|
|
||||||
version = "0.2.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "polling"
|
|
||||||
version = "3.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e53b6af1f60f36f8c2ac2aad5459d75a5a9b4be1e8cdd40264f315d78193e531"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"concurrent-queue",
|
|
||||||
"pin-project-lite",
|
|
||||||
"rustix",
|
|
||||||
"tracing",
|
|
||||||
"windows-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.56"
|
version = "1.0.78"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.26"
|
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 = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.8.0"
|
version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
|
checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
"rayon-core",
|
"rayon-core",
|
||||||
@@ -557,9 +461,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon-core"
|
name = "rayon-core"
|
||||||
version = "1.12.0"
|
version = "1.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
|
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-deque",
|
"crossbeam-deque",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
@@ -571,7 +475,7 @@ 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 = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -581,10 +485,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
|
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex-automata"
|
||||||
version = "1.7.3"
|
version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
|
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -593,15 +497,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.29"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed"
|
name = "rust-embed"
|
||||||
version = "8.0.0"
|
version = "8.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1e7d90385b59f0a6bf3d3b757f3ca4ece2048265d70db20a2016043d4509a40"
|
checksum = "a82c0bbc10308ed323529fd3c1dce8badda635aa319a5ff0e6466f33b8101e3f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rust-embed-impl",
|
"rust-embed-impl",
|
||||||
"rust-embed-utils",
|
"rust-embed-utils",
|
||||||
@@ -610,41 +514,28 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed-impl"
|
name = "rust-embed-impl"
|
||||||
version = "8.0.0"
|
version = "8.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c3d8c6fd84090ae348e63a84336b112b5c3918b3bf0493a581f7bd8ee623c29"
|
checksum = "6227c01b1783cdfee1bcf844eb44594cd16ec71c35305bf1c9fb5aade2735e16"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rust-embed-utils",
|
"rust-embed-utils",
|
||||||
"syn 2.0.13",
|
"syn 2.0.50",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed-utils"
|
name = "rust-embed-utils"
|
||||||
version = "8.0.0"
|
version = "8.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "873feff8cb7bf86fdf0a71bb21c95159f4e4a37dd7a4bd1855a940909b583ada"
|
checksum = "8cb0a25bfbb2d4b4402179c2cf030387d9990857ce08a32592c6238db9fa8665"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"globset",
|
"globset",
|
||||||
"sha2",
|
"sha2",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustix"
|
|
||||||
version = "0.38.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.4.0",
|
|
||||||
"errno",
|
|
||||||
"libc",
|
|
||||||
"linux-raw-sys",
|
|
||||||
"windows-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
@@ -655,22 +546,30 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "serde"
|
||||||
version = "1.2.0"
|
version = "1.0.197"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde_derive"
|
||||||
version = "1.0.160"
|
version = "1.0.197"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
|
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.50",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.6"
|
version = "0.10.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
@@ -679,9 +578,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "substack"
|
name = "substack"
|
||||||
@@ -702,9 +601,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.13"
|
version = "2.0.50"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
|
checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -742,23 +641,6 @@ dependencies = [
|
|||||||
"winapi 0.2.8",
|
"winapi 0.2.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing"
|
|
||||||
version = "0.1.37"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tracing-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-core"
|
|
||||||
version = "0.1.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "trait-set"
|
name = "trait-set"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@@ -772,21 +654,21 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.16.0"
|
version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.8"
|
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 = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
version = "1.10.1"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
@@ -808,9 +690,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walkdir"
|
name = "walkdir"
|
||||||
version = "2.3.3"
|
version = "2.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
|
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"same-file",
|
"same-file",
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
@@ -846,9 +728,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-util"
|
name = "winapi-util"
|
||||||
version = "0.1.5"
|
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 = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
]
|
]
|
||||||
@@ -861,18 +743,18 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.48.0"
|
version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.48.0"
|
version = "0.52.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc",
|
||||||
@@ -885,42 +767,62 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.48.0"
|
version = "0.52.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.48.0"
|
version = "0.52.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.48.0"
|
version = "0.52.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.48.0"
|
version = "0.52.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.48.0"
|
version = "0.52.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.48.0"
|
version = "0.52.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.48.0"
|
version = "0.52.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
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",
|
||||||
|
]
|
||||||
|
|||||||
36
Cargo.toml
36
Cargo.toml
@@ -1,15 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "orchidlang"
|
name = "orchidlang"
|
||||||
version = "0.2.2"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-or-later"
|
license = "GPL-3.0"
|
||||||
repository = "https://github.com/lbfalvy/orchid"
|
repository = "https://github.com/lbfalvy/orchid"
|
||||||
description = """
|
description = """
|
||||||
An embeddable pure functional scripting language
|
An embeddable pure functional scripting language
|
||||||
"""
|
"""
|
||||||
authors = [
|
authors = ["Lawrence Bethlenfalvy <lbfalvy@protonmail.com>"]
|
||||||
"Lawrence Bethlenfalvy <lbfalvy@protonmail.com>"
|
|
||||||
]
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
@@ -23,21 +21,21 @@ doc = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hashbrown = "0.14"
|
hashbrown = "0.14"
|
||||||
ordered-float = "4.1"
|
ordered-float = "4.2"
|
||||||
itertools = "0.12"
|
itertools = "0.12"
|
||||||
dyn-clone = "1.0"
|
dyn-clone = "1.0"
|
||||||
clap = { version = "4.4", features = ["derive"] }
|
|
||||||
trait-set = "0.3"
|
trait-set = "0.3"
|
||||||
paste = "1.0"
|
paste = "1.0"
|
||||||
rust-embed = { version = "8.0", features = ["include-exclude"] }
|
rust-embed = { version = "8.2", features = ["include-exclude"] }
|
||||||
take_mut = "0.2.2"
|
take_mut = "0.2"
|
||||||
unicode-segmentation = "1.10.1"
|
unicode-segmentation = "1.11"
|
||||||
polling = "3.3.0"
|
never = "0.1"
|
||||||
never = "0.1.0"
|
substack = "1.1"
|
||||||
memorize = "2.0.0"
|
intern-all = "0.4.1"
|
||||||
substack = "1.1.0"
|
once_cell = "1.19"
|
||||||
rayon = "1.8.0"
|
const_format = "0.2"
|
||||||
intern-all = "0.2.0"
|
bound = "0.5"
|
||||||
once_cell = "1.19.0"
|
# Dependencies of orcx
|
||||||
const_format = "0.2.32"
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
termsize = "0.1.6"
|
rayon = "1.8"
|
||||||
|
termsize = "0.1"
|
||||||
|
|||||||
19
ROADMAP.md
19
ROADMAP.md
@@ -2,15 +2,15 @@ This document is a wishlist, its items aren't ordered in any way other than inli
|
|||||||
|
|
||||||
# Language
|
# Language
|
||||||
|
|
||||||
## Typeclasses
|
None! Thanks to very aggressive modularization, changes to the core language are almost never needed to achieve specific goals
|
||||||
Elixir-style protocols probably, only with n-ary dispatch which I saw in SICP-js
|
|
||||||
|
|
||||||
# Rules
|
# Rules
|
||||||
|
|
||||||
## Placeholder constraints
|
## Placeholder constraints
|
||||||
Simultaneously match a pattern to a subexpression and give it a name to copy it over
|
Simultaneously match a pattern to a subexpression and give it a name to copy it over
|
||||||
|
|
||||||
- Copy unique 1->1 names over by default to preserve their location info
|
## Role annotations
|
||||||
|
Some way for the rule repository to record the roles certain tokens took in patterns, and some way for the macros to attach semantic information to these roles, so that dev tooling can understand the purpose of each token
|
||||||
|
|
||||||
# STL
|
# STL
|
||||||
|
|
||||||
@@ -20,11 +20,8 @@ Functions for each command type which destructure it and pass it to an Orchid ca
|
|||||||
## Runtime error handling
|
## Runtime error handling
|
||||||
result? multipath cps utils? Not sure yet.
|
result? multipath cps utils? Not sure yet.
|
||||||
|
|
||||||
## Pattern matching
|
|
||||||
This was the main trick in Orchid, still want to do it, still need to polish the language first
|
|
||||||
|
|
||||||
## Macro error handling
|
## Macro error handling
|
||||||
Error tokens with rules to lift them out. Kinda depends on preservation of location info in rules to be really useful
|
Error tokens with rules to lift them out.
|
||||||
|
|
||||||
# Systems
|
# Systems
|
||||||
|
|
||||||
@@ -36,3 +33,11 @@ Event-driven I/O with single-fire events and resubscription to relay backpressur
|
|||||||
|
|
||||||
## New: Marshall
|
## New: Marshall
|
||||||
Serialization of Orchid data, including code, given customizable sets of serializable foreign items. Alternatively, code reflection so that all this can go in the STL
|
Serialization of Orchid data, including code, given customizable sets of serializable foreign items. Alternatively, code reflection so that all this can go in the STL
|
||||||
|
|
||||||
|
# Miscellaneous
|
||||||
|
|
||||||
|
## Language server
|
||||||
|
A very rudimentary language server to visually indicate what the macros do to your code
|
||||||
|
|
||||||
|
## Type checker
|
||||||
|
In the distant hopeful future, I'd like to support a type system
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
type-complexity-threshold = 300
|
type-complexity-threshold = 300
|
||||||
|
avoid-breaking-exported-api = false
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import std::(to_float, to_string, panic, string::char_at)
|
import std::(panic, string::char_at)
|
||||||
|
import std::conv::(to_float, to_string)
|
||||||
|
|
||||||
export const main := do{
|
export const main := do{
|
||||||
cps print "left operand: ";
|
cps data = prompt "left operand: ";
|
||||||
cps data = readln;
|
|
||||||
let a = to_float data;
|
let a = to_float data;
|
||||||
cps print "operator: ";
|
cps op = prompt "operator: ";
|
||||||
cps op = readln;
|
cps println "you selected \"${op}\"";
|
||||||
cps println ("you selected \"" ++ op ++ "\"");
|
cps data = prompt "right operand: ";
|
||||||
cps print "right operand: ";
|
|
||||||
cps data = readln;
|
|
||||||
let b = to_float data;
|
let b = to_float data;
|
||||||
let result = (
|
let result = (
|
||||||
if op == "+" then a + b
|
if op == "+" then a + b
|
||||||
@@ -17,6 +15,6 @@ export const main := do{
|
|||||||
else if op == "/" then a / b
|
else if op == "/" then a / b
|
||||||
else (panic "Unsupported operation")
|
else (panic "Unsupported operation")
|
||||||
);
|
);
|
||||||
cps println ("Result: " ++ to_string result);
|
cps println "Result: ${result}";
|
||||||
0
|
exit_status::success
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,35 @@
|
|||||||
import system::(io, fs, async)
|
import system::(io, fs, async)
|
||||||
import std::(to_string, to_uint, inspect)
|
import std::(conv::(to_string, to_uint), inspect)
|
||||||
|
|
||||||
--[
|
const folder_view := \path. do cps {
|
||||||
const folder_view_old := \path. do{
|
cps println $ "Contents of ${path}";
|
||||||
cps println $ "Contents of " ++ fs::os_print path;
|
|
||||||
cps entries = async::block_on $ fs::read_dir path;
|
cps entries = async::block_on $ fs::read_dir path;
|
||||||
cps list::enumerate entries
|
cps list::enumerate entries
|
||||||
|> list::map ((t[id, t[name, is_dir]]) =>
|
|> list::map ((t[id, t[name, is_dir]]) =>
|
||||||
println $ to_string id ++ ": " ++ fs::os_print name ++ if is_dir then "/" else ""
|
println $ "${id}: ${name}" ++ if is_dir then "/" else ""
|
||||||
)
|
)
|
||||||
|> list::chain;
|
|> list::chain;
|
||||||
cps print "select an entry, or .. to move up: ";
|
cps choice = prompt "select an entry, or .. to move up: ";
|
||||||
cps choice = readln;
|
cps new_path = if choice == ".." then do cps {
|
||||||
if (choice == "..") then do {
|
let t[parent_path, _] = fs::pop_path path
|
||||||
let parent_path = fs::pop_path path
|
|> option::assume;
|
||||||
|> option::assume
|
cps pass parent_path;
|
||||||
|> tuple::pick 0 2;
|
} else do cps {
|
||||||
next parent_path
|
|
||||||
} else do {
|
|
||||||
let t[subname, is_dir] = to_uint choice
|
let t[subname, is_dir] = to_uint choice
|
||||||
|> (list::get entries)
|
|> (list::get entries)
|
||||||
|> option::assume;
|
|> option::assume;
|
||||||
let subpath = fs::join_paths path subname;
|
let subpath = fs::join_paths path subname;
|
||||||
if is_dir then next subpath
|
cps if is_dir then pass subpath else do cps {
|
||||||
else do {
|
|
||||||
cps file = async::block_on $ fs::read_file subpath;
|
cps file = async::block_on $ fs::read_file subpath;
|
||||||
cps contents = async::block_on $ io::read_string file;
|
cps contents = async::block_on $ io::read_string file;
|
||||||
cps println contents;
|
cps println contents;
|
||||||
next path
|
cps _ = prompt "Hit Enter to return to the parent directory: ";
|
||||||
}
|
cps pass path;
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
]--
|
cps pass new_path;
|
||||||
|
|
||||||
const folder_view := \path. do cps {
|
|
||||||
cps println $ "Contents of " ++ fs::os_print path;
|
|
||||||
cps entries = async::block_on $ fs::read_dir path;
|
|
||||||
let t[name, is_dir] = option::assume $ list::get entries 0;
|
|
||||||
cps println $ to_string name ++ " " ++ fs::os_print is_dir
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const main := loop_over (path = fs::cwd) {
|
const main := loop_over (path = fs::cwd) {
|
||||||
cps folder_view path;
|
cps path = folder_view path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1 @@
|
|||||||
import std::exit_status
|
const main := println "Hello World!" exit_status::success
|
||||||
import std::conv
|
|
||||||
import std::number
|
|
||||||
import std::tuple
|
|
||||||
import std::list
|
|
||||||
|
|
||||||
const main := match t["set", "foo", 1] {
|
|
||||||
t[= "set", key, val] =>
|
|
||||||
$"Setting ${ key ++ $"${1 + 1}" } to ${val}"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ export const main := do{
|
|||||||
|> list::reduce ((a, b) => a + b)
|
|> list::reduce ((a, b) => a + b)
|
||||||
|> option::assume;
|
|> option::assume;
|
||||||
cps println $ to_string sum;
|
cps println $ to_string sum;
|
||||||
0
|
exit_status::success
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import std::to_string
|
import std::conv::to_string
|
||||||
|
|
||||||
export const main := do{
|
export const main := do{
|
||||||
let foo = map::new[
|
let foo = map::new[
|
||||||
@@ -10,5 +10,5 @@ export const main := do{
|
|||||||
let num = map::get foo "bar"
|
let num = map::get foo "bar"
|
||||||
|> option::assume;
|
|> option::assume;
|
||||||
cps println $ to_string num;
|
cps println $ to_string num;
|
||||||
0
|
exit_status::success
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ const bar := map::new[
|
|||||||
]
|
]
|
||||||
|
|
||||||
const test2 := match bar {
|
const test2 := match bar {
|
||||||
map::having ["is_alive" = true, "greeting" = foo] => foo
|
map::having ["is_alive" = true, "greeting" = hello, "name" = name] => hello
|
||||||
}
|
}
|
||||||
|
|
||||||
const tests := test2 ++ ", " ++ test1
|
const tests := "${test2}, ${test1}"
|
||||||
|
|
||||||
const main := conv::to_string bar
|
const main := conv::to_string bar
|
||||||
|
|||||||
23
examples/protocol/main.orc
Normal file
23
examples/protocol/main.orc
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
protocol animal (
|
||||||
|
const noise := vcall "noise"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dog (
|
||||||
|
const new := \name. wrap name
|
||||||
|
impl super::animal := map::new [
|
||||||
|
"noise" = \dog. "${dog}: Woof!"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
type cat (
|
||||||
|
const new := wrap 0
|
||||||
|
impl super::animal := map::new [
|
||||||
|
"noise" = \_. "a cat: Mew!"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
const main := do {
|
||||||
|
list::new [dog::new "Pavlov", cat::new]
|
||||||
|
|> list::map (\a. println $ animal::noise a)
|
||||||
|
|> list::chain exit_status::success
|
||||||
|
}
|
||||||
9
language-server-log.txt
Normal file
9
language-server-log.txt
Normal file
File diff suppressed because one or more lines are too long
@@ -1,49 +1,61 @@
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use orchidlang::error::Reporter;
|
||||||
use orchidlang::facade::macro_runner::MacroRunner;
|
use orchidlang::facade::macro_runner::MacroRunner;
|
||||||
use orchidlang::libs::std::exit_status::ExitStatus;
|
use orchidlang::libs::std::exit_status::OrcExitStatus;
|
||||||
|
use orchidlang::location::{CodeGenInfo, CodeLocation};
|
||||||
use orchidlang::name::Sym;
|
use orchidlang::name::Sym;
|
||||||
|
use orchidlang::pipeline::project::{ItemKind, ProjItem, ProjectTree};
|
||||||
|
use orchidlang::sym;
|
||||||
|
|
||||||
use crate::cli::cmd_prompt;
|
use crate::cli::cmd_prompt;
|
||||||
|
|
||||||
/// A little utility to step through the resolution of a macro set
|
/// A little utility to step through the reproject of a macro set
|
||||||
pub fn main(macro_runner: MacroRunner, sym: Sym) -> ExitStatus {
|
pub fn main(tree: ProjectTree, symbol: Sym) -> OrcExitStatus {
|
||||||
let outname = sym.iter().join("::");
|
print!("Macro debugger starting on {symbol}");
|
||||||
let (mut code, location) = match macro_runner.consts.get(&sym) {
|
let location = CodeLocation::new_gen(CodeGenInfo::no_details(sym!(orcx::macro_runner)));
|
||||||
Some(rep) => (rep.value.clone(), rep.range.clone()),
|
let expr_ent = match tree.0.walk1_ref(&[], &symbol[..], |_| true) {
|
||||||
None => {
|
Ok((e, _)) => e.clone(),
|
||||||
let valid = macro_runner.consts.keys();
|
Err(e) => {
|
||||||
let valid_str = valid.map(|t| t.iter().join("::")).join("\n\t");
|
eprintln!("{}", e.at(&location.origin()));
|
||||||
eprintln!("Symbol {outname} not found\nvalid symbols: \n\t{valid_str}\n");
|
return OrcExitStatus::Failure;
|
||||||
return ExitStatus::Failure;
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
print!("Debugging macros in {outname} defined at {location}");
|
let mut expr = match expr_ent.item() {
|
||||||
println!("\nInitial state: {code}");
|
Some(ProjItem { kind: ItemKind::Const(c) }) => c.clone(),
|
||||||
|
_ => {
|
||||||
|
eprintln!("macro-debug argument must be a constant");
|
||||||
|
return OrcExitStatus::Failure;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let reporter = Reporter::new();
|
||||||
|
let macro_runner = MacroRunner::new(&tree, None, &reporter);
|
||||||
|
reporter.assert_exit();
|
||||||
|
println!("\nInitial state: {expr}");
|
||||||
// print_for_debug(&code);
|
// print_for_debug(&code);
|
||||||
let mut steps = macro_runner.step(sym).enumerate();
|
let mut steps = macro_runner.step(expr.clone()).enumerate();
|
||||||
loop {
|
loop {
|
||||||
let (cmd, _) = cmd_prompt("\ncmd> ").unwrap();
|
let (cmd, _) = cmd_prompt("\ncmd> ").unwrap();
|
||||||
match cmd.trim() {
|
match cmd.trim() {
|
||||||
"" | "n" | "next" => match steps.next() {
|
"" | "n" | "next" => match steps.next() {
|
||||||
None => print!("Halted"),
|
None => print!("Halted"),
|
||||||
Some((idx, c)) => {
|
Some((idx, c)) => {
|
||||||
code = c;
|
expr = c;
|
||||||
print!("Step {idx}: {code}");
|
print!("Step {idx}: {expr}");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"p" | "print" => {
|
"p" | "print" => {
|
||||||
let glossary = code.value.collect_names();
|
let glossary = expr.value.collect_names();
|
||||||
let gl_str = glossary.iter().map(|t| t.iter().join("::")).join(", ");
|
let gl_str = glossary.iter().join(", ");
|
||||||
print!("code: {code}\nglossary: {gl_str}")
|
print!("code: {expr}\nglossary: {gl_str}")
|
||||||
},
|
},
|
||||||
"d" | "dump" => print!("Rules: {}", macro_runner.repo),
|
"d" | "dump" => print!("Rules: {}", macro_runner.repo),
|
||||||
"q" | "quit" => return ExitStatus::Success,
|
"q" | "quit" => return OrcExitStatus::Success,
|
||||||
"complete" => {
|
"complete" => {
|
||||||
match steps.last() {
|
match steps.last() {
|
||||||
Some((idx, c)) => print!("Step {idx}: {c}"),
|
Some((idx, c)) => print!("Step {idx}: {c}"),
|
||||||
None => print!("Already halted"),
|
None => print!("Already halted"),
|
||||||
}
|
}
|
||||||
return ExitStatus::Success;
|
return OrcExitStatus::Success;
|
||||||
},
|
},
|
||||||
"h" | "help" => print!(
|
"h" | "help" => print!(
|
||||||
"Available commands:
|
"Available commands:
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
pub mod macro_debug;
|
pub mod macro_debug;
|
||||||
pub mod print_project;
|
pub mod print_project;
|
||||||
|
pub mod shared;
|
||||||
|
pub mod tests;
|
||||||
|
|||||||
@@ -10,11 +10,7 @@ pub struct ProjPrintOpts {
|
|||||||
|
|
||||||
fn indent(amount: u16) -> String { " ".repeat(amount.into()) }
|
fn indent(amount: u16) -> String { " ".repeat(amount.into()) }
|
||||||
|
|
||||||
pub fn print_proj_mod(
|
pub fn print_proj_mod(module: &ProjectMod, lvl: u16, opts: ProjPrintOpts) -> String {
|
||||||
module: &ProjectMod,
|
|
||||||
lvl: u16,
|
|
||||||
opts: ProjPrintOpts,
|
|
||||||
) -> String {
|
|
||||||
let mut acc = String::new();
|
let mut acc = String::new();
|
||||||
let tab = indent(lvl);
|
let tab = indent(lvl);
|
||||||
for (key, ModEntry { member, x }) in &module.entries {
|
for (key, ModEntry { member, x }) in &module.entries {
|
||||||
|
|||||||
64
src/bin/features/shared.rs
Normal file
64
src/bin/features/shared.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
use std::io::BufReader;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
use orchidlang::facade::loader::Loader;
|
||||||
|
use orchidlang::libs::asynch::system::AsynchSystem;
|
||||||
|
use orchidlang::libs::directfs::DirectFS;
|
||||||
|
use orchidlang::libs::io::{IOService, Sink, Source, Stream};
|
||||||
|
use orchidlang::libs::scheduler::system::SeqScheduler;
|
||||||
|
use orchidlang::libs::std::std_system::StdConfig;
|
||||||
|
|
||||||
|
pub fn stdin_source() -> Source { BufReader::new(Box::new(std::io::stdin())) }
|
||||||
|
pub fn stdout_sink() -> Sink { Box::new(std::io::stdout()) }
|
||||||
|
pub fn stderr_sink() -> Sink { Box::new(std::io::stderr()) }
|
||||||
|
|
||||||
|
pub fn with_std_env<T>(cb: impl for<'a> FnOnce(Loader<'a>) -> T) -> T {
|
||||||
|
with_env(stdin_source(), stdout_sink(), stderr_sink(), cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_env<T>(
|
||||||
|
stdin: Source,
|
||||||
|
stdout: Sink,
|
||||||
|
stderr: Sink,
|
||||||
|
cb: impl for<'a> FnOnce(Loader<'a>) -> T,
|
||||||
|
) -> T {
|
||||||
|
let mut asynch = AsynchSystem::new();
|
||||||
|
let scheduler = SeqScheduler::new(&mut asynch);
|
||||||
|
let std_streams = [
|
||||||
|
("stdin", Stream::Source(stdin)),
|
||||||
|
("stdout", Stream::Sink(stdout)),
|
||||||
|
("stderr", Stream::Sink(stderr)),
|
||||||
|
];
|
||||||
|
let env = Loader::new()
|
||||||
|
.add_system(StdConfig { impure: true })
|
||||||
|
.add_system(asynch)
|
||||||
|
.add_system(scheduler.clone())
|
||||||
|
.add_system(IOService::new(scheduler.clone(), std_streams))
|
||||||
|
.add_system(DirectFS::new(scheduler));
|
||||||
|
cb(env)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn worker_cnt() -> usize { thread::available_parallelism().map(usize::from).unwrap_or(1) }
|
||||||
|
|
||||||
|
macro_rules! unwrap_exit {
|
||||||
|
($param:expr) => {
|
||||||
|
match $param {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
|
return ExitCode::FAILURE;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($param:expr; $error:expr) => {
|
||||||
|
match $param {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
|
return $error;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use unwrap_exit;
|
||||||
111
src/bin/features/tests.rs
Normal file
111
src/bin/features/tests.rs
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
use std::fmt;
|
||||||
|
use std::io::BufReader;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use itertools::Itertools;
|
||||||
|
use orchidlang::error::{ProjectError, ProjectResult, Reporter};
|
||||||
|
use orchidlang::facade::loader::Loader;
|
||||||
|
use orchidlang::facade::macro_runner::MacroRunner;
|
||||||
|
use orchidlang::facade::merge_trees::NortConst;
|
||||||
|
use orchidlang::facade::process::Process;
|
||||||
|
use orchidlang::foreign::error::{RTError, RTErrorObj, RTResult};
|
||||||
|
use orchidlang::foreign::inert::Inert;
|
||||||
|
use orchidlang::interpreter::error::RunError;
|
||||||
|
use orchidlang::interpreter::nort;
|
||||||
|
use orchidlang::libs::io::{Sink, Source};
|
||||||
|
use orchidlang::libs::std::exit_status::OrcExitStatus;
|
||||||
|
use orchidlang::name::Sym;
|
||||||
|
use rayon::iter::ParallelIterator;
|
||||||
|
use rayon::slice::ParallelSlice;
|
||||||
|
|
||||||
|
use super::shared::{with_env, worker_cnt};
|
||||||
|
|
||||||
|
pub fn mock_source() -> Source { BufReader::new(Box::new(&[][..])) }
|
||||||
|
pub fn mock_sink() -> Sink { Box::<Vec<u8>>::default() }
|
||||||
|
pub fn with_mock_env<T>(cb: impl for<'a> FnOnce(Loader<'a>) -> T) -> T {
|
||||||
|
with_env(mock_source(), mock_sink(), mock_sink(), cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TestDidNotHalt(Sym);
|
||||||
|
impl RTError for TestDidNotHalt {}
|
||||||
|
impl fmt::Display for TestDidNotHalt {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Test {} did not halt", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TestDidNotSucceed(Sym, nort::Expr);
|
||||||
|
impl RTError for TestDidNotSucceed {}
|
||||||
|
impl fmt::Display for TestDidNotSucceed {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Test {} settled on {}", self.0, self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_test(proc: &mut Process, name: Sym, data: NortConst) -> RTResult<()> {
|
||||||
|
let res = proc.run(data.value, Some(10_000)).map_err(|e| match e {
|
||||||
|
RunError::Extern(e) => e,
|
||||||
|
RunError::Interrupted(_) => TestDidNotHalt(name.clone()).pack(),
|
||||||
|
})?;
|
||||||
|
match res.clone().downcast()? {
|
||||||
|
Inert(OrcExitStatus::Success) => Ok(()),
|
||||||
|
_ => Err(TestDidNotSucceed(name, res).pack()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn run_tests(
|
||||||
|
dir: &Path,
|
||||||
|
macro_limit: usize,
|
||||||
|
threads: Option<usize>,
|
||||||
|
tests: &[(Sym, NortConst)],
|
||||||
|
) -> ProjectResult<()> {
|
||||||
|
with_mock_env(|env| {
|
||||||
|
let reporter = Reporter::new();
|
||||||
|
env.proc_dir(dir.to_owned(), true, Some(macro_limit), &reporter);
|
||||||
|
reporter.bind()
|
||||||
|
})?;
|
||||||
|
let threads = threads.unwrap_or_else(worker_cnt);
|
||||||
|
rayon::ThreadPoolBuilder::new().num_threads(threads).build_global().unwrap();
|
||||||
|
let batch_size = tests.len().div_ceil(threads);
|
||||||
|
let errors = (tests.par_chunks(batch_size))
|
||||||
|
.map(|tests| {
|
||||||
|
with_mock_env(|env| {
|
||||||
|
let reporter = Reporter::new();
|
||||||
|
let mut proc = env.proc_dir(dir.to_owned(), true, Some(macro_limit), &reporter);
|
||||||
|
reporter.assert(); // checked above
|
||||||
|
(tests.iter())
|
||||||
|
.filter_map(|(test, constant)| {
|
||||||
|
Some((test.clone(), run_test(&mut proc, test.clone(), constant.clone()).err()?))
|
||||||
|
})
|
||||||
|
.collect_vec()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
if errors.is_empty() { Ok(()) } else { Err(TestsFailed(errors).pack()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TestsFailed(HashMap<Sym, RTErrorObj>);
|
||||||
|
impl ProjectError for TestsFailed {
|
||||||
|
const DESCRIPTION: &'static str = "Various tests failed";
|
||||||
|
fn message(&self) -> String {
|
||||||
|
([format!("{} tests failed. Errors:", self.0.len())].into_iter())
|
||||||
|
.chain(self.0.iter().map(|(k, e)| format!("In {k}, {e}")))
|
||||||
|
.join("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_tree_tests(dir: &Path, reporter: &Reporter) -> ProjectResult<Vec<(Sym, NortConst)>> {
|
||||||
|
with_mock_env(|env| {
|
||||||
|
let tree = env.load_dir(dir.to_owned(), reporter);
|
||||||
|
let tree = MacroRunner::new(&tree, Some(10_000), reporter).run_macros(tree, reporter);
|
||||||
|
(tree.all_consts().into_iter())
|
||||||
|
.filter(|(_, rep)| rep.comments.iter().any(|s| s.trim() == "test"))
|
||||||
|
.map(|(k, v)| Ok((k.clone(), NortConst::convert_from(v, reporter))))
|
||||||
|
.collect::<ProjectResult<Vec<_>>>()
|
||||||
|
})
|
||||||
|
}
|
||||||
285
src/bin/orcx.rs
285
src/bin/orcx.rs
@@ -2,37 +2,37 @@ mod cli;
|
|||||||
mod features;
|
mod features;
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::BufReader;
|
use std::io::{stdin, stdout, Write};
|
||||||
use std::num::NonZeroUsize;
|
use std::path::PathBuf;
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
use std::thread::available_parallelism;
|
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::HashSet;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchidlang::error::{ProjectError, ProjectErrorObj, ProjectResult};
|
use never::Never;
|
||||||
use orchidlang::facade::loader::Loader;
|
use orchidlang::error::Reporter;
|
||||||
use orchidlang::facade::macro_runner::MacroRunner;
|
use orchidlang::facade::macro_runner::MacroRunner;
|
||||||
use orchidlang::facade::merge_trees::merge_trees;
|
use orchidlang::facade::merge_trees::{merge_trees, NortConst};
|
||||||
use orchidlang::facade::process::Process;
|
use orchidlang::facade::process::Process;
|
||||||
use orchidlang::foreign::inert::Inert;
|
use orchidlang::foreign::inert::Inert;
|
||||||
use orchidlang::interpreter::context::Halt;
|
use orchidlang::gen::tpl;
|
||||||
use orchidlang::interpreter::nort;
|
use orchidlang::gen::traits::Gen;
|
||||||
use orchidlang::libs::asynch::system::AsynchSystem;
|
use orchidlang::interpreter::gen_nort::nort_gen;
|
||||||
use orchidlang::libs::directfs::DirectFS;
|
use orchidlang::interpreter::nort::{self};
|
||||||
use orchidlang::libs::io::{IOService, Stream};
|
use orchidlang::libs::std::exit_status::OrcExitStatus;
|
||||||
use orchidlang::libs::scheduler::system::SeqScheduler;
|
use orchidlang::libs::std::string::OrcString;
|
||||||
use orchidlang::libs::std::exit_status::ExitStatus;
|
use orchidlang::location::{CodeGenInfo, CodeLocation, SourceRange};
|
||||||
use orchidlang::libs::std::std_system::StdConfig;
|
|
||||||
use orchidlang::location::{CodeGenInfo, CodeLocation};
|
|
||||||
use orchidlang::name::Sym;
|
use orchidlang::name::Sym;
|
||||||
|
use orchidlang::parse::context::FlatLocContext;
|
||||||
|
use orchidlang::parse::lexer::{lex, Lexeme};
|
||||||
|
use orchidlang::sym;
|
||||||
use orchidlang::tree::{ModMemberRef, TreeTransforms};
|
use orchidlang::tree::{ModMemberRef, TreeTransforms};
|
||||||
use rayon::prelude::ParallelIterator;
|
use orchidlang::virt_fs::{decl_file, DeclTree};
|
||||||
use rayon::slice::ParallelSlice;
|
|
||||||
|
|
||||||
use crate::features::macro_debug;
|
use crate::features::macro_debug;
|
||||||
use crate::features::print_project::{print_proj_mod, ProjPrintOpts};
|
use crate::features::print_project::{print_proj_mod, ProjPrintOpts};
|
||||||
|
use crate::features::shared::{stderr_sink, stdout_sink, unwrap_exit, with_env, with_std_env};
|
||||||
|
use crate::features::tests::{get_tree_tests, mock_source, run_test, run_tests, with_mock_env};
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
enum Command {
|
enum Command {
|
||||||
@@ -58,6 +58,7 @@ enum Command {
|
|||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
width: Option<u16>,
|
width: Option<u16>,
|
||||||
},
|
},
|
||||||
|
Repl,
|
||||||
}
|
}
|
||||||
/// Orchid interpreter
|
/// Orchid interpreter
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
@@ -111,132 +112,35 @@ impl Args {
|
|||||||
pub fn chk_proj(&self) -> Result<(), String> { self.chk_dir_main() }
|
pub fn chk_proj(&self) -> Result<(), String> { self.chk_dir_main() }
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! unwrap_exit {
|
|
||||||
($param:expr) => {
|
|
||||||
match $param {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("{e}");
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_std_proc<T>(
|
|
||||||
dir: &Path,
|
|
||||||
macro_limit: usize,
|
|
||||||
f: impl for<'a> FnOnce(Process<'a>) -> ProjectResult<T>,
|
|
||||||
) -> ProjectResult<T> {
|
|
||||||
with_std_env(|env| {
|
|
||||||
let mr = MacroRunner::new(&env.load_dir(dir.to_owned())?)?;
|
|
||||||
let source_syms = mr.run_macros(Some(macro_limit))?;
|
|
||||||
let consts = merge_trees(source_syms, env.systems())?;
|
|
||||||
let proc = Process::new(consts, env.handlers());
|
|
||||||
f(proc)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
pub fn run_test(proc: &mut Process, name: Sym) -> ProjectResult<()> { Ok(()) }
|
|
||||||
pub fn run_tests(
|
|
||||||
dir: &Path,
|
|
||||||
macro_limit: usize,
|
|
||||||
threads: Option<usize>,
|
|
||||||
tests: &[Sym],
|
|
||||||
) -> ProjectResult<()> {
|
|
||||||
with_std_proc(dir, macro_limit, |proc| proc.validate_refs())?;
|
|
||||||
let threads = threads
|
|
||||||
.or_else(|| available_parallelism().ok().map(NonZeroUsize::into))
|
|
||||||
.unwrap_or(1);
|
|
||||||
rayon::ThreadPoolBuilder::new().num_threads(threads).build_global().unwrap();
|
|
||||||
let batch_size = tests.len().div_ceil(threads);
|
|
||||||
let errors = tests
|
|
||||||
.par_chunks(batch_size)
|
|
||||||
.map(|tests| {
|
|
||||||
let res = with_std_proc(dir, macro_limit, |mut proc| {
|
|
||||||
let mut errors = HashMap::new();
|
|
||||||
for test in tests {
|
|
||||||
if let Err(e) = run_test(&mut proc, test.clone()) {
|
|
||||||
errors.insert(test.clone(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(errors)
|
|
||||||
});
|
|
||||||
res.expect("Tested earlier")
|
|
||||||
})
|
|
||||||
.reduce(HashMap::new, |l, r| l.into_iter().chain(r).collect());
|
|
||||||
if errors.is_empty() { Ok(()) } else { Err(TestsFailed(errors).pack()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TestsFailed(HashMap<Sym, ProjectErrorObj>);
|
|
||||||
impl ProjectError for TestsFailed {
|
|
||||||
const DESCRIPTION: &'static str = "Various tests failed";
|
|
||||||
fn message(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"{} tests failed. Errors:\n{}",
|
|
||||||
self.0.len(),
|
|
||||||
self.0.iter().map(|(k, e)| format!("In {k}, {e}")).join("\n")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_tree_tests(dir: &Path) -> ProjectResult<Vec<Sym>> {
|
|
||||||
with_std_env(|env| {
|
|
||||||
env.load_dir(dir.to_owned()).map(|tree| {
|
|
||||||
(tree.all_consts().into_iter())
|
|
||||||
.filter(|(_, rep)| rep.comments.iter().any(|s| s.trim() == "test"))
|
|
||||||
.map(|(k, _)| k.clone())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_std_env<T>(cb: impl for<'a> FnOnce(Loader<'a>) -> T) -> T {
|
|
||||||
let mut asynch = AsynchSystem::new();
|
|
||||||
let scheduler = SeqScheduler::new(&mut asynch);
|
|
||||||
let std_streams = [
|
|
||||||
("stdin", Stream::Source(BufReader::new(Box::new(std::io::stdin())))),
|
|
||||||
("stdout", Stream::Sink(Box::new(std::io::stdout()))),
|
|
||||||
("stderr", Stream::Sink(Box::new(std::io::stderr()))),
|
|
||||||
];
|
|
||||||
let env = Loader::new()
|
|
||||||
.add_system(StdConfig { impure: true })
|
|
||||||
.add_system(asynch)
|
|
||||||
.add_system(scheduler.clone())
|
|
||||||
.add_system(IOService::new(scheduler.clone(), std_streams))
|
|
||||||
.add_system(DirectFS::new(scheduler));
|
|
||||||
cb(env)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() -> ExitCode {
|
pub fn main() -> ExitCode {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
unwrap_exit!(args.chk_proj());
|
unwrap_exit!(args.chk_proj());
|
||||||
let dir = PathBuf::from(args.dir);
|
let dir = PathBuf::from(args.dir);
|
||||||
let main = args.main.map_or_else(
|
let main_s = args.main.as_ref().map_or("tree::main::main", |s| s);
|
||||||
|| Sym::literal("tree::main::main"),
|
let main = Sym::parse(main_s).expect("--main cannot be empty");
|
||||||
|main| Sym::parse(&main).expect("--main cannot be empty"),
|
let location = CodeLocation::new_gen(CodeGenInfo::no_details(sym!(orcx::entrypoint)));
|
||||||
);
|
let reporter = Reporter::new();
|
||||||
|
|
||||||
// subcommands
|
// subcommands
|
||||||
|
#[allow(clippy::blocks_in_conditions)]
|
||||||
match args.command {
|
match args.command {
|
||||||
Some(Command::ListMacros) => with_std_env(|env| {
|
Some(Command::ListMacros) => with_mock_env(|env| {
|
||||||
let tree = unwrap_exit!(env.load_main(dir, main));
|
let tree = env.load_main(dir, [main], &reporter);
|
||||||
let mr = unwrap_exit!(MacroRunner::new(&tree));
|
let mr = MacroRunner::new(&tree, None, &reporter);
|
||||||
println!("Parsed rules: {}", mr.repo);
|
println!("Parsed rules: {}", mr.repo);
|
||||||
ExitCode::SUCCESS
|
ExitCode::SUCCESS
|
||||||
}),
|
}),
|
||||||
Some(Command::ProjectTree { hide_locations, width }) => {
|
Some(Command::ProjectTree { hide_locations, width }) => {
|
||||||
let tree = unwrap_exit!(with_std_env(|env| env.load_main(dir, main)));
|
let tree = with_mock_env(|env| env.load_main(dir, [main], &reporter));
|
||||||
let w = width.or_else(|| termsize::get().map(|s| s.cols)).unwrap_or(74);
|
let w = width.or_else(|| termsize::get().map(|s| s.cols)).unwrap_or(74);
|
||||||
let print_opts = ProjPrintOpts { width: w, hide_locations };
|
let print_opts = ProjPrintOpts { width: w, hide_locations };
|
||||||
println!("Project tree: {}", print_proj_mod(&tree.0, 0, print_opts));
|
println!("Project tree: {}", print_proj_mod(&tree.0, 0, print_opts));
|
||||||
ExitCode::SUCCESS
|
ExitCode::SUCCESS
|
||||||
},
|
},
|
||||||
Some(Command::MacroDebug { symbol }) => with_std_env(|env| {
|
Some(Command::MacroDebug { symbol }) => with_mock_env(|env| {
|
||||||
let tree = unwrap_exit!(env.load_main(dir, main));
|
let tree = env.load_main(dir, [main], &reporter);
|
||||||
let symbol = Sym::parse(&symbol).expect("macro-debug needs an argument");
|
let symbol = Sym::parse(&symbol).expect("macro-debug needs an argument");
|
||||||
macro_debug::main(unwrap_exit!(MacroRunner::new(&tree)), symbol).code()
|
macro_debug::main(tree, symbol).code()
|
||||||
}),
|
}),
|
||||||
Some(Command::Test { only: Some(_), threads: Some(_), .. }) => {
|
Some(Command::Test { only: Some(_), threads: Some(_), .. }) => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@@ -253,25 +157,33 @@ pub fn main() -> ExitCode {
|
|||||||
ExitCode::FAILURE
|
ExitCode::FAILURE
|
||||||
},
|
},
|
||||||
Some(Command::Test { only: None, threads, system: None }) => {
|
Some(Command::Test { only: None, threads, system: None }) => {
|
||||||
let tree_tests = unwrap_exit!(get_tree_tests(&dir));
|
let tree_tests = reporter.unwrap_exit(get_tree_tests(&dir, &reporter));
|
||||||
unwrap_exit!(run_tests(&dir, args.macro_limit, threads, &tree_tests));
|
unwrap_exit!(run_tests(&dir, args.macro_limit, threads, &tree_tests));
|
||||||
ExitCode::SUCCESS
|
ExitCode::SUCCESS
|
||||||
},
|
},
|
||||||
Some(Command::Test { only: Some(symbol), threads: None, system: None }) => {
|
Some(Command::Test { only: Some(symbol), threads: None, system: None }) => {
|
||||||
let symbol = Sym::parse(&symbol).expect("Test needs an argument");
|
let symbol = Sym::parse(&symbol).expect("Test needs an argument");
|
||||||
unwrap_exit!(run_tests(&dir, args.macro_limit, Some(1), &[symbol]));
|
with_env(mock_source(), stdout_sink(), stderr_sink(), |env| {
|
||||||
|
// iife in lieu of try blocks
|
||||||
|
let tree = env.load_main(dir.clone(), [symbol.clone()], &reporter);
|
||||||
|
let mr = MacroRunner::new(&tree, Some(args.macro_limit), &reporter);
|
||||||
|
let consts = mr.run_macros(tree, &reporter).all_consts();
|
||||||
|
let test = consts.get(&symbol).expect("Test not found");
|
||||||
|
let nc = NortConst::convert_from(test.clone(), &reporter);
|
||||||
|
let mut proc = Process::new(merge_trees(consts, env.systems(), &reporter), env.handlers());
|
||||||
|
unwrap_exit!(run_test(&mut proc, symbol.clone(), nc.clone()));
|
||||||
ExitCode::SUCCESS
|
ExitCode::SUCCESS
|
||||||
|
})
|
||||||
},
|
},
|
||||||
Some(Command::Test { only: None, threads, system: Some(system) }) => {
|
Some(Command::Test { only: None, threads, system: Some(system) }) => {
|
||||||
let subtrees = unwrap_exit!(with_std_env(|env| {
|
let subtrees = unwrap_exit!(with_mock_env(|env| {
|
||||||
match env.systems().find(|s| s.name == system) {
|
match env.systems().find(|s| s.name == system) {
|
||||||
None => Err(format!("System {system} not found")),
|
None => Err(format!("System {system} not found")),
|
||||||
Some(sys) => {
|
Some(sys) => {
|
||||||
let mut paths = HashSet::new();
|
let mut paths = HashSet::new();
|
||||||
sys.code.search_all((), |path, node, ()| {
|
sys.code.search_all((), |path, node, ()| {
|
||||||
if matches!(node, ModMemberRef::Item(_)) {
|
if matches!(node, ModMemberRef::Item(_)) {
|
||||||
let name = Sym::new(path.unreverse())
|
let name = Sym::new(path.unreverse()).expect("Empty path means global file");
|
||||||
.expect("Empty path means global file");
|
|
||||||
paths.insert(name);
|
paths.insert(name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -279,48 +191,93 @@ pub fn main() -> ExitCode {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
let in_subtrees =
|
let in_subtrees = |sym: Sym| subtrees.iter().any(|sub| sym[..].starts_with(&sub[..]));
|
||||||
|sym: Sym| subtrees.iter().any(|sub| sym[..].starts_with(&sub[..]));
|
let tests = with_mock_env(|env| {
|
||||||
let tests = unwrap_exit!(with_std_env(|env| -> ProjectResult<_> {
|
let tree = env.load_main(dir.clone(), [main.clone()], &reporter);
|
||||||
let tree = env.load_main(dir.clone(), main.clone())?;
|
let mr = MacroRunner::new(&tree, Some(args.macro_limit), &reporter);
|
||||||
let mr = MacroRunner::new(&tree)?;
|
let src_consts = mr.run_macros(tree, &reporter).all_consts();
|
||||||
let src_consts = mr.run_macros(Some(args.macro_limit))?;
|
let consts = merge_trees(src_consts, env.systems(), &reporter);
|
||||||
let consts = merge_trees(src_consts, env.systems())?;
|
(consts.into_iter())
|
||||||
let test_names = (consts.into_iter())
|
.filter(|(k, v)| in_subtrees(k.clone()) && v.comments.iter().any(|c| c.trim() == "test"))
|
||||||
.filter(|(k, v)| {
|
.collect_vec()
|
||||||
in_subtrees(k.clone())
|
});
|
||||||
&& v.comments.iter().any(|c| c.trim() == "test")
|
|
||||||
})
|
|
||||||
.map(|p| p.0)
|
|
||||||
.collect_vec();
|
|
||||||
Ok(test_names)
|
|
||||||
}));
|
|
||||||
eprintln!("Running {} tests", tests.len());
|
eprintln!("Running {} tests", tests.len());
|
||||||
unwrap_exit!(run_tests(&dir, args.macro_limit, threads, &tests));
|
unwrap_exit!(run_tests(&dir, args.macro_limit, threads, &tests));
|
||||||
eprintln!("All tests pass");
|
eprintln!("All tests pass");
|
||||||
ExitCode::SUCCESS
|
ExitCode::SUCCESS
|
||||||
},
|
},
|
||||||
None => with_std_env(|env| {
|
None => with_std_env(|env| {
|
||||||
let tree = unwrap_exit!(env.load_main(dir, main.clone()));
|
let proc = env.proc_main(dir, [main.clone()], true, Some(args.macro_limit), &reporter);
|
||||||
let mr = unwrap_exit!(MacroRunner::new(&tree));
|
reporter.assert_exit();
|
||||||
let src_consts = unwrap_exit!(mr.run_macros(Some(args.macro_limit)));
|
let ret = unwrap_exit!(proc.run(nort::Clause::Constant(main).into_expr(location), None));
|
||||||
let consts = unwrap_exit!(merge_trees(src_consts, env.systems()));
|
|
||||||
let mut proc = Process::new(consts, env.handlers());
|
|
||||||
unwrap_exit!(proc.validate_refs());
|
|
||||||
let main = nort::Clause::Constant(main.clone())
|
|
||||||
.to_expr(CodeLocation::Gen(CodeGenInfo::no_details("entrypoint")));
|
|
||||||
let ret = unwrap_exit!(proc.run(main, None));
|
|
||||||
let Halt { state, inert, .. } = ret;
|
|
||||||
drop(proc);
|
drop(proc);
|
||||||
assert!(inert, "Gas is not used, only inert data should be yielded");
|
match ret.clone().downcast() {
|
||||||
match state.clone().downcast() {
|
Ok(Inert(OrcExitStatus::Success)) => ExitCode::SUCCESS,
|
||||||
Ok(Inert(ExitStatus::Success)) => ExitCode::SUCCESS,
|
Ok(Inert(OrcExitStatus::Failure)) => ExitCode::FAILURE,
|
||||||
Ok(Inert(ExitStatus::Failure)) => ExitCode::FAILURE,
|
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("{}", state.clause);
|
println!("{}", ret.clause);
|
||||||
ExitCode::SUCCESS
|
ExitCode::SUCCESS
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
Some(Command::Repl) => with_std_env(|env| {
|
||||||
|
let sctx = env.project_ctx(&reporter);
|
||||||
|
loop {
|
||||||
|
let reporter = Reporter::new();
|
||||||
|
print!("orc");
|
||||||
|
let mut src = String::new();
|
||||||
|
let mut paren_tally = 0;
|
||||||
|
loop {
|
||||||
|
print!("> ");
|
||||||
|
stdout().flush().unwrap();
|
||||||
|
let mut buf = String::new();
|
||||||
|
stdin().read_line(&mut buf).unwrap();
|
||||||
|
src += &buf;
|
||||||
|
let range = SourceRange::mock();
|
||||||
|
let spctx = sctx.parsing(range.code());
|
||||||
|
let pctx = FlatLocContext::new(&spctx, &range);
|
||||||
|
let res =
|
||||||
|
lex(Vec::new(), &buf, &pctx, |_| Ok::<_, Never>(false)).unwrap_or_else(|e| match e {});
|
||||||
|
res.tokens.iter().for_each(|e| match &e.lexeme {
|
||||||
|
Lexeme::LP(_) => paren_tally += 1,
|
||||||
|
Lexeme::RP(_) => paren_tally -= 1,
|
||||||
|
_ => (),
|
||||||
|
});
|
||||||
|
if 0 == paren_tally {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let tree = env.load_project_main(
|
||||||
|
[sym!(tree::main::__repl_input__)],
|
||||||
|
DeclTree::ns("tree::main", [decl_file(&format!("const __repl_input__ := {src}"))]),
|
||||||
|
&reporter,
|
||||||
|
);
|
||||||
|
let mr = MacroRunner::new(&tree, Some(args.macro_limit), &reporter);
|
||||||
|
let proj_consts = mr.run_macros(tree, &reporter).all_consts();
|
||||||
|
let consts = merge_trees(proj_consts, env.systems(), &reporter);
|
||||||
|
let ctx = nort_gen(location.clone());
|
||||||
|
let to_string_tpl = tpl::A(tpl::C("std::string::convert"), tpl::Slot);
|
||||||
|
if let Err(err) = reporter.bind() {
|
||||||
|
eprintln!("{err}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let proc = Process::new(consts, env.handlers());
|
||||||
|
let prompt = tpl::C("tree::main::__repl_input__").template(ctx.clone(), []);
|
||||||
|
let out = match proc.run(prompt, Some(1000)) {
|
||||||
|
Ok(out) => out,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if let Ok(out) = proc.run(to_string_tpl.template(ctx, [out.clone()]), Some(1000)) {
|
||||||
|
if let Ok(s) = out.clone().downcast::<Inert<OrcString>>() {
|
||||||
|
println!("{}", s.0.as_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("{out}")
|
||||||
|
}
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
220
src/error.rs
220
src/error.rs
@@ -1,28 +1,30 @@
|
|||||||
//! Abstractions for handling various code-related errors under a common trait
|
//! Abstractions for handling various code-related errors under a common trait
|
||||||
//! object.
|
//! object.
|
||||||
|
|
||||||
use core::fmt;
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::fmt::{Debug, Display};
|
use std::cell::RefCell;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::{fmt, process};
|
||||||
|
|
||||||
use dyn_clone::{clone_box, DynClone};
|
use dyn_clone::{clone_box, DynClone};
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::location::CodeLocation;
|
use crate::location::CodeOrigin;
|
||||||
use crate::utils::boxed_iter::{box_once, BoxedIter};
|
use crate::utils::boxed_iter::{box_once, BoxedIter};
|
||||||
#[allow(unused)] // for doc
|
#[allow(unused)] // for doc
|
||||||
use crate::virt_fs::CodeNotFound;
|
use crate::virt_fs::CodeNotFound;
|
||||||
|
|
||||||
/// A point of interest in resolving the error, such as the point where
|
/// A point of interest in resolving the error, such as the point where
|
||||||
/// processing got stuck, a command that is likely to be incorrect
|
/// processing got stuck, a command that is likely to be incorrect
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ErrorPosition {
|
pub struct ErrorPosition {
|
||||||
/// The suspected location
|
/// The suspected origin
|
||||||
pub location: CodeLocation,
|
pub origin: CodeOrigin,
|
||||||
/// Any information about the role of this location
|
/// Any information about the role of this origin
|
||||||
pub message: Option<String>,
|
pub message: Option<String>,
|
||||||
}
|
}
|
||||||
impl From<CodeLocation> for ErrorPosition {
|
impl From<CodeOrigin> for ErrorPosition {
|
||||||
fn from(location: CodeLocation) -> Self { Self { location, message: None } }
|
fn from(origin: CodeOrigin) -> Self { Self { origin, message: None } }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors addressed to the developer which are to be resolved with
|
/// Errors addressed to the developer which are to be resolved with
|
||||||
@@ -36,13 +38,13 @@ pub trait ProjectError: Sized + Send + Sync + 'static {
|
|||||||
/// Code positions relevant to this error. If you don't implement this, you
|
/// Code positions relevant to this error. If you don't implement this, you
|
||||||
/// must implement [ProjectError::one_position]
|
/// must implement [ProjectError::one_position]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> {
|
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> + '_ {
|
||||||
box_once(ErrorPosition { location: self.one_position(), message: None })
|
box_once(ErrorPosition { origin: self.one_position(), message: None })
|
||||||
}
|
}
|
||||||
/// Short way to provide a single location. If you don't implement this, you
|
/// Short way to provide a single origin. If you don't implement this, you
|
||||||
/// must implement [ProjectError::positions]
|
/// must implement [ProjectError::positions]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn one_position(&self) -> CodeLocation { unimplemented!() }
|
fn one_position(&self) -> CodeOrigin { unimplemented!() }
|
||||||
/// Convert the error into an `Arc<dyn DynProjectError>` to be able to
|
/// Convert the error into an `Arc<dyn DynProjectError>` to be able to
|
||||||
/// handle various errors together
|
/// handle various errors together
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@@ -53,7 +55,11 @@ pub trait ProjectError: Sized + Send + Sync + 'static {
|
|||||||
pub trait DynProjectError: Send + Sync {
|
pub trait DynProjectError: Send + Sync {
|
||||||
/// Access type information about this error
|
/// Access type information about this error
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any_ref(&self) -> &dyn Any;
|
||||||
|
/// Pack the error into a trait object, or leave it as-is if it's already a
|
||||||
|
/// trait object
|
||||||
|
#[must_use]
|
||||||
|
fn into_packed(self: Arc<Self>) -> ProjectErrorObj;
|
||||||
/// A general description of this type of error
|
/// A general description of this type of error
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn description(&self) -> &str;
|
fn description(&self) -> &str;
|
||||||
@@ -62,13 +68,14 @@ pub trait DynProjectError: Send + Sync {
|
|||||||
fn message(&self) -> String { self.description().to_string() }
|
fn message(&self) -> String { self.description().to_string() }
|
||||||
/// Code positions relevant to this error.
|
/// Code positions relevant to this error.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn positions(&self) -> BoxedIter<ErrorPosition>;
|
fn positions(&self) -> BoxedIter<'_, ErrorPosition>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> DynProjectError for T
|
impl<T> DynProjectError for T
|
||||||
where T: ProjectError
|
where T: ProjectError
|
||||||
{
|
{
|
||||||
fn as_any(&self) -> &dyn Any { self }
|
fn as_any_ref(&self) -> &dyn Any { self }
|
||||||
|
fn into_packed(self: Arc<Self>) -> ProjectErrorObj { self }
|
||||||
fn description(&self) -> &str { T::DESCRIPTION }
|
fn description(&self) -> &str { T::DESCRIPTION }
|
||||||
fn message(&self) -> String { ProjectError::message(self) }
|
fn message(&self) -> String { ProjectError::message(self) }
|
||||||
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||||
@@ -76,19 +83,27 @@ where T: ProjectError
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for dyn DynProjectError {
|
impl DynProjectError for ProjectErrorObj {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn as_any_ref(&self) -> &dyn Any { (**self).as_any_ref() }
|
||||||
|
fn description(&self) -> &str { (**self).description() }
|
||||||
|
fn into_packed(self: Arc<Self>) -> ProjectErrorObj { (*self).clone() }
|
||||||
|
fn message(&self) -> String { (**self).message() }
|
||||||
|
fn positions(&self) -> BoxedIter<'_, ErrorPosition> { (**self).positions() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for dyn DynProjectError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let description = self.description();
|
let description = self.description();
|
||||||
let message = self.message();
|
let message = self.message();
|
||||||
let positions = self.positions().collect::<Vec<_>>();
|
let positions = self.positions().collect::<Vec<_>>();
|
||||||
writeln!(f, "Project error: {description}\n{message}")?;
|
writeln!(f, "Project error: {description}\n{message}")?;
|
||||||
if positions.is_empty() {
|
if positions.is_empty() {
|
||||||
writeln!(f, "No locations specified")?;
|
writeln!(f, "No origins specified")?;
|
||||||
} else {
|
} else {
|
||||||
for ErrorPosition { location, message } in positions {
|
for ErrorPosition { origin, message } in positions {
|
||||||
match message {
|
match message {
|
||||||
None => writeln!(f, "@{location}"),
|
None => writeln!(f, "@{origin}"),
|
||||||
Some(msg) => writeln!(f, "@{location}: {msg}"),
|
Some(msg) => writeln!(f, "@{origin}: {msg}"),
|
||||||
}?
|
}?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,22 +111,20 @@ impl Display for dyn DynProjectError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for dyn DynProjectError {
|
impl fmt::Debug for dyn DynProjectError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self}") }
|
||||||
write!(f, "{self}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type-erased [ProjectError] implementor through the [DynProjectError]
|
/// Type-erased [ProjectError] implementor through the [DynProjectError]
|
||||||
/// object-trait
|
/// object-trait
|
||||||
pub type ProjectErrorObj = Arc<dyn DynProjectError>;
|
pub type ProjectErrorObj = Arc<dyn DynProjectError>;
|
||||||
/// Alias for a result with an error of [Rc] of [ProjectError] trait object.
|
/// Alias for a result with an error of [ProjectErrorObj]. This is the type of
|
||||||
/// This is the type of result most commonly returned by pre-run operations.
|
/// result most commonly returned by pre-runtime operations.
|
||||||
pub type ProjectResult<T> = Result<T, ProjectErrorObj>;
|
pub type ProjectResult<T> = Result<T, ProjectErrorObj>;
|
||||||
|
|
||||||
/// A trait for error types that are only missing a location. Do not depend on
|
/// A trait for error types that are only missing an origin. Do not depend on
|
||||||
/// this trait, refer to [DynErrorSansLocation] instead.
|
/// this trait, refer to [DynErrorSansOrigin] instead.
|
||||||
pub trait ErrorSansLocation: Clone + Sized + Send + Sync + 'static {
|
pub trait ErrorSansOrigin: Clone + Sized + Send + Sync + 'static {
|
||||||
/// General description of the error condition
|
/// General description of the error condition
|
||||||
const DESCRIPTION: &'static str;
|
const DESCRIPTION: &'static str;
|
||||||
/// Specific description of the error including code fragments or concrete
|
/// Specific description of the error including code fragments or concrete
|
||||||
@@ -119,67 +132,154 @@ pub trait ErrorSansLocation: Clone + Sized + Send + Sync + 'static {
|
|||||||
fn message(&self) -> String { Self::DESCRIPTION.to_string() }
|
fn message(&self) -> String { Self::DESCRIPTION.to_string() }
|
||||||
/// Convert the error to a type-erased structure for handling on shared
|
/// Convert the error to a type-erased structure for handling on shared
|
||||||
/// channels
|
/// channels
|
||||||
fn pack(self) -> ErrorSansLocationObj { Box::new(self) }
|
fn pack(self) -> ErrorSansOriginObj { Box::new(self) }
|
||||||
|
/// A shortcut to streamline switching code between [ErrorSansOriginObj] and
|
||||||
|
/// concrete types
|
||||||
|
fn bundle(self, origin: &CodeOrigin) -> ProjectErrorObj { self.pack().bundle(origin) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Object-safe equivalent to [ErrorSansLocation]. Implement that one instead of
|
/// Object-safe equivalent to [ErrorSansOrigin]. Implement that one instead of
|
||||||
/// this. Typically found as [ErrorSansLocationObj]
|
/// this. Typically found as [ErrorSansOriginObj]
|
||||||
pub trait DynErrorSansLocation: Any + Send + Sync + DynClone {
|
pub trait DynErrorSansOrigin: Any + Send + Sync + DynClone {
|
||||||
/// Allow to downcast the base object to distinguish between various errors.
|
/// Allow to downcast the base object to distinguish between various errors.
|
||||||
/// The main intended purpose is to trigger a fallback when [CodeNotFound] is
|
/// The main intended purpose is to trigger a fallback when [CodeNotFound] is
|
||||||
/// encountered, but the possibilities are not limited to that.
|
/// encountered, but the possibilities are not limited to that.
|
||||||
fn as_any_ref(&self) -> &dyn Any;
|
fn as_any_ref(&self) -> &dyn Any;
|
||||||
|
/// Regularize the type
|
||||||
|
fn into_packed(self: Box<Self>) -> ErrorSansOriginObj;
|
||||||
/// Generic description of the error condition
|
/// Generic description of the error condition
|
||||||
fn description(&self) -> &str;
|
fn description(&self) -> &str;
|
||||||
/// Specific description of this particular error
|
/// Specific description of this particular error
|
||||||
fn message(&self) -> String;
|
fn message(&self) -> String;
|
||||||
|
/// Add an origin
|
||||||
|
fn bundle(self: Box<Self>, origin: &CodeOrigin) -> ProjectErrorObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type-erased [ErrorSansLocation] implementor through the object-trait
|
/// Type-erased [ErrorSansOrigin] implementor through the object-trait
|
||||||
/// [DynErrorSansLocation]. This can be turned into a [ProjectErrorObj] with
|
/// [DynErrorSansOrigin]. This can be turned into a [ProjectErrorObj] with
|
||||||
/// [bundle_location].
|
/// [ErrorSansOriginObj::bundle].
|
||||||
pub type ErrorSansLocationObj = Box<dyn DynErrorSansLocation>;
|
pub type ErrorSansOriginObj = Box<dyn DynErrorSansOrigin>;
|
||||||
/// A generic project result without location
|
/// A generic project result without origin
|
||||||
pub type ResultSansLocation<T> = Result<T, ErrorSansLocationObj>;
|
pub type ResultSansOrigin<T> = Result<T, ErrorSansOriginObj>;
|
||||||
|
|
||||||
impl<T: ErrorSansLocation + 'static> DynErrorSansLocation for T {
|
impl<T: ErrorSansOrigin + 'static> DynErrorSansOrigin for T {
|
||||||
fn description(&self) -> &str { Self::DESCRIPTION }
|
fn description(&self) -> &str { Self::DESCRIPTION }
|
||||||
fn message(&self) -> String { self.message() }
|
fn message(&self) -> String { (*self).message() }
|
||||||
fn as_any_ref(&self) -> &dyn Any { self }
|
fn as_any_ref(&self) -> &dyn Any { self }
|
||||||
|
fn into_packed(self: Box<Self>) -> ErrorSansOriginObj { (*self).pack() }
|
||||||
|
fn bundle(self: Box<Self>, origin: &CodeOrigin) -> ProjectErrorObj {
|
||||||
|
Arc::new(OriginBundle(origin.clone(), *self))
|
||||||
}
|
}
|
||||||
impl Clone for ErrorSansLocationObj {
|
}
|
||||||
|
impl Clone for ErrorSansOriginObj {
|
||||||
fn clone(&self) -> Self { clone_box(&**self) }
|
fn clone(&self) -> Self { clone_box(&**self) }
|
||||||
}
|
}
|
||||||
impl DynErrorSansLocation for ErrorSansLocationObj {
|
impl DynErrorSansOrigin for ErrorSansOriginObj {
|
||||||
fn description(&self) -> &str { (**self).description() }
|
fn description(&self) -> &str { (**self).description() }
|
||||||
fn message(&self) -> String { (**self).message() }
|
fn message(&self) -> String { (**self).message() }
|
||||||
fn as_any_ref(&self) -> &dyn Any { (**self).as_any_ref() }
|
fn as_any_ref(&self) -> &dyn Any { (**self).as_any_ref() }
|
||||||
|
fn into_packed(self: Box<Self>) -> ErrorSansOriginObj { *self }
|
||||||
|
fn bundle(self: Box<Self>, origin: &CodeOrigin) -> ProjectErrorObj { (*self).bundle(origin) }
|
||||||
}
|
}
|
||||||
impl Display for ErrorSansLocationObj {
|
impl fmt::Display for ErrorSansOriginObj {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
writeln!(f, "{}\nLocation missing from error", self.message())
|
writeln!(f, "{}\nOrigin missing from error", self.message())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Debug for ErrorSansLocationObj {
|
impl fmt::Debug for ErrorSansOriginObj {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self}") }
|
||||||
write!(f, "{self}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LocationBundle(CodeLocation, Box<dyn DynErrorSansLocation>);
|
struct OriginBundle<T: ErrorSansOrigin>(CodeOrigin, T);
|
||||||
impl DynProjectError for LocationBundle {
|
impl<T: ErrorSansOrigin> DynProjectError for OriginBundle<T> {
|
||||||
fn as_any(&self) -> &dyn Any { self.1.as_any_ref() }
|
fn as_any_ref(&self) -> &dyn Any { self.1.as_any_ref() }
|
||||||
|
fn into_packed(self: Arc<Self>) -> ProjectErrorObj { self }
|
||||||
fn description(&self) -> &str { self.1.description() }
|
fn description(&self) -> &str { self.1.description() }
|
||||||
fn message(&self) -> String { self.1.message() }
|
fn message(&self) -> String { self.1.message() }
|
||||||
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||||
box_once(ErrorPosition { location: self.0.clone(), message: None })
|
box_once(ErrorPosition { origin: self.0.clone(), message: None })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a location to an [ErrorSansLocation]
|
/// A collection for tracking fatal errors without halting. Participating
|
||||||
pub fn bundle_location(
|
/// functions return [ProjectResult] even if they only ever construct [Ok]. When
|
||||||
location: &CodeLocation,
|
/// they call other participating functions, instead of directly forwarding
|
||||||
details: &dyn DynErrorSansLocation,
|
/// errors with `?` they should prefer constructing a fallback value with
|
||||||
) -> ProjectErrorObj {
|
/// [Reporter::fallback]. If any error is added to a [Reporter] in a function,
|
||||||
Arc::new(LocationBundle(location.clone(), clone_box(details)))
|
/// the return value is valid but its meaning need not be related in any way to
|
||||||
|
/// the inputs.
|
||||||
|
///
|
||||||
|
/// Returning [Err] from a function that accepts `&mut Reporter` indicates not
|
||||||
|
/// that there was a fatal error but that it wasn't possible to construct a
|
||||||
|
/// fallback, so if it can, the caller should construct one.
|
||||||
|
pub struct Reporter(RefCell<Vec<ProjectErrorObj>>);
|
||||||
|
impl Reporter {
|
||||||
|
/// Create a new error reporter
|
||||||
|
pub fn new() -> Self { Self(RefCell::new(Vec::new())) }
|
||||||
|
/// Returns true if any errors were regorded. If this ever returns true, it
|
||||||
|
/// will always return true in the future.
|
||||||
|
pub fn failing(&self) -> bool { !self.0.borrow().is_empty() }
|
||||||
|
/// Report a fatal error
|
||||||
|
pub fn report(&self, error: ProjectErrorObj) { self.0.borrow_mut().push(error) }
|
||||||
|
/// Catch a fatal error, report it, and substitute the value
|
||||||
|
pub fn fallback<T>(&self, res: ProjectResult<T>, cb: impl FnOnce(ProjectErrorObj) -> T) -> T {
|
||||||
|
res.inspect_err(|e| self.report(e.clone())).unwrap_or_else(cb)
|
||||||
|
}
|
||||||
|
/// Panic if there were errors
|
||||||
|
pub fn assert(&self) { self.unwrap(Ok(())) }
|
||||||
|
/// Exit with code -1 if there were errors
|
||||||
|
pub fn assert_exit(&self) { self.unwrap_exit(Ok(())) }
|
||||||
|
/// Panic with descriptive messages if there were errors. If there were no
|
||||||
|
/// errors, unwrap the result
|
||||||
|
pub fn unwrap<T>(&self, res: ProjectResult<T>) -> T {
|
||||||
|
if self.failing() {
|
||||||
|
panic!("Errors were encountered: \n{}", self.0.borrow().iter().join("\n"));
|
||||||
|
}
|
||||||
|
res.unwrap()
|
||||||
|
}
|
||||||
|
/// Print errors and exit if any occurred. If there were no errors, unwrap
|
||||||
|
/// the result
|
||||||
|
pub fn unwrap_exit<T>(&self, res: ProjectResult<T>) -> T {
|
||||||
|
if self.failing() {
|
||||||
|
eprintln!("Errors were encountered: \n{}", self.0.borrow().iter().join("\n"));
|
||||||
|
process::exit(-1)
|
||||||
|
}
|
||||||
|
res.unwrap_or_else(|e| {
|
||||||
|
eprintln!("{e}");
|
||||||
|
process::exit(-1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/// Take the errors out of the reporter
|
||||||
|
#[must_use]
|
||||||
|
pub fn into_errors(self) -> Option<Vec<ProjectErrorObj>> {
|
||||||
|
let v = self.0.into_inner();
|
||||||
|
if v.is_empty() { None } else { Some(v) }
|
||||||
|
}
|
||||||
|
/// Raise an error if the reporter contains any errors
|
||||||
|
pub fn bind(self) -> ProjectResult<()> {
|
||||||
|
match self.into_errors() {
|
||||||
|
None => Ok(()),
|
||||||
|
Some(v) if v.len() == 1 => Err(v.into_iter().exactly_one().unwrap()),
|
||||||
|
Some(v) => Err(MultiError(v).pack()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Reporter {
|
||||||
|
fn default() -> Self { Self::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MultiError(Vec<ProjectErrorObj>);
|
||||||
|
impl ProjectError for MultiError {
|
||||||
|
const DESCRIPTION: &'static str = "Multiple errors occurred";
|
||||||
|
fn message(&self) -> String { format!("{} errors occurred", self.0.len()) }
|
||||||
|
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> + '_ {
|
||||||
|
self.0.iter().flat_map(|e| {
|
||||||
|
e.positions().map(|pos| {
|
||||||
|
let emsg = e.message();
|
||||||
|
let msg = if let Some(pmsg) = pos.message { format!("{emsg}: {pmsg}") } else { emsg };
|
||||||
|
ErrorPosition { origin: pos.origin, message: Some(msg) }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,28 @@
|
|||||||
use std::path::{Path, PathBuf};
|
//! The main structure of the façade, collects systems and exposes various
|
||||||
use std::{fs, iter};
|
//! operations over the whole set.
|
||||||
|
|
||||||
use intern_all::{i, Tok};
|
use std::borrow::Borrow;
|
||||||
use substack::Substack;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use intern_all::i;
|
||||||
|
|
||||||
|
use super::macro_runner::MacroRunner;
|
||||||
|
use super::merge_trees::merge_trees;
|
||||||
|
use super::process::Process;
|
||||||
use super::system::{IntoSystem, System};
|
use super::system::{IntoSystem, System};
|
||||||
use crate::error::ProjectResult;
|
use super::unbound_ref::validate_refs;
|
||||||
|
use crate::error::Reporter;
|
||||||
use crate::gen::tree::ConstTree;
|
use crate::gen::tree::ConstTree;
|
||||||
|
use crate::interpreter::context::RunEnv;
|
||||||
use crate::interpreter::handler::HandlerTable;
|
use crate::interpreter::handler::HandlerTable;
|
||||||
use crate::location::{CodeGenInfo, CodeLocation};
|
use crate::location::{CodeGenInfo, CodeOrigin};
|
||||||
use crate::name::{Sym, VPath};
|
use crate::name::{PathSlice, Sym, VPath};
|
||||||
use crate::pipeline::load_solution::{load_solution, SolutionContext};
|
use crate::pipeline::load_project::{load_project, ProjectContext};
|
||||||
use crate::pipeline::project::ProjectTree;
|
use crate::pipeline::project::ProjectTree;
|
||||||
|
use crate::sym;
|
||||||
use crate::utils::combine::Combine;
|
use crate::utils::combine::Combine;
|
||||||
use crate::utils::sequence::Sequence;
|
use crate::utils::sequence::Sequence;
|
||||||
use crate::utils::unwrap_or::unwrap_or;
|
use crate::virt_fs::{DeclTree, DirNode, Loaded, VirtFS};
|
||||||
use crate::virt_fs::{DeclTree, DirNode, VirtFS};
|
|
||||||
|
|
||||||
/// A compiled environment ready to load user code. It stores the list of
|
/// A compiled environment ready to load user code. It stores the list of
|
||||||
/// systems and combines with usercode to produce a [Process]
|
/// systems and combines with usercode to produce a [Process]
|
||||||
@@ -28,9 +35,7 @@ impl<'a> Loader<'a> {
|
|||||||
pub fn new() -> Self { Self { systems: Vec::new() } }
|
pub fn new() -> Self { Self { systems: Vec::new() } }
|
||||||
|
|
||||||
/// Retrieve the list of systems
|
/// Retrieve the list of systems
|
||||||
pub fn systems(&self) -> impl Iterator<Item = &System<'a>> {
|
pub fn systems(&self) -> impl Iterator<Item = &System<'a>> { self.systems.iter() }
|
||||||
self.systems.iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Register a new system in the environment
|
/// Register a new system in the environment
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@@ -50,21 +55,22 @@ impl<'a> Loader<'a> {
|
|||||||
/// Combine the `constants` fields of all systems
|
/// Combine the `constants` fields of all systems
|
||||||
pub fn constants(&self) -> ConstTree {
|
pub fn constants(&self) -> ConstTree {
|
||||||
(self.systems())
|
(self.systems())
|
||||||
.try_fold(ConstTree::tree::<&str>([]), |acc, sys| {
|
.try_fold(ConstTree::tree::<&str>([]), |acc, sys| acc.combine(sys.constants.clone()))
|
||||||
acc.combine(sys.constants.clone())
|
|
||||||
})
|
|
||||||
.expect("Conflicting const trees")
|
.expect("Conflicting const trees")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handlers(self) -> HandlerTable<'a> {
|
/// Extract the command handlers from the systems, consuming the loader in the
|
||||||
(self.systems.into_iter())
|
/// process. This has to consume the systems because handler tables aren't
|
||||||
.fold(HandlerTable::new(), |t, sys| t.combine(sys.handlers))
|
/// Copy. It also establishes the practice that environments live on the
|
||||||
|
/// stack.
|
||||||
|
pub fn handlers(&self) -> HandlerTable<'_> {
|
||||||
|
(self.systems.iter()).fold(HandlerTable::new(), |t, sys| t.link(&sys.handlers))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compile the environment from the set of systems and return it directly.
|
/// Compile the environment from the set of systems and return it directly.
|
||||||
/// See [#load_dir]
|
/// See [#load_dir]
|
||||||
pub fn solution_ctx(&self) -> ProjectResult<SolutionContext> {
|
pub fn project_ctx<'b>(&self, reporter: &'b Reporter) -> ProjectContext<'_, 'b> {
|
||||||
Ok(SolutionContext {
|
ProjectContext {
|
||||||
lexer_plugins: Sequence::new(|| {
|
lexer_plugins: Sequence::new(|| {
|
||||||
self.systems().flat_map(|sys| &sys.lexer_plugins).map(|b| &**b)
|
self.systems().flat_map(|sys| &sys.lexer_plugins).map(|b| &**b)
|
||||||
}),
|
}),
|
||||||
@@ -72,47 +78,113 @@ impl<'a> Loader<'a> {
|
|||||||
self.systems().flat_map(|sys| &sys.line_parsers).map(|b| &**b)
|
self.systems().flat_map(|sys| &sys.line_parsers).map(|b| &**b)
|
||||||
}),
|
}),
|
||||||
preludes: Sequence::new(|| self.systems().flat_map(|sys| &sys.prelude)),
|
preludes: Sequence::new(|| self.systems().flat_map(|sys| &sys.prelude)),
|
||||||
})
|
reporter,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Combine source code from all systems with the specified directory into a
|
/// Combine source code from all systems with the specified directory into a
|
||||||
/// common [VirtFS]
|
/// common [VirtFS]
|
||||||
pub fn make_dir_tree(&self, dir: PathBuf) -> DeclTree {
|
pub fn make_dir_fs(&self, dir: PathBuf) -> DeclTree {
|
||||||
let dir_node = DirNode::new(dir, ".orc").rc();
|
let dir_node = DirNode::new(dir, ".orc").rc();
|
||||||
let base = DeclTree::tree([("tree", DeclTree::leaf(dir_node))]);
|
DeclTree::tree([("tree", DeclTree::leaf(dir_node))])
|
||||||
(self.systems().try_fold(base, |acc, sub| acc.combine(sub.code.clone())))
|
}
|
||||||
|
|
||||||
|
/// All system trees merged into one
|
||||||
|
pub fn system_fs(&self) -> DeclTree {
|
||||||
|
(self.systems().try_fold(DeclTree::empty(), |acc, sub| acc.combine(sub.code.clone())))
|
||||||
.expect("Conflicting system trees")
|
.expect("Conflicting system trees")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A wrapper around [load_project] that only takes the arguments that aren't
|
||||||
|
/// fully specified by systems
|
||||||
|
pub fn load_project_main(
|
||||||
|
&self,
|
||||||
|
entrypoints: impl IntoIterator<Item = Sym>,
|
||||||
|
root: DeclTree,
|
||||||
|
reporter: &Reporter,
|
||||||
|
) -> ProjectTree {
|
||||||
|
let tgt_loc = CodeOrigin::Gen(CodeGenInfo::no_details(sym!(facade::entrypoint)));
|
||||||
|
let constants = self.constants().unwrap_mod();
|
||||||
|
let targets = entrypoints.into_iter().map(|s| (s, tgt_loc.clone()));
|
||||||
|
let root = self.system_fs().combine(root).expect("System trees conflict with root");
|
||||||
|
load_project(&self.project_ctx(reporter), targets, &constants, &root)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper around [load_project] that only takes the arguments that aren't
|
||||||
|
/// fully specified by systems
|
||||||
|
pub fn load_project(&self, root: DeclTree, reporter: &Reporter) -> ProjectTree {
|
||||||
|
let mut orc_files: Vec<VPath> = Vec::new();
|
||||||
|
find_all_orc_files([].borrow(), &mut orc_files, &root);
|
||||||
|
let entrypoints = (orc_files.into_iter()).map(|p| p.name_with_suffix(i!(str: "tree")).to_sym());
|
||||||
|
let tgt_loc = CodeOrigin::Gen(CodeGenInfo::no_details(sym!(facade::entrypoint)));
|
||||||
|
let constants = self.constants().unwrap_mod();
|
||||||
|
let targets = entrypoints.into_iter().map(|s| (s, tgt_loc.clone()));
|
||||||
|
let root = self.system_fs().combine(root).expect("System trees conflict with root");
|
||||||
|
load_project(&self.project_ctx(reporter), targets, &constants, &root)
|
||||||
|
}
|
||||||
|
|
||||||
/// Load a directory from the local file system as an Orchid project.
|
/// Load a directory from the local file system as an Orchid project.
|
||||||
/// File loading proceeds along import statements and ignores all files
|
/// File loading proceeds along import statements and ignores all files
|
||||||
/// not reachable from the specified file.
|
/// not reachable from the specified file.
|
||||||
pub fn load_main(
|
pub fn load_main(
|
||||||
&self,
|
&self,
|
||||||
dir: PathBuf,
|
dir: PathBuf,
|
||||||
target: Sym,
|
targets: impl IntoIterator<Item = Sym>,
|
||||||
) -> ProjectResult<ProjectTree> {
|
reporter: &Reporter,
|
||||||
let ctx = self.solution_ctx()?;
|
) -> ProjectTree {
|
||||||
let tgt_loc =
|
self.load_project_main(targets, self.make_dir_fs(dir), reporter)
|
||||||
CodeLocation::Gen(CodeGenInfo::no_details("facade::entrypoint"));
|
|
||||||
let root = self.make_dir_tree(dir.clone());
|
|
||||||
let targets = iter::once((target, tgt_loc));
|
|
||||||
let constants = self.constants().unwrap_mod();
|
|
||||||
load_solution(ctx, targets, &constants, &root)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load every orchid file in a directory
|
/// Load every orchid file in a directory
|
||||||
pub fn load_dir(&self, dir: PathBuf) -> ProjectResult<ProjectTree> {
|
pub fn load_dir(&self, dir: PathBuf, reporter: &Reporter) -> ProjectTree {
|
||||||
let ctx = self.solution_ctx()?;
|
self.load_project(self.make_dir_fs(dir), reporter)
|
||||||
let tgt_loc =
|
}
|
||||||
CodeLocation::Gen(CodeGenInfo::no_details("facade::entrypoint"));
|
|
||||||
let mut orc_files: Vec<VPath> = Vec::new();
|
/// Build a process by calling other utilities in [crate::facade]. A sort of
|
||||||
find_all_orc_files(&dir, &mut orc_files, Substack::Bottom);
|
/// facade over the facade. If you need a custom file system, consider
|
||||||
let root = self.make_dir_tree(dir.clone());
|
/// combining this with [Loader::load_project]. For usage with
|
||||||
let constants = self.constants().unwrap_mod();
|
/// [Loader::load_main] and [Loader::load_dir] we offer the shorthands
|
||||||
let targets = (orc_files.into_iter())
|
/// [Loader::proc_main] and [Loader::proc_dir].
|
||||||
.map(|p| (p.as_suffix_of(i("tree")).to_sym(), tgt_loc.clone()));
|
pub fn proc(
|
||||||
load_solution(ctx, targets, &constants, &root)
|
&'a self,
|
||||||
|
tree: ProjectTree,
|
||||||
|
check_refs: bool,
|
||||||
|
macro_limit: Option<usize>,
|
||||||
|
reporter: &Reporter,
|
||||||
|
) -> Process<'a> {
|
||||||
|
let mr = MacroRunner::new(&tree, macro_limit, reporter);
|
||||||
|
let pm_tree = mr.run_macros(tree, reporter);
|
||||||
|
let consts = merge_trees(pm_tree.all_consts(), self.systems(), reporter);
|
||||||
|
if check_refs {
|
||||||
|
validate_refs(consts.keys().cloned().collect(), reporter, &mut |sym, location| {
|
||||||
|
(consts.get(&sym).map(|nc| nc.value.clone()))
|
||||||
|
.ok_or_else(|| RunEnv::sym_not_found(sym, location))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Process::new(consts, self.handlers())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a project and process everything
|
||||||
|
pub fn proc_dir(
|
||||||
|
&'a self,
|
||||||
|
dir: PathBuf,
|
||||||
|
check_refs: bool,
|
||||||
|
macro_limit: Option<usize>,
|
||||||
|
reporter: &Reporter,
|
||||||
|
) -> Process<'a> {
|
||||||
|
self.proc(self.load_dir(dir.to_owned(), reporter), check_refs, macro_limit, reporter)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a project and process everything to load specific symbols
|
||||||
|
pub fn proc_main(
|
||||||
|
&'a self,
|
||||||
|
dir: PathBuf,
|
||||||
|
targets: impl IntoIterator<Item = Sym>,
|
||||||
|
check_refs: bool,
|
||||||
|
macro_limit: Option<usize>,
|
||||||
|
reporter: &Reporter,
|
||||||
|
) -> Process<'a> {
|
||||||
|
self.proc(self.load_main(dir.to_owned(), targets, reporter), check_refs, macro_limit, reporter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,24 +192,12 @@ impl<'a> Default for Loader<'a> {
|
|||||||
fn default() -> Self { Self::new() }
|
fn default() -> Self { Self::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_all_orc_files(
|
fn find_all_orc_files(path: &PathSlice, paths: &mut Vec<VPath>, vfs: &impl VirtFS) {
|
||||||
path: &Path,
|
match vfs.read(path) {
|
||||||
paths: &mut Vec<VPath>,
|
Err(_) => (),
|
||||||
stack: Substack<'_, Tok<String>>,
|
Ok(Loaded::Code(_)) => paths.push(path.to_vpath()),
|
||||||
) {
|
Ok(Loaded::Collection(items)) => items
|
||||||
assert!(path.exists(), "find_all_orc_files encountered missing path");
|
.iter()
|
||||||
if path.is_symlink() {
|
.for_each(|suffix| find_all_orc_files(&path.to_vpath().suffix([suffix.clone()]), paths, vfs)),
|
||||||
let path = unwrap_or!(fs::read_link(path).ok(); return);
|
|
||||||
find_all_orc_files(&path, paths, stack)
|
|
||||||
} else if path.is_file() {
|
|
||||||
if path.extension().and_then(|t| t.to_str()) == Some("orc") {
|
|
||||||
paths.push(VPath(stack.unreverse()))
|
|
||||||
}
|
|
||||||
} else if path.is_dir() {
|
|
||||||
let entries = unwrap_or!(path.read_dir().ok(); return);
|
|
||||||
for entry in entries.filter_map(Result::ok) {
|
|
||||||
let name = unwrap_or!(entry.file_name().into_string().ok(); return);
|
|
||||||
find_all_orc_files(&entry.path(), paths, stack.push(i(&name)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +1,69 @@
|
|||||||
|
//! Encapsulates the macro runner's scaffolding. Relies on a [ProjectTree]
|
||||||
|
//! loaded by the [super::loader::Loader]
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use crate::error::{ErrorPosition, ProjectError, ProjectErrorObj, ProjectResult, Reporter};
|
||||||
|
use crate::location::CodeOrigin;
|
||||||
use crate::error::{ProjectError, ProjectResult};
|
|
||||||
use crate::location::CodeLocation;
|
|
||||||
use crate::name::Sym;
|
|
||||||
use crate::parse::parsed;
|
use crate::parse::parsed;
|
||||||
use crate::pipeline::project::{
|
use crate::pipeline::project::{ItemKind, ProjItem, ProjectTree};
|
||||||
ConstReport, ProjectTree,
|
|
||||||
};
|
|
||||||
use crate::rule::repository::Repo;
|
use crate::rule::repository::Repo;
|
||||||
|
use crate::tree::TreeTransforms;
|
||||||
|
|
||||||
|
/// Encapsulates the macro repository and the constant list, and allows querying
|
||||||
|
/// for macro execution results
|
||||||
pub struct MacroRunner {
|
pub struct MacroRunner {
|
||||||
/// Optimized catalog of substitution rules
|
/// Optimized catalog of substitution rules
|
||||||
pub repo: Repo,
|
pub repo: Repo,
|
||||||
/// Runtime code containing macro invocations
|
/// Runtime code containing macro invocations
|
||||||
pub consts: HashMap<Sym, ConstReport>,
|
pub timeout: Option<usize>,
|
||||||
}
|
}
|
||||||
impl MacroRunner {
|
impl MacroRunner {
|
||||||
pub fn new(tree: &ProjectTree) -> ProjectResult<Self> {
|
/// Initialize a macro runner
|
||||||
|
pub fn new(tree: &ProjectTree, timeout: Option<usize>, reporter: &Reporter) -> Self {
|
||||||
let rules = tree.all_rules();
|
let rules = tree.all_rules();
|
||||||
let repo = Repo::new(rules).map_err(|(rule, e)| e.to_project(&rule))?;
|
let repo = Repo::new(rules, reporter);
|
||||||
Ok(Self { repo, consts: tree.all_consts().into_iter().collect() })
|
Self { repo, timeout }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_macros(
|
/// Process the macros in an expression.
|
||||||
&self,
|
pub fn process_expr(&self, expr: parsed::Expr) -> ProjectResult<parsed::Expr> {
|
||||||
timeout: Option<usize>,
|
match self.timeout {
|
||||||
) -> ProjectResult<HashMap<Sym, ConstReport>> {
|
None => Ok((self.repo.pass(&expr)).unwrap_or_else(|| expr.clone())),
|
||||||
let mut symbols = HashMap::new();
|
|
||||||
for (name, report) in self.consts.iter() {
|
|
||||||
let value = match timeout {
|
|
||||||
None => (self.repo.pass(&report.value))
|
|
||||||
.unwrap_or_else(|| report.value.clone()),
|
|
||||||
Some(limit) => {
|
Some(limit) => {
|
||||||
let (o, leftover_gas) = self.repo.long_step(&report.value, limit + 1);
|
let (o, leftover_gas) = self.repo.long_step(&expr, limit + 1);
|
||||||
match leftover_gas {
|
if 0 < leftover_gas {
|
||||||
1.. => o,
|
return Ok(o);
|
||||||
_ => {
|
}
|
||||||
let err = MacroTimeout {
|
Err(MacroTimeout { location: expr.range.origin(), limit }.pack())
|
||||||
location: CodeLocation::Source(report.range.clone()),
|
|
||||||
symbol: name.clone(),
|
|
||||||
limit,
|
|
||||||
};
|
|
||||||
return Err(err.pack());
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
|
||||||
};
|
|
||||||
symbols.insert(name.clone(), ConstReport { value, ..report.clone() });
|
|
||||||
}
|
}
|
||||||
Ok(symbols)
|
|
||||||
|
/// Run all macros in the project.
|
||||||
|
pub fn run_macros(&self, tree: ProjectTree, reporter: &Reporter) -> ProjectTree {
|
||||||
|
ProjectTree(tree.0.map_data(
|
||||||
|
|_, item| match &item.kind {
|
||||||
|
ItemKind::Const(c) => match self.process_expr(c.clone()) {
|
||||||
|
Ok(expr) => ProjItem { kind: ItemKind::Const(expr) },
|
||||||
|
Err(e) => {
|
||||||
|
reporter.report(e);
|
||||||
|
item
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => item,
|
||||||
|
},
|
||||||
|
|_, x| x,
|
||||||
|
|_, x| x,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Obtain an iterator that steps through the preprocessing of a constant
|
/// Obtain an iterator that steps through the preprocessing of a constant
|
||||||
/// for debugging macros
|
/// for debugging macros
|
||||||
pub fn step(&self, sym: Sym) -> impl Iterator<Item = parsed::Expr> + '_ {
|
pub fn step(&self, mut expr: parsed::Expr) -> impl Iterator<Item = parsed::Expr> + '_ {
|
||||||
let mut target =
|
|
||||||
self.consts.get(&sym).expect("Target not found").value.clone();
|
|
||||||
iter::from_fn(move || {
|
iter::from_fn(move || {
|
||||||
target = self.repo.step(&target)?;
|
expr = self.repo.step(&expr)?;
|
||||||
Some(target.clone())
|
Some(expr.clone())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,17 +71,32 @@ impl MacroRunner {
|
|||||||
/// Error raised when a macro runs too long
|
/// Error raised when a macro runs too long
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MacroTimeout {
|
pub struct MacroTimeout {
|
||||||
location: CodeLocation,
|
location: CodeOrigin,
|
||||||
symbol: Sym,
|
|
||||||
limit: usize,
|
limit: usize,
|
||||||
}
|
}
|
||||||
impl ProjectError for MacroTimeout {
|
impl ProjectError for MacroTimeout {
|
||||||
const DESCRIPTION: &'static str = "Macro execution has not halted";
|
const DESCRIPTION: &'static str = "Macro execution has not halted";
|
||||||
|
|
||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
let Self { symbol, limit, .. } = self;
|
let Self { limit, .. } = self;
|
||||||
format!("Macro processing in {symbol} took more than {limit} steps")
|
format!("Macro processing took more than {limit} steps")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn one_position(&self) -> CodeLocation { self.location.clone() }
|
fn one_position(&self) -> CodeOrigin { self.location.clone() }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MacroErrors(Vec<ProjectErrorObj>);
|
||||||
|
impl ProjectError for MacroErrors {
|
||||||
|
const DESCRIPTION: &'static str = "Errors occurred during macro execution";
|
||||||
|
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> + '_ {
|
||||||
|
self.0.iter().enumerate().flat_map(|(i, e)| {
|
||||||
|
e.positions().map(move |ep| ErrorPosition {
|
||||||
|
origin: ep.origin,
|
||||||
|
message: Some(match ep.message {
|
||||||
|
Some(msg) => format!("Error #{}: {}; {msg}", i + 1, e.message()),
|
||||||
|
None => format!("Error #{}: {}", i + 1, e.message()),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,27 @@
|
|||||||
|
//! Combine constants from [super::macro_runner::MacroRunner::run_macros] with
|
||||||
|
//! systems from [super::loader::Loader::systems]
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
use super::system::System;
|
use super::system::System;
|
||||||
use crate::error::ProjectResult;
|
use crate::error::Reporter;
|
||||||
|
use crate::foreign::inert::Inert;
|
||||||
|
use crate::foreign::to_clause::ToClause;
|
||||||
use crate::intermediate::ast_to_ir::ast_to_ir;
|
use crate::intermediate::ast_to_ir::ast_to_ir;
|
||||||
use crate::intermediate::ir_to_nort::ir_to_nort;
|
use crate::intermediate::ir_to_nort::ir_to_nort;
|
||||||
use crate::interpreter::nort;
|
use crate::interpreter::nort;
|
||||||
use crate::location::{CodeGenInfo, CodeLocation};
|
use crate::location::{CodeGenInfo, CodeLocation};
|
||||||
use crate::name::Sym;
|
use crate::name::{NameLike, Sym};
|
||||||
use crate::pipeline::project::ConstReport;
|
use crate::pipeline::project::ConstReport;
|
||||||
|
use crate::sym;
|
||||||
use crate::tree::{ModMemberRef, TreeTransforms};
|
use crate::tree::{ModMemberRef, TreeTransforms};
|
||||||
use crate::utils::unwrap_or::unwrap_or;
|
use crate::utils::unwrap_or::unwrap_or;
|
||||||
|
|
||||||
/// Equivalent of [crate::pipeline::project::ConstReport] for the interpreter's
|
/// Equivalent of [crate::pipeline::project::ConstReport] for the interpreter's
|
||||||
/// representation, [crate::interpreter::nort].
|
/// representation, [crate::interpreter::nort].
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct NortConst {
|
pub struct NortConst {
|
||||||
/// Comments associated with the constant which may affect its interpretation
|
/// Comments associated with the constant which may affect its interpretation
|
||||||
pub comments: Vec<Arc<String>>,
|
pub comments: Vec<Arc<String>>,
|
||||||
@@ -23,31 +30,40 @@ pub struct NortConst {
|
|||||||
/// Value assigned to the constant
|
/// Value assigned to the constant
|
||||||
pub value: nort::Expr,
|
pub value: nort::Expr,
|
||||||
}
|
}
|
||||||
|
impl NortConst {
|
||||||
|
/// Convert into NORT constant from AST constant
|
||||||
|
pub fn convert_from(value: ConstReport, reporter: &Reporter) -> NortConst {
|
||||||
|
let module = Sym::new(value.name.split_last().1[..].iter())
|
||||||
|
.expect("Constant names from source are at least 2 long");
|
||||||
|
let location = CodeLocation::new_src(value.range.clone(), value.name);
|
||||||
|
let nort = match ast_to_ir(value.value, value.range, module.clone()) {
|
||||||
|
Ok(ir) => ir_to_nort(&ir),
|
||||||
|
Err(e) => {
|
||||||
|
reporter.report(e);
|
||||||
|
Inert(0).to_expr(location.clone())
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Self { value: nort, location, comments: value.comments }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Combine a list of symbols loaded from source and the constant trees from
|
/// Combine a list of symbols loaded from source and the constant trees from
|
||||||
/// each system.
|
/// each system.
|
||||||
pub fn merge_trees<'a: 'b, 'b>(
|
pub fn merge_trees<'a: 'b, 'b>(
|
||||||
source: impl IntoIterator<Item = (Sym, ConstReport)> + 'b,
|
source: impl IntoIterator<Item = (Sym, ConstReport)>,
|
||||||
systems: impl IntoIterator<Item = &'b System<'a>> + 'b,
|
systems: impl IntoIterator<Item = &'b System<'a>> + 'b,
|
||||||
) -> ProjectResult<impl IntoIterator<Item = (Sym, NortConst)> + 'static> {
|
reporter: &Reporter,
|
||||||
|
) -> HashMap<Sym, NortConst> {
|
||||||
let mut out = HashMap::new();
|
let mut out = HashMap::new();
|
||||||
for (name, rep) in source {
|
for (name, rep) in source.into_iter() {
|
||||||
let ir = ast_to_ir(rep.value, name.clone())?;
|
out.insert(name.clone(), NortConst::convert_from(rep, reporter));
|
||||||
// if name == Sym::literal("tree::main::main") {
|
|
||||||
// panic!("{ir:?}");
|
|
||||||
// }
|
|
||||||
out.insert(name.clone(), NortConst {
|
|
||||||
value: ir_to_nort(&ir),
|
|
||||||
location: CodeLocation::Source(rep.range),
|
|
||||||
comments: rep.comments,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
for system in systems {
|
for system in systems {
|
||||||
let const_module = system.constants.unwrap_mod_ref();
|
let const_module = system.constants.unwrap_mod_ref();
|
||||||
const_module.search_all((), |stack, node, ()| {
|
const_module.search_all((), |stack, node, ()| {
|
||||||
let c = unwrap_or!(node => ModMemberRef::Item; return);
|
let c = unwrap_or!(node => ModMemberRef::Item; return);
|
||||||
let location = CodeLocation::Gen(CodeGenInfo::details(
|
let location = CodeLocation::new_gen(CodeGenInfo::details(
|
||||||
"constant from",
|
sym!(facade::merge_tree),
|
||||||
format!("system.name={}", system.name),
|
format!("system.name={}", system.name),
|
||||||
));
|
));
|
||||||
let value = c.clone().gen_nort(stack.clone(), location.clone());
|
let value = c.clone().gen_nort(stack.clone(), location.clone());
|
||||||
@@ -55,5 +71,5 @@ pub fn merge_trees<'a: 'b, 'b>(
|
|||||||
out.insert(Sym::new(stack.unreverse()).expect("root item is forbidden"), crep);
|
out.insert(Sym::new(stack.unreverse()).expect("root item is forbidden"), crep);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(out)
|
out
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
pub mod loader;
|
pub mod loader;
|
||||||
pub mod macro_runner;
|
pub mod macro_runner;
|
||||||
|
pub mod merge_trees;
|
||||||
pub mod process;
|
pub mod process;
|
||||||
pub mod system;
|
pub mod system;
|
||||||
pub mod merge_trees;
|
pub mod unbound_ref;
|
||||||
|
|||||||
@@ -1,111 +0,0 @@
|
|||||||
use std::iter;
|
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
|
||||||
use never::Never;
|
|
||||||
|
|
||||||
use super::process::Process;
|
|
||||||
use super::system::System;
|
|
||||||
use crate::error::{ErrorPosition, ProjectError, ProjectResult};
|
|
||||||
use crate::intermediate::ast_to_ir::ast_to_ir;
|
|
||||||
use crate::intermediate::ir_to_nort::ir_to_nort;
|
|
||||||
use crate::interpreter::handler::HandlerTable;
|
|
||||||
use crate::location::{CodeGenInfo, CodeLocation};
|
|
||||||
use crate::name::{Sym, VPath};
|
|
||||||
use crate::parse::parsed;
|
|
||||||
use crate::pipeline::project::{
|
|
||||||
collect_consts, collect_rules, ConstReport, ProjectTree,
|
|
||||||
};
|
|
||||||
use crate::rule::repository::Repo;
|
|
||||||
use crate::tree::ModMember;
|
|
||||||
|
|
||||||
/// Everything needed for macro execution, and constructing the process
|
|
||||||
pub struct PreMacro<'a> {
|
|
||||||
/// Optimized catalog of substitution rules
|
|
||||||
pub repo: Repo,
|
|
||||||
/// Runtime code containing macro invocations
|
|
||||||
pub consts: HashMap<Sym, (ConstReport, CodeLocation)>,
|
|
||||||
/// Libraries and plug-ins
|
|
||||||
pub systems: Vec<System<'a>>,
|
|
||||||
}
|
|
||||||
impl<'a> PreMacro<'a> {
|
|
||||||
/// Build a [PreMacro] from a source tree and system list
|
|
||||||
pub fn new(
|
|
||||||
tree: &ProjectTree,
|
|
||||||
systems: Vec<System<'a>>,
|
|
||||||
) -> ProjectResult<Self> {
|
|
||||||
Ok(Self {
|
|
||||||
repo,
|
|
||||||
consts: (consts.into_iter())
|
|
||||||
.map(|(name, expr)| {
|
|
||||||
let (ent, _) = (tree.0)
|
|
||||||
.walk1_ref(&[], &name.split_last().1[..], |_| true)
|
|
||||||
.expect("path sourced from symbol names");
|
|
||||||
let location = (ent.x.locations.first().cloned())
|
|
||||||
.unwrap_or_else(|| CodeLocation::Source(expr.value.range.clone()));
|
|
||||||
(name, (expr, location))
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
systems,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run all macros to termination or the optional timeout. If a timeout does
|
|
||||||
/// not occur, returns a process which can execute Orchid code
|
|
||||||
pub fn run_macros(
|
|
||||||
self,
|
|
||||||
timeout: Option<usize>,
|
|
||||||
) -> ProjectResult<Process<'a>> {
|
|
||||||
let Self { systems, repo, consts } = self;
|
|
||||||
for sys in systems.iter() {
|
|
||||||
let const_module = sys.constants.unwrap_mod_ref();
|
|
||||||
let _ = const_module.search_all((), &mut |path, module, ()| {
|
|
||||||
for (key, ent) in &module.entries {
|
|
||||||
if let ModMember::Item(c) = &ent.member {
|
|
||||||
let path = VPath::new(path.unreverse()).as_prefix_of(key.clone());
|
|
||||||
let cginfo = CodeGenInfo::details(
|
|
||||||
"constant from",
|
|
||||||
format!("system.name={}", sys.name),
|
|
||||||
);
|
|
||||||
symbols
|
|
||||||
.insert(path.to_sym(), c.gen_nort(CodeLocation::Gen(cginfo)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok::<(), Never>(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(Process {
|
|
||||||
symbols,
|
|
||||||
handlers: (systems.into_iter())
|
|
||||||
.fold(HandlerTable::new(), |tbl, sys| tbl.combine(sys.handlers)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Obtain an iterator that steps through the preprocessing of a constant
|
|
||||||
/// for debugging macros
|
|
||||||
pub fn step(&self, sym: Sym) -> impl Iterator<Item = parsed::Expr> + '_ {
|
|
||||||
let mut target =
|
|
||||||
self.consts.get(&sym).expect("Target not found").0.value.clone();
|
|
||||||
iter::from_fn(move || {
|
|
||||||
target = self.repo.step(&target)?;
|
|
||||||
Some(target.clone())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error raised when a macro runs too long
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct MacroTimeout {
|
|
||||||
location: CodeLocation,
|
|
||||||
symbol: Sym,
|
|
||||||
limit: usize,
|
|
||||||
}
|
|
||||||
impl ProjectError for MacroTimeout {
|
|
||||||
const DESCRIPTION: &'static str = "Macro execution has not halted";
|
|
||||||
|
|
||||||
fn message(&self) -> String {
|
|
||||||
let Self { symbol, limit, .. } = self;
|
|
||||||
format!("Macro processing in {symbol} took more than {limit} steps")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn one_position(&self) -> CodeLocation { self.location.clone() }
|
|
||||||
}
|
|
||||||
@@ -1,29 +1,31 @@
|
|||||||
|
//! Run Orchid commands in the context of the loaded environment. Either
|
||||||
|
//! returned by [super::loader::Loader::proc], or constructed manually from the
|
||||||
|
//! return value of [super::merge_trees::merge_trees] and
|
||||||
|
//! [super::loader::Loader::handlers].
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use super::merge_trees::NortConst;
|
use super::merge_trees::NortConst;
|
||||||
use crate::error::{ErrorPosition, ProjectError, ProjectResult};
|
use crate::interpreter::context::{Halt, RunEnv, RunParams};
|
||||||
use crate::interpreter::context::{Halt, RunContext};
|
|
||||||
use crate::interpreter::error::RunError;
|
use crate::interpreter::error::RunError;
|
||||||
use crate::interpreter::handler::{run_handler, HandlerTable};
|
use crate::interpreter::handler::HandlerTable;
|
||||||
use crate::interpreter::nort::{Clause, Expr};
|
use crate::interpreter::nort::Expr;
|
||||||
use crate::location::CodeLocation;
|
use crate::interpreter::run::run;
|
||||||
use crate::name::Sym;
|
use crate::name::Sym;
|
||||||
|
|
||||||
/// This struct ties the state of systems to loaded code, and allows to call
|
/// This struct ties the state of systems to loaded code, and allows to call
|
||||||
/// Orchid-defined functions
|
/// Orchid-defined functions
|
||||||
pub struct Process<'a> {
|
pub struct Process<'a>(RunEnv<'a>);
|
||||||
pub(crate) symbols: HashMap<Sym, Expr>,
|
|
||||||
pub(crate) handlers: HandlerTable<'a>,
|
|
||||||
}
|
|
||||||
impl<'a> Process<'a> {
|
impl<'a> Process<'a> {
|
||||||
/// Build a process from the return value of [crate::facade::merge_trees] and
|
/// Build a process from the return value of [crate::facade::merge_trees] and
|
||||||
pub fn new(
|
pub fn new(
|
||||||
consts: impl IntoIterator<Item = (Sym, NortConst)>,
|
consts: impl IntoIterator<Item = (Sym, NortConst)>,
|
||||||
handlers: HandlerTable<'a>,
|
handlers: HandlerTable<'a>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let symbols = consts.into_iter().map(|(k, v)| (k, v.value)).collect();
|
let symbols: HashMap<_, _> = consts.into_iter().map(|(k, v)| (k, v.value)).collect();
|
||||||
Self { handlers, symbols }
|
Self(RunEnv::new(handlers, move |sym, location| {
|
||||||
|
symbols.get(&sym).cloned().ok_or_else(|| RunEnv::sym_not_found(sym, location))
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the given command in this process. If gas is specified, at most as
|
/// Execute the given command in this process. If gas is specified, at most as
|
||||||
@@ -31,81 +33,7 @@ impl<'a> Process<'a> {
|
|||||||
///
|
///
|
||||||
/// This is useful to catch infinite loops or ensure that a tenant program
|
/// This is useful to catch infinite loops or ensure that a tenant program
|
||||||
/// yields
|
/// yields
|
||||||
pub fn run(
|
pub fn run(&self, prompt: Expr, gas: Option<usize>) -> Result<Halt, RunError<'_>> {
|
||||||
&mut self,
|
run(prompt, &self.0, &mut RunParams { stack: 1000, gas })
|
||||||
prompt: Expr,
|
|
||||||
gas: Option<usize>,
|
|
||||||
) -> Result<Halt, RunError> {
|
|
||||||
let ctx = RunContext { gas, symbols: &self.symbols, stack_size: 1000 };
|
|
||||||
run_handler(prompt, &mut self.handlers, ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find all unbound constant names in a symbol. This is often useful to
|
|
||||||
/// identify dynamic loading targets.
|
|
||||||
#[must_use]
|
|
||||||
pub fn unbound_refs(&self, key: Sym) -> Vec<(Sym, CodeLocation)> {
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
let sym = self.symbols.get(&key).expect("symbol must exist");
|
|
||||||
sym.search_all(&mut |s: &Expr| {
|
|
||||||
if let Clause::Constant(sym) = &*s.cls() {
|
|
||||||
if !self.symbols.contains_key(sym) {
|
|
||||||
errors.push((sym.clone(), s.location()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None::<()>
|
|
||||||
});
|
|
||||||
errors
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assert that the code contains no invalid constants. This ensures that,
|
|
||||||
/// unless [Clause::Constant]s are created procedurally,
|
|
||||||
/// a [crate::interpreter::error::RunError::MissingSymbol] cannot be produced
|
|
||||||
pub fn validate_refs(&self) -> ProjectResult<()> {
|
|
||||||
let mut errors = Vec::new();
|
|
||||||
for key in self.symbols.keys() {
|
|
||||||
errors.extend(self.unbound_refs(key.clone()).into_iter().map(
|
|
||||||
|(symbol, location)| MissingSymbol {
|
|
||||||
symbol,
|
|
||||||
location,
|
|
||||||
referrer: key.clone(),
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
match errors.is_empty() {
|
|
||||||
true => Ok(()),
|
|
||||||
false => Err(MissingSymbols { errors }.pack()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct MissingSymbol {
|
|
||||||
referrer: Sym,
|
|
||||||
location: CodeLocation,
|
|
||||||
symbol: Sym,
|
|
||||||
}
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct MissingSymbols {
|
|
||||||
errors: Vec<MissingSymbol>,
|
|
||||||
}
|
|
||||||
impl ProjectError for MissingSymbols {
|
|
||||||
const DESCRIPTION: &'static str = "A name not referring to a known symbol was found in the source after \
|
|
||||||
macro execution. This can either mean that a symbol name was mistyped, or \
|
|
||||||
that macro execution didn't correctly halt.";
|
|
||||||
|
|
||||||
fn message(&self) -> String {
|
|
||||||
format!(
|
|
||||||
"The following symbols do not exist:\n{}",
|
|
||||||
(self.errors.iter())
|
|
||||||
.map(|MissingSymbol { symbol, referrer, .. }| format!(
|
|
||||||
"{symbol} referenced in {referrer}"
|
|
||||||
))
|
|
||||||
.join("\n")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> {
|
|
||||||
(self.errors.iter())
|
|
||||||
.map(|i| ErrorPosition { location: i.location.clone(), message: None })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
|
//! Unified extension struct instances of which are catalogued by
|
||||||
|
//! [super::loader::Loader]. Language extensions must implement [IntoSystem].
|
||||||
|
|
||||||
use crate::error::{ErrorPosition, ProjectError};
|
use crate::error::{ErrorPosition, ProjectError};
|
||||||
use crate::gen::tree::ConstTree;
|
use crate::gen::tree::ConstTree;
|
||||||
use crate::interpreter::handler::HandlerTable;
|
use crate::interpreter::handler::HandlerTable;
|
||||||
use crate::name::VName;
|
use crate::name::VName;
|
||||||
use crate::parse::lex_plugin::LexerPlugin;
|
use crate::parse::lex_plugin::LexerPlugin;
|
||||||
use crate::parse::parse_plugin::ParseLinePlugin;
|
use crate::parse::parse_plugin::ParseLinePlugin;
|
||||||
use crate::pipeline::load_solution::Prelude;
|
use crate::pipeline::load_project::Prelude;
|
||||||
use crate::virt_fs::DeclTree;
|
use crate::virt_fs::DeclTree;
|
||||||
|
|
||||||
/// A description of every point where an external library can hook into Orchid.
|
/// A description of every point where an external library can hook into Orchid.
|
||||||
@@ -48,8 +51,7 @@ pub struct MissingSystemCode {
|
|||||||
referrer: VName,
|
referrer: VName,
|
||||||
}
|
}
|
||||||
impl ProjectError for MissingSystemCode {
|
impl ProjectError for MissingSystemCode {
|
||||||
const DESCRIPTION: &'static str =
|
const DESCRIPTION: &'static str = "A system tried to import a path that doesn't exist";
|
||||||
"A system tried to import a path that doesn't exist";
|
|
||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
format!(
|
format!(
|
||||||
"Path {} imported by {} is not defined by {} or any system before it",
|
"Path {} imported by {} is not defined by {} or any system before it",
|
||||||
@@ -61,8 +63,7 @@ impl ProjectError for MissingSystemCode {
|
|||||||
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> { [] }
|
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> { [] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for objects that can be converted into a [System] in the presence
|
/// Trait for objects that can be converted into a [System].
|
||||||
/// of an [Interner].
|
|
||||||
pub trait IntoSystem<'a> {
|
pub trait IntoSystem<'a> {
|
||||||
/// Convert this object into a system using an interner
|
/// Convert this object into a system using an interner
|
||||||
fn into_system(self) -> System<'a>;
|
fn into_system(self) -> System<'a>;
|
||||||
|
|||||||
94
src/facade/unbound_ref.rs
Normal file
94
src/facade/unbound_ref.rs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
//! Referencing a constant that doesn't exist is a runtime error in Orchid, even
|
||||||
|
//! though this common error condition is usually caused by faulty macro
|
||||||
|
//! execution. This module constains functions to detect and raise these errors
|
||||||
|
//! eagerly.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use hashbrown::HashSet;
|
||||||
|
use trait_set::trait_set;
|
||||||
|
|
||||||
|
use crate::error::{ProjectError, Reporter};
|
||||||
|
use crate::interpreter::nort::{Clause, Expr};
|
||||||
|
use crate::location::{CodeGenInfo, CodeLocation};
|
||||||
|
use crate::name::Sym;
|
||||||
|
use crate::sym;
|
||||||
|
|
||||||
|
/// Start with a symbol
|
||||||
|
pub fn unbound_refs_sym<E: SubError>(
|
||||||
|
symbol: Sym,
|
||||||
|
location: CodeLocation,
|
||||||
|
visited: &mut HashSet<Sym>,
|
||||||
|
load: &mut impl FnMut(Sym, CodeLocation) -> Result<Expr, E>,
|
||||||
|
reporter: &Reporter,
|
||||||
|
) {
|
||||||
|
if visited.insert(symbol.clone()) {
|
||||||
|
match load(symbol.clone(), location.clone()) {
|
||||||
|
Err(error) => reporter.report(MissingSymbol { symbol, location, error }.pack()),
|
||||||
|
Ok(expr) => unbound_refs_expr(expr, visited, load, reporter),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find all unbound constant names in a snippet. This is mostly useful to
|
||||||
|
/// detect macro errors.
|
||||||
|
pub fn unbound_refs_expr<E: SubError>(
|
||||||
|
expr: Expr,
|
||||||
|
visited: &mut HashSet<Sym>,
|
||||||
|
load: &mut impl FnMut(Sym, CodeLocation) -> Result<Expr, E>,
|
||||||
|
reporter: &Reporter,
|
||||||
|
) {
|
||||||
|
expr.search_all(&mut |s: &Expr| {
|
||||||
|
if let Clause::Constant(symbol) = &*s.cls_mut() {
|
||||||
|
unbound_refs_sym(symbol.clone(), s.location(), visited, load, reporter)
|
||||||
|
}
|
||||||
|
None::<()>
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assert that the code contains no invalid references that reference missing
|
||||||
|
/// symbols. [Clause::Constant]s can be created procedurally, so this isn't a
|
||||||
|
/// total guarantee, more of a convenience.
|
||||||
|
pub fn validate_refs<E: SubError>(
|
||||||
|
all_syms: HashSet<Sym>,
|
||||||
|
reporter: &Reporter,
|
||||||
|
load: &mut impl FnMut(Sym, CodeLocation) -> Result<Expr, E>,
|
||||||
|
) -> HashSet<Sym> {
|
||||||
|
let mut visited = HashSet::new();
|
||||||
|
for sym in all_syms {
|
||||||
|
let location = CodeLocation::new_gen(CodeGenInfo::no_details(sym!(orchidlang::validate_refs)));
|
||||||
|
unbound_refs_sym(sym, location, &mut visited, load, reporter);
|
||||||
|
}
|
||||||
|
visited
|
||||||
|
}
|
||||||
|
|
||||||
|
trait_set! {
|
||||||
|
/// Any error the reference walker can package into a [MissingSymbol]
|
||||||
|
pub trait SubError = fmt::Display + Clone + Send + Sync + 'static;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information about a reproject failure
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MissingSymbol<E: SubError> {
|
||||||
|
/// The error returned by the loader function. This is usually a ismple "not
|
||||||
|
/// found", but with unusual setups it might provide some useful info.
|
||||||
|
pub error: E,
|
||||||
|
/// Location of the first reference to the missing symbol.
|
||||||
|
pub location: CodeLocation,
|
||||||
|
/// The symbol in question
|
||||||
|
pub symbol: Sym,
|
||||||
|
}
|
||||||
|
impl<E: SubError> ProjectError for MissingSymbol<E> {
|
||||||
|
const DESCRIPTION: &'static str = "A name not referring to a known symbol was found in the source after \
|
||||||
|
macro execution. This can either mean that a symbol name was mistyped, or \
|
||||||
|
that macro execution didn't correctly halt.";
|
||||||
|
fn message(&self) -> String { format!("{}: {}", self.symbol, self.error) }
|
||||||
|
fn one_position(&self) -> crate::location::CodeOrigin { self.location.origin() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct MissingSymbols {
|
||||||
|
// errors: Vec<ErrorPosition>,
|
||||||
|
// }
|
||||||
|
// impl ProjectError for MissingSymbols {
|
||||||
|
// fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> {
|
||||||
|
// self.errors.iter().cloned() } }
|
||||||
@@ -1,65 +1,70 @@
|
|||||||
|
//! Adaptor trait to embed Rust values in Orchid expressions
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use never::Never;
|
use never::Never;
|
||||||
|
|
||||||
use super::error::{ExternError, ExternResult};
|
use super::error::{RTError, RTResult};
|
||||||
use crate::interpreter::context::RunContext;
|
use crate::interpreter::context::{RunEnv, RunParams};
|
||||||
use crate::interpreter::error::RunError;
|
|
||||||
use crate::interpreter::nort;
|
use crate::interpreter::nort;
|
||||||
use crate::location::{CodeLocation, SourceRange};
|
use crate::location::{CodeLocation, SourceRange};
|
||||||
use crate::name::NameLike;
|
use crate::name::NameLike;
|
||||||
|
use crate::parse::lexer::Lexeme;
|
||||||
use crate::parse::parsed;
|
use crate::parse::parsed;
|
||||||
use crate::utils::ddispatch::{request, Request, Responder};
|
use crate::utils::ddispatch::{request, Request, Responder};
|
||||||
|
|
||||||
/// Information returned by [Atomic::run].
|
/// Information returned by [Atomic::run].
|
||||||
pub enum AtomicReturn {
|
pub enum AtomicReturn {
|
||||||
/// No work was done. If the atom takes an argument, it can be provided now
|
/// No work was done. If the atom takes an argument, it can be provided now
|
||||||
Inert(nort::Clause),
|
Inert(Atom),
|
||||||
/// Work was done, returns new clause and consumed gas. 1 gas is already
|
/// Work was done, returns new clause and consumed gas. 1 gas is already
|
||||||
/// consumed by the virtual call, so nonzero values indicate expensive
|
/// consumed by the virtual call, so nonzero values indicate expensive
|
||||||
/// operations.
|
/// operations.
|
||||||
Change(usize, nort::Clause),
|
Change(usize, nort::Clause),
|
||||||
}
|
}
|
||||||
impl AtomicReturn {
|
impl AtomicReturn {
|
||||||
/// Report indicating that the value is inert
|
/// Report indicating that the value is inert. The result here is always [Ok],
|
||||||
pub fn inert<T: Atomic, E>(this: T) -> Result<Self, E> {
|
/// it's meant to match the return type of [Atomic::run]
|
||||||
Ok(Self::Inert(this.atom_cls()))
|
#[allow(clippy::unnecessary_wraps)]
|
||||||
}
|
pub fn inert<T: Atomic, E>(this: T) -> Result<Self, E> { Ok(Self::Inert(Atom::new(this))) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returned by [Atomic::run]
|
/// Returned by [Atomic::run]
|
||||||
pub type AtomicResult = Result<AtomicReturn, RunError>;
|
pub type AtomicResult = RTResult<AtomicReturn>;
|
||||||
|
|
||||||
/// General error produced when a non-function [Atom] is applied to something as
|
/// General error produced when a non-function [Atom] is applied to something as
|
||||||
/// a function.
|
/// a function.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NotAFunction(pub nort::Expr);
|
pub struct NotAFunction(pub nort::Expr);
|
||||||
impl ExternError for NotAFunction {}
|
impl RTError for NotAFunction {}
|
||||||
impl Display for NotAFunction {
|
impl fmt::Display for NotAFunction {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{:?} is not a function", self.0)
|
write!(f, "{:?} is not a function", self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about a function call presented to an external function
|
/// Information about a function call presented to an external function
|
||||||
pub struct CallData<'a> {
|
pub struct CallData<'a, 'b> {
|
||||||
/// Location of the function expression
|
/// Location of the function expression
|
||||||
pub location: CodeLocation,
|
pub location: CodeLocation,
|
||||||
/// The argument the function was called on. Functions are curried
|
/// The argument the function was called on. Functions are curried
|
||||||
pub arg: nort::Expr,
|
pub arg: nort::Expr,
|
||||||
/// Information relating to this interpreter run
|
/// Globally available information such as the list of all constants
|
||||||
pub ctx: RunContext<'a>,
|
pub env: &'a RunEnv<'b>,
|
||||||
|
/// Resource limits and other details specific to this interpreter run
|
||||||
|
pub params: &'a mut RunParams,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about a normalization run presented to an atom
|
/// Information about a normalization run presented to an atom
|
||||||
#[derive(Clone)]
|
pub struct RunData<'a, 'b> {
|
||||||
pub struct RunData<'a> {
|
|
||||||
/// Location of the atom
|
/// Location of the atom
|
||||||
pub location: CodeLocation,
|
pub location: CodeLocation,
|
||||||
/// Information about the execution
|
/// Globally available information such as the list of all constants
|
||||||
pub ctx: RunContext<'a>,
|
pub env: &'a RunEnv<'b>,
|
||||||
|
/// Resource limits and other details specific to this interpreter run
|
||||||
|
pub params: &'a mut RunParams,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Functionality the interpreter needs to handle a value
|
/// Functionality the interpreter needs to handle a value
|
||||||
@@ -67,15 +72,15 @@ pub struct RunData<'a> {
|
|||||||
/// # Lifecycle methods
|
/// # Lifecycle methods
|
||||||
///
|
///
|
||||||
/// Atomics expose the methods [Atomic::redirect], [Atomic::run],
|
/// Atomics expose the methods [Atomic::redirect], [Atomic::run],
|
||||||
/// [Atomic::apply] and [Atomic::apply_ref] to interact with the interpreter.
|
/// [Atomic::apply] and [Atomic::apply_mut] to interact with the interpreter.
|
||||||
/// The interpreter first tries to call `redirect` to find a subexpression to
|
/// The interpreter first tries to call `redirect` to find a subexpression to
|
||||||
/// normalize. If it returns `None` or the subexpression is inert, `run` is
|
/// normalize. If it returns `None` or the subexpression is inert, `run` is
|
||||||
/// called. `run` takes ownership of the value and returns a new one.
|
/// called. `run` takes ownership of the value and returns a new one.
|
||||||
///
|
///
|
||||||
/// If `run` indicated in its return value that the result is inert and the atom
|
/// If `run` indicated in its return value that the result is inert and the atom
|
||||||
/// is in the position of a function, `apply` or `apply_ref` is called depending
|
/// is in the position of a function, `apply` or `apply_mut` is called depending
|
||||||
/// upon whether the atom is referenced elsewhere. `apply` falls back to
|
/// upon whether the atom is referenced elsewhere. `apply` falls back to
|
||||||
/// `apply_ref` so implementing it is considered an optimization to avoid
|
/// `apply_mut` so implementing it is considered an optimization to avoid
|
||||||
/// excessive copying.
|
/// excessive copying.
|
||||||
///
|
///
|
||||||
/// Atoms don't generally have to be copyable because clauses are refcounted in
|
/// Atoms don't generally have to be copyable because clauses are refcounted in
|
||||||
@@ -83,7 +88,7 @@ pub struct RunData<'a> {
|
|||||||
/// and apply them as functions to multiple different arguments so atoms that
|
/// and apply them as functions to multiple different arguments so atoms that
|
||||||
/// represent functions have to support application by-ref without consuming the
|
/// represent functions have to support application by-ref without consuming the
|
||||||
/// function itself.
|
/// function itself.
|
||||||
pub trait Atomic: Any + Debug + Responder + Send
|
pub trait Atomic: Any + fmt::Debug + Responder + Send
|
||||||
where Self: 'static
|
where Self: 'static
|
||||||
{
|
{
|
||||||
/// Casts this value to [Any] so that its original value can be salvaged
|
/// Casts this value to [Any] so that its original value can be salvaged
|
||||||
@@ -99,6 +104,12 @@ where Self: 'static
|
|||||||
/// See [Atomic::as_any], exactly the same but for references
|
/// See [Atomic::as_any], exactly the same but for references
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn as_any_ref(&self) -> &dyn Any;
|
fn as_any_ref(&self) -> &dyn Any;
|
||||||
|
/// Print the atom's type name. Should only ever be implemented as
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
/// fn type_name(&self) -> &'static str { std::any::type_name::<Self>() }
|
||||||
|
/// ```
|
||||||
|
fn type_name(&self) -> &'static str;
|
||||||
|
|
||||||
/// Returns a reference to a possible expression held inside the atom which
|
/// Returns a reference to a possible expression held inside the atom which
|
||||||
/// can be reduced. For an overview of the lifecycle see [Atomic]
|
/// can be reduced. For an overview of the lifecycle see [Atomic]
|
||||||
@@ -112,22 +123,20 @@ where Self: 'static
|
|||||||
fn run(self: Box<Self>, run: RunData) -> AtomicResult;
|
fn run(self: Box<Self>, run: RunData) -> AtomicResult;
|
||||||
|
|
||||||
/// Combine the function with an argument to produce a new clause. Falls back
|
/// Combine the function with an argument to produce a new clause. Falls back
|
||||||
/// to [Atomic::apply_ref] by default.
|
/// to [Atomic::apply_mut] by default.
|
||||||
///
|
///
|
||||||
/// For an overview of the lifecycle see [Atomic]
|
/// For an overview of the lifecycle see [Atomic]
|
||||||
fn apply(self: Box<Self>, call: CallData) -> ExternResult<nort::Clause> {
|
fn apply(mut self: Box<Self>, call: CallData) -> RTResult<nort::Clause> { self.apply_mut(call) }
|
||||||
self.apply_ref(call)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Combine the function with an argument to produce a new clause
|
/// Combine the function with an argument to produce a new clause
|
||||||
///
|
///
|
||||||
/// For an overview of the lifecycle see [Atomic]
|
/// For an overview of the lifecycle see [Atomic]
|
||||||
fn apply_ref(&self, call: CallData) -> ExternResult<nort::Clause>;
|
fn apply_mut(&mut self, call: CallData) -> RTResult<nort::Clause>;
|
||||||
|
|
||||||
/// Must return true for atoms parsed from identical source.
|
/// Must return true for atoms parsed from identical source.
|
||||||
/// If the atom cannot be parsed from source, it can safely be ignored
|
/// If the atom cannot be parsed from source, it can safely be ignored
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
fn parser_eq(&self, other: &dyn Any) -> bool { false }
|
fn parser_eq(&self, other: &dyn Atomic) -> bool { false }
|
||||||
|
|
||||||
/// Wrap the atom in a clause to be placed in an [AtomicResult].
|
/// Wrap the atom in a clause to be placed in an [AtomicResult].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@@ -139,29 +148,38 @@ where Self: 'static
|
|||||||
/// Shorthand for `self.atom_cls().to_inst()`
|
/// Shorthand for `self.atom_cls().to_inst()`
|
||||||
fn atom_clsi(self) -> nort::ClauseInst
|
fn atom_clsi(self) -> nort::ClauseInst
|
||||||
where Self: Sized {
|
where Self: Sized {
|
||||||
self.atom_cls().to_inst()
|
self.atom_cls().into_inst()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrap the atom in a new expression instance to be placed in a tree
|
/// Wrap the atom in a new expression instance to be placed in a tree
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn atom_expr(self, location: CodeLocation) -> nort::Expr
|
fn atom_expr(self, location: CodeLocation) -> nort::Expr
|
||||||
where Self: Sized {
|
where Self: Sized {
|
||||||
self.atom_clsi().to_expr(location)
|
self.atom_clsi().into_expr(location)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrap the atom in a clause to be placed in a [sourcefile::FileEntry].
|
/// Wrap the atom in a clause to be placed in a
|
||||||
|
/// [crate::parse::parsed::SourceLine].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn ast_cls(self) -> parsed::Clause
|
fn ast_cls(self) -> parsed::Clause
|
||||||
where Self: Sized + Clone {
|
where Self: Sized + Clone {
|
||||||
parsed::Clause::Atom(AtomGenerator::cloner(self))
|
parsed::Clause::Atom(AtomGenerator::cloner(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrap the atom in an expression to be placed in a [sourcefile::FileEntry].
|
/// Wrap the atom in an expression to be placed in a
|
||||||
|
/// [crate::parse::parsed::SourceLine].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn ast_exp<N: NameLike>(self, range: SourceRange) -> parsed::Expr
|
fn ast_exp<N: NameLike>(self, range: SourceRange) -> parsed::Expr
|
||||||
where Self: Sized + Clone {
|
where Self: Sized + Clone {
|
||||||
self.ast_cls().into_expr(range)
|
self.ast_cls().into_expr(range)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wrap this atomic value in a lexeme. This means that the atom will
|
||||||
|
/// participate in macro reproject, so it must implement [Atomic::parser_eq].
|
||||||
|
fn lexeme(self) -> Lexeme
|
||||||
|
where Self: Sized + Clone {
|
||||||
|
Lexeme::Atom(AtomGenerator::cloner(self))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A struct for generating any number of [Atom]s. Since atoms aren't Clone,
|
/// A struct for generating any number of [Atom]s. Since atoms aren't Clone,
|
||||||
@@ -170,9 +188,7 @@ where Self: 'static
|
|||||||
pub struct AtomGenerator(Arc<dyn Fn() -> Atom + Send + Sync>);
|
pub struct AtomGenerator(Arc<dyn Fn() -> Atom + Send + Sync>);
|
||||||
impl AtomGenerator {
|
impl AtomGenerator {
|
||||||
/// Use a factory function to create any number of atoms
|
/// Use a factory function to create any number of atoms
|
||||||
pub fn new(f: impl Fn() -> Atom + Send + Sync + 'static) -> Self {
|
pub fn new(f: impl Fn() -> Atom + Send + Sync + 'static) -> Self { Self(Arc::new(f)) }
|
||||||
Self(Arc::new(f))
|
|
||||||
}
|
|
||||||
/// Clone a representative atom when called
|
/// Clone a representative atom when called
|
||||||
pub fn cloner(atom: impl Atomic + Clone) -> Self {
|
pub fn cloner(atom: impl Atomic + Clone) -> Self {
|
||||||
let lock = Mutex::new(atom);
|
let lock = Mutex::new(atom);
|
||||||
@@ -181,26 +197,22 @@ impl AtomGenerator {
|
|||||||
/// Generate an atom
|
/// Generate an atom
|
||||||
pub fn run(&self) -> Atom { self.0() }
|
pub fn run(&self) -> Atom { self.0() }
|
||||||
}
|
}
|
||||||
impl Debug for AtomGenerator {
|
impl fmt::Debug for AtomGenerator {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.run()) }
|
||||||
write!(f, "{:?}", self.run())
|
|
||||||
}
|
}
|
||||||
|
impl PartialEq for AtomGenerator {
|
||||||
|
fn eq(&self, other: &Self) -> bool { self.run().0.parser_eq(&*other.run().0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a black box unit of code with its own normalization steps.
|
/// Represents a black box unit of data with its own normalization steps.
|
||||||
/// Typically [ExternFn] will produce an [Atom] when applied to a [Clause],
|
/// Typically Rust functions integrated with [super::fn_bridge::xfn] will
|
||||||
/// this [Atom] will then forward `run` calls to the argument until it becomes
|
/// produce and consume [Atom]s to represent both raw data, pending
|
||||||
/// inert at which point the [Atom] will validate and process the argument,
|
/// computational tasks, and curried partial calls awaiting their next argument.
|
||||||
/// returning a different [Atom] intended for processing by external code, a new
|
|
||||||
/// [ExternFn] to capture an additional argument, or an Orchid expression
|
|
||||||
/// to pass control back to the interpreter.
|
|
||||||
pub struct Atom(pub Box<dyn Atomic>);
|
pub struct Atom(pub Box<dyn Atomic>);
|
||||||
impl Atom {
|
impl Atom {
|
||||||
/// Wrap an [Atomic] in a type-erased box
|
/// Wrap an [Atomic] in a type-erased box
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new<T: 'static + Atomic>(data: T) -> Self {
|
pub fn new<T: 'static + Atomic>(data: T) -> Self { Self(Box::new(data) as Box<dyn Atomic>) }
|
||||||
Self(Box::new(data) as Box<dyn Atomic>)
|
|
||||||
}
|
|
||||||
/// Get the contained data
|
/// Get the contained data
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn data(&self) -> &dyn Atomic { self.0.as_ref() as &dyn Atomic }
|
pub fn data(&self) -> &dyn Atomic { self.0.as_ref() as &dyn Atomic }
|
||||||
@@ -213,7 +225,7 @@ impl Atom {
|
|||||||
*self.0.as_any().downcast().expect("Type mismatch on Atom::cast")
|
*self.0.as_any().downcast().expect("Type mismatch on Atom::cast")
|
||||||
}
|
}
|
||||||
/// Normalize the contained data
|
/// Normalize the contained data
|
||||||
pub fn run(self, run: RunData) -> AtomicResult { self.0.run(run) }
|
pub fn run(self, run: RunData<'_, '_>) -> AtomicResult { self.0.run(run) }
|
||||||
/// Request a delegate from the encapsulated data
|
/// Request a delegate from the encapsulated data
|
||||||
pub fn request<T: 'static>(&self) -> Option<T> { request(self.0.as_ref()) }
|
pub fn request<T: 'static>(&self) -> Option<T> { request(self.0.as_ref()) }
|
||||||
/// Downcast the atom to a concrete atomic type, or return the original atom
|
/// Downcast the atom to a concrete atomic type, or return the original atom
|
||||||
@@ -225,23 +237,15 @@ impl Atom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Downcast an atom by reference
|
/// Downcast an atom by reference
|
||||||
pub fn downcast_ref<T: Atomic>(&self) -> Option<&T> {
|
pub fn downcast_ref<T: Atomic>(&self) -> Option<&T> { self.0.as_any_ref().downcast_ref() }
|
||||||
self.0.as_any_ref().downcast_ref()
|
|
||||||
}
|
|
||||||
/// Combine the function with an argument to produce a new clause
|
/// Combine the function with an argument to produce a new clause
|
||||||
pub fn apply(self, call: CallData) -> ExternResult<nort::Clause> {
|
pub fn apply(self, call: CallData) -> RTResult<nort::Clause> { self.0.apply(call) }
|
||||||
self.0.apply(call)
|
|
||||||
}
|
|
||||||
/// Combine the function with an argument to produce a new clause
|
/// Combine the function with an argument to produce a new clause
|
||||||
pub fn apply_ref(&self, call: CallData) -> ExternResult<nort::Clause> {
|
pub fn apply_mut(&mut self, call: CallData) -> RTResult<nort::Clause> { self.0.apply_mut(call) }
|
||||||
self.0.apply_ref(call)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Atom {
|
impl fmt::Debug for Atom {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self.data()) }
|
||||||
write!(f, "{:?}", self.data())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Responder for Never {
|
impl Responder for Never {
|
||||||
@@ -250,9 +254,8 @@ impl Responder for Never {
|
|||||||
impl Atomic for Never {
|
impl Atomic for Never {
|
||||||
fn as_any(self: Box<Self>) -> Box<dyn Any> { match *self {} }
|
fn as_any(self: Box<Self>) -> Box<dyn Any> { match *self {} }
|
||||||
fn as_any_ref(&self) -> &dyn Any { match *self {} }
|
fn as_any_ref(&self) -> &dyn Any { match *self {} }
|
||||||
|
fn type_name(&self) -> &'static str { match *self {} }
|
||||||
fn redirect(&mut self) -> Option<&mut nort::Expr> { match *self {} }
|
fn redirect(&mut self) -> Option<&mut nort::Expr> { match *self {} }
|
||||||
fn run(self: Box<Self>, _: RunData) -> AtomicResult { match *self {} }
|
fn run(self: Box<Self>, _: RunData) -> AtomicResult { match *self {} }
|
||||||
fn apply_ref(&self, _: CallData) -> ExternResult<nort::Clause> {
|
fn apply_mut(&mut self, _: CallData) -> RTResult<nort::Clause> { match *self {} }
|
||||||
match *self {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
//! Automated wrappers to make working with CPS commands easier.
|
//! Automated wrappers to make working with CPS commands easier.
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt;
|
||||||
|
|
||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
use super::atom::{
|
use super::atom::{Atomic, AtomicResult, AtomicReturn, CallData, NotAFunction, RunData};
|
||||||
Atomic, AtomicResult, AtomicReturn, CallData, NotAFunction, RunData,
|
use super::error::{RTError, RTResult};
|
||||||
};
|
|
||||||
use super::error::{ExternError, ExternResult};
|
|
||||||
use crate::interpreter::nort::{Clause, Expr};
|
use crate::interpreter::nort::{Clause, Expr};
|
||||||
use crate::location::CodeLocation;
|
use crate::location::CodeLocation;
|
||||||
use crate::utils::ddispatch::{Request, Responder};
|
use crate::utils::ddispatch::{Request, Responder};
|
||||||
@@ -15,9 +13,9 @@ use crate::utils::pure_seq::pushed_ref;
|
|||||||
|
|
||||||
trait_set! {
|
trait_set! {
|
||||||
/// A "well behaved" type that can be used as payload in a CPS box
|
/// A "well behaved" type that can be used as payload in a CPS box
|
||||||
pub trait CPSPayload = Clone + Debug + Send + 'static;
|
pub trait CPSPayload = Clone + fmt::Debug + Send + 'static;
|
||||||
/// A function to handle a CPS box with a specific payload
|
/// A function to handle a CPS box with a specific payload
|
||||||
pub trait CPSHandler<T: CPSPayload> = FnMut(&T, &Expr) -> ExternResult<Expr>;
|
pub trait CPSHandler<T: CPSPayload> = FnMut(&T, &Expr) -> RTResult<Expr>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An Orchid Atom value encapsulating a payload and continuation points
|
/// An Orchid Atom value encapsulating a payload and continuation points
|
||||||
@@ -64,9 +62,9 @@ impl<T: CPSPayload> CPSBox<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_applicable(&self, err_loc: &CodeLocation) -> ExternResult<()> {
|
fn assert_applicable(&self, err_loc: &CodeLocation) -> RTResult<()> {
|
||||||
match self.argc {
|
match self.argc {
|
||||||
0 => Err(NotAFunction(self.clone().atom_expr(err_loc.clone())).rc()),
|
0 => Err(NotAFunction(self.clone().atom_expr(err_loc.clone())).pack()),
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,18 +75,17 @@ impl<T: CPSPayload> Responder for CPSBox<T> {
|
|||||||
impl<T: CPSPayload> Atomic for CPSBox<T> {
|
impl<T: CPSPayload> Atomic for CPSBox<T> {
|
||||||
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
|
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
|
||||||
fn as_any_ref(&self) -> &dyn std::any::Any { self }
|
fn as_any_ref(&self) -> &dyn std::any::Any { self }
|
||||||
fn parser_eq(&self, _: &dyn std::any::Any) -> bool { false }
|
fn type_name(&self) -> &'static str { std::any::type_name::<Self>() }
|
||||||
|
fn parser_eq(&self, _: &dyn Atomic) -> bool { false }
|
||||||
fn redirect(&mut self) -> Option<&mut Expr> { None }
|
fn redirect(&mut self) -> Option<&mut Expr> { None }
|
||||||
fn run(self: Box<Self>, _: RunData) -> AtomicResult {
|
fn run(self: Box<Self>, _: RunData) -> AtomicResult { AtomicReturn::inert(*self) }
|
||||||
AtomicReturn::inert(*self)
|
fn apply(mut self: Box<Self>, call: CallData) -> RTResult<Clause> {
|
||||||
}
|
|
||||||
fn apply(mut self: Box<Self>, call: CallData) -> ExternResult<Clause> {
|
|
||||||
self.assert_applicable(&call.location)?;
|
self.assert_applicable(&call.location)?;
|
||||||
self.argc -= 1;
|
self.argc -= 1;
|
||||||
self.continuations.push(call.arg);
|
self.continuations.push(call.arg);
|
||||||
Ok(self.atom_cls())
|
Ok(self.atom_cls())
|
||||||
}
|
}
|
||||||
fn apply_ref(&self, call: CallData) -> ExternResult<Clause> {
|
fn apply_mut(&mut self, call: CallData) -> RTResult<Clause> {
|
||||||
self.assert_applicable(&call.location)?;
|
self.assert_applicable(&call.location)?;
|
||||||
let new = Self {
|
let new = Self {
|
||||||
argc: self.argc - 1,
|
argc: self.argc - 1,
|
||||||
|
|||||||
@@ -1,34 +1,38 @@
|
|||||||
|
//! Errors produced by the interpreter
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use dyn_clone::DynClone;
|
use dyn_clone::DynClone;
|
||||||
|
|
||||||
|
use crate::error::ProjectErrorObj;
|
||||||
use crate::location::CodeLocation;
|
use crate::location::CodeLocation;
|
||||||
|
|
||||||
/// Errors produced by external code
|
/// Errors produced by external code when runtime-enforced assertions are
|
||||||
pub trait ExternError: Display + Send + Sync + DynClone {
|
/// violated.
|
||||||
|
pub trait RTError: fmt::Display + Send + Sync + DynClone {
|
||||||
/// Convert into trait object
|
/// Convert into trait object
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn rc(self) -> ExternErrorObj
|
fn pack(self) -> RTErrorObj
|
||||||
where Self: 'static + Sized {
|
where Self: 'static + Sized {
|
||||||
Arc::new(self)
|
Arc::new(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for dyn ExternError {
|
impl fmt::Debug for dyn RTError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ExternError({self})") }
|
||||||
write!(f, "ExternError({self})")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for dyn ExternError {}
|
impl Error for dyn RTError {}
|
||||||
|
|
||||||
|
impl RTError for ProjectErrorObj {}
|
||||||
|
|
||||||
/// An error produced by Rust code called form Orchid. The error is type-erased.
|
/// An error produced by Rust code called form Orchid. The error is type-erased.
|
||||||
pub type ExternErrorObj = Arc<dyn ExternError>;
|
pub type RTErrorObj = Arc<dyn RTError>;
|
||||||
|
|
||||||
/// A result produced by Rust code called from Orchid.
|
/// A result produced by Rust code called from Orchid.
|
||||||
pub type ExternResult<T> = Result<T, ExternErrorObj>;
|
pub type RTResult<T> = Result<T, RTErrorObj>;
|
||||||
|
|
||||||
/// Some expectation (usually about the argument types of a function) did not
|
/// Some expectation (usually about the argument types of a function) did not
|
||||||
/// hold.
|
/// hold.
|
||||||
@@ -42,30 +46,22 @@ pub struct AssertionError {
|
|||||||
impl AssertionError {
|
impl AssertionError {
|
||||||
/// Construct, upcast and wrap in a Result that never succeeds for easy
|
/// Construct, upcast and wrap in a Result that never succeeds for easy
|
||||||
/// short-circuiting
|
/// short-circuiting
|
||||||
pub fn fail<T>(
|
pub fn fail<T>(location: CodeLocation, message: &'static str, details: String) -> RTResult<T> {
|
||||||
location: CodeLocation,
|
|
||||||
message: &'static str,
|
|
||||||
details: String,
|
|
||||||
) -> ExternResult<T> {
|
|
||||||
Err(Self::ext(location, message, details))
|
Err(Self::ext(location, message, details))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct and upcast to [ExternError]
|
/// Construct and upcast to [RTErrorObj]
|
||||||
pub fn ext(
|
pub fn ext(location: CodeLocation, message: &'static str, details: String) -> RTErrorObj {
|
||||||
location: CodeLocation,
|
Self { location, message, details }.pack()
|
||||||
message: &'static str,
|
|
||||||
details: String,
|
|
||||||
) -> ExternErrorObj {
|
|
||||||
Self { location, message, details }.rc()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for AssertionError {
|
impl fmt::Display for AssertionError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "Error: expected {}", self.message)?;
|
write!(f, "Error: expected {}", self.message)?;
|
||||||
write!(f, " at {}", self.location)?;
|
write!(f, " at {}", self.location)?;
|
||||||
write!(f, " details: {}", self.details)
|
write!(f, " details: {}", self.details)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExternError for AssertionError {}
|
impl RTError for AssertionError {}
|
||||||
|
|||||||
@@ -1,26 +1,27 @@
|
|||||||
|
//! Insert Rust functions in Orchid code almost seamlessly
|
||||||
|
|
||||||
use std::any::{Any, TypeId};
|
use std::any::{Any, TypeId};
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use intern_all::{i, Tok};
|
use intern_all::{i, Tok};
|
||||||
|
|
||||||
use super::atom::{Atomic, AtomicResult, AtomicReturn, CallData, RunData};
|
use super::atom::{Atomic, AtomicResult, AtomicReturn, CallData, RunData};
|
||||||
use super::error::ExternResult;
|
use super::error::RTResult;
|
||||||
use super::to_clause::ToClause;
|
use super::to_clause::ToClause;
|
||||||
use super::try_from_expr::TryFromExpr;
|
use super::try_from_expr::TryFromExpr;
|
||||||
use crate::interpreter::nort::{Clause, Expr};
|
use crate::interpreter::nort::{Clause, Expr};
|
||||||
use crate::utils::ddispatch::Responder;
|
use crate::utils::ddispatch::Responder;
|
||||||
|
|
||||||
/// Return a unary lambda wrapped in this struct to take an additional argument
|
/// Return a unary lambda wrapped in this struct to take an additional argument
|
||||||
/// in a function passed to Orchid through a member of the [super::xfn_1ary]
|
/// in a function passed to Orchid through [super::fn_bridge::xfn].
|
||||||
/// family.
|
|
||||||
///
|
///
|
||||||
/// Container for a unary [FnOnce] that uniquely states the argument and return
|
/// Container for a unary [FnOnce] that uniquely states the argument and return
|
||||||
/// type. Rust functions are never overloaded, but inexplicably the [Fn] traits
|
/// type. Rust functions are never overloaded, but inexplicably the [Fn] traits
|
||||||
/// take the argument tuple as a generic parameter which means that it cannot
|
/// take the argument tuple as a generic parameter which means that it cannot
|
||||||
/// be a unique dispatch target.
|
/// be a unique dispatch target.
|
||||||
///
|
///
|
||||||
/// If the function takes an instance of [Lazy], it will contain the expression
|
/// If the function takes an instance of [Thunk], it will contain the expression
|
||||||
/// the function was applied to without any specific normalization. If it takes
|
/// the function was applied to without any specific normalization. If it takes
|
||||||
/// any other type, the argument will be fully normalized and cast using the
|
/// any other type, the argument will be fully normalized and cast using the
|
||||||
/// type's [TryFromExpr] impl.
|
/// type's [TryFromExpr] impl.
|
||||||
@@ -45,11 +46,11 @@ impl<T, U, F: Clone> Clone for Param<T, U, F> {
|
|||||||
Self { name: self.name.clone(), data: self.data.clone(), _t: PhantomData, _u: PhantomData }
|
Self { name: self.name.clone(), data: self.data.clone(), _t: PhantomData, _u: PhantomData }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T, U, F> Display for Param<T, U, F> {
|
impl<T, U, F> fmt::Display for Param<T, U, F> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&self.name) }
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.name) }
|
||||||
}
|
}
|
||||||
impl<T, U, F> Debug for Param<T, U, F> {
|
impl<T, U, F> fmt::Debug for Param<T, U, F> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_tuple("Param").field(&*self.name).finish()
|
f.debug_tuple("Param").field(&*self.name).finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +61,7 @@ impl<T, U, F> Debug for Param<T, U, F> {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Thunk(pub Expr);
|
pub struct Thunk(pub Expr);
|
||||||
impl TryFromExpr for Thunk {
|
impl TryFromExpr for Thunk {
|
||||||
fn from_expr(expr: Expr) -> ExternResult<Self> { Ok(Thunk(expr)) }
|
fn from_expr(expr: Expr) -> RTResult<Self> { Ok(Thunk(expr)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FnMiddleStage<T, U, F> {
|
struct FnMiddleStage<T, U, F> {
|
||||||
@@ -71,8 +72,8 @@ struct FnMiddleStage<T, U, F> {
|
|||||||
impl<T, U, F: Clone> Clone for FnMiddleStage<T, U, F> {
|
impl<T, U, F: Clone> Clone for FnMiddleStage<T, U, F> {
|
||||||
fn clone(&self) -> Self { Self { arg: self.arg.clone(), f: self.f.clone() } }
|
fn clone(&self) -> Self { Self { arg: self.arg.clone(), f: self.f.clone() } }
|
||||||
}
|
}
|
||||||
impl<T, U, F> Debug for FnMiddleStage<T, U, F> {
|
impl<T, U, F> fmt::Debug for FnMiddleStage<T, U, F> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "FnMiddleStage({} {})", self.f, self.arg)
|
write!(f, "FnMiddleStage({} {})", self.f, self.arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,6 +86,7 @@ impl<
|
|||||||
{
|
{
|
||||||
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
|
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
|
||||||
fn as_any_ref(&self) -> &dyn std::any::Any { self }
|
fn as_any_ref(&self) -> &dyn std::any::Any { self }
|
||||||
|
fn type_name(&self) -> &'static str { std::any::type_name::<Self>() }
|
||||||
fn redirect(&mut self) -> Option<&mut Expr> {
|
fn redirect(&mut self) -> Option<&mut Expr> {
|
||||||
// this should be ctfe'd
|
// this should be ctfe'd
|
||||||
(TypeId::of::<T>() != TypeId::of::<Thunk>()).then_some(&mut self.arg)
|
(TypeId::of::<T>() != TypeId::of::<Thunk>()).then_some(&mut self.arg)
|
||||||
@@ -93,7 +95,7 @@ impl<
|
|||||||
let Self { arg, f: Param { data: f, .. } } = *self;
|
let Self { arg, f: Param { data: f, .. } } = *self;
|
||||||
Ok(AtomicReturn::Change(0, f(arg.downcast()?).to_clause(r.location)))
|
Ok(AtomicReturn::Change(0, f(arg.downcast()?).to_clause(r.location)))
|
||||||
}
|
}
|
||||||
fn apply_ref(&self, _: CallData) -> ExternResult<Clause> { panic!("Atom should have decayed") }
|
fn apply_mut(&mut self, _: CallData) -> RTResult<Clause> { panic!("Atom should have decayed") }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U, F> Responder for Param<T, U, F> {}
|
impl<T, U, F> Responder for Param<T, U, F> {}
|
||||||
@@ -106,12 +108,13 @@ impl<
|
|||||||
{
|
{
|
||||||
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
|
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
|
||||||
fn as_any_ref(&self) -> &dyn std::any::Any { self }
|
fn as_any_ref(&self) -> &dyn std::any::Any { self }
|
||||||
|
fn type_name(&self) -> &'static str { std::any::type_name::<Self>() }
|
||||||
fn redirect(&mut self) -> Option<&mut Expr> { None }
|
fn redirect(&mut self) -> Option<&mut Expr> { None }
|
||||||
fn run(self: Box<Self>, _: RunData) -> AtomicResult { AtomicReturn::inert(*self) }
|
fn run(self: Box<Self>, _: RunData) -> AtomicResult { AtomicReturn::inert(*self) }
|
||||||
fn apply_ref(&self, call: CallData) -> ExternResult<Clause> {
|
fn apply_mut(&mut self, call: CallData) -> RTResult<Clause> {
|
||||||
Ok(FnMiddleStage { arg: call.arg, f: self.clone() }.atom_cls())
|
Ok(FnMiddleStage { arg: call.arg, f: self.clone() }.atom_cls())
|
||||||
}
|
}
|
||||||
fn apply(self: Box<Self>, call: CallData) -> ExternResult<Clause> {
|
fn apply(self: Box<Self>, call: CallData) -> RTResult<Clause> {
|
||||||
Ok(FnMiddleStage { arg: call.arg, f: *self }.atom_cls())
|
Ok(FnMiddleStage { arg: call.arg, f: *self }.atom_cls())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +137,7 @@ pub fn xfn<const N: usize, Argv, Ret>(
|
|||||||
/// - the return type must implement [ToClause]
|
/// - the return type must implement [ToClause]
|
||||||
///
|
///
|
||||||
/// Take [Thunk] to consume the argument as-is, without normalization.
|
/// Take [Thunk] to consume the argument as-is, without normalization.
|
||||||
pub trait Xfn<const N: usize, Argv, Ret>: Clone + 'static {
|
pub trait Xfn<const N: usize, Argv, Ret>: Clone + Send + 'static {
|
||||||
/// Convert Rust type to Orchid function, given a name for logging
|
/// Convert Rust type to Orchid function, given a name for logging
|
||||||
fn to_atomic(self, name: Tok<String>) -> impl Atomic + Clone;
|
fn to_atomic(self, name: Tok<String>) -> impl Atomic + Clone;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,169 +0,0 @@
|
|||||||
use std::any::Any;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use super::atom::{Atom, Atomic, AtomicResult, CallData, RunData};
|
|
||||||
use super::error::{ExternErrorObj, ExternResult};
|
|
||||||
use super::process::Unstable;
|
|
||||||
use super::to_clause::ToClause;
|
|
||||||
use crate::gen::tpl;
|
|
||||||
use crate::gen::traits::Gen;
|
|
||||||
use crate::interpreter::error::RunError;
|
|
||||||
use crate::interpreter::gen_nort::nort_gen;
|
|
||||||
use crate::interpreter::nort::{Clause, Expr};
|
|
||||||
use crate::location::CodeLocation;
|
|
||||||
use crate::utils::clonable_iter::Clonable;
|
|
||||||
use crate::utils::ddispatch::Responder;
|
|
||||||
|
|
||||||
impl<T: ToClause> ToClause for Option<T> {
|
|
||||||
fn to_clause(self, location: CodeLocation) -> Clause {
|
|
||||||
let ctx = nort_gen(location.clone());
|
|
||||||
match self {
|
|
||||||
None => tpl::C("std::option::none").template(ctx, []),
|
|
||||||
Some(t) => tpl::A(tpl::C("std::option::some"), tpl::Slot)
|
|
||||||
.template(ctx, [t.to_clause(location)]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ToClause, U: ToClause> ToClause for Result<T, U> {
|
|
||||||
fn to_clause(self, location: CodeLocation) -> Clause {
|
|
||||||
let ctx = nort_gen(location.clone());
|
|
||||||
match self {
|
|
||||||
Ok(t) => tpl::A(tpl::C("std::result::ok"), tpl::Slot)
|
|
||||||
.template(ctx, [t.to_clause(location)]),
|
|
||||||
Err(e) => tpl::A(tpl::C("std::result::err"), tpl::Slot)
|
|
||||||
.template(ctx, [e.to_clause(location)]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PendingError(ExternErrorObj);
|
|
||||||
impl Responder for PendingError {}
|
|
||||||
impl Debug for PendingError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "PendingError({})", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Atomic for PendingError {
|
|
||||||
fn as_any(self: Box<Self>) -> Box<dyn Any> { self }
|
|
||||||
fn as_any_ref(&self) -> &dyn Any { self }
|
|
||||||
fn redirect(&mut self) -> Option<&mut Expr> { None }
|
|
||||||
fn run(self: Box<Self>, _: RunData) -> AtomicResult {
|
|
||||||
Err(RunError::Extern(self.0))
|
|
||||||
}
|
|
||||||
fn apply_ref(&self, _: CallData) -> ExternResult<Clause> {
|
|
||||||
panic!("This atom decays instantly")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ToClause> ToClause for ExternResult<T> {
|
|
||||||
fn to_clause(self, location: CodeLocation) -> Clause {
|
|
||||||
match self {
|
|
||||||
Err(e) => PendingError(e).atom_cls(),
|
|
||||||
Ok(t) => t.to_clause(location),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ListGen<I>(Clonable<I>)
|
|
||||||
where
|
|
||||||
I: Iterator + Send,
|
|
||||||
I::Item: ToClause + Send;
|
|
||||||
impl<I> Clone for ListGen<I>
|
|
||||||
where
|
|
||||||
I: Iterator + Send,
|
|
||||||
I::Item: ToClause + Send,
|
|
||||||
{
|
|
||||||
fn clone(&self) -> Self { Self(self.0.clone()) }
|
|
||||||
}
|
|
||||||
impl<I> ToClause for ListGen<I>
|
|
||||||
where
|
|
||||||
I: Iterator + Send + 'static,
|
|
||||||
I::Item: ToClause + Clone + Send,
|
|
||||||
{
|
|
||||||
fn to_clause(mut self, location: CodeLocation) -> Clause {
|
|
||||||
let ctx = nort_gen(location.clone());
|
|
||||||
match self.0.next() {
|
|
||||||
None => tpl::C("std::lit::end").template(ctx, []),
|
|
||||||
Some(val) => {
|
|
||||||
let atom = Unstable::new(|run| self.to_clause(run.location));
|
|
||||||
tpl::a2(tpl::C("std::lit::cons"), tpl::Slot, tpl::V(atom))
|
|
||||||
.template(ctx, [val.to_clause(location)])
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert an iterator into a lazy-evaluated Orchid list.
|
|
||||||
pub fn list<I>(items: I) -> impl ToClause
|
|
||||||
where
|
|
||||||
I: IntoIterator + Clone + Send + Sync + 'static,
|
|
||||||
I::IntoIter: Send,
|
|
||||||
I::Item: ToClause + Clone + Send,
|
|
||||||
{
|
|
||||||
Unstable::new(move |RunData { location, .. }| {
|
|
||||||
ListGen(Clonable::new(
|
|
||||||
items.clone().into_iter().map(move |t| t.to_clsi(location.clone())),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ToClause + Clone + Send + Sync + 'static> ToClause for Vec<T> {
|
|
||||||
fn to_clause(self, location: CodeLocation) -> Clause {
|
|
||||||
list(self).to_clause(location)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToClause for Atom {
|
|
||||||
fn to_clause(self, _: CodeLocation) -> Clause { Clause::Atom(self) }
|
|
||||||
}
|
|
||||||
|
|
||||||
mod tuple_impls {
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::ToClause;
|
|
||||||
use crate::foreign::atom::Atomic;
|
|
||||||
use crate::foreign::error::AssertionError;
|
|
||||||
use crate::foreign::implementations::ExternResult;
|
|
||||||
use crate::foreign::inert::Inert;
|
|
||||||
use crate::foreign::try_from_expr::TryFromExpr;
|
|
||||||
use crate::interpreter::nort::{Clause, Expr};
|
|
||||||
use crate::libs::std::tuple::Tuple;
|
|
||||||
use crate::location::CodeLocation;
|
|
||||||
|
|
||||||
macro_rules! gen_tuple_impl {
|
|
||||||
( ($($T:ident)*) ($($t:ident)*)) => {
|
|
||||||
impl<$($T: ToClause),*> ToClause for ($($T,)*) {
|
|
||||||
fn to_clause(self, location: CodeLocation) -> Clause {
|
|
||||||
let ($($t,)*) = self;
|
|
||||||
Inert(Tuple(Arc::new(vec![
|
|
||||||
$($t.to_expr(location.clone()),)*
|
|
||||||
]))).atom_cls()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<$($T: TryFromExpr),*> TryFromExpr for ($($T,)*) {
|
|
||||||
fn from_expr(ex: Expr) -> ExternResult<Self> {
|
|
||||||
let Inert(Tuple(slice)) = ex.clone().downcast()?;
|
|
||||||
match &slice[..] {
|
|
||||||
[$($t),*] => Ok(($($t.clone().downcast()?,)*)),
|
|
||||||
_ => AssertionError::fail(ex.location(), "Tuple length mismatch", format!("{ex}"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
gen_tuple_impl!((A)(a));
|
|
||||||
gen_tuple_impl!((A B) (a b));
|
|
||||||
gen_tuple_impl!((A B C) (a b c));
|
|
||||||
gen_tuple_impl!((A B C D) (a b c d));
|
|
||||||
gen_tuple_impl!((A B C D E) (a b c d e));
|
|
||||||
gen_tuple_impl!((A B C D E F) (a b c d e f));
|
|
||||||
gen_tuple_impl!((A B C D E F G) (a b c d e f g));
|
|
||||||
gen_tuple_impl!((A B C D E F G H) (a b c d e f g h));
|
|
||||||
gen_tuple_impl!((A B C D E F G H I) (a b c d e f g h i));
|
|
||||||
gen_tuple_impl!((A B C D E F G H I J) (a b c d e f g h i j));
|
|
||||||
gen_tuple_impl!((A B C D E F G H I J K) (a b c d e f g h i j k));
|
|
||||||
gen_tuple_impl!((A B C D E F G H I J K L) (a b c d e f g h i j k l));
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
|
//! An [Atomic] that wraps inert data.
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use ordered_float::NotNan;
|
use ordered_float::NotNan;
|
||||||
|
|
||||||
use super::atom::{
|
use super::atom::{Atom, Atomic, AtomicResult, AtomicReturn, CallData, NotAFunction, RunData};
|
||||||
Atom, Atomic, AtomicResult, AtomicReturn, CallData, NotAFunction, RunData,
|
use super::error::{RTError, RTResult};
|
||||||
};
|
|
||||||
use super::error::{ExternError, ExternResult};
|
|
||||||
use super::try_from_expr::TryFromExpr;
|
use super::try_from_expr::TryFromExpr;
|
||||||
use crate::foreign::error::AssertionError;
|
use crate::foreign::error::AssertionError;
|
||||||
use crate::interpreter::nort::{Clause, Expr};
|
use crate::interpreter::nort::{Clause, Expr};
|
||||||
@@ -17,11 +17,10 @@ use crate::utils::ddispatch::{Request, Responder};
|
|||||||
|
|
||||||
/// A proxy trait that implements [Atomic] for blobs of data in Rust code that
|
/// A proxy trait that implements [Atomic] for blobs of data in Rust code that
|
||||||
/// cannot be processed and always report inert. Since these are expected to be
|
/// cannot be processed and always report inert. Since these are expected to be
|
||||||
/// parameters of functions defined with [define_fn] it also automatically
|
/// parameters of Rust functions it also automatically implements [TryFromExpr]
|
||||||
/// implements [TryFromExpr] so that a conversion doesn't have to be
|
/// so that `Inert<MyType>` arguments can be parsed directly.
|
||||||
/// provided in argument lists.
|
pub trait InertPayload: fmt::Debug + Clone + Send + 'static {
|
||||||
pub trait InertPayload: Debug + Clone + Send + 'static {
|
/// Typename to be shown in the error when a conversion from [Expr] fails
|
||||||
/// Typename to be shown in the error when a conversion from [ExprInst] fails
|
|
||||||
///
|
///
|
||||||
/// This will default to `type_name::<Self>()` when it becomes stable
|
/// This will default to `type_name::<Self>()` when it becomes stable
|
||||||
const TYPE_STR: &'static str;
|
const TYPE_STR: &'static str;
|
||||||
@@ -37,7 +36,8 @@ pub trait InertPayload: Debug + Clone + Send + 'static {
|
|||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// fn strict_eq(&self, other: &Self) -> bool { self == other }
|
/// fn strict_eq(&self, other: &Self) -> bool { self == other }
|
||||||
/// ```
|
/// ```
|
||||||
fn strict_eq(&self, _: &Self) -> bool { false }
|
#[allow(unused_variables)]
|
||||||
|
fn strict_eq(&self, other: &Self) -> bool { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An atom that stores a value and rejects all interpreter interactions. It is
|
/// An atom that stores a value and rejects all interpreter interactions. It is
|
||||||
@@ -61,42 +61,36 @@ impl<T: InertPayload> DerefMut for Inert<T> {
|
|||||||
|
|
||||||
impl<T: InertPayload> Responder for Inert<T> {
|
impl<T: InertPayload> Responder for Inert<T> {
|
||||||
fn respond(&self, mut request: Request) {
|
fn respond(&self, mut request: Request) {
|
||||||
if request.can_serve::<T>() {
|
if request.can_serve::<T>() { request.serve(self.0.clone()) } else { self.0.respond(request) }
|
||||||
request.serve(self.0.clone())
|
|
||||||
} else {
|
|
||||||
self.0.respond(request)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: InertPayload> Atomic for Inert<T> {
|
impl<T: InertPayload> Atomic for Inert<T> {
|
||||||
fn as_any(self: Box<Self>) -> Box<dyn Any> { self }
|
fn as_any(self: Box<Self>) -> Box<dyn Any> { self }
|
||||||
fn as_any_ref(&self) -> &dyn Any { self }
|
fn as_any_ref(&self) -> &dyn Any { self }
|
||||||
|
fn type_name(&self) -> &'static str { std::any::type_name::<Self>() }
|
||||||
|
|
||||||
fn redirect(&mut self) -> Option<&mut Expr> { None }
|
fn redirect(&mut self) -> Option<&mut Expr> { None }
|
||||||
fn run(self: Box<Self>, _: RunData) -> AtomicResult {
|
fn run(self: Box<Self>, _: RunData) -> AtomicResult { AtomicReturn::inert(*self) }
|
||||||
AtomicReturn::inert(*self)
|
fn apply_mut(&mut self, call: CallData) -> RTResult<Clause> {
|
||||||
|
Err(NotAFunction(self.clone().atom_expr(call.location)).pack())
|
||||||
}
|
}
|
||||||
fn apply_ref(&self, call: CallData) -> ExternResult<Clause> {
|
fn parser_eq(&self, other: &dyn Atomic) -> bool {
|
||||||
Err(NotAFunction(self.clone().atom_expr(call.location)).rc())
|
other.as_any_ref().downcast_ref::<Self>().map_or(false, |other| self.0.strict_eq(&other.0))
|
||||||
}
|
|
||||||
fn parser_eq(&self, other: &dyn Any) -> bool {
|
|
||||||
(other.downcast_ref::<Self>())
|
|
||||||
.map_or(false, |other| self.0.strict_eq(&other.0))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: InertPayload> TryFromExpr for Inert<T> {
|
impl<T: InertPayload> TryFromExpr for Inert<T> {
|
||||||
fn from_expr(expr: Expr) -> ExternResult<Self> {
|
fn from_expr(expr: Expr) -> RTResult<Self> {
|
||||||
let Expr { clause, location } = expr;
|
let Expr { clause, location } = expr;
|
||||||
match clause.try_unwrap() {
|
match clause.try_unwrap() {
|
||||||
Ok(Clause::Atom(at)) => at.try_downcast::<Self>().map_err(|a| {
|
Ok(Clause::Atom(at)) => at
|
||||||
AssertionError::ext(location, T::TYPE_STR, format!("{a:?}"))
|
.try_downcast::<Self>()
|
||||||
}),
|
.map_err(|a| AssertionError::ext(location, T::TYPE_STR, format!("{a:?}"))),
|
||||||
Err(inst) => match &*inst.cls() {
|
Err(inst) => match &*inst.cls_mut() {
|
||||||
Clause::Atom(at) =>
|
Clause::Atom(at) => at
|
||||||
at.downcast_ref::<Self>().cloned().ok_or_else(|| {
|
.downcast_ref::<Self>()
|
||||||
AssertionError::ext(location, T::TYPE_STR, format!("{inst}"))
|
.cloned()
|
||||||
}),
|
.ok_or_else(|| AssertionError::ext(location, T::TYPE_STR, format!("{inst}"))),
|
||||||
cls => AssertionError::fail(location, "atom", format!("{cls}")),
|
cls => AssertionError::fail(location, "atom", format!("{cls}")),
|
||||||
},
|
},
|
||||||
Ok(cls) => AssertionError::fail(location, "atom", format!("{cls}")),
|
Ok(cls) => AssertionError::fail(location, "atom", format!("{cls}")),
|
||||||
@@ -104,10 +98,8 @@ impl<T: InertPayload> TryFromExpr for Inert<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: InertPayload + Display> Display for Inert<T> {
|
impl<T: InertPayload + fmt::Display> fmt::Display for Inert<T> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) }
|
||||||
write!(f, "{}", self.0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InertPayload for bool {
|
impl InertPayload for bool {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ pub mod atom;
|
|||||||
pub mod cps_box;
|
pub mod cps_box;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod fn_bridge;
|
pub mod fn_bridge;
|
||||||
pub mod implementations;
|
|
||||||
pub mod inert;
|
pub mod inert;
|
||||||
pub mod process;
|
pub mod process;
|
||||||
pub mod to_clause;
|
pub mod to_clause;
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
use std::fmt::Debug;
|
//! An [Atomic] implementor that runs a callback and turns into the return
|
||||||
|
//! value. Useful to generate expressions that depend on values in the
|
||||||
|
//! interpreter's context, and to defer the generation of expensive
|
||||||
|
//! subexpressions
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
use super::atom::{Atomic, AtomicReturn, CallData, RunData};
|
use super::atom::{Atomic, AtomicResult, AtomicReturn, CallData, RunData};
|
||||||
use super::error::ExternResult;
|
use super::error::RTResult;
|
||||||
use super::to_clause::ToClause;
|
use super::to_clause::ToClause;
|
||||||
use crate::interpreter::nort::{Clause, Expr};
|
use crate::interpreter::nort::{Clause, Expr};
|
||||||
use crate::utils::ddispatch::Responder;
|
use crate::utils::ddispatch::Responder;
|
||||||
@@ -16,21 +20,20 @@ impl<F: FnOnce(RunData) -> R + Send + 'static, R: ToClause> Unstable<F> {
|
|||||||
pub const fn new(f: F) -> Self { Self(f) }
|
pub const fn new(f: F) -> Self { Self(f) }
|
||||||
}
|
}
|
||||||
impl<F> Responder for Unstable<F> {}
|
impl<F> Responder for Unstable<F> {}
|
||||||
impl<F> Debug for Unstable<F> {
|
impl<F> fmt::Debug for Unstable<F> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Unstable").finish_non_exhaustive()
|
f.debug_struct("Unstable").finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<F: FnOnce(RunData) -> R + Send + 'static, R: ToClause> Atomic
|
impl<F: FnOnce(RunData) -> R + Send + 'static, R: ToClause> Atomic for Unstable<F> {
|
||||||
for Unstable<F>
|
|
||||||
{
|
|
||||||
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
|
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
|
||||||
fn as_any_ref(&self) -> &dyn std::any::Any { self }
|
fn as_any_ref(&self) -> &dyn std::any::Any { self }
|
||||||
fn apply_ref(&self, _: CallData) -> ExternResult<Clause> {
|
fn type_name(&self) -> &'static str { std::any::type_name::<Self>() }
|
||||||
panic!("This atom decays instantly")
|
|
||||||
}
|
fn apply_mut(&mut self, _: CallData) -> RTResult<Clause> { panic!("This atom decays instantly") }
|
||||||
fn run(self: Box<Self>, run: RunData) -> super::atom::AtomicResult {
|
fn run(self: Box<Self>, run: RunData) -> AtomicResult {
|
||||||
let clause = self.0(run.clone()).to_clause(run.location.clone());
|
let loc = run.location.clone();
|
||||||
|
let clause = self.0(run).to_clause(loc);
|
||||||
Ok(AtomicReturn::Change(0, clause))
|
Ok(AtomicReturn::Change(0, clause))
|
||||||
}
|
}
|
||||||
fn redirect(&mut self) -> Option<&mut Expr> { None }
|
fn redirect(&mut self) -> Option<&mut Expr> { None }
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
use super::atom::Atomic;
|
//! Conversions from Rust values to Orchid expressions. Many APIs and
|
||||||
|
//! [super::fn_bridge] in particular use this to automatically convert values on
|
||||||
|
//! the boundary. The opposite conversion is [super::try_from_expr::TryFromExpr]
|
||||||
|
|
||||||
|
use super::atom::{Atomic, RunData};
|
||||||
|
use super::process::Unstable;
|
||||||
|
use crate::gen::tpl;
|
||||||
|
use crate::gen::traits::Gen;
|
||||||
|
use crate::interpreter::gen_nort::nort_gen;
|
||||||
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
|
use crate::interpreter::nort::{Clause, ClauseInst, Expr};
|
||||||
use crate::location::CodeLocation;
|
use crate::location::CodeLocation;
|
||||||
|
use crate::utils::clonable_iter::Clonable;
|
||||||
|
|
||||||
/// A trait for things that are infallibly convertible to [ClauseInst]. These
|
/// A trait for things that are infallibly convertible to [ClauseInst]. These
|
||||||
/// types can be returned by callbacks passed to the [super::xfn_1ary] family of
|
/// types can be returned by callbacks passed to [super::fn_bridge::xfn].
|
||||||
/// functions.
|
|
||||||
pub trait ToClause: Sized {
|
pub trait ToClause: Sized {
|
||||||
/// Convert this value to a [Clause]. If your value can only be directly
|
/// Convert this value to a [Clause]. If your value can only be directly
|
||||||
/// converted to a [ClauseInst], you can call `ClauseInst::to_clause` to
|
/// converted to a [ClauseInst], you can call `ClauseInst::to_clause` to
|
||||||
@@ -29,15 +37,166 @@ impl ToClause for Clause {
|
|||||||
fn to_clause(self, _: CodeLocation) -> Clause { self }
|
fn to_clause(self, _: CodeLocation) -> Clause { self }
|
||||||
}
|
}
|
||||||
impl ToClause for ClauseInst {
|
impl ToClause for ClauseInst {
|
||||||
fn to_clause(self, _: CodeLocation) -> Clause {
|
fn to_clause(self, _: CodeLocation) -> Clause { self.into_cls() }
|
||||||
self.into_cls()
|
|
||||||
}
|
|
||||||
fn to_clsi(self, _: CodeLocation) -> ClauseInst { self }
|
fn to_clsi(self, _: CodeLocation) -> ClauseInst { self }
|
||||||
}
|
}
|
||||||
impl ToClause for Expr {
|
impl ToClause for Expr {
|
||||||
fn to_clause(self, location: CodeLocation) -> Clause {
|
fn to_clause(self, location: CodeLocation) -> Clause { self.clause.to_clause(location) }
|
||||||
self.clause.to_clause(location)
|
|
||||||
}
|
|
||||||
fn to_clsi(self, _: CodeLocation) -> ClauseInst { self.clause }
|
fn to_clsi(self, _: CodeLocation) -> ClauseInst { self.clause }
|
||||||
fn to_expr(self, _: CodeLocation) -> Expr { self }
|
fn to_expr(self, _: CodeLocation) -> Expr { self }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ListGen<I>(Clonable<I>)
|
||||||
|
where
|
||||||
|
I: Iterator + Send,
|
||||||
|
I::Item: ToClause + Send;
|
||||||
|
impl<I> Clone for ListGen<I>
|
||||||
|
where
|
||||||
|
I: Iterator + Send,
|
||||||
|
I::Item: ToClause + Send,
|
||||||
|
{
|
||||||
|
fn clone(&self) -> Self { Self(self.0.clone()) }
|
||||||
|
}
|
||||||
|
impl<I> ToClause for ListGen<I>
|
||||||
|
where
|
||||||
|
I: Iterator + Send + 'static,
|
||||||
|
I::Item: ToClause + Clone + Send,
|
||||||
|
{
|
||||||
|
fn to_clause(mut self, location: CodeLocation) -> Clause {
|
||||||
|
let ctx = nort_gen(location.clone());
|
||||||
|
match self.0.next() {
|
||||||
|
None => tpl::C("std::list::end").template(ctx, []),
|
||||||
|
Some(val) => {
|
||||||
|
let atom = Unstable::new(|run| self.to_clause(run.location));
|
||||||
|
tpl::a2(tpl::C("std::list::cons"), tpl::Slot, tpl::V(atom))
|
||||||
|
.template(ctx, [val.to_clause(location)])
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert an iterator into a lazy-evaluated Orchid list.
|
||||||
|
pub fn list<I>(items: I) -> impl ToClause
|
||||||
|
where
|
||||||
|
I: IntoIterator + Clone + Send + Sync + 'static,
|
||||||
|
I::IntoIter: Send,
|
||||||
|
I::Item: ToClause + Clone + Send,
|
||||||
|
{
|
||||||
|
Unstable::new(move |RunData { location, .. }| {
|
||||||
|
ListGen(Clonable::new(items.clone().into_iter().map(move |t| t.to_clsi(location.clone()))))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
mod implementations {
|
||||||
|
use std::any::Any;
|
||||||
|
use std::fmt;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use super::{list, ToClause};
|
||||||
|
use crate::foreign::atom::{Atom, Atomic, AtomicResult, CallData, RunData};
|
||||||
|
use crate::foreign::error::{AssertionError, RTErrorObj, RTResult};
|
||||||
|
use crate::foreign::inert::Inert;
|
||||||
|
use crate::foreign::try_from_expr::TryFromExpr;
|
||||||
|
use crate::gen::tpl;
|
||||||
|
use crate::gen::traits::Gen;
|
||||||
|
use crate::interpreter::gen_nort::nort_gen;
|
||||||
|
use crate::interpreter::nort::{Clause, Expr};
|
||||||
|
use crate::libs::std::tuple::Tuple;
|
||||||
|
use crate::location::CodeLocation;
|
||||||
|
use crate::utils::ddispatch::Responder;
|
||||||
|
|
||||||
|
impl<T: ToClause> ToClause for Option<T> {
|
||||||
|
fn to_clause(self, location: CodeLocation) -> Clause {
|
||||||
|
let ctx = nort_gen(location.clone());
|
||||||
|
match self {
|
||||||
|
None => tpl::C("std::option::none").template(ctx, []),
|
||||||
|
Some(t) =>
|
||||||
|
tpl::A(tpl::C("std::option::some"), tpl::Slot).template(ctx, [t.to_clause(location)]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToClause, U: ToClause> ToClause for Result<T, U> {
|
||||||
|
fn to_clause(self, location: CodeLocation) -> Clause {
|
||||||
|
let ctx = nort_gen(location.clone());
|
||||||
|
match self {
|
||||||
|
Ok(t) =>
|
||||||
|
tpl::A(tpl::C("std::result::ok"), tpl::Slot).template(ctx, [t.to_clause(location)]),
|
||||||
|
Err(e) =>
|
||||||
|
tpl::A(tpl::C("std::result::err"), tpl::Slot).template(ctx, [e.to_clause(location)]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PendingError(RTErrorObj);
|
||||||
|
impl Responder for PendingError {}
|
||||||
|
impl fmt::Debug for PendingError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "PendingError({})", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Atomic for PendingError {
|
||||||
|
fn as_any(self: Box<Self>) -> Box<dyn Any> { self }
|
||||||
|
fn as_any_ref(&self) -> &dyn Any { self }
|
||||||
|
fn type_name(&self) -> &'static str { std::any::type_name::<Self>() }
|
||||||
|
|
||||||
|
fn redirect(&mut self) -> Option<&mut Expr> { None }
|
||||||
|
fn run(self: Box<Self>, _: RunData) -> AtomicResult { Err(self.0) }
|
||||||
|
fn apply_mut(&mut self, _: CallData) -> RTResult<Clause> {
|
||||||
|
panic!("This atom decays instantly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToClause> ToClause for RTResult<T> {
|
||||||
|
fn to_clause(self, location: CodeLocation) -> Clause {
|
||||||
|
match self {
|
||||||
|
Err(e) => PendingError(e).atom_cls(),
|
||||||
|
Ok(t) => t.to_clause(location),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ToClause + Clone + Send + Sync + 'static> ToClause for Vec<T> {
|
||||||
|
fn to_clause(self, location: CodeLocation) -> Clause { list(self).to_clause(location) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToClause for Atom {
|
||||||
|
fn to_clause(self, _: CodeLocation) -> Clause { Clause::Atom(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! gen_tuple_impl {
|
||||||
|
( ($($T:ident)*) ($($t:ident)*)) => {
|
||||||
|
impl<$($T: ToClause),*> ToClause for ($($T,)*) {
|
||||||
|
fn to_clause(self, location: CodeLocation) -> Clause {
|
||||||
|
let ($($t,)*) = self;
|
||||||
|
Inert(Tuple(Arc::new(vec![
|
||||||
|
$($t.to_expr(location.clone()),)*
|
||||||
|
]))).atom_cls()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<$($T: TryFromExpr),*> TryFromExpr for ($($T,)*) {
|
||||||
|
fn from_expr(ex: Expr) -> RTResult<Self> {
|
||||||
|
let Inert(Tuple(slice)) = ex.clone().downcast()?;
|
||||||
|
match &slice[..] {
|
||||||
|
[$($t),*] => Ok(($($t.clone().downcast()?,)*)),
|
||||||
|
_ => AssertionError::fail(ex.location(), "Tuple length mismatch", format!("{ex}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
gen_tuple_impl!((A)(a));
|
||||||
|
gen_tuple_impl!((A B) (a b));
|
||||||
|
gen_tuple_impl!((A B C) (a b c));
|
||||||
|
gen_tuple_impl!((A B C D) (a b c d));
|
||||||
|
gen_tuple_impl!((A B C D E) (a b c d e));
|
||||||
|
gen_tuple_impl!((A B C D E F) (a b c d e f));
|
||||||
|
gen_tuple_impl!((A B C D E F G) (a b c d e f g));
|
||||||
|
gen_tuple_impl!((A B C D E F G H) (a b c d e f g h));
|
||||||
|
gen_tuple_impl!((A B C D E F G H I) (a b c d e f g h i));
|
||||||
|
gen_tuple_impl!((A B C D E F G H I J) (a b c d e f g h i j));
|
||||||
|
gen_tuple_impl!((A B C D E F G H I J K) (a b c d e f g h i j k));
|
||||||
|
gen_tuple_impl!((A B C D E F G H I J K L) (a b c d e f g h i j k l));
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
use super::error::ExternResult;
|
//! Conversions from Orchid expressions to Rust values. Many APIs and
|
||||||
|
//! [super::fn_bridge] in particular use this to automatically convert values on
|
||||||
|
//! the boundary. Failures cause an interpreter exit
|
||||||
|
|
||||||
|
use super::error::RTResult;
|
||||||
use crate::interpreter::nort::{ClauseInst, Expr};
|
use crate::interpreter::nort::{ClauseInst, Expr};
|
||||||
use crate::location::CodeLocation;
|
use crate::location::CodeLocation;
|
||||||
|
|
||||||
@@ -6,15 +10,15 @@ use crate::location::CodeLocation;
|
|||||||
/// foreign functions request automatic argument downcasting.
|
/// foreign functions request automatic argument downcasting.
|
||||||
pub trait TryFromExpr: Sized {
|
pub trait TryFromExpr: Sized {
|
||||||
/// Match and clone the value out of an [Expr]
|
/// Match and clone the value out of an [Expr]
|
||||||
fn from_expr(expr: Expr) -> ExternResult<Self>;
|
fn from_expr(expr: Expr) -> RTResult<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFromExpr for Expr {
|
impl TryFromExpr for Expr {
|
||||||
fn from_expr(expr: Expr) -> ExternResult<Self> { Ok(expr) }
|
fn from_expr(expr: Expr) -> RTResult<Self> { Ok(expr) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFromExpr for ClauseInst {
|
impl TryFromExpr for ClauseInst {
|
||||||
fn from_expr(expr: Expr) -> ExternResult<Self> { Ok(expr.clsi()) }
|
fn from_expr(expr: Expr) -> RTResult<Self> { Ok(expr.clsi()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request a value of a particular type and also return its location for
|
/// Request a value of a particular type and also return its location for
|
||||||
@@ -22,7 +26,5 @@ impl TryFromExpr for ClauseInst {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct WithLoc<T>(pub CodeLocation, pub T);
|
pub struct WithLoc<T>(pub CodeLocation, pub T);
|
||||||
impl<T: TryFromExpr> TryFromExpr for WithLoc<T> {
|
impl<T: TryFromExpr> TryFromExpr for WithLoc<T> {
|
||||||
fn from_expr(expr: Expr) -> ExternResult<Self> {
|
fn from_expr(expr: Expr) -> RTResult<Self> { Ok(Self(expr.location(), T::from_expr(expr)?)) }
|
||||||
Ok(Self(expr.location(), T::from_expr(expr)?))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
//! Various elemental components to build expression trees that all implement
|
//! Various elemental components to build expression trees that all implement
|
||||||
//! [GenClause].
|
//! [GenClause].
|
||||||
|
|
||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use super::traits::{GenClause, Generable};
|
use super::traits::{GenClause, Generable};
|
||||||
use crate::foreign::atom::{Atom, AtomGenerator, Atomic};
|
use crate::foreign::atom::{Atom, AtomGenerator, Atomic};
|
||||||
|
|
||||||
@@ -44,11 +42,7 @@ impl<F: GenClause, X: GenClause> GenClause for A<F, X> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Apply a function to two arguments
|
/// Apply a function to two arguments
|
||||||
pub fn a2(
|
pub fn a2(f: impl GenClause, x: impl GenClause, y: impl GenClause) -> impl GenClause {
|
||||||
f: impl GenClause,
|
|
||||||
x: impl GenClause,
|
|
||||||
y: impl GenClause,
|
|
||||||
) -> impl GenClause {
|
|
||||||
A(A(f, x), y)
|
A(A(f, x), y)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,16 +59,12 @@ impl<B: GenClause> GenClause for L<B> {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct P(pub &'static str);
|
pub struct P(pub &'static str);
|
||||||
impl GenClause for P {
|
impl GenClause for P {
|
||||||
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::arg(ctx, self.0) }
|
||||||
T::arg(ctx, self.0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Slot for an Orchid value to be specified during execution
|
/// Slot for an Orchid value to be specified during execution
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Slot;
|
pub struct Slot;
|
||||||
impl GenClause for Slot {
|
impl GenClause for Slot {
|
||||||
fn generate<T: Generable>(&self, _: T::Ctx<'_>, pop: &impl Fn() -> T) -> T {
|
fn generate<T: Generable>(&self, _: T::Ctx<'_>, pop: &impl Fn() -> T) -> T { pop() }
|
||||||
pop()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
use std::backtrace::Backtrace;
|
use std::backtrace::Backtrace;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt::Debug;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::foreign::atom::Atom;
|
use crate::foreign::atom::Atom;
|
||||||
|
|
||||||
@@ -15,10 +15,7 @@ pub trait Generable: Sized {
|
|||||||
/// Wrap external data.
|
/// Wrap external data.
|
||||||
fn atom(ctx: Self::Ctx<'_>, a: Atom) -> Self;
|
fn atom(ctx: Self::Ctx<'_>, a: Atom) -> Self;
|
||||||
/// Generate a reference to a constant
|
/// Generate a reference to a constant
|
||||||
fn constant<'a>(
|
fn constant<'a>(ctx: Self::Ctx<'_>, name: impl IntoIterator<Item = &'a str>) -> Self;
|
||||||
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
|
||||||
fn apply(
|
fn apply(
|
||||||
ctx: Self::Ctx<'_>,
|
ctx: Self::Ctx<'_>,
|
||||||
@@ -27,11 +24,7 @@ pub trait Generable: Sized {
|
|||||||
) -> 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(
|
fn lambda(ctx: Self::Ctx<'_>, name: &str, body: impl FnOnce(Self::Ctx<'_>) -> Self) -> Self;
|
||||||
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;
|
||||||
@@ -39,11 +32,11 @@ pub trait Generable: Sized {
|
|||||||
|
|
||||||
/// Expression templates which can be instantiated in multiple representations
|
/// Expression templates which can be instantiated in multiple representations
|
||||||
/// of Orchid. Expressions can be built from the elements defined in
|
/// of Orchid. Expressions can be built from the elements defined in
|
||||||
/// [super::lit].
|
/// [super::tpl].
|
||||||
///
|
///
|
||||||
/// Do not depend on this trait, use [Gen] instead. Conversely, implement this
|
/// Do not depend on this trait, use [Gen] instead. Conversely, implement this
|
||||||
/// instead of [Gen].
|
/// instead of [Gen].
|
||||||
pub trait GenClause: Debug + Sized {
|
pub trait GenClause: fmt::Debug + Sized {
|
||||||
/// Enact the template at runtime to build a given type.
|
/// Enact the template at runtime to build a given type.
|
||||||
/// `pop` pops from the runtime template parameter list passed to the
|
/// `pop` pops from the runtime template parameter list passed to the
|
||||||
/// generator.
|
/// generator.
|
||||||
@@ -56,7 +49,7 @@ pub trait GenClause: Debug + Sized {
|
|||||||
///
|
///
|
||||||
/// Do not implement this trait, it's the frontend for [GenClause]. Conversely,
|
/// Do not implement this trait, it's the frontend for [GenClause]. Conversely,
|
||||||
/// do not consume [GenClause].
|
/// do not consume [GenClause].
|
||||||
pub trait Gen<T: Generable, U>: Debug {
|
pub trait Gen<T: Generable, U>: fmt::Debug {
|
||||||
/// Create an instance of this template with some parameters
|
/// Create an instance of this template with some parameters
|
||||||
fn template(&self, ctx: T::Ctx<'_>, params: U) -> T;
|
fn template(&self, ctx: T::Ctx<'_>, params: U) -> T;
|
||||||
}
|
}
|
||||||
@@ -64,11 +57,15 @@ pub trait Gen<T: Generable, U>: Debug {
|
|||||||
impl<T: Generable, I: IntoIterator<Item = T>, G: GenClause> Gen<T, I> for G {
|
impl<T: Generable, I: IntoIterator<Item = T>, G: GenClause> Gen<T, I> for G {
|
||||||
fn template(&self, ctx: T::Ctx<'_>, params: I) -> T {
|
fn template(&self, ctx: T::Ctx<'_>, params: I) -> T {
|
||||||
let values = RefCell::new(params.into_iter().collect::<VecDeque<_>>());
|
let values = RefCell::new(params.into_iter().collect::<VecDeque<_>>());
|
||||||
let t = self.generate(ctx, &|| {
|
let t =
|
||||||
values.borrow_mut().pop_front().expect("Not enough values provided")
|
self.generate(ctx, &|| values.borrow_mut().pop_front().expect("Not enough values provided"));
|
||||||
});
|
|
||||||
let leftover = values.borrow().len();
|
let leftover = values.borrow().len();
|
||||||
assert_eq!(leftover, 0, "Too many values provided ({leftover} left) {}", Backtrace::force_capture());
|
assert_eq!(
|
||||||
|
leftover,
|
||||||
|
0,
|
||||||
|
"Too many values provided ({leftover} left) {}",
|
||||||
|
Backtrace::force_capture()
|
||||||
|
);
|
||||||
t
|
t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Components to build in-memory module trees that in Orchid. These modules
|
//! Components to build in-memory module trees that in Orchid. These modules
|
||||||
//! can only contain constants and other modules.
|
//! can only contain constants and other modules.
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt;
|
||||||
|
|
||||||
use dyn_clone::{clone_box, DynClone};
|
use dyn_clone::{clone_box, DynClone};
|
||||||
use intern_all::Tok;
|
use intern_all::Tok;
|
||||||
@@ -20,8 +20,8 @@ use crate::tree::{ModEntry, ModMember, TreeConflict};
|
|||||||
use crate::utils::combine::Combine;
|
use crate::utils::combine::Combine;
|
||||||
|
|
||||||
trait_set! {
|
trait_set! {
|
||||||
trait TreeLeaf = Gen<Expr, [Expr; 0]> + DynClone;
|
trait TreeLeaf = Gen<Expr, [Expr; 0]> + DynClone + Send;
|
||||||
trait XfnCB = FnOnce(Substack<Tok<String>>) -> AtomGenerator + DynClone;
|
trait XfnCB = FnOnce(Substack<Tok<String>>) -> AtomGenerator + DynClone + Send;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum GCKind {
|
enum GCKind {
|
||||||
@@ -32,11 +32,14 @@ enum GCKind {
|
|||||||
/// A leaf in the [ConstTree]
|
/// A leaf in the [ConstTree]
|
||||||
pub struct GenConst(GCKind);
|
pub struct GenConst(GCKind);
|
||||||
impl GenConst {
|
impl GenConst {
|
||||||
fn c(data: impl GenClause + Clone + 'static) -> Self { Self(GCKind::Const(Box::new(data))) }
|
fn c(data: impl GenClause + Send + Clone + 'static) -> Self {
|
||||||
|
Self(GCKind::Const(Box::new(data)))
|
||||||
|
}
|
||||||
fn f<const N: usize, Argv, Ret>(f: impl Xfn<N, Argv, Ret>) -> Self {
|
fn f<const N: usize, Argv, Ret>(f: impl Xfn<N, Argv, Ret>) -> Self {
|
||||||
Self(GCKind::Xfn(N, Box::new(move |stck| {
|
Self(GCKind::Xfn(
|
||||||
AtomGenerator::cloner(xfn(&stck.unreverse().iter().join("::"), f))
|
N,
|
||||||
})))
|
Box::new(move |stck| AtomGenerator::cloner(xfn(&stck.unreverse().iter().join("::"), f))),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
/// Instantiate as [crate::interpreter::nort]
|
/// Instantiate as [crate::interpreter::nort]
|
||||||
pub fn gen_nort(self, stck: Substack<Tok<String>>, location: CodeLocation) -> Expr {
|
pub fn gen_nort(self, stck: Substack<Tok<String>>, location: CodeLocation) -> Expr {
|
||||||
@@ -46,8 +49,8 @@ impl GenConst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Debug for GenConst {
|
impl fmt::Debug for GenConst {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
GCKind::Const(c) => write!(f, "{c:?}"),
|
GCKind::Const(c) => write!(f, "{c:?}"),
|
||||||
GCKind::Xfn(n, _) => write!(f, "xfn/{n}"),
|
GCKind::Xfn(n, _) => write!(f, "xfn/{n}"),
|
||||||
@@ -75,19 +78,30 @@ impl Combine for GenConst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A lightweight module tree that can be built declaratively by hand to
|
/// A lightweight module tree that can be built declaratively by hand to
|
||||||
/// describe libraries of external functions in Rust. It implements [Add] for
|
/// describe libraries of external functions in Rust. It implements [Combine]
|
||||||
/// added convenience
|
/// for merging libraries.
|
||||||
pub type ConstTree = ModEntry<GenConst, (), ()>;
|
pub type ConstTree = ModEntry<GenConst, (), ()>;
|
||||||
|
|
||||||
/// Describe a constant
|
/// Describe a constant
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn leaf(value: impl GenClause + Clone + 'static) -> ConstTree {
|
pub fn leaf(value: impl GenClause + Clone + Send + 'static) -> ConstTree {
|
||||||
ModEntry::wrap(ModMember::Item(GenConst::c(value)))
|
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]
|
/// Describe an [Atomic]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn atom_leaf(atom: impl Atomic + Clone + 'static) -> ConstTree { leaf(tpl::V(atom)) }
|
pub fn atom_leaf(atom: impl Atomic + Clone + Send + 'static) -> ConstTree { leaf(tpl::V(atom)) }
|
||||||
|
|
||||||
/// Describe an [Atomic] which appears as an entry in a [ConstTree::tree]
|
/// Describe an [Atomic] which appears as an entry in a [ConstTree::tree]
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use substack::Substack;
|
|||||||
|
|
||||||
use super::ir;
|
use super::ir;
|
||||||
use crate::error::{ProjectError, ProjectResult};
|
use crate::error::{ProjectError, ProjectResult};
|
||||||
use crate::location::{CodeLocation, SourceRange};
|
use crate::location::{CodeOrigin, SourceRange};
|
||||||
use crate::name::Sym;
|
use crate::name::Sym;
|
||||||
use crate::parse::parsed;
|
use crate::parse::parsed;
|
||||||
use crate::utils::unwrap_or::unwrap_or;
|
use crate::utils::unwrap_or::unwrap_or;
|
||||||
@@ -17,20 +17,24 @@ trait IRErrorKind: Clone + Send + Sync + 'static {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct IRError<T: IRErrorKind>(SourceRange, Sym, T);
|
struct IRError<T: IRErrorKind> {
|
||||||
|
at: SourceRange,
|
||||||
|
sym: SourceRange,
|
||||||
|
_kind: T,
|
||||||
|
}
|
||||||
|
impl<T: IRErrorKind> IRError<T> {
|
||||||
|
fn new(at: SourceRange, sym: SourceRange, _kind: T) -> Self { Self { at, sym, _kind } }
|
||||||
|
}
|
||||||
impl<T: IRErrorKind> ProjectError for IRError<T> {
|
impl<T: IRErrorKind> ProjectError for IRError<T> {
|
||||||
const DESCRIPTION: &'static str = T::DESCR;
|
const DESCRIPTION: &'static str = T::DESCR;
|
||||||
fn message(&self) -> String { format!("In {}, {}", self.1, T::DESCR) }
|
fn message(&self) -> String { format!("In {}, {}", self.sym, T::DESCR) }
|
||||||
fn one_position(&self) -> CodeLocation {
|
fn one_position(&self) -> CodeOrigin { CodeOrigin::Source(self.at.clone()) }
|
||||||
CodeLocation::Source(self.0.clone())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct EmptyS;
|
struct EmptyS;
|
||||||
impl IRErrorKind for EmptyS {
|
impl IRErrorKind for EmptyS {
|
||||||
const DESCR: &'static str =
|
const DESCR: &'static str = "`()` as a clause is meaningless in lambda calculus";
|
||||||
"`()` as a clause is meaningless in lambda calculus";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -54,26 +58,29 @@ impl IRErrorKind for PhLeak {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Try to convert an expression from AST format to typed lambda
|
/// Try to convert an expression from AST format to typed lambda
|
||||||
pub fn ast_to_ir(expr: parsed::Expr, symbol: Sym) -> ProjectResult<ir::Expr> {
|
pub fn ast_to_ir(expr: parsed::Expr, symbol: SourceRange, module: Sym) -> ProjectResult<ir::Expr> {
|
||||||
expr_rec(expr, Context::new(symbol))
|
expr_rec(expr, Context::new(symbol, module))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct Context<'a> {
|
struct Context<'a> {
|
||||||
names: Substack<'a, Sym>,
|
names: Substack<'a, Sym>,
|
||||||
symbol: Sym,
|
range: SourceRange,
|
||||||
|
module: Sym,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Context<'a> {
|
impl<'a> Context<'a> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn w_name<'b>(&'b self, name: Sym) -> Context<'b>
|
fn w_name<'b>(&'b self, name: Sym) -> Context<'b>
|
||||||
where 'a: 'b {
|
where 'a: 'b {
|
||||||
Context { names: self.names.push(name), symbol: self.symbol.clone() }
|
Context { names: self.names.push(name), range: self.range.clone(), module: self.module.clone() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Context<'static> {
|
impl Context<'static> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn new(symbol: Sym) -> Self { Self { names: Substack::Bottom, symbol } }
|
fn new(symbol: SourceRange, module: Sym) -> Self {
|
||||||
|
Self { names: Substack::Bottom, range: symbol, module }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process an expression sequence
|
/// Process an expression sequence
|
||||||
@@ -83,29 +90,25 @@ fn exprv_rec(
|
|||||||
location: SourceRange,
|
location: SourceRange,
|
||||||
) -> ProjectResult<ir::Expr> {
|
) -> ProjectResult<ir::Expr> {
|
||||||
let last = unwrap_or! {v.pop_back(); {
|
let last = unwrap_or! {v.pop_back(); {
|
||||||
return Err(IRError(location, ctx.symbol, EmptyS).pack());
|
return Err(IRError::new(location, ctx.range, EmptyS).pack());
|
||||||
}};
|
}};
|
||||||
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.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)?;
|
let x = expr_rec(last, ctx.clone())?;
|
||||||
let value = ir::Clause::Apply(Rc::new(f), Rc::new(x));
|
let value = ir::Clause::Apply(Rc::new(f), Rc::new(x));
|
||||||
Ok(ir::Expr { value, location: CodeLocation::Source(location) })
|
Ok(ir::Expr::new(value, location, ctx.module))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process an expression
|
/// Process an expression
|
||||||
fn expr_rec(
|
fn expr_rec(parsed::Expr { value, range }: parsed::Expr, ctx: Context) -> ProjectResult<ir::Expr> {
|
||||||
parsed::Expr { value, range }: parsed::Expr,
|
|
||||||
ctx: Context,
|
|
||||||
) -> ProjectResult<ir::Expr> {
|
|
||||||
match value {
|
match value {
|
||||||
parsed::Clause::S(parsed::PType::Par, body) => {
|
parsed::Clause::S(parsed::PType::Par, body) => {
|
||||||
return exprv_rec(body.to_vec().into(), ctx, range);
|
return exprv_rec(body.to_vec().into(), ctx, range);
|
||||||
},
|
},
|
||||||
parsed::Clause::S(..) =>
|
parsed::Clause::S(..) => return Err(IRError::new(range, ctx.range, BadGroup).pack()),
|
||||||
return Err(IRError(range, ctx.symbol, BadGroup).pack()),
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
let value = match value {
|
let value = match value {
|
||||||
@@ -114,29 +117,24 @@ fn expr_rec(
|
|||||||
let name = match &arg[..] {
|
let name = match &arg[..] {
|
||||||
[parsed::Expr { value: parsed::Clause::Name(name), .. }] => name,
|
[parsed::Expr { value: parsed::Clause::Name(name), .. }] => name,
|
||||||
[parsed::Expr { value: parsed::Clause::Placeh { .. }, .. }] =>
|
[parsed::Expr { value: parsed::Clause::Placeh { .. }, .. }] =>
|
||||||
return Err(IRError(range.clone(), ctx.symbol, PhLeak).pack()),
|
return Err(IRError::new(range.clone(), ctx.range, PhLeak).pack()),
|
||||||
_ => return Err(IRError(range.clone(), ctx.symbol, InvalidArg).pack()),
|
_ => return Err(IRError::new(range.clone(), ctx.range, InvalidArg).pack()),
|
||||||
};
|
};
|
||||||
let body_ctx = ctx.w_name(name.clone());
|
let body_ctx = ctx.w_name(name.clone());
|
||||||
let body = exprv_rec(b.to_vec().into(), body_ctx, range.clone())?;
|
let body = exprv_rec(b.to_vec().into(), body_ctx, range.clone())?;
|
||||||
ir::Clause::Lambda(Rc::new(body))
|
ir::Clause::Lambda(Rc::new(body))
|
||||||
},
|
},
|
||||||
parsed::Clause::Name(name) => {
|
parsed::Clause::Name(name) => {
|
||||||
let lvl_opt = (ctx.names.iter())
|
let lvl_opt = (ctx.names.iter()).enumerate().find(|(_, n)| **n == name).map(|(lvl, _)| lvl);
|
||||||
.enumerate()
|
|
||||||
.find(|(_, n)| **n == name)
|
|
||||||
.map(|(lvl, _)| lvl);
|
|
||||||
match lvl_opt {
|
match lvl_opt {
|
||||||
Some(lvl) => ir::Clause::LambdaArg(lvl),
|
Some(lvl) => ir::Clause::LambdaArg(lvl),
|
||||||
None => ir::Clause::Constant(name.clone()),
|
None => ir::Clause::Constant(name.clone()),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parsed::Clause::S(parsed::PType::Par, entries) =>
|
parsed::Clause::S(parsed::PType::Par, entries) =>
|
||||||
exprv_rec(entries.to_vec().into(), ctx, range.clone())?.value,
|
exprv_rec(entries.to_vec().into(), ctx.clone(), range.clone())?.value,
|
||||||
parsed::Clause::S(..) =>
|
parsed::Clause::S(..) => return Err(IRError::new(range, ctx.range, BadGroup).pack()),
|
||||||
return Err(IRError(range, ctx.symbol, BadGroup).pack()),
|
parsed::Clause::Placeh { .. } => return Err(IRError::new(range, ctx.range, PhLeak).pack()),
|
||||||
parsed::Clause::Placeh { .. } =>
|
|
||||||
return Err(IRError(range, ctx.symbol, PhLeak).pack()),
|
|
||||||
};
|
};
|
||||||
Ok(ir::Expr::new(value, range.clone()))
|
Ok(ir::Expr::new(value, range, ctx.module))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//! innovations in the processing and execution of code will likely operate on
|
//! innovations in the processing and execution of code will likely operate on
|
||||||
//! this representation.
|
//! this representation.
|
||||||
|
|
||||||
use std::fmt::{Debug, Write};
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::foreign::atom::AtomGenerator;
|
use crate::foreign::atom::AtomGenerator;
|
||||||
@@ -16,40 +16,44 @@ use crate::utils::string_from_charset::string_from_charset;
|
|||||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
struct Wrap(bool, bool);
|
struct Wrap(bool, bool);
|
||||||
|
|
||||||
|
/// Code element with associated metadata
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Expr {
|
pub struct Expr {
|
||||||
|
/// Code element
|
||||||
pub value: Clause,
|
pub value: Clause,
|
||||||
|
/// Location metadata
|
||||||
pub location: CodeLocation,
|
pub location: CodeLocation,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
pub fn new(value: Clause, location: SourceRange) -> Self {
|
/// Create an IR expression
|
||||||
Self { value, location: CodeLocation::Source(location) }
|
pub fn new(value: Clause, location: SourceRange, module: Sym) -> Self {
|
||||||
|
Self { value, location: CodeLocation::new_src(location, module) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deep_fmt(
|
fn deep_fmt(&self, f: &mut fmt::Formatter<'_>, depth: usize, tr: Wrap) -> fmt::Result {
|
||||||
&self,
|
|
||||||
f: &mut std::fmt::Formatter<'_>,
|
|
||||||
depth: usize,
|
|
||||||
tr: Wrap,
|
|
||||||
) -> std::fmt::Result {
|
|
||||||
let Expr { value, .. } = self;
|
let Expr { value, .. } = self;
|
||||||
value.deep_fmt(f, depth, tr)?;
|
value.deep_fmt(f, depth, tr)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Expr {
|
impl fmt::Debug for Expr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.deep_fmt(f, 0, Wrap(false, false))
|
self.deep_fmt(f, 0, Wrap(false, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Semantic code element
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Clause {
|
pub enum Clause {
|
||||||
|
/// Function call expression
|
||||||
Apply(Rc<Expr>, Rc<Expr>),
|
Apply(Rc<Expr>, Rc<Expr>),
|
||||||
|
/// Function expression
|
||||||
Lambda(Rc<Expr>),
|
Lambda(Rc<Expr>),
|
||||||
|
/// Reference to an external constant
|
||||||
Constant(Sym),
|
Constant(Sym),
|
||||||
|
/// Reference to a function argument
|
||||||
LambdaArg(usize),
|
LambdaArg(usize),
|
||||||
/// An opaque non-callable value, eg. a file handle
|
/// An opaque non-callable value, eg. a file handle
|
||||||
Atom(AtomGenerator),
|
Atom(AtomGenerator),
|
||||||
@@ -58,32 +62,27 @@ pub enum Clause {
|
|||||||
const ARGNAME_CHARSET: &str = "abcdefghijklmnopqrstuvwxyz";
|
const ARGNAME_CHARSET: &str = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
fn parametric_fmt(
|
fn parametric_fmt(
|
||||||
f: &mut std::fmt::Formatter<'_>,
|
f: &mut fmt::Formatter<'_>,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
prefix: &str,
|
prefix: &str,
|
||||||
body: &Expr,
|
body: &Expr,
|
||||||
wrap_right: bool,
|
wrap_right: bool,
|
||||||
) -> std::fmt::Result {
|
) -> fmt::Result {
|
||||||
// if wrap_right {
|
if wrap_right {
|
||||||
f.write_char('(')?;
|
write!(f, "(")?;
|
||||||
// }
|
}
|
||||||
f.write_str(prefix)?;
|
f.write_str(prefix)?;
|
||||||
f.write_str(&string_from_charset(depth as u64, ARGNAME_CHARSET))?;
|
f.write_str(&string_from_charset(depth as u64, ARGNAME_CHARSET))?;
|
||||||
f.write_str(".")?;
|
f.write_str(".")?;
|
||||||
body.deep_fmt(f, depth + 1, Wrap(false, false))?;
|
body.deep_fmt(f, depth + 1, Wrap(false, false))?;
|
||||||
// if wrap_right {
|
if wrap_right {
|
||||||
f.write_char(')')?;
|
write!(f, ")")?;
|
||||||
// }
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clause {
|
impl Clause {
|
||||||
fn deep_fmt(
|
fn deep_fmt(&self, f: &mut fmt::Formatter<'_>, depth: usize, Wrap(wl, wr): Wrap) -> fmt::Result {
|
||||||
&self,
|
|
||||||
f: &mut std::fmt::Formatter<'_>,
|
|
||||||
depth: usize,
|
|
||||||
Wrap(wl, wr): Wrap,
|
|
||||||
) -> std::fmt::Result {
|
|
||||||
match self {
|
match self {
|
||||||
Self::Atom(a) => write!(f, "{a:?}"),
|
Self::Atom(a) => write!(f, "{a:?}"),
|
||||||
Self::Lambda(body) => parametric_fmt(f, depth, "\\", body, wr),
|
Self::Lambda(body) => parametric_fmt(f, depth, "\\", body, wr),
|
||||||
@@ -92,15 +91,15 @@ impl Clause {
|
|||||||
f.write_str(&string_from_charset(lambda_depth, ARGNAME_CHARSET))
|
f.write_str(&string_from_charset(lambda_depth, ARGNAME_CHARSET))
|
||||||
},
|
},
|
||||||
Self::Apply(func, x) => {
|
Self::Apply(func, x) => {
|
||||||
// if wl {
|
if wl {
|
||||||
f.write_char('(')?;
|
write!(f, "(")?;
|
||||||
// }
|
}
|
||||||
func.deep_fmt(f, depth, Wrap(false, true))?;
|
func.deep_fmt(f, depth, Wrap(false, true))?;
|
||||||
f.write_char(' ')?;
|
write!(f, " ")?;
|
||||||
x.deep_fmt(f, depth, Wrap(true, wr && !wl))?;
|
x.deep_fmt(f, depth, Wrap(true, wr && !wl))?;
|
||||||
// if wl {
|
if wl {
|
||||||
f.write_char(')')?;
|
write!(f, ")")?;
|
||||||
// }
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
Self::Constant(token) => write!(f, "{token}"),
|
Self::Constant(token) => write!(f, "{token}"),
|
||||||
@@ -108,8 +107,8 @@ impl Clause {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Clause {
|
impl fmt::Debug for Clause {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.deep_fmt(f, 0, Wrap(false, false))
|
self.deep_fmt(f, 0, Wrap(false, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::interpreter::nort;
|
|||||||
use crate::interpreter::nort_builder::NortBuilder;
|
use crate::interpreter::nort_builder::NortBuilder;
|
||||||
|
|
||||||
fn expr(expr: &ir::Expr, ctx: NortBuilder<(), usize>) -> nort::Expr {
|
fn expr(expr: &ir::Expr, ctx: NortBuilder<(), usize>) -> nort::Expr {
|
||||||
clause(&expr.value, ctx).to_expr(expr.location.clone())
|
clause(&expr.value, ctx).into_expr(expr.location.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clause(cls: &ir::Clause, ctx: NortBuilder<(), usize>) -> nort::Clause {
|
fn clause(cls: &ir::Clause, ctx: NortBuilder<(), usize>) -> nort::Clause {
|
||||||
@@ -21,6 +21,7 @@ fn clause(cls: &ir::Clause, ctx: NortBuilder<(), usize>) -> nort::Clause {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert an expression.
|
||||||
pub fn ir_to_nort(expr: &ir::Expr) -> nort::Expr {
|
pub fn ir_to_nort(expr: &ir::Expr) -> nort::Expr {
|
||||||
let c = NortBuilder::new(&|count| {
|
let c = NortBuilder::new(&|count| {
|
||||||
let mut count: usize = *count;
|
let mut count: usize = *count;
|
||||||
@@ -32,5 +33,5 @@ pub fn ir_to_nort(expr: &ir::Expr) -> nort::Expr {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
nort::ClauseInst::new(clause(&expr.value, c)).to_expr(expr.location.clone())
|
nort::ClauseInst::new(clause(&expr.value, c)).into_expr(expr.location.clone())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
pub(crate) mod ast_to_ir;
|
//! Intermediate representation. Currently just an indirection between
|
||||||
pub(crate) mod ir;
|
//! [super::parse::parsed] and [super::interpreter::nort], in the future
|
||||||
pub(crate) mod ir_to_nort;
|
//! hopefully a common point for alternate encodings, optimizations and targets.
|
||||||
|
|
||||||
|
pub mod ast_to_ir;
|
||||||
|
pub mod ir;
|
||||||
|
pub mod ir_to_nort;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use never::Never;
|
use never::Never;
|
||||||
|
|
||||||
use super::context::RunContext;
|
use super::context::{RunEnv, RunParams};
|
||||||
use super::error::RunError;
|
|
||||||
use super::nort::{Clause, ClauseInst, Expr};
|
use super::nort::{Clause, ClauseInst, Expr};
|
||||||
use super::path_set::{PathSet, Step};
|
use super::path_set::{PathSet, Step};
|
||||||
use crate::foreign::atom::CallData;
|
use crate::foreign::atom::CallData;
|
||||||
|
use crate::foreign::error::RTResult;
|
||||||
|
|
||||||
/// Process the clause at the end of the provided path. Note that paths always
|
/// Process the clause at the end of the provided path. Note that paths always
|
||||||
/// point to at least one target. Note also that this is not cached as a
|
/// point to at least one target. Note also that this is not cached as a
|
||||||
@@ -16,12 +16,12 @@ fn map_at<E>(
|
|||||||
) -> Result<Clause, E> {
|
) -> Result<Clause, E> {
|
||||||
// Pass through some unambiguous wrapper clauses
|
// Pass through some unambiguous wrapper clauses
|
||||||
match source {
|
match source {
|
||||||
Clause::Identity(alt) => return map_at(path, &alt.cls(), mapper),
|
Clause::Identity(alt) => return map_at(path, &alt.cls_mut(), mapper),
|
||||||
Clause::Lambda { args, body: Expr { location: b_loc, clause } } =>
|
Clause::Lambda { args, body: Expr { location: b_loc, clause } } =>
|
||||||
return Ok(Clause::Lambda {
|
return Ok(Clause::Lambda {
|
||||||
args: args.clone(),
|
args: args.clone(),
|
||||||
body: Expr {
|
body: Expr {
|
||||||
clause: map_at(path, &clause.cls(), mapper)?.to_inst(),
|
clause: map_at(path, &clause.cls_mut(), mapper)?.into_inst(),
|
||||||
location: b_loc.clone(),
|
location: b_loc.clone(),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@@ -33,7 +33,7 @@ fn map_at<E>(
|
|||||||
(val, None) => mapper(val)?,
|
(val, None) => mapper(val)?,
|
||||||
// If it's an Apply, execute the next step in the path
|
// If it's an Apply, execute the next step in the path
|
||||||
(Clause::Apply { f, x }, Some(head)) => {
|
(Clause::Apply { f, x }, Some(head)) => {
|
||||||
let proc = |x: &Expr| Ok(map_at(path, &x.cls(), mapper)?.to_expr(x.location()));
|
let proc = |x: &Expr| Ok(map_at(path, &x.cls_mut(), mapper)?.into_expr(x.location()));
|
||||||
match head {
|
match head {
|
||||||
None => Clause::Apply { f: proc(f)?, x: x.clone() },
|
None => Clause::Apply { f: proc(f)?, x: x.clone() },
|
||||||
Some(n) => {
|
Some(n) => {
|
||||||
@@ -68,12 +68,12 @@ pub fn substitute(
|
|||||||
let mut argv = x.clone();
|
let mut argv = x.clone();
|
||||||
let f = match conts.get(&None) {
|
let f = match conts.get(&None) {
|
||||||
None => f.clone(),
|
None => f.clone(),
|
||||||
Some(sp) => substitute(sp, value.clone(), &f.cls(), on_sub).to_expr(f.location()),
|
Some(sp) => substitute(sp, value.clone(), &f.cls_mut(), on_sub).into_expr(f.location()),
|
||||||
};
|
};
|
||||||
for (i, old) in argv.iter_mut().rev().enumerate() {
|
for (i, old) in argv.iter_mut().rev().enumerate() {
|
||||||
if let Some(sp) = conts.get(&Some(i)) {
|
if let Some(sp) = conts.get(&Some(i)) {
|
||||||
let tmp = substitute(sp, value.clone(), &old.cls(), on_sub);
|
let tmp = substitute(sp, value.clone(), &old.cls_mut(), on_sub);
|
||||||
*old = tmp.to_expr(old.location());
|
*old = tmp.into_expr(old.location());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Clause::Apply { f, x: argv })
|
Ok(Clause::Apply { f, x: argv })
|
||||||
@@ -89,15 +89,20 @@ pub fn substitute(
|
|||||||
.unwrap_or_else(|e| match e {})
|
.unwrap_or_else(|e| match e {})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn apply_as_atom(f: Expr, arg: Expr, ctx: RunContext) -> Result<Clause, RunError> {
|
pub(super) fn apply_as_atom(
|
||||||
let call = CallData { location: f.location(), arg, ctx };
|
f: Expr,
|
||||||
|
arg: Expr,
|
||||||
|
env: &RunEnv,
|
||||||
|
params: &mut RunParams,
|
||||||
|
) -> RTResult<Clause> {
|
||||||
|
let call = CallData { location: f.location(), arg, env, params };
|
||||||
match f.clause.try_unwrap() {
|
match f.clause.try_unwrap() {
|
||||||
Ok(clause) => match clause {
|
Ok(clause) => match clause {
|
||||||
Clause::Atom(atom) => Ok(atom.apply(call)?),
|
Clause::Atom(atom) => Ok(atom.apply(call)?),
|
||||||
_ => panic!("Not an atom"),
|
_ => panic!("Not an atom"),
|
||||||
},
|
},
|
||||||
Err(clsi) => match &*clsi.cls() {
|
Err(clsi) => match &mut *clsi.cls_mut() {
|
||||||
Clause::Atom(atom) => Ok(atom.apply_ref(call)?),
|
Clause::Atom(atom) => Ok(atom.apply_mut(call)?),
|
||||||
_ => panic!("Not an atom"),
|
_ => panic!("Not an atom"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,67 @@
|
|||||||
|
//! Addiitional information passed to the interpreter
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
use super::nort::Expr;
|
use super::handler::HandlerTable;
|
||||||
|
use super::nort::{Clause, Expr};
|
||||||
|
use crate::foreign::error::{RTError, RTErrorObj, RTResult};
|
||||||
|
use crate::location::CodeLocation;
|
||||||
use crate::name::Sym;
|
use crate::name::Sym;
|
||||||
|
|
||||||
/// All the data associated with an interpreter run
|
/// Data that must not change except in well-defined ways while any data
|
||||||
#[derive(Clone)]
|
/// associated with this process persists
|
||||||
pub struct RunContext<'a> {
|
pub struct RunEnv<'a> {
|
||||||
/// Table used to resolve constants
|
/// Mutable callbacks the code can invoke with continuation passing
|
||||||
pub symbols: &'a HashMap<Sym, Expr>,
|
pub handlers: HandlerTable<'a>,
|
||||||
/// The number of reduction steps the interpreter can take before returning
|
/// Constants referenced in the code in [super::nort::Clause::Constant] nodes
|
||||||
pub gas: Option<usize>,
|
pub symbols: RefCell<HashMap<Sym, RTResult<Expr>>>,
|
||||||
/// The limit of recursion
|
/// Callback to invoke when a symbol is not found
|
||||||
pub stack_size: usize,
|
pub symbol_cb: Box<dyn Fn(Sym, CodeLocation) -> RTResult<Expr> + 'a>,
|
||||||
}
|
}
|
||||||
impl<'a> RunContext<'a> {
|
|
||||||
|
impl<'a> RunEnv<'a> {
|
||||||
|
/// Create a new context. The return values of the symbol callback are cached
|
||||||
|
pub fn new(
|
||||||
|
handlers: HandlerTable<'a>,
|
||||||
|
symbol_cb: impl Fn(Sym, CodeLocation) -> RTResult<Expr> + 'a,
|
||||||
|
) -> Self {
|
||||||
|
Self { handlers, symbols: RefCell::new(HashMap::new()), symbol_cb: Box::new(symbol_cb) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produce an error indicating that a symbol was missing
|
||||||
|
pub fn sym_not_found(sym: Sym, location: CodeLocation) -> RTErrorObj {
|
||||||
|
MissingSymbol { location, sym }.pack()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load a symbol from cache or invoke the callback
|
||||||
|
pub fn load(&self, sym: Sym, location: CodeLocation) -> RTResult<Expr> {
|
||||||
|
let mut guard = self.symbols.borrow_mut();
|
||||||
|
let (_, r) = (guard.raw_entry_mut().from_key(&sym))
|
||||||
|
.or_insert_with(|| (sym.clone(), (self.symbol_cb)(sym, location)));
|
||||||
|
r.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to resolve the command with the command handler table
|
||||||
|
pub fn dispatch(&self, expr: &Clause, location: CodeLocation) -> Option<Expr> {
|
||||||
|
match expr {
|
||||||
|
Clause::Atom(at) => self.handlers.dispatch(&*at.0, location),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Limits and other context that is subject to change
|
||||||
|
pub struct RunParams {
|
||||||
|
/// Number of reduction steps permitted before the program is preempted
|
||||||
|
pub gas: Option<usize>,
|
||||||
|
/// Maximum recursion depth. Orchid uses a soft stack so this can be very
|
||||||
|
/// large, but it must not be
|
||||||
|
pub stack: usize,
|
||||||
|
}
|
||||||
|
impl RunParams {
|
||||||
/// Consume some gas if it is being counted
|
/// Consume some gas if it is being counted
|
||||||
pub fn use_gas(&mut self, amount: usize) {
|
pub fn use_gas(&mut self, amount: usize) {
|
||||||
if let Some(g) = self.gas.as_mut() {
|
if let Some(g) = self.gas.as_mut() {
|
||||||
@@ -22,39 +70,26 @@ impl<'a> RunContext<'a> {
|
|||||||
}
|
}
|
||||||
/// Gas is being counted and there is none left
|
/// Gas is being counted and there is none left
|
||||||
pub fn no_gas(&self) -> bool { self.gas == Some(0) }
|
pub fn no_gas(&self) -> bool { self.gas == Some(0) }
|
||||||
|
/// Add gas to make execution longer, or to resume execution in a preempted
|
||||||
|
/// expression
|
||||||
|
pub fn add_gas(&mut self, amount: usize) {
|
||||||
|
if let Some(g) = self.gas.as_mut() {
|
||||||
|
*g = g.saturating_add(amount)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// All the data produced by an interpreter run
|
/// The interpreter's sole output excluding error conditions is an expression
|
||||||
|
pub type Halt = Expr;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Halt {
|
pub(crate) struct MissingSymbol {
|
||||||
/// The new expression tree
|
pub sym: Sym,
|
||||||
pub state: Expr,
|
pub location: CodeLocation,
|
||||||
/// Leftover [Context::gas] if counted
|
|
||||||
pub gas: Option<usize>,
|
|
||||||
/// If true, the next run would not modify the expression
|
|
||||||
pub inert: bool,
|
|
||||||
}
|
}
|
||||||
impl Halt {
|
impl RTError for MissingSymbol {}
|
||||||
/// Check if gas has run out. Returns false if gas is not being used
|
impl fmt::Display for MissingSymbol {
|
||||||
pub fn preempted(&self) -> bool { self.gas.map_or(false, |g| g == 0) }
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
/// Returns a general report of the return
|
write!(f, "{}, called at {} is not loaded", self.sym, self.location)
|
||||||
pub fn status(&self) -> ReturnStatus {
|
|
||||||
if self.preempted() {
|
|
||||||
ReturnStatus::Preempted
|
|
||||||
} else if self.inert {
|
|
||||||
ReturnStatus::Inert
|
|
||||||
} else {
|
|
||||||
ReturnStatus::Active
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Possible states of a [Return]
|
|
||||||
pub enum ReturnStatus {
|
|
||||||
/// The data is not normalizable any further
|
|
||||||
Inert,
|
|
||||||
/// Gas is being used and it ran out
|
|
||||||
Preempted,
|
|
||||||
/// Normalization stopped for a different reason and should continue.
|
|
||||||
Active,
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,66 +1,33 @@
|
|||||||
use std::fmt::{self, Debug, Display};
|
//! Error produced by the interpreter.
|
||||||
|
|
||||||
use itertools::Itertools;
|
use std::fmt;
|
||||||
|
|
||||||
use super::nort::Expr;
|
use super::run::State;
|
||||||
use super::run::Interrupted;
|
use crate::foreign::error::{RTError, RTErrorObj};
|
||||||
use crate::foreign::error::{ExternError, ExternErrorObj};
|
|
||||||
use crate::location::CodeLocation;
|
|
||||||
use crate::name::Sym;
|
|
||||||
|
|
||||||
/// Print a stack trace
|
/// Error produced by the interpreter. This could be because the code is faulty,
|
||||||
pub fn strace(stack: &[Expr]) -> String {
|
/// but equally because gas was being counted and it ran out.
|
||||||
stack.iter().rev().map(|x| format!("{x}\n at {}", x.location)).join("\n")
|
#[derive(Debug)]
|
||||||
}
|
pub enum RunError<'a> {
|
||||||
|
|
||||||
/// Problems in the process of execution
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum RunError {
|
|
||||||
/// A Rust function encountered an error
|
/// A Rust function encountered an error
|
||||||
Extern(ExternErrorObj),
|
Extern(RTErrorObj),
|
||||||
/// Ran out of gas
|
/// Ran out of gas
|
||||||
Interrupted(Interrupted),
|
Interrupted(State<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ExternError + 'static> From<T> for RunError {
|
impl<'a, T: RTError + 'static> From<T> for RunError<'a> {
|
||||||
fn from(value: T) -> Self { Self::Extern(value.rc()) }
|
fn from(value: T) -> Self { Self::Extern(value.pack()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ExternErrorObj> for RunError {
|
impl<'a> From<RTErrorObj> for RunError<'a> {
|
||||||
fn from(value: ExternErrorObj) -> Self { Self::Extern(value) }
|
fn from(value: RTErrorObj) -> Self { Self::Extern(value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for RunError {
|
impl<'a> fmt::Display for RunError<'a> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Interrupted(i) => {
|
Self::Interrupted(i) => write!(f, "Ran out of gas:\n{i}"),
|
||||||
write!(f, "Ran out of gas:\n{}", strace(&i.stack))
|
|
||||||
},
|
|
||||||
Self::Extern(e) => write!(f, "Program fault: {e}"),
|
Self::Extern(e) => write!(f, "Program fault: {e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub(crate) struct StackOverflow {
|
|
||||||
pub stack: Vec<Expr>,
|
|
||||||
}
|
|
||||||
impl ExternError for StackOverflow {}
|
|
||||||
impl Display for StackOverflow {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let limit = self.stack.len() - 2; // 1 for failed call, 1 for current
|
|
||||||
write!(f, "Stack depth exceeded {limit}:\n{}", strace(&self.stack))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub(crate) struct MissingSymbol {
|
|
||||||
pub sym: Sym,
|
|
||||||
pub loc: CodeLocation,
|
|
||||||
}
|
|
||||||
impl ExternError for MissingSymbol {}
|
|
||||||
impl Display for MissingSymbol {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "{}, called at {} is not loaded", self.sym, self.loc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -26,55 +26,32 @@ impl Generable for Expr {
|
|||||||
f_cb: impl FnOnce(Self::Ctx<'_>) -> Self,
|
f_cb: impl FnOnce(Self::Ctx<'_>) -> Self,
|
||||||
x_cb: impl FnOnce(Self::Ctx<'_>) -> Self,
|
x_cb: impl FnOnce(Self::Ctx<'_>) -> Self,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
(ctx
|
(ctx.1.apply_logic(|c| f_cb((ctx.0.clone(), c)), |c| x_cb((ctx.0.clone(), c))))
|
||||||
.1
|
.into_expr(ctx.0.clone())
|
||||||
.apply_logic(|c| f_cb((ctx.0.clone(), c)), |c| x_cb((ctx.0.clone(), c))))
|
|
||||||
.to_expr(ctx.0.clone())
|
|
||||||
}
|
}
|
||||||
fn arg(ctx: Self::Ctx<'_>, name: &str) -> Self {
|
fn arg(ctx: Self::Ctx<'_>, name: &str) -> Self {
|
||||||
Clause::arg(ctx.clone(), name).to_expr(ctx.0.clone())
|
Clause::arg(ctx.clone(), name).into_expr(ctx.0.clone())
|
||||||
}
|
}
|
||||||
fn atom(ctx: Self::Ctx<'_>, a: Atom) -> Self {
|
fn atom(ctx: Self::Ctx<'_>, a: Atom) -> Self {
|
||||||
Clause::atom(ctx.clone(), a).to_expr(ctx.0.clone())
|
Clause::atom(ctx.clone(), a).into_expr(ctx.0.clone())
|
||||||
}
|
}
|
||||||
fn constant<'a>(
|
fn constant<'a>(ctx: Self::Ctx<'_>, name: impl IntoIterator<Item = &'a str>) -> Self {
|
||||||
ctx: Self::Ctx<'_>,
|
Clause::constant(ctx.clone(), name).into_expr(ctx.0.clone())
|
||||||
name: impl IntoIterator<Item = &'a str>,
|
|
||||||
) -> Self {
|
|
||||||
Clause::constant(ctx.clone(), name).to_expr(ctx.0.clone())
|
|
||||||
}
|
}
|
||||||
fn lambda(
|
fn lambda(ctx: Self::Ctx<'_>, name: &str, body: impl FnOnce(Self::Ctx<'_>) -> Self) -> Self {
|
||||||
ctx: Self::Ctx<'_>,
|
(ctx.1.lambda_logic(name, |c| body((ctx.0.clone(), c)))).into_expr(ctx.0.clone())
|
||||||
name: &str,
|
|
||||||
body: impl FnOnce(Self::Ctx<'_>) -> Self,
|
|
||||||
) -> Self {
|
|
||||||
(ctx.1.lambda_logic(name, |c| body((ctx.0.clone(), c))))
|
|
||||||
.to_expr(ctx.0.clone())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Generable for ClauseInst {
|
impl Generable for ClauseInst {
|
||||||
type Ctx<'a> = NortGenCtx<'a>;
|
type Ctx<'a> = NortGenCtx<'a>;
|
||||||
fn arg(ctx: Self::Ctx<'_>, name: &str) -> Self {
|
fn arg(ctx: Self::Ctx<'_>, name: &str) -> Self { Clause::arg(ctx, name).into_inst() }
|
||||||
Clause::arg(ctx, name).to_inst()
|
fn atom(ctx: Self::Ctx<'_>, a: Atom) -> Self { Clause::atom(ctx, a).into_inst() }
|
||||||
|
fn constant<'a>(ctx: Self::Ctx<'_>, name: impl IntoIterator<Item = &'a str>) -> Self {
|
||||||
|
Clause::constant(ctx, name).into_inst()
|
||||||
}
|
}
|
||||||
fn atom(ctx: Self::Ctx<'_>, a: Atom) -> Self {
|
fn lambda(ctx: Self::Ctx<'_>, name: &str, body: impl FnOnce(Self::Ctx<'_>) -> Self) -> Self {
|
||||||
Clause::atom(ctx, a).to_inst()
|
(ctx.1.lambda_logic(name, |c| body((ctx.0.clone(), c)).into_expr(ctx.0.clone())))
|
||||||
}
|
|
||||||
fn constant<'a>(
|
|
||||||
ctx: Self::Ctx<'_>,
|
|
||||||
name: impl IntoIterator<Item = &'a str>,
|
|
||||||
) -> Self {
|
|
||||||
Clause::constant(ctx, name).to_inst()
|
|
||||||
}
|
|
||||||
fn lambda(
|
|
||||||
ctx: Self::Ctx<'_>,
|
|
||||||
name: &str,
|
|
||||||
body: impl FnOnce(Self::Ctx<'_>) -> Self,
|
|
||||||
) -> Self {
|
|
||||||
(ctx
|
|
||||||
.1
|
|
||||||
.lambda_logic(name, |c| body((ctx.0.clone(), c)).to_expr(ctx.0.clone())))
|
|
||||||
.to_clsi(ctx.0.clone())
|
.to_clsi(ctx.0.clone())
|
||||||
}
|
}
|
||||||
fn apply(
|
fn apply(
|
||||||
@@ -83,8 +60,8 @@ impl Generable for ClauseInst {
|
|||||||
x: impl FnOnce(Self::Ctx<'_>) -> Self,
|
x: impl FnOnce(Self::Ctx<'_>) -> Self,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
(ctx.1.apply_logic(
|
(ctx.1.apply_logic(
|
||||||
|c| f((ctx.0.clone(), c)).to_expr(ctx.0.clone()),
|
|c| f((ctx.0.clone(), c)).into_expr(ctx.0.clone()),
|
||||||
|c| x((ctx.0.clone(), c)).to_expr(ctx.0.clone()),
|
|c| x((ctx.0.clone(), c)).into_expr(ctx.0.clone()),
|
||||||
))
|
))
|
||||||
.to_clsi(ctx.0.clone())
|
.to_clsi(ctx.0.clone())
|
||||||
}
|
}
|
||||||
@@ -93,10 +70,7 @@ impl Generable for ClauseInst {
|
|||||||
impl Generable for Clause {
|
impl Generable for Clause {
|
||||||
type Ctx<'a> = NortGenCtx<'a>;
|
type Ctx<'a> = NortGenCtx<'a>;
|
||||||
fn atom(_: Self::Ctx<'_>, a: Atom) -> Self { Clause::Atom(a) }
|
fn atom(_: Self::Ctx<'_>, a: Atom) -> Self { Clause::Atom(a) }
|
||||||
fn constant<'a>(
|
fn constant<'a>(_: Self::Ctx<'_>, name: impl IntoIterator<Item = &'a str>) -> Self {
|
||||||
_: Self::Ctx<'_>,
|
|
||||||
name: impl IntoIterator<Item = &'a str>,
|
|
||||||
) -> Self {
|
|
||||||
let sym = Sym::new(name.into_iter().map(i)).expect("Empty constant");
|
let sym = Sym::new(name.into_iter().map(i)).expect("Empty constant");
|
||||||
Clause::Constant(sym)
|
Clause::Constant(sym)
|
||||||
}
|
}
|
||||||
@@ -106,21 +80,15 @@ impl Generable for Clause {
|
|||||||
x: impl FnOnce(Self::Ctx<'_>) -> Self,
|
x: impl FnOnce(Self::Ctx<'_>) -> Self,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ctx.1.apply_logic(
|
ctx.1.apply_logic(
|
||||||
|c| f((ctx.0.clone(), c)).to_expr(ctx.0.clone()),
|
|c| f((ctx.0.clone(), c)).into_expr(ctx.0.clone()),
|
||||||
|c| x((ctx.0.clone(), c)).to_expr(ctx.0.clone()),
|
|c| x((ctx.0.clone(), c)).into_expr(ctx.0.clone()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fn arg(ctx: Self::Ctx<'_>, name: &str) -> Self {
|
fn arg(ctx: Self::Ctx<'_>, name: &str) -> Self {
|
||||||
ctx.1.arg_logic(name);
|
ctx.1.arg_logic(name);
|
||||||
Clause::LambdaArg
|
Clause::LambdaArg
|
||||||
}
|
}
|
||||||
fn lambda(
|
fn lambda(ctx: Self::Ctx<'_>, name: &str, body: impl FnOnce(Self::Ctx<'_>) -> Self) -> Self {
|
||||||
ctx: Self::Ctx<'_>,
|
ctx.1.lambda_logic(name, |c| body((ctx.0.clone(), c)).into_expr(ctx.0.clone()))
|
||||||
name: &str,
|
|
||||||
body: impl FnOnce(Self::Ctx<'_>) -> Self,
|
|
||||||
) -> Self {
|
|
||||||
ctx
|
|
||||||
.1
|
|
||||||
.lambda_logic(name, |c| body((ctx.0.clone(), c)).to_expr(ctx.0.clone()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,39 @@
|
|||||||
|
//! Impure functions that can be triggered by Orchid code when a command
|
||||||
|
//! evaluates to an atom representing a command
|
||||||
|
|
||||||
use std::any::{Any, TypeId};
|
use std::any::{Any, TypeId};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
use super::context::{Halt, RunContext};
|
use super::nort::Expr;
|
||||||
use super::error::RunError;
|
use crate::foreign::atom::Atomic;
|
||||||
use super::nort::{Clause, Expr};
|
use crate::foreign::error::RTResult;
|
||||||
use super::run::run;
|
|
||||||
use crate::foreign::atom::{Atom, Atomic};
|
|
||||||
use crate::foreign::error::ExternResult;
|
|
||||||
use crate::foreign::to_clause::ToClause;
|
use crate::foreign::to_clause::ToClause;
|
||||||
use crate::location::CodeLocation;
|
use crate::location::CodeLocation;
|
||||||
|
|
||||||
trait_set! {
|
trait_set! {
|
||||||
trait Handler = for<'a> FnMut(&'a dyn Any, CodeLocation) -> Expr;
|
trait Handler = for<'a> Fn(&'a dyn Any, CodeLocation) -> Expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A table of command handlers
|
enum HTEntry<'a> {
|
||||||
|
Handler(Box<dyn Handler + 'a>),
|
||||||
|
Forward(&'a (dyn Handler + 'a)),
|
||||||
|
}
|
||||||
|
impl<'a> AsRef<dyn Handler + 'a> for HTEntry<'a> {
|
||||||
|
fn as_ref(&self) -> &(dyn Handler + 'a) {
|
||||||
|
match self {
|
||||||
|
HTEntry::Handler(h) => &**h,
|
||||||
|
HTEntry::Forward(h) => *h,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A table of impure command handlers exposed to Orchid
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct HandlerTable<'a> {
|
pub struct HandlerTable<'a> {
|
||||||
handlers: HashMap<TypeId, Box<dyn Handler + 'a>>,
|
handlers: HashMap<TypeId, HTEntry<'a>>,
|
||||||
}
|
}
|
||||||
impl<'a> HandlerTable<'a> {
|
impl<'a> HandlerTable<'a> {
|
||||||
/// Create a new [HandlerTable]
|
/// Create a new [HandlerTable]
|
||||||
@@ -28,35 +42,25 @@ impl<'a> HandlerTable<'a> {
|
|||||||
|
|
||||||
/// Add a handler function to interpret a command and select the continuation.
|
/// Add a handler function to interpret a command and select the continuation.
|
||||||
/// See [HandlerTable#with] for a declarative option.
|
/// See [HandlerTable#with] for a declarative option.
|
||||||
pub fn register<T: 'static, R: ToClause>(
|
pub fn register<T: 'static, R: ToClause>(&mut self, f: impl for<'b> FnMut(&'b T) -> R + 'a) {
|
||||||
&mut self,
|
let cell = RefCell::new(f);
|
||||||
mut f: impl for<'b> FnMut(&'b T) -> R + 'a,
|
|
||||||
) {
|
|
||||||
let cb = move |a: &dyn Any, loc: CodeLocation| {
|
let cb = move |a: &dyn Any, loc: CodeLocation| {
|
||||||
f(a.downcast_ref().expect("found by TypeId")).to_expr(loc)
|
cell.borrow_mut()(a.downcast_ref().expect("found by TypeId")).to_expr(loc)
|
||||||
};
|
};
|
||||||
let prev = self.handlers.insert(TypeId::of::<T>(), Box::new(cb));
|
let prev = self.handlers.insert(TypeId::of::<T>(), HTEntry::Handler(Box::new(cb)));
|
||||||
assert!(prev.is_none(), "A handler for this type is already registered");
|
assert!(prev.is_none(), "A handler for this type is already registered");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a handler function to interpret a command and select the continuation.
|
/// Add a handler function to interpret a command and select the continuation.
|
||||||
/// See [HandlerTable#register] for a procedural option.
|
/// See [HandlerTable#register] for a procedural option.
|
||||||
pub fn with<T: 'static>(
|
pub fn with<T: 'static>(mut self, f: impl FnMut(&T) -> RTResult<Expr> + 'a) -> Self {
|
||||||
mut self,
|
|
||||||
f: impl FnMut(&T) -> ExternResult<Expr> + 'a,
|
|
||||||
) -> Self {
|
|
||||||
self.register(f);
|
self.register(f);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find and execute the corresponding handler for this type
|
/// Find and execute the corresponding handler for this type
|
||||||
pub fn dispatch(
|
pub fn dispatch(&self, arg: &dyn Atomic, loc: CodeLocation) -> Option<Expr> {
|
||||||
&mut self,
|
(self.handlers.get(&arg.as_any_ref().type_id())).map(|ent| ent.as_ref()(arg.as_any_ref(), loc))
|
||||||
arg: &dyn Atomic,
|
|
||||||
loc: CodeLocation,
|
|
||||||
) -> Option<Expr> {
|
|
||||||
(self.handlers.get_mut(&arg.as_any_ref().type_id()))
|
|
||||||
.map(|f| f(arg.as_any_ref(), loc))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Combine two non-overlapping handler sets
|
/// Combine two non-overlapping handler sets
|
||||||
@@ -68,30 +72,45 @@ impl<'a> HandlerTable<'a> {
|
|||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add entries that forward requests to a borrowed non-overlapping handler
|
||||||
|
/// set
|
||||||
|
pub fn link<'b: 'a>(mut self, other: &'b HandlerTable<'b>) -> Self {
|
||||||
|
for (key, value) in other.handlers.iter() {
|
||||||
|
let prev = self.handlers.insert(*key, HTEntry::Forward(value.as_ref()));
|
||||||
|
assert!(prev.is_none(), "Duplicate handlers")
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [run] orchid code, executing any commands it returns using the specified
|
#[cfg(test)]
|
||||||
/// [Handler]s.
|
#[allow(unconditional_recursion)]
|
||||||
pub fn run_handler(
|
#[allow(clippy::ptr_arg)]
|
||||||
mut state: Expr,
|
mod test {
|
||||||
handlers: &mut HandlerTable,
|
use std::marker::PhantomData;
|
||||||
mut ctx: RunContext,
|
|
||||||
) -> Result<Halt, RunError> {
|
use super::HandlerTable;
|
||||||
loop {
|
|
||||||
let halt = run(state, ctx.clone())?;
|
/// Ensure that the method I use to verify covariance actually passes with
|
||||||
state = halt.state;
|
/// covariant and fails with invariant
|
||||||
ctx.use_gas(halt.gas.unwrap_or(0));
|
///
|
||||||
let state_cls = state.cls();
|
/// The failing case:
|
||||||
if let Clause::Atom(Atom(a)) = &*state_cls {
|
/// ```
|
||||||
if let Some(res) = handlers.dispatch(a.as_ref(), state.location()) {
|
/// struct Cov2<'a>(PhantomData<&'a mut &'a ()>);
|
||||||
drop(state_cls);
|
/// fn fail<'a>(_c: &Cov2<'a>, _s: &'a String) { fail(_c, &String::new()) }
|
||||||
state = res;
|
/// ```
|
||||||
continue;
|
#[allow(unused)]
|
||||||
}
|
fn covariant_control() {
|
||||||
}
|
struct Cov<'a>(PhantomData<&'a ()>);
|
||||||
if halt.inert || ctx.no_gas() {
|
fn pass<'a>(_c: &Cov<'a>, _s: &'a String) { pass(_c, &String::new()) }
|
||||||
drop(state_cls);
|
}
|
||||||
break Ok(Halt { gas: ctx.gas, inert: halt.inert, state });
|
|
||||||
}
|
/// The &mut ensures that 'a in the two functions must be disjoint, and that
|
||||||
|
/// ht must outlive both. For this to compile, Rust has to cast ht to the
|
||||||
|
/// shorter lifetimes, ensuring covariance
|
||||||
|
#[allow(unused)]
|
||||||
|
fn assert_covariant() {
|
||||||
|
fn pass<'a>(_ht: HandlerTable<'a>, _s: &'a String) { pass(_ht, &String::new()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
//! functions to interact with Orchid code
|
//! functions to execute Orchid code
|
||||||
pub mod apply;
|
mod apply;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod gen_nort;
|
pub mod gen_nort;
|
||||||
pub mod handler;
|
pub mod handler;
|
||||||
pub mod nort_builder;
|
|
||||||
pub mod nort;
|
pub mod nort;
|
||||||
|
pub mod nort_builder;
|
||||||
pub(crate) mod path_set;
|
pub(crate) mod path_set;
|
||||||
pub mod run;
|
pub mod run;
|
||||||
|
|||||||
@@ -13,25 +13,23 @@
|
|||||||
//! function calls store multiple arguments in a deque.
|
//! function calls store multiple arguments in a deque.
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::DerefMut;
|
||||||
use std::sync::{Arc, Mutex, TryLockError};
|
use std::sync::{Arc, Mutex, MutexGuard, TryLockError};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use super::error::RunError;
|
|
||||||
use super::path_set::PathSet;
|
use super::path_set::PathSet;
|
||||||
use crate::foreign::atom::Atom;
|
use crate::foreign::atom::Atom;
|
||||||
#[allow(unused)] // for doc
|
#[allow(unused)] // for doc
|
||||||
use crate::foreign::atom::Atomic;
|
use crate::foreign::atom::Atomic;
|
||||||
use crate::foreign::error::ExternResult;
|
use crate::foreign::error::{RTErrorObj, RTResult};
|
||||||
use crate::foreign::try_from_expr::TryFromExpr;
|
use crate::foreign::try_from_expr::TryFromExpr;
|
||||||
use crate::location::CodeLocation;
|
use crate::location::CodeLocation;
|
||||||
use crate::name::Sym;
|
use crate::name::Sym;
|
||||||
#[allow(unused)] // for doc
|
#[allow(unused)] // for doc
|
||||||
use crate::parse::parsed;
|
use crate::parse::parsed;
|
||||||
use crate::utils::ddispatch::request;
|
use crate::utils::ddispatch::request;
|
||||||
use crate::utils::take_with_output::take_with_output;
|
|
||||||
|
|
||||||
/// Kinda like [AsMut] except it supports a guard
|
/// Kinda like [AsMut] except it supports a guard
|
||||||
pub(crate) trait AsDerefMut<T> {
|
pub(crate) trait AsDerefMut<T> {
|
||||||
@@ -48,19 +46,17 @@ pub struct Expr {
|
|||||||
}
|
}
|
||||||
impl Expr {
|
impl Expr {
|
||||||
/// Constructor
|
/// Constructor
|
||||||
pub fn new(clause: ClauseInst, location: CodeLocation) -> Self {
|
pub fn new(clause: ClauseInst, location: CodeLocation) -> Self { Self { clause, location } }
|
||||||
Self { clause, location }
|
|
||||||
}
|
|
||||||
/// Obtain the location of the expression
|
/// Obtain the location of the expression
|
||||||
pub fn location(&self) -> CodeLocation { self.location.clone() }
|
pub fn location(&self) -> CodeLocation { self.location.clone() }
|
||||||
|
|
||||||
/// Convert into any type that implements [TryFromExpr]. Calls to this
|
/// Convert into any type that implements [TryFromExpr]. Calls to this
|
||||||
/// function are generated wherever a conversion is elided in an extern
|
/// function are generated wherever a conversion is elided in an extern
|
||||||
/// function.
|
/// function.
|
||||||
pub fn downcast<T: TryFromExpr>(self) -> ExternResult<T> {
|
pub fn downcast<T: TryFromExpr>(self) -> RTResult<T> {
|
||||||
let Expr { mut clause, location } = self;
|
let Expr { mut clause, location } = self;
|
||||||
loop {
|
loop {
|
||||||
let cls_deref = clause.cls();
|
let cls_deref = clause.cls_mut();
|
||||||
match &*cls_deref {
|
match &*cls_deref {
|
||||||
Clause::Identity(alt) => {
|
Clause::Identity(alt) => {
|
||||||
let temp = alt.clone();
|
let temp = alt.clone();
|
||||||
@@ -79,22 +75,16 @@ impl Expr {
|
|||||||
/// returning [Some]
|
/// returning [Some]
|
||||||
///
|
///
|
||||||
/// See also [parsed::Expr::search_all]
|
/// See also [parsed::Expr::search_all]
|
||||||
pub fn search_all<T>(
|
pub fn search_all<T>(&self, predicate: &mut impl FnMut(&Self) -> Option<T>) -> Option<T> {
|
||||||
&self,
|
|
||||||
predicate: &mut impl FnMut(&Self) -> Option<T>,
|
|
||||||
) -> Option<T> {
|
|
||||||
if let Some(t) = predicate(self) {
|
if let Some(t) = predicate(self) {
|
||||||
return Some(t);
|
return Some(t);
|
||||||
}
|
}
|
||||||
self.clause.inspect(|c| match c {
|
self.clause.inspect(|c| match c {
|
||||||
Clause::Identity(_alt) => unreachable!("Handled by inspect"),
|
Clause::Identity(_alt) => unreachable!("Handled by inspect"),
|
||||||
Clause::Apply { f, x } => (f.search_all(predicate))
|
Clause::Apply { f, x } =>
|
||||||
.or_else(|| x.iter().find_map(|x| x.search_all(predicate))),
|
(f.search_all(predicate)).or_else(|| x.iter().find_map(|x| x.search_all(predicate))),
|
||||||
Clause::Lambda { body, .. } => body.search_all(predicate),
|
Clause::Lambda { body, .. } => body.search_all(predicate),
|
||||||
Clause::Constant(_)
|
Clause::Constant(_) | Clause::LambdaArg | Clause::Atom(_) | Clause::Bottom(_) => None,
|
||||||
| Clause::LambdaArg
|
|
||||||
| Clause::Atom(_)
|
|
||||||
| Clause::Bottom(_) => None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,46 +92,27 @@ impl Expr {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn clsi(&self) -> ClauseInst { self.clause.clone() }
|
pub fn clsi(&self) -> ClauseInst { self.clause.clone() }
|
||||||
|
|
||||||
/// Readonly access to the [Clause]
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// if the clause is already borrowed
|
|
||||||
#[must_use]
|
|
||||||
pub fn cls(&self) -> impl Deref<Target = Clause> + '_ { self.clause.cls() }
|
|
||||||
|
|
||||||
/// Read-Write access to the [Clause]
|
/// Read-Write access to the [Clause]
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// if the clause is already borrowed
|
/// if the clause is already borrowed
|
||||||
#[must_use]
|
pub fn cls_mut(&self) -> MutexGuard<'_, Clause> { self.clause.cls_mut() }
|
||||||
pub fn cls_mut(&self) -> impl DerefMut<Target = Clause> + '_ {
|
|
||||||
self.clause.cls_mut()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Expr {
|
impl fmt::Debug for Expr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{:?}@{}", self.clause, self.location)
|
write!(f, "{:?}@{}", self.clause, self.location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Expr {
|
impl fmt::Display for Expr {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.clause) }
|
||||||
write!(f, "{}", self.clause)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsDerefMut<Clause> for Expr {
|
impl AsDerefMut<Clause> for Expr {
|
||||||
fn as_deref_mut(&mut self) -> impl DerefMut<Target = Clause> + '_ {
|
fn as_deref_mut(&mut self) -> impl DerefMut<Target = Clause> + '_ { self.clause.cls_mut() }
|
||||||
self.clause.cls_mut()
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// [ExprInst::with_literal] produces this marker unit to indicate that the
|
|
||||||
/// expression is not a literal
|
|
||||||
pub struct NotALiteral;
|
|
||||||
|
|
||||||
/// A wrapper around expressions to handle their multiple occurences in
|
/// A wrapper around expressions to handle their multiple occurences in
|
||||||
/// the tree together
|
/// the tree together
|
||||||
@@ -159,91 +130,17 @@ impl ClauseInst {
|
|||||||
Arc::try_unwrap(self.0).map(|c| c.into_inner().unwrap()).map_err(Self)
|
Arc::try_unwrap(self.0).map(|c| c.into_inner().unwrap()).map_err(Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read-only access to the shared clause instance
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// if the clause is already borrowed in read-write mode
|
|
||||||
#[must_use]
|
|
||||||
pub fn cls(&self) -> impl Deref<Target = Clause> + '_ {
|
|
||||||
self.0.lock().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read-Write access to the shared clause instance
|
/// Read-Write access to the shared clause instance
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// if the clause is already borrowed, this will block until it is released.
|
||||||
///
|
pub fn cls_mut(&self) -> MutexGuard<'_, Clause> { self.0.lock().unwrap() }
|
||||||
/// if the clause is already borrowed
|
|
||||||
#[must_use]
|
|
||||||
pub fn cls_mut(&self) -> impl DerefMut<Target = Clause> + '_ {
|
|
||||||
self.0.lock().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Call a normalization function on the expression. The expr is
|
|
||||||
/// updated with the new clause which affects all copies of it
|
|
||||||
/// across the tree.
|
|
||||||
///
|
|
||||||
/// This function bypasses and collapses identities, but calling it in a plain
|
|
||||||
/// loop intermittently re-acquires the mutex, and looping inside of it breaks
|
|
||||||
/// identity collapsing. [ClauseInst::try_normalize_trampoline] solves these
|
|
||||||
/// problems.
|
|
||||||
pub fn try_normalize<T>(
|
|
||||||
&self,
|
|
||||||
mapper: impl FnOnce(Clause) -> Result<(Clause, T), RunError>,
|
|
||||||
) -> Result<(Self, T), RunError> {
|
|
||||||
enum Report<T> {
|
|
||||||
Nested(ClauseInst, T),
|
|
||||||
Plain(T),
|
|
||||||
}
|
|
||||||
let ret = take_with_output(&mut *self.cls_mut(), |clause| match &clause {
|
|
||||||
// don't modify identities, instead update and return the nested clause
|
|
||||||
Clause::Identity(alt) => match alt.try_normalize(mapper) {
|
|
||||||
Ok((nested, t)) => (clause, Ok(Report::Nested(nested, t))),
|
|
||||||
Err(e) => (Clause::Bottom(e.clone()), Err(e)),
|
|
||||||
},
|
|
||||||
_ => match mapper(clause) {
|
|
||||||
Err(e) => (Clause::Bottom(e.clone()), Err(e)),
|
|
||||||
Ok((clause, t)) => (clause, Ok(Report::Plain(t))),
|
|
||||||
},
|
|
||||||
})?;
|
|
||||||
Ok(match ret {
|
|
||||||
Report::Nested(nested, t) => (nested, t),
|
|
||||||
Report::Plain(t) => (self.clone(), t),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Repeatedly call a normalization function on the held clause, switching
|
|
||||||
/// [ClauseInst] values as needed to ensure that
|
|
||||||
pub fn try_normalize_trampoline<T>(
|
|
||||||
mut self,
|
|
||||||
mut mapper: impl FnMut(Clause) -> Result<(Clause, Option<T>), RunError>,
|
|
||||||
) -> Result<(Self, T), RunError> {
|
|
||||||
loop {
|
|
||||||
let (next, exit) = self.try_normalize(|mut cls| {
|
|
||||||
loop {
|
|
||||||
if matches!(cls, Clause::Identity(_)) {
|
|
||||||
break Ok((cls, None));
|
|
||||||
}
|
|
||||||
let (next, exit) = mapper(cls)?;
|
|
||||||
if let Some(exit) = exit {
|
|
||||||
break Ok((next, Some(exit)));
|
|
||||||
}
|
|
||||||
cls = next;
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
if let Some(exit) = exit {
|
|
||||||
break Ok((next, exit));
|
|
||||||
}
|
|
||||||
self = next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Call a predicate on the clause, returning whatever the
|
/// Call a predicate on the clause, returning whatever the
|
||||||
/// predicate returns. This is a convenience function for reaching
|
/// predicate returns. This is a convenience function for reaching
|
||||||
/// through the [Mutex]. The clause will never be [Clause::Identity].
|
/// through the [Mutex]. The clause will never be [Clause::Identity].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn inspect<T>(&self, predicate: impl FnOnce(&Clause) -> T) -> T {
|
pub fn inspect<T>(&self, predicate: impl FnOnce(&Clause) -> T) -> T {
|
||||||
match &*self.cls() {
|
match &*self.cls_mut() {
|
||||||
Clause::Identity(sub) => sub.inspect(predicate),
|
Clause::Identity(sub) => sub.inspect(predicate),
|
||||||
x => predicate(x),
|
x => predicate(x),
|
||||||
}
|
}
|
||||||
@@ -253,7 +150,7 @@ impl ClauseInst {
|
|||||||
/// If it's not an atomic, fail the request automatically.
|
/// If it's not an atomic, fail the request automatically.
|
||||||
#[must_use = "your request might not have succeeded"]
|
#[must_use = "your request might not have succeeded"]
|
||||||
pub fn request<T: 'static>(&self) -> Option<T> {
|
pub fn request<T: 'static>(&self) -> Option<T> {
|
||||||
match &*self.cls() {
|
match &*self.cls_mut() {
|
||||||
Clause::Atom(a) => request(&*a.0),
|
Clause::Atom(a) => request(&*a.0),
|
||||||
Clause::Identity(alt) => alt.request(),
|
Clause::Identity(alt) => alt.request(),
|
||||||
_ => None,
|
_ => None,
|
||||||
@@ -261,7 +158,7 @@ impl ClauseInst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Associate a location with this clause
|
/// Associate a location with this clause
|
||||||
pub fn to_expr(self, location: CodeLocation) -> Expr {
|
pub fn into_expr(self, location: CodeLocation) -> Expr {
|
||||||
Expr { clause: self.clone(), location: location.clone() }
|
Expr { clause: self.clone(), location: location.clone() }
|
||||||
}
|
}
|
||||||
/// Check ahead-of-time if this clause contains an atom. Calls
|
/// Check ahead-of-time if this clause contains an atom. Calls
|
||||||
@@ -269,7 +166,7 @@ impl ClauseInst {
|
|||||||
///
|
///
|
||||||
/// Since atoms cannot become normalizable, if this is true and previous
|
/// Since atoms cannot become normalizable, if this is true and previous
|
||||||
/// normalization failed, the atom is known to be in normal form.
|
/// normalization failed, the atom is known to be in normal form.
|
||||||
pub fn is_atom(&self) -> bool { matches!(&*self.cls(), Clause::Atom(_)) }
|
pub fn is_atom(&self) -> bool { matches!(&*self.cls_mut(), Clause::Atom(_)) }
|
||||||
|
|
||||||
/// Tries to unwrap the [Arc]. If that fails, clones it field by field.
|
/// Tries to unwrap the [Arc]. If that fails, clones it field by field.
|
||||||
/// If it's a [Clause::Atom] which cannot be cloned, wraps it in a
|
/// If it's a [Clause::Atom] which cannot be cloned, wraps it in a
|
||||||
@@ -278,21 +175,24 @@ impl ClauseInst {
|
|||||||
/// Implementation of [crate::foreign::to_clause::ToClause::to_clause]. The
|
/// Implementation of [crate::foreign::to_clause::ToClause::to_clause]. The
|
||||||
/// trait is more general so it requires a location which this one doesn't.
|
/// trait is more general so it requires a location which this one doesn't.
|
||||||
pub fn into_cls(self) -> Clause {
|
pub fn into_cls(self) -> Clause {
|
||||||
self.try_unwrap().unwrap_or_else(|clsi| match &*clsi.cls() {
|
self.try_unwrap().unwrap_or_else(|clsi| match &*clsi.cls_mut() {
|
||||||
Clause::Apply { f, x } => Clause::Apply { f: f.clone(), x: x.clone() },
|
Clause::Apply { f, x } => Clause::Apply { f: f.clone(), x: x.clone() },
|
||||||
Clause::Atom(_) => Clause::Identity(clsi.clone()),
|
Clause::Atom(_) => Clause::Identity(clsi.clone()),
|
||||||
Clause::Bottom(e) => Clause::Bottom(e.clone()),
|
Clause::Bottom(e) => Clause::Bottom(e.clone()),
|
||||||
Clause::Constant(c) => Clause::Constant(c.clone()),
|
Clause::Constant(c) => Clause::Constant(c.clone()),
|
||||||
Clause::Identity(sub) => Clause::Identity(sub.clone()),
|
Clause::Identity(sub) => Clause::Identity(sub.clone()),
|
||||||
Clause::Lambda { args, body } =>
|
Clause::Lambda { args, body } => Clause::Lambda { args: args.clone(), body: body.clone() },
|
||||||
Clause::Lambda { args: args.clone(), body: body.clone() },
|
|
||||||
Clause::LambdaArg => Clause::LambdaArg,
|
Clause::LambdaArg => Clause::LambdaArg,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decides if this clause is the exact same instance as another. Most useful
|
||||||
|
/// to detect potential deadlocks.
|
||||||
|
pub fn is_same(&self, other: &Self) -> bool { Arc::ptr_eq(&self.0, &other.0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for ClauseInst {
|
impl fmt::Debug for ClauseInst {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self.0.try_lock() {
|
match self.0.try_lock() {
|
||||||
Ok(expr) => write!(f, "{expr:?}"),
|
Ok(expr) => write!(f, "{expr:?}"),
|
||||||
Err(TryLockError::Poisoned(_)) => write!(f, "<poisoned>"),
|
Err(TryLockError::Poisoned(_)) => write!(f, "<poisoned>"),
|
||||||
@@ -301,8 +201,8 @@ impl Debug for ClauseInst {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ClauseInst {
|
impl fmt::Display for ClauseInst {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self.0.try_lock() {
|
match self.0.try_lock() {
|
||||||
Ok(expr) => write!(f, "{expr}"),
|
Ok(expr) => write!(f, "{expr}"),
|
||||||
Err(TryLockError::Poisoned(_)) => write!(f, "<poisoned>"),
|
Err(TryLockError::Poisoned(_)) => write!(f, "<poisoned>"),
|
||||||
@@ -312,16 +212,14 @@ impl Display for ClauseInst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AsDerefMut<Clause> for ClauseInst {
|
impl AsDerefMut<Clause> for ClauseInst {
|
||||||
fn as_deref_mut(&mut self) -> impl DerefMut<Target = Clause> + '_ {
|
fn as_deref_mut(&mut self) -> impl DerefMut<Target = Clause> + '_ { self.cls_mut() }
|
||||||
self.cls_mut()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Distinct types of expressions recognized by the interpreter
|
/// Distinct types of expressions recognized by the interpreter
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Clause {
|
pub enum Clause {
|
||||||
/// An expression that causes an error
|
/// An expression that causes an error
|
||||||
Bottom(RunError),
|
Bottom(RTErrorObj),
|
||||||
/// Indicates that this [ClauseInst] has the same value as the other
|
/// Indicates that this [ClauseInst] has the same value as the other
|
||||||
/// [ClauseInst]. This has two benefits;
|
/// [ClauseInst]. This has two benefits;
|
||||||
///
|
///
|
||||||
@@ -358,21 +256,18 @@ pub enum Clause {
|
|||||||
}
|
}
|
||||||
impl Clause {
|
impl Clause {
|
||||||
/// Wrap a clause in a refcounted lock
|
/// Wrap a clause in a refcounted lock
|
||||||
pub fn to_inst(self) -> ClauseInst { ClauseInst::new(self) }
|
pub fn into_inst(self) -> ClauseInst { ClauseInst::new(self) }
|
||||||
/// Wrap a clause in an expression.
|
/// Wrap a clause in an expression.
|
||||||
pub fn to_expr(self, location: CodeLocation) -> Expr {
|
pub fn into_expr(self, location: CodeLocation) -> Expr { self.into_inst().into_expr(location) }
|
||||||
self.to_inst().to_expr(location)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Clause {
|
impl fmt::Display for Clause {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Clause::Atom(a) => write!(f, "{a:?}"),
|
Clause::Atom(a) => write!(f, "{a:?}"),
|
||||||
Clause::Bottom(err) => write!(f, "bottom({err})"),
|
Clause::Bottom(err) => write!(f, "bottom({err})"),
|
||||||
Clause::LambdaArg => write!(f, "arg"),
|
Clause::LambdaArg => write!(f, "arg"),
|
||||||
Clause::Apply { f: fun, x } =>
|
Clause::Apply { f: fun, x } => write!(f, "({fun} {})", x.iter().join(" ")),
|
||||||
write!(f, "({fun} {})", x.iter().join(" ")),
|
|
||||||
Clause::Lambda { args, body } => match args {
|
Clause::Lambda { args, body } => match args {
|
||||||
Some(path) => write!(f, "[\\{path}.{body}]"),
|
Some(path) => write!(f, "[\\{path}.{body}]"),
|
||||||
None => write!(f, "[\\_.{body}]"),
|
None => write!(f, "[\\_.{body}]"),
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! Helper for generating the interpreter's internal representation
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
@@ -34,8 +36,7 @@ impl ArgCollector {
|
|||||||
/// the callback it returns is called on every lambda ancestor's associated
|
/// the callback it returns is called on every lambda ancestor's associated
|
||||||
/// data from closest to outermost ancestor. The first lambda where this
|
/// data from closest to outermost ancestor. The first lambda where this
|
||||||
/// callback returns true is considered to own the argument.
|
/// callback returns true is considered to own the argument.
|
||||||
pub type LambdaPicker<'a, T, U> =
|
pub type LambdaPicker<'a, T, U> = &'a dyn for<'b> Fn(&'b U) -> Box<dyn FnMut(&T) -> bool + 'b>;
|
||||||
&'a dyn for<'b> Fn(&'b U) -> Box<dyn FnMut(&T) -> bool + 'b>;
|
|
||||||
|
|
||||||
/// Bundle of information passed down through recursive fnuctions to instantiate
|
/// Bundle of information passed down through recursive fnuctions to instantiate
|
||||||
/// runtime [Expr], [super::nort::ClauseInst] or [Clause].
|
/// runtime [Expr], [super::nort::ClauseInst] or [Clause].
|
||||||
@@ -83,7 +84,7 @@ impl<'a, T: ?Sized, U: ?Sized> NortBuilder<'a, T, U> {
|
|||||||
IntGenData::Apply(_) => panic!("This is removed after handling"),
|
IntGenData::Apply(_) => panic!("This is removed after handling"),
|
||||||
IntGenData::Lambda(n, rc) => match lambda_chk(n) {
|
IntGenData::Lambda(n, rc) => match lambda_chk(n) {
|
||||||
false => Ok(path),
|
false => Ok(path),
|
||||||
true => Err((path, *rc))
|
true => Err((path, *rc)),
|
||||||
},
|
},
|
||||||
IntGenData::AppArg(n) => Ok(pushed(path, Some(*n))),
|
IntGenData::AppArg(n) => Ok(pushed(path, Some(*n))),
|
||||||
IntGenData::AppF => Ok(pushed(path, None)),
|
IntGenData::AppF => Ok(pushed(path, None)),
|
||||||
@@ -100,11 +101,7 @@ impl<'a, T: ?Sized, U: ?Sized> NortBuilder<'a, T, U> {
|
|||||||
/// Push a stackframe corresponding to a lambda expression, build the body,
|
/// Push a stackframe corresponding to a lambda expression, build the body,
|
||||||
/// then record the path set collected by [NortBuilder::arg_logic] calls
|
/// then record the path set collected by [NortBuilder::arg_logic] calls
|
||||||
/// within the body.
|
/// within the body.
|
||||||
pub fn lambda_logic(
|
pub fn lambda_logic(self, name: &T, body: impl FnOnce(NortBuilder<T, U>) -> Expr) -> Clause {
|
||||||
self,
|
|
||||||
name: &T,
|
|
||||||
body: impl FnOnce(NortBuilder<T, U>) -> Expr,
|
|
||||||
) -> Clause {
|
|
||||||
let coll = ArgCollector::new();
|
let coll = ArgCollector::new();
|
||||||
let frame = IntGenData::Lambda(name, &coll.0);
|
let frame = IntGenData::Lambda(name, &coll.0);
|
||||||
let body = self.non_app_step(|ctx| body(ctx.push(frame)));
|
let body = self.non_app_step(|ctx| body(ctx.push(frame)));
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use crate::utils::join::join_maps;
|
|||||||
/// function. If [Some(n)], it steps to the `n`th _last_ argument.
|
/// function. If [Some(n)], it steps to the `n`th _last_ argument.
|
||||||
pub type Step = Option<usize>;
|
pub type Step = Option<usize>;
|
||||||
fn print_step(step: Step) -> String {
|
fn print_step(step: Step) -> String {
|
||||||
if let Some(n) = step { format!("{n}>") } else { "f>".to_string() }
|
if let Some(n) = step { format!("{n}") } else { "f".to_string() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A branching path selecting some placeholders (but at least one) in a Lambda
|
/// A branching path selecting some placeholders (but at least one) in a Lambda
|
||||||
@@ -59,10 +59,7 @@ impl PathSet {
|
|||||||
};
|
};
|
||||||
let short_len = short.steps.len();
|
let short_len = short.steps.len();
|
||||||
let long_len = long.steps.len();
|
let long_len = long.steps.len();
|
||||||
let match_len = (short.steps.iter())
|
let match_len = (short.steps.iter()).zip(long.steps.iter()).take_while(|(a, b)| a == b).count();
|
||||||
.zip(long.steps.iter())
|
|
||||||
.take_while(|(a, b)| a == b)
|
|
||||||
.count();
|
|
||||||
// fact: match_len <= short_len <= long_len
|
// fact: match_len <= short_len <= long_len
|
||||||
if short_len == match_len && match_len == long_len {
|
if short_len == match_len && match_len == long_len {
|
||||||
// implies match_len == short_len == long_len
|
// implies match_len == short_len == long_len
|
||||||
@@ -71,10 +68,8 @@ impl PathSet {
|
|||||||
(Some(_), None) | (None, Some(_)) => {
|
(Some(_), None) | (None, Some(_)) => {
|
||||||
panic!("One of these paths is faulty")
|
panic!("One of these paths is faulty")
|
||||||
},
|
},
|
||||||
(Some(s), Some(l)) => Self::branch(
|
(Some(s), Some(l)) =>
|
||||||
short.steps.iter().cloned(),
|
Self::branch(short.steps.iter().cloned(), join_maps(s, l, |_, l, r| l.overlay(r))),
|
||||||
join_maps(s, l, |_, l, r| l.overlay(r)),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
} else if short_len == match_len {
|
} else if short_len == match_len {
|
||||||
// implies match_len == short_len < long_len
|
// implies match_len == short_len < long_len
|
||||||
@@ -102,10 +97,7 @@ impl PathSet {
|
|||||||
let new_short = Self { next: short.next.clone(), steps: new_short_steps };
|
let new_short = Self { next: short.next.clone(), steps: new_short_steps };
|
||||||
let new_long_steps = long.steps.split_off(match_len + 1);
|
let new_long_steps = long.steps.split_off(match_len + 1);
|
||||||
let new_long = Self { next: long.next.clone(), steps: new_long_steps };
|
let new_long = Self { next: long.next.clone(), steps: new_long_steps };
|
||||||
Self::branch(short.steps, [
|
Self::branch(short.steps, [(short_last, new_short), (long.steps[match_len], new_long)])
|
||||||
(short_last, new_short),
|
|
||||||
(long.steps[match_len], new_long),
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,22 +111,25 @@ impl PathSet {
|
|||||||
|
|
||||||
impl fmt::Display for PathSet {
|
impl fmt::Display for PathSet {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let step_s = self.steps.iter().copied().map(print_step).join("");
|
let step_s = self.steps.iter().copied().map(print_step).join(">");
|
||||||
match &self.next {
|
match &self.next {
|
||||||
Some(conts) => {
|
Some(conts) => {
|
||||||
let opts =
|
let opts = (conts.iter())
|
||||||
conts.iter().map(|(h, t)| format!("{}{t}", print_step(*h))).join("|");
|
.sorted_unstable_by_key(|(k, _)| k.map_or(0, |n| n + 1))
|
||||||
write!(f, "{step_s}({opts})")
|
.map(|(h, t)| format!("{}>{t}", print_step(*h)))
|
||||||
|
.join("|");
|
||||||
|
if !step_s.is_empty() {
|
||||||
|
write!(f, "{step_s}>")?;
|
||||||
|
}
|
||||||
|
write!(f, "({opts})")
|
||||||
},
|
},
|
||||||
None => write!(f, "{step_s}x"),
|
None => write!(f, "{step_s}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for PathSet {
|
impl fmt::Debug for PathSet {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "PathSet({self})") }
|
||||||
write!(f, "PathSet({self})")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -146,7 +141,7 @@ mod tests {
|
|||||||
let ps1 = PathSet { next: None, steps: VecDeque::from([Some(2), None]) };
|
let ps1 = PathSet { next: None, steps: VecDeque::from([Some(2), None]) };
|
||||||
let ps2 = PathSet { next: None, steps: VecDeque::from([Some(3), Some(1)]) };
|
let ps2 = PathSet { next: None, steps: VecDeque::from([Some(3), Some(1)]) };
|
||||||
let sum = ps1.clone().overlay(ps2.clone());
|
let sum = ps1.clone().overlay(ps2.clone());
|
||||||
assert_eq!(format!("{sum}"), "(2>f>x|3>1>x)");
|
assert_eq!(format!("{sum}"), "(2>f|3>1)");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extend_scaffold() -> PathSet {
|
fn extend_scaffold() -> PathSet {
|
||||||
|
|||||||
@@ -1,129 +1,249 @@
|
|||||||
use std::mem;
|
//! Executes Orchid code
|
||||||
|
|
||||||
use super::context::{Halt, RunContext};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::sync::{MutexGuard, TryLockError};
|
||||||
|
use std::{fmt, mem};
|
||||||
|
|
||||||
|
use bound::Bound;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use super::context::{Halt, RunEnv, RunParams};
|
||||||
use super::error::RunError;
|
use super::error::RunError;
|
||||||
use super::nort::{Clause, Expr};
|
use super::nort::{Clause, Expr};
|
||||||
use crate::foreign::atom::{AtomicReturn, RunData};
|
use crate::foreign::atom::{AtomicReturn, RunData};
|
||||||
use crate::foreign::error::ExternError;
|
use crate::foreign::error::{RTError, RTErrorObj};
|
||||||
use crate::interpreter::apply::{apply_as_atom, substitute};
|
use crate::interpreter::apply::{apply_as_atom, substitute};
|
||||||
use crate::interpreter::error::{strace, MissingSymbol, StackOverflow};
|
use crate::location::CodeLocation;
|
||||||
use crate::utils::pure_seq::pushed;
|
use crate::utils::take_with_output::take_with_output;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Stackframe {
|
||||||
|
expr: Expr,
|
||||||
|
cls: Bound<MutexGuard<'static, Clause>, Expr>,
|
||||||
|
}
|
||||||
|
impl Stackframe {
|
||||||
|
pub fn new(expr: Expr) -> Option<Self> {
|
||||||
|
match Bound::try_new(expr.clone(), |e| e.clause.0.try_lock()) {
|
||||||
|
Ok(cls) => Some(Stackframe { cls, expr }),
|
||||||
|
Err(bound_e) if matches!(bound_e.wrapped(), TryLockError::WouldBlock) => None,
|
||||||
|
Err(bound_e) => panic!("{:?}", bound_e.wrapped()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn wait_new(expr: Expr) -> Self {
|
||||||
|
Self { cls: Bound::new(expr.clone(), |e| e.clause.0.lock().unwrap()), expr }
|
||||||
|
}
|
||||||
|
pub fn record_cycle(&mut self) -> RTErrorObj {
|
||||||
|
let err = CyclicalExpression(self.expr.clone()).pack();
|
||||||
|
*self.cls = Clause::Bottom(err.clone());
|
||||||
|
err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Deref for Stackframe {
|
||||||
|
type Target = Clause;
|
||||||
|
fn deref(&self) -> &Self::Target { &self.cls }
|
||||||
|
}
|
||||||
|
impl DerefMut for Stackframe {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.cls }
|
||||||
|
}
|
||||||
|
impl fmt::Display for Stackframe {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}\n at {}", *self.cls, self.expr.location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Interpreter state when processing was interrupted
|
/// Interpreter state when processing was interrupted
|
||||||
#[derive(Debug, Clone)]
|
pub struct State<'a> {
|
||||||
pub struct Interrupted {
|
stack: Vec<Stackframe>,
|
||||||
/// Cached soft stack to save the interpreter having to rebuild it from the
|
popped: Option<Expr>,
|
||||||
/// bottom.
|
env: &'a RunEnv<'a>,
|
||||||
pub stack: Vec<Expr>,
|
|
||||||
}
|
}
|
||||||
impl Interrupted {
|
impl<'a> State<'a> {
|
||||||
/// Continue processing where it was interrupted
|
/// Create a new trivial state with a specified stack size and a single
|
||||||
pub fn resume(self, ctx: RunContext) -> Result<Halt, RunError> { run_stack(self.stack, ctx) }
|
/// element on the stack
|
||||||
|
fn new(base: Expr, env: &'a RunEnv<'a>) -> Self {
|
||||||
|
let stack = vec![Stackframe::new(base).expect("Initial state should not be locked")];
|
||||||
|
State { stack, popped: None, env }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Normalize an expression using beta reduction with memoization
|
/// Try to push an expression on the stack, raise appropriate errors if the
|
||||||
pub fn run(expr: Expr, ctx: RunContext) -> Result<Halt, RunError> {
|
/// expression is already on the stack (and thus references itself), or if the
|
||||||
let mut v = Vec::with_capacity(1000);
|
/// stack now exceeds the pre-defined height
|
||||||
v.push(expr);
|
fn push_expr(&'_ mut self, expr: Expr, params: &RunParams) -> Result<(), RunError<'a>> {
|
||||||
run_stack(v, ctx)
|
let sf = match Stackframe::new(expr.clone()) {
|
||||||
|
Some(sf) => sf,
|
||||||
|
None => match self.stack.iter_mut().rev().find(|sf| sf.expr.clause.is_same(&expr.clause)) {
|
||||||
|
None => Stackframe::wait_new(expr),
|
||||||
|
Some(sf) => return Err(RunError::Extern(sf.record_cycle())),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.stack.push(sf);
|
||||||
|
if params.stack < self.stack.len() {
|
||||||
|
let so = StackOverflow(self.stack.iter().map(|sf| sf.expr.clone()).collect());
|
||||||
|
return Err(RunError::Extern(so.pack()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_stack(mut stack: Vec<Expr>, mut ctx: RunContext) -> Result<Halt, RunError> {
|
/// Process this state until it either completes, runs out of gas as specified
|
||||||
let mut expr = stack.pop().expect("Empty stack");
|
/// in the context, or produces an error.
|
||||||
let mut popped = false;
|
pub fn run(mut self, params: &mut RunParams) -> Result<Halt, RunError<'a>> {
|
||||||
loop {
|
loop {
|
||||||
// print!("Now running {expr}");
|
if params.no_gas() {
|
||||||
// let trace = strace(&stack);
|
return Err(RunError::Interrupted(self));
|
||||||
// if trace.is_empty() {
|
|
||||||
// println!("\n")
|
|
||||||
// } else {
|
|
||||||
// println!("\n{trace}\n")
|
|
||||||
// };
|
|
||||||
if ctx.no_gas() {
|
|
||||||
return Err(RunError::Interrupted(Interrupted { stack: pushed(stack, expr) }));
|
|
||||||
}
|
}
|
||||||
ctx.use_gas(1);
|
params.use_gas(1);
|
||||||
enum Res {
|
let top = self.stack.last_mut().expect("Stack never empty");
|
||||||
Inert,
|
let location = top.expr.location();
|
||||||
Cont,
|
let op = take_with_output(&mut *top.cls, |c| {
|
||||||
|
match step(c, self.popped, location, self.env, params) {
|
||||||
|
Err(e) => (Clause::Bottom(e.clone()), Err(RunError::Extern(e))),
|
||||||
|
Ok((cls, cmd)) => (cls, Ok(cmd)),
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
self.popped = None;
|
||||||
|
match op {
|
||||||
|
StackOp::Nop => continue,
|
||||||
|
StackOp::Push(ex) => self.push_expr(ex, params)?,
|
||||||
|
StackOp::Swap(ex) => {
|
||||||
|
self.stack.pop().expect("Stack never empty");
|
||||||
|
self.push_expr(ex, params)?
|
||||||
|
},
|
||||||
|
StackOp::Pop => {
|
||||||
|
let ret = self.stack.pop().expect("last_mut called above");
|
||||||
|
if self.stack.is_empty() {
|
||||||
|
if let Some(alt) = self.env.dispatch(&ret.cls, ret.expr.location()) {
|
||||||
|
self.push_expr(alt, params)?;
|
||||||
|
params.use_gas(1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return Ok(ret.expr);
|
||||||
|
} else {
|
||||||
|
self.popped = Some(ret.expr);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> fmt::Display for State<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.stack.iter().rev().join("\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> fmt::Debug for State<'a> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "State({self})") }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process an expression with specific resource limits
|
||||||
|
pub fn run<'a>(
|
||||||
|
base: Expr,
|
||||||
|
env: &'a RunEnv<'a>,
|
||||||
|
params: &mut RunParams,
|
||||||
|
) -> Result<Halt, RunError<'a>> {
|
||||||
|
State::new(base, env).run(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum StackOp {
|
||||||
|
Pop,
|
||||||
|
Nop,
|
||||||
|
Swap(Expr),
|
||||||
Push(Expr),
|
Push(Expr),
|
||||||
}
|
}
|
||||||
let (next_clsi, res) = expr.clause.try_normalize(|cls| match cls {
|
|
||||||
Clause::Identity(_) => panic!("Passed by try_normalize"),
|
fn step(
|
||||||
Clause::LambdaArg => panic!("Unbound argument"),
|
top: Clause,
|
||||||
Clause::Lambda { .. } => Ok((cls, Res::Inert)),
|
popped: Option<Expr>,
|
||||||
Clause::Bottom(b) => Err(b),
|
location: CodeLocation,
|
||||||
Clause::Constant(n) => match ctx.symbols.get(&n) {
|
env: &RunEnv,
|
||||||
Some(expr) => Ok((Clause::Identity(expr.clsi()), Res::Cont)),
|
params: &mut RunParams,
|
||||||
None => Err(RunError::Extern(MissingSymbol { sym: n.clone(), loc: expr.location() }.rc())),
|
) -> Result<(Clause, StackOp), RTErrorObj> {
|
||||||
|
match top {
|
||||||
|
Clause::Bottom(err) => Err(err),
|
||||||
|
Clause::LambdaArg => Ok((Clause::Bottom(UnboundArg(location).pack()), StackOp::Nop)),
|
||||||
|
l @ Clause::Lambda { .. } => Ok((l, StackOp::Pop)),
|
||||||
|
Clause::Identity(other) =>
|
||||||
|
Ok((Clause::Identity(other.clone()), StackOp::Swap(other.into_expr(location)))),
|
||||||
|
Clause::Constant(name) => {
|
||||||
|
let expr = env.load(name, location)?;
|
||||||
|
Ok((Clause::Identity(expr.clsi()), StackOp::Swap(expr.clone())))
|
||||||
|
},
|
||||||
|
Clause::Atom(mut at) => {
|
||||||
|
if let Some(delegate) = at.0.redirect() {
|
||||||
|
match popped {
|
||||||
|
Some(popped) => *delegate = popped,
|
||||||
|
None => {
|
||||||
|
let tmp = delegate.clone();
|
||||||
|
return Ok((Clause::Atom(at), StackOp::Push(tmp)));
|
||||||
},
|
},
|
||||||
Clause::Atom(mut a) => {
|
|
||||||
if !popped {
|
|
||||||
if let Some(delegate) = a.0.redirect() {
|
|
||||||
let next = delegate.clone();
|
|
||||||
return Ok((Clause::Atom(a), Res::Push(next)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let rd = RunData { ctx: ctx.clone(), location: expr.location() };
|
match at.run(RunData { params, env, location })? {
|
||||||
match a.run(rd)? {
|
AtomicReturn::Inert(at) => Ok((Clause::Atom(at), StackOp::Pop)),
|
||||||
AtomicReturn::Inert(c) => Ok((c, Res::Inert)),
|
|
||||||
AtomicReturn::Change(gas, c) => {
|
AtomicReturn::Change(gas, c) => {
|
||||||
ctx.use_gas(gas);
|
params.use_gas(gas);
|
||||||
Ok((c, Res::Cont))
|
Ok((c, StackOp::Nop))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Clause::Apply { f, mut x } => {
|
Clause::Apply { mut f, mut x } => {
|
||||||
if x.is_empty() {
|
if x.is_empty() {
|
||||||
return Ok((Clause::Identity(f.clsi()), Res::Cont));
|
return Ok((Clause::Identity(f.clsi()), StackOp::Swap(f)));
|
||||||
}
|
}
|
||||||
match &*f.cls() {
|
f = match popped {
|
||||||
Clause::Identity(f2) =>
|
None => return Ok((Clause::Apply { f: f.clone(), x }, StackOp::Push(f))),
|
||||||
return Ok((Clause::Apply { f: f2.clone().to_expr(f.location()), x }, Res::Cont)),
|
Some(ex) => ex,
|
||||||
Clause::Apply { f, x: x2 } => {
|
|
||||||
for item in x2.iter().rev() {
|
|
||||||
x.push_front(item.clone())
|
|
||||||
}
|
|
||||||
return Ok((Clause::Apply { f: f.clone(), x }, Res::Cont));
|
|
||||||
},
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
if !popped {
|
|
||||||
return Ok((Clause::Apply { f: f.clone(), x }, Res::Push(f)));
|
|
||||||
}
|
|
||||||
let f_cls = f.cls();
|
|
||||||
let arg = x.pop_front().expect("checked above");
|
|
||||||
let loc = f.location();
|
|
||||||
let f = match &*f_cls {
|
|
||||||
Clause::Atom(_) => {
|
|
||||||
mem::drop(f_cls);
|
|
||||||
apply_as_atom(f, arg, ctx.clone())?
|
|
||||||
},
|
|
||||||
Clause::Lambda { args, body } => match args {
|
|
||||||
None => body.clsi().into_cls(),
|
|
||||||
Some(args) => substitute(args, arg.clsi(), &body.cls(), &mut || ctx.use_gas(1)),
|
|
||||||
},
|
|
||||||
c => panic!("Run should never settle on {c}"),
|
|
||||||
};
|
};
|
||||||
Ok((Clause::Apply { f: f.to_expr(loc), x }, Res::Cont))
|
let val = x.pop_front().expect("Empty args handled above");
|
||||||
|
let f_mut = f.clause.cls_mut();
|
||||||
|
let mut cls = match &*f_mut {
|
||||||
|
Clause::Lambda { args, body } => match args {
|
||||||
|
None => Clause::Identity(body.clsi()),
|
||||||
|
Some(args) => substitute(args, val.clsi(), &body.cls_mut(), &mut || params.use_gas(1)),
|
||||||
},
|
},
|
||||||
})?;
|
Clause::Atom(_) => {
|
||||||
expr.clause = next_clsi;
|
mem::drop(f_mut);
|
||||||
popped = matches!(res, Res::Inert);
|
apply_as_atom(f, val, env, params)?
|
||||||
match res {
|
|
||||||
Res::Cont => continue,
|
|
||||||
Res::Inert => match stack.pop() {
|
|
||||||
None => return Ok(Halt { state: expr, gas: ctx.gas, inert: true }),
|
|
||||||
Some(prev) => expr = prev,
|
|
||||||
},
|
},
|
||||||
Res::Push(next) => {
|
c => unreachable!("Run should never settle on {c}"),
|
||||||
if stack.len() == ctx.stack_size {
|
};
|
||||||
stack.extend([expr, next]);
|
if !x.is_empty() {
|
||||||
return Err(RunError::Extern(StackOverflow { stack }.rc()));
|
cls = Clause::Apply { f: cls.into_expr(location), x };
|
||||||
}
|
}
|
||||||
stack.push(expr);
|
Ok((cls, StackOp::Nop))
|
||||||
expr = next;
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct StackOverflow(Vec<Expr>);
|
||||||
|
impl RTError for StackOverflow {}
|
||||||
|
impl fmt::Display for StackOverflow {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
writeln!(f, "Stack depth exceeded {}:", self.0.len() - 1)?; // 1 for failed call, 1 for current
|
||||||
|
for item in self.0.iter().rev() {
|
||||||
|
match Stackframe::new(item.clone()) {
|
||||||
|
Some(sf) => writeln!(f, "{sf}")?,
|
||||||
|
None => writeln!(f, "Locked frame at {}", item.location)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct UnboundArg(CodeLocation);
|
||||||
|
impl RTError for UnboundArg {}
|
||||||
|
impl fmt::Display for UnboundArg {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Unbound argument found at {}. This is likely a codegen error", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct CyclicalExpression(Expr);
|
||||||
|
impl RTError for CyclicalExpression {}
|
||||||
|
impl fmt::Display for CyclicalExpression {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "The expression {} contains itself", self.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/lib.rs
10
src/lib.rs
@@ -1,10 +1,8 @@
|
|||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![doc(
|
#![warn(unit_bindings)]
|
||||||
html_logo_url = "https://raw.githubusercontent.com/lbfalvy/orchid/master/icon.svg"
|
#![warn(clippy::unnecessary_wraps)]
|
||||||
)]
|
#![doc(html_logo_url = "https://raw.githubusercontent.com/lbfalvy/orchid/master/icon.svg")]
|
||||||
#![doc(
|
#![doc(html_favicon_url = "https://raw.githubusercontent.com/lbfalvy/orchid/master/icon.svg")]
|
||||||
html_favicon_url = "https://raw.githubusercontent.com/lbfalvy/orchid/master/icon.svg"
|
|
||||||
)]
|
|
||||||
//! Orchid is a lazy, pure scripting language to be embedded in Rust
|
//! Orchid is a lazy, pure scripting language to be embedded in Rust
|
||||||
//! applications. Check out the repo for examples and other links.
|
//! applications. Check out the repo for examples and other links.
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|||||||
@@ -4,6 +4,6 @@
|
|||||||
//! beyond being general Rust functions.
|
//! beyond being general Rust functions.
|
||||||
//! It also exposes timers.
|
//! It also exposes timers.
|
||||||
|
|
||||||
|
mod delete_cell;
|
||||||
pub mod poller;
|
pub mod poller;
|
||||||
pub mod system;
|
pub mod system;
|
||||||
mod delete_cell;
|
|
||||||
|
|||||||
@@ -33,23 +33,17 @@ struct Timer<TOnce, TRec> {
|
|||||||
kind: TimerKind<TOnce, TRec>,
|
kind: TimerKind<TOnce, TRec>,
|
||||||
}
|
}
|
||||||
impl<TOnce, TRec> Clone for Timer<TOnce, TRec> {
|
impl<TOnce, TRec> Clone for Timer<TOnce, TRec> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self { Self { expires: self.expires, kind: self.kind.clone() } }
|
||||||
Self { expires: self.expires, kind: self.kind.clone() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl<TOnce, TRec> Eq for Timer<TOnce, TRec> {}
|
impl<TOnce, TRec> Eq for Timer<TOnce, TRec> {}
|
||||||
impl<TOnce, TRec> PartialEq for Timer<TOnce, TRec> {
|
impl<TOnce, TRec> PartialEq for Timer<TOnce, TRec> {
|
||||||
fn eq(&self, other: &Self) -> bool { self.expires.eq(&other.expires) }
|
fn eq(&self, other: &Self) -> bool { self.expires.eq(&other.expires) }
|
||||||
}
|
}
|
||||||
impl<TOnce, TRec> PartialOrd for Timer<TOnce, TRec> {
|
impl<TOnce, TRec> PartialOrd for Timer<TOnce, TRec> {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { Some(other.cmp(self)) }
|
||||||
Some(other.cmp(self))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl<TOnce, TRec> Ord for Timer<TOnce, TRec> {
|
impl<TOnce, TRec> Ord for Timer<TOnce, TRec> {
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering { other.expires.cmp(&self.expires) }
|
||||||
other.expires.cmp(&self.expires)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Representation of a scheduled timer
|
/// Representation of a scheduled timer
|
||||||
@@ -76,25 +70,16 @@ impl<TEv, TOnce, TRec: Clone> Poller<TEv, TOnce, TRec> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set a single-fire timer
|
/// Set a single-fire timer
|
||||||
pub fn set_timeout(
|
pub fn set_timeout(&mut self, duration: Duration, data: TOnce) -> TimerHandle<TOnce> {
|
||||||
&mut self,
|
|
||||||
duration: Duration,
|
|
||||||
data: TOnce,
|
|
||||||
) -> TimerHandle<TOnce> {
|
|
||||||
let data_cell = DeleteCell::new(data);
|
let data_cell = DeleteCell::new(data);
|
||||||
self.timers.push(Timer {
|
self
|
||||||
kind: TimerKind::Once(data_cell.clone()),
|
.timers
|
||||||
expires: Instant::now() + duration,
|
.push(Timer { kind: TimerKind::Once(data_cell.clone()), expires: Instant::now() + duration });
|
||||||
});
|
|
||||||
TimerHandle(data_cell)
|
TimerHandle(data_cell)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a recurring timer
|
/// Set a recurring timer
|
||||||
pub fn set_interval(
|
pub fn set_interval(&mut self, period: Duration, data: TRec) -> TimerHandle<TRec> {
|
||||||
&mut self,
|
|
||||||
period: Duration,
|
|
||||||
data: TRec,
|
|
||||||
) -> TimerHandle<TRec> {
|
|
||||||
let data_cell = DeleteCell::new(data);
|
let data_cell = DeleteCell::new(data);
|
||||||
self.timers.push(Timer {
|
self.timers.push(Timer {
|
||||||
expires: Instant::now() + period,
|
expires: Instant::now() + period,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
use std::any::{type_name, Any, TypeId};
|
use std::any::{type_name, Any, TypeId};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
@@ -19,7 +19,7 @@ use super::poller::{PollEvent, Poller, TimerHandle};
|
|||||||
use crate::facade::system::{IntoSystem, System};
|
use crate::facade::system::{IntoSystem, System};
|
||||||
use crate::foreign::atom::Atomic;
|
use crate::foreign::atom::Atomic;
|
||||||
use crate::foreign::cps_box::CPSBox;
|
use crate::foreign::cps_box::CPSBox;
|
||||||
use crate::foreign::error::ExternError;
|
use crate::foreign::error::RTError;
|
||||||
use crate::foreign::inert::{Inert, InertPayload};
|
use crate::foreign::inert::{Inert, InertPayload};
|
||||||
use crate::gen::tpl;
|
use crate::gen::tpl;
|
||||||
use crate::gen::traits::Gen;
|
use crate::gen::traits::Gen;
|
||||||
@@ -29,6 +29,7 @@ use crate::interpreter::handler::HandlerTable;
|
|||||||
use crate::interpreter::nort::Expr;
|
use crate::interpreter::nort::Expr;
|
||||||
use crate::libs::std::number::Numeric;
|
use crate::libs::std::number::Numeric;
|
||||||
use crate::location::{CodeGenInfo, CodeLocation};
|
use crate::location::{CodeGenInfo, CodeLocation};
|
||||||
|
use crate::sym;
|
||||||
use crate::utils::unwrap_or::unwrap_or;
|
use crate::utils::unwrap_or::unwrap_or;
|
||||||
use crate::virt_fs::{DeclTree, EmbeddedFS, PrefixFS, VirtFS};
|
use crate::virt_fs::{DeclTree, EmbeddedFS, PrefixFS, VirtFS};
|
||||||
|
|
||||||
@@ -50,8 +51,8 @@ impl CancelTimer {
|
|||||||
}
|
}
|
||||||
pub fn cancel(&self) { self.0.lock().unwrap()() }
|
pub fn cancel(&self) { self.0.lock().unwrap()() }
|
||||||
}
|
}
|
||||||
impl Debug for CancelTimer {
|
impl fmt::Debug for CancelTimer {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("CancelTimer").finish_non_exhaustive()
|
f.debug_struct("CancelTimer").finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,9 +67,9 @@ impl InertPayload for Yield {
|
|||||||
/// exited
|
/// exited
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct InfiniteBlock;
|
pub struct InfiniteBlock;
|
||||||
impl ExternError for InfiniteBlock {}
|
impl RTError for InfiniteBlock {}
|
||||||
impl Display for InfiniteBlock {
|
impl fmt::Display for InfiniteBlock {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
static MSG: &str = "User code yielded, but there are no timers or event \
|
static MSG: &str = "User code yielded, but there are no timers or event \
|
||||||
producers to wake it up in the future";
|
producers to wake it up in the future";
|
||||||
write!(f, "{}", MSG)
|
write!(f, "{}", MSG)
|
||||||
@@ -83,7 +84,7 @@ impl MessagePort {
|
|||||||
pub fn send<T: Send + 'static>(&mut self, message: T) { let _ = self.0.send(Box::new(message)); }
|
pub fn send<T: Send + 'static>(&mut self, message: T) { let _ = self.0.send(Box::new(message)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen() -> CodeGenInfo { CodeGenInfo::no_details("asynch") }
|
fn gen() -> CodeGenInfo { CodeGenInfo::no_details(sym!(asynch)) }
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
#[derive(RustEmbed)]
|
||||||
#[folder = "src/libs/asynch"]
|
#[folder = "src/libs/asynch"]
|
||||||
@@ -92,7 +93,7 @@ struct AsynchEmbed;
|
|||||||
|
|
||||||
fn code() -> DeclTree {
|
fn code() -> DeclTree {
|
||||||
DeclTree::ns("system::async", [DeclTree::leaf(
|
DeclTree::ns("system::async", [DeclTree::leaf(
|
||||||
PrefixFS::new(EmbeddedFS::new::<AsynchEmbed>(".orc", gen()), "", "io").rc(),
|
PrefixFS::new(EmbeddedFS::new::<AsynchEmbed>(".orc", gen()), "", "async").rc(),
|
||||||
)])
|
)])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +173,7 @@ impl<'a> IntoSystem<'a> for AsynchSystem<'a> {
|
|||||||
let mut polly = polly.borrow_mut();
|
let mut polly = polly.borrow_mut();
|
||||||
loop {
|
loop {
|
||||||
let next = unwrap_or!(polly.run();
|
let next = unwrap_or!(polly.run();
|
||||||
return Err(InfiniteBlock.rc())
|
return Err(InfiniteBlock.pack())
|
||||||
);
|
);
|
||||||
match next {
|
match next {
|
||||||
PollEvent::Once(expr) => return Ok(expr),
|
PollEvent::Once(expr) => return Ok(expr),
|
||||||
@@ -185,7 +186,7 @@ impl<'a> IntoSystem<'a> for AsynchSystem<'a> {
|
|||||||
if !events.is_empty() {
|
if !events.is_empty() {
|
||||||
microtasks = VecDeque::from(events);
|
microtasks = VecDeque::from(events);
|
||||||
// trampoline
|
// trampoline
|
||||||
let loc = CodeLocation::Gen(CodeGenInfo::no_details("system::asynch"));
|
let loc = CodeLocation::new_gen(CodeGenInfo::no_details(sym!(system::asynch)));
|
||||||
return Ok(Inert(Yield).atom_expr(loc));
|
return Ok(Inert(Yield).atom_expr(loc));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use super::osstring::os_string_lib;
|
|||||||
use crate::facade::system::{IntoSystem, System};
|
use crate::facade::system::{IntoSystem, System};
|
||||||
use crate::foreign::atom::Atomic;
|
use crate::foreign::atom::Atomic;
|
||||||
use crate::foreign::cps_box::CPSBox;
|
use crate::foreign::cps_box::CPSBox;
|
||||||
use crate::foreign::error::ExternResult;
|
use crate::foreign::error::RTResult;
|
||||||
use crate::foreign::inert::{Inert, InertPayload};
|
use crate::foreign::inert::{Inert, InertPayload};
|
||||||
use crate::foreign::process::Unstable;
|
use crate::foreign::process::Unstable;
|
||||||
use crate::foreign::to_clause::ToClause;
|
use crate::foreign::to_clause::ToClause;
|
||||||
@@ -84,7 +84,7 @@ fn read_dir(sched: &SeqScheduler, cmd: &CPSBox<ReadDirCmd>) -> Expr {
|
|||||||
.collect::<Result<Vec<_>, Clause>>();
|
.collect::<Result<Vec<_>, Clause>>();
|
||||||
match converted {
|
match converted {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let e = e.to_expr(fail.location());
|
let e = e.into_expr(fail.location());
|
||||||
let tpl = tpl::A(tpl::Slot, tpl::Slot);
|
let tpl = tpl::A(tpl::Slot, tpl::Slot);
|
||||||
vec![tpl.template(nort_gen(fail.location()), [fail, e])]
|
vec![tpl.template(nort_gen(fail.location()), [fail, e])]
|
||||||
},
|
},
|
||||||
@@ -141,7 +141,7 @@ fn join_paths(root: OsString, sub: OsString) -> OsString {
|
|||||||
fn pop_path(path: Inert<OsString>) -> Option<(Inert<OsString>, Inert<OsString>)> {
|
fn pop_path(path: Inert<OsString>) -> Option<(Inert<OsString>, Inert<OsString>)> {
|
||||||
let mut path = PathBuf::from(path.0);
|
let mut path = PathBuf::from(path.0);
|
||||||
let sub = path.file_name()?.to_owned();
|
let sub = path.file_name()?.to_owned();
|
||||||
debug_assert!(path.pop(), "file_name above returned Some");
|
assert!(path.pop(), "file_name above returned Some");
|
||||||
Some((Inert(path.into_os_string()), Inert(sub)))
|
Some((Inert(path.into_os_string()), Inert(sub)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,7 +177,7 @@ impl IntoSystem<'static> for DirectFS {
|
|||||||
xfn_ent("append_file", [open_file_append_cmd]),
|
xfn_ent("append_file", [open_file_append_cmd]),
|
||||||
xfn_ent("join_paths", [join_paths]),
|
xfn_ent("join_paths", [join_paths]),
|
||||||
xfn_ent("pop_path", [pop_path]),
|
xfn_ent("pop_path", [pop_path]),
|
||||||
atom_ent("cwd", [Unstable::new(|_| -> ExternResult<_> {
|
atom_ent("cwd", [Unstable::new(|_| -> RTResult<_> {
|
||||||
let path =
|
let path =
|
||||||
std::env::current_dir().map_err(|e| RuntimeError::ext(e.to_string(), "reading CWD"))?;
|
std::env::current_dir().map_err(|e| RuntimeError::ext(e.to_string(), "reading CWD"))?;
|
||||||
Ok(Inert(path.into_os_string()))
|
Ok(Inert(path.into_os_string()))
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
|
||||||
use crate::foreign::atom::Atomic;
|
use crate::foreign::atom::Atomic;
|
||||||
use crate::foreign::error::ExternResult;
|
use crate::foreign::error::RTResult;
|
||||||
use crate::foreign::inert::{Inert, InertPayload};
|
use crate::foreign::inert::{Inert, InertPayload};
|
||||||
use crate::foreign::to_clause::ToClause;
|
use crate::foreign::to_clause::ToClause;
|
||||||
use crate::foreign::try_from_expr::TryFromExpr;
|
use crate::foreign::try_from_expr::TryFromExpr;
|
||||||
@@ -12,9 +12,12 @@ use crate::location::CodeLocation;
|
|||||||
|
|
||||||
impl InertPayload for OsString {
|
impl InertPayload for OsString {
|
||||||
const TYPE_STR: &'static str = "OsString";
|
const TYPE_STR: &'static str = "OsString";
|
||||||
|
fn respond(&self, mut request: crate::utils::ddispatch::Request) {
|
||||||
|
request.serve_with(|| OrcString::from(self.to_string_lossy().to_string()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl TryFromExpr for OsString {
|
impl TryFromExpr for OsString {
|
||||||
fn from_expr(exi: Expr) -> ExternResult<Self> { Ok(Inert::from_expr(exi)?.0) }
|
fn from_expr(exi: Expr) -> RTResult<Self> { Ok(Inert::from_expr(exi)?.0) }
|
||||||
}
|
}
|
||||||
impl ToClause for OsString {
|
impl ToClause for OsString {
|
||||||
fn to_clause(self, _: CodeLocation) -> Clause { Inert(self).atom_cls() }
|
fn to_clause(self, _: CodeLocation) -> Clause { Inert(self).atom_cls() }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use super::flow::IOCmdHandlePack;
|
|||||||
use super::instances::{BRead, ReadCmd, SRead, WriteCmd};
|
use super::instances::{BRead, ReadCmd, SRead, WriteCmd};
|
||||||
use super::service::{Sink, Source};
|
use super::service::{Sink, Source};
|
||||||
use crate::foreign::cps_box::CPSBox;
|
use crate::foreign::cps_box::CPSBox;
|
||||||
use crate::foreign::error::ExternResult;
|
use crate::foreign::error::RTResult;
|
||||||
use crate::foreign::inert::Inert;
|
use crate::foreign::inert::Inert;
|
||||||
use crate::gen::tree::{xfn_ent, ConstTree};
|
use crate::gen::tree::{xfn_ent, ConstTree};
|
||||||
use crate::libs::scheduler::system::SharedHandle;
|
use crate::libs::scheduler::system::SharedHandle;
|
||||||
@@ -36,7 +36,7 @@ pub fn read_bytes(Inert(handle): ReadHandle, n: Inert<usize>) -> ReadCmdPack {
|
|||||||
pub fn read_until(
|
pub fn read_until(
|
||||||
Inert(handle): ReadHandle,
|
Inert(handle): ReadHandle,
|
||||||
Inert(pattern): Inert<usize>,
|
Inert(pattern): Inert<usize>,
|
||||||
) -> ExternResult<ReadCmdPack> {
|
) -> RTResult<ReadCmdPack> {
|
||||||
let pattern = pattern.try_into().map_err(|_| {
|
let pattern = pattern.try_into().map_err(|_| {
|
||||||
let msg = format!("{pattern} doesn't fit into a byte");
|
let msg = format!("{pattern} doesn't fit into a byte");
|
||||||
RuntimeError::ext(msg, "converting number to byte")
|
RuntimeError::ext(msg, "converting number to byte")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::foreign::error::ExternError;
|
use crate::foreign::error::RTError;
|
||||||
use crate::libs::scheduler::cancel_flag::CancelFlag;
|
use crate::libs::scheduler::cancel_flag::CancelFlag;
|
||||||
|
|
||||||
pub trait IOHandler<T> {
|
pub trait IOHandler<T> {
|
||||||
@@ -22,11 +22,7 @@ pub trait IOCmd: Send {
|
|||||||
type Result: Send;
|
type Result: Send;
|
||||||
type Handle;
|
type Handle;
|
||||||
|
|
||||||
fn execute(
|
fn execute(self, stream: &mut Self::Stream, cancel: CancelFlag) -> Self::Result;
|
||||||
self,
|
|
||||||
stream: &mut Self::Stream,
|
|
||||||
cancel: CancelFlag,
|
|
||||||
) -> Self::Result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -37,9 +33,9 @@ pub struct IOCmdHandlePack<Cmd: IOCmd> {
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct NoActiveStream(usize);
|
pub struct NoActiveStream(usize);
|
||||||
impl ExternError for NoActiveStream {}
|
impl RTError for NoActiveStream {}
|
||||||
impl Display for NoActiveStream {
|
impl fmt::Display for NoActiveStream {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "The stream {} had already been closed", self.0)
|
write!(f, "The stream {} had already been closed", self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use crate::libs::scheduler::system::SharedHandle;
|
|||||||
use crate::libs::std::binary::Binary;
|
use crate::libs::std::binary::Binary;
|
||||||
use crate::libs::std::string::OrcString;
|
use crate::libs::std::string::OrcString;
|
||||||
use crate::location::{CodeGenInfo, CodeLocation};
|
use crate::location::{CodeGenInfo, CodeLocation};
|
||||||
|
use crate::sym;
|
||||||
|
|
||||||
/// String reading command
|
/// String reading command
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
@@ -42,11 +43,7 @@ impl IOCmd for ReadCmd {
|
|||||||
|
|
||||||
// This is a buggy rule, check manually
|
// This is a buggy rule, check manually
|
||||||
#[allow(clippy::read_zero_byte_vec)]
|
#[allow(clippy::read_zero_byte_vec)]
|
||||||
fn execute(
|
fn execute(self, stream: &mut Self::Stream, _cancel: CancelFlag) -> Self::Result {
|
||||||
self,
|
|
||||||
stream: &mut Self::Stream,
|
|
||||||
_cancel: CancelFlag,
|
|
||||||
) -> Self::Result {
|
|
||||||
match self {
|
match self {
|
||||||
Self::RBytes(bread) => {
|
Self::RBytes(bread) => {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
@@ -84,13 +81,10 @@ pub(super) enum ReadResult {
|
|||||||
impl ReadResult {
|
impl ReadResult {
|
||||||
pub fn dispatch(self, succ: Expr, fail: Expr) -> Vec<Expr> {
|
pub fn dispatch(self, succ: Expr, fail: Expr) -> Vec<Expr> {
|
||||||
vec![match self {
|
vec![match self {
|
||||||
ReadResult::RBin(_, Err(e)) | ReadResult::RStr(_, Err(e)) =>
|
ReadResult::RBin(_, Err(e)) | ReadResult::RStr(_, Err(e)) => io_error_handler(e, fail),
|
||||||
io_error_handler(e, fail),
|
ReadResult::RBin(_, Ok(bytes)) => tpl::A(tpl::Slot, tpl::V(Inert(Binary(Arc::new(bytes)))))
|
||||||
ReadResult::RBin(_, Ok(bytes)) =>
|
|
||||||
tpl::A(tpl::Slot, tpl::V(Inert(Binary(Arc::new(bytes)))))
|
|
||||||
.template(nort_gen(succ.location()), [succ]),
|
.template(nort_gen(succ.location()), [succ]),
|
||||||
ReadResult::RStr(_, Ok(text)) =>
|
ReadResult::RStr(_, Ok(text)) => tpl::A(tpl::Slot, tpl::V(Inert(OrcString::from(text))))
|
||||||
tpl::A(tpl::Slot, tpl::V(Inert(OrcString::from(text))))
|
|
||||||
.template(nort_gen(succ.location()), [succ]),
|
.template(nort_gen(succ.location()), [succ]),
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
@@ -98,7 +92,7 @@ impl ReadResult {
|
|||||||
|
|
||||||
/// Function to convert [io::Error] to Orchid data
|
/// Function to convert [io::Error] to Orchid data
|
||||||
pub(crate) fn io_error_handler(_e: io::Error, handler: Expr) -> Expr {
|
pub(crate) fn io_error_handler(_e: io::Error, handler: Expr) -> Expr {
|
||||||
let ctx = nort_gen(CodeLocation::Gen(CodeGenInfo::no_details("io_error")));
|
let ctx = nort_gen(CodeLocation::new_gen(CodeGenInfo::no_details(sym!(system::io::io_error))));
|
||||||
tpl::A(tpl::Slot, tpl::V(Inert(0usize))).template(ctx, [handler])
|
tpl::A(tpl::Slot, tpl::V(Inert(0usize))).template(ctx, [handler])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,11 +109,7 @@ impl IOCmd for WriteCmd {
|
|||||||
type Handle = SharedHandle<Sink>;
|
type Handle = SharedHandle<Sink>;
|
||||||
type Result = WriteResult;
|
type Result = WriteResult;
|
||||||
|
|
||||||
fn execute(
|
fn execute(self, stream: &mut Self::Stream, _cancel: CancelFlag) -> Self::Result {
|
||||||
self,
|
|
||||||
stream: &mut Self::Stream,
|
|
||||||
_cancel: CancelFlag,
|
|
||||||
) -> Self::Result {
|
|
||||||
let result = match &self {
|
let result = match &self {
|
||||||
Self::Flush => stream.flush(),
|
Self::Flush => stream.flush(),
|
||||||
Self::WStr(str) => write!(stream, "{}", str).map(|_| ()),
|
Self::WStr(str) => write!(stream, "{}", str).map(|_| ()),
|
||||||
|
|||||||
@@ -24,8 +24,12 @@ export const readln := \ok. (
|
|||||||
\_. yield
|
\_. yield
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const prompt := \line. \ok. (
|
||||||
|
print line (readln ok)
|
||||||
|
)
|
||||||
|
|
||||||
export module prelude (
|
export module prelude (
|
||||||
import super::*
|
import super::*
|
||||||
|
|
||||||
export ::(print, println, readln)
|
export ::(print, println, readln, prompt)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,13 +8,13 @@
|
|||||||
//! and [crate::libs::std] for `std::panic`.
|
//! and [crate::libs::std] for `std::panic`.
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use orchidlang::libs::asynch::system::AsynchSystem;
|
|
||||||
//! use orchidlang::libs::scheduler::system::SeqScheduler;
|
|
||||||
//! use orchidlang::libs::std::std_system::StdConfig;
|
|
||||||
//! use orchidlang::libs::io::{IOService, Stream};
|
|
||||||
//! use orchidlang::facade::loader::Loader;
|
|
||||||
//! use std::io::BufReader;
|
//! use std::io::BufReader;
|
||||||
//!
|
//!
|
||||||
|
//! use orchidlang::facade::loader::Loader;
|
||||||
|
//! use orchidlang::libs::asynch::system::AsynchSystem;
|
||||||
|
//! use orchidlang::libs::io::{IOService, Stream};
|
||||||
|
//! use orchidlang::libs::scheduler::system::SeqScheduler;
|
||||||
|
//! use orchidlang::libs::std::std_system::StdConfig;
|
||||||
//!
|
//!
|
||||||
//! let mut asynch = AsynchSystem::new();
|
//! let mut asynch = AsynchSystem::new();
|
||||||
//! let scheduler = SeqScheduler::new(&mut asynch);
|
//! let scheduler = SeqScheduler::new(&mut asynch);
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ use crate::interpreter::gen_nort::nort_gen;
|
|||||||
use crate::interpreter::handler::HandlerTable;
|
use crate::interpreter::handler::HandlerTable;
|
||||||
use crate::libs::scheduler::system::{SeqScheduler, SharedHandle};
|
use crate::libs::scheduler::system::{SeqScheduler, SharedHandle};
|
||||||
use crate::location::CodeGenInfo;
|
use crate::location::CodeGenInfo;
|
||||||
use crate::name::VName;
|
use crate::pipeline::load_project::Prelude;
|
||||||
use crate::pipeline::load_solution::Prelude;
|
|
||||||
use crate::virt_fs::{DeclTree, EmbeddedFS, PrefixFS, VirtFS};
|
use crate::virt_fs::{DeclTree, EmbeddedFS, PrefixFS, VirtFS};
|
||||||
|
use crate::{sym, vname};
|
||||||
|
|
||||||
/// Any type that we can read controlled amounts of data from
|
/// Any type that we can read controlled amounts of data from
|
||||||
pub type Source = BufReader<Box<dyn Read + Send>>;
|
pub type Source = BufReader<Box<dyn Read + Send>>;
|
||||||
@@ -42,7 +42,7 @@ trait_set! {
|
|||||||
pub(super) trait StreamTable<'a> = IntoIterator<Item = (&'a str, Stream)>
|
pub(super) trait StreamTable<'a> = IntoIterator<Item = (&'a str, Stream)>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen() -> CodeGenInfo { CodeGenInfo::no_details("system::io") }
|
fn gen() -> CodeGenInfo { CodeGenInfo::no_details(sym!(system::io)) }
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
#[derive(RustEmbed)]
|
||||||
#[folder = "src/libs/io"]
|
#[folder = "src/libs/io"]
|
||||||
@@ -68,9 +68,7 @@ impl<'a, ST: IntoIterator<Item = (&'a str, Stream)>> IOService<'a, ST> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, ST: IntoIterator<Item = (&'a str, Stream)>> IntoSystem<'static>
|
impl<'a, ST: IntoIterator<Item = (&'a str, Stream)>> IntoSystem<'static> for IOService<'a, ST> {
|
||||||
for IOService<'a, ST>
|
|
||||||
{
|
|
||||||
fn into_system(self) -> System<'static> {
|
fn into_system(self) -> System<'static> {
|
||||||
let scheduler = self.scheduler.clone();
|
let scheduler = self.scheduler.clone();
|
||||||
let mut handlers = HandlerTable::new();
|
let mut handlers = HandlerTable::new();
|
||||||
@@ -89,8 +87,7 @@ impl<'a, ST: IntoIterator<Item = (&'a str, Stream)>> IntoSystem<'static>
|
|||||||
match result {
|
match result {
|
||||||
Ok(cancel) => tpl::A(tpl::Slot, tpl::V(CPSBox::new(1, cancel)))
|
Ok(cancel) => tpl::A(tpl::Slot, tpl::V(CPSBox::new(1, cancel)))
|
||||||
.template(nort_gen(cont.location()), [cont]),
|
.template(nort_gen(cont.location()), [cont]),
|
||||||
Err(e) => tpl::A(tpl::Slot, tpl::V(Inert(e)))
|
Err(e) => tpl::A(tpl::Slot, tpl::V(Inert(e))).template(nort_gen(fail.location()), [fail]),
|
||||||
.template(nort_gen(fail.location()), [fail]),
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let scheduler = self.scheduler.clone();
|
let scheduler = self.scheduler.clone();
|
||||||
@@ -109,15 +106,13 @@ impl<'a, ST: IntoIterator<Item = (&'a str, Stream)>> IntoSystem<'static>
|
|||||||
match result {
|
match result {
|
||||||
Ok(cancel) => tpl::A(tpl::Slot, tpl::V(CPSBox::new(1, cancel)))
|
Ok(cancel) => tpl::A(tpl::Slot, tpl::V(CPSBox::new(1, cancel)))
|
||||||
.template(nort_gen(cont.location()), [cont]),
|
.template(nort_gen(cont.location()), [cont]),
|
||||||
Err(e) => tpl::A(tpl::Slot, tpl::V(Inert(e)))
|
Err(e) => tpl::A(tpl::Slot, tpl::V(Inert(e))).template(nort_gen(fail.location()), [fail]),
|
||||||
.template(nort_gen(fail.location()), [fail]),
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let streams = self.global_streams.into_iter().map(|(n, stream)| {
|
let streams = self.global_streams.into_iter().map(|(n, stream)| {
|
||||||
let handle = match stream {
|
let handle = match stream {
|
||||||
Stream::Sink(sink) => leaf(tpl::V(Inert(SharedHandle::wrap(sink)))),
|
Stream::Sink(sink) => leaf(tpl::V(Inert(SharedHandle::wrap(sink)))),
|
||||||
Stream::Source(source) =>
|
Stream::Source(source) => leaf(tpl::V(Inert(SharedHandle::wrap(source)))),
|
||||||
leaf(tpl::V(Inert(SharedHandle::wrap(source)))),
|
|
||||||
};
|
};
|
||||||
(n, handle)
|
(n, handle)
|
||||||
});
|
});
|
||||||
@@ -127,8 +122,8 @@ impl<'a, ST: IntoIterator<Item = (&'a str, Stream)>> IntoSystem<'static>
|
|||||||
constants: io_bindings(streams),
|
constants: io_bindings(streams),
|
||||||
code: code(),
|
code: code(),
|
||||||
prelude: vec![Prelude {
|
prelude: vec![Prelude {
|
||||||
target: VName::literal("system::io::prelude"),
|
target: vname!(system::io::prelude),
|
||||||
exclude: VName::literal("system::io"),
|
exclude: vname!(system::io),
|
||||||
owner: gen(),
|
owner: gen(),
|
||||||
}],
|
}],
|
||||||
lexer_plugins: vec![],
|
lexer_plugins: vec![],
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ pub type SyncResult<T> = (T, Box<dyn Any + Send>);
|
|||||||
/// Output from handlers contains the resource being processed and any Orchid
|
/// Output from handlers contains the resource being processed and any Orchid
|
||||||
/// handlers executed as a result of the operation
|
/// handlers executed as a result of the operation
|
||||||
pub type HandlerRes<T> = (T, Vec<Expr>);
|
pub type HandlerRes<T> = (T, Vec<Expr>);
|
||||||
pub type SyncOperation<T> =
|
pub type SyncOperation<T> = Box<dyn FnOnce(T, CancelFlag) -> SyncResult<T> + Send>;
|
||||||
Box<dyn FnOnce(T, CancelFlag) -> SyncResult<T> + Send>;
|
|
||||||
pub type SyncOpResultHandler<T> =
|
pub type SyncOpResultHandler<T> =
|
||||||
Box<dyn FnOnce(T, Box<dyn Any + Send>, CancelFlag) -> (T, Vec<Expr>) + Send>;
|
Box<dyn FnOnce(T, Box<dyn Any + Send>, CancelFlag) -> (T, Vec<Expr>) + Send>;
|
||||||
|
|
||||||
@@ -22,12 +21,7 @@ struct SyncQueueItem<T> {
|
|||||||
|
|
||||||
pub enum NextItemReportKind<T> {
|
pub enum NextItemReportKind<T> {
|
||||||
Free(T),
|
Free(T),
|
||||||
Next {
|
Next { instance: T, cancelled: CancelFlag, operation: SyncOperation<T>, rest: BusyState<T> },
|
||||||
instance: T,
|
|
||||||
cancelled: CancelFlag,
|
|
||||||
operation: SyncOperation<T>,
|
|
||||||
rest: BusyState<T>,
|
|
||||||
},
|
|
||||||
Taken,
|
Taken,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,9 +41,7 @@ impl<T> BusyState<T> {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
BusyState {
|
BusyState {
|
||||||
handler: Box::new(|t, payload, cancel| {
|
handler: Box::new(|t, payload, cancel| {
|
||||||
let u = *payload
|
let u = *payload.downcast().expect("mismatched initial handler and operation");
|
||||||
.downcast()
|
|
||||||
.expect("mismatched initial handler and operation");
|
|
||||||
handler(t, u, cancel)
|
handler(t, u, cancel)
|
||||||
}),
|
}),
|
||||||
queue: VecDeque::new(),
|
queue: VecDeque::new(),
|
||||||
@@ -84,10 +76,7 @@ impl<T> BusyState<T> {
|
|||||||
Some(cancelled)
|
Some(cancelled)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn seal(
|
pub fn seal(&mut self, recipient: impl FnOnce(T) -> Vec<Expr> + Send + 'static) {
|
||||||
&mut self,
|
|
||||||
recipient: impl FnOnce(T) -> Vec<Expr> + Send + 'static,
|
|
||||||
) {
|
|
||||||
assert!(self.seal.is_none(), "Already sealed");
|
assert!(self.seal.is_none(), "Already sealed");
|
||||||
self.seal = Some(Box::new(recipient))
|
self.seal = Some(Box::new(recipient))
|
||||||
}
|
}
|
||||||
@@ -100,8 +89,7 @@ impl<T> BusyState<T> {
|
|||||||
result: Box<dyn Any + Send>,
|
result: Box<dyn Any + Send>,
|
||||||
cancelled: CancelFlag,
|
cancelled: CancelFlag,
|
||||||
) -> NextItemReport<T> {
|
) -> NextItemReport<T> {
|
||||||
let (mut instance, mut events) =
|
let (mut instance, mut events) = (self.handler)(instance, result, cancelled);
|
||||||
(self.handler)(instance, result, cancelled);
|
|
||||||
let next_item = loop {
|
let next_item = loop {
|
||||||
if let Some(candidate) = self.queue.pop_front() {
|
if let Some(candidate) = self.queue.pop_front() {
|
||||||
if candidate.cancelled.is_cancelled() {
|
if candidate.cancelled.is_cancelled() {
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ impl<T> IdMap<T> {
|
|||||||
pub fn insert(&mut self, t: T) -> u64 {
|
pub fn insert(&mut self, t: T) -> u64 {
|
||||||
let id = self.next_id;
|
let id = self.next_id;
|
||||||
self.next_id += 1;
|
self.next_id += 1;
|
||||||
(self.data.try_insert(id, t))
|
(self.data.try_insert(id, t)).unwrap_or_else(|_| panic!("IdMap keys should be unique"));
|
||||||
.unwrap_or_else(|_| panic!("IdMap keys should be unique"));
|
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
use std::any::{type_name, Any};
|
use std::any::{type_name, Any};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fmt::Debug;
|
use std::fmt;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ use super::id_map::IdMap;
|
|||||||
use super::thread_pool::ThreadPool;
|
use super::thread_pool::ThreadPool;
|
||||||
use crate::facade::system::{IntoSystem, System};
|
use crate::facade::system::{IntoSystem, System};
|
||||||
use crate::foreign::cps_box::CPSBox;
|
use crate::foreign::cps_box::CPSBox;
|
||||||
use crate::foreign::error::{AssertionError, ExternResult};
|
use crate::foreign::error::{AssertionError, RTResult};
|
||||||
use crate::foreign::inert::{Inert, InertPayload};
|
use crate::foreign::inert::{Inert, InertPayload};
|
||||||
use crate::gen::tree::{xfn_ent, ConstTree};
|
use crate::gen::tree::{xfn_ent, ConstTree};
|
||||||
use crate::interpreter::handler::HandlerTable;
|
use crate::interpreter::handler::HandlerTable;
|
||||||
@@ -84,7 +84,7 @@ impl<T> SharedHandle<T> {
|
|||||||
/// Remove the value from the handle if it's free. To interact with a handle
|
/// Remove the value from the handle if it's free. To interact with a handle
|
||||||
/// you probably want to use a [SeqScheduler], but sometimes this makes
|
/// you probably want to use a [SeqScheduler], but sometimes this makes
|
||||||
/// sense as eg. an optimization. You can return the value after processing
|
/// sense as eg. an optimization. You can return the value after processing
|
||||||
/// via [SyncHandle::untake].
|
/// via [SharedHandle::untake].
|
||||||
pub fn take(&self) -> Option<T> {
|
pub fn take(&self) -> Option<T> {
|
||||||
take_with_output(&mut *self.0.lock().unwrap(), |state| match state {
|
take_with_output(&mut *self.0.lock().unwrap(), |state| match state {
|
||||||
SharedResource::Free(t) => (SharedResource::Taken, Some(t)),
|
SharedResource::Free(t) => (SharedResource::Taken, Some(t)),
|
||||||
@@ -94,7 +94,7 @@ impl<T> SharedHandle<T> {
|
|||||||
|
|
||||||
/// Return the value to a handle that doesn't have one. The intended use case
|
/// Return the value to a handle that doesn't have one. The intended use case
|
||||||
/// is to return values synchronously after they have been removed with
|
/// is to return values synchronously after they have been removed with
|
||||||
/// [SyncHandle::untake].
|
/// [SharedHandle::take].
|
||||||
pub fn untake(&self, value: T) -> Result<(), T> {
|
pub fn untake(&self, value: T) -> Result<(), T> {
|
||||||
take_with_output(&mut *self.0.lock().unwrap(), |state| match state {
|
take_with_output(&mut *self.0.lock().unwrap(), |state| match state {
|
||||||
SharedResource::Taken => (SharedResource::Free(value), Ok(())),
|
SharedResource::Taken => (SharedResource::Free(value), Ok(())),
|
||||||
@@ -105,8 +105,8 @@ impl<T> SharedHandle<T> {
|
|||||||
impl<T> Clone for SharedHandle<T> {
|
impl<T> Clone for SharedHandle<T> {
|
||||||
fn clone(&self) -> Self { Self(self.0.clone()) }
|
fn clone(&self) -> Self { Self(self.0.clone()) }
|
||||||
}
|
}
|
||||||
impl<T> Debug for SharedHandle<T> {
|
impl<T> fmt::Debug for SharedHandle<T> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("SharedHandle")
|
f.debug_struct("SharedHandle")
|
||||||
.field("state", &self.state())
|
.field("state", &self.state())
|
||||||
.field("type", &type_name::<T>())
|
.field("type", &type_name::<T>())
|
||||||
@@ -127,8 +127,8 @@ impl<T: Send + 'static> InertPayload for SharedHandle<T> {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct TakeCmd(pub Arc<dyn Fn(SeqScheduler) + Send + Sync>);
|
struct TakeCmd(pub Arc<dyn Fn(SeqScheduler) + Send + Sync>);
|
||||||
impl Debug for TakeCmd {
|
impl fmt::Debug for TakeCmd {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "A command to drop a shared resource")
|
write!(f, "A command to drop a shared resource")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,7 +141,7 @@ impl InertPayload for SealedOrTaken {
|
|||||||
const TYPE_STR: &'static str = "SealedOrTaken";
|
const TYPE_STR: &'static str = "SealedOrTaken";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_and_drop(x: Expr) -> ExternResult<CPSBox<TakeCmd>> {
|
fn take_and_drop(x: Expr) -> RTResult<CPSBox<TakeCmd>> {
|
||||||
match x.clause.request() {
|
match x.clause.request() {
|
||||||
Some(t) => Ok(CPSBox::<TakeCmd>::new(1, t)),
|
Some(t) => Ok(CPSBox::<TakeCmd>::new(1, t)),
|
||||||
None => AssertionError::fail(x.location(), "SharedHandle", format!("{x}")),
|
None => AssertionError::fail(x.location(), "SharedHandle", format!("{x}")),
|
||||||
|
|||||||
@@ -36,13 +36,8 @@ pub trait Query: Send + 'static {
|
|||||||
/// runs exactly one type of task, this can appear only once in the code for
|
/// runs exactly one type of task, this can appear only once in the code for
|
||||||
/// a given thread pool. It is practical in a narrow set of cases, most of the
|
/// a given thread pool. It is practical in a narrow set of cases, most of the
|
||||||
/// time however you are better off defining an explicit reporter.
|
/// time however you are better off defining an explicit reporter.
|
||||||
fn then<F: FnOnce(Self::Result) + Send + 'static>(
|
fn then<F: FnOnce(Self::Result) + Send + 'static>(self, callback: F) -> QueryTask<Self, F>
|
||||||
self,
|
where Self: Sized {
|
||||||
callback: F,
|
|
||||||
) -> QueryTask<Self, F>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
QueryTask { query: self, callback }
|
QueryTask { query: self, callback }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
//! Error produced by numeric opperations
|
//! Error produced by numeric opperations
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::foreign::error::ExternError;
|
use crate::foreign::error::RTError;
|
||||||
|
|
||||||
/// Various errors produced by arithmetic operations
|
/// Various errors produced by arithmetic operations
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -17,8 +17,8 @@ pub enum ArithmeticError {
|
|||||||
NaN,
|
NaN,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ArithmeticError {
|
impl fmt::Display for ArithmeticError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::NaN => write!(f, "Operation resulted in NaN"),
|
Self::NaN => write!(f, "Operation resulted in NaN"),
|
||||||
Self::Overflow => write!(f, "Integer overflow"),
|
Self::Overflow => write!(f, "Integer overflow"),
|
||||||
@@ -28,4 +28,4 @@ impl Display for ArithmeticError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExternError for ArithmeticError {}
|
impl RTError for ArithmeticError {}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! `std::binary` Operations on binary buffers.
|
//! `std::binary` Operations on binary buffers.
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ use itertools::Itertools;
|
|||||||
|
|
||||||
use super::runtime_error::RuntimeError;
|
use super::runtime_error::RuntimeError;
|
||||||
use crate::foreign::atom::Atomic;
|
use crate::foreign::atom::Atomic;
|
||||||
use crate::foreign::error::ExternResult;
|
use crate::foreign::error::RTResult;
|
||||||
use crate::foreign::inert::{Inert, InertPayload};
|
use crate::foreign::inert::{Inert, InertPayload};
|
||||||
use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
|
use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
|
||||||
use crate::interpreter::nort::Clause;
|
use crate::interpreter::nort::Clause;
|
||||||
@@ -29,8 +29,8 @@ impl Deref for Binary {
|
|||||||
fn deref(&self) -> &Self::Target { &self.0 }
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Binary {
|
impl fmt::Debug for Binary {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let mut iter = self.0.iter().copied();
|
let mut iter = self.0.iter().copied();
|
||||||
f.write_str("Binary")?;
|
f.write_str("Binary")?;
|
||||||
for mut chunk in iter.by_ref().take(32).chunks(4).into_iter() {
|
for mut chunk in iter.by_ref().take(32).chunks(4).into_iter() {
|
||||||
@@ -51,7 +51,7 @@ pub fn concatenate(a: Inert<Binary>, b: Inert<Binary>) -> Inert<Binary> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Extract a subsection of the binary data
|
/// Extract a subsection of the binary data
|
||||||
pub fn slice(s: Inert<Binary>, i: Inert<usize>, len: Inert<usize>) -> ExternResult<Inert<Binary>> {
|
pub fn slice(s: Inert<Binary>, i: Inert<usize>, len: Inert<usize>) -> RTResult<Inert<Binary>> {
|
||||||
if i.0 + len.0 < s.0.0.len() {
|
if i.0 + len.0 < s.0.0.len() {
|
||||||
RuntimeError::fail("Byte index out of bounds".to_string(), "indexing binary")?
|
RuntimeError::fail("Byte index out of bounds".to_string(), "indexing binary")?
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ pub fn find(haystack: Inert<Binary>, needle: Inert<Binary>) -> Option<Clause> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Split binary data block into two smaller blocks
|
/// Split binary data block into two smaller blocks
|
||||||
pub fn split(bin: Inert<Binary>, i: Inert<usize>) -> ExternResult<(Inert<Binary>, Inert<Binary>)> {
|
pub fn split(bin: Inert<Binary>, i: Inert<usize>) -> RTResult<(Inert<Binary>, Inert<Binary>)> {
|
||||||
if bin.0.0.len() < i.0 {
|
if bin.0.0.len() < i.0 {
|
||||||
RuntimeError::fail("Byte index out of bounds".to_string(), "splitting binary")?
|
RuntimeError::fail("Byte index out of bounds".to_string(), "splitting binary")?
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ pub fn get_num(
|
|||||||
loc: Inert<usize>,
|
loc: Inert<usize>,
|
||||||
size: Inert<usize>,
|
size: Inert<usize>,
|
||||||
is_le: Inert<bool>,
|
is_le: Inert<bool>,
|
||||||
) -> ExternResult<Inert<usize>> {
|
) -> RTResult<Inert<usize>> {
|
||||||
if buf.0.0.len() < (loc.0 + size.0) {
|
if buf.0.0.len() < (loc.0 + size.0) {
|
||||||
RuntimeError::fail("section out of range".to_string(), "reading number from binary data")?
|
RuntimeError::fail("section out of range".to_string(), "reading number from binary data")?
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ pub fn from_num(
|
|||||||
size: Inert<usize>,
|
size: Inert<usize>,
|
||||||
is_le: Inert<bool>,
|
is_le: Inert<bool>,
|
||||||
data: Inert<usize>,
|
data: Inert<usize>,
|
||||||
) -> ExternResult<Inert<Binary>> {
|
) -> RTResult<Inert<Binary>> {
|
||||||
if INT_BYTES < size.0 {
|
if INT_BYTES < size.0 {
|
||||||
RuntimeError::fail(
|
RuntimeError::fail(
|
||||||
"more than std::bin::int_bytes bytes requested".to_string(),
|
"more than std::bin::int_bytes bytes requested".to_string(),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use super::number::Numeric;
|
use super::number::Numeric;
|
||||||
use super::string::OrcString;
|
use super::string::OrcString;
|
||||||
use crate::foreign::error::{AssertionError, ExternResult};
|
use crate::foreign::error::{AssertionError, RTResult};
|
||||||
use crate::foreign::inert::Inert;
|
use crate::foreign::inert::Inert;
|
||||||
use crate::foreign::try_from_expr::WithLoc;
|
use crate::foreign::try_from_expr::WithLoc;
|
||||||
use crate::gen::tpl;
|
use crate::gen::tpl;
|
||||||
@@ -26,7 +26,7 @@ pub fn if_then_else(WithLoc(loc, b): WithLoc<Inert<bool>>) -> Expr {
|
|||||||
/// - both are string,
|
/// - both are string,
|
||||||
/// - both are bool,
|
/// - both are bool,
|
||||||
/// - both are either uint or num
|
/// - both are either uint or num
|
||||||
pub fn equals(WithLoc(loc, a): WithLoc<Expr>, b: Expr) -> ExternResult<Inert<bool>> {
|
pub fn equals(WithLoc(loc, a): WithLoc<Expr>, b: Expr) -> RTResult<Inert<bool>> {
|
||||||
Ok(Inert(if let Ok(l) = a.clone().downcast::<Inert<OrcString>>() {
|
Ok(Inert(if let Ok(l) = a.clone().downcast::<Inert<OrcString>>() {
|
||||||
b.downcast::<Inert<OrcString>>().is_ok_and(|r| *l == *r)
|
b.downcast::<Inert<OrcString>>().is_ok_and(|r| *l == *r)
|
||||||
} else if let Ok(l) = a.clone().downcast::<Inert<bool>>() {
|
} else if let Ok(l) = a.clone().downcast::<Inert<bool>>() {
|
||||||
|
|||||||
@@ -1,69 +1,45 @@
|
|||||||
use once_cell::sync::Lazy;
|
|
||||||
use ordered_float::NotNan;
|
use ordered_float::NotNan;
|
||||||
|
|
||||||
use super::number::Numeric;
|
use super::number::Numeric;
|
||||||
use super::protocol::{gen_resolv, Protocol};
|
|
||||||
use super::string::OrcString;
|
use super::string::OrcString;
|
||||||
use crate::foreign::atom::Atomic;
|
use crate::foreign::error::{AssertionError, RTResult};
|
||||||
use crate::foreign::error::{AssertionError, ExternResult};
|
|
||||||
use crate::foreign::inert::Inert;
|
use crate::foreign::inert::Inert;
|
||||||
use crate::foreign::try_from_expr::WithLoc;
|
use crate::foreign::try_from_expr::WithLoc;
|
||||||
use crate::gen::tpl;
|
use crate::gen::tpl;
|
||||||
use crate::gen::traits::Gen;
|
use crate::gen::tree::{leaf, xfn_ent, ConstTree};
|
||||||
use crate::gen::tree::{xfn_ent, ConstTree};
|
use crate::interpreter::nort::ClauseInst;
|
||||||
use crate::interpreter::gen_nort::nort_gen;
|
|
||||||
use crate::interpreter::nort::{ClauseInst, Expr};
|
|
||||||
use crate::parse::numeric::parse_num;
|
use crate::parse::numeric::parse_num;
|
||||||
|
|
||||||
pub static TO_STRING: Lazy<Protocol> =
|
fn to_numeric(WithLoc(loc, a): WithLoc<ClauseInst>) -> RTResult<Numeric> {
|
||||||
Lazy::new(|| Protocol::new("to_string", []));
|
|
||||||
|
|
||||||
fn to_numeric(WithLoc(loc, a): WithLoc<ClauseInst>) -> ExternResult<Numeric> {
|
|
||||||
if let Some(n) = a.request::<Numeric>() {
|
if let Some(n) = a.request::<Numeric>() {
|
||||||
return Ok(n);
|
return Ok(n);
|
||||||
}
|
}
|
||||||
if let Some(s) = a.request::<OrcString>() {
|
if let Some(s) = a.request::<OrcString>() {
|
||||||
return parse_num(s.as_str()).map_err(|e| {
|
return parse_num(s.as_str())
|
||||||
AssertionError::ext(loc, "number syntax", format!("{e:?}"))
|
.map_err(|e| AssertionError::ext(loc, "number syntax", format!("{e:?}")));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
AssertionError::fail(loc, "string or number", format!("{a}"))
|
AssertionError::fail(loc, "string or number", format!("{a}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// parse a number. Accepts the same syntax Orchid does.
|
/// parse a number. Accepts the same syntax Orchid does.
|
||||||
pub fn to_float(a: WithLoc<ClauseInst>) -> ExternResult<Inert<NotNan<f64>>> {
|
pub fn to_float(a: WithLoc<ClauseInst>) -> RTResult<Inert<NotNan<f64>>> {
|
||||||
to_numeric(a).map(|n| Inert(n.as_float()))
|
to_numeric(a).map(|n| Inert(n.as_float()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an unsigned integer. Accepts the same formats Orchid does. If the
|
/// Parse an unsigned integer. Accepts the same formats Orchid does. If the
|
||||||
/// input is a number, floors it.
|
/// input is a number, floors it.
|
||||||
pub fn to_uint(a: WithLoc<ClauseInst>) -> ExternResult<Inert<usize>> {
|
pub fn to_uint(a: WithLoc<ClauseInst>) -> RTResult<Inert<usize>> {
|
||||||
to_numeric(a).map(|n| match n {
|
to_numeric(a).map(|n| match n {
|
||||||
Numeric::Float(f) => Inert(f.floor() as usize),
|
Numeric::Float(f) => Inert(f.floor() as usize),
|
||||||
Numeric::Uint(i) => Inert(i),
|
Numeric::Uint(i) => Inert(i),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a literal to a string using Rust's conversions for floats, chars and
|
|
||||||
/// uints respectively
|
|
||||||
pub fn to_string(WithLoc(loc, a): WithLoc<Expr>) -> Expr {
|
|
||||||
match a.clone().downcast::<Inert<OrcString>>() {
|
|
||||||
Ok(str) => str.atom_expr(loc),
|
|
||||||
Err(_) => match a.clause.request::<OrcString>() {
|
|
||||||
Some(str) => Inert(str).atom_expr(loc),
|
|
||||||
None => tpl::a2(gen_resolv("std::to_string"), tpl::Slot, tpl::Slot)
|
|
||||||
.template(nort_gen(loc), [a.clone(), a]),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn conv_lib() -> ConstTree {
|
pub fn conv_lib() -> ConstTree {
|
||||||
ConstTree::ns("std", [ConstTree::tree([
|
ConstTree::ns("std", [ConstTree::tree([ConstTree::tree_ent("conv", [
|
||||||
TO_STRING.as_tree_ent([]),
|
|
||||||
ConstTree::tree_ent("conv", [
|
|
||||||
xfn_ent("to_float", [to_float]),
|
xfn_ent("to_float", [to_float]),
|
||||||
xfn_ent("to_uint", [to_uint]),
|
xfn_ent("to_uint", [to_uint]),
|
||||||
xfn_ent("to_string", [to_string]),
|
// conversion logic moved to the string library
|
||||||
]),
|
("to_string", leaf(tpl::C("std::string::convert"))),
|
||||||
])])
|
])])])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
use std::collections::VecDeque;
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::foreign::atom::Atomic;
|
use crate::foreign::atom::Atomic;
|
||||||
use crate::foreign::fn_bridge::xfn;
|
use crate::foreign::fn_bridge::xfn;
|
||||||
use crate::foreign::process::Unstable;
|
use crate::foreign::process::Unstable;
|
||||||
use crate::foreign::to_clause::ToClause;
|
use crate::foreign::to_clause::ToClause;
|
||||||
use crate::foreign::try_from_expr::TryFromExpr;
|
use crate::foreign::try_from_expr::{TryFromExpr, WithLoc};
|
||||||
use crate::location::{CodeLocation, SourceRange};
|
use crate::location::SourceRange;
|
||||||
use crate::parse::parsed::{self, PType};
|
use crate::parse::parsed::{self, PType};
|
||||||
use crate::utils::pure_seq::pushed;
|
use crate::utils::pure_seq::pushed;
|
||||||
|
|
||||||
pub trait DeferredRuntimeCallback<T, U, R: ToClause>:
|
pub trait DeferredRuntimeCallback<T, R: ToClause>:
|
||||||
Fn(Vec<(T, U)>) -> R + Clone + Send + 'static
|
FnOnce(Vec<T>) -> R + Clone + Send + 'static
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
impl<T, U, R: ToClause, F: Fn(Vec<(T, U)>) -> R + Clone + Send + 'static>
|
impl<T, R: ToClause, F: FnOnce(Vec<T>) -> R + Clone + Send + 'static> DeferredRuntimeCallback<T, R>
|
||||||
DeferredRuntimeCallback<T, U, R> for F
|
for F
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,56 +27,39 @@ impl<T, U, R: ToClause, F: Fn(Vec<(T, U)>) -> R + Clone + Send + 'static>
|
|||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// If the list of remaining keys is empty
|
/// If the list of remaining keys is empty
|
||||||
fn table_receiver_rec<
|
fn table_receiver_rec<T: TryFromExpr + Clone + Send + 'static, R: ToClause + 'static>(
|
||||||
T: Clone + Send + 'static,
|
results: Vec<T>,
|
||||||
U: TryFromExpr + Clone + Send + 'static,
|
items: usize,
|
||||||
R: ToClause + 'static,
|
callback: impl DeferredRuntimeCallback<T, R>,
|
||||||
>(
|
|
||||||
range: SourceRange,
|
|
||||||
results: Vec<(T, U)>,
|
|
||||||
mut remaining_keys: VecDeque<T>,
|
|
||||||
callback: impl DeferredRuntimeCallback<T, U, R>,
|
|
||||||
) -> impl Atomic {
|
) -> impl Atomic {
|
||||||
let t = remaining_keys.pop_front().expect("empty handled elsewhere");
|
xfn("__table_receiver__", move |WithLoc(loc, t): WithLoc<T>| {
|
||||||
xfn("__table_receiver__", move |u: U| {
|
let results: Vec<T> = pushed(results, t);
|
||||||
let results = pushed(results, (t, u));
|
match items == results.len() {
|
||||||
match remaining_keys.is_empty() {
|
true => callback(results).to_clause(loc),
|
||||||
true => callback(results).to_clause(CodeLocation::Source(range)),
|
false => table_receiver_rec(results, items, callback).atom_cls(),
|
||||||
false => table_receiver_rec(range, results, remaining_keys, callback).atom_cls(),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn table_receiver<
|
fn table_receiver<T: TryFromExpr + Clone + Send + 'static, R: ToClause + 'static>(
|
||||||
T: Clone + Send + 'static,
|
items: usize,
|
||||||
U: TryFromExpr + Clone + Send + 'static,
|
callback: impl DeferredRuntimeCallback<T, R>,
|
||||||
R: ToClause + 'static,
|
|
||||||
>(
|
|
||||||
range: SourceRange,
|
|
||||||
keys: VecDeque<T>,
|
|
||||||
callback: impl DeferredRuntimeCallback<T, U, R>,
|
|
||||||
) -> parsed::Clause {
|
) -> parsed::Clause {
|
||||||
if keys.is_empty() {
|
if items == 0 {
|
||||||
Unstable::new(move |_| callback(Vec::new())).ast_cls()
|
Unstable::new(move |_| callback(Vec::new())).ast_cls()
|
||||||
} else {
|
} else {
|
||||||
Unstable::new(move |_| table_receiver_rec(range, Vec::new(), keys, callback).atom_cls())
|
Unstable::new(move |_| table_receiver_rec(Vec::new(), items, callback).atom_cls()).ast_cls()
|
||||||
.ast_cls()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defers the execution of the callback to runtime, allowing it to depend on
|
/// Defers the execution of the callback to runtime, allowing it to depend on
|
||||||
/// the result of Otchid expressions.
|
/// the result of Otchid expressions.
|
||||||
pub fn defer_to_runtime<
|
pub fn defer_to_runtime<T: TryFromExpr + Clone + Send + 'static, R: ToClause + 'static>(
|
||||||
T: Clone + Send + 'static,
|
|
||||||
U: TryFromExpr + Clone + Send + 'static,
|
|
||||||
R: ToClause + 'static,
|
|
||||||
>(
|
|
||||||
range: SourceRange,
|
range: SourceRange,
|
||||||
pairs: impl IntoIterator<Item = (T, Vec<parsed::Expr>)>,
|
exprs: impl Iterator<Item = Vec<parsed::Expr>>,
|
||||||
callback: impl DeferredRuntimeCallback<T, U, R>,
|
callback: impl DeferredRuntimeCallback<T, R>,
|
||||||
) -> parsed::Clause {
|
) -> parsed::Clause {
|
||||||
let (keys, ast_values) = pairs.into_iter().unzip::<_, _, VecDeque<_>, Vec<_>>();
|
let argv = exprs.into_iter().map(|v| parsed::Clause::S(PType::Par, Rc::new(v))).collect_vec();
|
||||||
let items = iter::once(table_receiver(range.clone(), keys, callback))
|
let items = iter::once(table_receiver(argv.len(), callback)).chain(argv);
|
||||||
.chain(ast_values.into_iter().map(|v| parsed::Clause::S(PType::Par, Rc::new(v))));
|
|
||||||
parsed::Clause::s('(', items, range)
|
parsed::Clause::s('(', items, range)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,17 @@ use crate::foreign::inert::{Inert, InertPayload};
|
|||||||
use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
|
use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
|
||||||
|
|
||||||
/// An Orchid equivalent to Rust's binary exit status model
|
/// An Orchid equivalent to Rust's binary exit status model
|
||||||
|
///
|
||||||
|
/// The "namespaced" name is to easily separate in autocomplete lists from both
|
||||||
|
/// [std::process::ExitStatus] and [std::process::ExitCode]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum ExitStatus {
|
pub enum OrcExitStatus {
|
||||||
/// unix exit code 0
|
/// unix exit code 0
|
||||||
Success,
|
Success,
|
||||||
/// unix exit code 1
|
/// unix exit code 1
|
||||||
Failure,
|
Failure,
|
||||||
}
|
}
|
||||||
impl ExitStatus {
|
impl OrcExitStatus {
|
||||||
/// Convert to Rust-land [ExitCode]
|
/// Convert to Rust-land [ExitCode]
|
||||||
pub fn code(self) -> ExitCode {
|
pub fn code(self) -> ExitCode {
|
||||||
match self {
|
match self {
|
||||||
@@ -26,15 +29,15 @@ impl ExitStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InertPayload for ExitStatus {
|
impl InertPayload for OrcExitStatus {
|
||||||
const TYPE_STR: &'static str = "ExitStatus";
|
const TYPE_STR: &'static str = "ExitStatus";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn exit_status_lib() -> ConstTree {
|
pub(super) fn exit_status_lib() -> ConstTree {
|
||||||
let is_success = |es: Inert<ExitStatus>| Inert(es.0 == ExitStatus::Success);
|
let is_success = |es: Inert<OrcExitStatus>| Inert(es.0 == OrcExitStatus::Success);
|
||||||
ConstTree::ns("std::exit_status", [ConstTree::tree([
|
ConstTree::ns("std::exit_status", [ConstTree::tree([
|
||||||
atom_ent("success", [Inert(ExitStatus::Success)]),
|
atom_ent("success", [Inert(OrcExitStatus::Success)]),
|
||||||
atom_ent("failure", [Inert(ExitStatus::Failure)]),
|
atom_ent("failure", [Inert(OrcExitStatus::Failure)]),
|
||||||
xfn_ent("is_success", [is_success]),
|
xfn_ent("is_success", [is_success]),
|
||||||
])])
|
])])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,13 @@
|
|||||||
use std::fmt::Debug;
|
use crate::foreign::fn_bridge::Thunk;
|
||||||
|
use crate::gen::tree::{xfn_ent, ConstTree};
|
||||||
use crate::foreign::atom::{Atomic, AtomicResult, AtomicReturn, CallData, RunData};
|
use crate::interpreter::nort::Expr;
|
||||||
use crate::foreign::error::ExternResult;
|
|
||||||
use crate::foreign::to_clause::ToClause;
|
|
||||||
use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
|
|
||||||
use crate::interpreter::nort::{Clause, Expr};
|
|
||||||
use crate::utils::ddispatch::Responder;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct Inspect;
|
|
||||||
impl Responder for Inspect {}
|
|
||||||
impl Atomic for Inspect {
|
|
||||||
fn as_any(self: Box<Self>) -> Box<dyn std::any::Any> { self }
|
|
||||||
fn as_any_ref(&self) -> &dyn std::any::Any { self }
|
|
||||||
fn redirect(&mut self) -> Option<&mut Expr> { None }
|
|
||||||
fn run(self: Box<Self>, _: RunData) -> AtomicResult { AtomicReturn::inert(*self) }
|
|
||||||
fn apply_ref(&self, call: CallData) -> ExternResult<Clause> {
|
|
||||||
eprintln!("{}", call.arg);
|
|
||||||
Ok(call.arg.to_clause(call.location))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inspect_lib() -> ConstTree {
|
pub fn inspect_lib() -> ConstTree {
|
||||||
ConstTree::ns("std", [ConstTree::tree([
|
ConstTree::ns("std", [ConstTree::tree([
|
||||||
atom_ent("inspect", [Inspect]),
|
xfn_ent("inspect", [|x: Thunk| {
|
||||||
|
eprintln!("{}", x.0);
|
||||||
|
x.0
|
||||||
|
}]),
|
||||||
xfn_ent("tee", [|x: Expr| {
|
xfn_ent("tee", [|x: Expr| {
|
||||||
eprintln!("{x}");
|
eprintln!("{x}");
|
||||||
x
|
x
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import super::(option, tuple, tuple::t, panic, pmatch, pmatch::=>, macro, tee)
|
import super::(option, tuple, tuple::t, panic, pmatch, pmatch::=>, macro, tee)
|
||||||
import super::(functional::*, procedural::*)
|
import super::(fn::*, procedural::*)
|
||||||
import super::(loop::*, bool::*, known::*, number::*)
|
import super::(loop::*, bool::*, known::*, number::*)
|
||||||
|
|
||||||
as_type list ()
|
as_type ()
|
||||||
|
|
||||||
export const cons := \hd. \tl. wrap (option::some t[hd, unwrap tl])
|
export const cons := \hd. \tl. wrap (option::some t[hd, unwrap tl])
|
||||||
export const end := wrap option::none
|
export const end := wrap option::none
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import super::procedural::*
|
import super::procedural::*
|
||||||
import super::bool::*
|
import super::bool::*
|
||||||
import super::functional::(return, identity)
|
import super::fn::(return, identity)
|
||||||
import super::known::*
|
import super::known::*
|
||||||
|
|
||||||
--[
|
--[
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import super::(bool::*, functional::*, known::*, loop::*, procedural::*, string::*)
|
import super::(bool::*, fn::*, known::*, loop::*, procedural::*, string::*)
|
||||||
import super::(panic, pmatch, macro, option, list, tuple, to_string, conv, pmatch::[=>])
|
import super::(panic, pmatch, macro, option, list, string, tuple, conv, pmatch::[=>])
|
||||||
|
|
||||||
as_type map (
|
as_type (
|
||||||
impl to_string := \map. "map[" ++ (
|
impl string::conversion := \map. "map[" ++ (
|
||||||
unwrap map
|
unwrap map
|
||||||
|> list::map (
|
|> list::map (
|
||||||
(tuple::t[k, v]) => conv::to_string k ++ " = " ++ conv::to_string v
|
(tuple::t[k, v]) => conv::to_string k ++ " = " ++ conv::to_string v
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ pub mod exit_status;
|
|||||||
mod inspect;
|
mod inspect;
|
||||||
pub mod number;
|
pub mod number;
|
||||||
mod panic;
|
mod panic;
|
||||||
mod protocol;
|
pub mod protocol;
|
||||||
pub mod reflect;
|
pub mod reflect;
|
||||||
pub mod runtime_error;
|
pub mod runtime_error;
|
||||||
mod state;
|
mod state;
|
||||||
pub mod std_system;
|
pub mod std_system;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
pub mod tuple;
|
pub mod tuple;
|
||||||
mod tstring;
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use ordered_float::NotNan;
|
|||||||
|
|
||||||
use super::arithmetic_error::ArithmeticError;
|
use super::arithmetic_error::ArithmeticError;
|
||||||
use crate::foreign::atom::Atomic;
|
use crate::foreign::atom::Atomic;
|
||||||
use crate::foreign::error::{AssertionError, ExternError, ExternResult};
|
use crate::foreign::error::{AssertionError, RTError, RTResult};
|
||||||
use crate::foreign::inert::Inert;
|
use crate::foreign::inert::Inert;
|
||||||
use crate::foreign::to_clause::ToClause;
|
use crate::foreign::to_clause::ToClause;
|
||||||
use crate::foreign::try_from_expr::TryFromExpr;
|
use crate::foreign::try_from_expr::TryFromExpr;
|
||||||
@@ -34,27 +34,25 @@ impl Numeric {
|
|||||||
pub fn as_float(&self) -> NotNan<f64> {
|
pub fn as_float(&self) -> NotNan<f64> {
|
||||||
match self {
|
match self {
|
||||||
Numeric::Float(n) => *n,
|
Numeric::Float(n) => *n,
|
||||||
Numeric::Uint(i) =>
|
Numeric::Uint(i) => NotNan::new(*i as f64).expect("ints cannot cast to NaN"),
|
||||||
NotNan::new(*i as f64).expect("ints cannot cast to NaN"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrap a f64 in a Numeric
|
/// Wrap a f64 in a Numeric
|
||||||
pub fn new(value: f64) -> ExternResult<Self> {
|
pub fn new(value: f64) -> RTResult<Self> {
|
||||||
match value.is_finite() {
|
match value.is_finite() {
|
||||||
false => Err(ArithmeticError::Infinity.rc()),
|
false => Err(ArithmeticError::Infinity.pack()),
|
||||||
true => match NotNan::new(value) {
|
true => match NotNan::new(value) {
|
||||||
Ok(f) => Ok(Self::Float(f)),
|
Ok(f) => Ok(Self::Float(f)),
|
||||||
Err(_) => Err(ArithmeticError::NaN.rc()),
|
Err(_) => Err(ArithmeticError::NaN.pack()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl TryFromExpr for Numeric {
|
impl TryFromExpr for Numeric {
|
||||||
fn from_expr(exi: Expr) -> ExternResult<Self> {
|
fn from_expr(exi: Expr) -> RTResult<Self> {
|
||||||
(exi.clause.request()).ok_or_else(|| {
|
(exi.clause.request())
|
||||||
AssertionError::ext(exi.location(), "a numeric value", format!("{exi}"))
|
.ok_or_else(|| AssertionError::ext(exi.location(), "a numeric value", format!("{exi}")))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,20 +67,18 @@ impl ToClause for Numeric {
|
|||||||
|
|
||||||
/// Add two numbers. If they're both uint, the output is uint. If either is
|
/// Add two numbers. If they're both uint, the output is uint. If either is
|
||||||
/// number, the output is number.
|
/// number, the output is number.
|
||||||
pub fn add(a: Numeric, b: Numeric) -> ExternResult<Numeric> {
|
pub fn add(a: Numeric, b: Numeric) -> RTResult<Numeric> {
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
(Numeric::Uint(a), Numeric::Uint(b)) => a
|
(Numeric::Uint(a), Numeric::Uint(b)) =>
|
||||||
.checked_add(b)
|
a.checked_add(b).map(Numeric::Uint).ok_or_else(|| ArithmeticError::Overflow.pack()),
|
||||||
.map(Numeric::Uint)
|
|
||||||
.ok_or_else(|| ArithmeticError::Overflow.rc()),
|
|
||||||
(Numeric::Float(a), Numeric::Float(b)) => Numeric::new(*(a + b)),
|
(Numeric::Float(a), Numeric::Float(b)) => Numeric::new(*(a + b)),
|
||||||
(Numeric::Float(a), Numeric::Uint(b))
|
(Numeric::Float(a), Numeric::Uint(b)) | (Numeric::Uint(b), Numeric::Float(a)) =>
|
||||||
| (Numeric::Uint(b), Numeric::Float(a)) => Numeric::new(*a + b as f64),
|
Numeric::new(*a + b as f64),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Subtract a number from another. Always returns Number.
|
/// Subtract a number from another. Always returns Number.
|
||||||
pub fn subtract(a: Numeric, b: Numeric) -> ExternResult<Numeric> {
|
pub fn subtract(a: Numeric, b: Numeric) -> RTResult<Numeric> {
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::new(a as f64 - b as f64),
|
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::new(a as f64 - b as f64),
|
||||||
(Numeric::Float(a), Numeric::Float(b)) => Numeric::new(*(a - b)),
|
(Numeric::Float(a), Numeric::Float(b)) => Numeric::new(*(a - b)),
|
||||||
@@ -93,36 +89,32 @@ pub fn subtract(a: Numeric, b: Numeric) -> ExternResult<Numeric> {
|
|||||||
|
|
||||||
/// Multiply two numbers. If they're both uint, the output is uint. If either
|
/// Multiply two numbers. If they're both uint, the output is uint. If either
|
||||||
/// is number, the output is number.
|
/// is number, the output is number.
|
||||||
pub fn multiply(a: Numeric, b: Numeric) -> ExternResult<Numeric> {
|
pub fn multiply(a: Numeric, b: Numeric) -> RTResult<Numeric> {
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
(Numeric::Uint(a), Numeric::Uint(b)) => a
|
(Numeric::Uint(a), Numeric::Uint(b)) =>
|
||||||
.checked_mul(b)
|
a.checked_mul(b).map(Numeric::Uint).ok_or_else(|| ArithmeticError::Overflow.pack()),
|
||||||
.map(Numeric::Uint)
|
|
||||||
.ok_or_else(|| ArithmeticError::Overflow.rc()),
|
|
||||||
(Numeric::Float(a), Numeric::Float(b)) => Numeric::new(*(a * b)),
|
(Numeric::Float(a), Numeric::Float(b)) => Numeric::new(*(a * b)),
|
||||||
(Numeric::Uint(a), Numeric::Float(b))
|
(Numeric::Uint(a), Numeric::Float(b)) | (Numeric::Float(b), Numeric::Uint(a)) =>
|
||||||
| (Numeric::Float(b), Numeric::Uint(a)) => Numeric::new(a as f64 * *b),
|
Numeric::new(a as f64 * *b),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Divide a number by another. Always returns Number.
|
/// Divide a number by another. Always returns Number.
|
||||||
pub fn divide(a: Numeric, b: Numeric) -> ExternResult<Numeric> {
|
pub fn divide(a: Numeric, b: Numeric) -> RTResult<Numeric> {
|
||||||
let a: f64 = a.as_f64();
|
let a: f64 = a.as_f64();
|
||||||
let b: f64 = b.as_f64();
|
let b: f64 = b.as_f64();
|
||||||
if b == 0.0 {
|
if b == 0.0 {
|
||||||
return Err(ArithmeticError::DivByZero.rc());
|
return Err(ArithmeticError::DivByZero.pack());
|
||||||
}
|
}
|
||||||
Numeric::new(a / b)
|
Numeric::new(a / b)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take the remainder of two numbers. If they're both uint, the output is
|
/// Take the remainder of two numbers. If they're both uint, the output is
|
||||||
/// uint. If either is number, the output is number.
|
/// uint. If either is number, the output is number.
|
||||||
pub fn remainder(a: Numeric, b: Numeric) -> ExternResult<Numeric> {
|
pub fn remainder(a: Numeric, b: Numeric) -> RTResult<Numeric> {
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
(Numeric::Uint(a), Numeric::Uint(b)) => a
|
(Numeric::Uint(a), Numeric::Uint(b)) =>
|
||||||
.checked_rem(b)
|
a.checked_rem(b).map(Numeric::Uint).ok_or_else(|| ArithmeticError::DivByZero.pack()),
|
||||||
.map(Numeric::Uint)
|
|
||||||
.ok_or_else(|| ArithmeticError::DivByZero.rc()),
|
|
||||||
(Numeric::Float(a), Numeric::Float(b)) => Numeric::new(*(a % b)),
|
(Numeric::Float(a), Numeric::Float(b)) => Numeric::new(*(a % b)),
|
||||||
(Numeric::Uint(a), Numeric::Float(b)) => Numeric::new(a as f64 % *b),
|
(Numeric::Uint(a), Numeric::Float(b)) => Numeric::new(a as f64 % *b),
|
||||||
(Numeric::Float(a), Numeric::Uint(b)) => Numeric::new(*a % b as f64),
|
(Numeric::Float(a), Numeric::Uint(b)) => Numeric::new(*a % b as f64),
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import std::(panic, pmatch, to_string, conv)
|
import std::(panic, pmatch, string, conv)
|
||||||
import std::(functional::*, string::*)
|
import std::(fn::*, string::*)
|
||||||
|
|
||||||
as_type option (
|
as_type (
|
||||||
impl to_string := \opt. (
|
impl string::conversion := \opt. (
|
||||||
handle opt "none" \x. "some(" ++ conv::to_string x ++ ")"
|
handle opt "none" \x. "some(" ++ conv::to_string x ++ ")"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use never::Never;
|
use never::Never;
|
||||||
|
|
||||||
use super::string::OrcString;
|
use super::string::OrcString;
|
||||||
use crate::foreign::error::{ExternError, ExternResult};
|
use crate::foreign::error::{RTError, RTResult};
|
||||||
use crate::foreign::inert::Inert;
|
use crate::foreign::inert::Inert;
|
||||||
use crate::gen::tree::{xfn_leaf, ConstTree};
|
use crate::gen::tree::{xfn_leaf, ConstTree};
|
||||||
|
|
||||||
@@ -13,18 +13,18 @@ use crate::gen::tree::{xfn_leaf, ConstTree};
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct OrchidPanic(Arc<String>);
|
pub struct OrchidPanic(Arc<String>);
|
||||||
|
|
||||||
impl Display for OrchidPanic {
|
impl fmt::Display for OrchidPanic {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "Orchid code panicked: {}", self.0)
|
write!(f, "Orchid code panicked: {}", self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExternError for OrchidPanic {}
|
impl RTError for OrchidPanic {}
|
||||||
|
|
||||||
/// Takes a message, returns an [ExternError] unconditionally.
|
/// Takes a message, returns an [ExternError] unconditionally.
|
||||||
pub fn orc_panic(msg: Inert<OrcString>) -> ExternResult<Never> {
|
pub fn orc_panic(msg: Inert<OrcString>) -> RTResult<Never> {
|
||||||
// any return value would work, but Clause is the simplest
|
// any return value would work, but Clause is the simplest
|
||||||
Err(OrchidPanic(Arc::new(msg.0.get_string())).rc())
|
Err(OrchidPanic(Arc::new(msg.0.get_string())).pack())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn panic_lib() -> ConstTree { ConstTree::ns("std::panic", [xfn_leaf(orc_panic)]) }
|
pub fn panic_lib() -> ConstTree { ConstTree::ns("std::panic", [xfn_leaf(orc_panic)]) }
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import std::string::*
|
|||||||
export ::[++]
|
export ::[++]
|
||||||
import std::bool::*
|
import std::bool::*
|
||||||
export ::([== !=], if, then, else, true, false, and, or, not)
|
export ::([== !=], if, then, else, true, false, and, or, not)
|
||||||
import std::functional::*
|
import std::fn::*
|
||||||
export ::([$ |>], identity, pass, pass2, return)
|
export ::([$ |>], identity, pass, pass2, return)
|
||||||
import std::procedural::*
|
import std::procedural::*
|
||||||
export ::(do, let, cps)
|
export ::(do, let, cps)
|
||||||
@@ -12,16 +12,14 @@ import std::tuple::t
|
|||||||
export ::(t)
|
export ::(t)
|
||||||
import std::pmatch::(match, [=>])
|
import std::pmatch::(match, [=>])
|
||||||
export ::(match, [=>])
|
export ::(match, [=>])
|
||||||
import std::tuple
|
|
||||||
import std::list
|
|
||||||
import std::map
|
|
||||||
import std::option
|
|
||||||
export ::(tuple, list, map, option)
|
|
||||||
import std::loop::*
|
import std::loop::*
|
||||||
export ::(loop_over, recursive, while)
|
export ::(loop_over, recursive, while)
|
||||||
|
|
||||||
import std::known::*
|
import std::known::*
|
||||||
export ::[, _ ; . =]
|
export ::[, _ ; . =]
|
||||||
|
|
||||||
|
import std::(tuple, list, map, option, exit_status)
|
||||||
|
export ::(tuple, list, map, option, exit_status)
|
||||||
|
|
||||||
import std
|
import std
|
||||||
export ::(std)
|
export ::(std)
|
||||||
|
|||||||
8
src/libs/std/protocol.orc
Normal file
8
src/libs/std/protocol.orc
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import std::(map, option, fn::*)
|
||||||
|
|
||||||
|
export const vcall := \proto. \key. \val. (
|
||||||
|
resolve proto val
|
||||||
|
|> map::get key
|
||||||
|
|> option::assume
|
||||||
|
$ break val
|
||||||
|
)
|
||||||
@@ -1,6 +1,16 @@
|
|||||||
use std::fmt::Debug;
|
//! Polymorphism through Elixir-style protocols associated with type-tagged
|
||||||
use std::iter;
|
//! values. A type-tag seals the value, and can only be unwrapped explicitly by
|
||||||
|
//! providing the correct type. This ensures that code that uses this explicit
|
||||||
|
//! polymorphism never uses the implicit polymorphism of dynamic typing.
|
||||||
|
//!
|
||||||
|
//! Atoms can also participate in this controlled form of polymorphism by
|
||||||
|
//! offering a [Tag] in their [crate::utils::ddispatch::Responder] callback.
|
||||||
|
//!
|
||||||
|
//! Protocols and types are modules with magic elements that distinguish them
|
||||||
|
//! from regular modules.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::{fmt, iter};
|
||||||
|
|
||||||
use const_format::formatcp;
|
use const_format::formatcp;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
@@ -8,18 +18,17 @@ use intern_all::{i, Tok};
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use super::cross_pipeline::defer_to_runtime;
|
use super::cross_pipeline::defer_to_runtime;
|
||||||
use super::reflect::{refer_seq, RefEqual};
|
use super::reflect::refer_seq;
|
||||||
use super::runtime_error::RuntimeError;
|
use super::runtime_error::RuntimeError;
|
||||||
use crate::error::ProjectResult;
|
use crate::error::ProjectResult;
|
||||||
use crate::foreign::atom::Atomic;
|
use crate::foreign::atom::Atomic;
|
||||||
use crate::foreign::error::ExternResult;
|
use crate::foreign::error::RTResult;
|
||||||
use crate::foreign::inert::{Inert, InertPayload};
|
use crate::foreign::inert::{Inert, InertPayload};
|
||||||
use crate::foreign::process::Unstable;
|
use crate::foreign::process::Unstable;
|
||||||
use crate::foreign::to_clause::ToClause;
|
|
||||||
use crate::gen::tpl;
|
use crate::gen::tpl;
|
||||||
use crate::gen::traits::GenClause;
|
use crate::gen::traits::GenClause;
|
||||||
use crate::gen::tree::{atom_leaf, xfn_ent, ConstTree};
|
use crate::gen::tree::{atom_ent, leaf, xfn_ent, ConstTree};
|
||||||
use crate::interpreter::nort as int;
|
use crate::interpreter::nort;
|
||||||
use crate::interpreter::nort::ClauseInst;
|
use crate::interpreter::nort::ClauseInst;
|
||||||
use crate::libs::parse_custom_line::custom_line;
|
use crate::libs::parse_custom_line::custom_line;
|
||||||
use crate::location::SourceRange;
|
use crate::location::SourceRange;
|
||||||
@@ -28,133 +37,175 @@ use crate::parse::frag::Frag;
|
|||||||
use crate::parse::lexer::Lexeme;
|
use crate::parse::lexer::Lexeme;
|
||||||
use crate::parse::parse_plugin::{ParseLinePlugin, ParsePluginReq};
|
use crate::parse::parse_plugin::{ParseLinePlugin, ParsePluginReq};
|
||||||
use crate::parse::parsed::{
|
use crate::parse::parsed::{
|
||||||
self, Constant, Member, MemberKind, ModuleBlock, PType, SourceLine,
|
self, Constant, Member, MemberKind, ModuleBlock, PType, SourceLine, SourceLineKind,
|
||||||
SourceLineKind,
|
|
||||||
};
|
};
|
||||||
use crate::utils::ddispatch::Request;
|
use crate::utils::ddispatch::Request;
|
||||||
|
|
||||||
|
// TODO: write an example that thoroughly tests this module. Test rust-interop
|
||||||
|
// with Tuple
|
||||||
|
|
||||||
|
/// Information available both for protocols and for tagged values
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct TypeData {
|
pub struct TypeData {
|
||||||
pub id: RefEqual,
|
/// The full path of the module designated to represent this type or protocol.
|
||||||
pub display_name: Tok<String>,
|
/// Also used to key impl tables in the counterpart (tag or protocol)
|
||||||
pub impls: HashMap<RefEqual, int::Expr>,
|
pub id: Sym,
|
||||||
|
/// Maps IDs of the counterpart (tag or protocol) to implementations
|
||||||
|
pub impls: Arc<HashMap<Sym, nort::Expr>>,
|
||||||
|
}
|
||||||
|
impl TypeData {
|
||||||
|
/// Create a new type data record from a known name and impls
|
||||||
|
pub fn new(id: Sym, impls: impl IntoIterator<Item = (Sym, nort::Expr)>) -> Self {
|
||||||
|
Self { id, impls: Arc::new(impls.into_iter().collect()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mk_mod<'a>(
|
||||||
|
rest: impl IntoIterator<Item = (&'a str, ConstTree)>,
|
||||||
|
impls: HashMap<Sym, nort::Expr>,
|
||||||
|
profile: ImplsProfile<impl WrapImpl>,
|
||||||
|
) -> ConstTree {
|
||||||
|
ConstTree::tree(rest.into_iter().chain([
|
||||||
|
(profile.own_id, leaf(tpl::A(tpl::C("std::reflect::modname"), tpl::V(Inert(1))))),
|
||||||
|
atom_ent(TYPE_KEY, [use_wrap(profile.wrap, impls)]),
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_mod<'a>(
|
||||||
|
rest: impl IntoIterator<Item = (&'a str, ConstTree)>,
|
||||||
|
data: TypeData,
|
||||||
|
profile: ImplsProfile<impl WrapImpl>,
|
||||||
|
) -> ConstTree {
|
||||||
|
let id = data.id.clone();
|
||||||
|
ConstTree::tree(rest.into_iter().chain([
|
||||||
|
atom_ent(profile.own_id, [Unstable::new(move |r| {
|
||||||
|
assert!(r.location.module == id, "Pre-initilaized type lib mounted on wrong prefix");
|
||||||
|
Inert(r.location.module)
|
||||||
|
})]),
|
||||||
|
atom_ent(TYPE_KEY, [profile.wrap.wrap(data)]),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Key for type data. The value is either [Inert<Protocol>] or [Inert<Tag>]
|
/// Key for type data. The value is either [Inert<Protocol>] or [Inert<Tag>]
|
||||||
const TYPE_KEY: &str = "__type_data__";
|
const TYPE_KEY: &str = "__type_data__";
|
||||||
|
|
||||||
|
/// A shared behaviour that may implement itself for types, and may be
|
||||||
|
/// implemented by types.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Protocol(pub Arc<TypeData>);
|
pub struct Protocol(pub TypeData);
|
||||||
impl Protocol {
|
impl Protocol {
|
||||||
const ID_KEY: &'static str = "__protocol_id__";
|
/// Name of the member the ID must be assigned to for a module to be
|
||||||
|
/// recognized as a protocol.
|
||||||
pub fn new_id(
|
pub const ID_KEY: &'static str = "__protocol_id__";
|
||||||
id: RefEqual,
|
const fn profile() -> ImplsProfile<impl WrapImpl> {
|
||||||
display_name: Tok<String>,
|
ImplsProfile {
|
||||||
impls: impl IntoIterator<Item = (RefEqual, int::Expr)>,
|
wrap: |t| Inert(Protocol(t)),
|
||||||
) -> Self {
|
own_id: Protocol::ID_KEY,
|
||||||
let impls = impls.into_iter().collect();
|
other_id: Tag::ID_KEY,
|
||||||
Protocol(Arc::new(TypeData { id, display_name, impls }))
|
prelude: formatcp!(
|
||||||
|
"import std
|
||||||
|
const {} := std::reflect::modname 1
|
||||||
|
const resolve := std::protocol::resolve {TYPE_KEY}
|
||||||
|
const vcall := std::protocol::vcall {TYPE_KEY}",
|
||||||
|
Protocol::ID_KEY
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(
|
/// Create a new protocol with a pre-determined name
|
||||||
display_name: &'static str,
|
pub fn new(id: Sym, impls: impl IntoIterator<Item = (Sym, nort::Expr)>) -> Self {
|
||||||
impls: impl IntoIterator<Item = (RefEqual, int::Expr)>,
|
Self(TypeData::new(id, impls))
|
||||||
) -> Self {
|
|
||||||
Self::new_id(RefEqual::new(), i(display_name), impls)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> RefEqual { self.0.id.clone() }
|
/// Attach a pre-existing protocol to the tree. Consider [Protocol::tree].
|
||||||
|
pub fn to_tree<'a>(&self, rest: impl IntoIterator<Item = (&'a str, ConstTree)>) -> ConstTree {
|
||||||
|
to_mod(rest, self.0.clone(), Self::profile())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_tree_ent<'a>(
|
/// Create a new protocol definition
|
||||||
&'a self,
|
pub fn tree<'a>(
|
||||||
|
impls: impl IntoIterator<Item = (Sym, nort::Expr)>,
|
||||||
rest: impl IntoIterator<Item = (&'a str, ConstTree)>,
|
rest: impl IntoIterator<Item = (&'a str, ConstTree)>,
|
||||||
) -> (&str, ConstTree) {
|
) -> ConstTree {
|
||||||
ConstTree::tree_ent(
|
mk_mod(rest, impls.into_iter().collect(), Self::profile())
|
||||||
self.0.display_name.as_str(),
|
|
||||||
rest.into_iter().chain([
|
|
||||||
(Self::ID_KEY, atom_leaf(Inert(self.id()))),
|
|
||||||
(TYPE_KEY, atom_leaf(Inert(self.clone()))),
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Debug for Protocol {
|
impl fmt::Debug for Protocol {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Protocol({})", self.0.id) }
|
||||||
f.debug_tuple(&self.0.display_name).field(&self.0.id.id()).finish()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl InertPayload for Protocol {
|
impl InertPayload for Protocol {
|
||||||
const TYPE_STR: &'static str = "Protocol";
|
const TYPE_STR: &'static str = "Protocol";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A type marker that can be attached to values to form a [Tagged]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Tag(pub Arc<TypeData>);
|
pub struct Tag(pub TypeData);
|
||||||
impl Tag {
|
impl Tag {
|
||||||
const ID_KEY: &'static str = "__type_id__";
|
const ID_KEY: &'static str = "__type_id__";
|
||||||
|
const fn profile() -> ImplsProfile<impl WrapImpl> {
|
||||||
pub fn new_id(
|
ImplsProfile {
|
||||||
id: RefEqual,
|
wrap: |t| Inert(Tag(t)),
|
||||||
display_name: Tok<String>,
|
own_id: Tag::ID_KEY,
|
||||||
impls: impl IntoIterator<Item = (RefEqual, int::Expr)>,
|
other_id: Protocol::ID_KEY,
|
||||||
) -> Self {
|
prelude: formatcp!(
|
||||||
let impls = impls.into_iter().collect();
|
"import std
|
||||||
Self(Arc::new(TypeData { id, display_name, impls }))
|
const {} := std::reflect::modname 1
|
||||||
|
const unwrap := std::protocol::unwrap {TYPE_KEY}
|
||||||
|
const wrap := std::protocol::wrap {TYPE_KEY}",
|
||||||
|
Tag::ID_KEY
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(
|
/// Create a new type-tag with a pre-determined name
|
||||||
display_name: &'static str,
|
pub fn new(id: Sym, impls: impl IntoIterator<Item = (Sym, nort::Expr)>) -> Self {
|
||||||
impls: impl IntoIterator<Item = (RefEqual, int::Expr)>,
|
Self(TypeData::new(id, impls))
|
||||||
) -> Self {
|
|
||||||
Self::new_id(RefEqual::new(), i(display_name), impls)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn id(&self) -> RefEqual { self.0.id.clone() }
|
/// Attach a pre-existing type-tag to the tree. Consider [Tag::tree]
|
||||||
|
pub fn to_tree<'a>(&self, rest: impl IntoIterator<Item = (&'a str, ConstTree)>) -> ConstTree {
|
||||||
|
to_mod(rest, self.0.clone(), Self::profile())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_tree_ent<'a>(
|
/// Create a new tag
|
||||||
&'a self,
|
pub fn tree<'a>(
|
||||||
|
impls: impl IntoIterator<Item = (Sym, nort::Expr)>,
|
||||||
rest: impl IntoIterator<Item = (&'a str, ConstTree)>,
|
rest: impl IntoIterator<Item = (&'a str, ConstTree)>,
|
||||||
) -> (&str, ConstTree) {
|
) -> ConstTree {
|
||||||
ConstTree::tree_ent(
|
mk_mod(rest, impls.into_iter().collect(), Self::profile())
|
||||||
self.0.display_name.as_str(),
|
|
||||||
rest.into_iter().chain([
|
|
||||||
(Self::ID_KEY, atom_leaf(Inert(self.id()))),
|
|
||||||
(TYPE_KEY, atom_leaf(Inert(self.clone()))),
|
|
||||||
]),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Debug for Tag {
|
impl fmt::Debug for Tag {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Tag({})", self.0.id) }
|
||||||
f.debug_tuple(&self.0.display_name).field(&self.0.id.id()).finish()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl InertPayload for Tag {
|
impl InertPayload for Tag {
|
||||||
const TYPE_STR: &'static str = "Tag";
|
const TYPE_STR: &'static str = "Tag";
|
||||||
fn strict_eq(&self, other: &Self) -> bool { self.0.id == other.0.id }
|
fn strict_eq(&self, other: &Self) -> bool { self.0.id == other.0.id }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A value with a type [Tag]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Tagged {
|
pub struct Tagged {
|
||||||
|
/// Type information
|
||||||
pub tag: Tag,
|
pub tag: Tag,
|
||||||
pub value: int::Expr,
|
/// Value
|
||||||
|
pub value: nort::Expr,
|
||||||
}
|
}
|
||||||
impl Debug for Tagged {
|
impl fmt::Debug for Tagged {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_tuple("Tagged").field(&self.tag).field(&self.value).finish()
|
write!(f, "Tagged({:?} {})", self.tag, self.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl InertPayload for Tagged {
|
impl InertPayload for Tagged {
|
||||||
const TYPE_STR: &'static str = "Tagged";
|
const TYPE_STR: &'static str = "Tagged";
|
||||||
fn respond(&self, mut request: Request) {
|
fn respond(&self, mut request: Request) { request.serve_with(|| self.tag.clone()) }
|
||||||
request.serve_with(|| self.tag.clone())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_impl(
|
fn parse_impl(
|
||||||
tail: Frag,
|
tail: Frag,
|
||||||
req: &dyn ParsePluginReq,
|
req: &dyn ParsePluginReq,
|
||||||
) -> Option<ProjectResult<(VName, parsed::Expr)>> {
|
) -> Option<ProjectResult<(VName, parsed::Expr)>> {
|
||||||
custom_line(tail, i("impl"), false, req).map(|res| {
|
custom_line(tail, i!(str: "impl"), false, req).map(|res| {
|
||||||
let (_, tail, _) = res?;
|
let (_, tail, _) = res?;
|
||||||
let (name, tail) = req.parse_nsname(tail)?;
|
let (name, tail) = req.parse_nsname(tail)?;
|
||||||
let (walrus, tail) = req.pop(tail.trim())?;
|
let (walrus, tail) = req.pop(tail.trim())?;
|
||||||
@@ -183,200 +234,125 @@ fn extract_impls(
|
|||||||
match parse_impl(line, req) {
|
match parse_impl(line, req) {
|
||||||
Some(result) => {
|
Some(result) => {
|
||||||
let (name, value) = result?;
|
let (name, value) = result?;
|
||||||
let target =
|
let target = Sym::new(name.suffix([typeid_name.clone()])).unwrap();
|
||||||
Sym::new(name.suffix([typeid_name.clone()])).unwrap();
|
|
||||||
impls.push(Impl { target, value });
|
impls.push(Impl { target, value });
|
||||||
},
|
},
|
||||||
None => lines.extend(
|
None => lines.extend((req.parse_line(line)?.into_iter()).map(|k| k.wrap(range.clone()))),
|
||||||
(req.parse_line(line)?.into_iter()).map(|k| k.wrap(range.clone())),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok((lines, impls))
|
Ok((lines, impls))
|
||||||
}
|
}
|
||||||
|
|
||||||
trait WrapImpl: Clone + Send + Sync + 'static {
|
trait WrapImpl: Clone + Copy + Send + Sync + 'static {
|
||||||
type R: ToClause;
|
type R: Atomic + Clone + 'static;
|
||||||
fn wrap(&self, data: Arc<TypeData>) -> Self::R;
|
fn wrap(&self, data: TypeData) -> Self::R;
|
||||||
}
|
}
|
||||||
impl<R: ToClause, F: Fn(Arc<TypeData>) -> R + Clone + Send + Sync + 'static>
|
impl<R: Atomic + Clone + 'static, F: Fn(TypeData) -> R + Clone + Copy + Send + Sync + 'static>
|
||||||
WrapImpl for F
|
WrapImpl for F
|
||||||
{
|
{
|
||||||
type R = R;
|
type R = R;
|
||||||
fn wrap(&self, data: Arc<TypeData>) -> Self::R { self(data) }
|
fn wrap(&self, data: TypeData) -> Self::R { self(data) }
|
||||||
|
}
|
||||||
|
fn use_wrap(wrap: impl WrapImpl, impls: HashMap<Sym, nort::Expr>) -> impl Atomic + Clone + 'static {
|
||||||
|
Unstable::new(move |r| wrap.wrap(TypeData::new(r.location.module, impls)))
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ImplsProfile<'a, W: WrapImpl> {
|
#[derive(Debug, Clone)]
|
||||||
|
struct ImplsProfile<W: WrapImpl> {
|
||||||
wrap: W,
|
wrap: W,
|
||||||
own_id: Tok<String>,
|
own_id: &'static str,
|
||||||
other_id: Tok<String>,
|
other_id: &'static str,
|
||||||
prelude: &'a str,
|
prelude: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_body_with_impls<W: WrapImpl>(
|
fn parse_body_with_impls(
|
||||||
display_name: Tok<String>,
|
|
||||||
body: Frag,
|
body: Frag,
|
||||||
req: &dyn ParsePluginReq,
|
req: &dyn ParsePluginReq,
|
||||||
range: SourceRange,
|
range: SourceRange,
|
||||||
profile: &ImplsProfile<'static, W>,
|
profile: ImplsProfile<impl WrapImpl>,
|
||||||
) -> ProjectResult<Vec<SourceLine>> {
|
) -> ProjectResult<Vec<SourceLine>> {
|
||||||
let id = RefEqual::new();
|
let ImplsProfile { other_id, prelude, wrap, .. } = profile.clone();
|
||||||
let (lines, impls) =
|
let (mut lines, impls) = extract_impls(body, req, range.clone(), i(other_id))?;
|
||||||
extract_impls(body, req, range.clone(), profile.other_id.clone())?;
|
|
||||||
|
|
||||||
Ok(
|
|
||||||
req
|
|
||||||
.parse_entries(profile.prelude, range.clone())
|
|
||||||
.into_iter()
|
|
||||||
.chain(
|
|
||||||
[
|
|
||||||
(profile.own_id.clone(), Inert(id.clone()).ast_cls()),
|
|
||||||
(
|
|
||||||
i(TYPE_KEY),
|
|
||||||
defer_to_runtime(
|
|
||||||
range.clone(),
|
|
||||||
impls.into_iter().flat_map({
|
|
||||||
let line_loc = range.clone();
|
let line_loc = range.clone();
|
||||||
move |Impl { target, value }| {
|
let type_data = defer_to_runtime(
|
||||||
[
|
range.clone(),
|
||||||
parsed::Clause::Name(target).into_expr(line_loc.clone()),
|
impls.into_iter().flat_map(move |Impl { target, value }| {
|
||||||
value,
|
[vec![parsed::Clause::Name(target).into_expr(line_loc.clone())], vec![value]]
|
||||||
]
|
|
||||||
.map(|e| ((), vec![e]))
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
{
|
move |pairs: Vec<nort::Expr>| -> RTResult<_> {
|
||||||
let display_name = display_name.clone();
|
|
||||||
let wrap = profile.wrap.clone();
|
|
||||||
move |pairs: Vec<((), int::Expr)>| -> ExternResult<_> {
|
|
||||||
let mut impls = HashMap::new();
|
|
||||||
debug_assert_eq!(pairs.len() % 2, 0, "key-value pairs");
|
debug_assert_eq!(pairs.len() % 2, 0, "key-value pairs");
|
||||||
let mut nvnvnv = pairs.into_iter().map(|t| t.1);
|
let mut impls = HashMap::with_capacity(pairs.len() / 2);
|
||||||
while let Some((name, value)) = nvnvnv.next_tuple() {
|
for (name, value) in pairs.into_iter().tuples() {
|
||||||
let key = name.downcast::<Inert<RefEqual>>()?;
|
impls.insert(name.downcast::<Inert<Sym>>()?.0, value);
|
||||||
impls.insert(key.0, value);
|
|
||||||
}
|
|
||||||
let id = id.clone();
|
|
||||||
let display_name = display_name.clone();
|
|
||||||
Ok(wrap.wrap(Arc::new(TypeData { id, display_name, impls })))
|
|
||||||
}
|
}
|
||||||
|
Ok(use_wrap(wrap, impls))
|
||||||
},
|
},
|
||||||
),
|
);
|
||||||
),
|
let type_data_line = Constant { name: i(TYPE_KEY), value: type_data.into_expr(range.clone()) };
|
||||||
]
|
lines.extend(req.parse_entries(prelude, range.clone()));
|
||||||
.map(|(name, value)| {
|
lines.push(MemberKind::Constant(type_data_line).into_line(true, range));
|
||||||
let value = parsed::Expr { value, range: range.clone() };
|
Ok(lines)
|
||||||
MemberKind::Constant(Constant { name, value })
|
|
||||||
.to_line(true, range.clone())
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.chain(lines)
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn protocol_impls_profile() -> ImplsProfile<'static, impl WrapImpl> {
|
|
||||||
ImplsProfile {
|
|
||||||
wrap: |t| Inert(Protocol(t)),
|
|
||||||
own_id: i(Protocol::ID_KEY),
|
|
||||||
other_id: i(Tag::ID_KEY),
|
|
||||||
prelude: formatcp!(
|
|
||||||
"import std::protocol
|
|
||||||
const resolve := protocol::resolve {TYPE_KEY}
|
|
||||||
const get_impl := protocol::get_impl {TYPE_KEY}"
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn type_impls_profile() -> ImplsProfile<'static, impl WrapImpl> {
|
|
||||||
ImplsProfile {
|
|
||||||
wrap: |t| Inert(Tag(t)),
|
|
||||||
own_id: i(Tag::ID_KEY),
|
|
||||||
other_id: i(Protocol::ID_KEY),
|
|
||||||
prelude: formatcp!(
|
|
||||||
"import std::protocol
|
|
||||||
const unwrap := protocol::unwrap {TYPE_KEY}
|
|
||||||
const wrap := protocol::wrap {TYPE_KEY}"
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct ProtocolParser;
|
struct ProtocolParser;
|
||||||
impl ParseLinePlugin for ProtocolParser {
|
impl ParseLinePlugin for ProtocolParser {
|
||||||
fn parse(
|
fn parse(&self, req: &dyn ParsePluginReq) -> Option<ProjectResult<Vec<SourceLineKind>>> {
|
||||||
&self,
|
custom_line(req.frag(), i!(str: "protocol"), true, req).map(|res| {
|
||||||
req: &dyn ParsePluginReq,
|
|
||||||
) -> Option<ProjectResult<Vec<SourceLineKind>>> {
|
|
||||||
custom_line(req.frag(), i("protocol"), true, req).map(|res| {
|
|
||||||
let (exported, tail, line_loc) = res?;
|
let (exported, tail, line_loc) = res?;
|
||||||
let (name, tail) = req.pop(tail)?;
|
let (name, tail) = req.pop(tail)?;
|
||||||
let name = req.expect_name(name)?;
|
let name = req.expect_name(name)?;
|
||||||
let tail = req.expect_block(tail, PType::Par)?;
|
let tail = req.expect_block(tail, PType::Par)?;
|
||||||
let profile = protocol_impls_profile();
|
let body = parse_body_with_impls(tail, req, line_loc, Protocol::profile())?;
|
||||||
let body =
|
|
||||||
parse_body_with_impls(name.clone(), tail, req, line_loc, &profile)?;
|
|
||||||
let kind = MemberKind::Module(ModuleBlock { name, body });
|
let kind = MemberKind::Module(ModuleBlock { name, body });
|
||||||
Ok(vec![SourceLineKind::Member(Member { exported, kind })])
|
Ok(vec![SourceLineKind::Member(Member { exported, kind })])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct TypeParser;
|
struct TypeParser;
|
||||||
impl ParseLinePlugin for TypeParser {
|
impl ParseLinePlugin for TypeParser {
|
||||||
fn parse(
|
fn parse(&self, req: &dyn ParsePluginReq) -> Option<ProjectResult<Vec<SourceLineKind>>> {
|
||||||
&self,
|
custom_line(req.frag(), i!(str: "type"), true, req).map(|res| {
|
||||||
req: &dyn ParsePluginReq,
|
|
||||||
) -> Option<ProjectResult<Vec<SourceLineKind>>> {
|
|
||||||
custom_line(req.frag(), i("type"), true, req).map(|res| {
|
|
||||||
let (exported, tail, line_loc) = res?;
|
let (exported, tail, line_loc) = res?;
|
||||||
let (name, tail) = req.pop(tail)?;
|
let (name, tail) = req.pop(tail)?;
|
||||||
let name = req.expect_name(name)?;
|
let name = req.expect_name(name)?;
|
||||||
let tail = req.expect_block(tail, PType::Par)?;
|
let tail = req.expect_block(tail, PType::Par)?;
|
||||||
let profile = type_impls_profile();
|
let body = parse_body_with_impls(tail, req, line_loc, Tag::profile())?;
|
||||||
let body =
|
|
||||||
parse_body_with_impls(name.clone(), tail, req, line_loc, &profile)?;
|
|
||||||
let kind = MemberKind::Module(ModuleBlock { name, body });
|
let kind = MemberKind::Module(ModuleBlock { name, body });
|
||||||
Ok(vec![SourceLineKind::Member(Member { exported, kind })])
|
Ok(vec![SourceLineKind::Member(Member { exported, kind })])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct AsProtocolParser;
|
struct AsProtocolParser;
|
||||||
impl ParseLinePlugin for AsProtocolParser {
|
impl ParseLinePlugin for AsProtocolParser {
|
||||||
fn parse(
|
fn parse(&self, req: &dyn ParsePluginReq) -> Option<ProjectResult<Vec<SourceLineKind>>> {
|
||||||
&self,
|
custom_line(req.frag(), i!(str: "as_protocol"), false, req).map(|res| {
|
||||||
req: &dyn ParsePluginReq,
|
|
||||||
) -> Option<ProjectResult<Vec<SourceLineKind>>> {
|
|
||||||
custom_line(req.frag(), i("as_protocol"), false, req).map(|res| {
|
|
||||||
let (_, tail, line_loc) = res?;
|
let (_, tail, line_loc) = res?;
|
||||||
let (name, tail) = req.pop(tail)?;
|
|
||||||
let name = req.expect_name(name)?;
|
|
||||||
let body = req.expect_block(tail, PType::Par)?;
|
let body = req.expect_block(tail, PType::Par)?;
|
||||||
let profile = protocol_impls_profile();
|
parse_body_with_impls(body, req, line_loc, Protocol::profile())
|
||||||
parse_body_with_impls(name, body, req, line_loc, &profile)
|
|
||||||
.map(|v| v.into_iter().map(|e| e.kind).collect())
|
.map(|v| v.into_iter().map(|e| e.kind).collect())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct AsTypeParser;
|
struct AsTypeParser;
|
||||||
impl ParseLinePlugin for AsTypeParser {
|
impl ParseLinePlugin for AsTypeParser {
|
||||||
fn parse(
|
fn parse(&self, req: &dyn ParsePluginReq) -> Option<ProjectResult<Vec<SourceLineKind>>> {
|
||||||
&self,
|
custom_line(req.frag(), i!(str: "as_type"), false, req).map(|res| {
|
||||||
req: &dyn ParsePluginReq,
|
|
||||||
) -> Option<ProjectResult<Vec<SourceLineKind>>> {
|
|
||||||
custom_line(req.frag(), i("as_type"), false, req).map(|res| {
|
|
||||||
let (_, tail, line_loc) = res?;
|
let (_, tail, line_loc) = res?;
|
||||||
let (name, tail) = req.pop(tail)?;
|
|
||||||
let name = req.expect_name(name)?;
|
|
||||||
let body = req.expect_block(tail, PType::Par)?;
|
let body = req.expect_block(tail, PType::Par)?;
|
||||||
let profile = type_impls_profile();
|
parse_body_with_impls(body, req, line_loc, Tag::profile())
|
||||||
parse_body_with_impls(name, body, req, line_loc, &profile)
|
|
||||||
.map(|v| v.into_iter().map(|e| e.kind).collect())
|
.map(|v| v.into_iter().map(|e| e.kind).collect())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Collection of all the parser plugins defined here
|
||||||
pub fn parsers() -> Vec<Box<dyn ParseLinePlugin>> {
|
pub fn parsers() -> Vec<Box<dyn ParseLinePlugin>> {
|
||||||
vec![
|
vec![
|
||||||
Box::new(ProtocolParser),
|
Box::new(ProtocolParser),
|
||||||
@@ -386,10 +362,8 @@ pub fn parsers() -> Vec<Box<dyn ParseLinePlugin>> {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap(
|
/// Check and remove the type tag from a value
|
||||||
tag: Inert<Tag>,
|
pub fn unwrap(tag: Inert<Tag>, tagged: Inert<Tagged>) -> RTResult<nort::Expr> {
|
||||||
tagged: Inert<Tagged>,
|
|
||||||
) -> ExternResult<int::Expr> {
|
|
||||||
if tagged.tag.strict_eq(&tag) {
|
if tagged.tag.strict_eq(&tag) {
|
||||||
return Ok(tagged.value.clone());
|
return Ok(tagged.value.clone());
|
||||||
}
|
}
|
||||||
@@ -397,49 +371,41 @@ pub fn unwrap(
|
|||||||
RuntimeError::fail(msg, "unwrapping type-tagged value")
|
RuntimeError::fail(msg, "unwrapping type-tagged value")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap(tag: Inert<Tag>, value: int::Expr) -> Inert<Tagged> {
|
/// Attach a type tag to a value
|
||||||
|
pub fn wrap(tag: Inert<Tag>, value: nort::Expr) -> Inert<Tagged> {
|
||||||
Inert(Tagged { tag: tag.0, value })
|
Inert(Tagged { tag: tag.0, value })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve(
|
/// Find the implementation of a protocol for a given value
|
||||||
protocol: Inert<Protocol>,
|
pub fn resolve(protocol: Inert<Protocol>, value: ClauseInst) -> RTResult<nort::Expr> {
|
||||||
value: ClauseInst,
|
|
||||||
) -> ExternResult<int::Expr> {
|
|
||||||
let tag = value.request::<Tag>().ok_or_else(|| {
|
let tag = value.request::<Tag>().ok_or_else(|| {
|
||||||
let msg = format!("{value} is not type-tagged");
|
let msg = format!("{value} is not type-tagged");
|
||||||
RuntimeError::ext(msg, "resolving protocol impl")
|
RuntimeError::ext(msg, "resolving protocol impl")
|
||||||
})?;
|
})?;
|
||||||
get_impl(protocol, Inert(tag))
|
if let Some(implem) = protocol.0.0.impls.get(&tag.0.id) {
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_impl(
|
|
||||||
Inert(proto): Inert<Protocol>,
|
|
||||||
Inert(tag): Inert<Tag>,
|
|
||||||
) -> ExternResult<int::Expr> {
|
|
||||||
if let Some(implem) = proto.0.impls.get(&tag.0.id) {
|
|
||||||
Ok(implem.clone())
|
Ok(implem.clone())
|
||||||
} else if let Some(implem) = tag.0.impls.get(&proto.0.id) {
|
} else if let Some(implem) = tag.0.impls.get(&protocol.0.0.id) {
|
||||||
Ok(implem.clone())
|
Ok(implem.clone())
|
||||||
} else {
|
} else {
|
||||||
let message = format!("{tag:?} doesn't implement {proto:?}");
|
let message = format!("{tag:?} doesn't implement {protocol:?}");
|
||||||
RuntimeError::fail(message, "dispatching protocol")
|
RuntimeError::fail(message, "dispatching protocol")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate a call to [resolve] bound to the given protocol
|
||||||
pub const fn gen_resolv(name: &'static str) -> impl GenClause {
|
pub const fn gen_resolv(name: &'static str) -> impl GenClause {
|
||||||
tpl::A(
|
tpl::A(
|
||||||
tpl::C("std::protocol::resolve"),
|
tpl::C("std::protocol::resolve"),
|
||||||
tpl::V(Unstable::new(move |_| {
|
tpl::V(Unstable::new(move |_| refer_seq(name.split("::").chain(iter::once(TYPE_KEY))))),
|
||||||
refer_seq(name.split("::").chain(iter::once(TYPE_KEY)))
|
|
||||||
})),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// All the functions exposed by the std::protocol library
|
||||||
pub fn protocol_lib() -> ConstTree {
|
pub fn protocol_lib() -> ConstTree {
|
||||||
ConstTree::ns("std::protocol", [ConstTree::tree([
|
ConstTree::ns("std::protocol", [ConstTree::tree([
|
||||||
xfn_ent("unwrap", [unwrap]),
|
xfn_ent("unwrap", [unwrap]),
|
||||||
xfn_ent("wrap", [wrap]),
|
xfn_ent("wrap", [wrap]),
|
||||||
xfn_ent("get_impl", [get_impl]),
|
|
||||||
xfn_ent("resolve", [resolve]),
|
xfn_ent("resolve", [resolve]),
|
||||||
|
xfn_ent("break", [|t: Inert<Tagged>| t.0.value]),
|
||||||
])])
|
])])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
//! `std::reflect` Abstraction-breaking operations for dynamically constructing
|
//! `std::reflect` Abstraction-breaking operations for dynamically constructing
|
||||||
//! [Clause::Constant] references.
|
//! [Clause::Constant] references.
|
||||||
|
|
||||||
use std::cmp;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::sync::atomic::{self, AtomicUsize};
|
use std::sync::atomic::{self, AtomicUsize};
|
||||||
|
use std::{cmp, fmt};
|
||||||
|
|
||||||
use intern_all::i;
|
use intern_all::i;
|
||||||
|
|
||||||
|
use super::runtime_error::RuntimeError;
|
||||||
|
use super::string::OrcString;
|
||||||
use crate::foreign::inert::{Inert, InertPayload};
|
use crate::foreign::inert::{Inert, InertPayload};
|
||||||
|
use crate::foreign::try_from_expr::WithLoc;
|
||||||
use crate::gen::tree::{xfn_ent, ConstTree};
|
use crate::gen::tree::{xfn_ent, ConstTree};
|
||||||
use crate::interpreter::nort::Clause;
|
use crate::interpreter::nort::{self, Clause};
|
||||||
use crate::name::Sym;
|
use crate::name::Sym;
|
||||||
|
|
||||||
impl InertPayload for Sym {
|
impl InertPayload for Sym {
|
||||||
const TYPE_STR: &'static str = "SymbolName";
|
const TYPE_STR: &'static str = "SymbolName";
|
||||||
|
fn strict_eq(&self, o: &Self) -> bool { self == o }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a constant reference at runtime. Referencing a nonexistent constant
|
/// Generate a constant reference at runtime. Referencing a nonexistent constant
|
||||||
@@ -39,8 +42,8 @@ impl RefEqual {
|
|||||||
/// Return the unique identifier of this [RefEqual] and its copies
|
/// Return the unique identifier of this [RefEqual] and its copies
|
||||||
pub fn id(&self) -> usize { self.0 }
|
pub fn id(&self) -> usize { self.0 }
|
||||||
}
|
}
|
||||||
impl Debug for RefEqual {
|
impl fmt::Debug for RefEqual {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_tuple("RefEqual").field(&self.id()).finish()
|
f.debug_tuple("RefEqual").field(&self.id()).finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,5 +68,11 @@ impl Hash for RefEqual {
|
|||||||
pub(super) fn reflect_lib() -> ConstTree {
|
pub(super) fn reflect_lib() -> ConstTree {
|
||||||
ConstTree::ns("std::reflect", [ConstTree::tree([
|
ConstTree::ns("std::reflect", [ConstTree::tree([
|
||||||
xfn_ent("ref_equal", [|l: Inert<RefEqual>, r: Inert<RefEqual>| Inert(l.0.id() == r.0.id())]),
|
xfn_ent("ref_equal", [|l: Inert<RefEqual>, r: Inert<RefEqual>| Inert(l.0.id() == r.0.id())]),
|
||||||
|
xfn_ent("modname", [|WithLoc(loc, _): WithLoc<nort::Expr>| Inert(loc.module)]),
|
||||||
|
xfn_ent("symbol", [|s: Inert<OrcString>| {
|
||||||
|
Sym::parse(s.0.as_str())
|
||||||
|
.map(Inert)
|
||||||
|
.map_err(|_| RuntimeError::ext("empty string".to_string(), "converting string to Symbol"))
|
||||||
|
}]),
|
||||||
])])
|
])])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import std::panic
|
import std::panic
|
||||||
|
|
||||||
as_type result ()
|
as_type ()
|
||||||
|
|
||||||
export const ok := \v. wrap \fe. \fv. fv v
|
export const ok := \v. wrap \fe. \fv. fv v
|
||||||
export const err := \e. wrap \fe. \fv. fe e
|
export const err := \e. wrap \fe. \fv. fe e
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
//! Errors thrown by the standard library in lieu of in-language error handling
|
//! Errors thrown by the standard library in lieu of in-language error handling
|
||||||
//! for runtime errors such as missing files.
|
//! for runtime errors such as missing files.
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::foreign::error::{ExternError, ExternErrorObj, ExternResult};
|
use crate::foreign::error::{RTError, RTErrorObj, RTResult};
|
||||||
|
|
||||||
/// Some external event prevented the operation from succeeding
|
/// Some external event prevented the operation from succeeding
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -15,20 +15,20 @@ pub struct RuntimeError {
|
|||||||
impl RuntimeError {
|
impl RuntimeError {
|
||||||
/// Construct, upcast and wrap in a Result that never succeeds for easy
|
/// Construct, upcast and wrap in a Result that never succeeds for easy
|
||||||
/// short-circuiting
|
/// short-circuiting
|
||||||
pub fn fail<T>(message: String, operation: &'static str) -> ExternResult<T> {
|
pub fn fail<T>(message: String, operation: &'static str) -> RTResult<T> {
|
||||||
Err(Self { message, operation }.rc())
|
Err(Self { message, operation }.pack())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct and upcast to [ExternError]
|
/// Construct and upcast to [RTErrorObj]
|
||||||
pub fn ext(message: String, operation: &'static str) -> ExternErrorObj {
|
pub fn ext(message: String, operation: &'static str) -> RTErrorObj {
|
||||||
Self { message, operation }.rc()
|
Self { message, operation }.pack()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for RuntimeError {
|
impl fmt::Display for RuntimeError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "Error while {}: {}", self.operation, self.message)
|
write!(f, "Error while {}: {}", self.operation, self.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExternError for RuntimeError {}
|
impl RTError for RuntimeError {}
|
||||||
|
|||||||
@@ -37,9 +37,7 @@ fn new_state(default: Thunk, cont: Thunk) -> Inert<NewStateCmd> {
|
|||||||
Inert(NewStateCmd(default.0, cont.0))
|
Inert(NewStateCmd(default.0, cont.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_state(s: Inert<State>, cont: Thunk) -> Inert<GetStateCmd> {
|
fn get_state(s: Inert<State>, cont: Thunk) -> Inert<GetStateCmd> { Inert(GetStateCmd(s.0, cont.0)) }
|
||||||
Inert(GetStateCmd(s.0, cont.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_state(s: Inert<State>, value: Thunk, cont: Thunk) -> Inert<SetStateCmd> {
|
fn set_state(s: Inert<State>, value: Thunk, cont: Thunk) -> Inert<SetStateCmd> {
|
||||||
Inert(SetStateCmd(s.0, value.0, cont.0))
|
Inert(SetStateCmd(s.0, value.0, cont.0))
|
||||||
|
|||||||
@@ -14,17 +14,16 @@ use super::panic::panic_lib;
|
|||||||
use super::protocol::{parsers, protocol_lib};
|
use super::protocol::{parsers, protocol_lib};
|
||||||
use super::reflect::reflect_lib;
|
use super::reflect::reflect_lib;
|
||||||
use super::state::{state_handlers, state_lib};
|
use super::state::{state_handlers, state_lib};
|
||||||
use super::string::str_lib;
|
use super::string::{str_lib, StringLexer};
|
||||||
use super::tstring::TStringLexer;
|
|
||||||
use super::tuple::tuple_lib;
|
use super::tuple::tuple_lib;
|
||||||
use crate::facade::system::{IntoSystem, System};
|
use crate::facade::system::{IntoSystem, System};
|
||||||
use crate::gen::tree::{ConstCombineErr, ConstTree};
|
use crate::gen::tree::{ConstCombineErr, ConstTree};
|
||||||
use crate::location::CodeGenInfo;
|
use crate::location::CodeGenInfo;
|
||||||
use crate::name::VName;
|
use crate::pipeline::load_project::Prelude;
|
||||||
use crate::pipeline::load_solution::Prelude;
|
|
||||||
use crate::tree::ModEntry;
|
use crate::tree::ModEntry;
|
||||||
use crate::utils::combine::Combine;
|
use crate::utils::combine::Combine;
|
||||||
use crate::virt_fs::{EmbeddedFS, VirtFS};
|
use crate::virt_fs::{EmbeddedFS, VirtFS};
|
||||||
|
use crate::{sym, vname};
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
#[derive(RustEmbed)]
|
||||||
#[folder = "src/libs/std"]
|
#[folder = "src/libs/std"]
|
||||||
@@ -51,13 +50,6 @@ impl StdConfig {
|
|||||||
.combine(reflect_lib())?
|
.combine(reflect_lib())?
|
||||||
.combine(state_lib())?
|
.combine(state_lib())?
|
||||||
.combine(str_lib())?;
|
.combine(str_lib())?;
|
||||||
// panic!(
|
|
||||||
// "{:?}",
|
|
||||||
// pure_tree
|
|
||||||
// .unwrap_mod_ref()
|
|
||||||
// .walk1_ref(&[], &[i("std"), i("protocol")], |_| true)
|
|
||||||
// .map(|p| p.0)
|
|
||||||
// );
|
|
||||||
if !self.impure {
|
if !self.impure {
|
||||||
return Ok(pure_tree);
|
return Ok(pure_tree);
|
||||||
}
|
}
|
||||||
@@ -71,15 +63,15 @@ impl IntoSystem<'static> for StdConfig {
|
|||||||
name: "stdlib",
|
name: "stdlib",
|
||||||
constants: self.stdlib().expect("stdlib tree is malformed"),
|
constants: self.stdlib().expect("stdlib tree is malformed"),
|
||||||
code: ModEntry::ns("std", [ModEntry::leaf(
|
code: ModEntry::ns("std", [ModEntry::leaf(
|
||||||
EmbeddedFS::new::<StdEmbed>(".orc", CodeGenInfo::no_details("std::fs")).rc(),
|
EmbeddedFS::new::<StdEmbed>(".orc", CodeGenInfo::no_details(sym!(std::fs))).rc(),
|
||||||
)]),
|
)]),
|
||||||
prelude: vec![Prelude {
|
prelude: vec![Prelude {
|
||||||
target: VName::literal("std::prelude"),
|
target: vname!(std::prelude),
|
||||||
exclude: VName::literal("std"),
|
exclude: vname!(std),
|
||||||
owner: CodeGenInfo::no_details("std::prelude"),
|
owner: CodeGenInfo::no_details(sym!(std::prelude)),
|
||||||
}],
|
}],
|
||||||
handlers: state_handlers(),
|
handlers: state_handlers(),
|
||||||
lexer_plugins: vec![Box::new(TStringLexer)],
|
lexer_plugins: vec![Box::new(StringLexer)],
|
||||||
line_parsers: parsers(),
|
line_parsers: parsers(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,34 @@
|
|||||||
//! `std::string` String processing
|
//! `std::string` String processing
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt;
|
||||||
|
use std::fmt::Write as _;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use intern_all::{i, Tok};
|
use intern_all::{i, Tok};
|
||||||
|
use itertools::Itertools;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
|
use super::protocol::{gen_resolv, Protocol};
|
||||||
use super::runtime_error::RuntimeError;
|
use super::runtime_error::RuntimeError;
|
||||||
use crate::foreign::atom::Atomic;
|
use crate::error::{ProjectErrorObj, ProjectResult};
|
||||||
use crate::foreign::error::ExternResult;
|
use crate::foreign::atom::{AtomGenerator, Atomic};
|
||||||
|
use crate::foreign::error::RTResult;
|
||||||
use crate::foreign::inert::{Inert, InertPayload};
|
use crate::foreign::inert::{Inert, InertPayload};
|
||||||
use crate::foreign::to_clause::ToClause;
|
use crate::foreign::to_clause::ToClause;
|
||||||
use crate::foreign::try_from_expr::TryFromExpr;
|
use crate::foreign::try_from_expr::{TryFromExpr, WithLoc};
|
||||||
|
use crate::gen::tpl;
|
||||||
|
use crate::gen::traits::Gen;
|
||||||
use crate::gen::tree::{xfn_ent, ConstTree};
|
use crate::gen::tree::{xfn_ent, ConstTree};
|
||||||
|
use crate::interpreter::gen_nort::nort_gen;
|
||||||
use crate::interpreter::nort::{Clause, Expr};
|
use crate::interpreter::nort::{Clause, Expr};
|
||||||
use crate::location::CodeLocation;
|
use crate::location::CodeLocation;
|
||||||
|
use crate::parse::context::ParseCtx;
|
||||||
|
use crate::parse::errors::ParseErrorKind;
|
||||||
|
use crate::parse::lex_plugin::{LexPluginRecur, LexPluginReq, LexerPlugin};
|
||||||
|
use crate::parse::lexer::{Entry, LexRes, Lexeme};
|
||||||
|
use crate::parse::parsed::PType;
|
||||||
use crate::utils::iter_find::iter_find;
|
use crate::utils::iter_find::iter_find;
|
||||||
|
|
||||||
/// An Orchid string which may or may not be interned
|
/// An Orchid string which may or may not be interned
|
||||||
@@ -28,8 +40,8 @@ pub enum OrcString {
|
|||||||
Runtime(Arc<String>),
|
Runtime(Arc<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for OrcString {
|
impl fmt::Debug for OrcString {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Runtime(s) => write!(f, "r\"{s}\""),
|
Self::Runtime(s) => write!(f, "r\"{s}\""),
|
||||||
Self::Interned(t) => write!(f, "i\"{t}\""),
|
Self::Interned(t) => write!(f, "i\"{t}\""),
|
||||||
@@ -49,7 +61,7 @@ impl OrcString {
|
|||||||
pub fn get_string(self) -> String {
|
pub fn get_string(self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Self::Interned(s) => s.as_str().to_owned(),
|
Self::Interned(s) => s.as_str().to_owned(),
|
||||||
Self::Runtime(rc) => Arc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone()),
|
Self::Runtime(rc) => Arc::unwrap_or_clone(rc),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,6 +85,10 @@ impl From<String> for OrcString {
|
|||||||
fn from(value: String) -> Self { Self::Runtime(Arc::new(value)) }
|
fn from(value: String) -> Self { Self::Runtime(Arc::new(value)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&str> for OrcString {
|
||||||
|
fn from(value: &str) -> Self { Self::from(value.to_string()) }
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Tok<String>> for OrcString {
|
impl From<Tok<String>> for OrcString {
|
||||||
fn from(value: Tok<String>) -> Self { Self::Interned(value) }
|
fn from(value: Tok<String>) -> Self { Self::Interned(value) }
|
||||||
}
|
}
|
||||||
@@ -96,13 +112,15 @@ impl ToClause for String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TryFromExpr for String {
|
impl TryFromExpr for String {
|
||||||
fn from_expr(exi: Expr) -> ExternResult<Self> {
|
fn from_expr(exi: Expr) -> RTResult<Self> {
|
||||||
Ok(exi.downcast::<Inert<OrcString>>()?.0.get_string())
|
Ok(exi.downcast::<Inert<OrcString>>()?.0.get_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn str_lib() -> ConstTree {
|
pub(super) fn str_lib() -> ConstTree {
|
||||||
ConstTree::ns("std::string", [ConstTree::tree([
|
ConstTree::ns("std::string", [ConstTree::tree([
|
||||||
|
// String conversion protocol implementable by external types
|
||||||
|
("conversion", Protocol::tree([], [])),
|
||||||
xfn_ent("slice", [|s: Inert<OrcString>, i: Inert<usize>, len: Inert<usize>| {
|
xfn_ent("slice", [|s: Inert<OrcString>, i: Inert<usize>, len: Inert<usize>| {
|
||||||
let graphs = s.0.as_str().graphemes(true);
|
let graphs = s.0.as_str().graphemes(true);
|
||||||
if i.0 == 0 {
|
if i.0 == 0 {
|
||||||
@@ -145,5 +163,239 @@ pub(super) fn str_lib() -> ConstTree {
|
|||||||
x => x,
|
x => x,
|
||||||
})
|
})
|
||||||
}]),
|
}]),
|
||||||
|
xfn_ent("convert", [|WithLoc(loc, a): WithLoc<Expr>| match a.clone().downcast() {
|
||||||
|
Ok(str) => Inert::<OrcString>::atom_expr(str, loc),
|
||||||
|
Err(_) => match a.clause.request::<OrcString>() {
|
||||||
|
Some(str) => Inert(str).atom_expr(loc),
|
||||||
|
None => tpl::a2(gen_resolv("std::string::conversion"), tpl::Slot, tpl::Slot)
|
||||||
|
.template(nort_gen(loc), [a.clone(), a]),
|
||||||
|
},
|
||||||
|
}]),
|
||||||
])])
|
])])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reasons why [parse_string] might fail. See [StringError]
|
||||||
|
enum StringErrorKind {
|
||||||
|
/// A unicode escape sequence wasn't followed by 4 hex digits
|
||||||
|
NotHex,
|
||||||
|
/// A unicode escape sequence contained an unassigned code point
|
||||||
|
BadCodePoint,
|
||||||
|
/// An unrecognized escape sequence was found
|
||||||
|
BadEscSeq,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error produced by [parse_string]
|
||||||
|
struct StringError {
|
||||||
|
/// Character where the error occured
|
||||||
|
pos: usize,
|
||||||
|
/// Reason for the error
|
||||||
|
kind: StringErrorKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StringError {
|
||||||
|
/// Convert into project error for reporting
|
||||||
|
pub fn into_proj(self, ctx: &dyn ParseCtx, pos: usize) -> ProjectErrorObj {
|
||||||
|
let start = pos + self.pos;
|
||||||
|
let location = ctx.range_loc(&(start..start + 1));
|
||||||
|
match self.kind {
|
||||||
|
StringErrorKind::NotHex => NotHex.pack(location),
|
||||||
|
StringErrorKind::BadCodePoint => BadCodePoint.pack(location),
|
||||||
|
StringErrorKind::BadEscSeq => BadEscapeSequence.pack(location),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process escape sequences in a string literal
|
||||||
|
fn parse_string(str: &str) -> Result<String, StringError> {
|
||||||
|
let mut target = String::new();
|
||||||
|
let mut iter = str.char_indices();
|
||||||
|
while let Some((_, c)) = iter.next() {
|
||||||
|
if c != '\\' {
|
||||||
|
target.push(c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let (mut pos, code) = iter.next().expect("lexer would have continued");
|
||||||
|
let next = match code {
|
||||||
|
c @ ('\\' | '/' | '"') => c,
|
||||||
|
'b' => '\x08',
|
||||||
|
'f' => '\x0f',
|
||||||
|
'n' => '\n',
|
||||||
|
'r' => '\r',
|
||||||
|
't' => '\t',
|
||||||
|
'\n' => 'skipws: loop {
|
||||||
|
match iter.next() {
|
||||||
|
None => return Ok(target),
|
||||||
|
Some((_, c)) =>
|
||||||
|
if !c.is_whitespace() {
|
||||||
|
break 'skipws c;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'u' => {
|
||||||
|
let acc = ((0..4).rev())
|
||||||
|
.map(|radical| {
|
||||||
|
let (j, c) = (iter.next()).ok_or(StringError { pos, kind: StringErrorKind::NotHex })?;
|
||||||
|
pos = j;
|
||||||
|
let b = u32::from_str_radix(&String::from(c), 16)
|
||||||
|
.map_err(|_| StringError { pos, kind: StringErrorKind::NotHex })?;
|
||||||
|
Ok(16u32.pow(radical) + b)
|
||||||
|
})
|
||||||
|
.fold_ok(0, u32::wrapping_add)?;
|
||||||
|
char::from_u32(acc).ok_or(StringError { pos, kind: StringErrorKind::BadCodePoint })?
|
||||||
|
},
|
||||||
|
_ => return Err(StringError { pos, kind: StringErrorKind::BadEscSeq }),
|
||||||
|
};
|
||||||
|
target.push(next);
|
||||||
|
}
|
||||||
|
Ok(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [LexerPlugin] for a string literal that supports interpolateion.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct StringLexer;
|
||||||
|
impl LexerPlugin for StringLexer {
|
||||||
|
fn lex<'a>(&self, req: &'_ dyn LexPluginReq<'a>) -> Option<ProjectResult<LexRes<'a>>> {
|
||||||
|
req.tail().strip_prefix('\"').map(|mut txt| {
|
||||||
|
let ctx = req.ctx();
|
||||||
|
let mut parts = vec![Entry::new(ctx.range(0, txt), Lexeme::LP(PType::Par))];
|
||||||
|
let mut str = String::new();
|
||||||
|
let commit_str = |str: &mut String, tail: &str, parts: &mut Vec<Entry>| -> ProjectResult<_> {
|
||||||
|
let str_val = parse_string(str).unwrap_or_else(|e| {
|
||||||
|
ctx.reporter().report(e.into_proj(ctx, ctx.pos(txt)));
|
||||||
|
String::new()
|
||||||
|
});
|
||||||
|
let ag = AtomGenerator::cloner(Inert(OrcString::from(i(&str_val))));
|
||||||
|
parts.push(Entry::new(ctx.range(str.len(), tail), Lexeme::Atom(ag)));
|
||||||
|
*str = String::new();
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
if let Some(rest) = txt.strip_prefix('"') {
|
||||||
|
commit_str(&mut str, txt, &mut parts)?;
|
||||||
|
parts.push(Entry::new(ctx.range(0, rest), Lexeme::RP(PType::Par)));
|
||||||
|
if parts.len() == 3 {
|
||||||
|
return Ok(LexRes { tail: rest, tokens: vec![parts[1].clone()] });
|
||||||
|
}
|
||||||
|
return Ok(LexRes { tail: rest, tokens: parts });
|
||||||
|
}
|
||||||
|
if let Some(rest) = txt.strip_prefix("${") {
|
||||||
|
let mut depth = 0;
|
||||||
|
commit_str(&mut str, rest, &mut parts)?;
|
||||||
|
parts.extend(req.insert("++ std::string::convert (", ctx.source_range(0, rest)));
|
||||||
|
let res = req.recurse(LexPluginRecur {
|
||||||
|
tail: rest,
|
||||||
|
exit: &mut |c| {
|
||||||
|
match c.chars().next() {
|
||||||
|
None => return Err(UnclosedInterpolation.pack(ctx.source_range(2, rest))),
|
||||||
|
Some('{') => depth += 1,
|
||||||
|
Some('}') if depth == 0 => return Ok(true),
|
||||||
|
Some('}') => depth -= 1,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
},
|
||||||
|
})?;
|
||||||
|
txt = &res.tail[1..]; // account for final }
|
||||||
|
parts.extend(res.tokens);
|
||||||
|
parts.extend(req.insert(") ++", ctx.source_range(0, txt)));
|
||||||
|
} else {
|
||||||
|
let mut chars = txt.chars();
|
||||||
|
match chars.next() {
|
||||||
|
None => return Err(NoStringEnd.pack(ctx.source_range(req.tail().len(), ""))),
|
||||||
|
Some('\\') => match chars.next() {
|
||||||
|
None => write!(str, "\\").expect("writing \\ into string"),
|
||||||
|
Some(next) => write!(str, "\\{next}").expect("writing \\ and char into string"),
|
||||||
|
},
|
||||||
|
Some(c) => write!(str, "{c}").expect("writing char into string"),
|
||||||
|
}
|
||||||
|
txt = chars.as_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An interpolated string section started with ${ wasn't closed with a balanced
|
||||||
|
/// }
|
||||||
|
pub struct UnclosedInterpolation;
|
||||||
|
impl ParseErrorKind for UnclosedInterpolation {
|
||||||
|
const DESCRIPTION: &'static str = "A ${ block within a $-string wasn't closed";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// String literal never ends
|
||||||
|
pub(super) struct NoStringEnd;
|
||||||
|
impl ParseErrorKind for NoStringEnd {
|
||||||
|
const DESCRIPTION: &'static str = "A string literal was not closed with `\"`";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A unicode escape sequence contains something other than a hex digit
|
||||||
|
pub(super) struct NotHex;
|
||||||
|
impl ParseErrorKind for NotHex {
|
||||||
|
const DESCRIPTION: &'static str = "Expected a hex digit";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A unicode escape sequence contains a number that isn't a unicode code point.
|
||||||
|
pub(super) struct BadCodePoint;
|
||||||
|
impl ParseErrorKind for BadCodePoint {
|
||||||
|
const DESCRIPTION: &'static str = "\\uXXXX escape sequence does not describe valid code point";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An unrecognized escape sequence occurred in a string.
|
||||||
|
pub(super) struct BadEscapeSequence;
|
||||||
|
impl ParseErrorKind for BadEscapeSequence {
|
||||||
|
const DESCRIPTION: &'static str = "Unrecognized escape sequence";
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use intern_all::i;
|
||||||
|
|
||||||
|
use super::StringLexer;
|
||||||
|
use crate::foreign::atom::Atomic;
|
||||||
|
use crate::foreign::inert::Inert;
|
||||||
|
use crate::libs::std::string::OrcString;
|
||||||
|
use crate::parse::context::MockContext;
|
||||||
|
use crate::parse::lex_plugin::{LexPlugReqImpl, LexerPlugin};
|
||||||
|
use crate::parse::lexer::Lexeme;
|
||||||
|
use crate::parse::parsed::PType;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn plain_string() {
|
||||||
|
let source = r#""Hello world!" - says the programmer"#;
|
||||||
|
let ctx = MockContext::new();
|
||||||
|
let req = LexPlugReqImpl { ctx: &ctx, tail: source };
|
||||||
|
let res = (StringLexer.lex(&req))
|
||||||
|
.expect("the snippet starts with a quote")
|
||||||
|
.expect("it contains a valid string");
|
||||||
|
let expected = [Inert(OrcString::from("Hello world!")).lexeme()];
|
||||||
|
assert_eq!(res.tokens, expected);
|
||||||
|
assert_eq!(res.tail, " - says the programmer");
|
||||||
|
assert!(!ctx.0.failing(), "No errors were generated")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[rustfmt::skip]
|
||||||
|
fn template_string() {
|
||||||
|
let source = r#""I <${1 + 2} parsers" - this dev"#;
|
||||||
|
let ctx = MockContext::new();
|
||||||
|
let req = LexPlugReqImpl { ctx: &ctx, tail: source };
|
||||||
|
let res = (StringLexer.lex(&req))
|
||||||
|
.expect("the snippet starts with a quote")
|
||||||
|
.expect("it contains a valid string");
|
||||||
|
use Lexeme::{Name, LP, NS, RP};
|
||||||
|
let expected = [
|
||||||
|
LP(PType::Par),
|
||||||
|
Inert(OrcString::from("I <")).lexeme(),
|
||||||
|
Name(i!(str: "++")),
|
||||||
|
// std::string::convert
|
||||||
|
Name(i!(str: "std")), NS, Name(i!(str: "string")), NS, Name(i!(str: "convert")),
|
||||||
|
// (1 + 1)
|
||||||
|
LP(PType::Par), Inert(1).lexeme(), Name(i!(str: "+")), Inert(2).lexeme(), RP(PType::Par),
|
||||||
|
Name(i!(str: "++")),
|
||||||
|
Inert(OrcString::from(" parsers")).lexeme(),
|
||||||
|
RP(PType::Par),
|
||||||
|
];
|
||||||
|
assert_eq!(res.tokens, expected);
|
||||||
|
assert_eq!(res.tail, " - this dev");
|
||||||
|
assert!(!ctx.0.failing(), "No errors were generated");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
use std::fmt::Write;
|
|
||||||
|
|
||||||
use intern_all::i;
|
|
||||||
|
|
||||||
use crate::error::ProjectResult;
|
|
||||||
use crate::foreign::atom::AtomGenerator;
|
|
||||||
use crate::foreign::inert::Inert;
|
|
||||||
use crate::libs::std::string::OrcString;
|
|
||||||
use crate::parse::errors::ParseErrorKind;
|
|
||||||
use crate::parse::lex_plugin::{LexPluginRecur, LexPluginReq, LexerPlugin};
|
|
||||||
use crate::parse::lexer::{Entry, LexRes, Lexeme};
|
|
||||||
use crate::parse::parsed::PType;
|
|
||||||
use crate::parse::string::parse_string;
|
|
||||||
|
|
||||||
pub struct TStringLexer;
|
|
||||||
impl LexerPlugin for TStringLexer {
|
|
||||||
fn lex<'a>(&self, req: &'_ dyn LexPluginReq<'a>) -> Option<ProjectResult<LexRes<'a>>> {
|
|
||||||
req.tail().strip_prefix("$\"").map(|mut txt| {
|
|
||||||
let ctx = req.ctx();
|
|
||||||
let mut parts = vec![Entry::new(ctx.range(0, txt), Lexeme::LP(PType::Par))];
|
|
||||||
let mut str = String::new();
|
|
||||||
let commit_str = |str: &mut String, tail: &str, parts: &mut Vec<Entry>| -> ProjectResult<_> {
|
|
||||||
let str_val = parse_string(str).map_err(|e| e.to_proj(ctx, ctx.pos(txt)))?;
|
|
||||||
let ag = AtomGenerator::cloner(Inert(OrcString::from(i(&str_val))));
|
|
||||||
parts.push(Entry::new(ctx.range(str.len(), tail), Lexeme::Atom(ag)));
|
|
||||||
*str = String::new();
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
loop {
|
|
||||||
if let Some(rest) = txt.strip_prefix('"') {
|
|
||||||
commit_str(&mut str, txt, &mut parts)?;
|
|
||||||
parts.push(Entry::new(ctx.range(0, rest), Lexeme::RP(PType::Par)));
|
|
||||||
return Ok(LexRes { tail: rest, tokens: parts });
|
|
||||||
}
|
|
||||||
if let Some(rest) = txt.strip_prefix("${") {
|
|
||||||
let mut depth = 0;
|
|
||||||
commit_str(&mut str, rest, &mut parts)?;
|
|
||||||
parts.extend(req.insert("++ std::conv::to_string (", ctx.source_range(0, rest)));
|
|
||||||
let res = req.recurse(LexPluginRecur {
|
|
||||||
tail: rest,
|
|
||||||
exit: &mut |c| {
|
|
||||||
match c.chars().next() {
|
|
||||||
None => return Err(UnclosedInterpolation.pack(ctx.source_range(2, rest))),
|
|
||||||
Some('{') => depth += 1,
|
|
||||||
Some('}') if depth == 0 => return Ok(true),
|
|
||||||
Some('}') => depth -= 1,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
Ok(false)
|
|
||||||
},
|
|
||||||
})?;
|
|
||||||
txt = &res.tail[1..]; // account for final }
|
|
||||||
parts.extend(res.tokens);
|
|
||||||
parts.extend(req.insert(") ++", ctx.source_range(0, txt)));
|
|
||||||
} else {
|
|
||||||
let mut chars = txt.chars();
|
|
||||||
match chars.next() {
|
|
||||||
None => return Err(NoTStringEnd.pack(ctx.source_range(req.tail().len(), ""))),
|
|
||||||
Some('\\') => match chars.next() {
|
|
||||||
None => write!(str, "\\").expect("writing \\ into string"),
|
|
||||||
Some(next) => write!(str, "\\{next}").expect("writing \\ and char into string"),
|
|
||||||
},
|
|
||||||
Some(c) => write!(str, "{c}").expect("writing char into string"),
|
|
||||||
}
|
|
||||||
txt = chars.as_str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UnclosedInterpolation;
|
|
||||||
impl ParseErrorKind for UnclosedInterpolation {
|
|
||||||
const DESCRIPTION: &'static str = "A ${ block within a $-string wasn't closed";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// String literal never ends
|
|
||||||
pub(super) struct NoTStringEnd;
|
|
||||||
impl ParseErrorKind for NoTStringEnd {
|
|
||||||
const DESCRIPTION: &'static str = "A $-string literal was not closed with `\"`";
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import super::(known::*, bool::*, number::*, string::*, functional::*)
|
import super::(known::*, bool::*, number::*, string::*, fn::*)
|
||||||
import super::loop::recursive
|
import super::loop::recursive
|
||||||
import super::(to_string, pmatch, macro, panic, conv, list, option)
|
import super::(pmatch, macro, panic, conv, list, option)
|
||||||
|
|
||||||
-- referenced in the impl table in Rust
|
-- referenced in the impl table in Rust
|
||||||
const to_string_impl := \t. "tuple[" ++ (
|
const to_string_impl := \t. "tuple[" ++ (
|
||||||
|
|||||||
@@ -1,26 +1,29 @@
|
|||||||
//! `std::tuple` A vector-based sequence for storing short sequences.
|
//! `std::tuple` A vector-based sequence for storing short sequences.
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use super::conv::TO_STRING;
|
|
||||||
use super::protocol::Tag;
|
use super::protocol::Tag;
|
||||||
use super::reflect::refer;
|
use super::reflect::refer;
|
||||||
use crate::foreign::error::{AssertionError, ExternResult};
|
use crate::foreign::error::{AssertionError, RTResult};
|
||||||
use crate::foreign::fn_bridge::Thunk;
|
use crate::foreign::fn_bridge::Thunk;
|
||||||
use crate::foreign::inert::{Inert, InertPayload};
|
use crate::foreign::inert::{Inert, InertPayload};
|
||||||
use crate::foreign::try_from_expr::WithLoc;
|
use crate::foreign::try_from_expr::WithLoc;
|
||||||
use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
|
use crate::gen::tree::{atom_ent, xfn_ent, ConstTree};
|
||||||
use crate::interpreter::nort::Expr;
|
use crate::interpreter::nort::Expr;
|
||||||
use crate::location::{CodeGenInfo, CodeLocation};
|
use crate::location::{CodeGenInfo, CodeLocation};
|
||||||
|
use crate::sym;
|
||||||
use crate::utils::ddispatch::Request;
|
use crate::utils::ddispatch::Request;
|
||||||
use crate::utils::pure_seq::pushed;
|
use crate::utils::pure_seq::pushed;
|
||||||
|
|
||||||
static TUPLE_TAG: Lazy<Tag> = Lazy::new(|| {
|
static TUPLE_TAG: Lazy<Tag> = Lazy::new(|| {
|
||||||
let location = CodeLocation::Gen(CodeGenInfo::no_details("stdlib::tuple::tag"));
|
let location = CodeLocation::new_gen(CodeGenInfo::no_details(sym!(std::tuple)));
|
||||||
Tag::new("tuple", [(TO_STRING.id(), refer("std::tuple::to_string_impl").to_expr(location))])
|
Tag::new(sym!(std::tuple), [(
|
||||||
|
sym!(std::string::conversion),
|
||||||
|
refer("std::tuple::to_string_impl").into_expr(location),
|
||||||
|
)])
|
||||||
});
|
});
|
||||||
|
|
||||||
/// A short contiquous random access sequence of Orchid values.
|
/// A short contiquous random access sequence of Orchid values.
|
||||||
@@ -30,8 +33,8 @@ impl InertPayload for Tuple {
|
|||||||
const TYPE_STR: &'static str = "tuple";
|
const TYPE_STR: &'static str = "tuple";
|
||||||
fn respond(&self, mut request: Request) { request.serve_with(|| TUPLE_TAG.clone()) }
|
fn respond(&self, mut request: Request) { request.serve_with(|| TUPLE_TAG.clone()) }
|
||||||
}
|
}
|
||||||
impl Debug for Tuple {
|
impl fmt::Debug for Tuple {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "Tuple")?;
|
write!(f, "Tuple")?;
|
||||||
f.debug_list().entries(self.0.iter().map(|e| &e.clause)).finish()
|
f.debug_list().entries(self.0.iter().map(|e| &e.clause)).finish()
|
||||||
}
|
}
|
||||||
@@ -39,7 +42,7 @@ impl Debug for Tuple {
|
|||||||
|
|
||||||
fn length(tuple: Inert<Tuple>) -> Inert<usize> { Inert(tuple.0.0.len()) }
|
fn length(tuple: Inert<Tuple>) -> Inert<usize> { Inert(tuple.0.0.len()) }
|
||||||
|
|
||||||
fn pick(WithLoc(loc, tuple): WithLoc<Inert<Tuple>>, idx: Inert<usize>) -> ExternResult<Expr> {
|
fn pick(WithLoc(loc, tuple): WithLoc<Inert<Tuple>>, idx: Inert<usize>) -> RTResult<Expr> {
|
||||||
(tuple.0.0.get(idx.0).cloned()).ok_or_else(|| {
|
(tuple.0.0.get(idx.0).cloned()).ok_or_else(|| {
|
||||||
let msg = format!("{} <= {idx}", tuple.0.0.len());
|
let msg = format!("{} <= {idx}", tuple.0.0.len());
|
||||||
AssertionError::ext(loc, "Tuple index out of bounds", msg)
|
AssertionError::ext(loc, "Tuple index out of bounds", msg)
|
||||||
@@ -47,15 +50,15 @@ fn pick(WithLoc(loc, tuple): WithLoc<Inert<Tuple>>, idx: Inert<usize>) -> Extern
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn push(Inert(tuple): Inert<Tuple>, item: Thunk) -> Inert<Tuple> {
|
fn push(Inert(tuple): Inert<Tuple>, item: Thunk) -> Inert<Tuple> {
|
||||||
let items = Arc::try_unwrap(tuple.0).unwrap_or_else(|a| (*a).clone());
|
let items = Arc::unwrap_or_clone(tuple.0);
|
||||||
Inert(Tuple(Arc::new(pushed(items, item.0))))
|
Inert(Tuple(Arc::new(pushed(items, item.0))))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn tuple_lib() -> ConstTree {
|
pub(super) fn tuple_lib() -> ConstTree {
|
||||||
ConstTree::ns("std", [ConstTree::tree([TUPLE_TAG.as_tree_ent([
|
ConstTree::ns("std::tuple", [TUPLE_TAG.to_tree([
|
||||||
atom_ent("empty", [Inert(Tuple(Arc::new(Vec::new())))]),
|
atom_ent("empty", [Inert(Tuple(Arc::new(Vec::new())))]),
|
||||||
xfn_ent("length", [length]),
|
xfn_ent("length", [length]),
|
||||||
xfn_ent("pick", [pick]),
|
xfn_ent("pick", [pick]),
|
||||||
xfn_ent("push", [push]),
|
xfn_ent("push", [push]),
|
||||||
])])])
|
])])
|
||||||
}
|
}
|
||||||
|
|||||||
158
src/location.rs
158
src/location.rs
@@ -1,19 +1,28 @@
|
|||||||
use std::fmt::{Debug, Display};
|
//! Structures that show where code or semantic elements came from
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::name::VPath;
|
use crate::name::{NameLike, Sym};
|
||||||
|
use crate::sym;
|
||||||
|
|
||||||
/// A full source code unit, such as a source file
|
/// A full source code unit, such as a source file
|
||||||
#[derive(Clone, Eq)]
|
#[derive(Clone, Eq)]
|
||||||
pub struct SourceCode {
|
pub struct SourceCode {
|
||||||
|
pub(crate) path: Sym,
|
||||||
|
pub(crate) text: Arc<String>,
|
||||||
|
}
|
||||||
|
impl SourceCode {
|
||||||
|
/// Create a new source file description
|
||||||
|
pub fn new(path: Sym, source: Arc<String>) -> Self { Self { path, text: source } }
|
||||||
/// Location the source code was loaded from in the virtual tree
|
/// Location the source code was loaded from in the virtual tree
|
||||||
pub path: Arc<VPath>,
|
pub fn path(&self) -> Sym { self.path.clone() }
|
||||||
/// Raw source code string
|
/// Raw source code string
|
||||||
pub source: Arc<String>,
|
pub fn text(&self) -> Arc<String> { self.text.clone() }
|
||||||
}
|
}
|
||||||
impl PartialEq for SourceCode {
|
impl PartialEq for SourceCode {
|
||||||
fn eq(&self, other: &Self) -> bool { self.path == other.path }
|
fn eq(&self, other: &Self) -> bool { self.path == other.path }
|
||||||
@@ -21,45 +30,55 @@ impl PartialEq for SourceCode {
|
|||||||
impl Hash for SourceCode {
|
impl Hash for SourceCode {
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.path.hash(state) }
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.path.hash(state) }
|
||||||
}
|
}
|
||||||
impl Debug for SourceCode {
|
impl fmt::Debug for SourceCode {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "CodeInfo({self})") }
|
||||||
write!(f, "CodeInfo({self})")
|
|
||||||
}
|
}
|
||||||
}
|
impl fmt::Display for SourceCode {
|
||||||
impl Display for SourceCode {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}.orc", self.path.str_iter().join("/"))
|
write!(f, "{}.orc", self.path.str_iter().join("/"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl AsRef<str> for SourceCode {
|
||||||
|
fn as_ref(&self) -> &str { &self.text }
|
||||||
|
}
|
||||||
|
|
||||||
/// Exact source code location. Includes where the code was loaded from, what
|
/// Exact source code location. Includes where the code was loaded from, what
|
||||||
/// the original source code was, and a byte range.
|
/// the original source code was, and a byte range.
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct SourceRange {
|
pub struct SourceRange {
|
||||||
/// Source code
|
pub(crate) code: SourceCode,
|
||||||
pub code: SourceCode,
|
pub(crate) range: Range<usize>,
|
||||||
/// Byte range
|
|
||||||
pub range: Range<usize>,
|
|
||||||
}
|
}
|
||||||
impl SourceRange {
|
impl SourceRange {
|
||||||
|
/// Create a dud [SourceRange] for testing. Its value is unspecified and
|
||||||
|
/// volatile.
|
||||||
|
pub fn mock() -> Self {
|
||||||
|
let code = SourceCode { path: sym!(test), text: Arc::new(String::new()) };
|
||||||
|
SourceRange { range: 0..1, code }
|
||||||
|
}
|
||||||
|
/// Source code
|
||||||
|
pub fn code(&self) -> SourceCode { self.code.clone() }
|
||||||
|
/// Source text
|
||||||
|
pub fn text(&self) -> Arc<String> { self.code.text.clone() }
|
||||||
|
/// Path the source text was loaded from
|
||||||
|
pub fn path(&self) -> Sym { self.code.path.clone() }
|
||||||
|
/// Byte range
|
||||||
|
pub fn range(&self) -> Range<usize> { self.range.clone() }
|
||||||
|
/// Syntactic location
|
||||||
|
pub fn origin(&self) -> CodeOrigin { CodeOrigin::Source(self.clone()) }
|
||||||
/// Transform the numeric byte range
|
/// Transform the numeric byte range
|
||||||
pub fn map_range(
|
pub fn map_range(&self, map: impl FnOnce(Range<usize>) -> Range<usize>) -> Self {
|
||||||
&self,
|
Self { code: self.code(), range: map(self.range()) }
|
||||||
map: impl FnOnce(Range<usize>) -> Range<usize>,
|
|
||||||
) -> Self {
|
|
||||||
Self { code: self.code.clone(), range: map(self.range.clone()) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Debug for SourceRange {
|
impl fmt::Debug for SourceRange {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "CodeRange({self})") }
|
||||||
write!(f, "CodeRange({} {:?})", self.code, self.range)
|
|
||||||
}
|
}
|
||||||
}
|
impl fmt::Display for SourceRange {
|
||||||
impl Display for SourceRange {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let Self { code, range } = self;
|
let Self { code, range } = self;
|
||||||
let (sl, sc) = pos2lc(code.source.as_str(), range.start);
|
let (sl, sc) = pos2lc(code.text.as_str(), range.start);
|
||||||
let (el, ec) = pos2lc(code.source.as_str(), range.end);
|
let (el, ec) = pos2lc(code.text.as_str(), range.end);
|
||||||
write!(f, "{code} {sl}:{sc}")?;
|
write!(f, "{code} {sl}:{sc}")?;
|
||||||
if el == sl {
|
if el == sl {
|
||||||
if sc + 1 == ec { Ok(()) } else { write!(f, "..{ec}") }
|
if sc + 1 == ec { Ok(()) } else { write!(f, "..{ec}") }
|
||||||
@@ -73,49 +92,40 @@ impl Display for SourceRange {
|
|||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct CodeGenInfo {
|
pub struct CodeGenInfo {
|
||||||
/// formatted like a Rust namespace
|
/// formatted like a Rust namespace
|
||||||
pub generator: &'static str,
|
pub generator: Sym,
|
||||||
/// Unformatted user message with relevant circumstances and parameters
|
/// Unformatted user message with relevant circumstances and parameters
|
||||||
pub details: Arc<String>,
|
pub details: Arc<String>,
|
||||||
}
|
}
|
||||||
impl CodeGenInfo {
|
impl CodeGenInfo {
|
||||||
/// A codegen marker with no user message and parameters
|
/// A codegen marker with no user message and parameters
|
||||||
pub fn no_details(generator: &'static str) -> Self {
|
pub fn no_details(generator: Sym) -> Self { Self { generator, details: Arc::new(String::new()) } }
|
||||||
Self { generator, details: Arc::new(String::new()) }
|
|
||||||
}
|
|
||||||
/// A codegen marker with a user message or parameters
|
/// A codegen marker with a user message or parameters
|
||||||
pub fn details(generator: &'static str, details: impl AsRef<str>) -> Self {
|
pub fn details(generator: Sym, details: impl AsRef<str>) -> Self {
|
||||||
Self { generator, details: Arc::new(details.as_ref().to_string()) }
|
Self { generator, details: Arc::new(details.as_ref().to_string()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Debug for CodeGenInfo {
|
impl fmt::Debug for CodeGenInfo {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "CodeGenInfo({self})") }
|
||||||
write!(f, "CodeGenInfo({self})")
|
|
||||||
}
|
}
|
||||||
}
|
impl fmt::Display for CodeGenInfo {
|
||||||
impl Display for CodeGenInfo {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "generated by {}", self.generator)?;
|
write!(f, "generated by {}", self.generator)?;
|
||||||
if !self.details.is_empty() {
|
if !self.details.is_empty() { write!(f, ", details: {}", self.details) } else { write!(f, ".") }
|
||||||
write!(f, ", details: {}", self.details)
|
|
||||||
} else {
|
|
||||||
write!(f, ".")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A location for error reporting. In the context of an error, identifies a
|
/// identifies a sequence of characters that contributed to the enclosing
|
||||||
/// sequence of suspect characters or the reason the code was generated for
|
/// construct or the reason the code was generated for generated code.
|
||||||
/// generated code. Meaningful within the context of a project.
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
pub enum CodeOrigin {
|
||||||
pub enum CodeLocation {
|
|
||||||
/// Character sequence
|
/// Character sequence
|
||||||
Source(SourceRange),
|
Source(SourceRange),
|
||||||
/// Generated construct
|
/// Generated construct
|
||||||
Gen(CodeGenInfo),
|
Gen(CodeGenInfo),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for CodeLocation {
|
impl fmt::Display for CodeOrigin {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Gen(info) => write!(f, "{info}"),
|
Self::Gen(info) => write!(f, "{info}"),
|
||||||
Self::Source(cr) => write!(f, "{cr}"),
|
Self::Source(cr) => write!(f, "{cr}"),
|
||||||
@@ -123,9 +133,53 @@ impl Display for CodeLocation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for CodeOrigin {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "CodeOrigin({self})") }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Location data associated with any code fragment. Identifies where the code
|
||||||
|
/// came from and where it resides in the tree.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct CodeLocation {
|
||||||
|
pub(crate) origin: CodeOrigin,
|
||||||
|
pub(crate) module: Sym,
|
||||||
|
}
|
||||||
|
impl CodeLocation {
|
||||||
|
pub(crate) fn new_src(range: SourceRange, module: Sym) -> Self {
|
||||||
|
Self { origin: CodeOrigin::Source(range), module }
|
||||||
|
}
|
||||||
|
/// Create a location for generated code. The generator string must not be
|
||||||
|
/// empty. For code, the generator string must contain at least one `::`
|
||||||
|
pub fn new_gen(gen: CodeGenInfo) -> Self {
|
||||||
|
Self { module: gen.generator.clone(), origin: CodeOrigin::Gen(gen) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the syntactic location
|
||||||
|
pub fn origin(&self) -> CodeOrigin { self.origin.clone() }
|
||||||
|
/// Get the name of the containing module
|
||||||
|
pub fn module(&self) -> Sym { self.module.clone() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CodeLocation {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
if self.module[..].is_empty() {
|
||||||
|
write!(f, "global {}", self.origin)
|
||||||
|
} else {
|
||||||
|
write!(f, "{} in {}", self.origin, self.module)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for CodeLocation {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "CodeLocation({self})") }
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn pos2lc(s: &str, i: usize) -> (usize, usize) {
|
fn pos2lc(s: &str, i: usize) -> (usize, usize) {
|
||||||
s.chars().take(i).fold((1, 1), |(line, col), char| {
|
s.chars().take(i).fold(
|
||||||
|
(1, 1),
|
||||||
|
|(line, col), char| {
|
||||||
if char == '\n' { (line + 1, 1) } else { (line, col + 1) }
|
if char == '\n' { (line + 1, 1) } else { (line, col + 1) }
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
406
src/name.rs
406
src/name.rs
@@ -1,45 +1,123 @@
|
|||||||
|
//! Various datatypes that all represent namespaced names.
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::fmt::{Debug, Display};
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
use std::iter::Cloned;
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use std::ops::Index;
|
use std::ops::{Deref, Index};
|
||||||
use std::vec;
|
use std::path::Path;
|
||||||
|
use std::{fmt, slice, vec};
|
||||||
|
|
||||||
use intern_all::{i, Tok};
|
use intern_all::{i, Tok};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use trait_set::trait_set;
|
||||||
|
|
||||||
use crate::utils::boxed_iter::BoxedIter;
|
trait_set! {
|
||||||
|
/// Traits that all name iterators should implement
|
||||||
|
pub trait NameIter = Iterator<Item = Tok<String>> + DoubleEndedIterator + ExactSizeIterator;
|
||||||
|
}
|
||||||
|
|
||||||
/// A borrowed name fragment which can be empty. See [VPath] for the owned
|
/// A borrowed name fragment which can be empty. See [VPath] for the owned
|
||||||
/// variant.
|
/// variant.
|
||||||
pub struct PathSlice<'a>(pub &'a [Tok<String>]);
|
#[derive(Hash, PartialEq, Eq)]
|
||||||
impl<'a> PathSlice<'a> {
|
#[repr(transparent)]
|
||||||
|
pub struct PathSlice([Tok<String>]);
|
||||||
|
impl PathSlice {
|
||||||
|
/// Create a new [PathSlice]
|
||||||
|
pub fn new(slice: &[Tok<String>]) -> &PathSlice {
|
||||||
|
// SAFETY: This is ok because PathSlice is #[repr(transparent)]
|
||||||
|
unsafe { &*(slice as *const [Tok<String>] as *const PathSlice) }
|
||||||
|
}
|
||||||
/// Convert to an owned name fragment
|
/// Convert to an owned name fragment
|
||||||
pub fn to_vpath(&self) -> VPath { VPath(self.0.to_vec()) }
|
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
|
/// Iterate over the segments
|
||||||
pub fn str_iter(&self) -> impl Iterator<Item = &'_ str> {
|
pub fn str_iter(&self) -> impl Iterator<Item = &'_ str> {
|
||||||
Box::new(self.0.iter().map(|s| s.as_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()]
|
||||||
}
|
}
|
||||||
impl<'a> Debug for PathSlice<'a> {
|
/// Find the longest shared suffix of this name and another sequence
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
pub fn cosuffix<'a>(&'a self, other: &PathSlice) -> &'a PathSlice {
|
||||||
write!(f, "VName({self})")
|
&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..]))
|
||||||
}
|
}
|
||||||
impl<'a> Display for PathSlice<'a> {
|
/// Number of path segments
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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) -> &[Tok<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("::"))
|
write!(f, "{}", self.str_iter().join("::"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> Borrow<[Tok<String>]> for PathSlice<'a> {
|
impl Borrow<[Tok<String>]> for PathSlice {
|
||||||
fn borrow(&self) -> &[Tok<String>] { self.0 }
|
fn borrow(&self) -> &[Tok<String>] { &self.0 }
|
||||||
|
}
|
||||||
|
impl<'a> IntoIterator for &'a PathSlice {
|
||||||
|
type IntoIter = Cloned<slice::Iter<'a, Tok<String>>>;
|
||||||
|
type Item = Tok<String>;
|
||||||
|
fn into_iter(self) -> Self::IntoIter { self.0.iter().cloned() }
|
||||||
}
|
}
|
||||||
impl<'a, T> Index<T> for PathSlice<'a>
|
|
||||||
where [Tok<String>]: Index<T>
|
|
||||||
{
|
|
||||||
type Output = <[Tok<String>] as Index<T>>::Output;
|
|
||||||
|
|
||||||
fn index(&self, index: T) -> &Self::Output { &self.0[index] }
|
mod idx_impls {
|
||||||
|
use std::ops;
|
||||||
|
|
||||||
|
use intern_all::Tok;
|
||||||
|
|
||||||
|
use super::PathSlice;
|
||||||
|
|
||||||
|
impl ops::Index<usize> for PathSlice {
|
||||||
|
type Output = Tok<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 = [Tok<String>];
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target { &self.0 }
|
||||||
|
}
|
||||||
|
impl Borrow<PathSlice> for [Tok<String>] {
|
||||||
|
fn borrow(&self) -> &PathSlice { PathSlice::new(self) }
|
||||||
|
}
|
||||||
|
impl<const N: usize> Borrow<PathSlice> for [Tok<String>; N] {
|
||||||
|
fn borrow(&self) -> &PathSlice { PathSlice::new(&self[..]) }
|
||||||
|
}
|
||||||
|
impl Borrow<PathSlice> for Vec<Tok<String>> {
|
||||||
|
fn borrow(&self) -> &PathSlice { PathSlice::new(&self[..]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A token path which may be empty. [VName] is the non-empty,
|
/// A token path which may be empty. [VName] is the non-empty,
|
||||||
@@ -51,6 +129,11 @@ impl VPath {
|
|||||||
pub fn new(items: impl IntoIterator<Item = Tok<String>>) -> Self {
|
pub fn new(items: impl IntoIterator<Item = Tok<String>>) -> Self {
|
||||||
Self(items.into_iter().collect())
|
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
|
/// Prepend some tokens to the path
|
||||||
pub fn prefix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self {
|
pub fn prefix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self {
|
||||||
Self(items.into_iter().chain(self.0).collect())
|
Self(items.into_iter().chain(self.0).collect())
|
||||||
@@ -71,22 +154,30 @@ impl VPath {
|
|||||||
pub fn into_name(self) -> Result<VName, EmptyNameError> { VName::new(self.0) }
|
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
|
/// Add a token to the path. Since now we know that it can't be empty, turn it
|
||||||
/// into a name.
|
/// into a name.
|
||||||
pub fn as_prefix_of(self, name: Tok<String>) -> VName {
|
pub fn name_with_prefix(self, name: Tok<String>) -> VName {
|
||||||
VName(self.into_iter().chain([name]).collect())
|
VName(self.into_iter().chain([name]).collect())
|
||||||
}
|
}
|
||||||
/// Add a token to the beginning of the. Since now we know that it can't be
|
/// Add a token to the beginning of the. Since now we know that it can't be
|
||||||
/// empty, turn it into a name.
|
/// empty, turn it into a name.
|
||||||
pub fn as_suffix_of(self, name: Tok<String>) -> VName {
|
pub fn name_with_suffix(self, name: Tok<String>) -> VName {
|
||||||
VName([name].into_iter().chain(self).collect())
|
VName([name].into_iter().chain(self).collect())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
impl Debug for VPath {
|
/// Convert a fs path to a vpath
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
pub fn from_path(path: &Path) -> Option<(Self, bool)> {
|
||||||
write!(f, "VName({self})")
|
let to_vpath = |p: &Path| p.iter().map(|c| c.to_str().map(i)).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 Display for VPath {
|
}
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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("::"))
|
write!(f, "{}", self.str_iter().join("::"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,13 +194,20 @@ impl IntoIterator for VPath {
|
|||||||
impl Borrow<[Tok<String>]> for VPath {
|
impl Borrow<[Tok<String>]> for VPath {
|
||||||
fn borrow(&self) -> &[Tok<String>] { self.0.borrow() }
|
fn borrow(&self) -> &[Tok<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
|
impl<T> Index<T> for VPath
|
||||||
where Vec<Tok<String>>: Index<T>
|
where PathSlice: Index<T>
|
||||||
{
|
{
|
||||||
type Output = <Vec<Tok<String>> as Index<T>>::Output;
|
type Output = <PathSlice as Index<T>>::Output;
|
||||||
|
|
||||||
fn index(&self, index: T) -> &Self::Output { &self.0[index] }
|
fn index(&self, index: T) -> &Self::Output { &Borrow::<PathSlice>::borrow(self)[index] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mutable representation of a namespaced identifier of at least one segment.
|
/// A mutable representation of a namespaced identifier of at least one segment.
|
||||||
@@ -123,9 +221,7 @@ pub struct VName(Vec<Tok<String>>);
|
|||||||
impl VName {
|
impl VName {
|
||||||
/// Assert that the sequence isn't empty and wrap it in [VName] to represent
|
/// Assert that the sequence isn't empty and wrap it in [VName] to represent
|
||||||
/// this invariant
|
/// this invariant
|
||||||
pub fn new(
|
pub fn new(items: impl IntoIterator<Item = Tok<String>>) -> Result<Self, EmptyNameError> {
|
||||||
items: impl IntoIterator<Item = Tok<String>>,
|
|
||||||
) -> Result<Self, EmptyNameError> {
|
|
||||||
let data: Vec<_> = items.into_iter().collect();
|
let data: Vec<_> = items.into_iter().collect();
|
||||||
if data.is_empty() { Err(EmptyNameError) } else { Ok(Self(data)) }
|
if data.is_empty() { Err(EmptyNameError) } else { Ok(Self(data)) }
|
||||||
}
|
}
|
||||||
@@ -138,20 +234,8 @@ impl VName {
|
|||||||
pub fn vec_mut(&mut self) -> &mut Vec<Tok<String>> { &mut self.0 }
|
pub fn vec_mut(&mut self) -> &mut Vec<Tok<String>> { &mut self.0 }
|
||||||
/// Intern the name and return a [Sym]
|
/// Intern the name and return a [Sym]
|
||||||
pub fn to_sym(&self) -> Sym { Sym(i(&self.0)) }
|
pub fn to_sym(&self) -> Sym { Sym(i(&self.0)) }
|
||||||
/// like Slice's split_first, but non-optional and tokens are cheap to clone
|
|
||||||
pub fn split_first(&self) -> (Tok<String>, &[Tok<String>]) {
|
|
||||||
let (h, t) = self.0.split_first().expect("VName can never be empty");
|
|
||||||
(h.clone(), t)
|
|
||||||
}
|
|
||||||
/// like Slice's split_last, but non-optional and tokens are cheap to clone
|
|
||||||
pub fn split_last(&self) -> (Tok<String>, &[Tok<String>]) {
|
|
||||||
let (f, b) = self.0.split_last().expect("VName can never be empty");
|
|
||||||
(f.clone(), b)
|
|
||||||
}
|
|
||||||
/// If this name has only one segment, return it
|
/// If this name has only one segment, return it
|
||||||
pub fn as_root(&self) -> Option<Tok<String>> {
|
pub fn as_root(&self) -> Option<Tok<String>> { self.0.iter().exactly_one().ok().cloned() }
|
||||||
self.0.iter().exactly_one().ok().cloned()
|
|
||||||
}
|
|
||||||
/// Prepend the segments to this name
|
/// Prepend the segments to this name
|
||||||
#[must_use = "This is a pure function"]
|
#[must_use = "This is a pure function"]
|
||||||
pub fn prefix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self {
|
pub fn prefix(self, items: impl IntoIterator<Item = Tok<String>>) -> Self {
|
||||||
@@ -163,33 +247,15 @@ impl VName {
|
|||||||
Self(self.0.into_iter().chain(items).collect())
|
Self(self.0.into_iter().chain(items).collect())
|
||||||
}
|
}
|
||||||
/// Read a `::` separated namespaced name
|
/// Read a `::` separated namespaced name
|
||||||
pub fn parse(s: &str) -> Result<Self, EmptyNameError> {
|
pub fn parse(s: &str) -> Result<Self, EmptyNameError> { Self::new(VPath::parse(s)) }
|
||||||
Self::new(VPath::parse(s))
|
|
||||||
}
|
|
||||||
/// Read a name from a string literal which can be known not to be empty
|
|
||||||
pub fn literal(s: &'static str) -> Self {
|
|
||||||
Self::parse(s).expect("name literal should not be empty")
|
|
||||||
}
|
|
||||||
/// Find the longest shared prefix of this name and another sequence
|
|
||||||
pub fn coprefix(&self, other: &[Tok<String>]) -> &[Tok<String>] {
|
|
||||||
&self.0
|
|
||||||
[0..self.0.iter().zip(other.iter()).take_while(|(l, r)| l == r).count()]
|
|
||||||
}
|
|
||||||
/// Obtain an iterator over the segments of the name
|
/// Obtain an iterator over the segments of the name
|
||||||
pub fn iter(&self) -> impl Iterator<Item = Tok<String>> + '_ {
|
pub fn iter(&self) -> impl Iterator<Item = Tok<String>> + '_ { self.0.iter().cloned() }
|
||||||
self.0.iter().cloned()
|
|
||||||
}
|
}
|
||||||
|
impl fmt::Debug for VName {
|
||||||
/// Convert to [PathSlice]
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "VName({self})") }
|
||||||
pub fn as_path_slice(&self) -> PathSlice { PathSlice(&self[..]) }
|
|
||||||
}
|
}
|
||||||
impl Debug for VName {
|
impl fmt::Display for VName {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "VName({self})")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for VName {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.str_iter().join("::"))
|
write!(f, "{}", self.str_iter().join("::"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,15 +265,22 @@ impl IntoIterator for VName {
|
|||||||
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
|
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
|
||||||
}
|
}
|
||||||
impl<T> Index<T> for VName
|
impl<T> Index<T> for VName
|
||||||
where Vec<Tok<String>>: Index<T>
|
where PathSlice: Index<T>
|
||||||
{
|
{
|
||||||
type Output = <Vec<Tok<String>> as Index<T>>::Output;
|
type Output = <PathSlice as Index<T>>::Output;
|
||||||
|
|
||||||
fn index(&self, index: T) -> &Self::Output { &self.0[index] }
|
fn index(&self, index: T) -> &Self::Output { &self.deref()[index] }
|
||||||
}
|
}
|
||||||
impl Borrow<[Tok<String>]> for VName {
|
impl Borrow<[Tok<String>]> for VName {
|
||||||
fn borrow(&self) -> &[Tok<String>] { self.0.borrow() }
|
fn borrow(&self) -> &[Tok<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
|
/// Error produced when a non-empty name [VName] or [Sym] is constructed with an
|
||||||
/// empty sequence
|
/// empty sequence
|
||||||
@@ -230,23 +303,12 @@ pub struct Sym(Tok<Vec<Tok<String>>>);
|
|||||||
impl Sym {
|
impl Sym {
|
||||||
/// Assert that the sequence isn't empty, intern it and wrap it in a [Sym] to
|
/// Assert that the sequence isn't empty, intern it and wrap it in a [Sym] to
|
||||||
/// represent this invariant
|
/// represent this invariant
|
||||||
pub fn new(
|
pub fn new(v: impl IntoIterator<Item = Tok<String>>) -> Result<Self, EmptyNameError> {
|
||||||
v: impl IntoIterator<Item = Tok<String>>,
|
|
||||||
) -> Result<Self, EmptyNameError> {
|
|
||||||
let items = v.into_iter().collect::<Vec<_>>();
|
let items = v.into_iter().collect::<Vec<_>>();
|
||||||
Self::from_tok(i(&items))
|
Self::from_tok(i(&items))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a `::` separated namespaced name.
|
/// Read a `::` separated namespaced name.
|
||||||
pub fn parse(s: &str) -> Result<Self, EmptyNameError> {
|
pub fn parse(s: &str) -> Result<Self, EmptyNameError> { Ok(Sym(i(&VName::parse(s)?.into_vec()))) }
|
||||||
Ok(Sym(i(&VName::parse(s)?.into_vec())))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a string and panic if it's not empty
|
|
||||||
pub fn literal(s: &'static str) -> Self {
|
|
||||||
Self::parse(s).expect("name literal should not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assert that a token isn't empty, and wrap it in a [Sym]
|
/// Assert that a token isn't empty, and wrap it in a [Sym]
|
||||||
pub fn from_tok(t: Tok<Vec<Tok<String>>>) -> Result<Self, EmptyNameError> {
|
pub fn from_tok(t: Tok<Vec<Tok<String>>>) -> Result<Self, EmptyNameError> {
|
||||||
if t.is_empty() { Err(EmptyNameError) } else { Ok(Self(t)) }
|
if t.is_empty() { Err(EmptyNameError) } else { Ok(Self(t)) }
|
||||||
@@ -255,80 +317,160 @@ impl Sym {
|
|||||||
pub fn tok(&self) -> Tok<Vec<Tok<String>>> { self.0.clone() }
|
pub fn tok(&self) -> Tok<Vec<Tok<String>>> { self.0.clone() }
|
||||||
/// Get a number unique to this name suitable for arbitrary ordering.
|
/// Get a number unique to this name suitable for arbitrary ordering.
|
||||||
pub fn id(&self) -> NonZeroUsize { self.0.id() }
|
pub fn id(&self) -> NonZeroUsize { self.0.id() }
|
||||||
/// Get an iterator over the tokens in this name
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = Tok<String>> + '_ {
|
|
||||||
self.0.iter().cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Like Slice's split_last, except this slice is never empty
|
|
||||||
pub fn split_last(&self) -> (Tok<String>, PathSlice) {
|
|
||||||
let (foot, torso) = self.0.split_last().expect("Sym never empty");
|
|
||||||
(foot.clone(), PathSlice(torso))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Like Slice's split_first, except this slice is never empty
|
|
||||||
pub fn split_first(&self) -> (Tok<String>, PathSlice) {
|
|
||||||
let (head, tail) = self.0.split_first().expect("Sym never empty");
|
|
||||||
(head.clone(), PathSlice(tail))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extern the sym for editing
|
/// Extern the sym for editing
|
||||||
pub fn to_vname(&self) -> VName { VName(self[..].to_vec()) }
|
pub fn to_vname(&self) -> VName { VName(self[..].to_vec()) }
|
||||||
|
|
||||||
/// Convert to [PathSlice]
|
|
||||||
pub fn as_path_slice(&self) -> PathSlice { PathSlice(&self[..]) }
|
|
||||||
}
|
}
|
||||||
impl Debug for Sym {
|
impl fmt::Debug for Sym {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Sym({self})") }
|
||||||
write!(f, "Sym({self})")
|
|
||||||
}
|
}
|
||||||
}
|
impl fmt::Display for Sym {
|
||||||
impl Display for Sym {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.str_iter().join("::"))
|
write!(f, "{}", self.str_iter().join("::"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T> Index<T> for Sym
|
impl<T> Index<T> for Sym
|
||||||
where Vec<Tok<String>>: Index<T>
|
where PathSlice: Index<T>
|
||||||
{
|
{
|
||||||
type Output = <Vec<Tok<String>> as Index<T>>::Output;
|
type Output = <PathSlice as Index<T>>::Output;
|
||||||
|
|
||||||
fn index(&self, index: T) -> &Self::Output { &(&*self.0)[index] }
|
fn index(&self, index: T) -> &Self::Output { &self.deref()[index] }
|
||||||
}
|
}
|
||||||
impl Borrow<[Tok<String>]> for Sym {
|
impl Borrow<[Tok<String>]> for Sym {
|
||||||
fn borrow(&self) -> &[Tok<String>] { self.0.borrow() }
|
fn borrow(&self) -> &[Tok<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
|
/// An abstraction over tokenized vs non-tokenized names so that they can be
|
||||||
/// handled together in datastructures. The names can never be empty
|
/// handled together in datastructures. The names can never be empty
|
||||||
#[allow(clippy::len_without_is_empty)] // never empty
|
#[allow(clippy::len_without_is_empty)] // never empty
|
||||||
pub trait NameLike: 'static + Clone + Eq + Hash + Debug + Display {
|
pub trait NameLike:
|
||||||
|
'static + Clone + Eq + Hash + fmt::Debug + fmt::Display + Borrow<PathSlice>
|
||||||
|
{
|
||||||
|
/// Convert into held slice
|
||||||
|
fn as_slice(&self) -> &[Tok<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
|
/// Fully resolve the name for printing
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn to_strv(&self) -> Vec<String> {
|
fn to_strv(&self) -> Vec<String> { self.iter().map(|s| s.to_string()).collect() }
|
||||||
self.str_iter().map(str::to_owned).collect()
|
|
||||||
}
|
|
||||||
/// Format the name as an approximate filename
|
/// Format the name as an approximate filename
|
||||||
fn as_src_path(&self) -> String {
|
fn as_src_path(&self) -> String { format!("{}.orc", self.iter().join("/")) }
|
||||||
format!("{}.orc", self.str_iter().join("/"))
|
|
||||||
}
|
|
||||||
/// Return the number of segments in the name
|
/// Return the number of segments in the name
|
||||||
fn len(&self) -> NonZeroUsize {
|
fn len(&self) -> NonZeroUsize {
|
||||||
NonZeroUsize::try_from(self.str_iter().count())
|
NonZeroUsize::try_from(self.iter().count()).expect("NameLike never empty")
|
||||||
.expect("NameLike never empty")
|
|
||||||
}
|
}
|
||||||
/// Fully resolve the name for printing
|
/// Like slice's `split_first` except we know that it always returns Some
|
||||||
fn str_iter(&self) -> BoxedIter<'_, &str>;
|
fn split_first(&self) -> (Tok<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) -> (Tok<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) -> Tok<String> { self.split_first().0 }
|
||||||
|
/// Get the last element
|
||||||
|
fn last(&self) -> Tok<String> { self.split_last().0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NameLike for Sym {
|
impl NameLike for Sym {}
|
||||||
fn str_iter(&self) -> BoxedIter<'_, &str> {
|
impl NameLike for VName {}
|
||||||
Box::new(self.0.iter().map(|s| s.as_str()))
|
|
||||||
|
/// 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(intern_all::i!([intern_all::Tok<String>]: &[
|
||||||
|
intern_all::i!(str: stringify!($seg1))
|
||||||
|
$( , intern_all::i!(str: stringify!($seg)) )*
|
||||||
|
][..])).unwrap()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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([
|
||||||
|
intern_all::i!(str: stringify!($seg1))
|
||||||
|
$( , intern_all::i!(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![
|
||||||
|
intern_all::i!(str: stringify!($seg1))
|
||||||
|
$( , intern_all::i!(str: stringify!($seg)) )+
|
||||||
|
])
|
||||||
|
};
|
||||||
|
() => {
|
||||||
|
$crate::name::VPath(vec![])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NameLike for VName {
|
/// Create a &[PathSlice] literal.
|
||||||
fn str_iter(&self) -> BoxedIter<'_, &str> {
|
///
|
||||||
Box::new(self.0.iter().map(|s| s.as_str()))
|
/// The components are interned much like in [sym]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! path_slice {
|
||||||
|
($seg1:tt $( :: $seg:tt)+) => {
|
||||||
|
$crate::name::PathSlice::new(&[
|
||||||
|
intern_all::i!(str: stringify!($seg1))
|
||||||
|
$( , intern_all::i!(str: stringify!($seg)) )+
|
||||||
|
])
|
||||||
|
};
|
||||||
|
() => {
|
||||||
|
$crate::name::PathSlice::new(&[])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
|
use intern_all::{i, Tok};
|
||||||
|
|
||||||
|
use super::{PathSlice, Sym, VName};
|
||||||
|
use crate::name::VPath;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn recur() {
|
||||||
|
let myname = vname!(foo::bar);
|
||||||
|
let _borrowed_slice: &[Tok<String>] = myname.borrow();
|
||||||
|
let _borrowed_pathslice: &PathSlice = myname.borrow();
|
||||||
|
let _deref_pathslice: &PathSlice = &myname;
|
||||||
|
let _as_slice_out: &[Tok<String>] = myname.as_slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn literals() {
|
||||||
|
assert_eq!(sym!(foo::bar::baz), Sym::new([i("foo"), i("bar"), i("baz")]).unwrap());
|
||||||
|
assert_eq!(vname!(foo::bar::baz), VName::new([i("foo"), i("bar"), i("baz")]).unwrap());
|
||||||
|
assert_eq!(vpath!(foo::bar::baz), VPath::new([i("foo"), i("bar"), i("baz")]));
|
||||||
|
assert_eq!(path_slice!(foo::bar::baz), PathSlice::new(&[i("foo"), i("bar"), i("baz")]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use super::lex_plugin::LexerPlugin;
|
use super::lex_plugin::LexerPlugin;
|
||||||
use super::parse_plugin::ParseLinePlugin;
|
use super::parse_plugin::ParseLinePlugin;
|
||||||
|
use crate::error::Reporter;
|
||||||
use crate::location::{SourceCode, SourceRange};
|
use crate::location::{SourceCode, SourceRange};
|
||||||
use crate::name::VPath;
|
|
||||||
use crate::utils::boxed_iter::{box_empty, BoxedIter};
|
use crate::utils::boxed_iter::{box_empty, BoxedIter};
|
||||||
use crate::utils::sequence::Sequence;
|
use crate::utils::sequence::Sequence;
|
||||||
|
|
||||||
/// Trait enclosing all context features
|
/// Trait enclosing all context features
|
||||||
///
|
///
|
||||||
/// The main implementation is [ParsingContext]
|
/// The main implementation is [ParseCtxImpl]
|
||||||
pub trait ParseCtx {
|
pub trait ParseCtx {
|
||||||
/// Get an object describing the file this source code comes from
|
/// Get an object describing the file this source code comes from
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@@ -23,11 +23,20 @@ pub trait ParseCtx {
|
|||||||
/// Get the list of all parser plugins
|
/// Get the list of all parser plugins
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin>;
|
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin>;
|
||||||
|
/// Error reporter
|
||||||
|
#[must_use]
|
||||||
|
fn reporter(&self) -> &Reporter;
|
||||||
/// Find our position in the text given the text we've yet to parse
|
/// Find our position in the text given the text we've yet to parse
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn pos(&self, tail: &str) -> usize { self.source().len() - tail.len() }
|
fn pos(&self, tail: &str) -> usize {
|
||||||
|
let tail_len = tail.len();
|
||||||
|
let source_len = self.source().len();
|
||||||
|
(self.source().len().checked_sub(tail.len())).unwrap_or_else(|| {
|
||||||
|
panic!("tail.len()={tail_len} greater than self.source().len()={source_len}; tail={tail:?}")
|
||||||
|
})
|
||||||
|
}
|
||||||
/// Generate a location given the length of a token and the unparsed text
|
/// Generate a location given the length of a token and the unparsed text
|
||||||
/// after it. See also [Context::range_loc] if the maths gets complex.
|
/// after it. See also [ParseCtx::range_loc] if the maths gets complex.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn range(&self, len: usize, tl: &str) -> Range<usize> {
|
fn range(&self, len: usize, tl: &str) -> Range<usize> {
|
||||||
match self.pos(tl).checked_sub(len) {
|
match self.pos(tl).checked_sub(len) {
|
||||||
@@ -50,14 +59,13 @@ pub trait ParseCtx {
|
|||||||
/// Get a reference to the full source text. This should not be used for
|
/// Get a reference to the full source text. This should not be used for
|
||||||
/// position math.
|
/// position math.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn source(&self) -> Arc<String> { self.code_info().source.clone() }
|
fn source(&self) -> Arc<String> { self.code_info().text.clone() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, C: ParseCtx + 'a + ?Sized> ParseCtx for &'a C {
|
impl<'a, C: ParseCtx + 'a + ?Sized> ParseCtx for &'a C {
|
||||||
|
fn reporter(&self) -> &Reporter { (*self).reporter() }
|
||||||
fn lexers(&self) -> BoxedIter<'_, &dyn LexerPlugin> { (*self).lexers() }
|
fn lexers(&self) -> BoxedIter<'_, &dyn LexerPlugin> { (*self).lexers() }
|
||||||
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin> {
|
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin> { (*self).line_parsers() }
|
||||||
(*self).line_parsers()
|
|
||||||
}
|
|
||||||
fn pos(&self, tail: &str) -> usize { (*self).pos(tail) }
|
fn pos(&self, tail: &str) -> usize { (*self).pos(tail) }
|
||||||
fn code_info(&self) -> SourceCode { (*self).code_info() }
|
fn code_info(&self) -> SourceCode { (*self).code_info() }
|
||||||
fn source(&self) -> Arc<String> { (*self).source() }
|
fn source(&self) -> Arc<String> { (*self).source() }
|
||||||
@@ -66,20 +74,21 @@ impl<'a, C: ParseCtx + 'a + ?Sized> ParseCtx for &'a C {
|
|||||||
|
|
||||||
/// Struct implementing context
|
/// Struct implementing context
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ParseCtxImpl<'a> {
|
pub struct ParseCtxImpl<'a, 'b> {
|
||||||
/// File to be parsed; where it belongs in the tree and its text
|
/// File to be parsed; where it belongs in the tree and its text
|
||||||
pub code: SourceCode,
|
pub code: SourceCode,
|
||||||
|
/// Error aggregator
|
||||||
|
pub reporter: &'b Reporter,
|
||||||
/// Lexer plugins for parsing custom literals
|
/// Lexer plugins for parsing custom literals
|
||||||
pub lexers: Sequence<'a, &'a (dyn LexerPlugin + 'a)>,
|
pub lexers: Sequence<'a, &'a (dyn LexerPlugin + 'a)>,
|
||||||
/// Parser plugins for parsing custom line structures
|
/// Parser plugins for parsing custom line structures
|
||||||
pub line_parsers: Sequence<'a, &'a dyn ParseLinePlugin>,
|
pub line_parsers: Sequence<'a, &'a dyn ParseLinePlugin>,
|
||||||
}
|
}
|
||||||
impl<'a> ParseCtx for ParseCtxImpl<'a> {
|
impl<'a, 'b> ParseCtx for ParseCtxImpl<'a, 'b> {
|
||||||
|
fn reporter(&self) -> &Reporter { self.reporter }
|
||||||
// Rust doesn't realize that this lifetime is covariant
|
// Rust doesn't realize that this lifetime is covariant
|
||||||
#[allow(clippy::map_identity)]
|
#[allow(clippy::map_identity)]
|
||||||
fn lexers(&self) -> BoxedIter<'_, &dyn LexerPlugin> {
|
fn lexers(&self) -> BoxedIter<'_, &dyn LexerPlugin> { Box::new(self.lexers.iter().map(|r| r)) }
|
||||||
Box::new(self.lexers.iter().map(|r| r))
|
|
||||||
}
|
|
||||||
#[allow(clippy::map_identity)]
|
#[allow(clippy::map_identity)]
|
||||||
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin> {
|
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin> {
|
||||||
Box::new(self.line_parsers.iter().map(|r| r))
|
Box::new(self.line_parsers.iter().map(|r| r))
|
||||||
@@ -87,24 +96,31 @@ impl<'a> ParseCtx for ParseCtxImpl<'a> {
|
|||||||
fn code_info(&self) -> SourceCode { self.code.clone() }
|
fn code_info(&self) -> SourceCode { self.code.clone() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Context instance for testing
|
/// Context instance for testing. Implicitly provides a reporter and panics if
|
||||||
pub struct MockContext;
|
/// any errors are reported
|
||||||
|
pub struct MockContext(pub Reporter);
|
||||||
|
impl MockContext {
|
||||||
|
/// Create a new mock
|
||||||
|
pub fn new() -> Self { Self(Reporter::new()) }
|
||||||
|
}
|
||||||
|
impl Default for MockContext {
|
||||||
|
fn default() -> Self { Self::new() }
|
||||||
|
}
|
||||||
impl ParseCtx for MockContext {
|
impl ParseCtx for MockContext {
|
||||||
|
fn reporter(&self) -> &Reporter { &self.0 }
|
||||||
fn pos(&self, tail: &str) -> usize { usize::MAX / 2 - tail.len() }
|
fn pos(&self, tail: &str) -> usize { usize::MAX / 2 - tail.len() }
|
||||||
// these are expendable
|
// these are expendable
|
||||||
fn code_info(&self) -> SourceCode {
|
fn code_info(&self) -> SourceCode { SourceRange::mock().code() }
|
||||||
SourceCode {
|
|
||||||
path: Arc::new(VPath(vec![])),
|
|
||||||
source: Arc::new(String::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn lexers(&self) -> BoxedIter<'_, &dyn LexerPlugin> { box_empty() }
|
fn lexers(&self) -> BoxedIter<'_, &dyn LexerPlugin> { box_empty() }
|
||||||
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin> { box_empty() }
|
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin> { box_empty() }
|
||||||
}
|
}
|
||||||
|
impl Drop for MockContext {
|
||||||
|
fn drop(&mut self) { self.0.assert() }
|
||||||
|
}
|
||||||
|
|
||||||
/// Context that assigns the same location to every subset of the source code.
|
/// Context that assigns the same location to every subset of the source code.
|
||||||
/// Its main use case is to process source code that was dynamically generated
|
/// Its main use case is to process source code that was dynamically generated
|
||||||
/// in response to some user code.
|
/// in response to some user code. See also [ReporterContext]
|
||||||
pub struct FlatLocContext<'a, C: ParseCtx + ?Sized> {
|
pub struct FlatLocContext<'a, C: ParseCtx + ?Sized> {
|
||||||
sub: &'a C,
|
sub: &'a C,
|
||||||
range: &'a SourceRange,
|
range: &'a SourceRange,
|
||||||
@@ -115,13 +131,33 @@ impl<'a, C: ParseCtx + ?Sized> FlatLocContext<'a, C> {
|
|||||||
pub fn new(sub: &'a C, range: &'a SourceRange) -> Self { Self { sub, range } }
|
pub fn new(sub: &'a C, range: &'a SourceRange) -> Self { Self { sub, range } }
|
||||||
}
|
}
|
||||||
impl<'a, C: ParseCtx + ?Sized> ParseCtx for FlatLocContext<'a, C> {
|
impl<'a, C: ParseCtx + ?Sized> ParseCtx for FlatLocContext<'a, C> {
|
||||||
|
fn reporter(&self) -> &Reporter { self.sub.reporter() }
|
||||||
fn pos(&self, _: &str) -> usize { 0 }
|
fn pos(&self, _: &str) -> usize { 0 }
|
||||||
fn lexers(&self) -> BoxedIter<'_, &dyn LexerPlugin> { self.sub.lexers() }
|
fn lexers(&self) -> BoxedIter<'_, &dyn LexerPlugin> { self.sub.lexers() }
|
||||||
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin> {
|
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin> { self.sub.line_parsers() }
|
||||||
self.sub.line_parsers()
|
|
||||||
}
|
|
||||||
fn code_info(&self) -> SourceCode { self.range.code.clone() }
|
fn code_info(&self) -> SourceCode { self.range.code.clone() }
|
||||||
fn range(&self, _: usize, _: &str) -> Range<usize> {
|
fn range(&self, _: usize, _: &str) -> Range<usize> { self.range.range.clone() }
|
||||||
self.range.range.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Context that forwards everything to a wrapped context except for error
|
||||||
|
/// reporting. See also [FlatLocContext]
|
||||||
|
pub struct ReporterContext<'a, C: ParseCtx + ?Sized> {
|
||||||
|
sub: &'a C,
|
||||||
|
reporter: &'a Reporter,
|
||||||
|
}
|
||||||
|
impl<'a, C: ParseCtx + ?Sized> ReporterContext<'a, C> {
|
||||||
|
/// Create a new context that will collect errors separately and forward
|
||||||
|
/// everything else to an enclosed context
|
||||||
|
pub fn new(sub: &'a C, reporter: &'a Reporter) -> Self { Self { sub, reporter } }
|
||||||
|
}
|
||||||
|
impl<'a, C: ParseCtx + ?Sized> ParseCtx for ReporterContext<'a, C> {
|
||||||
|
fn reporter(&self) -> &Reporter { self.reporter }
|
||||||
|
fn pos(&self, tail: &str) -> usize { self.sub.pos(tail) }
|
||||||
|
fn lexers(&self) -> BoxedIter<'_, &dyn LexerPlugin> { self.sub.lexers() }
|
||||||
|
fn line_parsers(&self) -> BoxedIter<'_, &dyn ParseLinePlugin> { self.sub.line_parsers() }
|
||||||
|
fn code_info(&self) -> SourceCode { self.sub.code_info() }
|
||||||
|
fn range(&self, len: usize, tl: &str) -> Range<usize> { self.sub.range(len, tl) }
|
||||||
|
fn range_loc(&self, range: &Range<usize>) -> SourceRange { self.sub.range_loc(range) }
|
||||||
|
fn source(&self) -> Arc<String> { self.sub.source() }
|
||||||
|
fn source_range(&self, len: usize, tl: &str) -> SourceRange { self.sub.source_range(len, tl) }
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user