@platforma-sdk/workflow-tengo 5.3.3 → 5.3.4

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 (52) hide show
  1. package/dist/index.cjs +2 -2
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.js +2 -2
  4. package/dist/tengo/lib/exec.lib.tengo +4 -0
  5. package/dist/tengo/lib/exec.monetization_internal.lib.tengo +8 -0
  6. package/dist/tengo/lib/pframes.spec.lib.tengo +6 -0
  7. package/dist/tengo/lib/pframes.util.lib.tengo +404 -1
  8. package/dist/tengo/lib/pframes.xsv.lib.tengo +117 -65
  9. package/dist/tengo/lib/pt.expression.lib.tengo +828 -268
  10. package/dist/tengo/lib/pt.lib.tengo +567 -216
  11. package/dist/tengo/lib/pt.util.lib.tengo +231 -15
  12. package/dist/tengo/lib/slices.lib.tengo +20 -1
  13. package/dist/tengo/lib/smart.lib.tengo +0 -1
  14. package/dist/tengo/tpl/anonymize.deanonymize.plj.gz +0 -0
  15. package/dist/tengo/tpl/anonymize.impl.plj.gz +0 -0
  16. package/dist/tengo/tpl/check_network.create_workdir_from_storage.plj.gz +0 -0
  17. package/dist/tengo/tpl/check_network.download_blob.plj.gz +0 -0
  18. package/dist/tengo/tpl/check_network.run_hello_world.plj.gz +0 -0
  19. package/dist/tengo/tpl/check_network.run_hello_world_py.plj.gz +0 -0
  20. package/dist/tengo/tpl/check_network.upload_blob.plj.gz +0 -0
  21. package/dist/tengo/tpl/check_network.upload_template.plj.gz +0 -0
  22. package/dist/tengo/tpl/check_network.workdir_type_name_extractor.plj.gz +0 -0
  23. package/dist/tengo/tpl/exec.R.create-renv.plj.gz +0 -0
  24. package/dist/tengo/tpl/exec.delayed-compute-allocation.impl.plj.gz +0 -0
  25. package/dist/tengo/tpl/exec.exec-impl.plj.gz +0 -0
  26. package/dist/tengo/tpl/exec.exec.plj.gz +0 -0
  27. package/dist/tengo/tpl/exec.load-software.impl.plj.gz +0 -0
  28. package/dist/tengo/tpl/exec.monetization_info.plj.gz +0 -0
  29. package/dist/tengo/tpl/exec.monetization_to_json.plj.gz +0 -0
  30. package/dist/tengo/tpl/exec.python.create-venv.plj.gz +0 -0
  31. package/dist/tengo/tpl/exec.python.install-deps.plj.gz +0 -0
  32. package/dist/tengo/tpl/exec.python.venv-bin-path.plj.gz +0 -0
  33. package/dist/tengo/tpl/ll.get-field.plj.gz +0 -0
  34. package/dist/tengo/tpl/pframes.export-multiple-pcolumns.plj.gz +0 -0
  35. package/dist/tengo/tpl/pframes.export-pframe-for-ui.plj.gz +0 -0
  36. package/dist/tengo/tpl/pframes.export-pframe.plj.gz +0 -0
  37. package/dist/tengo/tpl/pframes.export-single-pcolumn.plj.gz +0 -0
  38. package/dist/tengo/tpl/pframes.import-dir.plj.gz +0 -0
  39. package/dist/tengo/tpl/pframes.parse-to-json.plj.gz +0 -0
  40. package/dist/tengo/tpl/pframes.process-pcolumn-data.plj.gz +0 -0
  41. package/dist/tengo/tpl/pframes.remove-nulls.plj.gz +0 -0
  42. package/dist/tengo/tpl/pframes.slice-data.plj.gz +0 -0
  43. package/dist/tengo/tpl/pframes.xsv-import-if-not-null.plj.gz +0 -0
  44. package/dist/tengo/tpl/pframes.xsv-import-map.plj.gz +0 -0
  45. package/dist/tengo/tpl/pt.import-dir.plj.gz +0 -0
  46. package/dist/tengo/tpl/pt.workflow-run.plj.gz +0 -0
  47. package/dist/tengo/tpl/waiter.waiter.plj.gz +0 -0
  48. package/dist/tengo/tpl/workdir.save.plj.gz +0 -0
  49. package/dist/tengo/tpl/workflow.build-ctx.plj.gz +0 -0
  50. package/dist/tengo/tpl/workflow.query-anchored.plj.gz +0 -0
  51. package/package.json +3 -3
  52. package/dist/tengo/tpl/pframes.xsv-export-pframe.plj.gz +0 -0
@@ -3,11 +3,15 @@ maps := import("@platforma-sdk/workflow-tengo:maps")
3
3
  slices := import("@platforma-sdk/workflow-tengo:slices")
4
4
  validation := import("@platforma-sdk/workflow-tengo:validation")
5
5
  fmt := import("fmt")
6
+ objects := import("@platforma-sdk/workflow-tengo:objects")
7
+ oop := import("@platforma-sdk/workflow-tengo:oop")
8
+ pframesSpec := import("@platforma-sdk/workflow-tengo:pframes.spec")
6
9
 
7
10
 
8
11
 
9
12
  _newExpression := undefined
10
13
  _newWindowExpression := undefined
14
+ axis := undefined
11
15
  col := undefined
12
16
  lit := undefined
13
17
  allHorizontal := undefined
