ucg 0.8.0

A configuration generation grammar.
Documentation
// 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 string to split on.
//
// * split_at - function splits the wrapped string at an character index.
//
// * parse_int - function that parses an integer from the beginning of a string.
//
// * 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,
       chars=chars,
       split_on=split_on,
       split_at=split_at,
       substr=substr,
       parse_int=parse_int_func,
      }) {
    let lists = import "std/lists.ucg";
    let len = lists.len(mod.str);
    let str = mod.str;
    let chars = reduce(func(acc, char) => acc + [char], [], mod.str);

    let split_on = module{
        on=" ",
        buf = "",
        out = [],
        str=mod.str,
    } => (result) {
        let recurse = module {
            buf = "",
            acc = [],
            str = mod.str,
            sep = NULL,
        } => (result) {
            (mod.sep != NULL) || fail "mod.sep can not be NULL";
            
            let pkg = mod.pkg();
            let this = mod.this;

            let check_str = pkg.ops{str=mod.str};
            let split_str = pkg.ops{str=mod.sep};
        
            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) => {
                // terminal condition
                true = mod.acc + [mod.buf],
                //true = mod,
                // recurse condition.
                false = select (maybe_prefix.str == mod.sep) => {
                    true = this{ // this is a match to our separator
                        str=maybe_suffix.str,
                        sep=mod.sep,
                        acc=mod.acc + [mod.buf],
                    },
                    false = this{
                        buf=mod.buf + check_str.chars.0,
                        str=check_str.substr{start=1}.str,
                        sep=mod.sep,
                        acc=mod.acc,
                    },
                },
            };
        };

        let result = recurse{sep=mod.on, str=mod.str};
    };

    let split_at = func(idx) => filter(
        func(name, val) => name != "counter",
        reduce(
            func(acc, char) => acc{
                counter = acc.counter + 1,
                left = select (acc.counter < idx, acc.left) => {
                    true = acc.left + char,
                },
                right = select (acc.counter >= idx, acc.right) => {
                    true = acc.right + char,
                },
            },
            {counter = 0, left = "", right = ""},
            mod.str
        )
    );

    let parse_int = module{
        chars = [],
        acc = "",
    } => (f.maybe{val=result}) {
        let this = mod.this;
        let f = import "std/functional.ucg";
        let lists = import "std/lists.ucg";

        let is_int = func(c) => select (c, false) => {
                "0" = true,
                "1" = true,
                "2" = true,
                "3" = true,
                "4" = true,
                "5" = true,
                "6" = true,
                "7" = true,
                "8" = true,
                "9" = true,
        };
        
        let result = select (lists.len(mod.chars) == 0, mod.acc) => {
            false = select (is_int(mod.chars.0), mod.acc) => {
                true = this{chars=lists.tail(mod.chars), acc=mod.acc+mod.chars.0}.unwrap(),
            },
        };
    };

    let parse_int_func = func() => parse_int{chars=chars}.do(func(s) => int(s));

    let substr = module{
        str = mod.str,
        start = 0,
        end = len,
    } => (result) {
        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) => {
                true = acc.str + char,
            },
        };
        let result = pkg.ops{str=reduce(
            reducer, {counter = 0, str = ""}, mod.str).str};
    };
};

let wrap = func(str) => ops{str=str};