Commit 519dd1ac authored by Jeff Smits's avatar Jeff Smits

Found case where we need scoping of let-defined strategies, as predicted in TODO comment

parent c128b076
This diff is collapsed.
module test73
imports libstratego-lib
strategies
test1 = ( rules( a : _ -> 1 ) /~["a"]\ rules( a : _ -> 0 ) ) ; a
test2 = ( rules( b : _ -> 1 ) /~["b"]\ rules( b : _ -> 1 ) ) ; b
test3 = ( rules( c : _ -> 1 ) /c\ rules( c : _ -> 0 ) ) ; c
test4 = ( rules( d : _ -> 1 ) /d\ rules( d : _ -> 1 ) ) ; d
strategies
main =
test-suite(!"intersection with rulenames",
test-it-now
)
test-it-now =
apply-and-fail(!"intersection with rulenames 1",test1,![])
; apply-test(!"intersection with rulenames 2",test2,![],!1)
; apply-and-fail(!"normal intersection 1",test3,![])
; apply-test(!"normal intersection 2",test4,![],!1)
......@@ -84,7 +84,7 @@ impl<'a> MutContext<'a> {
pub fn get_term(&self, term_name: &str) -> Result<ATermRef> {
self.get_term_option(term_name)
.and_then(|o| o.ok_or(Error::UninitializedVariable))
.and_then(|o| o.ok_or(Error::UninitializedVariable(String::from(term_name))))
}
pub fn match_term(&self, term_name: &str, current: &ATermRef) -> Result<()> {
......@@ -211,6 +211,18 @@ impl<ATerm> Scope<ATerm> {
}
}
pub fn from_let_defs<I>(no_of_scopes: usize, defs: I) -> Scope<ATerm>
where I: IntoIterator<Item = StrategyDef>
{
Scope {
term: HashMap::default(),
strategy: defs.into_iter()
.map(|sdeft| (sdeft.name(), sdeft.set_scope_on_let_sdef(no_of_scopes)))
.collect(),
overlay: false,
}
}
pub fn from_fresh_variables<I>(fresh_vars: I) -> Scope<ATerm>
where I: IntoIterator<Item = String>
{
......
......@@ -16,7 +16,7 @@ pub enum Error {
UndefinedStrategy(String),
UndefinedVariable(String),
UndefinedPrimitive(String),
UninitializedVariable,
UninitializedVariable(String),
StrategyFailed,
UnknownBehaviour(&'static str),
InterpreterExit(i32),
......@@ -32,7 +32,7 @@ impl<'t> error::Error for Error {
Error::UndefinedStrategy(_) => "couldn't find strategy",
Error::UndefinedVariable(_) => "couldn't find variable",
Error::UndefinedPrimitive(_) => "couldn't find primitive",
Error::UninitializedVariable => "variable was used but not bound",
Error::UninitializedVariable(_) => "variable was used but not bound",
Error::StrategyFailed => "strategy failed",
Error::UnknownBehaviour(_) => "unknown behaviour",
Error::InterpreterExit(_) => "interpreter exit",
......@@ -48,7 +48,7 @@ impl<'t> error::Error for Error {
Error::UndefinedStrategy(_) |
Error::UndefinedVariable(_) |
Error::UndefinedPrimitive(_) |
Error::UninitializedVariable |
Error::UninitializedVariable(_) |
Error::StrategyFailed |
Error::UnknownBehaviour(_) |
Error::InterpreterExit(_) => None,
......@@ -66,7 +66,7 @@ impl<'t> fmt::Display for Error {
Error::UndefinedStrategy(ref s) => write!(f, "UndefinedStrategyError: {}", s),
Error::UndefinedVariable(ref s) => write!(f, "UndefinedVariableError: {}", s),
Error::UndefinedPrimitive(ref s) => write!(f, "UndefinedPrimitiveError: {}", s),
Error::UninitializedVariable => write!(f, "Uninitialized variable"),
Error::UninitializedVariable(ref s) => write!(f, "Uninitialized variable: {}", s),
Error::StrategyFailed => write!(f, "Strategy failed"),
Error::UnknownBehaviour(s) => write!(f, "Unknown behaviour for {}", s),
Error::InterpreterExit(i) => write!(f, "Interpreter exited with code {}", i),
......
......@@ -135,8 +135,9 @@ impl<'a> Eval<'a> for preprocess::Strategy {
use context;
match *self {
Let(ref defs, ref body) => {
// TODO: Figure out if sdefts in let need to save a scopes-stack pointer thingy
context.push_scope(context::Scope::from_defs(defs.clone()));
// note the + 1, because lets allow recursive definitions
let no_of_scopes = context.scopes.borrow().len() + 1;
context.push_scope(context::Scope::from_let_defs(no_of_scopes, defs.clone()));
// Don't unwind scopes stack when fatal error occurred? Then again, why make such a
// failure case faster at the cost of many extra branches for the happy path...
let result = body.eval(context, current);
......@@ -144,14 +145,17 @@ impl<'a> Eval<'a> for preprocess::Strategy {
result
}
CallT(ref name, ref sargs, ref targs) => {
println!("Debug. CallT of {}", name);
let sdeft = context.get_strategy(name)?;
let no_of_scopes = context.scopes.borrow().len();
if "z_1480" == name {
println!("Debug. {:?}", sdeft);
}
let sargs = sargs
.into_iter()
.cloned()
.enumerate()
.map(|(n, s)| {
StrategyDef::from_strategy(context, name.clone(), n, s, no_of_scopes)
StrategyDef::from_strategy(context, name.clone(), n, s)
})
.collect::<Result<_>>()?;
let targs = targs
......@@ -162,7 +166,6 @@ impl<'a> Eval<'a> for preprocess::Strategy {
}
CallDynamic(ref term_name, ref sargs, ref targs) => {
let strategy_name = context.get_term(term_name)?;
let no_of_scopes = context.scopes.borrow().len();
let strategy_name = b(&strategy_name)
.get_string()
.ok_or_else(|| {
......@@ -179,8 +182,7 @@ impl<'a> Eval<'a> for preprocess::Strategy {
StrategyDef::from_strategy(context,
strategy_name.clone(),
n,
s,
no_of_scopes)
s)
})
.collect::<Result<_>>()?;
let targs = targs
......@@ -221,13 +223,12 @@ impl<'a> Eval<'a> for preprocess::Strategy {
}
}
PrimT(ref name, ref sargs, ref targs) => {
let no_of_scopes = context.scopes.borrow().len();
let sargs = sargs
.into_iter()
.cloned()
.enumerate()
.map(|(n, s)| {
StrategyDef::from_strategy(context, name.clone(), n, s, no_of_scopes)
StrategyDef::from_strategy(context, name.clone(), n, s)
})
.collect::<Result<_>>()?;
let targs = targs
......
......@@ -45,6 +45,7 @@ pub enum StrategyDef {
sargs: Vec<String>,
targs: Vec<String>,
body: Strategy,
no_of_scopes: Option<usize>,
},
Anonymous {
argument_of: String,
......@@ -67,9 +68,27 @@ impl StrategyDef {
ref sargs,
ref targs,
ref body,
no_of_scopes,
} => {
if actual_sargs.len() != sargs.len() || actual_targs.len() != targs.len() {
Err(Error::UndefinedStrategy(name.clone()))
} else if let Some(no_of_scopes) = no_of_scopes {
context.stack_tracer.borrow_mut().push(name.clone());
let popped_scopes = context.scopes.borrow_mut().split_off(no_of_scopes);
context.push_scope(Scope::new(targs.iter().cloned().zip(actual_targs),
sargs.iter().cloned().zip(actual_sargs)));
let result = body.eval(context, current);
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
} else {
context.stack_tracer.borrow_mut().push(name.clone());
context.push_scope(Scope::new(targs.iter().cloned().zip(actual_targs),
......@@ -114,9 +133,9 @@ impl StrategyDef {
pub fn from_strategy(context: &MutContext,
name: String,
number: usize,
body: Strategy,
no_of_scopes: usize)
body: Strategy)
-> Result<Self> {
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!
match body {
......@@ -135,6 +154,22 @@ impl StrategyDef {
}
}
pub fn set_scope_on_let_sdef(self, no_of_scopes: usize) -> Self {
match self {
StrategyDef::Predefined { name, sargs, targs, body, no_of_scopes: None } => {
StrategyDef::Predefined {
name: name,
sargs: sargs,
targs: targs,
body: body,
no_of_scopes: Some(no_of_scopes),
}
}
_ => panic!("set_scope_on_let_sdef called on already scoped predefined strategy or \
anonymous strategy")
}
}
pub fn name(&self) -> String {
match *self {
StrategyDef::Predefined { ref name, .. } => name.clone(),
......@@ -160,6 +195,7 @@ impl TryFrom<ctree::Def> for StrategyDef {
sargs: sargs.into_iter().map(|vardec| vardec.0).collect(),
targs: targs.into_iter().map(|vardec| vardec.0).collect(),
body: body,
no_of_scopes: None,
}
})
}
......
......@@ -142,6 +142,8 @@ pub fn indexedSet_getIndex<'a>(context: &MutContext<'a>,
Ok(context.factory.int(result as i32))
}
// TODO: libstratego-lib uses a primitive SSL_indexedSet_getElem, which does not exist in the Java implementation!
pub fn indexedSet_elements<'a>(context: &MutContext<'a>,
sargs: Vec<StrategyDef>,
targs: Vec<ATermRef>,
......
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