vtlengine 1.1rc1__py3-none-any.whl → 1.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of vtlengine might be problematic. Click here for more details.
- vtlengine/API/_InternalApi.py +231 -6
- vtlengine/API/__init__.py +258 -69
- vtlengine/AST/ASTComment.py +56 -0
- vtlengine/AST/ASTConstructor.py +71 -18
- vtlengine/AST/ASTConstructorModules/Expr.py +191 -72
- vtlengine/AST/ASTConstructorModules/ExprComponents.py +81 -38
- vtlengine/AST/ASTConstructorModules/Terminals.py +76 -31
- vtlengine/AST/ASTConstructorModules/__init__.py +50 -0
- vtlengine/AST/ASTEncoders.py +4 -0
- vtlengine/AST/ASTString.py +622 -0
- vtlengine/AST/ASTTemplate.py +28 -2
- vtlengine/AST/DAG/__init__.py +10 -1
- vtlengine/AST/__init__.py +127 -14
- vtlengine/Exceptions/messages.py +9 -0
- vtlengine/Interpreter/__init__.py +53 -8
- vtlengine/Model/__init__.py +9 -4
- vtlengine/Operators/Aggregation.py +7 -5
- vtlengine/Operators/Analytic.py +16 -11
- vtlengine/Operators/Conditional.py +20 -5
- vtlengine/Operators/Time.py +11 -10
- vtlengine/Utils/__init__.py +49 -0
- vtlengine/__init__.py +4 -2
- vtlengine/files/parser/__init__.py +16 -26
- vtlengine/files/parser/_rfc_dialect.py +1 -1
- vtlengine/py.typed +0 -0
- vtlengine-1.1.1.dist-info/METADATA +92 -0
- {vtlengine-1.1rc1.dist-info → vtlengine-1.1.1.dist-info}/RECORD +29 -26
- {vtlengine-1.1rc1.dist-info → vtlengine-1.1.1.dist-info}/WHEEL +1 -1
- vtlengine-1.1rc1.dist-info/METADATA +0 -248
- {vtlengine-1.1rc1.dist-info → vtlengine-1.1.1.dist-info}/LICENSE.md +0 -0
vtlengine/AST/ASTTemplate.py
CHANGED
|
@@ -186,8 +186,6 @@ class ASTTemplate(NodeVisitor):
|
|
|
186
186
|
|
|
187
187
|
return node.value
|
|
188
188
|
"""
|
|
189
|
-
if node.value in self.datasets:
|
|
190
|
-
return self.datasets[node.value]
|
|
191
189
|
return node.value
|
|
192
190
|
|
|
193
191
|
def visit_Optional(self, node: AST.Optional) -> AST.AST:
|
|
@@ -329,6 +327,18 @@ class ASTTemplate(NodeVisitor):
|
|
|
329
327
|
self.visit(case.thenOp)
|
|
330
328
|
self.visit(node.elseOp)
|
|
331
329
|
|
|
330
|
+
def visit_CaseObj(self, node: AST.CaseObj) -> Any:
|
|
331
|
+
"""
|
|
332
|
+
CaseObj: (condition, thenOp)
|
|
333
|
+
|
|
334
|
+
Basic usage:
|
|
335
|
+
|
|
336
|
+
self.visit(node.condition)
|
|
337
|
+
self.visit(node.thenOp)
|
|
338
|
+
"""
|
|
339
|
+
self.visit(node.condition)
|
|
340
|
+
self.visit(node.thenOp)
|
|
341
|
+
|
|
332
342
|
def visit_Validation(self, node: AST.Validation) -> Any:
|
|
333
343
|
"""
|
|
334
344
|
Validation: (op, validation, params, inbalance, invalid)
|
|
@@ -505,6 +515,12 @@ class ASTTemplate(NodeVisitor):
|
|
|
505
515
|
for child in node.operands:
|
|
506
516
|
self.visit(child)
|
|
507
517
|
|
|
518
|
+
def visit_ParFunction(self, node: AST.ParFunction) -> None:
|
|
519
|
+
"""
|
|
520
|
+
ParFunction: (operand)
|
|
521
|
+
"""
|
|
522
|
+
self.visit(node.operand)
|
|
523
|
+
|
|
508
524
|
def visit_NoOp(self, node: AST.NoOp) -> None: # pylint: disable=unused-argument
|
|
509
525
|
"""
|
|
510
526
|
NoOp: ()
|
|
@@ -526,3 +542,13 @@ class ASTTemplate(NodeVisitor):
|
|
|
526
542
|
"""
|
|
527
543
|
for param in node.params:
|
|
528
544
|
self.visit(param)
|
|
545
|
+
|
|
546
|
+
def visit_Windowing(self, node: AST.Windowing) -> None:
|
|
547
|
+
"""
|
|
548
|
+
Windowing: (type_, start, start_mode, stop, stop_mode)
|
|
549
|
+
"""
|
|
550
|
+
|
|
551
|
+
def visit_Comment(self, node: AST.Comment) -> None:
|
|
552
|
+
"""
|
|
553
|
+
Comment: (value)
|
|
554
|
+
"""
|
vtlengine/AST/DAG/__init__.py
CHANGED
|
@@ -85,10 +85,13 @@ class DAGAnalyzer(ASTTemplate):
|
|
|
85
85
|
all_output = []
|
|
86
86
|
global_inputs = []
|
|
87
87
|
inserted = []
|
|
88
|
+
persistent_datasets = []
|
|
88
89
|
for key, statement in self.dependencies.items():
|
|
89
90
|
outputs = statement[OUTPUTS]
|
|
90
91
|
persistent = statement[PERSISTENT]
|
|
91
92
|
reference = outputs + persistent
|
|
93
|
+
if len(persistent) == 1 and persistent[0] not in persistent_datasets:
|
|
94
|
+
persistent_datasets.append(persistent[0])
|
|
92
95
|
deletion_key = key
|
|
93
96
|
all_output.append(reference[0])
|
|
94
97
|
for subKey, subStatement in self.dependencies.items():
|
|
@@ -127,10 +130,11 @@ class DAGAnalyzer(ASTTemplate):
|
|
|
127
130
|
statements[INSERT][key] = [element]
|
|
128
131
|
|
|
129
132
|
statements[GLOBAL] = global_inputs
|
|
133
|
+
statements[PERSISTENT] = persistent_datasets
|
|
130
134
|
return statements
|
|
131
135
|
|
|
132
136
|
@classmethod
|
|
133
|
-
def createDAG(cls, ast:
|
|
137
|
+
def createDAG(cls, ast: Start):
|
|
134
138
|
""" """
|
|
135
139
|
# Visit AST.
|
|
136
140
|
dag = cls()
|
|
@@ -143,6 +147,11 @@ class DAGAnalyzer(ASTTemplate):
|
|
|
143
147
|
# Create output dict.
|
|
144
148
|
if len(dag.edges) != 0:
|
|
145
149
|
dag.sortAST(ast)
|
|
150
|
+
else:
|
|
151
|
+
MLStatements: list = [
|
|
152
|
+
ML for ML in ast.children if not isinstance(ML, (HRuleset, DPRuleset, Operator))
|
|
153
|
+
]
|
|
154
|
+
dag.check_overwriting(MLStatements)
|
|
146
155
|
return dag
|
|
147
156
|
|
|
148
157
|
except nx.NetworkXUnfeasible as error:
|
vtlengine/AST/__init__.py
CHANGED
|
@@ -11,7 +11,7 @@ from dataclasses import dataclass
|
|
|
11
11
|
from typing import Any, Dict, List, Optional, Type, Union
|
|
12
12
|
|
|
13
13
|
from vtlengine.DataTypes import ScalarType
|
|
14
|
-
from vtlengine.Model import Role
|
|
14
|
+
from vtlengine.Model import Dataset, Role
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
@dataclass
|
|
@@ -20,6 +20,11 @@ class AST:
|
|
|
20
20
|
AST: (children)
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
+
line_start: int
|
|
24
|
+
column_start: int
|
|
25
|
+
line_stop: int
|
|
26
|
+
column_stop: int
|
|
27
|
+
|
|
23
28
|
@classmethod
|
|
24
29
|
def __all_annotations(cls) -> Dict[str, Any]:
|
|
25
30
|
class_attributes = {}
|
|
@@ -47,6 +52,30 @@ class AST:
|
|
|
47
52
|
|
|
48
53
|
__repr__ = __str__
|
|
49
54
|
|
|
55
|
+
def ast_equality(self, other):
|
|
56
|
+
if not isinstance(other, self.__class__):
|
|
57
|
+
return False
|
|
58
|
+
for k in self.__all_annotations():
|
|
59
|
+
if (
|
|
60
|
+
getattr(self, k) != getattr(other, k)
|
|
61
|
+
and k not in AST.__annotations__
|
|
62
|
+
and k != "children" # We do not want to compare the children order here
|
|
63
|
+
):
|
|
64
|
+
return False
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
__eq__ = ast_equality
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass
|
|
71
|
+
class Comment(AST):
|
|
72
|
+
"""
|
|
73
|
+
Comment: (value)
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
value: str
|
|
77
|
+
__eq__ = AST.ast_equality
|
|
78
|
+
|
|
50
79
|
|
|
51
80
|
@dataclass
|
|
52
81
|
class Start(AST):
|
|
@@ -56,6 +85,8 @@ class Start(AST):
|
|
|
56
85
|
|
|
57
86
|
children: List[AST]
|
|
58
87
|
|
|
88
|
+
__eq__ = AST.ast_equality
|
|
89
|
+
|
|
59
90
|
|
|
60
91
|
@dataclass
|
|
61
92
|
class Assignment(AST):
|
|
@@ -67,6 +98,8 @@ class Assignment(AST):
|
|
|
67
98
|
op: str
|
|
68
99
|
right: AST
|
|
69
100
|
|
|
101
|
+
__eq__ = AST.ast_equality
|
|
102
|
+
|
|
70
103
|
|
|
71
104
|
@dataclass
|
|
72
105
|
class PersistentAssignment(Assignment):
|
|
@@ -85,7 +118,9 @@ class VarID(AST):
|
|
|
85
118
|
Could be: DATASET or a COMPONENT.
|
|
86
119
|
"""
|
|
87
120
|
|
|
88
|
-
value:
|
|
121
|
+
value: str
|
|
122
|
+
|
|
123
|
+
__eq__ = AST.ast_equality
|
|
89
124
|
|
|
90
125
|
|
|
91
126
|
@dataclass
|
|
@@ -100,6 +135,8 @@ class UnaryOp(AST):
|
|
|
100
135
|
op: str
|
|
101
136
|
operand: AST
|
|
102
137
|
|
|
138
|
+
__eq__ = AST.ast_equality
|
|
139
|
+
|
|
103
140
|
|
|
104
141
|
@dataclass
|
|
105
142
|
class BinOp(AST):
|
|
@@ -113,6 +150,8 @@ class BinOp(AST):
|
|
|
113
150
|
op: str
|
|
114
151
|
right: AST
|
|
115
152
|
|
|
153
|
+
__eq__ = AST.ast_equality
|
|
154
|
+
|
|
116
155
|
|
|
117
156
|
@dataclass
|
|
118
157
|
class MulOp(AST):
|
|
@@ -124,6 +163,8 @@ class MulOp(AST):
|
|
|
124
163
|
op: str
|
|
125
164
|
children: List[AST]
|
|
126
165
|
|
|
166
|
+
__eq__ = AST.ast_equality
|
|
167
|
+
|
|
127
168
|
|
|
128
169
|
@dataclass
|
|
129
170
|
class ParamOp(AST):
|
|
@@ -137,12 +178,16 @@ class ParamOp(AST):
|
|
|
137
178
|
children: List[AST]
|
|
138
179
|
params: List[AST]
|
|
139
180
|
|
|
181
|
+
__eq__ = AST.ast_equality
|
|
182
|
+
|
|
140
183
|
|
|
141
184
|
@dataclass
|
|
142
185
|
class UDOCall(AST):
|
|
143
186
|
op: str
|
|
144
187
|
params: List[AST]
|
|
145
188
|
|
|
189
|
+
__eq__ = AST.ast_equality
|
|
190
|
+
|
|
146
191
|
|
|
147
192
|
@dataclass
|
|
148
193
|
class JoinOp(AST):
|
|
@@ -156,9 +201,11 @@ class JoinOp(AST):
|
|
|
156
201
|
|
|
157
202
|
op: str
|
|
158
203
|
clauses: List[AST]
|
|
159
|
-
using: Optional[List[
|
|
204
|
+
using: Optional[List[str]]
|
|
160
205
|
isLast: bool = False
|
|
161
206
|
|
|
207
|
+
__eq__ = AST.ast_equality
|
|
208
|
+
|
|
162
209
|
|
|
163
210
|
@dataclass
|
|
164
211
|
class Constant(AST):
|
|
@@ -172,6 +219,8 @@ class Constant(AST):
|
|
|
172
219
|
type_: str
|
|
173
220
|
value: Optional[Union[str, int, float, bool]]
|
|
174
221
|
|
|
222
|
+
__eq__ = AST.ast_equality
|
|
223
|
+
|
|
175
224
|
|
|
176
225
|
@dataclass
|
|
177
226
|
class ParamConstant(Constant):
|
|
@@ -184,6 +233,8 @@ class ParamConstant(Constant):
|
|
|
184
233
|
type_: str
|
|
185
234
|
value: str
|
|
186
235
|
|
|
236
|
+
__eq__ = AST.ast_equality
|
|
237
|
+
|
|
187
238
|
|
|
188
239
|
@dataclass
|
|
189
240
|
class Identifier(AST):
|
|
@@ -194,6 +245,8 @@ class Identifier(AST):
|
|
|
194
245
|
value: str
|
|
195
246
|
kind: str
|
|
196
247
|
|
|
248
|
+
__eq__ = AST.ast_equality
|
|
249
|
+
|
|
197
250
|
|
|
198
251
|
@dataclass
|
|
199
252
|
class ID(AST):
|
|
@@ -206,6 +259,8 @@ class ID(AST):
|
|
|
206
259
|
type_: str
|
|
207
260
|
value: str
|
|
208
261
|
|
|
262
|
+
__eq__ = AST.ast_equality
|
|
263
|
+
|
|
209
264
|
|
|
210
265
|
@dataclass
|
|
211
266
|
class Collection(AST):
|
|
@@ -220,6 +275,8 @@ class Collection(AST):
|
|
|
220
275
|
children: List[AST]
|
|
221
276
|
kind: str = "Set"
|
|
222
277
|
|
|
278
|
+
__eq__ = AST.ast_equality
|
|
279
|
+
|
|
223
280
|
|
|
224
281
|
@dataclass
|
|
225
282
|
class Windowing(AST):
|
|
@@ -234,11 +291,13 @@ class Windowing(AST):
|
|
|
234
291
|
"""
|
|
235
292
|
|
|
236
293
|
type_: str
|
|
237
|
-
start: str
|
|
294
|
+
start: Union[int, str]
|
|
238
295
|
start_mode: str
|
|
239
296
|
stop: Union[int, str]
|
|
240
297
|
stop_mode: str
|
|
241
298
|
|
|
299
|
+
__eq__ = AST.ast_equality
|
|
300
|
+
|
|
242
301
|
|
|
243
302
|
@dataclass
|
|
244
303
|
class OrderBy(AST):
|
|
@@ -249,6 +308,8 @@ class OrderBy(AST):
|
|
|
249
308
|
if self.order not in ["asc", "desc"]:
|
|
250
309
|
raise ValueError(f"Invalid order: {self.order}")
|
|
251
310
|
|
|
311
|
+
__eq__ = AST.ast_equality
|
|
312
|
+
|
|
252
313
|
|
|
253
314
|
@dataclass
|
|
254
315
|
class Analytic(AST):
|
|
@@ -274,6 +335,8 @@ class Analytic(AST):
|
|
|
274
335
|
if self.partition_by is None and self.order_by is None:
|
|
275
336
|
raise ValueError("Partition by or order by must be provided on Analytic.")
|
|
276
337
|
|
|
338
|
+
__eq__ = AST.ast_equality
|
|
339
|
+
|
|
277
340
|
|
|
278
341
|
@dataclass
|
|
279
342
|
class RegularAggregation(AST):
|
|
@@ -288,6 +351,8 @@ class RegularAggregation(AST):
|
|
|
288
351
|
dataset: Optional[AST] = None
|
|
289
352
|
isLast: bool = False
|
|
290
353
|
|
|
354
|
+
__eq__ = AST.ast_equality
|
|
355
|
+
|
|
291
356
|
|
|
292
357
|
@dataclass
|
|
293
358
|
class RenameNode(AST):
|
|
@@ -298,6 +363,8 @@ class RenameNode(AST):
|
|
|
298
363
|
old_name: str
|
|
299
364
|
new_name: str
|
|
300
365
|
|
|
366
|
+
__eq__ = AST.ast_equality
|
|
367
|
+
|
|
301
368
|
|
|
302
369
|
@dataclass
|
|
303
370
|
class Aggregation(AST):
|
|
@@ -316,6 +383,8 @@ class Aggregation(AST):
|
|
|
316
383
|
grouping: Optional[List[AST]] = None
|
|
317
384
|
having_clause: Optional[AST] = None
|
|
318
385
|
|
|
386
|
+
__eq__ = AST.ast_equality
|
|
387
|
+
|
|
319
388
|
|
|
320
389
|
@dataclass
|
|
321
390
|
class TimeAggregation(AST):
|
|
@@ -331,6 +400,8 @@ class TimeAggregation(AST):
|
|
|
331
400
|
operand: Optional[AST] = None
|
|
332
401
|
conf: Optional[str] = None
|
|
333
402
|
|
|
403
|
+
__eq__ = AST.ast_equality
|
|
404
|
+
|
|
334
405
|
|
|
335
406
|
@dataclass
|
|
336
407
|
class If(AST):
|
|
@@ -342,15 +413,14 @@ class If(AST):
|
|
|
342
413
|
thenOp: AST
|
|
343
414
|
elseOp: AST
|
|
344
415
|
|
|
416
|
+
__eq__ = AST.ast_equality
|
|
417
|
+
|
|
345
418
|
|
|
346
|
-
|
|
419
|
+
@dataclass
|
|
420
|
+
class CaseObj(AST):
|
|
347
421
|
condition: AST
|
|
348
422
|
thenOp: AST
|
|
349
423
|
|
|
350
|
-
def __init__(self, condition: AST, thenOp: AST):
|
|
351
|
-
self.condition = condition
|
|
352
|
-
self.thenOp = thenOp
|
|
353
|
-
|
|
354
424
|
|
|
355
425
|
@dataclass
|
|
356
426
|
class Case(AST):
|
|
@@ -361,6 +431,8 @@ class Case(AST):
|
|
|
361
431
|
cases: List[CaseObj]
|
|
362
432
|
elseOp: AST
|
|
363
433
|
|
|
434
|
+
__eq__ = AST.ast_equality
|
|
435
|
+
|
|
364
436
|
|
|
365
437
|
@dataclass
|
|
366
438
|
class Validation(AST):
|
|
@@ -375,6 +447,8 @@ class Validation(AST):
|
|
|
375
447
|
imbalance: Optional[AST]
|
|
376
448
|
invalid: bool
|
|
377
449
|
|
|
450
|
+
__eq__ = AST.ast_equality
|
|
451
|
+
|
|
378
452
|
|
|
379
453
|
@dataclass
|
|
380
454
|
class ComponentType(AST):
|
|
@@ -386,6 +460,8 @@ class ComponentType(AST):
|
|
|
386
460
|
data_type: Optional[Type[ScalarType]] = None
|
|
387
461
|
role: Optional[Role] = None
|
|
388
462
|
|
|
463
|
+
__eq__ = AST.ast_equality
|
|
464
|
+
|
|
389
465
|
|
|
390
466
|
@dataclass
|
|
391
467
|
class ASTScalarType(AST):
|
|
@@ -400,6 +476,8 @@ class DatasetType(AST):
|
|
|
400
476
|
|
|
401
477
|
components: List[ComponentType]
|
|
402
478
|
|
|
479
|
+
__eq__ = AST.ast_equality
|
|
480
|
+
|
|
403
481
|
|
|
404
482
|
@dataclass
|
|
405
483
|
class Types(AST):
|
|
@@ -418,6 +496,8 @@ class Types(AST):
|
|
|
418
496
|
nullable: Optional[bool]
|
|
419
497
|
name: Optional[str] = None
|
|
420
498
|
|
|
499
|
+
__eq__ = AST.ast_equality
|
|
500
|
+
|
|
421
501
|
|
|
422
502
|
@dataclass
|
|
423
503
|
class Argument(AST):
|
|
@@ -429,6 +509,8 @@ class Argument(AST):
|
|
|
429
509
|
type_: Type[ScalarType]
|
|
430
510
|
default: Optional[AST]
|
|
431
511
|
|
|
512
|
+
__eq__ = AST.ast_equality
|
|
513
|
+
|
|
432
514
|
|
|
433
515
|
@dataclass
|
|
434
516
|
class Operator(AST):
|
|
@@ -441,6 +523,8 @@ class Operator(AST):
|
|
|
441
523
|
output_type: str
|
|
442
524
|
expression: AST
|
|
443
525
|
|
|
526
|
+
__eq__ = AST.ast_equality
|
|
527
|
+
|
|
444
528
|
|
|
445
529
|
# TODO: Is this class necessary?
|
|
446
530
|
@dataclass
|
|
@@ -452,6 +536,8 @@ class DefIdentifier(AST):
|
|
|
452
536
|
value: str
|
|
453
537
|
kind: str
|
|
454
538
|
|
|
539
|
+
__eq__ = AST.ast_equality
|
|
540
|
+
|
|
455
541
|
|
|
456
542
|
@dataclass
|
|
457
543
|
class DPRIdentifier(AST):
|
|
@@ -463,6 +549,8 @@ class DPRIdentifier(AST):
|
|
|
463
549
|
kind: str
|
|
464
550
|
alias: Optional[str] = None
|
|
465
551
|
|
|
552
|
+
__eq__ = AST.ast_equality
|
|
553
|
+
|
|
466
554
|
|
|
467
555
|
# TODO: Are HRBinOp and HRUnOp necessary?
|
|
468
556
|
@dataclass
|
|
@@ -476,6 +564,8 @@ class HRBinOp(AST):
|
|
|
476
564
|
op: str
|
|
477
565
|
right: DefIdentifier
|
|
478
566
|
|
|
567
|
+
__eq__ = AST.ast_equality
|
|
568
|
+
|
|
479
569
|
|
|
480
570
|
@dataclass
|
|
481
571
|
class HRUnOp(AST):
|
|
@@ -487,6 +577,8 @@ class HRUnOp(AST):
|
|
|
487
577
|
op: str
|
|
488
578
|
operand: DefIdentifier
|
|
489
579
|
|
|
580
|
+
__eq__ = AST.ast_equality
|
|
581
|
+
|
|
490
582
|
|
|
491
583
|
# TODO: Unify HRule and DPRule?
|
|
492
584
|
@dataclass
|
|
@@ -497,8 +589,10 @@ class HRule(AST):
|
|
|
497
589
|
|
|
498
590
|
name: Optional[str]
|
|
499
591
|
rule: HRBinOp
|
|
500
|
-
erCode: Optional[
|
|
501
|
-
erLevel: Optional[
|
|
592
|
+
erCode: Optional[str]
|
|
593
|
+
erLevel: Optional[int]
|
|
594
|
+
|
|
595
|
+
__eq__ = AST.ast_equality
|
|
502
596
|
|
|
503
597
|
|
|
504
598
|
@dataclass
|
|
@@ -509,8 +603,10 @@ class DPRule(AST):
|
|
|
509
603
|
|
|
510
604
|
name: Optional[str]
|
|
511
605
|
rule: HRBinOp
|
|
512
|
-
erCode: Optional[
|
|
513
|
-
erLevel: Optional[
|
|
606
|
+
erCode: Optional[str]
|
|
607
|
+
erLevel: Optional[int]
|
|
608
|
+
|
|
609
|
+
__eq__ = AST.ast_equality
|
|
514
610
|
|
|
515
611
|
|
|
516
612
|
# TODO: Unify HRuleset and DPRuleset?
|
|
@@ -525,6 +621,8 @@ class HRuleset(AST):
|
|
|
525
621
|
element: DefIdentifier
|
|
526
622
|
rules: List[HRule]
|
|
527
623
|
|
|
624
|
+
__eq__ = AST.ast_equality
|
|
625
|
+
|
|
528
626
|
|
|
529
627
|
@dataclass
|
|
530
628
|
class DPRuleset(AST):
|
|
@@ -537,6 +635,8 @@ class DPRuleset(AST):
|
|
|
537
635
|
params: Union[DefIdentifier, list]
|
|
538
636
|
rules: List[DPRule]
|
|
539
637
|
|
|
638
|
+
__eq__ = AST.ast_equality
|
|
639
|
+
|
|
540
640
|
|
|
541
641
|
@dataclass
|
|
542
642
|
class EvalOp(AST):
|
|
@@ -548,9 +648,11 @@ class EvalOp(AST):
|
|
|
548
648
|
|
|
549
649
|
name: str
|
|
550
650
|
operands: List[AST]
|
|
551
|
-
output: Optional[
|
|
651
|
+
output: Optional[Dataset]
|
|
552
652
|
language: Optional[str]
|
|
553
653
|
|
|
654
|
+
__eq__ = AST.ast_equality
|
|
655
|
+
|
|
554
656
|
|
|
555
657
|
@dataclass
|
|
556
658
|
class NoOp(AST):
|
|
@@ -559,3 +661,14 @@ class NoOp(AST):
|
|
|
559
661
|
"""
|
|
560
662
|
|
|
561
663
|
pass
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
@dataclass
|
|
667
|
+
class ParFunction(AST):
|
|
668
|
+
"""
|
|
669
|
+
ParFunction: (operand)
|
|
670
|
+
"""
|
|
671
|
+
|
|
672
|
+
operand: AST
|
|
673
|
+
|
|
674
|
+
__eq__ = AST.ast_equality
|
vtlengine/Exceptions/messages.py
CHANGED
|
@@ -15,6 +15,13 @@ centralised_messages = {
|
|
|
15
15
|
"0-1-2-3": "Component {component} is duplicated.",
|
|
16
16
|
"0-1-2-4": "Invalid json structure because {err} on file {filename}.",
|
|
17
17
|
"0-1-2-5": "File {file} must be encoded in utf-8 (without BOM).",
|
|
18
|
+
# Run SDMX errors
|
|
19
|
+
"0-1-3-1": "Expected exactly one input dataset in the whole script, found: {number_datasets}",
|
|
20
|
+
"0-1-3-2": "SDMX Dataset {schema} requires to have a Schema object defined as structure",
|
|
21
|
+
"0-1-3-3": "If no mappings are provided, only one dataset is allowed.",
|
|
22
|
+
"0-1-3-4": "Dataset {short_urn} not found in mapping dictionary.",
|
|
23
|
+
"0-1-3-5": "Dataset {dataset_name} not found in the input datasets.",
|
|
24
|
+
"0-1-3-6": "Input name {missing} not found in the input datasets.",
|
|
18
25
|
# JSON Schema validations
|
|
19
26
|
"0-3-1-1": "Dataset {dataset} is not valid according to JSON schema",
|
|
20
27
|
# Infer Data Structure errors
|
|
@@ -206,6 +213,8 @@ centralised_messages = {
|
|
|
206
213
|
"1-1-19-9": "At op {op}: {op} can only be applied to a {comp_type} with a {param}",
|
|
207
214
|
# New Unary time operators
|
|
208
215
|
"1-1-19-10": "{op} can only be applied to operands with data type as Date or Time Period",
|
|
216
|
+
"1-1-19-11": "The time aggregation operand has to be "
|
|
217
|
+
"defined if not used inside an aggregation.",
|
|
209
218
|
# Other time operators
|
|
210
219
|
"2-1-19-1": "At op {op}: Invalid values {value_1} and {value_2} for duration, "
|
|
211
220
|
"periodIndTo parameter must be a larger duration value than the "
|
|
@@ -8,9 +8,10 @@ import pandas as pd
|
|
|
8
8
|
import vtlengine.AST as AST
|
|
9
9
|
import vtlengine.Exceptions
|
|
10
10
|
import vtlengine.Operators as Operators
|
|
11
|
+
from vtlengine.AST import VarID
|
|
11
12
|
from vtlengine.AST.ASTTemplate import ASTTemplate
|
|
12
13
|
from vtlengine.AST.DAG import HRDAGAnalyzer
|
|
13
|
-
from vtlengine.AST.DAG._words import DELETE, GLOBAL, INSERT
|
|
14
|
+
from vtlengine.AST.DAG._words import DELETE, GLOBAL, INSERT, PERSISTENT
|
|
14
15
|
from vtlengine.AST.Grammar.tokens import (
|
|
15
16
|
AGGREGATE,
|
|
16
17
|
ALL,
|
|
@@ -113,6 +114,8 @@ class InterpreterAnalyzer(ASTTemplate):
|
|
|
113
114
|
output_path: Optional[Union[str, Path]] = None
|
|
114
115
|
# Time Period Representation
|
|
115
116
|
time_period_representation: Optional[TimePeriodRepresentation] = None
|
|
117
|
+
# Return only persistent
|
|
118
|
+
return_only_persistent: bool = True
|
|
116
119
|
# Flags to change behavior
|
|
117
120
|
nested_condition: Union[str, bool] = False
|
|
118
121
|
is_from_assignment: bool = False
|
|
@@ -188,7 +191,9 @@ class InterpreterAnalyzer(ASTTemplate):
|
|
|
188
191
|
# We do not save global input datasets, only results of transformations
|
|
189
192
|
self.datasets[ds_name].data = None
|
|
190
193
|
continue
|
|
191
|
-
|
|
194
|
+
if self.return_only_persistent and ds_name not in self.ds_analysis[PERSISTENT]:
|
|
195
|
+
self.datasets[ds_name].data = None
|
|
196
|
+
continue
|
|
192
197
|
# Saving only datasets, no scalars
|
|
193
198
|
save_datapoints(
|
|
194
199
|
self.time_period_representation,
|
|
@@ -335,7 +340,7 @@ class InterpreterAnalyzer(ASTTemplate):
|
|
|
335
340
|
for i, rule in enumerate(node.rules):
|
|
336
341
|
rule.name = (i + 1).__str__()
|
|
337
342
|
|
|
338
|
-
cond_comp = []
|
|
343
|
+
cond_comp: List[Any] = []
|
|
339
344
|
if isinstance(node.element, list):
|
|
340
345
|
cond_comp = [x.value for x in node.element[:-1]]
|
|
341
346
|
node.element = node.element[-1]
|
|
@@ -369,6 +374,9 @@ class InterpreterAnalyzer(ASTTemplate):
|
|
|
369
374
|
def visit_PersistentAssignment(self, node: AST.PersistentAssignment) -> Any:
|
|
370
375
|
return self.visit_Assignment(node)
|
|
371
376
|
|
|
377
|
+
def visit_ParFunction(self, node: AST.ParFunction) -> Any:
|
|
378
|
+
return self.visit(node.operand)
|
|
379
|
+
|
|
372
380
|
def visit_BinOp(self, node: AST.BinOp) -> Any:
|
|
373
381
|
is_from_if = False
|
|
374
382
|
if (
|
|
@@ -390,7 +398,13 @@ class InterpreterAnalyzer(ASTTemplate):
|
|
|
390
398
|
comp_name = f"{node.left.value}#{self.udo_params[-1][node.right.value]}"
|
|
391
399
|
else:
|
|
392
400
|
comp_name = f"{node.left.value}#{node.right.value}"
|
|
393
|
-
ast_var_id = AST.VarID(
|
|
401
|
+
ast_var_id = AST.VarID(
|
|
402
|
+
value=comp_name,
|
|
403
|
+
line_start=node.right.line_start,
|
|
404
|
+
line_stop=node.right.line_stop,
|
|
405
|
+
column_start=node.right.column_start,
|
|
406
|
+
column_stop=node.right.column_stop,
|
|
407
|
+
)
|
|
394
408
|
return self.visit(ast_var_id)
|
|
395
409
|
left_operand = self.visit(node.left)
|
|
396
410
|
right_operand = self.visit(node.right)
|
|
@@ -572,6 +586,8 @@ class InterpreterAnalyzer(ASTTemplate):
|
|
|
572
586
|
for comp_name in node.partition_by:
|
|
573
587
|
if comp_name in self.udo_params[-1]:
|
|
574
588
|
partitioning.append(self.udo_params[-1][comp_name])
|
|
589
|
+
elif comp_name in operand.get_identifiers_names():
|
|
590
|
+
partitioning.append(comp_name)
|
|
575
591
|
else:
|
|
576
592
|
raise SemanticError(
|
|
577
593
|
"2-3-9",
|
|
@@ -583,7 +599,7 @@ class InterpreterAnalyzer(ASTTemplate):
|
|
|
583
599
|
for o in node.order_by:
|
|
584
600
|
if o.component in self.udo_params[-1]:
|
|
585
601
|
o.component = self.udo_params[-1][o.component]
|
|
586
|
-
|
|
602
|
+
elif o.component not in operand.get_identifiers_names():
|
|
587
603
|
raise SemanticError(
|
|
588
604
|
"2-3-9",
|
|
589
605
|
comp_type="Component",
|
|
@@ -778,7 +794,7 @@ class InterpreterAnalyzer(ASTTemplate):
|
|
|
778
794
|
)
|
|
779
795
|
elif is_partial_present == 2:
|
|
780
796
|
raise SemanticError("1-1-13-9", comp_name=node.value)
|
|
781
|
-
node.value = found_comp
|
|
797
|
+
node.value = found_comp # type:ignore[assignment]
|
|
782
798
|
if node.value not in self.regular_aggregation_dataset.components:
|
|
783
799
|
raise SemanticError(
|
|
784
800
|
"1-1-1-10",
|
|
@@ -1273,6 +1289,10 @@ class InterpreterAnalyzer(ASTTemplate):
|
|
|
1273
1289
|
signature_type=hr_info["node"].signature_type,
|
|
1274
1290
|
element=hr_info["node"].element,
|
|
1275
1291
|
rules=aux,
|
|
1292
|
+
line_start=node.line_start,
|
|
1293
|
+
line_stop=node.line_stop,
|
|
1294
|
+
column_start=node.column_start,
|
|
1295
|
+
column_stop=node.column_stop,
|
|
1276
1296
|
)
|
|
1277
1297
|
HRDAGAnalyzer().visit(hierarchy_ast)
|
|
1278
1298
|
|
|
@@ -1580,8 +1600,19 @@ class InterpreterAnalyzer(ASTTemplate):
|
|
|
1580
1600
|
)
|
|
1581
1601
|
}
|
|
1582
1602
|
)
|
|
1603
|
+
|
|
1604
|
+
if self.condition_stack and len(self.condition_stack) > 0:
|
|
1605
|
+
last_condition_dataset = (
|
|
1606
|
+
self.then_condition_dataset[-1]
|
|
1607
|
+
if self.condition_stack[-1] == THEN_ELSE["then"]
|
|
1608
|
+
else (self.else_condition_dataset[-1])
|
|
1609
|
+
)
|
|
1610
|
+
measure_name = last_condition_dataset.get_measures_names()[0]
|
|
1611
|
+
then_data = then_data[then_data[name].isin(last_condition_dataset.data[measure_name])]
|
|
1612
|
+
else_data = else_data[else_data[name].isin(last_condition_dataset.data[measure_name])]
|
|
1583
1613
|
then_dataset = Dataset(name=name, components=components, data=then_data)
|
|
1584
1614
|
else_dataset = Dataset(name=name, components=components, data=else_data)
|
|
1615
|
+
|
|
1585
1616
|
self.then_condition_dataset.append(then_dataset)
|
|
1586
1617
|
self.else_condition_dataset.append(else_dataset)
|
|
1587
1618
|
|
|
@@ -1592,11 +1623,13 @@ class InterpreterAnalyzer(ASTTemplate):
|
|
|
1592
1623
|
or self.condition_stack is None
|
|
1593
1624
|
):
|
|
1594
1625
|
return left_operand, right_operand
|
|
1626
|
+
|
|
1595
1627
|
merge_dataset = (
|
|
1596
1628
|
self.then_condition_dataset.pop()
|
|
1597
1629
|
if self.condition_stack.pop() == THEN_ELSE["then"]
|
|
1598
1630
|
else (self.else_condition_dataset.pop())
|
|
1599
1631
|
)
|
|
1632
|
+
|
|
1600
1633
|
merge_index = merge_dataset.data[merge_dataset.get_measures_names()[0]].to_list()
|
|
1601
1634
|
ids = merge_dataset.get_identifiers_names()
|
|
1602
1635
|
if isinstance(left_operand, (Dataset, DataComponent)):
|
|
@@ -1873,8 +1906,20 @@ class InterpreterAnalyzer(ASTTemplate):
|
|
|
1873
1906
|
return result
|
|
1874
1907
|
|
|
1875
1908
|
def visit_TimeAggregation(self, node: AST.TimeAggregation) -> None:
|
|
1876
|
-
|
|
1877
|
-
|
|
1909
|
+
if node.operand is not None:
|
|
1910
|
+
operand = self.visit(node.operand)
|
|
1911
|
+
else:
|
|
1912
|
+
if self.aggregation_dataset is None:
|
|
1913
|
+
raise SemanticError("1-1-19-11")
|
|
1914
|
+
component_name = Time_Aggregation._get_time_id(self.aggregation_dataset)
|
|
1915
|
+
ast_operand = VarID(
|
|
1916
|
+
value=component_name,
|
|
1917
|
+
line_start=node.line_start,
|
|
1918
|
+
line_stop=node.line_stop,
|
|
1919
|
+
column_start=node.column_start,
|
|
1920
|
+
column_stop=node.column_stop,
|
|
1921
|
+
)
|
|
1922
|
+
operand = self.visit(ast_operand)
|
|
1878
1923
|
return Time_Aggregation.analyze(
|
|
1879
1924
|
operand=operand,
|
|
1880
1925
|
period_from=node.period_from,
|