@rsconcept/domain 1.5.2 → 1.6.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 (37) hide show
  1. package/dist/{analyzer-0kmNRqcr.d.ts → analyzer-camCAEkQ.d.ts} +2 -2
  2. package/dist/{ast-annotations-BnVekMjH.d.ts → ast-annotations-DiXmFraa.d.ts} +2 -2
  3. package/dist/{calculator-OV-RNXre.d.ts → calculator-BuVooqpb.d.ts} +2 -2
  4. package/dist/{error-DYLkv4B3.d.ts → error-CZXO5Y6a.d.ts} +7 -3
  5. package/dist/index.d.ts +4 -4
  6. package/dist/library/rsengine.d.ts +1 -1
  7. package/dist/library/rsform-api.d.ts +1 -1
  8. package/dist/library/rsform-api.js +2 -1
  9. package/dist/library/rsform-api.js.map +1 -1
  10. package/dist/library/rsform.d.ts +1 -1
  11. package/dist/rslang/ast-annotations.d.ts +1 -1
  12. package/dist/rslang/error.d.ts +1 -1
  13. package/dist/rslang/error.js +6 -2
  14. package/dist/rslang/error.js.map +1 -1
  15. package/dist/rslang/eval/calculator.d.ts +1 -1
  16. package/dist/rslang/eval/evaluator.d.ts +1 -1
  17. package/dist/rslang/index.d.ts +4 -4
  18. package/dist/rslang/parser/syntax-errors.d.ts +1 -1
  19. package/dist/rslang/parser/syntax-errors.js +44 -0
  20. package/dist/rslang/parser/syntax-errors.js.map +1 -1
  21. package/dist/rslang/semantic/analyzer.d.ts +1 -1
  22. package/dist/rslang/semantic/analyzer.js +5 -2
  23. package/dist/rslang/semantic/analyzer.js.map +1 -1
  24. package/dist/rslang/semantic/type-auditor.d.ts +1 -1
  25. package/dist/rslang/semantic/type-auditor.js +32 -11
  26. package/dist/rslang/semantic/type-auditor.js.map +1 -1
  27. package/dist/rslang/semantic/value-auditor.d.ts +1 -1
  28. package/package.json +1 -1
  29. package/src/library/rsform-api.test.ts +30 -2
  30. package/src/library/rsform-api.ts +2 -1
  31. package/src/rslang/error.ts +6 -2
  32. package/src/rslang/parser/parser.test.ts +21 -0
  33. package/src/rslang/parser/syntax-errors.ts +63 -0
  34. package/src/rslang/semantic/analyzer.test.ts +56 -6
  35. package/src/rslang/semantic/analyzer.ts +5 -2
  36. package/src/rslang/semantic/type-auditor.test.ts +17 -7
  37. package/src/rslang/semantic/type-auditor.ts +31 -22
@@ -45,15 +45,65 @@ describe('RSLang analyzer', () => {
45
45
  });
46
46
 
47
47
  it('Expected type', () => {
48
- expectError('X1', { expected: TypeClass.logic }, { code: RSErrorCode.expectedType, from: 0, to: 2 });
49
- expectError('1=1', { expected: TypeClass.typification }, { code: RSErrorCode.expectedType, from: 0, to: 3 });
50
- expectError('1=1', { expected: TypeClass.function }, { code: RSErrorCode.expectedType, from: 0, to: 3 });
51
- expectError('[a ∈ X1] a=a', { expected: TypeClass.function }, { code: RSErrorCode.expectedType, from: 0, to: 12 });
52
- expectError('[a ∈ X1] a', { expected: TypeClass.predicate }, { code: RSErrorCode.expectedType, from: 0, to: 10 });
48
+ expectError(
49
+ 'X1',
50
+ { expected: TypeClass.logic },
51
+ {
52
+ code: RSErrorCode.expectedType,
53
+ from: 0,
54
+ to: 2,
55
+ params: [String(TypeClass.logic), labelType(bool(basic('X1')))]
56
+ }
57
+ );
58
+ expectError(
59
+ '1=1',
60
+ { expected: TypeClass.typification },
61
+ {
62
+ code: RSErrorCode.expectedType,
63
+ from: 0,
64
+ to: 3,
65
+ params: [String(TypeClass.typification), 'Logic']
66
+ }
67
+ );
68
+ expectError(
69
+ '1=1',
70
+ { expected: TypeClass.function },
71
+ {
72
+ code: RSErrorCode.expectedType,
73
+ from: 0,
74
+ to: 3,
75
+ params: [String(TypeClass.function), 'Logic']
76
+ }
77
+ );
78
+ expectError(
79
+ '[a ∈ X1] a=a',
80
+ { expected: TypeClass.function },
81
+ {
82
+ code: RSErrorCode.expectedType,
83
+ from: 0,
84
+ to: 12,
85
+ params: [String(TypeClass.function), '[X1] → Logic']
86
+ }
87
+ );
88
+ expectError(
89
+ '[a ∈ X1] a',
90
+ { expected: TypeClass.predicate },
91
+ {
92
+ code: RSErrorCode.expectedType,
93
+ from: 0,
94
+ to: 10,
95
+ params: [String(TypeClass.predicate), '[X1] → X1']
96
+ }
97
+ );
53
98
  });
