use crate::{GlobalType, Label, LocalTypeR, ValType};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum GlobalTypeDB {
End,
Comm {
sender: String,
receiver: String,
branches: Vec<(Label, GlobalTypeDB)>,
},
Rec(Box<GlobalTypeDB>),
Var(usize),
}
impl GlobalTypeDB {
#[must_use]
pub fn from_global_type(g: &GlobalType) -> Self {
Self::from_global_type_with_env(g, &[])
}
pub(crate) fn from_global_type_with_env(g: &GlobalType, env: &[&str]) -> Self {
match g {
GlobalType::End => GlobalTypeDB::End,
GlobalType::Comm {
sender,
receiver,
branches,
} => GlobalTypeDB::Comm {
sender: sender.clone(),
receiver: receiver.clone(),
branches: branches
.iter()
.map(|(l, cont)| (l.clone(), Self::from_global_type_with_env(cont, env)))
.collect(),
},
GlobalType::Mu { var, body } => {
let mut new_env = vec![var.as_str()];
new_env.extend(env);
GlobalTypeDB::Rec(Box::new(Self::from_global_type_with_env(body, &new_env)))
}
GlobalType::Var(name) => {
let index = env.iter().position(|&v| v == name).unwrap_or(env.len()); GlobalTypeDB::Var(index)
}
}
}
#[must_use]
pub fn normalize(&self) -> Self {
match self {
GlobalTypeDB::End => GlobalTypeDB::End,
GlobalTypeDB::Comm {
sender,
receiver,
branches,
} => {
let mut sorted_branches: Vec<_> = branches
.iter()
.map(|(l, cont)| (l.clone(), cont.normalize()))
.collect();
sorted_branches.sort_by(|a, b| a.0.name.cmp(&b.0.name));
GlobalTypeDB::Comm {
sender: sender.clone(),
receiver: receiver.clone(),
branches: sorted_branches,
}
}
GlobalTypeDB::Rec(body) => GlobalTypeDB::Rec(Box::new(body.normalize())),
GlobalTypeDB::Var(idx) => GlobalTypeDB::Var(*idx),
}
}
}
impl From<&GlobalType> for GlobalTypeDB {
fn from(g: &GlobalType) -> Self {
GlobalTypeDB::from_global_type(g)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum LocalTypeRDB {
End,
Send {
partner: String,
branches: Vec<(Label, Option<ValType>, LocalTypeRDB)>,
},
Recv {
partner: String,
branches: Vec<(Label, Option<ValType>, LocalTypeRDB)>,
},
Rec(Box<LocalTypeRDB>),
Var(usize),
}
impl LocalTypeRDB {
#[must_use]
pub fn from_local_type(t: &LocalTypeR) -> Self {
Self::from_local_type_with_env(t, &[])
}
pub(crate) fn from_local_type_with_env(t: &LocalTypeR, env: &[&str]) -> Self {
match t {
LocalTypeR::End => LocalTypeRDB::End,
LocalTypeR::Send { partner, branches } => LocalTypeRDB::Send {
partner: partner.clone(),
branches: branches
.iter()
.map(|(l, vt, cont)| {
(
l.clone(),
vt.clone(),
Self::from_local_type_with_env(cont, env),
)
})
.collect(),
},
LocalTypeR::Recv { partner, branches } => LocalTypeRDB::Recv {
partner: partner.clone(),
branches: branches
.iter()
.map(|(l, vt, cont)| {
(
l.clone(),
vt.clone(),
Self::from_local_type_with_env(cont, env),
)
})
.collect(),
},
LocalTypeR::Mu { var, body } => {
let mut new_env = vec![var.as_str()];
new_env.extend(env);
LocalTypeRDB::Rec(Box::new(Self::from_local_type_with_env(body, &new_env)))
}
LocalTypeR::Var(name) => {
let index = env.iter().position(|&v| v == name).unwrap_or(env.len()); LocalTypeRDB::Var(index)
}
}
}
#[must_use]
pub fn normalize(&self) -> Self {
match self {
LocalTypeRDB::End => LocalTypeRDB::End,
LocalTypeRDB::Send { partner, branches } => {
let mut sorted_branches: Vec<_> = branches
.iter()
.map(|(l, vt, cont)| (l.clone(), vt.clone(), cont.normalize()))
.collect();
sorted_branches.sort_by(|a, b| a.0.name.cmp(&b.0.name));
LocalTypeRDB::Send {
partner: partner.clone(),
branches: sorted_branches,
}
}
LocalTypeRDB::Recv { partner, branches } => {
let mut sorted_branches: Vec<_> = branches
.iter()
.map(|(l, vt, cont)| (l.clone(), vt.clone(), cont.normalize()))
.collect();
sorted_branches.sort_by(|a, b| a.0.name.cmp(&b.0.name));
LocalTypeRDB::Recv {
partner: partner.clone(),
branches: sorted_branches,
}
}
LocalTypeRDB::Rec(body) => LocalTypeRDB::Rec(Box::new(body.normalize())),
LocalTypeRDB::Var(idx) => LocalTypeRDB::Var(*idx),
}
}
}
impl From<&LocalTypeR> for LocalTypeRDB {
fn from(t: &LocalTypeR) -> Self {
LocalTypeRDB::from_local_type(t)
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
#[test]
fn test_alpha_equivalent_global_types() {
let g1 = GlobalType::mu(
"x",
GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("x")),
);
let g2 = GlobalType::mu(
"y",
GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("y")),
);
let db1 = GlobalTypeDB::from(&g1);
let db2 = GlobalTypeDB::from(&g2);
assert_eq!(db1, db2);
}
#[test]
fn test_alpha_equivalent_local_types() {
let t1 = LocalTypeR::mu(
"x",
LocalTypeR::send("B", Label::new("msg"), LocalTypeR::var("x")),
);
let t2 = LocalTypeR::mu(
"y",
LocalTypeR::send("B", Label::new("msg"), LocalTypeR::var("y")),
);
let db1 = LocalTypeRDB::from(&t1);
let db2 = LocalTypeRDB::from(&t2);
assert_eq!(db1, db2);
}
#[test]
fn test_nested_recursion() {
let g1 = GlobalType::mu(
"x",
GlobalType::mu(
"y",
GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("y")),
),
);
let g2 = GlobalType::mu(
"a",
GlobalType::mu(
"b",
GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("b")),
),
);
let db1 = GlobalTypeDB::from(&g1);
let db2 = GlobalTypeDB::from(&g2);
assert_eq!(db1, db2);
assert_matches!(db1, GlobalTypeDB::Rec(outer) => {
assert_matches!(*outer, GlobalTypeDB::Rec(inner) => {
assert_matches!(*inner, GlobalTypeDB::Comm { ref branches, .. } => {
assert_matches!(branches[0].1, GlobalTypeDB::Var(0));
});
});
});
}
#[test]
fn test_reference_to_outer_binder() {
let g = GlobalType::mu(
"x",
GlobalType::mu(
"y",
GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("x")),
),
);
let db = GlobalTypeDB::from(&g);
assert_matches!(db, GlobalTypeDB::Rec(outer) => {
assert_matches!(*outer, GlobalTypeDB::Rec(inner) => {
assert_matches!(*inner, GlobalTypeDB::Comm { ref branches, .. } => {
assert_matches!(branches[0].1, GlobalTypeDB::Var(1));
});
});
});
}
#[test]
fn test_different_structures_not_equal() {
let g1 = GlobalType::mu(
"x",
GlobalType::send("A", "B", Label::new("msg"), GlobalType::End),
);
let g2 = GlobalType::mu(
"x",
GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("x")),
);
let db1 = GlobalTypeDB::from(&g1);
let db2 = GlobalTypeDB::from(&g2);
assert_ne!(db1, db2);
}
#[test]
fn test_normalize_branch_order() {
let g = GlobalType::comm(
"A",
"B",
vec![
(Label::new("b"), GlobalType::End),
(Label::new("a"), GlobalType::End),
],
);
let db = GlobalTypeDB::from(&g).normalize();
assert_matches!(db, GlobalTypeDB::Comm { branches, .. } => {
assert_eq!(branches[0].0.name, "a");
assert_eq!(branches[1].0.name, "b");
});
}
#[test]
fn test_unbound_variable() {
let g = GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("t"));
let db = GlobalTypeDB::from(&g);
assert_matches!(db, GlobalTypeDB::Comm { ref branches, .. } => {
assert_matches!(branches[0].1, GlobalTypeDB::Var(0));
});
}
#[test]
fn test_local_type_send_recv() {
let t = LocalTypeR::mu(
"x",
LocalTypeR::send(
"B",
Label::new("msg"),
LocalTypeR::recv("A", Label::new("reply"), LocalTypeR::var("x")),
),
);
let db = LocalTypeRDB::from(&t);
assert_matches!(db, LocalTypeRDB::Rec(body) => {
assert_matches!(*body, LocalTypeRDB::Send { ref partner, ref branches } => {
assert_eq!(partner, "B");
assert_matches!(&branches[0].2, LocalTypeRDB::Recv { partner, branches: recv_branches } => {
assert_eq!(partner, "A");
assert_matches!(recv_branches[0].2, LocalTypeRDB::Var(0));
});
});
});
}
}