schala/schala-lang/language/src/typechecking.rs

283 lines
7.2 KiB
Rust

use std::rc::Rc;
//THINGS TODO
// look at the haskell compiler, see where in its flow the typechecking happens
// -nope, ghc deliberately does typechecking before desugaring to core
// cf. a history of haskell, peyton-jones
use crate::ast::*;
use crate::util::ScopeStack;
use crate::builtin::PrefixOp;
pub type TypeName = Rc<String>;
pub struct TypeContext<'a> {
variable_map: ScopeStack<'a, Rc<String>, Type>,
//evar_count: u32
}
/// `InferResult` is the monad in which type inference takes place.
type InferResult<T> = Result<T, TypeError>;
#[derive(Debug, Clone)]
struct TypeError { msg: String }
impl TypeError {
fn new<A>(msg: &str) -> InferResult<A> { //TODO make these kinds of error-producing functions CoW-ready
Err(TypeError { msg: msg.to_string() })
}
}
#[derive(Debug, Clone)]
pub enum Type {
Const(TypeConst),
Arrow(Box<Type>, Box<Type>),
Compound {
ty: Box<Type>,
args:Vec<Type>
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TypeConst {
Unit,
Nat,
Int,
Float,
StringT,
Bool,
Ordering,
UserDefined
}
macro_rules! ty {
($type_name:ident) => { Type::Const(TypeConst::$type_name) };
($t1:ident -> $t2:ident) => { Type::Arrow(Box::new(ty!($t1)), Box::new(ty!($t2))) };
($t1:ident -> $t2:ident -> $t3:ident) => { Type::Arrow(Box::new(ty!($t1)), Box::new(ty!($t2 -> $t3))) };
}
//TODO find a better way to capture the to/from string logic
impl Type {
fn to_string(&self) -> String {
use self::Type::*;
use self::TypeConst::*;
match self {
Const(Unit) => format!("()"),
Const(Nat) => format!("Nat"),
Const(Int) => format!("Int"),
Const(Float) => format!("Float"),
Const(StringT) => format!("String"),
Const(Bool) => format!("Bool"),
Const(Ordering) => format!("Ordering"),
_ => format!("UNKNOWN TYPE"),
}
}
fn from_string(string: &str) -> Option<Type> {
Some(match string {
"()" | "Unit" => ty!(Unit),
"Nat" => ty!(Nat),
"Int" => ty!(Int),
"Float" => ty!(Float),
"String" => ty!(StringT),
"Bool" => ty!(Bool),
"Ordering" => ty!(Ordering),
_ => return None
})
}
}
/*
/// `Type` is parameterized by whether the type variables can be just universal, or universal or
/// existential.
#[derive(Debug, Clone)]
enum Type<A> {
Var(A),
Const(TConst),
Arrow(Box<Type<A>>, Box<Type<A>>),
}
#[derive(Debug, Clone)]
enum TVar {
Univ(UVar),
Exist(ExistentialVar)
}
#[derive(Debug, Clone)]
struct UVar(Rc<String>);
#[derive(Debug, Clone)]
struct ExistentialVar(u32);
impl Type<UVar> {
fn to_tvar(&self) -> Type<TVar> {
match self {
Type::Var(UVar(name)) => Type::Var(TVar::Univ(UVar(name.clone()))),
Type::Const(ref c) => Type::Const(c.clone()),
Type::Arrow(a, b) => Type::Arrow(
Box::new(a.to_tvar()),
Box::new(b.to_tvar())
)
}
}
}
impl Type<TVar> {
fn skolemize(&self) -> Type<UVar> {
match self {
Type::Var(TVar::Univ(uvar)) => Type::Var(uvar.clone()),
Type::Var(TVar::Exist(_)) => Type::Var(UVar(Rc::new(format!("sk")))),
Type::Const(ref c) => Type::Const(c.clone()),
Type::Arrow(a, b) => Type::Arrow(
Box::new(a.skolemize()),
Box::new(b.skolemize())
)
}
}
}
impl TypeIdentifier {
fn to_monotype(&self) -> Type<UVar> {
match self {
TypeIdentifier::Tuple(_) => Type::Const(TConst::Nat),
TypeIdentifier::Singleton(TypeSingletonName { name, .. }) => {
match &name[..] {
"Nat" => Type::Const(TConst::Nat),
"Int" => Type::Const(TConst::Int),
"Float" => Type::Const(TConst::Float),
"Bool" => Type::Const(TConst::Bool),
"String" => Type::Const(TConst::StringT),
_ => Type::Const(TConst::Nat),
}
}
}
}
}
#[derive(Debug, Clone)]
enum TConst {
User(Rc<String>),
Unit,
Nat,
Int,
Float,
StringT,
Bool,
}
impl TConst {
fn user(name: &str) -> TConst {
TConst::User(Rc::new(name.to_string()))
}
}
*/
impl<'a> TypeContext<'a> {
pub fn new() -> TypeContext<'a> {
TypeContext {
variable_map: ScopeStack::new(None),
//evar_count: 0
}
}
fn get_type_from_name(&self, name: &TypeIdentifier) -> InferResult<Type> {
use self::TypeIdentifier::*;
Ok(match name {
Singleton(TypeSingletonName { name, params }) => {
match Type::from_string(&name) {
Some(ty) => ty,
None => return TypeError::new("Unknown type name")
}
},
Tuple(_) => return TypeError::new("tuples aren't ready yet"),
})
}
pub fn typecheck(&mut self, ast: &AST) -> Result<String, String> {
let mut returned_type = Type::Const(TypeConst::Unit);
for statement in ast.0.iter() {
returned_type = self.statement(statement.node()).map_err(|err| { err.msg })?
}
Ok(returned_type.to_string())
}
fn statement(&mut self, statement: &Statement) -> InferResult<Type> {
match statement {
Statement::ExpressionStatement(e) => self.expr(e.node()),
Statement::Declaration(decl) => self.decl(decl),
}
}
fn decl(&mut self, decl: &Declaration) -> InferResult<Type> {
use self::Declaration::*;
match decl {
Binding { name, expr, .. } => {
let ty = self.expr(expr)?;
self.variable_map.insert(name.clone(), ty);
},
_ => (),
}
Ok(ty!(Unit))
}
fn expr(&mut self, expr: &Expression) -> InferResult<Type> {
match expr {
Expression(expr_type, Some(anno)) => {
let t1 = self.expr_type(expr_type)?;
let t2 = self.get_type_from_name(anno)?;
self.unify(t2, t1)
},
Expression(expr_type, None) => self.expr_type(expr_type)
}
}
fn expr_type(&mut self, expr: &ExpressionType) -> InferResult<Type> {
use self::ExpressionType::*;
Ok(match expr {
NatLiteral(_) => ty!(Nat),
BoolLiteral(_) => ty!(Bool),
FloatLiteral(_) => ty!(Float),
StringLiteral(_) => ty!(StringT),
PrefixExp(op, expr) => self.prefix(op, expr.node())?,
IfExpression { discriminator, body } => self.if_expr(discriminator, body)?,
Value(val) => self.handle_value(val)?,
_ => ty!(Unit),
})
}
fn prefix(&mut self, op: &PrefixOp, expr: &Expression) -> InferResult<Type> {
let f = match op.get_type() {
Ok(ty) => ty,
Err(e) => return TypeError::new("Couldn't find a type for this prefix op")
};
let x = self.expr(expr)?;
self.handle_apply(f, x)
}
fn handle_apply(&mut self, f: Type, x: Type) -> InferResult<Type> {
Ok(ty!(Unit))
}
fn if_expr(&mut self, discriminator: &Discriminator, body: &IfExpressionBody) -> InferResult<Type> {
//only handle simple case for now
Ok(ty!(Unit))
}
fn handle_value(&mut self, val: &Rc<String>) -> InferResult<Type> {
match self.variable_map.lookup(val) {
Some(ty) => Ok(ty.clone()),
None => TypeError::new(&format!("Couldn't find variable: {}", val))
}
}
fn unify(&mut self, t1: Type, t2: Type) -> InferResult<Type> {
use self::Type::*; use self::TypeConst::*;
Ok(match (t1, t2) {
(Const(ref c1), Const(ref c2)) if c1 == c2 => Const(c1.clone()), //choice of c1 is arbitrary I *think*
(a, b) => return TypeError::new(&format!("{:?} and {:?} do not unify", a, b)),
})
}
}