vtlengine 1.4.0rc2__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 (66) hide show
  1. vtlengine/API/_InternalApi.py +791 -0
  2. vtlengine/API/__init__.py +612 -0
  3. vtlengine/API/data/schema/external_routines_schema.json +34 -0
  4. vtlengine/API/data/schema/json_schema_2.1.json +116 -0
  5. vtlengine/API/data/schema/value_domain_schema.json +97 -0
  6. vtlengine/AST/ASTComment.py +57 -0
  7. vtlengine/AST/ASTConstructor.py +598 -0
  8. vtlengine/AST/ASTConstructorModules/Expr.py +1928 -0
  9. vtlengine/AST/ASTConstructorModules/ExprComponents.py +995 -0
  10. vtlengine/AST/ASTConstructorModules/Terminals.py +790 -0
  11. vtlengine/AST/ASTConstructorModules/__init__.py +50 -0
  12. vtlengine/AST/ASTDataExchange.py +10 -0
  13. vtlengine/AST/ASTEncoders.py +32 -0
  14. vtlengine/AST/ASTString.py +675 -0
  15. vtlengine/AST/ASTTemplate.py +558 -0
  16. vtlengine/AST/ASTVisitor.py +25 -0
  17. vtlengine/AST/DAG/__init__.py +479 -0
  18. vtlengine/AST/DAG/_words.py +10 -0
  19. vtlengine/AST/Grammar/Vtl.g4 +705 -0
  20. vtlengine/AST/Grammar/VtlTokens.g4 +409 -0
  21. vtlengine/AST/Grammar/__init__.py +0 -0
  22. vtlengine/AST/Grammar/lexer.py +2139 -0
  23. vtlengine/AST/Grammar/parser.py +16597 -0
  24. vtlengine/AST/Grammar/tokens.py +169 -0
  25. vtlengine/AST/VtlVisitor.py +824 -0
  26. vtlengine/AST/__init__.py +674 -0
  27. vtlengine/DataTypes/TimeHandling.py +562 -0
  28. vtlengine/DataTypes/__init__.py +863 -0
  29. vtlengine/DataTypes/_time_checking.py +135 -0
  30. vtlengine/Exceptions/__exception_file_generator.py +96 -0
  31. vtlengine/Exceptions/__init__.py +159 -0
  32. vtlengine/Exceptions/messages.py +1004 -0
  33. vtlengine/Interpreter/__init__.py +2048 -0
  34. vtlengine/Model/__init__.py +501 -0
  35. vtlengine/Operators/Aggregation.py +357 -0
  36. vtlengine/Operators/Analytic.py +455 -0
  37. vtlengine/Operators/Assignment.py +23 -0
  38. vtlengine/Operators/Boolean.py +106 -0
  39. vtlengine/Operators/CastOperator.py +451 -0
  40. vtlengine/Operators/Clause.py +366 -0
  41. vtlengine/Operators/Comparison.py +488 -0
  42. vtlengine/Operators/Conditional.py +495 -0
  43. vtlengine/Operators/General.py +191 -0
  44. vtlengine/Operators/HROperators.py +254 -0
  45. vtlengine/Operators/Join.py +447 -0
  46. vtlengine/Operators/Numeric.py +422 -0
  47. vtlengine/Operators/RoleSetter.py +77 -0
  48. vtlengine/Operators/Set.py +176 -0
  49. vtlengine/Operators/String.py +578 -0
  50. vtlengine/Operators/Time.py +1144 -0
  51. vtlengine/Operators/Validation.py +275 -0
  52. vtlengine/Operators/__init__.py +900 -0
  53. vtlengine/Utils/__Virtual_Assets.py +34 -0
  54. vtlengine/Utils/__init__.py +479 -0
  55. vtlengine/__extras_check.py +17 -0
  56. vtlengine/__init__.py +27 -0
  57. vtlengine/files/__init__.py +0 -0
  58. vtlengine/files/output/__init__.py +35 -0
  59. vtlengine/files/output/_time_period_representation.py +55 -0
  60. vtlengine/files/parser/__init__.py +240 -0
  61. vtlengine/files/parser/_rfc_dialect.py +22 -0
  62. vtlengine/py.typed +0 -0
  63. vtlengine-1.4.0rc2.dist-info/METADATA +89 -0
  64. vtlengine-1.4.0rc2.dist-info/RECORD +66 -0
  65. vtlengine-1.4.0rc2.dist-info/WHEEL +4 -0
  66. vtlengine-1.4.0rc2.dist-info/licenses/LICENSE.md +661 -0
