Merge pull request #3514 from InspectorCaracal/funcparser-pron-targets

Adds support for mapping keys, pronoun-verb agreement to actor stance callables
This commit is contained in:
Griatch 2024-04-27 20:37:52 +02:00 committed by GitHub
commit 01783bd376
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 119 additions and 24 deletions

View file

@ -337,13 +337,17 @@ Here the `caller` is the one sending the message and `receiver` the one to see i
result of `you_obj.get_display_name(looker=receiver)`. This allows for a single string to echo differently
depending on who sees it, and also to reference other people in the same way.
- `$You([key])` - same as `$you` but always capitalized.
- `$conj(verb)` ([code](evennia.utils.funcparser.funcparser_callable_conjugate)) - conjugates a verb
between 2nd person presens to 3rd person presence depending on who
- `$conj(verb [,key])` ([code](evennia.utils.funcparser.funcparser_callable_conjugate)) - conjugates a verb
between 2nd person presence to 3rd person presence depending on who
sees the string. For example `"$You() $conj(smiles)".` will show as "You smile." and "Tom smiles." depending
on who sees it. This makes use of the tools in [evennia.utils.verb_conjugation](evennia.utils.verb_conjugation)
to do this, and only works for English verbs.
- `$pron(pronoun [,options])` ([code](evennia.utils.funcparser.funcparser_callable_pronoun)) - Dynamically
- `$pron(pronoun [,options] [,key])` ([code](evennia.utils.funcparser.funcparser_callable_pronoun)) - Dynamically
map pronouns (like his, herself, you, its etc) between 1st/2nd person to 3rd person.
- `$pconj(verb, [,key])` ([code](evennia.utils.funcparser.funcparser_callable_conjugate_for_pronouns)) - conjugates
a verb between 2nd and 3rd person, like `$conj`, but for pronouns instead of nouns to account for plural
gendering. For example `"$Pron(you) $pconj(smiles)"` will show to others as "He smiles" for a gender of "male", or
"They smile" for a gender of "plural".
### `evennia.prototypes.protfuncs`

View file

