@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.
- package/dist/{analyzer-DlSq3Y3r.d.ts → analyzer-BliNe3pV.d.ts} +2 -2
- package/dist/{ast-annotations-BiMjkKvz.d.ts → ast-annotations-GOkbNN8F.d.ts} +2 -2
- package/dist/{calculator-C9W2jkSx.d.ts → calculator-DFevxZyV.d.ts} +3 -3
- package/dist/{error-E1LVq_3w.d.ts → error-deey7EiC.d.ts} +2 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +2 -2
- package/dist/library/rsengine.d.ts +2 -3
- package/dist/library/rsengine.js +15 -43
- package/dist/library/rsengine.js.map +1 -1
- package/dist/library/rsform-api.d.ts +1 -1
- package/dist/library/rsform.d.ts +1 -1
- package/dist/library/rsmodel-api.d.ts +1 -1
- package/dist/library/rsmodel-api.js +1 -1
- package/dist/library/rsmodel.d.ts +1 -1
- package/dist/rslang/ast-annotations.d.ts +1 -1
- package/dist/rslang/error.d.ts +1 -1
- package/dist/rslang/error.js +1 -0
- package/dist/rslang/error.js.map +1 -1
- package/dist/rslang/eval/calculator.d.ts +1 -1
- package/dist/rslang/eval/calculator.js +1 -1
- package/dist/rslang/eval/evaluation-cache.d.ts +1 -1
- package/dist/rslang/eval/evaluator.d.ts +2 -2
- package/dist/rslang/eval/evaluator.js +17 -11
- package/dist/rslang/eval/evaluator.js.map +1 -1
- package/dist/rslang/eval/value-api.d.ts +1 -1
- package/dist/rslang/eval/value-api.js +1 -1
- package/dist/rslang/eval/value.d.ts +2 -2
- package/dist/rslang/eval/value.js +2 -2
- package/dist/rslang/index.d.ts +6 -6
- package/dist/rslang/index.js +1 -1
- package/dist/rslang/parser/syntax-errors.d.ts +1 -1
- package/dist/rslang/semantic/analyzer.d.ts +1 -1
- package/dist/rslang/semantic/type-auditor.d.ts +1 -1
- package/dist/rslang/semantic/value-auditor.d.ts +1 -1
- package/dist/{value-CTjX6825.d.ts → value-BduA_ZGp.d.ts} +8 -2
- package/dist/{value-api-Bw-SgaYY.d.ts → value-api-B6T6f4Ia.d.ts} +2 -2
- package/dist/{value-B8UtCqaK.js → value-ybFBRuz6.js} +14 -3
- package/dist/{value-B8UtCqaK.js.map → value-ybFBRuz6.js.map} +1 -1
- package/package.json +4 -4
- package/src/index.ts +1 -1
- package/src/library/rsengine.ts +14 -59
- package/src/rslang/error.ts +1 -0
- package/src/rslang/eval/calculator.test.ts +8 -0
- package/src/rslang/eval/evaluator.test.ts +5 -1
- package/src/rslang/eval/evaluator.ts +22 -13
- package/src/rslang/eval/value.ts +14 -1
package/src/library/rsengine.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
}
|
package/src/rslang/error.ts
CHANGED
|
@@ -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
|
-
|
|
157
|
-
|
|
158
|
-
this.
|
|
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 ===
|
|
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
|
-
|
|
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
|
-
|
|
914
|
-
|
|
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 {
|
package/src/rslang/eval/value.ts
CHANGED
|
@@ -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
|
|
80
|
+
throw new CalcInvalidDataError(printValue(el1!), printValue(el2!));
|
|
68
81
|
}
|
|
69
82
|
|
|
70
83
|
const arr1 = el1;
|