interpreter.rs 25.7 KB
Newer Older
1
use context::MutContext;
2
use error::{Result, Error};
3
use factory::{ATermFactory, ATermRef};
4
use preprocess;
5
use preprocess::{StrategyDef, preprocess};
6
use primitives::Primitives;
7

Jeff Smits's avatar
Jeff Smits committed
8
use aterm::{ATerm as A, ATermFactory as ATF};
9
use aterm::BrokenF32;
10
use aterm::string_share::InternedString;
11

12
use ref_eq::ref_eq;
13 14

use std::iter;
15
use std::result;
16

17 18 19 20 21 22
macro_rules! eprintln {
    ($($tt:tt)*) => {{
        use std::io::{Write, stderr};
        writeln!(&mut stderr(), $($tt)*).unwrap();
    }}
}
23

24
#[derive(Debug)]
25
pub struct TracedError(pub Error, pub String);
26

Jeff Smits's avatar
Jeff Smits committed
27
pub struct Libraries<A> {
28
    libs: Vec<A>,
Jeff Smits's avatar
Jeff Smits committed
29
    prims: Vec<&'static Primitives>,
30 31
}

32 33
impl From<Error> for TracedError {
    fn from(e: Error) -> Self {
34
        TracedError(e, String::new())
35 36 37 38 39
    }
}

impl From<::std::io::Error> for TracedError {
    fn from(e: ::std::io::Error) -> Self {
40
        TracedError(e.into(), String::new())
41 42 43 44 45
    }
}

impl From<::std::fmt::Error> for TracedError {
    fn from(e: ::std::fmt::Error) -> Self {
46
        TracedError(e.into(), String::new())
47 48 49
    }
}

Jeff Smits's avatar
Jeff Smits committed
50
impl<A> iter::FromIterator<(A, Option<&'static Primitives>)> for Libraries<A> {
51
    fn from_iter<T>(iter: T) -> Self
52 53
    where
        T: IntoIterator<Item = (A, Option<&'static Primitives>)>,
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
    {
        let mut libs = Libraries {
            libs: Vec::new(),
            prims: Vec::new(),
        };
        for (a, p1) in iter {
            libs.libs.push(a);
            if let Some(p1) = p1 {
                if libs.prims.iter().all(|p2| !ref_eq(p1, *p2)) {
                    libs.prims.push(p1);
                }
            }
        }
        libs
    }
}

Jeff Smits's avatar
Jeff Smits committed
71
pub fn interpret_main<'f>(
72
    factory: &'f ATermFactory,
Jeff Smits's avatar
Jeff Smits committed
73 74 75
    program: ATermRef<'f>,
    libraries: Libraries<ATermRef<'f>>,
) -> result::Result<ATermRef<'f>, TracedError> {
76 77 78 79
    interpret(
        factory,
        program,
        libraries,
80
        factory.intern_string("main_0_0"),
Jeff Smits's avatar
Jeff Smits committed
81
        factory.list(iter::once(factory.string(String::from("main_0_0")))),
82
    )
83 84
}

85
pub fn interpret<'f>(
86
    factory: &'f ATermFactory,
Jeff Smits's avatar
Jeff Smits committed
87 88
    program: ATermRef<'f>,
    libraries: Libraries<ATermRef<'f>>,
89
    strategy: InternedString<'f>,
Jeff Smits's avatar
Jeff Smits committed
90 91
    input: ATermRef<'f>,
) -> result::Result<ATermRef<'f>, TracedError> {
92
    use context::Scope;
Jeff Smits's avatar
Jeff Smits committed
93
    use ctree::match_;
94

Jeff Smits's avatar
Jeff Smits committed
95
    let program = match match_(&program).and_then(preprocess) {
96
        Ok(v) => v,
97
        Err(e) => return Err(TracedError(e, String::new())),
98 99
    };
    let libs = match libraries
100 101
        .libs
        .into_iter()
Jeff Smits's avatar
Jeff Smits committed
102
        .map(|l| match_(&l).and_then(preprocess))
103
        .collect::<Result<Vec<_>>>() {
104
        Ok(v) => v,
105
        Err(e) => return Err(TracedError(e, String::new())),
106
    };
107 108 109
    let base_scopes = libs.iter().map(Scope::from_defs).collect();
    let context = MutContext::new(factory, base_scopes, libraries.prims);
    context.push_scope(Scope::from_defs(&program));
