You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
212 lines
6.0 KiB
212 lines
6.0 KiB
use std::{fmt::Display, str};
|
|
|
|
pub type Ident = String;
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum PrimitiveType {
|
|
Nat,
|
|
Bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum TypeTag {
|
|
Num,
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum TaggedType {
|
|
Tagged(TypeTag, Ident, Box<TaggedType>),
|
|
Concrete(Type),
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum Type {
|
|
Generic(Ident), // a
|
|
Primitive(PrimitiveType), // Bool | Nat
|
|
Arrow(Box<Type>, Box<Type>), // 0 -> 1
|
|
}
|
|
|
|
impl From<Type> for TaggedType {
|
|
fn from(value: Type) -> Self {
|
|
Self::Concrete(value)
|
|
}
|
|
}
|
|
|
|
impl TaggedType {
|
|
pub fn r#type(&self) -> &Type {
|
|
match self {
|
|
TaggedType::Tagged(_, _, tagged_type) => tagged_type.r#type(),
|
|
TaggedType::Concrete(t) => t,
|
|
}
|
|
}
|
|
|
|
pub fn type_mut(&mut self) -> &mut Type {
|
|
match self {
|
|
TaggedType::Tagged(_, _, tagged_type) => tagged_type.type_mut(),
|
|
TaggedType::Concrete(t) => t,
|
|
}
|
|
}
|
|
|
|
pub fn to_concrete(self) -> Option<Type> {
|
|
match self {
|
|
TaggedType::Tagged(type_tag, _, tagged_type) => None,
|
|
TaggedType::Concrete(t) => {
|
|
if t.is_concrete() {
|
|
Some(t)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn matches(&self, typ: &Type) -> (bool, Option<Ident>) {
|
|
match self {
|
|
TaggedType::Tagged(type_tag, ident, tagged_type) => {
|
|
if typ.has_tag(type_tag) {
|
|
(true, Some(ident.clone()))
|
|
} else {
|
|
tagged_type.matches(typ)
|
|
}
|
|
}
|
|
TaggedType::Concrete(c) => (c == typ, None),
|
|
}
|
|
}
|
|
|
|
pub fn arrow(self) -> Option<(TaggedType, TaggedType)> {
|
|
match self {
|
|
TaggedType::Tagged(type_tag, ident, tagged_type) => {
|
|
let (lhs, rhs) = tagged_type.arrow()?;
|
|
Some((
|
|
TaggedType::Tagged(type_tag.clone(), ident.clone(), Box::new(lhs))
|
|
.clear_unused_names(),
|
|
TaggedType::Tagged(type_tag, ident, Box::new(rhs)).clear_unused_names(),
|
|
))
|
|
}
|
|
TaggedType::Concrete(Type::Arrow(lhs, rhs)) => {
|
|
Some((TaggedType::Concrete(*lhs), TaggedType::Concrete(*rhs)))
|
|
}
|
|
TaggedType::Concrete(_) => None,
|
|
}
|
|
}
|
|
|
|
pub fn specialize(self, ident: &str, typ: &Type) -> TaggedType {
|
|
match self {
|
|
TaggedType::Tagged(_, i, tagged_type) if i == ident => {
|
|
tagged_type.specialize(ident, typ)
|
|
}
|
|
TaggedType::Tagged(t, i, tagged_type) => {
|
|
TaggedType::Tagged(t, i, Box::new(tagged_type.specialize(ident, typ)))
|
|
}
|
|
TaggedType::Concrete(c) => TaggedType::Concrete(c.specialize(ident, typ)),
|
|
}
|
|
}
|
|
|
|
fn name_used(&self, ident: &str) -> bool {
|
|
match self {
|
|
TaggedType::Tagged(_, _, tagged_type) => tagged_type.name_used(ident),
|
|
TaggedType::Concrete(c) => c.name_used(ident),
|
|
}
|
|
}
|
|
|
|
fn clear_unused_names(self) -> TaggedType {
|
|
match self {
|
|
TaggedType::Tagged(type_tag, ident, tagged_type) => {
|
|
if tagged_type.name_used(&ident) {
|
|
TaggedType::Tagged(type_tag, ident, tagged_type)
|
|
} else {
|
|
*tagged_type
|
|
}
|
|
}
|
|
t => t,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<PrimitiveType> for Type {
|
|
fn from(value: PrimitiveType) -> Self {
|
|
Self::Primitive(value)
|
|
}
|
|
}
|
|
|
|
impl Type {
|
|
pub fn arrow<T1: Into<Type>, T2: Into<Type>>(t1: T1, t2: T2) -> Self {
|
|
Self::Arrow(Box::new(t1.into()), Box::new(t2.into()))
|
|
}
|
|
|
|
pub fn is_concrete(&self) -> bool {
|
|
match self {
|
|
Type::Generic(_) => false,
|
|
Type::Primitive(primitive_type) => true,
|
|
Type::Arrow(t1, t2) => t1.is_concrete() && t2.is_concrete(),
|
|
}
|
|
}
|
|
|
|
pub fn has_tag(&self, tag: &TypeTag) -> bool {
|
|
match self {
|
|
Type::Generic(_) => false,
|
|
Type::Primitive(primitive_type) => match (primitive_type, tag) {
|
|
(PrimitiveType::Nat, TypeTag::Num) => true,
|
|
_ => false,
|
|
},
|
|
Type::Arrow(_, _) => false,
|
|
}
|
|
}
|
|
|
|
fn name_used(&self, ident: &str) -> bool {
|
|
match self {
|
|
Type::Generic(i) if *i == ident => true,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
fn specialize(self, ident: &str, typ: &Type) -> Type {
|
|
match self {
|
|
Type::Generic(i) if i == ident => typ.clone(),
|
|
Type::Arrow(lhs, rhs) => Type::Arrow(
|
|
Box::new(lhs.specialize(ident, typ)),
|
|
Box::new(rhs.specialize(ident, typ)),
|
|
),
|
|
t => t,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for TypeTag {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
TypeTag::Num => write!(f, "Num"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for TaggedType {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
TaggedType::Tagged(type_tag, ident, tagged_type) => {
|
|
write!(f, "{type_tag} {ident} => {tagged_type}")
|
|
}
|
|
TaggedType::Concrete(t) => write!(f, "{t}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for Type {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Type::Generic(i) => write!(f, "{i}"),
|
|
Type::Primitive(primitive_type) => write!(f, "{primitive_type}"),
|
|
Type::Arrow(t1, t2) => write!(f, "({t1} -> {t2})"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for PrimitiveType {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
PrimitiveType::Nat => write!(f, "Nat"),
|
|
PrimitiveType::Bool => write!(f, "Bool"),
|
|
}
|
|
}
|
|
}
|