@rsconcept/domain 1.6.1 → 1.7.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 (50) hide show
  1. package/dist/{analyzer-camCAEkQ.d.ts → analyzer-QxWs73gP.d.ts} +2 -2
  2. package/dist/{ast-annotations-DiXmFraa.d.ts → ast-annotations-CtF7C9uK.d.ts} +2 -2
  3. package/dist/{calculator-BuVooqpb.d.ts → calculator-REeX2ZS1.d.ts} +2 -2
  4. package/dist/{error-CZXO5Y6a.d.ts → error-toW0TUHF.d.ts} +10 -1
  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.d.ts +1 -1
  9. package/dist/rslang/ast-annotations.d.ts +1 -1
  10. package/dist/rslang/error.d.ts +1 -1
  11. package/dist/rslang/error.js +9 -0
  12. package/dist/rslang/error.js.map +1 -1
  13. package/dist/rslang/eval/calculator.d.ts +1 -1
  14. package/dist/rslang/eval/evaluator.d.ts +1 -1
  15. package/dist/rslang/index.d.ts +4 -4
  16. package/dist/rslang/labels.d.ts +1 -1
  17. package/dist/rslang/labels.js +1 -1
  18. package/dist/rslang/labels.js.map +1 -1
  19. package/dist/rslang/parser/normalize.js +5 -1
  20. package/dist/rslang/parser/normalize.js.map +1 -1
  21. package/dist/rslang/parser/syntax-errors/index.d.ts +22 -0
  22. package/dist/rslang/parser/syntax-errors/index.js +2 -0
  23. package/dist/rslang/semantic/analyzer.d.ts +1 -1
  24. package/dist/rslang/semantic/analyzer.js +1 -1
  25. package/dist/rslang/semantic/type-auditor.d.ts +1 -1
  26. package/dist/rslang/semantic/value-auditor.d.ts +1 -1
  27. package/dist/syntax-errors-pCI6RicF.js +689 -0
  28. package/dist/syntax-errors-pCI6RicF.js.map +1 -0
  29. package/package.json +1 -1
  30. package/src/rslang/error.ts +9 -0
  31. package/src/rslang/labels.ts +1 -1
  32. package/src/rslang/parser/README.md +73 -0
  33. package/src/rslang/parser/normalize.ts +5 -1
  34. package/src/rslang/parser/parser.test.ts +92 -6
  35. package/src/rslang/parser/syntax-errors/README.md +61 -0
  36. package/src/rslang/parser/syntax-errors/ast-utils.ts +112 -0
  37. package/src/rslang/parser/syntax-errors/classify.ts +70 -0
  38. package/src/rslang/parser/syntax-errors/context.ts +25 -0
  39. package/src/rslang/parser/syntax-errors/error-builders.ts +28 -0
  40. package/src/rslang/parser/syntax-errors/expression-errors.ts +237 -0
  41. package/src/rslang/parser/syntax-errors/function-calls.ts +76 -0
  42. package/src/rslang/parser/syntax-errors/generators.ts +307 -0
  43. package/src/rslang/parser/syntax-errors/incomplete-formal.ts +76 -0
  44. package/src/rslang/parser/syntax-errors/incomplete-operands.ts +184 -0
  45. package/src/rslang/parser/syntax-errors/index.ts +102 -0
  46. package/src/rslang/parser/syntax-errors/trailing-slot.ts +45 -0
  47. package/dist/rslang/parser/syntax-errors.d.ts +0 -14
  48. package/dist/rslang/parser/syntax-errors.js +0 -311
  49. package/dist/rslang/parser/syntax-errors.js.map +0 -1
  50. package/src/rslang/parser/syntax-errors.ts +0 -511
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Incomplete formal headers: `[α∈…]` declarations and quantifiers (`∀α∈…`).
3
+ */
4
+
5
+ import type { AstNode } from '../../../parsing';
6
+ import { RSErrorCode, type RSErrorDescription } from '../../error';
7
+ import { TypeClass as TypeClassEnum } from '../../semantic/typification';
8
+ import { Arguments, Variable_pack } from '../parser.terms';
9
+ import { TokenID } from '../token';
10
+
11
+ import { findAncestor, isFunctionDeclNode, isQuantifierNode, subtreeHasError } from './ast-utils';
12
+ import type { ClassifyContext, ErrorClassifier } from './context';
13
+ import { detectTrailingErrorSlot } from './trailing-slot';
14
+
15
+ export const classifyIncompleteFormal: ErrorClassifier = (node, expression, context) =>
16
+ detectIncompleteFunctionDecl(node, expression, context.expected) ?? detectIncompleteQuantifier(node, expression);
17
+
18
+ function detectIncompleteFunctionDecl(
19
+ node: AstNode,
20
+ expression: string,
21
+ expected?: ClassifyContext['expected']
22
+ ): RSErrorDescription | null {
23
+ return detectTrailingErrorSlot(node, expression, findAncestor(node, isFunctionDeclNode), {
24
+ headerValid: container => {
25
+ const argsNode = container.children.find(
26
+ child => child.typeID === Arguments || child.typeID === TokenID.NT_ARGUMENTS
27
+ );
28
+ return argsNode !== undefined && !subtreeHasError(argsNode);
29
+ },
30
+ code: () => incompleteFormalExpressionCode(expected)
31
+ });
32
+ }
33
+
34
+ function incompleteFormalExpressionCode(expected?: ClassifyContext['expected']): RSErrorCode {
35
+ switch (expected) {
36
+ case TypeClassEnum.function:
37
+ return RSErrorCode.expectedExpressionBody;
38
+ case TypeClassEnum.predicate:
39
+ case TypeClassEnum.logic:
40
+ return RSErrorCode.expectedLogicBody;
41
+ default:
42
+ return RSErrorCode.expectedFunctionBody;
43
+ }
44
+ }
45
+
46
+ function detectIncompleteQuantifier(node: AstNode, expression: string): RSErrorDescription | null {
47
+ return detectTrailingErrorSlot(node, expression, findAncestor(node, isQuantifierNode), {
48
+ headerValid: container => {
49
+ const varPack = container.children.find(
50
+ child => child.typeID === Variable_pack || child.typeID === TokenID.NT_ENUM_DECL
51
+ );
52
+ return varPack !== undefined && !subtreeHasError(varPack);
53
+ },
54
+ code: (container, bodyChild) => {
55
+ const inIndex = container.children.findIndex(child => expression.slice(child.from, child.to) === '∈');
56
+ if (inIndex < 0) {
57
+ return RSErrorCode.expectedQuantifierDomain;
58
+ }
59
+
60
+ const domainChild = container.children[inIndex + 1];
61
+ if (domainChild === undefined || domainChild === bodyChild || subtreeHasError(domainChild)) {
62
+ return RSErrorCode.expectedQuantifierDomain;
63
+ }
64
+
65
+ return RSErrorCode.expectedQuantifierBody;
66
+ }
67
+ });
68
+ }
69
+
70
+ export function isIncompleteGeneratorBodyCode(code: RSErrorCode): boolean {
71
+ return (
72
+ code === RSErrorCode.expectedDeclarativeBody ||
73
+ code === RSErrorCode.expectedImperativeBody ||
74
+ code === RSErrorCode.expectedRecursiveBody
75
+ );
76
+ }
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Incomplete operators and bracket argument lists (`P1[S1, ]`).
3
+ *
4
+ * Tuple enums `(α,β,)` intentionally stay `unknownSyntax` — not `expectedArgument`.
5
+ */
6
+
7
+ import type { AstNode } from '../../../parsing';
8
+ import { RSErrorCode, type RSErrorDescription } from '../../error';
9
+ import { Boolean, Expr_enum_min2, Logic_binary, Logic_predicates, Logic_unary, Setexpr_binary } from '../parser.terms';
10
+ import { TokenID } from '../token';
11
+
12
+ import {
13
+ findAncestor,
14
+ findMatchingCloseBracket,
15
+ getErrorOperandNode,
16
+ isFilterNode,
17
+ isFunctionNode,
18
+ isPredicateNode,
19
+ subtreeHasError
20
+ } from './ast-utils';
21
+ import type { ErrorClassifier } from './context';
22
+ import { errorAt, hasOnlyClosingSuffix, isTextToken, sliceText } from './error-builders';
23
+
24
+ export const classifyIncompleteOperands: ErrorClassifier = (node, expression) =>
25
+ detectIncompleteUnaryOperand(node, expression) ??
26
+ detectIncompleteBinaryOperand(node, expression) ??
27
+ detectIncompleteBooleanOperand(node, expression);
28
+
29
+ export const classifyIncompleteArgument: ErrorClassifier = (node, expression) =>
30
+ detectIncompleteArgument(node, expression);
31
+
32
+ function detectIncompleteUnaryOperand(node: AstNode, expression: string): RSErrorDescription | null {
33
+ const unaryNode = findAncestor(node, target => target.typeID === Logic_unary);
34
+ if (unaryNode === null) {
35
+ return null;
36
+ }
37
+
38
+ const operandChild = unaryNode.children[unaryNode.children.length - 1];
39
+ if (operandChild?.typeID !== TokenID.ERROR || node !== operandChild) {
40
+ return null;
41
+ }
42
+
43
+ if (expression.slice(unaryNode.to).trim().length > 0) {
44
+ return null;
45
+ }
46
+
47
+ const operatorChild = unaryNode.children[0];
48
+ if (operatorChild === undefined || operatorChild === operandChild) {
49
+ return null;
50
+ }
51
+
52
+ if (!isTextToken(operatorChild, expression, '¬')) {
53
+ return null;
54
+ }
55
+
56
+ return errorAt(operandChild, RSErrorCode.expectedUnaryOperand);
57
+ }
58
+
59
+ function detectIncompleteBinaryOperand(node: AstNode, expression: string): RSErrorDescription | null {
60
+ const binaryNode = findAncestor(
61
+ node,
62
+ target => target.typeID === Logic_binary || target.typeID === Logic_predicates || target.typeID === Setexpr_binary
63
+ );
64
+ if (binaryNode === null) {
65
+ return null;
66
+ }
67
+
68
+ const operandChild = binaryNode.children[binaryNode.children.length - 1];
69
+ if (operandChild?.typeID !== TokenID.ERROR || node !== operandChild) {
70
+ return null;
71
+ }
72
+
73
+ if (expression.slice(binaryNode.to).trim().length > 0) {
74
+ return null;
75
+ }
76
+
77
+ if (binaryNode.children.length < 2) {
78
+ return null;
79
+ }
80
+
81
+ const leftChild = binaryNode.children[0];
82
+ if (leftChild.typeID === TokenID.ERROR || subtreeHasError(leftChild)) {
83
+ return null;
84
+ }
85
+
86
+ return errorAt(operandChild, RSErrorCode.expectedRightOperand);
87
+ }
88
+
89
+ function detectIncompleteBooleanOperand(node: AstNode, expression: string): RSErrorDescription | null {
90
+ const booleanNode = findAncestor(node, target => target.typeID === Boolean);
91
+ if (booleanNode === null) {
92
+ return null;
93
+ }
94
+
95
+ const operandChild = findTrailingErrorOperand(booleanNode, expression);
96
+ if (operandChild === null || node !== operandChild) {
97
+ return null;
98
+ }
99
+
100
+ if (!hasOnlyClosingSuffix(expression, booleanNode.to)) {
101
+ return null;
102
+ }
103
+
104
+ return errorAt(operandChild, RSErrorCode.expectedRightOperand);
105
+ }
106
+
107
+ function detectIncompleteArgument(node: AstNode, expression: string): RSErrorDescription | null {
108
+ const enumNode = findAncestor(node, target => target.typeID === Expr_enum_min2);
109
+ if (enumNode === null || !isLegitimateArgumentEnum(enumNode, expression)) {
110
+ return null;
111
+ }
112
+
113
+ const errorChild = findEnumArgumentError(enumNode, expression);
114
+ if (errorChild === null || node !== errorChild || !hasOnlyClosingSuffix(expression, enumNode.to)) {
115
+ return null;
116
+ }
117
+
118
+ return errorAt(errorChild, RSErrorCode.expectedArgument);
119
+ }
120
+
121
+ function findTrailingErrorOperand(container: AstNode, expression: string): AstNode | null {
122
+ for (let index = container.children.length - 1; index >= 0; index--) {
123
+ const child = container.children[index];
124
+ if (isOperandStructuralToken(child, expression)) {
125
+ continue;
126
+ }
127
+ return getErrorOperandNode(child);
128
+ }
129
+ return null;
130
+ }
131
+
132
+ function findEnumArgumentError(enumNode: AstNode, expression: string): AstNode | null {
133
+ for (const child of enumNode.children) {
134
+ if (isTextToken(child, expression, ',')) {
135
+ continue;
136
+ }
137
+ const errorNode = getErrorOperandNode(child);
138
+ if (errorNode !== null) {
139
+ return errorNode;
140
+ }
141
+ }
142
+ return null;
143
+ }
144
+
145
+ function isOperandStructuralToken(node: AstNode, expression: string): boolean {
146
+ const text = sliceText(node, expression);
147
+ return text === '(' || text === ')' || text === 'ℬ';
148
+ }
149
+
150
+ function isLegitimateArgumentEnum(enumNode: AstNode, expression: string): boolean {
151
+ return findCallHostForBracketEnum(enumNode, expression) !== null;
152
+ }
153
+
154
+ function findCallHostForBracketEnum(enumNode: AstNode, expression: string): AstNode | null {
155
+ let current: AstNode | null = enumNode.parent;
156
+ while (current !== null) {
157
+ for (const child of current.children) {
158
+ if (!isFunctionNode(child) && !isPredicateNode(child) && !isFilterNode(child)) {
159
+ continue;
160
+ }
161
+ if (isEnumInCallBrackets(enumNode, child, expression)) {
162
+ return child;
163
+ }
164
+ }
165
+ current = current.parent;
166
+ }
167
+ return null;
168
+ }
169
+
170
+ function isEnumInCallBrackets(enumNode: AstNode, nameNode: AstNode, expression: string): boolean {
171
+ const rest = expression.slice(nameNode.to);
172
+ const match = /^\s*\[/.exec(rest);
173
+ if (match === null) {
174
+ return false;
175
+ }
176
+
177
+ const openPos = nameNode.to + match.index + match[0].indexOf('[');
178
+ const closePos = findMatchingCloseBracket(expression, openPos);
179
+ if (closePos < 0) {
180
+ return false;
181
+ }
182
+
183
+ return enumNode.from > openPos && enumNode.from < closePos;
184
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Syntax error extraction — public entry point.
3
+ *
4
+ * Expects the **hybrid** AST produced by `normalizeAST` (see `parser/README.md`).
5
+ * See {@link ./README.md} for pipeline, classifier order, and how to add codes.
6
+ *
7
+ * @module rslang/parser/syntax-errors
8
+ */
9
+
10
+ import { type AstNode, visitAstDFS } from '../../../parsing';
11
+ import { annotateError } from '../../ast-annotations';
12
+ import { type ErrorReporter, type RSErrorDescription } from '../../error';
13
+ import { type TypeClass } from '../../semantic/typification';
14
+ import { TokenID } from '../token';
15
+
16
+ import { classifyFallbackError, classifyParseError } from './classify';
17
+ import { deduplicateErrors, extractBracketErrors, extractForbiddenCharacterErrors } from './expression-errors';
18
+ import { isIncompleteGeneratorBodyCode } from './incomplete-formal';
19
+
20
+ /** When set, refines incomplete formal headers (`[α∈…]`, quantifiers). */
21
+ export interface SyntaxErrorOptions {
22
+ expected?: TypeClass;
23
+ }
24
+
25
+ /**
26
+ * Walks a parsed expression AST and reports syntax errors.
27
+ *
28
+ * @param ast - Root AST from `buildTree(parser.parse(expression))`
29
+ * @param expression - Original source text (positions must match the AST)
30
+ * @param reporter - Called once per deduplicated error
31
+ * @param annotateErrors - When true, also calls `annotateError` on the triggering node
32
+ * @param options - Optional typification hint for incomplete formal expressions
33
+ */
34
+ export function extractSyntaxErrors(
35
+ ast: AstNode,
36
+ expression: string,
37
+ reporter: ErrorReporter,
38
+ annotateErrors: boolean = false,
39
+ options?: SyntaxErrorOptions
40
+ ) {
41
+ const forbiddenErrors = extractForbiddenCharacterErrors(expression);
42
+ if (forbiddenErrors.length > 0) {
43
+ for (const error of forbiddenErrors) {
44
+ reporter(error);
45
+ if (annotateErrors) {
46
+ annotateError(ast, error.code, error.params);
47
+ }
48
+ }
49
+ return;
50
+ }
51
+
52
+ const collected: RSErrorDescription[] = [];
53
+ const annotationTargets = new Map<RSErrorDescription, AstNode>();
54
+ const collect = (error: RSErrorDescription, target?: AstNode) => {
55
+ collected.push(error);
56
+ if (target !== undefined) {
57
+ annotationTargets.set(error, target);
58
+ }
59
+ };
60
+
61
+ const bracketError = extractBracketErrors(expression);
62
+ const hasBracketErrors = bracketError !== null;
63
+ visitAstDFS(ast, node => extractInternal(node, expression, collect, hasBracketErrors, options));
64
+
65
+ const hasIncompleteGenerator = collected.some(error => isIncompleteGeneratorBodyCode(error.code));
66
+ if (bracketError !== null && !hasIncompleteGenerator) {
67
+ collect(bracketError, ast);
68
+ }
69
+
70
+ for (const error of deduplicateErrors(collected)) {
71
+ reporter(error);
72
+ if (annotateErrors) {
73
+ const target = annotationTargets.get(error);
74
+ if (target !== undefined) {
75
+ annotateError(target, error.code, error.params);
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
+ function extractInternal(
82
+ node: AstNode,
83
+ expression: string,
84
+ collect: (error: RSErrorDescription, target: AstNode) => void,
85
+ hasBracketErrors: boolean,
86
+ options?: SyntaxErrorOptions
87
+ ) {
88
+ if (node.typeID !== TokenID.ERROR) {
89
+ return;
90
+ }
91
+
92
+ const classified = classifyParseError(node, expression, { hasBracketErrors, expected: options?.expected });
93
+ if (classified !== null) {
94
+ collect(classified, node);
95
+ return;
96
+ }
97
+
98
+ const fallback = classifyFallbackError(node, hasBracketErrors);
99
+ if (fallback !== null) {
100
+ collect(fallback.error, fallback.target);
101
+ }
102
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Reusable pattern: container header is valid, last child is `ERROR`, input ends here.
3
+ *
4
+ * @see incomplete-formal.ts — function declarations, quantifiers
5
+ */
6
+
7
+ import type { AstNode } from '../../../parsing';
8
+ import { type RSErrorCode, type RSErrorDescription } from '../../error';
9
+ import { TokenID } from '../token';
10
+
11
+ import { errorAt } from './error-builders';
12
+
13
+ export function detectTrailingErrorSlot(
14
+ node: AstNode,
15
+ expression: string,
16
+ container: AstNode | null,
17
+ options: {
18
+ headerValid?: (container: AstNode) => boolean;
19
+ code: RSErrorCode | ((container: AstNode, bodyChild: AstNode) => RSErrorCode | null);
20
+ }
21
+ ): RSErrorDescription | null {
22
+ if (container === null) {
23
+ return null;
24
+ }
25
+
26
+ const bodyChild = container.children[container.children.length - 1];
27
+ if (bodyChild?.typeID !== TokenID.ERROR || node !== bodyChild) {
28
+ return null;
29
+ }
30
+
31
+ if (expression.slice(container.to).trim().length > 0) {
32
+ return null;
33
+ }
34
+
35
+ if (options.headerValid !== undefined && !options.headerValid(container)) {
36
+ return null;
37
+ }
38
+
39
+ const code = typeof options.code === 'function' ? options.code(container, bodyChild) : options.code;
40
+ if (code === null) {
41
+ return null;
42
+ }
43
+
44
+ return errorAt(bodyChild, code);
45
+ }
@@ -1,14 +0,0 @@
1
- import { t as AstNode } from "../../ast-C8sIpKdL.js";
2
- import { g as TypeClass } from "../../typification-Dk-fisgO.js";
3
- import { t as ErrorReporter } from "../../error-CZXO5Y6a.js";
4
-
5
- //#region src/rslang/parser/syntax-errors.d.ts
6
- /** Options for syntax error extraction. */
7
- interface SyntaxErrorOptions {
8
- expected?: TypeClass;
9
- }
10
- /** Extracts syntax errors from an AST. */
11
- declare function extractSyntaxErrors(ast: AstNode, expression: string, reporter: ErrorReporter, annotateErrors?: boolean, options?: SyntaxErrorOptions): void;
12
- //#endregion
13
- export { SyntaxErrorOptions, extractSyntaxErrors };
14
- //# sourceMappingURL=syntax-errors.d.ts.map