1
1
Fork 0
mirror of https://github.com/azur1s/bobbylisp.git synced 2024-10-16 02:37:40 -05:00

We are so back part 2

This commit is contained in:
azur 2023-04-23 17:13:06 +07:00
parent 9eb4bf27fb
commit 019bc88186
9 changed files with 510 additions and 213 deletions

299
Cargo.lock generated
View file

@ -13,6 +13,55 @@ dependencies = [
"version_check", "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]] [[package]]
name = "ariadne" name = "ariadne"
version = "0.2.0" version = "0.2.0"
@ -29,10 +78,17 @@ version = "0.1.0"
dependencies = [ dependencies = [
"ariadne", "ariadne",
"chumsky", "chumsky",
"clap",
"syntax", "syntax",
"typing", "typing",
] ]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.79" version = "1.0.79"
@ -55,6 +111,75 @@ dependencies = [
"stacker", "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]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.13.2" version = "0.13.2"
@ -65,10 +190,51 @@ dependencies = [
] ]
[[package]] [[package]]
name = "libc" name = "heck"
version = "0.2.140" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "once_cell" name = "once_cell"
@ -76,6 +242,15 @@ version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 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]] [[package]]
name = "psm" name = "psm"
version = "0.1.21" version = "0.1.21"
@ -85,6 +260,29 @@ dependencies = [
"cc", "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]] [[package]]
name = "stacker" name = "stacker"
version = "0.1.15" version = "0.1.15"
@ -98,6 +296,23 @@ dependencies = [
"winapi", "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]] [[package]]
name = "syntax" name = "syntax"
version = "0.1.0" version = "0.1.0"
@ -113,12 +328,24 @@ dependencies = [
"syntax", "syntax",
] ]
[[package]]
name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.10" version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"
@ -147,6 +374,72 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 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]] [[package]]
name = "yansi" name = "yansi"
version = "0.5.1" version = "0.5.1"

View file

@ -6,5 +6,6 @@ edition = "2021"
[dependencies] [dependencies]
ariadne = "0.2.0" ariadne = "0.2.0"
chumsky = "1.0.0-alpha.3" chumsky = "1.0.0-alpha.3"
clap = { version = "4.2.4", features = ["derive"] }
syntax = { path = "../syntax" } syntax = { path = "../syntax" }
typing = { path = "../typing" } typing = { path = "../typing" }

12
bin/src/args.rs Normal file
View file

@ -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()
}

View file

