pytrilogy 0.0.3.43__py3-none-any.whl → 0.0.3.44__py3-none-any.whl
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.3.43.dist-info → pytrilogy-0.0.3.44.dist-info}/METADATA +1 -1
- {pytrilogy-0.0.3.43.dist-info → pytrilogy-0.0.3.44.dist-info}/RECORD +14 -14
- {pytrilogy-0.0.3.43.dist-info → pytrilogy-0.0.3.44.dist-info}/WHEEL +1 -1
- trilogy/__init__.py +1 -1
- trilogy/core/models/author.py +12 -14
- trilogy/core/models/build.py +52 -47
- trilogy/core/processing/node_generators/common.py +1 -1
- trilogy/core/processing/node_generators/window_node.py +10 -4
- trilogy/parsing/common.py +40 -25
- trilogy/parsing/parse_engine.py +83 -13
- trilogy/parsing/trilogy.lark +44 -34
- {pytrilogy-0.0.3.43.dist-info → pytrilogy-0.0.3.44.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.43.dist-info → pytrilogy-0.0.3.44.dist-info}/licenses/LICENSE.md +0 -0
- {pytrilogy-0.0.3.43.dist-info → pytrilogy-0.0.3.44.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
pytrilogy-0.0.3.
|
|
2
|
-
trilogy/__init__.py,sha256=
|
|
1
|
+
pytrilogy-0.0.3.44.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
2
|
+
trilogy/__init__.py,sha256=_DLr4-giprU_EZXs7LGyoeoIt8LaSs4wYqbdAajaVr4,303
|
|
3
3
|
trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
trilogy/constants.py,sha256=5eQxk1A0pv-TQk3CCvgZCFA9_K-6nxrOm7E5Lxd7KIY,1652
|
|
5
5
|
trilogy/engine.py,sha256=OK2RuqCIUId6yZ5hfF8J1nxGP0AJqHRZiafcowmW0xc,1728
|
|
@@ -22,8 +22,8 @@ trilogy/core/internal.py,sha256=iicDBlC6nM8d7e7jqzf_ZOmpUsW8yrr2AA8AqEiLx-s,1577
|
|
|
22
22
|
trilogy/core/optimization.py,sha256=aihzx4-2-mSjx5td1TDTYGvc7e9Zvy-_xEyhPqLS-Ig,8314
|
|
23
23
|
trilogy/core/query_processor.py,sha256=Vl-u0F0rbqI2liv82yJgiZCB255Kx_KiuzZVHL6aeTM,19459
|
|
24
24
|
trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
-
trilogy/core/models/author.py,sha256=
|
|
26
|
-
trilogy/core/models/build.py,sha256=
|
|
25
|
+
trilogy/core/models/author.py,sha256=7lPUVm1uCXYsyV85p34AvtLLzHqDGCeesTUT8ZHCwo4,76859
|
|
26
|
+
trilogy/core/models/build.py,sha256=EsI7BLmFXdxj1an3NnKR_Qm79tcjlFKjmLjmt3_v2eA,61829
|
|
27
27
|
trilogy/core/models/build_environment.py,sha256=s_C9xAHuD3yZ26T15pWVBvoqvlp2LdZ8yjsv2_HdXLk,5363
|
|
28
28
|
trilogy/core/models/core.py,sha256=wx6hJcFECMG-Ij972ADNkr-3nFXkYESr82ObPiC46_U,10875
|
|
29
29
|
trilogy/core/models/datasource.py,sha256=6RjJUd2u4nYmEwFBpJlM9LbHVYDv8iHJxqiBMZqUrwI,9422
|
|
@@ -40,7 +40,7 @@ trilogy/core/processing/graph_utils.py,sha256=8QUVrkE9j-9C1AyrCb1nQEh8daCe0u1HuX
|
|
|
40
40
|
trilogy/core/processing/utility.py,sha256=rfzdgl-vWkCyhLzXNNuWgPLK59eiYypQb6TdZKymUqk,21469
|
|
41
41
|
trilogy/core/processing/node_generators/__init__.py,sha256=o8rOFHPSo-s_59hREwXMW6gjUJCsiXumdbJNozHUf-Y,800
|
|
42
42
|
trilogy/core/processing/node_generators/basic_node.py,sha256=UVsXMn6jTjm_ofVFt218jAS11s4RV4zD781vP4im-GI,3371
|
|
43
|
-
trilogy/core/processing/node_generators/common.py,sha256=
|
|
43
|
+
trilogy/core/processing/node_generators/common.py,sha256=nVeH_AdO58ygtNSO0wNgMR7_h2D0dFSGM_rh1fJd4Yc,9468
|
|
44
44
|
trilogy/core/processing/node_generators/filter_node.py,sha256=lT167yBgy3P9sDBM1Cjj0PKSXro8dvGtBmc8nwsUjig,8366
|
|
45
45
|
trilogy/core/processing/node_generators/group_node.py,sha256=kO-ersxIL04rZwX5-vFIFQQnp357PFo_7ZKXoGq3wyc,5989
|
|
46
46
|
trilogy/core/processing/node_generators/group_to_node.py,sha256=jKcNCDOY6fNblrdZwaRU0sbUSr9H0moQbAxrGgX6iGA,3832
|
|
@@ -52,7 +52,7 @@ trilogy/core/processing/node_generators/select_node.py,sha256=Y-zO0AFkTrpi2Lyebj
|
|
|
52
52
|
trilogy/core/processing/node_generators/synonym_node.py,sha256=9LHK2XHDjbyTLjmDQieskG8fqbiSpRnFOkfrutDnOTE,2258
|
|
53
53
|
trilogy/core/processing/node_generators/union_node.py,sha256=VNo6Oey4p8etU9xrOh2oTT2lIOTvY6PULUPRvVa2uxU,2877
|
|
54
54
|
trilogy/core/processing/node_generators/unnest_node.py,sha256=cOEKnMRzXUW3bwmiOlgn3E1-B38osng0dh2pDykwITY,2410
|
|
55
|
-
trilogy/core/processing/node_generators/window_node.py,sha256=
|
|
55
|
+
trilogy/core/processing/node_generators/window_node.py,sha256=RUHgpYovQObFod1xRIMWtDzMcxwlm4-1Fdrf_Cuw5W4,6346
|
|
56
56
|
trilogy/core/processing/node_generators/select_helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
57
|
trilogy/core/processing/node_generators/select_helpers/datasource_injection.py,sha256=GMW07bb6hXurhF0hZLYoMAKSIS65tat5hwBjvqqPeSA,6516
|
|
58
58
|
trilogy/core/processing/nodes/__init__.py,sha256=Lxr3rs_bqOAtMtn3DHIkY058ZzjyLM5mSfGMKW2z0NY,5555
|
|
@@ -87,13 +87,13 @@ trilogy/hooks/graph_hook.py,sha256=c-vC-IXoJ_jDmKQjxQyIxyXPOuUcLIURB573gCsAfzQ,2
|
|
|
87
87
|
trilogy/hooks/query_debugger.py,sha256=1npRjww94sPV5RRBBlLqMJRaFkH9vhEY6o828MeoEcw,5583
|
|
88
88
|
trilogy/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
89
89
|
trilogy/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
90
|
-
trilogy/parsing/common.py,sha256=
|
|
90
|
+
trilogy/parsing/common.py,sha256=U9RNi1GyPTQaitZGwXy1QftdC5PWYArP7V8t-v3H8Po,27157
|
|
91
91
|
trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
|
|
92
92
|
trilogy/parsing/exceptions.py,sha256=92E5i2frv5hj9wxObJZsZqj5T6bglvPzvdvco_vW1Zk,38
|
|
93
93
|
trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
94
|
-
trilogy/parsing/parse_engine.py,sha256=
|
|
94
|
+
trilogy/parsing/parse_engine.py,sha256=9SO2q8m5MlZo_Eho-_r6hmTSm5VH38k47C2iHTtYwjU,68224
|
|
95
95
|
trilogy/parsing/render.py,sha256=hI4y-xjXrEXvHslY2l2TQ8ic0zAOpN41ADH37J2_FZY,19047
|
|
96
|
-
trilogy/parsing/trilogy.lark,sha256=
|
|
96
|
+
trilogy/parsing/trilogy.lark,sha256=zbDAIG7gpsImxBtteD8E2pKwcJCGpM-rEQDRqpgzoSQ,13717
|
|
97
97
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
98
|
trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
|
|
99
99
|
trilogy/std/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -102,8 +102,8 @@ trilogy/std/display.preql,sha256=2BbhvqR4rcltyAbOXAUo7SZ_yGFYZgFnurglHMbjW2g,40
|
|
|
102
102
|
trilogy/std/geography.preql,sha256=-fqAGnBL6tR-UtT8DbSek3iMFg66ECR_B_41pODxv-k,504
|
|
103
103
|
trilogy/std/money.preql,sha256=ZHW-csTX-kYbOLmKSO-TcGGgQ-_DMrUXy0BjfuJSFxM,80
|
|
104
104
|
trilogy/std/report.preql,sha256=LbV-XlHdfw0jgnQ8pV7acG95xrd1-p65fVpiIc-S7W4,202
|
|
105
|
-
pytrilogy-0.0.3.
|
|
106
|
-
pytrilogy-0.0.3.
|
|
107
|
-
pytrilogy-0.0.3.
|
|
108
|
-
pytrilogy-0.0.3.
|
|
109
|
-
pytrilogy-0.0.3.
|
|
105
|
+
pytrilogy-0.0.3.44.dist-info/METADATA,sha256=-TUyQi3fENhsnYhqsy2HiD-thLyAz6S8TAmgdVuaCZo,9100
|
|
106
|
+
pytrilogy-0.0.3.44.dist-info/WHEEL,sha256=GHB6lJx2juba1wDgXDNlMTyM13ckjBMKf-OnwgKOCtA,91
|
|
107
|
+
pytrilogy-0.0.3.44.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
|
|
108
|
+
pytrilogy-0.0.3.44.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
109
|
+
pytrilogy-0.0.3.44.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/core/models/author.py
CHANGED
|
@@ -629,18 +629,8 @@ class Comparison(ConceptArgs, Mergeable, DataTyped, Namespaced, BaseModel):
|
|
|
629
629
|
)
|
|
630
630
|
elif self.operator in (ComparisonOperator.IN, ComparisonOperator.NOT_IN):
|
|
631
631
|
right_type = arg_to_datatype(self.right)
|
|
632
|
-
if not any(
|
|
633
|
-
[
|
|
634
|
-
isinstance(self.right, ConceptRef),
|
|
635
|
-
right_type in (DataType.LIST,),
|
|
636
|
-
isinstance(right_type, (ListType, ListWrapper, TupleWrapper)),
|
|
637
|
-
]
|
|
638
|
-
):
|
|
639
|
-
raise SyntaxError(
|
|
640
|
-
f"Cannot use {self.operator.value} with non-list, non-tuple, non-concept object {self.right} in {str(self)}"
|
|
641
|
-
)
|
|
642
632
|
|
|
643
|
-
|
|
633
|
+
if isinstance(right_type, ListType) and not is_compatible_datatype(
|
|
644
634
|
arg_to_datatype(self.left), right_type.value_data_type
|
|
645
635
|
):
|
|
646
636
|
raise SyntaxError(
|
|
@@ -1916,7 +1906,7 @@ class AggregateWrapper(Mergeable, DataTyped, ConceptArgs, Namespaced, BaseModel)
|
|
|
1916
1906
|
|
|
1917
1907
|
|
|
1918
1908
|
class FilterItem(DataTyped, Namespaced, ConceptArgs, BaseModel):
|
|
1919
|
-
content:
|
|
1909
|
+
content: Expr
|
|
1920
1910
|
where: "WhereClause"
|
|
1921
1911
|
|
|
1922
1912
|
@field_validator("content", mode="before")
|
|
@@ -1932,13 +1922,21 @@ class FilterItem(DataTyped, Namespaced, ConceptArgs, BaseModel):
|
|
|
1932
1922
|
self, source: Concept, target: Concept, modifiers: List[Modifier]
|
|
1933
1923
|
) -> "FilterItem":
|
|
1934
1924
|
return FilterItem.model_construct(
|
|
1935
|
-
content=
|
|
1925
|
+
content=(
|
|
1926
|
+
self.content.with_merge(source, target, modifiers)
|
|
1927
|
+
if isinstance(self.content, Mergeable)
|
|
1928
|
+
else self.content
|
|
1929
|
+
),
|
|
1936
1930
|
where=self.where.with_merge(source, target, modifiers),
|
|
1937
1931
|
)
|
|
1938
1932
|
|
|
1939
1933
|
def with_namespace(self, namespace: str) -> "FilterItem":
|
|
1940
1934
|
return FilterItem.model_construct(
|
|
1941
|
-
content=
|
|
1935
|
+
content=(
|
|
1936
|
+
self.content.with_namespace(namespace)
|
|
1937
|
+
if isinstance(self.content, Namespaced)
|
|
1938
|
+
else self.content
|
|
1939
|
+
),
|
|
1942
1940
|
where=self.where.with_namespace(namespace),
|
|
1943
1941
|
)
|
|
1944
1942
|
|
trilogy/core/models/build.py
CHANGED
|
@@ -1496,22 +1496,43 @@ class Factory:
|
|
|
1496
1496
|
):
|
|
1497
1497
|
return base
|
|
1498
1498
|
|
|
1499
|
+
def instantiate_concept(
|
|
1500
|
+
self,
|
|
1501
|
+
arg: (
|
|
1502
|
+
AggregateWrapper
|
|
1503
|
+
| FunctionCallWrapper
|
|
1504
|
+
| WindowItem
|
|
1505
|
+
| FilterItem
|
|
1506
|
+
| Function
|
|
1507
|
+
| ListWrapper[Any]
|
|
1508
|
+
| MapWrapper[Any, Any]
|
|
1509
|
+
| int
|
|
1510
|
+
| float
|
|
1511
|
+
| str
|
|
1512
|
+
),
|
|
1513
|
+
) -> tuple[Concept, BuildConcept]:
|
|
1514
|
+
from trilogy.parsing.common import arbitrary_to_concept
|
|
1515
|
+
|
|
1516
|
+
new = arbitrary_to_concept(
|
|
1517
|
+
arg,
|
|
1518
|
+
environment=self.environment,
|
|
1519
|
+
)
|
|
1520
|
+
built = self.build(new)
|
|
1521
|
+
self.local_concepts[new.address] = built
|
|
1522
|
+
return new, built
|
|
1523
|
+
|
|
1499
1524
|
@build.register
|
|
1500
1525
|
def _(self, base: None) -> None:
|
|
1501
1526
|
return base
|
|
1502
1527
|
|
|
1503
1528
|
@build.register
|
|
1504
1529
|
def _(self, base: Function) -> BuildFunction | BuildAggregateWrapper:
|
|
1505
|
-
from trilogy.parsing.common import arbitrary_to_concept
|
|
1506
1530
|
|
|
1507
1531
|
raw_args: list[Concept | FuncArgs] = []
|
|
1508
1532
|
for arg in base.arguments:
|
|
1509
1533
|
# to do proper discovery, we need to inject virtual intermediate ocncepts
|
|
1510
1534
|
if isinstance(arg, (AggregateWrapper, FilterItem, WindowItem)):
|
|
1511
|
-
narg =
|
|
1512
|
-
arg,
|
|
1513
|
-
environment=self.environment,
|
|
1514
|
-
)
|
|
1535
|
+
narg, _ = self.instantiate_concept(arg)
|
|
1515
1536
|
raw_args.append(narg)
|
|
1516
1537
|
else:
|
|
1517
1538
|
raw_args.append(arg)
|
|
@@ -1532,24 +1553,16 @@ class Factory:
|
|
|
1532
1553
|
for x in arguments:
|
|
1533
1554
|
if isinstance(x, (ConceptRef, Concept)):
|
|
1534
1555
|
final_args.append(x)
|
|
1535
|
-
elif isinstance(x, (AggregateWrapper, FilterItem, WindowItem)):
|
|
1536
|
-
newx = arbitrary_to_concept(
|
|
1537
|
-
x,
|
|
1538
|
-
environment=self.environment,
|
|
1539
|
-
)
|
|
1540
|
-
final_args.append(newx)
|
|
1541
1556
|
else:
|
|
1542
1557
|
# constants, etc, can be ignored for group
|
|
1543
1558
|
continue
|
|
1544
|
-
|
|
1559
|
+
_, rval = self.instantiate_concept(
|
|
1545
1560
|
AggregateWrapper(
|
|
1546
1561
|
function=group_base.lineage.function,
|
|
1547
1562
|
by=final_args,
|
|
1548
|
-
)
|
|
1549
|
-
environment=self.environment,
|
|
1563
|
+
)
|
|
1550
1564
|
)
|
|
1551
1565
|
|
|
1552
|
-
rval = self.build(group_base)
|
|
1553
1566
|
return BuildFunction.model_construct(
|
|
1554
1567
|
operator=base.operator,
|
|
1555
1568
|
arguments=[rval, *[self.build(c) for c in raw_args[1:]]],
|
|
@@ -1580,20 +1593,13 @@ class Factory:
|
|
|
1580
1593
|
|
|
1581
1594
|
@build.register
|
|
1582
1595
|
def _(self, base: CaseWhen) -> BuildCaseWhen:
|
|
1583
|
-
from trilogy.parsing.common import arbitrary_to_concept
|
|
1584
1596
|
|
|
1585
1597
|
comparison = base.comparison
|
|
1586
1598
|
if isinstance(comparison, (AggregateWrapper, FilterItem, WindowItem)):
|
|
1587
|
-
comparison =
|
|
1588
|
-
comparison,
|
|
1589
|
-
environment=self.environment,
|
|
1590
|
-
)
|
|
1599
|
+
comparison, _ = self.instantiate_concept(comparison)
|
|
1591
1600
|
expr: Concept | FuncArgs = base.expr
|
|
1592
1601
|
if isinstance(expr, (AggregateWrapper, FilterItem, WindowItem)):
|
|
1593
|
-
expr =
|
|
1594
|
-
expr,
|
|
1595
|
-
environment=self.environment,
|
|
1596
|
-
)
|
|
1602
|
+
expr, _ = self.instantiate_concept(expr)
|
|
1597
1603
|
return BuildCaseWhen.model_construct(
|
|
1598
1604
|
comparison=self.build(comparison),
|
|
1599
1605
|
expr=self.build(expr),
|
|
@@ -1615,6 +1621,7 @@ class Factory:
|
|
|
1615
1621
|
else:
|
|
1616
1622
|
build_lineage = None
|
|
1617
1623
|
derivation = Concept.calculate_derivation(build_lineage, base.purpose)
|
|
1624
|
+
|
|
1618
1625
|
granularity = Concept.calculate_granularity(
|
|
1619
1626
|
derivation, final_grain, build_lineage
|
|
1620
1627
|
)
|
|
@@ -1675,16 +1682,12 @@ class Factory:
|
|
|
1675
1682
|
|
|
1676
1683
|
@build.register
|
|
1677
1684
|
def _(self, base: OrderItem) -> BuildOrderItem:
|
|
1678
|
-
from trilogy.parsing.common import arbitrary_to_concept
|
|
1679
1685
|
|
|
1680
1686
|
bexpr: Any
|
|
1681
1687
|
if isinstance(base.expr, (AggregateWrapper, WindowItem, FilterItem)) or (
|
|
1682
1688
|
isinstance(base.expr, Function) and base.expr.operator == FunctionType.GROUP
|
|
1683
1689
|
):
|
|
1684
|
-
bexpr =
|
|
1685
|
-
base.expr,
|
|
1686
|
-
environment=self.environment,
|
|
1687
|
-
)
|
|
1690
|
+
bexpr, _ = self.instantiate_concept(base.expr)
|
|
1688
1691
|
else:
|
|
1689
1692
|
bexpr = base.expr
|
|
1690
1693
|
return BuildOrderItem.model_construct(
|
|
@@ -1707,15 +1710,10 @@ class Factory:
|
|
|
1707
1710
|
|
|
1708
1711
|
@build.register
|
|
1709
1712
|
def _(self, base: WindowItem) -> BuildWindowItem:
|
|
1710
|
-
# to do proper discovery, we need to inject virtual intermediate ocncepts
|
|
1711
|
-
from trilogy.parsing.common import arbitrary_to_concept
|
|
1712
1713
|
|
|
1713
1714
|
content: Concept | FuncArgs = base.content
|
|
1714
1715
|
if isinstance(content, (AggregateWrapper, FilterItem, WindowItem)):
|
|
1715
|
-
content =
|
|
1716
|
-
content,
|
|
1717
|
-
environment=self.environment,
|
|
1718
|
-
)
|
|
1716
|
+
content, _ = self.instantiate_concept(content)
|
|
1719
1717
|
final_by = []
|
|
1720
1718
|
for x in base.order_by:
|
|
1721
1719
|
if (
|
|
@@ -1743,30 +1741,26 @@ class Factory:
|
|
|
1743
1741
|
|
|
1744
1742
|
@build.register
|
|
1745
1743
|
def _(self, base: SubselectComparison) -> BuildSubselectComparison:
|
|
1744
|
+
right: Any = base.right
|
|
1745
|
+
if isinstance(base.right, (AggregateWrapper, WindowItem, FilterItem, Function)):
|
|
1746
|
+
right_c, _ = self.instantiate_concept(base.right)
|
|
1747
|
+
right = right_c
|
|
1746
1748
|
return BuildSubselectComparison.model_construct(
|
|
1747
|
-
left=
|
|
1748
|
-
right=
|
|
1749
|
+
left=self.build(base.left),
|
|
1750
|
+
right=self.build(right),
|
|
1749
1751
|
operator=base.operator,
|
|
1750
1752
|
)
|
|
1751
1753
|
|
|
1752
1754
|
@build.register
|
|
1753
1755
|
def _(self, base: Comparison) -> BuildComparison:
|
|
1754
|
-
from trilogy.parsing.common import arbitrary_to_concept
|
|
1755
1756
|
|
|
1756
1757
|
left = base.left
|
|
1757
1758
|
if isinstance(left, (AggregateWrapper, WindowItem, FilterItem)):
|
|
1758
|
-
left_c =
|
|
1759
|
-
left,
|
|
1760
|
-
environment=self.environment,
|
|
1761
|
-
)
|
|
1759
|
+
left_c, _ = self.instantiate_concept(left)
|
|
1762
1760
|
left = left_c # type: ignore
|
|
1763
1761
|
right = base.right
|
|
1764
1762
|
if isinstance(right, (AggregateWrapper, WindowItem, FilterItem)):
|
|
1765
|
-
right_c =
|
|
1766
|
-
right,
|
|
1767
|
-
environment=self.environment,
|
|
1768
|
-
)
|
|
1769
|
-
|
|
1763
|
+
right_c, _ = self.instantiate_concept(right)
|
|
1770
1764
|
right = right_c # type: ignore
|
|
1771
1765
|
return BuildComparison.model_construct(
|
|
1772
1766
|
left=(self.build(left)),
|
|
@@ -1826,6 +1820,13 @@ class Factory:
|
|
|
1826
1820
|
|
|
1827
1821
|
@build.register
|
|
1828
1822
|
def _(self, base: FilterItem) -> BuildFilterItem:
|
|
1823
|
+
if isinstance(
|
|
1824
|
+
base.content, (Function, AggregateWrapper, WindowItem, FilterItem)
|
|
1825
|
+
):
|
|
1826
|
+
_, built = self.instantiate_concept(base.content)
|
|
1827
|
+
return BuildFilterItem.model_construct(
|
|
1828
|
+
content=built, where=self.build(base.where)
|
|
1829
|
+
)
|
|
1829
1830
|
return BuildFilterItem.model_construct(
|
|
1830
1831
|
content=self.build(base.content), where=self.build(base.where)
|
|
1831
1832
|
)
|
|
@@ -1969,6 +1970,10 @@ class Factory:
|
|
|
1969
1970
|
new.datasources[k] = self.build(d)
|
|
1970
1971
|
for k, a in base.alias_origin_lookup.items():
|
|
1971
1972
|
new.alias_origin_lookup[k] = self.build(a)
|
|
1973
|
+
# add in anything that was built as a side-effect
|
|
1974
|
+
for bk, bv in self.local_concepts.items():
|
|
1975
|
+
if bk not in new.concepts:
|
|
1976
|
+
new.concepts[bk] = bv
|
|
1972
1977
|
new.gen_concept_list_caches()
|
|
1973
1978
|
return new
|
|
1974
1979
|
|
|
@@ -178,7 +178,7 @@ def gen_enrichment_node(
|
|
|
178
178
|
for x in extra_required
|
|
179
179
|
):
|
|
180
180
|
log_lambda(
|
|
181
|
-
f"{str(type(base_node).__name__)} returning property optimized enrichment node"
|
|
181
|
+
f"{str(type(base_node).__name__)} returning property optimized enrichment node for {extra_required[0].keys}"
|
|
182
182
|
)
|
|
183
183
|
return gen_property_enrichment_node(
|
|
184
184
|
base_node,
|
|
@@ -32,7 +32,9 @@ def resolve_window_parent_concepts(
|
|
|
32
32
|
if concept.lineage.order_by:
|
|
33
33
|
for item in concept.lineage.order_by:
|
|
34
34
|
base += item.concept_arguments
|
|
35
|
-
|
|
35
|
+
if concept.grain:
|
|
36
|
+
for gitem in concept.grain.components:
|
|
37
|
+
base.append(environment.concepts[gitem])
|
|
36
38
|
return concept.lineage.content, unique(base, "address")
|
|
37
39
|
|
|
38
40
|
|
|
@@ -131,20 +133,24 @@ def gen_window_node(
|
|
|
131
133
|
)
|
|
132
134
|
_window_node.rebuild_cache()
|
|
133
135
|
_window_node.resolve()
|
|
136
|
+
|
|
134
137
|
window_node = StrategyNode(
|
|
135
138
|
input_concepts=[concept] + additional_outputs + parent_concepts + targets,
|
|
136
139
|
output_concepts=[concept] + additional_outputs + parent_concepts + targets,
|
|
137
140
|
environment=environment,
|
|
138
141
|
parents=[_window_node],
|
|
139
142
|
preexisting_conditions=conditions.conditional if conditions else None,
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
+
grain=BuildGrain.from_concepts(
|
|
144
|
+
concepts=[concept] + additional_outputs + parent_concepts + targets,
|
|
145
|
+
environment=environment,
|
|
146
|
+
),
|
|
143
147
|
)
|
|
144
148
|
if not non_equivalent_optional:
|
|
145
149
|
logger.info(
|
|
146
150
|
f"{padding(depth)}{LOGGER_PREFIX} no optional concepts, returning window node"
|
|
147
151
|
)
|
|
152
|
+
# prune outputs if we don't need join keys
|
|
153
|
+
window_node.set_output_concepts([concept] + additional_outputs + targets)
|
|
148
154
|
return window_node
|
|
149
155
|
|
|
150
156
|
missing_optional = [
|
trilogy/parsing/common.py
CHANGED
|
@@ -458,7 +458,25 @@ def filter_item_to_concept(
|
|
|
458
458
|
metadata: Metadata | None = None,
|
|
459
459
|
) -> Concept:
|
|
460
460
|
fmetadata = metadata or Metadata()
|
|
461
|
-
|
|
461
|
+
if isinstance(parent.content, ConceptRef):
|
|
462
|
+
cparent = environment.concepts[parent.content.address]
|
|
463
|
+
elif isinstance(
|
|
464
|
+
parent.content,
|
|
465
|
+
(
|
|
466
|
+
FilterItem,
|
|
467
|
+
AggregateWrapper,
|
|
468
|
+
FunctionCallWrapper,
|
|
469
|
+
WindowItem,
|
|
470
|
+
Function,
|
|
471
|
+
ListWrapper,
|
|
472
|
+
MapWrapper,
|
|
473
|
+
),
|
|
474
|
+
):
|
|
475
|
+
cparent = arbitrary_to_concept(parent.content, environment, namespace=namespace)
|
|
476
|
+
else:
|
|
477
|
+
raise NotImplementedError(
|
|
478
|
+
f"Filter item with non ref content {parent.content} not yet supported"
|
|
479
|
+
)
|
|
462
480
|
modifiers = get_upstream_modifiers(
|
|
463
481
|
cparent.concept_arguments, environment=environment
|
|
464
482
|
)
|
|
@@ -494,24 +512,6 @@ def window_item_to_concept(
|
|
|
494
512
|
metadata: Metadata | None = None,
|
|
495
513
|
) -> Concept:
|
|
496
514
|
fmetadata = metadata or Metadata()
|
|
497
|
-
# if isinstance(
|
|
498
|
-
# parent.content,
|
|
499
|
-
# (
|
|
500
|
-
# AggregateWrapper
|
|
501
|
-
# | FunctionCallWrapper
|
|
502
|
-
# | WindowItem
|
|
503
|
-
# | FilterItem
|
|
504
|
-
# | Function
|
|
505
|
-
# | ListWrapper
|
|
506
|
-
# | MapWrapper
|
|
507
|
-
# ),
|
|
508
|
-
# ):
|
|
509
|
-
# new_parent = arbitrary_to_concept(
|
|
510
|
-
# parent.content, environment=environment, namespace=namespace
|
|
511
|
-
# )
|
|
512
|
-
# environment.add_concept(new_parent)
|
|
513
|
-
# parent = parent.model_copy(update={"content": new_parent.reference})
|
|
514
|
-
|
|
515
515
|
if not isinstance(parent.content, ConceptRef):
|
|
516
516
|
raise NotImplementedError(
|
|
517
517
|
f"Window function wiht non ref content {parent.content} not yet supported"
|
|
@@ -523,16 +523,26 @@ def window_item_to_concept(
|
|
|
523
523
|
local_purpose, keys = get_purpose_and_keys(None, (bcontent,), environment)
|
|
524
524
|
else:
|
|
525
525
|
local_purpose = Purpose.PROPERTY
|
|
526
|
-
keys =
|
|
527
|
-
bcontent.address,
|
|
528
|
-
|
|
526
|
+
keys = Grain.from_concepts(
|
|
527
|
+
[bcontent.address] + [y.address for y in parent.over], environment
|
|
528
|
+
).components
|
|
529
529
|
|
|
530
|
+
# when including the order by in discovery grain
|
|
530
531
|
if parent.order_by:
|
|
531
532
|
grain_components = parent.over + [bcontent.output]
|
|
532
533
|
for item in parent.order_by:
|
|
533
|
-
|
|
534
|
+
# confirm that it's not just an aggregate at the grain of the stuff we're already keying of of
|
|
535
|
+
# in which case we can ignore contributions
|
|
536
|
+
if (
|
|
537
|
+
isinstance(item.expr, AggregateWrapper)
|
|
538
|
+
and set([x.address for x in item.expr.by]) == keys
|
|
539
|
+
):
|
|
540
|
+
continue
|
|
541
|
+
else:
|
|
542
|
+
grain_components += item.concept_arguments
|
|
534
543
|
else:
|
|
535
544
|
grain_components = parent.over + [bcontent.output]
|
|
545
|
+
|
|
536
546
|
final_grain = Grain.from_concepts(grain_components, environment)
|
|
537
547
|
modifiers = get_upstream_modifiers(bcontent.concept_arguments, environment)
|
|
538
548
|
datatype = parent.content.datatype
|
|
@@ -651,7 +661,9 @@ def rowset_concept(
|
|
|
651
661
|
orig_concept = environment.concepts[orig_address.address]
|
|
652
662
|
name = orig_concept.name
|
|
653
663
|
if isinstance(orig_concept.lineage, FilterItem):
|
|
654
|
-
if orig_concept.lineage.where == rowset.select.where_clause
|
|
664
|
+
if orig_concept.lineage.where == rowset.select.where_clause and isinstance(
|
|
665
|
+
orig_concept.lineage.content, (ConceptRef, Concept)
|
|
666
|
+
):
|
|
655
667
|
name = environment.concepts[orig_concept.lineage.content.address].name
|
|
656
668
|
base_namespace = (
|
|
657
669
|
f"{rowset.name}.{orig_concept.namespace}"
|
|
@@ -761,7 +773,10 @@ def arbitrary_to_concept(
|
|
|
761
773
|
)
|
|
762
774
|
elif isinstance(parent, FilterItem):
|
|
763
775
|
if not name:
|
|
764
|
-
|
|
776
|
+
if isinstance(parent.content, ConceptRef):
|
|
777
|
+
name = f"{VIRTUAL_CONCEPT_PREFIX}_filter_{parent.content.name}_{string_to_hash(str(parent))}"
|
|
778
|
+
else:
|
|
779
|
+
name = f"{VIRTUAL_CONCEPT_PREFIX}_filter_{string_to_hash(str(parent))}"
|
|
765
780
|
return filter_item_to_concept(
|
|
766
781
|
parent,
|
|
767
782
|
name,
|
trilogy/parsing/parse_engine.py
CHANGED
|
@@ -495,7 +495,7 @@ class ParseToObjects(Transformer):
|
|
|
495
495
|
return ComparisonOperator([x.value.lower() for x in args])
|
|
496
496
|
|
|
497
497
|
def COMPARISON_OPERATOR(self, args) -> ComparisonOperator:
|
|
498
|
-
return ComparisonOperator(args)
|
|
498
|
+
return ComparisonOperator(args.strip())
|
|
499
499
|
|
|
500
500
|
def LOGICAL_OPERATOR(self, args) -> BooleanOperator:
|
|
501
501
|
return BooleanOperator(args.lower())
|
|
@@ -682,8 +682,7 @@ class ParseToObjects(Transformer):
|
|
|
682
682
|
return ConceptDerivationStatement(concept=concept)
|
|
683
683
|
|
|
684
684
|
raise SyntaxError(
|
|
685
|
-
f"Received invalid type {type(args[2])} {args[2]} as input to
|
|
686
|
-
" transform"
|
|
685
|
+
f"Received invalid type {type(args[2])} {args[2]} as input to concept derivation: `{self.text_lookup[self.token_address][meta.start_pos:meta.end_pos]}`"
|
|
687
686
|
)
|
|
688
687
|
|
|
689
688
|
@v_args(meta=True)
|
|
@@ -1255,7 +1254,7 @@ class ParseToObjects(Transformer):
|
|
|
1255
1254
|
intersection = base.locally_derived.intersection(pre_keys)
|
|
1256
1255
|
if intersection:
|
|
1257
1256
|
raise ParseError(
|
|
1258
|
-
f"Select statement {base}
|
|
1257
|
+
f"Select statement {base} creates new derived concepts {list(intersection)} from transformations with identical name(s) to existing concept(s). Do you mean to drop the calculation and directly use the existing concept? If not, alias these concept(s) under new names."
|
|
1259
1258
|
)
|
|
1260
1259
|
return base
|
|
1261
1260
|
|
|
@@ -1365,11 +1364,78 @@ class ParseToObjects(Transformer):
|
|
|
1365
1364
|
def literal(self, args):
|
|
1366
1365
|
return args[0]
|
|
1367
1366
|
|
|
1367
|
+
def product_operator(self, args) -> Function | Any:
|
|
1368
|
+
if len(args) == 1:
|
|
1369
|
+
return args[0]
|
|
1370
|
+
result = args[0]
|
|
1371
|
+
for i in range(1, len(args), 2):
|
|
1372
|
+
new_result = None
|
|
1373
|
+
op = args[i]
|
|
1374
|
+
right = args[i + 1]
|
|
1375
|
+
if op == "*":
|
|
1376
|
+
new_result = self.function_factory.create_function(
|
|
1377
|
+
[result, right], operator=FunctionType.MULTIPLY
|
|
1378
|
+
)
|
|
1379
|
+
elif op == "/":
|
|
1380
|
+
new_result = self.function_factory.create_function(
|
|
1381
|
+
[result, right], operator=FunctionType.DIVIDE
|
|
1382
|
+
)
|
|
1383
|
+
elif op == "%":
|
|
1384
|
+
new_result = self.function_factory.create_function(
|
|
1385
|
+
[result, right], operator=FunctionType.MOD
|
|
1386
|
+
)
|
|
1387
|
+
else:
|
|
1388
|
+
raise ValueError(f"Unknown operator: {op}")
|
|
1389
|
+
result = new_result
|
|
1390
|
+
return new_result
|
|
1391
|
+
|
|
1392
|
+
def PLUS_OR_MINUS(self, args) -> str:
|
|
1393
|
+
return args.value
|
|
1394
|
+
|
|
1395
|
+
def MULTIPLY_DIVIDE_PERCENT(self, args) -> str:
|
|
1396
|
+
return args[0]
|
|
1397
|
+
|
|
1398
|
+
@v_args(meta=True)
|
|
1399
|
+
def sum_operator(self, meta: Meta, args) -> Function | Any:
|
|
1400
|
+
if len(args) == 1:
|
|
1401
|
+
return args[0]
|
|
1402
|
+
result = args[0]
|
|
1403
|
+
for i in range(1, len(args), 2):
|
|
1404
|
+
new_result = None
|
|
1405
|
+
op = args[i]
|
|
1406
|
+
right = args[i + 1]
|
|
1407
|
+
if op == "+":
|
|
1408
|
+
new_result = self.function_factory.create_function(
|
|
1409
|
+
[result, right], operator=FunctionType.ADD, meta=meta
|
|
1410
|
+
)
|
|
1411
|
+
elif op == "-":
|
|
1412
|
+
new_result = self.function_factory.create_function(
|
|
1413
|
+
[result, right], operator=FunctionType.SUBTRACT, meta=meta
|
|
1414
|
+
)
|
|
1415
|
+
elif op == "||":
|
|
1416
|
+
new_result = self.function_factory.create_function(
|
|
1417
|
+
[result, right], operator=FunctionType.CONCAT, meta=meta
|
|
1418
|
+
)
|
|
1419
|
+
elif op == "like":
|
|
1420
|
+
new_result = self.function_factory.create_function(
|
|
1421
|
+
[result, right], operator=FunctionType.LIKE, meta=meta
|
|
1422
|
+
)
|
|
1423
|
+
else:
|
|
1424
|
+
raise ValueError(f"Unknown operator: {op}")
|
|
1425
|
+
result = new_result
|
|
1426
|
+
return result
|
|
1427
|
+
|
|
1368
1428
|
def comparison(self, args) -> Comparison:
|
|
1369
|
-
if args
|
|
1370
|
-
|
|
1429
|
+
if len(args) == 1:
|
|
1430
|
+
return args[0]
|
|
1371
1431
|
left = args[0]
|
|
1372
1432
|
right = args[2]
|
|
1433
|
+
if args[1] in (ComparisonOperator.IN, ComparisonOperator.NOT_IN):
|
|
1434
|
+
return SubselectComparison(
|
|
1435
|
+
left=left,
|
|
1436
|
+
right=right,
|
|
1437
|
+
operator=args[1],
|
|
1438
|
+
)
|
|
1373
1439
|
return Comparison(left=left, right=right, operator=args[1])
|
|
1374
1440
|
|
|
1375
1441
|
def between_comparison(self, args) -> Conditional:
|
|
@@ -1500,13 +1566,14 @@ class ParseToObjects(Transformer):
|
|
|
1500
1566
|
|
|
1501
1567
|
def filter_item(self, args) -> FilterItem:
|
|
1502
1568
|
where: WhereClause
|
|
1503
|
-
|
|
1569
|
+
expr, raw = args
|
|
1504
1570
|
if isinstance(raw, WhereClause):
|
|
1505
1571
|
where = raw
|
|
1506
1572
|
else:
|
|
1507
1573
|
where = WhereClause(conditional=raw)
|
|
1508
|
-
|
|
1509
|
-
|
|
1574
|
+
if isinstance(expr, str):
|
|
1575
|
+
expr = self.environment.concepts[expr].reference
|
|
1576
|
+
return FilterItem(content=expr, where=where)
|
|
1510
1577
|
|
|
1511
1578
|
# BEGIN FUNCTIONS
|
|
1512
1579
|
@v_args(meta=True)
|
|
@@ -1725,10 +1792,7 @@ class ParseToObjects(Transformer):
|
|
|
1725
1792
|
def fyear(self, meta, args):
|
|
1726
1793
|
return self.function_factory.create_function(args, FunctionType.YEAR, meta)
|
|
1727
1794
|
|
|
1728
|
-
|
|
1729
|
-
@v_args(meta=True)
|
|
1730
|
-
def fcast(self, meta, args) -> Function:
|
|
1731
|
-
# if it's casting a constant, we'll process that directly
|
|
1795
|
+
def internal_fcast(self, meta, args):
|
|
1732
1796
|
args = process_function_args(args, meta=meta, environment=self.environment)
|
|
1733
1797
|
if isinstance(args[0], str):
|
|
1734
1798
|
processed: date | datetime | int | float | bool | str
|
|
@@ -1753,9 +1817,15 @@ class ParseToObjects(Transformer):
|
|
|
1753
1817
|
)
|
|
1754
1818
|
return self.function_factory.create_function(args, FunctionType.CAST, meta)
|
|
1755
1819
|
|
|
1820
|
+
# utility functions
|
|
1821
|
+
@v_args(meta=True)
|
|
1822
|
+
def fcast(self, meta, args) -> Function:
|
|
1823
|
+
return self.internal_fcast(meta, args)
|
|
1824
|
+
|
|
1756
1825
|
# math functions
|
|
1757
1826
|
@v_args(meta=True)
|
|
1758
1827
|
def fadd(self, meta, args) -> Function:
|
|
1828
|
+
|
|
1759
1829
|
return self.function_factory.create_function(args, FunctionType.ADD, meta)
|
|
1760
1830
|
|
|
1761
1831
|
@v_args(meta=True)
|
trilogy/parsing/trilogy.lark
CHANGED
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
|
|
53
53
|
//column_assignment
|
|
54
54
|
//figure out if we want static
|
|
55
|
-
column_assignment: (raw_column_assignment | IDENTIFIER | QUOTED_IDENTIFIER |
|
|
55
|
+
column_assignment: (raw_column_assignment | IDENTIFIER | QUOTED_IDENTIFIER | expr ) ":" concept_assignment
|
|
56
56
|
|
|
57
57
|
RAW_ENTRY.1: /raw\s*\(/s
|
|
58
58
|
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
type_declaration: "type" IDENTIFIER data_type
|
|
103
103
|
|
|
104
104
|
// user_id where state = Mexico
|
|
105
|
-
_filter_alt: IDENTIFIER "?" conditional
|
|
105
|
+
_filter_alt: (IDENTIFIER | "(" expr ")") "?" conditional
|
|
106
106
|
_filter_base: "filter"i IDENTIFIER where
|
|
107
107
|
filter_item: _filter_base | _filter_alt
|
|
108
108
|
|
|
@@ -129,12 +129,6 @@
|
|
|
129
129
|
|
|
130
130
|
limit: "LIMIT"i /[0-9]+/
|
|
131
131
|
|
|
132
|
-
!window_order: /TOP|BOTTOM/i
|
|
133
|
-
|
|
134
|
-
window: window_order /[0-9]+/
|
|
135
|
-
|
|
136
|
-
window_order_by: "BY"i column_list
|
|
137
|
-
|
|
138
132
|
order_list: expr ordering ("," expr ordering)* ","?
|
|
139
133
|
|
|
140
134
|
over_list: concept_lit ("," concept_lit )* ","?
|
|
@@ -166,11 +160,9 @@
|
|
|
166
160
|
|
|
167
161
|
!array_comparison: ( ("NOT"i "IN"i) | "IN"i)
|
|
168
162
|
|
|
169
|
-
COMPARISON_OPERATOR: /(is\s+not
|
|
163
|
+
COMPARISON_OPERATOR: /(\s+is\s+not\s|\s+is\s|\s+in\s|\s+not\s+in\s|=|>=|<=|!=|>|<)/i
|
|
170
164
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
between_comparison: expr "between"i expr "and"i expr
|
|
165
|
+
between_comparison: "between"i expr "and"i expr
|
|
174
166
|
|
|
175
167
|
subselect_comparison: expr array_comparison (literal | _constant_functions | _string_functions | concept_lit | filter_item | window_item | unnest | fgroup | expr_tuple | parenthetical )
|
|
176
168
|
|
|
@@ -187,31 +179,49 @@
|
|
|
187
179
|
union: _UNION (expr ",")* expr ")"
|
|
188
180
|
|
|
189
181
|
//indexing into an expression is a function
|
|
190
|
-
index_access:
|
|
191
|
-
map_key_access:
|
|
192
|
-
attr_access:
|
|
182
|
+
index_access: atom "[" int_lit "]"
|
|
183
|
+
map_key_access: atom "[" string_lit "]"
|
|
184
|
+
attr_access: atom "." string_lit
|
|
193
185
|
|
|
186
|
+
?expr: comparison_root | between_root
|
|
194
187
|
|
|
195
|
-
|
|
188
|
+
?comparison_root: sum_chain (COMPARISON_OPERATOR sum_chain)? -> comparison
|
|
189
|
+
?between_root: sum_chain "between"i sum_chain "and"i sum_chain -> between_comparison
|
|
196
190
|
|
|
197
|
-
|
|
198
|
-
_basic_expr: literal | concept_lit | parenthetical | expr_tuple
|
|
191
|
+
PLUS_OR_MINUS: ("+" | /-(?!>)/ | "||" | "like" )
|
|
199
192
|
|
|
200
|
-
|
|
201
|
-
|
|
193
|
+
?sum_chain: product_chain (PLUS_OR_MINUS product_chain)* -> sum_operator
|
|
194
|
+
|
|
195
|
+
MULTIPLY_DIVIDE_PERCENT: ("*" | "/" | "%")
|
|
196
|
+
|
|
197
|
+
?product_chain: atom ( MULTIPLY_DIVIDE_PERCENT atom)* -> product_operator
|
|
198
|
+
|
|
199
|
+
?atom: literal | concept_lit | parenthetical
|
|
200
|
+
| expr_tuple
|
|
201
|
+
| custom_function
|
|
202
|
+
| _constant_functions
|
|
203
|
+
| _static_functions
|
|
204
|
+
| _generic_functions
|
|
205
|
+
| _date_functions
|
|
206
|
+
| aggregate_functions
|
|
207
|
+
| window_item
|
|
208
|
+
| unnest
|
|
209
|
+
| union
|
|
210
|
+
| fgroup
|
|
211
|
+
| filter_item
|
|
212
|
+
| _access_expr
|
|
213
|
+
| aggregate_by
|
|
202
214
|
|
|
203
|
-
# Function-like expressions
|
|
204
|
-
_functional_expr: _constant_functions | _static_functions | filter_item | aggregate_functions | window_item | custom_function | fgroup | unnest | union | aggregate_by
|
|
205
215
|
|
|
206
216
|
# Access patterns
|
|
207
217
|
_access_expr: index_access | map_key_access | attr_access
|
|
208
218
|
// functions
|
|
209
219
|
|
|
210
|
-
fadd: (/add\(/ expr "," expr ")" )
|
|
211
|
-
fsub: ("subtract"i "(" expr "," expr ")" )
|
|
212
|
-
fmul: ("multiply"i "(" expr "," expr ")" )
|
|
213
|
-
fdiv: ( "divide"i "(" expr "," expr ")")
|
|
214
|
-
fmod: ( "mod"i "(" expr "," (int_lit | concept_lit ) ")")
|
|
220
|
+
fadd: (/add\(/ expr "," expr ")" )
|
|
221
|
+
fsub: ("subtract"i "(" expr "," expr ")" )
|
|
222
|
+
fmul: ("multiply"i "(" expr "," expr ")" )
|
|
223
|
+
fdiv: ( "divide"i "(" expr "," expr ")")
|
|
224
|
+
fmod: ( "mod"i "(" expr "," (int_lit | concept_lit ) ")")
|
|
215
225
|
_ROUND.1: "round"i "("
|
|
216
226
|
fround: _ROUND expr "," expr ")"
|
|
217
227
|
fabs: "abs"i "(" expr ")"
|
|
@@ -224,9 +234,9 @@
|
|
|
224
234
|
|
|
225
235
|
//generic
|
|
226
236
|
_fcast_primary: "cast"i "(" expr "as"i data_type ")"
|
|
227
|
-
_fcast_alt:
|
|
237
|
+
_fcast_alt: atom "::" data_type
|
|
228
238
|
fcast: _fcast_primary | _fcast_alt
|
|
229
|
-
concat: ("concat"i "(" (expr ",")* expr ")")
|
|
239
|
+
concat: ("concat"i "(" (expr ",")* expr ")")
|
|
230
240
|
fcoalesce: "coalesce"i "(" (expr ",")* expr ")"
|
|
231
241
|
fcase_when: "WHEN"i conditional "THEN"i expr
|
|
232
242
|
fcase_else: "ELSE"i expr
|
|
@@ -235,7 +245,7 @@
|
|
|
235
245
|
fnot: "NOT"i expr
|
|
236
246
|
fbool: "bool"i "(" expr ")"
|
|
237
247
|
|
|
238
|
-
_generic_functions: fcast |
|
|
248
|
+
_generic_functions: fcast | concat | fcoalesce | fcase | len | fnot | fbool
|
|
239
249
|
|
|
240
250
|
//constant
|
|
241
251
|
CURRENT_DATE.1: /current_date\(\)/
|
|
@@ -333,12 +343,12 @@
|
|
|
333
343
|
|
|
334
344
|
_date_functions: fdate | fdate_add | fdate_sub | fdate_diff | fdatetime | ftimestamp | fsecond | fminute | fhour | fday | fday_of_week | fweek | fmonth | fquarter | fyear | fdate_part | fdate_trunc
|
|
335
345
|
|
|
336
|
-
_static_functions: _string_functions | _math_functions
|
|
346
|
+
_static_functions: _string_functions | _math_functions
|
|
337
347
|
|
|
338
348
|
custom_function: "@" IDENTIFIER "(" (expr ",")* expr ")"
|
|
339
349
|
|
|
340
350
|
// base language constructs
|
|
341
|
-
concept_lit:
|
|
351
|
+
concept_lit: IDENTIFIER
|
|
342
352
|
IDENTIFIER: /[a-zA-Z\_][a-zA-Z0-9\_\.]*/
|
|
343
353
|
WILDCARD_IDENTIFIER: /[a-zA-Z\_][a-zA-Z0-9\_\-\.\*]*/
|
|
344
354
|
QUOTED_IDENTIFIER: /`[a-zA-Z\_][a-zA-Z0-9\_\.\-\*\:\s]*`/
|
|
@@ -353,9 +363,9 @@
|
|
|
353
363
|
_double_quote: "\"" ( DOUBLE_STRING_CHARS )* "\""
|
|
354
364
|
string_lit: _single_quote | _double_quote | MULTILINE_STRING
|
|
355
365
|
|
|
356
|
-
MINUS: "-"
|
|
357
366
|
|
|
358
|
-
|
|
367
|
+
|
|
368
|
+
int_lit: /\-?[0-9]+/
|
|
359
369
|
|
|
360
370
|
float_lit: /[0-9]*\.[0-9]+/
|
|
361
371
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|