context.rs 9.6 KB
Newer Older
1
use error::{Result, Error};
2
use factory::{ATermFactory, ATerm, ATermRef};
3
use preprocess::{Def, StrategyDef};
4
use primitives::{Primitives, eval_prim_ref};
5
use primitives::ssl::State;
6

7 8
use fnv::FnvHashMap;

9 10
use std::fmt;
use std::borrow::Borrow;
11
use std::cell::RefCell;
12

Jeff Smits's avatar
Jeff Smits committed
13
/// This context is internally mutable in `RefCell` fields. This is just for convenience as we don't
14
/// want to pass around the mutable fields separately. But the internal mutability is not a hidden
15
/// thing, there is observable effect, so the name of the context includes `Mut`.
Jeff Smits's avatar
Jeff Smits committed
16
pub struct MutContext<'d, 'f: 'd> {
17 18
    pub stack_tracer: RefCell<StackTracer<'f>>,
    pub scopes: RefCell<Vec<Scope<'d, 'f, ATermRef<'f>>>>,
19
    pub factory: &'f ATermFactory,
Jeff Smits's avatar
Jeff Smits committed
20
    pub primitives: Vec<&'static Primitives>,
Jeff Smits's avatar
Jeff Smits committed
21
    pub ssl_state: RefCell<State<'f>>,
22 23
}

Jeff Smits's avatar
Jeff Smits committed
24
impl<'d, 'f: 'd> MutContext<'d, 'f> {
25
    pub fn new(
26
        factory: &'f ATermFactory,
27
        scopes: Vec<Scope<'d, 'f, ATermRef<'f>>>,
28
        primitives: Vec<&'static Primitives>,
29
    ) -> MutContext<'d, 'f> {
30 31
        MutContext {
            stack_tracer: RefCell::new(StackTracer::default()),
32
            factory: factory,
Jeff Smits's avatar
Jeff Smits committed
33
            primitives: primitives,
34
            scopes: RefCell::new(scopes),
35
            ssl_state: RefCell::new(State::new()),
36 37 38
        }
    }

39 40
    pub fn call_primitive(
        &self,
41
        prim_name: &'f str,
42 43
        sargs: &[StrategyDef<'d, 'f>],
        targs: &[ATermRef<'f>],
Jeff Smits's avatar
Jeff Smits committed
44 45
        current: ATermRef<'f>,
    ) -> Result<ATermRef<'f>> {
Jeff Smits's avatar
Jeff Smits committed
46 47 48
        let prim_ref = self.primitives
            .iter()
            .flat_map(|prims| prims.get(prim_name))
Jeff Smits's avatar
Jeff Smits committed
49
            .cloned() // ATermRef should be very cheap to clone
Jeff Smits's avatar
Jeff Smits committed
50 51
            .next()
            .ok_or_else(|| Error::UndefinedPrimitive(prim_name.to_owned()))?;
52
        self.stack_tracer.borrow_mut().push(prim_name);
53
        let result = eval_prim_ref(&prim_ref, self, sargs, targs, current);
54
        if result.is_ok() {
55
            self.stack_tracer.borrow_mut().pop_on_success();
56
        } else {
57
            self.stack_tracer.borrow_mut().pop_on_failure();
58 59
        }
        result
Jeff Smits's avatar
Jeff Smits committed
60 61
    }

62
    // Should really return a &SDef, but I can't figure out lifetimes that borrowck will accept :(
63
    pub fn get_strategy(&self, strat_name: &str) -> Result<StrategyDef<'d, 'f>> {
64
        self.scopes
65
            .borrow()
66 67 68
            .iter()
            .rev()
            .flat_map(|scope| scope.strategy.get(strat_name))
69
            .cloned() // StrategyDef is fairly cheap to clone
70
            .next()
Jeff Smits's avatar
Jeff Smits committed
71
            .ok_or_else(|| Error::UndefinedStrategy(strat_name.to_owned()))
72 73
    }

Jeff Smits's avatar
Jeff Smits committed
74
    pub fn get_term(&self, term_name: &str) -> Result<ATermRef<'f>> {
Jeff Smits's avatar
Jeff Smits committed
75 76
        Scopes::get_term_option(&self.scopes.borrow(), term_name)
            .and_then(|(_, o)| o.ok_or(Error::StrategyFailed))
Jeff Smits's avatar
Jeff Smits committed
77 78
    }

79
    pub fn match_term(&self, term_name: &'f str, current: &ATermRef<'f>) -> Result<()> {
80
        Scopes::match_term(&mut self.scopes.borrow_mut(), term_name, current)
Jeff Smits's avatar
Jeff Smits committed
81 82
    }

