@elaraai/east 0.0.1-beta.0
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.
- package/LICENSE.md +682 -0
- package/README.md +276 -0
- package/dist/src/analyze.d.ts +95 -0
- package/dist/src/analyze.d.ts.map +1 -0
- package/dist/src/analyze.js +1110 -0
- package/dist/src/analyze.js.map +1 -0
- package/dist/src/ast.d.ts +263 -0
- package/dist/src/ast.d.ts.map +1 -0
- package/dist/src/ast.js +151 -0
- package/dist/src/ast.js.map +1 -0
- package/dist/src/ast_to_ir.d.ts +24 -0
- package/dist/src/ast_to_ir.d.ts.map +1 -0
- package/dist/src/ast_to_ir.js +834 -0
- package/dist/src/ast_to_ir.js.map +1 -0
- package/dist/src/builtins.d.ts +18 -0
- package/dist/src/builtins.d.ts.map +1 -0
- package/dist/src/builtins.js +1105 -0
- package/dist/src/builtins.js.map +1 -0
- package/dist/src/comparison.d.ts +28 -0
- package/dist/src/comparison.d.ts.map +1 -0
- package/dist/src/comparison.js +1017 -0
- package/dist/src/comparison.js.map +1 -0
- package/dist/src/compile.d.ts +22 -0
- package/dist/src/compile.d.ts.map +1 -0
- package/dist/src/compile.js +3260 -0
- package/dist/src/compile.js.map +1 -0
- package/dist/src/containers/ref.d.ts +106 -0
- package/dist/src/containers/ref.d.ts.map +1 -0
- package/dist/src/containers/ref.js +100 -0
- package/dist/src/containers/ref.js.map +1 -0
- package/dist/src/containers/sortedmap.d.ts +165 -0
- package/dist/src/containers/sortedmap.d.ts.map +1 -0
- package/dist/src/containers/sortedmap.js +237 -0
- package/dist/src/containers/sortedmap.js.map +1 -0
- package/dist/src/containers/sortedset.d.ts +185 -0
- package/dist/src/containers/sortedset.d.ts.map +1 -0
- package/dist/src/containers/sortedset.js +312 -0
- package/dist/src/containers/sortedset.js.map +1 -0
- package/dist/src/containers/variant.d.ts +131 -0
- package/dist/src/containers/variant.d.ts.map +1 -0
- package/dist/src/containers/variant.js +68 -0
- package/dist/src/containers/variant.js.map +1 -0
- package/dist/src/datetime_format/parse.d.ts +50 -0
- package/dist/src/datetime_format/parse.d.ts.map +1 -0
- package/dist/src/datetime_format/parse.js +908 -0
- package/dist/src/datetime_format/parse.js.map +1 -0
- package/dist/src/datetime_format/print.d.ts +35 -0
- package/dist/src/datetime_format/print.d.ts.map +1 -0
- package/dist/src/datetime_format/print.js +157 -0
- package/dist/src/datetime_format/print.js.map +1 -0
- package/dist/src/datetime_format/tokenize.d.ts +76 -0
- package/dist/src/datetime_format/tokenize.d.ts.map +1 -0
- package/dist/src/datetime_format/tokenize.js +271 -0
- package/dist/src/datetime_format/tokenize.js.map +1 -0
- package/dist/src/datetime_format/types.d.ts +99 -0
- package/dist/src/datetime_format/types.d.ts.map +1 -0
- package/dist/src/datetime_format/types.js +103 -0
- package/dist/src/datetime_format/types.js.map +1 -0
- package/dist/src/datetime_format/validate.d.ts +51 -0
- package/dist/src/datetime_format/validate.d.ts.map +1 -0
- package/dist/src/datetime_format/validate.js +208 -0
- package/dist/src/datetime_format/validate.js.map +1 -0
- package/dist/src/default.d.ts +21 -0
- package/dist/src/default.d.ts.map +1 -0
- package/dist/src/default.js +82 -0
- package/dist/src/default.js.map +1 -0
- package/dist/src/eastir.d.ts +33 -0
- package/dist/src/eastir.d.ts.map +1 -0
- package/dist/src/eastir.js +92 -0
- package/dist/src/eastir.js.map +1 -0
- package/dist/src/error.d.ts +13 -0
- package/dist/src/error.d.ts.map +1 -0
- package/dist/src/error.js +8 -0
- package/dist/src/error.js.map +1 -0
- package/dist/src/expr/array.d.ts +1711 -0
- package/dist/src/expr/array.d.ts.map +1 -0
- package/dist/src/expr/array.js +1805 -0
- package/dist/src/expr/array.js.map +1 -0
- package/dist/src/expr/ast.d.ts +17 -0
- package/dist/src/expr/ast.d.ts.map +1 -0
- package/dist/src/expr/ast.js +302 -0
- package/dist/src/expr/ast.js.map +1 -0
- package/dist/src/expr/blob.d.ts +141 -0
- package/dist/src/expr/blob.d.ts.map +1 -0
- package/dist/src/expr/blob.js +198 -0
- package/dist/src/expr/blob.js.map +1 -0
- package/dist/src/expr/block.d.ts +201 -0
- package/dist/src/expr/block.d.ts.map +1 -0
- package/dist/src/expr/block.js +1505 -0
- package/dist/src/expr/block.js.map +1 -0
- package/dist/src/expr/boolean.d.ts +207 -0
- package/dist/src/expr/boolean.d.ts.map +1 -0
- package/dist/src/expr/boolean.js +261 -0
- package/dist/src/expr/boolean.js.map +1 -0
- package/dist/src/expr/datetime.d.ts +544 -0
- package/dist/src/expr/datetime.d.ts.map +1 -0
- package/dist/src/expr/datetime.js +980 -0
- package/dist/src/expr/datetime.js.map +1 -0
- package/dist/src/expr/dict.d.ts +1242 -0
- package/dist/src/expr/dict.d.ts.map +1 -0
- package/dist/src/expr/dict.js +1492 -0
- package/dist/src/expr/dict.js.map +1 -0
- package/dist/src/expr/expr.d.ts +95 -0
- package/dist/src/expr/expr.d.ts.map +1 -0
- package/dist/src/expr/expr.js +171 -0
- package/dist/src/expr/expr.js.map +1 -0
- package/dist/src/expr/float.d.ts +357 -0
- package/dist/src/expr/float.d.ts.map +1 -0
- package/dist/src/expr/float.js +637 -0
- package/dist/src/expr/float.js.map +1 -0
- package/dist/src/expr/function.d.ts +46 -0
- package/dist/src/expr/function.d.ts.map +1 -0
- package/dist/src/expr/function.js +58 -0
- package/dist/src/expr/function.js.map +1 -0
- package/dist/src/expr/index.d.ts +450 -0
- package/dist/src/expr/index.d.ts.map +1 -0
- package/dist/src/expr/index.js +423 -0
- package/dist/src/expr/index.js.map +1 -0
- package/dist/src/expr/integer.d.ts +256 -0
- package/dist/src/expr/integer.d.ts.map +1 -0
- package/dist/src/expr/integer.js +311 -0
- package/dist/src/expr/integer.js.map +1 -0
- package/dist/src/expr/libs/array.d.ts +106 -0
- package/dist/src/expr/libs/array.d.ts.map +1 -0
- package/dist/src/expr/libs/array.js +140 -0
- package/dist/src/expr/libs/array.js.map +1 -0
- package/dist/src/expr/libs/blob.d.ts +42 -0
- package/dist/src/expr/libs/blob.d.ts.map +1 -0
- package/dist/src/expr/libs/blob.js +70 -0
- package/dist/src/expr/libs/blob.js.map +1 -0
- package/dist/src/expr/libs/datetime.d.ts +479 -0
- package/dist/src/expr/libs/datetime.d.ts.map +1 -0
- package/dist/src/expr/libs/datetime.js +624 -0
- package/dist/src/expr/libs/datetime.js.map +1 -0
- package/dist/src/expr/libs/dict.d.ts +66 -0
- package/dist/src/expr/libs/dict.d.ts.map +1 -0
- package/dist/src/expr/libs/dict.js +77 -0
- package/dist/src/expr/libs/dict.js.map +1 -0
- package/dist/src/expr/libs/float.d.ts +299 -0
- package/dist/src/expr/libs/float.d.ts.map +1 -0
- package/dist/src/expr/libs/float.js +564 -0
- package/dist/src/expr/libs/float.js.map +1 -0
- package/dist/src/expr/libs/integer.d.ts +228 -0
- package/dist/src/expr/libs/integer.d.ts.map +1 -0
- package/dist/src/expr/libs/integer.js +398 -0
- package/dist/src/expr/libs/integer.js.map +1 -0
- package/dist/src/expr/libs/set.d.ts +59 -0
- package/dist/src/expr/libs/set.d.ts.map +1 -0
- package/dist/src/expr/libs/set.js +69 -0
- package/dist/src/expr/libs/set.js.map +1 -0
- package/dist/src/expr/libs/string.d.ts +71 -0
- package/dist/src/expr/libs/string.d.ts.map +1 -0
- package/dist/src/expr/libs/string.js +75 -0
- package/dist/src/expr/libs/string.js.map +1 -0
- package/dist/src/expr/never.d.ts +15 -0
- package/dist/src/expr/never.d.ts.map +1 -0
- package/dist/src/expr/never.js +12 -0
- package/dist/src/expr/never.js.map +1 -0
- package/dist/src/expr/null.d.ts +15 -0
- package/dist/src/expr/null.d.ts.map +1 -0
- package/dist/src/expr/null.js +12 -0
- package/dist/src/expr/null.js.map +1 -0
- package/dist/src/expr/ref.d.ts +103 -0
- package/dist/src/expr/ref.d.ts.map +1 -0
- package/dist/src/expr/ref.js +131 -0
- package/dist/src/expr/ref.js.map +1 -0
- package/dist/src/expr/regex_validation.d.ts +25 -0
- package/dist/src/expr/regex_validation.d.ts.map +1 -0
- package/dist/src/expr/regex_validation.js +130 -0
- package/dist/src/expr/regex_validation.js.map +1 -0
- package/dist/src/expr/set.d.ts +1071 -0
- package/dist/src/expr/set.d.ts.map +1 -0
- package/dist/src/expr/set.js +1137 -0
- package/dist/src/expr/set.js.map +1 -0
- package/dist/src/expr/string.d.ts +414 -0
- package/dist/src/expr/string.d.ts.map +1 -0
- package/dist/src/expr/string.js +683 -0
- package/dist/src/expr/string.js.map +1 -0
- package/dist/src/expr/struct.d.ts +48 -0
- package/dist/src/expr/struct.d.ts.map +1 -0
- package/dist/src/expr/struct.js +65 -0
- package/dist/src/expr/struct.js.map +1 -0
- package/dist/src/expr/types.d.ts +68 -0
- package/dist/src/expr/types.d.ts.map +1 -0
- package/dist/src/expr/types.js +6 -0
- package/dist/src/expr/types.js.map +1 -0
- package/dist/src/expr/variant.d.ts +137 -0
- package/dist/src/expr/variant.d.ts.map +1 -0
- package/dist/src/expr/variant.js +105 -0
- package/dist/src/expr/variant.js.map +1 -0
- package/dist/src/fuzz.d.ts +80 -0
- package/dist/src/fuzz.d.ts.map +1 -0
- package/dist/src/fuzz.js +300 -0
- package/dist/src/fuzz.js.map +1 -0
- package/dist/src/index.d.ts +21 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +21 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/internal.d.ts +36 -0
- package/dist/src/internal.d.ts.map +1 -0
- package/dist/src/internal.js +11 -0
- package/dist/src/internal.js.map +1 -0
- package/dist/src/ir.d.ts +1571 -0
- package/dist/src/ir.d.ts.map +1 -0
- package/dist/src/ir.js +56 -0
- package/dist/src/ir.js.map +1 -0
- package/dist/src/location.d.ts +48 -0
- package/dist/src/location.d.ts.map +1 -0
- package/dist/src/location.js +62 -0
- package/dist/src/location.js.map +1 -0
- package/dist/src/platform.d.ts +21 -0
- package/dist/src/platform.d.ts.map +1 -0
- package/dist/src/platform.js +8 -0
- package/dist/src/platform.js.map +1 -0
- package/dist/src/serialization/beast.d.ts +39 -0
- package/dist/src/serialization/beast.d.ts.map +1 -0
- package/dist/src/serialization/beast.js +555 -0
- package/dist/src/serialization/beast.js.map +1 -0
- package/dist/src/serialization/beast2-stream.d.ts +38 -0
- package/dist/src/serialization/beast2-stream.d.ts.map +1 -0
- package/dist/src/serialization/beast2-stream.js +665 -0
- package/dist/src/serialization/beast2-stream.js.map +1 -0
- package/dist/src/serialization/beast2.d.ts +41 -0
- package/dist/src/serialization/beast2.d.ts.map +1 -0
- package/dist/src/serialization/beast2.js +489 -0
- package/dist/src/serialization/beast2.js.map +1 -0
- package/dist/src/serialization/binary-utils.d.ts +151 -0
- package/dist/src/serialization/binary-utils.d.ts.map +1 -0
- package/dist/src/serialization/binary-utils.js +929 -0
- package/dist/src/serialization/binary-utils.js.map +1 -0
- package/dist/src/serialization/east.d.ts +84 -0
- package/dist/src/serialization/east.d.ts.map +1 -0
- package/dist/src/serialization/east.js +1802 -0
- package/dist/src/serialization/east.js.map +1 -0
- package/dist/src/serialization/index.d.ts +11 -0
- package/dist/src/serialization/index.d.ts.map +1 -0
- package/dist/src/serialization/index.js +12 -0
- package/dist/src/serialization/index.js.map +1 -0
- package/dist/src/serialization/json.d.ts +36 -0
- package/dist/src/serialization/json.d.ts.map +1 -0
- package/dist/src/serialization/json.js +849 -0
- package/dist/src/serialization/json.js.map +1 -0
- package/dist/src/type_of_type.d.ts +115 -0
- package/dist/src/type_of_type.d.ts.map +1 -0
- package/dist/src/type_of_type.js +362 -0
- package/dist/src/type_of_type.js.map +1 -0
- package/dist/src/types.d.ts +648 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +1631 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +87 -0
|
@@ -0,0 +1,1805 @@
|
|
|
1
|
+
import { get_location } from "../location.js";
|
|
2
|
+
import { ArrayType, BooleanType, FunctionType, IntegerType, NullType, StringType, isSubtype, isTypeEqual, printType, NeverType, OptionType, isDataType, StructType, VariantType, SetType, FloatType, DictType } from "../types.js";
|
|
3
|
+
import { valueOrExprToAst, valueOrExprToAstTyped } from "./ast.js";
|
|
4
|
+
import { AstSymbol, Expr, FactorySymbol, TypeSymbol } from "./expr.js";
|
|
5
|
+
import { none, some } from "../containers/variant.js";
|
|
6
|
+
/**
|
|
7
|
+
* Expression representing mutable array values and operations.
|
|
8
|
+
*
|
|
9
|
+
* ArrayExpr provides comprehensive methods for array manipulation including element access,
|
|
10
|
+
* mutation (push/pop/sort/reverse), transformation (map/filter/reduce), searching, slicing,
|
|
11
|
+
* grouping, and aggregation. Arrays are mutable, ordered collections with zero-based indexing.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* // Array manipulation
|
|
16
|
+
* const processArray = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
|
|
17
|
+
* $(arr.pushLast(10n)); // Mutate: add element
|
|
18
|
+
* const doubled = arr.map(($, x, i) => x.multiply(2n));
|
|
19
|
+
* $.return(doubled);
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* // Filtering and mapping
|
|
23
|
+
* const filterMap = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
|
|
24
|
+
* const evens = arr.filter(($, x, i) => x.remainder(2n).equal(0n));
|
|
25
|
+
* $.return(evens.map(($, x, i) => x.add(1n)));
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Aggregation
|
|
29
|
+
* const sumArray = East.function([ArrayType(IntegerType)], IntegerType, ($, arr) => {
|
|
30
|
+
* $.return(arr.reduce(($, acc, x, i) => acc.add(x), 0n));
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export class ArrayExpr extends Expr {
|
|
35
|
+
value_type;
|
|
36
|
+
constructor(value_type, ast, createExpr) {
|
|
37
|
+
super(ast.type, ast, createExpr);
|
|
38
|
+
this.value_type = value_type;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns the size (length) of the array.
|
|
42
|
+
*
|
|
43
|
+
* @returns An IntegerExpr representing the number of elements
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const getSize = East.function([ArrayType(IntegerType)], IntegerType, ($, arr) => {
|
|
48
|
+
* $.return(arr.size());
|
|
49
|
+
* });
|
|
50
|
+
* const compiled = East.compile(getSize.toIR(), []);
|
|
51
|
+
* compiled([1n, 2n, 3n]); // 3n
|
|
52
|
+
* compiled([]); // 0n
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
size() {
|
|
56
|
+
return this[FactorySymbol]({
|
|
57
|
+
ast_type: "Builtin",
|
|
58
|
+
type: IntegerType,
|
|
59
|
+
location: get_location(2),
|
|
60
|
+
builtin: "ArraySize",
|
|
61
|
+
type_parameters: [this.value_type],
|
|
62
|
+
arguments: [this[AstSymbol]],
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Checks if an element exists at the specified index (i.e., if the index is in bounds).
|
|
67
|
+
*
|
|
68
|
+
* @param key - The zero-based index to check
|
|
69
|
+
* @returns A BooleanExpr that is true if the index is valid (0 <= index < size)
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* const hasElement = East.function([ArrayType(IntegerType), IntegerType], BooleanType, ($, arr, index) => {
|
|
74
|
+
* $.return(arr.has(index));
|
|
75
|
+
* });
|
|
76
|
+
* const compiled = East.compile(hasElement.toIR(), []);
|
|
77
|
+
* compiled([10n, 20n, 30n], 1n); // true
|
|
78
|
+
* compiled([10n, 20n, 30n], 5n); // false
|
|
79
|
+
* compiled([10n, 20n, 30n], -1n); // false
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
has(key) {
|
|
83
|
+
const keyAst = valueOrExprToAstTyped(key, IntegerType);
|
|
84
|
+
return this[FactorySymbol]({
|
|
85
|
+
ast_type: "Builtin",
|
|
86
|
+
type: BooleanType,
|
|
87
|
+
location: get_location(2),
|
|
88
|
+
builtin: "ArrayHas",
|
|
89
|
+
type_parameters: [this.value_type],
|
|
90
|
+
arguments: [this[AstSymbol], keyAst],
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Gets the element at the specified index.
|
|
95
|
+
*
|
|
96
|
+
* @param key - The zero-based index
|
|
97
|
+
* @param defaultFn - Optional function to provide a default value for out-of-bounds indices
|
|
98
|
+
* @returns An expression of the array's element type
|
|
99
|
+
*
|
|
100
|
+
* @throws East runtime error if index is out of bounds and no defaultFn is provided
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* const getElement = East.function([ArrayType(IntegerType), IntegerType], IntegerType, ($, arr, index) => {
|
|
105
|
+
* $.return(arr.get(index));
|
|
106
|
+
* });
|
|
107
|
+
* const compiled = East.compile(getElement.toIR(), []);
|
|
108
|
+
* compiled([10n, 20n, 30n], 1n); // 20n
|
|
109
|
+
* // compiled([10n, 20n, 30n], 5n) would throw error
|
|
110
|
+
*
|
|
111
|
+
* // With default value
|
|
112
|
+
* const getOrDefault = East.function([ArrayType(IntegerType), IntegerType], IntegerType, ($, arr, index) => {
|
|
113
|
+
* $.return(arr.get(index, ($, i) => -1n));
|
|
114
|
+
* });
|
|
115
|
+
* compiled = East.compile(getOrDefault.toIR(), []);
|
|
116
|
+
* compiled([10n, 20n, 30n], 5n); // -1n (out of bounds)
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
get(key, defaultFn) {
|
|
120
|
+
const keyAst = valueOrExprToAstTyped(key, IntegerType);
|
|
121
|
+
if (defaultFn !== undefined) {
|
|
122
|
+
const defaultFnAst = valueOrExprToAstTyped(defaultFn, FunctionType([IntegerType], this.value_type, null));
|
|
123
|
+
return this[FactorySymbol]({
|
|
124
|
+
ast_type: "Builtin",
|
|
125
|
+
type: this.value_type,
|
|
126
|
+
location: get_location(2),
|
|
127
|
+
builtin: "ArrayGetOrDefault",
|
|
128
|
+
type_parameters: [this.value_type],
|
|
129
|
+
arguments: [this[AstSymbol], keyAst, defaultFnAst],
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
return this[FactorySymbol]({
|
|
134
|
+
ast_type: "Builtin",
|
|
135
|
+
type: this.value_type,
|
|
136
|
+
location: get_location(2),
|
|
137
|
+
builtin: "ArrayGet",
|
|
138
|
+
type_parameters: [this.value_type],
|
|
139
|
+
arguments: [this[AstSymbol], keyAst],
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Safely gets the element at the specified index, returning an Option variant.
|
|
145
|
+
*
|
|
146
|
+
* @param key - The zero-based index
|
|
147
|
+
* @returns An OptionType expression containing .some(value) if in bounds, or .none if out of bounds
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```ts
|
|
151
|
+
* const tryGetElement = East.function([ArrayType(IntegerType), IntegerType], OptionType(IntegerType), ($, arr, index) => {
|
|
152
|
+
* $.return(arr.tryGet(index));
|
|
153
|
+
* });
|
|
154
|
+
* const compiled = East.compile(tryGetElement.toIR(), []);
|
|
155
|
+
* compiled([10n, 20n, 30n], 1n); // {_tag: "some", some: 20n}
|
|
156
|
+
* compiled([10n, 20n, 30n], 5n); // {_tag: "none", none: null}
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
tryGet(key) {
|
|
160
|
+
const keyAst = valueOrExprToAstTyped(key, IntegerType);
|
|
161
|
+
return this[FactorySymbol]({
|
|
162
|
+
ast_type: "Builtin",
|
|
163
|
+
type: OptionType(this.value_type),
|
|
164
|
+
location: get_location(2),
|
|
165
|
+
builtin: "ArrayTryGet",
|
|
166
|
+
type_parameters: [this.value_type],
|
|
167
|
+
arguments: [this[AstSymbol], keyAst],
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Updates (replaces) the element at the specified index (mutates the array).
|
|
172
|
+
*
|
|
173
|
+
* @param key - The zero-based index to update
|
|
174
|
+
* @param value - The new value to set at that index
|
|
175
|
+
* @returns NullExpr (operation performed for side effect)
|
|
176
|
+
*
|
|
177
|
+
* @throws East runtime error if index is out of bounds
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```ts
|
|
181
|
+
* const updateElement = East.function([ArrayType(IntegerType), IntegerType, IntegerType], ArrayType(IntegerType), ($, arr, index, value) => {
|
|
182
|
+
* $(arr.update(index, value)); // Mutate the array
|
|
183
|
+
* $.return(arr);
|
|
184
|
+
* });
|
|
185
|
+
* const compiled = East.compile(updateElement.toIR(), []);
|
|
186
|
+
* compiled([10n, 20n, 30n], 1n, 99n); // [10n, 99n, 30n]
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
update(key, value) {
|
|
190
|
+
const keyAst = valueOrExprToAstTyped(key, IntegerType);
|
|
191
|
+
const valueAst = valueOrExprToAstTyped(value, this.value_type);
|
|
192
|
+
return this[FactorySymbol]({
|
|
193
|
+
ast_type: "Builtin",
|
|
194
|
+
type: NullType,
|
|
195
|
+
location: get_location(2),
|
|
196
|
+
builtin: "ArrayUpdate",
|
|
197
|
+
type_parameters: [this.value_type],
|
|
198
|
+
arguments: [this[AstSymbol], keyAst, valueAst],
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
/** Modify an array element at index by merging it with a new value using a function accepting the new and current value. An error is thrown on missing key (index out of bounds).
|
|
202
|
+
*
|
|
203
|
+
* This is useful for patterns where you want to update an element based on its current value, e.g. incrementing a number, appending to a string, updating fields in a struct, or pushing to an array.
|
|
204
|
+
*
|
|
205
|
+
* @see {@link update} for simply replacing the value.
|
|
206
|
+
*/
|
|
207
|
+
merge(key, value, updateFn) {
|
|
208
|
+
const location = get_location(2);
|
|
209
|
+
const keyAst = valueOrExprToAstTyped(key, IntegerType);
|
|
210
|
+
const value2Type = value[TypeSymbol];
|
|
211
|
+
const updateFnExpr = Expr.from(updateFn, FunctionType([value2Type, this.value_type, IntegerType], this.value_type, null));
|
|
212
|
+
return this[FactorySymbol]({
|
|
213
|
+
ast_type: "Builtin",
|
|
214
|
+
type: NullType,
|
|
215
|
+
location,
|
|
216
|
+
builtin: "ArrayMerge",
|
|
217
|
+
type_parameters: [this.value_type, value2Type],
|
|
218
|
+
arguments: [this[AstSymbol], keyAst, value[AstSymbol], updateFnExpr[AstSymbol]],
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Adds an element to the end of the array (mutates the array).
|
|
223
|
+
*
|
|
224
|
+
* @param value - The value to append
|
|
225
|
+
* @returns NullExpr (operation performed for side effect)
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* ```ts
|
|
229
|
+
* const appendValue = East.function([ArrayType(IntegerType), IntegerType], ArrayType(IntegerType), ($, arr, value) => {
|
|
230
|
+
* $(arr.pushLast(value)); // Mutate the array
|
|
231
|
+
* $.return(arr);
|
|
232
|
+
* });
|
|
233
|
+
* const compiled = East.compile(appendValue.toIR(), []);
|
|
234
|
+
* compiled([1n, 2n, 3n], 4n); // [1n, 2n, 3n, 4n]
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
pushLast(value) {
|
|
238
|
+
const valueAst = valueOrExprToAstTyped(value, this.value_type);
|
|
239
|
+
return this[FactorySymbol]({
|
|
240
|
+
ast_type: "Builtin",
|
|
241
|
+
type: NullType,
|
|
242
|
+
location: get_location(2),
|
|
243
|
+
builtin: "ArrayPushLast",
|
|
244
|
+
type_parameters: [this.value_type],
|
|
245
|
+
arguments: [this[AstSymbol], valueAst],
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Removes and returns the last element from the array (mutates the array).
|
|
250
|
+
*
|
|
251
|
+
* @returns An expression of the array's element type containing the removed value
|
|
252
|
+
*
|
|
253
|
+
* @throws East runtime error if the array is empty
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```ts
|
|
257
|
+
* const removeLastValue = East.function([ArrayType(IntegerType)], IntegerType, ($, arr) => {
|
|
258
|
+
* $.return(arr.popLast());
|
|
259
|
+
* });
|
|
260
|
+
* const compiled = East.compile(removeLastValue.toIR(), []);
|
|
261
|
+
* compiled([1n, 2n, 3n]); // 3n (arr is now [1n, 2n])
|
|
262
|
+
* ```
|
|
263
|
+
*/
|
|
264
|
+
popLast() {
|
|
265
|
+
return this[FactorySymbol]({
|
|
266
|
+
ast_type: "Builtin",
|
|
267
|
+
type: this.value_type,
|
|
268
|
+
location: get_location(2),
|
|
269
|
+
builtin: "ArrayPopLast",
|
|
270
|
+
type_parameters: [this.value_type],
|
|
271
|
+
arguments: [this[AstSymbol]],
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Adds an element to the beginning of the array (mutates the array).
|
|
276
|
+
*
|
|
277
|
+
* @param value - The value to prepend
|
|
278
|
+
* @returns NullExpr (operation performed for side effect)
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```ts
|
|
282
|
+
* const prependValue = East.function([ArrayType(IntegerType), IntegerType], ArrayType(IntegerType), ($, arr, value) => {
|
|
283
|
+
* $(arr.pushFirst(value)); // Mutate the array
|
|
284
|
+
* $.return(arr);
|
|
285
|
+
* });
|
|
286
|
+
* const compiled = East.compile(prependValue.toIR(), []);
|
|
287
|
+
* compiled([1n, 2n, 3n], 0n); // [0n, 1n, 2n, 3n]
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
pushFirst(value) {
|
|
291
|
+
const valueAst = valueOrExprToAstTyped(value, this.value_type);
|
|
292
|
+
return this[FactorySymbol]({
|
|
293
|
+
ast_type: "Builtin",
|
|
294
|
+
type: NullType,
|
|
295
|
+
location: get_location(2),
|
|
296
|
+
builtin: "ArrayPushFirst",
|
|
297
|
+
type_parameters: [this.value_type],
|
|
298
|
+
arguments: [this[AstSymbol], valueAst],
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Removes and returns the first element from the array (mutates the array).
|
|
303
|
+
*
|
|
304
|
+
* @returns An expression of the array's element type containing the removed value
|
|
305
|
+
*
|
|
306
|
+
* @throws East runtime error if the array is empty
|
|
307
|
+
*
|
|
308
|
+
* @example
|
|
309
|
+
* ```ts
|
|
310
|
+
* const removeFirstValue = East.function([ArrayType(IntegerType)], IntegerType, ($, arr) => {
|
|
311
|
+
* $.return(arr.popFirst());
|
|
312
|
+
* });
|
|
313
|
+
* const compiled = East.compile(removeFirstValue.toIR(), []);
|
|
314
|
+
* compiled([1n, 2n, 3n]); // 1n (arr is now [2n, 3n])
|
|
315
|
+
* ```
|
|
316
|
+
*/
|
|
317
|
+
popFirst() {
|
|
318
|
+
return this[FactorySymbol]({
|
|
319
|
+
ast_type: "Builtin",
|
|
320
|
+
type: this.value_type,
|
|
321
|
+
location: get_location(2),
|
|
322
|
+
builtin: "ArrayPopFirst",
|
|
323
|
+
type_parameters: [this.value_type],
|
|
324
|
+
arguments: [this[AstSymbol]],
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Appends all elements from another array to this array (mutates the array).
|
|
329
|
+
*
|
|
330
|
+
* @param array - The array whose elements will be appended
|
|
331
|
+
* @returns NullExpr (operation performed for side effect)
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* ```ts
|
|
335
|
+
* const appendArray = East.function([ArrayType(IntegerType), ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr1, arr2) => {
|
|
336
|
+
* $(arr1.append(arr2)); // Mutate arr1
|
|
337
|
+
* $.return(arr1);
|
|
338
|
+
* });
|
|
339
|
+
* const compiled = East.compile(appendArray.toIR(), []);
|
|
340
|
+
* compiled([1n, 2n], [3n, 4n]); // [1n, 2n, 3n, 4n]
|
|
341
|
+
* ```
|
|
342
|
+
*/
|
|
343
|
+
append(array) {
|
|
344
|
+
const arrayExpr = Expr.from(array, this[TypeSymbol]);
|
|
345
|
+
return this[FactorySymbol]({
|
|
346
|
+
ast_type: "Builtin",
|
|
347
|
+
type: NullType,
|
|
348
|
+
location: get_location(2),
|
|
349
|
+
builtin: "ArrayAppend",
|
|
350
|
+
type_parameters: [this.value_type],
|
|
351
|
+
arguments: [this[AstSymbol], arrayExpr[AstSymbol]],
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Prepends all elements from another array to this array (mutates the array).
|
|
356
|
+
*
|
|
357
|
+
* @param array - The array whose elements will be prepended
|
|
358
|
+
* @returns NullExpr (operation performed for side effect)
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* ```ts
|
|
362
|
+
* const prependArray = East.function([ArrayType(IntegerType), ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr1, arr2) => {
|
|
363
|
+
* $(arr1.prepend(arr2)); // Mutate arr1
|
|
364
|
+
* $.return(arr1);
|
|
365
|
+
* });
|
|
366
|
+
* const compiled = East.compile(prependArray.toIR(), []);
|
|
367
|
+
* compiled([3n, 4n], [1n, 2n]); // [1n, 2n, 3n, 4n]
|
|
368
|
+
* ```
|
|
369
|
+
*/
|
|
370
|
+
prepend(array) {
|
|
371
|
+
// Convert the input array to an expression of the right type
|
|
372
|
+
const arrayExpr = Expr.from(array, this[TypeSymbol]);
|
|
373
|
+
return this[FactorySymbol]({
|
|
374
|
+
ast_type: "Builtin",
|
|
375
|
+
type: NullType,
|
|
376
|
+
location: get_location(2),
|
|
377
|
+
builtin: "ArrayPrepend",
|
|
378
|
+
type_parameters: [this.value_type],
|
|
379
|
+
arguments: [this[AstSymbol], arrayExpr[AstSymbol]],
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Merges all elements from another array into this array using a merge function.
|
|
384
|
+
*
|
|
385
|
+
* @param array - The array to merge from
|
|
386
|
+
* @param mergeFn - Function taking (currentValue, otherValue, index) and returning the merged value
|
|
387
|
+
* @returns Null (mutates the array in place)
|
|
388
|
+
*
|
|
389
|
+
* @throws East runtime error if the other array has more elements than this array
|
|
390
|
+
*
|
|
391
|
+
* @example
|
|
392
|
+
* ```ts
|
|
393
|
+
* const mergeArrays = East.function([ArrayType(IntegerType), ArrayType(IntegerType)], NullType, ($, arr1, arr2) => {
|
|
394
|
+
* arr1.mergeAll(arr2, ($, current, other, index) => current.add(other));
|
|
395
|
+
* $.return(null);
|
|
396
|
+
* });
|
|
397
|
+
* const compiled = East.compile(mergeArrays.toIR(), []);
|
|
398
|
+
* const arr1 = [10n, 20n, 30n];
|
|
399
|
+
* const arr2 = [1n, 2n, 3n];
|
|
400
|
+
* compiled(arr1, arr2); // null (arr1 is now [11n, 22n, 33n])
|
|
401
|
+
* ```
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* ```ts
|
|
405
|
+
* // Combining strings
|
|
406
|
+
* const mergeStrings = East.function([ArrayType(StringType), ArrayType(StringType)], ArrayType(StringType), ($, arr1, arr2) => {
|
|
407
|
+
* arr1.mergeAll(arr2, ($, current, other, index) => current.concat(" + ").concat(other));
|
|
408
|
+
* $.return(arr1);
|
|
409
|
+
* });
|
|
410
|
+
* const compiled = East.compile(mergeStrings.toIR(), []);
|
|
411
|
+
* compiled(["a", "b"], ["x", "y"]); // ["a + x", "b + y"]
|
|
412
|
+
* ```
|
|
413
|
+
*/
|
|
414
|
+
mergeAll(array, mergeFn) {
|
|
415
|
+
const array_type = array[TypeSymbol];
|
|
416
|
+
if (!array_type || array_type.type !== "Array") {
|
|
417
|
+
throw new Error(`Expected array type for mergeAll, got ${array_type ? printType(array_type) : "unknown"}`);
|
|
418
|
+
}
|
|
419
|
+
const value2Type = array_type.value;
|
|
420
|
+
const mergeFnExpr = Expr.from(mergeFn, FunctionType([this.value_type, value2Type, IntegerType], this.value_type, null));
|
|
421
|
+
return this[FactorySymbol]({
|
|
422
|
+
ast_type: "Builtin",
|
|
423
|
+
type: NullType,
|
|
424
|
+
location: get_location(2),
|
|
425
|
+
builtin: "ArrayMergeAll",
|
|
426
|
+
type_parameters: [this.value_type, value2Type],
|
|
427
|
+
arguments: [this[AstSymbol], array[AstSymbol], mergeFnExpr[AstSymbol]],
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Clears all elements from the array, making it empty.
|
|
432
|
+
*
|
|
433
|
+
* @returns Null (mutates the array in place)
|
|
434
|
+
*
|
|
435
|
+
* @example
|
|
436
|
+
* ```ts
|
|
437
|
+
* const clearArray = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
|
|
438
|
+
* arr.clear();
|
|
439
|
+
* $.return(arr);
|
|
440
|
+
* });
|
|
441
|
+
* const compiled = East.compile(clearArray.toIR(), []);
|
|
442
|
+
* compiled([1n, 2n, 3n]); // []
|
|
443
|
+
* ```
|
|
444
|
+
*/
|
|
445
|
+
clear() {
|
|
446
|
+
return this[FactorySymbol]({
|
|
447
|
+
ast_type: "Builtin",
|
|
448
|
+
type: NullType,
|
|
449
|
+
location: get_location(2),
|
|
450
|
+
builtin: "ArrayClear",
|
|
451
|
+
type_parameters: [this.value_type],
|
|
452
|
+
arguments: [this[AstSymbol]],
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Sorts the array in place, re-ordering it in memory.
|
|
457
|
+
*
|
|
458
|
+
* @param by - Optional projection function to determine sort order (defaults to sorting by the values themselves)
|
|
459
|
+
* @returns Null (mutates the array in place)
|
|
460
|
+
*
|
|
461
|
+
* @remarks
|
|
462
|
+
* The optional `by` function can project values to determine sort order (e.g., sort by a specific field).
|
|
463
|
+
*
|
|
464
|
+
* @example
|
|
465
|
+
* ```ts
|
|
466
|
+
* const sortNumbers = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
|
|
467
|
+
* arr.sortInPlace();
|
|
468
|
+
* $.return(arr);
|
|
469
|
+
* });
|
|
470
|
+
* const compiled = East.compile(sortNumbers.toIR(), []);
|
|
471
|
+
* compiled([3n, 1n, 4n, 1n, 5n]); // [1n, 1n, 3n, 4n, 5n]
|
|
472
|
+
* ```
|
|
473
|
+
*
|
|
474
|
+
* @example
|
|
475
|
+
* ```ts
|
|
476
|
+
* // Sorting structs by a field
|
|
477
|
+
* const PersonType = StructType({ name: StringType, age: IntegerType });
|
|
478
|
+
* const sortByAge = East.function([ArrayType(PersonType)], ArrayType(PersonType), ($, people) => {
|
|
479
|
+
* people.sortInPlace(($, p) => p.age);
|
|
480
|
+
* $.return(people);
|
|
481
|
+
* });
|
|
482
|
+
* const compiled = East.compile(sortByAge.toIR(), []);
|
|
483
|
+
* compiled([{ name: "Alice", age: 30n }, { name: "Bob", age: 25n }]);
|
|
484
|
+
* // [{ name: "Bob", age: 25n }, { name: "Alice", age: 30n }]
|
|
485
|
+
* ```
|
|
486
|
+
*
|
|
487
|
+
* @see {@link sort} to produce a new sorted array instead of sorting in place.
|
|
488
|
+
*/
|
|
489
|
+
sortInPlace(by) {
|
|
490
|
+
let byExpr;
|
|
491
|
+
if (by === undefined) {
|
|
492
|
+
byExpr = Expr.function([this.value_type], this.value_type, ($, x) => x);
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
byExpr = by instanceof Expr ? by : Expr.function([this.value_type], undefined, by);
|
|
496
|
+
}
|
|
497
|
+
const byType = byExpr[TypeSymbol];
|
|
498
|
+
const projectedType = byType.output;
|
|
499
|
+
if (!(isDataType(projectedType))) {
|
|
500
|
+
throw new Error(`Can only sort by data types, got ${printType(projectedType)}`);
|
|
501
|
+
}
|
|
502
|
+
return this[FactorySymbol]({
|
|
503
|
+
ast_type: "Builtin",
|
|
504
|
+
type: NullType,
|
|
505
|
+
location: get_location(2),
|
|
506
|
+
builtin: "ArraySortInPlace",
|
|
507
|
+
type_parameters: [this.value_type, projectedType],
|
|
508
|
+
arguments: [this[AstSymbol], byExpr[AstSymbol]],
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Produces a new array with the values sorted.
|
|
513
|
+
*
|
|
514
|
+
* @param by - Optional projection function to determine sort order (defaults to sorting by the values themselves)
|
|
515
|
+
* @returns A new ArrayExpr containing the sorted elements
|
|
516
|
+
*
|
|
517
|
+
* @remarks
|
|
518
|
+
* The optional `by` function can project values to determine sort order (e.g., sort by a specific field).
|
|
519
|
+
* This method does not mutate the original array.
|
|
520
|
+
*
|
|
521
|
+
* @example
|
|
522
|
+
* ```ts
|
|
523
|
+
* const sortNumbers = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
|
|
524
|
+
* $.return(arr.sort());
|
|
525
|
+
* });
|
|
526
|
+
* const compiled = East.compile(sortNumbers.toIR(), []);
|
|
527
|
+
* compiled([3n, 1n, 4n, 1n, 5n]); // [1n, 1n, 3n, 4n, 5n]
|
|
528
|
+
* ```
|
|
529
|
+
*
|
|
530
|
+
* @example
|
|
531
|
+
* ```ts
|
|
532
|
+
* // Sorting structs by a field (descending)
|
|
533
|
+
* const PersonType = StructType({ name: StringType, age: IntegerType });
|
|
534
|
+
* const sortByAgeDesc = East.function([ArrayType(PersonType)], ArrayType(PersonType), ($, people) => {
|
|
535
|
+
* $.return(people.sort(($, p) => p.age.negate()));
|
|
536
|
+
* });
|
|
537
|
+
* const compiled = East.compile(sortByAgeDesc.toIR(), []);
|
|
538
|
+
* compiled([{ name: "Alice", age: 30n }, { name: "Bob", age: 25n }]);
|
|
539
|
+
* // [{ name: "Alice", age: 30n }, { name: "Bob", age: 25n }]
|
|
540
|
+
* ```
|
|
541
|
+
*
|
|
542
|
+
* @see {@link sortInPlace} to sort the array in place.
|
|
543
|
+
*/
|
|
544
|
+
sort(by) {
|
|
545
|
+
let byExpr;
|
|
546
|
+
if (by === undefined) {
|
|
547
|
+
byExpr = Expr.function([this.value_type], this.value_type, ($, x) => x);
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
byExpr = by instanceof Expr ? by : Expr.function([this.value_type], undefined, by);
|
|
551
|
+
}
|
|
552
|
+
const byType = byExpr[TypeSymbol];
|
|
553
|
+
const projectedType = byType.output;
|
|
554
|
+
if (!(isDataType(projectedType))) {
|
|
555
|
+
throw new Error(`Can only sort by data types, got ${printType(projectedType)}`);
|
|
556
|
+
}
|
|
557
|
+
return this[FactorySymbol]({
|
|
558
|
+
ast_type: "Builtin",
|
|
559
|
+
type: this[TypeSymbol],
|
|
560
|
+
location: get_location(2),
|
|
561
|
+
builtin: "ArraySort",
|
|
562
|
+
type_parameters: [this.value_type, projectedType],
|
|
563
|
+
arguments: [this[AstSymbol], byExpr[AstSymbol]],
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Reverses the array in place.
|
|
568
|
+
*
|
|
569
|
+
* @returns Null (mutates the array in place)
|
|
570
|
+
*
|
|
571
|
+
* @example
|
|
572
|
+
* ```ts
|
|
573
|
+
* const reverseArray = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
|
|
574
|
+
* arr.reverseInPlace();
|
|
575
|
+
* $.return(arr);
|
|
576
|
+
* });
|
|
577
|
+
* const compiled = East.compile(reverseArray.toIR(), []);
|
|
578
|
+
* compiled([1n, 2n, 3n, 4n, 5n]); // [5n, 4n, 3n, 2n, 1n]
|
|
579
|
+
* ```
|
|
580
|
+
*
|
|
581
|
+
* @see {@link reverse} to produce a new reversed array instead of reversing in place.
|
|
582
|
+
*/
|
|
583
|
+
reverseInPlace() {
|
|
584
|
+
return this[FactorySymbol]({
|
|
585
|
+
ast_type: "Builtin",
|
|
586
|
+
type: NullType,
|
|
587
|
+
location: get_location(2),
|
|
588
|
+
builtin: "ArrayReverseInPlace",
|
|
589
|
+
type_parameters: [this.value_type],
|
|
590
|
+
arguments: [this[AstSymbol]],
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Produces a new array with the values in reverse order.
|
|
595
|
+
*
|
|
596
|
+
* @returns A new ArrayExpr with elements in reverse order
|
|
597
|
+
*
|
|
598
|
+
* @remarks
|
|
599
|
+
* This method does not mutate the original array.
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* ```ts
|
|
603
|
+
* const reverseArray = East.function([ArrayType(StringType)], ArrayType(StringType), ($, arr) => {
|
|
604
|
+
* $.return(arr.reverse());
|
|
605
|
+
* });
|
|
606
|
+
* const compiled = East.compile(reverseArray.toIR(), []);
|
|
607
|
+
* compiled(["a", "b", "c"]); // ["c", "b", "a"]
|
|
608
|
+
* ```
|
|
609
|
+
*
|
|
610
|
+
* @see {@link reverseInPlace} to reverse the array in place.
|
|
611
|
+
*/
|
|
612
|
+
reverse() {
|
|
613
|
+
return this[FactorySymbol]({
|
|
614
|
+
ast_type: "Builtin",
|
|
615
|
+
type: this[TypeSymbol],
|
|
616
|
+
location: get_location(2),
|
|
617
|
+
builtin: "ArrayReverse",
|
|
618
|
+
type_parameters: [this.value_type],
|
|
619
|
+
arguments: [this[AstSymbol]],
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* Checks if the array is sorted in ascending order.
|
|
624
|
+
*
|
|
625
|
+
* @param by - Optional projection function to determine sort order (defaults to sorting by the values themselves)
|
|
626
|
+
* @returns A BooleanExpr indicating whether the array is sorted
|
|
627
|
+
*
|
|
628
|
+
* @example
|
|
629
|
+
* ```ts
|
|
630
|
+
* const checkSorted = East.function([ArrayType(IntegerType)], BooleanType, ($, arr) => {
|
|
631
|
+
* $.return(arr.isSorted());
|
|
632
|
+
* });
|
|
633
|
+
* const compiled = East.compile(checkSorted.toIR(), []);
|
|
634
|
+
* compiled([1n, 2n, 3n, 4n]); // true
|
|
635
|
+
* compiled([1n, 3n, 2n, 4n]); // false
|
|
636
|
+
* ```
|
|
637
|
+
*
|
|
638
|
+
* @example
|
|
639
|
+
* ```ts
|
|
640
|
+
* // Checking if sorted by a specific field
|
|
641
|
+
* const PersonType = StructType({ name: StringType, age: IntegerType });
|
|
642
|
+
* const checkSortedByAge = East.function([ArrayType(PersonType)], BooleanType, ($, people) => {
|
|
643
|
+
* $.return(people.isSorted(($, p) => p.age));
|
|
644
|
+
* });
|
|
645
|
+
* const compiled = East.compile(checkSortedByAge.toIR(), []);
|
|
646
|
+
* compiled([{ name: "Bob", age: 25n }, { name: "Alice", age: 30n }]); // true
|
|
647
|
+
* ```
|
|
648
|
+
*/
|
|
649
|
+
isSorted(by) {
|
|
650
|
+
let byExpr;
|
|
651
|
+
if (by === undefined) {
|
|
652
|
+
byExpr = Expr.function([this.value_type], this.value_type, ($, x) => x);
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
byExpr = by instanceof Expr ? by : Expr.function([this.value_type], undefined, by);
|
|
656
|
+
}
|
|
657
|
+
const byType = byExpr[TypeSymbol];
|
|
658
|
+
const projectedType = byType.output;
|
|
659
|
+
if (!(isDataType(projectedType))) {
|
|
660
|
+
throw new Error(`Can only sort by data types, got ${printType(projectedType)}`);
|
|
661
|
+
}
|
|
662
|
+
return this[FactorySymbol]({
|
|
663
|
+
ast_type: "Builtin",
|
|
664
|
+
type: BooleanType,
|
|
665
|
+
location: get_location(2),
|
|
666
|
+
builtin: "ArrayIsSorted",
|
|
667
|
+
type_parameters: [this.value_type, projectedType],
|
|
668
|
+
arguments: [this[AstSymbol], byExpr[AstSymbol]],
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Returns the index of the first element in a sorted array that is >= the given value (binary search).
|
|
673
|
+
*
|
|
674
|
+
* @param value - The value to search for
|
|
675
|
+
* @param by - Optional projection function used for sorting (must match the array's sort order)
|
|
676
|
+
* @returns An IntegerExpr representing the index (may be out of bounds if all elements are less)
|
|
677
|
+
*
|
|
678
|
+
* @remarks
|
|
679
|
+
* This method assumes the array is already sorted. If all elements are less than the given value,
|
|
680
|
+
* the returned index will equal the array size (out-of-bounds).
|
|
681
|
+
*
|
|
682
|
+
* @example
|
|
683
|
+
* ```ts
|
|
684
|
+
* const findFirst = East.function([ArrayType(IntegerType), IntegerType], IntegerType, ($, arr, value) => {
|
|
685
|
+
* $.return(arr.findSortedFirst(value));
|
|
686
|
+
* });
|
|
687
|
+
* const compiled = East.compile(findFirst.toIR(), []);
|
|
688
|
+
* compiled([1n, 3n, 5n, 7n, 9n], 5n); // 2n (index of 5)
|
|
689
|
+
* compiled([1n, 3n, 5n, 7n, 9n], 4n); // 2n (index where 4 would be inserted)
|
|
690
|
+
* compiled([1n, 3n, 5n, 7n, 9n], 10n); // 5n (out of bounds)
|
|
691
|
+
* ```
|
|
692
|
+
*
|
|
693
|
+
* @example
|
|
694
|
+
* ```ts
|
|
695
|
+
* // Using with projection function
|
|
696
|
+
* const PersonType = StructType({ name: StringType, age: IntegerType });
|
|
697
|
+
* const findByAge = East.function([ArrayType(PersonType), IntegerType], IntegerType, ($, people, age) => {
|
|
698
|
+
* $.return(people.findSortedFirst(age, ($, p) => p.age));
|
|
699
|
+
* });
|
|
700
|
+
* const compiled = East.compile(findByAge.toIR(), []);
|
|
701
|
+
* // Assumes people array is sorted by age
|
|
702
|
+
* compiled([{ name: "Bob", age: 25n }, { name: "Alice", age: 30n }], 28n); // 1n
|
|
703
|
+
* ```
|
|
704
|
+
*/
|
|
705
|
+
findSortedFirst(value, by) {
|
|
706
|
+
const valueAst = valueOrExprToAst(value);
|
|
707
|
+
const projectedType = valueAst.type;
|
|
708
|
+
if (!isDataType(projectedType)) {
|
|
709
|
+
throw new Error(`Can only search for data types, got ${printType(projectedType)}`);
|
|
710
|
+
}
|
|
711
|
+
let byExpr;
|
|
712
|
+
if (by === undefined) {
|
|
713
|
+
// It would be cool to infer an "obvious" projection here, e.g. prefix of a struct
|
|
714
|
+
byExpr = Expr.function([this.value_type], projectedType, ($, x) => x);
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
byExpr = by instanceof Expr ? by : Expr.function([this.value_type], projectedType, by);
|
|
718
|
+
}
|
|
719
|
+
return this[FactorySymbol]({
|
|
720
|
+
ast_type: "Builtin",
|
|
721
|
+
type: IntegerType,
|
|
722
|
+
location: get_location(2),
|
|
723
|
+
builtin: "ArrayFindSortedFirst",
|
|
724
|
+
type_parameters: [this.value_type, projectedType],
|
|
725
|
+
arguments: [this[AstSymbol], valueAst, byExpr[AstSymbol]],
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Returns the index of the last element in a sorted array that is <= the given value (binary search).
|
|
730
|
+
*
|
|
731
|
+
* @param value - The value to search for
|
|
732
|
+
* @param by - Optional projection function used for sorting (must match the array's sort order)
|
|
733
|
+
* @returns An IntegerExpr representing the index (may be -1 if all elements are greater)
|
|
734
|
+
*
|
|
735
|
+
* @remarks
|
|
736
|
+
* This method assumes the array is already sorted. If all elements are greater than the given value,
|
|
737
|
+
* the returned index will be -1 (out-of-bounds).
|
|
738
|
+
*
|
|
739
|
+
* @example
|
|
740
|
+
* ```ts
|
|
741
|
+
* const findLast = East.function([ArrayType(IntegerType), IntegerType], IntegerType, ($, arr, value) => {
|
|
742
|
+
* $.return(arr.findSortedLast(value));
|
|
743
|
+
* });
|
|
744
|
+
* const compiled = East.compile(findLast.toIR(), []);
|
|
745
|
+
* compiled([1n, 3n, 5n, 7n, 9n], 5n); // 2n (index of 5)
|
|
746
|
+
* compiled([1n, 3n, 5n, 7n, 9n], 6n); // 2n (index of last element <= 6)
|
|
747
|
+
* compiled([1n, 3n, 5n, 7n, 9n], 0n); // -1n (all elements are greater)
|
|
748
|
+
* ```
|
|
749
|
+
*/
|
|
750
|
+
findSortedLast(value, by) {
|
|
751
|
+
const valueAst = valueOrExprToAst(value);
|
|
752
|
+
const projectedType = valueAst.type;
|
|
753
|
+
if (!isDataType(projectedType)) {
|
|
754
|
+
throw new Error(`Can only search for data types, got ${printType(projectedType)}`);
|
|
755
|
+
}
|
|
756
|
+
let byExpr;
|
|
757
|
+
if (by === undefined) {
|
|
758
|
+
// It would be cool to infer an "obvious" projection here, e.g. prefix of a struct
|
|
759
|
+
byExpr = Expr.function([this.value_type], projectedType, ($, x) => x);
|
|
760
|
+
}
|
|
761
|
+
else {
|
|
762
|
+
byExpr = by instanceof Expr ? by : Expr.function([this.value_type], projectedType, by);
|
|
763
|
+
}
|
|
764
|
+
return this[FactorySymbol]({
|
|
765
|
+
ast_type: "Builtin",
|
|
766
|
+
type: IntegerType,
|
|
767
|
+
location: get_location(2),
|
|
768
|
+
builtin: "ArrayFindSortedLast",
|
|
769
|
+
type_parameters: [this.value_type, projectedType],
|
|
770
|
+
arguments: [this[AstSymbol], valueAst, byExpr[AstSymbol]],
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Returns the start and end indices of elements in a sorted array that equal the given value (binary search).
|
|
775
|
+
*
|
|
776
|
+
* @param value - The value to search for
|
|
777
|
+
* @param by - Optional projection function used for sorting (must match the array's sort order)
|
|
778
|
+
* @returns A struct with `start` and `end` indices (exclusive end, may be out of bounds if not found)
|
|
779
|
+
*
|
|
780
|
+
* @remarks
|
|
781
|
+
* This method assumes the array is already sorted. If no elements match, the range will have zero size.
|
|
782
|
+
* The indices may be out-of-bounds if no elements are found.
|
|
783
|
+
*
|
|
784
|
+
* @example
|
|
785
|
+
* ```ts
|
|
786
|
+
* const findRange = East.function([ArrayType(IntegerType), IntegerType], StructType({ start: IntegerType, end: IntegerType }), ($, arr, value) => {
|
|
787
|
+
* $.return(arr.findSortedRange(value));
|
|
788
|
+
* });
|
|
789
|
+
* const compiled = East.compile(findRange.toIR(), []);
|
|
790
|
+
* compiled([1n, 3n, 5n, 5n, 5n, 7n, 9n], 5n); // { start: 2n, end: 5n }
|
|
791
|
+
* compiled([1n, 3n, 5n, 7n, 9n], 4n); // { start: 2n, end: 2n } (not found)
|
|
792
|
+
* ```
|
|
793
|
+
*/
|
|
794
|
+
findSortedRange(value, by) {
|
|
795
|
+
const valueAst = valueOrExprToAst(value);
|
|
796
|
+
const projectedType = valueAst.type;
|
|
797
|
+
if (!isDataType(projectedType)) {
|
|
798
|
+
throw new Error(`Can only search for data types, got ${printType(projectedType)}`);
|
|
799
|
+
}
|
|
800
|
+
let byExpr;
|
|
801
|
+
if (by === undefined) {
|
|
802
|
+
// It would be cool to infer an "obvious" projection here, e.g. prefix of a struct
|
|
803
|
+
byExpr = Expr.function([this.value_type], projectedType, ($, x) => x);
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
byExpr = by instanceof Expr ? by : Expr.function([this.value_type], projectedType, by);
|
|
807
|
+
}
|
|
808
|
+
return this[FactorySymbol]({
|
|
809
|
+
ast_type: "Builtin",
|
|
810
|
+
type: StructType({ start: IntegerType, end: IntegerType }),
|
|
811
|
+
location: get_location(2),
|
|
812
|
+
builtin: "ArrayFindSortedRange",
|
|
813
|
+
type_parameters: [this.value_type, projectedType],
|
|
814
|
+
arguments: [this[AstSymbol], valueAst, byExpr[AstSymbol]],
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Extracts a slice of the array from start to end index (exclusive).
|
|
819
|
+
*
|
|
820
|
+
* @param start - The starting index (inclusive)
|
|
821
|
+
* @param end - The ending index (exclusive)
|
|
822
|
+
* @returns A new ArrayExpr containing the sliced elements
|
|
823
|
+
*
|
|
824
|
+
* @remarks
|
|
825
|
+
* This method does not mutate the original array.
|
|
826
|
+
*
|
|
827
|
+
* @example
|
|
828
|
+
* ```ts
|
|
829
|
+
* const sliceArray = East.function([ArrayType(IntegerType), IntegerType, IntegerType], ArrayType(IntegerType), ($, arr, start, end) => {
|
|
830
|
+
* $.return(arr.slice(start, end));
|
|
831
|
+
* });
|
|
832
|
+
* const compiled = East.compile(sliceArray.toIR(), []);
|
|
833
|
+
* compiled([10n, 20n, 30n, 40n, 50n], 1n, 4n); // [20n, 30n, 40n]
|
|
834
|
+
* compiled([10n, 20n, 30n, 40n, 50n], 0n, 2n); // [10n, 20n]
|
|
835
|
+
* ```
|
|
836
|
+
*/
|
|
837
|
+
slice(start, end) {
|
|
838
|
+
const startExpr = Expr.from(start, IntegerType);
|
|
839
|
+
const endExpr = Expr.from(end, IntegerType);
|
|
840
|
+
return this[FactorySymbol]({
|
|
841
|
+
ast_type: "Builtin",
|
|
842
|
+
type: this[TypeSymbol],
|
|
843
|
+
location: get_location(2),
|
|
844
|
+
builtin: "ArraySlice",
|
|
845
|
+
type_parameters: [this.value_type],
|
|
846
|
+
arguments: [this[AstSymbol], startExpr[AstSymbol], endExpr[AstSymbol]],
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Creates a new array containing values from this array followed by values of the other array.
|
|
851
|
+
*
|
|
852
|
+
* @param other - The array to concatenate
|
|
853
|
+
* @returns A new ArrayExpr with combined elements
|
|
854
|
+
*
|
|
855
|
+
* @remarks
|
|
856
|
+
* This method does not mutate the original arrays.
|
|
857
|
+
*
|
|
858
|
+
* @example
|
|
859
|
+
* ```ts
|
|
860
|
+
* const concatArrays = East.function([ArrayType(IntegerType), ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr1, arr2) => {
|
|
861
|
+
* $.return(arr1.concat(arr2));
|
|
862
|
+
* });
|
|
863
|
+
* const compiled = East.compile(concatArrays.toIR(), []);
|
|
864
|
+
* compiled([1n, 2n, 3n], [4n, 5n]); // [1n, 2n, 3n, 4n, 5n]
|
|
865
|
+
* ```
|
|
866
|
+
*/
|
|
867
|
+
concat(other) {
|
|
868
|
+
const otherExpr = Expr.from(other, this[TypeSymbol]);
|
|
869
|
+
return this[FactorySymbol]({
|
|
870
|
+
ast_type: "Builtin",
|
|
871
|
+
type: this[TypeSymbol],
|
|
872
|
+
location: get_location(2),
|
|
873
|
+
builtin: "ArrayConcat",
|
|
874
|
+
type_parameters: [this.value_type],
|
|
875
|
+
arguments: [this[AstSymbol], otherExpr[AstSymbol]],
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
/**
|
|
879
|
+
* Returns a new array containing values at the specified indices.
|
|
880
|
+
*
|
|
881
|
+
* @param keys - An array of indices to retrieve
|
|
882
|
+
* @param onMissing - Optional function to provide default values for out-of-bounds indices
|
|
883
|
+
* @returns A new ArrayExpr with elements at the specified indices
|
|
884
|
+
*
|
|
885
|
+
* @throws East runtime error if an index is out of bounds and no onMissing function is provided
|
|
886
|
+
*
|
|
887
|
+
* @example
|
|
888
|
+
* ```ts
|
|
889
|
+
* const getMultiple = East.function([ArrayType(StringType), ArrayType(IntegerType)], ArrayType(StringType), ($, arr, indices) => {
|
|
890
|
+
* $.return(arr.getKeys(indices));
|
|
891
|
+
* });
|
|
892
|
+
* const compiled = East.compile(getMultiple.toIR(), []);
|
|
893
|
+
* compiled(["a", "b", "c", "d"], [0n, 2n, 3n]); // ["a", "c", "d"]
|
|
894
|
+
* ```
|
|
895
|
+
*
|
|
896
|
+
* @example
|
|
897
|
+
* ```ts
|
|
898
|
+
* // With default for missing keys
|
|
899
|
+
* const getWithDefault = East.function([ArrayType(IntegerType), ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr, indices) => {
|
|
900
|
+
* $.return(arr.getKeys(indices, ($, key) => -1n));
|
|
901
|
+
* });
|
|
902
|
+
* const compiled = East.compile(getWithDefault.toIR(), []);
|
|
903
|
+
* compiled([10n, 20n, 30n], [1n, 5n, 2n]); // [20n, -1n, 30n]
|
|
904
|
+
* ```
|
|
905
|
+
*/
|
|
906
|
+
getKeys(keys, onMissing) {
|
|
907
|
+
const keysExpr = Expr.from(keys, ArrayType(IntegerType));
|
|
908
|
+
let default_function_ast;
|
|
909
|
+
if (onMissing === undefined) {
|
|
910
|
+
const location = get_location(2);
|
|
911
|
+
const default_function = Expr.function([IntegerType], this.value_type, ($, key) => $.error(Expr.str `Cannot get key ${key} from array`, location));
|
|
912
|
+
default_function_ast = Expr.ast(default_function);
|
|
913
|
+
}
|
|
914
|
+
else {
|
|
915
|
+
const default_function_expr = Expr.from(onMissing, FunctionType([IntegerType], this.value_type, null));
|
|
916
|
+
default_function_ast = Expr.ast(default_function_expr);
|
|
917
|
+
}
|
|
918
|
+
return this[FactorySymbol]({
|
|
919
|
+
ast_type: "Builtin",
|
|
920
|
+
type: this[TypeSymbol],
|
|
921
|
+
location: get_location(2),
|
|
922
|
+
builtin: "ArrayGetKeys",
|
|
923
|
+
type_parameters: [this.value_type],
|
|
924
|
+
arguments: [this[AstSymbol], keysExpr[AstSymbol], default_function_ast],
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Executes a function for each element in the array.
|
|
929
|
+
*
|
|
930
|
+
* @param fn - Function taking (element, index) to execute for each entry
|
|
931
|
+
* @returns Null
|
|
932
|
+
*
|
|
933
|
+
* @example
|
|
934
|
+
* ```ts
|
|
935
|
+
* const log = East.platform(FunctionType([StringType], NullType));
|
|
936
|
+
* const forEachExample = East.function([ArrayType(IntegerType)], NullType, ($, arr) => {
|
|
937
|
+
* arr.forEach(($, elem, index) => {
|
|
938
|
+
* log(Expr.str`Index ${index}: ${elem}`);
|
|
939
|
+
* });
|
|
940
|
+
* $.return(null);
|
|
941
|
+
* });
|
|
942
|
+
* const compiled = East.compile(forEachExample.toIR(), [log.implement(console.log)]);
|
|
943
|
+
* compiled([10n, 20n, 30n]);
|
|
944
|
+
* // Logs: "Index 0: 10", "Index 1: 20", "Index 2: 30"
|
|
945
|
+
* ```
|
|
946
|
+
*/
|
|
947
|
+
forEach(fn) {
|
|
948
|
+
if (!(fn instanceof Expr)) {
|
|
949
|
+
fn = Expr.function([this.value_type, IntegerType], undefined, fn);
|
|
950
|
+
}
|
|
951
|
+
const fnType = fn[TypeSymbol];
|
|
952
|
+
const returnType = fnType.output;
|
|
953
|
+
return this[FactorySymbol]({
|
|
954
|
+
ast_type: "Builtin",
|
|
955
|
+
type: NullType,
|
|
956
|
+
location: get_location(2),
|
|
957
|
+
builtin: "ArrayForEach",
|
|
958
|
+
type_parameters: [this.value_type, returnType],
|
|
959
|
+
arguments: [this[AstSymbol], fn[AstSymbol]],
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Creates a shallow copy of the array.
|
|
964
|
+
*
|
|
965
|
+
* @returns A new ArrayExpr with the same elements
|
|
966
|
+
*
|
|
967
|
+
* @remarks
|
|
968
|
+
* Only the top-level array is cloned. Deeply nested structures (e.g., arrays of arrays) share references.
|
|
969
|
+
*
|
|
970
|
+
* @example
|
|
971
|
+
* ```ts
|
|
972
|
+
* const copyArray = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
|
|
973
|
+
* const copy = arr.copy();
|
|
974
|
+
* copy.pushLast(99n);
|
|
975
|
+
* $.return(copy);
|
|
976
|
+
* });
|
|
977
|
+
* const compiled = East.compile(copyArray.toIR(), []);
|
|
978
|
+
* const original = [1n, 2n, 3n];
|
|
979
|
+
* compiled(original); // [1n, 2n, 3n, 99n]
|
|
980
|
+
* // original is unchanged: [1n, 2n, 3n]
|
|
981
|
+
* ```
|
|
982
|
+
*/
|
|
983
|
+
copy() {
|
|
984
|
+
return this[FactorySymbol]({
|
|
985
|
+
ast_type: "Builtin",
|
|
986
|
+
type: this[TypeSymbol],
|
|
987
|
+
location: get_location(2),
|
|
988
|
+
builtin: "ArrayCopy",
|
|
989
|
+
type_parameters: [this.value_type],
|
|
990
|
+
arguments: [this[AstSymbol]],
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
map(fn) {
|
|
994
|
+
// We need to infer the output type
|
|
995
|
+
if (fn instanceof Expr) {
|
|
996
|
+
// The function was provided as an expression
|
|
997
|
+
if (!(fn[TypeSymbol] && fn[TypeSymbol].type === "Function")) {
|
|
998
|
+
throw new Error("Expected a Function expression");
|
|
999
|
+
}
|
|
1000
|
+
const output_type = fn[TypeSymbol].output;
|
|
1001
|
+
const n_inputs = fn[TypeSymbol].inputs.length;
|
|
1002
|
+
if (n_inputs === 2) {
|
|
1003
|
+
// Two input function (value, index)
|
|
1004
|
+
if (!isSubtype(this.value_type, fn[TypeSymbol].inputs[0])) {
|
|
1005
|
+
throw new Error(`Expected Function input to be ${printType(this.value_type)}, got ${printType(fn[TypeSymbol].inputs[0])}`);
|
|
1006
|
+
}
|
|
1007
|
+
if (!isTypeEqual(IntegerType, fn[TypeSymbol].inputs[1])) {
|
|
1008
|
+
throw new Error(`Expected Function second input to be ${printType(IntegerType)}, got ${printType(fn[TypeSymbol].inputs[1])}`);
|
|
1009
|
+
}
|
|
1010
|
+
return this[FactorySymbol]({
|
|
1011
|
+
ast_type: "Builtin",
|
|
1012
|
+
type: ArrayType(output_type),
|
|
1013
|
+
location: get_location(2),
|
|
1014
|
+
builtin: "ArrayMap",
|
|
1015
|
+
type_parameters: [this.value_type, output_type],
|
|
1016
|
+
arguments: [this[AstSymbol], fn[AstSymbol]],
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
else {
|
|
1020
|
+
throw new Error(`Expected Function to have 2 inputs, got ${n_inputs} inputs`);
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
else {
|
|
1024
|
+
// The function was provided as a lambda
|
|
1025
|
+
// We need to convert it to a FunctionExpr first, but we also need to infer the output type (using undefined)
|
|
1026
|
+
const functionExpr = Expr.function([this.value_type, IntegerType], undefined, fn);
|
|
1027
|
+
return this.map(functionExpr);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Filters array elements using a predicate function.
|
|
1032
|
+
*
|
|
1033
|
+
* @param predicate - Function taking (element, index) and returning true to keep the element
|
|
1034
|
+
* @returns A new ArrayExpr containing only elements for which the predicate returned true
|
|
1035
|
+
*
|
|
1036
|
+
* @example
|
|
1037
|
+
* ```ts
|
|
1038
|
+
* const filterEven = East.function([ArrayType(IntegerType)], ArrayType(IntegerType), ($, arr) => {
|
|
1039
|
+
* $.return(arr.filter(($, x, i) => x.modulo(2n).equal(0n)));
|
|
1040
|
+
* });
|
|
1041
|
+
* const compiled = East.compile(filterEven.toIR(), []);
|
|
1042
|
+
* compiled([1n, 2n, 3n, 4n, 5n, 6n]); // [2n, 4n, 6n]
|
|
1043
|
+
* ```
|
|
1044
|
+
*
|
|
1045
|
+
* @example
|
|
1046
|
+
* ```ts
|
|
1047
|
+
* // Filter by index
|
|
1048
|
+
* const filterFirstThree = East.function([ArrayType(StringType)], ArrayType(StringType), ($, arr) => {
|
|
1049
|
+
* $.return(arr.filter(($, elem, index) => index.lessThan(3n)));
|
|
1050
|
+
* });
|
|
1051
|
+
* const compiled = East.compile(filterFirstThree.toIR(), []);
|
|
1052
|
+
* compiled(["a", "b", "c", "d", "e"]); // ["a", "b", "c"]
|
|
1053
|
+
* ```
|
|
1054
|
+
*
|
|
1055
|
+
* @example
|
|
1056
|
+
* ```ts
|
|
1057
|
+
* // Filter structs by field
|
|
1058
|
+
* const PersonType = StructType({ name: StringType, age: IntegerType });
|
|
1059
|
+
* const filterAdults = East.function([ArrayType(PersonType)], ArrayType(PersonType), ($, people) => {
|
|
1060
|
+
* $.return(people.filter(($, p, i) => p.age.greaterOrEqual(18n)));
|
|
1061
|
+
* });
|
|
1062
|
+
* const compiled = East.compile(filterAdults.toIR(), []);
|
|
1063
|
+
* compiled([{ name: "Alice", age: 30n }, { name: "Bob", age: 15n }]); // [{ name: "Alice", age: 30n }]
|
|
1064
|
+
* ```
|
|
1065
|
+
*/
|
|
1066
|
+
filter(predicate) {
|
|
1067
|
+
if (!(predicate instanceof Expr)) {
|
|
1068
|
+
predicate = Expr.function([this.value_type, IntegerType], BooleanType, predicate);
|
|
1069
|
+
}
|
|
1070
|
+
// Check if it's actually a FunctionExpr by looking at its type
|
|
1071
|
+
if (!predicate[TypeSymbol] || predicate[TypeSymbol].type !== "Function") {
|
|
1072
|
+
throw new Error("forEach expects a Function expression");
|
|
1073
|
+
}
|
|
1074
|
+
const functionType = predicate[TypeSymbol];
|
|
1075
|
+
if (functionType.inputs.length === 2) {
|
|
1076
|
+
// Two input function (value, index)
|
|
1077
|
+
if (!isSubtype(this.value_type, functionType.inputs[0])) {
|
|
1078
|
+
throw new Error(`Expected Function input to be ${printType(this.value_type)}, got ${printType(functionType.inputs[0])}`);
|
|
1079
|
+
}
|
|
1080
|
+
if (!isTypeEqual(IntegerType, functionType.inputs[1])) {
|
|
1081
|
+
throw new Error(`Expected Function second input to be ${printType(IntegerType)}, got ${printType(functionType.inputs[1])}`);
|
|
1082
|
+
}
|
|
1083
|
+
return this[FactorySymbol]({
|
|
1084
|
+
ast_type: "Builtin",
|
|
1085
|
+
location: get_location(2),
|
|
1086
|
+
type: this[TypeSymbol],
|
|
1087
|
+
builtin: "ArrayFilter",
|
|
1088
|
+
type_parameters: [this.value_type],
|
|
1089
|
+
arguments: [this[AstSymbol], predicate[AstSymbol]],
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
else {
|
|
1093
|
+
throw new Error(`Expected Function to have 2 inputs, got ${functionType.inputs.length} inputs`);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
filterMap(fn) {
|
|
1097
|
+
const fnAst = valueOrExprToAstTyped(fn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1098
|
+
const returnType = fnAst.type.output;
|
|
1099
|
+
if (returnType.type !== "Variant") {
|
|
1100
|
+
throw new Error(`Expected Function to return an Option type, got ${printType(returnType)}`);
|
|
1101
|
+
}
|
|
1102
|
+
if (!Object.keys(returnType.cases).every(k => k === "none" || k === "some")) {
|
|
1103
|
+
throw new Error(`Expected Function to return an Option type, got ${printType(returnType)}`);
|
|
1104
|
+
}
|
|
1105
|
+
const someType = returnType.cases["some"] ?? NeverType;
|
|
1106
|
+
return this[FactorySymbol]({
|
|
1107
|
+
ast_type: "Builtin",
|
|
1108
|
+
type: ArrayType(someType),
|
|
1109
|
+
location: get_location(2),
|
|
1110
|
+
builtin: "ArrayFilterMap",
|
|
1111
|
+
type_parameters: [this.value_type, someType],
|
|
1112
|
+
arguments: [this[AstSymbol], fnAst],
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
firstMap(fn) {
|
|
1116
|
+
const fnAst = valueOrExprToAstTyped(fn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1117
|
+
const returnType = fnAst.type.output;
|
|
1118
|
+
if (returnType.type !== "Variant") {
|
|
1119
|
+
throw new Error(`Expected Function to return an Option type, got ${printType(returnType)}`);
|
|
1120
|
+
}
|
|
1121
|
+
if (!Object.keys(returnType.cases).every(k => k === "none" || k === "some")) {
|
|
1122
|
+
throw new Error(`Expected Function to return an Option type, got ${printType(returnType)}`);
|
|
1123
|
+
}
|
|
1124
|
+
const someType = returnType.cases["some"] ?? NeverType;
|
|
1125
|
+
return this[FactorySymbol]({
|
|
1126
|
+
ast_type: "Builtin",
|
|
1127
|
+
type: returnType,
|
|
1128
|
+
location: get_location(2),
|
|
1129
|
+
builtin: "ArrayFirstMap",
|
|
1130
|
+
type_parameters: [this.value_type, someType],
|
|
1131
|
+
arguments: [this[AstSymbol], fnAst],
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
findFirst(value, by) {
|
|
1135
|
+
if (by === undefined) {
|
|
1136
|
+
return this.firstMap(($, v, k) => Expr.equal(v, value).ifElse(() => some(k), () => none));
|
|
1137
|
+
}
|
|
1138
|
+
else {
|
|
1139
|
+
const byExpr = (by instanceof Expr ? by : Expr.function([this.value_type, IntegerType], undefined, by));
|
|
1140
|
+
if (byExpr[TypeSymbol].type !== "Function") {
|
|
1141
|
+
throw new Error("Expected a Function expression for 'by' parameter");
|
|
1142
|
+
}
|
|
1143
|
+
const byType = byExpr[TypeSymbol];
|
|
1144
|
+
if (byType.inputs.length !== 2) {
|
|
1145
|
+
throw new Error(`Expected 'by' function to have 2 inputs, got ${byType.inputs.length}`);
|
|
1146
|
+
}
|
|
1147
|
+
if (!isSubtype(this.value_type, byType.inputs[0])) {
|
|
1148
|
+
throw new Error(`Expected 'by' function input to be ${printType(this.value_type)}, got ${printType(byType.inputs[0])}`);
|
|
1149
|
+
}
|
|
1150
|
+
return this.firstMap(($, v, k) => Expr.equal(byExpr(v, k), value).ifElse(() => some(k), () => none));
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
findAll(value, by) {
|
|
1154
|
+
if (by === undefined) {
|
|
1155
|
+
return this.filterMap(($, v, k) => Expr.equal(v, value).ifElse(() => some(k), () => none));
|
|
1156
|
+
}
|
|
1157
|
+
else {
|
|
1158
|
+
const byExpr = (by instanceof Expr ? by : Expr.function([this.value_type, IntegerType], undefined, by));
|
|
1159
|
+
if (byExpr[TypeSymbol].type !== "Function") {
|
|
1160
|
+
throw new Error("Expected a Function expression for 'by' parameter");
|
|
1161
|
+
}
|
|
1162
|
+
const byType = byExpr[TypeSymbol];
|
|
1163
|
+
if (byType.inputs.length !== 2) {
|
|
1164
|
+
throw new Error(`Expected 'by' function to have 2 inputs, got ${byType.inputs.length}`);
|
|
1165
|
+
}
|
|
1166
|
+
if (!isSubtype(this.value_type, byType.inputs[0])) {
|
|
1167
|
+
throw new Error(`Expected 'by' function input to be ${printType(this.value_type)}, got ${printType(byType.inputs[0])}`);
|
|
1168
|
+
}
|
|
1169
|
+
return this.filterMap(($, v, k) => Expr.equal(byExpr(v, k), value).ifElse(() => some(k), () => none));
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Reduces the array to a single value using an accumulator function and initial value.
|
|
1174
|
+
*
|
|
1175
|
+
* @param combineFn - Function taking (accumulator, element, index) and returning the new accumulator value
|
|
1176
|
+
* @param init - Initial value for the reduction (determines output type)
|
|
1177
|
+
* @returns The final accumulated value
|
|
1178
|
+
*
|
|
1179
|
+
* @remarks
|
|
1180
|
+
* The accumulator function is called for each element with the previous result (or initial value for first element),
|
|
1181
|
+
* the current value, and the current index. This operation is safe on empty arrays (returns the initial value).
|
|
1182
|
+
*
|
|
1183
|
+
* @example
|
|
1184
|
+
* ```ts
|
|
1185
|
+
* const sumArray = East.function([ArrayType(IntegerType)], IntegerType, ($, arr) => {
|
|
1186
|
+
* $.return(arr.reduce(($, acc, val, index) => acc.add(val), 0n));
|
|
1187
|
+
* });
|
|
1188
|
+
* const compiled = East.compile(sumArray.toIR(), []);
|
|
1189
|
+
* compiled([1n, 2n, 3n, 4n]); // 10n
|
|
1190
|
+
* compiled([]); // 0n (initial value)
|
|
1191
|
+
* ```
|
|
1192
|
+
*
|
|
1193
|
+
* @example
|
|
1194
|
+
* ```ts
|
|
1195
|
+
* // Building a string from array
|
|
1196
|
+
* const joinArray = East.function([ArrayType(StringType)], StringType, ($, arr) => {
|
|
1197
|
+
* $.return(arr.reduce(($, acc, val, index) =>
|
|
1198
|
+
* $.if(index.equal(0n), () => val, () => acc.concat(", ").concat(val))
|
|
1199
|
+
* , ""));
|
|
1200
|
+
* });
|
|
1201
|
+
* const compiled = East.compile(joinArray.toIR(), []);
|
|
1202
|
+
* compiled(["a", "b", "c"]); // "a, b, c"
|
|
1203
|
+
* ```
|
|
1204
|
+
*
|
|
1205
|
+
* @see {@link mapReduce} for a version that projects each value before reducing (and errors on empty arrays).
|
|
1206
|
+
* @see {@link sum}, {@link mean}, {@link every}, and {@link some} for common reduction operations.
|
|
1207
|
+
*/
|
|
1208
|
+
reduce(combineFn, init) {
|
|
1209
|
+
const initAst = valueOrExprToAst(init);
|
|
1210
|
+
const returnType = initAst.type;
|
|
1211
|
+
const combineAst = valueOrExprToAstTyped(combineFn, FunctionType([returnType, this.value_type, IntegerType], returnType, null));
|
|
1212
|
+
return this[FactorySymbol]({
|
|
1213
|
+
ast_type: "Builtin",
|
|
1214
|
+
type: returnType,
|
|
1215
|
+
location: get_location(2),
|
|
1216
|
+
builtin: "ArrayFold",
|
|
1217
|
+
type_parameters: [this.value_type, returnType],
|
|
1218
|
+
arguments: [this[AstSymbol], initAst, combineAst],
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
mapReduce(mapFn, combineFn) {
|
|
1222
|
+
const mapAst = valueOrExprToAstTyped(mapFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1223
|
+
const mapType = mapAst.type.output;
|
|
1224
|
+
const combineAst = valueOrExprToAstTyped(combineFn, FunctionType([mapType, mapType], mapType, null));
|
|
1225
|
+
return this[FactorySymbol]({
|
|
1226
|
+
ast_type: "Builtin",
|
|
1227
|
+
type: mapType,
|
|
1228
|
+
location: get_location(2),
|
|
1229
|
+
builtin: "ArrayMapReduce",
|
|
1230
|
+
type_parameters: [this.value_type, mapType],
|
|
1231
|
+
arguments: [this[AstSymbol], mapAst, combineAst],
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
// Common reducers are provided, based on reduce
|
|
1235
|
+
/**
|
|
1236
|
+
* Returns true if every element satisfies the predicate, false otherwise.
|
|
1237
|
+
*
|
|
1238
|
+
* @param fn - Optional predicate function (required for non-Boolean arrays)
|
|
1239
|
+
* @returns A BooleanExpr (true for empty arrays)
|
|
1240
|
+
*
|
|
1241
|
+
* @remarks
|
|
1242
|
+
* This method short-circuits on the first false element for efficiency.
|
|
1243
|
+
*
|
|
1244
|
+
* @example
|
|
1245
|
+
* ```ts
|
|
1246
|
+
* const allPositive = East.function([ArrayType(IntegerType)], BooleanType, ($, arr) => {
|
|
1247
|
+
* $.return(arr.every(($, x, i) => x.greater(0n)));
|
|
1248
|
+
* });
|
|
1249
|
+
* const compiled = East.compile(allPositive.toIR(), []);
|
|
1250
|
+
* compiled([1n, 2n, 3n]); // true
|
|
1251
|
+
* compiled([1n, -2n, 3n]); // false
|
|
1252
|
+
* compiled([]); // true (empty array)
|
|
1253
|
+
* ```
|
|
1254
|
+
*
|
|
1255
|
+
* @example
|
|
1256
|
+
* ```ts
|
|
1257
|
+
* // Checking structs
|
|
1258
|
+
* const PersonType = StructType({ name: StringType, age: IntegerType });
|
|
1259
|
+
* const allAdults = East.function([ArrayType(PersonType)], BooleanType, ($, people) => {
|
|
1260
|
+
* $.return(people.every(($, p, i) => p.age.greaterOrEqual(18n)));
|
|
1261
|
+
* });
|
|
1262
|
+
* const compiled = East.compile(allAdults.toIR(), []);
|
|
1263
|
+
* compiled([{ name: "Alice", age: 30n }, { name: "Bob", age: 25n }]); // true
|
|
1264
|
+
* ```
|
|
1265
|
+
*
|
|
1266
|
+
* @see {@link some} to check if at least one element is true.
|
|
1267
|
+
*/
|
|
1268
|
+
every(fn) {
|
|
1269
|
+
if (fn === undefined) {
|
|
1270
|
+
if (!isTypeEqual(this.value_type, BooleanType)) {
|
|
1271
|
+
throw new Error(`Can only perform every on array of booleans, got ${printType(this.value_type)}`);
|
|
1272
|
+
}
|
|
1273
|
+
// Short-circuit on first false value
|
|
1274
|
+
const result = this.firstMap(($, v, _k) => v.not().ifElse(() => some(null), () => none));
|
|
1275
|
+
return Expr.match(result, { some: () => false, none: () => true });
|
|
1276
|
+
}
|
|
1277
|
+
const fnAst = valueOrExprToAstTyped(fn, FunctionType([this.value_type, IntegerType], BooleanType, null));
|
|
1278
|
+
// Short-circuit on first false value
|
|
1279
|
+
const result = this.firstMap(($, v, k) => {
|
|
1280
|
+
const result = Expr.fromAst(fnAst)(v, k);
|
|
1281
|
+
return result.not().ifElse(() => some(null), () => none);
|
|
1282
|
+
});
|
|
1283
|
+
return Expr.match(result, { some: () => false, none: () => true });
|
|
1284
|
+
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Returns true if at least one element satisfies the predicate, false otherwise.
|
|
1287
|
+
*
|
|
1288
|
+
* @param fn - Optional predicate function (required for non-Boolean arrays)
|
|
1289
|
+
* @returns A BooleanExpr (false for empty arrays)
|
|
1290
|
+
*
|
|
1291
|
+
* @remarks
|
|
1292
|
+
* This method short-circuits on the first true element for efficiency.
|
|
1293
|
+
*
|
|
1294
|
+
* @example
|
|
1295
|
+
* ```ts
|
|
1296
|
+
* const hasNegative = East.function([ArrayType(IntegerType)], BooleanType, ($, arr) => {
|
|
1297
|
+
* $.return(arr.some(($, x, i) => x.less(0n)));
|
|
1298
|
+
* });
|
|
1299
|
+
* const compiled = East.compile(hasNegative.toIR(), []);
|
|
1300
|
+
* compiled([1n, 2n, 3n]); // false
|
|
1301
|
+
* compiled([1n, -2n, 3n]); // true
|
|
1302
|
+
* compiled([]); // false (empty array)
|
|
1303
|
+
* ```
|
|
1304
|
+
*
|
|
1305
|
+
* @example
|
|
1306
|
+
* ```ts
|
|
1307
|
+
* // Checking structs
|
|
1308
|
+
* const PersonType = StructType({ name: StringType, age: IntegerType });
|
|
1309
|
+
* const hasMinor = East.function([ArrayType(PersonType)], BooleanType, ($, people) => {
|
|
1310
|
+
* $.return(people.some(($, p, i) => p.age.less(18n)));
|
|
1311
|
+
* });
|
|
1312
|
+
* const compiled = East.compile(hasMinor.toIR(), []);
|
|
1313
|
+
* compiled([{ name: "Alice", age: 30n }, { name: "Bob", age: 15n }]); // true
|
|
1314
|
+
* ```
|
|
1315
|
+
*
|
|
1316
|
+
* @see {@link every} to check if all elements are true.
|
|
1317
|
+
*/
|
|
1318
|
+
some(fn) {
|
|
1319
|
+
if (fn === undefined) {
|
|
1320
|
+
if (!isTypeEqual(this.value_type, BooleanType)) {
|
|
1321
|
+
throw new Error(`Can only perform some on array of booleans, got ${printType(this.value_type)}`);
|
|
1322
|
+
}
|
|
1323
|
+
// Short-circuit on first true value
|
|
1324
|
+
const result = this.firstMap(($, v, _k) => v.ifElse(() => some(null), () => none));
|
|
1325
|
+
return Expr.match(result, { some: () => true, none: () => false });
|
|
1326
|
+
}
|
|
1327
|
+
const fnAst = valueOrExprToAstTyped(fn, FunctionType([this.value_type, IntegerType], BooleanType, null));
|
|
1328
|
+
// Short-circuit on first true value
|
|
1329
|
+
const result = this.firstMap(($, v, k) => {
|
|
1330
|
+
const result = Expr.fromAst(fnAst)(v, k);
|
|
1331
|
+
return result.ifElse(() => some(null), () => none);
|
|
1332
|
+
});
|
|
1333
|
+
return Expr.match(result, { some: () => true, none: () => false });
|
|
1334
|
+
}
|
|
1335
|
+
sum(fn) {
|
|
1336
|
+
if (fn === undefined) {
|
|
1337
|
+
if (!(isTypeEqual(this.value_type, IntegerType) || isTypeEqual(this.value_type, FloatType))) {
|
|
1338
|
+
throw new Error(`Can only perform sum on array of numbers (Integer or Float), got ${printType(this.value_type)}`);
|
|
1339
|
+
}
|
|
1340
|
+
const zero = isTypeEqual(this.value_type, IntegerType) ? 0n : 0.0;
|
|
1341
|
+
return this.reduce(($, previous, value) => previous.add(value), zero);
|
|
1342
|
+
}
|
|
1343
|
+
else {
|
|
1344
|
+
const fnAst = valueOrExprToAstTyped(fn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1345
|
+
const returnType = fnAst.type.output;
|
|
1346
|
+
if (!(isTypeEqual(returnType, IntegerType) || isTypeEqual(returnType, FloatType))) {
|
|
1347
|
+
throw new Error(`Can only perform sum on array of numbers (Integer or Float), got ${printType(returnType)}`);
|
|
1348
|
+
}
|
|
1349
|
+
const zero = isTypeEqual(returnType, IntegerType) ? 0n : 0.0;
|
|
1350
|
+
return this.reduce(($, previous, value, key) => previous.add(Expr.fromAst(fnAst)(value, key)), zero);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
mean(fn) {
|
|
1354
|
+
if (fn === undefined) {
|
|
1355
|
+
if (isTypeEqual(this.value_type, IntegerType)) {
|
|
1356
|
+
return this.reduce(($, previous, value) => previous.add(value.toFloat()), 0.0).divide(this.size().toFloat());
|
|
1357
|
+
}
|
|
1358
|
+
else if (isTypeEqual(this.value_type, FloatType)) {
|
|
1359
|
+
return this.reduce(($, previous, value) => previous.add(value), 0.0).divide(this.size().toFloat());
|
|
1360
|
+
}
|
|
1361
|
+
else {
|
|
1362
|
+
throw new Error(`Can only perform sum on array of numbers (Integer or Float), got ${printType(this.value_type)}`);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
else {
|
|
1366
|
+
const fnAst = valueOrExprToAstTyped(fn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1367
|
+
const returnType = fnAst.type.output;
|
|
1368
|
+
if (isTypeEqual(returnType, IntegerType)) {
|
|
1369
|
+
return this.reduce(($, previous, value, key) => previous.add(Expr.fromAst(fnAst)(value, key).toFloat()), 0.0).divide(this.size().toFloat());
|
|
1370
|
+
}
|
|
1371
|
+
else if (isTypeEqual(returnType, FloatType)) {
|
|
1372
|
+
return this.reduce(($, previous, value, key) => previous.add(Expr.fromAst(fnAst)(value, key)), 0.0).divide(this.size().toFloat());
|
|
1373
|
+
}
|
|
1374
|
+
else {
|
|
1375
|
+
throw new Error(`Can only perform sum on array of numbers (Integer or Float), got ${printType(returnType)}`);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
findMaximum(by) {
|
|
1380
|
+
if (by === undefined) {
|
|
1381
|
+
// No projection - compare elements directly
|
|
1382
|
+
if (!(isDataType(this.value_type))) {
|
|
1383
|
+
throw new Error(`Can only perform findMaximum on arrays of data types, got ${printType(this.value_type)}`);
|
|
1384
|
+
}
|
|
1385
|
+
// Check if array is empty first
|
|
1386
|
+
return Expr.equal(this.size(), 0n).ifElse(() => none, () => {
|
|
1387
|
+
const result = this.mapReduce(($, x, k) => ({ index: k, value: x }), ($, a, b) => Expr.greaterEqual(a.value, b.value).ifElse(() => a, () => b));
|
|
1388
|
+
return some(result.index);
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
else {
|
|
1392
|
+
// With projection - need to track element, projected value, and index
|
|
1393
|
+
const byExpr = (by instanceof Expr ? by : Expr.function([this.value_type, IntegerType], undefined, by));
|
|
1394
|
+
if (byExpr[TypeSymbol].type !== "Function") {
|
|
1395
|
+
throw new Error("Expected a Function expression for 'by' parameter");
|
|
1396
|
+
}
|
|
1397
|
+
const byType = byExpr[TypeSymbol];
|
|
1398
|
+
const projectedType = byType.output;
|
|
1399
|
+
if (!isDataType(projectedType)) {
|
|
1400
|
+
throw new Error(`Can only compare data types, got ${printType(projectedType)}`);
|
|
1401
|
+
}
|
|
1402
|
+
// Check if array is empty first
|
|
1403
|
+
return Expr.equal(this.size(), 0n).ifElse(() => none, () => {
|
|
1404
|
+
// Map to { index, key }, reduce by comparing keys, extract index
|
|
1405
|
+
const result = this.mapReduce(($, x, k) => ({ index: k, key: byExpr(x, k) }), ($, a, b) => Expr.greaterEqual(a.key, b.key).ifElse(() => a, () => b));
|
|
1406
|
+
return some(result.index);
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
findMinimum(by) {
|
|
1411
|
+
if (by === undefined) {
|
|
1412
|
+
// No projection - compare elements directly
|
|
1413
|
+
if (!(isDataType(this.value_type))) {
|
|
1414
|
+
throw new Error(`Can only perform findMinimum on arrays of data types, got ${printType(this.value_type)}`);
|
|
1415
|
+
}
|
|
1416
|
+
// Check if array is empty first
|
|
1417
|
+
return Expr.equal(this.size(), 0n).ifElse(() => none, () => {
|
|
1418
|
+
const result = this.mapReduce(($, x, k) => ({ index: k, value: x }), ($, a, b) => Expr.lessEqual(a.value, b.value).ifElse(() => a, () => b));
|
|
1419
|
+
return some(result.index);
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
else {
|
|
1423
|
+
// With projection - need to track element, projected value, and index
|
|
1424
|
+
const byExpr = (by instanceof Expr ? by : Expr.function([this.value_type, IntegerType], undefined, by));
|
|
1425
|
+
if (byExpr[TypeSymbol].type !== "Function") {
|
|
1426
|
+
throw new Error("Expected a Function expression for 'by' parameter");
|
|
1427
|
+
}
|
|
1428
|
+
const byType = byExpr[TypeSymbol];
|
|
1429
|
+
const projectedType = byType.output;
|
|
1430
|
+
if (!isDataType(projectedType)) {
|
|
1431
|
+
throw new Error(`Can only compare data types, got ${printType(projectedType)}`);
|
|
1432
|
+
}
|
|
1433
|
+
// Check if array is empty first
|
|
1434
|
+
return Expr.equal(this.size(), 0n).ifElse(() => none, () => {
|
|
1435
|
+
// Map to { index, key }, reduce by comparing keys, extract index
|
|
1436
|
+
const result = this.mapReduce(($, x, k) => ({ index: k, key: byExpr(x, k) }), ($, a, b) => Expr.lessEqual(a.key, b.key).ifElse(() => a, () => b));
|
|
1437
|
+
return some(result.index);
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
maximum(by) {
|
|
1442
|
+
if (by === undefined) {
|
|
1443
|
+
// No projection - compare elements directly
|
|
1444
|
+
// Use mapReduce with lambda functions directly
|
|
1445
|
+
return this.mapReduce(($, x) => x, Expr.function([this.value_type, this.value_type], this.value_type, ($, a, b) => Expr.max(a, b)));
|
|
1446
|
+
}
|
|
1447
|
+
else {
|
|
1448
|
+
// With projection - need to track both element and projected value
|
|
1449
|
+
const byExpr = (by instanceof Expr ? by : Expr.function([this.value_type, IntegerType], undefined, by));
|
|
1450
|
+
if (byExpr[TypeSymbol].type !== "Function") {
|
|
1451
|
+
throw new Error("Expected a Function expression for 'by' parameter");
|
|
1452
|
+
}
|
|
1453
|
+
const byType = byExpr[TypeSymbol];
|
|
1454
|
+
const projectedType = byType.output;
|
|
1455
|
+
if (!isDataType(projectedType)) {
|
|
1456
|
+
throw new Error(`Can only compare data types, got ${printType(projectedType)}`);
|
|
1457
|
+
}
|
|
1458
|
+
// Map to { element, key }, reduce by comparing keys, extract element
|
|
1459
|
+
// Use greaterEqual to keep the first element when keys are equal
|
|
1460
|
+
const result = this.mapReduce(($, x, k) => ({ element: x, key: byExpr(x, k) }), ($, a, b) => Expr.greaterEqual(a.key, b.key).ifElse(() => a, () => b));
|
|
1461
|
+
return result.element;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
minimum(by) {
|
|
1465
|
+
if (by === undefined) {
|
|
1466
|
+
// No projection - compare elements directly
|
|
1467
|
+
if (!(isDataType(this.value_type))) {
|
|
1468
|
+
throw new Error(`Can only perform minimum on arrays of data types, got ${printType(this.value_type)}`);
|
|
1469
|
+
}
|
|
1470
|
+
// Use mapReduce with lambda functions directly
|
|
1471
|
+
return this.mapReduce(($, x) => x, Expr.function([this.value_type, this.value_type], this.value_type, ($, a, b) => Expr.min(a, b)));
|
|
1472
|
+
}
|
|
1473
|
+
else {
|
|
1474
|
+
// With projection - need to track both element and projected value
|
|
1475
|
+
const byExpr = (by instanceof Expr ? by : Expr.function([this.value_type, IntegerType], undefined, by));
|
|
1476
|
+
if (byExpr[TypeSymbol].type !== "Function") {
|
|
1477
|
+
throw new Error("Expected a Function expression for 'by' parameter");
|
|
1478
|
+
}
|
|
1479
|
+
const byType = byExpr[TypeSymbol];
|
|
1480
|
+
const projectedType = byType.output;
|
|
1481
|
+
if (!isDataType(projectedType)) {
|
|
1482
|
+
throw new Error(`Can only compare data types, got ${printType(projectedType)}`);
|
|
1483
|
+
}
|
|
1484
|
+
// Map to { element, key }, reduce by comparing keys, extract element
|
|
1485
|
+
// Use lessEqual to keep the first element when keys are equal
|
|
1486
|
+
const result = this.mapReduce(($, x, k) => ({ element: x, key: byExpr(x, k) }), ($, a, b) => Expr.lessEqual(a.key, b.key).ifElse(() => a, () => b));
|
|
1487
|
+
return result.element;
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Joins string array elements with a delimiter.
|
|
1492
|
+
*
|
|
1493
|
+
* @param string - The delimiter string to insert between elements
|
|
1494
|
+
* @returns A StringExpr with all elements joined
|
|
1495
|
+
*
|
|
1496
|
+
* @remarks
|
|
1497
|
+
* This method only works on arrays of strings.
|
|
1498
|
+
*
|
|
1499
|
+
* @example
|
|
1500
|
+
* ```ts
|
|
1501
|
+
* const joinStrings = East.function([ArrayType(StringType), StringType], StringType, ($, arr, delimiter) => {
|
|
1502
|
+
* $.return(arr.stringJoin(delimiter));
|
|
1503
|
+
* });
|
|
1504
|
+
* const compiled = East.compile(joinStrings.toIR(), []);
|
|
1505
|
+
* compiled(["a", "b", "c"], ", "); // "a, b, c"
|
|
1506
|
+
* compiled(["hello", "world"], " "); // "hello world"
|
|
1507
|
+
* compiled([], ", "); // ""
|
|
1508
|
+
* ```
|
|
1509
|
+
*/
|
|
1510
|
+
stringJoin(string) {
|
|
1511
|
+
if (!isTypeEqual(this.value_type, StringType)) {
|
|
1512
|
+
throw new Error(`Can only perform stringJoin on array of strings - try using map to convert the values to strings first`);
|
|
1513
|
+
}
|
|
1514
|
+
const stringAst = valueOrExprToAstTyped(string, StringType);
|
|
1515
|
+
return this[FactorySymbol]({
|
|
1516
|
+
ast_type: "Builtin",
|
|
1517
|
+
type: StringType,
|
|
1518
|
+
location: get_location(2),
|
|
1519
|
+
builtin: "ArrayStringJoin",
|
|
1520
|
+
type_parameters: [],
|
|
1521
|
+
arguments: [this[AstSymbol], stringAst],
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
toSet(keyFn) {
|
|
1525
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1526
|
+
const keyType = keyFnAst.type.output;
|
|
1527
|
+
return this[FactorySymbol]({
|
|
1528
|
+
ast_type: "Builtin",
|
|
1529
|
+
type: SetType(keyType),
|
|
1530
|
+
location: get_location(2),
|
|
1531
|
+
builtin: "ArrayToSet",
|
|
1532
|
+
type_parameters: [this.value_type, keyType],
|
|
1533
|
+
arguments: [this[AstSymbol], keyFnAst],
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
toDict(keyFn, valueFn, onConflictFn) {
|
|
1537
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn ?? ((_$, x, i) => i), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1538
|
+
const keyType = keyFnAst.type.output;
|
|
1539
|
+
const valueFnAst = valueOrExprToAstTyped(valueFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1540
|
+
const valueType = valueFnAst.type.output;
|
|
1541
|
+
let onConflictAst;
|
|
1542
|
+
if (onConflictFn === undefined) {
|
|
1543
|
+
const location = get_location(2);
|
|
1544
|
+
const onConflictFunction = Expr.function([valueType, valueType, keyType], valueType, ($, existing, value, key) => $.error(Expr.str `Cannot insert duplicate key ${key} into dict`, location));
|
|
1545
|
+
onConflictAst = Expr.ast(onConflictFunction);
|
|
1546
|
+
}
|
|
1547
|
+
else {
|
|
1548
|
+
onConflictAst = valueOrExprToAstTyped(onConflictFn, FunctionType([valueType, valueType, keyType], valueType, null));
|
|
1549
|
+
}
|
|
1550
|
+
return this[FactorySymbol]({
|
|
1551
|
+
ast_type: "Builtin",
|
|
1552
|
+
type: DictType(keyType, valueType),
|
|
1553
|
+
location: get_location(2),
|
|
1554
|
+
builtin: "ArrayToDict",
|
|
1555
|
+
type_parameters: [this.value_type, keyType, valueType],
|
|
1556
|
+
arguments: [this[AstSymbol], keyFnAst, valueFnAst, onConflictAst],
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
flatMap(fn) {
|
|
1560
|
+
const fnAst = valueOrExprToAstTyped(fn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1561
|
+
const returnType = fnAst.type.output;
|
|
1562
|
+
if (returnType.type !== "Array") {
|
|
1563
|
+
throw new Error(`Expected Function to return an Array type, got ${printType(returnType)}`);
|
|
1564
|
+
}
|
|
1565
|
+
const elementType = returnType.value;
|
|
1566
|
+
return this[FactorySymbol]({
|
|
1567
|
+
ast_type: "Builtin",
|
|
1568
|
+
type: ArrayType(elementType),
|
|
1569
|
+
location: get_location(2),
|
|
1570
|
+
builtin: "ArrayFlattenToArray",
|
|
1571
|
+
type_parameters: [this.value_type, elementType],
|
|
1572
|
+
arguments: [this[AstSymbol], fnAst],
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
flattenToSet(fn) {
|
|
1576
|
+
const fnAst = valueOrExprToAstTyped(fn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1577
|
+
const returnType = fnAst.type.output;
|
|
1578
|
+
if (returnType.type !== "Set") {
|
|
1579
|
+
throw new Error(`Expected Function to return a Set type, got ${printType(returnType)}`);
|
|
1580
|
+
}
|
|
1581
|
+
const elementType = returnType.key;
|
|
1582
|
+
return this[FactorySymbol]({
|
|
1583
|
+
ast_type: "Builtin",
|
|
1584
|
+
type: SetType(elementType),
|
|
1585
|
+
location: get_location(2),
|
|
1586
|
+
builtin: "ArrayFlattenToSet",
|
|
1587
|
+
type_parameters: [this.value_type, elementType],
|
|
1588
|
+
arguments: [this[AstSymbol], fnAst],
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
flattenToDict(fn, onConflictFn) {
|
|
1592
|
+
const fnAst = valueOrExprToAstTyped(fn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1593
|
+
const returnType = fnAst.type.output;
|
|
1594
|
+
if (returnType.type !== "Dict") {
|
|
1595
|
+
throw new Error(`Expected Function to return a Dict type, got ${printType(returnType)}`);
|
|
1596
|
+
}
|
|
1597
|
+
const keyType = returnType.key;
|
|
1598
|
+
const valueType = returnType.value;
|
|
1599
|
+
let onConflictAst;
|
|
1600
|
+
if (onConflictFn === undefined) {
|
|
1601
|
+
const location = get_location(2);
|
|
1602
|
+
const onConflictFunction = Expr.function([valueType, valueType, keyType], valueType, ($, existing, value, key) => $.error(Expr.str `Cannot insert duplicate key ${key} into dict`, location));
|
|
1603
|
+
onConflictAst = Expr.ast(onConflictFunction);
|
|
1604
|
+
}
|
|
1605
|
+
else {
|
|
1606
|
+
onConflictAst = valueOrExprToAstTyped(onConflictFn, FunctionType([valueType, valueType, keyType], valueType, null));
|
|
1607
|
+
}
|
|
1608
|
+
return this[FactorySymbol]({
|
|
1609
|
+
ast_type: "Builtin",
|
|
1610
|
+
type: DictType(keyType, valueType),
|
|
1611
|
+
location: get_location(2),
|
|
1612
|
+
builtin: "ArrayFlattenToDict",
|
|
1613
|
+
type_parameters: [this.value_type, keyType, valueType],
|
|
1614
|
+
arguments: [this[AstSymbol], fnAst, onConflictAst],
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
groupReduce(keyFn, initFn, reduceFn) {
|
|
1618
|
+
// Note - initFn has to be before reduceFn, otherwise the TypeScript type inference doesn't work properly
|
|
1619
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1620
|
+
const keyType = keyFnAst.type.output;
|
|
1621
|
+
const initFnAst = valueOrExprToAstTyped(initFn, FunctionType([keyType], undefined, null));
|
|
1622
|
+
const initType = initFnAst.type.output;
|
|
1623
|
+
const reduceFnAst = valueOrExprToAstTyped(reduceFn, FunctionType([initType, this.value_type, IntegerType], initType, null));
|
|
1624
|
+
return this[FactorySymbol]({
|
|
1625
|
+
ast_type: "Builtin",
|
|
1626
|
+
type: DictType(keyType, initType),
|
|
1627
|
+
location: get_location(2),
|
|
1628
|
+
builtin: "ArrayGroupFold",
|
|
1629
|
+
type_parameters: [this.value_type, keyType, initType],
|
|
1630
|
+
arguments: [this[AstSymbol], keyFnAst, initFnAst, reduceFnAst],
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
groupSize(keyFn) {
|
|
1634
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1635
|
+
return this.toDict(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), ((_$) => 1n), ((_$, a, b) => a.add(b)));
|
|
1636
|
+
}
|
|
1637
|
+
groupEvery(keyFn, predFn) {
|
|
1638
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1639
|
+
const predFnAst = valueOrExprToAstTyped(predFn, FunctionType([this.value_type, IntegerType], BooleanType, null));
|
|
1640
|
+
return this.groupReduce(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), (() => true), ((_$, acc, elem, idx) => {
|
|
1641
|
+
const pred = Expr.fromAst(predFnAst)(elem, idx);
|
|
1642
|
+
return acc.and(() => pred);
|
|
1643
|
+
}));
|
|
1644
|
+
}
|
|
1645
|
+
groupSome(keyFn, predFn) {
|
|
1646
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1647
|
+
const predFnAst = valueOrExprToAstTyped(predFn, FunctionType([this.value_type, IntegerType], BooleanType, null));
|
|
1648
|
+
return this.groupReduce(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), (() => false), ((_$, acc, elem, idx) => {
|
|
1649
|
+
const pred = Expr.fromAst(predFnAst)(elem, idx);
|
|
1650
|
+
return acc.or(() => pred);
|
|
1651
|
+
}));
|
|
1652
|
+
}
|
|
1653
|
+
groupFindAll(keyFn, value, projFn) {
|
|
1654
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1655
|
+
const keyFnExpr = Expr.fromAst(keyFnAst);
|
|
1656
|
+
if (projFn === undefined) {
|
|
1657
|
+
// Without projection: find where element == value
|
|
1658
|
+
return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from([], ArrayType(IntegerType))), (($, acc, elem, idx) => {
|
|
1659
|
+
$.if(Expr.equal(elem, value), ($) => $(acc.pushLast(idx)));
|
|
1660
|
+
return acc;
|
|
1661
|
+
}));
|
|
1662
|
+
}
|
|
1663
|
+
else {
|
|
1664
|
+
// With projection: find where projFn(element) == value
|
|
1665
|
+
const projFnExpr = (projFn instanceof Expr ? projFn : Expr.function([this.value_type, IntegerType], undefined, projFn));
|
|
1666
|
+
if (projFnExpr[TypeSymbol].type !== "Function") {
|
|
1667
|
+
throw new Error("Expected a Function expression for 'projFn' parameter");
|
|
1668
|
+
}
|
|
1669
|
+
return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from([], ArrayType(IntegerType))), (($, acc, elem, idx) => {
|
|
1670
|
+
$.if(Expr.equal(projFnExpr(elem, idx), value), ($) => $(acc.pushLast(idx)));
|
|
1671
|
+
return acc;
|
|
1672
|
+
}));
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
groupFindFirst(keyFn, value, projFn) {
|
|
1676
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1677
|
+
const keyFnExpr = Expr.fromAst(keyFnAst);
|
|
1678
|
+
if (projFn === undefined) {
|
|
1679
|
+
// Without projection: find where element == value
|
|
1680
|
+
return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from(none, OptionType(IntegerType))), ((_$, acc, elem, idx) => {
|
|
1681
|
+
const isSome = acc.hasTag("some");
|
|
1682
|
+
return isSome.ifElse(() => acc, () => Expr.equal(elem, value).ifElse(() => some(idx), () => Expr.from(none, OptionType(IntegerType))));
|
|
1683
|
+
}));
|
|
1684
|
+
}
|
|
1685
|
+
else {
|
|
1686
|
+
// With projection: find where projFn(element) == value
|
|
1687
|
+
const projFnExpr = (projFn instanceof Expr ? projFn : Expr.function([this.value_type, IntegerType], undefined, projFn));
|
|
1688
|
+
if (projFnExpr[TypeSymbol].type !== "Function") {
|
|
1689
|
+
throw new Error("Expected a Function expression for 'projFn' parameter");
|
|
1690
|
+
}
|
|
1691
|
+
return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from(none, OptionType(IntegerType))), ((_$, acc, elem, idx) => {
|
|
1692
|
+
const isSome = acc.hasTag("some");
|
|
1693
|
+
return isSome.ifElse(() => acc, () => Expr.equal(projFnExpr(elem, idx), value).ifElse(() => some(idx), () => Expr.from(none, OptionType(IntegerType))));
|
|
1694
|
+
}));
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
groupFindMinimum(keyFn, byFn) {
|
|
1698
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1699
|
+
const byFnAst = valueOrExprToAstTyped(byFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1700
|
+
return this.toDict(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), ((_$, elem, idx) => ({ by: Expr.fromAst(byFnAst)(elem, idx), index: idx })), ((_$, a, b) => Expr.lessEqual(a.by, b.by).ifElse(() => a, () => b))).map(((_$, v) => v.index));
|
|
1701
|
+
}
|
|
1702
|
+
groupFindMaximum(keyFn, byFn) {
|
|
1703
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1704
|
+
const byFnAst = valueOrExprToAstTyped(byFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1705
|
+
return this.toDict(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), ((_$, elem, idx) => ({ by: Expr.fromAst(byFnAst)(elem, idx), index: idx })), ((_$, a, b) => Expr.greaterEqual(a.by, b.by).ifElse(() => a, () => b))).map(((_$, v) => v.index));
|
|
1706
|
+
}
|
|
1707
|
+
groupSum(keyFn, valueFn) {
|
|
1708
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1709
|
+
const valueFnAst = valueOrExprToAstTyped(valueFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1710
|
+
const valueType = valueFnAst.type.output;
|
|
1711
|
+
const isInteger = isTypeEqual(valueType, IntegerType);
|
|
1712
|
+
const isFloat = isTypeEqual(valueType, FloatType);
|
|
1713
|
+
if (!isInteger && !isFloat) {
|
|
1714
|
+
throw new Error(`Can only perform groupSum on Integer or Float values, got ${printType(valueType)}`);
|
|
1715
|
+
}
|
|
1716
|
+
return this.toDict(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), ((_$, elem, idx) => Expr.fromAst(valueFnAst)(elem, idx)), ((_$, a, b) => a.add(b)));
|
|
1717
|
+
}
|
|
1718
|
+
groupMean(keyFn, valueFn) {
|
|
1719
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1720
|
+
const valueFnAst = valueOrExprToAstTyped(valueFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1721
|
+
const valueType = valueFnAst.type.output;
|
|
1722
|
+
const isInteger = isTypeEqual(valueType, IntegerType);
|
|
1723
|
+
const isFloat = isTypeEqual(valueType, FloatType);
|
|
1724
|
+
if (!isInteger && !isFloat) {
|
|
1725
|
+
throw new Error(`Can only perform groupMean on Integer or Float values, got ${printType(valueType)}`);
|
|
1726
|
+
}
|
|
1727
|
+
return this.toDict(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), ((_$, elem, idx) => {
|
|
1728
|
+
const val = Expr.fromAst(valueFnAst)(elem, idx);
|
|
1729
|
+
return { sum: isInteger ? val.toFloat() : val, count: 1n };
|
|
1730
|
+
}), ((_$, a, b) => ({ sum: a.sum.add(b.sum), count: a.count.add(b.count) }))).map(((_$, v) => v.sum.divide(v.count.toFloat())));
|
|
1731
|
+
}
|
|
1732
|
+
groupToArrays(keyFn, valueFn) {
|
|
1733
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1734
|
+
const valueFnAst = valueOrExprToAstTyped(valueFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1735
|
+
const keyFnExpr = Expr.fromAst(keyFnAst);
|
|
1736
|
+
const valueFnExpr = Expr.fromAst(valueFnAst);
|
|
1737
|
+
const valueType = valueFnAst.type.output;
|
|
1738
|
+
return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from([], ArrayType(valueType))), (($, acc, elem, idx) => {
|
|
1739
|
+
const val = valueFnExpr(elem, idx);
|
|
1740
|
+
$(acc.pushLast(val));
|
|
1741
|
+
return acc;
|
|
1742
|
+
}));
|
|
1743
|
+
}
|
|
1744
|
+
groupToSets(keyFn, valueFn) {
|
|
1745
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1746
|
+
const valueFnAst = valueOrExprToAstTyped(valueFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1747
|
+
const keyFnExpr = Expr.fromAst(keyFnAst);
|
|
1748
|
+
const valueFnExpr = Expr.fromAst(valueFnAst);
|
|
1749
|
+
const valueType = valueFnAst.type.output;
|
|
1750
|
+
return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from(new Set(), SetType(valueType))), (($, acc, elem, idx) => {
|
|
1751
|
+
const val = valueFnExpr(elem, idx);
|
|
1752
|
+
$(acc.tryInsert(val));
|
|
1753
|
+
return acc;
|
|
1754
|
+
}));
|
|
1755
|
+
}
|
|
1756
|
+
groupMinimum(keyFn, byFn) {
|
|
1757
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1758
|
+
const byFnAst = valueOrExprToAstTyped(byFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1759
|
+
return this.toDict(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), ((_$, elem, idx) => ({ by: Expr.fromAst(byFnAst)(elem, idx), elem })), ((_$, a, b) => Expr.lessEqual(a.by, b.by).ifElse(() => a, () => b))).map(((_$, v) => v.elem));
|
|
1760
|
+
}
|
|
1761
|
+
groupMaximum(keyFn, byFn) {
|
|
1762
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1763
|
+
const byFnAst = valueOrExprToAstTyped(byFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1764
|
+
return this.toDict(((_$, elem, idx) => Expr.fromAst(keyFnAst)(elem, idx)), ((_$, elem, idx) => ({ by: Expr.fromAst(byFnAst)(elem, idx), elem })), ((_$, a, b) => Expr.greaterEqual(a.by, b.by).ifElse(() => a, () => b))).map(((_$, v) => v.elem));
|
|
1765
|
+
}
|
|
1766
|
+
groupToDicts(keyFn, keyFn2, valueFn, combineFn) {
|
|
1767
|
+
const keyFnAst = valueOrExprToAstTyped(keyFn, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1768
|
+
const keyFn2Ast = valueOrExprToAstTyped(keyFn2, FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1769
|
+
const valueFnAst = valueOrExprToAstTyped(valueFn ?? ((_$, x) => x), FunctionType([this.value_type, IntegerType], undefined, null));
|
|
1770
|
+
const keyFnExpr = Expr.fromAst(keyFnAst);
|
|
1771
|
+
const keyFn2Expr = Expr.fromAst(keyFn2Ast);
|
|
1772
|
+
const valueFnExpr = Expr.fromAst(valueFnAst);
|
|
1773
|
+
const key2Type = keyFn2Ast.type.output;
|
|
1774
|
+
const valueType = valueFnAst.type.output;
|
|
1775
|
+
if (combineFn !== undefined) {
|
|
1776
|
+
// With conflict resolution - use tryGet + match to check existence, then insert or combine
|
|
1777
|
+
const combineFnAst = valueOrExprToAstTyped(combineFn, FunctionType([valueType, valueType], valueType, null));
|
|
1778
|
+
const combineFnExpr = Expr.fromAst(combineFnAst);
|
|
1779
|
+
return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from(new Map(), DictType(key2Type, valueType))), (($, dict, elem, idx) => {
|
|
1780
|
+
const innerKey = keyFn2Expr(elem, idx);
|
|
1781
|
+
const val = valueFnExpr(elem, idx);
|
|
1782
|
+
$.match(dict.tryGet(innerKey), {
|
|
1783
|
+
some: ($, existing) => {
|
|
1784
|
+
const combined = combineFnExpr(existing, val);
|
|
1785
|
+
$(dict.update(innerKey, combined));
|
|
1786
|
+
},
|
|
1787
|
+
none: ($) => {
|
|
1788
|
+
$(dict.insert(innerKey, val));
|
|
1789
|
+
}
|
|
1790
|
+
});
|
|
1791
|
+
return dict;
|
|
1792
|
+
}));
|
|
1793
|
+
}
|
|
1794
|
+
else {
|
|
1795
|
+
// Without conflict resolution - use insert (errors on duplicate)
|
|
1796
|
+
return this.groupReduce(((_$, elem, idx) => keyFnExpr(elem, idx)), ((_$, _key) => Expr.from(new Map(), DictType(key2Type, valueType))), (($, dict, elem, idx) => {
|
|
1797
|
+
const innerKey = keyFn2Expr(elem, idx);
|
|
1798
|
+
const val = valueFnExpr(elem, idx);
|
|
1799
|
+
$(dict.insert(innerKey, val));
|
|
1800
|
+
return dict;
|
|
1801
|
+
}));
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
//# sourceMappingURL=array.js.map
|