Commit 3c725ab2 by Jeff Smits

Refactored StrategyDef, seems to have slight positive effect on performance

parent b115f9b9
......@@ -233,8 +233,9 @@ impl<ATerm> Scope<ATerm> {
Scope {
term: HashMap::default(),
strategy: defs.into_iter()
.map(|sdeft| {
(sdeft.name(), sdeft.set_scope_on_let_sdef(no_of_scopes))
.map(|mut sdeft| {
sdeft.set_scope_on_let_sdef(no_of_scopes);
(sdeft.name(), sdeft)
})
.collect(),
is_overlay: false,
......
......@@ -3,6 +3,7 @@ use context::{MutContext, StackTracer};
use error::{Result, Error};
use factory::{ATermFactory, ATerm, ATermRef};
use preprocess;
use preprocess::Strategy;
use preprocess::{StrategyDef, preprocess};
use primitives::Primitives;
......@@ -118,7 +119,7 @@ pub fn interpret<'a>(
Ok(v) => v,
Err(e) => return Err(TracedError(e, context.stack_tracer.into_inner())),
};
match main.eval(&context, Vec::new(), Vec::new(), input) {
match eval_sdef(&main, &context, Vec::new(), Vec::new(), input) {
Ok(v) => Ok(v),
Err(e) => Err(TracedError(e, context.stack_tracer.into_inner())),
}
......@@ -130,7 +131,7 @@ pub trait Eval<'a> {
impl<'a> Eval<'a> for StrategyDef {
fn eval(&self, context: &MutContext<'a>, current: ATermRef) -> Result<ATermRef> {
self.eval(context, Vec::new(), Vec::new(), current)
eval_sdef(self, context, Vec::new(), Vec::new(), current)
}
}
......@@ -153,15 +154,13 @@ impl<'a> Eval<'a> for preprocess::Strategy {
.into_iter()
.cloned()
.enumerate()
.map(|(n, s)| {
StrategyDef::from_strategy(context, name.clone(), n, s)
})
.map(|(n, s)| strategy_def_from_strategy(context, name, n, s))
.collect::<Result<_>>()?;
let targs = targs
.into_iter()
.map(|build_term| build_term.build(context))
.collect::<Result<_>>()?;
sdeft.eval(context, sargs, targs, current)
eval_sdef(&sdeft, context, sargs, targs, current)
}
CallDynamic(ref term_name, ref sargs, ref targs) => {
let strategy_name = context.get_term(term_name)?;
......@@ -178,14 +177,14 @@ impl<'a> Eval<'a> for preprocess::Strategy {
.cloned()
.enumerate()
.map(|(n, s)| {
StrategyDef::from_strategy(context, strategy_name.clone(), n, s)
strategy_def_from_strategy(context, &strategy_name, n, s)
})
.collect::<Result<_>>()?;
let targs = targs
.into_iter()
.map(|build_term| build_term.build(context))
.collect::<Result<_>>()?;
sdeft.eval(context, sargs, targs, current)
eval_sdef(&sdeft, context, sargs, targs, current)
}
Fail => Err(Error::StrategyFailed),
Id => Ok(current),
......@@ -235,9 +234,7 @@ impl<'a> Eval<'a> for preprocess::Strategy {
.into_iter()
.cloned()
.enumerate()
.map(|(n, s)| {
StrategyDef::from_strategy(context, name.clone(), n, s)
})
.map(|(n, s)| strategy_def_from_strategy(context, name, n, s))
.collect::<Result<_>>()?;
let targs = targs
.into_iter()
......@@ -544,3 +541,61 @@ fn all_rec<'a>(
.collect::<Result<Vec<_>>>()
.map(Vec::into_boxed_slice)
}
pub fn eval_sdef<'a>(
strategy_def: &StrategyDef,
context: &MutContext<'a>,
actual_sargs: Vec<StrategyDef>,
actual_targs: Vec<ATermRef>,
current: ATermRef,
) -> Result<ATermRef> {
let name = strategy_def.name();
if !strategy_def.inner.matching_counts(
actual_sargs.len(),
actual_targs.len(),
)
{
Err(Error::UndefinedStrategy(name))
} else {
context.stack_tracer.borrow_mut().push(name);
let popped_scopes = strategy_def.no_of_scopes.map_or_else(Vec::new, |n| {
context.scopes.borrow_mut().split_off(n)
});
context.push_scope(strategy_def.inner.build_scope(actual_sargs, actual_targs));
let result = strategy_def.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
}
}
pub fn strategy_def_from_strategy(
context: &MutContext,
name: &str,
number: usize,
body: Strategy,
) -> Result<StrategyDef> {
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 {
Strategy::CallT(ref name, ref sargs, ref targs) if sargs.is_empty() && targs.is_empty() => {
context.get_strategy(name)
}
_ => {
Ok(StrategyDef::anonymous(
name.to_owned(),
number,
body,
no_of_scopes,
))
}
}
}
......@@ -2,7 +2,6 @@ use context::{MutContext, Scope};
use ctree;
use error::{Result, Error};
use factory::{ATermFactory, ATermRef};
use interpreter::Eval;
use aterm::BrokenF32;
use aterm::{ATerm as A, ATermFactory as ATF};
......@@ -29,166 +28,112 @@ pub fn preprocess(program: ctree::Module) -> Result<Scope<ATermRef>> {
Ok(Scope::from_defs(defs))
}
// TODO: Make fields public so the eval code can be moved to the interpreter module
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum StrategyDef {
pub struct StrategyDef {
pub body: Strategy,
pub no_of_scopes: Option<usize>,
pub inner: StrategyDefInner,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum StrategyDefInner {
Predefined {
name: String,
sargs: Vec<String>,
targs: Vec<String>,
body: Strategy,
no_of_scopes: Option<usize>,
},
Anonymous {
argument_of: String,
argument_no: usize,
body: Strategy,
no_of_scopes: usize,
},
}
impl StrategyDef {
// TODO: refactor StrategyDef to remove code duplication below.
pub fn eval<'a>(
&self,
context: &MutContext<'a>,
actual_sargs: Vec<StrategyDef>,
actual_targs: Vec<ATermRef>,
current: ATermRef,
) -> Result<ATermRef> {
impl StrategyDefInner {
pub fn matching_counts(&self, c_sargs: usize, c_targs: usize) -> bool {
match *self {
StrategyDef::Predefined {
ref name,
StrategyDefInner::Predefined {
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 {
// This is a closure, we need to keep only the number of scopes from when the
// closure was made.
// This works because closure cannot be returned, so the number of scopes always
// reflects the environment in which the closure was made.
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),
sargs.iter().cloned().zip(actual_sargs),
));
let result = body.eval(context, current);
context.pop_scope();
if result.is_ok() {
context.stack_tracer.borrow_mut().pop_on_success();
} else {
context.stack_tracer.borrow_mut().pop_on_failure();
}
result
}
}
StrategyDef::Anonymous {
ref body,
no_of_scopes,
..
} => {
let name = self.name();
if !(actual_sargs.is_empty() && actual_targs.is_empty()) {
Err(Error::UndefinedStrategy(name))
} else {
context.stack_tracer.borrow_mut().push(name);
let popped_scopes = context.scopes.borrow_mut().split_off(no_of_scopes);
let result = body.eval(context, current);
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
}
}
}
}
pub fn from_strategy(
context: &MutContext,
name: String,
number: 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 {
Strategy::CallT(ref name, ref sargs, ref targs)
if sargs.is_empty() && targs.is_empty() => context.get_strategy(name),
_ => {
Ok(StrategyDef::Anonymous {
argument_of: name,
argument_no: number,
body: body,
no_of_scopes: no_of_scopes,
})
}
} => sargs.len() == c_sargs && targs.len() == c_targs,
StrategyDefInner::Anonymous { .. } => c_sargs == 0 && c_targs == 0,
}
}
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,
pub fn build_scope<ATerm>(
&self,
actual_sargs: Vec<StrategyDef>,
actual_targs: Vec<ATerm>,
) -> Scope<ATerm> {
match *self {
StrategyDefInner::Predefined {
ref sargs,
ref targs,
..
} => {
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"
Scope::new(
targs.iter().cloned().zip(actual_targs),
sargs.iter().cloned().zip(actual_sargs),
)
}
StrategyDefInner::Anonymous { .. } => Scope::new(Vec::new(), Vec::new()),
}
}
}
impl StrategyDef {
pub fn set_scope_on_let_sdef(&mut self, no_of_scopes: usize) {
debug_assert!(
self.no_of_scopes.is_none(),
"set_scope_on_let_sdef called on already scoped predefined strategy or anonymous strategy"
);
self.no_of_scopes = Some(no_of_scopes);
}
pub fn name(&self) -> String {
match *self {
StrategyDef::Predefined { ref name, .. } => name.clone(),
StrategyDef::Anonymous {
match self.inner {
StrategyDefInner::Predefined { ref name, .. } => name.clone(),
StrategyDefInner::Anonymous {
ref argument_of,
ref argument_no,
..
} => format!("{}.{}", argument_of, argument_no),
}
}
pub fn predefined(
name: String,
sargs: Vec<String>,
targs: Vec<String>,
body: Strategy,
no_of_scopes: Option<usize>,
) -> Self {
StrategyDef {
body: body,
no_of_scopes: no_of_scopes,
inner: StrategyDefInner::Predefined {
name: name,
sargs: sargs,
targs: targs,
},
}
}
pub fn anonymous(
argument_of: String,
argument_no: usize,
body: Strategy,
no_of_scopes: usize,
) -> Self {
StrategyDef {
body: body,
no_of_scopes: Some(no_of_scopes),
inner: StrategyDefInner::Anonymous {
argument_of: argument_of,
argument_no: argument_no,
},
}
}
}
impl TryFrom<ctree::Def> for StrategyDef {
......@@ -198,13 +143,13 @@ impl TryFrom<ctree::Def> for StrategyDef {
match value {
ctree::Def::SDefT(name, sargs, targs, body) => {
body.try_into().map(|body| {
StrategyDef::Predefined {
name: name,
sargs: sargs.into_iter().map(|vardec| vardec.0).collect(),
targs: targs.into_iter().map(|vardec| vardec.0).collect(),
body: body,
no_of_scopes: None,
}
Self::predefined(
name,
sargs.into_iter().map(|vardec| vardec.0).collect(),
targs.into_iter().map(|vardec| vardec.0).collect(),
body,
None,
)
})
}
ctree::Def::ExtSDef(_, _, _) => Err(Error::CTreeParse("Unknown behaviour: ExtSDef")),
......
......@@ -2,6 +2,7 @@ use context::MutContext;
use error::{Error, Result};
use factory::{Blob, HashTable, ATermRef};
use preprocess::StrategyDef;
use interpreter::eval_sdef;
use aterm::{ATermFactory as ATF, ATerm as A};
......@@ -250,7 +251,8 @@ pub fn table_fold<'a>(
let table: &RefCell<HashTable> = table.borrow();
for (key, value) in table.borrow().iter() {
current = sargs[0].eval(
current = eval_sdef(
&sargs[0],
context,
Vec::new(),
vec![key.clone(), value.clone()],
......@@ -287,12 +289,7 @@ pub fn table_keys_fold<'a>(
let table: &RefCell<HashTable> = table.borrow();
for key in table.borrow().keys() {
current = sargs[0].eval(
context,
Vec::new(),
vec![key.clone()],
current,
)?;
current = eval_sdef(&sargs[0], context, Vec::new(), vec![key.clone()], current)?;
}
Ok(current)
......@@ -324,12 +321,7 @@ pub fn table_values_fold<'a>(
let table: &RefCell<HashTable> = table.borrow();
for value in table.borrow().values() {
current = sargs[0].eval(
context,
Vec::new(),
vec![value.clone()],
current,
)?;
current = eval_sdef(&sargs[0], context, Vec::new(), vec![value.clone()], current)?;
}
Ok(current)
......
......@@ -2,6 +2,7 @@ use context::MutContext;
use error::{Error, Result};
use factory::{IndexedSet, ATermRef};
use preprocess::StrategyDef;
use interpreter::eval_sdef;
use aterm::{ATermFactory as ATF, ATerm as A};
......@@ -94,7 +95,8 @@ pub fn indexedSet_put<'a>(
let mut set: RefMut<IndexedSet> = set.borrow_mut();
match set.get(key) {
Some(&index) => {
sargs[0].eval(
eval_sdef(
&sargs[0],
context,
Vec::new(),
Vec::new(),
......
......@@ -2,6 +2,7 @@ use context::MutContext;
use error::{Error, Result};
use factory::ATermRef;
use preprocess::StrategyDef;
use interpreter::eval_sdef;
use aterm::ATermFactory as ATF;
use aterm::ATerm;
......@@ -38,7 +39,7 @@ pub fn list_loop<'a>(
let list = targs[0].get_list().ok_or(Error::StrategyFailed)?;
for t in list {
let _ = sargs[0].eval(context, Vec::new(), Vec::new(), t.clone())?;
let _ = eval_sdef(&sargs[0], context, Vec::new(), Vec::new(), t.clone())?;
}
Ok(current)
......@@ -62,12 +63,7 @@ pub fn list_fold<'a>(
let mut current = targs[0].clone();
for t in list {
current = sargs[0].eval(
context,
Vec::new(),
vec![t.clone()],
t.clone(),
)?;
current = eval_sdef(&sargs[0], context, Vec::new(), vec![t.clone()], t.clone())?;
}
Ok(current)
......
......@@ -2,6 +2,7 @@ use context::MutContext;
use error::{Error, Result};
use factory::ATermRef;
use preprocess::StrategyDef;
use interpreter::eval_sdef;
use aterm::{Term, ATermFactory as ATF, ATerm as A};
use aterm::print::ATermWrite;
......@@ -153,7 +154,7 @@ pub fn get_appl_arguments_map<'a>(
let list = targs[0].get_list().ok_or(Error::StrategyFailed)?;
let mut result = Vec::with_capacity(list.len());
for (n, term) in list.into_iter().enumerate() {
result[n] = sargs[0].eval(context, Vec::new(), Vec::new(), term.clone())?;
result[n] = eval_sdef(&sargs[0], context, Vec::new(), Vec::new(), term.clone())?;
}
Ok(context.factory.list(result))
}
......@@ -240,12 +241,7 @@ pub fn preserve_annotations_attachments<'a>(
));
}
// TODO: Support attachments
let after = sargs[0].eval(
context,
Vec::new(),
Vec::new(),
current.clone(),
)?;
let after = eval_sdef(&sargs[0], context, Vec::new(), Vec::new(), current.clone())?;
Ok(context.factory.with_annos(
b(&after).as_inner().term.clone(),
b(&current).as_inner().annotations.iter().cloned(),
......
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