use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Type {
Int,
Bool,
String,
Var(String),
Named { name: String, args: Vec<Type> },
Quotation(Box<Effect>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Effect {
pub inputs: StackType,
pub outputs: StackType,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StackType {
Empty,
Cons { rest: Box<StackType>, top: Type },
RowVar(String),
}
impl StackType {
pub fn empty() -> Self {
StackType::Empty
}
pub fn push(self, ty: Type) -> Self {
StackType::Cons {
rest: Box::new(self),
top: ty,
}
}
pub fn from_vec(types: Vec<Type>) -> Self {
types
.into_iter()
.fold(StackType::Empty, |stack, ty| stack.push(ty))
}
pub fn pop(self) -> Option<(StackType, Type)> {
match self {
StackType::Cons { rest, top } => Some((*rest, top)),
StackType::Empty => None,
StackType::RowVar(_) => None, }
}
pub fn depth(&self) -> Option<usize> {
match self {
StackType::Empty => Some(0),
StackType::Cons { rest, .. } => rest.depth().map(|d| d + 1),
StackType::RowVar(_) => None, }
}
pub fn is_row_var(&self) -> bool {
matches!(self, StackType::RowVar(_))
}
}
impl Effect {
pub fn new(inputs: StackType, outputs: StackType) -> Self {
Effect { inputs, outputs }
}
pub fn from_vecs(inputs: Vec<Type>, outputs: Vec<Type>) -> Self {
Effect {
inputs: StackType::from_vec(inputs),
outputs: StackType::from_vec(outputs),
}
}
pub fn compose(first: &Effect, second: &Effect) -> Option<Effect> {
if first.outputs == second.inputs {
Some(Effect {
inputs: first.inputs.clone(),
outputs: second.outputs.clone(),
})
} else {
None
}
}
}
impl Type {
pub fn is_copy(&self) -> bool {
match self {
Type::Int | Type::Bool => true,
Type::String => false,
Type::Var(_) => false, Type::Named { .. } => false, Type::Quotation(_) => true, }
}
pub fn is_linear(&self) -> bool {
!self.is_copy()
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Type::Int => write!(f, "Int"),
Type::Bool => write!(f, "Bool"),
Type::String => write!(f, "String"),
Type::Var(name) => write!(f, "{}", name),
Type::Named { name, args } => {
write!(f, "{}", name)?;
if !args.is_empty() {
write!(f, "<")?;
for (i, arg) in args.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", arg)?;
}
write!(f, ">")?;
}
Ok(())
}
Type::Quotation(eff) => write!(f, "[{}]", eff),
}
}
}
impl fmt::Display for StackType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StackType::Empty => write!(f, ""),
StackType::Cons { rest, top } => {
if !matches!(**rest, StackType::Empty) {
write!(f, "{} ", rest)?;
}
write!(f, "{}", top)
}
StackType::RowVar(name) => write!(f, "{}", name),
}
}
}
impl fmt::Display for Effect {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "( {} -- {} )", self.inputs, self.outputs)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stack_operations() {
let stack = StackType::empty().push(Type::Int).push(Type::Bool);
assert_eq!(stack.depth(), Some(2));
let (rest, top) = stack.pop().unwrap();
assert_eq!(top, Type::Bool);
assert_eq!(rest.depth(), Some(1));
}
#[test]
fn test_effect_composition() {
let dup = Effect::from_vecs(
vec![Type::Var("A".to_string())],
vec![Type::Var("A".to_string()), Type::Var("A".to_string())],
);
let add = Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Int]);
assert!(Effect::compose(&dup, &add).is_none());
let dup_int = Effect::from_vecs(vec![Type::Int], vec![Type::Int, Type::Int]);
let composed = Effect::compose(&dup_int, &add);
assert!(composed.is_some());
let composed = composed.unwrap();
assert_eq!(composed.inputs.depth(), Some(1));
assert_eq!(composed.outputs.depth(), Some(1));
}
#[test]
fn test_copy_types() {
assert!(Type::Int.is_copy());
assert!(Type::Bool.is_copy());
assert!(!Type::String.is_copy());
assert!(Type::String.is_linear());
}
}