DOCS: Update docs with a bunch of fixes.

This commit is contained in:
Jeremy Wall 2019-01-24 20:05:08 -06:00
parent f1c7d76fdd
commit 44055c28e9
4 changed files with 46 additions and 35 deletions

View File

@ -18,11 +18,11 @@ let container_conf = {
};
```
Then we can define a helper macro for creating our host and port mappings
Then we can define a helper function for creating our host and port mappings
```
// A little helper macro for creating our host and port mappings.
let map_to_container = macro (host, container) => {
// A little helper func for creating our host and port mappings.
let map_to_container = func (host, container) => {
result = "@:@" % (host, container)
};

View File

@ -24,8 +24,12 @@ Some words are reserved in UCG and can not be used as a named binding.
* let
* import
* as
* in
* is
* not
* fail
* select
* macro
* func
* module
* env
* map

View File

@ -172,7 +172,7 @@ The type must be a string literal matching one of:
* `"float"`
* `"tuple"`
* `"list"`
* `"macro"`
* `"func"`
* `"module"`
```
@ -278,30 +278,28 @@ UCG can generate lists from a range with an optional step.
0:2:10 == [0, 2, 4, 6, 8, 10];
```
Macros
Functions
-----
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.
Functions 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 functions 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 function with the `function` keyword followed by the
arguments in parentheses, a `=>`, and then a valid expression.
```
let mymacro = macro (arg1, arg2) => {
let myfunc = func (arg1, arg2) => {
host = arg1,
port = arg2,
connstr = "couchdb://@:@" % (arg1, arg2),
};
let my_dbconf = mymacro("couchdb.example.org", "9090");
let my_dbconf = myfunc("couchdb.example.org", "9090");
let my_dbhost = dbconf.host;
let add = macro(arg1, arg2) => arg1 + arg2;
let add = func (arg1, arg2) => arg1 + arg2;
add(1, 1) == 2;
```
@ -312,29 +310,29 @@ UCG has a few functional processing expressions called `map`, `filter`, and
`reduce`. All of them can process a string, list, or tuple.
Their syntax starts with either `map` `filter`, or `reduce followed by a symbol
that references a valid macro and finally an expression that resolves to either
that references a valid func and finally an expression that resolves to either
a list or a tuple.
### Map expressions
Map macros should produce either a valid value or a list of [field, value] that
Map functions should produce either a valid value or a list of [field, value] that
will replace the element or field it is curently processing.
**For Lists**
When mapping a macro across a list the result field can be any valid value. The
macro is expected to take a single argument.
When mapping a function across a list the result field can be any valid value. The
function is expected to take a single argument.
```
let list1 = [1, 2, 3, 4];
let mapper = macro(item) => item + 1;
let mapper = func (item) => item + 1;
map mapper list1 == [2, 3, 4, 5];
```
**For Tuples**
Macros for mapping across a tuple are expected to take two arguments. The first
Functions 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
field. The result should be a two item list with the first item being the new
field name and the second item being the new value.
@ -344,7 +342,7 @@ let test_tpl = {
foo = "bar",
quux = "baz",
};
let tpl_mapper = macro(name, val) => select name, [name, val], {
let tpl_mapper = func (name, val) => select name, [name, val], {
"foo" = ["foo", "barbar"],
quux = ["cute", "pygmy"],
};
@ -361,7 +359,7 @@ the item or field staying in the resulting list or tuple.
```
let list2 = ["foo", "bar", "foo", "bar"];
let filtrator = macro(item) => select item, NULL, {
let filtrator = func (item) => select item, NULL, {
foo = item,
};
@ -375,13 +373,15 @@ let test_tpl = {
foo = "bar",
quux = "baz",
};
let tpl_filter = macro(name, val) => name != "foo";
let tpl_filter = func (name, val) => name != "foo";
filter tpl_filter test_tpl == { quux = "baz" };
```
### Reduce expressions
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.
Reduce expressions start with the reduce keyword followed by a symbol
referencing a func, an expression for the accumulator, and finally the tuple or
list to process.
**Tuples**
@ -390,7 +390,7 @@ let test_tpl = {
foo = "bar",
quux = "baz",
};
let tpl_reducer = macro(acc, name, val) => acc{
let tpl_reducer = func (acc, name, val) => acc{
keys = self.keys + [name],
vals = self.vals + [val],
};
@ -402,7 +402,7 @@ reduce tpl_reducer {keys = [], vals = []}, test_tpl == {keys = ["foo", "quux"],
```
let list1 = [1, 2, 3, 4];
let list_reducer = macro(acc, item) => acc + item;
let list_reducer = func (acc, item) => acc + item;
list_reducer 0, list1 == 0 + 1 + 2 + 3 + 4;
```
@ -452,7 +452,7 @@ let ifresult = select true, NULL, {
Modules
-------
UCG has another form of reusable execution that is a little more composable than macros
UCG has another form of reusable execution that is a little more robust than functions
are. Modules allow you to parameterize a set of statements and build the statements
later. Modules are an expression. They can be bound to a value and then reused later.
Modules do not close over their environment by they can import other UCG files into
@ -468,7 +468,7 @@ The body of the module can contain any valid UCG statement.
let top_mod = module {
deep_value = "None",
} => {
import "shared.UCG" as shared_macros;
let shared_funcs = import "shared.UCG";
let embedded_def = module {
deep_value = "None",

View File

@ -48,7 +48,7 @@ let_keyword: "let" ;
import_keyword: "import" ;
include_keyword: "include" ;
as_keyword: "as" ;
macro_keyword: "macro" ;
func_keyword: "func" ;
map_keyword: "map" ;
reduce_keyword: "map" ;
filter_keyword: "filter" ;
@ -60,6 +60,7 @@ fail_keyword: "fail" ;
null_keyword: "NULL" ;
in_keyword: "in" ;
is_keyword: "in" ;
not_keyword: "module" ;
escaped: "\", VISIBLE_CHAR ;
str: quot, { escaped | UTF8_CHAR }, quot ;
float: (DIGIT+, dot, { DIGIT }) | (dot, DIGIT+) ;
@ -108,11 +109,11 @@ literal: str | integer | float | list | tuple | null_keyword;
grouped: lparen, expr, rparen ;
```
#### Macro Definition
#### Function Definition
```
arglist: expr, { comma, expr }, [comma] ;
macro_def: macro_keyword, lparen, [ arglist ], rparen, fatcomma, tuple ;
func_def: func_keyword, lparen, [ arglist ], rparen, fatcomma, tuple ;
```
#### Module Definition
@ -167,13 +168,19 @@ import_expr: import_keyword, str ;
fail_expr: fail_keyword, (str | format_expr) ;
```
#### Not Expression
```
not_expr: not_keyword, expr ;
```
#### Non Operator Expression
```
non_operator_expr: literal
| grouped
| import_expr
| macrodef
| funcdef
| module_def
| fail_expr
| format_expr