├── .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 | }
--------------------------------------------------------------------------------