context.rs 9.51 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
            .next()
51
            .ok_or_else(|| Error::UndefinedPrimitive(String::from(prim_name)))?;
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
    pub fn get_strategy(&self, strat_name: &str) -> Result<StrategyDef<'d, 'f>> {
63
        self.scopes
64
            .borrow()
65 66 67
            .iter()
            .rev()
            .flat_map(|scope| scope.strategy.get(strat_name))
68
            .cloned() // StrategyDef is fairly cheap to clone
69
            .next()
70
            .ok_or_else(|| Error::UndefinedStrategy(String::from(strat_name)))
71 72
    }

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

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

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

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

        self.push_scope(overlay);
    }

96
    fn _apply_overlay(&self, scope: Scope<'d, 'f, ATermRef<'f>>) {
97 98 99 100
        debug_assert!(
            scope.strategy.is_empty(),
            "_apply_overlay: Interpreter bug, overlay is leaking a strategy definition"
        );
101
        for (name, value) in scope.term {
102
            if let Some(value) = value {
103
                let result = self.match_term(name, &value);
104 105 106 107 108 109 110 111 112 113
                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? >:("
                )
            }
114
        }
115 116 117 118

    }

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

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

139 140 141 142 143 144
    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
145 146
            last_scope.is_overlay,
            "clear_overlay: Interpreter bug, unexpected normal scope"
147 148 149 150
        );
        last_scope.term.clear();
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

286 287 288
    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
289
        current: &ATermRef<'f>,
290
    ) -> Result<()> {
291 292
        let term = get_term_option(scopes, term_name)?;
        match term {
293
            (_, Some(term)) => {
294
                if Borrow::<ATerm>::borrow(&term) == current.borrow() {
295
                    Ok(())
296
                } else {
297
                    Err(Error::StrategyFailed)
298 299
                }
            }
300 301 302
            (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
303
                        "match_term: No scope had {}, but we just \
304
                                replaced {} when we added it?!",
Jeff Smits's avatar
Jeff Smits committed
305 306 307
                        term_name,
                        t
                    ))
308
                } else {
309
                    Ok(())
310 311
                }
            }
312 313 314
        }
    }
}