├── .gitignore ├── .vscode └── launch.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── src ├── data.rs ├── eval.rs ├── exec.rs ├── lexer.rs ├── main.rs └── parser │ ├── expr.rs │ ├── ind.rs │ └── mod.rs ├── testcases ├── even.mtk ├── imp.mtk ├── nat.mtk ├── str.mtk └── vector.mtk └── tools └── vscode ├── .vscodeignore ├── language-configuration.json ├── package.json └── syntaxes └── meowthink.tmLanguage.json /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}/tools/vscode" 14 | ] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "MeowThink" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "anyhow", 10 | "im", 11 | "log", 12 | "paw", 13 | "simplelog", 14 | "structopt", 15 | "thiserror", 16 | ] 17 | 18 | [[package]] 19 | name = "ansi_term" 20 | version = "0.11.0" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 23 | dependencies = [ 24 | "winapi", 25 | ] 26 | 27 | [[package]] 28 | name = "anyhow" 29 | version = "1.0.49" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "0a03e93e97a28fbc9f42fbc5ba0886a3c67eb637b476dbee711f80a6ffe8223d" 32 | 33 | [[package]] 34 | name = "atty" 35 | version = "0.2.14" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 38 | dependencies = [ 39 | "hermit-abi", 40 | "libc", 41 | "winapi", 42 | ] 43 | 44 | [[package]] 45 | name = "autocfg" 46 | version = "1.0.1" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 49 | 50 | [[package]] 51 | name = "bitflags" 52 | version = "1.3.2" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 55 | 56 | [[package]] 57 | name = "bitmaps" 58 | version = "2.1.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" 61 | dependencies = [ 62 | "typenum", 63 | ] 64 | 65 | [[package]] 66 | name = "cfg-if" 67 | version = "1.0.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 70 | 71 | [[package]] 72 | name = "chrono" 73 | version = "0.4.19" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 76 | dependencies = [ 77 | "libc", 78 | "num-integer", 79 | "num-traits", 80 | "time", 81 | "winapi", 82 | ] 83 | 84 | [[package]] 85 | name = "clap" 86 | version = "2.33.3" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 89 | dependencies = [ 90 | "ansi_term", 91 | "atty", 92 | "bitflags", 93 | "strsim", 94 | "textwrap", 95 | "unicode-width", 96 | "vec_map", 97 | ] 98 | 99 | [[package]] 100 | name = "heck" 101 | version = "0.3.3" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 104 | dependencies = [ 105 | "unicode-segmentation", 106 | ] 107 | 108 | [[package]] 109 | name = "hermit-abi" 110 | version = "0.1.19" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 113 | dependencies = [ 114 | "libc", 115 | ] 116 | 117 | [[package]] 118 | name = "im" 119 | version = "15.0.0" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "111c1983f3c5bb72732df25cddacee9b546d08325fb584b5ebd38148be7b0246" 122 | dependencies = [ 123 | "bitmaps", 124 | "rand_core", 125 | "rand_xoshiro", 126 | "sized-chunks", 127 | "typenum", 128 | "version_check", 129 | ] 130 | 131 | [[package]] 132 | name = "lazy_static" 133 | version = "1.4.0" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 136 | 137 | [[package]] 138 | name = "libc" 139 | version = "0.2.108" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" 142 | 143 | [[package]] 144 | name = "log" 145 | version = "0.4.14" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 148 | dependencies = [ 149 | "cfg-if", 150 | ] 151 | 152 | [[package]] 153 | name = "num-integer" 154 | version = "0.1.44" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 157 | dependencies = [ 158 | "autocfg", 159 | "num-traits", 160 | ] 161 | 162 | [[package]] 163 | name = "num-traits" 164 | version = "0.2.14" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 167 | dependencies = [ 168 | "autocfg", 169 | ] 170 | 171 | [[package]] 172 | name = "paris" 173 | version = "1.5.8" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "a25b44bc50356452ab4fdcdd6d6b0f70eddc4f9fd16c9ba142f69e74fb195165" 176 | 177 | [[package]] 178 | name = "paw" 179 | version = "1.0.0" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "09c0fc9b564dbc3dc2ed7c92c0c144f4de340aa94514ce2b446065417c4084e9" 182 | dependencies = [ 183 | "paw-attributes", 184 | "paw-raw", 185 | ] 186 | 187 | [[package]] 188 | name = "paw-attributes" 189 | version = "1.0.2" 190 | source = "registry+https://github.com/rust-lang/crates.io-index" 191 | checksum = "0f35583365be5d148e959284f42526841917b7bfa09e2d1a7ad5dde2cf0eaa39" 192 | dependencies = [ 193 | "proc-macro2", 194 | "quote", 195 | "syn", 196 | ] 197 | 198 | [[package]] 199 | name = "paw-raw" 200 | version = "1.0.0" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | checksum = "7f0b59668fe80c5afe998f0c0bf93322bf2cd66cafeeb80581f291716f3467f2" 203 | 204 | [[package]] 205 | name = "proc-macro-error" 206 | version = "1.0.4" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 209 | dependencies = [ 210 | "proc-macro-error-attr", 211 | "proc-macro2", 212 | "quote", 213 | "syn", 214 | "version_check", 215 | ] 216 | 217 | [[package]] 218 | name = "proc-macro-error-attr" 219 | version = "1.0.4" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 222 | dependencies = [ 223 | "proc-macro2", 224 | "quote", 225 | "version_check", 226 | ] 227 | 228 | [[package]] 229 | name = "proc-macro2" 230 | version = "1.0.32" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" 233 | dependencies = [ 234 | "unicode-xid", 235 | ] 236 | 237 | [[package]] 238 | name = "quote" 239 | version = "1.0.10" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" 242 | dependencies = [ 243 | "proc-macro2", 244 | ] 245 | 246 | [[package]] 247 | name = "rand_core" 248 | version = "0.5.1" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 251 | 252 | [[package]] 253 | name = "rand_xoshiro" 254 | version = "0.4.0" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004" 257 | dependencies = [ 258 | "rand_core", 259 | ] 260 | 261 | [[package]] 262 | name = "simplelog" 263 | version = "0.11.1" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "ecabc0118918611790b8615670ab79296272cbe09496b6884b02b1e929c20886" 266 | dependencies = [ 267 | "chrono", 268 | "log", 269 | "paris", 270 | "termcolor", 271 | ] 272 | 273 | [[package]] 274 | name = "sized-chunks" 275 | version = "0.6.5" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" 278 | dependencies = [ 279 | "bitmaps", 280 | "typenum", 281 | ] 282 | 283 | [[package]] 284 | name = "strsim" 285 | version = "0.8.0" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 288 | 289 | [[package]] 290 | name = "structopt" 291 | version = "0.3.25" 292 | source = "registry+https://github.com/rust-lang/crates.io-index" 293 | checksum = "40b9788f4202aa75c240ecc9c15c65185e6a39ccdeb0fd5d008b98825464c87c" 294 | dependencies = [ 295 | "clap", 296 | "lazy_static", 297 | "paw", 298 | "structopt-derive", 299 | ] 300 | 301 | [[package]] 302 | name = "structopt-derive" 303 | version = "0.4.18" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" 306 | dependencies = [ 307 | "heck", 308 | "proc-macro-error", 309 | "proc-macro2", 310 | "quote", 311 | "syn", 312 | ] 313 | 314 | [[package]] 315 | name = "syn" 316 | version = "1.0.82" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" 319 | dependencies = [ 320 | "proc-macro2", 321 | "quote", 322 | "unicode-xid", 323 | ] 324 | 325 | [[package]] 326 | name = "termcolor" 327 | version = "1.1.2" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 330 | dependencies = [ 331 | "winapi-util", 332 | ] 333 | 334 | [[package]] 335 | name = "textwrap" 336 | version = "0.11.0" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 339 | dependencies = [ 340 | "unicode-width", 341 | ] 342 | 343 | [[package]] 344 | name = "thiserror" 345 | version = "1.0.30" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 348 | dependencies = [ 349 | "thiserror-impl", 350 | ] 351 | 352 | [[package]] 353 | name = "thiserror-impl" 354 | version = "1.0.30" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 357 | dependencies = [ 358 | "proc-macro2", 359 | "quote", 360 | "syn", 361 | ] 362 | 363 | [[package]] 364 | name = "time" 365 | version = "0.1.44" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 368 | dependencies = [ 369 | "libc", 370 | "wasi", 371 | "winapi", 372 | ] 373 | 374 | [[package]] 375 | name = "typenum" 376 | version = "1.14.0" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" 379 | 380 | [[package]] 381 | name = "unicode-segmentation" 382 | version = "1.8.0" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" 385 | 386 | [[package]] 387 | name = "unicode-width" 388 | version = "0.1.9" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 391 | 392 | [[package]] 393 | name = "unicode-xid" 394 | version = "0.2.2" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 397 | 398 | [[package]] 399 | name = "vec_map" 400 | version = "0.8.2" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 403 | 404 | [[package]] 405 | name = "version_check" 406 | version = "0.9.3" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" 409 | 410 | [[package]] 411 | name = "wasi" 412 | version = "0.10.0+wasi-snapshot-preview1" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 415 | 416 | [[package]] 417 | name = "winapi" 418 | version = "0.3.9" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 421 | dependencies = [ 422 | "winapi-i686-pc-windows-gnu", 423 | "winapi-x86_64-pc-windows-gnu", 424 | ] 425 | 426 | [[package]] 427 | name = "winapi-i686-pc-windows-gnu" 428 | version = "0.4.0" 429 | source = "registry+https://github.com/rust-lang/crates.io-index" 430 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 431 | 432 | [[package]] 433 | name = "winapi-util" 434 | version = "0.1.5" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 437 | dependencies = [ 438 | "winapi", 439 | ] 440 | 441 | [[package]] 442 | name = "winapi-x86_64-pc-windows-gnu" 443 | version = "0.4.0" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 446 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "MeowThink" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1.0.49" 10 | im = "15.0.0" 11 | log = "0.4.14" 12 | simplelog = { version = "^0.11.0", features = ["paris"] } 13 | paw = "1.0.0" 14 | structopt = { version="0.3.25", features=["paw"] } 15 | thiserror = "1.0.30" 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Liu Xiaoyi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MeowThink 2 | 3 | > 猫咪思考.jpg 4 | 5 | MeowThink is a miniature dependent typed language intended to create a proof assistant within 2000 LOC of Rust. 6 | 7 | It uses: 8 | - A special equality type (to avoid the need to implement dependent pattern matching) 9 | - Recursion with (restrictive) totality check 10 | - Inductive type / type families 11 | - Record types and modules 12 | 13 | For examples, see files within the `testcases` directory. `tools` contains a VSCode extension providing basic syntax highlighting. 14 | 15 | Currently MeowThink is in a WIP state. A lot of FIXMEs and TODOs are still in the source code! 16 | 17 | ## TODO 18 | - [ ] Dependent ap 19 | - [ ] Macros 20 | - [ ] Axioms 21 | 22 | ## License 23 | All source code within this repository is released under the MIT license. A full copy of the license text can be found in the LICENSE file. 24 | -------------------------------------------------------------------------------- /src/data.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | 3 | #[derive(Debug)] 4 | pub struct Expr<'a, T> { 5 | pub inner: ExprInner<'a, T>, 6 | pub data: T, 7 | } 8 | 9 | #[derive(Debug)] 10 | pub enum ExprInner<'a, T> { 11 | // Type of types 12 | PartialType(Box>), 13 | Universe { 14 | level: Option, 15 | }, 16 | 17 | // Type 18 | Ind { 19 | sig: Option>>, 20 | ctors: Vec>, 21 | }, 22 | Fun(Fun<'a, T>), 23 | StructTy(Vec>), 24 | 25 | // Other "Non-types" 26 | Lambda { 27 | // Currently only with one argument 28 | arg: Box>, 29 | ret: Option>>, 30 | body: Box>, 31 | 32 | rec: Option<&'a str>, 33 | }, 34 | Binding { 35 | binding: Box>, 36 | rest: Box>, 37 | }, 38 | Name(Box>), 39 | Ap(Box<(Expr<'a, T>, Expr<'a, T>)>), 40 | Eq { 41 | lhs: Box>, 42 | rhs: Box>, 43 | }, 44 | Cast { 45 | orig: Box>, 46 | eq: Box>, 47 | }, 48 | EqAp { 49 | eq: Box>, 50 | fun: Box>, 51 | }, 52 | Match { 53 | matched: Box>, 54 | arms: Vec>, 55 | }, 56 | CtorOf { 57 | parent: Box>, 58 | variant: &'a str, 59 | }, 60 | SelfInvoc, 61 | ReflInvoc, 62 | Struct(Vec>), 63 | Field { 64 | parent: Box>, 65 | field: &'a str, 66 | }, 67 | 68 | Import(Path<'a>), 69 | } 70 | 71 | impl<'a, T> ExprInner<'a, T> { 72 | pub fn with(self, data: T) -> Expr<'a, T> { 73 | Expr { 74 | inner: self, 75 | data, 76 | } 77 | } 78 | } 79 | 80 | #[derive(Debug)] 81 | pub struct Name<'a, T> { 82 | pub name: &'a str, 83 | pub sig: Option>, 84 | } 85 | 86 | #[derive(Debug)] 87 | pub struct Binding<'a, T> { 88 | pub name: Name<'a, T>, 89 | pub val: Expr<'a, T>, 90 | } 91 | 92 | #[derive(Debug)] 93 | pub struct Fun<'a, T> { 94 | pub input: Box>, 95 | pub output: Box>, 96 | } 97 | 98 | #[derive(Debug)] 99 | pub struct MatchArm<'a, T> { 100 | pub ctor: &'a str, 101 | pub data: Vec<&'a str>, 102 | pub ev: Vec>, 103 | pub sig: Option>, 104 | pub body: Expr<'a, T>, 105 | } 106 | 107 | #[derive(Debug)] 108 | pub struct Ctor<'a, T> { 109 | pub name: &'a str, 110 | pub sig: Expr<'a, T>, 111 | } 112 | 113 | #[derive(Debug)] 114 | pub enum RelPathSeg<'a> { 115 | Up, 116 | Down(&'a str), 117 | } 118 | 119 | #[derive(Debug)] 120 | pub enum Path<'a> { 121 | Absolute(Vec<&'a str>), 122 | Relative(Vec>), 123 | } 124 | 125 | impl<'a> Display for Path<'a> { 126 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 127 | match self { 128 | Path::Absolute(segs) => write!(f, "{}", segs.join("."))?, 129 | Path::Relative(segs) => for seg in segs{ 130 | match seg { 131 | RelPathSeg::Up => write!(f, ".")?, 132 | RelPathSeg::Down(seg) => write!(f, ".{}", seg)?, 133 | } 134 | } 135 | } 136 | 137 | Ok(()) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/eval.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, collections::HashMap, fmt::Display, marker::PhantomData, path::PathBuf}; 2 | 3 | use thiserror::Error; 4 | use std::fmt::Debug; 5 | 6 | use crate::{data::{Expr, ExprInner, Name}, exec::Runner}; 7 | 8 | #[derive(Debug, PartialEq, Eq)] 9 | pub struct Ind { 10 | sig: Rc, 11 | // indexes: Vec>, 12 | variants: im::HashMap>, 13 | } 14 | 15 | impl Ind { 16 | pub fn substitute(&self, ident: ExtBindingIdent, with: &Value) -> Ind { 17 | log::debug!("Ind sub: {:?} [{}/{:?}]", self, ident, with); 18 | Ind { 19 | sig: self.sig.clone().substitute(ident, with), 20 | variants: self.variants.iter().map(|(name, orig)| (name.clone(), orig.clone().substitute(ident, with))).collect(), 21 | } 22 | } 23 | } 24 | 25 | #[derive(Debug, PartialEq, Clone, Eq)] 26 | pub enum IndPtr { 27 | SelfInvoc, 28 | Complete(Rc), 29 | } 30 | 31 | #[derive(Debug, Clone, PartialEq, Eq)] 32 | pub struct Variant { 33 | captures: Vec, 34 | value: Value, 35 | // TODO: allow different ret in match 36 | // ret: Type, 37 | } 38 | 39 | impl Variant { 40 | pub fn substitute(&self, ident: ExtBindingIdent, with: &Value) -> Variant { 41 | if self.captures.contains(&ident) { 42 | return self.clone(); 43 | } 44 | 45 | Variant { 46 | captures: self.captures.clone(), 47 | value: self.value.substitute(ident, with), 48 | } 49 | } 50 | } 51 | 52 | #[derive(Debug, Clone, Eq, PartialEq)] 53 | pub enum Value { 54 | Refl, 55 | TypedRefl(Rc), 56 | Equality(Rc), 57 | Type(Rc), 58 | Lambda { 59 | ident: ExtBindingIdent, 60 | recursor: ExtBindingIdent, 61 | body: Box, 62 | }, 63 | PartiallyIndexedInd { 64 | ind: IndPtr, 65 | // Maybe partially indexed 66 | indexes: im::Vector, 67 | }, 68 | Inductive { 69 | ind: Rc, 70 | ctor: String, 71 | // Maybe partially applied 72 | data: im::Vector, 73 | }, 74 | Struct(im::HashMap), 75 | 76 | // Delayed evaluation 77 | Ap { 78 | fun: Box, 79 | arg: Box, 80 | }, 81 | Match { 82 | matched: Box, 83 | variants: im::HashMap, 84 | }, 85 | Field { 86 | parent: Box, 87 | field: String, 88 | }, 89 | Pending(ExtBindingIdent), 90 | Placeholder, // Hole in identity, hole in argument, etc 91 | } 92 | 93 | #[derive(PartialEq, Eq, Clone, Debug)] 94 | pub enum Type { 95 | Hole, 96 | Partial(Rc), 97 | 98 | Type { 99 | universe: Option, 100 | }, 101 | Eq { 102 | within: Rc, 103 | lhs: Value, 104 | rhs: Value, 105 | }, 106 | Fun { 107 | arg: Rc, 108 | ident: ExtBindingIdent, 109 | ret: Rc, 110 | }, 111 | FullyIndexedInd { 112 | ind: IndPtr, 113 | indexes: im::Vector, 114 | }, 115 | Struct(im::HashMap>), 116 | // Ap, Match, Pending 117 | Delayed(Value), 118 | } 119 | 120 | impl Display for Type { 121 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 122 | match self { 123 | Type::Hole => write!(f, "_"), 124 | Type::Partial(t) => write!(f, "partial {}", t), 125 | Type::Type { universe } => write!(f, "type {}", universe.map(|i| i.to_string()).unwrap_or("_".to_owned())), 126 | Type::Eq { within, lhs, rhs } => write!(f, "<{} = {}>", lhs, rhs), 127 | Type::Fun { arg, ident, ret } => write!(f, "([{}]: {}) -> {}", ident, arg, ret), 128 | Type::FullyIndexedInd { ind, indexes } => { 129 | write!(f, "")?; 130 | for index in indexes.iter() { 131 | write!(f, " {}", index)?; 132 | } 133 | Ok(()) 134 | }, 135 | Type::Struct(fields) => { 136 | write!(f, "struct {{")?; 137 | let mut is_first = true; 138 | for (k, v) in fields { 139 | if is_first { 140 | write!(f, " ")?; 141 | } else { 142 | write!(f, ", ")?; 143 | } 144 | write!(f, "{}: {}", k, v)?; 145 | is_first = false; 146 | } 147 | if !is_first { 148 | write!(f, " ")?; 149 | } 150 | write!(f, "}}")?; 151 | Ok(()) 152 | } 153 | Type::Delayed(v) => write!(f, "", v), 154 | } 155 | } 156 | } 157 | 158 | impl From> for Value { 159 | fn from(v: Rc) -> Self { 160 | Value::Type(v) 161 | } 162 | } 163 | 164 | impl Value { 165 | pub fn substitute(&self, ident: ExtBindingIdent, with: &Value) -> Value { 166 | let ret = match self { 167 | Value::Pending(self_ident) => if *self_ident == ident { 168 | with.clone() 169 | } else { 170 | self.clone() 171 | } 172 | Value::Ap { fun, arg } => Value::Ap { 173 | fun: Box::new(fun.substitute(ident, with)), 174 | arg: Box::new(arg.substitute(ident, with)), 175 | }, 176 | Value::Match { matched, variants } => Value::Match { 177 | matched: Box::new(matched.substitute(ident, with)), 178 | variants: variants.iter().map(|(ctor, variant)| (ctor.clone(), variant.substitute(ident, with))).collect(), 179 | }, 180 | Value::Equality(t) => Value::Equality(t.clone().substitute(ident, with)), 181 | Value::Type(t) => Value::Type(t.clone().substitute(ident, with)), 182 | Value::Lambda { ident: arg, recursor, body } => { 183 | if *arg == ident || ident == *recursor { 184 | // Overridden, skip 185 | self.clone() 186 | } else { 187 | Value::Lambda { ident: *arg, recursor: *recursor, body: Box::new(body.substitute(ident, with)) } 188 | } 189 | }, 190 | Value::PartiallyIndexedInd { ind, indexes } => { 191 | let ind = match ind { 192 | IndPtr::SelfInvoc => IndPtr::SelfInvoc, 193 | IndPtr::Complete(i) => IndPtr::Complete(Rc::new(i.substitute(ident, with))), 194 | }; 195 | Value::PartiallyIndexedInd { 196 | ind, 197 | indexes: indexes.iter().map(|e| e.substitute(ident, with)).collect(), 198 | } 199 | }, 200 | Value::Inductive { ind, ctor, data } => { 201 | Value::Inductive { 202 | ind: Rc::new(ind.substitute(ident, with)), 203 | ctor: ctor.clone(), 204 | data: data.iter().map(|d| d.substitute(ident, with)).collect() 205 | } 206 | }, 207 | Value::Struct(fields) => { 208 | Value::Struct( 209 | fields.iter().map(|(name, val)| (name.clone(), val.substitute(ident, with))).collect() 210 | ) 211 | }, 212 | Value::Field { parent, field } => { 213 | Value::Field { parent: Box::new(parent.substitute(ident, with)), field: field.clone() } 214 | }, 215 | Value::Placeholder => Value::Placeholder, 216 | Value::Refl => Value::Refl, 217 | Value::TypedRefl(ty) => Value::TypedRefl(ty.clone().substitute(ident, with)), 218 | }; 219 | 220 | ret.compact() 221 | } 222 | 223 | pub fn match_with(self, variants: im::HashMap) -> Value { 224 | match self { 225 | Value::Inductive { ind, ctor, data } => { 226 | // TODO: sanity check ind = matched 227 | let variant = variants.get(&ctor).unwrap(); 228 | assert_eq!(data.len(), variant.captures.len()); 229 | let body = data.into_iter().zip(variant.captures.iter()).fold(variant.value.clone(), |acc, (val, ident)| acc.substitute(*ident, &val)); 230 | body.progress() 231 | }, 232 | // Halts progression in variants if match is not resolved 233 | _ => Value::Match { 234 | matched: Box::new(self), 235 | variants, 236 | }, 237 | } 238 | } 239 | 240 | pub fn ap_with(self, arg: Value) -> Value { 241 | log::debug!("Ap: {:?} <- {:?}", self, arg); 242 | match self { 243 | Value::Lambda { ident, recursor, ref body } => { 244 | body.0.substitute(ident, &arg).substitute(recursor, &self).progress() 245 | } 246 | Value::PartiallyIndexedInd { ind, mut indexes } => { 247 | // TODO: sanity check: arity? 248 | indexes.push_back(arg); 249 | Value::PartiallyIndexedInd { 250 | ind, 251 | indexes, 252 | }.progress() 253 | }, 254 | Value::Inductive { ind, ctor, mut data } => { 255 | // TODO: sanity check: arity? 256 | data.push_back(arg); 257 | Value::Inductive { ind, ctor, data }.progress() 258 | }, 259 | Value::Refl => Value::TypedRefl(arg.try_unwrap_as_type::<()>().unwrap()), 260 | Value::TypedRefl(ty) => Value::Equality(Rc::new(Type::Eq { 261 | within: ty, 262 | lhs: arg.clone(), 263 | rhs: arg.clone(), 264 | })), 265 | _ => Value::Ap { 266 | fun: Box::new(self), 267 | arg: Box::new(arg.progress()), 268 | }, 269 | } 270 | } 271 | 272 | pub fn field_with(self, field: String) -> Value { 273 | match self { 274 | Value::Struct(mut fields) => { 275 | fields.remove(&field).unwrap() 276 | }, 277 | _ => Value::Field { 278 | parent: Box::new(self), 279 | field, 280 | }, 281 | } 282 | } 283 | 284 | pub fn try_unwrap_as_type(self) -> EvalResult, PI> { 285 | match self { 286 | Value::Type(t) => Ok(t), 287 | Value::PartiallyIndexedInd{ ind, indexes } => { 288 | // FIXME: check arity 289 | Ok(Rc::new(Type::FullyIndexedInd { ind, indexes })) 290 | }, 291 | Value::Placeholder => Ok(Rc::new(Type::Hole)), 292 | Value::Ap { .. } | Value::Match { .. } | Value::Pending(_) => Ok(Rc::new(Type::Delayed(self))), 293 | _ => panic!("Trying to unwrap as type: {:?}", self), 294 | } 295 | } 296 | 297 | pub fn progress(self) -> Value { 298 | match self { 299 | Value::Equality(ty) => Value::Equality(ty.progress()), 300 | Value::Type(ty) => Value::Type(ty.progress()), 301 | Value::Lambda { ident, recursor, mut body } => { 302 | body.0 = body.0.progress(); 303 | Value::Lambda { ident, recursor, body } 304 | }, 305 | Value::PartiallyIndexedInd { ind, indexes } => Value::PartiallyIndexedInd { ind, indexes: indexes.into_iter().map(Value::progress).collect() }, 306 | Value::Inductive { ind, ctor, data } => Value::Inductive { 307 | ind, ctor, 308 | data: data.into_iter().map(Value::progress).collect(), 309 | }, 310 | Value::Struct(fields) => Value::Struct( 311 | fields.iter().map(|(name, val)| (name.clone(), val.clone().progress())).collect() 312 | ), 313 | Value::Pending(_) => self, 314 | 315 | // TODO: optimize Ap and Match 316 | Value::Ap { fun, arg } => fun.progress().ap_with(*arg), 317 | Value::Match { matched, variants } => matched.progress().match_with(variants), 318 | Value::Field { parent, field } => parent.progress().field_with(field), 319 | Value::Placeholder => Value::Placeholder, 320 | Value::Refl => Value::Refl, 321 | Value::TypedRefl(ty) => Value::TypedRefl(ty.progress()), 322 | } 323 | } 324 | 325 | pub fn unify(&self, ano: &Value) -> Option { 326 | // FIXME: correctly unify match body, lambda body, recursive unify, etc 327 | log::debug!("Value unify: {} <-> {}", self, ano); 328 | if self == ano { 329 | return Some(self.clone()); 330 | } 331 | match (self, ano) { 332 | (&Value::Placeholder, _) => Some(ano.clone()), 333 | (_, &Value::Placeholder) => Some(self.clone()), 334 | (Value::Type(t), Value::Type(at)) => Some(Value::Type(t.clone().unify(at.clone())?).compact()), 335 | (Value::Lambda { ident, recursor, body }, Value::Lambda { ident: ai, recursor: ar, body: ab }) => { 336 | let replaced_ab = ab.substitute(*ai, &Value::Pending(*ident)).substitute(*ar, &Value::Pending(*recursor)); 337 | if replaced_ab == **body { 338 | return Some(self.clone()); 339 | } else { 340 | return None; 341 | } 342 | } 343 | _ => None, 344 | } 345 | } 346 | 347 | pub fn promote_ind_type(self) -> Value { 348 | if let Value::PartiallyIndexedInd { ind, indexes } = &self { 349 | if let IndPtr::Complete(c) = ind { 350 | assert!(c.sig.arity() == indexes.len()); 351 | } 352 | return Value::Type(Rc::new(Type::FullyIndexedInd { ind: ind.clone(), indexes: indexes.clone() })); 353 | } 354 | return self; 355 | } 356 | 357 | pub fn demote_ind_type(self) -> Value { 358 | if let Value::Type(t) = &self { 359 | if let Type::FullyIndexedInd { ind, indexes } = &**t { 360 | return Value::PartiallyIndexedInd { ind: ind.clone(), indexes: indexes.clone() }; 361 | } 362 | } 363 | return self; 364 | } 365 | 366 | pub fn compact(self) -> Value { 367 | // Persumably we don't need deep compact 368 | match self { 369 | Value::Type(ref t) if let Type::Delayed(d) = &**t => d.clone(), 370 | _ => self, 371 | } 372 | } 373 | } 374 | 375 | impl Display for Value { 376 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 377 | match self { 378 | Value::Equality(eq) => write!(f, "", eq), 379 | Value::Type(t) => write!(f, "", t), 380 | Value::Lambda { .. } => write!(f, ""), 381 | Value::PartiallyIndexedInd { .. } => write!(f, ""), 382 | Value::Inductive { ctor, data, .. } => { 383 | write!(f, "{}", ctor)?; 384 | for d in data.iter() { 385 | write!(f, " ({})", d)?; 386 | } 387 | Ok(()) 388 | } 389 | Value::Struct(fields) => { 390 | write!(f, "{{")?; 391 | let mut is_first = true; 392 | for (k, v) in fields { 393 | if is_first { 394 | write!(f, " ")?; 395 | } else { 396 | write!(f, ", ")?; 397 | } 398 | write!(f, "{}: {}", k, v)?; 399 | is_first = false; 400 | } 401 | if !is_first { 402 | write!(f, " ")?; 403 | } 404 | write!(f, "}}")?; 405 | Ok(()) 406 | } 407 | Value::Ap { fun, arg } => write!(f, "{} {}", fun, arg), 408 | Value::Match { matched, ..} => write!(f, "match {}", matched), 409 | Value::Pending(p) => write!(f, "[{}]", p), 410 | Value::Placeholder => write!(f, "_"), 411 | Value::Refl => write!(f, "refl"), 412 | Value::TypedRefl(t) => write!(f, "refl_{:?}", t), 413 | Value::Field { parent, field } => write!(f, "({}).{}", parent, field), 414 | } 415 | } 416 | } 417 | 418 | impl Type { 419 | pub fn unify(self: Rc, ano: Rc) -> Option> { 420 | if self == ano { 421 | return Some(self); 422 | } 423 | 424 | // TODO: partial ord? 425 | match (self.as_ref(), ano.as_ref()) { 426 | (_, Type::Hole) => Some(self.clone()), 427 | (Type::Hole, _) => Some(ano.clone()), 428 | (Type::Partial(a), Type::Partial(b)) => if a == b { 429 | Some(self.clone()) 430 | } else { 431 | a.clone().unify(b.clone()).map(Type::Partial).map(Rc::new) 432 | } 433 | (Type::Type{ universe }, Type::Type{ universe: anou }) => { 434 | if universe == anou || anou.is_none() { 435 | return Some(self.clone()) 436 | } 437 | if universe.is_none() { 438 | return Some(ano.clone()) 439 | } 440 | None 441 | }, 442 | (Type::Fun{ arg, ident, ret }, Type::Fun{ arg: anoarg, ident: anoident, ret: anoret }) => { 443 | let unified_arg = arg.clone().unify(anoarg.clone())?; 444 | if ident == anoident { 445 | return Some(Rc::new(Type::Fun { 446 | arg: unified_arg, 447 | ident: ident.clone(), 448 | ret: ret.clone(), 449 | })) 450 | } 451 | 452 | let unified_ret = anoret.clone().substitute(*anoident, &Value::Pending(*ident)).unify(ret.clone())?; 453 | 454 | Some(Rc::new(Type::Fun { 455 | arg: unified_arg, 456 | ident: ident.clone(), 457 | ret: unified_ret, 458 | })) 459 | }, 460 | (Type::Eq { within, lhs, rhs }, Type::Eq { within: aw, lhs: al, rhs: ar }) => Some(Rc::new(Type::Eq { 461 | within: within.clone().unify(aw.clone())?, 462 | lhs: lhs.unify(al)?.clone(), 463 | rhs: rhs.unify(ar)?.clone(), 464 | })), 465 | (Type::FullyIndexedInd { ind, indexes }, Type::FullyIndexedInd { ind: ai, indexes: aidx }) => { 466 | match (ind, ai) { 467 | (IndPtr::Complete(ic), IndPtr::Complete(aic)) => { 468 | if ic != aic { 469 | return None; 470 | } 471 | }, 472 | (IndPtr::SelfInvoc, IndPtr::SelfInvoc) => {}, 473 | _ => return None, 474 | } 475 | let mut idx = im::Vector::new(); 476 | for (ii, aii) in indexes.iter().zip(aidx) { 477 | idx.push_back(ii.unify(aii)?); 478 | } 479 | Some(Rc::new(Type::FullyIndexedInd { ind: ind.clone(), indexes: idx })) 480 | }, 481 | (Type::Struct(fa), Type::Struct(fb)) => { 482 | if fa.len() != fb.len() { 483 | return None; 484 | } 485 | 486 | let mut ret = im::HashMap::new(); 487 | for (k, va) in fa { 488 | if let Some(vb) = fb.get(k) { 489 | ret.insert(k.clone(), va.clone().unify(vb.clone())?); 490 | } else { 491 | return None; 492 | } 493 | } 494 | 495 | Some(Rc::new(Type::Struct(ret))) 496 | }, 497 | // _ => panic!("Type unification not impleneted: {}, {}", self, ano), 498 | _ => None 499 | } 500 | } 501 | 502 | pub fn try_unify(self: Rc, ano: Rc) -> EvalResult, PI> { 503 | self.clone().unify(ano.clone()).ok_or_else(|| { 504 | EvalError::Ununifiable{ 505 | expected: self, 506 | actual: ano, 507 | _pi: PhantomData::default(), 508 | } 509 | }) 510 | } 511 | 512 | pub fn arity(&self) -> usize { 513 | match self { 514 | Type::Fun{ ret, .. } => ret.arity() + 1, 515 | _ => 0, 516 | } 517 | } 518 | 519 | pub fn substitute(self: Rc, ident: ExtBindingIdent, with: &Value) -> Rc { 520 | log::debug!("Type {:?} [{}/{:?}]", self, ident, with); 521 | let ret = match self.as_ref() { 522 | Type::Hole => self.clone(), 523 | Type::Partial(p) => Rc::new(Type::Partial(p.clone().substitute(ident, with))), 524 | Type::Type { .. } => self.clone(), 525 | Type::Fun { arg, ident: sident, ret } => { 526 | if *sident == ident { 527 | // Inside ctor type instantiate 528 | return self.clone(); 529 | } 530 | Rc::new(Type::Fun { 531 | arg: arg.clone().substitute(ident, with), 532 | ident: *sident, 533 | ret: ret.clone().substitute(ident, with), 534 | }) 535 | }, 536 | Type::FullyIndexedInd { ind, indexes } => { 537 | let ind = match ind { 538 | IndPtr::SelfInvoc => IndPtr::SelfInvoc, 539 | IndPtr::Complete(c) => IndPtr::Complete(Rc::new(c.substitute(ident, with))), 540 | }; 541 | Rc::new(Type::FullyIndexedInd { 542 | ind, 543 | indexes: indexes.iter().map(|i| i.substitute(ident, with)).collect(), 544 | }) 545 | }, 546 | Type::Struct(fields) => { 547 | Rc::new(Type::Struct( 548 | fields.iter().map(|(name, val)| (name.clone(), val.clone().substitute(ident, with))).collect() 549 | )) 550 | }, 551 | Type::Delayed(d) => d.substitute(ident, with).try_unwrap_as_type::<()>().unwrap(), 552 | Type::Eq { within, lhs, rhs } => { 553 | Rc::new(Type::Eq { 554 | within: within.clone().substitute(ident, with), 555 | lhs: lhs.substitute(ident, with), 556 | rhs: rhs.substitute(ident, with), 557 | }) 558 | } 559 | }; 560 | 561 | ret.progress().compact() 562 | } 563 | 564 | pub fn instantiate_self(self: Rc, ind: Rc, top: bool) -> Rc { 565 | // Assumes strict postivity 566 | let ret = match self.as_ref() { 567 | Type::Fun { arg, ident: sident, ret } => { 568 | let arg = if top { 569 | arg.clone().instantiate_self(ind.clone(), false) 570 | } else { 571 | arg.clone() 572 | }; 573 | 574 | let ret = ret.clone().instantiate_self(ind, top); 575 | 576 | Rc::new(Type::Fun { arg, ident: *sident, ret }) 577 | }, 578 | Type::FullyIndexedInd { ind: orig, indexes } if orig == &IndPtr::SelfInvoc => { 579 | Rc::new(Type::FullyIndexedInd { 580 | ind: IndPtr::Complete(ind), 581 | indexes: indexes.clone(), 582 | }) 583 | }, 584 | _ => self.clone(), 585 | }; 586 | 587 | ret.progress().compact() 588 | } 589 | 590 | pub fn progress(self: Rc) -> Rc { 591 | if let Type::Delayed(v) = &*self { 592 | return Rc::new(Type::Delayed(v.clone().progress())).compact(); 593 | } 594 | 595 | return self; 596 | } 597 | 598 | pub fn assert_concrete(&self) -> EvalResult<(), PI> { 599 | // FIXME: impl 600 | Ok(()) 601 | } 602 | 603 | pub fn compact(self: Rc) -> Rc { 604 | // Persumably we don't need deep compact 605 | match &*self { 606 | Type::Delayed(Value::Type(t)) => t.clone(), 607 | _ => self, 608 | } 609 | } 610 | } 611 | 612 | #[derive(Error, Debug)] 613 | pub enum EvalError { 614 | #[error("Ununifiable types, expected {expected}, got {actual}")] 615 | Ununifiable { 616 | expected: Rc, 617 | actual: Rc, 618 | 619 | // TODO: implement parser info 620 | _pi: PhantomData, 621 | }, 622 | 623 | #[error("Expected type, got {actual} with type {ty}")] 624 | TypeOnly { 625 | actual: Value, 626 | ty: Rc, 627 | }, 628 | 629 | #[error("Can only match inductive types, got {actual} with type {ty}")] 630 | NonIndMatch { 631 | actual: Value, 632 | ty: Rc, 633 | }, 634 | 635 | #[error("Can only select ctor of non-indexed inductive types, got {actual}")] 636 | NonIndCtor { 637 | actual: Value, 638 | }, 639 | 640 | #[error("Ctor `{ctor}` not present in inductive types {ind:?}")] 641 | UndefinedCtor { 642 | ctor: String, 643 | ind: Rc, 644 | }, 645 | 646 | #[error("Can only select field of structs, got {actual} of type {ty}")] 647 | NonStructField { 648 | actual: Value, 649 | ty: Rc, 650 | }, 651 | 652 | #[error("Field `{field}` not present in type {ty:?}")] 653 | UndefinedField { 654 | field: String, 655 | ty: Rc, 656 | }, 657 | 658 | #[error("Matching arm for ctor `{ctor}` of {ind:?} got wrong number of data binding: got {actual}")] 659 | MatchWrongArity { 660 | ctor: String, 661 | ind: Rc, 662 | actual: usize, 663 | }, 664 | 665 | #[error("Matching arm for ctor `{ctor}` of {ind:?} got wrong number of evidence: got {actual}")] 666 | MatchWrongEvidence { 667 | ctor: String, 668 | ind: Rc, 669 | actual: usize, 670 | }, 671 | 672 | #[error("Ctor `{ctors:?}` missing when matching inductive types {ind:?}")] 673 | MatchNonExhaustive { 674 | ctors: Vec, 675 | ind: Rc, 676 | }, 677 | 678 | #[error("`self` used outside of inductive defination")] 679 | SelfOutsideInd, 680 | 681 | #[error("Undefined name / constructor: {name}")] 682 | Undefined { 683 | name: String, 684 | }, 685 | 686 | #[error("Unexpected type signature")] 687 | UnexpectedSig { 688 | // name: &'a Name<'a, PI>, 689 | name: String, 690 | }, 691 | 692 | #[error("Unbounded recursion")] 693 | UnboundedRecursion, 694 | 695 | #[error("Cannot cast with type: {ty}")] 696 | NonTyEqCast { 697 | ty: Rc, 698 | }, 699 | 700 | #[error("Cannot transport with dependent function: {ty}")] 701 | DependentTransport { 702 | ty: Rc, 703 | }, 704 | 705 | #[error("Error occured when processing import {path}: {error}")] 706 | Import { 707 | path: String, 708 | error: anyhow::Error, 709 | }, 710 | } 711 | 712 | type EvalResult = Result>; 713 | 714 | /// Distinguishing different external binding 715 | type ExtBindingIdent = usize; 716 | 717 | #[derive(Debug, Clone, PartialEq, Eq)] 718 | pub enum Source { 719 | /// Functiona argument 720 | External { 721 | ident: ExtBindingIdent, 722 | }, 723 | 724 | /// Destructed from an external argument 725 | Destructed { 726 | source: ExtBindingIdent, 727 | }, 728 | 729 | /// Given as recursor 730 | Recursor { 731 | /// Null = partial recursor 732 | linked: ExtBindingIdent, 733 | }, 734 | 735 | /// From arbitrary expression 736 | Constructed, 737 | } 738 | 739 | #[derive(Debug, Clone, PartialEq, Eq)] 740 | pub struct Evaluated( 741 | pub Value, 742 | pub Rc, 743 | pub Source, 744 | ); 745 | 746 | impl Evaluated { 747 | pub fn create(v: Value, t: Rc, src: Source) -> EvalResult { 748 | // TODO: check if t is concrete 749 | t.assert_concrete()?; 750 | Ok(Self(v.compact(), t.compact(), src)) 751 | } 752 | 753 | pub fn try_unwrap_as_type(self) -> EvalResult, PI> { 754 | // TODO: return universe 755 | match self.1.as_ref() { 756 | Type::Type { .. } => {}, 757 | _ => return Err(EvalError::TypeOnly{ actual: self.0, ty: self.1 }) 758 | } 759 | self.0.try_unwrap_as_type() 760 | } 761 | 762 | pub fn substitute(&self, ident: ExtBindingIdent, with: &Value) -> Evaluated { 763 | Evaluated( 764 | self.0.substitute(ident, with), 765 | self.1.clone().substitute(ident, with), 766 | self.2.clone() 767 | ) 768 | } 769 | } 770 | 771 | #[derive(Clone, Default)] 772 | struct Scope<'a> { 773 | bindings: im::HashMap<&'a str, Evaluated>, 774 | ind_type: Option>, 775 | } 776 | 777 | impl<'a> Scope<'a> { 778 | pub fn bind(&self, name: &'a str, bounded: Evaluated) -> Self { 779 | Scope { 780 | bindings: self.bindings.update(name, bounded), 781 | ind_type: self.ind_type.clone(), 782 | } 783 | } 784 | 785 | pub fn inside_ind(&self, ty: Rc) -> Self { 786 | Scope { 787 | bindings: self.bindings.clone(), 788 | ind_type: Some(ty), 789 | } 790 | } 791 | } 792 | 793 | #[derive(Default)] 794 | pub struct EvalCtx { 795 | ticket_gen: usize, 796 | } 797 | 798 | impl EvalCtx { 799 | // TODO: ensure hint and result type is always unifiable 800 | fn eval<'a, PI: Debug>( 801 | &mut self, 802 | expr: &'a Expr<'a, PI>, 803 | scope: &Scope<'a>, 804 | hint: Rc, 805 | at: &PathBuf, 806 | runner: &mut Runner, 807 | ) -> EvalResult { 808 | log::debug!("Eval: {:?}", expr); 809 | match &expr.inner { 810 | ExprInner::PartialType(inner) => { 811 | let inner_hint = hint.try_unify(Rc::new(Type::Type{ universe: None }))?; 812 | 813 | let inner = self.eval(inner.as_ref(), scope, inner_hint, at, runner)?; 814 | let Evaluated(_, t, src) = inner.clone(); 815 | let wrapped = Type::Partial(inner.try_unwrap_as_type()?); 816 | Evaluated::create(Value::Type(Rc::new(wrapped)), t, src) 817 | }, 818 | ExprInner::Ind { sig, ctors } => { 819 | let interface = if let Some(sig) = sig { 820 | let sig_val = self.eval_hint(sig.as_ref(), scope, at, runner)?; 821 | // TODO: check if sig is a type 822 | hint.try_unify(sig_val)? 823 | } else { 824 | hint 825 | }; 826 | 827 | // TODO: ensure hint is concrete 828 | // TODO: check if hint is _ -> _ -> type _ ? 829 | 830 | let inner_scope = scope.inside_ind(interface.clone()); 831 | let mut mapped_ctors = im::HashMap::new(); 832 | for ctor in ctors.iter() { 833 | let ctor_type_value = self.eval_hint(&ctor.sig, &inner_scope, at, runner)?; 834 | 835 | // TODO: check strict positivity and ctor yield type 836 | // TODO: check ctor is inside the required universe 837 | mapped_ctors.insert(ctor.name.to_owned(), ctor_type_value); 838 | } 839 | 840 | let ind = IndPtr::Complete(Rc::new(Ind { variants: mapped_ctors, sig: interface.clone() })); 841 | let mut self_val = Value::PartiallyIndexedInd { ind, indexes: im::Vector::new() }; 842 | if let Type::Type { .. } = &*interface { 843 | self_val = self_val.promote_ind_type(); 844 | } 845 | Evaluated::create(self_val, interface, Source::Constructed) 846 | }, 847 | ExprInner::Fun(f) => { 848 | let self_hint = hint.try_unify(Rc::new(Type::Type{ universe: None }))?; 849 | let (input_arg_name, input_type) = match &f.input.inner { 850 | ExprInner::Name(name) if name.sig.is_some() => { 851 | (Some(name.name), name.sig.as_ref().unwrap()) 852 | } 853 | _ => (None, f.input.as_ref()), 854 | }; 855 | let input_type_val = self.eval_hint(input_type, scope, at, runner)?; 856 | 857 | // Update scope before passing down for named dependent type variable 858 | let inner_scope_data; 859 | let mut inner_scope = scope; 860 | let arg_ident = self.count_external(); 861 | if let Some(arg_name) = input_arg_name { 862 | let bounded = Evaluated::create(Value::Pending(arg_ident), input_type_val.clone(), Source::External{ ident: arg_ident })?; 863 | inner_scope_data = scope.bind(arg_name, bounded); 864 | inner_scope = &inner_scope_data; 865 | } 866 | let ret_type_val = self.eval(f.output.as_ref(), inner_scope, Rc::new(Type::Type{ universe: None }), at, runner)?.try_unwrap_as_type()?; 867 | let self_type_val = Rc::new(Type::Fun{ 868 | arg: input_type_val, 869 | ident: arg_ident, 870 | ret: ret_type_val, 871 | }); 872 | // FIXME: pin down universe 873 | Evaluated::create(Value::Type(self_type_val), Rc::new(Type::Type{ universe: Some(0) }), Source::Constructed) 874 | }, 875 | ExprInner::Lambda { arg, ret, body, rec } => { 876 | let hint = hint.try_unify(Rc::new(Type::Fun { 877 | arg: Rc::new(Type::Hole), 878 | ident: 0, 879 | ret: Rc::new(Type::Hole), 880 | }))?; 881 | 882 | let (arg_hint, mut arg_ident, ret_hint) = match hint.as_ref() { 883 | Type::Fun{ arg, ident, ret } => (arg, *ident, ret), 884 | _ => unreachable!() 885 | }; 886 | 887 | // Evaluate arg type 888 | if arg_ident == 0 { 889 | arg_ident = self.count_external(); 890 | } 891 | let arg_type = if let Some(sig) = arg.sig.as_ref() { 892 | self.eval_hint(sig, scope, at, runner)?.try_unify(arg_hint.clone())? 893 | } else { 894 | arg_hint.clone() 895 | }; 896 | 897 | // Introduce arg into scope 898 | let bounded = Evaluated::create(Value::Pending(arg_ident), arg_type.clone(), Source::External{ ident: arg_ident })?; 899 | let ret_scope = scope.bind(arg.name, bounded); 900 | 901 | // Evaluate ret type 902 | let ret_type= if let Some(sig) = ret.as_ref() { 903 | self.eval_hint(sig, &ret_scope, at, runner)?.try_unify(ret_hint.clone())? 904 | } else { 905 | ret_hint.clone() 906 | }; 907 | 908 | let self_type = Rc::new(Type::Fun { 909 | arg: arg_type.clone(), 910 | ident: arg_ident, 911 | ret: ret_type.clone(), 912 | }); 913 | 914 | // Introduce rec 915 | let recursor_ident = self.count_external(); 916 | let mut inner_scope = ret_scope; 917 | if let Some(rec) = rec { 918 | let rec_bounded = Evaluated::create(Value::Pending(recursor_ident), self_type.clone(), Source::Recursor{ linked: arg_ident })?; 919 | inner_scope = inner_scope.bind(rec, rec_bounded); 920 | } 921 | 922 | // Do shadowed eval / type check in body 923 | let body_eval = self.eval(body.as_ref(), &inner_scope, ret_type, at, runner)?; 924 | let body_ty = body_eval.1.clone(); 925 | 926 | let self_val = Value::Lambda { 927 | ident: arg_ident, 928 | recursor: recursor_ident, 929 | body: Box::new(body_eval), 930 | }; 931 | 932 | let self_type = Rc::new(Type::Fun { 933 | arg: arg_type, 934 | ident: arg_ident, 935 | ret: body_ty, 936 | }); 937 | 938 | Evaluated::create(self_val, self_type, Source::Constructed) 939 | }, 940 | ExprInner::Binding { binding, rest } => { 941 | let binding_hint = if let Some(sig) = &binding.name.sig { 942 | self.eval_hint(sig, scope, at, runner)? 943 | } else { 944 | Rc::new(Type::Hole) 945 | }; 946 | let evaled = self.eval(&binding.val, scope, binding_hint, at, runner)?; 947 | let inner_scope = scope.bind(binding.name.name, evaled); 948 | self.eval(rest.as_ref(), &inner_scope, hint, at, runner) 949 | }, 950 | ExprInner::Name(name) => { 951 | if name.sig.is_some() { 952 | return Err(EvalError::UnexpectedSig{ name: name.name.to_owned() }) 953 | } 954 | let value = scope.bindings.get(name.name).cloned().ok_or(EvalError::Undefined{ name: name.name.to_owned() })?; 955 | hint.try_unify(value.1.clone())?; 956 | Ok(value) 957 | }, 958 | ExprInner::Ap(pair) => { 959 | let (f, arg) = pair.as_ref(); 960 | // TODO: finer grind hints 961 | let f_eval = self.eval(f, scope, Rc::new(Type::Fun { 962 | arg: Rc::new(Type::Hole), 963 | ident: 0, 964 | ret: Rc::new(Type::Hole), 965 | }), at, runner)?; 966 | 967 | log::debug!("Applying function {:#?}", f_eval); 968 | let (arg_type, arg_ident, ret_type) = match f_eval.1.as_ref() { 969 | Type::Fun { arg, ident, ret } => (arg, *ident, ret), 970 | _ => panic!("Why did ap get a {:?}", f_eval.1), 971 | }; 972 | let arg_eval = self.eval(arg, scope, arg_type.clone(), at, runner)?; 973 | // TODO: assert arg type concrete 974 | let ret_type = ret_type.clone().substitute(arg_ident, &arg_eval.0); 975 | let ret_type = ret_type.try_unify(hint)?; 976 | 977 | if let Source::Recursor { linked } = f_eval.2 { 978 | match arg_eval.2 { 979 | Source::Destructed { source } if source == linked => {}, 980 | _ => return Err(EvalError::UnboundedRecursion), 981 | } 982 | } 983 | 984 | let mut val = Value::Ap { 985 | fun: Box::new(f_eval.0), 986 | arg: Box::new(arg_eval.0), 987 | }.progress(); 988 | 989 | if let Type::Type { .. } = &*ret_type { 990 | val = val.promote_ind_type(); 991 | } 992 | 993 | Evaluated::create(val, ret_type, Source::Constructed) 994 | }, 995 | ExprInner::Match { matched, arms } => { 996 | // Build pendings 997 | let Evaluated(matched_val, matched_type, matched_src) = self.eval(matched.as_ref(), scope, Rc::new(Type::Hole), at, runner)?; 998 | let (ind, indexes) = match matched_type.as_ref() { 999 | Type::FullyIndexedInd { ind, indexes } => { 1000 | match ind { 1001 | IndPtr::SelfInvoc => unreachable!(), // We should never get self out side of ind defination 1002 | IndPtr::Complete(ind) => (ind, indexes), 1003 | } 1004 | }, 1005 | _ => { 1006 | return Err(EvalError::NonIndMatch{ actual: matched_val, ty: matched_type }); 1007 | }, 1008 | }; 1009 | 1010 | let ind_arity = ind.sig.arity(); 1011 | assert!(ind_arity == indexes.len()); 1012 | 1013 | let data_src = match matched_src { 1014 | Source::External { ident: source } | Source::Destructed { source } => Source::Destructed { source }, 1015 | _ => Source::Constructed, 1016 | }; 1017 | 1018 | // Check exhaustiveness 1019 | let mut remaining = ind.variants.clone(); 1020 | let mut evaluated_arms = HashMap::new(); 1021 | let mut cur_hint = hint; 1022 | for arm in arms { 1023 | if arm.sig.is_some() { 1024 | // TODO: impl typed match arms 1025 | panic!("Currently don't supports typed match"); 1026 | } 1027 | 1028 | let mut variant_sig = remaining.remove(arm.ctor).ok_or(EvalError::UndefinedCtor { ctor: arm.ctor.to_owned(), ind: ind.clone() })?; 1029 | 1030 | let arity = variant_sig.arity(); 1031 | if arity != arm.data.len() { 1032 | return Err(EvalError::MatchWrongArity { actual: arm.data.len(), ctor: arm.ctor.to_owned(), ind: ind.clone() }) 1033 | } 1034 | if ind_arity + 1 != arm.ev.len() { 1035 | return Err(EvalError::MatchWrongEvidence { actual: arm.ev.len(), ctor: arm.ctor.to_owned(), ind: ind.clone() }) 1036 | } 1037 | 1038 | // Generate datum bindings 1039 | let mut arm_scope = scope.clone(); 1040 | let mut data_idents = Vec::new(); 1041 | let mut data_names = arm.data.iter(); 1042 | let arm_indexes = loop { 1043 | match variant_sig.as_ref() { 1044 | Type::Fun { arg, ident: orig, ret } => { 1045 | let ident = self.count_external(); 1046 | data_idents.push(ident); 1047 | let val = Value::Pending(ident); 1048 | 1049 | let bounded = Evaluated(val.clone(), arg.clone().instantiate_self(ind.clone(), true), data_src.clone()); 1050 | let name = data_names.next().unwrap(); 1051 | arm_scope = arm_scope.bind(*name, bounded); 1052 | 1053 | variant_sig = ret.clone().substitute(*orig, &val); 1054 | }, 1055 | Type::FullyIndexedInd { ind, indexes } => { 1056 | assert!(*ind == IndPtr::SelfInvoc); 1057 | break indexes; 1058 | }, 1059 | _ => unreachable!() 1060 | } 1061 | }; 1062 | 1063 | let data_arm_scope = arm_scope.clone(); 1064 | let constructed = Value::Inductive { 1065 | ind: ind.clone(), 1066 | ctor: arm.ctor.to_owned(), 1067 | data: data_idents.iter().cloned().map(|ident| Value::Pending(ident)).collect() 1068 | }; 1069 | let mut matched_eq = Rc::new(Type::Eq { 1070 | within: matched_type.clone(), 1071 | lhs: constructed, 1072 | rhs: matched_val.clone(), 1073 | }); 1074 | let mut ev_names = arm.ev.iter(); 1075 | let full_ev_name = ev_names.next().unwrap(); 1076 | if let Some(sig) = full_ev_name.sig.as_ref(){ 1077 | matched_eq = self.eval_hint(sig, &data_arm_scope, at, runner)?.try_unify(matched_eq)?; 1078 | } 1079 | let matched_ev = Evaluated(Value::Equality(matched_eq.clone()), matched_eq, Source::Constructed); 1080 | arm_scope = arm_scope.bind(full_ev_name.name, matched_ev); 1081 | 1082 | let mut ind_sig = ind.sig.clone(); 1083 | let mut index_pairs = ev_names.zip(arm_indexes.iter().zip(indexes.iter())); 1084 | loop { 1085 | match ind_sig.as_ref() { 1086 | Type::Fun { arg, ident: orig, ret } => { 1087 | // Base type 1088 | let (ev_name, (lhs, rhs)) = index_pairs.next().unwrap(); 1089 | let mut ev_ty = Rc::new(Type::Eq { 1090 | within: arg.clone(), 1091 | lhs: lhs.clone(), 1092 | rhs: rhs.clone(), 1093 | }); 1094 | if let Some(sig) = ev_name.sig.as_ref(){ 1095 | ev_ty = self.eval_hint(sig, &data_arm_scope, at, runner)?.try_unify(ev_ty)?; 1096 | } 1097 | let ev = Evaluated(Value::Equality(ev_ty.clone()), ev_ty, Source::Constructed); 1098 | arm_scope = arm_scope.bind(ev_name.name, ev); 1099 | 1100 | ind_sig = ret.clone().substitute(*orig, lhs); 1101 | }, 1102 | Type::Type { .. } => break, 1103 | _ => unreachable!(), 1104 | } 1105 | } 1106 | 1107 | let body = self.eval(&arm.body, &arm_scope, cur_hint.clone(), at, runner)?; 1108 | cur_hint = cur_hint.try_unify(body.1.clone())?; 1109 | evaluated_arms.insert(arm.ctor, (data_idents, body)); 1110 | } 1111 | 1112 | if remaining.len() != 0 { 1113 | return Err(EvalError::MatchNonExhaustive { 1114 | ctors: remaining.keys().cloned().collect(), 1115 | ind: ind.clone(), 1116 | }); 1117 | } 1118 | 1119 | // Merge source 1120 | let mut src = evaluated_arms.values().next().map(|(_, e)| e.2.clone()).unwrap_or(Source::Constructed); 1121 | for (_, arm) in evaluated_arms.values() { 1122 | match arm.2 { 1123 | Source::Destructed { .. } if arm.2 == src => {}, 1124 | _ => src = Source::Constructed, 1125 | } 1126 | } 1127 | 1128 | // Check if all variants have same result type 1129 | cur_hint.assert_concrete()?; 1130 | let variants: EvalResult, PI> = evaluated_arms.into_iter().map(|(name, (captures, eval))| { 1131 | cur_hint.clone().try_unify(eval.1)?; 1132 | Ok((name.to_owned(), Variant { 1133 | captures, 1134 | value: eval.0, 1135 | })) 1136 | }).collect(); 1137 | let variants = variants?; 1138 | 1139 | // TODO: respect opaque? 1140 | let val = Value::Match { 1141 | matched: Box::new(matched_val), 1142 | variants, 1143 | }.progress(); 1144 | 1145 | Evaluated::create(val, cur_hint, src) 1146 | }, 1147 | ExprInner::CtorOf { parent, variant } => { 1148 | let parent = self.eval(parent.as_ref(), scope, Rc::new(Type::Hole), at, runner)?; 1149 | let demoted = parent.0.demote_ind_type(); 1150 | let ind = match demoted { 1151 | Value::PartiallyIndexedInd { ind, indexes } if indexes.len() == 0 => { 1152 | match ind { 1153 | IndPtr::SelfInvoc => unreachable!(), // We should never get self out side of ind defination 1154 | IndPtr::Complete(ind) => ind, 1155 | } 1156 | }, 1157 | _ => { 1158 | return Err(EvalError::NonIndCtor { actual: demoted }); 1159 | }, 1160 | }; 1161 | 1162 | let ctor_type = ind.variants.get(*variant).ok_or(EvalError::UndefinedCtor { ctor: (*variant).to_owned(), ind: ind.clone() })?; 1163 | let ctor_type = ctor_type.clone().instantiate_self(ind.clone(), true); 1164 | let ctor_type = ctor_type.try_unify(hint)?; 1165 | 1166 | Evaluated::create( 1167 | Value::Inductive { 1168 | ind, 1169 | ctor: (*variant).to_owned(), 1170 | data: im::Vector::new(), 1171 | }, 1172 | ctor_type, 1173 | Source::Constructed, 1174 | ) 1175 | }, 1176 | ExprInner::Universe{ level } => { 1177 | let level = level.expect("Currently we don't support the `type _` universe kind"); 1178 | let self_type = hint.try_unify(Rc::new(Type::Type { 1179 | universe: Some(level + 1), 1180 | }))?; 1181 | Evaluated::create( 1182 | Value::Type(Rc::new(Type::Type { universe: Some(level) })), 1183 | self_type, 1184 | Source::Constructed, 1185 | ) 1186 | } 1187 | ExprInner::SelfInvoc => { 1188 | let val = Value::PartiallyIndexedInd{ ind: IndPtr::SelfInvoc, indexes: im::Vector::new() }; 1189 | Evaluated::create(val, scope.ind_type.clone().ok_or(EvalError::SelfOutsideInd)?, Source::Constructed) 1190 | } 1191 | ExprInner::ReflInvoc => { 1192 | let ty_ident = self.count_external(); 1193 | let val_ident = self.count_external(); 1194 | let ty = Rc::new(Type::Delayed(Value::Pending(ty_ident))); 1195 | Evaluated::create(Value::Refl, Rc::new(Type::Fun { 1196 | arg: Rc::new(Type::Type { universe: None }), 1197 | ident: ty_ident, 1198 | ret: Rc::new(Type::Fun { 1199 | arg: ty.clone(), 1200 | ident: val_ident, 1201 | ret: Rc::new(Type::Eq { 1202 | within: ty, 1203 | lhs: Value::Pending(val_ident), 1204 | rhs: Value::Pending(val_ident), 1205 | }), 1206 | }), 1207 | }), Source::Constructed) 1208 | } 1209 | ExprInner::Cast { orig, eq } => { 1210 | let eq = self.eval(eq, scope, Rc::new(Type::Eq { 1211 | within: Rc::new(Type::Type { universe: None }), 1212 | lhs: Value::Type(Rc::new(Type::Hole)), 1213 | rhs: Value::Type(hint).compact(), 1214 | }), at, runner)?; 1215 | let (lhs, rhs, univ) = match eq.1.as_ref() { 1216 | Type::Eq { 1217 | within, 1218 | lhs, 1219 | rhs, 1220 | } => { 1221 | let univ = if let Type::Type { universe } = within.as_ref() { 1222 | universe 1223 | } else { 1224 | return Err(EvalError::NonTyEqCast { ty: eq.1 }); 1225 | }; 1226 | (lhs.clone().try_unwrap_as_type()?, rhs.clone().try_unwrap_as_type()?, univ) 1227 | }, 1228 | _ => return Err(EvalError::NonTyEqCast { ty: eq.1 }), 1229 | }; 1230 | 1231 | // TODO: check universe 1232 | let mut val = self.eval(orig, scope, lhs.clone(), at, runner)?; 1233 | // Fixme: check lhs and orig type is the same 1234 | if val.1 != lhs { 1235 | println!("evaluated type: {}", val.1); 1236 | println!("expected type: {}", lhs); 1237 | panic!("Cast eq sanity check failed"); 1238 | } 1239 | 1240 | val.1 = rhs; 1241 | Ok(val) 1242 | }, 1243 | ExprInner::EqAp { eq, fun } => { 1244 | let unified = hint.try_unify(Rc::new(Type::Eq { 1245 | within: Rc::new(Type::Hole), 1246 | lhs: Value::Placeholder, 1247 | rhs: Value::Placeholder, 1248 | }))?; 1249 | 1250 | let within = match unified.as_ref() { 1251 | Type::Eq { 1252 | within, 1253 | .. 1254 | } => { 1255 | within 1256 | }, 1257 | _ => unreachable!() 1258 | }; 1259 | 1260 | let eq = self.eval(eq, scope, Rc::new(Type::Eq { 1261 | within: Rc::new(Type::Hole), 1262 | lhs: Value::Placeholder, 1263 | rhs: Value::Placeholder, 1264 | }), at, runner)?; 1265 | 1266 | let (lhs, rhs, arg_ty) = match eq.1.as_ref() { 1267 | Type::Eq { 1268 | within, 1269 | lhs, 1270 | rhs, 1271 | } => { 1272 | (lhs, rhs, within) 1273 | }, 1274 | _ => unreachable!() 1275 | }; 1276 | 1277 | let fun = self.eval(fun, scope, Rc::new(Type::Fun { 1278 | arg: arg_ty.clone(), 1279 | ident: 0, 1280 | ret: Rc::new(Type::Hole), 1281 | }), at, runner)?; 1282 | 1283 | // Asserts ret is non dependent on fun 1284 | let (ident, ret) = match fun.1.as_ref() { 1285 | Type::Fun { ident, ret, .. } => (ident, ret), 1286 | _ => unreachable!() 1287 | }; 1288 | 1289 | let lhs_ap_ty = ret.clone().substitute(*ident, lhs); 1290 | let rhs_ap_ty = ret.clone().substitute(*ident, rhs); 1291 | if lhs_ap_ty != rhs_ap_ty { 1292 | log::info!("{:?}", lhs_ap_ty); 1293 | log::info!("{:?}", rhs_ap_ty); 1294 | return Err(EvalError::DependentTransport { ty: fun.1 }) 1295 | } 1296 | 1297 | within.clone().try_unify(lhs_ap_ty.clone())?; 1298 | 1299 | let lhs_ap = Value::Ap { 1300 | fun: Box::new(fun.0.clone()), 1301 | arg: Box::new(lhs.clone()), 1302 | }.progress(); 1303 | 1304 | let rhs_ap = Value::Ap { 1305 | fun: Box::new(fun.0), 1306 | arg: Box::new(rhs.clone()), 1307 | }.progress(); 1308 | 1309 | let ret_ty = Rc::new(Type::Eq { 1310 | within: lhs_ap_ty.clone(), 1311 | lhs: lhs_ap, 1312 | rhs: rhs_ap, 1313 | }); 1314 | 1315 | Evaluated::create(Value::Equality(ret_ty.clone()), ret_ty, Source::Constructed) 1316 | }, 1317 | ExprInner::Eq { lhs, rhs } => { 1318 | let lhs = self.eval(lhs, scope, Rc::new(Type::Hole), at, runner)?; 1319 | let rhs = self.eval(rhs, scope, lhs.1.clone(), at, runner)?; 1320 | 1321 | let within = rhs.1.clone(); 1322 | let eq = Rc::new(Type::Eq { 1323 | within, 1324 | lhs: lhs.0, 1325 | rhs: rhs.0, 1326 | }); 1327 | 1328 | // FIXME: universe + 1 1329 | 1330 | Evaluated::create( 1331 | Value::Type(eq), 1332 | Rc::new(Type::Type { universe: None }), 1333 | Source::Constructed, 1334 | ) 1335 | }, 1336 | ExprInner::StructTy(fields) => { 1337 | let hint = hint.try_unify(Rc::new(Type::Type{ universe: None }))?; 1338 | let mut ret = im::HashMap::new(); 1339 | for f in fields { 1340 | let v = f.sig.as_ref() 1341 | .map(|s| self.eval(s, scope, Rc::new(Type::Type{ universe: None }), at, runner)) 1342 | .map(|e| e.and_then(|e| e.0.try_unwrap_as_type())).transpose()? 1343 | .unwrap_or_else(|| Rc::new(Type::Hole)); 1344 | ret.insert(f.name.to_owned(), v); 1345 | } 1346 | 1347 | // TODO: check universe 1348 | Evaluated::create( 1349 | Value::Type(Rc::new(Type::Struct(ret))), 1350 | hint, 1351 | Source::Constructed, 1352 | ) 1353 | }, 1354 | ExprInner::Struct(fields) => { 1355 | let mut field_hints = im::HashMap::new(); 1356 | for binding in fields { 1357 | let sig = if let Some(sig) = &binding.name.sig { 1358 | self.eval_hint(sig, scope, at, runner)? 1359 | } else { 1360 | Rc::new(Type::Hole) 1361 | }; 1362 | field_hints.insert(binding.name.name.to_owned(), sig); 1363 | } 1364 | let unified = hint.try_unify(Rc::new(Type::Struct(field_hints)))?; 1365 | let mut field_hints = match &*unified { 1366 | Type::Struct(f) => f.clone(), 1367 | _ => unreachable!(), 1368 | }; 1369 | 1370 | let mut values = im::HashMap::new(); 1371 | for binding in fields { 1372 | let hint = field_hints.get(binding.name.name).unwrap(); 1373 | let evaled = self.eval(&binding.val, scope, hint.clone(), at, runner)?; 1374 | values.insert(binding.name.name.to_owned(), evaled.0); 1375 | field_hints.insert(binding.name.name.to_owned(), evaled.1); 1376 | } 1377 | 1378 | Evaluated::create(Value::Struct(values), Rc::new(Type::Struct(field_hints)), Source::Constructed) 1379 | }, 1380 | 1381 | ExprInner::Field { parent, field } => { 1382 | let parent_eval = self.eval(parent.as_ref(), scope, Rc::new(Type::Hole), at, runner)?; 1383 | let field_types = match &*parent_eval.1 { 1384 | &Type::Struct(ref s) => { 1385 | s 1386 | }, 1387 | _ => return Err(EvalError::NonStructField { actual: parent_eval.0, ty: parent_eval.1 }), 1388 | }; 1389 | let field_ty = field_types.get(*field).ok_or_else(|| EvalError::UndefinedField { field: (*field).to_owned(), ty: parent_eval.1.clone() })?; 1390 | let field_ty = hint.try_unify(field_ty.clone())?; 1391 | 1392 | let val = parent_eval.0.field_with((*field).to_owned()); 1393 | 1394 | let src = match parent_eval.2 { 1395 | Source::External { ident: source } | Source::Destructed { source } => Source::Destructed { source }, 1396 | _ => Source::Constructed, 1397 | }; 1398 | 1399 | Evaluated::create(val, field_ty, src) 1400 | }, 1401 | ExprInner::Import(path) => { 1402 | runner.run_import(at, path).map_err(|error| EvalError::Import { path: format!("{}", path), error }) 1403 | }, 1404 | } 1405 | } 1406 | 1407 | pub fn eval_hint<'a, PI: Debug>(&mut self, sig: &'a Expr<'a, PI>, scope: &Scope<'a>, at: &PathBuf, runner: &mut Runner) -> EvalResult, PI> { 1408 | let evaluated = self.eval(sig, scope, Rc::new(Type::Type{ universe: None }), at, runner)?; 1409 | // TODO: check is type 1410 | evaluated.try_unwrap_as_type() 1411 | } 1412 | 1413 | fn count_external(&mut self) -> ExtBindingIdent { 1414 | self.ticket_gen += 1; 1415 | self.ticket_gen 1416 | } 1417 | 1418 | pub fn eval_top<'a, PI: Debug>(&mut self, expr: &'a Expr<'a, PI>, at: &PathBuf, runner: &mut Runner) -> EvalResult { 1419 | let scope = Scope::default(); 1420 | let hint = Rc::new(Type::Hole); 1421 | self.eval(expr, &scope, hint, at, runner) 1422 | } 1423 | } -------------------------------------------------------------------------------- /src/exec.rs: -------------------------------------------------------------------------------- 1 | use std::{path::PathBuf, collections::{HashMap, HashSet}, ops::RangeBounds}; 2 | 3 | use crate::{data::Path, eval::{EvalCtx, Evaluated}}; 4 | use crate::{lexer::tokenize, parser::parse}; 5 | 6 | fn resolve<'a>(cur: &PathBuf, p: &Path<'a>) -> anyhow::Result { 7 | let mut cur = cur.clone(); 8 | match p { 9 | Path::Absolute(_) => { 10 | unimplemented!() 11 | }, 12 | Path::Relative(ref segs) => { 13 | if !cur.pop() { 14 | return Err(anyhow::anyhow!("Unable to resolve: {}", p)); 15 | } 16 | for seg in segs { 17 | match seg { 18 | crate::data::RelPathSeg::Up => if !cur.pop() { 19 | return Err(anyhow::anyhow!("Unable to resolve: {}", p)); 20 | } 21 | crate::data::RelPathSeg::Down(d) => cur.push(*d), 22 | } 23 | } 24 | }, 25 | } 26 | 27 | cur.set_extension("mtk"); 28 | Ok(cur) 29 | } 30 | 31 | #[derive(Default)] 32 | pub struct Runner { 33 | cache: HashMap, 34 | inflights: HashSet, 35 | } 36 | 37 | impl Runner { 38 | pub fn run(&mut self, file: PathBuf) -> anyhow::Result { 39 | let file = file.canonicalize()?; 40 | 41 | if !self.inflights.insert(file.clone()) { 42 | return Err(anyhow::anyhow!("Curcular import at {}", file.display())); 43 | } 44 | 45 | let input = std::fs::read_to_string(&file)?; 46 | let tokens = tokenize(&input); 47 | let mut tokens = tokens.peekable(); 48 | 49 | let ast = match parse(&mut tokens) { 50 | Ok(ast) => ast, 51 | Err(e) => { 52 | log::error!("{}", e); 53 | return Err(anyhow::anyhow!("Parsing failed")) 54 | } 55 | }; 56 | 57 | log::debug!("{:#?}", ast); 58 | 59 | let mut ctx = EvalCtx::default(); 60 | 61 | let ret = ctx.eval_top(&ast, &file, self).map_err(|e| { 62 | log::error!("{}", e); 63 | anyhow::anyhow!("Evaluation failed") 64 | })?; 65 | 66 | assert!(self.inflights.remove(&file)); 67 | self.cache.insert(file, ret.clone()); 68 | 69 | Ok(ret) 70 | } 71 | 72 | pub fn run_import<'a>(&mut self, cur: &PathBuf, p: &Path<'a>) -> anyhow::Result { 73 | let p = resolve(cur, p)?; 74 | log::debug!("Import: {}", p.display()); 75 | 76 | self.run(p) 77 | } 78 | } -------------------------------------------------------------------------------- /src/lexer.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, PartialEq, Eq)] 2 | pub enum TokenInner<'a> { 3 | /// Paren 4 | ParenLeft, ParenRight, 5 | 6 | /// Bracket 7 | BracketLeft, BracketRight, 8 | 9 | /// Semi-colon 10 | SemiColon, 11 | 12 | // Comma 13 | Comma, 14 | 15 | Keyword(Keyword), 16 | 17 | /// Any other alphanumerical word 18 | Word(&'a str), 19 | 20 | /// Any other symbol combination 21 | Symb(&'a str), 22 | } 23 | 24 | #[derive(Debug, PartialEq, Eq)] 25 | pub enum PreTokenInner<'a> { 26 | Token(TokenInner<'a>), 27 | 28 | /// !alphabetical 29 | Macro(&'a str), 30 | 31 | Comment(&'a str), 32 | } 33 | 34 | #[derive(Debug, PartialEq, Eq)] 35 | pub enum Keyword { 36 | Let, 37 | Ind, 38 | Partial, 39 | Type, 40 | Match, 41 | SelfType, 42 | Refl, 43 | Struct, 44 | Import, 45 | } 46 | 47 | impl Keyword { 48 | pub fn from_str(s: &str) -> Option { 49 | match s { 50 | "ind" => Some(Self::Ind), 51 | "partial" => Some(Self::Partial), 52 | "let" => Some(Self::Let), 53 | "type" => Some(Self::Type), 54 | "match" => Some(Self::Match), 55 | "self" => Some(Self::SelfType), 56 | "refl" => Some(Self::Refl), 57 | "struct" => Some(Self::Struct), 58 | "import" => Some(Self::Import), 59 | // "import" 60 | _ => None, 61 | } 62 | } 63 | } 64 | 65 | #[derive(Debug)] 66 | pub struct SrcRange { 67 | pub from: (usize, usize), 68 | pub to: (usize, usize), 69 | } 70 | 71 | #[derive(Debug)] 72 | pub struct Token<'a> { 73 | pub inner: TokenInner<'a>, 74 | pub loc: SrcRange, 75 | } 76 | 77 | #[derive(Debug)] 78 | pub struct PreToken<'a> { 79 | pub inner: PreTokenInner<'a>, 80 | pub loc: SrcRange, 81 | } 82 | 83 | pub fn map_builtin<'a>(c: char) -> Option> { 84 | match c { 85 | '{' => Some(TokenInner::BracketLeft), 86 | '}' => Some(TokenInner::BracketRight), 87 | '(' => Some(TokenInner::ParenLeft), 88 | ')' => Some(TokenInner::ParenRight), 89 | ';' => Some(TokenInner::SemiColon), 90 | ',' => Some(TokenInner::Comma), 91 | _ => None, 92 | } 93 | } 94 | 95 | pub fn tokenize<'a>(mut input: &'a str) -> impl Iterator> { 96 | let mut current_line = 0; 97 | let mut current_pos = 0; 98 | // TODO: handles Unicode 99 | std::iter::from_fn(move || -> Option> { 100 | while let Some(first) = input.chars().next() { 101 | if !first.is_whitespace() { 102 | break; 103 | } 104 | 105 | if first == '\n' { 106 | current_pos = 0; 107 | current_line += 1; 108 | } else { 109 | current_pos += 1; // TODO: width? 110 | } 111 | input = &input[first.len_utf8()..]; 112 | } 113 | 114 | if input.len() == 0 { 115 | return None; 116 | } 117 | 118 | let first_char = input.chars().nth(0).unwrap(); 119 | if let Some(inner) = map_builtin(first_char) { 120 | let loc = SrcRange { 121 | from: (current_line, current_pos), 122 | to: (current_line, current_pos), 123 | }; 124 | 125 | current_pos += 1; 126 | input = &input[1..]; 127 | return Some(Token { 128 | inner, loc 129 | }); 130 | } 131 | 132 | let mut token_len = first_char.len_utf8(); 133 | let mut token_visible_len = 1; 134 | 135 | let is_punc = first_char.is_ascii_punctuation() && first_char != '_'; 136 | let is_macro = if first_char == '!' { 137 | let second = input.chars().nth(1); 138 | if let Some(inner) = second { 139 | if inner.is_alphanumeric() || inner == '_' { 140 | true 141 | } else { 142 | false 143 | } 144 | } else { 145 | false 146 | } 147 | } else { 148 | false 149 | }; 150 | 151 | for c in input.chars().skip(1) { 152 | if c.is_whitespace() || (c.is_ascii_punctuation() && c != '_') != (is_punc && !is_macro) || map_builtin(c).is_some() { 153 | break; 154 | } 155 | 156 | token_len += c.len_utf8(); 157 | token_visible_len += 1; 158 | } 159 | 160 | let content = &input[0..token_len]; 161 | input = &input[token_len..]; 162 | 163 | let inner = if is_macro { 164 | unimplemented!("macro"); 165 | } else if let Some(kw) = Keyword::from_str(content) { 166 | TokenInner::Keyword(kw) 167 | } else if is_punc { 168 | TokenInner::Symb(content) 169 | } else { 170 | TokenInner::Word(content) 171 | }; 172 | 173 | let loc = SrcRange { 174 | from: (current_line, current_pos), 175 | to: (current_line, current_pos + token_visible_len - 1), 176 | }; 177 | 178 | current_pos += token_visible_len; 179 | 180 | Some(Token { 181 | inner, 182 | loc, 183 | }) 184 | }) 185 | } -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(if_let_guard)] 2 | 3 | mod data; 4 | mod lexer; 5 | mod parser; 6 | mod eval; 7 | mod exec; 8 | 9 | use std::path::PathBuf; 10 | 11 | use structopt::StructOpt; 12 | use simplelog::*; 13 | 14 | use crate::eval::Evaluated; 15 | 16 | #[derive(StructOpt)] 17 | struct Args { 18 | /// Log level 19 | #[structopt(long, default_value="info")] 20 | log_level: log::LevelFilter, 21 | 22 | /// Path to the entry file 23 | entry: PathBuf, 24 | } 25 | 26 | #[paw::main] 27 | fn main(args: Args) -> anyhow::Result<()> { 28 | TermLogger::init(args.log_level, Config::default(), TerminalMode::Mixed, ColorChoice::Auto)?; 29 | let mut runner = exec::Runner::default(); 30 | let Evaluated(v, t, src) = runner.run(args.entry)?; 31 | log::info!("{}", v); 32 | log::debug!("{:#?}", v); 33 | 34 | Ok(()) 35 | } 36 | -------------------------------------------------------------------------------- /src/parser/expr.rs: -------------------------------------------------------------------------------- 1 | use std::iter::Peekable; 2 | 3 | use crate::{data::*, lexer::{Token, TokenInner, Keyword}}; 4 | 5 | use super::{ParseError, ParseResult, ind::parse_ctors, parse_single, parse_str, parse_level}; 6 | 7 | fn binding_power_bin(op: &str) -> Option<(isize, isize)> { 8 | match op { 9 | // # => 100 10 | // . => 10 11 | "^" => Some((-9, -10)), 12 | "/" => Some((-9, -10)), 13 | "==" => Some((-19, -20)), 14 | "->" => Some((-24, -25)), 15 | ":" => Some((-29, -30)), 16 | // "=>" -40 17 | "=" => Some((-50, -49)), 18 | _ => None, 19 | } 20 | } 21 | 22 | pub fn parse_expr<'a, I: Iterator>>(tokens: &mut Peekable, power: isize) -> ParseResult<'a, Expr<'a, ()>> { 23 | let first = tokens.next().ok_or(ParseError::SoftEOF)?; 24 | let mut lhs = match first.inner { 25 | TokenInner::SemiColon | TokenInner::ParenRight | TokenInner::BracketRight | TokenInner::Comma => { 26 | return Err(ParseError::UnexpectedToken{ token: first }); 27 | } 28 | TokenInner::ParenLeft => { 29 | // Step after the paren 30 | let inner = parse_expr(tokens, isize::MIN)?; 31 | parse_single(tokens, TokenInner::ParenRight)?; 32 | inner 33 | }, 34 | TokenInner::BracketLeft => { 35 | let inner = ExprInner::Struct(parse_struct_body(tokens)?).with(()); 36 | parse_single(tokens, TokenInner::BracketRight)?; 37 | inner 38 | }, 39 | 40 | TokenInner::Keyword(kw) => match kw { 41 | // TODO: check for binding power for keywords, and update binding power 42 | Keyword::Let => { 43 | let binding = Box::new(parse_binding_tail(tokens)?); 44 | let rest = Box::new(parse_expr(tokens, power)?); 45 | ExprInner::Binding { 46 | binding, 47 | rest, 48 | }.with(()) 49 | }, 50 | Keyword::Struct => { 51 | parse_single(tokens, TokenInner::BracketLeft)?; 52 | let inner = ExprInner::StructTy(parse_struct_type_body(tokens)?).with(()); 53 | parse_single(tokens, TokenInner::BracketRight)?; 54 | inner 55 | }, 56 | Keyword::Partial => { 57 | let inner = parse_expr(tokens, 90)?; 58 | ExprInner::PartialType(Box::new(inner)).with(()) 59 | }, 60 | Keyword::Ind => { 61 | let peek = tokens.peek(); 62 | let sig = if peek.is_none() || peek.unwrap().inner != TokenInner::BracketLeft { 63 | Some(Box::new(parse_expr(tokens, 80)?)) 64 | } else { 65 | None 66 | }; 67 | let ctors = parse_ctors(tokens)?; 68 | ExprInner::Ind { 69 | sig, 70 | ctors, 71 | }.with(()) 72 | }, 73 | Keyword::Match => { 74 | let matched = Box::new(parse_expr(tokens, 70)?); 75 | let arms = parse_match_body(tokens)?; 76 | 77 | ExprInner::Match { 78 | matched, 79 | arms, 80 | }.with(()) 81 | }, 82 | Keyword::Type => { 83 | let level = parse_level(tokens)?; 84 | ExprInner::Universe { level }.with(()) 85 | }, 86 | Keyword::SelfType => ExprInner::SelfInvoc.with(()), 87 | Keyword::Refl => ExprInner::ReflInvoc.with(()), 88 | Keyword::Import => ExprInner::Import(parse_import_tail(tokens)?).with(()), 89 | }, 90 | TokenInner::Symb(op) | TokenInner::Word(op) => { 91 | if op == "\\" { 92 | parse_lambda_tail(tokens)? 93 | } else { 94 | // Is a single word 95 | ExprInner::Name(Box::new(Name { 96 | name: op, 97 | sig: None, 98 | })).with(()) 99 | } 100 | } 101 | }; 102 | 103 | loop { 104 | let op = match tokens.peek() { 105 | None => break, 106 | Some(inner) => inner, 107 | }; 108 | 109 | let mut is_binary = false; 110 | match op.inner { 111 | TokenInner::SemiColon | TokenInner::ParenRight | TokenInner::BracketRight | TokenInner::Comma | TokenInner::BracketLeft => { 112 | break; 113 | } 114 | TokenInner::Symb(op) | TokenInner::Word(op) => { 115 | if op == "#" { 116 | tokens.next().unwrap(); 117 | is_binary = true; 118 | lhs = ExprInner::CtorOf { 119 | parent: Box::new(lhs), 120 | variant: parse_str(tokens)?, 121 | }.with(()); 122 | } else if op == "." { 123 | tokens.next().unwrap(); 124 | is_binary = true; 125 | lhs = ExprInner::Field { 126 | parent: Box::new(lhs), 127 | field: parse_str(tokens)?, 128 | }.with(()); 129 | } else if op == "=>" { 130 | break; 131 | } else if let Some((lbp, rbp)) = binding_power_bin(op) { 132 | is_binary = true; 133 | if lbp < power { 134 | break; 135 | } 136 | 137 | let token = tokens.next().unwrap(); 138 | let rhs = parse_expr(tokens, rbp)?; 139 | 140 | match op { 141 | "->" => { 142 | lhs = ExprInner::Fun(Fun { 143 | input: Box::new(lhs), 144 | output: Box::new(rhs), 145 | }).with(()); 146 | }, 147 | ":" => { 148 | // TODO: let's do this more sophicately 149 | if let ExprInner::Name(ref mut name) = lhs.inner { 150 | if name.sig.is_some() { 151 | return Err(ParseError::UnexpectedToken{ token }); 152 | } 153 | 154 | name.sig = Some(rhs); 155 | } else { 156 | return Err(ParseError::UnexpectedToken{ token }); 157 | } 158 | }, 159 | "==" => { 160 | lhs = ExprInner::Eq { 161 | lhs: Box::new(lhs), 162 | rhs: Box::new(rhs), 163 | }.with(()); 164 | }, 165 | "/" => { 166 | lhs = ExprInner::Cast{ 167 | orig: Box::new(lhs), 168 | eq: Box::new(rhs), 169 | }.with(()); 170 | }, 171 | "^" => { 172 | lhs = ExprInner::EqAp { 173 | eq: Box::new(lhs), 174 | fun: Box::new(rhs), 175 | }.with(()); 176 | }, 177 | _ => unreachable!(), 178 | } 179 | } 180 | } 181 | _ => {} 182 | } 183 | 184 | if !is_binary { 185 | // Is function application 186 | if power >= 0 { 187 | break; 188 | } 189 | 190 | let rhs = parse_expr(tokens, 1)?; 191 | lhs = ExprInner::Ap(Box::new((lhs, rhs))).with(()); 192 | } 193 | } 194 | 195 | Ok(lhs) 196 | } 197 | 198 | pub fn parse_capture_group<'a, I: Iterator>>(tokens: &mut Peekable) -> ParseResult<'a, Name<'a, ()>> { 199 | let contains_paren = match tokens.peek() { 200 | None => return Err(ParseError::EOF), 201 | Some(t) => match t.inner { 202 | TokenInner::ParenLeft => { 203 | tokens.next(); 204 | true 205 | }, 206 | TokenInner::Word(_) | TokenInner::Symb(_) => false, 207 | _ => return Err(ParseError::UnexpectedToken{ token: tokens.next().unwrap() }), 208 | } 209 | }; 210 | 211 | // Parse name & sig 212 | let name = parse_str(tokens)?; 213 | let sig = if tokens.peek().map(|t| t.inner == TokenInner::Symb(":")) == Some(true) { 214 | tokens.next(); 215 | Some(parse_expr(tokens, -30)?) 216 | } else { 217 | None 218 | }; 219 | 220 | if contains_paren { 221 | parse_single(tokens, TokenInner::ParenRight)?; 222 | } 223 | 224 | Ok(Name { 225 | name, sig 226 | }) 227 | } 228 | 229 | 230 | pub fn parse_lambda_tail<'a, I: Iterator>>(tokens: &mut Peekable) -> ParseResult<'a, Expr<'a, ()>> { 231 | let arg = Box::new(parse_capture_group(tokens)?); 232 | // Parse result value 233 | let ret = if tokens.peek().map(|t| t.inner == TokenInner::Symb("->")) == Some(true) { 234 | tokens.next(); 235 | Some(Box::new(parse_expr(tokens, -20)?)) 236 | } else { 237 | None 238 | }; 239 | 240 | let mut rec = None; 241 | while tokens.peek().map(|t| t.inner == TokenInner::Comma) == Some(true) { 242 | tokens.next(); 243 | let mut attr = Vec::new(); 244 | let mut first = None; 245 | 246 | loop { 247 | let peek = tokens.peek().map(|t| t.inner == TokenInner::Symb("=>") || t.inner == TokenInner::Symb(",")); 248 | if peek == Some(true) || peek == None { 249 | break; 250 | } 251 | 252 | if first.is_none() { 253 | first = Some(tokens.next().unwrap()); 254 | } else { 255 | attr.push(parse_str(tokens)?); 256 | } 257 | } 258 | 259 | let directive = match first { 260 | None => return Err(ParseError::EOF), 261 | Some(ref t) => match t.inner { 262 | TokenInner::Word(d) => d, 263 | _ => return Err(ParseError::UnexpectedToken{ token: first.unwrap() }), 264 | } 265 | }; 266 | 267 | match directive { 268 | "rec" if attr.len() == 1 => { 269 | rec = Some(attr[0]); 270 | }, 271 | _ => return Err(ParseError::MalformedAttr{ token: first.unwrap() }) 272 | } 273 | } 274 | 275 | parse_single(tokens, TokenInner::Symb("=>"))?; 276 | let body = Box::new(parse_expr(tokens, -40)?); 277 | Ok(ExprInner::Lambda { 278 | arg, 279 | ret, 280 | body, 281 | 282 | rec, 283 | }.with(())) 284 | } 285 | 286 | pub fn parse_binding_tail<'a, I: Iterator>>(tokens: &mut Peekable) -> ParseResult<'a, Binding<'a, ()>> { 287 | let name = parse_str(tokens)?; 288 | let mut sig = None; 289 | if let Some(t) = tokens.next() { 290 | match t.inner { 291 | TokenInner::Symb(":") => { 292 | // Has signature 293 | sig = Some(parse_expr(tokens, -30)?); 294 | parse_single(tokens, TokenInner::Symb("="))?; 295 | } 296 | TokenInner::Symb("=") => {}, 297 | _ => { 298 | return Err(ParseError::UnexpectedToken{ token: t }); 299 | } 300 | } 301 | } else { 302 | return Err(ParseError::SoftEOF); 303 | } 304 | 305 | let val = parse_expr(tokens, -49)?; 306 | parse_single(tokens, TokenInner::SemiColon)?; 307 | 308 | Ok(Binding { 309 | name: Name { 310 | name, 311 | sig, 312 | }, 313 | val, 314 | }) 315 | } 316 | 317 | pub fn parse_match_arm<'a, I: Iterator>>(tokens: &mut Peekable) -> ParseResult<'a, MatchArm<'a, ()>> { 318 | let ctor = parse_str(tokens)?; 319 | let mut data = Vec::new(); 320 | let mut ev = Vec::new(); 321 | let mut sig = None; 322 | 323 | loop { 324 | match tokens.peek() { 325 | None => break, 326 | Some(token) => match token.inner { 327 | TokenInner::Symb("/") => break, 328 | TokenInner::Symb("=>") => break, 329 | TokenInner::Symb("->") => break, 330 | _ => {} 331 | }, 332 | } 333 | 334 | data.push(parse_str(tokens)?); 335 | } 336 | 337 | loop { 338 | match tokens.next() { 339 | None => return Err(ParseError::EOF), 340 | Some(token) => match token.inner { 341 | TokenInner::Symb("/") => { 342 | // Parse evidences 343 | loop { 344 | match tokens.peek() { 345 | None => break, 346 | Some(token) => match token.inner { 347 | TokenInner::Symb("=>") => break, 348 | TokenInner::Symb("->") => break, 349 | _ => {} 350 | }, 351 | } 352 | 353 | ev.push(parse_capture_group(tokens)?); 354 | } 355 | }, 356 | TokenInner::Symb("->") => { 357 | sig = Some(parse_expr(tokens, 41)?); 358 | }, 359 | TokenInner::Symb("=>") => break, 360 | _ => return Err(ParseError::UnexpectedToken{ token }), 361 | } 362 | } 363 | } 364 | 365 | let body = parse_expr(tokens, -49)?; 366 | parse_single(tokens, TokenInner::SemiColon)?; 367 | Ok(MatchArm { 368 | ctor, 369 | data, 370 | ev, 371 | sig, 372 | body, 373 | }) 374 | } 375 | 376 | pub fn parse_match_body<'a, I: Iterator>>(tokens: &mut Peekable) -> ParseResult<'a, Vec>> { 377 | parse_single(tokens, TokenInner::BracketLeft)?; 378 | 379 | let mut arms = Vec::new(); 380 | 381 | // TODO: write a combinator: do_until 382 | loop { 383 | match tokens.peek() { 384 | None => break, 385 | Some(token) => match token.inner { 386 | TokenInner::BracketRight => break, 387 | _ => {} 388 | }, 389 | } 390 | 391 | arms.push(parse_match_arm(tokens)?); 392 | } 393 | parse_single(tokens, TokenInner::BracketRight)?; 394 | 395 | Ok(arms) 396 | } 397 | 398 | pub fn parse_struct_body<'a, I: Iterator>>(tokens: &mut Peekable) -> ParseResult<'a, Vec>> { 399 | let mut fields = Vec::new(); 400 | 401 | loop { 402 | if let Some(&TokenInner::BracketRight) = tokens.peek().map(|t| &t.inner) { 403 | break; 404 | } 405 | fields.push(parse_binding_tail(tokens)?); 406 | } 407 | 408 | Ok(fields) 409 | } 410 | 411 | pub fn parse_struct_type_body<'a, I: Iterator>>(tokens: &mut Peekable) -> ParseResult<'a, Vec>> { 412 | let mut fields = Vec::new(); 413 | 414 | loop { 415 | if let Some(&TokenInner::BracketRight) = tokens.peek().map(|t| &t.inner) { 416 | break; 417 | } 418 | 419 | let name = parse_str(tokens)?; 420 | let mut sig = None; 421 | if let Some(t) = tokens.next() { 422 | match t.inner { 423 | TokenInner::Symb(":") => { 424 | // Has signature 425 | sig = Some(parse_expr(tokens, -30)?); 426 | parse_single(tokens, TokenInner::SemiColon)?; 427 | } 428 | TokenInner::SemiColon => {}, 429 | _ => { 430 | return Err(ParseError::UnexpectedToken{ token: t }); 431 | } 432 | } 433 | } else { 434 | return Err(ParseError::SoftEOF); 435 | } 436 | fields.push(Name { name, sig }); 437 | } 438 | 439 | Ok(fields) 440 | } 441 | 442 | pub fn parse_import_tail<'a, I: Iterator>>(tokens: &mut Peekable) -> ParseResult<'a, Path<'a>> { 443 | let mut segs = Vec::new(); 444 | loop { 445 | let cur = match tokens.peek() { 446 | Some(t) => if let TokenInner::Word(w) = t.inner { 447 | tokens.next(); 448 | Some(w) 449 | } else { 450 | None 451 | }, 452 | _ => None, 453 | }; 454 | 455 | let at_end = match tokens.peek() { 456 | Some(t) => match t.inner { 457 | TokenInner::Symb(".") => { 458 | tokens.next(); 459 | false 460 | }, 461 | _ => true, 462 | }, 463 | _ => true, 464 | }; 465 | if at_end && !cur.is_some() { 466 | if let Some(token) = tokens.next() { 467 | return Err(ParseError::UnexpectedToken { token }) 468 | } else { 469 | return Err(ParseError::EOF) 470 | } 471 | } 472 | 473 | segs.push(cur); 474 | if at_end { 475 | break; 476 | } 477 | } 478 | 479 | let is_abs = segs.first().unwrap().is_some(); 480 | let iter = segs.iter(); 481 | if is_abs { 482 | let segs: Result, _> = segs.into_iter().map(|s| s.ok_or(ParseError::AbsPathUp)).collect(); 483 | let segs = segs?; 484 | Ok(Path::Absolute(segs)) 485 | } else { 486 | let segs = segs.into_iter().skip(1).map(|s| match s { 487 | Some(s) => RelPathSeg::Down(s), 488 | None => RelPathSeg::Up, 489 | }).collect(); 490 | Ok(Path::Relative(segs)) 491 | } 492 | } -------------------------------------------------------------------------------- /src/parser/ind.rs: -------------------------------------------------------------------------------- 1 | use std::iter::Peekable; 2 | 3 | use crate::{data::Ctor, lexer::{Token, TokenInner}}; 4 | 5 | use super::{ParseError, ParseResult, expr::parse_expr, parse_single, parse_str}; 6 | 7 | pub fn parse_ctors<'a, I: Iterator>>(tokens: &mut Peekable) -> ParseResult<'a, Vec>> { 8 | parse_single(tokens, TokenInner::BracketLeft)?; 9 | let mut result = Vec::new(); 10 | loop { 11 | match tokens.peek() { 12 | None => return Err(ParseError::EOF), 13 | Some(t) => { 14 | if t.inner == TokenInner::BracketRight { 15 | tokens.next(); 16 | break; 17 | } 18 | }, 19 | } 20 | let cur = parse_ctor(tokens)?; 21 | result.push(cur); 22 | } 23 | 24 | Ok(result) 25 | } 26 | 27 | pub fn parse_ctor<'a, I: Iterator>>(tokens: &mut Peekable) -> ParseResult<'a, Ctor<'a, ()>> { 28 | let name = parse_str(tokens)?; 29 | parse_single(tokens, TokenInner::Symb(":"))?; 30 | let sig = parse_expr(tokens, -30)?; 31 | parse_single(tokens, TokenInner::SemiColon)?; 32 | Ok(Ctor { 33 | name, 34 | sig, 35 | }) 36 | } -------------------------------------------------------------------------------- /src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod expr; 2 | pub mod ind; 3 | 4 | use std::iter::Peekable; 5 | 6 | use thiserror::Error; 7 | 8 | use crate::{data::Expr, lexer::{Token, TokenInner}}; 9 | 10 | use self::expr::parse_expr; 11 | 12 | 13 | #[derive(Error, Debug)] 14 | pub enum ParseError<'a> { 15 | #[error("Unexpected token: {token:?}")] 16 | UnexpectedToken { 17 | token: Token<'a> 18 | }, 19 | 20 | #[error("Malformed constructor: {token:?}")] 21 | MalformedCtr { 22 | token: Token<'a>, 23 | }, 24 | 25 | #[error("Malformed attribute: {token:?}")] 26 | MalformedAttr { 27 | token: Token<'a>, 28 | }, 29 | 30 | #[error("Malformed attribute: {token:?}")] 31 | MalformedUniverse { 32 | token: Token<'a>, 33 | }, 34 | 35 | #[error("Absolute import cannot have ..")] 36 | AbsPathUp, 37 | 38 | #[error("Unexpected EOF")] 39 | EOF, 40 | 41 | #[error("Unexpected EOF")] 42 | SoftEOF, 43 | } 44 | 45 | pub type ParseResult<'a, T> = Result>; 46 | 47 | fn parse_single<'a, I: Iterator>>(tokens: &mut Peekable, expected: TokenInner) -> ParseResult<'a, Token<'a>> { 48 | let token = tokens.next().ok_or(ParseError::SoftEOF)?; 49 | if token.inner != expected { 50 | return Err(ParseError::UnexpectedToken{ token }); 51 | } 52 | 53 | Ok(token) 54 | } 55 | 56 | fn parse_str<'a, I: Iterator>>(tokens: &mut Peekable) -> ParseResult<'a, &'a str> { 57 | let token = tokens.next().ok_or(ParseError::SoftEOF)?; 58 | match token.inner { 59 | TokenInner::Symb(s) | TokenInner::Word(s) => Ok(s), 60 | _ => Err(ParseError::UnexpectedToken{ token }), 61 | } 62 | } 63 | 64 | fn parse_level<'a, I: Iterator>>(tokens: &mut Peekable) -> ParseResult<'a, Option> { 65 | let token = tokens.next().ok_or(ParseError::SoftEOF)?; 66 | match token.inner { 67 | TokenInner::Symb("_") => Ok(None), 68 | TokenInner::Word(s) => s.parse::().map_err(|_| ParseError::MalformedUniverse{ token }).map(Some), 69 | _ => Err(ParseError::MalformedUniverse{ token }), 70 | } 71 | } 72 | 73 | pub fn parse<'a, I: Iterator>>(tokens: &mut Peekable) -> ParseResult<'a, Expr<'a, ()>> { 74 | parse_expr(tokens, isize::MIN) 75 | } -------------------------------------------------------------------------------- /testcases/even.mtk: -------------------------------------------------------------------------------- 1 | let nat: type 0 2 | = ind (type 0) { 3 | 0 : self; 4 | succ : self -> self; 5 | }; 6 | 7 | let double : nat -> nat 8 | = \x, rec double => match x { 9 | 0 / _ => nat#0; 10 | succ s / _ => nat#succ (nat#succ (double s)); 11 | }; 12 | 13 | let is_even : nat -> type 0 14 | = ind (nat -> type 0) { 15 | 0: self nat#0; 16 | SS: (n: nat) -> self n -> self (nat#succ (nat#succ n)); 17 | }; 18 | 19 | let all_double_is_even : (n: nat) -> is_even (double n) 20 | = \n, rec ih => match n { 21 | 0 / (neqz: nat#0 == n) => ( 22 | let neqz1 = neqz ^ double; 23 | let neqz2 = neqz1 ^ (\(x: nat) => is_even x); 24 | is_even#0 / neqz2 25 | ); 26 | succ prev / (neqsp: nat#succ prev == n) => ( 27 | let ihev: is_even (double prev) = ih prev; 28 | let ihevSS: is_even (nat#succ (nat#succ (double prev))) = is_even#SS (double prev) ihev; 29 | let neqsp1: (nat#succ (nat#succ (double prev))) == double n = neqsp ^ double; 30 | let neqsp2 = neqsp1 ^ (\(x: nat) => is_even x); 31 | ihevSS / neqsp2 32 | ); 33 | }; 34 | 35 | let false = ind (type 0) {}; 36 | let 1 = nat#succ nat#0; 37 | let eq_refl : (A: type 0) -> (x: A) -> (y: A) -> x == y -> y == x 38 | = \A => \x => \y => \ev => ( 39 | let transp = \(r: A) => r == x; 40 | let xrefl : x == x = refl A x; 41 | let eqeq : (x == x) == (y == x) = ev ^ transp; 42 | xrefl / eqeq 43 | ); 44 | 45 | let succ_not_zero : (x: nat) -> (nat#succ x) == nat#0 -> false 46 | = \x => \ev => ( 47 | let transp : nat -> type 0 = \(n: nat) => match n { 48 | 0 / _ => false; 49 | succ _ / _ => struct {}; 50 | }; 51 | 52 | let tyeq : struct {} == false = ev ^ transp; 53 | {} / tyeq 54 | ); 55 | 56 | let succ_inj : (n: nat) -> (m: nat) -> nat#succ n == nat#succ m -> n == m 57 | = \n => \m => \ev => ( 58 | let tycast : nat -> type 0 = \(n: nat) => match n { 59 | 0 / _ => struct {}; 60 | succ _ / _ => nat; 61 | }; 62 | let eq : (n: nat) -> tycast n = \n => match n { 63 | 0 / (eqev: nat#0 == n) => ( 64 | let tceq : struct {} == tycast n = eqev ^ tycast; 65 | {} / tceq 66 | ); 67 | succ k / (eqev: nat#succ k == n) => ( 68 | let tceq : nat == tycast n = eqev ^ tycast; 69 | k / tceq 70 | ); 71 | }; 72 | ev ^ eq 73 | ); 74 | 75 | let one_not_even : is_even 1 -> false 76 | = \ev => match ev { 77 | 0 / _ (eqev: nat#0 == 1) => ( 78 | let eqev1 = eq_refl nat nat#0 1 eqev; 79 | succ_not_zero nat#0 eqev1 80 | ); 81 | SS k _ / _ (eqev: nat#succ (nat#succ k) == 1) => ( 82 | let eqev1 : nat#succ k == nat#0 = succ_inj (nat#succ k) nat#0 eqev; 83 | succ_not_zero k eqev1 84 | ); 85 | }; 86 | 87 | {} -------------------------------------------------------------------------------- /testcases/imp.mtk: -------------------------------------------------------------------------------- 1 | let imported = import .str; 2 | 3 | imported.a -------------------------------------------------------------------------------- /testcases/nat.mtk: -------------------------------------------------------------------------------- 1 | let nat : type 0 2 | = ind { 3 | 0 : self; 4 | S: self -> self; 5 | }; 6 | 7 | let plus : nat -> nat -> nat 8 | = \a, rec r => \b => match a { 9 | 0 / _ => b; 10 | S x / _ => r x (nat#S b); 11 | }; 12 | 13 | let times : nat -> nat -> nat 14 | = \a, rec r => \b => match a { 15 | 0 / _ => nat#0; 16 | S x / _ => plus b (r x b); 17 | }; 18 | 19 | let 1 : nat = nat#S nat#0; 20 | let 2 : nat = nat#S 1; 21 | let 3 : nat = nat#S 2; 22 | 23 | times 3 2 -------------------------------------------------------------------------------- /testcases/str.mtk: -------------------------------------------------------------------------------- 1 | let nat: type 0 2 | = ind (type 0) { 3 | 0 : self; 4 | succ : self -> self; 5 | }; 6 | 7 | let s: struct { 8 | a: nat; 9 | b; 10 | n: type 0; 11 | } = { 12 | a: nat = nat#0; 13 | b = nat#succ nat#0; 14 | n = nat; 15 | }; 16 | 17 | s -------------------------------------------------------------------------------- /testcases/vector.mtk: -------------------------------------------------------------------------------- 1 | let nat : type 0 2 | = ind { 3 | 0 : self; 4 | S: self -> self; 5 | }; 6 | 7 | let plus : nat -> nat -> nat 8 | = \a, rec r => \b => match a { 9 | 0 / _ => b, 10 | S x / _ => r x (S b) 11 | }; 12 | 13 | let vector 14 | = \(T: Type) => 15 | def (nat -> type) { 16 | nil : self 0; 17 | con : (pl: nat) -> T -> self pl -> self (succ pl); 18 | }; -------------------------------------------------------------------------------- /tools/vscode/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | .gitignore 4 | vsc-extension-quickstart.md 5 | -------------------------------------------------------------------------------- /tools/vscode/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment. Remove this entry if your language does not support line comments 4 | "lineComment": "//", 5 | // symbols used for start and end a block comment. Remove this entry if your language does not support block comments 6 | "blockComment": [ "/*", "*/" ] 7 | }, 8 | // symbols used as brackets 9 | "brackets": [ 10 | ["{", "}"], 11 | ["[", "]"], 12 | ["(", ")"] 13 | ], 14 | // symbols that are auto closed when typing 15 | "autoClosingPairs": [ 16 | ["{", "}"], 17 | ["[", "]"], 18 | ["(", ")"], 19 | ["\"", "\""], 20 | ["'", "'"] 21 | ], 22 | // symbols that can be used to surround a selection 23 | "surroundingPairs": [ 24 | ["{", "}"], 25 | ["[", "]"], 26 | ["(", ")"], 27 | ["\"", "\""], 28 | ["'", "'"] 29 | ] 30 | } -------------------------------------------------------------------------------- /tools/vscode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "meowthink-syntax", 3 | "displayName": "MeowThink-syntax", 4 | "description": "Syntax for MeowThink", 5 | "version": "0.0.1", 6 | "engines": { 7 | "vscode": "^1.63.0" 8 | }, 9 | "categories": [ 10 | "Programming Languages" 11 | ], 12 | "contributes": { 13 | "languages": [{ 14 | "id": "meowthink", 15 | "aliases": ["MeowThink", "meowthink"], 16 | "extensions": [".mtk"], 17 | "configuration": "./language-configuration.json" 18 | }], 19 | "grammars": [{ 20 | "language": "meowthink", 21 | "scopeName": "source.mtk", 22 | "path": "./syntaxes/meowthink.tmLanguage.json" 23 | }] 24 | } 25 | } -------------------------------------------------------------------------------- /tools/vscode/syntaxes/meowthink.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "name": "MeowThink", 4 | "patterns": [ 5 | { 6 | "include": "#patterns" 7 | } 8 | ], 9 | "repository": { 10 | "patterns": { 11 | "patterns": [{ 12 | "name": "storage.type.meowthink", 13 | "match": "\\b(ind|struct)\\b" 14 | }, { 15 | "name": "keyword.control.meowthink", 16 | "match": "\\b(let|match|import|type)\\b" 17 | }, { 18 | "name": "variable.language.meowthink", 19 | "match": "\\b(refl|self)\\b" 20 | }] 21 | } 22 | }, 23 | "scopeName": "source.mtk" 24 | } --------------------------------------------------------------------------------