clarity-pattern-parser 10.1.14 → 10.1.16
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 +70 -43
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +70 -43
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +70 -43
- package/dist/index.js.map +1 -1
- package/dist/patterns/Options.d.ts +3 -0
- package/dist/patterns/Sequence.d.ts +1 -0
- package/package.json +1 -1
- package/src/grammar/ComplexGrammar.test.ts +16 -0
- package/src/grammar/Grammar.test.ts +3 -0
- package/src/intellisense/AutoComplete.test.ts +28 -1
- package/src/intellisense/AutoComplete.ts +14 -11
- package/src/patterns/Options.ts +62 -11
- package/src/patterns/Sequence.ts +20 -7
- package/src/patterns/DepthCache.ts +0 -26
|
@@ -10,6 +10,7 @@ export declare class Options implements Pattern {
|
|
|
10
10
|
private _children;
|
|
11
11
|
private _isGreedy;
|
|
12
12
|
private _firstIndex;
|
|
13
|
+
private _recursiveDepth;
|
|
13
14
|
get id(): string;
|
|
14
15
|
get type(): string;
|
|
15
16
|
get name(): string;
|
|
@@ -17,11 +18,13 @@ export declare class Options implements Pattern {
|
|
|
17
18
|
set parent(pattern: Pattern | null);
|
|
18
19
|
get children(): Pattern[];
|
|
19
20
|
constructor(name: string, options: Pattern[], isGreedy?: boolean);
|
|
21
|
+
private _calculateRecursiveDepth;
|
|
20
22
|
private _assignChildrenToParent;
|
|
21
23
|
test(text: string): boolean;
|
|
22
24
|
exec(text: string, record?: boolean): ParseResult;
|
|
23
25
|
parse(cursor: Cursor): Node | null;
|
|
24
26
|
private _tryToParse;
|
|
27
|
+
private _isBeyondRecursiveDepth;
|
|
25
28
|
getTokens(): string[];
|
|
26
29
|
getTokensAfter(_childReference: Pattern): string[];
|
|
27
30
|
getNextTokens(): string[];
|
package/package.json
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { patterns } from "./patterns";
|
|
2
|
+
|
|
3
|
+
describe("Complex Grammar Tests", () => {
|
|
4
|
+
test("Nested", () => {
|
|
5
|
+
const { expression } = patterns`
|
|
6
|
+
integer = /[0-9]+/
|
|
7
|
+
variable = /[A-Za-z][A-Za-z0-9]*/
|
|
8
|
+
space = /\\s+/
|
|
9
|
+
or-expression = expression + space? + "||" + space? + expression
|
|
10
|
+
mult-expression = expression + space? + "*" + space? + expression
|
|
11
|
+
expression = or-expression | mult-expression | integer | variable
|
|
12
|
+
`;
|
|
13
|
+
const result = expression.exec("a * b || d * e");
|
|
14
|
+
expect(result.ast?.toString()).toBe("a * b || d * e");
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -9,6 +9,7 @@ import { Repeat } from "../patterns/Repeat";
|
|
|
9
9
|
import { Grammar } from "./Grammar";
|
|
10
10
|
import { Optional } from "../patterns/Optional";
|
|
11
11
|
import { Context } from "../patterns/Context";
|
|
12
|
+
import { patterns } from "..";
|
|
12
13
|
|
|
13
14
|
describe("Grammar", () => {
|
|
14
15
|
test("Literal", () => {
|
|
@@ -569,4 +570,6 @@ describe("Grammar", () => {
|
|
|
569
570
|
const result = fullname.exec("John Doe");
|
|
570
571
|
expect(result?.ast?.value).toBe("John Doe");
|
|
571
572
|
});
|
|
573
|
+
|
|
574
|
+
|
|
572
575
|
});
|
|
@@ -490,4 +490,31 @@ describe("AutoComplete", () => {
|
|
|
490
490
|
expect(results.options).toEqual(expected);
|
|
491
491
|
});
|
|
492
492
|
|
|
493
|
-
|
|
493
|
+
test("Furthest Error", () => {
|
|
494
|
+
const branchOne = new Sequence("branch-1", [
|
|
495
|
+
new Literal("space-1-1", " "),
|
|
496
|
+
new Literal("space-1-2", " "),
|
|
497
|
+
new Options('branch-1-options', [
|
|
498
|
+
new Literal("AA", "AA"),
|
|
499
|
+
new Literal("AB", "AB"),
|
|
500
|
+
])
|
|
501
|
+
]);
|
|
502
|
+
const branchTwo = new Sequence("branch-2", [
|
|
503
|
+
new Literal("space-2-1", " "),
|
|
504
|
+
new Literal("space-2-2", " "),
|
|
505
|
+
new Options('branch-2-options', [
|
|
506
|
+
new Literal("BA", "BA"),
|
|
507
|
+
new Literal("BB", "BB")
|
|
508
|
+
])
|
|
509
|
+
]);
|
|
510
|
+
const eitherBranch = new Options("either-branch", [branchOne, branchTwo]);
|
|
511
|
+
|
|
512
|
+
const autoComplete = new AutoComplete(eitherBranch);
|
|
513
|
+
const results = autoComplete.suggestFor(" B");
|
|
514
|
+
const expected = [
|
|
515
|
+
{ startIndex: 3, text: "A" },
|
|
516
|
+
{ startIndex: 3, text: "B" },
|
|
517
|
+
];
|
|
518
|
+
expect(results.options).toEqual(expected);
|
|
519
|
+
})
|
|
520
|
+
});
|
|
@@ -76,9 +76,10 @@ export class AutoComplete {
|
|
|
76
76
|
errorAtIndex = this._cursor.furthestError.endIndex;
|
|
77
77
|
error = this._cursor.furthestError;
|
|
78
78
|
|
|
79
|
-
errorAtIndex = options.reduce(
|
|
80
|
-
Math.max(errorAtIndex, option.startIndex),
|
|
81
|
-
errorAtIndex
|
|
79
|
+
errorAtIndex = options.reduce(
|
|
80
|
+
(errorAtIndex, option) => Math.max(errorAtIndex, option.startIndex),
|
|
81
|
+
errorAtIndex
|
|
82
|
+
);
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
return {
|
|
@@ -98,17 +99,19 @@ export class AutoComplete {
|
|
|
98
99
|
|
|
99
100
|
private _getAllOptions() {
|
|
100
101
|
const errorMatches = this._getOptionsFromErrors();
|
|
101
|
-
const
|
|
102
|
-
const finalResults: SuggestionOption[] = [];
|
|
102
|
+
const validLeafMatches = this._cursor.leafMatches.filter(v => v.node?.lastIndex === this._cursor.getLastIndex())
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
const leafMatchSuggestions = validLeafMatches.map((m) => this._createSuggestionsFromMatch(m)).flat();
|
|
105
|
+
const uniqueResults: SuggestionOption[] = [];
|
|
106
|
+
|
|
107
|
+
[...leafMatchSuggestions, ...errorMatches].forEach(m=>{
|
|
108
|
+
const index = uniqueResults.findIndex(f => m.text === f.text);
|
|
106
109
|
if (index === -1){
|
|
107
|
-
|
|
110
|
+
uniqueResults.push(m);
|
|
108
111
|
}
|
|
109
112
|
});
|
|
110
113
|
|
|
111
|
-
return
|
|
114
|
+
return uniqueResults;
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
private _getOptionsFromErrors() {
|
|
@@ -118,9 +121,9 @@ export class AutoComplete {
|
|
|
118
121
|
const tokens = this._getTokensForPattern(e.pattern);
|
|
119
122
|
const adjustedTokens = tokens.map(t => t.slice(e.endIndex - e.startIndex));
|
|
120
123
|
return this._createSuggestions(e.endIndex, adjustedTokens);
|
|
121
|
-
});
|
|
124
|
+
}).flat();
|
|
122
125
|
|
|
123
|
-
return suggestions
|
|
126
|
+
return suggestions;
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
private _createSuggestionsFromRoot(): SuggestionOption[] {
|
package/src/patterns/Options.ts
CHANGED
|
@@ -4,13 +4,11 @@ import { Pattern } from "./Pattern";
|
|
|
4
4
|
import { clonePatterns } from "./clonePatterns";
|
|
5
5
|
import { findPattern } from "./findPattern";
|
|
6
6
|
import { ParseResult } from "./ParseResult";
|
|
7
|
-
import {
|
|
7
|
+
import { Context } from "./Context";
|
|
8
8
|
|
|
9
9
|
/*
|
|
10
10
|
The following is created to reduce the overhead of recursion check.
|
|
11
11
|
*/
|
|
12
|
-
|
|
13
|
-
const depthCache = new DepthCache();
|
|
14
12
|
let idIndex = 0;
|
|
15
13
|
|
|
16
14
|
export class Options implements Pattern {
|
|
@@ -21,6 +19,7 @@ export class Options implements Pattern {
|
|
|
21
19
|
private _children: Pattern[];
|
|
22
20
|
private _isGreedy: boolean;
|
|
23
21
|
private _firstIndex: number;
|
|
22
|
+
private _recursiveDepth: number;
|
|
24
23
|
|
|
25
24
|
get id(): string {
|
|
26
25
|
return this._id;
|
|
@@ -61,6 +60,42 @@ export class Options implements Pattern {
|
|
|
61
60
|
this._children = children;
|
|
62
61
|
this._firstIndex = 0;
|
|
63
62
|
this._isGreedy = isGreedy;
|
|
63
|
+
this._recursiveDepth = this._calculateRecursiveDepth();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private _calculateRecursiveDepth() {
|
|
67
|
+
let depth = 0;
|
|
68
|
+
|
|
69
|
+
this._children.forEach((child) => {
|
|
70
|
+
let hasReference = false;
|
|
71
|
+
let descendant = child.children[0];
|
|
72
|
+
|
|
73
|
+
while (descendant != null) {
|
|
74
|
+
if (descendant.type === "reference" && descendant.name === this.name) {
|
|
75
|
+
hasReference = true;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (descendant.type === "context") {
|
|
80
|
+
const pattern = (descendant as Context).getPatternWithinContext(this.name);
|
|
81
|
+
if (pattern != null) {
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
descendant = descendant.children[0];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (hasReference) {
|
|
90
|
+
depth++;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (depth === 0) {
|
|
95
|
+
depth = this.children.length;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return depth;
|
|
64
99
|
}
|
|
65
100
|
|
|
66
101
|
private _assignChildrenToParent(children: Pattern[]): void {
|
|
@@ -90,13 +125,9 @@ export class Options implements Pattern {
|
|
|
90
125
|
|
|
91
126
|
parse(cursor: Cursor): Node | null {
|
|
92
127
|
// This is a cache to help with speed
|
|
93
|
-
this._firstIndex = cursor.index;
|
|
94
|
-
depthCache.incrementDepth(this._id, this._firstIndex);
|
|
95
|
-
|
|
96
128
|
this._firstIndex = cursor.index;
|
|
97
129
|
const node = this._tryToParse(cursor);
|
|
98
130
|
|
|
99
|
-
depthCache.decrementDepth(this._id, this._firstIndex);
|
|
100
131
|
|
|
101
132
|
if (node != null) {
|
|
102
133
|
cursor.moveTo(node.lastIndex);
|
|
@@ -109,14 +140,15 @@ export class Options implements Pattern {
|
|
|
109
140
|
}
|
|
110
141
|
|
|
111
142
|
private _tryToParse(cursor: Cursor): Node | null {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
143
|
+
let children = this.children;
|
|
144
|
+
|
|
145
|
+
if (this._isBeyondRecursiveDepth()) {
|
|
146
|
+
children = children.slice().reverse();
|
|
115
147
|
}
|
|
116
148
|
|
|
117
149
|
const results: (Node | null)[] = [];
|
|
118
150
|
|
|
119
|
-
for (const pattern of
|
|
151
|
+
for (const pattern of children) {
|
|
120
152
|
cursor.moveTo(this._firstIndex);
|
|
121
153
|
let result = null;
|
|
122
154
|
|
|
@@ -139,6 +171,25 @@ export class Options implements Pattern {
|
|
|
139
171
|
return nonNullResults[0] || null;
|
|
140
172
|
}
|
|
141
173
|
|
|
174
|
+
private _isBeyondRecursiveDepth() {
|
|
175
|
+
let depth = 0;
|
|
176
|
+
let pattern: Pattern | null = this;
|
|
177
|
+
|
|
178
|
+
while (pattern != null) {
|
|
179
|
+
if (pattern.id === this.id) {
|
|
180
|
+
depth++;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (depth > this._recursiveDepth) {
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
pattern = pattern.parent;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
142
193
|
getTokens(): string[] {
|
|
143
194
|
const tokens: string[] = [];
|
|
144
195
|
|
package/src/patterns/Sequence.ts
CHANGED
|
@@ -4,9 +4,7 @@ import { Node } from "../ast/Node";
|
|
|
4
4
|
import { clonePatterns } from "./clonePatterns";
|
|
5
5
|
import { filterOutNull } from "./filterOutNull";
|
|
6
6
|
import { findPattern } from "./findPattern";
|
|
7
|
-
import { DepthCache } from "./DepthCache";
|
|
8
7
|
|
|
9
|
-
const depthCache = new DepthCache();
|
|
10
8
|
let idIndex = 0;
|
|
11
9
|
|
|
12
10
|
export class Sequence implements Pattern {
|
|
@@ -87,12 +85,8 @@ export class Sequence implements Pattern {
|
|
|
87
85
|
parse(cursor: Cursor): Node | null {
|
|
88
86
|
// This is a cache to help with speed
|
|
89
87
|
this._firstIndex = cursor.index;
|
|
90
|
-
depthCache.incrementDepth(this._id, this._firstIndex);
|
|
91
|
-
|
|
92
88
|
this._nodes = [];
|
|
93
|
-
|
|
94
89
|
const passed = this.tryToParse(cursor);
|
|
95
|
-
depthCache.decrementDepth(this._id, this._firstIndex);
|
|
96
90
|
|
|
97
91
|
if (passed) {
|
|
98
92
|
const node = this.createNode(cursor);
|
|
@@ -108,7 +102,7 @@ export class Sequence implements Pattern {
|
|
|
108
102
|
}
|
|
109
103
|
|
|
110
104
|
private tryToParse(cursor: Cursor): boolean {
|
|
111
|
-
if (
|
|
105
|
+
if (this._isBeyondRecursiveDepth()) {
|
|
112
106
|
cursor.recordErrorAt(this._firstIndex, this._firstIndex, this);
|
|
113
107
|
return false;
|
|
114
108
|
}
|
|
@@ -173,6 +167,25 @@ export class Sequence implements Pattern {
|
|
|
173
167
|
return passed;
|
|
174
168
|
}
|
|
175
169
|
|
|
170
|
+
private _isBeyondRecursiveDepth() {
|
|
171
|
+
let depth = 0;
|
|
172
|
+
let pattern: Pattern | null = this;
|
|
173
|
+
|
|
174
|
+
while (pattern != null) {
|
|
175
|
+
if (pattern.id === this.id && this._firstIndex === (pattern as Sequence)._firstIndex) {
|
|
176
|
+
depth++;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (depth > 1) {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
pattern = pattern.parent;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
176
189
|
private getLastValidNode(): Node | null {
|
|
177
190
|
const nodes = filterOutNull(this._nodes);
|
|
178
191
|
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
export class DepthCache {
|
|
2
|
-
private _depthMap: Record<string, Record<number, number>> = {};
|
|
3
|
-
|
|
4
|
-
getDepth(name: string, cursorIndex: number) {
|
|
5
|
-
if (this._depthMap[name] == null) {
|
|
6
|
-
this._depthMap[name] = {};
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
if (this._depthMap[name][cursorIndex] == null) {
|
|
10
|
-
this._depthMap[name][cursorIndex] = 0;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return this._depthMap[name][cursorIndex];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
incrementDepth(name: string, cursorIndex: number) {
|
|
18
|
-
const depth = this.getDepth(name, cursorIndex);
|
|
19
|
-
this._depthMap[name][cursorIndex] = depth + 1;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
decrementDepth(name: string, cursorIndex: number) {
|
|
23
|
-
const depth = this.getDepth(name, cursorIndex);
|
|
24
|
-
this._depthMap[name][cursorIndex] = depth - 1;
|
|
25
|
-
}
|
|
26
|
-
}
|