@rsconcept/domain 1.3.0 → 1.4.1

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 (46) hide show
  1. package/dist/{analyzer-DlSq3Y3r.d.ts → analyzer-BliNe3pV.d.ts} +2 -2
  2. package/dist/{ast-annotations-BiMjkKvz.d.ts → ast-annotations-GOkbNN8F.d.ts} +2 -2
  3. package/dist/{calculator-C9W2jkSx.d.ts → calculator-DFevxZyV.d.ts} +3 -3
  4. package/dist/{error-E1LVq_3w.d.ts → error-deey7EiC.d.ts} +2 -1
  5. package/dist/index.d.ts +7 -7
  6. package/dist/index.js +2 -2
  7. package/dist/library/rsengine.d.ts +2 -3
  8. package/dist/library/rsengine.js +15 -43
  9. package/dist/library/rsengine.js.map +1 -1
  10. package/dist/library/rsform-api.d.ts +1 -1
  11. package/dist/library/rsform.d.ts +1 -1
  12. package/dist/library/rsmodel-api.d.ts +1 -1
  13. package/dist/library/rsmodel-api.js +1 -1
  14. package/dist/library/rsmodel.d.ts +1 -1
  15. package/dist/rslang/ast-annotations.d.ts +1 -1
  16. package/dist/rslang/error.d.ts +1 -1
  17. package/dist/rslang/error.js +1 -0
  18. package/dist/rslang/error.js.map +1 -1
  19. package/dist/rslang/eval/calculator.d.ts +1 -1
  20. package/dist/rslang/eval/calculator.js +1 -1
  21. package/dist/rslang/eval/evaluation-cache.d.ts +1 -1
  22. package/dist/rslang/eval/evaluator.d.ts +2 -2
  23. package/dist/rslang/eval/evaluator.js +17 -11
  24. package/dist/rslang/eval/evaluator.js.map +1 -1
  25. package/dist/rslang/eval/value-api.d.ts +1 -1
  26. package/dist/rslang/eval/value-api.js +1 -1
  27. package/dist/rslang/eval/value.d.ts +2 -2
  28. package/dist/rslang/eval/value.js +2 -2
  29. package/dist/rslang/index.d.ts +6 -6
  30. package/dist/rslang/index.js +1 -1
  31. package/dist/rslang/parser/syntax-errors.d.ts +1 -1
  32. package/dist/rslang/semantic/analyzer.d.ts +1 -1
  33. package/dist/rslang/semantic/type-auditor.d.ts +1 -1
  34. package/dist/rslang/semantic/value-auditor.d.ts +1 -1
  35. package/dist/{value-CTjX6825.d.ts → value-BduA_ZGp.d.ts} +8 -2
  36. package/dist/{value-api-Bw-SgaYY.d.ts → value-api-B6T6f4Ia.d.ts} +2 -2
  37. package/dist/{value-B8UtCqaK.js → value-ybFBRuz6.js} +14 -3
  38. package/dist/{value-B8UtCqaK.js.map → value-ybFBRuz6.js.map} +1 -1
  39. package/package.json +4 -4
  40. package/src/index.ts +1 -1
  41. package/src/library/rsengine.ts +14 -59
  42. package/src/rslang/error.ts +1 -0
  43. package/src/rslang/eval/calculator.test.ts +8 -0
  44. package/src/rslang/eval/evaluator.test.ts +5 -1
  45. package/src/rslang/eval/evaluator.ts +22 -13
  46. package/src/rslang/eval/value.ts +14 -1