110 111
    let main = match context.get_strategy(strategy) {
        Ok(v) => v,
Jeff Smits's avatar
Jeff Smits committed
112 113 114 115 116 117
        Err(e) => {
            return Err(TracedError(
                e,
                context.stack_tracer.into_inner().to_string(),
            ))
        }
118
    };
119 120
    let result = eval_sdef(&main, &context, Vec::new(), Vec::new(), input);
    match result {
121
        Ok(v) => Ok(v),
Jeff Smits's avatar
Jeff Smits committed
122 123 124 125
        Err(e) => Err(TracedError(
            e,
            context.stack_tracer.into_inner().to_string(),
        )),
126
    }
127 128
}

129
pub trait Eval<'d, 'f> {
Jeff Smits's avatar
Jeff Smits committed
130
    fn eval(&'d self, context: &MutContext<'d, 'f>, current: ATermRef<'f>) -> Result<ATermRef<'f>>;
131 132
}

Jeff Smits's avatar
Jeff Smits committed
133
impl<'d, 'f: 'd> Eval<'d, 'f> for preprocess::Strategy<'f> {
Jeff Smits's avatar
Jeff Smits committed
134
    fn eval(&'d self, context: &MutContext<'d, 'f>, current: ATermRef<'f>) -> Result<ATermRef<'f>> {
135
        use preprocess::Strategy::*;
136
        use context;
137
        match *self {
138
            Let(ref defs, ref body) => {
139
                // note the + 1, because `let`s allow recursive definitions
140
                let no_of_scopes = context.scopes.borrow().len() + 1;
141
                context.push_scope(context::Scope::from_let_defs(no_of_scopes, defs.iter()));
142
                let result = body.eval(context, current);
Jeff Smits's avatar
Jeff Smits committed
143
                context.pop_scope();
144 145
                result
            }
146
            CallT(name, ref sargs, ref targs) => {
147
                let sdeft = context.get_strategy(name)?;
148 149
                let sargs = sargs
                    .into_iter()
150
                    .enumerate()
151
                    .map(|(n, s)| strategy_def_from_strategy(context, name, n, s))
152
                    .collect::<Result<_>>()?;
153 154
                let targs = targs
                    .into_iter()
155
                    .map(|term| build_term::build(term, context))
156
                    .collect::<Result<_>>()?;
157
                eval_sdef(&sdeft, context, sargs, targs, current)
158
            }
159
            CallDynamic(term_name, ref sargs, ref targs) => {
160
                let strategy_name = context.get_term(term_name)?;
Jeff Smits's avatar
Jeff Smits committed
161
                let strategy_name = strategy_name.get_string().ok_or_else(|| {
162
                    Error::UndefinedStrategy(format!(
163 164
                        "Invocation target is invalid \
                            (cannot be evaluated): {}",
165 166 167
                        strategy_name
                    ))
                })?;
168
                let sdeft = context.get_strategy(strategy_name)?;
169 170
                let sargs = sargs
                    .into_iter()
171
                    .enumerate()
Jeff Smits's avatar
Jeff Smits committed
172
                    .map(|(n, s)| {
173
                        strategy_def_from_strategy(context, strategy_name, n, s)
174
                    })
175
                    .collect::<Result<_>>()?;
176 177
                let targs = targs
                    .into_iter()
178
                    .map(|term| build_term::build(term, context))
179
                    .collect::<Result<_>>()?;
180
                eval_sdef(&sdeft, context, sargs, targs, current)
Jeff Smits's avatar
Jeff Smits committed
181
            }
182 183
            Fail => Err(Error::StrategyFailed),
            Id => Ok(current),
184 185 186 187 188 189 190 191 192 193 194 195
            Match(ref term) => {
                context.push_overlay();
                match eval_match(context, term, &current) {
                    Ok(()) => {
                        context.apply_overlay();
                        Ok(current)
                    }
                    Err(e) => {
                        context.drop_overlay();
                        Err(e)
                    }
                }
196
            }
197
            Build(ref term) => build_term::build(term, context),
198
            Scope(ref fresh_names, ref body) => {
Jeff Smits's avatar
Jeff Smits committed
199 200 201
                context.push_scope(context::Scope::from_fresh_variables(
                    fresh_names.iter().cloned(),
                ));
202
                let result = body.eval(context, current);
Jeff Smits's avatar
Jeff Smits committed
203
                context.pop_scope();
204 205
                result
            }
206 207 208 209 210 211
            Seq(ref strats) => {
                let mut current = current;
                for strat in strats.iter() {
                    current = strat.eval(context, current)?;
                }
                Ok(current)
212
            }
213
            GuardedLChoice(ref s_if_then, ref s_else) => {
214
                context.push_overlay();
215 216 217 218 219 220 221 222 223 224 225 226 227
                for &(ref s_if, ref s_then) in s_if_then.iter() {
                    match s_if.eval(context, current.clone()) {
                        Ok(current) => {
                            context.apply_overlay();
                            return s_then.eval(context, current);
                        }
                        Err(Error::StrategyFailed) => {
                            context.clear_overlay();
                        }
                        Err(e) => {
                            context.drop_overlay();
                            return Err(e);
                        }
Jeff Smits's avatar
Jeff Smits committed
228
                    }
229
                }
230 231
                context.drop_overlay();
                s_else.eval(context, current)
232
            }
233
            ConsMatch(ref map, ref s_else) => {
Jeff Smits's avatar
Jeff Smits committed
234
                let matching_arm = current.get_application().and_then(
235
                    |(cons, _)| map.get(&cons),
Jeff Smits's avatar
Jeff Smits committed
236
                );
237 238 239 240 241 242
                if let Option::Some(s_match) = matching_arm {
                    s_match.eval(context, current.clone())
                } else {
                    s_else.eval(context, current)
                }
            }
243
            PrimT(name, ref sargs, ref targs) => {
244 245
                let sargs = sargs
                    .into_iter()
246
                    .enumerate()
247
                    .map(|(n, s)| strategy_def_from_strategy(context, name, n, s))
248
                    .collect::<Result<Vec<_>>>()?;
249 250
                let targs = targs
                    .into_iter()
251
                    .map(|term| build_term::build(term, context))
252 253
                    .collect::<Result<Vec<_>>>()?;
                context.call_primitive(name, &sargs, &targs, current)
Jeff Smits's avatar
Jeff Smits committed
254
            }
Jeff Smits's avatar
Jeff Smits committed
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
            Some(ref strat) => {
                eval_(
                    strat,
                    context,
                    current,
                    some_rec,
                    |_| Err(Error::StrategyFailed),
                )
            }
            One(ref strat) => {
                eval_(
                    strat,
                    context,
                    current,
                    one_rec,
                    |_| Err(Error::StrategyFailed),
                )
            }
Jeff Smits's avatar
Jeff Smits committed
273
            All(ref strat) => eval_(strat, context, current, all_rec, Ok),
274
            ImportTerm(modname) => {
Jeff Smits's avatar
Jeff Smits committed
275
                // This primitive really is interwoven with SSL :(
276
                use primitives::ssl;
277 278
                let fd = ssl::fopen(
                    context,
279 280 281
                    &[],
                    &[
                        context.factory.string(modname),
282
                        context.factory.string(String::new()),
283 284 285
                    ],
                    current,
                )?;
286
                ssl::read_term_from_stream(context, &[], &[fd.clone()], fd)
Jeff Smits's avatar
Jeff Smits committed
287
            }
288 289 290 291
        }
    }
}

