Command-handling object This object defines command-handling behavior. Public methods: match_command(str) Match commands against str commands() Get list of commands all_commands() Get commands on ancestors too shortcuts() Get list of shortcuts all_shortcuts() Get shortcuts on ancestors too sub_shortcut_fields(subs, fields) Substitute for shortcut fields Owner methods: add_command(template, method) Add a command del_command(method) Remove a command add_shortcut(pattern, method, subs) Add a shortcut del_shortcut(method) Remove a shortcut parent root object has_commands var root name 'has_commands var has_commands commands 0 var has_commands shortcuts 0 method init_has_commands if (caller() != $root) throw(~perm, "Caller is not $root."); commands = []; shortcuts = []; . method uninit_has_commands if (caller() != $root) throw(~perm, "Caller is not $root."); commands = 0; shortcuts = 0; . eval .initialize(); . method add_command arg template, method; if (!.is_owned_by(sender())) throw(~perm, "Sender is not an owner."); if (type(template) != 'string || type(method) != 'symbol) throw(~type, "Template and method are not a string and symbol."); commands = [@commands, [template, method]]; . method del_command arg method; var command; if (!.is_owned_by(sender())) throw(~perm, "Sender is not an owner."); for command in (commands) { if (command[2] == method) { commands = setremove(commands, command); return; } } throw(~commandnf, "No command with method " + tostr(method)); . method add_shortcut arg pattern, method, subs; if (!.is_owned_by(sender())) throw(~perm, "Sender is not an owner."); if (type(pattern) != 'string || type(method) != 'symbol || type(subs) != 'list) throw(~type, "Pattern, method, and subs are not a string, symbol, and list."); shortcuts = [@shortcuts, [pattern, method, subs]]; . method del_shortcut arg method; var shortcut; if (!.is_owned_by(sender())) throw(~perm, "Sender is not an owner."); for shortcut in (shortcuts) { if (shortcut[2] == method) { shortcuts = setremove(shortcuts, shortcut); return; } } throw(~shortcutnf, "No shortcut with method " + tostr(method)); . method match_command arg str; var shortcut, cmd, fields; // Try shortcuts. for shortcut in (.all_shortcuts()) { fields = match_pattern(shortcut[1], str); if (fields) return [shortcut[2], .sub_shortcut_fields(shortcut[3], fields)]; } // Try commands. for cmd in (.all_commands()) { fields = match_template(cmd[1], str); if (fields) return [cmd[2], fields]; } // Give up. return 0; . method commands return commands; . method all_commands var p, cmdlist, pc; // Collect complete command list from ancestors. Ancestors may not be // command-handling objects, in which case (| p.commands() |) is // ~methodnf. cmdlist = []; for p in (ancestors()) { pc = (| p.commands() |); if (pc) cmdlist = cmdlist + pc; if (p == definer()) break; } return cmdlist; . method shortcuts return shortcuts; . method all_shortcuts var ancestor, list, ancestor_shortcuts; // Collect complete command list from ancestors. Ancestors may not be // command-handling objects, in which case (| p.shortcuts() |) is // ~methodnf. list = []; for ancestor in (ancestors()) { ancestor_shortcuts = (| ancestor.shortcuts() |); if (ancestor_shortcuts) list = list + ancestor_shortcuts; if (ancestor == definer()) break; } return list; . method sub_shortcut_fields arg subs, fields; var subbed_list, elem; subbed_list = []; for elem in (subs) { if (type(elem) == 'string) subbed_list = [@subbed_list, elem]; else if (type(elem) == 'integer) subbed_list = [@subbed_list, (> fields[elem] <)]; else throw(~type, "Substitution element is of wrong type."); } return subbed_list; .