Skip to content

Commit ebf38f1

Browse files
committed
Map i-regexp pattern to PCRE-like pattern
1 parent 30d4400 commit ebf38f1

File tree

10 files changed

+51
-16
lines changed

10 files changed

+51
-16
lines changed

jsonpath_rfc9535/environment.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ def check_well_typedness(
217217
)
218218
elif typ == ExpressionType.LOGICAL:
219219
if not isinstance(
220-
arg, (FilterQuery, (LogicalExpression, ComparisonExpression))
220+
arg, (FilterQuery, LogicalExpression, ComparisonExpression)
221221
):
222222
raise JSONPathTypeError(
223223
f"{token.value}() argument {idx} must be of LogicalType",

jsonpath_rfc9535/filter_expressions.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ def __eq__(self, other: object) -> bool:
186186

187187
def evaluate(self, context: FilterContext) -> bool:
188188
"""Evaluate the filter expression in the given _context_."""
189+
# TODO: sort circuit eval of right if left is false
189190
return _compare(
190191
self.left.evaluate(context), self.operator, self.right.evaluate(context)
191192
)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from typing import List
2+
3+
4+
def map_re(pattern: str) -> str:
5+
escaped = False
6+
char_class = False
7+
parts: List[str] = []
8+
for ch in pattern:
9+
if escaped:
10+
parts.append(ch)
11+
escaped = False
12+
continue
13+
14+
if ch == ".":
15+
if not char_class:
16+
parts.append(r"(?:(?![\r\n])\P{Cs}|\\p{Cs}\p{Cs})")
17+
else:
18+
parts.append(ch)
19+
elif ch == "\\":
20+
escaped = True
21+
parts.append(ch)
22+
elif ch == "[":
23+
char_class = True
24+
parts.append(ch)
25+
elif ch == "]":
26+
char_class = False
27+
parts.append(ch)
28+
else:
29+
parts.append(ch)
30+
31+
return "".join(parts)

jsonpath_rfc9535/function_extensions/match.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from jsonpath_rfc9535.function_extensions import ExpressionType
66
from jsonpath_rfc9535.function_extensions import FilterFunction
77

8+
from ._pattern import map_re
9+
810

911
class Match(FilterFunction):
1012
"""The standard `match` function."""
@@ -16,6 +18,6 @@ def __call__(self, string: str, pattern: str) -> bool:
1618
"""Return `True` if _string_ matches _pattern_, or `False` otherwise."""
1719
try:
1820
# re.fullmatch caches compiled patterns internally
19-
return bool(re.fullmatch(pattern, string))
21+
return bool(re.fullmatch(map_re(pattern), string))
2022
except (TypeError, re.error):
2123
return False

jsonpath_rfc9535/function_extensions/search.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from jsonpath_rfc9535.function_extensions import ExpressionType
66
from jsonpath_rfc9535.function_extensions import FilterFunction
77

8+
from ._pattern import map_re
9+
810

911
class Search(FilterFunction):
1012
"""The standard `search` function."""
@@ -16,6 +18,6 @@ def __call__(self, string: str, pattern: str) -> bool:
1618
"""Return `True` if _string_ contains _pattern_, or `False` otherwise."""
1719
try:
1820
# re.search caches compiled patterns internally
19-
return bool(re.search(pattern, string))
21+
return bool(re.search(map_re(pattern), string, re.VERSION1))
2022
except (TypeError, re.error):
2123
return False

jsonpath_rfc9535/parse.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ def __init__(self, *, env: JSONPathEnvironment) -> None:
114114
TokenType.TRUE: self.parse_boolean,
115115
}
116116

117+
# TODO: can a function argument be a grouped expression?
118+
# TODO: can a function argument contain a !?
119+
117120
self.function_argument_map: Dict[
118121
TokenType, Callable[[TokenStream], Expression]
119122
] = {
@@ -412,6 +415,7 @@ def parse_grouped_expression(self, stream: TokenStream) -> Expression:
412415
raise JSONPathSyntaxError(
413416
"unbalanced parentheses", token=stream.current
414417
)
418+
# TODO: only if binary op
415419
expr = self.parse_infix_expression(stream, expr)
416420

417421
stream.expect(TokenType.RPAREN)

jsonpath_rfc9535/query.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class JSONPathQuery:
3232
segments: The `JSONPathSegment` instances that make up this query.
3333
"""
3434

35-
__slots__ = ("env", "fake_root", "segments")
35+
__slots__ = ("env", "segments")
3636

3737
def __init__(
3838
self,

tests/test_errors.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,12 @@ class FilterLiteralTestCase(NamedTuple):
9797
FilterLiteralTestCase("just int", "$[?2]"),
9898
FilterLiteralTestCase("just float", "$[?2.2]"),
9999
FilterLiteralTestCase("just null", "$[?null]"),
100-
FilterLiteralTestCase("literal and literal", "$[?true and false]"),
101-
FilterLiteralTestCase("literal or literal", "$[?true or false]"),
102-
FilterLiteralTestCase("comparison and literal", "$[?true == false and false]"),
103-
FilterLiteralTestCase("comparison or literal", "$[?true == false or false]"),
104-
FilterLiteralTestCase("literal and comparison", "$[?true and true == false]"),
105-
FilterLiteralTestCase("literal or comparison", "$[?false or true == false]"),
100+
FilterLiteralTestCase("literal and literal", "$[?true && false]"),
101+
FilterLiteralTestCase("literal or literal", "$[?true || false]"),
102+
FilterLiteralTestCase("comparison and literal", "$[?true == false && false]"),
103+
FilterLiteralTestCase("comparison or literal", "$[?true == false || false]"),
104+
FilterLiteralTestCase("literal and comparison", "$[?true && true == false]"),
105+
FilterLiteralTestCase("literal or comparison", "$[?false || true == false]"),
106106
]
107107

108108

tests/test_parse.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,6 @@ class Case:
7171
query="$.some[?(@.thing >= 7)]",
7272
want="$['some'][?@['thing'] >= 7]",
7373
),
74-
Case(
75-
description="filter with >=",
76-
query="$.some[?(@.thing >= 7)]",
77-
want="$['some'][?@['thing'] >= 7]",
78-
),
7974
Case(
8075
description="filter with !=",
8176
query="$.some[?(@.thing != 7)]",

0 commit comments

Comments
 (0)