├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── FUNDING.yml ├── README.md ├── bin ├── Cargo.toml └── src │ ├── args.rs │ └── main.rs ├── com ├── Cargo.toml └── src │ └── lib.rs ├── example ├── err_ty.hlm ├── factorial.hlm └── simple.hlm ├── ir ├── Cargo.toml └── src │ └── lib.rs ├── rust-toolchain.toml ├── syntax ├── Cargo.toml └── src │ ├── expr.rs │ ├── lib.rs │ ├── parser.rs │ └── ty.rs └── typing ├── Cargo.toml └── src ├── infer.rs ├── lib.rs ├── rename.rs └── typed.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ahash" 7 | version = "0.8.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" 10 | dependencies = [ 11 | "cfg-if", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "anstream" 18 | version = "0.3.0" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" 21 | dependencies = [ 22 | "anstyle", 23 | "anstyle-parse", 24 | "anstyle-query", 25 | "anstyle-wincon", 26 | "colorchoice", 27 | "is-terminal", 28 | "utf8parse", 29 | ] 30 | 31 | [[package]] 32 | name = "anstyle" 33 | version = "1.0.0" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" 36 | 37 | [[package]] 38 | name = "anstyle-parse" 39 | version = "0.2.0" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" 42 | dependencies = [ 43 | "utf8parse", 44 | ] 45 | 46 | [[package]] 47 | name = "anstyle-query" 48 | version = "1.0.0" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" 51 | dependencies = [ 52 | "windows-sys", 53 | ] 54 | 55 | [[package]] 56 | name = "anstyle-wincon" 57 | version = "1.0.0" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" 60 | dependencies = [ 61 | "anstyle", 62 | "windows-sys", 63 | ] 64 | 65 | [[package]] 66 | name = "ariadne" 67 | version = "0.2.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "367fd0ad87307588d087544707bc5fbf4805ded96c7db922b70d368fa1cb5702" 70 | dependencies = [ 71 | "unicode-width", 72 | "yansi", 73 | ] 74 | 75 | [[package]] 76 | name = "bin" 77 | version = "0.1.0" 78 | dependencies = [ 79 | "ariadne", 80 | "chumsky", 81 | "clap", 82 | "com", 83 | "ir", 84 | "syntax", 85 | "typing", 86 | ] 87 | 88 | [[package]] 89 | name = "bitflags" 90 | version = "1.3.2" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 93 | 94 | [[package]] 95 | name = "cc" 96 | version = "1.0.79" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 99 | 100 | [[package]] 101 | name = "cfg-if" 102 | version = "1.0.0" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 105 | 106 | [[package]] 107 | name = "chumsky" 108 | version = "1.0.0-alpha.3" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "379cdc19530b72a1e76d94a350676eaea1455375533eb38f18dfa712f9996902" 111 | dependencies = [ 112 | "hashbrown", 113 | "stacker", 114 | ] 115 | 116 | [[package]] 117 | name = "clap" 118 | version = "4.2.4" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62" 121 | dependencies = [ 122 | "clap_builder", 123 | "clap_derive", 124 | "once_cell", 125 | ] 126 | 127 | [[package]] 128 | name = "clap_builder" 129 | version = "4.2.4" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" 132 | dependencies = [ 133 | "anstream", 134 | "anstyle", 135 | "bitflags", 136 | "clap_lex", 137 | "strsim", 138 | ] 139 | 140 | [[package]] 141 | name = "clap_derive" 142 | version = "4.2.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" 145 | dependencies = [ 146 | "heck", 147 | "proc-macro2", 148 | "quote", 149 | "syn", 150 | ] 151 | 152 | [[package]] 153 | name = "clap_lex" 154 | version = "0.4.1" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" 157 | 158 | [[package]] 159 | name = "colorchoice" 160 | version = "1.0.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 163 | 164 | [[package]] 165 | name = "com" 166 | version = "0.1.0" 167 | dependencies = [ 168 | "chumsky", 169 | "syntax", 170 | "typing", 171 | ] 172 | 173 | [[package]] 174 | name = "errno" 175 | version = "0.3.1" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" 178 | dependencies = [ 179 | "errno-dragonfly", 180 | "libc", 181 | "windows-sys", 182 | ] 183 | 184 | [[package]] 185 | name = "errno-dragonfly" 186 | version = "0.1.2" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 189 | dependencies = [ 190 | "cc", 191 | "libc", 192 | ] 193 | 194 | [[package]] 195 | name = "hashbrown" 196 | version = "0.13.2" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" 199 | dependencies = [ 200 | "ahash", 201 | ] 202 | 203 | [[package]] 204 | name = "heck" 205 | version = "0.4.1" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 208 | 209 | [[package]] 210 | name = "hermit-abi" 211 | version = "0.3.1" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 214 | 215 | [[package]] 216 | name = "io-lifetimes" 217 | version = "1.0.10" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" 220 | dependencies = [ 221 | "hermit-abi", 222 | "libc", 223 | "windows-sys", 224 | ] 225 | 226 | [[package]] 227 | name = "ir" 228 | version = "0.1.0" 229 | dependencies = [ 230 | "chumsky", 231 | "syntax", 232 | "typing", 233 | ] 234 | 235 | [[package]] 236 | name = "is-terminal" 237 | version = "0.4.7" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" 240 | dependencies = [ 241 | "hermit-abi", 242 | "io-lifetimes", 243 | "rustix", 244 | "windows-sys", 245 | ] 246 | 247 | [[package]] 248 | name = "libc" 249 | version = "0.2.147" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" 252 | 253 | [[package]] 254 | name = "linux-raw-sys" 255 | version = "0.3.4" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" 258 | 259 | [[package]] 260 | name = "once_cell" 261 | version = "1.17.1" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 264 | 265 | [[package]] 266 | name = "proc-macro2" 267 | version = "1.0.56" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" 270 | dependencies = [ 271 | "unicode-ident", 272 | ] 273 | 274 | [[package]] 275 | name = "psm" 276 | version = "0.1.21" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" 279 | dependencies = [ 280 | "cc", 281 | ] 282 | 283 | [[package]] 284 | name = "quote" 285 | version = "1.0.26" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 288 | dependencies = [ 289 | "proc-macro2", 290 | ] 291 | 292 | [[package]] 293 | name = "rustix" 294 | version = "0.37.14" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" 297 | dependencies = [ 298 | "bitflags", 299 | "errno", 300 | "io-lifetimes", 301 | "libc", 302 | "linux-raw-sys", 303 | "windows-sys", 304 | ] 305 | 306 | [[package]] 307 | name = "stacker" 308 | version = "0.1.15" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" 311 | dependencies = [ 312 | "cc", 313 | "cfg-if", 314 | "libc", 315 | "psm", 316 | "winapi", 317 | ] 318 | 319 | [[package]] 320 | name = "strsim" 321 | version = "0.10.0" 322 | source = "registry+https://github.com/rust-lang/crates.io-index" 323 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 324 | 325 | [[package]] 326 | name = "syn" 327 | version = "2.0.15" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" 330 | dependencies = [ 331 | "proc-macro2", 332 | "quote", 333 | "unicode-ident", 334 | ] 335 | 336 | [[package]] 337 | name = "syntax" 338 | version = "0.1.0" 339 | dependencies = [ 340 | "chumsky", 341 | ] 342 | 343 | [[package]] 344 | name = "typing" 345 | version = "0.1.0" 346 | dependencies = [ 347 | "chumsky", 348 | "syntax", 349 | ] 350 | 351 | [[package]] 352 | name = "unicode-ident" 353 | version = "1.0.8" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 356 | 357 | [[package]] 358 | name = "unicode-width" 359 | version = "0.1.10" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 362 | 363 | [[package]] 364 | name = "utf8parse" 365 | version = "0.2.1" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 368 | 369 | [[package]] 370 | name = "version_check" 371 | version = "0.9.4" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 374 | 375 | [[package]] 376 | name = "winapi" 377 | version = "0.3.9" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 380 | dependencies = [ 381 | "winapi-i686-pc-windows-gnu", 382 | "winapi-x86_64-pc-windows-gnu", 383 | ] 384 | 385 | [[package]] 386 | name = "winapi-i686-pc-windows-gnu" 387 | version = "0.4.0" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 390 | 391 | [[package]] 392 | name = "winapi-x86_64-pc-windows-gnu" 393 | version = "0.4.0" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 396 | 397 | [[package]] 398 | name = "windows-sys" 399 | version = "0.48.0" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 402 | dependencies = [ 403 | "windows-targets", 404 | ] 405 | 406 | [[package]] 407 | name = "windows-targets" 408 | version = "0.48.0" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" 411 | dependencies = [ 412 | "windows_aarch64_gnullvm", 413 | "windows_aarch64_msvc", 414 | "windows_i686_gnu", 415 | "windows_i686_msvc", 416 | "windows_x86_64_gnu", 417 | "windows_x86_64_gnullvm", 418 | "windows_x86_64_msvc", 419 | ] 420 | 421 | [[package]] 422 | name = "windows_aarch64_gnullvm" 423 | version = "0.48.0" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" 426 | 427 | [[package]] 428 | name = "windows_aarch64_msvc" 429 | version = "0.48.0" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" 432 | 433 | [[package]] 434 | name = "windows_i686_gnu" 435 | version = "0.48.0" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" 438 | 439 | [[package]] 440 | name = "windows_i686_msvc" 441 | version = "0.48.0" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" 444 | 445 | [[package]] 446 | name = "windows_x86_64_gnu" 447 | version = "0.48.0" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" 450 | 451 | [[package]] 452 | name = "windows_x86_64_gnullvm" 453 | version = "0.48.0" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" 456 | 457 | [[package]] 458 | name = "windows_x86_64_msvc" 459 | version = "0.48.0" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" 462 | 463 | [[package]] 464 | name = "yansi" 465 | version = "0.5.1" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" 468 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "bin", 5 | "syntax", 6 | "typing", 7 | "ir", 8 | "com", 9 | ] -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: azur1s -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

Holymer

