@rsconcept/domain 1.7.0 → 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.
- 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-pCI6RicF.js} +399 -233
- package/dist/syntax-errors-pCI6RicF.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 +237 -0
- package/src/rslang/parser/syntax-errors/function-calls.ts +76 -0
- package/src/rslang/parser/syntax-errors/generators.ts +307 -0
- package/src/rslang/parser/syntax-errors/incomplete-formal.ts +76 -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,159 @@
|
|
|
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);
|
|
117
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);
|
|
118
142
|
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));
|
|
143
|
+
return detectIncompleteGeneratorBody(node, expression, findAncestor(node, (target) => target.typeID === 60 || target.typeID === TokenID.NT_DECLARATIVE_EXPR), RSErrorCode.expectedDeclarativeBody, (slot) => isIncompleteDeclarativeBody(slot.nodes));
|
|
120
144
|
}
|
|
121
145
|
function detectIncompleteImperative(node, expression) {
|
|
122
146
|
return detectIncompleteGeneratorBody(node, expression, findAncestor(node, (target) => target.typeID === 64 || target.typeID === TokenID.NT_IMPERATIVE_EXPR), RSErrorCode.expectedImperativeBody, (slot) => isIncompleteImperativeBody(slot, expression));
|
|
123
147
|
}
|
|
124
148
|
function detectIncompleteRecursion(node, expression) {
|
|
125
|
-
return detectIncompleteGeneratorBody(node, expression, findAncestor(node, (target) => target.typeID === 68), RSErrorCode.expectedRecursiveBody, (slot) => isIncompleteRecursionBody(slot));
|
|
149
|
+
return detectIncompleteGeneratorBody(node, expression, findAncestor(node, (target) => target.typeID === 68), RSErrorCode.expectedRecursiveBody, (slot) => isIncompleteRecursionBody(slot.nodes));
|
|
126
150
|
}
|
|
127
151
|
function detectIncompleteGeneratorBody(node, expression, generatorNode, code, isIncomplete) {
|
|
128
|
-
if (generatorNode === null) return null;
|
|
129
|
-
if (expression.slice(generatorNode.to).trim().length > 0) return null;
|
|
152
|
+
if (generatorNode === null || expression.slice(generatorNode.to).trim().length > 0) return null;
|
|
130
153
|
const slot = getGeneratorBodySlot(generatorNode, expression);
|
|
131
|
-
if (slot === null || !isIncomplete(slot)) return null;
|
|
132
|
-
if (!generatorHeaderIsValid(generatorNode, expression, slot.headerEndIndex)) return null;
|
|
154
|
+
if (slot === null || !isIncomplete(slot) || !generatorHeaderIsValid(generatorNode, expression, slot.headerEndIndex)) return null;
|
|
133
155
|
if (!isErrorInBodySlot(node, slot, generatorNode)) return null;
|
|
134
|
-
return
|
|
156
|
+
return errorAt(node, code);
|
|
135
157
|
}
|
|
136
158
|
function getGeneratorBodySlot(generatorNode, expression) {
|
|
137
159
|
if (generatorNode.typeID === 60 || generatorNode.typeID === TokenID.NT_DECLARATIVE_EXPR) return getDeclarativeBodySlot(generatorNode, expression);
|
|
@@ -140,7 +162,7 @@ function getGeneratorBodySlot(generatorNode, expression) {
|
|
|
140
162
|
return null;
|
|
141
163
|
}
|
|
142
164
|
function getDeclarativeBodySlot(declNode, expression) {
|
|
143
|
-
const pipeIndex = declNode.children.findIndex((child) =>
|
|
165
|
+
const pipeIndex = declNode.children.findIndex((child) => isTextToken(child, expression, "|"));
|
|
144
166
|
if (pipeIndex >= 0) return {
|
|
145
167
|
nodes: contentChildren(declNode, expression, pipeIndex + 1),
|
|
146
168
|
headerEndIndex: pipeIndex + 1
|
|
@@ -154,7 +176,7 @@ function getDeclarativeBodySlot(declNode, expression) {
|
|
|
154
176
|
};
|
|
155
177
|
}
|
|
156
178
|
function getImperativeBodySlot(impNode, expression) {
|
|
157
|
-
const pipeIndex = impNode.children.findIndex((child) =>
|
|
179
|
+
const pipeIndex = impNode.children.findIndex((child) => isTextToken(child, expression, "|"));
|
|
158
180
|
if (pipeIndex >= 0) return {
|
|
159
181
|
nodes: contentChildren(impNode, expression, pipeIndex + 1),
|
|
160
182
|
headerEndIndex: pipeIndex + 1
|
|
@@ -186,11 +208,11 @@ function contentChildren(generatorNode, expression, fromIndex) {
|
|
|
186
208
|
return generatorNode.children.slice(fromIndex).filter((child) => !isClosingBrace(child, expression));
|
|
187
209
|
}
|
|
188
210
|
function findDeclarativeDomain(declNode, expression) {
|
|
189
|
-
const inIndex = declNode.children.findIndex((child) =>
|
|
211
|
+
const inIndex = declNode.children.findIndex((child) => isTextToken(child, expression, "∈"));
|
|
190
212
|
if (inIndex < 0) return null;
|
|
191
213
|
for (let index = inIndex + 1; index < declNode.children.length; index++) {
|
|
192
214
|
const child = declNode.children[index];
|
|
193
|
-
if (
|
|
215
|
+
if (isTextToken(child, expression, "|") || isClosingBrace(child, expression)) break;
|
|
194
216
|
if (child.typeID === 38) return child;
|
|
195
217
|
}
|
|
196
218
|
return null;
|
|
@@ -200,7 +222,7 @@ function findImperativeTuple(impNode) {
|
|
|
200
222
|
return null;
|
|
201
223
|
}
|
|
202
224
|
function findLastPipeIndex(nodes, expression) {
|
|
203
|
-
for (let index = nodes.length - 1; index >= 0; index--) if (
|
|
225
|
+
for (let index = nodes.length - 1; index >= 0; index--) if (isTextToken(nodes[index], expression, "|")) return index;
|
|
204
226
|
return -1;
|
|
205
227
|
}
|
|
206
228
|
function generatorHeaderIsValid(generatorNode, expression, headerEndIndex) {
|
|
@@ -212,16 +234,16 @@ function generatorHeaderIsValid(generatorNode, expression, headerEndIndex) {
|
|
|
212
234
|
}
|
|
213
235
|
return true;
|
|
214
236
|
}
|
|
215
|
-
function isIncompleteDeclarativeBody(
|
|
216
|
-
return isEmptyOrErrorOnlyBody(
|
|
237
|
+
function isIncompleteDeclarativeBody(nodes) {
|
|
238
|
+
return isEmptyOrErrorOnlyBody(nodes);
|
|
217
239
|
}
|
|
218
240
|
function isIncompleteImperativeBody(slot, expression) {
|
|
219
241
|
if (slot.nodes.length === 0) return true;
|
|
220
242
|
if (slot.nodes.length === 1 && slot.nodes[0].typeID === 65) return isImpBlocksIncomplete(slot.nodes[0], expression);
|
|
221
243
|
return isEmptyOrErrorOnlyBody(slot.nodes);
|
|
222
244
|
}
|
|
223
|
-
function isIncompleteRecursionBody(
|
|
224
|
-
return isEmptyOrErrorOnlyBody(
|
|
245
|
+
function isIncompleteRecursionBody(nodes) {
|
|
246
|
+
return isEmptyOrErrorOnlyBody(nodes);
|
|
225
247
|
}
|
|
226
248
|
function isEmptyOrErrorOnlyBody(nodes) {
|
|
227
249
|
if (nodes.length === 0) return true;
|
|
@@ -232,7 +254,7 @@ function isImpBlocksIncomplete(blocksNode, expression) {
|
|
|
232
254
|
if (blocksNode.children.length === 0) return true;
|
|
233
255
|
const lastChild = blocksNode.children[blocksNode.children.length - 1];
|
|
234
256
|
if (lastChild.typeID === TokenID.ERROR) return true;
|
|
235
|
-
if (
|
|
257
|
+
if (isTextToken(lastChild, expression, ";")) return true;
|
|
236
258
|
if (lastChild.typeID === 38 && isErrorPlaceholder(lastChild)) return true;
|
|
237
259
|
return false;
|
|
238
260
|
}
|
|
@@ -251,149 +273,211 @@ function isImpBlocksTailError(blocksNode, errorNode) {
|
|
|
251
273
|
const lastChild = blocksNode.children[blocksNode.children.length - 1];
|
|
252
274
|
return lastChild.typeID === TokenID.ERROR && (errorNode === lastChild || isDescendantOf(lastChild, errorNode));
|
|
253
275
|
}
|
|
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
276
|
function findRecursionInit(recursionNode, expression) {
|
|
268
|
-
const assignIndex = recursionNode.children.findIndex((child) =>
|
|
277
|
+
const assignIndex = recursionNode.children.findIndex((child) => isTextToken(child, expression, ":="));
|
|
269
278
|
if (assignIndex < 0) return null;
|
|
270
279
|
for (let index = assignIndex + 1; index < recursionNode.children.length; index++) {
|
|
271
280
|
const child = recursionNode.children[index];
|
|
272
281
|
if (child.typeID === TokenID.ERROR) break;
|
|
273
|
-
if (
|
|
282
|
+
if (isTextToken(child, expression, "|") || isClosingBrace(child, expression)) continue;
|
|
274
283
|
return child;
|
|
275
284
|
}
|
|
276
285
|
return null;
|
|
277
286
|
}
|
|
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
287
|
function isClosingBrace(node, expression) {
|
|
295
|
-
return node.typeID !== TokenID.ERROR &&
|
|
288
|
+
return node.typeID !== TokenID.ERROR && isTextToken(node, expression, "}");
|
|
296
289
|
}
|
|
297
290
|
function isStructuralGeneratorToken(node, expression) {
|
|
298
291
|
const text = expression.slice(node.from, node.to);
|
|
299
292
|
return text === "{" || text === "}" || text === "|" || text === "∈" || text === ":=" || text === ";";
|
|
300
293
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (
|
|
308
|
-
if (
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
};
|
|
313
|
-
return null;
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region src/rslang/parser/syntax-errors/trailing-slot.ts
|
|
296
|
+
function detectTrailingErrorSlot(node, expression, container, options) {
|
|
297
|
+
if (container === null) return null;
|
|
298
|
+
const bodyChild = container.children[container.children.length - 1];
|
|
299
|
+
if (bodyChild?.typeID !== TokenID.ERROR || node !== bodyChild) return null;
|
|
300
|
+
if (expression.slice(container.to).trim().length > 0) return null;
|
|
301
|
+
if (options.headerValid !== void 0 && !options.headerValid(container)) return null;
|
|
302
|
+
const code = typeof options.code === "function" ? options.code(container, bodyChild) : options.code;
|
|
303
|
+
if (code === null) return null;
|
|
304
|
+
return errorAt(bodyChild, code);
|
|
314
305
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
306
|
+
//#endregion
|
|
307
|
+
//#region src/rslang/parser/syntax-errors/incomplete-formal.ts
|
|
308
|
+
const classifyIncompleteFormal = (node, expression, context) => detectIncompleteFunctionDecl(node, expression, context.expected) ?? detectIncompleteQuantifier(node, expression);
|
|
309
|
+
function detectIncompleteFunctionDecl(node, expression, expected) {
|
|
310
|
+
return detectTrailingErrorSlot(node, expression, findAncestor(node, isFunctionDeclNode), {
|
|
311
|
+
headerValid: (container) => {
|
|
312
|
+
const argsNode = container.children.find((child) => child.typeID === 77 || child.typeID === TokenID.NT_ARGUMENTS);
|
|
313
|
+
return argsNode !== void 0 && !subtreeHasError(argsNode);
|
|
314
|
+
},
|
|
315
|
+
code: () => incompleteFormalExpressionCode(expected)
|
|
316
|
+
});
|
|
319
317
|
}
|
|
320
|
-
function
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
318
|
+
function incompleteFormalExpressionCode(expected) {
|
|
319
|
+
switch (expected) {
|
|
320
|
+
case TypeClass.function: return RSErrorCode.expectedExpressionBody;
|
|
321
|
+
case TypeClass.predicate:
|
|
322
|
+
case TypeClass.logic: return RSErrorCode.expectedLogicBody;
|
|
323
|
+
default: return RSErrorCode.expectedFunctionBody;
|
|
325
324
|
}
|
|
326
|
-
return null;
|
|
327
325
|
}
|
|
328
|
-
function
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
326
|
+
function detectIncompleteQuantifier(node, expression) {
|
|
327
|
+
return detectTrailingErrorSlot(node, expression, findAncestor(node, isQuantifierNode), {
|
|
328
|
+
headerValid: (container) => {
|
|
329
|
+
const varPack = container.children.find((child) => child.typeID === 31 || child.typeID === TokenID.NT_ENUM_DECL);
|
|
330
|
+
return varPack !== void 0 && !subtreeHasError(varPack);
|
|
331
|
+
},
|
|
332
|
+
code: (container, bodyChild) => {
|
|
333
|
+
const inIndex = container.children.findIndex((child) => expression.slice(child.from, child.to) === "∈");
|
|
334
|
+
if (inIndex < 0) return RSErrorCode.expectedQuantifierDomain;
|
|
335
|
+
const domainChild = container.children[inIndex + 1];
|
|
336
|
+
if (domainChild === void 0 || domainChild === bodyChild || subtreeHasError(domainChild)) return RSErrorCode.expectedQuantifierDomain;
|
|
337
|
+
return RSErrorCode.expectedQuantifierBody;
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
function isIncompleteGeneratorBodyCode(code) {
|
|
342
|
+
return code === RSErrorCode.expectedDeclarativeBody || code === RSErrorCode.expectedImperativeBody || code === RSErrorCode.expectedRecursiveBody;
|
|
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,28 @@ 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 first = [...unknowns].sort((left, right) => left.from - right.from)[0];
|
|
578
|
+
return [...others, first];
|
|
579
|
+
}
|
|
580
|
+
function rangesOverlap(a, b) {
|
|
581
|
+
return a.from < b.to && b.from < a.to;
|
|
582
|
+
}
|
|
477
583
|
function isOpenBracket(symbol) {
|
|
478
584
|
return symbol === "(" || symbol === "[" || symbol === "{";
|
|
479
585
|
}
|
|
@@ -518,6 +624,66 @@ function findMatchingCloseParen(expression, openPos) {
|
|
|
518
624
|
return -1;
|
|
519
625
|
}
|
|
520
626
|
//#endregion
|
|
521
|
-
|
|
627
|
+
//#region src/rslang/parser/syntax-errors/index.ts
|
|
628
|
+
/**
|
|
629
|
+
* Syntax error extraction — public entry point.
|
|
630
|
+
*
|
|
631
|
+
* Expects the **hybrid** AST produced by `normalizeAST` (see `parser/README.md`).
|
|
632
|
+
* See {@link ./README.md} for pipeline, classifier order, and how to add codes.
|
|
633
|
+
*
|
|
634
|
+
* @module rslang/parser/syntax-errors
|
|
635
|
+
*/
|
|
636
|
+
/**
|
|
637
|
+
* Walks a parsed expression AST and reports syntax errors.
|
|
638
|
+
*
|
|
639
|
+
* @param ast - Root AST from `buildTree(parser.parse(expression))`
|
|
640
|
+
* @param expression - Original source text (positions must match the AST)
|
|
641
|
+
* @param reporter - Called once per deduplicated error
|
|
642
|
+
* @param annotateErrors - When true, also calls `annotateError` on the triggering node
|
|
643
|
+
* @param options - Optional typification hint for incomplete formal expressions
|
|
644
|
+
*/
|
|
645
|
+
function extractSyntaxErrors(ast, expression, reporter, annotateErrors = false, options) {
|
|
646
|
+
const forbiddenErrors = extractForbiddenCharacterErrors(expression);
|
|
647
|
+
if (forbiddenErrors.length > 0) {
|
|
648
|
+
for (const error of forbiddenErrors) {
|
|
649
|
+
reporter(error);
|
|
650
|
+
if (annotateErrors) annotateError(ast, error.code, error.params);
|
|
651
|
+
}
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
const collected = [];
|
|
655
|
+
const annotationTargets = /* @__PURE__ */ new Map();
|
|
656
|
+
const collect = (error, target) => {
|
|
657
|
+
collected.push(error);
|
|
658
|
+
if (target !== void 0) annotationTargets.set(error, target);
|
|
659
|
+
};
|
|
660
|
+
const bracketError = extractBracketErrors(expression);
|
|
661
|
+
const hasBracketErrors = bracketError !== null;
|
|
662
|
+
visitAstDFS(ast, (node) => extractInternal(node, expression, collect, hasBracketErrors, options));
|
|
663
|
+
const hasIncompleteGenerator = collected.some((error) => isIncompleteGeneratorBodyCode(error.code));
|
|
664
|
+
if (bracketError !== null && !hasIncompleteGenerator) collect(bracketError, ast);
|
|
665
|
+
for (const error of deduplicateErrors(collected)) {
|
|
666
|
+
reporter(error);
|
|
667
|
+
if (annotateErrors) {
|
|
668
|
+
const target = annotationTargets.get(error);
|
|
669
|
+
if (target !== void 0) annotateError(target, error.code, error.params);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
function extractInternal(node, expression, collect, hasBracketErrors, options) {
|
|
674
|
+
if (node.typeID !== TokenID.ERROR) return;
|
|
675
|
+
const classified = classifyParseError(node, expression, {
|
|
676
|
+
hasBracketErrors,
|
|
677
|
+
expected: options?.expected
|
|
678
|
+
});
|
|
679
|
+
if (classified !== null) {
|
|
680
|
+
collect(classified, node);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
const fallback = classifyFallbackError(node, hasBracketErrors);
|
|
684
|
+
if (fallback !== null) collect(fallback.error, fallback.target);
|
|
685
|
+
}
|
|
686
|
+
//#endregion
|
|
687
|
+
export { extractSyntaxErrors as t };
|
|
522
688
|
|
|
523
|
-
//# sourceMappingURL=syntax-errors.js.map
|
|
689
|
+
//# sourceMappingURL=syntax-errors-pCI6RicF.js.map
|