REFACTOR: Add a little more abstraction for map and filter.

* Clearer and easier to follow code.
* Smaller functions.
* Easier to add future functionality.
This commit is contained in:
Jeremy Wall 2019-01-06 15:45:00 -06:00
parent d40f30dcf9
commit ef01f166b2
3 changed files with 59 additions and 41 deletions

View File

@ -79,8 +79,10 @@ fn test_binary_operator_precedence() {
} }
#[test] #[test]
fn test_list_operations() { fn test_functional_processing() {
assert_build(include_str!("../../integration_tests/list_ops_test.ucg")); assert_build(include_str!(
"../../integration_tests/functional_processing_test.ucg"
));
} }
#[test] #[test]

View File

@ -1112,49 +1112,65 @@ impl<'a> FileBuilder<'a> {
} }
} }
fn eval_list_op(&mut self, def: &ListOpDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> { fn eval_functional_list_processing(
let maybe_list = self.eval_expr(&def.target, scope)?; &mut self,
let l = match maybe_list.as_ref() { elems: &Vec<Rc<Val>>,
&Val::List(ref elems) => elems, def: &MacroDef,
other => { outfield: &PositionedItem<String>,
return Err(Box::new(error::BuildError::new( typ: &ListOpType,
format!("Expected List as target but got {:?}", other.type_name()), ) -> Result<Rc<Val>, Box<dyn Error>> {
error::ErrorType::TypeFail, let mut out = Vec::new();
def.target.pos().clone(), for item in elems.iter() {
))); let argvals = vec![item.clone()];
} let fields = def.eval(self.file.clone(), self, argvals)?;
}; if let Some(v) = find_in_fieldlist(&outfield.val, &fields) {
let mac_sym = Value::Symbol(def.mac.clone()); match typ {
if let &Val::Macro(ref macdef) = self.eval_value(&mac_sym, &self.scope.clone())?.as_ref() { ListOpType::Map => {
let mut out = Vec::new(); out.push(v.clone());
for item in l.iter() { }
let argvals = vec![item.clone()]; ListOpType::Filter => {
let fields = macdef.eval(self.file.clone(), self, argvals)?; if let &Val::Empty = v.as_ref() {
if let Some(v) = find_in_fieldlist(&def.field.val, &fields) { // noop
match def.typ { continue;
ListOpType::Map => { } else if let &Val::Boolean(false) = v.as_ref() {
out.push(v.clone()); // noop
} continue;
ListOpType::Filter => {
if let &Val::Empty = v.as_ref() {
// noop
continue;
} else if let &Val::Boolean(false) = v.as_ref() {
// noop
continue;
}
out.push(item.clone());
} }
out.push(item.clone());
} }
} }
} }
return Ok(Rc::new(Val::List(out)));
} }
return Err(Box::new(error::BuildError::new( return Ok(Rc::new(Val::List(out)));
format!("Expected macro but got {:?}", def.mac), }
error::ErrorType::TypeFail,
def.pos.clone(), fn eval_functional_processing(
))); &mut self,
def: &ListOpDef,
scope: &Scope,
) -> Result<Rc<Val>, Box<dyn Error>> {
let maybe_list = self.eval_expr(&def.target, scope)?;
let maybe_mac = self.eval_value(&Value::Symbol(def.mac.clone()), &self.scope.clone())?;
let macdef = match maybe_mac.as_ref() {
&Val::Macro(ref macdef) => macdef,
_ => {
return Err(Box::new(error::BuildError::new(
format!("Expected macro but got {:?}", def.mac),
error::ErrorType::TypeFail,
def.pos.clone(),
)));
}
};
return match maybe_list.as_ref() {
&Val::List(ref elems) => {
self.eval_functional_list_processing(elems, macdef, &def.field, &def.typ)
}
other => Err(Box::new(error::BuildError::new(
format!("Expected List as target but got {:?}", other.type_name()),
error::ErrorType::TypeFail,
def.target.pos().clone(),
))),
};
} }
fn build_assert(&mut self, tok: &Token) -> Result<Rc<Val>, Box<dyn Error>> { fn build_assert(&mut self, tok: &Token) -> Result<Rc<Val>, Box<dyn Error>> {
@ -1278,7 +1294,7 @@ impl<'a> FileBuilder<'a> {
&Expression::Macro(ref def) => self.eval_macro_def(def), &Expression::Macro(ref def) => self.eval_macro_def(def),
&Expression::Module(ref def) => self.eval_module_def(def, scope), &Expression::Module(ref def) => self.eval_module_def(def, scope),
&Expression::Select(ref def) => self.eval_select(def, scope), &Expression::Select(ref def) => self.eval_select(def, scope),
&Expression::ListOp(ref def) => self.eval_list_op(def, scope), &Expression::ListOp(ref def) => self.eval_functional_processing(def, scope),
&Expression::Include(ref def) => self.eval_include(def), &Expression::Include(ref def) => self.eval_include(def),
} }
} }