relationalai 0.12.13__py3-none-any.whl → 0.13.0.dev0__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.
Files changed (825) hide show
  1. relationalai/__init__.py +1 -209
  2. relationalai/config/__init__.py +56 -0
  3. relationalai/config/config.py +289 -0
  4. relationalai/config/config_fields.py +86 -0
  5. relationalai/config/connections/__init__.py +46 -0
  6. relationalai/config/connections/base.py +23 -0
  7. relationalai/config/connections/duckdb.py +29 -0
  8. relationalai/config/connections/snowflake.py +243 -0
  9. relationalai/config/external/__init__.py +17 -0
  10. relationalai/config/external/dbt_converter.py +101 -0
  11. relationalai/config/external/dbt_models.py +93 -0
  12. relationalai/config/external/snowflake_converter.py +41 -0
  13. relationalai/config/external/snowflake_models.py +85 -0
  14. relationalai/config/external/utils.py +19 -0
  15. relationalai/semantics/__init__.py +146 -22
  16. relationalai/semantics/backends/lqp/annotations.py +11 -0
  17. relationalai/semantics/backends/sql/sql_compiler.py +327 -0
  18. relationalai/semantics/frontend/base.py +1707 -0
  19. relationalai/semantics/frontend/core.py +179 -0
  20. relationalai/semantics/frontend/front_compiler.py +1313 -0
  21. relationalai/semantics/frontend/pprint.py +408 -0
  22. relationalai/semantics/metamodel/__init__.py +6 -40
  23. relationalai/semantics/metamodel/builtins.py +205 -769
  24. relationalai/semantics/metamodel/metamodel.py +437 -0
  25. relationalai/semantics/metamodel/metamodel_analyzer.py +519 -0
  26. relationalai/semantics/metamodel/pprint.py +412 -0
  27. relationalai/semantics/metamodel/rewriter.py +266 -0
  28. relationalai/semantics/metamodel/typer.py +1378 -0
  29. relationalai/semantics/std/__init__.py +60 -40
  30. relationalai/semantics/std/aggregates.py +149 -0
  31. relationalai/semantics/std/common.py +44 -0
  32. relationalai/semantics/std/constraints.py +37 -43
  33. relationalai/semantics/std/datetime.py +246 -135
  34. relationalai/semantics/std/decimals.py +45 -52
  35. relationalai/semantics/std/floats.py +13 -5
  36. relationalai/semantics/std/integers.py +26 -11
  37. relationalai/semantics/std/math.py +183 -112
  38. relationalai/semantics/std/numbers.py +86 -0
  39. relationalai/semantics/std/re.py +80 -62
  40. relationalai/semantics/std/strings.py +117 -60
  41. relationalai/shims/executor.py +147 -0
  42. relationalai/shims/helpers.py +126 -0
  43. relationalai/shims/hoister.py +221 -0
  44. relationalai/shims/mm2v0.py +1290 -0
  45. relationalai/tools/cli/__init__.py +6 -0
  46. relationalai/tools/cli/cli.py +90 -0
  47. relationalai/tools/cli/components/__init__.py +5 -0
  48. relationalai/tools/cli/components/progress_reader.py +1524 -0
  49. relationalai/tools/cli/components/utils.py +58 -0
  50. relationalai/tools/cli/config_template.py +45 -0
  51. relationalai/tools/cli/dev.py +19 -0
  52. relationalai/tools/debugger.py +289 -183
  53. relationalai/tools/typer_debugger.py +93 -0
  54. relationalai/util/dataclasses.py +43 -0
  55. relationalai/util/docutils.py +40 -0
  56. relationalai/util/error.py +199 -0
  57. relationalai/util/format.py +48 -106
  58. relationalai/util/naming.py +145 -0
  59. relationalai/util/python.py +35 -0
  60. relationalai/util/runtime.py +156 -0
  61. relationalai/util/schema.py +197 -0
  62. relationalai/util/source.py +185 -0
  63. relationalai/util/structures.py +163 -0
  64. relationalai/util/tracing.py +261 -0
  65. relationalai-0.13.0.dev0.dist-info/METADATA +46 -0
  66. relationalai-0.13.0.dev0.dist-info/RECORD +488 -0
  67. relationalai-0.13.0.dev0.dist-info/WHEEL +5 -0
  68. relationalai-0.13.0.dev0.dist-info/entry_points.txt +3 -0
  69. relationalai-0.13.0.dev0.dist-info/top_level.txt +2 -0
  70. v0/relationalai/__init__.py +216 -0
  71. v0/relationalai/clients/azure.py +477 -0
  72. v0/relationalai/clients/client.py +912 -0
  73. v0/relationalai/clients/config.py +673 -0
  74. v0/relationalai/clients/direct_access_client.py +118 -0
  75. v0/relationalai/clients/hash_util.py +31 -0
  76. v0/relationalai/clients/local.py +571 -0
  77. v0/relationalai/clients/profile_polling.py +73 -0
  78. v0/relationalai/clients/result_helpers.py +420 -0
  79. v0/relationalai/clients/snowflake.py +3869 -0
  80. v0/relationalai/clients/types.py +113 -0
  81. v0/relationalai/clients/use_index_poller.py +980 -0
  82. v0/relationalai/clients/util.py +356 -0
  83. v0/relationalai/debugging.py +389 -0
  84. v0/relationalai/dsl.py +1749 -0
  85. v0/relationalai/early_access/builder/__init__.py +30 -0
  86. v0/relationalai/early_access/builder/builder/__init__.py +35 -0
  87. v0/relationalai/early_access/builder/snowflake/__init__.py +12 -0
  88. v0/relationalai/early_access/builder/std/__init__.py +25 -0
  89. v0/relationalai/early_access/builder/std/decimals/__init__.py +12 -0
  90. v0/relationalai/early_access/builder/std/integers/__init__.py +12 -0
  91. v0/relationalai/early_access/builder/std/math/__init__.py +12 -0
  92. v0/relationalai/early_access/builder/std/strings/__init__.py +14 -0
  93. v0/relationalai/early_access/devtools/__init__.py +12 -0
  94. v0/relationalai/early_access/devtools/benchmark_lqp/__init__.py +12 -0
  95. v0/relationalai/early_access/devtools/extract_lqp/__init__.py +12 -0
  96. v0/relationalai/early_access/dsl/adapters/orm/adapter_qb.py +427 -0
  97. v0/relationalai/early_access/dsl/adapters/orm/parser.py +636 -0
  98. v0/relationalai/early_access/dsl/adapters/owl/adapter.py +176 -0
  99. v0/relationalai/early_access/dsl/adapters/owl/parser.py +160 -0
  100. v0/relationalai/early_access/dsl/bindings/common.py +402 -0
  101. v0/relationalai/early_access/dsl/bindings/csv.py +170 -0
  102. v0/relationalai/early_access/dsl/bindings/legacy/binding_models.py +143 -0
  103. v0/relationalai/early_access/dsl/bindings/snowflake.py +64 -0
  104. v0/relationalai/early_access/dsl/codegen/binder.py +411 -0
  105. v0/relationalai/early_access/dsl/codegen/common.py +79 -0
  106. v0/relationalai/early_access/dsl/codegen/helpers.py +23 -0
  107. v0/relationalai/early_access/dsl/codegen/relations.py +700 -0
  108. v0/relationalai/early_access/dsl/codegen/weaver.py +417 -0
  109. v0/relationalai/early_access/dsl/core/builders/__init__.py +47 -0
  110. v0/relationalai/early_access/dsl/core/builders/logic.py +19 -0
  111. v0/relationalai/early_access/dsl/core/builders/scalar_constraint.py +11 -0
  112. v0/relationalai/early_access/dsl/core/constraints/predicate/atomic.py +455 -0
  113. v0/relationalai/early_access/dsl/core/constraints/predicate/universal.py +73 -0
  114. v0/relationalai/early_access/dsl/core/constraints/scalar.py +310 -0
  115. v0/relationalai/early_access/dsl/core/context.py +13 -0
  116. v0/relationalai/early_access/dsl/core/cset.py +132 -0
  117. v0/relationalai/early_access/dsl/core/exprs/__init__.py +116 -0
  118. v0/relationalai/early_access/dsl/core/exprs/relational.py +18 -0
  119. v0/relationalai/early_access/dsl/core/exprs/scalar.py +412 -0
  120. v0/relationalai/early_access/dsl/core/instances.py +44 -0
  121. v0/relationalai/early_access/dsl/core/logic/__init__.py +193 -0
  122. v0/relationalai/early_access/dsl/core/logic/aggregation.py +98 -0
  123. v0/relationalai/early_access/dsl/core/logic/exists.py +223 -0
  124. v0/relationalai/early_access/dsl/core/logic/helper.py +163 -0
  125. v0/relationalai/early_access/dsl/core/namespaces.py +32 -0
  126. v0/relationalai/early_access/dsl/core/relations.py +276 -0
  127. v0/relationalai/early_access/dsl/core/rules.py +112 -0
  128. v0/relationalai/early_access/dsl/core/std/__init__.py +45 -0
  129. v0/relationalai/early_access/dsl/core/temporal/recall.py +6 -0
  130. v0/relationalai/early_access/dsl/core/types/__init__.py +270 -0
  131. v0/relationalai/early_access/dsl/core/types/concepts.py +128 -0
  132. v0/relationalai/early_access/dsl/core/types/constrained/__init__.py +267 -0
  133. v0/relationalai/early_access/dsl/core/types/constrained/nominal.py +143 -0
  134. v0/relationalai/early_access/dsl/core/types/constrained/subtype.py +124 -0
  135. v0/relationalai/early_access/dsl/core/types/standard.py +92 -0
  136. v0/relationalai/early_access/dsl/core/types/unconstrained.py +50 -0
  137. v0/relationalai/early_access/dsl/core/types/variables.py +203 -0
  138. v0/relationalai/early_access/dsl/ir/compiler.py +318 -0
  139. v0/relationalai/early_access/dsl/ir/executor.py +260 -0
  140. v0/relationalai/early_access/dsl/ontologies/constraints.py +88 -0
  141. v0/relationalai/early_access/dsl/ontologies/export.py +30 -0
  142. v0/relationalai/early_access/dsl/ontologies/models.py +453 -0
  143. v0/relationalai/early_access/dsl/ontologies/python_printer.py +303 -0
  144. v0/relationalai/early_access/dsl/ontologies/readings.py +60 -0
  145. v0/relationalai/early_access/dsl/ontologies/relationships.py +322 -0
  146. v0/relationalai/early_access/dsl/ontologies/roles.py +87 -0
  147. v0/relationalai/early_access/dsl/ontologies/subtyping.py +55 -0
  148. v0/relationalai/early_access/dsl/orm/constraints.py +438 -0
  149. v0/relationalai/early_access/dsl/orm/measures/dimensions.py +200 -0
  150. v0/relationalai/early_access/dsl/orm/measures/initializer.py +16 -0
  151. v0/relationalai/early_access/dsl/orm/measures/measure_rules.py +275 -0
  152. v0/relationalai/early_access/dsl/orm/measures/measures.py +299 -0
  153. v0/relationalai/early_access/dsl/orm/measures/role_exprs.py +268 -0
  154. v0/relationalai/early_access/dsl/orm/models.py +256 -0
  155. v0/relationalai/early_access/dsl/orm/object_oriented_printer.py +344 -0
  156. v0/relationalai/early_access/dsl/orm/printer.py +469 -0
  157. v0/relationalai/early_access/dsl/orm/reasoners.py +480 -0
  158. v0/relationalai/early_access/dsl/orm/relations.py +19 -0
  159. v0/relationalai/early_access/dsl/orm/relationships.py +251 -0
  160. v0/relationalai/early_access/dsl/orm/types.py +42 -0
  161. v0/relationalai/early_access/dsl/orm/utils.py +79 -0
  162. v0/relationalai/early_access/dsl/orm/verb.py +204 -0
  163. v0/relationalai/early_access/dsl/physical_metadata/tables.py +133 -0
  164. v0/relationalai/early_access/dsl/relations.py +170 -0
  165. v0/relationalai/early_access/dsl/rulesets.py +69 -0
  166. v0/relationalai/early_access/dsl/schemas/__init__.py +450 -0
  167. v0/relationalai/early_access/dsl/schemas/builder.py +48 -0
  168. v0/relationalai/early_access/dsl/schemas/comp_names.py +51 -0
  169. v0/relationalai/early_access/dsl/schemas/components.py +203 -0
  170. v0/relationalai/early_access/dsl/schemas/contexts.py +156 -0
  171. v0/relationalai/early_access/dsl/schemas/exprs.py +89 -0
  172. v0/relationalai/early_access/dsl/schemas/fragments.py +464 -0
  173. v0/relationalai/early_access/dsl/serialization.py +79 -0
  174. v0/relationalai/early_access/dsl/serialize/exporter.py +163 -0
  175. v0/relationalai/early_access/dsl/snow/api.py +104 -0
  176. v0/relationalai/early_access/dsl/snow/common.py +76 -0
  177. v0/relationalai/early_access/dsl/state_mgmt/__init__.py +129 -0
  178. v0/relationalai/early_access/dsl/state_mgmt/state_charts.py +125 -0
  179. v0/relationalai/early_access/dsl/state_mgmt/transitions.py +130 -0
  180. v0/relationalai/early_access/dsl/types/__init__.py +40 -0
  181. v0/relationalai/early_access/dsl/types/concepts.py +12 -0
  182. v0/relationalai/early_access/dsl/types/entities.py +135 -0
  183. v0/relationalai/early_access/dsl/types/values.py +17 -0
  184. v0/relationalai/early_access/dsl/utils.py +102 -0
  185. v0/relationalai/early_access/graphs/__init__.py +13 -0
  186. v0/relationalai/early_access/lqp/__init__.py +12 -0
  187. v0/relationalai/early_access/lqp/compiler/__init__.py +12 -0
  188. v0/relationalai/early_access/lqp/constructors/__init__.py +18 -0
  189. v0/relationalai/early_access/lqp/executor/__init__.py +12 -0
  190. v0/relationalai/early_access/lqp/ir/__init__.py +12 -0
  191. v0/relationalai/early_access/lqp/passes/__init__.py +12 -0
  192. v0/relationalai/early_access/lqp/pragmas/__init__.py +12 -0
  193. v0/relationalai/early_access/lqp/primitives/__init__.py +12 -0
  194. v0/relationalai/early_access/lqp/types/__init__.py +12 -0
  195. v0/relationalai/early_access/lqp/utils/__init__.py +12 -0
  196. v0/relationalai/early_access/lqp/validators/__init__.py +12 -0
  197. v0/relationalai/early_access/metamodel/__init__.py +58 -0
  198. v0/relationalai/early_access/metamodel/builtins/__init__.py +12 -0
  199. v0/relationalai/early_access/metamodel/compiler/__init__.py +12 -0
  200. v0/relationalai/early_access/metamodel/dependency/__init__.py +12 -0
  201. v0/relationalai/early_access/metamodel/factory/__init__.py +17 -0
  202. v0/relationalai/early_access/metamodel/helpers/__init__.py +12 -0
  203. v0/relationalai/early_access/metamodel/ir/__init__.py +14 -0
  204. v0/relationalai/early_access/metamodel/rewrite/__init__.py +7 -0
  205. v0/relationalai/early_access/metamodel/typer/__init__.py +3 -0
  206. v0/relationalai/early_access/metamodel/typer/typer/__init__.py +12 -0
  207. v0/relationalai/early_access/metamodel/types/__init__.py +15 -0
  208. v0/relationalai/early_access/metamodel/util/__init__.py +15 -0
  209. v0/relationalai/early_access/metamodel/visitor/__init__.py +12 -0
  210. v0/relationalai/early_access/rel/__init__.py +12 -0
  211. v0/relationalai/early_access/rel/executor/__init__.py +12 -0
  212. v0/relationalai/early_access/rel/rel_utils/__init__.py +12 -0
  213. v0/relationalai/early_access/rel/rewrite/__init__.py +7 -0
  214. v0/relationalai/early_access/solvers/__init__.py +19 -0
  215. v0/relationalai/early_access/sql/__init__.py +11 -0
  216. v0/relationalai/early_access/sql/executor/__init__.py +3 -0
  217. v0/relationalai/early_access/sql/rewrite/__init__.py +3 -0
  218. v0/relationalai/early_access/tests/logging/__init__.py +12 -0
  219. v0/relationalai/early_access/tests/test_snapshot_base/__init__.py +12 -0
  220. v0/relationalai/early_access/tests/utils/__init__.py +12 -0
  221. v0/relationalai/environments/__init__.py +35 -0
  222. v0/relationalai/environments/base.py +381 -0
  223. v0/relationalai/environments/colab.py +14 -0
  224. v0/relationalai/environments/generic.py +71 -0
  225. v0/relationalai/environments/ipython.py +68 -0
  226. v0/relationalai/environments/jupyter.py +9 -0
  227. v0/relationalai/environments/snowbook.py +169 -0
  228. v0/relationalai/errors.py +2455 -0
  229. v0/relationalai/experimental/SF.py +38 -0
  230. v0/relationalai/experimental/inspect.py +47 -0
  231. v0/relationalai/experimental/pathfinder/__init__.py +158 -0
  232. v0/relationalai/experimental/pathfinder/api.py +160 -0
  233. v0/relationalai/experimental/pathfinder/automaton.py +584 -0
  234. v0/relationalai/experimental/pathfinder/bridge.py +226 -0
  235. v0/relationalai/experimental/pathfinder/compiler.py +416 -0
  236. v0/relationalai/experimental/pathfinder/datalog.py +214 -0
  237. v0/relationalai/experimental/pathfinder/diagnostics.py +56 -0
  238. v0/relationalai/experimental/pathfinder/filter.py +236 -0
  239. v0/relationalai/experimental/pathfinder/glushkov.py +439 -0
  240. v0/relationalai/experimental/pathfinder/options.py +265 -0
  241. v0/relationalai/experimental/pathfinder/rpq.py +344 -0
  242. v0/relationalai/experimental/pathfinder/transition.py +200 -0
  243. v0/relationalai/experimental/pathfinder/utils.py +26 -0
  244. v0/relationalai/experimental/paths/api.py +143 -0
  245. v0/relationalai/experimental/paths/benchmarks/grid_graph.py +37 -0
  246. v0/relationalai/experimental/paths/examples/basic_example.py +40 -0
  247. v0/relationalai/experimental/paths/examples/minimal_engine_warmup.py +3 -0
  248. v0/relationalai/experimental/paths/examples/movie_example.py +77 -0
  249. v0/relationalai/experimental/paths/examples/paths_benchmark.py +115 -0
  250. v0/relationalai/experimental/paths/examples/paths_example.py +116 -0
  251. v0/relationalai/experimental/paths/examples/pattern_to_automaton.py +28 -0
  252. v0/relationalai/experimental/paths/find_paths_via_automaton.py +85 -0
  253. v0/relationalai/experimental/paths/graph.py +185 -0
  254. v0/relationalai/experimental/paths/path_algorithms/find_paths.py +280 -0
  255. v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +26 -0
  256. v0/relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +111 -0
  257. v0/relationalai/experimental/paths/path_algorithms/single.py +59 -0
  258. v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +39 -0
  259. v0/relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +103 -0
  260. v0/relationalai/experimental/paths/path_algorithms/usp-old.py +130 -0
  261. v0/relationalai/experimental/paths/path_algorithms/usp-tuple.py +183 -0
  262. v0/relationalai/experimental/paths/path_algorithms/usp.py +150 -0
  263. v0/relationalai/experimental/paths/product_graph.py +93 -0
  264. v0/relationalai/experimental/paths/rpq/automaton.py +584 -0
  265. v0/relationalai/experimental/paths/rpq/diagnostics.py +56 -0
  266. v0/relationalai/experimental/paths/rpq/rpq.py +378 -0
  267. v0/relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +90 -0
  268. v0/relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +119 -0
  269. v0/relationalai/experimental/paths/tests/tests_limit_sp_single.py +104 -0
  270. v0/relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +113 -0
  271. v0/relationalai/experimental/paths/tests/tests_limit_walks_single.py +149 -0
  272. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +70 -0
  273. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +64 -0
  274. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +115 -0
  275. v0/relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +75 -0
  276. v0/relationalai/experimental/paths/tests/tests_single_paths.py +152 -0
  277. v0/relationalai/experimental/paths/tests/tests_single_walks.py +208 -0
  278. v0/relationalai/experimental/paths/tests/tests_single_walks_undirected.py +297 -0
  279. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +107 -0
  280. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +76 -0
  281. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +76 -0
  282. v0/relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +110 -0
  283. v0/relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +229 -0
  284. v0/relationalai/experimental/paths/tests/tests_usp_nsp_single.py +108 -0
  285. v0/relationalai/experimental/paths/tree_agg.py +168 -0
  286. v0/relationalai/experimental/paths/utilities/iterators.py +27 -0
  287. v0/relationalai/experimental/paths/utilities/prefix_sum.py +91 -0
  288. v0/relationalai/experimental/solvers.py +1087 -0
  289. v0/relationalai/loaders/__init__.py +0 -0
  290. v0/relationalai/loaders/csv.py +195 -0
  291. v0/relationalai/loaders/loader.py +177 -0
  292. v0/relationalai/loaders/types.py +23 -0
  293. v0/relationalai/rel_emitter.py +373 -0
  294. v0/relationalai/rel_utils.py +185 -0
  295. v0/relationalai/semantics/__init__.py +29 -0
  296. v0/relationalai/semantics/devtools/benchmark_lqp.py +536 -0
  297. v0/relationalai/semantics/devtools/compilation_manager.py +294 -0
  298. v0/relationalai/semantics/devtools/extract_lqp.py +110 -0
  299. v0/relationalai/semantics/internal/internal.py +3785 -0
  300. v0/relationalai/semantics/internal/snowflake.py +324 -0
  301. v0/relationalai/semantics/lqp/builtins.py +16 -0
  302. v0/relationalai/semantics/lqp/compiler.py +22 -0
  303. v0/relationalai/semantics/lqp/constructors.py +68 -0
  304. v0/relationalai/semantics/lqp/executor.py +469 -0
  305. v0/relationalai/semantics/lqp/intrinsics.py +24 -0
  306. v0/relationalai/semantics/lqp/model2lqp.py +839 -0
  307. v0/relationalai/semantics/lqp/passes.py +680 -0
  308. v0/relationalai/semantics/lqp/primitives.py +252 -0
  309. v0/relationalai/semantics/lqp/result_helpers.py +202 -0
  310. v0/relationalai/semantics/lqp/rewrite/annotate_constraints.py +57 -0
  311. v0/relationalai/semantics/lqp/rewrite/cdc.py +216 -0
  312. v0/relationalai/semantics/lqp/rewrite/extract_common.py +338 -0
  313. v0/relationalai/semantics/lqp/rewrite/extract_keys.py +449 -0
  314. v0/relationalai/semantics/lqp/rewrite/function_annotations.py +114 -0
  315. v0/relationalai/semantics/lqp/rewrite/functional_dependencies.py +314 -0
  316. v0/relationalai/semantics/lqp/rewrite/quantify_vars.py +296 -0
  317. v0/relationalai/semantics/lqp/rewrite/splinter.py +76 -0
  318. v0/relationalai/semantics/lqp/types.py +101 -0
  319. v0/relationalai/semantics/lqp/utils.py +160 -0
  320. v0/relationalai/semantics/lqp/validators.py +57 -0
  321. v0/relationalai/semantics/metamodel/__init__.py +40 -0
  322. v0/relationalai/semantics/metamodel/builtins.py +774 -0
  323. v0/relationalai/semantics/metamodel/compiler.py +133 -0
  324. v0/relationalai/semantics/metamodel/dependency.py +862 -0
  325. v0/relationalai/semantics/metamodel/executor.py +61 -0
  326. v0/relationalai/semantics/metamodel/factory.py +287 -0
  327. v0/relationalai/semantics/metamodel/helpers.py +361 -0
  328. v0/relationalai/semantics/metamodel/rewrite/discharge_constraints.py +39 -0
  329. v0/relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +210 -0
  330. v0/relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +78 -0
  331. v0/relationalai/semantics/metamodel/rewrite/flatten.py +549 -0
  332. v0/relationalai/semantics/metamodel/rewrite/format_outputs.py +165 -0
  333. v0/relationalai/semantics/metamodel/typer/checker.py +353 -0
  334. v0/relationalai/semantics/metamodel/typer/typer.py +1395 -0
  335. v0/relationalai/semantics/reasoners/__init__.py +10 -0
  336. v0/relationalai/semantics/reasoners/graph/__init__.py +37 -0
  337. v0/relationalai/semantics/reasoners/graph/core.py +9020 -0
  338. v0/relationalai/semantics/reasoners/optimization/__init__.py +68 -0
  339. v0/relationalai/semantics/reasoners/optimization/common.py +88 -0
  340. v0/relationalai/semantics/reasoners/optimization/solvers_dev.py +568 -0
  341. v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +1163 -0
  342. v0/relationalai/semantics/rel/builtins.py +40 -0
  343. v0/relationalai/semantics/rel/compiler.py +989 -0
  344. v0/relationalai/semantics/rel/executor.py +359 -0
  345. v0/relationalai/semantics/rel/rel.py +482 -0
  346. v0/relationalai/semantics/rel/rel_utils.py +276 -0
  347. v0/relationalai/semantics/snowflake/__init__.py +3 -0
  348. v0/relationalai/semantics/sql/compiler.py +2503 -0
  349. v0/relationalai/semantics/sql/executor/duck_db.py +52 -0
  350. v0/relationalai/semantics/sql/executor/result_helpers.py +64 -0
  351. v0/relationalai/semantics/sql/executor/snowflake.py +145 -0
  352. v0/relationalai/semantics/sql/rewrite/denormalize.py +222 -0
  353. v0/relationalai/semantics/sql/rewrite/double_negation.py +49 -0
  354. v0/relationalai/semantics/sql/rewrite/recursive_union.py +127 -0
  355. v0/relationalai/semantics/sql/rewrite/sort_output_query.py +246 -0
  356. v0/relationalai/semantics/sql/sql.py +504 -0
  357. v0/relationalai/semantics/std/__init__.py +54 -0
  358. v0/relationalai/semantics/std/constraints.py +43 -0
  359. v0/relationalai/semantics/std/datetime.py +363 -0
  360. v0/relationalai/semantics/std/decimals.py +62 -0
  361. v0/relationalai/semantics/std/floats.py +7 -0
  362. v0/relationalai/semantics/std/integers.py +22 -0
  363. v0/relationalai/semantics/std/math.py +141 -0
  364. v0/relationalai/semantics/std/pragmas.py +11 -0
  365. v0/relationalai/semantics/std/re.py +83 -0
  366. v0/relationalai/semantics/std/std.py +14 -0
  367. v0/relationalai/semantics/std/strings.py +63 -0
  368. v0/relationalai/semantics/tests/__init__.py +0 -0
  369. v0/relationalai/semantics/tests/test_snapshot_abstract.py +143 -0
  370. v0/relationalai/semantics/tests/test_snapshot_base.py +9 -0
  371. v0/relationalai/semantics/tests/utils.py +46 -0
  372. v0/relationalai/std/__init__.py +70 -0
  373. v0/relationalai/tools/__init__.py +0 -0
  374. v0/relationalai/tools/cli.py +1940 -0
  375. v0/relationalai/tools/cli_controls.py +1826 -0
  376. v0/relationalai/tools/cli_helpers.py +390 -0
  377. v0/relationalai/tools/debugger.py +183 -0
  378. v0/relationalai/tools/debugger_client.py +109 -0
  379. v0/relationalai/tools/debugger_server.py +302 -0
  380. v0/relationalai/tools/dev.py +685 -0
  381. v0/relationalai/tools/qb_debugger.py +425 -0
  382. v0/relationalai/util/clean_up_databases.py +95 -0
  383. v0/relationalai/util/format.py +123 -0
  384. v0/relationalai/util/list_databases.py +9 -0
  385. v0/relationalai/util/otel_configuration.py +25 -0
  386. v0/relationalai/util/otel_handler.py +484 -0
  387. v0/relationalai/util/snowflake_handler.py +88 -0
  388. v0/relationalai/util/span_format_test.py +43 -0
  389. v0/relationalai/util/span_tracker.py +207 -0
  390. v0/relationalai/util/spans_file_handler.py +72 -0
  391. v0/relationalai/util/tracing_handler.py +34 -0
  392. frontend/debugger/dist/.gitignore +0 -2
  393. frontend/debugger/dist/assets/favicon-Dy0ZgA6N.png +0 -0
  394. frontend/debugger/dist/assets/index-Cssla-O7.js +0 -208
  395. frontend/debugger/dist/assets/index-DlHsYx1V.css +0 -9
  396. frontend/debugger/dist/index.html +0 -17
  397. relationalai/clients/azure.py +0 -477
  398. relationalai/clients/client.py +0 -912
  399. relationalai/clients/config.py +0 -673
  400. relationalai/clients/direct_access_client.py +0 -118
  401. relationalai/clients/export_procedure.py.jinja +0 -249
  402. relationalai/clients/hash_util.py +0 -31
  403. relationalai/clients/local.py +0 -571
  404. relationalai/clients/profile_polling.py +0 -73
  405. relationalai/clients/result_helpers.py +0 -420
  406. relationalai/clients/snowflake.py +0 -3869
  407. relationalai/clients/types.py +0 -113
  408. relationalai/clients/use_index_poller.py +0 -980
  409. relationalai/clients/util.py +0 -356
  410. relationalai/debugging.py +0 -389
  411. relationalai/dsl.py +0 -1749
  412. relationalai/early_access/builder/__init__.py +0 -30
  413. relationalai/early_access/builder/builder/__init__.py +0 -35
  414. relationalai/early_access/builder/snowflake/__init__.py +0 -12
  415. relationalai/early_access/builder/std/__init__.py +0 -25
  416. relationalai/early_access/builder/std/decimals/__init__.py +0 -12
  417. relationalai/early_access/builder/std/integers/__init__.py +0 -12
  418. relationalai/early_access/builder/std/math/__init__.py +0 -12
  419. relationalai/early_access/builder/std/strings/__init__.py +0 -14
  420. relationalai/early_access/devtools/__init__.py +0 -12
  421. relationalai/early_access/devtools/benchmark_lqp/__init__.py +0 -12
  422. relationalai/early_access/devtools/extract_lqp/__init__.py +0 -12
  423. relationalai/early_access/dsl/adapters/orm/adapter_qb.py +0 -427
  424. relationalai/early_access/dsl/adapters/orm/parser.py +0 -636
  425. relationalai/early_access/dsl/adapters/owl/adapter.py +0 -176
  426. relationalai/early_access/dsl/adapters/owl/parser.py +0 -160
  427. relationalai/early_access/dsl/bindings/common.py +0 -402
  428. relationalai/early_access/dsl/bindings/csv.py +0 -170
  429. relationalai/early_access/dsl/bindings/legacy/binding_models.py +0 -143
  430. relationalai/early_access/dsl/bindings/snowflake.py +0 -64
  431. relationalai/early_access/dsl/codegen/binder.py +0 -411
  432. relationalai/early_access/dsl/codegen/common.py +0 -79
  433. relationalai/early_access/dsl/codegen/helpers.py +0 -23
  434. relationalai/early_access/dsl/codegen/relations.py +0 -700
  435. relationalai/early_access/dsl/codegen/weaver.py +0 -417
  436. relationalai/early_access/dsl/core/builders/__init__.py +0 -47
  437. relationalai/early_access/dsl/core/builders/logic.py +0 -19
  438. relationalai/early_access/dsl/core/builders/scalar_constraint.py +0 -11
  439. relationalai/early_access/dsl/core/constraints/predicate/atomic.py +0 -455
  440. relationalai/early_access/dsl/core/constraints/predicate/universal.py +0 -73
  441. relationalai/early_access/dsl/core/constraints/scalar.py +0 -310
  442. relationalai/early_access/dsl/core/context.py +0 -13
  443. relationalai/early_access/dsl/core/cset.py +0 -132
  444. relationalai/early_access/dsl/core/exprs/__init__.py +0 -116
  445. relationalai/early_access/dsl/core/exprs/relational.py +0 -18
  446. relationalai/early_access/dsl/core/exprs/scalar.py +0 -412
  447. relationalai/early_access/dsl/core/instances.py +0 -44
  448. relationalai/early_access/dsl/core/logic/__init__.py +0 -193
  449. relationalai/early_access/dsl/core/logic/aggregation.py +0 -98
  450. relationalai/early_access/dsl/core/logic/exists.py +0 -223
  451. relationalai/early_access/dsl/core/logic/helper.py +0 -163
  452. relationalai/early_access/dsl/core/namespaces.py +0 -32
  453. relationalai/early_access/dsl/core/relations.py +0 -276
  454. relationalai/early_access/dsl/core/rules.py +0 -112
  455. relationalai/early_access/dsl/core/std/__init__.py +0 -45
  456. relationalai/early_access/dsl/core/temporal/recall.py +0 -6
  457. relationalai/early_access/dsl/core/types/__init__.py +0 -270
  458. relationalai/early_access/dsl/core/types/concepts.py +0 -128
  459. relationalai/early_access/dsl/core/types/constrained/__init__.py +0 -267
  460. relationalai/early_access/dsl/core/types/constrained/nominal.py +0 -143
  461. relationalai/early_access/dsl/core/types/constrained/subtype.py +0 -124
  462. relationalai/early_access/dsl/core/types/standard.py +0 -92
  463. relationalai/early_access/dsl/core/types/unconstrained.py +0 -50
  464. relationalai/early_access/dsl/core/types/variables.py +0 -203
  465. relationalai/early_access/dsl/ir/compiler.py +0 -318
  466. relationalai/early_access/dsl/ir/executor.py +0 -260
  467. relationalai/early_access/dsl/ontologies/constraints.py +0 -88
  468. relationalai/early_access/dsl/ontologies/export.py +0 -30
  469. relationalai/early_access/dsl/ontologies/models.py +0 -453
  470. relationalai/early_access/dsl/ontologies/python_printer.py +0 -303
  471. relationalai/early_access/dsl/ontologies/readings.py +0 -60
  472. relationalai/early_access/dsl/ontologies/relationships.py +0 -322
  473. relationalai/early_access/dsl/ontologies/roles.py +0 -87
  474. relationalai/early_access/dsl/ontologies/subtyping.py +0 -55
  475. relationalai/early_access/dsl/orm/constraints.py +0 -438
  476. relationalai/early_access/dsl/orm/measures/dimensions.py +0 -200
  477. relationalai/early_access/dsl/orm/measures/initializer.py +0 -16
  478. relationalai/early_access/dsl/orm/measures/measure_rules.py +0 -275
  479. relationalai/early_access/dsl/orm/measures/measures.py +0 -299
  480. relationalai/early_access/dsl/orm/measures/role_exprs.py +0 -268
  481. relationalai/early_access/dsl/orm/models.py +0 -256
  482. relationalai/early_access/dsl/orm/object_oriented_printer.py +0 -344
  483. relationalai/early_access/dsl/orm/printer.py +0 -469
  484. relationalai/early_access/dsl/orm/reasoners.py +0 -480
  485. relationalai/early_access/dsl/orm/relations.py +0 -19
  486. relationalai/early_access/dsl/orm/relationships.py +0 -251
  487. relationalai/early_access/dsl/orm/types.py +0 -42
  488. relationalai/early_access/dsl/orm/utils.py +0 -79
  489. relationalai/early_access/dsl/orm/verb.py +0 -204
  490. relationalai/early_access/dsl/physical_metadata/tables.py +0 -133
  491. relationalai/early_access/dsl/relations.py +0 -170
  492. relationalai/early_access/dsl/rulesets.py +0 -69
  493. relationalai/early_access/dsl/schemas/__init__.py +0 -450
  494. relationalai/early_access/dsl/schemas/builder.py +0 -48
  495. relationalai/early_access/dsl/schemas/comp_names.py +0 -51
  496. relationalai/early_access/dsl/schemas/components.py +0 -203
  497. relationalai/early_access/dsl/schemas/contexts.py +0 -156
  498. relationalai/early_access/dsl/schemas/exprs.py +0 -89
  499. relationalai/early_access/dsl/schemas/fragments.py +0 -464
  500. relationalai/early_access/dsl/serialization.py +0 -79
  501. relationalai/early_access/dsl/serialize/exporter.py +0 -163
  502. relationalai/early_access/dsl/snow/api.py +0 -104
  503. relationalai/early_access/dsl/snow/common.py +0 -76
  504. relationalai/early_access/dsl/state_mgmt/__init__.py +0 -129
  505. relationalai/early_access/dsl/state_mgmt/state_charts.py +0 -125
  506. relationalai/early_access/dsl/state_mgmt/transitions.py +0 -130
  507. relationalai/early_access/dsl/types/__init__.py +0 -40
  508. relationalai/early_access/dsl/types/concepts.py +0 -12
  509. relationalai/early_access/dsl/types/entities.py +0 -135
  510. relationalai/early_access/dsl/types/values.py +0 -17
  511. relationalai/early_access/dsl/utils.py +0 -102
  512. relationalai/early_access/graphs/__init__.py +0 -13
  513. relationalai/early_access/lqp/__init__.py +0 -12
  514. relationalai/early_access/lqp/compiler/__init__.py +0 -12
  515. relationalai/early_access/lqp/constructors/__init__.py +0 -18
  516. relationalai/early_access/lqp/executor/__init__.py +0 -12
  517. relationalai/early_access/lqp/ir/__init__.py +0 -12
  518. relationalai/early_access/lqp/passes/__init__.py +0 -12
  519. relationalai/early_access/lqp/pragmas/__init__.py +0 -12
  520. relationalai/early_access/lqp/primitives/__init__.py +0 -12
  521. relationalai/early_access/lqp/types/__init__.py +0 -12
  522. relationalai/early_access/lqp/utils/__init__.py +0 -12
  523. relationalai/early_access/lqp/validators/__init__.py +0 -12
  524. relationalai/early_access/metamodel/__init__.py +0 -58
  525. relationalai/early_access/metamodel/builtins/__init__.py +0 -12
  526. relationalai/early_access/metamodel/compiler/__init__.py +0 -12
  527. relationalai/early_access/metamodel/dependency/__init__.py +0 -12
  528. relationalai/early_access/metamodel/factory/__init__.py +0 -17
  529. relationalai/early_access/metamodel/helpers/__init__.py +0 -12
  530. relationalai/early_access/metamodel/ir/__init__.py +0 -14
  531. relationalai/early_access/metamodel/rewrite/__init__.py +0 -7
  532. relationalai/early_access/metamodel/typer/__init__.py +0 -3
  533. relationalai/early_access/metamodel/typer/typer/__init__.py +0 -12
  534. relationalai/early_access/metamodel/types/__init__.py +0 -15
  535. relationalai/early_access/metamodel/util/__init__.py +0 -15
  536. relationalai/early_access/metamodel/visitor/__init__.py +0 -12
  537. relationalai/early_access/rel/__init__.py +0 -12
  538. relationalai/early_access/rel/executor/__init__.py +0 -12
  539. relationalai/early_access/rel/rel_utils/__init__.py +0 -12
  540. relationalai/early_access/rel/rewrite/__init__.py +0 -7
  541. relationalai/early_access/solvers/__init__.py +0 -19
  542. relationalai/early_access/sql/__init__.py +0 -11
  543. relationalai/early_access/sql/executor/__init__.py +0 -3
  544. relationalai/early_access/sql/rewrite/__init__.py +0 -3
  545. relationalai/early_access/tests/logging/__init__.py +0 -12
  546. relationalai/early_access/tests/test_snapshot_base/__init__.py +0 -12
  547. relationalai/early_access/tests/utils/__init__.py +0 -12
  548. relationalai/environments/__init__.py +0 -35
  549. relationalai/environments/base.py +0 -381
  550. relationalai/environments/colab.py +0 -14
  551. relationalai/environments/generic.py +0 -71
  552. relationalai/environments/ipython.py +0 -68
  553. relationalai/environments/jupyter.py +0 -9
  554. relationalai/environments/snowbook.py +0 -169
  555. relationalai/errors.py +0 -2455
  556. relationalai/experimental/SF.py +0 -38
  557. relationalai/experimental/inspect.py +0 -47
  558. relationalai/experimental/pathfinder/__init__.py +0 -158
  559. relationalai/experimental/pathfinder/api.py +0 -160
  560. relationalai/experimental/pathfinder/automaton.py +0 -584
  561. relationalai/experimental/pathfinder/bridge.py +0 -226
  562. relationalai/experimental/pathfinder/compiler.py +0 -416
  563. relationalai/experimental/pathfinder/datalog.py +0 -214
  564. relationalai/experimental/pathfinder/diagnostics.py +0 -56
  565. relationalai/experimental/pathfinder/filter.py +0 -236
  566. relationalai/experimental/pathfinder/glushkov.py +0 -439
  567. relationalai/experimental/pathfinder/options.py +0 -265
  568. relationalai/experimental/pathfinder/pathfinder-v0.7.0.rel +0 -1951
  569. relationalai/experimental/pathfinder/rpq.py +0 -344
  570. relationalai/experimental/pathfinder/transition.py +0 -200
  571. relationalai/experimental/pathfinder/utils.py +0 -26
  572. relationalai/experimental/paths/README.md +0 -107
  573. relationalai/experimental/paths/api.py +0 -143
  574. relationalai/experimental/paths/benchmarks/grid_graph.py +0 -37
  575. relationalai/experimental/paths/code_organization.md +0 -2
  576. relationalai/experimental/paths/examples/Movies.ipynb +0 -16328
  577. relationalai/experimental/paths/examples/basic_example.py +0 -40
  578. relationalai/experimental/paths/examples/minimal_engine_warmup.py +0 -3
  579. relationalai/experimental/paths/examples/movie_example.py +0 -77
  580. relationalai/experimental/paths/examples/movies_data/actedin.csv +0 -193
  581. relationalai/experimental/paths/examples/movies_data/directed.csv +0 -45
  582. relationalai/experimental/paths/examples/movies_data/follows.csv +0 -7
  583. relationalai/experimental/paths/examples/movies_data/movies.csv +0 -39
  584. relationalai/experimental/paths/examples/movies_data/person.csv +0 -134
  585. relationalai/experimental/paths/examples/movies_data/produced.csv +0 -16
  586. relationalai/experimental/paths/examples/movies_data/ratings.csv +0 -10
  587. relationalai/experimental/paths/examples/movies_data/wrote.csv +0 -11
  588. relationalai/experimental/paths/examples/paths_benchmark.py +0 -115
  589. relationalai/experimental/paths/examples/paths_example.py +0 -116
  590. relationalai/experimental/paths/examples/pattern_to_automaton.py +0 -28
  591. relationalai/experimental/paths/find_paths_via_automaton.py +0 -85
  592. relationalai/experimental/paths/graph.py +0 -185
  593. relationalai/experimental/paths/path_algorithms/find_paths.py +0 -280
  594. relationalai/experimental/paths/path_algorithms/one_sided_ball_repetition.py +0 -26
  595. relationalai/experimental/paths/path_algorithms/one_sided_ball_upto.py +0 -111
  596. relationalai/experimental/paths/path_algorithms/single.py +0 -59
  597. relationalai/experimental/paths/path_algorithms/two_sided_balls_repetition.py +0 -39
  598. relationalai/experimental/paths/path_algorithms/two_sided_balls_upto.py +0 -103
  599. relationalai/experimental/paths/path_algorithms/usp-old.py +0 -130
  600. relationalai/experimental/paths/path_algorithms/usp-tuple.py +0 -183
  601. relationalai/experimental/paths/path_algorithms/usp.py +0 -150
  602. relationalai/experimental/paths/product_graph.py +0 -93
  603. relationalai/experimental/paths/rpq/automaton.py +0 -584
  604. relationalai/experimental/paths/rpq/diagnostics.py +0 -56
  605. relationalai/experimental/paths/rpq/rpq.py +0 -378
  606. relationalai/experimental/paths/tests/tests_limit_sp_max_length.py +0 -90
  607. relationalai/experimental/paths/tests/tests_limit_sp_multiple.py +0 -119
  608. relationalai/experimental/paths/tests/tests_limit_sp_single.py +0 -104
  609. relationalai/experimental/paths/tests/tests_limit_walks_multiple.py +0 -113
  610. relationalai/experimental/paths/tests/tests_limit_walks_single.py +0 -149
  611. relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_multiple.py +0 -70
  612. relationalai/experimental/paths/tests/tests_one_sided_ball_repetition_single.py +0 -64
  613. relationalai/experimental/paths/tests/tests_one_sided_ball_upto_multiple.py +0 -115
  614. relationalai/experimental/paths/tests/tests_one_sided_ball_upto_single.py +0 -75
  615. relationalai/experimental/paths/tests/tests_single_paths.py +0 -152
  616. relationalai/experimental/paths/tests/tests_single_walks.py +0 -208
  617. relationalai/experimental/paths/tests/tests_single_walks_undirected.py +0 -297
  618. relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_multiple.py +0 -107
  619. relationalai/experimental/paths/tests/tests_two_sided_balls_repetition_single.py +0 -76
  620. relationalai/experimental/paths/tests/tests_two_sided_balls_upto_multiple.py +0 -76
  621. relationalai/experimental/paths/tests/tests_two_sided_balls_upto_single.py +0 -110
  622. relationalai/experimental/paths/tests/tests_usp_nsp_multiple.py +0 -229
  623. relationalai/experimental/paths/tests/tests_usp_nsp_single.py +0 -108
  624. relationalai/experimental/paths/tree_agg.py +0 -168
  625. relationalai/experimental/paths/utilities/iterators.py +0 -27
  626. relationalai/experimental/paths/utilities/prefix_sum.py +0 -91
  627. relationalai/experimental/solvers.py +0 -1087
  628. relationalai/loaders/csv.py +0 -195
  629. relationalai/loaders/loader.py +0 -177
  630. relationalai/loaders/types.py +0 -23
  631. relationalai/rel_emitter.py +0 -373
  632. relationalai/rel_utils.py +0 -185
  633. relationalai/semantics/designs/query_builder/identify_by.md +0 -106
  634. relationalai/semantics/devtools/benchmark_lqp.py +0 -536
  635. relationalai/semantics/devtools/compilation_manager.py +0 -294
  636. relationalai/semantics/devtools/extract_lqp.py +0 -110
  637. relationalai/semantics/internal/internal.py +0 -3785
  638. relationalai/semantics/internal/snowflake.py +0 -324
  639. relationalai/semantics/lqp/README.md +0 -34
  640. relationalai/semantics/lqp/builtins.py +0 -16
  641. relationalai/semantics/lqp/compiler.py +0 -22
  642. relationalai/semantics/lqp/constructors.py +0 -68
  643. relationalai/semantics/lqp/executor.py +0 -469
  644. relationalai/semantics/lqp/intrinsics.py +0 -24
  645. relationalai/semantics/lqp/model2lqp.py +0 -839
  646. relationalai/semantics/lqp/passes.py +0 -680
  647. relationalai/semantics/lqp/primitives.py +0 -252
  648. relationalai/semantics/lqp/result_helpers.py +0 -202
  649. relationalai/semantics/lqp/rewrite/annotate_constraints.py +0 -57
  650. relationalai/semantics/lqp/rewrite/cdc.py +0 -216
  651. relationalai/semantics/lqp/rewrite/extract_common.py +0 -338
  652. relationalai/semantics/lqp/rewrite/extract_keys.py +0 -449
  653. relationalai/semantics/lqp/rewrite/function_annotations.py +0 -114
  654. relationalai/semantics/lqp/rewrite/functional_dependencies.py +0 -314
  655. relationalai/semantics/lqp/rewrite/quantify_vars.py +0 -296
  656. relationalai/semantics/lqp/rewrite/splinter.py +0 -76
  657. relationalai/semantics/lqp/types.py +0 -101
  658. relationalai/semantics/lqp/utils.py +0 -160
  659. relationalai/semantics/lqp/validators.py +0 -57
  660. relationalai/semantics/metamodel/compiler.py +0 -133
  661. relationalai/semantics/metamodel/dependency.py +0 -862
  662. relationalai/semantics/metamodel/executor.py +0 -61
  663. relationalai/semantics/metamodel/factory.py +0 -287
  664. relationalai/semantics/metamodel/helpers.py +0 -361
  665. relationalai/semantics/metamodel/rewrite/discharge_constraints.py +0 -39
  666. relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +0 -210
  667. relationalai/semantics/metamodel/rewrite/extract_nested_logicals.py +0 -78
  668. relationalai/semantics/metamodel/rewrite/flatten.py +0 -549
  669. relationalai/semantics/metamodel/rewrite/format_outputs.py +0 -165
  670. relationalai/semantics/metamodel/typer/checker.py +0 -353
  671. relationalai/semantics/metamodel/typer/typer.py +0 -1395
  672. relationalai/semantics/reasoners/__init__.py +0 -10
  673. relationalai/semantics/reasoners/graph/README.md +0 -620
  674. relationalai/semantics/reasoners/graph/__init__.py +0 -37
  675. relationalai/semantics/reasoners/graph/core.py +0 -9020
  676. relationalai/semantics/reasoners/graph/design/beyond_demand_transform.md +0 -797
  677. relationalai/semantics/reasoners/graph/tests/README.md +0 -21
  678. relationalai/semantics/reasoners/optimization/__init__.py +0 -68
  679. relationalai/semantics/reasoners/optimization/common.py +0 -88
  680. relationalai/semantics/reasoners/optimization/solvers_dev.py +0 -568
  681. relationalai/semantics/reasoners/optimization/solvers_pb.py +0 -1163
  682. relationalai/semantics/rel/builtins.py +0 -40
  683. relationalai/semantics/rel/compiler.py +0 -989
  684. relationalai/semantics/rel/executor.py +0 -359
  685. relationalai/semantics/rel/rel.py +0 -482
  686. relationalai/semantics/rel/rel_utils.py +0 -276
  687. relationalai/semantics/snowflake/__init__.py +0 -3
  688. relationalai/semantics/sql/compiler.py +0 -2503
  689. relationalai/semantics/sql/executor/duck_db.py +0 -52
  690. relationalai/semantics/sql/executor/result_helpers.py +0 -64
  691. relationalai/semantics/sql/executor/snowflake.py +0 -145
  692. relationalai/semantics/sql/rewrite/denormalize.py +0 -222
  693. relationalai/semantics/sql/rewrite/double_negation.py +0 -49
  694. relationalai/semantics/sql/rewrite/recursive_union.py +0 -127
  695. relationalai/semantics/sql/rewrite/sort_output_query.py +0 -246
  696. relationalai/semantics/sql/sql.py +0 -504
  697. relationalai/semantics/std/pragmas.py +0 -11
  698. relationalai/semantics/std/std.py +0 -14
  699. relationalai/semantics/tests/test_snapshot_abstract.py +0 -143
  700. relationalai/semantics/tests/test_snapshot_base.py +0 -9
  701. relationalai/semantics/tests/utils.py +0 -46
  702. relationalai/std/__init__.py +0 -70
  703. relationalai/tools/cli.py +0 -1940
  704. relationalai/tools/cli_controls.py +0 -1826
  705. relationalai/tools/cli_helpers.py +0 -390
  706. relationalai/tools/debugger_client.py +0 -109
  707. relationalai/tools/debugger_server.py +0 -302
  708. relationalai/tools/dev.py +0 -685
  709. relationalai/tools/notes +0 -7
  710. relationalai/tools/qb_debugger.py +0 -425
  711. relationalai/util/clean_up_databases.py +0 -95
  712. relationalai/util/list_databases.py +0 -9
  713. relationalai/util/otel_configuration.py +0 -25
  714. relationalai/util/otel_handler.py +0 -484
  715. relationalai/util/snowflake_handler.py +0 -88
  716. relationalai/util/span_format_test.py +0 -43
  717. relationalai/util/span_tracker.py +0 -207
  718. relationalai/util/spans_file_handler.py +0 -72
  719. relationalai/util/tracing_handler.py +0 -34
  720. relationalai-0.12.13.dist-info/METADATA +0 -74
  721. relationalai-0.12.13.dist-info/RECORD +0 -449
  722. relationalai-0.12.13.dist-info/WHEEL +0 -4
  723. relationalai-0.12.13.dist-info/entry_points.txt +0 -3
  724. relationalai-0.12.13.dist-info/licenses/LICENSE +0 -202
  725. relationalai_test_util/__init__.py +0 -4
  726. relationalai_test_util/fixtures.py +0 -228
  727. relationalai_test_util/snapshot.py +0 -252
  728. relationalai_test_util/traceback.py +0 -118
  729. /relationalai/{analysis → semantics/frontend}/__init__.py +0 -0
  730. /relationalai/{auth/__init__.py → semantics/metamodel/metamodel_compiler.py} +0 -0
  731. /relationalai/{early_access → shims}/__init__.py +0 -0
  732. {relationalai/early_access/dsl/adapters → v0/relationalai/analysis}/__init__.py +0 -0
  733. {relationalai → v0/relationalai}/analysis/mechanistic.py +0 -0
  734. {relationalai → v0/relationalai}/analysis/whynot.py +0 -0
  735. {relationalai/early_access/dsl/adapters/orm → v0/relationalai/auth}/__init__.py +0 -0
  736. {relationalai → v0/relationalai}/auth/jwt_generator.py +0 -0
  737. {relationalai → v0/relationalai}/auth/oauth_callback_server.py +0 -0
  738. {relationalai → v0/relationalai}/auth/token_handler.py +0 -0
  739. {relationalai → v0/relationalai}/auth/util.py +0 -0
  740. {relationalai → v0/relationalai}/clients/__init__.py +0 -0
  741. {relationalai → v0/relationalai}/clients/cache_store.py +0 -0
  742. {relationalai → v0/relationalai}/compiler.py +0 -0
  743. {relationalai → v0/relationalai}/dependencies.py +0 -0
  744. {relationalai → v0/relationalai}/docutils.py +0 -0
  745. {relationalai/early_access/dsl/adapters/owl → v0/relationalai/early_access}/__init__.py +0 -0
  746. {relationalai → v0/relationalai}/early_access/dsl/__init__.py +0 -0
  747. {relationalai/early_access/dsl/bindings → v0/relationalai/early_access/dsl/adapters}/__init__.py +0 -0
  748. {relationalai/early_access/dsl/bindings/legacy → v0/relationalai/early_access/dsl/adapters/orm}/__init__.py +0 -0
  749. {relationalai → v0/relationalai}/early_access/dsl/adapters/orm/model.py +0 -0
  750. {relationalai/early_access/dsl/codegen → v0/relationalai/early_access/dsl/adapters/owl}/__init__.py +0 -0
  751. {relationalai → v0/relationalai}/early_access/dsl/adapters/owl/model.py +0 -0
  752. {relationalai/early_access/dsl/core/temporal → v0/relationalai/early_access/dsl/bindings}/__init__.py +0 -0
  753. {relationalai/early_access/dsl/ir → v0/relationalai/early_access/dsl/bindings/legacy}/__init__.py +0 -0
  754. {relationalai/early_access/dsl/ontologies → v0/relationalai/early_access/dsl/codegen}/__init__.py +0 -0
  755. {relationalai → v0/relationalai}/early_access/dsl/constants.py +0 -0
  756. {relationalai → v0/relationalai}/early_access/dsl/core/__init__.py +0 -0
  757. {relationalai → v0/relationalai}/early_access/dsl/core/constraints/__init__.py +0 -0
  758. {relationalai → v0/relationalai}/early_access/dsl/core/constraints/predicate/__init__.py +0 -0
  759. {relationalai → v0/relationalai}/early_access/dsl/core/stack.py +0 -0
  760. {relationalai/early_access/dsl/orm → v0/relationalai/early_access/dsl/core/temporal}/__init__.py +0 -0
  761. {relationalai → v0/relationalai}/early_access/dsl/core/utils.py +0 -0
  762. {relationalai/early_access/dsl/orm/measures → v0/relationalai/early_access/dsl/ir}/__init__.py +0 -0
  763. {relationalai/early_access/dsl/physical_metadata → v0/relationalai/early_access/dsl/ontologies}/__init__.py +0 -0
  764. {relationalai → v0/relationalai}/early_access/dsl/ontologies/raw_source.py +0 -0
  765. {relationalai/early_access/dsl/serialize → v0/relationalai/early_access/dsl/orm}/__init__.py +0 -0
  766. {relationalai/early_access/dsl/snow → v0/relationalai/early_access/dsl/orm/measures}/__init__.py +0 -0
  767. {relationalai → v0/relationalai}/early_access/dsl/orm/reasoner_errors.py +0 -0
  768. {relationalai/loaders → v0/relationalai/early_access/dsl/physical_metadata}/__init__.py +0 -0
  769. {relationalai/semantics/tests → v0/relationalai/early_access/dsl/serialize}/__init__.py +0 -0
  770. {relationalai → v0/relationalai}/early_access/dsl/serialize/binding_model.py +0 -0
  771. {relationalai → v0/relationalai}/early_access/dsl/serialize/model.py +0 -0
  772. {relationalai/tools → v0/relationalai/early_access/dsl/snow}/__init__.py +0 -0
  773. {relationalai → v0/relationalai}/early_access/tests/__init__.py +0 -0
  774. {relationalai → v0/relationalai}/environments/ci.py +0 -0
  775. {relationalai → v0/relationalai}/environments/hex.py +0 -0
  776. {relationalai → v0/relationalai}/environments/terminal.py +0 -0
  777. {relationalai → v0/relationalai}/experimental/__init__.py +0 -0
  778. {relationalai → v0/relationalai}/experimental/graphs.py +0 -0
  779. {relationalai → v0/relationalai}/experimental/paths/__init__.py +0 -0
  780. {relationalai → v0/relationalai}/experimental/paths/benchmarks/__init__.py +0 -0
  781. {relationalai → v0/relationalai}/experimental/paths/path_algorithms/__init__.py +0 -0
  782. {relationalai → v0/relationalai}/experimental/paths/rpq/__init__.py +0 -0
  783. {relationalai → v0/relationalai}/experimental/paths/rpq/filter.py +0 -0
  784. {relationalai → v0/relationalai}/experimental/paths/rpq/glushkov.py +0 -0
  785. {relationalai → v0/relationalai}/experimental/paths/rpq/transition.py +0 -0
  786. {relationalai → v0/relationalai}/experimental/paths/utilities/__init__.py +0 -0
  787. {relationalai → v0/relationalai}/experimental/paths/utilities/utilities.py +0 -0
  788. {relationalai → v0/relationalai}/metagen.py +0 -0
  789. {relationalai → v0/relationalai}/metamodel.py +0 -0
  790. {relationalai → v0/relationalai}/rel.py +0 -0
  791. {relationalai → v0/relationalai}/semantics/devtools/__init__.py +0 -0
  792. {relationalai → v0/relationalai}/semantics/internal/__init__.py +0 -0
  793. {relationalai → v0/relationalai}/semantics/internal/annotations.py +0 -0
  794. {relationalai → v0/relationalai}/semantics/lqp/__init__.py +0 -0
  795. {relationalai → v0/relationalai}/semantics/lqp/ir.py +0 -0
  796. {relationalai → v0/relationalai}/semantics/lqp/pragmas.py +0 -0
  797. {relationalai → v0/relationalai}/semantics/lqp/rewrite/__init__.py +0 -0
  798. {relationalai → v0/relationalai}/semantics/metamodel/dataflow.py +0 -0
  799. {relationalai → v0/relationalai}/semantics/metamodel/ir.py +0 -0
  800. {relationalai → v0/relationalai}/semantics/metamodel/rewrite/__init__.py +0 -0
  801. {relationalai → v0/relationalai}/semantics/metamodel/typer/__init__.py +0 -0
  802. {relationalai → v0/relationalai}/semantics/metamodel/types.py +0 -0
  803. {relationalai → v0/relationalai}/semantics/metamodel/util.py +0 -0
  804. {relationalai → v0/relationalai}/semantics/metamodel/visitor.py +0 -0
  805. {relationalai → v0/relationalai}/semantics/reasoners/experimental/__init__.py +0 -0
  806. {relationalai → v0/relationalai}/semantics/rel/__init__.py +0 -0
  807. {relationalai → v0/relationalai}/semantics/sql/__init__.py +0 -0
  808. {relationalai → v0/relationalai}/semantics/sql/executor/__init__.py +0 -0
  809. {relationalai → v0/relationalai}/semantics/sql/rewrite/__init__.py +0 -0
  810. {relationalai → v0/relationalai}/semantics/tests/logging.py +0 -0
  811. {relationalai → v0/relationalai}/std/aggregates.py +0 -0
  812. {relationalai → v0/relationalai}/std/dates.py +0 -0
  813. {relationalai → v0/relationalai}/std/graphs.py +0 -0
  814. {relationalai → v0/relationalai}/std/inspect.py +0 -0
  815. {relationalai → v0/relationalai}/std/math.py +0 -0
  816. {relationalai → v0/relationalai}/std/re.py +0 -0
  817. {relationalai → v0/relationalai}/std/strings.py +0 -0
  818. {relationalai → v0/relationalai}/tools/cleanup_snapshots.py +0 -0
  819. {relationalai → v0/relationalai}/tools/constants.py +0 -0
  820. {relationalai → v0/relationalai}/tools/query_utils.py +0 -0
  821. {relationalai → v0/relationalai}/tools/snapshot_viewer.py +0 -0
  822. {relationalai → v0/relationalai}/util/__init__.py +0 -0
  823. {relationalai → v0/relationalai}/util/constants.py +0 -0
  824. {relationalai → v0/relationalai}/util/graph.py +0 -0
  825. {relationalai → v0/relationalai}/util/timeout.py +0 -0
