vtlengine 1.0.3rc3__py3-none-any.whl → 1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of vtlengine might be problematic. Click here for more details.

Files changed (53) hide show
  1. vtlengine/API/_InternalApi.py +288 -61
  2. vtlengine/API/__init__.py +269 -71
  3. vtlengine/API/data/schema/json_schema_2.1.json +116 -0
  4. vtlengine/AST/ASTComment.py +56 -0
  5. vtlengine/AST/ASTConstructor.py +76 -22
  6. vtlengine/AST/ASTConstructorModules/Expr.py +238 -120
  7. vtlengine/AST/ASTConstructorModules/ExprComponents.py +126 -61
  8. vtlengine/AST/ASTConstructorModules/Terminals.py +97 -42
  9. vtlengine/AST/ASTConstructorModules/__init__.py +50 -0
  10. vtlengine/AST/ASTEncoders.py +5 -1
  11. vtlengine/AST/ASTString.py +608 -0
  12. vtlengine/AST/ASTTemplate.py +28 -2
  13. vtlengine/AST/DAG/__init__.py +10 -4
  14. vtlengine/AST/Grammar/lexer.py +0 -1
  15. vtlengine/AST/Grammar/parser.py +185 -440
  16. vtlengine/AST/VtlVisitor.py +0 -1
  17. vtlengine/AST/__init__.py +127 -14
  18. vtlengine/DataTypes/TimeHandling.py +50 -15
  19. vtlengine/DataTypes/__init__.py +79 -7
  20. vtlengine/Exceptions/__init__.py +3 -5
  21. vtlengine/Exceptions/messages.py +74 -105
  22. vtlengine/Interpreter/__init__.py +136 -46
  23. vtlengine/Model/__init__.py +14 -11
  24. vtlengine/Operators/Aggregation.py +17 -9
  25. vtlengine/Operators/Analytic.py +64 -20
  26. vtlengine/Operators/Assignment.py +0 -1
  27. vtlengine/Operators/CastOperator.py +44 -44
  28. vtlengine/Operators/Clause.py +16 -10
  29. vtlengine/Operators/Comparison.py +20 -12
  30. vtlengine/Operators/Conditional.py +47 -15
  31. vtlengine/Operators/General.py +9 -4
  32. vtlengine/Operators/HROperators.py +4 -14
  33. vtlengine/Operators/Join.py +15 -14
  34. vtlengine/Operators/Numeric.py +32 -26
  35. vtlengine/Operators/RoleSetter.py +6 -2
  36. vtlengine/Operators/Set.py +12 -8
  37. vtlengine/Operators/String.py +9 -9
  38. vtlengine/Operators/Time.py +145 -124
  39. vtlengine/Operators/Validation.py +10 -4
  40. vtlengine/Operators/__init__.py +56 -69
  41. vtlengine/Utils/__init__.py +55 -1
  42. vtlengine/__extras_check.py +17 -0
  43. vtlengine/__init__.py +2 -2
  44. vtlengine/files/output/__init__.py +2 -1
  45. vtlengine/files/output/_time_period_representation.py +2 -1
  46. vtlengine/files/parser/__init__.py +52 -46
  47. vtlengine/files/parser/_time_checking.py +4 -4
  48. {vtlengine-1.0.3rc3.dist-info → vtlengine-1.1.dist-info}/METADATA +21 -17
  49. vtlengine-1.1.dist-info/RECORD +61 -0
  50. {vtlengine-1.0.3rc3.dist-info → vtlengine-1.1.dist-info}/WHEEL +1 -1
  51. vtlengine/DataTypes/NumericTypesHandling.py +0 -38
  52. vtlengine-1.0.3rc3.dist-info/RECORD +0 -58
  53. {vtlengine-1.0.3rc3.dist-info → vtlengine-1.1.dist-info}/LICENSE.md +0 -0
@@ -12,6 +12,7 @@ from vtlengine.AST import (
12
12
  VarID,
13
13
  Windowing,
14
14
  )
