context.rs 9.89 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

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

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

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

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

75 76 77 78
    fn get_term_option(&self, term_name: &str) -> Result<Option<ATermRef>> {
        Scopes::get_term_option(&self.scopes.borrow(), term_name)
    }

Jeff Smits's avatar
Jeff Smits committed
79
    pub fn get_term(&self, term_name: &str) -> Result<ATermRef> {
80
        self.get_term_option(term_name).and_then(|o| {
81 82
            o.ok_or(Error::StrategyFailed)
        })
Jeff Smits's avatar
Jeff Smits committed
83 84
    }

85
    pub fn match_term(&self, term_name: &'d str, current: &ATermRef) -> Result<()> {
86
        Scopes::match_term(&mut self.scopes.borrow_mut(), term_name, current)
Jeff Smits's avatar
Jeff Smits committed
87 88
    }

89
    pub fn push_scope(&self, scope: Scope<'d, ATermRef>) {
Jeff Smits's avatar
Jeff Smits committed
90 91 92
        self.scopes.borrow_mut().push(scope);
    }

93 94
    pub fn push_overlay(&self) {
        let overlay = Scope {
95 96
            term: FnvHashMap::default(),
            strategy: FnvHashMap::default(),
97
            is_overlay: true,
98 99 100 101 102
        };

        self.push_scope(overlay);
    }

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

    }

    pub fn apply_overlay(&self) {
126 127 128 129
        let scope = self.scopes.borrow_mut().pop().expect(
            "apply_overlay: Interpreter bug, unexpected end of stack",
        );
        debug_assert!(
130 131
            scope.is_overlay,
            "apply_overlay: Interpreter bug, unexpected normal scope"
132
        );
133
        self._apply_overlay(scope);
134 135 136
    }

    pub fn drop_overlay(&self) {
137 138 139 140
        let popped_scope = self.scopes.borrow_mut().pop().expect(
            "drop_overlay: Interpreter bug, unexpected end of stack",
        );
        debug_assert!(
141
            popped_scope.is_overlay,
142 143
            "drop_overlay: Interpreter bug, unexpected normal scope"
        );
Jeff Smits's avatar
Jeff Smits committed
144 145
    }

146 147 148 149 150 151 152 153 154 155 156 157
    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!(
        last_scope.is_overlay,
        "clear_overlay: Interpreter bug, unexpected normal scope"
        );
        last_scope.term.clear();
    }

Jeff Smits's avatar
Jeff Smits committed
158
    pub fn pop_scope(&self) {
159 160 161 162
        let scope = self.scopes.borrow_mut().pop().expect(
            "pop_scope: Interpreter bug, unexpected end of stack",
        );
        debug_assert!(
163
            !scope.is_overlay,
164 165
            "pop_scope: Interpreter bug, unexpected overlay"
        );
166 167 168 169 170 171
    }
}

#[derive(Debug, Default, Clone)]
pub struct StackTracer {
    stack: Vec<String>,
172
    current_depth: usize,
173 174 175 176
}

impl StackTracer {
    pub fn push(&mut self, str: String) {
177
        self.stack.truncate(self.current_depth);
178
        self.stack.push(str);
179
        self.current_depth += 1;
180 181
    }

182 183
    pub fn pop_on_failure(&mut self) {
        self.current_depth -= 1;
184 185
    }

186 187 188
    pub fn pop_on_success(&mut self) {
        let _ = self.stack.pop();
        self.current_depth = self.stack.len();
189
    }
Jeff Smits's avatar
Jeff Smits committed
190

Jeff Smits's avatar
Jeff Smits committed
191
    pub fn stack(&self) -> ::std::slice::Iter<String> {
Jeff Smits's avatar
Jeff Smits committed
192 193
        self.stack.iter()
    }
194 195 196 197 198
}

impl fmt::Display for StackTracer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for (n, name) in self.stack.iter().enumerate() {
Jeff Smits's avatar
Jeff Smits committed
199
            if n + 1 == self.current_depth {
200 201 202 203 204 205 206 207 208 209
                writeln!(f, "{} <==", name)?;
            } else {
                writeln!(f, "{}", name)?;
            }
        }
        Ok(())
    }
}