54
99
 
55
100
  it('Reports analyzer-level ranges for whole-expression errors', () => {
56
101
  const result = analyzer.checkFull('X1', { expected: TypeClass.logic });
57
- expect(result.errors[0]).toMatchObject({ code: RSErrorCode.expectedType, from: 0, to: 2 });
102
+ expect(result.errors[0]).toMatchObject({
103
+ code: RSErrorCode.expectedType,
104
+ from: 0,
105
+ to: 2,
106
+ params: [String(TypeClass.logic), labelType(bool(basic('X1')))]
107
+ });
58
108
  });
59
109
  });
@@ -1,6 +1,7 @@
1
1
  import { type AstNode, buildTree } from '../../parsing';
2
2
  import { annotateError } from '../ast-annotations';
3
3
  import { RSErrorCode, type RSErrorDescription } from '../error';
4
+ import { labelType } from '../labels';
4
5
  import { normalizeAST } from '../parser/normalize';
5
6
  import { parser as rslangParser } from '../parser/parser';
6
7
  import { extractSyntaxErrors } from '../parser/syntax-errors';
@@ -136,13 +137,15 @@ export class RSLangAnalyzer {
136
137
  return { success: true, type: debool(type), valueClass: ValueClass.VALUE, errors, ast };
137
138
  }
138
139
  if (options?.expected && getTypeClass(type.typeID) !== options.expected) {
140
+ const errorParams = [String(options.expected), labelType(type)];
139
141
  reporter({
140
142
  code: RSErrorCode.expectedType,
141
143
  from: ast.from,
142
- to: ast.to
144
+ to: ast.to,
145
+ params: errorParams
143
146
  });
144
147
  if (options?.annotateErrors) {
145
- annotateError(ast, RSErrorCode.expectedType);
148
+ annotateError(ast, RSErrorCode.expectedType, errorParams);
146
149
  }
147
150
  return { success: false, type: null, valueClass: null, errors, ast };
148
151
  }