15
+ from vtlengine.AST.ASTConstructorModules import extract_token_info
15
16
  from vtlengine.AST.Grammar.parser import Parser
16
17
  from vtlengine.AST.VtlVisitor import VtlVisitor
17
18
  from vtlengine.DataTypes import (
@@ -37,26 +38,27 @@ def _remove_scaped_characters(text):
37
38
  class Terminals(VtlVisitor):
38
39
  def visitConstant(self, ctx: Parser.ConstantContext):
39
40
  token = ctx.children[0].getSymbol()
41
+ token_info = extract_token_info(token)
40
42
 
41
43
  if token.type == Parser.INTEGER_CONSTANT:
42
- constant_node = Constant("INTEGER_CONSTANT", int(token.text))
44
+ constant_node = Constant(type_="INTEGER_CONSTANT", value=int(token.text), **token_info)
43
45
 
44
46
  elif token.type == Parser.NUMBER_CONSTANT:
45
- constant_node = Constant("FLOAT_CONSTANT", float(token.text))
47
+ constant_node = Constant(type_="FLOAT_CONSTANT", value=float(token.text), **token_info)
46
48
 
47
49
  elif token.type == Parser.BOOLEAN_CONSTANT:
48
50
  if token.text == "true":
49
- constant_node = Constant("BOOLEAN_CONSTANT", True)
51
+ constant_node = Constant(type_="BOOLEAN_CONSTANT", value=True, **token_info)
50
52
  elif token.text == "false":
51
- constant_node = Constant("BOOLEAN_CONSTANT", False)
53
+ constant_node = Constant(type_="BOOLEAN_CONSTANT", value=False, **token_info)
52
54
  else:
53
55
  raise NotImplementedError
54
56
 
55
57
  elif token.type == Parser.STRING_CONSTANT:
56
- constant_node = Constant("STRING_CONSTANT", token.text[1:-1])
58
+ constant_node = Constant(type_="STRING_CONSTANT", value=token.text[1:-1], **token_info)
57
59
 
58
60
  elif token.type == Parser.NULL_CONSTANT:
59
- constant_node = Constant("NULL_CONSTANT", None)
61
+ constant_node = Constant(type_="NULL_CONSTANT", value=None, **token_info)
60
62
 
61
63
  else:
62
64
  raise NotImplementedError
@@ -66,18 +68,19 @@ class Terminals(VtlVisitor):
66
68
  def visitVarID(self, ctx: Parser.VarIDContext):
67
69
  token = ctx.children[0].getSymbol()
68
70
  token.text = _remove_scaped_characters(token.text)
69
- var_id_node = VarID(token.text)
71
+ token_info = extract_token_info(token)
72
+ var_id_node = VarID(value=token.text, **token_info)
70
73
  return var_id_node
71
74
 
72
75
  def visitVarIdExpr(self, ctx: Parser.VarIdExprContext):
73
-
74
76
  if isinstance(ctx.children[0], Parser.VarIDContext):
75
77
  return self.visitVarID(ctx.children[0])
76
78
 
77
79
  token = ctx.children[0].getSymbol()
78
80
  # check token text
79
81
  token.text = _remove_scaped_characters(token.text)
80
- var_id_node = VarID(token.text)
82
+ token_info = extract_token_info(token)
83
+ var_id_node = VarID(value=token.text, **token_info)
81
84
  return var_id_node
82
85
 
83
86
  def visitSimpleComponentId(self, ctx: Parser.SimpleComponentIdContext):
@@ -88,7 +91,7 @@ class Terminals(VtlVisitor):
88
91
  # check token text
89
92
  token.text = _remove_scaped_characters(token.text)
90
93
 
91
- return Identifier(token.text, "ComponentID")
94
+ return Identifier(value=token.text, kind="ComponentID", **extract_token_info(ctx))
92
95
 
93
96
  def visitComponentID(self, ctx: Parser.ComponentIDContext):
94
97
  ctx_list = list(ctx.getChildren())
@@ -99,7 +102,11 @@ class Terminals(VtlVisitor):
99
102
  "'"
100
103
  ): # The component could be imbalance, errorcode or errorlevel
