new object $libraries: $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 .trim() { arg string, [dir]; var reg, range; // trim whitespace from string, args can be 'left or 'right if (type(string) != 'string) throw(~invarg, "Argument must be a string."); dir = [@dir, 'right][1]; if (dir == 'left) { range = "[^ ] *$".match_regexp(string); if (!range) return string; range = [1, (range[1])[2]]; } else { range = "[^ ]".match_regexp(string); if (!range) return string; range = [(range[1])[1]]; } return .subrange(@range); }; public method .alphabet() { return alphabet; }; public method .numbers() { return numbers; }; public method .non_alphanumeric() { return non_alphanumeric; }; 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; // strips all of "strip" characters from the string // if "strip" is -1 it will use .non_alphanumeric() if ((type(string) != 'string) || ((type(strip) != 'string) && (strip != (-1)))) throw(~type, "First argument must be a string, second can be -1"); new_str = ""; if (strip == (-1)) new_str = non_alphanumeric; for char in [1 .. strlen(string)] { if (!((string[char]) in strip)) new_str = new_str + (string[char]); } return new_str; }; 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; // explodes an english list ("foo, bar and zoo"). line = line.explode(","); output = []; for x in (line) { x = .trim(x); if ((| x.subrange(1, 3) |) == "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 = .to_string(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 .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), " "); output = [@output, string.subrange(1, cutoff - 1)]; string = prefix + (string.subrange(cutoff + 1)); } return [@output, string]; }; 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), " "); output = (output + "\n") + (string.subrange(1, cutoff - 1)); string = prefix + (string.subrange(cutoff + 1)); } return (output + "\n") + string; }; public method .rindex() { arg str, c; var i; i = strlen(str); while (i) { if ((str[i]) == c) return i; i = i - 1; } return 0; }; public method .is_boolean() { arg str; if ((.match_begin("yes", str)) || ((.match_begin("true", str)) || (str == "1"))) return 1; else if ((.match_begin("no", str)) || ((.match_begin("false", str)) || (str == "0"))) return 0; else return -1; }; 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 .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 .regexp() { arg regexp, string; var match, m, complete, out; match = $string.match_regexp(regexp, string); 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 .to_symbol() { arg [args]; return (> tosym(@args) <); }; new object $list: $libraries; var $root inited = 1; public method .to_string() { arg list, [sep]; var str, part; // uses $parse.unparse() rather than tostr() if (!list) return ""; sep = [@sep, " "][1]; str = tostr(list[1]); for part in (list.delete(1)) str = (str + sep) + tostr(part); return str; }; public method .to_english() { arg list, [options]; var empty, and, sep; // uses $parse.unparse() rather than tostr() 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 ((.to_string(list.delete(list.length()), sep)) + and) + tostr(list[list.length()]); }; public method .map() { arg list, method, [args]; var out, x; // call 'method on each object, return results. out = []; for x in (list) out = [@out, x.(method)(@args)]; return out; }; public method .reverse() { arg list; var elm, reversed; // .reverse(list) // -> list with its elements reversed reversed = []; for elm in (list) reversed = [elm, @reversed]; return reversed; }; public method .compress() { arg list; var out, last, x; // [a,a,b,b,c,c,d,d] => [a,b,c,d] // removes duplicate entries in a list out = []; for x in (list) { if (!(x in out)) out = [@out, x]; } return out; }; public method .last() { arg list; return list[list.length()]; }; public method .slice() { arg big_list, element; var list, ret_list; // Return elementh' element of all lists in big_list // No type or length checking done for speed purposes. ret_list = []; for list in (big_list) ret_list = [@ret_list, list[element]]; return ret_list; }; public method .max() { arg list; // return greatest element of list (no type checking performed) // if list is [], returns 0 while ((list.length()) > 1) { if ((list[1]) > (list[2])) list = list.delete(2); else list = list.delete(1); } return [@list, 0][1]; }; public method .min() { arg list; // return least element of list (no type checking performed) // if list is [], returns 0 while ((list.length()) > 1) { if ((list[1]) < (list[2])) list = list.delete(2); else list = list.delete(1); } return [@list, 0][1]; }; 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 .flatten() { arg list; var toret, elem; // [[[x], x], x] => [x, x, x] toret = []; for elem in (list) { if (type(elem) == 'list) toret = toret + (.flatten(elem)); else toret = toret + [elem]; } return toret; }; public method .to_buffer() { arg [args]; return (> $buffer.from_strings(@args) <); }; public method .setdifference() { 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 .setcontains() { 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 .setequal() { 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 if (list == []) return 0; 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]; // 9-25-95/21:26 Jenner ($jenner), moved from $jenner.msort }; public 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 out, elem; out = []; for elem in (list) out = out + [prefix + elem]; return out; }; public method .valid_objects() { arg list; var obj; for obj in (list) { if (!valid(obj)) list = list.setremove(obj); } return list; }; new object $parse: $libraries; var $root inited = 1; public method .html_traceback() { arg status, t; var line, out, x; out = ("<h2>" + ((t[1])[2])) + "</h2>"; out = [out, ("<i><b>Thrown by " + (._html_traceback(@t[2]))) + "</b></i>", "<p>"]; for x in [3 .. listlen(t)] { line = ("<code><i>" + ($parse.unparse((t[x])[1]))) + "</i>: "; out = out + [(line + (._traceback(@t[x]))) + "</code><br>"]; } return .response(status, [@out, "</p>"]); }; public method .traceback() { arg t; var line, out, x; out = "=> " + ((t[1])[2]); out = [out, "Thrown by " + (._traceback(@t[2]))]; for x in [3 .. listlen(t)] { line = ($parse.unparse((t[x])[1])) + ": "; line = line + (._traceback(@t[x])); out = [@out, line]; } return out; }; public method ._html_traceback() { arg type, what, [more]; var line; switch (type) { case 'function: return ("function <tt>" + tostr(what)) + "()</tt>"; case 'opcode: return ("operator <tt>" + ($parse.unparse(what))) + "</tt>"; default: line = ((($parse.unparse(more[2])) + ".") + tostr(what)) + "()"; if ((more[1]) != (more[2])) line = ((line + " (") + ($parse.unparse(more[1]))) + ") "; line = (line + " line ") + tostr(more[3]); return line; } }; public method ._traceback() { arg type, what, [more]; var line; switch (type) { case 'function: return ("function " + tostr(what)) + "()"; case 'opcode: return "operator " + ($parse.unparse(what)); default: line = ((($parse.unparse(more[1])) + ".") + tostr(what)) + "()"; if ((more[1]) != (more[2])) line = ((line + " (") + ($parse.unparse(more[2]))) + ") "; line = (line + " line ") + tostr(more[3]); return line; } }; public method .get_name() { arg obj; var name; if (!valid(obj)) return ("** Invalid " + toliteral(obj)) + " **"; return obj.objname(); }; public method .unparse() { arg data; var str, element, association, pos, method; switch (type(data)) { case 'integer, 'float: return tostr(data); case 'string, 'symbol, 'error, 'buffer: return toliteral(data); case 'dbref: return .get_name(data); case 'list: if (!data) return "[]"; str = "["; for element in (sublist(data, 1, listlen(data) - 1)) { str = str + (.unparse(element)); str = str + ", "; } str = str + (.unparse(data[listlen(data)])); return str + "]"; case 'dictionary: if (!data) return "#[]"; str = "#["; for association in (data) { str = str + (.unparse(association)); str = str + ", "; } return substr(str, 1, strlen(str) - 2) + "]"; case 'frob: catch any return data.unparse(); with return toliteral(data); } }; public method .filter_for_html() { arg text; var x, line; for x in [1 .. listlen(text)] { if (text[x]) text = replace(text, x, strsub(strsub(strsub(text[x], "&", "&"), "<", "<"), ">", ">")); } return text; }; public method .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 ($string.match_template(templates[t], part)) { 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]; }; new object $dictionary: $libraries; var $root inited = 1; 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 .union() { arg dict1, dict2; var key; for key in (dict1) dict2 = dict2.add(key[1], key[2]); return dict2; }; public method .values() { arg dict; var list, x, k; // returns values same as dict_keys() returns keys. k = dict.keys(); list = []; for x in (k) list = [@list, dict[x]]; return list; }; 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 " + ($parse.unparse(key))) + " (") + ($parse.unparse(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 " + ($parse.unparse(key))) + " (") + ($parse.unparse(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 = value.union([elem]); else value = [elem]; return dict.add(key, value); }; 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;