@@ -0,0 +1,479 @@
1
+ """
2
+ Interpreter DAG Analyzer.
3
+ ===========================
4
+
5
+ Description
6
+ -----------
7
+ Direct Acyclic Graph.
8
+ """
9
+
10
+ import copy
11
+ from dataclasses import dataclass
12
+ from typing import Any, Dict, Optional
13
+
14
+ import networkx as nx
15
+
16
+ from vtlengine.AST import (
17
+ AST,
18
+ Aggregation,
19
+ Analytic,
20
+ Assignment,
21
+ BinOp,
22
+ Constant,
23
+ DefIdentifier,
24
+ DPRuleset,
25
+ HRuleset,
26
+ Identifier,
27
+ JoinOp,
28
+ Operator,
29
+ ParamOp,
30
+ PersistentAssignment,
31
+ RegularAggregation,
32
+ Start,
33
+ UDOCall,
34
+ VarID,
35
+ )
36
+ from vtlengine.AST.ASTTemplate import ASTTemplate
37
+ from vtlengine.AST.DAG._words import DELETE, GLOBAL, INPUTS, INSERT, OUTPUTS, PERSISTENT, UNKNOWN
38
+ from vtlengine.AST.Grammar.tokens import AS, DROP, KEEP, MEMBERSHIP, RENAME, TO
39
+ from vtlengine.Exceptions import SemanticError
40
+ from vtlengine.Model import Component
41
+
42
+
43
+ @dataclass
44
+ class DAGAnalyzer(ASTTemplate):
45
+ udos: Optional[Dict[str, Any]] = None
46
+ numberOfStatements: int = 1
47
+ dependencies: Optional[Dict[str, Any]] = None
48
+ vertex: Optional[Dict[str, Any]] = None
49
+ extVertex: Optional[Dict[str, Any]] = None
50
+ persVertex: Optional[list] = None
51
+ edges: Optional[Dict[str, Any]] = None
52
+ nov: int = 0
53
+ dot: Optional[str] = None
54
+ sorting: Optional[list] = None
55
+ output: Optional[Dict[str, Any]] = None
56
+
57
+ # Handlers
58
+ isFirstAssignment: bool = False
59
+ isFromRegularAggregation: bool = False
60
+ isDataset: bool = False
61
+ alias: Optional[list] = None
62
+
63
+ # Statement Structure
64
+ inputs: Optional[list] = None
65
+ outputs: Optional[list] = None
66
+ persistent: Optional[list] = None
67
+ unknown_variables: Optional[list] = None
68
+ unknown_variables_statement: Optional[list] = None
69
+
70
+ def __post_init__(self):
71
+ self.dependencies = {}
72
+ self.vertex = {}
73
+ self.extVertex = {}
74
+ self.persVertex = []
75
+ self.edges = {}
76
+ self.inputs = []
77
+ self.outputs = []
78
+ self.persistent = []
79
+ self.alias = []
80
+ self.unknown_variables = []
81
+ self.unknown_variables_statement = []
82
+
83
+ @classmethod
84
+ def ds_structure(cls, ast: AST):
85
+ # Visit AST.
86
+ dag = cls()
87
+ dag.visit(ast)
88
+ return dag._ds_usage_analysis()
89
+
90
+ def _ds_usage_analysis(self):
91
+ statements = {INSERT: {}, DELETE: {}}
92
+ all_output = []
93
+ global_inputs = []
94
+ inserted = []
95
+ persistent_datasets = []
96
+ for key, statement in self.dependencies.items():
97
+ outputs = statement[OUTPUTS]
98
+ persistent = statement[PERSISTENT]
99
+ reference = outputs + persistent
100
+ if len(persistent) == 1 and persistent[0] not in persistent_datasets:
101
+ persistent_datasets.append(persistent[0])
102
+ deletion_key = key
103
+ all_output.append(reference[0])
104
+ for subKey, subStatement in self.dependencies.items():
105
+ candidates = subStatement[INPUTS]
106
+ if candidates and reference[0] in candidates:
107
+ deletion_key = subKey
108
+ if deletion_key in statements[DELETE]:
109
+ statements[DELETE][deletion_key].append(reference[0])
110
+ else:
111
+ statements[DELETE][deletion_key] = reference
112
+
113
+ # Deletion of gloabl inputs
114
+ for key, statement in self.dependencies.items():
115
+ inputs = statement[INPUTS]
116
+ for element in inputs:
117
+ if element not in all_output and element not in global_inputs:
118
+ deletion_key = key
119
+ global_inputs.append(element)
120
+ for subKey, subStatement in self.dependencies.items():
121
+ candidates = subStatement[INPUTS]
122
+ if candidates and element in candidates:
123
+ deletion_key = subKey
124
+ if deletion_key in statements[DELETE]:
125
+ statements[DELETE][deletion_key].append(element)
126
+ else:
127
+ statements[DELETE][deletion_key] = [element]
128
+
129
+ # Insertion of global inputs
130
+ for key, statement in self.dependencies.items():
131
+ for element in statement[INPUTS]:
132
+ if element not in inserted and element in global_inputs:
133
+ inserted.append(element)
134
+ if key in statements[INSERT]:
135
+ statements[INSERT][key].append(element)
136
+ else:
137
+ statements[INSERT][key] = [element]
138
+
139
+ statements[GLOBAL] = global_inputs
140
+ statements[PERSISTENT] = persistent_datasets
141
+ return statements
142
+
143
+ @classmethod
144
+ def createDAG(cls, ast: Start):
145
+ """ """
146
+ # Visit AST.
147
+ dag = cls()
148
+ dag.visit(ast)
149
+ # Create graph.
150
+ dag.loadVertex()
151
+ dag.loadEdges()
152
+ try:
153
+ dag.nx_topologicalSort()
154
+ # Create output dict.
155
+ if len(dag.edges) != 0:
156
+ dag.sortAST(ast)
157
+ else:
158
+ MLStatements: list = [
159
+ ML for ML in ast.children if not isinstance(ML, (HRuleset, DPRuleset, Operator))
160
+ ]
161
+ dag.check_overwriting(MLStatements)
162
+ return dag
163
+
164
+ except nx.NetworkXUnfeasible:
165
+ error_keys = {}
166
+ for v in dag.edges.values():
167
+ aux_v0, aux_v1 = v[1], v[0]
168
+ for iv in dag.edges.values():
169
+ if aux_v0 == iv[0] and aux_v1 == iv[1]:
170
+ error_keys[aux_v0] = dag.dependencies[aux_v0]
171
+ error_keys[aux_v1] = dag.dependencies[aux_v1]
172
+ break
173
+ raise SemanticError("1-3-2-3", op="createDAG", nodes=error_keys) from None
174
+ except SemanticError as error:
175
+ raise error
176
+ except Exception as error:
177
+ raise SemanticError(code="1-3-2-0") from error
178
+
179
+ def loadVertex(self):
180
+ """ """
181
+ # For each vertex
182
+ for key, statement in self.dependencies.items():
183
+ output = statement[OUTPUTS] + statement[PERSISTENT] + statement[UNKNOWN]
184
+ # If the statement has no := or -> symbol there is no vertex to add.
185
+ if len(output) != 0:
186
+ self.vertex[key] = output[0]
187
+
188
+ # Set the number of vertex.
189
+ self.nov = len(self.vertex)
190
+
191
+ def loadEdges(self):
192
+ """ """
193
+ if len(self.vertex) != 0:
194
+ count_edges = 0
195
+ # Build a mapping of datasets to their statement keys
196
+ ref_to_keys = {}
197
+ for key, statement in self.dependencies.items():
198
+ reference = statement[OUTPUTS] + statement[PERSISTENT]
199
+ if reference:
200
+ ref_value = reference[0]
201
+ ref_to_keys[ref_value] = key
202
+
203
+ # Create edges by checking inputs against the mapping
204
+ for subKey, subStatement in self.dependencies.items():
205
+ for input_val in subStatement[INPUTS]:
206
+ if input_val in ref_to_keys:
207
+ key = ref_to_keys[input_val]
208
+ self.edges[count_edges] = (key, subKey)
209
+ count_edges += 1
210
+
211
+ def nx_topologicalSort(self):
212
+ """ """
213
+ edges = list(self.edges.values())
214
+ DAG = nx.DiGraph()
215
+ DAG.add_nodes_from(self.vertex)
216
+ DAG.add_edges_from(edges)
217
+ self.sorting = list(nx.topological_sort(DAG))
218
+
219
+ def sort_elements(self, MLStatements):
220
+ inter = []
221
+ for x in self.sorting:
222
+ for i in range(len(MLStatements)):
223
+ if i == x - 1:
224
+ inter.append(MLStatements[i])
225
+ return inter
226
+
227
+ def check_overwriting(self, statements):
228
+ non_repeated_outputs = []
229
+ for statement in statements:
230
+ if statement.left.value in non_repeated_outputs:
231
+ raise SemanticError("1-2-2", varId_value=statement.left.value)
232
+ else:
233
+ non_repeated_outputs.append(statement.left.value)
234
+
235
+ def sortAST(self, ast: AST):
236
+ """ """
237
+ statements_nodes = ast.children
238
+ HRuleStatements: list = [HRule for HRule in statements_nodes if isinstance(HRule, HRuleset)]
239
+ DPRuleStatement: list = [
240
+ DPRule for DPRule in statements_nodes if isinstance(DPRule, DPRuleset)
241
+ ]
242
+ DOStatement: list = [DO for DO in statements_nodes if isinstance(DO, Operator)]
243
+ MLStatements: list = [
244
+ ML for ML in statements_nodes if not isinstance(ML, (HRuleset, DPRuleset, Operator))
245
+ ]
246
+
247
+ intermediate = self.sort_elements(MLStatements)
248
+ self.check_overwriting(intermediate)
249
+ ast.children = HRuleStatements + DPRuleStatement + DOStatement + intermediate
250
+
251
+ def statementStructure(self) -> dict:
252
+ """ """
253
+ inputs = list(set(self.inputs))
254
+ outputs = list(set(self.outputs))
255
+ persistent = list(set(self.persistent))
256
+ unknown = list(set(self.unknown_variables_statement))
257
+
258
+ # Remove inputs that are outputs of some statement.
259
+ inputsF = [inputf for inputf in inputs if inputf not in outputs]
260
+
261
+ dict_ = {INPUTS: inputsF, OUTPUTS: outputs, PERSISTENT: persistent, UNKNOWN: unknown}
262
+ for variable in self.unknown_variables_statement:
263
+ if variable not in self.unknown_variables:
264
+ self.unknown_variables.append(variable)
265
+ return dict_
266
+
267
+ """______________________________________________________________________________________
268
+
269
+
270
+ Start of visiting AST nodes.
271
+
272
+ _______________________________________________________________________________________"""
273
+
274
+ def visit_Start(self, node: Start) -> None:
275
+ """
276
+ Start: (children)
277
+
278
+ Basic usage:
279
+
280
+ for child in node.children:
281
+ self.visit(child)
282
+ """
283
+ udos = {}
284
+ for ast_element in node.children:
285
+ if isinstance(ast_element, Operator):
286
+ udos[ast_element.op] = ast_element
287
+ self.udos = udos
288
+ for child in node.children:
289
+ if isinstance(child, (Assignment, PersistentAssignment)):
290
+ self.isFirstAssignment = True
291
+ self.visit(child)
292
+
293
+ # Analyze inputs and outputs per each statement.
294
+ self.dependencies[self.numberOfStatements] = copy.deepcopy(
295
+ self.statementStructure()
296
+ )
297
+
298
+ # Count the number of statements in order to name the scope symbol table for
299
+ # each one.
300
+ self.numberOfStatements += 1
301
+
302
+ self.alias = []
303
+
304
+ self.inputs = []
305
+ self.outputs = []
306
+ self.persistent = []
307
+ self.unknown_variables_statement = []
308
+ aux = copy.copy(self.unknown_variables)
309
+ for variable in aux:
310
+ for _number_of_statement, dependency in self.dependencies.items():
311
+ if variable in dependency[OUTPUTS]:
312
+ if variable in self.unknown_variables:
313
+ self.unknown_variables.remove(variable)
314
+ for _number_of_statement, dependency in self.dependencies.items():
315
+ if variable in dependency[UNKNOWN]:
316
+ dependency[UNKNOWN].remove(variable)
317
+ dependency[INPUTS].append(variable)
318
+ if variable not in self.inputs:
319
+ self.inputs.append(variable)
320
+
321
+ def visit_Assignment(self, node: Assignment) -> None:
322
+ if self.isFirstAssignment:
323
+ self.outputs.append(node.left.value)
324
+ self.isFirstAssignment = False
325
+
326
+ self.visit(node.right)
327
+
328
+ def visit_PersistentAssignment(self, node: PersistentAssignment) -> None:
329
+ if self.isFirstAssignment:
330
+ self.persistent.append(node.left.value)
331
+ self.isFirstAssignment = False
332
+
333
+ self.visit(node.right)
334
+
335
+ def visit_RegularAggregation(self, node: RegularAggregation) -> None:
336
+ self.visit(node.dataset)
337
+ if node.op in [KEEP, DROP, RENAME]:
338
+ return
339
+ for child in node.children:
340
+ self.isFromRegularAggregation = True
341
+ self.visit(child)
342
+ self.isFromRegularAggregation = False
343
+
344
+ def visit_BinOp(self, node: BinOp) -> None:
345
+ if node.op == MEMBERSHIP:
346
+ self.isDataset = True
347
+ self.visit(node.left)
348
+ self.isDataset = False
349
+ self.visit(node.right)
350
+ elif node.op == AS or node.op == TO:
351
+ self.visit(node.left)
352
+ self.alias.append(node.right.value)
353
+ else:
354
+ self.visit(node.left)
355
+ self.visit(node.right)
356
+
357
+ def visit_VarID(self, node: VarID) -> None:
358
+ if (not self.isFromRegularAggregation or self.isDataset) and node.value not in self.alias:
359
+ self.inputs.append(node.value)
360
+ elif (
361
+ self.isFromRegularAggregation
362
+ and node.value not in self.alias
363
+ and not self.isDataset
364
+ and node.value not in self.unknown_variables_statement
365
+ ):
366
+ self.unknown_variables_statement.append(node.value)
367
+
368
+ def visit_Identifier(self, node: Identifier) -> None:
369
+ if node.kind == "DatasetID" and node.value not in self.alias:
370
+ self.inputs.append(node.value)
371
+
372
+ def visit_ParamOp(self, node: ParamOp) -> None:
373
+ if self.udos and node.op in self.udos:
374
+ DO_AST: Operator = self.udos[node.op]
375
+
376
+ for arg in node.params:
377
+ index_arg = node.params.index(arg)
378
+ if DO_AST.parameters[index_arg].type_.kind == "DataSet":
379
+ self.visit(arg)
380
+ else:
381
+ super(DAGAnalyzer, self).visit_ParamOp(node)
382
+
383
+ def visit_Aggregation(self, node: Aggregation) -> None:
384
+ if node.operand is not None:
385
+ self.visit(node.operand)
386
+
387
+ def visit_Analytic(self, node: Analytic) -> None:
388
+ if node.operand is not None:
389
+ self.visit(node.operand)
390
+
391
+ def visit_JoinOp(self, node: JoinOp) -> None:
392
+ for clause in node.clauses:
393
+ self.visit(clause)
394
+
395
+ def visit_UDOCall(self, node: UDOCall) -> None:
396
+ node_args = (self.udos or {}).get(node.op)
397
+ if not node_args:
398
+ super().visit_UDOCall(node)
399
+ else:
400
+ node_sig = [type(p.type_) for p in node_args.parameters]
401
+ for sig, param in zip(node_sig, node.params):
402
+ if not isinstance(param, Constant) and sig is not Component:
403
+ self.visit(param)
404
+
405
+
406
+ class HRDAGAnalyzer(DAGAnalyzer):
407
+ @classmethod
408
+ def createDAG(cls, ast: HRuleset):
409
+ # Visit AST.
410
+ dag = cls()
411
+ dag.visit(ast)
412
+ # Create graph.
413
+ dag.loadVertex()
414
+ dag.loadEdges()
415
+ try:
416
+ dag.nx_topologicalSort()
417
+ # Create output dict.
418
+ if len(dag.edges) != 0:
419
+ dag.rules_ast = dag.sort_elements(dag.rules_ast)
420
+ ast.rules = dag.rules_ast
421
+ return dag
422
+
423
+ except nx.NetworkXUnfeasible:
424
+ error_keys = {}
425
+ for v in dag.edges.values():
426
+ aux_v0, aux_v1 = v[1], v[0]
427
+ for iv in dag.edges.values():
428
+ if aux_v0 == iv[0] and aux_v1 == iv[1]:
429
+ error_keys[aux_v0] = dag.dependencies[aux_v0]
430
+ error_keys[aux_v1] = dag.dependencies[aux_v1]
431
+ break
432
+ raise SemanticError(code="1-3-2-3", op="createHRDAG", nodes=error_keys)
433
+
434
+ def visit_HRuleset(self, node: HRuleset) -> None:
435
+ """
436
+ HRuleset: (name, element, rules)
437
+
438
+ Basic usage:
439
+
440
+ self.visit(node.element)
441
+ for rule in node.rules:
442
+ self.visit(rule)
443
+ """
444
+ self.hierarchy_ruleset_name = node.name
445
+ if isinstance(node.element, list):
446
+ for element in node.element:
447
+ self.visit(element)
448
+ else:
449
+ self.visit(node.element)
450
+ # self.visit(node.element)
451
+ self.rules_ast = node.rules
452
+ for rule in node.rules:
453
+ self.isFirstAssignment = True
454
+ self.visit(rule)
455
+ self.dependencies[self.numberOfStatements] = copy.deepcopy(self.statementStructure())
456
+
457
+ # Count the number of statements in order to name the scope symbol table for each one.
458
+ self.numberOfStatements += 1
459
+ self.alias = []
460
+
461
+ self.inputs = []
462
+ self.outputs = []
463
+ self.persistent = []
464
+
465
+ def visit_DefIdentifier(self, node: DefIdentifier):
466
+ """
467
+ DefIdentifier: (value, kind)
468
+
469
+ Basic usage:
470
+
471
+ return node.value
472
+ """
473
+ # def visit_Identifier(self, node: Identifier) -> None:
474
+ if node.kind == "CodeItemID": # and node.value not in self.alias:
475
+ if self.isFirstAssignment:
476
+ self.isFirstAssignment = False
477
+ self.outputs.append(node.value)
478
+ else:
479
+ self.inputs.append(node.value)
@@ -0,0 +1,10 @@
1
+ # DS analysis
2
+ INSERT = "insertion"
3
+ DELETE = "deletion"
4
+ GLOBAL = "global_inputs"
5
+
6
+ INPUTS = "inputs"
7
+ OUTPUTS = "outputs"
8
+ PERSISTENT = "persistent"
9
+ STATEMENT_ = "statement"
10
+ UNKNOWN = "unknown_variables"