/
CDC-1.2b/
CDC-1.2b/src/
parent $libraries
object $parse

var $root child_index 0
var $root owners [$parse]
var $root fertile 0
var $root inited 1
var $root owned [$parse]
var $parse boolean_strs [["yes", "true", "1", "on"], ["no", "false", "0", "off"]]
var $root manager $parse
var $root writable [$parse]
var $root readable ['parameters, 'methods, 'code]
var $root dbref 'parse

method reference
    arg string, [sep];
    var middle, ref;
    
    // receives: "<object><seperator><method/param>"
    // returns ["<object>", "<method/param>"]
    // seperator defaults to a period.
    sep = [@sep, "."][1];
    middle = sep in string;
    if (!middle)
        ref = [string, ""];
    else if (middle == 1)
        ref = [sender().namef(['dbref]), substr(string, 2)];
    else
        ref = [substr(string, 1, middle - 1), substr(string, middle + 1)];
    
    // assumes "()" will appear at the end of the reference if at all,
    // and strips it off if present
    if ("()" in (ref[2]))
        ref = [ref[1], substr(ref[2], 1, ("()" in (ref[2])) - 1)];
    return ref;
.

method object_match
    arg name, [who];
    var msg;
    
    // .object_match("name"[, who])
    // -> 0        name was the empty string
    // -> ~objnf   nothing matched name
    // -> ~ambig   more than one object matched name
    // Attempt to match an object name with who.match_environment().  If one is found, return it.  Else, print a message and return one of the above false values.
    // 'who' defaults to sender().
    who = who ? who[1] | sender();
    if (!name) {
        (| who.tell("You must give the name of something.") |);
        return 0;
    }
    catch ~objnf, ~ambig {
        return who.match_environment(name);
    } with handler {
        switch (error()) {
            case ~objnf:
                msg = ("I don't see any \"" + name) + "\" here.";
            case ~ambig:
                msg = ("I don't know which \"" + name) + "\" you mean.";
        }
        (| who.tell(msg) |);
        return error();
    }
.

method tell_error
    arg problem, [args];
    var who, syntax, line, sprefix, prefix;
    
    // arg 1 == error
    // arg 2 (opt) == syntax
    // arg 3 (opt) == who
    // arg 4 (opt) == subbing object (in place of 'Object') -- string.
    syntax = [@args, 0][1];
    who = [@args, sender(), sender()][2];
    sprefix = (| sender().setting("error-syntax-prefix") |) || "=> ";
    prefix = (| sender().setting("error-prefix") |) || "!  ";
    if (syntax)
        who.tell(((sprefix + "Syntax: `") + syntax) + "`");
    if (problem) {
        if (type(problem) == 'string) {
            problem = $string.wrap_line(problem, (| who.linelen() |) || 79, prefix, 1);
        } else {
            for line in [1 .. listlen(problem)]
                problem = replace(problem, line, prefix + (problem[line]));
        }
        who.tell(problem);
    }
    throw(~stop, "", 'no_traceback);
.

method usage
    arg method, [dbref];
    var code, extracted;
    
    // .usage(method[, dbref])
    // Extract initial comments from the given method, returning them as a list of strings.
    // Throw ~methodnf if dbref does not define method.
    // dbref defaults to sender.
    dbref = dbref ? dbref[1] | sender();
    dbref = dbref.find_method(method);
    code = dbref.list_method(method);
    extracted = [];
    if ((code[1]) == "disallow_overrides;")
        code = delete(code, 1);
    if (("arg " in (code[1])) == 1)
        code = delete(code, 1);
    if (("var " in (code[1])) == 1)
        code = delete(code, 1);
    while (!(code[1]))
        code = delete(code, 1);
    while (("//" in (code[1])) == 1) {
        extracted = [@extracted, strsub(code[1], "//", "")];
        code = delete(code, 1);
    }
    return extracted;
.

method match
    arg string;
    var loc, obj;
    
    // called by $user.match_* methods, simply parses up the basic me/here/$*
    if (string == "me")
        return sender();
    if (string == "here")
        return sender().location();
    if (string && ((string[1]) == "$")) {
        obj = toobj(substr(string, 2));
        if (!valid(obj))
            throw(~objnf, "No such object " + string);
        return obj;
    } else {
        return 0;
    }
.

method 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.");
.

method full_reference
    arg str, [args];
    var sep, defobj, middle, ref, type, match;
    
    defobj = [@args, sender()][1];
    match = [@args, [$object, 'to_dbref], [$object, 'to_dbref]][2];
    if ("()" in str)
        str = substr(str, 1, ("()" in str) - 1);
    if ("." in str) {
        type = 'method;
        sep = ".";
    } else if ("," in str) {
        type = 'parameter;
        sep = ",";
    } else {
        type = 'unknown;
        sep = ".";
    }
    middle = sep in str;
    if (!middle)
        ref = [(> (match[1]).(match[2])(str) <), ""];
    else if (middle == 1)
        ref = [defobj, substr(str, 2)];
    else
        ref = [(> (match[1]).(match[2])(substr(str, 1, middle - 1)) <), substr(str, middle + 1)];
    return [type, ref[1], (ref[2]) ? tosym(ref[2]) | 0];
.

method traceback
    arg traceback, [args];
    var line, out, pre, lines, cur, x, error;
    
    // $parse.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(@sublist(traceback[2], 2)))];
    else
        out = [@out, (((pre + "Error ") + toliteral(error)) + " thrown by ") + (._traceback(@sublist(traceback[2], 2)))];
    
    //
    for x in [1 .. listlen(traceback) - 2] {
        if ((x <= lines) || (lines == (-1))) {
            line = ($data.unparse((traceback[x + 2])[1])) + ": ";
            line = line + (._traceback(@sublist(traceback[x + 2], 2)));
            out = [@out, pre + line];
        }
    }
    return out;
.

method _traceback
    arg what, [more];
    var line;
    
    if (more)
        return (((((($data.unparse(more[1])) + ".") + tostr(what)) + "() (") + ($data.unparse(more[2]))) + ") line ") + tostr(more[3]);
    else
        return tostr(what);
.

method options
    arg line, [defaults];
    var loc, opt, x, out, defs;
    
    // this will not pay attention to groupings with quotes, should add
    // the functionality in later.
    // templates: #[["opt", [0/1, 0/1]]]; / #[['opt, [bool, value]]]; 
    defaults = [@defaults, #[]][1];
    if (!(("-" in line) || ("+" in line)))
        return [explode(line), defaults];
    line = explode(line);
    out = line;
    for x in [1 .. listlen(line)] {
        if (((line[x])[1]) in ["-", "+"]) {
            out = setremove(out, line[x]);
            opt = substr(line[x], 2);
            defs = (| defaults[opt] |) || [0, ""];
            defs = [((line[x])[1]) in ["+"], defs[2]];
            if (defs[2]) {
                if (listlen(line) >= (x + 1)) {
                    defs = [defs[1], line[x + 1]];
                    out = setremove(out, line[x + 1]);
                }
            }
            defaults = dict_add(defaults, opt, defs);
        }
    }
    return [out, defaults];
.

method my_options
    arg line, options;
    var args, word, c, pos, key, p, p2;
    
    word = "";
    args = "";
    while (line) {
        c = line[1];
        switch (c) {
            case "+":
                pos = " " in line;
                if (!pos) {
                    key = substr(line, 2);
                    line = "";
                } else {
                    key = substr(line, 2, pos);
                    line = substr(line, pos);
                }
                key = tosym(key);
                catch ~keynf {
                    if (((options[key])[1]) == 'bool)
                        options = dict_add(options, key, ['bool, 1]);
                    else
                        throw(~opttype, ("Option " + tostr(key)) + " is not boolean.");
                } with handler {
                    throw(~badopt, ("Option " + tostr(key)) + " is not a valid option.");
                }
            case "-":
                pos = " " in line;
                if (!pos) {
                    key = substr(line, 2);
                    line = "";
                } else {
                    key = substr(line, 2, pos);
                    line = substr(line, pos);
                }
                key = tosym(key);
                catch ~keynf {
                    if (((options[key])[1]) == 'bool)
                        options = dict_add(options, key, ['bool, 0]);
                    else
                        throw(~opttype, ("Option " + tostr(key)) + " is not boolean.");
                } with handler {
                    throw(~badopt, ("Option " + tostr(key)) + " is not a valid option.");
                }
            case "=":
                key = word;
                word = "";
                pos = " " in line;
                if (!pos)
                    pos = strlen(line);
                key = tosym(key);
                catch ~keynf {
                    if (((options[key])[1]) == 'value)
                        options = dict_add(options, key, ['value, substr(line, 2, pos - 1)]);
                    else
                        throw(~opttype, ("Option " + tostr(key)) + " is not a value.");
                } with handler {
                    throw(~badopt, ("Option " + tostr(key)) + " is not a valid option.");
                }
                line = substr(line, pos + 1);
            case " ":
                args = args + word;
                word = "";
                line = substr(line, 2);
            default:
                pos = " " in line;
                if (!pos)
                    pos = strlen(line);
                p = "=" in line;
                if ((p > 0) && (p < pos)) {
                    word = word + substr(line, 1, p - 1);
                    line = substr(line, p);
                } else {
                    p = "-" in line;
                    p2 = "+" in line;
                    if (p2 < p)
                        p = p2;
                    if ((p > 0) && (p < pos)) {
                        args = args + substr(line, 1, p - 1);
                        line = substr(line, p);
                    } else {
                        args = args + substr(line, 1, pos);
                        line = substr(line, pos + 1);
                    }
                }
        }
    }
    args = args + word;
    for key in (dict_keys(options)) {
        word = (options[key])[2];
        if (type(word) == 'string)
            word = $string.trim(word);
        options = dict_add(options, key, word);
    }
    return [args, options];
.

method xreference
    arg str, [args];
    var out, p, tmp, defobj, match;
    
    defobj = [@args, sender()][1];
    match = [@args, [$object, 'to_dbref], [$object, 'to_dbref]][2];
    p = "<" in str;
    if (p) {
        out = [substr(str, 1, p - 1)];
        tmp = substr(str, p + 1);
        p = ">" in tmp;
        if (!p)
            throw(~parse, ("Unable to parse reference \"" + str) + "\".");
        out = [out[1], substr(tmp, 1, p - 1)];
        tmp = (> .split_reference(substr(tmp, p + 1)) <);
        out = [tmp[1], @out, tmp[3]];
    } else {
        out = (> .split_reference(str) <);
        out = [out[1], out[2], out[2], out[3]];
    }
    if (!(out[2]))
        out = replace(out, 2, defobj);
    else
        out = replace(out, 2, (> (match[1]).(match[2])(out[2]) <));
    if (!(out[3]))
        out = replace(out, 3, defobj);
    else
        out = replace(out, 3, (> (match[1]).(match[2])(out[3]) <));
    return out;
.

method split_reference
    arg str;
    var sep, type, mid;
    
    if ("." in str) {
        type = 'method;
        sep = ".";
    } else if (":" in str) {
        type = 'variable;
        sep = ":";
    } else if (";" in str) {
        type = 'anc_variable;
        sep = ";";
    } else {
        throw(~parse, ("Invalid reference \"" + str) + "\"");
    }
    if (type == 'method) {
        if ("()" in str)
            str = substr(str, 1, ("()" in str) - 1);
    }
    mid = sep in str;
    if (!mid)
        return [type, str, 0];
    else if (mid == 1)
        return [type, "", tosym(substr(str, 2))];
    else
        return [type, substr(str, 1, mid - 1), tosym(substr(str, mid + 1))];
.

method range
    arg str;
    var out;
    
    out = str.explode("-");
    if (listlen(out) == 1) {
        out = [(> ._range(str) <), 'single];
    } else if (listlen(out) == 2) {
        out = out.replace(1, (> ._range(out[1]) <));
        out = out.replace(2, (> ._range(out[2]) <));
    } else {
        throw(~range, "Invalid range reference.");
    }
    return out;
.

method _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.");
        }
    }
.