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.
Files changed (66) hide show
  1. vtlengine/API/_InternalApi.py +791 -0
  2. vtlengine/API/__init__.py +612 -0
  3. vtlengine/API/data/schema/external_routines_schema.json +34 -0
  4. vtlengine/API/data/schema/json_schema_2.1.json +116 -0
  5. vtlengine/API/data/schema/value_domain_schema.json +97 -0
  6. vtlengine/AST/ASTComment.py +57 -0
  7. vtlengine/AST/ASTConstructor.py +598 -0
  8. vtlengine/AST/ASTConstructorModules/Expr.py +1928 -0
  9. vtlengine/AST/ASTConstructorModules/ExprComponents.py +995 -0
  10. vtlengine/AST/ASTConstructorModules/Terminals.py +790 -0
  11. vtlengine/AST/ASTConstructorModules/__init__.py +50 -0
  12. vtlengine/AST/ASTDataExchange.py +10 -0
  13. vtlengine/AST/ASTEncoders.py +32 -0
  14. vtlengine/AST/ASTString.py +675 -0
  15. vtlengine/AST/ASTTemplate.py +558 -0
  16. vtlengine/AST/ASTVisitor.py +25 -0
  17. vtlengine/AST/DAG/__init__.py +479 -0
  18. vtlengine/AST/DAG/_words.py +10 -0
  19. vtlengine/AST/Grammar/Vtl.g4 +705 -0
  20. vtlengine/AST/Grammar/VtlTokens.g4 +409 -0
  21. vtlengine/AST/Grammar/__init__.py +0 -0
  22. vtlengine/AST/Grammar/lexer.py +2139 -0
  23. vtlengine/AST/Grammar/parser.py +16597 -0
  24. vtlengine/AST/Grammar/tokens.py +169 -0
  25. vtlengine/AST/VtlVisitor.py +824 -0
  26. vtlengine/AST/__init__.py +674 -0
  27. vtlengine/DataTypes/TimeHandling.py +562 -0
  28. vtlengine/DataTypes/__init__.py +863 -0
  29. vtlengine/DataTypes/_time_checking.py +135 -0
  30. vtlengine/Exceptions/__exception_file_generator.py +96 -0
  31. vtlengine/Exceptions/__init__.py +159 -0
  32. vtlengine/Exceptions/messages.py +1004 -0
  33. vtlengine/Interpreter/__init__.py +2048 -0
  34. vtlengine/Model/__init__.py +501 -0
  35. vtlengine/Operators/Aggregation.py +357 -0
  36. vtlengine/Operators/Analytic.py +455 -0
  37. vtlengine/Operators/Assignment.py +23 -0
  38. vtlengine/Operators/Boolean.py +106 -0
  39. vtlengine/Operators/CastOperator.py +451 -0
  40. vtlengine/Operators/Clause.py +366 -0
  41. vtlengine/Operators/Comparison.py +488 -0
  42. vtlengine/Operators/Conditional.py +495 -0
  43. vtlengine/Operators/General.py +191 -0
  44. vtlengine/Operators/HROperators.py +254 -0
  45. vtlengine/Operators/Join.py +447 -0
  46. vtlengine/Operators/Numeric.py +422 -0
  47. vtlengine/Operators/RoleSetter.py +77 -0
  48. vtlengine/Operators/Set.py +176 -0
  49. vtlengine/Operators/String.py +578 -0
  50. vtlengine/Operators/Time.py +1144 -0
  51. vtlengine/Operators/Validation.py +275 -0
  52. vtlengine/Operators/__init__.py +900 -0
  53. vtlengine/Utils/__Virtual_Assets.py +34 -0
  54. vtlengine/Utils/__init__.py +479 -0
  55. vtlengine/__extras_check.py +17 -0
  56. vtlengine/__init__.py +27 -0
  57. vtlengine/files/__init__.py +0 -0
  58. vtlengine/files/output/__init__.py +35 -0
  59. vtlengine/files/output/_time_period_representation.py +55 -0
  60. vtlengine/files/parser/__init__.py +240 -0
  61. vtlengine/files/parser/_rfc_dialect.py +22 -0
  62. vtlengine/py.typed +0 -0
  63. vtlengine-1.4.0rc2.dist-info/METADATA +89 -0
  64. vtlengine-1.4.0rc2.dist-info/RECORD +66 -0
  65. vtlengine-1.4.0rc2.dist-info/WHEEL +4 -0
  66. vtlengine-1.4.0rc2.dist-info/licenses/LICENSE.md +661 -0
