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.
- vtlengine/API/_InternalApi.py +791 -0
- vtlengine/API/__init__.py +612 -0
- vtlengine/API/data/schema/external_routines_schema.json +34 -0
- vtlengine/API/data/schema/json_schema_2.1.json +116 -0
- vtlengine/API/data/schema/value_domain_schema.json +97 -0
- vtlengine/AST/ASTComment.py +57 -0
- vtlengine/AST/ASTConstructor.py +598 -0
- vtlengine/AST/ASTConstructorModules/Expr.py +1928 -0
- vtlengine/AST/ASTConstructorModules/ExprComponents.py +995 -0
- vtlengine/AST/ASTConstructorModules/Terminals.py +790 -0
- vtlengine/AST/ASTConstructorModules/__init__.py +50 -0
- vtlengine/AST/ASTDataExchange.py +10 -0
- vtlengine/AST/ASTEncoders.py +32 -0
- vtlengine/AST/ASTString.py +675 -0
- vtlengine/AST/ASTTemplate.py +558 -0
- vtlengine/AST/ASTVisitor.py +25 -0
- vtlengine/AST/DAG/__init__.py +479 -0
- vtlengine/AST/DAG/_words.py +10 -0
- vtlengine/AST/Grammar/Vtl.g4 +705 -0
- vtlengine/AST/Grammar/VtlTokens.g4 +409 -0
- vtlengine/AST/Grammar/__init__.py +0 -0
- vtlengine/AST/Grammar/lexer.py +2139 -0
- vtlengine/AST/Grammar/parser.py +16597 -0
- vtlengine/AST/Grammar/tokens.py +169 -0
- vtlengine/AST/VtlVisitor.py +824 -0
- vtlengine/AST/__init__.py +674 -0
- vtlengine/DataTypes/TimeHandling.py +562 -0
- vtlengine/DataTypes/__init__.py +863 -0
- vtlengine/DataTypes/_time_checking.py +135 -0
- vtlengine/Exceptions/__exception_file_generator.py +96 -0
- vtlengine/Exceptions/__init__.py +159 -0
- vtlengine/Exceptions/messages.py +1004 -0
- vtlengine/Interpreter/__init__.py +2048 -0
- vtlengine/Model/__init__.py +501 -0
- vtlengine/Operators/Aggregation.py +357 -0
- vtlengine/Operators/Analytic.py +455 -0
- vtlengine/Operators/Assignment.py +23 -0
- vtlengine/Operators/Boolean.py +106 -0
- vtlengine/Operators/CastOperator.py +451 -0
- vtlengine/Operators/Clause.py +366 -0
- vtlengine/Operators/Comparison.py +488 -0
- vtlengine/Operators/Conditional.py +495 -0
- vtlengine/Operators/General.py +191 -0
- vtlengine/Operators/HROperators.py +254 -0
- vtlengine/Operators/Join.py +447 -0
- vtlengine/Operators/Numeric.py +422 -0
- vtlengine/Operators/RoleSetter.py +77 -0
- vtlengine/Operators/Set.py +176 -0
- vtlengine/Operators/String.py +578 -0
- vtlengine/Operators/Time.py +1144 -0
- vtlengine/Operators/Validation.py +275 -0
- vtlengine/Operators/__init__.py +900 -0
- vtlengine/Utils/__Virtual_Assets.py +34 -0
- vtlengine/Utils/__init__.py +479 -0
- vtlengine/__extras_check.py +17 -0
- vtlengine/__init__.py +27 -0
- vtlengine/files/__init__.py +0 -0
- vtlengine/files/output/__init__.py +35 -0
- vtlengine/files/output/_time_period_representation.py +55 -0
- vtlengine/files/parser/__init__.py +240 -0
- vtlengine/files/parser/_rfc_dialect.py +22 -0
- vtlengine/py.typed +0 -0
- vtlengine-1.4.0rc2.dist-info/METADATA +89 -0
- vtlengine-1.4.0rc2.dist-info/RECORD +66 -0
- vtlengine-1.4.0rc2.dist-info/WHEEL +4 -0
- 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)
|