pytrilogy 0.0.3.29__tar.gz → 0.0.3.31__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.

Files changed (139) hide show
  1. {pytrilogy-0.0.3.29/pytrilogy.egg-info → pytrilogy-0.0.3.31}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/pytrilogy.egg-info/SOURCES.txt +1 -0
  4. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_user_functions.py +25 -1
  5. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/__init__.py +1 -1
  6. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/enums.py +3 -0
  7. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/functions.py +9 -1
  8. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/models/author.py +68 -2
  9. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/models/build.py +7 -0
  10. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/statements/author.py +4 -1
  11. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/dialect/base.py +1 -0
  12. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/parsing/common.py +8 -1
  13. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/parsing/parse_engine.py +13 -5
  14. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/parsing/render.py +31 -0
  15. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/parsing/trilogy.lark +4 -2
  16. pytrilogy-0.0.3.31/trilogy/std/display.preql +3 -0
  17. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/LICENSE.md +0 -0
  18. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/README.md +0 -0
  19. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/pyproject.toml +0 -0
  20. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/pytrilogy.egg-info/dependency_links.txt +0 -0
  21. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/pytrilogy.egg-info/entry_points.txt +0 -0
  22. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/pytrilogy.egg-info/requires.txt +0 -0
  23. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/pytrilogy.egg-info/top_level.txt +0 -0
  24. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/setup.cfg +0 -0
  25. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/setup.py +0 -0
  26. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_datatypes.py +0 -0
  27. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_declarations.py +0 -0
  28. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_derived_concepts.py +0 -0
  29. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_discovery_nodes.py +0 -0
  30. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_enums.py +0 -0
  31. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_environment.py +0 -0
  32. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_executor.py +0 -0
  33. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_functions.py +0 -0
  34. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_imports.py +0 -0
  35. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_metadata.py +0 -0
  36. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_models.py +0 -0
  37. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_multi_join_assignments.py +0 -0
  38. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_parse_engine.py +0 -0
  39. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_parsing.py +0 -0
  40. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_partial_handling.py +0 -0
  41. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_query_processing.py +0 -0
  42. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_query_render.py +0 -0
  43. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_select.py +0 -0
  44. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_show.py +0 -0
  45. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_statements.py +0 -0
  46. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_typing.py +0 -0
  47. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_undefined_concept.py +0 -0
  48. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/tests/test_where_clause.py +0 -0
  49. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/authoring/__init__.py +0 -0
  50. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/compiler.py +0 -0
  51. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/constants.py +0 -0
  52. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/__init__.py +0 -0
  53. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/constants.py +0 -0
  54. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/env_processor.py +0 -0
  55. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/environment_helpers.py +0 -0
  56. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/ergonomics.py +0 -0
  57. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/exceptions.py +0 -0
  58. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/graph_models.py +0 -0
  59. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/internal.py +0 -0
  60. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/models/__init__.py +0 -0
  61. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/models/build_environment.py +0 -0
  62. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/models/core.py +0 -0
  63. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/models/datasource.py +0 -0
  64. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/models/environment.py +0 -0
  65. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/models/execute.py +0 -0
  66. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/optimization.py +0 -0
  67. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/optimizations/__init__.py +0 -0
  68. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/optimizations/base_optimization.py +0 -0
  69. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/optimizations/inline_constant.py +0 -0
  70. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/optimizations/inline_datasource.py +0 -0
  71. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  72. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/__init__.py +0 -0
  73. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  74. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/graph_utils.py +0 -0
  75. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/__init__.py +0 -0
  76. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  77. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/common.py +0 -0
  78. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  79. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/group_node.py +0 -0
  80. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  81. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  82. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  83. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  84. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  85. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
  86. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  87. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/select_node.py +0 -0
  88. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
  89. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/union_node.py +0 -0
  90. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  91. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/node_generators/window_node.py +0 -0
  92. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/nodes/__init__.py +0 -0
  93. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/nodes/base_node.py +0 -0
  94. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/nodes/filter_node.py +0 -0
  95. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/nodes/group_node.py +0 -0
  96. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/nodes/merge_node.py +0 -0
  97. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  98. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/nodes/union_node.py +0 -0
  99. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  100. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/nodes/window_node.py +0 -0
  101. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/processing/utility.py +0 -0
  102. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/query_processor.py +0 -0
  103. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/statements/__init__.py +0 -0
  104. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/statements/build.py +0 -0
  105. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/statements/common.py +0 -0
  106. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/core/statements/execute.py +0 -0
  107. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/dialect/__init__.py +0 -0
  108. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/dialect/bigquery.py +0 -0
  109. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/dialect/common.py +0 -0
  110. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/dialect/config.py +0 -0
  111. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/dialect/dataframe.py +0 -0
  112. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/dialect/duckdb.py +0 -0
  113. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/dialect/enums.py +0 -0
  114. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/dialect/postgres.py +0 -0
  115. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/dialect/presto.py +0 -0
  116. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/dialect/snowflake.py +0 -0
  117. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/dialect/sql_server.py +0 -0
  118. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/engine.py +0 -0
  119. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/executor.py +0 -0
  120. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/hooks/__init__.py +0 -0
  121. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/hooks/base_hook.py +0 -0
  122. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/hooks/graph_hook.py +0 -0
  123. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/hooks/query_debugger.py +0 -0
  124. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/metadata/__init__.py +0 -0
  125. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/parser.py +0 -0
  126. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/parsing/__init__.py +0 -0
  127. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/parsing/config.py +0 -0
  128. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/parsing/exceptions.py +0 -0
  129. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/parsing/helpers.py +0 -0
  130. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/py.typed +0 -0
  131. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/render.py +0 -0
  132. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/scripts/__init__.py +0 -0
  133. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/scripts/trilogy.py +0 -0
  134. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/std/__init__.py +0 -0
  135. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/std/dashboard.preql +0 -0
  136. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/std/date.preql +0 -0
  137. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/std/geography.preql +0 -0
  138. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/std/money.preql +0 -0
  139. {pytrilogy-0.0.3.29 → pytrilogy-0.0.3.31}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.29
