pytrilogy 0.0.3.117__py3-none-any.whl → 0.0.3.120__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.117
3
+ Version: 0.0.3.120
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Classifier: Programming Language :: Python
6
6
  Classifier: Programming Language :: Python :: 3
@@ -1,5 +1,5 @@
1
- pytrilogy-0.0.3.117.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
2
- trilogy/__init__.py,sha256=TkCDQL8X9qEaoFjTuzamNRfxRiggkAyy6htNlnHQ6XU,304
1
+ pytrilogy-0.0.3.120.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
2
+ trilogy/__init__.py,sha256=BF_I6R5zTDgQVvWrf-0TvooZTV3UV4ex98-KtPY70ak,304
3
3
  trilogy/constants.py,sha256=_Tm7YGaAZuxH77X5ve0TajU0dQD7RcGV6ECrTHRL3qQ,2678
4
4
  trilogy/engine.py,sha256=v4TpNktM4zZ9OX7jZH2nde4dpX5uAH2U23ELfULTCSg,2280
5
5
  trilogy/executor.py,sha256=uKlCnPp4FHkgsa_dDcQJ4y-ObtvKat2KFx05c-z1mZo,17885
@@ -9,7 +9,7 @@ trilogy/render.py,sha256=qQWwduymauOlB517UtM-VGbVe8Cswa4UJub5aGbSO6c,1512
9
9
  trilogy/utility.py,sha256=euQccZLKoYBz0LNg5tzLlvv2YHvXh9HArnYp1V3uXsM,763
10
10
  trilogy/ai/__init__.py,sha256=H6gpzScruX2xgZNRDMjQ31Wy45irJbdebX1fU_gOwI8,581
11
11
  trilogy/ai/constants.py,sha256=Aj-_mFqskcXqIlBjX_A9eqH0V9M8mqX3uJwUhr9puak,5064
12
- trilogy/ai/conversation.py,sha256=I11xmUZikuKmh-W-jt38OvtyhpHwhpQ6Eeut6dkjI-c,3467
12
+ trilogy/ai/conversation.py,sha256=yei5iKsVCyo-GRmHtfto2grCjBs_TwAbGChD3w97pqg,3482
13
13
  trilogy/ai/enums.py,sha256=vghPPx0W-DioQSgq4T0MGL-8ekFh6O6d52dHo7KsKtg,118
14
14
  trilogy/ai/execute.py,sha256=DTARZxm_btCJq4Yd_jPRHJAcbsMLbjEsjR7KKyKBkTI,1335
15
15
  trilogy/ai/models.py,sha256=Au4QnTIlv7e-p3XgTJYZqTSndPMGRIbOvCUWlekE81A,683
@@ -28,19 +28,19 @@ trilogy/core/env_processor.py,sha256=H-rr2ALj31l5oh3FqeI47Qju6OOfiXBacXNJGNZ92zQ
28
28
  trilogy/core/environment_helpers.py,sha256=TRlqVctqIRBxzfjRBmpQsAVoiCcsEKBhG1B6PUE0l1M,12743
29
29
  trilogy/core/ergonomics.py,sha256=e-7gE29vPLFdg0_A1smQ7eOrUwKl5VYdxRSTddHweRA,1631
30
30
  trilogy/core/exceptions.py,sha256=axkVXYJYQXCCwMHwlyDA232g4tCOwdCZUt7eHeUMDMg,2829
31
- trilogy/core/functions.py,sha256=QidWH7dDjvLjXA0ujCkemUzYjkYmtMD2T78hnvk86K8,34876
31
+ trilogy/core/functions.py,sha256=yvy4t1pZmsdTpcP5gxPIpS2G2ULNotmmF7HZcYDH6bE,35857
32
32
  trilogy/core/graph_models.py,sha256=4EWFTHGfYd72zvS2HYoV6hm7nMC_VEd7vWr6txY-ig0,3400
