summaryrefslogtreecommitdiff
path: root/src/chomp/check/infer.rs
blob: 941ddba9e62b13f202b14f505ef9275cc6cb0bca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
use super::super::{
    ast::{Alt, Call, Cat, Epsilon, Fix, Literal, Parameter, Variable},
    context::Context,
    error::{AltError, CatError, FixError, TypeError},
    set::{FirstSet, FlastSet},
    typed::Type,
    visit::{Visitable, Visitor},
};

#[derive(Debug)]
pub struct TypeInfer<'a> {
    pub context: &'a mut Context,
}

impl Visitor for TypeInfer<'_> {
    type Out = Result<Type, TypeError>;

    fn visit_epsilon(&mut self, _eps: &Epsilon) -> Self::Out {
        Ok(Type::new(true, FirstSet::default(), FlastSet::default()))
    }

    fn visit_literal(&mut self, lit: &Literal) -> Self::Out {
        Ok(Type::of_str(&lit.value()))
    }

    fn visit_cat(&mut self, cat: &Cat) -> Self::Out {
        let first = cat.first().visit(self)?;
        let second = self
            .context
            .with_unguard(|context| cat.second().visit(&mut TypeInfer { context }))?;

        if first.nullable() {
            Err(TypeError::Cat(CatError::FirstNullable(cat.clone())))
        } else if !first
            .flast_set()
            .intersect_first(second.first_set())
            .is_empty()
        {
            Err(TypeError::Cat(CatError::FirstFlastOverlap(cat.clone())))
        } else {
            Ok(first.cat(second))
        }
    }

    fn visit_alt(&mut self, alt: &Alt) -> Self::Out {
        let left = alt.left().visit(self)?;
        let right = alt.right().visit(self)?;

        if left.nullable() && right.nullable() {
            Err(TypeError::Alt(AltError::BothNullable(alt.clone())))
        } else if !left.first_set().intersect(right.first_set()).is_empty() {
            Err(TypeError::Alt(AltError::FirstOverlap(alt.clone())))
        } else {
            Ok(left.alt(right))
        }
    }

    fn visit_fix(&mut self, fix: &Fix) -> Self::Out {
        let mut res = Type::default();
        let mut last = None;

        while last.map(|r| r != res).unwrap_or(true) {
            last = Some(res);
            res = self
                .context
                .with_variable_type(last.as_ref().cloned().unwrap(), |context| {
                    fix.inner().visit(&mut TypeInfer { context })
                })
                .map_err(|e| {
                    TypeError::Fix(FixError(
                        fix.clone(),
                        last.as_ref().cloned().unwrap(),
                        Box::new(e),
                    ))
                })?;
        }

        Ok(res)
    }

    fn visit_variable(&mut self, var: &Variable) -> Self::Out {
        Ok(self.context.get_variable_type(&var)?.clone())
    }

    fn visit_parameter(&mut self, _param: &Parameter) -> Self::Out {
        todo!()
    }

    fn visit_call(&mut self, _call: &Call) -> Self::Out {
        todo!()
    }
}