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",
]
[[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"

View file

@ -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" }

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

View file

@ -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"] }

View file

@ -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: &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")
})
}

View file

@ -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 = ;

View file

@ -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<TExpr<'src>, String> {
let (e, span) = e;
match e {
fn infer(
&mut self, e: (Expr<'src>, SimpleSpan), expected: Type
) -> (TExpr<'src>, Vec<InferError<'src>>) {
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::<Result<Vec<_>, 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::<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() {
// 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<InferError>) {
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)
}

View file

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