Skip to content

Commit d765590

Browse files
hf-kkleinKonstantinlord-haffi
authored
feat: allow "n" as upper bound of repeatability (prevent parser crashes but don't do anything with the value yet) (#572)
* feat: allow `n` as upper bound of repeatability #571 * wip * isort * python 3.9 * too many returns and no added value * mypy * black * Update src/ahbicht/models/mapping_results.py Co-authored-by: Leon Haffmans <[email protected]> * import --------- Co-authored-by: Konstantin <[email protected]> Co-authored-by: Leon Haffmans <[email protected]>
1 parent 749bb15 commit d765590

File tree

7 files changed

+27
-14
lines changed

7 files changed

+27
-14
lines changed

src/ahbicht/condition_node_distinction.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from ahbicht.models.condition_node_type import ConditionNodeType
88

9-
REGEX_PACKAGE_REPEATABILITY = re.compile(r"^(?P<n>\d)\.\.(?P<m>\d)$")
9+
REGEX_PACKAGE_REPEATABILITY = re.compile(r"^(?P<n>\d+)\.\.(?P<m>\d+|n)$")
1010

1111

1212
def derive_condition_node_type(condition_key: str) -> ConditionNodeType:
@@ -16,7 +16,7 @@ def derive_condition_node_type(condition_key: str) -> ConditionNodeType:
1616
if condition_key.endswith("P"):
1717
return ConditionNodeType.PACKAGE
1818
match = REGEX_PACKAGE_REPEATABILITY.match(condition_key)
19-
if match and int(match.group("n")) <= int(match.group("m")):
19+
if match:
2020
return ConditionNodeType.PACKAGE_REPEATABILITY
2121
if 1 <= int(condition_key) <= 499:
2222
return ConditionNodeType.REQUIREMENT_CONSTRAINT

src/ahbicht/expressions/ahb_expression_parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
PREFIX_OPERATOR: "X"i | "O"i | "U"i
2727
MODAL_MARK: /M(uss)?|S(oll)?|K(ann)?/i
2828
// Matches if it looks like a condition expression, but does not yet check if it is a syntactically valid one:
29-
CONDITION_EXPRESSION: /(?!\BU\B)[\[\]\(\)U∧O∨X⊻\d\sP\.UB]+/i
29+
CONDITION_EXPRESSION: /(?!\BU\B)[\[\]\(\)U∧O∨X⊻\d\sP\.UBn]+/i
3030
"""
3131
# Regarding the negative lookahead in the condition expression regex see examples https://regex101.com/r/6fFHD4/1
3232
# and CTRL+F for "Mus[2]" in the unittest that fails if you remove the lookahead.

src/ahbicht/expressions/condition_expression_parser.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
GRAMMAR = r"""
2323
?expression: expression "O"i expression -> or_composition
2424
| expression "∨" expression -> or_composition // the logical or
25-
| expression "V"i expression -> or_composition // a 'v' for those who first chose to introduce logical symbols but now can't find them on their keyboard
25+
| expression "V"i expression -> or_composition // a 'v' for those who first chose to introduce logical symbols like ∨ but now can't find them on their keyboard
2626
| expression "X"i expression -> xor_composition
2727
| expression "⊻" expression -> xor_composition
2828
| expression "U"i expression -> and_composition
@@ -38,7 +38,7 @@
3838
condition: "[" CONDITION_KEY "]" // a rule for condition keys
3939
TIME_CONDITION_KEY: /UB(1|2|3)/ // a terminal for "übergreifende Bedingungen für Zeitpunktangaben"
4040
CONDITION_KEY: INT // a TERMINAL for all the remaining ints (lower priority)
41-
REPEATABILITY: /\d+\.{2}[1-9]\d*/ // a terminal for repetitions n..m with n>=0 and m>n
41+
REPEATABILITY: /\d+\.{2}(?:([1-9]\d*)|n)/ // a terminal for repetitions n..m with n>=0 and m>n or m=="n"
4242
PACKAGE_KEY: INT "P" // a TERMINAL for all INTs followed by "P" (high priority)
4343
%import common.INT
4444
%import common.WS

src/ahbicht/models/mapping_results.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
This module contains classes that are returned by mappers, meaning they contain a mapping.
33
"""
44

5-
from typing import Optional
5+
import math
6+
from typing import Literal, Optional, Union
67

78
import attrs
89
from efoli import EdifactFormat
@@ -96,9 +97,10 @@ def check_max_greater_or_equal_than_min(instance: "Repeatability", attribute, va
9697
"""
9798
assert that 0<=min<max and not both min and max are 0
9899
"""
99-
if not 0 <= instance.min_occurrences <= instance.max_occurrences:
100-
raise ValueError(f"0≤n≤m is not fulfilled for n={instance.min_occurrences}, m={instance.max_occurrences}")
101-
if instance.min_occurrences == instance.max_occurrences == 0:
100+
max_occurrences = math.inf if instance.max_occurrences == "n" else instance.max_occurrences
101+
if not 0 <= instance.min_occurrences <= max_occurrences:
102+
raise ValueError(f"0≤n≤m is not fulfilled for n={instance.min_occurrences}, m={max_occurrences}")
103+
if instance.min_occurrences == max_occurrences == 0:
102104
raise ValueError("not both min and max occurrences must be 0")
103105

104106

@@ -116,8 +118,11 @@ class Repeatability:
116118
how often the segment/code has to be repeated (lower, inclusive bound); may be 0 for optional packages
117119
"""
118120

119-
max_occurrences: int = attrs.field(
120-
validator=attrs.validators.and_(attrs.validators.instance_of(int), check_max_greater_or_equal_than_min)
121+
max_occurrences: Union[int, Literal["n"]] = attrs.field(
122+
validator=attrs.validators.or_( # type:ignore[misc]
123+
attrs.validators.and_(attrs.validators.instance_of(int), check_max_greater_or_equal_than_min),
124+
attrs.validators.in_("n"),
125+
)
121126
)
122127
"""
123128
how often the segment/coode may be repeated at most (upper, inclusive bound).

src/ahbicht/utility_functions.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import inspect
77
import re
88
from re import Match
9-
from typing import Awaitable, Callable, List, Optional, TypeVar, Union
9+
from typing import Awaitable, Callable, List, Literal, Optional, TypeVar, Union
1010

1111
from lark import Tree
1212

@@ -16,7 +16,9 @@
1616
Result = TypeVar("Result")
1717

1818

19-
_repeatability_pattern = re.compile(r"^(?P<min>\d+)\.{2}(?P<max>\d+)$") #: a pattern to match "n..m" repeatabilities
19+
_repeatability_pattern = re.compile(
20+
r"^(?P<min>\d+)\.{2}(?:(?P<max>\d+)|n)$"
21+
) #: a pattern to match "n..m" repeatabilities
2022

2123

2224
async def gather_if_necessary(results_and_awaitable_results: List[Union[Result, Awaitable[Result]]]) -> List[Result]:
@@ -77,5 +79,9 @@ def parse_repeatability(repeatability_string: str) -> Repeatability:
7779
if match is None:
7880
raise ValueError(f"The given string '{repeatability_string}' could not be parsed as repeatability")
7981
min_repeatability = int(match["min"])
80-
max_repeatability = int(match["max"])
82+
max_repeatability: Union[int, Literal["n"]]
83+
try:
84+
max_repeatability = int(match["max"])
85+
except TypeError:
86+
max_repeatability = "n"
8187
return Repeatability(min_occurrences=min_repeatability, max_occurrences=max_repeatability)

unittests/test_utility_functions.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ async def test_gather_if_necessary(self, mixed_input: List[Union[T, Awaitable[T]
4444
pytest.param("1..1", Repeatability(min_occurrences=1, max_occurrences=1)),
4545
pytest.param("1..2", Repeatability(min_occurrences=1, max_occurrences=2)),
4646
pytest.param("71..89", Repeatability(min_occurrences=71, max_occurrences=89)),
47+
pytest.param("71..n", Repeatability(min_occurrences=71, max_occurrences="n")),
4748
],
4849
)
4950
def test_parse_repeatability(self, candidate: str, expected_result: Repeatability):

unittests/test_validity_check.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def configure(binder):
7070
pytest.param(
7171
"X ([950] [509] ∧ ([64] V [70])) V ([960] [522] ∧ [71] ∧ [53])", True
7272
), # nur echt mit 'V' statt LOR
73+
pytest.param("X [1P0..n]", True),
7374
],
7475
)
7576
async def test_is_valid_expression(self, ahb_expression: str, expected_result: bool, inject_cer_evaluators):

0 commit comments

Comments
 (0)