diff --git a/Cargo.lock b/Cargo.lock
index 584889c..68e562d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,18 +4,18 @@ version = 3
[[package]]
name = "aho-corasick"
-version = "1.1.2"
+version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "anstream"
-version = "0.6.12"
+version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540"
+checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -70,9 +70,9 @@ dependencies = [
[[package]]
name = "autocfg"
-version = "1.1.0"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
[[package]]
name = "bit-set"
@@ -97,9 +97,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.4.2"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
+checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "cfg-if"
@@ -108,10 +108,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
-name = "clap"
-version = "4.5.1"
+name = "cfg_aliases"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+
+[[package]]
+name = "clap"
+version = "4.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
dependencies = [
"clap_builder",
"clap_derive",
@@ -119,9 +125,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.5.1"
+version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
+checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
dependencies = [
"anstream",
"anstyle",
@@ -131,14 +137,14 @@ dependencies = [
[[package]]
name = "clap_derive"
-version = "4.5.0"
+version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
+checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
dependencies = [
"heck",
"proc-macro2",
"quote",
- "syn 2.0.51",
+ "syn",
]
[[package]]
@@ -149,9 +155,9 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "clipboard-win"
-version = "5.2.0"
+version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12f9a0700e0127ba15d1d52dd742097f821cd9c65939303a44d970465040a297"
+checksum = "d517d4b86184dbb111d3556a10f1c8a04da7428d2987bf1081602bf11c3aa9ee"
dependencies = [
"error-code",
]
@@ -170,20 +176,14 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "ctrlc"
-version = "3.4.2"
+version = "3.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b"
+checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345"
dependencies = [
"nix",
"windows-sys",
]
-[[package]]
-name = "diff"
-version = "0.1.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
-
[[package]]
name = "dirs-next"
version = "2.0.0"
@@ -244,9 +244,9 @@ dependencies = [
[[package]]
name = "error-code"
-version = "3.1.0"
+version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26a147e1a6641a55d994b3e4e9fa4d9b180c8d652c09b363af8c9bf1b8e04139"
+checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b"
[[package]]
name = "fd-lock"
@@ -284,15 +284,9 @@ checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]]
name = "heck"
-version = "0.4.1"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
-
-[[package]]
-name = "hermit-abi"
-version = "0.3.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "home"
@@ -305,63 +299,51 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.2.3"
+version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177"
+checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
"equivalent",
"hashbrown",
]
-[[package]]
-name = "is-terminal"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
-dependencies = [
- "hermit-abi",
- "libc",
- "windows-sys",
-]
-
[[package]]
name = "itertools"
-version = "0.10.5"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
[[package]]
name = "lalrpop"
-version = "0.20.0"
+version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da4081d44f4611b66c6dd725e6de3169f9f63905421e8626fcb86b6a898998b8"
+checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca"
dependencies = [
"ascii-canvas",
"bit-set",
- "diff",
"ena",
- "is-terminal",
"itertools",
"lalrpop-util",
"petgraph",
"regex",
- "regex-syntax 0.7.5",
+ "regex-syntax",
"string_cache",
"term",
"tiny-keccak",
"unicode-xid",
+ "walkdir",
]
[[package]]
name = "lalrpop-util"
-version = "0.20.0"
+version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f35c735096c0293d313e8f2a641627472b83d01b937177fe76e5e2708d31e0d"
+checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553"
dependencies = [
- "regex",
+ "regex-automata",
]
[[package]]
@@ -382,7 +364,7 @@ version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.5.0",
"libc",
"redox_syscall",
]
@@ -405,21 +387,21 @@ dependencies = [
[[package]]
name = "log"
-version = "0.4.20"
+version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "memchr"
-version = "2.7.1"
+version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
+checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "new_debug_unreachable"
-version = "1.0.4"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
[[package]]
name = "nibble_vec"
@@ -432,12 +414,13 @@ dependencies = [
[[package]]
name = "nix"
-version = "0.27.1"
+version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
+checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.5.0",
"cfg-if",
+ "cfg_aliases",
"libc",
]
@@ -541,9 +524,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "proc-macro2"
-version = "1.0.78"
+version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
+checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [
"unicode-ident",
]
@@ -619,46 +602,40 @@ dependencies = [
[[package]]
name = "regex"
-version = "1.10.3"
+version = "1.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
+checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
- "regex-syntax 0.8.2",
+ "regex-syntax",
]
[[package]]
name = "regex-automata"
-version = "0.4.5"
+version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
+checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
"aho-corasick",
"memchr",
- "regex-syntax 0.8.2",
+ "regex-syntax",
]
[[package]]
name = "regex-syntax"
-version = "0.7.5"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
-
-[[package]]
-name = "regex-syntax"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "rustix"
-version = "0.38.31"
+version = "0.38.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
+checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.5.0",
"errno",
"libc",
"linux-raw-sys",
@@ -673,11 +650,11 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]]
name = "rustyline"
-version = "13.0.0"
+version = "14.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86"
+checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63"
dependencies = [
- "bitflags 2.4.2",
+ "bitflags 2.5.0",
"cfg-if",
"clipboard-win",
"fd-lock",
@@ -690,7 +667,16 @@ dependencies = [
"unicode-segmentation",
"unicode-width",
"utf8parse",
- "winapi",
+ "windows-sys",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
]
[[package]]
@@ -707,9 +693,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "smallvec"
-version = "1.13.1"
+version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "string_cache"
@@ -732,20 +718,9 @@ checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
[[package]]
name = "syn"
-version = "1.0.109"
+version = "2.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.51"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c"
+checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0"
dependencies = [
"proc-macro2",
"quote",
@@ -754,7 +729,7 @@ dependencies = [
[[package]]
name = "talc-bin"
-version = "0.1.0"
+version = "0.2.0"
dependencies = [
"clap",
"ctrlc",
@@ -765,7 +740,7 @@ dependencies = [
[[package]]
name = "talc-lang"
-version = "0.1.0"
+version = "0.2.0"
dependencies = [
"lalrpop",
"lalrpop-util",
@@ -774,22 +749,24 @@ dependencies = [
"num-rational",
"num-traits",
"thiserror",
+ "unicode-ident",
]
[[package]]
name = "talc-macros"
-version = "0.1.0"
+version = "0.2.0"
dependencies = [
"quote",
- "syn 1.0.109",
+ "syn",
]
[[package]]
name = "talc-std"
-version = "0.1.0"
+version = "0.2.0"
dependencies = [
"lazy_static",
"rand",
+ "regex",
"talc-lang",
"talc-macros",
]
@@ -807,22 +784,22 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "1.0.57"
+version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
+checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.57"
+version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
+checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.51",
+ "syn",
]
[[package]]
@@ -864,6 +841,16 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@@ -886,6 +873,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+[[package]]
+name = "winapi-util"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+dependencies = [
+ "winapi",
+]
+
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
@@ -898,7 +894,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
- "windows-targets 0.52.3",
+ "windows-targets 0.52.4",
]
[[package]]
@@ -918,17 +914,17 @@ dependencies = [
[[package]]
name = "windows-targets"
-version = "0.52.3"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f"
+checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [
- "windows_aarch64_gnullvm 0.52.3",
- "windows_aarch64_msvc 0.52.3",
- "windows_i686_gnu 0.52.3",
- "windows_i686_msvc 0.52.3",
- "windows_x86_64_gnu 0.52.3",
- "windows_x86_64_gnullvm 0.52.3",
- "windows_x86_64_msvc 0.52.3",
+ "windows_aarch64_gnullvm 0.52.4",
+ "windows_aarch64_msvc 0.52.4",
+ "windows_i686_gnu 0.52.4",
+ "windows_i686_msvc 0.52.4",
+ "windows_x86_64_gnu 0.52.4",
+ "windows_x86_64_gnullvm 0.52.4",
+ "windows_x86_64_msvc 0.52.4",
]
[[package]]
@@ -939,9 +935,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.52.3"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6"
+checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]]
name = "windows_aarch64_msvc"
@@ -951,9 +947,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.52.3"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f"
+checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]]
name = "windows_i686_gnu"
@@ -963,9 +959,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
-version = "0.52.3"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb"
+checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]]
name = "windows_i686_msvc"
@@ -975,9 +971,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
-version = "0.52.3"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58"
+checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]]
name = "windows_x86_64_gnu"
@@ -987,9 +983,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.52.3"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614"
+checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]]
name = "windows_x86_64_gnullvm"
@@ -999,9 +995,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.52.3"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c"
+checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]]
name = "windows_x86_64_msvc"
@@ -1011,6 +1007,6 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.52.3"
+version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6"
+checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0a04128
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ae5dd08
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# talc
+
+## installation
+
+```sh
+cargo install --profile release-lto --path talc-bin
+```
diff --git a/talc-bin/Cargo.toml b/talc-bin/Cargo.toml
index 25d823b..908a13b 100644
--- a/talc-bin/Cargo.toml
+++ b/talc-bin/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "talc-bin"
-version = "0.1.0"
+version = "0.2.0"
edition = "2021"
[[bin]]
@@ -10,6 +10,6 @@ path = "src/main.rs"
[dependencies]
talc-lang = { path = "../talc-lang" }
talc-std = { path = "../talc-std" }
-rustyline = "13.0"
+rustyline = "14.0"
clap = { version = "4.5", features = ["derive"] }
ctrlc = "3.4"
diff --git a/talc-bin/src/helper.rs b/talc-bin/src/helper.rs
index 6c5103c..273de31 100644
--- a/talc-bin/src/helper.rs
+++ b/talc-bin/src/helper.rs
@@ -1,7 +1,7 @@
use std::{borrow::Cow, cell::RefCell, collections::HashMap, rc::Rc};
use rustyline::{completion::Completer, highlight::Highlighter, hint::Hinter, validate::{ValidationContext, ValidationResult, Validator}, Helper, Result};
-use talc_lang::{Lexer, Vm};
+use talc_lang::{lstring::LStr, Lexer, Vm};
#[derive(Clone, Copy)]
enum TokenType {
@@ -62,8 +62,8 @@ impl Completer for TalcHelper {
let res: String = res.chars().rev().collect();
let mut keys = self.vm.borrow().globals().keys()
.map(|sym| sym.name())
- .filter(|name| name.starts_with(&res))
- .map(|s| s.to_string())
+ .filter(|name| name.starts_with(LStr::from_str(&res)))
+ .map(LStr::to_string)
.collect::>();
keys.sort();
Ok((pos - res.as_bytes().len(), keys))
@@ -150,7 +150,7 @@ impl Validator for TalcHelper {
v => { mismatch = Some((v, t)); break }
}
"do" => match delims.last().copied() {
- Some("while") | Some("for") | Some("catch") => {
+ Some("while" | "for" | "catch") => {
delims.pop();
delims.push(t);
},
diff --git a/talc-bin/src/main.rs b/talc-bin/src/main.rs
index ea38fbb..3b1d1fa 100644
--- a/talc-bin/src/main.rs
+++ b/talc-bin/src/main.rs
@@ -28,12 +28,12 @@ struct Args {
color: ColorChoice,
}
-fn exec(src: String, args: &Args) -> ExitCode {
+fn exec(src: &str, args: &Args) -> ExitCode {
let parser = talc_lang::Parser::new();
let mut vm = Vm::new(256);
talc_std::load_all(&mut vm);
- let ex = match parser.parse(&src) {
+ let ex = match parser.parse(src) {
Ok(ex) => ex,
Err(e) => {
eprintln!("Error: {e}");
@@ -66,7 +66,7 @@ fn main() -> ExitCode {
}
match std::fs::read_to_string(args.file.as_ref().unwrap()) {
- Ok(s) => exec(s, &args),
+ Ok(s) => exec(&s, &args),
Err(e) => {
eprintln!("Error: {e}");
ExitCode::FAILURE
diff --git a/talc-lang/Cargo.toml b/talc-lang/Cargo.toml
index 3e0a900..a7dd2e0 100644
--- a/talc-lang/Cargo.toml
+++ b/talc-lang/Cargo.toml
@@ -1,15 +1,16 @@
[package]
name = "talc-lang"
-version = "0.1.0"
+version = "0.2.0"
edition = "2021"
[dependencies]
lalrpop-util = { version = "0.20", features = ["lexer", "unicode"] }
num-complex = "0.4"
num-rational = { version = "0.4", default-features = false, features = [] }
-num-traits = "*"
+num-traits = "0.2"
thiserror = "1.0"
lazy_static = "1.4"
+unicode-ident = "1.0"
[build-dependencies]
lalrpop = { version = "0.20", default_features = false, features = ["lexer", "unicode"]}
diff --git a/talc-lang/src/ast.rs b/talc-lang/src/ast.rs
index 98e8039..cc5c306 100644
--- a/talc-lang/src/ast.rs
+++ b/talc-lang/src/ast.rs
@@ -1,8 +1,9 @@
-use crate::{value::Value, symbol::Symbol};
+use crate::{lstring::LStr, symbol::Symbol, value::Value};
#[derive(Clone, Copy, Debug)]
pub enum BinaryOp {
Add, Sub, Mul, Div, Mod, Pow, IntDiv,
+ Shr, Shl, BitAnd, BitXor, BitOr,
Eq, Ne, Gt, Lt, Ge, Le,
Concat, Append,
Range, RangeIncl,
@@ -16,17 +17,18 @@ pub enum UnaryOp {
#[derive(Debug)]
pub enum Expr<'s> {
Literal(Value),
- Ident(&'s str),
+ Ident(&'s LStr),
UnaryOp(UnaryOp, Box>),
BinaryOp(BinaryOp, Box>, Box>),
Assign(Option, Box>, Box>),
- AssignVar(&'s str, Box>),
- AssignGlobal(&'s str, Box>),
+ AssignVar(&'s LStr, Box>),
+ AssignGlobal(&'s LStr, Box>),
Index(Box>, Box>),
FnCall(Box>, Vec>),
+ AssocFnCall(Box>, Symbol, Vec>),
Pipe(Box>, Box>),
Block(Vec>),
@@ -38,20 +40,20 @@ pub enum Expr<'s> {
Or(Box>, Box>),
If(Box>, Box>, Option>>),
While(Box>, Box>),
- For(&'s str, Box>, Box>),
- Lambda(Vec<&'s str>, Box>),
+ For(&'s LStr, Box>, Box>),
+ Lambda(Vec<&'s LStr>, Box>),
Try(Box>, Vec>),
}
#[derive(Debug)]
pub struct CatchBlock<'s> {
- pub name: Option<&'s str>,
+ pub name: Option<&'s LStr>,
pub types: Option>,
pub body: Expr<'s>,
}
#[derive(Debug)]
pub enum LValue<'s> {
- Ident(&'s str),
+ Ident(&'s LStr),
Index(Box>, Box>),
}
diff --git a/talc-lang/src/compiler.rs b/talc-lang/src/compiler.rs
index 6537333..13458c5 100644
--- a/talc-lang/src/compiler.rs
+++ b/talc-lang/src/compiler.rs
@@ -2,13 +2,15 @@ use std::rc::Rc;
use crate::ast::{BinaryOp, Expr, LValue, CatchBlock};
use crate::chunk::{Instruction as I, Chunk, Arg24, Catch};
+use crate::lstr;
+use crate::lstring::LStr;
use crate::symbol::Symbol;
use crate::value::function::{FuncAttrs, Function};
use crate::value::Value;
#[derive(Debug, Clone)]
pub struct Local {
- name: Rc,
+ name: Rc,
scope: usize,
}
@@ -42,7 +44,7 @@ pub fn compile_repl(expr: &Expr, globals: &[Local]) -> (Function, Vec) {
impl<'a> Default for Compiler<'a> {
fn default() -> Self {
let locals = vec![Local {
- name: "self".into(),
+ name: lstr!("self").into(),
scope: 0,
}];
Self {
@@ -74,7 +76,7 @@ impl<'a> Compiler<'a> {
}
}
- fn new_function(&'a self, args: &[&str]) -> Self {
+ fn new_function(&'a self, args: &[&LStr]) -> Self {
let mut new = Self {
mode: CompilerMode::Function,
parent: Some(self),
@@ -204,20 +206,20 @@ impl<'a> Compiler<'a> {
// variables
//
- fn resolve_local(&mut self, name: &str) -> Option {
+ fn resolve_local(&mut self, name: &LStr) -> Option {
self.locals.iter().rev()
.position(|v| v.name.as_ref() == name)
.map(|x| self.locals.len() - x - 1)
}
- fn resolve_global(&mut self, name: &str) -> Option {
+ fn resolve_global(&mut self, name: &LStr) -> Option {
self.globals.iter().rev()
.position(|v| v.name.as_ref() == name)
.map(|x| self.globals.len() - x - 1)
}
- fn load_var(&mut self, name: &str) {
+ fn load_var(&mut self, name: &LStr) {
match (self.resolve_local(name), self.resolve_global(name)) {
(Some(n), None) => {
self.emit(I::LoadLocal(Arg24::from_usize(n)));
@@ -232,7 +234,7 @@ impl<'a> Compiler<'a> {
}
}
- fn declare_local(&mut self, name: &str) -> usize {
+ fn declare_local(&mut self, name: &LStr) -> usize {
if let Some(i) = self.resolve_local(name) {
if self.locals[i].scope == self.scope {
self.emit(I::StoreLocal(Arg24::from_usize(i)));
@@ -254,7 +256,7 @@ impl<'a> Compiler<'a> {
self.emit(I::StoreLocal(Arg24::from_usize(i)));
}
- fn store_global(&mut self, name: &str) {
+ fn store_global(&mut self, name: &LStr) {
let sym = Symbol::get(name);
self.emit(I::StoreGlobal(Arg24::from_symbol(sym)));
if let Some(i) = self.resolve_global(name) {
@@ -268,7 +270,7 @@ impl<'a> Compiler<'a> {
});
}
- fn store_default(&mut self, name: &str) {
+ fn store_default(&mut self, name: &LStr) {
match (self.resolve_local(name), self.resolve_global(name)) {
(Some(n), None) => self.store_local(n),
(Some(n), Some(m)) if n >= m => self.store_local(n),
@@ -369,6 +371,17 @@ impl<'a> Compiler<'a> {
}
self.emit(I::Call(args.len() as u8));
},
+ Expr::AssocFnCall(o, f, args) => {
+ self.expr(o);
+ self.emit(I::Dup);
+ self.emit(I::Symbol(Arg24::from_symbol(*f)));
+ self.emit(I::Index);
+ self.emit(I::Swap);
+ for a in args {
+ self.expr(a);
+ }
+ self.emit(I::Call((args.len() + 1) as u8));
+ },
Expr::Return(e) => {
self.expr(e);
self.emit(I::Return);
@@ -467,7 +480,7 @@ impl<'a> Compiler<'a> {
self.chunk.finish_catch_table(idx, table);
}
- fn expr_for(&mut self, name: &str, iter: &Expr, body: &Expr) {
+ fn expr_for(&mut self, name: &LStr, iter: &Expr, body: &Expr) {
// load iterable and convert to iterator
self.expr(iter);
self.emit(I::IterBegin);
@@ -498,7 +511,7 @@ impl<'a> Compiler<'a> {
self.emit(I::Nil);
}
- fn expr_lambda(&mut self, args: &[&str], body: &Expr) {
+ fn expr_lambda(&mut self, args: &[&LStr], body: &Expr) {
let mut inner = self.new_function(args);
inner.parent = Some(self);
inner.expr(body);
diff --git a/talc-lang/src/exception.rs b/talc-lang/src/exception.rs
index 67174a0..79a5adf 100644
--- a/talc-lang/src/exception.rs
+++ b/talc-lang/src/exception.rs
@@ -1,12 +1,12 @@
use std::{rc::Rc, collections::HashMap, cell::RefCell, fmt::Display};
-use crate::{value::{HashValue, Value}, symbol::{SYM_DATA, SYM_MSG, SYM_TYPE}, symbol::Symbol};
+use crate::{lstring::LStr, symbol::{Symbol, SYM_DATA, SYM_MSG, SYM_TYPE}, value::{HashValue, Value}};
pub type Result = std::result::Result;
#[derive(Clone, Debug)]
pub struct Exception {
pub ty: Symbol,
- pub msg: Option>,
+ pub msg: Option>,
pub data: Option,
}
@@ -15,7 +15,7 @@ impl Exception {
Self { ty, msg: None, data: None }
}
- pub fn new_with_msg(ty: Symbol, msg: Rc) -> Self {
+ pub fn new_with_msg(ty: Symbol, msg: Rc) -> Self {
Self { ty, msg: Some(msg), data: None }
}
@@ -23,7 +23,7 @@ impl Exception {
Self { ty, msg: None, data: Some(data) }
}
- pub fn new_with_msg_data(ty: Symbol, msg: Rc, data: Value) -> Self {
+ pub fn new_with_msg_data(ty: Symbol, msg: Rc, data: Value) -> Self {
Self { ty, msg: Some(msg), data: Some(data) }
}
@@ -79,7 +79,9 @@ macro_rules! exception {
($exc_ty:expr, $fstr:literal, $($arg:expr),*) => {
$crate::exception::Exception::new_with_msg(
$exc_ty,
- format!($fstr, $($arg),*).into()
+ $crate::lstring::LString::from(
+ format!($fstr, $($arg),*)
+ ).into()
)
};
($exc_ty:expr, $fstr:literal) => {
diff --git a/talc-lang/src/lib.rs b/talc-lang/src/lib.rs
index 6894f9b..8e2fcae 100644
--- a/talc-lang/src/lib.rs
+++ b/talc-lang/src/lib.rs
@@ -18,6 +18,7 @@ pub mod value;
pub mod exception;
pub mod chunk;
pub mod compiler;
+pub mod lstring;
pub use parser::BlockParser as Parser;
pub use vm::Vm;
diff --git a/talc-lang/src/lstring.rs b/talc-lang/src/lstring.rs
new file mode 100644
index 0000000..2ffafa8
--- /dev/null
+++ b/talc-lang/src/lstring.rs
@@ -0,0 +1,824 @@
+use std::{borrow::{Borrow, BorrowMut, Cow}, ffi::OsStr, fmt::{self, Write}, io, iter::{Copied, FusedIterator}, ops::{Add, AddAssign, Deref, DerefMut, Index, IndexMut}, rc::Rc, slice, str::Utf8Error, string::FromUtf8Error};
+
+use unicode_ident::{is_xid_continue, is_xid_start};
+
+#[macro_export]
+macro_rules! lstr {
+ ($s:literal) => {
+ $crate::lstring::LStr::from_str($s)
+ };
+}
+
+#[macro_export]
+macro_rules! lformat {
+ ($($t:tt)*) => {
+ $crate::lstring::LString::from(format!($($t)*))
+ };
+}
+
+//
+// utility
+//
+
+#[inline]
+fn is_continue(b: u8) -> bool {
+ b & 0xc0 == 0x80
+}
+
+#[inline]
+fn calc_continue(mut ch: u32, bytes: &[u8]) -> Option {
+ for b in bytes {
+ if !is_continue(*b) { return None }
+ ch = (ch << 6) | (b & 0x3f) as u32;
+ }
+ char::from_u32(ch)
+}
+
+#[inline]
+fn next_codepoint(bytes: &[u8]) -> Option<(&[u8], Result)> {
+ let init = *bytes.first()?;
+ match init {
+ 0..=0x7f => return Some((&bytes[1..], Ok(init as char))),
+ 0xc0..=0xdf => 'case: {
+ if bytes.len() < 2 { break 'case }
+ let Some(ch) = calc_continue(init as u32 & 0x1f, &bytes[1..2]) else {
+ break 'case;
+ };
+ return Some((&bytes[2..], Ok(ch)))
+ },
+ 0xe0..=0xef => 'case: {
+ if bytes.len() < 3 { break 'case }
+ let Some(ch) = calc_continue(init as u32 & 0x0f, &bytes[1..3]) else {
+ break 'case;
+ };
+ return Some((&bytes[3..], Ok(ch)))
+ },
+ 0xf0..=0xf7 => 'case: {
+ if bytes.len() < 4 { break 'case }
+ let Some(ch) = calc_continue(init as u32 & 0x07, &bytes[1..4]) else {
+ break 'case;
+ };
+ return Some((&bytes[4..], Ok(ch)))
+ }
+ _ => (),
+ };
+
+ Some((&bytes[1..], Err(init)))
+}
+
+#[inline]
+fn next_codepoint_back(bytes: &[u8]) -> Option<(&[u8], Result)> {
+ let len = bytes.len();
+ if len < 1 { return None }
+
+ let last = bytes[len-1];
+ if (0..=0x7f).contains(&last) {
+ return Some((&bytes[..len-1], Ok(last as char)))
+ }
+
+ 'case: {
+ if !is_continue(last) { break 'case }
+
+ if len < 2 { break 'case }
+ let b1 = bytes[len-2];
+ if 0xe0 & b1 == 0xc0 {
+ if let Some(ch) = calc_continue(b1 as u32 & 0x1f, &[last]) {
+ return Some((&bytes[..len-2], Ok(ch)))
+ };
+ } else if !is_continue(b1) {
+ break 'case
+ }
+
+ if len < 3 { break 'case }
+ let b2 = bytes[len-3];
+ if 0xf0 & b2 == 0xe0 {
+ if let Some(ch) = calc_continue(b2 as u32 & 0x0f, &[b1, last]) {
+ return Some((&bytes[..len-3], Ok(ch)))
+ };
+ } else if !is_continue(b2) {
+ break 'case
+ }
+
+ if len < 4 { break 'case }
+ let b3 = bytes[len-4];
+ if 0xf8 & b3 == 0xf0 {
+ if let Some(ch) = calc_continue(b3 as u32 & 0x07, &[b2, b1, last]) {
+ return Some((&bytes[..len-4], Ok(ch)))
+ };
+ }
+ }
+ Some((&bytes[..len-1], Err(last)))
+}
+
+#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[repr(transparent)]
+pub struct LString {
+ inner: Vec,
+}
+
+#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[repr(transparent)]
+pub struct LStr {
+ inner: [u8],
+}
+
+impl fmt::Debug for LStr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_char('"')?;
+ let mut bytes = &self.inner;
+ while let Some((new_bytes, res)) = next_codepoint(bytes) {
+ bytes = new_bytes;
+ match res {
+ Ok('"') => f.write_str("\\\"")?,
+ Ok('\\') => f.write_str("\\\\")?,
+ Ok('\x00') => f.write_str("\\0")?,
+ Ok('\x07') => f.write_str("\\a")?,
+ Ok('\x08') => f.write_str("\\b")?,
+ Ok('\x09') => f.write_str("\\t")?,
+ Ok('\x0a') => f.write_str("\\n")?,
+ Ok('\x0b') => f.write_str("\\v")?,
+ Ok('\x0c') => f.write_str("\\f")?,
+ Ok('\x0d') => f.write_str("\\r")?,
+ Ok('\x1b') => f.write_str("\\e")?,
+ Ok(c) if c.is_control() => write!(f, "\\u{{{:x}}}", c as u32)?,
+ Ok(c) => f.write_char(c)?,
+ Err(b) => write!(f, "\\x{b:02x}")?,
+ }
+ }
+ f.write_char('"')
+ }
+}
+
+impl fmt::Display for LStr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut bytes = &self.inner;
+ while let Some((new_bytes, res)) = next_codepoint(bytes) {
+ bytes = new_bytes;
+ if let Ok(c) = res {
+ f.write_char(c)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+impl fmt::Debug for LString {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(self.as_ref(), f)
+ }
+}
+
+impl fmt::Display for LString {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self.as_ref(), f)
+ }
+}
+
+//
+// deref, asref, borrow
+//
+
+impl Deref for LString {
+ type Target = LStr;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ <&LStr as From<&[u8]>>::from(self.inner.as_ref())
+ }
+}
+
+impl DerefMut for LString {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ <&mut LStr as From<&mut [u8]>>::from(self.inner.as_mut())
+ }
+}
+
+impl AsRef for LString {
+ #[inline]
+ fn as_ref(&self) -> &LStr {
+ <&LStr as From<&[u8]>>::from(self.inner.as_ref())
+ }
+}
+
+impl AsMut for LString {
+ #[inline]
+ fn as_mut(&mut self) -> &mut LStr {
+ <&mut LStr as From<&mut [u8]>>::from(self.inner.as_mut())
+ }
+}
+
+impl Borrow for LString {
+ #[inline]
+ fn borrow(&self) -> &LStr {
+ <&LStr as From<&[u8]>>::from(self.inner.as_ref())
+ }
+}
+
+impl BorrowMut for LString {
+ #[inline]
+ fn borrow_mut(&mut self) -> &mut LStr {
+ <&mut LStr as From<&mut [u8]>>::from(self.inner.as_mut())
+ }
+}
+
+//
+// conversions
+//
+
+impl From for Vec {
+ #[inline]
+ fn from(value: LString) -> Self { value.inner }
+}
+
+impl From> for LString {
+ #[inline]
+ fn from(value: Vec) -> Self { Self { inner: value } }
+}
+
+impl From for LString {
+ #[inline]
+ fn from(value: String) -> Self { Self { inner: value.into_bytes() } }
+}
+
+impl From<&LStr> for LString {
+ #[inline]
+ fn from(value: &LStr) -> Self { value.to_owned() }
+}
+
+impl From<&str> for LString {
+ #[inline]
+ fn from(value: &str) -> Self { value.to_owned().into() }
+}
+
+impl From<&[u8]> for LString {
+ #[inline]
+ fn from(value: &[u8]) -> Self { value.to_owned().into() }
+}
+
+impl From> for LString {
+ #[inline]
+ fn from(value: Cow<'_, LStr>) -> Self {
+ match value {
+ Cow::Borrowed(b) => b.to_owned(),
+ Cow::Owned(o) => o
+ }
+ }
+}
+
+impl From> for LString {
+ #[inline]
+ fn from(value: Cow<'_, str>) -> Self {
+ value.into_owned().into()
+ }
+}
+
+impl From> for LString {
+ #[inline]
+ fn from(value: Cow<'_, [u8]>) -> Self {
+ value.into_owned().into()
+ }
+}
+
+impl<'a> From<&'a LStr> for &'a [u8] {
+ #[inline]
+ fn from(value: &'a LStr) -> Self { &value.inner }
+}
+
+impl<'a> From<&'a [u8]> for &'a LStr {
+ #[inline]
+ fn from(value: &'a [u8]) -> Self {
+ LStr::from_bytes(value)
+ }
+}
+
+impl<'a> From<&'a str> for &'a LStr {
+ #[inline]
+ fn from(value: &'a str) -> Self {
+ LStr::from_str(value)
+ }
+}
+
+impl<'a> From<&'a mut LStr> for &'a mut [u8] {
+ #[inline]
+ fn from(value: &'a mut LStr) -> Self { &mut value.inner }
+}
+
+impl<'a> From<&'a mut [u8]> for &'a mut LStr {
+ #[inline]
+ fn from(value: &'a mut [u8]) -> Self {
+ LStr::from_bytes_mut(value)
+ }
+}
+
+impl<'a> From<&'a LString> for &'a LStr {
+ #[inline]
+ fn from(value: &'a LString) -> Self { value }
+}
+
+impl From<&LStr> for Rc {
+ #[inline]
+ fn from(v: &LStr) -> Rc {
+ let arc = Rc::<[u8]>::from(v.as_bytes());
+ unsafe { Rc::from_raw(Rc::into_raw(arc) as *const LStr) }
+ }
+}
+
+impl From for Rc {
+ #[inline]
+ fn from(v: LString) -> Rc {
+ Rc::from(&v[..])
+ }
+}
+
+impl ToOwned for LStr {
+ type Owned = LString;
+
+ #[inline]
+ fn to_owned(&self) -> Self::Owned {
+ self.as_bytes().to_owned().into()
+ }
+}
+
+impl TryFrom for String {
+ type Error = FromUtf8Error;
+
+ #[inline]
+ fn try_from(value: LString) -> Result {
+ String::from_utf8(value.into())
+ }
+}
+
+impl<'a> TryFrom<&'a LStr> for &'a str {
+ type Error = Utf8Error;
+
+ #[inline]
+ fn try_from(value: &'a LStr) -> Result {
+ std::str::from_utf8(&value.inner)
+ }
+}
+
+impl From for LString {
+ #[inline]
+ fn from(value: char) -> Self {
+ value.to_string().into()
+ }
+}
+
+impl From for LString {
+ #[inline]
+ fn from(value: u8) -> Self {
+ vec![value].into()
+ }
+}
+
+impl FromIterator for LString {
+ #[inline]
+ fn from_iter>(iter: I) -> Self {
+ String::from_iter(iter).into()
+ }
+}
+
+impl FromIterator for LString {
+ #[inline]
+ fn from_iter>(iter: T) -> Self {
+ Vec::from_iter(iter).into()
+ }
+}
+
+impl Extend for LString {
+ #[inline]
+ fn extend>(&mut self, iter: I) {
+ self.inner.extend(iter);
+ }
+}
+
+impl<'a> Extend<&'a u8> for LString {
+ #[inline]
+ fn extend>(&mut self, iter: I) {
+ self.inner.extend(iter);
+ }
+}
+
+impl Extend for LString {
+ #[inline]
+ fn extend>(&mut self, iter: I) {
+ let iter = iter.into_iter();
+ let (lo, _) = iter.size_hint();
+ self.reserve(lo);
+ iter.for_each(move |ch| self.push_char(ch));
+ }
+}
+
+impl<'a> Extend<&'a char> for LString {
+ #[inline]
+ fn extend>(&mut self, iter: I) {
+ let iter = iter.into_iter();
+ let (lo, _) = iter.size_hint();
+ self.reserve(lo);
+ iter.for_each(move |ch| self.push_char(*ch));
+ }
+}
+
+//
+// write
+//
+
+impl io::Write for LString {
+ fn write(&mut self, buf: &[u8]) -> io::Result {
+ self.extend(buf);
+ Ok(buf.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> { Ok(()) }
+}
+
+//impl fmt::Write for LString {
+// fn write_str(&mut self, s: &str) -> fmt::Result {
+// self.extend(s.as_bytes());
+// Ok(())
+// }
+//}
+
+//
+// methods
+//
+
+impl LString {
+ #[inline]
+ pub const fn new() -> Self {
+ Self { inner: Vec::new() }
+ }
+
+ #[inline]
+ pub fn with_capacity(capacity: usize) -> Self {
+ Vec::with_capacity(capacity).into()
+ }
+
+ #[inline]
+ pub fn reserve(&mut self, additional: usize) {
+ self.inner.reserve(additional);
+ }
+
+ #[inline]
+ pub fn push_byte(&mut self, byte: u8) {
+ self.inner.push(byte);
+ }
+
+ #[inline]
+ pub fn push_bytes(&mut self, bytes: &[u8]) {
+ self.inner.extend_from_slice(bytes);
+ }
+
+ #[inline]
+ pub fn push_char(&mut self, ch: char) {
+ let mut buf = [0; 5];
+ self.push_bytes(ch.encode_utf8(&mut buf).as_bytes());
+ }
+
+ #[inline]
+ pub fn push_lstr(&mut self, lstring: &LStr) {
+ self.push_bytes(lstring.as_bytes());
+ }
+
+ #[inline]
+ pub fn push_str(&mut self, string: &str) {
+ self.push_bytes(string.as_bytes());
+ }
+
+ #[inline]
+ pub fn clear(&mut self) {
+ self.inner.clear();
+ }
+
+ #[inline]
+ pub fn leak<'a>(self) -> &'a mut LStr {
+ self.inner.leak().into()
+ }
+
+ #[inline]
+ pub fn into_string(self) -> Result {
+ String::from_utf8(self.inner)
+ }
+}
+
+#[derive(Clone)]
+pub struct Bytes<'a>(Copied>);
+
+impl<'a> Iterator for Bytes<'a> {
+ type Item = u8;
+
+ #[inline]
+ fn next(&mut self) -> Option { self.0.next() }
+ #[inline]
+ fn size_hint(&self) -> (usize, Option) { self.0.size_hint() }
+ #[inline]
+ fn count(self) -> usize { self.0.count() }
+ #[inline]
+ fn last(self) -> Option { self.0.last() }
+ #[inline]
+ fn nth(&mut self, n: usize) -> Option { self.0.nth(n) }
+ #[inline]
+ fn all bool>(&mut self, f: F) -> bool {
+ self.0.all(f)
+ }
+ #[inline]
+ fn any bool>(&mut self, f: F) -> bool {
+ self.0.any(f)
+ }
+ #[inline]
+ fn find bool>(&mut self, predicate: P) -> Option {
+ self.0.find(predicate)
+ }
+ #[inline]
+ fn position bool>(&mut self, predicate: P) -> Option {
+ self.0.position(predicate)
+ }
+}
+
+impl<'a> ExactSizeIterator for Bytes<'a> {
+ #[inline]
+ fn len(&self) -> usize { self.0.len() }
+}
+
+impl<'a> FusedIterator for Bytes<'a> {}
+
+#[derive(Clone)]
+pub struct LosslessChars<'a>(&'a [u8]);
+
+impl<'a> Iterator for LosslessChars<'a> {
+ type Item = Result;
+
+ #[inline]
+ fn next(&mut self) -> Option {
+ let (new_bytes, res) = next_codepoint(self.0)?;
+ self.0 = new_bytes;
+ Some(res)
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option) {
+ let len = self.0.len();
+ ((len + 3)/4, Some(len))
+ }
+}
+
+impl<'a> DoubleEndedIterator for LosslessChars<'a> {
+ fn next_back(&mut self) -> Option {
+ let (new_bytes, res) = next_codepoint_back(self.0)?;
+ self.0 = new_bytes;
+ Some(res)
+ }
+}
+
+
+#[derive(Clone)]
+pub struct Chars<'a>(LosslessChars<'a>);
+
+impl<'a> Iterator for Chars<'a> {
+ type Item = char;
+
+ #[inline]
+ fn next(&mut self) -> Option {
+ loop {
+ if let Ok(c) = self.0.next()? {
+ return Some(c)
+ }
+ }
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option) {
+ self.0.size_hint()
+ }
+}
+
+impl<'a> DoubleEndedIterator for Chars<'a> {
+ fn next_back(&mut self) -> Option {
+ loop {
+ if let Ok(c) = self.0.next_back()? {
+ return Some(c)
+ }
+ }
+ }
+}
+
+
+impl LStr {
+ #[inline]
+ pub const fn from_str(string: &str) -> &Self {
+ Self::from_bytes(string.as_bytes())
+ }
+
+ #[inline]
+ pub const fn from_bytes(bytes: &[u8]) -> &Self {
+ unsafe { &*(bytes as *const [u8] as *const LStr) }
+ }
+
+ #[inline]
+ pub fn from_bytes_mut(bytes: &mut [u8]) -> &mut Self {
+ unsafe { &mut *(bytes as *mut [u8] as *mut LStr) }
+ }
+
+ #[inline]
+ pub const fn as_bytes(&self) -> &[u8] { &self.inner }
+
+ #[inline]
+ pub fn as_bytes_mut(&mut self) -> &mut [u8] { &mut self.inner }
+
+ #[inline]
+ pub fn bytes(&self) -> Bytes {
+ Bytes(self.as_bytes().iter().copied())
+ }
+ #[inline]
+ pub fn chars(&self) -> Chars {
+ Chars(self.chars_lossless())
+ }
+ #[inline]
+ pub fn chars_lossless(&self) -> LosslessChars {
+ LosslessChars(self.as_bytes())
+ }
+
+ #[inline]
+ pub const fn to_str(&self) -> Result<&str, Utf8Error> {
+ std::str::from_utf8(&self.inner)
+ }
+
+ #[inline]
+ pub const fn len(&self) -> usize {
+ self.inner.len()
+ }
+
+ #[inline]
+ pub const fn is_empty(&self) -> bool {
+ self.inner.is_empty()
+ }
+
+ #[inline]
+ pub const fn is_utf8(&self) -> bool {
+ self.to_str().is_ok()
+ }
+
+ #[inline]
+ pub fn to_utf8_lossy(&self) -> Cow<'_, LStr> {
+ match String::from_utf8_lossy(self.as_bytes()) {
+ Cow::Borrowed(b) => Cow::Borrowed(b.into()),
+ Cow::Owned(o) => Cow::Owned(o.into()),
+ }
+ }
+
+ #[inline]
+ pub fn to_os_str(&self) -> Cow {
+ #[cfg(unix)] {
+ use std::os::unix::ffi::OsStrExt;
+ Cow::Borrowed(OsStr::from_bytes(self.as_bytes()))
+ }
+ #[cfg(not(unix))] {
+ Cow::Owned(self.to_string().into())
+ }
+ }
+
+ fn convert_while_ascii(&self, f: impl Fn(&u8) -> u8) -> LString {
+ let mut out = LString::new();
+ for b in self.bytes() {
+ if !(0..=0x7f).contains(&b) {
+ break
+ }
+ out.push_byte(f(&b));
+ }
+ out
+ }
+
+ pub fn to_uppercase(&self) -> LString {
+ let mut out = self.convert_while_ascii(u8::to_ascii_uppercase);
+ for ch in self[out.len()..].chars_lossless() {
+ match ch {
+ Ok(c) => out.extend(c.to_uppercase()),
+ Err(b) => out.push_byte(b),
+ }
+ }
+ out
+ }
+
+ pub fn to_lowercase(&self) -> LString {
+ let mut out = self.convert_while_ascii(u8::to_ascii_lowercase);
+ for ch in self[out.len()..].chars_lossless() {
+ match ch {
+ Ok(c) => out.extend(c.to_lowercase()),
+ Err(b) => out.push_byte(b),
+ }
+ }
+ out
+ }
+
+ pub fn trim(&self) -> &LStr {
+ self.trim_by(char::is_whitespace)
+ }
+
+ pub fn trim_by(&self, pattern: impl Fn(char) -> bool) -> &LStr {
+ let mut start = 0;
+ for ch in self.chars_lossless() {
+ if !ch.is_ok_and(&pattern) {
+ break
+ }
+ start += ch.map_or_else(|_| 1, char::len_utf8);
+ }
+ if start == self.len() {
+ return &self[0..0]
+ }
+ let mut end = self.len();
+ for ch in self.chars_lossless().rev() {
+ if !ch.is_ok_and(&pattern) {
+ break
+ }
+ end -= ch.map_or_else(|_| 1, char::len_utf8);
+ }
+ &self[start..end]
+ }
+
+ pub fn starts_with(&self, s: &LStr) -> bool {
+ self.as_bytes().starts_with(s.as_bytes())
+ }
+
+ pub fn ends_with(&self, s: &LStr) -> bool {
+ self.as_bytes().ends_with(s.as_bytes())
+ }
+
+ pub fn is_identifier(&self) -> bool {
+ let mut chars = self.chars_lossless();
+ let first = chars.next()
+ .is_some_and(|ch| ch.is_ok_and(is_xid_start));
+ if !first {
+ return false
+ }
+ chars.all(|ch| ch.is_ok_and(is_xid_continue))
+ }
+}
+
+impl Add<&LStr> for LString {
+ type Output = Self;
+
+ #[inline]
+ fn add(mut self, rhs: &LStr) -> Self::Output {
+ self.push_lstr(rhs);
+ self
+ }
+}
+
+impl AddAssign<&LStr> for LString {
+ #[inline]
+ fn add_assign(&mut self, rhs: &LStr) {
+ self.push_lstr(rhs);
+ }
+}
+
+impl Default for &LStr {
+ fn default() -> Self { [].as_ref().into() }
+}
+
+impl Default for &mut LStr {
+ fn default() -> Self { [].as_mut().into() }
+}
+
+macro_rules! impl_index {
+ ($ty:ty) => {
+ impl Index<$ty> for LStr {
+ type Output = LStr;
+
+ fn index(&self, index: $ty) -> &Self::Output {
+ self.inner.index(index).into()
+ }
+ }
+
+ impl IndexMut<$ty> for LStr {
+ fn index_mut(&mut self, index: $ty) -> &mut LStr {
+ self.inner.index_mut(index).into()
+ }
+ }
+
+ impl Index<$ty> for LString {
+ type Output = LStr;
+
+ fn index(&self, index: $ty) -> &Self::Output {
+ self.inner.index(index).into()
+ }
+ }
+
+ impl IndexMut<$ty> for LString {
+ fn index_mut(&mut self, index: $ty) -> &mut LStr {
+ self.inner.index_mut(index).into()
+ }
+ }
+ };
+}
+
+impl_index!(std::ops::Range);
+impl_index!(std::ops::RangeFrom);
+impl_index!(std::ops::RangeFull);
+impl_index!(std::ops::RangeInclusive);
+impl_index!(std::ops::RangeTo);
+impl_index!(std::ops::RangeToInclusive);
+impl_index!((std::ops::Bound, std::ops::Bound));
+
diff --git a/talc-lang/src/parser.lalrpop b/talc-lang/src/parser.lalrpop
index d59c7c0..985b238 100644
--- a/talc-lang/src/parser.lalrpop
+++ b/talc-lang/src/parser.lalrpop
@@ -3,6 +3,8 @@ use crate::ast::*;
use crate::value::Value;
use crate::symbol::Symbol;
use crate::parser_util::*;
+use crate::lstring::LStr;
+use crate::lstr;
use num_complex::Complex64;
grammar;
@@ -100,8 +102,8 @@ Assign: Box> = {
LValue: Box> = {
=> Box::new(LValue::Ident(<>)),
- "!" => Box::new(LValue::Index(l, r)),
- "." => Box::new(LValue::Index(l, Box::new(Expr::Literal(Value::Symbol(Symbol::get(r)))))),
+ "!" => Box::new(LValue::Index(l, r)),
+ "." => Box::new(LValue::Index(l, Box::new(Expr::Literal(Value::Symbol(Symbol::get(r)))))),
}
AssignOp: Option = {
@@ -153,8 +155,8 @@ Pipe: Box> = {
Lambda: Box> = {
"\\" "->" => Box::new(Expr::Lambda(xs, e)),
- ":" => Box::new(Expr::Lambda(vec!["$"], e)),
- "::" => Box::new(Expr::Lambda(vec!["$", "$$"], e)),
+ ":" => Box::new(Expr::Lambda(vec![lstr!("$")], e)),
+ "::" => Box::new(Expr::Lambda(vec![lstr!("$"), lstr!("$$")], e)),
BinaryCompare,
}
@@ -194,16 +196,43 @@ BinaryAppend: Box> = {
// .. ..= ..*
BinaryRange: Box> = {
- => Box::new(Expr::BinaryOp(o, l, r)),
- "..*" => Box::new(Expr::UnaryOp(UnaryOp::RangeEndless, l)),
- BinaryAdd,
+ => Box::new(Expr::BinaryOp(o, l, r)),
+ "..*" => Box::new(Expr::UnaryOp(UnaryOp::RangeEndless, l)),
+ BinaryBitOr,
}
RangeOp: BinaryOp = {
".." => BinaryOp::Range,
"..=" => BinaryOp::RangeIncl,
}
+// #|
+BinaryBitOr: Box> = {
+ "#|" => Box::new(Expr::BinaryOp(BinaryOp::BitOr, l, r)),
+ BinaryBitXor,
+}
+// #^
+BinaryBitXor: Box> = {
+ "#^" => Box::new(Expr::BinaryOp(BinaryOp::BitXor, l, r)),
+ BinaryBitAnd,
+}
+
+// #&
+BinaryBitAnd: Box> = {
+ "#&" => Box::new(Expr::BinaryOp(BinaryOp::BitAnd, l, r)),
+ BinaryShift,
+}
+
+// >> <<
+BinaryShift: Box> = {
+ => Box::new(Expr::BinaryOp(o, l, r)),
+ BinaryAdd,
+}
+
+ShiftOp: BinaryOp = {
+ ">>" => BinaryOp::Shr,
+ "<<" => BinaryOp::Shl,
+}
// + -
BinaryAdd: Box> = {
@@ -248,13 +277,13 @@ BinaryPow: Box> = {
// index ( ! )
BinaryIndex: Box> = {
"!" => Box::new(Expr::Index(l, r)),
- FunctionCall,
+ CallOrAccess,
}
// unary-
UnaryMinus2: Box> = {
"-" => Box::new(Expr::UnaryOp(UnaryOp::Neg, r)),
- FunctionCall,
+ CallOrAccess,
}
@@ -262,21 +291,14 @@ UnaryMinus2: Box> = {
// things
//
-
// function call
-FunctionCall: Box> = {
- "(" ")" => Box::new(Expr::FnCall(l, r)),
- FieldAccess,
-}
-
-
-// field access
-FieldAccess: Box> = {
- "." => Box::new(Expr::Index(l, Box::new(Expr::Literal(Value::Symbol(Symbol::get(r)))))),
+CallOrAccess: Box> = {
+ "(" ")" => Box::new(Expr::FnCall(l, args)),
+ "->" "(" ")" => Box::new(Expr::AssocFnCall(l, Symbol::get(r), args)),
+ "." => Box::new(Expr::Index(l, Box::new(Expr::Literal(Value::Symbol(Symbol::get(r)))))),
Term,
}
-
//
// base
//
@@ -291,8 +313,8 @@ TermNotIdent: Box> = {
"(" ")" => <>,
"[" "]" => Box::new(Expr::List(<>)),
"{" "}" => Box::new(Expr::Table(<>)),
- "$" => Box::new(Expr::Ident("$")),
- "$$" => Box::new(Expr::Ident("$$")),
+ "$" => Box::new(Expr::Ident(lstr!("$"))),
+ "$$" => Box::new(Expr::Ident(lstr!("$$"))),
"do" "end" => <>,
"if" => <>,
@@ -358,12 +380,12 @@ TableKey: Expr<'input> = {
TermNotIdent => *<>,
}
-IdentList: Vec<&'input str> = {
+IdentList: Vec<&'input LStr> = {
",")*>
=> { if let Some(x) = x { xs.push(x) }; xs }
}
-Identifier: &'input str = TokIdentifier;
+Identifier: &'input LStr = TokIdentifier => <>.into();
//
@@ -404,8 +426,8 @@ Literal: Value = {
"nil" => Value::Nil,
}
-StringLiteral: Rc = {
- TokStringSingle => <>[1..<>.len()-1].into(),
+StringLiteral: Rc = {
+ TokStringSingle => LStr::from_str(&<>[1..<>.len()-1]).into(),
TokStringDouble =>? parse_str_escapes(&<>[1..<>.len()-1])
.map(|s| s.into())
.map_err(|e| ParseError::from(e).into()),
diff --git a/talc-lang/src/parser_util.rs b/talc-lang/src/parser_util.rs
index d53def7..8193db8 100644
--- a/talc-lang/src/parser_util.rs
+++ b/talc-lang/src/parser_util.rs
@@ -2,6 +2,8 @@ use std::num::{ParseIntError, ParseFloatError};
use thiserror::Error;
+use crate::lstring::{LStr, LString};
+
#[derive(Clone, Debug, Error)]
pub enum ParseError {
#[error("{0}")]
@@ -32,31 +34,30 @@ pub enum StrEscapeError {
CodepointTooLarge,
}
-pub fn parse_str_escapes(src: &str) -> Result {
- let mut s = String::with_capacity(src.len());
+pub fn parse_str_escapes(src: &str) -> Result {
+ let mut s = LString::with_capacity(src.len());
let mut chars = src.chars();
while let Some(c) = chars.next() {
- if c != '\\' { s.push(c); continue }
+ if c != '\\' { s.push_char(c); continue }
let c = chars.next().ok_or(StrEscapeError::Eof)?;
match c {
- '"' | '\'' | '\\' => s.push(c),
- '0' => s.push('\0'),
- 'a' => s.push('\x07'),
- 'b' => s.push('\x08'),
- 't' => s.push('\t'),
- 'n' => s.push('\n'),
- 'v' => s.push('\x0b'),
- 'f' => s.push('\x0c'),
- 'r' => s.push('\r'),
- 'e' => s.push('\x1b'),
+ '"' | '\'' | '\\' => s.push_char(c),
+ '0' => s.push_char('\0'),
+ 'a' => s.push_char('\x07'),
+ 'b' => s.push_char('\x08'),
+ 't' => s.push_char('\t'),
+ 'n' => s.push_char('\n'),
+ 'v' => s.push_char('\x0b'),
+ 'f' => s.push_char('\x0c'),
+ 'r' => s.push_char('\r'),
+ 'e' => s.push_char('\x1b'),
'x' => {
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n1 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n2 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
- // can't panic: all bytes 0..256 are valid codepoints
- s.push(char::from_u32(n1 * 16 + n2).unwrap());
+ s.push_byte((n1 * 16 + n2) as u8);
},
'u' => {
let Some('{') = chars.next() else {
@@ -74,7 +75,7 @@ pub fn parse_str_escapes(src: &str) -> Result {
n = n * 16 + c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
}
let ch = char::from_u32(n).ok_or(StrEscapeError::InvalidCodepoint(n))?;
- s.push(ch);
+ s.push_char(ch);
},
c => return Err(StrEscapeError::Invalid(c)),
@@ -84,9 +85,9 @@ pub fn parse_str_escapes(src: &str) -> Result {
Ok(s)
}
-pub fn parse_float(f: &str) -> Result {
+pub fn parse_float<'a, S: Into<&'a LStr>>(f: S) -> Result {
let mut s = String::new();
- for c in f.chars() {
+ for c in f.into().chars() {
if c != '_' {
s.push(c);
}
@@ -94,9 +95,9 @@ pub fn parse_float(f: &str) -> Result {
s.parse()
}
-pub fn parse_int(f: &str, radix: u32) -> Result {
+pub fn parse_int<'a, S: Into<&'a LStr>>(f: S, radix: u32) -> Result {
let mut s = String::new();
- for c in f.chars() {
+ for c in f.into().chars() {
if c != '_' {
s.push(c);
}
diff --git a/talc-lang/src/symbol.rs b/talc-lang/src/symbol.rs
index 8f8d22d..c96ec07 100644
--- a/talc-lang/src/symbol.rs
+++ b/talc-lang/src/symbol.rs
@@ -4,8 +4,8 @@ use lazy_static::lazy_static;
#[derive(Default)]
struct SymbolTable {
- names: Vec<&'static str>,
- values: HashMap<&'static str, Symbol>
+ names: Vec<&'static LStr>,
+ values: HashMap<&'static LStr, Symbol>
}
lazy_static! {
@@ -53,14 +53,16 @@ fn get_table() -> &'static Mutex {
impl Symbol {
/// # Panics
/// If the mutex storing the symbol table is poisoned
- pub fn try_get(name: &str) -> Option {
+ pub fn try_get<'a, S: Into<&'a LStr>>(name: S) -> Option {
+ let name = name.into();
let table = get_table().lock().expect("couldn't lock symbol table");
table.values.get(name).copied()
}
/// # Panics
/// If the mutex storing the symbol table is poisoned
- pub fn get(name: &str) -> Self {
+ pub fn get<'a, S: Into<&'a LStr>>(name: S) -> Self {
+ let name = name.into();
let mut table = get_table().lock().expect("couldn't lock symbol table");
if let Some(sym) = table.values.get(&name) {
@@ -71,7 +73,7 @@ impl Symbol {
assert!(symno <= MAX_SYMBOL, "too many symbols");
let sym = Symbol(symno as u32);
- let name = String::leak(name.to_owned());
+ let name = LString::leak(name.to_owned());
table.names.push(name);
table.values.insert(name, sym);
@@ -102,9 +104,9 @@ impl Symbol {
/// # Panics
/// If the mutex storing the symbol table is poisoned
- pub fn name(self) -> &'static str {
+ pub fn name(self) -> &'static LStr {
let table = get_table().lock().expect("couldn't lock symbol table");
- table.names.get(self.0 as usize).cloned().expect("symbol does not exist")
+ table.names.get(self.0 as usize).copied().expect("symbol does not exist")
}
}
@@ -119,3 +121,6 @@ macro_rules! symbol {
}
pub use symbol;
+
+use crate::lstring::{LStr, LString};
+
diff --git a/talc-lang/src/value/function.rs b/talc-lang/src/value/function.rs
index afdda20..ca38366 100644
--- a/talc-lang/src/value/function.rs
+++ b/talc-lang/src/value/function.rs
@@ -2,7 +2,7 @@ use std::rc::Rc;
use crate::{chunk::Chunk, Vm, exception::Result};
-use super::{Value};
+use super::Value;
#[derive(Clone, Copy, Debug, Default)]
pub struct FuncAttrs {
diff --git a/talc-lang/src/value/index.rs b/talc-lang/src/value/index.rs
index 835eb48..4abc138 100644
--- a/talc-lang/src/value/index.rs
+++ b/talc-lang/src/value/index.rs
@@ -30,6 +30,40 @@ impl Value {
let i = i.try_into()?;
Ok(t.get(&i).cloned().unwrap_or(Value::Nil))
},
+ (V::String(s), V::Range(r)) => {
+ let slen = s.len();
+ match r.ty {
+ RangeType::Open => {
+ if r.start < 0 || r.start > slen as i64 {
+ throw!(*SYM_INDEX_ERROR,
+ "index {} out of bounds for string of length {}", r.stop, slen)
+ }
+ if r.stop < 0 || r.stop > slen as i64 {
+ throw!(*SYM_INDEX_ERROR,
+ "index {} out of bounds for string of length {}", r.stop, slen)
+ }
+ Ok(s[r.start as usize..r.stop as usize].into())
+ },
+ RangeType::Closed => {
+ if r.start < 0 || r.start > slen as i64 {
+ throw!(*SYM_INDEX_ERROR,
+ "index {} out of bounds for string of length {}", r.stop, slen)
+ }
+ if r.stop < 0 || r.stop >= slen as i64 {
+ throw!(*SYM_INDEX_ERROR,
+ "index {} out of bounds for string of length {}", r.stop, slen)
+ }
+ Ok(s[r.start as usize..=r.stop as usize].into())
+ },
+ RangeType::Endless => {
+ if r.start < 0 || r.start > slen as i64 {
+ throw!(*SYM_INDEX_ERROR,
+ "index {} out of bounds for string of length {}", r.stop, slen)
+ }
+ Ok(s[r.start as usize..].into())
+ },
+ }
+ },
(col, idx) => if let Ok(ii) = idx.clone().to_iter_function() {
let col = col.clone();
let func = move |vm: &mut Vm, _| {
diff --git a/talc-lang/src/value/mod.rs b/talc-lang/src/value/mod.rs
index cee77e5..fe2020e 100644
--- a/talc-lang/src/value/mod.rs
+++ b/talc-lang/src/value/mod.rs
@@ -1,8 +1,13 @@
+use std::any::Any;
+use std::borrow::Cow;
+use std::io;
use std::{cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, rc::Rc};
pub use num_complex::Complex64;
+use num_complex::ComplexFloat;
pub use num_rational::Rational64;
+use crate::lstring::{LStr, LString};
use crate::symbol::{Symbol, SYM_HASH_ERROR};
use crate::exception::{Exception, throw};
@@ -30,64 +35,160 @@ pub enum Value {
Complex(Complex64),
Cell(Rc>),
- String(Rc),
+ String(Rc),
List(RcList),
Table(RcTable),
Function(Rc),
NativeFunc(Rc),
+
+ Native(Rc),
+}
+
+pub trait NativeValue: std::fmt::Debug + Any {
+ fn get_type(&self) -> Symbol;
+ fn as_any(&self) -> &dyn Any;
+
+ fn to_lstring(&self, w: &mut LString, _repr: bool) -> io::Result<()> {
+ w.extend(b"");
+ Ok(())
+ }
+ fn partial_eq(&self, _other: &Value) -> bool {
+ false
+ }
+ fn copy_value(&self) -> Result