Reworked the rpsystem with a working search replacement mechanism (allowing to search objects by sdesc)
This commit is contained in:
parent
793a307867
commit
c34561dba2
1 changed files with 99 additions and 81 deletions
|
|
@ -262,7 +262,7 @@ def parse_language(speaker, emote):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def parse_sdescs_and_recogs(sender, candidates, emote, map_obj=False):
|
def parse_sdescs_and_recogs(sender, candidates, string, search_mode=False):
|
||||||
"""
|
"""
|
||||||
Read a textraw emote and parse it into an intermediary
|
Read a textraw emote and parse it into an intermediary
|
||||||
format for distributing to all observers.
|
format for distributing to all observers.
|
||||||
|
|
@ -272,16 +272,20 @@ def parse_sdescs_and_recogs(sender, candidates, emote, map_obj=False):
|
||||||
recog data will be considered in the parsing.
|
recog data will be considered in the parsing.
|
||||||
candidates (iterable): A list of objects valid for referencing
|
candidates (iterable): A list of objects valid for referencing
|
||||||
in the emote.
|
in the emote.
|
||||||
emote (str): The incoming emote from the caller.
|
string (str): The string (like an emote) we want to analyze for keywords.
|
||||||
map_obj (bool, optional): Return a mapping including the real db
|
search_mode (bool, optional): If `True`, the "emote" is a query string
|
||||||
object rather than the sdesc.
|
we want to analyze. If so, the return value is changed.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(emote, mapping) (tuple): A tuple where the emote is the emote
|
(emote, mapping) (tuple): If `search_mode` is `False`
|
||||||
string, with all references replaced with
|
(default), a tuple where the emote is the emote string, with
|
||||||
internal-representation {#dbref} markers and mapping is a
|
all references replaced with internal-representation {#dbref}
|
||||||
dictionary `{"#dbref":"sdesc", ...}` if `map_obj` is
|
markers and mapping is a dictionary `{"#dbref":"sdesc", ...}`
|
||||||
`False` (default) and `{"#dbref":obj,...}` otherwise.
|
if `map_obj` is `False` (default) and `{"#dbref":obj,...}`
|
||||||
|
otherwise.
|
||||||
|
result (list): If `search_mode` is `True` we are
|
||||||
|
performing a search query on `string`, looking for a specific
|
||||||
|
object. A list with zero, one or more matches.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
EmoteException: For various ref-matching errors.
|
EmoteException: For various ref-matching errors.
|
||||||
|
|
@ -296,31 +300,26 @@ def parse_sdescs_and_recogs(sender, candidates, emote, map_obj=False):
|
||||||
- says, "..." are
|
- says, "..." are
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
# Load all candidate regex tuples [(regex, obj, sdesc/recog),...]
|
||||||
# escape mapping syntax on the form {#id} if it exists already in emote,
|
candidate_regexes = \
|
||||||
# if so it is replaced with just "id".
|
[(_RE_SELF_REF, sender, sender.sdesc.get())] + \
|
||||||
emote = _RE_REF.sub("\1", emote)
|
[sender.recog.get_regex_tuple(obj)
|
||||||
|
|
||||||
# build all candidate regex tuples
|
|
||||||
candidate_regexes = [sender.recog.get_regex_tuple(obj)
|
|
||||||
for obj in candidates if hasattr(obj, "recog")] + \
|
for obj in candidates if hasattr(obj, "recog")] + \
|
||||||
[obj.sdesc.get_regex_tuple()
|
[obj.sdesc.get_regex_tuple()
|
||||||
for obj in candidates if hasattr(obj, "sdesc")]
|
for obj in candidates if hasattr(obj, "sdesc")]
|
||||||
# filter out non-found data
|
# filter out non-found data
|
||||||
candidate_regexes = [tup for tup in candidate_regexes if tup]
|
candidate_regexes = [tup for tup in candidate_regexes if tup]
|
||||||
|
|
||||||
# handle self-reference first
|
# escape mapping syntax on the form {#id} if it exists already in emote,
|
||||||
objlist = []
|
# if so it is replaced with just "id".
|
||||||
mapping = {}
|
string = _RE_REF.sub("\1", string)
|
||||||
if _RE_SELF_REF.search(emote):
|
|
||||||
key = "#%i" % sender.id
|
|
||||||
emote = _RE_SELF_REF.sub("{%s}" % key, emote)
|
|
||||||
mapping[key] = sender if map_obj else (sender.db.sdesc or sender.key)
|
|
||||||
objlist.append(sender)
|
|
||||||
|
|
||||||
# we now loop over all references and analyze them
|
# we now loop over all references and analyze them
|
||||||
|
mapping = {}
|
||||||
errors = []
|
errors = []
|
||||||
for marker_match in reversed(list(_RE_OBJ_REF_START.finditer(emote))):
|
obj = None
|
||||||
|
nmatches = 0
|
||||||
|
for marker_match in reversed(list(_RE_OBJ_REF_START.finditer(string))):
|
||||||
# we scan backwards so we can replace in-situ without messing
|
# we scan backwards so we can replace in-situ without messing
|
||||||
# up later occurrences. Given a marker match, query from
|
# up later occurrences. Given a marker match, query from
|
||||||
# start index forward for all candidates.
|
# start index forward for all candidates.
|
||||||
|
|
@ -332,57 +331,68 @@ def parse_sdescs_and_recogs(sender, candidates, emote, map_obj=False):
|
||||||
# +1 for _NUM_SEP, if defined
|
# +1 for _NUM_SEP, if defined
|
||||||
istart = istart0 #+ (len(num_identifier) + 1 if num_identifier else 0)
|
istart = istart0 #+ (len(num_identifier) + 1 if num_identifier else 0)
|
||||||
|
|
||||||
print "marker match:", marker_match.group(), istart0, istart, emote[istart:]
|
print "marker match:", marker_match.group(), istart0, istart, string[istart:]
|
||||||
#print "candidates:", [tup[2] for tup in candidate_regexes]
|
#print "candidates:", [tup[2] for tup in candidate_regexes]
|
||||||
# loop over all candidate regexes and match against the string following the match
|
# loop over all candidate regexes and match against the string following the match
|
||||||
matches = ((reg.match(emote[istart:]), obj, text) for reg, obj, text in candidate_regexes)
|
matches = ((reg.match(string[istart:]), obj, text) for reg, obj, text in candidate_regexes)
|
||||||
|
|
||||||
# score matches by how long part of the string was matched
|
# score matches by how long part of the string was matched
|
||||||
matches = [(match.end() if match else -1, obj, text) for match, obj, text in matches]
|
matches = [(match.end() if match else -1, obj, text) for match, obj, text in matches]
|
||||||
print "matches:", istart, matches
|
print "matches:", istart, matches
|
||||||
maxscore = max(score for score, obj, text in matches)
|
maxscore = max(score for score, obj, text in matches)
|
||||||
|
|
||||||
# analyze result
|
|
||||||
if maxscore == -1:
|
|
||||||
# No matches
|
|
||||||
errors.append(_EMOTE_NOMATCH_ERROR.format(ref=marker_match.group()))
|
|
||||||
continue
|
|
||||||
|
|
||||||
# we have a valid maxscore, extract all matches with this value
|
# we have a valid maxscore, extract all matches with this value
|
||||||
bestmatches = [(obj, text) for score, obj, text in matches if maxscore == score]
|
bestmatches = [(obj, text) for score, obj, text in matches if maxscore == score != -1]
|
||||||
nmatches = len(bestmatches)
|
nmatches = len(bestmatches)
|
||||||
print "nmatches:", nmatches, bestmatches
|
|
||||||
|
|
||||||
if nmatches == 1:
|
if not nmatches:
|
||||||
|
# no matches
|
||||||
|
obj = None
|
||||||
|
nmatches = 0
|
||||||
|
elif nmatches == 1:
|
||||||
# an exact match.
|
# an exact match.
|
||||||
obj = bestmatches[0][0]
|
obj = bestmatches[0][0]
|
||||||
if nmatches:
|
nmatches = 1
|
||||||
# several matches have the same score.
|
elif all(bestmatches[0][0].id == obj.id for obj, text in bestmatches):
|
||||||
inum = max(0, int(num_identifier) - 1) if num_identifier else None
|
# multi-match but all reference the same obj (could happen
|
||||||
if all(bestmatches[0][0].id == obj.id for obj, text in bestmatches):
|
# with clashing recogs/sdescs)
|
||||||
# multi-matches all references the same obj (could happen with clashing recogs/sdescs)
|
obj = bestmatches[0][0]
|
||||||
obj = bestmatches[0][0]
|
nmatches = 1
|
||||||
|
else:
|
||||||
|
# multi-match.
|
||||||
|
# was a numerical identifier given to help us separate the multi-match?
|
||||||
|
inum = min(max(0, int(num_identifier) - 1), nmatches-1) if num_identifier else None
|
||||||
|
if inum is not None:
|
||||||
|
# A valid inum is given. Use this to separate data
|
||||||
|
obj = bestmatches[inum][0]
|
||||||
|
nmatches = 1
|
||||||
else:
|
else:
|
||||||
# was a numerical identifier given to help us separate the multi-match?
|
# no identifier given - a real multimatch
|
||||||
if inum is None or inum > nmatches:
|
obj = bestmatches
|
||||||
# no match or invalid match id given
|
|
||||||
refname = marker_match.group()
|
|
||||||
reflist = ["%s%s%s (%s)" % (inum+1, _NUM_SEP, _RE_PREFIX.sub("", refname), text)
|
|
||||||
for inum, (obj, text) in enumerate(bestmatches) if score == maxscore]
|
|
||||||
errors.append(_EMOTE_MULTIMATCH_ERROR.format(
|
|
||||||
ref=marker_match.group(), reflist="\n ".join(reflist)))
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
# A valid inum is given. Use this to separate data
|
|
||||||
obj = bestmatches[inum][0]
|
|
||||||
|
|
||||||
# if we get to this point we have identifed a local object tied to this sdesc or recog marker.
|
if search_mode:
|
||||||
# we replace it with the interal representation on the form {#dbref}.
|
# single-object search
|
||||||
print "replace emote:", istart, maxscore, emote, emote[istart + maxscore:]
|
break
|
||||||
key = "#%i" % obj.id
|
elif nmatches == 0:
|
||||||
emote = emote[:istart0] + "{%s}" % key + emote[istart + maxscore:]
|
errors.append(_EMOTE_NOMATCH_ERROR.format(ref=marker_match.group()))
|
||||||
mapping[key] = obj if map_obj else (obj.db.sdesc or obj.key)
|
elif nmatches == 1:
|
||||||
objlist.append(obj)
|
key = "#%i" % obj.id
|
||||||
|
string = string[:istart0] + "{%s}" % key + string[istart + maxscore:]
|
||||||
|
mapping[key] = obj if search_mode else (obj.db.sdesc or obj.key)
|
||||||
|
else:
|
||||||
|
refname = marker_match.group()
|
||||||
|
reflist = ["%s%s%s (%s)" % (inum+1, _NUM_SEP, _RE_PREFIX.sub("", refname), text)
|
||||||
|
for inum, (obj, text) in enumerate(bestmatches) if score == maxscore]
|
||||||
|
errors.append(_EMOTE_MULTIMATCH_ERROR.format(
|
||||||
|
ref=marker_match.group(), reflist="\n ".join(reflist)))
|
||||||
|
if search_mode:
|
||||||
|
# return list of object(s) matching
|
||||||
|
if nmatches == 0:
|
||||||
|
return []
|
||||||
|
elif nmatches == 1:
|
||||||
|
return [obj]
|
||||||
|
else:
|
||||||
|
return [tup[0] for tup in obj]
|
||||||
|
|
||||||
if errors:
|
if errors:
|
||||||
# make sure to not let errors through.
|
# make sure to not let errors through.
|
||||||
|
|
@ -390,7 +400,7 @@ def parse_sdescs_and_recogs(sender, candidates, emote, map_obj=False):
|
||||||
|
|
||||||
# at this point all references have been replaced with {#xxx} markers and the mapping contains
|
# at this point all references have been replaced with {#xxx} markers and the mapping contains
|
||||||
# a 1:1 mapping between those inline markers and objects.
|
# a 1:1 mapping between those inline markers and objects.
|
||||||
return emote, mapping
|
return string, mapping
|
||||||
|
|
||||||
|
|
||||||
def receive_emote(sender, receiver, emote, sdesc_mapping, language_mapping):
|
def receive_emote(sender, receiver, emote, sdesc_mapping, language_mapping):
|
||||||
|
|
@ -624,17 +634,19 @@ class CmdRecog(Command): # assign personal alias to object in room
|
||||||
alias = self.alias
|
alias = self.alias
|
||||||
prefixed_sdesc = sdesc if sdesc.startswith(_PREFIX) else _PREFIX + sdesc
|
prefixed_sdesc = sdesc if sdesc.startswith(_PREFIX) else _PREFIX + sdesc
|
||||||
candidates = caller.location.contents
|
candidates = caller.location.contents
|
||||||
try:
|
matches = parse_sdescs_and_recogs(caller, candidates, prefixed_sdesc, search_mode=True)
|
||||||
_, mapping = parse_sdescs_and_recogs(caller, candidates, prefixed_sdesc, map_obj=True)
|
nmatches = len(matches)
|
||||||
|
# handle 0, 1 and >1 matches
|
||||||
except EmoteError as err:
|
if nmatches == 0:
|
||||||
# errors are handled already here
|
caller.msg(_EMOTE_NOMATCH_ERROR.format(ref=sdesc))
|
||||||
caller.msg(err.message)
|
elif nmatches > 1:
|
||||||
return
|
reflist = ["%s%s%s (%s)" % (inum+1, _NUM_SEP, _RE_PREFIX.sub("", sdesc), obj.sdesc.get())
|
||||||
obj = mapping.values()[0]
|
for inum, obj in enumerate(matches)]
|
||||||
# we have all we need, add the recog alias
|
caller.msg(_EMOTE_MULTIMATCH_ERROR.format(ref=sdesc,reflist="\n ".join(reflist)))
|
||||||
alias = caller.set_recog(obj, alias)
|
else:
|
||||||
caller.msg("You will now remember {w%s{n as {w%s{n." % (obj.db.sdesc, alias))
|
# we have all we need, add the recog alias
|
||||||
|
alias = caller.recog.add(obj, alias)
|
||||||
|
caller.msg("You will now remember {w%s{n as {w%s{n." % (obj.db.sdesc, alias))
|
||||||
|
|
||||||
|
|
||||||
class CmdLanguage(Command): # list available languages
|
class CmdLanguage(Command): # list available languages
|
||||||
|
|
@ -795,7 +807,6 @@ class RecogHandler(object):
|
||||||
SdescError: When recog could not be set or sdesc longer
|
SdescError: When recog could not be set or sdesc longer
|
||||||
than `max_length`.
|
than `max_length`.
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# strip emote components from recog
|
# strip emote components from recog
|
||||||
recog = _RE_REF.sub("\1",
|
recog = _RE_REF.sub("\1",
|
||||||
|
|
@ -912,13 +923,20 @@ class RPObject(DefaultObject):
|
||||||
if (isinstance(searchdata, basestring) and not
|
if (isinstance(searchdata, basestring) and not
|
||||||
(kwargs.get("global_search") or
|
(kwargs.get("global_search") or
|
||||||
kwargs.get("candidates"))):
|
kwargs.get("candidates"))):
|
||||||
try:
|
matches = parse_sdescs_and_recogs(self, self.location.contents,
|
||||||
_, mapping = parse_sdescs_and_recogs(self,
|
_PREFIX + searchdata, search_mode=True)
|
||||||
self.location.contents, _PREFIX + searchdata, map_obj=True)
|
nmatches = len(matches)
|
||||||
return mapping.values()[0]
|
print "matches:", matches
|
||||||
except EmoteError:
|
if nmatches == 1:
|
||||||
pass
|
return matches[0]
|
||||||
# fall back to original search method
|
elif nmatches > 1:
|
||||||
|
# multimatch
|
||||||
|
print matches
|
||||||
|
reflist = ["%s%s%s (%s)" % (inum+1, _NUM_SEP, searchdata, obj.sdesc.get())
|
||||||
|
for inum, obj in enumerate(matches)]
|
||||||
|
self.msg(_EMOTE_MULTIMATCH_ERROR.format(ref=searchdata,reflist="\n ".join(reflist)))
|
||||||
|
return
|
||||||
|
# fall back to normal search
|
||||||
return super(RPObject, self).search(searchdata, **kwargs)
|
return super(RPObject, self).search(searchdata, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue