summaryrefslogtreecommitdiff
path: root/dhall/src/semantics/resolve/env.rs
blob: bde99d17dbf21de37e09ce6ce37f417985982353 (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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use std::collections::HashMap;

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

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

pub type CyclesStack = Vec<ImportLocation>;

/// Environment for resolving imports
pub struct ImportEnv<'cx> {
    cx: Ctxt<'cx>,
    disk_cache: Option<Cache>, // `None` if it failed to initialize
    mem_cache: HashMap<ImportLocation, ImportResultId<'cx>>,
    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<'cx> ImportEnv<'cx> {
    pub fn new(cx: Ctxt<'cx>) -> Self {
        ImportEnv {
            cx,
            disk_cache: Cache::new().ok(),
            mem_cache: Default::default(),
            stack: Default::default(),
        }
    }

    pub fn cx(&self) -> Ctxt<'cx> {
        self.cx
    }

    pub fn get_from_mem_cache(
        &self,
        location: &ImportLocation,
    ) -> Option<ImportResultId<'cx>> {
        Some(*self.mem_cache.get(location)?)
    }

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

    pub fn check_hash(
        &self,
        import: ImportId<'cx>,
        result: ImportResultId<'cx>,
    ) -> Result<(), Error> {
        check_hash(self.cx(), import, result)
    }

    pub fn write_to_mem_cache(
        &mut self,
        location: ImportLocation,
        result: ImportResultId<'cx>,
    ) {
        self.mem_cache.insert(location, result);
    }

    pub fn write_to_disk_cache(
        &self,
        hash: &Option<Hash>,
        result: ImportResultId<'cx>,
    ) {
        if let Some(disk_cache) = self.disk_cache.as_ref() {
            if let Some(hash) = hash {
                let expr = &self.cx()[result];
                let _ = disk_cache.insert(self.cx(), hash, expr);
            }
        }
    }

    pub fn with_cycle_detection(
        &mut self,
        location: ImportLocation,
        do_resolve: impl FnOnce(&mut Self) -> Result<Typed<'cx>, Error>,
    ) -> Result<Typed<'cx>, 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
    }
}