From 8fd19928b2553d65a2c391841bd1a5b50fb0fa88 Mon Sep 17 00:00:00 2001 From: InspectorCaracal Date: Wed, 6 Jul 2022 15:03:40 -0600 Subject: [PATCH 1/6] add special handling for self-references --- evennia/contrib/rpg/rpsystem/rpsystem.py | 27 +++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/evennia/contrib/rpg/rpsystem/rpsystem.py b/evennia/contrib/rpg/rpsystem/rpsystem.py index f50ad88326..89e284dcb9 100644 --- a/evennia/contrib/rpg/rpsystem/rpsystem.py +++ b/evennia/contrib/rpg/rpsystem/rpsystem.py @@ -202,7 +202,7 @@ _RE_RIGHT_BRACKETS = re.compile(r"\}+", _RE_FLAGS) _RE_REF = re.compile(r"\{+\#([0-9]+[\^\~tv]{0,1})\}+") # This regex is used to quickly reference one self in an emote. -_RE_SELF_REF = re.compile(r"/me|@", _RE_FLAGS) +_RE_SELF_REF = re.compile(r"/me\b|@", _RE_FLAGS) # regex for non-alphanumberic end of a string _RE_CHAREND = re.compile(r"\W+$", _RE_FLAGS) @@ -365,6 +365,31 @@ def parse_sdescs_and_recogs(sender, candidates, string, search_mode=False, case_ errors = [] obj = None nmatches = 0 + # first, find and replace any self-refs + for self_match in list(_RE_SELF_REF.finditer(string)): + matched = self_match.group() + case = "~" # retain original case of sdesc + if case_sensitive: + # case sensitive mode + # internal flags for the case used for the original /query + # - t for titled input (like /Name) + # - ^ for all upercase input (like /NAME) + # - v for lower-case input (like /name) + # - ~ for mixed case input (like /nAmE) + matchtext = matched.lstrip(_PREFIX) + if matchtext.istitle(): + case = "t" + elif matchtext.isupper(): + case = "^" + elif matchtext.islower(): + case = "v" + + key = f"#{sender.id}{case}" + # replaced with ref + string = string.replace(matched,f"{{{key}}}") + mapping[key] = sender + + for marker_match in reversed(list(_RE_OBJ_REF_START.finditer(string))): # we scan backwards so we can replace in-situ without messing # up later occurrences. Given a marker match, query from From 169ec4fa5949e8c3ec8c39aa98559e75c3dba4e6 Mon Sep 17 00:00:00 2001 From: InspectorCaracal Date: Wed, 6 Jul 2022 15:37:52 -0600 Subject: [PATCH 2/6] move the case-ref analysis to a helper --- evennia/contrib/rpg/rpsystem/rpsystem.py | 56 ++++++++++-------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/evennia/contrib/rpg/rpsystem/rpsystem.py b/evennia/contrib/rpg/rpsystem/rpsystem.py index 89e284dcb9..f4db843ec0 100644 --- a/evennia/contrib/rpg/rpsystem/rpsystem.py +++ b/evennia/contrib/rpg/rpsystem/rpsystem.py @@ -235,6 +235,26 @@ class RecogError(Exception): class LanguageError(Exception): pass +def _get_case_ref(string): + """ + Helper function which parses capitalization and + returns the appropriate case-ref character for emotes. + """ + # default to retaining the original case + case = "~" + # internal flags for the case used for the original /query + # - t for titled input (like /Name) + # - ^ for all upercase input (like /NAME) + # - v for lower-case input (like /name) + # - ~ for mixed case input (like /nAmE) + if string.istitle(): + case = "t" + elif string.isupper(): + case = "^" + elif string.islower(): + case = "v" + + return case # emoting mechanisms def parse_language(speaker, emote): @@ -368,22 +388,7 @@ def parse_sdescs_and_recogs(sender, candidates, string, search_mode=False, case_ # first, find and replace any self-refs for self_match in list(_RE_SELF_REF.finditer(string)): matched = self_match.group() - case = "~" # retain original case of sdesc - if case_sensitive: - # case sensitive mode - # internal flags for the case used for the original /query - # - t for titled input (like /Name) - # - ^ for all upercase input (like /NAME) - # - v for lower-case input (like /name) - # - ~ for mixed case input (like /nAmE) - matchtext = matched.lstrip(_PREFIX) - if matchtext.istitle(): - case = "t" - elif matchtext.isupper(): - case = "^" - elif matchtext.islower(): - case = "v" - + case = _get_case_ref(matched.lstrip(_PREFIX)) if case_sensitive else "~" key = f"#{sender.id}{case}" # replaced with ref string = string.replace(matched,f"{{{key}}}") @@ -481,24 +486,9 @@ def parse_sdescs_and_recogs(sender, candidates, string, search_mode=False, case_ errors.append(_EMOTE_NOMATCH_ERROR.format(ref=marker_match.group())) elif nmatches == 1: # a unique match - parse into intermediary representation - case = "~" # retain original case of sdesc - if case_sensitive: - # case sensitive mode - # internal flags for the case used for the original /query - # - t for titled input (like /Name) - # - ^ for all upercase input (like /NAME) - # - v for lower-case input (like /name) - # - ~ for mixed case input (like /nAmE) - matchtext = marker_match.group().lstrip(_PREFIX) - if matchtext.istitle(): - case = "t" - elif matchtext.isupper(): - case = "^" - elif matchtext.islower(): - case = "v" - - key = f"#{obj.id}{case}" + case = _get_case_ref(marker_match.group()) if case_sensitive else "~" # recombine emote with matched text replaced by ref + key = f"#{obj.id}{case}" string = f"{head}{{{key}}}{tail}" mapping[key] = obj From dbaaf80e5e29e3408bfa62f635014a69c9134bcf Mon Sep 17 00:00:00 2001 From: InspectorCaracal Date: Mon, 11 Jul 2022 10:42:50 -0600 Subject: [PATCH 3/6] quick patch to be consistent with test expectations --- evennia/contrib/rpg/rpsystem/rpsystem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evennia/contrib/rpg/rpsystem/rpsystem.py b/evennia/contrib/rpg/rpsystem/rpsystem.py index f4db843ec0..1e79af0b04 100644 --- a/evennia/contrib/rpg/rpsystem/rpsystem.py +++ b/evennia/contrib/rpg/rpsystem/rpsystem.py @@ -388,7 +388,7 @@ def parse_sdescs_and_recogs(sender, candidates, string, search_mode=False, case_ # first, find and replace any self-refs for self_match in list(_RE_SELF_REF.finditer(string)): matched = self_match.group() - case = _get_case_ref(matched.lstrip(_PREFIX)) if case_sensitive else "~" + case = _get_case_ref(matched.lstrip(_PREFIX)) if case_sensitive else "" key = f"#{sender.id}{case}" # replaced with ref string = string.replace(matched,f"{{{key}}}") @@ -486,7 +486,7 @@ def parse_sdescs_and_recogs(sender, candidates, string, search_mode=False, case_ errors.append(_EMOTE_NOMATCH_ERROR.format(ref=marker_match.group())) elif nmatches == 1: # a unique match - parse into intermediary representation - case = _get_case_ref(marker_match.group()) if case_sensitive else "~" + case = _get_case_ref(marker_match.group()) if case_sensitive else "" # recombine emote with matched text replaced by ref key = f"#{obj.id}{case}" string = f"{head}{{{key}}}{tail}" From add1518c95c4917444764116adb6f84dff749642 Mon Sep 17 00:00:00 2001 From: InspectorCaracal Date: Mon, 11 Jul 2022 11:12:40 -0600 Subject: [PATCH 4/6] match punctuated self-ref cases with regex --- evennia/contrib/rpg/rpsystem/rpsystem.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/evennia/contrib/rpg/rpsystem/rpsystem.py b/evennia/contrib/rpg/rpsystem/rpsystem.py index 1e79af0b04..8592feb108 100644 --- a/evennia/contrib/rpg/rpsystem/rpsystem.py +++ b/evennia/contrib/rpg/rpsystem/rpsystem.py @@ -202,7 +202,7 @@ _RE_RIGHT_BRACKETS = re.compile(r"\}+", _RE_FLAGS) _RE_REF = re.compile(r"\{+\#([0-9]+[\^\~tv]{0,1})\}+") # This regex is used to quickly reference one self in an emote. -_RE_SELF_REF = re.compile(r"/me\b|@", _RE_FLAGS) +_RE_SELF_REF = re.compile(r"(/me|@)(?=\W+)", _RE_FLAGS) # regex for non-alphanumberic end of a string _RE_CHAREND = re.compile(r"\W+$", _RE_FLAGS) @@ -359,7 +359,7 @@ def parse_sdescs_and_recogs(sender, candidates, string, search_mode=False, case_ """ # build a list of candidates with all possible referrable names # include 'me' keyword for self-ref - candidate_map = [(sender, "me")] + candidate_map = [] for obj in candidates: # check if sender has any recogs for obj and add if hasattr(sender, "recog"): @@ -391,10 +391,9 @@ def parse_sdescs_and_recogs(sender, candidates, string, search_mode=False, case_ case = _get_case_ref(matched.lstrip(_PREFIX)) if case_sensitive else "" key = f"#{sender.id}{case}" # replaced with ref - string = string.replace(matched,f"{{{key}}}") + string = _RE_SELF_REF.sub(f"{{{key}}}", string, count=1) mapping[key] = sender - - + for marker_match in reversed(list(_RE_OBJ_REF_START.finditer(string))): # we scan backwards so we can replace in-situ without messing # up later occurrences. Given a marker match, query from From 303f836fa19b2785ad9832c1f9d861379fde6819 Mon Sep 17 00:00:00 2001 From: InspectorCaracal Date: Mon, 11 Jul 2022 11:23:17 -0600 Subject: [PATCH 5/6] update tests --- evennia/contrib/rpg/rpsystem/tests.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/evennia/contrib/rpg/rpsystem/tests.py b/evennia/contrib/rpg/rpsystem/tests.py index bd2ea32896..b8c7048c8d 100644 --- a/evennia/contrib/rpg/rpsystem/tests.py +++ b/evennia/contrib/rpg/rpsystem/tests.py @@ -97,6 +97,7 @@ recog02 = "Mr Receiver2" recog10 = "Mr Sender" emote = 'With a flair, /me looks at /first and /colliding sdesc-guy. She says "This is a test."' case_emote = "/Me looks at /first. Then, /me looks at /FIRST, /First and /Colliding twice." +poss_emote = "/Me frowns at /first for trying to steal /me's test." class TestRPSystem(BaseEvenniaTest): @@ -140,7 +141,7 @@ class TestRPSystem(BaseEvenniaTest): ), ) - def parse_sdescs_and_recogs(self): + def test_parse_sdescs_and_recogs(self): speaker = self.speaker speaker.sdesc.add(sdesc0) self.receiver1.sdesc.add(sdesc1) @@ -149,9 +150,9 @@ class TestRPSystem(BaseEvenniaTest): result = ( 'With a flair, {#9} looks at {#10} and {#11}. She says "This is a test."', { - "#11": "Another nice colliding sdesc-guy for tests", - "#10": "The first receiver of emotes.", - "#9": "A nice sender of emotes", + "#11": self.receiver2, + "#10": self.receiver1, + "#9": speaker, }, ) self.assertEqual( @@ -164,6 +165,24 @@ class TestRPSystem(BaseEvenniaTest): result, ) + def test_possessive_selfref(self): + speaker = self.speaker + speaker.sdesc.add(sdesc0) + self.receiver1.sdesc.add(sdesc1) + self.receiver2.sdesc.add(sdesc2) + candidates = (self.receiver1, self.receiver2) + result = ( + "{#9} frowns at {#10} for trying to steal {#9}'s test.", + { + "#10": self.receiver1, + "#9": speaker, + }, + ) + self.assertEqual( + rpsystem.parse_sdescs_and_recogs(speaker, candidates, poss_emote, case_sensitive=False), + result, + ) + def test_get_sdesc(self): looker = self.speaker # Sender target = self.receiver1 # Receiver1 From c9772e199fd8b2fb2af2e6ffaf4acfdf2dc5b8c8 Mon Sep 17 00:00:00 2001 From: InspectorCaracal Date: Mon, 11 Jul 2022 11:54:18 -0600 Subject: [PATCH 6/6] actually update tests --- evennia/contrib/rpg/rpsystem/tests.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/evennia/contrib/rpg/rpsystem/tests.py b/evennia/contrib/rpg/rpsystem/tests.py index b8c7048c8d..f62c77f63b 100644 --- a/evennia/contrib/rpg/rpsystem/tests.py +++ b/evennia/contrib/rpg/rpsystem/tests.py @@ -146,13 +146,16 @@ class TestRPSystem(BaseEvenniaTest): speaker.sdesc.add(sdesc0) self.receiver1.sdesc.add(sdesc1) self.receiver2.sdesc.add(sdesc2) + id0 = f"#{speaker.id}" + id1 = f"#{self.receiver1.id}" + id2 = f"#{self.receiver2.id}" candidates = (self.receiver1, self.receiver2) result = ( - 'With a flair, {#9} looks at {#10} and {#11}. She says "This is a test."', + 'With a flair, {'+id0+'} looks at {'+id1+'} and {'+id2+'}. She says "This is a test."', { - "#11": self.receiver2, - "#10": self.receiver1, - "#9": speaker, + id2: self.receiver2, + id1: self.receiver1, + id0: speaker, }, ) self.assertEqual( @@ -170,12 +173,15 @@ class TestRPSystem(BaseEvenniaTest): speaker.sdesc.add(sdesc0) self.receiver1.sdesc.add(sdesc1) self.receiver2.sdesc.add(sdesc2) + id0 = f"#{speaker.id}" + id1 = f"#{self.receiver1.id}" + id2 = f"#{self.receiver2.id}" candidates = (self.receiver1, self.receiver2) result = ( - "{#9} frowns at {#10} for trying to steal {#9}'s test.", + "{"+id0+"} frowns at {"+id1+"} for trying to steal {"+id0+"}'s test.", { - "#10": self.receiver1, - "#9": speaker, + id1: self.receiver1, + id0: speaker, }, ) self.assertEqual(