@ -3,48 +3,37 @@ use chumsky::{Parser, prelude::Input};
use syntax::parser::{lexer, exprs_parser}; use syntax::parser::{lexer, exprs_parser};
use typing::infer::infer_exprs; use typing::infer::infer_exprs;
pub mod args;
fn main() { fn main() {
let src = " let args = args::get_args();
let r = { let filename = args.file.clone();
let x = let src = std::fs::read_to_string(&args.file).expect("file not found");
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 (ts, errs) = lexer().parse(&src).into_output_errors(); 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() let (ast, parse_errs) = exprs_parser()
.map_with_span(|ast, span| (ast, span)) .map_with_span(|ast, span| (ast, span))
.parse(tokens.as_slice().spanned((src.len()..src.len()).into())) .parse(tokens.as_slice().spanned((src.len()..src.len()).into()))
.into_output_errors(); .into_output_errors();
if let Some(ast) = ast.filter(|_| errs.len() + parse_errs.len() == 0) { (ast, parse_errs)
} else {
(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); let (ast, e) = infer_exprs(ast.0);
if !e.is_empty() { if !e.is_empty() {
println!("{:?}", e); e.iter().for_each(|e| println!("{e:?}"));
} }
if !ast.is_empty() { if !ast.is_empty() {
println!("{:?}", ast); ast.iter().for_each(|(e, _)| println!("{e:?}"));
} }
} (Some(ast), e)
parse_errs
} else { } else {
Vec::new() (None, vec![])
}; };
errs.into_iter() errs.into_iter()

View file

@ -4,4 +4,4 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
chumsky = "1.0.0-alpha.3" chumsky = { version = "1.0.0-alpha.3", features = ["label"] }

View file

@ -15,10 +15,20 @@ pub fn lexer<'src>() -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, e
.then_ignore(just('"')) .then_ignore(just('"'))
.map_slice(Token::Str); .map_slice(Token::Str);
let word = text::ident().map(|s: &str| match s { fn id_filter<C>(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), "true" => Token::Bool(true),
"false" => Token::Bool(false), "false" => Token::Bool(false),
"unit" => Token::Unit,
"let" => Token::Let, "let" => Token::Let,
"in" => Token::In, "in" => Token::In,
"func" => Token::Func, "func" => Token::Func,
@ -30,6 +40,7 @@ pub fn lexer<'src>() -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, e
}); });
let sym = choice(( let sym = choice((
just("()").to(Token::Unit),
just("\\").to(Token::Lambda), just("\\").to(Token::Lambda),
just("->").to(Token::Arrow), just("->").to(Token::Arrow),
@ -72,7 +83,7 @@ pub fn lexer<'src>() -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, e
)); ));
token token
.map_with_span(|tok, span| (tok, span)) .map_with_span(move |tok, span| (tok, span))
.padded() .padded()
// If we get an error, skip to the next character and try again. // If we get an error, skip to the next character and try again.
.recover_with(skip_then_retry_until(any().ignored(), end())) .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 logical
.labelled("expression")
}) })
} }
@ -310,11 +321,20 @@ pub fn type_parser<'tokens, 'src: 'tokens>() -> impl Parser<
> + Clone { > + Clone {
recursive(|ty| { recursive(|ty| {
let lit_ty = select! { let lit_ty = select! {
Token::Ident("bool") => Type::Bool, Token::Ident("Bool") => Type::Bool,
Token::Ident("num") => Type::Num, Token::Ident("Num") => Type::Num,
Token::Ident("str") => Type::Str, 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, 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() let tys_paren = ty.clone()
.separated_by(just(Token::Comma)) .separated_by(just(Token::Comma))
@ -351,6 +371,8 @@ pub fn type_parser<'tokens, 'src: 'tokens>() -> impl Parser<
.or(array) .or(array)
.or(func) .or(func)
.or(tuple) .or(tuple)
.boxed()
.labelled("type")
}) })
} }

View file

@ -1,46 +1 @@
-- Source let f = \f, g, h, a, b, c, d = ;
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)
))

View file