3 |
4 | 5 | Holymer is a programming language 6 | 7 | ## Update 8 | Archived: see [ichor](https://github.com/azur1s/ichor) instead. It's literally what this meant to be 9 | 10 | ## Status 11 | - [x] Parser 12 | - [x] Typechecker 13 | - [x] IR 14 | - [ ] Optimizer 15 | - [ ] Complier 16 | 17 | The IR output can sometimes be run with scheme interpreter, sometimes. 18 | 19 | ## Contributing 20 | You need to have [Rust Toolchain](https://github.com/rust-lang/rust) installed on your machine before building it. 21 | ```shell 22 | $ git clone https://github.com/azur1s/holymer.git 23 | $ cd holymer 24 | # build with `cargo build` 25 | ``` 26 | -------------------------------------------------------------------------------- /bin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bin" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | ariadne = "0.2.0" 8 | chumsky = "1.0.0-alpha.3" 9 | clap = { version = "4.2.4", features = ["derive"] } 10 | syntax = { path = "../syntax" } 11 | typing = { path = "../typing" } 12 | ir = { path = "../ir" } 13 | com = { path = "../com" } 14 | 15 | [[bin]] 16 | name = "hc" 17 | path = "src/main.rs" -------------------------------------------------------------------------------- /bin/src/args.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | 3 | #[derive(Debug, Parser)] 4 | pub struct Args { 5 | /// The path to the file to be compiled. 6 | #[arg(required = true)] 7 | pub file: String, 8 | /// Only run the type checker. 9 | #[arg(short = 'c', long = "check")] 10 | pub typecheck: bool, 11 | } 12 | 13 | pub fn get_args() -> Args { 14 | Args::parse() 15 | } -------------------------------------------------------------------------------- /bin/src/main.rs: -------------------------------------------------------------------------------- 1 | use ariadne::{sources, Color, Label, Report, ReportKind}; 2 | use chumsky::{Parser, prelude::Input}; 3 | 4 | use ir::lower_expr; 5 | use syntax::parser::{lexer, exprs_parser}; 6 | use typing::infer::{infer_exprs, InferErrorKind}; 7 | 8 | pub mod args; 9 | 10 | fn main() { 11 | let args = args::get_args(); 12 | let filename = args.file.clone(); 13 | let src = std::fs::read_to_string(&args.file).expect("file not found"); 14 | 15 | // Lexing & parsing 16 | let (ts, errs) = lexer().parse(&src).into_output_errors(); 17 | 18 | let (ast, parse_errs) = if let Some(tokens) = &ts { 19 | let (ast, parse_errs) = exprs_parser() 20 | .map_with_span(|ast, span| (ast, span)) 21 | .parse(tokens.as_slice().spanned((src.len()..src.len()).into())) 22 | .into_output_errors(); 23 | 24 | (ast, parse_errs) 25 | } else { 26 | (None, vec![]) 27 | }; 28 | 29 | // Typecheck if there are no lexing or parsing errors 30 | if let Some(ast) = ast.filter(|_| errs.len() + parse_errs.len() == 0) { 31 | let (ast, e) = infer_exprs(ast.0); 32 | // If there is an error, print it 33 | if !e.is_empty() { 34 | e.into_iter() 35 | .for_each(|e| { 36 | let mut r = Report::build(ReportKind::Error, filename.clone(), e.span.start) 37 | .with_message(e.title.to_string()); 38 | 39 | for (msg, kind, span) in e.labels { 40 | r = r.with_label( 41 | Label::new((filename.clone(), span.into_range())) 42 | .with_message(msg.to_string()) 43 | .with_color(match kind { 44 | InferErrorKind::Error => Color::Red, 45 | InferErrorKind::Hint => Color::Blue, 46 | }), 47 | ); 48 | } 49 | 50 | r 51 | .finish() 52 | .print(sources([(filename.clone(), src.clone())])) 53 | .unwrap() 54 | }); 55 | // Else go to the next stage 56 | } else { 57 | if args.typecheck { 58 | ast.iter().for_each(|node| println!("{:?}", node.0)); 59 | return; 60 | } 61 | // ast.iter().for_each(|node| println!("{:?}", node.0)); 62 | let irs = ast.into_iter().map(|node| lower_expr(node.0)).collect::>(); 63 | irs.iter().for_each(|ir| println!("{}", ir)); 64 | } 65 | }; 66 | 67 | // Report lex & parse errors 68 | errs.into_iter() 69 | .map(|e| e.map_token(|c| c.to_string())) 70 | .chain( 71 | parse_errs 72 | .into_iter() 73 | .map(|e| e.map_token(|tok| tok.to_string())), 74 | ) 75 | .for_each(|e| { 76 | Report::build(ReportKind::Error, filename.clone(), e.span().start) 77 | .with_message(e.to_string()) 78 | .with_label( 79 | Label::new((filename.clone(), e.span().into_range())) 80 | .with_message(e.reason().to_string()) 81 | .with_color(Color::Red), 82 | ) 83 | .finish() 84 | .print(sources([(filename.clone(), src.clone())])) 85 | .unwrap() 86 | }); 87 | } -------------------------------------------------------------------------------- /com/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "com" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | chumsky = "1.0.0-alpha.3" 8 | syntax = { path = "../syntax" } 9 | typing = { path = "../typing" } -------------------------------------------------------------------------------- /com/src/lib.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azur1s/holymer/5280bdabd7f0dbb0c8f0374ba5cccef6a19d530d/com/src/lib.rs -------------------------------------------------------------------------------- /example/err_ty.hlm: -------------------------------------------------------------------------------- 1 | let f = fun (a (Int) -> Int, y Int) -> a(y); 2 | let g = fun (a (Int, Int) -> Int, y Int) -> a(y, 1); 3 | 4 | let a = fun (x Int) -> x + 1; 5 | let b = fun (x Int, y Int) -> x + y; 6 | 7 | f(a, 1); 8 | g(a, 1); 9 | f(b, 1); 10 | g(b, 1); -------------------------------------------------------------------------------- /example/factorial.hlm: -------------------------------------------------------------------------------- 1 | let factorial = fun (n Int) Int -> 2 | if n > 1 3 | then n * factorial(n - 1) 4 | else 1 5 | ; 6 | 7 | factorial(5); -------------------------------------------------------------------------------- /example/simple.hlm: -------------------------------------------------------------------------------- 1 | let add = fun (x Int, y Int) Int -> x + y; 2 | let succ = fun (x) -> x + 1; 3 | let mul = fun (x, y) -> x * y; 4 | 5 | add(33, 35) 6 | |> fun (x) -> succ(x) 7 | |> fun (x) -> mul(x, 10) -------------------------------------------------------------------------------- /ir/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ir" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | chumsky = "1.0.0-alpha.3" 8 | syntax = { path = "../syntax" } 9 | typing = { path = "../typing" } 10 | -------------------------------------------------------------------------------- /ir/src/lib.rs: -------------------------------------------------------------------------------- 1 | use typing::typed::TExpr; 2 | use syntax::expr::{Lit as ExprLit, UnaryOp, BinaryOp}; 3 | 4 | use std::fmt::{Display, Formatter, Result as FmtResult}; 5 | 6 | #[derive(Clone, Debug)] 7 | pub enum Lit<'src> { 8 | Unit, 9 | Bool(bool), 10 | Int(i64), 11 | Str(&'src str), 12 | } 13 | 14 | impl Display for Lit<'_> { 15 | fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { 16 | match self { 17 | Lit::Unit => write!(f, "()"), 18 | Lit::Bool(b) => write!(f, "{}", b), 19 | Lit::Int(i) => write!(f, "{}", i), 20 | Lit::Str(s) => write!(f, "\"{}\"", s), 21 | } 22 | } 23 | } 24 | 25 | #[derive(Clone, Debug)] 26 | pub enum Expr<'src> { 27 | Lit(Lit<'src>), 28 | // v0 29 | Var(&'src str), 30 | // f(v0, v1, ...) 31 | Call(Vec), 32 | } 33 | 34 | impl Display for Expr<'_> { 35 | fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { 36 | match self { 37 | Expr::Lit(l) => write!(f, "{}", l), 38 | Expr::Var(s) => write!(f, "{}", s), 39 | Expr::Call(v) => { 40 | write!(f, "(")?; 41 | for (i, e) in v.iter().enumerate() { 42 | if i != 0 { 43 | write!(f, " ")?; 44 | } 45 | write!(f, "{}", e)?; 46 | } 47 | write!(f, ")") 48 | } 49 | } 50 | } 51 | } 52 | 53 | macro_rules! unbox { 54 | ($e:expr) => { 55 | *(($e).0) 56 | }; 57 | } 58 | 59 | macro_rules! str { 60 | ($e:expr) => { 61 | Expr::Lit(Lit::Str($e)) 62 | }; 63 | } 64 | 65 | macro_rules! var { 66 | ($e:expr) => { 67 | Expr::Var($e) 68 | }; 69 | } 70 | 71 | macro_rules! call { 72 | ($e:expr) => { 73 | Expr::Call($e) 74 | }; 75 | } 76 | 77 | pub fn lower_lit(lit: ExprLit) -> Lit { 78 | match lit { 79 | ExprLit::Unit => Lit::Unit, 80 | ExprLit::Bool(b) => Lit::Bool(b), 81 | ExprLit::Int(i) => Lit::Int(i), 82 | ExprLit::Str(s) => Lit::Str(s), 83 | } 84 | } 85 | 86 | pub fn lower_expr(e: TExpr) -> Expr { 87 | match e { 88 | TExpr::Lit(l) => Expr::Lit(lower_lit(l)), 89 | TExpr::Ident(s) => var!(s), 90 | TExpr::Unary { op, expr, .. } => { 91 | let expr = lower_expr(unbox!(expr)); 92 | match op { 93 | UnaryOp::Neg => call!(vec![var!("neg"), expr]), 94 | UnaryOp::Not => call!(vec![var!("not"), expr]), 95 | } 96 | } 97 | TExpr::Binary { op: BinaryOp::Pipe, lhs, rhs, .. } => { 98 | let lhs = lower_expr(unbox!(lhs)); // arguments 99 | let rhs = lower_expr(unbox!(rhs)); // function 100 | call!(vec![rhs, lhs]) 101 | } 102 | TExpr::Binary { op, lhs, rhs, .. } => { 103 | let lhs = lower_expr(unbox!(lhs)); 104 | let rhs = lower_expr(unbox!(rhs)); 105 | match op { 106 | BinaryOp::Add => call!(vec![var!("+"), lhs, rhs]), 107 | BinaryOp::Sub => call!(vec![var!("-"), lhs, rhs]), 108 | BinaryOp::Mul => call!(vec![var!("*"), lhs, rhs]), 109 | BinaryOp::Div => call!(vec![var!("/"), lhs, rhs]), 110 | BinaryOp::Rem => call!(vec![var!("%"), lhs, rhs]), 111 | BinaryOp::Eq => call!(vec![var!("=="), lhs, rhs]), 112 | BinaryOp::Ne => call!(vec![var!("!="), lhs, rhs]), 113 | BinaryOp::Lt => call!(vec![var!("<"), lhs, rhs]), 114 | BinaryOp::Le => call!(vec![var!("<="), lhs, rhs]), 115 | BinaryOp::Gt => call!(vec![var!(">"), lhs, rhs]), 116 | BinaryOp::Ge => call!(vec![var!(">="), lhs, rhs]), 117 | BinaryOp::And => call!(vec![var!("&&"), lhs, rhs]), 118 | BinaryOp::Or => call!(vec![var!("||"), lhs, rhs]), 119 | BinaryOp::Pipe => unreachable!("pipe operator is handled separately"), 120 | } 121 | } 122 | TExpr::Lambda { params, body, .. } => { 123 | let body = lower_expr(unbox!(body)); 124 | call!(vec![ 125 | var!("lambda"), 126 | call!(params.into_iter().map(|(p, _)| var!(p)).collect()), 127 | body, 128 | ]) 129 | } 130 | TExpr::Call { func, args } => { 131 | let func = lower_expr(unbox!(func)); 132 | let args = args.into_iter() 133 | .map(|(a, _)| lower_expr(a)) 134 | .collect::>(); 135 | call!(vec![func].into_iter().chain(args).collect()) 136 | } 137 | TExpr::If { cond, t, f, .. } => { 138 | let cond = lower_expr(unbox!(cond)); 139 | let t = lower_expr(unbox!(t)); 140 | let f = lower_expr(unbox!(f)); 141 | call!(vec![var!("if"), cond, t, f]) 142 | } 143 | TExpr::Let { name, value, body, .. } => { 144 | let value = lower_expr(unbox!(value)); 145 | let body = lower_expr(unbox!(body)); 146 | call!(vec![var!("let"), str!(name), value, body]) 147 | } 148 | TExpr::Define { name, value, .. } => { 149 | let value = lower_expr(unbox!(value)); 150 | call!(vec![var!("define"), var!(name), value]) 151 | } 152 | TExpr::Block { exprs, void, .. } => { 153 | let exprs = exprs.into_iter() 154 | .map(|(e, _)| lower_expr(e)) 155 | .collect::>(); 156 | if void { 157 | call!(vec![var!("block"), call!(exprs)]) 158 | } else { 159 | call!(vec![var!("block"), call!(exprs), var!("()")]) 160 | } 161 | } 162 | } 163 | } -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" -------------------------------------------------------------------------------- /syntax/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "syntax" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | chumsky = { version = "1.0.0-alpha.3", features = ["label"] } 8 | -------------------------------------------------------------------------------- /syntax/src/expr.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{ Display, Formatter, self }; 2 | use chumsky::span::SimpleSpan; 3 | 4 | use super::ty::Type; 5 | 6 | #[derive(Clone, Debug, PartialEq)] 7 | pub enum Delim { Paren, Brack, Brace } 8 | 9 | // The tokens of the language. 10 | // 'src is the lifetime of the source code string. 11 | #[derive(Clone, Debug, PartialEq)] 12 | pub enum Token<'src> { 13 | Unit, Bool(bool), Int(i64), Str(&'src str), 14 | Ident(&'src str), 15 | 16 | Add, Sub, Mul, Div, Rem, 17 | Eq, Ne, Lt, Gt, Le, Ge, 18 | And, Or, Not, 19 | Pipe, 20 | 21 | Assign, Comma, Colon, Semicolon, 22 | Open(Delim), Close(Delim), 23 | Lambda, Arrow, 24 | 25 | Let, In, Func, Return, If, Then, Else, 26 | } 27 | 28 | impl<'src> Display for Token<'src> { 29 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 30 | match self { 31 | Token::Unit => write!(f, "()"), 32 | Token::Bool(b) => write!(f, "{}", b), 33 | Token::Int(n) => write!(f, "{}", n), 34 | Token::Str(s) => write!(f, "\"{}\"", s), 35 | Token::Ident(s) => write!(f, "{}", s), 36 | 37 | Token::Add => write!(f, "+"), 38 | Token::Sub => write!(f, "-"), 39 | Token::Mul => write!(f, "*"), 40 | Token::Div => write!(f, "/"), 41 | Token::Rem => write!(f, "%"), 42 | Token::Eq => write!(f, "=="), 43 | Token::Ne => write!(f, "!="), 44 | Token::Lt => write!(f, "<"), 45 | Token::Gt => write!(f, ">"), 46 | Token::Le => write!(f, "<="), 47 | Token::Ge => write!(f, ">="), 48 | Token::And => write!(f, "&&"), 49 | Token::Or => write!(f, "||"), 50 | Token::Not => write!(f, "!"), 51 | Token::Pipe => write!(f, "|>"), 52 | 53 | Token::Assign => write!(f, "="), 54 | Token::Comma => write!(f, ","), 55 | Token::Colon => write!(f, ":"), 56 | Token::Semicolon => write!(f, ";"), 57 | Token::Open(d) => write!(f, "{}", match d { 58 | Delim::Paren => "(", 59 | Delim::Brack => "[", 60 | Delim::Brace => "{", 61 | }), 62 | Token::Close(d) => write!(f, "{}", match d { 63 | Delim::Paren => ")", 64 | Delim::Brack => "]", 65 | Delim::Brace => "}", 66 | }), 67 | Token::Lambda => write!(f, "\\"), 68 | Token::Arrow => write!(f, "->"), 69 | 70 | Token::Let => write!(f, "let"), 71 | Token::In => write!(f, "in"), 72 | Token::Func => write!(f, "func"), 73 | Token::Return => write!(f, "return"), 74 | Token::If => write!(f, "if"), 75 | Token::Then => write!(f, "then"), 76 | Token::Else => write!(f, "else"), 77 | } 78 | } 79 | } 80 | 81 | pub type Span = SimpleSpan; 82 | 83 | #[derive(Clone, Debug, PartialEq)] 84 | pub enum Lit<'src> { 85 | Unit, 86 | Bool(bool), 87 | Int(i64), 88 | Str(&'src str), 89 | } 90 | 91 | #[derive(Clone, Debug)] 92 | pub enum UnaryOp { Neg, Not } 93 | 94 | impl Display for UnaryOp { 95 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 96 | match self { 97 | UnaryOp::Neg => write!(f, "-"), 98 | UnaryOp::Not => write!(f, "!"), 99 | } 100 | } 101 | } 102 | 103 | #[derive(Clone, Debug, PartialEq)] 104 | pub enum BinaryOp { 105 | Add, Sub, Mul, Div, Rem, 106 | And, Or, 107 | Eq, Ne, Lt, Le, Gt, Ge, 108 | Pipe, 109 | } 110 | 111 | impl Display for BinaryOp { 112 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 113 | match self { 114 | BinaryOp::Add => write!(f, "+"), 115 | BinaryOp::Sub => write!(f, "-"), 116 | BinaryOp::Mul => write!(f, "*"), 117 | BinaryOp::Div => write!(f, "/"), 118 | BinaryOp::Rem => write!(f, "%"), 119 | BinaryOp::And => write!(f, "&&"), 120 | BinaryOp::Or => write!(f, "||"), 121 | BinaryOp::Eq => write!(f, "=="), 122 | BinaryOp::Ne => write!(f, "!="), 123 | BinaryOp::Lt => write!(f, "<"), 124 | BinaryOp::Le => write!(f, "<="), 125 | BinaryOp::Gt => write!(f, ">"), 126 | BinaryOp::Ge => write!(f, ">="), 127 | BinaryOp::Pipe => write!(f, "|>"), 128 | } 129 | } 130 | } 131 | 132 | pub type Spanned = (T, Span); 133 | 134 | // Clone is needed for type checking since the type checking 135 | // algorithm is recursive and sometimes consume the AST. 136 | #[derive(Clone, Debug)] 137 | pub enum Expr<'src> { 138 | Lit(Lit<'src>), 139 | Ident(&'src str), 140 | 141 | Unary(UnaryOp, Spanned>), 142 | Binary(BinaryOp, Spanned>, Spanned>), 143 | 144 | Lambda(Vec<(&'src str, Option)>, Option, Spanned>), 145 | Call(Spanned>, Vec>), 146 | 147 | If { 148 | cond: Spanned>, 149 | t: Spanned>, 150 | f: Spanned>, 151 | }, 152 | Let { 153 | name: &'src str, 154 | ty: Option, 155 | value: Spanned>, 156 | body: Spanned>, 157 | }, 158 | Define { 159 | name: &'src str, 160 | ty: Option, 161 | value: Spanned>, 162 | }, 163 | Block { 164 | exprs: Vec>>, 165 | void: bool, // True if last expression is discarded (ends with semicolon). 166 | }, 167 | } -------------------------------------------------------------------------------- /syntax/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod expr; 2 | pub mod parser; 3 | pub mod ty; 4 | -------------------------------------------------------------------------------- /syntax/src/parser.rs: -------------------------------------------------------------------------------- 1 | use chumsky::prelude::*; 2 | 3 | use super::{ expr::*, ty::Type }; 4 | 5 | pub fn lexer<'src>() -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, extra::Err>> { 6 | // let num = text::int(10) 7 | // .then(just('.').then(text::digits(10)).or_not()) 8 | // .slice() 9 | // .from_str() 10 | // .unwrapped() 11 | // .map(Token::Int); 12 | let int = text::int(10) 13 | .slice() 14 | .from_str() 15 | .unwrapped() 16 | .map(Token::Int); 17 | 18 | let strn = just('"') 19 | .ignore_then(none_of('"').repeated()) 20 | .then_ignore(just('"')) 21 | .map_slice(Token::Str); 22 | 23 | fn id_filter(c: &C) -> bool where C: text::Char { 24 | c.to_char().is_ascii_alphabetic() 25 | || "_'".contains(c.to_char()) 26 | } 27 | let id = any() 28 | .filter(id_filter) 29 | .then(any() 30 | .filter(id_filter) 31 | .repeated()) 32 | .slice(); 33 | 34 | let word = id.map(|s: &str| match s { 35 | "true" => Token::Bool(true), 36 | "false" => Token::Bool(false), 37 | "let" => Token::Let, 38 | "in" => Token::In, 39 | "fun" => Token::Func, 40 | "return" => Token::Return, 41 | "if" => Token::If, 42 | "then" => Token::Then, 43 | "else" => Token::Else, 44 | _ => Token::Ident(s), 45 | }); 46 | 47 | let sym = choice(( 48 | just("()").to(Token::Unit), 49 | just("\\").to(Token::Lambda), 50 | just("->").to(Token::Arrow), 51 | just("|>").to(Token::Pipe), 52 | 53 | just('+').to(Token::Add), 54 | just('-').to(Token::Sub), 55 | just('*').to(Token::Mul), 56 | just('/').to(Token::Div), 57 | just('%').to(Token::Rem), 58 | just("==").to(Token::Eq), 59 | just("!=").to(Token::Ne), 60 | just("<=").to(Token::Le), 61 | just(">=").to(Token::Ge), 62 | just('<').to(Token::Lt), 63 | just('>').to(Token::Gt), 64 | just("&&").to(Token::And), 65 | just("||").to(Token::Or), 66 | just('!').to(Token::Not), 67 | 68 | just('=').to(Token::Assign), 69 | just(',').to(Token::Comma), 70 | just(':').to(Token::Colon), 71 | just(';').to(Token::Semicolon), 72 | )); 73 | 74 | let delim = choice(( 75 | just('(').to(Token::Open(Delim::Paren)), 76 | just(')').to(Token::Close(Delim::Paren)), 77 | just('[').to(Token::Open(Delim::Brack)), 78 | just(']').to(Token::Close(Delim::Brack)), 79 | just('{').to(Token::Open(Delim::Brace)), 80 | just('}').to(Token::Close(Delim::Brace)), 81 | )); 82 | 83 | let token = choice(( 84 | int, 85 | strn, 86 | word, 87 | sym, 88 | delim, 89 | )); 90 | 91 | let comment = just("//") 92 | .then(any().and_is(just('\n').not()).repeated()) 93 | .padded(); 94 | 95 | token 96 | .map_with_span(move |tok, span| (tok, span)) 97 | .padded_by(comment.repeated()) 98 | .padded() 99 | // If we get an error, skip to the next character and try again. 100 | .recover_with(skip_then_retry_until(any().ignored(), end())) 101 | .repeated() 102 | .collect() 103 | } 104 | 105 | // (a, s) -> (Box::new(a), s) 106 | fn boxspan(a: Spanned) -> Spanned> { 107 | (Box::new(a.0), a.1) 108 | } 109 | 110 | // Lifetime 'tokens is the lifetime of the token buffer from the lexer. 111 | type ParserInput<'tokens, 'src> = 112 | chumsky::input::SpannedInput< 113 | Token<'src>, 114 | Span, 115 | &'tokens [(Token<'src>, Span)] 116 | >; 117 | 118 | pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser< 119 | 'tokens, 120 | ParserInput<'tokens, 'src>, 121 | Spanned>, 122 | extra::Err, Span>>, 123 | > + Clone { 124 | recursive(|expr| { 125 | let lit = select! { 126 | Token::Unit => Expr::Lit(Lit::Unit), 127 | Token::Bool(b) => Expr::Lit(Lit::Bool(b)), 128 | Token::Int(n) => Expr::Lit(Lit::Int(n)), 129 | Token::Str(s) => Expr::Lit(Lit::Str(s)), 130 | }; 131 | 132 | let symbol = select! { 133 | Token::Ident(s) => s, 134 | }; 135 | 136 | let ident = symbol 137 | .map(Expr::Ident); 138 | 139 | let paren_expr = expr.clone() 140 | .delimited_by( 141 | just(Token::Open(Delim::Paren)), 142 | just(Token::Close(Delim::Paren)), 143 | ) 144 | .map(|e: Spanned| e.0); 145 | 146 | let lambda = just(Token::Func) 147 | .ignore_then( 148 | (symbol 149 | .then(type_parser().or_not()) 150 | .separated_by(just(Token::Comma)) 151 | .collect::>() 152 | .delimited_by( 153 | just(Token::Open(Delim::Paren)), 154 | just(Token::Close(Delim::Paren)), 155 | )) 156 | .or(just(Token::Unit).to(Vec::new())) 157 | ) 158 | .then(type_parser().or_not()) 159 | .then_ignore(just(Token::Arrow)) 160 | .then(expr.clone()) 161 | .map(|((args, ret), body)| Expr::Lambda(args, ret, boxspan(body))); 162 | 163 | // ident (: type)? 164 | let bind = symbol 165 | .then( 166 | just(Token::Colon) 167 | .ignore_then(type_parser()) 168 | .or_not() 169 | ) 170 | .then_ignore(just(Token::Assign)) 171 | .then(expr.clone()) 172 | .map(|((name, ty), expr)| (name, ty, boxspan(expr))); 173 | 174 | let let_or_define = just(Token::Let) 175 | .ignore_then(bind) 176 | .then( 177 | just(Token::In) 178 | .ignore_then(expr.clone()) 179 | .or_not() 180 | ) 181 | .map(|((name, ty, expr), body)| match body { 182 | Some(body) => Expr::Let { name, ty, value: expr, body: boxspan(body) }, 183 | None => Expr::Define { name, ty, value: expr }, 184 | }); 185 | 186 | let if_ = just(Token::If) 187 | .ignore_then(expr.clone()) 188 | .then_ignore(just(Token::Then)) 189 | .then(expr.clone()) 190 | .then_ignore(just(Token::Else)) 191 | .then(expr.clone()) 192 | .map(|((cond, t), f)| Expr::If { 193 | cond: boxspan(cond), 194 | t: boxspan(t), 195 | f: boxspan(f) 196 | }); 197 | 198 | let block = expr.clone() 199 | .map(boxspan) 200 | .then_ignore(just(Token::Semicolon)) 201 | .repeated() 202 | .collect::>() 203 | .then(expr.clone() 204 | .map(boxspan) 205 | .or_not()) 206 | .delimited_by( 207 | just(Token::Open(Delim::Brace)), 208 | just(Token::Close(Delim::Brace)), 209 | ) 210 | .map(|(mut exprs, end)| { 211 | let void = end.is_none(); 212 | if let Some(end) = end { 213 | exprs.push(end); 214 | } 215 | Expr::Block { 216 | exprs, 217 | void, 218 | } 219 | }); 220 | 221 | let atom = lit 222 | .or(ident) 223 | .or(paren_expr) 224 | .or(lambda) 225 | .or(let_or_define) 226 | .or(if_) 227 | .or(block) 228 | .map_with_span(|e, s| (e, s)) 229 | .boxed() 230 | .labelled("(atomic) expression"); 231 | 232 | let call = atom 233 | .then( 234 | expr.clone() 235 | .separated_by(just(Token::Comma)) 236 | .allow_trailing() 237 | .collect::>() 238 | .delimited_by( 239 | just(Token::Open(Delim::Paren)), 240 | just(Token::Close(Delim::Paren)), 241 | ) 242 | .or_not() 243 | ) 244 | .map_with_span(|(f, args), s| match args { 245 | Some(args) => (Expr::Call(boxspan(f), args), s), 246 | None => (f.0, f.1), 247 | }); 248 | 249 | let op = choice(( 250 | just(Token::Sub).to(UnaryOp::Neg), 251 | just(Token::Not).to(UnaryOp::Not), 252 | )); 253 | let unary = op 254 | .map_with_span(|op, s| (op, s)) 255 | .repeated() 256 | .foldr( 257 | call, 258 | |op, expr| { 259 | let span = op.1.start..expr.1.end; 260 | (Expr::Unary(op.0, boxspan(expr)), span.into()) 261 | }); 262 | 263 | let op = choice(( 264 | just(Token::Mul).to(BinaryOp::Mul), 265 | just(Token::Div).to(BinaryOp::Div), 266 | just(Token::Rem).to(BinaryOp::Rem), 267 | )); 268 | let product = unary.clone() 269 | .foldl( 270 | op.then(unary).repeated(), 271 | |a, (op, b)| { 272 | let span = a.1.start..b.1.end; 273 | (Expr::Binary(op, boxspan(a), boxspan(b)), span.into()) 274 | } 275 | ); 276 | 277 | let op = choice(( 278 | just(Token::Add).to(BinaryOp::Add), 279 | just(Token::Sub).to(BinaryOp::Sub), 280 | )); 281 | let sum = product.clone() 282 | .foldl( 283 | op.then(product).repeated(), 284 | |a, (op, b)| { 285 | let span = a.1.start..b.1.end; 286 | (Expr::Binary(op, boxspan(a), boxspan(b)), span.into()) 287 | } 288 | ); 289 | 290 | let op = choice(( 291 | just(Token::Eq).to(BinaryOp::Eq), 292 | just(Token::Ne).to(BinaryOp::Ne), 293 | just(Token::Lt).to(BinaryOp::Lt), 294 | just(Token::Le).to(BinaryOp::Le), 295 | just(Token::Gt).to(BinaryOp::Gt), 296 | just(Token::Ge).to(BinaryOp::Ge), 297 | )); 298 | let comparison = sum.clone() 299 | .foldl( 300 | op.then(sum).repeated(), 301 | |a, (op, b)| { 302 | let span = a.1.start..b.1.end; 303 | (Expr::Binary(op, boxspan(a), boxspan(b)), span.into()) 304 | } 305 | ); 306 | 307 | let op = choice(( 308 | just(Token::And).to(BinaryOp::And), 309 | just(Token::Or).to(BinaryOp::Or), 310 | )); 311 | let logical = comparison.clone() 312 | .foldl( 313 | op.then(comparison).repeated(), 314 | |a, (op, b)| { 315 | let span = a.1.start..b.1.end; 316 | (Expr::Binary(op, boxspan(a), boxspan(b)), span.into()) 317 | } 318 | ); 319 | 320 | let pipe = logical.clone() 321 | .foldl( 322 | just(Token::Pipe).to(BinaryOp::Pipe) 323 | .then(logical).repeated(), 324 | |a, (op, b)| { 325 | let span = a.1.start..b.1.end; 326 | (Expr::Binary(op, boxspan(a), boxspan(b)), span.into()) 327 | } 328 | ); 329 | 330 | pipe 331 | .labelled("expression") 332 | }) 333 | } 334 | 335 | pub fn type_parser<'tokens, 'src: 'tokens>() -> impl Parser< 336 | 'tokens, 337 | ParserInput<'tokens, 'src>, 338 | Type, 339 | extra::Err, Span>>, 340 | > + Clone { 341 | recursive(|ty| { 342 | let lit_ty = select! { 343 | Token::Ident("Bool") => Type::Bool, 344 | Token::Ident("Int") => Type::Int, 345 | Token::Ident("Str") => Type::Str, 346 | // TODO: Support type variables in both the parser and the type checker. 347 | Token::Ident(_) => Type::Var(69), 348 | Token::Unit => Type::Unit, 349 | }.validate(|tys, span, emitter| { 350 | if let Type::Var(_) = tys { 351 | emitter.emit(Rich::custom(span, 352 | "Type variables are not yet supported.".to_string() 353 | )); 354 | } 355 | tys 356 | }); 357 | 358 | let tys_paren = ty.clone() 359 | .separated_by(just(Token::Comma)) 360 | .allow_trailing() 361 | .collect::>() 362 | .delimited_by( 363 | just(Token::Open(Delim::Paren)), 364 | just(Token::Close(Delim::Paren)), 365 | ); 366 | 367 | let func = tys_paren.clone() 368 | .then_ignore(just(Token::Arrow)) 369 | .then(ty.clone()) 370 | .map(|(ta, tr)| Type::Func(ta, Box::new(tr))); 371 | 372 | let tuple = tys_paren 373 | .validate(|tys, span, emitter| { 374 | if tys.is_empty() { 375 | emitter.emit(Rich::custom(span, 376 | "Tuple must have at least one element. Use `()` for the unit type." 377 | .to_string() 378 | )); 379 | } 380 | tys 381 | }) 382 | .map(Type::Tuple); 383 | 384 | let array = ty.clone() 385 | .delimited_by( 386 | just(Token::Open(Delim::Brack)), 387 | just(Token::Close(Delim::Brack)), 388 | ) 389 | .map(|t| Type::Array(Box::new(t))); 390 | 391 | lit_ty 392 | .or(array) 393 | .or(func) 394 | .or(tuple) 395 | .boxed() 396 | .labelled("type") 397 | }) 398 | } 399 | 400 | pub fn exprs_parser<'tokens, 'src: 'tokens>() -> impl Parser< 401 | 'tokens, 402 | ParserInput<'tokens, 'src>, 403 | Vec>>, 404 | extra::Err, Span>>, 405 | > + Clone { 406 | expr_parser() 407 | .separated_by(just(Token::Semicolon)) 408 | .allow_trailing() 409 | .collect::>() 410 | } 411 | 412 | #[cfg(test)] 413 | mod tests { 414 | use super::*; 415 | 416 | #[test] 417 | fn test_type_parser() { 418 | let input = "(() -> () -> () -> (num)) -> bool"; 419 | let (ts, errs) = lexer().parse(input).into_output_errors(); 420 | 421 | assert!(ts.is_some()); 422 | assert!(errs.is_empty()); 423 | 424 | if let Some(ts) = ts { 425 | let (ast, parse_errs) = type_parser() 426 | .map_with_span(|ty, span| (ty, span)) 427 | .parse(ts.as_slice().spanned((input.len()..input.len()).into())) 428 | .into_output_errors(); 429 | 430 | println!("{:?}", ast); 431 | println!("{:?}", parse_errs); 432 | } 433 | } 434 | 435 | #[test] 436 | fn test_expr_parser_atom() { 437 | let input = " 438 | let id : (A) -> A = (\\x -> x) in { 439 | if false 440 | then id(3.14) 441 | else id(true); 442 | } 443 | "; 444 | let (ast, errs) = lexer().parse(input).into_output_errors(); 445 | 446 | assert!(ast.is_some()); 447 | assert!(errs.is_empty()); 448 | 449 | if let Some(ast) = ast { 450 | let (ast, parse_errs) = expr_parser() 451 | .map_with_span(|ty, span| (ty, span)) 452 | .parse(ast.as_slice().spanned((input.len()..input.len()).into())) 453 | .into_output_errors(); 454 | 455 | println!("{:?}", ast); 456 | println!("{:?}", parse_errs); 457 | } 458 | } 459 | } -------------------------------------------------------------------------------- /syntax/src/ty.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Display, Formatter}; 2 | 3 | // TODO: Introduce lifetime here to reduce cloning. 4 | #[derive(Clone, Debug, Eq, PartialEq)] 5 | pub enum Type { 6 | Unit, Bool, Int, Str, 7 | Var(usize), // This type is only used during type inference. 8 | Func(Vec, Box), 9 | Tuple(Vec), 10 | Array(Box), 11 | } 12 | 13 | impl Display for Type { 14 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 15 | match *self { 16 | Type::Unit => write!(f, "Unit"), 17 | Type::Bool => write!(f, "Bool"), 18 | Type::Int => write!(f, "Int"), 19 | Type::Str => write!(f, "Str"), 20 | Type::Var(id) => write!(f, "{}", itoa(id)), 21 | Type::Func(ref args, ref ret) => { 22 | write!(f, "({}", args[0])?; 23 | for arg in &args[1..] { 24 | write!(f, " {}", arg)?; 25 | } 26 | write!(f, ") -> {}", ret) 27 | } 28 | Type::Tuple(ref tys) => { 29 | write!(f, "({}", tys[0])?; 30 | for ty in &tys[1..] { 31 | write!(f, " {}", ty)?; 32 | } 33 | write!(f, ")") 34 | } 35 | Type::Array(ref ty) => write!(f, "[{}]", ty), 36 | } 37 | } 38 | } 39 | 40 | /// Convert a number to a string of lowercase letters 41 | pub fn itoa(i: usize) -> String { 42 | let mut s = String::new(); 43 | let mut i = i; 44 | 45 | while i >= 26 { 46 | s.push((b'A' + (i % 26) as u8) as char); 47 | i /= 26; 48 | } 49 | s.push((b'A' + i as u8) as char); 50 | s 51 | } -------------------------------------------------------------------------------- /typing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "typing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | chumsky = "1.0.0-alpha.3" 8 | syntax = { path = "../syntax" } 9 | -------------------------------------------------------------------------------- /typing/src/infer.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use chumsky::span::SimpleSpan; 3 | use syntax::{ 4 | expr::{ 5 | Lit, UnaryOp, BinaryOp, 6 | Expr, 7 | }, 8 | ty::*, 9 | }; 10 | 11 | use crate::rename::{rename_exprs, rename_type}; 12 | 13 | use super::typed::TExpr; 14 | 15 | macro_rules! ok { 16 | ($e:expr) => { 17 | ($e, vec![]) 18 | }; 19 | } 20 | 21 | macro_rules! unbox { 22 | ($e:expr) => { 23 | (*$e.0, $e.1) 24 | }; 25 | } 26 | 27 | #[derive(Clone, Debug)] 28 | pub enum InferErrorKind { 29 | Error, 30 | Hint, 31 | } 32 | 33 | #[derive(Clone, Debug)] 34 | pub struct InferError { 35 | pub title: String, 36 | pub labels: Vec<(String, InferErrorKind, SimpleSpan)>, 37 | pub span: SimpleSpan, 38 | } 39 | 40 | impl InferError { 41 | pub fn new>(title: S, span: SimpleSpan) -> Self { 42 | Self { 43 | title: title.into(), 44 | labels: Vec::new(), 45 | span, 46 | } 47 | } 48 | 49 | pub fn add_error>(mut self, reason: S, span: SimpleSpan) -> Self { 50 | self.labels.push((reason.into(), InferErrorKind::Error, span)); 51 | self 52 | } 53 | 54 | pub fn add_hint>(mut self, reason: S, span: SimpleSpan) -> Self { 55 | self.labels.push((reason.into(), InferErrorKind::Hint, span)); 56 | self 57 | } 58 | } 59 | 60 | #[derive(Clone, Debug, PartialEq)] 61 | struct Constraint { 62 | t1: Type, 63 | t2: Type, 64 | // Where the constraint was generated, for error reporting 65 | span: SimpleSpan, 66 | } 67 | 68 | impl Constraint { 69 | fn new(t1: Type, t2: Type, span: SimpleSpan) -> Self { 70 | Self { 71 | t1, 72 | t2, 73 | span, 74 | } 75 | } 76 | } 77 | 78 | #[derive(Clone, Debug)] 79 | struct Infer<'src> { 80 | env: HashMap<&'src str, Type>, 81 | subst: Vec, 82 | constraints: Vec, 83 | } 84 | 85 | impl<'src> Infer<'src> { 86 | fn new() -> Self { 87 | Infer { 88 | env: HashMap::new(), 89 | subst: Vec::new(), 90 | constraints: Vec::new(), 91 | } 92 | } 93 | 94 | /// Generate a fresh type variable 95 | fn fresh(&mut self) -> Type { 96 | let i = self.subst.len(); 97 | self.subst.push(Type::Var(i)); 98 | Type::Var(i) 99 | } 100 | 101 | /// Get a substitution for a type variable 102 | fn subst(&self, i: usize) -> Option { 103 | self.subst.get(i).cloned() 104 | } 105 | 106 | /// Add new constraint 107 | fn add_constraint(&mut self, c: Constraint) { 108 | self.constraints.push(c); 109 | } 110 | 111 | /// Check if a type variable occurs in a type 112 | fn occurs(&self, i: usize, t: Type) -> bool { 113 | use Type::*; 114 | match t { 115 | Unit | Bool | Int | Str => false, 116 | Var(j) => { 117 | if let Some(t) = self.subst(j) { 118 | if t != Var(j) { 119 | return self.occurs(i, t); 120 | } 121 | } 122 | i == j 123 | }, 124 | Func(args, ret) => { 125 | args.into_iter().any(|t| self.occurs(i, t)) || self.occurs(i, *ret) 126 | }, 127 | Tuple(tys) => tys.into_iter().any(|t| self.occurs(i, t)), 128 | Array(ty) => self.occurs(i, *ty), 129 | } 130 | } 131 | 132 | /// Unify two types 133 | fn unify(&mut self, c: Constraint) -> Result<(), InferError> { 134 | macro_rules! constraint { 135 | ($t1:expr, $t2:expr) => { 136 | Constraint::new($t1, $t2, c.span) 137 | }; 138 | } 139 | 140 | use Type::*; 141 | match (c.t1.clone(), c.t2.clone()) { 142 | // Literal types 143 | (Unit, Unit) 144 | | (Bool, Bool) 145 | | (Int, Int) 146 | | (Str, Str) => Ok(()), 147 | 148 | // Variable 149 | (Var(i), Var(j)) if i == j => Ok(()), // Same variables can be unified 150 | (Var(i), t2) => { 151 | // If the substitution is not the variable itself, 152 | // unify the substitution with t2 153 | if let Some(t) = self.subst(i) { 154 | if t != Var(i) { 155 | return self.unify(constraint!(t, t2)); 156 | } 157 | } 158 | // If the variable occurs in t2 159 | if self.occurs(i, t2.clone()) { 160 | return Err(InferError::new("Infinite type", c.span) 161 | .add_error(format!( 162 | "This type contains itself: {}", rename_type(Var(i)) 163 | ), c.span)); 164 | } 165 | // Set the substitution 166 | self.subst[i] = t2; 167 | Ok(()) 168 | }, 169 | (t1, Var(i)) => { 170 | if let Some(t) = self.subst(i) { 171 | if t != Var(i) { 172 | return self.unify(constraint!(t1, t)); 173 | } 174 | } 175 | if self.occurs(i, t1.clone()) { 176 | return Err(InferError::new("Infinite type", c.span) 177 | .add_error(format!( 178 | "This type contains itself: {}", 179 | rename_type(Var(i)) 180 | ), c.span)); 181 | } 182 | self.subst[i] = t1; 183 | Ok(()) 184 | }, 185 | 186 | // Function 187 | (Func(a1, r1), Func(a2, r2)) => { 188 | // Check the number of arguments 189 | if a1.len() != a2.len() { 190 | let e = InferError::new("Argument length mismatch", c.span) 191 | .add_error(format!( 192 | "This function is expected to take {} arguments, found {}", 193 | a2.len(), a1.len() 194 | ), c.span); 195 | return Err(e); 196 | } 197 | // Unify the arguments 198 | for (a1, a2) in a1.into_iter().zip(a2.into_iter()) { 199 | self.unify(constraint!(a1, a2))?; 200 | } 201 | // Unify the return types 202 | self.unify(constraint!(*r1, *r2)) 203 | }, 204 | 205 | // Tuple 206 | (Tuple(t1), Tuple(t2)) => { 207 | // Check the number of elements 208 | if t1.len() != t2.len() { 209 | return Err(InferError::new("Tuple length mismatch", c.span) 210 | .add_error(format!( 211 | "Expected {} elements, found {}", 212 | t1.len(), t2.len() 213 | ), c.span)); 214 | } 215 | // Unify the elements 216 | for (t1, t2) in t1.into_iter().zip(t2.into_iter()) { 217 | self.unify(constraint!(t1, t2))?; 218 | } 219 | Ok(()) 220 | }, 221 | 222 | // Array 223 | (Array(t1), Array(t2)) => self.unify(constraint!(*t1, *t2)), 224 | 225 | // The rest will be type mismatch 226 | (t1, t2) => Err(InferError::new("Type mismatch", c.span) 227 | .add_error(format!( 228 | "Expected {}, found {}", 229 | rename_type(t1), rename_type(t2) 230 | ), c.span)), 231 | } 232 | } 233 | 234 | /// Solve the constraints by unifying them 235 | fn solve(&mut self) -> Vec { 236 | let mut errors = Vec::new(); 237 | for c in self.constraints.clone().into_iter() { 238 | if let Err(e) = self.unify(c) { 239 | errors.push(e); 240 | } 241 | } 242 | errors 243 | } 244 | 245 | /// Substitute the type variables with the substitutions 246 | fn substitute(&mut self, t: Type) -> Type { 247 | use Type::*; 248 | match t { 249 | // Only match any type that can contain type variables 250 | Var(i) => { 251 | if let Some(t) = self.subst(i) { 252 | if t != Var(i) { 253 | return self.substitute(t); 254 | } 255 | } 256 | Var(i) 257 | }, 258 | Func(args, ret) => { 259 | Func( 260 | args.into_iter().map(|t| self.substitute(t)).collect(), 261 | Box::new(self.substitute(*ret)), 262 | ) 263 | }, 264 | Tuple(tys) => Tuple(tys.into_iter().map(|t| self.substitute(t)).collect()), 265 | Array(ty) => Array(Box::new(self.substitute(*ty))), 266 | // The rest will be returned as is 267 | _ => t, 268 | } 269 | } 270 | 271 | /// Find a type variable in (typed) expression and substitute them 272 | fn substitute_texp(&mut self, e: TExpr<'src>) -> TExpr<'src> { 273 | use TExpr::*; 274 | match e { 275 | Lit(_) | Ident(_) => e, 276 | Unary { op, expr: (e, lspan), ret_ty } => { 277 | Unary { 278 | op, 279 | expr: (Box::new(self.substitute_texp(*e)), lspan), 280 | ret_ty, 281 | } 282 | }, 283 | Binary { op, lhs: (lhs, lspan), rhs: (rhs, rspan), ret_ty } => { 284 | let lhst = self.substitute_texp(*lhs); 285 | let rhst = self.substitute_texp(*rhs); 286 | Binary { 287 | op, 288 | lhs: (Box::new(lhst), lspan), 289 | rhs: (Box::new(rhst), rspan), 290 | ret_ty: self.substitute(ret_ty), 291 | } 292 | }, 293 | Lambda { params, body: (body, bspan), ret_ty } => { 294 | let bodyt = self.substitute_texp(*body); 295 | let paramst = params.into_iter() 296 | .map(|(name, ty)| (name, self.substitute(ty))) 297 | .collect::>(); 298 | Lambda { 299 | params: paramst, 300 | body: (Box::new(bodyt), bspan), 301 | ret_ty: self.substitute(ret_ty), 302 | } 303 | }, 304 | Call { func: (func, fspan), args } => { 305 | let funct = self.substitute_texp(*func); 306 | let argst = args.into_iter() 307 | .map(|(arg, span)| (self.substitute_texp(arg), span)) 308 | .collect::>(); 309 | Call { 310 | func: (Box::new(funct), fspan), 311 | args: argst, 312 | } 313 | }, 314 | If { cond: (cond, cspan), t: (t, tspan), f: (f, fspan), br_ty } => { 315 | let condt = self.substitute_texp(*cond); 316 | let tt = self.substitute_texp(*t); 317 | let ft = self.substitute_texp(*f); 318 | If { 319 | cond: (Box::new(condt), cspan), 320 | t: (Box::new(tt), tspan), 321 | f: (Box::new(ft), fspan), 322 | br_ty, 323 | } 324 | }, 325 | Let { name, ty, value: (v, vspan), body: (b, bspan) } => { 326 | let vt = self.substitute_texp(*v); 327 | let bt = self.substitute_texp(*b); 328 | Let { 329 | name, 330 | ty: self.substitute(ty), 331 | value: (Box::new(vt), vspan), 332 | body: (Box::new(bt), bspan), 333 | } 334 | }, 335 | Define { name, ty, value: (v, vspan) } => { 336 | let vt = self.substitute_texp(*v); 337 | Define { 338 | name, 339 | ty: self.substitute(ty), 340 | value: (Box::new(vt), vspan), 341 | } 342 | }, 343 | Block { exprs, void, ret_ty } => { 344 | let exprst = exprs.into_iter() 345 | .map(|(e, span)| (self.substitute_texp(e), span)) 346 | .collect::>(); 347 | Block { 348 | exprs: exprst, 349 | void, 350 | ret_ty: self.substitute(ret_ty), 351 | } 352 | }, 353 | } 354 | } 355 | 356 | /// Infer the type of an expression 357 | fn infer( 358 | &mut self, e: (Expr<'src>, SimpleSpan), expected: Type 359 | ) -> (TExpr<'src>, Vec) { 360 | let span = e.1; 361 | macro_rules! constraint { 362 | ($ty:expr) => { 363 | self.add_constraint(Constraint::new(expected, $ty, span)) 364 | }; 365 | } 366 | 367 | match e.0 { 368 | // Literal values 369 | // Push the constraint (expected type to be the literal type) and 370 | // return the typed expression 371 | Expr::Lit(l) => match l { 372 | Lit::Unit => { 373 | constraint!(Type::Unit); 374 | ok!(TExpr::Lit(Lit::Unit)) 375 | } 376 | Lit::Bool(b) => { 377 | constraint!(Type::Bool); 378 | ok!(TExpr::Lit(Lit::Bool(b))) 379 | } 380 | Lit::Int(i) => { 381 | constraint!(Type::Int); 382 | ok!(TExpr::Lit(Lit::Int(i))) 383 | } 384 | Lit::Str(s) => { 385 | constraint!(Type::Str); 386 | ok!(TExpr::Lit(Lit::Str(s))) 387 | } 388 | } 389 | 390 | // Identifiers 391 | // The same as literals but the type is looked up in the environment 392 | Expr::Ident(ref x) => { 393 | if let Some(t) = self.env.get(x) { 394 | constraint!(t.clone()); 395 | ok!(TExpr::Ident(x)) 396 | } else { 397 | let kind = match &expected { 398 | Type::Func(_, _) => "function", 399 | _ => "value", 400 | }; 401 | (TExpr::Ident(x), vec![ 402 | InferError::new(format!("Undefined {}", kind), span) 403 | .add_error(format!("`{}` is not defined", x), span) 404 | ]) 405 | } 406 | } 407 | 408 | // Unary & binary operators 409 | // The type of the left and right hand side are inferred and 410 | // the expected type is determined by the operator 411 | Expr::Unary(op, e) => match op { 412 | // Numeric operators (Int -> Int) 413 | UnaryOp::Neg => { 414 | let (te, err) = self.infer(unbox!(e), Type::Int); 415 | constraint!(Type::Int); 416 | (TExpr::Unary { 417 | op, 418 | expr: (Box::new(te), span), 419 | ret_ty: Type::Int, 420 | }, err) 421 | }, 422 | // Boolean operators (Bool -> Bool) 423 | UnaryOp::Not => { 424 | let (te, err) = self.infer(unbox!(e), Type::Bool); 425 | constraint!(Type::Bool); 426 | (TExpr::Unary { 427 | op, 428 | expr: (Box::new(te), span), 429 | ret_ty: Type::Bool, 430 | }, err) 431 | }, 432 | } 433 | Expr::Binary(op, lhs, rhs) => match op { 434 | // Numeric operators (Int -> Int -> Int) 435 | BinaryOp::Add 436 | | BinaryOp::Sub 437 | | BinaryOp::Mul 438 | | BinaryOp::Div 439 | | BinaryOp::Rem 440 | => { 441 | let (lt, mut errs0) = self.infer(unbox!(lhs), Type::Int); 442 | let (rt, errs1) = self.infer(unbox!(rhs), Type::Int); 443 | errs0.extend(errs1); 444 | constraint!(Type::Int); 445 | (TExpr::Binary { 446 | op, 447 | lhs: (Box::new(lt), lhs.1), 448 | rhs: (Box::new(rt), rhs.1), 449 | ret_ty: Type::Int, 450 | }, errs0) 451 | }, 452 | // Boolean operators (Bool -> Bool -> Bool) 453 | BinaryOp::And 454 | | BinaryOp::Or 455 | => { 456 | let (lt, mut errs0) = self.infer(unbox!(lhs), Type::Bool); 457 | let (rt, errs1) = self.infer(unbox!(rhs), Type::Bool); 458 | errs0.extend(errs1); 459 | constraint!(Type::Bool); 460 | (TExpr::Binary { 461 | op, 462 | lhs: (Box::new(lt), lhs.1), 463 | rhs: (Box::new(rt), rhs.1), 464 | ret_ty: Type::Bool, 465 | }, errs0) 466 | }, 467 | // Comparison operators ('a -> 'a -> Bool) 468 | BinaryOp::Eq 469 | | BinaryOp::Ne 470 | | BinaryOp::Lt 471 | | BinaryOp::Le 472 | | BinaryOp::Gt 473 | | BinaryOp::Ge 474 | => { 475 | // Create a fresh type variable and then use it as the 476 | // expected type for both the left and right hand side 477 | // so the type on both side have to be the same 478 | let t = self.fresh(); 479 | let (lt, mut errs0) = self.infer(unbox!(lhs), t.clone()); 480 | let (rt, errs1) = self.infer(unbox!(rhs), t); 481 | errs0.extend(errs1); 482 | constraint!(Type::Bool); 483 | (TExpr::Binary { 484 | op, 485 | lhs: (Box::new(lt), lhs.1), 486 | rhs: (Box::new(rt), rhs.1), 487 | ret_ty: Type::Bool, 488 | }, errs0) 489 | }, 490 | 491 | BinaryOp::Pipe => { 492 | // Since this is parsed with a fold left, the right hand 493 | // side should always be a function 494 | let t = self.fresh(); 495 | let (lt, mut errs0) = self.infer(unbox!(lhs), t.clone()); 496 | // The right hand side should be a function that takes 497 | // 1 argument with the type of t 498 | let (rt, errs1) = self.infer( 499 | unbox!(rhs), 500 | Type::Func(vec![t.clone()], Box::new(t.clone())), 501 | ); 502 | errs0.extend(errs1); 503 | constraint!(t.clone()); 504 | (TExpr::Binary { 505 | op, 506 | lhs: (Box::new(lt), lhs.1), 507 | rhs: (Box::new(rt), rhs.1), 508 | ret_ty: t, 509 | }, errs0) 510 | }, 511 | } 512 | 513 | // Lambda 514 | Expr::Lambda(args, ret, b) => { 515 | // Get the return type or create a fresh type variable 516 | let rt = ret.unwrap_or(self.fresh()); 517 | // Fill in the type of the arguments with a fresh type 518 | let xs = args.into_iter() 519 | .map(|(x, t)| (x, t.unwrap_or(self.fresh()))) 520 | .collect::>(); 521 | 522 | // Create a new environment, and add the arguments to it 523 | // and use the new environment to infer the body 524 | let mut env = self.env.clone(); 525 | xs.clone().into_iter().for_each(|(x, t)| { env.insert(x, t); }); 526 | let mut inf = self.clone(); 527 | inf.env = env; 528 | let (bt, errs) = inf.infer(unbox!(b), rt.clone()); 529 | 530 | // Add the substitutions & constraints from the body 531 | // if it doesn't already exist 532 | for s in inf.subst { 533 | if !self.subst.contains(&s) { 534 | self.subst.push(s); 535 | } 536 | } 537 | for c in inf.constraints { 538 | if !self.constraints.contains(&c) { 539 | self.constraints.push(c); 540 | } 541 | } 542 | 543 | // Push the constraints 544 | constraint!(Type::Func( 545 | xs.clone().into_iter() 546 | .map(|x| x.1) 547 | .collect(), 548 | Box::new(rt.clone()), 549 | )); 550 | 551 | (TExpr::Lambda { 552 | params: xs, 553 | body: (Box::new(bt), b.1), 554 | ret_ty: rt, 555 | }, errs) 556 | }, 557 | 558 | // Call 559 | Expr::Call(f, args) => { 560 | // Generate fresh types for the arguments 561 | let freshes = args.clone().into_iter() 562 | .map(|_| self.fresh()) 563 | .collect::>(); 564 | // Create a function type 565 | let fsig = Type::Func( 566 | freshes.clone(), 567 | Box::new(expected), 568 | ); 569 | // Expect the function to have the function type 570 | let (ft, mut errs) = self.infer(unbox!(f), fsig); 571 | // Infer the arguments 572 | let (xs, xerrs) = args.into_iter() 573 | .zip(freshes.into_iter()) 574 | .map(|(x, t)| { 575 | let span = x.1; 576 | let (xt, err) = self.infer(x, t); 577 | ((xt, span), err) 578 | }) 579 | // Flatten errors 580 | .fold((vec![], vec![]), |(mut xs, mut errs), ((x, span), err)| { 581 | xs.push((x, span)); 582 | errs.extend(err); 583 | (xs, errs) 584 | }); 585 | errs.extend(xerrs); 586 | 587 | (TExpr::Call { 588 | func: (Box::new(ft), f.1), 589 | args: xs, 590 | }, errs) 591 | }, 592 | 593 | // If 594 | Expr::If { cond, t, f } => { 595 | // Condition has to be a boolean 596 | let (ct, mut errs) = self.infer(unbox!(cond), Type::Bool); 597 | // The type of the if expression is the same as the 598 | // expected type 599 | let (tt, terrs) = self.infer(unbox!(t), expected.clone()); 600 | let (ft, ferrs) = self.infer(unbox!(f), expected.clone()); 601 | errs.extend(terrs); 602 | errs.extend(ferrs); 603 | 604 | (TExpr::If { 605 | cond: (Box::new(ct), cond.1), 606 | t: (Box::new(tt), t.1), 607 | f: (Box::new(ft), f.1), 608 | br_ty: expected, 609 | }, errs) 610 | }, 611 | 612 | // Let & define 613 | Expr::Let { name, ty, value, body } => { 614 | // Infer the type of the value 615 | let ty = ty.unwrap_or(self.fresh()); 616 | let (vt, mut errs) = self.infer(unbox!(value), ty.clone()); 617 | 618 | // Create a new environment and add the binding to it 619 | // and then use the new environment to infer the body 620 | let mut env = self.env.clone(); 621 | env.insert(name.clone(), ty.clone()); 622 | let mut inf = Infer::new(); 623 | inf.env = env; 624 | let (bt, berrs) = inf.infer(unbox!(body), expected.clone()); 625 | errs.extend(berrs); 626 | 627 | for s in inf.subst { 628 | if !self.subst.contains(&s) { 629 | self.subst.push(s); 630 | } 631 | } 632 | for c in inf.constraints { 633 | if !self.constraints.contains(&c) { 634 | self.constraints.push(c); 635 | } 636 | } 637 | 638 | (TExpr::Let { 639 | name, ty, 640 | value: (Box::new(vt), value.1), 641 | body: (Box::new(bt), body.1), 642 | }, errs) 643 | }, 644 | Expr::Define { name, ty, value } => { 645 | let ty = ty.unwrap_or(self.fresh()); 646 | self.env.insert(name.clone(), ty.clone()); 647 | let (val_ty, errs) = self.infer(unbox!(value), ty.clone()); 648 | 649 | constraint!(Type::Unit); 650 | 651 | (TExpr::Define { 652 | name, 653 | ty, 654 | value: (Box::new(val_ty), value.1), 655 | }, errs) 656 | }, 657 | 658 | // Block 659 | Expr::Block { exprs, void } => { 660 | // Infer the type of each expression 661 | let mut last = None; 662 | let len = exprs.len(); 663 | let (texprs, errs) = exprs.into_iter() 664 | .enumerate() 665 | .map(|(i, x)| { 666 | let span = x.1; 667 | let t = self.fresh(); 668 | let (xt, err) = self.infer(unbox!(x), t.clone()); 669 | // Save the type of the last expression 670 | if i == len - 1 { 671 | last = Some(t); 672 | } 673 | ((xt, span), err) 674 | }) 675 | .fold((vec![], vec![]), |(mut xs, mut errs), ((x, span), err)| { 676 | xs.push((x, span)); 677 | errs.extend(err); 678 | (xs, errs) 679 | }); 680 | 681 | let rt = if void || last.is_none() { 682 | // If the block is void or there is no expression, 683 | // the return type is unit 684 | constraint!(Type::Unit); 685 | Type::Unit 686 | } else { 687 | // Otherwise, the return type is the same as the expected type 688 | // constraint!(last.unwrap()); 689 | self.add_constraint(Constraint::new(expected.clone(), last.unwrap(), span)); 690 | expected 691 | }; 692 | 693 | (TExpr::Block { 694 | exprs: texprs, 695 | void, 696 | ret_ty: rt, 697 | }, errs) 698 | }, 699 | } 700 | } 701 | } 702 | 703 | /// Infer a list of expressions 704 | pub fn infer_exprs(es: Vec<(Expr, SimpleSpan)>) -> (Vec<(TExpr, SimpleSpan)>, Vec) { 705 | let mut inf = Infer::new(); 706 | // Type expressions 707 | let mut tes = vec![]; 708 | // Unsubstituted typed expressions 709 | let mut errors = vec![]; 710 | 711 | for e in es { 712 | let span = e.1; 713 | let fresh = inf.fresh(); 714 | // Infer the types 715 | let (te, err) = inf.infer(e, fresh); 716 | 717 | // Push the expression to the list 718 | tes.push((te.clone(), span)); 719 | 720 | if !err.is_empty() { 721 | errors.extend(err); 722 | } 723 | } 724 | 725 | let solve_errors = inf.solve(); 726 | if !solve_errors.is_empty() { 727 | errors.extend(solve_errors); 728 | } else { 729 | // Substitute the types 730 | tes = tes.into_iter() 731 | .map(|(te, s)| (inf.substitute_texp(te), s)) 732 | .collect(); 733 | } 734 | 735 | (rename_exprs(tes), errors) 736 | } -------------------------------------------------------------------------------- /typing/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod infer; 2 | pub mod rename; 3 | pub mod typed; -------------------------------------------------------------------------------- /typing/src/rename.rs: -------------------------------------------------------------------------------- 1 | use chumsky::span::SimpleSpan; 2 | use syntax::ty::Type; 3 | 4 | use crate::typed::TExpr; 5 | 6 | /// A renamer to rename type variables to a "minimized" form for more readable output 7 | pub struct Renamer { 8 | // Type variables encountered so far 9 | vars: Vec, 10 | } 11 | 12 | impl<'src> Renamer { 13 | pub fn new() -> Self { 14 | Self { 15 | vars: vec![], 16 | } 17 | } 18 | 19 | fn rename_var(&self, i: usize) -> Type { 20 | let n = self.vars.iter().position(|x| x == &i).unwrap(); 21 | Type::Var(n) 22 | } 23 | 24 | fn add_var(&mut self, i: usize) { 25 | if !self.vars.contains(&i) { 26 | self.vars.push(i); 27 | } 28 | } 29 | 30 | fn find_var(&mut self, t: Type) { 31 | match t { 32 | Type::Var(i) => { 33 | self.add_var(i); 34 | }, 35 | Type::Func(args, ret) => { 36 | args.into_iter().for_each(|t| self.find_var(t)); 37 | self.find_var(*ret); 38 | }, 39 | Type::Tuple(tys) => { 40 | tys.into_iter().for_each(|t| self.find_var(t)); 41 | }, 42 | Type::Array(ty) => { 43 | self.find_var(*ty); 44 | }, 45 | _ => {}, 46 | } 47 | } 48 | 49 | fn traverse(&mut self, e: TExpr) { 50 | match e { 51 | TExpr::Unary { expr, ret_ty, ..} => { 52 | self.traverse(*expr.0); 53 | self.find_var(ret_ty); 54 | }, 55 | TExpr::Binary { lhs, rhs, ret_ty, ..} => { 56 | self.traverse(*lhs.0); 57 | self.traverse(*rhs.0); 58 | self.find_var(ret_ty); 59 | }, 60 | TExpr::Lambda { params, body, ret_ty } => { 61 | for (_, t) in params { self.find_var(t); } 62 | self.find_var(ret_ty); 63 | self.traverse(*body.0); 64 | }, 65 | TExpr::Call { func, args } => { 66 | self.traverse(*func.0); 67 | for arg in args { 68 | self.traverse(arg.0); 69 | } 70 | }, 71 | TExpr::Let { ty, value, body, .. } => { 72 | self.find_var(ty); 73 | self.traverse(*value.0); 74 | self.traverse(*body.0); 75 | }, 76 | TExpr::Define { ty, value, .. } => { 77 | self.find_var(ty); 78 | self.traverse(*value.0); 79 | }, 80 | TExpr::Block { exprs, ret_ty, .. } => { 81 | for expr in exprs { 82 | self.traverse(expr.0); 83 | } 84 | self.find_var(ret_ty); 85 | }, 86 | _ => {}, 87 | } 88 | } 89 | 90 | fn rename_type(&self, t: Type) -> Type { 91 | match t { 92 | Type::Var(i) => self.rename_var(i), 93 | Type::Func(args, ret) => { 94 | Type::Func( 95 | args.into_iter().map(|x| self.rename_type(x)).collect(), 96 | Box::new(self.rename_type(*ret)), 97 | ) 98 | }, 99 | Type::Tuple(tys) => { 100 | Type::Tuple(tys.into_iter().map(|x| self.rename_type(x)).collect()) 101 | }, 102 | Type::Array(ty) => { 103 | Type::Array(Box::new(self.rename_type(*ty))) 104 | }, 105 | _ => t, 106 | } 107 | } 108 | 109 | fn rename_texp(&self, e: TExpr<'src>) -> TExpr<'src> { 110 | match e { 111 | TExpr::Unary { op, expr, ret_ty } => { 112 | TExpr::Unary { 113 | op, 114 | expr: (Box::new(self.rename_texp(*expr.0)), expr.1), 115 | ret_ty: self.rename_type(ret_ty) 116 | } 117 | }, 118 | TExpr::Binary { op, lhs, rhs, ret_ty } => { 119 | TExpr::Binary { 120 | op, 121 | lhs: (Box::new(self.rename_texp(*lhs.0)), lhs.1), 122 | rhs: (Box::new(self.rename_texp(*rhs.0)), rhs.1), 123 | ret_ty: self.rename_type(ret_ty) 124 | } 125 | }, 126 | TExpr::Lambda { params, body, ret_ty } => { 127 | TExpr::Lambda { 128 | params: params.into_iter() 129 | .map(|(x, t)| (x, self.rename_type(t))) 130 | .collect(), 131 | body: (Box::new(self.rename_texp(*body.0)), body.1), 132 | ret_ty: self.rename_type(ret_ty) 133 | } 134 | }, 135 | TExpr::Call { func, args } => { 136 | TExpr::Call { 137 | func: (Box::new(self.rename_texp(*func.0)), func.1), 138 | args: args.into_iter() 139 | .map(|x| (self.rename_texp(x.0), x.1)) 140 | .collect() 141 | } 142 | }, 143 | TExpr::Let { name, ty, value, body } => { 144 | TExpr::Let { 145 | name, 146 | ty: self.rename_type(ty), 147 | value: (Box::new(self.rename_texp(*value.0)), value.1), 148 | body: (Box::new(self.rename_texp(*body.0)), body.1) 149 | } 150 | }, 151 | TExpr::Define { name, ty, value } => { 152 | TExpr::Define { 153 | name, 154 | ty: self.rename_type(ty), 155 | value: (Box::new(self.rename_texp(*value.0)), value.1) 156 | } 157 | }, 158 | TExpr::Block { exprs, void, ret_ty } => { 159 | TExpr::Block { 160 | exprs: exprs.into_iter() 161 | .map(|x| (self.rename_texp(x.0), x.1)) 162 | .collect(), 163 | void, 164 | ret_ty: self.rename_type(ret_ty) 165 | } 166 | }, 167 | _ => e, 168 | } 169 | } 170 | } 171 | 172 | pub fn rename_type(t: Type) -> Type { 173 | let mut renamer = Renamer::new(); 174 | renamer.find_var(t.clone()); 175 | renamer.rename_type(t) 176 | } 177 | 178 | pub fn rename_exprs(es: Vec<(TExpr, SimpleSpan)>) -> Vec<(TExpr, SimpleSpan)> { 179 | let mut renamer = Renamer::new(); 180 | es.clone().into_iter() 181 | .for_each(|e| renamer.traverse(e.0)); 182 | es.into_iter() 183 | .map(|(e, s)| (renamer.rename_texp(e), s)) 184 | .collect() 185 | } -------------------------------------------------------------------------------- /typing/src/typed.rs: -------------------------------------------------------------------------------- 1 | use syntax::{ 2 | expr::{ 3 | BinaryOp, 4 | UnaryOp, 5 | Lit, 6 | Spanned, 7 | }, 8 | ty::Type, 9 | }; 10 | 11 | // Typed version of the expression. 12 | #[derive(Clone, Debug)] 13 | pub enum TExpr<'src> { 14 | Lit(Lit<'src>), 15 | Ident(&'src str), 16 | 17 | Unary { 18 | op: UnaryOp, 19 | expr: Spanned>, 20 | ret_ty: Type, 21 | }, 22 | Binary { 23 | op: BinaryOp, 24 | lhs: Spanned>, 25 | rhs: Spanned>, 26 | ret_ty: Type, 27 | }, 28 | 29 | Lambda { 30 | params: Vec<(&'src str, Type)>, 31 | body: Spanned>, 32 | ret_ty: Type, 33 | }, 34 | Call { 35 | func: Spanned>, 36 | args: Vec>, 37 | }, 38 | If { 39 | cond: Spanned>, 40 | t: Spanned>, 41 | f: Spanned>, 42 | br_ty: Type, 43 | }, 44 | Let { 45 | name: &'src str, 46 | ty: Type, 47 | value: Spanned>, 48 | body: Spanned>, 49 | }, 50 | Define { 51 | name: &'src str, 52 | ty: Type, 53 | value: Spanned>, 54 | }, 55 | Block { 56 | exprs: Vec>, 57 | void: bool, 58 | ret_ty: Type, 59 | }, 60 | } --------------------------------------------------------------------------------