STL rework
- fixed lots of bugs - overlay libraries work correctly and reliably - the STL is an overlay library - examples updated
This commit is contained in:
225
Cargo.lock
generated
225
Cargo.lock
generated
@@ -24,6 +24,15 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@@ -85,6 +94,25 @@ 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 = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a246e68bb43f6cd9db24bea052a53e40405417c5fb372e3d1a8a7f770a564ef5"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.79"
|
version = "1.0.79"
|
||||||
@@ -109,9 +137,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.2.7"
|
version = "4.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938"
|
checksum = "80672091db20273a15cf9fdd4e47ed43b5091ec9841bf4c6145c9dfbbcae09ed"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -120,9 +148,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.2.7"
|
version = "4.3.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd"
|
checksum = "c1458a1df40e1e2afebb7ab60ce55c1fa8f431146205aa5f4887e0b111c27636"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -133,9 +161,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.2.0"
|
version = "4.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
|
checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -145,9 +173,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.4.1"
|
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 = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
|
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
@@ -155,6 +183,35 @@ 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 = "cpufeatures"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dyn-clone"
|
name = "dyn-clone"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
@@ -188,6 +245,22 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
@@ -199,6 +272,19 @@ dependencies = [
|
|||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "globset"
|
||||||
|
version = "0.4.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"bstr",
|
||||||
|
"fnv",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@@ -273,6 +359,21 @@ version = "0.3.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
|
checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
@@ -299,15 +400,16 @@ dependencies = [
|
|||||||
"itertools",
|
"itertools",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"paste",
|
"paste",
|
||||||
|
"rust-embed",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"trait-set",
|
"trait-set",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-float"
|
name = "ordered-float"
|
||||||
version = "3.6.0"
|
version = "3.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13a384337e997e6860ffbaa83708b2ef329fd8c54cb67a5f64d421e0f943254f"
|
checksum = "2fc2dbde8f8a79f2102cc474ceb0ad68e3b80b85289ea62389b60e66777e4213"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
@@ -345,6 +447,58 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed"
|
||||||
|
version = "6.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b68543d5527e158213414a92832d2aab11a84d2571a5eb021ebe22c43aab066"
|
||||||
|
dependencies = [
|
||||||
|
"rust-embed-impl",
|
||||||
|
"rust-embed-utils",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed-impl"
|
||||||
|
version = "6.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4d4e0f0ced47ded9a68374ac145edd65a6c1fa13a96447b873660b2a568a0fd7"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rust-embed-utils",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed-utils"
|
||||||
|
version = "7.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "512b0ab6853f7e14e3c8754acb43d6f748bb9ced66aa5915a6553ac8213f7731"
|
||||||
|
dependencies = [
|
||||||
|
"globset",
|
||||||
|
"sha2",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.37.19"
|
version = "0.37.19"
|
||||||
@@ -359,6 +513,32 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.160"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stacker"
|
name = "stacker"
|
||||||
version = "0.1.15"
|
version = "0.1.15"
|
||||||
@@ -431,6 +611,12 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
@@ -449,6 +635,16 @@ version = "0.9.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
@@ -471,6 +667,15 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|||||||
15
Cargo.toml
15
Cargo.toml
@@ -23,11 +23,12 @@ doc = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
chumsky = "0.9.2"
|
chumsky = "0.9"
|
||||||
hashbrown = "0.13.2"
|
hashbrown = "0.13"
|
||||||
ordered-float = "3.0"
|
ordered-float = "3.7"
|
||||||
itertools = "0.10"
|
itertools = "0.10"
|
||||||
dyn-clone = "1.0.11"
|
dyn-clone = "1.0"
|
||||||
clap = { version = "4.2.4", features = ["derive"] }
|
clap = { version = "4.3", features = ["derive"] }
|
||||||
trait-set = "0.3.0"
|
trait-set = "0.3"
|
||||||
paste = "1.0.12"
|
paste = "1.0"
|
||||||
|
rust-embed = { version = "6.6", features = ["include-exclude"] }
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import std::(parse_float, to_string)
|
import std::(proc::*, to_float, to_string, io::(readline, print))
|
||||||
import std::(readline, print)
|
|
||||||
|
|
||||||
export main := do{
|
export main := do{
|
||||||
cps print "left operand: ";
|
cps print "left operand: ";
|
||||||
cps data = readline;
|
cps data = readline;
|
||||||
let a = parse_float data;
|
let a = to_float data;
|
||||||
cps print "operator: ";
|
cps print "operator: ";
|
||||||
cps op = readline;
|
cps op = readline;
|
||||||
cps print ("you selected \"" ++ op ++ "\"\n");
|
cps print ("you selected \"" ++ op ++ "\"\n");
|
||||||
cps print "right operand: ";
|
cps print "right operand: ";
|
||||||
cps data = readline;
|
cps data = readline;
|
||||||
let b = parse_float data;
|
let b = to_float data;
|
||||||
let result = (
|
let result = (
|
||||||
if op == "+" then a + b
|
if op == "+" then a + b
|
||||||
else if op == "-" then a - b
|
else if op == "-" then a - b
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
import std::print
|
import std::io::print
|
||||||
|
|
||||||
main := print "Hello, world!\n" "goodbye"
|
main := print "Hello, world!\n" "goodbye"
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import option
|
|
||||||
import super::fn::*
|
|
||||||
|
|
||||||
pair := \a.\b. \f. f a b
|
|
||||||
|
|
||||||
-- Constructors
|
|
||||||
|
|
||||||
export cons := \hd.\tl. option::some (pair hd tl)
|
|
||||||
export end := option::none
|
|
||||||
|
|
||||||
export pop := \list.\default.\f.list default \cons.cons f
|
|
||||||
|
|
||||||
-- Operators
|
|
||||||
|
|
||||||
export reduce := \list.\acc.\f. (
|
|
||||||
loop r on (list acc) with
|
|
||||||
pop list acc \head.\tail. r tail (f acc head)
|
|
||||||
)
|
|
||||||
|
|
||||||
export map := \list.\f. (
|
|
||||||
loop r on (list) with
|
|
||||||
pop list end \head.\tail. cons (f head) (r tail)
|
|
||||||
)
|
|
||||||
|
|
||||||
export skip := \list.\n. (
|
|
||||||
loop r on (list n) with
|
|
||||||
if n == 0 then list
|
|
||||||
else pop list end \head.\tail. r tail (n - 1)
|
|
||||||
)
|
|
||||||
|
|
||||||
export take := \list.\n. (
|
|
||||||
loop r on (list n) with
|
|
||||||
if n == 0 then end
|
|
||||||
else pop list end \head.\tail. cons head $ r tail $ n - 1
|
|
||||||
)
|
|
||||||
|
|
||||||
new[...$item, ...$rest:1] =0x2p84=> (cons (...$item) new[...$rest])
|
|
||||||
new[...$end] =0x1p84=> (cons (...$end) end)
|
|
||||||
new[] =0x1p84=> end
|
|
||||||
|
|
||||||
export ::(new)
|
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
import std::(to_string, print)
|
import std::(proc::*, io::print, to_string)
|
||||||
import super::list
|
|
||||||
import fn::*
|
|
||||||
|
|
||||||
export main := do{
|
export main := do{
|
||||||
let foo = list::new[1, 2, 3, 4, 5, 6];
|
let foo = list::new[1, 2, 3, 4, 5, 6];
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
export some := \v. \d.\f. f v
|
|
||||||
export none := \d.\f. d
|
|
||||||
|
|
||||||
export map := \option.\f. option none f
|
|
||||||
export flatten := \option. option none \opt. opt
|
|
||||||
export flatmap := \option.\f. option none \opt. map opt f
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
export Y := \f.(\x.f (x x))(\x.f (x x))
|
|
||||||
|
|
||||||
export loop $r on (..$parameters) with ...$tail =0x5p129=> Y (\$r.
|
|
||||||
bind_names (..$parameters) (...$tail)
|
|
||||||
) ..$parameters
|
|
||||||
|
|
||||||
-- bind each of the names in the first argument as a parameter for the second argument
|
|
||||||
bind_names ($name ..$rest) $payload =0x1p250=> \$name. bind_names (..$rest) $payload
|
|
||||||
bind_names () (...$payload) =0x1p250=> ...$payload
|
|
||||||
|
|
||||||
export ...$prefix $ ...$suffix:1 =0x1p34=> ...$prefix (...$suffix)
|
|
||||||
export ...$prefix |> $fn ..$suffix:1 =0x2p32=> $fn (...$prefix) ..$suffix
|
|
||||||
|
|
||||||
export (...$argv) => ...$body =0x2p129=> (bind_names (...$argv) (...$body))
|
|
||||||
$name => ...$body =0x1p129=> (\$name. ...$body)
|
|
||||||
@@ -1,8 +1,4 @@
|
|||||||
import list
|
import std::(proc::*, io::print, to_string)
|
||||||
import map
|
|
||||||
import option
|
|
||||||
import fn::*
|
|
||||||
import std::(print, to_string)
|
|
||||||
|
|
||||||
export main := do{
|
export main := do{
|
||||||
let foo = map::new[
|
let foo = map::new[
|
||||||
|
|||||||
@@ -36,35 +36,13 @@
|
|||||||
},
|
},
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"tomoki1207.pdf",
|
|
||||||
"james-yu.latex-workshop",
|
|
||||||
"bungcip.better-toml",
|
"bungcip.better-toml",
|
||||||
"maptz.regionfolder",
|
"maptz.regionfolder",
|
||||||
"serayuzgur.crates",
|
"serayuzgur.crates",
|
||||||
"tamasfe.even-better-toml",
|
"tamasfe.even-better-toml",
|
||||||
"haskell.haskell",
|
|
||||||
"justusadam.language-haskell",
|
|
||||||
"yzhang.markdown-all-in-one",
|
"yzhang.markdown-all-in-one",
|
||||||
"goessner.mdmath",
|
"gruntfuggly.todo-tree",
|
||||||
"gruntfuggly.todo-tree"
|
"vadimcn.vscode-lldb"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"launch": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "lldb",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Cargo launch",
|
|
||||||
"cwd": "${workspaceFolder:orchid}",
|
|
||||||
"program": "${workspaceFolder}/target/debug/orchid",
|
|
||||||
"cargo": {
|
|
||||||
"args": [
|
|
||||||
"run",
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"args": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,6 @@ newline_style = "Unix"
|
|||||||
normalize_comments = true
|
normalize_comments = true
|
||||||
wrap_comments = true
|
wrap_comments = true
|
||||||
overflow_delimited_expr = true
|
overflow_delimited_expr = true
|
||||||
single_line_if_else_max_width = 50
|
|
||||||
use_small_heuristics = "Max"
|
use_small_heuristics = "Max"
|
||||||
|
|
||||||
# literals
|
# literals
|
||||||
|
|||||||
2
src/bin/cli/mod.rs
Normal file
2
src/bin/cli/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
mod prompt;
|
||||||
|
pub use prompt::cmd_prompt;
|
||||||
11
src/bin/cli/prompt.rs
Normal file
11
src/bin/cli/prompt.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
use std::io::{self, Error, Write};
|
||||||
|
|
||||||
|
pub fn cmd_prompt(prompt: &str) -> Result<(String, Vec<String>), Error> {
|
||||||
|
print!("{}", prompt);
|
||||||
|
io::stdout().flush()?;
|
||||||
|
let mut cmdln = String::new();
|
||||||
|
io::stdin().read_line(&mut cmdln)?;
|
||||||
|
let mut segments = cmdln.split(' ');
|
||||||
|
let cmd = if let Some(cmd) = segments.next() { cmd } else { "" };
|
||||||
|
Ok((cmd.to_string(), segments.map(str::to_string).collect()))
|
||||||
|
}
|
||||||
259
src/bin/main.rs
259
src/bin/main.rs
@@ -1,127 +1,129 @@
|
|||||||
use std::borrow::Borrow;
|
mod cli;
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
use std::process;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use orchidlang::interner::{InternedDisplay, Interner, Sym};
|
use orchidlang::interner::{InternedDisplay, Interner, Sym};
|
||||||
use orchidlang::pipeline::file_loader::{mk_cache, Loaded};
|
use orchidlang::{ast, ast_to_interpreted, interpreter, pipeline, rule, stl};
|
||||||
use orchidlang::pipeline::{
|
|
||||||
collect_consts, collect_rules, from_const_tree, parse_layer, ProjectTree,
|
use crate::cli::cmd_prompt;
|
||||||
};
|
|
||||||
use orchidlang::rule::Repo;
|
|
||||||
use orchidlang::sourcefile::{FileEntry, Import};
|
|
||||||
use orchidlang::{ast_to_interpreted, interpreter, stl};
|
|
||||||
|
|
||||||
/// Orchid interpreter
|
/// Orchid interpreter
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about, long_about = None)]
|
||||||
struct Args {
|
struct Args {
|
||||||
/// Folder containing main.orc
|
/// Folder containing main.orc or the manually specified entry module
|
||||||
#[arg(short, long, default_value = ".")]
|
#[arg(short, long, default_value = ".")]
|
||||||
pub project: String,
|
pub dir: String,
|
||||||
|
/// Entrypoint for the interpreter
|
||||||
|
#[arg(short, long, default_value = "main::main")]
|
||||||
|
pub main: String,
|
||||||
|
/// Maximum number of steps taken by the macro executor
|
||||||
|
#[arg(long, default_value_t = 10_000)]
|
||||||
|
pub macro_limit: usize,
|
||||||
|
/// Print the parsed ruleset and exit
|
||||||
|
#[arg(long)]
|
||||||
|
pub dump_repo: bool,
|
||||||
|
/// Step through the macro execution process in the specified symbol
|
||||||
|
#[arg(long, default_value = "")]
|
||||||
|
pub macro_debug: String,
|
||||||
}
|
}
|
||||||
impl Args {
|
impl Args {
|
||||||
pub fn chk_proj(&self) -> Result<(), String> {
|
/// Validate the project directory and the
|
||||||
let mut path = PathBuf::from(&self.project);
|
pub fn chk_dir_main(&self) -> Result<(), String> {
|
||||||
path.push(PathBuf::from("main.orc"));
|
let dir_path = PathBuf::from(&self.dir);
|
||||||
if File::open(&path).is_ok() {
|
if !dir_path.is_dir() {
|
||||||
|
return Err(format!("{} is not a directory", dir_path.display()));
|
||||||
|
}
|
||||||
|
let segs = self.main.split("::").collect::<Vec<_>>();
|
||||||
|
if segs.len() < 2 {
|
||||||
|
return Err("Entry point too short".to_string());
|
||||||
|
}
|
||||||
|
let (pathsegs, _) = segs.split_at(segs.len() - 1);
|
||||||
|
let mut possible_files = pathsegs.iter().scan(dir_path, |path, seg| {
|
||||||
|
path.push(seg);
|
||||||
|
Some(path.with_extension("orc"))
|
||||||
|
});
|
||||||
|
if possible_files.all(|p| File::open(p).is_err()) {
|
||||||
|
return Err(format!(
|
||||||
|
"{} not found in {}",
|
||||||
|
pathsegs.join("::"),
|
||||||
|
PathBuf::from(&self.dir).display()
|
||||||
|
));
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
|
||||||
Err(format!("{} not found", path.display()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn chk_proj(&self) -> Result<(), String> {
|
||||||
|
self.chk_dir_main()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
/// Load and parse all source related to the symbol `target` or all symbols
|
||||||
let args = Args::parse();
|
/// in the namespace `target` in the context of the STL. All sourcefiles must
|
||||||
args.chk_proj().unwrap_or_else(|e| panic!("{e}"));
|
/// reside within `dir`.
|
||||||
run_dir(PathBuf::try_from(args.project).unwrap().borrow());
|
fn load_dir(dir: &Path, target: Sym, i: &Interner) -> pipeline::ProjectTree {
|
||||||
}
|
let file_cache = pipeline::file_loader::mk_dir_cache(dir.to_path_buf(), i);
|
||||||
|
let library = stl::mk_stl(i, stl::StlOptions::default());
|
||||||
static PRELUDE_TXT: &str = r#"
|
pipeline::parse_layer(
|
||||||
import std::(
|
&[target],
|
||||||
add, subtract, multiply, remainder, divide,
|
&|path| file_cache.find(&path),
|
||||||
equals, ifthenelse,
|
&library,
|
||||||
concatenate
|
&stl::mk_prelude(i),
|
||||||
)
|
|
||||||
|
|
||||||
export ...$a + ...$b =0x2p36=> (add (...$a) (...$b))
|
|
||||||
export ...$a - ...$b:1 =0x2p36=> (subtract (...$a) (...$b))
|
|
||||||
export ...$a * ...$b =0x1p36=> (multiply (...$a) (...$b))
|
|
||||||
export ...$a % ...$b:1 =0x1p36=> (remainder (...$a) (...$b))
|
|
||||||
export ...$a / ...$b:1 =0x1p36=> (divide (...$a) (...$b))
|
|
||||||
export ...$a == ...$b =0x3p36=> (equals (...$a) (...$b))
|
|
||||||
export ...$a ++ ...$b =0x4p36=> (concatenate (...$a) (...$b))
|
|
||||||
|
|
||||||
export do { ...$statement ; ...$rest:1 } =0x2p130=> statement (...$statement) do { ...$rest }
|
|
||||||
export do { ...$return } =0x1p130=> ...$return
|
|
||||||
|
|
||||||
export statement (let $name = ...$value) ...$next =0x1p230=> (
|
|
||||||
(\$name. ...$next) (...$value)
|
|
||||||
)
|
|
||||||
export statement (cps $name = ...$operation) ...$next =0x2p230=> (
|
|
||||||
(...$operation) \$name. ...$next
|
|
||||||
)
|
|
||||||
export statement (cps ...$operation) ...$next =0x1p230=> (
|
|
||||||
(...$operation) (...$next)
|
|
||||||
)
|
|
||||||
|
|
||||||
export if ...$cond then ...$true else ...$false:1 =0x1p84=> (
|
|
||||||
ifthenelse (...$cond) (...$true) (...$false)
|
|
||||||
)
|
|
||||||
|
|
||||||
export ::(,)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
fn prelude_path(i: &Interner) -> Sym {
|
|
||||||
i.i(&[i.i("prelude")][..])
|
|
||||||
}
|
|
||||||
fn mainmod_path(i: &Interner) -> Sym {
|
|
||||||
i.i(&[i.i("main")][..])
|
|
||||||
}
|
|
||||||
fn entrypoint(i: &Interner) -> Sym {
|
|
||||||
i.i(&[i.i("main"), i.i("main")][..])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_environment(i: &Interner) -> ProjectTree {
|
|
||||||
let env = from_const_tree(
|
|
||||||
HashMap::from([(i.i("std"), stl::mk_stl(i))]),
|
|
||||||
&[i.i("std")],
|
|
||||||
i,
|
i,
|
||||||
);
|
|
||||||
let loader = |path: Sym| {
|
|
||||||
if path == prelude_path(i) {
|
|
||||||
Ok(Loaded::Code(Rc::new(PRELUDE_TXT.to_string())))
|
|
||||||
} else {
|
|
||||||
panic!(
|
|
||||||
"Prelude pointed to non-std path {}",
|
|
||||||
i.extern_vec(path).join("::")
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
};
|
|
||||||
parse_layer(&[prelude_path(i)], &loader, &env, &[], i).expect("prelude error")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_dir(i: &Interner, dir: &Path) -> ProjectTree {
|
|
||||||
let environment = load_environment(i);
|
|
||||||
let file_cache = mk_cache(dir.to_path_buf(), i);
|
|
||||||
let loader = |path| file_cache.find(&path);
|
|
||||||
let prelude =
|
|
||||||
[FileEntry::Import(vec![Import { path: prelude_path(i), name: None }])];
|
|
||||||
parse_layer(&[mainmod_path(i)], &loader, &environment, &prelude, i)
|
|
||||||
.expect("Failed to load source code")
|
.expect("Failed to load source code")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_dir(dir: &Path) {
|
pub fn to_sym(data: &str, i: &Interner) -> Sym {
|
||||||
|
i.i(&data.split("::").map(|s| i.i(s)).collect::<Vec<_>>()[..])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A little utility to step through the resolution of a macro set
|
||||||
|
pub fn macro_debug(repo: rule::Repo, mut code: ast::Expr, i: &Interner) {
|
||||||
|
let mut idx = 0;
|
||||||
|
println!("Macro debugger working on {}", code.bundle(i));
|
||||||
|
loop {
|
||||||
|
let (cmd, _) = cmd_prompt("cmd> ").unwrap();
|
||||||
|
match cmd.trim() {
|
||||||
|
"" | "n" | "next" =>
|
||||||
|
if let Some(c) = repo.step(&code) {
|
||||||
|
idx += 1;
|
||||||
|
code = c;
|
||||||
|
println!("Step {idx}: {}", code.bundle(i));
|
||||||
|
},
|
||||||
|
"p" | "print" => println!("Step {idx}: {}", code.bundle(i)),
|
||||||
|
"d" | "dump" => println!("Rules: {}", repo.bundle(i)),
|
||||||
|
"q" | "quit" => return,
|
||||||
|
"h" | "help" => println!(
|
||||||
|
"Available commands:
|
||||||
|
\t<blank>, n, next\t\ttake a step
|
||||||
|
\tp, print\t\tprint the current state
|
||||||
|
\tq, quit\t\texit
|
||||||
|
\th, help\t\tprint this text"
|
||||||
|
),
|
||||||
|
_ => {
|
||||||
|
println!("unrecognized command \"{}\", try \"help\"", cmd);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let args = Args::parse();
|
||||||
|
args.chk_proj().unwrap_or_else(|e| panic!("{e}"));
|
||||||
|
let dir = PathBuf::try_from(args.dir).unwrap();
|
||||||
let i = Interner::new();
|
let i = Interner::new();
|
||||||
let project = load_dir(&i, dir);
|
let main = to_sym(&args.main, &i);
|
||||||
let rules = collect_rules(&project);
|
let project = load_dir(&dir, main, &i);
|
||||||
let consts = collect_consts(&project, &i);
|
let rules = pipeline::collect_rules(&project);
|
||||||
println!("Initializing rule repository with {} rules", rules.len());
|
let consts = pipeline::collect_consts(&project, &i);
|
||||||
let repo = Repo::new(rules, &i).unwrap_or_else(|(rule, error)| {
|
let repo = rule::Repo::new(rules, &i).unwrap_or_else(|(rule, error)| {
|
||||||
panic!(
|
panic!(
|
||||||
"Rule error: {}
|
"Rule error: {}
|
||||||
Offending rule: {}",
|
Offending rule: {}",
|
||||||
@@ -129,42 +131,36 @@ pub fn run_dir(dir: &Path) {
|
|||||||
rule.bundle(&i)
|
rule.bundle(&i)
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
println!("Repo dump: {}", repo.bundle(&i));
|
if args.dump_repo {
|
||||||
|
println!("Parsed rules: {}", repo.bundle(&i));
|
||||||
|
return;
|
||||||
|
} else if !args.macro_debug.is_empty() {
|
||||||
|
let name = to_sym(&args.macro_debug, &i);
|
||||||
|
let code = consts
|
||||||
|
.get(&name)
|
||||||
|
.unwrap_or_else(|| panic!("Constant {} not found", args.macro_debug));
|
||||||
|
return macro_debug(repo, code.clone(), &i);
|
||||||
|
}
|
||||||
let mut exec_table = HashMap::new();
|
let mut exec_table = HashMap::new();
|
||||||
for (name, source) in consts.iter() {
|
for (name, source) in consts.iter() {
|
||||||
// let nval = entrypoint(&i); let name = &nval; let source = &consts[name];
|
|
||||||
let mut tree = source.clone();
|
|
||||||
let displayname = i.extern_vec(*name).join("::");
|
let displayname = i.extern_vec(*name).join("::");
|
||||||
let macro_timeout = 100;
|
let (unmatched, steps_left) = repo.long_step(source, args.macro_limit + 1);
|
||||||
println!("Executing macros in {displayname}...",);
|
assert!(steps_left > 0, "Macro execution in {displayname} did not halt");
|
||||||
let mut idx = 0;
|
let runtree = ast_to_interpreted(&unmatched).unwrap_or_else(|e| {
|
||||||
let unmatched = loop {
|
panic!("Postmacro conversion error in {displayname}: {e}")
|
||||||
if idx == macro_timeout {
|
});
|
||||||
panic!("Macro execution in {displayname} didn't halt")
|
|
||||||
}
|
|
||||||
match repo.step(&tree) {
|
|
||||||
None => break tree,
|
|
||||||
Some(phase) => {
|
|
||||||
println!("Step {idx}/{macro_timeout}: {}", phase.bundle(&i));
|
|
||||||
tree = phase;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
idx += 1;
|
|
||||||
};
|
|
||||||
let runtree = ast_to_interpreted(&unmatched)
|
|
||||||
.unwrap_or_else(|e| panic!("Postmacro conversion error: {e}"));
|
|
||||||
exec_table.insert(*name, runtree);
|
exec_table.insert(*name, runtree);
|
||||||
}
|
}
|
||||||
println!("macro execution complete");
|
|
||||||
let ctx =
|
let ctx =
|
||||||
interpreter::Context { symbols: &exec_table, interner: &i, gas: None };
|
interpreter::Context { symbols: &exec_table, interner: &i, gas: None };
|
||||||
let entrypoint = exec_table.get(&entrypoint(&i)).unwrap_or_else(|| {
|
let entrypoint = exec_table.get(&main).unwrap_or_else(|| {
|
||||||
|
let main = args.main;
|
||||||
|
let symbols =
|
||||||
|
exec_table.keys().map(|t| i.extern_vec(*t).join("::")).join(", ");
|
||||||
panic!(
|
panic!(
|
||||||
"entrypoint not found, known keys are: {}",
|
"Entrypoint not found!
|
||||||
exec_table
|
Entrypoint was {main}
|
||||||
.keys()
|
known keys are {symbols}"
|
||||||
.map(|t| i.r(*t).iter().map(|t| i.r(*t)).join("::"))
|
|
||||||
.join(", ")
|
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let io_handler = orchidlang::stl::handleIO;
|
let io_handler = orchidlang::stl::handleIO;
|
||||||
@@ -173,12 +169,11 @@ pub fn run_dir(dir: &Path) {
|
|||||||
ret.unwrap_or_else(|e| panic!("Runtime error: {}", e));
|
ret.unwrap_or_else(|e| panic!("Runtime error: {}", e));
|
||||||
if inert {
|
if inert {
|
||||||
println!("Settled at {}", state.expr().clause.bundle(&i));
|
println!("Settled at {}", state.expr().clause.bundle(&i));
|
||||||
println!(
|
if let Some(g) = gas {
|
||||||
"Remaining gas: {}",
|
println!("Remaining gas: {g}")
|
||||||
gas.map(|g| g.to_string()).unwrap_or(String::from("∞"))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if gas == Some(0) {
|
} else if gas == Some(0) {
|
||||||
println!("Ran out of gas!")
|
eprintln!("Ran out of gas!");
|
||||||
|
process::exit(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,21 @@ use std::fmt::Debug;
|
|||||||
#[allow(unused)] // for the doc comments
|
#[allow(unused)] // for the doc comments
|
||||||
use dyn_clone::DynClone;
|
use dyn_clone::DynClone;
|
||||||
|
|
||||||
|
#[allow(unused)] // for the doc comments
|
||||||
|
use crate::define_fn;
|
||||||
#[allow(unused)] // for the doc comments
|
#[allow(unused)] // for the doc comments
|
||||||
use crate::foreign::{Atomic, ExternFn};
|
use crate::foreign::{Atomic, ExternFn};
|
||||||
#[allow(unused)] // for the doc comments
|
#[allow(unused)] // for the doc comments
|
||||||
|
use crate::write_fn_step;
|
||||||
|
#[allow(unused)] // for the doc comments
|
||||||
use crate::Primitive;
|
use crate::Primitive;
|
||||||
|
|
||||||
/// A macro that generates implementations of [Atomic] to simplify the
|
/// A macro that generates implementations of [Atomic] to simplify the
|
||||||
/// development of external bindings for Orchid.
|
/// development of external bindings for Orchid.
|
||||||
///
|
///
|
||||||
|
/// Most use cases are fulfilled by [define_fn], pathological cases can combine
|
||||||
|
/// [write_fn_step] with manual [Atomic] implementations.
|
||||||
|
///
|
||||||
/// The macro depends on implementations of [`AsRef<Clause>`] and
|
/// The macro depends on implementations of [`AsRef<Clause>`] and
|
||||||
/// [`From<(&Self, Clause)>`] for extracting the clause to be processed and then
|
/// [`From<(&Self, Clause)>`] for extracting the clause to be processed and then
|
||||||
/// reconstructing the [Atomic]. Naturally, supertraits of [Atomic] are also
|
/// reconstructing the [Atomic]. Naturally, supertraits of [Atomic] are also
|
||||||
@@ -32,34 +39,35 @@ use crate::Primitive;
|
|||||||
///
|
///
|
||||||
/// _definition of the `add` function in the STL_
|
/// _definition of the `add` function in the STL_
|
||||||
/// ```
|
/// ```
|
||||||
|
/// use orchidlang::{Literal};
|
||||||
/// use orchidlang::interpreted::ExprInst;
|
/// use orchidlang::interpreted::ExprInst;
|
||||||
/// use orchidlang::stl::Numeric;
|
/// use orchidlang::stl::litconv::with_lit;
|
||||||
/// use orchidlang::{atomic_impl, atomic_redirect, externfn_impl};
|
/// use orchidlang::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
///
|
///
|
||||||
|
/// /// Convert a literal to a string using Rust's conversions for floats, chars and
|
||||||
|
/// /// uints respectively
|
||||||
/// #[derive(Clone)]
|
/// #[derive(Clone)]
|
||||||
/// pub struct Add2;
|
/// struct ToString;
|
||||||
/// externfn_impl!(Add2, |_: &Self, x: ExprInst| Ok(Add1 { x }));
|
|
||||||
///
|
///
|
||||||
/// #[derive(Debug, Clone)]
|
/// externfn_impl!{
|
||||||
/// pub struct Add1 {
|
/// ToString, |_: &Self, expr_inst: ExprInst|{
|
||||||
/// x: ExprInst,
|
/// Ok(InternalToString {
|
||||||
|
/// expr_inst
|
||||||
|
/// })
|
||||||
/// }
|
/// }
|
||||||
/// atomic_redirect!(Add1, x);
|
|
||||||
/// atomic_impl!(Add1);
|
|
||||||
/// externfn_impl!(Add1, |this: &Self, x: ExprInst| {
|
|
||||||
/// let a: Numeric = this.x.clone().try_into()?;
|
|
||||||
/// Ok(Add0 { a, x })
|
|
||||||
/// });
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Clone)]
|
|
||||||
/// pub struct Add0 {
|
|
||||||
/// a: Numeric,
|
|
||||||
/// x: ExprInst,
|
|
||||||
/// }
|
/// }
|
||||||
/// atomic_redirect!(Add0, x);
|
/// #[derive(std::fmt::Debug,Clone)]
|
||||||
/// atomic_impl!(Add0, |Self { a, x }: &Self, _| {
|
/// struct InternalToString {
|
||||||
/// let b: Numeric = x.clone().try_into()?;
|
/// expr_inst: ExprInst,
|
||||||
/// Ok((*a + b).into())
|
/// }
|
||||||
|
/// atomic_redirect!(InternalToString, expr_inst);
|
||||||
|
/// atomic_impl!(InternalToString, |Self { expr_inst }: &Self, _|{
|
||||||
|
/// with_lit(expr_inst, |l| Ok(match l {
|
||||||
|
/// Literal::Char(c) => c.to_string(),
|
||||||
|
/// Literal::Uint(i) => i.to_string(),
|
||||||
|
/// Literal::Num(n) => n.to_string(),
|
||||||
|
/// Literal::Str(s) => s.clone(),
|
||||||
|
/// })).map(|s| Literal::Str(s).into())
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
@@ -92,7 +100,11 @@ macro_rules! atomic_impl {
|
|||||||
// branch off or wrap up
|
// branch off or wrap up
|
||||||
let clause = if inert {
|
let clause = if inert {
|
||||||
let closure = $next_phase;
|
let closure = $next_phase;
|
||||||
match closure(&next_self, ctx) {
|
let res: Result<
|
||||||
|
$crate::interpreted::Clause,
|
||||||
|
std::rc::Rc<dyn $crate::foreign::ExternError>,
|
||||||
|
> = closure(&next_self, ctx);
|
||||||
|
match res {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => return Err($crate::interpreter::RuntimeError::Extern(e)),
|
Err(e) => return Err($crate::interpreter::RuntimeError::Extern(e)),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,15 @@ use crate::write_fn_step;
|
|||||||
/// defined in the first step and returns a [Result] of the success type or
|
/// defined in the first step and returns a [Result] of the success type or
|
||||||
/// `Rc<dyn ExternError>`.
|
/// `Rc<dyn ExternError>`.
|
||||||
///
|
///
|
||||||
|
/// To avoid typing the same expression a lot, the conversion is optional.
|
||||||
|
/// If it is omitted, the field is initialized with a [TryInto::try_into] call
|
||||||
|
/// from `&ExprInst` to the target type. In this case, the error is
|
||||||
|
/// short-circuited using `?` so conversions through `FromResidual` are allowed.
|
||||||
|
/// The optional syntax starts with `as`.
|
||||||
|
///
|
||||||
|
/// If all conversions are omitted, the alias definition (`expr=$ident in`) has
|
||||||
|
/// no effect and is therefore optional.
|
||||||
|
///
|
||||||
/// Finally, the body of the function is provided as an expression which can
|
/// Finally, the body of the function is provided as an expression which can
|
||||||
/// reference all of the arguments by their names, each bound to a ref of the
|
/// reference all of the arguments by their names, each bound to a ref of the
|
||||||
/// specified type.
|
/// specified type.
|
||||||
@@ -45,13 +54,59 @@ use crate::write_fn_step;
|
|||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// A simpler format is also offered for unary functions:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use orchidlang::stl::litconv::with_lit;
|
||||||
|
/// use orchidlang::{define_fn, Literal};
|
||||||
|
///
|
||||||
|
/// define_fn! {
|
||||||
|
/// /// Convert a literal to a string using Rust's conversions for floats,
|
||||||
|
/// /// chars and uints respectively
|
||||||
|
/// ToString = |x| with_lit(x, |l| Ok(match l {
|
||||||
|
/// Literal::Char(c) => c.to_string(),
|
||||||
|
/// Literal::Uint(i) => i.to_string(),
|
||||||
|
/// Literal::Num(n) => n.to_string(),
|
||||||
|
/// Literal::Str(s) => s.clone(),
|
||||||
|
/// })).map(|s| Literal::Str(s).into())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! define_fn {
|
macro_rules! define_fn {
|
||||||
|
// Unary function entry
|
||||||
|
($( #[ $attr:meta ] )* $qual:vis $name:ident = $body:expr) => {paste::paste!{
|
||||||
|
$crate::write_fn_step!(
|
||||||
|
$( #[ $attr ] )* $qual $name
|
||||||
|
>
|
||||||
|
[< Internal $name >]
|
||||||
|
);
|
||||||
|
$crate::write_fn_step!(
|
||||||
|
[< Internal $name >]
|
||||||
|
{}
|
||||||
|
out = expr => Ok(expr);
|
||||||
|
{
|
||||||
|
let lambda = $body;
|
||||||
|
lambda(out)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}};
|
||||||
|
// xname is optional only if every conversion is implicit
|
||||||
|
($( #[ $attr:meta ] )* $qual:vis $name:ident {
|
||||||
|
$( $arg:ident: $typ:ty ),+
|
||||||
|
} => $body:expr) => {
|
||||||
|
$crate::define_fn!{expr=expr in
|
||||||
|
$( #[ $attr ] )* $qual $name {
|
||||||
|
$( $arg: $typ ),*
|
||||||
|
} => $body
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// multi-parameter function entry
|
||||||
(expr=$xname:ident in
|
(expr=$xname:ident in
|
||||||
$( #[ $attr:meta ] )*
|
$( #[ $attr:meta ] )*
|
||||||
$qual:vis $name:ident {
|
$qual:vis $name:ident {
|
||||||
$arg0:ident: $typ0:ty as $parse0:expr
|
$arg0:ident: $typ0:ty $( as $parse0:expr )?
|
||||||
$(, $arg:ident: $typ:ty as $parse:expr )*
|
$(, $arg:ident: $typ:ty $( as $parse:expr )? )*
|
||||||
} => $body:expr
|
} => $body:expr
|
||||||
) => {paste::paste!{
|
) => {paste::paste!{
|
||||||
// Generate initial state
|
// Generate initial state
|
||||||
@@ -64,8 +119,10 @@ macro_rules! define_fn {
|
|||||||
$crate::define_fn!(@MIDDLE $xname [< Internal $name >] ($body)
|
$crate::define_fn!(@MIDDLE $xname [< Internal $name >] ($body)
|
||||||
()
|
()
|
||||||
(
|
(
|
||||||
($arg0: $typ0 as $parse0)
|
( $arg0: $typ0 $( as $parse0)? )
|
||||||
$( ($arg: $typ as $parse) )*
|
$(
|
||||||
|
( $arg: $typ $( as $parse)? )
|
||||||
|
)*
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}};
|
}};
|
||||||
@@ -80,10 +137,10 @@ macro_rules! define_fn {
|
|||||||
// later fields
|
// later fields
|
||||||
(
|
(
|
||||||
// field that should be processed by this step
|
// field that should be processed by this step
|
||||||
( $arg0:ident: $typ0:ty as $parse0:expr )
|
( $arg0:ident: $typ0:ty $( as $parse0:expr )? )
|
||||||
// ensure that we have a next stage
|
// ensure that we have a next stage
|
||||||
$(
|
$(
|
||||||
( $arg:ident: $typ:ty as $parse:expr )
|
( $arg:ident: $typ:ty $( as $parse:expr )? )
|
||||||
)+
|
)+
|
||||||
)
|
)
|
||||||
) => {paste::paste!{
|
) => {paste::paste!{
|
||||||
@@ -93,7 +150,7 @@ macro_rules! define_fn {
|
|||||||
$( $arg_prev:ident : $typ_prev:ty ),*
|
$( $arg_prev:ident : $typ_prev:ty ),*
|
||||||
}
|
}
|
||||||
[< $name $arg0:upper >]
|
[< $name $arg0:upper >]
|
||||||
where $arg0:$typ0 = $xname => $parse0;
|
where $arg0:$typ0 $( = $xname => $parse0 )? ;
|
||||||
);
|
);
|
||||||
$crate::define_fn!(@MIDDLE $xname [< $name $arg0:upper >] ($body)
|
$crate::define_fn!(@MIDDLE $xname [< $name $arg0:upper >] ($body)
|
||||||
(
|
(
|
||||||
@@ -101,7 +158,9 @@ macro_rules! define_fn {
|
|||||||
($arg0: $typ0)
|
($arg0: $typ0)
|
||||||
)
|
)
|
||||||
(
|
(
|
||||||
$( ($arg: $typ as $parse) )+
|
$(
|
||||||
|
( $arg: $typ $( as $parse)? )
|
||||||
|
)+
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}};
|
}};
|
||||||
@@ -113,7 +172,7 @@ macro_rules! define_fn {
|
|||||||
)
|
)
|
||||||
// the last one is initialized before the body runs
|
// the last one is initialized before the body runs
|
||||||
(
|
(
|
||||||
($arg0:ident: $typ0:ty as $parse0:expr)
|
($arg0:ident: $typ0:ty $( as $parse0:expr )? )
|
||||||
)
|
)
|
||||||
) => {
|
) => {
|
||||||
$crate::write_fn_step!(
|
$crate::write_fn_step!(
|
||||||
@@ -121,7 +180,7 @@ macro_rules! define_fn {
|
|||||||
{
|
{
|
||||||
$( $arg_prev: $typ_prev ),*
|
$( $arg_prev: $typ_prev ),*
|
||||||
}
|
}
|
||||||
$arg0:$typ0 = $xname => $parse0;
|
$arg0:$typ0 $( = $xname => $parse0 )? ;
|
||||||
$body
|
$body
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
#[allow(unused)] // for doc
|
#[allow(unused)] // for doc
|
||||||
use crate::define_fn;
|
use crate::define_fn;
|
||||||
#[allow(unused)] // for doc
|
#[allow(unused)] // for doc
|
||||||
|
use crate::foreign::Atomic;
|
||||||
|
#[allow(unused)] // for doc
|
||||||
use crate::foreign::ExternFn;
|
use crate::foreign::ExternFn;
|
||||||
#[allow(unused)] // for doc
|
#[allow(unused)] // for doc
|
||||||
use crate::interpreted::ExprInst;
|
use crate::interpreted::ExprInst;
|
||||||
|
|
||||||
/// Write one step in the state machine representing a simple n-ary non-variadic
|
/// Write one step in the state machine representing a simple n-ary non-variadic
|
||||||
/// Orchid function. There are no known use cases for it that aren't expressed
|
/// Orchid function. Most use cases are better covered by [define_fn] which
|
||||||
/// better with [define_fn] which generates calls to this macro.
|
/// generates calls to this macro. This macro can be used in combination with
|
||||||
|
/// manual [Atomic] implementations to define a function that only behaves like
|
||||||
|
/// a simple n-ary non-variadic function with respect to some of its arguments.
|
||||||
///
|
///
|
||||||
/// There are three ways to call this macro for the initial state, internal
|
/// There are three ways to call this macro for the initial state, internal
|
||||||
/// state, and exit state. All of them are demonstrated in one example and
|
/// state, and exit state. All of them are demonstrated in one example and
|
||||||
@@ -25,14 +29,14 @@ use crate::interpreted::ExprInst;
|
|||||||
/// // Middle state
|
/// // Middle state
|
||||||
/// write_fn_step!(
|
/// write_fn_step!(
|
||||||
/// CharAt1 {}
|
/// CharAt1 {}
|
||||||
/// CharAt0 where s = x => with_str(x, |s| Ok(s.clone()));
|
/// CharAt0 where s: String = x => with_str(x, |s| Ok(s.clone()));
|
||||||
/// );
|
/// );
|
||||||
/// // Exit state
|
/// // Exit state
|
||||||
/// write_fn_step!(
|
/// write_fn_step!(
|
||||||
/// CharAt0 { s: String }
|
/// CharAt0 { s: String }
|
||||||
/// i = x => with_uint(x, Ok);
|
/// i = x => with_uint(x, Ok);
|
||||||
/// {
|
/// {
|
||||||
/// if let Some(c) = s.chars().nth(i as usize) {
|
/// if let Some(c) = s.chars().nth(*i as usize) {
|
||||||
/// Ok(Clause::P(Primitive::Literal(Literal::Char(c))))
|
/// Ok(Clause::P(Primitive::Literal(Literal::Char(c))))
|
||||||
/// } else {
|
/// } else {
|
||||||
/// RuntimeError::fail(
|
/// RuntimeError::fail(
|
||||||
@@ -52,7 +56,7 @@ use crate::interpreted::ExprInst;
|
|||||||
/// struct definition. A field called `expr_inst` of type [ExprInst] is added
|
/// struct definition. A field called `expr_inst` of type [ExprInst] is added
|
||||||
/// implicitly, so the first middle state has an empty field list. The next
|
/// implicitly, so the first middle state has an empty field list. The next
|
||||||
/// state is also provided, alongside the name and conversion of the next
|
/// state is also provided, alongside the name and conversion of the next
|
||||||
/// parameter from a [&ExprInst] under the provided alias to a
|
/// parameter from a `&ExprInst` under the provided alias to a
|
||||||
/// `Result<_, Rc<dyn ExternError>>`. The success type is inferred from the
|
/// `Result<_, Rc<dyn ExternError>>`. The success type is inferred from the
|
||||||
/// type of the field at the place of its actual definition. This conversion is
|
/// type of the field at the place of its actual definition. This conversion is
|
||||||
/// done in the implementation of [ExternFn] which also places the new
|
/// done in the implementation of [ExternFn] which also places the new
|
||||||
@@ -67,6 +71,12 @@ use crate::interpreted::ExprInst;
|
|||||||
/// argument names bound. The arguments here are all references to their actual
|
/// argument names bound. The arguments here are all references to their actual
|
||||||
/// types except for the last one which is converted from [ExprInst] immediately
|
/// types except for the last one which is converted from [ExprInst] immediately
|
||||||
/// before the body is evaluated.
|
/// before the body is evaluated.
|
||||||
|
///
|
||||||
|
/// To avoid typing the same parsing process a lot, the conversion is optional.
|
||||||
|
/// If it is omitted, the field is initialized with a [TryInto::try_into] call
|
||||||
|
/// from `&ExprInst` to the target type. In this case, the error is
|
||||||
|
/// short-circuited using `?` so conversions through `FromResidual` are allowed.
|
||||||
|
/// The optional syntax starts with the `=` sign and ends before the semicolon.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! write_fn_step {
|
macro_rules! write_fn_step {
|
||||||
// write entry stage
|
// write entry stage
|
||||||
@@ -87,7 +97,7 @@ macro_rules! write_fn_step {
|
|||||||
$( $arg:ident : $typ:ty ),*
|
$( $arg:ident : $typ:ty ),*
|
||||||
}
|
}
|
||||||
$next:ident where
|
$next:ident where
|
||||||
$added:ident $( : $added_typ:ty )? = $xname:ident => $extract:expr ;
|
$added:ident $( : $added_typ:ty )? $( = $xname:ident => $extract:expr )? ;
|
||||||
) => {
|
) => {
|
||||||
$( #[ $attr ] )*
|
$( #[ $attr ] )*
|
||||||
#[derive(std::fmt::Debug, Clone)]
|
#[derive(std::fmt::Debug, Clone)]
|
||||||
@@ -100,8 +110,8 @@ macro_rules! write_fn_step {
|
|||||||
$crate::externfn_impl!(
|
$crate::externfn_impl!(
|
||||||
$name,
|
$name,
|
||||||
|this: &Self, expr_inst: $crate::interpreted::ExprInst| {
|
|this: &Self, expr_inst: $crate::interpreted::ExprInst| {
|
||||||
let $xname = &this.expr_inst;
|
let $added $( :$added_typ )? =
|
||||||
let $added $( :$added_typ )? = $extract?;
|
$crate::write_fn_step!(@CONV &this.expr_inst $(, $xname $extract )?);
|
||||||
Ok($next{
|
Ok($next{
|
||||||
$( $arg: this.$arg.clone(), )*
|
$( $arg: this.$arg.clone(), )*
|
||||||
$added, expr_inst
|
$added, expr_inst
|
||||||
@@ -114,23 +124,37 @@ macro_rules! write_fn_step {
|
|||||||
$( #[ $attr:meta ] )* $quant:vis $name:ident {
|
$( #[ $attr:meta ] )* $quant:vis $name:ident {
|
||||||
$( $arg:ident: $typ:ty ),*
|
$( $arg:ident: $typ:ty ),*
|
||||||
}
|
}
|
||||||
$added:ident $(: $added_typ:ty )? = $xname:ident => $extract:expr ;
|
$added:ident $(: $added_typ:ty )? $( = $xname:ident => $extract:expr )? ;
|
||||||
$process:expr
|
$process:expr
|
||||||
) => {
|
) => {
|
||||||
$( #[ $attr ] )*
|
$( #[ $attr ] )*
|
||||||
#[derive(std::fmt::Debug, Clone)]
|
#[derive(std::fmt::Debug, Clone)]
|
||||||
$quant struct $name {
|
$quant struct $name {
|
||||||
$( $arg: $typ, )+
|
$( $arg: $typ, )*
|
||||||
expr_inst: $crate::interpreted::ExprInst,
|
expr_inst: $crate::interpreted::ExprInst,
|
||||||
}
|
}
|
||||||
$crate::atomic_redirect!($name, expr_inst);
|
$crate::atomic_redirect!($name, expr_inst);
|
||||||
$crate::atomic_impl!(
|
$crate::atomic_impl!(
|
||||||
$name,
|
$name,
|
||||||
|Self{ $($arg, )* expr_inst }: &Self, _| {
|
|Self{ $($arg, )* expr_inst }: &Self, _| {
|
||||||
let $xname = expr_inst;
|
let added $(: $added_typ )? =
|
||||||
let $added $(: $added_typ )? = $extract?;
|
$crate::write_fn_step!(@CONV expr_inst $(, $xname $extract )?);
|
||||||
|
let $added = &added;
|
||||||
$process
|
$process
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
// Write conversion expression for an ExprInst
|
||||||
|
(@CONV $locxname:expr, $xname:ident $extract:expr) => {
|
||||||
|
{
|
||||||
|
let $xname = $locxname;
|
||||||
|
match $extract {
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
Ok(r) => r,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(@CONV $locxname:expr) => {
|
||||||
|
($locxname).try_into()?
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ impl<T: Eq + Hash + Clone> TypedInterner<T> {
|
|||||||
pub fn r(&self, t: Tok<T>) -> &T {
|
pub fn r(&self, t: Tok<T>) -> &T {
|
||||||
let values = self.values.borrow();
|
let values = self.values.borrow();
|
||||||
let key = t.into_usize() - 1;
|
let key = t.into_usize() - 1;
|
||||||
values[key].0
|
values[key].0.borrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Intern a static reference without allocating the data on the heap
|
/// Intern a static reference without allocating the data on the heap
|
||||||
@@ -91,7 +91,7 @@ impl<T: Eq + Hash + Clone> Drop for TypedInterner<T> {
|
|||||||
if !owned {
|
if !owned {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
unsafe { Box::from_raw((item as *const T).cast_mut()) };
|
let _ = unsafe { Box::from_raw((item as *const T).cast_mut()) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ pub mod rule;
|
|||||||
pub mod stl;
|
pub mod stl;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
pub use interner::Sym;
|
||||||
pub use representations::ast_to_interpreted::ast_to_interpreted;
|
pub use representations::ast_to_interpreted::ast_to_interpreted;
|
||||||
pub use representations::{
|
pub use representations::{
|
||||||
ast, interpreted, sourcefile, tree, Literal, Location, PathSet, Primitive,
|
ast, interpreted, sourcefile, tree, Literal, Location, PathSet, Primitive,
|
||||||
|
|||||||
@@ -83,11 +83,7 @@ pub fn import_parser<'a>(
|
|||||||
Some(Import {
|
Some(Import {
|
||||||
path: ctx.interner().i(&path),
|
path: ctx.interner().i(&path),
|
||||||
name: {
|
name: {
|
||||||
if name == ctx.interner().i("*") {
|
if name == ctx.interner().i("*") { None } else { Some(name) }
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(name)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -115,11 +115,7 @@ impl InternedDisplay for Lexeme {
|
|||||||
Self::PH(Placeholder { name, class }) => match *class {
|
Self::PH(Placeholder { name, class }) => match *class {
|
||||||
PHClass::Scalar => write!(f, "${}", i.r(*name)),
|
PHClass::Scalar => write!(f, "${}", i.r(*name)),
|
||||||
PHClass::Vec { nonzero, prio } => {
|
PHClass::Vec { nonzero, prio } => {
|
||||||
if nonzero {
|
if nonzero { write!(f, "...") } else { write!(f, "..") }?;
|
||||||
write!(f, "...")
|
|
||||||
} else {
|
|
||||||
write!(f, "..")
|
|
||||||
}?;
|
|
||||||
write!(f, "${}", i.r(*name))?;
|
write!(f, "${}", i.r(*name))?;
|
||||||
if prio != 0 {
|
if prio != 0 {
|
||||||
write!(f, ":{}", prio)?;
|
write!(f, ":{}", prio)?;
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
//! File system implementation of the source loader callback
|
//! Source loader callback definition and builtin implementations
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
|
|
||||||
|
use chumsky::text::Character;
|
||||||
|
use rust_embed::RustEmbed;
|
||||||
|
|
||||||
use crate::interner::{Interner, Sym};
|
use crate::interner::{Interner, Sym};
|
||||||
use crate::pipeline::error::{
|
use crate::pipeline::error::{
|
||||||
ErrorPosition, ProjectError, UnexpectedDirectory,
|
ErrorPosition, ProjectError, UnexpectedDirectory,
|
||||||
@@ -86,13 +89,52 @@ pub fn load_file(root: &Path, path: &[impl AsRef<str>]) -> IOResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generates a cached file loader for a directory
|
/// Generates a cached file loader for a directory
|
||||||
pub fn mk_cache(root: PathBuf, i: &Interner) -> Cache<Sym, IOResult> {
|
pub fn mk_dir_cache(root: PathBuf, i: &Interner) -> Cache<Sym, IOResult> {
|
||||||
Cache::new(move |token: Sym, _this| -> IOResult {
|
Cache::new(move |token: Sym, _this| -> IOResult {
|
||||||
let path = i.r(token).iter().map(|t| i.r(*t).as_str()).collect::<Vec<_>>();
|
let path = i.r(token).iter().map(|t| i.r(*t).as_str()).collect::<Vec<_>>();
|
||||||
load_file(&root, &path)
|
load_file(&root, &path)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load a file from the specified path from an embed table
|
||||||
|
pub fn load_embed<T: 'static + RustEmbed>(path: &str, ext: &str) -> IOResult {
|
||||||
|
let file_path = path.to_string() + ext;
|
||||||
|
if let Some(file) = T::get(&file_path) {
|
||||||
|
let s = file.data.iter().map(|c| c.to_char()).collect::<String>();
|
||||||
|
Ok(Loaded::Code(Rc::new(s)))
|
||||||
|
} else {
|
||||||
|
let entries = T::iter()
|
||||||
|
.map(|c| c.to_string())
|
||||||
|
.filter_map(|path: String| {
|
||||||
|
let item_prefix = path.to_string() + "/";
|
||||||
|
path.strip_prefix(&item_prefix).map(|subpath| {
|
||||||
|
let item_name = subpath
|
||||||
|
.split_inclusive('/')
|
||||||
|
.next()
|
||||||
|
.expect("Exact match excluded earlier");
|
||||||
|
item_name
|
||||||
|
.strip_suffix('/') // subdirectory
|
||||||
|
.or_else(|| item_name.strip_suffix(ext)) // file
|
||||||
|
.expect("embed should be filtered to extension")
|
||||||
|
.to_string()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
Ok(Loaded::Collection(Rc::new(entries)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a cached file loader for a [RustEmbed]
|
||||||
|
pub fn mk_embed_cache<'a, T: 'static + RustEmbed>(
|
||||||
|
ext: &'a str,
|
||||||
|
i: &'a Interner,
|
||||||
|
) -> Cache<'a, Sym, IOResult> {
|
||||||
|
Cache::new(move |token: Sym, _this| -> IOResult {
|
||||||
|
let path = i.extern_vec(token).join("/");
|
||||||
|
load_embed::<T>(&path, ext)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Loads the string contents of a file at the given location.
|
/// Loads the string contents of a file at the given location.
|
||||||
/// If the path points to a directory, raises an error.
|
/// If the path points to a directory, raises an error.
|
||||||
pub fn load_text(
|
pub fn load_text(
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ use std::rc::Rc;
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
use super::alias_map::AliasMap;
|
use super::alias_map::AliasMap;
|
||||||
use super::decls::InjectedAsFn;
|
use super::decls::{InjectedAsFn, UpdatedFn};
|
||||||
use crate::ast::{Expr, Rule};
|
use crate::ast::{Expr, Rule};
|
||||||
use crate::interner::{Interner, Sym, Tok};
|
use crate::interner::{Interner, Sym, Tok};
|
||||||
use crate::pipeline::{ProjectExt, ProjectModule};
|
use crate::pipeline::{ProjectExt, ProjectModule};
|
||||||
use crate::representations::tree::{ModEntry, ModMember};
|
use crate::representations::tree::{ModEntry, ModMember};
|
||||||
use crate::utils::Substack;
|
use crate::utils::Substack;
|
||||||
|
|
||||||
fn resolve(
|
fn resolve_rec(
|
||||||
token: Sym,
|
token: Sym,
|
||||||
alias_map: &AliasMap,
|
alias_map: &AliasMap,
|
||||||
i: &Interner,
|
i: &Interner,
|
||||||
@@ -18,7 +18,7 @@ fn resolve(
|
|||||||
if let Some(alias) = alias_map.resolve(token) {
|
if let Some(alias) = alias_map.resolve(token) {
|
||||||
Some(i.r(alias).clone())
|
Some(i.r(alias).clone())
|
||||||
} else if let Some((foot, body)) = i.r(token).split_last() {
|
} else if let Some((foot, body)) = i.r(token).split_last() {
|
||||||
let mut new_beginning = resolve(i.i(body), alias_map, i)?;
|
let mut new_beginning = resolve_rec(i.i(body), alias_map, i)?;
|
||||||
new_beginning.push(*foot);
|
new_beginning.push(*foot);
|
||||||
Some(new_beginning)
|
Some(new_beginning)
|
||||||
} else {
|
} else {
|
||||||
@@ -26,6 +26,18 @@ fn resolve(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve(
|
||||||
|
token: Sym,
|
||||||
|
alias_map: &AliasMap,
|
||||||
|
injected_as: &impl InjectedAsFn,
|
||||||
|
i: &Interner,
|
||||||
|
) -> Option<Sym> {
|
||||||
|
injected_as(&i.r(token)[..]).or_else(|| {
|
||||||
|
let next_v = resolve_rec(token, alias_map, i)?;
|
||||||
|
Some(injected_as(&next_v).unwrap_or_else(|| i.i(&next_v)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn process_expr(
|
fn process_expr(
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
alias_map: &AliasMap,
|
alias_map: &AliasMap,
|
||||||
@@ -33,16 +45,7 @@ fn process_expr(
|
|||||||
i: &Interner,
|
i: &Interner,
|
||||||
) -> Expr {
|
) -> Expr {
|
||||||
expr
|
expr
|
||||||
.map_names(&|n| {
|
.map_names(&|n| resolve(n, alias_map, injected_as, i))
|
||||||
injected_as(&i.r(n)[..]).or_else(|| {
|
|
||||||
let next_v = resolve(n, alias_map, i)?;
|
|
||||||
// println!("Resolved alias {} to {}",
|
|
||||||
// i.extern_vec(n).join("::"),
|
|
||||||
// i.extern_all(&next_v).join("::")
|
|
||||||
// );
|
|
||||||
Some(injected_as(&next_v).unwrap_or_else(|| i.i(&next_v)))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| expr.clone())
|
.unwrap_or_else(|| expr.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,10 +57,9 @@ fn apply_aliases_rec(
|
|||||||
alias_map: &AliasMap,
|
alias_map: &AliasMap,
|
||||||
i: &Interner,
|
i: &Interner,
|
||||||
injected_as: &impl InjectedAsFn,
|
injected_as: &impl InjectedAsFn,
|
||||||
|
updated: &impl UpdatedFn,
|
||||||
) -> ProjectModule {
|
) -> ProjectModule {
|
||||||
let items = module
|
let items = (module.items.iter())
|
||||||
.items
|
|
||||||
.iter()
|
|
||||||
.map(|(name, ent)| {
|
.map(|(name, ent)| {
|
||||||
let ModEntry { exported, member } = ent;
|
let ModEntry { exported, member } = ent;
|
||||||
let member = match member {
|
let member = match member {
|
||||||
@@ -65,9 +67,7 @@ fn apply_aliases_rec(
|
|||||||
ModMember::Item(process_expr(expr, alias_map, injected_as, i)),
|
ModMember::Item(process_expr(expr, alias_map, injected_as, i)),
|
||||||
ModMember::Sub(module) => {
|
ModMember::Sub(module) => {
|
||||||
let subpath = path.push(*name);
|
let subpath = path.push(*name);
|
||||||
let is_ignored =
|
let new_mod = if !updated(&subpath.iter().rev_vec_clone()) {
|
||||||
injected_as(&subpath.iter().rev_vec_clone()).is_some();
|
|
||||||
let new_mod = if is_ignored {
|
|
||||||
module.clone()
|
module.clone()
|
||||||
} else {
|
} else {
|
||||||
let module = module.as_ref();
|
let module = module.as_ref();
|
||||||
@@ -77,6 +77,7 @@ fn apply_aliases_rec(
|
|||||||
alias_map,
|
alias_map,
|
||||||
i,
|
i,
|
||||||
injected_as,
|
injected_as,
|
||||||
|
updated,
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
ModMember::Sub(new_mod)
|
ModMember::Sub(new_mod)
|
||||||
@@ -85,23 +86,18 @@ fn apply_aliases_rec(
|
|||||||
(*name, ModEntry { exported: *exported, member })
|
(*name, ModEntry { exported: *exported, member })
|
||||||
})
|
})
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
let rules = module
|
let rules = (module.extra.rules.iter())
|
||||||
.extra
|
|
||||||
.rules
|
|
||||||
.iter()
|
|
||||||
.map(|rule| {
|
.map(|rule| {
|
||||||
let Rule { pattern, prio, template } = rule;
|
let Rule { pattern, prio, template } = rule;
|
||||||
Rule {
|
Rule {
|
||||||
prio: *prio,
|
prio: *prio,
|
||||||
pattern: Rc::new(
|
pattern: Rc::new(
|
||||||
pattern
|
(pattern.iter())
|
||||||
.iter()
|
|
||||||
.map(|expr| process_expr(expr, alias_map, injected_as, i))
|
.map(|expr| process_expr(expr, alias_map, injected_as, i))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
),
|
),
|
||||||
template: Rc::new(
|
template: Rc::new(
|
||||||
template
|
(template.iter())
|
||||||
.iter()
|
|
||||||
.map(|expr| process_expr(expr, alias_map, injected_as, i))
|
.map(|expr| process_expr(expr, alias_map, injected_as, i))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
),
|
),
|
||||||
@@ -113,7 +109,11 @@ fn apply_aliases_rec(
|
|||||||
imports: module.imports.clone(),
|
imports: module.imports.clone(),
|
||||||
extra: ProjectExt {
|
extra: ProjectExt {
|
||||||
rules,
|
rules,
|
||||||
exports: module.extra.exports.clone(),
|
exports: (module.extra.exports.iter())
|
||||||
|
.map(|(k, v)| {
|
||||||
|
(*k, resolve(*v, alias_map, injected_as, i).unwrap_or(*v))
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
file: module.extra.file.clone(),
|
file: module.extra.file.clone(),
|
||||||
imports_from: module.extra.imports_from.clone(),
|
imports_from: module.extra.imports_from.clone(),
|
||||||
},
|
},
|
||||||
@@ -125,6 +125,14 @@ pub fn apply_aliases(
|
|||||||
alias_map: &AliasMap,
|
alias_map: &AliasMap,
|
||||||
i: &Interner,
|
i: &Interner,
|
||||||
injected_as: &impl InjectedAsFn,
|
injected_as: &impl InjectedAsFn,
|
||||||
|
updated: &impl UpdatedFn,
|
||||||
) -> ProjectModule {
|
) -> ProjectModule {
|
||||||
apply_aliases_rec(Substack::Bottom, module, alias_map, i, injected_as)
|
apply_aliases_rec(
|
||||||
|
Substack::Bottom,
|
||||||
|
module,
|
||||||
|
alias_map,
|
||||||
|
i,
|
||||||
|
injected_as,
|
||||||
|
updated,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
|
use core::panic;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::alias_map::AliasMap;
|
use super::alias_map::AliasMap;
|
||||||
use super::decls::InjectedAsFn;
|
use super::decls::UpdatedFn;
|
||||||
use crate::interner::{Interner, Tok};
|
use crate::interner::{Interner, Tok};
|
||||||
use crate::pipeline::error::{NotExported, ProjectError};
|
use crate::pipeline::error::{NotExported, ProjectError};
|
||||||
use crate::pipeline::project_tree::{split_path, ProjectModule, ProjectTree};
|
use crate::pipeline::project_tree::{split_path, ProjectModule, ProjectTree};
|
||||||
use crate::representations::tree::{ModMember, WalkErrorKind};
|
use crate::representations::tree::{ModMember, WalkErrorKind};
|
||||||
use crate::utils::{pushed, Substack};
|
use crate::utils::{pushed, unwrap_or, Substack};
|
||||||
|
|
||||||
/// Assert that a module identified by a path can see a given symbol
|
/// Assert that a module identified by a path can see a given symbol
|
||||||
fn assert_visible(
|
fn assert_visible(
|
||||||
@@ -15,18 +16,24 @@ fn assert_visible(
|
|||||||
project: &ProjectTree,
|
project: &ProjectTree,
|
||||||
i: &Interner,
|
i: &Interner,
|
||||||
) -> Result<(), Rc<dyn ProjectError>> {
|
) -> Result<(), Rc<dyn ProjectError>> {
|
||||||
let (tgt_item, tgt_path) = if let Some(s) = target.split_last() {
|
let (tgt_item, tgt_path) = unwrap_or!(target.split_last(); return Ok(()));
|
||||||
s
|
|
||||||
} else {
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
let shared_len =
|
let shared_len =
|
||||||
source.iter().zip(tgt_path.iter()).take_while(|(a, b)| a == b).count();
|
source.iter().zip(tgt_path.iter()).take_while(|(a, b)| a == b).count();
|
||||||
let shared_root =
|
let vis_ignored_len = usize::min(tgt_path.len(), shared_len + 1);
|
||||||
project.0.walk(&tgt_path[..shared_len], false).expect("checked in parsing");
|
let private_root =
|
||||||
let direct_parent =
|
(project.0).walk(&tgt_path[..vis_ignored_len], false).unwrap_or_else(|e| {
|
||||||
shared_root.walk(&tgt_path[shared_len..], true).map_err(|e| {
|
let path_slc = &tgt_path[..vis_ignored_len];
|
||||||
match e.kind {
|
let bad_path = i.extern_all(path_slc).join("::");
|
||||||
|
eprintln!(
|
||||||
|
"Error while walking {bad_path}; {:?} on step {}",
|
||||||
|
e.kind, e.pos
|
||||||
|
);
|
||||||
|
eprintln!("looking from {}", i.extern_all(source).join("::"));
|
||||||
|
panic!("")
|
||||||
|
});
|
||||||
|
let direct_parent = private_root
|
||||||
|
.walk(&tgt_path[vis_ignored_len..], true)
|
||||||
|
.map_err(|e| match e.kind {
|
||||||
WalkErrorKind::Missing => panic!("checked in parsing"),
|
WalkErrorKind::Missing => panic!("checked in parsing"),
|
||||||
WalkErrorKind::Private => {
|
WalkErrorKind::Private => {
|
||||||
let full_path = &tgt_path[..shared_len + e.pos];
|
let full_path = &tgt_path[..shared_len + e.pos];
|
||||||
@@ -40,11 +47,9 @@ fn assert_visible(
|
|||||||
}
|
}
|
||||||
.rc()
|
.rc()
|
||||||
},
|
},
|
||||||
}
|
|
||||||
})?;
|
})?;
|
||||||
let tgt_item_exported = direct_parent.extra.exports.contains_key(tgt_item);
|
let tgt_item_exported = direct_parent.extra.exports.contains_key(tgt_item);
|
||||||
let target_prefixes_source =
|
let target_prefixes_source = shared_len == tgt_path.len();
|
||||||
shared_len == tgt_path.len() && source.get(shared_len) == Some(tgt_item);
|
|
||||||
if !tgt_item_exported && !target_prefixes_source {
|
if !tgt_item_exported && !target_prefixes_source {
|
||||||
let (file, sub) = split_path(target, project);
|
let (file, sub) = split_path(target, project);
|
||||||
let (ref_file, ref_sub) = split_path(source, project);
|
let (ref_file, ref_sub) = split_path(source, project);
|
||||||
@@ -69,20 +74,30 @@ fn collect_aliases_rec(
|
|||||||
project: &ProjectTree,
|
project: &ProjectTree,
|
||||||
alias_map: &mut AliasMap,
|
alias_map: &mut AliasMap,
|
||||||
i: &Interner,
|
i: &Interner,
|
||||||
injected_as: &impl InjectedAsFn,
|
updated: &impl UpdatedFn,
|
||||||
) -> Result<(), Rc<dyn ProjectError>> {
|
) -> Result<(), Rc<dyn ProjectError>> {
|
||||||
// Assume injected module has been alias-resolved
|
// Assume injected module has been alias-resolved
|
||||||
let mod_path_v = path.iter().rev_vec_clone();
|
let mod_path_v = path.iter().rev_vec_clone();
|
||||||
if injected_as(&mod_path_v).is_some() {
|
if !updated(&mod_path_v) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
for (&name, &target_mod) in module.extra.imports_from.iter() {
|
for (&name, &target_mod_name) in module.extra.imports_from.iter() {
|
||||||
let target_mod_v = i.r(target_mod);
|
let target_mod_v = i.r(target_mod_name);
|
||||||
let target_sym_v = pushed(target_mod_v, name);
|
let target_sym_v = pushed(target_mod_v, name);
|
||||||
assert_visible(&mod_path_v, &target_sym_v, project, i)?;
|
assert_visible(&mod_path_v, &target_sym_v, project, i)?;
|
||||||
let sym_path_v = pushed(&mod_path_v, name);
|
let sym_path_v = pushed(&mod_path_v, name);
|
||||||
let sym_path = i.i(&sym_path_v);
|
let sym_path = i.i(&sym_path_v);
|
||||||
let target_sym = i.i(&target_sym_v);
|
let target_mod = (project.0.walk(target_mod_v, false))
|
||||||
|
.expect("checked above in assert_visible");
|
||||||
|
let target_sym =
|
||||||
|
*target_mod.extra.exports.get(&name).unwrap_or_else(|| {
|
||||||
|
panic!(
|
||||||
|
"error in {}, {} has no member {}",
|
||||||
|
i.extern_all(&mod_path_v).join("::"),
|
||||||
|
i.extern_all(target_mod_v).join("::"),
|
||||||
|
i.r(name)
|
||||||
|
)
|
||||||
|
});
|
||||||
alias_map.link(sym_path, target_sym);
|
alias_map.link(sym_path, target_sym);
|
||||||
}
|
}
|
||||||
for (&name, entry) in module.items.iter() {
|
for (&name, entry) in module.items.iter() {
|
||||||
@@ -97,7 +112,7 @@ fn collect_aliases_rec(
|
|||||||
project,
|
project,
|
||||||
alias_map,
|
alias_map,
|
||||||
i,
|
i,
|
||||||
injected_as,
|
updated,
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -109,14 +124,7 @@ pub fn collect_aliases(
|
|||||||
project: &ProjectTree,
|
project: &ProjectTree,
|
||||||
alias_map: &mut AliasMap,
|
alias_map: &mut AliasMap,
|
||||||
i: &Interner,
|
i: &Interner,
|
||||||
injected_as: &impl InjectedAsFn,
|
updated: &impl UpdatedFn,
|
||||||
) -> Result<(), Rc<dyn ProjectError>> {
|
) -> Result<(), Rc<dyn ProjectError>> {
|
||||||
collect_aliases_rec(
|
collect_aliases_rec(Substack::Bottom, module, project, alias_map, i, updated)
|
||||||
Substack::Bottom,
|
|
||||||
module,
|
|
||||||
project,
|
|
||||||
alias_map,
|
|
||||||
i,
|
|
||||||
injected_as,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ use crate::interner::{Sym, Tok};
|
|||||||
|
|
||||||
trait_set! {
|
trait_set! {
|
||||||
pub trait InjectedAsFn = Fn(&[Tok<String>]) -> Option<Sym>;
|
pub trait InjectedAsFn = Fn(&[Tok<String>]) -> Option<Sym>;
|
||||||
|
pub trait UpdatedFn = Fn(&[Tok<String>]) -> bool;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use super::alias_map::AliasMap;
|
use super::alias_map::AliasMap;
|
||||||
use super::apply_aliases::apply_aliases;
|
use super::apply_aliases::apply_aliases;
|
||||||
use super::collect_aliases::collect_aliases;
|
use super::collect_aliases::collect_aliases;
|
||||||
use super::decls::InjectedAsFn;
|
use super::decls::{InjectedAsFn, UpdatedFn};
|
||||||
use crate::interner::Interner;
|
use crate::interner::Interner;
|
||||||
use crate::pipeline::error::ProjectError;
|
use crate::pipeline::error::ProjectError;
|
||||||
use crate::pipeline::project_tree::ProjectTree;
|
use crate::pipeline::project_tree::ProjectTree;
|
||||||
@@ -16,21 +14,11 @@ pub fn resolve_imports(
|
|||||||
project: ProjectTree,
|
project: ProjectTree,
|
||||||
i: &Interner,
|
i: &Interner,
|
||||||
injected_as: &impl InjectedAsFn,
|
injected_as: &impl InjectedAsFn,
|
||||||
|
updated: &impl UpdatedFn,
|
||||||
) -> Result<ProjectTree, Rc<dyn ProjectError>> {
|
) -> Result<ProjectTree, Rc<dyn ProjectError>> {
|
||||||
let mut map = AliasMap::new();
|
let mut map = AliasMap::new();
|
||||||
collect_aliases(project.0.as_ref(), &project, &mut map, i, injected_as)?;
|
collect_aliases(project.0.as_ref(), &project, &mut map, i, updated)?;
|
||||||
println!(
|
let new_mod =
|
||||||
"Aliases: {{{:?}}}",
|
apply_aliases(project.0.as_ref(), &map, i, injected_as, updated);
|
||||||
map
|
|
||||||
.targets
|
|
||||||
.iter()
|
|
||||||
.map(|(kt, vt)| format!(
|
|
||||||
"{} => {}",
|
|
||||||
i.extern_vec(*kt).join("::"),
|
|
||||||
i.extern_vec(*vt).join("::")
|
|
||||||
))
|
|
||||||
.join(", ")
|
|
||||||
);
|
|
||||||
let new_mod = apply_aliases(project.0.as_ref(), &map, i, injected_as);
|
|
||||||
Ok(ProjectTree(Rc::new(new_mod)))
|
Ok(ProjectTree(Rc::new(new_mod)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ mod import_resolution;
|
|||||||
mod parse_layer;
|
mod parse_layer;
|
||||||
mod project_tree;
|
mod project_tree;
|
||||||
mod source_loader;
|
mod source_loader;
|
||||||
mod split_name;
|
|
||||||
|
|
||||||
pub use parse_layer::parse_layer;
|
pub use parse_layer::parse_layer;
|
||||||
pub use project_tree::{
|
pub use project_tree::{
|
||||||
|
|||||||
@@ -34,13 +34,16 @@ pub fn parse_layer(
|
|||||||
};
|
};
|
||||||
let source =
|
let source =
|
||||||
source_loader::load_source(targets, prelude, i, loader, &|path| {
|
source_loader::load_source(targets, prelude, i, loader, &|path| {
|
||||||
injected_as(path).is_some()
|
environment.0.walk(&i.r(path)[..], false).is_ok()
|
||||||
})?;
|
})?;
|
||||||
let tree = project_tree::build_tree(source, i, prelude, &injected_names)?;
|
let tree = project_tree::build_tree(source, i, prelude, &injected_names)?;
|
||||||
let sum = ProjectTree(Rc::new(
|
let sum = ProjectTree(Rc::new(
|
||||||
environment.0.as_ref().clone() + tree.0.as_ref().clone(),
|
environment.0.as_ref().clone().overlay(tree.0.as_ref().clone()),
|
||||||
));
|
));
|
||||||
let resolvd = import_resolution::resolve_imports(sum, i, &injected_as)?;
|
let resolvd =
|
||||||
|
import_resolution::resolve_imports(sum, i, &injected_as, &|path| {
|
||||||
|
tree.0.walk(path, false).is_ok()
|
||||||
|
})?;
|
||||||
// Addition among modules favours the left hand side.
|
// Addition among modules favours the left hand side.
|
||||||
Ok(resolvd)
|
Ok(resolvd)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,11 +57,7 @@ fn source_to_module(
|
|||||||
let imports = data
|
let imports = data
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|ent| {
|
.filter_map(|ent| {
|
||||||
if let FileEntry::Import(impv) = ent {
|
if let FileEntry::Import(impv) = ent { Some(impv.iter()) } else { None }
|
||||||
Some(impv.iter())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.flatten()
|
.flatten()
|
||||||
.cloned()
|
.cloned()
|
||||||
@@ -182,6 +178,10 @@ fn files_to_module(
|
|||||||
i: &Interner,
|
i: &Interner,
|
||||||
) -> Rc<Module<Expr, ProjectExt>> {
|
) -> Rc<Module<Expr, ProjectExt>> {
|
||||||
let lvl = path.len();
|
let lvl = path.len();
|
||||||
|
debug_assert!(
|
||||||
|
files.iter().map(|f| f.path.len()).max().unwrap() >= lvl,
|
||||||
|
"path is longer than any of the considered file paths"
|
||||||
|
);
|
||||||
let path_v = path.iter().rev_vec_clone();
|
let path_v = path.iter().rev_vec_clone();
|
||||||
if files.len() == 1 && files[0].path.len() == lvl {
|
if files.len() == 1 && files[0].path.len() == lvl {
|
||||||
return source_to_module(
|
return source_to_module(
|
||||||
@@ -227,6 +227,7 @@ pub fn build_tree(
|
|||||||
prelude: &[FileEntry],
|
prelude: &[FileEntry],
|
||||||
injected: &impl InjectedOperatorsFn,
|
injected: &impl InjectedOperatorsFn,
|
||||||
) -> Result<ProjectTree, Rc<dyn ProjectError>> {
|
) -> Result<ProjectTree, Rc<dyn ProjectError>> {
|
||||||
|
assert!(!files.is_empty(), "A tree requires at least one module");
|
||||||
let ops_cache = collect_ops::mk_cache(&files, i, injected);
|
let ops_cache = collect_ops::mk_cache(&files, i, injected);
|
||||||
let mut entries = files
|
let mut entries = files
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
use std::println;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use itertools::Itertools;
|
|
||||||
use trait_set::trait_set;
|
use trait_set::trait_set;
|
||||||
|
|
||||||
use crate::interner::{Interner, Sym, Tok};
|
use crate::interner::{Interner, Sym, Tok};
|
||||||
use crate::pipeline::error::{ModuleNotFound, ProjectError};
|
use crate::pipeline::error::{ModuleNotFound, ProjectError};
|
||||||
use crate::pipeline::source_loader::LoadedSourceTable;
|
use crate::pipeline::source_loader::LoadedSourceTable;
|
||||||
use crate::pipeline::split_name::split_name;
|
|
||||||
use crate::representations::tree::WalkErrorKind;
|
use crate::representations::tree::WalkErrorKind;
|
||||||
use crate::utils::Cache;
|
use crate::utils::{split_max_prefix, unwrap_or, Cache};
|
||||||
|
|
||||||
pub type OpsResult = Result<Rc<HashSet<Tok<String>>>, Rc<dyn ProjectError>>;
|
pub type OpsResult = Result<Rc<HashSet<Tok<String>>>, Rc<dyn ProjectError>>;
|
||||||
pub type ExportedOpsCache<'a> = Cache<'a, Sym, OpsResult>;
|
pub type ExportedOpsCache<'a> = Cache<'a, Sym, OpsResult>;
|
||||||
@@ -33,22 +30,12 @@ pub fn collect_exported_ops(
|
|||||||
i: &Interner,
|
i: &Interner,
|
||||||
injected: &impl InjectedOperatorsFn,
|
injected: &impl InjectedOperatorsFn,
|
||||||
) -> OpsResult {
|
) -> OpsResult {
|
||||||
if let Some(ops) = injected(path) {
|
let injected = injected(path).unwrap_or_else(|| Rc::new(HashSet::new()));
|
||||||
if path == i.i(&[i.i("prelude")][..]) {
|
|
||||||
println!("%%% Prelude exported ops %%%");
|
|
||||||
println!("{}", ops.iter().map(|t| i.r(*t)).join(", "));
|
|
||||||
}
|
|
||||||
return Ok(ops);
|
|
||||||
}
|
|
||||||
let is_file = |n: &[Tok<String>]| loaded.contains_key(&i.i(n));
|
let is_file = |n: &[Tok<String>]| loaded.contains_key(&i.i(n));
|
||||||
let path_s = &i.r(path)[..];
|
let path_s = &i.r(path)[..];
|
||||||
let name_split = split_name(path_s, &is_file);
|
let name_split = split_max_prefix(path_s, &is_file);
|
||||||
let (fpath_v, subpath_v) = if let Some(f) = name_split {
|
let (fpath_v, subpath_v) = unwrap_or!(name_split; return Ok(Rc::new(
|
||||||
f
|
(loaded.keys())
|
||||||
} else {
|
|
||||||
return Ok(Rc::new(
|
|
||||||
loaded
|
|
||||||
.keys()
|
|
||||||
.copied()
|
.copied()
|
||||||
.filter_map(|modname| {
|
.filter_map(|modname| {
|
||||||
let modname_s = i.r(modname);
|
let modname_s = i.r(modname);
|
||||||
@@ -58,9 +45,9 @@ pub fn collect_exported_ops(
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.chain(injected.iter().copied())
|
||||||
.collect::<HashSet<_>>(),
|
.collect::<HashSet<_>>(),
|
||||||
));
|
)));
|
||||||
};
|
|
||||||
let fpath = i.i(fpath_v);
|
let fpath = i.i(fpath_v);
|
||||||
let preparsed = &loaded[&fpath].preparsed;
|
let preparsed = &loaded[&fpath].preparsed;
|
||||||
let module = preparsed.0.walk(subpath_v, false).map_err(|walk_err| {
|
let module = preparsed.0.walk(subpath_v, false).map_err(|walk_err| {
|
||||||
@@ -70,8 +57,7 @@ pub fn collect_exported_ops(
|
|||||||
},
|
},
|
||||||
WalkErrorKind::Missing => ModuleNotFound {
|
WalkErrorKind::Missing => ModuleNotFound {
|
||||||
file: i.extern_vec(fpath),
|
file: i.extern_vec(fpath),
|
||||||
subpath: subpath_v
|
subpath: (subpath_v.iter())
|
||||||
.iter()
|
|
||||||
.take(walk_err.pos)
|
.take(walk_err.pos)
|
||||||
.map(|t| i.r(*t))
|
.map(|t| i.r(*t))
|
||||||
.cloned()
|
.cloned()
|
||||||
@@ -80,12 +66,11 @@ pub fn collect_exported_ops(
|
|||||||
.rc(),
|
.rc(),
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
let out: HashSet<_> =
|
let out = (module.items.iter())
|
||||||
module.items.iter().filter(|(_, v)| v.exported).map(|(k, _)| *k).collect();
|
.filter(|(_, v)| v.exported)
|
||||||
if path == i.i(&[i.i("prelude")][..]) {
|
.map(|(k, _)| *k)
|
||||||
println!("%%% Prelude exported ops %%%");
|
.chain(injected.iter().copied())
|
||||||
println!("{}", out.iter().map(|t| i.r(*t)).join(", "));
|
.collect::<HashSet<_>>();
|
||||||
}
|
|
||||||
Ok(Rc::new(out))
|
Ok(Rc::new(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use super::exported_ops::{ExportedOpsCache, OpsResult};
|
use super::exported_ops::{ExportedOpsCache, OpsResult};
|
||||||
use crate::interner::{Interner, Tok};
|
use crate::interner::{Interner, Tok};
|
||||||
@@ -34,13 +33,11 @@ pub fn collect_ops_for(
|
|||||||
) -> OpsResult {
|
) -> OpsResult {
|
||||||
let tree = &loaded[&i.i(file)].preparsed.0;
|
let tree = &loaded[&i.i(file)].preparsed.0;
|
||||||
let mut ret = HashSet::new();
|
let mut ret = HashSet::new();
|
||||||
println!("collecting ops for {}", i.extern_all(file).join("::"));
|
|
||||||
tree_all_ops(tree.as_ref(), &mut ret);
|
tree_all_ops(tree.as_ref(), &mut ret);
|
||||||
tree.visit_all_imports(&mut |modpath, _module, import| {
|
tree.visit_all_imports(&mut |modpath, _module, import| {
|
||||||
if let Some(n) = import.name {
|
if let Some(n) = import.name {
|
||||||
ret.insert(n);
|
ret.insert(n);
|
||||||
} else {
|
} else {
|
||||||
println!("\tglob import from {}", i.extern_vec(import.path).join("::"));
|
|
||||||
let path = import_abs_path(file, modpath, &i.r(import.path)[..], i)
|
let path = import_abs_path(file, modpath, &i.r(import.path)[..], i)
|
||||||
.expect("This error should have been caught during loading");
|
.expect("This error should have been caught during loading");
|
||||||
ret.extend(ops_cache.find(&i.i(&path))?.iter().copied());
|
ret.extend(ops_cache.find(&i.i(&path))?.iter().copied());
|
||||||
@@ -48,9 +45,5 @@ pub fn collect_ops_for(
|
|||||||
Ok::<_, Rc<dyn ProjectError>>(())
|
Ok::<_, Rc<dyn ProjectError>>(())
|
||||||
})?;
|
})?;
|
||||||
ret.drain_filter(|t| !is_op(i.r(*t)));
|
ret.drain_filter(|t| !is_op(i.r(*t)));
|
||||||
if file == &[i.i("map")][..] {
|
|
||||||
println!(" %%% ops in map %%% ");
|
|
||||||
println!("{}", ret.iter().map(|t| i.r(*t)).join(", "))
|
|
||||||
}
|
|
||||||
Ok(Rc::new(ret))
|
Ok(Rc::new(ret))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ impl Add for ProjectExt {
|
|||||||
pub type ProjectModule = Module<Expr, ProjectExt>;
|
pub type ProjectModule = Module<Expr, ProjectExt>;
|
||||||
|
|
||||||
/// Module corresponding to the root of a project
|
/// Module corresponding to the root of a project
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct ProjectTree(pub Rc<ProjectModule>);
|
pub struct ProjectTree(pub Rc<ProjectModule>);
|
||||||
|
|
||||||
fn collect_rules_rec(bag: &mut Vec<Rule>, module: &ProjectModule) {
|
fn collect_rules_rec(bag: &mut Vec<Rule>, module: &ProjectModule) {
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use super::loaded_source::{LoadedSource, LoadedSourceTable};
|
use super::loaded_source::{LoadedSource, LoadedSourceTable};
|
||||||
use super::preparse::preparse;
|
use super::preparse::preparse;
|
||||||
use crate::interner::{Interner, Sym, Tok};
|
use crate::interner::{Interner, Sym};
|
||||||
use crate::pipeline::error::ProjectError;
|
use crate::pipeline::error::ProjectError;
|
||||||
use crate::pipeline::file_loader::{load_text, IOResult, Loaded};
|
use crate::pipeline::file_loader::{load_text, IOResult, Loaded};
|
||||||
use crate::pipeline::import_abs_path::import_abs_path;
|
use crate::pipeline::import_abs_path::import_abs_path;
|
||||||
use crate::pipeline::split_name::split_name;
|
|
||||||
use crate::representations::sourcefile::FileEntry;
|
use crate::representations::sourcefile::FileEntry;
|
||||||
|
use crate::utils::split_max_prefix;
|
||||||
|
|
||||||
/// Load the source at the given path or all within if it's a collection,
|
/// Load the source at the given path or all within if it's a collection,
|
||||||
/// and all sources imported from these.
|
/// and all sources imported from these.
|
||||||
@@ -18,46 +18,26 @@ fn load_abs_path_rec(
|
|||||||
prelude: &[FileEntry],
|
prelude: &[FileEntry],
|
||||||
i: &Interner,
|
i: &Interner,
|
||||||
get_source: &impl Fn(Sym) -> IOResult,
|
get_source: &impl Fn(Sym) -> IOResult,
|
||||||
is_injected: &impl Fn(&[Tok<String>]) -> bool,
|
is_injected_module: &impl Fn(Sym) -> bool,
|
||||||
) -> Result<(), Rc<dyn ProjectError>> {
|
) -> Result<(), Rc<dyn ProjectError>> {
|
||||||
let abs_pathv = i.r(abs_path);
|
// # Termination
|
||||||
// short-circuit if this import is defined externally or already known
|
//
|
||||||
if is_injected(abs_pathv) | table.contains_key(&abs_path) {
|
// Every recursion of this function either
|
||||||
|
// - adds one of the files in the source directory to `table` or
|
||||||
|
// - recursively traverses a directory tree
|
||||||
|
// therefore eventually the function exits, assuming that the directory tree
|
||||||
|
// contains no cycles.
|
||||||
|
|
||||||
|
// Termination: exit if entry already visited
|
||||||
|
if table.contains_key(&abs_path) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// try splitting the path to file, swallowing any IO errors
|
// try splitting the path to file, swallowing any IO errors
|
||||||
let is_file = |p| (get_source)(p).map(|l| l.is_code()).unwrap_or(false);
|
let is_file = |sym| get_source(sym).map(|l| l.is_code()).unwrap_or(false);
|
||||||
let name_split = split_name(abs_pathv, &|p| is_file(i.i(p)));
|
let name_split = split_max_prefix(&i.r(abs_path)[..], &|p| is_file(i.i(p)));
|
||||||
let filename = if let Some((f, _)) = name_split {
|
if let Some((filename, _)) = name_split {
|
||||||
f
|
// if the filename is valid, load, preparse and record this file
|
||||||
} else {
|
|
||||||
// If the path could not be split to file, load it as directory
|
|
||||||
let coll = if let Loaded::Collection(c) = (get_source)(abs_path)? {
|
|
||||||
c
|
|
||||||
}
|
|
||||||
// ^^ raise any IO error that was previously swallowed
|
|
||||||
else {
|
|
||||||
panic!("split_name returned None but the path is a file")
|
|
||||||
};
|
|
||||||
// recurse on all files and folders within
|
|
||||||
for item in coll.iter() {
|
|
||||||
let abs_subpath = abs_pathv
|
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.chain(iter::once(i.i(item)))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
load_abs_path_rec(
|
|
||||||
i.i(&abs_subpath),
|
|
||||||
table,
|
|
||||||
prelude,
|
|
||||||
i,
|
|
||||||
get_source,
|
|
||||||
is_injected,
|
|
||||||
)?
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
};
|
|
||||||
// otherwise load, preparse and record this file
|
|
||||||
let text = load_text(i.i(filename), &get_source, i)?;
|
let text = load_text(i.i(filename), &get_source, i)?;
|
||||||
let preparsed = preparse(
|
let preparsed = preparse(
|
||||||
filename.iter().map(|t| i.r(*t)).cloned().collect(),
|
filename.iter().map(|t| i.r(*t)).cloned().collect(),
|
||||||
@@ -65,7 +45,10 @@ fn load_abs_path_rec(
|
|||||||
prelude,
|
prelude,
|
||||||
i,
|
i,
|
||||||
)?;
|
)?;
|
||||||
table.insert(abs_path, LoadedSource { text, preparsed: preparsed.clone() });
|
table.insert(i.i(filename), LoadedSource {
|
||||||
|
text,
|
||||||
|
preparsed: preparsed.clone(),
|
||||||
|
});
|
||||||
// recurse on all imported modules
|
// recurse on all imported modules
|
||||||
preparsed.0.visit_all_imports(&mut |modpath, _module, import| {
|
preparsed.0.visit_all_imports(&mut |modpath, _module, import| {
|
||||||
let abs_pathv =
|
let abs_pathv =
|
||||||
@@ -77,23 +60,64 @@ fn load_abs_path_rec(
|
|||||||
prelude,
|
prelude,
|
||||||
i,
|
i,
|
||||||
get_source,
|
get_source,
|
||||||
is_injected,
|
is_injected_module,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
// If the path is not within a file, load it as directory
|
||||||
|
let coll = match get_source(abs_path) {
|
||||||
|
Ok(Loaded::Collection(coll)) => coll,
|
||||||
|
Ok(Loaded::Code(_)) =>
|
||||||
|
unreachable!("split_name returned None but the path is a file"),
|
||||||
|
Err(e) => {
|
||||||
|
let parent = i.r(abs_path).split_last().expect("import path nonzero").1;
|
||||||
|
// exit without error if it was injected, or raise any IO error that was
|
||||||
|
// previously swallowed
|
||||||
|
return if is_injected_module(i.i(parent)) { Ok(()) } else { Err(e) };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// recurse on all files and folders within
|
||||||
|
for item in coll.iter() {
|
||||||
|
let abs_subpath = (i.r(abs_path).iter())
|
||||||
|
.copied()
|
||||||
|
.chain(iter::once(i.i(item)))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
load_abs_path_rec(
|
||||||
|
i.i(&abs_subpath),
|
||||||
|
table,
|
||||||
|
prelude,
|
||||||
|
i,
|
||||||
|
get_source,
|
||||||
|
is_injected_module,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load and preparse all files reachable from the load targets via
|
/// Load and preparse all files reachable from the load targets via
|
||||||
/// imports that aren't injected.
|
/// imports that aren't injected.
|
||||||
|
///
|
||||||
|
/// is_injected_module must return false for injected symbols, but may return
|
||||||
|
/// true for parents of injected modules that are not directly part of the
|
||||||
|
/// injected data (the ProjectTree doesn't make a distinction between the two)
|
||||||
pub fn load_source(
|
pub fn load_source(
|
||||||
targets: &[Sym],
|
targets: &[Sym],
|
||||||
prelude: &[FileEntry],
|
prelude: &[FileEntry],
|
||||||
i: &Interner,
|
i: &Interner,
|
||||||
get_source: &impl Fn(Sym) -> IOResult,
|
get_source: &impl Fn(Sym) -> IOResult,
|
||||||
is_injected: &impl Fn(&[Tok<String>]) -> bool,
|
is_injected_module: &impl Fn(Sym) -> bool,
|
||||||
) -> Result<LoadedSourceTable, Rc<dyn ProjectError>> {
|
) -> Result<LoadedSourceTable, Rc<dyn ProjectError>> {
|
||||||
let mut table = LoadedSourceTable::new();
|
let mut table = LoadedSourceTable::new();
|
||||||
for target in targets {
|
for target in targets {
|
||||||
load_abs_path_rec(*target, &mut table, prelude, i, get_source, is_injected)?
|
load_abs_path_rec(
|
||||||
|
*target,
|
||||||
|
&mut table,
|
||||||
|
prelude,
|
||||||
|
i,
|
||||||
|
get_source,
|
||||||
|
is_injected_module,
|
||||||
|
)?
|
||||||
}
|
}
|
||||||
Ok(table)
|
Ok(table)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
use crate::interner::Tok;
|
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
// FIXME couldn't find a good factoring
|
|
||||||
pub fn split_name<'a>(
|
|
||||||
path: &'a [Tok<String>],
|
|
||||||
is_valid: &impl Fn(&[Tok<String>]) -> bool,
|
|
||||||
) -> Option<(&'a [Tok<String>], &'a [Tok<String>])> {
|
|
||||||
for split in (0..=path.len()).rev() {
|
|
||||||
let (filename, subpath) = path.split_at(split);
|
|
||||||
if is_valid(filename) {
|
|
||||||
return Some((filename, subpath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
@@ -179,11 +179,8 @@ impl Clause {
|
|||||||
match self {
|
match self {
|
||||||
Clause::Lambda(arg, body) => {
|
Clause::Lambda(arg, body) => {
|
||||||
arg.visit_names(binds, cb);
|
arg.visit_names(binds, cb);
|
||||||
let new_binds = if let Clause::Name(n) = arg.value {
|
let new_binds =
|
||||||
binds.push(n)
|
if let Clause::Name(n) = arg.value { binds.push(n) } else { binds };
|
||||||
} else {
|
|
||||||
binds
|
|
||||||
};
|
|
||||||
for x in body.iter() {
|
for x in body.iter() {
|
||||||
x.visit_names(new_binds, cb)
|
x.visit_names(new_binds, cb)
|
||||||
}
|
}
|
||||||
@@ -216,11 +213,7 @@ impl Clause {
|
|||||||
val.unwrap_or_else(|| e.clone())
|
val.unwrap_or_else(|| e.clone())
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
if any_some {
|
if any_some { Some(Clause::S(*c, Rc::new(new_body))) } else { None }
|
||||||
Some(Clause::S(*c, Rc::new(new_body)))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Clause::Lambda(arg, body) => {
|
Clause::Lambda(arg, body) => {
|
||||||
let new_arg = arg.map_names(pred);
|
let new_arg = arg.map_names(pred);
|
||||||
|
|||||||
@@ -219,3 +219,9 @@ impl<T: Into<Literal>> From<T> for Clause {
|
|||||||
Self::P(Primitive::Literal(value.into()))
|
Self::P(Primitive::Literal(value.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Into<Clause>> From<T> for ExprInst {
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
value.into().wrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
//! Building blocks of a source file
|
//! Building blocks of a source file
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
use itertools::{Either, Itertools};
|
use itertools::{Either, Itertools};
|
||||||
|
|
||||||
use crate::ast::{Constant, Rule};
|
use crate::ast::{Constant, Rule};
|
||||||
@@ -157,7 +159,9 @@ pub fn absolute_path(
|
|||||||
if tail.is_empty() {
|
if tail.is_empty() {
|
||||||
Ok(new_abs.to_vec())
|
Ok(new_abs.to_vec())
|
||||||
} else {
|
} else {
|
||||||
absolute_path(new_abs, tail, i)
|
let new_rel =
|
||||||
|
iter::once(i.i("self")).chain(tail.iter().copied()).collect::<Vec<_>>();
|
||||||
|
absolute_path(new_abs, &new_rel, i)
|
||||||
}
|
}
|
||||||
} else if *head == i.i("self") {
|
} else if *head == i.i("self") {
|
||||||
Ok(abs_location.iter().chain(tail.iter()).copied().collect())
|
Ok(abs_location.iter().chain(tail.iter()).copied().collect())
|
||||||
|
|||||||
@@ -107,23 +107,23 @@ impl<TItem: Clone, TExt: Clone> Module<TItem, TExt> {
|
|||||||
) -> Result<(), E> {
|
) -> Result<(), E> {
|
||||||
self.visit_all_imports_rec(Substack::Bottom, callback)
|
self.visit_all_imports_rec(Substack::Bottom, callback)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<TItem: Clone, TExt: Clone + Add<Output = TExt>> Add
|
/// Combine two module trees; wherever they conflict, the overlay is
|
||||||
for Module<TItem, TExt>
|
/// preferred.
|
||||||
{
|
pub fn overlay(mut self, overlay: Self) -> Self
|
||||||
type Output = Self;
|
where
|
||||||
|
TExt: Add<TExt, Output = TExt>,
|
||||||
fn add(mut self, rhs: Self) -> Self::Output {
|
{
|
||||||
let Module { extra, imports, items } = rhs;
|
let Module { extra, imports, items } = overlay;
|
||||||
|
let mut new_items = HashMap::new();
|
||||||
for (key, right) in items {
|
for (key, right) in items {
|
||||||
// if both contain a submodule
|
// if both contain a submodule
|
||||||
if let Some(left) = self.items.remove(&key) {
|
if let Some(left) = self.items.remove(&key) {
|
||||||
if let ModMember::Sub(rsub) = &right.member {
|
if let ModMember::Sub(rsub) = &right.member {
|
||||||
if let ModMember::Sub(lsub) = &left.member {
|
if let ModMember::Sub(lsub) = &left.member {
|
||||||
// merge them with rhs exportedness
|
// merge them with rhs exportedness
|
||||||
let new_mod = lsub.as_ref().clone() + rsub.as_ref().clone();
|
let new_mod = lsub.as_ref().clone().overlay(rsub.as_ref().clone());
|
||||||
self.items.insert(key, ModEntry {
|
new_items.insert(key, ModEntry {
|
||||||
exported: right.exported,
|
exported: right.exported,
|
||||||
member: ModMember::Sub(Rc::new(new_mod)),
|
member: ModMember::Sub(Rc::new(new_mod)),
|
||||||
});
|
});
|
||||||
@@ -132,10 +132,14 @@ impl<TItem: Clone, TExt: Clone + Add<Output = TExt>> Add
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// otherwise right shadows left
|
// otherwise right shadows left
|
||||||
self.items.insert(key, right);
|
new_items.insert(key, right);
|
||||||
}
|
}
|
||||||
|
new_items.extend(self.items.into_iter());
|
||||||
self.imports.extend(imports.into_iter());
|
self.imports.extend(imports.into_iter());
|
||||||
self.extra = self.extra + extra;
|
Module {
|
||||||
self
|
items: new_items,
|
||||||
|
imports: self.imports,
|
||||||
|
extra: self.extra + extra,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,11 +56,8 @@ fn mk_vec(pattern: &[Expr]) -> VecMatcher {
|
|||||||
pattern.last().map(vec_attrs).is_some(),
|
pattern.last().map(vec_attrs).is_some(),
|
||||||
"pattern must end with a vectorial"
|
"pattern must end with a vectorial"
|
||||||
);
|
);
|
||||||
let (left, (key, prio, nonzero), right) = split_at_max_vec(pattern)
|
let (left, (key, _, nonzero), right) = split_at_max_vec(pattern)
|
||||||
.expect("pattern must have vectorial placeholders at least at either end");
|
.expect("pattern must have vectorial placeholders at least at either end");
|
||||||
if prio >= 1 {
|
|
||||||
println!("Nondefault priority {} found", prio)
|
|
||||||
}
|
|
||||||
let r_sep_size = scal_cnt(right.iter());
|
let r_sep_size = scal_cnt(right.iter());
|
||||||
let (r_sep, r_side) = right.split_at(r_sep_size);
|
let (r_sep, r_side) = right.split_at(r_sep_size);
|
||||||
let l_sep_size = scal_cnt(left.iter().rev());
|
let l_sep_size = scal_cnt(left.iter().rev());
|
||||||
|
|||||||
@@ -114,29 +114,25 @@ impl<M: Matcher> Repository<M> {
|
|||||||
/// Attempt to run each rule in priority order `limit` times. Returns
|
/// Attempt to run each rule in priority order `limit` times. Returns
|
||||||
/// the final tree and the number of iterations left to the limit.
|
/// the final tree and the number of iterations left to the limit.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn long_step(
|
pub fn long_step(&self, code: &Expr, mut limit: usize) -> (Expr, usize) {
|
||||||
&self,
|
|
||||||
code: &Expr,
|
|
||||||
mut limit: usize,
|
|
||||||
) -> Result<(Expr, usize), RuleError> {
|
|
||||||
if limit == 0 {
|
if limit == 0 {
|
||||||
return Ok((code.clone(), 0));
|
return (code.clone(), 0);
|
||||||
}
|
}
|
||||||
if let Some(mut processed) = self.step(code) {
|
if let Some(mut processed) = self.step(code) {
|
||||||
limit -= 1;
|
limit -= 1;
|
||||||
if limit == 0 {
|
if limit == 0 {
|
||||||
return Ok((processed, 0));
|
return (processed, 0);
|
||||||
}
|
}
|
||||||
while let Some(out) = self.step(&processed) {
|
while let Some(out) = self.step(&processed) {
|
||||||
limit -= 1;
|
limit -= 1;
|
||||||
if limit == 0 {
|
if limit == 0 {
|
||||||
return Ok((out, 0));
|
return (out, 0);
|
||||||
}
|
}
|
||||||
processed = out;
|
processed = out;
|
||||||
}
|
}
|
||||||
Ok((processed, limit))
|
(processed, limit)
|
||||||
} else {
|
} else {
|
||||||
Ok((code.clone(), limit))
|
(code.clone(), limit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/stl/arithmetic_error.rs
Normal file
28
src/stl/arithmetic_error.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::foreign::ExternError;
|
||||||
|
|
||||||
|
/// Various errors produced by arithmetic operations
|
||||||
|
pub enum ArithmeticError {
|
||||||
|
/// Integer overflow
|
||||||
|
Overflow,
|
||||||
|
/// Float overflow
|
||||||
|
Infinity,
|
||||||
|
/// Division or modulo by zero
|
||||||
|
DivByZero,
|
||||||
|
/// Other, unexpected operation produced NaN
|
||||||
|
NaN,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ArithmeticError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::NaN => write!(f, "Operation resulted in NaN"),
|
||||||
|
Self::Overflow => write!(f, "Integer overflow"),
|
||||||
|
Self::Infinity => write!(f, "Operation resulted in Infinity"),
|
||||||
|
Self::DivByZero => write!(f, "A division by zero was attempted"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExternError for ArithmeticError {}
|
||||||
4
src/stl/bool.orc
Normal file
4
src/stl/bool.orc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export ...$a == ...$b =0x3p36=> (equals (...$a) (...$b))
|
||||||
|
export if ...$cond then ...$true else ...$false:1 =0x1p84=> (
|
||||||
|
ifthenelse (...$cond) (...$true) (...$false)
|
||||||
|
)
|
||||||
92
src/stl/bool.rs
Normal file
92
src/stl/bool.rs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::foreign::Atom;
|
||||||
|
use crate::interner::Interner;
|
||||||
|
use crate::pipeline::ConstTree;
|
||||||
|
use crate::representations::interpreted::{Clause, ExprInst};
|
||||||
|
use crate::representations::Primitive;
|
||||||
|
use crate::stl::litconv::with_lit;
|
||||||
|
use crate::stl::AssertionError;
|
||||||
|
use crate::{atomic_inert, define_fn, Literal, PathSet};
|
||||||
|
|
||||||
|
/// Booleans exposed to Orchid
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Boolean(pub bool);
|
||||||
|
atomic_inert!(Boolean);
|
||||||
|
|
||||||
|
impl From<bool> for Boolean {
|
||||||
|
fn from(value: bool) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&ExprInst> for Boolean {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: &ExprInst) -> Result<Self, Self::Error> {
|
||||||
|
let expr = value.expr();
|
||||||
|
if let Clause::P(Primitive::Atom(Atom(a))) = &expr.clause {
|
||||||
|
if let Some(b) = a.as_any().downcast_ref::<Boolean>() {
|
||||||
|
return Ok(*b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define_fn! {expr=x in
|
||||||
|
/// Compares the inner values if
|
||||||
|
///
|
||||||
|
/// - both values are char,
|
||||||
|
/// - both are string,
|
||||||
|
/// - both are either uint or num
|
||||||
|
Equals {
|
||||||
|
a: Literal as with_lit(x, |l| Ok(l.clone())),
|
||||||
|
b: Literal as with_lit(x, |l| Ok(l.clone()))
|
||||||
|
} => Ok(Boolean::from(match (a, b) {
|
||||||
|
(Literal::Char(c1), Literal::Char(c2)) => c1 == c2,
|
||||||
|
(Literal::Num(n1), Literal::Num(n2)) => n1 == n2,
|
||||||
|
(Literal::Str(s1), Literal::Str(s2)) => s1 == s2,
|
||||||
|
(Literal::Uint(i1), Literal::Uint(i2)) => i1 == i2,
|
||||||
|
(Literal::Num(n1), Literal::Uint(u1)) => *n1 == (*u1 as f64),
|
||||||
|
(Literal::Uint(u1), Literal::Num(n1)) => *n1 == (*u1 as f64),
|
||||||
|
(..) => AssertionError::fail(
|
||||||
|
b.clone().into(),
|
||||||
|
"the expected type"
|
||||||
|
)?,
|
||||||
|
}).to_atom_cls())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even though it's a ternary function, IfThenElse is implemented as an unary
|
||||||
|
// foreign function, as the rest of the logic can be defined in Orchid.
|
||||||
|
define_fn! {
|
||||||
|
/// Takes a boolean and two branches, runs the first if the bool is true, the
|
||||||
|
/// second if it's false.
|
||||||
|
IfThenElse = |x: &ExprInst| x.try_into()
|
||||||
|
.map_err(|_| AssertionError::ext(x.clone(), "a boolean"))
|
||||||
|
.map(|b: Boolean| if b.0 {Clause::Lambda {
|
||||||
|
args: Some(PathSet { steps: Rc::new(vec![]), next: None }),
|
||||||
|
body: Clause::Lambda {
|
||||||
|
args: None,
|
||||||
|
body: Clause::LambdaArg.wrap()
|
||||||
|
}.wrap(),
|
||||||
|
}} else {Clause::Lambda {
|
||||||
|
args: None,
|
||||||
|
body: Clause::Lambda {
|
||||||
|
args: Some(PathSet { steps: Rc::new(vec![]), next: None }),
|
||||||
|
body: Clause::LambdaArg.wrap(),
|
||||||
|
}.wrap(),
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bool(i: &Interner) -> ConstTree {
|
||||||
|
ConstTree::tree([(
|
||||||
|
i.i("bool"),
|
||||||
|
ConstTree::tree([
|
||||||
|
(i.i("ifthenelse"), ConstTree::xfn(IfThenElse)),
|
||||||
|
(i.i("equals"), ConstTree::xfn(Equals)),
|
||||||
|
(i.i("true"), ConstTree::atom(Boolean(true))),
|
||||||
|
(i.i("false"), ConstTree::atom(Boolean(false))),
|
||||||
|
]),
|
||||||
|
)])
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
use crate::atomic_inert;
|
|
||||||
use crate::foreign::Atom;
|
|
||||||
use crate::representations::interpreted::{Clause, ExprInst};
|
|
||||||
use crate::representations::Primitive;
|
|
||||||
|
|
||||||
/// Booleans exposed to Orchid
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub struct Boolean(pub bool);
|
|
||||||
atomic_inert!(Boolean);
|
|
||||||
|
|
||||||
impl From<bool> for Boolean {
|
|
||||||
fn from(value: bool) -> Self {
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<ExprInst> for Boolean {
|
|
||||||
type Error = ();
|
|
||||||
|
|
||||||
fn try_from(value: ExprInst) -> Result<Self, Self::Error> {
|
|
||||||
let expr = value.expr();
|
|
||||||
if let Clause::P(Primitive::Atom(Atom(a))) = &expr.clause {
|
|
||||||
if let Some(b) = a.as_any().downcast_ref::<Boolean>() {
|
|
||||||
return Ok(*b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use super::super::assertion_error::AssertionError;
|
|
||||||
use super::super::litconv::with_lit;
|
|
||||||
use super::boolean::Boolean;
|
|
||||||
use crate::representations::interpreted::ExprInst;
|
|
||||||
use crate::representations::Literal;
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
|
||||||
|
|
||||||
/// Compares the inner values if
|
|
||||||
///
|
|
||||||
/// - both values are char,
|
|
||||||
/// - both are string,
|
|
||||||
/// - both are either uint or num
|
|
||||||
///
|
|
||||||
/// Next state: [Equals1]
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Equals2;
|
|
||||||
externfn_impl!(Equals2, |_: &Self, x: ExprInst| Ok(Equals1 { x }));
|
|
||||||
|
|
||||||
/// Prev state: [Equals2]; Next state: [Equals0]
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Equals1 {
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(Equals1, x);
|
|
||||||
atomic_impl!(Equals1);
|
|
||||||
externfn_impl!(Equals1, |this: &Self, x: ExprInst| {
|
|
||||||
with_lit(&this.x, |l| Ok(Equals0 { a: l.clone(), x }))
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Prev state: [Equals1]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Equals0 {
|
|
||||||
a: Literal,
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(Equals0, x);
|
|
||||||
atomic_impl!(Equals0, |Self { a, x }: &Self, _| {
|
|
||||||
let eqls = with_lit(x, |l| {
|
|
||||||
Ok(match (a, l) {
|
|
||||||
(Literal::Char(c1), Literal::Char(c2)) => c1 == c2,
|
|
||||||
(Literal::Num(n1), Literal::Num(n2)) => n1 == n2,
|
|
||||||
(Literal::Str(s1), Literal::Str(s2)) => s1 == s2,
|
|
||||||
(Literal::Uint(i1), Literal::Uint(i2)) => i1 == i2,
|
|
||||||
(Literal::Num(n1), Literal::Uint(u1)) => *n1 == (*u1 as f64),
|
|
||||||
(Literal::Uint(u1), Literal::Num(n1)) => *n1 == (*u1 as f64),
|
|
||||||
(..) => AssertionError::fail(x.clone(), "the expected type")?,
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
Ok(Boolean::from(eqls).to_atom_cls())
|
|
||||||
});
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use super::super::assertion_error::AssertionError;
|
|
||||||
use super::Boolean;
|
|
||||||
use crate::representations::interpreted::{Clause, ExprInst};
|
|
||||||
use crate::representations::PathSet;
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
|
||||||
|
|
||||||
/// Takes a boolean and two branches, runs the first if the bool is true, the
|
|
||||||
/// second if it's false.
|
|
||||||
///
|
|
||||||
/// Next state: [IfThenElse0]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct IfThenElse1;
|
|
||||||
externfn_impl!(IfThenElse1, |_: &Self, x: ExprInst| Ok(IfThenElse0 { x }));
|
|
||||||
|
|
||||||
/// Prev state: [IfThenElse1]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct IfThenElse0 {
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(IfThenElse0, x);
|
|
||||||
atomic_impl!(IfThenElse0, |this: &Self, _| {
|
|
||||||
let Boolean(b) = this
|
|
||||||
.x
|
|
||||||
.clone()
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| AssertionError::ext(this.x.clone(), "a boolean"))?;
|
|
||||||
Ok(if b {
|
|
||||||
Clause::Lambda {
|
|
||||||
args: Some(PathSet { steps: Rc::new(vec![]), next: None }),
|
|
||||||
body: Clause::Lambda { args: None, body: Clause::LambdaArg.wrap() }
|
|
||||||
.wrap(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Clause::Lambda {
|
|
||||||
args: None,
|
|
||||||
body: Clause::Lambda {
|
|
||||||
args: Some(PathSet { steps: Rc::new(vec![]), next: None }),
|
|
||||||
body: Clause::LambdaArg.wrap(),
|
|
||||||
}
|
|
||||||
.wrap(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
mod boolean;
|
|
||||||
mod equals;
|
|
||||||
mod ifthenelse;
|
|
||||||
pub use boolean::Boolean;
|
|
||||||
|
|
||||||
use crate::interner::Interner;
|
|
||||||
use crate::pipeline::ConstTree;
|
|
||||||
|
|
||||||
pub fn bool(i: &Interner) -> ConstTree {
|
|
||||||
ConstTree::tree([
|
|
||||||
(i.i("ifthenelse"), ConstTree::xfn(ifthenelse::IfThenElse1)),
|
|
||||||
(i.i("equals"), ConstTree::xfn(equals::Equals2)),
|
|
||||||
(i.i("true"), ConstTree::atom(Boolean(true))),
|
|
||||||
(i.i("false"), ConstTree::atom(Boolean(false))),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
67
src/stl/conv.rs
Normal file
67
src/stl/conv.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
use chumsky::Parser;
|
||||||
|
use ordered_float::NotNan;
|
||||||
|
|
||||||
|
use super::litconv::with_lit;
|
||||||
|
use super::{ArithmeticError, AssertionError};
|
||||||
|
use crate::foreign::ExternError;
|
||||||
|
use crate::interner::Interner;
|
||||||
|
use crate::parse::{float_parser, int_parser};
|
||||||
|
use crate::pipeline::ConstTree;
|
||||||
|
use crate::{define_fn, Literal};
|
||||||
|
|
||||||
|
define_fn! {
|
||||||
|
/// parse a number. Accepts the same syntax Orchid does.
|
||||||
|
ToFloat = |x| with_lit(x, |l| match l {
|
||||||
|
Literal::Str(s) => float_parser()
|
||||||
|
.parse(s.as_str())
|
||||||
|
.map_err(|_| AssertionError::ext(
|
||||||
|
x.clone(),
|
||||||
|
"cannot be parsed into a float"
|
||||||
|
)),
|
||||||
|
Literal::Num(n) => Ok(*n),
|
||||||
|
Literal::Uint(i) => NotNan::new(*i as f64)
|
||||||
|
.map_err(|_| ArithmeticError::NaN.into_extern()),
|
||||||
|
Literal::Char(char) => char
|
||||||
|
.to_digit(10)
|
||||||
|
.map(|i| NotNan::new(i as f64).expect("u32 to f64 yielded NaN"))
|
||||||
|
.ok_or_else(|| AssertionError::ext(x.clone(), "is not a decimal digit")),
|
||||||
|
}).map(|nn| Literal::Num(nn).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
define_fn! {
|
||||||
|
/// Parse an unsigned integer. Accepts the same formats Orchid does. If the
|
||||||
|
/// input is a number, floors it.
|
||||||
|
ToUint = |x| with_lit(x, |l| match l {
|
||||||
|
Literal::Str(s) => int_parser()
|
||||||
|
.parse(s.as_str())
|
||||||
|
.map_err(|_| AssertionError::ext(
|
||||||
|
x.clone(),
|
||||||
|
"cannot be parsed into an unsigned int",
|
||||||
|
)),
|
||||||
|
Literal::Num(n) => Ok(n.floor() as u64),
|
||||||
|
Literal::Uint(i) => Ok(*i),
|
||||||
|
Literal::Char(char) => char
|
||||||
|
.to_digit(10)
|
||||||
|
.map(u64::from)
|
||||||
|
.ok_or(AssertionError::ext(x.clone(), "is not a decimal digit")),
|
||||||
|
}).map(|u| Literal::Uint(u).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
define_fn! {
|
||||||
|
/// Convert a literal to a string using Rust's conversions for floats, chars and
|
||||||
|
/// uints respectively
|
||||||
|
ToString = |x| with_lit(x, |l| Ok(match l {
|
||||||
|
Literal::Char(c) => c.to_string(),
|
||||||
|
Literal::Uint(i) => i.to_string(),
|
||||||
|
Literal::Num(n) => n.to_string(),
|
||||||
|
Literal::Str(s) => s.clone(),
|
||||||
|
})).map(|s| Literal::Str(s).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn conv(i: &Interner) -> ConstTree {
|
||||||
|
ConstTree::tree([
|
||||||
|
(i.i("to_float"), ConstTree::xfn(ToFloat)),
|
||||||
|
(i.i("to_uint"), ConstTree::xfn(ToUint)),
|
||||||
|
(i.i("to_string"), ConstTree::xfn(ToString)),
|
||||||
|
])
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
use crate::interner::Interner;
|
|
||||||
use crate::pipeline::ConstTree;
|
|
||||||
|
|
||||||
mod parse_float;
|
|
||||||
mod parse_uint;
|
|
||||||
mod to_string;
|
|
||||||
|
|
||||||
pub fn conv(i: &Interner) -> ConstTree {
|
|
||||||
ConstTree::tree([
|
|
||||||
(i.i("parse_float"), ConstTree::xfn(parse_float::ParseFloat1)),
|
|
||||||
(i.i("parse_uint"), ConstTree::xfn(parse_uint::ParseUint1)),
|
|
||||||
(i.i("to_string"), ConstTree::xfn(to_string::ToString1)),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use chumsky::Parser;
|
|
||||||
|
|
||||||
use super::super::assertion_error::AssertionError;
|
|
||||||
use super::super::litconv::with_lit;
|
|
||||||
use crate::parse::float_parser;
|
|
||||||
use crate::representations::interpreted::ExprInst;
|
|
||||||
use crate::representations::Literal;
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
|
||||||
|
|
||||||
/// parse a number. Accepts the same syntax Orchid does
|
|
||||||
///
|
|
||||||
/// Next state: [ParseFloat0]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ParseFloat1;
|
|
||||||
externfn_impl!(ParseFloat1, |_: &Self, x: ExprInst| Ok(ParseFloat0 { x }));
|
|
||||||
|
|
||||||
/// Prev state: [ParseFloat1]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ParseFloat0 {
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(ParseFloat0, x);
|
|
||||||
atomic_impl!(ParseFloat0, |Self { x }: &Self, _| {
|
|
||||||
let number = with_lit(x, |l| {
|
|
||||||
Ok(match l {
|
|
||||||
Literal::Str(s) => {
|
|
||||||
let parser = float_parser();
|
|
||||||
parser.parse(s.as_str()).map_err(|_| {
|
|
||||||
AssertionError::ext(x.clone(), "cannot be parsed into a float")
|
|
||||||
})?
|
|
||||||
},
|
|
||||||
Literal::Num(n) => *n,
|
|
||||||
Literal::Uint(i) => (*i as u32).into(),
|
|
||||||
Literal::Char(char) => char
|
|
||||||
.to_digit(10)
|
|
||||||
.ok_or(AssertionError::ext(x.clone(), "is not a decimal digit"))?
|
|
||||||
.into(),
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
Ok(number.into())
|
|
||||||
});
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use chumsky::Parser;
|
|
||||||
|
|
||||||
use super::super::assertion_error::AssertionError;
|
|
||||||
use super::super::litconv::with_lit;
|
|
||||||
use crate::parse::int_parser;
|
|
||||||
use crate::representations::interpreted::ExprInst;
|
|
||||||
use crate::representations::Literal;
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
|
||||||
|
|
||||||
/// Parse an unsigned integer. Accepts the same formats Orchid does. If the
|
|
||||||
/// input is a number, floors it.
|
|
||||||
///
|
|
||||||
/// Next state: [ParseUint0]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ParseUint1;
|
|
||||||
externfn_impl!(ParseUint1, |_: &Self, x: ExprInst| Ok(ParseUint0 { x }));
|
|
||||||
|
|
||||||
/// Prev state: [ParseUint1]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ParseUint0 {
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(ParseUint0, x);
|
|
||||||
atomic_impl!(ParseUint0, |Self { x }: &Self, _| {
|
|
||||||
let uint = with_lit(x, |l| {
|
|
||||||
Ok(match l {
|
|
||||||
Literal::Str(s) => {
|
|
||||||
let parser = int_parser();
|
|
||||||
parser.parse(s.as_str()).map_err(|_| {
|
|
||||||
AssertionError::ext(
|
|
||||||
x.clone(),
|
|
||||||
"cannot be parsed into an unsigned int",
|
|
||||||
)
|
|
||||||
})?
|
|
||||||
},
|
|
||||||
Literal::Num(n) => n.floor() as u64,
|
|
||||||
Literal::Uint(i) => *i,
|
|
||||||
Literal::Char(char) => char
|
|
||||||
.to_digit(10)
|
|
||||||
.ok_or(AssertionError::ext(x.clone(), "is not a decimal digit"))?
|
|
||||||
.into(),
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
Ok(uint.into())
|
|
||||||
});
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use super::super::litconv::with_lit;
|
|
||||||
use crate::representations::interpreted::ExprInst;
|
|
||||||
use crate::representations::Literal;
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
|
||||||
|
|
||||||
/// Convert a literal to a string using Rust's conversions for floats, chars and
|
|
||||||
/// uints respectively
|
|
||||||
///
|
|
||||||
/// Next state: [ToString0]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ToString1;
|
|
||||||
externfn_impl!(ToString1, |_: &Self, x: ExprInst| Ok(ToString0 { x }));
|
|
||||||
|
|
||||||
/// Prev state: [ToString1]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct ToString0 {
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(ToString0, x);
|
|
||||||
atomic_impl!(ToString0, |Self { x }: &Self, _| {
|
|
||||||
let string = with_lit(x, |l| {
|
|
||||||
Ok(match l {
|
|
||||||
Literal::Char(c) => c.to_string(),
|
|
||||||
Literal::Uint(i) => i.to_string(),
|
|
||||||
Literal::Num(n) => n.to_string(),
|
|
||||||
Literal::Str(s) => s.clone(),
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
Ok(string.into())
|
|
||||||
});
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use crate::foreign::{Atomic, AtomicReturn};
|
|
||||||
use crate::interner::InternedDisplay;
|
|
||||||
use crate::interpreter::Context;
|
|
||||||
use crate::representations::interpreted::ExprInst;
|
|
||||||
use crate::{atomic_defaults, externfn_impl};
|
|
||||||
|
|
||||||
/// Print and return whatever expression is in the argument without normalizing
|
|
||||||
/// it.
|
|
||||||
///
|
|
||||||
/// Next state: [Debug1]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Debug2;
|
|
||||||
externfn_impl!(Debug2, |_: &Self, x: ExprInst| Ok(Debug1 { x }));
|
|
||||||
|
|
||||||
/// Prev state: [Debug2]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Debug1 {
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
impl Atomic for Debug1 {
|
|
||||||
atomic_defaults!();
|
|
||||||
fn run(&self, ctx: Context) -> crate::foreign::AtomicResult {
|
|
||||||
println!("{}", self.x.bundle(ctx.interner));
|
|
||||||
Ok(AtomicReturn {
|
|
||||||
clause: self.x.expr().clause.clone(),
|
|
||||||
gas: ctx.gas.map(|g| g - 1),
|
|
||||||
inert: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
use crate::interner::Interner;
|
|
||||||
use crate::pipeline::ConstTree;
|
|
||||||
|
|
||||||
mod debug;
|
|
||||||
mod io;
|
|
||||||
mod panic;
|
|
||||||
mod print;
|
|
||||||
mod readline;
|
|
||||||
|
|
||||||
pub use io::{handle, IO};
|
|
||||||
|
|
||||||
pub fn cpsio(i: &Interner) -> ConstTree {
|
|
||||||
ConstTree::tree([
|
|
||||||
(i.i("print"), ConstTree::xfn(print::Print2)),
|
|
||||||
(i.i("readline"), ConstTree::xfn(readline::Readln2)),
|
|
||||||
(i.i("debug"), ConstTree::xfn(debug::Debug2)),
|
|
||||||
(i.i("panic"), ConstTree::xfn(panic::Panic1)),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
use super::super::litconv::with_str;
|
|
||||||
use crate::foreign::ExternError;
|
|
||||||
use crate::representations::interpreted::ExprInst;
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
|
||||||
|
|
||||||
/// Takes a message, returns an [ExternError] unconditionally.
|
|
||||||
///
|
|
||||||
/// Next state: [Panic0]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Panic1;
|
|
||||||
externfn_impl!(Panic1, |_: &Self, x: ExprInst| Ok(Panic0 { x }));
|
|
||||||
|
|
||||||
/// Prev state: [Panic1]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Panic0 {
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(Panic0, x);
|
|
||||||
atomic_impl!(Panic0, |Self { x }: &Self, _| {
|
|
||||||
with_str(x, |s| Err(OrchidPanic(s.clone()).into_extern()))
|
|
||||||
});
|
|
||||||
|
|
||||||
/// An unrecoverable error in Orchid land. Of course, because Orchid is lazy, it
|
|
||||||
/// only applies to the expressions that use the one that generated it.
|
|
||||||
pub struct OrchidPanic(String);
|
|
||||||
|
|
||||||
impl Display for OrchidPanic {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "Orchid code panicked: {}", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExternError for OrchidPanic {}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use super::super::litconv::with_str;
|
|
||||||
use super::io::IO;
|
|
||||||
use crate::foreign::{Atomic, AtomicResult, AtomicReturn};
|
|
||||||
use crate::interpreter::Context;
|
|
||||||
use crate::representations::interpreted::ExprInst;
|
|
||||||
use crate::{atomic_defaults, atomic_impl, atomic_redirect, externfn_impl};
|
|
||||||
|
|
||||||
/// Wrap a string and the continuation into an [IO] event to be evaluated by the
|
|
||||||
/// embedder.
|
|
||||||
///
|
|
||||||
/// Next state: [Print1]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Print2;
|
|
||||||
externfn_impl!(Print2, |_: &Self, x: ExprInst| Ok(Print1 { x }));
|
|
||||||
|
|
||||||
/// Prev state: [Print2]; Next state: [Print0]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Print1 {
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(Print1, x);
|
|
||||||
atomic_impl!(Print1);
|
|
||||||
externfn_impl!(Print1, |this: &Self, x: ExprInst| {
|
|
||||||
with_str(&this.x, |s| Ok(Print0 { s: s.clone(), x }))
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Prev state: [Print1]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Print0 {
|
|
||||||
s: String,
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
impl Atomic for Print0 {
|
|
||||||
atomic_defaults!();
|
|
||||||
fn run(&self, ctx: Context) -> AtomicResult {
|
|
||||||
Ok(AtomicReturn::from_data(IO::Print(self.s.clone(), self.x.clone()), ctx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use super::io::IO;
|
|
||||||
use crate::foreign::{Atomic, AtomicResult, AtomicReturn};
|
|
||||||
use crate::interpreter::Context;
|
|
||||||
use crate::representations::interpreted::ExprInst;
|
|
||||||
use crate::{atomic_defaults, externfn_impl};
|
|
||||||
|
|
||||||
/// Create an [IO] event that reads a line form standard input and calls the
|
|
||||||
/// continuation with it.
|
|
||||||
///
|
|
||||||
/// Next state: [Readln1]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Readln2;
|
|
||||||
externfn_impl!(Readln2, |_: &Self, x: ExprInst| Ok(Readln1 { x }));
|
|
||||||
|
|
||||||
/// Prev state: [Readln2]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Readln1 {
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
impl Atomic for Readln1 {
|
|
||||||
atomic_defaults!();
|
|
||||||
fn run(&self, ctx: Context) -> AtomicResult {
|
|
||||||
Ok(AtomicReturn::from_data(IO::Readline(self.x.clone()), ctx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ export loop $r on (..$parameters) with ...$tail =0x5p129=> Y (\$r.
|
|||||||
bind_names ($name ..$rest) $payload =0x1p250=> \$name. bind_names (..$rest) $payload
|
bind_names ($name ..$rest) $payload =0x1p250=> \$name. bind_names (..$rest) $payload
|
||||||
bind_names () (...$payload) =0x1p250=> ...$payload
|
bind_names () (...$payload) =0x1p250=> ...$payload
|
||||||
|
|
||||||
export ...$prefix $ ...$suffix:1 =0x1p39=> ...$prefix (...$suffix)
|
export ...$prefix $ ...$suffix:1 =0x1p38=> ...$prefix (...$suffix)
|
||||||
export ...$prefix |> $fn ..$suffix:1 =0x2p32=> $fn (...$prefix) ..$suffix
|
export ...$prefix |> $fn ..$suffix:1 =0x2p32=> $fn (...$prefix) ..$suffix
|
||||||
|
|
||||||
export (...$argv) => ...$body =0x2p129=> (bind_names (...$argv) (...$body))
|
export (...$argv) => ...$body =0x2p129=> (bind_names (...$argv) (...$body))
|
||||||
29
src/stl/io/inspect.rs
Normal file
29
src/stl/io/inspect.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use crate::foreign::{Atomic, AtomicReturn};
|
||||||
|
use crate::interner::InternedDisplay;
|
||||||
|
use crate::interpreter::Context;
|
||||||
|
use crate::representations::interpreted::ExprInst;
|
||||||
|
use crate::{atomic_defaults, write_fn_step};
|
||||||
|
|
||||||
|
write_fn_step! {
|
||||||
|
/// Print and return whatever expression is in the argument without
|
||||||
|
/// normalizing it.
|
||||||
|
pub Inspect > Inspect1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Inspect1 {
|
||||||
|
expr_inst: ExprInst,
|
||||||
|
}
|
||||||
|
impl Atomic for Inspect1 {
|
||||||
|
atomic_defaults!();
|
||||||
|
fn run(&self, ctx: Context) -> crate::foreign::AtomicResult {
|
||||||
|
println!("{}", self.expr_inst.bundle(ctx.interner));
|
||||||
|
Ok(AtomicReturn {
|
||||||
|
clause: self.expr_inst.expr().clause.clone(),
|
||||||
|
gas: ctx.gas.map(|g| g - 1),
|
||||||
|
inert: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/stl/io/mod.rs
Normal file
30
src/stl/io/mod.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
use crate::interner::Interner;
|
||||||
|
use crate::pipeline::ConstTree;
|
||||||
|
|
||||||
|
mod command;
|
||||||
|
mod inspect;
|
||||||
|
mod panic;
|
||||||
|
mod print;
|
||||||
|
mod readline;
|
||||||
|
|
||||||
|
pub use command::{handle, IO};
|
||||||
|
|
||||||
|
pub fn io(i: &Interner, allow_impure: bool) -> ConstTree {
|
||||||
|
let pure = ConstTree::tree([(
|
||||||
|
i.i("io"),
|
||||||
|
ConstTree::tree([
|
||||||
|
(i.i("print"), ConstTree::xfn(print::Print)),
|
||||||
|
(i.i("readline"), ConstTree::xfn(readline::ReadLn)),
|
||||||
|
(i.i("panic"), ConstTree::xfn(panic::Panic)),
|
||||||
|
]),
|
||||||
|
)]);
|
||||||
|
if !allow_impure {
|
||||||
|
pure
|
||||||
|
} else {
|
||||||
|
pure
|
||||||
|
+ ConstTree::tree([(
|
||||||
|
i.i("io"),
|
||||||
|
ConstTree::tree([(i.i("debug"), ConstTree::xfn(inspect::Inspect))]),
|
||||||
|
)])
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/stl/io/panic.rs
Normal file
21
src/stl/io/panic.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use super::super::litconv::with_str;
|
||||||
|
use crate::define_fn;
|
||||||
|
use crate::foreign::ExternError;
|
||||||
|
|
||||||
|
define_fn! {
|
||||||
|
/// Takes a message, returns an [ExternError] unconditionally.
|
||||||
|
pub Panic = |x| with_str(x, |s| Err(OrchidPanic(s.clone()).into_extern()))
|
||||||
|
}
|
||||||
|
/// An unrecoverable error in Orchid land. Because Orchid is lazy, this only
|
||||||
|
/// invalidates expressions that reference the one that generated it.
|
||||||
|
pub struct OrchidPanic(String);
|
||||||
|
|
||||||
|
impl Display for OrchidPanic {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Orchid code panicked: {}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExternError for OrchidPanic {}
|
||||||
31
src/stl/io/print.rs
Normal file
31
src/stl/io/print.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use super::super::litconv::with_str;
|
||||||
|
use super::command::IO;
|
||||||
|
use crate::foreign::{Atomic, AtomicResult, AtomicReturn};
|
||||||
|
use crate::interpreter::Context;
|
||||||
|
use crate::representations::interpreted::ExprInst;
|
||||||
|
use crate::{atomic_defaults, write_fn_step};
|
||||||
|
|
||||||
|
write_fn_step! {
|
||||||
|
/// Wrap a string and the continuation into an [IO] event to be evaluated by
|
||||||
|
/// the embedder.
|
||||||
|
pub Print > Print1
|
||||||
|
}
|
||||||
|
write_fn_step! {
|
||||||
|
Print1 {}
|
||||||
|
Print0 where message = x => with_str(x, |s| Ok(s.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Print0 {
|
||||||
|
message: String,
|
||||||
|
expr_inst: ExprInst,
|
||||||
|
}
|
||||||
|
impl Atomic for Print0 {
|
||||||
|
atomic_defaults!();
|
||||||
|
fn run(&self, ctx: Context) -> AtomicResult {
|
||||||
|
let command = IO::Print(self.message.clone(), self.expr_inst.clone());
|
||||||
|
Ok(AtomicReturn::from_data(command, ctx))
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/stl/io/readline.rs
Normal file
25
src/stl/io/readline.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use super::command::IO;
|
||||||
|
use crate::foreign::{Atomic, AtomicResult, AtomicReturn};
|
||||||
|
use crate::interpreter::Context;
|
||||||
|
use crate::representations::interpreted::ExprInst;
|
||||||
|
use crate::{atomic_defaults, write_fn_step};
|
||||||
|
|
||||||
|
write_fn_step! {
|
||||||
|
/// Create an [IO] event that reads a line form standard input and calls the
|
||||||
|
/// continuation with it.
|
||||||
|
pub ReadLn > ReadLn1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct ReadLn1 {
|
||||||
|
expr_inst: ExprInst,
|
||||||
|
}
|
||||||
|
impl Atomic for ReadLn1 {
|
||||||
|
atomic_defaults!();
|
||||||
|
fn run(&self, ctx: Context) -> AtomicResult {
|
||||||
|
let command = IO::Readline(self.expr_inst.clone());
|
||||||
|
Ok(AtomicReturn::from_data(command, ctx))
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/stl/known.orc
Normal file
1
src/stl/known.orc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export ::(,)
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import option
|
import super::(option, fn::*, bool::*, known::*, num::*,)
|
||||||
import super::fn::*
|
|
||||||
|
|
||||||
pair := \a.\b. \f. f a b
|
pair := \a.\b. \f. f a b
|
||||||
|
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
import list
|
import super::(bool::*, fn::*, known::*, list, option, proc::*)
|
||||||
import option
|
import std::io::panic
|
||||||
import fn::*
|
|
||||||
import std::to_string
|
|
||||||
import std::debug
|
|
||||||
|
|
||||||
-- utilities for using lists as pairs
|
-- utilities for using lists as pairs
|
||||||
|
|
||||||
@@ -1,13 +1,68 @@
|
|||||||
|
use hashbrown::HashMap;
|
||||||
|
use rust_embed::RustEmbed;
|
||||||
|
|
||||||
use super::bool::bool;
|
use super::bool::bool;
|
||||||
use super::conv::conv;
|
use super::conv::conv;
|
||||||
use super::cpsio::cpsio;
|
use super::io::io;
|
||||||
use super::num::num;
|
use super::num::num;
|
||||||
use super::str::str;
|
use super::str::str;
|
||||||
use crate::interner::Interner;
|
use crate::interner::{Interner, Sym};
|
||||||
use crate::pipeline::ConstTree;
|
use crate::pipeline::file_loader::mk_embed_cache;
|
||||||
|
use crate::pipeline::{from_const_tree, parse_layer, ProjectTree};
|
||||||
|
use crate::sourcefile::{FileEntry, Import};
|
||||||
|
|
||||||
|
/// Feature flags for the STL.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct StlOptions {
|
||||||
|
/// Whether impure functions (such as io::debug) are allowed. An embedder
|
||||||
|
/// would typically disable this flag
|
||||||
|
pub impure: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RustEmbed)]
|
||||||
|
#[folder = "src/stl"]
|
||||||
|
#[prefix = "std/"]
|
||||||
|
#[include = "*.orc"]
|
||||||
|
struct StlEmbed;
|
||||||
|
|
||||||
|
// TODO: fix all orc modules to not rely on prelude
|
||||||
|
|
||||||
/// Build the standard library used by the interpreter by combining the other
|
/// Build the standard library used by the interpreter by combining the other
|
||||||
/// libraries
|
/// libraries
|
||||||
pub fn mk_stl(i: &Interner) -> ConstTree {
|
pub fn mk_stl(i: &Interner, options: StlOptions) -> ProjectTree {
|
||||||
cpsio(i) + conv(i) + bool(i) + str(i) + num(i)
|
let const_tree = from_const_tree(
|
||||||
|
HashMap::from([(
|
||||||
|
i.i("std"),
|
||||||
|
io(i, options.impure) + conv(i) + bool(i) + str(i) + num(i),
|
||||||
|
)]),
|
||||||
|
&[i.i("std")],
|
||||||
|
i,
|
||||||
|
);
|
||||||
|
let ld_cache = mk_embed_cache::<StlEmbed>(".orc", i);
|
||||||
|
parse_layer(
|
||||||
|
&StlEmbed::iter()
|
||||||
|
.map(|path| -> Sym {
|
||||||
|
let segtoks = path
|
||||||
|
.strip_suffix(".orc")
|
||||||
|
.expect("the embed is filtered for suffix")
|
||||||
|
.split('/')
|
||||||
|
.map(|segment| i.i(segment))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
i.i(&segtoks[..])
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()[..],
|
||||||
|
&|p| ld_cache.find(&p),
|
||||||
|
&const_tree,
|
||||||
|
&[],
|
||||||
|
i,
|
||||||
|
)
|
||||||
|
.expect("Parse error in STL")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate prelude lines to be injected to every module compiled with the STL
|
||||||
|
pub fn mk_prelude(i: &Interner) -> Vec<FileEntry> {
|
||||||
|
vec![FileEntry::Import(vec![Import {
|
||||||
|
path: i.i(&[i.i("std"), i.i("prelude")][..]),
|
||||||
|
name: None,
|
||||||
|
}])]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
//! Constants exposed to usercode by the interpreter
|
//! Constants exposed to usercode by the interpreter
|
||||||
|
mod arithmetic_error;
|
||||||
mod assertion_error;
|
mod assertion_error;
|
||||||
mod bool;
|
mod bool;
|
||||||
mod conv;
|
mod conv;
|
||||||
mod cpsio;
|
mod io;
|
||||||
pub mod litconv;
|
pub mod litconv;
|
||||||
mod mk_stl;
|
mod mk_stl;
|
||||||
mod num;
|
mod num;
|
||||||
mod runtime_error;
|
mod runtime_error;
|
||||||
mod str;
|
mod str;
|
||||||
|
|
||||||
|
pub use arithmetic_error::ArithmeticError;
|
||||||
pub use assertion_error::AssertionError;
|
pub use assertion_error::AssertionError;
|
||||||
pub use cpsio::{handle as handleIO, IO};
|
pub use io::{handle as handleIO, IO};
|
||||||
pub use mk_stl::mk_stl;
|
pub use mk_stl::{mk_prelude, mk_stl, StlOptions};
|
||||||
pub use runtime_error::RuntimeError;
|
pub use runtime_error::RuntimeError;
|
||||||
|
|
||||||
pub use self::bool::Boolean;
|
pub use self::bool::Boolean;
|
||||||
|
|||||||
5
src/stl/num.orc
Normal file
5
src/stl/num.orc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export ...$a + ...$b =0x2p36=> (add (...$a) (...$b))
|
||||||
|
export ...$a - ...$b:1 =0x2p36=> (subtract (...$a) (...$b))
|
||||||
|
export ...$a * ...$b =0x1p36=> (multiply (...$a) (...$b))
|
||||||
|
export ...$a % ...$b:1 =0x1p36=> (remainder (...$a) (...$b))
|
||||||
|
export ...$a / ...$b:1 =0x1p36=> (divide (...$a) (...$b))
|
||||||
149
src/stl/num.rs
Normal file
149
src/stl/num.rs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use ordered_float::NotNan;
|
||||||
|
|
||||||
|
use super::litconv::with_lit;
|
||||||
|
use super::{ArithmeticError, AssertionError};
|
||||||
|
use crate::define_fn;
|
||||||
|
use crate::foreign::ExternError;
|
||||||
|
use crate::interner::Interner;
|
||||||
|
use crate::pipeline::ConstTree;
|
||||||
|
use crate::representations::interpreted::{Clause, ExprInst};
|
||||||
|
use crate::representations::{Literal, Primitive};
|
||||||
|
|
||||||
|
// region: Numeric, type to handle floats and uints together
|
||||||
|
|
||||||
|
/// A number, either floating point or unsigned int, visible to Orchid.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Numeric {
|
||||||
|
/// A nonnegative integer such as a size, index or count
|
||||||
|
Uint(u64),
|
||||||
|
/// A float other than NaN. Orchid has no silent errors
|
||||||
|
Num(NotNan<f64>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Numeric {
|
||||||
|
fn as_f64(&self) -> f64 {
|
||||||
|
match self {
|
||||||
|
Numeric::Num(n) => **n,
|
||||||
|
Numeric::Uint(i) => *i as f64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrap a f64 in a Numeric
|
||||||
|
fn num(value: f64) -> Result<Self, Rc<dyn ExternError>> {
|
||||||
|
if value.is_finite() {
|
||||||
|
NotNan::new(value)
|
||||||
|
.map(Self::Num)
|
||||||
|
.map_err(|_| ArithmeticError::NaN.into_extern())
|
||||||
|
} else {
|
||||||
|
Err(ArithmeticError::Infinity.into_extern())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&ExprInst> for Numeric {
|
||||||
|
type Error = Rc<dyn ExternError>;
|
||||||
|
fn try_from(value: &ExprInst) -> Result<Self, Self::Error> {
|
||||||
|
with_lit(value, |l| match l {
|
||||||
|
Literal::Uint(i) => Ok(Numeric::Uint(*i)),
|
||||||
|
Literal::Num(n) => Ok(Numeric::Num(*n)),
|
||||||
|
_ => AssertionError::fail(value.clone(), "an integer or number")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Numeric> for Clause {
|
||||||
|
fn from(value: Numeric) -> Self {
|
||||||
|
Clause::P(Primitive::Literal(match value {
|
||||||
|
Numeric::Uint(i) => Literal::Uint(i),
|
||||||
|
Numeric::Num(n) => Literal::Num(n),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region: operations
|
||||||
|
|
||||||
|
define_fn! {
|
||||||
|
/// Add two numbers. If they're both uint, the output is uint. If either is
|
||||||
|
/// number, the output is number.
|
||||||
|
Add { a: Numeric, b: Numeric } => match (a, b) {
|
||||||
|
(Numeric::Uint(a), Numeric::Uint(b)) => {
|
||||||
|
a.checked_add(*b)
|
||||||
|
.map(Numeric::Uint)
|
||||||
|
.ok_or_else(|| ArithmeticError::Overflow.into_extern())
|
||||||
|
}
|
||||||
|
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(*(a + b)),
|
||||||
|
(Numeric::Num(a), Numeric::Uint(b)) | (Numeric::Uint(b), Numeric::Num(a))
|
||||||
|
=> Numeric::num(a.into_inner() + *b as f64),
|
||||||
|
}.map(Numeric::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
define_fn! {
|
||||||
|
/// Subtract a number from another. Always returns Number.
|
||||||
|
Subtract { a: Numeric, b: Numeric } => match (a, b) {
|
||||||
|
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::num(*a as f64 - *b as f64),
|
||||||
|
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(*(a - b)),
|
||||||
|
(Numeric::Num(a), Numeric::Uint(b)) => Numeric::num(**a - *b as f64),
|
||||||
|
(Numeric::Uint(a), Numeric::Num(b)) => Numeric::num(*a as f64 - **b),
|
||||||
|
}.map(Numeric::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
define_fn! {
|
||||||
|
/// Multiply two numbers. If they're both uint, the output is uint. If either
|
||||||
|
/// is number, the output is number.
|
||||||
|
Multiply { a: Numeric, b: Numeric } => match (a, b) {
|
||||||
|
(Numeric::Uint(a), Numeric::Uint(b)) => {
|
||||||
|
a.checked_mul(*b)
|
||||||
|
.map(Numeric::Uint)
|
||||||
|
.ok_or_else(|| ArithmeticError::Overflow.into_extern())
|
||||||
|
}
|
||||||
|
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(*(a * b)),
|
||||||
|
(Numeric::Uint(a), Numeric::Num(b)) | (Numeric::Num(b), Numeric::Uint(a))
|
||||||
|
=> Numeric::num(*a as f64 * **b),
|
||||||
|
}.map(Numeric::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
define_fn! {
|
||||||
|
/// Divide a number by another. Always returns Number.
|
||||||
|
Divide { a: Numeric, b: Numeric } => {
|
||||||
|
let a: f64 = a.as_f64();
|
||||||
|
let b: f64 = b.as_f64();
|
||||||
|
if b == 0.0 {
|
||||||
|
return Err(ArithmeticError::DivByZero.into_extern())
|
||||||
|
}
|
||||||
|
Numeric::num(a / b).map(Numeric::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
define_fn! {
|
||||||
|
/// Take the remainder of two numbers. If they're both uint, the output is
|
||||||
|
/// uint. If either is number, the output is number.
|
||||||
|
Remainder { a: Numeric, b: Numeric } => match (a, b) {
|
||||||
|
(Numeric::Uint(a), Numeric::Uint(b)) => {
|
||||||
|
a.checked_rem(*b)
|
||||||
|
.map(Numeric::Uint)
|
||||||
|
.ok_or_else(|| ArithmeticError::DivByZero.into_extern())
|
||||||
|
}
|
||||||
|
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(*(a % b)),
|
||||||
|
(Numeric::Uint(a), Numeric::Num(b)) => Numeric::num(*a as f64 % **b),
|
||||||
|
(Numeric::Num(a), Numeric::Uint(b)) => Numeric::num(**a % *b as f64),
|
||||||
|
}.map(Numeric::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
pub fn num(i: &Interner) -> ConstTree {
|
||||||
|
ConstTree::tree([(
|
||||||
|
i.i("num"),
|
||||||
|
ConstTree::tree([
|
||||||
|
(i.i("add"), ConstTree::xfn(Add)),
|
||||||
|
(i.i("subtract"), ConstTree::xfn(Subtract)),
|
||||||
|
(i.i("multiply"), ConstTree::xfn(Multiply)),
|
||||||
|
(i.i("divide"), ConstTree::xfn(Divide)),
|
||||||
|
(i.i("remainder"), ConstTree::xfn(Remainder)),
|
||||||
|
]),
|
||||||
|
)])
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
mod numeric;
|
|
||||||
pub mod operators;
|
|
||||||
pub use numeric::Numeric;
|
|
||||||
|
|
||||||
use crate::interner::Interner;
|
|
||||||
use crate::pipeline::ConstTree;
|
|
||||||
|
|
||||||
pub fn num(i: &Interner) -> ConstTree {
|
|
||||||
ConstTree::tree([
|
|
||||||
(i.i("add"), ConstTree::xfn(operators::add::Add2)),
|
|
||||||
(i.i("subtract"), ConstTree::xfn(operators::subtract::Subtract2)),
|
|
||||||
(i.i("multiply"), ConstTree::xfn(operators::multiply::Multiply2)),
|
|
||||||
(i.i("divide"), ConstTree::xfn(operators::divide::Divide2)),
|
|
||||||
(i.i("remainder"), ConstTree::xfn(operators::remainder::Remainder2)),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
use std::ops::{Add, Div, Mul, Rem, Sub};
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use ordered_float::NotNan;
|
|
||||||
|
|
||||||
use super::super::assertion_error::AssertionError;
|
|
||||||
use super::super::litconv::with_lit;
|
|
||||||
use crate::foreign::ExternError;
|
|
||||||
use crate::representations::interpreted::{Clause, ExprInst};
|
|
||||||
use crate::representations::{Literal, Primitive};
|
|
||||||
|
|
||||||
/// A number, either floating point or unsigned int, visible to Orchid.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub enum Numeric {
|
|
||||||
/// A nonnegative integer such as a size, index or count
|
|
||||||
Uint(u64),
|
|
||||||
/// A float other than NaN. Orchid has no silent errors
|
|
||||||
Num(NotNan<f64>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Numeric {
|
|
||||||
/// Wrap a f64 in a Numeric
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// if the value is NaN or Infinity.try_into()
|
|
||||||
fn num<T: Into<f64>>(value: T) -> Self {
|
|
||||||
let f = value.into();
|
|
||||||
assert!(f.is_finite(), "unrepresentable number");
|
|
||||||
NotNan::try_from(f).map(Self::Num).expect("not a number")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add for Numeric {
|
|
||||||
type Output = Numeric;
|
|
||||||
|
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
|
||||||
match (self, rhs) {
|
|
||||||
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::Uint(a + b),
|
|
||||||
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(a + b),
|
|
||||||
(Numeric::Uint(a), Numeric::Num(b))
|
|
||||||
| (Numeric::Num(b), Numeric::Uint(a)) =>
|
|
||||||
Numeric::num::<f64>(a as f64 + *b),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub for Numeric {
|
|
||||||
type Output = Numeric;
|
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
|
||||||
match (self, rhs) {
|
|
||||||
(Numeric::Uint(a), Numeric::Uint(b)) if b <= a => Numeric::Uint(a - b),
|
|
||||||
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::num(a as f64 - b as f64),
|
|
||||||
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(a - b),
|
|
||||||
(Numeric::Uint(a), Numeric::Num(b)) => Numeric::num(a as f64 - *b),
|
|
||||||
(Numeric::Num(a), Numeric::Uint(b)) => Numeric::num(*a - b as f64),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mul for Numeric {
|
|
||||||
type Output = Numeric;
|
|
||||||
|
|
||||||
fn mul(self, rhs: Self) -> Self::Output {
|
|
||||||
match (self, rhs) {
|
|
||||||
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::Uint(a * b),
|
|
||||||
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(a * b),
|
|
||||||
(Numeric::Uint(a), Numeric::Num(b))
|
|
||||||
| (Numeric::Num(b), Numeric::Uint(a)) =>
|
|
||||||
Numeric::Num(NotNan::new(a as f64).unwrap() * b),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Div for Numeric {
|
|
||||||
type Output = Numeric;
|
|
||||||
|
|
||||||
fn div(self, rhs: Self) -> Self::Output {
|
|
||||||
let a: f64 = self.into();
|
|
||||||
let b: f64 = rhs.into();
|
|
||||||
Numeric::num(a / b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rem for Numeric {
|
|
||||||
type Output = Numeric;
|
|
||||||
|
|
||||||
fn rem(self, rhs: Self) -> Self::Output {
|
|
||||||
match (self, rhs) {
|
|
||||||
(Numeric::Uint(a), Numeric::Uint(b)) => Numeric::Uint(a % b),
|
|
||||||
(Numeric::Num(a), Numeric::Num(b)) => Numeric::num(a % b),
|
|
||||||
(Numeric::Uint(a), Numeric::Num(b)) => Numeric::num(a as f64 % *b),
|
|
||||||
(Numeric::Num(a), Numeric::Uint(b)) => Numeric::num(*a % b as f64),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<ExprInst> for Numeric {
|
|
||||||
type Error = Rc<dyn ExternError>;
|
|
||||||
fn try_from(value: ExprInst) -> Result<Self, Self::Error> {
|
|
||||||
with_lit(&value.clone(), |l| match l {
|
|
||||||
Literal::Uint(i) => Ok(Numeric::Uint(*i)),
|
|
||||||
Literal::Num(n) => Ok(Numeric::Num(*n)),
|
|
||||||
_ => AssertionError::fail(value, "an integer or number")?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Numeric> for Clause {
|
|
||||||
fn from(value: Numeric) -> Self {
|
|
||||||
Clause::P(Primitive::Literal(match value {
|
|
||||||
Numeric::Uint(i) => Literal::Uint(i),
|
|
||||||
Numeric::Num(n) => Literal::Num(n),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Numeric> for String {
|
|
||||||
fn from(value: Numeric) -> Self {
|
|
||||||
match value {
|
|
||||||
Numeric::Uint(i) => i.to_string(),
|
|
||||||
Numeric::Num(n) => n.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Numeric> for f64 {
|
|
||||||
fn from(val: Numeric) -> Self {
|
|
||||||
match val {
|
|
||||||
Numeric::Num(n) => *n,
|
|
||||||
Numeric::Uint(i) => i as f64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use super::super::Numeric;
|
|
||||||
use crate::representations::interpreted::ExprInst;
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Add2;
|
|
||||||
externfn_impl!(Add2, |_: &Self, x: ExprInst| Ok(Add1 { x }));
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Add1 {
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(Add1, x);
|
|
||||||
atomic_impl!(Add1);
|
|
||||||
externfn_impl!(Add1, |this: &Self, x: ExprInst| {
|
|
||||||
let a: Numeric = this.x.clone().try_into()?;
|
|
||||||
Ok(Add0 { a, x })
|
|
||||||
});
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Add0 {
|
|
||||||
a: Numeric,
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(Add0, x);
|
|
||||||
atomic_impl!(Add0, |Self { a, x }: &Self, _| {
|
|
||||||
let b: Numeric = x.clone().try_into()?;
|
|
||||||
Ok((*a + b).into())
|
|
||||||
});
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use super::super::Numeric;
|
|
||||||
use crate::representations::interpreted::ExprInst;
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
|
||||||
|
|
||||||
/// Divides two numbers
|
|
||||||
///
|
|
||||||
/// Next state: [Divide1]
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Divide2;
|
|
||||||
externfn_impl!(Divide2, |_: &Self, x: ExprInst| Ok(Divide1 { x }));
|
|
||||||
|
|
||||||
/// Prev state: [Divide2]; Next state: [Divide0]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Divide1 {
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(Divide1, x);
|
|
||||||
atomic_impl!(Divide1);
|
|
||||||
externfn_impl!(Divide1, |this: &Self, x: ExprInst| {
|
|
||||||
let a: Numeric = this.x.clone().try_into()?;
|
|
||||||
Ok(Divide0 { a, x })
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Prev state: [Divide1]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Divide0 {
|
|
||||||
a: Numeric,
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(Divide0, x);
|
|
||||||
atomic_impl!(Divide0, |Self { a, x }: &Self, _| {
|
|
||||||
let b: Numeric = x.clone().try_into()?;
|
|
||||||
Ok((*a / b).into())
|
|
||||||
});
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
pub mod add;
|
|
||||||
pub mod divide;
|
|
||||||
pub mod multiply;
|
|
||||||
pub mod remainder;
|
|
||||||
pub mod subtract;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use super::super::Numeric;
|
|
||||||
use crate::representations::interpreted::ExprInst;
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
|
||||||
|
|
||||||
/// Multiplies two numbers
|
|
||||||
///
|
|
||||||
/// Next state: [Multiply1]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Multiply2;
|
|
||||||
externfn_impl!(Multiply2, |_: &Self, x: ExprInst| Ok(Multiply1 { x }));
|
|
||||||
|
|
||||||
/// Prev state: [Multiply2]; Next state: [Multiply0]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Multiply1 {
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(Multiply1, x);
|
|
||||||
atomic_impl!(Multiply1);
|
|
||||||
externfn_impl!(Multiply1, |this: &Self, x: ExprInst| {
|
|
||||||
let a: Numeric = this.x.clone().try_into()?;
|
|
||||||
Ok(Multiply0 { a, x })
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Prev state: [Multiply1]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Multiply0 {
|
|
||||||
a: Numeric,
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(Multiply0, x);
|
|
||||||
atomic_impl!(Multiply0, |Self { a, x }: &Self, _| {
|
|
||||||
let b: Numeric = x.clone().try_into()?;
|
|
||||||
Ok((*a * b).into())
|
|
||||||
});
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use super::super::Numeric;
|
|
||||||
use crate::representations::interpreted::ExprInst;
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
|
||||||
|
|
||||||
/// Takes the modulus of two numbers.
|
|
||||||
///
|
|
||||||
/// Next state: [Remainder1]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Remainder2;
|
|
||||||
externfn_impl!(Remainder2, |_: &Self, x: ExprInst| Ok(Remainder1 { x }));
|
|
||||||
|
|
||||||
/// Prev state: [Remainder2]; Next state: [Remainder0]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Remainder1 {
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(Remainder1, x);
|
|
||||||
atomic_impl!(Remainder1);
|
|
||||||
externfn_impl!(Remainder1, |this: &Self, x: ExprInst| {
|
|
||||||
let a: Numeric = this.x.clone().try_into()?;
|
|
||||||
Ok(Remainder0 { a, x })
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Prev state: [Remainder1]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Remainder0 {
|
|
||||||
a: Numeric,
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(Remainder0, x);
|
|
||||||
atomic_impl!(Remainder0, |Self { a, x }: &Self, _| {
|
|
||||||
let b: Numeric = x.clone().try_into()?;
|
|
||||||
Ok((*a % b).into())
|
|
||||||
});
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
use std::fmt::Debug;
|
|
||||||
|
|
||||||
use super::super::Numeric;
|
|
||||||
use crate::representations::interpreted::ExprInst;
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
|
||||||
|
|
||||||
/// Subtracts two numbers
|
|
||||||
///
|
|
||||||
/// Next state: [Subtract1]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Subtract2;
|
|
||||||
externfn_impl!(Subtract2, |_: &Self, x: ExprInst| Ok(Subtract1 { x }));
|
|
||||||
|
|
||||||
/// Prev state: [Subtract2]; Next state: [Subtract0]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Subtract1 {
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(Subtract1, x);
|
|
||||||
atomic_impl!(Subtract1);
|
|
||||||
externfn_impl!(Subtract1, |this: &Self, x: ExprInst| {
|
|
||||||
let a: Numeric = this.x.clone().try_into()?;
|
|
||||||
Ok(Subtract0 { a, x })
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Prev state: [Subtract1]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Subtract0 {
|
|
||||||
a: Numeric,
|
|
||||||
x: ExprInst,
|
|
||||||
}
|
|
||||||
atomic_redirect!(Subtract0, x);
|
|
||||||
atomic_impl!(Subtract0, |Self { a, x }: &Self, _| {
|
|
||||||
let b: Numeric = x.clone().try_into()?;
|
|
||||||
Ok((*a - b).into())
|
|
||||||
});
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import std::panic
|
import std::io::panic
|
||||||
|
|
||||||
export some := \v. \d.\f. f v
|
export some := \v. \d.\f. f v
|
||||||
export none := \d.\f. d
|
export none := \d.\f. d
|
||||||
15
src/stl/prelude.orc
Normal file
15
src/stl/prelude.orc
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import std::num::*
|
||||||
|
export ::(+, -, *, /, %)
|
||||||
|
import std::str::*
|
||||||
|
export ::(++)
|
||||||
|
import std::bool::*
|
||||||
|
export ::(==, if, then, else)
|
||||||
|
import std::fn::*
|
||||||
|
export ::(loop, on, with, $, |>, =>)
|
||||||
|
import std::list
|
||||||
|
import std::map
|
||||||
|
import std::option
|
||||||
|
export ::(list, map, option)
|
||||||
|
|
||||||
|
import std::known::*
|
||||||
|
export ::(,)
|
||||||
12
src/stl/proc.orc
Normal file
12
src/stl/proc.orc
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export do { ...$statement ; ...$rest:1 } =0x2p130=> statement (...$statement) do { ...$rest }
|
||||||
|
export do { ...$return } =0x1p130=> ...$return
|
||||||
|
|
||||||
|
export statement (let $name = ...$value) ...$next =0x1p230=> (
|
||||||
|
(\$name. ...$next) (...$value)
|
||||||
|
)
|
||||||
|
export statement (cps $name = ...$operation) ...$next =0x2p230=> (
|
||||||
|
(...$operation) \$name. ...$next
|
||||||
|
)
|
||||||
|
export statement (cps ...$operation) ...$next =0x1p230=> (
|
||||||
|
(...$operation) (...$next)
|
||||||
|
)
|
||||||
1
src/stl/str.orc
Normal file
1
src/stl/str.orc
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export ...$a ++ ...$b =0x4p36=> (concatenate (...$a) (...$b))
|
||||||
39
src/stl/str.rs
Normal file
39
src/stl/str.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use super::litconv::{with_str, with_uint};
|
||||||
|
use super::RuntimeError;
|
||||||
|
use crate::interner::Interner;
|
||||||
|
use crate::pipeline::ConstTree;
|
||||||
|
use crate::{define_fn, Literal};
|
||||||
|
|
||||||
|
define_fn! {expr=x in
|
||||||
|
/// Append a string to another
|
||||||
|
pub Concatenate {
|
||||||
|
a: String as with_str(x, |s| Ok(s.clone())),
|
||||||
|
b: String as with_str(x, |s| Ok(s.clone()))
|
||||||
|
} => Ok(Literal::Str(a.to_owned() + b).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
define_fn! {expr=x in
|
||||||
|
pub CharAt {
|
||||||
|
s: String as with_str(x, |s| Ok(s.clone())),
|
||||||
|
i: u64 as with_uint(x, Ok)
|
||||||
|
} => {
|
||||||
|
if let Some(c) = s.chars().nth(*i as usize) {
|
||||||
|
Ok(Literal::Char(c).into())
|
||||||
|
} else {
|
||||||
|
RuntimeError::fail(
|
||||||
|
"Character index out of bounds".to_string(),
|
||||||
|
"indexing string",
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn str(i: &Interner) -> ConstTree {
|
||||||
|
ConstTree::tree([(
|
||||||
|
i.i("str"),
|
||||||
|
ConstTree::tree([
|
||||||
|
(i.i("concatenate"), ConstTree::xfn(Concatenate)),
|
||||||
|
(i.i("char_at"), ConstTree::xfn(CharAt)),
|
||||||
|
]),
|
||||||
|
)])
|
||||||
|
}
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
use super::super::litconv::{with_str, with_uint};
|
|
||||||
use super::super::runtime_error::RuntimeError;
|
|
||||||
use crate::interpreted::Clause;
|
|
||||||
use crate::{write_fn_step, Literal, Primitive};
|
|
||||||
|
|
||||||
write_fn_step!(pub CharAt2 > CharAt1);
|
|
||||||
write_fn_step!(
|
|
||||||
CharAt1 {}
|
|
||||||
CharAt0 where s = x => with_str(x, |s| Ok(s.clone())) ;
|
|
||||||
);
|
|
||||||
write_fn_step!(
|
|
||||||
CharAt0 { s: String }
|
|
||||||
i = x => with_uint(x, Ok);
|
|
||||||
{
|
|
||||||
if let Some(c) = s.chars().nth(i as usize) {
|
|
||||||
Ok(Clause::P(Primitive::Literal(Literal::Char(c))))
|
|
||||||
} else {
|
|
||||||
RuntimeError::fail(
|
|
||||||
"Character index out of bounds".to_string(),
|
|
||||||
"indexing string",
|
|
||||||
)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
use super::super::litconv::with_str;
|
|
||||||
use crate::define_fn;
|
|
||||||
use crate::representations::interpreted::Clause;
|
|
||||||
use crate::representations::{Literal, Primitive};
|
|
||||||
|
|
||||||
define_fn! {expr=x in
|
|
||||||
/// Append a string to another
|
|
||||||
pub Concatenate {
|
|
||||||
a: String as with_str(x, |s| Ok(s.clone())),
|
|
||||||
b: String as with_str(x, |s| Ok(s.clone()))
|
|
||||||
} => {
|
|
||||||
Ok(Clause::P(Primitive::Literal(Literal::Str(a.to_owned() + &b))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
mod char_at;
|
|
||||||
mod concatenate;
|
|
||||||
|
|
||||||
use crate::interner::Interner;
|
|
||||||
use crate::pipeline::ConstTree;
|
|
||||||
|
|
||||||
pub fn str(i: &Interner) -> ConstTree {
|
|
||||||
ConstTree::tree([(
|
|
||||||
i.i("concatenate"),
|
|
||||||
ConstTree::xfn(concatenate::Concatenate),
|
|
||||||
)])
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ mod print_nname;
|
|||||||
mod pushed;
|
mod pushed;
|
||||||
mod replace_first;
|
mod replace_first;
|
||||||
mod side;
|
mod side;
|
||||||
|
mod split_max_prefix;
|
||||||
mod string_from_charset;
|
mod string_from_charset;
|
||||||
mod substack;
|
mod substack;
|
||||||
mod unwrap_or;
|
mod unwrap_or;
|
||||||
@@ -12,6 +13,7 @@ pub use print_nname::sym2string;
|
|||||||
pub use pushed::pushed;
|
pub use pushed::pushed;
|
||||||
pub use replace_first::replace_first;
|
pub use replace_first::replace_first;
|
||||||
pub use side::Side;
|
pub use side::Side;
|
||||||
|
pub use split_max_prefix::split_max_prefix;
|
||||||
pub use substack::{Stackframe, Substack, SubstackIterator};
|
pub use substack::{Stackframe, Substack, SubstackIterator};
|
||||||
pub(crate) use unwrap_or::unwrap_or;
|
pub(crate) use unwrap_or::unwrap_or;
|
||||||
pub mod iter;
|
pub mod iter;
|
||||||
|
|||||||
14
src/utils/split_max_prefix.rs
Normal file
14
src/utils/split_max_prefix.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/// Split off the longest prefix accepted by the validator
|
||||||
|
#[allow(clippy::type_complexity)] // FIXME couldn't find a good factoring
|
||||||
|
pub fn split_max_prefix<'a, T>(
|
||||||
|
path: &'a [T],
|
||||||
|
is_valid: &impl Fn(&[T]) -> bool,
|
||||||
|
) -> Option<(&'a [T], &'a [T])> {
|
||||||
|
for split in (0..=path.len()).rev() {
|
||||||
|
let (filename, subpath) = path.split_at(split);
|
||||||
|
if is_valid(filename) {
|
||||||
|
return Some((filename, subpath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
@@ -41,17 +41,13 @@ impl<'a, T> Substack<'a, T> {
|
|||||||
}
|
}
|
||||||
/// Create a new frame on top of this substack
|
/// Create a new frame on top of this substack
|
||||||
pub fn new_frame(&'a self, item: T) -> Stackframe<'a, T> {
|
pub fn new_frame(&'a self, item: T) -> Stackframe<'a, T> {
|
||||||
Stackframe { item, prev: self, len: self.opt().map_or(1, |s| s.len) }
|
Stackframe { item, prev: self, len: self.opt().map_or(1, |s| s.len + 1) }
|
||||||
}
|
}
|
||||||
/// obtain the previous stackframe if one exists
|
/// obtain the previous stackframe if one exists
|
||||||
/// TODO: this should return a [Substack]
|
/// TODO: this should return a [Substack]
|
||||||
pub fn pop(&'a self, count: usize) -> Option<&'a Stackframe<'a, T>> {
|
pub fn pop(&'a self, count: usize) -> Option<&'a Stackframe<'a, T>> {
|
||||||
if let Self::Frame(p) = self {
|
if let Self::Frame(p) = self {
|
||||||
if count == 0 {
|
if count == 0 { Some(p) } else { p.prev.pop(count - 1) }
|
||||||
Some(p)
|
|
||||||
} else {
|
|
||||||
p.prev.pop(count - 1)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
/// A macro version of [Option::unwrap_or_else] which supports flow
|
/// A macro version of [Option::unwrap_or_else] which supports flow
|
||||||
/// control statements such as `return` and `break` in the "else" branch.
|
/// control statements such as `return` and `break` in the "else" branch.
|
||||||
|
/// It also supports unwrapping concrete variants of other enums
|
||||||
macro_rules! unwrap_or {
|
macro_rules! unwrap_or {
|
||||||
($m:expr; $fail:expr) => {{
|
($m:expr; $fail:expr) => {{
|
||||||
if let Some(res) = ($m) { res } else { $fail }
|
if let Some(res) = ($m) { res } else { $fail }
|
||||||
}};
|
}};
|
||||||
|
($m:expr => $pattern:path; $fail:expr) => {{
|
||||||
|
if let $pattern(res) = ($m) { res } else { $fail }
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use unwrap_or;
|
pub(crate) use unwrap_or;
|
||||||
|
|||||||
Reference in New Issue
Block a user