@@ -0,0 +1,578 @@
1
+ import operator
2
+ import re
3
+ from typing import Any, Optional, Union
4
+
5
+ # if os.environ.get("SPARK", False):
6
+ # import pyspark.pandas as pd
7
+ # else:
8
+ # import pandas as pd
9
+ import pandas as pd
10
+
11
+ import vtlengine.Operators as Operator
12
+ from vtlengine.AST.Grammar.tokens import (
13
+ CONCAT,
14
+ INSTR,
15
+ LCASE,
16
+ LEN,
17
+ LTRIM,
18
+ REPLACE,
19
+ RTRIM,
20
+ SUBSTR,
21
+ TRIM,
22
+ UCASE,
23
+ )
24
+ from vtlengine.DataTypes import Integer, String, check_unary_implicit_promotion
25
+ from vtlengine.Exceptions import SemanticError
26
+ from vtlengine.Model import DataComponent, Dataset, Scalar
27
+
28
+
29
+ class Unary(Operator.Unary):
30
+ type_to_check = String
31
+
32
+ @classmethod
33
+ def op_func(cls, x: Any) -> Any:
34
+ x = "" if pd.isnull(x) else str(x)
35
+ return cls.py_op(x)
36
+
37
+ @classmethod
38
+ def apply_operation_component(cls, series: Any) -> Any:
39
+ """Applies the operation to a component"""
40
+ return series.map(lambda x: cls.py_op(str(x)), na_action="ignore")
41
+
42
+ @classmethod
43
+ def validate_dataset(cls, dataset: Dataset) -> None:
44
+ """
45
+ Validate that the dataset has exactly one measure.
46
+ """
47
+ measures = dataset.get_measures()
48
+
49
+ if len(measures) != 1:
50
+ raise SemanticError("1-1-18-1", op=cls.op, name=dataset.name)
51
+
52
+
53
+ class Length(Unary):
54
+ op = LEN
55
+ return_type = Integer
56
+ py_op = len
57
+
58
+ @classmethod
59
+ def op_func(cls, x: Any) -> Any:
60
+ result = super().op_func(x)
61
+ if pd.isnull(result):
62
+ return 0
63
+ return result
64
+
65
+ @classmethod
66
+ def apply_operation_component(cls, series: Any) -> Any:
67
+ """Applies the operation to a component"""
68
+ return series.map(cls.op_func)
69
+
70
+
71
+ class Lower(Unary):
72
+ op = LCASE
73
+ py_op = str.lower
74
+ return_type = String
75
+
76
+
77
+ class Upper(Unary):
78
+ op = UCASE
79
+ py_op = str.upper
80
+ return_type = String
81
+
82
+
83
+ class Trim(Unary):
84
+ op = TRIM
85
+ py_op = str.strip
86
+ return_type = String
87
+
88
+
89
+ class Ltrim(Unary):
90
+ op = LTRIM
91
+ py_op = str.lstrip
92
+ return_type = String
93
+
94
+
95
+ class Rtrim(Unary):
96
+ op = RTRIM
97
+ py_op = str.rstrip
98
+ return_type = String
99
+
100
+
101
+ class Binary(Operator.Binary):
102
+ type_to_check = String
103
+
104
+ @classmethod
105
+ def op_func(cls, x: Any, y: Any) -> Any:
106
+ x = "" if pd.isnull(x) else str(x)
107
+ y = "" if pd.isnull(y) else str(y)
108
+ return cls.py_op(x, y)
109
+
110
+
111
+ class Concatenate(Binary):
112
+ op = CONCAT
113
+ py_op = operator.concat
114
+ return_type = String
115
+
116
+
117
+ class Parameterized(Unary):
118
+ @classmethod
119
+ def validate(cls, *args: Any) -> Any:
120
+ operand: Operator.ALL_MODEL_DATA_TYPES
121
+ param1: Optional[Scalar]
122
+ param2: Optional[Scalar]
123
+ operand, param1, param2 = (args + (None, None))[:3]
124
+
125
+ if param1 is not None:
126
+ cls.check_param(param1, 1)
127
+ if param2 is not None:
128
+ cls.check_param(param2, 2)
129
+ return super().validate(operand)
130
+
131
+ @classmethod
132
+ def op_func(cls, *args: Any) -> Any:
133
+ x: Optional[Any]
134
+ param1: Optional[Any]
135
+ param2: Optional[Any]
136
+ x, param1, param2 = (args + (None, None))[:3]
137
+
138
+ x = "" if pd.isnull(x) else x
139
+ return cls.py_op(x, param1, param2)
140
+
141
+ @classmethod
142
+ def apply_operation_two_series(cls, *args: Any) -> Any:
143
+ left_series, right_series = args
144
+
145
+ return left_series.combine(right_series, cls.op_func)
146
+
147
+ @classmethod
148
+ def apply_operation_series_scalar(cls, *args: Any) -> Any:
149
+ series, param1, param2 = args
150
+
151
+ return series.map(lambda x: cls.op_func(x, param1, param2))
152
+
153
+ @classmethod
154
+ def dataset_evaluation(cls, *args: Any) -> Dataset:
155
+ operand: Dataset
156
+ param1: Optional[Union[DataComponent, Scalar]]
157
+ param2: Optional[Union[DataComponent, Scalar]]
158
+ operand, param1, param2 = (args + (None, None))[:3]
159
+
160
+ result = cls.validate(operand, param1, param2)
161
+ result.data = operand.data.copy() if operand.data is not None else pd.DataFrame()
162
+ for measure_name in operand.get_measures_names():
163
+ if isinstance(param1, DataComponent) or isinstance(param2, DataComponent):
164
+ result.data[measure_name] = cls.apply_operation_series(
165
+ result.data[measure_name], param1, param2
166
+ )
167
+ else:
168
+ param_value1 = None if param1 is None else param1.value
169
+ param_value2 = None if param2 is None else param2.value
170
+ result.data[measure_name] = cls.apply_operation_series_scalar(
171
+ result.data[measure_name], param_value1, param_value2
172
+ )
173
+
174
+ cols_to_keep = operand.get_identifiers_names() + operand.get_measures_names()
175
+ result.data = result.data[cols_to_keep]
176
+ cls.modify_measure_column(result)
177
+ return result
178
+
179
+ @classmethod
180
+ def component_evaluation(cls, *args: Any) -> DataComponent:
181
+ operand: DataComponent
182
+ param1: Optional[Union[DataComponent, Scalar]]
183
+ param2: Optional[Union[DataComponent, Scalar]]
184
+ operand, param1, param2 = (args + (None, None))[:3]
185
+
186
+ result = cls.validate(operand, param1, param2)
187
+ result.data = operand.data.copy() if operand.data is not None else pd.Series()
188
+ if isinstance(param1, DataComponent) or isinstance(param2, DataComponent):
189
+ result.data = cls.apply_operation_series(result.data, param1, param2)
190
+ else:
191
+ param_value1 = None if param1 is None else param1.value
192
+ param_value2 = None if param2 is None else param2.value
193
+ result.data = cls.apply_operation_series_scalar(
194
+ operand.data, param_value1, param_value2
195
+ )
196
+ return result
197
+
198
+ @classmethod
199
+ def scalar_evaluation(cls, *args: Any) -> Scalar:
200
+ operand: Scalar
201
+ param1: Optional[Scalar]
202
+ param2: Optional[Scalar]
203
+ operand, param1, param2 = (args + (None, None))[:3]
204
+
205
+ result = cls.validate(operand, param1, param2)
206
+ param_value1 = None if param1 is None else param1.value
207
+ param_value2 = None if param2 is None else param2.value
208
+ result.value = cls.op_func(operand.value, param_value1, param_value2)
209
+ return result
210
+
211
+ @classmethod
212
+ def evaluate(cls, *args: Any) -> Union[Dataset, DataComponent, Scalar]:
213
+ param1: Optional[Union[DataComponent, Scalar]]
214
+ param2: Optional[Union[DataComponent, Scalar]]
215
+ operand, param1, param2 = (args + (None, None))[:3]
216
+
217
+ if isinstance(operand, Dataset):
218
+ return cls.dataset_evaluation(operand, param1, param2)
219
+ if isinstance(operand, DataComponent):
220
+ return cls.component_evaluation(operand, param1, param2)
221
+ return cls.scalar_evaluation(operand, param1, param2)
222
+
223
+ @classmethod
224
+ def check_param(cls, *args: Any) -> None:
225
+ raise Exception("Method should be implemented by inheritors")
226
+
227
+ @classmethod
228
+ def check_param_value(cls, *args: Any) -> None:
229
+ raise Exception("Method should be implemented by inheritors")
230
+
231
+ @classmethod
232
+ def generate_series_from_param(cls, *args: Any) -> Any:
233
+ param: Optional[Union[DataComponent, Scalar]] = None
234
+ length: int
235
+ if len(args) == 2:
236
+ param, length = args
237
+ else:
238
+ length = args[0]
239
+
240
+ if param is None:
241
+ return pd.Series(index=range(length), dtype=object)
242
+ if isinstance(param, Scalar):
243
+ return pd.Series(data=[param.value], index=range(length))
244
+ return param.data
245
+
246
+ @classmethod
247
+ def apply_operation_series(cls, *args: Any) -> Any:
248
+ param1: Optional[Union[DataComponent, Scalar]]
249
+ param2: Optional[Union[DataComponent, Scalar]]
250
+ data, param1, param2 = (args + (None, None))[:3]
251
+
252
+ param1_data = cls.generate_series_from_param(param1, len(data))
253
+ param2_data = cls.generate_series_from_param(param2, len(data))
254
+ df = pd.DataFrame([data, param1_data, param2_data]).T
255
+ n1, n2, n3 = df.columns
256
+ return df.apply(lambda x: cls.op_func(x[n1], x[n2], x[n3]), axis=1)
257
+
258
+
259
+ class Substr(Parameterized):
260
+ op = SUBSTR
261
+ return_type = String
262
+
263
+ @classmethod
264
+ def validate_params(cls, params: Any) -> None:
265
+ if len(params) != 2:
266
+ raise SemanticError("1-1-18-7", op=cls.op, number=len(params), expected=2)
267
+
268
+ @classmethod
269
+ def py_op(cls, x: str, param1: Any, param2: Any) -> Any:
270
+ x = str(x)
271
+ param1 = None if pd.isnull(param1) else int(param1)
272
+ param2 = None if pd.isnull(param2) else int(param2)
273
+ if param1 is None and param2 is None:
274
+ return x
275
+ if param1 is None:
276
+ param1 = 0
277
+ elif param1 != 0:
278
+ param1 -= 1
279
+ elif param1 > (len(x)):
280
+ return ""
281
+ param2 = len(x) if param2 is None or param1 + param2 > len(x) else param1 + param2
282
+ return x[param1:param2]
283
+
284
+ @classmethod
285
+ def check_param(cls, param: Optional[Union[DataComponent, Scalar]], position: int) -> None:
286
+ if not param:
287
+ return
288
+ if position not in (1, 2):
289
+ raise SemanticError("1-1-18-3", op=cls.op, pos=position)
290
+ data_type: Any = param.data_type
291
+
292
+ if not check_unary_implicit_promotion(data_type, Integer):
293
+ raise SemanticError("1-1-18-4", op=cls.op, param_type=cls.op, correct_type="Integer")
294
+
295
+ if isinstance(param, DataComponent):
296
+ if param.data is not None:
297
+ param.data.map(lambda x: cls.check_param_value(x, position))
298
+ else:
299
+ cls.check_param_value(param.value, position)
300
+
301
+ @classmethod
302
+ def check_param_value(cls, param: Optional[Any], position: int) -> None:
303
+ if param is not None:
304
+ if not pd.isnull(param) and not param >= 1 and position == 1:
305
+ raise SemanticError("1-1-18-4", op=cls.op, param_type="Start", correct_type=">= 1")
306
+ elif not pd.isnull(param) and not param >= 0 and position == 2:
307
+ raise SemanticError("1-1-18-4", op=cls.op, param_type="Length", correct_type=">= 0")
308
+
309
+
310
+ class Replace(Parameterized):
311
+ op = REPLACE
312
+ return_type = String
313
+
314
+ @classmethod
315
+ def py_op(cls, x: str, param1: Optional[Any], param2: Optional[Any]) -> Any:
316
+ if pd.isnull(param1):
317
+ return ""
318
+ elif pd.isnull(param2):
319
+ param2 = ""
320
+ x = str(x)
321
+ if param1 is not None and param2 is not None:
322
+ return x.replace(param1, param2)
323
+ return x
324
+
325
+ @classmethod
326
+ def check_param(cls, param: Optional[Union[DataComponent, Scalar]], position: int) -> None:
327
+ if not param:
328
+ return
329
+ if position not in (1, 2):
330
+ raise SemanticError("1-1-18-3", op=cls.op, pos=position)
331
+ data_type: Any = param.data_type
332
+
333
+ if not check_unary_implicit_promotion(data_type, String):
334
+ raise SemanticError("1-1-18-4", op=cls.op, param_type=cls.op, correct_type="String")
335
+
336
+ @classmethod
337
+ def validate_params(cls, params: Any) -> None:
338
+ if len(params) != 2:
339
+ raise SemanticError("1-1-18-7", op=cls.op, number=len(params), expected=2)
340
+
341
+
342
+ class Instr(Parameterized):
343
+ op = INSTR
344
+ return_type = Integer
345
+
346
+ @classmethod
347
+ def validate(
348
+ cls,
349
+ operand: Operator.ALL_MODEL_DATA_TYPES,
350
+ param1: Optional[Operator.ALL_MODEL_DATA_TYPES] = None,
351
+ param2: Optional[Operator.ALL_MODEL_DATA_TYPES] = None,
352
+ param3: Optional[Operator.ALL_MODEL_DATA_TYPES] = None,
353
+ ) -> Any:
354
+ if (
355
+ isinstance(param1, Dataset)
356
+ or isinstance(param2, Dataset)
357
+ or isinstance(param3, Dataset)
358
+ ):
359
+ raise SemanticError("1-1-18-10", op=cls.op)
360
+ if param1 is not None:
361
+ cls.check_param(param1, 1)
362
+ if param2 is not None:
363
+ cls.check_param(param2, 2)
364
+ if param3 is not None:
365
+ cls.check_param(param3, 3)
366
+
367
+ return super().validate(operand)
368
+
369
+ @classmethod
370
+ def validate_params(cls, params: Any) -> None:
371
+ if len(params) != 2:
372
+ raise SemanticError("1-1-18-7", op=cls.op, number=len(params), expected=2)
373
+
374
+ @classmethod
375
+ def check_param(cls, param: Optional[Union[DataComponent, Scalar]], position: int) -> None:
376
+ if not param:
377
+ return
378
+ if position not in (1, 2, 3):
379
+ raise SemanticError("1-1-18-9", op=cls.op)
380
+ data_type: Any = param.data_type
381
+
382
+ if position == 1:
383
+ if not check_unary_implicit_promotion(data_type, String):
384
+ raise SemanticError(
385
+ "1-1-18-4", op=cls.op, param_type="Pattern", correct_type="String"
386
+ )
387
+ elif position == 2:
388
+ if not check_unary_implicit_promotion(data_type, Integer):
389
+ raise SemanticError(
390
+ "1-1-18-4", op=cls.op, param_type="Start", correct_type="Integer"
391
+ )
392
+ else:
393
+ if not check_unary_implicit_promotion(data_type, Integer):
394
+ raise SemanticError(
395
+ "1-1-18-4",
396
+ op=cls.op,
397
+ param_type="Occurrence",
398
+ correct_type="Integer",
399
+ )
400
+ if isinstance(param, DataComponent):
401
+ if param.data is not None:
402
+ param.data.map(lambda x: cls.check_param_value(x, position))
403
+ else:
404
+ cls.check_param_value(param.value, position)
405
+
406
+ @classmethod
407
+ def check_param_value(cls, param: Any, position: int) -> None:
408
+ if position == 2 and not pd.isnull(param) and param < 1:
409
+ raise SemanticError("1-1-18-4", op=cls.op, param_type="Start", correct_type=">= 1")
410
+ elif position == 3 and not pd.isnull(param) and param < 1:
411
+ raise SemanticError("1-1-18-4", op=cls.op, param_type="Occurrence", correct_type=">= 1")
412
+
413
+ @classmethod
414
+ def apply_operation_series_scalar(
415
+ cls, series: Any, param1: Any, param2: Any, param3: Any
416
+ ) -> Any:
417
+ return series.map(lambda x: cls.op_func(x, param1, param2, param3))
418
+
419
+ @classmethod
420
+ def apply_operation_series(
421
+ cls,
422
+ data: Any,
423
+ param1: Optional[Union[DataComponent, Scalar]],
424
+ param2: Optional[Union[DataComponent, Scalar]],
425
+ param3: Optional[Union[DataComponent, Scalar]],
426
+ ) -> Any:
427
+ param1_data = cls.generate_series_from_param(param1, len(data))
428
+ param2_data = cls.generate_series_from_param(param2, len(data))
429
+ param3_data = cls.generate_series_from_param(param3, len(data))
430
+
431
+ df = pd.DataFrame([data, param1_data, param2_data, param3_data]).T
432
+ n1, n2, n3, n4 = df.columns
433
+ return df.apply(lambda x: cls.op_func(x[n1], x[n2], x[n3], x[n4]), axis=1)
434
+
435
+ @classmethod
436
+ def dataset_evaluation( # type: ignore[override]
437
+ cls,
438
+ operand: Dataset,
439
+ param1: Optional[Union[DataComponent, Scalar]],
440
+ param2: Optional[Union[DataComponent, Scalar]],
441
+ param3: Optional[Union[DataComponent, Scalar]],
442
+ ) -> Dataset:
443
+ result = cls.validate(operand, param1, param2, param3)
444
+ result.data = operand.data.copy() if operand.data is not None else pd.DataFrame()
445
+ for measure_name in operand.get_measures_names():
446
+ if (
447
+ isinstance(param1, DataComponent)
448
+ or isinstance(param2, DataComponent)
449
+ or isinstance(param3, DataComponent)
450
+ ):
451
+ if operand.data is not None:
452
+ result.data[measure_name] = cls.apply_operation_series(
453
+ operand.data[measure_name], param1, param2, param3
454
+ )
455
+ else:
456
+ param_value1 = None if param1 is None else param1.value
457
+ param_value2 = None if param2 is None else param2.value
458
+ param_value3 = None if param3 is None else param3.value
459
+ result.data[measure_name] = cls.apply_operation_series_scalar(
460
+ result.data[measure_name], param_value1, param_value2, param_value3
461
+ )
462
+ cols_to_keep = operand.get_identifiers_names() + operand.get_measures_names()
463
+ result.data = result.data[cols_to_keep]
464
+ cls.modify_measure_column(result)
465
+ return result
466
+
467
+ @classmethod
468
+ def component_evaluation( # type: ignore[override]
469
+ cls,
470
+ operand: DataComponent,
471
+ param1: Optional[Union[DataComponent, Scalar]],
472
+ param2: Optional[Union[DataComponent, Scalar]],
473
+ param3: Optional[Union[DataComponent, Scalar]],
474
+ ) -> DataComponent:
475
+ result = cls.validate(operand, param1, param2, param3)
476
+ result.data = operand.data.copy() if operand.data is not None else pd.Series()
477
+ if (
478
+ isinstance(param1, DataComponent)
479
+ or isinstance(param2, DataComponent)
480
+ or isinstance(param3, DataComponent)
481
+ ):
482
+ result.data = cls.apply_operation_series(operand.data, param1, param2, param3)
483
+ else:
484
+ param_value1 = None if param1 is None else param1.value
485
+ param_value2 = None if param2 is None else param2.value
486
+ param_value3 = None if param3 is None else param3.value
487
+ result.data = cls.apply_operation_series_scalar(
488
+ operand.data, param_value1, param_value2, param_value3
489
+ )
490
+ return result
491
+
492
+ @classmethod
493
+ def scalar_evaluation( # type: ignore[override]
494
+ cls,
495
+ operand: Scalar,
496
+ param1: Optional[Scalar],
497
+ param2: Optional[Scalar],
498
+ param3: Optional[Scalar],
499
+ ) -> Scalar:
500
+ result = cls.validate(operand, param1, param2, param3)
501
+ param_value1 = None if param1 is None else param1.value
502
+ param_value2 = None if param2 is None else param2.value
503
+ param_value3 = None if param3 is None else param3.value
504
+ result.value = cls.op_func(operand.value, param_value1, param_value2, param_value3)
505
+ return result
506
+
507
+ @classmethod
508
+ def evaluate(
509
+ cls,
510
+ operand: Operator.ALL_MODEL_DATA_TYPES,
511
+ param1: Optional[Any] = None,
512
+ param2: Optional[Any] = None,
513
+ param3: Optional[Any] = None,
514
+ ) -> Any:
515
+ if isinstance(operand, Dataset):
516
+ return cls.dataset_evaluation(operand, param1, param2, param3)
517
+ if isinstance(operand, DataComponent):
518
+ return cls.component_evaluation(operand, param1, param2, param3)
519
+ if isinstance(operand, Scalar):
520
+ return cls.scalar_evaluation(operand, param1, param2, param3)
521
+
522
+ @classmethod
523
+ def op_func( # type: ignore[override]
524
+ cls,
525
+ x: Any,
526
+ param1: Optional[Any],
527
+ param2: Optional[Any],
528
+ param3: Optional[Any],
529
+ ) -> Any:
530
+ if pd.isnull(x):
531
+ return None
532
+ return cls.py_op(x, param1, param2, param3)
533
+
534
+ @classmethod
535
+ def py_op(
536
+ cls,
537
+ str_value: str,
538
+ str_to_find: Optional[str],
539
+ start: Optional[int],
540
+ occurrence: Optional[int],
541
+ ) -> Any:
542
+ str_value = str(str_value)
543
+ if not pd.isnull(start):
544
+ if isinstance(start, (int, float)):
545
+ start = int(start - 1)
546
+ else:
547
+ # OPERATORS_STRINGOPERATORS.92
548
+ raise SemanticError(
549
+ "1-1-18-4", op=cls.op, param_type="Start", correct_type="Integer"
550
+ )
551
+ else:
552
+ start = 0
553
+
554
+ if not pd.isnull(occurrence):
555
+ if isinstance(occurrence, (int, float)):
556
+ occurrence = int(occurrence - 1)
557
+ else:
558
+ # OPERATORS_STRINGOPERATORS.93
559
+ raise SemanticError(
560
+ "1-1-18-4",
561
+ op=cls.op,
562
+ param_type="Occurrence",
563
+ correct_type="Integer",
564
+ )
565
+ else:
566
+ occurrence = 0
567
+ if pd.isnull(str_to_find):
568
+ return 0
569
+ else:
570
+ str_to_find = str(str_to_find)
571
+
572
+ occurrences_list = [m.start() for m in re.finditer(str_to_find, str_value[start:])]
573
+
574
+ length = len(occurrences_list)
575
+
576
+ position = 0 if occurrence > length - 1 else int(start + occurrences_list[occurrence] + 1)
577
+
578
+ return position