clarity-pattern-parser 10.1.17 → 10.1.21
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 +41 -11
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +41 -11
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +41 -11
- package/dist/index.js.map +1 -1
- package/dist/intellisense/AutoComplete.d.ts +1 -0
- package/package.json +1 -1
- package/src/intellisense/AutoComplete.test.ts +85 -0
- package/src/intellisense/AutoComplete.ts +28 -9
- package/src/patterns/ExpressionPattern.ts +133 -0
- package/src/patterns/Options.ts +8 -4
- package/src/patterns/RightAssociatedPattern.ts +71 -0
- package/src/patterns/Sequence.ts +14 -6
|
@@ -21,6 +21,7 @@ export declare class AutoComplete {
|
|
|
21
21
|
private _text;
|
|
22
22
|
constructor(pattern: Pattern, options?: AutoCompleteOptions);
|
|
23
23
|
suggestForWithCursor(cursor: Cursor): Suggestion;
|
|
24
|
+
private getFurthestPosition;
|
|
24
25
|
suggestFor(text: string): Suggestion;
|
|
25
26
|
private _getAllOptions;
|
|
26
27
|
private _getOptionsFromErrors;
|
package/package.json
CHANGED
|
@@ -490,4 +490,89 @@ describe("AutoComplete", () => {
|
|
|
490
490
|
expect(results.options).toEqual(expected);
|
|
491
491
|
});
|
|
492
492
|
|
|
493
|
+
test("Multiple Complex Branches", () => {
|
|
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
|
+
new Literal("BC", "BC"),
|
|
501
|
+
])
|
|
502
|
+
]);
|
|
503
|
+
const branchTwo = new Sequence("branch-2", [
|
|
504
|
+
new Literal("space-2-1", " "),
|
|
505
|
+
new Literal("space-2-2", " "),
|
|
506
|
+
new Options('branch-2-options', [
|
|
507
|
+
new Literal("BA", "BA"),
|
|
508
|
+
new Literal("BB", "BB")
|
|
509
|
+
])
|
|
510
|
+
]);
|
|
511
|
+
const eitherBranch = new Options("either-branch", [branchOne, branchTwo]);
|
|
512
|
+
|
|
513
|
+
const autoComplete = new AutoComplete(eitherBranch);
|
|
514
|
+
const results = autoComplete.suggestFor(" B");
|
|
515
|
+
const expected = [
|
|
516
|
+
{ startIndex: 3, text: "A" },
|
|
517
|
+
{ startIndex: 3, text: "B" },
|
|
518
|
+
{ startIndex: 3, text: "C" },
|
|
519
|
+
];
|
|
520
|
+
expect(results.options).toEqual(expected);
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
test("Recursion With Or", () => {
|
|
525
|
+
const ref = new Reference("names");
|
|
526
|
+
const names = new Options("names", [
|
|
527
|
+
ref,
|
|
528
|
+
new Literal("john", "John"),
|
|
529
|
+
new Literal("jane", "Jane")
|
|
530
|
+
]);
|
|
531
|
+
|
|
532
|
+
const autoComplete = new AutoComplete(names);
|
|
533
|
+
const suggestion = autoComplete.suggestFor("Jo");
|
|
534
|
+
|
|
535
|
+
expect(suggestion.options).toEqual([
|
|
536
|
+
{ text: 'hn', startIndex: 2 }
|
|
537
|
+
]);
|
|
538
|
+
expect(suggestion.error?.endIndex).toBe(2);
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
test("Recursion With And", () => {
|
|
542
|
+
const firstNames = new Options("first-names", [
|
|
543
|
+
new Literal("john", "John"),
|
|
544
|
+
new Literal("jane", "Jane"),
|
|
545
|
+
]);
|
|
546
|
+
|
|
547
|
+
const lastNames = new Options("last-names", [
|
|
548
|
+
new Literal("doe", "Doe"),
|
|
549
|
+
new Literal("smith", "Smith"),
|
|
550
|
+
]);
|
|
551
|
+
|
|
552
|
+
const fullName = new Sequence("full-name", [
|
|
553
|
+
firstNames,
|
|
554
|
+
new Literal("space", " "),
|
|
555
|
+
lastNames
|
|
556
|
+
]);
|
|
557
|
+
|
|
558
|
+
const ref = new Reference("names");
|
|
559
|
+
const names = new Sequence("names", [
|
|
560
|
+
fullName,
|
|
561
|
+
ref,
|
|
562
|
+
lastNames,
|
|
563
|
+
]);
|
|
564
|
+
|
|
565
|
+
const autoComplete = new AutoComplete(names, {
|
|
566
|
+
greedyPatternNames: ["space"]
|
|
567
|
+
});
|
|
568
|
+
const suggestion = autoComplete.suggestFor("John");
|
|
569
|
+
|
|
570
|
+
expect(suggestion.options).toEqual([{
|
|
571
|
+
"text": " Doe",
|
|
572
|
+
"startIndex": 4
|
|
573
|
+
}, {
|
|
574
|
+
"text": " Smith",
|
|
575
|
+
"startIndex": 4
|
|
576
|
+
}]);
|
|
577
|
+
});
|
|
493
578
|
});
|
|
@@ -73,12 +73,8 @@ export class AutoComplete {
|
|
|
73
73
|
error = new ParseError(startIndex, endIndex, this._pattern);
|
|
74
74
|
errorAtIndex = startIndex;
|
|
75
75
|
} else if (!isComplete && this._cursor.hasError && this._cursor.furthestError != null) {
|
|
76
|
-
errorAtIndex = this.
|
|
77
|
-
error = this.
|
|
78
|
-
|
|
79
|
-
errorAtIndex = options.reduce((errorAtIndex, option) =>
|
|
80
|
-
Math.max(errorAtIndex, option.startIndex),
|
|
81
|
-
errorAtIndex);
|
|
76
|
+
errorAtIndex = this.getFurthestPosition(cursor);
|
|
77
|
+
error = new ParseError(errorAtIndex, errorAtIndex, this._pattern);
|
|
82
78
|
}
|
|
83
79
|
|
|
84
80
|
return {
|
|
@@ -92,6 +88,29 @@ export class AutoComplete {
|
|
|
92
88
|
|
|
93
89
|
}
|
|
94
90
|
|
|
91
|
+
private getFurthestPosition(cursor: Cursor): number {
|
|
92
|
+
const furthestError = cursor.furthestError;
|
|
93
|
+
const furthestMatch = cursor.allMatchedNodes[cursor.allMatchedNodes.length - 1];
|
|
94
|
+
|
|
95
|
+
if (furthestError && furthestMatch) {
|
|
96
|
+
if (furthestError.endIndex > furthestMatch.endIndex) {
|
|
97
|
+
return furthestMatch.endIndex;
|
|
98
|
+
} else {
|
|
99
|
+
return furthestError.endIndex;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (furthestError == null && furthestMatch != null) {
|
|
104
|
+
return furthestMatch.endIndex;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (furthestMatch == null && furthestError != null) {
|
|
108
|
+
return furthestError.endIndex;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
95
114
|
suggestFor(text: string): Suggestion {
|
|
96
115
|
return this.suggestForWithCursor(new Cursor(text));
|
|
97
116
|
}
|
|
@@ -101,9 +120,9 @@ export class AutoComplete {
|
|
|
101
120
|
const leafMatches = this._cursor.leafMatches.map((m) => this._createSuggestionsFromMatch(m)).flat();
|
|
102
121
|
const finalResults: SuggestionOption[] = [];
|
|
103
122
|
|
|
104
|
-
[...leafMatches, ...errorMatches].forEach(m=>{
|
|
105
|
-
const index = finalResults.findIndex(f=> m.text === f.text);
|
|
106
|
-
if (index === -1){
|
|
123
|
+
[...leafMatches, ...errorMatches].forEach(m => {
|
|
124
|
+
const index = finalResults.findIndex(f => m.text === f.text);
|
|
125
|
+
if (index === -1) {
|
|
107
126
|
finalResults.push(m);
|
|
108
127
|
}
|
|
109
128
|
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Node } from "../ast/Node";
|
|
2
|
+
import { Cursor } from "./Cursor";
|
|
3
|
+
import { ParseResult } from "./ParseResult";
|
|
4
|
+
import { Pattern } from "./Pattern";
|
|
5
|
+
|
|
6
|
+
let indexId = 0;
|
|
7
|
+
|
|
8
|
+
export class ExpressionPattern implements Pattern {
|
|
9
|
+
private _id: string;
|
|
10
|
+
private _type: string;
|
|
11
|
+
private _name: string;
|
|
12
|
+
private _parent: Pattern | null;
|
|
13
|
+
private _token: string;
|
|
14
|
+
private _firstIndex: number;
|
|
15
|
+
private _patterns: Pattern[];
|
|
16
|
+
private _unaryPatterns: Pattern[];
|
|
17
|
+
private _binaryPatterns: Pattern[];
|
|
18
|
+
|
|
19
|
+
get id(): string {
|
|
20
|
+
return this._id;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get type(): string {
|
|
24
|
+
return this._type;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get name(): string {
|
|
28
|
+
return this._name;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get token(): string {
|
|
32
|
+
return this._token;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get parent(): Pattern | null {
|
|
36
|
+
return this._parent;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
set parent(pattern: Pattern | null) {
|
|
40
|
+
this._parent = pattern;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get children(): Pattern[] {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
constructor(name: string, patterns: []) {
|
|
48
|
+
this._id = `expression-${indexId++}`;
|
|
49
|
+
this._type = "expression";
|
|
50
|
+
this._unaryPatterns = [];
|
|
51
|
+
this._binaryPatterns = [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private _organizePatterns() {
|
|
55
|
+
this._patterns.forEach((pattern) => {
|
|
56
|
+
if (this._isBinary(pattern)) {
|
|
57
|
+
this._binaryPatterns.push(pattern);
|
|
58
|
+
} else {
|
|
59
|
+
this._unaryPatterns.push();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private _isBinary(pattern: Pattern) {
|
|
65
|
+
if (pattern.type === "right-associated" && this._isBinaryPattern(pattern.children[0])) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return this._isBinaryPattern(pattern);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private _isBinaryPattern(pattern: Pattern) {
|
|
73
|
+
return pattern.type === "sequence" &&
|
|
74
|
+
pattern.children[0].type === "reference" &&
|
|
75
|
+
pattern.children[0].name === this.name &&
|
|
76
|
+
pattern.children[2].type === "reference" &&
|
|
77
|
+
pattern.children[2].name === this.name &&
|
|
78
|
+
pattern.children.length === 3;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private extractDelimiter(pattern) {
|
|
82
|
+
return pattern.children[1];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
parse(cursor: Cursor): Node | null {
|
|
86
|
+
this._firstIndex = cursor.index;
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
exec(text: string, record?: boolean | undefined): ParseResult {
|
|
91
|
+
throw new Error("Method not implemented.");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
test(text: string, record?: boolean | undefined): boolean {
|
|
95
|
+
throw new Error("Method not implemented.");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
clone(name?: string | undefined): Pattern {
|
|
99
|
+
throw new Error("Method not implemented.");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getTokens(): string[] {
|
|
103
|
+
throw new Error("Method not implemented.");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
getTokensAfter(childReference: Pattern): string[] {
|
|
107
|
+
throw new Error("Method not implemented.");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
getNextTokens(): string[] {
|
|
111
|
+
throw new Error("Method not implemented.");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
getPatterns(): Pattern[] {
|
|
115
|
+
throw new Error("Method not implemented.");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
getPatternsAfter(childReference: Pattern): Pattern[] {
|
|
119
|
+
throw new Error("Method not implemented.");
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
getNextPatterns(): Pattern[] {
|
|
123
|
+
throw new Error("Method not implemented.");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
find(predicate: (pattern: Pattern) => boolean): Pattern | null {
|
|
127
|
+
throw new Error("Method not implemented.");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
isEqual(pattern: Pattern): boolean {
|
|
131
|
+
throw new Error("Method not implemented.");
|
|
132
|
+
}
|
|
133
|
+
}
|
package/src/patterns/Options.ts
CHANGED
|
@@ -108,8 +108,6 @@ export class Options implements Pattern {
|
|
|
108
108
|
return null;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
111
|
private _tryToParse(cursor: Cursor): Node | null {
|
|
114
112
|
if (depthCache.getDepth(this._id, this._firstIndex) > 2) {
|
|
115
113
|
cursor.recordErrorAt(this._firstIndex, this._firstIndex, this);
|
|
@@ -144,8 +142,11 @@ export class Options implements Pattern {
|
|
|
144
142
|
getTokens(): string[] {
|
|
145
143
|
const tokens: string[] = [];
|
|
146
144
|
|
|
147
|
-
for (const
|
|
148
|
-
|
|
145
|
+
for (const pattern of this._children) {
|
|
146
|
+
if (pattern.type === "reference" && pattern.name === this.name) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
tokens.push(...pattern.getTokens());
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
return tokens;
|
|
@@ -171,6 +172,9 @@ export class Options implements Pattern {
|
|
|
171
172
|
const patterns: Pattern[] = [];
|
|
172
173
|
|
|
173
174
|
for (const pattern of this._children) {
|
|
175
|
+
if (pattern.type === "reference" && pattern.name === this.name) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
174
178
|
patterns.push(...pattern.getPatterns());
|
|
175
179
|
}
|
|
176
180
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Node } from "../ast/Node";
|
|
2
|
+
import { Cursor } from "./Cursor";
|
|
3
|
+
import { ParseResult } from "./ParseResult";
|
|
4
|
+
import { Pattern } from "./Pattern";
|
|
5
|
+
|
|
6
|
+
let indexId = 0;
|
|
7
|
+
|
|
8
|
+
export class RightAssociatedPattern implements Pattern {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly type: string;
|
|
11
|
+
readonly name: string;
|
|
12
|
+
private _parent: Pattern | null;
|
|
13
|
+
readonly children: Pattern[];
|
|
14
|
+
|
|
15
|
+
get parent() {
|
|
16
|
+
return this._parent;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
set parent(pattern: Pattern | null) {
|
|
20
|
+
this._parent = pattern;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
constructor(pattern: Pattern) {
|
|
24
|
+
this.id = `right-associated-${indexId++}`;
|
|
25
|
+
this.type = "right-associated";
|
|
26
|
+
this.name = "";
|
|
27
|
+
this.parent = null;
|
|
28
|
+
this.children = [pattern];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
parse(cursor: Cursor): Node | null {
|
|
32
|
+
return this.children[0].parse(cursor);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
exec(text: string, record?: boolean | undefined): ParseResult {
|
|
36
|
+
return this.children[0].exec(text, record);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
test(text: string, record?: boolean | undefined): boolean {
|
|
40
|
+
return this.children[0].test(text, record);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
clone(name?: string | undefined): Pattern {
|
|
44
|
+
return new RightAssociatedPattern(this.children[0]);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getTokens(): string[] {
|
|
48
|
+
return this.children[0].getTokens();
|
|
49
|
+
}
|
|
50
|
+
getTokensAfter(childReference: Pattern): string[] {
|
|
51
|
+
return this.children[0].getTokensAfter(childReference);
|
|
52
|
+
}
|
|
53
|
+
getNextTokens(): string[] {
|
|
54
|
+
return this.children[0].getNextTokens();
|
|
55
|
+
}
|
|
56
|
+
getPatterns(): Pattern[] {
|
|
57
|
+
return this.children[0].getPatterns();
|
|
58
|
+
}
|
|
59
|
+
getPatternsAfter(childReference: Pattern): Pattern[] {
|
|
60
|
+
return this.children[0].getPatternsAfter(childReference);
|
|
61
|
+
}
|
|
62
|
+
getNextPatterns(): Pattern[] {
|
|
63
|
+
return this.children[0].getNextPatterns();
|
|
64
|
+
}
|
|
65
|
+
find(predicate: (pattern: Pattern) => boolean): Pattern | null {
|
|
66
|
+
return this.children[0].find(predicate);
|
|
67
|
+
}
|
|
68
|
+
isEqual(pattern: Pattern): boolean {
|
|
69
|
+
return pattern.type === this.type && this.children.every((c, index) => c.isEqual(pattern.children[index]));
|
|
70
|
+
}
|
|
71
|
+
}
|
package/src/patterns/Sequence.ts
CHANGED
|
@@ -216,10 +216,13 @@ export class Sequence implements Pattern {
|
|
|
216
216
|
getTokens(): string[] {
|
|
217
217
|
const tokens: string[] = [];
|
|
218
218
|
|
|
219
|
-
for (const
|
|
220
|
-
|
|
219
|
+
for (const pattern of this._children) {
|
|
220
|
+
if (pattern.type === "reference" && pattern.name === this.name && pattern === this.children[0]) {
|
|
221
|
+
return tokens;
|
|
222
|
+
}
|
|
221
223
|
|
|
222
|
-
|
|
224
|
+
tokens.push(...pattern.getTokens());
|
|
225
|
+
if (pattern.type !== "optional" && pattern.type !== "not") {
|
|
223
226
|
break;
|
|
224
227
|
}
|
|
225
228
|
}
|
|
@@ -247,10 +250,15 @@ export class Sequence implements Pattern {
|
|
|
247
250
|
getPatterns(): Pattern[] {
|
|
248
251
|
const patterns: Pattern[] = [];
|
|
249
252
|
|
|
250
|
-
for (const
|
|
251
|
-
|
|
253
|
+
for (const pattern of this._children) {
|
|
254
|
+
|
|
255
|
+
if (pattern.type === "reference" && pattern.name === this.name && pattern === this.children[0]) {
|
|
256
|
+
return patterns;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
patterns.push(...pattern.getPatterns());
|
|
252
260
|
|
|
253
|
-
if (
|
|
261
|
+
if (pattern.type !== "optional" && pattern.type !== "not") {
|
|
254
262
|
break;
|
|
255
263
|
}
|
|
256
264
|
}
|