33
33
  trilogy/core/internal.py,sha256=r9QagDB2GvpqlyD_I7VrsfbVfIk5mnok2znEbv72Aa4,2681
34
34
  trilogy/core/optimization.py,sha256=eKieOaWXUtoNTVQbThGA5tqrI06ZR6SUFOqGe4Jw0k4,9262
35
35
  trilogy/core/query_processor.py,sha256=rMrtLSQxVm7yeyh0nWjDNI9nnu4Xi0NgHvBJ14gvu4I,20384
36
36
  trilogy/core/utility.py,sha256=3VC13uSQWcZNghgt7Ot0ZTeEmNqs__cx122abVq9qhM,410
37
37
  trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
- trilogy/core/models/author.py,sha256=YnBwxanbn9uBAExz5POM0_RFw9GIHmjfLZRVzA10Gws,85862
38
+ trilogy/core/models/author.py,sha256=gsKPOwgnebnAs2HV72ZHdXYfoUzDs8jQNse8vEwtG3k,86081
39
39
  trilogy/core/models/build.py,sha256=zVHD8jo8V8E0aGWtobzXG7hY3VjLChiW4s1QBx9sD5k,72938
40
40
  trilogy/core/models/build_environment.py,sha256=mpx7MKGc60fnZLVdeLi2YSREy7eQbQYycCrP4zF-rHU,5258
41
- trilogy/core/models/core.py,sha256=iT9WdZoiXeglmUHWn6bZyXCTBpkApTGPKtNm_Mhbu_g,12987
41
+ trilogy/core/models/core.py,sha256=mv1QOxf-6uGoWpfwuucNAW3W_ZZGQuxtnPWAXhQsxSI,13290
42
42
  trilogy/core/models/datasource.py,sha256=wogTevZ-9CyUW2a8gjzqMCieircxi-J5lkI7EOAZnck,9596
43
- trilogy/core/models/environment.py,sha256=-r6_imqkJ10iPTbWGrdfeF2Th480UKLsG8ihACyqO8w,28823
43
+ trilogy/core/models/environment.py,sha256=m2NZ-UqmHQ1qJo6I-cVGG9Z24O6RTDkrvEd1IfVmWls,28889
44
44
  trilogy/core/models/execute.py,sha256=3fgEdho2e7S0outq91cCzb9jFwz6L1hTbsTrJwGvIFs,42311
45
45
  trilogy/core/optimizations/__init__.py,sha256=yspWc25M5SgAuvXYoSt5J8atyPbDlOfsKjIo5yGD9s4,368
46
46
  trilogy/core/optimizations/base_optimization.py,sha256=gzDOKImoFn36k7XBD3ysEYDnbnb6vdVIztUfFQZsGnM,513
@@ -50,7 +50,7 @@ trilogy/core/optimizations/predicate_pushdown.py,sha256=5ubatgq1IwWQ4L2FDt4--y16
50
50
  trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
51
  trilogy/core/processing/concept_strategies_v3.py,sha256=MBQeJGBDW2w3xxwano43-5MVBqfbIU9M3K7RFbnSKNA,23367
52
52
  trilogy/core/processing/discovery_node_factory.py,sha256=llnLxZo1NqBRIuuPz0GUohym6LZFhVkPT3xSiORi3k4,15446
53
- trilogy/core/processing/discovery_utility.py,sha256=mO0npZMRlQSzxt3l4m8garKBAOrXFkzt3eiiUyUSoIU,13528
53
+ trilogy/core/processing/discovery_utility.py,sha256=FpCHCvGslHIJkMIOLBmTJRQKfUtwcwiNgzDxcOsJBIc,13532
54
54
  trilogy/core/processing/discovery_validation.py,sha256=eZ4HfHMpqZLI8MGG2jez8arS8THs6ceuVrQFIY6gXrU,5364
