@garrix82/reactgenie-dsl 1.0.1 → 1.0.2
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/.env +10 -0
- package/.env.example +17 -0
- package/.github/workflows/publish.yml +20 -0
- package/package.json +1 -5
- package/dist/__test__/dsl-descriptor.test.d.ts +0 -1
- package/dist/__test__/dsl-descriptor.test.js +0 -27
- package/dist/__test__/dsl-descriptor.test.js.map +0 -1
- package/dist/__test__/example_descriptor.d.ts +0 -125
- package/dist/__test__/example_descriptor.js +0 -607
- package/dist/__test__/example_descriptor.js.map +0 -1
- package/dist/__test__/food_descriptor.state.json +0 -1
- package/dist/__test__/food_descriptor.test.d.ts +0 -74
- package/dist/__test__/food_descriptor.test.js +0 -205
- package/dist/__test__/food_descriptor.test.js.map +0 -1
- package/dist/__test__/nl-interpreter-provider-selection.test.d.ts +0 -1
- package/dist/__test__/nl-interpreter-provider-selection.test.js +0 -73
- package/dist/__test__/nl-interpreter-provider-selection.test.js.map +0 -1
- package/dist/__test__/nl-interpreter.test.d.ts +0 -1
- package/dist/__test__/nl-interpreter.test.js +0 -86
- package/dist/__test__/nl-interpreter.test.js.map +0 -1
- package/dist/decorators/__test__/decorators.test.d.ts +0 -1
- package/dist/decorators/__test__/decorators.test.js +0 -182
- package/dist/decorators/__test__/decorators.test.js.map +0 -1
- package/dist/decorators/__test__/inheritance-descriptor.test.d.ts +0 -1
- package/dist/decorators/__test__/inheritance-descriptor.test.js +0 -107
- package/dist/decorators/__test__/inheritance-descriptor.test.js.map +0 -1
- package/dist/dsl/__test__/dsl-interpreter.test.d.ts +0 -1
- package/dist/dsl/__test__/dsl-interpreter.test.js +0 -334
- package/dist/dsl/__test__/dsl-interpreter.test.js.map +0 -1
- package/dist/dsl/__test__/parser.gen.test.d.ts +0 -1
- package/dist/dsl/__test__/parser.gen.test.js +0 -283
- package/dist/dsl/__test__/parser.gen.test.js.map +0 -1
- package/dist/nl/__test__/context-aware-prompt.test.d.ts +0 -1
- package/dist/nl/__test__/context-aware-prompt.test.js +0 -247
- package/dist/nl/__test__/context-aware-prompt.test.js.map +0 -1
- package/dist/nl/__test__/context-selector.test.d.ts +0 -1
- package/dist/nl/__test__/context-selector.test.js +0 -20
- package/dist/nl/__test__/context-selector.test.js.map +0 -1
- package/dist/nl/__test__/nl-parser-groq-transport.test.d.ts +0 -1
- package/dist/nl/__test__/nl-parser-groq-transport.test.js +0 -87
- package/dist/nl/__test__/nl-parser-groq-transport.test.js.map +0 -1
- package/dist/nl/__test__/nl-parser-openai-parity.test.d.ts +0 -1
- package/dist/nl/__test__/nl-parser-openai-parity.test.js +0 -206
- package/dist/nl/__test__/nl-parser-openai-parity.test.js.map +0 -1
- package/dist/nl/__test__/nl-parser-openai-sampling.test.d.ts +0 -1
- package/dist/nl/__test__/nl-parser-openai-sampling.test.js +0 -44
- package/dist/nl/__test__/nl-parser-openai-sampling.test.js.map +0 -1
- package/dist/nl/__test__/nl-parser-openai-transport.test.d.ts +0 -1
- package/dist/nl/__test__/nl-parser-openai-transport.test.js +0 -55
- package/dist/nl/__test__/nl-parser-openai-transport.test.js.map +0 -1
- package/dist/nl/__test__/nl-parser-utils.test.d.ts +0 -1
- package/dist/nl/__test__/nl-parser-utils.test.js +0 -70
- package/dist/nl/__test__/nl-parser-utils.test.js.map +0 -1
- package/dist/nl/__test__/nl-parser.test.d.ts +0 -1
- package/dist/nl/__test__/nl-parser.test.js +0 -64
- package/dist/nl/__test__/nl-parser.test.js.map +0 -1
- package/dist/nl/__test__/parameter-tuning.test.d.ts +0 -1
- package/dist/nl/__test__/parameter-tuning.test.js +0 -95
- package/dist/nl/__test__/parameter-tuning.test.js.map +0 -1
- package/dist/nl/__test__/semantic-parsing-experiment.test.d.ts +0 -1
- package/dist/nl/__test__/semantic-parsing-experiment.test.js +0 -178
- package/dist/nl/__test__/semantic-parsing-experiment.test.js.map +0 -1
- package/dist/nl/llm-monitoring.test.d.ts +0 -5
- package/dist/nl/llm-monitoring.test.js +0 -101
- package/dist/nl/llm-monitoring.test.js.map +0 -1
- package/lib/__test__/dsl-descriptor.test.ts +0 -27
- package/lib/__test__/example_descriptor.ts +0 -762
- package/lib/__test__/food_descriptor.state.json +0 -1
- package/lib/__test__/food_descriptor.test.ts +0 -331
- package/lib/__test__/nl-interpreter-provider-selection.test.ts +0 -126
- package/lib/__test__/nl-interpreter.test.ts +0 -129
- package/lib/decorators/__test__/decorators.test.ts +0 -177
- package/lib/decorators/__test__/inheritance-descriptor.test.ts +0 -92
- package/lib/decorators/decorators.ts +0 -754
- package/lib/decorators/index.ts +0 -2
- package/lib/decorators/store.ts +0 -47
- package/lib/dsl/__test__/dsl-interpreter.test.ts +0 -453
- package/lib/dsl/__test__/parser.gen.test.ts +0 -296
- package/lib/dsl/dsl-interpreter.ts +0 -974
- package/lib/dsl/index.ts +0 -1
- package/lib/dsl/parser.gen.js +0 -1479
- package/lib/dsl/parser.pegjs +0 -130
- package/lib/dsl-descriptor.ts +0 -241
- package/lib/index.ts +0 -5
- package/lib/nl/__test__/context-aware-prompt.test.ts +0 -372
- package/lib/nl/__test__/context-selector.test.ts +0 -27
- package/lib/nl/__test__/nl-parser-groq-transport.test.ts +0 -139
- package/lib/nl/__test__/nl-parser-openai-parity.test.ts +0 -381
- package/lib/nl/__test__/nl-parser-openai-sampling.test.ts +0 -73
- package/lib/nl/__test__/nl-parser-openai-transport.test.ts +0 -79
- package/lib/nl/__test__/nl-parser-utils.test.ts +0 -98
- package/lib/nl/__test__/nl-parser.test.ts +0 -119
- package/lib/nl/__test__/parameter-tuning.test.ts +0 -137
- package/lib/nl/__test__/semantic-parsing-experiment.test.ts +0 -260
- package/lib/nl/context-selector.ts +0 -123
- package/lib/nl/index.ts +0 -19
- package/lib/nl/llm-monitoring.test.ts +0 -136
- package/lib/nl/llm-monitoring.ts +0 -339
- package/lib/nl/nl-parser-groq.ts +0 -510
- package/lib/nl/nl-parser-utils.ts +0 -310
- package/lib/nl/nl-parser.ts +0 -616
- package/lib/nl/prompt-gen.ts +0 -607
- package/lib/nl/prompt-res.ts +0 -207
- package/lib/nl-interpreter.ts +0 -262
|
@@ -1,974 +0,0 @@
|
|
|
1
|
-
import { parse } from "./parser.gen.js";
|
|
2
|
-
import {
|
|
3
|
-
ClassDescriptor,
|
|
4
|
-
FuncDescriptor,
|
|
5
|
-
GenieObject,
|
|
6
|
-
ParamDescriptor,
|
|
7
|
-
} from "../dsl-descriptor";
|
|
8
|
-
|
|
9
|
-
function parseType(value_type: string): {
|
|
10
|
-
is_array: boolean;
|
|
11
|
-
original_type: string;
|
|
12
|
-
} {
|
|
13
|
-
if (value_type.endsWith("[]")) {
|
|
14
|
-
return {
|
|
15
|
-
is_array: true,
|
|
16
|
-
original_type: value_type.substring(0, value_type.length - 2),
|
|
17
|
-
};
|
|
18
|
-
} else {
|
|
19
|
-
return { is_array: false, original_type: value_type };
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class InterpreterError extends Error {
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export class ClassMissingError extends InterpreterError {
|
|
27
|
-
constructor(public class_name: string) {
|
|
28
|
-
super(`Class ${class_name} is missing`);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export class FieldMissingError extends InterpreterError {
|
|
33
|
-
constructor(public class_name: string, public field_name: string) {
|
|
34
|
-
super(`Field ${class_name}.${field_name} is missing`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export class FieldStaticError extends InterpreterError {
|
|
39
|
-
constructor(public class_name: string, public field_name: string) {
|
|
40
|
-
super(`Field ${class_name}.${field_name} should be static`);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export class FunctionMissingError extends InterpreterError {
|
|
45
|
-
constructor(public class_name: string, public func_name: string) {
|
|
46
|
-
super(`Function ${class_name}.${func_name} is missing`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export class FunctionStaticError extends InterpreterError {
|
|
51
|
-
constructor(public class_name: string, public func_name: string) {
|
|
52
|
-
super(`Function ${class_name}.${func_name} should be static`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export class InvalidParameterTypeError extends InterpreterError {
|
|
57
|
-
constructor(
|
|
58
|
-
public func_name: string,
|
|
59
|
-
public parameter_name: string,
|
|
60
|
-
public expected_type: string,
|
|
61
|
-
public received_type: string
|
|
62
|
-
) {
|
|
63
|
-
super(
|
|
64
|
-
`Invalid DSL: ${func_name}(${parameter_name}: ...) requires a dotted accessor such as .name, but received ${received_type}.`
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
type ElementWithField = {
|
|
70
|
-
element: any,
|
|
71
|
-
fieldValue: any,
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export class DslInterpreter {
|
|
75
|
-
// region array functions
|
|
76
|
-
|
|
77
|
-
// TODO: better comparison functions
|
|
78
|
-
private matching(value: any, template: any) {
|
|
79
|
-
// Prefer robust comparison rather than assuming identical JS types.
|
|
80
|
-
// 1) both strings -> case-insensitive substring
|
|
81
|
-
if (typeof value === "string" && typeof template === "string") {
|
|
82
|
-
return value.toLowerCase().includes(template.toLowerCase());
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// 2) string value that contains a date and numeric template (e.g. "2026-02-15" vs month=2)
|
|
86
|
-
if (typeof value === "string" && typeof template === "number") {
|
|
87
|
-
const parsed = new Date(value);
|
|
88
|
-
if (!isNaN(parsed.getTime())) {
|
|
89
|
-
return parsed.getMonth() + 1 === template;
|
|
90
|
-
}
|
|
91
|
-
// fallback: allow numeric substring match
|
|
92
|
-
return value.includes(String(template));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// 3) numeric value vs string template (e.g. month stored as number, template was "2")
|
|
96
|
-
if ((typeof value === "number" || typeof value === "boolean") && typeof template === "string") {
|
|
97
|
-
const asNum = Number(template);
|
|
98
|
-
if (!isNaN(asNum)) return value === asNum;
|
|
99
|
-
return String(value).toLowerCase() === template.toLowerCase();
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// 4) primitive strict equality
|
|
103
|
-
if (typeof value === "number" || typeof value === "boolean") {
|
|
104
|
-
return value === template;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// 5) object deep-compare using constructor params when available
|
|
108
|
-
if (value && template && typeof value === "object" && typeof template === "object") {
|
|
109
|
-
if (typeof value._getConstructorParams === "function" && typeof template._getConstructorParams === "function") {
|
|
110
|
-
return (
|
|
111
|
-
JSON.stringify(value._getConstructorParams()) ===
|
|
112
|
-
JSON.stringify(template._getConstructorParams())
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
// final fallback
|
|
116
|
-
return JSON.stringify(value) === JSON.stringify(template);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
private contains(value: any, template: any) {
|
|
123
|
-
// strings
|
|
124
|
-
if (typeof value === "string" && typeof template === "string") {
|
|
125
|
-
return value.toLowerCase().includes(template.toLowerCase());
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// string date vs numeric template (e.g. receivedAt vs DateTime.today().month)
|
|
129
|
-
if (typeof value === "string" && typeof template === "number") {
|
|
130
|
-
const parsed = new Date(value);
|
|
131
|
-
if (!isNaN(parsed.getTime())) {
|
|
132
|
-
return parsed.getMonth() + 1 === template;
|
|
133
|
-
}
|
|
134
|
-
return value.includes(String(template));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// array
|
|
138
|
-
else if (Array.isArray(value)) {
|
|
139
|
-
let contains = false;
|
|
140
|
-
for (let i = 0; i < value.length; i++) {
|
|
141
|
-
// use matching
|
|
142
|
-
if (this.matching(value[i], template)) {
|
|
143
|
-
contains = true;
|
|
144
|
-
break;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return contains;
|
|
148
|
-
} else {
|
|
149
|
-
return value === template;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
private equals(a: any, b: any) {
|
|
154
|
-
if (a === b) {
|
|
155
|
-
return true;
|
|
156
|
-
} else if (typeof a === "string" && typeof b === "string") {
|
|
157
|
-
return a.toLowerCase() === b.toLowerCase();
|
|
158
|
-
} else {
|
|
159
|
-
return false;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
private compareObjectsSafely(left: any, right: any): number | null {
|
|
164
|
-
const leftCompare = left?.constructor?.compare;
|
|
165
|
-
if (typeof leftCompare === "function") {
|
|
166
|
-
return leftCompare(left, right);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const rightCompare = right?.constructor?.compare;
|
|
170
|
-
if (typeof rightCompare === "function") {
|
|
171
|
-
return rightCompare(left, right);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return null;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// for common array functions: matching, between, equals, sort, and access
|
|
178
|
-
private arrayFunctionDescriptors: FuncDescriptor[] = [
|
|
179
|
-
new FuncDescriptor(
|
|
180
|
-
"matching",
|
|
181
|
-
[
|
|
182
|
-
new ParamDescriptor("field", "accessor"),
|
|
183
|
-
new ParamDescriptor("value", "object"),
|
|
184
|
-
],
|
|
185
|
-
"object[]",
|
|
186
|
-
false
|
|
187
|
-
),
|
|
188
|
-
new FuncDescriptor(
|
|
189
|
-
"contains",
|
|
190
|
-
[
|
|
191
|
-
new ParamDescriptor("field", "accessor"),
|
|
192
|
-
new ParamDescriptor("value", "object"),
|
|
193
|
-
],
|
|
194
|
-
"object[]",
|
|
195
|
-
false
|
|
196
|
-
),
|
|
197
|
-
new FuncDescriptor(
|
|
198
|
-
"between",
|
|
199
|
-
[
|
|
200
|
-
new ParamDescriptor("field", "accessor"),
|
|
201
|
-
new ParamDescriptor("from", "object", false),
|
|
202
|
-
new ParamDescriptor("to", "object", false),
|
|
203
|
-
],
|
|
204
|
-
"object[]",
|
|
205
|
-
false
|
|
206
|
-
),
|
|
207
|
-
new FuncDescriptor(
|
|
208
|
-
"equals",
|
|
209
|
-
[
|
|
210
|
-
new ParamDescriptor("field", "accessor"),
|
|
211
|
-
new ParamDescriptor("value", "object"),
|
|
212
|
-
],
|
|
213
|
-
"object[]",
|
|
214
|
-
false
|
|
215
|
-
),
|
|
216
|
-
new FuncDescriptor(
|
|
217
|
-
"sort",
|
|
218
|
-
[
|
|
219
|
-
new ParamDescriptor("field", "accessor"),
|
|
220
|
-
new ParamDescriptor("ascending", "boolean"),
|
|
221
|
-
],
|
|
222
|
-
"object[]",
|
|
223
|
-
false
|
|
224
|
-
),
|
|
225
|
-
new FuncDescriptor(
|
|
226
|
-
"index",
|
|
227
|
-
[new ParamDescriptor("index", "int")],
|
|
228
|
-
"object",
|
|
229
|
-
false
|
|
230
|
-
),
|
|
231
|
-
new FuncDescriptor(
|
|
232
|
-
"length",
|
|
233
|
-
[],
|
|
234
|
-
"int",
|
|
235
|
-
false
|
|
236
|
-
)
|
|
237
|
-
];
|
|
238
|
-
// implementations of functions
|
|
239
|
-
private arrayFunctionImplementations: {
|
|
240
|
-
[name: string]: (...args: any[]) => Promise<any>;
|
|
241
|
-
} = {
|
|
242
|
-
matching: async ({
|
|
243
|
-
value,
|
|
244
|
-
array,
|
|
245
|
-
}: {
|
|
246
|
-
value: any;
|
|
247
|
-
array: ElementWithField[];
|
|
248
|
-
}) => {
|
|
249
|
-
return array.filter((element) => {
|
|
250
|
-
return this.matching(element.fieldValue, value);
|
|
251
|
-
}).map((v) => v.element);
|
|
252
|
-
},
|
|
253
|
-
contains: async({
|
|
254
|
-
value,
|
|
255
|
-
array,
|
|
256
|
-
}: {
|
|
257
|
-
field: (element: any) => Promise<any>;
|
|
258
|
-
value: any;
|
|
259
|
-
array: ElementWithField[];
|
|
260
|
-
}) => {
|
|
261
|
-
return array.filter((element) => {
|
|
262
|
-
return this.contains(element.fieldValue, value);
|
|
263
|
-
}).map((v) => v.element);
|
|
264
|
-
},
|
|
265
|
-
between: async ({
|
|
266
|
-
from,
|
|
267
|
-
to,
|
|
268
|
-
array,
|
|
269
|
-
}: {
|
|
270
|
-
from?: any;
|
|
271
|
-
to?: any;
|
|
272
|
-
array: ElementWithField[];
|
|
273
|
-
}) => {
|
|
274
|
-
let newArray = [];
|
|
275
|
-
for (const element of array) {
|
|
276
|
-
let first: boolean = true;
|
|
277
|
-
let second: boolean = true;
|
|
278
|
-
let value = element.fieldValue;
|
|
279
|
-
let valueIsPrimitive =
|
|
280
|
-
typeof value === "string" ||
|
|
281
|
-
typeof value === "number" ||
|
|
282
|
-
typeof value === "boolean";
|
|
283
|
-
if (from !== undefined) {
|
|
284
|
-
let fromIsPrimitive =
|
|
285
|
-
typeof from === "string" ||
|
|
286
|
-
typeof from === "number" ||
|
|
287
|
-
typeof from === "boolean";
|
|
288
|
-
if (fromIsPrimitive && valueIsPrimitive) {
|
|
289
|
-
first = value >= from;
|
|
290
|
-
} else if (fromIsPrimitive && !valueIsPrimitive) {
|
|
291
|
-
const compareResult = this.compareObjectsSafely(value, from);
|
|
292
|
-
first = compareResult !== null ? compareResult >= 0 : false;
|
|
293
|
-
} else if (!fromIsPrimitive && valueIsPrimitive) {
|
|
294
|
-
const compareResult = this.compareObjectsSafely(value, from);
|
|
295
|
-
first = compareResult !== null ? compareResult >= 0 : false;
|
|
296
|
-
} else {
|
|
297
|
-
const compareResult = this.compareObjectsSafely(value, from);
|
|
298
|
-
first = compareResult !== null ? compareResult >= 0 : false;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
if (to !== undefined) {
|
|
302
|
-
let toIsPrimitive =
|
|
303
|
-
typeof to === "string" ||
|
|
304
|
-
typeof to === "number" ||
|
|
305
|
-
typeof to === "boolean";
|
|
306
|
-
if (toIsPrimitive && valueIsPrimitive) {
|
|
307
|
-
second = value <= to;
|
|
308
|
-
} else if (toIsPrimitive && !valueIsPrimitive) {
|
|
309
|
-
const compareResult = this.compareObjectsSafely(value, to);
|
|
310
|
-
second = compareResult !== null ? compareResult <= 0 : false;
|
|
311
|
-
} else if (!toIsPrimitive && valueIsPrimitive) {
|
|
312
|
-
const compareResult = this.compareObjectsSafely(value, to);
|
|
313
|
-
second = compareResult !== null ? compareResult <= 0 : false;
|
|
314
|
-
} else {
|
|
315
|
-
const compareResult = this.compareObjectsSafely(value, to);
|
|
316
|
-
second = compareResult !== null ? compareResult <= 0 : false;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
if (first && second) {
|
|
320
|
-
newArray.push(element.element);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
return newArray;
|
|
324
|
-
},
|
|
325
|
-
equals: async ({
|
|
326
|
-
value,
|
|
327
|
-
array,
|
|
328
|
-
}: {
|
|
329
|
-
value: any;
|
|
330
|
-
array: ElementWithField[];
|
|
331
|
-
}) => {
|
|
332
|
-
return array.filter((element) => {
|
|
333
|
-
return this.equals(element.fieldValue, value);
|
|
334
|
-
}).map((v) => v.element);
|
|
335
|
-
},
|
|
336
|
-
sort: async ({
|
|
337
|
-
ascending,
|
|
338
|
-
array,
|
|
339
|
-
}: {
|
|
340
|
-
ascending: boolean;
|
|
341
|
-
array: ElementWithField[];
|
|
342
|
-
}) => {
|
|
343
|
-
return array.sort((a, b) => {
|
|
344
|
-
if (ascending) {
|
|
345
|
-
if (a.fieldValue < b.fieldValue) {
|
|
346
|
-
return -1;
|
|
347
|
-
} else if (a.fieldValue > b.fieldValue) {
|
|
348
|
-
return 1;
|
|
349
|
-
} else {
|
|
350
|
-
return 0;
|
|
351
|
-
}
|
|
352
|
-
} else {
|
|
353
|
-
if (a.fieldValue < b.fieldValue) {
|
|
354
|
-
return 1;
|
|
355
|
-
} else if (a.fieldValue > b.fieldValue) {
|
|
356
|
-
return -1;
|
|
357
|
-
} else {
|
|
358
|
-
return 0;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}).map((v) => v.element);
|
|
362
|
-
},
|
|
363
|
-
index: async ({ index, array }: { index: any; array: any[] }) => {
|
|
364
|
-
console.assert(typeof index === "number");
|
|
365
|
-
if (index < 0) {
|
|
366
|
-
index = array.length + index;
|
|
367
|
-
}
|
|
368
|
-
return array[index];
|
|
369
|
-
},
|
|
370
|
-
length: async ({ array }: { array: any[] }) => {
|
|
371
|
-
return array.length;
|
|
372
|
-
}
|
|
373
|
-
};
|
|
374
|
-
|
|
375
|
-
// endregion
|
|
376
|
-
|
|
377
|
-
resolveSteps: any[] = [];
|
|
378
|
-
resolveStepsEnabled: boolean = false;
|
|
379
|
-
public classDescriptors: ClassDescriptor<GenieObject>[];
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
constructor(classDescriptors: ClassDescriptor<GenieObject>[], public dry_run = false) {
|
|
383
|
-
this.classDescriptors = [...classDescriptors];
|
|
384
|
-
this.classDescriptors.push(new ClassDescriptor("String", [], [], null,));
|
|
385
|
-
this.classDescriptors.push(new ClassDescriptor("Number", [], [], null,));
|
|
386
|
-
this.classDescriptors.push(new ClassDescriptor("float", [], [], null,));
|
|
387
|
-
this.classDescriptors.push(new ClassDescriptor("int", [], [], null,));
|
|
388
|
-
this.classDescriptors.push(new ClassDescriptor("Boolean", [], [], null,));
|
|
389
|
-
// console.log(this.classDescriptors);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* interpret a DSL expression
|
|
394
|
-
**/
|
|
395
|
-
public async interpret(input: string): Promise<any> {
|
|
396
|
-
const ast = parse(input) as object[];
|
|
397
|
-
var lastResult = null;
|
|
398
|
-
for (const statement of ast) {
|
|
399
|
-
lastResult = await this.resolve(statement);
|
|
400
|
-
}
|
|
401
|
-
return lastResult;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
public async interpretSteps(input: string): Promise<{ ast: any, result: any, steps: {ast: any, result: any}[] }[]> {
|
|
405
|
-
const ast = parse(input) as object[];
|
|
406
|
-
var lastResult = null;
|
|
407
|
-
let allSteps = [];
|
|
408
|
-
for (const statement of ast) {
|
|
409
|
-
// console.log(JSON.stringify(ast));
|
|
410
|
-
this.resolveSteps = [];
|
|
411
|
-
this.resolveStepsEnabled = true;
|
|
412
|
-
lastResult = await this.resolve(statement);
|
|
413
|
-
this.resolveStepsEnabled = false;
|
|
414
|
-
allSteps.push({ ast: statement, result: lastResult, steps: this.resolveSteps });
|
|
415
|
-
}
|
|
416
|
-
return allSteps;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* Generate a description of the current ast
|
|
421
|
-
* @param ast
|
|
422
|
-
*/
|
|
423
|
-
public async describe(ast: any) {
|
|
424
|
-
switch (ast.type) {
|
|
425
|
-
case "array":
|
|
426
|
-
const elements = []
|
|
427
|
-
for (const e of ast.value) {
|
|
428
|
-
elements.push(await this.describe(e))
|
|
429
|
-
}
|
|
430
|
-
return {
|
|
431
|
-
type: "array",
|
|
432
|
-
elements: elements,
|
|
433
|
-
};
|
|
434
|
-
case "string":
|
|
435
|
-
case "int":
|
|
436
|
-
case "boolean":
|
|
437
|
-
return ast.value;
|
|
438
|
-
case "object":
|
|
439
|
-
if (ast.value !== undefined) {
|
|
440
|
-
if (ast.objectType == "string" || ast.objectType == "int" || ast.objectType == "float" || ast.objectType == "number") {
|
|
441
|
-
return {
|
|
442
|
-
type: "object",
|
|
443
|
-
objectType: ast.objectType,
|
|
444
|
-
value: ast.value,
|
|
445
|
-
};
|
|
446
|
-
} else {
|
|
447
|
-
// if ast.value have update function, call it
|
|
448
|
-
if (ast.value.update !== undefined) {
|
|
449
|
-
await ast.value.update()
|
|
450
|
-
}
|
|
451
|
-
return { type: "object", value: await ast.value.description() };
|
|
452
|
-
}
|
|
453
|
-
} else {
|
|
454
|
-
console.assert(ast.objectType == "void");
|
|
455
|
-
return { type: "object", value: "undefined" };
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
public async describeSteps(list: any[]) {
|
|
461
|
-
const steps = []
|
|
462
|
-
for (const e of list) {
|
|
463
|
-
steps.push(await this.describe(e.result))
|
|
464
|
-
}
|
|
465
|
-
return steps;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* strip all helper information from an ast node (so that it can be passed to a function)
|
|
470
|
-
* @param ast
|
|
471
|
-
*/
|
|
472
|
-
public strip(ast: any) {
|
|
473
|
-
switch (ast.type) {
|
|
474
|
-
case "class":
|
|
475
|
-
return this.classDescriptors.find((c) => c.className === ast.value)
|
|
476
|
-
.classConstructor;
|
|
477
|
-
case "array":
|
|
478
|
-
return ast.value.map((v) => this.strip(v));
|
|
479
|
-
case "accessor":
|
|
480
|
-
return ast;
|
|
481
|
-
|
|
482
|
-
// return value for primitive types
|
|
483
|
-
case "string":
|
|
484
|
-
case "int":
|
|
485
|
-
case "boolean":
|
|
486
|
-
case "object":
|
|
487
|
-
return ast.value;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
/**
|
|
492
|
-
* resolve an ast node, make it a concrete value
|
|
493
|
-
* @param ast the ast node to resolve
|
|
494
|
-
* @param env the environment in which to resolve the node
|
|
495
|
-
*/
|
|
496
|
-
public async resolve(ast: any, env: null | any = null): Promise<any> {
|
|
497
|
-
let result = null;
|
|
498
|
-
switch (ast.type) {
|
|
499
|
-
case "access":
|
|
500
|
-
result = await this.resolveAccess(ast, env);
|
|
501
|
-
break;
|
|
502
|
-
case "index":
|
|
503
|
-
result = await this.resolveIndex(ast, env);
|
|
504
|
-
break;
|
|
505
|
-
case "function_call":
|
|
506
|
-
result = await this.resolveFunctionCall(ast, env);
|
|
507
|
-
break;
|
|
508
|
-
case "array":
|
|
509
|
-
result = await this.resolveArray(ast, env);
|
|
510
|
-
break;
|
|
511
|
-
|
|
512
|
-
// don't do anything for primitive types
|
|
513
|
-
case "string":
|
|
514
|
-
case "int":
|
|
515
|
-
case "float":
|
|
516
|
-
case "boolean":
|
|
517
|
-
case "accessor":
|
|
518
|
-
case "object":
|
|
519
|
-
result = ast;
|
|
520
|
-
break;
|
|
521
|
-
|
|
522
|
-
default:
|
|
523
|
-
throw new Error("Unsupported AST type: " + ast.type);
|
|
524
|
-
}
|
|
525
|
-
if (this.resolveStepsEnabled) {
|
|
526
|
-
this.resolveSteps.push({ ast: ast, result: result });
|
|
527
|
-
}
|
|
528
|
-
// if (this.resolveStepsEnabled) {
|
|
529
|
-
// console.log(result);
|
|
530
|
-
// }
|
|
531
|
-
return result;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
private describeResolvedParameterType(param: any): string {
|
|
535
|
-
if (!param || typeof param !== "object") {
|
|
536
|
-
return typeof param;
|
|
537
|
-
}
|
|
538
|
-
if (param.type === "object" && typeof param.objectType === "string") {
|
|
539
|
-
return param.objectType;
|
|
540
|
-
}
|
|
541
|
-
return param.type || typeof param;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
private validateResolvedParameters(
|
|
545
|
-
func_name: string,
|
|
546
|
-
funcDescriptor: FuncDescriptor,
|
|
547
|
-
parameters: Map<string, any>
|
|
548
|
-
) {
|
|
549
|
-
for (const descriptor of funcDescriptor.parameters) {
|
|
550
|
-
const param = parameters.get(descriptor.name);
|
|
551
|
-
if (param == null) {
|
|
552
|
-
continue;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
if (descriptor.type === "accessor" && param.type !== "accessor") {
|
|
556
|
-
throw new InvalidParameterTypeError(
|
|
557
|
-
func_name,
|
|
558
|
-
descriptor.name,
|
|
559
|
-
descriptor.type,
|
|
560
|
-
this.describeResolvedParameterType(param)
|
|
561
|
-
);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
* resolve an access node, make it a concrete value
|
|
568
|
-
* @param ast the ast node to resolve
|
|
569
|
-
* @param env the environment in which to resolve the node
|
|
570
|
-
* @private
|
|
571
|
-
*/
|
|
572
|
-
private async resolveAccess(ast: any, env: any) {
|
|
573
|
-
console.assert(env == null);
|
|
574
|
-
let parent;
|
|
575
|
-
if (typeof ast.parent === "string") {
|
|
576
|
-
parent = { type: "class", value: ast.parent };
|
|
577
|
-
} else if (ast.parent.type === "object") {
|
|
578
|
-
parent = ast.parent;
|
|
579
|
-
} else {
|
|
580
|
-
parent = await this.resolve(ast.parent, null);
|
|
581
|
-
}
|
|
582
|
-
if (typeof ast.access === "string") {
|
|
583
|
-
const isObject = parent.type === "object";
|
|
584
|
-
const className = isObject ? parent.objectType : parent.value;
|
|
585
|
-
const classDescriptor = this.classDescriptors.find(
|
|
586
|
-
(c) => c.className === className
|
|
587
|
-
);
|
|
588
|
-
if (classDescriptor === undefined) {
|
|
589
|
-
if (parent.type == "array") {
|
|
590
|
-
const arrayValue = [];
|
|
591
|
-
for (const v of parent.value) {
|
|
592
|
-
try {
|
|
593
|
-
arrayValue.push(await this.resolveAccess({
|
|
594
|
-
...ast,
|
|
595
|
-
parent: v
|
|
596
|
-
}, env));
|
|
597
|
-
} catch (e) {
|
|
598
|
-
console.log(e);
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
let objType = null
|
|
602
|
-
if (arrayValue.length > 0) {
|
|
603
|
-
objType = arrayValue[0].objectType
|
|
604
|
-
}
|
|
605
|
-
return {
|
|
606
|
-
type: "array",
|
|
607
|
-
value: arrayValue,
|
|
608
|
-
objectType: objType
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
|
-
else {
|
|
612
|
-
throw new ClassMissingError(className);
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
const fieldDescriptor = Array.from(classDescriptor.fields).find(
|
|
616
|
-
(f) => f.field === ast.access
|
|
617
|
-
);
|
|
618
|
-
if (fieldDescriptor === undefined) {
|
|
619
|
-
throw new FieldMissingError(className, ast.access);
|
|
620
|
-
}
|
|
621
|
-
// we can only access if the field is static or the parent is an object
|
|
622
|
-
if (!isObject && !fieldDescriptor.isStatic) {
|
|
623
|
-
throw new FieldStaticError(className, ast.access);
|
|
624
|
-
}
|
|
625
|
-
const fieldType = parseType(fieldDescriptor.fieldType);
|
|
626
|
-
let fieldValue;
|
|
627
|
-
if (this.dry_run) {
|
|
628
|
-
if (fieldType.is_array) {
|
|
629
|
-
fieldValue = [null];
|
|
630
|
-
} else {
|
|
631
|
-
fieldValue = null
|
|
632
|
-
}
|
|
633
|
-
} else {
|
|
634
|
-
if (isObject) {
|
|
635
|
-
const parentObject = this.strip(parent);
|
|
636
|
-
if (parentObject === undefined || parentObject === null) {
|
|
637
|
-
throw new InterpreterError(
|
|
638
|
-
`Cannot access ${className}.${ast.access}: target object is undefined`
|
|
639
|
-
);
|
|
640
|
-
}
|
|
641
|
-
if (!this.dry_run) {
|
|
642
|
-
await parentObject.update();
|
|
643
|
-
}
|
|
644
|
-
fieldValue = parentObject[ast.access];
|
|
645
|
-
} else {
|
|
646
|
-
fieldValue = classDescriptor.classConstructor[ast.access];
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
if (fieldType.is_array) {
|
|
650
|
-
// noinspection JSObjectNullOrUndefined
|
|
651
|
-
return {
|
|
652
|
-
type: "array",
|
|
653
|
-
value: fieldValue.map((v) => {
|
|
654
|
-
return {
|
|
655
|
-
type: "object",
|
|
656
|
-
value: v,
|
|
657
|
-
objectType: fieldType.original_type,
|
|
658
|
-
};
|
|
659
|
-
}),
|
|
660
|
-
};
|
|
661
|
-
} else {
|
|
662
|
-
return {
|
|
663
|
-
type: "object",
|
|
664
|
-
value: fieldValue,
|
|
665
|
-
objectType: fieldType.original_type,
|
|
666
|
-
};
|
|
667
|
-
}
|
|
668
|
-
} else {
|
|
669
|
-
return await this.resolve(ast.access, parent);
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
/**
|
|
674
|
-
* resolve an index node, make it a concrete value
|
|
675
|
-
* @param ast the ast node to resolve
|
|
676
|
-
* @param env the environment in which to resolve the node
|
|
677
|
-
* @private
|
|
678
|
-
*/
|
|
679
|
-
private async resolveIndex(ast: any, env: any) {
|
|
680
|
-
console.assert(env == null);
|
|
681
|
-
let indexParam;
|
|
682
|
-
if (typeof ast.index === "number") {
|
|
683
|
-
indexParam = { type: "int", value: ast.index };
|
|
684
|
-
} else {
|
|
685
|
-
indexParam = ast.index;
|
|
686
|
-
}
|
|
687
|
-
return await this.resolve(
|
|
688
|
-
{
|
|
689
|
-
type: "access",
|
|
690
|
-
parent: ast.parent,
|
|
691
|
-
access: {
|
|
692
|
-
type: "function_call",
|
|
693
|
-
func_name: "index",
|
|
694
|
-
parameters: [{ parameter: "index", value: indexParam }],
|
|
695
|
-
},
|
|
696
|
-
},
|
|
697
|
-
env
|
|
698
|
-
);
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
/**
|
|
702
|
-
* resolve an array node, make it a concrete value
|
|
703
|
-
* @param ast the ast node to resolve
|
|
704
|
-
* @param env the environment in which to resolve the node
|
|
705
|
-
* @private
|
|
706
|
-
*/
|
|
707
|
-
private async resolveArray(ast: any, env: any) {
|
|
708
|
-
console.assert(env == null);
|
|
709
|
-
const values = [];
|
|
710
|
-
for (const v of ast.value) {
|
|
711
|
-
values.push(await this.resolve(v, null));
|
|
712
|
-
}
|
|
713
|
-
// make sure all the elements are objects
|
|
714
|
-
console.assert(values.every((v) => v.type === "object"));
|
|
715
|
-
// make sure all the elements have the same type
|
|
716
|
-
console.assert(values.every((v) => v.objectType === values[0].objectType));
|
|
717
|
-
return {
|
|
718
|
-
type: "array",
|
|
719
|
-
value: values,
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
/**
|
|
724
|
-
* resolve a function call node, make it a concrete value
|
|
725
|
-
* @param ast the ast node to resolve
|
|
726
|
-
* @param env the environment in which to resolve the node
|
|
727
|
-
* @private
|
|
728
|
-
*/
|
|
729
|
-
private async resolveFunctionCall(ast: any, env: any) {
|
|
730
|
-
const parameter_entries: Array<[string, any]> = [];
|
|
731
|
-
if (ast.parameters !== null) {
|
|
732
|
-
for (const p of ast.parameters) {
|
|
733
|
-
parameter_entries.push([p.parameter, await this.resolve(p.value, null)]);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
const parameters = new Map<string, any>(parameter_entries);
|
|
737
|
-
// find the function descriptor
|
|
738
|
-
let classDescriptor: ClassDescriptor<GenieObject>;
|
|
739
|
-
let funcDescriptor: FuncDescriptor;
|
|
740
|
-
let isArray = false;
|
|
741
|
-
let isArrayElementFunction = false;
|
|
742
|
-
if (env !== null) {
|
|
743
|
-
switch (env.type) {
|
|
744
|
-
case "class":
|
|
745
|
-
classDescriptor =
|
|
746
|
-
this.classDescriptors.find((c) => c.className === env.value);
|
|
747
|
-
if (classDescriptor === undefined) {
|
|
748
|
-
throw new ClassMissingError(env.value);
|
|
749
|
-
}
|
|
750
|
-
funcDescriptor = Array.from(
|
|
751
|
-
classDescriptor.functions
|
|
752
|
-
).find((f) => f.func_name === ast.func_name);
|
|
753
|
-
if (funcDescriptor === undefined) {
|
|
754
|
-
throw new FunctionMissingError(env.value, ast.func_name);
|
|
755
|
-
} else if (!funcDescriptor.isStatic) {
|
|
756
|
-
throw new FunctionStaticError(env.value, ast.func_name);
|
|
757
|
-
}
|
|
758
|
-
break;
|
|
759
|
-
case "object":
|
|
760
|
-
classDescriptor = this.classDescriptors.find((c) => c.className === env.objectType);
|
|
761
|
-
if (classDescriptor === undefined) {
|
|
762
|
-
throw new ClassMissingError(env.objectType);
|
|
763
|
-
}
|
|
764
|
-
funcDescriptor = Array.from(
|
|
765
|
-
classDescriptor.functions
|
|
766
|
-
).find((f) => f.func_name === ast.func_name);
|
|
767
|
-
if (funcDescriptor === undefined) {
|
|
768
|
-
throw new FunctionMissingError(env.objectType, ast.func_name);
|
|
769
|
-
}
|
|
770
|
-
break;
|
|
771
|
-
// deal with array functions (`matching`, `between`, `equals`)
|
|
772
|
-
case "array":
|
|
773
|
-
if(env.value.length == 0)
|
|
774
|
-
return{
|
|
775
|
-
type: "array",
|
|
776
|
-
value: [],
|
|
777
|
-
objectType: null
|
|
778
|
-
}
|
|
779
|
-
funcDescriptor = this.arrayFunctionDescriptors.find(
|
|
780
|
-
(f) => f.func_name === ast.func_name
|
|
781
|
-
);
|
|
782
|
-
if (funcDescriptor === undefined || env.value[0]["type"] === "array") {
|
|
783
|
-
const arrayValue = [];
|
|
784
|
-
for (const v of env.value) {
|
|
785
|
-
arrayValue.push(await this.resolveFunctionCall(ast, v));
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
return {
|
|
789
|
-
type: "array",
|
|
790
|
-
value: arrayValue,
|
|
791
|
-
objectType: arrayValue[0].objectType
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
// if there is any element in the array, replace the descriptor with the correct type
|
|
795
|
-
if (env.value.length > 0) {
|
|
796
|
-
funcDescriptor = JSON.parse(JSON.stringify(funcDescriptor));
|
|
797
|
-
funcDescriptor.returnType = funcDescriptor.returnType.replace(
|
|
798
|
-
"object",
|
|
799
|
-
env.value[0].objectType
|
|
800
|
-
);
|
|
801
|
-
}
|
|
802
|
-
isArray = true;
|
|
803
|
-
break;
|
|
804
|
-
}
|
|
805
|
-
} else {
|
|
806
|
-
// function is class constructor
|
|
807
|
-
const classDescriptor = this.classDescriptors.find(
|
|
808
|
-
(c) => c.className === ast.func_name
|
|
809
|
-
);
|
|
810
|
-
funcDescriptor = Array.from(classDescriptor.functions).find(
|
|
811
|
-
(f) => f.func_name === "constructor"
|
|
812
|
-
);
|
|
813
|
-
}
|
|
814
|
-
this.validateResolvedParameters(ast.func_name, funcDescriptor, parameters);
|
|
815
|
-
// reformat the parameters to match the function signature
|
|
816
|
-
let matchedParameters = {};
|
|
817
|
-
for (const descriptor of funcDescriptor.parameters) {
|
|
818
|
-
const param = parameters.get(descriptor.name);
|
|
819
|
-
if (param != null) {
|
|
820
|
-
matchedParameters[descriptor.name] = this.strip(param);
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
// function is a class constructor
|
|
825
|
-
if (env === null) {
|
|
826
|
-
const classDescriptor = this.classDescriptors.find(
|
|
827
|
-
(c) => c.className === ast.func_name
|
|
828
|
-
);
|
|
829
|
-
if (this.dry_run) {
|
|
830
|
-
return {
|
|
831
|
-
type: "object",
|
|
832
|
-
value: null,
|
|
833
|
-
objectType: ast.func_name,
|
|
834
|
-
};
|
|
835
|
-
} else {
|
|
836
|
-
return {
|
|
837
|
-
type: "object",
|
|
838
|
-
value: new classDescriptor.classConstructor(matchedParameters),
|
|
839
|
-
objectType: ast.func_name,
|
|
840
|
-
};
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
const returnType = parseType(funcDescriptor.returnType);
|
|
845
|
-
|
|
846
|
-
if (isArray) {
|
|
847
|
-
for (const element of env.value) {
|
|
848
|
-
if ((element as any).type == "object" && !this.dry_run) {
|
|
849
|
-
await (element as any).value.update();
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
matchedParameters["array"] = this.strip(env);
|
|
853
|
-
if (matchedParameters["field"] !== undefined) {
|
|
854
|
-
function constructAccess(accessor: any, parent: any) {
|
|
855
|
-
if (typeof accessor == "string") {
|
|
856
|
-
return {
|
|
857
|
-
type: "access",
|
|
858
|
-
parent: parent,
|
|
859
|
-
access: accessor,
|
|
860
|
-
};
|
|
861
|
-
} else {
|
|
862
|
-
if (accessor.type == "access") {
|
|
863
|
-
return {
|
|
864
|
-
type: "access",
|
|
865
|
-
parent: constructAccess(accessor.parent, parent),
|
|
866
|
-
access: accessor.access,
|
|
867
|
-
};
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
const field = matchedParameters["field"].field;
|
|
872
|
-
if (this.dry_run) {
|
|
873
|
-
// dry run for accessor
|
|
874
|
-
await this.resolve(constructAccess(field, {
|
|
875
|
-
type: "object",
|
|
876
|
-
value: null,
|
|
877
|
-
objectType: returnType.original_type,
|
|
878
|
-
}), null)
|
|
879
|
-
} else {
|
|
880
|
-
if (ast.func_name != "index" && ast.func_name != "length") {
|
|
881
|
-
const originalArray = matchedParameters["array"];
|
|
882
|
-
const array = [];
|
|
883
|
-
for (const element of originalArray) {
|
|
884
|
-
const fieldValue = await this.strip(await this.resolve(
|
|
885
|
-
constructAccess(field, {
|
|
886
|
-
type: "object",
|
|
887
|
-
value: element,
|
|
888
|
-
objectType: returnType.original_type,
|
|
889
|
-
})
|
|
890
|
-
));
|
|
891
|
-
array.push({
|
|
892
|
-
element: element,
|
|
893
|
-
fieldValue: fieldValue,
|
|
894
|
-
});
|
|
895
|
-
}
|
|
896
|
-
matchedParameters["array"] = array;
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
const targetImplementation = isArray
|
|
903
|
-
? this.arrayFunctionImplementations
|
|
904
|
-
: this.strip(env);
|
|
905
|
-
|
|
906
|
-
if (
|
|
907
|
-
!isArray &&
|
|
908
|
-
env?.type === "object" &&
|
|
909
|
-
!this.dry_run &&
|
|
910
|
-
(targetImplementation === undefined || targetImplementation === null)
|
|
911
|
-
) {
|
|
912
|
-
throw new InterpreterError(
|
|
913
|
-
`Cannot call ${ast.func_name}: target object is undefined`
|
|
914
|
-
);
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
// call the function
|
|
918
|
-
if (returnType.is_array) {
|
|
919
|
-
if (this.dry_run) {
|
|
920
|
-
return {
|
|
921
|
-
type: "array",
|
|
922
|
-
value: [{
|
|
923
|
-
type: "object",
|
|
924
|
-
value: null,
|
|
925
|
-
objectType: returnType.original_type,
|
|
926
|
-
}],
|
|
927
|
-
};
|
|
928
|
-
} else {
|
|
929
|
-
if (env.type === "object" && !this.dry_run) {
|
|
930
|
-
if (typeof targetImplementation.update !== "function") {
|
|
931
|
-
throw new InterpreterError(
|
|
932
|
-
`Cannot call ${ast.func_name}: target object has no update method`
|
|
933
|
-
);
|
|
934
|
-
}
|
|
935
|
-
await targetImplementation.update();
|
|
936
|
-
}
|
|
937
|
-
return {
|
|
938
|
-
type: "array",
|
|
939
|
-
value: (await targetImplementation[ast.func_name](matchedParameters)).map(
|
|
940
|
-
(v) => {
|
|
941
|
-
return {
|
|
942
|
-
type: "object",
|
|
943
|
-
value: v,
|
|
944
|
-
objectType: returnType.original_type,
|
|
945
|
-
};
|
|
946
|
-
}
|
|
947
|
-
),
|
|
948
|
-
};
|
|
949
|
-
}
|
|
950
|
-
} else {
|
|
951
|
-
if (this.dry_run) {
|
|
952
|
-
return {
|
|
953
|
-
type: "object",
|
|
954
|
-
value: null,
|
|
955
|
-
objectType: returnType.original_type,
|
|
956
|
-
};
|
|
957
|
-
} else {
|
|
958
|
-
if (env.type === "object" && !this.dry_run) {
|
|
959
|
-
if (typeof targetImplementation.update !== "function") {
|
|
960
|
-
throw new InterpreterError(
|
|
961
|
-
`Cannot call ${ast.func_name}: target object has no update method`
|
|
962
|
-
);
|
|
963
|
-
}
|
|
964
|
-
await targetImplementation.update();
|
|
965
|
-
}
|
|
966
|
-
return {
|
|
967
|
-
type: "object",
|
|
968
|
-
value: await(targetImplementation[ast.func_name](matchedParameters)),
|
|
969
|
-
objectType: returnType.original_type,
|
|
970
|
-
};
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
}
|