REFACTOR: Cleanup the syntax for the select expr

This makes it both easier to correctly write a select expression
as well as easier to parse and report syntax errors.
This commit is contained in:
Jeremy Wall 2019-11-02 11:01:47 -05:00
parent f199e0f871
commit a90df8a362
20 changed files with 187 additions and 143 deletions

View File

@ -403,7 +403,7 @@ let test_tpl = {
foo = "bar",
quux = "baz",
};
let tpl_mapper = func (name, val) => select name, [name, val], {
let tpl_mapper = func (name, val) => select (name, [name, val]) => {
"foo" = ["foo", "barbar"],
quux = ["cute", "pygmy"],
};
@ -429,7 +429,7 @@ the item or field staying in the resulting list or tuple.
```
let list2 = ["foo", "bar", "foo", "bar"];
let filtrator = func (item) => select item, NULL, {
let filtrator = func (item) => select (item, NULL) {
foo = item,
};
@ -514,27 +514,27 @@ Conditionals
UCG supports a limited conditional expression called a select. A select
expression starts with the `select` keyword and is followed by a an expression
resolving to a string or boolean naming the field to select, an optional
expression resolving to the default value, and finally a tuple literal to
select the field from. If the field selected is not in the tuple then the
default value will be used. If no default is specified then select will
throw a compile failure for the unhandled case.
expression resolving to the default value in parenthesis a `=>` and finally a
tuple literal to select the field from. If the field selected is not in the
tuple then the default value will be used. If no default is specified then
select will throw a compile failure for the unhandled case.
```
let want = "baz";
// field default
select want, "quux", {
select (want, "quux") => {
baz = "foo",
fuzz = "bang",
}; // result will be "foo"
// field default
select "quack", "quux", {
select ("quack", "quux") => {
baz = "foo",
fuzz = "bang",
}; // result will be "quux"
let ifresult = select true, {
let ifresult = select (true) => {
true = "true result",
false = "false result",
}; // result will be "true result"

View File

@ -49,6 +49,7 @@ import_keyword: "import" ;
include_keyword: "include" ;
as_keyword: "as" ;
func_keyword: "func" ;
select_keyword: "select" ;
map_keyword: "map" ;
reduce_keyword: "map" ;
filter_keyword: "filter" ;
@ -111,6 +112,12 @@ literal: str | integer | float | list | tuple | null_keyword;
grouped: lparen, expr, rparen ;
```
#### Select expressions
```
select_expr: select_keyword, lparen, expr, [comma, expr], fatcomma, tuple ;
```
#### Function Definition
```
@ -189,6 +196,7 @@ trace_expr: trace_keyword, expr ;
```
non_operator_expr: literal
| grouped
| select_def
| import_expr
| funcdef
| module_def

View File

@ -121,8 +121,8 @@ assert {
};
let want = "foo";
assert {
ok = select want, 1, { foo=2, } == 2,
desc = "select want, 1, { foo=2, } == 2",
ok = select (want, 1) => { foo=2, } == 2,
desc = "select (want, 1) => { foo=2, } == 2",
};
// Contains comparison operators.

View File

@ -4,7 +4,7 @@ let list1 = [1, 2, 3, 4];
let list2 = ["foo", "bar", "foo", "bar"];
let mapper = func (item) => item + 1;
let filtrator = func (item) => select item, NULL, {
let filtrator = func (item) => select (item, NULL) => {
foo = item,
};
@ -84,7 +84,7 @@ assert {
desc = "map(identity_tpl_mapper, test_tpl) == test_tpl",
};
let tpl_mapper = func (name, val) => select name, [name, val], {
let tpl_mapper = func (name, val) => select (name, [name, val]) => {
"foo" = ["foo", "barbar"],
quux = ["cute", "pygmy"],
};

View File

@ -33,7 +33,7 @@ let embedded_mod = module {
deep_value = "None",
environ = "None",
} => {
let env_name = select mod.environ, "qa", {
let env_name = select (mod.environ, "qa") => {
None = "qa",
prod = "prod",
qa = "qa",
@ -113,7 +113,7 @@ assert t.equal{
let recursive_module = module {start=1, end=10} => (result) {
let this = mod.this;
let result = select mod.start != mod.end, mod.start, {
let result = select (mod.start != mod.end, mod.start) => {
true = this{start=mod.start + 1, end=mod.end},
};
};

View File

@ -1,12 +1,12 @@
let goodwant = "door1";
let badwant = "door4";
let got = select goodwant, "OOPS", {
let got = select (goodwant, "OOPS") => {
door1 = "grand prize",
door2 = "you lose",
};
let defaultgot = select badwant, "OOPS", {
let defaultgot = select (badwant, "OOPS") => {
door1 = "grand prize",
door2 = "you lose",
};
@ -23,7 +23,7 @@ assert {
// select inside a function
let condfunc = func (arg) => {
output = select arg, NULL, {
output = select (arg, NULL) => {
opt1 = "yay",
opt2 = "boo",
},
@ -44,7 +44,7 @@ assert {
desc = "condfunc(\"invalid\") == {output = NULL}",
};
let iflike = select true, "default", {
let iflike = select (true, "default") => {
true = "I was true",
false = "I was false",
};
@ -54,7 +54,7 @@ assert {
desc = "iflike == \"I was true\"",
};
let no_default_test = func(b) => select b, {
let no_default_test = func(b) => select (b) => {
true = "I was true",
false = "I was false",
};
@ -73,7 +73,7 @@ let tpl_fields = {
test = "foo",
};
let no_default_select_result = select tpl_fields.test == "foo", {
let no_default_select_result = select (tpl_fields.test == "foo") => {
true = true,
false = false,
};
@ -83,9 +83,9 @@ assert {
desc = "no default successfully select true",
};
let nested_select_result_no_defaults = select true, {
let nested_select_result_no_defaults = select (true) => {
true = true,
false = select false, {
false = select (false) => {
true = fail "whoops! this should never have triggered.",
false = true,
},

View File

@ -516,12 +516,13 @@ where
}
}
write!(self.w, "select ")?;
write!(self.w, "(")?;
self.render_expr(&_def.val)?;
write!(self.w, ", ")?;
if let Some(ref e) = _def.default {
self.render_expr(e)?;
write!(self.w, ", ")?;
self.render_expr(e)?;
}
write!(self.w, ") => ")?;
self.render_tuple_def(&_def.tuple)?;
}
Expression::Simple(ref _def) => {

View File

@ -149,13 +149,13 @@ fn test_out_expr_printing() {
#[test]
fn test_select_expr_no_default_printing() {
let input = "select true, {\n true = 1,\n false = 2,\n};";
let input = "select (true) => {\n true = 1,\n false = 2,\n};";
assert_eq!(print_to_buffer(input), format!("{}\n", input));
}
#[test]
fn test_select_expr_with_default_printing() {
let input = "select true, 3, {\n true = 1,\n false = 2,\n};";
let input = "select (true, 3) => {\n true = 1,\n false = 2,\n};";
assert_eq!(print_to_buffer(input), format!("{}\n", input));
}

View File

@ -550,10 +550,10 @@ fn test_func_call_wrong_argument_type_compile_failure() {
#[test]
fn test_select_missed_case_string_no_default_compile_failure() {
assert_build_failure(
"select \"a\", { b = 1, };",
"select (\"a\") => { b = 1, };",
vec![
Regex::new(r"Unhandled select case with no default").unwrap(),
Regex::new(r"line: 1 column: 8").unwrap(),
Regex::new(r"line: 1 column: 9").unwrap(),
],
)
}
@ -561,10 +561,10 @@ fn test_select_missed_case_string_no_default_compile_failure() {
#[test]
fn test_select_missed_case_boolean_no_default_compile_failure() {
assert_build_failure(
"select true, { false = 1, };",
"select (true) => { false = 1, };",
vec![
Regex::new(r"Unhandled select case with no default").unwrap(),
Regex::new(r"line: 1 column: 8").unwrap(),
Regex::new(r"line: 1 column: 9").unwrap(),
],
)
}
@ -578,4 +578,4 @@ fn test_bad_import_path_compile_failure() {
Regex::new(r"line: 1 column: 18").unwrap(),
],
)
}
}

View File

@ -461,47 +461,82 @@ fn tuple_to_select<'a>(
}
}
fn select_expression(input: SliceIter<Token>) -> Result<SliceIter<Token>, Expression> {
let parsed = do_each!(input,
make_fn!(
alt_select_expression<SliceIter<Token>, Expression>,
do_each!(
pos => pos,
_ => word!("select"),
val => do_each!(
expr => trace_parse!(must!(expression)),
_ => must!(punct!(",")),
(expr)
),
default_and_map => either!(
_ => must!(punct!("(")),
val => must!(expression),
default => optional!(
do_each!(
default => do_each!(
expr => trace_parse!(expression),
_ => punct!(","),
(expr)
),
map => trace_parse!(tuple),
(Some(default), map)
),
do_each!(
map => trace_parse!(must!(tuple)),
(None, map)
_ => punct!(","),
def => must!(expression),
(def)
)
),
(val, default_and_map.0, default_and_map.1)
);
match parsed {
Result::Abort(e) => Result::Abort(e),
Result::Fail(e) => Result::Fail(e),
Result::Incomplete(offset) => Result::Incomplete(offset),
Result::Complete(rest, (val, default, map)) => {
match tuple_to_select(input.clone(), val, default, map) {
Ok(expr) => Result::Complete(rest, expr),
Err(e) => Result::Fail(Error::caused_by(
"Invalid Select Expression",
Box::new(e),
Box::new(rest.clone()),
)),
}
}
}
}
_ => optional!(punct!(",")),
_ => must!(punct!(")")),
_ => must!(punct!("=>")),
_ => must!(punct!("{")),
tpl => must!(field_list),
_ => optional!(punct!(",")),
_ => must!(punct!("}")),
(Expression::Select(SelectDef {
val: Box::new(val),
default: default.map(|e| Box::new(e)),
tuple: tpl,
pos: pos,
}))
)
);
//fn select_expression(input: SliceIter<Token>) -> Result<SliceIter<Token>, Expression> {
//let parsed = do_each!(input,
//_ => word!("select"),
//val => do_each!(
//expr => wrap_err!(
//trace_parse!(must!(expression)),
//"Did you forget a comma after your selector value?"),
//_ => must!(punct!(",")),
//(expr)
//),
//default_and_map => either!(
//do_each!(
//default => do_each!(
//expr => trace_parse!(expression),
//_ => punct!(","),
//(expr)
//),
//map => trace_parse!(must!(tuple)),
//(Some(default), map)
//),
//do_each!(
//map => wrap_err!(
//trace_parse!(must!(tuple)),
//"Did you forget a comma after your default value?"
//),
//(None, map)
//)
//),
//(val, default_and_map.0, default_and_map.1)
//);
//match parsed {
//Result::Abort(e) => Result::Abort(e),
//Result::Fail(e) => Result::Fail(e),
//Result::Incomplete(offset) => Result::Incomplete(offset),
//Result::Complete(rest, (val, default, map)) => {
//match tuple_to_select(input.clone(), val, default, map) {
//Ok(expr) => Result::Complete(rest, expr),
//Err(e) => Result::Fail(Error::caused_by(
//"Invalid Select Expression",
//Box::new(e),
//Box::new(rest.clone()),
//)),
//}
//}
//}
//}
make_fn!(
simple_format_args<SliceIter<Token>, FormatArgs>,
@ -764,7 +799,7 @@ make_fn!(
trace_parse!(not_expression),
trace_parse!(fail_expression),
trace_parse!(module_expression),
trace_parse!(select_expression),
trace_parse!(alt_select_expression),
trace_parse!(grouped_expression),
trace_parse!(include_expression),
trace_parse!(unprefixed_expression)

View File

@ -18,11 +18,11 @@ let maybe = module{
} => ({do=do, is_null=is_null, or=or, unwrap=unwrap, expect=expect}) {
let this = mod.this;
let do = func (op) => select (mod.val != NULL), this{val=NULL}, {
let do = func (op) => select ((mod.val != NULL), this{val=NULL}) => {
true = this{val=op(mod.val)},
};
let or = func (op) => select (mod.val == NULL), this{val=mod.val}, {
let or = func (op) => select ((mod.val == NULL), this{val=mod.val}) => {
true = this{val=op()},
};
@ -30,7 +30,7 @@ let maybe = module{
let unwrap = func() => mod.val;
let expect = func(msg) => select mod.val != NULL, fail msg, {
let expect = func(msg) => select (mod.val != NULL, fail msg) => {
true = mod.val,
};
};

View File

@ -9,7 +9,7 @@ let str_join = module{
list=NULL,
} => (result) {
// The function used by reduce to join the list into a string.
let joiner = func (acc, item) => select (acc.out == ""), NULL, {
let joiner = func (acc, item) => select ((acc.out == ""), NULL) => {
true = acc{
out="@@" % (acc.out,item),
},
@ -32,7 +32,7 @@ let reverse = func(list) => reduce(func (acc, item) => [item] + acc, [], list);
// head returns the first item in a list as a list of one item.
// This function is safe for empty list inputs but is not safe
// for NULL inputs.
let head = func(list) => select len(list) > 0, [], {
let head = func(list) => select (len(list) > 0, []) => {
true = [list.0],
};
@ -40,7 +40,7 @@ let head = func(list) => select len(list) > 0, [], {
// This function is safe for empty lists but is not safe
// for NULL inputs.
let tail = func(list) => reduce(
func (acc, item) => select acc.count > 0, acc{count=1, tail=[]}, {
func (acc, item) => select (acc.count > 0, acc{count=1, tail=[]}) => {
true = acc{count = acc.count + 1, tail = acc.tail + [item]},
},
{count=0},
@ -81,7 +81,7 @@ let slice = module {
let list_len = list.len(mod.list);
let end = select mod.end is "null", mod.end, {
let end = select (mod.end is "null", mod.end) => {
true = list_len,
};
@ -92,7 +92,7 @@ let slice = module {
let reducer = func(acc, item) => acc{
count = acc.count + 1,
list = select (acc.count >= mod.start) && (acc.count <= end), acc.list, {
list = select ((acc.count >= mod.start) && (acc.count <= end), acc.list) => {
true = acc.list + [item],
},
};
@ -118,7 +118,7 @@ let zip = module{
let len2 = len(mod.list2);
// Compute the min range of the two lists.
let rng = select (len1 >= len2), NULL, {
let rng = select ((len1 >= len2), NULL) => {
true = 0:(len1 - 1),
false = 0:(len2 - 1),
};

View File

@ -12,14 +12,14 @@ let base_types = [
];
// Computes the base type of a value.
let base_type_of = func (val) => reduce(func (acc, f) => select (acc.val is f), f, {
let base_type_of = func (val) => reduce(func (acc, f) => select ((acc.val is f), f) => {
true = acc{typ = f},
false = acc,
}, {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.
let must = func (m, msg) => select m, fail msg, {
let must = func (m, msg) => select (m, fail msg) => {
true = m,
};
@ -97,13 +97,13 @@ let shaped = module {
let simple_handler = func (val, shape) => val is schema.base_type_of(shape);
let tuple_handler = func (acc, name, value) => acc{
ok = select (name) in acc.shape, mod.partial, {
ok = select ((name) in acc.shape, mod.partial) => {
true = this{val=value, shape=acc.shape.(name), partial=mod.partial},
},
};
let shape_tuple_handler = func (acc, field_name, value) => acc{
ok = select (field_name) in acc.val, false, {
ok = select ((field_name) in acc.val, false) => {
true = this{val=value, shape=acc.val.(field_name), partial=false},
},
};
@ -118,7 +118,7 @@ let shaped = module {
ok = acc.ok && schema.any{val=value, types=acc.shape},
};
let result = select schema.base_type_of(mod.val), false, {
let result = select (schema.base_type_of(mod.val), false) => {
str = simple_handler(mod.val, mod.shape),
int = simple_handler(mod.val, mod.shape),
float = simple_handler(mod.val, mod.shape),
@ -127,7 +127,7 @@ let shaped = module {
tuple = (mod.shape is "tuple") &&
match_shape_fields(mod.val, mod.shape) &&
match_tuple_fields(mod.val, mod.shape),
list = (mod.shape is "list") && select mod.shape == [], true, {
list = (mod.shape is "list") && select (mod.shape == [], true) => {
false = reduce(list_handler, {shape=mod.shape, ok=true}, mod.val).ok,
},
func = simple_handler(mod.val, mod.shape),

View File

@ -48,12 +48,12 @@ let ops = module {
let maybe_prefix = check_str.substr{end=split_str.len - 1};
let maybe_suffix = check_str.substr{start=split_str.len};
let result = select maybe_prefix.len == 0, {
let result = select (maybe_prefix.len == 0) => {
// terminal condition
true = mod.acc + [mod.buf],
//true = mod,
// recurse condition.
false = select maybe_prefix.str == mod.sep, {
false = select (maybe_prefix.str == mod.sep) => {
true = this{ // this is a match to our separator
str=maybe_suffix.str,
sep=mod.sep,
@ -77,10 +77,10 @@ let ops = module {
reduce(
func(acc, char) => acc{
counter = acc.counter + 1,
left = select acc.counter < idx, acc.left, {
left = select (acc.counter < idx, acc.left) => {
true = acc.left + char,
},
right = select acc.counter >= idx, acc.right, {
right = select (acc.counter >= idx, acc.right) => {
true = acc.right + char,
},
},
@ -97,7 +97,7 @@ let ops = module {
let pkg = mod.pkg();
let reducer = func(acc, char) => acc{
counter = acc.counter + 1,
str = select ((acc.counter >= mod.start) && (acc.counter <= mod.end)), acc.str, {
str = select ((acc.counter >= mod.start) && (acc.counter <= mod.end), acc.str) => {
true = acc.str + char,
},
};

View File

@ -38,7 +38,7 @@ let equal = module{
(mod.desc != NULL) || fail "description can't be null";
let ok = mod.left == mod.right;
let desc = select (mod.desc == ""), "@ == @" % (mod.left, mod.right), {
let desc = select ((mod.desc == ""), "@ == @" % (mod.left, mod.right)) => {
false = mod.desc,
};
};
@ -54,7 +54,7 @@ let not_equal = module{
(mod.desc != NULL) || fail "description can't be null";
let ok = mod.left != mod.right;
let desc = select (mod.desc == ""), "@ != @" % (mod.left, mod.right), {
let desc = select ((mod.desc == ""), "@ != @" % (mod.left, mod.right)) => {
false = mod.desc,
};
};

View File

@ -1,34 +1,34 @@
let list = import "std/lists.ucg";
let asserts = import "std/testing.ucg";
let t = import "std/testing.ucg";
let list_to_join = [1, 2, 3];
assert asserts.equal{
assert t.equal{
left=list.ops{list=list_to_join}.str_join(","),
right="1,2,3"
};
assert asserts.equal{
assert t.equal{
left=list.ops{list=[0, 1, 2, 3]}.len,
right=4,
};
assert asserts.equal{
assert t.equal{
left = list.ops{list=[0, 1, 2, 3]}.reverse().list,
right = [3, 2, 1, 0],
};
assert asserts.equal{
assert t.equal{
left=list.ops{list=["foo", "bar"]}.enumerate().list,
right=[[0, "foo"], [1, "bar"]],
};
assert asserts.equal{
assert t.equal{
left=list.enumerate{start=1, list=["foo", "bar"]},
right=[[1, "foo"], [2, "bar"]],
};
assert asserts.equal{
assert t.equal{
left=list.enumerate{
start=1,
step=2,
@ -37,47 +37,47 @@ assert asserts.equal{
right=[[1, "foo"], [3, "bar"]],
};
assert asserts.equal{
assert t.equal{
left=list.zip{list1=[0, 1], list2=[3, 4]},
right=[[0, 3], [1, 4]],
};
assert asserts.equal{
assert t.equal{
left=list.tail([0,1,2,3,4]),
right=[1,2,3,4],
};
assert asserts.equal{
assert t.equal{
left=list.ops{list=[0,1,2,3,4]}.head(),
right=[0],
};
assert asserts.equal{
assert t.equal{
left=list.ops{list=[0,1,2,3,4]}.tail().list,
right=[1,2,3,4],
};
assert asserts.equal{
assert t.equal{
left=list.slice{end=2, list=[0,1,2,3]},
right=[0,1,2],
};
assert asserts.equal{
assert t.equal{
left=list.slice{list=[0,1,2,3]},
right=[0,1,2,3],
};
assert asserts.equal{
assert t.equal{
left=list.slice{end=0, list=[0,1,2,3]},
right=[0],
};
assert asserts.equal{
assert t.equal{
left=list.slice{list=[]},
right=[],
};
assert asserts.equal{
assert t.equal{
left=list.slice{list=[0]},
right=[0],
};

View File

@ -1,54 +1,54 @@
let strings = import "std/strings.ucg";
let asserts = import "std/testing.ucg";
let t = import "std/testing.ucg";
let str_class = strings.ops{str="foo bar"};
assert asserts.equal{
assert t.equal{
left = str_class.split_on{},
right = ["foo", "bar"],
};
assert asserts.equal{
assert t.equal{
left = strings.ops{str="foo"}.split_on{},
right = ["foo"],
};
assert asserts.equal{
assert t.equal{
left = strings.ops{str=""}.split_on{},
right = [""],
};
assert asserts.equal{
assert t.equal{
left = strings.ops{str="foo=>bar=>quux"}.split_on{on="=>"},
right = ["foo", "bar", "quux"],
};
assert asserts.equal{
assert t.equal{
left = str_class.split_at(3),
right = {left="foo", right=" bar"},
};
assert asserts.equal{
assert t.equal{
left = str_class.len,
right = 7,
};
assert asserts.equal{
assert t.equal{
left = str_class.chars,
right = ["f", "o", "o", " ", "b", "a", "r"],
};
assert asserts.equal{
assert t.equal{
left = str_class.substr{start=1}.str,
right = "oo bar",
};
assert asserts.equal{
assert t.equal{
left = str_class.substr{end=5}.str,
right = "foo ba",
};
assert asserts.equal{
assert t.equal{
left = str_class.substr{end=8}.str,
right = "foo bar",
};

View File

@ -1,67 +1,67 @@
let asserts = import "std/testing.ucg";
let t = import "std/testing.ucg";
let not_equal_result = asserts.not_equal{
let not_equal_result = t.not_equal{
left=1,
right=2,
};
assert not_equal_result;
assert asserts.equal{
assert t.equal{
left=not_equal_result.desc,
right="1 != 2",
};
let bad_not_equal_result = asserts.not_equal{
let bad_not_equal_result = t.not_equal{
left=1,
right=1,
};
assert asserts.not_ok{test=bad_not_equal_result.ok};
assert asserts.equal{
assert t.not_ok{test=bad_not_equal_result.ok};
assert t.equal{
left=bad_not_equal_result.desc,
right="1 != 1",
};
let equal_result = asserts.equal{
let equal_result = t.equal{
left=1,
right=1,
};
assert equal_result;
assert asserts.equal{
assert t.equal{
left=equal_result.desc,
right="1 == 1",
};
let bad_equal_result = asserts.equal{
let bad_equal_result = t.equal{
left=1,
right=2,
};
assert asserts.equal{
assert t.equal{
left=bad_equal_result.desc,
right="1 == 2",
};
let ok_result = asserts.ok{
let ok_result = t.ok{
test=true,
};
assert ok_result;
assert asserts.equal{
assert t.equal{
left=ok_result.desc,
right="TODO description",
};
let bad_ok_result = asserts.ok{
let bad_ok_result = t.ok{
test=false,
};
assert asserts.equal{
assert t.equal{
left=bad_ok_result.desc,
right="TODO description",
};
let not_ok_result = asserts.not_ok{
let not_ok_result = t.not_ok{
test=false,
};
assert not_ok_result;
assert asserts.equal{
assert t.equal{
left=not_ok_result.desc,
right="TODO description",
};

View File

@ -1,5 +1,5 @@
// Assert that a value is a tuple. Generate a compile failure if it is not.
let assert_tuple = func (tpl) => select tpl is "tuple", NULL, {
let assert_tuple = func (tpl) => select (tpl is "tuple", NULL) => {
false = fail "@ is not a tuple" % (tpl),
};
@ -91,12 +91,12 @@ let field_type = module{
pkg.assert_tuple(mod.tpl);
// Next we assert that mod.field is a string.
select mod.field is "str", NULL, {
select (mod.field is "str", NULL) => {
false = fail "@ is not a string" % (mod.field),
};
// and finally that mod.type is a string
select mod.type is "str", NULL, {
select (mod.type is "str", NULL) => {
false = fail "@ is not a string" % (mod.type),
};
@ -104,7 +104,7 @@ let field_type = module{
let it = pkg.iter{tpl=mod.tpl};
// The reducer function used to check the field's types if it exists.
let reducer = func (acc, l) => acc && (select l.0 == mod.field, NULL, {
let reducer = func (acc, l) => acc && (select (l.0 == mod.field, NULL) => {
true = l.1 is mod.type, // if this is the field then we propagate if it's the right type.
false = true, // if this isn't the field then we propagate true
});

View File

@ -6,7 +6,7 @@ let validate_ns = func(ns) => schema.any{val=ns, types=["", {prefix="", uri=""}]
// Constructs an xml namespace for use in an xml tag or document root.
// Use NULL for the uri argument to use just the prefix.
let ns = func(prefix, uri) => select uri != NULL, {
let ns = func(prefix, uri) => select (uri != NULL) => {
true = {
prefix = prefix,
uri = uri,
@ -30,9 +30,9 @@ let tag = module{
children = mod.children,
};
let result = select mod.ns == NULL, {
let result = select (mod.ns == NULL) => {
true = base,
false = select pkg.validate_ns(mod.ns), {
false = select (pkg.validate_ns(mod.ns)) => {
true = base{ns=mod.ns},
false = fail "XML Tag namespaces must be a string or tuple of {prefix=, uri=} but is @" % (mod.ns),
},
@ -49,7 +49,7 @@ let validate_node = func(node) => schema.any{val=node, types=["", {name = ""}],
//
// The root node must be a valid xml node. Either a text or an
// xml node.
let doc = func(root) => select validate_node(root), {
let doc = func(root) => select (validate_node(root)) => {
true = {
root = root,
},