Allow variables in RHS of if expressions (#28)
# Pull Request ## Description Allows variables in the RHS of expressions (e.g. `<ppp:if tagsSeen contains tag>`) for more complex logic ## Type of change Please delete options that are not relevant. - [x] New feature (non-breaking change which adds functionality) - [x] This change requires a documentation update ## How Has This Been Tested? Added additional tests to the existing test suite. ## Checklist - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules - [x] I have checked my code and corrected any misspellings
This commit is contained in:
+1
-1
@@ -209,7 +209,7 @@ The `conditionN` can be:
|
||||
| `variable [not] operation value` | check the variable against a value |
|
||||
| `variable [not] operation (value1,value2,...)` | check the variable against a list of values |
|
||||
|
||||
For a simple value the allowed operations are `eq`, `ne`, `gt`, `lt`, `ge`, `le`, `contains` and the value can be a quoted string or an integer. For a list of values the allowed operations are `contains`, `in` and the value of the variable is checked against all the elements of the list until one matches. The operation can be preceded by `not` for readability, instead of using it in the front.
|
||||
For a simple value the allowed operations are `eq`, `ne`, `gt`, `lt`, `ge`, `le`, `contains` and the value can be a quoted string, an integer, or another variable name (e.g., `<ppp:if var1 contains var2>`). When the value is a variable name, it is resolved to the variable's current value before comparison. For a list of values the allowed operations are `contains`, `in` and the value of the variable is checked against all the elements of the list until one matches. The operation can be preceded by `not` for readability, instead of using it in the front.
|
||||
|
||||
When a variable used in an integer comparison is undefined or cannot be converted to an integer (e.g. it contains non-numeric text or is an empty string), the behavior depends on the `on_warning` setting: in `warn` mode the comparison evaluates to false, and in `stop` mode an error is raised.
|
||||
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@ operation_and: condition _WHITESPACE "and" _WHITESPACE condition
|
||||
operation_or: condition _WHITESPACE "or" _WHITESPACE condition
|
||||
operation_not: "not" ( ( _WHITESPACE ungrouped_condition ) | ( _WHITESPACE? grouped_condition ) )
|
||||
truthy_operand: IDENTIFIER
|
||||
comparison_simple_value: IDENTIFIER _WHITESPACE ( /not/ _WHITESPACE )? /eq|ne|gt|lt|ge|le|contains/ _WHITESPACE SIMPLEVALUE
|
||||
comparison_simple_value: IDENTIFIER _WHITESPACE ( /not/ _WHITESPACE )? /eq|ne|gt|lt|ge|le|contains/ _WHITESPACE ( SIMPLEVALUE | IDENTIFIER )
|
||||
comparison_list_value: IDENTIFIER _WHITESPACE ( /not/ _WHITESPACE )? /contains|in/ _WHITESPACE listvalue
|
||||
listvalue.9: "(" _WHITESPACE? SIMPLEVALUE ( _WHITESPACE? "," _WHITESPACE? SIMPLEVALUE )* _WHITESPACE? ")"
|
||||
|
||||
|
||||
@@ -1082,6 +1082,24 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
)
|
||||
)
|
||||
|
||||
def __resolve_cond_value(self, c: str):
|
||||
"""Resolve a condition value: try int first, fall back to variable lookup."""
|
||||
try:
|
||||
return int(c)
|
||||
except ValueError:
|
||||
# Bare identifier — resolve as variable reference
|
||||
if c.startswith("_"):
|
||||
val = self.__ppp.system_variables.get(c, None)
|
||||
if val is None:
|
||||
val = ""
|
||||
self.warn_or_stop(f"Unknown system variable {c}")
|
||||
else:
|
||||
val = self.__get_user_variable_value(c)
|
||||
if val is None:
|
||||
val = ""
|
||||
self.warn_or_stop(f"Unknown user variable {c}")
|
||||
return val.lower() if isinstance(val, str) else val
|
||||
|
||||
def __eval_basiccondition(self, cond_var: str, cond_comp: str, cond_value: str | list[str]) -> bool:
|
||||
"""
|
||||
Evaluate a condition based on the given variable, comparison, and value.
|
||||
@@ -1133,7 +1151,7 @@ class PromptPostProcessor: # pylint: disable=too-few-public-methods,too-many-in
|
||||
(
|
||||
c[1:-1].lower()
|
||||
if c.startswith('"') or c.startswith("'")
|
||||
else True if c.lower() == "true" else False if c.lower() == "false" or c == "" else int(c)
|
||||
else True if c.lower() == "true" else False if c.lower() == "false" or c == "" else self.__resolve_cond_value(c)
|
||||
)
|
||||
for c in cond_value
|
||||
)
|
||||
|
||||
@@ -1328,6 +1328,44 @@ class TestPromptPostProcessor(TestPromptPostProcessorBase):
|
||||
ppp=self.nocupppp,
|
||||
)
|
||||
|
||||
# Variable-vs-variable comparison tests
|
||||
|
||||
def test_cmd_if_var_vs_var_eq(self): # var eq var: both set to same value, if-branch taken
|
||||
self.process(
|
||||
PromptPair(
|
||||
"<ppp:set v1>hello<ppp:/set><ppp:set v2>hello<ppp:/set><ppp:if v1 eq v2>YES<ppp:else>NO<ppp:/if>",
|
||||
"",
|
||||
),
|
||||
PromptPair("YES", ""),
|
||||
)
|
||||
|
||||
def test_cmd_if_var_vs_var_ne(self): # var ne var: different values, ne condition true
|
||||
self.process(
|
||||
PromptPair(
|
||||
"<ppp:set v1>apple<ppp:/set><ppp:set v2>orange<ppp:/set><ppp:if v1 ne v2>YES<ppp:else>NO<ppp:/if>",
|
||||
"",
|
||||
),
|
||||
PromptPair("YES", ""),
|
||||
)
|
||||
|
||||
def test_cmd_if_var_vs_var_contains(self): # var contains var: var1 contains var2's value
|
||||
self.process(
|
||||
PromptPair(
|
||||
"<ppp:set v1>hello world<ppp:/set><ppp:set v2>hello<ppp:/set><ppp:if v1 contains v2>YES<ppp:else>NO<ppp:/if>",
|
||||
"",
|
||||
),
|
||||
PromptPair("YES", ""),
|
||||
)
|
||||
|
||||
def test_cmd_if_var_vs_var_not_contains(self): # var not contains var: var1 does not contain var2's value
|
||||
self.process(
|
||||
PromptPair(
|
||||
"<ppp:set v1>hello world<ppp:/set><ppp:set v2>goodbye<ppp:/set><ppp:if v1 not contains v2>YES<ppp:else>NO<ppp:/if>",
|
||||
"",
|
||||
),
|
||||
PromptPair("YES", ""),
|
||||
)
|
||||
|
||||
# NaN/undefined variable integer comparison tests
|
||||
|
||||
def test_cmd_if_undefined_var_int_compare_warn(self): # undefined var integer compare with on_warning=warn
|
||||
|
||||
Reference in New Issue
Block a user