clarity-pattern-parser 10.0.3 → 10.0.4
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 +33 -15
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +33 -15
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +33 -15
- package/dist/index.js.map +1 -1
- package/dist/patterns/Cursor.d.ts +5 -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 +13 -4
- package/src/patterns/Options.test.ts +24 -0
- package/src/patterns/Options.ts +13 -1
|
@@ -2,6 +2,11 @@ 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
|
+
constructor(patternId: string, patternName: string);
|
|
9
|
+
}
|
|
5
10
|
export declare class Cursor {
|
|
6
11
|
private _text;
|
|
7
12
|
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,17 @@ 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
|
+
|
|
10
|
+
constructor(patternId: string, patternName: string) {
|
|
11
|
+
super("Cyclical Parse Error");
|
|
12
|
+
this.patternId = patternId;
|
|
13
|
+
this.patternName = patternName;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
6
17
|
export class Cursor {
|
|
7
18
|
private _text: string;
|
|
8
19
|
private _index: number;
|
|
@@ -145,16 +156,14 @@ export class Cursor {
|
|
|
145
156
|
}
|
|
146
157
|
|
|
147
158
|
startParseWith(pattern: Pattern) {
|
|
148
|
-
const patternName = pattern.name;
|
|
149
|
-
|
|
150
159
|
const trace = {
|
|
151
160
|
pattern,
|
|
152
161
|
cursorIndex: this.index
|
|
153
162
|
};
|
|
154
163
|
|
|
155
|
-
const hasCycle = this._stackTrace.
|
|
164
|
+
const hasCycle = this._stackTrace.filter(t => t.pattern.id === pattern.id && this.index === t.cursorIndex).length > 1;
|
|
156
165
|
if (hasCycle) {
|
|
157
|
-
throw new
|
|
166
|
+
throw new CyclicalParseError(pattern.id, pattern.name);
|
|
158
167
|
}
|
|
159
168
|
|
|
160
169
|
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,26 @@ 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
|
+
});
|
|
274
298
|
});
|
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,18 @@ 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 (error: any) {
|
|
115
|
+
if (error.patternId === this._id) {
|
|
116
|
+
continue;
|
|
117
|
+
} else {
|
|
118
|
+
cursor.endParse();
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
110
122
|
|
|
111
123
|
if (this._isGreedy) {
|
|
112
124
|
results.push(result);
|