parent
9f69cfd65b
commit
3fc99af58b
@ -0,0 +1,138 @@
|
|||||||
|
use std::{collections::HashSet, fmt::Display};
|
||||||
|
|
||||||
|
use crate::parse::Ident;
|
||||||
|
|
||||||
|
use super::tagged::TypeTag;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum PrimitiveType {
|
||||||
|
Nat,
|
||||||
|
Bool,
|
||||||
|
Float,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Type {
|
||||||
|
Variable(Ident), // a
|
||||||
|
Primitive(PrimitiveType), // Bool | Nat
|
||||||
|
Arrow(Box<Type>, Box<Type>), // 0 -> 1
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PrimitiveType> for Type {
|
||||||
|
fn from(value: PrimitiveType) -> Self {
|
||||||
|
Self::Primitive(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Type {
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
Self::Variable(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 split_arrow(self) -> Option<(Type, Type)> {
|
||||||
|
match self {
|
||||||
|
Type::Arrow(lhs, rhs) => Some((*lhs, *rhs)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_concrete(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Type::Variable(_) => false,
|
||||||
|
Type::Primitive(_) => true,
|
||||||
|
Type::Arrow(t1, t2) => t1.is_concrete() && t2.is_concrete(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_tag(&self, tag: &TypeTag) -> bool {
|
||||||
|
match self {
|
||||||
|
Type::Variable(_) => true,
|
||||||
|
Type::Primitive(primitive_type) => matches!(
|
||||||
|
(primitive_type, tag),
|
||||||
|
(_, TypeTag::Any) | (PrimitiveType::Nat | PrimitiveType::Float, TypeTag::Num)
|
||||||
|
),
|
||||||
|
Type::Arrow(_, _) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_name<F: Fn(&mut String)>(&mut self, f: &F) {
|
||||||
|
match self {
|
||||||
|
Type::Variable(v) => f(v),
|
||||||
|
Type::Arrow(lhs, rhs) => {
|
||||||
|
lhs.map_name(f);
|
||||||
|
rhs.map_name(f);
|
||||||
|
}
|
||||||
|
Type::Primitive(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name_used(&self, ident: &str) -> bool {
|
||||||
|
match self {
|
||||||
|
Type::Variable(i) if *i == ident => true,
|
||||||
|
Type::Arrow(lhs, rhs) => lhs.name_used(ident) || rhs.name_used(ident),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_var(&self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
Type::Variable(v) => Some(v.clone()),
|
||||||
|
Type::Primitive(_) | Type::Arrow(_, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_vars(&self) -> HashSet<String> {
|
||||||
|
match self {
|
||||||
|
Type::Variable(v) => {
|
||||||
|
let mut set = HashSet::new();
|
||||||
|
set.insert(v.to_string());
|
||||||
|
set
|
||||||
|
}
|
||||||
|
Type::Primitive(_) => HashSet::new(),
|
||||||
|
Type::Arrow(lhs, rhs) => {
|
||||||
|
let mut vars = lhs.type_vars();
|
||||||
|
for var in rhs.type_vars() {
|
||||||
|
vars.insert(var);
|
||||||
|
}
|
||||||
|
vars
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn specialize(self, ident: &str, typ: &Type) -> Type {
|
||||||
|
match self {
|
||||||
|
Type::Variable(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 Type {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Type::Variable(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"),
|
||||||
|
PrimitiveType::Float => write!(f, "Float"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,363 +1,5 @@
|
|||||||
use std::{
|
mod concrete;
|
||||||
collections::{HashMap, HashSet},
|
mod tagged;
|
||||||
fmt::Display,
|
|
||||||
mem,
|
|
||||||
rc::Rc,
|
|
||||||
str,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type Ident = String;
|
pub use concrete::{PrimitiveType, Type};
|
||||||
|
pub use tagged::{TaggedType, TypeTag};
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub enum PrimitiveType {
|
|
||||||
Nat,
|
|
||||||
Bool,
|
|
||||||
Float,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub enum TypeTag {
|
|
||||||
Num,
|
|
||||||
Any,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub enum TaggedType {
|
|
||||||
Tagged(TypeTag, Ident, Box<TaggedType>),
|
|
||||||
Concrete(Type),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub enum Type {
|
|
||||||
Variable(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 TypeTag {
|
|
||||||
// If one tag tightens the other return that tag, otherwise none,
|
|
||||||
pub fn tightens(self, other: Self) -> Option<Self> {
|
|
||||||
match (self, other) {
|
|
||||||
(TypeTag::Num, TypeTag::Num) => Some(Self::Num),
|
|
||||||
(TypeTag::Num, TypeTag::Any) => Some(Self::Num),
|
|
||||||
(TypeTag::Any, TypeTag::Num) => Some(Self::Num),
|
|
||||||
(TypeTag::Any, TypeTag::Any) => Some(Self::Any),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 into_concrete(self) -> Option<Type> {
|
|
||||||
match self {
|
|
||||||
TaggedType::Tagged(type_tag, _, tagged_type) => None,
|
|
||||||
TaggedType::Concrete(t) => Some(t),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 split_arrow(self) -> Option<(TaggedType, TaggedType)> {
|
|
||||||
match self {
|
|
||||||
TaggedType::Tagged(type_tag, ident, tagged_type) => {
|
|
||||||
let (lhs, rhs) = tagged_type.split_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 make_arrow(self, rhs: TaggedType) -> Self {
|
|
||||||
match self {
|
|
||||||
TaggedType::Tagged(type_tag, ident, tagged_type) => {
|
|
||||||
TaggedType::Tagged(type_tag, ident, Box::new(tagged_type.make_arrow(rhs)))
|
|
||||||
}
|
|
||||||
TaggedType::Concrete(t) => rhs.make_arrow_lhs(t),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_arrow_lhs(self, lhs: Type) -> Self {
|
|
||||||
match self {
|
|
||||||
TaggedType::Tagged(type_tag, ident, tagged_type) => {
|
|
||||||
TaggedType::Tagged(type_tag, ident, Box::new(tagged_type.make_arrow_lhs(lhs)))
|
|
||||||
}
|
|
||||||
TaggedType::Concrete(t) => Type::arrow(lhs, t).into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn map_name<F: Fn(&mut String)>(&mut self, f: F) {
|
|
||||||
match self {
|
|
||||||
TaggedType::Tagged(type_tag, ident, tagged_type) => {
|
|
||||||
f(ident);
|
|
||||||
tagged_type.map_name(f);
|
|
||||||
}
|
|
||||||
TaggedType::Concrete(t) => t.map_name(&f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_var(&self) -> Option<String> {
|
|
||||||
match self {
|
|
||||||
TaggedType::Tagged(type_tag, _, tagged_type) => tagged_type.type_var(),
|
|
||||||
TaggedType::Concrete(c) => c.type_var(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn free_vars(&self) -> HashSet<String> {
|
|
||||||
match self {
|
|
||||||
TaggedType::Tagged(type_tag, ident, tagged_type) => {
|
|
||||||
let mut vars = tagged_type.free_vars();
|
|
||||||
vars.retain(|v| v != ident);
|
|
||||||
vars
|
|
||||||
}
|
|
||||||
TaggedType::Concrete(c) => c.type_vars(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub 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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_concrete(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
TaggedType::Tagged(type_tag, _, tagged_type) => tagged_type.is_concrete(),
|
|
||||||
TaggedType::Concrete(c) => c.is_concrete(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn normalise(self) -> TaggedType {
|
|
||||||
let only_used = self.clear_unused_names();
|
|
||||||
fn dedup(this: TaggedType, mut used: Rc<HashMap<String, TypeTag>>) -> TaggedType {
|
|
||||||
match this {
|
|
||||||
TaggedType::Tagged(type_tag, ident, tagged_type) => {
|
|
||||||
if !used.contains_key(&ident) {
|
|
||||||
Rc::make_mut(&mut used).insert(ident, type_tag);
|
|
||||||
dedup(*tagged_type, used)
|
|
||||||
} else {
|
|
||||||
let Some(tag) = TypeTag::tightens(
|
|
||||||
Rc::make_mut(&mut used).remove(&ident).unwrap(),
|
|
||||||
type_tag,
|
|
||||||
) else {
|
|
||||||
todo!()
|
|
||||||
};
|
|
||||||
Rc::make_mut(&mut used).insert(ident.clone(), tag.clone());
|
|
||||||
dedup(*tagged_type, used)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TaggedType::Concrete(c) => mem::take(Rc::make_mut(&mut used))
|
|
||||||
.into_iter()
|
|
||||||
.fold(TaggedType::Concrete(c), |acc, (i, t)| {
|
|
||||||
TaggedType::Tagged(t, i, Box::new(acc))
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dedup(only_used, Rc::new(HashMap::new()))
|
|
||||||
}
|
|
||||||
|
|
||||||
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, Box::new(tagged_type.clear_unused_names()))
|
|
||||||
} else {
|
|
||||||
tagged_type.clear_unused_names()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t => t,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PrimitiveType> for Type {
|
|
||||||
fn from(value: PrimitiveType) -> Self {
|
|
||||||
Self::Primitive(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for Type {
|
|
||||||
fn from(value: &str) -> Self {
|
|
||||||
Self::Variable(value.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 split_arrow(self) -> Option<(Type, Type)> {
|
|
||||||
match self {
|
|
||||||
Type::Arrow(lhs, rhs) => Some((*lhs, *rhs)),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_concrete(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Type::Variable(_) => 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::Variable(_) => true,
|
|
||||||
Type::Primitive(primitive_type) => matches!(
|
|
||||||
(primitive_type, tag),
|
|
||||||
(_, TypeTag::Any)
|
|
||||||
| (PrimitiveType::Nat, TypeTag::Num)
|
|
||||||
| (PrimitiveType::Float, TypeTag::Num)
|
|
||||||
),
|
|
||||||
Type::Arrow(_, _) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn map_name<F: Fn(&mut String)>(&mut self, f: &F) {
|
|
||||||
match self {
|
|
||||||
Type::Variable(v) => f(v),
|
|
||||||
Type::Arrow(lhs, rhs) => {
|
|
||||||
lhs.map_name(f);
|
|
||||||
rhs.map_name(f);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn name_used(&self, ident: &str) -> bool {
|
|
||||||
match self {
|
|
||||||
Type::Variable(i) if *i == ident => true,
|
|
||||||
Type::Arrow(lhs, rhs) => lhs.name_used(ident) || rhs.name_used(ident),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_var(&self) -> Option<String> {
|
|
||||||
match self {
|
|
||||||
Type::Variable(v) => Some(v.clone()),
|
|
||||||
Type::Primitive(primitive_type) => None,
|
|
||||||
Type::Arrow(_, _) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_vars(&self) -> HashSet<String> {
|
|
||||||
match self {
|
|
||||||
Type::Variable(v) => {
|
|
||||||
let mut set = HashSet::new();
|
|
||||||
set.insert(v.to_string());
|
|
||||||
set
|
|
||||||
}
|
|
||||||
Type::Primitive(primitive_type) => HashSet::new(),
|
|
||||||
Type::Arrow(lhs, rhs) => {
|
|
||||||
let mut vars = lhs.type_vars();
|
|
||||||
for var in rhs.type_vars().into_iter() {
|
|
||||||
vars.insert(var);
|
|
||||||
}
|
|
||||||
vars
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn specialize(self, ident: &str, typ: &Type) -> Type {
|
|
||||||
match self {
|
|
||||||
Type::Variable(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"),
|
|
||||||
TypeTag::Any => write!(f, "Any"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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::Variable(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"),
|
|
||||||
PrimitiveType::Float => write!(f, "Float"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,180 @@
|
|||||||
|
use std::{collections::HashSet, fmt::Display};
|
||||||
|
|
||||||
|
use crate::parse::Ident;
|
||||||
|
|
||||||
|
use super::concrete::Type;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum TypeTag {
|
||||||
|
Num,
|
||||||
|
Any,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum TaggedType {
|
||||||
|
Tagged(TypeTag, Ident, Box<TaggedType>),
|
||||||
|
Concrete(Type),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Type> for TaggedType {
|
||||||
|
fn from(value: Type) -> Self {
|
||||||
|
Self::Concrete(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeTag {
|
||||||
|
// If one tag tightens the other return that tag, otherwise none,
|
||||||
|
pub fn tightens(self, other: Self) -> Option<Self> {
|
||||||
|
#[allow(unreachable_patterns)] // #[non_exhaustive] doesn't apply with tuples
|
||||||
|
match (self, other) {
|
||||||
|
(TypeTag::Any, t) | (t, TypeTag::Any) => Some(t),
|
||||||
|
(TypeTag::Num, TypeTag::Num) => Some(Self::Num),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TaggedType {
|
||||||
|
pub fn into_concrete(self) -> Option<Type> {
|
||||||
|
match self {
|
||||||
|
TaggedType::Tagged(_, _, _) => None,
|
||||||
|
TaggedType::Concrete(t) => Some(t),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 split_arrow(self) -> Option<(TaggedType, TaggedType)> {
|
||||||
|
match self {
|
||||||
|
TaggedType::Tagged(type_tag, ident, tagged_type) => {
|
||||||
|
let (lhs, rhs) = tagged_type.split_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 make_arrow(self, rhs: TaggedType) -> Self {
|
||||||
|
match self {
|
||||||
|
TaggedType::Tagged(type_tag, ident, tagged_type) => {
|
||||||
|
TaggedType::Tagged(type_tag, ident, Box::new(tagged_type.make_arrow(rhs)))
|
||||||
|
}
|
||||||
|
TaggedType::Concrete(t) => rhs.make_arrow_lhs(t),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_arrow_lhs(self, lhs: Type) -> Self {
|
||||||
|
match self {
|
||||||
|
TaggedType::Tagged(type_tag, ident, tagged_type) => {
|
||||||
|
TaggedType::Tagged(type_tag, ident, Box::new(tagged_type.make_arrow_lhs(lhs)))
|
||||||
|
}
|
||||||
|
TaggedType::Concrete(t) => Type::arrow(lhs, t).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_name<F: Fn(&mut String)>(&mut self, f: F) {
|
||||||
|
match self {
|
||||||
|
TaggedType::Tagged(_, ident, tagged_type) => {
|
||||||
|
f(ident);
|
||||||
|
tagged_type.map_name(f);
|
||||||
|
}
|
||||||
|
TaggedType::Concrete(t) => t.map_name(&f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_var(&self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
TaggedType::Tagged(_, _, tagged_type) => tagged_type.type_var(),
|
||||||
|
TaggedType::Concrete(c) => c.type_var(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn free_vars(&self) -> HashSet<String> {
|
||||||
|
match self {
|
||||||
|
TaggedType::Tagged(_, ident, tagged_type) => {
|
||||||
|
let mut vars = tagged_type.free_vars();
|
||||||
|
vars.retain(|v| v != ident);
|
||||||
|
vars
|
||||||
|
}
|
||||||
|
TaggedType::Concrete(c) => c.type_vars(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_concrete(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
TaggedType::Tagged(_, _, tagged_type) => tagged_type.is_concrete(),
|
||||||
|
TaggedType::Concrete(c) => c.is_concrete(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, Box::new(tagged_type.clear_unused_names()))
|
||||||
|
} else {
|
||||||
|
tagged_type.clear_unused_names()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t @ TaggedType::Concrete(_) => t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TypeTag {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
TypeTag::Num => write!(f, "Num"),
|
||||||
|
TypeTag::Any => write!(f, "Any"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue