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,1928 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from copy import copy
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from antlr4.tree.Tree import TerminalNodeImpl
|
|
6
|
+
|
|
7
|
+
from vtlengine.AST import (
|
|
8
|
+
ID,
|
|
9
|
+
Aggregation,
|
|
10
|
+
Analytic,
|
|
11
|
+
Assignment,
|
|
12
|
+
BinOp,
|
|
13
|
+
Case,
|
|
14
|
+
CaseObj,
|
|
15
|
+
Constant,
|
|
16
|
+
EvalOp,
|
|
17
|
+
Identifier,
|
|
18
|
+
If,
|
|
19
|
+
JoinOp,
|
|
20
|
+
MulOp,
|
|
21
|
+
ParamConstant,
|
|
22
|
+
ParamOp,
|
|
23
|
+
ParFunction,
|
|
24
|
+
RegularAggregation,
|
|
25
|
+
RenameNode,
|
|
26
|
+
TimeAggregation,
|
|
27
|
+
UDOCall,
|
|
28
|
+
UnaryOp,
|
|
29
|
+
Validation,
|
|
30
|
+
VarID,
|
|
31
|
+
Windowing,
|
|
32
|
+
)
|
|
33
|
+
from vtlengine.AST.ASTConstructorModules import extract_token_info
|
|
34
|
+
from vtlengine.AST.ASTConstructorModules.ExprComponents import ExprComp
|
|
35
|
+
from vtlengine.AST.ASTConstructorModules.Terminals import Terminals
|
|
36
|
+
from vtlengine.AST.ASTDataExchange import de_ruleset_elements
|
|
37
|
+
from vtlengine.AST.Grammar.parser import Parser
|
|
38
|
+
from vtlengine.AST.Grammar.tokens import DATASET_PRIORITY
|
|
39
|
+
from vtlengine.AST.VtlVisitor import VtlVisitor
|
|
40
|
+
from vtlengine.Exceptions import SemanticError
|
|
41
|
+
from vtlengine.Model import Role
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Expr(VtlVisitor):
|
|
45
|
+
"""______________________________________________________________________________________
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
Expr Definition.
|
|
49
|
+
|
|
50
|
+
_______________________________________________________________________________________
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def visitExpr(self, ctx: Parser.ExprContext):
|
|
54
|
+
"""
|
|
55
|
+
expr:
|
|
56
|
+
LPAREN expr RPAREN # parenthesisExpr
|
|
57
|
+
| functions # functionsExpression
|
|
58
|
+
| dataset=expr QLPAREN clause=datasetClause QRPAREN # clauseExpr
|
|
59
|
+
| expr MEMBERSHIP simpleComponentId # membershipExpr
|
|
60
|
+
| op=(PLUS|MINUS|NOT) right=expr # unaryExpr
|
|
61
|
+
| left=expr op=(MUL|DIV) right=expr # arithmeticExpr
|
|
62
|
+
| left=expr op=(PLUS|MINUS|CONCAT) right=expr # arithmeticExprOrConcat
|
|
63
|
+
| left=expr op=comparisonOperand right=expr # comparisonExpr
|
|
64
|
+
| left=expr op=(IN|NOT_IN)(lists|valueDomainID) # inNotInExpr
|
|
65
|
+
| left=expr op=AND right=expr # booleanExpr
|
|
66
|
+
| left=expr op=(OR|XOR) right=expr # booleanExpr
|
|
67
|
+
| IF conditionalExpr=expr THEN thenExpr=expr ELSE elseExpr=expr # ifExpr
|
|
68
|
+
| CASE WHEN expr THEN expr ELSE expr END # caseExpr
|
|
69
|
+
| constant # constantExpr
|
|
70
|
+
| varID # varIdExpr
|
|
71
|
+
;
|
|
72
|
+
""" # noqa E501
|
|
73
|
+
ctx_list = list(ctx.getChildren())
|
|
74
|
+
c = ctx_list[0]
|
|
75
|
+
|
|
76
|
+
if isinstance(ctx, Parser.ParenthesisExprContext):
|
|
77
|
+
return self.visitParenthesisExpr(ctx)
|
|
78
|
+
|
|
79
|
+
elif isinstance(ctx, Parser.MembershipExprContext):
|
|
80
|
+
return self.visitMembershipExpr(ctx)
|
|
81
|
+
|
|
82
|
+
# dataset=expr QLPAREN clause=datasetClause QRPAREN # clauseExpr
|
|
83
|
+
elif isinstance(ctx, Parser.ClauseExprContext):
|
|
84
|
+
return self.visitClauseExpr(ctx)
|
|
85
|
+
|
|
86
|
+
# functions
|
|
87
|
+
elif isinstance(ctx, Parser.FunctionsExpressionContext):
|
|
88
|
+
return self.visitFunctionsExpression(c)
|
|
89
|
+
|
|
90
|
+
# op=(PLUS|MINUS|NOT) right=expr # unary expression
|
|
91
|
+
elif isinstance(ctx, Parser.UnaryExprContext):
|
|
92
|
+
return self.visitUnaryExpr(ctx)
|
|
93
|
+
|
|
94
|
+
# | left=expr op=(MUL|DIV) right=expr # arithmeticExpr
|
|
95
|
+
elif isinstance(ctx, Parser.ArithmeticExprContext):
|
|
96
|
+
return self.visitArithmeticExpr(ctx)
|
|
97
|
+
|
|
98
|
+
# | left=expr op=(PLUS|MINUS|CONCAT) right=expr # arithmeticExprOrConcat
|
|
99
|
+
elif isinstance(ctx, Parser.ArithmeticExprOrConcatContext):
|
|
100
|
+
return self.visitArithmeticExprOrConcat(ctx)
|
|
101
|
+
|
|
102
|
+
# | left=expr op=comparisonOperand right=expr # comparisonExpr
|
|
103
|
+
elif isinstance(ctx, Parser.ComparisonExprContext):
|
|
104
|
+
return self.visitComparisonExpr(ctx)
|
|
105
|
+
|
|
106
|
+
# | left=expr op=(IN|NOT_IN)(lists|valueDomainID) # inNotInExpr
|
|
107
|
+
elif isinstance(ctx, Parser.InNotInExprContext):
|
|
108
|
+
return self.visitInNotInExpr(ctx)
|
|
109
|
+
|
|
110
|
+
# | left=expr op=AND right=expr # booleanExpr
|
|
111
|
+
# | left=expr op=(OR|XOR) right=expr
|
|
112
|
+
elif isinstance(ctx, Parser.BooleanExprContext):
|
|
113
|
+
return self.visitBooleanExpr(ctx)
|
|
114
|
+
|
|
115
|
+
# IF conditionalExpr=expr THEN thenExpr=expr ELSE elseExpr=expr # ifExpr
|
|
116
|
+
elif isinstance(c, TerminalNodeImpl) and (c.getSymbol().type == Parser.IF):
|
|
117
|
+
condition_node = self.visitExpr(ctx_list[1])
|
|
118
|
+
then_op_node = self.visitExpr(ctx_list[3])
|
|
119
|
+
else_op_node = self.visitExpr(ctx_list[5])
|
|
120
|
+
|
|
121
|
+
if_node = If(
|
|
122
|
+
condition=condition_node,
|
|
123
|
+
thenOp=then_op_node,
|
|
124
|
+
elseOp=else_op_node,
|
|
125
|
+
**extract_token_info(ctx),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return if_node
|
|
129
|
+
|
|
130
|
+
# CASE WHEN expr THEN expr ELSE expr END # caseExpr
|
|
131
|
+
elif isinstance(c, TerminalNodeImpl) and (c.getSymbol().type == Parser.CASE):
|
|
132
|
+
if len(ctx_list) % 4 != 3:
|
|
133
|
+
raise ValueError("Syntax error.")
|
|
134
|
+
|
|
135
|
+
else_node = self.visitExpr(ctx_list[-1])
|
|
136
|
+
ctx_list = ctx_list[1:-2]
|
|
137
|
+
cases = []
|
|
138
|
+
|
|
139
|
+
for i in range(0, len(ctx_list), 4):
|
|
140
|
+
condition = self.visitExpr(ctx_list[i + 1])
|
|
141
|
+
thenOp = self.visitExpr(ctx_list[i + 3])
|
|
142
|
+
case_obj = CaseObj(
|
|
143
|
+
condition=condition, thenOp=thenOp, **extract_token_info(ctx_list[i + 1])
|
|
144
|
+
)
|
|
145
|
+
cases.append(case_obj)
|
|
146
|
+
|
|
147
|
+
case_node = Case(cases=cases, elseOp=else_node, **extract_token_info(ctx))
|
|
148
|
+
|
|
149
|
+
return case_node
|
|
150
|
+
|
|
151
|
+
# constant
|
|
152
|
+
elif isinstance(ctx, Parser.ConstantExprContext):
|
|
153
|
+
return Terminals().visitConstant(c)
|
|
154
|
+
|
|
155
|
+
# varID
|
|
156
|
+
elif isinstance(ctx, Parser.VarIdExprContext):
|
|
157
|
+
return Terminals().visitVarIdExpr(c)
|
|
158
|
+
|
|
159
|
+
else:
|
|
160
|
+
# AST_ASTCONSTRUCTOR.3
|
|
161
|
+
raise NotImplementedError
|
|
162
|
+
|
|
163
|
+
def bin_op_creator(self, ctx: Parser.ExprContext):
|
|
164
|
+
ctx_list = list(ctx.getChildren())
|
|
165
|
+
left_node = self.visitExpr(ctx_list[0])
|
|
166
|
+
if isinstance(ctx_list[1], Parser.ComparisonOperandContext):
|
|
167
|
+
op = list(ctx_list[1].getChildren())[0].getSymbol().text
|
|
168
|
+
else:
|
|
169
|
+
op = ctx_list[1].getSymbol().text
|
|
170
|
+
right_node = self.visitExpr(ctx_list[2])
|
|
171
|
+
token_info = extract_token_info(ctx)
|
|
172
|
+
bin_op_node = BinOp(left=left_node, op=op, right=right_node, **token_info)
|
|
173
|
+
|
|
174
|
+
return bin_op_node
|
|
175
|
+
|
|
176
|
+
def visitArithmeticExpr(self, ctx: Parser.ArithmeticExprContext):
|
|
177
|
+
return self.bin_op_creator(ctx)
|
|
178
|
+
|
|
179
|
+
def visitArithmeticExprOrConcat(self, ctx: Parser.ArithmeticExprOrConcatContext):
|
|
180
|
+
return self.bin_op_creator(ctx)
|
|
181
|
+
|
|
182
|
+
def visitComparisonExpr(self, ctx: Parser.ComparisonExprContext):
|
|
183
|
+
return self.bin_op_creator(ctx)
|
|
184
|
+
|
|
185
|
+
def visitInNotInExpr(self, ctx: Parser.InNotInExprContext):
|
|
186
|
+
ctx_list = list(ctx.getChildren())
|
|
187
|
+
left_node = self.visitExpr(ctx_list[0])
|
|
188
|
+
op = ctx_list[1].symbol.text
|
|
189
|
+
|
|
190
|
+
if isinstance(ctx_list[2], Parser.ListsContext):
|
|
191
|
+
right_node = Terminals().visitLists(ctx_list[2])
|
|
192
|
+
elif isinstance(ctx_list[2], Parser.ValueDomainIDContext):
|
|
193
|
+
right_node = Terminals().visitValueDomainID(ctx_list[2])
|
|
194
|
+
else:
|
|
195
|
+
raise NotImplementedError
|
|
196
|
+
bin_op_node = BinOp(left=left_node, op=op, right=right_node, **extract_token_info(ctx))
|
|
197
|
+
|
|
198
|
+
return bin_op_node
|
|
199
|
+
|
|
200
|
+
def visitBooleanExpr(self, ctx: Parser.BooleanExprContext):
|
|
201
|
+
return self.bin_op_creator(ctx)
|
|
202
|
+
|
|
203
|
+
def visitParenthesisExpr(self, ctx: Parser.ParenthesisExprContext):
|
|
204
|
+
operand = self.visitExpr(list(ctx.getChildren())[1])
|
|
205
|
+
return ParFunction(operand=operand, **extract_token_info(ctx))
|
|
206
|
+
|
|
207
|
+
def visitUnaryExpr(self, ctx: Parser.UnaryExprContext):
|
|
208
|
+
c_list = list(ctx.getChildren())
|
|
209
|
+
op = c_list[0].getSymbol().text
|
|
210
|
+
right = self.visitExpr(c_list[1])
|
|
211
|
+
|
|
212
|
+
return UnaryOp(op=op, operand=right, **extract_token_info(ctx))
|
|
213
|
+
|
|
214
|
+
def visitMembershipExpr(self, ctx: Parser.MembershipExprContext):
|
|
215
|
+
ctx_list = list(ctx.getChildren())
|
|
216
|
+
c = ctx_list[0]
|
|
217
|
+
membership = [
|
|
218
|
+
componentID
|
|
219
|
+
for componentID in ctx_list
|
|
220
|
+
if isinstance(componentID, Parser.SimpleComponentIdContext)
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
previous_node = self.visitExpr(c)
|
|
224
|
+
|
|
225
|
+
# It is only possible to put a membership at the end so go the last one.
|
|
226
|
+
if len(membership) != 0:
|
|
227
|
+
previous_node = BinOp(
|
|
228
|
+
left=previous_node,
|
|
229
|
+
op="#",
|
|
230
|
+
right=Terminals().visitSimpleComponentId(membership[0]),
|
|
231
|
+
**extract_token_info(ctx),
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
return previous_node
|
|
235
|
+
|
|
236
|
+
def visitClauseExpr(self, ctx: Parser.ClauseExprContext):
|
|
237
|
+
ctx_list = list(ctx.getChildren())
|
|
238
|
+
|
|
239
|
+
dataset = self.visitExpr(ctx_list[0])
|
|
240
|
+
|
|
241
|
+
dataset_clause = self.visitDatasetClause(ctx_list[2])
|
|
242
|
+
|
|
243
|
+
dataset_clause.dataset = dataset
|
|
244
|
+
|
|
245
|
+
return dataset_clause
|
|
246
|
+
|
|
247
|
+
"""______________________________________________________________________________________
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
Functions Definition.
|
|
251
|
+
|
|
252
|
+
_______________________________________________________________________________________"""
|
|
253
|
+
|
|
254
|
+
def visitFunctionsExpression(self, ctx: Parser.FunctionsExpressionContext):
|
|
255
|
+
"""
|
|
256
|
+
functions:
|
|
257
|
+
joinOperators # joinFunctions
|
|
258
|
+
| genericOperators # genericFunctions
|
|
259
|
+
| stringOperators # stringFunctions
|
|
260
|
+
| numericOperators # numericFunctions
|
|
261
|
+
| comparisonOperators # comparisonFunctions
|
|
262
|
+
| timeOperators # timeFunctions
|
|
263
|
+
| setOperators # setFunctions
|
|
264
|
+
| hierarchyOperators # hierarchyFunctions
|
|
265
|
+
| validationOperators # validationFunctions
|
|
266
|
+
| conditionalOperators # conditionalFunctions
|
|
267
|
+
| aggrOperatorsGrouping # aggregateFunctions
|
|
268
|
+
| anFunction # analyticFunctions
|
|
269
|
+
;
|
|
270
|
+
"""
|
|
271
|
+
c = ctx.children[0]
|
|
272
|
+
|
|
273
|
+
if isinstance(ctx, Parser.JoinFunctionsContext):
|
|
274
|
+
return self.visitJoinFunctions(c)
|
|
275
|
+
|
|
276
|
+
elif isinstance(ctx, Parser.GenericFunctionsContext):
|
|
277
|
+
return self.visitGenericFunctions(c)
|
|
278
|
+
|
|
279
|
+
elif isinstance(ctx, Parser.StringFunctionsContext):
|
|
280
|
+
return self.visitStringFunctions(c)
|
|
281
|
+
|
|
282
|
+
elif isinstance(ctx, Parser.NumericFunctionsContext):
|
|
283
|
+
return self.visitNumericFunctions(c)
|
|
284
|
+
|
|
285
|
+
elif isinstance(ctx, Parser.ComparisonFunctionsContext):
|
|
286
|
+
return self.visitComparisonFunctions(c)
|
|
287
|
+
|
|
288
|
+
elif isinstance(ctx, Parser.TimeFunctionsContext):
|
|
289
|
+
return self.visitTimeFunctions(c)
|
|
290
|
+
|
|
291
|
+
elif isinstance(ctx, Parser.SetFunctionsContext):
|
|
292
|
+
return self.visitSetFunctions(c)
|
|
293
|
+
|
|
294
|
+
elif isinstance(ctx, Parser.HierarchyFunctionsContext):
|
|
295
|
+
return self.visitHierarchyFunctions(c)
|
|
296
|
+
|
|
297
|
+
elif isinstance(ctx, Parser.ValidationFunctionsContext):
|
|
298
|
+
return self.visitValidationFunctions(c)
|
|
299
|
+
|
|
300
|
+
elif isinstance(ctx, Parser.ConditionalFunctionsContext):
|
|
301
|
+
return self.visitConditionalFunctions(c)
|
|
302
|
+
|
|
303
|
+
elif isinstance(ctx, Parser.AggregateFunctionsContext):
|
|
304
|
+
return self.visitAggregateFunctions(c)
|
|
305
|
+
|
|
306
|
+
elif isinstance(ctx, Parser.AnalyticFunctionsContext):
|
|
307
|
+
return self.visitAnalyticFunctions(c)
|
|
308
|
+
|
|
309
|
+
else:
|
|
310
|
+
raise NotImplementedError
|
|
311
|
+
|
|
312
|
+
"""
|
|
313
|
+
-----------------------------------
|
|
314
|
+
Join Functions
|
|
315
|
+
-----------------------------------
|
|
316
|
+
"""
|
|
317
|
+
|
|
318
|
+
def visitJoinFunctions(self, ctx: Parser.JoinFunctionsContext):
|
|
319
|
+
ctx_list = list(ctx.getChildren())
|
|
320
|
+
|
|
321
|
+
using_node = None
|
|
322
|
+
|
|
323
|
+
op_node = ctx_list[0].getSymbol().text
|
|
324
|
+
|
|
325
|
+
if op_node in ["inner_join", "left_join"]:
|
|
326
|
+
clause_node, using_node = self.visitJoinClause(ctx_list[2])
|
|
327
|
+
else:
|
|
328
|
+
clause_node = self.visitJoinClauseWithoutUsing(ctx_list[2])
|
|
329
|
+
|
|
330
|
+
body_node = self.visitJoinBody(ctx_list[3])
|
|
331
|
+
|
|
332
|
+
token_info = extract_token_info(ctx)
|
|
333
|
+
|
|
334
|
+
if len(body_node) != 0:
|
|
335
|
+
previous_node = JoinOp(op=op_node, clauses=clause_node, using=using_node, **token_info)
|
|
336
|
+
regular_aggregation = None
|
|
337
|
+
for body in body_node:
|
|
338
|
+
regular_aggregation = body
|
|
339
|
+
regular_aggregation.dataset = previous_node
|
|
340
|
+
previous_node = regular_aggregation
|
|
341
|
+
|
|
342
|
+
# set the last of the body clauses (ie dataclauses).
|
|
343
|
+
previous_node.isLast = True
|
|
344
|
+
|
|
345
|
+
return regular_aggregation
|
|
346
|
+
|
|
347
|
+
else:
|
|
348
|
+
join_node = JoinOp(op=op_node, clauses=clause_node, using=using_node, **token_info)
|
|
349
|
+
join_node.isLast = True
|
|
350
|
+
return join_node
|
|
351
|
+
|
|
352
|
+
def visitJoinClauseItem(self, ctx: Parser.JoinClauseItemContext):
|
|
353
|
+
ctx_list = list(ctx.getChildren())
|
|
354
|
+
left_node = self.visitExpr(ctx_list[0])
|
|
355
|
+
if len(ctx_list) == 1:
|
|
356
|
+
return left_node
|
|
357
|
+
|
|
358
|
+
token_info = extract_token_info(ctx)
|
|
359
|
+
intop_node = ctx_list[1].getSymbol().text
|
|
360
|
+
right_node = Identifier(
|
|
361
|
+
value=Terminals().visitAlias(ctx_list[2]),
|
|
362
|
+
kind="DatasetID",
|
|
363
|
+
**extract_token_info(ctx_list[1].getSymbol()),
|
|
364
|
+
)
|
|
365
|
+
return BinOp(left=left_node, op=intop_node, right=right_node, **token_info)
|
|
366
|
+
|
|
367
|
+
def visitJoinClause(self, ctx: Parser.JoinClauseContext):
|
|
368
|
+
"""
|
|
369
|
+
JoinClauseItem (COMMA joinClauseItem)* (USING componentID (COMMA componentID)*)?
|
|
370
|
+
"""
|
|
371
|
+
ctx_list = list(ctx.getChildren())
|
|
372
|
+
|
|
373
|
+
clause_nodes = []
|
|
374
|
+
component_nodes = []
|
|
375
|
+
using = None
|
|
376
|
+
|
|
377
|
+
items = [item for item in ctx_list if isinstance(item, Parser.JoinClauseItemContext)]
|
|
378
|
+
components = [
|
|
379
|
+
component for component in ctx_list if isinstance(component, Parser.ComponentIDContext)
|
|
380
|
+
]
|
|
381
|
+
|
|
382
|
+
for item in items:
|
|
383
|
+
clause_nodes.append(self.visitJoinClauseItem(item))
|
|
384
|
+
|
|
385
|
+
if len(components) != 0:
|
|
386
|
+
for component in components:
|
|
387
|
+
component_nodes.append(Terminals().visitComponentID(component).value)
|
|
388
|
+
using = component_nodes
|
|
389
|
+
|
|
390
|
+
return clause_nodes, using
|
|
391
|
+
|
|
392
|
+
def visitJoinClauseWithoutUsing(self, ctx: Parser.JoinClauseWithoutUsingContext):
|
|
393
|
+
"""
|
|
394
|
+
joinClause: joinClauseItem (COMMA joinClauseItem)* (USING componentID (COMMA componentID)*)? ;
|
|
395
|
+
""" # noqa E501
|
|
396
|
+
ctx_list = list(ctx.getChildren())
|
|
397
|
+
|
|
398
|
+
clause_nodes = []
|
|
399
|
+
|
|
400
|
+
items = [item for item in ctx_list if isinstance(item, Parser.JoinClauseItemContext)]
|
|
401
|
+
|
|
402
|
+
for item in items:
|
|
403
|
+
clause_nodes.append(self.visitJoinClauseItem(item))
|
|
404
|
+
|
|
405
|
+
return clause_nodes
|
|
406
|
+
|
|
407
|
+
def visitJoinBody(self, ctx: Parser.JoinBodyContext):
|
|
408
|
+
"""
|
|
409
|
+
joinBody: filterClause? (calcClause|joinApplyClause|aggrClause)? (keepOrDropClause)? renameClause?
|
|
410
|
+
""" # noqa E501
|
|
411
|
+
ctx_list = list(ctx.getChildren())
|
|
412
|
+
|
|
413
|
+
body_nodes = []
|
|
414
|
+
|
|
415
|
+
for c in ctx_list:
|
|
416
|
+
if isinstance(c, Parser.FilterClauseContext):
|
|
417
|
+
body_nodes.append(self.visitFilterClause(c))
|
|
418
|
+
elif isinstance(c, Parser.CalcClauseContext):
|
|
419
|
+
body_nodes.append(self.visitCalcClause(c))
|
|
420
|
+
elif isinstance(c, Parser.JoinApplyClauseContext):
|
|
421
|
+
body_nodes.append(self.visitJoinApplyClause(c))
|
|
422
|
+
elif isinstance(c, Parser.AggrClauseContext):
|
|
423
|
+
body_nodes.append(self.visitAggrClause(c))
|
|
424
|
+
elif isinstance(c, Parser.KeepOrDropClauseContext):
|
|
425
|
+
body_nodes.append(self.visitKeepOrDropClause(c))
|
|
426
|
+
elif isinstance(c, Parser.RenameClauseContext):
|
|
427
|
+
body_nodes.append(self.visitRenameClause(c))
|
|
428
|
+
else:
|
|
429
|
+
raise NotImplementedError
|
|
430
|
+
|
|
431
|
+
return body_nodes
|
|
432
|
+
|
|
433
|
+
# TODO Unary Op here?
|
|
434
|
+
def visitJoinApplyClause(self, ctx: Parser.JoinApplyClauseContext):
|
|
435
|
+
"""
|
|
436
|
+
joinApplyClause: APPLY expr ;
|
|
437
|
+
"""
|
|
438
|
+
ctx_list = list(ctx.getChildren())
|
|
439
|
+
op_node = ctx_list[0].getSymbol().text
|
|
440
|
+
operand_nodes = [self.visitExpr(ctx_list[1])]
|
|
441
|
+
|
|
442
|
+
return RegularAggregation(op=op_node, children=operand_nodes, **extract_token_info(ctx))
|
|
443
|
+
|
|
444
|
+
"""
|
|
445
|
+
-----------------------------------
|
|
446
|
+
Generic Functions
|
|
447
|
+
-----------------------------------
|
|
448
|
+
"""
|
|
449
|
+
|
|
450
|
+
def visitGenericFunctions(self, ctx: Parser.GenericFunctionsContext):
|
|
451
|
+
if isinstance(ctx, Parser.CallDatasetContext):
|
|
452
|
+
return self.visitCallDataset(ctx)
|
|
453
|
+
elif isinstance(ctx, Parser.EvalAtomContext):
|
|
454
|
+
return self.visitEvalAtom(ctx)
|
|
455
|
+
elif isinstance(ctx, Parser.CastExprDatasetContext):
|
|
456
|
+
return self.visitCastExprDataset(ctx)
|
|
457
|
+
else:
|
|
458
|
+
raise NotImplementedError
|
|
459
|
+
|
|
460
|
+
def visitCallDataset(self, ctx: Parser.CallDatasetContext):
|
|
461
|
+
"""
|
|
462
|
+
callFunction: operatorID LPAREN (parameter (COMMA parameter)*)? RPAREN ;
|
|
463
|
+
"""
|
|
464
|
+
ctx_list = list(ctx.getChildren())
|
|
465
|
+
c = ctx_list[0]
|
|
466
|
+
|
|
467
|
+
op = Terminals().visitOperatorID(c)
|
|
468
|
+
param_nodes = [
|
|
469
|
+
self.visitParameter(element)
|
|
470
|
+
for element in ctx_list
|
|
471
|
+
if isinstance(element, Parser.ParameterContext)
|
|
472
|
+
]
|
|
473
|
+
|
|
474
|
+
return UDOCall(op=op, params=param_nodes, **extract_token_info(ctx))
|
|
475
|
+
|
|
476
|
+
def visitEvalAtom(self, ctx: Parser.EvalAtomContext):
|
|
477
|
+
"""
|
|
478
|
+
| EVAL LPAREN routineName LPAREN (varID|scalarItem)? (COMMA (varID|scalarItem))* RPAREN (LANGUAGE STRING_CONSTANT)? (RETURNS evalDatasetType)? RPAREN # evalAtom
|
|
479
|
+
""" # noqa E501
|
|
480
|
+
ctx_list = list(ctx.getChildren())
|
|
481
|
+
|
|
482
|
+
routine_name = Terminals().visitRoutineName(ctx_list[2])
|
|
483
|
+
|
|
484
|
+
# Think of a way to maintain the order, for now its not necessary.
|
|
485
|
+
var_ids_nodes = [
|
|
486
|
+
Terminals().visitVarID(varID)
|
|
487
|
+
for varID in ctx_list
|
|
488
|
+
if isinstance(varID, Parser.VarIDContext)
|
|
489
|
+
]
|
|
490
|
+
constant_nodes = [
|
|
491
|
+
Terminals().visitScalarItem(scalar)
|
|
492
|
+
for scalar in ctx_list
|
|
493
|
+
if isinstance(scalar, Parser.ScalarItemContext)
|
|
494
|
+
]
|
|
495
|
+
children_nodes = var_ids_nodes + constant_nodes
|
|
496
|
+
|
|
497
|
+
# Reference manual says it is mandatory.
|
|
498
|
+
language_name = [
|
|
499
|
+
language
|
|
500
|
+
for language in ctx_list
|
|
501
|
+
if isinstance(language, TerminalNodeImpl)
|
|
502
|
+
and language.getSymbol().type == Parser.STRING_CONSTANT
|
|
503
|
+
]
|
|
504
|
+
if len(language_name) == 0:
|
|
505
|
+
# AST_ASTCONSTRUCTOR.12
|
|
506
|
+
raise SemanticError("1-3-2-1", option="language")
|
|
507
|
+
# Reference manual says it is mandatory.
|
|
508
|
+
output_node = [
|
|
509
|
+
Terminals().visitEvalDatasetType(output)
|
|
510
|
+
for output in ctx_list
|
|
511
|
+
if isinstance(output, Parser.EvalDatasetTypeContext)
|
|
512
|
+
]
|
|
513
|
+
if len(output_node) == 0:
|
|
514
|
+
# AST_ASTCONSTRUCTOR.13
|
|
515
|
+
raise SemanticError("1-3-2-1", option="output")
|
|
516
|
+
|
|
517
|
+
return EvalOp(
|
|
518
|
+
name=routine_name,
|
|
519
|
+
operands=children_nodes,
|
|
520
|
+
output=output_node[0],
|
|
521
|
+
language=language_name[0].getSymbol().text,
|
|
522
|
+
**extract_token_info(ctx),
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
def visitCastExprDataset(self, ctx: Parser.CastExprDatasetContext):
|
|
526
|
+
"""
|
|
527
|
+
| CAST LPAREN expr COMMA (basicScalarType|valueDomainName) (COMMA STRING_CONSTANT)? RPAREN # castExprDataset
|
|
528
|
+
""" # noqa E501
|
|
529
|
+
ctx_list = list(ctx.getChildren())
|
|
530
|
+
c = ctx_list[0]
|
|
531
|
+
|
|
532
|
+
token = c.getSymbol()
|
|
533
|
+
|
|
534
|
+
op = token.text
|
|
535
|
+
expr_node = [
|
|
536
|
+
self.visitExpr(expr) for expr in ctx_list if isinstance(expr, Parser.ExprContext)
|
|
537
|
+
]
|
|
538
|
+
basic_scalar_type = [
|
|
539
|
+
Terminals().visitBasicScalarType(type_)
|
|
540
|
+
for type_ in ctx_list
|
|
541
|
+
if isinstance(type_, Parser.BasicScalarTypeContext)
|
|
542
|
+
]
|
|
543
|
+
|
|
544
|
+
[
|
|
545
|
+
Terminals().visitValueDomainName(valueD)
|
|
546
|
+
for valueD in ctx_list
|
|
547
|
+
if isinstance(valueD, Parser.ValueDomainNameContext)
|
|
548
|
+
]
|
|
549
|
+
|
|
550
|
+
if len(ctx_list) > 6:
|
|
551
|
+
param_node = [
|
|
552
|
+
ParamConstant(
|
|
553
|
+
type_="PARAM_CAST",
|
|
554
|
+
value=str_.symbol.text.strip('"'),
|
|
555
|
+
**extract_token_info(str_.getSymbol()),
|
|
556
|
+
)
|
|
557
|
+
for str_ in ctx_list
|
|
558
|
+
if isinstance(str_, TerminalNodeImpl)
|
|
559
|
+
and str_.getSymbol().type == Parser.STRING_CONSTANT
|
|
560
|
+
]
|
|
561
|
+
else:
|
|
562
|
+
param_node = []
|
|
563
|
+
|
|
564
|
+
if len(basic_scalar_type) == 1:
|
|
565
|
+
children_nodes = expr_node + basic_scalar_type
|
|
566
|
+
|
|
567
|
+
return ParamOp(
|
|
568
|
+
op=op, children=children_nodes, params=param_node, **extract_token_info(ctx)
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
else:
|
|
572
|
+
# AST_ASTCONSTRUCTOR.14
|
|
573
|
+
raise NotImplementedError
|
|
574
|
+
|
|
575
|
+
def visitParameter(self, ctx: Parser.ParameterContext):
|
|
576
|
+
ctx_list = list(ctx.getChildren())
|
|
577
|
+
c = ctx_list[0]
|
|
578
|
+
|
|
579
|
+
if isinstance(c, Parser.ExprContext):
|
|
580
|
+
return self.visitExpr(c)
|
|
581
|
+
elif isinstance(c, TerminalNodeImpl):
|
|
582
|
+
return ID(
|
|
583
|
+
type_="OPTIONAL", value=c.getSymbol().text, **extract_token_info(c.getSymbol())
|
|
584
|
+
)
|
|
585
|
+
else:
|
|
586
|
+
raise NotImplementedError
|
|
587
|
+
|
|
588
|
+
"""
|
|
589
|
+
-----------------------------------
|
|
590
|
+
String Functions
|
|
591
|
+
-----------------------------------
|
|
592
|
+
"""
|
|
593
|
+
|
|
594
|
+
def visitStringFunctions(self, ctx: Parser.StringFunctionsContext):
|
|
595
|
+
if isinstance(ctx, Parser.UnaryStringFunctionContext):
|
|
596
|
+
return self.visitUnaryStringFunction(ctx)
|
|
597
|
+
elif isinstance(ctx, Parser.SubstrAtomContext):
|
|
598
|
+
return self.visitSubstrAtom(ctx)
|
|
599
|
+
elif isinstance(ctx, Parser.ReplaceAtomContext):
|
|
600
|
+
return self.visitReplaceAtom(ctx)
|
|
601
|
+
elif isinstance(ctx, Parser.InstrAtomContext):
|
|
602
|
+
return self.visitInstrAtom(ctx)
|
|
603
|
+
else:
|
|
604
|
+
raise NotImplementedError
|
|
605
|
+
|
|
606
|
+
def visitUnaryStringFunction(self, ctx: Parser.UnaryStringFunctionContext):
|
|
607
|
+
ctx_list = list(ctx.getChildren())
|
|
608
|
+
c = ctx_list[0]
|
|
609
|
+
|
|
610
|
+
token = c.getSymbol()
|
|
611
|
+
op_node = token.text
|
|
612
|
+
operand_node = self.visitExpr(ctx_list[2])
|
|
613
|
+
return UnaryOp(op=op_node, operand=operand_node, **extract_token_info(ctx))
|
|
614
|
+
|
|
615
|
+
def visitSubstrAtom(self, ctx: Parser.SubstrAtomContext):
|
|
616
|
+
ctx_list = list(ctx.getChildren())
|
|
617
|
+
c = ctx_list[0]
|
|
618
|
+
|
|
619
|
+
params_nodes = []
|
|
620
|
+
children_nodes = []
|
|
621
|
+
|
|
622
|
+
token = c.getSymbol()
|
|
623
|
+
childrens = [expr for expr in ctx_list if isinstance(expr, Parser.ExprContext)]
|
|
624
|
+
params = [param for param in ctx_list if isinstance(param, Parser.OptionalExprContext)]
|
|
625
|
+
|
|
626
|
+
op_node = token.text
|
|
627
|
+
for children in childrens:
|
|
628
|
+
children_nodes.append(self.visitExpr(children))
|
|
629
|
+
|
|
630
|
+
if len(params) != 0:
|
|
631
|
+
for param in params:
|
|
632
|
+
params_nodes.append(self.visitOptionalExpr(param))
|
|
633
|
+
|
|
634
|
+
return ParamOp(
|
|
635
|
+
op=op_node, children=children_nodes, params=params_nodes, **extract_token_info(ctx)
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
def visitReplaceAtom(self, ctx: Parser.ReplaceAtomContext):
|
|
639
|
+
ctx_list = list(ctx.getChildren())
|
|
640
|
+
c = ctx_list[0]
|
|
641
|
+
|
|
642
|
+
token = c.getSymbol()
|
|
643
|
+
expressions = [
|
|
644
|
+
self.visitExpr(expr) for expr in ctx_list if isinstance(expr, Parser.ExprContext)
|
|
645
|
+
]
|
|
646
|
+
params = [
|
|
647
|
+
self.visitOptionalExpr(param)
|
|
648
|
+
for param in ctx_list
|
|
649
|
+
if isinstance(param, Parser.OptionalExprContext)
|
|
650
|
+
]
|
|
651
|
+
|
|
652
|
+
op_node = token.text
|
|
653
|
+
|
|
654
|
+
children_nodes = [expressions[0]]
|
|
655
|
+
params_nodes = [expressions[1]] + params
|
|
656
|
+
|
|
657
|
+
return ParamOp(
|
|
658
|
+
op=op_node, children=children_nodes, params=params_nodes, **extract_token_info(ctx)
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
def visitInstrAtom(self, ctx: Parser.InstrAtomContext):
|
|
662
|
+
ctx_list = list(ctx.getChildren())
|
|
663
|
+
c = ctx_list[0]
|
|
664
|
+
|
|
665
|
+
token = c.getSymbol()
|
|
666
|
+
expressions = [
|
|
667
|
+
self.visitExpr(expr) for expr in ctx_list if isinstance(expr, Parser.ExprContext)
|
|
668
|
+
]
|
|
669
|
+
params = [
|
|
670
|
+
self.visitOptionalExpr(param)
|
|
671
|
+
for param in ctx_list
|
|
672
|
+
if isinstance(param, Parser.OptionalExprContext)
|
|
673
|
+
]
|
|
674
|
+
|
|
675
|
+
op_node = token.text
|
|
676
|
+
|
|
677
|
+
children_nodes = [expressions[0]]
|
|
678
|
+
params_nodes = [expressions[1]] + params
|
|
679
|
+
|
|
680
|
+
return ParamOp(
|
|
681
|
+
op=op_node, children=children_nodes, params=params_nodes, **extract_token_info(ctx)
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
"""
|
|
685
|
+
-----------------------------------
|
|
686
|
+
Numeric Functions
|
|
687
|
+
-----------------------------------
|
|
688
|
+
"""
|
|
689
|
+
|
|
690
|
+
def visitNumericFunctions(self, ctx: Parser.NumericFunctionsContext):
|
|
691
|
+
if isinstance(ctx, Parser.UnaryNumericContext):
|
|
692
|
+
return self.visitUnaryNumeric(ctx)
|
|
693
|
+
elif isinstance(ctx, Parser.UnaryWithOptionalNumericContext):
|
|
694
|
+
return self.visitUnaryWithOptionalNumeric(ctx)
|
|
695
|
+
elif isinstance(ctx, Parser.BinaryNumericContext):
|
|
696
|
+
return self.visitBinaryNumeric(ctx)
|
|
697
|
+
else:
|
|
698
|
+
raise NotImplementedError
|
|
699
|
+
|
|
700
|
+
def visitUnaryNumeric(self, ctx: Parser.UnaryNumericContext):
|
|
701
|
+
ctx_list = list(ctx.getChildren())
|
|
702
|
+
c = ctx_list[0]
|
|
703
|
+
|
|
704
|
+
token = c.getSymbol()
|
|
705
|
+
op_node = token.text
|
|
706
|
+
operand_node = self.visitExpr(ctx_list[2])
|
|
707
|
+
return UnaryOp(op=op_node, operand=operand_node, **extract_token_info(ctx))
|
|
708
|
+
|
|
709
|
+
def visitUnaryWithOptionalNumeric(self, ctx: Parser.UnaryWithOptionalNumericContext):
|
|
710
|
+
ctx_list = list(ctx.getChildren())
|
|
711
|
+
c = ctx_list[0]
|
|
712
|
+
|
|
713
|
+
params_nodes = []
|
|
714
|
+
children_nodes = []
|
|
715
|
+
|
|
716
|
+
token = c.getSymbol()
|
|
717
|
+
childrens = [expr for expr in ctx_list if isinstance(expr, Parser.ExprContext)]
|
|
718
|
+
params = [param for param in ctx_list if isinstance(param, Parser.OptionalExprContext)]
|
|
719
|
+
|
|
720
|
+
op_node = token.text
|
|
721
|
+
for children in childrens:
|
|
722
|
+
children_nodes.append(self.visitExpr(children))
|
|
723
|
+
|
|
724
|
+
if len(params) != 0:
|
|
725
|
+
for param in params:
|
|
726
|
+
params_nodes.append(self.visitOptionalExpr(param))
|
|
727
|
+
|
|
728
|
+
return ParamOp(
|
|
729
|
+
op=op_node, children=children_nodes, params=params_nodes, **extract_token_info(ctx)
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
def visitBinaryNumeric(self, ctx: Parser.BinaryNumericContext):
|
|
733
|
+
ctx_list = list(ctx.getChildren())
|
|
734
|
+
c = ctx_list[0]
|
|
735
|
+
|
|
736
|
+
token = c.getSymbol()
|
|
737
|
+
|
|
738
|
+
left_node = self.visitExpr(ctx_list[2])
|
|
739
|
+
op_node = token.text
|
|
740
|
+
right_node = self.visitExpr(ctx_list[4])
|
|
741
|
+
return BinOp(left=left_node, op=op_node, right=right_node, **extract_token_info(ctx))
|
|
742
|
+
|
|
743
|
+
"""
|
|
744
|
+
-----------------------------------
|
|
745
|
+
Comparison Functions
|
|
746
|
+
-----------------------------------
|
|
747
|
+
"""
|
|
748
|
+
|
|
749
|
+
def visitComparisonFunctions(self, ctx: Parser.ComparisonFunctionsContext):
|
|
750
|
+
if isinstance(ctx, Parser.BetweenAtomContext):
|
|
751
|
+
return self.visitBetweenAtom(ctx)
|
|
752
|
+
elif isinstance(ctx, Parser.CharsetMatchAtomContext):
|
|
753
|
+
return self.visitCharsetMatchAtom(ctx)
|
|
754
|
+
elif isinstance(ctx, Parser.IsNullAtomContext):
|
|
755
|
+
return self.visitIsNullAtom(ctx)
|
|
756
|
+
elif isinstance(ctx, Parser.ExistInAtomContext):
|
|
757
|
+
return self.visitExistInAtom(ctx)
|
|
758
|
+
else:
|
|
759
|
+
raise NotImplementedError
|
|
760
|
+
|
|
761
|
+
def visitBetweenAtom(self, ctx: Parser.BetweenAtomContext):
|
|
762
|
+
ctx_list = list(ctx.getChildren())
|
|
763
|
+
c = ctx_list[0]
|
|
764
|
+
|
|
765
|
+
children_nodes = []
|
|
766
|
+
|
|
767
|
+
token = c.getSymbol()
|
|
768
|
+
childrens = [expr for expr in ctx_list if isinstance(expr, Parser.ExprContext)]
|
|
769
|
+
|
|
770
|
+
op_node = token.text
|
|
771
|
+
for children in childrens:
|
|
772
|
+
children_nodes.append(self.visitExpr(children))
|
|
773
|
+
|
|
774
|
+
return MulOp(op=op_node, children=children_nodes, **extract_token_info(ctx))
|
|
775
|
+
|
|
776
|
+
def visitCharsetMatchAtom(self, ctx: Parser.CharsetMatchAtomContext):
|
|
777
|
+
ctx_list = list(ctx.getChildren())
|
|
778
|
+
c = ctx_list[0]
|
|
779
|
+
token = c.getSymbol()
|
|
780
|
+
|
|
781
|
+
left_node = self.visitExpr(ctx_list[2])
|
|
782
|
+
op_node = token.text
|
|
783
|
+
right_node = self.visitExpr(ctx_list[4])
|
|
784
|
+
return BinOp(left=left_node, op=op_node, right=right_node, **extract_token_info(ctx))
|
|
785
|
+
|
|
786
|
+
def visitIsNullAtom(self, ctx: Parser.IsNullAtomContext):
|
|
787
|
+
ctx_list = list(ctx.getChildren())
|
|
788
|
+
c = ctx_list[0]
|
|
789
|
+
token = c.getSymbol()
|
|
790
|
+
op_node = token.text
|
|
791
|
+
operand_node = self.visitExpr(ctx_list[2])
|
|
792
|
+
return UnaryOp(op=op_node, operand=operand_node, **extract_token_info(ctx))
|
|
793
|
+
|
|
794
|
+
def visitExistInAtom(self, ctx: Parser.ExistInAtomContext):
|
|
795
|
+
ctx_list = list(ctx.getChildren())
|
|
796
|
+
token = ctx_list[0].getSymbol()
|
|
797
|
+
op = token.text
|
|
798
|
+
|
|
799
|
+
operand_nodes = [
|
|
800
|
+
self.visitExpr(expr) for expr in ctx_list if isinstance(expr, Parser.ExprContext)
|
|
801
|
+
]
|
|
802
|
+
retain_nodes = [
|
|
803
|
+
Terminals().visitRetainType(retain)
|
|
804
|
+
for retain in ctx_list
|
|
805
|
+
if isinstance(retain, Parser.RetainTypeContext)
|
|
806
|
+
]
|
|
807
|
+
|
|
808
|
+
return MulOp(op=op, children=operand_nodes + retain_nodes, **extract_token_info(ctx))
|
|
809
|
+
|
|
810
|
+
"""
|
|
811
|
+
-----------------------------------
|
|
812
|
+
Time Functions
|
|
813
|
+
-----------------------------------
|
|
814
|
+
"""
|
|
815
|
+
|
|
816
|
+
def visitTimeFunctions(self, ctx: Parser.TimeFunctionsContext):
|
|
817
|
+
if isinstance(ctx, Parser.PeriodAtomContext):
|
|
818
|
+
# return self.visitPeriodAtom(ctx)
|
|
819
|
+
return self.visitTimeUnaryAtom(ctx)
|
|
820
|
+
elif isinstance(ctx, Parser.FillTimeAtomContext):
|
|
821
|
+
return self.visitFillTimeAtom(ctx)
|
|
822
|
+
elif isinstance(ctx, Parser.FlowAtomContext):
|
|
823
|
+
return self.visitFlowAtom(ctx)
|
|
824
|
+
elif isinstance(ctx, Parser.TimeShiftAtomContext):
|
|
825
|
+
return self.visitTimeShiftAtom(ctx)
|
|
826
|
+
elif isinstance(ctx, Parser.TimeAggAtomContext):
|
|
827
|
+
return self.visitTimeAggAtom(ctx)
|
|
828
|
+
elif isinstance(ctx, Parser.CurrentDateAtomContext):
|
|
829
|
+
return self.visitCurrentDateAtom(ctx)
|
|
830
|
+
elif isinstance(ctx, Parser.DateDiffAtomContext):
|
|
831
|
+
return self.visitTimeDiffAtom(ctx)
|
|
832
|
+
elif isinstance(ctx, Parser.DateAddAtomContext):
|
|
833
|
+
return self.visitTimeAddAtom(ctx)
|
|
834
|
+
elif isinstance(
|
|
835
|
+
ctx,
|
|
836
|
+
(
|
|
837
|
+
Parser.YearAtomContext,
|
|
838
|
+
Parser.MonthAtomContext,
|
|
839
|
+
Parser.DayOfMonthAtomContext,
|
|
840
|
+
Parser.DayOfYearAtomContext,
|
|
841
|
+
Parser.DayToYearAtomContext,
|
|
842
|
+
Parser.DayToMonthAtomContext,
|
|
843
|
+
Parser.YearToDayAtomContext,
|
|
844
|
+
Parser.MonthToDayAtomContext,
|
|
845
|
+
),
|
|
846
|
+
):
|
|
847
|
+
return self.visitTimeUnaryAtom(ctx)
|
|
848
|
+
else:
|
|
849
|
+
raise NotImplementedError
|
|
850
|
+
|
|
851
|
+
def visitTimeUnaryAtom(self, ctx: Any):
|
|
852
|
+
ctx_list = list(ctx.getChildren())
|
|
853
|
+
c = ctx_list[0]
|
|
854
|
+
|
|
855
|
+
op = c.getSymbol().text
|
|
856
|
+
operand_node = [
|
|
857
|
+
self.visitExpr(operand)
|
|
858
|
+
for operand in ctx_list
|
|
859
|
+
if isinstance(operand, Parser.ExprContext)
|
|
860
|
+
]
|
|
861
|
+
|
|
862
|
+
if len(operand_node) == 0:
|
|
863
|
+
# AST_ASTCONSTRUCTOR.15
|
|
864
|
+
raise NotImplementedError
|
|
865
|
+
|
|
866
|
+
return UnaryOp(op=op, operand=operand_node[0], **extract_token_info(ctx))
|
|
867
|
+
|
|
868
|
+
def visitTimeShiftAtom(self, ctx: Parser.TimeShiftAtomContext):
|
|
869
|
+
"""
|
|
870
|
+
timeShiftExpr: TIMESHIFT '(' expr ',' INTEGER_CONSTANT ')' ;
|
|
871
|
+
"""
|
|
872
|
+
ctx_list = list(ctx.getChildren())
|
|
873
|
+
c = ctx_list[0]
|
|
874
|
+
|
|
875
|
+
op = c.getSymbol().text
|
|
876
|
+
left_node = self.visitExpr(ctx_list[2])
|
|
877
|
+
right_node = Constant(
|
|
878
|
+
type_="INTEGER_CONSTANT",
|
|
879
|
+
value=Terminals().visitSignedInteger(ctx_list[4]),
|
|
880
|
+
**extract_token_info(ctx_list[4]),
|
|
881
|
+
)
|
|
882
|
+
|
|
883
|
+
return BinOp(left=left_node, op=op, right=right_node, **extract_token_info(ctx))
|
|
884
|
+
|
|
885
|
+
def visitFillTimeAtom(self, ctx: Parser.FillTimeAtomContext):
|
|
886
|
+
"""
|
|
887
|
+
timeSeriesExpr: FILL_TIME_SERIES '(' expr (',' (SINGLE|ALL))? ')' ;
|
|
888
|
+
"""
|
|
889
|
+
ctx_list = list(ctx.getChildren())
|
|
890
|
+
c = ctx_list[0]
|
|
891
|
+
|
|
892
|
+
op = c.getSymbol().text
|
|
893
|
+
children_node = [self.visitExpr(ctx_list[2])]
|
|
894
|
+
|
|
895
|
+
if len(ctx_list) > 4:
|
|
896
|
+
param_constant_node = [
|
|
897
|
+
ParamConstant(
|
|
898
|
+
type_="PARAM_TIMESERIES",
|
|
899
|
+
value=ctx_list[4].getSymbol().text,
|
|
900
|
+
**extract_token_info(ctx_list[4].getSymbol()),
|
|
901
|
+
)
|
|
902
|
+
]
|
|
903
|
+
else:
|
|
904
|
+
param_constant_node = []
|
|
905
|
+
|
|
906
|
+
return ParamOp(
|
|
907
|
+
op=op, children=children_node, params=param_constant_node, **extract_token_info(ctx)
|
|
908
|
+
)
|
|
909
|
+
|
|
910
|
+
def visitTimeAggAtom(self, ctx: Parser.TimeAggAtomContext):
|
|
911
|
+
"""
|
|
912
|
+
TIME_AGG LPAREN periodIndTo=STRING_CONSTANT (COMMA periodIndFrom=(STRING_CONSTANT| OPTIONAL ))? (COMMA op=optionalExpr)? (COMMA (FIRST|LAST))? RPAREN # timeAggAtom
|
|
913
|
+
""" # noqa E501
|
|
914
|
+
ctx_list = list(ctx.getChildren())
|
|
915
|
+
c = ctx_list[0]
|
|
916
|
+
|
|
917
|
+
op = c.getSymbol().text
|
|
918
|
+
period_to = str(ctx.periodIndTo.text)[1:-1]
|
|
919
|
+
period_from = None
|
|
920
|
+
|
|
921
|
+
if ctx.periodIndFrom is not None and ctx.periodIndFrom.type != Parser.OPTIONAL:
|
|
922
|
+
period_from = str(ctx.periodIndFrom.text)[1:-1]
|
|
923
|
+
|
|
924
|
+
conf = [
|
|
925
|
+
str_.getSymbol().text
|
|
926
|
+
for str_ in ctx_list
|
|
927
|
+
if isinstance(str_, TerminalNodeImpl)
|
|
928
|
+
and str_.getSymbol().type in [Parser.FIRST, Parser.LAST]
|
|
929
|
+
]
|
|
930
|
+
|
|
931
|
+
conf = None if len(conf) == 0 else conf[0]
|
|
932
|
+
|
|
933
|
+
if ctx.op is not None:
|
|
934
|
+
operand_node = self.visitOptionalExpr(ctx.op)
|
|
935
|
+
if isinstance(operand_node, ID):
|
|
936
|
+
operand_node = None
|
|
937
|
+
elif isinstance(operand_node, Identifier):
|
|
938
|
+
operand_node = VarID(value=operand_node.value, **extract_token_info(ctx))
|
|
939
|
+
else:
|
|
940
|
+
operand_node = None
|
|
941
|
+
|
|
942
|
+
if operand_node is None:
|
|
943
|
+
# AST_ASTCONSTRUCTOR.17
|
|
944
|
+
raise Exception("Optional as expression node is not allowed in Time Aggregation")
|
|
945
|
+
return TimeAggregation(
|
|
946
|
+
op=op,
|
|
947
|
+
operand=operand_node,
|
|
948
|
+
period_to=period_to,
|
|
949
|
+
period_from=period_from,
|
|
950
|
+
conf=conf,
|
|
951
|
+
**extract_token_info(ctx),
|
|
952
|
+
)
|
|
953
|
+
|
|
954
|
+
def visitFlowAtom(self, ctx: Parser.FlowAtomContext):
|
|
955
|
+
ctx_list = list(ctx.getChildren())
|
|
956
|
+
|
|
957
|
+
op_node = ctx_list[0].getSymbol().text
|
|
958
|
+
operand_node = self.visitExpr(ctx_list[2])
|
|
959
|
+
return UnaryOp(op=op_node, operand=operand_node, **extract_token_info(ctx))
|
|
960
|
+
|
|
961
|
+
def visitCurrentDateAtom(self, ctx: Parser.CurrentDateAtomContext):
|
|
962
|
+
c = list(ctx.getChildren())[0]
|
|
963
|
+
return MulOp(op=c.getSymbol().text, children=[], **extract_token_info(ctx))
|
|
964
|
+
|
|
965
|
+
def visitTimeDiffAtom(self, ctx: Parser.TimeShiftAtomContext):
|
|
966
|
+
""" """
|
|
967
|
+
ctx_list = list(ctx.getChildren())
|
|
968
|
+
c = ctx_list[0]
|
|
969
|
+
|
|
970
|
+
op = c.getSymbol().text
|
|
971
|
+
left_node = self.visitExpr(ctx_list[2])
|
|
972
|
+
right_node = self.visitExpr(ctx_list[4])
|
|
973
|
+
|
|
974
|
+
return BinOp(left=left_node, op=op, right=right_node, **extract_token_info(ctx))
|
|
975
|
+
|
|
976
|
+
def visitTimeAddAtom(self, ctx: Parser.TimeShiftAtomContext):
|
|
977
|
+
""" """
|
|
978
|
+
|
|
979
|
+
ctx_list = list(ctx.getChildren())
|
|
980
|
+
c = ctx_list[0]
|
|
981
|
+
|
|
982
|
+
op = c.getSymbol().text
|
|
983
|
+
children_node = [self.visitExpr(ctx_list[2])]
|
|
984
|
+
|
|
985
|
+
param_constant_node = []
|
|
986
|
+
|
|
987
|
+
if len(ctx_list) > 4:
|
|
988
|
+
param_constant_node = [self.visitExpr(ctx_list[4])]
|
|
989
|
+
if len(ctx_list) > 6:
|
|
990
|
+
param_constant_node.append(self.visitExpr(ctx_list[6]))
|
|
991
|
+
|
|
992
|
+
return ParamOp(
|
|
993
|
+
op=op, children=children_node, params=param_constant_node, **extract_token_info(ctx)
|
|
994
|
+
)
|
|
995
|
+
|
|
996
|
+
"""
|
|
997
|
+
-----------------------------------
|
|
998
|
+
Conditional Functions
|
|
999
|
+
-----------------------------------
|
|
1000
|
+
"""
|
|
1001
|
+
|
|
1002
|
+
def visitConditionalFunctions(self, ctx: Parser.ConditionalFunctionsContext):
|
|
1003
|
+
if isinstance(ctx, Parser.NvlAtomContext):
|
|
1004
|
+
return self.visitNvlAtom(ctx)
|
|
1005
|
+
else:
|
|
1006
|
+
raise NotImplementedError
|
|
1007
|
+
|
|
1008
|
+
def visitNvlAtom(self, ctx: Parser.NvlAtomContext):
|
|
1009
|
+
ctx_list = list(ctx.getChildren())
|
|
1010
|
+
c = ctx_list[0]
|
|
1011
|
+
|
|
1012
|
+
token = c.getSymbol()
|
|
1013
|
+
|
|
1014
|
+
left_node = self.visitExpr(ctx_list[2])
|
|
1015
|
+
op_node = token.text
|
|
1016
|
+
right_node = self.visitExpr(ctx_list[4])
|
|
1017
|
+
return BinOp(left=left_node, op=op_node, right=right_node, **extract_token_info(ctx))
|
|
1018
|
+
|
|
1019
|
+
"""
|
|
1020
|
+
-----------------------------------
|
|
1021
|
+
Set Functions
|
|
1022
|
+
-----------------------------------
|
|
1023
|
+
"""
|
|
1024
|
+
|
|
1025
|
+
def visitSetFunctions(self, ctx: Parser.SetFunctionsContext):
|
|
1026
|
+
"""
|
|
1027
|
+
setExpr: UNION LPAREN left=expr (COMMA expr)+ RPAREN # unionAtom
|
|
1028
|
+
| INTERSECT LPAREN left=expr (COMMA expr)+ RPAREN # intersectAtom
|
|
1029
|
+
| op=(SETDIFF|SYMDIFF) LPAREN left=expr COMMA right=expr RPAREN # setOrSYmDiffAtom
|
|
1030
|
+
""" # noqa E501
|
|
1031
|
+
if isinstance(ctx, Parser.UnionAtomContext):
|
|
1032
|
+
return self.visitUnionAtom(ctx)
|
|
1033
|
+
elif isinstance(ctx, Parser.IntersectAtomContext):
|
|
1034
|
+
return self.visitIntersectAtom(ctx)
|
|
1035
|
+
elif isinstance(ctx, Parser.SetOrSYmDiffAtomContext):
|
|
1036
|
+
return self.visitSetOrSYmDiffAtom(ctx)
|
|
1037
|
+
else:
|
|
1038
|
+
raise NotImplementedError
|
|
1039
|
+
|
|
1040
|
+
def visitUnionAtom(self, ctx: Parser.UnionAtomContext):
|
|
1041
|
+
ctx_list = list(ctx.getChildren())
|
|
1042
|
+
exprs_nodes = [
|
|
1043
|
+
self.visitExpr(expr) for expr in ctx_list if isinstance(expr, Parser.ExprContext)
|
|
1044
|
+
]
|
|
1045
|
+
|
|
1046
|
+
return MulOp(
|
|
1047
|
+
op=ctx_list[0].getSymbol().text, children=exprs_nodes, **extract_token_info(ctx)
|
|
1048
|
+
)
|
|
1049
|
+
|
|
1050
|
+
def visitIntersectAtom(self, ctx: Parser.IntersectAtomContext):
|
|
1051
|
+
ctx_list = list(ctx.getChildren())
|
|
1052
|
+
exprs_nodes = [
|
|
1053
|
+
self.visitExpr(expr) for expr in ctx_list if isinstance(expr, Parser.ExprContext)
|
|
1054
|
+
]
|
|
1055
|
+
|
|
1056
|
+
return MulOp(
|
|
1057
|
+
op=ctx_list[0].getSymbol().text, children=exprs_nodes, **extract_token_info(ctx)
|
|
1058
|
+
)
|
|
1059
|
+
|
|
1060
|
+
def visitSetOrSYmDiffAtom(self, ctx: Parser.SetOrSYmDiffAtomContext):
|
|
1061
|
+
ctx_list = list(ctx.getChildren())
|
|
1062
|
+
exprs_nodes = [
|
|
1063
|
+
self.visitExpr(expr) for expr in ctx_list if isinstance(expr, Parser.ExprContext)
|
|
1064
|
+
]
|
|
1065
|
+
|
|
1066
|
+
return MulOp(
|
|
1067
|
+
op=ctx_list[0].getSymbol().text, children=exprs_nodes, **extract_token_info(ctx)
|
|
1068
|
+
)
|
|
1069
|
+
|
|
1070
|
+
"""
|
|
1071
|
+
-----------------------------------
|
|
1072
|
+
Hierarchy Functions
|
|
1073
|
+
-----------------------------------
|
|
1074
|
+
"""
|
|
1075
|
+
|
|
1076
|
+
def visitHierarchyFunctions(self, ctx: Parser.HierarchyFunctionsContext):
|
|
1077
|
+
"""
|
|
1078
|
+
HIERARCHY LPAREN op=expr COMMA hrName=IDENTIFIER (conditionClause)? (RULE ruleComponent=componentID)? (validationMode)? (inputModeHierarchy)? outputModeHierarchy? RPAREN
|
|
1079
|
+
""" # noqa E501
|
|
1080
|
+
ctx_list = list(ctx.getChildren())
|
|
1081
|
+
c = ctx_list[0]
|
|
1082
|
+
|
|
1083
|
+
op = c.getSymbol().text
|
|
1084
|
+
dataset_node = self.visitExpr(ctx_list[2])
|
|
1085
|
+
rule_name_node = Identifier(
|
|
1086
|
+
value=ctx_list[4].getSymbol().text,
|
|
1087
|
+
kind="RuleID",
|
|
1088
|
+
**extract_token_info(ctx_list[4].getSymbol()),
|
|
1089
|
+
)
|
|
1090
|
+
|
|
1091
|
+
conditions = []
|
|
1092
|
+
modes = "non_null"
|
|
1093
|
+
inputs = "rule"
|
|
1094
|
+
retains = "computed"
|
|
1095
|
+
rule_comp = None
|
|
1096
|
+
|
|
1097
|
+
for c in ctx_list:
|
|
1098
|
+
if isinstance(c, Parser.ConditionClauseContext):
|
|
1099
|
+
conditions.append(Terminals().visitConditionClause(c))
|
|
1100
|
+
elif isinstance(c, Parser.ComponentIDContext):
|
|
1101
|
+
rule_comp = Terminals().visitComponentID(c)
|
|
1102
|
+
elif isinstance(c, Parser.ValidationModeContext):
|
|
1103
|
+
modes = Terminals().visitValidationMode(c)
|
|
1104
|
+
elif isinstance(c, Parser.InputModeHierarchyContext):
|
|
1105
|
+
inputs = Terminals().visitInputModeHierarchy(c)
|
|
1106
|
+
elif isinstance(c, Parser.OutputModeHierarchyContext):
|
|
1107
|
+
retains = Terminals().visitOutputModeHierarchy(c)
|
|
1108
|
+
|
|
1109
|
+
if len(conditions) != 0:
|
|
1110
|
+
# AST_ASTCONSTRUCTOR.22
|
|
1111
|
+
conditions = conditions[0]
|
|
1112
|
+
|
|
1113
|
+
if inputs == DATASET_PRIORITY:
|
|
1114
|
+
raise NotImplementedError("Dataset Priority input mode on HR is not implemented")
|
|
1115
|
+
param_constant_node = []
|
|
1116
|
+
|
|
1117
|
+
param_constant_node.append(
|
|
1118
|
+
ParamConstant(type_="PARAM_MODE", value=modes, **extract_token_info(ctx))
|
|
1119
|
+
)
|
|
1120
|
+
param_constant_node.append(
|
|
1121
|
+
ParamConstant(type_="PARAM_INPUT", value=inputs, **extract_token_info(ctx))
|
|
1122
|
+
)
|
|
1123
|
+
param_constant_node.append(
|
|
1124
|
+
ParamConstant(type_="PARAM_OUTPUT", value=retains, **extract_token_info(ctx))
|
|
1125
|
+
)
|
|
1126
|
+
|
|
1127
|
+
if not rule_comp and rule_name_node.value in de_ruleset_elements:
|
|
1128
|
+
if isinstance(de_ruleset_elements[rule_name_node.value], list):
|
|
1129
|
+
rule_element = de_ruleset_elements[rule_name_node.value][-1]
|
|
1130
|
+
else:
|
|
1131
|
+
rule_element = de_ruleset_elements[rule_name_node.value]
|
|
1132
|
+
if rule_element.kind == "DatasetID":
|
|
1133
|
+
check_hierarchy_rule = rule_element.value
|
|
1134
|
+
rule_comp = Identifier(
|
|
1135
|
+
value=check_hierarchy_rule, kind="ComponentID", **extract_token_info(ctx)
|
|
1136
|
+
)
|
|
1137
|
+
else: # ValuedomainID
|
|
1138
|
+
raise SemanticError("1-1-10-4", op=op)
|
|
1139
|
+
children = [dataset_node, rule_comp, rule_name_node, *conditions]
|
|
1140
|
+
children = [node for node in children if node is not None]
|
|
1141
|
+
return ParamOp(
|
|
1142
|
+
op=op,
|
|
1143
|
+
children=children,
|
|
1144
|
+
params=param_constant_node,
|
|
1145
|
+
**extract_token_info(ctx),
|
|
1146
|
+
)
|
|
1147
|
+
|
|
1148
|
+
"""
|
|
1149
|
+
-----------------------------------
|
|
1150
|
+
Validation Functions
|
|
1151
|
+
-----------------------------------
|
|
1152
|
+
"""
|
|
1153
|
+
|
|
1154
|
+
def visitValidationFunctions(self, ctx: Parser.ValidationFunctionsContext):
|
|
1155
|
+
if isinstance(ctx, Parser.ValidateDPrulesetContext):
|
|
1156
|
+
return self.visitValidateDPruleset(ctx)
|
|
1157
|
+
elif isinstance(ctx, Parser.ValidateHRrulesetContext):
|
|
1158
|
+
return self.visitValidateHRruleset(ctx)
|
|
1159
|
+
elif isinstance(ctx, Parser.ValidationSimpleContext):
|
|
1160
|
+
return self.visitValidationSimple(ctx)
|
|
1161
|
+
|
|
1162
|
+
def visitValidateDPruleset(self, ctx: Parser.ValidateDPrulesetContext):
|
|
1163
|
+
"""
|
|
1164
|
+
validationDatapoint: CHECK_DATAPOINT '(' expr ',' IDENTIFIER (COMPONENTS componentID (',' componentID)*)? (INVALID|ALL_MEASURES|ALL)? ')' ;
|
|
1165
|
+
""" # noqa E501
|
|
1166
|
+
ctx_list = list(ctx.getChildren())
|
|
1167
|
+
c = ctx_list[0]
|
|
1168
|
+
|
|
1169
|
+
op = c.getSymbol().text
|
|
1170
|
+
|
|
1171
|
+
operand_node = self.visitExpr(ctx_list[2])
|
|
1172
|
+
rule_name = ctx_list[4].getSymbol().text
|
|
1173
|
+
|
|
1174
|
+
components = [
|
|
1175
|
+
Terminals().visitComponentID(comp)
|
|
1176
|
+
for comp in ctx_list
|
|
1177
|
+
if isinstance(comp, Parser.ComponentIDContext)
|
|
1178
|
+
]
|
|
1179
|
+
aux_components = []
|
|
1180
|
+
for x in components:
|
|
1181
|
+
if isinstance(x, BinOp):
|
|
1182
|
+
aux_components.append(x.right.value)
|
|
1183
|
+
else:
|
|
1184
|
+
aux_components.append(x.value)
|
|
1185
|
+
|
|
1186
|
+
components = aux_components
|
|
1187
|
+
|
|
1188
|
+
# Default value for output is invalid.
|
|
1189
|
+
output = "invalid"
|
|
1190
|
+
|
|
1191
|
+
if isinstance(ctx_list[-2], Parser.ValidationOutputContext):
|
|
1192
|
+
output = Terminals().visitValidationOutput(ctx_list[-2])
|
|
1193
|
+
|
|
1194
|
+
return ParamOp(
|
|
1195
|
+
op=op,
|
|
1196
|
+
children=[operand_node, rule_name, *components],
|
|
1197
|
+
params=[output],
|
|
1198
|
+
**extract_token_info(ctx),
|
|
1199
|
+
)
|
|
1200
|
+
|
|
1201
|
+
# TODO Not fully implemented only basic usage available.
|
|
1202
|
+
def visitValidateHRruleset(self, ctx: Parser.ValidateHRrulesetContext):
|
|
1203
|
+
"""
|
|
1204
|
+
CHECK_HIERARCHY LPAREN op=expr COMMA hrName=IDENTIFIER conditionClause? (RULE componentID)? validationMode? inputMode? validationOutput? RPAREN # validateHRruleset
|
|
1205
|
+
""" # noqa E501
|
|
1206
|
+
|
|
1207
|
+
ctx_list = list(ctx.getChildren())
|
|
1208
|
+
c = ctx_list[0]
|
|
1209
|
+
|
|
1210
|
+
op = c.getSymbol().text
|
|
1211
|
+
|
|
1212
|
+
dataset_node = self.visitExpr(ctx_list[2])
|
|
1213
|
+
rule_name_node = Identifier(
|
|
1214
|
+
value=ctx_list[4].getSymbol().text,
|
|
1215
|
+
kind="RuleID",
|
|
1216
|
+
**extract_token_info(ctx_list[4].getSymbol()),
|
|
1217
|
+
)
|
|
1218
|
+
|
|
1219
|
+
conditions = []
|
|
1220
|
+
# Default values
|
|
1221
|
+
modes = "non_null"
|
|
1222
|
+
inputs = "dataset"
|
|
1223
|
+
retains = "invalid"
|
|
1224
|
+
rule_comp = None
|
|
1225
|
+
|
|
1226
|
+
for c in ctx_list:
|
|
1227
|
+
if isinstance(c, Parser.ConditionClauseContext):
|
|
1228
|
+
conditions.append(Terminals().visitConditionClause(c))
|
|
1229
|
+
elif isinstance(c, Parser.ComponentIDContext):
|
|
1230
|
+
rule_comp = Terminals().visitComponentID(c)
|
|
1231
|
+
elif isinstance(c, Parser.ValidationModeContext):
|
|
1232
|
+
modes = Terminals().visitValidationMode(c)
|
|
1233
|
+
elif isinstance(c, Parser.InputModeContext):
|
|
1234
|
+
inputs = Terminals().visitInputMode(c)
|
|
1235
|
+
elif isinstance(c, Parser.ValidationOutputContext):
|
|
1236
|
+
retains = Terminals().visitValidationOutput(c)
|
|
1237
|
+
|
|
1238
|
+
if len(conditions) != 0:
|
|
1239
|
+
# AST_ASTCONSTRUCTOR.22
|
|
1240
|
+
conditions = conditions[0]
|
|
1241
|
+
|
|
1242
|
+
param_constant_node = []
|
|
1243
|
+
|
|
1244
|
+
if inputs == DATASET_PRIORITY:
|
|
1245
|
+
raise NotImplementedError("Dataset Priority input mode on HR is not implemented")
|
|
1246
|
+
|
|
1247
|
+
param_constant_node.append(
|
|
1248
|
+
ParamConstant(type_="PARAM_MODE", value=modes, **extract_token_info(ctx))
|
|
1249
|
+
)
|
|
1250
|
+
param_constant_node.append(
|
|
1251
|
+
ParamConstant(type_="PARAM_INPUT", value=inputs, **extract_token_info(ctx))
|
|
1252
|
+
)
|
|
1253
|
+
param_constant_node.append(
|
|
1254
|
+
ParamConstant(type_="PARAM_OUTPUT", value=retains, **extract_token_info(ctx))
|
|
1255
|
+
)
|
|
1256
|
+
|
|
1257
|
+
if not rule_comp:
|
|
1258
|
+
rule_name = rule_name_node.value
|
|
1259
|
+
if rule_name in de_ruleset_elements:
|
|
1260
|
+
if isinstance(de_ruleset_elements[rule_name], list):
|
|
1261
|
+
rule_element = de_ruleset_elements[rule_name][-1]
|
|
1262
|
+
else:
|
|
1263
|
+
rule_element = de_ruleset_elements[rule_name]
|
|
1264
|
+
|
|
1265
|
+
if rule_element.kind == "DatasetID":
|
|
1266
|
+
check_hierarchy_rule = rule_element.value
|
|
1267
|
+
rule_comp = Identifier(
|
|
1268
|
+
value=check_hierarchy_rule,
|
|
1269
|
+
kind="ComponentID",
|
|
1270
|
+
**extract_token_info(ctx),
|
|
1271
|
+
)
|
|
1272
|
+
else: # ValuedomainID
|
|
1273
|
+
raise SemanticError("1-1-10-4", op=op)
|
|
1274
|
+
children = [dataset_node, rule_comp, rule_name_node, *conditions]
|
|
1275
|
+
children = [node for node in children if node is not None]
|
|
1276
|
+
return ParamOp(
|
|
1277
|
+
op=op,
|
|
1278
|
+
children=children,
|
|
1279
|
+
params=param_constant_node,
|
|
1280
|
+
**extract_token_info(ctx),
|
|
1281
|
+
)
|
|
1282
|
+
|
|
1283
|
+
def visitValidationSimple(self, ctx: Parser.ValidationSimpleContext):
|
|
1284
|
+
"""
|
|
1285
|
+
| CHECK LPAREN op=expr (codeErr=erCode)? (levelCode=erLevel)? imbalanceExpr? output=(INVALID|ALL)? RPAREN # validationSimple
|
|
1286
|
+
""" # noqa E501
|
|
1287
|
+
ctx_list = list(ctx.getChildren())
|
|
1288
|
+
c = ctx_list[0]
|
|
1289
|
+
token = c.getSymbol()
|
|
1290
|
+
|
|
1291
|
+
validation_node = self.visitExpr(ctx_list[2])
|
|
1292
|
+
|
|
1293
|
+
inbalance_node = None
|
|
1294
|
+
error_code = None
|
|
1295
|
+
error_level = None
|
|
1296
|
+
for param in ctx_list:
|
|
1297
|
+
if isinstance(param, Parser.ErCodeContext):
|
|
1298
|
+
error_code = Terminals().visitErCode(param)
|
|
1299
|
+
elif isinstance(param, Parser.ErLevelContext):
|
|
1300
|
+
error_level = Terminals().visitErLevel(param)
|
|
1301
|
+
elif isinstance(param, Parser.ImbalanceExprContext):
|
|
1302
|
+
inbalance_node = self.visitImbalanceExpr(param)
|
|
1303
|
+
|
|
1304
|
+
invalid = ctx_list[-2] if isinstance(ctx_list[-2], TerminalNodeImpl) else None
|
|
1305
|
+
invalid_value = False if invalid is None else invalid.getSymbol().text == "invalid"
|
|
1306
|
+
|
|
1307
|
+
return Validation(
|
|
1308
|
+
op=token.text,
|
|
1309
|
+
validation=validation_node,
|
|
1310
|
+
error_code=error_code,
|
|
1311
|
+
error_level=error_level,
|
|
1312
|
+
imbalance=inbalance_node,
|
|
1313
|
+
invalid=invalid_value,
|
|
1314
|
+
**extract_token_info(ctx),
|
|
1315
|
+
)
|
|
1316
|
+
|
|
1317
|
+
def visitImbalanceExpr(self, ctx: Parser.ImbalanceExprContext):
|
|
1318
|
+
ctx_list = list(ctx.getChildren())
|
|
1319
|
+
return self.visitExpr(ctx_list[1])
|
|
1320
|
+
|
|
1321
|
+
"""
|
|
1322
|
+
-----------------------------------
|
|
1323
|
+
Aggregate Functions
|
|
1324
|
+
-----------------------------------
|
|
1325
|
+
"""
|
|
1326
|
+
|
|
1327
|
+
# TODO Count function count() without parameters. Used at least in aggregations at having.
|
|
1328
|
+
def visitAggregateFunctions(self, ctx: Parser.AggregateFunctionsContext):
|
|
1329
|
+
"""
|
|
1330
|
+
aggrFunction: SUM '(' expr ')'
|
|
1331
|
+
| AVG '(' expr ')'
|
|
1332
|
+
| COUNT '(' expr? ')'
|
|
1333
|
+
| MEDIAN '(' expr ')'
|
|
1334
|
+
| MIN '(' expr ')'
|
|
1335
|
+
| MAX '(' expr ')'
|
|
1336
|
+
| RANK '(' expr ')'
|
|
1337
|
+
| STDDEV_POP '(' expr ')'
|
|
1338
|
+
| STDDEV_SAMP '(' expr ')'
|
|
1339
|
+
| VAR_POP '(' expr ')'
|
|
1340
|
+
| VAR_SAMP '(' expr ')'
|
|
1341
|
+
;
|
|
1342
|
+
"""
|
|
1343
|
+
if isinstance(ctx, Parser.AggrDatasetContext):
|
|
1344
|
+
return self.visitAggrDataset(ctx)
|
|
1345
|
+
else:
|
|
1346
|
+
raise NotImplementedError
|
|
1347
|
+
|
|
1348
|
+
def visitAggrDataset(self, ctx: Parser.AggrDatasetContext):
|
|
1349
|
+
ctx_list = list(ctx.getChildren())
|
|
1350
|
+
# c = ctx_list[0]
|
|
1351
|
+
|
|
1352
|
+
grouping_op = None
|
|
1353
|
+
group_node = None
|
|
1354
|
+
have_node = None
|
|
1355
|
+
|
|
1356
|
+
groups = [group for group in ctx_list if isinstance(group, Parser.GroupingClauseContext)]
|
|
1357
|
+
haves = [have for have in ctx_list if isinstance(have, Parser.HavingClauseContext)]
|
|
1358
|
+
|
|
1359
|
+
op_node = ctx_list[0].getSymbol().text
|
|
1360
|
+
operand = self.visitExpr(ctx_list[2])
|
|
1361
|
+
|
|
1362
|
+
if len(groups) != 0:
|
|
1363
|
+
grouping_op, group_node = self.visitGroupingClause(groups[0])
|
|
1364
|
+
if len(haves) != 0:
|
|
1365
|
+
have_node, expr = self.visitHavingClause(haves[0])
|
|
1366
|
+
have_node.expr = expr
|
|
1367
|
+
|
|
1368
|
+
return Aggregation(
|
|
1369
|
+
op=op_node,
|
|
1370
|
+
operand=operand,
|
|
1371
|
+
grouping_op=grouping_op,
|
|
1372
|
+
grouping=group_node,
|
|
1373
|
+
having_clause=have_node,
|
|
1374
|
+
**extract_token_info(ctx),
|
|
1375
|
+
)
|
|
1376
|
+
|
|
1377
|
+
"""
|
|
1378
|
+
-----------------------------------
|
|
1379
|
+
Analytic Functions
|
|
1380
|
+
-----------------------------------
|
|
1381
|
+
"""
|
|
1382
|
+
|
|
1383
|
+
def visitAnalyticFunctions(self, ctx: Parser.AnalyticFunctionsContext):
|
|
1384
|
+
# ctx_list = list(ctx.getChildren())
|
|
1385
|
+
|
|
1386
|
+
if isinstance(ctx, Parser.AnSimpleFunctionContext):
|
|
1387
|
+
return self.visitAnSimpleFunction(ctx)
|
|
1388
|
+
elif isinstance(ctx, Parser.LagOrLeadAnContext):
|
|
1389
|
+
return self.visitLagOrLeadAn(ctx)
|
|
1390
|
+
elif isinstance(ctx, Parser.RatioToReportAnContext):
|
|
1391
|
+
return self.visitRatioToReportAn(ctx)
|
|
1392
|
+
else:
|
|
1393
|
+
raise NotImplementedError
|
|
1394
|
+
|
|
1395
|
+
def visitAnSimpleFunction(self, ctx: Parser.AnSimpleFunctionContext):
|
|
1396
|
+
ctx_list = list(ctx.getChildren())
|
|
1397
|
+
|
|
1398
|
+
window = None
|
|
1399
|
+
partition_by = None
|
|
1400
|
+
order_by = None
|
|
1401
|
+
|
|
1402
|
+
op_node = ctx_list[0].getSymbol().text
|
|
1403
|
+
operand = self.visitExpr(ctx_list[2])
|
|
1404
|
+
|
|
1405
|
+
for c in ctx_list[5:-2]:
|
|
1406
|
+
if isinstance(c, Parser.PartitionByClauseContext):
|
|
1407
|
+
partition_by = Terminals().visitPartitionByClause(c)
|
|
1408
|
+
continue
|
|
1409
|
+
elif isinstance(c, Parser.OrderByClauseContext):
|
|
1410
|
+
order_by = Terminals().visitOrderByClause(c)
|
|
1411
|
+
continue
|
|
1412
|
+
elif isinstance(c, Parser.WindowingClauseContext):
|
|
1413
|
+
window = Terminals().visitWindowingClause(c)
|
|
1414
|
+
continue
|
|
1415
|
+
else:
|
|
1416
|
+
raise NotImplementedError
|
|
1417
|
+
|
|
1418
|
+
if window is None:
|
|
1419
|
+
window = Windowing(
|
|
1420
|
+
type_="data",
|
|
1421
|
+
start=-1,
|
|
1422
|
+
stop=0,
|
|
1423
|
+
start_mode="preceding",
|
|
1424
|
+
stop_mode="current",
|
|
1425
|
+
**extract_token_info(ctx),
|
|
1426
|
+
)
|
|
1427
|
+
|
|
1428
|
+
return Analytic(
|
|
1429
|
+
op=op_node,
|
|
1430
|
+
operand=operand,
|
|
1431
|
+
partition_by=partition_by,
|
|
1432
|
+
order_by=order_by,
|
|
1433
|
+
window=window,
|
|
1434
|
+
**extract_token_info(ctx),
|
|
1435
|
+
)
|
|
1436
|
+
|
|
1437
|
+
def visitLagOrLeadAn(self, ctx: Parser.LagOrLeadAnContext):
|
|
1438
|
+
ctx_list = list(ctx.getChildren())
|
|
1439
|
+
|
|
1440
|
+
params = None
|
|
1441
|
+
partition_by = None
|
|
1442
|
+
order_by = None
|
|
1443
|
+
|
|
1444
|
+
op_node = ctx_list[0].getSymbol().text
|
|
1445
|
+
operand = self.visitExpr(ctx_list[2])
|
|
1446
|
+
|
|
1447
|
+
for c in ctx_list[4:-2]:
|
|
1448
|
+
if isinstance(c, Parser.PartitionByClauseContext):
|
|
1449
|
+
partition_by = Terminals().visitPartitionByClause(c)
|
|
1450
|
+
continue
|
|
1451
|
+
elif isinstance(c, Parser.OrderByClauseContext):
|
|
1452
|
+
order_by = Terminals().visitOrderByClause(c)
|
|
1453
|
+
continue
|
|
1454
|
+
elif isinstance(c, (Parser.SignedIntegerContext, Parser.ScalarItemContext)):
|
|
1455
|
+
if params is None:
|
|
1456
|
+
params = []
|
|
1457
|
+
if isinstance(c, Parser.SignedIntegerContext):
|
|
1458
|
+
params.append(Terminals().visitSignedInteger(c))
|
|
1459
|
+
else:
|
|
1460
|
+
params.append(Terminals().visitScalarItem(c))
|
|
1461
|
+
continue
|
|
1462
|
+
|
|
1463
|
+
if len(params) == 0:
|
|
1464
|
+
# AST_ASTCONSTRUCTOR.16
|
|
1465
|
+
raise Exception(f"{op_node} requires an offset parameter.")
|
|
1466
|
+
|
|
1467
|
+
return Analytic(
|
|
1468
|
+
op=op_node,
|
|
1469
|
+
operand=operand,
|
|
1470
|
+
partition_by=partition_by,
|
|
1471
|
+
order_by=order_by,
|
|
1472
|
+
params=params,
|
|
1473
|
+
**extract_token_info(ctx),
|
|
1474
|
+
)
|
|
1475
|
+
|
|
1476
|
+
def visitRatioToReportAn(self, ctx: Parser.RatioToReportAnContext):
|
|
1477
|
+
ctx_list = list(ctx.getChildren())
|
|
1478
|
+
|
|
1479
|
+
# params = None
|
|
1480
|
+
order_by = None
|
|
1481
|
+
|
|
1482
|
+
op_node = ctx_list[0].getSymbol().text
|
|
1483
|
+
operand = self.visitExpr(ctx_list[2])
|
|
1484
|
+
|
|
1485
|
+
partition_by = Terminals().visitPartitionByClause(ctx_list[5])
|
|
1486
|
+
|
|
1487
|
+
return Analytic(
|
|
1488
|
+
op=op_node,
|
|
1489
|
+
operand=operand,
|
|
1490
|
+
partition_by=partition_by,
|
|
1491
|
+
order_by=order_by,
|
|
1492
|
+
**extract_token_info(ctx),
|
|
1493
|
+
)
|
|
1494
|
+
|
|
1495
|
+
"""______________________________________________________________________________________
|
|
1496
|
+
|
|
1497
|
+
|
|
1498
|
+
Clause Definition.
|
|
1499
|
+
|
|
1500
|
+
_______________________________________________________________________________________"""
|
|
1501
|
+
|
|
1502
|
+
def visitDatasetClause(self, ctx: Parser.DatasetClauseContext):
|
|
1503
|
+
"""
|
|
1504
|
+
datasetClause:
|
|
1505
|
+
renameClause
|
|
1506
|
+
| aggrClause
|
|
1507
|
+
| filterClause
|
|
1508
|
+
| calcClause
|
|
1509
|
+
| keepClause
|
|
1510
|
+
| dropClause
|
|
1511
|
+
| pivotExpr
|
|
1512
|
+
| unpivotExpr
|
|
1513
|
+
| subspaceExpr
|
|
1514
|
+
;
|
|
1515
|
+
"""
|
|
1516
|
+
ctx_list = list(ctx.getChildren())
|
|
1517
|
+
c = ctx_list[0]
|
|
1518
|
+
|
|
1519
|
+
# RENAME renameClause
|
|
1520
|
+
if isinstance(c, Parser.RenameClauseContext):
|
|
1521
|
+
return self.visitRenameClause(c)
|
|
1522
|
+
|
|
1523
|
+
# aggrClause
|
|
1524
|
+
elif isinstance(c, Parser.AggrClauseContext):
|
|
1525
|
+
return self.visitAggrClause(c)
|
|
1526
|
+
|
|
1527
|
+
# filterClause
|
|
1528
|
+
elif isinstance(c, Parser.FilterClauseContext):
|
|
1529
|
+
return self.visitFilterClause(c)
|
|
1530
|
+
|
|
1531
|
+
# calcClause
|
|
1532
|
+
elif isinstance(c, Parser.CalcClauseContext):
|
|
1533
|
+
return self.visitCalcClause(c)
|
|
1534
|
+
|
|
1535
|
+
# keepClause
|
|
1536
|
+
elif isinstance(c, Parser.KeepOrDropClauseContext):
|
|
1537
|
+
return self.visitKeepOrDropClause(c)
|
|
1538
|
+
|
|
1539
|
+
# pivotExpr
|
|
1540
|
+
elif isinstance(c, Parser.PivotOrUnpivotClauseContext):
|
|
1541
|
+
return self.visitPivotOrUnpivotClause(c)
|
|
1542
|
+
|
|
1543
|
+
# subspaceExpr
|
|
1544
|
+
elif isinstance(c, Parser.SubspaceClauseContext):
|
|
1545
|
+
return self.visitSubspaceClause(c)
|
|
1546
|
+
|
|
1547
|
+
"""
|
|
1548
|
+
-----------------------------------
|
|
1549
|
+
Rename Clause
|
|
1550
|
+
-----------------------------------
|
|
1551
|
+
"""
|
|
1552
|
+
|
|
1553
|
+
def visitRenameClause(self, ctx: Parser.RenameClauseContext):
|
|
1554
|
+
"""
|
|
1555
|
+
renameClause: RENAME renameClauseItem (COMMA renameClauseItem)*;
|
|
1556
|
+
"""
|
|
1557
|
+
ctx_list = list(ctx.getChildren())
|
|
1558
|
+
|
|
1559
|
+
renames = [
|
|
1560
|
+
ctx_child
|
|
1561
|
+
for ctx_child in ctx_list
|
|
1562
|
+
if isinstance(ctx_child, Parser.RenameClauseItemContext)
|
|
1563
|
+
]
|
|
1564
|
+
rename_nodes = []
|
|
1565
|
+
|
|
1566
|
+
for ctx_rename in renames:
|
|
1567
|
+
rename_nodes.append(self.visitRenameClauseItem(ctx_rename))
|
|
1568
|
+
|
|
1569
|
+
return RegularAggregation(
|
|
1570
|
+
op=ctx_list[0].getSymbol().text, children=rename_nodes, **extract_token_info(ctx)
|
|
1571
|
+
)
|
|
1572
|
+
|
|
1573
|
+
def visitRenameClauseItem(self, ctx: Parser.RenameClauseItemContext):
|
|
1574
|
+
"""
|
|
1575
|
+
renameClauseItem: fromName=componentID TO toName=componentID;
|
|
1576
|
+
"""
|
|
1577
|
+
ctx_list = list(ctx.getChildren())
|
|
1578
|
+
|
|
1579
|
+
left_node = Terminals().visitComponentID(ctx_list[0])
|
|
1580
|
+
if isinstance(left_node, BinOp):
|
|
1581
|
+
left_node = f"{left_node.left.value}{left_node.op}{left_node.right.value}"
|
|
1582
|
+
else:
|
|
1583
|
+
left_node = left_node.value
|
|
1584
|
+
|
|
1585
|
+
right_node = Terminals().visitVarID(ctx_list[2]).value
|
|
1586
|
+
|
|
1587
|
+
return RenameNode(old_name=left_node, new_name=right_node, **extract_token_info(ctx))
|
|
1588
|
+
|
|
1589
|
+
"""
|
|
1590
|
+
-----------------------------------
|
|
1591
|
+
Aggregate Clause
|
|
1592
|
+
-----------------------------------
|
|
1593
|
+
"""
|
|
1594
|
+
|
|
1595
|
+
def visitAggregateClause(self, ctx: Parser.AggregateClauseContext):
|
|
1596
|
+
"""
|
|
1597
|
+
aggregateClause: aggrFunctionClause (',' aggrFunctionClause)* ;
|
|
1598
|
+
"""
|
|
1599
|
+
ctx_list = list(ctx.getChildren())
|
|
1600
|
+
|
|
1601
|
+
aggregates_nodes = []
|
|
1602
|
+
|
|
1603
|
+
aggregates = [
|
|
1604
|
+
aggregate
|
|
1605
|
+
for aggregate in ctx_list
|
|
1606
|
+
if isinstance(aggregate, Parser.AggrFunctionClauseContext)
|
|
1607
|
+
]
|
|
1608
|
+
|
|
1609
|
+
for agg in aggregates:
|
|
1610
|
+
aggregates_nodes.append(self.visitAggrFunctionClause(agg))
|
|
1611
|
+
|
|
1612
|
+
return aggregates_nodes
|
|
1613
|
+
|
|
1614
|
+
def visitAggrFunctionClause(self, ctx: Parser.AggrFunctionClauseContext):
|
|
1615
|
+
"""
|
|
1616
|
+
aggrFunctionClause: (componentRole)? componentID ':=' aggrFunction ;
|
|
1617
|
+
"""
|
|
1618
|
+
ctx_list = list(ctx.getChildren())
|
|
1619
|
+
c = ctx_list[0]
|
|
1620
|
+
|
|
1621
|
+
if isinstance(c, Parser.ComponentRoleContext):
|
|
1622
|
+
role = Terminals().visitComponentRole(c)
|
|
1623
|
+
base_index = 1
|
|
1624
|
+
else:
|
|
1625
|
+
base_index = 0
|
|
1626
|
+
role = Role.MEASURE
|
|
1627
|
+
|
|
1628
|
+
left_node = Terminals().visitSimpleComponentId(ctx_list[base_index])
|
|
1629
|
+
op_node = ":="
|
|
1630
|
+
right_node = ExprComp().visitAggregateFunctionsComponents(ctx_list[base_index + 2])
|
|
1631
|
+
# Encoding the role information inside the Assignment for easiness and simplicity.
|
|
1632
|
+
# Cannot find another way with less lines of code
|
|
1633
|
+
left_node.role = role
|
|
1634
|
+
|
|
1635
|
+
return Assignment(left=left_node, op=op_node, right=right_node, **extract_token_info(ctx))
|
|
1636
|
+
|
|
1637
|
+
def visitAggrClause(self, ctx: Parser.AggrClauseContext):
|
|
1638
|
+
"""
|
|
1639
|
+
aggrClause: AGGREGATE aggregateClause (groupingClause havingClause?)? ;
|
|
1640
|
+
"""
|
|
1641
|
+
ctx_list = list(ctx.getChildren())
|
|
1642
|
+
c = ctx_list[0]
|
|
1643
|
+
|
|
1644
|
+
op_node = c.getSymbol().text
|
|
1645
|
+
group_node = None
|
|
1646
|
+
grouping_op = None
|
|
1647
|
+
have_node = None
|
|
1648
|
+
|
|
1649
|
+
groups = [group for group in ctx_list if isinstance(group, Parser.GroupingClauseContext)]
|
|
1650
|
+
haves = [have for have in ctx_list if isinstance(have, Parser.HavingClauseContext)]
|
|
1651
|
+
|
|
1652
|
+
aggregate_nodes = self.visitAggregateClause(ctx_list[1])
|
|
1653
|
+
|
|
1654
|
+
children = []
|
|
1655
|
+
|
|
1656
|
+
if len(groups) != 0:
|
|
1657
|
+
grouping_op, group_node = self.visitGroupingClause(groups[0])
|
|
1658
|
+
if len(haves) > 0:
|
|
1659
|
+
have_node, expr = self.visitHavingClause(haves[0])
|
|
1660
|
+
have_node.expr = expr
|
|
1661
|
+
for element in aggregate_nodes:
|
|
1662
|
+
element.right = Aggregation(
|
|
1663
|
+
op=element.right.op,
|
|
1664
|
+
operand=element.right.operand,
|
|
1665
|
+
grouping_op=grouping_op,
|
|
1666
|
+
grouping=group_node,
|
|
1667
|
+
having_clause=have_node,
|
|
1668
|
+
**extract_token_info(ctx_list[1]),
|
|
1669
|
+
)
|
|
1670
|
+
children.append(copy(element))
|
|
1671
|
+
|
|
1672
|
+
return RegularAggregation(op=op_node, children=children, **extract_token_info(ctx))
|
|
1673
|
+
|
|
1674
|
+
def visitGroupingClause(self, ctx: Parser.GroupingClauseContext):
|
|
1675
|
+
"""
|
|
1676
|
+
groupingClause:
|
|
1677
|
+
GROUP op=(BY | EXCEPT) componentID (COMMA componentID)* # groupByOrExcept
|
|
1678
|
+
| GROUP ALL exprComponent # groupAll
|
|
1679
|
+
;
|
|
1680
|
+
"""
|
|
1681
|
+
if isinstance(ctx, Parser.GroupByOrExceptContext):
|
|
1682
|
+
return self.visitGroupByOrExcept(ctx)
|
|
1683
|
+
elif isinstance(ctx, Parser.GroupAllContext):
|
|
1684
|
+
return self.visitGroupAll(ctx)
|
|
1685
|
+
else:
|
|
1686
|
+
raise NotImplementedError
|
|
1687
|
+
|
|
1688
|
+
def visitHavingClause(self, ctx: Parser.HavingClauseContext):
|
|
1689
|
+
"""
|
|
1690
|
+
havingClause: HAVING exprComponent ;
|
|
1691
|
+
"""
|
|
1692
|
+
ctx_list = list(ctx.getChildren())
|
|
1693
|
+
op_node = ctx_list[0].getSymbol().text
|
|
1694
|
+
|
|
1695
|
+
text = ctx_list[1].start.source[1].strdata
|
|
1696
|
+
expr = re.split("having", text)[1]
|
|
1697
|
+
expr = "having " + expr[:-2].strip()
|
|
1698
|
+
|
|
1699
|
+
if "]" in expr:
|
|
1700
|
+
index = expr.index("]")
|
|
1701
|
+
expr = expr[:index]
|
|
1702
|
+
if "end" in expr:
|
|
1703
|
+
index = expr.index("end")
|
|
1704
|
+
expr = expr[:index]
|
|
1705
|
+
if expr.count(")") > expr.count("("):
|
|
1706
|
+
index = expr.rindex(")")
|
|
1707
|
+
expr = expr[:index]
|
|
1708
|
+
|
|
1709
|
+
if "{" in expr or "}" in expr:
|
|
1710
|
+
expr = expr.replace("{", "(")
|
|
1711
|
+
expr = expr.replace("}", ")")
|
|
1712
|
+
if "not_in" in expr:
|
|
1713
|
+
expr = expr.replace("not_in", "not in")
|
|
1714
|
+
if '"' in expr:
|
|
1715
|
+
expr = expr.replace('"', "'")
|
|
1716
|
+
|
|
1717
|
+
if isinstance(ctx_list[1], Parser.ComparisonExprCompContext):
|
|
1718
|
+
param_nodes = ExprComp().visitComparisonExprComp(ctx_list[1])
|
|
1719
|
+
elif isinstance(ctx_list[1], Parser.InNotInExprCompContext):
|
|
1720
|
+
param_nodes = ExprComp().visitInNotInExprComp(ctx_list[1])
|
|
1721
|
+
elif isinstance(ctx_list[1], Parser.BooleanExprCompContext):
|
|
1722
|
+
param_nodes = ExprComp().visitBooleanExprComp(ctx_list[1])
|
|
1723
|
+
else:
|
|
1724
|
+
raise NotImplementedError
|
|
1725
|
+
|
|
1726
|
+
return ParamOp(
|
|
1727
|
+
op=op_node, children=None, params=param_nodes, **extract_token_info(ctx)
|
|
1728
|
+
), expr
|
|
1729
|
+
|
|
1730
|
+
def visitGroupByOrExcept(self, ctx: Parser.GroupByOrExceptContext):
|
|
1731
|
+
ctx_list = list(ctx.getChildren())
|
|
1732
|
+
|
|
1733
|
+
token_left = ctx_list[0].getSymbol().text
|
|
1734
|
+
token_right = ctx_list[1].getSymbol().text
|
|
1735
|
+
|
|
1736
|
+
op_node = token_left + " " + token_right
|
|
1737
|
+
|
|
1738
|
+
children_nodes = [
|
|
1739
|
+
Terminals().visitComponentID(identifier)
|
|
1740
|
+
for identifier in ctx_list
|
|
1741
|
+
if isinstance(identifier, Parser.ComponentIDContext)
|
|
1742
|
+
]
|
|
1743
|
+
|
|
1744
|
+
return op_node, children_nodes
|
|
1745
|
+
|
|
1746
|
+
def visitGroupAll(self, ctx: Parser.GroupAllContext):
|
|
1747
|
+
ctx_list = list(ctx.getChildren())
|
|
1748
|
+
|
|
1749
|
+
token_left = ctx_list[0].getSymbol().text
|
|
1750
|
+
token_right = ctx_list[1].getSymbol().text
|
|
1751
|
+
|
|
1752
|
+
op_node = token_left + " " + token_right
|
|
1753
|
+
|
|
1754
|
+
children_nodes = [ExprComp().visitExprComponent(ctx_list[2])]
|
|
1755
|
+
|
|
1756
|
+
return op_node, children_nodes
|
|
1757
|
+
|
|
1758
|
+
"""
|
|
1759
|
+
-----------------------------------
|
|
1760
|
+
Filter Clause
|
|
1761
|
+
-----------------------------------
|
|
1762
|
+
"""
|
|
1763
|
+
|
|
1764
|
+
def visitFilterClause(self, ctx: Parser.FilterClauseContext):
|
|
1765
|
+
"""
|
|
1766
|
+
filterClause: FILTER expr;
|
|
1767
|
+
"""
|
|
1768
|
+
ctx_list = list(ctx.getChildren())
|
|
1769
|
+
c = ctx_list[0]
|
|
1770
|
+
token = c.getSymbol()
|
|
1771
|
+
|
|
1772
|
+
op_node = token.text
|
|
1773
|
+
operand_nodes = []
|
|
1774
|
+
operand_nodes.append(ExprComp().visitExprComponent(ctx_list[1]))
|
|
1775
|
+
|
|
1776
|
+
return RegularAggregation(op=op_node, children=operand_nodes, **extract_token_info(ctx))
|
|
1777
|
+
|
|
1778
|
+
"""
|
|
1779
|
+
-----------------------------------
|
|
1780
|
+
Calc Clause
|
|
1781
|
+
-----------------------------------
|
|
1782
|
+
"""
|
|
1783
|
+
|
|
1784
|
+
def visitCalcClause(self, ctx: Parser.CalcClauseContext):
|
|
1785
|
+
"""
|
|
1786
|
+
calcClause: CALC calcClauseItem (',' calcClauseItem)*;
|
|
1787
|
+
"""
|
|
1788
|
+
ctx_list = list(ctx.getChildren())
|
|
1789
|
+
c = ctx_list[0]
|
|
1790
|
+
|
|
1791
|
+
calcClauseItems = [
|
|
1792
|
+
calcClauseItem
|
|
1793
|
+
for calcClauseItem in ctx_list
|
|
1794
|
+
if isinstance(calcClauseItem, Parser.CalcClauseItemContext)
|
|
1795
|
+
]
|
|
1796
|
+
calcClauseItems_nodes = []
|
|
1797
|
+
|
|
1798
|
+
op_node = c.getSymbol().text
|
|
1799
|
+
for calcClauseItem in calcClauseItems:
|
|
1800
|
+
result = self.visitCalcClauseItem(calcClauseItem)
|
|
1801
|
+
calcClauseItems_nodes.append(result)
|
|
1802
|
+
|
|
1803
|
+
return RegularAggregation(
|
|
1804
|
+
op=op_node, children=calcClauseItems_nodes, **extract_token_info(ctx)
|
|
1805
|
+
)
|
|
1806
|
+
|
|
1807
|
+
def visitCalcClauseItem(self, ctx: Parser.CalcClauseItemContext):
|
|
1808
|
+
"""
|
|
1809
|
+
calcClauseItem: (componentRole)? componentID ASSIGN exprComponent;
|
|
1810
|
+
"""
|
|
1811
|
+
ctx_list = list(ctx.getChildren())
|
|
1812
|
+
c = ctx_list[0]
|
|
1813
|
+
|
|
1814
|
+
if isinstance(c, Parser.ComponentRoleContext):
|
|
1815
|
+
role = Terminals().visitComponentRole(c)
|
|
1816
|
+
|
|
1817
|
+
left_node = Terminals().visitComponentID(ctx_list[1])
|
|
1818
|
+
op_node = ":="
|
|
1819
|
+
right_node = ExprComp().visitExprComponent(ctx_list[3])
|
|
1820
|
+
operand_node = Assignment(
|
|
1821
|
+
left=left_node, op=op_node, right=right_node, **extract_token_info(ctx)
|
|
1822
|
+
)
|
|
1823
|
+
if role is None:
|
|
1824
|
+
return UnaryOp(
|
|
1825
|
+
op=Role.MEASURE.value.lower(), operand=operand_node, **extract_token_info(c)
|
|
1826
|
+
)
|
|
1827
|
+
return UnaryOp(op=role.value.lower(), operand=operand_node, **extract_token_info(c))
|
|
1828
|
+
else:
|
|
1829
|
+
left_node = Terminals().visitSimpleComponentId(c)
|
|
1830
|
+
op_node = ":="
|
|
1831
|
+
right_node = ExprComp().visitExprComponent(ctx_list[2])
|
|
1832
|
+
|
|
1833
|
+
operand_node = Assignment(
|
|
1834
|
+
left=left_node, op=op_node, right=right_node, **extract_token_info(ctx)
|
|
1835
|
+
)
|
|
1836
|
+
return UnaryOp(
|
|
1837
|
+
op=Role.MEASURE.value.lower(), operand=operand_node, **extract_token_info(ctx)
|
|
1838
|
+
)
|
|
1839
|
+
|
|
1840
|
+
def visitKeepOrDropClause(self, ctx: Parser.KeepOrDropClauseContext):
|
|
1841
|
+
"""
|
|
1842
|
+
keepOrDropClause: op = (KEEP | DROP) componentID (COMMA componentID)* ;
|
|
1843
|
+
"""
|
|
1844
|
+
|
|
1845
|
+
ctx_list = list(ctx.getChildren())
|
|
1846
|
+
c = ctx_list[0]
|
|
1847
|
+
|
|
1848
|
+
items = [item for item in ctx_list if isinstance(item, Parser.ComponentIDContext)]
|
|
1849
|
+
nodes = []
|
|
1850
|
+
|
|
1851
|
+
op_node = c.getSymbol().text
|
|
1852
|
+
for item in items:
|
|
1853
|
+
nodes.append(Terminals().visitComponentID(item))
|
|
1854
|
+
|
|
1855
|
+
return RegularAggregation(op=op_node, children=nodes, **extract_token_info(ctx))
|
|
1856
|
+
|
|
1857
|
+
"""
|
|
1858
|
+
-----------------------------------
|
|
1859
|
+
Pivot/Unpivot Clause
|
|
1860
|
+
-----------------------------------
|
|
1861
|
+
"""
|
|
1862
|
+
|
|
1863
|
+
def visitPivotOrUnpivotClause(self, ctx: Parser.PivotOrUnpivotClauseContext):
|
|
1864
|
+
"""
|
|
1865
|
+
pivotOrUnpivotClause: op=(PIVOT|UNPIVOT) id_=componentID COMMA mea=componentID ;
|
|
1866
|
+
"""
|
|
1867
|
+
ctx_list = list(ctx.getChildren())
|
|
1868
|
+
c = ctx_list[0]
|
|
1869
|
+
token = c.getSymbol()
|
|
1870
|
+
|
|
1871
|
+
op_node = token.text
|
|
1872
|
+
children_nodes = []
|
|
1873
|
+
children_nodes.append(Terminals().visitComponentID(ctx_list[1]))
|
|
1874
|
+
children_nodes.append(Terminals().visitComponentID(ctx_list[3]))
|
|
1875
|
+
|
|
1876
|
+
return RegularAggregation(op=op_node, children=children_nodes, **extract_token_info(ctx))
|
|
1877
|
+
|
|
1878
|
+
"""
|
|
1879
|
+
-----------------------------------
|
|
1880
|
+
Subspace Clause
|
|
1881
|
+
-----------------------------------
|
|
1882
|
+
"""
|
|
1883
|
+
|
|
1884
|
+
def visitSubspaceClause(self, ctx: Parser.SubspaceClauseContext):
|
|
1885
|
+
"""
|
|
1886
|
+
subspaceClause: SUBSPACE subspaceClauseItem (COMMA subspaceClauseItem)*;"""
|
|
1887
|
+
ctx_list = list(ctx.getChildren())
|
|
1888
|
+
c = ctx_list[0]
|
|
1889
|
+
|
|
1890
|
+
subspace_nodes = []
|
|
1891
|
+
subspaces = [
|
|
1892
|
+
subspace
|
|
1893
|
+
for subspace in ctx_list
|
|
1894
|
+
if isinstance(subspace, Parser.SubspaceClauseItemContext)
|
|
1895
|
+
]
|
|
1896
|
+
|
|
1897
|
+
for subspace in subspaces:
|
|
1898
|
+
subspace_nodes.append(self.visitSubspaceClauseItem(subspace))
|
|
1899
|
+
|
|
1900
|
+
op_node = c.getSymbol().text
|
|
1901
|
+
return RegularAggregation(op=op_node, children=subspace_nodes, **extract_token_info(ctx))
|
|
1902
|
+
|
|
1903
|
+
def visitSubspaceClauseItem(self, ctx: Parser.SubspaceClauseItemContext):
|
|
1904
|
+
ctx_list = list(ctx.getChildren())
|
|
1905
|
+
|
|
1906
|
+
left_node = Terminals().visitVarID(ctx_list[0])
|
|
1907
|
+
op_node = ctx_list[1].getSymbol().text
|
|
1908
|
+
if isinstance(ctx_list[2], Parser.ScalarItemContext):
|
|
1909
|
+
right_node = Terminals().visitScalarItem(ctx_list[2])
|
|
1910
|
+
else:
|
|
1911
|
+
right_node = Terminals().visitVarID(ctx_list[2])
|
|
1912
|
+
return BinOp(left=left_node, op=op_node, right=right_node, **extract_token_info(ctx))
|
|
1913
|
+
|
|
1914
|
+
def visitOptionalExpr(self, ctx: Parser.OptionalExprContext):
|
|
1915
|
+
"""
|
|
1916
|
+
optionalExpr: expr
|
|
1917
|
+
| OPTIONAL ;
|
|
1918
|
+
"""
|
|
1919
|
+
ctx_list = list(ctx.getChildren())
|
|
1920
|
+
c = ctx_list[0]
|
|
1921
|
+
|
|
1922
|
+
if isinstance(c, Parser.ExprContext):
|
|
1923
|
+
return self.visitExpr(c)
|
|
1924
|
+
|
|
1925
|
+
elif isinstance(c, TerminalNodeImpl):
|
|
1926
|
+
token = c.getSymbol()
|
|
1927
|
+
opt = token.text
|
|
1928
|
+
return ID(type_="OPTIONAL", value=opt, **extract_token_info(ctx))
|