pytrilogy 0.0.3.24__py3-none-any.whl → 0.0.3.26__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.24.dist-info → pytrilogy-0.0.3.26.dist-info}/METADATA +1 -1
- {pytrilogy-0.0.3.24.dist-info → pytrilogy-0.0.3.26.dist-info}/RECORD +15 -15
- trilogy/__init__.py +1 -1
- trilogy/core/enums.py +1 -0
- trilogy/core/environment_helpers.py +9 -10
- trilogy/core/functions.py +37 -11
- trilogy/core/models/author.py +13 -3
- trilogy/core/models/core.py +13 -1
- trilogy/dialect/base.py +1 -1
- trilogy/parsing/parse_engine.py +31 -14
- trilogy/parsing/trilogy.lark +1 -1
- {pytrilogy-0.0.3.24.dist-info → pytrilogy-0.0.3.26.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.3.24.dist-info → pytrilogy-0.0.3.26.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.24.dist-info → pytrilogy-0.0.3.26.dist-info}/licenses/LICENSE.md +0 -0
- {pytrilogy-0.0.3.24.dist-info → pytrilogy-0.0.3.26.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.26.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
2
|
+
trilogy/__init__.py,sha256=jpttmNzh2EbgBflGCJnC8pwUwKWANddEpY_K5_4Hsn0,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
|
|
@@ -11,21 +11,21 @@ trilogy/utility.py,sha256=euQccZLKoYBz0LNg5tzLlvv2YHvXh9HArnYp1V3uXsM,763
|
|
|
11
11
|
trilogy/authoring/__init__.py,sha256=ohkYA3_LGYZh3fwzEYKTN6ofACDI5GYl3VCbGxVvlzY,2233
|
|
12
12
|
trilogy/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
trilogy/core/constants.py,sha256=7XaCpZn5mQmjTobbeBn56SzPWq9eMNDfzfsRU-fP0VE,171
|
|
14
|
-
trilogy/core/enums.py,sha256=
|
|
14
|
+
trilogy/core/enums.py,sha256=zpXhm8qk6i_ih4O6ro5sPMbIQSQDtzLcBcX5eKWQZU4,7197
|
|
15
15
|
trilogy/core/env_processor.py,sha256=pFsxnluKIusGKx1z7tTnfsd_xZcPy9pZDungkjkyvI0,3170
|
|
16
|
-
trilogy/core/environment_helpers.py,sha256=
|
|
16
|
+
trilogy/core/environment_helpers.py,sha256=i0oyxfLjFQW6ZbD6g2wa0DXUoEvd2sHG28TZ1UBs2os,7573
|
|
17
17
|
trilogy/core/ergonomics.py,sha256=e-7gE29vPLFdg0_A1smQ7eOrUwKl5VYdxRSTddHweRA,1631
|
|
18
18
|
trilogy/core/exceptions.py,sha256=JPYyBcit3T_pRtlHdtKSeVJkIyWUTozW2aaut25A2xI,673
|
|
19
|
-
trilogy/core/functions.py,sha256=
|
|
19
|
+
trilogy/core/functions.py,sha256=Ke8-d2RtHZszAQdx_6dNyHtELbvMIGZo2CsglxqtvEQ,26890
|
|
20
20
|
trilogy/core/graph_models.py,sha256=z17EoO8oky2QOuO6E2aMWoVNKEVJFhLdsQZOhC4fNLU,2079
|
|
21
21
|
trilogy/core/internal.py,sha256=iicDBlC6nM8d7e7jqzf_ZOmpUsW8yrr2AA8AqEiLx-s,1577
|
|
22
22
|
trilogy/core/optimization.py,sha256=xGO8piVsLrpqrx-Aid_Y56_5slSv4eZmlP64hCHRiEc,7957
|
|
23
23
|
trilogy/core/query_processor.py,sha256=Do8YpdPBdsbKtl9n37hobzk8SORMGqH-e_zNNxd-BE4,19456
|
|
24
24
|
trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
-
trilogy/core/models/author.py,sha256
|
|
25
|
+
trilogy/core/models/author.py,sha256=-90COW7QY6w-g8GkjivPZqG2yNitn74e13m5KYgB1IU,73985
|
|
26
26
|
trilogy/core/models/build.py,sha256=2K-dANoFFl36yFdEDf7FDL36zM8FKSzpjLT5K2Na8Kc,57549
|
|
27
27
|
trilogy/core/models/build_environment.py,sha256=8UggvlPU708GZWYPJMc_ou2r7M3TY2g69eqGvz03YX0,5528
|
|
28
|
-
trilogy/core/models/core.py,sha256=
|
|
28
|
+
trilogy/core/models/core.py,sha256=wx6hJcFECMG-Ij972ADNkr-3nFXkYESr82ObPiC46_U,10875
|
|
29
29
|
trilogy/core/models/datasource.py,sha256=6RjJUd2u4nYmEwFBpJlM9LbHVYDv8iHJxqiBMZqUrwI,9422
|
|
30
30
|
trilogy/core/models/environment.py,sha256=eB52hyb1e9fJFybP5u5tUiaNbBHGXkMdm0i2V-LUUiw,26888
|
|
31
31
|
trilogy/core/models/execute.py,sha256=KZHiovlSr_3ZjyzKD1mdBlAqnPCqFCChQkO4_4WlGtg,34224
|
|
@@ -70,7 +70,7 @@ trilogy/core/statements/build.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
|
|
|
70
70
|
trilogy/core/statements/common.py,sha256=KxEmz2ySySyZ6CTPzn0fJl5NX2KOk1RPyuUSwWhnK1g,759
|
|
71
71
|
trilogy/core/statements/execute.py,sha256=cSlvpHFOqpiZ89pPZ5GDp9Hu6j6uj-5_h21FWm_L-KM,1248
|
|
72
72
|
trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
73
|
-
trilogy/dialect/base.py,sha256=
|
|
73
|
+
trilogy/dialect/base.py,sha256=ogBNUDt0UEmx-F8EXGG65i6fcz2KcIz8a3gwE7YXCVw,40956
|
|
74
74
|
trilogy/dialect/bigquery.py,sha256=PkjFcNGZHYOe655PmJhb8a0afdFULuovqP0qQVO8m0I,2953
|
|
75
75
|
trilogy/dialect/common.py,sha256=XjHkP8Dqezjkd2JU5xoAlMRS_6HNyXQCF4CykLK3C8o,5011
|
|
76
76
|
trilogy/dialect/config.py,sha256=olnyeVU5W5T6b9-dMeNAnvxuPlyc2uefb7FRME094Ec,3834
|
|
@@ -91,13 +91,13 @@ trilogy/parsing/common.py,sha256=99tDKrpQTk-SpyTXUqKFtm-lfmhjCOQIn25hxbQvRRg,214
|
|
|
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=Ffm3YB2jTPcA81CZTkpQ7iAwD1LP10QQpjoKk9fey4I,61849
|
|
95
95
|
trilogy/parsing/render.py,sha256=o_XuQWhcwx1lD9eGVqkqZEwkmQK0HdmWWokGBtdeH4I,17837
|
|
96
|
-
trilogy/parsing/trilogy.lark,sha256=
|
|
96
|
+
trilogy/parsing/trilogy.lark,sha256=GYRO0LJYUtzf1__d2b0L2EJzRGfnsI2SaQHzh9RgxnA,12945
|
|
97
97
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
98
|
trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
|
|
99
|
-
pytrilogy-0.0.3.
|
|
100
|
-
pytrilogy-0.0.3.
|
|
101
|
-
pytrilogy-0.0.3.
|
|
102
|
-
pytrilogy-0.0.3.
|
|
103
|
-
pytrilogy-0.0.3.
|
|
99
|
+
pytrilogy-0.0.3.26.dist-info/METADATA,sha256=-Klg8gwJa_TaSlCwqqMiknzDCnTFjv2HMP_iHfgFoOw,9100
|
|
100
|
+
pytrilogy-0.0.3.26.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
101
|
+
pytrilogy-0.0.3.26.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
|
|
102
|
+
pytrilogy-0.0.3.26.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
103
|
+
pytrilogy-0.0.3.26.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/core/enums.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from trilogy.constants import DEFAULT_NAMESPACE
|
|
2
2
|
from trilogy.core.enums import ConceptSource, FunctionType, Purpose
|
|
3
|
-
from trilogy.core.functions import AttrAccess
|
|
3
|
+
from trilogy.core.functions import AttrAccess, FunctionFactory
|
|
4
4
|
from trilogy.core.models.author import Concept, Function, Metadata
|
|
5
5
|
from trilogy.core.models.core import DataType, StructType, arg_to_datatype
|
|
6
6
|
from trilogy.core.models.environment import Environment
|
|
@@ -19,6 +19,7 @@ FUNCTION_DESCRIPTION_MAPS = {
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def generate_date_concepts(concept: Concept, environment: Environment):
|
|
22
|
+
factory = FunctionFactory(environment=environment)
|
|
22
23
|
if concept.metadata and concept.metadata.description:
|
|
23
24
|
base_description = concept.metadata.description
|
|
24
25
|
else:
|
|
@@ -40,17 +41,15 @@ def generate_date_concepts(concept: Concept, environment: Environment):
|
|
|
40
41
|
if concept.purpose == Purpose.CONSTANT
|
|
41
42
|
else Purpose.PROPERTY
|
|
42
43
|
)
|
|
43
|
-
|
|
44
|
+
function = factory.create_function(
|
|
44
45
|
operator=ftype,
|
|
45
|
-
|
|
46
|
-
output_purpose=default_type,
|
|
47
|
-
arguments=[concept],
|
|
46
|
+
args=[concept],
|
|
48
47
|
)
|
|
49
48
|
new_concept = Concept(
|
|
50
49
|
name=f"{concept.name}.{fname}",
|
|
51
50
|
datatype=DataType.INTEGER,
|
|
52
51
|
purpose=default_type,
|
|
53
|
-
lineage=
|
|
52
|
+
lineage=function,
|
|
54
53
|
grain=concept.grain,
|
|
55
54
|
namespace=concept.namespace,
|
|
56
55
|
keys=set(
|
|
@@ -68,6 +67,7 @@ def generate_date_concepts(concept: Concept, environment: Environment):
|
|
|
68
67
|
|
|
69
68
|
|
|
70
69
|
def generate_datetime_concepts(concept: Concept, environment: Environment):
|
|
70
|
+
factory = FunctionFactory(environment=environment)
|
|
71
71
|
if concept.metadata and concept.metadata.description:
|
|
72
72
|
base_description = concept.metadata.description
|
|
73
73
|
else:
|
|
@@ -88,11 +88,10 @@ def generate_datetime_concepts(concept: Concept, environment: Environment):
|
|
|
88
88
|
if concept.purpose == Purpose.CONSTANT
|
|
89
89
|
else Purpose.PROPERTY
|
|
90
90
|
)
|
|
91
|
-
|
|
91
|
+
|
|
92
|
+
const_function = factory.create_function(
|
|
92
93
|
operator=ftype,
|
|
93
|
-
|
|
94
|
-
output_purpose=default_type,
|
|
95
|
-
arguments=[concept],
|
|
94
|
+
args=[concept],
|
|
96
95
|
)
|
|
97
96
|
new_concept = Concept(
|
|
98
97
|
name=f"{concept.name}.{fname}",
|
trilogy/core/functions.py
CHANGED
|
@@ -43,7 +43,9 @@ class FunctionConfig:
|
|
|
43
43
|
arg_count: int = 1
|
|
44
44
|
valid_inputs: set[DataType] | list[set[DataType]] | None = None
|
|
45
45
|
output_purpose: Purpose | None = None
|
|
46
|
-
output_type:
|
|
46
|
+
output_type: (
|
|
47
|
+
DataType | ListType | MapType | StructType | NumericType | TraitDataType | None
|
|
48
|
+
) = None
|
|
47
49
|
output_type_function: Optional[Callable] = None
|
|
48
50
|
|
|
49
51
|
|
|
@@ -124,6 +126,30 @@ def create_struct_output(
|
|
|
124
126
|
return StructType(fields=types, fields_map=zipped)
|
|
125
127
|
|
|
126
128
|
|
|
129
|
+
def get_date_part_output(args: list[Any]):
|
|
130
|
+
target = args[1]
|
|
131
|
+
if target == DatePart.YEAR:
|
|
132
|
+
return TraitDataType(type=DataType.INTEGER, traits=["year"])
|
|
133
|
+
elif target == DatePart.MONTH:
|
|
134
|
+
return TraitDataType(type=DataType.INTEGER, traits=["month"])
|
|
135
|
+
elif target == DatePart.DAY:
|
|
136
|
+
return TraitDataType(type=DataType.INTEGER, traits=["day"])
|
|
137
|
+
elif target == DatePart.HOUR:
|
|
138
|
+
return TraitDataType(type=DataType.INTEGER, traits=["hour"])
|
|
139
|
+
elif target == DatePart.MINUTE:
|
|
140
|
+
return TraitDataType(type=DataType.INTEGER, traits=["minute"])
|
|
141
|
+
elif target == DatePart.SECOND:
|
|
142
|
+
return TraitDataType(type=DataType.INTEGER, traits=["second"])
|
|
143
|
+
elif target == DatePart.WEEK:
|
|
144
|
+
return TraitDataType(type=DataType.INTEGER, traits=["week"])
|
|
145
|
+
elif target == DatePart.QUARTER:
|
|
146
|
+
return TraitDataType(type=DataType.INTEGER, traits=["quarter"])
|
|
147
|
+
elif target == DatePart.DAY_OF_WEEK:
|
|
148
|
+
return TraitDataType(type=DataType.INTEGER, traits=["day_of_week"])
|
|
149
|
+
else:
|
|
150
|
+
raise InvalidSyntaxException(f"Date part not supported for {target}")
|
|
151
|
+
|
|
152
|
+
|
|
127
153
|
def get_date_trunc_output(
|
|
128
154
|
args: list[Any],
|
|
129
155
|
):
|
|
@@ -360,7 +386,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
360
386
|
{DataType.DATE_PART},
|
|
361
387
|
],
|
|
362
388
|
output_purpose=Purpose.PROPERTY,
|
|
363
|
-
|
|
389
|
+
output_type_function=get_date_part_output,
|
|
364
390
|
arg_count=2,
|
|
365
391
|
),
|
|
366
392
|
FunctionType.DATE_ADD: FunctionConfig(
|
|
@@ -443,7 +469,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
443
469
|
DataType.STRING,
|
|
444
470
|
},
|
|
445
471
|
output_purpose=Purpose.PROPERTY,
|
|
446
|
-
output_type=DataType.INTEGER,
|
|
472
|
+
output_type=TraitDataType(type=DataType.INTEGER, traits=["second"]),
|
|
447
473
|
arg_count=1,
|
|
448
474
|
),
|
|
449
475
|
FunctionType.MINUTE: FunctionConfig(
|
|
@@ -454,7 +480,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
454
480
|
DataType.STRING,
|
|
455
481
|
},
|
|
456
482
|
output_purpose=Purpose.PROPERTY,
|
|
457
|
-
output_type=DataType.INTEGER,
|
|
483
|
+
output_type=TraitDataType(type=DataType.INTEGER, traits=["minute"]),
|
|
458
484
|
arg_count=1,
|
|
459
485
|
),
|
|
460
486
|
FunctionType.HOUR: FunctionConfig(
|
|
@@ -465,7 +491,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
465
491
|
DataType.STRING,
|
|
466
492
|
},
|
|
467
493
|
output_purpose=Purpose.PROPERTY,
|
|
468
|
-
output_type=DataType.INTEGER,
|
|
494
|
+
output_type=TraitDataType(type=DataType.INTEGER, traits=["hour"]),
|
|
469
495
|
arg_count=1,
|
|
470
496
|
),
|
|
471
497
|
FunctionType.DAY: FunctionConfig(
|
|
@@ -476,7 +502,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
476
502
|
DataType.STRING,
|
|
477
503
|
},
|
|
478
504
|
output_purpose=Purpose.PROPERTY,
|
|
479
|
-
output_type=DataType.INTEGER,
|
|
505
|
+
output_type=TraitDataType(type=DataType.INTEGER, traits=["day"]),
|
|
480
506
|
arg_count=1,
|
|
481
507
|
),
|
|
482
508
|
FunctionType.WEEK: FunctionConfig(
|
|
@@ -487,7 +513,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
487
513
|
DataType.STRING,
|
|
488
514
|
},
|
|
489
515
|
output_purpose=Purpose.PROPERTY,
|
|
490
|
-
output_type=DataType.INTEGER,
|
|
516
|
+
output_type=TraitDataType(type=DataType.INTEGER, traits=["week"]),
|
|
491
517
|
arg_count=1,
|
|
492
518
|
),
|
|
493
519
|
FunctionType.MONTH: FunctionConfig(
|
|
@@ -498,7 +524,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
498
524
|
DataType.STRING,
|
|
499
525
|
},
|
|
500
526
|
output_purpose=Purpose.PROPERTY,
|
|
501
|
-
output_type=DataType.INTEGER,
|
|
527
|
+
output_type=TraitDataType(type=DataType.INTEGER, traits=["month"]),
|
|
502
528
|
arg_count=1,
|
|
503
529
|
),
|
|
504
530
|
FunctionType.QUARTER: FunctionConfig(
|
|
@@ -509,7 +535,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
509
535
|
DataType.STRING,
|
|
510
536
|
},
|
|
511
537
|
output_purpose=Purpose.PROPERTY,
|
|
512
|
-
output_type=DataType.INTEGER,
|
|
538
|
+
output_type=TraitDataType(type=DataType.INTEGER, traits=["quarter"]),
|
|
513
539
|
arg_count=1,
|
|
514
540
|
),
|
|
515
541
|
FunctionType.YEAR: FunctionConfig(
|
|
@@ -520,7 +546,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
520
546
|
DataType.STRING,
|
|
521
547
|
},
|
|
522
548
|
output_purpose=Purpose.PROPERTY,
|
|
523
|
-
output_type=DataType.INTEGER,
|
|
549
|
+
output_type=TraitDataType(type=DataType.INTEGER, traits=["year"]),
|
|
524
550
|
arg_count=1,
|
|
525
551
|
),
|
|
526
552
|
FunctionType.DAY_OF_WEEK: FunctionConfig(
|
|
@@ -531,7 +557,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
531
557
|
DataType.STRING,
|
|
532
558
|
},
|
|
533
559
|
output_purpose=Purpose.PROPERTY,
|
|
534
|
-
output_type=DataType.INTEGER,
|
|
560
|
+
output_type=TraitDataType(type=DataType.INTEGER, traits=["day_of_week"]),
|
|
535
561
|
arg_count=1,
|
|
536
562
|
),
|
|
537
563
|
FunctionType.ADD: FunctionConfig(
|
trilogy/core/models/author.py
CHANGED
|
@@ -255,7 +255,7 @@ class Parenthetical(
|
|
|
255
255
|
return arg_to_datatype(self.content)
|
|
256
256
|
|
|
257
257
|
|
|
258
|
-
class Conditional(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
258
|
+
class Conditional(Mergeable, ConceptArgs, Namespaced, DataTyped, BaseModel):
|
|
259
259
|
left: Expr
|
|
260
260
|
right: Expr
|
|
261
261
|
operator: BooleanOperator
|
|
@@ -357,6 +357,11 @@ class Conditional(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
357
357
|
output += self.right.existence_arguments
|
|
358
358
|
return output
|
|
359
359
|
|
|
360
|
+
@property
|
|
361
|
+
def output_datatype(self):
|
|
362
|
+
# a conditional is always a boolean
|
|
363
|
+
return DataType.BOOL
|
|
364
|
+
|
|
360
365
|
def decompose(self):
|
|
361
366
|
chunks = []
|
|
362
367
|
if self.operator == BooleanOperator.AND:
|
|
@@ -545,7 +550,7 @@ class Grain(Namespaced, BaseModel):
|
|
|
545
550
|
return self.__add__(other)
|
|
546
551
|
|
|
547
552
|
|
|
548
|
-
class Comparison(ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
553
|
+
class Comparison(ConceptArgs, Mergeable, DataTyped, Namespaced, BaseModel):
|
|
549
554
|
left: Union[
|
|
550
555
|
int,
|
|
551
556
|
str,
|
|
@@ -745,6 +750,11 @@ class Comparison(ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
745
750
|
output += self.right.existence_arguments
|
|
746
751
|
return output
|
|
747
752
|
|
|
753
|
+
@property
|
|
754
|
+
def output_datatype(self):
|
|
755
|
+
# a conditional is always a boolean
|
|
756
|
+
return DataType.BOOL
|
|
757
|
+
|
|
748
758
|
|
|
749
759
|
class SubselectComparison(Comparison):
|
|
750
760
|
def __eq__(self, other):
|
|
@@ -1401,7 +1411,7 @@ def get_basic_type(
|
|
|
1401
1411
|
if isinstance(type, NumericType):
|
|
1402
1412
|
return DataType.NUMERIC
|
|
1403
1413
|
if isinstance(type, TraitDataType):
|
|
1404
|
-
return type.type
|
|
1414
|
+
return get_basic_type(type.type)
|
|
1405
1415
|
return type
|
|
1406
1416
|
|
|
1407
1417
|
|
trilogy/core/models/core.py
CHANGED
|
@@ -105,7 +105,7 @@ class DataType(Enum):
|
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
class TraitDataType(BaseModel):
|
|
108
|
-
type: DataType
|
|
108
|
+
type: DataType | NumericType | StructType | ListType | MapType
|
|
109
109
|
traits: list[str]
|
|
110
110
|
|
|
111
111
|
def __hash__(self):
|
|
@@ -134,6 +134,9 @@ class NumericType(BaseModel):
|
|
|
134
134
|
precision: int = 20
|
|
135
135
|
scale: int = 5
|
|
136
136
|
|
|
137
|
+
def __str__(self) -> str:
|
|
138
|
+
return f"Numeric({self.precision},{self.scale})"
|
|
139
|
+
|
|
137
140
|
@property
|
|
138
141
|
def data_type(self):
|
|
139
142
|
return DataType.NUMERIC
|
|
@@ -356,12 +359,21 @@ def merge_datatypes(
|
|
|
356
359
|
|
|
357
360
|
def is_compatible_datatype(left, right):
|
|
358
361
|
# for unknown types, we can't make any assumptions
|
|
362
|
+
if all(
|
|
363
|
+
isinstance(x, NumericType)
|
|
364
|
+
or x in (DataType.INTEGER, DataType.FLOAT, DataType.NUMERIC)
|
|
365
|
+
for x in (left, right)
|
|
366
|
+
):
|
|
367
|
+
return True
|
|
368
|
+
elif isinstance(left, NumericType) or isinstance(right, NumericType):
|
|
369
|
+
return False
|
|
359
370
|
if right == DataType.UNKNOWN or left == DataType.UNKNOWN:
|
|
360
371
|
return True
|
|
361
372
|
if left == right:
|
|
362
373
|
return True
|
|
363
374
|
if {left, right} == {DataType.NUMERIC, DataType.FLOAT}:
|
|
364
375
|
return True
|
|
376
|
+
|
|
365
377
|
if {left, right} == {DataType.NUMERIC, DataType.INTEGER}:
|
|
366
378
|
return True
|
|
367
379
|
if {left, right} == {DataType.FLOAT, DataType.INTEGER}:
|
trilogy/dialect/base.py
CHANGED
|
@@ -680,7 +680,7 @@ class BaseDialect:
|
|
|
680
680
|
elif isinstance(e, datetime):
|
|
681
681
|
return self.FUNCTION_MAP[FunctionType.DATETIME_LITERAL](e)
|
|
682
682
|
elif isinstance(e, TraitDataType):
|
|
683
|
-
return self.
|
|
683
|
+
return self.render_expr(e.type, cte=cte, cte_map=cte_map)
|
|
684
684
|
else:
|
|
685
685
|
raise ValueError(f"Unable to render type {type(e)} {e}")
|
|
686
686
|
|
trilogy/parsing/parse_engine.py
CHANGED
|
@@ -91,6 +91,7 @@ from trilogy.core.models.core import (
|
|
|
91
91
|
TupleWrapper,
|
|
92
92
|
arg_to_datatype,
|
|
93
93
|
dict_to_map_wrapper,
|
|
94
|
+
is_compatible_datatype,
|
|
94
95
|
list_to_wrapper,
|
|
95
96
|
tuple_to_wrapper,
|
|
96
97
|
)
|
|
@@ -146,6 +147,8 @@ SELF_LABEL = "root"
|
|
|
146
147
|
|
|
147
148
|
MAX_PARSE_DEPTH = 10
|
|
148
149
|
|
|
150
|
+
STDLIB_ROOT = Path(__file__).parent.parent
|
|
151
|
+
|
|
149
152
|
|
|
150
153
|
@dataclass
|
|
151
154
|
class WholeGrainWrapper:
|
|
@@ -449,20 +452,27 @@ class ParseToObjects(Transformer):
|
|
|
449
452
|
) -> DataType | TraitDataType | ListType | StructType | MapType | NumericType:
|
|
450
453
|
resolved = args[0]
|
|
451
454
|
traits = args[2:]
|
|
455
|
+
base: DataType | TraitDataType | ListType | StructType | MapType | NumericType
|
|
452
456
|
if isinstance(resolved, StructType):
|
|
453
|
-
|
|
457
|
+
base = resolved
|
|
454
458
|
elif isinstance(resolved, ListType):
|
|
455
|
-
|
|
459
|
+
base = resolved
|
|
456
460
|
elif isinstance(resolved, NumericType):
|
|
457
|
-
|
|
461
|
+
base = resolved
|
|
458
462
|
elif isinstance(resolved, MapType):
|
|
459
|
-
|
|
460
|
-
|
|
463
|
+
base = resolved
|
|
464
|
+
else:
|
|
465
|
+
base = DataType(args[0].lower())
|
|
461
466
|
if traits:
|
|
462
467
|
for trait in traits:
|
|
463
468
|
if trait not in self.environment.data_types:
|
|
464
469
|
raise ParseError(
|
|
465
|
-
f"Invalid type
|
|
470
|
+
f"Invalid trait (type) {trait} for {base}, line {meta.line}."
|
|
471
|
+
)
|
|
472
|
+
matched = self.environment.data_types[trait]
|
|
473
|
+
if not is_compatible_datatype(matched.type, base):
|
|
474
|
+
raise ParseError(
|
|
475
|
+
f"Invalid trait (type) {trait} for {base}, line {meta.line}. Trait expects type {matched.type}, has {base}"
|
|
466
476
|
)
|
|
467
477
|
return TraitDataType(type=base, traits=traits)
|
|
468
478
|
|
|
@@ -936,9 +946,12 @@ class ParseToObjects(Transformer):
|
|
|
936
946
|
select=args[-1],
|
|
937
947
|
)
|
|
938
948
|
|
|
939
|
-
def resolve_import_address(self, address) -> str:
|
|
940
|
-
if
|
|
941
|
-
|
|
949
|
+
def resolve_import_address(self, address, is_stdlib: bool = False) -> str:
|
|
950
|
+
if (
|
|
951
|
+
isinstance(
|
|
952
|
+
self.environment.config.import_resolver, FileSystemImportResolver
|
|
953
|
+
)
|
|
954
|
+
or is_stdlib
|
|
942
955
|
):
|
|
943
956
|
with open(address, "r", encoding="utf-8") as f:
|
|
944
957
|
text = f.read()
|
|
@@ -946,7 +959,7 @@ class ParseToObjects(Transformer):
|
|
|
946
959
|
lookup = address
|
|
947
960
|
if lookup not in self.environment.config.import_resolver.content:
|
|
948
961
|
raise ImportError(
|
|
949
|
-
f"Unable to import file {lookup}, not found in
|
|
962
|
+
f"Unable to import file {lookup}, not found in imsport resolver"
|
|
950
963
|
)
|
|
951
964
|
text = self.environment.config.import_resolver.content[lookup]
|
|
952
965
|
else:
|
|
@@ -964,13 +977,17 @@ class ParseToObjects(Transformer):
|
|
|
964
977
|
cache_key = args[0]
|
|
965
978
|
input_path = args[0]
|
|
966
979
|
path = input_path.split(".")
|
|
967
|
-
|
|
968
|
-
if
|
|
980
|
+
is_stdlib = False
|
|
981
|
+
if path[0] == "std":
|
|
982
|
+
is_stdlib = True
|
|
983
|
+
target = join(STDLIB_ROOT, *path) + ".preql"
|
|
984
|
+
token_lookup: Path | str = Path(target)
|
|
985
|
+
elif isinstance(
|
|
969
986
|
self.environment.config.import_resolver, FileSystemImportResolver
|
|
970
987
|
):
|
|
971
988
|
target = join(self.environment.working_path, *path) + ".preql"
|
|
972
989
|
# tokens + text are cached by path
|
|
973
|
-
token_lookup
|
|
990
|
+
token_lookup = Path(target)
|
|
974
991
|
elif isinstance(self.environment.config.import_resolver, DictImportResolver):
|
|
975
992
|
target = ".".join(path)
|
|
976
993
|
token_lookup = target
|
|
@@ -991,7 +1008,7 @@ class ParseToObjects(Transformer):
|
|
|
991
1008
|
raw_tokens = self.tokens[token_lookup]
|
|
992
1009
|
text = self.text_lookup[token_lookup]
|
|
993
1010
|
else:
|
|
994
|
-
text = self.resolve_import_address(target)
|
|
1011
|
+
text = self.resolve_import_address(target, is_stdlib)
|
|
995
1012
|
self.text_lookup[token_lookup] = text
|
|
996
1013
|
|
|
997
1014
|
try:
|
trilogy/parsing/trilogy.lark
CHANGED
|
@@ -287,7 +287,7 @@
|
|
|
287
287
|
_YEAR.1: "year("i
|
|
288
288
|
fyear: _YEAR expr ")"
|
|
289
289
|
|
|
290
|
-
DATE_PART: "DAY"i | "WEEK"i | "MONTH"i | "QUARTER"i | "YEAR"i | "MINUTE"i | "HOUR"i | "SECOND"i
|
|
290
|
+
DATE_PART: "DAY"i | "WEEK"i | "MONTH"i | "QUARTER"i | "YEAR"i | "MINUTE"i | "HOUR"i | "SECOND"i | "DAY_OF_WEEK"i
|
|
291
291
|
_DATE_TRUNC.1: "date_trunc("i
|
|
292
292
|
fdate_trunc: _DATE_TRUNC expr "," DATE_PART ")"
|
|
293
293
|
_DATE_PART.1: "date_part("i
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|