@@ -185,6 +185,7 @@ const errorData = [
185
185
  ['∀a∈X1 a=a & a=a', { code: RSErrorCode.localOutOfScopeParentheses, from: 12, to: 13, params: ['a'] }],
186
186
  ['∀a∈X1 a=a & debool(X1)=a', { code: RSErrorCode.localOutOfScopeParentheses, from: 23, to: 24, params: ['a'] }],
187
187
  ['∀ξ∈X1 ξ=ξ & ∀φ∈X1 φ=ξ', { code: RSErrorCode.localUndeclaredInSubexpr, from: 20, to: 21, params: ['ξ', 'X1'] }],
188
+ ['∀ξ∈X1 ξ=ξ & (ξ=ξ & ∀φ∈X1 φ=φ)', { code: RSErrorCode.localOutOfScopeParentheses, from: 13, to: 14, params: ['ξ'] }],
188
189
  ['D{t∈X1 | t=t} ∪ {t}', { code: RSErrorCode.localOutOfScope, from: 17, to: 18, params: ['t'] }],
189
190
  // Set predicates
190
191
  ['X1∈X1', { code: RSErrorCode.invalidElementPredicate, from: 0, to: 5, params: ['ℬ(X1)', '∈', 'ℬ(X1)'] }],
@@ -213,13 +214,22 @@ const errorData = [
213
214
  ['X1 ∪ ∅', { code: RSErrorCode.invalidEmptySetUsage, from: 5, to: 6 }],
214
215
  ['X1 ∪ S1', { code: RSErrorCode.typesNotEqual, from: 0, to: 7, params: ['ℬ(X1)', 'ℬ(X1×X1)', '∪'] }],
215
216
  ['S1 ∪ X1', { code: RSErrorCode.typesNotEqual, from: 0, to: 7, params: ['ℬ(X1×X1)', 'ℬ(X1)', '∪'] }],
216
- ['Pr1(X1)', { code: RSErrorCode.invalidProjectionSet, from: 4, to: 6, params: ['Pr1', 'ℬ(X1)'] }],
217
- ['Pr3(S1)', { code: RSErrorCode.invalidProjectionSet, from: 4, to: 6, params: ['Pr3', 'ℬ(X1×X1)'] }],
218
- ['Pr1,3(S1)', { code: RSErrorCode.invalidProjectionSet, from: 6, to: 8, params: ['Pr1,3', 'ℬ(X1×X1)'] }],
219
- ['Pr1((1,2))', { code: RSErrorCode.invalidProjectionSet, from: 4, to: 9, params: ['Z×Z'] }],
220
- ['pr1(X1)', { code: RSErrorCode.invalidProjectionTuple, from: 4, to: 6, params: ['pr1', 'ℬ(X1)'] }],
221
- ['pr1(debool(X1))', { code: RSErrorCode.invalidProjectionTuple, from: 4, to: 14, params: ['pr1', 'X1'] }],
222
- ['pr3(debool(S1))', { code: RSErrorCode.invalidProjectionTuple, from: 4, to: 14, params: ['pr3', 'X1×X1'] }],
217
+ ['Pr1(X1)', { code: RSErrorCode.projectionSetArgumentNotTupleSet, from: 4, to: 6, params: ['Pr1', 'ℬ(X1)'] }],
218
+ [
219
+ 'Pr3(S1)',
220
+ { code: RSErrorCode.projectionSetIndexOutOfRange, from: 4, to: 6, params: ['Pr3', '3', '2', 'ℬ(X1×X1)'] }
221
+ ],
222
+ [
223
+ 'Pr1,3(S1)',
224
+ { code: RSErrorCode.projectionSetIndexOutOfRange, from: 6, to: 8, params: ['Pr1,3', '3', '2', 'ℬ(X1×X1)'] }
225
+ ],
226
+ ['Pr1((1,2))', { code: RSErrorCode.projectionSetArgumentNotSet, from: 4, to: 9, params: ['Pr1', 'Z×Z'] }],
227
+ ['pr1(X1)', { code: RSErrorCode.projectionTupleArgumentNotTuple, from: 4, to: 6, params: ['pr1', 'ℬ(X1)'] }],
228
+ ['pr1(debool(X1))', { code: RSErrorCode.projectionTupleArgumentNotTuple, from: 4, to: 14, params: ['pr1', 'X1'] }],
229
+ [
230
+ 'pr3(debool(S1))',
231
+ { code: RSErrorCode.projectionTupleIndexOutOfRange, from: 4, to: 14, params: ['pr3', '3', '2', 'X1×X1'] }
232
+ ],
223
233
  [
224
234
  'Fi1[X1](ℬ(X1))',
225
235
  { code: RSErrorCode.invalidFilterArgumentType, from: 8, to: 13, params: ['Fi1', 'ℬℬ(X1)', 'ℬ(R0)'] }
@@ -673,18 +673,21 @@ export class TypeAuditor {
673
673
  }
674
674
 
675
675
  private visitProjectSet(node: AstNode): ExpressionType | null {
676
- const argument = this.childTypeDebool(node, 0, RSErrorCode.invalidProjectionSet);
677
- if (argument === null) {
676
+ const operator = labelRSLangNode(node);
677
+ const argNode = node.children[0];
678
+ const argType = this.childTypification(node, 0);
679
+ if (argType === null) {
678
680
  return null;
679
681
  }
680
- if (argument.typeID === TypeID.anyTypification) {
682
+ if (argType.typeID === TypeID.anyTypification) {
681
683
  return EmptySetT;
682
684
  }
685
+ if (argType.typeID !== TypeID.collection) {
686
+ return this.onError(RSErrorCode.projectionSetArgumentNotSet, argNode, [operator, labelType(argType)]);
687
+ }
688
+ const argument = debool(argType);
683
689
  if (argument.typeID !== TypeID.tuple) {
684
- return this.onError(RSErrorCode.invalidProjectionSet, node.children[0], [
685
- labelRSLangNode(node),
686
- labelType(bool(argument))
687
- ]);
690
+ return this.onError(RSErrorCode.projectionSetArgumentNotTupleSet, argNode, [operator, labelType(bool(argument))]);
688
691
  }
689
692
 
690
693
  const indices = getNodeIndices(node);
@@ -692,8 +695,10 @@ export class TypeAuditor {
692
695
  for (const index of indices) {
693
696
  const newComponent = component(argument, index);
694
697
  if (newComponent === null) {
695
- return this.onError(RSErrorCode.invalidProjectionSet, node.children[0], [
696
- labelRSLangNode(node),
698
+ return this.onError(RSErrorCode.projectionSetIndexOutOfRange, argNode, [
699
+ operator,
700
+ String(index),
701
+ String(argument.factors.length),
697
702
  labelType(bool(argument))
698
703
  ]);
699
704
  } else {
@@ -708,6 +713,8 @@ export class TypeAuditor {
708
713
  }
709
714
 
710
715
  private visitProjectTuple(node: AstNode): ExpressionType | null {
716
+ const operator = labelRSLangNode(node);
717
+ const argNode = node.children[0];
711
718
  const argument = this.childTypification(node, 0);
712
719
  if (argument === null) {
713
720
  return null;
@@ -716,10 +723,7 @@ export class TypeAuditor {
716
723
  return argument;
717
724
  }
718
725
  if (argument.typeID !== TypeID.tuple) {
719
- return this.onError(RSErrorCode.invalidProjectionTuple, node.children[0], [
720
- labelRSLangNode(node),
721
- labelType(argument)
722
- ]);
726
+ return this.onError(RSErrorCode.projectionTupleArgumentNotTuple, argNode, [operator, labelType(argument)]);
723
727
  }
724
728
 
725
729
  const indices = getNodeIndices(node);
@@ -727,8 +731,10 @@ export class TypeAuditor {
727
731
  for (const index of indices) {
728
732
  const newComponent = component(argument, index);
729
733
  if (newComponent === null) {
730
- return this.onError(RSErrorCode.invalidProjectionTuple, node.children[0], [
731
- labelRSLangNode(node),
734
+ return this.onError(RSErrorCode.projectionTupleIndexOutOfRange, argNode, [
735
+ operator,
736
+ String(index),
737
+ String(argument.factors.length),
732
738
  labelType(argument)
733
739
  ]);
734
740
  } else {
@@ -1080,14 +1086,17 @@ function isQuantifierNode(typeID: number): boolean {
1080
1086
  return typeID === TokenID.QUANTOR_UNIVERSAL || typeID === TokenID.QUANTOR_EXISTS;
1081
1087
  }
1082
1088
 
1083
- function subtreeContainsQuantifier(node: AstNode): boolean {
1084
- if (isQuantifierNode(node.typeID)) {
1085
- return true;
1086
- }
1087
- for (const child of node.children) {
1088
- if (subtreeContainsQuantifier(child)) {
1089
+ /** Quantifier on the path from `node` up through `stopAt` (inclusive). */
1090
+ function pathContainsQuantifier(node: AstNode, stopAt: AstNode): boolean {
1091
+ let current: AstNode | null = node;
1092
+ while (current !== null) {
1093
+ if (isQuantifierNode(current.typeID)) {
1089
1094
  return true;
1090
1095
  }
1096
+ if (current === stopAt) {
1097
+ return false;
1098
+ }
1099
+ current = current.parent;
1091
1100
  }
1092
1101
  return false;
1093
1102
  }
@@ -1167,7 +1176,7 @@ class LocalContext {
1167
1176
  } else if (local.level < 1) {
1168
1177
  const subexprRoot = findRightLogicOperandRoot(node);
1169
1178
  if (subexprRoot !== null) {
1170
- if (subtreeContainsQuantifier(subexprRoot)) {
1179
+ if (pathContainsQuantifier(node, subexprRoot)) {
1171
1180
  return this.onError(RSErrorCode.localUndeclaredInSubexpr, node, [alias, labelType(local.type)]);
1172
1181
  }
1173
1182
  return this.onError(RSErrorCode.localOutOfScopeParentheses, node, [alias]);