1use crate::{GlobalType, Label, LocalTypeR, ValType};
29use serde::{Deserialize, Serialize};
30
31#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
52pub enum GlobalTypeDB {
53 End,
55 Comm {
57 sender: String,
58 receiver: String,
59 branches: Vec<(Label, GlobalTypeDB)>,
60 },
61 Rec(Box<GlobalTypeDB>),
63 Var(usize),
65}
66
67impl GlobalTypeDB {
68 #[must_use]
72 pub fn from_global_type(g: &GlobalType) -> Self {
73 Self::from_global_type_with_env(g, &[])
74 }
75
76 pub(crate) fn from_global_type_with_env(g: &GlobalType, env: &[&str]) -> Self {
77 match g {
78 GlobalType::End => GlobalTypeDB::End,
79 GlobalType::Comm {
80 sender,
81 receiver,
82 branches,
83 } => GlobalTypeDB::Comm {
84 sender: sender.clone(),
85 receiver: receiver.clone(),
86 branches: branches
87 .iter()
88 .map(|(l, cont)| (l.clone(), Self::from_global_type_with_env(cont, env)))
89 .collect(),
90 },
91 GlobalType::Mu { var, body } => {
92 let mut new_env = vec![var.as_str()];
94 new_env.extend(env);
95 GlobalTypeDB::Rec(Box::new(Self::from_global_type_with_env(body, &new_env)))
96 }
97 GlobalType::Var(name) => {
98 let index = env.iter().position(|&v| v == name).unwrap_or(env.len()); GlobalTypeDB::Var(index)
101 }
102 }
103 }
104
105 #[must_use]
109 pub fn normalize(&self) -> Self {
110 match self {
111 GlobalTypeDB::End => GlobalTypeDB::End,
112 GlobalTypeDB::Comm {
113 sender,
114 receiver,
115 branches,
116 } => {
117 let mut sorted_branches: Vec<_> = branches
118 .iter()
119 .map(|(l, cont)| (l.clone(), cont.normalize()))
120 .collect();
121 sorted_branches.sort_by(|a, b| a.0.name.cmp(&b.0.name));
122 GlobalTypeDB::Comm {
123 sender: sender.clone(),
124 receiver: receiver.clone(),
125 branches: sorted_branches,
126 }
127 }
128 GlobalTypeDB::Rec(body) => GlobalTypeDB::Rec(Box::new(body.normalize())),
129 GlobalTypeDB::Var(idx) => GlobalTypeDB::Var(*idx),
130 }
131 }
132}
133
134impl From<&GlobalType> for GlobalTypeDB {
135 fn from(g: &GlobalType) -> Self {
136 GlobalTypeDB::from_global_type(g)
137 }
138}
139
140#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
164pub enum LocalTypeRDB {
165 End,
167 Send {
169 partner: String,
170 branches: Vec<(Label, Option<ValType>, LocalTypeRDB)>,
171 },
172 Recv {
174 partner: String,
175 branches: Vec<(Label, Option<ValType>, LocalTypeRDB)>,
176 },
177 Rec(Box<LocalTypeRDB>),
179 Var(usize),
181}
182
183impl LocalTypeRDB {
184 #[must_use]
188 pub fn from_local_type(t: &LocalTypeR) -> Self {
189 Self::from_local_type_with_env(t, &[])
190 }
191
192 pub(crate) fn from_local_type_with_env(t: &LocalTypeR, env: &[&str]) -> Self {
193 match t {
194 LocalTypeR::End => LocalTypeRDB::End,
195 LocalTypeR::Send { partner, branches } => LocalTypeRDB::Send {
196 partner: partner.clone(),
197 branches: branches
198 .iter()
199 .map(|(l, vt, cont)| {
200 (
201 l.clone(),
202 vt.clone(),
203 Self::from_local_type_with_env(cont, env),
204 )
205 })
206 .collect(),
207 },
208 LocalTypeR::Recv { partner, branches } => LocalTypeRDB::Recv {
209 partner: partner.clone(),
210 branches: branches
211 .iter()
212 .map(|(l, vt, cont)| {
213 (
214 l.clone(),
215 vt.clone(),
216 Self::from_local_type_with_env(cont, env),
217 )
218 })
219 .collect(),
220 },
221 LocalTypeR::Mu { var, body } => {
222 let mut new_env = vec![var.as_str()];
224 new_env.extend(env);
225 LocalTypeRDB::Rec(Box::new(Self::from_local_type_with_env(body, &new_env)))
226 }
227 LocalTypeR::Var(name) => {
228 let index = env.iter().position(|&v| v == name).unwrap_or(env.len()); LocalTypeRDB::Var(index)
231 }
232 }
233 }
234
235 #[must_use]
239 pub fn normalize(&self) -> Self {
240 match self {
241 LocalTypeRDB::End => LocalTypeRDB::End,
242 LocalTypeRDB::Send { partner, branches } => {
243 let mut sorted_branches: Vec<_> = branches
244 .iter()
245 .map(|(l, vt, cont)| (l.clone(), vt.clone(), cont.normalize()))
246 .collect();
247 sorted_branches.sort_by(|a, b| a.0.name.cmp(&b.0.name));
248 LocalTypeRDB::Send {
249 partner: partner.clone(),
250 branches: sorted_branches,
251 }
252 }
253 LocalTypeRDB::Recv { partner, branches } => {
254 let mut sorted_branches: Vec<_> = branches
255 .iter()
256 .map(|(l, vt, cont)| (l.clone(), vt.clone(), cont.normalize()))
257 .collect();
258 sorted_branches.sort_by(|a, b| a.0.name.cmp(&b.0.name));
259 LocalTypeRDB::Recv {
260 partner: partner.clone(),
261 branches: sorted_branches,
262 }
263 }
264 LocalTypeRDB::Rec(body) => LocalTypeRDB::Rec(Box::new(body.normalize())),
265 LocalTypeRDB::Var(idx) => LocalTypeRDB::Var(*idx),
266 }
267 }
268}
269
270impl From<&LocalTypeR> for LocalTypeRDB {
271 fn from(t: &LocalTypeR) -> Self {
272 LocalTypeRDB::from_local_type(t)
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279 use assert_matches::assert_matches;
280
281 #[test]
282 fn test_alpha_equivalent_global_types() {
283 let g1 = GlobalType::mu(
285 "x",
286 GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("x")),
287 );
288 let g2 = GlobalType::mu(
290 "y",
291 GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("y")),
292 );
293
294 let db1 = GlobalTypeDB::from(&g1);
295 let db2 = GlobalTypeDB::from(&g2);
296
297 assert_eq!(db1, db2);
298 }
299
300 #[test]
301 fn test_alpha_equivalent_local_types() {
302 let t1 = LocalTypeR::mu(
304 "x",
305 LocalTypeR::send("B", Label::new("msg"), LocalTypeR::var("x")),
306 );
307 let t2 = LocalTypeR::mu(
309 "y",
310 LocalTypeR::send("B", Label::new("msg"), LocalTypeR::var("y")),
311 );
312
313 let db1 = LocalTypeRDB::from(&t1);
314 let db2 = LocalTypeRDB::from(&t2);
315
316 assert_eq!(db1, db2);
317 }
318
319 #[test]
320 fn test_nested_recursion() {
321 let g1 = GlobalType::mu(
323 "x",
324 GlobalType::mu(
325 "y",
326 GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("y")),
327 ),
328 );
329 let g2 = GlobalType::mu(
331 "a",
332 GlobalType::mu(
333 "b",
334 GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("b")),
335 ),
336 );
337
338 let db1 = GlobalTypeDB::from(&g1);
339 let db2 = GlobalTypeDB::from(&g2);
340
341 assert_eq!(db1, db2);
342
343 assert_matches!(db1, GlobalTypeDB::Rec(outer) => {
345 assert_matches!(*outer, GlobalTypeDB::Rec(inner) => {
346 assert_matches!(*inner, GlobalTypeDB::Comm { ref branches, .. } => {
347 assert_matches!(branches[0].1, GlobalTypeDB::Var(0));
348 });
349 });
350 });
351 }
352
353 #[test]
354 fn test_reference_to_outer_binder() {
355 let g = GlobalType::mu(
357 "x",
358 GlobalType::mu(
359 "y",
360 GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("x")),
361 ),
362 );
363
364 let db = GlobalTypeDB::from(&g);
365
366 assert_matches!(db, GlobalTypeDB::Rec(outer) => {
368 assert_matches!(*outer, GlobalTypeDB::Rec(inner) => {
369 assert_matches!(*inner, GlobalTypeDB::Comm { ref branches, .. } => {
370 assert_matches!(branches[0].1, GlobalTypeDB::Var(1));
371 });
372 });
373 });
374 }
375
376 #[test]
377 fn test_different_structures_not_equal() {
378 let g1 = GlobalType::mu(
380 "x",
381 GlobalType::send("A", "B", Label::new("msg"), GlobalType::End),
382 );
383 let g2 = GlobalType::mu(
385 "x",
386 GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("x")),
387 );
388
389 let db1 = GlobalTypeDB::from(&g1);
390 let db2 = GlobalTypeDB::from(&g2);
391
392 assert_ne!(db1, db2);
393 }
394
395 #[test]
396 fn test_normalize_branch_order() {
397 let g = GlobalType::comm(
399 "A",
400 "B",
401 vec![
402 (Label::new("b"), GlobalType::End),
403 (Label::new("a"), GlobalType::End),
404 ],
405 );
406
407 let db = GlobalTypeDB::from(&g).normalize();
408
409 assert_matches!(db, GlobalTypeDB::Comm { branches, .. } => {
410 assert_eq!(branches[0].0.name, "a");
411 assert_eq!(branches[1].0.name, "b");
412 });
413 }
414
415 #[test]
416 fn test_unbound_variable() {
417 let g = GlobalType::send("A", "B", Label::new("msg"), GlobalType::var("t"));
419
420 let db = GlobalTypeDB::from(&g);
421
422 assert_matches!(db, GlobalTypeDB::Comm { ref branches, .. } => {
424 assert_matches!(branches[0].1, GlobalTypeDB::Var(0));
425 });
426 }
427
428 #[test]
429 fn test_local_type_send_recv() {
430 let t = LocalTypeR::mu(
432 "x",
433 LocalTypeR::send(
434 "B",
435 Label::new("msg"),
436 LocalTypeR::recv("A", Label::new("reply"), LocalTypeR::var("x")),
437 ),
438 );
439
440 let db = LocalTypeRDB::from(&t);
441
442 assert_matches!(db, LocalTypeRDB::Rec(body) => {
443 assert_matches!(*body, LocalTypeRDB::Send { ref partner, ref branches } => {
444 assert_eq!(partner, "B");
445 assert_matches!(&branches[0].2, LocalTypeRDB::Recv { partner, branches: recv_branches } => {
446 assert_eq!(partner, "A");
447 assert_matches!(recv_branches[0].2, LocalTypeRDB::Var(0));
448 });
449 });
450 });
451 }
452}