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