mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
FEATURE: Allow expressions for functions in reduce/map/filter.
This commit is contained in:
parent
8861990a94
commit
4d6fd87c3d
@ -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
|
||||
|
@ -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
|
||||
```
|
||||
|
||||
|
@ -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.",
|
||||
};
|
@ -498,7 +498,7 @@ pub enum FuncOpDef {
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct ReduceOpDef {
|
||||
pub func: PositionedItem<String>,
|
||||
pub func: Box<Expression>,
|
||||
pub acc: Box<Expression>,
|
||||
pub target: Box<Expression>,
|
||||
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<String>,
|
||||
pub func: Box<Expression>,
|
||||
pub target: Box<Expression>,
|
||||
pub pos: Position,
|
||||
}
|
||||
|
@ -1364,7 +1364,7 @@ impl<'a> FileBuilder<'a> {
|
||||
fn eval_reduce_op(&self, def: &ReduceOpDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
||||
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<Rc<Val>, Box<dyn Error>> {
|
||||
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,
|
||||
_ => {
|
||||
|
@ -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,
|
||||
})))
|
||||
|
@ -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;
|
||||
};
|
@ -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),
|
||||
|
@ -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;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user