Lol starting over from scratch again
H-M is hard :/
This commit is contained in:
parent
f88d2331e3
commit
70ec79c4b3
@ -7,320 +7,34 @@ use std::fmt::Write;
|
|||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use parsing;
|
||||||
|
|
||||||
|
pub struct TypeContext;
|
||||||
|
|
||||||
|
impl TypeContext {
|
||||||
|
pub fn new() -> TypeContext {
|
||||||
|
TypeContext { }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn debug_types(&self) -> String {
|
||||||
|
format!("Nothing to debug")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_check_ast(&mut self, input: &parsing::AST) -> Result<String, String> {
|
||||||
|
Ok(format!("VOID VOID VOID"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* GIANT TODO - use the rust im crate, unless I make this code way less haskell-ish after it's done
|
/* GIANT TODO - use the rust im crate, unless I make this code way less haskell-ish after it's done
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use parsing;
|
/*
|
||||||
|
|
||||||
type TypeName = Rc<String>;
|
type TypeName = Rc<String>;
|
||||||
|
|
||||||
pub type TypeResult<T> = Result<T, String>;
|
pub type TypeResult<T> = Result<T, String>;
|
||||||
|
*/
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
enum MonoType {
|
|
||||||
Const(TypeConst),
|
|
||||||
Var(TypeName),
|
|
||||||
Function(Box<MonoType>, Box<MonoType>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
enum TypeConst {
|
|
||||||
Unit,
|
|
||||||
Nat,
|
|
||||||
Int,
|
|
||||||
Float,
|
|
||||||
StringT,
|
|
||||||
Bool,
|
|
||||||
Tuple(Vec<MonoType>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MonoType {
|
|
||||||
fn free_vars(&self) -> HashSet<TypeName> {
|
|
||||||
use self::MonoType::*;
|
|
||||||
match self {
|
|
||||||
Const(_) => HashSet::new(),
|
|
||||||
Var(a) => {
|
|
||||||
let mut h = HashSet::new();
|
|
||||||
h.insert(a.clone());
|
|
||||||
h
|
|
||||||
},
|
|
||||||
Function(a, b) => {
|
|
||||||
a.free_vars().union(&b.free_vars()).cloned().collect()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO maybe this should be type self, and consume?
|
|
||||||
fn apply_substitution(&self, s: &Substitution) -> MonoType {
|
|
||||||
use self::MonoType::*;
|
|
||||||
match self {
|
|
||||||
Const(t) => Const(t.clone()),
|
|
||||||
Var(a) => s.0.get(a).map(|x| x.clone()).unwrap_or(Var(a.clone())),
|
|
||||||
Function(a, b) => Function(
|
|
||||||
Box::new(a.apply_substitution(s)),
|
|
||||||
Box::new(b.apply_substitution(s))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
struct PolyType(HashSet<TypeName>, MonoType);
|
|
||||||
|
|
||||||
impl PolyType {
|
|
||||||
fn free_vars(&self) -> HashSet<TypeName> {
|
|
||||||
let mtype = self.1.free_vars();
|
|
||||||
self.0.difference(&mtype).cloned().collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_substitution(&self, s: &Substitution) -> PolyType {
|
|
||||||
let mut map: HashMap<TypeName, MonoType> = HashMap::new();
|
|
||||||
for (name, monotype) in s.0.iter() {
|
|
||||||
if let None = self.0.get(name) {
|
|
||||||
map.insert(name.clone(), monotype.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let newsub = Substitution(map);
|
|
||||||
let new = self.1.apply_substitution(&newsub);
|
|
||||||
PolyType(self.0.clone(), new)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
|
||||||
struct Substitution(HashMap<TypeName, MonoType>);
|
|
||||||
|
|
||||||
impl Substitution {
|
|
||||||
fn new() -> Substitution {
|
|
||||||
Substitution(HashMap::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bind_variable(name: &TypeName, var: &MonoType) -> Substitution {
|
|
||||||
Substitution(hashmap! {
|
|
||||||
name.clone() => var.clone()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge(self, other: Substitution) -> Substitution {
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
for (name, ty) in self.0.into_iter() {
|
|
||||||
map.insert(name, ty);
|
|
||||||
}
|
|
||||||
for (name, ty) in other.0.into_iter() {
|
|
||||||
map.insert(name, ty);
|
|
||||||
}
|
|
||||||
Substitution(map)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
struct TypeEnvironment {
|
|
||||||
map: HashMap<TypeName, PolyType>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeEnvironment {
|
|
||||||
fn apply_substitution(&self, s: &Substitution) -> TypeEnvironment {
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
for (name, polytype) in self.map.iter() {
|
|
||||||
map.insert(name.clone(), polytype.apply_substitution(s));
|
|
||||||
}
|
|
||||||
TypeEnvironment { map }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup(&self, name: &TypeName) -> Option<PolyType> {
|
|
||||||
self.map.get(name).map(|x| x.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extend(&mut self, name: &TypeName, ty: PolyType) {
|
|
||||||
self.map.insert(name.clone(), ty);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn free_vars(&self) -> HashSet<TypeName> {
|
|
||||||
let mut free = HashSet::new();
|
|
||||||
for (_, ptype) in self.map.iter() {
|
|
||||||
free = free.union(&ptype.free_vars()).cloned().collect()
|
|
||||||
}
|
|
||||||
free
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TypeContext {
|
|
||||||
environment: TypeEnvironment,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeContext {
|
|
||||||
pub fn new() -> TypeContext {
|
|
||||||
TypeContext { environment: TypeEnvironment::default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn debug_types(&self) -> String {
|
|
||||||
let mut output = format!("Type context\n");
|
|
||||||
for (sym, ty) in &self.environment.map {
|
|
||||||
write!(output, "{} -> {:?}\n", sym, ty).unwrap();
|
|
||||||
}
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_check_ast(&mut self, ast: &parsing::AST) -> TypeResult<String> {
|
|
||||||
let ref block = ast.0;
|
|
||||||
let output = {
|
|
||||||
let mut infer = Infer::new(&mut self.environment);
|
|
||||||
let output = infer.block(block);
|
|
||||||
output
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("ENV LOOKS LIKE: {:?}", self.environment);
|
|
||||||
|
|
||||||
match output {
|
|
||||||
Ok(s) => Ok(format!("{:?}", s)),
|
|
||||||
Err(s) => Err(format!("Error: {:?}", s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Infer<'a> {
|
|
||||||
env: &'a mut TypeEnvironment,
|
|
||||||
_idents: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum InferError {
|
|
||||||
CannotUnify(MonoType, MonoType),
|
|
||||||
OccursCheckFailed(TypeName, MonoType),
|
|
||||||
UnknownIdentifier(TypeName),
|
|
||||||
Custom(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
type InferResult<T> = Result<T, InferError>;
|
|
||||||
|
|
||||||
impl<'a> Infer<'a> {
|
|
||||||
|
|
||||||
fn new(env: &'a mut TypeEnvironment) -> Infer {
|
|
||||||
Infer {
|
|
||||||
env,
|
|
||||||
_idents: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fresh(&mut self) -> MonoType {
|
|
||||||
let i = self._idents;
|
|
||||||
self._idents += 1;
|
|
||||||
let name = Rc::new(format!("{}", ('a' as u8 + 1) as char));
|
|
||||||
MonoType::Var(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn instantiate(&mut self, ptype: PolyType) -> MonoType {
|
|
||||||
let mtype = ptype.1.clone();
|
|
||||||
let mut m = HashMap::new();
|
|
||||||
for name in ptype.0.iter() {
|
|
||||||
m.insert(name.clone(), self.fresh());
|
|
||||||
}
|
|
||||||
let sub = Substitution(m);
|
|
||||||
mtype.apply_substitution(&sub)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generalize(&mut self, ty: MonoType) -> PolyType {
|
|
||||||
let free_mtype = ty.free_vars();
|
|
||||||
let free_env = self.env.free_vars();
|
|
||||||
let diff: HashSet<TypeName> = free_mtype.difference(&free_env).cloned().collect();
|
|
||||||
PolyType(diff, ty)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block(&mut self, block: &Vec<parsing::Statement>) -> InferResult<MonoType> {
|
|
||||||
let mut ret = MonoType::Const(TypeConst::Unit);
|
|
||||||
for s in block {
|
|
||||||
ret = match s {
|
|
||||||
parsing::Statement::ExpressionStatement(expr) => self.infer_expression(expr)?,
|
|
||||||
parsing::Statement::Declaration(decl) => {
|
|
||||||
self.infer_declaration(decl)?;
|
|
||||||
MonoType::Const(TypeConst::Unit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn infer_declaration(&mut self, decl: &parsing::Declaration) -> InferResult<MonoType> {
|
|
||||||
use parsing::Declaration::*;
|
|
||||||
use parsing::Signature;
|
|
||||||
match decl {
|
|
||||||
Binding { name, expr, .. } => {
|
|
||||||
let tau: MonoType = self.infer_expression(&expr)?;
|
|
||||||
let sigma = self.generalize(tau);
|
|
||||||
self.env.extend(name, sigma);
|
|
||||||
},
|
|
||||||
FuncDecl(Signature { name, params, type_anno }, block) => {
|
|
||||||
|
|
||||||
let mut fn_type_env = TypeEnvironment::default();
|
|
||||||
let mut local_infer = Infer::new(&mut fn_type_env);
|
|
||||||
|
|
||||||
let mut arg_types: Vec<MonoType> = Vec::new();
|
|
||||||
|
|
||||||
for (param_name, maybe_type) in params {
|
|
||||||
println!("HANDLING PARAM: {}", param_name);
|
|
||||||
let tau = local_infer.fresh();
|
|
||||||
let sigma = PolyType(HashSet::new(), tau);
|
|
||||||
local_infer.env.extend(param_name, sigma);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ret_type = local_infer.block(block)?;
|
|
||||||
println!("RET TYPE: {:?}", ret_type);
|
|
||||||
|
|
||||||
let mut final_type = MonoType::Function(Box::new(MonoType::Const(TypeConst::Unit)), Box::new(ret_type));
|
|
||||||
println!("ARG TYPES: {:?}", arg_types);
|
|
||||||
|
|
||||||
for ty in arg_types.into_iter().rev() {
|
|
||||||
final_type = MonoType::Function(Box::new(ty), Box::new(final_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
let final_ptype = self.generalize(final_type);
|
|
||||||
|
|
||||||
self.env.extend(name, final_ptype);
|
|
||||||
},
|
|
||||||
_ => return Err(InferError::Custom(format!("This decl not yet supported")))
|
|
||||||
}
|
|
||||||
Ok(MonoType::Const(TypeConst::Unit))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn infer_expression(&mut self, expr: &parsing::Expression) -> InferResult<MonoType> {
|
|
||||||
match expr {
|
|
||||||
parsing::Expression(e, Some(anno)) => {
|
|
||||||
return Err(InferError::Custom(format!("Annotations not done yet")))
|
|
||||||
/*
|
|
||||||
let anno_ty = anno.to_type()?;
|
|
||||||
let ty = self.infer_exprtype(&e)?;
|
|
||||||
self.unify(ty, anno_ty)
|
|
||||||
*/
|
|
||||||
},
|
|
||||||
parsing::Expression(e, None) => self.infer_expression_type(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn infer_expression_type(&mut self, expr: &parsing::ExpressionType) -> InferResult<MonoType> {
|
|
||||||
use self::parsing::ExpressionType::*;
|
|
||||||
Ok(match expr {
|
|
||||||
NatLiteral(_) => MonoType::Const(TypeConst::Nat),
|
|
||||||
FloatLiteral(_) => MonoType::Const(TypeConst::Float),
|
|
||||||
StringLiteral(_) => MonoType::Const(TypeConst::StringT),
|
|
||||||
BoolLiteral(_) => MonoType::Const(TypeConst::Bool),
|
|
||||||
Value(name) => {
|
|
||||||
let sigma = match self.env.lookup(name) {
|
|
||||||
Some(ty) => ty,
|
|
||||||
None => return Err(InferError::UnknownIdentifier(name.clone())),
|
|
||||||
};
|
|
||||||
let tau = self.instantiate(sigma);
|
|
||||||
tau
|
|
||||||
},
|
|
||||||
Call { f, arguments } => {
|
|
||||||
/*
|
|
||||||
let sigma = match sel
|
|
||||||
*/
|
|
||||||
unimplemented!()
|
|
||||||
},
|
|
||||||
e => return Err(InferError::Custom(format!("this expression type not done yet: {:?}", e)))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO this should just check the name against a map, and that map should be pre-populated with
|
/* TODO this should just check the name against a map, and that map should be pre-populated with
|
||||||
* types */
|
* types */
|
||||||
|
Loading…
Reference in New Issue
Block a user