bug fixes and performance improvements
This commit is contained in:
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pdf filter=lfs diff=lfs merge=lfs -text
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@
|
|||||||
*.out
|
*.out
|
||||||
*.pdf
|
*.pdf
|
||||||
*.gz
|
*.gz
|
||||||
|
!notes/papers/report/template/Figures/**
|
||||||
440
Cargo.lock
generated
440
Cargo.lock
generated
@@ -24,12 +24,73 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is-terminal",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.21.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.79"
|
version = "1.0.79"
|
||||||
@@ -42,6 +103,12 @@ 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 = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg_aliases"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chumsky"
|
name = "chumsky"
|
||||||
version = "0.9.2"
|
version = "0.9.2"
|
||||||
@@ -52,6 +119,54 @@ dependencies = [
|
|||||||
"stacker",
|
"stacker",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"bitflags",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.13",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dashmap"
|
name = "dashmap"
|
||||||
version = "4.0.2"
|
version = "4.0.2"
|
||||||
@@ -70,15 +185,36 @@ checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.6.1"
|
version = "1.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
|
||||||
|
dependencies = [
|
||||||
|
"errno-dragonfly",
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno-dragonfly"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.6"
|
version = "0.2.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -112,6 +248,12 @@ dependencies = [
|
|||||||
"ahash 0.8.3",
|
"ahash 0.8.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.2.6"
|
version = "0.2.6"
|
||||||
@@ -122,10 +264,48 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "hermit-abi"
|
||||||
version = "0.10.3"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
|
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "io-lifetimes"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.3.1",
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-terminal"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.3.1",
|
||||||
|
"io-lifetimes",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
@@ -142,15 +322,37 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.126"
|
version = "0.2.142"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mappable-rc"
|
name = "mappable-rc"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b65e7f462b4fbfe1a3c857747c9d027dd55faffaeffbca68f70d0becfe7e93c5"
|
checksum = "204651f31b0a6a7b2128d2b92c372cd94607b210c3a6b6e542c57a8cfd4db996"
|
||||||
|
|
||||||
|
[[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"
|
||||||
@@ -167,7 +369,7 @@ version = "1.15.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi 0.2.6",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -179,9 +381,11 @@ checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orchid"
|
name = "orchid"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"base64",
|
||||||
"chumsky",
|
"chumsky",
|
||||||
|
"clap",
|
||||||
"dyn-clone",
|
"dyn-clone",
|
||||||
"hashbrown 0.13.2",
|
"hashbrown 0.13.2",
|
||||||
"itertools",
|
"itertools",
|
||||||
@@ -189,23 +393,49 @@ dependencies = [
|
|||||||
"mappable-rc",
|
"mappable-rc",
|
||||||
"ordered-float",
|
"ordered-float",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
"static_init",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-float"
|
name = "ordered-float"
|
||||||
version = "3.0.0"
|
version = "3.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96bcbab4bfea7a59c2c0fe47211a1ac4e3e96bea6eb446d704f310bc5c732ae2"
|
checksum = "13a384337e997e6860ffbaa83708b2ef329fd8c54cb67a5f64d421e0f943254f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "parking_lot"
|
||||||
version = "1.0.39"
|
version = "0.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||||
|
dependencies = [
|
||||||
|
"instant",
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"instant",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"smallvec",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.56"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -221,13 +451,42 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.18"
|
version = "1.0.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.37.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"io-lifetimes",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.10.0"
|
version = "1.10.0"
|
||||||
@@ -248,10 +507,55 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "static_init"
|
||||||
version = "1.0.95"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
|
checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg_aliases",
|
||||||
|
"libc",
|
||||||
|
"parking_lot",
|
||||||
|
"parking_lot_core",
|
||||||
|
"static_init_macro",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "static_init_macro"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf"
|
||||||
|
dependencies = [
|
||||||
|
"cfg_aliases",
|
||||||
|
"memchr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -260,29 +564,35 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.31"
|
version = "1.0.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.31"
|
version = "1.0.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.13",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.0"
|
version = "1.0.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
@@ -292,9 +602,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.2+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
@@ -317,3 +627,69 @@ name = "winapi-x86_64-pc-windows-gnu"
|
|||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "orchid"
|
name = "orchid"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
authors = [
|
||||||
|
"Lawrence Bethlenfalvy <lbfalvy@protonmail.com>"
|
||||||
|
]
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@@ -15,3 +18,6 @@ itertools = "0.10"
|
|||||||
smallvec = { version = "1.10.0", features = ['const_generics'] }
|
smallvec = { version = "1.10.0", features = ['const_generics'] }
|
||||||
dyn-clone = "1.0.11"
|
dyn-clone = "1.0.11"
|
||||||
lasso = { version = "0.6.0", features = ['multi-threaded'] }
|
lasso = { version = "0.6.0", features = ['multi-threaded'] }
|
||||||
|
base64 = "0.21.0"
|
||||||
|
static_init = "1.0.3"
|
||||||
|
clap = { version = "4.2.7", features = ["derive"] }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import prelude::*
|
import prelude::*
|
||||||
import std::conv::(parse_float, to_string)
|
import std::(parse_float, to_string)
|
||||||
import std::cpsio::(readline, print)
|
import std::(readline, print)
|
||||||
import std::str::(concatenate)
|
import std::(concatenate)
|
||||||
|
|
||||||
export main := do{
|
export main := do{
|
||||||
cps data = readline;
|
cps data = readline;
|
||||||
@@ -20,3 +20,5 @@ export main := do{
|
|||||||
cps print (to_string result ++ "\n");
|
cps print (to_string result ++ "\n");
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- export main := 1 do { 1 ; 2 } 3
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
"[markdown]": {
|
"[markdown][latex]": {
|
||||||
"editor.unicodeHighlight.ambiguousCharacters": false,
|
"editor.unicodeHighlight.ambiguousCharacters": false,
|
||||||
"editor.unicodeHighlight.invisibleCharacters": false,
|
"editor.unicodeHighlight.invisibleCharacters": false,
|
||||||
"diffEditor.ignoreTrimWhitespace": false,
|
"diffEditor.ignoreTrimWhitespace": false,
|
||||||
@@ -23,13 +23,43 @@
|
|||||||
},
|
},
|
||||||
"[rust]": {
|
"[rust]": {
|
||||||
"editor.rulers": [74]
|
"editor.rulers": [74]
|
||||||
}
|
},
|
||||||
|
"rust-analyzer.showUnlinkedFileNotification": false,
|
||||||
|
"files.associations": {
|
||||||
|
"*.mjsd": "markdown"
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"extensions": {
|
"extensions": {
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"tomoki1207.pdf",
|
"tomoki1207.pdf",
|
||||||
"james-yu.latex-workshop",
|
"james-yu.latex-workshop",
|
||||||
"bungcip.better-toml"
|
"bungcip.better-toml",
|
||||||
|
"maptz.regionfolder",
|
||||||
|
"serayuzgur.crates",
|
||||||
|
"tamasfe.even-better-toml",
|
||||||
|
"haskell.haskell",
|
||||||
|
"justusadam.language-haskell",
|
||||||
|
"yzhang.markdown-all-in-one",
|
||||||
|
"goessner.mdmath",
|
||||||
|
"gruntfuggly.todo-tree"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"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": []
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
19
src/cli.rs
Normal file
19
src/cli.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use std::{fmt::Display, io::{stdin, BufRead, stdout, Write}};
|
||||||
|
|
||||||
|
pub fn prompt<T: Display, E: Display>(
|
||||||
|
prompt: &str,
|
||||||
|
default: T,
|
||||||
|
mut try_cast: impl FnMut(String) -> Result<T, E>
|
||||||
|
) -> T {
|
||||||
|
loop {
|
||||||
|
print!("{prompt} ({default}): ");
|
||||||
|
stdout().lock().flush().unwrap();
|
||||||
|
let mut input = String::with_capacity(100);
|
||||||
|
stdin().lock().read_line(&mut input).unwrap();
|
||||||
|
if input.len() == 0 {return default}
|
||||||
|
match try_cast(input) {
|
||||||
|
Ok(t) => return t,
|
||||||
|
Err(e) => println!("Error: {e}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
use itertools::Itertools;
|
|
||||||
use mappable_rc::Mrc;
|
|
||||||
|
|
||||||
use crate::utils::{collect_to_mrc, to_mrc_slice};
|
|
||||||
|
|
||||||
use crate::representations::typed::{Clause, Expr};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct Application<'a> {
|
|
||||||
id: u64,
|
|
||||||
value: &'a Expr,
|
|
||||||
types: bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn apply_lambda(app: Application, body: Expr) -> Expr {
|
|
||||||
// apply_lambda_expr_rec(id, value, body)
|
|
||||||
// .unwrap_or(body)
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn apply_lambda_expr_rec(
|
|
||||||
app@Application{ id, types, value }: Application, expr: &Expr
|
|
||||||
) -> Option<Expr> {
|
|
||||||
let Expr(clause, typ) = expr;
|
|
||||||
match clause {
|
|
||||||
Clause::LambdaArg(arg_id) | Clause::AutoArg(arg_id) if *arg_id == id => {
|
|
||||||
let full_typ =
|
|
||||||
value.1.iter()
|
|
||||||
.chain(typ.iter())
|
|
||||||
.cloned().collect_vec();
|
|
||||||
Some(Expr(value.0.to_owned(), full_typ))
|
|
||||||
}
|
|
||||||
cl => {
|
|
||||||
let new_cl = apply_lambda_clause_rec(app, cl);
|
|
||||||
let new_typ = if !types {None} else {
|
|
||||||
typ.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_lambda_clause_rec(
|
|
||||||
app: Application, clause: &Clause
|
|
||||||
) -> Option<Clause> {
|
|
||||||
match clause {
|
|
||||||
// Only element actually manipulated
|
|
||||||
Clause::LambdaArg(_) | Clause::AutoArg(_) => None,
|
|
||||||
// Traverse, yield Some if either had changed.
|
|
||||||
Clause::Apply(f, x) => {
|
|
||||||
let new_f = apply_lambda_expr_rec(app, f.as_ref());
|
|
||||||
let new_x = apply_lambda_expr_rec(app, x.as_ref());
|
|
||||||
match (new_f, new_x) { // Mind the shadows
|
|
||||||
(None, None) => None,
|
|
||||||
(None, Some(x)) => Some(Clause::Apply(f.clone(), Box::new(x))),
|
|
||||||
(Some(f), None) => Some(Clause::Apply(Box::new(f), x.clone())),
|
|
||||||
(Some(f), Some(x)) => Some(Clause::Apply(Box::new(f), Box::new(x)))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Clause::Lambda(own_id, t, b) => apply_lambda__traverse_param(id, value, own_id, t, b, Clause::Lambda),
|
|
||||||
Clause::Auto(own_id, t, b) => apply_lambda__traverse_param(id, value, own_id, t, b, Clause::Auto),
|
|
||||||
// Leaf nodes
|
|
||||||
Clause::Atom(_) | Clause::ExternFn(_) | Clause::Literal(_) => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_lambda__traverse_param(
|
|
||||||
id: u64, value: Mrc<Expr>,
|
|
||||||
own_id: u64, typ: Mrc<[Clause]>, b: Mrc<Expr>,
|
|
||||||
wrap: impl Fn(u64, Mrc<[Clause]>, Mrc<Expr>) -> Clause
|
|
||||||
) -> Option<Clause> {
|
|
||||||
let any_t = false;
|
|
||||||
let mut t_acc = vec![];
|
|
||||||
for t in typ.iter() {
|
|
||||||
let newt = apply_lambda_clause_rec(id, Mrc::clone(&value), t.clone());
|
|
||||||
any_t |= newt.is_some();
|
|
||||||
t_acc.push(newt.unwrap_or_else(|| t.clone()))
|
|
||||||
}
|
|
||||||
// Respect shadowing
|
|
||||||
let new_b = if own_id == id {None} else {
|
|
||||||
apply_lambda_expr_rec(id, value, Mrc::clone(&b))
|
|
||||||
};
|
|
||||||
if any_t { // mind the shadows
|
|
||||||
let typ = to_mrc_slice(t_acc);
|
|
||||||
if let Some(b) = new_b {
|
|
||||||
Some(wrap(own_id, typ, b))
|
|
||||||
} else {Some(wrap(own_id, typ, b))}
|
|
||||||
} else if let Some(b) = new_b {
|
|
||||||
Some(wrap(own_id, typ, b))
|
|
||||||
} else {Some(wrap(own_id, typ, b))}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
mod normalize;
|
|
||||||
mod partial_hash;
|
|
||||||
mod reduction_tree;
|
|
||||||
mod apply_lambda;
|
|
||||||
pub use apply_lambda::apply_lambda;
|
|
||||||
mod syntax_eq;
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
use mappable_rc::Mrc;
|
|
||||||
|
|
||||||
use crate::utils::collect_to_mrc;
|
|
||||||
|
|
||||||
use super::super::representations::typed::{Clause, Expr};
|
|
||||||
|
|
||||||
fn normalize(Expr(clause, typ): Expr) -> Expr {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect_autos(
|
|
||||||
Expr(clause, typ): Expr,
|
|
||||||
arg_types: Vec<Mrc<[Clause]>>,
|
|
||||||
indirect_argt_trees: Vec<Mrc<[Clause]>>,
|
|
||||||
sunk_types: &mut dyn Iterator<Item = Clause>
|
|
||||||
) -> (Vec<Mrc<[Clause]>>, Expr) {
|
|
||||||
if let Clause::Auto(argt, body) = clause {
|
|
||||||
|
|
||||||
}
|
|
||||||
else {(
|
|
||||||
arg_types,
|
|
||||||
Expr(
|
|
||||||
clause,
|
|
||||||
collect_to_mrc(
|
|
||||||
typ.iter().cloned()
|
|
||||||
.chain(sunk_types)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
use std::hash::{Hasher, Hash};
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
|
|
||||||
use crate::utils::ProtoMap;
|
|
||||||
|
|
||||||
use super::super::representations::typed::{Clause, Expr};
|
|
||||||
use super::super::utils::Stackframe;
|
|
||||||
|
|
||||||
const PARAMETRICS_INLINE_COUNT:usize = 5;
|
|
||||||
// type Parametrics<'a> = ProtoMap<'a, u64, bool, PARAMETRICS_INLINE_COUNT>;
|
|
||||||
|
|
||||||
/// Hash the parts of an expression that are required to be equal for syntactic equality.
|
|
||||||
pub fn partial_hash_rec<H: Hasher>(
|
|
||||||
Expr(clause, _): &Expr, state: &mut H,
|
|
||||||
parametrics: Option<&Stackframe<u64>>
|
|
||||||
) {
|
|
||||||
match clause {
|
|
||||||
// Skip autos
|
|
||||||
Clause::Auto(id, _, body) => {
|
|
||||||
partial_hash_rec(body, state, parametrics)
|
|
||||||
}
|
|
||||||
// Annotate everything else with a prefix
|
|
||||||
// - Recurse into the tree of lambdas and calls - classic lambda calc
|
|
||||||
Clause::Lambda(id, _, body) => {
|
|
||||||
state.write_u8(0);
|
|
||||||
partial_hash_rec(body, state, Some(&Stackframe::opush(parametrics, *id)))
|
|
||||||
}
|
|
||||||
Clause::Apply(f, x) => {
|
|
||||||
state.write_u8(1);
|
|
||||||
partial_hash_rec(f, state, parametrics.clone());
|
|
||||||
partial_hash_rec(x, state, parametrics);
|
|
||||||
}
|
|
||||||
Clause::AutoArg(..) => state.write_u8(2),
|
|
||||||
// - Only recognize the depth of an argument if it refers to a non-auto parameter
|
|
||||||
Clause::LambdaArg(own_id) => {
|
|
||||||
let pos = parametrics
|
|
||||||
.and_then(|sf| sf.iter().position(|id| id == own_id))
|
|
||||||
.unwrap_or(usize::MAX);
|
|
||||||
state.write_u8(3);
|
|
||||||
state.write_usize(pos)
|
|
||||||
}
|
|
||||||
// - Hash leaves like normal
|
|
||||||
Clause::Literal(lit) => { state.write_u8(4); lit.hash(state) }
|
|
||||||
Clause::Atom(at) => { state.write_u8(5); at.hash(state) }
|
|
||||||
Clause::ExternFn(f) => { state.write_u8(6); f.hash(state) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
use mappable_rc::Mrc;
|
|
||||||
|
|
||||||
use crate::box_chain;
|
|
||||||
use crate::utils::BoxedIter;
|
|
||||||
use crate::utils::iter::{box_once, box_empty};
|
|
||||||
|
|
||||||
use super::apply_lambda::apply_lambda;
|
|
||||||
use super::super::representations::typed::{Clause, Expr};
|
|
||||||
|
|
||||||
/// Call the function with the first Expression that isn't an Auto,
|
|
||||||
/// wrap all elements in the returned iterator back in the original sequence of Autos.
|
|
||||||
pub fn skip_autos<'a,
|
|
||||||
F: 'a + FnOnce(Mrc<Expr>) -> I,
|
|
||||||
I: Iterator<Item = Mrc<Expr>> + 'static
|
|
||||||
>(
|
|
||||||
expr: Mrc<Expr>, function: F
|
|
||||||
) -> BoxedIter<'static, Mrc<Expr>> {
|
|
||||||
if let Expr(Clause::Auto(id, arg, body), typ) = expr.as_ref() {
|
|
||||||
return Box::new(skip_autos(Mrc::clone(body), function).map({
|
|
||||||
let arg = Mrc::clone(arg);
|
|
||||||
let typ = Mrc::clone(typ);
|
|
||||||
move |body| {
|
|
||||||
Mrc::new(Expr(Clause::Auto(
|
|
||||||
*id,
|
|
||||||
Mrc::clone(&arg),
|
|
||||||
body
|
|
||||||
), Mrc::clone(&typ)))
|
|
||||||
}
|
|
||||||
})) as BoxedIter<'static, Mrc<Expr>>
|
|
||||||
}
|
|
||||||
Box::new(function(expr))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Produces an iterator of every expression that can be produced from this one through B-reduction.
|
|
||||||
fn direct_reductions(ex: Mrc<Expr>) -> impl Iterator<Item = Mrc<Expr>> {
|
|
||||||
skip_autos(ex, |mexpr| {
|
|
||||||
let Expr(clause, typ_ref) = mexpr.as_ref();
|
|
||||||
match clause {
|
|
||||||
Clause::Apply(f, x) => box_chain!(
|
|
||||||
skip_autos(Mrc::clone(f), |mexpr| {
|
|
||||||
let Expr(f, _) = mexpr.as_ref();
|
|
||||||
match f {
|
|
||||||
Clause::Lambda(id, _, body) => box_once(
|
|
||||||
apply_lambda(*id, Mrc::clone(x), Mrc::clone(body))
|
|
||||||
),
|
|
||||||
Clause::ExternFn(xfn) => {
|
|
||||||
let Expr(xval, xtyp) = x.as_ref();
|
|
||||||
xfn.apply(xval.clone())
|
|
||||||
.map(|ret| box_once(Mrc::new(Expr(ret, Mrc::clone(xtyp)))))
|
|
||||||
.unwrap_or(box_empty())
|
|
||||||
},
|
|
||||||
// Parametric newtypes are atoms of function type
|
|
||||||
Clause::Atom(..) | Clause::LambdaArg(..) | Clause::AutoArg(..) | Clause::Apply(..) => box_empty(),
|
|
||||||
Clause::Literal(lit) =>
|
|
||||||
panic!("Literal expression {lit:?} can't be applied as function"),
|
|
||||||
Clause::Auto(..) => unreachable!("skip_autos should have filtered this"),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
direct_reductions(Mrc::clone(f)).map({
|
|
||||||
let typ = Mrc::clone(typ_ref);
|
|
||||||
let x = Mrc::clone(x);
|
|
||||||
move |f| Mrc::new(Expr(Clause::Apply(
|
|
||||||
f,
|
|
||||||
Mrc::clone(&x)
|
|
||||||
), Mrc::clone(&typ)))
|
|
||||||
}),
|
|
||||||
direct_reductions(Mrc::clone(x)).map({
|
|
||||||
let typ = Mrc::clone(typ_ref);
|
|
||||||
let f = Mrc::clone(f);
|
|
||||||
move |x| Mrc::new(Expr(Clause::Apply(
|
|
||||||
Mrc::clone(&f),
|
|
||||||
x
|
|
||||||
), Mrc::clone(&typ)))
|
|
||||||
})
|
|
||||||
),
|
|
||||||
Clause::Lambda(id, argt, body) => {
|
|
||||||
let id = *id;
|
|
||||||
let typ = Mrc::clone(typ_ref);
|
|
||||||
let argt = Mrc::clone(argt);
|
|
||||||
let body = Mrc::clone(body);
|
|
||||||
let body_reductions = direct_reductions(body)
|
|
||||||
.map(move |body| {
|
|
||||||
let argt = Mrc::clone(&argt);
|
|
||||||
Mrc::new(Expr(
|
|
||||||
Clause::Lambda(id, argt, body),
|
|
||||||
Mrc::clone(&typ)
|
|
||||||
))
|
|
||||||
});
|
|
||||||
Box::new(body_reductions)
|
|
||||||
},
|
|
||||||
Clause::Auto(..) => unreachable!("skip_autos should have filtered this"),
|
|
||||||
Clause::Literal(..) | Clause::ExternFn(..) | Clause::Atom(..)
|
|
||||||
| Clause::LambdaArg(..) | Clause::AutoArg(..) => box_empty(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
@@ -1,206 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
|
||||||
use mappable_rc::Mrc;
|
|
||||||
|
|
||||||
use crate::utils::{ProtoMap, Side, mrc_empty_slice, collect_to_mrc, Stackframe, mrc_concat, Product2};
|
|
||||||
|
|
||||||
use super::super::representations::typed::{Clause, Expr};
|
|
||||||
|
|
||||||
pub fn swap<T, U>((t, u): (T, U)) -> (U, T) { (u, t) }
|
|
||||||
|
|
||||||
// @ @ (0, (foo 1)) ~ @ (0, 0)
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// - get rid of leftovers from Explicit
|
|
||||||
// - adapt to new index-based system
|
|
||||||
|
|
||||||
enum UnifError {
|
|
||||||
Conflict,
|
|
||||||
}
|
|
||||||
|
|
||||||
type LambdaMap<'a> = Option<&'a Stackframe<'a, (u64, u64)>>;
|
|
||||||
|
|
||||||
/// The context associates a given variable (by absolute index) on a given side to
|
|
||||||
/// an expression on the opposite side rooted at the specified depth.
|
|
||||||
/// The root depths are used to translate betwee de Brujin arguments and absolute indices.
|
|
||||||
struct Context(HashMap<u64, Mrc<Expr>>);
|
|
||||||
impl Context {
|
|
||||||
fn set(&mut self, id: u64, value: &Mrc<Expr>, lambdas: LambdaMap) -> Result<Option<Mrc<Expr>>, UnifError> {
|
|
||||||
Ok(
|
|
||||||
if let Some(local) = self.0.get(&id) {
|
|
||||||
Some(
|
|
||||||
self.unify_expr(local, value, lambdas)?
|
|
||||||
.pick(Mrc::clone(local), Mrc::clone(value))
|
|
||||||
)
|
|
||||||
} else { None }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unify_expr(&mut self,
|
|
||||||
left: &Mrc<Expr>, right: &Mrc<Expr>, lambdas: LambdaMap
|
|
||||||
) -> Result<Product2<Mrc<Expr>>, UnifError> {
|
|
||||||
let Expr(left_val, left_typs) = left.as_ref();
|
|
||||||
let Expr(right_val, right_typs) = right.as_ref();
|
|
||||||
let val = match (left_val, right_val) {
|
|
||||||
(Clause::AutoArg(l), Clause::AutoArg(r)) if l == r => Product2::Either,
|
|
||||||
(Clause::AutoArg(id), _) => self.set(*id, left, lambdas)?.as_ref()
|
|
||||||
.map_or(Product2::Left, |e| Product2::New(e.0.clone())),
|
|
||||||
(_, Clause::AutoArg(id)) => self.set(*id, right, lambdas)?.as_ref()
|
|
||||||
.map_or(Product2::Right, |e| Product2::New(e.0.clone())),
|
|
||||||
_ => self.unify_clause(left_val, right_val, lambdas)?
|
|
||||||
};
|
|
||||||
Ok(match val {
|
|
||||||
Product2::Either if right_typs.is_empty() && left_typs.is_empty() => Product2::Either,
|
|
||||||
Product2::Left | Product2::Either if right_typs.is_empty() => Product2::Left,
|
|
||||||
Product2::Right | Product2::Either if left_typs.is_empty() => Product2::Right,
|
|
||||||
product => {
|
|
||||||
let all_types = mrc_concat(left_typs, right_typs);
|
|
||||||
Product2::New(Mrc::new(Expr(
|
|
||||||
product.pick(left_val.clone(), right_val.clone()),
|
|
||||||
all_types
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unify_clauses(&mut self,
|
|
||||||
left: &Mrc<[Clause]>, right: &Mrc<[Clause]>, lambdas: LambdaMap
|
|
||||||
) -> Result<Product2<Clause>, UnifError> {
|
|
||||||
if left.len() != right.len() {return Err(UnifError::Conflict)}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unify_clause(&mut self,
|
|
||||||
left: &Clause, right: &Clause, lambdas: LambdaMap
|
|
||||||
) -> Result<Product2<Clause>, UnifError> {
|
|
||||||
Ok(match (left, right) {
|
|
||||||
(Clause::Literal(l), Clause::Literal(r)) if l == r => Product2::Either,
|
|
||||||
(Clause::Atom(l), Clause::Atom(r)) if l == r => Product2::Either,
|
|
||||||
(Clause::ExternFn(l), Clause::ExternFn(r)) if l == r => Product2::Either,
|
|
||||||
(Clause::LambdaArg(l), Clause::LambdaArg(r)) => if l == r {Product2::Either} else {
|
|
||||||
let is_equal = Stackframe::o_into_iter(lambdas)
|
|
||||||
.first_some(|(l_candidate, r_candidate)| {
|
|
||||||
if l_candidate == l && r_candidate == r {Some(true)} // match
|
|
||||||
else if l_candidate == l || r_candidate == r {Some(false)} // shadow
|
|
||||||
else {None} // irrelevant
|
|
||||||
}).unwrap_or(false);
|
|
||||||
// Reference:
|
|
||||||
if is_equal {Product2::Left} else {return Err(UnifError::Conflict)}
|
|
||||||
}
|
|
||||||
(Clause::AutoArg(_), _) | (_, Clause::AutoArg(_)) => {
|
|
||||||
unreachable!("unify_expr should have handled this")
|
|
||||||
}
|
|
||||||
(Clause::Lambda(l_id, l_arg, l_body), Clause::Lambda(r_id, r_arg, r_body)) => {
|
|
||||||
let lambdas = Stackframe::opush(lambdas, (*l_id, *r_id));
|
|
||||||
self.unify_expr(l_body, r_body, Some(&lambdas))?
|
|
||||||
.map(|ex| Clause::Lambda(*l_id, mrc_empty_slice(), ex))
|
|
||||||
}
|
|
||||||
(Clause::Apply(l_f, l_x), Clause::Apply(r_f, r_x)) => {
|
|
||||||
self.unify_expr(l_f, r_f, lambdas)?.join((Mrc::clone(l_f), Mrc::clone(r_f)),
|
|
||||||
self.unify_expr(l_x, r_x, lambdas)?, (Mrc::clone(l_x), Mrc::clone(r_x))
|
|
||||||
).map(|(f, x)| Clause::Apply(f, x))
|
|
||||||
}
|
|
||||||
(Clause::Auto(l_id, l_arg, l_body), Clause::Auto(r_id, r_arg, r_body)) => {
|
|
||||||
let typ = self.unify(l_arg, r_arg, lambdas)?;
|
|
||||||
let body = self.unify_expr(l_body, r_body, lambdas)?;
|
|
||||||
typ.join((l_arg, r_arg), )
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const IS_AUTO_INLINE:usize = 5;
|
|
||||||
|
|
||||||
// All data to be forwarded during recursion about one half of a unification task
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct UnifHalfTask<'a> {
|
|
||||||
/// The expression to be unified
|
|
||||||
expr: &'a Expr,
|
|
||||||
/// Stores whether a given uid is auto or lambda
|
|
||||||
is_auto: ProtoMap<'a, usize, bool, IS_AUTO_INLINE>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> UnifHalfTask<'a> {
|
|
||||||
fn push_auto(&mut self, body: &Expr, key: usize) {
|
|
||||||
self.expr = body;
|
|
||||||
self.is_auto.set(&key, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_lambda(&mut self, body: &Expr, key: usize) {
|
|
||||||
self.expr = body;
|
|
||||||
self.is_auto.set(&key, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Ctx = HashMap<usize, Mrc<Expr>>;
|
|
||||||
|
|
||||||
/// Ascertain syntactic equality. Syntactic equality means that
|
|
||||||
/// - lambda elements are verbatim equal
|
|
||||||
/// - auto constraints are pairwise syntactically equal after sorting
|
|
||||||
///
|
|
||||||
/// Context associates variables with subtrees resolved on the opposite side
|
|
||||||
pub fn unify_syntax_rec( // the stacks store true for autos, false for lambdas
|
|
||||||
ctx: &mut HashMap<(Side, usize), (usize, Mrc<Expr>)>,
|
|
||||||
ltask@UnifHalfTask{ expr: lexpr@Expr(lclause, _), .. }: UnifHalfTask,
|
|
||||||
rtask@UnifHalfTask{ expr: rexpr@Expr(rclause, _), .. }: UnifHalfTask
|
|
||||||
) -> Option<(UnifResult, UnifResult)> {
|
|
||||||
// Ensure that ex1 is a value-level construct
|
|
||||||
match lclause {
|
|
||||||
Clause::Auto(id, _, body) => {
|
|
||||||
let res = unify_syntax_rec(ltask.push_auto(body).0, rtask);
|
|
||||||
return if ltask.explicits.is_some() {
|
|
||||||
res.map(|(r1, r2)| (r1.useExplicit(), r2))
|
|
||||||
} else {res}
|
|
||||||
}
|
|
||||||
_ => ()
|
|
||||||
};
|
|
||||||
// Reduce ex2's auto handling to ex1's. In the optimizer we trust
|
|
||||||
if let Clause::Auto(..) | Clause::Explicit(..) = rclause {
|
|
||||||
return unify_syntax_rec(rtask, ltask).map(swap);
|
|
||||||
}
|
|
||||||
// Neither ex1 nor ex2 can be Auto or Explicit
|
|
||||||
match (lclause, rclause) {
|
|
||||||
// recurse into both
|
|
||||||
(Clause::Lambda(_, lbody), Clause::Lambda(_, rbody)) => unify_syntax_rec(
|
|
||||||
ltask.push_lambda(lbody),
|
|
||||||
rtask.push_lambda(rbody)
|
|
||||||
),
|
|
||||||
(Clause::Apply(lf, lx), Clause::Apply(rf, rx)) => {
|
|
||||||
let (lpart, rpart) = unify_syntax_rec(
|
|
||||||
ltask.push_expr(lf),
|
|
||||||
rtask.push_expr(rf)
|
|
||||||
)?;
|
|
||||||
lpart.dropUsedExplicits(&mut ltask);
|
|
||||||
rpart.dropUsedExplicits(&mut rtask);
|
|
||||||
unify_syntax_rec(ltask.push_expr(lx), rtask.push_expr(rx))
|
|
||||||
}
|
|
||||||
(Clause::Atom(latom), Clause::Atom(ratom)) => {
|
|
||||||
if latom != ratom { None }
|
|
||||||
else { Some((UnifResult::default(), UnifResult::default())) }
|
|
||||||
}
|
|
||||||
(Clause::ExternFn(lf), Clause::ExternFn(rf)) => {
|
|
||||||
if lf != rf { None }
|
|
||||||
else { Some((UnifResult::default(), UnifResult::default())) }
|
|
||||||
}
|
|
||||||
(Clause::Literal(llit), Clause::Literal(rlit)) => {
|
|
||||||
if llit != rlit { None }
|
|
||||||
else { Some((UnifResult::default(), UnifResult::default())) }
|
|
||||||
}
|
|
||||||
// TODO Select a representative
|
|
||||||
(Clause::Argument(depth1), Clause::Argument(depth2)) => {
|
|
||||||
!*stack1.iter().nth(*depth1).unwrap_or(&false)
|
|
||||||
&& !*stack2.iter().nth(*depth2).unwrap_or(&false)
|
|
||||||
&& stack1.iter().count() - depth1 == stack2.iter().count() - depth2
|
|
||||||
}
|
|
||||||
// TODO Assign a substitute
|
|
||||||
(Clause::Argument(placeholder), _) => {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tricky unifications
|
|
||||||
// @A. A A 1 ~ @B. 2 B B = fails if left-authoritative
|
|
||||||
// @A. 1 A A ~ @B. B B 2
|
|
||||||
// @A. A 1 A ~ @B. B B 2
|
|
||||||
// @ 0 X 0 ~ @ 0 0 Y
|
|
||||||
8
src/external/assertion_error.rs
vendored
8
src/external/assertion_error.rs
vendored
@@ -2,21 +2,21 @@ use std::rc::Rc;
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::foreign::ExternError;
|
use crate::foreign::ExternError;
|
||||||
use crate::representations::interpreted::Clause;
|
use crate::representations::interpreted::ExprInst;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AssertionError{
|
pub struct AssertionError{
|
||||||
pub value: Clause,
|
pub value: ExprInst,
|
||||||
pub assertion: &'static str,
|
pub assertion: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AssertionError {
|
impl AssertionError {
|
||||||
pub fn fail(value: Clause, assertion: &'static str) -> Result<!, Rc<dyn ExternError>> {
|
pub fn fail(value: ExprInst, assertion: &'static str) -> Result<!, Rc<dyn ExternError>> {
|
||||||
return Err(Self { value, assertion }.into_extern())
|
return Err(Self { value, assertion }.into_extern())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ext(value: Clause, assertion: &'static str) -> Rc<dyn ExternError> {
|
pub fn ext(value: ExprInst, assertion: &'static str) -> Rc<dyn ExternError> {
|
||||||
return Self { value, assertion }.into_extern()
|
return Self { value, assertion }.into_extern()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/external/bool/boolean.rs
vendored
12
src/external/bool/boolean.rs
vendored
@@ -1,5 +1,6 @@
|
|||||||
|
use crate::foreign::Atom;
|
||||||
use crate::{atomic_inert, representations::{interpreted::Clause, Primitive}, foreign::Atom};
|
use crate::representations::{interpreted::{Clause, ExprInst}, Primitive};
|
||||||
|
use crate::atomic_inert;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct Boolean(pub bool);
|
pub struct Boolean(pub bool);
|
||||||
@@ -7,11 +8,12 @@ atomic_inert!(Boolean);
|
|||||||
|
|
||||||
impl From<bool> for Boolean { fn from(value: bool) -> Self { Self(value) } }
|
impl From<bool> for Boolean { fn from(value: bool) -> Self { Self(value) } }
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a Clause> for Boolean {
|
impl TryFrom<ExprInst> for Boolean {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
fn try_from(value: &'a Clause) -> Result<Self, Self::Error> {
|
fn try_from(value: ExprInst) -> Result<Self, Self::Error> {
|
||||||
if let Clause::P(Primitive::Atom(Atom(a))) = value {
|
let expr = value.expr();
|
||||||
|
if let Clause::P(Primitive::Atom(Atom(a))) = &expr.clause {
|
||||||
if let Some(b) = a.as_any().downcast_ref::<Boolean>() {
|
if let Some(b) = a.as_any().downcast_ref::<Boolean>() {
|
||||||
return Ok(*b)
|
return Ok(*b)
|
||||||
}
|
}
|
||||||
|
|||||||
46
src/external/bool/equals.rs
vendored
46
src/external/bool/equals.rs
vendored
@@ -1,10 +1,8 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
|
use crate::external::litconv::with_lit;
|
||||||
|
use crate::representations::{interpreted::ExprInst, Literal};
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
use crate::foreign::Atom;
|
|
||||||
use crate::representations::{Primitive, Literal};
|
|
||||||
use crate::representations::interpreted::Clause;
|
|
||||||
|
|
||||||
use super::super::assertion_error::AssertionError;
|
use super::super::assertion_error::AssertionError;
|
||||||
use super::boolean::Boolean;
|
use super::boolean::Boolean;
|
||||||
@@ -15,38 +13,34 @@ use super::boolean::Boolean;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Equals2;
|
pub struct Equals2;
|
||||||
externfn_impl!(Equals2, |_: &Self, c: Clause| {Ok(Equals1{c})});
|
externfn_impl!(Equals2, |_: &Self, x: ExprInst| {Ok(Equals1{x})});
|
||||||
|
|
||||||
/// Partially applied Equals function
|
/// Partially applied Equals function
|
||||||
///
|
///
|
||||||
/// Prev state: [Equals2]; Next state: [Equals0]
|
/// Prev state: [Equals2]; Next state: [Equals0]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Equals1{ c: Clause }
|
pub struct Equals1{ x: ExprInst }
|
||||||
atomic_redirect!(Equals1, c);
|
atomic_redirect!(Equals1, x);
|
||||||
atomic_impl!(Equals1);
|
atomic_impl!(Equals1);
|
||||||
externfn_impl!(Equals1, |this: &Self, c: Clause| {
|
externfn_impl!(Equals1, |this: &Self, x: ExprInst| {
|
||||||
let a: Literal = this.c.clone().try_into()
|
with_lit(&this.x, |l| Ok(Equals0{ a: l.clone(), x }))
|
||||||
.map_err(|_| AssertionError::ext(this.c.clone(), "a primitive"))?;
|
|
||||||
Ok(Equals0{ a, c })
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Fully applied Equals function.
|
/// Fully applied Equals function.
|
||||||
///
|
///
|
||||||
/// Prev state: [Equals1]
|
/// Prev state: [Equals1]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Equals0 { a: Literal, c: Clause }
|
pub struct Equals0 { a: Literal, x: ExprInst }
|
||||||
atomic_redirect!(Equals0, c);
|
atomic_redirect!(Equals0, x);
|
||||||
atomic_impl!(Equals0, |Self{ a, c }: &Self| {
|
atomic_impl!(Equals0, |Self{ a, x }: &Self| {
|
||||||
let b: Literal = c.clone().try_into()
|
let eqls = with_lit(x, |l| Ok(match (a, l) {
|
||||||
.map_err(|_| AssertionError::ext(c.clone(), "a literal value"))?;
|
(Literal::Char(c1), Literal::Char(c2)) => c1 == c2,
|
||||||
let eqls = match (a, b) {
|
(Literal::Num(n1), Literal::Num(n2)) => n1 == n2,
|
||||||
(Literal::Char(c1), Literal::Char(c2)) => *c1 == c2,
|
(Literal::Str(s1), Literal::Str(s2)) => s1 == s2,
|
||||||
(Literal::Num(n1), Literal::Num(n2)) => *n1 == n2,
|
(Literal::Uint(i1), Literal::Uint(i2)) => i1 == i2,
|
||||||
(Literal::Str(s1), Literal::Str(s2)) => *s1 == s2,
|
(_, _) => AssertionError::fail(x.clone(), "the expected type")?,
|
||||||
(Literal::Uint(i1), Literal::Uint(i2)) => *i1 == i2,
|
}))?;
|
||||||
(_, _) => AssertionError::fail(c.clone(), "the expected type")?,
|
Ok(Boolean::from(eqls).to_atom_cls())
|
||||||
};
|
|
||||||
Ok(Clause::P(Primitive::Atom(Atom::new(Boolean::from(eqls)))))
|
|
||||||
});
|
});
|
||||||
|
|||||||
28
src/external/bool/ifthenelse.rs
vendored
28
src/external/bool/ifthenelse.rs
vendored
@@ -1,11 +1,9 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::external::assertion_error::AssertionError;
|
use crate::external::assertion_error::AssertionError;
|
||||||
use crate::representations::PathSet;
|
use crate::representations::{PathSet, interpreted::{Clause, ExprInst}};
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
use crate::representations::interpreted::Clause;
|
|
||||||
|
|
||||||
use super::Boolean;
|
use super::Boolean;
|
||||||
|
|
||||||
@@ -15,29 +13,29 @@ use super::Boolean;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct IfThenElse1;
|
pub struct IfThenElse1;
|
||||||
externfn_impl!(IfThenElse1, |_: &Self, c: Clause| {Ok(IfThenElse0{c})});
|
externfn_impl!(IfThenElse1, |_: &Self, x: ExprInst| {Ok(IfThenElse0{x})});
|
||||||
|
|
||||||
/// Partially applied IfThenElse function
|
/// Partially applied IfThenElse function
|
||||||
///
|
///
|
||||||
/// Prev state: [IfThenElse1]; Next state: [IfThenElse0]
|
/// Prev state: [IfThenElse1]; Next state: [IfThenElse0]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct IfThenElse0{ c: Clause }
|
pub struct IfThenElse0{ x: ExprInst }
|
||||||
atomic_redirect!(IfThenElse0, c);
|
atomic_redirect!(IfThenElse0, x);
|
||||||
atomic_impl!(IfThenElse0, |this: &Self| {
|
atomic_impl!(IfThenElse0, |this: &Self| {
|
||||||
let Boolean(b) = (&this.c).try_into()
|
let Boolean(b) = this.x.clone().try_into()
|
||||||
.map_err(|_| AssertionError::ext(this.c.clone(), "a boolean"))?;
|
.map_err(|_| AssertionError::ext(this.x.clone(), "a boolean"))?;
|
||||||
Ok(if b { Clause::Lambda {
|
Ok(if b { Clause::Lambda {
|
||||||
args: Some(PathSet { steps: Rc::new(vec![]), next: None }),
|
args: Some(PathSet { steps: Rc::new(vec![]), next: None }),
|
||||||
body: Rc::new(Clause::Lambda {
|
body: Clause::Lambda {
|
||||||
args: None,
|
args: None,
|
||||||
body: Rc::new(Clause::LambdaArg)
|
body: Clause::LambdaArg.wrap()
|
||||||
})
|
}.wrap()
|
||||||
}} else { Clause::Lambda {
|
}} else { Clause::Lambda {
|
||||||
args: None,
|
args: None,
|
||||||
body: Rc::new(Clause::Lambda {
|
body: Clause::Lambda {
|
||||||
args: Some(PathSet { steps: Rc::new(vec![]), next: None }),
|
args: Some(PathSet { steps: Rc::new(vec![]), next: None }),
|
||||||
body: Rc::new(Clause::LambdaArg)
|
body: Clause::LambdaArg.wrap()
|
||||||
})
|
}.wrap()
|
||||||
}})
|
}})
|
||||||
});
|
});
|
||||||
11
src/external/bool/mod.rs
vendored
11
src/external/bool/mod.rs
vendored
@@ -3,11 +3,12 @@ mod boolean;
|
|||||||
mod ifthenelse;
|
mod ifthenelse;
|
||||||
pub use boolean::Boolean;
|
pub use boolean::Boolean;
|
||||||
|
|
||||||
use crate::project::{Loader, extlib_loader};
|
use crate::{pipeline::ConstTree, interner::Interner};
|
||||||
|
|
||||||
pub fn bool() -> impl Loader {
|
|
||||||
extlib_loader(vec![
|
pub fn bool(i: &Interner) -> ConstTree {
|
||||||
("ifthenelse", Box::new(ifthenelse::IfThenElse1)),
|
ConstTree::tree([
|
||||||
("equals", Box::new(equals::Equals2))
|
(i.i("ifthenelse"), ConstTree::xfn(ifthenelse::IfThenElse1)),
|
||||||
|
(i.i("equals"), ConstTree::xfn(equals::Equals2))
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
12
src/external/conv/mod.rs
vendored
12
src/external/conv/mod.rs
vendored
@@ -1,13 +1,13 @@
|
|||||||
use crate::project::{extlib_loader, Loader};
|
use crate::{interner::Interner, pipeline::ConstTree};
|
||||||
|
|
||||||
mod to_string;
|
mod to_string;
|
||||||
mod parse_float;
|
mod parse_float;
|
||||||
mod parse_uint;
|
mod parse_uint;
|
||||||
|
|
||||||
pub fn conv() -> impl Loader {
|
pub fn conv(i: &Interner) -> ConstTree {
|
||||||
extlib_loader(vec![
|
ConstTree::tree([
|
||||||
("parse_float", Box::new(parse_float::ParseFloat1)),
|
(i.i("parse_float"), ConstTree::xfn(parse_float::ParseFloat1)),
|
||||||
("parse_uint", Box::new(parse_uint::ParseUint1)),
|
(i.i("parse_uint"), ConstTree::xfn(parse_uint::ParseUint1)),
|
||||||
("to_string", Box::new(to_string::ToString1))
|
(i.i("to_string"), ConstTree::xfn(to_string::ToString1))
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
35
src/external/conv/parse_float.rs
vendored
35
src/external/conv/parse_float.rs
vendored
@@ -2,14 +2,12 @@
|
|||||||
use chumsky::Parser;
|
use chumsky::Parser;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
use super::super::assertion_error::AssertionError;
|
use super::super::assertion_error::AssertionError;
|
||||||
|
use crate::external::litconv::with_lit;
|
||||||
use crate::parse::float_parser;
|
use crate::parse::float_parser;
|
||||||
|
use crate::representations::{interpreted::ExprInst, Literal};
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
use crate::foreign::ExternError;
|
|
||||||
use crate::representations::{Primitive, Literal};
|
|
||||||
use crate::representations::interpreted::Clause;
|
|
||||||
|
|
||||||
/// ParseFloat a number
|
/// ParseFloat a number
|
||||||
///
|
///
|
||||||
@@ -17,30 +15,27 @@ use crate::representations::interpreted::Clause;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ParseFloat1;
|
pub struct ParseFloat1;
|
||||||
externfn_impl!(ParseFloat1, |_: &Self, c: Clause| {Ok(ParseFloat0{c})});
|
externfn_impl!(ParseFloat1, |_: &Self, x: ExprInst| {Ok(ParseFloat0{x})});
|
||||||
|
|
||||||
/// Applied to_string function
|
/// Applied to_string function
|
||||||
///
|
///
|
||||||
/// Prev state: [ParseFloat1]
|
/// Prev state: [ParseFloat1]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ParseFloat0{ c: Clause }
|
pub struct ParseFloat0{ x: ExprInst }
|
||||||
atomic_redirect!(ParseFloat0, c);
|
atomic_redirect!(ParseFloat0, x);
|
||||||
atomic_impl!(ParseFloat0, |Self{ c }: &Self| {
|
atomic_impl!(ParseFloat0, |Self{ x }: &Self| {
|
||||||
let literal: &Literal = c.try_into()
|
let number = with_lit(x, |l| Ok(match l {
|
||||||
.map_err(|_| AssertionError::ext(c.clone(), "a literal value"))?;
|
|
||||||
let number = match literal {
|
|
||||||
Literal::Str(s) => {
|
Literal::Str(s) => {
|
||||||
let parser = float_parser();
|
let parser = float_parser();
|
||||||
parser.parse(s.as_str()).map_err(|_| AssertionError{
|
parser.parse(s.as_str())
|
||||||
value: c.clone(), assertion: "cannot be parsed into a float"
|
.map_err(|_| AssertionError::ext(x.clone(), "cannot be parsed into a float"))?
|
||||||
}.into_extern())?
|
|
||||||
}
|
}
|
||||||
Literal::Num(n) => *n,
|
Literal::Num(n) => *n,
|
||||||
Literal::Uint(i) => (*i as u32).into(),
|
Literal::Uint(i) => (*i as u32).into(),
|
||||||
Literal::Char(char) => char.to_digit(10).ok_or(AssertionError{
|
Literal::Char(char) => char.to_digit(10)
|
||||||
value: c.clone(), assertion: "is not a decimal digit"
|
.ok_or(AssertionError::ext(x.clone(), "is not a decimal digit"))?
|
||||||
}.into_extern())?.into()
|
.into()
|
||||||
};
|
}))?;
|
||||||
Ok(Clause::P(Primitive::Literal(Literal::Num(number))))
|
Ok(number.into())
|
||||||
});
|
});
|
||||||
38
src/external/conv/parse_uint.rs
vendored
38
src/external/conv/parse_uint.rs
vendored
@@ -2,14 +2,11 @@
|
|||||||
use chumsky::Parser;
|
use chumsky::Parser;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
use super::super::assertion_error::AssertionError;
|
use crate::external::{litconv::with_lit, assertion_error::AssertionError};
|
||||||
use crate::parse::int_parser;
|
use crate::representations::{interpreted::ExprInst, Literal};
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
use crate::foreign::ExternError;
|
use crate::parse::int_parser;
|
||||||
use crate::representations::{Primitive, Literal};
|
|
||||||
use crate::representations::interpreted::Clause;
|
|
||||||
|
|
||||||
/// Parse a number
|
/// Parse a number
|
||||||
///
|
///
|
||||||
@@ -17,30 +14,27 @@ use crate::representations::interpreted::Clause;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ParseUint1;
|
pub struct ParseUint1;
|
||||||
externfn_impl!(ParseUint1, |_: &Self, c: Clause| {Ok(ParseUint0{c})});
|
externfn_impl!(ParseUint1, |_: &Self, x: ExprInst| {Ok(ParseUint0{x})});
|
||||||
|
|
||||||
/// Applied ParseUint function
|
/// Applied ParseUint function
|
||||||
///
|
///
|
||||||
/// Prev state: [ParseUint1]
|
/// Prev state: [ParseUint1]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ParseUint0{ c: Clause }
|
pub struct ParseUint0{ x: ExprInst }
|
||||||
atomic_redirect!(ParseUint0, c);
|
atomic_redirect!(ParseUint0, x);
|
||||||
atomic_impl!(ParseUint0, |Self{ c }: &Self| {
|
atomic_impl!(ParseUint0, |Self{ x }: &Self| {
|
||||||
let literal: &Literal = c.try_into()
|
let uint = with_lit(x, |l| Ok(match l {
|
||||||
.map_err(|_| AssertionError::ext(c.clone(), "a literal value"))?;
|
|
||||||
let uint = match literal {
|
|
||||||
Literal::Str(s) => {
|
Literal::Str(s) => {
|
||||||
let parser = int_parser();
|
let parser = int_parser();
|
||||||
parser.parse(s.as_str()).map_err(|_| AssertionError{
|
parser.parse(s.as_str())
|
||||||
value: c.clone(), assertion: "cannot be parsed into an unsigned int"
|
.map_err(|_| AssertionError::ext(x.clone(), "cannot be parsed into an unsigned int"))?
|
||||||
}.into_extern())?
|
|
||||||
}
|
}
|
||||||
Literal::Num(n) => n.floor() as u64,
|
Literal::Num(n) => n.floor() as u64,
|
||||||
Literal::Uint(i) => *i,
|
Literal::Uint(i) => *i,
|
||||||
Literal::Char(char) => char.to_digit(10).ok_or(AssertionError{
|
Literal::Char(char) => char.to_digit(10)
|
||||||
value: c.clone(), assertion: "is not a decimal digit"
|
.ok_or(AssertionError::ext(x.clone(), "is not a decimal digit"))?
|
||||||
}.into_extern())? as u64
|
.into()
|
||||||
};
|
}))?;
|
||||||
Ok(Clause::P(Primitive::Literal(Literal::Uint(uint))))
|
Ok(uint.into())
|
||||||
});
|
});
|
||||||
24
src/external/conv/to_string.rs
vendored
24
src/external/conv/to_string.rs
vendored
@@ -1,11 +1,9 @@
|
|||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
use crate::external::assertion_error::AssertionError;
|
use crate::external::litconv::with_lit;
|
||||||
|
use crate::representations::{interpreted::ExprInst, Literal};
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
use crate::representations::{Primitive, Literal};
|
|
||||||
use crate::representations::interpreted::Clause;
|
|
||||||
|
|
||||||
/// ToString a clause
|
/// ToString a clause
|
||||||
///
|
///
|
||||||
@@ -13,23 +11,21 @@ use crate::representations::interpreted::Clause;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ToString1;
|
pub struct ToString1;
|
||||||
externfn_impl!(ToString1, |_: &Self, c: Clause| {Ok(ToString0{c})});
|
externfn_impl!(ToString1, |_: &Self, x: ExprInst| {Ok(ToString0{x})});
|
||||||
|
|
||||||
/// Applied ToString function
|
/// Applied ToString function
|
||||||
///
|
///
|
||||||
/// Prev state: [ToString1]
|
/// Prev state: [ToString1]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ToString0{ c: Clause }
|
pub struct ToString0{ x: ExprInst }
|
||||||
atomic_redirect!(ToString0, c);
|
atomic_redirect!(ToString0, x);
|
||||||
atomic_impl!(ToString0, |Self{ c }: &Self| {
|
atomic_impl!(ToString0, |Self{ x }: &Self| {
|
||||||
let literal: &Literal = c.try_into()
|
let string = with_lit(x, |l| Ok(match l {
|
||||||
.map_err(|_| AssertionError::ext(c.clone(), "a literal value"))?;
|
|
||||||
let string = match literal {
|
|
||||||
Literal::Char(c) => c.to_string(),
|
Literal::Char(c) => c.to_string(),
|
||||||
Literal::Uint(i) => i.to_string(),
|
Literal::Uint(i) => i.to_string(),
|
||||||
Literal::Num(n) => n.to_string(),
|
Literal::Num(n) => n.to_string(),
|
||||||
Literal::Str(s) => s.clone()
|
Literal::Str(s) => s.clone()
|
||||||
};
|
}))?;
|
||||||
Ok(Clause::P(Primitive::Literal(Literal::Str(string))))
|
Ok(string.into())
|
||||||
});
|
});
|
||||||
|
|||||||
10
src/external/cpsio/mod.rs
vendored
10
src/external/cpsio/mod.rs
vendored
@@ -1,11 +1,11 @@
|
|||||||
use crate::project::{Loader, extlib_loader};
|
use crate::{interner::Interner, pipeline::ConstTree};
|
||||||
|
|
||||||
mod print;
|
mod print;
|
||||||
mod readline;
|
mod readline;
|
||||||
|
|
||||||
pub fn cpsio() -> impl Loader {
|
pub fn cpsio(i: &Interner) -> ConstTree {
|
||||||
extlib_loader(vec![
|
ConstTree::tree([
|
||||||
("print", Box::new(print::Print2)),
|
(i.i("print"), ConstTree::xfn(print::Print2)),
|
||||||
("readline", Box::new(readline::Readln2))
|
(i.i("readline"), ConstTree::xfn(readline::Readln2))
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
22
src/external/cpsio/print.rs
vendored
22
src/external/cpsio/print.rs
vendored
@@ -1,11 +1,10 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::external::str::cls2str;
|
use crate::external::litconv::with_str;
|
||||||
use crate::representations::PathSet;
|
use crate::representations::PathSet;
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
use crate::representations::interpreted::Clause;
|
use crate::representations::interpreted::{Clause, ExprInst};
|
||||||
|
|
||||||
/// Print function
|
/// Print function
|
||||||
///
|
///
|
||||||
@@ -13,20 +12,21 @@ use crate::representations::interpreted::Clause;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Print2;
|
pub struct Print2;
|
||||||
externfn_impl!(Print2, |_: &Self, c: Clause| {Ok(Print1{c})});
|
externfn_impl!(Print2, |_: &Self, x: ExprInst| {Ok(Print1{x})});
|
||||||
|
|
||||||
/// Partially applied Print function
|
/// Partially applied Print function
|
||||||
///
|
///
|
||||||
/// Prev state: [Print2]; Next state: [Print0]
|
/// Prev state: [Print2]; Next state: [Print0]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Print1{ c: Clause }
|
pub struct Print1{ x: ExprInst }
|
||||||
atomic_redirect!(Print1, c);
|
atomic_redirect!(Print1, x);
|
||||||
atomic_impl!(Print1, |Self{ c }: &Self| {
|
atomic_impl!(Print1, |Self{ x }: &Self| {
|
||||||
let message = cls2str(&c)?;
|
with_str(x, |s| {
|
||||||
print!("{}", message);
|
print!("{}", s);
|
||||||
Ok(Clause::Lambda {
|
Ok(Clause::Lambda {
|
||||||
args: Some(PathSet{ steps: Rc::new(vec![]), next: None }),
|
args: Some(PathSet{ steps: Rc::new(vec![]), next: None }),
|
||||||
body: Rc::new(Clause::LambdaArg)
|
body: Clause::LambdaArg.wrap()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
19
src/external/cpsio/readline.rs
vendored
19
src/external/cpsio/readline.rs
vendored
@@ -1,12 +1,10 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::io::stdin;
|
use std::io::stdin;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
use crate::external::runtime_error::RuntimeError;
|
use crate::external::runtime_error::RuntimeError;
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
use crate::representations::{Primitive, Literal};
|
use crate::representations::{Primitive, Literal};
|
||||||
use crate::representations::interpreted::Clause;
|
use crate::representations::interpreted::{Clause, ExprInst};
|
||||||
|
|
||||||
/// Readln function
|
/// Readln function
|
||||||
///
|
///
|
||||||
@@ -14,22 +12,21 @@ use crate::representations::interpreted::Clause;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Readln2;
|
pub struct Readln2;
|
||||||
externfn_impl!(Readln2, |_: &Self, c: Clause| {Ok(Readln1{c})});
|
externfn_impl!(Readln2, |_: &Self, x: ExprInst| {Ok(Readln1{x})});
|
||||||
|
|
||||||
/// Partially applied Readln function
|
/// Partially applied Readln function
|
||||||
///
|
///
|
||||||
/// Prev state: [Readln2]; Next state: [Readln0]
|
/// Prev state: [Readln2]; Next state: [Readln0]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Readln1{ c: Clause }
|
pub struct Readln1{ x: ExprInst }
|
||||||
atomic_redirect!(Readln1, c);
|
atomic_redirect!(Readln1, x);
|
||||||
atomic_impl!(Readln1, |Self{ c }: &Self| {
|
atomic_impl!(Readln1, |Self{ x }: &Self| {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
stdin().read_line(&mut buf).map_err(|e| RuntimeError::ext(e.to_string(), "reading from stdin"))?;
|
stdin().read_line(&mut buf).map_err(|e| RuntimeError::ext(e.to_string(), "reading from stdin"))?;
|
||||||
buf.pop();
|
buf.pop();
|
||||||
Ok(Clause::Apply {
|
Ok(Clause::Apply {
|
||||||
f: Rc::new(c.clone()),
|
f: x.clone(),
|
||||||
x: Rc::new(Clause::P(Primitive::Literal(Literal::Str(buf)))),
|
x: Clause::P(Primitive::Literal(Literal::Str(buf))).wrap()
|
||||||
id: 0
|
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
34
src/external/litconv.rs
vendored
Normal file
34
src/external/litconv.rs
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::foreign::ExternError;
|
||||||
|
use crate::external::assertion_error::AssertionError;
|
||||||
|
use crate::representations::interpreted::ExprInst;
|
||||||
|
use crate::representations::Literal;
|
||||||
|
|
||||||
|
pub fn with_lit<T>(x: &ExprInst,
|
||||||
|
predicate: impl FnOnce(&Literal) -> Result<T, Rc<dyn ExternError>>
|
||||||
|
) -> Result<T, Rc<dyn ExternError>> {
|
||||||
|
x.with_literal(predicate)
|
||||||
|
.map_err(|()| AssertionError::ext(x.clone(), "a literal value"))
|
||||||
|
.and_then(|r| r)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_str<T>(x: &ExprInst,
|
||||||
|
predicate: impl FnOnce(&String) -> Result<T, Rc<dyn ExternError>>
|
||||||
|
) -> Result<T, Rc<dyn ExternError>> {
|
||||||
|
with_lit(x, |l| {
|
||||||
|
if let Literal::Str(s) = l {predicate(&s)} else {
|
||||||
|
AssertionError::fail(x.clone(), "a string")?
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_uint<T>(x: &ExprInst,
|
||||||
|
predicate: impl FnOnce(u64) -> Result<T, Rc<dyn ExternError>>
|
||||||
|
) -> Result<T, Rc<dyn ExternError>> {
|
||||||
|
with_lit(x, |l| {
|
||||||
|
if let Literal::Uint(u) = l {predicate(*u)} else {
|
||||||
|
AssertionError::fail(x.clone(), "an uint")?
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
1
src/external/mod.rs
vendored
1
src/external/mod.rs
vendored
@@ -6,3 +6,4 @@ mod str;
|
|||||||
mod cpsio;
|
mod cpsio;
|
||||||
mod runtime_error;
|
mod runtime_error;
|
||||||
mod bool;
|
mod bool;
|
||||||
|
mod litconv;
|
||||||
|
|||||||
16
src/external/num/mod.rs
vendored
16
src/external/num/mod.rs
vendored
@@ -2,14 +2,14 @@ mod numeric;
|
|||||||
pub mod operators;
|
pub mod operators;
|
||||||
pub use numeric::Numeric;
|
pub use numeric::Numeric;
|
||||||
|
|
||||||
use crate::project::{extlib_loader, Loader};
|
use crate::{interner::Interner, pipeline::ConstTree};
|
||||||
|
|
||||||
pub fn num() -> impl Loader {
|
pub fn num(i: &Interner) -> ConstTree {
|
||||||
extlib_loader(vec![
|
ConstTree::tree([
|
||||||
("add", Box::new(operators::add::Add2)),
|
(i.i("add"), ConstTree::xfn(operators::add::Add2)),
|
||||||
("subtract", Box::new(operators::subtract::Subtract2)),
|
(i.i("subtract"), ConstTree::xfn(operators::subtract::Subtract2)),
|
||||||
("multiply", Box::new(operators::multiply::Multiply2)),
|
(i.i("multiply"), ConstTree::xfn(operators::multiply::Multiply2)),
|
||||||
("divide", Box::new(operators::divide::Divide2)),
|
(i.i("divide"), ConstTree::xfn(operators::divide::Divide2)),
|
||||||
("remainder", Box::new(operators::remainder::Remainder2))
|
(i.i("remainder"), ConstTree::xfn(operators::remainder::Remainder2))
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
21
src/external/num/numeric.rs
vendored
21
src/external/num/numeric.rs
vendored
@@ -4,9 +4,11 @@ use std::rc::Rc;
|
|||||||
use ordered_float::NotNan;
|
use ordered_float::NotNan;
|
||||||
|
|
||||||
use crate::external::assertion_error::AssertionError;
|
use crate::external::assertion_error::AssertionError;
|
||||||
|
use crate::external::litconv::with_lit;
|
||||||
use crate::foreign::ExternError;
|
use crate::foreign::ExternError;
|
||||||
use crate::representations::{Primitive, Literal};
|
use crate::representations::Literal;
|
||||||
use crate::representations::interpreted::Clause;
|
use crate::representations::Primitive;
|
||||||
|
use crate::representations::interpreted::{Clause, ExprInst};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum Numeric {
|
pub enum Numeric {
|
||||||
@@ -93,17 +95,14 @@ impl Rem for Numeric {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Clause> for Numeric {
|
impl TryFrom<ExprInst> for Numeric {
|
||||||
type Error = Rc<dyn ExternError>;
|
type Error = Rc<dyn ExternError>;
|
||||||
fn try_from(value: Clause) -> Result<Self, Self::Error> {
|
fn try_from(value: ExprInst) -> Result<Self, Self::Error> {
|
||||||
let l = if let Clause::P(Primitive::Literal(l)) = value.clone() {l} else {
|
with_lit(&value.clone(), |l| match l {
|
||||||
AssertionError::fail(value, "a literal value")?
|
Literal::Uint(i) => Ok(Numeric::Uint(*i)),
|
||||||
};
|
Literal::Num(n) => Ok(Numeric::Num(*n)),
|
||||||
match l {
|
|
||||||
Literal::Uint(i) => Ok(Numeric::Uint(i)),
|
|
||||||
Literal::Num(n) => Ok(Numeric::Num(n)),
|
|
||||||
_ => AssertionError::fail(value, "an integer or number")?
|
_ => AssertionError::fail(value, "an integer or number")?
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
27
src/external/num/operators/add.rs
vendored
27
src/external/num/operators/add.rs
vendored
@@ -2,10 +2,9 @@
|
|||||||
use super::super::Numeric;
|
use super::super::Numeric;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
use crate::representations::interpreted::Clause;
|
use crate::representations::interpreted::ExprInst;
|
||||||
|
|
||||||
/// Add function
|
/// Add function
|
||||||
///
|
///
|
||||||
@@ -13,29 +12,29 @@ use crate::representations::interpreted::Clause;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Add2;
|
pub struct Add2;
|
||||||
externfn_impl!(Add2, |_: &Self, c: Clause| {Ok(Add1{c})});
|
externfn_impl!(Add2, |_: &Self, x: ExprInst| {Ok(Add1{x})});
|
||||||
|
|
||||||
/// Partially applied Add function
|
/// Partially applied Add function
|
||||||
///
|
///
|
||||||
/// Prev state: [Add2]; Next state: [Add0]
|
/// Prev state: [Add2]; Next state: [Add0]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Add1{ c: Clause }
|
pub struct Add1{ x: ExprInst }
|
||||||
atomic_redirect!(Add1, c);
|
atomic_redirect!(Add1, x);
|
||||||
atomic_impl!(Add1);
|
atomic_impl!(Add1);
|
||||||
externfn_impl!(Add1, |this: &Self, c: Clause| {
|
externfn_impl!(Add1, |this: &Self, x: ExprInst| {
|
||||||
let a: Numeric = this.c.clone().try_into()?;
|
let a: Numeric = this.x.clone().try_into()?;
|
||||||
Ok(Add0{ a, c })
|
Ok(Add0{ a, x })
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Fully applied Add function.
|
/// Fully applied Add function.
|
||||||
///
|
///
|
||||||
/// Prev state: [Add1]
|
/// Prev state: [Add1]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Add0 { a: Numeric, c: Clause }
|
pub struct Add0 { a: Numeric, x: ExprInst }
|
||||||
atomic_redirect!(Add0, c);
|
atomic_redirect!(Add0, x);
|
||||||
atomic_impl!(Add0, |Self{ a, c }: &Self| {
|
atomic_impl!(Add0, |Self{ a, x }: &Self| {
|
||||||
let b: Numeric = c.clone().try_into()?;
|
let b: Numeric = x.clone().try_into()?;
|
||||||
Ok((*a + b).into())
|
Ok((*a + b).into())
|
||||||
});
|
});
|
||||||
|
|||||||
27
src/external/num/operators/divide.rs
vendored
27
src/external/num/operators/divide.rs
vendored
@@ -2,10 +2,9 @@
|
|||||||
use super::super::Numeric;
|
use super::super::Numeric;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
use crate::representations::interpreted::Clause;
|
use crate::representations::interpreted::ExprInst;
|
||||||
|
|
||||||
/// Divide function
|
/// Divide function
|
||||||
///
|
///
|
||||||
@@ -13,29 +12,29 @@ use crate::representations::interpreted::Clause;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Divide2;
|
pub struct Divide2;
|
||||||
externfn_impl!(Divide2, |_: &Self, c: Clause| {Ok(Divide1{c})});
|
externfn_impl!(Divide2, |_: &Self, x: ExprInst| {Ok(Divide1{x})});
|
||||||
|
|
||||||
/// Partially applied Divide function
|
/// Partially applied Divide function
|
||||||
///
|
///
|
||||||
/// Prev state: [Divide2]; Next state: [Divide0]
|
/// Prev state: [Divide2]; Next state: [Divide0]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Divide1{ c: Clause }
|
pub struct Divide1{ x: ExprInst }
|
||||||
atomic_redirect!(Divide1, c);
|
atomic_redirect!(Divide1, x);
|
||||||
atomic_impl!(Divide1);
|
atomic_impl!(Divide1);
|
||||||
externfn_impl!(Divide1, |this: &Self, c: Clause| {
|
externfn_impl!(Divide1, |this: &Self, x: ExprInst| {
|
||||||
let a: Numeric = this.c.clone().try_into()?;
|
let a: Numeric = this.x.clone().try_into()?;
|
||||||
Ok(Divide0{ a, c })
|
Ok(Divide0{ a, x })
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Fully applied Divide function.
|
/// Fully applied Divide function.
|
||||||
///
|
///
|
||||||
/// Prev state: [Divide1]
|
/// Prev state: [Divide1]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Divide0 { a: Numeric, c: Clause }
|
pub struct Divide0 { a: Numeric, x: ExprInst }
|
||||||
atomic_redirect!(Divide0, c);
|
atomic_redirect!(Divide0, x);
|
||||||
atomic_impl!(Divide0, |Self{ a, c }: &Self| {
|
atomic_impl!(Divide0, |Self{ a, x }: &Self| {
|
||||||
let b: Numeric = c.clone().try_into()?;
|
let b: Numeric = x.clone().try_into()?;
|
||||||
Ok((*a / b).into())
|
Ok((*a / b).into())
|
||||||
});
|
});
|
||||||
27
src/external/num/operators/multiply.rs
vendored
27
src/external/num/operators/multiply.rs
vendored
@@ -2,10 +2,9 @@
|
|||||||
use super::super::Numeric;
|
use super::super::Numeric;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
use crate::representations::interpreted::Clause;
|
use crate::representations::interpreted::ExprInst;
|
||||||
|
|
||||||
/// Multiply function
|
/// Multiply function
|
||||||
///
|
///
|
||||||
@@ -13,29 +12,29 @@ use crate::representations::interpreted::Clause;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Multiply2;
|
pub struct Multiply2;
|
||||||
externfn_impl!(Multiply2, |_: &Self, c: Clause| {Ok(Multiply1{c})});
|
externfn_impl!(Multiply2, |_: &Self, x: ExprInst| {Ok(Multiply1{x})});
|
||||||
|
|
||||||
/// Partially applied Multiply function
|
/// Partially applied Multiply function
|
||||||
///
|
///
|
||||||
/// Prev state: [Multiply2]; Next state: [Multiply0]
|
/// Prev state: [Multiply2]; Next state: [Multiply0]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Multiply1{ c: Clause }
|
pub struct Multiply1{ x: ExprInst }
|
||||||
atomic_redirect!(Multiply1, c);
|
atomic_redirect!(Multiply1, x);
|
||||||
atomic_impl!(Multiply1);
|
atomic_impl!(Multiply1);
|
||||||
externfn_impl!(Multiply1, |this: &Self, c: Clause| {
|
externfn_impl!(Multiply1, |this: &Self, x: ExprInst| {
|
||||||
let a: Numeric = this.c.clone().try_into()?;
|
let a: Numeric = this.x.clone().try_into()?;
|
||||||
Ok(Multiply0{ a, c })
|
Ok(Multiply0{ a, x })
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Fully applied Multiply function.
|
/// Fully applied Multiply function.
|
||||||
///
|
///
|
||||||
/// Prev state: [Multiply1]
|
/// Prev state: [Multiply1]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Multiply0 { a: Numeric, c: Clause }
|
pub struct Multiply0 { a: Numeric, x: ExprInst }
|
||||||
atomic_redirect!(Multiply0, c);
|
atomic_redirect!(Multiply0, x);
|
||||||
atomic_impl!(Multiply0, |Self{ a, c }: &Self| {
|
atomic_impl!(Multiply0, |Self{ a, x }: &Self| {
|
||||||
let b: Numeric = c.clone().try_into()?;
|
let b: Numeric = x.clone().try_into()?;
|
||||||
Ok((*a * b).into())
|
Ok((*a * b).into())
|
||||||
});
|
});
|
||||||
27
src/external/num/operators/remainder.rs
vendored
27
src/external/num/operators/remainder.rs
vendored
@@ -2,10 +2,9 @@
|
|||||||
use super::super::Numeric;
|
use super::super::Numeric;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
use crate::representations::interpreted::Clause;
|
use crate::representations::interpreted::ExprInst;
|
||||||
|
|
||||||
/// Remainder function
|
/// Remainder function
|
||||||
///
|
///
|
||||||
@@ -13,29 +12,29 @@ use crate::representations::interpreted::Clause;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Remainder2;
|
pub struct Remainder2;
|
||||||
externfn_impl!(Remainder2, |_: &Self, c: Clause| {Ok(Remainder1{c})});
|
externfn_impl!(Remainder2, |_: &Self, x: ExprInst| {Ok(Remainder1{x})});
|
||||||
|
|
||||||
/// Partially applied Remainder function
|
/// Partially applied Remainder function
|
||||||
///
|
///
|
||||||
/// Prev state: [Remainder2]; Next state: [Remainder0]
|
/// Prev state: [Remainder2]; Next state: [Remainder0]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Remainder1{ c: Clause }
|
pub struct Remainder1{ x: ExprInst }
|
||||||
atomic_redirect!(Remainder1, c);
|
atomic_redirect!(Remainder1, x);
|
||||||
atomic_impl!(Remainder1);
|
atomic_impl!(Remainder1);
|
||||||
externfn_impl!(Remainder1, |this: &Self, c: Clause| {
|
externfn_impl!(Remainder1, |this: &Self, x: ExprInst| {
|
||||||
let a: Numeric = this.c.clone().try_into()?;
|
let a: Numeric = this.x.clone().try_into()?;
|
||||||
Ok(Remainder0{ a, c })
|
Ok(Remainder0{ a, x })
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Fully applied Remainder function.
|
/// Fully applied Remainder function.
|
||||||
///
|
///
|
||||||
/// Prev state: [Remainder1]
|
/// Prev state: [Remainder1]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Remainder0 { a: Numeric, c: Clause }
|
pub struct Remainder0 { a: Numeric, x: ExprInst }
|
||||||
atomic_redirect!(Remainder0, c);
|
atomic_redirect!(Remainder0, x);
|
||||||
atomic_impl!(Remainder0, |Self{ a, c }: &Self| {
|
atomic_impl!(Remainder0, |Self{ a, x }: &Self| {
|
||||||
let b: Numeric = c.clone().try_into()?;
|
let b: Numeric = x.clone().try_into()?;
|
||||||
Ok((*a % b).into())
|
Ok((*a % b).into())
|
||||||
});
|
});
|
||||||
27
src/external/num/operators/subtract.rs
vendored
27
src/external/num/operators/subtract.rs
vendored
@@ -2,10 +2,9 @@
|
|||||||
use super::super::Numeric;
|
use super::super::Numeric;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
use crate::representations::interpreted::{Clause};
|
use crate::representations::interpreted::ExprInst;
|
||||||
|
|
||||||
/// Subtract function
|
/// Subtract function
|
||||||
///
|
///
|
||||||
@@ -13,29 +12,29 @@ use crate::representations::interpreted::{Clause};
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Subtract2;
|
pub struct Subtract2;
|
||||||
externfn_impl!(Subtract2, |_: &Self, c: Clause| {Ok(Subtract1{c})});
|
externfn_impl!(Subtract2, |_: &Self, x: ExprInst| {Ok(Subtract1{x})});
|
||||||
|
|
||||||
/// Partially applied Subtract function
|
/// Partially applied Subtract function
|
||||||
///
|
///
|
||||||
/// Prev state: [Subtract2]; Next state: [Subtract0]
|
/// Prev state: [Subtract2]; Next state: [Subtract0]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Subtract1{ c: Clause }
|
pub struct Subtract1{ x: ExprInst }
|
||||||
atomic_redirect!(Subtract1, c);
|
atomic_redirect!(Subtract1, x);
|
||||||
atomic_impl!(Subtract1);
|
atomic_impl!(Subtract1);
|
||||||
externfn_impl!(Subtract1, |this: &Self, c: Clause| {
|
externfn_impl!(Subtract1, |this: &Self, x: ExprInst| {
|
||||||
let a: Numeric = this.c.clone().try_into()?;
|
let a: Numeric = this.x.clone().try_into()?;
|
||||||
Ok(Subtract0{ a, c })
|
Ok(Subtract0{ a, x })
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Fully applied Subtract function.
|
/// Fully applied Subtract function.
|
||||||
///
|
///
|
||||||
/// Prev state: [Subtract1]
|
/// Prev state: [Subtract1]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Subtract0 { a: Numeric, c: Clause }
|
pub struct Subtract0 { a: Numeric, x: ExprInst }
|
||||||
atomic_redirect!(Subtract0, c);
|
atomic_redirect!(Subtract0, x);
|
||||||
atomic_impl!(Subtract0, |Self{ a, c }: &Self| {
|
atomic_impl!(Subtract0, |Self{ a, x }: &Self| {
|
||||||
let b: Numeric = c.clone().try_into()?;
|
let b: Numeric = x.clone().try_into()?;
|
||||||
Ok((*a - b).into())
|
Ok((*a - b).into())
|
||||||
});
|
});
|
||||||
19
src/external/std.rs
vendored
19
src/external/std.rs
vendored
@@ -1,6 +1,5 @@
|
|||||||
use std::collections::HashMap;
|
use crate::pipeline::ConstTree;
|
||||||
|
use crate::interner::Interner;
|
||||||
use crate::project::{map_loader, Loader};
|
|
||||||
|
|
||||||
use super::bool::bool;
|
use super::bool::bool;
|
||||||
use super::cpsio::cpsio;
|
use super::cpsio::cpsio;
|
||||||
@@ -8,12 +7,10 @@ use super::conv::conv;
|
|||||||
use super::str::str;
|
use super::str::str;
|
||||||
use super::num::num;
|
use super::num::num;
|
||||||
|
|
||||||
pub fn std() -> impl Loader {
|
pub fn std(i: &Interner) -> ConstTree {
|
||||||
map_loader(HashMap::from([
|
cpsio(i)
|
||||||
("cpsio", cpsio().boxed()),
|
+ conv(i)
|
||||||
("conv", conv().boxed()),
|
+ bool(i)
|
||||||
("bool", bool().boxed()),
|
+ str(i)
|
||||||
("str", str().boxed()),
|
+ num(i)
|
||||||
("num", num().boxed()),
|
|
||||||
]))
|
|
||||||
}
|
}
|
||||||
33
src/external/str/char_at.rs
vendored
33
src/external/str/char_at.rs
vendored
@@ -1,11 +1,10 @@
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
use crate::external::assertion_error::AssertionError;
|
use crate::external::litconv::{with_str, with_uint};
|
||||||
use crate::external::runtime_error::RuntimeError;
|
use crate::external::runtime_error::RuntimeError;
|
||||||
use crate::representations::{Literal, Primitive};
|
use crate::representations::{Literal, Primitive};
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
use crate::representations::interpreted::Clause;
|
use crate::representations::interpreted::{Clause, ExprInst};
|
||||||
|
|
||||||
/// CharAt function
|
/// CharAt function
|
||||||
///
|
///
|
||||||
@@ -13,35 +12,31 @@ use crate::representations::interpreted::Clause;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CharAt2;
|
pub struct CharAt2;
|
||||||
externfn_impl!(CharAt2, |_: &Self, c: Clause| {Ok(CharAt1{c})});
|
externfn_impl!(CharAt2, |_: &Self, x: ExprInst| {Ok(CharAt1{x})});
|
||||||
|
|
||||||
/// Partially applied CharAt function
|
/// Partially applied CharAt function
|
||||||
///
|
///
|
||||||
/// Prev state: [CharAt2]; Next state: [CharAt0]
|
/// Prev state: [CharAt2]; Next state: [CharAt0]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CharAt1{ c: Clause }
|
pub struct CharAt1{ x: ExprInst }
|
||||||
atomic_redirect!(CharAt1, c);
|
atomic_redirect!(CharAt1, x);
|
||||||
atomic_impl!(CharAt1);
|
atomic_impl!(CharAt1);
|
||||||
externfn_impl!(CharAt1, |this: &Self, c: Clause| {
|
externfn_impl!(CharAt1, |this: &Self, x: ExprInst| {
|
||||||
let s = if let Ok(Literal::Str(s)) = this.c.clone().try_into() {s}
|
with_str(&this.x, |s| Ok(CharAt0{ s: s.clone(), x }))
|
||||||
else {AssertionError::fail(this.c.clone(), "a string")?};
|
|
||||||
Ok(CharAt0{ s, c })
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Fully applied CharAt function.
|
/// Fully applied CharAt function.
|
||||||
///
|
///
|
||||||
/// Prev state: [CharAt1]
|
/// Prev state: [CharAt1]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CharAt0 { s: String, c: Clause }
|
pub struct CharAt0 { s: String, x: ExprInst }
|
||||||
atomic_redirect!(CharAt0, c);
|
atomic_redirect!(CharAt0, x);
|
||||||
atomic_impl!(CharAt0, |Self{ s, c }: &Self| {
|
atomic_impl!(CharAt0, |Self{ s, x }: &Self| {
|
||||||
let i = if let Ok(Literal::Uint(i)) = c.clone().try_into() {i}
|
with_uint(x, |i| if let Some(c) = s.chars().nth(i as usize) {
|
||||||
else {AssertionError::fail(c.clone(), "an uint")?};
|
|
||||||
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("Character index out of bounds".to_string(), "indexing string")?
|
RuntimeError::fail("Character index out of bounds".to_string(), "indexing string")?
|
||||||
}
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
13
src/external/str/cls2str.rs
vendored
13
src/external/str/cls2str.rs
vendored
@@ -1,13 +0,0 @@
|
|||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use crate::foreign::ExternError;
|
|
||||||
use crate::external::assertion_error::AssertionError;
|
|
||||||
use crate::representations::{interpreted::Clause, Literal};
|
|
||||||
|
|
||||||
pub fn cls2str(c: &Clause) -> Result<&String, Rc<dyn ExternError>> {
|
|
||||||
let literal: &Literal = c.try_into()
|
|
||||||
.map_err(|_| AssertionError::ext(c.clone(), "a literal value"))?;
|
|
||||||
if let Literal::Str(s) = literal {Ok(s)} else {
|
|
||||||
AssertionError::fail(c.clone(), "a string")?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
26
src/external/str/concatenate.rs
vendored
26
src/external/str/concatenate.rs
vendored
@@ -1,11 +1,9 @@
|
|||||||
use super::cls2str;
|
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::Hash;
|
|
||||||
|
|
||||||
|
use crate::external::litconv::with_str;
|
||||||
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
use crate::{atomic_impl, atomic_redirect, externfn_impl};
|
||||||
use crate::representations::{Primitive, Literal};
|
use crate::representations::{Primitive, Literal};
|
||||||
use crate::representations::interpreted::Clause;
|
use crate::representations::interpreted::{Clause, ExprInst};
|
||||||
|
|
||||||
/// Concatenate function
|
/// Concatenate function
|
||||||
///
|
///
|
||||||
@@ -13,29 +11,29 @@ use crate::representations::interpreted::Clause;
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Concatenate2;
|
pub struct Concatenate2;
|
||||||
externfn_impl!(Concatenate2, |_: &Self, c: Clause| {Ok(Concatenate1{c})});
|
externfn_impl!(Concatenate2, |_: &Self, c: ExprInst| {Ok(Concatenate1{c})});
|
||||||
|
|
||||||
/// Partially applied Concatenate function
|
/// Partially applied Concatenate function
|
||||||
///
|
///
|
||||||
/// Prev state: [Concatenate2]; Next state: [Concatenate0]
|
/// Prev state: [Concatenate2]; Next state: [Concatenate0]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Concatenate1{ c: Clause }
|
pub struct Concatenate1{ c: ExprInst }
|
||||||
atomic_redirect!(Concatenate1, c);
|
atomic_redirect!(Concatenate1, c);
|
||||||
atomic_impl!(Concatenate1);
|
atomic_impl!(Concatenate1);
|
||||||
externfn_impl!(Concatenate1, |this: &Self, c: Clause| {
|
externfn_impl!(Concatenate1, |this: &Self, c: ExprInst| {
|
||||||
let a: String = cls2str(&this.c)?.clone();
|
with_str(&this.c, |a| Ok(Concatenate0{ a: a.clone(), c }))
|
||||||
Ok(Concatenate0{ a, c })
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Fully applied Concatenate function.
|
/// Fully applied Concatenate function.
|
||||||
///
|
///
|
||||||
/// Prev state: [Concatenate1]
|
/// Prev state: [Concatenate1]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Concatenate0 { a: String, c: Clause }
|
pub struct Concatenate0 { a: String, c: ExprInst }
|
||||||
atomic_redirect!(Concatenate0, c);
|
atomic_redirect!(Concatenate0, c);
|
||||||
atomic_impl!(Concatenate0, |Self{ a, c }: &Self| {
|
atomic_impl!(Concatenate0, |Self{ a, c }: &Self| {
|
||||||
let b: &String = cls2str(c)?;
|
with_str(c, |b| Ok(Clause::P(Primitive::Literal(
|
||||||
Ok(Clause::P(Primitive::Literal(Literal::Str(a.to_owned() + b))))
|
Literal::Str(a.to_owned() + b)
|
||||||
|
))))
|
||||||
});
|
});
|
||||||
|
|||||||
11
src/external/str/mod.rs
vendored
11
src/external/str/mod.rs
vendored
@@ -1,11 +1,10 @@
|
|||||||
mod concatenate;
|
mod concatenate;
|
||||||
mod cls2str;
|
|
||||||
mod char_at;
|
mod char_at;
|
||||||
pub use cls2str::cls2str;
|
|
||||||
use crate::project::{Loader, extlib_loader};
|
|
||||||
|
|
||||||
pub fn str() -> impl Loader {
|
use crate::{pipeline::ConstTree, interner::Interner};
|
||||||
extlib_loader(vec![
|
|
||||||
("concatenate", Box::new(concatenate::Concatenate2))
|
pub fn str(i: &Interner) -> ConstTree {
|
||||||
|
ConstTree::tree([
|
||||||
|
(i.i("concatenate"), ConstTree::xfn(concatenate::Concatenate2))
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@@ -5,9 +5,17 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use dyn_clone::DynClone;
|
use dyn_clone::DynClone;
|
||||||
|
|
||||||
use crate::representations::interpreted::{
|
use crate::interpreter::{RuntimeError, Context};
|
||||||
Clause, RuntimeError, InternalError
|
|
||||||
};
|
use crate::representations::Primitive;
|
||||||
|
pub use crate::representations::interpreted::Clause;
|
||||||
|
use crate::representations::interpreted::ExprInst;
|
||||||
|
|
||||||
|
// Aliases for concise macros
|
||||||
|
pub type RcError = Rc<dyn ExternError>;
|
||||||
|
pub type AtomicResult = Result<(Clause, Option<usize>), RuntimeError>;
|
||||||
|
pub type XfnResult = Result<(Clause, Option<usize>), RcError>;
|
||||||
|
pub type RcExpr = ExprInst;
|
||||||
|
|
||||||
pub trait ExternError: Display {
|
pub trait ExternError: Display {
|
||||||
fn into_extern(self) -> Rc<dyn ExternError>
|
fn into_extern(self) -> Rc<dyn ExternError>
|
||||||
@@ -21,10 +29,13 @@ pub trait ExternError: Display {
|
|||||||
/// these are also external functions.
|
/// these are also external functions.
|
||||||
pub trait ExternFn: DynClone {
|
pub trait ExternFn: DynClone {
|
||||||
fn name(&self) -> &str;
|
fn name(&self) -> &str;
|
||||||
fn apply(&self, arg: Clause) -> Result<Clause, Rc<dyn ExternError>>;
|
fn apply(&self, arg: ExprInst, ctx: Context) -> XfnResult;
|
||||||
fn hash(&self, state: &mut dyn std::hash::Hasher) {
|
fn hash(&self, state: &mut dyn std::hash::Hasher) {
|
||||||
state.write_str(self.name())
|
state.write_str(self.name())
|
||||||
}
|
}
|
||||||
|
fn to_xfn_cls(self) -> Clause where Self: Sized + 'static {
|
||||||
|
Clause::P(Primitive::ExternFn(Box::new(self)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for dyn ExternFn {}
|
impl Eq for dyn ExternFn {}
|
||||||
@@ -44,11 +55,10 @@ impl Debug for dyn ExternFn {
|
|||||||
|
|
||||||
pub trait Atomic: Any + Debug + DynClone where Self: 'static {
|
pub trait Atomic: Any + Debug + DynClone where Self: 'static {
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
fn definitely_eq(&self, _other: &dyn Any) -> bool;
|
fn run(&self, ctx: Context) -> AtomicResult;
|
||||||
fn hash(&self, hasher: &mut dyn std::hash::Hasher);
|
fn to_atom_cls(self) -> Clause where Self: Sized {
|
||||||
fn run_once(&self) -> Result<Clause, InternalError>;
|
Clause::P(Primitive::Atom(Atom(Box::new(self))))
|
||||||
fn run_n_times(&self, n: usize) -> Result<(Clause, usize), RuntimeError>;
|
}
|
||||||
fn run_to_completion(&self) -> Result<Clause, RuntimeError>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a black box unit of code with its own normalization steps.
|
/// Represents a black box unit of code with its own normalization steps.
|
||||||
@@ -83,19 +93,8 @@ impl Clone for Atom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Atom {
|
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
|
||||||
self.0.hash(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Debug for Atom {
|
impl Debug for Atom {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "##ATOM[{:?}]##", self.data())
|
write!(f, "##ATOM[{:?}]##", self.data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Eq for Atom {}
|
|
||||||
impl PartialEq for Atom {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.data().definitely_eq(other.data().as_any())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,11 +9,5 @@ use crate::foreign::Atomic;
|
|||||||
macro_rules! atomic_defaults {
|
macro_rules! atomic_defaults {
|
||||||
() => {
|
() => {
|
||||||
fn as_any(&self) -> &dyn std::any::Any { self }
|
fn as_any(&self) -> &dyn std::any::Any { self }
|
||||||
fn definitely_eq(&self, _other: &dyn std::any::Any) -> bool {
|
|
||||||
_other.downcast_ref().map(|o| self == o).unwrap_or(false)
|
|
||||||
}
|
|
||||||
fn hash(&self, mut hasher: &mut dyn std::hash::Hasher) {
|
|
||||||
<Self as std::hash::Hash>::hash(self, &mut hasher)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -39,66 +39,40 @@ use std::fmt::Debug;
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! atomic_impl {
|
macro_rules! atomic_impl {
|
||||||
($typ:ident) => {
|
($typ:ident) => {
|
||||||
atomic_impl!{$typ, |this: &Self| Ok(Clause::P(
|
atomic_impl!{$typ, |this: &Self| {
|
||||||
$crate::representations::Primitive::ExternFn(Box::new(this.clone()))
|
use $crate::foreign::ExternFn;
|
||||||
))}
|
Ok(this.clone().to_xfn_cls())
|
||||||
|
}}
|
||||||
};
|
};
|
||||||
($typ:ident, $next_phase:expr) => {
|
($typ:ident, $next_phase:expr) => {
|
||||||
impl $crate::foreign::Atomic for $typ {
|
impl $crate::foreign::Atomic for $typ {
|
||||||
$crate::atomic_defaults!{}
|
$crate::atomic_defaults!{}
|
||||||
fn run_once(&self) -> Result<
|
|
||||||
$crate::representations::interpreted::Clause,
|
fn run(&self, ctx: $crate::interpreter::Context)
|
||||||
$crate::representations::interpreted::InternalError
|
-> $crate::foreign::AtomicResult
|
||||||
> {
|
{
|
||||||
match <Self as AsRef<$crate::representations::interpreted::Clause>>::as_ref(self).run_once() {
|
// extract the expression
|
||||||
Err($crate::representations::interpreted::InternalError::NonReducible) => {
|
let expr = <Self as
|
||||||
($next_phase)(self)
|
AsRef<$crate::foreign::RcExpr>
|
||||||
.map_err($crate::representations::interpreted::RuntimeError::Extern)
|
>::as_ref(self).clone();
|
||||||
.map_err($crate::representations::interpreted::InternalError::Runtime)
|
// run the expression
|
||||||
}
|
let ret = $crate::interpreter::run(expr, ctx)?;
|
||||||
Ok(arg) => Ok($crate::representations::interpreted::Clause::P(
|
let $crate::interpreter::Return{ gas, state } = ret;
|
||||||
$crate::representations::Primitive::Atom(
|
// rebuild the atomic
|
||||||
$crate::foreign::Atom::new(
|
let next_self = <Self as
|
||||||
<Self as From<(&Self, Clause)>>::from((self, arg))
|
From<(&Self, $crate::foreign::RcExpr)>
|
||||||
|
>::from((self, state));
|
||||||
|
// branch off or wrap up
|
||||||
|
let next_clause = if gas.map(|g| g > 0).unwrap_or(true) {
|
||||||
|
match ($next_phase)(&next_self) {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => return Err(
|
||||||
|
$crate::interpreter::RuntimeError::Extern(e)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
)),
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn run_n_times(&self, n: usize) -> Result<
|
|
||||||
(
|
|
||||||
$crate::representations::interpreted::Clause,
|
|
||||||
usize
|
|
||||||
),
|
|
||||||
$crate::representations::interpreted::RuntimeError
|
|
||||||
> {
|
|
||||||
match <Self as AsRef<Clause>>::as_ref(self).run_n_times(n) {
|
|
||||||
Ok((arg, k)) if k == n => Ok((Clause::P(
|
|
||||||
$crate::representations::Primitive::Atom(
|
|
||||||
$crate::foreign::Atom::new(
|
|
||||||
<Self as From<(&Self, Clause)>>::from((self, arg))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
), k)),
|
|
||||||
Ok((arg, k)) => {
|
|
||||||
let intermediate = <Self as From<(&Self, Clause)>>::from((self, arg));
|
|
||||||
($next_phase)(&intermediate)
|
|
||||||
.map(|cls| (cls, k))
|
|
||||||
.map_err($crate::representations::interpreted::RuntimeError::Extern)
|
|
||||||
}
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn run_to_completion(&self) -> Result<Clause, $crate::representations::interpreted::RuntimeError> {
|
|
||||||
match <Self as AsRef<Clause>>::as_ref(self).run_to_completion() {
|
|
||||||
Ok(arg) => {
|
|
||||||
let intermediate = <Self as From<(&Self, Clause)>>::from((self, arg));
|
|
||||||
($next_phase)(&intermediate)
|
|
||||||
.map_err($crate::representations::interpreted::RuntimeError::Extern)
|
|
||||||
},
|
|
||||||
Err(e) => Err(e)
|
|
||||||
}
|
}
|
||||||
|
} else { next_self.to_atom_cls() };
|
||||||
|
// package and return
|
||||||
|
Ok((next_clause, gas))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,34 +14,11 @@ macro_rules! atomic_inert {
|
|||||||
($typ:ident) => {
|
($typ:ident) => {
|
||||||
impl $crate::foreign::Atomic for $typ {
|
impl $crate::foreign::Atomic for $typ {
|
||||||
$crate::atomic_defaults!{}
|
$crate::atomic_defaults!{}
|
||||||
fn run_once(&self) -> Result<
|
|
||||||
$crate::representations::interpreted::Clause,
|
fn run(&self, ctx: $crate::interpreter::Context)
|
||||||
$crate::representations::interpreted::InternalError
|
-> $crate::foreign::AtomicResult
|
||||||
> {
|
{
|
||||||
Err($crate::representations::interpreted::InternalError::NonReducible)
|
Ok((self.clone().to_atom_cls(), ctx.gas))
|
||||||
}
|
|
||||||
fn run_n_times(&self, _: usize) -> Result<
|
|
||||||
(
|
|
||||||
$crate::representations::interpreted::Clause,
|
|
||||||
usize
|
|
||||||
),
|
|
||||||
$crate::representations::interpreted::RuntimeError
|
|
||||||
> {
|
|
||||||
Ok(($crate::representations::interpreted::Clause::P(
|
|
||||||
$crate::representations::Primitive::Atom(
|
|
||||||
$crate::foreign::Atom::new(self.clone())
|
|
||||||
)
|
|
||||||
), 0))
|
|
||||||
}
|
|
||||||
fn run_to_completion(&self) -> Result<
|
|
||||||
$crate::representations::interpreted::Clause,
|
|
||||||
$crate::representations::interpreted::RuntimeError
|
|
||||||
> {
|
|
||||||
Ok($crate::representations::interpreted::Clause::P(
|
|
||||||
$crate::representations::Primitive::Atom(
|
|
||||||
$crate::foreign::Atom::new(self.clone())
|
|
||||||
)
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,21 +6,23 @@ use super::atomic_impl;
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! atomic_redirect {
|
macro_rules! atomic_redirect {
|
||||||
($typ:ident) => {
|
($typ:ident) => {
|
||||||
impl AsRef<Clause> for $typ {
|
impl AsRef<$crate::foreign::RcExpr> for $typ {
|
||||||
fn as_ref(&self) -> &Clause { &self.0 }
|
fn as_ref(&self) -> &Clause { &self.0 }
|
||||||
}
|
}
|
||||||
impl From<(&Self, Clause)> for $typ {
|
impl From<(&Self, $crate::foreign::RcExpr)> for $typ {
|
||||||
fn from((old, clause): (&Self, Clause)) -> Self {
|
fn from((old, clause): (&Self, Clause)) -> Self {
|
||||||
Self{ 0: clause, ..old.clone() }
|
Self{ 0: clause, ..old.clone() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($typ:ident, $field:ident) => {
|
($typ:ident, $field:ident) => {
|
||||||
impl AsRef<Clause> for $typ {
|
impl AsRef<$crate::foreign::RcExpr>
|
||||||
fn as_ref(&self) -> &Clause { &self.$field }
|
for $typ {
|
||||||
|
fn as_ref(&self) -> &$crate::foreign::RcExpr { &self.$field }
|
||||||
}
|
}
|
||||||
impl From<(&Self, Clause)> for $typ {
|
impl From<(&Self, $crate::foreign::RcExpr)>
|
||||||
fn from((old, $field): (&Self, Clause)) -> Self {
|
for $typ {
|
||||||
|
fn from((old, $field): (&Self, $crate::foreign::RcExpr)) -> Self {
|
||||||
Self{ $field, ..old.clone() }
|
Self{ $field, ..old.clone() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,19 +22,18 @@ macro_rules! externfn_impl {
|
|||||||
impl $crate::foreign::ExternFn for $typ {
|
impl $crate::foreign::ExternFn for $typ {
|
||||||
fn name(&self) -> &str {stringify!($typ)}
|
fn name(&self) -> &str {stringify!($typ)}
|
||||||
fn apply(&self,
|
fn apply(&self,
|
||||||
c: $crate::representations::interpreted::Clause
|
arg: $crate::foreign::RcExpr,
|
||||||
) -> Result<
|
ctx: $crate::interpreter::Context
|
||||||
$crate::representations::interpreted::Clause,
|
) -> $crate::foreign::XfnResult {
|
||||||
std::rc::Rc<dyn $crate::foreign::ExternError>
|
match ($next_atomic)(self, arg) { // ? casts the result but we want to strictly forward it
|
||||||
> {
|
Ok(r) => Ok((
|
||||||
match ($next_atomic)(self, c) { // ? casts the result but we want to strictly forward it
|
|
||||||
Ok(r) => Ok(
|
|
||||||
$crate::representations::interpreted::Clause::P(
|
$crate::representations::interpreted::Clause::P(
|
||||||
$crate::representations::Primitive::Atom(
|
$crate::representations::Primitive::Atom(
|
||||||
$crate::foreign::Atom::new(r)
|
$crate::foreign::Atom::new(r)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
),
|
),
|
||||||
|
ctx.gas.map(|g| g - 1)
|
||||||
|
)),
|
||||||
Err(e) => Err(e)
|
Err(e) => Err(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
53
src/interner/display.rs
Normal file
53
src/interner/display.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
use core::fmt::Formatter;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::interner::Interner;
|
||||||
|
|
||||||
|
/// A variant of [std::fmt::Display] for objects that contain interned
|
||||||
|
/// strings and therefore can only be stringified in the presence of a
|
||||||
|
/// string interner
|
||||||
|
///
|
||||||
|
/// The functions defined here are suffixed to distinguish them from
|
||||||
|
/// the ones in Display and ToString respectively, because Rust can't
|
||||||
|
/// identify functions based on arity
|
||||||
|
pub trait InternedDisplay {
|
||||||
|
/// formats the value using the given formatter and string interner
|
||||||
|
fn fmt_i(&self,
|
||||||
|
f: &mut std::fmt::Formatter<'_>,
|
||||||
|
i: &Interner,
|
||||||
|
) -> std::fmt::Result;
|
||||||
|
|
||||||
|
/// Converts the value to a string to be displayed
|
||||||
|
fn to_string_i(&self, i: &Interner) -> String {
|
||||||
|
// Copied from <https://doc.rust-lang.org/src/alloc/string.rs.html#2526>
|
||||||
|
let mut buf = String::new();
|
||||||
|
let mut formatter = Formatter::new(&mut buf);
|
||||||
|
// Bypass format_args!() to avoid write_str with zero-length strs
|
||||||
|
Self::fmt_i(self, &mut formatter, i)
|
||||||
|
.expect("a Display implementation returned an error unexpectedly");
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bundle<'a>(&'a self, interner: &'a Interner)
|
||||||
|
-> DisplayBundle<'a, Self>
|
||||||
|
{
|
||||||
|
DisplayBundle { interner, data: self }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> InternedDisplay for T where T: Display {
|
||||||
|
fn fmt_i(&self, f: &mut std::fmt::Formatter<'_>, _i: &Interner) -> std::fmt::Result {
|
||||||
|
<Self as Display>::fmt(&self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DisplayBundle<'a, T: InternedDisplay + ?Sized> {
|
||||||
|
interner: &'a Interner,
|
||||||
|
data: &'a T
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: InternedDisplay> Display for DisplayBundle<'a, T> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.data.fmt_i(f, self.interner)
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/interner/mod.rs
Normal file
9
src/interner/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
mod monotype;
|
||||||
|
mod multitype;
|
||||||
|
mod token;
|
||||||
|
mod display;
|
||||||
|
|
||||||
|
pub use monotype::TypedInterner;
|
||||||
|
pub use multitype::Interner;
|
||||||
|
pub use token::Token;
|
||||||
|
pub use display::{DisplayBundle, InternedDisplay};
|
||||||
120
src/interner/monotype.rs
Normal file
120
src/interner/monotype.rs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
use std::num::NonZeroU32;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
use std::hash::{Hash, BuildHasher};
|
||||||
|
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
|
use super::token::Token;
|
||||||
|
|
||||||
|
pub struct TypedInterner<T: 'static + Eq + Hash + Clone>{
|
||||||
|
tokens: RefCell<HashMap<&'static T, Token<T>>>,
|
||||||
|
values: RefCell<Vec<(&'static T, bool)>>
|
||||||
|
}
|
||||||
|
impl<T: Eq + Hash + Clone> TypedInterner<T> {
|
||||||
|
/// Create a fresh interner instance
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
tokens: RefCell::new(HashMap::new()),
|
||||||
|
values: RefCell::new(Vec::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Intern an object, returning a token
|
||||||
|
pub fn i<Q: ?Sized + Eq + Hash + ToOwned<Owned = T>>(&self, q: &Q)
|
||||||
|
-> Token<T> where T: Borrow<Q>
|
||||||
|
{
|
||||||
|
let mut tokens = self.tokens.borrow_mut();
|
||||||
|
let hash = compute_hash(tokens.hasher(), q);
|
||||||
|
let raw_entry = tokens.raw_entry_mut().from_hash(hash, |k| {
|
||||||
|
<T as Borrow<Q>>::borrow(k) == q
|
||||||
|
});
|
||||||
|
let kv = raw_entry.or_insert_with(|| {
|
||||||
|
let mut values = self.values.borrow_mut();
|
||||||
|
let uniq_key: NonZeroU32 = (values.len() as u32 + 1u32)
|
||||||
|
.try_into().expect("can never be zero");
|
||||||
|
let keybox = Box::new(q.to_owned());
|
||||||
|
let keyref = Box::leak(keybox);
|
||||||
|
values.push((keyref, true));
|
||||||
|
let token = Token::<T>::from_id(uniq_key);
|
||||||
|
(keyref, token)
|
||||||
|
});
|
||||||
|
*kv.1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve a token, obtaining an object
|
||||||
|
/// It is illegal to use a token obtained from one interner with another.
|
||||||
|
pub fn r(&self, t: Token<T>) -> &T {
|
||||||
|
let values = self.values.borrow();
|
||||||
|
let key = t.into_usize() - 1;
|
||||||
|
values[key].0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Intern a static reference without allocating the data on the heap
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn intern_static(&self, tref: &'static T) -> Token<T> {
|
||||||
|
let mut tokens = self.tokens.borrow_mut();
|
||||||
|
let token = *tokens.raw_entry_mut().from_key(tref)
|
||||||
|
.or_insert_with(|| {
|
||||||
|
let mut values = self.values.borrow_mut();
|
||||||
|
let uniq_key: NonZeroU32 = (values.len() as u32 + 1u32)
|
||||||
|
.try_into().expect("can never be zero");
|
||||||
|
values.push((tref, false));
|
||||||
|
let token = Token::<T>::from_id(uniq_key);
|
||||||
|
(tref, token)
|
||||||
|
}).1;
|
||||||
|
token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl<T: Eq + Hash + Clone> TypedInterner<Vec<T>> {
|
||||||
|
// pub fn iv<Q>(&self, qs: &[Q]) -> Token<Vec<T>>
|
||||||
|
// where
|
||||||
|
// Q: Eq + Hash + ToOwned<Owned = T>,
|
||||||
|
// T: Borrow<Q>
|
||||||
|
// {
|
||||||
|
// let mut tokens = self.tokens.borrow_mut();
|
||||||
|
// let hash = compute_hash(tokens.hasher(), qs);
|
||||||
|
// let raw_entry = tokens.raw_entry_mut().from_hash(hash, |k| {
|
||||||
|
// k.iter().zip(qs.iter()).all(|(t, q)| t.borrow() == q)
|
||||||
|
// });
|
||||||
|
// let kv = raw_entry.or_insert_with(|| {
|
||||||
|
// let mut values = self.values.borrow_mut();
|
||||||
|
// let uniq_key: NonZeroU32 = (values.len() as u32 + 1u32)
|
||||||
|
// .try_into().expect("can never be zero");
|
||||||
|
// let tv = qs.iter().map(Q::to_owned).collect::<Vec<_>>();
|
||||||
|
// let keybox = Box::new(tv);
|
||||||
|
// let keyref = Box::leak(keybox);
|
||||||
|
// values.push((keyref, true));
|
||||||
|
// let token = Token::<Vec<T>>::from_id(uniq_key);
|
||||||
|
// (keyref, token)
|
||||||
|
// });
|
||||||
|
// *kv.1
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl<T: Eq + Hash + Clone> Drop for TypedInterner<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// make sure all values leaked by us are dropped
|
||||||
|
// FIXME: with the new hashmap logic we can actually store Rc-s
|
||||||
|
// which negates the need for unsafe here
|
||||||
|
let mut values = self.values.borrow_mut();
|
||||||
|
for (item, owned) in values.drain(..) {
|
||||||
|
if !owned {continue}
|
||||||
|
unsafe {
|
||||||
|
Box::from_raw((item as *const T).cast_mut())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to compute hashes outside a hashmap
|
||||||
|
fn compute_hash(
|
||||||
|
hash_builder: &impl BuildHasher,
|
||||||
|
key: &(impl Hash + ?Sized)
|
||||||
|
) -> u64 {
|
||||||
|
use core::hash::Hasher;
|
||||||
|
let mut state = hash_builder.build_hasher();
|
||||||
|
key.hash(&mut state);
|
||||||
|
state.finish()
|
||||||
|
}
|
||||||
102
src/interner/multitype.rs
Normal file
102
src/interner/multitype.rs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
use std::borrow::Borrow;
|
||||||
|
use std::cell::{RefCell, RefMut};
|
||||||
|
use std::any::{TypeId, Any};
|
||||||
|
use std::hash::Hash;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
|
use super::monotype::TypedInterner;
|
||||||
|
use super::token::Token;
|
||||||
|
|
||||||
|
pub struct Interner {
|
||||||
|
interners: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
|
||||||
|
}
|
||||||
|
impl Interner {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { interners: RefCell::new(HashMap::new()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn i<Q: ?Sized>(&self, q: &Q) -> Token<Q::Owned>
|
||||||
|
where Q: Eq + Hash + ToOwned,
|
||||||
|
Q::Owned: 'static + Eq + Hash + Clone,
|
||||||
|
Q::Owned: Borrow<Q>
|
||||||
|
{
|
||||||
|
let mut interners = self.interners.borrow_mut();
|
||||||
|
let interner = get_interner(&mut interners);
|
||||||
|
interner.i(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn r<T: 'static + Eq + Hash + Clone>(&self, t: Token<T>) -> &T {
|
||||||
|
let mut interners = self.interners.borrow_mut();
|
||||||
|
let interner = get_interner(&mut interners);
|
||||||
|
// TODO: figure this out
|
||||||
|
unsafe{ (interner.r(t) as *const T).as_ref().unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fully resolve
|
||||||
|
/// TODO: make this generic over containers
|
||||||
|
pub fn extern_vec<T: 'static + Eq + Hash + Clone>(&self,
|
||||||
|
t: Token<Vec<Token<T>>>
|
||||||
|
) -> Vec<T> {
|
||||||
|
let mut interners = self.interners.borrow_mut();
|
||||||
|
let v_int = get_interner(&mut interners);
|
||||||
|
let t_int = get_interner(&mut interners);
|
||||||
|
let v = v_int.r(t);
|
||||||
|
v.iter()
|
||||||
|
.map(|t| t_int.r(*t))
|
||||||
|
.cloned()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extern_all<T: 'static + Eq + Hash + Clone>(&self,
|
||||||
|
s: &[Token<T>]
|
||||||
|
) -> Vec<T> {
|
||||||
|
s.iter()
|
||||||
|
.map(|t| self.r(*t))
|
||||||
|
.cloned()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get or create an interner for a given type.
|
||||||
|
fn get_interner<T: 'static + Eq + Hash + Clone>(
|
||||||
|
interners: &mut RefMut<HashMap<TypeId, Rc<dyn Any>>>
|
||||||
|
) -> Rc<TypedInterner<T>> {
|
||||||
|
let boxed = interners.raw_entry_mut().from_key(&TypeId::of::<T>())
|
||||||
|
.or_insert_with(|| (
|
||||||
|
TypeId::of::<T>(),
|
||||||
|
Rc::new(TypedInterner::<T>::new())
|
||||||
|
)).1.clone();
|
||||||
|
boxed.downcast().expect("the typeid is supposed to protect from this")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_string() {
|
||||||
|
let interner = Interner::new();
|
||||||
|
let key1 = interner.i("foo");
|
||||||
|
let key2 = interner.i(&"foo".to_string());
|
||||||
|
assert_eq!(key1, key2)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_slice() {
|
||||||
|
let interner = Interner::new();
|
||||||
|
let key1 = interner.i(&vec![1, 2, 3]);
|
||||||
|
let key2 = interner.i(&[1, 2, 3][..]);
|
||||||
|
assert_eq!(key1, key2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn test_str_slice() {
|
||||||
|
let interner = Interner::new();
|
||||||
|
let key1 = interner.i(&vec!["a".to_string(), "b".to_string(), "c".to_string()]);
|
||||||
|
let key2 = interner.i(&["a", "b", "c"][..]);
|
||||||
|
// assert_eq!(key1, key2);
|
||||||
|
}
|
||||||
|
}
|
||||||
57
src/interner/token.rs
Normal file
57
src/interner/token.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use std::{num::NonZeroU32, marker::PhantomData};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
use std::cmp::PartialEq;
|
||||||
|
|
||||||
|
pub struct Token<T>{
|
||||||
|
id: NonZeroU32,
|
||||||
|
phantom_data: PhantomData<T>
|
||||||
|
}
|
||||||
|
impl<T> Token<T> {
|
||||||
|
pub fn from_id(id: NonZeroU32) -> Self {
|
||||||
|
Self { id, phantom_data: PhantomData }
|
||||||
|
}
|
||||||
|
pub fn into_id(self) -> NonZeroU32 {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
pub fn into_usize(self) -> usize {
|
||||||
|
let zero: u32 = self.id.into();
|
||||||
|
zero as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Debug for Token<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Token({})", self.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Copy for Token<T> {}
|
||||||
|
impl<T> Clone for Token<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self{ id: self.id, phantom_data: PhantomData }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Eq for Token<T> {}
|
||||||
|
impl<T> PartialEq for Token<T> {
|
||||||
|
fn eq(&self, other: &Self) -> bool { self.id == other.id }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Ord for Token<T> {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.id.cmp(&other.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> PartialOrd for Token<T> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(&other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Hash for Token<T> {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
state.write_u32(self.id.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
104
src/interpreter/apply.rs
Normal file
104
src/interpreter/apply.rs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
use crate::foreign::Atom;
|
||||||
|
use crate::representations::Primitive;
|
||||||
|
use crate::representations::PathSet;
|
||||||
|
use crate::representations::interpreted::{ExprInst, Clause};
|
||||||
|
use crate::utils::Side;
|
||||||
|
|
||||||
|
use super::Return;
|
||||||
|
use super::error::RuntimeError;
|
||||||
|
use super::context::Context;
|
||||||
|
|
||||||
|
/// Process the clause at the end of the provided path.
|
||||||
|
/// Note that paths always point to at least one target.
|
||||||
|
/// Note also that this is not cached as a normalization step in the
|
||||||
|
/// intermediate expressions.
|
||||||
|
fn map_at<E>(
|
||||||
|
path: &[Side], source: ExprInst,
|
||||||
|
mapper: &mut impl FnMut(&Clause) -> Result<Clause, E>
|
||||||
|
) -> Result<ExprInst, E> {
|
||||||
|
source.try_update(|value| {
|
||||||
|
// Pass right through lambdas
|
||||||
|
if let Clause::Lambda { args, body } = value {
|
||||||
|
return Ok(Clause::Lambda {
|
||||||
|
args: args.clone(),
|
||||||
|
body: map_at(path, body.clone(), mapper)?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// If the path ends here, process the next (non-lambda) node
|
||||||
|
let (head, tail) = if let Some(sf) = path.split_first() {sf} else {
|
||||||
|
return Ok(mapper(value)?)
|
||||||
|
};
|
||||||
|
// If it's an Apply, execute the next step in the path
|
||||||
|
if let Clause::Apply { f, x } = value {
|
||||||
|
return Ok(match head {
|
||||||
|
Side::Left => Clause::Apply {
|
||||||
|
f: map_at(tail, f.clone(), mapper)?,
|
||||||
|
x: x.clone(),
|
||||||
|
},
|
||||||
|
Side::Right => Clause::Apply {
|
||||||
|
f: f.clone(),
|
||||||
|
x: map_at(tail, x.clone(), mapper)?,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
panic!("Invalid path")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn substitute(paths: &PathSet, value: Clause, body: ExprInst) -> ExprInst {
|
||||||
|
let PathSet{ steps, next } = paths;
|
||||||
|
map_at(&steps, body, &mut |checkpoint| -> Result<Clause, !> {
|
||||||
|
match (checkpoint, next) {
|
||||||
|
(Clause::Lambda{..}, _) => unreachable!("Handled by map_at"),
|
||||||
|
(Clause::Apply { f, x }, Some((left, right))) => Ok(Clause::Apply {
|
||||||
|
f: substitute(&left, value.clone(), f.clone()),
|
||||||
|
x: substitute(&right, value.clone(), x.clone()),
|
||||||
|
}),
|
||||||
|
(Clause::LambdaArg, None) => Ok(value.clone()),
|
||||||
|
(_, None) => panic!("Substitution path ends in something other than LambdaArg"),
|
||||||
|
(_, Some(_)) => panic!("Substitution path leads into something other than Apply"),
|
||||||
|
}
|
||||||
|
}).into_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply a function-like expression to a parameter.
|
||||||
|
/// If any work is being done, gas will be deducted.
|
||||||
|
pub fn apply(
|
||||||
|
f: ExprInst, x: ExprInst, mut ctx: Context
|
||||||
|
) -> Result<Return, RuntimeError> {
|
||||||
|
let state = f.clone().try_update(|clause| match clause {
|
||||||
|
// apply an ExternFn or an internal function
|
||||||
|
Clause::P(Primitive::ExternFn(f)) => {
|
||||||
|
let (clause, gas) = f.apply(x, ctx.clone())
|
||||||
|
.map_err(|e| RuntimeError::Extern(e))?;
|
||||||
|
ctx.gas = gas.map(|g| g - 1); // cost of extern call
|
||||||
|
Ok(clause)
|
||||||
|
}
|
||||||
|
Clause::Lambda{args, body} => Ok(if let Some(args) = args {
|
||||||
|
let x_cls = x.expr().clause.clone();
|
||||||
|
let new_xpr_inst = substitute(args, x_cls, body.clone());
|
||||||
|
let new_xpr = new_xpr_inst.expr();
|
||||||
|
// cost of substitution
|
||||||
|
// XXX: should this be the number of occurrences instead?
|
||||||
|
ctx.gas = ctx.gas.map(|x| x - 1);
|
||||||
|
new_xpr.clause.clone()
|
||||||
|
} else {body.expr().clause.clone()}),
|
||||||
|
Clause::Constant(name) => {
|
||||||
|
let symval = ctx.symbols.get(name).expect("missing symbol for function").clone();
|
||||||
|
ctx.gas = ctx.gas.map(|x| x - 1); // cost of lookup
|
||||||
|
Ok(Clause::Apply { f: symval, x, })
|
||||||
|
}
|
||||||
|
Clause::P(Primitive::Atom(Atom(atom))) => { // take a step in expanding atom
|
||||||
|
let (clause, gas) = atom.run(ctx.clone())?;
|
||||||
|
ctx.gas = gas.map(|x| x - 1); // cost of dispatch
|
||||||
|
Ok(Clause::Apply { f: clause.wrap(), x })
|
||||||
|
},
|
||||||
|
Clause::Apply{ f: fun, x: arg } => { // take a step in resolving pre-function
|
||||||
|
let res = apply(fun.clone(), arg.clone(), ctx.clone())?;
|
||||||
|
ctx.gas = res.gas; // if work has been done, it has been paid
|
||||||
|
Ok(Clause::Apply{ f: res.state, x })
|
||||||
|
},
|
||||||
|
_ => Err(RuntimeError::NonFunctionApplication(f.clone()))
|
||||||
|
})?;
|
||||||
|
Ok(Return { state, gas: ctx.gas })
|
||||||
|
}
|
||||||
27
src/interpreter/context.rs
Normal file
27
src/interpreter/context.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
|
use crate::representations::interpreted::ExprInst;
|
||||||
|
use crate::interner::Token;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Context<'a> {
|
||||||
|
pub symbols: &'a HashMap<Token<Vec<Token<String>>>, ExprInst>,
|
||||||
|
pub gas: Option<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context<'_> {
|
||||||
|
pub fn is_stuck(&self, res: Option<usize>) -> bool {
|
||||||
|
match (res, self.gas) {
|
||||||
|
(Some(a), Some(b)) => a == b,
|
||||||
|
(None, None) => false,
|
||||||
|
(None, Some(_)) => panic!("gas not tracked despite limit"),
|
||||||
|
(Some(_), None) => panic!("gas tracked without request"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Return {
|
||||||
|
pub state: ExprInst,
|
||||||
|
pub gas: Option<usize>,
|
||||||
|
}
|
||||||
27
src/interpreter/error.rs
Normal file
27
src/interpreter/error.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::representations::interpreted::ExprInst;
|
||||||
|
use crate::foreign::ExternError;
|
||||||
|
|
||||||
|
/// Problems in the process of execution
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum RuntimeError {
|
||||||
|
Extern(Rc<dyn ExternError>),
|
||||||
|
NonFunctionApplication(ExprInst),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Rc<dyn ExternError>> for RuntimeError {
|
||||||
|
fn from(value: Rc<dyn ExternError>) -> Self {
|
||||||
|
Self::Extern(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for RuntimeError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Extern(e) => write!(f, "Error in external function: {e}"),
|
||||||
|
Self::NonFunctionApplication(loc) => write!(f, "Primitive applied as function at {loc:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/interpreter/mod.rs
Normal file
8
src/interpreter/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
mod apply;
|
||||||
|
mod error;
|
||||||
|
mod context;
|
||||||
|
mod run;
|
||||||
|
|
||||||
|
pub use context::{Context, Return};
|
||||||
|
pub use error::RuntimeError;
|
||||||
|
pub use run::{run};
|
||||||
39
src/interpreter/run.rs
Normal file
39
src/interpreter/run.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use crate::foreign::Atom;
|
||||||
|
use crate::representations::Primitive;
|
||||||
|
use crate::representations::interpreted::{Clause, ExprInst};
|
||||||
|
|
||||||
|
use super::apply::apply;
|
||||||
|
use super::error::RuntimeError;
|
||||||
|
use super::context::{Context, Return};
|
||||||
|
|
||||||
|
pub fn run(expr: ExprInst, mut ctx: Context)
|
||||||
|
-> Result<Return, RuntimeError>
|
||||||
|
{
|
||||||
|
let state = expr.try_normalize(|cls| -> Result<Clause, RuntimeError> {
|
||||||
|
let mut i = cls.clone();
|
||||||
|
while ctx.gas.map(|g| g > 0).unwrap_or(true) {
|
||||||
|
match &i {
|
||||||
|
Clause::Apply { f, x } => {
|
||||||
|
let res = apply(f.clone(), x.clone(), ctx.clone())?;
|
||||||
|
if ctx.is_stuck(res.gas) {return Ok(i)}
|
||||||
|
ctx.gas = res.gas;
|
||||||
|
i = res.state.expr().clause.clone();
|
||||||
|
}
|
||||||
|
Clause::P(Primitive::Atom(Atom(data))) => {
|
||||||
|
let (clause, gas) = data.run(ctx.clone())?;
|
||||||
|
if ctx.is_stuck(gas) {return Ok(i)}
|
||||||
|
ctx.gas = gas;
|
||||||
|
i = clause.clone();
|
||||||
|
}
|
||||||
|
Clause::Constant(c) => {
|
||||||
|
let symval = ctx.symbols.get(c).expect("missing symbol for value");
|
||||||
|
ctx.gas = ctx.gas.map(|g| g - 1); // cost of lookup
|
||||||
|
i = symval.expr().clause.clone();
|
||||||
|
}
|
||||||
|
_ => return Ok(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(i)
|
||||||
|
})?;
|
||||||
|
Ok(Return { state, gas: ctx.gas })
|
||||||
|
}
|
||||||
144
src/main.rs
144
src/main.rs
@@ -1,6 +1,3 @@
|
|||||||
#![feature(specialization)]
|
|
||||||
#![feature(adt_const_params)]
|
|
||||||
#![feature(generic_const_exprs)]
|
|
||||||
#![feature(generators, generator_trait)]
|
#![feature(generators, generator_trait)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
#![feature(unwrap_infallible)]
|
#![feature(unwrap_infallible)]
|
||||||
@@ -8,124 +5,51 @@
|
|||||||
#![feature(hasher_prefixfree_extras)]
|
#![feature(hasher_prefixfree_extras)]
|
||||||
#![feature(closure_lifetime_binder)]
|
#![feature(closure_lifetime_binder)]
|
||||||
#![feature(generic_arg_infer)]
|
#![feature(generic_arg_infer)]
|
||||||
use std::{env::current_dir, collections::HashMap};
|
#![feature(array_chunks)]
|
||||||
|
#![feature(fmt_internals)]
|
||||||
|
#![feature(map_try_insert)]
|
||||||
|
#![feature(slice_group_by)]
|
||||||
|
#![feature(trait_alias)]
|
||||||
|
|
||||||
// mod executor;
|
|
||||||
mod parse;
|
mod parse;
|
||||||
pub(crate) mod project;
|
mod interner;
|
||||||
|
mod interpreter;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod representations;
|
mod representations;
|
||||||
mod rule;
|
mod rule;
|
||||||
mod scheduler;
|
|
||||||
pub(crate) mod foreign;
|
pub(crate) mod foreign;
|
||||||
mod external;
|
mod external;
|
||||||
mod foreign_macros;
|
mod foreign_macros;
|
||||||
use lasso::Rodeo;
|
mod pipeline;
|
||||||
|
mod run_dir;
|
||||||
|
mod cli;
|
||||||
|
use std::{path::PathBuf, fs::File};
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use cli::prompt;
|
||||||
pub use representations::ast;
|
pub use representations::ast;
|
||||||
use ast::{Expr, Clause};
|
use run_dir::run_dir;
|
||||||
// use representations::typed as t;
|
|
||||||
use mappable_rc::Mrc;
|
|
||||||
use project::{rule_collector, file_loader};
|
|
||||||
use rule::Repository;
|
|
||||||
use utils::to_mrc_slice;
|
|
||||||
|
|
||||||
use crate::external::std::std;
|
/// Orchid interpreter
|
||||||
use crate::project::{map_loader, string_loader, Loader, ModuleError};
|
#[derive(Parser, Debug)]
|
||||||
use crate::representations::{ast_to_postmacro, postmacro_to_interpreted};
|
#[command(author, version, about, long_about = None)]
|
||||||
|
struct Args {
|
||||||
fn literal(orig: &[&str]) -> Mrc<[String]> {
|
/// Folder containing main.orc
|
||||||
to_mrc_slice(vliteral(orig))
|
#[arg(short, long)]
|
||||||
}
|
pub project: Option<String>
|
||||||
|
|
||||||
fn vliteral(orig: &[&str]) -> Vec<String> {
|
|
||||||
orig.iter().map(|&s| s.to_owned()).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
static PRELUDE:&str = r#"
|
|
||||||
import std::(
|
|
||||||
num::(add, subtract, multiply, remainder, divide),
|
|
||||||
bool::(equals, ifthenelse),
|
|
||||||
str::concatenate
|
|
||||||
)
|
|
||||||
|
|
||||||
export (...$a + ...$b) =1001=> (add (...$a) (...$b))
|
|
||||||
export (...$a - ...$b:1) =1001=> (subtract (...$a) (...$b))
|
|
||||||
export (...$a * ...$b) =1000=> (multiply (...$a) (...$b))
|
|
||||||
export (...$a % ...$b:1) =1000=> (remainder (...$a) (...$b))
|
|
||||||
export (...$a / ...$b:1) =1000=> (divide (...$a) (...$b))
|
|
||||||
export (...$a == ...$b) =1002=> (equals (...$a) (...$b))
|
|
||||||
export (...$a ++ ...$b) =1003=> (concatenate (...$a) (...$b))
|
|
||||||
|
|
||||||
export do { ...$statement ; ...$rest:1 } =10_001=> (
|
|
||||||
statement (...$statement) do { ...$rest }
|
|
||||||
)
|
|
||||||
export do { ...$return } =10_000=> (...$return)
|
|
||||||
|
|
||||||
export statement (let $_name = ...$value) ...$next =10_000=> (
|
|
||||||
(\$_name. ...$next) (...$value)
|
|
||||||
)
|
|
||||||
export statement (cps $_name = ...$operation) ...$next =10_001=> (
|
|
||||||
(...$operation) \$_name. ...$next
|
|
||||||
)
|
|
||||||
export statement (cps ...$operation) ...$next =10_000=> (
|
|
||||||
(...$operation) (...$next)
|
|
||||||
)
|
|
||||||
|
|
||||||
export if ...$cond then ...$true else ...$false:1 =5_000=> (
|
|
||||||
ifthenelse (...$cond) (...$true) (...$false)
|
|
||||||
)
|
|
||||||
"#;
|
|
||||||
|
|
||||||
fn initial_tree() -> Mrc<[Expr]> {
|
|
||||||
to_mrc_slice(vec![Expr(Clause::Name {
|
|
||||||
local: None,
|
|
||||||
qualified: literal(&["mod", "main", "main"])
|
|
||||||
}, to_mrc_slice(vec![]))])
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
fn load_project() {
|
|
||||||
let mut rodeo = Rodeo::default();
|
|
||||||
let collect_rules = rule_collector(
|
|
||||||
rodeo,
|
|
||||||
map_loader(HashMap::from([
|
|
||||||
("std", std().boxed()),
|
|
||||||
("prelude", string_loader(PRELUDE).boxed()),
|
|
||||||
("mod", file_loader(current_dir().expect("Missing CWD!")).boxed())
|
|
||||||
]))
|
|
||||||
);
|
|
||||||
let rules = match collect_rules.try_find(&literal(&["mod", "main"])) {
|
|
||||||
Ok(rules) => rules,
|
|
||||||
Err(err) => if let ModuleError::Syntax(pe) = err {
|
|
||||||
panic!("{}", pe);
|
|
||||||
} else {panic!("{:#?}", err)}
|
|
||||||
};
|
|
||||||
let mut tree = initial_tree();
|
|
||||||
println!("Start processing {tree:?}");
|
|
||||||
let repo = Repository::new(rules.as_ref().to_owned());
|
|
||||||
println!("Ruleset: {repo:?}");
|
|
||||||
xloop!(let mut i = 0; i < 100; i += 1; {
|
|
||||||
match repo.step(Mrc::clone(&tree)) {
|
|
||||||
Ok(Some(phase)) => {
|
|
||||||
//println!("Step {i}: {phase:?}");
|
|
||||||
tree = phase;
|
|
||||||
},
|
|
||||||
Ok(None) => {
|
|
||||||
println!("Execution complete");
|
|
||||||
break
|
|
||||||
},
|
|
||||||
Err(e) => panic!("Rule error: {e:?}")
|
|
||||||
}
|
|
||||||
}; panic!("Macro execution didn't halt"));
|
|
||||||
let pmtree = ast_to_postmacro::exprv(tree.as_ref())
|
|
||||||
.unwrap_or_else(|e| panic!("Postmacro conversion error: {e}"));
|
|
||||||
let runtree = postmacro_to_interpreted::expr_rec(&pmtree)
|
|
||||||
.unwrap_or_else(|e| panic!("Interpreted conversion error: {e}"));
|
|
||||||
let stable = runtree.run_to_completion()
|
|
||||||
.unwrap_or_else(|e| panic!("Runtime error {e}"));
|
|
||||||
println!("Settled at {stable:?}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
load_project();
|
let args = Args::parse();
|
||||||
|
let path = args.project.unwrap_or_else(|| {
|
||||||
|
prompt("Enter a project root", ".".to_string(), |p| {
|
||||||
|
let mut path: PathBuf = p.trim().into();
|
||||||
|
path.push("main.orc");
|
||||||
|
match File::open(&path) {
|
||||||
|
Ok(_) => Ok(p),
|
||||||
|
Err(e) => Err(format!("{}: {e}", path.display()))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
run_dir(&PathBuf::try_from(path).unwrap());
|
||||||
}
|
}
|
||||||
|
|||||||
48
src/parse/context.rs
Normal file
48
src/parse/context.rs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::interner::Interner;
|
||||||
|
|
||||||
|
/// Trait enclosing all context features
|
||||||
|
///
|
||||||
|
/// Hiding type parameters in associated types allows for simpler
|
||||||
|
/// parser definitions
|
||||||
|
pub trait Context: Clone {
|
||||||
|
type Op: AsRef<str>;
|
||||||
|
|
||||||
|
fn ops<'a>(&'a self) -> &'a [Self::Op];
|
||||||
|
fn file(&self) -> Rc<Vec<String>>;
|
||||||
|
fn interner<'a>(&'a self) -> &'a Interner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Struct implementing context
|
||||||
|
///
|
||||||
|
/// Hiding type parameters in associated types allows for simpler
|
||||||
|
/// parser definitions
|
||||||
|
pub struct ParsingContext<'a, Op> {
|
||||||
|
pub ops: &'a [Op],
|
||||||
|
pub interner: &'a Interner,
|
||||||
|
pub file: Rc<Vec<String>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Op> ParsingContext<'a, Op> {
|
||||||
|
pub fn new(ops: &'a [Op], interner: &'a Interner, file: Rc<Vec<String>>)
|
||||||
|
-> Self { Self { ops, interner, file } }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Op> Clone for ParsingContext<'a, Op> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
ops: self.ops,
|
||||||
|
interner: self.interner,
|
||||||
|
file: self.file.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Op: AsRef<str>> Context for ParsingContext<'_, Op> {
|
||||||
|
type Op = Op;
|
||||||
|
|
||||||
|
fn interner<'a>(&'a self) -> &'a Interner { self.interner }
|
||||||
|
fn file(&self) -> Rc<Vec<String>> {self.file.clone()}
|
||||||
|
fn ops<'a>(&'a self) -> &'a [Self::Op] { self.ops }
|
||||||
|
}
|
||||||
46
src/parse/enum_filter.rs
Normal file
46
src/parse/enum_filter.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/// Produces filter_mapping functions for enum types:
|
||||||
|
/// ```rs
|
||||||
|
/// enum_parser!(Foo::Bar | "Some error!") // Accepts Foo::Bar(T) into T
|
||||||
|
/// enum_parser!(Foo::Bar) // same as above but with the default error "Expected Foo::Bar"
|
||||||
|
/// enum_parser!(Foo >> Quz; Bar, Baz) // Parses Foo::Bar(T) into Quz::Bar(T) and Foo::Baz(U) into Quz::Baz(U)
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! enum_filter {
|
||||||
|
($p:path | $m:tt) => {
|
||||||
|
{
|
||||||
|
|l| {
|
||||||
|
if let $p(x) = l { Ok(x) }
|
||||||
|
else { Err($m) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($p:path >> $q:path; $i:ident | $m:tt) => {
|
||||||
|
{
|
||||||
|
use $p as srcpath;
|
||||||
|
use $q as tgtpath;
|
||||||
|
let base = enum_filter!(srcpath::$i | $m);
|
||||||
|
move |l| base(l).map(tgtpath::$i)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($p:path >> $q:path; $i:ident) => {
|
||||||
|
enum_filter!($p >> $q; $i | {concat!("Expected ", stringify!($i))})
|
||||||
|
};
|
||||||
|
($p:path >> $q:path; $($i:ident),+ | $m:tt) => {
|
||||||
|
{
|
||||||
|
use $p as srcpath;
|
||||||
|
use $q as tgtpath;
|
||||||
|
|l| match l {
|
||||||
|
$( srcpath::$i(x) => Ok(tgtpath::$i(x)), )+
|
||||||
|
_ => Err($m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($p:path >> $q:path; $($i:ident),+) => {
|
||||||
|
enum_filter!($p >> $q; $($i),+ | {
|
||||||
|
concat!("Expected one of ", $(stringify!($i), " "),+)
|
||||||
|
})
|
||||||
|
};
|
||||||
|
($p:path) => {
|
||||||
|
enum_filter!($p | {concat!("Expected ", stringify!($p))})
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
/// Produces parsers for tokenized sequences of enum types:
|
|
||||||
/// ```rs
|
|
||||||
/// enum_parser!(Foo::Bar | "Some error!") // Parses Foo::Bar(T) into T
|
|
||||||
/// enum_parser!(Foo::Bar) // same as above but with the default error "Expected Foo::Bar"
|
|
||||||
/// enum_parser!(Foo >> Quz; Bar, Baz) // Parses Foo::Bar(T) into Quz::Bar(T) and Foo::Baz(U) into Quz::Baz(U)
|
|
||||||
/// ```
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! enum_parser {
|
|
||||||
($p:path | $m:tt) => {
|
|
||||||
{
|
|
||||||
::chumsky::prelude::filter_map(|s, l| {
|
|
||||||
if let $p(x) = l { Ok(x) }
|
|
||||||
else { Err(::chumsky::prelude::Simple::custom(s, $m))}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($p:path >> $q:path; $i:ident) => {
|
|
||||||
{
|
|
||||||
use $p as srcpath;
|
|
||||||
use $q as tgtpath;
|
|
||||||
enum_parser!(srcpath::$i | (concat!("Expected ", stringify!($i)))).map(tgtpath::$i)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($p:path >> $q:path; $($i:ident),+) => {
|
|
||||||
{
|
|
||||||
::chumsky::prelude::choice((
|
|
||||||
$( enum_parser!($p >> $q; $i) ),+
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($p:path) => { enum_parser!($p | (concat!("Expected ", stringify!($p)))) };
|
|
||||||
}
|
|
||||||
@@ -1,155 +1,107 @@
|
|||||||
|
use std::ops::Range;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use chumsky::{self, prelude::*, Parser};
|
use chumsky::{self, prelude::*, Parser};
|
||||||
use lasso::Spur;
|
|
||||||
use crate::enum_parser;
|
|
||||||
use crate::representations::Primitive;
|
|
||||||
use crate::representations::{Literal, ast::{Clause, Expr}};
|
|
||||||
|
|
||||||
use super::lexer::Lexeme;
|
use crate::enum_filter;
|
||||||
|
use crate::representations::Primitive;
|
||||||
|
use crate::representations::ast::{Clause, Expr};
|
||||||
|
use crate::representations::location::Location;
|
||||||
|
use crate::interner::Token;
|
||||||
|
|
||||||
|
use super::context::Context;
|
||||||
|
use super::lexer::{Lexeme, Entry, filter_map_lex};
|
||||||
|
|
||||||
/// Parses any number of expr wrapped in (), [] or {}
|
/// Parses any number of expr wrapped in (), [] or {}
|
||||||
fn sexpr_parser<P>(
|
fn sexpr_parser(
|
||||||
expr: P
|
expr: impl Parser<Entry, Expr, Error = Simple<Entry>> + Clone
|
||||||
) -> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone
|
) -> impl Parser<Entry, (Clause, Range<usize>), Error = Simple<Entry>> + Clone {
|
||||||
where P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone {
|
let body = expr.repeated();
|
||||||
Lexeme::paren_parser(expr.repeated())
|
choice((
|
||||||
.map(|(del, b)| Clause::S(del, Rc::new(b)))
|
Lexeme::LP('(').parser().then(body.clone())
|
||||||
|
.then(Lexeme::RP('(').parser()),
|
||||||
|
Lexeme::LP('[').parser().then(body.clone())
|
||||||
|
.then(Lexeme::RP('[').parser()),
|
||||||
|
Lexeme::LP('{').parser().then(body.clone())
|
||||||
|
.then(Lexeme::RP('{').parser()),
|
||||||
|
)).map(|((lp, body), rp)| {
|
||||||
|
let Entry{lexeme, range: Range{start, ..}} = lp;
|
||||||
|
let end = rp.range.end;
|
||||||
|
let char = if let Lexeme::LP(c) = lexeme {c}
|
||||||
|
else {unreachable!("The parser only matches Lexeme::LP")};
|
||||||
|
(Clause::S(char, Rc::new(body)), start..end)
|
||||||
|
}).labelled("S-expression")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses `\name.body` or `\name:type.body` where name is any valid name
|
/// Parses `\name.body` or `\name:type.body` where name is any valid name
|
||||||
/// and type and body are both expressions. Comments are allowed
|
/// and type and body are both expressions. Comments are allowed
|
||||||
/// and ignored everywhere in between the tokens
|
/// and ignored everywhere in between the tokens
|
||||||
fn lambda_parser<'a, P, F>(
|
fn lambda_parser<'a>(
|
||||||
expr: P, intern: &'a F
|
expr: impl Parser<Entry, Expr, Error = Simple<Entry>> + Clone + 'a,
|
||||||
) -> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone + 'a
|
ctx: impl Context + 'a
|
||||||
where
|
) -> impl Parser<Entry, (Clause, Range<usize>), Error = Simple<Entry>> + Clone + 'a {
|
||||||
P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone + 'a,
|
Lexeme::BS.parser()
|
||||||
F: Fn(&str) -> Spur + 'a {
|
.ignore_then(expr.clone())
|
||||||
just(Lexeme::BS)
|
.then_ignore(Lexeme::Name(ctx.interner().i(".")).parser())
|
||||||
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
|
||||||
.ignore_then(namelike_parser(intern))
|
|
||||||
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
|
||||||
.then(
|
|
||||||
just(Lexeme::Type)
|
|
||||||
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
|
||||||
.ignore_then(expr.clone().repeated())
|
|
||||||
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
|
||||||
.or_not().map(Option::unwrap_or_default)
|
|
||||||
)
|
|
||||||
.then_ignore(just(Lexeme::name(".")))
|
|
||||||
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
|
||||||
.then(expr.repeated().at_least(1))
|
.then(expr.repeated().at_least(1))
|
||||||
.map(|((name, typ), body): ((Clause, Vec<Expr>), Vec<Expr>)| {
|
.map_with_span(move |(arg, body), span| {
|
||||||
Clause::Lambda(Rc::new(name), Rc::new(typ), Rc::new(body))
|
(Clause::Lambda(Rc::new(arg), Rc::new(body)), span)
|
||||||
})
|
}).labelled("Lambda")
|
||||||
}
|
|
||||||
|
|
||||||
/// see [lambda_parser] but `@` instead of `\` and the name is optional
|
|
||||||
fn auto_parser<'a, P, F>(
|
|
||||||
expr: P, intern: &'a F
|
|
||||||
) -> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone + 'a
|
|
||||||
where
|
|
||||||
P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone + 'a,
|
|
||||||
F: Fn(&str) -> Spur + 'a {
|
|
||||||
just(Lexeme::At)
|
|
||||||
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
|
||||||
.ignore_then(namelike_parser(intern).or_not())
|
|
||||||
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
|
||||||
.then(
|
|
||||||
just(Lexeme::Type)
|
|
||||||
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
|
||||||
.ignore_then(expr.clone().repeated())
|
|
||||||
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
|
||||||
.or_not().map(Option::unwrap_or_default)
|
|
||||||
)
|
|
||||||
.then_ignore(just(Lexeme::name(".")))
|
|
||||||
.then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
|
||||||
.then(expr.repeated().at_least(1))
|
|
||||||
.try_map(|((name, typ), body): ((Option<Clause>, Vec<Expr>), Vec<Expr>), s| {
|
|
||||||
if name.is_none() && typ.is_empty() {
|
|
||||||
Err(Simple::custom(s, "Auto without name or type has no effect"))
|
|
||||||
} else {
|
|
||||||
Ok(Clause::Auto(name.map(Rc::new), Rc::new(typ), Rc::new(body)))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses a sequence of names separated by :: <br/>
|
/// Parses a sequence of names separated by :: <br/>
|
||||||
/// Comments are allowed and ignored in between
|
/// Comments and line breaks are allowed and ignored in between
|
||||||
pub fn ns_name_parser<'a, F>(intern: &'a F)
|
pub fn ns_name_parser<'a>(ctx: impl Context + 'a)
|
||||||
-> impl Parser<Lexeme, Vec<Spur>, Error = Simple<Lexeme>> + Clone + 'a
|
-> impl Parser<Entry, (Token<Vec<Token<String>>>, Range<usize>), Error = Simple<Entry>> + Clone + 'a
|
||||||
where F: Fn(&str) -> Spur + 'a {
|
{
|
||||||
enum_parser!(Lexeme::Name)
|
filter_map_lex(enum_filter!(Lexeme::Name))
|
||||||
.map(|s| intern(&s))
|
.separated_by(Lexeme::NS.parser()).at_least(1)
|
||||||
.separated_by(
|
.map(move |elements| {
|
||||||
enum_parser!(Lexeme::Comment).repeated()
|
let start = elements.first().expect("can never be empty").1.start;
|
||||||
.then(just(Lexeme::NS))
|
let end = elements.last().expect("can never be empty").1.end;
|
||||||
.then(enum_parser!(Lexeme::Comment).repeated())
|
let tokens =
|
||||||
).at_least(1)
|
/*ctx.prefix().iter().copied().chain*/(
|
||||||
|
elements.iter().map(|(t, _)| *t)
|
||||||
|
).collect::<Vec<_>>();
|
||||||
|
(ctx.interner().i(&tokens), start..end)
|
||||||
|
}).labelled("Namespaced name")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse any legal argument name starting with a `$`
|
pub fn namelike_parser<'a>(ctx: impl Context + 'a)
|
||||||
fn placeholder_parser() -> impl Parser<Lexeme, String, Error = Simple<Lexeme>> + Clone {
|
-> impl Parser<Entry, (Clause, Range<usize>), Error = Simple<Entry>> + Clone + 'a
|
||||||
enum_parser!(Lexeme::Name).try_map(|name, span| {
|
{
|
||||||
name.strip_prefix('$').map(&str::to_string)
|
|
||||||
.ok_or_else(|| Simple::custom(span, "Not a placeholder"))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn namelike_parser<'a, F>(intern: &'a F)
|
|
||||||
-> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone + 'a
|
|
||||||
where F: Fn(&str) -> Spur + 'a {
|
|
||||||
choice((
|
choice((
|
||||||
just(Lexeme::name("...")).to(true)
|
filter_map_lex(enum_filter!(Lexeme::PH))
|
||||||
.or(just(Lexeme::name("..")).to(false))
|
.map(|(ph, range)| (Clause::Placeh(ph), range)),
|
||||||
.then(placeholder_parser())
|
ns_name_parser(ctx)
|
||||||
.then(
|
.map(|(token, range)| (Clause::Name(token), range)),
|
||||||
just(Lexeme::Type)
|
|
||||||
.ignore_then(enum_parser!(Lexeme::Uint))
|
|
||||||
.or_not().map(Option::unwrap_or_default)
|
|
||||||
)
|
|
||||||
.map(|((nonzero, key), prio)| Clause::Placeh{key, vec: Some((
|
|
||||||
prio.try_into().unwrap(),
|
|
||||||
nonzero
|
|
||||||
))}),
|
|
||||||
ns_name_parser(intern)
|
|
||||||
.map(|qualified| Clause::Name(Rc::new(qualified))),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clause_parser<'a, P, F>(
|
pub fn clause_parser<'a>(
|
||||||
expr: P, intern: &'a F
|
expr: impl Parser<Entry, Expr, Error = Simple<Entry>> + Clone + 'a,
|
||||||
) -> impl Parser<Lexeme, Clause, Error = Simple<Lexeme>> + Clone + 'a
|
ctx: impl Context + 'a
|
||||||
where
|
) -> impl Parser<Entry, (Clause, Range<usize>), Error = Simple<Entry>> + Clone + 'a {
|
||||||
P: Parser<Lexeme, Expr, Error = Simple<Lexeme>> + Clone + 'a,
|
choice((
|
||||||
F: Fn(&str) -> Spur + 'a {
|
filter_map_lex(enum_filter!(Lexeme >> Primitive; Literal))
|
||||||
enum_parser!(Lexeme::Comment).repeated()
|
.map(|(p, s)| (Clause::P(p), s)).labelled("Literal"),
|
||||||
.ignore_then(choice((
|
|
||||||
enum_parser!(Lexeme >> Literal; Uint, Num, Char, Str)
|
|
||||||
.map(Primitive::Literal).map(Clause::P),
|
|
||||||
placeholder_parser().map(|key| Clause::Placeh{key, vec: None}),
|
|
||||||
namelike_parser(intern),
|
|
||||||
sexpr_parser(expr.clone()),
|
sexpr_parser(expr.clone()),
|
||||||
lambda_parser(expr.clone(), intern),
|
lambda_parser(expr.clone(), ctx.clone()),
|
||||||
auto_parser(expr.clone(), intern),
|
namelike_parser(ctx),
|
||||||
just(Lexeme::At).ignore_then(expr.clone()).map(|arg| {
|
)).labelled("Clause")
|
||||||
Clause::Explicit(Rc::new(arg))
|
|
||||||
})
|
|
||||||
))).then_ignore(enum_parser!(Lexeme::Comment).repeated())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an expression
|
/// Parse an expression
|
||||||
pub fn xpr_parser<'a, F>(intern: &'a F)
|
pub fn xpr_parser<'a>(ctx: impl Context + 'a)
|
||||||
-> impl Parser<Lexeme, Expr, Error = Simple<Lexeme>> + 'a
|
-> impl Parser<Entry, Expr, Error = Simple<Entry>> + 'a
|
||||||
where F: Fn(&str) -> Spur + 'a {
|
{
|
||||||
recursive(|expr| {
|
recursive(move |expr| {
|
||||||
let clause = clause_parser(expr, intern);
|
clause_parser(expr, ctx.clone())
|
||||||
clause.clone().then(
|
.map(move |(value, range)| {
|
||||||
just(Lexeme::Type)
|
Expr{
|
||||||
.ignore_then(clause.clone())
|
value: value.clone(),
|
||||||
.repeated()
|
location: Location::Range { file: ctx.file(), range }
|
||||||
)
|
}
|
||||||
.map(|(val, typ)| Expr(val, Rc::new(typ)))
|
})
|
||||||
}).labelled("Expression")
|
}).labelled("Expression")
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use chumsky::{Parser, prelude::*};
|
use chumsky::{Parser, prelude::*};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use lasso::Spur;
|
|
||||||
use crate::representations::sourcefile::Import;
|
use crate::representations::sourcefile::Import;
|
||||||
use crate::utils::iter::{box_once, box_flatten, into_boxed_iter, BoxedIterIter};
|
use crate::utils::iter::{box_once, box_flatten, into_boxed_iter, BoxedIterIter};
|
||||||
use crate::{enum_parser, box_chain};
|
use crate::interner::Token;
|
||||||
|
use crate::{box_chain, enum_filter};
|
||||||
|
|
||||||
use super::lexer::Lexeme;
|
use super::Entry;
|
||||||
|
use super::context::Context;
|
||||||
|
use super::lexer::{Lexeme, filter_map_lex};
|
||||||
|
|
||||||
/// initialize a BoxedIter<BoxedIter<String>> with a single element.
|
/// initialize a BoxedIter<BoxedIter<String>> with a single element.
|
||||||
fn init_table(name: Spur) -> BoxedIterIter<'static, Spur> {
|
fn init_table(name: Token<String>) -> BoxedIterIter<'static, Token<String>> {
|
||||||
// I'm not at all confident that this is a good approach.
|
// I'm not at all confident that this is a good approach.
|
||||||
box_once(box_once(name))
|
box_once(box_once(name))
|
||||||
}
|
}
|
||||||
@@ -21,34 +21,36 @@ fn init_table(name: Spur) -> BoxedIterIter<'static, Spur> {
|
|||||||
/// preferably contain crossplatform filename-legal characters but the
|
/// preferably contain crossplatform filename-legal characters but the
|
||||||
/// symbols are explicitly allowed to go wild.
|
/// symbols are explicitly allowed to go wild.
|
||||||
/// There's a blacklist in [name]
|
/// There's a blacklist in [name]
|
||||||
pub fn import_parser<'a, F>(intern: &'a F)
|
pub fn import_parser<'a>(ctx: impl Context + 'a)
|
||||||
-> impl Parser<Lexeme, Vec<Import>, Error = Simple<Lexeme>> + 'a
|
-> impl Parser<Entry, Vec<Import>, Error = Simple<Entry>> + 'a
|
||||||
where F: Fn(&str) -> Spur + 'a {
|
{
|
||||||
let globstar = intern("*");
|
|
||||||
// TODO: this algorithm isn't cache friendly and copies a lot
|
// TODO: this algorithm isn't cache friendly and copies a lot
|
||||||
recursive(move |expr:Recursive<Lexeme, BoxedIterIter<Spur>, Simple<Lexeme>>| {
|
recursive({
|
||||||
enum_parser!(Lexeme::Name).map(|s| intern(s.as_str()))
|
let ctx = ctx.clone();
|
||||||
.separated_by(just(Lexeme::NS))
|
move |expr:Recursive<Entry, BoxedIterIter<Token<String>>, Simple<Entry>>| {
|
||||||
|
filter_map_lex(enum_filter!(Lexeme::Name)).map(|(t, _)| t)
|
||||||
|
.separated_by(Lexeme::NS.parser())
|
||||||
.then(
|
.then(
|
||||||
just(Lexeme::NS)
|
Lexeme::NS.parser()
|
||||||
.ignore_then(
|
.ignore_then(
|
||||||
choice((
|
choice((
|
||||||
expr.clone()
|
expr.clone()
|
||||||
.separated_by(just(Lexeme::name(",")))
|
.separated_by(Lexeme::Name(ctx.interner().i(",")).parser())
|
||||||
.delimited_by(just(Lexeme::LP('(')), just(Lexeme::RP('(')))
|
.delimited_by(Lexeme::LP('(').parser(), Lexeme::RP('(').parser())
|
||||||
.map(|v| box_flatten(v.into_iter()))
|
.map(|v| box_flatten(v.into_iter()))
|
||||||
.labelled("import group"),
|
.labelled("import group"),
|
||||||
// Each expr returns a list of imports, flatten into common list
|
// Each expr returns a list of imports, flatten into common list
|
||||||
just(Lexeme::name("*")).map(move |_| init_table(globstar))
|
Lexeme::Name(ctx.interner().i("*")).parser()
|
||||||
|
.map(move |_| init_table(ctx.interner().i("*")))
|
||||||
.labelled("wildcard import"), // Just a *, wrapped
|
.labelled("wildcard import"), // Just a *, wrapped
|
||||||
enum_parser!(Lexeme::Name)
|
filter_map_lex(enum_filter!(Lexeme::Name))
|
||||||
.map(|s| init_table(intern(s.as_str())))
|
.map(|(t, _)| init_table(t))
|
||||||
.labelled("import terminal") // Just a name, wrapped
|
.labelled("import terminal") // Just a name, wrapped
|
||||||
))
|
))
|
||||||
).or_not()
|
).or_not()
|
||||||
)
|
)
|
||||||
.map(|(name, opt_post): (Vec<Spur>, Option<BoxedIterIter<Spur>>)|
|
.map(|(name, opt_post): (Vec<Token<String>>, Option<BoxedIterIter<Token<String>>>)|
|
||||||
-> BoxedIterIter<Spur> {
|
-> BoxedIterIter<Token<String>> {
|
||||||
if let Some(post) = opt_post {
|
if let Some(post) = opt_post {
|
||||||
Box::new(post.map(move |el| {
|
Box::new(post.map(move |el| {
|
||||||
box_chain!(name.clone().into_iter(), el)
|
box_chain!(name.clone().into_iter(), el)
|
||||||
@@ -57,15 +59,16 @@ where F: Fn(&str) -> Spur + 'a {
|
|||||||
box_once(into_boxed_iter(name))
|
box_once(into_boxed_iter(name))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}).map(move |paths| {
|
}).map(move |paths| {
|
||||||
paths.filter_map(|namespaces| {
|
paths.filter_map(|namespaces| {
|
||||||
let mut path = namespaces.collect_vec();
|
let mut path = namespaces.collect_vec();
|
||||||
let name = path.pop()?;
|
let name = path.pop()?;
|
||||||
Some(Import {
|
Some(Import {
|
||||||
path: Rc::new(path),
|
path: ctx.interner().i(&path),
|
||||||
name: {
|
name: {
|
||||||
if name == globstar { None }
|
if name == ctx.interner().i("*") { None }
|
||||||
else { Some(name.to_owned()) }
|
else { Some(name) }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}).collect()
|
}).collect()
|
||||||
|
|||||||
@@ -1,53 +1,88 @@
|
|||||||
use std::{ops::Range, iter, fmt};
|
use std::fmt;
|
||||||
use ordered_float::NotNan;
|
use std::ops::Range;
|
||||||
use chumsky::{Parser, prelude::*};
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use crate::{utils::{BoxedIter, iter::{box_once, box_flatten}}, box_chain};
|
|
||||||
|
|
||||||
|
use ordered_float::NotNan;
|
||||||
|
use chumsky::{Parser, prelude::*, text::keyword, Span};
|
||||||
|
|
||||||
|
use crate::ast::{Placeholder, PHClass};
|
||||||
|
use crate::representations::Literal;
|
||||||
|
use crate::interner::{Token, InternedDisplay, Interner};
|
||||||
|
|
||||||
|
use super::context::Context;
|
||||||
|
use super::placeholder;
|
||||||
use super::{number, string, name, comment};
|
use super::{number, string, name, comment};
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Entry(pub Lexeme, pub Range<usize>);
|
pub struct Entry{
|
||||||
impl Debug for Entry {
|
pub lexeme: Lexeme,
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
pub range: Range<usize>
|
||||||
write!(f, "{:?}", self.0)
|
}
|
||||||
// f.debug_tuple("Entry").field(&self.0).field(&self.1).finish()
|
impl Entry {
|
||||||
|
pub fn is_filler(&self) -> bool {
|
||||||
|
matches!(self.lexeme, Lexeme::Comment(_))
|
||||||
|
|| matches!(self.lexeme, Lexeme::BR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InternedDisplay for Entry {
|
||||||
|
fn fmt_i(&self, f: &mut std::fmt::Formatter<'_>, i: &Interner) -> std::fmt::Result {
|
||||||
|
self.lexeme.fmt_i(f, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Entry> for (Lexeme, Range<usize>) {
|
impl From<Entry> for (Lexeme, Range<usize>) {
|
||||||
fn from(ent: Entry) -> Self {
|
fn from(ent: Entry) -> Self {
|
||||||
(ent.0, ent.1)
|
(ent.lexeme, ent.range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
impl Span for Entry {
|
||||||
|
type Context = Lexeme;
|
||||||
|
type Offset = usize;
|
||||||
|
|
||||||
|
fn context(&self) -> Self::Context {self.lexeme.clone()}
|
||||||
|
fn start(&self) -> Self::Offset {self.range.start()}
|
||||||
|
fn end(&self) -> Self::Offset {self.range.end()}
|
||||||
|
fn new(context: Self::Context, range: Range<Self::Offset>) -> Self {
|
||||||
|
Self{
|
||||||
|
lexeme: context,
|
||||||
|
range
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Lexeme {
|
pub enum Lexeme {
|
||||||
Num(NotNan<f64>),
|
Literal(Literal),
|
||||||
Uint(u64),
|
Name(Token<String>),
|
||||||
Char(char),
|
|
||||||
Str(String),
|
|
||||||
Name(String),
|
|
||||||
Rule(NotNan<f64>),
|
Rule(NotNan<f64>),
|
||||||
NS, // namespace separator
|
/// Walrus operator (formerly shorthand macro)
|
||||||
|
Const,
|
||||||
|
/// Line break
|
||||||
|
BR,
|
||||||
|
/// Namespace separator
|
||||||
|
NS,
|
||||||
|
/// Left paren
|
||||||
LP(char),
|
LP(char),
|
||||||
|
/// Right paren
|
||||||
RP(char),
|
RP(char),
|
||||||
BS, // Backslash
|
/// Backslash
|
||||||
|
BS,
|
||||||
At,
|
At,
|
||||||
Type, // type operator
|
Type, // type operator
|
||||||
Comment(String),
|
Comment(String),
|
||||||
Export,
|
Export,
|
||||||
Import,
|
Import,
|
||||||
|
Namespace,
|
||||||
|
PH(Placeholder)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Lexeme {
|
impl InternedDisplay for Lexeme {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt_i(&self, f: &mut std::fmt::Formatter<'_>, i: &Interner) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Num(n) => write!(f, "{}", n),
|
Self::Literal(l) => write!(f, "{:?}", l),
|
||||||
Self::Uint(i) => write!(f, "{}", i),
|
Self::Name(token) => write!(f, "{}", i.r(*token)),
|
||||||
Self::Char(c) => write!(f, "{:?}", c),
|
Self::Const => write!(f, ":="),
|
||||||
Self::Str(s) => write!(f, "{:?}", s),
|
|
||||||
Self::Name(name) => write!(f, "{}", name),
|
|
||||||
Self::Rule(prio) => write!(f, "={}=>", prio),
|
Self::Rule(prio) => write!(f, "={}=>", prio),
|
||||||
Self::NS => write!(f, "::"),
|
Self::NS => write!(f, "::"),
|
||||||
Self::LP(l) => write!(f, "{}", l),
|
Self::LP(l) => write!(f, "{}", l),
|
||||||
@@ -57,102 +92,114 @@ impl Debug for Lexeme {
|
|||||||
'{' => write!(f, "}}"),
|
'{' => write!(f, "}}"),
|
||||||
_ => f.debug_tuple("RP").field(l).finish()
|
_ => f.debug_tuple("RP").field(l).finish()
|
||||||
},
|
},
|
||||||
|
Self::BR => write!(f, "\n"),
|
||||||
Self::BS => write!(f, "\\"),
|
Self::BS => write!(f, "\\"),
|
||||||
Self::At => write!(f, "@"),
|
Self::At => write!(f, "@"),
|
||||||
Self::Type => write!(f, ":"),
|
Self::Type => write!(f, ":"),
|
||||||
Self::Comment(text) => write!(f, "--[{}]--", text),
|
Self::Comment(text) => write!(f, "--[{}]--", text),
|
||||||
Self::Export => write!(f, "export"),
|
Self::Export => write!(f, "export"),
|
||||||
Self::Import => write!(f, "import"),
|
Self::Import => write!(f, "import"),
|
||||||
|
Self::Namespace => write!(f, "namespace"),
|
||||||
|
Self::PH(Placeholder { name, class }) => match *class {
|
||||||
|
PHClass::Scalar => write!(f, "${}", i.r(*name)),
|
||||||
|
PHClass::Vec { nonzero, prio } => {
|
||||||
|
if nonzero {write!(f, "...")}
|
||||||
|
else {write!(f, "..")}?;
|
||||||
|
write!(f, "${}", i.r(*name))?;
|
||||||
|
if prio != 0 {write!(f, ":{}", prio)?;};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Lexeme {
|
impl Lexeme {
|
||||||
pub fn name<T: ToString>(n: T) -> Self {
|
pub fn rule(prio: impl Into<f64>) -> Self {
|
||||||
Lexeme::Name(n.to_string())
|
Lexeme::Rule(
|
||||||
|
NotNan::new(prio.into())
|
||||||
|
.expect("Rule priority cannot be NaN")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
pub fn rule<T>(prio: T) -> Self where T: Into<f64> {
|
|
||||||
Lexeme::Rule(NotNan::new(prio.into()).expect("Rule priority cannot be NaN"))
|
pub fn parser<E: chumsky::Error<Entry>>(self)
|
||||||
}
|
-> impl Parser<Entry, Entry, Error = E> + Clone {
|
||||||
pub fn paren_parser<T, P>(
|
filter(move |ent: &Entry| ent.lexeme == self)
|
||||||
expr: P
|
|
||||||
) -> impl Parser<Lexeme, (char, T), Error = Simple<Lexeme>> + Clone
|
|
||||||
where P: Parser<Lexeme, T, Error = Simple<Lexeme>> + Clone {
|
|
||||||
choice((
|
|
||||||
expr.clone().delimited_by(just(Lexeme::LP('(')), just(Lexeme::RP('(')))
|
|
||||||
.map(|t| ('(', t)),
|
|
||||||
expr.clone().delimited_by(just(Lexeme::LP('[')), just(Lexeme::RP('[')))
|
|
||||||
.map(|t| ('[', t)),
|
|
||||||
expr.delimited_by(just(Lexeme::LP('{')), just(Lexeme::RP('{')))
|
|
||||||
.map(|t| ('{', t)),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct LexedText(pub Vec<Vec<Entry>>);
|
pub struct LexedText(pub Vec<Entry>);
|
||||||
|
|
||||||
impl Debug for LexedText {
|
impl InternedDisplay for LexedText {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt_i(&self, f: &mut fmt::Formatter<'_>, i: &Interner) -> fmt::Result {
|
||||||
for row in &self.0 {
|
for tok in self.0.iter() {
|
||||||
for tok in row {
|
tok.fmt_i(f, i)?;
|
||||||
tok.fmt(f)?;
|
|
||||||
f.write_str(" ")?
|
f.write_str(" ")?
|
||||||
}
|
}
|
||||||
f.write_str("\n")?
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type LexSubres<'a> = BoxedIter<'a, Entry>;
|
fn paren_parser(lp: char, rp: char)
|
||||||
|
-> impl Parser<char, Lexeme, Error=Simple<char>>
|
||||||
fn paren_parser<'a>(
|
{
|
||||||
expr: Recursive<'a, char, LexSubres<'a>, Simple<char>>,
|
just(lp).to(Lexeme::LP(lp))
|
||||||
lp: char, rp: char
|
.or(just(rp).to(Lexeme::RP(lp)))
|
||||||
) -> impl Parser<char, LexSubres<'a>, Error=Simple<char>> + 'a {
|
|
||||||
expr.padded().repeated()
|
|
||||||
.map(|x| box_flatten(x.into_iter()))
|
|
||||||
.delimited_by(just(lp), just(rp)).map_with_span(move |b, s| {
|
|
||||||
box_chain!(
|
|
||||||
iter::once(Entry(Lexeme::LP(lp), s.start..s.start+1)),
|
|
||||||
b,
|
|
||||||
iter::once(Entry(Lexeme::RP(lp), s.end-1..s.end))
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lexer<'a, T: 'a>(ops: &[T]) -> impl Parser<char, Vec<Entry>, Error=Simple<char>> + 'a
|
pub fn literal_parser() -> impl Parser<char, Literal, Error = Simple<char>> {
|
||||||
where T: AsRef<str> + Clone {
|
choice((
|
||||||
let all_ops = ops.iter().map(|o| o.as_ref().to_string())
|
number::int_parser().map(Literal::Uint), // all ints are valid floats so it takes precedence
|
||||||
.chain([",", ".", "..", "..."].into_iter().map(str::to_string))
|
number::float_parser().map(Literal::Num),
|
||||||
|
string::char_parser().map(Literal::Char),
|
||||||
|
string::str_parser().map(Literal::Str),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static BASE_OPS: &[&str] = &[",", ".", "..", "..."];
|
||||||
|
|
||||||
|
pub fn lexer<'a>(ctx: impl Context + 'a)
|
||||||
|
-> impl Parser<char, Vec<Entry>, Error=Simple<char>> + 'a
|
||||||
|
{
|
||||||
|
let all_ops = ctx.ops().iter()
|
||||||
|
.map(|op| op.as_ref())
|
||||||
|
.chain(BASE_OPS.iter().cloned())
|
||||||
|
.map(str::to_string)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
just("export").padded().to(Lexeme::Export)
|
|
||||||
.or(just("import").padded().to(Lexeme::Import))
|
|
||||||
.or_not().then(recursive(move |recurse: Recursive<char, LexSubres, Simple<char>>| {
|
|
||||||
choice((
|
choice((
|
||||||
paren_parser(recurse.clone(), '(', ')'),
|
keyword("export").to(Lexeme::Export),
|
||||||
paren_parser(recurse.clone(), '[', ']'),
|
keyword("module").to(Lexeme::Namespace),
|
||||||
paren_parser(recurse.clone(), '{', '}'),
|
keyword("import").to(Lexeme::Import),
|
||||||
choice((
|
paren_parser('(', ')'),
|
||||||
just(":=").padded().to(Lexeme::rule(0f64)),
|
paren_parser('[', ']'),
|
||||||
|
paren_parser('{', '}'),
|
||||||
|
just(":=").to(Lexeme::Const),
|
||||||
just("=").ignore_then(number::float_parser()).then_ignore(just("=>")).map(Lexeme::rule),
|
just("=").ignore_then(number::float_parser()).then_ignore(just("=>")).map(Lexeme::rule),
|
||||||
comment::comment_parser().map(Lexeme::Comment),
|
comment::comment_parser().map(Lexeme::Comment),
|
||||||
just("::").padded().to(Lexeme::NS),
|
just("::").to(Lexeme::NS),
|
||||||
just('\\').padded().to(Lexeme::BS),
|
just('\\').to(Lexeme::BS),
|
||||||
just('@').padded().to(Lexeme::At),
|
just('@').to(Lexeme::At),
|
||||||
just(':').to(Lexeme::Type),
|
just(':').to(Lexeme::Type),
|
||||||
number::int_parser().map(Lexeme::Uint), // all ints are valid floats so it takes precedence
|
just('\n').to(Lexeme::BR),
|
||||||
number::float_parser().map(Lexeme::Num),
|
placeholder::placeholder_parser(ctx.clone()).map(Lexeme::PH),
|
||||||
string::char_parser().map(Lexeme::Char),
|
literal_parser().map(Lexeme::Literal),
|
||||||
string::str_parser().map(Lexeme::Str),
|
name::name_parser(&all_ops).map(move |n| {
|
||||||
name::name_parser(&all_ops).map(Lexeme::Name), // includes namespacing
|
Lexeme::Name(ctx.interner().i(&n))
|
||||||
)).map_with_span(|lx, span| box_once(Entry(lx, span)) as LexSubres)
|
})
|
||||||
))
|
))
|
||||||
}).separated_by(one_of("\t ").repeated())
|
.map_with_span(|lexeme, range| Entry{ lexeme, range })
|
||||||
.flatten().collect())
|
.padded_by(one_of(" \t").repeated())
|
||||||
.map(|(prefix, rest): (Option<Lexeme>, Vec<Entry>)| {
|
.repeated()
|
||||||
prefix.into_iter().map(|l| Entry(l, 0..6)).chain(rest.into_iter()).collect()
|
.then_ignore(end())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn filter_map_lex<'a, O, M: ToString>(
|
||||||
|
f: impl Fn(Lexeme) -> Result<O, M> + Clone + 'a
|
||||||
|
) -> impl Parser<Entry, (O, Range<usize>), Error = Simple<Entry>> + Clone + 'a {
|
||||||
|
filter_map(move |s: Range<usize>, e: Entry| {
|
||||||
|
let out = f(e.lexeme).map_err(|msg| Simple::custom(s.clone(), msg))?;
|
||||||
|
Ok((out, s))
|
||||||
})
|
})
|
||||||
.then_ignore(text::whitespace()).then_ignore(end())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,14 @@ mod comment;
|
|||||||
mod expression;
|
mod expression;
|
||||||
mod sourcefile;
|
mod sourcefile;
|
||||||
mod import;
|
mod import;
|
||||||
mod enum_parser;
|
|
||||||
mod parse;
|
mod parse;
|
||||||
|
mod enum_filter;
|
||||||
|
mod placeholder;
|
||||||
|
mod context;
|
||||||
|
|
||||||
pub use sourcefile::line_parser;
|
pub use sourcefile::line_parser;
|
||||||
pub use lexer::{lexer, Lexeme, Entry as LexerEntry};
|
pub use lexer::{lexer, Lexeme, Entry};
|
||||||
pub use name::is_op;
|
pub use name::is_op;
|
||||||
pub use parse::{parse, reparse, ParseError};
|
pub use parse::{parse, ParseError};
|
||||||
pub use number::{float_parser, int_parser};
|
pub use number::{float_parser, int_parser};
|
||||||
|
pub use context::ParsingContext;
|
||||||
@@ -1,58 +1,69 @@
|
|||||||
use chumsky::{self, prelude::*, Parser};
|
use chumsky::{self, prelude::*, Parser};
|
||||||
|
|
||||||
/// Matches any one of the passed operators, longest-first
|
/// Matches any one of the passed operators, preferring longer ones
|
||||||
fn op_parser<'a, T: AsRef<str> + Clone>(ops: &[T]) -> BoxedParser<'a, char, String, Simple<char>> {
|
fn op_parser<'a>(ops: &[impl AsRef<str> + Clone])
|
||||||
let mut sorted_ops: Vec<String> = ops.iter().map(|t| t.as_ref().to_string()).collect();
|
-> BoxedParser<'a, char, String, Simple<char>>
|
||||||
|
{
|
||||||
|
let mut sorted_ops: Vec<String> = ops.iter()
|
||||||
|
.map(|t| t.as_ref().to_string()).collect();
|
||||||
sorted_ops.sort_by_key(|op| -(op.len() as i64));
|
sorted_ops.sort_by_key(|op| -(op.len() as i64));
|
||||||
sorted_ops.into_iter()
|
sorted_ops.into_iter()
|
||||||
.map(|op| just(op).boxed())
|
.map(|op| just(op).boxed())
|
||||||
.reduce(|a, b| a.or(b).boxed())
|
.reduce(|a, b| a.or(b).boxed())
|
||||||
.unwrap_or_else(|| empty().map(|()| panic!("Empty isn't meant to match")).boxed())
|
.unwrap_or_else(|| {
|
||||||
.labelled("operator").boxed()
|
empty().map(|()| panic!("Empty isn't meant to match")).boxed()
|
||||||
|
}).labelled("operator").boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Characters that cannot be parsed as part of an operator
|
||||||
|
///
|
||||||
|
/// The initial operator list overrides this.
|
||||||
|
static NOT_NAME_CHAR: &[char] = &[
|
||||||
|
':', // used for namespacing and type annotations
|
||||||
|
'\\', '@', // parametric expression starters
|
||||||
|
'"', '\'', // parsed as primitives and therefore would never match
|
||||||
|
'(', ')', '[', ']', '{', '}', // must be strictly balanced
|
||||||
|
'.', // Argument-body separator in parametrics
|
||||||
|
',', // used in imports
|
||||||
|
];
|
||||||
|
|
||||||
/// Matches anything that's allowed as an operator
|
/// Matches anything that's allowed as an operator
|
||||||
///
|
///
|
||||||
/// Blacklist rationale:
|
/// FIXME: `@name` without a dot should be parsed correctly for overrides.
|
||||||
/// - `:` is used for namespacing and type annotations, both are distinguished from operators
|
/// Could be an operator but then parametrics should take precedence,
|
||||||
/// - `\` and `@` are parametric expression starters
|
/// which might break stuff. investigate.
|
||||||
/// - `"` and `'` are read as primitives and would never match.
|
|
||||||
/// - `(` and `)` are strictly balanced and this must remain the case for automation and streaming.
|
|
||||||
/// - `.` is the discriminator for parametrics.
|
|
||||||
/// - ',' is always a standalone single operator, so it can never be part of a name
|
|
||||||
///
|
///
|
||||||
/// FIXME: `@name` without a dot should be parsed correctly for overrides. Could be an operator but
|
/// TODO: `'` could work as an operator whenever it isn't closed.
|
||||||
/// then parametrics should take precedence, which might break stuff. investigate.
|
/// It's common im maths so it's worth a try
|
||||||
///
|
///
|
||||||
/// TODO: `'` could work as an operator whenever it isn't closed. It's common im maths so it's
|
/// TODO: `.` could possibly be parsed as an operator in some contexts.
|
||||||
/// worth a try
|
/// This operator is very common in maths so it's worth a try.
|
||||||
///
|
/// Investigate.
|
||||||
/// TODO: `.` could possibly be parsed as an operator depending on context. This operator is very
|
pub fn modname_parser<'a>()
|
||||||
/// common in maths so it's worth a try. Investigate.
|
-> impl Parser<char, String, Error = Simple<char>> + 'a
|
||||||
pub fn modname_parser<'a>() -> impl Parser<char, String, Error = Simple<char>> + 'a {
|
{
|
||||||
let not_name_char: Vec<char> = vec![':', '\\', '@', '"', '\'', '(', ')', '[', ']', '{', '}', ',', '.'];
|
filter(move |c| !NOT_NAME_CHAR.contains(c) && !c.is_whitespace())
|
||||||
filter(move |c| !not_name_char.contains(c) && !c.is_whitespace())
|
|
||||||
.repeated().at_least(1)
|
.repeated().at_least(1)
|
||||||
.collect()
|
.collect()
|
||||||
.labelled("modname")
|
.labelled("modname")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an operator or name. Failing both, parse everything up to the next whitespace or
|
/// Parse an operator or name. Failing both, parse everything up to
|
||||||
/// blacklisted character as a new operator.
|
/// the next whitespace or blacklisted character as a new operator.
|
||||||
pub fn name_parser<'a, T: AsRef<str> + Clone>(
|
pub fn name_parser<'a>(ops: &[impl AsRef<str> + Clone])
|
||||||
ops: &[T]
|
-> impl Parser<char, String, Error = Simple<char>> + 'a
|
||||||
) -> impl Parser<char, String, Error = Simple<char>> + 'a {
|
{
|
||||||
choice((
|
choice((
|
||||||
op_parser(ops), // First try to parse a known operator
|
op_parser(ops), // First try to parse a known operator
|
||||||
text::ident().labelled("plain text"), // Failing that, parse plain text
|
text::ident().labelled("plain text"), // Failing that, parse plain text
|
||||||
modname_parser() // Finally parse everything until tne next terminal as a new operator
|
modname_parser() // Finally parse everything until tne next forbidden char
|
||||||
))
|
))
|
||||||
.labelled("name")
|
.labelled("name")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decide if a string can be an operator. Operators can include digits and text, just not at the
|
/// Decide if a string can be an operator. Operators can include digits
|
||||||
/// start.
|
/// and text, just not at the start.
|
||||||
pub fn is_op<T: AsRef<str>>(s: T) -> bool {
|
pub fn is_op(s: impl AsRef<str>) -> bool {
|
||||||
return match s.as_ref().chars().next() {
|
return match s.as_ref().chars().next() {
|
||||||
Some(x) => !x.is_alphanumeric(),
|
Some(x) => !x.is_alphanumeric(),
|
||||||
None => false
|
None => false
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ fn pow_uint_parser(base: u32) -> impl Parser<char, u64, Error = Simple<char>> {
|
|||||||
|
|
||||||
/// parse an uint from a base determined by its prefix or lack thereof
|
/// parse an uint from a base determined by its prefix or lack thereof
|
||||||
///
|
///
|
||||||
/// Not to be convused with [uint_parser] which is a component of it.
|
/// Not to be confused with [uint_parser] which is a component of it.
|
||||||
pub fn int_parser() -> impl Parser<char, u64, Error = Simple<char>> {
|
pub fn int_parser() -> impl Parser<char, u64, Error = Simple<char>> {
|
||||||
choice((
|
choice((
|
||||||
just("0b").ignore_then(pow_uint_parser(2)),
|
just("0b").ignore_then(pow_uint_parser(2)),
|
||||||
|
|||||||
@@ -1,75 +1,58 @@
|
|||||||
use std::{ops::Range, fmt::Debug};
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use chumsky::{prelude::{Simple, end}, Stream, Parser};
|
use chumsky::{prelude::*, Parser};
|
||||||
use itertools::Itertools;
|
|
||||||
use lasso::Spur;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{ast::Rule, parse::{lexer::LexedText, sourcefile::split_lines}, representations::sourcefile::FileEntry};
|
use crate::representations::sourcefile::{FileEntry};
|
||||||
|
use crate::parse::sourcefile::split_lines;
|
||||||
|
|
||||||
use super::{Lexeme, lexer, line_parser, LexerEntry};
|
use super::context::Context;
|
||||||
|
use super::{lexer, line_parser, Entry};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Error, Debug, Clone)]
|
#[derive(Error, Debug, Clone)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
#[error("Could not tokenize {0:?}")]
|
#[error("Could not tokenize {0:?}")]
|
||||||
Lex(Vec<Simple<char>>),
|
Lex(Vec<Simple<char>>),
|
||||||
#[error("Could not parse {0:#?}")]
|
#[error("Could not parse {:?} on line {}", .0.first().unwrap().1.span(), .0.first().unwrap().0)]
|
||||||
Ast(Vec<Simple<Lexeme>>)
|
Ast(Vec<(usize, Simple<Entry>)>)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse<'a, Op, F>(
|
/// All the data required for parsing
|
||||||
ops: &[Op], data: &str, intern: &F
|
|
||||||
) -> Result<Vec<FileEntry>, ParseError>
|
|
||||||
where
|
/// Parse a string of code into a collection of module elements;
|
||||||
Op: 'a + AsRef<str> + Clone,
|
/// imports, exports, comments, declarations, etc.
|
||||||
F: Fn(&str) -> Spur
|
///
|
||||||
|
/// Notice that because the lexer splits operators based on the provided
|
||||||
|
/// list, the output will only be correct if operator list already
|
||||||
|
/// contains all operators defined or imported by this module.
|
||||||
|
pub fn parse<'a>(data: &str, ctx: impl Context)
|
||||||
|
-> Result<Vec<FileEntry>, ParseError>
|
||||||
{
|
{
|
||||||
let lexie = lexer(ops);
|
// TODO: wrap `i`, `ops` and `prefix` in a parsing context
|
||||||
let token_batchv = split_lines(data).map(|line| {
|
let lexie = lexer(ctx.clone());
|
||||||
lexie.parse(line).map_err(ParseError::Lex)
|
let token_batchv = lexie.parse(data).map_err(ParseError::Lex)?;
|
||||||
}).collect::<Result<Vec<_>, _>>()?;
|
// println!("Lexed:\n{}", LexedText(token_batchv.clone()).bundle(ctx.interner()));
|
||||||
println!("Lexed:\n{:?}", LexedText(token_batchv.clone()));
|
// println!("Lexed:\n{:?}", token_batchv.clone());
|
||||||
let parsr = line_parser(intern).then_ignore(end());
|
let parsr = line_parser(ctx).then_ignore(end());
|
||||||
let (parsed_lines, errors_per_line) = token_batchv.into_iter().filter(|v| {
|
let (parsed_lines, errors_per_line) = split_lines(&token_batchv)
|
||||||
!v.is_empty()
|
.enumerate()
|
||||||
}).map(|v| {
|
.map(|(i, entv)| (i,
|
||||||
// Find the first invalid position for Stream::for_iter
|
entv.iter()
|
||||||
let LexerEntry(_, Range{ end, .. }) = v.last().unwrap().clone();
|
.filter(|e| !e.is_filler())
|
||||||
// Stream expects tuples, lexer outputs structs
|
.cloned()
|
||||||
let tuples = v.into_iter().map_into::<(Lexeme, Range<usize>)>();
|
.collect::<Vec<_>>()
|
||||||
parsr.parse(Stream::from_iter(end..end+1, tuples))
|
))
|
||||||
// ^^^^^^^^^^
|
.filter(|(_, l)| l.len() > 0)
|
||||||
// I haven't the foggiest idea why this is needed, parsers are supposed to be lazy so the
|
.map(|(i, l)| (i, parsr.parse(l)))
|
||||||
// end of input should make little difference
|
.map(|(i, res)| match res {
|
||||||
}).map(|res| match res {
|
Ok(r) => (Some(r), (i, vec![])),
|
||||||
Ok(r) => (Some(r), vec![]),
|
Err(e) => (None, (i, e))
|
||||||
Err(e) => (None, e)
|
|
||||||
}).unzip::<_, _, Vec<_>, Vec<_>>();
|
}).unzip::<_, _, Vec<_>, Vec<_>>();
|
||||||
let total_err = errors_per_line.into_iter()
|
let total_err = errors_per_line.into_iter()
|
||||||
.flat_map(Vec::into_iter)
|
.flat_map(|(i, v)| v.into_iter().map(move |e| (i, e)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if !total_err.is_empty() { Err(ParseError::Ast(total_err)) }
|
if !total_err.is_empty() { Err(ParseError::Ast(total_err)) }
|
||||||
else { Ok(parsed_lines.into_iter().map(Option::unwrap).collect()) }
|
else { Ok(parsed_lines.into_iter().map(Option::unwrap).collect()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reparse<'a, Op, F>(
|
|
||||||
ops: &[Op], data: &str, pre: &[FileEntry], intern: &F
|
|
||||||
)
|
|
||||||
-> Result<Vec<FileEntry>, ParseError>
|
|
||||||
where
|
|
||||||
Op: 'a + AsRef<str> + Clone,
|
|
||||||
F: Fn(&str) -> Spur
|
|
||||||
{
|
|
||||||
let result = parse(ops, data, intern)?;
|
|
||||||
Ok(result.into_iter().zip(pre.iter()).map(|(mut output, donor)| {
|
|
||||||
if let FileEntry::Rule(Rule{source, ..}, _) = &mut output {
|
|
||||||
if let FileEntry::Rule(Rule{source: s2, ..}, _) = donor {
|
|
||||||
*source = s2.clone()
|
|
||||||
} else {
|
|
||||||
panic!("Preparse and reparse received different row types!")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output
|
|
||||||
}).collect())
|
|
||||||
}
|
|
||||||
|
|||||||
30
src/parse/placeholder.rs
Normal file
30
src/parse/placeholder.rs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
use chumsky::{Parser, prelude::*};
|
||||||
|
|
||||||
|
use crate::ast::{Placeholder, PHClass};
|
||||||
|
|
||||||
|
use super::{number::int_parser, context::Context};
|
||||||
|
|
||||||
|
pub fn placeholder_parser<'a>(ctx: impl Context + 'a)
|
||||||
|
-> impl Parser<char, Placeholder, Error = Simple<char>> + 'a
|
||||||
|
{
|
||||||
|
choice((
|
||||||
|
just("...").to(Some(true)),
|
||||||
|
just("..").to(Some(false)),
|
||||||
|
empty().to(None)
|
||||||
|
))
|
||||||
|
.then(just("$").ignore_then(text::ident()))
|
||||||
|
.then(just(":").ignore_then(int_parser()).or_not())
|
||||||
|
.try_map(move |((vec_nonzero, name), vec_prio), span| {
|
||||||
|
let name = ctx.interner().i(&name);
|
||||||
|
if let Some(nonzero) = vec_nonzero {
|
||||||
|
let prio = vec_prio.unwrap_or_default();
|
||||||
|
Ok(Placeholder { name, class: PHClass::Vec { nonzero, prio } })
|
||||||
|
} else {
|
||||||
|
if vec_prio.is_some() {
|
||||||
|
Err(Simple::custom(span, "Scalar placeholders have no priority"))
|
||||||
|
} else {
|
||||||
|
Ok(Placeholder { name, class: PHClass::Scalar })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,81 +1,139 @@
|
|||||||
use std::iter;
|
use std::iter;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::representations::sourcefile::FileEntry;
|
use crate::representations::location::Location;
|
||||||
use crate::enum_parser;
|
use crate::representations::sourcefile::{FileEntry, Member};
|
||||||
use crate::ast::{Expr, Rule};
|
use crate::enum_filter;
|
||||||
|
use crate::ast::{Rule, Constant, Expr, Clause};
|
||||||
|
use crate::interner::Token;
|
||||||
|
|
||||||
use super::expression::{xpr_parser, ns_name_parser};
|
use super::Entry;
|
||||||
|
use super::context::Context;
|
||||||
|
use super::expression::xpr_parser;
|
||||||
use super::import::import_parser;
|
use super::import::import_parser;
|
||||||
use super::lexer::Lexeme;
|
use super::lexer::{Lexeme, filter_map_lex};
|
||||||
use chumsky::{Parser, prelude::*};
|
|
||||||
use lasso::Spur;
|
|
||||||
use ordered_float::NotNan;
|
|
||||||
|
|
||||||
fn rule_parser<'a, F>(intern: &'a F) -> impl Parser<Lexeme, (
|
use chumsky::{Parser, prelude::*};
|
||||||
Vec<Expr>, NotNan<f64>, Vec<Expr>
|
use itertools::Itertools;
|
||||||
), Error = Simple<Lexeme>> + 'a
|
|
||||||
where F: Fn(&str) -> Spur + 'a {
|
fn rule_parser<'a>(ctx: impl Context + 'a)
|
||||||
xpr_parser(intern).repeated()
|
-> impl Parser<Entry, Rule, Error = Simple<Entry>> + 'a
|
||||||
.then(enum_parser!(Lexeme::Rule))
|
{
|
||||||
.then(xpr_parser(intern).repeated())
|
xpr_parser(ctx.clone()).repeated().at_least(1)
|
||||||
.map(|((a, b), c)| (a, b, c))
|
.then(filter_map_lex(enum_filter!(Lexeme::Rule)))
|
||||||
.labelled("Rule")
|
.then(xpr_parser(ctx).repeated().at_least(1))
|
||||||
|
.map(|((s, (prio, _)), t)| Rule{
|
||||||
|
source: Rc::new(s),
|
||||||
|
prio,
|
||||||
|
target: Rc::new(t)
|
||||||
|
}).labelled("Rule")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_parser<'a, F>(intern: &'a F)
|
fn const_parser<'a>(ctx: impl Context + 'a)
|
||||||
-> impl Parser<Lexeme, FileEntry, Error = Simple<Lexeme>> + 'a
|
-> impl Parser<Entry, Constant, Error = Simple<Entry>> + 'a
|
||||||
where F: Fn(&str) -> Spur + 'a {
|
{
|
||||||
|
filter_map_lex(enum_filter!(Lexeme::Name))
|
||||||
|
.then_ignore(Lexeme::Const.parser())
|
||||||
|
.then(xpr_parser(ctx.clone()).repeated().at_least(1))
|
||||||
|
.map(move |((name, _), value)| Constant{
|
||||||
|
name,
|
||||||
|
value: if let Ok(ex) = value.iter().exactly_one() { ex.clone() }
|
||||||
|
else {
|
||||||
|
let start = value.first().expect("value cannot be empty")
|
||||||
|
.location.range().expect("all locations in parsed source are known")
|
||||||
|
.start;
|
||||||
|
let end = value.last().expect("asserted right above")
|
||||||
|
.location.range().expect("all locations in parsed source are known")
|
||||||
|
.end;
|
||||||
|
Expr{
|
||||||
|
location: Location::Range { file: ctx.file(), range: start..end },
|
||||||
|
value: Clause::S('(', Rc::new(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_errors<T, E: chumsky::Error<T>>(e: Vec<E>) -> E {
|
||||||
|
e.into_iter()
|
||||||
|
.reduce(chumsky::Error::merge)
|
||||||
|
.expect("Error list must be non_enmpty")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn namespace_parser<'a>(
|
||||||
|
line: impl Parser<Entry, FileEntry, Error = Simple<Entry>> + 'a,
|
||||||
|
) -> impl Parser<Entry, (Token<String>, Vec<FileEntry>), Error = Simple<Entry>> + 'a {
|
||||||
|
Lexeme::Namespace.parser()
|
||||||
|
.ignore_then(filter_map_lex(enum_filter!(Lexeme::Name)))
|
||||||
|
.then(
|
||||||
|
any().repeated().delimited_by(
|
||||||
|
Lexeme::LP('{').parser(),
|
||||||
|
Lexeme::RP('{').parser()
|
||||||
|
).try_map(move |body, _| {
|
||||||
|
split_lines(&body)
|
||||||
|
.map(|l| line.parse(l))
|
||||||
|
.collect::<Result<Vec<_>,_>>()
|
||||||
|
.map_err(collect_errors)
|
||||||
|
})
|
||||||
|
).map(move |((name, _), body)| {
|
||||||
|
(name, body)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn member_parser<'a>(
|
||||||
|
line: impl Parser<Entry, FileEntry, Error = Simple<Entry>> + 'a,
|
||||||
|
ctx: impl Context + 'a
|
||||||
|
) -> impl Parser<Entry, Member, Error = Simple<Entry>> + 'a {
|
||||||
choice((
|
choice((
|
||||||
// In case the usercode wants to parse doc
|
namespace_parser(line)
|
||||||
enum_parser!(Lexeme >> FileEntry; Comment),
|
.map(|(name, body)| Member::Namespace(name, body)),
|
||||||
just(Lexeme::Import)
|
rule_parser(ctx.clone()).map(Member::Rule),
|
||||||
.ignore_then(import_parser(intern).map(FileEntry::Import))
|
const_parser(ctx).map(Member::Constant),
|
||||||
.then_ignore(enum_parser!(Lexeme::Comment).or_not()),
|
|
||||||
just(Lexeme::Export).map_err_with_span(|e, s| {
|
|
||||||
println!("{:?} could not yield an export", s); e
|
|
||||||
}).ignore_then(
|
|
||||||
just(Lexeme::NS).ignore_then(
|
|
||||||
ns_name_parser(intern).map(Rc::new)
|
|
||||||
.separated_by(just(Lexeme::name(",")))
|
|
||||||
.delimited_by(just(Lexeme::LP('(')), just(Lexeme::RP('(')))
|
|
||||||
).map(FileEntry::Export)
|
|
||||||
.or(rule_parser(intern).map(|(source, prio, target)| {
|
|
||||||
FileEntry::Rule(Rule {
|
|
||||||
source: Rc::new(source),
|
|
||||||
prio,
|
|
||||||
target: Rc::new(target)
|
|
||||||
}, true)
|
|
||||||
}))
|
|
||||||
),
|
|
||||||
// This could match almost anything so it has to go last
|
|
||||||
rule_parser(intern).map(|(source, prio, target)| {
|
|
||||||
FileEntry::Rule(Rule{
|
|
||||||
source: Rc::new(source),
|
|
||||||
prio,
|
|
||||||
target: Rc::new(target)
|
|
||||||
}, false)
|
|
||||||
}),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn split_lines(data: &str) -> impl Iterator<Item = &str> {
|
pub fn line_parser<'a>(ctx: impl Context + 'a)
|
||||||
let mut source = data.char_indices();
|
-> impl Parser<Entry, FileEntry, Error = Simple<Entry>> + 'a
|
||||||
|
{
|
||||||
|
recursive(|line: Recursive<Entry, FileEntry, Simple<Entry>>| {
|
||||||
|
choice((
|
||||||
|
// In case the usercode wants to parse doc
|
||||||
|
filter_map_lex(enum_filter!(Lexeme >> FileEntry; Comment)).map(|(ent, _)| ent),
|
||||||
|
// plain old imports
|
||||||
|
Lexeme::Import.parser()
|
||||||
|
.ignore_then(import_parser(ctx.clone()).map(FileEntry::Import)),
|
||||||
|
Lexeme::Export.parser().ignore_then(choice((
|
||||||
|
// token collection
|
||||||
|
Lexeme::NS.parser().ignore_then(
|
||||||
|
filter_map_lex(enum_filter!(Lexeme::Name)).map(|(e, _)| e)
|
||||||
|
.separated_by(Lexeme::Name(ctx.interner().i(",")).parser())
|
||||||
|
.delimited_by(Lexeme::LP('(').parser(), Lexeme::RP('(').parser())
|
||||||
|
).map(FileEntry::Export),
|
||||||
|
// public declaration
|
||||||
|
member_parser(line.clone(), ctx.clone()).map(FileEntry::Exported)
|
||||||
|
))),
|
||||||
|
// This could match almost anything so it has to go last
|
||||||
|
member_parser(line, ctx).map(FileEntry::Internal),
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn split_lines(data: &[Entry]) -> impl Iterator<Item = &[Entry]> {
|
||||||
|
let mut source = data.iter().enumerate();
|
||||||
let mut last_slice = 0;
|
let mut last_slice = 0;
|
||||||
iter::from_fn(move || {
|
iter::from_fn(move || {
|
||||||
let mut paren_count = 0;
|
let mut paren_count = 0;
|
||||||
while let Some((i, c)) = source.next() {
|
while let Some((i, Entry{ lexeme, .. })) = source.next() {
|
||||||
match c {
|
match lexeme {
|
||||||
'(' | '{' | '[' => paren_count += 1,
|
Lexeme::LP(_) => paren_count += 1,
|
||||||
')' | '}' | ']' => paren_count -= 1,
|
Lexeme::RP(_) => paren_count -= 1,
|
||||||
'\n' if paren_count == 0 => {
|
Lexeme::BR if paren_count == 0 => {
|
||||||
let begin = last_slice;
|
let begin = last_slice;
|
||||||
last_slice = i;
|
last_slice = i+1;
|
||||||
return Some(&data[begin..i]);
|
return Some(&data[begin..i]);
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
})
|
}).filter(|s| s.len() > 0)
|
||||||
}
|
}
|
||||||
15
src/pipeline/error/mod.rs
Normal file
15
src/pipeline/error/mod.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
mod project_error;
|
||||||
|
mod parse_error_with_path;
|
||||||
|
mod unexpected_directory;
|
||||||
|
mod module_not_found;
|
||||||
|
mod not_exported;
|
||||||
|
mod too_many_supers;
|
||||||
|
mod visibility_mismatch;
|
||||||
|
|
||||||
|
pub use project_error::{ErrorPosition, ProjectError};
|
||||||
|
pub use parse_error_with_path::ParseErrorWithPath;
|
||||||
|
pub use unexpected_directory::UnexpectedDirectory;
|
||||||
|
pub use module_not_found::ModuleNotFound;
|
||||||
|
pub use not_exported::NotExported;
|
||||||
|
pub use too_many_supers::TooManySupers;
|
||||||
|
pub use visibility_mismatch::VisibilityMismatch;
|
||||||
25
src/pipeline/error/module_not_found.rs
Normal file
25
src/pipeline/error/module_not_found.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use crate::utils::{BoxedIter, iter::box_once};
|
||||||
|
|
||||||
|
use super::{ProjectError, ErrorPosition};
|
||||||
|
|
||||||
|
/// Error produced when an import refers to a nonexistent module
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct ModuleNotFound {
|
||||||
|
pub file: Vec<String>,
|
||||||
|
pub subpath: Vec<String>
|
||||||
|
}
|
||||||
|
impl ProjectError for ModuleNotFound {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"an import refers to a nonexistent module"
|
||||||
|
}
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"module {} in {} was not found",
|
||||||
|
self.subpath.join("::"),
|
||||||
|
self.file.join("/"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||||
|
box_once(ErrorPosition::just_file(self.file.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/pipeline/error/not_exported.rs
Normal file
36
src/pipeline/error/not_exported.rs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::{utils::BoxedIter, representations::location::Location};
|
||||||
|
|
||||||
|
use super::{ProjectError, ErrorPosition};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NotExported {
|
||||||
|
pub file: Vec<String>,
|
||||||
|
pub subpath: Vec<String>,
|
||||||
|
pub referrer_file: Vec<String>,
|
||||||
|
pub referrer_subpath: Vec<String>,
|
||||||
|
}
|
||||||
|
impl ProjectError for NotExported {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"An import refers to a symbol that exists but isn't exported"
|
||||||
|
}
|
||||||
|
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||||
|
Box::new([
|
||||||
|
ErrorPosition{
|
||||||
|
location: Location::File(Rc::new(self.file.clone())),
|
||||||
|
message: Some(format!(
|
||||||
|
"{} isn't exported",
|
||||||
|
self.subpath.join("::")
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
ErrorPosition{
|
||||||
|
location: Location::File(Rc::new(self.referrer_file.clone())),
|
||||||
|
message: Some(format!(
|
||||||
|
"{} cannot see this symbol",
|
||||||
|
self.referrer_subpath.join("::")
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
].into_iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/pipeline/error/parse_error_with_path.rs
Normal file
37
src/pipeline/error/parse_error_with_path.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::representations::location::Location;
|
||||||
|
use crate::utils::BoxedIter;
|
||||||
|
use crate::parse::ParseError;
|
||||||
|
|
||||||
|
use super::ErrorPosition;
|
||||||
|
use super::ProjectError;
|
||||||
|
|
||||||
|
/// Produced by stages that parse text when it fails.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ParseErrorWithPath {
|
||||||
|
pub full_source: String,
|
||||||
|
pub path: Vec<String>,
|
||||||
|
pub error: ParseError
|
||||||
|
}
|
||||||
|
impl ProjectError for ParseErrorWithPath {
|
||||||
|
fn description(&self) -> &str {"Failed to parse code"}
|
||||||
|
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||||
|
match &self.error {
|
||||||
|
ParseError::Lex(lex) => Box::new(lex.iter().map(|s| ErrorPosition {
|
||||||
|
location: Location::Range {
|
||||||
|
file: Rc::new(self.path.clone()),
|
||||||
|
range: s.span(),
|
||||||
|
},
|
||||||
|
message: Some(s.to_string())
|
||||||
|
})),
|
||||||
|
ParseError::Ast(ast) => Box::new(ast.iter().map(|(_i, s)| ErrorPosition {
|
||||||
|
location: s.found().map(|e| Location::Range {
|
||||||
|
file: Rc::new(self.path.clone()),
|
||||||
|
range: e.range.clone()
|
||||||
|
}).unwrap_or_else(|| Location::File(Rc::new(self.path.clone()))),
|
||||||
|
message: Some(s.label().unwrap_or("Parse error").to_string())
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/pipeline/error/project_error.rs
Normal file
50
src/pipeline/error/project_error.rs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
use std::fmt::{Debug, Display};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::representations::location::Location;
|
||||||
|
use crate::utils::BoxedIter;
|
||||||
|
|
||||||
|
/// A point of interest in resolving the error, such as the point where
|
||||||
|
/// processing got stuck, a command that is likely to be incorrect
|
||||||
|
pub struct ErrorPosition {
|
||||||
|
pub location: Location,
|
||||||
|
pub message: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ErrorPosition {
|
||||||
|
/// An error position referring to an entire file with no comment
|
||||||
|
pub fn just_file(file: Vec<String>) -> Self {
|
||||||
|
Self { message: None, location: Location::File(Rc::new(file)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Errors addressed to the developer which are to be resolved with
|
||||||
|
/// code changes
|
||||||
|
pub trait ProjectError: Debug {
|
||||||
|
/// A general description of this type of error
|
||||||
|
fn description(&self) -> &str;
|
||||||
|
/// A formatted message that includes specific parameters
|
||||||
|
fn message(&self) -> String {String::new()}
|
||||||
|
/// Code positions relevant to this error
|
||||||
|
fn positions(&self) -> BoxedIter<ErrorPosition>;
|
||||||
|
/// Convert the error into an [Rc<dyn ProjectError>] to be able to
|
||||||
|
/// handle various errors together
|
||||||
|
fn rc(self) -> Rc<dyn ProjectError> where Self: Sized + 'static {
|
||||||
|
Rc::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for dyn ProjectError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let description = self.description();
|
||||||
|
let message = self.message();
|
||||||
|
let positions = self.positions();
|
||||||
|
write!(f, "Problem with the project: {description}; {message}")?;
|
||||||
|
for ErrorPosition { location, message } in positions {
|
||||||
|
write!(f, "@{location}: {}",
|
||||||
|
message.unwrap_or("location of interest".to_string())
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/pipeline/error/too_many_supers.rs
Normal file
38
src/pipeline/error/too_many_supers.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::{utils::{BoxedIter, iter::box_once}, representations::location::Location};
|
||||||
|
|
||||||
|
use super::{ProjectError, ErrorPosition};
|
||||||
|
|
||||||
|
/// Error produced when an import path starts with more `super` segments
|
||||||
|
/// than the current module's absolute path
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct TooManySupers {
|
||||||
|
pub path: Vec<String>,
|
||||||
|
pub offender_file: Vec<String>,
|
||||||
|
pub offender_mod: Vec<String>
|
||||||
|
}
|
||||||
|
impl ProjectError for TooManySupers {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"an import path starts with more `super` segments than \
|
||||||
|
the current module's absolute path"
|
||||||
|
}
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"path {} in {} contains too many `super` steps.",
|
||||||
|
self.path.join("::"),
|
||||||
|
self.offender_mod.join("::")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||||
|
box_once(ErrorPosition {
|
||||||
|
location: Location::File(Rc::new(self.offender_file.clone())),
|
||||||
|
message: Some(format!(
|
||||||
|
"path {} in {} contains too many `super` steps.",
|
||||||
|
self.path.join("::"),
|
||||||
|
self.offender_mod.join("::")
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/pipeline/error/unexpected_directory.rs
Normal file
26
src/pipeline/error/unexpected_directory.rs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
use crate::utils::{BoxedIter, iter::box_once};
|
||||||
|
|
||||||
|
use super::ErrorPosition;
|
||||||
|
use super::ProjectError;
|
||||||
|
|
||||||
|
/// Produced when a stage that deals specifically with code encounters
|
||||||
|
/// a path that refers to a directory
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UnexpectedDirectory {
|
||||||
|
pub path: Vec<String>
|
||||||
|
}
|
||||||
|
impl ProjectError for UnexpectedDirectory {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"A stage that deals specifically with code encountered a path \
|
||||||
|
that refers to a directory"
|
||||||
|
}
|
||||||
|
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||||
|
box_once(ErrorPosition::just_file(self.path.clone()))
|
||||||
|
}
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"{} was expected to be a file but a directory was found",
|
||||||
|
self.path.join("/")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/pipeline/error/visibility_mismatch.rs
Normal file
25
src/pipeline/error/visibility_mismatch.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
use crate::representations::location::Location;
|
||||||
|
use crate::utils::{BoxedIter, iter::box_once};
|
||||||
|
|
||||||
|
use super::project_error::{ProjectError, ErrorPosition};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VisibilityMismatch{
|
||||||
|
pub namespace: Vec<String>,
|
||||||
|
pub file: Rc<Vec<String>>
|
||||||
|
}
|
||||||
|
impl ProjectError for VisibilityMismatch {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"Some occurences of a namespace are exported but others are not"
|
||||||
|
}
|
||||||
|
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||||
|
box_once(ErrorPosition {
|
||||||
|
location: Location::File(self.file.clone()),
|
||||||
|
message: Some(format!(
|
||||||
|
"{} is opened multiple times with different visibilities",
|
||||||
|
self.namespace.join("::")
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
106
src/pipeline/file_loader.rs
Normal file
106
src/pipeline/file_loader.rs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::io;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
use crate::utils::iter::box_once;
|
||||||
|
use crate::utils::{Cache, BoxedIter};
|
||||||
|
use crate::interner::{Interner, Token};
|
||||||
|
use crate::pipeline::error::UnexpectedDirectory;
|
||||||
|
use crate::pipeline::error::{ProjectError, ErrorPosition};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FileLoadingError{
|
||||||
|
file: io::Error,
|
||||||
|
dir: io::Error,
|
||||||
|
path: Vec<String>
|
||||||
|
}
|
||||||
|
impl ProjectError for FileLoadingError {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"Neither a file nor a directory could be read from \
|
||||||
|
the requested path"
|
||||||
|
}
|
||||||
|
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||||
|
box_once(ErrorPosition::just_file(self.path.clone()))
|
||||||
|
}
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("File: {}\nDirectory: {}", self.file, self.dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the result of loading code from a string-tree form such
|
||||||
|
/// as the file system.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Loaded {
|
||||||
|
Code(Rc<String>),
|
||||||
|
Collection(Rc<Vec<String>>),
|
||||||
|
}
|
||||||
|
impl Loaded {
|
||||||
|
pub fn is_code(&self) -> bool {matches!(self, Loaded::Code(_))}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type IOResult = Result<Loaded, Rc<dyn ProjectError>>;
|
||||||
|
|
||||||
|
pub type FileCache<'a> = Cache<'a, Token<Vec<Token<String>>>, IOResult>;
|
||||||
|
|
||||||
|
/// Load a file from a path expressed in Rust strings, but relative to
|
||||||
|
/// a root expressed as an OS Path.
|
||||||
|
pub fn load_file(root: &Path, path: &[impl AsRef<str>]) -> IOResult {
|
||||||
|
// let os_path = path.into_iter()
|
||||||
|
// .map_into::<OsString>()
|
||||||
|
// .collect::<Vec<_>>();
|
||||||
|
let full_path = path.iter().fold(
|
||||||
|
root.to_owned(),
|
||||||
|
|p, s| p.join(s.as_ref())
|
||||||
|
);
|
||||||
|
let file_path = full_path.with_extension("orc");
|
||||||
|
let file_error = match fs::read_to_string(&file_path) {
|
||||||
|
Ok(string) => return Ok(Loaded::Code(Rc::new(string))),
|
||||||
|
Err(err) => err
|
||||||
|
};
|
||||||
|
let dir = match fs::read_dir(&full_path) {
|
||||||
|
Ok(dir) => dir,
|
||||||
|
Err(dir_error) => {
|
||||||
|
return Err(FileLoadingError {
|
||||||
|
file: file_error,
|
||||||
|
dir: dir_error,
|
||||||
|
path: path.iter()
|
||||||
|
.map(|s| s.as_ref().to_string())
|
||||||
|
.collect(),
|
||||||
|
}.rc())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let names = dir.filter_map(Result::ok)
|
||||||
|
.filter_map(|ent| {
|
||||||
|
let fname = ent.file_name().into_string().ok()?;
|
||||||
|
let ftyp = ent.metadata().ok()?.file_type();
|
||||||
|
Some(if ftyp.is_dir() {fname} else {
|
||||||
|
fname.strip_suffix(".or")?.to_string()
|
||||||
|
})
|
||||||
|
}).collect();
|
||||||
|
Ok(Loaded::Collection(Rc::new(names)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates a cached file loader for a directory
|
||||||
|
pub fn mk_cache(root: PathBuf, i: &Interner) -> FileCache {
|
||||||
|
Cache::new(move |token: Token<Vec<Token<String>>>, _this| -> IOResult {
|
||||||
|
let path = i.r(token).iter()
|
||||||
|
.map(|t| i.r(*t).as_str())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
load_file(&root, &path)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads the string contents of a file at the given location.
|
||||||
|
/// If the path points to a directory, raises an error.
|
||||||
|
pub fn load_text(
|
||||||
|
path: Token<Vec<Token<String>>>,
|
||||||
|
load_file: &impl Fn(Token<Vec<Token<String>>>) -> IOResult,
|
||||||
|
i: &Interner
|
||||||
|
) -> Result<Rc<String>, Rc<dyn ProjectError>> {
|
||||||
|
if let Loaded::Code(s) = load_file(path)? {Ok(s)}
|
||||||
|
else {Err(UnexpectedDirectory{
|
||||||
|
path: i.r(path).iter().map(|t| i.r(*t)).cloned().collect()
|
||||||
|
}.rc())}
|
||||||
|
}
|
||||||
32
src/pipeline/import_abs_path.rs
Normal file
32
src/pipeline/import_abs_path.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::representations::tree::Module;
|
||||||
|
use crate::representations::sourcefile::absolute_path;
|
||||||
|
use crate::utils::{Substack};
|
||||||
|
use crate::interner::{Token, Interner};
|
||||||
|
|
||||||
|
use super::error::{ProjectError, TooManySupers};
|
||||||
|
|
||||||
|
pub fn import_abs_path(
|
||||||
|
src_path: &[Token<String>],
|
||||||
|
mod_stack: Substack<Token<String>>,
|
||||||
|
module: &Module<impl Clone, impl Clone>,
|
||||||
|
import_path: &[Token<String>],
|
||||||
|
i: &Interner,
|
||||||
|
) -> Result<Vec<Token<String>>, Rc<dyn ProjectError>> {
|
||||||
|
// path of module within file
|
||||||
|
let mod_pathv = mod_stack.iter().rev_vec_clone();
|
||||||
|
// path of module within compilation
|
||||||
|
let abs_pathv = src_path.iter().copied()
|
||||||
|
.chain(mod_pathv.iter().copied())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
// preload-target path relative to module
|
||||||
|
// preload-target path within compilation
|
||||||
|
absolute_path(&abs_pathv, import_path, i, &|n| {
|
||||||
|
module.items.contains_key(&n)
|
||||||
|
}).map_err(|_| TooManySupers{
|
||||||
|
path: import_path.iter().map(|t| i.r(*t)).cloned().collect(),
|
||||||
|
offender_file: src_path.iter().map(|t| i.r(*t)).cloned().collect(),
|
||||||
|
offender_mod: mod_pathv.iter().map(|t| i.r(*t)).cloned().collect(),
|
||||||
|
}.rc())
|
||||||
|
}
|
||||||
53
src/pipeline/import_resolution/alias_map.rs
Normal file
53
src/pipeline/import_resolution/alias_map.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
use hashbrown::{HashMap, HashSet};
|
||||||
|
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
use crate::interner::Token;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct AliasMap{
|
||||||
|
pub targets: HashMap<Token<Vec<Token<String>>>, Token<Vec<Token<String>>>>,
|
||||||
|
pub aliases: HashMap<Token<Vec<Token<String>>>, HashSet<Token<Vec<Token<String>>>>>,
|
||||||
|
}
|
||||||
|
impl AliasMap {
|
||||||
|
pub fn new() -> Self {Self::default()}
|
||||||
|
|
||||||
|
pub fn link(&mut self, alias: Token<Vec<Token<String>>>, target: Token<Vec<Token<String>>>) {
|
||||||
|
let prev = self.targets.insert(alias, target);
|
||||||
|
debug_assert!(prev.is_none(), "Alias already has a target");
|
||||||
|
multimap_entry(&mut self.aliases, &target).insert(alias);
|
||||||
|
// Remove aliases of the alias
|
||||||
|
if let Some(alts) = self.aliases.remove(&alias) {
|
||||||
|
for alt in alts {
|
||||||
|
// Assert that this step has always been done in the past
|
||||||
|
debug_assert!(
|
||||||
|
self.aliases.get(&alt)
|
||||||
|
.map(HashSet::is_empty)
|
||||||
|
.unwrap_or(true),
|
||||||
|
"Alias set of alias not empty"
|
||||||
|
);
|
||||||
|
debug_assert!(
|
||||||
|
self.targets.insert(alt, target) == Some(alias),
|
||||||
|
"Name not target of its own alias"
|
||||||
|
);
|
||||||
|
multimap_entry(&mut self.aliases, &target).insert(alt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve(&self, alias: Token<Vec<Token<String>>>) -> Option<Token<Vec<Token<String>>>> {
|
||||||
|
self.targets.get(&alias).copied()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// find or create the set belonging to the given key in the given
|
||||||
|
/// map-to-set (aka. multimap)
|
||||||
|
fn multimap_entry<'a, K: Eq + Hash + Clone, V>(
|
||||||
|
map: &'a mut HashMap<K, HashSet<V>>,
|
||||||
|
key: &'_ K
|
||||||
|
) -> &'a mut HashSet<V> {
|
||||||
|
map.raw_entry_mut()
|
||||||
|
.from_key(key)
|
||||||
|
.or_insert_with(|| (key.clone(), HashSet::new()))
|
||||||
|
.1
|
||||||
|
}
|
||||||
87
src/pipeline/import_resolution/apply_aliases.rs
Normal file
87
src/pipeline/import_resolution/apply_aliases.rs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
|
use crate::{utils::Substack, interner::{Token, Interner}, pipeline::{ProjectModule, ProjectExt}, representations::tree::{ModEntry, ModMember}, ast::{Rule, Expr}};
|
||||||
|
|
||||||
|
use super::{alias_map::AliasMap, decls::InjectedAsFn};
|
||||||
|
|
||||||
|
fn process_expr(
|
||||||
|
expr: &Expr,
|
||||||
|
alias_map: &AliasMap,
|
||||||
|
injected_as: &impl InjectedAsFn,
|
||||||
|
i: &Interner,
|
||||||
|
) -> Expr {
|
||||||
|
expr.map_names(&|n| {
|
||||||
|
injected_as(&i.r(n)[..]).or_else(|| {
|
||||||
|
alias_map.resolve(n).map(|n| {
|
||||||
|
injected_as(&i.r(n)[..]).unwrap_or(n)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}).unwrap_or_else(|| expr.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: replace is_injected with injected_as
|
||||||
|
/// Replace all aliases with the name they're originally defined as
|
||||||
|
fn apply_aliases_rec(
|
||||||
|
path: Substack<Token<String>>,
|
||||||
|
module: &ProjectModule,
|
||||||
|
alias_map: &AliasMap,
|
||||||
|
i: &Interner,
|
||||||
|
injected_as: &impl InjectedAsFn,
|
||||||
|
) -> ProjectModule {
|
||||||
|
let items = module.items.iter().map(|(name, ent)| {
|
||||||
|
let ModEntry{ exported, member } = ent;
|
||||||
|
let member = match member {
|
||||||
|
ModMember::Item(expr) => ModMember::Item(
|
||||||
|
process_expr(expr, alias_map, injected_as, i)
|
||||||
|
),
|
||||||
|
ModMember::Sub(module) => {
|
||||||
|
let subpath = path.push(*name);
|
||||||
|
let is_ignored = injected_as(&subpath.iter().rev_vec_clone()).is_some();
|
||||||
|
let new_mod = if is_ignored {module.clone()} else {
|
||||||
|
let module = module.as_ref();
|
||||||
|
Rc::new(apply_aliases_rec(
|
||||||
|
subpath, module,
|
||||||
|
alias_map, i, injected_as
|
||||||
|
))
|
||||||
|
};
|
||||||
|
ModMember::Sub(new_mod)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(*name, ModEntry{ exported: *exported, member })
|
||||||
|
}).collect::<HashMap<_, _>>();
|
||||||
|
let rules = module.extra.rules.iter().map(|rule| {
|
||||||
|
let Rule{ source, prio, target } = rule;
|
||||||
|
Rule{
|
||||||
|
prio: *prio,
|
||||||
|
source: Rc::new(source.iter()
|
||||||
|
.map(|expr| process_expr(expr, alias_map, injected_as, i))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
),
|
||||||
|
target: Rc::new(target.iter()
|
||||||
|
.map(|expr| process_expr(expr, alias_map, injected_as, i))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
ProjectModule{
|
||||||
|
items,
|
||||||
|
imports: module.imports.clone(),
|
||||||
|
extra: ProjectExt{
|
||||||
|
rules,
|
||||||
|
exports: module.extra.exports.clone(),
|
||||||
|
file: module.extra.file.clone(),
|
||||||
|
imports_from: module.extra.imports_from.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_aliases(
|
||||||
|
module: &ProjectModule,
|
||||||
|
alias_map: &AliasMap,
|
||||||
|
i: &Interner,
|
||||||
|
injected_as: &impl InjectedAsFn,
|
||||||
|
) -> ProjectModule {
|
||||||
|
apply_aliases_rec(Substack::Bottom, module, alias_map, i, injected_as)
|
||||||
|
}
|
||||||
103
src/pipeline/import_resolution/collect_aliases.rs
Normal file
103
src/pipeline/import_resolution/collect_aliases.rs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::representations::tree::{WalkErrorKind, ModMember};
|
||||||
|
use crate::pipeline::error::{ProjectError, NotExported};
|
||||||
|
use crate::pipeline::project_tree::{ProjectTree, split_path, ProjectModule};
|
||||||
|
use crate::interner::{Token, Interner};
|
||||||
|
use crate::utils::{Substack, pushed};
|
||||||
|
|
||||||
|
use super::alias_map::AliasMap;
|
||||||
|
use super::decls::InjectedAsFn;
|
||||||
|
|
||||||
|
/// Assert that a module identified by a path can see a given symbol
|
||||||
|
fn assert_visible(
|
||||||
|
source: &[Token<String>], // must point to a file or submodule
|
||||||
|
target: &[Token<String>], // may point to a symbol or module of any kind
|
||||||
|
project: &ProjectTree,
|
||||||
|
i: &Interner
|
||||||
|
) -> Result<(), Rc<dyn ProjectError>> {
|
||||||
|
let (tgt_item, tgt_path) = if let Some(s) = target.split_last() {s}
|
||||||
|
else {return Ok(())};
|
||||||
|
let shared_len = source.iter()
|
||||||
|
.zip(tgt_path.iter())
|
||||||
|
.take_while(|(a, b)| a == b)
|
||||||
|
.count();
|
||||||
|
let shared_root = project.0.walk(&tgt_path[..shared_len], false)
|
||||||
|
.expect("checked in parsing");
|
||||||
|
let direct_parent = shared_root.walk(&tgt_path[shared_len..], true)
|
||||||
|
.map_err(|e| match e.kind {
|
||||||
|
WalkErrorKind::Missing => panic!("checked in parsing"),
|
||||||
|
WalkErrorKind::Private => {
|
||||||
|
let full_path = &tgt_path[..shared_len + e.pos];
|
||||||
|
let (file, sub) = split_path(full_path, &project);
|
||||||
|
let (ref_file, ref_sub) = split_path(source, &project);
|
||||||
|
NotExported{
|
||||||
|
file: i.extern_all(file),
|
||||||
|
subpath: i.extern_all(sub),
|
||||||
|
referrer_file: i.extern_all(ref_file),
|
||||||
|
referrer_subpath: i.extern_all(ref_sub),
|
||||||
|
}.rc()
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
let tgt_item_exported = direct_parent.extra.exports.contains_key(tgt_item);
|
||||||
|
let target_prefixes_source = shared_len == tgt_path.len()
|
||||||
|
&& source.get(shared_len) == Some(tgt_item);
|
||||||
|
if !tgt_item_exported && !target_prefixes_source {
|
||||||
|
let (file, sub) = split_path(target, &project);
|
||||||
|
let (ref_file, ref_sub) = split_path(source, &project);
|
||||||
|
Err(NotExported{
|
||||||
|
file: i.extern_all(file),
|
||||||
|
subpath: i.extern_all(sub),
|
||||||
|
referrer_file: i.extern_all(ref_file),
|
||||||
|
referrer_subpath: i.extern_all(ref_sub),
|
||||||
|
}.rc())
|
||||||
|
} else {Ok(())}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Populate target and alias maps from the module tree recursively
|
||||||
|
fn collect_aliases_rec(
|
||||||
|
path: Substack<Token<String>>,
|
||||||
|
module: &ProjectModule,
|
||||||
|
project: &ProjectTree,
|
||||||
|
alias_map: &mut AliasMap,
|
||||||
|
i: &Interner,
|
||||||
|
injected_as: &impl InjectedAsFn,
|
||||||
|
) -> Result<(), Rc<dyn ProjectError>> {
|
||||||
|
// Assume injected module has been alias-resolved
|
||||||
|
let mod_path_v = path.iter().rev_vec_clone();
|
||||||
|
if injected_as(&mod_path_v).is_some() {return Ok(())};
|
||||||
|
for (&name, &target_mod) in module.extra.imports_from.iter() {
|
||||||
|
let target_mod_v = i.r(target_mod);
|
||||||
|
let target_sym_v = pushed(target_mod_v, name);
|
||||||
|
assert_visible(&mod_path_v, &target_sym_v, project, i)?;
|
||||||
|
let sym_path_v = pushed(&mod_path_v, name);
|
||||||
|
let sym_path = i.i(&sym_path_v);
|
||||||
|
let target_sym = i.i(&target_sym_v);
|
||||||
|
alias_map.link(sym_path, target_sym);
|
||||||
|
}
|
||||||
|
for (&name, entry) in module.items.iter() {
|
||||||
|
let submodule = if let ModMember::Sub(s) = &entry.member {
|
||||||
|
s.as_ref()
|
||||||
|
} else {continue};
|
||||||
|
collect_aliases_rec(
|
||||||
|
path.push(name),
|
||||||
|
submodule, project, alias_map,
|
||||||
|
i, injected_as,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Populate target and alias maps from the module tree
|
||||||
|
pub fn collect_aliases(
|
||||||
|
module: &ProjectModule,
|
||||||
|
project: &ProjectTree,
|
||||||
|
alias_map: &mut AliasMap,
|
||||||
|
i: &Interner,
|
||||||
|
injected_as: &impl InjectedAsFn,
|
||||||
|
) -> Result<(), Rc<dyn ProjectError>> {
|
||||||
|
collect_aliases_rec(
|
||||||
|
Substack::Bottom, module, project, alias_map,
|
||||||
|
i, injected_as
|
||||||
|
)
|
||||||
|
}
|
||||||
5
src/pipeline/import_resolution/decls.rs
Normal file
5
src/pipeline/import_resolution/decls.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
use crate::interner::Token;
|
||||||
|
|
||||||
|
pub trait InjectedAsFn = Fn(
|
||||||
|
&[Token<String>]
|
||||||
|
) -> Option<Token<Vec<Token<String>>>>;
|
||||||
7
src/pipeline/import_resolution/mod.rs
Normal file
7
src/pipeline/import_resolution/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
mod alias_map;
|
||||||
|
mod collect_aliases;
|
||||||
|
mod apply_aliases;
|
||||||
|
mod resolve_imports;
|
||||||
|
mod decls;
|
||||||
|
|
||||||
|
pub use resolve_imports::resolve_imports;
|
||||||
28
src/pipeline/import_resolution/resolve_imports.rs
Normal file
28
src/pipeline/import_resolution/resolve_imports.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::interner::Interner;
|
||||||
|
use crate::pipeline::error::ProjectError;
|
||||||
|
use crate::pipeline::project_tree::ProjectTree;
|
||||||
|
|
||||||
|
|
||||||
|
use super::alias_map::AliasMap;
|
||||||
|
use super::collect_aliases::collect_aliases;
|
||||||
|
use super::apply_aliases::apply_aliases;
|
||||||
|
use super::decls::InjectedAsFn;
|
||||||
|
|
||||||
|
/// Follow import chains to locate the original name of all tokens, then
|
||||||
|
/// replace these aliases with the original names throughout the tree
|
||||||
|
pub fn resolve_imports(
|
||||||
|
project: ProjectTree,
|
||||||
|
i: &Interner,
|
||||||
|
injected_as: &impl InjectedAsFn,
|
||||||
|
) -> Result<ProjectTree, Rc<dyn ProjectError>> {
|
||||||
|
let mut map = AliasMap::new();
|
||||||
|
collect_aliases(
|
||||||
|
project.0.as_ref(),
|
||||||
|
&project, &mut map,
|
||||||
|
i, injected_as
|
||||||
|
)?;
|
||||||
|
let new_mod = apply_aliases(project.0.as_ref(), &map, i, injected_as);
|
||||||
|
Ok(ProjectTree(Rc::new(new_mod)))
|
||||||
|
}
|
||||||
19
src/pipeline/mod.rs
Normal file
19
src/pipeline/mod.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
pub mod error;
|
||||||
|
mod project_tree;
|
||||||
|
mod source_loader;
|
||||||
|
mod import_abs_path;
|
||||||
|
mod split_name;
|
||||||
|
mod import_resolution;
|
||||||
|
pub mod file_loader;
|
||||||
|
mod parse_layer;
|
||||||
|
|
||||||
|
pub use parse_layer::parse_layer;
|
||||||
|
pub use project_tree::{
|
||||||
|
ConstTree, ProjectExt, ProjectModule, ProjectTree, from_const_tree,
|
||||||
|
collect_consts, collect_rules,
|
||||||
|
};
|
||||||
|
// pub use file_loader::{Loaded, FileLoadingError, IOResult};
|
||||||
|
// pub use error::{
|
||||||
|
// ErrorPosition, ModuleNotFound, NotExported, ParseErrorWithPath,
|
||||||
|
// ProjectError, TooManySupers, UnexpectedDirectory
|
||||||
|
// };
|
||||||
52
src/pipeline/parse_layer.rs
Normal file
52
src/pipeline/parse_layer.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::representations::sourcefile::FileEntry;
|
||||||
|
use crate::interner::{Token, Interner};
|
||||||
|
|
||||||
|
use super::{project_tree, import_resolution};
|
||||||
|
use super::source_loader;
|
||||||
|
use super::file_loader::IOResult;
|
||||||
|
use super::error::ProjectError;
|
||||||
|
use super::ProjectTree;
|
||||||
|
|
||||||
|
/// Using an IO callback, produce a project tree that includes the given
|
||||||
|
/// target symbols or files if they're defined.
|
||||||
|
///
|
||||||
|
/// The environment accessible to the loaded source can be specified with
|
||||||
|
/// a pre-existing tree which will be merged with the loaded data, and a
|
||||||
|
/// prelude which will be prepended to each individual file. Since the
|
||||||
|
/// prelude gets compiled with each file, normally it should be a glob
|
||||||
|
/// import pointing to a module in the environment.
|
||||||
|
pub fn parse_layer<'a>(
|
||||||
|
targets: &[Token<Vec<Token<String>>>],
|
||||||
|
loader: &impl Fn(Token<Vec<Token<String>>>) -> IOResult,
|
||||||
|
environment: &'a ProjectTree,
|
||||||
|
prelude: &[FileEntry],
|
||||||
|
i: &Interner,
|
||||||
|
) -> Result<ProjectTree, Rc<dyn ProjectError>> {
|
||||||
|
// A path is injected if it is walkable in the injected tree
|
||||||
|
let injected_as = |path: &[Token<String>]| {
|
||||||
|
let (item, modpath) = path.split_last()?;
|
||||||
|
let module = environment.0.walk(modpath, false).ok()?;
|
||||||
|
let inj = module.extra.exports.get(item).copied()?;
|
||||||
|
Some(inj)
|
||||||
|
};
|
||||||
|
let injected_names = |path: Token<Vec<Token<String>>>| {
|
||||||
|
let pathv = &i.r(path)[..];
|
||||||
|
let module = environment.0.walk(&pathv, false).ok()?;
|
||||||
|
Some(Rc::new(
|
||||||
|
module.extra.exports.keys().copied().collect()
|
||||||
|
))
|
||||||
|
};
|
||||||
|
let source = source_loader::load_source(
|
||||||
|
targets, i, loader, &|path| injected_as(path).is_some()
|
||||||
|
)?;
|
||||||
|
let tree = project_tree::build_tree(source, i, prelude, &injected_names)?;
|
||||||
|
let sum = ProjectTree(Rc::new(
|
||||||
|
environment.0.as_ref().clone()
|
||||||
|
+ tree.0.as_ref().clone()
|
||||||
|
));
|
||||||
|
let resolvd = import_resolution::resolve_imports(sum, i, &injected_as)?;
|
||||||
|
// Addition among modules favours the left hand side.
|
||||||
|
Ok(resolvd)
|
||||||
|
}
|
||||||
52
src/pipeline/project_tree/add_prelude.rs
Normal file
52
src/pipeline/project_tree/add_prelude.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use crate::representations::sourcefile::{Member, FileEntry};
|
||||||
|
use crate::interner::Token;
|
||||||
|
|
||||||
|
fn member_rec(
|
||||||
|
// object
|
||||||
|
member: Member,
|
||||||
|
// context
|
||||||
|
path: &[Token<String>],
|
||||||
|
prelude: &[FileEntry],
|
||||||
|
) -> Member {
|
||||||
|
match member {
|
||||||
|
Member::Namespace(name, body) => {
|
||||||
|
let new_body = entv_rec(
|
||||||
|
body,
|
||||||
|
path,
|
||||||
|
prelude
|
||||||
|
);
|
||||||
|
Member::Namespace(name, new_body)
|
||||||
|
},
|
||||||
|
any => any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn entv_rec(
|
||||||
|
// object
|
||||||
|
data: Vec<FileEntry>,
|
||||||
|
// context
|
||||||
|
mod_path: &[Token<String>],
|
||||||
|
prelude: &[FileEntry],
|
||||||
|
) -> Vec<FileEntry> {
|
||||||
|
prelude.iter().cloned()
|
||||||
|
.chain(data.into_iter()
|
||||||
|
.map(|ent| match ent {
|
||||||
|
FileEntry::Exported(mem) => FileEntry::Exported(member_rec(
|
||||||
|
mem, mod_path, prelude
|
||||||
|
)),
|
||||||
|
FileEntry::Internal(mem) => FileEntry::Internal(member_rec(
|
||||||
|
mem, mod_path, prelude
|
||||||
|
)),
|
||||||
|
any => any
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_prelude(
|
||||||
|
data: Vec<FileEntry>,
|
||||||
|
path: &[Token<String>],
|
||||||
|
prelude: &[FileEntry],
|
||||||
|
) -> Vec<FileEntry> {
|
||||||
|
entv_rec(data, path, prelude)
|
||||||
|
}
|
||||||
215
src/pipeline/project_tree/build_tree.rs
Normal file
215
src/pipeline/project_tree/build_tree.rs
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
|
use crate::pipeline::error::ProjectError;
|
||||||
|
use crate::interner::{Token, Interner};
|
||||||
|
use crate::utils::iter::{box_once, box_empty};
|
||||||
|
use crate::utils::{Substack, pushed};
|
||||||
|
use crate::ast::{Expr, Constant};
|
||||||
|
use crate::pipeline::source_loader::{LoadedSourceTable, LoadedSource};
|
||||||
|
use crate::representations::tree::{Module, ModMember, ModEntry};
|
||||||
|
use crate::representations::sourcefile::{FileEntry, Member, absolute_path};
|
||||||
|
|
||||||
|
use super::collect_ops::InjectedOperatorsFn;
|
||||||
|
use super::{collect_ops, ProjectTree, ProjectExt};
|
||||||
|
use super::parse_file::parse_file;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ParsedSource<'a> {
|
||||||
|
path: Vec<Token<String>>,
|
||||||
|
loaded: &'a LoadedSource,
|
||||||
|
parsed: Vec<FileEntry>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn split_path<'a>(path: &'a [Token<String>], proj: &'a ProjectTree)
|
||||||
|
-> (&'a [Token<String>], &'a [Token<String>])
|
||||||
|
{
|
||||||
|
let (end, body) = if let Some(s) = path.split_last() {s}
|
||||||
|
else {return (&[], &[])};
|
||||||
|
let mut module = proj.0.walk(body, false).expect("invalid path cannot be split");
|
||||||
|
if let ModMember::Sub(m) = &module.items[end].member {
|
||||||
|
module = m.clone();
|
||||||
|
}
|
||||||
|
let file = module.extra.file.as_ref()
|
||||||
|
.map(|s| &path[..s.len()])
|
||||||
|
.unwrap_or(&path[..]);
|
||||||
|
let subpath = &path[file.len()..];
|
||||||
|
(file, subpath)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert normalized, prefixed source into a module
|
||||||
|
fn source_to_module(
|
||||||
|
// level
|
||||||
|
path: Substack<Token<String>>,
|
||||||
|
preparsed: &Module<impl Clone, impl Clone>,
|
||||||
|
// data
|
||||||
|
data: Vec<FileEntry>,
|
||||||
|
// context
|
||||||
|
i: &Interner,
|
||||||
|
filepath_len: usize,
|
||||||
|
) -> Rc<Module<Expr, ProjectExt>> {
|
||||||
|
let path_v = path.iter().rev_vec_clone();
|
||||||
|
let imports = data.iter()
|
||||||
|
.filter_map(|ent| if let FileEntry::Import(impv) = ent {
|
||||||
|
Some(impv.iter())
|
||||||
|
} else {None})
|
||||||
|
.flatten()
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let imports_from = imports.iter()
|
||||||
|
.map(|imp| {
|
||||||
|
let mut imp_path_v = i.r(imp.path).clone();
|
||||||
|
imp_path_v.push(imp.name.expect("imports normalized"));
|
||||||
|
let mut abs_path = absolute_path(
|
||||||
|
&path_v,
|
||||||
|
&imp_path_v,
|
||||||
|
i, &|n| preparsed.items.contains_key(&n)
|
||||||
|
).expect("tested in preparsing");
|
||||||
|
let name = abs_path.pop().expect("importing the global context");
|
||||||
|
(name, i.i(&abs_path))
|
||||||
|
})
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
let exports = data.iter()
|
||||||
|
.flat_map(|ent| {
|
||||||
|
let mk_ent = |name| (name, i.i(&pushed(&path_v, name)));
|
||||||
|
match ent {
|
||||||
|
FileEntry::Export(names)
|
||||||
|
=> Box::new(names.iter().copied().map(mk_ent)),
|
||||||
|
FileEntry::Exported(mem) => match mem {
|
||||||
|
Member::Constant(constant) => box_once(mk_ent(constant.name)),
|
||||||
|
Member::Namespace(name, _) => box_once(mk_ent(*name)),
|
||||||
|
Member::Rule(rule) => {
|
||||||
|
let mut names = Vec::new();
|
||||||
|
for e in rule.source.iter() {
|
||||||
|
e.visit_names(Substack::Bottom, &mut |n| {
|
||||||
|
if let Some([name]) = i.r(n).strip_prefix(&path_v[..]) {
|
||||||
|
names.push((*name, n))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Box::new(names.into_iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => box_empty()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
let rules = data.iter()
|
||||||
|
.filter_map(|ent| match ent {
|
||||||
|
FileEntry::Exported(Member::Rule(rule)) => Some(rule),
|
||||||
|
FileEntry::Internal(Member::Rule(rule)) => Some(rule),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let items = data.into_iter()
|
||||||
|
.filter_map(|ent| match ent {
|
||||||
|
FileEntry::Exported(Member::Namespace(name, body)) => {
|
||||||
|
let prep_member = &preparsed.items[&name].member;
|
||||||
|
let new_prep = if let ModMember::Sub(s) = prep_member {s.as_ref()}
|
||||||
|
else { panic!("preparsed missing a submodule") };
|
||||||
|
let module = source_to_module(
|
||||||
|
path.push(name),
|
||||||
|
new_prep, body, i, filepath_len
|
||||||
|
);
|
||||||
|
let member = ModMember::Sub(module);
|
||||||
|
Some((name, ModEntry{ exported: true, member }))
|
||||||
|
}
|
||||||
|
FileEntry::Internal(Member::Namespace(name, body)) => {
|
||||||
|
let prep_member = &preparsed.items[&name].member;
|
||||||
|
let new_prep = if let ModMember::Sub(s) = prep_member {s.as_ref()}
|
||||||
|
else { panic!("preparsed missing a submodule") };
|
||||||
|
let module = source_to_module(
|
||||||
|
path.push(name),
|
||||||
|
new_prep, body, i, filepath_len
|
||||||
|
);
|
||||||
|
let member = ModMember::Sub(module);
|
||||||
|
Some((name, ModEntry{ exported: false, member }))
|
||||||
|
}
|
||||||
|
FileEntry::Exported(Member::Constant(Constant{ name, value })) => {
|
||||||
|
let member = ModMember::Item(value);
|
||||||
|
Some((name, ModEntry{ exported: true, member }))
|
||||||
|
}
|
||||||
|
FileEntry::Internal(Member::Constant(Constant{ name, value })) => {
|
||||||
|
let member = ModMember::Item(value);
|
||||||
|
Some((name, ModEntry{ exported: false, member }))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
Rc::new(Module {
|
||||||
|
imports,
|
||||||
|
items,
|
||||||
|
extra: ProjectExt {
|
||||||
|
imports_from,
|
||||||
|
exports,
|
||||||
|
rules,
|
||||||
|
file: Some(path_v[..filepath_len].to_vec())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn files_to_module(
|
||||||
|
path: Substack<Token<String>>,
|
||||||
|
files: &[ParsedSource],
|
||||||
|
i: &Interner
|
||||||
|
) -> Rc<Module<Expr, ProjectExt>> {
|
||||||
|
let lvl = path.len();
|
||||||
|
let path_v = path.iter().rev_vec_clone();
|
||||||
|
if files.len() == 1 && files[0].path.len() == lvl {
|
||||||
|
return source_to_module(
|
||||||
|
path,
|
||||||
|
files[0].loaded.preparsed.0.as_ref(),
|
||||||
|
files[0].parsed.clone(),
|
||||||
|
i, path.len()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
let items = files.group_by(|a, b| a.path[lvl] == b.path[lvl]).into_iter()
|
||||||
|
.map(|files| {
|
||||||
|
let namespace = files[0].path[lvl];
|
||||||
|
let subpath = path.push(namespace);
|
||||||
|
let module = files_to_module(subpath, files, i);
|
||||||
|
let member = ModMember::Sub(module);
|
||||||
|
(namespace, ModEntry{ exported: true, member })
|
||||||
|
})
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
let exports = items.keys()
|
||||||
|
.copied()
|
||||||
|
.map(|name| (name, i.i(&pushed(&path_v, name))))
|
||||||
|
.collect();
|
||||||
|
Rc::new(Module{
|
||||||
|
items,
|
||||||
|
imports: vec![],
|
||||||
|
extra: ProjectExt {
|
||||||
|
exports,
|
||||||
|
imports_from: HashMap::new(),
|
||||||
|
rules: vec![], file: None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_tree<'a>(
|
||||||
|
files: LoadedSourceTable,
|
||||||
|
i: &Interner,
|
||||||
|
prelude: &[FileEntry],
|
||||||
|
injected: &impl InjectedOperatorsFn,
|
||||||
|
) -> Result<ProjectTree, Rc<dyn ProjectError>> {
|
||||||
|
let ops_cache = collect_ops::mk_cache(&files, i, injected);
|
||||||
|
let mut entries = files.iter()
|
||||||
|
.map(|(path, loaded)| Ok((
|
||||||
|
i.r(*path),
|
||||||
|
loaded,
|
||||||
|
parse_file(*path, &files, &ops_cache, i, prelude)?
|
||||||
|
)))
|
||||||
|
.collect::<Result<Vec<_>, Rc<dyn ProjectError>>>()?;
|
||||||
|
// sort by similarity, then longest-first
|
||||||
|
entries.sort_unstable_by(|a, b| a.0.cmp(&b.0).reverse());
|
||||||
|
let files = entries.into_iter()
|
||||||
|
.map(|(path, loaded, parsed)| ParsedSource{
|
||||||
|
loaded, parsed,
|
||||||
|
path: path.clone()
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
Ok(ProjectTree(files_to_module(Substack::Bottom, &files, i)))
|
||||||
|
}
|
||||||
75
src/pipeline/project_tree/collect_ops/exported_ops.rs
Normal file
75
src/pipeline/project_tree/collect_ops/exported_ops.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use hashbrown::HashSet;
|
||||||
|
|
||||||
|
use crate::representations::tree::WalkErrorKind;
|
||||||
|
use crate::pipeline::source_loader::LoadedSourceTable;
|
||||||
|
use crate::pipeline::error::{ProjectError, ModuleNotFound};
|
||||||
|
use crate::interner::{Token, Interner};
|
||||||
|
use crate::utils::Cache;
|
||||||
|
use crate::pipeline::split_name::split_name;
|
||||||
|
|
||||||
|
pub type OpsResult = Result<Rc<HashSet<Token<String>>>, Rc<dyn ProjectError>>;
|
||||||
|
pub type ExportedOpsCache<'a> = Cache<'a, Token<Vec<Token<String>>>, OpsResult>;
|
||||||
|
|
||||||
|
pub trait InjectedOperatorsFn = Fn(
|
||||||
|
Token<Vec<Token<String>>>
|
||||||
|
) -> Option<Rc<HashSet<Token<String>>>>;
|
||||||
|
|
||||||
|
fn coprefix<T: Eq>(
|
||||||
|
l: impl Iterator<Item = T>,
|
||||||
|
r: impl Iterator<Item = T>
|
||||||
|
) -> usize {
|
||||||
|
l.zip(r).take_while(|(a, b)| a == b).count()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collect all names exported by the module at the specified path
|
||||||
|
pub fn collect_exported_ops(
|
||||||
|
path: Token<Vec<Token<String>>>,
|
||||||
|
loaded: &LoadedSourceTable,
|
||||||
|
i: &Interner,
|
||||||
|
injected: &impl InjectedOperatorsFn
|
||||||
|
) -> OpsResult {
|
||||||
|
if let Some(i) = injected(path) {return Ok(i)}
|
||||||
|
let is_file = |n: &[Token<String>]| loaded.contains_key(&i.i(n));
|
||||||
|
let path_s = &i.r(path)[..];
|
||||||
|
let name_split = split_name(path_s, &is_file);
|
||||||
|
let (fpath_v, subpath_v) = if let Some(f) = name_split {f} else {
|
||||||
|
return Ok(Rc::new(loaded.keys().copied()
|
||||||
|
.filter_map(|modname| {
|
||||||
|
let modname_s = i.r(modname);
|
||||||
|
if path_s.len() == coprefix(path_s.iter(), modname_s.iter()) {
|
||||||
|
Some(modname_s[path_s.len()])
|
||||||
|
} else {None}
|
||||||
|
})
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
))
|
||||||
|
};
|
||||||
|
let fpath = i.i(fpath_v);
|
||||||
|
let preparsed = &loaded[&fpath].preparsed;
|
||||||
|
let module = preparsed.0.walk(&subpath_v, false)
|
||||||
|
.map_err(|walk_err| match walk_err.kind {
|
||||||
|
WalkErrorKind::Private => unreachable!("visibility is not being checked here"),
|
||||||
|
WalkErrorKind::Missing => ModuleNotFound{
|
||||||
|
file: i.extern_vec(fpath),
|
||||||
|
subpath: subpath_v.into_iter()
|
||||||
|
.take(walk_err.pos)
|
||||||
|
.map(|t| i.r(*t))
|
||||||
|
.cloned()
|
||||||
|
.collect()
|
||||||
|
}.rc(),
|
||||||
|
})?;
|
||||||
|
Ok(Rc::new(module.items.iter()
|
||||||
|
.filter(|(_, v)| v.exported)
|
||||||
|
.map(|(k, _)| *k)
|
||||||
|
.collect()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mk_cache<'a>(
|
||||||
|
loaded: &'a LoadedSourceTable,
|
||||||
|
i: &'a Interner,
|
||||||
|
injected: &'a impl InjectedOperatorsFn,
|
||||||
|
) -> ExportedOpsCache<'a> {
|
||||||
|
Cache::new(|path, _this| collect_exported_ops(path, loaded, i, injected))
|
||||||
|
}
|
||||||
8
src/pipeline/project_tree/collect_ops/mod.rs
Normal file
8
src/pipeline/project_tree/collect_ops/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
mod exported_ops;
|
||||||
|
mod ops_for;
|
||||||
|
|
||||||
|
pub use exported_ops::{
|
||||||
|
ExportedOpsCache, OpsResult, InjectedOperatorsFn,
|
||||||
|
collect_exported_ops, mk_cache
|
||||||
|
};
|
||||||
|
pub use ops_for::collect_ops_for;
|
||||||
49
src/pipeline/project_tree/collect_ops/ops_for.rs
Normal file
49
src/pipeline/project_tree/collect_ops/ops_for.rs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use hashbrown::HashSet;
|
||||||
|
|
||||||
|
use crate::parse::is_op;
|
||||||
|
use crate::pipeline::error::ProjectError;
|
||||||
|
use crate::pipeline::source_loader::LoadedSourceTable;
|
||||||
|
use crate::interner::{Token, Interner};
|
||||||
|
use crate::representations::tree::{Module, ModMember};
|
||||||
|
use crate::pipeline::import_abs_path::import_abs_path;
|
||||||
|
|
||||||
|
use super::exported_ops::{ExportedOpsCache, OpsResult};
|
||||||
|
|
||||||
|
/// Collect all operators and names, exported or local, defined in this
|
||||||
|
/// tree.
|
||||||
|
fn tree_all_ops(
|
||||||
|
module: &Module<impl Clone, impl Clone>,
|
||||||
|
ops: &mut HashSet<Token<String>>
|
||||||
|
) {
|
||||||
|
ops.extend(module.items.keys().copied());
|
||||||
|
for ent in module.items.values() {
|
||||||
|
if let ModMember::Sub(m) = &ent.member {
|
||||||
|
tree_all_ops(m.as_ref(), ops);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collect all names imported in this file
|
||||||
|
pub fn collect_ops_for(
|
||||||
|
file: &[Token<String>],
|
||||||
|
loaded: &LoadedSourceTable,
|
||||||
|
ops_cache: &ExportedOpsCache,
|
||||||
|
i: &Interner
|
||||||
|
) -> OpsResult {
|
||||||
|
let tree = &loaded[&i.i(file)].preparsed.0;
|
||||||
|
let mut ret = HashSet::new();
|
||||||
|
tree_all_ops(tree.as_ref(), &mut ret);
|
||||||
|
tree.visit_all_imports(&mut |modpath, module, import| {
|
||||||
|
if let Some(n) = import.name { ret.insert(n); } else {
|
||||||
|
let path = import_abs_path(
|
||||||
|
&file, modpath, module, &i.r(import.path)[..], i
|
||||||
|
).expect("This error should have been caught during loading");
|
||||||
|
ret.extend(ops_cache.find(&i.i(&path))?.iter().copied());
|
||||||
|
}
|
||||||
|
Ok::<_, Rc<dyn ProjectError>>(())
|
||||||
|
})?;
|
||||||
|
ret.drain_filter(|t| !is_op(i.r(*t)));
|
||||||
|
Ok(Rc::new(ret))
|
||||||
|
}
|
||||||
93
src/pipeline/project_tree/const_tree.rs
Normal file
93
src/pipeline/project_tree/const_tree.rs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
use std::{ops::Add, rc::Rc};
|
||||||
|
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
|
use crate::representations::tree::{ModEntry, ModMember, Module};
|
||||||
|
use crate::representations::Primitive;
|
||||||
|
use crate::representations::location::Location;
|
||||||
|
use crate::foreign::ExternFn;
|
||||||
|
use crate::interner::{Token, Interner};
|
||||||
|
use crate::ast::{Expr, Clause};
|
||||||
|
use crate::utils::{Substack, pushed};
|
||||||
|
|
||||||
|
use super::{ProjectModule, ProjectExt, ProjectTree};
|
||||||
|
|
||||||
|
pub enum ConstTree {
|
||||||
|
Const(Expr),
|
||||||
|
Tree(HashMap<Token<String>, ConstTree>)
|
||||||
|
}
|
||||||
|
impl ConstTree {
|
||||||
|
pub fn xfn(xfn: impl ExternFn + 'static) -> Self {
|
||||||
|
Self::Const(Expr{
|
||||||
|
location: Location::Unknown,
|
||||||
|
value: Clause::P(Primitive::ExternFn(Box::new(xfn)))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn tree(
|
||||||
|
arr: impl IntoIterator<Item = (Token<String>, Self)>
|
||||||
|
) -> Self {
|
||||||
|
Self::Tree(arr.into_iter().collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Add for ConstTree {
|
||||||
|
type Output = ConstTree;
|
||||||
|
|
||||||
|
fn add(self, rhs: ConstTree) -> Self::Output {
|
||||||
|
if let (Self::Tree(t1), Self::Tree(mut t2)) = (self, rhs) {
|
||||||
|
let mut product = HashMap::new();
|
||||||
|
for (key, i1) in t1 {
|
||||||
|
if let Some(i2) = t2.remove(&key) {
|
||||||
|
product.insert(key, i1 + i2);
|
||||||
|
} else {
|
||||||
|
product.insert(key, i1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
product.extend(t2.into_iter());
|
||||||
|
Self::Tree(product)
|
||||||
|
} else {
|
||||||
|
panic!("cannot combine tree and value fields")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_const_tree_rec(
|
||||||
|
path: Substack<Token<String>>,
|
||||||
|
consts: HashMap<Token<String>, ConstTree>,
|
||||||
|
file: &[Token<String>],
|
||||||
|
i: &Interner,
|
||||||
|
) -> ProjectModule {
|
||||||
|
let mut items = HashMap::new();
|
||||||
|
let path_v = path.iter().rev_vec_clone();
|
||||||
|
for (name, item) in consts {
|
||||||
|
items.insert(name, ModEntry{
|
||||||
|
exported: true,
|
||||||
|
member: match item {
|
||||||
|
ConstTree::Const(c) => ModMember::Item(c),
|
||||||
|
ConstTree::Tree(t) => ModMember::Sub(Rc::new(
|
||||||
|
from_const_tree_rec(path.push(name), t, file, i)
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let exports = items.keys()
|
||||||
|
.map(|name| (*name, i.i(&pushed(&path_v, *name))))
|
||||||
|
.collect();
|
||||||
|
Module {
|
||||||
|
items,
|
||||||
|
imports: vec![],
|
||||||
|
extra: ProjectExt {
|
||||||
|
exports,
|
||||||
|
file: Some(file.to_vec()),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_const_tree(
|
||||||
|
consts: HashMap<Token<String>, ConstTree>,
|
||||||
|
file: &[Token<String>],
|
||||||
|
i: &Interner,
|
||||||
|
) -> ProjectTree {
|
||||||
|
let module = from_const_tree_rec(Substack::Bottom, consts, file, i);
|
||||||
|
ProjectTree(Rc::new(module))
|
||||||
|
}
|
||||||
38
src/pipeline/project_tree/mod.rs
Normal file
38
src/pipeline/project_tree/mod.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/* FILE SEPARATION BOUNDARY
|
||||||
|
|
||||||
|
Collect all operators accessible in each file, parse the files with
|
||||||
|
correct tokenization, resolve glob imports, convert expressions to
|
||||||
|
refer to tokens with (local) absolute path, and connect them into a
|
||||||
|
single tree.
|
||||||
|
|
||||||
|
The module checks for imports from missing modules (including submodules).
|
||||||
|
All other errors must be checked later.
|
||||||
|
|
||||||
|
Injection strategy:
|
||||||
|
Return all items of the given module in the injected tree for `injected`
|
||||||
|
The output of this stage is a tree, which can simply be overlaid with
|
||||||
|
the injected tree
|
||||||
|
*/
|
||||||
|
|
||||||
|
mod collect_ops;
|
||||||
|
mod parse_file;
|
||||||
|
mod build_tree;
|
||||||
|
mod normalize_imports;
|
||||||
|
mod prefix;
|
||||||
|
mod tree;
|
||||||
|
mod const_tree;
|
||||||
|
mod add_prelude;
|
||||||
|
|
||||||
|
pub use collect_ops::InjectedOperatorsFn;
|
||||||
|
|
||||||
|
pub use const_tree::{
|
||||||
|
ConstTree, from_const_tree,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use tree::{
|
||||||
|
ProjectExt, ProjectModule, ProjectTree, collect_consts, collect_rules
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use build_tree::{
|
||||||
|
build_tree, split_path
|
||||||
|
};
|
||||||
84
src/pipeline/project_tree/normalize_imports.rs
Normal file
84
src/pipeline/project_tree/normalize_imports.rs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
use crate::representations::tree::{Module, ModMember};
|
||||||
|
use crate::representations::sourcefile::{Member, FileEntry, Import};
|
||||||
|
use crate::utils::BoxedIter;
|
||||||
|
use crate::utils::{Substack, iter::box_once};
|
||||||
|
use crate::interner::{Interner, Token};
|
||||||
|
use crate::pipeline::import_abs_path::import_abs_path;
|
||||||
|
|
||||||
|
use super::collect_ops::ExportedOpsCache;
|
||||||
|
|
||||||
|
fn member_rec(
|
||||||
|
// level
|
||||||
|
mod_stack: Substack<Token<String>>,
|
||||||
|
preparsed: &Module<impl Clone, impl Clone>,
|
||||||
|
// object
|
||||||
|
member: Member,
|
||||||
|
// context
|
||||||
|
path: &[Token<String>],
|
||||||
|
ops_cache: &ExportedOpsCache,
|
||||||
|
i: &Interner
|
||||||
|
) -> Member {
|
||||||
|
match member {
|
||||||
|
Member::Namespace(name, body) => {
|
||||||
|
let prepmember = &preparsed.items[&name].member;
|
||||||
|
let subprep = if let ModMember::Sub(m) = prepmember {m.clone()}
|
||||||
|
else {unreachable!("This name must point to a namespace")};
|
||||||
|
let new_body = entv_rec(
|
||||||
|
mod_stack.push(name),
|
||||||
|
subprep.as_ref(),
|
||||||
|
body,
|
||||||
|
path, ops_cache, i
|
||||||
|
);
|
||||||
|
Member::Namespace(name, new_body)
|
||||||
|
},
|
||||||
|
any => any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn entv_rec(
|
||||||
|
// level
|
||||||
|
mod_stack: Substack<Token<String>>,
|
||||||
|
preparsed: &Module<impl Clone, impl Clone>,
|
||||||
|
// object
|
||||||
|
data: Vec<FileEntry>,
|
||||||
|
// context
|
||||||
|
mod_path: &[Token<String>],
|
||||||
|
ops_cache: &ExportedOpsCache,
|
||||||
|
i: &Interner
|
||||||
|
) -> Vec<FileEntry> {
|
||||||
|
data.into_iter()
|
||||||
|
.map(|ent| match ent {
|
||||||
|
FileEntry::Import(imps) => FileEntry::Import(imps.into_iter()
|
||||||
|
.flat_map(|import| if let Import{ name: None, path } = import {
|
||||||
|
let p = import_abs_path(
|
||||||
|
mod_path, mod_stack, preparsed, &i.r(path)[..], i
|
||||||
|
).expect("Should have emerged in preparsing");
|
||||||
|
let names = ops_cache.find(&i.i(&p))
|
||||||
|
.expect("Should have emerged in second parsing");
|
||||||
|
let imports = names.iter()
|
||||||
|
.map(move |&n| Import{ name: Some(n), path })
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
Box::new(imports.into_iter()) as BoxedIter<Import>
|
||||||
|
} else {box_once(import)})
|
||||||
|
.collect()
|
||||||
|
),
|
||||||
|
FileEntry::Exported(mem) => FileEntry::Exported(member_rec(
|
||||||
|
mod_stack, preparsed, mem, mod_path, ops_cache, i
|
||||||
|
)),
|
||||||
|
FileEntry::Internal(mem) => FileEntry::Internal(member_rec(
|
||||||
|
mod_stack, preparsed, mem, mod_path, ops_cache, i
|
||||||
|
)),
|
||||||
|
any => any
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn normalize_imports(
|
||||||
|
preparsed: &Module<impl Clone, impl Clone>,
|
||||||
|
data: Vec<FileEntry>,
|
||||||
|
path: &[Token<String>],
|
||||||
|
ops_cache: &ExportedOpsCache,
|
||||||
|
i: &Interner
|
||||||
|
) -> Vec<FileEntry> {
|
||||||
|
entv_rec(Substack::Bottom, preparsed, data, path, ops_cache, i)
|
||||||
|
}
|
||||||
44
src/pipeline/project_tree/parse_file.rs
Normal file
44
src/pipeline/project_tree/parse_file.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::parse;
|
||||||
|
use crate::pipeline::error::ProjectError;
|
||||||
|
use crate::representations::sourcefile::{FileEntry, normalize_namespaces};
|
||||||
|
use crate::pipeline::source_loader::LoadedSourceTable;
|
||||||
|
use crate::interner::{Token, Interner};
|
||||||
|
|
||||||
|
use super::add_prelude::add_prelude;
|
||||||
|
use super::collect_ops::{ExportedOpsCache, collect_ops_for};
|
||||||
|
use super::normalize_imports::normalize_imports;
|
||||||
|
use super::prefix::prefix;
|
||||||
|
|
||||||
|
pub fn parse_file(
|
||||||
|
path: Token<Vec<Token<String>>>,
|
||||||
|
loaded: &LoadedSourceTable,
|
||||||
|
ops_cache: &ExportedOpsCache,
|
||||||
|
i: &Interner,
|
||||||
|
prelude: &[FileEntry],
|
||||||
|
) -> Result<Vec<FileEntry>, Rc<dyn ProjectError>> {
|
||||||
|
let ld = &loaded[&path];
|
||||||
|
// let ops_cache = collect_ops::mk_cache(loaded, i);
|
||||||
|
let ops = collect_ops_for(&i.r(path)[..], loaded, ops_cache, i)?;
|
||||||
|
let ops_vec = ops.iter()
|
||||||
|
.map(|t| i.r(*t))
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let ctx = parse::ParsingContext{
|
||||||
|
interner: i,
|
||||||
|
ops: &ops_vec,
|
||||||
|
file: Rc::new(i.extern_vec(path))
|
||||||
|
};
|
||||||
|
let entries = parse::parse(ld.text.as_str(), ctx)
|
||||||
|
.expect("This error should have been caught during loading");
|
||||||
|
let with_prelude = add_prelude(entries, &i.r(path)[..], prelude);
|
||||||
|
let impnormalized = normalize_imports(
|
||||||
|
&ld.preparsed.0, with_prelude, &i.r(path)[..], ops_cache, i
|
||||||
|
);
|
||||||
|
let nsnormalized = normalize_namespaces(
|
||||||
|
Box::new(impnormalized.into_iter()), i
|
||||||
|
).expect("This error should have been caught during preparsing");
|
||||||
|
let prefixed = prefix(nsnormalized, &i.r(path)[..], ops_cache, i);
|
||||||
|
Ok(prefixed)
|
||||||
|
}
|
||||||
82
src/pipeline/project_tree/prefix.rs
Normal file
82
src/pipeline/project_tree/prefix.rs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::ast::{Constant, Rule};
|
||||||
|
use crate::interner::{Token, Interner};
|
||||||
|
use crate::utils::Substack;
|
||||||
|
use crate::representations::sourcefile::{Member, FileEntry};
|
||||||
|
|
||||||
|
use super::collect_ops::ExportedOpsCache;
|
||||||
|
|
||||||
|
fn member_rec(
|
||||||
|
// level
|
||||||
|
mod_stack: Substack<Token<String>>,
|
||||||
|
// object
|
||||||
|
data: Member,
|
||||||
|
// context
|
||||||
|
path: &[Token<String>],
|
||||||
|
ops_cache: &ExportedOpsCache,
|
||||||
|
i: &Interner
|
||||||
|
) -> Member {
|
||||||
|
// let except = |op| imported.contains(&op);
|
||||||
|
let except = |_| false;
|
||||||
|
let prefix_v = path.iter().copied()
|
||||||
|
.chain(mod_stack.iter().rev_vec_clone().into_iter())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let prefix = i.i(&prefix_v);
|
||||||
|
match data {
|
||||||
|
Member::Namespace(name, body) => {
|
||||||
|
let new_body = entv_rec(
|
||||||
|
mod_stack.push(name),
|
||||||
|
body,
|
||||||
|
path, ops_cache, i
|
||||||
|
);
|
||||||
|
Member::Namespace(name, new_body)
|
||||||
|
}
|
||||||
|
Member::Constant(constant) => Member::Constant(Constant{
|
||||||
|
name: constant.name,
|
||||||
|
value: constant.value.prefix(prefix, i, &except)
|
||||||
|
}),
|
||||||
|
Member::Rule(rule) => Member::Rule(Rule{
|
||||||
|
prio: rule.prio,
|
||||||
|
source: Rc::new(rule.source.iter()
|
||||||
|
.map(|e| e.prefix(prefix, i, &except))
|
||||||
|
.collect()
|
||||||
|
),
|
||||||
|
target: Rc::new(rule.target.iter()
|
||||||
|
.map(|e| e.prefix(prefix, i, &except))
|
||||||
|
.collect()
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn entv_rec(
|
||||||
|
// level
|
||||||
|
mod_stack: Substack<Token<String>>,
|
||||||
|
// object
|
||||||
|
data: Vec<FileEntry>,
|
||||||
|
// context
|
||||||
|
path: &[Token<String>],
|
||||||
|
ops_cache: &ExportedOpsCache,
|
||||||
|
i: &Interner
|
||||||
|
) -> Vec<FileEntry> {
|
||||||
|
data.into_iter().map(|fe| match fe {
|
||||||
|
FileEntry::Exported(mem) => FileEntry::Exported(member_rec(
|
||||||
|
mod_stack, mem, path, ops_cache, i
|
||||||
|
)),
|
||||||
|
FileEntry::Internal(mem) => FileEntry::Internal(member_rec(
|
||||||
|
mod_stack, mem, path, ops_cache, i
|
||||||
|
)),
|
||||||
|
// XXX should [FileEntry::Export] be prefixed?
|
||||||
|
any => any
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prefix(
|
||||||
|
data: Vec<FileEntry>,
|
||||||
|
path: &[Token<String>],
|
||||||
|
ops_cache: &ExportedOpsCache,
|
||||||
|
i: &Interner
|
||||||
|
) -> Vec<FileEntry> {
|
||||||
|
entv_rec(Substack::Bottom, data, path, ops_cache, i)
|
||||||
|
}
|
||||||
87
src/pipeline/project_tree/tree.rs
Normal file
87
src/pipeline/project_tree/tree.rs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
use std::{ops::Add, rc::Rc};
|
||||||
|
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
|
||||||
|
use crate::representations::tree::{Module, ModMember};
|
||||||
|
use crate::ast::{Rule, Expr};
|
||||||
|
use crate::interner::{Token, Interner};
|
||||||
|
use crate::utils::Substack;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct ProjectExt{
|
||||||
|
/// Pairs each foreign token to the module it was imported from
|
||||||
|
pub imports_from: HashMap<Token<String>, Token<Vec<Token<String>>>>,
|
||||||
|
/// Pairs each exported token to its original full name.
|
||||||
|
pub exports: HashMap<Token<String>, Token<Vec<Token<String>>>>,
|
||||||
|
/// All rules defined in this module, exported or not
|
||||||
|
pub rules: Vec<Rule>,
|
||||||
|
/// Filename, if known, for error reporting
|
||||||
|
pub file: Option<Vec<Token<String>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for ProjectExt {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(mut self, rhs: Self) -> Self::Output {
|
||||||
|
let ProjectExt{ imports_from, exports, rules, file } = rhs;
|
||||||
|
self.imports_from.extend(imports_from.into_iter());
|
||||||
|
self.exports.extend(exports.into_iter());
|
||||||
|
self.rules.extend(rules.into_iter());
|
||||||
|
if file.is_some() { self.file = file }
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ProjectModule = Module<Expr, ProjectExt>;
|
||||||
|
pub struct ProjectTree(pub Rc<ProjectModule>);
|
||||||
|
|
||||||
|
fn collect_rules_rec(bag: &mut Vec<Rule>, module: &ProjectModule) {
|
||||||
|
bag.extend(module.extra.rules.iter().cloned());
|
||||||
|
for item in module.items.values() {
|
||||||
|
if let ModMember::Sub(module) = &item.member {
|
||||||
|
collect_rules_rec(bag, module.as_ref());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_rules(project: &ProjectTree) -> Vec<Rule> {
|
||||||
|
let mut rules = Vec::new();
|
||||||
|
collect_rules_rec(&mut rules, project.0.as_ref());
|
||||||
|
rules
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_consts_rec(
|
||||||
|
path: Substack<Token<String>>,
|
||||||
|
bag: &mut HashMap<Token<Vec<Token<String>>>, Expr>,
|
||||||
|
module: &ProjectModule,
|
||||||
|
i: &Interner
|
||||||
|
) {
|
||||||
|
for (key, entry) in module.items.iter() {
|
||||||
|
match &entry.member {
|
||||||
|
ModMember::Item(expr) => {
|
||||||
|
let mut name = path.iter().rev_vec_clone();
|
||||||
|
name.push(*key);
|
||||||
|
bag.insert(i.i(&name), expr.clone());
|
||||||
|
}
|
||||||
|
ModMember::Sub(module) => {
|
||||||
|
collect_consts_rec(
|
||||||
|
path.push(*key),
|
||||||
|
bag, module, i
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_consts(project: &ProjectTree, i: &Interner)
|
||||||
|
-> HashMap<Token<Vec<Token<String>>>, Expr>
|
||||||
|
{
|
||||||
|
let mut consts = HashMap::new();
|
||||||
|
collect_consts_rec(
|
||||||
|
Substack::Bottom,
|
||||||
|
&mut consts,
|
||||||
|
project.0.as_ref(),
|
||||||
|
i
|
||||||
|
);
|
||||||
|
consts
|
||||||
|
}
|
||||||
82
src/pipeline/source_loader/load_source.rs
Normal file
82
src/pipeline/source_loader/load_source.rs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
use std::iter;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::pipeline::error::ProjectError;
|
||||||
|
use crate::pipeline::import_abs_path::import_abs_path;
|
||||||
|
use crate::pipeline::split_name::split_name;
|
||||||
|
use crate::interner::{Token, Interner};
|
||||||
|
|
||||||
|
use crate::pipeline::file_loader::{Loaded, load_text, IOResult};
|
||||||
|
use super::loaded_source::{LoadedSourceTable, LoadedSource};
|
||||||
|
use super::preparse::preparse;
|
||||||
|
|
||||||
|
/// Load the source at the given path or all within if it's a collection,
|
||||||
|
/// and all sources imported from these.
|
||||||
|
fn load_abs_path_rec(
|
||||||
|
abs_path: Token<Vec<Token<String>>>,
|
||||||
|
table: &mut LoadedSourceTable,
|
||||||
|
i: &Interner,
|
||||||
|
get_source: &impl Fn(Token<Vec<Token<String>>>) -> IOResult,
|
||||||
|
is_injected: &impl Fn(&[Token<String>]) -> bool
|
||||||
|
) -> Result<(), Rc<dyn ProjectError>> {
|
||||||
|
let abs_pathv = i.r(abs_path);
|
||||||
|
// short-circuit if this import is defined externally or already known
|
||||||
|
if is_injected(&abs_pathv) | table.contains_key(&abs_path) {
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
// 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 name_split = split_name(&abs_pathv, &|p| is_file(i.i(p)));
|
||||||
|
let filename = if let Some((f, _)) = name_split {f} 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, 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 preparsed = preparse(
|
||||||
|
filename.iter().map(|t| i.r(*t)).cloned().collect(),
|
||||||
|
text.as_str(), i
|
||||||
|
)?;
|
||||||
|
table.insert(abs_path, LoadedSource{ text, preparsed: preparsed.clone() });
|
||||||
|
// recurse on all imported modules
|
||||||
|
preparsed.0.visit_all_imports(&mut |modpath, module, import| {
|
||||||
|
let abs_pathv = import_abs_path(
|
||||||
|
&filename, modpath,
|
||||||
|
module, &import.nonglob_path(i), i
|
||||||
|
)?;
|
||||||
|
// recurse on imported module
|
||||||
|
load_abs_path_rec(i.i(&abs_pathv), table, i, get_source, is_injected)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load and preparse all files reachable from the load targets via
|
||||||
|
/// imports that aren't injected.
|
||||||
|
pub fn load_source(
|
||||||
|
targets: &[Token<Vec<Token<String>>>],
|
||||||
|
i: &Interner,
|
||||||
|
get_source: &impl Fn(Token<Vec<Token<String>>>) -> IOResult,
|
||||||
|
is_injected: &impl Fn(&[Token<String>]) -> bool,
|
||||||
|
) -> Result<LoadedSourceTable, Rc<dyn ProjectError>> {
|
||||||
|
let mut table = LoadedSourceTable::new();
|
||||||
|
for target in targets {
|
||||||
|
load_abs_path_rec(
|
||||||
|
*target,
|
||||||
|
&mut table,
|
||||||
|
i, get_source, is_injected
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
Ok(table)
|
||||||
|
}
|
||||||
13
src/pipeline/source_loader/loaded_source.rs
Normal file
13
src/pipeline/source_loader/loaded_source.rs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
use std::{rc::Rc, collections::HashMap};
|
||||||
|
|
||||||
|
use crate::interner::Token;
|
||||||
|
|
||||||
|
use super::preparse::Preparsed;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LoadedSource {
|
||||||
|
pub text: Rc<String>,
|
||||||
|
pub preparsed: Preparsed,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type LoadedSourceTable = HashMap<Token<Vec<Token<String>>>, LoadedSource>;
|
||||||
25
src/pipeline/source_loader/mod.rs
Normal file
25
src/pipeline/source_loader/mod.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/* PULL LOGISTICS BOUNDARY
|
||||||
|
|
||||||
|
Specifying exactly what this module should be doing was an unexpectedly
|
||||||
|
hard challenge. It is intended to encapsulate all pull logistics, but
|
||||||
|
this definition is apparently prone to scope creep.
|
||||||
|
|
||||||
|
Load files, preparse them to obtain a list of imports, follow these.
|
||||||
|
Preparsing also returns the module tree and list of exported synbols
|
||||||
|
for free, which is needed later so the output of preparsing is also
|
||||||
|
attached to the module output.
|
||||||
|
|
||||||
|
The module checks for IO errors, syntax errors, malformed imports and
|
||||||
|
imports from missing files. All other errors must be checked later.
|
||||||
|
|
||||||
|
Injection strategy:
|
||||||
|
see whether names are valid in the injected tree for is_injected
|
||||||
|
*/
|
||||||
|
|
||||||
|
mod load_source;
|
||||||
|
mod loaded_source;
|
||||||
|
mod preparse;
|
||||||
|
|
||||||
|
pub use loaded_source::{LoadedSource, LoadedSourceTable};
|
||||||
|
pub use load_source::load_source;
|
||||||
|
pub use preparse::Preparsed;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user