vtlengine 1.0__py3-none-any.whl → 1.0.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 +153 -100
- vtlengine/API/__init__.py +109 -67
- vtlengine/AST/ASTConstructor.py +188 -98
- vtlengine/AST/ASTConstructorModules/Expr.py +306 -200
- vtlengine/AST/ASTConstructorModules/ExprComponents.py +172 -102
- vtlengine/AST/ASTConstructorModules/Terminals.py +158 -95
- vtlengine/AST/ASTEncoders.py +1 -1
- vtlengine/AST/ASTTemplate.py +8 -9
- vtlengine/AST/ASTVisitor.py +8 -12
- vtlengine/AST/DAG/__init__.py +43 -35
- vtlengine/AST/DAG/_words.py +4 -4
- vtlengine/AST/Grammar/lexer.py +732 -142
- vtlengine/AST/Grammar/parser.py +2188 -826
- vtlengine/AST/Grammar/tokens.py +128 -128
- vtlengine/AST/VtlVisitor.py +7 -4
- vtlengine/AST/__init__.py +22 -11
- vtlengine/DataTypes/NumericTypesHandling.py +5 -4
- vtlengine/DataTypes/TimeHandling.py +194 -301
- vtlengine/DataTypes/__init__.py +304 -218
- vtlengine/Exceptions/__init__.py +52 -27
- vtlengine/Exceptions/messages.py +134 -62
- vtlengine/Interpreter/__init__.py +781 -487
- vtlengine/Model/__init__.py +165 -121
- vtlengine/Operators/Aggregation.py +156 -95
- vtlengine/Operators/Analytic.py +115 -59
- vtlengine/Operators/Assignment.py +7 -4
- vtlengine/Operators/Boolean.py +27 -32
- vtlengine/Operators/CastOperator.py +177 -131
- vtlengine/Operators/Clause.py +137 -99
- vtlengine/Operators/Comparison.py +148 -117
- vtlengine/Operators/Conditional.py +149 -98
- vtlengine/Operators/General.py +68 -47
- vtlengine/Operators/HROperators.py +91 -72
- vtlengine/Operators/Join.py +217 -118
- vtlengine/Operators/Numeric.py +89 -44
- vtlengine/Operators/RoleSetter.py +16 -15
- vtlengine/Operators/Set.py +61 -36
- vtlengine/Operators/String.py +213 -139
- vtlengine/Operators/Time.py +334 -216
- vtlengine/Operators/Validation.py +117 -76
- vtlengine/Operators/__init__.py +340 -213
- vtlengine/Utils/__init__.py +195 -40
- vtlengine/__init__.py +1 -1
- vtlengine/files/output/__init__.py +15 -6
- vtlengine/files/output/_time_period_representation.py +10 -9
- vtlengine/files/parser/__init__.py +77 -52
- vtlengine/files/parser/_rfc_dialect.py +6 -5
- vtlengine/files/parser/_time_checking.py +46 -37
- vtlengine-1.0.1.dist-info/METADATA +236 -0
- vtlengine-1.0.1.dist-info/RECORD +58 -0
- {vtlengine-1.0.dist-info → vtlengine-1.0.1.dist-info}/WHEEL +1 -1
- vtlengine-1.0.dist-info/METADATA +0 -104
- vtlengine-1.0.dist-info/RECORD +0 -58
- {vtlengine-1.0.dist-info → vtlengine-1.0.1.dist-info}/LICENSE.md +0 -0
|
@@ -1,21 +1,36 @@
|
|
|
1
|
-
from vtlengine.DataTypes import
|
|
2
|
-
|
|
1
|
+
from vtlengine.DataTypes import (
|
|
2
|
+
Boolean,
|
|
3
|
+
Date,
|
|
4
|
+
Duration,
|
|
5
|
+
Integer,
|
|
6
|
+
Number,
|
|
7
|
+
String,
|
|
8
|
+
TimeInterval,
|
|
9
|
+
TimePeriod,
|
|
10
|
+
)
|
|
3
11
|
from antlr4.tree.Tree import TerminalNodeImpl
|
|
4
12
|
|
|
5
|
-
from vtlengine.AST import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
13
|
+
from vtlengine.AST import (
|
|
14
|
+
BinOp,
|
|
15
|
+
Collection,
|
|
16
|
+
Constant,
|
|
17
|
+
DPRIdentifier,
|
|
18
|
+
Identifier,
|
|
19
|
+
OrderBy,
|
|
20
|
+
ParamConstant,
|
|
21
|
+
ParamOp,
|
|
22
|
+
VarID,
|
|
23
|
+
Windowing,
|
|
24
|
+
)
|
|
10
25
|
from vtlengine.AST.Grammar.parser import Parser
|
|
11
26
|
from vtlengine.AST.VtlVisitor import VtlVisitor
|
|
12
27
|
from vtlengine.Model import Component, Dataset, Role, Scalar
|
|
13
28
|
|
|
14
29
|
|
|
15
30
|
def _remove_scaped_characters(text):
|
|
16
|
-
has_scaped_char = text.find("
|
|
31
|
+
has_scaped_char = text.find("'") != -1
|
|
17
32
|
if has_scaped_char:
|
|
18
|
-
text = str(text.replace("
|
|
33
|
+
text = str(text.replace("'", ""))
|
|
19
34
|
return text
|
|
20
35
|
|
|
21
36
|
|
|
@@ -24,24 +39,24 @@ class Terminals(VtlVisitor):
|
|
|
24
39
|
token = ctx.children[0].getSymbol()
|
|
25
40
|
|
|
26
41
|
if token.type == Parser.INTEGER_CONSTANT:
|
|
27
|
-
constant_node = Constant(
|
|
42
|
+
constant_node = Constant("INTEGER_CONSTANT", int(token.text))
|
|
28
43
|
|
|
29
44
|
elif token.type == Parser.NUMBER_CONSTANT:
|
|
30
|
-
constant_node = Constant(
|
|
45
|
+
constant_node = Constant("FLOAT_CONSTANT", float(token.text))
|
|
31
46
|
|
|
32
47
|
elif token.type == Parser.BOOLEAN_CONSTANT:
|
|
33
|
-
if token.text ==
|
|
34
|
-
constant_node = Constant(
|
|
35
|
-
elif token.text ==
|
|
36
|
-
constant_node = Constant(
|
|
48
|
+
if token.text == "true":
|
|
49
|
+
constant_node = Constant("BOOLEAN_CONSTANT", True)
|
|
50
|
+
elif token.text == "false":
|
|
51
|
+
constant_node = Constant("BOOLEAN_CONSTANT", False)
|
|
37
52
|
else:
|
|
38
53
|
raise NotImplementedError
|
|
39
54
|
|
|
40
55
|
elif token.type == Parser.STRING_CONSTANT:
|
|
41
|
-
constant_node = Constant(
|
|
56
|
+
constant_node = Constant("STRING_CONSTANT", token.text[1:-1])
|
|
42
57
|
|
|
43
58
|
elif token.type == Parser.NULL_CONSTANT:
|
|
44
|
-
constant_node = Constant(
|
|
59
|
+
constant_node = Constant("NULL_CONSTANT", None)
|
|
45
60
|
|
|
46
61
|
else:
|
|
47
62
|
raise NotImplementedError
|
|
@@ -73,26 +88,30 @@ class Terminals(VtlVisitor):
|
|
|
73
88
|
# check token text
|
|
74
89
|
token.text = _remove_scaped_characters(token.text)
|
|
75
90
|
|
|
76
|
-
return Identifier(token.text,
|
|
91
|
+
return Identifier(token.text, "ComponentID")
|
|
77
92
|
|
|
78
93
|
def visitComponentID(self, ctx: Parser.ComponentIDContext):
|
|
79
94
|
ctx_list = list(ctx.getChildren())
|
|
80
95
|
|
|
81
96
|
if len(ctx_list) == 1:
|
|
82
97
|
component_name = ctx_list[0].getSymbol().text
|
|
83
|
-
if component_name.startswith("
|
|
84
|
-
|
|
98
|
+
if component_name.startswith("'") and component_name.endswith(
|
|
99
|
+
"'"
|
|
100
|
+
): # The component could be imbalance, errorcode or errorlevel
|
|
85
101
|
component_name = component_name[1:-1]
|
|
86
|
-
return Identifier(component_name,
|
|
102
|
+
return Identifier(component_name, "ComponentID")
|
|
87
103
|
else:
|
|
88
104
|
component_name = ctx_list[2].getSymbol().text
|
|
89
|
-
if component_name.startswith("
|
|
90
|
-
|
|
105
|
+
if component_name.startswith("'") and component_name.endswith(
|
|
106
|
+
"'"
|
|
107
|
+
): # The component could be imbalance, errorcode or errorlevel
|
|
91
108
|
component_name = component_name[1:-1]
|
|
92
109
|
op_node = ctx_list[1].getSymbol().text
|
|
93
|
-
return BinOp(
|
|
94
|
-
|
|
95
|
-
|
|
110
|
+
return BinOp(
|
|
111
|
+
left=Identifier(ctx_list[0].getSymbol().text, "DatasetID"),
|
|
112
|
+
op=op_node,
|
|
113
|
+
right=Identifier(component_name, "ComponentID"),
|
|
114
|
+
)
|
|
96
115
|
|
|
97
116
|
def visitOperatorID(self, ctx: Parser.OperatorIDContext):
|
|
98
117
|
"""
|
|
@@ -107,8 +126,9 @@ class Terminals(VtlVisitor):
|
|
|
107
126
|
"""
|
|
108
127
|
valueDomainID: IDENTIFIER ;
|
|
109
128
|
"""
|
|
110
|
-
return Collection(
|
|
111
|
-
|
|
129
|
+
return Collection(
|
|
130
|
+
name=ctx.children[0].getSymbol().text, children=[], kind="ValueDomain", type=""
|
|
131
|
+
)
|
|
112
132
|
|
|
113
133
|
def visitRulesetID(self, ctx: Parser.RulesetIDContext):
|
|
114
134
|
"""
|
|
@@ -126,8 +146,9 @@ class Terminals(VtlVisitor):
|
|
|
126
146
|
ctx_list = list(ctx.getChildren())
|
|
127
147
|
# AST_ASTCONSTRUCTOR.48
|
|
128
148
|
raise NotImplementedError(
|
|
129
|
-
|
|
130
|
-
|
|
149
|
+
"Value Domain '{}' not available for cast operator or scalar type "
|
|
150
|
+
"representation or rulesets.".format(ctx_list[0].getSymbol().text)
|
|
151
|
+
)
|
|
131
152
|
|
|
132
153
|
def visitValueDomainValue(self, ctx: Parser.ValueDomainValueContext):
|
|
133
154
|
return _remove_scaped_characters(ctx.children[0].getSymbol().text)
|
|
@@ -205,9 +226,9 @@ class Terminals(VtlVisitor):
|
|
|
205
226
|
"""
|
|
206
227
|
viralAttribute: VIRAL ATTRIBUTE;
|
|
207
228
|
"""
|
|
208
|
-
ctx_list = list(ctx.getChildren())
|
|
209
|
-
c = ctx_list[0]
|
|
210
|
-
token = c.getSymbol()
|
|
229
|
+
# ctx_list = list(ctx.getChildren())
|
|
230
|
+
# c = ctx_list[0]
|
|
231
|
+
# token = c.getSymbol()
|
|
211
232
|
|
|
212
233
|
raise NotImplementedError
|
|
213
234
|
|
|
@@ -222,7 +243,8 @@ class Terminals(VtlVisitor):
|
|
|
222
243
|
scalars = [scalar for scalar in ctx_list if isinstance(scalar, Parser.SimpleScalarContext)]
|
|
223
244
|
|
|
224
245
|
scalars_with_cast = [
|
|
225
|
-
scalar for scalar in ctx_list if isinstance(scalar, Parser.ScalarWithCastContext)
|
|
246
|
+
scalar for scalar in ctx_list if isinstance(scalar, Parser.ScalarWithCastContext)
|
|
247
|
+
]
|
|
226
248
|
|
|
227
249
|
for scalar in scalars:
|
|
228
250
|
scalar_nodes.append(self.visitSimpleScalar(scalar))
|
|
@@ -230,7 +252,7 @@ class Terminals(VtlVisitor):
|
|
|
230
252
|
for scalar_with_cast in scalars_with_cast:
|
|
231
253
|
scalar_nodes.append(self.visitScalarWithCast(scalar_with_cast))
|
|
232
254
|
|
|
233
|
-
return Collection(
|
|
255
|
+
return Collection("Set", None, scalar_nodes)
|
|
234
256
|
|
|
235
257
|
def visitMultModifier(self, ctx: Parser.MultModifierContext):
|
|
236
258
|
"""
|
|
@@ -244,12 +266,21 @@ class Terminals(VtlVisitor):
|
|
|
244
266
|
"""
|
|
245
267
|
ctx_list = list(ctx.getChildren())
|
|
246
268
|
|
|
247
|
-
component_node = [
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
269
|
+
component_node = [
|
|
270
|
+
self.visitComponentType(component)
|
|
271
|
+
for component in ctx_list
|
|
272
|
+
if isinstance(component, Parser.ComponentTypeContext)
|
|
273
|
+
]
|
|
274
|
+
component_name = [
|
|
275
|
+
self.visitComponentID(component).value
|
|
276
|
+
for component in ctx_list
|
|
277
|
+
if isinstance(component, Parser.ComponentIDContext)
|
|
278
|
+
]
|
|
279
|
+
component_mult = [
|
|
280
|
+
self.visitMultModifier(modifier)
|
|
281
|
+
for modifier in ctx_list
|
|
282
|
+
if isinstance(modifier, Parser.MultModifierContext)
|
|
283
|
+
]
|
|
253
284
|
|
|
254
285
|
if len(component_mult) != 0:
|
|
255
286
|
# AST_ASTCONSTRUCTOR.51
|
|
@@ -272,17 +303,28 @@ class Terminals(VtlVisitor):
|
|
|
272
303
|
"""
|
|
273
304
|
ctx_list = list(ctx.getChildren())
|
|
274
305
|
|
|
275
|
-
types = (
|
|
276
|
-
|
|
306
|
+
types = (
|
|
307
|
+
Parser.BasicScalarTypeContext,
|
|
308
|
+
Parser.ValueDomainNameContext,
|
|
309
|
+
Parser.ScalarTypeConstraintContext,
|
|
310
|
+
)
|
|
277
311
|
scalartype = [scalartype for scalartype in ctx_list if isinstance(scalartype, types)][0]
|
|
278
312
|
|
|
279
|
-
scalartype_constraint = [
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
313
|
+
scalartype_constraint = [
|
|
314
|
+
constraint
|
|
315
|
+
for constraint in ctx_list
|
|
316
|
+
if isinstance(constraint, Parser.ScalarTypeConstraintContext)
|
|
317
|
+
]
|
|
318
|
+
not_ = [
|
|
319
|
+
not_.getSymbol().text
|
|
320
|
+
for not_ in ctx_list
|
|
321
|
+
if isinstance(not_, TerminalNodeImpl) and not_.getSymbol().type == Parser.NOT
|
|
322
|
+
]
|
|
323
|
+
null_constant = [
|
|
324
|
+
null.getSymbol().text
|
|
325
|
+
for null in ctx_list
|
|
326
|
+
if isinstance(null, TerminalNodeImpl) and null.getSymbol().type == Parser.NULL_CONSTANT
|
|
327
|
+
]
|
|
286
328
|
|
|
287
329
|
if isinstance(scalartype, Parser.BasicScalarTypeContext):
|
|
288
330
|
if scalartype.children[0].getSymbol().type == Parser.SCALAR:
|
|
@@ -315,8 +357,11 @@ class Terminals(VtlVisitor):
|
|
|
315
357
|
"""
|
|
316
358
|
ctx_list = list(ctx.getChildren())
|
|
317
359
|
|
|
318
|
-
components = [
|
|
319
|
-
|
|
360
|
+
components = [
|
|
361
|
+
self.visitCompConstraint(constraint)
|
|
362
|
+
for constraint in ctx_list
|
|
363
|
+
if isinstance(constraint, Parser.CompConstraintContext)
|
|
364
|
+
]
|
|
320
365
|
components = {component.name: component for component in components}
|
|
321
366
|
|
|
322
367
|
return Dataset(name="Dataset", components=components, data=None)
|
|
@@ -332,9 +377,9 @@ class Terminals(VtlVisitor):
|
|
|
332
377
|
|
|
333
378
|
def visitDpRuleset(self, ctx: Parser.DpRulesetContext):
|
|
334
379
|
"""
|
|
335
|
-
DATAPOINT # dataPoint
|
|
336
|
-
| DATAPOINT_ON_VD (GLPAREN valueDomainName (MUL valueDomainName)* GRPAREN )? # dataPointVd
|
|
337
|
-
| DATAPOINT_ON_VAR (GLPAREN varID (MUL varID)* GRPAREN )? # dataPointVar
|
|
380
|
+
DATAPOINT # dataPoint # noqa E501
|
|
381
|
+
| DATAPOINT_ON_VD (GLPAREN valueDomainName (MUL valueDomainName)* GRPAREN )? # dataPointVd # noqa E501
|
|
382
|
+
| DATAPOINT_ON_VAR (GLPAREN varID (MUL varID)* GRPAREN )? # dataPointVar # noqa E501
|
|
338
383
|
;
|
|
339
384
|
"""
|
|
340
385
|
# AST_ASTCONSTRUCTOR.54
|
|
@@ -342,9 +387,9 @@ class Terminals(VtlVisitor):
|
|
|
342
387
|
|
|
343
388
|
def visitHrRuleset(self, ctx: Parser.HrRulesetContext):
|
|
344
389
|
"""
|
|
345
|
-
hrRuleset: HIERARCHICAL # hrRulesetType
|
|
346
|
-
| HIERARCHICAL_ON_VD ( GLPAREN vdName=IDENTIFIER (LPAREN valueDomainName (MUL valueDomainName)* RPAREN)? GRPAREN )? # hrRulesetVdType
|
|
347
|
-
| HIERARCHICAL_ON_VAR ( GLPAREN varName=varID (LPAREN varID (MUL varID)* RPAREN)? GRPAREN )? # hrRulesetVarType
|
|
390
|
+
hrRuleset: HIERARCHICAL # hrRulesetType # noqa E501
|
|
391
|
+
| HIERARCHICAL_ON_VD ( GLPAREN vdName=IDENTIFIER (LPAREN valueDomainName (MUL valueDomainName)* RPAREN)? GRPAREN )? # hrRulesetVdType # noqa E501
|
|
392
|
+
| HIERARCHICAL_ON_VAR ( GLPAREN varName=varID (LPAREN varID (MUL varID)* RPAREN)? GRPAREN )? # hrRulesetVarType # noqa E501
|
|
348
393
|
;
|
|
349
394
|
"""
|
|
350
395
|
# AST_ASTCONSTRUCTOR.55
|
|
@@ -357,8 +402,11 @@ class Terminals(VtlVisitor):
|
|
|
357
402
|
ctx_list = list(ctx.getChildren())
|
|
358
403
|
|
|
359
404
|
role_node = self.visitComponentRole(ctx_list[0])
|
|
360
|
-
data_type = [
|
|
361
|
-
|
|
405
|
+
data_type = [
|
|
406
|
+
self.visitScalarType(constraint)
|
|
407
|
+
for constraint in ctx_list
|
|
408
|
+
if isinstance(constraint, Parser.ScalarTypeContext)
|
|
409
|
+
]
|
|
362
410
|
if len(data_type) > 0:
|
|
363
411
|
data_type = data_type[0]
|
|
364
412
|
else:
|
|
@@ -454,7 +502,7 @@ class Terminals(VtlVisitor):
|
|
|
454
502
|
|
|
455
503
|
def visitScalarWithCast(self, ctx: Parser.ScalarWithCastContext):
|
|
456
504
|
"""
|
|
457
|
-
|
|
505
|
+
| CAST LPAREN constant COMMA (basicScalarType) (COMMA STRING_CONSTANT)? RPAREN #scalarWithCast # noqa E501
|
|
458
506
|
"""
|
|
459
507
|
ctx_list = list(ctx.getChildren())
|
|
460
508
|
c = ctx_list[0]
|
|
@@ -466,7 +514,7 @@ class Terminals(VtlVisitor):
|
|
|
466
514
|
basic_scalar_type = [self.visitBasicScalarType(ctx_list[4])]
|
|
467
515
|
|
|
468
516
|
if len(ctx_list) > 6:
|
|
469
|
-
param_node = [ParamConstant(
|
|
517
|
+
param_node = [ParamConstant("PARAM_CAST", ctx_list[6])]
|
|
470
518
|
else:
|
|
471
519
|
param_node = []
|
|
472
520
|
|
|
@@ -495,15 +543,15 @@ class Terminals(VtlVisitor):
|
|
|
495
543
|
token = ctx.children[0].getSymbol()
|
|
496
544
|
|
|
497
545
|
if token.type == Parser.BOOLEAN_CONSTANT:
|
|
498
|
-
if token.text ==
|
|
499
|
-
param_constant_node = Constant(
|
|
500
|
-
elif token.text ==
|
|
501
|
-
param_constant_node = Constant(
|
|
546
|
+
if token.text == "true":
|
|
547
|
+
param_constant_node = Constant("BOOLEAN_CONSTANT", True)
|
|
548
|
+
elif token.text == "false":
|
|
549
|
+
param_constant_node = Constant("BOOLEAN_CONSTANT", False)
|
|
502
550
|
else:
|
|
503
551
|
raise NotImplementedError
|
|
504
552
|
|
|
505
553
|
elif token.type == Parser.ALL:
|
|
506
|
-
param_constant_node = ParamConstant(
|
|
554
|
+
param_constant_node = ParamConstant("PARAM_CONSTANT", token.text)
|
|
507
555
|
|
|
508
556
|
else:
|
|
509
557
|
raise NotImplementedError
|
|
@@ -539,7 +587,7 @@ class Terminals(VtlVisitor):
|
|
|
539
587
|
try:
|
|
540
588
|
return str(self.visitConstant(ctx_list[1]).value)
|
|
541
589
|
except Exception:
|
|
542
|
-
raise Exception(f
|
|
590
|
+
raise Exception(f"Error code must be a string, line {ctx_list[1].getSymbol().line}")
|
|
543
591
|
|
|
544
592
|
def visitErLevel(self, ctx: Parser.ErLevelContext):
|
|
545
593
|
"""
|
|
@@ -550,9 +598,9 @@ class Terminals(VtlVisitor):
|
|
|
550
598
|
try:
|
|
551
599
|
return int(self.visitConstant(ctx_list[1]).value)
|
|
552
600
|
except Exception:
|
|
553
|
-
raise Exception(f
|
|
601
|
+
raise Exception(f"Error level must be an integer, line {ctx_list[1].start.line}")
|
|
554
602
|
|
|
555
|
-
def visitSignature(self, ctx: Parser.SignatureContext, kind=
|
|
603
|
+
def visitSignature(self, ctx: Parser.SignatureContext, kind="ComponentID"):
|
|
556
604
|
"""
|
|
557
605
|
varID (AS alias)?
|
|
558
606
|
"""
|
|
@@ -577,8 +625,9 @@ class Terminals(VtlVisitor):
|
|
|
577
625
|
def visitConditionClause(self, ctx: Parser.ConditionClauseContext):
|
|
578
626
|
ctx_list = list(ctx.getChildren())
|
|
579
627
|
|
|
580
|
-
components = [
|
|
581
|
-
|
|
628
|
+
components = [
|
|
629
|
+
self.visitComponentID(c) for c in ctx_list if isinstance(c, Parser.ComponentIDContext)
|
|
630
|
+
]
|
|
582
631
|
|
|
583
632
|
return components
|
|
584
633
|
|
|
@@ -604,21 +653,25 @@ class Terminals(VtlVisitor):
|
|
|
604
653
|
def visitPartitionByClause(self, ctx: Parser.PartitionByClauseContext):
|
|
605
654
|
ctx_list = list(ctx.getChildren())
|
|
606
655
|
|
|
607
|
-
return [
|
|
608
|
-
|
|
656
|
+
return [
|
|
657
|
+
self.visitComponentID(compID).value
|
|
658
|
+
for compID in ctx_list
|
|
659
|
+
if isinstance(compID, Parser.ComponentIDContext)
|
|
660
|
+
]
|
|
609
661
|
|
|
610
662
|
def visitOrderByClause(self, ctx: Parser.OrderByClauseContext):
|
|
611
663
|
ctx_list = list(ctx.getChildren())
|
|
612
664
|
|
|
613
|
-
return [
|
|
614
|
-
|
|
665
|
+
return [
|
|
666
|
+
self.visitOrderByItem(c) for c in ctx_list if isinstance(c, Parser.OrderByItemContext)
|
|
667
|
+
]
|
|
615
668
|
|
|
616
669
|
def visitWindowingClause(self, ctx: Parser.WindowingClauseContext):
|
|
617
670
|
ctx_list = list(ctx.getChildren())
|
|
618
671
|
|
|
619
672
|
win_mode = ctx_list[0].getSymbol().text # Windowing mode (data points | range )
|
|
620
673
|
|
|
621
|
-
if win_mode ==
|
|
674
|
+
if win_mode == "data":
|
|
622
675
|
num_rows_1, mode_1 = self.visitLimitClauseItem(ctx_list[3])
|
|
623
676
|
num_rows_2, mode_2 = self.visitLimitClauseItem(ctx_list[5])
|
|
624
677
|
else:
|
|
@@ -628,23 +681,31 @@ class Terminals(VtlVisitor):
|
|
|
628
681
|
first = num_rows_1 # unbounded (default value)
|
|
629
682
|
second = num_rows_2 # current data point (default value)
|
|
630
683
|
|
|
631
|
-
if mode_2 ==
|
|
632
|
-
if
|
|
684
|
+
if mode_2 == "preceding":
|
|
685
|
+
if (
|
|
686
|
+
mode_1 == "preceding" and num_rows_1 == -1 and num_rows_2 == -1
|
|
687
|
+
): # preceding and preceding (error)
|
|
633
688
|
raise Exception(
|
|
634
|
-
f
|
|
689
|
+
f"Cannot have 2 preceding clauses with unbounded in analytic clause, "
|
|
690
|
+
f"line {ctx_list[3].start.line}"
|
|
691
|
+
)
|
|
635
692
|
|
|
636
|
-
if
|
|
693
|
+
if (
|
|
694
|
+
mode_1 == "following" and num_rows_1 == -1 and num_rows_2 == -1
|
|
695
|
+
): # following and following (error)
|
|
637
696
|
raise Exception(
|
|
638
|
-
f
|
|
697
|
+
f"Cannot have 2 following clauses with unbounded in analytic clause, "
|
|
698
|
+
f"line {ctx_list[3].start.line}"
|
|
699
|
+
)
|
|
639
700
|
|
|
640
701
|
if mode_1 == mode_2:
|
|
641
|
-
if mode_1 ==
|
|
702
|
+
if mode_1 == "preceding" and first != -1 and second > first: # 3 and 1: must be [-3:-1]
|
|
642
703
|
return create_windowing(win_mode, [second, first], [mode_2, mode_1])
|
|
643
|
-
if mode_1 ==
|
|
704
|
+
if mode_1 == "preceding" and second == -1:
|
|
644
705
|
return create_windowing(win_mode, [second, first], [mode_2, mode_1])
|
|
645
|
-
if mode_1 ==
|
|
706
|
+
if mode_1 == "following" and second != -1 and second < first: # 3 and 1: must be [1:3]
|
|
646
707
|
return create_windowing(win_mode, [second, first], [mode_2, mode_1])
|
|
647
|
-
if mode_1 ==
|
|
708
|
+
if mode_1 == "following" and first == -1:
|
|
648
709
|
return create_windowing(win_mode, [second, first], [mode_2, mode_1])
|
|
649
710
|
|
|
650
711
|
return create_windowing(win_mode, [first, second], [mode_1, mode_2])
|
|
@@ -653,25 +714,26 @@ class Terminals(VtlVisitor):
|
|
|
653
714
|
ctx_list = list(ctx.getChildren())
|
|
654
715
|
|
|
655
716
|
if len(ctx_list) == 1:
|
|
656
|
-
return OrderBy(component=self.visitComponentID(ctx_list[0]).value,
|
|
657
|
-
order='asc')
|
|
717
|
+
return OrderBy(component=self.visitComponentID(ctx_list[0]).value, order="asc")
|
|
658
718
|
|
|
659
|
-
return OrderBy(
|
|
660
|
-
|
|
719
|
+
return OrderBy(
|
|
720
|
+
component=self.visitComponentID(ctx_list[0]).value, order=ctx_list[1].getSymbol().text
|
|
721
|
+
)
|
|
661
722
|
|
|
662
723
|
def visitLimitClauseItem(self, ctx: Parser.LimitClauseItemContext):
|
|
663
724
|
ctx_list = list(ctx.getChildren())
|
|
664
725
|
c = ctx_list[0]
|
|
665
|
-
if c.getSymbol().text ==
|
|
726
|
+
if c.getSymbol().text == "unbounded":
|
|
666
727
|
result = -1
|
|
667
|
-
elif c.getSymbol().text ==
|
|
728
|
+
elif c.getSymbol().text == "current":
|
|
668
729
|
result = 0
|
|
669
730
|
return result, ctx_list[0].getSymbol().text
|
|
670
731
|
else:
|
|
671
732
|
result = int(c.getSymbol().text)
|
|
672
733
|
if result < 0:
|
|
673
734
|
raise Exception(
|
|
674
|
-
f
|
|
735
|
+
f"Cannot use negative numbers ({result}) on limitClause, line {c.symbol.line}"
|
|
736
|
+
)
|
|
675
737
|
|
|
676
738
|
return result, ctx_list[1].getSymbol().text
|
|
677
739
|
|
|
@@ -683,5 +745,6 @@ def create_windowing(win_mode, values, modes):
|
|
|
683
745
|
elif values[e] == 0:
|
|
684
746
|
values[e] = "CURRENT ROW"
|
|
685
747
|
|
|
686
|
-
return Windowing(
|
|
687
|
-
|
|
748
|
+
return Windowing(
|
|
749
|
+
type_=win_mode, start=values[0], stop=values[1], start_mode=modes[0], stop_mode=modes[1]
|
|
750
|
+
)
|
vtlengine/AST/ASTEncoders.py
CHANGED
vtlengine/AST/ASTTemplate.py
CHANGED
|
@@ -6,6 +6,7 @@ Description
|
|
|
6
6
|
-----------
|
|
7
7
|
Template to start a new visitor for the AST.
|
|
8
8
|
"""
|
|
9
|
+
|
|
9
10
|
from typing import Any
|
|
10
11
|
|
|
11
12
|
import vtlengine.AST as AST
|
|
@@ -168,7 +169,7 @@ class ASTTemplate(NodeVisitor):
|
|
|
168
169
|
"""
|
|
169
170
|
return node.value
|
|
170
171
|
|
|
171
|
-
def visit_ParamConstant(self, node: AST.ParamConstant) ->
|
|
172
|
+
def visit_ParamConstant(self, node: AST.ParamConstant) -> Any:
|
|
172
173
|
"""
|
|
173
174
|
Constant: (type, value)
|
|
174
175
|
|
|
@@ -178,7 +179,7 @@ class ASTTemplate(NodeVisitor):
|
|
|
178
179
|
"""
|
|
179
180
|
return node.value
|
|
180
181
|
|
|
181
|
-
def visit_Identifier(self, node: AST.Identifier) ->
|
|
182
|
+
def visit_Identifier(self, node: AST.Identifier) -> Any:
|
|
182
183
|
"""
|
|
183
184
|
Identifier: (value)
|
|
184
185
|
|
|
@@ -208,7 +209,7 @@ class ASTTemplate(NodeVisitor):
|
|
|
208
209
|
|
|
209
210
|
return node.value
|
|
210
211
|
"""
|
|
211
|
-
if node.value ==
|
|
212
|
+
if node.value == "_":
|
|
212
213
|
return
|
|
213
214
|
return node.value
|
|
214
215
|
|
|
@@ -277,9 +278,7 @@ class ASTTemplate(NodeVisitor):
|
|
|
277
278
|
self.visit(group)
|
|
278
279
|
|
|
279
280
|
def visit_Analytic(self, node: AST.Analytic) -> None:
|
|
280
|
-
"""
|
|
281
|
-
|
|
282
|
-
"""
|
|
281
|
+
""" """
|
|
283
282
|
if node.operand is not None:
|
|
284
283
|
self.visit(node.operand)
|
|
285
284
|
|
|
@@ -301,7 +300,7 @@ class ASTTemplate(NodeVisitor):
|
|
|
301
300
|
if node.operand is not None:
|
|
302
301
|
self.visit(node.operand)
|
|
303
302
|
|
|
304
|
-
def visit_If(self, node: AST.If) ->
|
|
303
|
+
def visit_If(self, node: AST.If) -> Any:
|
|
305
304
|
"""
|
|
306
305
|
If: (condition, thenOp, elseOp)
|
|
307
306
|
|
|
@@ -315,7 +314,7 @@ class ASTTemplate(NodeVisitor):
|
|
|
315
314
|
self.visit(node.thenOp)
|
|
316
315
|
self.visit(node.elseOp)
|
|
317
316
|
|
|
318
|
-
def visit_Validation(self, node: AST.Validation) ->
|
|
317
|
+
def visit_Validation(self, node: AST.Validation) -> Any:
|
|
319
318
|
"""
|
|
320
319
|
Validation: (op, validation, params, inbalance, invalid)
|
|
321
320
|
|
|
@@ -476,7 +475,7 @@ class ASTTemplate(NodeVisitor):
|
|
|
476
475
|
"""
|
|
477
476
|
return node.value
|
|
478
477
|
|
|
479
|
-
def visit_EvalOp(self, node: AST.EvalOp) ->
|
|
478
|
+
def visit_EvalOp(self, node: AST.EvalOp) -> Any:
|
|
480
479
|
"""
|
|
481
480
|
EvalOp: (name, children, output, language)
|
|
482
481
|
|
vtlengine/AST/ASTVisitor.py
CHANGED
|
@@ -7,23 +7,19 @@ Description
|
|
|
7
7
|
Node Dispatcher.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
from typing import Any
|
|
10
11
|
|
|
11
|
-
class NodeVisitor(object):
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
"""
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
class NodeVisitor(object):
|
|
14
|
+
""" """
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
def visit(self, node: Any):
|
|
17
|
+
""" """
|
|
18
|
+
method_name = "visit_" + type(node).__name__
|
|
21
19
|
visitor = getattr(self, method_name, self.generic_visit)
|
|
22
20
|
return visitor(node)
|
|
23
21
|
|
|
24
22
|
def generic_visit(self, node):
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
"""
|
|
23
|
+
""" """
|
|
28
24
|
# AST_ASTVISITOR.1
|
|
29
|
-
raise Exception(
|
|
25
|
+
raise Exception("No visit_{} method".format(type(node).__name__))
|