@ -10,12 +10,27 @@ use syntax::{
use super::typed::TExpr; use super::typed::TExpr;
macro_rules! ok {
($e:expr) => {
($e, vec![])
};
}
macro_rules! unbox { macro_rules! unbox {
($e:expr) => { ($e:expr) => {
(*$e.0, $e.1) (*$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)] #[derive(Clone, Debug)]
struct Infer<'src> { struct Infer<'src> {
env: HashMap<&'src str, Type>, env: HashMap<&'src str, Type>,
@ -71,7 +86,7 @@ impl<'src> Infer<'src> {
} }
/// Unify two types /// 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::*; use Type::*;
match (t1, t2) { match (t1, t2) {
// Literal types // Literal types
@ -92,7 +107,7 @@ impl<'src> Infer<'src> {
} }
// If the variable occurs in t2 // If the variable occurs in t2
if self.occurs(i, t2.clone()) { if self.occurs(i, t2.clone()) {
return Err(format!("Infinite type: '{} = {}", itoa(i), t2)); return Err(InferError::InfiniteType(Var(i), t2));
} }
// Set the substitution // Set the substitution
self.subst[i] = t2; self.subst[i] = t2;
@ -105,7 +120,7 @@ impl<'src> Infer<'src> {
} }
} }
if self.occurs(i, t1.clone()) { 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; self.subst[i] = t1;
Ok(()) Ok(())
@ -115,7 +130,7 @@ impl<'src> Infer<'src> {
(Func(a1, r1), Func(a2, r2)) => { (Func(a1, r1), Func(a2, r2)) => {
// Check the number of arguments // Check the number of arguments
if a1.len() != a2.len() { 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 // Unify the arguments
for (a1, a2) in a1.into_iter().zip(a2.into_iter()) { for (a1, a2) in a1.into_iter().zip(a2.into_iter()) {
@ -129,7 +144,7 @@ impl<'src> Infer<'src> {
(Tuple(t1), Tuple(t2)) => { (Tuple(t1), Tuple(t2)) => {
// Check the number of elements // Check the number of elements
if t1.len() != t2.len() { 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 // Unify the elements
for (t1, t2) in t1.into_iter().zip(t2.into_iter()) { 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), (Array(t1), Array(t2)) => self.unify(*t1, *t2),
// The rest will be type mismatch // 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 /// 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() { for (t1, t2, _span) in self.constraints.clone().into_iter() {
self.unify(t1, t2)?; self.unify(t1, t2)?;
} }
@ -266,30 +281,45 @@ impl<'src> Infer<'src> {
} }
/// Infer the type of an expression /// Infer the type of an expression
fn infer(&mut self, e: (Expr<'src>, SimpleSpan), expected: Type) -> Result<TExpr<'src>, String> { fn infer(
let (e, span) = e; &mut self, e: (Expr<'src>, SimpleSpan), expected: Type
match e { ) -> (TExpr<'src>, Vec<InferError<'src>>) {
let span = e.1;
match e.0 {
// Literal values // Literal values
// Push the constraint (expected type to be the literal type) and // Push the constraint (expected type to be the literal type) and
// return the typed expression // return the typed expression
Expr::Lit(l) => { Expr::Lit(l) => match l {
let t = match l { Lit::Unit => {
Lit::Unit => Type::Unit, self.add_constraint(expected, Type::Unit, span);
Lit::Bool(_) => Type::Bool, ok!(TExpr::Lit(Lit::Unit))
Lit::Num(_) => Type::Num, }
Lit::Str(_) => Type::Str, Lit::Bool(b) => {
}; self.add_constraint(expected, Type::Bool, span);
self.add_constraint(expected, t, span); ok!(TExpr::Lit(Lit::Bool(b)))
Ok(TExpr::Lit(l)) }
}, 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 // Identifiers
// The same as literals but the type is looked up in the environment // The same as literals but the type is looked up in the environment
Expr::Ident(ref x) => { Expr::Ident(ref x) => {
let t = self.env.get(x) if let Some(t) = self.env.get(x) {
.ok_or(format!("Unbound variable: {}", x))?;
self.add_constraint(expected, t.clone(), span); self.add_constraint(expected, t.clone(), span);
Ok(TExpr::Ident(x.clone())) ok!(TExpr::Ident(x))
} else {
(TExpr::Ident(x), vec![match expected {
Type::Func(_, _) => InferError::UnboundFunction(x, span),
_ => InferError::UnboundVariable(x, span),
}])
}
} }
// Unary & binary operators // Unary & binary operators
@ -298,23 +328,23 @@ impl<'src> Infer<'src> {
Expr::Unary(op, e) => match op { Expr::Unary(op, e) => match op {
// Numeric operators (Num -> Num) // Numeric operators (Num -> Num)
UnaryOp::Neg => { 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); self.add_constraint(expected, Type::Num, span);
Ok(TExpr::Unary { (TExpr::Unary {
op, op,
expr: (Box::new(et), e.1), expr: (Box::new(te), span),
ret_ty: Type::Num, ret_ty: Type::Num,
}) }, err)
}, },
// Boolean operators (Bool -> Bool) // Boolean operators (Bool -> Bool)
UnaryOp::Not => { 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); self.add_constraint(expected, Type::Bool, span);
Ok(TExpr::Unary { (TExpr::Unary {
op, op,
expr: (Box::new(et), e.1), expr: (Box::new(te), span),
ret_ty: Type::Bool, ret_ty: Type::Bool,
}) }, err)
}, },
} }
Expr::Binary(op, lhs, rhs) => match op { Expr::Binary(op, lhs, rhs) => match op {
@ -325,29 +355,31 @@ impl<'src> Infer<'src> {
| BinaryOp::Div | BinaryOp::Div
| BinaryOp::Rem | BinaryOp::Rem
=> { => {
let lt = self.infer(unbox!(lhs), Type::Num)?; let (lt, mut errs0) = self.infer(unbox!(lhs), Type::Num);
let rt = self.infer(unbox!(rhs), Type::Num)?; let (rt, errs1) = self.infer(unbox!(rhs), Type::Num);
errs0.extend(errs1);
self.add_constraint(expected, Type::Num, span); self.add_constraint(expected, Type::Num, span);
Ok(TExpr::Binary { (TExpr::Binary {
op, op,
lhs: (Box::new(lt), lhs.1), lhs: (Box::new(lt), lhs.1),
rhs: (Box::new(rt), rhs.1), rhs: (Box::new(rt), rhs.1),
ret_ty: Type::Num, ret_ty: Type::Num,
}) }, errs0)
}, },
// Boolean operators (Bool -> Bool -> Bool) // Boolean operators (Bool -> Bool -> Bool)
BinaryOp::And BinaryOp::And
| BinaryOp::Or | BinaryOp::Or
=> { => {
let lt = self.infer(unbox!(lhs), Type::Bool)?; let (lt, mut errs0) = self.infer(unbox!(lhs), Type::Bool);
let rt = self.infer(unbox!(rhs), Type::Bool)?; let (rt, errs1) = self.infer(unbox!(rhs), Type::Bool);
errs0.extend(errs1);
self.add_constraint(expected, Type::Bool, span); self.add_constraint(expected, Type::Bool, span);
Ok(TExpr::Binary { (TExpr::Binary {
op, op,
lhs: (Box::new(lt), lhs.1), lhs: (Box::new(lt), lhs.1),
rhs: (Box::new(rt), rhs.1), rhs: (Box::new(rt), rhs.1),
ret_ty: Type::Bool, ret_ty: Type::Bool,
}) }, errs0)
}, },
// Comparison operators ('a -> 'a -> Bool) // Comparison operators ('a -> 'a -> Bool)
BinaryOp::Eq BinaryOp::Eq
@ -361,15 +393,16 @@ impl<'src> Infer<'src> {
// expected type for both the left and right hand side // expected type for both the left and right hand side
// so the type on both side have to be the same // so the type on both side have to be the same
let t = self.fresh(); let t = self.fresh();
let lt = self.infer(unbox!(lhs), t.clone())?; let (lt, mut errs0) = self.infer(unbox!(lhs), t.clone());
let rt = self.infer(unbox!(rhs), t)?; let (rt, errs1) = self.infer(unbox!(rhs), t);
errs0.extend(errs1);
self.add_constraint(expected, Type::Bool, span); self.add_constraint(expected, Type::Bool, span);
Ok(TExpr::Binary { (TExpr::Binary {
op, op,
lhs: (Box::new(lt), lhs.1), lhs: (Box::new(lt), lhs.1),
rhs: (Box::new(rt), rhs.1), rhs: (Box::new(rt), rhs.1),
ret_ty: Type::Bool, 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); }); xs.clone().into_iter().for_each(|(x, t)| { env.insert(x, t); });
let mut inf = self.clone(); let mut inf = self.clone();
inf.env = env; 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 // Add the substitutions & constraints from the body
// if it doesn't already exist // if it doesn't already exist
@ -411,11 +444,11 @@ impl<'src> Infer<'src> {
Box::new(rt.clone()), Box::new(rt.clone()),
), span); ), span);
Ok(TExpr::Lambda { (TExpr::Lambda {
params: xs, params: xs,
body: (Box::new(bt), b.1), body: (Box::new(bt), b.1),
ret_ty: rt, ret_ty: rt,
}) }, errs)
}, },
// Call // Call
@ -430,41 +463,53 @@ impl<'src> Infer<'src> {
Box::new(expected), Box::new(expected),
); );
// Expect the function to have the function type // 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 // Infer the arguments
let xs = args.into_iter() let (xs, xerrs) = args.into_iter()
.zip(freshes.into_iter()) .zip(freshes.into_iter())
.map(|(x, t)| Ok((self.infer(x, t)?, span))) .map(|(x, t)| {
.collect::<Result<Vec<_>, String>>()?; 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), func: (Box::new(ft), f.1),
args: xs, args: xs,
}) }, errs)
}, },
// If // If
Expr::If { cond, t, f } => { Expr::If { cond, t, f } => {
// Condition has to be a boolean // 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 // The type of the if expression is the same as the
// expected type // expected type
let tt = self.infer(unbox!(t), expected.clone())?; let (tt, terrs) = self.infer(unbox!(t), expected.clone());
let et = self.infer(unbox!(f), 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), cond: (Box::new(ct), cond.1),
t: (Box::new(tt), t.1), t: (Box::new(tt), t.1),
f: (Box::new(et), f.1), f: (Box::new(ft), f.1),
br_ty: expected, br_ty: expected,
}) }, errs)
}, },
// Let & define // Let & define
Expr::Let { name, ty, value, body } => { Expr::Let { name, ty, value, body } => {
// Infer the type of the value // Infer the type of the value
let ty = ty.unwrap_or(self.fresh()); 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 // Create a new environment and add the binding to it
// and then use the new environment to infer the body // and then use the new environment to infer the body
@ -472,23 +517,27 @@ impl<'src> Infer<'src> {
env.insert(name.clone(), ty.clone()); env.insert(name.clone(), ty.clone());
let mut inf = Infer::new(); let mut inf = Infer::new();
inf.env = env; 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, name, ty,
value: (Box::new(vt), value.1), value: (Box::new(vt), value.1),
body: (Box::new(bt), body.1), body: (Box::new(bt), body.1),
}) }, errs)
}, },
Expr::Define { name, ty, value } => { Expr::Define { name, ty, value } => {
let ty = ty.unwrap_or(self.fresh()); 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()); self.env.insert(name.clone(), ty.clone());
Ok(TExpr::Define { self.constraints.push((expected, Type::Unit, e.1));
name, ty,
value: (Box::new(vt), value.1), (TExpr::Define {
}) name,
ty,
value: (Box::new(val_ty), value.1),
}, errs)
}, },
// Block // Block
@ -496,18 +545,23 @@ impl<'src> Infer<'src> {
// Infer the type of each expression // Infer the type of each expression
let mut last = None; let mut last = None;
let len = exprs.len(); let len = exprs.len();
let xs = exprs.into_iter() let (texprs, errs) = exprs.into_iter()
.enumerate() .enumerate()
.map(|(i, x)| { .map(|(i, x)| {
let span = x.1;
let t = self.fresh(); 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 // Save the type of the last expression
if i == len - 1 { if i == len - 1 {
last = Some(t); last = Some(t);
} }
Ok((xt, x.1)) ((xt, span), err)
}) })
.collect::<Result<Vec<_>, 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() { let rt = if void || last.is_none() {
// If the block is void or there is no expression, // If the block is void or there is no expression,
@ -520,70 +574,42 @@ impl<'src> Infer<'src> {
expected expected
}; };
Ok(TExpr::Block { (TExpr::Block {
exprs: xs, exprs: texprs,
void, void,
ret_ty: rt, ret_ty: rt,
}) }, errs)
}, },
} }
} }
} }
/// Infer a list of expressions /// 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<InferError>) {
let mut inf = Infer::new(); let mut inf = Infer::new();
// Typed expressions let mut typed_exprs = vec![];
let mut tes = vec![]; let mut errors = vec![];
// Typed expressions without substitutions
let mut tes_nosub = vec![];
// Errors
let mut errs = vec![];
for (e, s) in es { for e in es {
let f = inf.fresh(); let span = e.1;
let t = inf.infer((e, s), f).unwrap(); let fresh = inf.fresh();
tes.push(Some((t.clone(), s))); let (te, err) = inf.infer(e, fresh);
tes_nosub.push((t, s)); typed_exprs.push((te, span));
if !err.is_empty() {
errors.extend(err);
}
}
match inf.solve() { match inf.solve() {
Ok(_) => { Ok(_) => {
// Substitute the type variables for the solved expressions typed_exprs = typed_exprs.into_iter()
tes = tes.into_iter() .map(|(x, s)| (inf.substitute_texp(x), s))
.map(|te| match te {
Some((t, s)) => {
Some((inf.substitute_texp(t), s))
},
None => None,
})
.collect(); .collect();
}, }
Err(e) => { Err(e) => {
errs.push(e); errors.push(e);
// Replace the expression with None
tes.pop();
tes.push(None);
},
} }
} }
// Union typed expressions, replacing None with the typed expression without substitutions (typed_exprs, errors)
// 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);
},
}
}
(
// Renamer::new().process(tes_union),
tes_union,
errs.join("\n")
)
} }

View file

@ -1,4 +1,3 @@
use chumsky::span::SimpleSpan;
use syntax::{ use syntax::{
expr::{ expr::{
BinaryOp, BinaryOp,