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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -149,13 +149,13 @@ fn test_out_expr_printing() {
#[test] #[test]
fn test_select_expr_no_default_printing() { 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)); assert_eq!(print_to_buffer(input), format!("{}\n", input));
} }
#[test] #[test]
fn test_select_expr_with_default_printing() { 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)); 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] #[test]
fn test_select_missed_case_string_no_default_compile_failure() { fn test_select_missed_case_string_no_default_compile_failure() {
assert_build_failure( assert_build_failure(
"select \"a\", { b = 1, };", "select (\"a\") => { b = 1, };",
vec![ vec![
Regex::new(r"Unhandled select case with no default").unwrap(), 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] #[test]
fn test_select_missed_case_boolean_no_default_compile_failure() { fn test_select_missed_case_boolean_no_default_compile_failure() {
assert_build_failure( assert_build_failure(
"select true, { false = 1, };", "select (true) => { false = 1, };",
vec![ vec![
Regex::new(r"Unhandled select case with no default").unwrap(), 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(),
], ],
) )
} }

View File

@ -461,47 +461,82 @@ fn tuple_to_select<'a>(
} }
} }
fn select_expression(input: SliceIter<Token>) -> Result<SliceIter<Token>, Expression> { make_fn!(
let parsed = do_each!(input, alt_select_expression<SliceIter<Token>, Expression>,
do_each!(
pos => pos,
_ => word!("select"), _ => word!("select"),
val => do_each!( _ => must!(punct!("(")),
expr => trace_parse!(must!(expression)), val => must!(expression),
_ => must!(punct!(",")), default => optional!(
(expr)
),
default_and_map => either!(
do_each!( do_each!(
default => do_each!( _ => punct!(","),
expr => trace_parse!(expression), def => must!(expression),
_ => punct!(","), (def)
(expr)
),
map => trace_parse!(tuple),
(Some(default), map)
),
do_each!(
map => trace_parse!(must!(tuple)),
(None, map)
) )
), ),
(val, default_and_map.0, default_and_map.1) _ => optional!(punct!(",")),
); _ => must!(punct!(")")),
match parsed { _ => must!(punct!("=>")),
Result::Abort(e) => Result::Abort(e), _ => must!(punct!("{")),
Result::Fail(e) => Result::Fail(e), tpl => must!(field_list),
Result::Incomplete(offset) => Result::Incomplete(offset), _ => optional!(punct!(",")),
Result::Complete(rest, (val, default, map)) => { _ => must!(punct!("}")),
match tuple_to_select(input.clone(), val, default, map) { (Expression::Select(SelectDef {
Ok(expr) => Result::Complete(rest, expr), val: Box::new(val),
Err(e) => Result::Fail(Error::caused_by( default: default.map(|e| Box::new(e)),
"Invalid Select Expression", tuple: tpl,
Box::new(e), pos: pos,
Box::new(rest.clone()), }))
)), )
} );
}
} //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!( make_fn!(
simple_format_args<SliceIter<Token>, FormatArgs>, simple_format_args<SliceIter<Token>, FormatArgs>,
@ -764,7 +799,7 @@ make_fn!(
trace_parse!(not_expression), trace_parse!(not_expression),
trace_parse!(fail_expression), trace_parse!(fail_expression),
trace_parse!(module_expression), trace_parse!(module_expression),
trace_parse!(select_expression), trace_parse!(alt_select_expression),
trace_parse!(grouped_expression), trace_parse!(grouped_expression),
trace_parse!(include_expression), trace_parse!(include_expression),
trace_parse!(unprefixed_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}) { } => ({do=do, is_null=is_null, or=or, unwrap=unwrap, expect=expect}) {
let this = mod.this; 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)}, 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()}, true = this{val=op()},
}; };
@ -30,7 +30,7 @@ let maybe = module{
let unwrap = func() => mod.val; 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, true = mod.val,
}; };
}; };

View File

@ -9,7 +9,7 @@ let str_join = module{
list=NULL, list=NULL,
} => (result) { } => (result) {
// The function used by reduce to join the list into a string. // 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{ true = acc{
out="@@" % (acc.out,item), 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. // 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 // This function is safe for empty list inputs but is not safe
// for NULL inputs. // for NULL inputs.
let head = func(list) => select len(list) > 0, [], { let head = func(list) => select (len(list) > 0, []) => {
true = [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 // This function is safe for empty lists but is not safe
// for NULL inputs. // for NULL inputs.
let tail = func(list) => reduce( 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]}, true = acc{count = acc.count + 1, tail = acc.tail + [item]},
}, },
{count=0}, {count=0},
@ -81,7 +81,7 @@ let slice = module {
let list_len = list.len(mod.list); 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, true = list_len,
}; };
@ -92,7 +92,7 @@ let slice = module {
let reducer = func(acc, item) => acc{ let reducer = func(acc, item) => acc{
count = acc.count + 1, 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], true = acc.list + [item],
}, },
}; };
@ -118,7 +118,7 @@ let zip = module{
let len2 = len(mod.list2); let len2 = len(mod.list2);
// Compute the min range of the two lists. // Compute the min range of the two lists.
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),
}; };