#[derive(Debug, Default, Clone)]
210
pub struct Scope<'a, ATerm> {
211 212
    pub term: FnvHashMap<&'a str, Option<ATerm>>,
    pub strategy: FnvHashMap<&'a str, StrategyDef<'a>>,
213
    is_overlay: bool,
214 215
}

216 217
impl<'a, ATerm> Scope<'a, ATerm> {
    pub fn new<IA, IS>(terms: IA, defs: IS) -> Self
218
    where
219 220
        IA: IntoIterator<Item = (&'a str, ATerm)>,
        IS: IntoIterator<Item = (&'a str, StrategyDef<'a>)>,
Jeff Smits's avatar
Jeff Smits committed
221 222
    {
        Scope {
Jeff Smits's avatar
Jeff Smits committed
223
            term: terms.into_iter().map(|(n, a)| (n, Some(a))).collect(),
Jeff Smits's avatar
Jeff Smits committed
224
            strategy: defs.into_iter().collect(),
225
            is_overlay: false,
Jeff Smits's avatar
Jeff Smits committed
226 227 228
        }
    }

229
    pub fn from_defs<I>(defs: I) -> Self
230
    where
231
        I: IntoIterator<Item = &'a Def>,
232 233
    {
        Scope {
234
            term: FnvHashMap::default(),
Jeff Smits's avatar
Jeff Smits committed
235
            strategy: defs.into_iter()
236
                .map(|def| (def.name.as_str(), StrategyDef::from_def(def)))
237
                .collect(),
238
            is_overlay: false,
239 240 241
        }
    }

242
    pub fn from_let_defs<I>(no_of_scopes: usize, defs: I) -> Self
243
    where
244
        I: IntoIterator<Item=&'a Def>,
245 246
    {
        Scope {
247
            term: FnvHashMap::default(),
248
            strategy: defs.into_iter()
249
                .map(|def| (def.name.as_str(), StrategyDef::from_let_def(def, no_of_scopes)))
250
                .collect(),
251
            is_overlay: false,
252 253 254
        }
    }

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

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

271 272 273 274 275 276
    pub fn get_term_option(
        vec: &Vec<Scope<ATermRef>>,
        term_name: &str,
    ) -> Result<Option<ATermRef>> {
        vec.iter()
            .rev()
277
            .flat_map(|scope| scope.term.get(term_name))
278 279 280 281 282
            .cloned()
            .next()
            .ok_or_else(|| Error::UndefinedVariable(term_name.to_owned()))
    }

283 284 285
    pub fn match_term<'a>(
        scopes: &mut Vec<Scope<'a, ATermRef>>,
        term_name: &'a str,
286 287
        current: &ATermRef,
    ) -> Result<()> {
288 289 290 291 292 293 294 295 296 297 298
        let term = get_term_option(scopes, term_name)?;
        match term {
            Some(term) => {
                if Borrow::<ATerm>::borrow(&term) == current.borrow() {
                    return Ok(());
                } else {
                    return Err(Error::StrategyFailed);
                }
            }
            None => {
                for mut scope in scopes.iter_mut().rev() {
299 300
                    if scope.is_overlay || scope.term.contains_key(term_name) {
                        if let Some(Some(t)) = scope.term.insert(
301
                            term_name,
302
                            Some(current.clone()),
303 304 305 306 307 308 309 310 311 312
                        )
                        {
                            unreachable!(format!(
                                "match_term: No scope had {}, but we just \
                                replaced {} when we added it?!",
                                term_name,
                                t
                            ))
                        } else {
                            return Ok(());
313 314 315 316
                        }
                    }
                }
            }
317
        }
318 319
        unreachable!(format!(
            "match_term: First we could find {} unbound, then we couldn't \
320
            anymore?!",
321 322
            term_name
        ))
323 324
    }
}