@rsconcept/domain 1.7.0 → 1.7.2
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-CqUJ3FO9.d.ts → analyzer-QxWs73gP.d.ts} +2 -2
- package/dist/{ast-annotations-DvB1VcUv.d.ts → ast-annotations-CtF7C9uK.d.ts} +2 -2
- package/dist/{calculator-DKxDpTdT.d.ts → calculator-REeX2ZS1.d.ts} +2 -2
- package/dist/{error-CLnXbkly.d.ts → error-toW0TUHF.d.ts} +6 -1
- package/dist/index.d.ts +4 -4
- package/dist/library/rsengine.d.ts +1 -1
- package/dist/library/rsform-api.d.ts +1 -1
- package/dist/library/rsform.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 +5 -0
- package/dist/rslang/error.js.map +1 -1
- package/dist/rslang/eval/calculator.d.ts +1 -1
- package/dist/rslang/eval/evaluator.d.ts +1 -1
- package/dist/rslang/index.d.ts +4 -4
- package/dist/rslang/labels.d.ts +1 -1
- package/dist/rslang/labels.js +1 -1
- package/dist/rslang/labels.js.map +1 -1
- package/dist/rslang/parser/normalize.js +5 -1
- package/dist/rslang/parser/normalize.js.map +1 -1
- package/dist/rslang/parser/syntax-errors/index.d.ts +22 -0
- package/dist/rslang/parser/syntax-errors/index.js +2 -0
- package/dist/rslang/semantic/analyzer.d.ts +1 -1
- package/dist/rslang/semantic/analyzer.js +1 -1
- package/dist/rslang/semantic/type-auditor.d.ts +1 -1
- package/dist/rslang/semantic/value-auditor.d.ts +1 -1
- package/dist/{rslang/parser/syntax-errors.js → syntax-errors-B6c2vsan.js} +421 -233
- package/dist/syntax-errors-B6c2vsan.js.map +1 -0
- package/package.json +1 -1
- package/src/rslang/error.ts +5 -0
- package/src/rslang/labels.ts +1 -1
- package/src/rslang/parser/README.md +73 -0
- package/src/rslang/parser/normalize.ts +5 -1
- package/src/rslang/parser/parser.test.ts +53 -0
- package/src/rslang/parser/syntax-errors/README.md +61 -0
- package/src/rslang/parser/syntax-errors/ast-utils.ts +112 -0
- package/src/rslang/parser/syntax-errors/classify.ts +70 -0
- package/src/rslang/parser/syntax-errors/context.ts +25 -0
- package/src/rslang/parser/syntax-errors/error-builders.ts +28 -0
- package/src/rslang/parser/syntax-errors/expression-errors.ts +255 -0
- package/src/rslang/parser/syntax-errors/function-calls.ts +76 -0
- package/src/rslang/parser/syntax-errors/generators.ts +315 -0
- package/src/rslang/parser/syntax-errors/incomplete-formal.ts +68 -0
- package/src/rslang/parser/syntax-errors/incomplete-operands.ts +184 -0
- package/src/rslang/parser/syntax-errors/index.ts +102 -0
- package/src/rslang/parser/syntax-errors/trailing-slot.ts +45 -0
- package/dist/rslang/parser/syntax-errors.d.ts +0 -14
- package/dist/rslang/parser/syntax-errors.js.map +0 -1
- package/src/rslang/parser/syntax-errors.ts +0 -929
|
@@ -1,137 +1,162 @@
|
|
|
1
|
-
import { annotateError } from "
|
|
2
|
-
import { RSErrorCode } from "
|
|
3
|
-
import { visitAstDFS } from "
|
|
4
|
-
import "
|
|
5
|
-
import { TokenID } from "./token.js";
|
|
6
|
-
import { TypeClass } from "
|
|
7
|
-
import "./parser.terms.js";
|
|
8
|
-
//#region src/rslang/parser/syntax-errors.ts
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
const collected = [];
|
|
23
|
-
const annotationTargets = /* @__PURE__ */ new Map();
|
|
24
|
-
const collect = (error, target) => {
|
|
25
|
-
collected.push(error);
|
|
26
|
-
if (target !== void 0) annotationTargets.set(error, target);
|
|
27
|
-
};
|
|
28
|
-
const bracketError = extractBracketErrors(expression);
|
|
29
|
-
const hasBracketErrors = bracketError !== null;
|
|
30
|
-
visitAstDFS(ast, (node) => extractInternal(node, expression, collect, hasBracketErrors, options));
|
|
31
|
-
const hasIncompleteGenerator = collected.some((error) => isIncompleteGeneratorBodyCode(error.code));
|
|
32
|
-
if (bracketError !== null && !hasIncompleteGenerator) collect(bracketError, ast);
|
|
33
|
-
for (const error of deduplicateErrors(collected)) {
|
|
34
|
-
reporter(error);
|
|
35
|
-
if (annotateErrors) {
|
|
36
|
-
const target = annotationTargets.get(error);
|
|
37
|
-
if (target !== void 0) annotateError(target, error.code, error.params);
|
|
38
|
-
}
|
|
1
|
+
import { annotateError } from "./rslang/ast-annotations.js";
|
|
2
|
+
import { RSErrorCode } from "./rslang/error.js";
|
|
3
|
+
import { visitAstDFS } from "./parsing/ast.js";
|
|
4
|
+
import "./parsing/index.js";
|
|
5
|
+
import { TokenID } from "./rslang/parser/token.js";
|
|
6
|
+
import { TypeClass } from "./rslang/semantic/typification.js";
|
|
7
|
+
import "./rslang/parser/parser.terms.js";
|
|
8
|
+
//#region src/rslang/parser/syntax-errors/context.ts
|
|
9
|
+
/** Skips classifiers that conflict with bracket-level diagnostics. */
|
|
10
|
+
function whenNoBracketErrors(classifier) {
|
|
11
|
+
return (node, expression, context) => context.hasBracketErrors ? null : classifier(node, expression, context);
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/rslang/parser/syntax-errors/ast-utils.ts
|
|
15
|
+
function findAncestor(node, predicate) {
|
|
16
|
+
let current = node;
|
|
17
|
+
while (current !== null) {
|
|
18
|
+
if (predicate(current)) return current;
|
|
19
|
+
current = current.parent;
|
|
39
20
|
}
|
|
21
|
+
return null;
|
|
40
22
|
}
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
from: target.from,
|
|
47
|
-
to: target.to,
|
|
48
|
-
params
|
|
49
|
-
}, target);
|
|
23
|
+
function findRelatedNode(node, predicate) {
|
|
24
|
+
let current = node.parent;
|
|
25
|
+
while (current !== null) {
|
|
26
|
+
for (const child of current.children) if (predicate(child)) return child;
|
|
27
|
+
current = current.parent;
|
|
50
28
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
function isDescendantOf(ancestor, node) {
|
|
32
|
+
let current = node.parent;
|
|
33
|
+
while (current !== null) {
|
|
34
|
+
if (current === ancestor) return true;
|
|
35
|
+
current = current.parent;
|
|
55
36
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
function subtreeHasError(node) {
|
|
40
|
+
if (node.typeID === TokenID.ERROR) return true;
|
|
41
|
+
return node.children.some(subtreeHasError);
|
|
42
|
+
}
|
|
43
|
+
function isFunctionDeclNode(node) {
|
|
44
|
+
return node.typeID === 76 || node.typeID === TokenID.NT_FUNC_DEFINITION;
|
|
45
|
+
}
|
|
46
|
+
function isQuantifierNode(node) {
|
|
47
|
+
return node.typeID === 29;
|
|
48
|
+
}
|
|
49
|
+
function isFunctionNode(node) {
|
|
50
|
+
return node.typeID === 69 || node.typeID === TokenID.ID_FUNCTION;
|
|
51
|
+
}
|
|
52
|
+
function isPredicateNode(node) {
|
|
53
|
+
return node.typeID === 26 || node.typeID === TokenID.ID_PREDICATE;
|
|
54
|
+
}
|
|
55
|
+
function isFilterNode(node) {
|
|
56
|
+
return node.typeID === 59 || node.typeID === TokenID.FILTER;
|
|
57
|
+
}
|
|
58
|
+
function findMatchingCloseBracket(expression, openPos) {
|
|
59
|
+
if (expression[openPos] !== "[") return -1;
|
|
60
|
+
let depth = 0;
|
|
61
|
+
for (let index = openPos; index < expression.length; index++) {
|
|
62
|
+
const symbol = expression[index];
|
|
63
|
+
if (symbol === "[") depth++;
|
|
64
|
+
else if (symbol === "]") {
|
|
65
|
+
depth--;
|
|
66
|
+
if (depth === 0) return index;
|
|
67
|
+
}
|
|
60
68
|
}
|
|
61
|
-
|
|
62
|
-
if (!ignoreUnknownErrors) emit(node.from === node.to ? parent : node, RSErrorCode.unknownSyntax);
|
|
63
|
-
}
|
|
64
|
-
function classifyParseError(node, expression, options) {
|
|
65
|
-
const incompleteFunctionDecl = detectIncompleteFunctionDecl(node, expression, options?.expected);
|
|
66
|
-
if (incompleteFunctionDecl !== null) return incompleteFunctionDecl;
|
|
67
|
-
const incompleteQuantifier = detectIncompleteQuantifier(node, expression);
|
|
68
|
-
if (incompleteQuantifier !== null) return incompleteQuantifier;
|
|
69
|
-
const incompleteGenerator = detectIncompleteGenerator(node, expression);
|
|
70
|
-
if (incompleteGenerator !== null) return incompleteGenerator;
|
|
71
|
-
const filterParen = detectFilterParenMismatch(node, expression);
|
|
72
|
-
if (filterParen !== null) return filterParen;
|
|
73
|
-
return detectGlobalFuncWithoutArgs(node, expression);
|
|
69
|
+
return -1;
|
|
74
70
|
}
|
|
75
|
-
function
|
|
76
|
-
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
if (
|
|
71
|
+
function getErrorOperandNode(node) {
|
|
72
|
+
if (node.typeID === TokenID.ERROR) return node;
|
|
73
|
+
if (node.children.length === 1) return getErrorOperandNode(node.children[0]);
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
function isErrorPlaceholder(node) {
|
|
77
|
+
if (node.typeID === TokenID.ERROR) return true;
|
|
78
|
+
if (node.children.length === 0) return false;
|
|
79
|
+
return node.children.every((child) => isErrorPlaceholder(child));
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/rslang/parser/syntax-errors/function-calls.ts
|
|
83
|
+
const classifyFilterParenMismatch = (node, expression) => {
|
|
84
|
+
const filterNode = findAncestor(node, isFilterNode) ?? findRelatedNode(node, isFilterNode);
|
|
85
|
+
if (filterNode === null || expression[filterNode.to] !== "(") return null;
|
|
83
86
|
return {
|
|
84
|
-
code:
|
|
85
|
-
from:
|
|
86
|
-
to:
|
|
87
|
+
code: RSErrorCode.invalidFilterSyntax,
|
|
88
|
+
from: filterNode.from,
|
|
89
|
+
to: filterNode.to + 1
|
|
87
90
|
};
|
|
91
|
+
};
|
|
92
|
+
const classifyGlobalFuncCall = (node, expression) => {
|
|
93
|
+
const funcNode = findAncestor(node, (n) => isFunctionNode(n) || isPredicateNode(n)) ?? findRelatedNode(node, (n) => isFunctionNode(n) || isPredicateNode(n));
|
|
94
|
+
if (funcNode === null) return null;
|
|
95
|
+
return classifyFuncWithoutArgs(funcNode, expression);
|
|
96
|
+
};
|
|
97
|
+
function classifyFuncWithoutArgs(funcNode, expression) {
|
|
98
|
+
const name = expression.slice(funcNode.from, funcNode.to);
|
|
99
|
+
const rest = expression.slice(funcNode.to);
|
|
100
|
+
const withoutArgs = {
|
|
101
|
+
code: RSErrorCode.globalFuncWithoutArgs,
|
|
102
|
+
from: funcNode.from,
|
|
103
|
+
to: funcNode.to,
|
|
104
|
+
params: [name]
|
|
105
|
+
};
|
|
106
|
+
if (/^\s*$/.test(rest)) return withoutArgs;
|
|
107
|
+
if (/^\s*\(/.test(rest)) return {
|
|
108
|
+
code: RSErrorCode.globalFuncParenCall,
|
|
109
|
+
from: funcNode.from,
|
|
110
|
+
to: funcNode.to,
|
|
111
|
+
params: [name]
|
|
112
|
+
};
|
|
113
|
+
const openBracket = /^\s*\[/.exec(rest);
|
|
114
|
+
if (openBracket === null) return null;
|
|
115
|
+
const closeIdx = findMatchingCloseBracket(rest, openBracket[0].length - 1);
|
|
116
|
+
if (closeIdx >= 0 && rest.slice(openBracket[0].length, closeIdx).trim().length === 0) return withoutArgs;
|
|
117
|
+
return null;
|
|
88
118
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
case TypeClass.predicate:
|
|
93
|
-
case TypeClass.logic: return RSErrorCode.expectedLogicBody;
|
|
94
|
-
default: return RSErrorCode.expectedFunctionBody;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
function detectIncompleteQuantifier(node, expression) {
|
|
98
|
-
const quantNode = findAncestor(node, isQuantifierNode);
|
|
99
|
-
if (quantNode === null) return null;
|
|
100
|
-
const bodyChild = quantNode.children[quantNode.children.length - 1];
|
|
101
|
-
if (bodyChild?.typeID !== TokenID.ERROR || node !== bodyChild) return null;
|
|
102
|
-
const headerChildren = quantNode.children.slice(0, -1);
|
|
103
|
-
if (headerChildren.length < 3) return null;
|
|
104
|
-
const varPack = headerChildren.find((child) => child.typeID === 31 || child.typeID === TokenID.NT_ENUM_DECL);
|
|
105
|
-
if (varPack === void 0 || subtreeHasError(varPack)) return null;
|
|
106
|
-
const domainChild = headerChildren[headerChildren.length - 1];
|
|
107
|
-
if (domainChild === varPack || subtreeHasError(domainChild)) return null;
|
|
108
|
-
if (expression.slice(quantNode.to).trim().length > 0) return null;
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/rslang/parser/syntax-errors/error-builders.ts
|
|
121
|
+
function errorAt(node, code, params) {
|
|
109
122
|
return {
|
|
110
|
-
code
|
|
111
|
-
from:
|
|
112
|
-
to:
|
|
123
|
+
code,
|
|
124
|
+
from: node.from,
|
|
125
|
+
to: node.to,
|
|
126
|
+
params
|
|
113
127
|
};
|
|
114
128
|
}
|
|
115
|
-
function
|
|
116
|
-
|
|
129
|
+
function hasOnlyClosingSuffix(expression, from) {
|
|
130
|
+
const suffix = expression.slice(from).trim();
|
|
131
|
+
return suffix.length === 0 || /^[\]\)\}]*$/.test(suffix);
|
|
132
|
+
}
|
|
133
|
+
function sliceText(node, expression) {
|
|
134
|
+
return expression.slice(node.from, node.to);
|
|
135
|
+
}
|
|
136
|
+
function isTextToken(node, expression, text) {
|
|
137
|
+
return sliceText(node, expression) === text;
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/rslang/parser/syntax-errors/generators.ts
|
|
141
|
+
const classifyIncompleteGenerator = (node, expression) => detectIncompleteDeclarative(node, expression) ?? detectIncompleteImperative(node, expression) ?? detectIncompleteRecursion(node, expression);
|
|
142
|
+
function isIncompleteGeneratorBodyCode(code) {
|
|
143
|
+
return code === RSErrorCode.expectedDeclarativeBody || code === RSErrorCode.expectedImperativeBody || code === RSErrorCode.expectedRecursiveBody;
|
|
117
144
|
}
|
|
118
145
|
function detectIncompleteDeclarative(node, expression) {
|
|
119
|
-
return detectIncompleteGeneratorBody(node, expression, findAncestor(node, (target) => target.typeID === 60 || target.typeID === TokenID.NT_DECLARATIVE_EXPR), RSErrorCode.expectedDeclarativeBody, (slot) => isIncompleteDeclarativeBody(slot));
|
|
146
|
+
return detectIncompleteGeneratorBody(node, expression, findAncestor(node, (target) => target.typeID === 60 || target.typeID === TokenID.NT_DECLARATIVE_EXPR), RSErrorCode.expectedDeclarativeBody, (slot) => isIncompleteDeclarativeBody(slot.nodes));
|
|
120
147
|
}
|
|
121
148
|
function detectIncompleteImperative(node, expression) {
|
|
122
149
|
return detectIncompleteGeneratorBody(node, expression, findAncestor(node, (target) => target.typeID === 64 || target.typeID === TokenID.NT_IMPERATIVE_EXPR), RSErrorCode.expectedImperativeBody, (slot) => isIncompleteImperativeBody(slot, expression));
|
|
123
150
|
}
|
|
124
151
|
function detectIncompleteRecursion(node, expression) {
|
|
125
|
-
return detectIncompleteGeneratorBody(node, expression, findAncestor(node, (target) => target.typeID === 68), RSErrorCode.expectedRecursiveBody, (slot) => isIncompleteRecursionBody(slot));
|
|
152
|
+
return detectIncompleteGeneratorBody(node, expression, findAncestor(node, (target) => target.typeID === 68), RSErrorCode.expectedRecursiveBody, (slot) => isIncompleteRecursionBody(slot.nodes));
|
|
126
153
|
}
|
|
127
154
|
function detectIncompleteGeneratorBody(node, expression, generatorNode, code, isIncomplete) {
|
|
128
|
-
if (generatorNode === null) return null;
|
|
129
|
-
if (expression.slice(generatorNode.to).trim().length > 0) return null;
|
|
155
|
+
if (generatorNode === null || expression.slice(generatorNode.to).trim().length > 0) return null;
|
|
130
156
|
const slot = getGeneratorBodySlot(generatorNode, expression);
|
|
131
|
-
if (slot === null || !isIncomplete(slot)) return null;
|
|
132
|
-
if (!generatorHeaderIsValid(generatorNode, expression, slot.headerEndIndex)) return null;
|
|
157
|
+
if (slot === null || !isIncomplete(slot) || !generatorHeaderIsValid(generatorNode, expression, slot.headerEndIndex)) return null;
|
|
133
158
|
if (!isErrorInBodySlot(node, slot, generatorNode)) return null;
|
|
134
|
-
return
|
|
159
|
+
return errorAt(node, code);
|
|
135
160
|
}
|
|
136
161
|
function getGeneratorBodySlot(generatorNode, expression) {
|
|
137
162
|
if (generatorNode.typeID === 60 || generatorNode.typeID === TokenID.NT_DECLARATIVE_EXPR) return getDeclarativeBodySlot(generatorNode, expression);
|
|
@@ -140,7 +165,7 @@ function getGeneratorBodySlot(generatorNode, expression) {
|
|
|
140
165
|
return null;
|
|
141
166
|
}
|
|
142
167
|
function getDeclarativeBodySlot(declNode, expression) {
|
|
143
|
-
const pipeIndex = declNode.children.findIndex((child) =>
|
|
168
|
+
const pipeIndex = declNode.children.findIndex((child) => isTextToken(child, expression, "|"));
|
|
144
169
|
if (pipeIndex >= 0) return {
|
|
145
170
|
nodes: contentChildren(declNode, expression, pipeIndex + 1),
|
|
146
171
|
headerEndIndex: pipeIndex + 1
|
|
@@ -154,7 +179,7 @@ function getDeclarativeBodySlot(declNode, expression) {
|
|
|
154
179
|
};
|
|
155
180
|
}
|
|
156
181
|
function getImperativeBodySlot(impNode, expression) {
|
|
157
|
-
const pipeIndex = impNode.children.findIndex((child) =>
|
|
182
|
+
const pipeIndex = impNode.children.findIndex((child) => isTextToken(child, expression, "|"));
|
|
158
183
|
if (pipeIndex >= 0) return {
|
|
159
184
|
nodes: contentChildren(impNode, expression, pipeIndex + 1),
|
|
160
185
|
headerEndIndex: pipeIndex + 1
|
|
@@ -186,11 +211,11 @@ function contentChildren(generatorNode, expression, fromIndex) {
|
|
|
186
211
|
return generatorNode.children.slice(fromIndex).filter((child) => !isClosingBrace(child, expression));
|
|
187
212
|
}
|
|
188
213
|
function findDeclarativeDomain(declNode, expression) {
|
|
189
|
-
const inIndex = declNode.children.findIndex((child) =>
|
|
214
|
+
const inIndex = declNode.children.findIndex((child) => isTextToken(child, expression, "∈"));
|
|
190
215
|
if (inIndex < 0) return null;
|
|
191
216
|
for (let index = inIndex + 1; index < declNode.children.length; index++) {
|
|
192
217
|
const child = declNode.children[index];
|
|
193
|
-
if (
|
|
218
|
+
if (isTextToken(child, expression, "|") || isClosingBrace(child, expression)) break;
|
|
194
219
|
if (child.typeID === 38) return child;
|
|
195
220
|
}
|
|
196
221
|
return null;
|
|
@@ -200,7 +225,7 @@ function findImperativeTuple(impNode) {
|
|
|
200
225
|
return null;
|
|
201
226
|
}
|
|
202
227
|
function findLastPipeIndex(nodes, expression) {
|
|
203
|
-
for (let index = nodes.length - 1; index >= 0; index--) if (
|
|
228
|
+
for (let index = nodes.length - 1; index >= 0; index--) if (isTextToken(nodes[index], expression, "|")) return index;
|
|
204
229
|
return -1;
|
|
205
230
|
}
|
|
206
231
|
function generatorHeaderIsValid(generatorNode, expression, headerEndIndex) {
|
|
@@ -212,16 +237,16 @@ function generatorHeaderIsValid(generatorNode, expression, headerEndIndex) {
|
|
|
212
237
|
}
|
|
213
238
|
return true;
|
|
214
239
|
}
|
|
215
|
-
function isIncompleteDeclarativeBody(
|
|
216
|
-
return isEmptyOrErrorOnlyBody(
|
|
240
|
+
function isIncompleteDeclarativeBody(nodes) {
|
|
241
|
+
return isEmptyOrErrorOnlyBody(nodes);
|
|
217
242
|
}
|
|
218
243
|
function isIncompleteImperativeBody(slot, expression) {
|
|
219
244
|
if (slot.nodes.length === 0) return true;
|
|
220
245
|
if (slot.nodes.length === 1 && slot.nodes[0].typeID === 65) return isImpBlocksIncomplete(slot.nodes[0], expression);
|
|
221
246
|
return isEmptyOrErrorOnlyBody(slot.nodes);
|
|
222
247
|
}
|
|
223
|
-
function isIncompleteRecursionBody(
|
|
224
|
-
return isEmptyOrErrorOnlyBody(
|
|
248
|
+
function isIncompleteRecursionBody(nodes) {
|
|
249
|
+
return isEmptyOrErrorOnlyBody(nodes);
|
|
225
250
|
}
|
|
226
251
|
function isEmptyOrErrorOnlyBody(nodes) {
|
|
227
252
|
if (nodes.length === 0) return true;
|
|
@@ -232,7 +257,7 @@ function isImpBlocksIncomplete(blocksNode, expression) {
|
|
|
232
257
|
if (blocksNode.children.length === 0) return true;
|
|
233
258
|
const lastChild = blocksNode.children[blocksNode.children.length - 1];
|
|
234
259
|
if (lastChild.typeID === TokenID.ERROR) return true;
|
|
235
|
-
if (
|
|
260
|
+
if (isTextToken(lastChild, expression, ";")) return true;
|
|
236
261
|
if (lastChild.typeID === 38 && isErrorPlaceholder(lastChild)) return true;
|
|
237
262
|
return false;
|
|
238
263
|
}
|
|
@@ -251,149 +276,208 @@ function isImpBlocksTailError(blocksNode, errorNode) {
|
|
|
251
276
|
const lastChild = blocksNode.children[blocksNode.children.length - 1];
|
|
252
277
|
return lastChild.typeID === TokenID.ERROR && (errorNode === lastChild || isDescendantOf(lastChild, errorNode));
|
|
253
278
|
}
|
|
254
|
-
function isErrorPlaceholder(node) {
|
|
255
|
-
if (node.typeID === TokenID.ERROR) return true;
|
|
256
|
-
if (node.children.length === 0) return false;
|
|
257
|
-
return node.children.every((child) => isErrorPlaceholder(child));
|
|
258
|
-
}
|
|
259
|
-
function isDescendantOf(ancestor, node) {
|
|
260
|
-
let current = node.parent;
|
|
261
|
-
while (current !== null) {
|
|
262
|
-
if (current === ancestor) return true;
|
|
263
|
-
current = current.parent;
|
|
264
|
-
}
|
|
265
|
-
return false;
|
|
266
|
-
}
|
|
267
279
|
function findRecursionInit(recursionNode, expression) {
|
|
268
|
-
const assignIndex = recursionNode.children.findIndex((child) =>
|
|
280
|
+
const assignIndex = recursionNode.children.findIndex((child) => isTextToken(child, expression, ":="));
|
|
269
281
|
if (assignIndex < 0) return null;
|
|
270
282
|
for (let index = assignIndex + 1; index < recursionNode.children.length; index++) {
|
|
271
283
|
const child = recursionNode.children[index];
|
|
272
284
|
if (child.typeID === TokenID.ERROR) break;
|
|
273
|
-
if (
|
|
285
|
+
if (isTextToken(child, expression, "|") || isClosingBrace(child, expression)) continue;
|
|
274
286
|
return child;
|
|
275
287
|
}
|
|
276
288
|
return null;
|
|
277
289
|
}
|
|
278
|
-
function incompleteGeneratorError(code, errorNode) {
|
|
279
|
-
return {
|
|
280
|
-
code,
|
|
281
|
-
from: errorNode.from,
|
|
282
|
-
to: errorNode.to
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
function isIncompleteGeneratorBodyCode(code) {
|
|
286
|
-
return code === RSErrorCode.expectedDeclarativeBody || code === RSErrorCode.expectedImperativeBody || code === RSErrorCode.expectedRecursiveBody;
|
|
287
|
-
}
|
|
288
|
-
function isPipeToken(node, expression) {
|
|
289
|
-
return expression.slice(node.from, node.to) === "|";
|
|
290
|
-
}
|
|
291
|
-
function isSemicolonToken(node, expression) {
|
|
292
|
-
return expression.slice(node.from, node.to) === ";";
|
|
293
|
-
}
|
|
294
290
|
function isClosingBrace(node, expression) {
|
|
295
|
-
return node.typeID !== TokenID.ERROR &&
|
|
291
|
+
return node.typeID !== TokenID.ERROR && isTextToken(node, expression, "}");
|
|
296
292
|
}
|
|
297
293
|
function isStructuralGeneratorToken(node, expression) {
|
|
298
294
|
const text = expression.slice(node.from, node.to);
|
|
299
295
|
return text === "{" || text === "}" || text === "|" || text === "∈" || text === ":=" || text === ";";
|
|
300
296
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (
|
|
308
|
-
if (
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
};
|
|
313
|
-
return null;
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region src/rslang/parser/syntax-errors/trailing-slot.ts
|
|
299
|
+
function detectTrailingErrorSlot(node, expression, container, options) {
|
|
300
|
+
if (container === null) return null;
|
|
301
|
+
const bodyChild = container.children[container.children.length - 1];
|
|
302
|
+
if (bodyChild?.typeID !== TokenID.ERROR || node !== bodyChild) return null;
|
|
303
|
+
if (expression.slice(container.to).trim().length > 0) return null;
|
|
304
|
+
if (options.headerValid !== void 0 && !options.headerValid(container)) return null;
|
|
305
|
+
const code = typeof options.code === "function" ? options.code(container, bodyChild) : options.code;
|
|
306
|
+
if (code === null) return null;
|
|
307
|
+
return errorAt(bodyChild, code);
|
|
314
308
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
309
|
+
//#endregion
|
|
310
|
+
//#region src/rslang/parser/syntax-errors/incomplete-formal.ts
|
|
311
|
+
const classifyIncompleteFormal = (node, expression, context) => detectIncompleteFunctionDecl(node, expression, context.expected) ?? detectIncompleteQuantifier(node, expression);
|
|
312
|
+
function detectIncompleteFunctionDecl(node, expression, expected) {
|
|
313
|
+
return detectTrailingErrorSlot(node, expression, findAncestor(node, isFunctionDeclNode), {
|
|
314
|
+
headerValid: (container) => {
|
|
315
|
+
const argsNode = container.children.find((child) => child.typeID === 77 || child.typeID === TokenID.NT_ARGUMENTS);
|
|
316
|
+
return argsNode !== void 0 && !subtreeHasError(argsNode);
|
|
317
|
+
},
|
|
318
|
+
code: () => incompleteFormalExpressionCode(expected)
|
|
319
|
+
});
|
|
319
320
|
}
|
|
320
|
-
function
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
321
|
+
function incompleteFormalExpressionCode(expected) {
|
|
322
|
+
switch (expected) {
|
|
323
|
+
case TypeClass.function: return RSErrorCode.expectedExpressionBody;
|
|
324
|
+
case TypeClass.predicate:
|
|
325
|
+
case TypeClass.logic: return RSErrorCode.expectedLogicBody;
|
|
326
|
+
default: return RSErrorCode.expectedFunctionBody;
|
|
325
327
|
}
|
|
326
|
-
return null;
|
|
327
328
|
}
|
|
328
|
-
function
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
329
|
+
function detectIncompleteQuantifier(node, expression) {
|
|
330
|
+
return detectTrailingErrorSlot(node, expression, findAncestor(node, isQuantifierNode), {
|
|
331
|
+
headerValid: (container) => {
|
|
332
|
+
const varPack = container.children.find((child) => child.typeID === 31 || child.typeID === TokenID.NT_ENUM_DECL);
|
|
333
|
+
return varPack !== void 0 && !subtreeHasError(varPack);
|
|
334
|
+
},
|
|
335
|
+
code: (container, bodyChild) => {
|
|
336
|
+
const inIndex = container.children.findIndex((child) => expression.slice(child.from, child.to) === "∈");
|
|
337
|
+
if (inIndex < 0) return RSErrorCode.expectedQuantifierDomain;
|
|
338
|
+
const domainChild = container.children[inIndex + 1];
|
|
339
|
+
if (domainChild === void 0 || domainChild === bodyChild || subtreeHasError(domainChild)) return RSErrorCode.expectedQuantifierDomain;
|
|
340
|
+
return RSErrorCode.expectedQuantifierBody;
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
//#endregion
|
|
345
|
+
//#region src/rslang/parser/syntax-errors/incomplete-operands.ts
|
|
346
|
+
const classifyIncompleteOperands = (node, expression) => detectIncompleteUnaryOperand(node, expression) ?? detectIncompleteBinaryOperand(node, expression) ?? detectIncompleteBooleanOperand(node, expression);
|
|
347
|
+
const classifyIncompleteArgument = (node, expression) => detectIncompleteArgument(node, expression);
|
|
348
|
+
function detectIncompleteUnaryOperand(node, expression) {
|
|
349
|
+
const unaryNode = findAncestor(node, (target) => target.typeID === 24);
|
|
350
|
+
if (unaryNode === null) return null;
|
|
351
|
+
const operandChild = unaryNode.children[unaryNode.children.length - 1];
|
|
352
|
+
if (operandChild?.typeID !== TokenID.ERROR || node !== operandChild) return null;
|
|
353
|
+
if (expression.slice(unaryNode.to).trim().length > 0) return null;
|
|
354
|
+
const operatorChild = unaryNode.children[0];
|
|
355
|
+
if (operatorChild === void 0 || operatorChild === operandChild) return null;
|
|
356
|
+
if (!isTextToken(operatorChild, expression, "¬")) return null;
|
|
357
|
+
return errorAt(operandChild, RSErrorCode.expectedUnaryOperand);
|
|
358
|
+
}
|
|
359
|
+
function detectIncompleteBinaryOperand(node, expression) {
|
|
360
|
+
const binaryNode = findAncestor(node, (target) => target.typeID === 33 || target.typeID === 3 || target.typeID === 44);
|
|
361
|
+
if (binaryNode === null) return null;
|
|
362
|
+
const operandChild = binaryNode.children[binaryNode.children.length - 1];
|
|
363
|
+
if (operandChild?.typeID !== TokenID.ERROR || node !== operandChild) return null;
|
|
364
|
+
if (expression.slice(binaryNode.to).trim().length > 0) return null;
|
|
365
|
+
if (binaryNode.children.length < 2) return null;
|
|
366
|
+
const leftChild = binaryNode.children[0];
|
|
367
|
+
if (leftChild.typeID === TokenID.ERROR || subtreeHasError(leftChild)) return null;
|
|
368
|
+
return errorAt(operandChild, RSErrorCode.expectedRightOperand);
|
|
369
|
+
}
|
|
370
|
+
function detectIncompleteBooleanOperand(node, expression) {
|
|
371
|
+
const booleanNode = findAncestor(node, (target) => target.typeID === 56);
|
|
372
|
+
if (booleanNode === null) return null;
|
|
373
|
+
const operandChild = findTrailingErrorOperand(booleanNode, expression);
|
|
374
|
+
if (operandChild === null || node !== operandChild) return null;
|
|
375
|
+
if (!hasOnlyClosingSuffix(expression, booleanNode.to)) return null;
|
|
376
|
+
return errorAt(operandChild, RSErrorCode.expectedRightOperand);
|
|
377
|
+
}
|
|
378
|
+
function detectIncompleteArgument(node, expression) {
|
|
379
|
+
const enumNode = findAncestor(node, (target) => target.typeID === 9);
|
|
380
|
+
if (enumNode === null || !isLegitimateArgumentEnum(enumNode, expression)) return null;
|
|
381
|
+
const errorChild = findEnumArgumentError(enumNode, expression);
|
|
382
|
+
if (errorChild === null || node !== errorChild || !hasOnlyClosingSuffix(expression, enumNode.to)) return null;
|
|
383
|
+
return errorAt(errorChild, RSErrorCode.expectedArgument);
|
|
384
|
+
}
|
|
385
|
+
function findTrailingErrorOperand(container, expression) {
|
|
386
|
+
for (let index = container.children.length - 1; index >= 0; index--) {
|
|
387
|
+
const child = container.children[index];
|
|
388
|
+
if (isOperandStructuralToken(child, expression)) continue;
|
|
389
|
+
return getErrorOperandNode(child);
|
|
343
390
|
}
|
|
344
391
|
return null;
|
|
345
392
|
}
|
|
346
|
-
function
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
393
|
+
function findEnumArgumentError(enumNode, expression) {
|
|
394
|
+
for (const child of enumNode.children) {
|
|
395
|
+
if (isTextToken(child, expression, ",")) continue;
|
|
396
|
+
const errorNode = getErrorOperandNode(child);
|
|
397
|
+
if (errorNode !== null) return errorNode;
|
|
351
398
|
}
|
|
352
399
|
return null;
|
|
353
400
|
}
|
|
354
|
-
function
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
function isQuantifierNode(node) {
|
|
358
|
-
return node.typeID === 29;
|
|
359
|
-
}
|
|
360
|
-
function isFunctionNode(node) {
|
|
361
|
-
return node.typeID === 69 || node.typeID === TokenID.ID_FUNCTION;
|
|
362
|
-
}
|
|
363
|
-
function isPredicateNode(node) {
|
|
364
|
-
return node.typeID === 26 || node.typeID === TokenID.ID_PREDICATE;
|
|
401
|
+
function isOperandStructuralToken(node, expression) {
|
|
402
|
+
const text = sliceText(node, expression);
|
|
403
|
+
return text === "(" || text === ")" || text === "ℬ";
|
|
365
404
|
}
|
|
366
|
-
function
|
|
367
|
-
return
|
|
405
|
+
function isLegitimateArgumentEnum(enumNode, expression) {
|
|
406
|
+
return findCallHostForBracketEnum(enumNode, expression) !== null;
|
|
368
407
|
}
|
|
369
|
-
function
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
else if (symbol === "]") {
|
|
376
|
-
depth--;
|
|
377
|
-
if (depth === 0) return index;
|
|
408
|
+
function findCallHostForBracketEnum(enumNode, expression) {
|
|
409
|
+
let current = enumNode.parent;
|
|
410
|
+
while (current !== null) {
|
|
411
|
+
for (const child of current.children) {
|
|
412
|
+
if (!isFunctionNode(child) && !isPredicateNode(child) && !isFilterNode(child)) continue;
|
|
413
|
+
if (isEnumInCallBrackets(enumNode, child, expression)) return child;
|
|
378
414
|
}
|
|
415
|
+
current = current.parent;
|
|
379
416
|
}
|
|
380
|
-
return
|
|
417
|
+
return null;
|
|
381
418
|
}
|
|
382
|
-
function
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
419
|
+
function isEnumInCallBrackets(enumNode, nameNode, expression) {
|
|
420
|
+
const rest = expression.slice(nameNode.to);
|
|
421
|
+
const match = /^\s*\[/.exec(rest);
|
|
422
|
+
if (match === null) return false;
|
|
423
|
+
const openPos = nameNode.to + match.index + match[0].indexOf("[");
|
|
424
|
+
const closePos = findMatchingCloseBracket(expression, openPos);
|
|
425
|
+
if (closePos < 0) return false;
|
|
426
|
+
return enumNode.from > openPos && enumNode.from < closePos;
|
|
427
|
+
}
|
|
428
|
+
//#endregion
|
|
429
|
+
//#region src/rslang/parser/syntax-errors/classify.ts
|
|
430
|
+
const PARSE_ERROR_CLASSIFIERS = [
|
|
431
|
+
classifyIncompleteFormal,
|
|
432
|
+
classifyIncompleteGenerator,
|
|
433
|
+
whenNoBracketErrors(classifyIncompleteOperands),
|
|
434
|
+
whenNoBracketErrors(classifyIncompleteArgument),
|
|
435
|
+
classifyFilterParenMismatch,
|
|
436
|
+
classifyGlobalFuncCall
|
|
437
|
+
];
|
|
438
|
+
/** Runs classifiers in priority order for one `ERROR` AST node. */
|
|
439
|
+
function classifyParseError(node, expression, context) {
|
|
440
|
+
for (const classifier of PARSE_ERROR_CLASSIFIERS) {
|
|
441
|
+
const error = classifier(node, expression, context);
|
|
442
|
+
if (error !== null) return error;
|
|
390
443
|
}
|
|
391
|
-
|
|
392
|
-
return result.filter((error) => error.code !== RSErrorCode.unknownSyntax || !specific.some((item) => rangesOverlap(error, item)));
|
|
444
|
+
return null;
|
|
393
445
|
}
|
|
394
|
-
|
|
395
|
-
|
|
446
|
+
/** Default rules when no classifier matched: `expectedLocal` or `unknownSyntax`. */
|
|
447
|
+
function classifyFallbackError(node, ignoreUnknownErrors) {
|
|
448
|
+
const parent = node.parent;
|
|
449
|
+
if (parent === null) return ignoreUnknownErrors ? null : {
|
|
450
|
+
error: {
|
|
451
|
+
code: RSErrorCode.unknownSyntax,
|
|
452
|
+
from: node.from,
|
|
453
|
+
to: node.to
|
|
454
|
+
},
|
|
455
|
+
target: node
|
|
456
|
+
};
|
|
457
|
+
if (parent.typeID === 4) return {
|
|
458
|
+
error: {
|
|
459
|
+
code: RSErrorCode.expectedLocal,
|
|
460
|
+
from: parent.from,
|
|
461
|
+
to: parent.to
|
|
462
|
+
},
|
|
463
|
+
target: parent
|
|
464
|
+
};
|
|
465
|
+
if (ignoreUnknownErrors) return null;
|
|
466
|
+
const target = node.from === node.to ? parent : node;
|
|
467
|
+
return {
|
|
468
|
+
error: {
|
|
469
|
+
code: RSErrorCode.unknownSyntax,
|
|
470
|
+
from: target.from,
|
|
471
|
+
to: target.to
|
|
472
|
+
},
|
|
473
|
+
target
|
|
474
|
+
};
|
|
396
475
|
}
|
|
476
|
+
//#endregion
|
|
477
|
+
//#region src/rslang/parser/syntax-errors/expression-errors.ts
|
|
478
|
+
/**
|
|
479
|
+
* Expression-level diagnostics (no AST required) and error deduplication.
|
|
480
|
+
*/
|
|
397
481
|
const ALLOWED_EXPRESSION_CHARS = buildAllowedExpressionChars();
|
|
398
482
|
function buildAllowedExpressionChars() {
|
|
399
483
|
const chars = /* @__PURE__ */ new Set();
|
|
@@ -474,6 +558,50 @@ function extractBracketErrors(expression) {
|
|
|
474
558
|
}
|
|
475
559
|
return null;
|
|
476
560
|
}
|
|
561
|
+
function deduplicateErrors(errors) {
|
|
562
|
+
const result = [];
|
|
563
|
+
for (const error of errors) {
|
|
564
|
+
if (result.some((existing) => existing.code === error.code && existing.from === error.from && existing.to === error.to)) continue;
|
|
565
|
+
if (error.code !== RSErrorCode.unknownSyntax) {
|
|
566
|
+
for (let index = result.length - 1; index >= 0; index--) if (result[index].code === RSErrorCode.unknownSyntax && rangesOverlap(error, result[index])) result.splice(index, 1);
|
|
567
|
+
}
|
|
568
|
+
result.push(error);
|
|
569
|
+
}
|
|
570
|
+
const specific = result.filter((error) => error.code !== RSErrorCode.unknownSyntax);
|
|
571
|
+
return collapseContiguousUnknownSyntax(result.filter((error) => error.code !== RSErrorCode.unknownSyntax || !specific.some((item) => rangesOverlap(error, item))));
|
|
572
|
+
}
|
|
573
|
+
function collapseContiguousUnknownSyntax(errors) {
|
|
574
|
+
const unknowns = errors.filter((error) => error.code === RSErrorCode.unknownSyntax);
|
|
575
|
+
if (unknowns.length <= 1) return errors;
|
|
576
|
+
const others = errors.filter((error) => error.code !== RSErrorCode.unknownSyntax);
|
|
577
|
+
const sorted = [...unknowns].sort((left, right) => left.from - right.from || left.to - right.to);
|
|
578
|
+
const merged = [];
|
|
579
|
+
let clusterFrom = sorted[0].from;
|
|
580
|
+
let clusterTo = sorted[0].to;
|
|
581
|
+
for (let index = 1; index < sorted.length; index++) {
|
|
582
|
+
const error = sorted[index];
|
|
583
|
+
if (error.from <= clusterTo) {
|
|
584
|
+
clusterTo = Math.max(clusterTo, error.to);
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
merged.push({
|
|
588
|
+
code: RSErrorCode.unknownSyntax,
|
|
589
|
+
from: clusterFrom,
|
|
590
|
+
to: clusterTo
|
|
591
|
+
});
|
|
592
|
+
clusterFrom = error.from;
|
|
593
|
+
clusterTo = error.to;
|
|
594
|
+
}
|
|
595
|
+
merged.push({
|
|
596
|
+
code: RSErrorCode.unknownSyntax,
|
|
597
|
+
from: clusterFrom,
|
|
598
|
+
to: clusterTo
|
|
599
|
+
});
|
|
600
|
+
return [...others, ...merged];
|
|
601
|
+
}
|
|
602
|
+
function rangesOverlap(a, b) {
|
|
603
|
+
return a.from < b.to && b.from < a.to;
|
|
604
|
+
}
|
|
477
605
|
function isOpenBracket(symbol) {
|
|
478
606
|
return symbol === "(" || symbol === "[" || symbol === "{";
|
|
479
607
|
}
|
|
@@ -518,6 +646,66 @@ function findMatchingCloseParen(expression, openPos) {
|
|
|
518
646
|
return -1;
|
|
519
647
|
}
|
|
520
648
|
//#endregion
|
|
521
|
-
|
|
649
|
+
//#region src/rslang/parser/syntax-errors/index.ts
|
|
650
|
+
/**
|
|
651
|
+
* Syntax error extraction — public entry point.
|
|
652
|
+
*
|
|
653
|
+
* Expects the **hybrid** AST produced by `normalizeAST` (see `parser/README.md`).
|
|
654
|
+
* See {@link ./README.md} for pipeline, classifier order, and how to add codes.
|
|
655
|
+
*
|
|
656
|
+
* @module rslang/parser/syntax-errors
|
|
657
|
+
*/
|
|
658
|
+
/**
|
|
659
|
+
* Walks a parsed expression AST and reports syntax errors.
|
|
660
|
+
*
|
|
661
|
+
* @param ast - Root AST from `buildTree(parser.parse(expression))`
|
|
662
|
+
* @param expression - Original source text (positions must match the AST)
|
|
663
|
+
* @param reporter - Called once per deduplicated error
|
|
664
|
+
* @param annotateErrors - When true, also calls `annotateError` on the triggering node
|
|
665
|
+
* @param options - Optional typification hint for incomplete formal expressions
|
|
666
|
+
*/
|
|
667
|
+
function extractSyntaxErrors(ast, expression, reporter, annotateErrors = false, options) {
|
|
668
|
+
const forbiddenErrors = extractForbiddenCharacterErrors(expression);
|
|
669
|
+
if (forbiddenErrors.length > 0) {
|
|
670
|
+
for (const error of forbiddenErrors) {
|
|
671
|
+
reporter(error);
|
|
672
|
+
if (annotateErrors) annotateError(ast, error.code, error.params);
|
|
673
|
+
}
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
const collected = [];
|
|
677
|
+
const annotationTargets = /* @__PURE__ */ new Map();
|
|
678
|
+
const collect = (error, target) => {
|
|
679
|
+
collected.push(error);
|
|
680
|
+
if (target !== void 0) annotationTargets.set(error, target);
|
|
681
|
+
};
|
|
682
|
+
const bracketError = extractBracketErrors(expression);
|
|
683
|
+
const hasBracketErrors = bracketError !== null;
|
|
684
|
+
visitAstDFS(ast, (node) => extractInternal(node, expression, collect, hasBracketErrors, options));
|
|
685
|
+
const hasIncompleteGenerator = collected.some((error) => isIncompleteGeneratorBodyCode(error.code));
|
|
686
|
+
if (bracketError !== null && !hasIncompleteGenerator) collect(bracketError, ast);
|
|
687
|
+
for (const error of deduplicateErrors(collected)) {
|
|
688
|
+
reporter(error);
|
|
689
|
+
if (annotateErrors) {
|
|
690
|
+
const target = annotationTargets.get(error);
|
|
691
|
+
if (target !== void 0) annotateError(target, error.code, error.params);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
function extractInternal(node, expression, collect, hasBracketErrors, options) {
|
|
696
|
+
if (node.typeID !== TokenID.ERROR) return;
|
|
697
|
+
const classified = classifyParseError(node, expression, {
|
|
698
|
+
hasBracketErrors,
|
|
699
|
+
expected: options?.expected
|
|
700
|
+
});
|
|
701
|
+
if (classified !== null) {
|
|
702
|
+
collect(classified, node);
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
const fallback = classifyFallbackError(node, hasBracketErrors);
|
|
706
|
+
if (fallback !== null) collect(fallback.error, fallback.target);
|
|
707
|
+
}
|
|
708
|
+
//#endregion
|
|
709
|
+
export { extractSyntaxErrors as t };
|
|
522
710
|
|
|
523
|
-
//# sourceMappingURL=syntax-errors.js.map
|
|
711
|
+
//# sourceMappingURL=syntax-errors-B6c2vsan.js.map
|