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;