@@ -0,0 +1,1378 @@
1
+ from __future__ import annotations
2
+
3
+ from collections import defaultdict
4
+ from dataclasses import dataclass
5
+ from typing import Optional, Union, Iterable, TypeVar, Tuple
6
+
7
+ from ...util import tracing
8
+ from ...util.error import err, exc, Source, Part
9
+ from ...util.naming import sanitize
10
+
11
+ from . import metamodel as mm, builtins as bt
12
+ from .rewriter import Walker, Rewriter, NO_WALK
13
+ from .builtins import builtins as b
14
+ from ...util.structures import OrderedSet
15
+
16
+ #--------------------------------------------------
17
+ # Typer pass
18
+ #--------------------------------------------------
19
+
20
+ T = TypeVar("T", bound=mm.Model | mm.Task)
21
+
22
+ class Typer:
23
+ def __init__(self, enforce=False):
24
+ self.enforce = enforce
25
+ self.model_net: Optional[PropagationNetwork] = None
26
+ # TODO: remove this once we are using spans for the debugger
27
+ self.last_net: Optional[PropagationNetwork] = None
28
+
29
+ def infer_model(self, model: mm.Model) -> mm.Model:
30
+ """
31
+ Infer types for the given model, returning a new model with updated types and
32
+ storing the results within this typer so that subsequent calls can reuse them.
33
+ """
34
+ # create a brand new network with data from this model
35
+ self.model_net = PropagationNetwork(model)
36
+ return self._infer(model, self.model_net)
37
+
38
+ def infer_query(self, query: mm.Task) -> mm.Task:
39
+ """
40
+ Infer types for the given query, returning a new query with updated types. If the
41
+ typer was used to infer a model previously, the information from that model analysis
42
+ will be reused.
43
+ """
44
+ if self.model_net is not None:
45
+ net = PropagationNetwork(self.model_net.model)
46
+ net.load_types(self.model_net.resolved_types)
47
+ else:
48
+ net = PropagationNetwork(mm.Model(root=query))
49
+ self.last_net = net
50
+ return self._infer(query, net)
51
+
52
+ #--------------------------------------------------
53
+ # Internal implementation
54
+ #--------------------------------------------------
55
+
56
+ def _infer(self, node: T, net: PropagationNetwork) -> T:
57
+ # build the propagation network by analyzing the model
58
+ with tracing.span("typer.analyze"):
59
+ Analyzer(net).analyze(node)
60
+
61
+ # propagate the types through the network
62
+ with tracing.span("typer.propagate") as span:
63
+ net.propagate()
64
+ # span["type_graph"] = net.to_mermaid()
65
+ # span["type_report"] = net.to_fish()
66
+
67
+ # replace the fields in the model with the new types
68
+ with tracing.span("typer.replace"):
69
+ replacer = Replacer(net)
70
+ final = replacer.rewrite(node)
71
+
72
+ # report any errors found during typing
73
+ if net.errors:
74
+ for error in net.errors:
75
+ error.report()
76
+ if self.enforce:
77
+ exc("TyperError", "Type errors detected during type inference.")
78
+
79
+ return final
80
+
81
+
82
+ #--------------------------------------------------
83
+ # Propagation Network
84
+ #--------------------------------------------------
85
+
86
+ # Nodes in the network can be:
87
+ #
88
+ # Literal: always have a concrete type, so types flow from them.
89
+ # Var: used to move values (and types) between different tasks.
90
+ # Field:
91
+ # - abstract field in an update to a model relation: we need to resolve the type of the
92
+ # field, so it is the target of an edge from the arg
93
+ # - output field being looked up into an abstract var: the field is the source of an
94
+ # edge (even if the field is abstract; we may figure this out eventually)
95
+ # Task: only tasks that reference relations are represented in the network (lookup, update,
96
+ # and aggregate). If a task is in the network it means we need to figure something out
97
+ # about the relation it references. A relation can fall into one of these categories:
98
+ # - placeholder: the relation is just a placeholder that needs to replaced by concrete
99
+ # relations from the model that have the same name and arity. The task will be the
100
+ # target of edges from its args, because they all need to be resolved before deciding
101
+ # which concrete relation(s) to use.
102
+ # - overload: the relation has multiple overloads, so we need to decide which one to use.
103
+ # Similarly, the task will be the target of edges from its args.
104
+ # - abstract: the relation has abstract types in its fields, so we need to resolve which
105
+ # concrete types to use. This includes relations that have type variables, and we
106
+ # need to ensure that concrete types are used consistently. The task will be the
107
+ # target of edges from the args that are abstract.
108
+ # - concrete: the relation is concrete, but we still need to ensure that types match.
109
+ #
110
+ # - type preserver:
111
+ # The task can be:
112
+ # - target: if there are inputs that need to be resolved before we can analyze the task
113
+ # (e.g. an abstract var)
114
+ # - source: if the task has outputs that are abstract and need to flow to other nodes
115
+ #
116
+
117
+
118
+ # The core idea of the typer is to build a propagation network where nodes
119
+ # are vars, fields, or overloaded lookups/updates/aggregates. The intuition
120
+ # is that _all_ types in the IR ultimately flow from relation fields, so if
121
+ # we figure those out we just need to propagate their types to unknown vars, which
122
+ # may then flow into other fields and so on.
123
+
124
+ # This means the network only needs to contain nodes that either directly flow into
125
+ # an abstract node or are themselves abstract. We need to track overloads because
126
+ # their arguments effectively act like abstract vars until we've resolved the final types.
127
+
128
+ Node = Union[mm.Var, mm.Field, mm.Literal, mm.Lookup, mm.Update, mm.Aggregate]
129
+
130
+ class PropagationNetwork():
131
+ def __init__(self, model: mm.Model):
132
+ # the model that is either being analyzed or that is the context for a query
133
+ self.model = model
134
+
135
+ # the resolved types of nodes in the network
136
+ self.resolved_types: dict[Node, mm.Type] = {}
137
+
138
+ # map from unresolved placeholder relations to their potential target replacements
139
+ self.potential_targets: dict[mm.Relation, list[mm.Relation]] = {}
140
+
141
+ # track the set of nodes that represent entry points into the network
142
+ self.roots = OrderedSet()
143
+ # we separately want to track nodes that were loaded from a previous run
144
+ # so that even if we have edges to them, we _still_ consider them roots
145
+ # and properly propagate types from them at the beginning
146
+ self.loaded_roots = set()
147
+
148
+ # edges in the propagation network, from one node to potentially many
149
+ self.edges:dict[Node, OrderedSet[Node]] = defaultdict(lambda: OrderedSet())
150
+ self.back_edges:dict[Node, OrderedSet[Node]] = defaultdict(lambda: OrderedSet())
151
+ # all nodes that are the target of an edge (to find roots)
152
+ self.has_incoming = set()
153
+
154
+ # type requirements: for a var with abstract declared type, the set of fields that
155
+ # it must match the type of because it flows into them
156
+ self.type_requirements:dict[mm.Var, OrderedSet[mm.Field]] = defaultdict(lambda: OrderedSet())
157
+
158
+ # all errors collected during inference
159
+ self.errors:list[TyperError] = []
160
+
161
+ # overloads resolved for a lookup/update/aggregate, by node id. This is only for
162
+ # relations that declare overloads
163
+ self.resolved_overload:dict[int, mm.Overload] = {}
164
+ # placeholders resolved for a lookup, by node id. This is only for relations that
165
+ # are placeholders (i.e. only Any fields) and will be replaced by references to
166
+ # these concrete relations. E.g. a query for "name(Any, Any)" may be replaced by
167
+ # the union of "name(Dog, String)" and name(Cat, String)".
168
+ self.resolved_placeholder:dict[int, list[mm.Relation]] = {}
169
+ # for a given lookup/update/aggregate that involves numbers, the specific number
170
+ # type resolved for it.
171
+ self.resolved_number:dict[int, mm.NumberType] = {}
172
+ # keep track of references resolved to avoid re-resolving
173
+ self.resolved_references:set[int] = set()
174
+
175
+ #--------------------------------------------------
176
+ # Error reporting
177
+ #--------------------------------------------------
178
+
179
+ def type_mismatch(self, node: Node, expected: mm.Type, actual: mm.Type):
180
+ self.errors.append(TypeMismatch(node, expected, actual))
181
+
182
+ def invalid_type(self, node: Node, type: mm.Type):
183
+ self.errors.append(InvalidType(node, type))
184
+
185
+ def unresolved_overload(self, node: mm.Lookup|mm.Update|mm.Aggregate):
186
+ # TODO - consider renaming this to UnresolvedReference
187
+ self.errors.append(UnresolvedOverload(node, [self.resolve(a) for a in node.args]))
188
+
189
+ def unresolved_type(self, node: Node):
190
+ self.errors.append(UnresolvedType(node))
191
+
192
+ def has_errors(self, node: Node) -> bool:
193
+ for mismatch in self.errors:
194
+ if mismatch.node == node:
195
+ return True
196
+ return False
197
+
198
+ #--------------------------------------------------
199
+ # Types and Edges
200
+ #--------------------------------------------------
201
+
202
+ def add_edge(self, source: Node, target: Node):
203
+ # manage roots
204
+ if target in self.roots and target not in self.loaded_roots:
205
+ self.roots.remove(target)
206
+ if source not in self.has_incoming:
207
+ self.roots.add(source)
208
+ # register edge
209
+ self.edges[source].add(target)
210
+ self.back_edges[target].add(source)
211
+ self.has_incoming.add(target)
212
+
213
+ def add_resolved_type(self, node: Node, type: mm.Type):
214
+ """ Register that this node was resolved to have this type. """
215
+ if node in self.resolved_types:
216
+ self.resolved_types[node] = merge_types(self.resolved_types[node], type)
217
+ else:
218
+ self.resolved_types[node] = type
219
+
220
+ def add_type_requirement(self, source: mm.Var, field: mm.Field):
221
+ """ Register that this var, which has an abstract declared type, must match the type
222
+ of this field as it flows into it.. """
223
+ self.type_requirements[source].add(field)
224
+
225
+ #--------------------------------------------------
226
+ # Load previous types
227
+ #--------------------------------------------------
228
+
229
+ def load_types(self, type_dict: dict[Node, mm.Type]):
230
+ for node, type in type_dict.items():
231
+ if isinstance(node, (mm.Field)):
232
+ self.add_resolved_type(node, type)
233
+ self.loaded_roots.add(node)
234
+ self.roots.add(node)
235
+
236
+ #--------------------------------------------------
237
+ # Resolve
238
+ #--------------------------------------------------
239
+
240
+ def resolve(self, value: Node|mm.Value) -> mm.Type:
241
+ if isinstance(value, (mm.Var, mm.Field, mm.Literal)):
242
+ return self.resolved_types.get(value) or to_type(value)
243
+ assert not isinstance(value, (mm.Lookup, mm.Update, mm.Aggregate)), "Should never try to resolve a task"
244
+ return to_type(value)
245
+
246
+ #--------------------------------------------------
247
+ # Overloads
248
+ #--------------------------------------------------
249
+
250
+ def get_fields(self, relation: mm.Relation, name: str) -> Iterable[mm.Field]:
251
+ """ Get the fields of this relation, potentially reordered to match the reading with the given name."""
252
+ if name == relation.name:
253
+ return relation.fields
254
+ for reading in relation.readings:
255
+ if reading.name == name:
256
+ # reorder the fields to match the correct ordering
257
+ fields = []
258
+ for idx in reading.field_order:
259
+ fields.append(relation.fields[idx])
260
+ return fields
261
+ return []
262
+
263
+ def get_args(self, node: mm.Lookup, target: mm.Relation):
264
+ """ Get the args of this lookup, potentially reordered to match the reading with the given name."""
265
+ for reading in target.readings:
266
+ if reading.name == node.relation.name:
267
+ # reorder the args to match the correct ordering
268
+ args = []
269
+ for idx in reading.field_order:
270
+ args.append(node.args[idx])
271
+ return args
272
+ return node.args
273
+
274
+
275
+ def resolve_reference(self, op: mm.Lookup|mm.Update|mm.Aggregate) -> Optional[mm.Overload|list[mm.Relation]]:
276
+ # check if we have any unresolved args that are required, if all of our args
277
+ # are unresolved (len(incoming) == len(relation.fields)) then we have no information
278
+ # to try and resolve the overloads with
279
+ # incoming, required = self.resolve_reference_deps(op)
280
+ #
281
+ if not self.dependencies_resolved(op):
282
+ return None
283
+
284
+ relation = get_relation(op)
285
+ if bt.is_placeholder(relation):
286
+ # when replacing a placeholder, we may have multiple matches
287
+ matches = []
288
+ resolved_args = [self.resolve(arg) for arg in op.args]
289
+ for target in self.potential_targets.get(relation, []):
290
+ fields = self.get_fields(target, relation.name)
291
+ if all(type_matches(arg, self.resolve(field))
292
+ for arg, field in zip(resolved_args, fields)):
293
+ matches.append(target)
294
+
295
+ return matches
296
+
297
+ elif relation.overloads:
298
+ # when resolving an overload for a concrete relation, we can only have one
299
+ resolved_args = [self.resolve(arg) for arg in op.args]
300
+ is_function = bt.is_function(relation)
301
+ for overload in relation.overloads:
302
+ # note that for functions we only consider input fields when matching
303
+ if all(type_matches(arg, field_type) or conversion_allowed(arg, field_type)
304
+ for arg, field, field_type in zip(resolved_args, relation.fields, overload.types)
305
+ if field.input or not is_function):
306
+ self.resolved_overload[op.id] = overload
307
+ return overload
308
+ return [] # no matches found
309
+ else:
310
+ # this is a relation with type vars or numbers that need to be specialized
311
+ return [relation]
312
+
313
+
314
+ def dependencies_resolved(self, op:mm.Lookup|mm.Update|mm.Aggregate):
315
+ # if this is a placeholder, we need assume all possible args were resolved
316
+ if bt.is_placeholder(get_relation(op)):
317
+ return True
318
+ # else, find whether all back-edges were resolved
319
+ for node in self.back_edges[op]:
320
+ if isinstance(node, (mm.Var, mm.Field, mm.Literal)):
321
+ node_type = self.resolve(node)
322
+ if bt.is_abstract(node_type):
323
+ return False
324
+ return True
325
+
326
+
327
+
328
+
329
+ #--------------------------------------------------
330
+ # Propagation
331
+ #--------------------------------------------------
332
+
333
+ def propagate(self):
334
+ edges = self.edges
335
+ work_list = []
336
+
337
+ # go through all the roots and find any that are not abstract, they'll
338
+ # be the first nodes to push types through the network
339
+ unhandled = OrderedSet()
340
+ for node in self.roots:
341
+ if not isinstance(node, (mm.Var, mm.Field, mm.Literal)):
342
+ continue
343
+ node_type = self.resolve(node)
344
+ if not bt.is_abstract(node_type):
345
+ work_list.append(node)
346
+ else:
347
+ unhandled.add(node)
348
+
349
+ # push known type nodes through the edges
350
+ while work_list:
351
+ source = work_list.pop(0)
352
+ self.resolved_references.add(source.id)
353
+ if source in unhandled:
354
+ unhandled.remove(source)
355
+ source_type = self.resolve(source)
356
+ # check to see if the source has ended up with a set of types that
357
+ # aren't valid, e.g. a union of primitives
358
+ if invalid_type(source_type):
359
+ self.invalid_type(source, source_type)
360
+
361
+ # propagate our type to each outgoing edge
362
+ for out in edges.get(source, []):
363
+ # if this is an overload then we need to try and resolve it
364
+ if isinstance(out, (mm.Update, mm.Lookup, mm.Aggregate)):
365
+ if not out.id in self.resolved_references:
366
+ found = self.resolve_reference(out)
367
+ if found is not None:
368
+ self.resolved_references.add(out.id)
369
+ start_arg_types = [self.resolve(arg) for arg in out.args]
370
+ self.propagate_reference(out, start_arg_types, found)
371
+ # final_arg_types = [self.resolve(arg) for arg in out.args]
372
+ for arg, start in zip(out.args, start_arg_types):
373
+ # if start != final and
374
+ # this won't cause loops because we resolve refs only once;
375
+ # we can be safer and check?
376
+ if arg not in work_list:
377
+ work_list.append(arg)
378
+ # otherwise, we just add to the outgoing node's type and if it
379
+ # changes we add it to the work list
380
+ elif start := self.resolve(out):
381
+ self.add_resolved_type(out, source_type)
382
+ # if out not in work_list and (start != self.resolve(out) or out in unhandled):
383
+ # if out not in work_list:
384
+ if out not in work_list and (start != self.resolve(out) or not out.id in self.resolved_references):
385
+ work_list.append(out)
386
+
387
+ for source in unhandled:
388
+ self.unresolved_type(source)
389
+
390
+ # now that we've pushed all the types through the network, we need to validate
391
+ # that all type requirements of those nodes are met
392
+ for node, fields in self.type_requirements.items():
393
+ node_type = self.resolve(node)
394
+ for field in fields:
395
+ field_type = self.resolve(field)
396
+ if not type_matches(node_type, field_type) and not conversion_allowed(node_type, field_type):
397
+ # node_base = bt.get_primitive_supertype(node_type)
398
+ # field_base = bt.get_primitive_supertype(field_type)
399
+ # if node_base and field_base and conversion_allowed(node_base, field_base):
400
+ # pass
401
+ # else:
402
+ self.type_mismatch(node, field_type, node_type)
403
+
404
+
405
+ def propagate_reference(self, node:mm.Lookup|mm.Update|mm.Aggregate, resolved_args:list[mm.Type], references:mm.Overload|list[mm.Relation]):
406
+ # TODO: distinguish between overloads and placeholders better
407
+ if not references:
408
+ return self.unresolved_overload(node)
409
+
410
+ # we need to determine the final types of our args by taking all the references
411
+ # and adding the type of their fields back to the args.
412
+ relation = get_relation(node)
413
+
414
+ if bt.is_placeholder(relation):
415
+ assert(references and isinstance(references, list))
416
+ # we've resolved the placeholder, so store that
417
+ self.resolved_placeholder[node.id] = references
418
+
419
+ # we need to determine the final types of our args by taking all the references
420
+ # and adding the type of their fields back to the args.
421
+ for reference in references:
422
+ resolved_fields = [self.resolve(f) for f in self.get_fields(reference, relation.name)]
423
+ for field_type, arg_type, arg in zip(resolved_fields, resolved_args, node.args):
424
+ if bt.is_abstract(arg_type) and isinstance(arg, mm.Var):
425
+ self.add_resolved_type(arg, field_type)
426
+ else:
427
+ if isinstance(references, mm.Overload):
428
+ # we resolved an overload, use its types
429
+ types = list(references.types)
430
+ else:
431
+ # we resolved to a single concrete relation that contains type vars or needs
432
+ # number specialization, so use that relation's field types
433
+ types = list([self.resolve(f) for f in relation.fields])
434
+
435
+ # if our overload preserves types, we check to see if there's a preserved
436
+ # output type given the inputs and if so, shadow the field's type with the
437
+ # preserved type
438
+ resolved_fields = types
439
+ if bt.is_function(relation) and len(set(resolved_fields)) == 1:
440
+ input_types = set([arg_type for field, arg_type
441
+ in zip(relation.fields, resolved_args) if field.input])
442
+ if out_type := self.try_preserve_type(input_types):
443
+ resolved_fields = [field_type if field.input else out_type
444
+ for field, field_type in zip(relation.fields, types)]
445
+
446
+ # TODO - we also need to make sure the type vars are constently resolved here
447
+ # i.e. if types contain typevars, check that the args that are bound to those
448
+ # typevars are consistent
449
+ if b.core.Number in types or (b.core.TypeVar in types and any(bt.is_number(t) for t in resolved_args)):
450
+ # this overload contains generic numbers or typevars bound to numbers, so
451
+ # find which specific type of number to use given the arguments being passed
452
+ number, resolved_fields = self.specialize_number(relation, resolved_fields, resolved_args)
453
+ self.resolved_number[node.id] = number
454
+ # push the specialized number to the outputs
455
+ for field, field_type, arg in zip(relation.fields, resolved_fields, node.args):
456
+ if not field.input and isinstance(arg, mm.Var):
457
+ self.add_resolved_type(arg, field_type)
458
+ else:
459
+ for field_type, arg, arg_type in zip(resolved_fields, node.args, resolved_args):
460
+ if bt.is_abstract(arg_type) and isinstance(arg, mm.Var):
461
+ self.add_resolved_type(arg, field_type)
462
+
463
+
464
+ def try_preserve_type(self, types:set[mm.Type]) -> Optional[mm.Type]:
465
+ # we keep the input type as the output type if either all inputs
466
+ # are the exact same type or there's one nominal and its base primitive
467
+ # type, e.g. USD + Decimal
468
+ if len(types) == 1:
469
+ return next(iter(types))
470
+ if len(types) == 2:
471
+ t1, t2 = types
472
+ t1_base = bt.get_primitive_supertype(t1)
473
+ t2_base = bt.get_primitive_supertype(t2)
474
+ if t1_base is None or t2_base is None:
475
+ base_equivalent = type_matches(t1, t2, is_type_relation=True)
476
+ else:
477
+ base_equivalent = type_matches(t1_base, t2_base)
478
+ if base_equivalent:
479
+ # as long as one of the types is a base primitive, we can use the
480
+ # other type as final preserved type
481
+ if bt.is_primitive(t1):
482
+ return t2
483
+ elif bt.is_primitive(t2):
484
+ return t1
485
+ return None
486
+
487
+ def get_number(self, t: mm.Type) -> mm.NumberType:
488
+ # Get a number type from the given type, if it is Number return the default number
489
+ x = bt.get_number_supertype(t)
490
+ if isinstance(x, mm.NumberType):
491
+ return x
492
+ return b.core.DefaultNumber
493
+
494
+ def specialize_number(self, op, field_types:list[mm.Type], arg_types:list[mm.Type]) -> Tuple[mm.NumberType, list[mm.Type]]:
495
+ """
496
+ Find the number type to use for an overload that has Number in its field_types,
497
+ and which is being referred to with these arg_types.
498
+
499
+ Return a tuple where the first element is the specialized number type, and the second
500
+ element is a new list that contains the same types as field_types but with
501
+ Number replaced by this specialized number.
502
+ """
503
+ if op == b.core.div:
504
+ # see https://docs.snowflake.com/en/sql-reference/operators-arithmetic#division
505
+ numerator, denominator = self.get_number(arg_types[0]), self.get_number(arg_types[1])
506
+ s = max(numerator.scale, min(numerator.scale + 6, 12))
507
+ number = mm.NumberType(name=f"Number(38,{s})", precision=38, scale=s, source=op.source, super_types=(b.core.Numeric,))
508
+ return number, [numerator, denominator, number]
509
+ elif op == b.core.mul:
510
+ # see https://docs.snowflake.com/en/sql-reference/operators-arithmetic#multiplication
511
+ t1, t2 = self.get_number(arg_types[0]), self.get_number(arg_types[1])
512
+ S1 = t1.scale
513
+ S2 = t2.scale
514
+ s = min(S1 + S2, max(S1, S2, 12))
515
+ number = mm.NumberType(name=f"Number(38,{s})", precision=38, scale=s, source=op.source, super_types=(b.core.Numeric,))
516
+ return number, [t1, t2, number]
517
+ elif op == b.aggregates.avg:
518
+ # TODO!! - implement proper avg specialization
519
+ pass
520
+
521
+
522
+ number = None
523
+ for arg_type in arg_types:
524
+ # x = types.decimal_supertype(arg_type)
525
+ x = bt.get_number_supertype(arg_type)
526
+ if isinstance(x, mm.NumberType):
527
+ # the current specialization policy is to select the number with largest
528
+ # scale and, if there multiple with the largest scale, the one with the
529
+ # largest precision. This is safe because when converting a number to the
530
+ # specialized number, we never truncate fractional digits (because we
531
+ # selected the largest scale) and, if the non-fractional digits are too
532
+ # large to fit the specialized number, we will have a runtime overflow,
533
+ # which should alert the user of the problem.
534
+ #
535
+ # In the future we can implement more complex policies. For example,
536
+ # snowflake has well documented behavior for how the output of operations
537
+ # behave in face of different number types, and we may use that:
538
+ # https://docs.snowflake.com/en/sql-reference/operators-arithmetic#scale-and-precision-in-arithmetic-operations
539
+ if number is None or x.scale > number.scale or (x.scale == number.scale and x.precision > number.precision):
540
+ number = x
541
+ assert(isinstance(number, mm.NumberType))
542
+ return number, [number if bt.is_number(field_type) else field_type
543
+ for field_type in field_types]
544
+
545
+
546
+ #--------------------------------------------------
547
+ # Display
548
+ #--------------------------------------------------
549
+
550
+ # draw the network as a mermaid graph for the debugger
551
+ def to_mermaid(self, max_edges=500) -> str:
552
+
553
+ # add links for edges while collecting nodes
554
+ nodes = OrderedSet()
555
+ link_strs = []
556
+ # edges
557
+ for src, dsts in self.edges.items():
558
+ nodes.add(src)
559
+ for dst in dsts:
560
+ if len(link_strs) > max_edges:
561
+ break
562
+ nodes.add(dst)
563
+ link_strs.append(f"n{src.id} --> n{dst.id}")
564
+ if len(link_strs) > max_edges:
565
+ break
566
+ # type requirements
567
+ for src, dsts in self.type_requirements.items():
568
+ nodes.add(src)
569
+ for dst in dsts:
570
+ if len(link_strs) > max_edges:
571
+ break
572
+ nodes.add(dst)
573
+ link_strs.append(f"n{src.id} -.-> n{dst.id}")
574
+ if len(link_strs) > max_edges:
575
+ break
576
+
577
+ def type_span(t:mm.Type) -> str:
578
+ type_str = t.name if isinstance(t, mm.ScalarType) else str(t)
579
+ return f"<span style='color:cyan;'>{type_str.strip()}</span>"
580
+
581
+ def reference_span(rel:mm.Relation, arg_types:list[mm.Type], root:str) -> str:
582
+ args = []
583
+ for field, arg_type in zip(rel.fields, arg_types):
584
+ field_type = self.resolve(field)
585
+ if not type_matches(arg_type, field_type) and not conversion_allowed(arg_type, field_type) and not bt.is_abstract(field_type):
586
+ args.append(f"<span style='color:yellow;'>{str(arg_type).strip()} -> {str(field_type).strip()}</span>")
587
+ elif isinstance(arg_type, mm.UnionType):
588
+ args.append(type_span(field_type))
589
+ else:
590
+ args.append(type_span(arg_type))
591
+ return f'{rel.name}{root}({", ".join(args)})'
592
+
593
+ resolved = self.resolved_types
594
+ node_strs = []
595
+ for node in nodes:
596
+ klass = ""
597
+ root = "(*)" if node in self.roots else ""
598
+ if isinstance(node, mm.Var):
599
+ ir_type = resolved.get(node) or self.resolve(node)
600
+ type_str = type_span(ir_type)
601
+ label = f'(["{node.name}{root}:{type_str}"])'
602
+ elif isinstance(node, mm.Literal):
603
+ ir_type = resolved.get(node) or self.resolve(node)
604
+ type_str = type_span(ir_type)
605
+ klass = ":::literal"
606
+ label = f'[/"{node.value}{root}: {type_str}"\\]'
607
+ elif isinstance(node, mm.Field):
608
+ ir_type = resolved.get(node) or self.resolve(node)
609
+ type_str = type_span(ir_type)
610
+ klass = ":::field"
611
+ rel = node._relation
612
+ if rel is not None:
613
+ rel = str(node._relation)
614
+ label = f'{{{{"{node.name}{root}:{type_str}\nfrom {rel}"}}}}'
615
+ else:
616
+ label = f'{{{{"{node.name}{root}:\n{type_str}"}}}}'
617
+ elif isinstance(node, (mm.Lookup, mm.Update, mm.Aggregate)):
618
+ arg_types = [self.resolve(arg) for arg in node.args]
619
+ if node.id in self.resolved_placeholder:
620
+ overloads = self.resolved_placeholder[node.id]
621
+ content = "<br/>".join([reference_span(o, arg_types, root) for o in overloads])
622
+ else:
623
+ content = reference_span(get_relation(node), arg_types, root)
624
+ label = f'[/"{content}"/]'
625
+ # elif isinstance(node, mm.Relation):
626
+ # label = f'[("{node}")]'
627
+ else:
628
+ raise NotImplementedError(f"Unknown node type: {type(node)}")
629
+ if self.has_errors(node):
630
+ klass = ":::error"
631
+ node_strs.append(f'n{node.id}{label}{klass}')
632
+
633
+ node_str = "\n ".join(node_strs)
634
+ link_str = "\n ".join(link_strs)
635
+ template = f"""
636
+ %%{{init: {{'theme':'dark', 'flowchart':{{'useMaxWidth':false, 'htmlLabels': true}}}}}}%%
637
+ flowchart TD
638
+ linkStyle default stroke:#666
639
+ classDef field fill:#245,stroke:#478
640
+ classDef literal fill:#452,stroke:#784
641
+ classDef error fill:#624,stroke:#945,color:#f9a
642
+ classDef default stroke:#444,stroke-width:2px, font-size:12px
643
+
644
+ %% nodes
645
+ {node_str}
646
+
647
+ %% edges
648
+ {link_str}
649
+ """
650
+ return template
651
+
652
+ # simplified, less verbose (compared to mermaid) output for snapshot testing
653
+ def to_fish(self) -> str:
654
+ resolved = self.resolved_types
655
+ nodes = OrderedSet()
656
+ for src, dsts in self.edges.items():
657
+ nodes.add(src)
658
+ nodes.update(dsts)
659
+ for src, dsts in self.type_requirements.items():
660
+ nodes.add(src)
661
+ nodes.update(dsts)
662
+
663
+ def type_to_str(t:mm.Type) -> str:
664
+ type_str = t.name if isinstance(t, mm.ScalarType) else str(t)
665
+ return type_str.strip()
666
+
667
+ def overload_str(rel:mm.Relation, arg_types:list[mm.Type]) -> str:
668
+ args = []
669
+ for field, arg_type in zip(rel.fields, arg_types):
670
+ field_type = self.resolve(field)
671
+ if not type_matches(arg_type, field_type):
672
+ args.append(f"{str(arg_type).strip()} -?> {str(field_type).strip()}")
673
+ elif isinstance(arg_type, mm.UnionType):
674
+ args.append(type_to_str(field_type))
675
+ else:
676
+ args.append(type_to_str(arg_type))
677
+ return f'{rel.name}({", ".join(args)})'
678
+
679
+ def node_kind(node:mm.Node) -> str:
680
+ if isinstance(node, (mm.Lookup, mm.Update, mm.Aggregate)):
681
+ return "overload"
682
+ return type(node).__name__.lower()
683
+
684
+ def error_info(err:TyperError) -> str:
685
+ if isinstance(err, TypeMismatch):
686
+ return f'Type Mismatch|expected {get_name(err.expected)}, got {get_name(err.actual)}'
687
+ return f'{type(err).__name__}'
688
+
689
+ nl = "\n"
690
+ node_strs = []
691
+ for node in nodes:
692
+ info = ""
693
+ if isinstance(node, mm.Var):
694
+ ir_type = resolved.get(node) or self.resolve(node)
695
+ if not(isinstance(ir_type, mm.ScalarType) and node.name == ir_type.name):
696
+ info = f'{node.name}|{type_to_str(ir_type)}'
697
+ elif isinstance(node, mm.Field):
698
+ ir_type = resolved.get(node) or self.resolve(node)
699
+ info = f'{node.name}|{type_to_str(ir_type)}'
700
+ elif isinstance(node, (mm.Lookup, mm.Update, mm.Aggregate)):
701
+ arg_types = [self.resolve(arg) for arg in node.args]
702
+ if node.id in self.resolved_placeholder:
703
+ overloads = self.resolved_placeholder[node.id]
704
+ content = nl.join([overload_str(o, arg_types) for o in overloads])
705
+ else:
706
+ content = overload_str(get_relation(node), arg_types)
707
+ info = f'{content}'
708
+ else:
709
+ raise NotImplementedError(f"Unknown node type: {type(node)}")
710
+
711
+ if info:
712
+ error_suffix = " !" if self.has_errors(node) else ""
713
+ node_strs.append(f'{node_kind(node)}|{info}{error_suffix}')
714
+
715
+ node_strs.sort()
716
+ if self.errors:
717
+ node_strs.append("---ERRORS---")
718
+ for err in self.errors:
719
+ node_strs.append(f'{error_info(err)} ({str(err.node).strip()})')
720
+
721
+ return f"""{nl.join(node_strs)}"""
722
+
723
+
724
+
725
+
726
+ #--------------------------------------------------
727
+ # Analyzer
728
+ #--------------------------------------------------
729
+
730
+ class Analyzer(Walker):
731
+ """ Walks the metamodel and builds the propagation network. """
732
+
733
+ def __init__(self, net:PropagationNetwork):
734
+ super().__init__()
735
+ self.net = net
736
+
737
+ def analyze(self, node: mm.Node):
738
+ self(node)
739
+
740
+ # TODO - ignoring requires for now because the typing of constraints seems incorrect
741
+ def enter_require(self, require: mm.Require):
742
+ return NO_WALK
743
+
744
+ def compute_potential_targets(self, relation: mm.Relation):
745
+ # register potential targets for placeholders
746
+ if bt.is_placeholder(relation):
747
+ self.net.potential_targets[relation] = get_potential_targets(self.net.model, relation)
748
+
749
+ #--------------------------------------------------
750
+ # Walk Update
751
+ #--------------------------------------------------
752
+
753
+ def update(self, node: mm.Update):
754
+ rel = node.relation
755
+ self.compute_potential_targets(rel)
756
+
757
+ # if this is a type relation, the update is asserting that the argument is of that
758
+ # type; so, it's fine to pass a super-type in to the population e.g. Employee(Person)
759
+ # should be a valid way to populate that a particular Person is also an Employee.
760
+ is_type_relation = isinstance(rel, mm.TypeNode)
761
+ for arg, field in zip(node.args, rel.fields):
762
+ field_type = field.type
763
+ arg_type = self.net.resolve(arg)
764
+
765
+ # if the arg is abstract, but the field isn't, then we need to make sure that
766
+ # once the arg is resolved we check that it matches the field type
767
+ if isinstance(arg, mm.Var) and bt.is_abstract(arg_type) and bt.is_concrete(field_type):
768
+ self.net.add_type_requirement(arg, field)
769
+
770
+ if bt.is_abstract(field_type) and isinstance(arg, (mm.Var, mm.Literal)):
771
+ # if the field is abstract, then eventually this arg will help determine
772
+ # the field's type, so add an edge from the arg to the field
773
+ self.net.add_edge(arg, field)
774
+ elif not type_matches(arg_type, field_type, is_type_relation=is_type_relation):
775
+ if not conversion_allowed(arg_type, field_type):
776
+ self.net.type_mismatch(node, field_type, arg_type)
777
+
778
+ #--------------------------------------------------
779
+ # Walk Lookups + Aggregates
780
+ #--------------------------------------------------
781
+
782
+ def lookup(self, node: mm.Lookup):
783
+ self.compute_potential_targets(node.relation)
784
+ self.visit_rel_op(node)
785
+
786
+ def aggregate(self, node: mm.Aggregate):
787
+ self.visit_rel_op(node)
788
+
789
+ def visit_rel_op(self, node: mm.Lookup|mm.Aggregate):
790
+ rel = get_relation(node)
791
+
792
+ # special case eq lookups
793
+ if isinstance(node, mm.Lookup) and rel == b.core.eq:
794
+ if self.visit_eq2(node):
795
+ return
796
+ # return self.visit_eq(node)
797
+
798
+ # special case when the relation needs to be resolved as there are overloads, placeholders,
799
+ # type vars or it needs number specialization
800
+ if self.requires_resolution(rel):
801
+ return self.visit_unresolved_reference(node)
802
+
803
+ # if this is a population check, then it's fine to pass a subtype in to do the check
804
+ # e.g. Employee(Person) is a valid way to check if a person is an employee
805
+ is_population_lookup = isinstance(rel, mm.TypeNode)
806
+ for arg, field in zip(node.args, rel.fields):
807
+ field_type = self.net.resolve(field)
808
+ arg_type = self.net.resolve(arg)
809
+ if not type_matches(arg_type, field_type, is_population_lookup):
810
+ # Do not complain if we can convert the arg to the field type.
811
+ if not conversion_allowed(arg_type, field_type):
812
+ # if the arg is a var and it matches when allowing for super types of
813
+ # the expected we can expect to refine it later; but we add a type
814
+ # requirement to check at the end
815
+ if isinstance(arg, mm.Var) and type_matches(arg_type, field_type, True):
816
+ self.net.add_type_requirement(arg, field)
817
+ else:
818
+ self.net.type_mismatch(node, field_type, arg_type)
819
+ # if we have an abstract var then this field will ultimately propagate to that
820
+ # var's type; also, if this is a population lookup, the type of the population
821
+ # being looked up will flow back to the var
822
+ if isinstance(arg, mm.Var):
823
+ if not field.input:
824
+ self.net.add_edge(field, arg)
825
+ else:
826
+ self.net.add_type_requirement(arg, field)
827
+ # if isinstance(arg, mm.Var) and (bt.is_abstract(arg_type) or is_population_lookup) and field_type != arg_type:
828
+ # self.net.add_edge(field, arg)
829
+ # elif isinstance(arg, mm.Var) and bt.is_abstract(arg_type) and bt.is_abstract(field_type):
830
+ # self.net.add_type_requirement(arg, field)
831
+
832
+
833
+ def requires_resolution(self, rel: mm.Relation) -> bool:
834
+ # has overloads or is a placeholder relation that needs replacement
835
+ if rel.overloads or bt.is_placeholder(rel):
836
+ return True
837
+ # there are type vars or numbers in the fields that need specialization
838
+ for field in rel.fields:
839
+ t = self.net.resolve(field)
840
+ if bt.is_type_var(t) or t == b.core.Number:
841
+ return True
842
+ return False
843
+
844
+ #--------------------------------------------------
845
+ # Reference resolution
846
+ #--------------------------------------------------
847
+
848
+ def visit_unresolved_reference(self, node: mm.Lookup|mm.Update|mm.Aggregate):
849
+ relation = get_relation(node)
850
+ # functions have their outputs determined by their inputs
851
+ is_function = bt.is_function(relation)
852
+ is_placeholder = bt.is_placeholder(relation)
853
+ # add edges between args and the relation based on input/output
854
+ for field, arg in zip(relation.fields, node.args):
855
+ if isinstance(arg, (mm.Var, mm.Literal)):
856
+ if field.input:
857
+ # the arg type will flow into the input
858
+ self.net.add_edge(arg, node)
859
+ # if relation == b.core.eq and bt.is_abstract(self.net.resolve(arg)):
860
+ # # for eq, the input also flows back to the arg
861
+ # self.net.add_edge(field, arg)
862
+ else:
863
+ if is_function:
864
+ # this is an output of a function, so the field type will flow to the arg
865
+ self.net.add_edge(node, arg)
866
+ else:
867
+ if is_placeholder:
868
+ self.net.add_edge(arg, node)
869
+ # the output of a non-function, arg and field flow to each other
870
+ # self.net.add_edge(arg, node)
871
+ # if not placeholder and
872
+ # if placeholder:
873
+ # self.net.add_edge(arg, node)
874
+ # el
875
+ if bt.is_abstract(field.type) and not is_placeholder:
876
+ self.net.add_edge(field, node)
877
+
878
+ if bt.is_abstract(self.net.resolve(arg)):
879
+ self.net.add_edge(node, arg)
880
+ # elif placeholder:
881
+ # # force the resolution of the placeholder by adding an edge
882
+ # self.net.add_edge(arg, node)
883
+
884
+
885
+ # for placeholders, we need to add edges from the target fields back to the node
886
+ # if relation in self.net.potential_targets:
887
+ # for target in self.net.potential_targets[relation]:
888
+ # for arg, field in zip(node.args, target.fields):
889
+ # if isinstance(arg, mm.Var) and bt.is_abstract(self.net.resolve(arg)):
890
+ # self.net.add_edge(field, node)
891
+ # for field in target.fields:
892
+ # if bt.is_abstract(self.net.resolve(field)):
893
+ # self.net.add_edge(field, node)
894
+
895
+
896
+
897
+ #--------------------------------------------------
898
+ # Eq
899
+ #--------------------------------------------------
900
+
901
+ def visit_eq2(self, node: mm.Lookup):
902
+ (left, right) = node.args
903
+ left_type = self.net.resolve(left)
904
+ right_type = self.net.resolve(right)
905
+ if bt.is_abstract(left_type) and bt.is_abstract(right_type):
906
+ assert isinstance(left, mm.Var) and isinstance(right, mm.Var)
907
+ # if both sides are abstract, then whatever we find out about
908
+ # either should propagate to the other
909
+ self.net.add_edge(left, right)
910
+ self.net.add_edge(right, left)
911
+ return True
912
+ else:
913
+ return False
914
+ def visit_eq(self, node: mm.Lookup):
915
+ (left, right) = node.args
916
+ left_type = self.net.resolve(left)
917
+ right_type = self.net.resolve(right)
918
+ if bt.is_abstract(left_type) and bt.is_abstract(right_type):
919
+ assert isinstance(left, mm.Var) and isinstance(right, mm.Var)
920
+ # if both sides are abstract, then whatever we find out about
921
+ # either should propagate to the other
922
+ self.net.add_edge(left, right)
923
+ self.net.add_edge(right, left)
924
+ elif bt.is_abstract(left_type):
925
+ assert isinstance(left, mm.Var)
926
+ if isinstance(right, (mm.Var, mm.Literal)):
927
+ self.net.add_edge(right, left)
928
+ elif bt.is_abstract(right_type):
929
+ assert isinstance(right, mm.Var)
930
+ if isinstance(left, (mm.Var, mm.Literal)):
931
+ self.net.add_edge(left, right)
932
+ elif not type_matches(left_type, right_type):
933
+ if not conversion_allowed(left_type, right_type) and not conversion_allowed(right_type, left_type):
934
+ self.net.type_mismatch(node, left_type, right_type)
935
+
936
+
937
+
938
+
939
+ #--------------------------------------------------
940
+ # Replacer
941
+ #--------------------------------------------------
942
+
943
+ # Once we've pushed all the types through the network, we need to replace the types of
944
+ # fields and vars that we may have discovered. We also need to replace placeholder lookups
945
+ # with the chosen relations and do any conversions that are needed.
946
+ class Replacer(Rewriter):
947
+ def __init__(self, net:PropagationNetwork):
948
+ super().__init__()
949
+ self.net = net
950
+
951
+ def rewrite(self, model: T) -> T:
952
+ return self(model) # type: ignore
953
+
954
+ def logical(self, logical: mm.Logical):
955
+ if len(logical.body) == 0:
956
+ return logical
957
+ # inline logicals that are just there to group other nodes during rewrite
958
+ body = []
959
+ for child in logical.body:
960
+ if isinstance(child, mm.Logical) and not child.optional and not child.scope:
961
+ body.extend(child.body)
962
+ else:
963
+ body.append(child)
964
+ return logical.mut(body = tuple(body))
965
+
966
+ #--------------------------------------------------
967
+ # Rewriter handlers
968
+ #--------------------------------------------------
969
+
970
+ def field(self, node: mm.Field):
971
+ # TODO - this is only modifying the relation in the model, but then we have a new
972
+ # relation there, which is different than the object referenced by tasks.
973
+ if node in self.net.resolved_types:
974
+ return mm.Field(node.name, self.net.resolved_types[node], node.input)
975
+ return node
976
+
977
+ def var(self, node: mm.Var):
978
+ if node in self.net.resolved_types:
979
+ return mm.Var(self.net.resolved_types[node], node.name)
980
+ return node
981
+
982
+ def literal(self, node: mm.Literal):
983
+ if node in self.net.resolved_types:
984
+ return mm.Literal(self.net.resolved_types[node], node.value)
985
+ return node
986
+
987
+ def update(self, node: mm.Update):
988
+ return self.convert_arguments(node, node.relation)
989
+
990
+ def lookup(self, node: mm.Lookup):
991
+ # We need to handle eq specially because its arguments can be converted symmetrically
992
+ if node.relation == b.core.eq:
993
+ return self.visit_eq_lookup(node)
994
+
995
+ args = types = None
996
+ if node.id in self.net.resolved_placeholder:
997
+ resolved_relations = self.net.resolved_placeholder[node.id]
998
+ args = self.net.get_args(node, resolved_relations[0])
999
+ types = [f.type for f in resolved_relations[0].fields]
1000
+ elif node.id in self.net.resolved_overload:
1001
+ resolved_relations = [node.relation]
1002
+ types = self.net.resolved_overload[node.id].types
1003
+ else:
1004
+ resolved_relations = [node.relation]
1005
+
1006
+ if len(resolved_relations) == 1:
1007
+ x = self.convert_arguments(node, resolved_relations[0], args, types)
1008
+ if isinstance(x, mm.Logical) and len(x.body) == 1:
1009
+ return x.body[0]
1010
+ else:
1011
+ return x
1012
+
1013
+ branches:list = []
1014
+ for target in resolved_relations:
1015
+ args = self.net.get_args(node, target)
1016
+ types = [f.type for f in self.net.get_fields(resolved_relations[0], node.relation.name)]
1017
+ # adding this logical to avoid issues in the old backend
1018
+ branches.append(mm.Logical((self.convert_arguments(node, target, args, types=types),)))
1019
+ return mm.Union(tuple(branches))
1020
+
1021
+ def convert_arguments(self, node: mm.Lookup|mm.Update, relation: mm.Relation, args: Iterable[mm.Value]|None=None, types: Iterable[mm.Type]|None=None) -> mm.Logical|mm.Lookup|mm.Update:
1022
+ args = args or node.args
1023
+ types = types or [self.net.resolve(f) for f in relation.fields]
1024
+ number_type = self.net.resolved_number.get(node.id)
1025
+ is_function = bt.is_function(relation)
1026
+ tasks = []
1027
+ final_args = []
1028
+ for arg, field, field_type in zip(args, relation.fields, types):
1029
+ if isinstance(arg, (mm.Var, mm.Literal)) and (not is_function or field.input):
1030
+ arg_type = to_type(arg)
1031
+ # field_type = to_type(field)
1032
+ if number_type and bt.is_number(arg_type) and not arg_type == b.core.ScaledNumber:
1033
+ field_type = number_type
1034
+ # the typer previously made sure that this should be valid so a type mismatch
1035
+ # means we need to convert
1036
+ if not type_matches(arg_type, field_type): # and conversion_allowed(arg_type, field_type):
1037
+ final_args.append(convert(arg, field_type, tasks))
1038
+ else:
1039
+ final_args.append(arg)
1040
+ else:
1041
+ final_args.append(arg)
1042
+ if isinstance(node, mm.Lookup):
1043
+ tasks.append(node.mut(relation = relation, args = tuple(final_args)))
1044
+ else:
1045
+ tasks.append(node.mut(relation = relation, args = tuple(final_args)))
1046
+ if len(tasks) == 1:
1047
+ return tasks[0]
1048
+ return mm.Logical(tuple(tasks))
1049
+
1050
+
1051
+ def visit_eq_lookup(self, node: mm.Lookup):
1052
+ (left, right) = node.args
1053
+ left_type = to_type(left)
1054
+ right_type = to_type(right)
1055
+
1056
+ if type_matches(left_type, right_type):
1057
+ return node
1058
+
1059
+ assert isinstance(left, (mm.Var, mm.Literal)) and isinstance(right, (mm.Var, mm.Literal))
1060
+ final_args = []
1061
+ tasks = []
1062
+ if conversion_allowed(left_type, right_type):
1063
+ final_args = [convert(left, right_type, tasks), right]
1064
+ elif conversion_allowed(right_type, left_type):
1065
+ final_args = [left, convert(right, left_type, tasks)]
1066
+ else:
1067
+ self.net.type_mismatch(node, left_type, right_type)
1068
+ return node
1069
+
1070
+ tasks.append(mm.Lookup(b.core.eq, tuple(final_args)))
1071
+ return mm.Logical(tuple(tasks))
1072
+
1073
+ #--------------------------------------------------
1074
+ # Helpers
1075
+ #--------------------------------------------------
1076
+
1077
+ def get_relation(node: mm.Lookup|mm.Update|mm.Aggregate) -> mm.Relation:
1078
+ if isinstance(node, mm.Aggregate):
1079
+ return node.aggregation
1080
+ return node.relation
1081
+
1082
+ def get_name(type: mm.Type) -> str:
1083
+ if isinstance(type, mm.ScalarType):
1084
+ return type.name
1085
+ elif isinstance(type, mm.UnionType):
1086
+ return '|'.join([get_name(t) for t in type.types])
1087
+ elif isinstance(type, mm.ListType):
1088
+ return f'List[{get_name(type.element_type)}]'
1089
+ elif isinstance(type, mm.TupleType):
1090
+ return f'Tuple[{", ".join([get_name(t) for t in type.element_types])}]'
1091
+ else:
1092
+ raise TypeError(f"Unknown type: {type}")
1093
+
1094
+ #--------------------------------------------------
1095
+ # Type and Relation helpers
1096
+ #--------------------------------------------------
1097
+
1098
+
1099
+ def is_potential_target(placeholder: mm.Relation, target: mm.Relation) -> bool:
1100
+ """ Whether this target is matches the placeholder signature and, thus, can be a potential target. """
1101
+ if placeholder != target and len(placeholder.fields) == len(target.fields) and not bt.is_placeholder(target):
1102
+ return placeholder.name == target.name or any(placeholder.name == reading.name for reading in target.readings)
1103
+ return False
1104
+
1105
+ def get_potential_targets(model: mm.Model, placeholder: mm.Relation) -> list[mm.Relation]:
1106
+ """ Get all potential target relations in the model that match the placeholder signature. """
1107
+ return list(filter(lambda r: is_potential_target(placeholder, r), model.relations))
1108
+
1109
+ def to_type(value: mm.Value|mm.Field|mm.Literal) -> mm.Type:
1110
+ if isinstance(value, (mm.Var, mm.Field, mm.Literal)):
1111
+ return value.type
1112
+
1113
+ if isinstance(value, mm.Type):
1114
+ return b.core.Type
1115
+
1116
+ if isinstance(value, tuple):
1117
+ return mm.TupleType(element_types=tuple(to_type(v) for v in value))
1118
+
1119
+ raise TypeError(f"Cannot determine IR type for value: {value} of type {type(value).__name__}")
1120
+
1121
+
1122
+ def convert(value: mm.Var|mm.Literal, to_type: mm.Type, tasks: list[mm.Task]) -> mm.Value:
1123
+ # if the arg is a literal, we can just change its type
1124
+ # TODO - we may want to check that the value is actually convertible
1125
+ if isinstance(value, mm.Literal):
1126
+ return mm.Literal(to_type, value.value)
1127
+
1128
+ # otherise we need to add a cast
1129
+ name = sanitize(value.name + "_" + get_name(to_type))
1130
+ to_type_base = bt.get_primitive_supertype(to_type) or to_type
1131
+ new_value = mm.Var(to_type_base, name)
1132
+ tasks.append(mm.Lookup(b.core.cast, (to_type_base, value, new_value)))
1133
+ return new_value
1134
+
1135
+
1136
+ def conversion_allowed(from_type: mm.Type, to_type: mm.Type) -> bool:
1137
+ # value type conversion is allowed
1138
+ x = bt.get_primitive_supertype(from_type)
1139
+ y = bt.get_primitive_supertype(to_type)
1140
+ if x and y and (x != from_type or y != to_type) and conversion_allowed(x, y):
1141
+ return True
1142
+
1143
+ # numbers can be converted to floats
1144
+ if bt.is_numeric(from_type) and to_type == b.core.Float:
1145
+ return True
1146
+
1147
+ # a number can be converted to another number of larger scale
1148
+ if isinstance(from_type, mm.NumberType) and isinstance(to_type, mm.NumberType):
1149
+ if to_type.scale > from_type.scale:
1150
+ return True
1151
+
1152
+ if from_type == b.core.Number and isinstance(to_type, mm.NumberType):
1153
+ return True
1154
+
1155
+ return False
1156
+
1157
+ def type_matches(actual: mm.Type, expected: mm.Type, is_type_relation=False) -> bool:
1158
+ """
1159
+ True iff we can use a value of the actual type when expecting the expected type, without
1160
+ conversions.
1161
+
1162
+ Any super-type of `actual` can match `expected`. For example if we expect a `Person`, we
1163
+ can use an `Employee` if `Employee < Person`.
1164
+
1165
+ In general, the other way around is not true: if we expect an `Employee` we cannot use a
1166
+ `Person` instead.
1167
+
1168
+ However, when the relation is a Type (previsously known as "population relations"), it
1169
+ is valid to provide sub-types of the expected type. For example, `Employee(Person)` is
1170
+ a valid way to check that `Person` is an `Employee` on a `Lookup`, or to assert that a
1171
+ particular `Person` is an `Employee` on an `Update`.
1172
+ """
1173
+ # exact match
1174
+ if actual == expected:
1175
+ return True
1176
+
1177
+ # any matches anything
1178
+ if actual == b.core.Any or expected == b.core.Any:
1179
+ return True
1180
+
1181
+ # type vars match anything
1182
+ if expected == b.core.TypeVar:
1183
+ return True
1184
+
1185
+ # TODO - remove this once we make them singletons per precision/scale
1186
+ if isinstance(actual, mm.NumberType) and isinstance(expected, mm.NumberType):
1187
+ if actual.precision == expected.precision and actual.scale == expected.scale:
1188
+ return True
1189
+
1190
+ # if an entity type var or any entity is expected, it matches any actual entity type
1191
+ if (expected == b.core.EntityTypeVar or bt.extends(expected, b.core.AnyEntity)) and not bt.is_primitive(actual):
1192
+ return True
1193
+
1194
+ # the abstract Number type and the number type variable match any number type
1195
+ if (expected == b.core.Number) and bt.is_number(actual):
1196
+ return True
1197
+
1198
+ if (expected == b.core.Numeric) and bt.is_numeric(actual):
1199
+ return True
1200
+
1201
+ # if actual is scalar, any of its parents may match the expected type
1202
+ if isinstance(actual, mm.ScalarType) and any([type_matches(parent, expected) for parent in actual.super_types]):
1203
+ return True
1204
+
1205
+ # if expected is a value type or this is a check for a type relation, any of the expected type's parents may match the actual type
1206
+ if (is_type_relation or bt.is_value_type(expected)) and isinstance(expected, mm.ScalarType) and any([type_matches(actual, parent, is_type_relation) for parent in expected.super_types]):
1207
+ return True
1208
+
1209
+ # if we expect a union, the actual can match any of its types
1210
+ if isinstance(expected, mm.UnionType):
1211
+ for t in expected.types:
1212
+ if type_matches(t, actual, is_type_relation):
1213
+ return True
1214
+
1215
+ # if actual is a union, every one of its types must match the expected type
1216
+ # if isinstance(actual, mm.UnionType):
1217
+ # for t in actual.types:
1218
+ # if not type_matches(t, expected, is_type_relation):
1219
+ # return False
1220
+ # return True
1221
+ # TODO - we have to distinguish between when we are checking that a specific arg matches
1222
+ # a relation vs when we are selecting relations for the placeholders; then we have to
1223
+ # decide between the above and this.
1224
+ if isinstance(actual, mm.UnionType):
1225
+ for t in actual.types:
1226
+ if type_matches(t, expected, is_type_relation):
1227
+ return True
1228
+
1229
+ # a list type matches if their element types match
1230
+ if isinstance(actual, (mm.ListType, mm.TupleType)) and isinstance(expected, mm.ListType):
1231
+ if isinstance(actual, mm.TupleType):
1232
+ for et in actual.element_types:
1233
+ if not type_matches(et, expected.element_type):
1234
+ return False
1235
+ return True
1236
+ return type_matches(actual.element_type, expected.element_type)
1237
+
1238
+ # a tuple types match if any of all their types match
1239
+ if isinstance(actual, mm.TupleType) and isinstance(expected, mm.TupleType):
1240
+ return all([type_matches(ae, ee) for ae, ee in zip(actual.element_types, expected.element_types)])
1241
+
1242
+ # accept tuples with a single element type to match a list with that type
1243
+ if isinstance(actual, mm.TupleType) and isinstance(expected, mm.ListType):
1244
+ if len(set(actual.element_types)) == 1:
1245
+ return type_matches(actual.element_types[0], expected.element_type)
1246
+
1247
+ # otherwise no match
1248
+ return False
1249
+
1250
+
1251
+ def merge_types(type1: mm.Type, type2: mm.Type) -> mm.Type:
1252
+ if type1 == type2:
1253
+ return type1
1254
+ types_to_process = [type1, type2]
1255
+
1256
+ # if one of them is the abstract Number type, pick the other
1257
+ if type1 == b.core.Number and isinstance(type2, mm.NumberType):
1258
+ return type2
1259
+ if type2 == b.core.Number and isinstance(type1, mm.NumberType):
1260
+ return type1
1261
+
1262
+ # if both are number types, pick the one with larger scale/precision
1263
+ if isinstance(type1, mm.NumberType) and isinstance(type2, mm.NumberType):
1264
+ if type1.scale > type2.scale or (type1.scale == type2.scale and type1.precision > type2.precision):
1265
+ return type1
1266
+ else:
1267
+ return type2
1268
+
1269
+ # if we are overriding a number with a float, pick float
1270
+ if isinstance(type1, mm.NumberType) and type2 == b.core.Float:
1271
+ return type2
1272
+
1273
+ # if one extends the other, pick the most specific one
1274
+ if bt.extends(type1, type2):
1275
+ return type1
1276
+ if bt.extends(type2, type1):
1277
+ return type2
1278
+
1279
+ # give precedence to nominal types (e.g. merging USD(decimal) with decimal gives USD(decimal))
1280
+ base_primitive_type1 = bt.get_primitive_supertype(type1)
1281
+ base_primitive_type2 = bt.get_primitive_supertype(type2)
1282
+ if base_primitive_type1 == base_primitive_type2:
1283
+ if bt.is_primitive(type1):
1284
+ return type2
1285
+ elif bt.is_primitive(type2):
1286
+ return type1
1287
+
1288
+ combined = OrderedSet()
1289
+ # Iterative flattening of union types
1290
+ while types_to_process:
1291
+ t = types_to_process.pop()
1292
+ if isinstance(t, mm.UnionType):
1293
+ types_to_process.extend(t.types)
1294
+ else:
1295
+ combined.add(t)
1296
+
1297
+ # If we have multiple types and Any or AnyEntity is one of them, remove Any
1298
+ if len(combined) > 1:
1299
+ if b.core.Any in combined:
1300
+ combined.remove(b.core.Any)
1301
+ if b.core.AnyEntity in combined:
1302
+ combined.remove(b.core.AnyEntity)
1303
+
1304
+ # If we still have multiple types, make sure supertypes are removed to keep only the
1305
+ # most specific types
1306
+ if len(combined) > 1:
1307
+ to_remove = set()
1308
+ for t1 in combined:
1309
+ for t2 in combined:
1310
+ if t1 != t2 and bt.extends(t1, t2):
1311
+ to_remove.add(t2)
1312
+ for r in to_remove:
1313
+ combined.remove(r)
1314
+
1315
+ # Return single type or create a union
1316
+ return next(iter(combined)) if len(combined) == 1 else mm.UnionType(types=tuple(combined))
1317
+
1318
+ def invalid_type(type:mm.Type) -> bool:
1319
+ if isinstance(type, mm.UnionType):
1320
+ # if there are multiple primitives, or a primitive and a non-primitive
1321
+ # then we have an invalid type
1322
+ if len(type.types) > 1:
1323
+ return any([bt.is_primitive(t) for t in type.types])
1324
+ return False
1325
+
1326
+
1327
+ #--------------------------------------------------
1328
+ # Type Errors
1329
+ #--------------------------------------------------
1330
+
1331
+ @dataclass
1332
+ class TyperError():
1333
+ node: mm.Node
1334
+
1335
+ def report(self):
1336
+ err(self.name(), self.message(), self.parts())
1337
+
1338
+ def name(self) -> str:
1339
+ return type(self).__name__
1340
+
1341
+ def message(self) -> str:
1342
+ raise NotImplementedError()
1343
+
1344
+ def parts(self) -> list[Part]:
1345
+ if self.node.source is None:
1346
+ return [str(self.node)]
1347
+ return [str(self.node), Source(self.node.source)]
1348
+
1349
+ @dataclass
1350
+ class TypeMismatch(TyperError):
1351
+ expected: mm.Type
1352
+ actual: mm.Type
1353
+
1354
+ def message(self) -> str:
1355
+ return f"Expected {get_name(self.expected)}, got {get_name(self.actual)}"
1356
+
1357
+ @dataclass
1358
+ class InvalidType(TyperError):
1359
+ type: mm.Type
1360
+
1361
+ def message(self) -> str:
1362
+ return f"Incompatible types infered: {get_name(self.type)}"
1363
+
1364
+ @dataclass
1365
+ class UnresolvedOverload(TyperError):
1366
+ arg_types: list[mm.Type]
1367
+
1368
+ def message(self) -> str:
1369
+ assert isinstance(self.node, (mm.Lookup, mm.Update, mm.Aggregate))
1370
+ rel = get_relation(self.node)
1371
+ types = ', '.join([get_name(t) for t in self.arg_types])
1372
+ return f"Unresolved overload: {rel.name}({types})"
1373
+
1374
+ @dataclass
1375
+ class UnresolvedType(TyperError):
1376
+
1377
+ def message(self) -> str:
1378
+ return "Unable to determine concrete type."