// ------------------------------------------------------------------ // Just a placeholder new object $libraries: $root; var $root inited = 1; var $root objname = 'libraries; // ------------------------------------------------------------------ // $string library new object $string: $libraries; var $root inited = 1; var $root objname = 'string; var alphabet = "abcdefghijklmnopqrstuvwxyz"; var numbers = "1234567890"; var 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 .. string.length()] { 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 explode { arg [args]; return (> explode(@args) <); }; public method match_template { arg template, string; return (> match_template(template, string) <); }; public method match_pattern { arg pattern, string; return (> match_pattern(pattern, string) <); }; public method match_regexp { arg [args]; return (> match_regexp(@args) <); }; 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 pad { arg [args]; return (> pad(@args) <); }; public method length { arg str; return (> strlen(str) <); }; public method subrange { arg [args]; return (> substr(@args) <); }; public method match_begin { arg [args]; return (> match_begin(@args) <); }; public method crypt { arg [args]; return (> crypt(@args) <); }; public method uppercase { arg string; return (> uppercase(string) <); }; public method lowercase { arg string; return (> lowercase(string) <); }; public method compare { arg str1, str2; return (> strcmp(str1, str2) <); }; public method format { arg format, [args]; return (> strfmt(format, args) <); }; public method to_symbol { arg [args]; return (> tosym(@args) <); }; public method replace { arg [args]; return (> strsub(@args) <); }; // ------------------------------------------------------------------ // $list library new object $list: $libraries; var $root inited = 1; var $root objname = 'list; 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 delete { arg [args]; return (> delete(@args) <); }; public method replace { arg [args]; return (> replace(@args) <); }; public method graft { 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 length { arg l; return (> listlen(l) <); }; public method union { arg [args]; return (> union(@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 setadd { arg [args]; return (> setadd(@args) <); }; 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 setremove { arg [args]; return (> setremove(@args) <); }; public method insert { arg [args]; return (> insert(@args) <); }; public method subrange { arg [args]; return (> sublist(@args) <); }; 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; }; // ------------------------------------------------------------------ // $parse library new object $parse: $libraries; var $root inited = 1; var $root objname = 'parse; 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 handler { return toliteral(data); } } }; public method filter_for_html { arg text; var x, line; for x in [1 .. listlen(text)] { if (text[x]) { line = text[x].replace("&", "&").replace("<", "<").replace(">", ">"); text = text.replace(x, line); } } 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]; }; // ------------------------------------------------------------------ // $dictionary library new object $dictionary: $libraries; var $root inited = 1; var $root objname = 'dictionary; 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 { arg [args]; return (> dict_add(@args) <); }; public method del { arg [args]; return (> dict_del(@args) <); }; public method keys { arg dict; return (> dict_keys(dict) <); }; 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); }; public method contains { arg [args]; return (> dict_contains(@args) <); }; // ------------------------------------------------------------------ new object $buffer: $libraries; var $root inited = 1; var $root objname = 'buffer; public method to_list { arg buf; var idx, list; list = []; for idx in [1 .. buf.length()] list = list + [buf.retrieve(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; }; public method length { arg buffer; return (> buffer_len(buffer) <); }; public method retrieve { arg buffer, position; return (> buffer_retrieve(buffer, position) <); }; public method append { arg buffer, buffer_2; return (> buffer_append(buffer, buffer_2) <); }; public method replace { arg buffer, position, value; return (> buffer_replace(buffer, position, value) <); }; public method add { arg buffer, value; return (> buffer_add(buffer, value) <); }; public method truncate { arg buffer, position; return (> buffer_truncate(buffer, position) <); }; public method tail { arg buffer, position; return (> buffer_tail(buffer, position) <); }; public method to_string { arg buffer; return (> buffer_to_string(buffer) <); }; public method to_strings { arg [args]; return (> buffer_to_strings(@args) <); }; public method from_string { arg string; return (> buffer_from_string(string) <); }; public method from_strings { arg strings, [seperator]; return (> buffer_from_strings(strings, @seperator) <); }; // ------------------------------------------------------------------ // $time library new object $time: $libraries; var $root inited 1 var $root objname 'time public method format { arg [args]; return (> strftime(@args) <); };