3
+ Version: 0.0.3.31
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.29
3
+ Version: 0.0.3.31
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -132,5 +132,6 @@ trilogy/scripts/trilogy.py
132
132
  trilogy/std/__init__.py
133
133
  trilogy/std/dashboard.preql
134
134
  trilogy/std/date.preql
135
+ trilogy/std/display.preql
135
136
  trilogy/std/geography.preql
136
137
  trilogy/std/money.preql
@@ -1,4 +1,6 @@
1
- from trilogy import Dialects
1
+ from pathlib import Path
2
+
3
+ from trilogy import Dialects, Environment
2
4
  from trilogy.core.enums import Derivation, Purpose
3
5
 
4
6
 
@@ -204,3 +206,25 @@ auto test <-SUM(CASE WHEN 10 = weekday THEN x ELSE 0 END) +
204
206
  assert test.keys == set()
205
207
  assert test.purpose == Purpose.METRIC
206
208
  assert test.derivation == Derivation.BASIC
209
+
210
+
211
+ def test_user_function_import():
212
+ env = Environment(working_path=Path(__file__).parent)
213
+ x = Dialects.DUCK_DB.default_executor(environment=env)
214
+
215
+ results = x.execute_query(
216
+ """
217
+ import test_env_functions as test_env_functions;
218
+
219
+ key x int;
220
+
221
+ merge test_env_functions.quad_test into x;
222
+
223
+ select
224
+ x as quad_test,
225
+ @test_env_functions.quadratic(2, 3, 4) as quad_two;
226
+
227
+ """
228
+ )
229
+ results = results.fetchall()
230
+ assert results[0].quad_test == 16.414213562373096
@@ -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.29"
7
+ __version__ = "0.0.3.31"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -153,6 +153,7 @@ class FunctionType(Enum):
153
153
  MOD = "mod"
154
154
  ROUND = "round"
155
155
  ABS = "abs"
156
+ SQRT = "sqrt"
156
157
 
157
158
  # Aggregates
158
159
  ## group is not a real aggregate - it just means group by this + some other set of fields
@@ -286,6 +287,8 @@ class ComparisonOperator(Enum):
286
287
  return ComparisonOperator.IS_NOT
287
288
  if value == ["in"]:
288
289
  return ComparisonOperator.IN
290
+ if str(value).lower() != str(value):
291
+ return ComparisonOperator(str(value).lower())
289
292
  return super()._missing_(str(value).lower())
290
293
 
291
294
 
@@ -607,7 +607,15 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
607
607
  ],
608
608
  output_purpose=Purpose.PROPERTY,
609
609
  output_type=DataType.INTEGER,
610
- arg_count=InfiniteFunctionArgs,
610
+ arg_count=2,
611
+ ),
612
+ FunctionType.SQRT: FunctionConfig(
613
+ valid_inputs=[
614
+ {DataType.INTEGER, DataType.FLOAT, DataType.NUMBER, DataType.NUMERIC},
615
+ ],
616
+ output_purpose=Purpose.PROPERTY,
617
+ output_type=DataType.INTEGER,
618
+ arg_count=1,
611
619
  ),
