pytrilogy 0.0.1.112__tar.gz → 0.0.1.113__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.
Potentially problematic release.
This version of pytrilogy might be problematic. Click here for more details.
- {pytrilogy-0.0.1.112/pytrilogy.egg-info → pytrilogy-0.0.1.113}/PKG-INFO +1 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_declarations.py +3 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_parsing.py +24 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_select.py +1 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_where_clause.py +1 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/enums.py +7 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/models.py +36 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/dialect/base.py +37 -17
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/parsing/common.py +1 -1
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/parsing/parse_engine.py +59 -311
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/LICENSE.md +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/README.md +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/pyproject.toml +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/pytrilogy.egg-info/SOURCES.txt +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/setup.cfg +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/setup.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_models.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/functions.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/concept_strategies_v3.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/concept_merge_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/executor.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/utility.py +0 -0
|
@@ -4,7 +4,7 @@ from trilogy.parser import parse
|
|
|
4
4
|
def test_declarations():
|
|
5
5
|
declarations = """key namespace.user_id int metadata(description="the description");
|
|
6
6
|
metric namespace.count <- count(namespace.user_id);
|
|
7
|
-
metric namespace.distinct_count <- count_distinct(namespace.user_id); #
|
|
7
|
+
metric namespace.distinct_count <- count_distinct(namespace.user_id); #the distinct count of user ids
|
|
8
8
|
"""
|
|
9
9
|
env, _ = parse(declarations)
|
|
10
10
|
|
|
@@ -14,3 +14,5 @@ def test_declarations():
|
|
|
14
14
|
env.concepts["namespace.distinct_count"].metadata.description
|
|
15
15
|
== "the distinct count of user ids"
|
|
16
16
|
)
|
|
17
|
+
|
|
18
|
+
assert env.concepts["namespace.user_id"].metadata.description == "the description"
|
|
@@ -211,7 +211,7 @@ rowset test<- select
|
|
|
211
211
|
;
|
|
212
212
|
|
|
213
213
|
select
|
|
214
|
-
count(test.name) -> test_name_count;
|
|
214
|
+
count( test.name ) -> test_name_count;
|
|
215
215
|
"""
|
|
216
216
|
)
|
|
217
217
|
# assert output_purpose == Purpose.METRIC
|
|
@@ -240,3 +240,26 @@ def test_between():
|
|
|
240
240
|
), type(right)
|
|
241
241
|
assert right.operator == ComparisonOperator.LTE
|
|
242
242
|
assert right.right == 5
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def test_the_comments():
|
|
246
|
+
_, parsed = parse_text(
|
|
247
|
+
"""const
|
|
248
|
+
# comment here?
|
|
249
|
+
order_id <- 4; SELECT
|
|
250
|
+
# TOOD - add in more columns?
|
|
251
|
+
order_id # this is the order id
|
|
252
|
+
WHERE
|
|
253
|
+
# order_id should not be null
|
|
254
|
+
order_id
|
|
255
|
+
# in this comp
|
|
256
|
+
is not
|
|
257
|
+
null; # nulls are the worst
|
|
258
|
+
|
|
259
|
+
"""
|
|
260
|
+
)
|
|
261
|
+
query = parsed[-1]
|
|
262
|
+
right = query.where_clause.conditional.right
|
|
263
|
+
assert isinstance(right, MagicConstants), type(right)
|
|
264
|
+
rendered = BaseDialect().render_expr(right)
|
|
265
|
+
assert rendered == "null"
|
|
@@ -76,6 +76,12 @@ class JoinType(Enum):
|
|
|
76
76
|
class Ordering(Enum):
|
|
77
77
|
ASCENDING = "asc"
|
|
78
78
|
DESCENDING = "desc"
|
|
79
|
+
ASC_NULLS_AUTO = "asc nulls auto"
|
|
80
|
+
ASC_NULLS_FIRST = "asc nulls first"
|
|
81
|
+
ASC_NULLS_LAST = "asc nulls last"
|
|
82
|
+
DESC_NULLS_FIRST = "desc nulls first"
|
|
83
|
+
DESC_NULLS_LAST = "desc nulls last"
|
|
84
|
+
DESC_NULLS_AUTO = "desc nulls auto"
|
|
79
85
|
|
|
80
86
|
|
|
81
87
|
class WindowType(Enum):
|
|
@@ -220,6 +226,7 @@ class ComparisonOperator(Enum):
|
|
|
220
226
|
ILIKE = "ilike"
|
|
221
227
|
CONTAINS = "contains"
|
|
222
228
|
ELSE = "else"
|
|
229
|
+
BETWEEN = "between"
|
|
223
230
|
|
|
224
231
|
@classmethod
|
|
225
232
|
def _missing_(cls, value):
|
|
@@ -812,6 +812,7 @@ class Function(Namespaced, SelectGrain, BaseModel):
|
|
|
812
812
|
"Parenthetical",
|
|
813
813
|
CaseWhen,
|
|
814
814
|
"CaseElse",
|
|
815
|
+
list,
|
|
815
816
|
ListWrapper[int],
|
|
816
817
|
ListWrapper[str],
|
|
817
818
|
ListWrapper[float],
|
|
@@ -996,6 +997,7 @@ class WindowItem(Namespaced, SelectGrain, BaseModel):
|
|
|
996
997
|
content: Concept
|
|
997
998
|
order_by: List["OrderItem"]
|
|
998
999
|
over: List["Concept"] = Field(default_factory=list)
|
|
1000
|
+
index: Optional[int] = None
|
|
999
1001
|
|
|
1000
1002
|
def with_namespace(self, namespace: str) -> "WindowItem":
|
|
1001
1003
|
return WindowItem(
|
|
@@ -2735,6 +2737,14 @@ class Comparison(ConceptArgs, Namespaced, SelectGrain, BaseModel):
|
|
|
2735
2737
|
raise SyntaxError(
|
|
2736
2738
|
f"Cannot compare {self.left} and {self.right} of different types"
|
|
2737
2739
|
)
|
|
2740
|
+
if self.operator == ComparisonOperator.BETWEEN:
|
|
2741
|
+
if (
|
|
2742
|
+
not isinstance(self.right, ComparisonOperator)
|
|
2743
|
+
and self.right.operator == BooleanOperator.AND
|
|
2744
|
+
):
|
|
2745
|
+
raise SyntaxError(
|
|
2746
|
+
f"Between operator must have two operands with and, not {self.right}"
|
|
2747
|
+
)
|
|
2738
2748
|
|
|
2739
2749
|
def __add__(self, other):
|
|
2740
2750
|
if not isinstance(other, (Comparison, Conditional, Parenthetical)):
|
|
@@ -2810,6 +2820,29 @@ class Comparison(ConceptArgs, Namespaced, SelectGrain, BaseModel):
|
|
|
2810
2820
|
output += get_concept_arguments(self.right)
|
|
2811
2821
|
return output
|
|
2812
2822
|
|
|
2823
|
+
@property
|
|
2824
|
+
def row_arguments(self) -> List[Concept]:
|
|
2825
|
+
output = []
|
|
2826
|
+
if isinstance(self.left, ConceptArgs):
|
|
2827
|
+
output += self.left.row_arguments
|
|
2828
|
+
else:
|
|
2829
|
+
output += get_concept_arguments(self.left)
|
|
2830
|
+
if isinstance(self.right, ConceptArgs):
|
|
2831
|
+
output += self.right.row_arguments
|
|
2832
|
+
else:
|
|
2833
|
+
output += get_concept_arguments(self.right)
|
|
2834
|
+
return output
|
|
2835
|
+
|
|
2836
|
+
@property
|
|
2837
|
+
def existence_arguments(self) -> List[Tuple[Concept, ...]]:
|
|
2838
|
+
"""Return concepts directly referenced in where clause"""
|
|
2839
|
+
output: List[Tuple[Concept, ...]] = []
|
|
2840
|
+
if isinstance(self.left, ConceptArgs):
|
|
2841
|
+
output += self.left.existence_arguments
|
|
2842
|
+
if isinstance(self.right, ConceptArgs):
|
|
2843
|
+
output += self.right.existence_arguments
|
|
2844
|
+
return output
|
|
2845
|
+
|
|
2813
2846
|
|
|
2814
2847
|
class SubselectComparison(Comparison):
|
|
2815
2848
|
|
|
@@ -2909,6 +2942,7 @@ class Conditional(ConceptArgs, Namespaced, SelectGrain, BaseModel):
|
|
|
2909
2942
|
float,
|
|
2910
2943
|
list,
|
|
2911
2944
|
bool,
|
|
2945
|
+
MagicConstants,
|
|
2912
2946
|
Concept,
|
|
2913
2947
|
Comparison,
|
|
2914
2948
|
"Conditional",
|
|
@@ -2922,11 +2956,13 @@ class Conditional(ConceptArgs, Namespaced, SelectGrain, BaseModel):
|
|
|
2922
2956
|
float,
|
|
2923
2957
|
list,
|
|
2924
2958
|
bool,
|
|
2959
|
+
MagicConstants,
|
|
2925
2960
|
Concept,
|
|
2926
2961
|
Comparison,
|
|
2927
2962
|
"Conditional",
|
|
2928
2963
|
"Parenthetical",
|
|
2929
2964
|
Function,
|
|
2965
|
+
FilterItem,
|
|
2930
2966
|
]
|
|
2931
2967
|
operator: BooleanOperator
|
|
2932
2968
|
|
|
@@ -10,6 +10,7 @@ from trilogy.core.enums import (
|
|
|
10
10
|
WindowType,
|
|
11
11
|
DatePart,
|
|
12
12
|
PurposeLineage,
|
|
13
|
+
ComparisonOperator,
|
|
13
14
|
)
|
|
14
15
|
from trilogy.core.models import (
|
|
15
16
|
ListType,
|
|
@@ -58,17 +59,23 @@ def INVALID_REFERENCE_STRING(x: Any, callsite: str = ""):
|
|
|
58
59
|
|
|
59
60
|
|
|
60
61
|
def window_factory(string: str, include_concept: bool = False) -> Callable:
|
|
61
|
-
def render_window(
|
|
62
|
+
def render_window(
|
|
63
|
+
concept: str, window: str, sort: str, offset: int | None = None
|
|
64
|
+
) -> str:
|
|
62
65
|
if not include_concept:
|
|
63
66
|
concept = ""
|
|
67
|
+
if offset:
|
|
68
|
+
base = f"{string}({concept}, {offset})"
|
|
69
|
+
else:
|
|
70
|
+
base = f"{string}({concept})"
|
|
64
71
|
if window and sort:
|
|
65
|
-
return f"{
|
|
72
|
+
return f"{base} over (partition by {window} order by {sort} )"
|
|
66
73
|
elif window:
|
|
67
|
-
return f"{
|
|
74
|
+
return f"{base} over (partition by {window})"
|
|
68
75
|
elif sort:
|
|
69
|
-
return f"{
|
|
76
|
+
return f"{base} over (order by {sort} )"
|
|
70
77
|
else:
|
|
71
|
-
return f"{
|
|
78
|
+
return f"{base} over ()"
|
|
72
79
|
|
|
73
80
|
return render_window
|
|
74
81
|
|
|
@@ -109,10 +116,10 @@ FUNCTION_MAP = {
|
|
|
109
116
|
FunctionType.INDEX_ACCESS: lambda x: f"{x[0]}[{x[1]}]",
|
|
110
117
|
FunctionType.UNNEST: lambda x: f"unnest({x[0]})",
|
|
111
118
|
# math
|
|
112
|
-
FunctionType.ADD: lambda x: f"
|
|
113
|
-
FunctionType.SUBTRACT: lambda x: f"
|
|
114
|
-
FunctionType.DIVIDE: lambda x: f"
|
|
115
|
-
FunctionType.MULTIPLY: lambda x: f"
|
|
119
|
+
FunctionType.ADD: lambda x: f"{x[0]} + {x[1]}",
|
|
120
|
+
FunctionType.SUBTRACT: lambda x: f"{x[0]} - {x[1]}",
|
|
121
|
+
FunctionType.DIVIDE: lambda x: f"{x[0]} / {x[1]}",
|
|
122
|
+
FunctionType.MULTIPLY: lambda x: f"{x[0]} * {x[1]}",
|
|
116
123
|
FunctionType.ROUND: lambda x: f"round({x[0]},{x[1]})",
|
|
117
124
|
FunctionType.MOD: lambda x: f"({x[0]} % {x[1]})",
|
|
118
125
|
# aggregate types
|
|
@@ -355,17 +362,30 @@ class BaseDialect:
|
|
|
355
362
|
lookup = lookup_cte.existence_source_map[e.right.address]
|
|
356
363
|
|
|
357
364
|
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value} (select {lookup[0]}.{self.QUOTE_CHARACTER}{e.right.safe_address}{self.QUOTE_CHARACTER} from {lookup[0]})"
|
|
358
|
-
elif isinstance(e.right, (ListWrapper, Parenthetical)):
|
|
365
|
+
elif isinstance(e.right, (ListWrapper, Parenthetical, list)):
|
|
359
366
|
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map)}"
|
|
360
|
-
|
|
367
|
+
|
|
368
|
+
elif isinstance(
|
|
369
|
+
e.right,
|
|
370
|
+
(
|
|
371
|
+
str,
|
|
372
|
+
int,
|
|
373
|
+
bool,
|
|
374
|
+
float,
|
|
375
|
+
),
|
|
376
|
+
):
|
|
361
377
|
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value} ({self.render_expr(e.right, cte=cte, cte_map=cte_map)})"
|
|
362
378
|
else:
|
|
363
|
-
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value}
|
|
379
|
+
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map)}"
|
|
364
380
|
elif isinstance(e, Comparison):
|
|
381
|
+
if e.operator == ComparisonOperator.BETWEEN:
|
|
382
|
+
right_comp = e.right
|
|
383
|
+
assert isinstance(right_comp, Conditional)
|
|
384
|
+
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value} {self.render_expr(right_comp.left, cte=cte, cte_map=cte_map) and self.render_expr(right_comp.right, cte=cte, cte_map=cte_map)}"
|
|
365
385
|
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map)}"
|
|
366
386
|
elif isinstance(e, Conditional):
|
|
367
387
|
# conditions need to be nested in parentheses
|
|
368
|
-
return f"
|
|
388
|
+
return f"{self.render_expr(e.left, cte=cte, cte_map=cte_map)} {e.operator.value} {self.render_expr(e.right, cte=cte, cte_map=cte_map)}"
|
|
369
389
|
elif isinstance(e, WindowItem):
|
|
370
390
|
rendered_order_components = [
|
|
371
391
|
f"{self.render_expr(x.expr, cte, cte_map=cte_map)} {x.order.value}"
|
|
@@ -375,11 +395,11 @@ class BaseDialect:
|
|
|
375
395
|
self.render_expr(x, cte, cte_map=cte_map) for x in e.over
|
|
376
396
|
]
|
|
377
397
|
return f"{self.WINDOW_FUNCTION_MAP[e.type](concept = self.render_expr(e.content, cte=cte, cte_map=cte_map), window=','.join(rendered_over_components), sort=','.join(rendered_order_components))}" # noqa: E501
|
|
378
|
-
elif isinstance(e, FilterItem):
|
|
379
|
-
return f"CASE WHEN {self.render_expr(e.where.conditional, cte=cte, cte_map=cte_map)} THEN {self.render_expr(e.content, cte=cte, cte_map=cte_map)} ELSE 0 END"
|
|
380
398
|
elif isinstance(e, Parenthetical):
|
|
381
399
|
# conditions need to be nested in parentheses
|
|
382
|
-
|
|
400
|
+
if isinstance(e.content, list):
|
|
401
|
+
return f"( {','.join([self.render_expr(x, cte=cte, cte_map=cte_map) for x in e.content])} )"
|
|
402
|
+
return f"( {self.render_expr(e.content, cte=cte, cte_map=cte_map)} )"
|
|
383
403
|
elif isinstance(e, CaseWhen):
|
|
384
404
|
return f"WHEN {self.render_expr(e.comparison, cte=cte, cte_map=cte_map) } THEN {self.render_expr(e.expr, cte=cte, cte_map=cte_map) }"
|
|
385
405
|
elif isinstance(e, CaseElse):
|
|
@@ -412,7 +432,7 @@ class BaseDialect:
|
|
|
412
432
|
elif isinstance(e, ListWrapper):
|
|
413
433
|
return f"[{','.join([self.render_expr(x, cte=cte, cte_map=cte_map) for x in e])}]"
|
|
414
434
|
elif isinstance(e, list):
|
|
415
|
-
return f"{','.join([self.render_expr(x, cte=cte, cte_map=cte_map) for x in e])}"
|
|
435
|
+
return f"[{','.join([self.render_expr(x, cte=cte, cte_map=cte_map) for x in e])}]"
|
|
416
436
|
elif isinstance(e, DataType):
|
|
417
437
|
return str(e.value)
|
|
418
438
|
elif isinstance(e, DatePart):
|
|
@@ -41,7 +41,7 @@ def concept_list_to_keys(concepts: Tuple[Concept, ...]) -> Tuple[Concept, ...]:
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
def constant_to_concept(
|
|
44
|
-
parent: ListWrapper | int | float | str,
|
|
44
|
+
parent: ListWrapper | list | int | float | str,
|
|
45
45
|
name: str,
|
|
46
46
|
namespace: str,
|
|
47
47
|
purpose: Purpose | None = None,
|
|
@@ -114,298 +114,17 @@ from trilogy.parsing.common import (
|
|
|
114
114
|
arbitrary_to_concept,
|
|
115
115
|
)
|
|
116
116
|
|
|
117
|
+
CONSTANT_TYPES = (int, float, str, bool, list, ListWrapper)
|
|
117
118
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
| select_statement
|
|
128
|
-
| persist_statement
|
|
129
|
-
| rowset_derivation_statement
|
|
130
|
-
| import_statement
|
|
131
|
-
| merge_statement
|
|
132
|
-
|
|
133
|
-
_TERMINATOR: ";"i /\s*/
|
|
134
|
-
|
|
135
|
-
comment: /#.*(\n|$)/ | /\/\/.*\n/
|
|
136
|
-
|
|
137
|
-
// property display_name string
|
|
138
|
-
concept_declaration: PURPOSE IDENTIFIER data_type concept_nullable_modifier? metadata?
|
|
139
|
-
//customer_id.property first_name STRING;
|
|
140
|
-
//<customer_id,country>.property local_alias STRING
|
|
141
|
-
concept_property_declaration: PROPERTY (prop_ident | IDENTIFIER) data_type concept_nullable_modifier? metadata?
|
|
142
|
-
//metric post_length <- len(post_text);
|
|
143
|
-
concept_derivation: (PURPOSE | AUTO | PROPERTY ) (prop_ident | IDENTIFIER) "<" "-" expr
|
|
144
|
-
|
|
145
|
-
rowset_derivation_statement: ("rowset"i IDENTIFIER "<" "-" (multi_select_statement | select_statement)) | ("with"i IDENTIFIER "as"i (multi_select_statement | select_statement))
|
|
146
|
-
|
|
147
|
-
constant_derivation: CONST IDENTIFIER "<" "-" literal
|
|
148
|
-
concept_nullable_modifier: "?"
|
|
149
|
-
concept: (concept_declaration | concept_derivation | concept_property_declaration | constant_derivation)
|
|
150
|
-
|
|
151
|
-
//concept property
|
|
152
|
-
prop_ident: "<" (IDENTIFIER ",")* IDENTIFIER ","? ">" "." IDENTIFIER
|
|
153
|
-
|
|
154
|
-
// datasource concepts
|
|
155
|
-
datasource: "datasource" IDENTIFIER "(" column_assignment_list ")" grain_clause? (address | query)
|
|
156
|
-
|
|
157
|
-
grain_clause: "grain" "(" column_list ")"
|
|
158
|
-
|
|
159
|
-
address: "address" ADDRESS
|
|
160
|
-
|
|
161
|
-
query: "query" MULTILINE_STRING
|
|
162
|
-
|
|
163
|
-
concept_assignment: IDENTIFIER | (MODIFIER "[" concept_assignment "]" ) | (SHORTHAND_MODIFIER concept_assignment )
|
|
164
|
-
|
|
165
|
-
column_assignment: ((IDENTIFIER | raw_column_assignment | _static_functions ) ":" concept_assignment)
|
|
166
|
-
|
|
167
|
-
raw_column_assignment: "raw" "(" MULTILINE_STRING ")"
|
|
168
|
-
|
|
169
|
-
column_assignment_list : (column_assignment "," )* column_assignment ","?
|
|
170
|
-
|
|
171
|
-
column_list : (IDENTIFIER "," )* IDENTIFIER ","?
|
|
172
|
-
|
|
173
|
-
import_statement: "import" (IDENTIFIER ".") * IDENTIFIER "as" IDENTIFIER
|
|
174
|
-
|
|
175
|
-
// persist_statement
|
|
176
|
-
persist_statement: "persist"i IDENTIFIER "into"i IDENTIFIER "from"i select_statement grain_clause?
|
|
177
|
-
|
|
178
|
-
// select statement
|
|
179
|
-
select_statement: "select"i select_list where? comment* order_by? comment* limit? comment*
|
|
180
|
-
|
|
181
|
-
// multiple_selects
|
|
182
|
-
multi_select_statement: select_statement ("merge" select_statement)+ "align"i align_clause where? comment* order_by? comment* limit? comment*
|
|
183
|
-
|
|
184
|
-
align_item: IDENTIFIER ":" IDENTIFIER ("," IDENTIFIER)* ","?
|
|
185
|
-
|
|
186
|
-
align_clause: align_item ("," align_item)* ","?
|
|
187
|
-
|
|
188
|
-
// merge statemment
|
|
189
|
-
merge_statement: "merge" IDENTIFIER ("," IDENTIFIER)* ","? comment*
|
|
190
|
-
|
|
191
|
-
// FUNCTION blocks
|
|
192
|
-
function: raw_function
|
|
193
|
-
function_binding_item: IDENTIFIER ":" data_type
|
|
194
|
-
function_binding_list: (function_binding_item ",")* function_binding_item ","?
|
|
195
|
-
raw_function: "bind" "sql" IDENTIFIER "(" function_binding_list ")" "-" ">" data_type "as"i MULTILINE_STRING
|
|
196
|
-
|
|
197
|
-
// user_id where state = Mexico
|
|
198
|
-
filter_item: "filter"i IDENTIFIER where
|
|
199
|
-
|
|
200
|
-
// rank/lag/lead
|
|
201
|
-
WINDOW_TYPE: ("row_number"i|"rank"i|"lag"i|"lead"i | "sum"i) /[\s]+/
|
|
202
|
-
|
|
203
|
-
window_item: WINDOW_TYPE (IDENTIFIER | select_transform | comment+ ) window_item_over? window_item_order?
|
|
204
|
-
|
|
205
|
-
window_item_over: ("OVER"i over_list)
|
|
206
|
-
|
|
207
|
-
window_item_order: ("ORDER"i? "BY"i order_list)
|
|
208
|
-
|
|
209
|
-
select_hide_modifier: "--"
|
|
210
|
-
select_partial_modifier: "~"
|
|
211
|
-
select_item: (select_hide_modifier | select_partial_modifier)? (IDENTIFIER | select_transform | comment+ )
|
|
212
|
-
|
|
213
|
-
select_list: ( select_item "," )* select_item ","?
|
|
214
|
-
|
|
215
|
-
// count(post_id) -> post_count
|
|
216
|
-
_assignment: ("-" ">") | "as"
|
|
217
|
-
select_transform : expr _assignment IDENTIFIER metadata?
|
|
218
|
-
|
|
219
|
-
metadata: "metadata" "(" IDENTIFIER "=" _string_lit ")"
|
|
220
|
-
|
|
221
|
-
limit: "LIMIT"i /[0-9]+/
|
|
222
|
-
|
|
223
|
-
!window_order: ("TOP"i | "BOTTOM"i)
|
|
224
|
-
|
|
225
|
-
window: window_order /[0-9]+/
|
|
226
|
-
|
|
227
|
-
window_order_by: "BY"i column_list
|
|
228
|
-
|
|
229
|
-
order_list: (expr ORDERING "," )* expr ORDERING ","?
|
|
230
|
-
|
|
231
|
-
over_list: (IDENTIFIER "," )* IDENTIFIER ","?
|
|
232
|
-
|
|
233
|
-
ORDERING: ("ASC"i | "DESC"i)
|
|
234
|
-
|
|
235
|
-
order_by: "ORDER"i "BY"i order_list
|
|
236
|
-
|
|
237
|
-
//WHERE STATEMENT
|
|
238
|
-
|
|
239
|
-
LOGICAL_OPERATOR: "AND"i | "OR"i
|
|
240
|
-
|
|
241
|
-
conditional: expr LOGICAL_OPERATOR (conditional | expr)
|
|
242
|
-
|
|
243
|
-
where: "WHERE"i (expr | conditional)
|
|
244
|
-
|
|
245
|
-
expr_reference: IDENTIFIER
|
|
246
|
-
|
|
247
|
-
!array_comparison: ( ("NOT"i "IN"i) | "IN"i)
|
|
248
|
-
|
|
249
|
-
COMPARISON_OPERATOR: (/is[\s]+not/ | "is" |"=" | ">" | "<" | ">=" | "<=" | "!=" )
|
|
250
|
-
|
|
251
|
-
comparison: (expr COMPARISON_OPERATOR expr)
|
|
252
|
-
|
|
253
|
-
between_comparison: expr "BETWEEN"i expr "AND"i expr
|
|
254
|
-
|
|
255
|
-
subselect_comparison: expr array_comparison expr | (expr array_comparison expr_tuple)
|
|
256
|
-
|
|
257
|
-
expr_tuple: "(" (expr ",")* expr ","? ")"
|
|
258
|
-
|
|
259
|
-
//unnesting is a function
|
|
260
|
-
unnest: "UNNEST"i "(" expr ")"
|
|
261
|
-
//indexing into an expression is a function
|
|
262
|
-
index_access: expr "[" int_lit "]"
|
|
263
|
-
attr_access: expr "[" _string_lit "]"
|
|
264
|
-
|
|
265
|
-
parenthetical: "(" (conditional | expr) ")"
|
|
266
|
-
|
|
267
|
-
expr: window_item | filter_item | between_comparison | comparison | subselect_comparison | fgroup | aggregate_functions | unnest | _string_functions | _math_functions | _generic_functions | _constant_functions| _date_functions | literal | expr_reference | index_access | attr_access | parenthetical
|
|
268
|
-
|
|
269
|
-
// functions
|
|
270
|
-
|
|
271
|
-
fadd: ("add"i "(" expr "," expr ")" ) | ( expr "+" expr )
|
|
272
|
-
fsub: ("subtract"i "(" expr "," expr ")" ) | ( expr "-" expr )
|
|
273
|
-
fmul: ("multiply"i "(" expr "," expr ")" ) | ( expr "*" expr )
|
|
274
|
-
fdiv: ( "divide"i "(" expr "," expr ")") | ( expr "/" expr )
|
|
275
|
-
fmod: ( "mod"i "(" expr "," expr ")") | ( expr "%" expr )
|
|
276
|
-
fround: "round"i "(" expr "," expr ")"
|
|
277
|
-
fabs: "abs"i "(" expr ")"
|
|
278
|
-
|
|
279
|
-
_math_functions: fadd | fsub | fmul | fdiv | fround | fmod | fabs
|
|
280
|
-
|
|
281
|
-
//generic
|
|
282
|
-
fcast: "cast"i "(" expr "AS"i data_type ")"
|
|
283
|
-
concat: ("concat"i "(" (expr ",")* expr ")") | (expr "||" expr)
|
|
284
|
-
fcoalesce: "coalesce"i "(" (expr ",")* expr ")"
|
|
285
|
-
fcase_when: "WHEN"i (expr | conditional) "THEN"i expr
|
|
286
|
-
fcase_else: "ELSE"i expr
|
|
287
|
-
fcase: "CASE"i (fcase_when)* (fcase_else)? "END"i
|
|
288
|
-
len: "len"i "(" expr ")"
|
|
289
|
-
fnot: "NOT"i expr
|
|
290
|
-
|
|
291
|
-
_generic_functions: fcast | concat | fcoalesce | fcase | len | fnot
|
|
292
|
-
|
|
293
|
-
//constant
|
|
294
|
-
fcurrent_date: "current_date"i "(" ")"
|
|
295
|
-
fcurrent_datetime: "current_datetime"i "(" ")"
|
|
296
|
-
|
|
297
|
-
_constant_functions: fcurrent_date | fcurrent_datetime
|
|
298
|
-
|
|
299
|
-
//string
|
|
300
|
-
like: "like"i "(" expr "," _string_lit ")"
|
|
301
|
-
ilike: "ilike"i "(" expr "," _string_lit ")"
|
|
302
|
-
alt_like: expr "like"i expr
|
|
303
|
-
upper: "upper"i "(" expr ")"
|
|
304
|
-
lower: "lower"i "(" expr ")"
|
|
305
|
-
fsplit: "split"i "(" expr "," _string_lit ")"
|
|
306
|
-
fstrpos: "strpos"i "(" expr "," expr ")"
|
|
307
|
-
fsubstring: "substring"i "(" expr "," expr "," expr ")"
|
|
308
|
-
|
|
309
|
-
_string_functions: like | ilike | upper | lower | fsplit | fstrpos | fsubstring | alt_like
|
|
310
|
-
|
|
311
|
-
// special aggregate
|
|
312
|
-
fgroup: "group"i "(" expr ")" aggregate_over?
|
|
313
|
-
//aggregates
|
|
314
|
-
count: "count"i "(" expr ")"
|
|
315
|
-
count_distinct: "count_distinct"i "(" expr ")"
|
|
316
|
-
sum: "sum"i "(" expr ")"
|
|
317
|
-
avg: "avg"i "(" expr ")"
|
|
318
|
-
max: "max"i "(" expr ")"
|
|
319
|
-
min: "min"i "(" expr ")"
|
|
320
|
-
|
|
321
|
-
//aggregates can force a grain
|
|
322
|
-
aggregate_all: "*"
|
|
323
|
-
aggregate_over: ("BY"i (aggregate_all | over_list))
|
|
324
|
-
aggregate_functions: (count | count_distinct | sum | avg | max | min) aggregate_over?
|
|
325
|
-
|
|
326
|
-
// date functions
|
|
327
|
-
fdate: "date"i "(" expr ")"
|
|
328
|
-
fdatetime: "datetime"i "(" expr ")"
|
|
329
|
-
ftimestamp: "timestamp"i "(" expr ")"
|
|
330
|
-
|
|
331
|
-
fsecond: "second"i "(" expr ")"
|
|
332
|
-
fminute: "minute"i "(" expr ")"
|
|
333
|
-
fhour: "hour"i "(" expr ")"
|
|
334
|
-
fday: "day"i "(" expr ")"
|
|
335
|
-
fday_of_week: "day_of_week"i "(" expr ")"
|
|
336
|
-
fweek: "week"i "(" expr ")"
|
|
337
|
-
fmonth: "month"i "(" expr ")"
|
|
338
|
-
fquarter: "quarter"i "(" expr ")"
|
|
339
|
-
fyear: "year"i "(" expr ")"
|
|
340
|
-
|
|
341
|
-
DATE_PART: "DAY"i | "WEEK"i | "MONTH"i | "QUARTER"i | "YEAR"i | "MINUTE"i | "HOUR"i | "SECOND"i
|
|
342
|
-
fdate_trunc: "date_trunc"i "(" expr "," DATE_PART ")"
|
|
343
|
-
fdate_part: "date_part"i "(" expr "," DATE_PART ")"
|
|
344
|
-
fdate_add: "date_add"i "(" expr "," DATE_PART "," int_lit ")"
|
|
345
|
-
fdate_diff: "date_diff"i "(" expr "," expr "," DATE_PART ")"
|
|
346
|
-
|
|
347
|
-
_date_functions: fdate | fdate_add | fdate_diff | fdatetime | ftimestamp | fsecond | fminute | fhour | fday | fday_of_week | fweek | fmonth | fquarter | fyear | fdate_part | fdate_trunc
|
|
348
|
-
|
|
349
|
-
_static_functions: _string_functions | _math_functions | _generic_functions | _constant_functions| _date_functions
|
|
350
|
-
|
|
351
|
-
// base language constructs
|
|
352
|
-
IDENTIFIER: /[a-zA-Z_][a-zA-Z0-9_\\-\\.\-]*/
|
|
353
|
-
ADDRESS: /[a-zA-Z_][a-zA-Z0-9_\\-\\.\-\*]*/ | /`[a-zA-Z_][a-zA-Z0-9_\\-\\.\-\*]*`/
|
|
354
|
-
|
|
355
|
-
MULTILINE_STRING: /\'{3}(.*?)\'{3}/s
|
|
356
|
-
|
|
357
|
-
DOUBLE_STRING_CHARS: /(?:(?!\${)([^"\\]|\\.))+/+ // any character except "
|
|
358
|
-
SINGLE_STRING_CHARS: /(?:(?!\${)([^'\\]|\\.))+/+ // any character except '
|
|
359
|
-
_single_quote: "'" ( SINGLE_STRING_CHARS )* "'"
|
|
360
|
-
_double_quote: "\"" ( DOUBLE_STRING_CHARS )* "\""
|
|
361
|
-
_string_lit: _single_quote | _double_quote
|
|
362
|
-
|
|
363
|
-
MINUS: "-"
|
|
364
|
-
|
|
365
|
-
int_lit: MINUS? /[0-9]+/
|
|
366
|
-
|
|
367
|
-
float_lit: /[0-9]*\.[0-9]+/
|
|
368
|
-
|
|
369
|
-
array_lit: "[" (literal ",")* literal ","? "]"()
|
|
370
|
-
|
|
371
|
-
!bool_lit: "True"i | "False"i
|
|
372
|
-
|
|
373
|
-
!null_lit: "null"i
|
|
374
|
-
|
|
375
|
-
literal: _string_lit | int_lit | float_lit | bool_lit | null_lit | array_lit
|
|
376
|
-
|
|
377
|
-
MODIFIER: "Optional"i | "Partial"i | "Nullable"i
|
|
378
|
-
|
|
379
|
-
SHORTHAND_MODIFIER: "~"
|
|
380
|
-
|
|
381
|
-
struct_type: "struct" "<" ((data_type | IDENTIFIER) ",")* (data_type | IDENTIFIER) ","? ">"
|
|
382
|
-
|
|
383
|
-
list_type: "list" "<" data_type ">"
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
!data_type: "string"i | "number"i | "numeric"i | "map"i | "list"i | "array"i | "any"i | "int"i | "bigint" | "date"i | "datetime"i | "timestamp"i | "float"i | "bool"i | struct_type | list_type
|
|
387
|
-
|
|
388
|
-
PURPOSE: "key"i | "metric"i | "const"i | "constant"i
|
|
389
|
-
PROPERTY: "property"i
|
|
390
|
-
CONST: "const"i | "constant"i
|
|
391
|
-
AUTO: "AUTO"i
|
|
392
|
-
|
|
393
|
-
// meta functions
|
|
394
|
-
CONCEPTS: "CONCEPTS"i
|
|
395
|
-
DATASOURCES: "DATASOURCES"i
|
|
396
|
-
|
|
397
|
-
show_category: CONCEPTS | DATASOURCES
|
|
398
|
-
|
|
399
|
-
show_statement: "show"i ( show_category | select_statement | persist_statement) _TERMINATOR
|
|
400
|
-
|
|
401
|
-
%import common.WS_INLINE -> _WHITESPACE
|
|
402
|
-
%import common.WS
|
|
403
|
-
%ignore WS
|
|
404
|
-
""" # noqa: E501
|
|
405
|
-
|
|
406
|
-
PARSER = Lark(
|
|
407
|
-
grammar, start="start", propagate_positions=True, g_regex_flags=IGNORECASE
|
|
408
|
-
)
|
|
119
|
+
with open(join(dirname(__file__), "trilogy.lark"), "r") as f:
|
|
120
|
+
PARSER = Lark(
|
|
121
|
+
f.read(),
|
|
122
|
+
start="start",
|
|
123
|
+
propagate_positions=True,
|
|
124
|
+
g_regex_flags=IGNORECASE,
|
|
125
|
+
parser="lalr",
|
|
126
|
+
cache=True,
|
|
127
|
+
)
|
|
409
128
|
|
|
410
129
|
|
|
411
130
|
def parse_concept_reference(
|
|
@@ -569,6 +288,9 @@ class ParseToObjects(Transformer):
|
|
|
569
288
|
def IDENTIFIER(self, args) -> str:
|
|
570
289
|
return args.value
|
|
571
290
|
|
|
291
|
+
def concept_lit(self, args) -> Concept:
|
|
292
|
+
return self.environment.concepts.__getitem__(args[0])
|
|
293
|
+
|
|
572
294
|
def ADDRESS(self, args) -> str:
|
|
573
295
|
return args.value
|
|
574
296
|
|
|
@@ -624,19 +346,23 @@ class ParseToObjects(Transformer):
|
|
|
624
346
|
def column_assignment(self, meta: Meta, args):
|
|
625
347
|
# TODO -> deal with conceptual modifiers
|
|
626
348
|
modifiers = []
|
|
627
|
-
|
|
349
|
+
alias = args[0]
|
|
350
|
+
concept_list = args[1]
|
|
628
351
|
# recursively collect modifiers
|
|
629
|
-
|
|
630
|
-
modifiers
|
|
631
|
-
|
|
352
|
+
if len(concept_list) > 1:
|
|
353
|
+
modifiers += concept_list[:-1]
|
|
354
|
+
concept = concept_list[-1]
|
|
632
355
|
resolved = self.environment.concepts.__getitem__( # type: ignore
|
|
633
|
-
key=concept
|
|
356
|
+
key=concept, line_no=meta.line
|
|
634
357
|
)
|
|
635
|
-
return ColumnAssignment(alias=
|
|
358
|
+
return ColumnAssignment(alias=alias, modifiers=modifiers, concept=resolved)
|
|
636
359
|
|
|
637
360
|
def _TERMINATOR(self, args):
|
|
638
361
|
return None
|
|
639
362
|
|
|
363
|
+
def _static_functions(self, args):
|
|
364
|
+
return args[0]
|
|
365
|
+
|
|
640
366
|
def MODIFIER(self, args) -> Modifier:
|
|
641
367
|
return Modifier(args.value)
|
|
642
368
|
|
|
@@ -895,6 +621,9 @@ class ParseToObjects(Transformer):
|
|
|
895
621
|
assert len(args) == 1
|
|
896
622
|
return Comment(text=args[0].value)
|
|
897
623
|
|
|
624
|
+
def PARSE_COMMENT(self, args):
|
|
625
|
+
return Comment(text=args.value)
|
|
626
|
+
|
|
898
627
|
@v_args(meta=True)
|
|
899
628
|
def select_transform(self, meta, args) -> ConceptTransform:
|
|
900
629
|
|
|
@@ -964,7 +693,7 @@ class ParseToObjects(Transformer):
|
|
|
964
693
|
if isinstance(content, ConceptTransform):
|
|
965
694
|
return SelectItem(content=content, modifiers=modifiers)
|
|
966
695
|
return SelectItem(
|
|
967
|
-
content=
|
|
696
|
+
content=content,
|
|
968
697
|
modifiers=modifiers,
|
|
969
698
|
)
|
|
970
699
|
|
|
@@ -974,8 +703,12 @@ class ParseToObjects(Transformer):
|
|
|
974
703
|
def limit(self, args):
|
|
975
704
|
return Limit(count=int(args[0].value))
|
|
976
705
|
|
|
977
|
-
def
|
|
978
|
-
|
|
706
|
+
def ordering(self, args: list[str]):
|
|
707
|
+
base = args[0].lower()
|
|
708
|
+
if len(args) > 1:
|
|
709
|
+
null_sort = args[-1]
|
|
710
|
+
return Ordering(" ".join([base, "nulls", null_sort.lower()]))
|
|
711
|
+
return Ordering(base)
|
|
979
712
|
|
|
980
713
|
def order_list(self, args):
|
|
981
714
|
|
|
@@ -1003,7 +736,7 @@ class ParseToObjects(Transformer):
|
|
|
1003
736
|
return OrderBy(items=args[0])
|
|
1004
737
|
|
|
1005
738
|
def over_list(self, args):
|
|
1006
|
-
return [
|
|
739
|
+
return [x for x in args]
|
|
1007
740
|
|
|
1008
741
|
@v_args(meta=True)
|
|
1009
742
|
def merge_statement(self, meta: Meta, args) -> MergeStatement:
|
|
@@ -1046,9 +779,7 @@ class ParseToObjects(Transformer):
|
|
|
1046
779
|
# add the parsed objects of the import in
|
|
1047
780
|
self.parsed = {**self.parsed, **nparser.parsed}
|
|
1048
781
|
except Exception as e:
|
|
1049
|
-
raise ImportError(
|
|
1050
|
-
f"Unable to import file {dirname(target)}, parsing error: {e}"
|
|
1051
|
-
)
|
|
782
|
+
raise ImportError(f"Unable to import file {target}, parsing error: {e}")
|
|
1052
783
|
|
|
1053
784
|
for _, concept in nparser.environment.concepts.items():
|
|
1054
785
|
self.environment.add_concept(concept.with_namespace(alias))
|
|
@@ -1312,13 +1043,23 @@ class ParseToObjects(Transformer):
|
|
|
1312
1043
|
type = args[0]
|
|
1313
1044
|
order_by = []
|
|
1314
1045
|
over = []
|
|
1315
|
-
|
|
1316
|
-
|
|
1046
|
+
index = None
|
|
1047
|
+
concept: Concept | None = None
|
|
1048
|
+
for item in args:
|
|
1049
|
+
if isinstance(item, int):
|
|
1050
|
+
index = item
|
|
1051
|
+
elif isinstance(item, WindowItemOrder):
|
|
1317
1052
|
order_by = item.contents
|
|
1318
1053
|
elif isinstance(item, WindowItemOver):
|
|
1319
1054
|
over = item.contents
|
|
1320
|
-
|
|
1321
|
-
|
|
1055
|
+
elif isinstance(item, str):
|
|
1056
|
+
concept = self.environment.concepts[item]
|
|
1057
|
+
elif isinstance(item, Concept):
|
|
1058
|
+
concept = item
|
|
1059
|
+
assert concept
|
|
1060
|
+
return WindowItem(
|
|
1061
|
+
type=type, content=concept, over=over, order_by=order_by, index=index
|
|
1062
|
+
)
|
|
1322
1063
|
|
|
1323
1064
|
def filter_item(self, args) -> FilterItem:
|
|
1324
1065
|
where: WhereClause
|
|
@@ -1516,6 +1257,9 @@ class ParseToObjects(Transformer):
|
|
|
1516
1257
|
)
|
|
1517
1258
|
return SubString(args)
|
|
1518
1259
|
|
|
1260
|
+
def logical_operator(self, args):
|
|
1261
|
+
return BooleanOperator(args[0].value.lower())
|
|
1262
|
+
|
|
1519
1263
|
@v_args(meta=True)
|
|
1520
1264
|
def lower(self, meta, args):
|
|
1521
1265
|
args = self.process_function_args(args, meta=meta)
|
|
@@ -1908,12 +1652,12 @@ class ParseToObjects(Transformer):
|
|
|
1908
1652
|
@v_args(meta=True)
|
|
1909
1653
|
def fcurrent_date(self, meta, args):
|
|
1910
1654
|
args = self.process_function_args(args, meta=meta)
|
|
1911
|
-
return CurrentDate(
|
|
1655
|
+
return CurrentDate([])
|
|
1912
1656
|
|
|
1913
1657
|
@v_args(meta=True)
|
|
1914
1658
|
def fcurrent_datetime(self, meta, args):
|
|
1915
1659
|
args = self.process_function_args(args, meta=meta)
|
|
1916
|
-
return CurrentDatetime(
|
|
1660
|
+
return CurrentDatetime([])
|
|
1917
1661
|
|
|
1918
1662
|
@v_args(meta=True)
|
|
1919
1663
|
def fnot(self, meta, args):
|
|
@@ -1929,10 +1673,14 @@ def unpack_visit_error(e: VisitError):
|
|
|
1929
1673
|
elif isinstance(e.orig_exc, (UndefinedConceptException, ImportError)):
|
|
1930
1674
|
raise e.orig_exc
|
|
1931
1675
|
elif isinstance(e.orig_exc, (ValidationError, TypeError)):
|
|
1932
|
-
raise InvalidSyntaxException(str(e.orig_exc))
|
|
1676
|
+
raise InvalidSyntaxException(str(e.orig_exc) + str(e.rule) + str(e.obj))
|
|
1933
1677
|
raise e
|
|
1934
1678
|
|
|
1935
1679
|
|
|
1680
|
+
def parse_text_raw(text: str, environment: Optional[Environment] = None):
|
|
1681
|
+
PARSER.parse(text)
|
|
1682
|
+
|
|
1683
|
+
|
|
1936
1684
|
def parse_text(text: str, environment: Optional[Environment] = None) -> Tuple[
|
|
1937
1685
|
Environment,
|
|
1938
1686
|
List[
|
|
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
|
|
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
|
|
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
|
{pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/concept_strategies_v3.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/__init__.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/basic_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/common.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/filter_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/group_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/group_to_node.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/rowset_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/select_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/unnest_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.1.112 → pytrilogy-0.0.1.113}/trilogy/core/processing/node_generators/window_node.py
RENAMED
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|