lock utilities (#54)

(an instance of Generic Utilities Package made by The_Mayor)




VERB SOURCE CODE:

init_scanner:
this.input_string = args[1];
this.input_length = length(args[1]);
this.input_index = 1;
this.index_incremented = 0;
.


scan_token:
string = this.input_string;
len = this.input_length;
i = this.input_index;
while ((i <= len) && (string[i] == " "))
    i = i + 1;
endwhile
if (i > len)
    this.index_incremented = 0;
    return "";
elseif ((ch = string[i]) in {"(", ")", "!", "?"})
    this.input_index = i + 1;
    this.index_incremented = 1;
    return ch;
elseif (ch in {"&", "|"})
    this.input_index = i = i + 1;
    this.index_incremented = 1;
    if ((i <= len) && (string[i] == ch))
        this.input_index = i + 1;
        this.index_incremented = 2;
    endif
    return ch + ch;
else
    start = i;
    while ((i <= len) && (!((ch = string[i]) in {"(", ")", "!", "?", "&", "|"})))
        i = i + 1;
    endwhile
    this.input_index = i;
    i = i - 1;
    while (string[i] == " ")
        i = i - 1;
    endwhile
    this.index_incremented = i - start;
    return this:canonicalize_spaces(string[start..i]);
endif
.


canonicalize_spaces:
name = args[1];
while (index(name, "  "))
    name = strsub(name, "  ", " ");
endwhile
return name;
.


parse_keyexp:
"parse_keyexp(STRING keyexpression, OBJ player) => returns a list containing the 
coded key, or a string containing an error message if the attempt failed.";
"";
"Grammar for key expressions:";
"";
"    E ::= A       ";
"       |  E || A  ";
"       |  E && A  ";
"    A ::= ( E )   ";
"       |  ! A     ";
"       |  object  ";
"       |  ? object  ";
this:init_scanner(args[1]);
this.player = args[2];
return this:parse_E();
.


parse_E:
exp = this:parse_A();
if (typeof(exp) != STR)
    while ((token = this:scan_token()) in {"&&", "||"})
        rhs = this:parse_A();
        if (typeof(rhs) == STR)
            return rhs;
        endif
        exp = {token, exp, rhs};
    endwhile
endif
this.input_index = this.input_index - this.index_incremented;
return exp;
.


parse_A:
token = this:scan_token();
if (token == "(")
    exp = this:parse_E();
    if ((typeof(exp) != STR) && ((tok = this:scan_token()) != ")"))
        return "Missing ')'";
    else
        return exp;
    endif
elseif (token == "!")
    exp = this:parse_A();
    if (typeof(exp) == STR)
        return exp;
    else
        return {"!", exp};
    endif
elseif (token == "?")
    next = this:scan_token();
    if (next in {"(", ")", "!", "&&", "||", "?"})
        return ("Missing object-name before '" + token) + "'";
    elseif (next == "")
        return "Missing object-name at end of key expression";
    else
        what = this:match_object(next);
        if (typeof(what) == OBJ)
            return {"?", this:match_object(next)};
        else
            return what;
        endif
    endif
elseif (token in {"&&", "||"})
    return ("Missing expression before '" + token) + "'";
elseif (token == "")
    return "Missing expression at end of key expression";
else
    return this:match_object(token);
endif
.


eval_key:
"eval_key(LIST|OBJ coded key, OBJ testobject) => returns true if testobject will 
solve the provided key.";
key = args[1];
who = args[2];
type = typeof(key);
if (!(type in {LIST, OBJ}))
    return 1;
elseif (typeof(key) == OBJ)
    return (who == key) || $object_utils:contains(who, key);
endif
op = key[1];
if (op == "!")
    return !this:eval_key(key[2], who);
elseif (op == "?")
    return key[2]:is_unlocked_for(who);
elseif (op == "&&")
    return this:eval_key(key[2], who) && this:eval_key(key[3], who);
elseif (op == "||")
    return this:eval_key(key[2], who) || this:eval_key(key[3], who);
else
    return 1 / 0;
endif
.


match_object:
"used by $lock_utils to unparse a key expression so one can use `here' and `me' as 
well as doing the regular object matching.";
token = args[1];
if (token == "me")
    return this.player;
elseif (token == "here")
    if (valid(this.player.location))
        return this.player.location;
    else
        return ("'here' has no meaning where " + this.player.name) + " is";
    endif
else
    what = this.player.location:match_object(token);
    if (what == $failed_match)
        return ("Can't find an object named '" + token) + "'";
    elseif (what == $ambiguous_match)
        return ("Multiple objects named '" + token) + "'";
    else
        return what;
    endif
endif
.


unparse_key:
":unparse_key(LIST|OBJ coded key) => returns a string describing the key in english/moo-code 
terms.";
"Example:";
"$lock_utils:unparse_key({\"||\", $hacker, $housekeeper}) => \"#18105[Hacker] || 
#36830[housekeeper]\"";
key = args[1];
type = typeof(key);
if (!(type in {LIST, OBJ}))
    return "(None.)";
elseif (type == OBJ)
    if (valid(key))
        return tostr(key, "[", key.name, "]");
    else
        return tostr(key);
    endif
else
    op = key[1];
    arg1 = this:unparse_key(key[2]);
    if (op == "?")
        return "?" + arg1;
    elseif (op == "!")
        if (typeof(key[2]) == LIST)
            return ("!(" + arg1) + ")";
        else
            return "!" + arg1;
        endif
    elseif (op in {"&&", "||"})
        other = (op == "&&") ? "||" | "&&";
        lhs = arg1;
        rhs = this:unparse_key(key[3]);
        if ((typeof(key[2]) == OBJ) || (key[2][1] != other))
            exp = lhs;
        else
            exp = ("(" + lhs) + ")";
        endif
        exp = ((exp + " ") + op) + " ";
        if ((typeof(key[3]) == OBJ) || (key[3][1] != other))
            exp = exp + rhs;
        else
            exp = ((exp + "(") + rhs) + ")";
        endif
        return exp;
    else
        return 1 / 0;
    endif
endif
.


eval_key_new:
set_task_perms($no_one);
key = args[1];
who = args[2];
type = typeof(key);
if (!(type in {LIST, OBJ}))
    return 1;
elseif (typeof(key) == OBJ)
    return (who == key) || $object_utils:contains(who, key);
endif
op = key[1];
if (op == "!")
    return !this:eval_key(key[2], who);
elseif (op == "?")
    return key[2]:is_unlocked_for(who);
elseif (op == "&&")
    return this:eval_key(key[2], who) && this:eval_key(key[3], who);
elseif (op == "||")
    return this:eval_key(key[2], who) || this:eval_key(key[3], who);
elseif (op == ".")
    if ($object_utils:has_property(who, key[2]) && who.(key[2]))
        return 1;
    else
        for thing in ($object_utils:all_contents(who))
            if ($object_utils:has_property(thing, key[2]) && thing.(key[2]))
                return 1;
            endif
        endfor
    endif
    return 0;
elseif (op == ":")
    if ($object_utils:has_verb(who, key[2]) && who:(key[2])())
        return 1;
    else
        for thing in ($object_utils:all_contents(who))
            if ($object_utils:has_verb(thing, key[2]) && thing:(key[2])())
                return 1;
            endif
        endfor
    endif
    return 0;
else
    return 1 / 0;
endif
.


parse_A_new:
token = this:scan_token();
if (token == "(")
    exp = this:parse_E();
    if ((typeof(exp) != STR) && (this:scan_token() != ")"))
        return "Missing ')'";
    else
        return exp;
    endif
elseif (token == "!")
    exp = this:parse_A();
    if (typeof(exp) == STR)
        return exp;
    else
        return {"!", exp};
    endif
elseif (token == "?")
    next = this:scan_token();
    if (next in {":", ".", "(", ")", "!", "&&", "||", "?"})
        return ("Missing object-name before '" + token) + "'";
    elseif (next == "")
        return "Missing object-name at end of key expression";
    else
        what = this:match_object(next);
        if (typeof(what) == OBJ)
            return {"?", this:match_object(next)};
        else
            return what;
        endif
    endif
elseif (token in {":", "."})
    next = this:scan_token();
    if (next in {":", ".", "(", ")", "!", "&&", "||", "?"})
        return ("Missing verb-or-property-name before '" + token) + "'";
    elseif (next == "")
        return "Missing verb-or-property-name at end of key expression";
    elseif (typeof(next) != STR)
        return "Non-string verb-or-property-name at end of key expression";
    else
        return {token, next};
    endif
elseif (token in {"&&", "||"})
    return ("Missing expression before '" + token) + "'";
elseif (token == "")
    return "Missing expression at end of key expression";
else
    return this:match_object(token);
endif
.



PROPERTY DATA:
      player
      input_index
      input_length
      input_string
      index_incremented