612
620
  FunctionType.ROUND: FunctionConfig(
613
621
  valid_inputs=[
@@ -376,7 +376,7 @@ class Conditional(Mergeable, ConceptArgs, Namespaced, DataTyped, BaseModel):
376
376
 
377
377
 
378
378
  class WhereClause(Mergeable, ConceptArgs, Namespaced, BaseModel):
379
- conditional: Union[SubselectComparison, Comparison, Conditional, "Parenthetical"]
379
+ conditional: Union[SubselectComparison, Comparison, Conditional, Parenthetical]
380
380
 
381
381
  def __repr__(self):
382
382
  return str(self.conditional)
@@ -564,6 +564,7 @@ class Comparison(ConceptArgs, Mergeable, DataTyped, Namespaced, BaseModel):
564
564
  "Conditional",
565
565
  DataType,
566
566
  "Comparison",
567
+ FunctionCallWrapper,
567
568
  "Parenthetical",
568
569
  MagicConstants,
569
570
  WindowItem,
@@ -583,6 +584,7 @@ class Comparison(ConceptArgs, Mergeable, DataTyped, Namespaced, BaseModel):
583
584
  Conditional,
584
585
  DataType,
585
586
  Comparison,
587
+ FunctionCallWrapper,
586
588
  Parenthetical,
587
589
  MagicConstants,
588
590
  WindowItem,
@@ -1622,7 +1624,6 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
1622
1624
  elif not valid_inputs:
1623
1625
  return v
1624
1626
  for idx, arg in enumerate(v):
1625
-
1626
1627
  if (
1627
1628
  isinstance(arg, ConceptRef)
1628
1629
  and get_basic_type(arg.datatype.data_type) not in valid_inputs[idx]
@@ -1753,6 +1754,69 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
1753
1754
  return base_grain
1754
1755
 
1755
1756
 
1757
+ class FunctionCallWrapper(
1758
+ DataTyped,
1759
+ ConceptArgs,
1760
+ Mergeable,
1761
+ Namespaced,
1762
+ BaseModel,
1763
+ ):
1764
+ content: Expr
1765
+ name: str
1766
+ args: List[Expr]
1767
+
1768
+ def __str__(self):
1769
+ return f'@{self.name}({",".join([str(x) for x in self.args])})'
1770
+
1771
+ def with_namespace(self, namespace) -> "FunctionCallWrapper":
1772
+ return FunctionCallWrapper.model_construct(
1773
+ content=(
1774
+ self.content.with_namespace(namespace)
1775
+ if isinstance(self.content, Namespaced)
1776
+ else self.content
1777
+ ),
1778
+ name=self.name,
1779
+ args=[
1780
+ x.with_namespace(namespace) if isinstance(x, Namespaced) else x
1781
+ for x in self.args
1782
+ ],
1783
+ )
1784
+
1785
+ def with_merge(
1786
+ self, source: Concept, target: Concept, modifiers: List[Modifier]
1787
+ ) -> "FunctionCallWrapper":
1788
+ return FunctionCallWrapper.model_construct(
1789
+ content=(
1790
+ self.content.with_merge(source, target, modifiers)
1791
+ if isinstance(self.content, Mergeable)
1792
+ else self.content
1793
+ ),
1794
+ name=self.name,
1795
+ args=[
1796
+ (
1797
+ x.with_merge(source, target, modifiers)
1798
+ if isinstance(x, Mergeable)
1799
+ else x
1800
+ )
1801
+ for x in self.args
1802
+ ],
1803
+ )
1804
+
1805
+ @property
1806
+ def concept_arguments(self) -> Sequence[ConceptRef]:
1807
+ base: List[ConceptRef] = []
1808
+ x = self.content
1809
+ if isinstance(x, ConceptRef):
1810
+ base += [x]
1811
+ elif isinstance(x, ConceptArgs):
1812
+ base += x.concept_arguments
1813
+ return base
1814
+
1815
+ @property
1816
+ def output_datatype(self):
1817
+ return arg_to_datatype(self.content)
1818
+
1819
+
1756
1820
  class AggregateWrapper(Mergeable, DataTyped, ConceptArgs, Namespaced, BaseModel):
1757
1821
  function: Function
1758
1822
  by: List[ConceptRef] = Field(default_factory=list)
@@ -2278,6 +2342,7 @@ Expr = (
2278
2342
  | ConceptRef
2279
2343
  | Comparison
2280
2344
  | Conditional
2345
+ | FunctionCallWrapper
2281
2346
  | Parenthetical
2282
2347
  | Function
2283
2348
  | AggregateWrapper
@@ -2289,6 +2354,7 @@ FuncArgs = (
2289
2354
  ConceptRef
2290
2355
  | AggregateWrapper
2291
2356
  | Function
2357
+ | FunctionCallWrapper
2292
2358
  | Parenthetical
2293
2359
  | CaseWhen
2294
2360
  | CaseElse
@@ -53,6 +53,7 @@ from trilogy.core.models.author import (
53
53
  FilterItem,
54
54
  FuncArgs,
55
55
  Function,
56
+ FunctionCallWrapper,
56
57
  Grain,
57
58
  HavingClause,
58
59
  Metadata,
@@ -1608,6 +1609,12 @@ class Factory:
1608
1609
  def _(self, base: OrderBy) -> BuildOrderBy:
1609
1610
  return BuildOrderBy.model_construct(items=[self.build(x) for x in base.items])
1610
1611
 
1612
+ @build.register
1613
+ def _(self, base: FunctionCallWrapper) -> BuildExpr:
1614
+ # function calls are kept around purely for the parse tree
1615
+ # so discard at the build point
1616
+ return self.build(base.content)
1617
+
1611
1618
  @build.register
1612
1619
  def _(self, base: OrderItem) -> BuildOrderItem:
1613
1620
  from trilogy.parsing.common import arbitrary_to_concept
@@ -22,6 +22,7 @@ from trilogy.core.models.author import (
22
22
  Expr,
23
23
  FilterItem,
24
24
  Function,
25
+ FunctionCallWrapper,
25
26
  Grain,
26
27
  HasUUID,
27
28
  HavingClause,
@@ -44,7 +45,9 @@ from trilogy.utility import unique
44
45
 
45
46
 
46
47
  class ConceptTransform(BaseModel):
47
- function: Function | FilterItem | WindowItem | AggregateWrapper
48
+ function: (
49
+ Function | FilterItem | WindowItem | AggregateWrapper | FunctionCallWrapper
50
+ )
48
51
  output: Concept # this has to be a full concept, as it may not exist in environment
49
52
  modifiers: List[Modifier] = Field(default_factory=list)
50
53
 
@@ -171,6 +171,7 @@ FUNCTION_MAP = {
171
171
  FunctionType.MULTIPLY: lambda x: " * ".join(x),
172
172
  FunctionType.ROUND: lambda x: f"round({x[0]},{x[1]})",
173
173
  FunctionType.MOD: lambda x: f"({x[0]} % {x[1]})",
174
+ FunctionType.SQRT: lambda x: f"sqrt({x[0]})",
174
175
  # aggregate types
175
176
  FunctionType.COUNT_DISTINCT: lambda x: f"count(distinct {x[0]})",
176
177
  FunctionType.COUNT: lambda x: f"count({x[0]})",
@@ -27,6 +27,7 @@ from trilogy.core.models.author import (
27
27
  ConceptRef,
28
28
  FilterItem,
29
29
  Function,
30
+ FunctionCallWrapper,
30
31
  Grain,
31
32
  HavingClause,
32
33
  ListWrapper,
@@ -595,6 +596,7 @@ def rowset_to_concepts(rowset: RowsetDerivationStatement, environment: Environme
595
596
  def arbitrary_to_concept(
596
597
  parent: (
597
598
  AggregateWrapper
599
+ | FunctionCallWrapper
598
600
  | WindowItem
599
601
  | FilterItem
600
602
  | Function
@@ -610,7 +612,12 @@ def arbitrary_to_concept(
610
612
  metadata: Metadata | None = None,
611
613
  ) -> Concept:
612
614
  namespace = namespace or environment.namespace
613
- if isinstance(parent, AggregateWrapper):
615
+ # this is purely for the parse tree, discard from derivation
616
+ if isinstance(parent, FunctionCallWrapper):
617
+ return arbitrary_to_concept(
618
+ parent.content, environment, namespace, name, metadata # type: ignore
619
+ )
620
+ elif isinstance(parent, AggregateWrapper):
614
621
  if not name:
615
622
  name = f"{VIRTUAL_CONCEPT_PREFIX}_agg_{parent.function.operator.value}_{string_to_hash(str(parent))}"
616
623
  return agg_wrapper_to_concept(
@@ -64,6 +64,7 @@ from trilogy.core.models.author import (
64
64
  Expr,
65
65
  FilterItem,
66
66
  Function,
67
+ FunctionCallWrapper,
67
68
  Grain,
68
69
  HavingClause,
69
70
  Metadata,
@@ -202,7 +203,7 @@ def expr_to_boolean(
202
203
  def unwrap_transformation(
203
204
  input: Expr,
204
205
  environment: Environment,
205
- ) -> Function | FilterItem | WindowItem | AggregateWrapper:
206
+ ) -> Function | FilterItem | WindowItem | AggregateWrapper | FunctionCallWrapper:
206
207
  if isinstance(input, Function):
207
208
  return input
208
209
  elif isinstance(input, AggregateWrapper):
@@ -219,6 +220,8 @@ def unwrap_transformation(
219
220
  return input
220
221
  elif isinstance(input, WindowItem):
221
222
  return input
223
+ elif isinstance(input, FunctionCallWrapper):
224
+ return input
222
225
  elif isinstance(input, Parenthetical):
223
226
  return unwrap_transformation(input.content, environment)
224
227
  else:
@@ -810,7 +813,6 @@ class ParseToObjects(Transformer):
810
813
  )
811
814
 
812
815
  metadata = Metadata(line_number=meta.line, concept_source=ConceptSource.SELECT)
813
-
814
816
  concept = arbitrary_to_concept(
815
817
  transformation,
816
818
  environment=self.environment,
@@ -959,7 +961,7 @@ class ParseToObjects(Transformer):
959
961
  lookup = address
960
962
  if lookup not in self.environment.config.import_resolver.content:
961
963
  raise ImportError(
962
- f"Unable to import file {lookup}, not found in imsport resolver"
964
+ f"Unable to import file {lookup}, not resolvable from provided source files."
963
965
  )
964
966
  text = self.environment.config.import_resolver.content[lookup]
965
967
  else:
@@ -1248,10 +1250,12 @@ class ParseToObjects(Transformer):
1248
1250
  )
1249
1251
  return FunctionDeclaration(name=identity, args=function_arguments, expr=output)
1250
1252
 
1251
- def custom_function(self, args):
1253
+ def custom_function(self, args) -> FunctionCallWrapper:
1252
1254
  name = args[0]
1253
1255
  args = args[1:]
1254
- remapped = self.environment.functions[name](*args)
1256
+ remapped = FunctionCallWrapper(
1257
+ content=self.environment.functions[name](*args), name=name, args=args
1258
+ )
1255
1259
  return remapped
1256
1260
 
1257
1261
  @v_args(meta=True)
@@ -1723,6 +1727,10 @@ class ParseToObjects(Transformer):
1723
1727
  def fmod(self, meta: Meta, args) -> Function:
1724
1728
  return self.function_factory.create_function(args, FunctionType.MOD, meta)
1725
1729
 
1730
+ @v_args(meta=True)
1731
+ def fsqrt(self, meta: Meta, args) -> Function:
1732
+ return self.function_factory.create_function(args, FunctionType.SQRT, meta)
1733
+
1726
1734
  @v_args(meta=True)
1727
1735
  def fround(self, meta, args) -> Function:
1728
1736
  return self.function_factory.create_function(args, FunctionType.ROUND, meta)
@@ -18,6 +18,7 @@ from trilogy.core.models.author import (
18
18
  Conditional,
19
19
  FilterItem,
20
20
  Function,
21
+ FunctionCallWrapper,
21
22
  Grain,
22
23
  OrderBy,
23
24
  OrderItem,
@@ -31,6 +32,7 @@ from trilogy.core.models.core import (
31
32
  ListType,
32
33
  ListWrapper,
33
34
  NumericType,
35
+ TraitDataType,
34
36
  TupleWrapper,
35
37
  )
36
38
  from trilogy.core.models.datasource import (
@@ -42,10 +44,12 @@ from trilogy.core.models.datasource import (
42
44
  )
43
45
  from trilogy.core.models.environment import Environment, Import
44
46
  from trilogy.core.statements.author import (
47
+ ArgBinding,
45
48
  ConceptDeclarationStatement,
46
49
  ConceptDerivationStatement,
47
50
  ConceptTransform,
48
51
  CopyStatement,
52
+ FunctionDeclaration,
49
53
  ImportStatement,
50
54
  MergeStatementV2,
51
55
  MultiSelectStatement,
@@ -54,6 +58,7 @@ from trilogy.core.statements.author import (
54
58
  RowsetDerivationStatement,
55
59
  SelectItem,
56
60
  SelectStatement,
61
+ TypeDeclaration,
57
62
  )
58
63
 
59
64
  QUERY_TEMPLATE = Template(
@@ -149,6 +154,21 @@ class Renderer:
149
154
  final = "\n\n".join("\n".join(x) for x in components)
150
155
  return final
151
156
 
157
+ @to_string.register
158
+ def _(self, arg: TypeDeclaration):
159
+ return f"type {arg.type.name} {self.to_string(arg.type.type)};"
160
+
161
+ @to_string.register
162
+ def _(self, arg: ArgBinding):
163
+ if arg.default:
164
+ return f"{arg.name}={self.to_string(arg.default)}"
165
+ return f"{arg.name}"
166
+
167
+ @to_string.register
168
+ def _(self, arg: FunctionDeclaration):
169
+ args = ", ".join([self.to_string(x) for x in arg.args])
170
+ return f"def {arg.name}({args}) -> {self.to_string(arg.expr)};"
171
+
152
172
  @to_string.register
153
173
  def _(self, arg: Datasource):
154
174
  assignments = ",\n ".join([self.to_string(x) for x in arg.columns])
@@ -198,6 +218,12 @@ class Renderer:
198
218
  def _(self, arg: "CaseElse"):
199
219
  return f"""ELSE {self.to_string(arg.expr)}"""
200
220
 
221
+ @to_string.register
222
+ def _(self, arg: "FunctionCallWrapper"):
223
+ args = [self.to_string(c) for c in arg.args]
224
+ arg_string = ", ".join(args)
225
+ return f"""@{arg.name}({arg_string})"""
226
+
201
227
  @to_string.register
202
228
  def _(self, arg: "Parenthetical"):
203
229
  return f"""({self.to_string(arg.content)})"""
@@ -210,6 +236,11 @@ class Renderer:
210
236
  def _(self, arg: "NumericType"):
211
237
  return f"""Numeric({arg.precision},{arg.scale})"""
212
238
 
239
+ @to_string.register
240
+ def _(self, arg: TraitDataType):
241
+ traits = "::".join([x for x in arg.traits])
242
+ return f"{self.to_string(arg.data_type)}::{traits}"
243
+
213
244
  @to_string.register
214
245
  def _(self, arg: ListWrapper):
215
246
  return "[" + ", ".join([self.to_string(x) for x in arg]) + "]"
@@ -180,6 +180,7 @@
180
180
  map_key_access: expr "[" string_lit "]"
181
181
  attr_access: expr "." string_lit
182
182
 
183
+
183
184
  expr: _constant_functions | window_item | filter_item | subselect_comparison | between_comparison | fgroup | aggregate_functions | unnest | union | _static_functions | literal | concept_lit | index_access | map_key_access | attr_access | parenthetical | expr_tuple | comparison | alt_like | custom_function
184
185
 
185
186
  // functions
@@ -191,8 +192,9 @@
191
192
  fmod: ( "mod"i "(" expr "," (int_lit | concept_lit ) ")") | ( expr "%" (int_lit | concept_lit ) )
192
193
  fround: "round"i "(" expr "," expr ")"
193
194
  fabs: "abs"i "(" expr ")"
195
+ fsqrt: "sqrt"i "(" expr ")"
194
196
 
195
- _math_functions: fmul | fdiv | fadd | fsub | fround | fmod | fabs
197
+ _math_functions: fmul | fdiv | fadd | fsub | fround | fmod | fabs | fsqrt
196
198
 
197
199
  //generic
198
200
  _fcast_primary: "cast"i "(" expr "as"i data_type ")"
@@ -306,7 +308,7 @@
306
308
  custom_function: "@" IDENTIFIER "(" (expr ",")* expr ")"
307
309
 
308
310
  // base language constructs
309
- concept_lit: IDENTIFIER
311
+ concept_lit: MINUS? IDENTIFIER
310
312
  IDENTIFIER: /[a-zA-Z\_][a-zA-Z0-9\_\.]*/
311
313
  WILDCARD_IDENTIFIER: /[a-zA-Z\_][a-zA-Z0-9\_\-\.\*]*/
312
314
  QUOTED_IDENTIFIER: /`[a-zA-Z\_][a-zA-Z0-9\_\.\-\*\:\s]*`/
@@ -0,0 +1,3 @@
1
+
2
+
3
+ type percent float; # Percentage value
File without changes
File without changes
File without changes
File without changes