object $code_lib: $libraries;

var $root inited = 1;

public method $code_lib.verify_code() {
    arg code, method, warn;
    var l, line, m, warns, isadmin, msg;
    
    warns = [];
    method = "\." + tostr(method) + "\(";
    isadmin = sender().is($admin);
    for l in [1 .. code.length()] {
        line = code[l];
    
        // if its in a comment, ignore it
        if (match_begin(line, "//"))
            continue;
    
        // required warnings, sorry
        if ((m = line.match_regexp("[^._]anticipate_assignment\(")))
            warns += .point_to_line("WARNING: call to anticipate_assignment()", m[1][1] + 2, m[1][2] - 2, l, line);
        if ((m = line.match_regexp("(!)[a-z0-9_]+ in "))) {
            warns += ["WARNING: possible ambiguity, line " + l];
            warns += .point_to_line("WARNING: parenthesis suggested around followup expression to '!'", m[2][1] + 1, m[2][2], l, line);
        }
        if ((m = line.match_regexp("(if *\(|&&|\|\|) *[a-z0-9_]+ *(=)[^=]")))
            warns += .point_to_line("WARNING: parenthesis suggested around assignment expression", m[3][1] + 1, m[3][2], l, line);
    
        // optional warnings
        if (warn && (m = line.match_regexp(method)))
            warns += .point_to_line("WARNING: Possible Recursion", m[1][1] + 2, m[1][2] - 2, l, line);
    }
    return warns;
};

public method $code_lib.generate_object_listing() {
    arg objs, multi, @args;
    var line, obj, col, name, fmt, out;
    
    if (!objs) {
        out = ["** None **"];
    } else {
        col = ((| sender().linelen() |) || 79) / 10;
        fmt = "%3L%" + tostr(col * 4) + "L %" + tostr(col) + "L %" + tostr(col) + "R ";
        out = [strfmt(fmt, "#", "Name", "Perms", "Size") + "Manager"];
        col = col * 4;
        for obj in (objs) {
            line = strfmt(fmt, obj.(multi)(@args).length(), obj.namef('xref), $object_lib.see_perms(obj, ["", ""]), obj.size());
            name = obj.manager().namef('xref);
            if (name.length() > col)
                name = name.pad(col);
            out += [line + name];
        }
    }
    return out;
};

public method $code_lib.point_to_line() {
    arg err, left, right, lineno, line;
    var out;
    
    return [err + ", line " + lineno + ":", "  " + line, strfmt("%*{-}l%*{^}l", left, "", right, "")];
};

public method $code_lib._debug_listing() {
    arg list;
    var indent, i, out, t, j;
    
    indent = "";
    out = [" Tick#  Event", " -----  -----------------------------"];
    t = 0;
    for i in (list) {
        if (type(i) == 'integer) {
            if (indent)
                indent = indent.subrange(3);
            out += [strfmt("%6r  %lreturn", i - t, indent)];
        } else {
            if (!t)
                t = i[1];
            j = strfmt("%6r  %l%l(%l)", i[1] - t, indent, ._show_ref(i).replace("()", ""), (toliteral(i[5]).match_pattern("[*]"))[1]);
            out += [j];
            indent += "  ";
        }
        refresh();
    }
    return out;
    
    // $#Edited: 03 Aug 97 13:53 $miro
};

public method $code_lib.generate_debug_listing() {
    arg info, mode;
    
    return .(tosym("_" + tostr(mode) + "_listing"))(info);
};

public method $code_lib._trace_listing() {
    arg list;
    var indent, i, out, t, j;
    
    indent = "";
    out = [" Tick#  Event", " -----  -----------------------------"];
    t = 0;
    for i in (list) {
        if (type(i) == 'integer) {
            if (indent)
                indent = indent.subrange(3);
        } else {
            if (!t)
                t = i[1];
            j = strfmt("%6r  %l%l", i[1] - t, indent, ._show_ref(i));
            out += [j.chop(79)];
            indent += "  ";
        }
        refresh();
    }
    return out;
};

public method $code_lib._trace_profile() {
    arg list;
    var i, out, ref, times, sums, callers, t, tic, max, start;
    
    out = ["Object<definer>.method                               Tics  (%)  Cummul. (%)"];
    times = #[];
    sums = #[];
    callers = [];
    for i in (list) {
        refresh();
        if (type(i) == 'integer) {
            if (callers) {
                [[ref, tic], @callers] = callers;
                times = times.add(ref, ((| times[ref] |) || 0) + i[1] - t);
                sums = sums.add(ref, ((| sums[ref] |) || 0) + i[1] - tic);
            }
            t = i;
        } else {
            if (callers) {
                ref = callers[1][1];
                times = times.add(ref, ((| sums[ref] |) || 0) + i[1] - t);
            }
            ref = strfmt("%l<%l>.%l", i[2], i[3], i[4]);
            callers = [[ref, i[1]], @callers];
            t = i[1];
        }
        start ?= t;
    }
    max = 0.01 * (t - start);
    out = map i in (times.keys()) to (refresh() && [i, times[i], times[i] / max, sums[i], sums[i] / max]);
    out = out.sort(out.slice(2));
    out = map i in (out) to (strfmt(i, "%50l%7r%7r%7r%7r"));
    return out;
};

public method $code_lib._profile_listing() {
    arg list;
    var i, out, ref, times, sums, callers, t, tic, max, start, head;
    
    head = ["Object<definer>.method                               Tics    (%)  Total    (%)", "----------------------                               ----    ---  -----    ---"];
    times = #[];
    sums = #[];
    callers = [];
    for i in (list) {
        refresh();
        if (type(i) == 'integer) {
            if (callers) {
                [[ref, tic], @callers] = callers;
                times = times.add(ref, ((| times[ref] |) || 0) + i - t);
                if (!(ref in callers))
                    sums = sums.add(ref, ((| sums[ref] |) || 0) + i - tic);
            }
            t = i;
        } else {
            if (callers) {
                ref = callers[1][1];
                times = times.add(ref, ((| times[ref] |) || 0) + i[1] - t);
            }
            ref = ._show_ref(i);
            callers = [[ref, i[1]], @callers];
            t = i[1];
        }
        start ?= t;
    }
    max = 0.01 * (t - start);
    out = map i in (times.keys()) to (refresh() && [i.chop(50), times[i], times[i] / max, sums[i], sums[i] / max]);
    out = out.sort(map i in (out) to (refresh() && -i[2]));
    out = map i in (out) to (strfmt("%50l%7r%6.1r%%%7r%6.1r%%", @i));
    out = head + out;
    return out;
    
    // $#Edited: 04 Nov 97 06:55 $miro
};

public method $code_lib._show_ref() {
    arg i;
    
    return i[2] != i[3] ? strfmt("%l<%l>.%l()", i[2], i[3], i[4]) : strfmt("%l.%l()", i[2], i[4]);
};