55
55
  trilogy/core/processing/graph_utils.py,sha256=8QUVrkE9j-9C1AyrCb1nQEh8daCe0u1HuXl-Te85lag,1205
56
56
  trilogy/core/processing/utility.py,sha256=ESs6pKqVP2c9eMdfB2JNjw7D7YnoezVwbLFx1D6OUYA,26088
@@ -74,7 +74,7 @@ trilogy/core/processing/node_generators/window_node.py,sha256=wNvmumGO6AIQ7C9bDU
74
74
  trilogy/core/processing/node_generators/select_helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
75
  trilogy/core/processing/node_generators/select_helpers/datasource_injection.py,sha256=m2YQ4OmG0N2O61a7NEq1ZzbTa7JsCC00lxB2ymjcYRI,8224
76
76
  trilogy/core/processing/nodes/__init__.py,sha256=zTge1EzwzEydlcMliIFO_TT7h7lS8l37lyZuQDir1h0,5487
77
- trilogy/core/processing/nodes/base_node.py,sha256=k4Z9qdL9BXxxHH66L5udQwSDcYeiaWEn3bRmy84SShs,18559
77
+ trilogy/core/processing/nodes/base_node.py,sha256=xrtijyy38xpTt3hzVdEVaDYhk1JWAuB8yGoDJ6R82Jg,18722
78
78
  trilogy/core/processing/nodes/filter_node.py,sha256=5VtRfKbCORx0dV-vQfgy3gOEkmmscL9f31ExvlODwvY,2461
79
79
  trilogy/core/processing/nodes/group_node.py,sha256=Ku8El9KQvRiTiHCZDS_jX0DjErSDNv7IIQMcd1Gsk7I,7449
80
80
  trilogy/core/processing/nodes/merge_node.py,sha256=4y_itKoipHKjpCIQjK9SHga-Fq-HqyeQLwAoSIFQ1hM,16567
@@ -117,9 +117,9 @@ trilogy/parsing/common.py,sha256=GijDRpysULL6vQWpFcjgxVASuTWXUVUi5fILHvjzkbg,355
117
117
  trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
118
118
  trilogy/parsing/exceptions.py,sha256=Xwwsv2C9kSNv2q-HrrKC1f60JNHShXcCMzstTSEbiCw,154
119
119
  trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
120
- trilogy/parsing/parse_engine.py,sha256=oCHQz5JaWvOu6u3IkpBWC8FwWCpDt0GNat5fxi9x0Eo,86742
120
+ trilogy/parsing/parse_engine.py,sha256=j0lBbJGObcptw4S7xng2hy-Pv-u5aSfFuxYk6TZvciY,87661
121
121
  trilogy/parsing/render.py,sha256=k7MNp8EBTqVBSVqFlgTHSwIhfSKLyJfSeb2fSbt9dVA,24307
122
- trilogy/parsing/trilogy.lark,sha256=mbwwPlz__xO3ydLXXSum0zZK8k8QyifPTJdyNyMDlSY,17280
122
+ trilogy/parsing/trilogy.lark,sha256=_FW9Hq7Xo6KDrf1JUxD9NBM0Zn9OyoZzWHj97zFqy-M,17449
123
123
  trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
124
124
  trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
125
125
  trilogy/std/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -130,10 +130,11 @@ trilogy/std/geography.preql,sha256=1A9Sq5PPMBnEPPf7f-rPVYxJfsnWpQ8oV_k4Fm3H2dU,6
130
130
  trilogy/std/metric.preql,sha256=DRECGhkMyqfit5Fl4Ut9zbWrJuSMI1iO9HikuyoBpE0,421
131
131
  trilogy/std/money.preql,sha256=XWwvAV3WxBsHX9zfptoYRnBigcfYwrYtBHXTME0xJuQ,2082
132
132
  trilogy/std/net.preql,sha256=WZCuvH87_rZntZiuGJMmBDMVKkdhTtxeHOkrXNwJ1EE,416
