mirror of
https://github.com/azur1s/bobbylisp.git
synced 2024-10-16 02:37:40 -05:00
Prepare for proper type inference
This commit is contained in:
parent
4b805823e6
commit
0c79321826
40
src/main.rs
40
src/main.rs
|
@ -1,6 +1,6 @@
|
||||||
use ariadne::{sources, Color, Label, Report, ReportKind};
|
use ariadne::{sources, Color, Label, Report, ReportKind};
|
||||||
use chumsky::{Parser, prelude::Input};
|
use chumsky::{Parser, prelude::Input};
|
||||||
use self::{parse::parser::{lexer, exprs_parser}, typing::check::check};
|
use self::{parse::parser::{lexer, exprs_parser}};
|
||||||
|
|
||||||
pub mod parse;
|
pub mod parse;
|
||||||
pub mod typing;
|
pub mod typing;
|
||||||
|
@ -8,11 +8,10 @@ pub mod typing;
|
||||||
fn main() {
|
fn main() {
|
||||||
let src = "
|
let src = "
|
||||||
{
|
{
|
||||||
let foo : num =
|
let foo =
|
||||||
let a : num = true,
|
let a = true in
|
||||||
b : num = 3
|
let b = false in
|
||||||
in
|
a + b;
|
||||||
a + b;
|
|
||||||
foo * 2
|
foo * 2
|
||||||
}
|
}
|
||||||
".to_string();
|
".to_string();
|
||||||
|
@ -27,34 +26,7 @@ fn main() {
|
||||||
.into_output_errors();
|
.into_output_errors();
|
||||||
|
|
||||||
if let Some(ast) = ast.filter(|_| errs.len() + parse_errs.len() == 0) {
|
if let Some(ast) = ast.filter(|_| errs.len() + parse_errs.len() == 0) {
|
||||||
match check(ast.0) {
|
println!("{:?}", ast);
|
||||||
Ok(tast) => println!("{:?}", tast),
|
|
||||||
Err(ty_err) => {
|
|
||||||
let mut r = Report::build(ReportKind::Error, filename.clone(), ty_err.loc.start)
|
|
||||||
.with_message(ty_err.msg)
|
|
||||||
.with_label(Label::new((filename.clone(), ty_err.loc.into_range()))
|
|
||||||
.with_message(match ty_err.note {
|
|
||||||
Some(note) => note,
|
|
||||||
None => "While type checking this expression".to_string(),
|
|
||||||
})
|
|
||||||
.with_color(Color::Red)
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some((hint, loc)) = ty_err.hint {
|
|
||||||
r = r.with_label(Label::new((filename.clone(), loc.into_range()))
|
|
||||||
.with_message(hint)
|
|
||||||
.with_color(Color::Yellow),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
r.finish()
|
|
||||||
.print(sources([(
|
|
||||||
filename.clone(),
|
|
||||||
src.clone(),
|
|
||||||
)]))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_errs
|
parse_errs
|
||||||
|
|
|
@ -178,8 +178,6 @@ pub enum BinaryOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Spanned<T> = (T, Span);
|
pub type Spanned<T> = (T, Span);
|
||||||
type Binding<'src> =
|
|
||||||
(&'src str, Option<Type>, Spanned<Box<Expr<'src>>>);
|
|
||||||
|
|
||||||
// Clone is needed for type checking since the type checking
|
// Clone is needed for type checking since the type checking
|
||||||
// algorithm is recursive and sometimes consume the AST.
|
// algorithm is recursive and sometimes consume the AST.
|
||||||
|
@ -200,10 +198,16 @@ pub enum Expr<'src> {
|
||||||
f: Spanned<Box<Self>>,
|
f: Spanned<Box<Self>>,
|
||||||
},
|
},
|
||||||
Let {
|
Let {
|
||||||
bindings: Vec<Binding<'src>>,
|
name: &'src str,
|
||||||
|
ty: Option<Type>,
|
||||||
|
value: Spanned<Box<Self>>,
|
||||||
body: Spanned<Box<Self>>,
|
body: Spanned<Box<Self>>,
|
||||||
},
|
},
|
||||||
Assign(Vec<Binding<'src>>),
|
Define {
|
||||||
|
name: &'src str,
|
||||||
|
ty: Option<Type>,
|
||||||
|
value: Spanned<Box<Self>>,
|
||||||
|
},
|
||||||
Block {
|
Block {
|
||||||
exprs: Vec<Spanned<Box<Self>>>,
|
exprs: Vec<Spanned<Box<Self>>>,
|
||||||
void: bool, // True if last expression is discarded (ends with semicolon).
|
void: bool, // True if last expression is discarded (ends with semicolon).
|
||||||
|
@ -268,8 +272,8 @@ pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser<
|
||||||
.then(expr.clone())
|
.then(expr.clone())
|
||||||
.map(|(args, body)| Expr::Lambda(args, boxspan(body)));
|
.map(|(args, body)| Expr::Lambda(args, boxspan(body)));
|
||||||
|
|
||||||
// (ident (: type)?)*
|
// ident (: type)?
|
||||||
let binds = symbol
|
let bind = symbol
|
||||||
.then(
|
.then(
|
||||||
just(Token::Colon)
|
just(Token::Colon)
|
||||||
.ignore_then(type_parser())
|
.ignore_then(type_parser())
|
||||||
|
@ -277,21 +281,18 @@ pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser<
|
||||||
)
|
)
|
||||||
.then_ignore(just(Token::Assign))
|
.then_ignore(just(Token::Assign))
|
||||||
.then(expr.clone())
|
.then(expr.clone())
|
||||||
.map(|((name, ty), expr)| (name, ty, boxspan(expr)))
|
.map(|((name, ty), expr)| (name, ty, boxspan(expr)));
|
||||||
.separated_by(just(Token::Comma))
|
|
||||||
.allow_trailing()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let let_or_assign = just(Token::Let)
|
let let_or_define = just(Token::Let)
|
||||||
.ignore_then(binds)
|
.ignore_then(bind)
|
||||||
.then(
|
.then(
|
||||||
just(Token::In)
|
just(Token::In)
|
||||||
.ignore_then(expr.clone())
|
.ignore_then(expr.clone())
|
||||||
.or_not()
|
.or_not()
|
||||||
)
|
)
|
||||||
.map(|(bindings, body)| match body {
|
.map(|((name, ty, expr), body)| match body {
|
||||||
Some(body) => Expr::Let { bindings, body: boxspan(body) },
|
Some(body) => Expr::Let { name, ty, value: expr, body: boxspan(body) },
|
||||||
None => Expr::Assign(bindings),
|
None => Expr::Define { name, ty, value: expr },
|
||||||
});
|
});
|
||||||
|
|
||||||
let if_ = just(Token::If)
|
let if_ = just(Token::If)
|
||||||
|
@ -333,7 +334,7 @@ pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser<
|
||||||
.or(ident)
|
.or(ident)
|
||||||
.or(paren_expr)
|
.or(paren_expr)
|
||||||
.or(lambda)
|
.or(lambda)
|
||||||
.or(let_or_assign)
|
.or(let_or_define)
|
||||||
.or(if_)
|
.or(if_)
|
||||||
.or(block)
|
.or(block)
|
||||||
.map_with_span(|e, s| (e, s))
|
.map_with_span(|e, s| (e, s))
|
||||||
|
|
|
@ -1,425 +0,0 @@
|
||||||
use crate::parse::parser::{
|
|
||||||
Span, Spanned,
|
|
||||||
UnaryOp, BinaryOp, Lit, Expr,
|
|
||||||
};
|
|
||||||
use super::{ty::Type, typed::TExpr};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
struct TypeContext<'src> {
|
|
||||||
bindings: Vec<(&'src str, Type)>,
|
|
||||||
funcs: Vec<(&'src str, Vec<Type>, Type)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'src> TypeContext<'src> {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
bindings: Vec::new(),
|
|
||||||
funcs: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Bind a type to a name.
|
|
||||||
fn bind(&mut self, name: &'src str, ty: Type) {
|
|
||||||
self.bindings.push((name, ty));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Bind a function (parameters and return type) to a name.
|
|
||||||
fn bind_func(&mut self, name: &'src str, args: Vec<Type>, ret_ty: Type) {
|
|
||||||
self.funcs.push((name, args, ret_ty));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup(&self, name: &str) -> Option<Type> {
|
|
||||||
self.bindings.iter()
|
|
||||||
.rev()
|
|
||||||
.find(|(n, _)| *n == name)
|
|
||||||
.map(|(_, t)| t.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup_func(&self, name: &str) -> Option<(Vec<Type>, Type)> {
|
|
||||||
self.funcs.iter()
|
|
||||||
.rev()
|
|
||||||
.find(|(n, _, _)| *n == name)
|
|
||||||
.map(|(_, args, ret_ty)| (args.clone(), ret_ty.clone()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct TypeError {
|
|
||||||
pub msg: String,
|
|
||||||
pub note: Option<String>,
|
|
||||||
pub hint: Option<(String, Span)>,
|
|
||||||
pub loc: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeError {
|
|
||||||
fn new(msg: String, loc: Span) -> Self {
|
|
||||||
Self {
|
|
||||||
msg,
|
|
||||||
note: None,
|
|
||||||
hint: None,
|
|
||||||
loc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_note(mut self, note: String) -> Self {
|
|
||||||
self.note = Some(note);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_hint(mut self, hint: String, loc: Span) -> Self {
|
|
||||||
self.hint = Some((hint, loc));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn type_expr<'src>(
|
|
||||||
env: &mut TypeContext<'src>, expr: Spanned<Expr<'src>>
|
|
||||||
) -> Result<Spanned<TExpr<'src>>, TypeError> {
|
|
||||||
macro_rules! oks { // Spanned Ok macro.
|
|
||||||
($e:expr $(,)?) => {
|
|
||||||
Ok(($e, expr.1))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
macro_rules! unbox { // Unbox a Spanned<Box<T>> into a Spanned<T>.
|
|
||||||
($e:expr) => {
|
|
||||||
(*$e.0, $e.1)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
macro_rules! sbox { // Box the first value of a Spanned<T>.
|
|
||||||
($e:expr) => {
|
|
||||||
(Box::new($e.0), $e.1)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
match expr.0 {
|
|
||||||
Expr::Lit(lit) => match lit {
|
|
||||||
Lit::Unit => oks!(TExpr::Lit(Lit::Unit)),
|
|
||||||
Lit::Bool(x) => oks!(TExpr::Lit(Lit::Bool(x))),
|
|
||||||
Lit::Num(x) => oks!(TExpr::Lit(Lit::Num(x))),
|
|
||||||
Lit::Str(x) => oks!(TExpr::Lit(Lit::Str(x))),
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Ident(name) => {
|
|
||||||
let ty = env.lookup(name)
|
|
||||||
.ok_or(TypeError::new(format!("Unknown identifier `{}`", name), expr.1))?;
|
|
||||||
oks!(TExpr::Ident(name, ty))
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Unary(op, e) => {
|
|
||||||
let te = type_expr(env, unbox!(e))?;
|
|
||||||
let ret_ty = match op {
|
|
||||||
UnaryOp::Neg => Type::Num,
|
|
||||||
UnaryOp::Not => Type::Bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
if te.0.ty() != &ret_ty {
|
|
||||||
return Err(TypeError::new(format!("Expected `{}` but found `{}`", ret_ty, te.0.ty()), te.1)
|
|
||||||
.with_note(format!("This have type `{}`", te.0.ty()))
|
|
||||||
.with_hint(format!("This operator requires a `{}`", ret_ty), (te.1.start-1..te.1.start).into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
oks!(TExpr::Unary {
|
|
||||||
op,
|
|
||||||
expr: sbox!(te),
|
|
||||||
ret_ty,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Binary(op, lhs, rhs) => {
|
|
||||||
let tlhs = type_expr(env, unbox!(lhs))?;
|
|
||||||
let trhs = type_expr(env, unbox!(rhs))?;
|
|
||||||
let op_ty = match op {
|
|
||||||
BinaryOp::Add
|
|
||||||
| BinaryOp::Sub
|
|
||||||
| BinaryOp::Mul
|
|
||||||
| BinaryOp::Div
|
|
||||||
| BinaryOp::Rem => Some(Type::Num),
|
|
||||||
BinaryOp::And
|
|
||||||
| BinaryOp::Or => Some(Type::Bool),
|
|
||||||
BinaryOp::Eq
|
|
||||||
| BinaryOp::Ne
|
|
||||||
| BinaryOp::Lt
|
|
||||||
| BinaryOp::Le
|
|
||||||
| BinaryOp::Gt
|
|
||||||
| BinaryOp::Ge => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let ret_ty;
|
|
||||||
if let Some(op_ty) = op_ty {
|
|
||||||
if tlhs.0.ty() != &op_ty {
|
|
||||||
return Err(TypeError::new(format!("Expected `{}` but found `{}`", op_ty, tlhs.0.ty()), tlhs.1)
|
|
||||||
.with_note(format!("This have type `{}`", tlhs.0.ty()))
|
|
||||||
.with_hint(format!("This operator requires a `{}`", op_ty), (tlhs.1.start-1..tlhs.1.start).into()));
|
|
||||||
}
|
|
||||||
if trhs.0.ty() != &op_ty {
|
|
||||||
return Err(TypeError::new(format!("Expected `{}` but found `{}`", op_ty, trhs.0.ty()), trhs.1)
|
|
||||||
.with_note(format!("This have type `{}`", trhs.0.ty()))
|
|
||||||
.with_hint(format!("This operator requires a `{}`", op_ty), (trhs.1.start-1..trhs.1.start).into()));
|
|
||||||
}
|
|
||||||
ret_ty = op_ty;
|
|
||||||
} else {
|
|
||||||
if tlhs.0.ty() != trhs.0.ty() {
|
|
||||||
return Err(TypeError::new(format!("Expected `{}` but found `{}`", tlhs.0.ty(), trhs.0.ty()), trhs.1)
|
|
||||||
.with_hint(
|
|
||||||
format!("Both have to be the same type. Got `{}` and `{}`", tlhs.0.ty(), trhs.0.ty()),
|
|
||||||
(tlhs.1.start..trhs.1.end).into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
ret_ty = Type::Bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
oks!(TExpr::Binary {
|
|
||||||
op,
|
|
||||||
lhs: sbox!(tlhs),
|
|
||||||
rhs: sbox!(trhs),
|
|
||||||
ret_ty,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Lambda(args, body) => {
|
|
||||||
// Create a new type environment.
|
|
||||||
let mut new_env = env.clone();
|
|
||||||
|
|
||||||
// Bind the arguments to the new environment.
|
|
||||||
let mut arg_tys = Vec::new();
|
|
||||||
for (arg, maybe_ty) in args {
|
|
||||||
let ty = match maybe_ty {
|
|
||||||
Some(ty) => ty,
|
|
||||||
None => todo!(), // TODO: infer the type of the argument after type checking the body.
|
|
||||||
};
|
|
||||||
arg_tys.push((arg, ty.clone()));
|
|
||||||
new_env.bind(arg, ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type check the body.
|
|
||||||
let tbody = type_expr(&mut new_env, unbox!(body))?;
|
|
||||||
|
|
||||||
// Return the typed lambda expression.
|
|
||||||
oks!(TExpr::Lambda {
|
|
||||||
params: arg_tys,
|
|
||||||
body: sbox!(tbody.clone()),
|
|
||||||
ret_ty: tbody.0.ty().clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Call(func, cargs) => {
|
|
||||||
// Get span of the arguments.
|
|
||||||
let args_span = cargs.iter()
|
|
||||||
.map(|arg| arg.1.into_range())
|
|
||||||
.fold(None, |acc: Option<std::ops::Range<usize>>, range| match acc {
|
|
||||||
Some(acc) => Some(acc.start..range.end),
|
|
||||||
None => Some(range),
|
|
||||||
})
|
|
||||||
.unwrap_or(func.1.end..func.1.end+2);
|
|
||||||
|
|
||||||
// Type check the arguments.
|
|
||||||
let mut targs = Vec::new();
|
|
||||||
for arg in cargs {
|
|
||||||
let targ = type_expr(env, arg)?;
|
|
||||||
targs.push(targ);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type check the function (callee).
|
|
||||||
let tfunc = type_expr(env, unbox!(func))?;
|
|
||||||
|
|
||||||
// Get the function type of the callee. (if any).
|
|
||||||
if let Some((param_tys, ret_ty)) = tfunc.0.clone().as_fn() {
|
|
||||||
|
|
||||||
// Check if the number of arguments match the number of parameters.
|
|
||||||
if param_tys.len() != targs.len() {
|
|
||||||
return Err(TypeError::new(
|
|
||||||
format!(
|
|
||||||
"Expected {} arguments, got {}",
|
|
||||||
param_tys.len(),
|
|
||||||
targs.len(),
|
|
||||||
),
|
|
||||||
args_span.into(),
|
|
||||||
).with_note(format!(
|
|
||||||
"Expected {} arguments",
|
|
||||||
param_tys.len(),
|
|
||||||
)).with_hint(
|
|
||||||
format!(
|
|
||||||
"This expect arguments of type `{}`",
|
|
||||||
param_tys.iter().map(|ty| ty.to_string()).collect::<Vec<_>>().join(", ")
|
|
||||||
),
|
|
||||||
func.1,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the types of the arguments match the types of the parameters.
|
|
||||||
for (arg, param) in targs.iter().zip(param_tys.iter()) {
|
|
||||||
if arg.0.ty() != param {
|
|
||||||
return Err(TypeError::new(
|
|
||||||
format!(
|
|
||||||
"Expected argument of type `{}`, got `{}`",
|
|
||||||
param,
|
|
||||||
arg.0.ty(),
|
|
||||||
),
|
|
||||||
arg.1,
|
|
||||||
).with_note(format!(
|
|
||||||
"Expected argument of type `{}`",
|
|
||||||
param,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the typed call expression.
|
|
||||||
oks!(TExpr::Call {
|
|
||||||
func: sbox!(tfunc),
|
|
||||||
args: targs,
|
|
||||||
ret_ty,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(TypeError::new(
|
|
||||||
format!("Expected function, got `{}`", tfunc.0.ty()),
|
|
||||||
tfunc.1,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::If { cond, t, f } => {
|
|
||||||
let tcond = type_expr(env, unbox!(cond))?;
|
|
||||||
let tt = type_expr(env, unbox!(t))?;
|
|
||||||
let tf = type_expr(env, unbox!(f))?;
|
|
||||||
|
|
||||||
// Check if the condition is of type `bool`.
|
|
||||||
if tcond.0.ty() != &Type::Bool {
|
|
||||||
return Err(TypeError::new(
|
|
||||||
format!("Expected condition of type `bool`, got `{}`", tcond.0.ty()),
|
|
||||||
tcond.1,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the true and false branches have the same type.
|
|
||||||
if tt.0.ty() != tf.0.ty() {
|
|
||||||
return Err(TypeError::new(
|
|
||||||
format!(
|
|
||||||
"Expected the branches to have the same type, got `{}` and `{}`",
|
|
||||||
tt.0.ty(),
|
|
||||||
tf.0.ty(),
|
|
||||||
),
|
|
||||||
tf.1,
|
|
||||||
).with_note(format!(
|
|
||||||
"Expected this branch to be type of `{}`",
|
|
||||||
tt.0.ty(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
oks!(TExpr::If {
|
|
||||||
cond: sbox!(tcond),
|
|
||||||
br_ty: tt.0.ty().clone(),
|
|
||||||
t: sbox!(tt),
|
|
||||||
f: sbox!(tf),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Let { bindings, body } => {
|
|
||||||
// Create a new type environment.
|
|
||||||
let mut new_env = env.clone();
|
|
||||||
|
|
||||||
// Type check the bindings.
|
|
||||||
let mut tbindings = Vec::new();
|
|
||||||
for (name, maybe_ty, expr) in bindings {
|
|
||||||
let ty = match maybe_ty {
|
|
||||||
Some(ty) => ty,
|
|
||||||
None => todo!("Type inferrence"), // TODO: infer.
|
|
||||||
};
|
|
||||||
let texpr = type_expr(&mut new_env, unbox!(expr))?;
|
|
||||||
|
|
||||||
// Check if the type of the binding matches the type of the expression.
|
|
||||||
if texpr.0.ty() != &ty {
|
|
||||||
return Err(TypeError::new(
|
|
||||||
format!(
|
|
||||||
"Expected the value to be of type `{}`, got `{}`",
|
|
||||||
ty,
|
|
||||||
texpr.0.ty(),
|
|
||||||
),
|
|
||||||
texpr.1,
|
|
||||||
).with_note(format!(
|
|
||||||
"Expected this value to be of type `{}`",
|
|
||||||
ty,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
tbindings.push((name, ty.clone(), sbox!(texpr)));
|
|
||||||
new_env.bind(name, ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type check the body.
|
|
||||||
let tbody = type_expr(&mut new_env, unbox!(body))?;
|
|
||||||
|
|
||||||
// Return the typed let expression.
|
|
||||||
oks!(TExpr::Let {
|
|
||||||
bindings: tbindings,
|
|
||||||
body: sbox!(tbody),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Assign(bindings) => {
|
|
||||||
// Type check the bindings.
|
|
||||||
let mut tbindings = Vec::new();
|
|
||||||
for (name, maybe_ty, expr) in bindings {
|
|
||||||
let ty = match maybe_ty {
|
|
||||||
Some(ty) => ty,
|
|
||||||
None => todo!("Type inferrence"), // TODO: infer.
|
|
||||||
};
|
|
||||||
let texpr = type_expr(env, unbox!(expr))?;
|
|
||||||
|
|
||||||
// Check if the type of the binding matches the type of the expression.
|
|
||||||
if texpr.0.ty() != &ty {
|
|
||||||
return Err(TypeError::new(
|
|
||||||
format!(
|
|
||||||
"Expected the binding to be of type `{}`, got `{}`",
|
|
||||||
ty,
|
|
||||||
texpr.0.ty(),
|
|
||||||
),
|
|
||||||
texpr.1,
|
|
||||||
).with_note(format!(
|
|
||||||
"Expected this binding to be of type `{}`",
|
|
||||||
ty,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
tbindings.push((name, ty.clone(), sbox!(texpr)));
|
|
||||||
env.bind(name, ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the typed assign expression.
|
|
||||||
oks!(TExpr::Assign(tbindings))
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Block { exprs, void } => {
|
|
||||||
let texprs = exprs
|
|
||||||
.into_iter()
|
|
||||||
.map(|e| type_expr(env, unbox!(e)))
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
|
|
||||||
let ret_ty = if void {
|
|
||||||
Type::Unit
|
|
||||||
} else if let Some(texpr) = texprs.last() {
|
|
||||||
texpr.0.ty().clone()
|
|
||||||
} else {
|
|
||||||
Type::Unit
|
|
||||||
};
|
|
||||||
|
|
||||||
oks!(TExpr::Block {
|
|
||||||
exprs: texprs,
|
|
||||||
void,
|
|
||||||
ret_ty,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unreachable_patterns)]
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check(es: Vec<Spanned<Expr<'_>>>) -> Result<Vec<Spanned<TExpr<'_>>>, TypeError> {
|
|
||||||
let mut env = TypeContext::new();
|
|
||||||
let mut tes = Vec::new();
|
|
||||||
for e in es {
|
|
||||||
let te = type_expr(&mut env, e)?;
|
|
||||||
tes.push(te);
|
|
||||||
}
|
|
||||||
Ok(tes)
|
|
||||||
}
|
|
|
@ -1,3 +1,2 @@
|
||||||
pub mod ty;
|
pub mod ty;
|
||||||
pub mod check;
|
|
||||||
pub mod typed;
|
pub mod typed;
|
|
@ -6,14 +6,11 @@ use crate::parse::parser::{
|
||||||
Spanned,
|
Spanned,
|
||||||
};
|
};
|
||||||
|
|
||||||
type TypedBinding<'src> =
|
|
||||||
(&'src str, Type, Spanned<Box<TExpr<'src>>>);
|
|
||||||
|
|
||||||
// Typed version of the expression.
|
// Typed version of the expression.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum TExpr<'src> {
|
pub enum TExpr<'src> {
|
||||||
Lit(Lit<'src>),
|
Lit(Lit<'src>),
|
||||||
Ident(&'src str, Type),
|
Ident(&'src str),
|
||||||
|
|
||||||
Unary {
|
Unary {
|
||||||
op: UnaryOp,
|
op: UnaryOp,
|
||||||
|
@ -35,7 +32,6 @@ pub enum TExpr<'src> {
|
||||||
Call {
|
Call {
|
||||||
func: Spanned<Box<Self>>,
|
func: Spanned<Box<Self>>,
|
||||||
args: Vec<Spanned<Self>>,
|
args: Vec<Spanned<Self>>,
|
||||||
ret_ty: Type,
|
|
||||||
},
|
},
|
||||||
If {
|
If {
|
||||||
cond: Spanned<Box<Self>>,
|
cond: Spanned<Box<Self>>,
|
||||||
|
@ -44,52 +40,19 @@ pub enum TExpr<'src> {
|
||||||
br_ty: Type,
|
br_ty: Type,
|
||||||
},
|
},
|
||||||
Let {
|
Let {
|
||||||
bindings: Vec<TypedBinding<'src>>,
|
name: &'src str,
|
||||||
|
ty: Type,
|
||||||
|
value: Spanned<Box<Self>>,
|
||||||
body: Spanned<Box<Self>>,
|
body: Spanned<Box<Self>>,
|
||||||
},
|
},
|
||||||
Assign(Vec<TypedBinding<'src>>),
|
Define {
|
||||||
|
name: &'src str,
|
||||||
|
ty: Type,
|
||||||
|
value: Spanned<Box<Self>>,
|
||||||
|
},
|
||||||
Block {
|
Block {
|
||||||
exprs: Vec<Spanned<Self>>,
|
exprs: Vec<Spanned<Self>>,
|
||||||
void: bool,
|
void: bool,
|
||||||
ret_ty: Type,
|
ret_ty: Type,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> TExpr<'src> {
|
|
||||||
pub fn ty(&self) -> &Type {
|
|
||||||
match self {
|
|
||||||
TExpr::Lit(lit) => match lit {
|
|
||||||
Lit::Unit => &Type::Unit,
|
|
||||||
Lit::Bool(_) => &Type::Bool,
|
|
||||||
Lit::Num(_) => &Type::Num,
|
|
||||||
Lit::Str(_) => &Type::Str,
|
|
||||||
},
|
|
||||||
TExpr::Ident(_, ty) => ty,
|
|
||||||
TExpr::Unary { ret_ty, .. } => ret_ty,
|
|
||||||
TExpr::Binary { ret_ty, .. } => ret_ty,
|
|
||||||
TExpr::Lambda { ret_ty, .. } => ret_ty,
|
|
||||||
TExpr::Call { ret_ty, .. } => ret_ty,
|
|
||||||
TExpr::If { br_ty, .. } => br_ty,
|
|
||||||
// Get the type from the body.
|
|
||||||
TExpr::Let { body, .. } => body.0.ty(),
|
|
||||||
// Assignment is always unit.
|
|
||||||
TExpr::Assign { .. } => &Type::Unit,
|
|
||||||
// Get the type from the last expression in the block
|
|
||||||
// if the expression is not ended with a semicolon.
|
|
||||||
TExpr::Block { ret_ty, .. } => ret_ty,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_fn(self) -> Option<(Vec<Type>, Type)> {
|
|
||||||
match self {
|
|
||||||
TExpr::Ident(_, Type::Func(params, ret_ty)) => Some((params, *ret_ty)),
|
|
||||||
TExpr::Lambda { params, ret_ty, .. } => {
|
|
||||||
let p = params.into_iter()
|
|
||||||
.map(|(_, ty)| ty)
|
|
||||||
.collect();
|
|
||||||
Some((p, ret_ty))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue