summaryrefslogtreecommitdiff
path: root/dhall/src/semantics/resolve/env.rs
blob: 29dd16bf01cc8cc7fc8ba0d9f24eb329cf8df24f (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
129
use std::collections::HashMap;

use crate::error::{Error, ImportError};
use crate::semantics::{AlphaVar, Cache, ImportLocation, VarEnv};
use crate::syntax::{Hash, Label, V};
use crate::Typed;

/// Environment for resolving names.
#[derive(Debug, Clone, Default)]
pub struct NameEnv {
    names: Vec<Label>,
}

pub type MemCache = HashMap<ImportLocation, Typed>;
pub type CyclesStack = Vec<ImportLocation>;

/// Environment for resolving imports
#[derive(Debug)]
pub struct ImportEnv {
    disk_cache: Option<Cache>, // Missing if it failed to initialize
    mem_cache: MemCache,
    stack: CyclesStack,
}

impl NameEnv {
    pub fn new() -> Self {
        NameEnv::default()
    }
    pub fn as_varenv(&self) -> VarEnv {
        VarEnv::from_size(self.names.len())
    }

    pub fn insert(&self, x: &Label) -> Self {
        let mut env = self.clone();
        env.insert_mut(x);
        env
    }
    pub fn insert_mut(&mut self, x: &Label) {
        self.names.push(x.clone())
    }
    pub fn remove_mut(&mut self) {
        self.names.pop();
    }

    pub fn unlabel_var(&self, var: &V) -> Option<AlphaVar> {
        let V(name, idx) = var;
        let (idx, _) = self
            .names
            .iter()
            .rev()
            .enumerate()
            .filter(|(_, n)| *n == name)
            .nth(*idx)?;
        Some(AlphaVar::new(idx))
    }
    pub fn label_var(&self, var: AlphaVar) -> V {
        let name = &self.names[self.names.len() - 1 - var.idx()];
        let idx = self
            .names
            .iter()
            .rev()
            .take(var.idx())
            .filter(|n| *n == name)
            .count();
        V(name.clone(), idx)
    }
}

impl ImportEnv {
    pub fn new() -> Self {
        ImportEnv {
            disk_cache: Cache::new().ok(),
            mem_cache: Default::default(),
            stack: Default::default(),
        }
    }

    pub fn get_from_mem_cache(
        &mut self,
        location: &ImportLocation,
    ) -> Option<Typed> {
        Some(self.mem_cache.get(location)?.clone())
    }

    pub fn get_from_disk_cache(
        &mut self,
        hash: &Option<Hash>,
    ) -> Option<Typed> {
        let hash = hash.as_ref()?;
        let expr = self.disk_cache.as_ref()?.get(hash).ok()?;
        Some(expr)
    }

    pub fn write_to_mem_cache(
        &mut self,
        location: ImportLocation,
        expr: Typed,
    ) {
        self.mem_cache.insert(location, expr);
    }

    pub fn write_to_disk_cache(&mut self, hash: &Option<Hash>, expr: &Typed) {
        if let Some(disk_cache) = self.disk_cache.as_ref() {
            if let Some(hash) = hash {
                let _ = disk_cache.insert(hash, &expr);
            }
        }
    }

    pub fn with_cycle_detection(
        &mut self,
        location: ImportLocation,
        do_resolve: impl FnOnce(&mut Self) -> Result<Typed, Error>,
    ) -> Result<Typed, Error> {
        if self.stack.contains(&location) {
            return Err(
                ImportError::ImportCycle(self.stack.clone(), location).into()
            );
        }
        // Push the current location on the stack
        self.stack.push(location);
        // Resolve the import recursively
        // WARNING: do not propagate errors here or the stack will get messed up.
        let result = do_resolve(self);
        // Remove location from the stack.
        self.stack.pop().unwrap();
        result
    }
}