@garrix82/reactgenie-dsl 1.0.0 → 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.
Files changed (105) hide show
  1. package/.env +10 -0
  2. package/.env.example +17 -0
  3. package/.github/workflows/publish.yml +20 -0
  4. package/README.md +5 -1
  5. package/package.json +1 -5
  6. package/dist/__test__/dsl-descriptor.test.d.ts +0 -1
  7. package/dist/__test__/dsl-descriptor.test.js +0 -27
  8. package/dist/__test__/dsl-descriptor.test.js.map +0 -1
  9. package/dist/__test__/example_descriptor.d.ts +0 -125
  10. package/dist/__test__/example_descriptor.js +0 -607
  11. package/dist/__test__/example_descriptor.js.map +0 -1
  12. package/dist/__test__/food_descriptor.state.json +0 -1
  13. package/dist/__test__/food_descriptor.test.d.ts +0 -74
  14. package/dist/__test__/food_descriptor.test.js +0 -205
  15. package/dist/__test__/food_descriptor.test.js.map +0 -1
  16. package/dist/__test__/nl-interpreter-provider-selection.test.d.ts +0 -1
  17. package/dist/__test__/nl-interpreter-provider-selection.test.js +0 -73
  18. package/dist/__test__/nl-interpreter-provider-selection.test.js.map +0 -1
  19. package/dist/__test__/nl-interpreter.test.d.ts +0 -1
  20. package/dist/__test__/nl-interpreter.test.js +0 -86
  21. package/dist/__test__/nl-interpreter.test.js.map +0 -1
  22. package/dist/decorators/__test__/decorators.test.d.ts +0 -1
  23. package/dist/decorators/__test__/decorators.test.js +0 -182
  24. package/dist/decorators/__test__/decorators.test.js.map +0 -1
  25. package/dist/decorators/__test__/inheritance-descriptor.test.d.ts +0 -1
  26. package/dist/decorators/__test__/inheritance-descriptor.test.js +0 -107
  27. package/dist/decorators/__test__/inheritance-descriptor.test.js.map +0 -1
  28. package/dist/dsl/__test__/dsl-interpreter.test.d.ts +0 -1
  29. package/dist/dsl/__test__/dsl-interpreter.test.js +0 -334
  30. package/dist/dsl/__test__/dsl-interpreter.test.js.map +0 -1
  31. package/dist/dsl/__test__/parser.gen.test.d.ts +0 -1
  32. package/dist/dsl/__test__/parser.gen.test.js +0 -283
  33. package/dist/dsl/__test__/parser.gen.test.js.map +0 -1
  34. package/dist/nl/__test__/context-aware-prompt.test.d.ts +0 -1
  35. package/dist/nl/__test__/context-aware-prompt.test.js +0 -247
  36. package/dist/nl/__test__/context-aware-prompt.test.js.map +0 -1
  37. package/dist/nl/__test__/context-selector.test.d.ts +0 -1
  38. package/dist/nl/__test__/context-selector.test.js +0 -20
  39. package/dist/nl/__test__/context-selector.test.js.map +0 -1
  40. package/dist/nl/__test__/nl-parser-groq-transport.test.d.ts +0 -1
  41. package/dist/nl/__test__/nl-parser-groq-transport.test.js +0 -87
  42. package/dist/nl/__test__/nl-parser-groq-transport.test.js.map +0 -1
  43. package/dist/nl/__test__/nl-parser-openai-parity.test.d.ts +0 -1
  44. package/dist/nl/__test__/nl-parser-openai-parity.test.js +0 -206
  45. package/dist/nl/__test__/nl-parser-openai-parity.test.js.map +0 -1
  46. package/dist/nl/__test__/nl-parser-openai-sampling.test.d.ts +0 -1
  47. package/dist/nl/__test__/nl-parser-openai-sampling.test.js +0 -44
  48. package/dist/nl/__test__/nl-parser-openai-sampling.test.js.map +0 -1
  49. package/dist/nl/__test__/nl-parser-openai-transport.test.d.ts +0 -1
  50. package/dist/nl/__test__/nl-parser-openai-transport.test.js +0 -55
  51. package/dist/nl/__test__/nl-parser-openai-transport.test.js.map +0 -1
  52. package/dist/nl/__test__/nl-parser-utils.test.d.ts +0 -1
  53. package/dist/nl/__test__/nl-parser-utils.test.js +0 -70
  54. package/dist/nl/__test__/nl-parser-utils.test.js.map +0 -1
  55. package/dist/nl/__test__/nl-parser.test.d.ts +0 -1
  56. package/dist/nl/__test__/nl-parser.test.js +0 -64
  57. package/dist/nl/__test__/nl-parser.test.js.map +0 -1
  58. package/dist/nl/__test__/parameter-tuning.test.d.ts +0 -1
  59. package/dist/nl/__test__/parameter-tuning.test.js +0 -95
  60. package/dist/nl/__test__/parameter-tuning.test.js.map +0 -1
  61. package/dist/nl/__test__/semantic-parsing-experiment.test.d.ts +0 -1
  62. package/dist/nl/__test__/semantic-parsing-experiment.test.js +0 -178
  63. package/dist/nl/__test__/semantic-parsing-experiment.test.js.map +0 -1
  64. package/dist/nl/llm-monitoring.test.d.ts +0 -5
  65. package/dist/nl/llm-monitoring.test.js +0 -101
  66. package/dist/nl/llm-monitoring.test.js.map +0 -1
  67. package/lib/__test__/dsl-descriptor.test.ts +0 -27
  68. package/lib/__test__/example_descriptor.ts +0 -762
  69. package/lib/__test__/food_descriptor.state.json +0 -1
  70. package/lib/__test__/food_descriptor.test.ts +0 -331
  71. package/lib/__test__/nl-interpreter-provider-selection.test.ts +0 -126
  72. package/lib/__test__/nl-interpreter.test.ts +0 -129
  73. package/lib/decorators/__test__/decorators.test.ts +0 -177
  74. package/lib/decorators/__test__/inheritance-descriptor.test.ts +0 -92
  75. package/lib/decorators/decorators.ts +0 -754
  76. package/lib/decorators/index.ts +0 -2
  77. package/lib/decorators/store.ts +0 -47
  78. package/lib/dsl/__test__/dsl-interpreter.test.ts +0 -453
  79. package/lib/dsl/__test__/parser.gen.test.ts +0 -296
  80. package/lib/dsl/dsl-interpreter.ts +0 -974
  81. package/lib/dsl/index.ts +0 -1
  82. package/lib/dsl/parser.gen.js +0 -1479
  83. package/lib/dsl/parser.pegjs +0 -130
  84. package/lib/dsl-descriptor.ts +0 -241
  85. package/lib/index.ts +0 -5
  86. package/lib/nl/__test__/context-aware-prompt.test.ts +0 -372
  87. package/lib/nl/__test__/context-selector.test.ts +0 -27
  88. package/lib/nl/__test__/nl-parser-groq-transport.test.ts +0 -139
  89. package/lib/nl/__test__/nl-parser-openai-parity.test.ts +0 -381
  90. package/lib/nl/__test__/nl-parser-openai-sampling.test.ts +0 -73
  91. package/lib/nl/__test__/nl-parser-openai-transport.test.ts +0 -79
  92. package/lib/nl/__test__/nl-parser-utils.test.ts +0 -98
  93. package/lib/nl/__test__/nl-parser.test.ts +0 -119
  94. package/lib/nl/__test__/parameter-tuning.test.ts +0 -137
  95. package/lib/nl/__test__/semantic-parsing-experiment.test.ts +0 -260
  96. package/lib/nl/context-selector.ts +0 -123
  97. package/lib/nl/index.ts +0 -19
  98. package/lib/nl/llm-monitoring.test.ts +0 -136
  99. package/lib/nl/llm-monitoring.ts +0 -339
  100. package/lib/nl/nl-parser-groq.ts +0 -510
  101. package/lib/nl/nl-parser-utils.ts +0 -310
  102. package/lib/nl/nl-parser.ts +0 -616
  103. package/lib/nl/prompt-gen.ts +0 -607
  104. package/lib/nl/prompt-res.ts +0 -207
  105. 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
- }