133
- trilogy/std/ranking.preql,sha256=LDoZrYyz4g3xsII9XwXfmstZD-_92i1Eox1UqkBIfi8,83
133
+ trilogy/std/ranking.preql,sha256=zDdmHcTerlCjaHDdVyjGTgcc2hAhnlDM_q5tiw0MPGE,108
134
134
  trilogy/std/report.preql,sha256=LbV-XlHdfw0jgnQ8pV7acG95xrd1-p65fVpiIc-S7W4,202
135
- pytrilogy-0.0.3.117.dist-info/METADATA,sha256=abcEvZwmW_1-WWPIeGOqghBy9u2MD3Rh0xCMbxQzA78,12911
136
- pytrilogy-0.0.3.117.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
137
- pytrilogy-0.0.3.117.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
138
- pytrilogy-0.0.3.117.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
139
- pytrilogy-0.0.3.117.dist-info/RECORD,,
135
+ trilogy/std/semantic.preql,sha256=k9_k672nUb5MKjSD1DW5jjn_odoYHl0yNeRPOjFabsE,95
136
+ pytrilogy-0.0.3.120.dist-info/METADATA,sha256=stpvqxUMDCp2V1Kb5HP-xcjX_H-IscwNLpfDBbFoXL0,12911
137
+ pytrilogy-0.0.3.120.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
138
+ pytrilogy-0.0.3.120.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
139
+ pytrilogy-0.0.3.120.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
140
+ pytrilogy-0.0.3.120.dist-info/RECORD,,
trilogy/__init__.py CHANGED
@@ -4,6 +4,6 @@ from trilogy.dialect.enums import Dialects
4
4
  from trilogy.executor import Executor
5
5
  from trilogy.parser import parse
6
6
 
7
- __version__ = "0.0.3.117"
7
+ __version__ = "0.0.3.120"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -61,7 +61,7 @@ class Conversation:
61
61
  if "```" in content:
62
62
  parts = content.split("```")
63
63
  if len(parts) >= 3:
64
- return parts[1].strip()
64
+ return parts[-2].strip()
65
65
  return content
66
66
 
