@rsconcept/domain 1.0.0

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 (224) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -0
  3. package/dist/cctext/index.d.ts +1 -0
  4. package/dist/cctext/index.js +42 -0
  5. package/dist/cctext/index.js.map +1 -0
  6. package/dist/cctext/language-api.d.ts +43 -0
  7. package/dist/cctext/language-api.js +252 -0
  8. package/dist/cctext/language-api.js.map +1 -0
  9. package/dist/cctext/language.d.ts +58 -0
  10. package/dist/cctext/language.js +44 -0
  11. package/dist/cctext/language.js.map +1 -0
  12. package/dist/graph/graph.d.ts +62 -0
  13. package/dist/graph/graph.js +385 -0
  14. package/dist/graph/graph.js.map +1 -0
  15. package/dist/graph/index.d.ts +1 -0
  16. package/dist/graph/index.js +384 -0
  17. package/dist/graph/index.js.map +1 -0
  18. package/dist/index.d.ts +28 -0
  19. package/dist/index.js +5851 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/library/folder-tree.d.ts +32 -0
  22. package/dist/library/folder-tree.js +136 -0
  23. package/dist/library/folder-tree.js.map +1 -0
  24. package/dist/library/index.d.ts +17 -0
  25. package/dist/library/index.js +2800 -0
  26. package/dist/library/index.js.map +1 -0
  27. package/dist/library/library-api.d.ts +6 -0
  28. package/dist/library/library-api.js +13 -0
  29. package/dist/library/library-api.js.map +1 -0
  30. package/dist/library/library.d.ts +56 -0
  31. package/dist/library/library.js +23 -0
  32. package/dist/library/library.js.map +1 -0
  33. package/dist/library/oss-api.d.ts +47 -0
  34. package/dist/library/oss-api.js +1105 -0
  35. package/dist/library/oss-api.js.map +1 -0
  36. package/dist/library/oss-layout-api.d.ts +36 -0
  37. package/dist/library/oss-layout-api.js +330 -0
  38. package/dist/library/oss-layout-api.js.map +1 -0
  39. package/dist/library/oss-layout.d.ts +25 -0
  40. package/dist/library/oss-layout.js +1 -0
  41. package/dist/library/oss-layout.js.map +1 -0
  42. package/dist/library/oss.d.ts +136 -0
  43. package/dist/library/oss.js +30 -0
  44. package/dist/library/oss.js.map +1 -0
  45. package/dist/library/rsengine.d.ts +116 -0
  46. package/dist/library/rsengine.js +2604 -0
  47. package/dist/library/rsengine.js.map +1 -0
  48. package/dist/library/rsform-api.d.ts +74 -0
  49. package/dist/library/rsform-api.js +879 -0
  50. package/dist/library/rsform-api.js.map +1 -0
  51. package/dist/library/rsform.d.ts +206 -0
  52. package/dist/library/rsform.js +32 -0
  53. package/dist/library/rsform.js.map +1 -0
  54. package/dist/library/rsmodel-api.d.ts +43 -0
  55. package/dist/library/rsmodel-api.js +836 -0
  56. package/dist/library/rsmodel-api.js.map +1 -0
  57. package/dist/library/rsmodel.d.ts +52 -0
  58. package/dist/library/rsmodel.js +25 -0
  59. package/dist/library/rsmodel.js.map +1 -0
  60. package/dist/library/structure-planner.d.ts +33 -0
  61. package/dist/library/structure-planner.js +481 -0
  62. package/dist/library/structure-planner.js.map +1 -0
  63. package/dist/parsing/ast.d.ts +49 -0
  64. package/dist/parsing/ast.js +93 -0
  65. package/dist/parsing/ast.js.map +1 -0
  66. package/dist/parsing/index.d.ts +3 -0
  67. package/dist/parsing/index.js +141 -0
  68. package/dist/parsing/index.js.map +1 -0
  69. package/dist/parsing/lezer-tree.d.ts +13 -0
  70. package/dist/parsing/lezer-tree.js +50 -0
  71. package/dist/parsing/lezer-tree.js.map +1 -0
  72. package/dist/rslang/api.d.ts +53 -0
  73. package/dist/rslang/api.js +846 -0
  74. package/dist/rslang/api.js.map +1 -0
  75. package/dist/rslang/ast-annotations.d.ts +18 -0
  76. package/dist/rslang/ast-annotations.js +56 -0
  77. package/dist/rslang/ast-annotations.js.map +1 -0
  78. package/dist/rslang/error.d.ts +85 -0
  79. package/dist/rslang/error.js +159 -0
  80. package/dist/rslang/error.js.map +1 -0
  81. package/dist/rslang/eval/calculator.d.ts +43 -0
  82. package/dist/rslang/eval/calculator.js +1639 -0
  83. package/dist/rslang/eval/calculator.js.map +1 -0
  84. package/dist/rslang/eval/evaluation-cache.d.ts +36 -0
  85. package/dist/rslang/eval/evaluation-cache.js +310 -0
  86. package/dist/rslang/eval/evaluation-cache.js.map +1 -0
  87. package/dist/rslang/eval/evaluator.d.ts +70 -0
  88. package/dist/rslang/eval/evaluator.js +1514 -0
  89. package/dist/rslang/eval/evaluator.js.map +1 -0
  90. package/dist/rslang/eval/value-api.d.ts +48 -0
  91. package/dist/rslang/eval/value-api.js +490 -0
  92. package/dist/rslang/eval/value-api.js.map +1 -0
  93. package/dist/rslang/eval/value.d.ts +36 -0
  94. package/dist/rslang/eval/value.js +118 -0
  95. package/dist/rslang/eval/value.js.map +1 -0
  96. package/dist/rslang/index.d.ts +17 -0
  97. package/dist/rslang/index.js +4314 -0
  98. package/dist/rslang/index.js.map +1 -0
  99. package/dist/rslang/labels.d.ts +16 -0
  100. package/dist/rslang/labels.js +315 -0
  101. package/dist/rslang/labels.js.map +1 -0
  102. package/dist/rslang/parser/expression-generator.d.ts +10 -0
  103. package/dist/rslang/parser/expression-generator.js +451 -0
  104. package/dist/rslang/parser/expression-generator.js.map +1 -0
  105. package/dist/rslang/parser/normalize.d.ts +11 -0
  106. package/dist/rslang/parser/normalize.js +507 -0
  107. package/dist/rslang/parser/normalize.js.map +1 -0
  108. package/dist/rslang/parser/parser.d.ts +5 -0
  109. package/dist/rslang/parser/parser.js +24 -0
  110. package/dist/rslang/parser/parser.js.map +1 -0
  111. package/dist/rslang/parser/parser.terms.d.ts +42 -0
  112. package/dist/rslang/parser/parser.terms.js +84 -0
  113. package/dist/rslang/parser/parser.terms.js.map +1 -0
  114. package/dist/rslang/parser/syntax-errors.d.ts +11 -0
  115. package/dist/rslang/parser/syntax-errors.js +403 -0
  116. package/dist/rslang/parser/syntax-errors.js.map +1 -0
  117. package/dist/rslang/parser/token.d.ts +79 -0
  118. package/dist/rslang/parser/token.js +95 -0
  119. package/dist/rslang/parser/token.js.map +1 -0
  120. package/dist/rslang/semantic/analyzer.d.ts +39 -0
  121. package/dist/rslang/semantic/analyzer.js +2604 -0
  122. package/dist/rslang/semantic/analyzer.js.map +1 -0
  123. package/dist/rslang/semantic/arguments-extractor.d.ts +42 -0
  124. package/dist/rslang/semantic/arguments-extractor.js +366 -0
  125. package/dist/rslang/semantic/arguments-extractor.js.map +1 -0
  126. package/dist/rslang/semantic/type-auditor.d.ts +73 -0
  127. package/dist/rslang/semantic/type-auditor.js +1570 -0
  128. package/dist/rslang/semantic/type-auditor.js.map +1 -0
  129. package/dist/rslang/semantic/typification-api.d.ts +27 -0
  130. package/dist/rslang/semantic/typification-api.js +320 -0
  131. package/dist/rslang/semantic/typification-api.js.map +1 -0
  132. package/dist/rslang/semantic/typification-parser.d.ts +12 -0
  133. package/dist/rslang/semantic/typification-parser.js +226 -0
  134. package/dist/rslang/semantic/typification-parser.js.map +1 -0
  135. package/dist/rslang/semantic/typification.d.ts +119 -0
  136. package/dist/rslang/semantic/typification.js +74 -0
  137. package/dist/rslang/semantic/typification.js.map +1 -0
  138. package/dist/rslang/semantic/value-auditor.d.ts +43 -0
  139. package/dist/rslang/semantic/value-auditor.js +523 -0
  140. package/dist/rslang/semantic/value-auditor.js.map +1 -0
  141. package/dist/rslang/semantic/value-class.d.ts +10 -0
  142. package/dist/rslang/semantic/value-class.js +9 -0
  143. package/dist/rslang/semantic/value-class.js.map +1 -0
  144. package/dist/rslang/typification-graph.d.ts +33 -0
  145. package/dist/rslang/typification-graph.js +311 -0
  146. package/dist/rslang/typification-graph.js.map +1 -0
  147. package/dist/shared/branded.d.ts +7 -0
  148. package/dist/shared/branded.js +1 -0
  149. package/dist/shared/branded.js.map +1 -0
  150. package/dist/shared/hash.d.ts +6 -0
  151. package/dist/shared/hash.js +18 -0
  152. package/dist/shared/hash.js.map +1 -0
  153. package/dist/shared/index.d.ts +2 -0
  154. package/dist/shared/index.js +18 -0
  155. package/dist/shared/index.js.map +1 -0
  156. package/package.json +184 -0
  157. package/src/cctext/index.ts +9 -0
  158. package/src/cctext/language-api.test.ts +149 -0
  159. package/src/cctext/language-api.ts +285 -0
  160. package/src/cctext/language.ts +80 -0
  161. package/src/graph/graph.test.ts +392 -0
  162. package/src/graph/graph.ts +433 -0
  163. package/src/graph/index.ts +1 -0
  164. package/src/index.ts +96 -0
  165. package/src/library/folder-tree.test.ts +47 -0
  166. package/src/library/folder-tree.ts +156 -0
  167. package/src/library/index.ts +46 -0
  168. package/src/library/library-api.test.ts +32 -0
  169. package/src/library/library-api.ts +11 -0
  170. package/src/library/library.ts +61 -0
  171. package/src/library/oss-api.ts +449 -0
  172. package/src/library/oss-layout-api.ts +377 -0
  173. package/src/library/oss-layout.ts +27 -0
  174. package/src/library/oss.ts +150 -0
  175. package/src/library/rsengine.ts +593 -0
  176. package/src/library/rsform-api.ts +533 -0
  177. package/src/library/rsform.ts +228 -0
  178. package/src/library/rsmodel-api.ts +340 -0
  179. package/src/library/rsmodel.ts +50 -0
  180. package/src/library/structure-planner.ts +143 -0
  181. package/src/parsing/ast.ts +136 -0
  182. package/src/parsing/index.ts +15 -0
  183. package/src/parsing/lezer-tree.ts +69 -0
  184. package/src/rslang/api.test.ts +116 -0
  185. package/src/rslang/api.ts +183 -0
  186. package/src/rslang/ast-annotations.ts +70 -0
  187. package/src/rslang/error.ts +129 -0
  188. package/src/rslang/eval/calculator.test.ts +124 -0
  189. package/src/rslang/eval/calculator.ts +121 -0
  190. package/src/rslang/eval/evaluation-cache.ts +257 -0
  191. package/src/rslang/eval/evaluator.test.ts +352 -0
  192. package/src/rslang/eval/evaluator.ts +935 -0
  193. package/src/rslang/eval/value-api.test.ts +105 -0
  194. package/src/rslang/eval/value-api.ts +444 -0
  195. package/src/rslang/eval/value.ts +102 -0
  196. package/src/rslang/index.ts +23 -0
  197. package/src/rslang/labels.ts +191 -0
  198. package/src/rslang/parser/expression-generator.test.ts +100 -0
  199. package/src/rslang/parser/expression-generator.ts +466 -0
  200. package/src/rslang/parser/normalize.test.ts +99 -0
  201. package/src/rslang/parser/normalize.ts +462 -0
  202. package/src/rslang/parser/parser.terms.ts +42 -0
  203. package/src/rslang/parser/parser.test.ts +153 -0
  204. package/src/rslang/parser/parser.ts +20 -0
  205. package/src/rslang/parser/rslang.grammar +251 -0
  206. package/src/rslang/parser/syntax-errors.ts +209 -0
  207. package/src/rslang/parser/token.ts +106 -0
  208. package/src/rslang/semantic/analyzer.test.ts +59 -0
  209. package/src/rslang/semantic/analyzer.ts +179 -0
  210. package/src/rslang/semantic/arguments-extractor.ts +327 -0
  211. package/src/rslang/semantic/type-auditor.test.ts +326 -0
  212. package/src/rslang/semantic/type-auditor.ts +1049 -0
  213. package/src/rslang/semantic/typification-api.test.ts +46 -0
  214. package/src/rslang/semantic/typification-api.ts +321 -0
  215. package/src/rslang/semantic/typification-parser.test.ts +50 -0
  216. package/src/rslang/semantic/typification-parser.ts +220 -0
  217. package/src/rslang/semantic/typification.ts +180 -0
  218. package/src/rslang/semantic/value-auditor.test.ts +206 -0
  219. package/src/rslang/semantic/value-auditor.ts +332 -0
  220. package/src/rslang/semantic/value-class.ts +11 -0
  221. package/src/rslang/typification-graph.ts +155 -0
  222. package/src/shared/branded.ts +6 -0
  223. package/src/shared/hash.ts +17 -0
  224. package/src/shared/index.ts +2 -0
