1use std::collections::{HashMap, HashSet};
33use std::env;
34use std::error;
35use std::fmt;
36use std::fs::File;
37use std::io::prelude::*;
38use std::path::{Path, PathBuf};
39
40use crate::cat_lexer;
41use crate::cat_parser;
42
43#[derive(Debug)]
44pub enum Exp<T> {
45 Empty(T),
46 Id(String, T),
47 Let(String, Box<Exp<T>>, Box<Exp<T>>, T),
48 TryWith(Box<Exp<T>>, Box<Exp<T>>, T),
49 Union(Box<Exp<T>>, Box<Exp<T>>, T),
51 Inter(Box<Exp<T>>, Box<Exp<T>>, T),
53 Diff(Box<Exp<T>>, Box<Exp<T>>, T),
55 Seq(Box<Exp<T>>, Box<Exp<T>>),
57 Cartesian(Box<Exp<T>>, Box<Exp<T>>),
59 Compl(Box<Exp<T>>, T),
61 Identity(Box<Exp<T>>),
63 IdentityUnion(Box<Exp<T>>),
65 Inverse(Box<Exp<T>>),
67 App(String, Box<Exp<T>>, T),
69}
70
71#[derive(Debug, Default)]
74pub struct Shadows {
75 map: HashMap<String, usize>,
76}
77
78impl Shadows {
79 pub fn new() -> Self {
80 Default::default()
81 }
82}
83
84impl<T> Exp<T> {
85 fn unshadow(&mut self, shadows: &mut Shadows, locals: &mut HashMap<String, usize>) {
86 use Exp::*;
87
88 match self {
89 Id(id, _) => {
90 if let Some(count) = locals.get(id) {
91 *id = format!("{}/l{}", id, count)
92 } else if let Some(count) = shadows.map.get(id) {
93 *id = format!("{}/{}", id, count)
94 }
95 }
96
97 Let(id, exp1, exp2, _) => {
98 exp1.unshadow(shadows, locals);
99 if let Some(count) = locals.get_mut(id) {
100 *count += 1;
101 *id = format!("{}/l{}", id, count)
102 } else {
103 locals.insert(id.clone(), 0);
104 *id = format!("{}/l0", id)
105 }
106 exp2.unshadow(shadows, locals)
107 }
108
109 Empty(_) => (),
110 Inverse(exp) | IdentityUnion(exp) | Identity(exp) | Compl(exp, _) | App(_, exp, _) => {
111 exp.unshadow(shadows, locals)
112 }
113 TryWith(exp1, exp2, _)
114 | Union(exp1, exp2, _)
115 | Inter(exp1, exp2, _)
116 | Diff(exp1, exp2, _)
117 | Seq(exp1, exp2)
118 | Cartesian(exp1, exp2) => {
119 exp1.unshadow(shadows, locals);
120 exp2.unshadow(shadows, locals)
121 }
122 }
123 }
124}
125
126#[derive(Debug)]
127pub enum Check {
128 Acyclic,
129 Irreflexive,
130 Empty,
131 NonAcyclic,
132 NonIrreflexive,
133 NonEmpty,
134}
135
136#[derive(Debug)]
137pub enum Def<T> {
138 Let(Vec<(String, Exp<T>)>),
139 Fn(String, Vec<(String, T)>, Exp<T>),
140 TClosure(String, Exp<T>),
141 RTClosure(String, Exp<T>),
142 Flag(Check, Exp<T>, String),
143 Check(Check, Exp<T>, Option<String>),
144 Show(Vec<String>),
145 ShowAs(Exp<T>, String),
146 Unshow(Vec<String>),
147 Set(String),
148 Relation(String),
149}
150
151#[derive(Debug)]
152pub enum ParseDef {
153 Def(Def<()>),
154 Include(String),
155}
156
157#[derive(Debug)]
159pub struct ParseCat {
160 pub tag: String,
161 pub defs: Vec<ParseDef>,
162}
163
164impl ParseCat {
165 pub fn from_string(contents: &str) -> Result<Self, String> {
166 let lexer = cat_lexer::Lexer::new(&contents);
167 match cat_parser::CatParser::new().parse(lexer) {
168 Ok(cat) => Ok(cat),
169 Err(e) => Err(format!("Failed to parse cat file: {}", e)),
170 }
171 }
172
173 pub fn from_file<P>(path: P) -> Result<Self, String>
174 where
175 P: AsRef<Path>,
176 {
177 let file_name = path.as_ref().display();
178 let mut contents = String::new();
179
180 match File::open(&path) {
181 Ok(mut handle) => match handle.read_to_string(&mut contents) {
182 Ok(_) => (),
183 Err(e) => return Err(format!("Error when reading cat file '{}': {}", file_name, e)),
184 },
185 Err(e) => return Err(format!("Error when opening cat file '{}': {}", file_name, e)),
186 }
187
188 Self::from_string(&contents)
189 }
190}
191
192#[derive(Debug)]
195pub struct Cat<T> {
196 pub tag: String,
197 pub defs: Vec<Def<T>>,
198}
199
200impl<T> Cat<T> {
201 pub fn unshadow(&mut self, shadows: &mut Shadows) {
203 use Def::*;
204
205 for def in self.defs.iter_mut().rev() {
206 match def {
207 Let(bindings) => {
208 for (id, exp) in bindings.iter_mut() {
209 exp.unshadow(shadows, &mut HashMap::new());
210 if let Some(count) = shadows.map.get_mut(id) {
211 *id = format!("{}/{}", id, count);
212 *count += 1
213 } else {
214 shadows.map.insert(id.clone(), 0);
215 }
216 }
217 }
218
219 TClosure(id, exp) | RTClosure(id, exp) => {
220 exp.unshadow(shadows, &mut HashMap::new());
221 if let Some(count) = shadows.map.get_mut(id) {
222 *id = format!("{}/{}", id, count);
223 *count += 1
224 } else {
225 shadows.map.insert(id.clone(), 0);
226 }
227 }
228
229 Fn(_, _, exp) | Flag(_, exp, _) | Check(_, exp, _) | ShowAs(exp, _) => {
230 exp.unshadow(shadows, &mut HashMap::new())
231 }
232
233 _ => (),
234 }
235 }
236 }
237
238 pub fn shows(&self) -> Vec<String> {
239 let mut shows = Vec::new();
240 for def in self.defs.iter() {
241 match def {
242 Def::Show(ids) => shows.append(&mut ids.clone()),
243 Def::ShowAs(_, id) => shows.push(id.clone()),
244 _ => (),
245 }
246 }
247 shows
248 }
249}
250
251impl Cat<Ty> {
252 pub fn relations<'a>(&'a self) -> Vec<&'a str> {
254 let mut rels: Vec<&'a str> = Vec::new();
255 for def in self.defs.iter() {
256 match def {
257 Def::Let(bindings) => bindings.iter().for_each(|(id, exp)| {
258 if ty_of(exp) == Ty::Rel {
259 rels.push(id)
260 }
261 }),
262 Def::TClosure(id, _) | Def::RTClosure(id, _) => rels.push(id),
263 _ => (),
264 }
265 }
266 rels
267 }
268}
269
270pub fn resolve_includes(cat_dirs: &[PathBuf], mut parse_cat: ParseCat) -> Result<Cat<()>, String> {
274 Ok(Cat {
275 tag: parse_cat.tag,
276 defs: parse_cat
277 .defs
278 .drain(..)
279 .map(|parse_def| match parse_def {
280 ParseDef::Def(def) => Ok(vec![def]),
281 ParseDef::Include(name) => find_cat(cat_dirs, &name).map(|cat| cat.defs),
282 })
283 .collect::<Result<Vec<_>, _>>()?
284 .drain(..)
285 .flatten()
286 .collect(),
287 })
288}
289
290static COS_CAT: &str = include_str!("../catlib/cos.cat");
291static STDLIB_CAT: &str = include_str!("../catlib/stdlib.cat");
292
293fn find_cat(cat_dirs: &[PathBuf], name: &str) -> Result<Cat<()>, String> {
294 if name == "cos.cat" {
295 let parse_cat = ParseCat::from_string(COS_CAT)?;
296 return resolve_includes(cat_dirs, parse_cat);
297 }
298
299 if name == "stdlib.cat" {
300 let parse_cat = ParseCat::from_string(STDLIB_CAT)?;
301 return resolve_includes(cat_dirs, parse_cat);
302 }
303
304 for dir in cat_dirs {
305 let cat_file = dir.join(name);
306 if cat_file.is_file() {
307 let parse_cat = ParseCat::from_file(cat_file)?;
308 return resolve_includes(cat_dirs, parse_cat);
309 }
310 }
311
312 Err(format!("Could not find cat: {}", name))
313}
314
315pub fn load_cat(name: &str) -> Result<Cat<()>, String> {
321 let path = Path::new(name);
322
323 let mut cat_dirs: Vec<PathBuf> = Vec::new();
324
325 if let Ok(directory) = env::var("HERDLIB") {
326 cat_dirs.push(directory.into())
327 }
328
329 let mut directory = path.to_path_buf();
330 directory.pop();
331 if directory.is_dir() {
332 cat_dirs.push(directory)
333 }
334
335 if path.is_file() {
336 let parse_cat = ParseCat::from_file(path)?;
337 resolve_includes(&cat_dirs, parse_cat)
338 } else {
339 find_cat(&cat_dirs, name)
340 }
341}
342
343#[derive(Copy, Clone, Debug, PartialEq, Eq)]
344pub enum Ty {
345 Rel,
346 Set,
347}
348
349pub struct Tcx {
351 bindings: HashMap<String, Vec<Ty>>,
352 functions: HashMap<String, (Ty, Ty)>,
353 unknowns: HashSet<String>,
354 found: HashMap<String, Ty>,
355}
356
357impl Tcx {
358 fn push<S: Into<String>>(&mut self, name: S, ty: Ty) {
359 let name = name.into();
360 match self.bindings.get_mut(&name) {
361 None => {
362 self.bindings.insert(name, vec![ty]);
363 }
364 Some(tys) => tys.push(ty),
365 }
366 }
367
368 fn pop(&mut self, name: &str) {
369 match self.bindings.get_mut(name) {
370 None => (),
371 Some(tys) => {
372 tys.pop();
373 }
374 }
375 }
376
377 fn peek(&self, name: &str) -> Option<Ty> {
378 match self.bindings.get(name) {
379 None => None,
380 Some(tys) => tys.last().copied(),
381 }
382 }
383}
384
385pub fn initial_tcx<I>(sets: I) -> Tcx
389where
390 I: Iterator<Item = String>,
391{
392 let mut bindings = HashMap::new();
393 let mut functions = HashMap::new();
394
395 for set in sets {
397 bindings.insert(set, vec![Ty::Set]);
398 }
399
400 functions.insert("domain".to_string(), (Ty::Rel, Ty::Set));
401 functions.insert("range".to_string(), (Ty::Rel, Ty::Set));
402 functions.insert("fencerel".to_string(), (Ty::Set, Ty::Rel));
403 functions.insert("ctrlcfence".to_string(), (Ty::Set, Ty::Rel));
404
405 Tcx { bindings, functions, unknowns: HashSet::new(), found: HashMap::new() }
406}
407
408#[derive(Debug)]
410pub struct TyError {
411 message: String,
412}
413
414impl fmt::Display for TyError {
415 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
416 write!(f, "{}", self.message)
417 }
418}
419
420impl error::Error for TyError {
421 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
422 None
423 }
424}
425
426fn ty_error<S: Into<String>, A>(message: S) -> Result<A, TyError> {
427 Err(TyError { message: message.into() })
428}
429
430pub fn ty_of(exp: &Exp<Ty>) -> Ty {
432 use Exp::*;
433 match exp {
434 Empty(ty) => *ty,
435 Id(_, ty) => *ty,
436 Let(_, _, _, ty) => *ty,
437 TryWith(_, _, ty) => *ty,
438 Union(_, _, ty) => *ty,
439 Inter(_, _, ty) => *ty,
440 Diff(_, _, ty) => *ty,
441 Seq(_, _) => Ty::Rel,
442 Cartesian(_, _) => Ty::Rel,
443 Compl(_, ty) => *ty,
444 Identity(_) => Ty::Rel,
445 IdentityUnion(_) => Ty::Rel,
446 Inverse(_) => Ty::Rel,
447 App(_, _, ty) => *ty,
448 }
449}
450
451fn check_exp(tcx: &mut Tcx, exp: &Exp<()>, ty: Ty) -> Result<Exp<Ty>, TyError> {
452 use Exp::*;
453 match exp {
454 Empty(()) => Ok(Empty(ty)),
455
456 Id(id, ()) if tcx.unknowns.contains(id) => {
457 match tcx.found.insert(id.clone(), ty) {
458 Some(prev_ty) if ty != prev_ty => return ty_error(format!("Inconsistent type for {}", id)),
459 _ => (),
460 }
461 Ok(Id(id.clone(), ty))
462 }
463
464 _ => {
465 let exp = infer_exp(tcx, exp)?;
466 if ty == ty_of(&exp) {
467 Ok(exp)
468 } else {
469 ty_error("Types do not match")
470 }
471 }
472 }
473}
474
475macro_rules! unary_infer {
476 ($tcx: ident, $ctor: path, $x: ident) => {{
477 let x = infer_exp($tcx, $x)?;
478 let ty = ty_of(&x);
479 Ok($ctor(Box::new(x), ty))
480 }};
481}
482
483macro_rules! binary_infer {
484 ($tcx: ident, $ctor: path, $x: ident, $y: ident) => {{
485 match infer_exp($tcx, $x) {
486 Ok(x) => {
487 let y = check_exp($tcx, $y, ty_of(&x))?;
488 if ty_of(&x) == ty_of(&y) {
489 let ty = ty_of(&x);
490 Ok($ctor(Box::new(x), Box::new(y), ty))
491 } else {
492 ty_error("Types do not match")
493 }
494 }
495 Err(_) => {
496 let y = infer_exp($tcx, $y)?;
497 let x = check_exp($tcx, $x, ty_of(&y))?;
498 if ty_of(&x) == ty_of(&y) {
499 let ty = ty_of(&x);
500 Ok($ctor(Box::new(x), Box::new(y), ty))
501 } else {
502 ty_error("Types do not match")
503 }
504 }
505 }
506 }};
507}
508
509fn infer_exp(tcx: &mut Tcx, exp: &Exp<()>) -> Result<Exp<Ty>, TyError> {
510 use Exp::*;
511 match exp {
512 Empty(()) => ty_error("Cannot infer the type of an empty relation or set"),
513
514 Id(id, ()) => match tcx.peek(id) {
515 Some(ty) => Ok(Id(id.clone(), ty)),
516 None => ty_error(format!("Identifier {} not defined", id)),
517 },
518
519 Let(v, x, y, ()) => {
520 let x = infer_exp(tcx, x)?;
521 tcx.push(v, ty_of(&x));
522 let y = infer_exp(tcx, y)?;
523 tcx.pop(v);
524 let ty = ty_of(&y);
525 Ok(Let(v.clone(), Box::new(x), Box::new(y), ty))
526 }
527
528 TryWith(x, y, ()) => match infer_exp(tcx, x) {
529 Ok(x) => {
530 let y = check_exp(tcx, y, ty_of(&x))?;
531 if ty_of(&x) == ty_of(&y) {
532 let ty = ty_of(&x);
533 Ok(TryWith(Box::new(x), Box::new(y), ty))
534 } else {
535 ty_error("Types do not mach in try with")
536 }
537 }
538 Err(_) => {
539 let y = infer_exp(tcx, y)?;
540 match check_exp(tcx, x, ty_of(&y)) {
541 Ok(x) => {
542 if ty_of(&x) == ty_of(&y) {
543 let ty = ty_of(&x);
544 Ok(TryWith(Box::new(x), Box::new(y), ty))
545 } else {
546 ty_error("Types do not match in try with")
547 }
548 }
549 Err(_) => Ok(y),
550 }
551 }
552 },
553
554 Union(x, y, ()) => binary_infer!(tcx, Union, x, y),
555 Inter(x, y, ()) => binary_infer!(tcx, Inter, x, y),
556 Diff(x, y, ()) => binary_infer!(tcx, Diff, x, y),
557
558 Seq(x, y) => {
559 let x = check_exp(tcx, x, Ty::Rel)?;
560 let y = check_exp(tcx, y, Ty::Rel)?;
561 Ok(Seq(Box::new(x), Box::new(y)))
562 }
563
564 Cartesian(x, y) => {
565 let x = check_exp(tcx, x, Ty::Set)?;
566 let y = check_exp(tcx, y, Ty::Set)?;
567 Ok(Cartesian(Box::new(x), Box::new(y)))
568 }
569
570 Compl(x, ()) => unary_infer!(tcx, Compl, x),
571
572 Identity(x) => {
573 let x = check_exp(tcx, x, Ty::Set)?;
574 Ok(Identity(Box::new(x)))
575 }
576
577 IdentityUnion(x) => {
578 let x = check_exp(tcx, x, Ty::Rel)?;
579 Ok(IdentityUnion(Box::new(x)))
580 }
581
582 Inverse(x) => {
583 let x = check_exp(tcx, x, Ty::Rel)?;
584 Ok(Inverse(Box::new(x)))
585 }
586
587 App(f, x, ()) => {
588 let (from_ty, to_ty) = match tcx.functions.get(f) {
589 Some(f_ty) => *f_ty,
590 None => return ty_error(format!("Function {} not defined", f)),
591 };
592 let x = check_exp(tcx, x, from_ty)?;
593 Ok(App(f.clone(), Box::new(x), to_ty))
594 }
595 }
596}
597
598fn infer_def(tcx: &mut Tcx, def: Def<()>) -> Result<Def<Ty>, TyError> {
599 use Def::*;
600 Ok(match def {
601 Set(name) => {
602 tcx.push(name.clone(), Ty::Set);
603 Set(name)
604 }
605
606 Relation(name) => {
607 tcx.push(name.clone(), Ty::Rel);
608 Relation(name)
609 }
610
611 Let(mut bindings) => {
612 let bindings: Vec<(String, Exp<Ty>)> = bindings
613 .drain(..)
614 .map(|(name, exp)| match infer_exp(tcx, &exp) {
615 Ok(exp) => Ok((name, exp)),
616 Err(e) => Err(e),
617 })
618 .collect::<Result<_, _>>()?;
619
620 tcx.unknowns.clear();
621 tcx.found.clear();
622
623 for (name, exp) in &bindings {
624 tcx.push(name, ty_of(exp));
625 }
626
627 Let(bindings)
628 }
629
630 Fn(name, mut params, body) => {
631 for (param, _) in ¶ms {
632 tcx.unknowns.insert(param.clone());
633 }
634
635 let body = infer_exp(tcx, &body)?;
636
637 let params: Vec<(String, Ty)> = params
638 .drain(..)
639 .map(|(param, _)| {
640 if let Some(ty) = tcx.found.get(¶m) {
641 Ok((param, *ty))
642 } else {
643 ty_error(format!("Could not infer type of function parameter {}", param))
644 }
645 })
646 .collect::<Result<_, _>>()?;
647
648 tcx.unknowns.clear();
649 tcx.found.clear();
650
651 Fn(name, params, body)
652 }
653
654 TClosure(id, exp) => {
655 let def = TClosure(id.clone(), check_exp(tcx, &exp, Ty::Rel)?);
656 tcx.push(id, Ty::Rel);
657 def
658 }
659
660 RTClosure(id, exp) => {
661 let def = RTClosure(id.clone(), check_exp(tcx, &exp, Ty::Rel)?);
662 tcx.push(id, Ty::Rel);
663 def
664 }
665
666 Flag(check, exp, id) => Flag(check, infer_exp(tcx, &exp)?, id),
667
668 Check(check, exp, opt_id) => Check(check, infer_exp(tcx, &exp)?, opt_id),
669
670 Show(ids) => Show(ids),
671
672 ShowAs(exp, id) => ShowAs(check_exp(tcx, &exp, Ty::Rel)?, id),
673
674 Unshow(ids) => Unshow(ids),
675 })
676}
677
678pub fn infer_cat(tcx: &mut Tcx, mut cat: Cat<()>) -> Result<Cat<Ty>, TyError> {
680 Ok(Cat { tag: cat.tag, defs: cat.defs.drain(..).map(|def| infer_def(tcx, def)).collect::<Result<_, _>>()? })
681}
682
683#[cfg(test)]
684mod tests {
685 use super::*;
686
687 #[test]
688 fn test_local_shadowing() {
689 use Exp::*;
690 let x = "x".to_string();
691 let mut exp = Let(
692 x.clone(),
693 Box::new(Id(x.clone(), ())),
694 Box::new(Let(x.clone(), Box::new(Id(x.clone(), ())), Box::new(Id(x.clone(), ())), ())),
695 (),
696 );
697 exp.unshadow(&mut Shadows::new(), &mut HashMap::new());
698 assert_eq!(
699 "Let(\"x/l0\", Id(\"x\", ()), Let(\"x/l1\", Id(\"x/l0\", ()), Id(\"x/l1\", ()), ()), ())",
700 &format!("{:?}", exp)
701 )
702 }
703}