67
67
  def generate_query(
@@ -78,7 +78,7 @@ class Conversation:
78
78
  if not response.strip()[-1] == ";":
79
79
  response += ";"
80
80
  try:
81
- env, raw = environment.parse(response)
81
+ _, raw = environment.parse(response)
82
82
  process_query(statement=raw[-1], environment=environment)
83
83
  return response
84
84
  except (
@@ -90,10 +90,9 @@ class Conversation:
90
90
  ) as e2:
91
91
  e = e2
92
92
  self.add_message(
93
- f"The previous response could not be parsed due to the error: {str(e)}. Please generate a new query with the issues fixed. Use the same response format.",
93
+ f"Your extracted response - {response} - could not be parsed due to the error: {str(e)}. Please generate a new query with the issues fixed. Use the same response format.",
94
94
  role="user",
95
95
  )
96
-
97
96
  raise Exception(
98
97
  f"Failed to generate a valid query after {attempts} attempts. Last error: {str(e)}. Full conversation: {self.messages}"
99
98
  )
trilogy/core/functions.py CHANGED
@@ -19,6 +19,7 @@ from trilogy.core.models.author import (
19
19
  Concept,
20
20
  ConceptRef,
21
21
  Conditional,
22
+ CustomType,
22
23
  Function,
23
24
  Parenthetical,
24
25
  UndefinedConcept,
@@ -40,6 +41,11 @@ from trilogy.core.models.environment import Environment
40
41
  GENERIC_ARGS = Concept | ConceptRef | Function | str | int | float | date | datetime
41
42
 
42
43
 
44
+ CUSTOM_PLACEHOLDER = CustomType(
45
+ name="__placeholder__", type=DataType.UNKNOWN, drop_on=[], add_on=[]
46
+ )
47
+
48
+
43
49
  @dataclass
44
50
  class FunctionConfig:
45
51
  arg_count: int = 1
@@ -99,7 +105,18 @@ def get_attr_datatype(
99
105
 
100
106
  def get_cast_output_type(
101
107
  args: list[Any],
102
- ) -> DataType:
108
+ ) -> DataType | TraitDataType:
109
+ base = arg_to_datatype(args[0])
110
+ if isinstance(base, TraitDataType):
111
+ traits = base.traits
112
+ else:
113
+ traits = []
114
+ if isinstance(args[1], TraitDataType):
115
+ return TraitDataType(
116
+ type=args[1].type, traits=list(set(traits + args[1].traits))
117
+ )
118
+ elif traits:
119
+ return TraitDataType(type=args[1], traits=traits)
103
120
  return args[1]
104
121
 
105
122
 
@@ -995,6 +1012,19 @@ class FunctionFactory:
995
1012
  final_output_type = base_output_type
996
1013
  else:
997
1014
  raise SyntaxError(f"Could not determine output type for {operator}")
1015
+ if isinstance(final_output_type, TraitDataType) and self.environment:
1016
+ final_output_type = TraitDataType(
1017
+ type=final_output_type.type,
1018
+ traits=[
1019
+ x
1020
+ for x in final_output_type.traits
1021
+ if operator
1022
+ not in self.environment.data_types.get(
1023
+ x, CUSTOM_PLACEHOLDER
1024
+ ).drop_on
1025
+ ],
1026
+ )
1027
+
998
1028
  if not output_purpose:
999
1029
  if operator in FunctionClass.AGGREGATE_FUNCTIONS.value:
1000
1030
  output_purpose = Purpose.METRIC
@@ -2598,11 +2598,16 @@ class ArgBinding(Namespaced, DataTyped, BaseModel):
2598
2598
 
2599
2599
  class CustomType(BaseModel):
2600
2600
  name: str
2601
- type: DataType
2601
+ type: DataType | list[DataType]
2602
+ drop_on: list[FunctionType] = Field(default_factory=list)
2603
+ add_on: list[FunctionType] = Field(default_factory=list)
2602
2604
 
2603
2605
  def with_namespace(self, namespace: str) -> "CustomType":
2604
2606
  return CustomType.model_construct(
2605
- name=address_with_namespace(self.name, namespace), type=self.type
2607
+ name=address_with_namespace(self.name, namespace),
2608
+ type=self.type,
2609
+ drop_on=self.drop_on,
2610
+ add_on=self.add_on,
2606
2611
  )
2607
2612
 
2608
2613
 
@@ -99,6 +99,7 @@ class DataType(Enum):
99
99
 
100
100
  # PARSING
101
101
  UNKNOWN = "unknown"
102
+ ANY = "any"
102
103
 
103
104
  @property
104
105
  def data_type(self):
@@ -411,6 +412,12 @@ def merge_datatypes(
411
412
 
412
413
  def is_compatible_datatype(left, right):
413
414
  # for unknown types, we can't make any assumptions
415
+ if isinstance(left, list):
416
+ return any(is_compatible_datatype(ltype, right) for ltype in left)
417
+ if isinstance(right, list):
418
+ return any(is_compatible_datatype(left, rtype) for rtype in right)
419
+ if left == DataType.ANY or right == DataType.ANY:
420
+ return True
414
421
  if all(
415
422
  isinstance(x, NumericType)
416
423
  or x in (DataType.INTEGER, DataType.FLOAT, DataType.NUMERIC)
@@ -215,7 +215,7 @@ class Environment(BaseModel):
215
215
  functions: Dict[str, CustomFunctionFactory] = Field(default_factory=dict)
216
216
  data_types: Dict[str, CustomType] = Field(default_factory=dict)
217
217
  named_statements: Dict[str, SelectLineage] = Field(default_factory=dict)
218
- imports: Dict[str, list[Import]] = Field(
218
+ imports: defaultdict[str, list[Import]] = Field(
219
219
  default_factory=lambda: defaultdict(list) # type: ignore
220
220
  )
221
221
  namespace: str = DEFAULT_NAMESPACE
@@ -252,7 +252,7 @@ class Environment(BaseModel):
252
252
  concepts=self.concepts.duplicate(),
253
253
  functions=dict(self.functions),
254
254
  data_types=dict(self.data_types),
255
- imports=dict(self.imports),
255
+ imports=defaultdict(list, self.imports),
256
256
  namespace=self.namespace,
257
257
  working_path=self.working_path,
258
258
  environment_config=self.config.model_copy(deep=True),
@@ -262,6 +262,7 @@ class Environment(BaseModel):
262
262
  alias_origin_lookup={
263
263
  k: v.duplicate() for k, v in self.alias_origin_lookup.items()
264
264
  },
265
+ env_file_path=self.env_file_path,
265
266
  )
266
267
 
267
268
  def _add_path_concepts(self):
@@ -40,15 +40,15 @@ def calculate_effective_parent_grain(
40
40
  pairs = join.concept_pairs or []
41
41
  for key in pairs:
42
42
  left = key.existing_datasource
43
- logger.info(f"adding left grain {left.grain} for join key {key.left}")
43
+ logger.debug(f"adding left grain {left.grain} for join key {key.left}")
44
44
  grain += left.grain
45
45
  seen.add(left.name)
46
46
  keys = [key.right for key in pairs]
47
47
  join_grain = BuildGrain.from_concepts(keys)
48
48
  if join_grain == join.right_datasource.grain:
49
- logger.info(f"irrelevant right join {join}, does not change grain")
49
+ logger.debug(f"irrelevant right join {join}, does not change grain")
50
50
  else:
51
- logger.info(
51
+ logger.debug(
52
52
  f"join changes grain, adding {join.right_datasource.grain} to {grain}"
53
53
  )
54
54
  grain += join.right_datasource.grain
@@ -68,7 +68,7 @@ def calculate_effective_parent_grain(
68
68
  ]
69
69
  )
70
70
  ):
71
- logger.info(f"adding unjoined grain {x.grain} for datasource {x.name}")
71
+ logger.debug(f"adding unjoined grain {x.grain} for datasource {x.name}")
72
72
  grain += x.grain
73
73
  return grain
74
74
  else:
@@ -258,10 +258,14 @@ class StrategyNode:
258
258
  self.partial_lcl = LooseBuildConceptList(concepts=partials)
259
259
  return partials
260
260
 
261
- def add_output_concepts(self, concepts: List[BuildConcept], rebuild: bool = True):
261
+ def add_output_concepts(
262
+ self, concepts: List[BuildConcept], rebuild: bool = True, unhide: bool = True
263
+ ):
262
264
  for concept in concepts:
263
265
  if concept.address not in self.output_lcl.addresses:
264
266
  self.output_concepts.append(concept)
267
+ if unhide and concept.address in self.hidden_concepts:
268
+ self.hidden_concepts.remove(concept.address)
265
269
  self.output_lcl = LooseBuildConceptList(concepts=self.output_concepts)
266
270
  if rebuild:
267
271
  self.rebuild_cache()
@@ -171,6 +171,16 @@ class FunctionBindingType:
171
171
  type: DataType | TraitDataType | None = None
172
172
 
173
173
 
174
+ @dataclass
175
+ class DropOn:
176
+ functions: List[FunctionType]
177
+
178
+
179
+ @dataclass
180
+ class AddOn:
181
+ functions: List[FunctionType]
182
+
183
+
174
184
  with open(join(dirname(__file__), "trilogy.lark"), "r") as f:
175
185
  PARSER = Lark(
176
186
  f.read(),
@@ -1512,11 +1522,35 @@ class ParseToObjects(Transformer):
1512
1522
  def function(self, meta: Meta, args) -> Function:
1513
1523
  return args[0]
1514
1524
 
1525
+ @v_args(meta=True)
1526
+ def type_drop_clause(self, meta: Meta, args) -> DropOn:
1527
+ return DropOn([FunctionType(x) for x in args])
1528
+
1529
+ @v_args(meta=True)
1530
+ def type_add_clause(self, meta: Meta, args) -> AddOn:
1531
+ return AddOn([FunctionType(x) for x in args])
1532
+
1515
1533
  @v_args(meta=True)
1516
1534
  def type_declaration(self, meta: Meta, args) -> TypeDeclaration:
1517
1535
  key = args[0]
1518
- datatype = args[1]
1519
- new = CustomType(name=key, type=datatype)
1536
+ datatype: list[DataType] = [x for x in args[1:] if isinstance(x, DataType)]
1537
+ if len(datatype) == 1:
1538
+ final_datatype: list[DataType] | DataType = datatype[0]
1539
+ else:
1540
+ final_datatype = datatype
1541
+ add_on = None
1542
+ drop_on = None
1543
+ for x in args[1:]:
1544
+ if isinstance(x, AddOn):
1545
+ add_on = x
1546
+ elif isinstance(x, DropOn):
1547
+ drop_on = x
1548
+ new = CustomType(
1549
+ name=key,
1550
+ type=final_datatype,
1551
+ drop_on=drop_on.functions if drop_on else [],
1552
+ add_on=add_on.functions if add_on else [],
1553
+ )
1520
1554
  self.environment.data_types[key] = new
1521
1555
  return TypeDeclaration(type=new)
1522
1556
 
@@ -2349,7 +2383,7 @@ def inject_context_maker(pos: int, text: str, span: int = 40) -> str:
2349
2383
  rcap = ""
2350
2384
  # if it goes beyond the end of text, no ...
2351
2385
  # if it terminates on a space, no need for ...
2352
- if not after[-1].isspace() and not (end > len(text)):
2386
+ if after and not after[-1].isspace() and not (end > len(text)):
2353
2387
  rcap = "..."
2354
2388
  lcap = ""
2355
2389
  if start > 0 and not before[0].isspace():
@@ -2444,8 +2478,6 @@ def parse_text(
2444
2478
  else:
2445
2479
  new_pos = pos
2446
2480
  e.interactive_parser.feed_token(Token("IDENTIFIER", e.token.value))
2447
- next(e.interactive_parser.iter_parse())
2448
-
2449
2481
  raise _create_syntax_error(201, new_pos, text)
2450
2482
  except UnexpectedToken:
2451
2483
  pass
@@ -109,7 +109,9 @@
109
109
  raw_function: "def" IDENTIFIER "(" function_binding_list ")" "->" expr
110
110
 
111
111
  // TYPE blocks
112
- type_declaration: "type" IDENTIFIER data_type
112
+ type_drop_clause: "DROP" IDENTIFIER ("|" IDENTIFIER)*
113
+ //type_add_clause: "ADD" IDENTIFIER ("|" IDENTIFIER)* type_add_clause?
114
+ type_declaration: "type" IDENTIFIER data_type ("|" data_type)* type_drop_clause?
113
115
 
114
116
  // user_id where state = Mexico
115
117
  _filter_alt: (IDENTIFIER | literal | "(" expr ")") "?" conditional
trilogy/std/ranking.preql CHANGED
@@ -4,3 +4,4 @@ type score int;
4
4
  type position int;
5
5
  type index int;
6
6
  type grade int;
7
+ type letter_grade string;
@@ -0,0 +1,6 @@
1
+
2
+
3
+ type identifier any;
4
+ type flag any
5
+ drop sum|count|avg|add|subtract|multiply|divide|mod
6
+ ;