REFACTOR: Remove builtin map and filter.

Work in progress.
This commit is contained in:
Jeremy Wall 2019-01-30 20:38:17 -06:00
parent 4d6fd87c3d
commit 160749ae5b
7 changed files with 85 additions and 181 deletions

View File

@ -1,4 +1,5 @@
// List processing
let map = import "std/functional.ucg".map;
let list1 = [1, 2, 3, 4];
let list2 = ["foo", "bar", "foo", "bar"];
@ -13,8 +14,8 @@ let boolfiltrator = func (item) => item < 5;
let identity_list_reducer = func (acc, item) => acc + [item];
assert {
ok = reduce identity_list_reducer, [], list1 == list1,
desc = "reduce identity_list_reducer, [], list1 == list1",
ok = reduce(identity_list_reducer, [], list1) == list1,
desc = "reduce(identity_list_reducer, [], list1) == list1",
};
let nested_list = {
@ -22,54 +23,54 @@ let nested_list = {
};
assert {
ok = reduce identity_list_reducer, [], (nested_list.list) == list1,
desc = "reduce identity_list_reducer, [], (nested_list.list) == list1",
ok = reduce(identity_list_reducer, [], (nested_list.list)) == list1,
desc = "reduce(identity_list_reducer, [], (nested_list.list)) == list1",
};
let list_reducer = func (acc, item) => acc + item;
assert {
ok = reduce list_reducer, 0, list1 == 0 + 1 + 2 + 3 + 4,
desc = "reduce list_reducer, 0, list1 == 0 + 1 + 2 + 3 + 4",
ok = reduce(list_reducer, 0, list1) == 0 + 1 + 2 + 3 + 4,
desc = "reduce(list_reducer, 0, list1) == 0 + 1 + 2 + 3 + 4",
};
assert {
ok = map mapper, list1 == [2, 3, 4, 5],
desc = "map mapper, list1 == [2, 3, 4, 5]",
ok = map(mapper, list1) == [2, 3, 4, 5],
desc = "map(mapper, list1) == [2, 3, 4, 5]",
};
assert {
ok = (map mapper, [1, 2, 3, 4]) == [2, 3, 4, 5],
desc = "(map mapper, [1, 2, 3, 4]) == [2, 3, 4, 5]",
ok = (map(mapper, [1, 2, 3, 4])) == [2, 3, 4, 5],
desc = "(map(mapper, [1, 2, 3, 4])) == [2, 3, 4, 5]",
};
assert {
ok = map mapper, [1, 2, 3, 4] == [2, 3, 4, 5],
desc = "map mapper, [1, 2, 3, 4] == [2, 3, 4, 5]",
ok = map(mapper, [1, 2, 3, 4]) == [2, 3, 4, 5],
desc = "map(mapper, [1, 2, 3, 4]) == [2, 3, 4, 5]",
};
let s_mapper = func (arg) => arg + ",";
assert {
ok = map s_mapper, "foo" == "f,o,o,",
ok = map(s_mapper, "foo") == "f,o,o,",
desc = "we can map over each character",
};
assert {
ok = filter filtrator, list2 == ["foo", "foo"],
desc = "filter filtrator, list2 == [\"foo\", \"foo\"]",
};
assert {
ok = (filter filtrator, ["foo", "bar", "foo", "bar"]) == ["foo", "foo"],
desc = "(filter filtrator, [\"foo\", \"bar\", \"foo\", \"bar\"]) == [\"foo\", \"foo\"]",
};
assert {
ok = filter filtrator, ["foo", "bar", "foo", "bar"] == ["foo", "foo"],
desc = "filter filtrator, [\"foo\", \"bar\", \"foo\", \"bar\"] == [\"foo\", \"foo\"]",
};
assert {
ok = filter boolfiltrator, [1, 2, 3, 4, 5, 6, 7] == [1, 2, 3, 4],
desc = "filter boolfiltrator, [1, 2, 3, 4, 5, 6, 7] == [1, 2, 3, 4]",
};
//assert {
// ok = filter filtrator, list2 == ["foo", "foo"],
// desc = "filter filtrator, list2 == [\"foo\", \"foo\"]",
//};
//
//assert {
// ok = (filter filtrator, ["foo", "bar", "foo", "bar"]) == ["foo", "foo"],
// desc = "(filter filtrator, [\"foo\", \"bar\", \"foo\", \"bar\"]) == [\"foo\", \"foo\"]",
//};
//assert {
// ok = filter filtrator, ["foo", "bar", "foo", "bar"] == ["foo", "foo"],
// desc = "filter filtrator, [\"foo\", \"bar\", \"foo\", \"bar\"] == [\"foo\", \"foo\"]",
//};
//assert {
// ok = filter boolfiltrator, [1, 2, 3, 4, 5, 6, 7] == [1, 2, 3, 4],
// desc = "filter boolfiltrator, [1, 2, 3, 4, 5, 6, 7] == [1, 2, 3, 4]",
//};
// Tuple processing
let test_tpl = {
@ -80,8 +81,8 @@ let test_tpl = {
let identity_tpl_mapper = func (name, val) => [name, val];
assert {
ok = map identity_tpl_mapper, test_tpl == test_tpl,
desc = "map identity_tpl_mapper, test_tpl == test_tpl",
ok = map(identity_tpl_mapper, test_tpl) == test_tpl,
desc = "map(identity_tpl_mapper, test_tpl) == test_tpl",
};
let tpl_mapper = func (name, val) => select name, [name, val], {
@ -90,8 +91,8 @@ let tpl_mapper = func (name, val) => select name, [name, val], {
};
assert {
ok = map tpl_mapper, test_tpl == {foo = "barbar", cute = "pygmy"},
desc = "map tpl_mapper, test_tpl == {foo = \"barbar\", cute = \"pygmy\"}",
ok = map(tpl_mapper, test_tpl) == {foo = "barbar", cute = "pygmy"},
desc = "map(tpl_mapper, test_tpl) == {foo = \"barbar\", cute = \"pygmy\"}",
};
let identity_tpl_filter = func (name, val) => true;
@ -99,22 +100,22 @@ let identity_tpl_filter = func (name, val) => true;
// strip out foo field
let tpl_filter = func (name, val) => name != "foo";
assert {
ok = filter identity_tpl_filter, test_tpl == test_tpl,
desc = "filter identity_tpl_filter, test_tpl == test_tpl",
};
assert {
ok = filter tpl_filter, test_tpl == { quux = "baz" },
desc = "filter tpl_filter, test_tpl == { quux = \"baz\" }",
};
let o_str_filter = func (s) => s != "o";
assert {
ok = filter o_str_filter, "foobar" == "fbar",
desc = "We can strip out characters",
};
//assert {
// ok = filter identity_tpl_filter, test_tpl == test_tpl,
// desc = "filter identity_tpl_filter, test_tpl == test_tpl",
//};
//
//assert {
// ok = filter tpl_filter, test_tpl == { quux = "baz" },
// desc = "filter tpl_filter, test_tpl == { quux = \"baz\" }",
//};
//
//let o_str_filter = func (s) => s != "o";
//
//assert {
// ok = filter o_str_filter, "foobar" == "fbar",
// desc = "We can strip out characters",
//};
let tpl_reducer = func (acc, name, val) => acc{
keys = self.keys + [name],
@ -122,25 +123,25 @@ let tpl_reducer = func (acc, name, val) => acc{
};
assert {
ok = reduce tpl_reducer, {keys = [], vals = []}, test_tpl == {keys = ["foo", "quux"], vals = ["bar", "baz"]},
desc = "reduce tpl_reducer, {keys = [], vals = []}, test_tpl == {keys = [\"foo\", \"quux\"], vals = [\"bar\", \"baz\"]}",
ok = reduce(tpl_reducer, {keys = [], vals = []}, test_tpl) == {keys = ["foo", "quux"], vals = ["bar", "baz"]},
desc = "reduce(tpl_reducer, {keys = [], vals = []}, test_tpl) == {keys = [\"foo\", \"quux\"], vals = [\"bar\", \"baz\"]}",
};
let str_identity_reducer = func (acc, s) => acc + s;
assert {
ok = reduce str_identity_reducer, "", "foo" == "foo",
ok = reduce(str_identity_reducer, "", "foo") == "foo",
desc = "identity reducer copies string",
};
let char_iter = func (acc, s) => acc + [s];
assert {
ok = reduce char_iter, [], "foo" == ["f", "o", "o"],
ok = reduce(char_iter, [], "foo") == ["f", "o", "o"],
desc = "we can split a string into grapheme clusters",
};
assert {
ok = reduce (char_iter), [], "foo" == ["f", "o", "o"],
ok = reduce((char_iter), [], "foo") == ["f", "o", "o"],
desc = "We can use arbitrary expressions to refer to the macro.",
};

View File

@ -489,13 +489,6 @@ pub struct ListDef {
pub pos: Position,
}
#[derive(Debug, PartialEq, Clone)]
pub enum FuncOpDef {
Reduce(ReduceOpDef),
Map(MapFilterOpDef),
Filter(MapFilterOpDef),
}
#[derive(Debug, PartialEq, Clone)]
pub struct ReduceOpDef {
pub func: Box<Expression>,
@ -504,21 +497,9 @@ pub struct ReduceOpDef {
pub pos: Position,
}
/// MapFilterOpDef implements the list operations in the UCG AST.
#[derive(Debug, PartialEq, Clone)]
pub struct MapFilterOpDef {
pub func: Box<Expression>,
pub target: Box<Expression>,
pub pos: Position,
}
impl FuncOpDef {
impl ReduceOpDef {
pub fn pos(&self) -> &Position {
match self {
FuncOpDef::Map(def) => &def.pos,
FuncOpDef::Filter(def) => &def.pos,
FuncOpDef::Reduce(def) => &def.pos,
}
&self.pos
}
}
@ -635,7 +616,7 @@ pub enum Expression {
Call(CallDef),
Func(FuncDef),
Select(SelectDef),
FuncOp(FuncOpDef),
FuncOp(ReduceOpDef),
Module(ModuleDef),
// Declarative failure expressions

View File

@ -75,18 +75,10 @@ impl<'a> AstWalker<'a> {
self.walk_expression(expr);
}
},
Expression::FuncOp(ref mut def) => match def {
FuncOpDef::Reduce(ref mut def) => {
self.walk_expression(def.target.as_mut());
self.walk_expression(def.acc.as_mut())
}
FuncOpDef::Map(ref mut def) => {
self.walk_expression(def.target.as_mut());
}
FuncOpDef::Filter(ref mut def) => {
self.walk_expression(def.target.as_mut());
}
},
Expression::FuncOp(ref mut def) => {
self.walk_expression(def.target.as_mut());
self.walk_expression(def.acc.as_mut());
}
Expression::Binary(ref mut def) => {
self.walk_expression(def.left.as_mut());
self.walk_expression(def.right.as_mut());

View File

@ -352,7 +352,7 @@ impl<'a> FileBuilder<'a> {
fn check_reserved_word(name: &str) -> bool {
match name {
"self" | "assert" | "true" | "false" | "let" | "import" | "as" | "select" | "func"
| "module" | "env" | "map" | "filter" | "NULL" | "out" | "in" | "is" | "not" => true,
| "module" | "env" | "NULL" | "out" | "in" | "is" | "not" => true,
_ => false,
}
}
@ -1465,40 +1465,6 @@ impl<'a> FileBuilder<'a> {
Ok(Rc::new(Val::Str(result)))
}
fn eval_functional_processing(
&self,
def: &MapFilterOpDef,
typ: ProcessingOpType,
scope: &Scope,
) -> Result<Rc<Val>, Box<dyn Error>> {
let maybe_target = self.eval_expr(&def.target, scope)?;
let maybe_mac = self.eval_expr(&def.func, scope)?;
let macdef = match maybe_mac.as_ref() {
&Val::Func(ref macdef) => macdef,
_ => {
return Err(Box::new(error::BuildError::new(
format!("Expected func but got {:?}", def.func),
error::ErrorType::TypeFail,
def.pos.clone(),
)));
}
};
return match maybe_target.as_ref() {
&Val::List(ref elems) => self.eval_functional_list_processing(elems, macdef, typ),
&Val::Tuple(ref fs) => self.eval_functional_tuple_processing(fs, macdef, typ),
// TODO(jwall): Strings?
&Val::Str(ref s) => self.eval_functional_string_processing(s, macdef, typ),
other => Err(Box::new(error::BuildError::new(
format!(
"Expected List or Tuple as target but got {:?}",
other.type_name()
),
error::ErrorType::TypeFail,
def.target.pos().clone(),
))),
};
}
fn record_assert_result(&mut self, msg: &str, is_success: bool) {
if !is_success {
let msg = format!("{} - NOT OK: {}\n", self.assert_collector.counter, msg);
@ -1647,18 +1613,6 @@ impl<'a> FileBuilder<'a> {
};
}
fn eval_func_op(&self, def: &FuncOpDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
match def {
FuncOpDef::Filter(ref def) => {
self.eval_functional_processing(def, ProcessingOpType::Filter, scope)
}
FuncOpDef::Map(ref def) => {
self.eval_functional_processing(def, ProcessingOpType::Map, scope)
}
FuncOpDef::Reduce(ref def) => self.eval_reduce_op(def, scope),
}
}
pub fn eval_range(&self, def: &RangeDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
let start = self.eval_expr(&def.start, scope)?;
let start = match start.as_ref() {
@ -1776,7 +1730,7 @@ impl<'a> FileBuilder<'a> {
self.eval_module_def(&mut def_clone, scope)
}
&Expression::Select(ref def) => self.eval_select(def, scope),
&Expression::FuncOp(ref def) => self.eval_func_op(def, scope),
&Expression::FuncOp(ref def) => self.eval_reduce_op(def, scope),
&Expression::Include(ref def) => self.eval_include(def),
&Expression::Import(ref def) => self.eval_import(def),
&Expression::Fail(ref def) => {

View File

@ -574,57 +574,22 @@ make_fn!(
do_each!(
pos => pos,
_ => word!("reduce"),
_ => must!(punct!("(")),
func => must!(expression),
_ => must!(punct!(",")),
acc => must!(trace_parse!(non_op_expression)),
_ => must!(punct!(",")),
tgt => must!(trace_parse!(non_op_expression)),
(Expression::FuncOp(FuncOpDef::Reduce(ReduceOpDef{
_ => must!(punct!(")")),
(Expression::FuncOp(ReduceOpDef{
func: Box::new(func),
acc: Box::new(acc),
target: Box::new(tgt),
pos: pos,
})))
}))
)
);
make_fn!(
map_expression<SliceIter<Token>, Expression>,
do_each!(
pos => pos,
_ => word!("map"),
func => must!(expression),
_ => must!(punct!(",")),
list => must!(trace_parse!(non_op_expression)),
(Expression::FuncOp(FuncOpDef::Map(MapFilterOpDef{
func: Box::new(func),
target: Box::new(list),
pos: pos,
})))
)
);
make_fn!(
filter_expression<SliceIter<Token>, Expression>,
do_each!(
pos => pos,
_ => word!("filter"),
func => must!(expression),
_ => must!(punct!(",")),
list => must!(trace_parse!(non_op_expression)),
(Expression::FuncOp(FuncOpDef::Filter(MapFilterOpDef{
func: Box::new(func),
target: Box::new(list),
pos: pos,
})))
)
);
make_fn!(
func_op_expression<SliceIter<Token>, Expression>,
either!(reduce_expression, map_expression, filter_expression)
);
make_fn!(
range_expression<SliceIter<Token>, Expression>,
do_each!(
@ -710,7 +675,7 @@ fn unprefixed_expression(input: SliceIter<Token>) -> ParseResult<Expression> {
make_fn!(
non_op_expression<SliceIter<Token>, Expression>,
either!(
trace_parse!(func_op_expression),
trace_parse!(reduce_expression),
trace_parse!(func_expression),
trace_parse!(import_expression),
trace_parse!(not_expression),

View File

@ -15,3 +15,14 @@ let if = module{
};
let identity = func (arg) => arg;
let s = import "std/schema.ucg";
let map_list_reducer = func(acc, item) => acc{list = acc.list + [acc.op(item)]};
let map_str_reducer = func(acc, item) => acc{str = acc.str + acc.op(item)};
let map = func(op, coll) => select s.base_type_of(coll), NULL, {
list = reduce(map_list_reducer, {op=op, list=[]}, coll).list,
str = reduce(map_str_reducer, {op=op, str=""}, coll).str,
};

View File

@ -17,7 +17,7 @@ let base_type_reducer = func (acc, f) => select (acc.val is f), f, {
};
// Computes the base type of a value.
let base_type_of = func (val) => (reduce base_type_reducer, {val=val, typ="null"}, base_types).typ;
let base_type_of = func (val) => (reduce(base_type_reducer, {val=val, typ="null"}, base_types)).typ;
// Turns any schema check module into a compile failure.
// The module must export the computed value as the result field.
@ -39,7 +39,7 @@ let any = module {
let reducer = func (acc, t) => acc{
ok = acc.ok || (schema.shaped{val=acc.val, shape=t}.result),
};
let any = func (val, types) => reduce reducer, {ok=false, val=val}, types;
let any = func (val, types) => reduce(reducer, {ok=false, val=val}, types);
let result = any(mod.val, mod.types).ok;
};
@ -82,7 +82,7 @@ let shaped = module {
float = simple_handler(mod.val, mod.shape),
bool = simple_handler(mod.val, mod.shape),
null = simple_handler(mod.val, mod.shape),
tuple = (reduce tuple_handler, {shape=mod.shape, ok=false}, (mod.val)).ok,
tuple = (reduce(tuple_handler, {shape=mod.shape, ok=false}, (mod.val))).ok,
list = simple_handler(mod.val, mod.shape),
func = simple_handler(mod.val, mod.shape),
module = simple_handler(mod.val, mod.shape),