mirror of https://github.com/azur1s/bobbylisp.git
195 lines
6.3 KiB
Rust
195 lines
6.3 KiB
Rust
use super::{
|
|
ty::Type,
|
|
typed::TExpr,
|
|
};
|
|
use crate::{parse::{
|
|
ptree::*,
|
|
span::*,
|
|
}, span};
|
|
|
|
#[derive(Clone, Debug)]
|
|
struct TypeEnv {
|
|
bindings: Vec<(String, Type)>,
|
|
funcs: Vec<(String, Vec<Type>, Type)>,
|
|
}
|
|
|
|
impl TypeEnv {
|
|
fn new() -> Self {
|
|
Self {
|
|
bindings: Vec::new(),
|
|
funcs: Vec::new(),
|
|
}
|
|
}
|
|
|
|
fn bind(&mut self, name: String, ty: Type) {
|
|
self.bindings.push((name, ty));
|
|
}
|
|
|
|
fn bind_func(&mut self, name: String, 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()))
|
|
}
|
|
}
|
|
|
|
struct TypeError {
|
|
msg: String,
|
|
loc: Span,
|
|
}
|
|
|
|
fn type_expr(env: &mut TypeEnv, expr: Spanned<Expr>) -> Result<Spanned<TExpr>, TypeError> {
|
|
match expr.value {
|
|
Expr::Lit(lit) => match lit {
|
|
Lit::Unit => Ok(span!(TExpr::Lit(Lit::Unit), expr.span)),
|
|
Lit::Bool(x) => Ok(span!(TExpr::Lit(Lit::Bool(x)), expr.span)),
|
|
Lit::Num(x) => Ok(span!(TExpr::Lit(Lit::Num(x)), expr.span)),
|
|
Lit::Str(x) => Ok(span!(TExpr::Lit(Lit::Str(x)), expr.span)),
|
|
},
|
|
|
|
Expr::Ident(name) => {
|
|
let ty = env.lookup(&name)
|
|
.ok_or(TypeError {
|
|
msg: format!("unknown identifier `{}`", name),
|
|
loc: expr.span.clone(),
|
|
})?;
|
|
Ok(span!(TExpr::Ident(name, ty), expr.span))
|
|
},
|
|
|
|
Expr::Unary(op, expr) => {
|
|
let span = expr.span.clone();
|
|
let texpr = type_expr(env, *expr)?;
|
|
let ret_ty = match op {
|
|
UnaryOp::Neg => Type::Num,
|
|
UnaryOp::Not => Type::Bool,
|
|
};
|
|
Ok(span!(
|
|
TExpr::Unary { op, expr: Box::new(texpr), ret_ty },
|
|
span
|
|
))
|
|
},
|
|
|
|
Expr::Binary(op, lhs, rhs) => {
|
|
let span = lhs.span.clone();
|
|
let tlhs = type_expr(env, *lhs)?;
|
|
let trhs = type_expr(env, *rhs)?;
|
|
let ret_ty = match op {
|
|
BinaryOp::Add
|
|
| BinaryOp::Sub
|
|
| BinaryOp::Mul
|
|
| BinaryOp::Div
|
|
| BinaryOp::Rem => Type::Num,
|
|
BinaryOp::And
|
|
| BinaryOp::Or => Type::Bool,
|
|
BinaryOp::Eq
|
|
| BinaryOp::Ne
|
|
| BinaryOp::Lt
|
|
| BinaryOp::Le
|
|
| BinaryOp::Gt
|
|
| BinaryOp::Ge => Type::Bool,
|
|
};
|
|
Ok(span!(
|
|
TExpr::Binary { op, lhs: Box::new(tlhs), rhs: Box::new(trhs), ret_ty },
|
|
span
|
|
))
|
|
},
|
|
|
|
Expr::Call(func, args) => {
|
|
let span = func.span.clone();
|
|
|
|
match func.value {
|
|
Expr::Ident(name) => {
|
|
// Get the function's argument and return types
|
|
let (arg_tys, ret_ty) = env.lookup_func(&name)
|
|
.ok_or(TypeError {
|
|
msg: format!("unknown function `{}`", name),
|
|
loc: span.clone(),
|
|
})?;
|
|
|
|
// Create a typed identifier
|
|
let tfunc = TExpr::Ident(
|
|
name,
|
|
Type::Func(arg_tys.clone(), Box::new(ret_ty.clone()))
|
|
);
|
|
|
|
// Check that the number of arguments matches
|
|
if arg_tys.len() != args.len() {
|
|
return Err(TypeError {
|
|
msg: format!(
|
|
"expected {} arguments, got {}",
|
|
arg_tys.len(), args.len()
|
|
),
|
|
loc: span,
|
|
});
|
|
}
|
|
|
|
// Type check the arguments
|
|
let mut targs = Vec::new();
|
|
for (arg, ty) in args.into_iter().zip(arg_tys) {
|
|
let targ = type_expr(env, arg)?;
|
|
if targ.value.ty() != &ty {
|
|
return Err(TypeError {
|
|
msg: format!(
|
|
"expected argument of type `{}`, got `{}`",
|
|
ty, targ.value.ty()
|
|
),
|
|
loc: targ.span,
|
|
});
|
|
}
|
|
targs.push(targ);
|
|
}
|
|
|
|
Ok(span!(
|
|
TExpr::Call {
|
|
func: Box::new(span!(tfunc, span.clone())),
|
|
args: targs,
|
|
ret_ty
|
|
},
|
|
span
|
|
))
|
|
},
|
|
Expr::Lambda(args, body) => {
|
|
// Create a new type environment
|
|
let mut new_env = env.clone();
|
|
|
|
// Bind the arguments to the new environment and also infer their types
|
|
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
|
|
};
|
|
arg_tys.push((arg.clone(), ty.clone()));
|
|
env.bind(arg, ty);
|
|
}
|
|
|
|
// Type check the body
|
|
let tbody = type_expr(&mut new_env, *body)?;
|
|
|
|
// Return the typed lambda expression
|
|
Ok(span!(
|
|
TExpr::Lambda {
|
|
params: arg_tys,
|
|
body: Box::new(tbody.clone()),
|
|
ret_ty: tbody.value.ty().clone(),
|
|
},
|
|
span
|
|
))
|
|
},
|
|
_ => todo!(),
|
|
}
|
|
},
|
|
_ => todo!(),
|
|
}
|
|
} |