101
104
  component_name = component_name[1:-1]
102
- return Identifier(component_name, "ComponentID")
105
+ return Identifier(
106
+ value=component_name,
107
+ kind="ComponentID",
108
+ **extract_token_info(ctx_list[0].getSymbol()),
109
+ )
103
110
  else:
104
111
  component_name = ctx_list[2].getSymbol().text
105
112
  if component_name.startswith("'") and component_name.endswith(
@@ -108,9 +115,18 @@ class Terminals(VtlVisitor):
108
115
  component_name = component_name[1:-1]
109
116
  op_node = ctx_list[1].getSymbol().text
110
117
  return BinOp(
111
- left=Identifier(ctx_list[0].getSymbol().text, "DatasetID"),
118
+ left=Identifier(
119
+ value=ctx_list[0].getSymbol().text,
120
+ kind="DatasetID",
121
+ **extract_token_info(ctx_list[0].getSymbol()),
122
+ ),
112
123
  op=op_node,
113
- right=Identifier(component_name, "ComponentID"),
124
+ right=Identifier(
125
+ value=component_name,
126
+ kind="ComponentID",
127
+ **extract_token_info(ctx_list[1].getSymbol()),
128
+ ),
129
+ **extract_token_info(ctx),
114
130
  )
115
131
 
116
132
  def visitOperatorID(self, ctx: Parser.OperatorIDContext):
@@ -127,7 +143,11 @@ class Terminals(VtlVisitor):
127
143
  valueDomainID: IDENTIFIER ;
128
144
  """
129
145
  return Collection(
130
- name=ctx.children[0].getSymbol().text, children=[], kind="ValueDomain", type=""
146
+ name=ctx.children[0].getSymbol().text,
147
+ children=[],
148
+ kind="ValueDomain",
149
+ type="",
150
+ **extract_token_info(ctx),
131
151
  )
132
152
 
133
153
  def visitRulesetID(self, ctx: Parser.RulesetIDContext):
@@ -252,7 +272,9 @@ class Terminals(VtlVisitor):
252
272
  for scalar_with_cast in scalars_with_cast:
253
273
  scalar_nodes.append(self.visitScalarWithCast(scalar_with_cast))
254
274
 
255
- return Collection("Set", None, scalar_nodes)
275
+ return Collection(
276
+ name="List", type="Lists", children=scalar_nodes, **extract_token_info(ctx)
277
+ )
256
278
 
257
279
  def visitMultModifier(self, ctx: Parser.MultModifierContext):
258
280
  """
@@ -381,7 +403,7 @@ class Terminals(VtlVisitor):
381
403
  | DATAPOINT_ON_VD (GLPAREN valueDomainName (MUL valueDomainName)* GRPAREN )? # dataPointVd
382
404
  | DATAPOINT_ON_VAR (GLPAREN varID (MUL varID)* GRPAREN )? # dataPointVar
383
405
  ;
384
- """ # noqa E501
406
+ """ # noqa E501
385
407
  # AST_ASTCONSTRUCTOR.54
386
408
  raise NotImplementedError
387
409
 
@@ -391,7 +413,7 @@ class Terminals(VtlVisitor):
391
413
  | HIERARCHICAL_ON_VD ( GLPAREN vdName=IDENTIFIER (LPAREN valueDomainName (MUL valueDomainName)* RPAREN)? GRPAREN )? # hrRulesetVdType
392
414
  | HIERARCHICAL_ON_VAR ( GLPAREN varName=varID (LPAREN varID (MUL varID)* RPAREN)? GRPAREN )? # hrRulesetVarType
393
415
  ;
394
- """ # noqa E501
416
+ """ # noqa E501
395
417
  # AST_ASTCONSTRUCTOR.55
396
418
  raise NotImplementedError
397
419
 
@@ -483,7 +505,6 @@ class Terminals(VtlVisitor):
483
505
  raise NotImplementedError
484
506
 
485
507
  def visitScalarItem(self, ctx: Parser.ScalarItemContext):
486
-
487
508
  ctx_list = list(ctx.getChildren())
488
509
  c = ctx_list[0]
489
510
 
@@ -497,7 +518,7 @@ class Terminals(VtlVisitor):
497
518
  def visitScalarWithCast(self, ctx: Parser.ScalarWithCastContext):
498
519
  """
499
520
  | CAST LPAREN constant COMMA (basicScalarType) (COMMA STRING_CONSTANT)? RPAREN #scalarWithCast # noqa E501
500
- """ # noqa E501
521
+ """ # noqa E501
501
522
  ctx_list = list(ctx.getChildren())
502
523
  c = ctx_list[0]
503
524
 
@@ -507,12 +528,22 @@ class Terminals(VtlVisitor):
507
528
  const_node = self.visitConstant(ctx_list[2])
508
529
  basic_scalar_type = [self.visitBasicScalarType(ctx_list[4])]
509
530
 
510
- param_node = [ParamConstant("PARAM_CAST", ctx_list[6])] if len(ctx_list) > 6 else []
531
+ param_node = (
532
+ [
533
+ ParamConstant(
534
+ type_="PARAM_CAST", value=ctx_list[6], **extract_token_info(ctx_list[6])
535
+ )
536
+ ]
537
+ if len(ctx_list) > 6
538
+ else []
539
+ )
511
540
 
512
541
  if len(basic_scalar_type) == 1:
513
542
  children_nodes = [const_node, basic_scalar_type[0]]
514
543
 
515
- return ParamOp(op=op, children=children_nodes, params=param_node)
544
+ return ParamOp(
545
+ op=op, children=children_nodes, params=param_node, **extract_token_info(ctx)
546
+ )
516
547
 
517
548
  else:
518
549
  # AST_ASTCONSTRUCTOR.14
@@ -535,14 +566,20 @@ class Terminals(VtlVisitor):
535
566
 
536
567
  if token.type == Parser.BOOLEAN_CONSTANT:
537
568
  if token.text == "true":
538
- param_constant_node = Constant("BOOLEAN_CONSTANT", True)
569
+ param_constant_node = Constant(
570
+ type_="BOOLEAN_CONSTANT", value=True, **extract_token_info(token)
571
+ )
539
572
  elif token.text == "false":
540
- param_constant_node = Constant("BOOLEAN_CONSTANT", False)
573
+ param_constant_node = Constant(
574
+ type_="BOOLEAN_CONSTANT", value=False, **extract_token_info(token)
575
+ )
541
576
  else:
542
577
  raise NotImplementedError
543
578
 
544
579
  elif token.type == Parser.ALL:
545
- param_constant_node = ParamConstant("PARAM_CONSTANT", token.text)
580
+ param_constant_node = ParamConstant(
581
+ type_="PARAM_CONSTANT", value=token.text, **extract_token_info(token)
582
+ )
546
583
 
547
584
  else:
548
585
  raise NotImplementedError
@@ -564,7 +601,7 @@ class Terminals(VtlVisitor):
564
601
  return ctx.children[0].getSymbol().text
565
602
 
566
603
  def visitSignedInteger(self, ctx: Parser.SignedIntegerContext):
567
- return ctx.children[0].getSymbol().text
604
+ return int(ctx.children[0].getSymbol().text)
568
605
 
569
606
  def visitComparisonOperand(self, ctx: Parser.ComparisonOperandContext):
570
607
  return ctx.children[0].getSymbol().text
@@ -593,8 +630,9 @@ class Terminals(VtlVisitor):
593
630
 
594
631
  def visitSignature(self, ctx: Parser.SignatureContext, kind="ComponentID"):
595
632
  """
596
- varID (AS alias)?
633
+ VarID (AS alias)?
597
634
  """
635
+ token_info = extract_token_info(ctx)
598
636
 
599
637
  ctx_list = list(ctx.getChildren())
600
638
  c = ctx_list[0]
@@ -604,10 +642,10 @@ class Terminals(VtlVisitor):
604
642
  alias_name = None
605
643
 
606
644
  if len(ctx_list) == 1:
607
- return DPRIdentifier(value=node_name, kind=kind, alias=alias_name)
645
+ return DPRIdentifier(value=node_name, kind=kind, alias=alias_name, **token_info)
608
646
 
609
647
  alias_name = self.visitAlias(ctx_list[2])
610
- return DPRIdentifier(value=node_name, kind=kind, alias=alias_name)
648
+ return DPRIdentifier(value=node_name, kind=kind, alias=alias_name, **token_info)
611
649
 
612
650
  """
613
651
  From Hierarchical
@@ -662,6 +700,8 @@ class Terminals(VtlVisitor):
662
700
 
663
701
  win_mode = ctx_list[0].getSymbol().text # Windowing mode (data points | range )
664
702
 
703
+ token_info = extract_token_info(ctx)
704
+
665
705
  if win_mode == "data":
666
706
  num_rows_1, mode_1 = self.visitLimitClauseItem(ctx_list[3])
667
707
  num_rows_2, mode_2 = self.visitLimitClauseItem(ctx_list[5])
@@ -672,8 +712,12 @@ class Terminals(VtlVisitor):
672
712
  first = num_rows_1 # unbounded (default value)
673
713
  second = num_rows_2 # current data point (default value)
674
714
 
675
- if (mode_2 == "preceding" and mode_1 == "preceding" and num_rows_1 == -1
676
- and num_rows_2 == -1): # preceding and preceding (error)
715
+ if (
716
+ mode_2 == "preceding"
717
+ and mode_1 == "preceding"
718
+ and num_rows_1 == -1
719
+ and num_rows_2 == -1
720
+ ): # preceding and preceding (error)
677
721
  raise Exception(
678
722
  f"Cannot have 2 preceding clauses with unbounded in analytic clause, "
679
723
  f"line {ctx_list[3].start.line}"
@@ -689,30 +733,36 @@ class Terminals(VtlVisitor):
689
733
 
690
734
  if mode_1 == mode_2:
691
735
  if mode_1 == "preceding" and first != -1 and second > first: # 3 and 1: must be [-3:-1]
692
- return create_windowing(win_mode, [second, first], [mode_2, mode_1])
736
+ return create_windowing(win_mode, [second, first], [mode_2, mode_1], token_info)
693
737
  if mode_1 == "preceding" and second == -1:
694
- return create_windowing(win_mode, [second, first], [mode_2, mode_1])
738
+ return create_windowing(win_mode, [second, first], [mode_2, mode_1], token_info)
695
739
  if mode_1 == "following" and second != -1 and second < first: # 3 and 1: must be [1:3]
696
- return create_windowing(win_mode, [second, first], [mode_2, mode_1])
740
+ return create_windowing(win_mode, [second, first], [mode_2, mode_1], token_info)
697
741
  if mode_1 == "following" and first == -1:
698
- return create_windowing(win_mode, [second, first], [mode_2, mode_1])
742
+ return create_windowing(win_mode, [second, first], [mode_2, mode_1], token_info)
699
743
 
700
- return create_windowing(win_mode, [first, second], [mode_1, mode_2])
744
+ return create_windowing(win_mode, [first, second], [mode_1, mode_2], token_info)
701
745
 
702
746
  def visitOrderByItem(self, ctx: Parser.OrderByItemContext):
703
747
  ctx_list = list(ctx.getChildren())
704
748
 
749
+ token_info = extract_token_info(ctx)
750
+
705
751
  if len(ctx_list) == 1:
706
- return OrderBy(component=self.visitComponentID(ctx_list[0]).value, order="asc")
752
+ return OrderBy(
753
+ component=self.visitComponentID(ctx_list[0]).value, order="asc", **token_info
754
+ )
707
755
 
708
756
  return OrderBy(
709
- component=self.visitComponentID(ctx_list[0]).value, order=ctx_list[1].getSymbol().text
757
+ component=self.visitComponentID(ctx_list[0]).value,
758
+ order=ctx_list[1].getSymbol().text,
759
+ **token_info,
710
760
  )
711
761
 
712
762
  def visitLimitClauseItem(self, ctx: Parser.LimitClauseItemContext):
713
763
  ctx_list = list(ctx.getChildren())
714
764
  c = ctx_list[0]
715
- if c.getSymbol().text == "unbounded":
765
+ if c.getSymbol().text.lower() == "unbounded":
716
766
  result = -1
717
767
  elif c.getSymbol().text == "current":
718
768
  result = 0
@@ -727,13 +777,18 @@ class Terminals(VtlVisitor):
727
777
  return result, ctx_list[1].getSymbol().text
728
778
 
729
779
 
730
- def create_windowing(win_mode, values, modes):
780
+ def create_windowing(win_mode, values, modes, token_info):
731
781
  for e in range(0, 2):
732
782
  if values[e] == -1:
733
- values[e] = "UNBOUNDED"
783
+ values[e] = "unbounded"
734
784
  elif values[e] == 0:
735
- values[e] = "CURRENT ROW"
785
+ values[e] = "current row"
736
786
 
737
787
  return Windowing(
738
- type_=win_mode, start=values[0], stop=values[1], start_mode=modes[0], stop_mode=modes[1]
788
+ type_=win_mode,
789
+ start=values[0],
790
+ stop=values[1],
791
+ start_mode=modes[0],
792
+ stop_mode=modes[1],
793
+ **token_info,
739
794
  )
@@ -0,0 +1,50 @@
1
+ from typing import Dict, Union
2
+
3
+ from antlr4.ParserRuleContext import ParserRuleContext
4
+ from antlr4.Token import CommonToken
5
+
6
+ from vtlengine.AST.Grammar.lexer import Lexer
7
+
8
+
9
+ def extract_token_info(token: Union[CommonToken, ParserRuleContext]) -> Dict[str, int]:
10
+ """
11
+ Extracts the token information from a token or ParserRuleContext.
12
+
13
+ The Token information includes:
14
+ - column_start: The starting column of the token.
15
+ - column_stop: The stopping column of the token.
16
+ - line_start: The starting line number of the token.
17
+ - line_stop: The stopping line number of the token.
18
+
19
+ The overall idea is to provide the information from which line and column,
20
+ and to which line and column, the text is referenced by the AST object, including children.
21
+
22
+ Important Note: the keys of the dict are the same as the class attributes of the AST Object.
23
+
24
+ Args:
25
+ token (Union[CommonToken, ParserRuleContext]): The token or ParserRuleContext to extract
26
+ information from.
27
+
28
+ Returns:
29
+ Dict[str, int]: A dictionary containing the token information.
30
+ """
31
+
32
+ if isinstance(token, ParserRuleContext):
33
+ return {
34
+ "column_start": token.start.column,
35
+ "column_stop": token.stop.column + len(token.stop.text),
36
+ "line_start": token.start.line,
37
+ "line_stop": token.stop.line,
38
+ }
39
+ line_start = token.line
40
+ line_stop = token.line
41
+ # For block comments, we need to add the lines inside the block, marked by \n, to the stop line.
42
+ # The ML_COMMENT does not take into account the final \n in its grammar.
43
+ if token.type == Lexer.ML_COMMENT:
44
+ line_stop = token.line + token.text.count("\n")
45
+ return {
46
+ "column_start": token.column,
47
+ "column_stop": token.column + len(token.text),
48
+ "line_start": line_start,
49
+ "line_stop": line_stop,
50
+ }
@@ -1,12 +1,16 @@
1
1
  import json
2
2
 
3
- import AST
3
+ from vtlengine import AST
4
+ from vtlengine.Model import Dataset
4
5
 
5
6
 
6
7
  class ComplexEncoder(json.JSONEncoder):
7
8
  def default(self, obj):
8
9
  if hasattr(obj, "toJSON"):
9
10
  return obj.toJSON()
11
+ # Makes a circular reference error if we do not check for this
12
+ elif isinstance(obj, Dataset):
13
+ return "dataset"
10
14
  else:
11
15
  return json.__dict__
12
16