Generic Programmer

    Private methods:

        programming_line(str)           Handle line in a program
        program_done()                  Handle end of program

    Comamnds:

        eval_cmd(s)                     Evaluate a string s
        program_cmd("@program", name, "on", obj)
                                        Start programming a method

parent builder
object programmer

var root name 'programmer
var programmer inited 0
var programmer programming 0
var programmer program_buf 0

method init_programmer
    if (caller() != $root)
        throw(~perm, "Caller is not $root.");
    programming = 0;
    program_buf = 0;
.

method uninit_programmer
    if (caller() != $root)
        throw(~perm, "Caller is not $root.");
    programming = 0;
    program_buf = 0;
.

eval
    .initialize();
    .add_shortcut(";*", 'eval_cmd, ["eval", 1]);
    .add_command("eval *", 'eval_cmd);
    .add_command("@program * on *", 'program_cmd);
    .add_command("@show *", 'show_cmd);
    .add_command("@params *", 'params_cmd);
    .add_command("@methods *", 'methods_cmd);
    .add_command("@verbs *", 'verbs_cmd);
    .add_command("@list * on *", 'list_cmd);
.

method parse_command
    arg str;
    var err;

    if (!.is_owned_by(sender()))
        throw(~perm, "Sender not an owner.");

    // Catch errors and display a stack trace.
    catch any {
        if (programming)
            .programming_line(str);
        else
            return pass(str);
    } with handler {
        for err in (traceback())
            .tell(err);
    }
.

method programming_line
    arg str;

    if (sender() != this() || caller() != definer())
        throw(~perm, "Sender not this.");
    if (str == ".")
        .program_done();
    else if (programming != 'ignore)
        program_buf = program_buf + [str];
.

method program_done
    var errors;

    if (sender() != this() || caller() != definer())
        throw(~perm, "Sender not this.");
    if (programming == 'ignore) {
        .tell("Finished ignoring input.");
    } else {
        catch ~perm {
            errors = programming[1].compile(program_buf, programming[2]);
            if (errors)
                .tell(errors);
            else
                .tell("Method compiled.");
        } with handler {
            .tell("You cannot program that object.");
        }
    }
    programming = 0;
    program_buf = 0;
.

method eval_cmd
    arg dummy1, s;
    var result;

    if (sender() != this())
        throw(~perm, "Sender not this.");

    // Evaluate the line.
    if (s && s[1] == ";")
        result = .eval([substr(s, 2)]);
    else
        result = .eval(["return " + s + ";"]);

    // Display the errors, or the result.
    if (result[1] == 'errors)
        .tell(result[2]);
    else if (!s || s[1] != ";")
        .tell("--> " + $data.unparse(result[2]));
.

method program_cmd
    arg dummy1, name, dummy2, obj;

    if (sender() != this())
        throw(~perm, "Sender not this.");

    // Find the object to program.
    catch ~objnf {
        obj = .match_environment(obj);
    } with handler {
        .tell("I don't see \"" + error_arg() + "\" here.");
        return;
    }

    if (!obj.is_owned_by(this())) {
        .tell("You can't program that object; ignoring text.");
        programming = 'ignore;
        return;
    }

    programming = [obj, tosym(name)];
    program_buf = [];
    .tell("Enter text for method " + name + ".");
.

method show_cmd
    arg dummy1, name;
    var obj;

    if (sender() != this())
        throw(~perm, "Sender not this.");
    catch ~objnf, ~perm {
        obj = .match_environment(name);
        .tell(obj.show());
    } with handler {
        switch (error()) {
            case ~objnf:
                .tell("I don't see \"" + error_arg() + "\" here.");
            case ~perm:
                .tell("Permission denied.");
        }
    }
.

method params_cmd
    arg dummy1, name;
    var obj, params, i;

    if (sender() != this())
        throw(~perm, "Sender not this.");
    catch ~objnf, ~perm {
        obj = .match_environment(name);
        params = obj.parameters();
    } with handler {
        switch (error()) {
            case ~objnf:
                .tell("I don't see \"" + error_arg() + "\" here.");
            case ~perm:
                .tell("Permission denied.");
        }
        return;
    }
    .tell("Parameters:");
    for i in (params)
        .tell("  " + tostr(i));
.

method methods_cmd
    arg dummy1, name;
    var obj, methods, i;

    if (sender() != this())
        throw(~perm, "Sender not this.");
    catch ~objnf, ~perm {
        obj = .match_environment(name);
        methods = obj.methods();
    } with handler {
        switch (error()) {
            case ~objnf:
                .tell("I don't see \"" + error_arg() + "\" here.");
            case ~perm:
                .tell("Permission denied.");
        }
        return;
    }
    .tell("Methods:");
    for i in (methods)
        .tell("  " + tostr(i));
.

method verbs_cmd
    arg dummy1, name;
    var obj, verbs, i, info, str;

    if (sender() != this())
        throw(~perm, "Sender not this.");

    // Find the object to show.
    catch ~objnf, ~perm {
        obj = .match_environment(name);
        verbs = obj.verb_templates();
    } with handler {
        switch (error()) {
            case ~objnf:
                .tell("I don't see \"" + error_arg() + "\" here.");
            case ~perm:
                .tell("Permission denied.");
        }
        return;
    }
    .tell("Verbs:");
    for i in (verbs) {
        str = "  " + pad(i, 40) + "  ";
        info = obj.local_verb_info(i);
        if (info[2] == 'remote)
            str = str + pad(tostr(info[1]), 20) + "  (remote)";
        else
            str = str + tostr(info[1]);
        .tell(str);
    }
.

method list_cmd
    arg dummy1, method, dummy2, name;
    var obj, anc, header, code;

    if (sender() != this())
        throw(~perm, "Sender not this.");

    // Find the object to show.
    method = tosym(method);
    catch ~objnf, ~perm {
        obj = .match_environment(name);
        anc = obj.find_method(method);
        code = anc.list_method(method);
    } with handler {
        switch (error()) {
            case ~objnf:
                .tell("I don't see \"" + error_arg() + "\" here.");
            case ~perm:
                .tell("Permission denied.");
        }
        return;
    }

    // List the method.
    header = toliteral(obj) + "." + tostr(method) + "() (defined on ";
    header = header + toliteral(anc) + "):";
    .tell(header);
    .tell(code);
.