2021-03-14 03:57:27 +01:00
|
|
|
pub(crate) mod hir;
|
|
|
|
|
2021-03-07 21:29:59 +01:00
|
|
|
use std::borrow::Cow;
|
2021-03-14 21:43:47 +01:00
|
|
|
use std::collections::HashMap;
|
2021-03-07 21:29:59 +01:00
|
|
|
use std::convert::TryFrom;
|
|
|
|
use std::fmt::{self, Display, Formatter};
|
|
|
|
|
2021-03-08 06:04:44 +01:00
|
|
|
use itertools::Itertools;
|
|
|
|
|
2021-03-07 21:29:59 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
|
|
pub struct InvalidIdentifier<'a>(Cow<'a, str>);
|
|
|
|
|
2021-03-08 06:04:44 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
2021-03-07 21:29:59 +01:00
|
|
|
pub struct Ident<'a>(pub Cow<'a, str>);
|
|
|
|
|
|
|
|
impl<'a> From<&'a Ident<'a>> for &'a str {
|
|
|
|
fn from(id: &'a Ident<'a>) -> Self {
|
|
|
|
id.0.as_ref()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Display for Ident<'a> {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(f, "{}", self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Ident<'a> {
|
|
|
|
pub fn to_owned(&self) -> Ident<'static> {
|
|
|
|
Ident(Cow::Owned(self.0.clone().into_owned()))
|
|
|
|
}
|
|
|
|
|
2021-03-28 19:28:49 +02:00
|
|
|
/// Construct an identifier from a &str without checking that it's a valid identifier
|
2021-03-07 21:29:59 +01:00
|
|
|
pub fn from_str_unchecked(s: &'a str) -> Self {
|
|
|
|
debug_assert!(is_valid_identifier(s));
|
|
|
|
Self(Cow::Borrowed(s))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_string_unchecked(s: String) -> Self {
|
|
|
|
debug_assert!(is_valid_identifier(&s));
|
|
|
|
Self(Cow::Owned(s))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_valid_identifier<S>(s: &S) -> bool
|
|
|
|
where
|
|
|
|
S: AsRef<str> + ?Sized,
|
|
|
|
{
|
|
|
|
s.as_ref()
|
|
|
|
.chars()
|
|
|
|
.any(|c| !c.is_alphanumeric() || !"_".contains(c))
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> TryFrom<&'a str> for Ident<'a> {
|
|
|
|
type Error = InvalidIdentifier<'a>;
|
|
|
|
|
|
|
|
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
|
|
|
|
if is_valid_identifier(s) {
|
|
|
|
Ok(Ident(Cow::Borrowed(s)))
|
|
|
|
} else {
|
|
|
|
Err(InvalidIdentifier(Cow::Borrowed(s)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> TryFrom<String> for Ident<'a> {
|
|
|
|
type Error = InvalidIdentifier<'static>;
|
|
|
|
|
|
|
|
fn try_from(s: String) -> Result<Self, Self::Error> {
|
|
|
|
if is_valid_identifier(&s) {
|
|
|
|
Ok(Ident(Cow::Owned(s)))
|
|
|
|
} else {
|
|
|
|
Err(InvalidIdentifier(Cow::Owned(s)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-08 06:04:44 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
2021-03-07 21:29:59 +01:00
|
|
|
pub enum BinaryOperator {
|
|
|
|
/// `+`
|
|
|
|
Add,
|
|
|
|
|
|
|
|
/// `-`
|
|
|
|
Sub,
|
|
|
|
|
|
|
|
/// `*`
|
|
|
|
Mul,
|
|
|
|
|
|
|
|
/// `/`
|
|
|
|
Div,
|
|
|
|
|
|
|
|
/// `^`
|
|
|
|
Pow,
|
|
|
|
|
|
|
|
/// `==`
|
|
|
|
Equ,
|
|
|
|
|
|
|
|
/// `!=`
|
|
|
|
Neq,
|
|
|
|
}
|
|
|
|
|
2021-03-08 06:04:44 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
2021-03-07 21:29:59 +01:00
|
|
|
pub enum UnaryOperator {
|
|
|
|
/// !
|
|
|
|
Not,
|
|
|
|
|
|
|
|
/// -
|
|
|
|
Neg,
|
|
|
|
}
|
|
|
|
|
2021-03-08 06:04:44 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
2021-03-14 16:53:13 +01:00
|
|
|
pub enum Literal<'a> {
|
2021-03-28 19:28:49 +02:00
|
|
|
Unit,
|
2021-03-07 21:29:59 +01:00
|
|
|
Int(u64),
|
2021-03-14 03:57:27 +01:00
|
|
|
Bool(bool),
|
2021-03-14 16:53:13 +01:00
|
|
|
String(Cow<'a, str>),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Literal<'a> {
|
|
|
|
pub fn to_owned(&self) -> Literal<'static> {
|
|
|
|
match self {
|
|
|
|
Literal::Int(i) => Literal::Int(*i),
|
|
|
|
Literal::Bool(b) => Literal::Bool(*b),
|
|
|
|
Literal::String(s) => Literal::String(Cow::Owned(s.clone().into_owned())),
|
2021-03-28 19:28:49 +02:00
|
|
|
Literal::Unit => Literal::Unit,
|
2021-03-14 16:53:13 +01:00
|
|
|
}
|
|
|
|
}
|
2021-03-07 21:29:59 +01:00
|
|
|
}
|
|
|
|
|
2021-04-17 08:28:24 +02:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
pub enum Pattern<'a> {
|
|
|
|
Id(Ident<'a>),
|
|
|
|
Tuple(Vec<Pattern<'a>>),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Pattern<'a> {
|
|
|
|
pub fn to_owned(&self) -> Pattern<'static> {
|
|
|
|
match self {
|
|
|
|
Pattern::Id(id) => Pattern::Id(id.to_owned()),
|
|
|
|
Pattern::Tuple(pats) => Pattern::Tuple(pats.iter().map(Pattern::to_owned).collect()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-13 19:12:03 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
pub struct Binding<'a> {
|
2021-04-17 08:28:24 +02:00
|
|
|
pub pat: Pattern<'a>,
|
2021-03-14 21:43:47 +01:00
|
|
|
pub type_: Option<Type<'a>>,
|
2021-03-13 19:12:03 +01:00
|
|
|
pub body: Expr<'a>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Binding<'a> {
|
|
|
|
fn to_owned(&self) -> Binding<'static> {
|
|
|
|
Binding {
|
2021-04-17 08:28:24 +02:00
|
|
|
pat: self.pat.to_owned(),
|
2021-03-14 21:43:47 +01:00
|
|
|
type_: self.type_.as_ref().map(|t| t.to_owned()),
|
2021-03-13 19:12:03 +01:00
|
|
|
body: self.body.to_owned(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-08 06:04:44 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
2021-03-07 21:29:59 +01:00
|
|
|
pub enum Expr<'a> {
|
|
|
|
Ident(Ident<'a>),
|
|
|
|
|
2021-03-14 16:53:13 +01:00
|
|
|
Literal(Literal<'a>),
|
2021-03-07 21:29:59 +01:00
|
|
|
|
|
|
|
UnaryOp {
|
|
|
|
op: UnaryOperator,
|
|
|
|
rhs: Box<Expr<'a>>,
|
|
|
|
},
|
|
|
|
|
|
|
|
BinaryOp {
|
|
|
|
lhs: Box<Expr<'a>>,
|
|
|
|
op: BinaryOperator,
|
|
|
|
rhs: Box<Expr<'a>>,
|
|
|
|
},
|
|
|
|
|
|
|
|
Let {
|
2021-03-13 19:12:03 +01:00
|
|
|
bindings: Vec<Binding<'a>>,
|
2021-03-07 21:29:59 +01:00
|
|
|
body: Box<Expr<'a>>,
|
|
|
|
},
|
|
|
|
|
|
|
|
If {
|
|
|
|
condition: Box<Expr<'a>>,
|
|
|
|
then: Box<Expr<'a>>,
|
|
|
|
else_: Box<Expr<'a>>,
|
|
|
|
},
|
2021-03-08 06:04:44 +01:00
|
|
|
|
|
|
|
Fun(Box<Fun<'a>>),
|
|
|
|
|
|
|
|
Call {
|
|
|
|
fun: Box<Expr<'a>>,
|
|
|
|
args: Vec<Expr<'a>>,
|
|
|
|
},
|
2021-03-13 19:12:03 +01:00
|
|
|
|
2021-04-17 08:28:24 +02:00
|
|
|
Tuple(Vec<Expr<'a>>),
|
|
|
|
|
2021-03-13 19:12:03 +01:00
|
|
|
Ascription {
|
|
|
|
expr: Box<Expr<'a>>,
|
2021-03-14 21:43:47 +01:00
|
|
|
type_: Type<'a>,
|
2021-03-13 19:12:03 +01:00
|
|
|
},
|
2021-03-07 21:29:59 +01:00
|
|
|
}
|
|
|
|
|
2021-03-08 06:04:44 +01:00
|
|
|
impl<'a> Expr<'a> {
|
|
|
|
pub fn to_owned(&self) -> Expr<'static> {
|
|
|
|
match self {
|
|
|
|
Expr::Ident(ref id) => Expr::Ident(id.to_owned()),
|
2021-03-14 16:53:13 +01:00
|
|
|
Expr::Literal(ref lit) => Expr::Literal(lit.to_owned()),
|
2021-04-17 08:28:24 +02:00
|
|
|
Expr::Tuple(ref members) => {
|
|
|
|
Expr::Tuple(members.into_iter().map(Expr::to_owned).collect())
|
|
|
|
}
|
2021-03-08 06:04:44 +01:00
|
|
|
Expr::UnaryOp { op, rhs } => Expr::UnaryOp {
|
|
|
|
op: *op,
|
|
|
|
rhs: Box::new((**rhs).to_owned()),
|
|
|
|
},
|
|
|
|
Expr::BinaryOp { lhs, op, rhs } => Expr::BinaryOp {
|
|
|
|
lhs: Box::new((**lhs).to_owned()),
|
|
|
|
op: *op,
|
|
|
|
rhs: Box::new((**rhs).to_owned()),
|
|
|
|
},
|
|
|
|
Expr::Let { bindings, body } => Expr::Let {
|
2021-03-13 19:12:03 +01:00
|
|
|
bindings: bindings.iter().map(|binding| binding.to_owned()).collect(),
|
2021-03-08 06:04:44 +01:00
|
|
|
body: Box::new((**body).to_owned()),
|
|
|
|
},
|
|
|
|
Expr::If {
|
|
|
|
condition,
|
|
|
|
then,
|
|
|
|
else_,
|
|
|
|
} => Expr::If {
|
|
|
|
condition: Box::new((**condition).to_owned()),
|
|
|
|
then: Box::new((**then).to_owned()),
|
|
|
|
else_: Box::new((**else_).to_owned()),
|
|
|
|
},
|
|
|
|
Expr::Fun(fun) => Expr::Fun(Box::new((**fun).to_owned())),
|
|
|
|
Expr::Call { fun, args } => Expr::Call {
|
|
|
|
fun: Box::new((**fun).to_owned()),
|
|
|
|
args: args.iter().map(|arg| arg.to_owned()).collect(),
|
|
|
|
},
|
2021-03-13 19:12:03 +01:00
|
|
|
Expr::Ascription { expr, type_ } => Expr::Ascription {
|
|
|
|
expr: Box::new((**expr).to_owned()),
|
2021-03-14 21:43:47 +01:00
|
|
|
type_: type_.to_owned(),
|
2021-03-13 19:12:03 +01:00
|
|
|
},
|
2021-03-08 06:04:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-14 21:43:47 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
pub struct Arg<'a> {
|
|
|
|
pub ident: Ident<'a>,
|
|
|
|
pub type_: Option<Type<'a>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Arg<'a> {
|
|
|
|
pub fn to_owned(&self) -> Arg<'static> {
|
|
|
|
Arg {
|
|
|
|
ident: self.ident.to_owned(),
|
|
|
|
type_: self.type_.as_ref().map(Type::to_owned),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> TryFrom<&'a str> for Arg<'a> {
|
|
|
|
type Error = <Ident<'a> as TryFrom<&'a str>>::Error;
|
|
|
|
|
|
|
|
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
|
|
|
|
Ok(Arg {
|
|
|
|
ident: Ident::try_from(value)?,
|
|
|
|
type_: None,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-08 06:04:44 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
2021-03-07 21:29:59 +01:00
|
|
|
pub struct Fun<'a> {
|
2021-03-14 21:43:47 +01:00
|
|
|
pub args: Vec<Arg<'a>>,
|
2021-03-07 21:29:59 +01:00
|
|
|
pub body: Expr<'a>,
|
|
|
|
}
|
|
|
|
|
2021-03-08 06:04:44 +01:00
|
|
|
impl<'a> Fun<'a> {
|
2021-03-14 21:43:47 +01:00
|
|
|
pub fn to_owned(&self) -> Fun<'static> {
|
2021-03-08 06:04:44 +01:00
|
|
|
Fun {
|
|
|
|
args: self.args.iter().map(|arg| arg.to_owned()).collect(),
|
|
|
|
body: self.body.to_owned(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-14 21:43:47 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
2021-03-07 21:29:59 +01:00
|
|
|
pub enum Decl<'a> {
|
2021-03-20 01:46:19 +01:00
|
|
|
Fun {
|
|
|
|
name: Ident<'a>,
|
|
|
|
body: Fun<'a>,
|
|
|
|
},
|
|
|
|
Ascription {
|
|
|
|
name: Ident<'a>,
|
|
|
|
type_: Type<'a>,
|
|
|
|
},
|
|
|
|
Extern {
|
|
|
|
name: Ident<'a>,
|
|
|
|
type_: FunctionType<'a>,
|
|
|
|
},
|
2021-03-08 06:04:44 +01:00
|
|
|
}
|
|
|
|
|
2021-03-14 21:43:47 +01:00
|
|
|
////
|
|
|
|
|
2021-03-08 06:04:44 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
2021-03-14 21:43:47 +01:00
|
|
|
pub struct FunctionType<'a> {
|
|
|
|
pub args: Vec<Type<'a>>,
|
|
|
|
pub ret: Box<Type<'a>>,
|
2021-03-08 06:04:44 +01:00
|
|
|
}
|
|
|
|
|
2021-03-14 21:43:47 +01:00
|
|
|
impl<'a> FunctionType<'a> {
|
|
|
|
pub fn to_owned(&self) -> FunctionType<'static> {
|
|
|
|
FunctionType {
|
|
|
|
args: self.args.iter().map(|a| a.to_owned()).collect(),
|
|
|
|
ret: Box::new((*self.ret).to_owned()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Display for FunctionType<'a> {
|
2021-03-08 06:04:44 +01:00
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(f, "fn {} -> {}", self.args.iter().join(", "), self.ret)
|
|
|
|
}
|
2021-03-07 21:29:59 +01:00
|
|
|
}
|
|
|
|
|
2021-03-08 06:04:44 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
2021-03-14 21:43:47 +01:00
|
|
|
pub enum Type<'a> {
|
2021-03-07 21:29:59 +01:00
|
|
|
Int,
|
|
|
|
Float,
|
|
|
|
Bool,
|
2021-03-14 16:53:13 +01:00
|
|
|
CString,
|
2021-03-28 19:28:49 +02:00
|
|
|
Unit,
|
2021-04-17 08:28:24 +02:00
|
|
|
Tuple(Vec<Type<'a>>),
|
2021-03-14 21:43:47 +01:00
|
|
|
Var(Ident<'a>),
|
|
|
|
Function(FunctionType<'a>),
|
2021-03-07 21:29:59 +01:00
|
|
|
}
|
|
|
|
|
2021-03-14 21:43:47 +01:00
|
|
|
impl<'a> Type<'a> {
|
|
|
|
pub fn to_owned(&self) -> Type<'static> {
|
|
|
|
match self {
|
|
|
|
Type::Int => Type::Int,
|
|
|
|
Type::Float => Type::Float,
|
|
|
|
Type::Bool => Type::Bool,
|
|
|
|
Type::CString => Type::CString,
|
2021-03-28 19:28:49 +02:00
|
|
|
Type::Unit => Type::Unit,
|
2021-03-14 21:43:47 +01:00
|
|
|
Type::Var(v) => Type::Var(v.to_owned()),
|
|
|
|
Type::Function(f) => Type::Function(f.to_owned()),
|
2021-04-17 08:28:24 +02:00
|
|
|
Type::Tuple(members) => Type::Tuple(members.iter().map(Type::to_owned).collect()),
|
2021-03-14 21:43:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn alpha_equiv(&self, other: &Self) -> bool {
|
|
|
|
fn do_alpha_equiv<'a>(
|
|
|
|
substs: &mut HashMap<&'a Ident<'a>, &'a Ident<'a>>,
|
|
|
|
lhs: &'a Type,
|
|
|
|
rhs: &'a Type,
|
|
|
|
) -> bool {
|
|
|
|
match (lhs, rhs) {
|
|
|
|
(Type::Var(v1), Type::Var(v2)) => substs.entry(v1).or_insert(v2) == &v2,
|
|
|
|
(
|
|
|
|
Type::Function(FunctionType {
|
|
|
|
args: args1,
|
|
|
|
ret: ret1,
|
|
|
|
}),
|
|
|
|
Type::Function(FunctionType {
|
|
|
|
args: args2,
|
|
|
|
ret: ret2,
|
|
|
|
}),
|
|
|
|
) => {
|
|
|
|
args1.len() == args2.len()
|
|
|
|
&& args1
|
|
|
|
.iter()
|
|
|
|
.zip(args2)
|
|
|
|
.all(|(a1, a2)| do_alpha_equiv(substs, a1, a2))
|
|
|
|
&& do_alpha_equiv(substs, ret1, ret2)
|
|
|
|
}
|
|
|
|
_ => lhs == rhs,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut substs = HashMap::new();
|
|
|
|
do_alpha_equiv(&mut substs, self, other)
|
|
|
|
}
|
2021-03-20 23:14:23 +01:00
|
|
|
|
|
|
|
pub fn traverse_type_vars<'b, F>(self, mut f: F) -> Type<'b>
|
|
|
|
where
|
|
|
|
F: FnMut(Ident<'a>) -> Type<'b> + Clone,
|
|
|
|
{
|
|
|
|
match self {
|
|
|
|
Type::Var(tv) => f(tv),
|
|
|
|
Type::Function(FunctionType { args, ret }) => Type::Function(FunctionType {
|
|
|
|
args: args
|
|
|
|
.into_iter()
|
|
|
|
.map(|t| t.traverse_type_vars(f.clone()))
|
|
|
|
.collect(),
|
|
|
|
ret: Box::new(ret.traverse_type_vars(f)),
|
|
|
|
}),
|
|
|
|
Type::Int => Type::Int,
|
|
|
|
Type::Float => Type::Float,
|
|
|
|
Type::Bool => Type::Bool,
|
|
|
|
Type::CString => Type::CString,
|
2021-04-17 08:28:24 +02:00
|
|
|
Type::Tuple(members) => Type::Tuple(
|
|
|
|
members
|
|
|
|
.into_iter()
|
|
|
|
.map(|t| t.traverse_type_vars(f.clone()))
|
|
|
|
.collect(),
|
|
|
|
),
|
2021-03-28 19:28:49 +02:00
|
|
|
Type::Unit => Type::Unit,
|
2021-03-20 23:14:23 +01:00
|
|
|
}
|
|
|
|
}
|
2021-04-17 08:28:24 +02:00
|
|
|
|
|
|
|
pub fn as_tuple(&self) -> Option<&Vec<Type<'a>>> {
|
|
|
|
if let Self::Tuple(v) = self {
|
|
|
|
Some(v)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2021-03-14 21:43:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Display for Type<'a> {
|
2021-03-07 21:29:59 +01:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
match self {
|
2021-03-08 06:04:44 +01:00
|
|
|
Type::Int => f.write_str("int"),
|
|
|
|
Type::Float => f.write_str("float"),
|
|
|
|
Type::Bool => f.write_str("bool"),
|
2021-03-14 16:53:13 +01:00
|
|
|
Type::CString => f.write_str("cstring"),
|
2021-03-28 19:28:49 +02:00
|
|
|
Type::Unit => f.write_str("()"),
|
2021-03-14 21:43:47 +01:00
|
|
|
Type::Var(v) => v.fmt(f),
|
2021-03-08 06:04:44 +01:00
|
|
|
Type::Function(ft) => ft.fmt(f),
|
2021-04-17 08:28:24 +02:00
|
|
|
Type::Tuple(ms) => write!(f, "({})", ms.iter().join(", ")),
|
2021-03-07 21:29:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-14 21:43:47 +01:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
fn type_var(n: &str) -> Type<'static> {
|
|
|
|
Type::Var(Ident::try_from(n.to_owned()).unwrap())
|
|
|
|
}
|
|
|
|
|
|
|
|
mod alpha_equiv {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn trivial() {
|
|
|
|
assert!(Type::Int.alpha_equiv(&Type::Int));
|
|
|
|
assert!(!Type::Int.alpha_equiv(&Type::Bool));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn simple_type_var() {
|
|
|
|
assert!(type_var("a").alpha_equiv(&type_var("b")));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn function_with_type_vars_equiv() {
|
|
|
|
assert!(Type::Function(FunctionType {
|
|
|
|
args: vec![type_var("a")],
|
|
|
|
ret: Box::new(type_var("b")),
|
|
|
|
})
|
|
|
|
.alpha_equiv(&Type::Function(FunctionType {
|
|
|
|
args: vec![type_var("b")],
|
|
|
|
ret: Box::new(type_var("a")),
|
|
|
|
})))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn function_with_type_vars_non_equiv() {
|
|
|
|
assert!(!Type::Function(FunctionType {
|
|
|
|
args: vec![type_var("a")],
|
|
|
|
ret: Box::new(type_var("a")),
|
|
|
|
})
|
|
|
|
.alpha_equiv(&Type::Function(FunctionType {
|
|
|
|
args: vec![type_var("b")],
|
|
|
|
ret: Box::new(type_var("a")),
|
|
|
|
})))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|