@ -1320,15 +1320,18 @@ def funcparser_callable_your_capitalize(
)
def funcparser_callable_conjugate(*args, caller=None, receiver=None, **kwargs):
def funcparser_callable_conjugate(*args, caller=None, receiver=None, mapping=None, **kwargs):
"""
Usage: $conj(word, [options])
Usage: $conj(word, [key])
Conjugate a verb according to if it should be 2nd or third person.
Keyword Args:
caller (Object): The object who represents 'you' in the string.
receiver (Object): The recipient of the string.
mapping (dict, optional): This is a mapping `{key:Object, ...}` and is
used to find which object the optional `key` argument refers to. If not given,
the `caller` kwarg is used.
Returns:
str: The parsed string.
@ -1337,13 +1340,10 @@ def funcparser_callable_conjugate(*args, caller=None, receiver=None, **kwargs):
ParsingError: If `you` and `recipient` were not both supplied.
Notes:
Note that the verb will not be capitalized. It also
assumes that the active party (You) is the one performing the verb.
This automatic conjugation will fail if the active part is another person
than 'you'. The caller/receiver must be passed to the parser directly.
Note that the verb will not be capitalized.
Examples:
This is often used in combination with the $you/You( callables.
This is often used in combination with the $you/You callables.
- `With a grin, $you() $conj(jump)`
@ -1356,14 +1356,76 @@ def funcparser_callable_conjugate(*args, caller=None, receiver=None, **kwargs):
if not (caller and receiver):
raise ParsingError("No caller/receiver supplied to $conj callable")
second_person_str, third_person_str = verb_actor_stance_components(args[0])
return second_person_str if caller == receiver else third_person_str
verb, *options = args
obj = caller
if mapping and options:
# get the correct referenced object from the mapping, or fall back to caller
obj = mapping.get(options[0], caller)
second_person_str, third_person_str = verb_actor_stance_components(verb)
return second_person_str if obj == receiver else third_person_str
def funcparser_callable_pronoun(*args, caller=None, receiver=None, capitalize=False, **kwargs):
def funcparser_callable_conjugate_for_pronouns(*args, caller=None, receiver=None, mapping=None, **kwargs):
"""
Usage: $pconj(word, [key])
Conjugate a verb according to if it should be 2nd or third person, respecting the
singular/plural gendering for third person.
Keyword Args:
caller (Object): The object who represents 'you' in the string.
receiver (Object): The recipient of the string.
mapping (dict, optional): This is a mapping `{key:Object, ...}` and is
used to find which object the optional `key` argument refers to. If not given,
the `caller` kwarg is used.
Returns:
str: The parsed string.
Raises:
ParsingError: If `you` and `recipient` were not both supplied.
Notes:
Note that the verb will not be capitalized.
Examples:
This is often used in combination with the $pron/Pron callables.
- `With a grin, $pron(you) $pconj(jump)`
You will see "With a grin, you jump."
With your gender as "male", others will see "With a grin, he jumps."
With your gender as "plural", others will see "With a grin, they jump."
"""
if not args:
return ""
if not (caller and receiver):
raise ParsingError("No caller/receiver supplied to $conj callable")
verb, *options = args
obj = caller
if mapping and options:
# get the correct referenced object from the mapping, or fall back to caller
obj = mapping.get(options[0], caller)
# identify whether the 3rd person form should be singular or plural
plural = False
if hasattr(obj, "gender"):
if callable(obj.gender):
plural = (obj.gender() == "plural")
else:
plural = (obj.gender == "plural")
second_person_str, third_person_str = verb_actor_stance_components(verb, plural=plural)
return second_person_str if obj == receiver else third_person_str
def funcparser_callable_pronoun(*args, caller=None, receiver=None, mapping=None, capitalize=False, **kwargs):
"""
Usage: $pron(word, [options])
Usage: $pron(word, [options], [key])
Adjust pronouns to the expected form. Pronouns are words you use instead of a
proper name, such as 'him', 'herself', 'theirs' etc. These look different
@ -1424,6 +1486,9 @@ def funcparser_callable_pronoun(*args, caller=None, receiver=None, capitalize=Fa
- `1st person`/`1st`/`1`
- `2nd person`/`2nd`/`2`
- `3rd person`/`3rd`/`3`
key (str, optional): If a mapping is provided, a string defining which object to
reference when finding the correct pronoun. If not provided, it defaults
to `caller`
Keyword Args:
@ -1435,6 +1500,9 @@ def funcparser_callable_pronoun(*args, caller=None, receiver=None, capitalize=Fa
receiver (Object): The recipient of the string. This being the same as
`caller` or not helps determine 2nd vs 3rd-person forms. This is
provided automatically by the funcparser.
mapping (dict, optional): This is a mapping `{key:Object, ...}` and is
used to find which object the optional `key` argument refers to. If not given,
the `caller` kwarg is used.
capitalize (bool): The input retains its capitalization. If this is set the output is
always capitalized.
@ -1457,8 +1525,17 @@ def funcparser_callable_pronoun(*args, caller=None, receiver=None, capitalize=Fa
"""
if not args:
return ""
# by default, we use the caller as the object being referred to
obj = caller
pronoun, *options = args
if options and mapping:
# check if the last argument is a valid mapping key
if options[-1] in mapping:
# get the object and remove the key from options
obj = mapping[options[-1]]
options = options[:-1]
# options is either multiple args or a space-separated string
if len(options) == 1:
options = options[0]
@ -1468,11 +1545,11 @@ def funcparser_callable_pronoun(*args, caller=None, receiver=None, capitalize=Fa
default_gender = "neutral"
default_viewpoint = "2nd person"
if hasattr(caller, "gender"):
if callable(caller.gender):
default_gender = caller.gender()
if hasattr(obj, "gender"):
if callable(obj.gender):
default_gender = obj.gender()
else:
default_gender = caller.gender
default_gender = obj.gender
if "viewpoint" in kwargs:
# passed into FuncParser initialization
@ -1490,7 +1567,7 @@ def funcparser_callable_pronoun(*args, caller=None, receiver=None, capitalize=Fa
pronoun_1st_or_2nd_person = pronoun_1st_or_2nd_person.capitalize()
pronoun_3rd_person = pronoun_3rd_person.capitalize()
return pronoun_1st_or_2nd_person if caller == receiver else pronoun_3rd_person
return pronoun_1st_or_2nd_person if obj == receiver else pronoun_3rd_person
def funcparser_callable_pronoun_capitalize(
@ -1557,6 +1634,7 @@ ACTOR_STANCE_CALLABLES = {
"obj": funcparser_callable_you,
"Obj": funcparser_callable_you_capitalize,
"conj": funcparser_callable_conjugate,
"pconj": funcparser_callable_conjugate_for_pronouns,
"pron": funcparser_callable_pronoun,
"Pron": funcparser_callable_pronoun_capitalize,
**FUNCPARSER_CALLABLES,

View file

@ -435,6 +435,7 @@ class TestDefaultCallables(TestCase):
("$You() $conj(smile) at him.", "You smile at him.", "Char1 smiles at him."),
("$You() $conj(smile) at $You(char1).", "You smile at You.", "Char1 smiles at Char1."),
("$You() $conj(smile) at $You(char2).", "You smile at Char2.", "Char1 smiles at You."),
("$You() $conj(smile) while $You(char2) $conj(waves, char2).", "You smile while Char2 waves.", "Char1 smiles while You wave."),
(
"$You(char2) $conj(smile) at $you(char1).",
"Char2 smile at you.",
@ -512,6 +513,15 @@ class TestDefaultCallables(TestCase):
ret = self.parser.parse(string, caller=self.obj1, raise_errors=True)
self.assertEqual(expected, ret)
def test_pronoun_mapping(self):
self.obj1.gender = "female"
self.obj2.gender = "male"
string = "Char1 raises $pron(your, char1) fist as Char2 raises $pron(yours, char2)"
expected = "Char1 raises her fist as Char2 raises his"
ret = self.parser.parse(string, caller=self.obj1, mapping={'char1': self.obj1, 'char2': self.obj2}, raise_errors=True)
self.assertEqual(expected, ret)
def test_pronoun_viewpoint(self):
string = "Char1 smiles at $pron(I)"

View file

@ -365,25 +365,28 @@ def verb_is_past_participle(verb):
return tense == "past participle"
def verb_actor_stance_components(verb):
def verb_actor_stance_components(verb, plural=False):
"""
Figure out actor stance components of a verb.
Args:
verb (str): The verb to analyze
plural (bool): Whether to force 3rd person to plural form
Returns:
tuple: The 2nd person (you) and 3rd person forms of the verb,
in the same tense as the ingoing verb.
"""
tense = verb_tense(verb)
them = "*" if plural else "3"
them_suff = "" if plural else "s"
if "participle" in tense or "plural" in tense:
return (verb, verb)
if tense == "infinitive" or "present" in tense:
you_str = verb_present(verb, person="2") or verb
them_str = verb_present(verb, person="3") or verb + "s"
them_str = verb_present(verb, person=them) or verb + them_suff
else:
you_str = verb_past(verb, person="2") or verb
them_str = verb_past(verb, person="3") or verb + "s"
them_str = verb_past(verb, person=them) or verb + them_suff
return (you_str, them_str)