From 019bc881860cffee5f6380b527b8f33e130e269e Mon Sep 17 00:00:00 2001 From: azur Date: Sun, 23 Apr 2023 17:13:06 +0700 Subject: [PATCH] We are so back part 2 --- Cargo.lock | 299 ++++++++++++++++++++++++++++++++++++++++++- bin/Cargo.toml | 1 + bin/src/args.rs | 12 ++ bin/src/main.rs | 53 +++----- syntax/Cargo.toml | 2 +- syntax/src/parser.rs | 38 ++++-- test.hlm | 47 +------ typing/src/infer.rs | 270 ++++++++++++++++++++------------------ typing/src/typed.rs | 1 - 9 files changed, 510 insertions(+), 213 deletions(-) create mode 100644 bin/src/args.rs diff --git a/Cargo.lock b/Cargo.lock index 0e3d12e..177147d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,6 +13,55 @@ dependencies = [ "version_check", ] +[[package]] +name = "anstream" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "ariadne" version = "0.2.0" @@ -29,10 +78,17 @@ version = "0.1.0" dependencies = [ "ariadne", "chumsky", + "clap", "syntax", "typing", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "cc" version = "1.0.79" @@ -55,6 +111,75 @@ dependencies = [ "stacker", ] +[[package]] +name = "clap" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "hashbrown" version = "0.13.2" @@ -65,10 +190,51 @@ dependencies = [ ] [[package]] -name = "libc" -version = "0.2.140" +name = "heck" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi", + "io-lifetimes", + "rustix", + "windows-sys", +] + +[[package]] +name = "libc" +version = "0.2.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" + +[[package]] +name = "linux-raw-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" [[package]] name = "once_cell" @@ -76,6 +242,15 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + [[package]] name = "psm" version = "0.1.21" @@ -85,6 +260,29 @@ dependencies = [ "cc", ] +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustix" +version = "0.37.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "stacker" version = "0.1.15" @@ -98,6 +296,23 @@ dependencies = [ "winapi", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syntax" version = "0.1.0" @@ -113,12 +328,24 @@ dependencies = [ "syntax", ] +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + [[package]] name = "unicode-width" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "version_check" version = "0.9.4" @@ -147,6 +374,72 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "yansi" version = "0.5.1" diff --git a/bin/Cargo.toml b/bin/Cargo.toml index 5a8dc75..a02598e 100644 --- a/bin/Cargo.toml +++ b/bin/Cargo.toml @@ -6,5 +6,6 @@ edition = "2021" [dependencies] ariadne = "0.2.0" chumsky = "1.0.0-alpha.3" +clap = { version = "4.2.4", features = ["derive"] } syntax = { path = "../syntax" } typing = { path = "../typing" } diff --git a/bin/src/args.rs b/bin/src/args.rs new file mode 100644 index 0000000..485fbd3 --- /dev/null +++ b/bin/src/args.rs @@ -0,0 +1,12 @@ +use clap::Parser; + +#[derive(Debug, Parser)] +pub struct Args { + /// The path to the file to be compiled. + #[arg(required = true)] + pub file: String, +} + +pub fn get_args() -> Args { + Args::parse() +} \ No newline at end of file diff --git a/bin/src/main.rs b/bin/src/main.rs index d1e89d0..6b7f155 100644 --- a/bin/src/main.rs +++ b/bin/src/main.rs @@ -3,48 +3,37 @@ use chumsky::{Parser, prelude::Input}; use syntax::parser::{lexer, exprs_parser}; use typing::infer::infer_exprs; +pub mod args; + fn main() { - let src = " - let r = { - let x = - if 0 == 1 - then { - let x = true; - if x then 1 else 2 - } - else 34 + { - let foo = 30 in - foo + 5 - }; - let y = { 1 } * 2; - if 1 + 1 == 2 - then x - else y - }; - ".to_string(); - let filename = "?".to_string(); + let args = args::get_args(); + let filename = args.file.clone(); + let src = std::fs::read_to_string(&args.file).expect("file not found"); let (ts, errs) = lexer().parse(&src).into_output_errors(); - let parse_errs = if let Some(tokens) = &ts { + let (ast, parse_errs) = if let Some(tokens) = &ts { let (ast, parse_errs) = exprs_parser() .map_with_span(|ast, span| (ast, span)) .parse(tokens.as_slice().spanned((src.len()..src.len()).into())) .into_output_errors(); - if let Some(ast) = ast.filter(|_| errs.len() + parse_errs.len() == 0) { - let (ast, e) = infer_exprs(ast.0); - if !e.is_empty() { - println!("{:?}", e); - } - if !ast.is_empty() { - println!("{:?}", ast); - } - } - - parse_errs + (ast, parse_errs) } else { - Vec::new() + (None, vec![]) + }; + + let (_typed_ast, _type_errs) = if let Some(ast) = ast.filter(|_| errs.len() + parse_errs.len() == 0) { + let (ast, e) = infer_exprs(ast.0); + if !e.is_empty() { + e.iter().for_each(|e| println!("{e:?}")); + } + if !ast.is_empty() { + ast.iter().for_each(|(e, _)| println!("{e:?}")); + } + (Some(ast), e) + } else { + (None, vec![]) }; errs.into_iter() diff --git a/syntax/Cargo.toml b/syntax/Cargo.toml index 65cdf95..b332876 100644 --- a/syntax/Cargo.toml +++ b/syntax/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -chumsky = "1.0.0-alpha.3" +chumsky = { version = "1.0.0-alpha.3", features = ["label"] } diff --git a/syntax/src/parser.rs b/syntax/src/parser.rs index 2bd49f1..dba1bcd 100644 --- a/syntax/src/parser.rs +++ b/syntax/src/parser.rs @@ -15,10 +15,20 @@ pub fn lexer<'src>() -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, e .then_ignore(just('"')) .map_slice(Token::Str); - let word = text::ident().map(|s: &str| match s { + fn id_filter(c: &C) -> bool where C: text::Char { + c.to_char().is_ascii_alphabetic() + || "_'".contains(c.to_char()) + } + let id = any() + .filter(id_filter) + .then(any() + .filter(id_filter) + .repeated()) + .slice(); + + let word = id.map(|s: &str| match s { "true" => Token::Bool(true), "false" => Token::Bool(false), - "unit" => Token::Unit, "let" => Token::Let, "in" => Token::In, "func" => Token::Func, @@ -30,6 +40,7 @@ pub fn lexer<'src>() -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, e }); let sym = choice(( + just("()").to(Token::Unit), just("\\").to(Token::Lambda), just("->").to(Token::Arrow), @@ -72,7 +83,7 @@ pub fn lexer<'src>() -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, e )); token - .map_with_span(|tok, span| (tok, span)) + .map_with_span(move |tok, span| (tok, span)) .padded() // If we get an error, skip to the next character and try again. .recover_with(skip_then_retry_until(any().ignored(), end())) @@ -297,8 +308,8 @@ pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser< } ); - #[allow(clippy::let_and_return)] logical + .labelled("expression") }) } @@ -310,11 +321,20 @@ pub fn type_parser<'tokens, 'src: 'tokens>() -> impl Parser< > + Clone { recursive(|ty| { let lit_ty = select! { - Token::Ident("bool") => Type::Bool, - Token::Ident("num") => Type::Num, - Token::Ident("str") => Type::Str, + Token::Ident("Bool") => Type::Bool, + Token::Ident("Num") => Type::Num, + Token::Ident("Str") => Type::Str, + // TODO: Support type variables in both the parser and the type checker. + Token::Ident(_) => Type::Var(69), Token::Unit => Type::Unit, - }; + }.validate(|tys, span, emitter| { + if let Type::Var(_) = tys { + emitter.emit(Rich::custom(span, + "Type variables are not yet supported.".to_string() + )); + } + tys + }); let tys_paren = ty.clone() .separated_by(just(Token::Comma)) @@ -351,6 +371,8 @@ pub fn type_parser<'tokens, 'src: 'tokens>() -> impl Parser< .or(array) .or(func) .or(tuple) + .boxed() + .labelled("type") }) } diff --git a/test.hlm b/test.hlm index c085fa3..306b566 100644 --- a/test.hlm +++ b/test.hlm @@ -1,46 +1 @@ --- Source - -fun make_add() : (num, num) -> num = - \a, b -> num = { - println(a + b); - return a + b; - }; - -let add = make_add(); - -fun main() = { - let foo = 34; - - let bar = 35 in { - let r = add(foo, bar); - - match r - | 69 -> println(r); - | 42 -> println("What"); - | _ -> println("Unreachable"); - }; -}; - ---- Alpha -Convert `match` to `if-else` -Convert `let` to `call` ---- - -(fun make_add [] - (lambda [a b] (do - (println (+ a b)) - (+ a b) - ))) - -(fun add [a b] (+ a b)) - -(fun main [] (do - (def foo 34) - - ((lambda [bar] (do - (def r (add foo bar)) - (if (= r 69) (println r) - (= r 42) (println "What") - true (println "Unreachable")) - )) bar) -)) +let f = \f, g, h, a, b, c, d = ; \ No newline at end of file diff --git a/typing/src/infer.rs b/typing/src/infer.rs index 8d43b62..7e3212e 100644 --- a/typing/src/infer.rs +++ b/typing/src/infer.rs @@ -10,12 +10,27 @@ use syntax::{ use super::typed::TExpr; +macro_rules! ok { + ($e:expr) => { + ($e, vec![]) + }; +} + macro_rules! unbox { ($e:expr) => { (*$e.0, $e.1) }; } +#[derive(Clone, Debug)] +pub enum InferError<'src> { + UnboundVariable(&'src str, SimpleSpan), + UnboundFunction(&'src str, SimpleSpan), + InfiniteType(Type, Type), + LengthMismatch(Type, Type), + TypeMismatch(Type, Type), +} + #[derive(Clone, Debug)] struct Infer<'src> { env: HashMap<&'src str, Type>, @@ -71,7 +86,7 @@ impl<'src> Infer<'src> { } /// Unify two types - fn unify(&mut self, t1: Type, t2: Type) -> Result<(), String> { + fn unify(&mut self, t1: Type, t2: Type) -> Result<(), InferError<'src>> { use Type::*; match (t1, t2) { // Literal types @@ -92,7 +107,7 @@ impl<'src> Infer<'src> { } // If the variable occurs in t2 if self.occurs(i, t2.clone()) { - return Err(format!("Infinite type: '{} = {}", itoa(i), t2)); + return Err(InferError::InfiniteType(Var(i), t2)); } // Set the substitution self.subst[i] = t2; @@ -105,7 +120,7 @@ impl<'src> Infer<'src> { } } if self.occurs(i, t1.clone()) { - return Err(format!("Infinite type: '{} = {}", itoa(i), t1)); + return Err(InferError::InfiniteType(Var(i), t1)); } self.subst[i] = t1; Ok(()) @@ -115,7 +130,7 @@ impl<'src> Infer<'src> { (Func(a1, r1), Func(a2, r2)) => { // Check the number of arguments if a1.len() != a2.len() { - return Err(format!("Function argument mismatch: {} != {}", a1.len(), a2.len())); + return Err(InferError::LengthMismatch(Func(a1, r1), Func(a2, r2))); } // Unify the arguments for (a1, a2) in a1.into_iter().zip(a2.into_iter()) { @@ -129,7 +144,7 @@ impl<'src> Infer<'src> { (Tuple(t1), Tuple(t2)) => { // Check the number of elements if t1.len() != t2.len() { - return Err(format!("Tuple element mismatch: {} != {}", t1.len(), t2.len())); + return Err(InferError::LengthMismatch(Tuple(t1), Tuple(t2))); } // Unify the elements for (t1, t2) in t1.into_iter().zip(t2.into_iter()) { @@ -142,12 +157,12 @@ impl<'src> Infer<'src> { (Array(t1), Array(t2)) => self.unify(*t1, *t2), // The rest will be type mismatch - (t1, t2) => Err(format!("Type mismatch: {} != {}", t1, t2)), + (t1, t2) => Err(InferError::TypeMismatch(t1, t2)), } } /// Solve the constraints by unifying them - fn solve(&mut self) -> Result<(), String> { + fn solve(&mut self) -> Result<(), InferError<'src>> { for (t1, t2, _span) in self.constraints.clone().into_iter() { self.unify(t1, t2)?; } @@ -266,30 +281,45 @@ impl<'src> Infer<'src> { } /// Infer the type of an expression - fn infer(&mut self, e: (Expr<'src>, SimpleSpan), expected: Type) -> Result, String> { - let (e, span) = e; - match e { + fn infer( + &mut self, e: (Expr<'src>, SimpleSpan), expected: Type + ) -> (TExpr<'src>, Vec>) { + let span = e.1; + match e.0 { // Literal values // Push the constraint (expected type to be the literal type) and // return the typed expression - Expr::Lit(l) => { - let t = match l { - Lit::Unit => Type::Unit, - Lit::Bool(_) => Type::Bool, - Lit::Num(_) => Type::Num, - Lit::Str(_) => Type::Str, - }; - self.add_constraint(expected, t, span); - Ok(TExpr::Lit(l)) - }, + Expr::Lit(l) => match l { + Lit::Unit => { + self.add_constraint(expected, Type::Unit, span); + ok!(TExpr::Lit(Lit::Unit)) + } + Lit::Bool(b) => { + self.add_constraint(expected, Type::Bool, span); + ok!(TExpr::Lit(Lit::Bool(b))) + } + Lit::Num(i) => { + self.add_constraint(expected, Type::Num, span); + ok!(TExpr::Lit(Lit::Num(i))) + } + Lit::Str(s) => { + self.add_constraint(expected, Type::Str, span); + ok!(TExpr::Lit(Lit::Str(s))) + } + } // Identifiers // The same as literals but the type is looked up in the environment Expr::Ident(ref x) => { - let t = self.env.get(x) - .ok_or(format!("Unbound variable: {}", x))?; - self.add_constraint(expected, t.clone(), span); - Ok(TExpr::Ident(x.clone())) + if let Some(t) = self.env.get(x) { + self.add_constraint(expected, t.clone(), span); + ok!(TExpr::Ident(x)) + } else { + (TExpr::Ident(x), vec![match expected { + Type::Func(_, _) => InferError::UnboundFunction(x, span), + _ => InferError::UnboundVariable(x, span), + }]) + } } // Unary & binary operators @@ -298,23 +328,23 @@ impl<'src> Infer<'src> { Expr::Unary(op, e) => match op { // Numeric operators (Num -> Num) UnaryOp::Neg => { - let et = self.infer(unbox!(e), Type::Num)?; + let (te, err) = self.infer(unbox!(e), Type::Num); self.add_constraint(expected, Type::Num, span); - Ok(TExpr::Unary { + (TExpr::Unary { op, - expr: (Box::new(et), e.1), + expr: (Box::new(te), span), ret_ty: Type::Num, - }) + }, err) }, // Boolean operators (Bool -> Bool) UnaryOp::Not => { - let et = self.infer(unbox!(e), Type::Bool)?; + let (te, err) = self.infer(unbox!(e), Type::Bool); self.add_constraint(expected, Type::Bool, span); - Ok(TExpr::Unary { + (TExpr::Unary { op, - expr: (Box::new(et), e.1), + expr: (Box::new(te), span), ret_ty: Type::Bool, - }) + }, err) }, } Expr::Binary(op, lhs, rhs) => match op { @@ -325,29 +355,31 @@ impl<'src> Infer<'src> { | BinaryOp::Div | BinaryOp::Rem => { - let lt = self.infer(unbox!(lhs), Type::Num)?; - let rt = self.infer(unbox!(rhs), Type::Num)?; + let (lt, mut errs0) = self.infer(unbox!(lhs), Type::Num); + let (rt, errs1) = self.infer(unbox!(rhs), Type::Num); + errs0.extend(errs1); self.add_constraint(expected, Type::Num, span); - Ok(TExpr::Binary { + (TExpr::Binary { op, lhs: (Box::new(lt), lhs.1), rhs: (Box::new(rt), rhs.1), ret_ty: Type::Num, - }) + }, errs0) }, // Boolean operators (Bool -> Bool -> Bool) BinaryOp::And | BinaryOp::Or => { - let lt = self.infer(unbox!(lhs), Type::Bool)?; - let rt = self.infer(unbox!(rhs), Type::Bool)?; + let (lt, mut errs0) = self.infer(unbox!(lhs), Type::Bool); + let (rt, errs1) = self.infer(unbox!(rhs), Type::Bool); + errs0.extend(errs1); self.add_constraint(expected, Type::Bool, span); - Ok(TExpr::Binary { + (TExpr::Binary { op, lhs: (Box::new(lt), lhs.1), rhs: (Box::new(rt), rhs.1), ret_ty: Type::Bool, - }) + }, errs0) }, // Comparison operators ('a -> 'a -> Bool) BinaryOp::Eq @@ -361,15 +393,16 @@ impl<'src> Infer<'src> { // expected type for both the left and right hand side // so the type on both side have to be the same let t = self.fresh(); - let lt = self.infer(unbox!(lhs), t.clone())?; - let rt = self.infer(unbox!(rhs), t)?; + let (lt, mut errs0) = self.infer(unbox!(lhs), t.clone()); + let (rt, errs1) = self.infer(unbox!(rhs), t); + errs0.extend(errs1); self.add_constraint(expected, Type::Bool, span); - Ok(TExpr::Binary { + (TExpr::Binary { op, lhs: (Box::new(lt), lhs.1), rhs: (Box::new(rt), rhs.1), ret_ty: Type::Bool, - }) + }, errs0) }, } @@ -388,7 +421,7 @@ impl<'src> Infer<'src> { xs.clone().into_iter().for_each(|(x, t)| { env.insert(x, t); }); let mut inf = self.clone(); inf.env = env; - let bt = inf.infer(unbox!(b), rt.clone())?; + let (bt, errs) = inf.infer(unbox!(b), rt.clone()); // Add the substitutions & constraints from the body // if it doesn't already exist @@ -411,11 +444,11 @@ impl<'src> Infer<'src> { Box::new(rt.clone()), ), span); - Ok(TExpr::Lambda { + (TExpr::Lambda { params: xs, body: (Box::new(bt), b.1), ret_ty: rt, - }) + }, errs) }, // Call @@ -430,41 +463,53 @@ impl<'src> Infer<'src> { Box::new(expected), ); // Expect the function to have the function type - let ft = self.infer(unbox!(f), fsig)?; + let (ft, mut errs) = self.infer(unbox!(f), fsig); // Infer the arguments - let xs = args.into_iter() + let (xs, xerrs) = args.into_iter() .zip(freshes.into_iter()) - .map(|(x, t)| Ok((self.infer(x, t)?, span))) - .collect::, String>>()?; + .map(|(x, t)| { + let span = x.1; + let (xt, err) = self.infer(x, t); + ((xt, span), err) + }) + // Flatten errors + .fold((vec![], vec![]), |(mut xs, mut errs), ((x, span), err)| { + xs.push((x, span)); + errs.extend(err); + (xs, errs) + }); + errs.extend(xerrs); - Ok(TExpr::Call { + (TExpr::Call { func: (Box::new(ft), f.1), args: xs, - }) + }, errs) }, // If Expr::If { cond, t, f } => { // Condition has to be a boolean - let ct = self.infer(unbox!(cond), Type::Bool)?; + let (ct, mut errs) = self.infer(unbox!(cond), Type::Bool); // The type of the if expression is the same as the // expected type - let tt = self.infer(unbox!(t), expected.clone())?; - let et = self.infer(unbox!(f), expected.clone())?; + let (tt, terrs) = self.infer(unbox!(t), expected.clone()); + let (ft, ferrs) = self.infer(unbox!(f), expected.clone()); + errs.extend(terrs); + errs.extend(ferrs); - Ok(TExpr::If { + (TExpr::If { cond: (Box::new(ct), cond.1), t: (Box::new(tt), t.1), - f: (Box::new(et), f.1), + f: (Box::new(ft), f.1), br_ty: expected, - }) + }, errs) }, // Let & define Expr::Let { name, ty, value, body } => { // Infer the type of the value let ty = ty.unwrap_or(self.fresh()); - let vt = self.infer(unbox!(value), ty.clone())?; + let (vt, mut errs) = self.infer(unbox!(value), ty.clone()); // Create a new environment and add the binding to it // and then use the new environment to infer the body @@ -472,23 +517,27 @@ impl<'src> Infer<'src> { env.insert(name.clone(), ty.clone()); let mut inf = Infer::new(); inf.env = env; - let bt = inf.infer(unbox!(body), expected.clone())?; + let (bt, berrs) = inf.infer(unbox!(body), expected.clone()); + errs.extend(berrs); - Ok(TExpr::Let { + (TExpr::Let { name, ty, value: (Box::new(vt), value.1), body: (Box::new(bt), body.1), - }) + }, errs) }, Expr::Define { name, ty, value } => { let ty = ty.unwrap_or(self.fresh()); - let vt = self.infer(unbox!(value), ty.clone())?; + let (val_ty, errs) = self.infer(unbox!(value), ty.clone()); self.env.insert(name.clone(), ty.clone()); - Ok(TExpr::Define { - name, ty, - value: (Box::new(vt), value.1), - }) + self.constraints.push((expected, Type::Unit, e.1)); + + (TExpr::Define { + name, + ty, + value: (Box::new(val_ty), value.1), + }, errs) }, // Block @@ -496,18 +545,23 @@ impl<'src> Infer<'src> { // Infer the type of each expression let mut last = None; let len = exprs.len(); - let xs = exprs.into_iter() + let (texprs, errs) = exprs.into_iter() .enumerate() .map(|(i, x)| { + let span = x.1; let t = self.fresh(); - let xt = self.infer(unbox!(x), t.clone())?; + let (xt, err) = self.infer(unbox!(x), t.clone()); // Save the type of the last expression if i == len - 1 { last = Some(t); } - Ok((xt, x.1)) + ((xt, span), err) }) - .collect::, String>>()?; + .fold((vec![], vec![]), |(mut xs, mut errs), ((x, span), err)| { + xs.push((x, span)); + errs.extend(err); + (xs, errs) + }); let rt = if void || last.is_none() { // If the block is void or there is no expression, @@ -520,70 +574,42 @@ impl<'src> Infer<'src> { expected }; - Ok(TExpr::Block { - exprs: xs, + (TExpr::Block { + exprs: texprs, void, ret_ty: rt, - }) + }, errs) }, } } } /// Infer a list of expressions -pub fn infer_exprs(es: Vec<(Expr, SimpleSpan)>) -> (Vec<(TExpr, SimpleSpan)>, String) { +pub fn infer_exprs(es: Vec<(Expr, SimpleSpan)>) -> (Vec<(TExpr, SimpleSpan)>, Vec) { let mut inf = Infer::new(); - // Typed expressions - let mut tes = vec![]; - // Typed expressions without substitutions - let mut tes_nosub = vec![]; - // Errors - let mut errs = vec![]; + let mut typed_exprs = vec![]; + let mut errors = vec![]; - for (e, s) in es { - let f = inf.fresh(); - let t = inf.infer((e, s), f).unwrap(); - tes.push(Some((t.clone(), s))); - tes_nosub.push((t, s)); - - match inf.solve() { - Ok(_) => { - // Substitute the type variables for the solved expressions - tes = tes.into_iter() - .map(|te| match te { - Some((t, s)) => { - Some((inf.substitute_texp(t), s)) - }, - None => None, - }) - .collect(); - }, - Err(e) => { - errs.push(e); - // Replace the expression with None - tes.pop(); - tes.push(None); - }, + for e in es { + let span = e.1; + let fresh = inf.fresh(); + let (te, err) = inf.infer(e, fresh); + typed_exprs.push((te, span)); + if !err.is_empty() { + errors.extend(err); } } - // Union typed expressions, replacing None with the typed expression without substitutions - // None means that the expression has an error - let mut tes_union = vec![]; - for (te, te_nosub) in tes.into_iter().zip(tes_nosub.into_iter()) { - match te { - Some(t) => { - tes_union.push(t); - }, - None => { - tes_union.push(te_nosub); - }, + match inf.solve() { + Ok(_) => { + typed_exprs = typed_exprs.into_iter() + .map(|(x, s)| (inf.substitute_texp(x), s)) + .collect(); + } + Err(e) => { + errors.push(e); } } - ( - // Renamer::new().process(tes_union), - tes_union, - errs.join("\n") - ) + (typed_exprs, errors) } \ No newline at end of file diff --git a/typing/src/typed.rs b/typing/src/typed.rs index 0d3d70c..90b30c0 100644 --- a/typing/src/typed.rs +++ b/typing/src/typed.rs @@ -1,4 +1,3 @@ -use chumsky::span::SimpleSpan; use syntax::{ expr::{ BinaryOp,