// ----------------------------------------------------------------- // // String Library // // ----------------------------------------------------------------- new object $string: $root; public method .trim(): native { arg str, [dir]; var reg, range; str = strsed("^ *", str, ""); return strsed(" *$", str, ""); }; public method .is_numeric() { arg string; return toint(string) || string == "0"; }; public method .strip() { arg str; return strsed("[^a-z0-9_]", str, "", "g") || str; }; public method .chop() { arg str, len, [end]; // chops string off strlen(end) characters before len and appends len end = [@end, "..."][1]; if (strlen(str) < len) return str; if (strlen(str) < strlen(end)) return str; return pad(str, len - strlen(end)) + end; }; 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 .to_list() { arg str; str = .strip(str); return (> tosym(str) <); }; // ------------------------------ // // here on down is web-specific library 'schtuff' // public method .break_fields() { arg args; var fields, field, values; fields = #[]; for field in (args.explode("&")) { field = field.explode("="); if (listlen(field) == 1) field = [field[1], ""]; fields = fields.add(@field); } return fields; }; 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 .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(">", ">"); }; // ----------------------------------------------------------------- // // Dictionary library // // ----------------------------------------------------------------- new object $dictionary: $root; public method .to_list() { arg dict; var list, x, k; // merges into an associated list. k = dict_keys(dict); list = []; for x in (k) list = [@list, [x, dict[x]]]; return list; }; public method .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. Order of keys in // result is not guaranteed. for key in (dict1) dict2 = dict_add(dict2, 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(dict); list = []; for x in (k) list = [@list, dict[x]]; return list; }; public method .replace() { arg dict, key, value; dict = (> dict_del(dict, key) <); dict = (> dict_add(dict, key, value) <); return dict; }; public method .invert() { arg dict; var inverted, x; // Invert a dict (keys<->values) inverted = #[]; for x in (dict_keys(dict)) inverted = dict_add(inverted, 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 " + key + " (" + value + ") is not a list."); if (value) value = [@value, elem]; else value = [elem]; return dict_add(dict, 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 " + key + " (" + value + ") is not a list."); value = setremove(value, elem); if (!value) return dict_del(dict, key); return dict_add(dict, key, value); }; public method .add(): native; public method .del(): native; public method .keys(): native; public method .contains(): native; new object $list: $root; public method .graft_strings() { arg list; var word, out, i; out = []; word = ""; for i in (list) { if (type(i) != 'symbol) { word += i; } else { out += [word, i]; word = ""; } } if (word) out += [word]; return out; }; public method .prefix() { arg list, prefix; var out, elem; out = []; for elem in (list) out = out + [prefix + elem]; return out; }; // Combines l1 and l2 by appending the first element of l2 to the last // of l1. public method .graft() { arg l1, l2; var last, first; if (type(l2) != 'list) l2 = [l2]; last = (| l1.last() |) || ""; first = (| l2[1] |) || ""; l1 = [@l1.chop(), last + first]; if (l2.length() > 1) l1 = l1 + sublist(l2, 2); return l1; }; public method .to_string() { arg list, [sep]; var str, part; if (!list) return ""; sep = [@sep, " "][1]; str = list[1]; for part in (delete(list, 1)) str = str + sep + part; return str; }; public method .to_english() { arg list, [options]; var empty, and, sep; empty = [@options, "nothing"][1]; switch (listlen(list)) { case 0: return empty; case 1: return tostr(list[1]); } and = [@options, " and ", " and "][2]; sep = [@options, ", ", ", ", ", "][3]; return .to_string(delete(list, listlen(list)), sep) + and + list[listlen(list)]; }; 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 .filter() { arg list, method, [args]; var out, x; // similar to .map, but returns a list of objects which returned a // true value from 'method. out = []; for x in (list) { if (x.(method)(@args)) out = [@out, x]; } return out; }; public method .element_maxlength() { arg list; var elm, max, len; for elm in (list) { len = tostr(elm).length(); if (len > max) max = len; } return max; }; public method .col2() { arg list, [args]; var lines, line, l1, l2, l, x, out, diff, sep, nl; sep = [@args, " "][1]; nl = [@args, "\n", "\n"][2]; l = listlen(list); if (l == 1) return list; // if it is an odd length, add one to it (it rounds down, and we want up) if (l % 2) diff = 1; l = (l / 2); l1 = sublist(list, 1, l + diff); l2 = sublist(list, l + diff + 1); out = []; for x in [1 .. l] out = [@out, l1[x] + sep + l2[x] + nl]; if (diff) out = [@out, l1[l+1] + nl]; return out; }; public method .lcolumnize() { arg list, [args]; var line, part, lines, max, cols, col, width, len, sep; len = [@args, 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 + pad(part, width); } if (line) return lines + [line]; return lines; }; 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 - strlen(separator); lines = []; while (list) { line = pad(list[1], width); list = sublist(list, 2); for curcol in [2 .. cols] { if (list) { line = line + separator + pad(list[1], width); list = sublist(list, 2); } } lines = [@lines, line]; } return lines; }; 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[listlen(list)]; }; public method .count() { arg elem, list; var count; // count of elem in list while (elem in list) { count = count + 1; list = sublist(list, (elem in list) + 1); } return count; }; public method .numbered_text() { arg text; var line; // returns that list with line numbers pre-pended for line in [1 .. listlen(text)] text = replace(text, line, (line < 10 ? " " | "") + line + ": " + text[line]); return text; }; 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 .swap() { arg list, a, b; var holder; // swap elements at indexes a and b holder = (> list[a] <); list = (> replace(list, a, list[b]) <); list = replace(list, b, holder); return list; }; public method .max() { arg list; // return greatest element of list (no type checking performed) // if list is [], returns 0 while (listlen(list) > 1) { if (list[1] > list[2]) list = delete(list, 2); else list = delete(list, 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 (listlen(list) > 1) { if (list[1] < list[2]) list = delete(list, 2); else list = delete(list, 1); } return [@list, 0][1]; }; 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 .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 .subrange(): native; public method .sum() { arg ints; var ret; // returns a sum of each integer in the list. ret = 0; while (ints) { ret = ret + ints[1]; ints = delete(ints, 1); } return ret; }; public method .to_buffer() { arg list; // assumes this is a list of strings (for now) return strings_to_buf(list); }; public method .delete(): native; public method .replace(): native; 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 (| sublist(list, 1, listlen(list) - count) |) || []; }; public method .length(): native; public method .union(): native; public method .del() { arg list, element; return (> setremove(list, element) <); }; public method .add() { arg list, element; return (> setadd(list, element) <); }; public method .tb_to_html() { 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>"); }; public method .tb_to_text() { arg t; var line, out, x; out = "=> " + t[1][2]; out = [out, "Thrown by " + ._traceback(@t[2])]; for x in [3 .. listlen(t)] out = [@out, t[x][1] + ": " + ._traceback(@t[x])]; return out; }; 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; } }; private method ._traceback() { arg type, what, [more]; var line; switch (type) { case 'function: return "function " + what + "()"; case 'opcode: return "operator " + what; default: line = "method " + more[2] + "." + tostr(what) + "()"; if (more[1] != more[2]) line = line + " (" + more[1] + ")"; line = line + " line " + more[3]; return line; } }; new object $time: $root; new object $http: $root;