83
    pub fn push_scope(&self, scope: Scope<'d, 'f, ATermRef<'f>>) {
Jeff Smits's avatar
Jeff Smits committed
84 85 86
        self.scopes.borrow_mut().push(scope);
    }

87 88
    pub fn push_overlay(&self) {
        let overlay = Scope {
89 90
            term: FnvHashMap::default(),
            strategy: FnvHashMap::default(),
91
            is_overlay: true,
92 93 94 95 96
        };

        self.push_scope(overlay);
    }

97
    fn _apply_overlay(&self, scope: Scope<'d, 'f, ATermRef<'f>>) {
98 99 100 101
        debug_assert!(
            scope.strategy.is_empty(),
            "_apply_overlay: Interpreter bug, overlay is leaking a strategy definition"
        );
102
        for (name, value) in scope.term {
103
            if let Some(value) = value {
104
                let result = self.match_term(name, &value);
105 106 107 108 109 110 111 112 113 114
                debug_assert!(
                    result.is_ok(),
                    "_apply_overlay: Interpreter bug, overlay has names that were bound underneath"
                );
            } else {
                unreachable!(
                    "_apply_overlay: Interpreter bug, who put an uninitialised variable in \
                              my overlay? >:("
                )
            }
115
        }
116 117 118 119

    }

    pub fn apply_overlay(&self) {
120 121 122 123
        let scope = self.scopes.borrow_mut().pop().expect(
            "apply_overlay: Interpreter bug, unexpected end of stack",
        );
        debug_assert!(
124 125
            scope.is_overlay,
            "apply_overlay: Interpreter bug, unexpected normal scope"
126
        );
127
        self._apply_overlay(scope);
128 129 130
    }

    pub fn drop_overlay(&self) {
131 132 133 134
        let popped_scope = self.scopes.borrow_mut().pop().expect(
            "drop_overlay: Interpreter bug, unexpected end of stack",
        );
        debug_assert!(
135
            popped_scope.is_overlay,
136 137
            "drop_overlay: Interpreter bug, unexpected normal scope"
        );
Jeff Smits's avatar
Jeff Smits committed
138 139
    }

140 141 142 143 144 145
    pub fn clear_overlay(&self) {
        let mut scopes = self.scopes.borrow_mut();
        let last_scope = scopes.last_mut().expect(
            "clear_overlay: Interpreter bug, unexpected end of stack",
        );
        debug_assert!(
Jeff Smits's avatar
Jeff Smits committed
146 147
            last_scope.is_overlay,
            "clear_overlay: Interpreter bug, unexpected normal scope"
148 149 150 151
        );
        last_scope.term.clear();
    }

Jeff Smits's avatar
Jeff Smits committed
152
    pub fn pop_scope(&self) {
153 154 155 156
        let scope = self.scopes.borrow_mut().pop().expect(
            "pop_scope: Interpreter bug, unexpected end of stack",
        );
        debug_assert!(
157
            !scope.is_overlay,
158 159
            "pop_scope: Interpreter bug, unexpected overlay"
        );
160 161 162 163
    }
}

#[derive(Debug, Default, Clone)]
164 165
pub struct StackTracer<'a> {
    stack: Vec<&'a str>,
166
    current_depth: usize,
167 168
}

169 170
impl<'a> StackTracer<'a> {
    pub fn push(&mut self, str: &'a str) {
171
        self.stack.truncate(self.current_depth);
172
        self.stack.push(str);
173
        self.current_depth += 1;
174 175
    }

176 177
    pub fn pop_on_failure(&mut self) {
        self.current_depth -= 1;
178 179
    }

180 181 182
    pub fn pop_on_success(&mut self) {
        let _ = self.stack.pop();
        self.current_depth = self.stack.len();
183
    }
Jeff Smits's avatar
Jeff Smits committed
184

185
    pub fn stack(&self) -> ::std::slice::Iter<&'a str> {
Jeff Smits's avatar
Jeff Smits committed
186 187
        self.stack.iter()
    }
188 189
}

190
impl<'a> fmt::Display for StackTracer<'a> {
191 192
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for (n, name) in self.stack.iter().enumerate() {
Jeff Smits's avatar
Jeff Smits committed
193
            if n + 1 == self.current_depth {
194 195 196 197 198 199 200 201 202 203
                writeln!(f, "{} <==", name)?;
            } else {
                writeln!(f, "{}", name)?;
            }
        }
        Ok(())
    }
}

#[derive(Debug, Default, Clone)]
204 205 206
pub struct Scope<'d, 'f: 'd, ATerm> {
    pub term: FnvHashMap<&'f str, Option<ATerm>>,
    pub strategy: FnvHashMap<&'f str, StrategyDef<'d, 'f>>,