@@ -37,16 +41,16 @@ _validateOptions := func(options, schema, functionName) {
37
41
 
38
42
 
39
43
  _isExpression := func(item) {
40
- return ll.isMap(item) && ll.methodExists(item, "getExpression")
44
+ return ll.isMap(item) && ll.methodExists(item, "_getExpression")
41
45
  }
42
46
 
43
47
 
44
48
  _isAggregation := func(item) {
45
- return ll.isMap(item) && ll.methodExists(item, "getAggregation")
49
+ return ll.isMap(item) && ll.methodExists(item, "_getAggregation")
46
50
  }
47
51
 
48
52
 
49
- _mapToExpressionStructList := func(itemsList, stringInterpretation) {
53
+ _mapToExpressionStructList := func(itemsList, stringInterpretation, ctx) {
50
54
  if !is_array(itemsList) {
51
55
  itemsList = [itemsList] // Allow single item to be passed
52
56
  }
@@ -57,12 +61,12 @@ _mapToExpressionStructList := func(itemsList, stringInterpretation) {
57
61
  return slices.map(itemsList, func(item) {
58
62
  if is_string(item) {
59
63
  if stringInterpretation == "col" {
60
- return col(item).getExpression()
64
+ return col(item)._getExpression(ctx)
61
65
  } else { // Must be "lit" due to the check above
62
- return lit(item).getExpression()
66
+ return lit(item)._getExpression(ctx)
63
67
  }
64
68
  } else if _isExpression(item) {
65
- return item.getExpression()
69
+ return item._getExpression(ctx)
66
70
  } else {
67
71
  ll.panic("Invalid item type in list: %T. Must be a string (interpreted as '%s') or an expression object.", item, stringInterpretation)
68
72
  return undefined // Unreachable
@@ -70,16 +74,16 @@ _mapToExpressionStructList := func(itemsList, stringInterpretation) {
70
74
  })
71
75
  }
72
76
 
73
- _mapToSingleExpressionStruct := func(item, stringInterpretation) {
77
+ _mapToSingleExpressionStruct := func(item, stringInterpretation, ctx) {
74
78
  if _isExpression(item) {
75
- return item.getExpression()
79
+ return item._getExpression(ctx)
76
80
  } else if is_int(item) || is_float(item) || is_bool(item) { // These are always literals
77
- return lit(item).getExpression()
81
+ return lit(item)._getExpression(ctx)
78
82
  } else if is_string(item) {
79
83
  if stringInterpretation == "col" {
80
- return col(item).getExpression()
84
+ return col(item)._getExpression(ctx)
81
85
  } else if stringInterpretation == "lit" {
82
- return lit(item).getExpression()
86
+ return lit(item)._getExpression(ctx)
83
87
  } else { // stringInterpretation is undefined or an invalid value
84
88
  ll.panic("String item '%v' provided, but its interpretation as 'col' or 'lit' was not specified or is not applicable in this context. (stringInterpretation: '%v')", item, stringInterpretation)
85
89
  return undefined
@@ -99,26 +103,28 @@ _mapToSingleExpressionStruct := func(item, stringInterpretation) {
99
103
  _newWindowExpression = func(expression, aggregation, currentAlias) {
100
104
  self := undefined
101
105
  self = ll.toStrict({
102
-
103
- getExpression: func() {
104
- if expression == undefined {
105
- ll.panic("Using this operation as a window expression is not allowed. Use it as a group by expression instead. %v", aggregation)
106
- }
107
- return expression
106
+
107
+
108
+
109
+
110
+ _getExpression: func(ctx) {
111
+ ll.assert(is_map(ctx), "ctx must be a map")
112
+ ll.assert(!is_undefined(expression), "Using this operation as a window expression is not allowed. Use it as a group by expression instead. %v", aggregation)
113
+ ll.assert(!is_undefined(currentAlias), "Alias is not defined for this window expression.")
114
+
115
+ valueExpr := expression(ctx)
116
+ return {type: "alias", value: valueExpr, name: currentAlias}
108
117
  },
109
118
 
110
- getAggregation: func() {
119
+ _getAggregation: func(ctx) {
111
120
  if aggregation == undefined {
112
121
  ll.panic("Using this operation as a group by expression is not allowed, most probably because you applied .over() to it. %v", expression)
113
122
  }
114
- return maps.deepMerge(aggregation, {name: self.getAlias()})
115
- },
116
-
117
- getAlias: func() {
118
123
  if currentAlias == undefined {
119
124
  ll.panic("Alias is not defined for this window expression.")
120
125
  }
121
- return currentAlias
126
+ aggStruct := aggregation(ctx)
127
+ return maps.deepMerge(aggStruct, {name: currentAlias})
122
128
  },
123
129
 
124
130
 
@@ -128,6 +134,7 @@ _newWindowExpression = func(expression, aggregation, currentAlias) {
128
134
 
129
135
 
130
136
  alias: func(newAlias) {
137
+ ll.assert(is_string(newAlias), "alias must be a string")
131
138
  return _newWindowExpression(expression, aggregation, newAlias)
132
139
  },
133
140
 
@@ -139,15 +146,20 @@ _newWindowExpression = func(expression, aggregation, currentAlias) {
139
146
  over: func(partitionBy) {
140
147
  ll.assert(is_array(partitionBy) || is_string(partitionBy) || _isExpression(partitionBy), "partitionBy must be an array of strings/expressions or a single string/expression")
141
148
 
142
- if len(expression.partitionBy) > 0 {
143
- ll.warn("partitionBy already set on this window expression, overriding.")
144
- }
145
149
 
146
- newExpression := maps.deepMerge(expression, {
147
- partitionBy: _mapToExpressionStructList(partitionBy, "col")
148
- })
150
+ newExpr := func(ctx) {
151
+ baseExpr := expression(ctx)
149
152
 
150
- return _newWindowExpression(newExpression, undefined, currentAlias)
153
+ if len(baseExpr.partitionBy) > 0 {
154
+ ll.warn("partitionBy already set on this window expression, overriding.")
155
+ }
156
+
157
+ return maps.deepMerge(baseExpr, {
158
+ partitionBy: _mapToExpressionStructList(partitionBy, "col", ctx)
159
+ })
160
+ }
161
+
162
+ return _newWindowExpression(newExpr, undefined, currentAlias)
151
163
  }
152
164
  })
153
165
  return self
@@ -157,48 +169,58 @@ _newWindowExpression = func(expression, aggregation, currentAlias) {
157
169
 
158
170
 
159
171
 
160
-
161
- _newExpression = func(expression, currentAlias) {
172
+ _newExpression = func(expression) {
162
173
  self := undefined // forward declaration
163
174
 
164
175
 
165
176
  binaryOp := func(opType, other) {
166
- rhsExprStruct := undefined
167
- if _isExpression(other) {
168
- rhsExprStruct = other.getExpression()
169
- } else if is_int(other) || is_float(other) || is_bool(other) || is_string(other) {
170
- rhsExprStruct = lit(other).getExpression()
171
- } else {
172
- ll.panic(opType + " expects an expression object or a literal (number, boolean, null) as argument. Got type: %T", other)
173
- }
174
-
175
177
  return _newExpression(
176
- {type: opType, lhs: expression, rhs: rhsExprStruct},
177
- undefined // Result of operation typically doesn't carry forward alias
178
+ func(ctx) {
179
+ lhsExpr := self._getExpression(ctx)
180
+ rhsExpr := undefined
181
+ if _isExpression(other) {
182
+ rhsExpr = other._getExpression(ctx)
183
+ } else if is_int(other) || is_float(other) || is_bool(other) || is_string(other) {
184
+ rhsExpr = lit(other)._getExpression(ctx)
185
+ } else {
186
+ ll.panic(opType + " expects an expression object or a literal (number, boolean, null) as argument. Got type: %T", other)
187
+ }
188
+ return {type: opType, lhs: lhsExpr, rhs: rhsExpr}
189
+ }
178
190
  )
179
191
  }
180
192
 
181
193
 
182
194
  unaryOp := func(opType) {
183
195
  return _newExpression(
184
- {type: opType, value: expression},
185
- undefined
196
+ func(ctx) {
197
+ valueExpr := self._getExpression(ctx)
198
+ return {type: opType, value: valueExpr}
199
+ }
186
200
  )
187
201
  }
188
202
 
189
203
 
190
204
  unaryStrOp := func(opType) {
191
205
  return _newExpression(
192
- {type: opType, value: expression},
193
- undefined
206
+ func(ctx) {
207
+ valueExpr := self._getExpression(ctx)
208
+ return {type: opType, value: valueExpr}
209
+ }
194
210
  )
195
211
  }
196
212
 
197
213
 
198
214
  aggOp := func(aggType) {
199
215
  return _newWindowExpression(
200
- {type: "aggregate", aggregation: aggType, value: expression, partitionBy: []},
201
- {aggregation: aggType, expression: expression},
216
+ func(ctx) {
217
+ valueExpr := self._getExpression(ctx)
218
+ return {type: "aggregate", aggregation: aggType, value: valueExpr, partitionBy: []}
219
+ },
220
+ func(ctx) {
221
+ valueExpr := self._getExpression(ctx)
222
+ return {aggregation: aggType, expression: valueExpr}
223
+ },
202
224
  undefined // Alias of underlying col, window expression itself is typically aliased later
203
225
  )
204
226
  }
@@ -206,20 +228,19 @@ _newExpression = func(expression, currentAlias) {
206
228
  aggOpBy := func(aggType, by) {
207
229
  return _newWindowExpression(
208
230
  undefined,
209
- {aggregation: aggType, expression: expression, by: _mapToExpressionStructList(by, "col")},
231
+ func(ctx) {
232
+ baseExpr := self._getExpression(ctx)
233
+ return {aggregation: aggType, expression: baseExpr, by: _mapToExpressionStructList(by, "col", {})}
234
+ },
210
235
  undefined // Alias of underlying col, window expression itself is typically aliased later
211
236
  )
212
237
  }
213
238
 
214
239
  self = ll.toStrict({
215
240
 
216
- getExpression: func() { return expression },
217
-
218
- getAlias: func() {
219
- if currentAlias == undefined {
220
- ll.panic("Alias is not defined for this expression.")
221
- }
222
- return currentAlias
241
+ _getExpression: func(ctx) {
242
+ ll.assert(is_map(ctx), "ctx must be a map")
243
+ return expression(ctx)
223
244
  },
224
245
 
225
246
 
@@ -228,7 +249,10 @@ _newExpression = func(expression, currentAlias) {
228
249
 
229
250
 
230
251
  alias: func(newAlias) {
231
- return _newExpression(expression, newAlias)
252
+ return _newExpression(func (ctx) {
253
+ valueExpr := self._getExpression(ctx)
254
+ return {type: "alias", value: valueExpr, name: newAlias}
255
+ })
232
256
  },
233
257
 
234
258
 
@@ -294,10 +318,10 @@ _newExpression = func(expression, currentAlias) {
294
318
  if !is_undefined(ops.strict) {
295
319
  strictVal = ops.strict
296
320
  }
297
- return _newExpression(
298
- {type: "cast", value: expression, dtype: dtype, strict: strictVal},
299
- undefined
300
- )
321
+ return _newExpression(func(ctx) {
322
+ valueExpr := self._getExpression(ctx)
323
+ return {type: "cast", value: valueExpr, dtype: dtype, strict: strictVal}
324
+ })
301
325
  },
302
326
 
303
327
 
@@ -382,21 +406,23 @@ _newExpression = func(expression, currentAlias) {
382
406
  }, "structField")
383
407
 
384
408
 
385
- structExpr := {
386
- type: "struct_field",
387
- struct: expression,
388
- fields: fields
389
- }
409
+ return _newExpression(func(ctx) {
410
+ structExpr := {
411
+ type: "struct_field",
412
+ struct: self._getExpression(ctx),
413
+ fields: fields
414
+ }
390
415
 
391
416
 
392
- if !is_undefined(ops.default) {
393
- structExpr.default = ops.default
394
- }
395
- if !is_undefined(ops.dtype) {
396
- structExpr.dtype = ops.dtype
397
- }
417
+ if !is_undefined(ops.default) {
418
+ structExpr.default = ops.default
419
+ }
420
+ if !is_undefined(ops.dtype) {
421
+ structExpr.dtype = ops.dtype
422
+ }
398
423
 
399
- return _newExpression(structExpr, undefined)
424
+ return structExpr
425
+ })
400
426
  },
401
427
 
402
428
 
@@ -420,12 +446,8 @@ _newExpression = func(expression, currentAlias) {
420
446
  strSlice: func(start, ...args) {
421
447
  ll.assert(len(args) <= 1, "strSlice expects at most one additional argument.")
422
448
 
423
- expr := {
424
- type: "substring",
425
- value: expression,
426
- start: _mapToSingleExpressionStruct(start, "col")
427
- }
428
-
449
+ length := undefined
450
+ end := undefined
429
451
  if len(args) > 0 {
430
452
  arg := args[0]
431
453
  if is_map(arg) {
@@ -438,17 +460,33 @@ _newExpression = func(expression, currentAlias) {
438
460
  ll.panic("strSlice cannot have both 'length' and 'end' options.")
439
461
  }
440
462
  if !is_undefined(ops.length) {
441
- expr.length = _mapToSingleExpressionStruct(ops.length, "col")
463
+ length = ops.length
442
464
  }
443
465
  if !is_undefined(ops.end) {
444
- expr.end = _mapToSingleExpressionStruct(ops.end, "col")
466
+ end = ops.end
445
467
  }
446
468
  } else {
447
- expr.length = _mapToSingleExpressionStruct(arg, "col")
469
+ length = arg
448
470
  }
449
471
  }
450
472
 
451
- return _newExpression(expr, undefined)
473
+ return _newExpression(func(ctx) {
474
+ valueExpr := self._getExpression(ctx)
475
+ expr := {
476
+ type: "substring",
477
+ value: valueExpr,
478
+ start: _mapToSingleExpressionStruct(start, "col", ctx)
479
+ }
480
+
481
+ if !is_undefined(length) {
482
+ expr.length = _mapToSingleExpressionStruct(length, "col", ctx)
483
+ }
484
+ if !is_undefined(end) {
485
+ expr.end = _mapToSingleExpressionStruct(end, "col", ctx)
486
+ }
487
+
488
+ return expr
489
+ })
452
490
  },
453
491
 
454
492
 
@@ -476,14 +514,17 @@ _newExpression = func(expression, currentAlias) {
476
514
  literalVal = ops.literal
477
515
  }
478
516
 
479
- return _newExpression({
480
- type: "str_replace",
481
- value: expression,
482
- pattern: _mapToSingleExpressionStruct(pattern, "lit"),
483
- replacement: _mapToSingleExpressionStruct(replacement, "lit"),
484
- replaceAll: replaceAllVal,
485
- literal: literalVal
486
- }, undefined)
517
+ return _newExpression(func(ctx) {
518
+ valueExpr := self._getExpression(ctx)
519
+ return {
520
+ type: "str_replace",
521
+ value: valueExpr,
522
+ pattern: _mapToSingleExpressionStruct(pattern, "lit", ctx),
523
+ replacement: _mapToSingleExpressionStruct(replacement, "lit", ctx),
524
+ replaceAll: replaceAllVal,
525
+ literal: literalVal
526
+ }
527
+ })
487
528
  },
488
529
 
489
530
 
@@ -527,13 +568,16 @@ _newExpression = func(expression, currentAlias) {
527
568
  strictVal = ops.strict
528
569
  }
529
570
 
530
- return _newExpression({
531
- type: "str_contains",
532
- value: expression,
533
- pattern: _mapToSingleExpressionStruct(pattern, "lit"),
534
- literal: literalVal,
535
- strict: strictVal
536
- }, undefined)
571
+ return _newExpression(func(ctx) {
572
+ valueExpr := self._getExpression(ctx)
573
+ return {
574
+ type: "str_contains",
575
+ value: valueExpr,
576
+ pattern: _mapToSingleExpressionStruct(pattern, "lit", ctx),
577
+ literal: literalVal,
578
+ strict: strictVal
579
+ }
580
+ })
537
581
  },
538
582
 
539
583
 
@@ -558,12 +602,15 @@ _newExpression = func(expression, currentAlias) {
558
602
  asciiCaseInsensitiveVal = ops.asciiCaseInsensitive
559
603
  }
560
604
 
561
- return _newExpression({
562
- type: "str_contains_any",
563
- value: expression,
564
- patterns: patterns,
565
- asciiCaseInsensitive: asciiCaseInsensitiveVal
566
- }, undefined)
605
+ return _newExpression(func(ctx) {
606
+ valueExpr := self._getExpression(ctx)
607
+ return {
608
+ type: "str_contains_any",
609
+ value: valueExpr,
610
+ patterns: patterns,
611
+ asciiCaseInsensitive: asciiCaseInsensitiveVal
612
+ }
613
+ })
567
614
  },
568
615
 
569
616
 
@@ -584,12 +631,15 @@ _newExpression = func(expression, currentAlias) {
584
631
  literalVal = ops.literal
585
632
  }
586
633
 
587
- return _newExpression({
588
- type: "str_count_matches",
589
- value: expression,
590
- pattern: _mapToSingleExpressionStruct(pattern, "lit"),
591
- literal: literalVal
592
- }, undefined)
634
+ return _newExpression(func(ctx) {
635
+ valueExpr := self._getExpression(ctx)
636
+ return {
637
+ type: "str_count_matches",
638
+ value: valueExpr,
639
+ pattern: _mapToSingleExpressionStruct(pattern, "lit", ctx),
640
+ literal: literalVal
641
+ }
642
+ })
593
643
  },
594
644
 
595
645
 
@@ -610,12 +660,15 @@ _newExpression = func(expression, currentAlias) {
610
660
  groupIndexVal = ops.groupIndex
611
661
  }
612
662
 
613
- return _newExpression({
614
- type: "str_extract",
615
- value: expression,
616
- pattern: _mapToSingleExpressionStruct(pattern, "lit"),
617
- groupIndex: groupIndexVal
618
- }, undefined)
663
+ return _newExpression(func(ctx) {
664
+ valueExpr := self._getExpression(ctx)
665
+ return {
666
+ type: "str_extract",
667
+ value: valueExpr,
668
+ pattern: _mapToSingleExpressionStruct(pattern, "lit", ctx),
669
+ groupIndex: groupIndexVal
670
+ }
671
+ })
619
672
  },
620
673
 
621
674
 
@@ -624,11 +677,14 @@ _newExpression = func(expression, currentAlias) {
624
677
 
625
678
 
626
679
  strStartsWith: func(prefix) {
627
- return _newExpression({
628
- type: "str_starts_with",
629
- value: expression,
630
- prefix: _mapToSingleExpressionStruct(prefix, "lit")
631
- }, undefined)
680
+ return _newExpression(func(ctx) {
681
+ valueExpr := self._getExpression(ctx)
682
+ return {
683
+ type: "str_starts_with",
684
+ value: valueExpr,
685
+ prefix: _mapToSingleExpressionStruct(prefix, "lit", ctx)
686
+ }
687
+ })
632
688
  },
633
689
 
634
690
 
@@ -637,11 +693,14 @@ _newExpression = func(expression, currentAlias) {
637
693
 
638
694
 
639
695
  strEndsWith: func(suffix) {
640
- return _newExpression({
641
- type: "str_ends_with",
642
- value: expression,
643
- suffix: _mapToSingleExpressionStruct(suffix, "lit")
644
- }, undefined)
696
+ return _newExpression(func(ctx) {
697
+ valueExpr := self._getExpression(ctx)
698
+ return {
699
+ type: "str_ends_with",
700
+ value: valueExpr,
701
+ suffix: _mapToSingleExpressionStruct(suffix, "lit", ctx)
702
+ }
703
+ })
645
704
  },
646
705
 
647
706
 
@@ -662,13 +721,17 @@ _newExpression = func(expression, currentAlias) {
662
721
  returnSimilarityVal = returnSimilarity
663
722
  }
664
723
 
665
- return _newExpression({
666
- type: "string_distance",
667
- string1: expression,
668
- string2: string2.getExpression(),
669
- metric: metric,
670
- returnSimilarity: returnSimilarityVal
671
- }, undefined)
724
+ return _newExpression(func(ctx) {
725
+ lhsExpr := self._getExpression(ctx)
726
+ rhsExpr := string2._getExpression(ctx)
727
+ return {
728
+ type: "string_distance",
729
+ string1: lhsExpr,
730
+ string2: rhsExpr,
731
+ metric: metric,
732
+ returnSimilarity: returnSimilarityVal
733
+ }
734
+ })
672
735
  },
673
736
 
674
737
 
@@ -684,13 +747,17 @@ _newExpression = func(expression, currentAlias) {
684
747
  ll.assert(is_string(metric), "metric must be a string")
685
748
  ll.assert(is_int(bound) || is_float(bound), "bound must be a number")
686
749
 
687
- return _newExpression({
688
- type: "fuzzy_string_filter",
689
- value: expression,
690
- pattern: patternExpr.getExpression(),
691
- metric: metric,
692
- bound: bound
693
- }, undefined)
750
+ return _newExpression(func(ctx) {
751
+ lhsExpr := self._getExpression(ctx)
752
+ rhsExpr := patternExpr._getExpression(ctx)
753
+ return {
754
+ type: "fuzzy_string_filter",
755
+ value: lhsExpr,
756
+ pattern: rhsExpr,
757
+ metric: metric,
758
+ bound: bound
759
+ }
760
+ })
694
761
  },
695
762
 
696
763
 
@@ -708,16 +775,19 @@ _newExpression = func(expression, currentAlias) {
708
775
  ll.assert(is_int(bits), "bits must be a number")
709
776
  }
710
777
 
711
- hashExpr := {
712
- type: "hash",
713
- value: expression,
714
- hashType: hashType,
715
- encoding: encoding
716
- }
717
- if bits != undefined {
718
- hashExpr.bits = bits
719
- }
720
- return _newExpression(hashExpr, undefined)
778
+ return _newExpression(func(ctx) {
779
+ valueExpr := self._getExpression(ctx)
780
+ hashExpr := {
781
+ type: "hash",
782
+ value: valueExpr,
783
+ hashType: hashType,
784
+ encoding: encoding
785
+ }
786
+ if bits != undefined {
787
+ hashExpr.bits = bits
788
+ }
789
+ return hashExpr
790
+ })
721
791
  },
722
792
 
723
793
 
@@ -727,10 +797,14 @@ _newExpression = func(expression, currentAlias) {
727
797
 
728
798
 
729
799
  fillNull: func(fillValueExpr) {
730
- return _newExpression(
731
- {type: "fill_null", input: expression, fillValue: _mapToSingleExpressionStruct(fillValueExpr, "lit")},
732
- undefined
733
- )
800
+ return _newExpression(func(ctx) {
801
+ valueExpr := self._getExpression(ctx)
802
+ return {
803
+ type: "fill_null",
804
+ input: valueExpr,
805
+ fillValue: _mapToSingleExpressionStruct(fillValueExpr, "lit", ctx)
806
+ }
807
+ })
734
808
  },
735
809
 
736
810
 
@@ -739,10 +813,14 @@ _newExpression = func(expression, currentAlias) {
739
813
 
740
814
 
741
815
  fillNaN: func(fillValueExpr) {
742
- return _newExpression(
743
- {type: "fill_nan", input: expression, fillValue: _mapToSingleExpressionStruct(fillValueExpr, "lit")},
744
- undefined
745
- )
816
+ return _newExpression(func(ctx) {
817
+ valueExpr := self._getExpression(ctx)
818
+ return {
819
+ type: "fill_nan",
820
+ input: valueExpr,
821
+ fillValue: _mapToSingleExpressionStruct(fillValueExpr, "lit", ctx)
822
+ }
823
+ })
746
824
  },
747
825
 
748
826
 
@@ -787,11 +865,14 @@ _newExpression = func(expression, currentAlias) {
787
865
  ll.assert(is_bool(descendingArgs[0]), "descending argument for rank must be a boolean")
788
866
  descendingVal = descendingArgs[0]
789
867
  }
790
- return _newWindowExpression({
791
- type: "rank",
792
- orderBy: [expression],
793
- partitionBy: [], // To be set by .over()
794
- descending: descendingVal
868
+ return _newWindowExpression(func(ctx) {
869
+ valueExpr := self._getExpression(ctx)
870
+ return {
871
+ type: "rank",
872
+ orderBy: [valueExpr],
873
+ partitionBy: [], // To be set by .over()
874
+ descending: descendingVal
875
+ }
795
876
  }, undefined)
796
877
  },
797
878
 
@@ -819,7 +900,11 @@ _newExpression = func(expression, currentAlias) {
819
900
  }, "cumsum")
820
901
  actualArgs = args[0:len(args)-1]
821
902
  if !is_undefined(ops.orderBy) {
822
- additionalOrderByVal = _mapToExpressionStructList(ops.orderBy, "col")
903
+ if is_array(ops.orderBy) {
904
+ additionalOrderByVal = ops.orderBy
905
+ } else {
906
+ additionalOrderByVal = [ops.orderBy]
907
+ }
823
908
  }
824
909
  if !is_undefined(ops.descending) {
825
910
  descendingVal = ops.descending
@@ -829,7 +914,11 @@ _newExpression = func(expression, currentAlias) {
829
914
 
830
915
  if len(additionalOrderByVal) == 0 && len(actualArgs) > 0 {
831
916
  if is_array(actualArgs[0]) || is_string(actualArgs[0]) || _isExpression(actualArgs[0]) {
832
- additionalOrderByVal = _mapToExpressionStructList(actualArgs[0], "col")
917
+ if is_array(actualArgs[0]) {
918
+ additionalOrderByVal = actualArgs[0]
919
+ } else {
920
+ additionalOrderByVal = [actualArgs[0]]
921
+ }
833
922
  if len(actualArgs) > 1 && is_bool(actualArgs[1]) {
834
923
  descendingVal = actualArgs[1]
835
924
  }
@@ -840,20 +929,107 @@ _newExpression = func(expression, currentAlias) {
840
929
  }
841
930
  }
842
931
 
843
- return _newWindowExpression({
844
- type: "cumsum",
845
- value: expression,
846
- additionalOrderBy: additionalOrderByVal,
847
- partitionBy: [],
848
- descending: descendingVal
932
+ return _newWindowExpression(func(ctx) {
933
+ valueExpr := self._getExpression(ctx)
934
+ return {
935
+ type: "cumsum",
936
+ value: valueExpr,
937
+ additionalOrderBy: _mapToExpressionStructList(additionalOrderByVal, "col", ctx),
938
+ partitionBy: [],
939
+ descending: descendingVal
940
+ }
849
941
  }, undefined, undefined)
850
942
  },
851
943
 
852
-
853
-
854
944
 
855
945
  toString: func() {
856
946
  return self.cast("string")
947
+ },
948
+
949
+
950
+
951
+
952
+
953
+
954
+ inSet: func(...values) {
955
+ ll.assert(len(values) > 0, "inSet method requires at least one value.")
956
+
957
+ set := []
958
+ for v in values {
959
+ ll.assert(
960
+ is_undefined(v) || is_bool(v) || is_int(v) || is_float(v) || is_string(v),
961
+ "Invalid argument in inSet: Expected primitive value, got %T", v)
962
+ set = append(set, v)
963
+ }
964
+ uniqueSet := slices.sortUnique(set)
965
+
966
+ return _newExpression(func(ctx) {
967
+ valueExpr := self._getExpression(ctx)
968
+ return {
969
+ type: "in_set",
970
+ value: valueExpr,
971
+ set: uniqueSet
972
+ }
973
+ })
974
+ },
975
+
976
+
977
+
978
+
979
+
980
+
981
+ matchesEcmaRegex: func(ecmaRegex) {
982
+ ll.assert(is_string(ecmaRegex) && len(ecmaRegex) > 0, "ecma_regex must be a non-empty string.")
983
+
984
+ return _newExpression(func(ctx) {
985
+ valueExpr := self._getExpression(ctx)
986
+ return {
987
+ type: "matches_ecma_regex",
988
+ value: valueExpr,
989
+ ecmaRegex: ecmaRegex
990
+ }
991
+ })
992
+ },
993
+
994
+
995
+
996
+
997
+
998
+
999
+
1000
+
1001
+
1002
+
1003
+ containsFuzzyMatch: func(reference, maxEdits, ...args) {
1004
+ ll.assert(is_string(reference) && len(reference) > 0, "pattern must be a non-empty string.")
1005
+ ll.assert(is_int(maxEdits) && maxEdits > 0, "maxEdits must be a positive integer.")
1006
+
1007
+ wildcard := undefined
1008
+ substitutionsOnly := undefined
1009
+ if len(args) > 0 {
1010
+ ops := _validateOptions(args, {
1011
+ "__options__,closed": "",
1012
+ "wildcard,?": "string",
1013
+ "substitutionsOnly,?": "bool"
1014
+ }, "containsFuzzyMatch")
1015
+ if !is_undefined(ops.wildcard) {
1016
+ ll.assert(len(ops.wildcard) == 1, "wildcard must be a single character.")
1017
+ }
1018
+ wildcard = ops.wildcard
1019
+ substitutionsOnly = ops.substitutionsOnly
1020
+ }
1021
+
1022
+ return _newExpression(func(ctx) {
1023
+ valueExpr := self._getExpression(ctx)
1024
+ return objects.deleteUndefined({
1025
+ type: "contains_fuzzy_match",
1026
+ value: valueExpr,
1027
+ reference: reference,
1028
+ maxEdits: maxEdits,
1029
+ wildcard: wildcard,
1030
+ substitutionsOnly: substitutionsOnly
1031
+ })
1032
+ })
857
1033
  }
858
1034
  })
859
1035
  return self
@@ -868,12 +1044,71 @@ _newExpression = func(expression, currentAlias) {
868
1044
 
869
1045
 
870
1046
 
1047
+
1048
+
1049
+
1050
+
1051
+
1052
+ axis = func(matcher) {
1053
+ processedMatcher := undefined
1054
+ if is_string(matcher) {
1055
+ processedMatcher = { name: matcher, domain: {} }
1056
+ } else if is_map(matcher) {
1057
+ processedMatcher = pframesSpec.axisSpecToMatcher(matcher)
1058
+ } else {
1059
+ ll.panic("Matcher for axis must be a string (axis name) or an AxisSpec-like map (with at least 'name' and 'type' fields).")
1060
+ }
1061
+ return _newExpression(func(ctx) {
1062
+ ll.assert(!is_undefined(ctx.specDistiller), "specDistiller must be defined")
1063
+ ll.assert(is_map(ctx.distilledAxesSpecs), "distilledAxesSpecs must be a map")
1064
+
1065
+ distilledMatcher := ctx.specDistiller.distill(processedMatcher)
1066
+ discriminativeDomains := ctx.specDistiller.getDiscriminativeDomainsSet(distilledMatcher.name)
1067
+ if ((!is_undefined(distilledMatcher.domain) && len(distilledMatcher.domain) > 0) != (!is_undefined(discriminativeDomains) && len(discriminativeDomains) > 0)) ||
1068
+ (!is_undefined(distilledMatcher.domain) && !is_undefined(discriminativeDomains) && len(distilledMatcher.domain) != len(discriminativeDomains)) {
1069
+ ll.panic("Axis matcher %v is not specific enough. Expected discriminative domains %v, got %v", distilledMatcher.name, discriminativeDomains, distilledMatcher.domain)
1070
+ }
1071
+
1072
+ axisSpec := undefined
1073
+ for spec in ctx.distilledAxesSpecs[distilledMatcher.name] {
1074
+ if !is_undefined(distilledMatcher.type) {
1075
+ if distilledMatcher.type != spec.type {
1076
+ continue
1077
+ }
1078
+ }
1079
+ if !is_undefined(distilledMatcher.domain) {
1080
+ if is_undefined(spec.domain) || len(spec.domain) < len(distilledMatcher.domain) {
1081
+ continue
1082
+ }
1083
+ for domainKey, domainValue in distilledMatcher.domain {
1084
+ if spec.domain[domainKey] != domainValue {
1085
+ continue
1086
+ }
1087
+ }
1088
+ }
1089
+ axisSpec = spec
1090
+ break
1091
+ }
1092
+ return {
1093
+ type: "axis",
1094
+ spec: axisSpec
1095
+ }
1096
+ })
1097
+ }
1098
+
1099
+
1100
+
1101
+
1102
+
1103
+
871
1104
  col = func(name) {
872
1105
  ll.assert(is_string(name), "col name must be a string")
873
- return _newExpression({
874
- type: "col",
875
- name: name
876
- }, name)
1106
+ return _newExpression(func(ctx) {
1107
+ return {
1108
+ type: "col",
1109
+ name: name
1110
+ }
1111
+ })
877
1112
  }
878
1113
 
879
1114
 
@@ -882,10 +1117,12 @@ col = func(name) {
882
1117
 
883
1118
 
884
1119
  lit = func(value) {
885
- return _newExpression({
886
- type: "const",
887
- value: value
888
- }, undefined)
1120
+ return _newExpression(func(ctx) {
1121
+ return {
1122
+ type: "const",
1123
+ value: value
1124
+ }
1125
+ })
889
1126
  }
890
1127
 
891
1128
 
@@ -908,11 +1145,13 @@ concatStr := func(expressions, ...options) {
908
1145
  delimiterVal = ops.delimiter
909
1146
  }
910
1147
 
911
- return _newExpression({
912
- type: "str_join",
913
- operands: _mapToExpressionStructList(expressions, "lit"),
914
- delimiter: delimiterVal
915
- }, undefined)
1148
+ return _newExpression(func(ctx) {
1149
+ return {
1150
+ type: "str_join",
1151
+ operands: _mapToExpressionStructList(expressions, "lit", ctx),
1152
+ delimiter: delimiterVal
1153
+ }
1154
+ })
916
1155
  }
917
1156
 
918
1157
 
@@ -923,10 +1162,12 @@ concatStr := func(expressions, ...options) {
923
1162
  minHorizontal := func(expressions) {
924
1163
  ll.assert(is_array(expressions), "Argument to minHorizontal must be an array of expressions")
925
1164
  ll.assert(len(expressions) > 0, "Expression array for minHorizontal cannot be empty")
926
- return _newExpression({
927
- type: "min",
928
- operands: _mapToExpressionStructList(expressions, "col")
929
- }, undefined)
1165
+ return _newExpression(func(ctx) {
1166
+ return {
1167
+ type: "min",
1168
+ operands: _mapToExpressionStructList(expressions, "col", ctx)
1169
+ }
1170
+ })
930
1171
  }
931
1172
 
932
1173
 
@@ -937,10 +1178,12 @@ minHorizontal := func(expressions) {
937
1178
  maxHorizontal := func(expressions) {
938
1179
  ll.assert(is_array(expressions), "Argument to maxHorizontal must be an array of expressions")
939
1180
  ll.assert(len(expressions) > 0, "Expression array for maxHorizontal cannot be empty")
940
- return _newExpression({
941
- type: "max",
942
- operands: _mapToExpressionStructList(expressions, "col")
943
- }, undefined)
1181
+ return _newExpression(func(ctx) {
1182
+ return {
1183
+ type: "max",
1184
+ operands: _mapToExpressionStructList(expressions, "col", ctx)
1185
+ }
1186
+ })
944
1187
  }
945
1188
 
946
1189
 
@@ -949,30 +1192,32 @@ maxHorizontal := func(expressions) {
949
1192
 
950
1193
 
951
1194
  allHorizontal = func(...args){
952
- processedOperands := []
953
- for arg in args {
954
- exprStruct := undefined
955
- if _isExpression(arg) {
956
- exprStruct = arg.getExpression()
957
- } else if is_string(arg) {
958
- exprStruct = col(arg).getExpression()
959
- } else {
960
- ll.panic("Invalid argument type for allHorizontal: %T. Must be an expression object or string.", arg)
1195
+ return _newExpression(func(ctx) {
1196
+ processedOperands := []
1197
+ for arg in args {
1198
+ exprStruct := undefined
1199
+ if _isExpression(arg) {
1200
+ exprStruct = arg._getExpression(ctx)
1201
+ } else if is_string(arg) {
1202
+ exprStruct = col(arg)._getExpression(ctx)
1203
+ } else {
1204
+ ll.panic("Invalid argument type for allHorizontal: %T. Must be an expression object or string.", arg)
1205
+ }
1206
+
1207
+ if exprStruct.type == "and" && is_array(exprStruct.operands) {
1208
+ processedOperands = append(processedOperands, exprStruct.operands...)
1209
+ } else {
1210
+ processedOperands = append(processedOperands, exprStruct)
1211
+ }
961
1212
  }
962
-
963
- if exprStruct.type == "and" && is_array(exprStruct.operands) {
964
- processedOperands = append(processedOperands, exprStruct.operands...)
965
- } else {
966
- processedOperands = append(processedOperands, exprStruct)
1213
+ if len(processedOperands) == 0 {
1214
+ ll.panic("allHorizontal requires at least one expression.")
1215
+ }
1216
+ return {
1217
+ type: "and",
1218
+ operands: processedOperands
967
1219
  }
968
- }
969
- if len(processedOperands) == 0 {
970
- ll.panic("allHorizontal requires at least one expression.")
971
- }
972
- return _newExpression({
973
- type: "and",
974
- operands: processedOperands
975
- }, undefined)
1220
+ })
976
1221
  }
977
1222
 
978
1223
 
@@ -981,30 +1226,32 @@ allHorizontal = func(...args){
981
1226
 
982
1227
 
983
1228
  anyHorizontal = func(...args){
984
- processedOperands := []
985
- for arg in args {
986
- exprStruct := undefined
987
- if _isExpression(arg) {
988
- exprStruct = arg.getExpression()
989
- } else if is_string(arg) {
990
- exprStruct = col(arg).getExpression()
991
- } else {
992
- ll.panic("Invalid argument type for anyHorizontal: %T. Must be an expression object or string.", arg)
1229
+ return _newExpression(func(ctx) {
1230
+ processedOperands := []
1231
+ for arg in args {
1232
+ exprStruct := undefined
1233
+ if _isExpression(arg) {
1234
+ exprStruct = arg._getExpression(ctx)
1235
+ } else if is_string(arg) {
1236
+ exprStruct = col(arg)._getExpression(ctx)
1237
+ } else {
1238
+ ll.panic("Invalid argument type for anyHorizontal: %T. Must be an expression object or string.", arg)
1239
+ }
1240
+
1241
+ if exprStruct.type == "or" && is_array(exprStruct.operands) {
1242
+ processedOperands = append(processedOperands, exprStruct.operands...)
1243
+ } else {
1244
+ processedOperands = append(processedOperands, exprStruct)
1245
+ }
993
1246
  }
994
-
995
- if exprStruct.type == "or" && is_array(exprStruct.operands) {
996
- processedOperands = append(processedOperands, exprStruct.operands...)
997
- } else {
998
- processedOperands = append(processedOperands, exprStruct)
1247
+ if len(processedOperands) == 0 {
1248
+ ll.panic("anyHorizontal requires at least one expression.")
999
1249
  }
1000
- }
1001
- if len(processedOperands) == 0 {
1002
- ll.panic("anyHorizontal requires at least one expression.")
1003
- }
1004
- return _newExpression({
1005
- type: "or",
1006
- operands: processedOperands
1007
- }, undefined)
1250
+ return {
1251
+ type: "or",
1252
+ operands: processedOperands
1253
+ }
1254
+ })
1008
1255
  }
1009
1256
 
1010
1257
 
@@ -1049,8 +1296,6 @@ rank = func(orderByExpressions, ...options) {
1049
1296
  descendingVal := false
1050
1297
 
1051
1298
  ll.assert(orderByExpressions != undefined, "rank() requires orderByExpressions (single expression/string or array of them).")
1052
- orderByVal = _mapToExpressionStructList(orderByExpressions, "col")
1053
- ll.assert(len(orderByVal) > 0, "rank() 'orderBy' list cannot be empty after processing arguments.")
1054
1299
 
1055
1300
  ops := _validateOptions(options, {
1056
1301
  "__options__,closed": "",
@@ -1060,11 +1305,15 @@ rank = func(orderByExpressions, ...options) {
1060
1305
  descendingVal = ops.descending
1061
1306
  }
1062
1307
 
1063
- return _newWindowExpression({
1064
- type: "rank",
1065
- orderBy: orderByVal,
1066
- partitionBy: [],
1067
- descending: descendingVal
1308
+ return _newWindowExpression(func(ctx) {
1309
+ orderByVal := _mapToExpressionStructList(orderByExpressions, "col", ctx)
1310
+ ll.assert(len(orderByVal) > 0, "rank() 'orderBy' list cannot be empty after processing arguments.")
1311
+ return {
1312
+ type: "rank",
1313
+ orderBy: orderByVal,
1314
+ partitionBy: [],
1315
+ descending: descendingVal
1316
+ }
1068
1317
  }, undefined, undefined)
1069
1318
  }
1070
1319
 
@@ -1082,14 +1331,17 @@ _newWhenThenBuilder := func(currentClauses, currentWhenExprOrUndefined) {
1082
1331
  ll.assert(_isExpression(thenExpr), ".then() expects an expression object argument.")
1083
1332
 
1084
1333
 
1085
- newClauses := maps.clone(currentClauses) // Deep copy existing clauses
1086
- newClauses = append(newClauses, {
1087
- when: currentWhenExprOrUndefined.getExpression(),
1088
- then: thenExpr.getExpression()
1089
- })
1334
+ newClausesExpr := func(ctx) {
1335
+ existingClauses := currentClauses(ctx)
1336
+ newClauses := maps.clone(existingClauses) // Deep copy existing clauses
1337
+ return append(newClauses, {
1338
+ when: currentWhenExprOrUndefined._getExpression(ctx),
1339
+ then: thenExpr._getExpression(ctx)
1340
+ })
1341
+ }
1090
1342
 
1091
1343
 
1092
- return _newWhenThenBuilder(newClauses, undefined)
1344
+ return _newWhenThenBuilder(newClausesExpr, undefined)
1093
1345
  },
1094
1346
 
1095
1347
 
@@ -1113,16 +1365,18 @@ _newWhenThenBuilder := func(currentClauses, currentWhenExprOrUndefined) {
1113
1365
 
1114
1366
  otherwise: func(otherwiseExpr) {
1115
1367
  ll.assert(currentWhenExprOrUndefined == undefined, ".otherwise() must follow a .then() call.")
1116
- ll.assert(len(currentClauses) > 0, "At least one .when().then() clause is required before .otherwise().")
1117
1368
  ll.assert(_isExpression(otherwiseExpr), ".otherwise() expects an expression object argument.")
1118
1369
 
1119
1370
 
1120
- finalClauses := maps.clone(currentClauses)
1121
- return _newExpression({
1122
- type: "when_then_otherwise",
1123
- conditions: finalClauses,
1124
- otherwise: otherwiseExpr.getExpression()
1125
- }, undefined)
1371
+ return _newExpression(func(ctx) {
1372
+ finalClauses := maps.clone(currentClauses(ctx))
1373
+ ll.assert(len(finalClauses) > 0, "At least one .when().then() clause is required before .otherwise().")
1374
+ return {
1375
+ type: "when_then_otherwise",
1376
+ conditions: finalClauses,
1377
+ otherwise: otherwiseExpr._getExpression(ctx)
1378
+ }
1379
+ })
1126
1380
  }
1127
1381
  })
1128
1382
  return builderSelf
@@ -1140,7 +1394,7 @@ _newWhenThenBuilder := func(currentClauses, currentWhenExprOrUndefined) {
1140
1394
 
1141
1395
  when = func(conditionExpr) {
1142
1396
  ll.assert(_isExpression(conditionExpr), "Initial when() expects an expression object argument.")
1143
- return _newWhenThenBuilder([], conditionExpr)
1397
+ return _newWhenThenBuilder(func(ctx) { return [] }, conditionExpr)
1144
1398
  }
1145
1399
 
1146
1400
 
@@ -1153,18 +1407,322 @@ when = func(conditionExpr) {
1153
1407
 
1154
1408
 
1155
1409
 
1156
-
1157
- rawExp := func(expression, ...alias) {
1158
- ll.assert(len(alias) <= 1, "rawExpression expects at most one alias argument")
1159
- aliasVal := undefined
1160
- if len(alias) > 0 {
1161
- aliasVal = alias[0]
1162
- }
1410
+ rawExp := func(expression) {
1163
1411
  ll.assert(is_map(expression), "rawExpression expects a map as argument")
1164
- return _newExpression(expression, aliasVal)
1412
+ return _newExpression(func(ctx) { return expression })
1413
+ }
1414
+
1415
+ _isSelector := func(expression) {
1416
+ return _isExpression(expression) && ll.methodExists(expression, "complement")
1165
1417
  }
1166
1418
 
1419
+ _newSelectorExpression := func(expression) {
1420
+ self := undefined
1421
+ newExpression := _newExpression(expression)
1422
+ self = ll.toStrict(oop.inherit(newExpression, {
1423
+
1424
+
1425
+
1426
+
1427
+
1428
+ complement: func() {
1429
+ return _newSelectorExpression(func(ctx) {
1430
+ return {
1431
+ type: "selector_complement",
1432
+ selector: self._getExpression(ctx)
1433
+ }
1434
+ })
1435
+ },
1436
+
1437
+
1438
+
1439
+
1440
+
1441
+
1442
+
1443
+ union: func(...others) {
1444
+ ll.assert(len(others) > 0, "union requires at least one other selector")
1445
+ return _newSelectorExpression(func(ctx) {
1446
+ selectors := [self._getExpression(ctx)]
1447
+ for other in others {
1448
+ ll.assert(_isSelector(other), "union requires selector expressions, got: %T", other)
1449
+ selectors = append(selectors, other._getExpression(ctx))
1450
+ }
1451
+ return {
1452
+ type: "selector_union",
1453
+ selectors: selectors
1454
+ }
1455
+ })
1456
+ },
1457
+
1458
+
1459
+
1460
+
1461
+
1462
+
1463
+
1464
+ intersection: func(...others) {
1465
+ ll.assert(len(others) > 0, "intersection requires at least one other selector")
1466
+ return _newSelectorExpression(func(ctx) {
1467
+ selectors := [self._getExpression(ctx)]
1468
+ for other in others {
1469
+ ll.assert(_isSelector(other), "intersection requires selector expressions, got: %T", other)
1470
+ selectors = append(selectors, other._getExpression(ctx))
1471
+ }
1472
+ return {
1473
+ type: "selector_intersection",
1474
+ selectors: selectors
1475
+ }
1476
+ })
1477
+ },
1478
+
1479
+
1480
+
1481
+
1482
+
1483
+
1484
+
1485
+ difference: func(...others) {
1486
+ ll.assert(len(others) > 0, "difference requires at least one other selector")
1487
+ return _newSelectorExpression(func(ctx) {
1488
+ selectors := [self._getExpression(ctx)]
1489
+ for other in others {
1490
+ ll.assert(_isSelector(other), "difference requires selector expressions, got: %T", other)
1491
+ selectors = append(selectors, other._getExpression(ctx))
1492
+ }
1493
+ return {
1494
+ type: "selector_difference",
1495
+ selectors: selectors
1496
+ }
1497
+ })
1498
+ },
1499
+
1500
+
1501
+
1502
+
1503
+
1504
+
1505
+
1506
+ symmetricDifference: func(...others) {
1507
+ ll.assert(len(others) > 0, "symmetricDifference requires at least one other selector")
1508
+ return _newSelectorExpression(func(ctx) {
1509
+ selectors := [self._getExpression(ctx)]
1510
+ for other in others {
1511
+ ll.assert(_isSelector(other), "symmetricDifference requires selector expressions, got: %T", other)
1512
+ selectors = append(selectors, other._getExpression(ctx))
1513
+ }
1514
+ return {
1515
+ type: "selector_symmetric_difference",
1516
+ selectors: selectors
1517
+ }
1518
+ })
1519
+ }
1520
+ }))
1521
+ return self
1522
+ }
1523
+
1524
+
1525
+
1526
+
1527
+
1528
+ sc := ll.toStrict({
1529
+
1530
+
1531
+
1532
+
1533
+
1534
+
1535
+ all: func() {
1536
+ return _newSelectorExpression(func(ctx) {
1537
+ return {type: "selector_all"}
1538
+ })
1539
+ },
1540
+
1541
+
1542
+
1543
+
1544
+
1545
+
1546
+
1547
+ string: func() {
1548
+ return _newSelectorExpression(func(ctx) {
1549
+ return {type: "selector_string"}
1550
+ })
1551
+ },
1552
+
1553
+
1554
+
1555
+
1556
+
1557
+
1558
+
1559
+ numeric: func() {
1560
+ return _newSelectorExpression(func(ctx) {
1561
+ return {type: "selector_numeric"}
1562
+ })
1563
+ },
1564
+
1565
+
1566
+
1567
+
1568
+
1569
+
1570
+
1571
+ integer: func() {
1572
+ return _newSelectorExpression(func(ctx) {
1573
+ return {type: "selector_integer"}
1574
+ })
1575
+ },
1576
+
1577
+
1578
+
1579
+
1580
+
1581
+
1582
+
1583
+ float: func() {
1584
+ return _newSelectorExpression(func(ctx) {
1585
+ return {type: "selector_float"}
1586
+ })
1587
+ },
1588
+
1589
+
1590
+
1591
+
1592
+
1593
+
1594
+
1595
+
1596
+ startsWith: func(prefix) {
1597
+ ll.assert(is_string(prefix), "startsWith requires a string argument, got: %T", prefix)
1598
+ return _newSelectorExpression(func(ctx) {
1599
+ return {
1600
+ type: "selector_starts_with",
1601
+ prefix: prefix
1602
+ }
1603
+ })
1604
+ },
1605
+
1606
+
1607
+
1608
+
1609
+
1610
+
1611
+
1612
+
1613
+ endsWith: func(suffix) {
1614
+ ll.assert(is_string(suffix), "endsWith requires a string argument, got: %T", suffix)
1615
+ return _newSelectorExpression(func(ctx) {
1616
+ return {
1617
+ type: "selector_ends_with",
1618
+ suffix: suffix
1619
+ }
1620
+ })
1621
+ },
1622
+
1623
+
1624
+
1625
+
1626
+
1627
+
1628
+
1629
+
1630
+ contains: func(substring) {
1631
+ ll.assert(is_string(substring), "contains requires a string argument, got: %T", substring)
1632
+ return _newSelectorExpression(func(ctx) {
1633
+ return {
1634
+ type: "selector_contains",
1635
+ substring: substring
1636
+ }
1637
+ })
1638
+ },
1639
+
1640
+
1641
+
1642
+
1643
+
1644
+
1645
+
1646
+
1647
+ matches: func(pattern) {
1648
+ ll.assert(is_string(pattern), "matches requires a string argument, got: %T", pattern)
1649
+ return _newSelectorExpression(func(ctx) {
1650
+ return {
1651
+ type: "selector_matches",
1652
+ pattern: pattern
1653
+ }
1654
+ })
1655
+ },
1656
+
1657
+
1658
+
1659
+
1660
+
1661
+
1662
+
1663
+
1664
+ exclude: func(...columns) {
1665
+ ll.assert(len(columns) > 0, "exclude requires at least one column name")
1666
+ for col in columns {
1667
+ ll.assert(is_string(col), "exclude requires string arguments, got: %T", col)
1668
+ }
1669
+ return _newSelectorExpression(func(ctx) {
1670
+ return {
1671
+ type: "selector_exclude",
1672
+ columns: columns
1673
+ }
1674
+ })
1675
+ },
1676
+
1677
+
1678
+
1679
+
1680
+
1681
+
1682
+
1683
+
1684
+ byName: func(...names) {
1685
+ ll.assert(len(names) > 0, "byName requires at least one column name")
1686
+ for name in names {
1687
+ ll.assert(is_string(name), "byName requires string arguments, got: %T", name)
1688
+ }
1689
+ return _newSelectorExpression(func(ctx) {
1690
+ return {
1691
+ type: "selector_by_name",
1692
+ names: names
1693
+ }
1694
+ })
1695
+ },
1696
+
1697
+
1698
+
1699
+
1700
+
1701
+
1702
+
1703
+
1704
+ axis: func(spec) {
1705
+ validation.assertType(spec, pframesSpec.P_AXIS_SPEC_SCHEMA)
1706
+ return _newSelectorExpression(func(ctx) {
1707
+ return {type: "selector_axis", spec: spec}
1708
+ })
1709
+ },
1710
+
1711
+
1712
+
1713
+
1714
+
1715
+
1716
+
1717
+ nested: func() {
1718
+ return _newSelectorExpression(func(ctx) {
1719
+ return {type: "selector_nested"}
1720
+ })
1721
+ }
1722
+ })
1723
+
1167
1724
  export ll.toStrict({
1725
+ axis: axis,
1168
1726
  col: col,
1169
1727
  lit: lit,
1170
1728
  concatStr: concatStr,
@@ -1177,6 +1735,8 @@ export ll.toStrict({
1177
1735
  rank: rank,
1178
1736
  when: when,
1179
1737
  rawExp: rawExp,
1738
+ sc: sc,
1739
+ _newExpression: _newExpression,
1180
1740
  _isExpression: _isExpression,
1181
1741
  _isAggregation: _isAggregation,
1182
1742
  _mapToExpressionStructList: _mapToExpressionStructList,