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