├── .gitattributes ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE2.0 ├── LICENSE-MIT ├── README.md ├── ast ├── Cargo.toml └── src │ ├── lib.rs │ └── visit.rs ├── ast_lowering ├── Cargo.toml └── src │ ├── lib.rs │ └── visitors.rs ├── hir ├── Cargo.toml └── src │ ├── lib.rs │ ├── ty.rs │ └── visit.rs ├── parser ├── Cargo.toml └── src │ ├── lexer.rs │ └── lib.rs ├── repl ├── Cargo.toml └── src │ ├── hir_engine │ ├── expr.rs │ ├── mod.rs │ └── symbol_table.rs │ ├── main.rs │ └── repl.rs ├── rustfmt.toml ├── strcache ├── Cargo.toml └── src │ └── lib.rs ├── test.bm └── typecheck ├── Cargo.toml └── src └── lib.rs /.gitattributes: -------------------------------------------------------------------------------- 1 | *.bm linguist-language=Rust 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | /rls 4 | repl_history.bismite -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "ansi_term" 5 | version = "0.11.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 8 | dependencies = [ 9 | "winapi", 10 | ] 11 | 12 | [[package]] 13 | name = "arrayref" 14 | version = "0.3.6" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" 17 | 18 | [[package]] 19 | name = "arrayvec" 20 | version = "0.5.1" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" 23 | 24 | [[package]] 25 | name = "ast" 26 | version = "0.1.0" 27 | dependencies = [ 28 | "codespan", 29 | ] 30 | 31 | [[package]] 32 | name = "ast_lowering" 33 | version = "0.1.0" 34 | dependencies = [ 35 | "ast", 36 | "hir", 37 | "parser", 38 | ] 39 | 40 | [[package]] 41 | name = "atty" 42 | version = "0.2.14" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 45 | dependencies = [ 46 | "hermit-abi", 47 | "libc", 48 | "winapi", 49 | ] 50 | 51 | [[package]] 52 | name = "autocfg" 53 | version = "1.0.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 56 | 57 | [[package]] 58 | name = "base64" 59 | version = "0.11.0" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" 62 | 63 | [[package]] 64 | name = "beef" 65 | version = "0.4.2" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "8d6c5d4c6a76bf19bdde42a6fdb271931921175a40a7078924bb6586219c6e7f" 68 | 69 | [[package]] 70 | name = "bitflags" 71 | version = "1.2.1" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 74 | 75 | [[package]] 76 | name = "blake2b_simd" 77 | version = "0.5.10" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" 80 | dependencies = [ 81 | "arrayref", 82 | "arrayvec", 83 | "constant_time_eq", 84 | ] 85 | 86 | [[package]] 87 | name = "cc" 88 | version = "1.0.50" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" 91 | 92 | [[package]] 93 | name = "cfg-if" 94 | version = "0.1.10" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 97 | 98 | [[package]] 99 | name = "clap" 100 | version = "2.33.1" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" 103 | dependencies = [ 104 | "ansi_term", 105 | "atty", 106 | "bitflags", 107 | "strsim", 108 | "textwrap", 109 | "unicode-width", 110 | "vec_map", 111 | ] 112 | 113 | [[package]] 114 | name = "codespan" 115 | version = "0.9.5" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "8ebaf6bb6a863ad6aa3a18729e9710c53d75df03306714d9cc1f7357a00cd789" 118 | dependencies = [ 119 | "codespan-reporting", 120 | ] 121 | 122 | [[package]] 123 | name = "codespan-reporting" 124 | version = "0.9.5" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "6e0762455306b1ed42bc651ef6a2197aabda5e1d4a43c34d5eab5c1a3634e81d" 127 | dependencies = [ 128 | "termcolor", 129 | "unicode-width", 130 | ] 131 | 132 | [[package]] 133 | name = "constant_time_eq" 134 | version = "0.1.5" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 137 | 138 | [[package]] 139 | name = "crossbeam-utils" 140 | version = "0.7.2" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" 143 | dependencies = [ 144 | "autocfg", 145 | "cfg-if", 146 | "lazy_static", 147 | ] 148 | 149 | [[package]] 150 | name = "dirs" 151 | version = "2.0.2" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" 154 | dependencies = [ 155 | "cfg-if", 156 | "dirs-sys", 157 | ] 158 | 159 | [[package]] 160 | name = "dirs-sys" 161 | version = "0.3.4" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" 164 | dependencies = [ 165 | "cfg-if", 166 | "libc", 167 | "redox_users", 168 | "winapi", 169 | ] 170 | 171 | [[package]] 172 | name = "fnv" 173 | version = "1.0.6" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" 176 | 177 | [[package]] 178 | name = "getrandom" 179 | version = "0.1.14" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 182 | dependencies = [ 183 | "cfg-if", 184 | "libc", 185 | "wasi", 186 | ] 187 | 188 | [[package]] 189 | name = "heck" 190 | version = "0.3.1" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" 193 | dependencies = [ 194 | "unicode-segmentation", 195 | ] 196 | 197 | [[package]] 198 | name = "hermit-abi" 199 | version = "0.1.13" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" 202 | dependencies = [ 203 | "libc", 204 | ] 205 | 206 | [[package]] 207 | name = "hir" 208 | version = "0.1.0" 209 | dependencies = [ 210 | "ast", 211 | "codespan", 212 | "string-interner", 213 | ] 214 | 215 | [[package]] 216 | name = "lazy_static" 217 | version = "1.4.0" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 220 | 221 | [[package]] 222 | name = "libc" 223 | version = "0.2.69" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" 226 | 227 | [[package]] 228 | name = "log" 229 | version = "0.4.8" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 232 | dependencies = [ 233 | "cfg-if", 234 | ] 235 | 236 | [[package]] 237 | name = "logos" 238 | version = "0.11.4" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "b91c49573597a5d6c094f9031617bb1fed15c0db68c81e6546d313414ce107e4" 241 | dependencies = [ 242 | "logos-derive", 243 | ] 244 | 245 | [[package]] 246 | name = "logos-derive" 247 | version = "0.11.5" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "797b1f8a0571b331c1b47e7db245af3dc634838da7a92b3bef4e30376ae1c347" 250 | dependencies = [ 251 | "beef", 252 | "fnv", 253 | "proc-macro2", 254 | "quote", 255 | "regex-syntax", 256 | "syn", 257 | "utf8-ranges", 258 | ] 259 | 260 | [[package]] 261 | name = "memchr" 262 | version = "2.3.3" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 265 | 266 | [[package]] 267 | name = "nix" 268 | version = "0.17.0" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" 271 | dependencies = [ 272 | "bitflags", 273 | "cc", 274 | "cfg-if", 275 | "libc", 276 | "void", 277 | ] 278 | 279 | [[package]] 280 | name = "parser" 281 | version = "0.1.0" 282 | dependencies = [ 283 | "ast", 284 | "codespan", 285 | "logos", 286 | ] 287 | 288 | [[package]] 289 | name = "proc-macro-error" 290 | version = "1.0.2" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "98e9e4b82e0ef281812565ea4751049f1bdcdfccda7d3f459f2e138a40c08678" 293 | dependencies = [ 294 | "proc-macro-error-attr", 295 | "proc-macro2", 296 | "quote", 297 | "syn", 298 | "version_check", 299 | ] 300 | 301 | [[package]] 302 | name = "proc-macro-error-attr" 303 | version = "1.0.2" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "4f5444ead4e9935abd7f27dc51f7e852a0569ac888096d5ec2499470794e2e53" 306 | dependencies = [ 307 | "proc-macro2", 308 | "quote", 309 | "syn", 310 | "syn-mid", 311 | "version_check", 312 | ] 313 | 314 | [[package]] 315 | name = "proc-macro2" 316 | version = "1.0.10" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" 319 | dependencies = [ 320 | "unicode-xid", 321 | ] 322 | 323 | [[package]] 324 | name = "quote" 325 | version = "1.0.3" 326 | source = "registry+https://github.com/rust-lang/crates.io-index" 327 | checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" 328 | dependencies = [ 329 | "proc-macro2", 330 | ] 331 | 332 | [[package]] 333 | name = "redox_syscall" 334 | version = "0.1.56" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 337 | 338 | [[package]] 339 | name = "redox_users" 340 | version = "0.3.4" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" 343 | dependencies = [ 344 | "getrandom", 345 | "redox_syscall", 346 | "rust-argon2", 347 | ] 348 | 349 | [[package]] 350 | name = "regex-syntax" 351 | version = "0.6.17" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" 354 | 355 | [[package]] 356 | name = "repl" 357 | version = "0.1.0" 358 | dependencies = [ 359 | "ast", 360 | "codespan", 361 | "codespan-reporting", 362 | "hir", 363 | "parser", 364 | "rustyline", 365 | "structopt", 366 | "typecheck", 367 | ] 368 | 369 | [[package]] 370 | name = "rust-argon2" 371 | version = "0.7.0" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" 374 | dependencies = [ 375 | "base64", 376 | "blake2b_simd", 377 | "constant_time_eq", 378 | "crossbeam-utils", 379 | ] 380 | 381 | [[package]] 382 | name = "rustyline" 383 | version = "6.1.2" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "1cd20b28d972040c627e209eb29f19c24a71a19d661cc5a220089176e20ee202" 386 | dependencies = [ 387 | "cfg-if", 388 | "dirs", 389 | "libc", 390 | "log", 391 | "memchr", 392 | "nix", 393 | "scopeguard", 394 | "unicode-segmentation", 395 | "unicode-width", 396 | "utf8parse", 397 | "winapi", 398 | ] 399 | 400 | [[package]] 401 | name = "scopeguard" 402 | version = "1.1.0" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 405 | 406 | [[package]] 407 | name = "serde" 408 | version = "1.0.106" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" 411 | 412 | [[package]] 413 | name = "strcache" 414 | version = "0.1.0" 415 | 416 | [[package]] 417 | name = "string-interner" 418 | version = "0.7.1" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "fd710eadff449a1531351b0e43eb81ea404336fa2f56c777427ab0e32a4cf183" 421 | dependencies = [ 422 | "serde", 423 | ] 424 | 425 | [[package]] 426 | name = "strsim" 427 | version = "0.8.0" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 430 | 431 | [[package]] 432 | name = "structopt" 433 | version = "0.3.14" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "863246aaf5ddd0d6928dfeb1a9ca65f505599e4e1b399935ef7e75107516b4ef" 436 | dependencies = [ 437 | "clap", 438 | "lazy_static", 439 | "structopt-derive", 440 | ] 441 | 442 | [[package]] 443 | name = "structopt-derive" 444 | version = "0.4.7" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "d239ca4b13aee7a2142e6795cbd69e457665ff8037aed33b3effdc430d2f927a" 447 | dependencies = [ 448 | "heck", 449 | "proc-macro-error", 450 | "proc-macro2", 451 | "quote", 452 | "syn", 453 | ] 454 | 455 | [[package]] 456 | name = "syn" 457 | version = "1.0.17" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" 460 | dependencies = [ 461 | "proc-macro2", 462 | "quote", 463 | "unicode-xid", 464 | ] 465 | 466 | [[package]] 467 | name = "syn-mid" 468 | version = "0.5.0" 469 | source = "registry+https://github.com/rust-lang/crates.io-index" 470 | checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" 471 | dependencies = [ 472 | "proc-macro2", 473 | "quote", 474 | "syn", 475 | ] 476 | 477 | [[package]] 478 | name = "termcolor" 479 | version = "1.1.0" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" 482 | dependencies = [ 483 | "winapi-util", 484 | ] 485 | 486 | [[package]] 487 | name = "textwrap" 488 | version = "0.11.0" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 491 | dependencies = [ 492 | "unicode-width", 493 | ] 494 | 495 | [[package]] 496 | name = "typecheck" 497 | version = "0.1.0" 498 | dependencies = [ 499 | "hir", 500 | ] 501 | 502 | [[package]] 503 | name = "unicode-segmentation" 504 | version = "1.6.0" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" 507 | 508 | [[package]] 509 | name = "unicode-width" 510 | version = "0.1.7" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" 513 | 514 | [[package]] 515 | name = "unicode-xid" 516 | version = "0.2.0" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 519 | 520 | [[package]] 521 | name = "utf8-ranges" 522 | version = "1.0.4" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" 525 | 526 | [[package]] 527 | name = "utf8parse" 528 | version = "0.2.0" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" 531 | 532 | [[package]] 533 | name = "vec_map" 534 | version = "0.8.2" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 537 | 538 | [[package]] 539 | name = "version_check" 540 | version = "0.9.2" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 543 | 544 | [[package]] 545 | name = "void" 546 | version = "1.0.2" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 549 | 550 | [[package]] 551 | name = "wasi" 552 | version = "0.9.0+wasi-snapshot-preview1" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 555 | 556 | [[package]] 557 | name = "winapi" 558 | version = "0.3.8" 559 | source = "registry+https://github.com/rust-lang/crates.io-index" 560 | checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 561 | dependencies = [ 562 | "winapi-i686-pc-windows-gnu", 563 | "winapi-x86_64-pc-windows-gnu", 564 | ] 565 | 566 | [[package]] 567 | name = "winapi-i686-pc-windows-gnu" 568 | version = "0.4.0" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 571 | 572 | [[package]] 573 | name = "winapi-util" 574 | version = "0.1.4" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" 577 | dependencies = [ 578 | "winapi", 579 | ] 580 | 581 | [[package]] 582 | name = "winapi-x86_64-pc-windows-gnu" 583 | version = "0.4.0" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 586 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "ast", 4 | "ast_lowering", 5 | "hir", 6 | "parser", 7 | "repl", 8 | "strcache", 9 | "typecheck", 10 | ] -------------------------------------------------------------------------------- /LICENSE-APACHE2.0: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2018 Wesley Norris 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright © 2018 Wesley Norris 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bismite 2 | 3 | `bismite` is a Rust-like toy programming language compiler in very early development. Changes may be added often or not often. More information will be added as the language is fleshed out. 4 | 5 | ## License 6 | `bismite` is licensed under your choice of `Apache 2.0` or `MIT`. -------------------------------------------------------------------------------- /ast/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ast" 3 | version = "0.1.0" 4 | authors = ["Wesley Norris "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | codespan = "0.9.2" -------------------------------------------------------------------------------- /ast/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod visit; 2 | 3 | use codespan::Span; 4 | pub use visit::Visitor; 5 | 6 | #[derive(Clone, Debug)] 7 | pub struct Geode { 8 | pub module: Module, 9 | pub span: Span, 10 | } 11 | 12 | #[derive(Clone, Debug)] 13 | pub struct Module { 14 | pub name: Identifier, 15 | pub items: Vec, 16 | pub span: Span, 17 | } 18 | 19 | #[derive(Clone, Debug)] 20 | pub enum AstNode { 21 | Statement(Statement), 22 | Expression(Expression), 23 | Item(Item), 24 | } 25 | 26 | #[derive(Clone, Debug)] 27 | pub enum Item { 28 | Function(Function), 29 | Struct(Struct), 30 | Module(Module), 31 | Use(Use), 32 | } 33 | 34 | impl Item { 35 | pub fn span(&self) -> Span { 36 | match self { 37 | Item::Function(f) => f.span, 38 | Item::Struct(s) => s.span, 39 | Item::Module(m) => m.span, 40 | Item::Use(u) => u.span, 41 | } 42 | } 43 | } 44 | 45 | #[derive(Clone, Debug)] 46 | pub struct Function { 47 | pub name: Identifier, 48 | pub parameters: Vec, 49 | pub return_ty: Option, 50 | pub body: Block, 51 | pub span: Span, 52 | } 53 | 54 | #[derive(Clone, Debug)] 55 | pub struct FunctionParameter { 56 | pub name: Identifier, 57 | pub ty: Type, 58 | pub span: Span, 59 | } 60 | 61 | #[derive(Clone, Debug)] 62 | pub struct Block { 63 | pub uses: Vec, 64 | pub statements: Vec, 65 | pub return_expr: Option, 66 | pub span: Span, 67 | } 68 | 69 | #[derive(Clone, Debug)] 70 | pub struct Struct { 71 | pub name: Identifier, 72 | pub members: Vec, 73 | pub span: Span, 74 | } 75 | 76 | #[derive(Clone, Debug)] 77 | pub struct StructMember { 78 | pub name: Identifier, 79 | pub ty: Type, 80 | pub span: Span, 81 | } 82 | 83 | #[derive(Clone, Debug)] 84 | pub struct Use { 85 | pub path: Path, 86 | pub span: Span, 87 | } 88 | 89 | #[derive(Clone, Debug)] 90 | pub struct Statement { 91 | pub kind: StatementKind, 92 | pub span: Span, 93 | } 94 | 95 | #[derive(Clone, Debug)] 96 | pub enum StatementKind { 97 | VariableBinding(VariableBinding), 98 | Expression(Expression), 99 | } 100 | 101 | #[derive(Clone, Debug)] 102 | pub struct VariableBinding { 103 | pub mutable: bool, 104 | pub name: Identifier, 105 | pub ty: Option, 106 | pub value: Expression, 107 | pub span: Span, 108 | } 109 | 110 | #[derive(Debug, Clone)] 111 | pub struct Expression { 112 | pub kind: ExpressionKind, 113 | pub span: Span, 114 | } 115 | 116 | #[derive(Debug, Clone)] 117 | pub enum ExpressionKind { 118 | Assignment(Box, Box), 119 | BinaryOperation(Box, BinOp, Box), 120 | Block(Box), 121 | Boolean(bool), 122 | FieldAccess(Box, Identifier), 123 | FnCall(Box, Vec), 124 | If(Box), 125 | Integer(i128), 126 | Path(Path), 127 | Struct(Box), 128 | Unary(UnaryOp, Box), 129 | Unit, 130 | } 131 | 132 | #[derive(Debug, Clone)] 133 | pub struct IfExpr { 134 | pub ifs: Vec, 135 | pub r#else: Option, 136 | pub span: Span, 137 | } 138 | 139 | #[derive(Debug, Clone)] 140 | pub struct If { 141 | pub condition: Expression, 142 | pub body: Block, 143 | pub span: Span, 144 | } 145 | 146 | #[derive(Debug, Clone)] 147 | pub struct StructExpr { 148 | pub name: Path, 149 | pub members: Vec, 150 | pub span: Span, 151 | } 152 | 153 | #[derive(Debug, Clone)] 154 | pub struct StructExprMember { 155 | pub name: Identifier, 156 | pub expression: Expression, 157 | pub span: Span, 158 | } 159 | 160 | #[derive(Debug, Clone)] 161 | pub struct Identifier { 162 | pub value: String, 163 | pub span: Span, 164 | } 165 | 166 | impl Identifier { 167 | pub fn dummy() -> Self { 168 | Self { value: String::new(), span: Span::new(0, 0) } 169 | } 170 | } 171 | 172 | #[derive(Clone, Copy, Debug, PartialEq)] 173 | pub enum BinOp { 174 | Add, 175 | Subtract, 176 | Multiply, 177 | Divide, 178 | LogicalAnd, 179 | Equal, 180 | } 181 | 182 | impl BinOp { 183 | pub fn is_comparison_op(self) -> bool { 184 | match self { 185 | BinOp::Equal => true, 186 | _ => false, 187 | } 188 | } 189 | 190 | pub fn is_arith_op(self) -> bool { 191 | match self { 192 | BinOp::Add | BinOp::Subtract | BinOp::Multiply | BinOp::Divide => true, 193 | _ => false, 194 | } 195 | } 196 | 197 | pub fn is_logic_op(self) -> bool { 198 | match self { 199 | BinOp::LogicalAnd => true, 200 | _ => false, 201 | } 202 | } 203 | } 204 | 205 | impl std::fmt::Display for BinOp { 206 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 207 | match self { 208 | BinOp::Add => write!(f, "+"), 209 | BinOp::Subtract => write!(f, "-"), 210 | BinOp::Multiply => write!(f, "*"), 211 | BinOp::Divide => write!(f, "/"), 212 | BinOp::LogicalAnd => write!(f, "&&"), 213 | BinOp::Equal => write!(f, "=="), 214 | } 215 | } 216 | } 217 | 218 | #[derive(Debug, Clone, Copy)] 219 | pub enum UnaryOp { 220 | Minus, 221 | } 222 | 223 | impl std::fmt::Display for UnaryOp { 224 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 225 | match self { 226 | UnaryOp::Minus => write!(f, "-"), 227 | } 228 | } 229 | } 230 | 231 | #[derive(Clone, Debug)] 232 | pub struct Type { 233 | pub kind: TypeKind, 234 | pub span: Span, 235 | } 236 | 237 | #[derive(Clone, Debug)] 238 | pub enum TypeKind { 239 | Bool, 240 | Integer, 241 | Named(Path), 242 | } 243 | 244 | #[derive(Clone, Debug)] 245 | pub struct Path { 246 | pub segments: Vec, 247 | pub span: Span, 248 | } 249 | -------------------------------------------------------------------------------- /ast/src/visit.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | pub trait Visitor: Sized { 4 | fn visit_module(&mut self, module: &Module) { 5 | walk::module(self, module); 6 | } 7 | 8 | fn visit_item(&mut self, item: &Item) { 9 | walk::item(self, item); 10 | } 11 | 12 | fn visit_function(&mut self, function: &Function) { 13 | walk::function(self, function); 14 | } 15 | 16 | fn visit_identifier(&mut self, _: &Identifier) { 17 | // Nothing else to do 18 | } 19 | 20 | fn visit_struct(&mut self, strukt: &Struct) { 21 | walk::structure(self, strukt); 22 | } 23 | 24 | fn visit_struct_member(&mut self, struct_member: &StructMember) { 25 | walk::struct_member(self, struct_member); 26 | } 27 | 28 | fn visit_path(&mut self, path: &Path) { 29 | walk::path(self, path); 30 | } 31 | 32 | fn visit_type(&mut self, ty: &Type) { 33 | walk::ty(self, ty); 34 | } 35 | 36 | fn visit_function_parameter(&mut self, _: &FunctionParameter) { 37 | todo!() 38 | } 39 | 40 | fn visit_block(&mut self, _: &Block) { 41 | todo!() 42 | } 43 | 44 | fn visit_node(&mut self, node: &AstNode) { 45 | walk::node(self, node) 46 | } 47 | 48 | fn visit_statement(&mut self, _: &Statement) { 49 | todo!() 50 | } 51 | 52 | fn visit_expression(&mut self, _: &Expression) { 53 | todo!() 54 | } 55 | 56 | fn visit_use(&mut self, _: &Use) { 57 | todo!() 58 | } 59 | } 60 | 61 | pub mod walk { 62 | use super::*; 63 | 64 | #[doc(hidden)] 65 | #[macro_export] 66 | macro_rules! _list { 67 | ($v:ident, $v_method:ident, $list:expr) => { 68 | for item in $list { 69 | $v.$v_method(item); 70 | } 71 | }; 72 | } 73 | 74 | pub use _list as list; 75 | 76 | pub fn module(visitor: &mut V, module: &Module) { 77 | list!(visitor, visit_item, &module.items); 78 | } 79 | 80 | pub fn item(visitor: &mut V, item: &Item) { 81 | match item { 82 | Item::Function(f) => visitor.visit_function(f), 83 | Item::Module(m) => visitor.visit_module(m), 84 | Item::Struct(s) => visitor.visit_struct(s), 85 | Item::Use(u) => visitor.visit_use(u), 86 | } 87 | } 88 | 89 | pub fn node(visitor: &mut V, node: &AstNode) { 90 | match node { 91 | AstNode::Expression(e) => visitor.visit_expression(e), 92 | AstNode::Item(i) => visitor.visit_item(i), 93 | AstNode::Statement(s) => visitor.visit_statement(s), 94 | } 95 | } 96 | 97 | pub fn structure(visitor: &mut V, strukt: &Struct) { 98 | list!(visitor, visit_struct_member, &strukt.members); 99 | } 100 | 101 | pub fn struct_member(visitor: &mut V, struct_member: &StructMember) { 102 | visitor.visit_identifier(&struct_member.name); 103 | visitor.visit_type(&struct_member.ty); 104 | } 105 | 106 | pub fn path(visitor: &mut V, path: &Path) { 107 | list!(visitor, visit_identifier, &path.segments); 108 | } 109 | 110 | pub fn ty(visitor: &mut V, ty: &Type) { 111 | match &ty.kind { 112 | TypeKind::Integer | TypeKind::Bool => todo!("hmm"), 113 | TypeKind::Named(path) => visitor.visit_path(path), 114 | } 115 | } 116 | 117 | pub fn function(visitor: &mut V, function: &Function) { 118 | list!(visitor, visit_function_parameter, &function.parameters); 119 | if let Some(ty) = &function.return_ty { 120 | visitor.visit_type(ty); 121 | } 122 | visitor.visit_block(&function.body); 123 | } 124 | 125 | pub fn usage(visitor: &mut V, usage: &Use) { 126 | visitor.visit_path(&usage.path); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /ast_lowering/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ast_lowering" 3 | version = "0.1.0" 4 | authors = ["Wesley Norris "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | ast = { path = "../ast" } 11 | hir = { path = "../hir" } 12 | 13 | [dev-dependencies] 14 | parser = { path = "../parser" } -------------------------------------------------------------------------------- /ast_lowering/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod visitors; 2 | 3 | use ast::Visitor; 4 | use hir::{Block, Expression, ExpressionKind, Function, Identifier, Item, ItemKind, Module, Path}; 5 | use std::collections::{HashMap, HashSet}; 6 | use visitors::UseCollector; 7 | 8 | #[derive(Debug, Default, Clone)] 9 | pub struct LoweringContext { 10 | current_path: Path, 11 | scopes: Vec, 12 | scope_storage: Vec>, 13 | } 14 | 15 | impl LoweringContext { 16 | pub fn lower_module(&mut self, module: &ast::Module) -> Module { 17 | self.with_scope(Scope::default(), |this| { 18 | let mut hir_module = Module::new(Identifier::convert(&module.name), Vec::new(), module.span); 19 | 20 | let mut collector = UseCollector::new(this); 21 | collector.visit_module(module); 22 | 23 | for item in &module.items { 24 | if let ast::Item::Use(_) = item { 25 | continue; 26 | } 27 | 28 | hir_module.items.push(this.lower_item(item)); 29 | } 30 | 31 | hir_module 32 | }) 33 | } 34 | 35 | pub fn lower_item(&mut self, item: &ast::Item) -> Item { 36 | match item { 37 | ast::Item::Function(f) => Item { kind: ItemKind::Function(self.lower_function(f)), span: f.span }, 38 | ast::Item::Module(m) => self.with_no_parents(|this| { 39 | let items = m.items.iter().map(|item| this.lower_item(item)).collect(); 40 | Item { kind: ItemKind::Module(Module::new(Identifier::convert(&m.name), items, m.span)), span: m.span } 41 | }), 42 | ast::Item::Struct(s) => todo!("lower struct"), 43 | ast::Item::Use(_) => panic!("attempting to lower a `use`"), 44 | } 45 | } 46 | 47 | pub fn lower_function(&mut self, function: &ast::Function) -> Function { 48 | Function { 49 | name: Identifier::convert(&function.name), 50 | parameters: vec![], 51 | body: self.lower_block(&function.body), 52 | return_type: hir::Type { kind: hir::TypeKind::Unit, span: function.span }, 53 | } 54 | } 55 | 56 | pub fn lower_block(&mut self, block: &ast::Block) -> Block { 57 | let mut scope = Scope::default(); 58 | 59 | for usage in &block.uses { 60 | let path = hir::Path::convert(&usage.path); 61 | scope.imports.insert(path.last(), path); 62 | } 63 | 64 | self.with_scope(scope, |this| { 65 | let statements = block.statements.iter().map(|s| this.lower_statement(s)).collect(); 66 | let return_expr = block 67 | .return_expr 68 | .as_ref() 69 | .map(|e| this.lower_expression(e)) 70 | .unwrap_or_else(|| Expression { kind: ExpressionKind::Unit, span: block.span }); 71 | Block { statements, return_expr, span: block.span } 72 | }) 73 | } 74 | 75 | pub fn lower_expression(&mut self, expression: &ast::Expression) -> Expression {} 76 | 77 | fn with_scope(&mut self, scope: Scope, mut f: impl FnMut(&mut Self) -> T) -> T { 78 | self.scopes.push(scope); 79 | let val = f(self); 80 | self.scopes.pop(); 81 | 82 | val 83 | } 84 | 85 | fn with_fresh_scope(&mut self, mut f: impl FnMut(&mut Self) -> T) -> T { 86 | self.scopes.push(Scope::default()); 87 | let val = f(self); 88 | self.scopes.pop(); 89 | 90 | val 91 | } 92 | 93 | fn with_no_parents(&mut self, mut f: impl FnMut(&mut Self) -> T) -> T { 94 | self.scopes.push(Scope::default()); 95 | let val = f(self); 96 | self.scopes.pop(); 97 | 98 | val 99 | } 100 | 101 | // Two step process to check for if an identifier is an import or binding: 102 | // 103 | // 1. Check latest scope to see if its a binding that would shadow an import 104 | // 2. Check latest scope imports 105 | // 3. If neither of the above, repeat for the parent scope if it exists 106 | fn get_ident_kind(&self, ident: Identifier) -> IdentKind { 107 | self.scopes.iter().rev().filter_map(|scope| scope.ident_kind(ident)).next().expect("ident doesn't exist rip") 108 | } 109 | } 110 | 111 | #[derive(Debug, Default, Clone)] 112 | struct Scope { 113 | pub(crate) imports: HashMap, 114 | pub(crate) identifiers: HashSet, 115 | } 116 | 117 | impl Scope { 118 | fn ident_kind(&self, ident: Identifier) -> Option { 119 | if self.identifiers.get(&ident).is_some() { 120 | Some(IdentKind::Binding) 121 | } else if let Some(path) = self.imports.get(&ident) { 122 | Some(IdentKind::Import(path.clone())) 123 | } else { 124 | None 125 | } 126 | } 127 | } 128 | 129 | #[derive(Debug, Clone)] 130 | enum IdentKind { 131 | Binding, 132 | Import(Path), 133 | } 134 | 135 | #[cfg(test)] 136 | mod tests { 137 | use super::*; 138 | 139 | #[test] 140 | fn test() { 141 | let code = "use foo::Bar; fn main() { }"; 142 | let ast = parser::Parser::new(code).geode().unwrap(); 143 | let mut lowering_context = LoweringContext::default(); 144 | dbg!(lowering_context.lower_module(&ast.module)); 145 | panic!() 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /ast_lowering/src/visitors.rs: -------------------------------------------------------------------------------- 1 | use super::IdentKind; 2 | use ast::Visitor; 3 | use hir::{Expression, ExpressionKind, Path}; 4 | 5 | pub struct UseCollector<'a> { 6 | ctx: &'a mut super::LoweringContext, 7 | } 8 | 9 | impl<'a> UseCollector<'a> { 10 | pub fn new(ctx: &'a mut super::LoweringContext) -> Self { 11 | Self { ctx } 12 | } 13 | } 14 | 15 | impl Visitor for UseCollector<'_> { 16 | fn visit_use(&mut self, usage: &ast::Use) { 17 | let path = hir::Path::convert(&usage.path); 18 | self.ctx.scopes.last_mut().expect("no import scope").imports.insert(path.last(), path); 19 | } 20 | 21 | fn visit_item(&mut self, item: &ast::Item) { 22 | if let ast::Item::Use(usage) = &item { 23 | self.visit_use(usage); 24 | } 25 | } 26 | } 27 | 28 | pub struct ExprPathReplacer { 29 | can_replace_idents: bool, 30 | } 31 | 32 | impl ExprPathReplacer { 33 | pub fn new() -> Self { 34 | Self { can_replace_idents: true } 35 | } 36 | 37 | pub fn replace(&mut self, ctx: &mut super::LoweringContext, expr: &mut Expression) { 38 | match &mut expr.kind { 39 | ExpressionKind::Unit | ExpressionKind::Integer(_) | ExpressionKind::Boolean(_) => {} 40 | ExpressionKind::Path(p) => match ctx.get_ident_kind(p.first()) { 41 | IdentKind::Binding => {} 42 | IdentKind::Import(mut full_path) => match p.is_identifier() { 43 | Some(_) if self.can_replace_idents => *p = full_path, 44 | None => { 45 | for segment in &p.segments[1..] { 46 | full_path.segments.push(*segment); 47 | } 48 | *p = full_path; 49 | } 50 | _ => {}, 51 | }, 52 | }, 53 | ExpressionKind::BinaryOperation(lhs, _, rhs) => { 54 | self.replace(ctx, &mut *lhs); 55 | self.replace(ctx, &mut *rhs); 56 | }, 57 | ExpressionKind::FieldAccess(lhs, _) => { 58 | self.replace(ctx, &mut *lhs); 59 | }, 60 | ExpressionKind::If(ifs) => { 61 | 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /hir/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hir" 3 | version = "0.1.0" 4 | authors = ["Wesley Norris "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | ast = { path = "../ast" } 11 | codespan = "0.9.2" 12 | string-interner = "0.7.1" -------------------------------------------------------------------------------- /hir/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod ty; 2 | pub mod visit; 3 | 4 | pub use ast::{BinOp, UnaryOp}; 5 | use codespan::Span; 6 | use std::{ 7 | cell::RefCell, 8 | fmt::{self, Display, Formatter}, 9 | }; 10 | pub use string_interner::{DefaultStringInterner, Sym}; 11 | pub use ty::*; 12 | 13 | std::thread_local! { 14 | static INTERNER: RefCell = RefCell::new(DefaultStringInterner::new()); 15 | } 16 | 17 | #[derive(Clone, Debug)] 18 | pub struct Module { 19 | pub name: Identifier, 20 | pub items: Vec, 21 | pub span: Span, 22 | } 23 | 24 | impl Module { 25 | pub fn new(name: Identifier, items: Vec, span: Span) -> Self { 26 | Self { name, items, span } 27 | } 28 | 29 | pub fn convert(module: &ast::Module) -> Self { 30 | Self { 31 | name: Identifier::convert(&module.name), 32 | items: module.items.iter().map(Item::convert).collect(), 33 | span: module.span, 34 | } 35 | } 36 | } 37 | 38 | #[derive(Clone, Debug)] 39 | pub struct Item { 40 | pub kind: ItemKind, 41 | pub span: Span, 42 | } 43 | 44 | impl Item { 45 | pub fn convert(item: &ast::Item) -> Self { 46 | Self { kind: ItemKind::convert(&item), span: item.span() } 47 | } 48 | } 49 | 50 | #[derive(Clone, Debug)] 51 | pub enum ItemKind { 52 | Module(Module), 53 | Function(Function), 54 | Struct(Struct), 55 | Use(Use), 56 | } 57 | 58 | impl ItemKind { 59 | pub fn convert(item: &ast::Item) -> Self { 60 | match item { 61 | ast::Item::Function(f) => ItemKind::Function(Function::convert(f)), 62 | ast::Item::Module(f) => ItemKind::Module(Module::convert(f)), 63 | ast::Item::Struct(f) => ItemKind::Struct(Struct::convert(f)), 64 | ast::Item::Use(u) => ItemKind::Use(Use::convert(u)), 65 | } 66 | } 67 | } 68 | 69 | #[derive(Clone, Debug)] 70 | pub struct Use { 71 | pub path: Path, 72 | pub span: Span, 73 | } 74 | 75 | impl Use { 76 | pub fn convert(usage: &ast::Use) -> Self { 77 | Self { path: Path::convert(&usage.path), span: usage.span } 78 | } 79 | } 80 | 81 | #[derive(Clone, Hash, PartialEq, Eq, Debug, Default)] 82 | pub struct Path { 83 | pub segments: Vec, 84 | } 85 | 86 | impl std::fmt::Display for Path { 87 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 88 | INTERNER.with(|i| { 89 | for (idx, ident) in self.segments.iter().enumerate() { 90 | write!(f, "{}", i.borrow_mut().resolve(ident.name).unwrap())?; 91 | 92 | if idx != self.segments.len() - 1 { 93 | write!(f, "::")?; 94 | } 95 | } 96 | 97 | Ok(()) 98 | }) 99 | } 100 | } 101 | 102 | impl Path { 103 | pub fn new() -> Self { 104 | Self::default() 105 | } 106 | 107 | pub fn from_identifier(ident: Identifier) -> Self { 108 | Self { segments: vec![ident] } 109 | } 110 | 111 | pub fn last(&self) -> Identifier { 112 | self.segments.last().cloned().unwrap() 113 | } 114 | 115 | pub fn first(&self) -> Identifier { 116 | self.segments.first().cloned().unwrap() 117 | } 118 | 119 | pub fn with_ident(&self, ident: Identifier) -> Self { 120 | let mut segments = self.segments.clone(); 121 | segments.push(ident); 122 | 123 | Self { segments } 124 | } 125 | 126 | pub fn is_identifier(&self) -> Option { 127 | match self.segments.len() { 128 | 1 => Some(self.first()), 129 | _ => None, 130 | } 131 | } 132 | 133 | pub fn pop(&mut self) -> Option { 134 | self.segments.pop() 135 | } 136 | 137 | pub fn convert(ast: &ast::Path) -> Self { 138 | Self { segments: ast.segments.iter().map(|ident| Identifier::convert(ident)).collect() } 139 | } 140 | 141 | pub fn canonicalize(&self) -> Self { 142 | let mut segments = Vec::with_capacity(self.segments.len()); 143 | 144 | for &seg in &self.segments { 145 | if seg.string() == "super" { 146 | segments.pop(); 147 | } else { 148 | segments.push(seg); 149 | } 150 | } 151 | 152 | Self { segments } 153 | } 154 | 155 | pub fn join(&self, other: &Self) -> Self { 156 | Self { 157 | segments: { 158 | let mut segs = self.segments.clone(); 159 | segs.extend_from_slice(&other.segments); 160 | segs 161 | }, 162 | } 163 | } 164 | } 165 | 166 | #[derive(Clone, Copy, Eq)] 167 | pub struct Identifier { 168 | pub name: Sym, 169 | pub span: Span, 170 | } 171 | 172 | impl Identifier { 173 | pub fn new(s: &str) -> Self { 174 | let sym = INTERNER.with(|i| i.borrow_mut().get_or_intern(s)); 175 | Identifier::with_dummy_span(sym) 176 | } 177 | 178 | pub fn string(self) -> String { 179 | INTERNER.with(|i| i.borrow().resolve(self.name).unwrap().to_owned()) 180 | } 181 | pub fn convert(ast: &ast::Identifier) -> Self { 182 | Self { name: INTERNER.with(|i| i.borrow_mut().get_or_intern(&ast.value)), span: ast.span } 183 | } 184 | 185 | pub fn with_dummy_span(name: Sym) -> Self { 186 | Self { name, span: Span::new(0, 0) } 187 | } 188 | } 189 | 190 | impl std::fmt::Display for Identifier { 191 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 192 | write!(f, "{:?}", self) 193 | } 194 | } 195 | 196 | impl std::fmt::Debug for Identifier { 197 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 198 | INTERNER.with(|i| write!(f, "{}", i.borrow_mut().resolve(self.name).unwrap())) 199 | } 200 | } 201 | 202 | impl PartialEq for Identifier { 203 | fn eq(&self, other: &Self) -> bool { 204 | self.name == other.name 205 | } 206 | } 207 | 208 | impl std::hash::Hash for Identifier { 209 | fn hash(&self, state: &mut H) { 210 | self.name.hash(state); 211 | } 212 | } 213 | 214 | #[derive(Clone, Debug)] 215 | pub struct Statement { 216 | pub kind: StatementKind, 217 | pub span: Span, 218 | } 219 | 220 | impl Statement { 221 | pub fn convert(statement: &ast::Statement) -> Self { 222 | Self { kind: StatementKind::convert(&statement.kind), span: statement.span } 223 | } 224 | } 225 | 226 | #[derive(Clone, Debug)] 227 | pub enum StatementKind { 228 | Local(Local), 229 | Expression(Expression), 230 | } 231 | 232 | impl StatementKind { 233 | pub fn convert(sk: &ast::StatementKind) -> Self { 234 | match sk { 235 | ast::StatementKind::Expression(e) => StatementKind::Expression(Expression::convert(e)), 236 | ast::StatementKind::VariableBinding(vb) => StatementKind::Local(Local::convert(vb)), 237 | } 238 | } 239 | } 240 | 241 | #[derive(Clone, Debug)] 242 | pub struct Local { 243 | pub name: Identifier, 244 | pub mutable: bool, 245 | pub ty: Type, 246 | pub value: Expression, 247 | pub span: Span, 248 | } 249 | 250 | impl Local { 251 | pub fn convert(vb: &ast::VariableBinding) -> Self { 252 | let name = Identifier::convert(&vb.name); 253 | let mutable = vb.mutable; 254 | let ty = Type::convert_optional(&vb.ty); 255 | let value = Expression::convert(&vb.value); 256 | let span = vb.span; 257 | 258 | Self { name, mutable, ty, value, span } 259 | } 260 | } 261 | 262 | #[derive(Clone, Debug)] 263 | pub struct Expression { 264 | pub kind: ExpressionKind, 265 | pub span: Span, 266 | } 267 | 268 | impl Expression { 269 | pub fn convert(expr: &ast::Expression) -> Self { 270 | let kind = ExpressionKind::convert(&expr.kind); 271 | let span = expr.span; 272 | 273 | Self { kind, span } 274 | } 275 | } 276 | 277 | #[derive(Clone, Debug)] 278 | pub enum ExpressionKind { 279 | Assignment(Box, Box), 280 | BinaryOperation(Box, BinOp, Box), 281 | Block(Box), 282 | Boolean(bool), 283 | FieldAccess(Box, Identifier), 284 | FnCall(Box, Vec), 285 | If(Box), 286 | Integer(i128), 287 | Path(Path), 288 | Struct(StructExpr), 289 | Unary(UnaryOp, Box), 290 | Unit, 291 | } 292 | 293 | impl ExpressionKind { 294 | pub fn convert(kind: &ast::ExpressionKind) -> Self { 295 | match kind { 296 | ast::ExpressionKind::Assignment(lhs, rhs) => { 297 | ExpressionKind::Assignment(Box::new(Expression::convert(lhs)), Box::new(Expression::convert(rhs))) 298 | } 299 | ast::ExpressionKind::BinaryOperation(e1, op, e2) => ExpressionKind::BinaryOperation( 300 | Box::new(Expression::convert(&e1)), 301 | *op, 302 | Box::new(Expression::convert(&e2)), 303 | ), 304 | ast::ExpressionKind::Block(b) => ExpressionKind::Block(Box::new(Block::convert(&b))), 305 | ast::ExpressionKind::Boolean(b) => ExpressionKind::Boolean(*b), 306 | ast::ExpressionKind::FieldAccess(e, ident) => { 307 | ExpressionKind::FieldAccess(Box::new(Expression::convert(e)), Identifier::convert(ident)) 308 | } 309 | ast::ExpressionKind::Integer(i) => ExpressionKind::Integer(*i), 310 | ast::ExpressionKind::Path(path) => ExpressionKind::Path(Path::convert(path)), 311 | ast::ExpressionKind::Struct(s) => ExpressionKind::Struct(StructExpr::convert(s)), 312 | ast::ExpressionKind::Unit => ExpressionKind::Unit, 313 | ast::ExpressionKind::FnCall(lhs, args) => ExpressionKind::FnCall( 314 | Box::new(Expression::convert(&lhs)), 315 | args.iter().map(Expression::convert).collect(), 316 | ), 317 | ast::ExpressionKind::Unary(op, expr) => ExpressionKind::Unary(*op, Box::new(Expression::convert(&expr))), 318 | ast::ExpressionKind::If(if_expr) => ExpressionKind::If(Box::new(IfExpr::convert(&if_expr))), 319 | } 320 | } 321 | } 322 | 323 | #[derive(Debug, Clone)] 324 | pub struct IfExpr { 325 | pub ifs: Vec, 326 | pub r#else: Block, 327 | pub span: Span, 328 | } 329 | 330 | impl IfExpr { 331 | pub fn convert(if_expr: &ast::IfExpr) -> Self { 332 | Self { 333 | ifs: if_expr.ifs.iter().map(If::convert).collect(), 334 | r#else: if_expr.r#else.as_ref().map(Block::convert).unwrap_or_else(|| Block { 335 | statements: Vec::new(), 336 | return_expr: Expression { kind: ExpressionKind::Unit, span: Span::new(0, 0) }, 337 | span: Span::new(0, 0), 338 | }), 339 | span: if_expr.span, 340 | } 341 | } 342 | } 343 | 344 | #[derive(Debug, Clone)] 345 | pub struct If { 346 | pub condition: Expression, 347 | pub body: Block, 348 | pub span: Span, 349 | } 350 | 351 | impl If { 352 | pub fn convert(if_: &ast::If) -> Self { 353 | Self { condition: Expression::convert(&if_.condition), body: Block::convert(&if_.body), span: if_.span } 354 | } 355 | } 356 | 357 | #[derive(Debug, Clone)] 358 | pub struct StructExpr { 359 | pub name: Path, 360 | pub members: Vec, 361 | pub span: Span, 362 | } 363 | 364 | impl StructExpr { 365 | pub fn convert(struct_expr: &ast::StructExpr) -> Self { 366 | let name = Path::convert(&struct_expr.name); 367 | let members = struct_expr.members.iter().map(StructExprMember::convert).collect(); 368 | let span = struct_expr.span; 369 | 370 | Self { name, members, span } 371 | } 372 | } 373 | 374 | #[derive(Debug, Clone)] 375 | pub struct StructExprMember { 376 | pub name: Identifier, 377 | pub expression: Expression, 378 | pub span: Span, 379 | } 380 | 381 | impl StructExprMember { 382 | pub fn convert(se_member: &ast::StructExprMember) -> Self { 383 | let name = Identifier::convert(&se_member.name); 384 | let expression = Expression::convert(&se_member.expression); 385 | let span = se_member.span; 386 | 387 | Self { name, expression, span } 388 | } 389 | } 390 | 391 | #[derive(Clone, Debug)] 392 | pub struct Block { 393 | pub statements: Vec, 394 | pub return_expr: Expression, 395 | pub span: Span, 396 | } 397 | 398 | impl Block { 399 | pub fn convert(block: &ast::Block) -> Self { 400 | Self { 401 | statements: block.statements.iter().map(Statement::convert).collect(), 402 | return_expr: block.return_expr.as_ref().map(Expression::convert).unwrap_or_else(|| Expression { 403 | kind: ExpressionKind::Unit, 404 | span: block.statements.last().map(|s| s.span).unwrap_or(block.span), 405 | }), 406 | span: block.span, 407 | } 408 | } 409 | } 410 | 411 | #[derive(Clone, Debug)] 412 | pub struct Function { 413 | pub name: Identifier, 414 | pub parameters: Vec, 415 | pub return_type: Type, 416 | pub body: Block, 417 | } 418 | 419 | impl Function { 420 | pub fn convert(f: &ast::Function) -> Self { 421 | Self { 422 | name: Identifier::convert(&f.name), 423 | parameters: f.parameters.iter().map(FunctionParameter::convert).collect(), 424 | return_type: f.return_ty.as_ref().map(Type::convert).unwrap_or_else(|| Type { 425 | kind: TypeKind::Unit, 426 | // FIXME 427 | span: Span::new(0, 0), 428 | }), 429 | body: Block::convert(&f.body), 430 | } 431 | } 432 | } 433 | 434 | #[derive(Clone, Debug)] 435 | pub struct FunctionParameter { 436 | pub name: Identifier, 437 | pub ty: Type, 438 | pub span: Span, 439 | } 440 | 441 | impl FunctionParameter { 442 | pub fn convert(fp: &ast::FunctionParameter) -> Self { 443 | Self { name: Identifier::convert(&fp.name), ty: Type::convert(&fp.ty), span: fp.span } 444 | } 445 | } 446 | 447 | #[derive(Clone, Debug)] 448 | pub struct Struct { 449 | pub name: Identifier, 450 | pub members: Vec, 451 | pub span: Span, 452 | } 453 | 454 | impl Struct { 455 | pub fn convert(strukt: &ast::Struct) -> Self { 456 | Self { 457 | name: Identifier::convert(&strukt.name), 458 | members: strukt.members.iter().map(StructMember::convert).collect(), 459 | span: strukt.span, 460 | } 461 | } 462 | } 463 | 464 | impl Display for Struct { 465 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 466 | writeln!(f, "struct {} {{", self.name)?; 467 | for member in &self.members { 468 | writeln!(f, " {}", member)?; 469 | } 470 | write!(f, "}}") 471 | } 472 | } 473 | 474 | #[derive(Clone, Debug)] 475 | pub struct StructMember { 476 | pub name: Identifier, 477 | pub ty: Type, 478 | pub span: Span, 479 | } 480 | 481 | impl StructMember { 482 | pub fn convert(member: &ast::StructMember) -> Self { 483 | Self { name: Identifier::convert(&member.name), ty: Type::convert(&member.ty), span: member.span } 484 | } 485 | } 486 | 487 | impl Display for StructMember { 488 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 489 | write!(f, "{}: {}", self.name, self.ty.kind) 490 | } 491 | } 492 | -------------------------------------------------------------------------------- /hir/src/ty.rs: -------------------------------------------------------------------------------- 1 | use crate::Path; 2 | use codespan::Span; 3 | use std::fmt::{self, Display, Formatter}; 4 | 5 | #[derive(Clone, Debug)] 6 | pub struct Type { 7 | pub kind: TypeKind, 8 | pub span: Span, 9 | } 10 | 11 | impl Type { 12 | pub fn convert(ty: &ast::Type) -> Self { 13 | Self { kind: TypeKind::convert(&ty.kind), span: ty.span } 14 | } 15 | 16 | pub fn convert_optional(ty: &Option) -> Self { 17 | match ty { 18 | Some(ty) => Self::convert(ty), 19 | None => Type { kind: TypeKind::Infer, span: Span::new(0, 0) }, 20 | } 21 | } 22 | } 23 | 24 | #[derive(Clone, Debug)] 25 | pub enum TypeKind { 26 | Integer, 27 | Bool, 28 | Path(Path), 29 | Unit, 30 | Infer, 31 | } 32 | 33 | impl TypeKind { 34 | pub fn convert(ast: &ast::TypeKind) -> Self { 35 | match ast { 36 | ast::TypeKind::Bool => TypeKind::Bool, 37 | ast::TypeKind::Integer => TypeKind::Integer, 38 | ast::TypeKind::Named(path) => TypeKind::Path(Path::convert(path)), 39 | } 40 | } 41 | } 42 | 43 | impl Display for TypeKind { 44 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 45 | match self { 46 | TypeKind::Integer => write!(f, "Int"), 47 | TypeKind::Bool => write!(f, "Bool"), 48 | TypeKind::Path(p) => write!(f, "{}", p), 49 | TypeKind::Unit => write!(f, "Unit"), 50 | TypeKind::Infer => write!(f, "_"), 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /hir/src/visit.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | 3 | pub trait Visitor: Sized { 4 | fn visit_module(&mut self, module: &Module) { 5 | walk::module(self, module); 6 | } 7 | 8 | fn visit_item(&mut self, item: &Item) { 9 | walk::item(self, item); 10 | } 11 | 12 | fn visit_function(&mut self, function: &Function) { 13 | walk::function(self, function); 14 | } 15 | 16 | fn visit_identifier(&mut self, _: &Identifier) { 17 | // Nothing else to do 18 | } 19 | 20 | fn visit_struct(&mut self, strukt: &Struct) { 21 | walk::structure(self, strukt); 22 | } 23 | 24 | fn visit_struct_member(&mut self, struct_member: &StructMember) { 25 | walk::struct_member(self, struct_member); 26 | } 27 | 28 | fn visit_path(&mut self, path: &Path) { 29 | walk::path(self, path); 30 | } 31 | 32 | fn visit_type(&mut self, ty: &Type) { 33 | walk::ty(self, ty); 34 | } 35 | 36 | fn visit_function_parameter(&mut self, _: &FunctionParameter) { 37 | todo!() 38 | } 39 | 40 | fn visit_block(&mut self, _: &Block) { 41 | todo!() 42 | } 43 | 44 | fn visit_statement(&mut self, _: &Statement) { 45 | todo!() 46 | } 47 | 48 | fn visit_expression(&mut self, _: &Expression) { 49 | todo!() 50 | } 51 | 52 | fn visit_use(&mut self, usage: &Use) { 53 | walk::usage(self, usage); 54 | } 55 | } 56 | 57 | pub mod walk { 58 | use super::*; 59 | 60 | #[doc(hidden)] 61 | #[macro_export] 62 | macro_rules! _list { 63 | ($v:ident, $v_method:ident, $list:expr) => { 64 | for item in $list { 65 | $v.$v_method(item); 66 | } 67 | }; 68 | } 69 | 70 | pub use _list as list; 71 | 72 | pub fn module(visitor: &mut V, module: &Module) { 73 | list!(visitor, visit_item, &module.items); 74 | } 75 | 76 | pub fn item(visitor: &mut V, item: &Item) { 77 | match &item.kind { 78 | ItemKind::Function(f) => visitor.visit_function(f), 79 | ItemKind::Module(m) => visitor.visit_module(m), 80 | ItemKind::Struct(s) => visitor.visit_struct(s), 81 | ItemKind::Use(u) => visitor.visit_use(u), 82 | } 83 | } 84 | 85 | pub fn structure(visitor: &mut V, strukt: &Struct) { 86 | list!(visitor, visit_struct_member, &strukt.members); 87 | } 88 | 89 | pub fn struct_member(visitor: &mut V, struct_member: &StructMember) { 90 | visitor.visit_identifier(&struct_member.name); 91 | visitor.visit_type(&struct_member.ty); 92 | } 93 | 94 | pub fn path(visitor: &mut V, path: &Path) { 95 | list!(visitor, visit_identifier, &path.segments); 96 | } 97 | 98 | pub fn ty(visitor: &mut V, ty: &Type) { 99 | match &ty.kind { 100 | TypeKind::Integer | TypeKind::Bool | TypeKind::Unit | TypeKind::Infer => todo!("hmm"), 101 | TypeKind::Path(path) => visitor.visit_path(path), 102 | } 103 | } 104 | 105 | pub fn function(visitor: &mut V, function: &Function) { 106 | list!(visitor, visit_function_parameter, &function.parameters); 107 | visitor.visit_type(&function.return_type); 108 | visitor.visit_block(&function.body); 109 | } 110 | 111 | pub fn usage(visitor: &mut V, usage: &Use) { 112 | visitor.visit_path(&usage.path); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parser" 3 | version = "0.1.0" 4 | authors = ["Wesley Norris "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | ast = { path = "../ast" } 11 | codespan = "0.9.5" 12 | logos = "0.11.4" -------------------------------------------------------------------------------- /parser/src/lexer.rs: -------------------------------------------------------------------------------- 1 | use codespan::Span; 2 | use logos::Logos; 3 | 4 | #[derive(Clone, Debug, PartialEq)] 5 | pub struct Token { 6 | pub span: Span, 7 | pub kind: TokenKind, 8 | } 9 | 10 | impl Token { 11 | pub fn new(span: Span, kind: TokenKind) -> Self { 12 | Self { span, kind } 13 | } 14 | 15 | pub fn span(&self) -> Span { 16 | self.span 17 | } 18 | 19 | pub fn kind(&self) -> &TokenKind { 20 | &self.kind 21 | } 22 | } 23 | 24 | impl std::fmt::Display for Token { 25 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 26 | write!(f, "{}", self.kind) 27 | } 28 | } 29 | 30 | #[derive(Logos, Clone, Debug, PartialEq)] 31 | pub enum TokenKind { 32 | #[error] 33 | Error, 34 | 35 | // Trivia 36 | #[token("(")] 37 | LeftParen, 38 | #[token(")")] 39 | RightParen, 40 | #[token("[")] 41 | LeftBracket, 42 | #[token("]")] 43 | RightBracket, 44 | #[token("->")] 45 | ThinArrow, 46 | #[token("=>")] 47 | ThickArrow, 48 | #[token(",")] 49 | Comma, 50 | #[token(";")] 51 | Semicolon, 52 | #[token("{")] 53 | LeftBrace, 54 | #[token("}")] 55 | RightBrace, 56 | #[token("+")] 57 | Plus, 58 | #[token("-")] 59 | Minus, 60 | #[token("*")] 61 | Star, 62 | #[token("/")] 63 | Slash, 64 | #[token("==")] 65 | DoubleEq, 66 | #[token("=")] 67 | Eq, 68 | #[token(":")] 69 | Colon, 70 | #[token(".")] 71 | Period, 72 | #[token("::")] 73 | PathSep, 74 | 75 | // Keywords 76 | #[token("fn")] 77 | Fn, 78 | #[token("if")] 79 | If, 80 | #[token("while")] 81 | While, 82 | #[token("let")] 83 | Let, 84 | #[token("use")] 85 | Use, 86 | #[token("mut")] 87 | Mut, 88 | #[token("struct")] 89 | Struct, 90 | #[token("module")] 91 | Module, 92 | #[token("else")] 93 | Else, 94 | 95 | // FIXME: Move to an enum when we can 96 | #[token("true")] 97 | True, 98 | #[token("false")] 99 | False, 100 | #[token("Int")] 101 | Int, 102 | #[token("Bool")] 103 | Bool, 104 | #[token("Unit")] 105 | Unit, 106 | 107 | #[token("\"")] 108 | DoubleQuote, 109 | #[token("'")] 110 | SingleQuote, 111 | #[regex(r#"(?x: 112 | [\p{XID_Start}_] 113 | \p{XID_Continue}* 114 | (\u{3F} | \u{21} | (\u{3F}\u{21}) | \u{2048})? # ? ! ?! ⁈ 115 | )"#, |lex| lex.slice().to_string())] 116 | Identifier(String), 117 | #[regex("\\d+", |lex| lex.slice().parse())] 118 | Integer(i128), 119 | 120 | Generic(String), 121 | Character(char), 122 | 123 | #[regex("[\n\r\t ]+", logos::skip)] 124 | Whitespace, 125 | } 126 | 127 | impl TokenKind { 128 | pub fn is_binop(&self) -> bool { 129 | match self { 130 | TokenKind::Plus | TokenKind::Minus | TokenKind::Star | TokenKind::Slash | TokenKind::DoubleEq => true, 131 | _ => false, 132 | } 133 | } 134 | 135 | pub fn as_str(&self) -> &'static str { 136 | use TokenKind::*; 137 | match self { 138 | LeftParen => "(", 139 | RightParen => ")", 140 | LeftBracket => "[", 141 | RightBracket => "]", 142 | ThinArrow => "->", 143 | ThickArrow => "=>", 144 | Comma => ",", 145 | Semicolon => ";", 146 | LeftBrace => "{", 147 | RightBrace => "}", 148 | Plus => "+", 149 | Minus => "-", 150 | Star => "*", 151 | Slash => "/", 152 | DoubleEq => "==", 153 | Eq => "=", 154 | Colon => ":", 155 | Period => ".", 156 | Fn => "fn", 157 | If => "if", 158 | While => "while", 159 | Let => "let", 160 | Use => "use", 161 | Mut => "mut", 162 | Struct => "struct", 163 | Module => "module", 164 | Else => "else", 165 | True => "true", 166 | False => "false", 167 | Int => "Int", 168 | Bool => "Bool", 169 | Unit => "Unit", 170 | DoubleQuote => "\"", 171 | SingleQuote => "'", 172 | PathSep => "::", 173 | Identifier(_) => "identifier", 174 | Integer(_) => "integer", 175 | Error | Generic(_) | Character(_) | Whitespace => unreachable!(), 176 | } 177 | } 178 | } 179 | 180 | impl std::fmt::Display for TokenKind { 181 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 182 | write!(f, "{}", self.as_str()) 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::match_bool)] 2 | 3 | mod lexer; 4 | 5 | pub use lexer::{Token, TokenKind}; 6 | 7 | use ast::*; 8 | use codespan::Span; 9 | use logos::Lexer; 10 | use std::collections::VecDeque; 11 | 12 | pub type Result = std::result::Result; 13 | 14 | pub enum Either { 15 | Left(T), 16 | Right(U), 17 | } 18 | 19 | #[derive(Debug, PartialEq)] 20 | pub enum ParseError { 21 | Eof, 22 | BadToken { got: Token, expected: Vec<&'static str> }, 23 | BadBinOp, 24 | } 25 | 26 | pub struct Parser<'a> { 27 | lexer: Lexer<'a, TokenKind>, 28 | peeks: VecDeque, 29 | } 30 | 31 | impl<'a> Parser<'a> { 32 | pub fn new(input: &'a str) -> Self { 33 | Self { lexer: Lexer::new(input), peeks: VecDeque::new() } 34 | } 35 | 36 | pub fn guess(&mut self) -> Result> { 37 | if self.peek() == Err(ParseError::Eof) { 38 | return Ok(None); 39 | } 40 | 41 | match self.peek()?.kind { 42 | TokenKind::Fn | TokenKind::Struct | TokenKind::Module | TokenKind::Use => { 43 | Ok(Some(AstNode::Item(self.item()?))) 44 | } 45 | _ => match self.statement_or_expression()? { 46 | Either::Left(stmt) => Ok(Some(AstNode::Statement(stmt))), 47 | Either::Right(expr) => Ok(Some(AstNode::Expression(expr))), 48 | }, 49 | } 50 | } 51 | 52 | pub fn geode(&mut self) -> Result { 53 | let module = self.module(true)?; 54 | let span = module.span; 55 | 56 | Ok(Geode { module, span }) 57 | } 58 | 59 | pub fn module(&mut self, implicit: bool) -> Result { 60 | let (start_span, name) = match implicit { 61 | true => match self.peek() { 62 | Ok(token) => (token.span(), Identifier::dummy()), 63 | Err(ParseError::Eof) => { 64 | return Ok(Module { name: Identifier::dummy(), items: Vec::new(), span: Span::new(0, 0) }) 65 | } 66 | Err(e) => return Err(e), 67 | }, 68 | false => { 69 | let start = self.eat(TokenKind::Module)?; 70 | let name = self.identifier()?; 71 | self.eat(TokenKind::LeftBrace)?; 72 | 73 | (start, name) 74 | } 75 | }; 76 | 77 | let mut items = Vec::new(); 78 | 79 | loop { 80 | match implicit { 81 | true => { 82 | if let Err(ParseError::Eof) = self.peek() { 83 | break; 84 | } 85 | } 86 | false if self.peek()?.kind == TokenKind::RightBrace => break, 87 | _ => {} 88 | } 89 | 90 | items.push(self.item()?); 91 | } 92 | 93 | let end_span = match implicit { 94 | true => items 95 | .last() 96 | .map(|i| i.span()) 97 | .unwrap_or_else(|| Span::new(start_span.start(), start_span.end() + codespan::ByteOffset::from(1))), 98 | false => self.eat(TokenKind::RightBrace)?, 99 | }; 100 | 101 | let span = start_span.merge(end_span); 102 | 103 | Ok(Module { name, items, span }) 104 | } 105 | 106 | pub fn item(&mut self) -> Result { 107 | match self.peek()?.kind { 108 | TokenKind::Fn => Ok(Item::Function(self.function()?)), 109 | TokenKind::Struct => Ok(Item::Struct(self.r#struct()?)), 110 | TokenKind::Module => Ok(Item::Module(self.module(false)?)), 111 | TokenKind::Use => Ok(Item::Use(self.usage()?)), 112 | _ => Err(ParseError::BadToken { got: self.peek()?, expected: vec!["fn", "struct", "module", "use"] }), 113 | } 114 | } 115 | 116 | pub fn function(&mut self) -> Result { 117 | let start_span = self.eat(TokenKind::Fn)?; 118 | let name = self.identifier()?; 119 | 120 | self.eat(TokenKind::LeftParen)?; 121 | 122 | let parameters = self.list(Self::function_parameter, TokenKind::RightParen)?; 123 | self.eat(TokenKind::RightParen)?; 124 | 125 | let return_ty = if self.peek()?.kind == TokenKind::ThinArrow { 126 | self.eat(TokenKind::ThinArrow)?; 127 | 128 | Some(self.ty()?) 129 | } else { 130 | None 131 | }; 132 | 133 | let body = self.block()?; 134 | let span = start_span.merge(body.span); 135 | 136 | Ok(Function { name, parameters, return_ty, body, span }) 137 | } 138 | 139 | pub fn function_parameter(&mut self) -> Result { 140 | let name = self.identifier()?; 141 | self.eat(TokenKind::Colon)?; 142 | let ty = self.ty()?; 143 | let span = name.span.merge(ty.span); 144 | 145 | Ok(FunctionParameter { name, ty, span }) 146 | } 147 | 148 | pub fn r#struct(&mut self) -> Result { 149 | let start_span = self.eat(TokenKind::Struct)?; 150 | let name = self.identifier()?; 151 | self.eat(TokenKind::LeftBrace)?; 152 | let members = self.list(Self::struct_member, TokenKind::RightBrace)?; 153 | let end_span = self.eat(TokenKind::RightBrace)?; 154 | let span = start_span.merge(end_span); 155 | 156 | Ok(Struct { name, members, span }) 157 | } 158 | 159 | pub fn struct_member(&mut self) -> Result { 160 | let name = self.identifier()?; 161 | self.eat(TokenKind::Colon)?; 162 | let ty = self.ty()?; 163 | let span = name.span.merge(ty.span); 164 | 165 | Ok(StructMember { name, ty, span }) 166 | } 167 | 168 | /// Note: does not consume the delimiter 169 | pub fn list(&mut self, mut f: F, delimiter: TokenKind) -> Result> 170 | where 171 | F: FnMut(&mut Self) -> Result, 172 | { 173 | let mut instances = Vec::new(); 174 | 175 | while self.peek()?.kind != delimiter { 176 | instances.push(f(self)?); 177 | 178 | if self.peek()?.kind == TokenKind::Comma { 179 | self.eat(TokenKind::Comma)?; 180 | } else { 181 | break; 182 | } 183 | } 184 | 185 | Ok(instances) 186 | } 187 | 188 | pub fn block(&mut self) -> Result { 189 | let start_span = self.eat(TokenKind::LeftBrace)?; 190 | 191 | let mut uses = Vec::new(); 192 | let mut statements = Vec::new(); 193 | let mut return_expr = None; 194 | let mut ate_expr = false; 195 | 196 | while self.peek()?.kind != TokenKind::RightBrace { 197 | match self.peek()?.kind { 198 | TokenKind::Use => uses.push(self.usage()?), 199 | _ => match self.statement_or_expression()? { 200 | Either::Left(stmt) if !ate_expr => statements.push(stmt), 201 | Either::Right(expr) if return_expr.is_none() => { 202 | return_expr = Some(expr); 203 | ate_expr = true; 204 | } 205 | _ => break, 206 | }, 207 | } 208 | } 209 | 210 | let end_span = self.eat(TokenKind::RightBrace)?; 211 | let span = start_span.merge(end_span); 212 | 213 | Ok(Block { uses, statements, return_expr, span }) 214 | } 215 | 216 | pub fn r#if(&mut self) -> Result { 217 | let mut ifs = Vec::new(); 218 | let mut r#else = None; 219 | 220 | let start_span = self.eat(TokenKind::If)?; 221 | let condition = self.expression()?; 222 | let body = self.block()?; 223 | let span = start_span.merge(body.span); 224 | 225 | ifs.push(If { condition, body, span }); 226 | 227 | while let Ok(Token { kind: TokenKind::Else, .. }) = self.peek() { 228 | let start_span = self.eat(TokenKind::Else)?; 229 | 230 | if let TokenKind::If = self.peek()?.kind { 231 | self.eat(TokenKind::If)?; 232 | let condition = self.expression()?; 233 | let body = self.block()?; 234 | let span = start_span.merge(body.span); 235 | 236 | ifs.push(If { condition, body, span }); 237 | } else { 238 | let body = self.block()?; 239 | r#else = Some(body); 240 | break; 241 | } 242 | } 243 | 244 | let span = start_span.merge(match &r#else { 245 | Some(block) => block.span, 246 | None => ifs.last().unwrap().span, 247 | }); 248 | 249 | Ok(IfExpr { ifs, r#else, span }) 250 | } 251 | 252 | pub fn statement_or_expression(&mut self) -> Result> { 253 | match self.peek()?.kind { 254 | TokenKind::Let => Ok(Either::Left(self.statement()?)), 255 | _ => { 256 | let expr = self.expression()?; 257 | 258 | if self.peek().map(|t| t.kind) == Ok(TokenKind::Semicolon) { 259 | let end_span = self.eat(TokenKind::Semicolon)?; 260 | let span = expr.span.merge(end_span); 261 | Ok(Either::Left(Statement { kind: StatementKind::Expression(expr), span })) 262 | } else { 263 | Ok(Either::Right(expr)) 264 | } 265 | } 266 | } 267 | } 268 | 269 | pub fn statement(&mut self) -> Result { 270 | match self.peek()?.kind { 271 | TokenKind::Let => { 272 | let binding = self.variable_binding()?; 273 | let span = binding.span; 274 | let kind = StatementKind::VariableBinding(binding); 275 | Ok(Statement { kind, span }) 276 | } 277 | TokenKind::Identifier(_) => { 278 | let expr = self.expression()?; 279 | let end_span = self.eat(TokenKind::Semicolon)?; 280 | let span = expr.span.merge(end_span); 281 | 282 | Ok(Statement { kind: StatementKind::Expression(expr), span }) 283 | } 284 | _ => todo!(), 285 | } 286 | } 287 | 288 | pub fn variable_binding(&mut self) -> Result { 289 | let let_span = self.eat(TokenKind::Let)?; 290 | 291 | let mutable = if self.peek()?.kind == TokenKind::Mut { 292 | self.eat(TokenKind::Mut)?; 293 | true 294 | } else { 295 | false 296 | }; 297 | 298 | let name = self.identifier()?; 299 | 300 | let ty = if self.peek()?.kind == TokenKind::Colon { 301 | self.eat(TokenKind::Colon)?; 302 | Some(self.ty()?) 303 | } else { 304 | None 305 | }; 306 | 307 | self.eat(TokenKind::Eq)?; 308 | let value = self.expression()?; 309 | let end = self.eat(TokenKind::Semicolon)?; 310 | 311 | Ok(VariableBinding { mutable, name, ty, value, span: let_span.merge(end) }) 312 | } 313 | 314 | pub fn expression(&mut self) -> Result { 315 | self.inner_expr(None) 316 | } 317 | 318 | fn inner_expr(&mut self, curr_binop: Option) -> Result { 319 | let mut primary = self.primary_expr()?; 320 | 321 | loop { 322 | if self.peek().is_err() { 323 | return Ok(primary); 324 | } 325 | 326 | match self.peek()?.kind { 327 | t if t.is_binop() => { 328 | let binop = self.binop()?; 329 | 330 | if matches!(curr_binop, Some(curr) if curr != binop) { 331 | return Err(ParseError::BadBinOp); 332 | } 333 | 334 | let rhs = self.inner_expr(Some(binop))?; 335 | let span = primary.span.merge(rhs.span); 336 | 337 | primary = Expression { 338 | kind: ExpressionKind::BinaryOperation(Box::new(primary), binop, Box::new(rhs)), 339 | span, 340 | }; 341 | } 342 | TokenKind::Period => { 343 | self.eat(TokenKind::Period)?; 344 | let ident = self.identifier()?; 345 | let span = primary.span.merge(ident.span); 346 | 347 | primary = Expression { kind: ExpressionKind::FieldAccess(Box::new(primary), ident), span }; 348 | } 349 | TokenKind::Eq => { 350 | self.eat(TokenKind::Eq)?; 351 | let rhs = self.expression()?; 352 | let span = primary.span.merge(rhs.span); 353 | 354 | return Ok(Expression { kind: ExpressionKind::Assignment(Box::new(primary), Box::new(rhs)), span }); 355 | } 356 | TokenKind::LeftParen => { 357 | let mut exprs = Vec::new(); 358 | self.eat(TokenKind::LeftParen)?; 359 | 360 | while self.peek()?.kind != TokenKind::RightParen { 361 | exprs.push(self.expression()?); 362 | 363 | if self.peek()?.kind == TokenKind::Comma { 364 | self.eat(TokenKind::Comma)?; 365 | } else { 366 | break; 367 | } 368 | } 369 | 370 | let end_span = self.eat(TokenKind::RightParen)?; 371 | let start_span = primary.span; 372 | 373 | primary = Expression { 374 | kind: ExpressionKind::FnCall(Box::new(primary), exprs), 375 | span: start_span.merge(end_span), 376 | }; 377 | } 378 | _ => return Ok(primary), 379 | } 380 | } 381 | } 382 | 383 | fn primary_expr(&mut self) -> Result { 384 | let peek = self.peek()?; 385 | let span = peek.span(); 386 | 387 | match peek.kind { 388 | TokenKind::Integer(n) => { 389 | self.eat(TokenKind::Integer(n))?; 390 | 391 | Ok(Expression { kind: ExpressionKind::Integer(n), span }) 392 | } 393 | TokenKind::LeftParen => { 394 | let start_span = self.eat(TokenKind::LeftParen)?; 395 | let mut expr = self.expression()?; 396 | let end_span = self.eat(TokenKind::RightParen)?; 397 | expr.span = start_span.merge(end_span); 398 | 399 | Ok(expr) 400 | } 401 | TokenKind::Identifier(_) => { 402 | let path = self.path()?; 403 | 404 | match self.peek() { 405 | Ok(Token { kind: TokenKind::LeftBrace, .. }) => { 406 | let struct_expr = Box::new(self.struct_expr(path)?); 407 | let span = struct_expr.span; 408 | 409 | Ok(Expression { kind: ExpressionKind::Struct(struct_expr), span }) 410 | } 411 | _ => Ok(Expression { kind: ExpressionKind::Path(path), span }), 412 | } 413 | } 414 | TokenKind::If => { 415 | let if_expr = self.r#if()?; 416 | let span = if_expr.span; 417 | Ok(Expression { kind: ExpressionKind::If(Box::new(if_expr)), span }) 418 | } 419 | TokenKind::Unit => { 420 | self.token()?; 421 | Ok(Expression { kind: ExpressionKind::Unit, span }) 422 | } 423 | TokenKind::LeftBrace => { 424 | let block = self.block()?; 425 | let span = block.span; 426 | Ok(Expression { kind: ExpressionKind::Block(Box::new(block)), span }) 427 | } 428 | TokenKind::Minus => { 429 | let start = self.eat(TokenKind::Minus)?; 430 | let expr = self.expression()?; 431 | let span = start.merge(expr.span); 432 | 433 | Ok(Expression { kind: ExpressionKind::Unary(UnaryOp::Minus, Box::new(expr)), span }) 434 | } 435 | b @ TokenKind::True | b @ TokenKind::False => { 436 | let value = match &b { 437 | TokenKind::True => true, 438 | TokenKind::False => false, 439 | _ => unreachable!(), 440 | }; 441 | 442 | self.eat(b)?; 443 | 444 | Ok(Expression { kind: ExpressionKind::Boolean(value), span }) 445 | } 446 | _ => Err(ParseError::BadToken { got: peek, expected: vec!["expression"] }), 447 | } 448 | } 449 | 450 | pub fn struct_expr(&mut self, name: Path) -> Result { 451 | self.eat(TokenKind::LeftBrace)?; 452 | let members = self.list(Self::struct_expr_member, TokenKind::RightBrace)?; 453 | let end_span = self.eat(TokenKind::RightBrace)?; 454 | let span = name.span.merge(end_span); 455 | 456 | Ok(StructExpr { name, members, span }) 457 | } 458 | 459 | pub fn struct_expr_member(&mut self) -> Result { 460 | let name = self.identifier()?; 461 | self.eat(TokenKind::Colon)?; 462 | let expression = self.expression()?; 463 | let span = name.span.merge(expression.span); 464 | 465 | Ok(StructExprMember { name, expression, span }) 466 | } 467 | 468 | pub fn ty(&mut self) -> Result { 469 | let token = self.peek()?; 470 | let span = token.span(); 471 | 472 | match token.kind { 473 | TokenKind::Int => { 474 | self.eat(TokenKind::Int)?; 475 | Ok(Type { kind: TypeKind::Integer, span }) 476 | } 477 | TokenKind::Bool => { 478 | self.eat(TokenKind::Bool)?; 479 | Ok(Type { kind: TypeKind::Bool, span }) 480 | } 481 | TokenKind::Identifier(_) => Ok(Type { kind: TypeKind::Named(self.path()?), span }), 482 | _ => Err(ParseError::BadToken { got: token, expected: vec!["type"] }), 483 | } 484 | } 485 | 486 | pub fn identifier(&mut self) -> Result { 487 | let token = self.token()?; 488 | let span = token.span(); 489 | 490 | match token.kind { 491 | TokenKind::Identifier(value) => Ok(Identifier { value, span }), 492 | _ => Err(ParseError::BadToken { got: token, expected: vec!["identifier"] }), 493 | } 494 | } 495 | 496 | pub fn path(&mut self) -> Result { 497 | let mut segments = Vec::new(); 498 | 499 | segments.push(self.identifier()?); 500 | 501 | while let Ok(Token { kind: TokenKind::PathSep, .. }) = self.peek() { 502 | self.eat(TokenKind::PathSep)?; 503 | segments.push(self.identifier()?); 504 | } 505 | 506 | let span = { 507 | let start = segments.first().unwrap().span; 508 | let end = segments.last().unwrap().span; 509 | 510 | start.merge(end) 511 | }; 512 | 513 | Ok(Path { segments, span }) 514 | } 515 | 516 | pub fn usage(&mut self) -> Result { 517 | let start_span = self.eat(TokenKind::Use)?; 518 | let path = self.path()?; 519 | let end_span = self.eat(TokenKind::Semicolon)?; 520 | let span = start_span.merge(end_span); 521 | 522 | Ok(Use { path, span }) 523 | } 524 | 525 | pub fn eat(&mut self, kind: TokenKind) -> Result { 526 | let token = self.token()?; 527 | 528 | if token.kind() == &kind { 529 | Ok(token.span()) 530 | } else { 531 | Err(ParseError::BadToken { got: token, expected: vec![kind.as_str()] }) 532 | } 533 | } 534 | 535 | pub fn peek(&mut self) -> Result { 536 | match self.peeks.front() { 537 | Some(tkn) => Ok(tkn.clone()), 538 | None => { 539 | let token = self.token()?; 540 | self.peeks.push_front(token.clone()); 541 | Ok(token) 542 | } 543 | } 544 | } 545 | 546 | pub fn peek2(&mut self) -> Result { 547 | match self.peeks.back() { 548 | Some(tkn) if self.peeks.len() == 2 => Ok(tkn.clone()), 549 | _ => { 550 | for _ in 0..(2 - self.peeks.len()) { 551 | loop { 552 | let token = self.lexer.next().ok_or(ParseError::Eof)?; 553 | 554 | if token == TokenKind::Whitespace { 555 | continue; 556 | } 557 | 558 | let span = self.lexer.span(); 559 | let span = Span::new(span.start as u32, span.end as u32); 560 | self.peeks.push_back(Token::new(span, token)); 561 | break; 562 | } 563 | } 564 | 565 | Ok(self.peeks.back().cloned().unwrap()) 566 | } 567 | } 568 | } 569 | 570 | pub fn binop(&mut self) -> Result { 571 | let token = self.token()?; 572 | 573 | match token.kind { 574 | TokenKind::Plus => Ok(BinOp::Add), 575 | TokenKind::Minus => Ok(BinOp::Subtract), 576 | TokenKind::Star => Ok(BinOp::Multiply), 577 | TokenKind::Slash => Ok(BinOp::Divide), 578 | TokenKind::DoubleEq => Ok(BinOp::Equal), 579 | _ => Err(ParseError::BadToken { got: token, expected: vec!["binary operator"] }), 580 | } 581 | } 582 | 583 | pub fn token(&mut self) -> Result { 584 | match self.peeks.pop_front() { 585 | Some(tkn) => Ok(tkn), 586 | // TODO: remove loop 587 | None => loop { 588 | let token = self.lexer.next().ok_or(ParseError::Eof)?; 589 | 590 | if token == TokenKind::Whitespace { 591 | continue; 592 | } 593 | 594 | let span = self.lexer.span(); 595 | let span = Span::new(span.start as u32, span.end as u32); 596 | 597 | break Ok(Token::new(span, token)); 598 | }, 599 | } 600 | } 601 | } 602 | -------------------------------------------------------------------------------- /repl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "repl" 3 | version = "0.1.0" 4 | authors = ["Wesley Norris "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | ast = { path = "../ast" } 11 | codespan = "0.9.4" 12 | codespan-reporting = "0.9.4" 13 | hir = { path = "../hir" } 14 | parser = { path = "../parser" } 15 | rustyline = "6.1.2" 16 | structopt = "0.3.14" 17 | typecheck = { path = "../typecheck" } -------------------------------------------------------------------------------- /repl/src/hir_engine/expr.rs: -------------------------------------------------------------------------------- 1 | use hir::{Identifier, Path}; 2 | use std::collections::HashMap; 3 | 4 | #[derive(Debug, Copy, Clone)] 5 | pub struct ExpressionId(pub usize); 6 | 7 | #[derive(Clone)] 8 | pub enum Expression { 9 | Integer(i128), 10 | Bool(bool), 11 | Struct(Path, HashMap), 12 | Function(Path), 13 | Unit, 14 | } 15 | 16 | impl Expression { 17 | pub fn is_unit(&self) -> bool { 18 | match self { 19 | Expression::Unit => true, 20 | _ => false, 21 | } 22 | } 23 | 24 | pub fn debug<'a>(&'a self, arena: &'a [Expression]) -> ExpressionDebug<'a> { 25 | ExpressionDebug { expr: self, arena, indent_level: 0 } 26 | } 27 | } 28 | 29 | pub struct ExpressionDebug<'a> { 30 | arena: &'a [Expression], 31 | expr: &'a Expression, 32 | indent_level: usize, 33 | } 34 | 35 | impl<'a> ExpressionDebug<'a> { 36 | pub fn add_indent(mut self, level: usize) -> Self { 37 | self.indent_level += level; 38 | self 39 | } 40 | } 41 | 42 | impl std::fmt::Debug for ExpressionDebug<'_> { 43 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 44 | match &self.expr { 45 | Expression::Integer(i) => write!(f, "{}", i), 46 | Expression::Bool(b) => write!(f, "{}", b), 47 | Expression::Struct(s, members) => { 48 | writeln!(f, "{} {{", s)?; 49 | for (ident, value) in members.iter() { 50 | writeln!( 51 | f, 52 | "{: write!(f, "Unit"), 62 | Expression::Function(path) => write!(f, "{}", path), 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /repl/src/hir_engine/mod.rs: -------------------------------------------------------------------------------- 1 | mod expr; 2 | mod symbol_table; 3 | 4 | use hir::{ 5 | visit::Visitor, BinOp, Block, Expression, ExpressionKind, Identifier, Item, ItemKind, Local, Path, Statement, 6 | StatementKind, UnaryOp, 7 | }; 8 | use std::{ 9 | collections::HashMap, 10 | fmt::{self, Debug, Formatter}, 11 | }; 12 | use symbol_table::SymbolTable; 13 | use typecheck::{Context, TypeEngine, TypeError, TypeId, TypeInfo}; 14 | 15 | pub enum HirEngineError { 16 | NotMutable(Identifier), 17 | RecursionLimitReached, 18 | TypeError(Box, Box), 19 | UnknownImport(Path), 20 | UnknownIdentifier(Identifier), 21 | } 22 | 23 | impl Debug for HirEngineError { 24 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 25 | match self { 26 | HirEngineError::NotMutable(ident) => write!(f, "Local `{}` was not declared mutable", ident), 27 | HirEngineError::RecursionLimitReached => write!(f, "Reached recursion limit while evaluating expression"), 28 | HirEngineError::TypeError(e, engine) => write!(f, "{:?}", e.debug(engine)), 29 | HirEngineError::UnknownImport(path) => write!(f, "UnknownImport({})", path), 30 | HirEngineError::UnknownIdentifier(ident) => write!(f, "UnknownIdentifier({})", ident), 31 | } 32 | } 33 | } 34 | 35 | pub struct HirEngine { 36 | type_engine: TypeEngine, 37 | symbol_table: SymbolTable, 38 | current_path: Path, 39 | aliases: HashMap>, 40 | functions: HashMap, 41 | values: Vec, 42 | do_typechecking: bool, 43 | expr_eval_count: usize, 44 | expr_eval_limit: usize, 45 | unnamable_count: usize, 46 | } 47 | 48 | impl Default for HirEngine { 49 | fn default() -> Self { 50 | Self { 51 | type_engine: Default::default(), 52 | symbol_table: Default::default(), 53 | current_path: Default::default(), 54 | aliases: Default::default(), 55 | functions: Default::default(), 56 | values: Default::default(), 57 | do_typechecking: true, 58 | expr_eval_count: 0, 59 | expr_eval_limit: 100, 60 | unnamable_count: 0, 61 | } 62 | } 63 | } 64 | 65 | impl HirEngine { 66 | pub fn new() -> Self { 67 | Self::default() 68 | } 69 | 70 | pub fn type_engine(&self) -> &TypeEngine { 71 | &self.type_engine 72 | } 73 | 74 | pub fn expr_arena(&self) -> &[expr::Expression] { 75 | &self.values 76 | } 77 | 78 | pub fn new_expr(&mut self, expr: expr::Expression) -> expr::ExpressionId { 79 | let id = expr::ExpressionId(self.values.len()); 80 | self.values.push(expr); 81 | id 82 | } 83 | 84 | pub fn evaluate_item(&mut self, item: &Item) -> Result<(), HirEngineError> { 85 | self.type_engine.typecheck_item(&self.mk_context(), item).map_err(|e| self.mk_type_error(e))?; 86 | 87 | match &item.kind { 88 | // alreayd inserted in typechecker 89 | ItemKind::Struct(_) => Ok(()), 90 | ItemKind::Module(module) => { 91 | self.current_path = self.current_path.with_ident(module.name); 92 | 93 | let aliases = self.aliases.entry(self.current_path.clone()).or_default(); 94 | UseCollector::new(aliases).visit_module(&module); 95 | 96 | // TODO: clear stuff on errors 97 | for item in &module.items { 98 | self.evaluate_item(item)?; 99 | } 100 | 101 | self.current_path.pop(); 102 | 103 | Ok(()) 104 | } 105 | ItemKind::Use(usage) => { 106 | let ctx = self.mk_context(); 107 | if self.type_engine.typeid_from_path(&ctx, &usage.path).is_none() { 108 | return Err(HirEngineError::UnknownImport(usage.path.clone())); 109 | } 110 | 111 | self.aliases 112 | .entry(self.current_path.clone()) 113 | .or_default() 114 | .insert(Path::from_identifier(usage.path.last()), usage.path.clone()); 115 | 116 | Ok(()) 117 | } 118 | ItemKind::Function(f) => { 119 | let ctx = self.mk_context(); 120 | let path = self.current_path.with_ident(f.name); 121 | let id = self.type_engine.typeid_from_path(&ctx, &path).unwrap(); 122 | self.functions.insert(path, (f.clone(), id, self.unnamable_count)); 123 | 124 | Ok(()) 125 | } 126 | } 127 | } 128 | 129 | pub fn evaluate_statement(&mut self, statement: &Statement) -> Result<(), HirEngineError> { 130 | match &statement.kind { 131 | StatementKind::Local(local) => self.evaluate_local(local), 132 | StatementKind::Expression(expr) => self.evaluate_expression(expr, None).map(drop), 133 | } 134 | } 135 | 136 | pub fn evaluate_local(&mut self, local: &Local) -> Result<(), HirEngineError> { 137 | let ctx = self.mk_context(); 138 | let expected = self.type_engine.from_hir_type(&ctx, &local.ty).map_err(|e| self.mk_type_error(e))?; 139 | let expr = self.evaluate_expression(&local.value, Some(expected))?; 140 | let expr = self.new_expr(expr); 141 | 142 | self.symbol_table.new_binding(symbol_table::Local::new(local.name, expr, expected, local.mutable)); 143 | 144 | Ok(()) 145 | } 146 | 147 | pub fn evaluate_expression( 148 | &mut self, 149 | expr: &Expression, 150 | expected_type: Option, 151 | ) -> Result { 152 | let expected_type = expected_type.unwrap_or_else(|| self.type_engine.fresh_infer()); 153 | let ctx = self.mk_context(); 154 | 155 | self.expr_eval_count += 1; 156 | if self.expr_eval_count > self.expr_eval_limit { 157 | self.do_typechecking = true; 158 | return Err(HirEngineError::RecursionLimitReached); 159 | } 160 | 161 | if self.do_typechecking { 162 | self.type_engine.typecheck_expression(&ctx, expr, expected_type).map_err(|e| self.mk_type_error(e))?; 163 | } 164 | 165 | self.do_typechecking = false; 166 | 167 | let res = (|| { 168 | Ok(match &expr.kind { 169 | ExpressionKind::Block(block) => self.evaluate_block(block, Some(expected_type), None)?, 170 | ExpressionKind::BinaryOperation(lhs, op, rhs) => { 171 | let lhs = self.evaluate_expression(lhs, None)?; 172 | let rhs = self.evaluate_expression(rhs, None)?; 173 | 174 | match op { 175 | op if op.is_arith_op() => match (lhs, rhs) { 176 | (expr::Expression::Integer(lhs), expr::Expression::Integer(rhs)) => match op { 177 | BinOp::Add => expr::Expression::Integer(lhs + rhs), 178 | BinOp::Subtract => expr::Expression::Integer(lhs - rhs), 179 | BinOp::Multiply => expr::Expression::Integer(lhs * rhs), 180 | BinOp::Divide => expr::Expression::Integer(lhs / rhs), 181 | _ => unreachable!(), 182 | }, 183 | _ => todo!("actual eval stuff"), 184 | }, 185 | BinOp::Equal => expr::Expression::Bool(self.expressions_are_equal(&lhs, &rhs)), 186 | _ => unreachable!(), 187 | } 188 | } 189 | ExpressionKind::Boolean(b) => expr::Expression::Bool(*b), 190 | ExpressionKind::FnCall(lhs, args) => self.evaluate_fn_call(lhs, args, Some(expected_type))?, 191 | ExpressionKind::If(if_expr) => { 192 | for if_expr in &if_expr.ifs { 193 | let condition = self.evaluate_expression(&if_expr.condition, None)?; 194 | 195 | match condition { 196 | expr::Expression::Bool(true) => { 197 | return self.evaluate_block(&if_expr.body, Some(expected_type), None); 198 | } 199 | expr::Expression::Bool(false) => continue, 200 | _ => unreachable!(), 201 | } 202 | } 203 | 204 | self.evaluate_block(&if_expr.r#else, Some(expected_type), None)? 205 | } 206 | ExpressionKind::Integer(i) => expr::Expression::Integer(*i), 207 | ExpressionKind::Path(path) => match path.is_identifier() { 208 | Some(ident) => match self.symbol_table.resolve_binding(ident) { 209 | Some(local) => self.values[local.value.0].clone(), 210 | None => match self 211 | .aliases 212 | .get(&self.current_path) 213 | .and_then(|map| { 214 | let real_path = map.get(path)?; 215 | Some((real_path, self.functions.get(real_path)?)) 216 | }) 217 | .or_else(|| Some((path, self.functions.get(path)?))) 218 | { 219 | Some((real_path, _)) => expr::Expression::Function(real_path.clone()), 220 | None => return Err(HirEngineError::UnknownIdentifier(ident)), 221 | }, 222 | }, 223 | _ => match self.functions.get(path) { 224 | Some(_) => expr::Expression::Function(path.clone()), 225 | _ => todo!("path stuff"), 226 | }, 227 | }, 228 | ExpressionKind::Struct(s) => expr::Expression::Struct( 229 | s.name.clone(), 230 | s.members 231 | .iter() 232 | .map(|member| { 233 | let expr = self.evaluate_expression(&member.expression, None)?; 234 | let expr = self.new_expr(expr); 235 | 236 | Ok((member.name, expr)) 237 | }) 238 | .collect::>()?, 239 | ), 240 | ExpressionKind::FieldAccess(lhs, ident) => { 241 | let s = self.evaluate_expression(lhs, None)?; 242 | 243 | match s { 244 | expr::Expression::Struct(_, members) => self.values[members.get(ident).unwrap().0].clone(), 245 | _ => unreachable!(), 246 | } 247 | } 248 | ExpressionKind::Assignment(lhs, rhs) => { 249 | let rhs = self.evaluate_expression(rhs, None)?; 250 | *self.get_place(lhs)? = rhs; 251 | 252 | expr::Expression::Unit 253 | } 254 | ExpressionKind::Unit => expr::Expression::Unit, 255 | ExpressionKind::Unary(op, expr) => { 256 | let expr = self.evaluate_expression(expr, None)?; 257 | 258 | match (op, expr) { 259 | (UnaryOp::Minus, expr::Expression::Integer(i)) => expr::Expression::Integer(-i), 260 | (UnaryOp::Minus, expr::Expression::Bool(b)) => expr::Expression::Bool(!b), 261 | _ => unreachable!(), 262 | } 263 | } 264 | }) 265 | })(); 266 | 267 | self.do_typechecking = true; 268 | self.expr_eval_count -= 1; 269 | 270 | res 271 | } 272 | 273 | pub fn evaluate_fn_call( 274 | &mut self, 275 | callable: &Expression, 276 | args: &[Expression], 277 | expected_type: Option, 278 | ) -> Result { 279 | match self.evaluate_expression(callable, None)? { 280 | expr::Expression::Function(path) => { 281 | self.do_typechecking = false; 282 | let (f, fn_id, unnamable) = self.functions.get(&path).unwrap().clone(); 283 | let parameters = match self.type_engine.typeinfo(fn_id) { 284 | TypeInfo::Function { parameters, .. } => parameters.clone(), 285 | _ => unreachable!(), 286 | }; 287 | let iter = parameters.iter().zip(args.iter()); 288 | 289 | let mut new_symbols = SymbolTable::new(); 290 | let old_aliases = self.aliases.clone(); 291 | 292 | for (param, arg) in iter { 293 | let expr = self.evaluate_expression(arg, Some(param.1)); 294 | 295 | if expr.is_err() { 296 | self.do_typechecking = true; 297 | } 298 | 299 | let expr = self.new_expr(expr?); 300 | 301 | new_symbols.new_binding(symbol_table::Local::new(param.0, expr, param.1, false)); 302 | } 303 | 304 | let old_symtab = self.symbol_table.clone(); 305 | let old_path = self.current_path.clone(); 306 | self.symbol_table = new_symbols; 307 | 308 | self.current_path = self.current_path.with_ident(f.name); 309 | 310 | let res = self.evaluate_block(&f.body, expected_type, Some(unnamable)); 311 | 312 | self.current_path.pop(); 313 | self.aliases = old_aliases; 314 | self.symbol_table = old_symtab; 315 | self.do_typechecking = true; 316 | 317 | res 318 | } 319 | _ => unreachable!(), 320 | } 321 | } 322 | 323 | pub fn evaluate_block( 324 | &mut self, 325 | block: &Block, 326 | expected_type: Option, 327 | existing_unnamable: Option, 328 | ) -> Result { 329 | let old_symtab = self.symbol_table.clone(); 330 | let old_aliases = self.aliases.clone(); 331 | 332 | self.symbol_table = SymbolTable::with_parent(&old_symtab); 333 | self.current_path = self.current_path.with_ident(Identifier::new(&match existing_unnamable { 334 | Some(n) => n.to_string(), 335 | None => { 336 | let n = self.unnamable_count.to_string(); 337 | self.unnamable_count += 1; 338 | n 339 | } 340 | })); 341 | 342 | let res = (|| { 343 | for item in &block.items { 344 | self.evaluate_item(item)?; 345 | match &item.kind { 346 | ItemKind::Struct(s) => { 347 | self.aliases 348 | .entry(self.current_path.clone()) 349 | .or_default() 350 | .insert(Path::from_identifier(s.name), self.current_path.with_ident(s.name)); 351 | } 352 | ItemKind::Function(f) => { 353 | self.aliases 354 | .entry(self.current_path.clone()) 355 | .or_default() 356 | .insert(Path::from_identifier(f.name), self.current_path.with_ident(f.name)); 357 | } 358 | ItemKind::Module(m) => { 359 | self.aliases 360 | .entry(self.current_path.clone()) 361 | .or_default() 362 | .insert(Path::from_identifier(m.name), self.current_path.with_ident(m.name)); 363 | } 364 | ItemKind::Use(u) => { 365 | self.aliases 366 | .entry(self.current_path.clone()) 367 | .or_default() 368 | .insert(Path::from_identifier(u.path.last()), u.path.clone()); 369 | } 370 | } 371 | } 372 | 373 | for statement in &block.statements { 374 | self.evaluate_statement(statement)?; 375 | } 376 | 377 | self.evaluate_expression(&block.return_expr, expected_type) 378 | })(); 379 | 380 | self.current_path.pop(); 381 | self.symbol_table = old_symtab; 382 | self.aliases = old_aliases; 383 | 384 | res 385 | } 386 | 387 | fn expressions_are_equal(&self, lhs: &expr::Expression, rhs: &expr::Expression) -> bool { 388 | match (lhs, rhs) { 389 | (expr::Expression::Integer(lhs), expr::Expression::Integer(rhs)) => lhs == rhs, 390 | (expr::Expression::Bool(lhs), expr::Expression::Bool(rhs)) => lhs == rhs, 391 | (expr::Expression::Unit, expr::Expression::Unit) => true, 392 | (expr::Expression::Struct(_, members), expr::Expression::Struct(_, members2)) => { 393 | for (ident, expr) in members.iter() { 394 | let expr1 = &self.expr_arena()[expr.0]; 395 | let expr2 = &self.expr_arena()[members2.get(ident).unwrap().0]; 396 | if !self.expressions_are_equal(expr1, expr2) { 397 | return false; 398 | } 399 | } 400 | 401 | true 402 | } 403 | (expr::Expression::Function(p), expr::Expression::Function(p2)) => p == p2, 404 | _ => unreachable!(), 405 | } 406 | } 407 | 408 | fn get_place(&mut self, expr: &Expression) -> Result<&mut expr::Expression, HirEngineError> { 409 | match &expr.kind { 410 | ExpressionKind::FieldAccess(lhs, field) => { 411 | let lhs = self.get_place(lhs)?; 412 | 413 | match lhs { 414 | expr::Expression::Struct(_, members) => { 415 | let id = members.get_mut(field).unwrap().0; 416 | Ok(&mut self.values[id]) 417 | } 418 | _ => unreachable!(), 419 | } 420 | } 421 | ExpressionKind::Path(path) => match path.is_identifier() { 422 | Some(ident) => match self.symbol_table.resolve_binding(ident) { 423 | Some(e) if e.mutable => Ok(&mut self.values[e.value.0]), 424 | Some(_) => Err(HirEngineError::NotMutable(ident)), 425 | None => unreachable!(), 426 | }, 427 | None => unreachable!(), 428 | }, 429 | _ => unreachable!(), 430 | } 431 | } 432 | 433 | pub fn typeinfo(&self, path: &Path) -> Option { 434 | let ctx = self.mk_context(); 435 | let id = self.type_engine.typeid_from_path(&ctx, path)?; 436 | Some(self.type_engine.typeinfo(id).clone()) 437 | } 438 | 439 | pub fn varinfo(&self, ident: Identifier) -> Option { 440 | self.symbol_table.resolve_binding(ident) 441 | } 442 | 443 | fn mk_type_error(&self, error: TypeError) -> HirEngineError { 444 | HirEngineError::TypeError(Box::new(error), Box::new(self.type_engine.clone())) 445 | } 446 | 447 | fn mk_context(&self) -> Context<'static> { 448 | Context { 449 | aliases: self.aliases.get(&self.current_path).cloned().unwrap_or_default(), 450 | bindings: self 451 | .symbol_table 452 | .bindings() 453 | .map(|local| (local.name, typecheck::BindingInfo { mutable: local.mutable, typeid: local.ty })) 454 | .collect(), 455 | parent: None, 456 | } 457 | } 458 | } 459 | -------------------------------------------------------------------------------- /repl/src/hir_engine/symbol_table.rs: -------------------------------------------------------------------------------- 1 | use hir::Identifier; 2 | use std::collections::HashMap; 3 | use typecheck::{TypeEngine, TypeId}; 4 | 5 | #[derive(Debug, Clone, Default)] 6 | pub struct SymbolTable { 7 | parent: Option>, 8 | symbols: HashMap, 9 | } 10 | 11 | impl SymbolTable { 12 | pub fn new() -> Self { 13 | Self::default() 14 | } 15 | 16 | pub fn with_parent(parent: &SymbolTable) -> SymbolTable { 17 | Self { parent: Some(Box::new(parent.clone())), symbols: HashMap::new() } 18 | } 19 | 20 | pub fn resolve_binding(&self, ident: Identifier) -> Option { 21 | match self.symbols.get(&ident) { 22 | Some(local) => Some(*local), 23 | None => match &self.parent { 24 | Some(parent) => parent.resolve_binding(ident), 25 | None => None, 26 | }, 27 | } 28 | } 29 | 30 | pub fn new_binding(&mut self, local: Local) { 31 | self.symbols.insert(local.name, local); 32 | } 33 | 34 | pub fn bindings(&self) -> Box + '_> { 35 | match &self.parent { 36 | Some(parent) => Box::new(parent.bindings().chain(self.symbols.values().copied())), 37 | None => Box::new(self.symbols.values().copied()), 38 | } 39 | } 40 | } 41 | 42 | #[derive(Debug, Clone, Copy)] 43 | pub struct Local { 44 | pub name: Identifier, 45 | pub value: super::expr::ExpressionId, 46 | pub ty: TypeId, 47 | pub mutable: bool, 48 | } 49 | 50 | impl Local { 51 | pub fn new(name: Identifier, value: super::expr::ExpressionId, ty: TypeId, mutable: bool) -> Self { 52 | Self { name, value, ty, mutable } 53 | } 54 | 55 | pub fn debug<'a>(&'a self, arena: &'a [super::expr::Expression], engine: &'a TypeEngine) -> LocalDebug<'a> { 56 | LocalDebug { local: self, arena, engine } 57 | } 58 | } 59 | 60 | pub struct LocalDebug<'a> { 61 | arena: &'a [super::expr::Expression], 62 | local: &'a Local, 63 | engine: &'a TypeEngine, 64 | } 65 | 66 | impl std::fmt::Debug for LocalDebug<'_> { 67 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 68 | writeln!(f, "Binding `{}`:", self.local.name)?; 69 | writeln!(f, " mutable? {}", self.local.mutable)?; 70 | writeln!(f, " type: {:?}", self.engine.typeinfo(self.local.ty).debug(self.engine).add_indent(3))?; 71 | write!(f, " value: {:?}", self.arena[self.local.value.0].debug(self.arena).add_indent(3)) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /repl/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::match_bool)] 2 | 3 | mod hir_engine; 4 | mod repl; 5 | 6 | use codespan_reporting::diagnostic::{Diagnostic, Label}; 7 | use codespan_reporting::files::SimpleFile; 8 | use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; 9 | use parser::ParseError; 10 | use repl::{Repl, ReplError, ReplErrorKind}; 11 | use structopt::StructOpt; 12 | 13 | #[derive(StructOpt)] 14 | struct Arguments { 15 | #[structopt(long = "clear-screen", short = "c")] 16 | clear_screen: bool, 17 | } 18 | 19 | fn main() { 20 | let args = Arguments::from_args(); 21 | 22 | let mut repl = Repl::new(); 23 | 24 | if args.clear_screen { 25 | println!("\x1B[2J\x1B[H"); 26 | } 27 | 28 | loop { 29 | match repl.run() { 30 | Ok(Some(text)) => println!("{}", text), 31 | Ok(None) => {} 32 | Err(e) => print_err(e), 33 | } 34 | } 35 | } 36 | 37 | fn print_err(e: ReplError) { 38 | match e { 39 | ReplError { kind: ReplErrorKind::MultiExpression, .. } => { 40 | println!("Error: can't evaluate more than one expression at one time") 41 | } 42 | ReplError { kind: ReplErrorKind::Readline(e), .. } => { 43 | println!("Error reading input: {}", e); 44 | } 45 | ReplError { source, kind: ReplErrorKind::ParseError(e) } => match e { 46 | ParseError::BadBinOp => { 47 | println!("Error: Multiple binary operations in the same expression require parenthesis") 48 | } 49 | ParseError::Eof => println!("Error: got eof"), 50 | ParseError::BadToken { got, expected } => { 51 | let file = SimpleFile::new("", source); 52 | let message = match &expected[..] { 53 | [single] => format!("Expected {}, got {}", single, got), 54 | _ => format!("Expected one of {}, got {}", expected.join(", "), got), 55 | }; 56 | let diagnostic = Diagnostic::error() 57 | .with_message("Unexpected token") 58 | .with_labels(vec![Label::primary((), got.span)]) 59 | .with_notes(vec![message]); 60 | let writer = StandardStream::stderr(ColorChoice::Always); 61 | let config = codespan_reporting::term::Config::default(); 62 | 63 | codespan_reporting::term::emit(&mut writer.lock(), &config, &file, &diagnostic).unwrap(); 64 | } 65 | }, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /repl/src/repl.rs: -------------------------------------------------------------------------------- 1 | use crate::hir_engine::HirEngine; 2 | use ast::AstNode; 3 | use parser::{ParseError, Parser}; 4 | use rustyline::{error::ReadlineError, hint::HistoryHinter, CompletionType, Config, Editor}; 5 | 6 | const HELP_MSG: &str = r"Commands: 7 | .help Displays this help text 8 | .ast Display the following code as its AST form 9 | .clear Clear the current screen 10 | .varinfo Display the variable information associated with the given identifier 11 | .typeinfo Display information about the type associated with the given path 12 | .loadfile Load the given file path as a module"; 13 | 14 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 15 | enum EvalMode { 16 | Ast, 17 | Eval, 18 | Hir, 19 | } 20 | 21 | pub enum LineReturn { 22 | Done(String), 23 | Empty, 24 | Error(rustyline::error::ReadlineError), 25 | } 26 | 27 | #[derive(Clone, Copy, Debug)] 28 | pub enum PromptMode { 29 | Continuing, 30 | Fresh, 31 | } 32 | 33 | impl PromptMode { 34 | fn as_str(self) -> &'static str { 35 | match self { 36 | PromptMode::Fresh => ">> ", 37 | PromptMode::Continuing => ".. ", 38 | } 39 | } 40 | } 41 | 42 | pub struct ReplError { 43 | pub source: String, 44 | pub kind: ReplErrorKind, 45 | } 46 | 47 | impl ReplError { 48 | pub fn new(source: String, kind: ReplErrorKind) -> Self { 49 | Self { source, kind } 50 | } 51 | } 52 | 53 | pub enum ReplErrorKind { 54 | ParseError(ParseError), 55 | MultiExpression, 56 | Readline(rustyline::error::ReadlineError), 57 | } 58 | 59 | pub struct Repl { 60 | code: String, 61 | editor: Editor, 62 | prompt_mode: PromptMode, 63 | hir_engine: HirEngine, 64 | } 65 | 66 | impl Repl { 67 | pub fn new() -> Self { 68 | let mut editor = Editor::with_config(Config::builder().completion_type(CompletionType::Circular).build()); 69 | editor.set_helper(Some(Helper::new())); 70 | let _ = editor.load_history("repl_history.bismite"); 71 | 72 | Self { code: String::new(), editor, prompt_mode: PromptMode::Fresh, hir_engine: HirEngine::new() } 73 | } 74 | pub fn run(&mut self) -> Result, ReplError> { 75 | let line = match self.read_line(self.prompt_mode) { 76 | LineReturn::Done(s) => s, 77 | LineReturn::Empty => { 78 | let _ = self.editor.save_history("repl_history.bismite"); 79 | std::process::exit(0) 80 | } 81 | LineReturn::Error(e) => return Err(ReplError::new(String::new(), ReplErrorKind::Readline(e))), 82 | }; 83 | 84 | if self.eval_repl_command(&line) { 85 | self.editor.add_history_entry(&*line); 86 | return Ok(None); 87 | } 88 | 89 | self.code += &line; 90 | self.code += "\n"; 91 | 92 | let old_code = self.code.clone(); 93 | let eval_mode = self.eval_mode(); 94 | 95 | let mut parser = Parser::new(&self.code); 96 | let mut nodes = Vec::new(); 97 | let mut hit_expr = false; 98 | 99 | loop { 100 | let node = parser.guess(); 101 | 102 | if let Err(ParseError::Eof) = &node { 103 | self.code = old_code; 104 | self.prompt_mode = PromptMode::Continuing; 105 | return Ok(None); 106 | } 107 | 108 | if hit_expr && matches!(&node, Ok(Some(AstNode::Expression(_)))) { 109 | let code = self.code.clone(); 110 | self.reset(); 111 | return Err(ReplError::new(code, ReplErrorKind::MultiExpression)); 112 | } 113 | 114 | match node { 115 | Ok(Some(node)) => { 116 | hit_expr = matches!(&node, AstNode::Expression(_)); 117 | nodes.push(node); 118 | } 119 | Ok(None) => break, 120 | Err(e) => { 121 | let code = self.code.clone(); 122 | self.reset(); 123 | return Err(ReplError::new(code, ReplErrorKind::ParseError(e))); 124 | } 125 | } 126 | } 127 | 128 | self.editor.add_history_entry(self.code.trim()); 129 | 130 | let mut eval_output = None; 131 | for node in nodes { 132 | match eval_mode { 133 | EvalMode::Eval => match node { 134 | AstNode::Item(item) => match self.hir_engine.evaluate_item(&hir::Item::convert(&item)) { 135 | Ok(_) => eval_output = None, 136 | Err(e) => eval_output = Some(format!("{:?}", e)), 137 | }, 138 | AstNode::Expression(e) => { 139 | match self.hir_engine.evaluate_expression(&hir::Expression::convert(&e), None) { 140 | Ok(e) if !e.is_unit() => { 141 | eval_output = Some(format!("{:?}", e.debug(self.hir_engine.expr_arena()))) 142 | } 143 | Ok(_) => eval_output = None, 144 | Err(e) => eval_output = Some(format!("{:?}", e)), 145 | } 146 | } 147 | AstNode::Statement(s) => match self.hir_engine.evaluate_statement(&hir::Statement::convert(&s)) { 148 | Ok(_) => eval_output = None, 149 | Err(e) => eval_output = Some(format!("{:?}", e)), 150 | }, 151 | }, 152 | EvalMode::Ast => eval_output = Some(format!("{:#?}", node)), 153 | EvalMode::Hir => { 154 | eval_output = Some(format!("{:#?}", { 155 | match &node { 156 | AstNode::Expression(_) => todo!(), 157 | AstNode::Item(i) => hir::Item::convert(i), 158 | AstNode::Statement(_) => todo!(), 159 | } 160 | })) 161 | } 162 | } 163 | } 164 | self.reset(); 165 | 166 | Ok(eval_output) 167 | } 168 | 169 | fn eval_repl_command(&mut self, s: &str) -> bool { 170 | let command = match s.split(' ').next() { 171 | Some(cmd) => cmd, 172 | None => return false, 173 | }; 174 | 175 | match command { 176 | ".clear" => { 177 | println!("\x1B[2J\x1B[H"); 178 | self.code.clear(); 179 | } 180 | ".help" => { 181 | println!("{}", HELP_MSG); 182 | } 183 | ".varinfo" => { 184 | let ident = match s.split(' ').nth(1) { 185 | Some(ident) => ident, 186 | None => { 187 | println!("Must provide an identifier to .varinfo, e.g. .varinfo my_variable"); 188 | return true; 189 | } 190 | }; 191 | let valid_ident = Parser::new(ident).identifier(); 192 | 193 | match valid_ident { 194 | Ok(valid_ident) => { 195 | let valid_ident = hir::Identifier::convert(&valid_ident); 196 | match self.hir_engine.varinfo(valid_ident) { 197 | Some(var_info) => println!( 198 | "{:?}", 199 | var_info.debug(self.hir_engine.expr_arena(), self.hir_engine.type_engine()) 200 | ), 201 | None => { 202 | println!("Variable with identifier `{}` not found in scope", valid_ident); 203 | return true; 204 | } 205 | } 206 | } 207 | Err(_) => { 208 | println!("Invalid identifier"); 209 | return true; 210 | } 211 | } 212 | } 213 | ".typeinfo" => { 214 | let path = match s.split(' ').nth(1) { 215 | Some(path) => path, 216 | None => { 217 | println!("Must provide a path to .typeinfo, e.g. .typeinfo MyType"); 218 | return true; 219 | } 220 | }; 221 | let path = match Parser::new(path).path() { 222 | Ok(path) => hir::Path::convert(&path), 223 | Err(e) => { 224 | println!("Invalid path: {:?}", e); 225 | return true; 226 | } 227 | }; 228 | 229 | match self.hir_engine.typeinfo(&path) { 230 | Some(type_info) => println!("{:?}", type_info.debug(&self.hir_engine.type_engine())), 231 | None => println!("Type with path `{}` not found in scope", path), 232 | } 233 | } 234 | ".loadfile" => { 235 | let file_path = match s.split(' ').nth(1) { 236 | Some(path) => path, 237 | None => { 238 | println!("Must provide a file path to .loadfile"); 239 | return true; 240 | } 241 | }; 242 | 243 | let file_contents = match std::fs::read_to_string(&file_path) { 244 | Ok(contents) => contents, 245 | Err(e) => { 246 | println!("Error reading file: {}", e); 247 | return true; 248 | } 249 | }; 250 | 251 | let parsed = match Parser::new(&file_contents).module(true) { 252 | Ok(mut module) => { 253 | module.name = ast::Identifier { 254 | value: std::path::Path::new(&file_path).file_stem().unwrap().to_string_lossy().to_string(), 255 | span: codespan::Span::new(0, 0), 256 | }; 257 | 258 | module 259 | } 260 | Err(e) => { 261 | println!("Error parsing file: {:?}", e); 262 | return true; 263 | } 264 | }; 265 | 266 | let module = hir::Module::convert(&parsed); 267 | let span = module.span; 268 | if let Err(e) = self.hir_engine.evaluate_item(&hir::Item { kind: hir::ItemKind::Module(module), span }) 269 | { 270 | println!("Error processing module: {:?}", e); 271 | } 272 | } 273 | _ => return false, 274 | } 275 | 276 | true 277 | } 278 | 279 | fn eval_mode(&mut self) -> EvalMode { 280 | let cmd = match self.code.split_whitespace().next() { 281 | Some(cmd) => cmd, 282 | None => return EvalMode::Eval, 283 | }; 284 | 285 | match cmd.trim() { 286 | ".ast" => { 287 | self.code = self.code.trim_start_matches(".ast").to_string(); 288 | EvalMode::Ast 289 | } 290 | ".hir" => { 291 | self.code = self.code.trim_start_matches(".hir").to_string(); 292 | EvalMode::Hir 293 | } 294 | _ => EvalMode::Eval, 295 | } 296 | } 297 | 298 | fn reset(&mut self) { 299 | self.code.clear(); 300 | self.prompt_mode = PromptMode::Fresh; 301 | } 302 | 303 | fn read_line(&mut self, mode: PromptMode) -> LineReturn { 304 | match self.editor.readline(mode.as_str()) { 305 | Ok(s) => LineReturn::Done(s), 306 | Err(ReadlineError::Eof) | Err(ReadlineError::Interrupted) => LineReturn::Empty, 307 | Err(e) => LineReturn::Error(e), 308 | } 309 | } 310 | } 311 | 312 | struct Helper { 313 | hinter: HistoryHinter, 314 | } 315 | 316 | impl Helper { 317 | fn new() -> Self { 318 | Self { hinter: HistoryHinter {} } 319 | } 320 | } 321 | 322 | impl rustyline::Helper for Helper {} 323 | impl rustyline::highlight::Highlighter for Helper { 324 | fn highlight_hint<'h>(&self, hint: &'h str) -> std::borrow::Cow<'h, str> { 325 | format!("\x1B[31;32m{}\x1B[0m", hint).into() 326 | } 327 | } 328 | impl rustyline::validate::Validator for Helper {} 329 | impl rustyline::completion::Completer for Helper { 330 | type Candidate = rustyline::completion::Pair; 331 | } 332 | impl rustyline::hint::Hinter for Helper { 333 | fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option { 334 | self.hinter.hint(line, pos, ctx) 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_small_heuristics = "max" 2 | max_width = 120 -------------------------------------------------------------------------------- /strcache/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "strcache" 3 | version = "0.1.0" 4 | authors = ["Wesley Norris "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /strcache/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cell::UnsafeCell, 3 | sync::atomic::{AtomicBool, AtomicUsize}, 4 | }; 5 | 6 | #[derive(Debug, Clone, Copy)] 7 | pub struct Index { 8 | index: usize, 9 | } 10 | 11 | #[derive(Debug)] 12 | pub struct StrCache { 13 | backing: UnsafeCell>, 14 | readers: AtomicUsize, 15 | write_locked: AtomicBool, 16 | } 17 | 18 | impl StrCache { 19 | pub const fn new() -> Self { 20 | Self { 21 | backing: UnsafeCell::new(Vec::new()), 22 | readers: AtomicUsize::new(0), 23 | write_locked: AtomicBool::new(false), 24 | } 25 | } 26 | } 27 | 28 | unsafe impl Sync for StrCache {} 29 | -------------------------------------------------------------------------------- /test.bm: -------------------------------------------------------------------------------- 1 | fn fib(n: Int) -> Int { 2 | if n == 0 { 3 | 0 4 | } else if n == 1 { 5 | 1 6 | } else { 7 | fib(n - 1) + fib(n - 2) 8 | } 9 | } -------------------------------------------------------------------------------- /typecheck/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "typecheck" 3 | version = "0.1.0" 4 | authors = ["Wesley Norris "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | hir = { path = "../hir" } -------------------------------------------------------------------------------- /typecheck/src/lib.rs: -------------------------------------------------------------------------------- 1 | use hir::{ 2 | BinOp, Block, Expression, ExpressionKind, Function, Identifier, Item, ItemKind, Path, Statement, StatementKind, 3 | Struct, StructExpr, Type, TypeKind, UnaryOp, 4 | }; 5 | use std::{ 6 | collections::HashMap, 7 | fmt::{self, Debug, Formatter}, 8 | }; 9 | 10 | pub type Result = std::result::Result; 11 | pub type TypeId = usize; 12 | 13 | #[derive(Debug, Copy, Clone)] 14 | pub struct BindingInfo { 15 | pub mutable: bool, 16 | pub typeid: TypeId, 17 | } 18 | 19 | #[derive(Default)] 20 | pub struct Context<'a> { 21 | pub aliases: HashMap, 22 | pub bindings: HashMap, 23 | pub parent: Option<&'a Context<'a>>, 24 | } 25 | 26 | impl<'a> Context<'a> { 27 | pub fn new() -> Context<'static> { 28 | Context { aliases: HashMap::new(), bindings: HashMap::new(), parent: None } 29 | } 30 | 31 | pub fn new_child<'b: 'a>(&'b self) -> Context<'a> { 32 | Self { aliases: HashMap::new(), bindings: HashMap::new(), parent: Some(self) } 33 | } 34 | 35 | pub fn new_path_alias(&mut self, original: Path, alias: Path) { 36 | self.aliases.insert(original, alias); 37 | } 38 | 39 | pub fn new_binding(&mut self, ident: Identifier, binding: BindingInfo) { 40 | self.bindings.insert(ident, binding); 41 | } 42 | 43 | pub fn resolve_path_alias(&self, path: &Path) -> Option<&Path> { 44 | match self.aliases.get(path) { 45 | Some(path) => Some(path), 46 | None => match &self.parent { 47 | Some(parent) => parent.resolve_path_alias(path), 48 | None => None, 49 | }, 50 | } 51 | } 52 | 53 | pub fn resolve_binding(&self, ident: Identifier) -> Option { 54 | match self.bindings.get(&ident) { 55 | Some(binding) => Some(*binding), 56 | None => match &self.parent { 57 | Some(parent) => parent.resolve_binding(ident), 58 | None => None, 59 | }, 60 | } 61 | } 62 | } 63 | 64 | pub enum TypeError { 65 | CannotInferType, 66 | MismatchedTypes { wanted: TypeInfo, have: TypeInfo }, 67 | NoField(TypeInfo, Identifier), 68 | NotCallable(TypeInfo), 69 | NotEnoughArgs, 70 | NotMutable(Identifier), 71 | NotValidRhs, 72 | TooManyArgs, 73 | UnknownBinOp { lhs: TypeInfo, op: BinOp, rhs: TypeInfo }, 74 | UnknownIdentifier(Identifier), 75 | UnknownType(Path), 76 | UnknownUnaryOp { op: UnaryOp, info: TypeInfo }, 77 | } 78 | 79 | impl TypeError { 80 | pub fn debug<'a>(&'a self, engine: &'a TypeEngine) -> TypeErrorDebug<'a> { 81 | TypeErrorDebug { error: self, engine } 82 | } 83 | } 84 | 85 | pub struct TypeErrorDebug<'a> { 86 | error: &'a TypeError, 87 | engine: &'a TypeEngine, 88 | } 89 | 90 | impl Debug for TypeErrorDebug<'_> { 91 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 92 | match &self.error { 93 | TypeError::NoField(info, field) => write!(f, "No field `{}` on type {}", field, info.name(self.engine)), 94 | TypeError::UnknownType(id) => write!(f, "Unknown type: `{}`", id.to_string()), 95 | TypeError::MismatchedTypes { wanted, have } => write!( 96 | f, 97 | "Type mismatch: expected `{}`, but found `{}`", 98 | wanted.name(self.engine), 99 | have.name(self.engine) 100 | ), 101 | TypeError::UnknownBinOp { lhs, op, rhs } => { 102 | write!(f, "No implmentation for `{}` {} `{}`", lhs.name(self.engine), op, rhs.name(self.engine)) 103 | } 104 | TypeError::UnknownIdentifier(ident) => write!(f, "(TypeError) Unknown identifier `{}`", ident), 105 | TypeError::NotValidRhs => write!(f, "Not a valid right hand side expression"), 106 | TypeError::NotCallable(info) => write!(f, "Type `{}` is not a function", info.name(self.engine)), 107 | TypeError::TooManyArgs => write!(f, "Too many arguments "), 108 | TypeError::NotEnoughArgs => write!(f, "Too few arguments "), 109 | TypeError::NotMutable(ident) => write!(f, "Local `{}` was not declared as mutable", ident), 110 | TypeError::UnknownUnaryOp { op, info } => { 111 | write!(f, "No implmentation for {}(`{}`)", op, info.name(self.engine)) 112 | } 113 | TypeError::CannotInferType => write!(f, "Cannot infer type"), 114 | } 115 | } 116 | } 117 | 118 | #[derive(Debug, Clone)] 119 | pub struct TypeEngine { 120 | types: Vec, 121 | name_map: HashMap, 122 | current_path: Path, 123 | unnamable_count: usize, 124 | } 125 | 126 | impl TypeEngine { 127 | pub fn new() -> Self { 128 | Self::default() 129 | } 130 | 131 | pub fn typeinfo(&self, id: TypeId) -> &TypeInfo { 132 | match &self.types[id] { 133 | TypeInfo::Ref(r) => self.typeinfo(*r), 134 | info => info, 135 | } 136 | } 137 | 138 | pub fn unify(&mut self, ctx: &Context<'_>, want: TypeId, have: TypeId) -> Result { 139 | match (self.types[want].clone(), self.types[have].clone()) { 140 | (TypeInfo::Bool, TypeInfo::Bool) => Ok(self.bool()), 141 | (TypeInfo::Integer, TypeInfo::Integer) => Ok(self.integer()), 142 | (TypeInfo::Unit, TypeInfo::Unit) => Ok(self.unit()), 143 | ( 144 | TypeInfo::Struct { full_path: full_path1, members: members1, .. }, 145 | TypeInfo::Struct { full_path: full_path2, members: members2, .. }, 146 | ) => { 147 | let full_path1 = ctx.resolve_path_alias(&full_path1).unwrap_or(&full_path1); 148 | let full_path2 = ctx.resolve_path_alias(&full_path2).unwrap_or(&full_path2); 149 | 150 | if full_path1 != full_path2 { 151 | return Err(TypeError::MismatchedTypes { 152 | wanted: self.types[want].clone(), 153 | have: self.types[have].clone(), 154 | }); 155 | } 156 | 157 | for (i, &a) in members1.iter() { 158 | let b = members2[i]; 159 | 160 | self.unify(ctx, a, b)?; 161 | } 162 | 163 | Ok(want) 164 | } 165 | ( 166 | TypeInfo::Function { parameters: parameters1, return_type: return_type1 }, 167 | TypeInfo::Function { parameters: parameters2, return_type: return_type2 }, 168 | ) => { 169 | match parameters1.len().cmp(¶meters2.len()) { 170 | std::cmp::Ordering::Less => return Err(TypeError::TooManyArgs), 171 | std::cmp::Ordering::Greater => return Err(TypeError::NotEnoughArgs), 172 | _ => {} 173 | } 174 | 175 | self.unify(ctx, return_type1, return_type2)?; 176 | 177 | Ok(want) 178 | } 179 | (TypeInfo::Infer, TypeInfo::Infer) => Err(TypeError::CannotInferType), 180 | (TypeInfo::Infer, _) => { 181 | self.types[want] = TypeInfo::Ref(have); 182 | Ok(have) 183 | } 184 | (_, TypeInfo::Infer) => { 185 | self.types[have] = TypeInfo::Ref(want); 186 | Ok(want) 187 | } 188 | (TypeInfo::Ref(a), _) => self.unify(ctx, a, have), 189 | (_, TypeInfo::Ref(b)) => self.unify(ctx, want, b), 190 | (a, b) => Err(TypeError::MismatchedTypes { wanted: a, have: b }), 191 | } 192 | } 193 | 194 | pub fn fresh_infer(&mut self) -> TypeId { 195 | self.types.push(TypeInfo::Infer); 196 | self.types.len() - 1 197 | } 198 | 199 | pub fn typecheck_expression(&mut self, ctx: &Context<'_>, expr: &Expression, expected: TypeId) -> Result { 200 | match &expr.kind { 201 | ExpressionKind::Integer(_) => self.unify(ctx, expected, self.integer()), 202 | ExpressionKind::Boolean(_) => self.unify(ctx, expected, self.bool()), 203 | ExpressionKind::Block(block) => self.typecheck_block(ctx, block, expected), 204 | ExpressionKind::Unit => self.unify(ctx, expected, self.unit()), 205 | ExpressionKind::FnCall(lhs, args) => { 206 | let infer = self.fresh_infer(); 207 | let fn_id = self.typecheck_expression(ctx, lhs, infer)?; 208 | 209 | match self.typeinfo(fn_id).clone() { 210 | TypeInfo::Function { parameters, return_type } => { 211 | match parameters.len().cmp(&args.len()) { 212 | std::cmp::Ordering::Less => return Err(TypeError::TooManyArgs), 213 | std::cmp::Ordering::Greater => return Err(TypeError::NotEnoughArgs), 214 | _ => {} 215 | } 216 | 217 | for ((_, id), arg) in parameters.iter().zip(args.iter()) { 218 | self.typecheck_expression(ctx, arg, *id)?; 219 | } 220 | 221 | self.unify(ctx, expected, return_type) 222 | } 223 | _ => Err(TypeError::NotCallable(self.typeinfo(fn_id).clone())), 224 | } 225 | } 226 | ExpressionKind::Struct(struct_expr) => { 227 | let have = self.gen_struct_typeinfo(ctx, struct_expr)?; 228 | let want = match self.resolve_two_way(ctx, &struct_expr.name) { 229 | Some(id) => id, 230 | None => return Err(TypeError::UnknownType(struct_expr.name.clone())), 231 | }; 232 | 233 | self.unify(ctx, want, have)?; 234 | self.unify(ctx, want, expected) 235 | } 236 | ExpressionKind::Path(path) => match path.is_identifier() { 237 | Some(ident) => match ctx.resolve_binding(ident) { 238 | Some(binding) => Ok(binding.typeid), 239 | None => match self.typeid_from_path(ctx, path) { 240 | Some(id) => match self.typeinfo(id) { 241 | TypeInfo::Function { .. } => Ok(self.unify(ctx, expected, id)?), 242 | info => Err(TypeError::NotCallable(info.clone())), 243 | }, 244 | None => Err(TypeError::UnknownIdentifier(ident)), 245 | }, 246 | }, 247 | None => self.name_map.get(&path).copied().ok_or_else(|| TypeError::UnknownType(path.clone())), 248 | }, 249 | ExpressionKind::FieldAccess(lhs, ident) => { 250 | let infer = self.fresh_infer(); 251 | let lhs_id = self.typecheck_expression(ctx, lhs, infer)?; 252 | let type_info = self.typeinfo(lhs_id); 253 | 254 | match type_info { 255 | TypeInfo::Struct { members, .. } if members.get(ident).is_some() => { 256 | Ok(members.get(ident).copied().unwrap()) 257 | } 258 | _ => Err(TypeError::NoField(type_info.clone(), *ident)), 259 | } 260 | } 261 | ExpressionKind::Assignment(lhs, rhs) => { 262 | let lhs_id = match &lhs.kind { 263 | ExpressionKind::FieldAccess(_, _) => { 264 | let infer = self.fresh_infer(); 265 | self.typecheck_expression(ctx, lhs, infer)? 266 | } 267 | ExpressionKind::Path(path) => match path.is_identifier() { 268 | Some(ident) => match ctx.resolve_binding(ident) { 269 | Some(binding) if binding.mutable => binding.typeid, 270 | Some(_) => return Err(TypeError::NotMutable(ident)), 271 | None => return Err(TypeError::UnknownIdentifier(ident)), 272 | }, 273 | None => return Err(TypeError::NotValidRhs), 274 | }, 275 | _ => return Err(TypeError::NotValidRhs), 276 | }; 277 | 278 | let infer = self.fresh_infer(); 279 | let rhs_id = self.typecheck_expression(ctx, rhs, infer)?; 280 | 281 | self.unify(ctx, lhs_id, rhs_id)?; 282 | 283 | Ok(self.unit()) 284 | } 285 | ExpressionKind::BinaryOperation(original_lhs, op, original_rhs) => { 286 | let op = *op; 287 | 288 | let infer = self.fresh_infer(); 289 | let lhs_id = self.typecheck_expression(ctx, original_lhs, infer)?; 290 | 291 | let infer = self.fresh_infer(); 292 | let rhs_id = self.typecheck_expression(ctx, original_rhs, infer)?; 293 | 294 | match (self.typeinfo(lhs_id), self.typeinfo(rhs_id)) { 295 | (TypeInfo::Integer, TypeInfo::Integer) if op.is_arith_op() => { 296 | Ok(self.unify(ctx, expected, self.integer())?) 297 | } 298 | (TypeInfo::Bool, TypeInfo::Bool) if op.is_logic_op() => { 299 | Ok(self.unify(ctx, expected, self.bool())?) 300 | } 301 | (_, _) if op == BinOp::Equal => { 302 | self.unify(ctx, lhs_id, rhs_id)?; 303 | Ok(self.bool()) 304 | } 305 | (_, _) => Err(TypeError::UnknownBinOp { 306 | lhs: self.types[lhs_id].clone(), 307 | op, 308 | rhs: self.types[rhs_id].clone(), 309 | }), 310 | } 311 | } 312 | ExpressionKind::If(if_expr) => { 313 | let expected = { 314 | let infer = self.fresh_infer(); 315 | // FIXME: probably less confusing to not early return 316 | // here and check each `if` and then `else` individually 317 | // to report their types 318 | let typeid = self.typecheck_block(ctx, &if_expr.r#else, infer)?; 319 | self.unify(ctx, expected, typeid)? 320 | }; 321 | 322 | for if_ in &if_expr.ifs { 323 | self.typecheck_expression(ctx, &if_.condition, self.bool())?; 324 | self.typecheck_block(ctx, &if_.body, expected)?; 325 | } 326 | 327 | Ok(expected) 328 | } 329 | ExpressionKind::Unary(op, expr) => { 330 | let expr = self.typecheck_expression(ctx, expr, expected)?; 331 | 332 | match self.typeinfo(expr) { 333 | TypeInfo::Integer | TypeInfo::Bool => Ok(expr), 334 | info => Err(TypeError::UnknownUnaryOp { op: *op, info: info.clone() }), 335 | } 336 | } 337 | } 338 | } 339 | 340 | pub fn register_path_type(&mut self, path: &Path, type_id: TypeId) -> Result<()> { 341 | // TODO: collisions? 342 | self.name_map.insert(path.clone(), type_id); 343 | 344 | Ok(()) 345 | } 346 | 347 | pub fn typecheck_struct(&mut self, ctx: &Context<'_>, strukt: &Struct) -> Result { 348 | let struct_path = self.current_path.with_ident(strukt.name); 349 | 350 | let type_info = TypeInfo::Struct { 351 | members: { 352 | strukt 353 | .members 354 | .iter() 355 | .map(|m| { 356 | let id = match &m.ty.kind { 357 | hir::TypeKind::Integer => self.integer(), 358 | hir::TypeKind::Bool => self.bool(), 359 | hir::TypeKind::Path(path) => match self.resolve_two_way(ctx, path) { 360 | Some(id) => id, 361 | None => return Err(TypeError::UnknownType(path.clone())), 362 | }, 363 | _ => todo!("more type stuff"), 364 | }; 365 | 366 | Ok((m.name, id)) 367 | }) 368 | .collect::>()? 369 | }, 370 | full_path: struct_path.clone(), 371 | }; 372 | 373 | self.name_map.insert(struct_path, self.types.len()); 374 | self.types.push(type_info); 375 | 376 | Ok(self.types.len() - 1) 377 | } 378 | 379 | pub fn typecheck_function(&mut self, ctx: &Context<'_>, function: &Function) -> Result { 380 | let mut ctx = Context { aliases: ctx.aliases.clone(), bindings: HashMap::new(), parent: None }; 381 | 382 | let mut parameters = Vec::new(); 383 | 384 | for fp in &function.parameters { 385 | let parameter_id = self.from_hir_type(&ctx, &fp.ty)?; 386 | ctx.bindings.insert(fp.name, BindingInfo { mutable: false, typeid: parameter_id }); 387 | parameters.push((fp.name, parameter_id)); 388 | } 389 | 390 | let return_type = self.from_hir_type(&ctx, &function.return_type)?; 391 | 392 | let fn_id = self.types.len(); 393 | self.name_map.insert(self.current_path.with_ident(function.name), fn_id); 394 | ctx.aliases.insert(Path::from_identifier(function.name), self.current_path.with_ident(function.name)); 395 | 396 | let type_info = TypeInfo::Function { parameters, return_type }; 397 | self.types.push(type_info); 398 | 399 | self.current_path = self.current_path.with_ident(function.name); 400 | let res = self.typecheck_block(&ctx, &function.body, return_type); 401 | self.current_path.pop(); 402 | 403 | if res.is_err() { 404 | self.name_map.remove(&self.current_path.with_ident(function.name)); 405 | res?; 406 | } 407 | 408 | Ok(fn_id) 409 | } 410 | 411 | pub fn typecheck_block(&mut self, ctx: &Context<'_>, block: &Block, expected: TypeId) -> Result { 412 | let mut child_ctx = ctx.new_child(); 413 | 414 | let unnamable = Identifier::new(&self.unnamable_count.to_string()); 415 | self.unnamable_count += 1; 416 | self.current_path = self.current_path.with_ident(unnamable); 417 | 418 | for statement in &block.statements { 419 | if let Some((name, id)) = self.typecheck_statement(&child_ctx, statement)? { 420 | child_ctx.bindings.insert(name, id); 421 | } 422 | } 423 | 424 | let res = self.typecheck_expression(&child_ctx, &block.return_expr, expected); 425 | self.current_path.pop(); 426 | 427 | res 428 | } 429 | 430 | pub fn typecheck_statement( 431 | &mut self, 432 | ctx: &Context<'_>, 433 | statement: &Statement, 434 | ) -> Result> { 435 | match &statement.kind { 436 | StatementKind::Expression(e) => { 437 | let infer = self.fresh_infer(); 438 | self.typecheck_expression(ctx, &e, infer)?; 439 | Ok(None) 440 | } 441 | StatementKind::Local(local) => { 442 | let typeid = self.from_hir_type(ctx, &local.ty)?; 443 | self.typecheck_expression(ctx, &local.value, typeid)?; 444 | 445 | Ok(Some((local.name, BindingInfo { mutable: local.mutable, typeid }))) 446 | } 447 | } 448 | } 449 | 450 | pub fn typecheck_item(&mut self, ctx: &Context<'_>, item: &Item) -> Result<()> { 451 | match &item.kind { 452 | ItemKind::Module(module) => { 453 | self.current_path = self.current_path.with_ident(module.name); 454 | let ctx = ctx.new_child(); 455 | for item in &module.items { 456 | self.typecheck_item(&ctx, item)?; 457 | } 458 | self.current_path.pop(); 459 | } 460 | ItemKind::Struct(strukt) => { 461 | self.typecheck_struct(ctx, strukt)?; 462 | } 463 | ItemKind::Function(f) => { 464 | self.typecheck_function(ctx, f)?; 465 | } 466 | ItemKind::Use(u) => { 467 | self.typeid_from_path(ctx, &u.path).ok_or_else(|| TypeError::UnknownType(u.path.clone()))?; 468 | } 469 | } 470 | 471 | Ok(()) 472 | } 473 | 474 | pub fn typeid_from_path(&self, ctx: &Context<'_>, path: &Path) -> Option { 475 | self.name_map.get(ctx.resolve_path_alias(path).unwrap_or(path)).copied() 476 | } 477 | 478 | pub fn from_hir_type(&mut self, ctx: &Context<'_>, ty: &Type) -> Result { 479 | match &ty.kind { 480 | TypeKind::Integer => Ok(self.integer()), 481 | TypeKind::Bool => Ok(self.bool()), 482 | TypeKind::Path(path) => { 483 | self.typeid_from_path(ctx, path).ok_or_else(|| TypeError::UnknownType(path.clone())) 484 | } 485 | TypeKind::Infer => Ok(self.fresh_infer()), 486 | TypeKind::Unit => Ok(self.unit()), 487 | } 488 | } 489 | 490 | fn gen_struct_typeinfo(&mut self, ctx: &Context<'_>, se: &StructExpr) -> Result { 491 | let type_info = TypeInfo::Struct { 492 | full_path: se.name.clone(), 493 | members: se 494 | .members 495 | .iter() 496 | .map(|member| { 497 | let id = self.fresh_infer(); 498 | Ok((member.name, self.typecheck_expression(ctx, &member.expression, id)?)) 499 | }) 500 | .collect::>()?, 501 | }; 502 | 503 | self.types.push(type_info); 504 | Ok(self.types.len() - 1) 505 | } 506 | 507 | fn resolve_two_way(&self, ctx: &Context<'_>, path: &Path) -> Option { 508 | self.name_map.get(ctx.resolve_path_alias(path).unwrap_or(path)).copied() 509 | } 510 | 511 | fn integer(&self) -> TypeId { 512 | 0 513 | } 514 | 515 | fn bool(&self) -> TypeId { 516 | 1 517 | } 518 | 519 | fn unit(&self) -> TypeId { 520 | 2 521 | } 522 | } 523 | 524 | impl Default for TypeEngine { 525 | fn default() -> Self { 526 | Self { 527 | types: vec![TypeInfo::Integer, TypeInfo::Bool, TypeInfo::Unit], 528 | name_map: HashMap::new(), 529 | current_path: Path::new(), 530 | unnamable_count: 0, 531 | } 532 | } 533 | } 534 | 535 | #[derive(Debug, Clone)] 536 | pub enum TypeInfo { 537 | Bool, 538 | Function { parameters: Vec<(Identifier, TypeId)>, return_type: TypeId }, 539 | Infer, 540 | Integer, 541 | Ref(TypeId), 542 | Struct { full_path: Path, members: HashMap }, 543 | Unit, 544 | } 545 | 546 | impl TypeInfo { 547 | pub fn debug<'a>(&'a self, engine: &'a TypeEngine) -> TypeInfoDebug<'a> { 548 | TypeInfoDebug { info: self, engine, indent_level: 0 } 549 | } 550 | 551 | pub fn name(&self, engine: &TypeEngine) -> String { 552 | match self { 553 | TypeInfo::Bool => String::from("Bool"), 554 | TypeInfo::Integer => String::from("Int"), 555 | TypeInfo::Unit => String::from("Unit"), 556 | TypeInfo::Struct { full_path, .. } => full_path.to_string(), 557 | TypeInfo::Ref(r) => engine.typeinfo(*r).name(engine), 558 | TypeInfo::Function { .. } => format!("{:?}", self.debug(engine)), 559 | info => unreachable!("{:?}", info), 560 | } 561 | } 562 | } 563 | 564 | pub struct TypeInfoDebug<'a> { 565 | info: &'a TypeInfo, 566 | engine: &'a TypeEngine, 567 | indent_level: usize, 568 | } 569 | 570 | impl<'a> TypeInfoDebug<'a> { 571 | pub fn add_indent(mut self, level: usize) -> Self { 572 | self.indent_level += level; 573 | self 574 | } 575 | } 576 | 577 | impl Debug for TypeInfoDebug<'_> { 578 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 579 | match &self.info { 580 | TypeInfo::Bool => write!(f, "Bool"), 581 | TypeInfo::Function { parameters, return_type } => { 582 | write!(f, "fn(")?; 583 | 584 | if let Some((ident, id)) = parameters.first() { 585 | write!(f, "{}: {}", ident, self.engine.typeinfo(*id).name(self.engine))?; 586 | } 587 | 588 | for (ident, id) in parameters.iter().skip(1) { 589 | write!(f, ", {}: {}", ident, self.engine.typeinfo(*id).name(self.engine))?; 590 | } 591 | 592 | write!(f, ") -> {}", self.engine.typeinfo(*return_type).name(self.engine)) 593 | } 594 | TypeInfo::Integer => write!(f, "Int"), 595 | TypeInfo::Struct { full_path, members, .. } => { 596 | writeln!(f, "{} {{", full_path)?; 597 | for (ident, &ty) in members { 598 | writeln!( 599 | f, 600 | "{: write!(f, "_"), 610 | TypeInfo::Unit => write!(f, "Unit"), 611 | TypeInfo::Ref(id) => write!(f, "{:?}", self.engine.typeinfo(*id).debug(self.engine)), 612 | } 613 | } 614 | } 615 | --------------------------------------------------------------------------------