207
    is_overlay: bool,
208 209
}

210
impl<'d, 'f: 'd, ATerm> Scope<'d, 'f, ATerm> {
211
    pub fn new<IA, IS>(terms: IA, defs: IS) -> Self
212
    where
213 214
        IA: IntoIterator<Item = (&'f str, ATerm)>,
        IS: IntoIterator<Item = (&'f str, StrategyDef<'d, 'f>)>,
Jeff Smits's avatar
Jeff Smits committed
215 216
    {
        Scope {
Jeff Smits's avatar
Jeff Smits committed
217
            term: terms.into_iter().map(|(n, a)| (n, Some(a))).collect(),
Jeff Smits's avatar
Jeff Smits committed
218
            strategy: defs.into_iter().collect(),
219
            is_overlay: false,
Jeff Smits's avatar
Jeff Smits committed
220 221 222
        }
    }

223
    pub fn from_defs<I>(defs: I) -> Self
224
    where
225
        I: IntoIterator<Item = &'d Def<'f>>,
226 227
    {
        Scope {
228
            term: FnvHashMap::default(),
Jeff Smits's avatar
Jeff Smits committed
229
            strategy: defs.into_iter()
230
                .map(|def| (def.name, StrategyDef::from_def(def)))
231
                .collect(),
232
            is_overlay: false,
233 234 235
        }
    }

236
    pub fn from_let_defs<I>(no_of_scopes: usize, defs: I) -> Self
237
    where
Jeff Smits's avatar
Jeff Smits committed
238
        I: IntoIterator<Item = &'d Def<'f>>,
239 240
    {
        Scope {
241
            term: FnvHashMap::default(),
242
            strategy: defs.into_iter()
Jeff Smits's avatar
Jeff Smits committed
243 244 245
                .map(|def| {
                    (def.name, StrategyDef::from_let_def(def, no_of_scopes))
                })
246
                .collect(),
247
            is_overlay: false,
248 249 250
        }
    }

251
    pub fn from_fresh_variables<I>(fresh_vars: I) -> Self
252
    where
253
        I: IntoIterator<Item = &'f str>,
254 255
    {
        Scope {
Jeff Smits's avatar
Jeff Smits committed
256 257 258 259
            term: fresh_vars
                .into_iter()
                .map(|fresh_var| (fresh_var, None))
                .collect(),
260
            strategy: FnvHashMap::default(),
261
            is_overlay: false,
Jeff Smits's avatar
Jeff Smits committed
262 263
        }
    }
264 265 266 267 268 269
}

#[allow(non_snake_case)]
pub mod Scopes {
    use super::*;

Jeff Smits's avatar
Jeff Smits committed
270
    pub fn get_term_option<'f>(
271
        scopes: &[Scope<ATermRef<'f>>],
272
        term_name: &str,
Jeff Smits's avatar
Jeff Smits committed
273
    ) -> Result<(usize, Option<ATermRef<'f>>)> {
274
        let mut offset = None;
Jeff Smits's avatar
Jeff Smits committed
275
        for (n, scope) in scopes.iter().enumerate().rev() {
276 277 278 279 280
            if offset == None && scope.is_overlay {
                offset = Some(n);
            }
            if let Some(binding) = scope.term.get(term_name) {
                let n = offset.unwrap_or(n);
Jeff Smits's avatar
Jeff Smits committed
281
                return Ok((n, binding.clone()));
282 283 284
            }
        }
        Err(Error::UndefinedVariable(term_name.to_owned()))
285 286
    }

287 288 289
    pub fn match_term<'d, 'f>(
        scopes: &mut Vec<Scope<'d, 'f, ATermRef<'f>>>,
        term_name: &'f str,
Jeff Smits's avatar
Jeff Smits committed
290
        current: &ATermRef<'f>,
291
    ) -> Result<()> {
292 293
        let term = get_term_option(scopes, term_name)?;
        match term {
294
            (_, Some(term)) => {
295
                if Borrow::<ATerm>::borrow(&term) == current.borrow() {
296
                    Ok(())
297
                } else {
298
                    Err(Error::StrategyFailed)
299 300
                }
            }
301 302 303
            (n, None) => {
                if let Some(Some(t)) = scopes[n].term.insert(term_name, Some(current.clone())) {
                    unreachable!(format!(
Jeff Smits's avatar
Jeff Smits committed
304
                        "match_term: No scope had {}, but we just \
305
                                replaced {} when we added it?!",
Jeff Smits's avatar
Jeff Smits committed
306 307 308
                        term_name,
                        t
                    ))
309
                } else {
310
                    Ok(())
311 312
                }
            }
313 314 315
        }
    }
}