new object $libraries: $root; var $root inited = 1; new object $network: $root; var $root inited = 1; new object $string: $libraries; var $root inited = 1; var $string alphabet = "abcdefghijklmnopqrstuvwxyz"; var $string numbers = "1234567890"; var $string non_alphanumeric = "!@#$%^&*()_+-=~`'{}[]|/?\",.<>;: "; public method .filter_text() { arg text; var x, line; for x in [1 .. listlen(text)] { if (text[x]) { line = text[x].replace("&", "&").replace("<", "<").replace(">", ">"); text = replace(text, x, line); } } return text; }; public method .filter_line() { arg line; return line.replace("&", "&").replace("<", "<").replace(">", ">"); }; public method .explode_url() { arg line; var out, args, i; i = "?" in line; if (i) { args = substr(line, i + 1); line = substr(line, 1, i - 1); } if (args) out = #[['path, explode(line, "/")], ['args, .break_fields(args)]]; else out = #[['path, explode(line, "/")], ['args, #[]]]; return out; }; public method .left() { arg str, width, [fchar]; // will NOT chop off 'str' if it is longer than width, use pad() for that. if (fchar) return str + (str.length() < width ? "".pad(width - str.length(), fchar[1]) : ""); else return str + (str.length() < width ? "".pad(width - str.length()) : ""); }; public method .fill() { arg n, [args]; var fill, x; // same as pad("", n, [args]); fill = [@args, " "][1]; return "".pad(n, fill); }; public method .center() { arg text, len, [args]; var lfill, rfill, textlen, padlen; // args[1] == string to center // args[2] == integer of width to center in // args[3] <op> == what to fill the left|right side with. // args[4] <op> == what to fill the right side with. lfill = args.length() >= 1 && args[1] || " "; rfill = args.length() >= 2 ? args[2] : lfill == " " ? "" : lfill; textlen = text.length(); padlen = (len - textlen) / 2; if (textlen < len) return .fill(padlen, lfill) + text + (rfill ? .fill(len - textlen - padlen, rfill) : ""); else return len > 0 ? text : text.pad(len); }; public method .to_list() { arg str, [sep]; var result, list; // separate a string into a list of strings, breaking wherever 'sep' appears. // if not provided, sep defaults to a comma. // One word of warning. sep should not contain an asterisk. If it does, // this routine will separate the string oddly, most likely losing bits. if (!str) return []; sep = "*" + (sep ? sep[1] : ",") + "*"; list = []; while (1) { result = str.match_pattern(sep); if (result) { list = list + [result[1]]; str = result[2]; } else { return list + [str]; } } }; public method .right() { arg str, width, [fchar]; // will not chop off 'str' if it is longer than width (unlike pad()) if (fchar) return "".pad(width - str.length(), fchar[1]) + str; else return "".pad(width - str.length()) + str; }; public method .alphabet() { return alphabet; }; public method .numbers() { return numbers; }; public method .is_numeric() { arg string; return toint(string) || string == "0"; }; public method .a_or_an() { arg string; if (string[1].lowercase() in "aeiou") return "an"; return "a"; }; public method .strip() { arg string, [strip]; var new_str, char; new_str = ""; if (!strip) strip = "!@#$%^&*()_+-=~`'{}[]|/?\"\,.<>;: "; else strip = strip[1]; for char in [1 .. strlen(string)] { if (!(string[char] in strip)) new_str = new_str + string[char]; } return new_str; }; public method .non_alphanumeric() { return non_alphanumeric; }; public method .chop() { arg str, len, [end]; // chops string off end.length() characters before len and appends len end = [@end, "..."][1]; if (str.length() < len) return str; if (str.length() < end.length()) return str; return str.pad(len - end.length()) + end; }; public method .explode_english_list() { arg line, [opts]; var x, output, tmp; if (!line) return []; // explodes an english list ("foo, bar and zoo"). line = line.explode(","); output = []; for x in (line) { x = .trim(x); if ((| x.subrange(1, 4) |) == "and ") output = [@output, .trim(x.subrange(4))]; else output = [@output, x]; } // check the last element, if they didn't specify 'noand if (!('noand in opts)) { line = output[output.length()].explode(); tmp = ""; for x in [1 .. line.length()] { if (line[x] == "and") { output = output.delete(output.length()); if (tmp) output = [@output, tmp]; tmp = $list.join(line.subrange(x + 1)); if (tmp) output = [@output, tmp]; // only bother with the first "and" break; } tmp = tmp + (tmp ? " " : "") + line[x]; } } return output; }; public method .explode_delimited() { arg str, left, right; var pattern, parsed, matched, match_num, match_result; // parse str looking for anything surrounded by left and right // ;$string.explode_delimited("foo%[bar]baz", "%[", "]") // => [["foo", 1, "baz"], ["bar"]] pattern = "*" + left + "*" + right + "*"; parsed = []; matched = []; match_num = 0; while (str) { match_result = str.match_pattern(pattern); if (match_result) { match_num = match_num + 1; parsed = [@parsed, match_result[1], match_num]; matched = [@matched, match_result[2]]; str = match_result[3]; } else { parsed = [@parsed, str]; str = ""; } } return [parsed, matched]; }; public method .wrap_line() { arg string, length, [stuff]; var output, cutoff, firstline, prefix; // takes string and wraps it by words, compared to length, breaks with \n prefix = [@stuff, ""][1]; firstline = [@stuff, 0, 0][2]; output = ""; if (firstline) string = prefix + string; while (string.length() > length) { cutoff = .rindex(string.subrange(1, length), " "); if (cutoff <= prefix.length()) { output += "\n" + string.subrange(1, length); string = prefix + string.subrange(length + 1); } else { output += "\n" + string.subrange(1, cutoff - 1); string = prefix + string.subrange(cutoff + 1); } } return (output ? output.subrange(3) + "\n" : "") + string; }; public method .rindex() { arg string, index; var loc, rest; // returns the first occurance of index starting from the end of the string, // and moving to the beginning. loc = index in string; rest = loc && string.subrange(loc + 1); while (loc && index in rest) { loc = loc + (index in rest); rest = loc && string.subrange(loc + 1); } return loc; }; public method .match_sub_tag() { arg string, tag; var x, expl, output, match, matches; // matches a string between 'tag' and " " in a larger string against // the sender's environment. If a match is found it subs the match.name // with the string, otherwize it lets it pass through with the tag, ie: // .match_sub_tag("this test #of something #note or other"); // => "this test #of something Note of sorts or other" // where the note is in the sender's environment. expl = .explode_delimited(string + " ", tag, " "); matches = expl[2]; expl = expl[1]; output = ""; for x in (expl) { if (type(x) == 'integer) { match = (| sender().match_environment(matches[x]) |); if (match) output = output + match.name() + " "; else output = output + tag + matches[x] + " "; } else { output = output + x; } } return output.subrange(1, output.length() - 1); }; public method .search_pat() { arg pat, text, [start_at]; var line, match_result, type; line = 1; type = [@start_at, 'pattern, 'pattern][2] == 'pattern ? 'match_pattern : 'match_regexp; if (start_at) { line = start_at[1]; start_at = [@start_at, 1, 1][2]; match_result = pat.(type)(text[line].subrange(line)); if (match_result != 0) { if (type == 'match_pattern) { pat = $string.pat_sub(pat, match_result); return [line, start_at + pat in text[line].subrange(start_at)]; } else { return [line, start_at + match_result[1][1]]; } } line = line + 1; } while (line <= text.length()) { match_result = pat.(type)(text[line]); if (match_result != 0) { if (type == 'pattern) { pat = $string.pat_sub(pat, match_result); return [line, pat in text[line]]; } else { return [line, match_result[1][1]]; } } line = line + 1; } throw(~strnf, "String not found in text."); }; public method .pat_sub() { arg pat, subs; var wc_idx; // wc_idx == wildcard index while (subs) { wc_idx = "*" in pat; if (wc_idx == 1) pat = subs[1] + pat.subrange(2); else if (wc_idx == pat.length()) pat = pat.subrange(1, wc_idx - 1) + subs[1]; else pat = pat.subrange(1, wc_idx - 1) + subs[1] + pat.subrange(wc_idx + 1); subs = subs.delete(1); } return pat; }; public method .is_boolean() { arg str; if ("yes".match_begin(str) || "true".match_begin(str) || str == "1") return 1; else if ("no".match_begin(str) || "false".match_begin(str) || str == "0") return 0; return -1; }; public method .parse_template() { arg str; var index, out; out = (str.explode(" *"))[1]; // index = "?" in str; // if (index) { // out = uppercase(str.subrange(1, index - 1)); // out = out + "?" + str.subrange(index + 1); // } else { // out = uppercase(out); // } return out; }; public method .repeat() { arg string, times; var t, out; // repeats <string> <times> times if (type(string) != 'string) throw(~type, "The first agrument must be a string."); if (type(times) != 'integer || times < 0) throw(~type, "The second agrument must be a non-negatiive integer."); out = ""; for t in [1 .. times] out = out + string; return out; }; public method .find_next() { arg str, choices; var t, first, pos; //Returns the index of the first string in choices to appear. //Returns str.length() if none are in str. first = str.length() + 1; for t in (choices) { pos = t in str; if (pos && pos < first) first = pos; } return first; }; public method .split_on_next() { arg str, choices; var pos, pre, post; // splits str around whichever choice appears first. pos = $string.find_next(str, choices); pre = (| str.subrange(1, pos - 1) |) || ""; post = (| str.subrange(pos + 1) |) || ""; return [pre, (| str[pos] |) || "", post]; }; public method .explode_template_word() { arg template; var t, x, idx, out, new; // this only explodes single word templates template = template.explode("|"); out = []; for t in (template) { idx = "?" in t; if (idx) { t = t.strip("?"); new = t.subrange(1, idx - 1); out = [@out, new]; for x in [idx .. t.length()] { new = new + t[x]; out = [@out, new]; } } else { out = [@out, t]; } } return out; }; public method .to_number() { arg str; if (str.is_numeric()) return toint(str); throw(~nonum, "\"" + str + "\" is not a number."); }; public method .toliteral() { arg [args]; return (> toliteral(@args) <); }; public method .last() { arg str; return str[str.length()]; }; public method .explode_list() { arg str; if ("," in str) return str.explode_english_list(); else return str.explode(); }; public method .onespace() { arg str; var sub; if ((sub = " +".sed(str, " ", "g"))) str = sub; return str; }; public method .find_next_escaped() { arg str, choices; var t, first, pos, good, p, start; //Returns the index of the first string in choices to appear. //If //Returns str.length() if none are in str. first = str.length() + 1; for t in (choices) { pos = str.find_escaped(t); if (pos < first) first = pos; } return first; }; public method .split_on_next_escaped() { arg str, choices; var pos, pre, post; // splits str around whichever choice appears first. pos = str.find_next_escaped(choices); pre = (| str.subrange(1, pos - 1) |) || ""; post = (| str.subrange(pos + 1) |) || ""; return [pre, (| str[pos] |) || "", post]; }; public method .find_escaped() { arg str, char; var good, start, pos, p; good = 0; start = 0; while (!good && start < str.length()) { pos = (char in str.subrange(start + 1)) + start; good = 1; if (pos > start) { p = pos - 1; while (p > 0 && str[p] == "\\") { good = good ? 0 : 1; p = p - 1; } } if (good) return pos; else start = pos; } }; public method .explode_quoted() { arg str; var out, result; out = []; while (str) { result = match_pattern(str, "*\"*\"*"); if (result) { out = [@out, @result[1].explode(), result[2].trim()]; str = result[3]; } else { out = [@out, @str.explode()]; str = ""; } } return out; }; public method .strip_article() { arg str; return strsed(str, "^(an|a|the) *", "", "g"); }; public method .rindexc() { arg str, c; var i; // same as rindex, but only with a single character, faster. i = str.length(); while (i) { if (str[i] == c) return i; i = i - 1; } return 0; }; public method .echo() { arg str; return str; }; public method .sub() { arg regexp, string; var match, m, complete, out; match = string.match_regexp(regexp); if (!match) return 0; complete = match[1]; out = []; for m in (match.delete(1)) { if (!m[1]) break; out = out + [string.subrange(@m)]; } return out || string.subrange(@complete); }; public method .unquote() { arg str; if (str && str[1] == "\"" && str[str.length()] == "\"") return str.subrange(2, str.length() - 2); return str; }; public method .to_buffer() { arg string; return (> $buffer.from_string(string) <); }; public method .valid_method_name() { arg str; return .strip_others(str, alphabet + numbers + "_").length() == str.length(); }; public method .strip_others() { arg string, valid; var new_str, char; // strips all but "strip" characters from the string new_str = ""; for char in [1 .. string.length()] { if (string[char] in valid) new_str = new_str + string[char]; } return new_str; }; public method .wrap_lines() { arg string, length, [stuff]; var output, cutoff, firstline, prefix; // takes string and wraps it by words, compared to length, returns a list. prefix = [@stuff, ""][1]; firstline = [@stuff, 0, 0][2]; output = []; if (firstline) string = prefix + string; while (string.length() > length) { cutoff = .rindex(string.subrange(1, length), " "); if (cutoff <= prefix.length()) { output = [@output, string.subrange(1, length)]; string = prefix + string.subrange(length + 1); } else { output = [@output, string.subrange(1, cutoff - 1)]; string = prefix + string.subrange(cutoff + 1); } } return [@output, string]; }; public method .to_symbol() { arg str; str = str.strip(); return (> tosym(str) <); }; public method .valid_ident() { arg str; return (| tosym(str) |) || 0; }; new object $list: $libraries; var $root inited = 1; public method .tb_to_text() { arg [args]; return (> $parse_lib.traceback(@args) <); }; public method .tb_to_html() { arg [args]; return (> $parse_lib.html_traceback(@args) <); }; public method .to_english() { arg list, [options]; var empty, and, sep; empty = [@options, "nothing"][1]; switch (list.length()) { case 0: return empty; case 1: return tostr(list[1]); } and = [@options, " and ", " and "][2]; sep = [@options, ", ", ", ", ", "][3]; return join(list.delete(list.length()), sep) + and + tostr(list[list.length()]); }; public method .mmap() { arg list, method, [args]; var x; // call 'method on each object, return results. return map x in (list) to (x.(method)(@args)); }; public method .mfilter() { arg list, method, [args]; var x; // similar to .mmap, but returns a list of objects which returned a // true value from 'method. return filter x in (list) where (x.method(@args)); }; public method .sort() { arg list, [sortby]; // calls ._sort(). Does not set an element to sort by yet, but should // eventually will have to fix. return ._sort(list, 1, list.length()); }; public method ._sort() { arg lst, x, y; var p, i, j; switch (y - x + 1) { case 0, 1: return lst; case 2: if (lst[x] <= lst[y]) return lst; p = lst[x]; lst = lst.replace(x, lst[y]); lst = lst.replace(y, p); return lst; case 3: if (lst[x] <= lst[x + 1]) { if (lst[x + 1] <= lst[y]) { ; } else if (lst[x] <= lst[y]) { p = lst[x + 1]; lst = lst.replace(x + 1, lst[y]); lst = lst.replace(y, p); } else { p = lst[x]; lst = lst.replace(x, lst[y]); lst = lst.replace(y, lst[x + 1]); lst = lst.replace(x + 1, p); } } else if (lst[x] <= lst[y]) { p = lst[x]; lst = lst.replace(x, lst[x + 1]); lst = lst.replace(x + 1, p); } else if (lst[x + 1] <= lst[y]) { p = lst[x]; lst = lst.replace(x, lst[x + 1]); lst = lst.replace(x + 1, lst[y]); lst = lst.replace(y, p); } else { p = lst[x]; lst = lst.replace(x, lst[y]); lst = lst.replace(y, p); } return lst; } p = lst[x]; i = x; j = y; while (1) { while (i < j && p <= lst[j]) j = j - 1; if (i == j) break; lst = lst.replace(i, lst[j]); i = i + 1; while (i < j && p >= lst[i]) i = i + 1; if (i == j) break; lst = lst.replace(j, lst[i]); j = j - 1; } lst = lst.replace(i, p); lst = ._sort(lst, x, i - 1); lst = ._sort(lst, i + 1, y); return lst; }; public method .columnize() { arg list, cols, [rest]; var width, lines, line, separator, linelength, curcol; // turn [...] into ". . ." // rest[1]==separator; rest[2]==linelength separator = [@rest, " "][1]; linelength = [@rest, 78, 78][2]; width = linelength / cols - separator.length(); lines = []; while (list) { line = list[1].pad(width); list = list.subrange(2); for curcol in [2 .. cols] { if (list) { line = line + separator + list[1].pad(width); list = list.subrange(2); } } lines = [@lines, line]; } return lines; }; public method .reverse() { arg list; var i, len; // .reverse(list) // -> list with its elements reversed len = list.length() + 1; return map i in [1 .. len - 1] to (list[len - i]); }; public method .compress() { arg list; var x; // [a,a,b,b,c,c,d,d] => [a,b,c,d] // removes duplicate entries in a list return hash x in (list) to ([x, 1]).keys(); }; public method .last() { arg list; return list[list.length()]; }; public method .count() { arg list, elem; var count; // count of elem in list while (elem in list) { count = count + 1; list = list.subrange((elem in list) + 1); } return count; }; public method .element_maxlength() { arg list; var elm; return map elm in (list) to (tostr(elm).length()).max(); }; public method .nth_element_maxlength() { arg lists, element; var list; // Returns longest string whose index is element in one of the lists in // lists. if (type(element) != 'integer) throw(~type, "Second argument is not an integer"); if (type(lists) != 'list) throw(~type, "First argument is not a list"); return map list in (lists) to (tostr(list[element]).length()).max(); }; public method .numbered_text() { arg text; var line; // receives a list of strings, returns that list with line numbers // prepended return map line in [1 .. text.length()] to ("%3r: %l".format(line, text[line])); }; public method .slice() { arg big_list, element; var list; // Return elementh' element of all lists in big_list // No type or length checking done for speed purposes. return map list in (big_list) to (list[element]); }; public method .swap() { arg list, a, b; var holder; // swap elements at indexes a and b holder = (> list[a] <); list = (> list.replace(a, list[b]) <); list = list.replace(b, holder); return list; }; public method .max() { arg list; return (| max(@list) |) || 0; }; public method .min() { arg list; return (| min(@list) |) || 0; }; public method .lcolumnize() { arg list, [args]; var line, part, lines, max, cols, col, width, len, sep; len = [@args, (| sender().linelen() |) || 79][1]; sep = [@args, "", ""][2]; lines = []; line = ""; max = .element_maxlength(list) + sep.length(); cols = len > max ? len / max : 1; width = len / cols - sep.length(); col = cols; for part in (list) { col = col - 1; if (!col) { lines = lines + [line + part]; line = ""; col = cols; continue; } line = line + part.pad(width); } if (line) return lines + [line]; return lines; }; public method .swapsort() { arg list, [sort_by]; var bot_elem, cur_elem, elem, compare; // note: iterative implementation allows sorts of extra-long lists elem = [@sort_by, 1]; compare = [@sort_by, 'gt, 'lt][2]; for bot_elem in [1 .. list.length()] { for cur_elem in [bot_elem + 1 .. list.length()] { if (._swap_compare(list[bot_elem], list[cur_elem], compare, elem)) list = $list.swap(list, bot_elem, cur_elem); } } return list; }; public method ._swap_compare() { arg elem1, elem2, compare, [elem]; elem = [@elem, 1][1]; switch (compare) { case 'lt: return elem1[elem] < elem2[elem]; case 'gt: return elem1[elem] > elem2[elem]; default: return 0; } }; public method .heapsort() { arg list, [sort_by]; var heap, sort_type, sort_elem; sort_elem = [@sort_by, 1][1]; sort_type = [@sort_by, 'gt, 'gt][2]; switch (sort_type) { case 'gt: heap = $small_first_heap_class.new(list, sort_elem); default: return list; } list = []; while (heap.length()) { list = heap.element(1); heap = heap.del(1); } return list; }; public method .map_to_english() { arg list, method, [args]; // because I (Lynx) am lazy return .to_english(.mmap(list, method, @args)); }; public method .map_to_string() { arg list, method, [args]; // because I (Lynx) am lazy return .join(.mmap(list, method, @args)); }; public method .flatten() { arg list; var toret, elem; // [[[x], x], x] => [x, x, x] toret = []; for elem in (list) { if (type(elem) == 'list) toret += .flatten(elem); else toret = toret + [elem]; } return toret; }; public method .sum() { arg data; var ret, i; // returns a sum of each element in the list. ret = data[1]; for i in (data.subrange(2)) ret += i; return ret; }; public method .non_alphanumeric() { // returns nun-alphanumeric in a list of characters return ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "-", "=", "~", "`", "'", "{", "}", "[", "]", "|", "/", "?", "\"", "\\", ",", ".", "<", ">", ";", ":", " "]; }; public method .numbers() { // returns a list of numbers return ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]; }; public method .alphabet() { //returns the alphabet in a list return ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]; }; public method .center_lines() { arg lines, width, [args]; var line; return map line in (lines) to ($string.center(line, width, @args)); }; public method .to_buffer() { arg [args]; return (> $buffer.from_strings(@args) <); }; public method .chop() { arg list, [count]; // chops the last <count> elements off the list. // return [] if count is longer then the list. count = count || 1; return (| list.subrange(1, list.length() - count) |) || []; }; public method .lmap() { arg list, method, [args]; var x, s; //call methods for each thing in list on sender() s = sender(); return map x in (list) to (s.(method)(x, @args)); }; public method .omap() { arg list, object, method, [args]; var obj; // calls object.method(obj, @args) for each obj in list return map obj in (list) to (object.(method)(obj, @args)); }; public method .del() { arg list, element; return (> list.setremove(element) <); }; public method .add() { arg list, element; return (> list.setadd(element) <); }; public method .set_difference() { arg [args]; var set, list, element; // Usage: diff(set 1, set 2, ..., set n) // Returns all elements of set 1 that are not in sets 2..n if (!args) return []; set = args[1]; for list in (args.delete(1)) { for element in (list) set = set.setremove(element); } return set; }; public method .set_contains() { arg [args]; var super, list, element; // True if the first list given is a superset of all subsequent lists. // False otherwise. [] is a superset of [] and nothing else; anything is // a superset of []. If only one list is given, return true. super = args ? args[1] : []; for list in (args.delete(1)) { for element in (list) { if (!(element in super)) return 0; } } return 1; }; public method .set_equal() { arg set1, set2; var element; // True if the two lists given contain the same elements. // False otherwise. while (set1) { element = set1[1]; if (!element in set2) return 0; while (element in set2) set2 = set2.setremove(element); while (element in set1) set1 = set1.setremove(element); } return set2 == []; }; public method .fold() { arg list, object, method, [args]; var i, out; // apply object.method to a current result and the next element, return the // result switch (list.length()) { case 0: return 0; case 1: return list[1]; } out = list[1]; for i in (list.subrange(2, list.length() - 1)) out = object.(method)(out, i, @args); return out; }; public method .intersection() { arg l1, l2; var i, out; // set intersection if the arguments out = []; for i in (l1) { if (i in l2) out = out.setadd(i); } return out; }; public method .msort() { arg list, [keys]; keys = keys ? keys[1] : list; if (listlen(list) != listlen(keys)) throw(~invarg, "Invalid key list - the list lengths must be the same."); if (!list) return []; return (._merge_sort(list, keys))[1]; }; private method ._merge_sort() { arg list, keys; var i, j, l1, k1, l2, k2, n1, n2, n; n = listlen(list); if (n == 1) return [list, keys]; n1 = n / 2; n2 = n - n1; l1 = ._merge_sort(list.subrange(1, n1), keys.subrange(1, n1)); k1 = l1[2]; l1 = l1[1]; l2 = ._merge_sort(list.subrange(n1 + 1, n2), keys.subrange(n1 + 1, n2)); k2 = l2[2]; l2 = l2[1]; list = []; keys = []; i = 1; j = 1; if (n > 30) pause(); while (i <= n1 && j <= n2) { if (k1[i] <= k2[j]) { list = [@list, l1[i]]; keys = [@keys, k1[i]]; i = i + 1; } else { list = [@list, l2[j]]; keys = [@keys, k2[j]]; j = j + 1; } } return [[@list, @l1.subrange(i), @l2.subrange(j)], [@keys, @k1.subrange(i), @k2.subrange(j)]]; }; public method .prefix() { arg list, prefix; var elem; return map elem in (list) to (prefix + elem); }; public method .valid_objects() { arg list; var obj; return filter obj in (list) where (valid(obj)); }; public method .grep() { arg lines, regexp; var line, result, out, reg; out = []; for line in [1 .. lines.length()] { reg = lines[line].match_regexp(regexp); if (reg) out = out + [[line, reg, lines[line]]]; } return out; }; public method .vcolumnize() { arg list, cols, [rest]; var linelength, sep, width, lines, i, j, line, outlist; linelength = [@rest, (| sender().linelen() |) || 79][1]; sep = [@rest, " ", " "][2]; lines = list.length() / cols + (list.length() % cols ? 1 : 0); width = linelength / cols; return ._vcolumnize(list, lines, cols, width, sep); }; public method .make() { arg n, [elt]; var i; elt = [@elt, 0][1]; return map i in [1 .. n] to (elt); }; public method ._vcolumnize() { arg list, lines, cols, width, [other]; var outlist, line, i, j, sep; sep = [@other, " "][1]; width -= sep.length(); lines = lines > list.length() ? list.length() : lines; outlist = []; for i in [1 .. lines] { line = list[i].pad(width); for j in [1 .. cols] (| (line = line + sep + list[i + j * lines].pad(width)) |); outlist = [@outlist, line]; } return outlist; }; public method .affix() { arg l1, l2; var last, first; // Combines l1 and l2 by appending the first element of l2 to the last // of l1. if (type(l2) != 'list) l2 = [l2]; last = (| l1.last() |) || ""; first = (| l2[1] |) || ""; l1 = [@l1.chop(), last + first]; if (l2.length() > 1) l1 = l1 + l2.subrange(2); return l1; }; public method .addkey() { arg l, key, val; var i; find i in [1 .. l.length()] where (l[i][1] == key); return i ? l.replace(i, [key, val]) : [@l, [key, val]]; }; public method .delkey() { arg l, key; var i; find i in [1 .. l.length()] where (l[i][1] == key); return i ? l.delete(i) : l; }; public method .getkey() { arg l, key; var i; find i in [1 .. l.length()] where (l[i][1] == key) || throw(~keynf, "Key not found."); return l[i][2]; }; public method .getkey_index() { arg l, key; var i; find i in [1 .. l.length()] where (l[i][1] == key) || throw(~keynf, "Key not found."); return i; }; public method .setremove_all() { arg list, remove; while (remove in list) list = .setremove(list, remove); return list; }; public method .vcolumnize2() { arg list, lines, [rest]; var linelength, sep, cols, width, i, j, line, outlist; linelength = [@rest, (| sender().linelen() |) || 79][1]; sep = [@rest, " ", " "][2]; cols = list.length() / lines + (list.length() % lines ? 1 : 0); width = linelength / cols; return ._vcolumnize(list, lines, cols, width, sep); }; public method .vcolumnize3() { arg list, lines, [rest]; var linelength, cols, width, i, j, line, outlist; linelength = [@rest, (| sender().linelen() |) || 79][1]; cols = list.length() / lines + (list.length() % lines ? 1 : 0); width = linelength / cols; outlist = []; for i in [1 .. lines] { line = ""; for j in [0 .. cols] (| (line = line + list[i + j * lines].pad(width)) |); outlist = [@outlist, line]; } return outlist; }; public method .vcolumnize4() { arg list, [args]; var linelength, sep, lines, cols, width, max; linelength = [@args, (| sender().linelen() |) || 79][1]; sep = [@args, "", ""][2]; max = .element_maxlength(list) + sep.length(); cols = linelength > max ? linelength / max : 1; width = linelength / cols; lines = list.length() / cols + (list.length() % cols ? 1 : 0); return ._vcolumnize(list, lines, cols, width, sep); }; public method .random() { arg list; return list[random(listlen(list))]; }; new object $dictionary: $libraries; var $root inited = 1; public method .map_method() { arg ls, what; var x, dict; // args[1] == list of objects // args[2] == symbol for method on objects // it will create a dictionary out of the two. dict = #[]; // Get list's method(whatever) and add it to the dictionary for x in [1 .. ls.length()] dict = dict.add(ls[x], ls[x].(what)()); return dict; }; public method .merge() { arg [args]; var x, dict, z, tule, dz, axz, keys; // merges all dictionaries into a single one, if they have the same key's -- // basing off of args[1] (this should be the longest list (i know, bad Lynx). dict = args[1]; keys = args[1].keys(); for x in [2 .. args.length()] { for z in (keys) { dz = dict[z]; axz = args[x][z]; if (type(dict[z]) == 'list) tule = dz; else tule = [dz]; tule = [@tule, axz]; dict = dict.add(z, tule); } } return dict; }; public method .to_list() { arg dict; var list, x, k; // merges into an associated list. k = dict.keys(); list = []; for x in (k) list = [@list, [x, dict[x]]]; return list; }; public method .merge_to_list() { arg [args]; var x, dict, z, tule, dz, axz, keys, list; // merges all dictionaries into a single list, where each related key // is merged with all it's other values as a subrange // basing off of args[1] (this should be the longest list (i know, bad Lynx). dict = .merge(@args); list = []; for z in (dict.keys()) list = [@list, dict[z]]; return list; }; public method .nunion() { arg dict1, dict2; var key; // like union() but for dictionaries. adds any keys from dict2 that don't // already exist in dict1 to dict1 and returns the result. Order of keys in // result is not guaranteed. if (dict1.keys().length() < dict2.keys().length()) { for key in (dict1) dict2 = dict2.add(key[1], key[2]); return dict2; } else { for key in (dict2) dict1 = dict1.add(key[1], key[2]); return dict1; } }; public method .replace() { arg dict, key, value; dict = (> dict.del(key) <); dict = (> dict.add(key, value) <); return dict; }; public method .apply() { arg tdict, list; var x; // Apply a translation-dict to a list for x in [1 .. list.length()] { catch ~keynf list = list.replace(x, tdict[list[x]]); } return list; }; public method .apply_to_keys() { arg tdict, dict; var x, newdict; // Apply a t-dict to the keys of a dict newdict = #[]; for x in (dict) { catch ~keynf x = x.replace(1, tdict[x[1]]); newdict = newdict.add(@x); } return newdict; }; public method .apply_to_values() { arg tdict, dict; var x, newdict; // Apply a t-dict to the values of a dict newdict = #[]; for x in (dict) { catch ~keynf x = x.replace(2, tdict[x[2]]); newdict = newdict.add(@x); } return newdict; }; public method .invert() { arg dict; var inverted, x; // Invert a dict (keys<->values) inverted = #[]; for x in (dict.keys()) inverted = inverted.add(dict[x], x); return inverted; }; public method .add_elem() { arg dict, key, elem; var value; // same as old dict_add_elem value = (| dict[key] |); if (type(value) != 'list && type(value) != 'error) throw(~type, "Value for key " + toliteral(key) + " (" + toliteral(value) + ") is not a list."); if (value) value = [@value, elem]; else value = [elem]; return dict.add(key, value); }; public method .del_elem() { arg dict, key, elem; var value; value = (| dict[key] |); if (type(value) != 'list && type(value) != 'error) throw(~type, "Value for key " + toliteral(key) + " (" + toliteral(value) + ") is not a list."); value = value.setremove(elem); if (!value) return dict.del(key); return dict.add(key, value); }; public method .add_elem_union() { arg dict, key, elem; var value; value = (| dict[key] |); if (value) value = setadd(value, elem); else value = [elem]; return dict_add(dict, key, value); }; public method .to_trie() { arg dict; var i, trie; trie = (<$trie, [0, ""]>); for i in (dict) { trie = trie.add(i[1], i[2]); refresh(); } return trie; }; public method .ordered_union() { arg dict1, dict2; var key; // like union() but for dictionaries. adds any keys from dict2 that don't // already exist in dict1 to dict1 and returns the result. // This one is guaranteed to pass the keys from argument1 to the argument2. It is more suitable for argument parsing. for key in (dict1) dict2 = dict2.add(key[1], key[2]); return dict2; // $#Copied 05 Jul 96 00:58 from $dictionary.union() by $jenner }; public method .setadd_elem() { arg dict, key, elem; var value; value = (| dict[key] |); if (value) value = setadd(value, elem); else value = [elem]; return dict_add(dict, key, value); }; new object $math: $libraries; var $root inited = 1; var $math pi = 3.14159; var $math pi2 = 6.28318; var $math origin_2d = [0.0, 0.0]; var $math origin_3d = [0.0, 0.0, 0.0]; var $math transmat_2d = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]; var $math transmat_3d = [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0]]; public method $math.polar_rectangular() { arg coords; return [coords[1] * cos(coords[2]), coords[1] * sin(coords[2])]; }; public method $math.rectangular_polar() { arg coords; var a; a = atan2(coords[2], coords[1]); if (a < 0) a += pi2; return [.distance(coords, origin_2d), a]; }; public method $math.pi() { return pi; }; public method $math.pi2() { return pi2; }; public method $math.deg_rad() { arg angle; return angle / 57.2958; }; public method $math.rad_deg() { arg angle; return angle * 57.2958; }; public method $math.matrix_add() { arg m1, m2; var i; return map i in [1 .. m1.length()] to (.add(m1[i], m2[i])); }; public method $math.matrix_sub() { arg m1, m2; var i; return map i in [1 .. m2.length()] to (.sub(m1[i], m2[i])); }; public method $math.matrix_mul() { arg m1, m2; var x, y; m2 = .transpose(m2); return map x in (m1) to (map y in (m2) to (.dot(x, y))); }; public method $math.spherical_rectangular() { arg coords; var r, phi, theta, r1; r = coords[1]; phi = coords[2]; theta = coords[3]; r1 = r * cos(theta); return [r1 * cos(phi), r1 * sin(phi), r * sin(theta)]; }; public method $math.rectangular_spherical() { arg coords; var a, d; a = atan2(coords[2], coords[1]); if (a < 0) a += pi2; return [(d = .distance(coords, origin_3d)), a, atan2(coords[3], .distance(coords.subrange(1, 2), origin_2d))]; }; public method $math.ident_mat() { arg n; var x, y; return map x in [1 .. n] to (map y in [1 .. n] to (x == y ? 1.0 : 0.0)); }; public method $math.translation_mat() { arg vector; var x, y; if (vector.length() == 2) return [@transmat_2d, [@vector, 1.0]]; else return [@transmat_3d, [@vector, 1.0]]; }; public method $math.rectangular_cylindrical() { arg coords; var a; a = atan2(coords[2], coords[1]); if (a < 0) a += pi2; return [.distance(coords, origin_2d), a, coords[3]]; }; public method $math.cylindrical_rectangular() { arg coords; return [coords[1] * cos(coords[2]), coords[1] * sin(coords[2]), coords[3]]; }; public method $math.matrix_scale() { arg s, m; var x; return map x in (m) to (.scale(s, x)); }; public method $math.tensor() { arg v1, v2; var x, y; return map x in (v1) to (map y in (v2) to (x * y)); }; public method $math.skew() { arg v; return [[0.0, v[3], -v[2]], [-v[3], 0.0, v[1]], [v[2], -v[1], 0.0]]; }; public method $math.rotation_mat_3d() { arg axis, angle; var s, c, m, tens; s = sin(angle); c = cos(angle); if (type(axis) == 'list) { axis = .scale(1.0 / .distance(axis, origin_3d), axis); tens = .tensor(axis, axis); m = .matrix_add(tens, .matrix_add(.matrix_scale(s, .skew(axis)), .matrix_scale(c, .matrix_sub(.ident_mat(3), tens)))); return [[@m[1], 0.0], [@m[2], 0.0], [@m[3], 0.0], [0.0, 0.0, 0.0, 1.0]]; } else { switch (axis) { case 'z: return [[c, s, 0.0, 0.0], [-s, c, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]]; case 'y: return [[c, 0.0, -s, 0.0], [0.0, 1.0, 0.0, 0.0], [s, 0.0, c, 0.0], [0.0, 0.0, 0.0, 1.0]]; case 'x: return [[1.0, 0.0, 0.0, 0.0], [0.0, c, s, 0.0], [0.0, -s, c, 0.0], [0.0, 0.0, 0.0, 1.0]]; } } }; public method $math.transform_vect() { arg m, v; var x, outvect, flag; if (m.length() == v.length() + 1) { v = [@v, 1.0]; flag = 1; } outvect = map x in (m) to (.dot(x, v)); return flag ? outvect.subrange(1, outvect.length() - 1) : outvect; }; public method $math.rotation_mat_2d() { arg angle; var s, c; s = sin(angle); c = cos(angle); return [[c, s, 0.0], [-s, c, 0.0], [0.0, 0.0, 1.0]]; }; public method $math.scale_mat() { arg scale; if (scale.length() == 2) return [[scale[1], 0.0, 0.0], [0, scale[2], 0.0], [0.0, 0.0, 1.0]]; else return [[scale[1], 0.0, 0.0, 0.0], [0.0, scale[2], 0.0, 0.0], [0.0, 0.0, scale[3], 0.0], [0.0, 0.0, 0.0, 1]]; }; new object $parse_lib: $libraries; var $root inited = 1; var $parse_lib boolean_strs = [["yes", "true", "1", "on"], ["no", "false", "0", "off"]]; public method .get_name() { arg obj; var name; if (!valid(obj)) return ("** Invalid " + toliteral(obj)) + " **"; return obj.objname(); }; public method $parse_lib.boolean() { arg str; if (str in boolean_strs[1]) return 1; else if (str in boolean_strs[2]) return 0; else throw(~unknown, "Boolean flag not recognized."); }; public method $parse_lib.html_traceback() { arg t, status; var line, out, x; out = "<h2>" + t[1][2].filter_line() + "</h2>"; out += "<i><b>Thrown by " + ._html_traceback(@t[2]) + "</b></i>\n<p>"; for x in [3 .. listlen(t)] out += "<code><i>" + t[x][1] + "</i>: " + ._traceback(@t[x]) + "</code><br>"; return $sys.response(status, out + "</p>"); }; private method ._html_traceback() { arg type, what, [more]; var line; switch (type) { case 'function: return "function <tt>" + what + "()</tt>"; case 'opcode: return "operator <tt>" + what + "</tt>"; default: line = "method <tt>" + more[2] + "." + tostr(what) + "()</tt>"; if (more[1] != more[2]) line = line + " (" + more[1] + ")"; line = line + " line " + more[3]; return line; } }; public method $parse_lib.traceback() { arg traceback, [args]; var line, out, pre, lines, cur, x, error; // $parse_lib.traceback(traceback(), lines, pre); // -1 lines represents the full error // pre is set to "! " unless otherwise specified. lines = [@args, -1][1]; pre = [@args, "! ", "! "][2]; error = [@args, 0, 0, 0][3]; out = [pre + "=> " + traceback[1][2]]; pre = pre + " "; if (error == 0) out = [@out, pre + "Thrown by " + ._traceback(@traceback[2].subrange(2))]; else out = [@out, pre + "Error " + error + " caused by " + ._traceback(@traceback[2].subrange(2))]; for x in [1 .. traceback.length() - 2] { if (x <= lines || lines == -1) { line = traceback[x + 2][1] + ": "; line = line + ._traceback(@traceback[x + 2].subrange(2)); out = [@out, pre + line]; } } return out; }; public method $parse_lib._traceback() { arg what, [more]; var line; if (more) { if (more[1] == more[2]) return more[1] + "." + what + "() line " + more[3]; else return more[2] + "." + what + "() (" + more[1] + ") line " + more[3]; } else { return what; } }; public method $parse_lib.range() { arg str; var out; out = str.split(" *- *"); if (out.length() == 1) { if ("," in str) return ['specific, str]; out = [(> ._range(str) <), 'single]; } else if (out.length() == 2) { out = out.replace(1, (> ._range(out[1]) <)); out = out.replace(2, (> ._range(out[2]) <)); } else { throw(~range, "Invalid range reference."); } return out; }; public method $parse_lib._range() { arg str; if (str.is_numeric()) { return toint(str); } else { switch (str[1]) { case "$": return 'end; case ".": return 'current; case "^": return 'start; default: throw(~range, "Invalid range reference."); } } }; public method $parse_lib.getopt() { arg line, [defaults]; var out, newlist, part, v, opt, t, templates, keys, key, l, x; // submit: [["template", value], [...]]; // => if value is 1, it will take the next part of the string // receive: [["template", "flag", bool, value]], [...]]; line = line.explode_quoted(); out = []; newlist = []; defaults = (| defaults[1] |) || []; templates = defaults.slice(1); x = 1; l = line.length(); while (1) { if (x > l) break; if (line[x][1] in ["-", "+"]) { opt = 0; v = ""; part = line[x].subrange(2); for t in [1 .. templates.length()] { if ("=" in part) { part = part.explode("="); v = (| part[2] |) || ""; part = part[1]; } if (part.match_template(templates[t])) { opt = [templates[t], part, line[x][1] == "+"]; if ((| defaults[t][2] |) && !v) { if (x + 1 <= l) { x = x + 1; if (line[x] == "=") { if (x + 1 <= l) x = x + 1; } v = line[x]; } } opt = opt + [v]; } } if (!opt) opt = [0, part, line[x][1] == "+", ""]; out = out + [opt]; } else { newlist = newlist + [line[x]]; } x = x + 1; } return [newlist, out]; }; public method $parse_lib.ref() { arg str, [args]; var def, me, obj, reg, member, match, type, second; me = [@args, sender()][1]; if (args.length() > 1) match = args[2]; else match = [me, 'match_environment, []]; if (str == ".") { // shortcut obj = (> match[1].(match[2])("", @match[3]) <); return ['object, obj, obj, 0, 0]; } if ((reg = regexp(str, "^(.*)<([^>]*)>(.*)$"))) { def = (> match[1].(match[2])(reg[2], @match[3]) <); str = reg[1] + reg[3]; } if ((reg = regexp(str, "([^\.,]*)([\.,]+)([^\( ]*)"))) { obj = reg[1]; member = reg[3]; type = reg[2]; if (type.length() > 1 && type[1] == "." && !obj) { type = type.subrange(2); obj = (> match[1].(match[2])("", @match[3]) <); } else { obj = obj ? (> match[1].(match[2])(obj, @match[3]) <) : me; } if ("." in type) { if ("," in type) second = 'variable; type = 'method; } else { type = 'variable; } } else { obj = (> match[1].(match[2])(str, @match[3]) <); type = 'object; } return [type, obj, def || obj, member, second]; }; new object $buffer: $libraries; var $root inited = 1; public method .to_list() { arg buf; var idx, list; list = []; for idx in [1 .. buf.length()] list = list + [buf[idx]]; return list; }; public method .from_list() { arg list; var buf, x; buf = `[]; for x in [1 .. list.length()] buf = buf.add(list[x]); return buf; }; new object $time: $libraries; var $root inited = 1; new object $integer: $libraries; var $root inited = 1; new object $http: $libraries; var $root inited = 1;