Stuff and bother.
This commit is contained in:
292
Cargo.lock
generated
292
Cargo.lock
generated
@@ -4,15 +4,15 @@ version = 4
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom 0.2.16",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy 0.7.35",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -28,10 +28,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36f5e3dca4e09a6f340a61a0e9c7b61e030c69fc27bf29d73218f7e5e3b7638f"
|
||||
dependencies = [
|
||||
"concolor",
|
||||
"unicode-width",
|
||||
"unicode-width 0.1.14",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
@@ -44,7 +50,9 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"ariadne",
|
||||
"internment",
|
||||
"itertools",
|
||||
"memmap2",
|
||||
"pretty",
|
||||
"proptest",
|
||||
"proptest-derive",
|
||||
"thiserror",
|
||||
@@ -73,15 +81,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.4"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "concolor"
|
||||
@@ -116,6 +124,12 @@ dependencies = [
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -152,25 +166,14 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.16"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi 0.14.7+wasi-0.2.4",
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -210,26 +213,29 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.16"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
|
||||
checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.176"
|
||||
version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
@@ -239,19 +245,18 @@ checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.13"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.9.8"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7"
|
||||
checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@@ -273,15 +278,15 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.11"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -290,28 +295,39 @@ version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy 0.8.27",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d22152487193190344590e4f30e219cf3fe140d9e7a3fdb683d82aa2c5f4156"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"termcolor",
|
||||
"typed-arena",
|
||||
"unicode-width 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proptest"
|
||||
version = "1.8.0"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bb0be07becd10686a0bb407298fb425360a5c44a663774406340c59a22de4ce"
|
||||
checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bit-vec",
|
||||
"bitflags 2.9.4",
|
||||
"lazy_static",
|
||||
"bitflags 2.10.0",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"rand_chacha",
|
||||
@@ -341,9 +357,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -380,7 +396,7 @@ version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -394,18 +410,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.13"
|
||||
version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.6"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
@@ -413,7 +429,7 @@ version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"bitflags 2.10.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
@@ -446,9 +462,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
version = "2.0.110"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
||||
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -462,12 +478,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.3",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.17"
|
||||
@@ -488,6 +513,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-arena"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
|
||||
|
||||
[[package]]
|
||||
name = "unarray"
|
||||
version = "0.1.4"
|
||||
@@ -496,9 +527,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.19"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
@@ -506,6 +537,12 @@ version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
@@ -521,21 +558,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.7+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c"
|
||||
dependencies = [
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.1+wasi-0.2.4"
|
||||
@@ -545,6 +567,15 @@ dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
@@ -557,16 +588,7 @@ version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -584,29 +606,13 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
"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]]
|
||||
@@ -615,90 +621,42 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.46.0"
|
||||
@@ -711,33 +669,13 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"zerocopy-derive 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
|
||||
dependencies = [
|
||||
"zerocopy-derive 0.8.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -6,7 +6,12 @@ edition = "2024"
|
||||
[dependencies]
|
||||
ariadne = { version = "0.5.1", features = ["auto-color"] }
|
||||
internment = { version = "0.8.6", features = ["arc", "arena"] }
|
||||
itertools = "0.14.0"
|
||||
memmap2 = "0.9.8"
|
||||
pretty = { version = "0.12.5", features = ["termcolor"] }
|
||||
proptest = "1.7.0"
|
||||
proptest-derive = "0.6.0"
|
||||
thiserror = "2.0.12"
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage)'] }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
mod arbitrary;
|
||||
mod ast;
|
||||
mod error;
|
||||
mod location;
|
||||
@@ -5,6 +6,7 @@ mod name;
|
||||
mod parse;
|
||||
#[cfg(test)]
|
||||
mod parser_tests;
|
||||
mod print;
|
||||
mod tokens;
|
||||
mod universe;
|
||||
|
||||
|
||||
548
src/syntax/arbitrary.rs
Normal file
548
src/syntax/arbitrary.rs
Normal file
@@ -0,0 +1,548 @@
|
||||
use std::fmt::Arguments;
|
||||
|
||||
use crate::syntax::ast::{ConstantValue, IntegerWithBase, Type};
|
||||
use crate::syntax::location::Location;
|
||||
use crate::syntax::name::Name;
|
||||
use itertools::Itertools;
|
||||
use proptest::arbitrary::Arbitrary;
|
||||
use proptest::prelude::{BoxedStrategy, Rng};
|
||||
use proptest::prop_oneof;
|
||||
use proptest::strategy::{NewTree, Strategy, ValueTree};
|
||||
use proptest::test_runner::TestRunner;
|
||||
|
||||
const MAXIMUM_TYPE_DEPTH: usize = 5;
|
||||
const MAXIMUM_TYPE_WIDTH: usize = 5;
|
||||
const MAXIMUM_STRING_SIZE: usize = 32;
|
||||
const PRIMITIVE_TYPES: &[&str] = &[
|
||||
"Char", "String", "I8", "I16", "I32", "I64", "U8", "U16", "U32", "U64",
|
||||
];
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TypeGenerationContext {
|
||||
available_constructors: Vec<Name>,
|
||||
available_variables: Vec<Name>,
|
||||
}
|
||||
|
||||
impl TypeGenerationContext {
|
||||
fn generate_type(&mut self, runner: &mut TestRunner, depth: usize) -> Type {
|
||||
let mut leaf_options = vec![];
|
||||
|
||||
if !self.available_constructors.is_empty() {
|
||||
for name in self.available_constructors.iter() {
|
||||
leaf_options.push(Type::Constructor(
|
||||
Location::manufactured(),
|
||||
name.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if !self.available_variables.is_empty() {
|
||||
for name in self.available_variables.iter() {
|
||||
leaf_options.push(Type::Variable(
|
||||
Location::manufactured(),
|
||||
name.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
for prim in PRIMITIVE_TYPES.iter() {
|
||||
leaf_options.push(Type::Primitive(
|
||||
Location::manufactured(),
|
||||
Name::new(Location::manufactured(), prim.to_string()),
|
||||
));
|
||||
}
|
||||
|
||||
if depth < MAXIMUM_TYPE_DEPTH && runner.rng().random_bool(0.5) {
|
||||
}
|
||||
|
||||
let index = runner.rng().random_range(0..leaf_options.len());
|
||||
leaf_options.remove(index)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TypeGenerationTree {
|
||||
current_value: Type,
|
||||
parent: Option<Box<TypeGenerationTree>>,
|
||||
untried_simplified_items: Option<Vec<TypeGenerationTree>>,
|
||||
}
|
||||
|
||||
impl TypeGenerationTree {
|
||||
/// Create a new type generation tree based on the given
|
||||
/// initial value.
|
||||
pub fn new(initial_value: Type) -> TypeGenerationTree {
|
||||
TypeGenerationTree {
|
||||
current_value: initial_value,
|
||||
parent: None,
|
||||
untried_simplified_items: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_powerset(_: &[Type]) -> Vec<Vec<Type>> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn simplify_type(incoming: &Type) -> Vec<Type> {
|
||||
match incoming {
|
||||
Type::Primitive(_, _) => vec![],
|
||||
Type::Constructor(_, _) => vec![],
|
||||
Type::Variable(_, _) => vec![],
|
||||
Type::Function(arg_types, ret_type) => {
|
||||
let simplified_return_types = simplify_type(ret_type.as_ref());
|
||||
|
||||
// we do the following as a set of steps, choosing to go deep rather than
|
||||
// broad immediately. So this works as follows:
|
||||
//
|
||||
// 1. If there are simplifications for the return type, then just
|
||||
// return variations with the simplified return type.
|
||||
// 2. If there are simplifications for the first argument, then
|
||||
// just return variations with the first argument simplified.
|
||||
// 3. Repeat for each of the arguments.
|
||||
// 4. At this point, all the subtypes are as simple as they can
|
||||
// be, so return a series of function types with fewer arguments.
|
||||
// 5. If we are a function with no arguments, then just return
|
||||
// the return type.
|
||||
if !simplified_return_types.is_empty() {
|
||||
return simplified_return_types
|
||||
.into_iter()
|
||||
.map(|ret| Type::Function(arg_types.clone(), Box::new(ret)))
|
||||
.collect();
|
||||
}
|
||||
|
||||
// now check the arguments, and see if we can simplify them in a
|
||||
// better way.
|
||||
for idx in 0..arg_types.len() {
|
||||
let simplified_arguments = simplify_type(&arg_types[idx]);
|
||||
|
||||
if simplified_arguments.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut new_function_types = vec![];
|
||||
|
||||
for simplified_arg in simplified_arguments.into_iter() {
|
||||
let mut new_args = vec![];
|
||||
|
||||
for item in &arg_types[0..idx] {
|
||||
new_args.push(item.clone());
|
||||
}
|
||||
new_args.push(simplified_arg);
|
||||
for item in &arg_types[idx + 1..arg_types.len()] {
|
||||
new_args.push(item.clone());
|
||||
}
|
||||
|
||||
new_function_types.push(Type::Function(new_args, ret_type.clone()));
|
||||
}
|
||||
|
||||
if !new_function_types.is_empty() {
|
||||
return new_function_types;
|
||||
}
|
||||
}
|
||||
|
||||
// ok, all of the arguments and the return type are already as
|
||||
// simple as they can be, so let's see if we can reduce the number
|
||||
// of arguments.
|
||||
let mut new_types = vec![];
|
||||
for args in arg_types.iter().powerset() {
|
||||
if args.len() != arg_types.len() {
|
||||
new_types.push(Type::Function(
|
||||
args.into_iter().cloned().collect(),
|
||||
ret_type.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if new_types.is_empty() {
|
||||
vec![ret_type.as_ref().clone()]
|
||||
} else {
|
||||
new_types
|
||||
}
|
||||
}
|
||||
|
||||
Type::Application(constructor_type, arg_types) => {
|
||||
// much like functions, we're going to try to simplify the constructor,
|
||||
// then we'll try to simplify the arguments, then we'll try to remove
|
||||
// arguments.
|
||||
let simplified_constructor = simplify_type(constructor_type.as_ref());
|
||||
|
||||
if !simplified_constructor.is_empty() {
|
||||
return simplified_constructor
|
||||
.into_iter()
|
||||
.map(|c| Type::Application(Box::new(c), arg_types.clone()))
|
||||
.collect();
|
||||
}
|
||||
|
||||
// now check the arguments, and see if we can simplify them in a
|
||||
// better way.
|
||||
for idx in 0..arg_types.len() {
|
||||
let simplified_arguments = simplify_type(&arg_types[idx]);
|
||||
|
||||
if simplified_arguments.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut new_appl_types = vec![];
|
||||
|
||||
for simplified_arg in simplified_arguments.into_iter() {
|
||||
let mut new_args = vec![];
|
||||
|
||||
for item in &arg_types[0..idx] {
|
||||
new_args.push(item.clone());
|
||||
}
|
||||
new_args.push(simplified_arg);
|
||||
for item in &arg_types[idx + 1..arg_types.len()] {
|
||||
new_args.push(item.clone());
|
||||
}
|
||||
|
||||
new_appl_types.push(Type::Application(constructor_type.clone(), new_args));
|
||||
}
|
||||
|
||||
if !new_appl_types.is_empty() {
|
||||
return new_appl_types;
|
||||
}
|
||||
}
|
||||
|
||||
// and now we'll try to reduce types.
|
||||
let mut new_types = vec![];
|
||||
for args in arg_types.iter().powerset() {
|
||||
if args.len() != arg_types.len() {
|
||||
new_types.push(Type::Application(
|
||||
constructor_type.clone(),
|
||||
args.into_iter().cloned().collect(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if new_types.is_empty() {
|
||||
vec![constructor_type.as_ref().clone()]
|
||||
} else {
|
||||
new_types
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueTree for TypeGenerationTree {
|
||||
type Value = Type;
|
||||
|
||||
fn current(&self) -> Self::Value {
|
||||
self.current_value.clone()
|
||||
}
|
||||
|
||||
fn simplify(&mut self) -> bool {
|
||||
match self.untried_simplified_items.as_mut() {
|
||||
None => {
|
||||
let mut simplified = simplify_type(&self.current_value)
|
||||
.into_iter()
|
||||
.map(|current_value| TypeGenerationTree {
|
||||
current_value,
|
||||
parent: Some(Box::new(self.clone())),
|
||||
|
||||
untried_simplified_items: None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
match simplified.pop() {
|
||||
None => {
|
||||
self.untried_simplified_items = Some(simplified);
|
||||
false
|
||||
}
|
||||
|
||||
Some(next_tree) => {
|
||||
self.untried_simplified_items = Some(simplified);
|
||||
*self = next_tree;
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(untried_simplifieds) => match untried_simplifieds.pop() {
|
||||
None => false,
|
||||
Some(x) => {
|
||||
*self = x;
|
||||
true
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn complicate(&mut self) -> bool {
|
||||
match self.parent.take() {
|
||||
None => false,
|
||||
Some(x) => {
|
||||
*self = *x;
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Strategy for TypeGenerationContext {
|
||||
type Tree = TypeGenerationTree;
|
||||
type Value = Type;
|
||||
|
||||
fn new_tree(&self, _runner: &mut TestRunner) -> NewTree<Self> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Arbitrary for Type {
|
||||
type Parameters = TypeGenerationContext;
|
||||
type Strategy = TypeGenerationContext;
|
||||
|
||||
fn arbitrary_with(_context: Self::Parameters) -> Self::Strategy {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub enum LegalConstantType {
|
||||
#[default]
|
||||
Any,
|
||||
String,
|
||||
Char,
|
||||
Number,
|
||||
}
|
||||
|
||||
impl Arbitrary for ConstantValue {
|
||||
type Parameters = LegalConstantType;
|
||||
type Strategy = BoxedStrategy<ConstantValue>;
|
||||
|
||||
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
|
||||
match args {
|
||||
LegalConstantType::Char => char::arbitrary()
|
||||
.prop_map(|x| ConstantValue::Character(Location::manufactured(), x))
|
||||
.boxed(),
|
||||
|
||||
LegalConstantType::String => {
|
||||
proptest::collection::vec(proptest::char::any(), MAXIMUM_STRING_SIZE)
|
||||
.prop_map(|x| {
|
||||
ConstantValue::String(Location::manufactured(), String::from_iter(x))
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
LegalConstantType::Number => {
|
||||
let value_strat = u64::arbitrary();
|
||||
let base_strat = proptest::prop_oneof![
|
||||
proptest::strategy::Just(None),
|
||||
proptest::strategy::Just(Some(2)),
|
||||
proptest::strategy::Just(Some(8)),
|
||||
proptest::strategy::Just(Some(10)),
|
||||
proptest::strategy::Just(Some(16)),
|
||||
];
|
||||
|
||||
(value_strat, base_strat)
|
||||
.prop_map(|(value, base)| {
|
||||
ConstantValue::Integer(
|
||||
Location::manufactured(),
|
||||
IntegerWithBase { base, value },
|
||||
)
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
||||
LegalConstantType::Any => proptest::prop_oneof![
|
||||
Self::arbitrary_with(LegalConstantType::Char),
|
||||
Self::arbitrary_with(LegalConstantType::String),
|
||||
Self::arbitrary_with(LegalConstantType::Number),
|
||||
]
|
||||
.boxed(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod simplifiers {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn types() {
|
||||
let loc = Location::manufactured();
|
||||
let foo = Name::new(loc.clone(), "Foo");
|
||||
let primint = Type::Primitive(loc.clone(), Name::new(loc.clone(), "Int"));
|
||||
let primchar = Type::Primitive(loc.clone(), Name::new(loc.clone(), "Char"));
|
||||
let primstr = Type::Primitive(loc.clone(), Name::new(loc.clone(), "String"));
|
||||
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Constructor(loc.clone(), foo.clone())),
|
||||
vec![]
|
||||
);
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Variable(loc.clone(), foo.clone())),
|
||||
vec![]
|
||||
);
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Primitive(loc.clone(), foo.clone())),
|
||||
vec![]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Function(vec![], Box::new(primint.clone()))),
|
||||
vec![primint.clone()]
|
||||
);
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Function(
|
||||
vec![primint.clone(), primchar.clone()],
|
||||
Box::new(primint.clone())
|
||||
)),
|
||||
vec![
|
||||
Type::Function(vec![], Box::new(primint.clone())),
|
||||
Type::Function(vec![primint.clone()], Box::new(primint.clone())),
|
||||
Type::Function(vec![primchar.clone()], Box::new(primint.clone())),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Function(
|
||||
vec![primint.clone(), primchar.clone(), primstr.clone()],
|
||||
Box::new(primint.clone())
|
||||
)),
|
||||
vec![
|
||||
Type::Function(vec![], Box::new(primint.clone())),
|
||||
Type::Function(vec![primint.clone()], Box::new(primint.clone())),
|
||||
Type::Function(vec![primchar.clone()], Box::new(primint.clone())),
|
||||
Type::Function(vec![primstr.clone()], Box::new(primint.clone())),
|
||||
Type::Function(
|
||||
vec![primint.clone(), primchar.clone()],
|
||||
Box::new(primint.clone())
|
||||
),
|
||||
Type::Function(
|
||||
vec![primint.clone(), primstr.clone()],
|
||||
Box::new(primint.clone())
|
||||
),
|
||||
Type::Function(
|
||||
vec![primchar.clone(), primstr.clone()],
|
||||
Box::new(primint.clone())
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Function(
|
||||
vec![primint.clone(), primchar.clone(), primstr.clone()],
|
||||
Box::new(Type::Function(vec![], Box::new(primint.clone()))),
|
||||
)),
|
||||
vec![Type::Function(
|
||||
vec![primint.clone(), primchar.clone(), primstr.clone()],
|
||||
Box::new(primint.clone())
|
||||
),]
|
||||
);
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Function(
|
||||
vec![primint.clone(), primchar.clone(), primstr.clone()],
|
||||
Box::new(Type::Function(
|
||||
vec![primint.clone(), primchar.clone()],
|
||||
Box::new(primint.clone())
|
||||
)),
|
||||
)),
|
||||
vec![
|
||||
Type::Function(
|
||||
vec![primint.clone(), primchar.clone(), primstr.clone()],
|
||||
Box::new(Type::Function(vec![], Box::new(primint.clone())))
|
||||
),
|
||||
Type::Function(
|
||||
vec![primint.clone(), primchar.clone(), primstr.clone()],
|
||||
Box::new(Type::Function(
|
||||
vec![primint.clone()],
|
||||
Box::new(primint.clone())
|
||||
))
|
||||
),
|
||||
Type::Function(
|
||||
vec![primint.clone(), primchar.clone(), primstr.clone()],
|
||||
Box::new(Type::Function(
|
||||
vec![primchar.clone()],
|
||||
Box::new(primint.clone())
|
||||
))
|
||||
),
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Function(
|
||||
vec![
|
||||
Type::Function(vec![], Box::new(primint.clone())),
|
||||
primstr.clone()
|
||||
],
|
||||
Box::new(primint.clone())
|
||||
)),
|
||||
vec![Type::Function(
|
||||
vec![primint.clone(), primstr.clone()],
|
||||
Box::new(primint.clone())
|
||||
)]
|
||||
);
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Function(
|
||||
vec![
|
||||
primint.clone(),
|
||||
Type::Function(vec![], Box::new(primint.clone()))
|
||||
],
|
||||
Box::new(primint.clone())
|
||||
)),
|
||||
vec![Type::Function(
|
||||
vec![primint.clone(), primint.clone()],
|
||||
Box::new(primint.clone())
|
||||
)]
|
||||
);
|
||||
|
||||
let applied = Type::Application(Box::new(primint.clone()), vec![]);
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Application(Box::new(primint.clone()), vec![])),
|
||||
vec![primint.clone()]
|
||||
);
|
||||
assert_eq!(simplify_type(&applied), vec![primint.clone()]);
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Application(
|
||||
Box::new(applied.clone()),
|
||||
vec![primint.clone()]
|
||||
)),
|
||||
vec![Type::Application(
|
||||
Box::new(primint.clone()),
|
||||
vec![primint.clone()]
|
||||
)]
|
||||
);
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Application(
|
||||
Box::new(primint.clone()),
|
||||
vec![applied.clone()]
|
||||
)),
|
||||
vec![Type::Application(
|
||||
Box::new(primint.clone()),
|
||||
vec![primint.clone()]
|
||||
)]
|
||||
);
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Application(
|
||||
Box::new(primint.clone()),
|
||||
vec![primchar.clone(), applied.clone(), primstr.clone()]
|
||||
)),
|
||||
vec![Type::Application(
|
||||
Box::new(primint.clone()),
|
||||
vec![primchar.clone(), primint.clone(), primstr.clone()]
|
||||
)]
|
||||
);
|
||||
assert_eq!(
|
||||
simplify_type(&Type::Application(
|
||||
Box::new(primint.clone()),
|
||||
vec![primchar.clone(), primint.clone(), primstr.clone()]
|
||||
)),
|
||||
vec![
|
||||
Type::Application(Box::new(primint.clone()), vec![]),
|
||||
Type::Application(Box::new(primint.clone()), vec![primchar.clone()]),
|
||||
Type::Application(Box::new(primint.clone()), vec![primint.clone()]),
|
||||
Type::Application(Box::new(primint.clone()), vec![primstr.clone()]),
|
||||
Type::Application(
|
||||
Box::new(primint.clone()),
|
||||
vec![primchar.clone(), primint.clone()]
|
||||
),
|
||||
Type::Application(
|
||||
Box::new(primint.clone()),
|
||||
vec![primchar.clone(), primstr.clone()]
|
||||
),
|
||||
Type::Application(
|
||||
Box::new(primint.clone()),
|
||||
vec![primint.clone(), primstr.clone()]
|
||||
)
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -244,7 +244,7 @@ pub struct TypeRestriction {
|
||||
pub arguments: Vec<Type>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Type {
|
||||
Constructor(Location, Name),
|
||||
Variable(Location, Name),
|
||||
@@ -253,6 +253,22 @@ pub enum Type {
|
||||
Function(Vec<Type>, Box<Type>),
|
||||
}
|
||||
|
||||
impl PartialEq for Type {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match self {
|
||||
Type::Constructor(_, x) => matches!(other, Type::Constructor(_, y) if x == y),
|
||||
Type::Variable(_, x) => matches!(other, Type::Variable(_, y) if x == y),
|
||||
Type::Primitive(_, x) => matches!(other, Type::Primitive(_, y) if x == y),
|
||||
Type::Application(con1, args1) => {
|
||||
matches!(other, Type::Application(con2, args2) if con1 == con2 && args1 == args2)
|
||||
}
|
||||
Type::Function(args1, ret1) => {
|
||||
matches!(other, Type::Function(args2, ret2) if args1 == args2 && ret1 == ret2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Located for Type {
|
||||
fn location(&self) -> Location {
|
||||
match self {
|
||||
@@ -277,7 +293,7 @@ impl Located for Type {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ConstantValue {
|
||||
Integer(Location, IntegerWithBase),
|
||||
Character(Location, char),
|
||||
@@ -294,6 +310,18 @@ impl Located for ConstantValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ConstantValue {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match self {
|
||||
ConstantValue::Character(_, x) => {
|
||||
matches!(other, ConstantValue::Character(_, y) if x == y)
|
||||
}
|
||||
ConstantValue::String(_, x) => matches!(other, ConstantValue::String(_, y) if x == y),
|
||||
ConstantValue::Integer(_, x) => matches!(other, ConstantValue::Integer(_, y) if x == y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Arbitrary)]
|
||||
pub struct IntegerWithBase {
|
||||
#[proptest(strategy = "proptest::prop_oneof![ \
|
||||
|
||||
@@ -38,6 +38,13 @@ impl Location {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn manufactured() -> Self {
|
||||
Location {
|
||||
file: ArcIntern::new("<manufactured>".into()),
|
||||
span: 0..0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extend_to(&self, other: &Location) -> Location {
|
||||
assert_eq!(self.file, other.file);
|
||||
Location {
|
||||
@@ -50,12 +57,23 @@ impl Location {
|
||||
self.span = min(self.span.start, span.start)..max(self.span.end, span.end);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn file(&self) -> &str {
|
||||
self.file.to_str().unwrap_or("<bad_name>")
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Range<usize> {
|
||||
self.span.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extension_and_merge() {
|
||||
let file = ArcIntern::new("/foo/bar.txt".into());
|
||||
let loc1 = Location::new(&file, 1..4);
|
||||
let loc2 = Location::new(&file, 4..8);
|
||||
|
||||
assert_eq!(loc1.extend_to(&loc2).source(), &file);
|
||||
assert_eq!(loc1.extend_to(&loc2).start(), 1);
|
||||
assert_eq!(loc1.extend_to(&loc2).end(), 8);
|
||||
|
||||
let loc3 = Location::new(&file, 12..16);
|
||||
assert_eq!(loc1.extend_to(&loc3).source(), &file);
|
||||
assert_eq!(loc1.extend_to(&loc3).start(), 1);
|
||||
assert_eq!(loc1.extend_to(&loc3).end(), 16);
|
||||
|
||||
assert_eq!(loc1.clone().merge_span(0..1).start(), 0);
|
||||
assert_eq!(loc1.merge_span(0..1).end(), 4);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use crate::syntax::Location;
|
||||
#[cfg(test)]
|
||||
use internment::ArcIntern;
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
use std::hash;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
static IDENTIFIER_COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Name {
|
||||
printable: String,
|
||||
identifier: u64,
|
||||
@@ -21,8 +23,8 @@ impl cmp::PartialEq for Name {
|
||||
|
||||
impl cmp::Eq for Name {}
|
||||
|
||||
impl hash::Hash for Name {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||
impl Hash for Name {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.identifier.hash(state);
|
||||
}
|
||||
}
|
||||
@@ -66,3 +68,89 @@ impl Name {
|
||||
self.location.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equality() {
|
||||
let file = ArcIntern::new("/foo.bang".into());
|
||||
let loc1 = Location::new(&file, 0..3);
|
||||
let loc2 = Location::new(&file, 9..12);
|
||||
|
||||
assert_ne!(Name::gensym("x"), Name::gensym("x"));
|
||||
assert_ne!(Name::new(loc1.clone(), "x"), Name::new(loc1.clone(), "x"));
|
||||
assert_eq!(
|
||||
Name {
|
||||
printable: "x".into(),
|
||||
identifier: 5,
|
||||
location: Some(loc1.clone())
|
||||
},
|
||||
Name {
|
||||
printable: "x".into(),
|
||||
identifier: 5,
|
||||
location: Some(loc2.clone())
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Name {
|
||||
printable: "x".into(),
|
||||
identifier: 5,
|
||||
location: Some(loc1.clone())
|
||||
},
|
||||
Name {
|
||||
printable: "x".into(),
|
||||
identifier: 5,
|
||||
location: None
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Name {
|
||||
printable: "x".into(),
|
||||
identifier: 5,
|
||||
location: Some(loc1.clone())
|
||||
},
|
||||
Name {
|
||||
printable: "y".into(),
|
||||
identifier: 5,
|
||||
location: None
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hashing() {
|
||||
let file = ArcIntern::new("/foo.bang".into());
|
||||
let loc1 = Location::new(&file, 0..3);
|
||||
let loc2 = Location::new(&file, 9..12);
|
||||
|
||||
let x1 = Name {
|
||||
printable: "x".into(),
|
||||
identifier: 1,
|
||||
location: Some(loc1),
|
||||
};
|
||||
let mut x2 = Name {
|
||||
printable: "x".into(),
|
||||
identifier: 2,
|
||||
location: Some(loc2),
|
||||
};
|
||||
let y1 = Name {
|
||||
printable: "y".into(),
|
||||
identifier: 1,
|
||||
location: None,
|
||||
};
|
||||
|
||||
let run_hash = |name: &Name| {
|
||||
let mut hash = std::hash::DefaultHasher::new();
|
||||
name.hash(&mut hash);
|
||||
hash.finish()
|
||||
};
|
||||
|
||||
let hash_x1 = run_hash(&x1);
|
||||
let hash_x2 = run_hash(&x2);
|
||||
let hash_y1 = run_hash(&y1);
|
||||
|
||||
assert_ne!(hash_x1, hash_x2);
|
||||
assert_eq!(hash_x1, hash_y1);
|
||||
|
||||
x2.bind_to(&x1);
|
||||
let rehashed_x2 = run_hash(&x2);
|
||||
assert_eq!(hash_x1, rehashed_x2);
|
||||
}
|
||||
|
||||
@@ -261,9 +261,13 @@ impl<'lexer> Parser<'lexer> {
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[cfg(not(coverage))]
|
||||
fn print_next_token(&mut self, comment: &str) {
|
||||
let token = self.next().expect("can get token");
|
||||
println!("[{comment}] next token will be {:?}", token.as_ref().map(|x| x.token.clone()));
|
||||
println!(
|
||||
"[{comment}] next token will be {:?}",
|
||||
token.as_ref().map(|x| x.token.clone())
|
||||
);
|
||||
if let Some(token) = token {
|
||||
self.save(token);
|
||||
}
|
||||
@@ -379,14 +383,7 @@ impl<'lexer> Parser<'lexer> {
|
||||
arguments,
|
||||
};
|
||||
|
||||
let Some(maybe_comma) = self.next()? else {
|
||||
return Ok(Some(restriction));
|
||||
};
|
||||
|
||||
match maybe_comma.token {
|
||||
Token::Comma => {}
|
||||
_ => self.save(maybe_comma),
|
||||
}
|
||||
let _ = self.require_token(Token::Comma, "");
|
||||
|
||||
Ok(Some(restriction))
|
||||
}
|
||||
@@ -402,30 +399,37 @@ impl<'lexer> Parser<'lexer> {
|
||||
let next = self
|
||||
.next()?
|
||||
.ok_or_else(|| self.bad_eof("looking for definition body"))?;
|
||||
self.save(next.clone());
|
||||
|
||||
if let Ok(structure) = self.parse_structure() {
|
||||
return Ok(Def::Structure(structure));
|
||||
match next.token {
|
||||
Token::ValueName(ref x) if x == "structure" => {
|
||||
self.save(next);
|
||||
Ok(Def::Structure(self.parse_structure()?))
|
||||
}
|
||||
|
||||
if let Ok(enumeration) = self.parse_enumeration() {
|
||||
return Ok(Def::Enumeration(enumeration));
|
||||
Token::ValueName(ref x) if x == "enumeration" => {
|
||||
self.save(next);
|
||||
Ok(Def::Enumeration(self.parse_enumeration()?))
|
||||
}
|
||||
|
||||
if let Ok(operator) = self.parse_operator() {
|
||||
return Ok(Def::Operator(operator));
|
||||
Token::ValueName(ref x)
|
||||
if x == "operator" || x == "prefix" || x == "infix" || x == "postfix" =>
|
||||
{
|
||||
self.save(next);
|
||||
Ok(Def::Operator(self.parse_operator()?))
|
||||
}
|
||||
|
||||
if let Ok(fun_or_val) = self.parse_function_or_value() {
|
||||
return Ok(fun_or_val);
|
||||
Token::ValueName(_) => {
|
||||
self.save(next);
|
||||
self.parse_function_or_value()
|
||||
}
|
||||
|
||||
Err(ParserError::UnexpectedToken {
|
||||
_ => Err(ParserError::UnexpectedToken {
|
||||
file: self.file.clone(),
|
||||
span: next.span,
|
||||
token: next.token,
|
||||
expected: "'structure', 'enumeration', or a value identifier".into(),
|
||||
})
|
||||
expected: "'structure', 'enumeration', 'operator', or a value identifier".into(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a structure definition.
|
||||
@@ -821,6 +825,7 @@ impl<'lexer> Parser<'lexer> {
|
||||
fn parse_function_def_arguments(&mut self) -> Result<Vec<FunctionArg>, ParserError> {
|
||||
let _ = self.require_token(Token::OpenParen, "start of function argument definition")?;
|
||||
let mut result = vec![];
|
||||
let mut just_skipped_comma = false;
|
||||
|
||||
loop {
|
||||
let next = self
|
||||
@@ -831,7 +836,22 @@ impl<'lexer> Parser<'lexer> {
|
||||
break;
|
||||
}
|
||||
|
||||
if matches!(next.token, Token::Comma) {
|
||||
if just_skipped_comma {
|
||||
return Err(ParserError::UnexpectedToken {
|
||||
file: self.file.clone(),
|
||||
span: next.span,
|
||||
token: next.token,
|
||||
expected: "after another comma in function arguments".into(),
|
||||
});
|
||||
}
|
||||
|
||||
just_skipped_comma = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
self.save(next);
|
||||
just_skipped_comma = false;
|
||||
let name = self.parse_name("function argument name")?;
|
||||
let mut arg_type = None;
|
||||
|
||||
@@ -992,6 +1012,7 @@ impl<'lexer> Parser<'lexer> {
|
||||
|
||||
Some(Box::new(sub_pattern))
|
||||
} else {
|
||||
self.save(maybe_paren);
|
||||
None
|
||||
}
|
||||
} else {
|
||||
@@ -1410,9 +1431,12 @@ impl<'lexer> Parser<'lexer> {
|
||||
|
||||
Token::TypeName(n) | Token::PrimitiveTypeName(n) => {
|
||||
let type_name = Name::new(self.to_location(next.span.clone()), n);
|
||||
let after_type_name = self.next()?.ok_or_else(|| {
|
||||
self.bad_eof("looking for colon, open brace, or open paren in constructor")
|
||||
})?;
|
||||
let Some(after_type_name) = self.next()? else {
|
||||
return Ok(Expression::Reference(
|
||||
type_name.location().unwrap().clone(),
|
||||
type_name,
|
||||
));
|
||||
};
|
||||
|
||||
match after_type_name.token {
|
||||
Token::OpenBrace => {
|
||||
@@ -1480,12 +1504,13 @@ impl<'lexer> Parser<'lexer> {
|
||||
Ok(Expression::Enumeration(ev))
|
||||
}
|
||||
|
||||
_ => Err(ParserError::UnexpectedToken {
|
||||
file: self.file.clone(),
|
||||
span: after_type_name.span,
|
||||
token: after_type_name.token,
|
||||
expected: "colon, open brace, or open paren in constructor".into(),
|
||||
}),
|
||||
_ => {
|
||||
self.save(after_type_name);
|
||||
Ok(Expression::Reference(
|
||||
type_name.location().unwrap().clone(),
|
||||
type_name,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,9 @@ fn types() {
|
||||
if b1.as_printed() == "a" && b2.as_printed() == "b")) &&
|
||||
matches!(z.as_ref(), Type::Variable(_, z1) if z1.as_printed() == "z")
|
||||
));
|
||||
assert!(parse_type("Cons a b ->").is_err());
|
||||
assert!(parse_type("(Cons a b) (Cons a b)").is_err());
|
||||
assert!(parse_type("(Cons a b) (Cons a b) :").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -129,6 +132,17 @@ fn type_restrictions() {
|
||||
Ok(TypeRestrictions{ restrictions }) if restrictions.is_empty()
|
||||
));
|
||||
|
||||
assert!(matches!(
|
||||
parse_tr("restrict(prim%Cons a b)"),
|
||||
Ok(TypeRestrictions { restrictions }) if restrictions.len() == 1 &&
|
||||
matches!(&restrictions[0], TypeRestriction {
|
||||
constructor,
|
||||
arguments,
|
||||
} if matches!(constructor, Type::Primitive(_, x) if x.as_printed() == "Cons") &&
|
||||
arguments.len() == 2 &&
|
||||
matches!(&arguments[0], Type::Variable(_, x) if x.as_printed() == "a") &&
|
||||
matches!(&arguments[1], Type::Variable(_, x) if x.as_printed() == "b"))));
|
||||
|
||||
assert!(matches!(
|
||||
parse_tr("restrict(Cons a b)"),
|
||||
Ok(TypeRestrictions { restrictions }) if restrictions.len() == 1 &&
|
||||
@@ -151,6 +165,8 @@ fn type_restrictions() {
|
||||
matches!(&arguments[0], Type::Variable(_, x) if x.as_printed() == "a") &&
|
||||
matches!(&arguments[1], Type::Variable(_, x) if x.as_printed() == "b"))));
|
||||
|
||||
assert!(parse_tr("restrict(cons a b,)").is_err());
|
||||
|
||||
assert!(parse_tr("restrict(,Cons a b,)").is_err());
|
||||
|
||||
assert!(matches!(
|
||||
@@ -216,6 +232,9 @@ fn field_definition() {
|
||||
if matches!(c.as_ref(), Type::Constructor(_, c) if c.as_printed() == "Word8") &&
|
||||
args.is_empty())));
|
||||
|
||||
assert!(parse_fd("foo :: Word8,").is_err());
|
||||
assert!(parse_fd("foo: Word8;").is_err());
|
||||
|
||||
assert!(matches!(
|
||||
parse_fd("foo: Cons a b,"),
|
||||
Ok(Some(StructureField{ name, field_type, .. }))
|
||||
@@ -400,6 +419,8 @@ fn enumerations() {
|
||||
EnumerationVariant { name: name2, argument: arg2, ..},
|
||||
] if name1.as_printed() == "A" && arg1.is_none() &&
|
||||
name2.as_printed() == "B" && arg2.is_none())));
|
||||
assert!(parse_en("enumeration Alternates { A").is_err());
|
||||
assert!(parse_en("enumeration Alternates { A; B }").is_err());
|
||||
assert!(matches!(
|
||||
parse_en("enumeration Alternates { A, B, }"),
|
||||
Ok(EnumerationDef { name, variants, .. })
|
||||
@@ -423,6 +444,9 @@ fn expressions() {
|
||||
assert!(matches!(
|
||||
parse_ex("x"),
|
||||
Ok(Expression::Reference(_,n)) if n.as_printed() == "x"));
|
||||
assert!(matches!(
|
||||
parse_ex("X"),
|
||||
Ok(Expression::Reference(_,n)) if n.as_printed() == "X"));
|
||||
assert!(matches!(
|
||||
parse_ex("(x)"),
|
||||
Ok(Expression::Reference(_,n)) if n.as_printed() == "x"));
|
||||
@@ -453,6 +477,7 @@ fn enumeration_values() {
|
||||
};
|
||||
|
||||
assert!(parse_ex("Hello::world").is_err());
|
||||
assert!(parse_ex("Hello::world(a,b)").is_err());
|
||||
assert!(matches!(
|
||||
parse_ex("Hello::World"),
|
||||
Ok(Expression::Enumeration(ev))
|
||||
@@ -465,6 +490,14 @@ fn enumeration_values() {
|
||||
if ev.type_name.as_printed() == "Hello" &&
|
||||
ev.variant_name.as_printed() == "World" &&
|
||||
ev.argument.is_some()));
|
||||
assert!(matches!(
|
||||
parse_ex("Hello::World + 1"),
|
||||
Ok(Expression::Call(plus, CallKind::Infix, args)) if
|
||||
matches!(plus.as_ref(), Expression::Reference(_, n) if n.as_printed() == "+") &&
|
||||
matches!(args.as_slice(), [
|
||||
Expression::Enumeration(_),
|
||||
Expression::Value(_)
|
||||
])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -475,6 +508,7 @@ fn structure_value() {
|
||||
result.parse_expression()
|
||||
};
|
||||
|
||||
assert!(parse_st("Foo{").is_err());
|
||||
assert!(parse_st("Foo{ , }").is_err());
|
||||
assert!(parse_st("Foo{ foo, }").is_err());
|
||||
assert!(parse_st("Foo{ foo: , }").is_err());
|
||||
@@ -635,6 +669,11 @@ fn calls() {
|
||||
Ok(Expression::Call(f, CallKind::Normal, args)) if
|
||||
matches!(f.as_ref(), Expression::Reference(_,n) if n.as_printed() == "f") &&
|
||||
args.is_empty()));
|
||||
assert!(parse_ex("f(").is_err());
|
||||
assert!(parse_ex("f(a").is_err());
|
||||
assert!(parse_ex("f(a,b").is_err());
|
||||
assert!(parse_ex("f(a,b,").is_err());
|
||||
assert!(parse_ex("f(a,b ::").is_err());
|
||||
assert!(matches!(
|
||||
parse_ex("f(a)"),
|
||||
Ok(Expression::Call(f, CallKind::Normal, args)) if
|
||||
@@ -656,6 +695,14 @@ fn calls() {
|
||||
Expression::Reference(_,a),
|
||||
Expression::Reference(_,b),
|
||||
] if a.as_printed() == "a" && b.as_printed() == "b")));
|
||||
assert!(matches!(
|
||||
parse_ex("f(A,b,)"),
|
||||
Ok(Expression::Call(f, CallKind::Normal, args)) if
|
||||
matches!(f.as_ref(), Expression::Reference(_,n) if n.as_printed() == "f") &&
|
||||
matches!(args.as_slice(), [
|
||||
Expression::Reference(_,a),
|
||||
Expression::Reference(_,b),
|
||||
] if a.as_printed() == "A" && b.as_printed() == "b")));
|
||||
assert!(parse_ex("f(,a,b,)").is_err());
|
||||
assert!(parse_ex("f(a,,b,)").is_err());
|
||||
assert!(parse_ex("f(a,b,,)").is_err());
|
||||
@@ -833,6 +880,9 @@ fn prefix_and_postfix() {
|
||||
parse_ex("a * ++ b"),
|
||||
Err(ParserError::UnexpectedToken{ token: Token::OperatorName(pp), .. })
|
||||
if pp == "++"));
|
||||
|
||||
// this is a little bit of a weird case.
|
||||
assert!(parse_ex("**").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1083,6 +1133,154 @@ fn definitions() {
|
||||
body.len() == 1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn functions() {
|
||||
let parse_def = |str| {
|
||||
let lexer = Lexer::from(str);
|
||||
let mut result = Parser::new("test", lexer);
|
||||
result.parse_definition()
|
||||
};
|
||||
|
||||
assert!(matches!(
|
||||
parse_def("function() { }"),
|
||||
Ok(Definition { export: ExportClass::Private, type_restrictions, definition, .. }) if
|
||||
type_restrictions.is_empty() &&
|
||||
matches!(&definition, Def::Function(FunctionDef { name, arguments, return_type, body, .. }) if
|
||||
name.as_printed() == "function" &&
|
||||
arguments.is_empty() &&
|
||||
return_type.is_none() &&
|
||||
body.len() == 1)));
|
||||
assert!(matches!(
|
||||
parse_def("fun(a) { }"),
|
||||
Ok(Definition { export: ExportClass::Private, type_restrictions, definition, .. }) if
|
||||
type_restrictions.is_empty() &&
|
||||
matches!(&definition, Def::Function(FunctionDef { name, arguments, return_type, body, .. }) if
|
||||
name.as_printed() == "fun" &&
|
||||
return_type.is_none() &&
|
||||
body.len() == 1 &&
|
||||
matches!(arguments.as_slice(), [FunctionArg{ name, arg_type: None }] if
|
||||
name.as_printed() == "a"))));
|
||||
assert!(matches!(
|
||||
parse_def("fun(a,b) { }"),
|
||||
Ok(Definition { export: ExportClass::Private, type_restrictions, definition, .. }) if
|
||||
type_restrictions.is_empty() &&
|
||||
matches!(&definition, Def::Function(FunctionDef { name, arguments, return_type, body, .. }) if
|
||||
name.as_printed() == "fun" &&
|
||||
return_type.is_none() &&
|
||||
body.len() == 1 &&
|
||||
matches!(arguments.as_slice(), [
|
||||
FunctionArg{ name: aname, arg_type: None },
|
||||
FunctionArg{ name: bname, arg_type: None }
|
||||
] if
|
||||
aname.as_printed() == "a" &&
|
||||
bname.as_printed() == "b"))));
|
||||
assert!(matches!(
|
||||
parse_def("fun(a,b,) { }"),
|
||||
Ok(Definition { export: ExportClass::Private, type_restrictions, definition, .. }) if
|
||||
type_restrictions.is_empty() &&
|
||||
matches!(&definition, Def::Function(FunctionDef { name, arguments, return_type, body, .. }) if
|
||||
name.as_printed() == "fun" &&
|
||||
return_type.is_none() &&
|
||||
body.len() == 1 &&
|
||||
matches!(arguments.as_slice(), [
|
||||
FunctionArg{ name: aname, arg_type: None },
|
||||
FunctionArg{ name: bname, arg_type: None }
|
||||
] if
|
||||
aname.as_printed() == "a" &&
|
||||
bname.as_printed() == "b"))));
|
||||
assert!(matches!(
|
||||
parse_def("fun(a:U8,b,) { }"),
|
||||
Ok(Definition { export: ExportClass::Private, type_restrictions, definition, .. }) if
|
||||
type_restrictions.is_empty() &&
|
||||
matches!(&definition, Def::Function(FunctionDef { name, arguments, return_type, body, .. }) if
|
||||
name.as_printed() == "fun" &&
|
||||
return_type.is_none() &&
|
||||
body.len() == 1 &&
|
||||
matches!(arguments.as_slice(), [
|
||||
FunctionArg{ name: aname, arg_type: Some(Type::Application(atype, atype_args)) },
|
||||
FunctionArg{ name: bname, arg_type: None }
|
||||
] if
|
||||
aname.as_printed() == "a" &&
|
||||
matches!(atype.as_ref(), Type::Constructor(_, n) if n.as_printed() == "U8") &&
|
||||
atype_args.is_empty() &&
|
||||
bname.as_printed() == "b"))));
|
||||
assert!(matches!(
|
||||
parse_def("fun(a:U8,b:U8,) { }"),
|
||||
Ok(Definition { export: ExportClass::Private, type_restrictions, definition, .. }) if
|
||||
type_restrictions.is_empty() &&
|
||||
matches!(&definition, Def::Function(FunctionDef { name, arguments, return_type, body, .. }) if
|
||||
name.as_printed() == "fun" &&
|
||||
return_type.is_none() &&
|
||||
body.len() == 1 &&
|
||||
matches!(arguments.as_slice(), [
|
||||
FunctionArg{ name: aname, arg_type: Some(Type::Application(atype, atype_args)) },
|
||||
FunctionArg{ name: bname, arg_type: Some(Type::Application(btype, btype_args)) }
|
||||
] if
|
||||
aname.as_printed() == "a" &&
|
||||
matches!(atype.as_ref(), Type::Constructor(_, n) if n.as_printed() == "U8") &&
|
||||
atype_args.is_empty() &&
|
||||
bname.as_printed() == "b" &&
|
||||
matches!(btype.as_ref(), Type::Constructor(_, n) if n.as_printed() == "U8") &&
|
||||
btype_args.is_empty()))));
|
||||
assert!(matches!(
|
||||
parse_def("fun(a,b:U8,) { }"),
|
||||
Ok(Definition { export: ExportClass::Private, type_restrictions, definition, .. }) if
|
||||
type_restrictions.is_empty() &&
|
||||
matches!(&definition, Def::Function(FunctionDef { name, arguments, return_type, body, .. }) if
|
||||
name.as_printed() == "fun" &&
|
||||
return_type.is_none() &&
|
||||
body.len() == 1 &&
|
||||
matches!(arguments.as_slice(), [
|
||||
FunctionArg{ name: aname, arg_type: None },
|
||||
FunctionArg{ name: bname, arg_type: Some(Type::Application(btype, btype_args)) }
|
||||
] if
|
||||
aname.as_printed() == "a" &&
|
||||
bname.as_printed() == "b" &&
|
||||
matches!(btype.as_ref(), Type::Constructor(_, n) if n.as_printed() == "U8") &&
|
||||
btype_args.is_empty()))));
|
||||
assert!(parse_def("fun(a,,b,) { }").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn definition_types() {
|
||||
let parse_def = |str| {
|
||||
let lexer = Lexer::from(str);
|
||||
let mut result = Parser::new("test", lexer);
|
||||
result.parse_definition()
|
||||
};
|
||||
|
||||
assert!(matches!(
|
||||
parse_def("x: prim%U8 = 1;"),
|
||||
Ok(Definition { definition, .. }) if
|
||||
matches!(&definition, Def::Value(ValueDef{ mtype, .. }) if
|
||||
matches!(mtype, Some(Type::Application(f, args)) if
|
||||
args.is_empty() &&
|
||||
matches!(f.as_ref(), Type::Primitive(_, name) if
|
||||
name.as_printed() == "U8")))));
|
||||
assert!(matches!(
|
||||
parse_def("x: Stupid Monad prim%U8 = 1;"),
|
||||
Ok(Definition { definition, .. }) if
|
||||
matches!(&definition, Def::Value(ValueDef{ mtype, .. }) if
|
||||
matches!(mtype, Some(Type::Application(f, args)) if
|
||||
matches!(f.as_ref(), Type::Constructor(_, name) if
|
||||
name.as_printed() == "Stupid") &&
|
||||
matches!(args.as_slice(), [Type::Constructor(_, cname), Type::Primitive(_, pname)] if
|
||||
cname.as_printed() == "Monad" &&
|
||||
pname.as_printed() == "U8")))));
|
||||
assert!(matches!(
|
||||
parse_def("x: Stupid (Monad prim%U8) = 1;"),
|
||||
Ok(Definition { definition, .. }) if
|
||||
matches!(&definition, Def::Value(ValueDef{ mtype, .. }) if
|
||||
matches!(mtype, Some(Type::Application(f, args)) if
|
||||
matches!(f.as_ref(), Type::Constructor(_, name) if
|
||||
name.as_printed() == "Stupid") &&
|
||||
matches!(args.as_slice(), [Type::Application(cname, args2)] if
|
||||
matches!(cname.as_ref(), Type::Constructor(_, c) if c.as_printed() == "Monad") &&
|
||||
matches!(args2.as_slice(), [Type::Primitive(_, pname)] if
|
||||
pname.as_printed() == "U8"))))));
|
||||
assert!(parse_def("x: Stupid (Monad prim%U8 = 1;").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn operators() {
|
||||
let parse = |str| {
|
||||
@@ -1108,16 +1306,21 @@ prefix operator $$ at 1 -> money;
|
||||
assert!(parse("right infix operator - -> negate;").is_err());
|
||||
assert!(parse("left infix operator - -> negate;").is_err());
|
||||
assert!(parse("infix operator at 8 - -> negate;").is_err());
|
||||
|
||||
assert!(parse("infix operator * at 16 -> multiply;").is_err());
|
||||
assert!(parse("infix operator * at apple -> multiply;").is_err());
|
||||
|
||||
// these are designed to replicate the examples in the infix_and_precedence
|
||||
// tests, but with the precedence set automatically by the parser.
|
||||
let plus_and_times = |expr| format!(r#"
|
||||
let plus_and_times = |expr| {
|
||||
format!(
|
||||
r#"
|
||||
infix left operator + at 6 -> add;
|
||||
infix right operator * at 7 -> mul;
|
||||
|
||||
x = {expr};
|
||||
"#);
|
||||
"#
|
||||
)
|
||||
};
|
||||
|
||||
let plus_example = plus_and_times("1 + 2 + 3");
|
||||
assert!(matches!(
|
||||
@@ -1155,7 +1358,6 @@ x = {expr};
|
||||
Expression::Value(ConstantValue::Integer(_, IntegerWithBase{ value: v3, .. }))
|
||||
] if *v2 == 2 && *v3 == 3)))))));
|
||||
|
||||
|
||||
let mixed_example = plus_and_times("1 + 2 * 3 + 4");
|
||||
assert!(matches!(
|
||||
parse(&mixed_example),
|
||||
@@ -1183,3 +1385,116 @@ x = {expr};
|
||||
matches!(v2, IntegerWithBase{ value: 2, .. }) &&
|
||||
matches!(v3, IntegerWithBase{ value: 3, .. })))))))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pattern_match() {
|
||||
let parse_ex = |str| {
|
||||
let lexer = Lexer::from(str);
|
||||
let mut result = Parser::new("test", lexer);
|
||||
result.parse_expression()
|
||||
};
|
||||
|
||||
assert!(matches!(
|
||||
parse_ex("match x { }"),
|
||||
Ok(Expression::Match(MatchExpr{ value, cases, .. })) if
|
||||
matches!(value.as_ref(), Expression::Reference(_, n) if n.as_printed() == "x") &&
|
||||
cases.is_empty()));
|
||||
assert!(matches!(
|
||||
parse_ex("match x { 1 -> 2 }"),
|
||||
Ok(Expression::Match(MatchExpr{ value, cases, .. })) if
|
||||
matches!(value.as_ref(), Expression::Reference(_, n) if n.as_printed() == "x") &&
|
||||
matches!(cases.as_slice(), [MatchCase { pattern, consequent }] if
|
||||
matches!(pattern, Pattern::Constant(ConstantValue::Integer(_, iwb)) if
|
||||
iwb.value == 1) &&
|
||||
matches!(consequent, Expression::Value(ConstantValue::Integer(_, iwb)) if
|
||||
iwb.value == 2))));
|
||||
assert!(matches!(
|
||||
parse_ex("match x { 1 -> 2, }"),
|
||||
Ok(Expression::Match(MatchExpr{ value, cases, .. })) if
|
||||
matches!(value.as_ref(), Expression::Reference(_, n) if n.as_printed() == "x") &&
|
||||
matches!(cases.as_slice(), [MatchCase { pattern, consequent }] if
|
||||
matches!(pattern, Pattern::Constant(ConstantValue::Integer(_, iwb)) if
|
||||
iwb.value == 1) &&
|
||||
matches!(consequent, Expression::Value(ConstantValue::Integer(_, iwb)) if
|
||||
iwb.value == 2))));
|
||||
assert!(matches!(
|
||||
parse_ex("match x { 1 -> 2, 3 -> 4 }"),
|
||||
Ok(Expression::Match(MatchExpr{ value, cases, .. })) if
|
||||
matches!(value.as_ref(), Expression::Reference(_, n) if n.as_printed() == "x") &&
|
||||
matches!(cases.as_slice(), [mcase1, mcase2] if
|
||||
matches!(mcase1, MatchCase { pattern, consequent } if
|
||||
matches!(pattern, Pattern::Constant(ConstantValue::Integer(_, iwb)) if
|
||||
iwb.value == 1) &&
|
||||
matches!(consequent, Expression::Value(ConstantValue::Integer(_, iwb)) if
|
||||
iwb.value == 2)) &&
|
||||
matches!(mcase2, MatchCase { pattern, consequent } if
|
||||
matches!(pattern, Pattern::Constant(ConstantValue::Integer(_, iwb)) if
|
||||
iwb.value == 3) &&
|
||||
matches!(consequent, Expression::Value(ConstantValue::Integer(_, iwb)) if
|
||||
iwb.value == 4)))));
|
||||
assert!(matches!(
|
||||
parse_ex("match x { y -> 2, }"),
|
||||
Ok(Expression::Match(MatchExpr{ value, cases, .. })) if
|
||||
matches!(value.as_ref(), Expression::Reference(_, n) if n.as_printed() == "x") &&
|
||||
matches!(cases.as_slice(), [MatchCase { pattern, consequent }] if
|
||||
matches!(pattern, Pattern::Variable(n) if n.as_printed() == "y") &&
|
||||
matches!(consequent, Expression::Value(ConstantValue::Integer(_, iwb)) if
|
||||
iwb.value == 2))));
|
||||
assert!(matches!(
|
||||
parse_ex("match x { Option::None -> 2, Option::Some(x) -> 4 }"),
|
||||
Ok(Expression::Match(MatchExpr{ value, cases, .. })) if
|
||||
matches!(value.as_ref(), Expression::Reference(_, n) if n.as_printed() == "x") &&
|
||||
matches!(cases.as_slice(), [mcase1, mcase2] if
|
||||
matches!(mcase1, MatchCase { pattern, consequent } if
|
||||
matches!(pattern, Pattern::EnumerationValue(ep) if
|
||||
matches!(ep, EnumerationPattern{ type_name, variant_name, argument: None, .. } if
|
||||
type_name.as_printed() == "Option" &&
|
||||
variant_name.as_printed() == "None")) &&
|
||||
matches!(consequent, Expression::Value(ConstantValue::Integer(_, iwb)) if
|
||||
iwb.value == 2)) &&
|
||||
matches!(mcase2, MatchCase { pattern, consequent } if
|
||||
matches!(pattern, Pattern::EnumerationValue(ep) if
|
||||
matches!(ep, EnumerationPattern{ type_name, variant_name, argument: Some(subp), .. } if
|
||||
type_name.as_printed() == "Option" &&
|
||||
variant_name.as_printed() == "Some" &&
|
||||
matches!(subp.as_ref(), Pattern::Variable(n) if n.as_printed() == "x"))) &&
|
||||
matches!(consequent, Expression::Value(ConstantValue::Integer(_, iwb)) if
|
||||
iwb.value == 4)))));
|
||||
assert!(matches!(
|
||||
parse_ex("match x { Foo{ a, b: 2, c: d } -> 6 }"),
|
||||
Ok(Expression::Match(MatchExpr{ value, cases, .. })) if
|
||||
matches!(value.as_ref(), Expression::Reference(_, n) if n.as_printed() == "x") &&
|
||||
matches!(cases.as_slice(), [MatchCase{ pattern, consequent }] if
|
||||
matches!(pattern, Pattern::Structure(StructurePattern{ type_name, fields, .. }) if
|
||||
type_name.as_printed() == "Foo" &&
|
||||
matches!(fields.as_slice(), [(field1, None), (field2, Some(pat2)), (field3, Some(pat3))] if
|
||||
field1.as_printed() == "a" &&
|
||||
field2.as_printed() == "b" &&
|
||||
field3.as_printed() == "c" &&
|
||||
matches!(pat2, Pattern::Constant(ConstantValue::Integer(_, iwb)) if iwb.value == 2) &&
|
||||
matches!(pat3, Pattern::Variable(n) if n.as_printed() == "d"))) &&
|
||||
matches!(consequent, Expression::Value(ConstantValue::Integer(_, iwb)) if
|
||||
iwb.value == 6))));
|
||||
assert!(matches!(
|
||||
parse_ex("match x { Foo{ a, b: 2, c } -> 6 }"),
|
||||
Ok(Expression::Match(MatchExpr{ value, cases, .. })) if
|
||||
matches!(value.as_ref(), Expression::Reference(_, n) if n.as_printed() == "x") &&
|
||||
matches!(cases.as_slice(), [MatchCase{ pattern, consequent }] if
|
||||
matches!(pattern, Pattern::Structure(StructurePattern{ type_name, fields, .. }) if
|
||||
type_name.as_printed() == "Foo" &&
|
||||
matches!(fields.as_slice(), [(field1, None), (field2, Some(pat2)), (field3, None)] if
|
||||
field1.as_printed() == "a" &&
|
||||
field2.as_printed() == "b" &&
|
||||
field3.as_printed() == "c" &&
|
||||
matches!(pat2, Pattern::Constant(ConstantValue::Integer(_, iwb)) if iwb.value == 2))) &&
|
||||
matches!(consequent, Expression::Value(ConstantValue::Integer(_, iwb)) if
|
||||
iwb.value == 6))));
|
||||
|
||||
assert!(parse_ex("match x { Foo -> 3 }").is_err());
|
||||
assert!(parse_ex("match x { (4) -> 3 }").is_err());
|
||||
assert!(parse_ex("match x { Foo{ 3, x } -> 3 }").is_err());
|
||||
assert!(parse_ex("match x { Foo{ x").is_err());
|
||||
assert!(parse_ex("match x { Foo{ x: 3").is_err());
|
||||
assert!(parse_ex("match x { Foo{ x:: 3").is_err());
|
||||
assert!(parse_ex("match x { Foo{ x: 3 4 } -> 4 }").is_err());
|
||||
}
|
||||
|
||||
70
src/syntax/print.rs
Normal file
70
src/syntax/print.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use crate::syntax::ast::{ConstantValue, Type};
|
||||
#[cfg(test)]
|
||||
use crate::syntax::parse::Parser;
|
||||
#[cfg(test)]
|
||||
use crate::syntax::tokens::Lexer;
|
||||
use pretty::{DocAllocator, Pretty};
|
||||
|
||||
impl<'a, D: ?Sized + DocAllocator<'a, A>, A: 'a> Pretty<'a, D, A> for Type {
|
||||
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
|
||||
match self {
|
||||
Type::Constructor(_, n) => allocator.as_string(n),
|
||||
Type::Variable(_, n) => allocator.as_string(n),
|
||||
Type::Primitive(_, n) => allocator.text("prim%").append(allocator.as_string(n)),
|
||||
|
||||
Type::Application(c, args) => c
|
||||
.pretty(allocator)
|
||||
.append(allocator.space())
|
||||
.append(allocator.intersperse(args, " ")),
|
||||
|
||||
Type::Function(args, ret) => allocator
|
||||
.intersperse(args, " ")
|
||||
.append(allocator.space())
|
||||
.append(ret.pretty(allocator)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, D: ?Sized + DocAllocator<'a, A>, A: 'a> Pretty<'a, D, A> for ConstantValue {
|
||||
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> {
|
||||
match self {
|
||||
ConstantValue::String(_, x) => allocator.text(format!("{x:?}")),
|
||||
ConstantValue::Character(_, c) => allocator.text(format!("{c:?}")),
|
||||
ConstantValue::Integer(_, iwb) => match iwb.base {
|
||||
None => allocator.as_string(iwb.value),
|
||||
Some(2) => allocator.text(format!("0b{:b}", iwb.value)),
|
||||
Some(8) => allocator.text(format!("0o{:o}", iwb.value)),
|
||||
Some(10) => allocator.text(format!("0d{}", iwb.value)),
|
||||
Some(16) => allocator.text(format!("0x{:x}", iwb.value)),
|
||||
Some(x) => panic!("Illegal base {x} for integer constant."),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proptest::proptest! {
|
||||
#[test]
|
||||
fn constants(x: ConstantValue) {
|
||||
let allocator: pretty::Arena = pretty::Arena::new();
|
||||
let docbuilder = x.clone().pretty(&allocator);
|
||||
let mut string_version = String::new();
|
||||
docbuilder.render_fmt(80, &mut string_version).expect("can render to string");
|
||||
let lexer = Lexer::from(string_version.as_str());
|
||||
let mut parser = Parser::new("test", lexer);
|
||||
let roundtripped = parser.parse_constant().expect("can parse constant");
|
||||
proptest::prop_assert_eq!(x, roundtripped);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn types(x: Type) {
|
||||
// let allocator: pretty::Arena = pretty::Arena::new();
|
||||
// let docbuilder = x.clone().pretty(&allocator);
|
||||
// let mut string_version = String::new();
|
||||
// docbuilder.render_fmt(80, &mut string_version).expect("can render to string");
|
||||
// println!("String version: {string_version:?}");
|
||||
// let lexer = Lexer::from(string_version.as_str());
|
||||
// let mut parser = Parser::new("test", lexer);
|
||||
// let roundtripped = parser.parse_type().expect("can parse constant");
|
||||
// proptest::prop_assert_eq!(x, roundtripped);
|
||||
// }
|
||||
}
|
||||
@@ -642,6 +642,13 @@ fn numbers_work_as_expected() {
|
||||
}),
|
||||
parsed_single_token("0o10")
|
||||
);
|
||||
assert_eq!(
|
||||
Token::Integer(IntegerWithBase {
|
||||
base: None,
|
||||
value: 10
|
||||
}),
|
||||
parsed_single_token("0010")
|
||||
);
|
||||
assert_eq!(
|
||||
Token::Integer(IntegerWithBase {
|
||||
base: Some(10),
|
||||
@@ -681,6 +688,20 @@ fn values_work_as_expected() {
|
||||
assert_eq!(Token::ValueName("ɑ".into()), parsed_single_token("ɑ"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn primitives() {
|
||||
assert_eq!(
|
||||
Token::PrimitiveValueName("add_u8".into()),
|
||||
parsed_single_token("prim%add_u8"),
|
||||
);
|
||||
assert_eq!(
|
||||
Token::PrimitiveTypeName("U8".into()),
|
||||
parsed_single_token("prim%U8"),
|
||||
);
|
||||
assert!(Lexer::from("prim%").next().unwrap().is_err());
|
||||
assert!(Lexer::from("prim%%").next().unwrap().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn operators_work_as_expected() {
|
||||
assert_eq!(Token::OperatorName("-".into()), parsed_single_token("-"));
|
||||
@@ -743,10 +764,119 @@ fn unicode() {
|
||||
let mut next_token = move || lexer.next().map(|x| x.expect("Can read valid token").token);
|
||||
assert_eq!(Some(Token::Character('¾')), next_token());
|
||||
|
||||
let mut lexer = Lexer::from("'\\u{111111111111}'");
|
||||
let mut lexer = Lexer::from("'\\u{11111111111111111111111111111}'");
|
||||
assert!(lexer.next().unwrap().is_err());
|
||||
let mut lexer = Lexer::from("'\\u{00BE'");
|
||||
assert!(lexer.next().unwrap().is_err());
|
||||
let mut lexer = Lexer::from("'\\u00BE}'");
|
||||
assert!(lexer.next().unwrap().is_err());
|
||||
let mut lexer = Lexer::from("'\\u");
|
||||
assert!(lexer.next().unwrap().is_err());
|
||||
let mut lexer = Lexer::from("'\\u{00Z}'");
|
||||
assert!(lexer.next().unwrap().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn character_string_errors() {
|
||||
let mut lexer = Lexer::from("'");
|
||||
assert!(lexer.next().unwrap().is_err());
|
||||
let mut lexer = Lexer::from("'-\\");
|
||||
assert!(lexer.next().unwrap().is_err());
|
||||
let mut lexer = Lexer::from("''");
|
||||
assert!(lexer.next().unwrap().is_err());
|
||||
let mut lexer = Lexer::from("'ab'");
|
||||
assert!(lexer.next().unwrap().is_err());
|
||||
let mut lexer = Lexer::from("'\\x'");
|
||||
assert!(lexer.next().unwrap().is_err());
|
||||
let mut lexer = Lexer::from("'a'");
|
||||
assert!(matches!(
|
||||
lexer.next(),
|
||||
Some(Ok(LocatedToken {
|
||||
token: Token::Character('a'),
|
||||
..
|
||||
}))
|
||||
));
|
||||
let mut lexer = Lexer::from("'\\0'");
|
||||
assert!(matches!(
|
||||
lexer.next(),
|
||||
Some(Ok(LocatedToken {
|
||||
token: Token::Character('\0'),
|
||||
..
|
||||
}))
|
||||
));
|
||||
let mut lexer = Lexer::from("'\\a'");
|
||||
assert!(matches!(
|
||||
lexer.next(),
|
||||
Some(Ok(LocatedToken {
|
||||
token: Token::Character(_),
|
||||
..
|
||||
}))
|
||||
));
|
||||
let mut lexer = Lexer::from("'\\b'");
|
||||
assert!(matches!(
|
||||
lexer.next(),
|
||||
Some(Ok(LocatedToken {
|
||||
token: Token::Character(_),
|
||||
..
|
||||
}))
|
||||
));
|
||||
let mut lexer = Lexer::from("'\\f'");
|
||||
assert!(matches!(
|
||||
lexer.next(),
|
||||
Some(Ok(LocatedToken {
|
||||
token: Token::Character(_),
|
||||
..
|
||||
}))
|
||||
));
|
||||
let mut lexer = Lexer::from("'\\n'");
|
||||
assert!(matches!(
|
||||
lexer.next(),
|
||||
Some(Ok(LocatedToken {
|
||||
token: Token::Character(_),
|
||||
..
|
||||
}))
|
||||
));
|
||||
let mut lexer = Lexer::from("'\\r'");
|
||||
assert!(matches!(
|
||||
lexer.next(),
|
||||
Some(Ok(LocatedToken {
|
||||
token: Token::Character(_),
|
||||
..
|
||||
}))
|
||||
));
|
||||
let mut lexer = Lexer::from("'\\t'");
|
||||
assert!(matches!(
|
||||
lexer.next(),
|
||||
Some(Ok(LocatedToken {
|
||||
token: Token::Character(_),
|
||||
..
|
||||
}))
|
||||
));
|
||||
let mut lexer = Lexer::from("'\\v'");
|
||||
assert!(matches!(
|
||||
lexer.next(),
|
||||
Some(Ok(LocatedToken {
|
||||
token: Token::Character(_),
|
||||
..
|
||||
}))
|
||||
));
|
||||
let mut lexer = Lexer::from("'\\''");
|
||||
assert!(matches!(
|
||||
lexer.next(),
|
||||
Some(Ok(LocatedToken {
|
||||
token: Token::Character('\''),
|
||||
..
|
||||
}))
|
||||
));
|
||||
let mut lexer = Lexer::from("'\\\\'");
|
||||
assert!(matches!(
|
||||
lexer.next(),
|
||||
Some(Ok(LocatedToken {
|
||||
token: Token::Character('\\'),
|
||||
..
|
||||
}))
|
||||
));
|
||||
|
||||
let mut lexer = Lexer::from("\"foo");
|
||||
assert!(lexer.next().unwrap().is_err());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user