clarity-pattern-parser 10.1.20 → 10.1.22
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 +39 -12
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +39 -12
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +39 -12
- package/dist/index.js.map +1 -1
- package/dist/patterns/Reference.d.ts +1 -1
- package/dist/patterns/isRecursivePattern.d.ts +2 -0
- package/package.json +1 -1
- package/src/intellisense/AutoComplete.test.ts +85 -0
- package/src/patterns/ExpressionPattern.ts +36 -1
- package/src/patterns/Options.ts +9 -2
- package/src/patterns/Reference.ts +4 -4
- package/src/patterns/RightAssociatedPattern.ts +71 -0
- package/src/patterns/Sequence.ts +14 -6
- package/src/patterns/isRecursivePattern.ts +21 -0
|
@@ -20,7 +20,7 @@ export declare class Reference implements Pattern {
|
|
|
20
20
|
test(text: string): boolean;
|
|
21
21
|
exec(text: string, record?: boolean): ParseResult;
|
|
22
22
|
parse(cursor: Cursor): Node | null;
|
|
23
|
-
|
|
23
|
+
getReferencePatternSafely(): Pattern;
|
|
24
24
|
private _findPattern;
|
|
25
25
|
private _isValidPattern;
|
|
26
26
|
private _getRoot;
|
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
|
});
|
|
@@ -12,6 +12,9 @@ export class ExpressionPattern implements Pattern {
|
|
|
12
12
|
private _parent: Pattern | null;
|
|
13
13
|
private _token: string;
|
|
14
14
|
private _firstIndex: number;
|
|
15
|
+
private _patterns: Pattern[];
|
|
16
|
+
private _unaryPatterns: Pattern[];
|
|
17
|
+
private _binaryPatterns: Pattern[];
|
|
15
18
|
|
|
16
19
|
get id(): string {
|
|
17
20
|
return this._id;
|
|
@@ -41,10 +44,42 @@ export class ExpressionPattern implements Pattern {
|
|
|
41
44
|
return [];
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
constructor(){
|
|
47
|
+
constructor(name: string, patterns: []) {
|
|
45
48
|
this._id = `expression-${indexId++}`;
|
|
46
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
|
+
}
|
|
47
80
|
|
|
81
|
+
private extractDelimiter(pattern) {
|
|
82
|
+
return pattern.children[1];
|
|
48
83
|
}
|
|
49
84
|
|
|
50
85
|
parse(cursor: Cursor): Node | null {
|
package/src/patterns/Options.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { clonePatterns } from "./clonePatterns";
|
|
|
5
5
|
import { findPattern } from "./findPattern";
|
|
6
6
|
import { ParseResult } from "./ParseResult";
|
|
7
7
|
import { DepthCache } from './DepthCache';
|
|
8
|
+
import { isRecursivePattern } from "./isRecursivePattern";
|
|
8
9
|
|
|
9
10
|
/*
|
|
10
11
|
The following is created to reduce the overhead of recursion check.
|
|
@@ -142,8 +143,11 @@ export class Options implements Pattern {
|
|
|
142
143
|
getTokens(): string[] {
|
|
143
144
|
const tokens: string[] = [];
|
|
144
145
|
|
|
145
|
-
for (const
|
|
146
|
-
|
|
146
|
+
for (const pattern of this._children) {
|
|
147
|
+
if (isRecursivePattern(pattern)) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
tokens.push(...pattern.getTokens());
|
|
147
151
|
}
|
|
148
152
|
|
|
149
153
|
return tokens;
|
|
@@ -169,6 +173,9 @@ export class Options implements Pattern {
|
|
|
169
173
|
const patterns: Pattern[] = [];
|
|
170
174
|
|
|
171
175
|
for (const pattern of this._children) {
|
|
176
|
+
if (isRecursivePattern(pattern)) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
172
179
|
patterns.push(...pattern.getPatterns());
|
|
173
180
|
}
|
|
174
181
|
|
|
@@ -70,10 +70,10 @@ export class Reference implements Pattern {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
parse(cursor: Cursor): Node | null {
|
|
73
|
-
return this.
|
|
73
|
+
return this.getReferencePatternSafely().parse(cursor);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
getReferencePatternSafely(): Pattern {
|
|
77
77
|
if (this._pattern === null) {
|
|
78
78
|
let pattern: Pattern | null = null;
|
|
79
79
|
|
|
@@ -150,7 +150,7 @@ export class Reference implements Pattern {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
getTokens(): string[] {
|
|
153
|
-
return this.
|
|
153
|
+
return this.getReferencePatternSafely().getTokens();
|
|
154
154
|
}
|
|
155
155
|
|
|
156
156
|
getTokensAfter(_lastMatched: Pattern): string[] {
|
|
@@ -170,7 +170,7 @@ export class Reference implements Pattern {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
getPatterns(): Pattern[] {
|
|
173
|
-
return this.
|
|
173
|
+
return this.getReferencePatternSafely().getPatterns();
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
getPatternsAfter(_childReference: Pattern): Pattern[] {
|
|
@@ -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
|
@@ -5,6 +5,7 @@ import { clonePatterns } from "./clonePatterns";
|
|
|
5
5
|
import { filterOutNull } from "./filterOutNull";
|
|
6
6
|
import { findPattern } from "./findPattern";
|
|
7
7
|
import { DepthCache } from "./DepthCache";
|
|
8
|
+
import { isRecursivePattern } from "./isRecursivePattern";
|
|
8
9
|
|
|
9
10
|
const depthCache = new DepthCache();
|
|
10
11
|
let idIndex = 0;
|
|
@@ -216,10 +217,13 @@ export class Sequence implements Pattern {
|
|
|
216
217
|
getTokens(): string[] {
|
|
217
218
|
const tokens: string[] = [];
|
|
218
219
|
|
|
219
|
-
for (const
|
|
220
|
-
|
|
220
|
+
for (const pattern of this._children) {
|
|
221
|
+
if (isRecursivePattern(pattern) && pattern === this._children[0]) {
|
|
222
|
+
return tokens;
|
|
223
|
+
}
|
|
221
224
|
|
|
222
|
-
|
|
225
|
+
tokens.push(...pattern.getTokens());
|
|
226
|
+
if (pattern.type !== "optional" && pattern.type !== "not") {
|
|
223
227
|
break;
|
|
224
228
|
}
|
|
225
229
|
}
|
|
@@ -247,10 +251,14 @@ export class Sequence implements Pattern {
|
|
|
247
251
|
getPatterns(): Pattern[] {
|
|
248
252
|
const patterns: Pattern[] = [];
|
|
249
253
|
|
|
250
|
-
for (const
|
|
251
|
-
|
|
254
|
+
for (const pattern of this._children) {
|
|
255
|
+
if (isRecursivePattern(pattern) && 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
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Pattern } from "./Pattern";
|
|
2
|
+
import { Reference } from "./Reference";
|
|
3
|
+
|
|
4
|
+
export function isRecursivePattern(pattern: Pattern) {
|
|
5
|
+
let onPattern = pattern.parent;
|
|
6
|
+
let depth = 0;
|
|
7
|
+
|
|
8
|
+
while (onPattern != null) {
|
|
9
|
+
if (onPattern.id === pattern.id) {
|
|
10
|
+
depth++;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
onPattern = onPattern.parent;
|
|
14
|
+
|
|
15
|
+
if (depth > 1){
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return false;
|
|
21
|
+
}
|