From 4d6fd87c3dad66c9c4fb27b64d14db434ffdfd57 Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Wed, 30 Jan 2019 20:23:35 -0600 Subject: [PATCH] FEATURE: Allow expressions for functions in reduce/map/filter. --- docsite/site/content/reference/expressions.md | 12 +-- docsite/site/content/reference/grammar.md | 4 +- .../functional_processing_test.ucg | 73 ++++++++++--------- src/ast/mod.rs | 4 +- src/build/mod.rs | 4 +- src/parse/mod.rs | 15 ++-- std/lists.ucg | 14 ++-- std/schema.ucg | 6 +- std/tuples.ucg | 12 +-- 9 files changed, 76 insertions(+), 68 deletions(-) diff --git a/docsite/site/content/reference/expressions.md b/docsite/site/content/reference/expressions.md index 08a7370..3144458 100644 --- a/docsite/site/content/reference/expressions.md +++ b/docsite/site/content/reference/expressions.md @@ -348,7 +348,7 @@ function is expected to take a single argument. let list1 = [1, 2, 3, 4]; let mapper = func (item) => item + 1; -map mapper list1 == [2, 3, 4, 5]; +map mapper, list1 == [2, 3, 4, 5]; ``` **For Tuples** @@ -367,7 +367,7 @@ let tpl_mapper = func (name, val) => select name, [name, val], { "foo" = ["foo", "barbar"], quux = ["cute", "pygmy"], }; -map tpl_mapper test_tpl == {foo = "barbar", cute = "pygmy"}; +map tpl_mapper, test_tpl == {foo = "barbar", cute = "pygmy"}; ``` ### Filter expressions @@ -384,7 +384,7 @@ let filtrator = func (item) => select item, NULL, { foo = item, }; -filter filtrator.result list2 == ["foo", "foo"]; +filter filtrator, list2 == ["foo", "foo"]; ``` **Tuples** @@ -395,7 +395,7 @@ let test_tpl = { quux = "baz", }; let tpl_filter = func (name, val) => name != "foo"; -filter tpl_filter test_tpl == { quux = "baz" }; +filter tpl_filter, test_tpl == { quux = "baz" }; ``` ### Reduce expressions @@ -416,7 +416,7 @@ let tpl_reducer = func (acc, name, val) => acc{ vals = self.vals + [val], }; -reduce tpl_reducer {keys = [], vals = []}, test_tpl == {keys = ["foo", "quux"], vals = ["bar", "baz"]}; +reduce tpl_reducer, {keys = [], vals = []}, test_tpl == {keys = ["foo", "quux"], vals = ["bar", "baz"]}; ``` **Lists** @@ -425,7 +425,7 @@ reduce tpl_reducer {keys = [], vals = []}, test_tpl == {keys = ["foo", "quux"], let list1 = [1, 2, 3, 4]; let list_reducer = func (acc, item) => acc + item; - list_reducer 0, list1 == 0 + 1 + 2 + 3 + 4; +reduce list_reducer, 0, list1 == 0 + 1 + 2 + 3 + 4; ``` Include expressions diff --git a/docsite/site/content/reference/grammar.md b/docsite/site/content/reference/grammar.md index ecee246..f0ee1c5 100644 --- a/docsite/site/content/reference/grammar.md +++ b/docsite/site/content/reference/grammar.md @@ -141,8 +141,8 @@ format_expr: str, percent, (format_arg_list | format_expr_arg) ; ``` func_op_kind: map_keyword | filter_keyword ; -map_or_filter_expr: func_op_kind, bareword, expr ; -reduce_expr: reduce_keyword, bareword, expr, expr ; +map_or_filter_expr: func_op_kind, expr, expr ; +reduce_expr: reduce_keyword, expr, expr, expr ; processing_expr: map_or_filter_expr | reduce_expr ``` diff --git a/integration_tests/functional_processing_test.ucg b/integration_tests/functional_processing_test.ucg index 2ab3151..fef9357 100644 --- a/integration_tests/functional_processing_test.ucg +++ b/integration_tests/functional_processing_test.ucg @@ -13,8 +13,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,53 +22,53 @@ 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\"]", + 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\"]", + 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\"]", + 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]", + 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 @@ -80,8 +80,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 +90,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; @@ -100,19 +100,19 @@ let identity_tpl_filter = func (name, val) => true; 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", + 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\" }", + 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", + ok = filter o_str_filter, "foobar" == "fbar", desc = "We can strip out characters", }; @@ -122,20 +122,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"], + desc = "We can use arbitrary expressions to refer to the macro.", }; \ No newline at end of file diff --git a/src/ast/mod.rs b/src/ast/mod.rs index ccc0c62..061abd3 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -498,7 +498,7 @@ pub enum FuncOpDef { #[derive(Debug, PartialEq, Clone)] pub struct ReduceOpDef { - pub func: PositionedItem, + pub func: Box, pub acc: Box, pub target: Box, pub pos: Position, @@ -507,7 +507,7 @@ pub struct ReduceOpDef { /// MapFilterOpDef implements the list operations in the UCG AST. #[derive(Debug, PartialEq, Clone)] pub struct MapFilterOpDef { - pub func: PositionedItem, + pub func: Box, pub target: Box, pub pos: Position, } diff --git a/src/build/mod.rs b/src/build/mod.rs index d404379..19e6a22 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -1364,7 +1364,7 @@ impl<'a> FileBuilder<'a> { fn eval_reduce_op(&self, def: &ReduceOpDef, scope: &Scope) -> Result, Box> { let maybe_target = self.eval_expr(&def.target, scope)?; let mut acc = self.eval_expr(&def.acc, scope)?; - let maybe_mac = self.eval_value(&Value::Symbol(def.func.clone()), &self.scope.clone())?; + let maybe_mac = self.eval_expr(&def.func, scope)?; let funcdef = match maybe_mac.as_ref() { &Val::Func(ref funcdef) => funcdef, _ => { @@ -1472,7 +1472,7 @@ impl<'a> FileBuilder<'a> { scope: &Scope, ) -> Result, Box> { let maybe_target = self.eval_expr(&def.target, scope)?; - let maybe_mac = self.eval_value(&Value::Symbol(def.func.clone()), &self.scope.clone())?; + let maybe_mac = self.eval_expr(&def.func, scope)?; let macdef = match maybe_mac.as_ref() { &Val::Func(ref macdef) => macdef, _ => { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 08986b0..82e8576 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -574,12 +574,13 @@ make_fn!( do_each!( pos => pos, _ => word!("reduce"), - funcname => must!(match_type!(BAREWORD)), + 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{ - func: (&funcname).into(), + func: Box::new(func), acc: Box::new(acc), target: Box::new(tgt), pos: pos, @@ -592,10 +593,11 @@ make_fn!( do_each!( pos => pos, _ => word!("map"), - funcname => must!(match_type!(BAREWORD)), + func => must!(expression), + _ => must!(punct!(",")), list => must!(trace_parse!(non_op_expression)), (Expression::FuncOp(FuncOpDef::Map(MapFilterOpDef{ - func: (&funcname).into(), + func: Box::new(func), target: Box::new(list), pos: pos, }))) @@ -607,10 +609,11 @@ make_fn!( do_each!( pos => pos, _ => word!("filter"), - funcname => must!(match_type!(BAREWORD)), + func => must!(expression), + _ => must!(punct!(",")), list => must!(trace_parse!(non_op_expression)), (Expression::FuncOp(FuncOpDef::Filter(MapFilterOpDef{ - func: (&funcname).into(), + func: Box::new(func), target: Box::new(list), pos: pos, }))) diff --git a/std/lists.ucg b/std/lists.ucg index 2d9c200..4c6cda1 100644 --- a/std/lists.ucg +++ b/std/lists.ucg @@ -11,7 +11,7 @@ let str_join = module{ }, }; - let result = (reduce joiner {sep=mod.sep, out=""}, (mod.list)).out; + let result = (reduce joiner, {sep=mod.sep, out=""}, (mod.list)).out; }; let len = module{ @@ -19,7 +19,7 @@ let len = module{ } => { let counter = func (acc, item) => acc + 1; - let result = reduce counter 0, (mod.list); + let result = reduce counter, 0, (mod.list); }; let reverse = module{ @@ -27,7 +27,7 @@ let reverse = module{ } => { let reducer = func (acc, item) => [item] + acc; - let result = reduce reducer [], (mod.list); + let result = reduce reducer, [], (mod.list); }; let enumerate = module{ @@ -42,7 +42,7 @@ let enumerate = module{ let acc = {count=mod.start, list=[], step=mod.step}; - let enumerated = reduce reducer acc, (mod.list); + let enumerated = reduce reducer, acc, (mod.list); let result = enumerated.list; }; @@ -52,8 +52,8 @@ let zip = module{ } => { let counter = func (acc, item) => acc + 1; - let len1 = reduce counter 0, (mod.list1); - let len2 = reduce counter 0, (mod.list2); + let len1 = reduce counter, 0, (mod.list1); + let len2 = reduce counter, 0, (mod.list2); let rng = select (len1 >= len2), NULL, { true = 0:(len1 - 1), @@ -72,5 +72,5 @@ let zip = module{ idxs = [], }; - let result = (reduce reducer acc, rng).result; + let result = (reduce reducer, acc, rng).result; }; \ No newline at end of file diff --git a/std/schema.ucg b/std/schema.ucg index 6f50600..9e80011 100644 --- a/std/schema.ucg +++ b/std/schema.ucg @@ -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), diff --git a/std/tuples.ucg b/std/tuples.ucg index ccb409c..b8f93c6 100644 --- a/std/tuples.ucg +++ b/std/tuples.ucg @@ -12,7 +12,7 @@ let fields = module{ let reducer = func (acc, field, value) => acc + [field]; - let result = reduce reducer [], (mod.tpl); + let result = reduce reducer, [], (mod.tpl); }; // return a list of the values in a tuple. @@ -25,7 +25,7 @@ let values = module{ let reducer = func (acc, field, value) => acc + [value]; - let result = reduce reducer [], (mod.tpl); + let result = reduce reducer, [], (mod.tpl); }; let iter = module{ @@ -37,7 +37,7 @@ let iter = module{ let reducer = func (acc, field, value) => acc + [[field, value]]; - let result = reduce reducer [], (mod.tpl); + let result = reduce reducer, [], (mod.tpl); }; let strip_nulls = module{ @@ -49,7 +49,7 @@ let strip_nulls = module{ let filterer = func (name, value) => value != NULL; - let result = filter filterer (mod.tpl); + let result = filter filterer, (mod.tpl); }; let has_fields = module{ @@ -64,7 +64,7 @@ let has_fields = module{ let reducer = func (acc, f) => acc && (f in fs); - let result = reduce reducer true, (mod.fields); + let result = reduce reducer, true, (mod.fields); }; let field_type = module{ @@ -95,7 +95,7 @@ let field_type = module{ false = true, // if this isn't the field then we propagate true }); - let is_type = reduce reducer true, it; + let is_type = reduce reducer, true, it; let result = has_field && is_type; }; \ No newline at end of file