new object $has_commands: $root;

var $has_commands commands = 0;
var $has_commands shortcuts = 0;
var $root inited = 1;

public method .commands() {
  return commands || [];
};

public method .match_command() {
  arg line;
  var cmd, char, word;

  // First try to match the shortcuts (quick)
  char = line[1] in .shortcuts().slice(1);
  if (char) {
    cmd = .shortcuts()[char];
    return [cmd[2], cmd[1], line.subrange(2)];
  }

  // Now try to match an actual command
  word = substr(line, 1, stridx(line, " ", 1)-1);
  for cmd in (.commands()) {
    if (word == cmd[1]) {
      return [cmd[2], word, @line.explode().subrange(2)];
    }
  }

  // No match
  return ['invalid_cmd];
};

public method .shortcuts() {
  return shortcuts || [];
};