FEATURE: Ops module wrapper idioms for stdlib.

This commit is contained in:
Jeremy Wall 2019-04-15 22:14:18 -05:00
parent 0e2e396ac3
commit 4afac26497
8 changed files with 120 additions and 17 deletions

View File

@ -1,5 +1,5 @@
// maybe is our monadic optional. It wraps a value that may or may not be the empty
// type NULL. It exports three operations.
// type NULL. It exports the following operations.
//
// * do - runs a user provided function of one argument on the value.
// Returns a maybe wrapped result.

View File

@ -138,4 +138,45 @@ let zip = module{
// The resulting zipped list.
let result = reduce(reducer, acc, rng).result;
};
// Wraps a list and provides a number of helpful operations.
//
// * len - property the length of the wrapped list.
//
// * str_join - function joins list into string with provided separator `sep`.
//
// * slice - function returns a slice of the list from `start` to `end`.
//
// * enumerate - function returns a list of pairs of [index, value] from the list.
// The returned list is wrapped in the ops module.
//
// * head - function returns the head of the list as list of one item.
//
// * tail - function returns the tail of the list.
// The returned list is wrapped in the ops module.
//
// * reverse - function returns the list reversed.
// The returned list is wrapped in the ops module.
let ops = module{
list=NULL,
} => ({len=len,
str_join=str_join,
slice=slice,
enumerate=enumerate,
tail=tail,
head=head,
reverse=reverse,
list=list,
}) {
let super = import "std/lists.ucg";
let list = mod.list;
let len = super.len(mod.list);
let str_join = func(sep) => super.str_join{list=mod.list, sep=sep};
let slice = func(start, end) => super.slice{start=start, end=end, list=mod.list};
let enumerate = func() => super.ops{list=super.enumerate{start=0, step=1, list=mod.list}};
let tail = func() => super.ops{list=super.tail(mod.list)};
let head = func() => super.head(mod.list);
let reverse = func() => super.ops{list=super.reverse(mod.list)};
};

View File