@@ -0,0 +1,1049 @@
1
+ /**
2
+ * Module: Type auditor for AST type checking.
3
+ */
4
+
5
+ import { type AstNode, getNodeIndices, getNodeText } from '../../parsing';
6
+ import { annotateError, annotateType } from '../ast-annotations';
7
+ import { type ErrorReporter, RSErrorCode } from '../error';
8
+ import { labelRSLangNode, labelToken, labelType } from '../labels';
9
+ import { TokenID } from '../parser/token';
10
+
11
+ import {
12
+ type Argument,
13
+ bool,
14
+ component,
15
+ debool,
16
+ type EchelonTuple,
17
+ EmptySetT,
18
+ type ExpressionType,
19
+ IntegerT,
20
+ isRadical,
21
+ isTypification,
22
+ LogicT,
23
+ type Parametrized,
24
+ tuple,
25
+ type TypeContext,
26
+ TypeID,
27
+ type Typification
28
+ } from './typification';
29
+ import {
30
+ checkCompatibility,
31
+ checkEquality,
32
+ compareTemplated,
33
+ hasGenerics,
34
+ mergeTypifications,
35
+ substituteBase
36
+ } from './typification-api';
37
+
38
+ /** Type auditor for AST type checking. */
39
+ export class TypeAuditor {
40
+ private static readonly TYPE_DEDUCTION_DEPTH = 5;
41
+
42
+ private context: TypeContext;
43
+ private reporter?: ErrorReporter;
44
+ private locals: LocalContext;
45
+ private annotateTypes: boolean;
46
+ private annotateErrors: boolean;
47
+
48
+ constructor(context: TypeContext) {
49
+ this.annotateTypes = false;
50
+ this.annotateErrors = false;
51
+ this.context = context;
52
+ this.locals = new LocalContext(this.onError.bind(this));
53
+ }
54
+
55
+ public run(
56
+ ast: AstNode,
57
+ annotateTypes: boolean,
58
+ reporter?: ErrorReporter,
59
+ annotateErrors: boolean = false
60
+ ): ExpressionType | null {
61
+ if (ast.hasError) {
62
+ return null;
63
+ }
64
+ this.annotateTypes = annotateTypes;
65
+ this.annotateErrors = annotateErrors;
66
+ this.reporter = reporter;
67
+ this.clear();
68
+ return this.dispatchVisit(ast);
69
+ }
70
+
71
+ private clear(): void {
72
+ this.locals = new LocalContext(this.onError.bind(this));
73
+ }
74
+
75
+ private dispatchDeclare(node: AstNode, domain: Typification): boolean {
76
+ const result = this.processDeclare(node, domain);
77
+ if (result === true && this.annotateTypes) {
78
+ annotateType(node, domain);
79
+ }
80
+ return result;
81
+ }
82
+
83
+ private processDeclare(node: AstNode, domain: Typification): boolean {
84
+ switch (node.typeID) {
85
+ case TokenID.ID_LOCAL:
86
+ return this.declareLocal(node, domain);
87
+ case TokenID.NT_TUPLE_DECL:
88
+ return this.declareTuple(node, domain);
89
+ case TokenID.NT_ENUM_DECL:
90
+ return this.declareEnumeration(node, domain);
91
+ }
92
+ return false;
93
+ }
94
+
95
+ private declareLocal(node: AstNode, domain: Typification): boolean {
96
+ return this.locals.pushLocal(node, domain);
97
+ }
98
+
99
+ private declareTuple(node: AstNode, domain: Typification): boolean {
100
+ if (domain.typeID !== TypeID.tuple || domain.factors.length !== node.children.length) {
101
+ this.onError(RSErrorCode.invalidCortegeDeclare, node.children[0]);
102
+ return false;
103
+ }
104
+ for (let child = 0; child < node.children.length; child++) {
105
+ if (!this.visitChildDeclaration(node, child, component(domain, child + 1)!)) {
106
+ return false;
107
+ }
108
+ }
109
+ return true;
110
+ }
111
+
112
+ private declareEnumeration(node: AstNode, domain: Typification): boolean {
113
+ for (const child of node.children) {
114
+ if (!this.dispatchDeclare(child, domain)) {
115
+ return false;
116
+ }
117
+ }
118
+ return true;
119
+ }
120
+
121
+ private dispatchVisit(node: AstNode): ExpressionType | null {
122
+ const result = this.processVisit(node);
123
+ if (result !== null && this.annotateTypes) {
124
+ annotateType(node, result);
125
+ }
126
+ return result;
127
+ }
128
+
129
+ private processVisit(node: AstNode): ExpressionType | null {
130
+ switch (node.typeID) {
131
+ case TokenID.ID_GLOBAL:
132
+ case TokenID.ID_FUNCTION:
133
+ case TokenID.ID_PREDICATE:
134
+ return this.visitGlobal(node);
135
+
136
+ case TokenID.ID_LOCAL:
137
+ return this.visitLocal(node);
138
+ case TokenID.ID_RADICAL:
139
+ return this.visitRadical(node);
140
+
141
+ case TokenID.LIT_INTEGER:
142
+ return IntegerT;
143
+ case TokenID.LIT_WHOLE_NUMBERS:
144
+ return bool(IntegerT);
145
+ case TokenID.LIT_EMPTYSET:
146
+ return this.visitEmptySet(node);
147
+
148
+ case TokenID.NT_ARGUMENTS:
149
+ return this.visitArgumentsEnum(node);
150
+ case TokenID.NT_ARG_DECL:
151
+ return this.visitArgument(node);
152
+
153
+ case TokenID.PLUS:
154
+ case TokenID.MINUS:
155
+ case TokenID.MULTIPLY:
156
+ return this.visitArithmetic(node);
157
+
158
+ case TokenID.QUANTOR_UNIVERSAL:
159
+ case TokenID.QUANTOR_EXISTS:
160
+ return this.visitQuantifier(node);
161
+
162
+ case TokenID.LOGIC_NOT:
163
+ return this.visitNegation(node);
164
+
165
+ case TokenID.LOGIC_AND:
166
+ case TokenID.LOGIC_OR:
167
+ case TokenID.LOGIC_IMPLICATION:
168
+ case TokenID.LOGIC_EQUIVALENT:
169
+ return this.visitLogicBinary(node);
170
+
171
+ case TokenID.EQUAL:
172
+ case TokenID.NOTEQUAL:
173
+ return this.visitEquals(node);
174
+
175
+ case TokenID.GREATER:
176
+ case TokenID.LESSER:
177
+ case TokenID.GREATER_OR_EQ:
178
+ case TokenID.LESSER_OR_EQ:
179
+ return this.visitIntegerPredicate(node);
180
+
181
+ case TokenID.SET_IN:
182
+ case TokenID.SET_NOT_IN:
183
+ case TokenID.SUBSET:
184
+ case TokenID.SUBSET_OR_EQ:
185
+ case TokenID.NOT_SUBSET:
186
+ return this.visitSetexprPredicate(node);
187
+
188
+ case TokenID.DECART:
189
+ return this.visitDecart(node);
190
+ case TokenID.BOOLEAN:
191
+ return this.visitBoolean(node);
192
+
193
+ case TokenID.NT_TUPLE:
194
+ return this.visitTuple(node);
195
+ case TokenID.NT_ENUMERATION:
196
+ return this.visitEnumeration(node);
197
+
198
+ case TokenID.BIGPR:
199
+ return this.visitProjectSet(node);
200
+ case TokenID.SMALLPR:
201
+ return this.visitProjectTuple(node);
202
+ case TokenID.FILTER:
203
+ return this.visitFilter(node);
204
+
205
+ case TokenID.CARD:
206
+ return this.visitCard(node);
207
+ case TokenID.REDUCE:
208
+ return this.visitReduce(node);
209
+ case TokenID.BOOL:
210
+ return this.visitBool(node);
211
+ case TokenID.DEBOOL:
212
+ return this.visitDebool(node);
213
+
214
+ case TokenID.SET_UNION:
215
+ case TokenID.SET_INTERSECTION:
216
+ case TokenID.SET_MINUS:
217
+ case TokenID.SET_SYMMETRIC_MINUS:
218
+ return this.visitSetexprBinary(node);
219
+
220
+ case TokenID.NT_FUNC_DEFINITION:
221
+ return this.visitFunctionDefinition(node);
222
+
223
+ case TokenID.NT_FUNC_CALL:
224
+ return this.visitFunctionCall(node);
225
+
226
+ case TokenID.ITERATE:
227
+ return this.visitIterate(node);
228
+ case TokenID.ASSIGN:
229
+ return this.visitAssign(node);
230
+
231
+ case TokenID.NT_DECLARATIVE_EXPR:
232
+ return this.visitDeclarative(node);
233
+ case TokenID.NT_IMPERATIVE_EXPR:
234
+ return this.visitImperative(node);
235
+
236
+ case TokenID.NT_RECURSIVE_FULL:
237
+ case TokenID.NT_RECURSIVE_SHORT:
238
+ return this.visitRecursion(node);
239
+ }
240
+ return null;
241
+ }
242
+
243
+ private onError(code: RSErrorCode, node: AstNode, params?: string[]): null {
244
+ this.reporter?.({ code, from: node.from, to: node.to, params });
245
+ if (this.annotateErrors) {
246
+ annotateError(node, code, params);
247
+ }
248
+ return null;
249
+ }
250
+
251
+ private visitChild(node: AstNode, index: number): ExpressionType | null {
252
+ if (index >= node.children.length) {
253
+ return null;
254
+ }
255
+ return this.dispatchVisit(node.children[index]);
256
+ }
257
+
258
+ private visitChildDeclaration(node: AstNode, index: number, domain: Typification): boolean {
259
+ if (index >= node.children.length) {
260
+ return false;
261
+ }
262
+ if (!this.dispatchDeclare(node.children[index], domain)) {
263
+ return false;
264
+ }
265
+ return true;
266
+ }
267
+
268
+ private visitAllAndReturn(node: AstNode, type: ExpressionType | null): ExpressionType | null {
269
+ for (const child of node.children) {
270
+ if (!this.dispatchVisit(child)) {
271
+ return null;
272
+ }
273
+ }
274
+ return type;
275
+ }
276
+
277
+ private childTypification(node: AstNode, index: number): Typification | null {
278
+ const result = this.visitChild(node, index);
279
+ if (result === null) {
280
+ return null;
281
+ }
282
+ if (!isTypification(result)) {
283
+ this.onError(RSErrorCode.expectedSetexpr, node.children[index], [labelType(result)]);
284
+ return null;
285
+ }
286
+ return result as Typification;
287
+ }
288
+
289
+ private childLogic(node: AstNode, index: number): boolean {
290
+ const result = this.visitChild(node, index);
291
+ if (result === null) {
292
+ return false;
293
+ }
294
+ if (result.typeID !== TypeID.logic) {
295
+ this.onError(RSErrorCode.expectedLogic, node.children[index], [labelType(result)]);
296
+ return false;
297
+ }
298
+ return true;
299
+ }
300
+
301
+ private childTypeDebool(node: AstNode, index: number, errorCode: RSErrorCode): Typification | null {
302
+ const result = this.childTypification(node, index);
303
+ if (result === null) {
304
+ return null;
305
+ }
306
+ if (result.typeID === TypeID.anyTypification) {
307
+ return result;
308
+ }
309
+ if (result.typeID !== TypeID.collection) {
310
+ this.onError(errorCode, node.children[index], [labelType(result)]);
311
+ return null;
312
+ }
313
+ return debool(result);
314
+ }
315
+
316
+ private visitLocal(node: AstNode): ExpressionType | null {
317
+ return this.locals.getLocalType(node);
318
+ }
319
+
320
+ private visitGlobal(node: AstNode): ExpressionType | null {
321
+ const alias = getNodeText(node);
322
+ const type = this.context.get(alias);
323
+ if (!type) {
324
+ return this.onError(RSErrorCode.globalNotTyped, node, [alias]);
325
+ }
326
+ return type;
327
+ }
328
+
329
+ private visitFunctionDefinition(node: AstNode): ExpressionType | null {
330
+ this.locals.startScope();
331
+ if (!this.visitChild(node, 0)) {
332
+ return null;
333
+ }
334
+
335
+ const args: Argument[] = [];
336
+ for (const local of this.locals.data) {
337
+ if (local.level === 1) {
338
+ args.push({ alias: local.alias, type: local.type });
339
+ }
340
+ }
341
+
342
+ const result = this.visitChild(node, 1);
343
+ if (result === null || result.typeID === TypeID.function || result.typeID === TypeID.predicate) {
344
+ return null;
345
+ }
346
+ this.locals.endScope(node.children[1]);
347
+ if (result.typeID === TypeID.logic) {
348
+ return {
349
+ typeID: TypeID.predicate,
350
+ result: result,
351
+ args: args
352
+ };
353
+ } else {
354
+ return {
355
+ typeID: TypeID.function,
356
+ result: result,
357
+ args: args
358
+ };
359
+ }
360
+ }
361
+
362
+ private visitFunctionCall(node: AstNode): ExpressionType | null {
363
+ const funcName = getNodeText(node.children[0]);
364
+ const funcType = this.context.get(funcName);
365
+ if (funcType?.typeID !== TypeID.function && funcType?.typeID !== TypeID.predicate) {
366
+ return this.onError(RSErrorCode.globalNotTyped, node.children[0], [funcName]);
367
+ }
368
+ if (this.annotateTypes) {
369
+ annotateType(node.children[0], funcType);
370
+ }
371
+
372
+ const substitutes = this.checkFuncArguments(node, funcName, funcType);
373
+ if (substitutes === null) {
374
+ return null;
375
+ }
376
+ if (funcType.result.typeID === TypeID.logic) {
377
+ return funcType.result;
378
+ } else {
379
+ const result = mangleRadicals(funcName, funcType.result);
380
+ if (substitutes.size > 0) {
381
+ substituteBase(result, substitutes);
382
+ }
383
+ return result;
384
+ }
385
+ }
386
+
387
+ private visitRadical(node: AstNode): ExpressionType | null {
388
+ const alias = getNodeText(node);
389
+ if (!this.isInsideFuncArgument(node)) {
390
+ return this.onError(RSErrorCode.radicalUsage, node, [alias]);
391
+ }
392
+ return bool({ typeID: TypeID.basic, baseID: alias });
393
+ }
394
+
395
+ private visitEmptySet(node: AstNode): ExpressionType | null {
396
+ const invalidParents: TokenID[] = [
397
+ TokenID.CARD,
398
+ TokenID.DEBOOL,
399
+ TokenID.SET_UNION,
400
+ TokenID.SET_INTERSECTION,
401
+ TokenID.SET_MINUS,
402
+ TokenID.SET_SYMMETRIC_MINUS,
403
+ TokenID.REDUCE,
404
+ TokenID.BIGPR,
405
+ TokenID.SMALLPR
406
+ ];
407
+ if (invalidParents.includes(node.parent?.typeID as TokenID)) {
408
+ return this.onError(RSErrorCode.invalidEmptySetUsage, node);
409
+ }
410
+ return EmptySetT;
411
+ }
412
+
413
+ private visitArgument(node: AstNode): ExpressionType | null {
414
+ const variable = node.children[0];
415
+ if (variable.typeID === TokenID.NT_TUPLE_DECL) {
416
+ return this.onError(RSErrorCode.invalidArgumentCortegeDeclare, variable);
417
+ }
418
+ const domain = this.childTypeDebool(node, 1, RSErrorCode.invalidTypeOperation);
419
+ if (domain === null) {
420
+ return null;
421
+ }
422
+ if (!this.visitChildDeclaration(node, 0, domain)) {
423
+ return null;
424
+ }
425
+ return domain;
426
+ }
427
+
428
+ private visitCard(node: AstNode): ExpressionType | null {
429
+ if (!this.childTypeDebool(node, 0, RSErrorCode.invalidCard)) {
430
+ return null;
431
+ }
432
+ return IntegerT;
433
+ }
434
+
435
+ private visitArithmetic(node: AstNode): ExpressionType | null {
436
+ const type1 = this.childTypification(node, 0);
437
+ if (type1 === null) {
438
+ return null;
439
+ }
440
+ if (!('isArithmetic' in type1 && type1.isArithmetic)) {
441
+ return this.onError(RSErrorCode.arithmeticNotSupported, node.children[0], [labelType(type1)]);
442
+ }
443
+
444
+ const type2 = this.childTypification(node, 1);
445
+ if (type2 === null) {
446
+ return null;
447
+ }
448
+ if (!('isArithmetic' in type2 && type2.isArithmetic)) {
449
+ return this.onError(RSErrorCode.arithmeticNotSupported, node.children[1], [labelType(type2)]);
450
+ }
451
+
452
+ const result = mergeTypifications(type1, type2);
453
+ if (result === null) {
454
+ return this.onError(RSErrorCode.typesNotCompatible, node, [labelType(type1), labelType(type2)]);
455
+ }
456
+ return result;
457
+ }
458
+
459
+ private visitIntegerPredicate(node: AstNode): ExpressionType | null {
460
+ const type1 = this.childTypification(node, 0);
461
+ if (type1 === null) {
462
+ return null;
463
+ }
464
+ if (!('isOrdered' in type1 && type1.isOrdered)) {
465
+ return this.onError(RSErrorCode.orderingNotSupported, node.children[0], [labelType(type1)]);
466
+ }
467
+
468
+ const type2 = this.childTypification(node, 1);
469
+ if (type2 === null) {
470
+ return null;
471
+ }
472
+ if (!('isOrdered' in type2 && type2.isOrdered)) {
473
+ return this.onError(RSErrorCode.orderingNotSupported, node.children[1], [labelType(type2)]);
474
+ }
475
+
476
+ if (!checkCompatibility(type1, type2)) {
477
+ return this.onError(RSErrorCode.typesNotCompatible, node, [labelType(type1), labelType(type2)]);
478
+ }
479
+ return LogicT;
480
+ }
481
+
482
+ private visitQuantifier(node: AstNode): ExpressionType | null {
483
+ this.locals.startScope();
484
+
485
+ const domain = this.childTypeDebool(node, 1, RSErrorCode.invalidTypeOperation);
486
+ if (domain === null) {
487
+ return null;
488
+ } else if (!this.visitChildDeclaration(node, 0, domain)) {
489
+ return null;
490
+ } else if (!this.childLogic(node, 2)) {
491
+ return null;
492
+ }
493
+
494
+ this.locals.endScope(node.children[2]);
495
+ return LogicT;
496
+ }
497
+
498
+ private visitNegation(node: AstNode): ExpressionType | null {
499
+ if (!this.childLogic(node, 0)) {
500
+ return null;
501
+ }
502
+ return LogicT;
503
+ }
504
+
505
+ private visitLogicBinary(node: AstNode): ExpressionType | null {
506
+ if (!this.childLogic(node, 0) || !this.childLogic(node, 1)) {
507
+ return null;
508
+ }
509
+ return LogicT;
510
+ }
511
+
512
+ private visitEquals(node: AstNode): ExpressionType | null {
513
+ const type1 = this.childTypification(node, 0);
514
+ if (type1 === null) {
515
+ return null;
516
+ }
517
+
518
+ const type2 = this.childTypification(node, 1);
519
+ if (type2 === null) {
520
+ return null;
521
+ }
522
+ if (!checkCompatibility(type1, type2)) {
523
+ return this.onError(RSErrorCode.typesNotCompatible, node, [labelType(type1), labelType(type2)]);
524
+ }
525
+ return LogicT;
526
+ }
527
+
528
+ private visitSetexprPredicate(node: AstNode): ExpressionType | null {
529
+ let type2 = this.childTypeDebool(node, 1, RSErrorCode.invalidTypeOperation);
530
+ if (type2 === null) {
531
+ return null;
532
+ }
533
+ const isSubset = this.isSubset(node.typeID as TokenID);
534
+ if (isSubset) {
535
+ type2 = bool(type2);
536
+ }
537
+ const type1 = this.childTypification(node, 0);
538
+ if (type1 === null) {
539
+ return null;
540
+ }
541
+
542
+ if (!checkCompatibility(type1, type2)) {
543
+ if (isSubset) {
544
+ return this.onError(RSErrorCode.typesNotEqual, node, [labelType(type1), labelType(type2)]);
545
+ } else {
546
+ return this.onError(RSErrorCode.invalidElementPredicate, node, [
547
+ labelType(type1),
548
+ labelToken(node.typeID as TokenID),
549
+ labelType(bool(type2))
550
+ ]);
551
+ }
552
+ }
553
+ return LogicT;
554
+ }
555
+
556
+ private visitDecart(node: AstNode): ExpressionType | null {
557
+ const factors: Typification[] = [];
558
+ for (let child = 0; child < node.children.length; child++) {
559
+ const type = this.childTypeDebool(node, child, RSErrorCode.invalidDecart);
560
+ if (type === null) {
561
+ return null;
562
+ } else {
563
+ factors.push(type);
564
+ }
565
+ }
566
+ return bool(tuple(factors));
567
+ }
568
+
569
+ private visitBoolean(node: AstNode): ExpressionType | null {
570
+ const type = this.childTypeDebool(node, 0, RSErrorCode.invalidBoolean);
571
+ if (type === null) {
572
+ return null;
573
+ }
574
+ return bool(bool(type));
575
+ }
576
+
577
+ private visitTuple(node: AstNode): ExpressionType | null {
578
+ const components: Typification[] = [];
579
+ for (let child = 0; child < node.children.length; child++) {
580
+ const type = this.childTypification(node, child);
581
+ if (type === null) {
582
+ return null;
583
+ }
584
+ components.push(type);
585
+ }
586
+ return tuple(components);
587
+ }
588
+
589
+ private visitEnumeration(node: AstNode): ExpressionType | null {
590
+ let type: ExpressionType | null = this.childTypification(node, 0);
591
+ if (type === null) {
592
+ return null;
593
+ }
594
+ for (let child = 1; child < node.children.length; child++) {
595
+ const childType = this.childTypification(node, child);
596
+ if (childType === null) {
597
+ return null;
598
+ }
599
+
600
+ const merge = mergeTypifications(type, childType);
601
+ if (merge === null) {
602
+ return this.onError(RSErrorCode.invalidEnumeration, node.children[child], [
603
+ labelType(type),
604
+ labelType(childType)
605
+ ]);
606
+ }
607
+ type = merge;
608
+ }
609
+ return bool(type);
610
+ }
611
+
612
+ private visitBool(node: AstNode): ExpressionType | null {
613
+ return this.visitEnumeration(node);
614
+ }
615
+
616
+ private visitDebool(node: AstNode): ExpressionType | null {
617
+ return this.childTypeDebool(node, 0, RSErrorCode.invalidDebool);
618
+ }
619
+
620
+ private visitSetexprBinary(node: AstNode): ExpressionType | null {
621
+ const type1 = this.childTypeDebool(node, 0, RSErrorCode.invalidTypeOperation);
622
+ if (type1 === null) {
623
+ return null;
624
+ }
625
+
626
+ const type2 = this.childTypeDebool(node, 1, RSErrorCode.invalidTypeOperation);
627
+ if (type2 === null) {
628
+ return null;
629
+ }
630
+
631
+ const result = mergeTypifications(type1, type2);
632
+ if (result === null) {
633
+ return this.onError(RSErrorCode.typesNotEqual, node, [labelType(bool(type1)), labelType(bool(type2))]);
634
+ }
635
+ return bool(result);
636
+ }
637
+
638
+ private visitProjectSet(node: AstNode): ExpressionType | null {
639
+ const argument = this.childTypeDebool(node, 0, RSErrorCode.invalidProjectionSet);
640
+ if (argument === null) {
641
+ return null;
642
+ }
643
+ if (argument.typeID === TypeID.anyTypification) {
644
+ return EmptySetT;
645
+ }
646
+ if (argument.typeID !== TypeID.tuple) {
647
+ return this.onError(RSErrorCode.invalidProjectionSet, node.children[0], [
648
+ labelRSLangNode(node),
649
+ labelType(bool(argument))
650
+ ]);
651
+ }
652
+
653
+ const indices = getNodeIndices(node);
654
+ const components: Typification[] = [];
655
+ for (const index of indices) {
656
+ const newComponent = component(argument, index);
657
+ if (newComponent === null) {
658
+ return this.onError(RSErrorCode.invalidProjectionSet, node.children[0], [
659
+ labelRSLangNode(node),
660
+ labelType(bool(argument))
661
+ ]);
662
+ } else {
663
+ components.push(newComponent);
664
+ }
665
+ }
666
+ if (components.length === 1) {
667
+ return bool(components[0]);
668
+ } else {
669
+ return bool(tuple(components));
670
+ }
671
+ }
672
+
673
+ private visitProjectTuple(node: AstNode): ExpressionType | null {
674
+ const argument = this.childTypification(node, 0);
675
+ if (argument === null) {
676
+ return null;
677
+ }
678
+ if (argument.typeID === TypeID.anyTypification) {
679
+ return argument;
680
+ }
681
+ if (argument.typeID !== TypeID.tuple) {
682
+ return this.onError(RSErrorCode.invalidProjectionTuple, node.children[0], [
683
+ labelRSLangNode(node),
684
+ labelType(argument)
685
+ ]);
686
+ }
687
+
688
+ const indices = getNodeIndices(node);
689
+ const components: Typification[] = [];
690
+ for (const index of indices) {
691
+ const newComponent = component(argument, index);
692
+ if (newComponent === null) {
693
+ return this.onError(RSErrorCode.invalidProjectionTuple, node.children[0], [
694
+ labelRSLangNode(node),
695
+ labelType(argument)
696
+ ]);
697
+ } else {
698
+ components.push(newComponent);
699
+ }
700
+ }
701
+ if (components.length === 1) {
702
+ return components[0];
703
+ } else {
704
+ return tuple(components);
705
+ }
706
+ }
707
+
708
+ private visitFilter(node: AstNode): ExpressionType | null {
709
+ const indices = getNodeIndices(node);
710
+ const tupleParam = indices.length === node.children.length - 1;
711
+ if (!tupleParam && node.children.length > 2) {
712
+ return this.onError(RSErrorCode.invalidFilterArity, node);
713
+ }
714
+
715
+ const argument = this.childTypification(node, node.children.length - 1);
716
+ if (argument === null) {
717
+ return null;
718
+ }
719
+ if (
720
+ argument.typeID === TypeID.anyTypification ||
721
+ (argument.typeID === TypeID.collection && argument.base.typeID === TypeID.anyTypification)
722
+ ) {
723
+ return EmptySetT;
724
+ }
725
+ if (argument.typeID !== TypeID.collection || argument.base.typeID !== TypeID.tuple) {
726
+ return this.onError(RSErrorCode.invalidFilterArgumentType, node.children[node.children.length - 1], [
727
+ labelRSLangNode(node),
728
+ labelType(argument)
729
+ ]);
730
+ }
731
+
732
+ const argBase = debool(argument) as EchelonTuple;
733
+ const bases: Typification[] = [];
734
+ for (const index of indices) {
735
+ const newBase = component(argBase, index);
736
+ if (newBase === null) {
737
+ return this.onError(RSErrorCode.invalidFilterArgumentType, node.children[node.children.length - 1], [
738
+ labelRSLangNode(node),
739
+ labelType(argument)
740
+ ]);
741
+ }
742
+ bases.push(newBase);
743
+ }
744
+
745
+ if (tupleParam) {
746
+ for (let child = 0; child + 1 < node.children.length; child++) {
747
+ const param = this.childTypification(node, child);
748
+ if (param === null) {
749
+ return null;
750
+ }
751
+ if (param.typeID !== TypeID.collection || !checkCompatibility(bases[child], debool(param))) {
752
+ return this.onError(RSErrorCode.typesNotEqual, node.children[child], [
753
+ labelType(param),
754
+ labelType(bool(bases[child]))
755
+ ]);
756
+ }
757
+ }
758
+ } else {
759
+ const param = this.childTypification(node, 0);
760
+ if (param === null) {
761
+ return null;
762
+ }
763
+ const paramType = param;
764
+ const expected = bool(tuple(bases));
765
+ if (paramType.typeID !== TypeID.collection || !checkCompatibility(expected, paramType)) {
766
+ return this.onError(RSErrorCode.typesNotEqual, node.children[0], [labelType(paramType), labelType(expected)]);
767
+ }
768
+ }
769
+ return argument;
770
+ }
771
+
772
+ private visitReduce(node: AstNode): ExpressionType | null {
773
+ const argument = this.childTypification(node, 0);
774
+ if (argument === null) {
775
+ return null;
776
+ }
777
+ if (
778
+ argument.typeID === TypeID.anyTypification ||
779
+ (argument.typeID === TypeID.collection && argument.base.typeID === TypeID.anyTypification)
780
+ ) {
781
+ return EmptySetT;
782
+ }
783
+ if (argument.typeID !== TypeID.collection || argument.base.typeID !== TypeID.collection) {
784
+ return this.onError(RSErrorCode.invalidReduce, node.children[0], [labelType(argument)]);
785
+ }
786
+ return debool(argument);
787
+ }
788
+
789
+ private visitArgumentsEnum(node: AstNode): ExpressionType | null {
790
+ return this.visitAllAndReturn(node, LogicT);
791
+ }
792
+
793
+ private visitDeclarative(node: AstNode): ExpressionType | null {
794
+ this.locals.startScope();
795
+
796
+ const domain = this.childTypeDebool(node, 1, RSErrorCode.invalidTypeOperation);
797
+ if (domain === null) {
798
+ return null;
799
+ } else if (!this.visitChildDeclaration(node, 0, domain)) {
800
+ return null;
801
+ } else if (!this.childLogic(node, 2)) {
802
+ return null;
803
+ }
804
+
805
+ this.locals.endScope(node.children[2]);
806
+ return bool(domain);
807
+ }
808
+
809
+ private visitImperative(node: AstNode): ExpressionType | null {
810
+ this.locals.startScope();
811
+
812
+ for (let child = 1; child < node.children.length; child++) {
813
+ if (!this.childLogic(node, child)) {
814
+ return null;
815
+ }
816
+ }
817
+
818
+ const type = this.childTypification(node, 0);
819
+ if (type === null) {
820
+ return null;
821
+ }
822
+
823
+ this.locals.endScope(node);
824
+ return bool(type);
825
+ }
826
+
827
+ private visitIterate(node: AstNode): ExpressionType | null {
828
+ const domain = this.childTypeDebool(node, 1, RSErrorCode.invalidTypeOperation);
829
+ if (domain === null) {
830
+ return null;
831
+ }
832
+ if (!this.visitChildDeclaration(node, 0, domain)) {
833
+ return null;
834
+ }
835
+ return LogicT;
836
+ }
837
+
838
+ private visitAssign(node: AstNode): ExpressionType | null {
839
+ const domain = this.childTypification(node, 1);
840
+ if (domain === null) {
841
+ return null;
842
+ }
843
+ if (!this.visitChildDeclaration(node, 0, domain)) {
844
+ return null;
845
+ }
846
+ return LogicT;
847
+ }
848
+
849
+ private visitRecursion(node: AstNode): ExpressionType | null {
850
+ this.locals.startScope();
851
+
852
+ const initType = this.childTypification(node, 1);
853
+ if (initType === null) {
854
+ return null;
855
+ }
856
+ if (!this.visitChildDeclaration(node, 0, initType)) {
857
+ return null;
858
+ }
859
+
860
+ const isFull = node.typeID === TokenID.NT_RECURSIVE_FULL;
861
+ const iterationIndex = isFull ? 3 : 2;
862
+
863
+ let iterationValue = this.childTypification(node, iterationIndex);
864
+ if (iterationValue === null) {
865
+ return null;
866
+ }
867
+ if (!checkCompatibility(iterationValue, initType)) {
868
+ return this.onError(RSErrorCode.typesNotEqual, node.children[iterationIndex], [
869
+ labelType(iterationValue),
870
+ labelType(initType)
871
+ ]);
872
+ }
873
+
874
+ if (hasGenerics(initType)) {
875
+ for (let retries = TypeAuditor.TYPE_DEDUCTION_DEPTH; retries > 0; retries--) {
876
+ this.locals.endScope(node);
877
+ this.locals.clearUnused();
878
+ this.locals.startScope();
879
+ if (!this.visitChildDeclaration(node, 0, iterationValue)) {
880
+ return null;
881
+ }
882
+ const newIteration = this.childTypification(node, iterationIndex);
883
+ if (newIteration === null) {
884
+ return null;
885
+ }
886
+ if (checkEquality(newIteration, iterationValue)) {
887
+ break;
888
+ }
889
+ iterationValue = newIteration;
890
+ }
891
+ }
892
+
893
+ if (isFull) {
894
+ if (!this.childLogic(node, 2)) {
895
+ return null;
896
+ }
897
+ }
898
+
899
+ this.locals.endScope(node);
900
+ return iterationValue;
901
+ }
902
+
903
+ private isSubset(token: TokenID): boolean {
904
+ return token === TokenID.SUBSET || token === TokenID.SUBSET_OR_EQ || token === TokenID.NOT_SUBSET;
905
+ }
906
+
907
+ private isInsideFuncArgument(node: AstNode): boolean {
908
+ while (node.parent && node.parent !== node) {
909
+ if (node.typeID === TokenID.NT_ARGUMENTS) return true;
910
+ node = node.parent;
911
+ }
912
+ return false;
913
+ }
914
+
915
+ private checkFuncArguments(node: AstNode, alias: string, type: Parametrized): Map<string, Typification> | null {
916
+ if (node.children.length - 1 !== type.args.length) {
917
+ return this.onError(RSErrorCode.invalidArgsArity, node.children[1], [
918
+ String(type.args.length),
919
+ String(node.children.length - 1)
920
+ ]);
921
+ }
922
+
923
+ const substitutes = new Map<string, Typification>();
924
+ for (let child = 1; child < node.children.length; child++) {
925
+ const childType = this.childTypification(node, child);
926
+ if (childType === null) {
927
+ return null;
928
+ }
929
+ const argType = mangleRadicals(alias, type.args[child - 1].type);
930
+ if (!compareTemplated(substitutes, argType, childType)) {
931
+ this.onError(RSErrorCode.invalidArgumentType, node.children[child], [
932
+ `${type.args[child - 1].alias}∈${labelType(argType)}`,
933
+ labelType(childType)
934
+ ]);
935
+ return null;
936
+ }
937
+ }
938
+ return substitutes;
939
+ }
940
+ }
941
+
942
+ // ========= Internals ========
943
+ function mangleRadicals(funcName: string, type: Typification): Typification {
944
+ switch (type.typeID) {
945
+ default:
946
+ throw new Error(`Unexpected type: ${type.typeID}`);
947
+ case TypeID.integer:
948
+ return type;
949
+ case TypeID.basic: {
950
+ if (isRadical(type.baseID)) {
951
+ return {
952
+ typeID: type.typeID,
953
+ baseID: type.baseID + funcName
954
+ };
955
+ }
956
+ return type;
957
+ }
958
+ case TypeID.collection: {
959
+ return {
960
+ typeID: type.typeID,
961
+ base: mangleRadicals(funcName, debool(type))
962
+ };
963
+ }
964
+ case TypeID.tuple: {
965
+ const factors: Typification[] = [];
966
+ for (let index = 1; index <= type.factors.length; ++index) {
967
+ factors.push(mangleRadicals(funcName, component(type, index)!));
968
+ }
969
+ return {
970
+ typeID: type.typeID,
971
+ factors: factors
972
+ };
973
+ }
974
+ }
975
+ }
976
+
977
+ /** Local variable data. */
978
+ interface LocalData {
979
+ alias: string;
980
+ level: number;
981
+ useCount: number;
982
+ type: Typification;
983
+ }
984
+
985
+ /** Local variables context. */
986
+ class LocalContext {
987
+ private onError: (code: RSErrorCode, node: AstNode, params: string[]) => null;
988
+
989
+ public data: LocalData[] = [];
990
+
991
+ constructor(onError: (code: RSErrorCode, node: AstNode, params: string[]) => null) {
992
+ this.onError = onError;
993
+ }
994
+
995
+ startScope(): void {
996
+ for (const local of this.data) {
997
+ if (local.level > 0) {
998
+ local.level = local.level + 1;
999
+ }
1000
+ }
1001
+ }
1002
+
1003
+ endScope(node: AstNode, skipUnused: boolean = false): void {
1004
+ for (const local of this.data) {
1005
+ local.level--;
1006
+ if (!skipUnused && local.level === 0 && local.useCount === 0) {
1007
+ this.onError(RSErrorCode.localNotUsed, node, [local.alias]);
1008
+ }
1009
+ }
1010
+ }
1011
+
1012
+ clearUnused(): void {
1013
+ this.data = this.data.filter(data => data.level > 0);
1014
+ }
1015
+
1016
+ pushLocal(node: AstNode, type: Typification): boolean {
1017
+ const alias = getNodeText(node);
1018
+ const existing = this.data.find(data => data.alias === alias);
1019
+ if (existing) {
1020
+ if (existing.level > 0) {
1021
+ this.onError(RSErrorCode.localShadowing, node, [alias]);
1022
+ return false;
1023
+ } else {
1024
+ this.onError(RSErrorCode.localDoubleDeclare, node, [alias]);
1025
+ const index = this.data.indexOf(existing);
1026
+ if (index !== -1) {
1027
+ this.data.splice(index, 1);
1028
+ }
1029
+ }
1030
+ }
1031
+ this.data.push({ alias, type, level: 1, useCount: 0 });
1032
+ return true;
1033
+ }
1034
+
1035
+ getLocalType(node: AstNode): Typification | null {
1036
+ const alias = getNodeText(node);
1037
+ const local = this.data.find(data => data.alias === alias);
1038
+ if (local === undefined) {
1039
+ this.onError(RSErrorCode.localUndeclared, node, [alias]);
1040
+ return null;
1041
+ } else if (local.level < 1) {
1042
+ this.onError(RSErrorCode.localOutOfScope, node, [alias]);
1043
+ return null;
1044
+ } else {
1045
+ local.useCount++;
1046
+ return local.type;
1047
+ }
1048
+ }
1049
+ }