Jeff Smits's avatar
Jeff Smits committed
292
fn eval_match<'d, 'f: 'd>(
293
    context: &MutContext<'d, 'f>,
294
    match_term: &'d preprocess::MatchTerm<'f>,
Jeff Smits's avatar
Jeff Smits committed
295
    current: &ATermRef<'f>,
296
) -> Result<()> {
297
    use preprocess::MatchTerm::*;
298 299
    use preprocess::C;

300
    match *match_term {
301
        Var(s) => context.match_term(s, current),
302 303 304
        Wld => Ok(()),
        Anno(ref cons, ref annos) => {
            eval_match(context, &*cons, current)?;
Jeff Smits's avatar
Jeff Smits committed
305
            let current_annos = current.annotations.into_iter().cloned();
306
            let current_annos = &context.factory.list(current_annos);
307 308
            eval_match(context, &*annos, current_annos)
        }
309
        As(v, ref term) => {
Jeff Smits's avatar
Jeff Smits committed
310
            context.match_term(v, current)?;
311 312 313
            eval_match(context, &*term, current)
        }
        Int(i) => {
314 315
            current
                .get_int()
316 317
                .and_then(|i2| if i == i2 { Some(()) } else { None })
                .ok_or(Error::StrategyFailed)
318 319
        }
        Real(BrokenF32(f)) => {
320 321 322 323
            eprintln!(
                "WARNING: Pattern matching against floating point literal: very unlikely to \
                       match!"
            );
324 325
            current
                .get_real()
326 327
                .and_then(|f2| if f == f2 { Some(()) } else { None })
                .ok_or(Error::StrategyFailed)
328
        }
329
        Str(s) => {
330 331
            current
                .get_string()
332
                .and_then(|s2| if s == s2 { Some(()) } else { None })
333
                .ok_or(Error::StrategyFailed)
334
        }
335
        Op(ref c, ref children) if c == "Cons" && children.len() == 2 => {
336
            match current.term {
337 338 339 340 341 342 343 344 345 346 347 348 349
                ::factory::Term::List(ref r) => {
                    match **r {
                        ::factory::TermList::Cons(ref head, ref tail) => {
                            eval_match(context, &children[0], head)?;
                            eval_match(
                                context,
                                &children[1],
                                &context.factory.list_term(tail.clone()),
                            )
                        }
                        ::factory::TermList::Nil => Err(Error::StrategyFailed),
                    }
                }
350 351
                _ => Err(Error::StrategyFailed),
            }
352
        }
353
        Op(ref c, ref children) if c == "Nil" && children.is_empty() => {
354 355
            current
                .get_list()
356 357 358
                .and_then(|v| if v.is_empty() { Some(()) } else { None })
                .ok_or(Error::StrategyFailed)
        }
359
        Op(cons, ref children) => {
360 361
            current
                .get_application()
362
                .ok_or(Error::StrategyFailed)
363
                .and_then(
364
                    |(current_cons, current_children)| if cons == current_cons &&
365 366 367 368 369 370 371 372 373 374 375 376 377
                        children.len() == current_children.len()
                    {
                        children
                            .iter()
                            .zip(current_children.into_iter())
                            .map(|(match_term, current_child)| {
                                eval_match(context, match_term, current_child)
                            })
                            .fold(Ok(()), Result::and)
                    } else {
                        Err(Error::StrategyFailed)
                    },
                )
378 379
        }
        Explode(ref cons, ref children) => {
380
            use factory::Term::*;
Jeff Smits's avatar
Jeff Smits committed
381
            match current.term {
382
                Application(current_cons, ref current_children) => {
383 384 385
                    let current_cons = &context.factory.string(
                        ::std::string::String::from(current_cons),
                    );
Jeff Smits's avatar
Jeff Smits committed
386 387
                    eval_match(context, cons, current_cons)?;
                    let current_children =
Jeff Smits's avatar
Jeff Smits committed
388 389
                        &context.factory.list(current_children.into_iter().cloned());
                    eval_match(context, children, current_children)?;
Jeff Smits's avatar
Jeff Smits committed
390
                }
Jeff Smits's avatar
Jeff Smits committed
391
                List(ref list) => {
392
                    eval_match(context, cons, &context.factory.nil())?;
Jeff Smits's avatar
Jeff Smits committed
393
                    eval_match(context, children, &context.factory.list_term(list.clone()))?;
Jeff Smits's avatar
Jeff Smits committed
394
                }
Jeff Smits's avatar
Jeff Smits committed
395 396 397 398 399 400
                Blob(_) => return Err(Error::StrategyFailed),
                Placeholder(_, ref t) => {
                    let current_cons = &context.factory.string("<>");
                    let current_children = &context.factory.list(::std::iter::once(t.clone()));
                    eval_match(context, cons, current_cons)?;
                    eval_match(context, children, current_children)?;
Jeff Smits's avatar
Jeff Smits committed
401
                }
402 403 404 405 406
                String(s) => {
                    eval_match(context, cons, &context.factory.string(format!("{:?}", s)))?;
                    eval_match(context, children, &context.factory.nil())?;
                }
                Int(_) | Long(_) | Real(_) => {
Jeff Smits's avatar
Jeff Smits committed
407
                    eval_match(context, cons, current)?;
408
                    eval_match(context, children, &context.factory.nil())?;
Jeff Smits's avatar
Jeff Smits committed
409 410 411
                }
            }
            Ok(())
412
        }
413 414 415
        Cached(C(ref cache), ref term) => {
            if let Option::Some(ref cached_term) = *cache.borrow() {
                if cached_term == current {
416
                    return Ok(());
417
                } else {
418
                    return Err(Error::StrategyFailed);
419 420 421 422 423
                }
            }
            let result = build_term::build(term, context)?;
            *cache.borrow_mut() = Some(result.clone());
            if result == *current {
Jeff Smits's avatar
Jeff Smits committed
424
                Ok(())
425
            } else {
Jeff Smits's avatar
Jeff Smits committed
426
                Err(Error::StrategyFailed)
427 428
            }
        }
429 430 431
    }
}

