├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── build.rs └── src ├── main.rs ├── parser.lalrpop └── test.ml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea/ 3 | *.iml 4 | -------------------------------------------------------------------------------- /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 = "aho-corasick" 7 | version = "1.1.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "ascii-canvas" 16 | version = "3.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" 19 | dependencies = [ 20 | "term", 21 | ] 22 | 23 | [[package]] 24 | name = "autocfg" 25 | version = "1.1.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 28 | 29 | [[package]] 30 | name = "bit-set" 31 | version = "0.5.3" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" 34 | dependencies = [ 35 | "bit-vec", 36 | ] 37 | 38 | [[package]] 39 | name = "bit-vec" 40 | version = "0.6.3" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" 43 | 44 | [[package]] 45 | name = "bitflags" 46 | version = "1.2.1" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 49 | 50 | [[package]] 51 | name = "bitflags" 52 | version = "2.4.2" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" 55 | 56 | [[package]] 57 | name = "cfg-if" 58 | version = "0.1.10" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 61 | 62 | [[package]] 63 | name = "cfg-if" 64 | version = "1.0.0" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 67 | 68 | [[package]] 69 | name = "crunchy" 70 | version = "0.2.2" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 73 | 74 | [[package]] 75 | name = "dirs-next" 76 | version = "2.0.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" 79 | dependencies = [ 80 | "cfg-if 1.0.0", 81 | "dirs-sys-next", 82 | ] 83 | 84 | [[package]] 85 | name = "dirs-sys-next" 86 | version = "0.1.2" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" 89 | dependencies = [ 90 | "libc", 91 | "redox_users", 92 | "winapi", 93 | ] 94 | 95 | [[package]] 96 | name = "either" 97 | version = "1.5.3" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" 100 | 101 | [[package]] 102 | name = "ena" 103 | version = "0.14.2" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "c533630cf40e9caa44bd91aadc88a75d75a4c3a12b4cfde353cbed41daa1e1f1" 106 | dependencies = [ 107 | "log", 108 | ] 109 | 110 | [[package]] 111 | name = "equivalent" 112 | version = "1.0.1" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 115 | 116 | [[package]] 117 | name = "fixedbitset" 118 | version = "0.4.2" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" 121 | 122 | [[package]] 123 | name = "getrandom" 124 | version = "0.2.12" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" 127 | dependencies = [ 128 | "cfg-if 1.0.0", 129 | "libc", 130 | "wasi", 131 | ] 132 | 133 | [[package]] 134 | name = "hashbrown" 135 | version = "0.14.3" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 138 | 139 | [[package]] 140 | name = "indexmap" 141 | version = "2.2.4" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "967d6dd42f16dbf0eb8040cb9e477933562684d3918f7d253f2ff9087fb3e7a3" 144 | dependencies = [ 145 | "equivalent", 146 | "hashbrown", 147 | ] 148 | 149 | [[package]] 150 | name = "itertools" 151 | version = "0.11.0" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" 154 | dependencies = [ 155 | "either", 156 | ] 157 | 158 | [[package]] 159 | name = "lalrpop" 160 | version = "0.20.2" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" 163 | dependencies = [ 164 | "ascii-canvas", 165 | "bit-set", 166 | "ena", 167 | "itertools", 168 | "lalrpop-util", 169 | "petgraph", 170 | "pico-args", 171 | "regex", 172 | "regex-syntax 0.8.2", 173 | "string_cache", 174 | "term", 175 | "tiny-keccak", 176 | "unicode-xid", 177 | "walkdir", 178 | ] 179 | 180 | [[package]] 181 | name = "lalrpop-util" 182 | version = "0.20.2" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" 185 | dependencies = [ 186 | "regex-automata", 187 | ] 188 | 189 | [[package]] 190 | name = "libc" 191 | version = "0.2.153" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 194 | 195 | [[package]] 196 | name = "libredox" 197 | version = "0.0.1" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" 200 | dependencies = [ 201 | "bitflags 2.4.2", 202 | "libc", 203 | "redox_syscall", 204 | ] 205 | 206 | [[package]] 207 | name = "lock_api" 208 | version = "0.4.11" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" 211 | dependencies = [ 212 | "autocfg", 213 | "scopeguard", 214 | ] 215 | 216 | [[package]] 217 | name = "log" 218 | version = "0.4.8" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 221 | dependencies = [ 222 | "cfg-if 0.1.10", 223 | ] 224 | 225 | [[package]] 226 | name = "memchr" 227 | version = "2.7.1" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 230 | 231 | [[package]] 232 | name = "new_debug_unreachable" 233 | version = "1.0.4" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" 236 | 237 | [[package]] 238 | name = "once_cell" 239 | version = "1.19.0" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 242 | 243 | [[package]] 244 | name = "parking_lot" 245 | version = "0.12.1" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 248 | dependencies = [ 249 | "lock_api", 250 | "parking_lot_core", 251 | ] 252 | 253 | [[package]] 254 | name = "parking_lot_core" 255 | version = "0.9.9" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" 258 | dependencies = [ 259 | "cfg-if 1.0.0", 260 | "libc", 261 | "redox_syscall", 262 | "smallvec", 263 | "windows-targets", 264 | ] 265 | 266 | [[package]] 267 | name = "petgraph" 268 | version = "0.6.4" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" 271 | dependencies = [ 272 | "fixedbitset", 273 | "indexmap", 274 | ] 275 | 276 | [[package]] 277 | name = "phf_shared" 278 | version = "0.10.0" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" 281 | dependencies = [ 282 | "siphasher", 283 | ] 284 | 285 | [[package]] 286 | name = "pico-args" 287 | version = "0.5.0" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" 290 | 291 | [[package]] 292 | name = "precomputed-hash" 293 | version = "0.1.1" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" 296 | 297 | [[package]] 298 | name = "proc-macro2" 299 | version = "1.0.78" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" 302 | dependencies = [ 303 | "unicode-ident", 304 | ] 305 | 306 | [[package]] 307 | name = "quote" 308 | version = "1.0.35" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 311 | dependencies = [ 312 | "proc-macro2", 313 | ] 314 | 315 | [[package]] 316 | name = "redox_syscall" 317 | version = "0.4.1" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" 320 | dependencies = [ 321 | "bitflags 1.2.1", 322 | ] 323 | 324 | [[package]] 325 | name = "redox_users" 326 | version = "0.4.4" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" 329 | dependencies = [ 330 | "getrandom", 331 | "libredox", 332 | "thiserror", 333 | ] 334 | 335 | [[package]] 336 | name = "regex" 337 | version = "1.3.6" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" 340 | dependencies = [ 341 | "regex-syntax 0.6.17", 342 | ] 343 | 344 | [[package]] 345 | name = "regex-automata" 346 | version = "0.4.5" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" 349 | dependencies = [ 350 | "aho-corasick", 351 | "memchr", 352 | "regex-syntax 0.8.2", 353 | ] 354 | 355 | [[package]] 356 | name = "regex-syntax" 357 | version = "0.6.17" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" 360 | 361 | [[package]] 362 | name = "regex-syntax" 363 | version = "0.8.2" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 366 | 367 | [[package]] 368 | name = "rustversion" 369 | version = "1.0.14" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" 372 | 373 | [[package]] 374 | name = "same-file" 375 | version = "1.0.6" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 378 | dependencies = [ 379 | "winapi-util", 380 | ] 381 | 382 | [[package]] 383 | name = "scopeguard" 384 | version = "1.2.0" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 387 | 388 | [[package]] 389 | name = "siphasher" 390 | version = "0.3.11" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" 393 | 394 | [[package]] 395 | name = "smallvec" 396 | version = "1.13.1" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" 399 | 400 | [[package]] 401 | name = "string_cache" 402 | version = "0.8.7" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" 405 | dependencies = [ 406 | "new_debug_unreachable", 407 | "once_cell", 408 | "parking_lot", 409 | "phf_shared", 410 | "precomputed-hash", 411 | ] 412 | 413 | [[package]] 414 | name = "syn" 415 | version = "2.0.52" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" 418 | dependencies = [ 419 | "proc-macro2", 420 | "quote", 421 | "unicode-ident", 422 | ] 423 | 424 | [[package]] 425 | name = "term" 426 | version = "0.7.0" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" 429 | dependencies = [ 430 | "dirs-next", 431 | "rustversion", 432 | "winapi", 433 | ] 434 | 435 | [[package]] 436 | name = "thiserror" 437 | version = "1.0.57" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" 440 | dependencies = [ 441 | "thiserror-impl", 442 | ] 443 | 444 | [[package]] 445 | name = "thiserror-impl" 446 | version = "1.0.57" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" 449 | dependencies = [ 450 | "proc-macro2", 451 | "quote", 452 | "syn", 453 | ] 454 | 455 | [[package]] 456 | name = "tiny-keccak" 457 | version = "2.0.2" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" 460 | dependencies = [ 461 | "crunchy", 462 | ] 463 | 464 | [[package]] 465 | name = "tt" 466 | version = "0.1.0" 467 | dependencies = [ 468 | "lalrpop", 469 | "lalrpop-util", 470 | ] 471 | 472 | [[package]] 473 | name = "unicode-ident" 474 | version = "1.0.12" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 477 | 478 | [[package]] 479 | name = "unicode-xid" 480 | version = "0.2.0" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 483 | 484 | [[package]] 485 | name = "walkdir" 486 | version = "2.4.0" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" 489 | dependencies = [ 490 | "same-file", 491 | "winapi-util", 492 | ] 493 | 494 | [[package]] 495 | name = "wasi" 496 | version = "0.11.0+wasi-snapshot-preview1" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 499 | 500 | [[package]] 501 | name = "winapi" 502 | version = "0.3.8" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 505 | dependencies = [ 506 | "winapi-i686-pc-windows-gnu", 507 | "winapi-x86_64-pc-windows-gnu", 508 | ] 509 | 510 | [[package]] 511 | name = "winapi-i686-pc-windows-gnu" 512 | version = "0.4.0" 513 | source = "registry+https://github.com/rust-lang/crates.io-index" 514 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 515 | 516 | [[package]] 517 | name = "winapi-util" 518 | version = "0.1.6" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 521 | dependencies = [ 522 | "winapi", 523 | ] 524 | 525 | [[package]] 526 | name = "winapi-x86_64-pc-windows-gnu" 527 | version = "0.4.0" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 530 | 531 | [[package]] 532 | name = "windows-targets" 533 | version = "0.48.5" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 536 | dependencies = [ 537 | "windows_aarch64_gnullvm", 538 | "windows_aarch64_msvc", 539 | "windows_i686_gnu", 540 | "windows_i686_msvc", 541 | "windows_x86_64_gnu", 542 | "windows_x86_64_gnullvm", 543 | "windows_x86_64_msvc", 544 | ] 545 | 546 | [[package]] 547 | name = "windows_aarch64_gnullvm" 548 | version = "0.48.5" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 551 | 552 | [[package]] 553 | name = "windows_aarch64_msvc" 554 | version = "0.48.5" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 557 | 558 | [[package]] 559 | name = "windows_i686_gnu" 560 | version = "0.48.5" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 563 | 564 | [[package]] 565 | name = "windows_i686_msvc" 566 | version = "0.48.5" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 569 | 570 | [[package]] 571 | name = "windows_x86_64_gnu" 572 | version = "0.48.5" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 575 | 576 | [[package]] 577 | name = "windows_x86_64_gnullvm" 578 | version = "0.48.5" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 581 | 582 | [[package]] 583 | name = "windows_x86_64_msvc" 584 | version = "0.48.5" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 587 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tt" 3 | version = "0.1.0" 4 | authors = ["Jadon Fowler "] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | lalrpop = { version = "0.20.2", features = ["lexer"] } 9 | lalrpop-util = "0.20.2" 10 | 11 | [build-dependencies] 12 | lalrpop = "0.20.2" 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dtt experiments 2 | 3 | Experimenting with Dependent Type Theory in Rust. The goal here is to 4 | attempt to lower MLT Terms to an SSA IR. 5 | 6 | The core is extremely simple and as such a number of things need to 7 | be _encoded_. 8 | 9 | [Dependent Products](https://math.stackexchange.com/a/673003): 10 | 11 | ``` 12 | Σ : Π A : U (A → U) → U′ 13 | Σ = λA : U . λB : A → U . Π C : U (Π x : A B(x) → C) → C 14 | 15 | sigma = \A : U 16 | ``` 17 | 18 | Here are some examples: 19 | 20 | ``` 21 | N : Type 0 22 | Z : N 23 | S : forall _ : N -> N 24 | three = \f : (forall _ : N -> N) => \x : N => (f (f (f x))) 25 | check (three S) 26 | check (three (three S)) 27 | eval ((three (three S)) Z) 28 | ``` 29 | 30 | running this code will produce: 31 | 32 | ``` 33 | (three S) : N -> N 34 | (three (three S)) : N -> N 35 | ((three (three S)) Z) 36 | = (S (S (S (S (S (S (S (S (S Z))))))))) 37 | : N 38 | ``` 39 | 40 | ## Irrelevance 41 | 42 | Thought: If irrelevant terms are erased, we can extract functions that don't need dependent types for computation but 43 | still benefit from type checking. 44 | 45 | ```ocaml 46 | Vec T .n 47 | val Vec : Π(x : Type 0) -> (.Π(l: C) -> Type 0) 48 | 49 | val append : Vec T .n -> Vec T .m -> Vec T .(n + m) 50 | append: Π(T: Type0) -> ( 51 | .Π(n: Nat) -> ( 52 | .Π(m: Nat) -> ( 53 | Π(_:Vec T .n) -> ( 54 | Π(_:Vec T .m) -> ( 55 | Vec T .(+ n m) 56 | ) 57 | ) 58 | ) 59 | ) 60 | ) 61 | 62 | val erased_Vec : Π(x : Type 0) -> Type 0 63 | val erased_append : Vec T -> Vec T -> Vec T 64 | erased_append : Π(T: Type0) -> ( 65 | Π(_:Vec T) -> ( 66 | Π(_:Vec T) -> ( 67 | Vec T 68 | ) 69 | ) 70 | ) 71 | ``` 72 | 73 | ### Erasing Π Types 74 | 75 | ``` 76 | Π(x : Type 0) -> (.Π(l: C) -> Type 0) 77 | Π(x : Type 0) -> Type 0 78 | 79 | .Π(x:X) -> T ==> .T, erroring if .T depends on x 80 | ``` 81 | 82 | ### Erasing λ Expressions 83 | 84 | ``` 85 | .λ(x:X) => E ==> .E, erroring if .E depends on x 86 | ``` 87 | 88 | ### Erasing Applications 89 | 90 | ``` 91 | ((Vec T) .n) 92 | (Vec T) 93 | 94 | .(a b) ==> .a, erroring if .a requires a parameter 95 | ``` 96 | 97 | ## References 98 | 99 | * [How to implement dependent type theory I](http://math.andrej.com/2012/11/08/how-to-implement-dependent-type-theory-i/) 100 | * [Practical Erasure in Dependently Typed Languages](https://eb.host.cs.st-andrews.ac.uk/drafts/dtp-erasure-draft.pdf) 101 | * [From System F to Typed Assembly Language](https://www.cs.princeton.edu/~dpw/papers/tal-toplas.pdf) 102 | * [Compiling with Dependent Types](https://www.williamjbowman.com/resources/wjb-dissertation.pdf) 103 | * [The Implicit Calculus of Constructions as a Programming Language with Dependent Types](http://www.lix.polytechnique.fr/Labo/Bruno.Bernardo/writings/barras-bernardo-icc-fossacs08.pdf) 104 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate lalrpop; 2 | 3 | fn main() { 4 | lalrpop::process_root().unwrap(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate lalrpop; 2 | #[macro_use] 3 | extern crate lalrpop_util; 4 | 5 | use std::cmp::max; 6 | use std::collections::HashMap; 7 | use std::fmt::{Display, Formatter, Result as FmtResult}; 8 | use std::ops::Deref; 9 | use std::rc::Rc; 10 | 11 | use lalrpop_util::{ErrorRecovery, ParseError}; 12 | use lalrpop_util::lexer::Token; 13 | 14 | lalrpop_mod!(pub parser); 15 | 16 | pub enum Top { 17 | Erase(Rc), 18 | Check(Rc), 19 | Declaration(String, Rc), 20 | Definition(String, Rc), 21 | DefinitionCheck(String, Rc), 22 | Eval(Rc), 23 | } 24 | 25 | #[derive(Debug, Clone, Hash, Eq, PartialEq)] 26 | pub enum Expr { 27 | Var(Variable), 28 | Universe(u64), 29 | Pi(bool, Abstraction), 30 | Lambda(bool, Abstraction), 31 | App(bool, Rc, Rc), 32 | } 33 | 34 | impl Expr { 35 | pub fn erase(&self) -> Rc { 36 | match self { 37 | Expr::Var(v) => Rc::new(Expr::Var(v.clone())), 38 | Expr::Universe(u) => Rc::new(Expr::Universe(u.clone())), 39 | Expr::Pi(irr, (v, e1, e2)) => { 40 | if *irr { 41 | e2.erase() 42 | } else { 43 | Rc::new(Expr::Pi(false, (v.clone(), e1.erase(), e2.erase()))) 44 | } 45 | }, 46 | Expr::Lambda(irr, (v, e1, e2)) => { 47 | if *irr { 48 | e2.erase() 49 | } else { 50 | Rc::new(Expr::Lambda(false, (v.clone(), e1.erase(), e2.erase()))) 51 | } 52 | }, 53 | Expr::App(irr, e1, e2) => { 54 | if *irr { 55 | e1.erase() 56 | } else { 57 | Rc::new(Expr::App(false, e1.erase(), e2.erase())) 58 | } 59 | }, 60 | } 61 | } 62 | } 63 | 64 | impl Display for Expr { 65 | fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { 66 | match self { 67 | Expr::Var(x) => x.fmt(f), 68 | Expr::Universe(k) => write!(f, "Type {}", k), 69 | Expr::Pi(irr, (x, t, e)) => { 70 | let i = if *irr { "." } else { "" }; 71 | match e.deref() { 72 | Expr::Var(v) => { 73 | write!(f, "{}{} -> {}", i, t, v) 74 | } 75 | _ => write!(f, "{}pi ({} : {}) -> {}", i, x, t, e) 76 | } 77 | } 78 | Expr::Lambda(irr, (x, t, e)) => { 79 | let i = if *irr { "." } else { "" }; 80 | write!(f, "{}fun {} : {} => {}", i, x, t, e) 81 | }, 82 | Expr::App(irr, e1, e2) => { 83 | let i = if *irr { "." } else { "" }; 84 | write!(f, "{}({} {})", i, e1, e2) 85 | } 86 | } 87 | } 88 | } 89 | 90 | /// (x,t,e) x : t is bound in e 91 | pub type Abstraction = (Variable, Rc, Rc); 92 | 93 | #[derive(Debug, Clone, Hash, Eq, PartialEq)] 94 | pub enum Variable { 95 | String(String), 96 | Gensym(String, u64), 97 | Dummy, 98 | } 99 | 100 | impl Display for Variable { 101 | fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { 102 | match self { 103 | Variable::String(x) => write!(f, "{}", x), 104 | Variable::Gensym(x, i) => write!(f, "{}{}", x, i), 105 | Variable::Dummy => write!(f, "_"), 106 | } 107 | } 108 | } 109 | 110 | #[derive(Debug)] 111 | pub struct Context { 112 | /// maps variables to their types and optional definitions 113 | vars: HashMap, Option>)> 114 | } 115 | 116 | impl Context { 117 | pub fn new() -> Self { 118 | Self { vars: HashMap::new() } 119 | } 120 | 121 | pub fn lookup_type(&self, x: &Variable) -> Option> { 122 | match self.vars.get(x) { 123 | Some(v) => Some(v.0.clone()), 124 | None => None 125 | } 126 | } 127 | 128 | pub fn lookup_value(&self, x: &Variable) -> Option> { 129 | match self.vars.get(x) { 130 | Some(v) => v.1.clone(), 131 | None => None 132 | } 133 | } 134 | 135 | pub fn extend(&self, x: Variable, typ: Rc, def: Option>) -> Self { 136 | let mut new_map = self.vars.clone(); 137 | new_map.insert(x, (typ, def)); 138 | Self { 139 | vars: new_map, 140 | } 141 | } 142 | } 143 | 144 | pub struct TypeChecker { 145 | gensym: u64 146 | } 147 | 148 | impl TypeChecker { 149 | pub fn new() -> Self { 150 | Self { gensym: 0 } 151 | } 152 | 153 | /// generates a fresh variable name with the same form 154 | pub fn refresh(&mut self, variable: Variable) -> Variable { 155 | self.gensym += 1; 156 | match variable { 157 | Variable::String(x) | Variable::Gensym(x, _) => { 158 | Variable::Gensym(x, self.gensym) 159 | } 160 | Variable::Dummy => { 161 | Variable::Gensym("_".into(), self.gensym) 162 | } 163 | } 164 | } 165 | 166 | /// performs the given substitutions for the variables in the expression 167 | pub fn substitute(&mut self, substitutions: &HashMap>, e: Rc) -> Rc { 168 | match e.deref() { 169 | Expr::Var(x) => { 170 | match substitutions.get(&x) { 171 | Some(e) => e.clone(), 172 | None => e, 173 | } 174 | } 175 | Expr::Pi(irr, a) => Rc::new(Expr::Pi(*irr, self.substitute_abstraction(&substitutions, a.clone()))), 176 | Expr::Lambda(irr, a) => Rc::new(Expr::Lambda(*irr, self.substitute_abstraction(&substitutions, a.clone()))), 177 | Expr::App(irr, e1, e2) => Rc::new(Expr::App(*irr, self.substitute(substitutions, e1.clone()), self.substitute(substitutions, e2.clone()))), 178 | _ => e 179 | } 180 | } 181 | 182 | fn substitute_abstraction( 183 | &mut self, 184 | substitutions: &HashMap>, 185 | abstraction: Abstraction, 186 | ) -> Abstraction { 187 | let x = abstraction.0; 188 | let x1 = self.refresh(x.clone()); 189 | let t = self.substitute(substitutions, abstraction.1); 190 | let mut new_substitutions = substitutions.clone(); 191 | new_substitutions.insert(x, Rc::new(Expr::Var(x1.clone()))); 192 | let e = self.substitute(&new_substitutions, abstraction.2); 193 | (x1, t, e) 194 | } 195 | 196 | pub fn infer_type(&mut self, ctx: &mut Context, e: Rc) -> Rc { 197 | match e.deref() { 198 | Expr::Var(x) => { 199 | match ctx.lookup_type(x) { 200 | Some(t) => t, 201 | None => { 202 | println!("{:#?}", ctx); 203 | panic!("can't find variable: {:?}", x) 204 | } 205 | } 206 | } 207 | Expr::Universe(k) => Rc::new(Expr::Universe(k + 1)), 208 | Expr::Pi(_, (x, t1, t2)) => { 209 | let k1 = self.infer_universe(ctx, t1.clone()); 210 | let mut ctx = ctx.extend(x.clone(), t1.clone(), None); 211 | let k2 = self.infer_universe(&mut ctx, t2.clone()); 212 | Rc::new(Expr::Universe(max(k1, k2))) 213 | } 214 | Expr::Lambda(irr, (x, t, e)) => { 215 | let _ = self.infer_universe(ctx, t.clone()); 216 | let mut ctx = ctx.extend(x.clone(), t.clone(), None); 217 | let te = self.infer_type(&mut ctx, e.clone()); 218 | Rc::new(Expr::Pi(*irr, (x.clone(), t.clone(), te))) 219 | } 220 | Expr::App(_, e1, e2) => { 221 | // println!("{} {}", e1, e2); 222 | let (x, s, t) = self.infer_pi(ctx, e1.clone()); 223 | let te = self.infer_type(ctx, e2.clone()); 224 | // println!(" {} : PI ({}: **{}**) -> {} ==? {} : **{}**", e1, x, s, t, e2, te); 225 | self.check_equal(ctx, s, te); 226 | let mut new = HashMap::new(); 227 | new.insert(x, e2.clone()); 228 | self.substitute(&new, t) 229 | } 230 | } 231 | } 232 | 233 | /// infer the universe level of the type in the context 234 | fn infer_universe(&mut self, ctx: &mut Context, typ: Rc) -> u64 { 235 | let u = self.infer_type(ctx, typ); 236 | match self.normalize(ctx, u).deref() { 237 | Expr::Universe(k) => k.clone(), 238 | e => { panic!("found expr {} when universe was expected", e) } 239 | } 240 | } 241 | 242 | /// infer the type of the expression in the context, verifying that it is 243 | /// a Pi expression and returns the abstraction 244 | fn infer_pi(&mut self, ctx: &mut Context, e: Rc) -> Abstraction { 245 | let t = self.infer_type(ctx, e.clone()); 246 | match self.normalize(ctx, t).deref() { 247 | Expr::Pi(_, a) => a.clone(), 248 | e1 => { panic!("found expr {} ==> {} when pi was expected", e, e1) } 249 | } 250 | } 251 | 252 | fn check_equal(&mut self, ctx: &mut Context, e1: Rc, e2: Rc) { 253 | if !self.equal(ctx, e1.clone(), e2.clone()) { 254 | panic!("expressions {} and {} are not equal?", e1, e2) 255 | } 256 | } 257 | 258 | /// normalizes the expression in the context, removing all redexes and unfolding all definitions 259 | fn normalize(&mut self, ctx: &mut Context, e: Rc) -> Rc { 260 | match e.deref() { 261 | Expr::Var(x) => { 262 | match ctx.lookup_value(x) { 263 | Some(e) => self.normalize(ctx, e), 264 | None => { 265 | match ctx.lookup_type(x) { 266 | Some(_t) => e, 267 | None => { 268 | for (v, (t, e)) in ctx.vars.iter() { 269 | println!("{} : {} = {:#?}", v, t, e); 270 | } 271 | panic!("can't find variable: {:?}", x) 272 | } 273 | } 274 | } 275 | } 276 | } 277 | Expr::App(irr, e1, e2) => { 278 | let e2 = self.normalize(ctx, e2.clone()); 279 | let e1 = self.normalize(ctx, e1.clone()); 280 | match e1.deref() { 281 | Expr::Lambda(_, (x, _, e1)) => { 282 | let mut new = HashMap::new(); 283 | new.insert(x.clone(), e2.clone()); 284 | let e1 = self.substitute(&new, e1.clone()); 285 | self.normalize(ctx, e1) 286 | } 287 | _ => Rc::new(Expr::App(*irr, e1, e2)) 288 | } 289 | } 290 | Expr::Pi(irr, a) => Rc::new(Expr::Pi(*irr, self.normalize_abstraction(ctx, a))), 291 | Expr::Lambda(irr, a) => Rc::new(Expr::Lambda(*irr, self.normalize_abstraction(ctx, a))), 292 | _ => e 293 | } 294 | } 295 | 296 | fn normalize_abstraction(&mut self, ctx: &mut Context, abstraction: &Abstraction) -> Abstraction { 297 | let (x, t, e) = abstraction; 298 | let t = self.normalize(ctx, t.clone()); 299 | let mut ctx = ctx.extend(x.clone(), t.clone(), None); 300 | (x.clone(), t, self.normalize(&mut ctx, e.clone())) 301 | } 302 | 303 | fn equal(&mut self, ctx: &mut Context, e1: Rc, e2: Rc) -> bool { 304 | let e1 = self.normalize(ctx, e1); 305 | let e2 = self.normalize(ctx, e2); 306 | match (e1.deref(), e2.deref()) { 307 | (Expr::Var(x1), Expr::Var(x2)) => x1 == x2, 308 | (Expr::App(irr1, e11, e12), Expr::App(irr2, e21, e22)) => 309 | self.equal(ctx, e11.clone(), e21.clone()) && 310 | self.equal(ctx, e12.clone(), e22.clone()) && 311 | *irr1 == *irr2, 312 | (Expr::Universe(k1), Expr::Universe(k2)) => k1 == k2, 313 | (Expr::Pi(irr1, a1), Expr::Pi(irr2, a2)) => self.equal_abstraction(ctx, a1, a2) && *irr1 == *irr2, 314 | (Expr::Lambda(irr1, a1), Expr::Lambda(irr2, a2)) => self.equal_abstraction(ctx, a1, a2) && *irr1 == *irr2, 315 | _ => false 316 | } 317 | } 318 | 319 | fn equal_abstraction(&mut self, ctx: &mut Context, a1: &Abstraction, a2: &Abstraction) -> bool { 320 | let (x, t1, e1) = a1; 321 | let (y, t2, e2) = a2; 322 | let mut new = HashMap::new(); 323 | new.insert(y.clone(), Rc::new(Expr::Var(x.clone()))); 324 | 325 | let e2 = self.substitute(&new, e2.clone()); 326 | self.equal(ctx, t1.clone(), t2.clone()) && (self.equal(ctx, e1.clone(), e2)) 327 | } 328 | } 329 | 330 | fn main() { 331 | let mut ctx = Context::new(); 332 | let mut checker = TypeChecker::new(); 333 | let args = std::env::args().collect::>(); 334 | let Some(file_name) = args.get(1) else { panic!("no file name") }; 335 | let code = std::fs::read_to_string(file_name).unwrap(); 336 | let mut errors: Vec> = Vec::new(); 337 | let result: Result, ParseError> = parser::ProgramParser::new().parse(&mut errors, &code); 338 | match result { 339 | Ok(tops) => { 340 | for top in tops { 341 | match top { 342 | Top::Check(def) => { 343 | let typ = checker.infer_type(&mut ctx, def.clone()); 344 | println!("{} : {}", def, typ); 345 | } 346 | Top::Erase(def) => { 347 | let typ = checker.infer_type(&mut ctx, def.clone()).erase(); 348 | println!("erased {} : {}\n", def, typ); 349 | } 350 | Top::Declaration(name, typ) => { 351 | println!("{} : {}", name, typ); 352 | ctx = ctx.extend(Variable::String(name), typ, None); 353 | } 354 | Top::Definition(name, def) => { 355 | let typ = checker.infer_type(&mut ctx, def.clone()); 356 | println!("{} = {}", name, def.clone()); 357 | println!("{}: {}\n", " ".repeat(name.len() + 1), typ.clone()); 358 | ctx = ctx.extend(Variable::String(name), typ, Some(def)); 359 | } 360 | Top::DefinitionCheck(name, def) => { 361 | let typ = checker.infer_type(&mut ctx, def.clone()); 362 | let typ_of_typ = checker.infer_type(&mut ctx, typ.clone()); 363 | println!("{} : {}", name, typ.clone()); 364 | println!("{}: {}\n", " ".repeat(name.len() + 1), typ_of_typ.clone()); 365 | ctx = ctx.extend(Variable::String(name), typ_of_typ, Some(typ)); 366 | } 367 | Top::Eval(e) => { 368 | let e1 = checker.normalize(&mut ctx, e.clone()); 369 | let t = checker.infer_type(&mut ctx, e1.clone()); 370 | println!("{}\n = {}\n : {}", e, e1, t); 371 | } 372 | } 373 | } 374 | } 375 | Err(e) => eprintln!("{}", e.to_string()) 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /src/parser.lalrpop: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | use lalrpop_util::ErrorRecovery; 3 | use crate::*; 4 | 5 | grammar<'err>(errors: &'err mut Vec, &'static str>>); 6 | 7 | pub Program: Vec = { 8 | => <>, 9 | } 10 | 11 | Top: Top = { 12 | "check" => Top::Check(e), 13 | "erase" => Top::Erase(e), 14 | "eval" => Top::Eval(e), 15 | "let" ":" => Top::Declaration(n, e), 16 | "let" "=" "check" => Top::DefinitionCheck(n, e), 17 | "let" "=" => Top::Definition(n, e), 18 | }; 19 | 20 | Expr: Rc = { 21 | "Type" => Rc::new(Expr::Universe(<>)), 22 | // pi 23 | "pi" ":" "->" => 24 | Rc::new(Expr::Pi(irr.is_some(), (Variable::String(n), t, e))), 25 | // lambda 26 | "fun" ":" "=>" => 27 | Rc::new(Expr::Lambda(irr.is_some(), (Variable::String(n), t, e))), 28 | // app 29 | "(" ")" => 30 | Rc::new(Expr::App(irr.is_some(), e1, e2)), 31 | "(" ")" => <>, 32 | => Rc::new(Expr::Var(Variable::String(<>))), 33 | } 34 | 35 | Num: u64 = { 36 | r"[0-9]+" => u64::from_str(<>).unwrap() 37 | }; 38 | 39 | Name: String = { 40 | r"[a-zA-Z_][a-zA-Z0-9_]*" => <>.to_string() 41 | }; 42 | -------------------------------------------------------------------------------- /src/test.ml: -------------------------------------------------------------------------------- 1 | let Ty: Type 1 2 | let N: Type 0 3 | let B: Type 0 4 | let Z: N 5 | let S: pi _: N -> N 6 | let tZ = N 7 | let tS = pi _: N -> N 8 | let C = pi s: tS -> (pi _: tZ -> N) 9 | let zero = fun s: tS => fun z: tZ => z 10 | let one = fun s: tS => fun z: tZ => (s z) 11 | let two = fun s: tS => fun z: tZ => (s (s z)) 12 | let three = fun s: tS => fun z: tZ => (s (s (s z))) 13 | let four = fun s: tS => fun z: tZ => (s ((three s) z)) 14 | let add = fun m: C => fun n: C => fun s: tS => fun z: tZ => ((m s) ((n s) z)) 15 | 16 | check (three S) 17 | check (three (three S)) 18 | eval ((three (three S)) Z) 19 | let tthree = check three 20 | 21 | check (S (S Z)) 22 | 23 | let Vec: 24 | pi x: Type 0 -> 25 | (.pi l: C -> Type 0) 26 | let append: 27 | pi T: Type 0 -> (.pi n: C -> (.pi m: C -> 28 | (pi _: .((Vec T) n) -> (pi _: .((Vec T) m) -> 29 | .((Vec T) ((add m) n) ))))) 30 | let tappend = check append 31 | let VecN2: .((Vec B) one) 32 | let tVecN2 = check VecN2 33 | 34 | erase Vec 35 | erase append 36 | 37 | let BU = check fun f : Type 0 => B 38 | --------------------------------------------------------------------------------