summaryrefslogtreecommitdiff
path: root/src/chomp/mod.rs
blob: c8c82aee5f4da5c894c88dfebbcc97b1b9b892ed (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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use std::{collections::HashMap, rc::Rc};

use intern::Intern;

use self::ast::Lambda;

mod ast;
mod cst;
mod intern;
mod typed;

type SubstTriple = (Intern<ast::Tree>, Intern<ast::Tree>, Intern<ast::Tree>);

#[derive(Debug)]
pub struct Context {
    // Abstract tree interning
    var_stack: Vec<Rc<ast::Tree>>,
    pub tree_interner: intern::TreeInterner,
    pub type_interner: intern::TypeInterner,

    // Caches
    as_tree: HashMap<Intern<cst::Shape>, Rc<ast::Tree>>,
    substitute: HashMap<SubstTriple, Option<Rc<ast::Tree>>>,
    translate: HashMap<Intern<ast::Tree>, Result<Option<Rc<ast::Tree>>, ast::TranslateError>>,
    type_of: HashMap<
        Intern<ast::Tree>,
        Result<typed::ConstrainedType, typed::TypeError>,
    >,
}

impl Context {
    /// Converts a concrete syntax tree into an abstract one.
    pub fn as_tree(&mut self, shape: &Rc<cst::Shape>) -> Rc<ast::Tree> {
        if let Some(tree) = self.as_tree.get(&shape.clone().into()) {
            return tree.clone();
        }
        let tree = cst::as_tree(self, shape);
        self.as_tree.insert(shape.clone().into(), tree.clone());
        tree
    }

    /// Creates a copy of `tree` that replaces variables pointing to `from` with `into`.
    pub fn substitute(
        &mut self,
        tree: &Rc<ast::Tree>,
        from: &Rc<ast::Tree>,
        into: &Rc<ast::Tree>,
    ) -> Option<Rc<ast::Tree>> {
        if let Some(tree) = self.substitute.get(&(
            tree.clone().into(),
            from.clone().into(),
            into.clone().into(),
        )) {
            return tree.clone();
        }
        let out = ast::substitute(self, tree, from, into);
        self.substitute.insert(
            (
                tree.clone().into(),
                from.clone().into(),
                into.clone().into(),
            ),
            out.clone(),
        );
        out
    }

    /// Expands let bindings and function calls until the top level construct is a base term.
    pub fn translate(
        &mut self,
        tree: &Rc<ast::Tree>,
    ) -> Result<Option<Rc<ast::Tree>>, ast::TranslateError> {
        if let Some(tree) = self.translate.get(&tree.clone().into()) {
            return tree.clone();
        }
        let out = ast::translate(self, tree);
        self.translate.insert(tree.clone().into(), out.clone());
        out
    }

    /// Computes set of unsolvable type constraints for a term. Returns an error
    /// if a solvable constraint is unsatisfiable, or the constraints cannot be inferred.
    pub fn type_of(
        &mut self,
        tree: &Rc<ast::Tree>,
    ) -> Result<typed::ConstrainedType, typed::TypeError> {
        if let Some(ty) = self.type_of.get(&tree.clone().into()) {
            return ty.clone();
        }
        let ty = typed::type_of(self, tree);
        self.type_of.insert(tree.clone().into(), ty.clone());
        ty
    }

    fn lookup_tree_variable(&self, idx: usize) -> Rc<ast::Tree> {
        self.var_stack[idx].clone()
    }

    fn new_lambda<F: FnOnce(&mut Self) -> Rc<ast::Tree>>(&mut self, f: F) -> Rc<ast::Tree> {
        // Create variable node
        let arg = Rc::new(ast::Tree::Variable);

        // Apply inner with the variable
        self.var_stack.push(arg.clone());
        let inner = f(self);
        self.var_stack.pop();

        // Create the lambda
        Rc::new(ast::Tree::Lambda(Lambda(arg, inner)))
    }

    fn new_let<F: FnOnce(&mut Self) -> Rc<ast::Tree>>(
        &mut self,
        bind: Rc<ast::Tree>,
        inner: F,
    ) -> Rc<ast::Tree> {
        // Create variable node
        let arg = Rc::new(ast::Tree::Variable);

        // Apply inner with the variable
        self.var_stack.push(arg.clone());
        let inner = inner(self);
        self.var_stack.pop();

        // Create the let
        Rc::new(ast::Tree::Let(bind, arg, inner))
    }
}