Commit 6d2973a1 by Jeff Smits

Reproducing the strange non-backtracking behaviour of strj/strc

parent eef19438
Specification([Signature([Constructors([ExtOpDecl("Nil",ConstType(Sort("List",[SortVar("a")])))])]),Strategies([SDefT("main_0_0",[],[],Scope(["b_0"],GuardedLChoice(Seq(Build(Anno(Int("1"),Op("Nil",[]))),Seq(Match(Var("b_0")),Fail)),Id,Seq(CallT(SVar("try_1_0"),[Fail],[]),Seq(Build(Var("b_0")),Seq(CallT(SVar("debug_1_0"),[Build(Anno(Str("c is still bound: "),Op("Nil",[])))],[]),Seq(Build(Anno(Int("2"),Op("Nil",[]))),Seq(Match(Var("b_0")),Seq(CallT(SVar("debug_1_0"),[Build(Anno(Str("But we can rebind it once: "),Op("Nil",[])))],[]),Seq(Build(Anno(Int("3"),Op("Nil",[]))),Seq(CallT(SVar("debug_1_0"),[Build(Anno(Str("Second time it matches again, so here is where we fail: "),Op("Nil",[])))],[]),Match(Var("b_0"))))))))))))),ExtSDef("debug_1_0",[VarDec("o_13",FunType([ConstType(Sort("ATerm",[]))],ConstType(Sort("ATerm",[]))))],[]),ExtSDef("try_1_0",[VarDec("w_22",FunType([ConstType(Sort("ATerm",[]))],ConstType(Sort("ATerm",[]))))],[])])])
\ No newline at end of file
module observable_non_backtracking
imports libstratego-lib
strategies
main = !1;?c;fail <+ try(fail;?c);!c;debug(!"c is still bound: ");!2;?c;debug(!"But we can rebind it once: ");!3;debug(!"Second time it matches again, so here is where we fail: ");?c
......@@ -4,7 +4,7 @@ use preprocess::{Def, StrategyDef};
use primitives::{Primitives, eval_prim_ref};
use primitives::ssl::State;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::borrow::Borrow;
use std::cell::RefCell;
......@@ -13,7 +13,7 @@ use std::cell::RefCell;
/// This context is internally mutable in `RefCell` fields. This is just for convenience as we don't
/// want to pass around the mutable fields separately. But the internal mutability is not a hidden
/// thing, there is observable effect, so the name of the context includes `Mut`.
pub struct MutContext<'d, 'f : 'd> {
pub struct MutContext<'d, 'f: 'd> {
pub stack_tracer: RefCell<StackTracer>,
pub scopes: RefCell<Vec<Scope<'d, ATermRef>>>,
pub factory: &'f ATermFactory,
......@@ -21,7 +21,7 @@ pub struct MutContext<'d, 'f : 'd> {
pub ssl_state: RefCell<State>,
}
impl<'d, 'f : 'd> MutContext<'d, 'f> {
impl<'d, 'f: 'd> MutContext<'d, 'f> {
pub fn new(
factory: &'f ATermFactory,
scopes: Vec<Scope<'d, ATermRef>>,
......@@ -59,7 +59,6 @@ impl<'d, 'f : 'd> MutContext<'d, 'f> {
result
}
// Should really return a &SDef, but I can't figure out lifetimes that borrowck will accept :(
pub fn get_strategy(&self, strat_name: &str) -> Result<StrategyDef<'d>> {
self.scopes
.borrow()
......@@ -71,12 +70,8 @@ impl<'d, 'f : 'd> MutContext<'d, 'f> {
.ok_or_else(|| Error::UndefinedStrategy(strat_name.to_owned()))
}
fn get_term_option(&self, term_name: &str) -> Result<Option<ATermRef>> {
Scopes::get_term_option(&self.scopes.borrow(), term_name)
}
pub fn get_term(&self, term_name: &str) -> Result<ATermRef> {
self.get_term_option(term_name).and_then(|o| {
Scopes::get_term_option_ignore_invalidation(&self.scopes.borrow(), term_name).and_then(|o| {
o.ok_or(Error::StrategyFailed)
})
}
......@@ -93,7 +88,7 @@ impl<'d, 'f : 'd> MutContext<'d, 'f> {
let overlay = Scope {
term: HashMap::new(),
strategy: HashMap::new(),
is_overlay: true,
kind: ScopeKind::Overlay,
};
self.push_scope(overlay);
......@@ -126,8 +121,8 @@ impl<'d, 'f : 'd> MutContext<'d, 'f> {
"apply_overlay: Interpreter bug, unexpected end of stack",
);
debug_assert!(
scope.is_overlay,
"apply_overlay: Interpreter bug, unexpected normal scope"
scope.kind == ScopeKind::Overlay,
"apply_overlay: Interpreter bug, unexpected non-overlay scope"
);
self._apply_overlay(scope);
}
......@@ -137,17 +132,30 @@ impl<'d, 'f : 'd> MutContext<'d, 'f> {
"drop_overlay: Interpreter bug, unexpected end of stack",
);
debug_assert!(
popped_scope.is_overlay,
popped_scope.kind != ScopeKind::Normal,
"drop_overlay: Interpreter bug, unexpected normal scope"
);
}
pub fn invalidate_overlay(&self) {
let mut scopes = self.scopes.borrow_mut();
let mut last_scope_ref = scopes.last_mut().expect(
"invalidate_overlay: Interpreter bug, unexpected end of stack",
);
debug_assert!(
last_scope_ref.kind == ScopeKind::Overlay,
"invalidate_overlay: Interpreter bug, unexpected non-overlay scope"
);
last_scope_ref.kind =
ScopeKind::InvalidatedOverlay(last_scope_ref.term.keys().cloned().collect());
}
pub fn pop_scope(&self) {
let scope = self.scopes.borrow_mut().pop().expect(
"pop_scope: Interpreter bug, unexpected end of stack",
);
debug_assert!(
!scope.is_overlay,
scope.kind == ScopeKind::Normal,
"pop_scope: Interpreter bug, unexpected overlay"
);
}
......@@ -193,11 +201,24 @@ impl fmt::Display for StackTracer {
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum ScopeKind {
Normal,
Overlay,
InvalidatedOverlay(HashSet<String>),
}
impl Default for ScopeKind {
fn default() -> Self {
ScopeKind::Normal
}
}
#[derive(Debug, Default, Clone)]
pub struct Scope<'a, ATerm> {
pub term: HashMap<String, Option<ATerm>>,
pub strategy: HashMap<String, StrategyDef<'a>>,
is_overlay: bool,
kind: ScopeKind,
}
impl<'a, ATerm> Scope<'a, ATerm> {
......@@ -209,7 +230,7 @@ impl<'a, ATerm> Scope<'a, ATerm> {
Scope {
term: terms.into_iter().map(|(n, a)| (n, Some(a))).collect(),
strategy: defs.into_iter().collect(),
is_overlay: false,
kind: ScopeKind::Normal,
}
}
......@@ -222,20 +243,25 @@ impl<'a, ATerm> Scope<'a, ATerm> {
strategy: defs.into_iter()
.map(|def| (def.name.clone(), StrategyDef::from_def(def)))
.collect(),
is_overlay: false,
kind: ScopeKind::Normal,
}
}
pub fn from_let_defs<I>(no_of_scopes: usize, defs: I) -> Self
where
I: IntoIterator<Item=&'a Def>,
I: IntoIterator<Item = &'a Def>,
{
Scope {
term: HashMap::default(),
strategy: defs.into_iter()
.map(|def| (def.name.clone(), StrategyDef::from_let_def(def, no_of_scopes)))
.map(|def| {
(
def.name.clone(),
StrategyDef::from_let_def(def, no_of_scopes),
)
})
.collect(),
is_overlay: false,
kind: ScopeKind::Normal,
}
}
......@@ -254,7 +280,7 @@ impl<'a, ATerm> Scope<'a, ATerm> {
result
},
strategy: HashMap::default(),
is_overlay: false,
kind: ScopeKind::Normal,
}
}
}
......@@ -263,7 +289,7 @@ impl<'a, ATerm> Scope<'a, ATerm> {
pub mod Scopes {
use super::*;
pub fn get_term_option(
pub fn get_term_option_ignore_invalidation(
vec: &Vec<Scope<ATermRef>>,
term_name: &str,
) -> Result<Option<ATermRef>> {
......@@ -275,6 +301,25 @@ pub mod Scopes {
.ok_or_else(|| Error::UndefinedVariable(term_name.to_owned()))
}
pub fn get_term_option(
vec: &Vec<Scope<ATermRef>>,
term_name: &str,
) -> Result<Option<ATermRef>> {
vec.iter()
.rev()
.flat_map(|scope| {
if let ScopeKind::InvalidatedOverlay(ref set) = scope.kind {
if set.contains(term_name) {
return None;
}
}
scope.term.get(term_name)
})
.cloned()
.next()
.ok_or_else(|| Error::UndefinedVariable(term_name.to_owned()))
}
pub fn match_term(
scopes: &mut Vec<Scope<ATermRef>>,
term_name: &str,
......@@ -291,21 +336,37 @@ pub mod Scopes {
}
None => {
for mut scope in scopes.iter_mut().rev() {
if scope.is_overlay || scope.term.contains_key(term_name) {
if let Some(Some(t)) = scope.term.insert(
let condition = match scope.kind {
ScopeKind::Normal => scope.term.contains_key(term_name),
ScopeKind::Overlay => true,
ScopeKind::InvalidatedOverlay(ref set) => set.contains(term_name),
};
if condition {
let insert_result = scope.term.insert(
term_name.to_owned(),
Some(current.clone()),
)
{
unreachable!(format!(
"match_term: No scope had {}, but we just \
replaced {} when we added it?!",
term_name,
t
))
} else {
return Ok(());
);
debug_assert!(
match scope.kind {
ScopeKind::Normal => insert_result == Some(None),
ScopeKind::Overlay => insert_result == None,
ScopeKind::InvalidatedOverlay(_) => {
match insert_result {
Some(Some(_)) => true,
_ => false,
}
}
},
"match_term: Interpreter bug, unexpected binding to term_name in scope"
);
if let ScopeKind::InvalidatedOverlay(ref mut set) = scope.kind {
let remove_result = set.remove(term_name);
debug_assert!(
remove_result == true,
"match_term: Interpreter bug, term_name was not in invalidation set"
);
}
return Ok(());
}
}
}
......
......@@ -127,7 +127,7 @@ pub trait Eval<'d, 'f> {
fn eval(&'d self, context: &MutContext<'d, 'f>, current: ATermRef) -> Result<ATermRef>;
}
impl<'d, 'f : 'd> Eval<'d, 'f> for preprocess::Strategy {
impl<'d, 'f: 'd> Eval<'d, 'f> for preprocess::Strategy {
fn eval(&'d self, context: &MutContext<'d, 'f>, current: ATermRef) -> Result<ATermRef> {
use preprocess::Strategy::*;
use context;
......@@ -210,9 +210,10 @@ impl<'d, 'f : 'd> Eval<'d, 'f> for preprocess::Strategy {
s_then.eval(context, current)
}
Err(Error::StrategyFailed) => {
// TODO: pull off the overlay, apply it after the s_else branch, but only where the s_else branch left variables unbound. This is the semantics that strc and strj use.
context.drop_overlay();
s_else.eval(context, current)
context.invalidate_overlay();
let result = s_else.eval(context, current);
context.apply_overlay();
result
}
Err(e) => {
context.drop_overlay();
......@@ -253,7 +254,7 @@ impl<'d, 'f : 'd> Eval<'d, 'f> for preprocess::Strategy {
}
}
fn eval_match<'d, 'f : 'd>(
fn eval_match<'d, 'f: 'd>(
context: &MutContext<'d, 'f>,
match_term: &preprocess::MatchTerm,
current: &ATermRef,
......@@ -371,7 +372,7 @@ fn eval_match<'d, 'f : 'd>(
}
}
fn eval_some<'d, 'f : 'd>(
fn eval_some<'d, 'f: 'd>(
strat: &'d preprocess::Strategy,
context: &MutContext<'d, 'f>,
current: ATermRef,
......@@ -405,7 +406,7 @@ fn eval_some<'d, 'f : 'd>(
}
}
fn eval_one<'d, 'f : 'd>(
fn eval_one<'d, 'f: 'd>(
strat: &'d preprocess::Strategy,
context: &MutContext<'d, 'f>,
current: ATermRef,
......@@ -439,7 +440,7 @@ fn eval_one<'d, 'f : 'd>(
}
}
fn eval_all<'d, 'f : 'd>(
fn eval_all<'d, 'f: 'd>(
strat: &'d preprocess::Strategy,
context: &MutContext<'d, 'f>,
current: ATermRef,
......@@ -473,7 +474,7 @@ fn eval_all<'d, 'f : 'd>(
}
}
fn one_rec<'d, 'f : 'd>(
fn one_rec<'d, 'f: 'd>(
r: &Box<[ATermRef]>,
strat: &'d preprocess::Strategy,
context: &MutContext<'d, 'f>,
......@@ -498,7 +499,7 @@ fn one_rec<'d, 'f : 'd>(
}
}
fn some_rec<'d, 'f : 'd>(
fn some_rec<'d, 'f: 'd>(
r: &Box<[ATermRef]>,
strat: &'d preprocess::Strategy,
context: &MutContext<'d, 'f>,
......@@ -520,7 +521,7 @@ fn some_rec<'d, 'f : 'd>(
}
}
fn all_rec<'d, 'f : 'd>(
fn all_rec<'d, 'f: 'd>(
r: &Box<[ATermRef]>,
strat: &'d preprocess::Strategy,
context: &MutContext<'d, 'f>,
......@@ -532,7 +533,7 @@ fn all_rec<'d, 'f : 'd>(
.map(Vec::into_boxed_slice)
}
pub fn eval_sdef<'d, 'f : 'd>(
pub fn eval_sdef<'d, 'f: 'd>(
strategy_def: &StrategyDef<'d>,
context: &MutContext<'d, 'f>,
actual_sargs: Vec<StrategyDef<'d>>,
......@@ -540,11 +541,7 @@ pub fn eval_sdef<'d, 'f : 'd>(
current: ATermRef,
) -> Result<ATermRef> {
let name = strategy_def.name();
if !strategy_def.matching_counts(
actual_sargs.len(),
actual_targs.len(),
)
{
if !strategy_def.matching_counts(actual_sargs.len(), actual_targs.len()) {
Err(Error::UndefinedStrategy(name))
} else {
context.stack_tracer.borrow_mut().push(name);
......@@ -570,7 +567,7 @@ pub fn eval_sdef<'d, 'f : 'd>(
}
}
pub fn strategy_def_from_strategy<'d, 'f : 'd>(
pub fn strategy_def_from_strategy<'d, 'f: 'd>(
context: &MutContext<'d, 'f>,
name: &str,
number: usize,
......
......@@ -30,10 +30,7 @@ pub fn preprocess<'a>(program: ctree::Module) -> Result<Vec<Def>> {
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum StrategyDef<'a> {
TopLevel(&'a Def),
Predefined {
def: &'a Def,
no_of_scopes: usize,
},
Predefined { def: &'a Def, no_of_scopes: usize },
Anonymous {
argument_of: String,
argument_no: usize,
......@@ -53,12 +50,12 @@ impl<'a> StrategyDef<'a> {
pub fn matching_counts(&self, c_sargs: usize, c_targs: usize) -> bool {
match *self {
StrategyDef::TopLevel(ref def) =>
def.sargs.len() == c_sargs && def.targs.len() == c_targs,
StrategyDef::Predefined {
def,
..
} => def.sargs.len() == c_sargs && def.targs.len() == c_targs,
StrategyDef::TopLevel(ref def) => {
def.sargs.len() == c_sargs && def.targs.len() == c_targs
}
StrategyDef::Predefined { def, .. } => {
def.sargs.len() == c_sargs && def.targs.len() == c_targs
}
StrategyDef::Anonymous { .. } => c_sargs == 0 && c_targs == 0,
}
}
......@@ -75,10 +72,7 @@ impl<'a> StrategyDef<'a> {
def.sargs.iter().cloned().zip(actual_sargs),
)
}
StrategyDef::Predefined {
def,
..
} => {
StrategyDef::Predefined { def, .. } => {
Scope::new(
def.targs.iter().cloned().zip(actual_targs),
def.sargs.iter().cloned().zip(actual_sargs),
......@@ -129,7 +123,7 @@ impl TryFrom<ctree::Def> for Def {
body.try_into().map(|body| {
Def {
name: name,
sargs: sargs.into_iter().map( | vardec| vardec.0).collect(),
sargs: sargs.into_iter().map(|vardec| vardec.0).collect(),
targs: targs.into_iter().map(|vardec| vardec.0).collect(),
body: body,
}
......@@ -294,11 +288,11 @@ impl TryFrom<ctree::PreTerm> for BuildTerm {
}
impl BuildTerm {
pub fn build<'d, 'f : 'd>(&self, context: &MutContext<'d, 'f>) -> Result<ATermRef> {
pub fn build<'d, 'f: 'd>(&self, context: &MutContext<'d, 'f>) -> Result<ATermRef> {
self.build_with_annos(context, None)
}
fn build_with_annos<'d, 'f : 'd>(
fn build_with_annos<'d, 'f: 'd>(
&self,
context: &MutContext<'d, 'f>,
annos: Option<Vec<ATermRef>>,
......@@ -408,7 +402,10 @@ impl BuildTerm {
}
/// Lifts `build` over a vector
fn build_vec<'d, 'f : 'd>(context: &MutContext<'d, 'f>, vec: &[BuildTerm]) -> Result<Vec<ATermRef>> {
fn build_vec<'d, 'f: 'd>(
context: &MutContext<'d, 'f>,
vec: &[BuildTerm],
) -> Result<Vec<ATermRef>> {
vec.into_iter().map(|t| t.build(context)).collect()
}
......@@ -490,7 +487,7 @@ impl TryFrom<ctree::PreTerm> for MatchTerm {
}
pub fn try_into_vec<T1, T2: TryFrom<T1>, I: IntoIterator<Item=T1>>(
pub fn try_into_vec<T1, T2: TryFrom<T1>, I: IntoIterator<Item = T1>>(
v: I,
) -> result::Result<Vec<T2>, <T2 as TryFrom<T1>>::Err> {
v.into_iter()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment