mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
FEATURE: Macros are just an expression now.
This is a breaking change for the map, filter, and reduce expressions but happily a forward compatible change for macros themselves.
This commit is contained in:
parent
b18d513a9f
commit
890387b4cc
@ -249,19 +249,46 @@ UCG can generate lists from a range with an optional step.
|
|||||||
0:2:10 == [0, 2, 4, 6, 8, 10];
|
0:2:10 == [0, 2, 4, 6, 8, 10];
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Macros
|
||||||
|
-----
|
||||||
|
|
||||||
|
Macros look like functions but they are resolved at compile time and
|
||||||
|
configurations don't execute so they never appear in output. Macros close over
|
||||||
|
the environment up to the point where they are declared in the file. One
|
||||||
|
consequence of this is that they can not call themselves so recursive macros
|
||||||
|
are not possible. This is probably a feature. They are useful for constructing
|
||||||
|
tuples of a certain shape or otherwise promoting data reuse. You define a macro
|
||||||
|
with the `macro` keyword followed by the arguments in parentheses, a `=>`, and
|
||||||
|
then a valid expression.
|
||||||
|
|
||||||
|
```
|
||||||
|
let mymacro = macro (arg1, arg2) => {
|
||||||
|
host = arg1,
|
||||||
|
port = arg2,
|
||||||
|
connstr = "couchdb://@:@" % (arg1, arg2),
|
||||||
|
};
|
||||||
|
|
||||||
|
let my_dbconf = mymacro("couchdb.example.org", "9090");
|
||||||
|
|
||||||
|
let my_dbhost = dbconf.host;
|
||||||
|
|
||||||
|
let add = macro(arg1, arg2) => arg1 + arg2;
|
||||||
|
add(1, 1) == 2;
|
||||||
|
```
|
||||||
|
|
||||||
Functional processing expressions
|
Functional processing expressions
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
UCG has a few functional processing expressions called `map` and `filter`. Both of
|
UCG has a few functional processing expressions called `map`, `filter`, and
|
||||||
them can process a list or tuple.
|
`reduce`. All of them can process a list or tuple.
|
||||||
|
|
||||||
Their syntax starts with either map or filter followed by a symbol that
|
Their syntax starts with either `map` `filter`, or `reduce followed by a symbol
|
||||||
references a valid macro and the outfield for the tuple the macro produces. and
|
that references a valid macro and finally an expression that resolves to either
|
||||||
finally an expression that resolves to either a list of tuple.
|
a list or a tuple.
|
||||||
|
|
||||||
### Map expressions
|
### Map expressions
|
||||||
|
|
||||||
Map macros should produce in the result field a value or [field, value] that
|
Map macros should produce either a valid value or a list of [field, value] that
|
||||||
will replace the element or field it is curently processing.
|
will replace the element or field it is curently processing.
|
||||||
|
|
||||||
**For Lists**
|
**For Lists**
|
||||||
@ -272,34 +299,32 @@ macro is expected to take a single argument.
|
|||||||
```
|
```
|
||||||
let list1 = [1, 2, 3, 4];
|
let list1 = [1, 2, 3, 4];
|
||||||
|
|
||||||
let mapper = macro(item) => { result = item + 1 };
|
let mapper = macro(item) => item + 1;
|
||||||
map mapper.result list1 == [2, 3, 4, 5];
|
map mapper list1 == [2, 3, 4, 5];
|
||||||
```
|
```
|
||||||
|
|
||||||
**For Tuples**
|
**For Tuples**
|
||||||
|
|
||||||
Macros for mapping across a tuple are expected to take two arguments. The first
|
Macros for mapping across a tuple are expected to take two arguments. The first
|
||||||
argument is the name of the field. The second argument is the value in that
|
argument is the name of the field. The second argument is the value in that
|
||||||
field. The result field should be a two item list with the first item being the
|
field. The result should be a two item list with the first item being the new
|
||||||
new field name and the second item being the new value.
|
field name and the second item being the new value.
|
||||||
|
|
||||||
```
|
```
|
||||||
let test_tpl = {
|
let test_tpl = {
|
||||||
foo = "bar",
|
foo = "bar",
|
||||||
quux = "baz",
|
quux = "baz",
|
||||||
};
|
};
|
||||||
let tpl_mapper = macro(name, val) => {
|
let tpl_mapper = macro(name, val) => select name, [name, val], {
|
||||||
result = select name, [name, val], {
|
"foo" = ["foo", "barbar"],
|
||||||
"foo" = ["foo", "barbar"],
|
quux = ["cute", "pygmy"],
|
||||||
quux = ["cute", "pygmy"],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
map tpl_mapper.result test_tpl == {foo = "barbar", cute = "pygmy"};
|
map tpl_mapper test_tpl == {foo = "barbar", cute = "pygmy"};
|
||||||
```
|
```
|
||||||
|
|
||||||
### Filter expressions
|
### Filter expressions
|
||||||
|
|
||||||
Filter expressions should return a result field with false or NULL for items to
|
Filter expressions should return a field with false or NULL for items to
|
||||||
filter out of the list or tuple. Any other value in the return field results in
|
filter out of the list or tuple. Any other value in the return field results in
|
||||||
the item or field staying in the resulting list or tuple.
|
the item or field staying in the resulting list or tuple.
|
||||||
|
|
||||||
@ -307,10 +332,8 @@ the item or field staying in the resulting list or tuple.
|
|||||||
|
|
||||||
```
|
```
|
||||||
let list2 = ["foo", "bar", "foo", "bar"];
|
let list2 = ["foo", "bar", "foo", "bar"];
|
||||||
let filtrator = macro(item) => {
|
let filtrator = macro(item) => select item, NULL, {
|
||||||
result = select item, NULL, {
|
foo = item,
|
||||||
foo = item,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
filter filtrator.result list2 == ["foo", "foo"];
|
filter filtrator.result list2 == ["foo", "foo"];
|
||||||
@ -323,15 +346,13 @@ let test_tpl = {
|
|||||||
foo = "bar",
|
foo = "bar",
|
||||||
quux = "baz",
|
quux = "baz",
|
||||||
};
|
};
|
||||||
let tpl_filter = macro(name, val) => {
|
let tpl_filter = macro(name, val) => name != "foo";
|
||||||
result = name != "foo",
|
filter tpl_filter test_tpl == { quux = "baz" };
|
||||||
};
|
|
||||||
filter tpl_filter.result test_tpl == { quux = "baz" };
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reduce expressions
|
### Reduce expressions
|
||||||
|
|
||||||
Reduce expressions start with the reduce keyword followed by a symbol referencing a macro a dot and the output field, then an expression for the accumulator and finally the tuple or list to process.
|
Reduce expressions start with the reduce keyword followed by a symbol referencing a macro an expression for the accumulator and finally the tuple or list to process.
|
||||||
|
|
||||||
**Tuples**
|
**Tuples**
|
||||||
|
|
||||||
@ -340,25 +361,21 @@ let test_tpl = {
|
|||||||
foo = "bar",
|
foo = "bar",
|
||||||
quux = "baz",
|
quux = "baz",
|
||||||
};
|
};
|
||||||
let tpl_reducer = macro(acc, name, val) => {
|
let tpl_reducer = macro(acc, name, val) => acc{
|
||||||
result = acc{
|
keys = self.keys + [name],
|
||||||
keys = self.keys + [name],
|
vals = self.vals + [val],
|
||||||
vals = self.vals + [val],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
reduce tpl_reducer.result {keys = [], vals = []}, test_tpl == {keys = ["foo", "quux"], vals = ["bar", "baz"]};
|
reduce tpl_reducer {keys = [], vals = []}, test_tpl == {keys = ["foo", "quux"], vals = ["bar", "baz"]};
|
||||||
```
|
```
|
||||||
|
|
||||||
**Lists**
|
**Lists**
|
||||||
|
|
||||||
```
|
```
|
||||||
let list1 = [1, 2, 3, 4];
|
let list1 = [1, 2, 3, 4];
|
||||||
let list_reducer = macro(acc, item) => {
|
let list_reducer = macro(acc, item) => acc + item;
|
||||||
result = acc + item,
|
|
||||||
};
|
|
||||||
|
|
||||||
list_reducer.result 0, list1 == 0 + 1 + 2 + 3 + 4;
|
list_reducer 0, list1 == 0 + 1 + 2 + 3 + 4;
|
||||||
```
|
```
|
||||||
|
|
||||||
Include expressions
|
Include expressions
|
||||||
@ -403,31 +420,6 @@ let ifresult = select true, NULL, {
|
|||||||
}; // result will be "true result"
|
}; // result will be "true result"
|
||||||
```
|
```
|
||||||
|
|
||||||
Macros
|
|
||||||
-----
|
|
||||||
|
|
||||||
Macros look like functions but they are resolved at compile time and
|
|
||||||
configurations don't execute so they never appear in output. Macros close over
|
|
||||||
their environment in the file they are declared in. They are useful for
|
|
||||||
constructing tuples of a certain shape or otherwise promoting data reuse. You
|
|
||||||
define a macro with the `macro` keyword followed by the arguments in
|
|
||||||
parentheses, a `=>`, and then a tuple literal.
|
|
||||||
|
|
||||||
```
|
|
||||||
let mymacro = macro (arg1, arg2) => {
|
|
||||||
host = arg1,
|
|
||||||
port = arg2,
|
|
||||||
connstr = "couchdb://@:@" % (arg1, arg2),
|
|
||||||
};
|
|
||||||
|
|
||||||
let my_dbconf = mymacro("couchdb.example.org", "9090");
|
|
||||||
|
|
||||||
let my_dbhost = dbconf.host;
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that while macros can close over their environment they can not reference
|
|
||||||
other fields in the macro body itself.
|
|
||||||
|
|
||||||
Modules
|
Modules
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
@ -136,8 +136,8 @@ format_expr: str, percent, lparen, [arglist], rparen ;
|
|||||||
|
|
||||||
```
|
```
|
||||||
func_op_kind: map_keyword | filter_keyword ;
|
func_op_kind: map_keyword | filter_keyword ;
|
||||||
map_or_filter_expr: func_op_kind, bareword, dot, bareword, expr ;
|
map_or_filter_expr: func_op_kind, bareword, expr ;
|
||||||
reduce_expr: reduce_keyword, bareword, dot, bareword, expr, expr ;
|
reduce_expr: reduce_keyword, bareword, expr, expr ;
|
||||||
processing_expr: map_or_filter_expr | reduce_expr
|
processing_expr: map_or_filter_expr | reduce_expr
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -3,24 +3,18 @@
|
|||||||
let list1 = [1, 2, 3, 4];
|
let list1 = [1, 2, 3, 4];
|
||||||
let list2 = ["foo", "bar", "foo", "bar"];
|
let list2 = ["foo", "bar", "foo", "bar"];
|
||||||
|
|
||||||
let mapper = macro(item) => { result = item + 1 };
|
let mapper = macro(item) => item + 1;
|
||||||
let filtrator = macro(item) => {
|
let filtrator = macro(item) => select item, NULL, {
|
||||||
result = select item, NULL, {
|
|
||||||
foo = item,
|
foo = item,
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let boolfiltrator = macro(item) => {
|
let boolfiltrator = macro(item) => item < 5;
|
||||||
result = item < 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
let identity_list_reducer = macro(acc, item) => {
|
let identity_list_reducer = macro(acc, item) => acc + [item];
|
||||||
result = acc + [item],
|
|
||||||
};
|
|
||||||
|
|
||||||
assert {
|
assert {
|
||||||
ok = reduce identity_list_reducer.result [], list1 == list1,
|
ok = reduce identity_list_reducer [], list1 == list1,
|
||||||
desc = "reduce identity_list_reducer.result [], list1 == list1",
|
desc = "reduce identity_list_reducer [], list1 == list1",
|
||||||
};
|
};
|
||||||
|
|
||||||
let nested_list = {
|
let nested_list = {
|
||||||
@ -28,47 +22,46 @@ let nested_list = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
assert {
|
assert {
|
||||||
ok = reduce identity_list_reducer.result [], (nested_list.list) == list1,
|
ok = reduce identity_list_reducer [], (nested_list.list) == list1,
|
||||||
desc = "reduce identity_list_reducer.result [], (nested_list.list) == list1",
|
desc = "reduce identity_list_reducer [], (nested_list.list) == list1",
|
||||||
};
|
};
|
||||||
|
|
||||||
let list_reducer = macro(acc, item) => {
|
let list_reducer = macro(acc, item) => acc + item;
|
||||||
result = 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",
|
||||||
};
|
};
|
||||||
|
|
||||||
assert {
|
assert {
|
||||||
ok = reduce list_reducer.result 0, list1 == 0 + 1 + 2 + 3 + 4,
|
ok = map mapper list1 == [2, 3, 4, 5],
|
||||||
desc = "reduce list_reducer.result 0, list1 == 0 + 1 + 2 + 3 + 4",
|
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]",
|
||||||
|
};
|
||||||
|
assert {
|
||||||
|
ok = map mapper [1, 2, 3, 4] == [2, 3, 4, 5],
|
||||||
|
desc = "map mapper [1, 2, 3, 4] == [2, 3, 4, 5]",
|
||||||
};
|
};
|
||||||
|
|
||||||
assert {
|
assert {
|
||||||
ok = map mapper.result list1 == [2, 3, 4, 5],
|
ok = filter filtrator list2 == ["foo", "foo"],
|
||||||
desc = "map mapper.result list1 == [2, 3, 4, 5]",
|
desc = "filter filtrator list2 == [\"foo\", \"foo\"]",
|
||||||
};
|
|
||||||
assert {
|
|
||||||
ok = (map mapper.result [1, 2, 3, 4]) == [2, 3, 4, 5],
|
|
||||||
desc = "(map mapper.result [1, 2, 3, 4]) == [2, 3, 4, 5]",
|
|
||||||
};
|
|
||||||
assert {
|
|
||||||
ok = map mapper.result [1, 2, 3, 4] == [2, 3, 4, 5],
|
|
||||||
desc = "map mapper.result [1, 2, 3, 4] == [2, 3, 4, 5]",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert {
|
assert {
|
||||||
ok = filter filtrator.result list2 == ["foo", "foo"],
|
ok = (filter filtrator ["foo", "bar", "foo", "bar"]) == ["foo", "foo"],
|
||||||
desc = "filter filtrator.result list2 == [\"foo\", \"foo\"]",
|
desc = "(filter filtrator [\"foo\", \"bar\", \"foo\", \"bar\"]) == [\"foo\", \"foo\"]",
|
||||||
};
|
};
|
||||||
assert {
|
assert {
|
||||||
ok = (filter filtrator.result ["foo", "bar", "foo", "bar"]) == ["foo", "foo"],
|
ok = filter filtrator ["foo", "bar", "foo", "bar"] == ["foo", "foo"],
|
||||||
desc = "(filter filtrator.result [\"foo\", \"bar\", \"foo\", \"bar\"]) == [\"foo\", \"foo\"]",
|
desc = "filter filtrator [\"foo\", \"bar\", \"foo\", \"bar\"] == [\"foo\", \"foo\"]",
|
||||||
};
|
};
|
||||||
assert {
|
assert {
|
||||||
ok = filter filtrator.result ["foo", "bar", "foo", "bar"] == ["foo", "foo"],
|
ok = filter boolfiltrator [1, 2, 3, 4, 5, 6, 7] == [1, 2, 3, 4],
|
||||||
desc = "filter filtrator.result [\"foo\", \"bar\", \"foo\", \"bar\"] == [\"foo\", \"foo\"]",
|
desc = "filter boolfiltrator [1, 2, 3, 4, 5, 6, 7] == [1, 2, 3, 4]",
|
||||||
};
|
|
||||||
assert {
|
|
||||||
ok = filter boolfiltrator.result [1, 2, 3, 4, 5, 6, 7] == [1, 2, 3, 4],
|
|
||||||
desc = "filter boolfiltrator.result [1, 2, 3, 4, 5, 6, 7] == [1, 2, 3, 4]",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tuple processing
|
// Tuple processing
|
||||||
@ -77,54 +70,44 @@ let test_tpl = {
|
|||||||
quux = "baz",
|
quux = "baz",
|
||||||
};
|
};
|
||||||
|
|
||||||
let identity_tpl_mapper = macro(name, val) => {
|
let identity_tpl_mapper = macro(name, val) => [name, val];
|
||||||
result = [name, val],
|
|
||||||
|
assert {
|
||||||
|
ok = map identity_tpl_mapper test_tpl == test_tpl,
|
||||||
|
desc = "map identity_tpl_mapper test_tpl == test_tpl",
|
||||||
|
};
|
||||||
|
|
||||||
|
let tpl_mapper = macro(name, val) => select name, [name, val], {
|
||||||
|
"foo" = ["foo", "barbar"],
|
||||||
|
quux = ["cute", "pygmy"],
|
||||||
};
|
};
|
||||||
|
|
||||||
assert {
|
assert {
|
||||||
ok = map identity_tpl_mapper.result test_tpl == test_tpl,
|
ok = map tpl_mapper test_tpl == {foo = "barbar", cute = "pygmy"},
|
||||||
desc = "map identity_tpl_mapper.result test_tpl == test_tpl",
|
desc = "map tpl_mapper test_tpl == {foo = \"barbar\", cute = \"pygmy\"}",
|
||||||
};
|
};
|
||||||
|
|
||||||
let tpl_mapper = macro(name, val) => {
|
let identity_tpl_filter = macro(name, val) => true;
|
||||||
result = select name, [name, val], {
|
|
||||||
"foo" = ["foo", "barbar"],
|
|
||||||
quux = ["cute", "pygmy"],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
assert {
|
|
||||||
ok = map tpl_mapper.result test_tpl == {foo = "barbar", cute = "pygmy"},
|
|
||||||
desc = "map tpl_mapper.result test_tpl == {foo = \"barbar\", cute = \"pygmy\"}",
|
|
||||||
};
|
|
||||||
|
|
||||||
let identity_tpl_filter = macro(name, val) => {
|
|
||||||
result = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
// strip out foo field
|
// strip out foo field
|
||||||
let tpl_filter = macro(name, val) => {
|
let tpl_filter = macro(name, val) => name != "foo";
|
||||||
result = name != "foo",
|
|
||||||
|
assert {
|
||||||
|
ok = filter identity_tpl_filter test_tpl == test_tpl,
|
||||||
|
desc = "filter identity_tpl_filter == test_tpl",
|
||||||
};
|
};
|
||||||
|
|
||||||
assert {
|
assert {
|
||||||
ok = filter identity_tpl_filter.result test_tpl == test_tpl,
|
ok = filter tpl_filter test_tpl == { quux = "baz" },
|
||||||
desc = "filter identity_tpl_filter.result test_tpl == test_tpl",
|
desc = "filter tpl_filter test_tpl == { quux = \"baz\" }",
|
||||||
};
|
};
|
||||||
|
|
||||||
assert {
|
let tpl_reducer = macro(acc, name, val) => acc{
|
||||||
ok = filter tpl_filter.result test_tpl == { quux = "baz" },
|
|
||||||
desc = "filter tpl_filter.result test_tpl == { quux = \"baz\" }",
|
|
||||||
};
|
|
||||||
|
|
||||||
let tpl_reducer = macro(acc, name, val) => {
|
|
||||||
result = acc{
|
|
||||||
keys = self.keys + [name],
|
keys = self.keys + [name],
|
||||||
vals = self.vals + [val],
|
vals = self.vals + [val],
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert {
|
assert {
|
||||||
ok = reduce tpl_reducer.result {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.result {keys = [], vals = []}, test_tpl == {keys = [\"foo\", \"quux\"], vals = [\"bar\", \"baz\"]}",
|
desc = "reduce tpl_reducer {keys = [], vals = []}, test_tpl == {keys = [\"foo\", \"quux\"], vals = [\"bar\", \"baz\"]}",
|
||||||
};
|
};
|
@ -75,4 +75,11 @@ let closure = macro(arg1) => {
|
|||||||
assert {
|
assert {
|
||||||
ok = closure("bar").result == "foobar",
|
ok = closure("bar").result == "foobar",
|
||||||
desc = "we closed over closed_over and got @" % (closure("bar").result),
|
desc = "we closed over closed_over and got @" % (closure("bar").result),
|
||||||
|
};
|
||||||
|
|
||||||
|
let concat = macro(arg1, arg2) => arg1 + arg2;
|
||||||
|
|
||||||
|
assert {
|
||||||
|
ok = concat("foo", "bar") == "foobar",
|
||||||
|
desc = "macros that aren't tuples work",
|
||||||
};
|
};
|
@ -379,7 +379,7 @@ impl<'a> From<&'a PositionedItem<String>> for PositionedItem<String> {
|
|||||||
pub struct MacroDef {
|
pub struct MacroDef {
|
||||||
pub scope: Option<Scope>,
|
pub scope: Option<Scope>,
|
||||||
pub argdefs: Vec<PositionedItem<String>>,
|
pub argdefs: Vec<PositionedItem<String>>,
|
||||||
pub fields: FieldList,
|
pub fields: Box<Expression>,
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,7 +484,6 @@ pub enum FuncOpDef {
|
|||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct ReduceOpDef {
|
pub struct ReduceOpDef {
|
||||||
pub mac: PositionedItem<String>,
|
pub mac: PositionedItem<String>,
|
||||||
pub field: PositionedItem<String>,
|
|
||||||
pub acc: Box<Expression>,
|
pub acc: Box<Expression>,
|
||||||
pub target: Box<Expression>,
|
pub target: Box<Expression>,
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
@ -494,7 +493,6 @@ pub struct ReduceOpDef {
|
|||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct MapFilterOpDef {
|
pub struct MapFilterOpDef {
|
||||||
pub mac: PositionedItem<String>,
|
pub mac: PositionedItem<String>,
|
||||||
pub field: PositionedItem<String>,
|
|
||||||
pub target: Box<Expression>,
|
pub target: Box<Expression>,
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ impl<'a> AstWalker<'a> {
|
|||||||
Expression::Grouped(ref mut expr) => {
|
Expression::Grouped(ref mut expr) => {
|
||||||
self.walk_expression(expr);
|
self.walk_expression(expr);
|
||||||
}
|
}
|
||||||
Expression::Macro(ref mut def) => self.walk_fieldset(&mut def.fields),
|
Expression::Macro(ref mut def) => self.walk_expression(def.fields.as_mut()),
|
||||||
Expression::Module(ref mut def) => {
|
Expression::Module(ref mut def) => {
|
||||||
self.walk_fieldset(&mut def.arg_set);
|
self.walk_fieldset(&mut def.arg_set);
|
||||||
for stmt in def.statements.iter_mut() {
|
for stmt in def.statements.iter_mut() {
|
||||||
|
162
src/build/mod.rs
162
src/build/mod.rs
@ -55,7 +55,7 @@ impl MacroDef {
|
|||||||
root: PathBuf,
|
root: PathBuf,
|
||||||
parent_builder: &FileBuilder,
|
parent_builder: &FileBuilder,
|
||||||
mut args: Vec<Rc<Val>>,
|
mut args: Vec<Rc<Val>>,
|
||||||
) -> Result<Vec<(PositionedItem<String>, Rc<Val>)>, Box<dyn Error>> {
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
||||||
// Error conditions. If the args don't match the length and types of the argdefs then this is
|
// Error conditions. If the args don't match the length and types of the argdefs then this is
|
||||||
// macro call error.
|
// macro call error.
|
||||||
if args.len() > self.argdefs.len() {
|
if args.len() > self.argdefs.len() {
|
||||||
@ -84,15 +84,7 @@ impl MacroDef {
|
|||||||
}
|
}
|
||||||
// We clobber anything that used to be in the scope with the arguments.
|
// We clobber anything that used to be in the scope with the arguments.
|
||||||
b.merge_build_output(build_output, true);
|
b.merge_build_output(build_output, true);
|
||||||
let mut result: Vec<(PositionedItem<String>, Rc<Val>)> = Vec::new();
|
Ok(b.eval_expr(self.fields.as_ref(), &b.scope.spawn_child())?)
|
||||||
for &(ref key, ref expr) in self.fields.iter() {
|
|
||||||
// We clone the expressions here because this macro may be consumed
|
|
||||||
// multiple times in the future.
|
|
||||||
let scope = b.scope.spawn_child();
|
|
||||||
let val = b.eval_expr(expr, &scope)?;
|
|
||||||
result.push((key.into(), val.clone()));
|
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1112,8 +1104,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
for arg in args.iter() {
|
for arg in args.iter() {
|
||||||
argvals.push(self.eval_expr(arg, scope)?);
|
argvals.push(self.eval_expr(arg, scope)?);
|
||||||
}
|
}
|
||||||
let fields = m.eval(self.file.clone(), self, argvals)?;
|
return Ok(m.eval(self.file.clone(), self, argvals)?);
|
||||||
return Ok(Rc::new(Val::Tuple(fields)));
|
|
||||||
}
|
}
|
||||||
Err(Box::new(error::BuildError::new(
|
Err(Box::new(error::BuildError::new(
|
||||||
// We should pretty print the selectors here.
|
// We should pretty print the selectors here.
|
||||||
@ -1195,28 +1186,25 @@ impl<'a> FileBuilder<'a> {
|
|||||||
&self,
|
&self,
|
||||||
elems: &Vec<Rc<Val>>,
|
elems: &Vec<Rc<Val>>,
|
||||||
def: &MacroDef,
|
def: &MacroDef,
|
||||||
outfield: &PositionedItem<String>,
|
|
||||||
typ: ProcessingOpType,
|
typ: ProcessingOpType,
|
||||||
) -> Result<Rc<Val>, Box<dyn Error>> {
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
for item in elems.iter() {
|
for item in elems.iter() {
|
||||||
let argvals = vec![item.clone()];
|
let argvals = vec![item.clone()];
|
||||||
let fields = def.eval(self.file.clone(), self, argvals)?;
|
let val = def.eval(self.file.clone(), self, argvals)?;
|
||||||
if let Some(v) = find_in_fieldlist(&outfield.val, &fields) {
|
match typ {
|
||||||
match typ {
|
ProcessingOpType::Map => {
|
||||||
ProcessingOpType::Map => {
|
out.push(val.clone());
|
||||||
out.push(v.clone());
|
}
|
||||||
}
|
ProcessingOpType::Filter => {
|
||||||
ProcessingOpType::Filter => {
|
if let &Val::Empty = val.as_ref() {
|
||||||
if let &Val::Empty = v.as_ref() {
|
// noop
|
||||||
// noop
|
continue;
|
||||||
continue;
|
} else if let &Val::Boolean(false) = val.as_ref() {
|
||||||
} else if let &Val::Boolean(false) = v.as_ref() {
|
// noop
|
||||||
// noop
|
continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
out.push(item.clone());
|
|
||||||
}
|
}
|
||||||
|
out.push(item.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1227,77 +1215,65 @@ impl<'a> FileBuilder<'a> {
|
|||||||
&self,
|
&self,
|
||||||
fs: &Vec<(PositionedItem<String>, Rc<Val>)>,
|
fs: &Vec<(PositionedItem<String>, Rc<Val>)>,
|
||||||
def: &MacroDef,
|
def: &MacroDef,
|
||||||
outfield: &PositionedItem<String>,
|
|
||||||
typ: ProcessingOpType,
|
typ: ProcessingOpType,
|
||||||
) -> Result<Rc<Val>, Box<dyn Error>> {
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
||||||
let mut out = Vec::new();
|
let mut out = Vec::new();
|
||||||
for &(ref name, ref val) in fs {
|
for &(ref name, ref val) in fs {
|
||||||
let argvals = vec![Rc::new(Val::Str(name.val.clone())), val.clone()];
|
let argvals = vec![Rc::new(Val::Str(name.val.clone())), val.clone()];
|
||||||
let fields = def.eval(self.file.clone(), self, argvals)?;
|
let result = def.eval(self.file.clone(), self, argvals)?;
|
||||||
if let Some(v) = find_in_fieldlist(&outfield.val, &fields) {
|
match typ {
|
||||||
match typ {
|
ProcessingOpType::Map => {
|
||||||
ProcessingOpType::Map => {
|
if let &Val::List(ref fs) = result.as_ref() {
|
||||||
if let &Val::List(ref fs) = v.as_ref() {
|
if fs.len() == 2 {
|
||||||
if fs.len() == 2 {
|
// index 0 should be a string for the new field name.
|
||||||
// index 0 should be a string for the new field name.
|
// index 1 should be the val.
|
||||||
// index 1 should be the val.
|
let new_name = if let &Val::Str(ref s) = fs[0].as_ref() {
|
||||||
let new_name = if let &Val::Str(ref s) = fs[0].as_ref() {
|
s.clone()
|
||||||
s.clone()
|
|
||||||
} else {
|
|
||||||
return Err(Box::new(error::BuildError::new(
|
|
||||||
format!(
|
|
||||||
"map on tuple expects the first item out list to be a string but got size {}",
|
|
||||||
fs[0].type_name()
|
|
||||||
),
|
|
||||||
error::ErrorType::TypeFail,
|
|
||||||
def.pos.clone(),
|
|
||||||
)));
|
|
||||||
};
|
|
||||||
out.push((
|
|
||||||
PositionedItem::new(new_name, name.pos.clone()),
|
|
||||||
fs[1].clone(),
|
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
return Err(Box::new(error::BuildError::new(
|
return Err(Box::new(error::BuildError::new(
|
||||||
format!(
|
format!(
|
||||||
"map on a tuple field expects a list of size 2 as output but got size {}",
|
"map on tuple expects the first item out list to be a string but got size {}",
|
||||||
fs.len()
|
fs[0].type_name()
|
||||||
),
|
),
|
||||||
error::ErrorType::TypeFail,
|
error::ErrorType::TypeFail,
|
||||||
def.pos.clone(),
|
def.pos.clone(),
|
||||||
)));
|
)));
|
||||||
}
|
};
|
||||||
|
out.push((
|
||||||
|
PositionedItem::new(new_name, name.pos.clone()),
|
||||||
|
fs[1].clone(),
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
return Err(Box::new(error::BuildError::new(
|
return Err(Box::new(error::BuildError::new(
|
||||||
format!(
|
format!(
|
||||||
"map on a tuple field expects a list as output but got {:?}",
|
"map on a tuple field expects a list of size 2 as output but got size {}",
|
||||||
v.type_name()
|
fs.len()
|
||||||
),
|
),
|
||||||
error::ErrorType::TypeFail,
|
error::ErrorType::TypeFail,
|
||||||
def.pos.clone(),
|
def.pos.clone(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
ProcessingOpType::Filter => {
|
return Err(Box::new(error::BuildError::new(
|
||||||
if let &Val::Empty = v.as_ref() {
|
format!(
|
||||||
// noop
|
"map on a tuple field expects a list as output but got {:?}",
|
||||||
continue;
|
result.type_name()
|
||||||
} else if let &Val::Boolean(false) = v.as_ref() {
|
),
|
||||||
// noop
|
error::ErrorType::TypeFail,
|
||||||
continue;
|
def.pos.clone(),
|
||||||
}
|
)));
|
||||||
out.push((name.clone(), val.clone()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
ProcessingOpType::Filter => {
|
||||||
return Err(Box::new(error::BuildError::new(
|
if let &Val::Empty = result.as_ref() {
|
||||||
format!(
|
// noop
|
||||||
"Result {} field does not exist in macro body!",
|
continue;
|
||||||
outfield.val
|
} else if let &Val::Boolean(false) = result.as_ref() {
|
||||||
),
|
// noop
|
||||||
error::ErrorType::NoSuchSymbol,
|
continue;
|
||||||
def.pos.clone(),
|
}
|
||||||
)));
|
out.push((name.clone(), val.clone()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Rc::new(Val::Tuple(out)))
|
Ok(Rc::new(Val::Tuple(out)))
|
||||||
@ -1321,16 +1297,8 @@ impl<'a> FileBuilder<'a> {
|
|||||||
&Val::List(ref elems) => {
|
&Val::List(ref elems) => {
|
||||||
for item in elems.iter() {
|
for item in elems.iter() {
|
||||||
let argvals = vec![acc.clone(), item.clone()];
|
let argvals = vec![acc.clone(), item.clone()];
|
||||||
let fields = macdef.eval(self.file.clone(), self, argvals)?;
|
let result = macdef.eval(self.file.clone(), self, argvals)?;
|
||||||
if let Some(v) = find_in_fieldlist(&def.field.val, &fields) {
|
acc = result;
|
||||||
acc = v.clone();
|
|
||||||
} else {
|
|
||||||
return Err(Box::new(error::BuildError::new(
|
|
||||||
format!("Result {} field does not exist in macro body!", def.field),
|
|
||||||
error::ErrorType::NoSuchSymbol,
|
|
||||||
def.pos.clone(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&Val::Tuple(ref fs) => {
|
&Val::Tuple(ref fs) => {
|
||||||
@ -1340,16 +1308,8 @@ impl<'a> FileBuilder<'a> {
|
|||||||
Rc::new(Val::Str(name.val.clone())),
|
Rc::new(Val::Str(name.val.clone())),
|
||||||
val.clone(),
|
val.clone(),
|
||||||
];
|
];
|
||||||
let fields = macdef.eval(self.file.clone(), self, argvals)?;
|
let result = macdef.eval(self.file.clone(), self, argvals)?;
|
||||||
if let Some(v) = find_in_fieldlist(&def.field.val, &fields) {
|
acc = result;
|
||||||
acc = v.clone();
|
|
||||||
} else {
|
|
||||||
return Err(Box::new(error::BuildError::new(
|
|
||||||
format!("Result field {}does not exist in macro body!", def.field),
|
|
||||||
error::ErrorType::NoSuchSymbol,
|
|
||||||
def.pos.clone(),
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
@ -1385,12 +1345,8 @@ impl<'a> FileBuilder<'a> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
return match maybe_target.as_ref() {
|
return match maybe_target.as_ref() {
|
||||||
&Val::List(ref elems) => {
|
&Val::List(ref elems) => self.eval_functional_list_processing(elems, macdef, typ),
|
||||||
self.eval_functional_list_processing(elems, macdef, &def.field, typ)
|
&Val::Tuple(ref fs) => self.eval_functional_tuple_processing(fs, macdef, typ),
|
||||||
}
|
|
||||||
&Val::Tuple(ref fs) => {
|
|
||||||
self.eval_functional_tuple_processing(fs, macdef, &def.field, typ)
|
|
||||||
}
|
|
||||||
other => Err(Box::new(error::BuildError::new(
|
other => Err(Box::new(error::BuildError::new(
|
||||||
format!(
|
format!(
|
||||||
"Expected List or Tuple as target but got {:?}",
|
"Expected List or Tuple as target but got {:?}",
|
||||||
|
@ -250,13 +250,10 @@ fn test_macro_hermetic() {
|
|||||||
.or_insert(Rc::new(Val::Macro(MacroDef {
|
.or_insert(Rc::new(Val::Macro(MacroDef {
|
||||||
scope: None,
|
scope: None,
|
||||||
argdefs: vec![value_node!("arg2".to_string(), Position::new(1, 0, 0))],
|
argdefs: vec![value_node!("arg2".to_string(), Position::new(1, 0, 0))],
|
||||||
fields: vec![(
|
fields: Box::new(Expression::Simple(Value::Symbol(value_node!(
|
||||||
make_tok!("foo", Position::new(1, 1, 1)),
|
"arg1".to_string(),
|
||||||
Expression::Simple(Value::Symbol(value_node!(
|
Position::new(1, 1, 1)
|
||||||
"arg1".to_string(),
|
)))),
|
||||||
Position::new(1, 1, 1)
|
|
||||||
))),
|
|
||||||
)],
|
|
||||||
pos: Position::new(1, 0, 0),
|
pos: Position::new(1, 0, 0),
|
||||||
})));
|
})));
|
||||||
test_expr_to_val(
|
test_expr_to_val(
|
||||||
|
@ -343,10 +343,9 @@ make_fn!(
|
|||||||
);
|
);
|
||||||
|
|
||||||
fn tuple_to_macro<'a>(
|
fn tuple_to_macro<'a>(
|
||||||
input: SliceIter<'a, Token>,
|
|
||||||
pos: Position,
|
pos: Position,
|
||||||
vals: Option<Vec<Value>>,
|
vals: Option<Vec<Value>>,
|
||||||
val: Value,
|
val: Expression,
|
||||||
) -> ConvertResult<'a, Expression> {
|
) -> ConvertResult<'a, Expression> {
|
||||||
let mut default_args = match vals {
|
let mut default_args = match vals {
|
||||||
None => Vec::new(),
|
None => Vec::new(),
|
||||||
@ -359,18 +358,12 @@ fn tuple_to_macro<'a>(
|
|||||||
val: s.to_string(),
|
val: s.to_string(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
match val {
|
Ok(Expression::Macro(MacroDef {
|
||||||
Value::Tuple(v) => Ok(Expression::Macro(MacroDef {
|
scope: None,
|
||||||
scope: None,
|
argdefs: arglist,
|
||||||
argdefs: arglist,
|
fields: Box::new(val),
|
||||||
fields: v.val,
|
pos: pos,
|
||||||
pos: pos,
|
}))
|
||||||
})),
|
|
||||||
val => Err(Error::new(
|
|
||||||
format!("Expected Tuple Got {:?}", val),
|
|
||||||
Box::new(input.clone()),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
make_fn!(
|
make_fn!(
|
||||||
@ -417,23 +410,21 @@ fn macro_expression(input: SliceIter<Token>) -> Result<SliceIter<Token>, Express
|
|||||||
arglist => trace_parse!(optional!(arglist)),
|
arglist => trace_parse!(optional!(arglist)),
|
||||||
_ => must!(punct!(")")),
|
_ => must!(punct!(")")),
|
||||||
_ => must!(punct!("=>")),
|
_ => must!(punct!("=>")),
|
||||||
map => trace_parse!(tuple),
|
map => trace_parse!(expression),
|
||||||
(pos, arglist, map)
|
(pos, arglist, map)
|
||||||
);
|
);
|
||||||
match parsed {
|
match parsed {
|
||||||
Result::Abort(e) => Result::Abort(e),
|
Result::Abort(e) => Result::Abort(e),
|
||||||
Result::Fail(e) => Result::Fail(e),
|
Result::Fail(e) => Result::Fail(e),
|
||||||
Result::Incomplete(offset) => Result::Incomplete(offset),
|
Result::Incomplete(offset) => Result::Incomplete(offset),
|
||||||
Result::Complete(rest, (pos, arglist, map)) => {
|
Result::Complete(rest, (pos, arglist, map)) => match tuple_to_macro(pos, arglist, map) {
|
||||||
match tuple_to_macro(rest.clone(), pos, arglist, map) {
|
Ok(expr) => Result::Complete(rest, expr),
|
||||||
Ok(expr) => Result::Complete(rest, expr),
|
Err(e) => Result::Fail(Error::caused_by(
|
||||||
Err(e) => Result::Fail(Error::caused_by(
|
"Invalid Macro syntax",
|
||||||
"Invalid Macro syntax",
|
Box::new(e),
|
||||||
Box::new(e),
|
Box::new(rest.clone()),
|
||||||
Box::new(rest.clone()),
|
)),
|
||||||
)),
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -574,14 +565,11 @@ make_fn!(
|
|||||||
pos => pos,
|
pos => pos,
|
||||||
_ => word!("reduce"),
|
_ => word!("reduce"),
|
||||||
macroname => match_type!(BAREWORD),
|
macroname => match_type!(BAREWORD),
|
||||||
_ => punct!("."),
|
|
||||||
outfield => match_type!(BAREWORD),
|
|
||||||
acc => trace_parse!(non_op_expression),
|
acc => trace_parse!(non_op_expression),
|
||||||
_ => punct!(","),
|
_ => punct!(","),
|
||||||
tgt => trace_parse!(non_op_expression),
|
tgt => trace_parse!(non_op_expression),
|
||||||
(Expression::FuncOp(FuncOpDef::Reduce(ReduceOpDef{
|
(Expression::FuncOp(FuncOpDef::Reduce(ReduceOpDef{
|
||||||
mac: (¯oname).into(),
|
mac: (¯oname).into(),
|
||||||
field: (&outfield).into(),
|
|
||||||
acc: Box::new(acc),
|
acc: Box::new(acc),
|
||||||
target: Box::new(tgt),
|
target: Box::new(tgt),
|
||||||
pos: pos,
|
pos: pos,
|
||||||
@ -596,12 +584,9 @@ make_fn!(
|
|||||||
_ => word!("map"),
|
_ => word!("map"),
|
||||||
// TODO This should become just a bareword symbol now
|
// TODO This should become just a bareword symbol now
|
||||||
macroname => match_type!(BAREWORD),
|
macroname => match_type!(BAREWORD),
|
||||||
_ => punct!("."),
|
|
||||||
outfield => match_type!(BAREWORD),
|
|
||||||
list => trace_parse!(non_op_expression),
|
list => trace_parse!(non_op_expression),
|
||||||
(Expression::FuncOp(FuncOpDef::Map(MapFilterOpDef{
|
(Expression::FuncOp(FuncOpDef::Map(MapFilterOpDef{
|
||||||
mac: (¯oname).into(),
|
mac: (¯oname).into(),
|
||||||
field: (&outfield).into(),
|
|
||||||
target: Box::new(list),
|
target: Box::new(list),
|
||||||
pos: pos,
|
pos: pos,
|
||||||
})))
|
})))
|
||||||
@ -615,12 +600,9 @@ make_fn!(
|
|||||||
_ => word!("filter"),
|
_ => word!("filter"),
|
||||||
// TODO This should become just a bareword symbol now
|
// TODO This should become just a bareword symbol now
|
||||||
macroname => match_type!(BAREWORD),
|
macroname => match_type!(BAREWORD),
|
||||||
_ => punct!("."),
|
|
||||||
outfield => match_type!(BAREWORD),
|
|
||||||
list => trace_parse!(non_op_expression),
|
list => trace_parse!(non_op_expression),
|
||||||
(Expression::FuncOp(FuncOpDef::Filter(MapFilterOpDef{
|
(Expression::FuncOp(FuncOpDef::Filter(MapFilterOpDef{
|
||||||
mac: (¯oname).into(),
|
mac: (¯oname).into(),
|
||||||
field: (&outfield).into(),
|
|
||||||
target: Box::new(list),
|
target: Box::new(list),
|
||||||
pos: pos,
|
pos: pos,
|
||||||
})))
|
})))
|
||||||
|
@ -1,23 +1,17 @@
|
|||||||
let maybe = module{
|
let maybe = module{
|
||||||
val = NULL,
|
val = NULL,
|
||||||
} => {
|
} => {
|
||||||
let do = macro(op) => {
|
let do = macro(op) => select (mod.val != NULL), NULL, {
|
||||||
result = select (mod.val != NULL), NULL, {
|
true = op(mod.val),
|
||||||
true = op(mod.val).result,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let if = module{
|
let if = module{
|
||||||
test = false,
|
test = false,
|
||||||
} => {
|
} => {
|
||||||
let do = macro(op, arg) => {
|
let do = macro(op, arg) => select mod.test, arg, {
|
||||||
result = select mod.test, arg, {
|
true = op(arg),
|
||||||
true = op(arg).result,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let identity = macro(arg) => {
|
let identity = macro(arg) => arg;
|
||||||
result = arg,
|
|
||||||
};
|
|
@ -2,38 +2,32 @@ let str_join = module{
|
|||||||
sep=" ",
|
sep=" ",
|
||||||
list=NULL,
|
list=NULL,
|
||||||
} => {
|
} => {
|
||||||
let joiner = macro(acc, item) => {
|
let joiner = macro(acc, item) => select (acc.out == ""), NULL, {
|
||||||
result = select (acc.out == ""), NULL, {
|
true = acc{
|
||||||
true = acc{
|
out="@@" % (acc.out,item),
|
||||||
out="@@" % (acc.out,item),
|
},
|
||||||
},
|
false = acc{
|
||||||
false = acc{
|
out="@@@" % (acc.out, acc.sep, item),
|
||||||
out="@@@" % (acc.out, acc.sep, item),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = (reduce joiner.result {sep=mod.sep, out=""}, (mod.list)).out;
|
let result = (reduce joiner {sep=mod.sep, out=""}, (mod.list)).out;
|
||||||
};
|
};
|
||||||
|
|
||||||
let len = module{
|
let len = module{
|
||||||
list = NULL,
|
list = NULL,
|
||||||
} => {
|
} => {
|
||||||
let counter = macro(acc, item) => {
|
let counter = macro(acc, item) => acc + 1;
|
||||||
result = acc + 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = reduce counter.result 0, (mod.list);
|
let result = reduce counter 0, (mod.list);
|
||||||
};
|
};
|
||||||
|
|
||||||
let reverse = module{
|
let reverse = module{
|
||||||
list = NULL,
|
list = NULL,
|
||||||
} => {
|
} => {
|
||||||
let reducer = macro(acc, item) => {
|
let reducer = macro(acc, item) => [item] + acc;
|
||||||
result = [item] + acc,
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = reduce reducer.result [], (mod.list);
|
let result = reduce reducer [], (mod.list);
|
||||||
};
|
};
|
||||||
|
|
||||||
let enumerate = module{
|
let enumerate = module{
|
||||||
@ -41,13 +35,14 @@ let enumerate = module{
|
|||||||
step = 1,
|
step = 1,
|
||||||
list = NULL,
|
list = NULL,
|
||||||
} => {
|
} => {
|
||||||
let reducer = macro(acc, item) => {
|
let reducer = macro(acc, item) => acc{
|
||||||
result = acc{count = acc.count + acc.step, list=acc.list + [[acc.count, item]]},
|
count = acc.count + acc.step,
|
||||||
|
list=acc.list + [[acc.count, item]],
|
||||||
};
|
};
|
||||||
|
|
||||||
let acc = {count=mod.start, list=[], step=mod.step};
|
let acc = {count=mod.start, list=[], step=mod.step};
|
||||||
|
|
||||||
let enumerated = reduce reducer.result acc, (mod.list);
|
let enumerated = reduce reducer acc, (mod.list);
|
||||||
let result = enumerated.list;
|
let result = enumerated.list;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -55,23 +50,19 @@ let zip = module{
|
|||||||
list1 = NULL,
|
list1 = NULL,
|
||||||
list2 = NULL,
|
list2 = NULL,
|
||||||
} => {
|
} => {
|
||||||
let counter = macro(acc, item) => {
|
let counter = macro(acc, item) => acc + 1;
|
||||||
result = acc + 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
let len1 = reduce counter.result 0, (mod.list1);
|
let len1 = reduce counter 0, (mod.list1);
|
||||||
let len2 = reduce counter.result 0, (mod.list2);
|
let len2 = reduce counter 0, (mod.list2);
|
||||||
|
|
||||||
let rng = select (len1 >= len2), NULL, {
|
let rng = select (len1 >= len2), NULL, {
|
||||||
true = 0:(len1 - 1),
|
true = 0:(len1 - 1),
|
||||||
false = 0:(len2 - 1),
|
false = 0:(len2 - 1),
|
||||||
};
|
};
|
||||||
|
|
||||||
let reducer = macro(acc, item) => {
|
let reducer = macro(acc, item) => acc{
|
||||||
result = acc{
|
result = acc.result + [[acc.list1.(item), acc.list2.(item)]],
|
||||||
result = acc.result + [[acc.list1.(item), acc.list2.(item)]],
|
idxs = acc.idxs + [item]
|
||||||
idxs = acc.idxs + [item]
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let acc = {
|
let acc = {
|
||||||
@ -81,5 +72,5 @@ let zip = module{
|
|||||||
idxs = [],
|
idxs = [],
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = (reduce reducer.result acc, rng).result;
|
let result = (reduce reducer acc, rng).result;
|
||||||
};
|
};
|
@ -1,31 +1,29 @@
|
|||||||
let t = (import "std/testing.ucg").asserts{};
|
let t = (import "std/testing.ucg").asserts{};
|
||||||
let f = (import "std/functional.ucg");
|
let f = (import "std/functional.ucg");
|
||||||
|
|
||||||
let op = macro(arg) => {
|
let op = macro(arg) => arg{foo="bar"};
|
||||||
result = arg{foo="bar"},
|
|
||||||
};
|
|
||||||
|
|
||||||
assert t.equal{
|
assert t.equal{
|
||||||
left = f.maybe{val=NULL}.do(op).result,
|
left = f.maybe{val=NULL}.do(op),
|
||||||
right = NULL,
|
right = NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert t.equal{
|
assert t.equal{
|
||||||
left = f.maybe{val={}}.do(op).result,
|
left = f.maybe{val={}}.do(op),
|
||||||
right = {foo="bar"},
|
right = {foo="bar"},
|
||||||
};
|
};
|
||||||
|
|
||||||
assert t.equal{
|
assert t.equal{
|
||||||
left = f.if{test=true}.do(op, {}).result,
|
left = f.if{test=true}.do(op, {}),
|
||||||
right = {foo="bar"},
|
right = {foo="bar"},
|
||||||
};
|
};
|
||||||
|
|
||||||
assert t.equal{
|
assert t.equal{
|
||||||
left = f.if{test=false}.do(op, {}).result,
|
left = f.if{test=false}.do(op, {}),
|
||||||
right = {},
|
right = {},
|
||||||
};
|
};
|
||||||
|
|
||||||
assert t.equal{
|
assert t.equal{
|
||||||
left = f.identity("foo").result,
|
left = f.identity("foo"),
|
||||||
right = "foo",
|
right = "foo",
|
||||||
};
|
};
|
@ -3,40 +3,32 @@
|
|||||||
let fields = module{
|
let fields = module{
|
||||||
tpl = NULL,
|
tpl = NULL,
|
||||||
} => {
|
} => {
|
||||||
let reducer = macro(acc, field, value) => {
|
let reducer = macro(acc, field, value) => acc + [field];
|
||||||
result = acc + [field],
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = reduce reducer.result [], (mod.tpl);
|
let result = reduce reducer [], (mod.tpl);
|
||||||
};
|
};
|
||||||
|
|
||||||
// return a list of the values in a tuple.
|
// return a list of the values in a tuple.
|
||||||
let values = module{
|
let values = module{
|
||||||
tpl = NULL,
|
tpl = NULL,
|
||||||
} => {
|
} => {
|
||||||
let reducer = macro(acc, field, value) => {
|
let reducer = macro(acc, field, value) => acc + [value];
|
||||||
result = acc + [value],
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = reduce reducer.result [], (mod.tpl);
|
let result = reduce reducer [], (mod.tpl);
|
||||||
};
|
};
|
||||||
|
|
||||||
let iter = module{
|
let iter = module{
|
||||||
tpl = NULL,
|
tpl = NULL,
|
||||||
} => {
|
} => {
|
||||||
let reducer = macro(acc, field, value) => {
|
let reducer = macro(acc, field, value) => acc + [[field, value]];
|
||||||
result = acc + [[field, value]],
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = reduce reducer.result [], (mod.tpl);
|
let result = reduce reducer [], (mod.tpl);
|
||||||
};
|
};
|
||||||
|
|
||||||
let strip_nulls = module{
|
let strip_nulls = module{
|
||||||
tpl = NULL,
|
tpl = NULL,
|
||||||
} => {
|
} => {
|
||||||
let filterer = macro(name, value) => {
|
let filterer = macro(name, value) => value != NULL;
|
||||||
result = value != NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = filter filterer.result (mod.tpl);
|
let result = filter filterer (mod.tpl);
|
||||||
};
|
};
|
Loading…
x
Reference in New Issue
Block a user