clarity-pattern-parser 10.0.3 → 10.0.5
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/index.browser.js +28 -15
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +28 -15
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +28 -15
- package/dist/index.js.map +1 -1
- package/dist/patterns/Cursor.d.ts +6 -0
- package/package.json +1 -1
- package/src/grammar/Grammar.ts +1 -1
- package/src/grammar/patterns/import.ts +1 -1
- package/src/grammar/patterns/optionsLiteral.ts +1 -1
- package/src/grammar/patterns/repeatLiteral.ts +8 -8
- package/src/intellisense/AutoComplete.ts +7 -7
- package/src/patterns/Cursor.ts +15 -4
- package/src/patterns/Options.test.ts +41 -0
- package/src/patterns/Options.ts +8 -1
|
@@ -2,6 +2,12 @@ import { Node } from "../ast/Node";
|
|
|
2
2
|
import { Match } from "./CursorHistory";
|
|
3
3
|
import { ParseError } from "./ParseError";
|
|
4
4
|
import { Pattern } from "./Pattern";
|
|
5
|
+
export declare class CyclicalParseError extends Error {
|
|
6
|
+
readonly patternId: string;
|
|
7
|
+
readonly patternName: string;
|
|
8
|
+
readonly cursorIndex: number;
|
|
9
|
+
constructor(patternId: string, patternName: string, cursorIndex: number);
|
|
10
|
+
}
|
|
5
11
|
export declare class Cursor {
|
|
6
12
|
private _text;
|
|
7
13
|
private _index;
|
package/package.json
CHANGED
package/src/grammar/Grammar.ts
CHANGED
|
@@ -322,7 +322,7 @@ export class Grammar {
|
|
|
322
322
|
const trimDivider = repeatNode.find(n => n.name === "trim-flag") != null;
|
|
323
323
|
const patterNode = repeatNode.children[1].type === "spaces" ? repeatNode.children[2] : repeatNode.children[1];
|
|
324
324
|
const pattern = this._buildPattern(patterNode);
|
|
325
|
-
const dividerSectionNode = repeatNode.find(n => n.name === "divider-section");
|
|
325
|
+
const dividerSectionNode = repeatNode.find(n => n.name === "repeat-divider-section");
|
|
326
326
|
|
|
327
327
|
const options: RepeatOptions = {
|
|
328
328
|
min: 1,
|
|
@@ -9,7 +9,7 @@ import { allSpaces, lineSpaces } from "./spaces";
|
|
|
9
9
|
import { Optional } from "../../patterns/Optional";
|
|
10
10
|
|
|
11
11
|
const optionalSpaces = new Optional("optional-spaces", allSpaces);
|
|
12
|
-
const optionalLineSpaces = new Optional("
|
|
12
|
+
const optionalLineSpaces = new Optional("optional-line-spaces", lineSpaces);
|
|
13
13
|
|
|
14
14
|
const importNameDivider = new Regex("import-name-divider", "(\\s+)?,(\\s+)?");
|
|
15
15
|
importNameDivider.setTokens([", "]);
|
|
@@ -7,7 +7,7 @@ import { Options } from "../../patterns/Options";
|
|
|
7
7
|
const patternName = name.clone("pattern-name");
|
|
8
8
|
patternName.setTokens(["[PATTERN_NAME]"]);
|
|
9
9
|
|
|
10
|
-
const patterns = new Options("
|
|
10
|
+
const patterns = new Options("options-patterns", [patternName, anonymousPattern]);
|
|
11
11
|
const defaultDivider = new Regex("default-divider", "\\s*[|]\\s*");
|
|
12
12
|
defaultDivider.setTokens(["|"]);
|
|
13
13
|
|
|
@@ -8,8 +8,8 @@ import { lineSpaces, spaces } from "./spaces";
|
|
|
8
8
|
import { Optional } from "../../patterns/Optional";
|
|
9
9
|
|
|
10
10
|
const optionalSpaces = new Optional("optional-spaces", spaces);
|
|
11
|
-
const openBracket = new Literal("open-bracket", "{");
|
|
12
|
-
const closeBracket = new Literal("close-bracket", "}");
|
|
11
|
+
const openBracket = new Literal("repeat-open-bracket", "{");
|
|
12
|
+
const closeBracket = new Literal("repeat-close-bracket", "}");
|
|
13
13
|
const comma = new Literal("comma", ",");
|
|
14
14
|
|
|
15
15
|
const integer = new Regex("integer", "([1-9][0-9]*)|0");
|
|
@@ -55,16 +55,16 @@ dividerComma.setTokens([", "]);
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
const patternName = name.clone("pattern-name");
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
const
|
|
58
|
+
const repeatPattern = new Options("repeat-pattern", [patternName, anonymousPattern]);
|
|
59
|
+
const repeatDividerPattern = repeatPattern.clone("repeat-divider-pattern");
|
|
60
|
+
const repeatDividerSection = new Sequence("repeat-divider-section", [dividerComma, repeatDividerPattern, trimFlag]);
|
|
61
|
+
const repeatOptionalDividerSection = new Optional("repeat-optional-divider-section", repeatDividerSection);
|
|
62
62
|
|
|
63
63
|
export const repeatLiteral = new Sequence("repeat-literal", [
|
|
64
64
|
openParen,
|
|
65
65
|
optionalSpaces,
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
repeatPattern,
|
|
67
|
+
repeatOptionalDividerSection,
|
|
68
68
|
optionalSpaces,
|
|
69
69
|
closeParen,
|
|
70
70
|
new Sequence("quantifier-section", [quantifier]),
|
|
@@ -48,11 +48,11 @@ export class AutoComplete {
|
|
|
48
48
|
errorAtIndex: 0,
|
|
49
49
|
cursor,
|
|
50
50
|
ast: null
|
|
51
|
-
}
|
|
51
|
+
};
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
let errorAtIndex = null;
|
|
55
|
-
let error = null
|
|
55
|
+
let error = null;
|
|
56
56
|
|
|
57
57
|
const ast = this._pattern.parse(this._cursor);
|
|
58
58
|
const isComplete = ast?.value === this._text;
|
|
@@ -74,7 +74,7 @@ export class AutoComplete {
|
|
|
74
74
|
errorAtIndex = startIndex;
|
|
75
75
|
} else if (!isComplete && this._cursor.hasError && this._cursor.furthestError != null) {
|
|
76
76
|
errorAtIndex = this._cursor.furthestError.endIndex;
|
|
77
|
-
error = this._cursor.furthestError
|
|
77
|
+
error = this._cursor.furthestError;
|
|
78
78
|
|
|
79
79
|
errorAtIndex = options.reduce((errorAtIndex, option) =>
|
|
80
80
|
Math.max(errorAtIndex, option.startIndex),
|
|
@@ -88,7 +88,7 @@ export class AutoComplete {
|
|
|
88
88
|
errorAtIndex,
|
|
89
89
|
cursor: cursor,
|
|
90
90
|
ast,
|
|
91
|
-
}
|
|
91
|
+
};
|
|
92
92
|
|
|
93
93
|
}
|
|
94
94
|
|
|
@@ -108,7 +108,7 @@ export class AutoComplete {
|
|
|
108
108
|
}
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
-
return finalResults
|
|
111
|
+
return finalResults;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
private _getOptionsFromErrors() {
|
|
@@ -159,7 +159,7 @@ export class AutoComplete {
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
private _getTokensForPattern(pattern: Pattern) {
|
|
162
|
-
const augmentedTokens = this._getAugmentedTokens(pattern)
|
|
162
|
+
const augmentedTokens = this._getAugmentedTokens(pattern);
|
|
163
163
|
|
|
164
164
|
if (this._options.greedyPatternNames != null && this._options.greedyPatternNames.includes(pattern.name)) {
|
|
165
165
|
const nextPatterns = pattern.getNextPatterns();
|
|
@@ -225,7 +225,7 @@ export class AutoComplete {
|
|
|
225
225
|
return {
|
|
226
226
|
text: text,
|
|
227
227
|
startIndex: furthestMatch,
|
|
228
|
-
}
|
|
228
|
+
};
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
static suggestFor(text: string, pattern: Pattern, options?: AutoCompleteOptions) {
|
package/src/patterns/Cursor.ts
CHANGED
|
@@ -3,6 +3,19 @@ import { CursorHistory, Match, Trace } from "./CursorHistory";
|
|
|
3
3
|
import { ParseError } from "./ParseError";
|
|
4
4
|
import { Pattern } from "./Pattern";
|
|
5
5
|
|
|
6
|
+
export class CyclicalParseError extends Error {
|
|
7
|
+
readonly patternId: string;
|
|
8
|
+
readonly patternName: string;
|
|
9
|
+
readonly cursorIndex: number;
|
|
10
|
+
|
|
11
|
+
constructor(patternId: string, patternName: string, cursorIndex: number) {
|
|
12
|
+
super("Cyclical Parse Error");
|
|
13
|
+
this.patternId = patternId;
|
|
14
|
+
this.patternName = patternName;
|
|
15
|
+
this.cursorIndex = cursorIndex;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
6
19
|
export class Cursor {
|
|
7
20
|
private _text: string;
|
|
8
21
|
private _index: number;
|
|
@@ -145,16 +158,14 @@ export class Cursor {
|
|
|
145
158
|
}
|
|
146
159
|
|
|
147
160
|
startParseWith(pattern: Pattern) {
|
|
148
|
-
const patternName = pattern.name;
|
|
149
|
-
|
|
150
161
|
const trace = {
|
|
151
162
|
pattern,
|
|
152
163
|
cursorIndex: this.index
|
|
153
164
|
};
|
|
154
165
|
|
|
155
|
-
const hasCycle = this._stackTrace.
|
|
166
|
+
const hasCycle = this._stackTrace.filter(t => t.pattern.id === pattern.id && this.index === t.cursorIndex).length > 1;
|
|
156
167
|
if (hasCycle) {
|
|
157
|
-
throw new
|
|
168
|
+
throw new CyclicalParseError(pattern.id, pattern.name, this.index);
|
|
158
169
|
}
|
|
159
170
|
|
|
160
171
|
this._history.pushStackTrace(trace);
|
|
@@ -5,6 +5,8 @@ import { Options } from "./Options";
|
|
|
5
5
|
import { Sequence } from "./Sequence";
|
|
6
6
|
import { Pattern } from "./Pattern";
|
|
7
7
|
import { Optional } from "./Optional";
|
|
8
|
+
import { Regex } from "./Regex";
|
|
9
|
+
import { Reference } from "./Reference";
|
|
8
10
|
|
|
9
11
|
describe("Options", () => {
|
|
10
12
|
test("Empty Options", () => {
|
|
@@ -271,4 +273,43 @@ describe("Options", () => {
|
|
|
271
273
|
const result = firstName.exec("Jane");
|
|
272
274
|
expect(result.ast?.value).toBe("Jane");
|
|
273
275
|
});
|
|
276
|
+
|
|
277
|
+
test("Cyclical Error Recorvery", () => {
|
|
278
|
+
const john = new Literal("john", "John");
|
|
279
|
+
const jane = new Literal("jane", "Jane");
|
|
280
|
+
const names = new Options("names", [john, jane]);
|
|
281
|
+
const questionMark = new Literal("?", "?");
|
|
282
|
+
const colon = new Literal(":", ":");
|
|
283
|
+
const space = new Regex("space", "\\s+");
|
|
284
|
+
const expressionReference = new Reference("expression");
|
|
285
|
+
const ternary = new Sequence("ternary", [expressionReference, space, questionMark, space, expressionReference, space, colon, space, expressionReference]);
|
|
286
|
+
const expression = new Options("expression", [names, ternary], true);
|
|
287
|
+
|
|
288
|
+
let result = expression.exec("John");
|
|
289
|
+
expect(result.ast?.toString()).toBe("John");
|
|
290
|
+
|
|
291
|
+
result = expression.exec("John ? Jane : John");
|
|
292
|
+
expect(result.ast?.toString()).toBe("John ? Jane : John");
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
result = expression.exec("John ? Jane : John ? Jane : John");
|
|
296
|
+
expect(result.ast?.toString()).toBe("John ? Jane : John ? Jane : John");
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("Deeper Cyclical Error Recorvery", () => {
|
|
300
|
+
const john = new Literal("john", "John");
|
|
301
|
+
const expressionReference = new Reference("expression");
|
|
302
|
+
|
|
303
|
+
const johns = new Sequence("johns", [john, expressionReference]);
|
|
304
|
+
const expression = new Options("expression", [
|
|
305
|
+
john,
|
|
306
|
+
johns,
|
|
307
|
+
], true);
|
|
308
|
+
|
|
309
|
+
let result = expression.exec("John");
|
|
310
|
+
expect(result.ast?.toString()).toBe("John");
|
|
311
|
+
|
|
312
|
+
result = expression.exec("JohnJohnJohnJohnJohn");
|
|
313
|
+
expect(result.ast?.toString()).toBe("JohnJohnJohnJohnJohn");
|
|
314
|
+
});
|
|
274
315
|
});
|
package/src/patterns/Options.ts
CHANGED
|
@@ -84,6 +84,7 @@ export class Options implements Pattern {
|
|
|
84
84
|
|
|
85
85
|
parse(cursor: Cursor): Node | null {
|
|
86
86
|
cursor.startParseWith(this);
|
|
87
|
+
|
|
87
88
|
this._firstIndex = cursor.index;
|
|
88
89
|
|
|
89
90
|
const node = this._tryToParse(cursor);
|
|
@@ -106,7 +107,13 @@ export class Options implements Pattern {
|
|
|
106
107
|
|
|
107
108
|
for (const pattern of this._children) {
|
|
108
109
|
cursor.moveTo(this._firstIndex);
|
|
109
|
-
|
|
110
|
+
let result = null;
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
result = pattern.parse(cursor);
|
|
114
|
+
} catch {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
110
117
|
|
|
111
118
|
if (this._isGreedy) {
|
|
112
119
|
results.push(result);
|