432 433 434 435 436 437 438 439 440
mod build_term {
    use preprocess::BuildTerm;
    use context::MutContext;
    use error::{Error, Result};
    use factory::ATermRef;

    use aterm::BrokenF32;
    use aterm::{ATerm as A, ATermFactory as ATF};

441 442 443 444
    pub fn build<'d, 'f: 'd>(
        term: &BuildTerm<'f>,
        context: &MutContext<'d, 'f>,
    ) -> Result<ATermRef<'f>> {
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
        build_with_annos(term, context, None)
    }

    fn build_with_annos<'d, 'f: 'd>(
        term: &BuildTerm<'f>,
        context: &MutContext<'d, 'f>,
        annos: Option<Vec<ATermRef<'f>>>,
    ) -> Result<ATermRef<'f>> {
        use preprocess::BuildTerm::*;
        use preprocess::C;

        match *term {
            Var(s) => {
                if let Some(annos) = annos {
                    let term = context.get_term(s)?;
                    Ok(context.factory.with_annos(term, annos))
                } else {
                    context.get_term(s)
                }
            }
            Int(i) => {
                Ok(context.factory.with_annos(
                    context.factory.int(i),
                    annos.unwrap_or_default(),
                ))
            }
            Real(BrokenF32(f)) => {
                Ok(context.factory.with_annos(
                    context.factory.real(f),
                    annos.unwrap_or_default(),
                ))
            }
            Str(s) => {
                Ok(context.factory.with_annos(
                    context.factory.string(s),
                    annos.unwrap_or_default(),
                ))
            }
            Op(s, ref t) => {
                let t = build_vec(context, t)?;
                if s == "Cons" && t.len() == 2 {
                    if let ::factory::Term::List(ref tail) = t[1].term {
                        Ok(context.factory.cons(t[0].clone(), tail.clone()))
                    } else {
                        Err(Error::StrategyFailed)
                    }
                } else if s == "Nil" && t.is_empty() {
                    Ok(context.factory.nil())
                } else {
                    Ok(context.factory.with_annos(
495
                        context.factory.application(s, t.into_iter()),
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
                        annos.unwrap_or_default(),
                    ))
                }
            }
            Explode(ref cons, ref children) => {
                let cons = build(cons, context)?;
                let children = build(children, context)?;
                let children = children.get_list().ok_or(Error::UnknownBehaviour(
                    "Non-list in build of explode pattern (#) second argument",
                ))?;

                use factory::Term::*;
                use ctree::string_unescape;

                match cons.term {
                    Application(_, _) => return Err(Error::StrategyFailed),
                    Placeholder(_, _) => {
                        return Err(Error::UnknownBehaviour(
                            "ATerm placeholder in build of explode pattern (#) first argument",
                        ))
                    }
                    Blob(_) => {
                        return Err(Error::UnknownBehaviour(
                            "Blob in build of explode pattern (#) first argument",
                        ))
                    }
                    List(_) => return Ok(context.factory.list(children.iter().cloned())),
                    String(s) => {
                        let result = if s.starts_with('"') {
                            Ok(context.factory.string(string_unescape(s)))
                        } else {
                            let application =
                                context.factory.application(s, children.iter().cloned());

                            Ok(context.factory.with_annos(
                                application,
                                annos.unwrap_or_default(),
                            ))
                        };
                        return result;
                    }
                    Int(_) | Long(_) | Real(_) => {}
                }
                Ok(cons)
            }
            // NOTE: `str '!((1{2}){3})'` gives `1{2}`, so the innermost `annos` list in a build is
            //  used! Therefore we just shadow the input `annos` with the annotations in this `Anno`
            Anno(ref term, ref annos) => {
                let annos: ATermRef<'f> = build(&**annos, context)?;
                if let Some(annos_vec) = annos.get_list() {
                    build_with_annos(term, context, Some(annos_vec.to_vec()))
                } else {
                    build_with_annos(term, context, Some(vec![annos.clone()]))
                }
            }
            Cached(C(ref cache), ref term) => {
                if let Option::Some(ref cached_term) = *cache.borrow() {
553
                    return Ok(cached_term.clone());
554
                }
555 556 557
                let result = build_with_annos(term, context, annos)?;
                *cache.borrow_mut() = Some(result.clone());
                Ok(result)
558 559 560 561 562
            }
        }
    }

    /// Lifts `build` over a vector
