def test_prefer_unhelpful(self): wrong = {"correct": False} also_wrong = {"correct": False} correct = lambda obj: obj["correct"] token = parser.OneOf([("foo", wrong), ("foo", also_wrong)], prefer=correct) self.assertRaises(parser.AmbiguityError, token.parseString, "foo")
def test_prefer_filter(self): preferred = {"correct": True} also_preferred = {"correct": True} wrong = {"correct": False} correct = lambda obj: obj["correct"] token = parser.OneOf( [("foo", preferred), ("foo", also_preferred), ("foo", wrong)], prefer=correct) try: token.parseString("foo") self.fail("Expected AmbiguityError") except parser.AmbiguityError as e: self.assertIn(("foo", preferred), e.matches) self.assertIn(("foo", also_preferred), e.matches) self.assertNotIn(("foo", wrong), e.matches)
def test_easy(self): result = object() token = parser.OneOf([("foo", result), ("bar", object())]) self.assert_parse(token, "foo", result)
def test_pattern(self): result = object() token = parser.OneOf( [("1", object()), ("12", result), ("12x", object())], pattern=pyp.Word(pyp.nums)) self.assertEqual(token.parseString("12x", parseAll=False)[0], result)
def test_prefer_disambiguate(self): result = {"correct": True} wrong = {"correct": False} correct = lambda obj: obj["correct"] token = parser.OneOf([("foo", result), ("foo", wrong)], prefer=correct) self.assert_parse(token, "foo", result)
def test_partial_but_exact(self): token = parser.OneOf([("foo", object()), ("bar", object())], exact=True) self.assertRaises(parser.NotFoundError, token.parseString, "f", parseAll=True)
def test_notfound(self): self.assertRaises(parser.NotFoundError, parser.OneOf([("foo", object())]).parseString, "bar", parseAll=True)
def test_prefer_full_case_insensitive(self): result = object() token = parser.OneOf([("foo", result), ("foobar", object())], exact=False) self.assert_parse(token, "Foo", result)
def test_full_ambiguous(self): token = parser.OneOf([("foo", object()), ("foo", object())], exact=False) self.assertRaises(parser.AmbiguityError, token.parseString, "foo", parseAll=True)
def test_partial(self): result = object() token = parser.OneOf([("foo", result), ("bar", object())], exact=False) self.assert_parse(token, "f", result)
def handle(self, player, line): """ Parse the input line for a command and arguments, reporting any errors or unresolvable ambiguity. """ line = line.strip() if not line: return split_line = line.split(None, 1) if len(split_line) == 1: split_line.append("") first, rest_of_line = split_line name = "" command = None # Check for nospace commands nospace_matches = [] for command in all_commands(): for name in command().nospace_names: # We can't use find_by_name because we don't know where the # nospace command ends. if line.startswith(name): # No partial matching, for the same reason. nospace_matches.append((name, command)) if len(nospace_matches) == 1: name, command = nospace_matches[0] if len(line) > len(name): arguments = line[len(name):] else: arguments = "" try: try: if len(nospace_matches) > 1: raise parser.AmbiguityError(line, 0, parser.Command.errmsg, parser.Command, nospace_matches) # Check for normal command matches pattern = parser.CommandName(fullOnly=True)("command") parse_result = pattern.parseString(first, parseAll=True) matched = parse_result["command"] arguments = rest_of_line if nospace_matches: # We found a regular command, but already had a nospace # command. raise parser.AmbiguityError(line, 0, parser.Command.errmsg, parser.Command, nospace_matches + [matched]) else: name, command = parse_result["command"] except parser.NotFoundError as e: # No commands match, what about exits? exits = [(exit.name, exit) for exit in db.find_all(lambda x: x.type == 'exit' and x.location == player.location) ] try: pattern = parser.OneOf(exits)("exit").setName("exit") parse_result = pattern.parseString(first, parseAll=True) # OneOf(exits) parsed, so exactly one exit matches. if not nospace_matches: command = commands.world.Go arguments = first except parser.AmbiguityError as f: # Multiple exits match and no full commands do. if not nospace_matches: player.send(f.verbose()) return # At this point there can only be one nospace command. # Let it fall all the way through to execution. except parser.NotFoundError as _: raise e except parser.NotFoundError as e: if not nospace_matches: message = e.verbose() # Check whether a require_full command would have matched rf_commands = [c for c in all_commands() if c.require_full] # (ignoring perfect matches because we would have already seen # them) rf_matches = utils.find_by_name(e.pstr, rf_commands, attributes=["names"])[1] if len(rf_matches) == 1: rf_name, rf_command = rf_matches[0] message += (" (If you mean \"{},\" you'll need to use the " "whole command name.)".format(rf_name)) elif rf_matches: rf_names = [c[0] for c in rf_matches] message += (" (If you meant one of these, you'll need to " "use the whole command name: {}.)".format( ", ".join(rf_names))) player.send(message) return except parser.AmbiguityError as e: # it's not clear from the name which command the user intended, # so see if any of their argument specs match what we got parsable_matches = [] for possible_name, possible_command in e.matches + nospace_matches: try: if nospace_matches and ((possible_name, possible_command) == nospace_matches[0]): test_arguments = line.split(possible_name, 1)[1] else: test_arguments = rest_of_line pattern = possible_command.args(player) args = pattern.parseString(test_arguments, parseAll=True) parsable_matches.append((possible_name, possible_command)) except pyparsing.ParseException: # user probably didn't intend this command; skip it. pass except utils.UserError: parsable_matches.append((possible_name, possible_command)) if len(parsable_matches) == 1: name, command = parsable_matches[0] if len(line) > len(name): if parsable_matches[0] in nospace_matches: arguments = line[len(name):] else: arguments = rest_of_line else: arguments = "" else: if parsable_matches: # we can at least narrow the field a little e.matches = parsable_matches player.send(e.verbose()) return # okay! we have a command! let's parse it. try: args = command.args(player).parseString(arguments, parseAll=True) command().execute(player, args) except utils.UserError as e: if hasattr(e, "verbose"): player.send(e.verbose()) else: player.send(str(e)) except pyparsing.ParseException as e: usages = command().usages if len(usages) > 1: from commands.help import Usage player.send("Usage:") Usage().execute(player, {"command": (name, command)}, tabs=True) else: player.send("Usage: " + usages[0]) player.send("(Try \"help {}\" for more help.)".format(name))
def args(cls, player): return pyp.Optional( parser.OneOf(channels.all())("channel").setName("channel") + pyp.Optional(parser.Text("text")))