#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SideEffect {
Yield(Box<Type>),
}
impl std::fmt::Display for SideEffect {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SideEffect::Yield(ty) => write!(f, "Yield {}", ty),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Type {
Int,
Float,
Bool,
String,
Symbol,
Channel,
Quotation(Box<Effect>),
Closure {
effect: Box<Effect>,
captures: Vec<Type>,
},
Union(String),
Var(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VariantFieldInfo {
pub name: String,
pub field_type: Type,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VariantInfo {
pub name: String,
pub fields: Vec<VariantFieldInfo>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnionTypeInfo {
pub name: String,
pub variants: Vec<VariantInfo>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum StackType {
Empty,
Cons {
rest: Box<StackType>,
top: Type,
},
RowVar(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Effect {
pub inputs: StackType,
pub outputs: StackType,
pub effects: Vec<SideEffect>,
}
impl StackType {
pub fn empty() -> Self {
StackType::Empty
}
pub fn singleton(ty: Type) -> Self {
StackType::Cons {
rest: Box::new(StackType::Empty),
top: ty,
}
}
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)),
_ => None,
}
}
}
impl Effect {
pub fn new(inputs: StackType, outputs: StackType) -> Self {
Effect {
inputs,
outputs,
effects: Vec::new(),
}
}
pub fn with_effects(inputs: StackType, outputs: StackType, effects: Vec<SideEffect>) -> Self {
Effect {
inputs,
outputs,
effects,
}
}
pub fn is_pure(&self) -> bool {
self.effects.is_empty()
}
pub fn has_yield(&self) -> bool {
self.effects
.iter()
.any(|e| matches!(e, SideEffect::Yield(_)))
}
}
impl std::fmt::Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Type::Int => write!(f, "Int"),
Type::Float => write!(f, "Float"),
Type::Bool => write!(f, "Bool"),
Type::String => write!(f, "String"),
Type::Symbol => write!(f, "Symbol"),
Type::Channel => write!(f, "Channel"),
Type::Quotation(effect) => write!(f, "[{}]", effect),
Type::Closure { effect, captures } => {
let cap_str: Vec<_> = captures.iter().map(|t| format!("{}", t)).collect();
write!(f, "Closure[{}, captures=({})]", effect, cap_str.join(", "))
}
Type::Union(name) => write!(f, "{}", name),
Type::Var(name) => write!(f, "{}", name),
}
}
}
impl std::fmt::Display for StackType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StackType::Empty => write!(f, "()"),
StackType::RowVar(name) => write!(f, "..{}", name),
StackType::Cons { rest, top } => {
let mut types = vec![format!("{}", top)];
let mut current = rest.as_ref();
loop {
match current {
StackType::Empty => break,
StackType::RowVar(name) => {
types.push(format!("..{}", name));
break;
}
StackType::Cons { rest, top } => {
types.push(format!("{}", top));
current = rest;
}
}
}
types.reverse();
write!(f, "({})", types.join(" "))
}
}
}
}
impl std::fmt::Display for Effect {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.effects.is_empty() {
write!(f, "{} -- {}", self.inputs, self.outputs)
} else {
let effects_str: Vec<_> = self.effects.iter().map(|e| format!("{}", e)).collect();
write!(
f,
"{} -- {} | {}",
self.inputs,
self.outputs,
effects_str.join(" ")
)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_stack() {
let stack = StackType::empty();
assert_eq!(stack, StackType::Empty);
}
#[test]
fn test_singleton_stack() {
let stack = StackType::singleton(Type::Int);
assert_eq!(
stack,
StackType::Cons {
rest: Box::new(StackType::Empty),
top: Type::Int
}
);
}
#[test]
fn test_push_pop() {
let stack = StackType::empty().push(Type::Int).push(Type::Bool);
let (rest, top) = stack.pop().unwrap();
assert_eq!(top, Type::Bool);
let (rest2, top2) = rest.pop().unwrap();
assert_eq!(top2, Type::Int);
assert_eq!(rest2, StackType::Empty);
}
#[test]
fn test_from_vec() {
let stack = StackType::from_vec(vec![Type::Int, Type::Bool, Type::String]);
let (rest, top) = stack.pop().unwrap();
assert_eq!(top, Type::String);
let (rest2, top2) = rest.pop().unwrap();
assert_eq!(top2, Type::Bool);
let (rest3, top3) = rest2.pop().unwrap();
assert_eq!(top3, Type::Int);
assert_eq!(rest3, StackType::Empty);
}
#[test]
fn test_row_variable() {
let stack = StackType::Cons {
rest: Box::new(StackType::RowVar("a".to_string())),
top: Type::Int,
};
let (rest, top) = stack.pop().unwrap();
assert_eq!(top, Type::Int);
assert_eq!(rest, StackType::RowVar("a".to_string()));
}
#[test]
fn test_effect() {
let effect = Effect::new(
StackType::singleton(Type::Int),
StackType::singleton(Type::Bool),
);
assert_eq!(effect.inputs, StackType::singleton(Type::Int));
assert_eq!(effect.outputs, StackType::singleton(Type::Bool));
}
#[test]
fn test_polymorphic_effect() {
let inputs = StackType::Cons {
rest: Box::new(StackType::RowVar("a".to_string())),
top: Type::Int,
};
let outputs = StackType::Cons {
rest: Box::new(StackType::RowVar("a".to_string())),
top: Type::Bool,
};
let effect = Effect::new(inputs, outputs);
assert!(matches!(effect.inputs, StackType::Cons { .. }));
assert!(matches!(effect.outputs, StackType::Cons { .. }));
}
}