View File

@ -12,14 +12,14 @@ let base_types = [
]; ];
// Computes the base type of a value. // 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}, true = acc{typ = f},
false = acc, false = acc,
}, {val=val, typ="null"}, base_types).typ; }, {val=val, typ="null"}, base_types).typ;
// Turns any schema check module into a compile failure. // Turns any schema check module into a compile failure.
// The module must export the computed value as the result field. // 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, true = m,
}; };
@ -97,13 +97,13 @@ let shaped = module {
let simple_handler = func (val, shape) => val is schema.base_type_of(shape); let simple_handler = func (val, shape) => val is schema.base_type_of(shape);
let tuple_handler = func (acc, name, value) => acc{ 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}, true = this{val=value, shape=acc.shape.(name), partial=mod.partial},
}, },
}; };
let shape_tuple_handler = func (acc, field_name, value) => acc{ 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}, 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}, 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), str = simple_handler(mod.val, mod.shape),
int = simple_handler(mod.val, mod.shape), int = simple_handler(mod.val, mod.shape),
float = 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") && tuple = (mod.shape is "tuple") &&
match_shape_fields(mod.val, mod.shape) && match_shape_fields(mod.val, mod.shape) &&
match_tuple_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, false = reduce(list_handler, {shape=mod.shape, ok=true}, mod.val).ok,
}, },
func = simple_handler(mod.val, mod.shape), 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_prefix = check_str.substr{end=split_str.len - 1};
let maybe_suffix = check_str.substr{start=split_str.len}; 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 // terminal condition
true = mod.acc + [mod.buf], true = mod.acc + [mod.buf],
//true = mod, //true = mod,
// recurse condition. // 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 true = this{ // this is a match to our separator
str=maybe_suffix.str, str=maybe_suffix.str,
sep=mod.sep, sep=mod.sep,
@ -77,10 +77,10 @@ let ops = module {
reduce( reduce(
func(acc, char) => acc{ func(acc, char) => acc{
counter = acc.counter + 1, counter = acc.counter + 1,
left = select acc.counter < idx, acc.left, { left = select (acc.counter < idx, acc.left) => {
true = acc.left + char, true = acc.left + char,
}, },
right = select acc.counter >= idx, acc.right, { right = select (acc.counter >= idx, acc.right) => {
true = acc.right + char, true = acc.right + char,
}, },
}, },
@ -97,7 +97,7 @@ let ops = module {
let pkg = mod.pkg(); let pkg = mod.pkg();
let reducer = func(acc, char) => acc{ let reducer = func(acc, char) => acc{
counter = acc.counter + 1, 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, true = acc.str + char,
}, },
}; };

View File

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

View File

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

View File

@ -1,54 +1,54 @@
let strings = import "std/strings.ucg"; 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"}; let str_class = strings.ops{str="foo bar"};
assert asserts.equal{ assert t.equal{
left = str_class.split_on{}, left = str_class.split_on{},
right = ["foo", "bar"], right = ["foo", "bar"],
}; };
assert asserts.equal{ assert t.equal{
left = strings.ops{str="foo"}.split_on{}, left = strings.ops{str="foo"}.split_on{},
right = ["foo"], right = ["foo"],
}; };
assert asserts.equal{ assert t.equal{
left = strings.ops{str=""}.split_on{}, left = strings.ops{str=""}.split_on{},
right = [""], right = [""],
}; };
assert asserts.equal{ assert t.equal{
left = strings.ops{str="foo=>bar=>quux"}.split_on{on="=>"}, left = strings.ops{str="foo=>bar=>quux"}.split_on{on="=>"},
right = ["foo", "bar", "quux"], right = ["foo", "bar", "quux"],
}; };
assert asserts.equal{ assert t.equal{
left = str_class.split_at(3), left = str_class.split_at(3),
right = {left="foo", right=" bar"}, right = {left="foo", right=" bar"},
}; };
assert asserts.equal{ assert t.equal{
left = str_class.len, left = str_class.len,
right = 7, right = 7,
}; };
assert asserts.equal{ assert t.equal{
left = str_class.chars, left = str_class.chars,
right = ["f", "o", "o", " ", "b", "a", "r"], right = ["f", "o", "o", " ", "b", "a", "r"],
}; };
assert asserts.equal{ assert t.equal{
left = str_class.substr{start=1}.str, left = str_class.substr{start=1}.str,
right = "oo bar", right = "oo bar",
}; };
assert asserts.equal{ assert t.equal{
left = str_class.substr{end=5}.str, left = str_class.substr{end=5}.str,
right = "foo ba", right = "foo ba",
}; };
assert asserts.equal{ assert t.equal{
left = str_class.substr{end=8}.str, left = str_class.substr{end=8}.str,
right = "foo bar", 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, left=1,
right=2, right=2,
}; };
assert not_equal_result; assert not_equal_result;
assert asserts.equal{ assert t.equal{
left=not_equal_result.desc, left=not_equal_result.desc,
right="1 != 2", right="1 != 2",
}; };
let bad_not_equal_result = asserts.not_equal{ let bad_not_equal_result = t.not_equal{
left=1, left=1,
right=1, right=1,
}; };
assert asserts.not_ok{test=bad_not_equal_result.ok}; assert t.not_ok{test=bad_not_equal_result.ok};
assert asserts.equal{ assert t.equal{
left=bad_not_equal_result.desc, left=bad_not_equal_result.desc,
right="1 != 1", right="1 != 1",
}; };
let equal_result = asserts.equal{ let equal_result = t.equal{
left=1, left=1,
right=1, right=1,
}; };
assert equal_result; assert equal_result;
assert asserts.equal{ assert t.equal{
left=equal_result.desc, left=equal_result.desc,
right="1 == 1", right="1 == 1",
}; };
let bad_equal_result = asserts.equal{ let bad_equal_result = t.equal{
left=1, left=1,
right=2, right=2,
}; };
assert asserts.equal{ assert t.equal{
left=bad_equal_result.desc, left=bad_equal_result.desc,
right="1 == 2", right="1 == 2",
}; };
let ok_result = asserts.ok{ let ok_result = t.ok{
test=true, test=true,
}; };
assert ok_result; assert ok_result;
assert asserts.equal{ assert t.equal{
left=ok_result.desc, left=ok_result.desc,
right="TODO description", right="TODO description",
}; };
let bad_ok_result = asserts.ok{ let bad_ok_result = t.ok{
test=false, test=false,
}; };
assert asserts.equal{ assert t.equal{
left=bad_ok_result.desc, left=bad_ok_result.desc,
right="TODO description", right="TODO description",
}; };
let not_ok_result = asserts.not_ok{ let not_ok_result = t.not_ok{
test=false, test=false,
}; };
assert not_ok_result; assert not_ok_result;
assert asserts.equal{ assert t.equal{
left=not_ok_result.desc, left=not_ok_result.desc,
right="TODO description", right="TODO description",
}; };

View File

@ -1,5 +1,5 @@
// Assert that a value is a tuple. Generate a compile failure if it is not. // 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), false = fail "@ is not a tuple" % (tpl),
}; };
@ -91,12 +91,12 @@ let field_type = module{
pkg.assert_tuple(mod.tpl); pkg.assert_tuple(mod.tpl);
// Next we assert that mod.field is a string. // 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), false = fail "@ is not a string" % (mod.field),
}; };
// and finally that mod.type is a string // 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), false = fail "@ is not a string" % (mod.type),
}; };
@ -104,7 +104,7 @@ let field_type = module{
let it = pkg.iter{tpl=mod.tpl}; let it = pkg.iter{tpl=mod.tpl};
// The reducer function used to check the field's types if it exists. // 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. 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 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. // Constructs an xml namespace for use in an xml tag or document root.
// Use NULL for the uri argument to use just the prefix. // 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 = { true = {
prefix = prefix, prefix = prefix,
uri = uri, uri = uri,
@ -30,9 +30,9 @@ let tag = module{
children = mod.children, children = mod.children,
}; };
let result = select mod.ns == NULL, { let result = select (mod.ns == NULL) => {
true = base, true = base,
false = select pkg.validate_ns(mod.ns), { false = select (pkg.validate_ns(mod.ns)) => {
true = base{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), 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 // The root node must be a valid xml node. Either a text or an
// xml node. // xml node.
let doc = func(root) => select validate_node(root), { let doc = func(root) => select (validate_node(root)) => {
true = { true = {
root = root, root = root,
}, },