@@ -23,7 +23,6 @@ export interface RSEngineServices {
23
23
  export interface RSEngineNotifications {
24
24
  onInvalidSetValue: () => void;
25
25
  onCalculationSuccess: (timeSpent: string) => void;
26
- onEvaluationError: (message: string) => void;
27
26
  }
28
27
 
29
28
  /** Calculation engine for {@link RSModel}. */
@@ -274,26 +273,12 @@ export class RSEngine {
274
273
 
275
274
  /** Evaluates expression for {@link RSEngine}. */
276
275
  public evaluateExpression(expression: string, cstType: CstType): CalculatorResult {
277
- return getEvaluationFor(expression, cstType, this.schema!, this.calculator, message =>
278
- this.notifications?.onEvaluationError(message)
279
- );
276
+ return getEvaluationFor(expression, cstType, this.schema!, this.calculator);
280
277
  }
281
278
 
282
279
  /** Evaluates AST for {@link RSEngine}. */
283
280
  public evaluateAst(ast: AstNode, options?: CalculatorEvaluateOptions): CalculatorResult {
284
- try {
285
- const evaluation = this.calculator.evaluateFull(ast, options);
286
- return evaluation;
287
- } catch (error) {
288
- this.notifications?.onEvaluationError((error as Error).message);
289
- console.error(error);
290
- return {
291
- value: null,
292
- iterations: 0,
293
- cacheHits: 0,
294
- errors: []
295
- };
296
- }
281
+ return this.calculator.evaluateFull(ast, options);
297
282
  }
298
283
 
299
284
  /** Calculates value for {@link Constituenta}. */
@@ -307,9 +292,7 @@ export class RSEngine {
307
292
  const predecessors = this.schema.graph.expandAllInputs([cstID]);
308
293
  this.prepareEvaluation(predecessors);
309
294
  }
310
- const result = getEvaluationFor(cst.definition_formal, cst.cst_type, this.schema, this.calculator, message =>
311
- this.notifications?.onEvaluationError(message)
312
- );
295
+ const result = getEvaluationFor(cst.definition_formal, cst.cst_type, this.schema, this.calculator);
313
296
 
314
297
  if (result.value === null) {
315
298
  this.calculator.resetValue(cst.alias);
@@ -492,9 +475,7 @@ export class RSEngine {
492
475
  if (dependencies.includes(cstID)) {
493
476
  const cst = this.schema!.cstByID.get(cstID)!;
494
477
  if (isInferrable(cst.cst_type)) {
495
- const value = fastEvaluation(cst.definition_formal, cst.cst_type, this.schema!, this.calculator, message =>
496
- this.notifications?.onEvaluationError(message)
497
- );
478
+ const value = fastEvaluation(cst.definition_formal, cst.cst_type, this.schema!, this.calculator);
498
479
  if (value !== null) {
499
480
  this.calculator.setValue(cst.alias, value);
500
481
  } else {
@@ -537,8 +518,7 @@ function getEvaluationFor(
537
518
  expression: string,
538
519
  cstType: CstType,
539
520
  schema: RSForm,
540
- calculator: RSCalculator,
541
- onEvaluationError?: (message: string) => void
521
+ calculator: RSCalculator
542
522
  ): CalculatorResult {
543
523
  const parse = getAnalysisFor(expression, cstType, schema);
544
524
  if (!parse.success || !parse.ast) {
@@ -548,46 +528,21 @@ function getEvaluationFor(
548
528
  cacheHits: 0,
549
529
  errors: parse.errors
550
530
  };
551
- } else {
552
- try {
553
- const result = calculator.evaluateFull(parse.ast);
554
- return {
555
- value: result.value,
556
- iterations: result.iterations,
557
- cacheHits: result.cacheHits,
558
- errors: [...parse.errors, ...result.errors]
559
- };
560
- } catch (error) {
561
- onEvaluationError?.((error as Error).message);
562
- console.error(expression, error);
563
- return {
564
- value: null,
565
- iterations: 0,
566
- cacheHits: 0,
567
- errors: []
568
- };
569
- }
570
531
  }
532
+ const result = calculator.evaluateFull(parse.ast);
533
+ return {
534
+ value: result.value,
535
+ iterations: result.iterations,
536
+ cacheHits: result.cacheHits,
537
+ errors: [...parse.errors, ...result.errors]
538
+ };
571
539
  }
572
540
 
573
541
  /** Evaluates expression for {@link RSModel}. */
574
- function fastEvaluation(
575
- expression: string,
576
- cstType: CstType,
577
- schema: RSForm,
578
- calculator: RSCalculator,
579
- onEvaluationError?: (message: string) => void
580
- ): Value | null {
542
+ function fastEvaluation(expression: string, cstType: CstType, schema: RSForm, calculator: RSCalculator): Value | null {
581
543
  const parse = getAnalysisFor(expression, cstType, schema);
582
544
  if (!parse.success || !parse.ast) {
583
545
  return null;
584
- } else {
585
- try {
586
- return calculator.evaluateFast(parse.ast);
587
- } catch (error) {
588
- onEvaluationError?.((error as Error).message);
589
- console.error(expression, error);
590
- return null;
591
- }
592
546
  }
547
+ return calculator.evaluateFast(parse.ast);
593
548
  }
@@ -84,6 +84,7 @@ export const RSErrorCode = {
84
84
  calcInvalidDebool: 0x8105, // 35333
85
85
  iterateInfinity: 0x8106, // 35334
86
86
  calculationNotSupported: 0x8107, // 35335
87
+ calcInvalidData: 0x8108, // 35336
87
88
 
88
89
  cstEmptyDerived: 0x8861, // 34913
89
90
  definitionNotAllowed: 0x8862 // 34914
@@ -73,6 +73,14 @@ describe('RSCalculator', () => {
73
73
  expectCalcFull('X11', { expectErrorCode: RSErrorCode.calcGlobalMissing });
74
74
  });
75
75
 
76
+ it('returns error for undeclared local variable', () => {
77
+ expectCalcFull('∀a∈X1 b=b', { expectErrorCode: RSErrorCode.localUndeclared });
78
+ });
79
+
80
+ it('returns error for incompatible value comparison', () => {
81
+ expectCalcFull('X1=1', { expectErrorCode: RSErrorCode.calcInvalidData });
82
+ });
83
+
76
84
  it('returns error for infinite quantifier', () => {
77
85
  expectCalcFull('∀a∈Z a=a', { expectErrorCode: RSErrorCode.iterateInfinity });
78
86
  });
@@ -192,6 +192,7 @@ const correctValuesData = [
192
192
  const errorData = [
193
193
  ['[a∈ℬ(R1)] a=a', { code: RSErrorCode.calculationNotSupported, from: 0, to: 13 }],
194
194
  ['debool(X1)', { code: RSErrorCode.calcInvalidDebool, from: 7, to: 9 }],
195
+ ['Pr1(1)', { code: RSErrorCode.calcUnknownError, from: 0, to: 6 }],
195
196
  ['∀a∈Z a=a', { code: RSErrorCode.iterateInfinity, from: 3, to: 4 }],
196
197
  ['ℬℬ({1,2,3,4,5})', { code: RSErrorCode.booleanBaseLimit, from: 1, to: 15, params: [String(BOOL_INFINITY)] }],
197
198
  ['X1\\D1', { code: RSErrorCode.calcGlobalMissing, from: 3, to: 5, params: ['D1'] }],
@@ -227,7 +228,10 @@ const errorData = [
227
228
  ['R{a:=X1 | D1=D1 | a∪X1}', { code: RSErrorCode.calcGlobalMissing, from: 10, to: 12, params: ['D1'] }],
228
229
  ['I{(a,b) | a:∈D1; b:=a; b≠a}', { code: RSErrorCode.calcGlobalMissing, from: 13, to: 15, params: ['D1'] }],
229
230
  ['I{(a,b) | a:∈X1; b:=a; b≠D1}', { code: RSErrorCode.calcGlobalMissing, from: 25, to: 27, params: ['D1'] }],
230
- ['I{(a,b) | a:∈X1; b:=D1; b≠a}', { code: RSErrorCode.calcGlobalMissing, from: 20, to: 22, params: ['D1'] }]
231
+ ['I{(a,b) | a:∈X1; b:=D1; b≠a}', { code: RSErrorCode.calcGlobalMissing, from: 20, to: 22, params: ['D1'] }],
232
+ ['∀a∈X1 b=b', { code: RSErrorCode.localUndeclared, from: 6, to: 7, params: ['b'] }],
233
+ ['X1=1', { code: RSErrorCode.calcInvalidData, from: 0, to: 4, params: ['{1, 2, 3}', '1'] }],
234
+ ['X1=1 & 1=1', { code: RSErrorCode.calcInvalidData, from: 0, to: 4, params: ['{1, 2, 3}', '1'] }]
231
235
  ];
232
236
 
233
237
  describe('Calculator', () => {
@@ -10,6 +10,7 @@ import { TokenID } from '../parser/token';
10
10
  import { EvaluationCache, EvaluationMetadata } from './evaluation-cache';
11
11
  import {
12
12
  BOOL_INFINITY,
13
+ CalcInvalidDataError,
13
14
  compare,
14
15
  EmptySetV,
15
16
  set,
@@ -153,11 +154,19 @@ export class Evaluator {
153
154
  }
154
155
  }
155
156
 
156
- const result = this.dispatchVisitImpl(node);
157
- if (!this.disableCache && result !== null && info.cacheable && stamp !== null) {
158
- this.evalCache.store(info.structuralKey, stamp, result);
157
+ try {
158
+ const result = this.dispatchVisitImpl(node);
159
+ if (!this.disableCache && result !== null && info.cacheable && stamp !== null) {
160
+ this.evalCache.store(info.structuralKey, stamp, result);
161
+ }
162
+ return result;
163
+ } catch (error) {
164
+ if (error instanceof CalcInvalidDataError) {
165
+ return this.onError(RSErrorCode.calcInvalidData, node, [error.left, error.right]);
166
+ }
167
+ this.onError(RSErrorCode.calcUnknownError, node);
168
+ return null;
159
169
  }
160
- return result;
161
170
  }
162
171
 
163
172
  private dispatchVisitImpl(node: AstNode): Value | null {
@@ -280,8 +289,8 @@ export class Evaluator {
280
289
 
281
290
  private visitGlobal(node: AstNode): Value | null {
282
291
  const alias = getNodeText(node);
283
- const value = this.context.get(alias);
284
- if (value === undefined) {
292
+ const value = this.context.get(alias) ?? null;
293
+ if (value === null) {
285
294
  return this.onError(RSErrorCode.calcGlobalMissing, node, [alias]);
286
295
  }
287
296
  return value;
@@ -289,7 +298,11 @@ export class Evaluator {
289
298
 
290
299
  private visitLocal(node: AstNode): Value | null {
291
300
  const alias = getNodeText(node);
292
- return this.locals.getLocal(alias);
301
+ const value = this.locals.findLocal(alias);
302
+ if (value === null) {
303
+ return this.onError(RSErrorCode.localUndeclared, node, [alias]);
304
+ }
305
+ return value;
293
306
  }
294
307
 
295
308
  private visitFunctionCall(node: AstNode): Value | null {
@@ -910,12 +923,8 @@ class LocalContext {
910
923
  }
911
924
  }
912
925
 
913
- getLocal(alias: string): Value {
914
- const binding = this.data.get(alias);
915
- if (binding === undefined) {
916
- throw new Error(`Local variable "${alias}" not found`);
917
- }
918
- return binding.value;
926
+ findLocal(alias: string): Value | null {
927
+ return this.data.get(alias)?.value ?? null;
919
928
  }
920
929
 
921
930
  buildDependencyStamp(reads: ReadonlySet<string>): string | null {
@@ -41,6 +41,19 @@ export function makeValuePath(path: number[]): ValuePath {
41
41
  /** Empty set ∅. */
42
42
  export const EmptySetV = [];
43
43
 
44
+ /** Thrown when a runtime value operation cannot be applied to the given operands. */
45
+ export class CalcInvalidDataError extends Error {
46
+ readonly left: string;
47
+ readonly right: string;
48
+
49
+ constructor(left: string, right: string) {
50
+ super(`Cannot compare different types ${left} and ${right}`);
51
+ this.name = 'CalcInvalidDataError';
52
+ this.left = left;
53
+ this.right = right;
54
+ }
55
+ }
56
+
44
57
  /** Compare two structured data without recursive calls. */
45
58
  export function compare(v1: Value, v2: Value): number {
46
59
  const stack1: Value[] = [v1];
@@ -64,7 +77,7 @@ export function compare(v1: Value, v2: Value): number {
64
77
  const isArray1 = Array.isArray(el1);
65
78
  const isArray2 = Array.isArray(el2);
66
79
  if (!isArray1 || !isArray2) {
67
- throw new Error(`Cannot compare different types ${printValue(el1!)} and ${printValue(el2!)}`);
80
+ throw new CalcInvalidDataError(printValue(el1!), printValue(el2!));
68
81
  }
69
82
 
70
83
  const arr1 = el1;