grammar-composer 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +144 -89
- package/dist/exports/Exports.d.ts +2 -0
- package/dist/exports/Exports.d.ts.map +1 -1
- package/dist/exports/Exports.js +2 -0
- package/dist/exports/Exports.js.map +1 -1
- package/dist/parser-generator/Grammar.d.ts +8 -1
- package/dist/parser-generator/Grammar.d.ts.map +1 -1
- package/dist/parser-generator/Grammar.js +30 -225
- package/dist/parser-generator/Grammar.js.map +1 -1
- package/dist/parser-generator/ParseError.d.ts +37 -0
- package/dist/parser-generator/ParseError.d.ts.map +1 -0
- package/dist/parser-generator/ParseError.js +226 -0
- package/dist/parser-generator/ParseError.js.map +1 -0
- package/dist/parser-generator/StaticAnalysis.d.ts +6 -0
- package/dist/parser-generator/StaticAnalysis.d.ts.map +1 -0
- package/dist/parser-generator/StaticAnalysis.js +283 -0
- package/dist/parser-generator/StaticAnalysis.js.map +1 -0
- package/dist/parser-generator/TerminalToText.d.ts +15 -0
- package/dist/parser-generator/TerminalToText.d.ts.map +1 -0
- package/dist/parser-generator/TerminalToText.js +158 -0
- package/dist/parser-generator/TerminalToText.js.map +1 -0
- package/dist/parser-generator/TopDownParser.d.ts +4 -2
- package/dist/parser-generator/TopDownParser.d.ts.map +1 -1
- package/dist/parser-generator/TopDownParser.js +44 -42
- package/dist/parser-generator/TopDownParser.js.map +1 -1
- package/dist/tests/Test.js +23 -23
- package/dist/tests/Test.js.map +1 -1
- package/dist/tests/test-grammars/JsonGrammar.d.ts +12 -11
- package/dist/tests/test-grammars/JsonGrammar.d.ts.map +1 -1
- package/dist/tests/test-grammars/JsonGrammar.js +25 -10
- package/dist/tests/test-grammars/JsonGrammar.js.map +1 -1
- package/dist/tests/test-grammars/RegExpGrammar.d.ts +25 -25
- package/dist/tests/test-grammars/RegExpGrammar.d.ts.map +1 -1
- package/dist/tests/test-grammars/RegExpGrammar.js +48 -31
- package/dist/tests/test-grammars/RegExpGrammar.js.map +1 -1
- package/dist/tests/test-grammars/XmlGrammar.d.ts +11 -10
- package/dist/tests/test-grammars/XmlGrammar.d.ts.map +1 -1
- package/dist/tests/test-grammars/XmlGrammar.js +19 -8
- package/dist/tests/test-grammars/XmlGrammar.js.map +1 -1
- package/dist/utilities/LineAndColumn.d.ts +7 -0
- package/dist/utilities/LineAndColumn.d.ts.map +1 -0
- package/dist/utilities/LineAndColumn.js +52 -0
- package/dist/utilities/LineAndColumn.js.map +1 -0
- package/package.json +1 -1
- package/src/exports/Exports.ts +2 -0
- package/src/parser-generator/Grammar.ts +59 -265
- package/src/parser-generator/ParseError.ts +337 -0
- package/src/parser-generator/StaticAnalysis.ts +340 -0
- package/src/parser-generator/TerminalToText.ts +196 -0
- package/src/parser-generator/TopDownParser.ts +67 -49
- package/src/tests/Test.ts +26 -24
- package/src/tests/test-grammars/JsonGrammar.ts +28 -10
- package/src/tests/test-grammars/RegExpGrammar.ts +65 -46
- package/src/tests/test-grammars/XmlGrammar.ts +20 -8
- package/src/utilities/LineAndColumn.ts +61 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { buildCaretSpacing, expandTabs, getLineAndColumn } from '../utilities/LineAndColumn.js';
|
|
2
|
+
import { escapeChar, TerminalToText } from './TerminalToText.js';
|
|
3
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
4
|
+
// Parse error class
|
|
5
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
6
|
+
export class ParseError extends Error {
|
|
7
|
+
input;
|
|
8
|
+
offset;
|
|
9
|
+
line;
|
|
10
|
+
column;
|
|
11
|
+
found;
|
|
12
|
+
expected;
|
|
13
|
+
contextProduction;
|
|
14
|
+
constructor(details, maxDisplayedLineLength = 100) {
|
|
15
|
+
const { line, column } = getLineAndColumn(details.input, details.offset);
|
|
16
|
+
const errorMessage = buildErrorMessage(details, line, column, maxDisplayedLineLength);
|
|
17
|
+
super(errorMessage);
|
|
18
|
+
this.name = 'ParseError';
|
|
19
|
+
this.input = details.input;
|
|
20
|
+
this.offset = details.offset;
|
|
21
|
+
this.line = line;
|
|
22
|
+
this.column = column;
|
|
23
|
+
if (details.offset < details.input.length) {
|
|
24
|
+
this.found = details.input[details.offset];
|
|
25
|
+
}
|
|
26
|
+
this.expected = details.expected;
|
|
27
|
+
this.contextProduction = details.contextProductionName;
|
|
28
|
+
}
|
|
29
|
+
// Dedicated factory for the case where parsing failed because no expected
|
|
30
|
+
// terminal matched at some position.
|
|
31
|
+
static createFailedParseError(input, offset, bestFailedMatches) {
|
|
32
|
+
const { expected, contextProduction } = analyzeFailedMatches(bestFailedMatches);
|
|
33
|
+
return new ParseError({
|
|
34
|
+
input,
|
|
35
|
+
offset,
|
|
36
|
+
expected,
|
|
37
|
+
contextProductionName: contextProduction.name
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// Dedicated factory for the case where parsing consumed only part of the input
|
|
41
|
+
// without any specific terminal failing.
|
|
42
|
+
static createIncompleteParseError(input, offset, parsedLength) {
|
|
43
|
+
return new ParseError({ input, offset, parsedLength });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
47
|
+
// Failed match analysis
|
|
48
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
49
|
+
// Computes the data needed to describe a parse failure: the deduplicated list of expected
|
|
50
|
+
// terminals (each retaining the production it belongs to) and the shared context production.
|
|
51
|
+
// The actual message formatting is owned by ParseError.
|
|
52
|
+
export function analyzeFailedMatches(failedMatches) {
|
|
53
|
+
const productionStacks = failedMatches.map(failedMatch => failedMatch.productionStack);
|
|
54
|
+
const commonProductionPrefix = findCommonProductionPrefix(productionStacks);
|
|
55
|
+
const contextProduction = commonProductionPrefix[commonProductionPrefix.length - 1];
|
|
56
|
+
// Collect every production each distinct terminal could be part of, together with
|
|
57
|
+
// the production stack that led to it. The production is the one directly below
|
|
58
|
+
// the shared context production.
|
|
59
|
+
const candidatesByTerminal = new Map();
|
|
60
|
+
for (const failedMatch of failedMatches) {
|
|
61
|
+
const subProduction = failedMatch.productionStack[commonProductionPrefix.length] ?? contextProduction;
|
|
62
|
+
if (subProduction === undefined) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const stringifiedFailedTerminal = stringifyFailedTerminal(failedMatch);
|
|
66
|
+
const candidates = candidatesByTerminal.get(stringifiedFailedTerminal) ?? [];
|
|
67
|
+
candidates.push({ production: subProduction, productionStack: failedMatch.productionStack });
|
|
68
|
+
candidatesByTerminal.set(stringifiedFailedTerminal, candidates);
|
|
69
|
+
}
|
|
70
|
+
// When the same terminal can be reached through several productions and one of
|
|
71
|
+
// them is an ancestor of another (e.g. the same pattern is referenced both
|
|
72
|
+
// directly and through a parent production), keep only the innermost production.
|
|
73
|
+
const expected = [];
|
|
74
|
+
for (const [stringifiedFailedTerminal, candidates] of candidatesByTerminal) {
|
|
75
|
+
for (const candidate of candidates) {
|
|
76
|
+
if (isRedundantCandidate(candidate, candidates)) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (!expected.some(entry => entry.productionName === candidate.production.name && entry.terminalString === stringifiedFailedTerminal)) {
|
|
80
|
+
expected.push({ productionName: candidate.production.name, terminalString: stringifiedFailedTerminal });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
expected,
|
|
86
|
+
contextProduction,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// A candidate is redundant when the same terminal is also expected by a deeper
|
|
90
|
+
// production within the same context, i.e. when another candidate's production
|
|
91
|
+
// appears below this candidate's production in its own stack.
|
|
92
|
+
function isRedundantCandidate(candidate, candidates) {
|
|
93
|
+
const candidateIndex = stackIndexOf(candidate.productionStack, candidate.production);
|
|
94
|
+
for (const other of candidates) {
|
|
95
|
+
if (other === candidate) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const otherIndex = stackIndexOf(candidate.productionStack, other.production);
|
|
99
|
+
if (otherIndex > candidateIndex) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
// Compares nonterminals by their canonical grammar reference, because the same
|
|
106
|
+
// production can be represented by multiple cloned instances during a parse.
|
|
107
|
+
function stackIndexOf(productionStack, production) {
|
|
108
|
+
const canonical = production.grammarNonterminal ?? production;
|
|
109
|
+
return productionStack.findIndex(nonterminal => (nonterminal.grammarNonterminal ?? nonterminal) === canonical);
|
|
110
|
+
}
|
|
111
|
+
function stringifyFailedTerminal(failedMatch) {
|
|
112
|
+
const { terminal, productionStack } = failedMatch;
|
|
113
|
+
if (terminal.name !== undefined) {
|
|
114
|
+
return terminal.name;
|
|
115
|
+
}
|
|
116
|
+
const innermostProduction = productionStack[productionStack.length - 1];
|
|
117
|
+
if (innermostProduction !== undefined && innermostProduction.content === terminal) {
|
|
118
|
+
return innermostProduction.name;
|
|
119
|
+
}
|
|
120
|
+
const stringifiedLengthLimit = 100;
|
|
121
|
+
const terminalToText = new TerminalToText(stringifiedLengthLimit);
|
|
122
|
+
return terminalToText.stringifyTerminal(terminal);
|
|
123
|
+
}
|
|
124
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
125
|
+
// Production grouping
|
|
126
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
127
|
+
// The stacks are compared by canonical nonterminal identity rather than object identity,
|
|
128
|
+
// because the same production can be represented by multiple distinct object instances
|
|
129
|
+
// during a single parse: the grammar builder creates spread copies of the canonical
|
|
130
|
+
// nonterminal for optional references and for references wrapped in cached(). Each clone
|
|
131
|
+
// keeps a reference to the original in 'grammarNonterminal'.
|
|
132
|
+
export function findCommonProductionPrefix(productionStacks) {
|
|
133
|
+
if (productionStacks.length === 0) {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
let commonPrefix = productionStacks[0];
|
|
137
|
+
for (let i = 1; i < productionStacks.length; i++) {
|
|
138
|
+
const stack = productionStacks[i];
|
|
139
|
+
let commonLength = 0;
|
|
140
|
+
while (commonLength < commonPrefix.length &&
|
|
141
|
+
commonLength < stack.length &&
|
|
142
|
+
areSameNonterminal(commonPrefix[commonLength], stack[commonLength])) {
|
|
143
|
+
commonLength += 1;
|
|
144
|
+
}
|
|
145
|
+
commonPrefix = commonPrefix.slice(0, commonLength);
|
|
146
|
+
if (commonPrefix.length === 0) {
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return commonPrefix;
|
|
151
|
+
}
|
|
152
|
+
function areSameNonterminal(a, b) {
|
|
153
|
+
return (a.grammarNonterminal ?? a) === (b.grammarNonterminal ?? b);
|
|
154
|
+
}
|
|
155
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
156
|
+
// Parse error helpers
|
|
157
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
158
|
+
function buildErrorMessage(details, line, column, maxDisplayedLineLength) {
|
|
159
|
+
const context = formatErrorContext(details.input, details.offset, maxDisplayedLineLength);
|
|
160
|
+
if (details.parsedLength !== undefined) {
|
|
161
|
+
return `Failed parsing the input text ${context}\n\n` +
|
|
162
|
+
`Stopped parsing at line ${line}, column ${column} (${details.parsedLength} of ${details.input.length} characters consumed).`;
|
|
163
|
+
}
|
|
164
|
+
const expected = details.expected ?? [];
|
|
165
|
+
const expectedMessage = expected.length > 0
|
|
166
|
+
? buildExpectedGroupsMessage(expected)
|
|
167
|
+
: '';
|
|
168
|
+
const contextMessage = details.contextProductionName !== undefined
|
|
169
|
+
? `While parsing '${details.contextProductionName}', expected one of the following:\n\n${expectedMessage}`
|
|
170
|
+
: `Expected one of the following:\n\n${expectedMessage}`;
|
|
171
|
+
return `Failed parsing the input text ${context}\n\n${contextMessage}`;
|
|
172
|
+
}
|
|
173
|
+
// Formats the expected terminals grouped by production, with a blank line between
|
|
174
|
+
// the groups so the message stays readable.
|
|
175
|
+
function buildExpectedGroupsMessage(expected) {
|
|
176
|
+
const expectedByProduction = new Map();
|
|
177
|
+
for (const { productionName, terminalString } of expected) {
|
|
178
|
+
const stringifiedTerminals = expectedByProduction.get(productionName) ?? [];
|
|
179
|
+
if (!stringifiedTerminals.includes(terminalString)) {
|
|
180
|
+
stringifiedTerminals.push(terminalString);
|
|
181
|
+
}
|
|
182
|
+
expectedByProduction.set(productionName, stringifiedTerminals);
|
|
183
|
+
}
|
|
184
|
+
return [...expectedByProduction.entries()]
|
|
185
|
+
.map(([productionName, stringifiedTerminals]) => `In '${productionName}': ${stringifiedTerminals.join(', ')}`)
|
|
186
|
+
.join('\n\n');
|
|
187
|
+
}
|
|
188
|
+
export function formatErrorContext(input, offset, maxDisplayedLineLength) {
|
|
189
|
+
const { line, column } = getLineAndColumn(input, offset);
|
|
190
|
+
let lineStartOffset = offset;
|
|
191
|
+
while (lineStartOffset > 0 && input[lineStartOffset - 1] !== '\n') {
|
|
192
|
+
lineStartOffset -= 1;
|
|
193
|
+
}
|
|
194
|
+
let lineEndOffset = offset;
|
|
195
|
+
while (lineEndOffset < input.length && input[lineEndOffset] !== '\n') {
|
|
196
|
+
lineEndOffset += 1;
|
|
197
|
+
}
|
|
198
|
+
const lineText = input.substring(lineStartOffset, lineEndOffset).replace(/\r$/, '');
|
|
199
|
+
let displayedText = lineText;
|
|
200
|
+
let displayedCaretColumn = column;
|
|
201
|
+
// If the failing line is very long (e.g. minified input), show a window around the
|
|
202
|
+
// error position instead of the entire line.
|
|
203
|
+
if (lineText.length > maxDisplayedLineLength) {
|
|
204
|
+
const rawCaretOffset = Math.min(column - 1, lineText.length);
|
|
205
|
+
const radius = Math.floor(maxDisplayedLineLength / 2);
|
|
206
|
+
const contextStart = Math.max(0, rawCaretOffset - radius);
|
|
207
|
+
const contextEnd = Math.min(lineText.length, rawCaretOffset + radius);
|
|
208
|
+
const hasTruncatedStart = contextStart > 0;
|
|
209
|
+
const hasTruncatedEnd = contextEnd < lineText.length;
|
|
210
|
+
const truncatedLine = lineText.substring(contextStart, contextEnd);
|
|
211
|
+
displayedText = `${hasTruncatedStart ? '…' : ''}${truncatedLine}${hasTruncatedEnd ? '…' : ''}`;
|
|
212
|
+
displayedCaretColumn = (rawCaretOffset - contextStart) + (hasTruncatedStart ? 1 : 0) + 1;
|
|
213
|
+
}
|
|
214
|
+
let unexpectedTargetText;
|
|
215
|
+
if (offset < input.length) {
|
|
216
|
+
const targetChar = input[offset];
|
|
217
|
+
const escapedTargetChar = escapeChar(targetChar);
|
|
218
|
+
unexpectedTargetText = ` (unexpected character '${escapedTargetChar}')`;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
unexpectedTargetText = ' (unexpected end of input)';
|
|
222
|
+
}
|
|
223
|
+
const text = `at line ${line}, column ${column}${unexpectedTargetText}:\n\n\t${expandTabs(displayedText)}\n\t${buildCaretSpacing(displayedText, displayedCaretColumn)}^`;
|
|
224
|
+
return text;
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=ParseError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParseError.js","sourceRoot":"","sources":["../../src/parser-generator/ParseError.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Pattern } from 'regexp-composer';
|
|
2
|
+
import { GrammarElement } from './Grammar.js';
|
|
3
|
+
export declare function detectAndAnnotateOptionalNodes(rootNode: GrammarElement): void;
|
|
4
|
+
export declare function detectAndErrorOnLeftRecursion(rootNode: GrammarElement): void;
|
|
5
|
+
export declare function validatePatternCaptureGroups(pattern: Pattern): void;
|
|
6
|
+
//# sourceMappingURL=StaticAnalysis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StaticAnalysis.d.ts","sourceRoot":"","sources":["../../src/parser-generator/StaticAnalysis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAEzC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAK7C,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,cAAc,QAmMtE;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,cAAc,QA+CrE;AAWD,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6EnE"}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { isArray, isBoolean, isString } from '../utilities/Utilities.js';
|
|
2
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
3
|
+
// Internal static analysis methods
|
|
4
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
export function detectAndAnnotateOptionalNodes(rootNode) {
|
|
6
|
+
const visitedNodes = new Set();
|
|
7
|
+
const resolvedNodes = new Map();
|
|
8
|
+
const unresolvedNodes = new Map();
|
|
9
|
+
function processDepthFirst(node) {
|
|
10
|
+
if (visitedNodes.has(node)) {
|
|
11
|
+
return resolvedNodes.get(node);
|
|
12
|
+
}
|
|
13
|
+
visitedNodes.add(node);
|
|
14
|
+
switch (node.type) {
|
|
15
|
+
case 'StringTerminal':
|
|
16
|
+
case 'PatternTerminal': {
|
|
17
|
+
resolvedNodes.set(node, node.optional);
|
|
18
|
+
return node.optional;
|
|
19
|
+
}
|
|
20
|
+
case 'Nonterminal':
|
|
21
|
+
case 'Repetition': {
|
|
22
|
+
const result = processDepthFirst(node.content);
|
|
23
|
+
if (node.optional) {
|
|
24
|
+
resolvedNodes.set(node, true);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
else if (isBoolean(result)) {
|
|
28
|
+
resolvedNodes.set(node, result);
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
unresolvedNodes.set(node, { dependencies: new Set([node.content]), isChoice: false });
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
case 'Sequence': {
|
|
37
|
+
const dependencies = new Set();
|
|
38
|
+
let hasNonOptionalResolvedMember = false;
|
|
39
|
+
for (const element of node.members) {
|
|
40
|
+
const result = processDepthFirst(element);
|
|
41
|
+
if (isBoolean(result)) {
|
|
42
|
+
if (result === false) {
|
|
43
|
+
hasNonOptionalResolvedMember = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
dependencies.add(element);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (node.optional === true) {
|
|
51
|
+
resolvedNodes.set(node, true);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
else if (hasNonOptionalResolvedMember) {
|
|
55
|
+
resolvedNodes.set(node, false);
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
else if (dependencies.size === 0) {
|
|
59
|
+
resolvedNodes.set(node, true);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
unresolvedNodes.set(node, { dependencies, isChoice: false });
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
case 'Choice': {
|
|
68
|
+
const dependencies = new Set();
|
|
69
|
+
let hasOptionalResolvedMember = false;
|
|
70
|
+
for (const element of node.members) {
|
|
71
|
+
const result = processDepthFirst(element);
|
|
72
|
+
if (isBoolean(result)) {
|
|
73
|
+
if (result === true) {
|
|
74
|
+
hasOptionalResolvedMember = true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
dependencies.add(element);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (node.optional === true) {
|
|
82
|
+
resolvedNodes.set(node, true);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
else if (hasOptionalResolvedMember) {
|
|
86
|
+
resolvedNodes.set(node, true);
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
else if (dependencies.size === 0) {
|
|
90
|
+
resolvedNodes.set(node, false);
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
unresolvedNodes.set(node, { dependencies, isChoice: true });
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
// Process depth first to resolve the easy cases, for productions that contain
|
|
102
|
+
// no cyclic references:
|
|
103
|
+
processDepthFirst(rootNode);
|
|
104
|
+
// Now the remainder consists of nodes containing cyclic references that have not yet been resolved.
|
|
105
|
+
// Use a form of iterative elimination and substitution to resolve them:
|
|
106
|
+
while (unresolvedNodes.size > 0) {
|
|
107
|
+
// This variable tracks whether at least one dependency was resolved, in any node.
|
|
108
|
+
// If it stays false, it means that no improvement was made during the iteration,
|
|
109
|
+
// and we should exit the loop.
|
|
110
|
+
let atLastOneDependencyResolvedInAnyNode = false;
|
|
111
|
+
const nodesToDelete = [];
|
|
112
|
+
// Scan the unresolved nodes to locate any new resolved dependencies
|
|
113
|
+
for (const [node, { dependencies, isChoice }] of unresolvedNodes) {
|
|
114
|
+
let nodeResolved = false;
|
|
115
|
+
// Iterate over all unresolved dependencies for the node
|
|
116
|
+
for (const dependency of Array.from(dependencies)) {
|
|
117
|
+
// Check if the dependency has been resolved
|
|
118
|
+
const value = resolvedNodes.get(dependency);
|
|
119
|
+
if (value !== undefined) {
|
|
120
|
+
// If it did, record that some dependencies were resolved
|
|
121
|
+
atLastOneDependencyResolvedInAnyNode = true;
|
|
122
|
+
if (isChoice) {
|
|
123
|
+
if (value === true) {
|
|
124
|
+
resolvedNodes.set(node, true);
|
|
125
|
+
nodeResolved = true;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
if (value === false) {
|
|
131
|
+
resolvedNodes.set(node, false);
|
|
132
|
+
nodeResolved = true;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
dependencies.delete(dependency);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (nodeResolved) {
|
|
140
|
+
nodesToDelete.push(node);
|
|
141
|
+
}
|
|
142
|
+
else if (dependencies.size === 0) {
|
|
143
|
+
if (isChoice) {
|
|
144
|
+
resolvedNodes.set(node, false);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
resolvedNodes.set(node, true);
|
|
148
|
+
}
|
|
149
|
+
nodesToDelete.push(node);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
for (const node of nodesToDelete) {
|
|
153
|
+
unresolvedNodes.delete(node);
|
|
154
|
+
}
|
|
155
|
+
// If not even one dependency was eliminated for any node,
|
|
156
|
+
// it means that only mutually cyclic nodes are left unresolved, so exit the loop.
|
|
157
|
+
if (!atLastOneDependencyResolvedInAnyNode) {
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// All remaining unresolved nodes must now be optional,
|
|
162
|
+
// since they are all mutually cyclic and all their non-cyclic grammar elements are known to be optional.
|
|
163
|
+
for (const node of unresolvedNodes.keys()) {
|
|
164
|
+
resolvedNodes.set(node, true);
|
|
165
|
+
unresolvedNodes.delete(node);
|
|
166
|
+
}
|
|
167
|
+
// Finally set the 'optional' property of all nodes based on the detected values.
|
|
168
|
+
for (const [node, isOptional] of resolvedNodes) {
|
|
169
|
+
node.optional = isOptional;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
export function detectAndErrorOnLeftRecursion(rootNode) {
|
|
173
|
+
const currentlyIteratedNodes = new Set();
|
|
174
|
+
function detect(node) {
|
|
175
|
+
if (currentlyIteratedNodes.has(node)) {
|
|
176
|
+
if (node.type === 'Nonterminal') {
|
|
177
|
+
throw new Error(`Detected left recursion for nonterminal '${node.name}'.`);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
throw new Error(`Detected left recursion for node: ${JSON.stringify(node, undefined, 4)}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
currentlyIteratedNodes.add(node);
|
|
184
|
+
switch (node.type) {
|
|
185
|
+
case 'Nonterminal':
|
|
186
|
+
case 'Repetition': {
|
|
187
|
+
detect(node.content);
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
case 'Sequence': {
|
|
191
|
+
for (const member of node.members) {
|
|
192
|
+
detect(member);
|
|
193
|
+
if (!member.optional) {
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
case 'Choice': {
|
|
200
|
+
for (const member of node.members) {
|
|
201
|
+
detect(member);
|
|
202
|
+
}
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
currentlyIteratedNodes.delete(node);
|
|
207
|
+
}
|
|
208
|
+
detect(rootNode);
|
|
209
|
+
}
|
|
210
|
+
// Walks the regexp-composer Pattern AST to validate its capture groups.
|
|
211
|
+
//
|
|
212
|
+
// The JavaScript RegExp engine collapses multiple named groups that share a name
|
|
213
|
+
// into a single entry, and cannot reliably report the ordering of a mix of named
|
|
214
|
+
// and unnamed groups. Both cases are problematic for the parser, so we detect them
|
|
215
|
+
// here, once, at grammar build time (before the RegExp is even compiled), instead of
|
|
216
|
+
// during parsing.
|
|
217
|
+
//
|
|
218
|
+
// Mirrors the AST traversal performed by regexp-composer's own 'isPatternOptional'.
|
|
219
|
+
export function validatePatternCaptureGroups(pattern) {
|
|
220
|
+
const seenNamedCaptureGroupNames = new Set();
|
|
221
|
+
let hasNamedCaptureGroup = false;
|
|
222
|
+
let hasUnnamedCaptureGroup = false;
|
|
223
|
+
function walk(node) {
|
|
224
|
+
if (isString(node)) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (isArray(node)) {
|
|
228
|
+
for (const element of node) {
|
|
229
|
+
walk(element);
|
|
230
|
+
}
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
switch (node.type) {
|
|
234
|
+
case 'capture': {
|
|
235
|
+
if (node.name !== undefined) {
|
|
236
|
+
if (seenNamedCaptureGroupNames.has(node.name)) {
|
|
237
|
+
throw new Error(`The regular expression pattern contains multiple named capture groups with the same name '${node.name}'. Named capture groups must have unique names.`);
|
|
238
|
+
}
|
|
239
|
+
seenNamedCaptureGroupNames.add(node.name);
|
|
240
|
+
hasNamedCaptureGroup = true;
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
hasUnnamedCaptureGroup = true;
|
|
244
|
+
}
|
|
245
|
+
walk(node.content);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
// Composite nodes that wrap content without creating a capturing group:
|
|
249
|
+
case 'possibly':
|
|
250
|
+
case 'zeroOrMore':
|
|
251
|
+
case 'oneOrMore':
|
|
252
|
+
case 'repeated':
|
|
253
|
+
case 'followedBy':
|
|
254
|
+
case 'notFollowedBy':
|
|
255
|
+
case 'precededBy':
|
|
256
|
+
case 'notPrecededBy': {
|
|
257
|
+
walk(node.content);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
case 'anyOf': {
|
|
261
|
+
for (const member of node.members) {
|
|
262
|
+
walk(member);
|
|
263
|
+
}
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
// These nodes never introduce capturing groups and contain only
|
|
267
|
+
// character-level tokens or backreferences:
|
|
268
|
+
case 'notAnyOfChars':
|
|
269
|
+
case 'specialToken':
|
|
270
|
+
case 'sameAs': {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
default: {
|
|
274
|
+
throw new Error(`Unrecognized pattern type: ${node.type}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
walk(pattern);
|
|
279
|
+
if (hasNamedCaptureGroup && hasUnnamedCaptureGroup) {
|
|
280
|
+
throw new Error(`The regular expression pattern contains a combination of named and unnamed capture groups. Due to limitations of the JavaScript RegExp engine, it is impossible to reliably identify the ordering of this combination, please use either all unnamed or all named capture groups, but not both.`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=StaticAnalysis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StaticAnalysis.js","sourceRoot":"","sources":["../../src/parser-generator/StaticAnalysis.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Pattern, SpecialToken } from 'regexp-composer';
|
|
2
|
+
import type { PatternTerminal, StringTerminal, Terminal } from './Grammar.js';
|
|
3
|
+
export declare class TerminalToText {
|
|
4
|
+
private stringifiedLengthLimit;
|
|
5
|
+
constructor(stringifiedLengthLimit: number);
|
|
6
|
+
stringifyTerminal(terminal: Terminal): string;
|
|
7
|
+
stringifyStringTerminal(terminal: StringTerminal): string;
|
|
8
|
+
stringifyPatternTerminal(terminal: PatternTerminal): string;
|
|
9
|
+
stringifyPattern(pattern: Pattern): string;
|
|
10
|
+
stringifyPatternStringLiteral(text: string): string;
|
|
11
|
+
stringifySpecialToken(token: SpecialToken): string;
|
|
12
|
+
truncateString(str: string, lengthLimit: number): string;
|
|
13
|
+
}
|
|
14
|
+
export declare function escapeChar(char: string): string;
|
|
15
|
+
//# sourceMappingURL=TerminalToText.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TerminalToText.d.ts","sourceRoot":"","sources":["../../src/parser-generator/TerminalToText.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAK7E,qBAAa,cAAc;IACd,OAAO,CAAC,sBAAsB;IAA1C,YAAoB,sBAAsB,EAAE,MAAM,EACjD;IAED,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAM5C;IAED,uBAAuB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAExD;IAED,wBAAwB,CAAC,QAAQ,EAAE,eAAe,UAmBjD;IAED,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CA+FzC;IAED,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUlD;IAED,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAgBjD;IAED,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAMvD;CACD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgB/C"}
|