@ -1,7 +1,29 @@
// Wraps a string and provides operations for that string.
//
// * len - property representing the length of the string in characters.
//
// * str - property the wrapped string.
//
// * split_on - module that splits the string on a character.
// - `on` field represents the character to split on.
// - `str` field defaults to the wrapped string. you can override this
// if desired.
//
// * split_at - function splits the wrapped string at an character index.
//
// * substr - module that returns a substr of the wrapped string.
// - `start` field is the index at which the substr starts (defaults to 0)
// - `end` field is the index at which the substr ends (defaults to end of string)
let ops = module {
str="",
} => {
} => ({len=len,
str=str,
split_on=split_on,
split_at=split_at,
substr=substr,
}) {
let len = import "std/lists.ucg".len(mod.str);
let str = mod.str;
let split_on = module{
on=" ",
@ -20,7 +42,7 @@ let ops = module {
let result = accumulated.out + [accumulated.buf];
};
let split_at = func(idx) => filter(
func(name, val) => name != "counter",
reduce(
@ -43,13 +65,14 @@ let ops = module {
start = 0,
end = len,
} => (result) {
let filepkg = import "std/strings.ucg";
let reducer = func(acc, char) => acc{
counter = acc.counter + 1,
str = select ((acc.counter >= mod.start) && (acc.counter <= mod.end)), acc.str, {
true = acc.str + char,
},
};
let result = reduce(
reducer, {counter = 0, str = ""}, mod.str).str;
let result = filepkg.ops{str=reduce(
reducer, {counter = 0, str = ""}, mod.str).str};
};
};

View File

@ -7,7 +7,7 @@ let ok = module{
// descriptive message to use in output.
desc=todo_desc,
} => ({ok=ok, desc=desc}) {
assert mod.desc != NULL;
(mod.desc != NULL) || fail "description can't be null";
let ok = mod.test;
let desc = "@" % (mod.desc);
@ -20,7 +20,7 @@ let not_ok = module{
// descriptive message to use in output.
desc=todo_desc,
} => ({ok=ok, desc=desc}) {
assert mod.desc != NULL;
(mod.desc != NULL) || fail "description can't be null";
let ok = not mod.test;
let desc = "@" % (mod.desc);
@ -35,7 +35,7 @@ let equal = module{
right=NULL,
desc="",
} => ({ok=ok, desc=desc}) {
assert mod.desc != NULL;
(mod.desc != NULL) || fail "description can't be null";
let ok = mod.left == mod.right;
let desc = select (mod.desc == ""), "@ == @" % (mod.left, mod.right), {
@ -51,7 +51,7 @@ let not_equal = module{
right=NULL,
desc="",
} => ({ok=ok, desc=desc}) {
assert mod.desc != NULL;
(mod.desc != NULL) || fail "description can't be null";
let ok = mod.left != mod.right;
let desc = select (mod.desc == ""), "@ != @" % (mod.left, mod.right), {

View File

@ -4,22 +4,22 @@ let asserts = import "std/testing.ucg";
let list_to_join = [1, 2, 3];
assert asserts.equal{
left=list.str_join{sep=",", list=list_to_join},
left=list.ops{list=list_to_join}.str_join(","),
right="1,2,3"
};
assert asserts.equal{
left=list.len([0, 1, 2, 3]),
left=list.ops{list=[0, 1, 2, 3]}.len,
right=4,
};
assert asserts.equal{
left = list.reverse([0, 1, 2, 3]),
left = list.ops{list=[0, 1, 2, 3]}.reverse().list,
right = [3, 2, 1, 0],
};
assert asserts.equal{
left=list.enumerate{list=["foo", "bar"]},
left=list.ops{list=["foo", "bar"]}.enumerate().list,
right=[[0, "foo"], [1, "bar"]],
};
@ -48,10 +48,15 @@ assert asserts.equal{
};
assert asserts.equal{
left=list.head([0,1,2,3,4]),
left=list.ops{list=[0,1,2,3,4]}.head(),
right=[0],
};
assert asserts.equal{
left=list.ops{list=[0,1,2,3,4]}.tail().list,
right=[1,2,3,4],
};
assert asserts.equal{
left=list.slice{end=2, list=[0,1,2,3]},
right=[0,1,2],

View File

@ -29,16 +29,16 @@ assert asserts.equal{
};
assert asserts.equal{
left = str_class.substr{start=1},
left = str_class.substr{start=1}.str,
right = "oo bar",
};
assert asserts.equal{
left = str_class.substr{end=5},
left = str_class.substr{end=5}.str,
right = "foo ba",
};
assert asserts.equal{
left = str_class.substr{end=8},
left = str_class.substr{end=8}.str,
right = "foo bar",
};

View File

@ -6,16 +6,31 @@ assert t.equal{
right = ["foo", "bar"],
};
assert t.equal{
left = tpl.ops{tpl={foo=1, bar=2}}.fields(),
right = ["foo", "bar"],
};
assert t.equal{
left = tpl.values{tpl={foo=1, bar=2}},
right = [1, 2],
};
assert t.equal{
left = tpl.ops{tpl={foo=1, bar=2}}.values(),
right = [1, 2],
};
assert t.equal{
left = tpl.iter{tpl={foo=1, bar=2}},
right = [["foo", 1], ["bar", 2]],
};
assert t.equal{
left = tpl.ops{tpl={foo=1, bar=2}}.iter(),
right = [["foo", 1], ["bar", 2]],
};
assert t.equal{
left = tpl.strip_nulls{tpl={foo="bar", bar=NULL}},
right = {foo="bar"},

View File

@ -36,6 +36,25 @@ let iter = module{
let result = reduce(func (acc, field, value) => acc + [[field, value]], [], (mod.tpl));
};
// Wraps a tuple and provides a number of operations on it.
//
// * fields - function returns all the fields in the wrapped tuple as a list.
//
// * values - function returns all the values in the wrapped tuple as a list.
//
// * iter - function returns a list of pairs of [field, value].
let ops = module{
tpl = NULL,
} => ({fields=fields, values=values, iter=iter}) {
(mod.tpl != NULL) || fail "tpl must not be null";
let super = import "std/tuples.ucg";
let fields = func() => super.fields{tpl=mod.tpl};
let values = func() => super.values{tpl=mod.tpl};
let iter = func() => super.iter{tpl=mod.tpl};
};
// Strip all the null fields from a tuple.
let strip_nulls = module{
tpl = NULL,