lexcql-parser 1.3.2__tar.gz → 1.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/PKG-INFO +5 -1
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/README.md +2 -0
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/pyproject.toml +22 -1
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/src/lexcql/parser.py +29 -25
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/src/lexcql/validation.py +6 -1
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/src/lexcql/LexLexer.g4 +0 -0
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/src/lexcql/LexLexer.interp +0 -0
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/src/lexcql/LexLexer.py +0 -0
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/src/lexcql/LexLexer.tokens +0 -0
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/src/lexcql/LexParser.g4 +0 -0
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/src/lexcql/LexParser.interp +0 -0
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/src/lexcql/LexParser.py +0 -0
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/src/lexcql/LexParser.tokens +0 -0
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/src/lexcql/LexParserListener.py +0 -0
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/src/lexcql/LexParserVisitor.py +0 -0
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/src/lexcql/__init__.py +0 -0
- {lexcql_parser-1.3.2 → lexcql_parser-1.3.4}/src/lexcql/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lexcql-parser
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.4
|
|
4
4
|
Summary: LexCQL Query Grammar and Parser
|
|
5
5
|
Keywords: LexCQL,FCS,CQL,Query Parser
|
|
6
6
|
Author: Erik Körner
|
|
@@ -25,6 +25,8 @@ Requires-Dist: twine>=6.2.0 ; extra == 'build'
|
|
|
25
25
|
Requires-Dist: black>=26.1.0 ; extra == 'style'
|
|
26
26
|
Requires-Dist: flake8>=7.3.0 ; extra == 'style'
|
|
27
27
|
Requires-Dist: isort>=8.0.0 ; extra == 'style'
|
|
28
|
+
Requires-Dist: mypy>=1.19.1 ; extra == 'style'
|
|
29
|
+
Requires-Dist: types-antlr4-python3-runtime>=4.13.0.20251118 ; extra == 'style'
|
|
28
30
|
Requires-Dist: pytest>=9.0.2 ; extra == 'test'
|
|
29
31
|
Requires-Dist: pytest-clarity>=1.0.1 ; extra == 'test'
|
|
30
32
|
Requires-Dist: pytest-cov>=7.0.0 ; extra == 'test'
|
|
@@ -175,6 +177,8 @@ uv sync --extra style
|
|
|
175
177
|
uv run isort --check --diff .
|
|
176
178
|
uv run black --check .
|
|
177
179
|
uv run flake8 . --show-source --statistics
|
|
180
|
+
|
|
181
|
+
uv run mypy src
|
|
178
182
|
```
|
|
179
183
|
|
|
180
184
|
Run tests:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "lexcql-parser"
|
|
3
|
-
version = "1.3.
|
|
3
|
+
version = "1.3.4"
|
|
4
4
|
description = "LexCQL Query Grammar and Parser"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -51,6 +51,8 @@ style = [
|
|
|
51
51
|
"black>=26.1.0",
|
|
52
52
|
"flake8>=7.3.0",
|
|
53
53
|
"isort>=8.0.0",
|
|
54
|
+
"mypy>=1.19.1",
|
|
55
|
+
"types-antlr4-python3-runtime>=4.13.0.20251118",
|
|
54
56
|
]
|
|
55
57
|
build = [
|
|
56
58
|
"twine>=6.2.0",
|
|
@@ -87,3 +89,22 @@ skip = [
|
|
|
87
89
|
"src/lexcql/LexParserVisitor.py",
|
|
88
90
|
]
|
|
89
91
|
skip_gitignore = true
|
|
92
|
+
|
|
93
|
+
# ignore all auto-generated antlr4 files,
|
|
94
|
+
# it needs a lot of work to quieten all warnings
|
|
95
|
+
[tool.mypy]
|
|
96
|
+
exclude = [
|
|
97
|
+
'LexLexer\.py$',
|
|
98
|
+
'LexParser\.py$',
|
|
99
|
+
'LexParserListener\.py$',
|
|
100
|
+
'LexParserVisitor\.py$',
|
|
101
|
+
]
|
|
102
|
+
check_untyped_defs = true
|
|
103
|
+
|
|
104
|
+
[[tool.mypy.overrides]]
|
|
105
|
+
module = [
|
|
106
|
+
"antlr4",
|
|
107
|
+
"antlr4.error",
|
|
108
|
+
"antlr4.error.ErrorListener",
|
|
109
|
+
]
|
|
110
|
+
ignore_missing_imports = true
|
|
@@ -94,12 +94,12 @@ class QueryVisitor(Generic[_R], metaclass=ABCMeta):
|
|
|
94
94
|
if not node:
|
|
95
95
|
return None
|
|
96
96
|
|
|
97
|
-
def noop(node: "QueryNode") -> Optional[_R]:
|
|
98
|
-
return self.defaultResult()
|
|
99
|
-
|
|
100
97
|
# search for specific visit function based on node_type
|
|
101
98
|
method_name = f"visit_{node.node_type}"
|
|
102
|
-
method = getattr(self, method_name,
|
|
99
|
+
method = getattr(self, method_name, self.visitChildren)
|
|
100
|
+
|
|
101
|
+
if LOGGER.isEnabledFor(logging.DEBUG):
|
|
102
|
+
LOGGER.debug("visiting '%s()': %s", method.__name__, node)
|
|
103
103
|
|
|
104
104
|
return method(node)
|
|
105
105
|
|
|
@@ -137,7 +137,7 @@ class QueryVisitorAdapter(QueryVisitor[_R]):
|
|
|
137
137
|
Generic with regards to the return type of the visit operation.
|
|
138
138
|
"""
|
|
139
139
|
|
|
140
|
-
def visit_SearchClauseGroup(self, node: "SearchClauseGroup") -> _R:
|
|
140
|
+
def visit_SearchClauseGroup(self, node: "SearchClauseGroup") -> Optional[_R]:
|
|
141
141
|
"""Visit a *search_clause_group* query node.
|
|
142
142
|
|
|
143
143
|
Args:
|
|
@@ -148,7 +148,7 @@ class QueryVisitorAdapter(QueryVisitor[_R]):
|
|
|
148
148
|
"""
|
|
149
149
|
return self.visitChildren(node)
|
|
150
150
|
|
|
151
|
-
def visit_Subquery(self, node: "Subquery") -> _R:
|
|
151
|
+
def visit_Subquery(self, node: "Subquery") -> Optional[_R]:
|
|
152
152
|
"""Visit a *subquery* query node.
|
|
153
153
|
|
|
154
154
|
Args:
|
|
@@ -159,7 +159,7 @@ class QueryVisitorAdapter(QueryVisitor[_R]):
|
|
|
159
159
|
"""
|
|
160
160
|
return self.visitChildren(node)
|
|
161
161
|
|
|
162
|
-
def visit_SearchClause(self, node: "SearchClause") -> _R:
|
|
162
|
+
def visit_SearchClause(self, node: "SearchClause") -> Optional[_R]:
|
|
163
163
|
"""Visit a *search_clause* query node.
|
|
164
164
|
|
|
165
165
|
Args:
|
|
@@ -170,7 +170,7 @@ class QueryVisitorAdapter(QueryVisitor[_R]):
|
|
|
170
170
|
"""
|
|
171
171
|
return self.visitChildren(node)
|
|
172
172
|
|
|
173
|
-
def visit_Relation(self, node: "Relation") -> _R:
|
|
173
|
+
def visit_Relation(self, node: "Relation") -> Optional[_R]:
|
|
174
174
|
"""Visit a *relation* query node.
|
|
175
175
|
|
|
176
176
|
Args:
|
|
@@ -181,7 +181,7 @@ class QueryVisitorAdapter(QueryVisitor[_R]):
|
|
|
181
181
|
"""
|
|
182
182
|
return self.visitChildren(node)
|
|
183
183
|
|
|
184
|
-
def visit_Modifier(self, node: "Modifier") -> _R:
|
|
184
|
+
def visit_Modifier(self, node: "Modifier") -> Optional[_R]:
|
|
185
185
|
"""Visit a *modifier* query node.
|
|
186
186
|
|
|
187
187
|
Args:
|
|
@@ -361,7 +361,7 @@ class QueryNode(Generic[_R], metaclass=ABCMeta):
|
|
|
361
361
|
strrepr += f"@{self.location.start}:{self.location.stop}"
|
|
362
362
|
return strrepr
|
|
363
363
|
|
|
364
|
-
def accept(self, visitor: QueryVisitor) -> _R:
|
|
364
|
+
def accept(self, visitor: QueryVisitor) -> Optional[_R]:
|
|
365
365
|
return visitor.visit(self)
|
|
366
366
|
|
|
367
367
|
|
|
@@ -411,7 +411,7 @@ class Relation(QueryNode):
|
|
|
411
411
|
relation: the relation name or symbol
|
|
412
412
|
modifiers: the list of modifiers for this relation or ``None``
|
|
413
413
|
"""
|
|
414
|
-
super().__init__(QueryNodeType.RELATION, children=modifiers)
|
|
414
|
+
super().__init__(QueryNodeType.RELATION, children=modifiers) # type: ignore
|
|
415
415
|
|
|
416
416
|
self.relation = relation
|
|
417
417
|
"""the relation"""
|
|
@@ -422,7 +422,7 @@ class Relation(QueryNode):
|
|
|
422
422
|
Returns:
|
|
423
423
|
List[Modifier]: the modifiers
|
|
424
424
|
"""
|
|
425
|
-
return self.children
|
|
425
|
+
return self.children # type: ignore
|
|
426
426
|
|
|
427
427
|
@property
|
|
428
428
|
def modifiers(self) -> List[Modifier]:
|
|
@@ -465,7 +465,7 @@ class SearchClause(QueryNode):
|
|
|
465
465
|
Returns:
|
|
466
466
|
Optional[Relation]: the relation or ``None``
|
|
467
467
|
"""
|
|
468
|
-
return self.get_child(0, Relation)
|
|
468
|
+
return self.get_child(0, Relation) # type: ignore
|
|
469
469
|
|
|
470
470
|
@property
|
|
471
471
|
def relation(self) -> Optional[Relation]:
|
|
@@ -542,7 +542,7 @@ class SearchClauseGroup(QueryNode):
|
|
|
542
542
|
return self.get_right_child()
|
|
543
543
|
|
|
544
544
|
@property
|
|
545
|
-
def boolean(self) ->
|
|
545
|
+
def boolean(self) -> RBoolean:
|
|
546
546
|
return self.get_boolean()
|
|
547
547
|
|
|
548
548
|
def has_boolean(self, r_boolean: RBoolean) -> bool:
|
|
@@ -586,7 +586,7 @@ class Subquery(QueryNode):
|
|
|
586
586
|
self.inParentheses = inParentheses
|
|
587
587
|
"""Is this query node in parentheses."""
|
|
588
588
|
|
|
589
|
-
def get_child(self) -> QueryNode:
|
|
589
|
+
def get_child(self) -> QueryNode: # type: ignore
|
|
590
590
|
"""Get the inner child
|
|
591
591
|
|
|
592
592
|
Returns:
|
|
@@ -754,18 +754,22 @@ class ExpressionTreeBuilder(LexParserVisitor):
|
|
|
754
754
|
if len(self.stack) > pos:
|
|
755
755
|
if len(self.stack) - pos == 1:
|
|
756
756
|
# TODO: noop?
|
|
757
|
-
node
|
|
757
|
+
node = self.stack.pop()
|
|
758
|
+
assert isinstance(node, QueryNode), f"node must be a QueryNode, found {node=}"
|
|
758
759
|
self.stack.append(node)
|
|
759
760
|
else:
|
|
760
|
-
children: List[QueryNode] = []
|
|
761
|
+
children: List[QueryNode | RBoolean] = []
|
|
761
762
|
while len(self.stack) > pos:
|
|
762
763
|
children.insert(0, self.stack.pop())
|
|
763
764
|
|
|
764
765
|
# build tree
|
|
765
|
-
node
|
|
766
|
+
node = children.pop(0)
|
|
767
|
+
assert isinstance(node, QueryNode), f"node must be a QueryNode, found {node=}"
|
|
766
768
|
while len(children) >= 2:
|
|
767
|
-
rBoolean
|
|
768
|
-
|
|
769
|
+
rBoolean = children.pop(0)
|
|
770
|
+
assert isinstance(rBoolean, RBoolean), f"node must be a RBoolean, found {rBoolean=}"
|
|
771
|
+
other = children.pop(0)
|
|
772
|
+
assert isinstance(other, QueryNode), f"node must be a QueryNode, found {other=}"
|
|
769
773
|
node = SearchClauseGroup(node, rBoolean, other)
|
|
770
774
|
if self.parser.enableSourceLocations:
|
|
771
775
|
node.location = SourceLocation.fromContext(ctx)
|
|
@@ -872,11 +876,11 @@ class ExpressionTreeBuilder(LexParserVisitor):
|
|
|
872
876
|
|
|
873
877
|
searchTerm: str
|
|
874
878
|
if ctx.SIMPLE_STRING() is not None:
|
|
875
|
-
tn
|
|
879
|
+
tn = ctx.SIMPLE_STRING()
|
|
876
880
|
assert isinstance(tn, TerminalNodeImpl), "visitSearch_term ctx.SIMPLE_STRING() must be TerminalNodeImpl"
|
|
877
881
|
searchTerm = tn.getSymbol().text
|
|
878
882
|
elif ctx.QUOTED_STRING() is not None:
|
|
879
|
-
tn
|
|
883
|
+
tn = ctx.QUOTED_STRING()
|
|
880
884
|
assert isinstance(tn, TerminalNodeImpl), "visitSearch_term ctx.QUOTED_STRING() must be TerminalNodeImpl"
|
|
881
885
|
searchTerm = tn.getSymbol().text
|
|
882
886
|
searchTerm = self.unquoteString(searchTerm)
|
|
@@ -919,7 +923,7 @@ class ExpressionTreeBuilder(LexParserVisitor):
|
|
|
919
923
|
m_ctx = ctx.modifier_list()
|
|
920
924
|
modifiers: List[Modifier] = []
|
|
921
925
|
if m_ctx is not None:
|
|
922
|
-
modifiers
|
|
926
|
+
modifiers = self.stack.pop()
|
|
923
927
|
|
|
924
928
|
name: str = self.stack.pop()
|
|
925
929
|
|
|
@@ -980,7 +984,7 @@ class ExpressionTreeBuilder(LexParserVisitor):
|
|
|
980
984
|
ctx.getText(),
|
|
981
985
|
)
|
|
982
986
|
|
|
983
|
-
tn
|
|
987
|
+
tn = ctx.modifier_name().simple_name().SIMPLE_STRING()
|
|
984
988
|
assert isinstance(
|
|
985
989
|
tn, TerminalNodeImpl
|
|
986
990
|
), "visitModifier ctx.modifier_name().simple_name().SIMPLE_STRING() must be TerminalNodeImpl"
|
|
@@ -991,7 +995,7 @@ class ExpressionTreeBuilder(LexParserVisitor):
|
|
|
991
995
|
r_ctx = ctx.modifier_relation()
|
|
992
996
|
if r_ctx is not None:
|
|
993
997
|
relation = r_ctx.relation_symbol().getText()
|
|
994
|
-
tn
|
|
998
|
+
tn = r_ctx.modifier_value().SIMPLE_STRING()
|
|
995
999
|
assert isinstance(
|
|
996
1000
|
tn, TerminalNodeImpl
|
|
997
1001
|
), "visitModifier r_ctx.modifier_value().SIMPLE_STRING() must be TerminalNodeImpl"
|
|
@@ -372,7 +372,12 @@ class LexCQLValidatorV0_3(Validator[None]):
|
|
|
372
372
|
)
|
|
373
373
|
else:
|
|
374
374
|
if index not in self.KNOWN_INDEXES:
|
|
375
|
-
|
|
375
|
+
if index == "def":
|
|
376
|
+
self.validation_warning(
|
|
377
|
+
node, f"Usage of legacy definition index '{node.index}'. Please update to 'definition'."
|
|
378
|
+
)
|
|
379
|
+
else:
|
|
380
|
+
self.validation_error(node, f"Unknown index '{node.index}'!")
|
|
376
381
|
|
|
377
382
|
# TODO: check `search_term` against relations/modifiers? (regex/masked)
|
|
378
383
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|