563 564 565 566
    fn build_vec<'d, 'f: 'd>(
        context: &MutContext<'d, 'f>,
        vec: &[BuildTerm<'f>],
    ) -> Result<Vec<ATermRef<'f>>> {
567 568 569 570
        vec.into_iter().map(|t| build(t, context)).collect()
    }
}

Jeff Smits's avatar
Jeff Smits committed
571
fn eval_<'d, 'f: 'd, F1, F2>(
572
    strat: &'d preprocess::Strategy<'f>,
573
    context: &MutContext<'d, 'f>,
Jeff Smits's avatar
Jeff Smits committed
574
    current: ATermRef<'f>,
Jeff Smits's avatar
Jeff Smits committed
575 576
    children_fun: F1,
    self_fun: F2,
Jeff Smits's avatar
Jeff Smits committed
577 578
) -> Result<ATermRef<'f>>
where
579
    F1: Fn(&[ATermRef<'f>],
Jeff Smits's avatar
Jeff Smits committed
580 581 582 583
       &'d preprocess::Strategy<'f>,
       &MutContext<'d, 'f>)
       -> Result<Box<[ATermRef<'f>]>>,
    F2: Fn(ATermRef<'f>) -> Result<ATermRef<'f>>,
Jeff Smits's avatar
Jeff Smits committed
584
{
585
    use factory::Term::*;
Jeff Smits's avatar
Jeff Smits committed
586

Jeff Smits's avatar
Jeff Smits committed
587 588 589
    {
        let annos = current.get_annotations();
        match current.term {
590 591
            Application(name, ref r) => {
                return children_fun(&*r, strat, context)
592
                    .map(|r| context.factory.application(name, r.iter().cloned()))
Jeff Smits's avatar
Jeff Smits committed
593 594 595 596 597
                    .map(|t| {
                        context.factory.with_annos(t, annos.into_iter().cloned())
                    })
            }
            List(ref r) => {
598 599
                return children_fun(&r.iter().collect::<Vec<_>>(), strat, context)
                    .map(|r| context.factory.list(r.iter().cloned()))
Jeff Smits's avatar
Jeff Smits committed
600 601 602
                    .map(|t| {
                        context.factory.with_annos(t, annos.into_iter().cloned())
                    })
Jeff Smits's avatar
Jeff Smits committed
603 604
            }
            Placeholder(_, ref r) => {
605
                return children_fun(&[r.clone()], strat, context)
Jeff Smits's avatar
Jeff Smits committed
606
                    .map(|p| {
607
                        debug_assert_eq!(p.len(), 1);
Jeff Smits's avatar
Jeff Smits committed
608 609 610 611 612 613
                        context.factory.stratego_placeholder(p[0].clone())
                    })
                    .map(|t| {
                        context.factory.with_annos(t, annos.into_iter().cloned())
                    })
            }
Jeff Smits's avatar
Jeff Smits committed
614
            Int(_) | Long(_) | Real(_) | String(_) | Blob(_) => {}
615
        }
616
    }
Jeff Smits's avatar
Jeff Smits committed
617
    self_fun(current)
618 619
}

Jeff Smits's avatar
Jeff Smits committed
620
fn one_rec<'d, 'f: 'd>(
621
    r: &[ATermRef<'f>],
622
    strat: &'d preprocess::Strategy<'f>,
623
    context: &MutContext<'d, 'f>,
Jeff Smits's avatar
Jeff Smits committed
624
) -> Result<Box<[ATermRef<'f>]>> {
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
    let mut result = Vec::with_capacity(r.len());
    let mut iter = r.into_iter();
    let mut success = false;
    while let Some(child) = iter.next() {
        if let Ok(new) = strat.eval(context, child.clone()) {
            result.push(new);
            success = true;
            break;
        } else {
            result.push(child.clone())
        }
    }
    if success {
        result.extend(iter.cloned());
        Ok(result.into_boxed_slice())
    } else {
        Err(Error::StrategyFailed)
    }
643 644
}

Jeff Smits's avatar
Jeff Smits committed
645
fn some_rec<'d, 'f: 'd>(
646
    r: &[ATermRef<'f>],
647
    strat: &'d preprocess::Strategy<'f>,
648
    context: &MutContext<'d, 'f>,
Jeff Smits's avatar
Jeff Smits committed
649
) -> Result<Box<[ATermRef<'f>]>> {
650 651
    let mut result = Vec::with_capacity(r.len());
    let mut success = false;
652
    for child in r {
653 654 655 656 657 658 659 660 661 662 663
        if let Ok(new) = strat.eval(context, child.clone()) {
            result.push(new);
            success = true;
        } else {
            result.push(child.clone())
        }
    }
    if success {
        Ok(result.into_boxed_slice())
    } else {
        Err(Error::StrategyFailed)
664 665 666
    }
}

Jeff Smits's avatar
Jeff Smits committed
667
fn all_rec<'d, 'f: 'd>(
668
    r: &[ATermRef<'f>],
669
    strat: &'d preprocess::Strategy<'f>,
670
    context: &MutContext<'d, 'f>,
Jeff Smits's avatar
Jeff Smits committed
671
) -> Result<Box<[ATermRef<'f>]>> {
672 673 674 675 676
    r.into_iter()
        .cloned()
        .map(|a| strat.eval(context, a))
        .collect::<Result<Vec<_>>>()
        .map(Vec::into_boxed_slice)
677
}
678

Jeff Smits's avatar
Jeff Smits committed
679
pub fn eval_sdef<'d, 'f: 'd>(
680
    strategy_def: &StrategyDef<'d, 'f>,
681
    context: &MutContext<'d, 'f>,
682
    actual_sargs: Vec<StrategyDef<'d, 'f>>,
Jeff Smits's avatar
Jeff Smits committed
683 684 685
    actual_targs: Vec<ATermRef<'f>>,
    current: ATermRef<'f>,
) -> Result<ATermRef<'f>> {
686
    let name = strategy_def.name();
Jeff Smits's avatar
Jeff Smits committed
687
    if !strategy_def.matching_counts(actual_sargs.len(), actual_targs.len()) {
688
        Err(Error::UndefinedStrategy(String::from(name)))
689 690
    } else {
        context.stack_tracer.borrow_mut().push(name);
691
        let popped_scopes = strategy_def.no_of_scopes().map_or_else(Vec::new, |n| {
692 693
            context.scopes.borrow_mut().split_off(n)
        });
694 695
        context.push_scope(strategy_def.build_scope(actual_sargs, actual_targs));
        let result = match *strategy_def {
696
            StrategyDef::TopLevel(def) |
697 698 699
            StrategyDef::Predefined { def, .. } => def.body.eval(context, current),
            StrategyDef::Anonymous { body, .. } => body.eval(context, current),
        };
700 701 702 703 704 705 706 707 708 709 710 711 712
        context.pop_scope();
        context.scopes.borrow_mut().extend_from_slice(
            &popped_scopes,
        );
        if result.is_ok() {
            context.stack_tracer.borrow_mut().pop_on_success();
        } else {
            context.stack_tracer.borrow_mut().pop_on_failure();
        }
        result
    }
}

Jeff Smits's avatar
Jeff Smits committed
713
pub fn strategy_def_from_strategy<'d, 'f: 'd>(
714
    context: &MutContext<'d, 'f>,
715
    name: InternedString<'f>,
716
    number: usize,
717 718
    body: &'d preprocess::Strategy<'f>,
) -> Result<StrategyDef<'d, 'f>> {
719
    use preprocess::Strategy;
720 721 722
    let no_of_scopes = context.scopes.borrow().len();
    // NOTE: this is not an optimisation but a necessary semantic distinction to allow
    // strategies that take arguments to be passed as strategy arguments!
723
    match *body {
724
        Strategy::CallT(name, ref sargs, ref targs) if sargs.is_empty() && targs.is_empty() => {
725 726 727
            context.get_strategy(name)
        }
        _ => {
Jeff Smits's avatar
Jeff Smits committed
728 729 730
            let artificial_name = context.factory.intern_string(
                format!("{}.{}", name, number),
            );
731
            Ok(StrategyDef::Anonymous {
732
                artificial_name: artificial_name,
733 734 735
                body: body,
                no_of_scopes: no_of_scopes,
            })
736 737 738
        }
    }
}