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:
parent
9eb4bf27fb
commit
019bc88186
299
Cargo.lock
generated
299
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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
12
bin/src/args.rs
Normal 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()
|
||||
}
|
|
@ -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) {
|
||||
(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);
|
||||
if !e.is_empty() {
|
||||
println!("{:?}", e);
|
||||
e.iter().for_each(|e| println!("{e:?}"));
|
||||
}
|
||||
if !ast.is_empty() {
|
||||
println!("{:?}", ast);
|
||||
ast.iter().for_each(|(e, _)| println!("{e:?}"));
|
||||
}
|
||||
}
|
||||
|
||||
parse_errs
|
||||
(Some(ast), e)
|
||||
} else {
|
||||
Vec::new()
|
||||
(None, vec![])
|
||||
};
|
||||
|
||||
errs.into_iter()
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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")
|
||||
})
|
||||
}
|
||||
|
||||
|
|
47
test.hlm
47
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 = ;
|
|
@ -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))?;
|
||||
if let Some(t) = self.env.get(x) {
|
||||
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
|
||||
|
@ -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));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
})
|
||||
typed_exprs = typed_exprs.into_iter()
|
||||
.map(|(x, s)| (inf.substitute_texp(x), s))
|
||||
.collect();
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
errs.push(e);
|
||||
// Replace the expression with None
|
||||
tes.pop();
|
||||
tes.push(None);
|
||||
},
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
// Renamer::new().process(tes_union),
|
||||
tes_union,
|
||||
errs.join("\n")
|
||||
)
|
||||
(typed_exprs, errors)
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
use chumsky::span::SimpleSpan;
|
||||
use syntax::{
|
||||
expr::{
|
||||
BinaryOp,
|
||||
|
|
Loading…
Reference in a new issue