clarity-pattern-parser 5.0.0 → 6.0.2
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/README.md +328 -38
- package/TODO.md +63 -7
- package/dist/ast/Node.d.ts +8 -2
- package/dist/index.browser.js +520 -205
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.esm.js +519 -206
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +520 -205
- package/dist/index.js.map +1 -1
- package/dist/intellisense/AutoComplete.d.ts +34 -0
- package/dist/intellisense/Suggestion.d.ts +10 -0
- package/dist/intellisense/SuggestionOption.d.ts +4 -0
- package/dist/patterns/And.d.ts +8 -7
- package/dist/patterns/Cursor.d.ts +6 -4
- package/dist/patterns/CursorHistory.d.ts +2 -2
- package/dist/patterns/Literal.d.ts +8 -8
- package/dist/patterns/Not.d.ts +9 -5
- package/dist/patterns/Or.d.ts +8 -5
- package/dist/patterns/Pattern.d.ts +8 -4
- package/dist/patterns/Reference.d.ts +11 -7
- package/dist/patterns/Regex.d.ts +8 -8
- package/dist/patterns/Repeat.d.ts +8 -7
- package/package.json +1 -1
- package/src/ast/Node.test.ts +116 -0
- package/src/ast/Node.ts +71 -5
- package/src/index.ts +14 -3
- package/src/intellisense/AutoComplete.test.ts +168 -23
- package/src/intellisense/AutoComplete.ts +102 -21
- package/src/intellisense/Suggestion.ts +3 -4
- package/src/intellisense/javascript/Javascript.test.ts +86 -62
- package/src/intellisense/javascript/{expressionStatement.ts → assignment.ts} +7 -8
- package/src/intellisense/javascript/escapedCharacter.ts +0 -1
- package/src/intellisense/javascript/exponent.ts +0 -2
- package/src/intellisense/javascript/expression.ts +44 -26
- package/src/intellisense/javascript/fraction.ts +0 -2
- package/src/intellisense/javascript/infixOperator.ts +6 -2
- package/src/intellisense/javascript/keywords.ts +3 -0
- package/src/intellisense/javascript/objectAccess.ts +9 -0
- package/src/intellisense/javascript/objectLiteral.ts +3 -3
- package/src/intellisense/javascript/propertyAccess.ts +8 -3
- package/src/intellisense/javascript/stringLiteral.ts +16 -8
- package/src/patterns/And.test.ts +74 -50
- package/src/patterns/And.ts +72 -36
- package/src/patterns/Cursor.ts +17 -14
- package/src/patterns/CursorHistory.ts +8 -8
- package/src/patterns/Literal.test.ts +79 -38
- package/src/patterns/Literal.ts +34 -41
- package/src/patterns/Not.test.ts +99 -8
- package/src/patterns/Not.ts +58 -14
- package/src/patterns/Or.test.ts +128 -13
- package/src/patterns/Or.ts +46 -13
- package/src/patterns/Pattern.ts +8 -4
- package/src/patterns/Reference.test.ts +127 -28
- package/src/patterns/Reference.ts +62 -32
- package/src/patterns/Regex.test.ts +76 -35
- package/src/patterns/Regex.ts +35 -43
- package/src/patterns/Repeat.test.ts +72 -41
- package/src/patterns/Repeat.ts +55 -38
- package/src/patterns/getNextPattern.test.ts +0 -39
- package/src/patterns/getNextPattern.ts +0 -18
|
@@ -3,6 +3,7 @@ import { Regex } from "./Regex";
|
|
|
3
3
|
import { Node } from "../ast/Node"
|
|
4
4
|
import { And } from "./And";
|
|
5
5
|
import { Literal } from "./Literal";
|
|
6
|
+
import { Pattern } from "./Pattern";
|
|
6
7
|
|
|
7
8
|
describe("Regex", () => {
|
|
8
9
|
test("Empty String", () => {
|
|
@@ -38,52 +39,24 @@ describe("Regex", () => {
|
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
test("Get Tokens", () => {
|
|
41
|
-
const
|
|
42
|
-
new Regex("a", "A"),
|
|
43
|
-
new Regex("b", "B")
|
|
44
|
-
]);
|
|
45
|
-
|
|
46
|
-
const a = parent.children[0] as Regex;
|
|
47
|
-
const b = parent.children[1] as Regex;
|
|
42
|
+
const a = new Regex("a", "A");
|
|
48
43
|
|
|
49
44
|
a.setTokens(["A"]);
|
|
50
|
-
b.setTokens(["B"]);
|
|
51
|
-
|
|
52
|
-
let tokens = a.getTokens();
|
|
53
|
-
let expectedTokens = ["A"];
|
|
54
|
-
|
|
55
|
-
expect(tokens).toEqual(expectedTokens);
|
|
56
|
-
|
|
57
|
-
a.enableContextualTokenAggregation();
|
|
58
|
-
|
|
59
|
-
tokens = a.getTokens();
|
|
60
|
-
expectedTokens = ["AB"];
|
|
61
45
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
a.disableContextualTokenAggregation();
|
|
65
|
-
|
|
66
|
-
tokens = a.getTokens();
|
|
67
|
-
expectedTokens = ["A"];
|
|
46
|
+
const tokens = a.getTokens();
|
|
47
|
+
const expectedTokens = ["A"];
|
|
68
48
|
|
|
69
49
|
expect(tokens).toEqual(expectedTokens);
|
|
70
50
|
});
|
|
71
51
|
|
|
72
|
-
test("Get
|
|
52
|
+
test("Get Tokens After", () => {
|
|
73
53
|
const regex = new Regex("a", "A");
|
|
74
|
-
const tokens = regex.
|
|
54
|
+
const tokens = regex.getTokensAfter(new Literal("bogus", "bogus"));
|
|
75
55
|
const expected: string[] = [];
|
|
76
56
|
|
|
77
57
|
expect(tokens).toEqual(expected)
|
|
78
58
|
});
|
|
79
59
|
|
|
80
|
-
test("Get Next Pattern", () => {
|
|
81
|
-
const regex = new Regex("a", "A");
|
|
82
|
-
const nextPattern = regex.getNextPattern();
|
|
83
|
-
|
|
84
|
-
expect(nextPattern).toBeNull()
|
|
85
|
-
});
|
|
86
|
-
|
|
87
60
|
test("Properties", () => {
|
|
88
61
|
const regex = new Regex("a", "A");
|
|
89
62
|
|
|
@@ -93,9 +66,77 @@ describe("Regex", () => {
|
|
|
93
66
|
expect(regex.children).toEqual([]);
|
|
94
67
|
});
|
|
95
68
|
|
|
96
|
-
test("
|
|
69
|
+
test("Exec", () => {
|
|
97
70
|
const regex = new Regex("a", "A");
|
|
98
|
-
const { ast: result } = regex.
|
|
71
|
+
const { ast: result } = regex.exec("B");
|
|
99
72
|
expect(result).toBeNull();
|
|
100
73
|
});
|
|
74
|
+
|
|
75
|
+
test("Test With Match", () => {
|
|
76
|
+
const regex = new Regex("a", "A");
|
|
77
|
+
const result = regex.test("A");
|
|
78
|
+
expect(result).toBeTruthy();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("Test With No Match", () => {
|
|
82
|
+
const regex = new Regex("a", "A");
|
|
83
|
+
const result = regex.test("B");
|
|
84
|
+
expect(result).toBeFalsy();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("Get Next Tokens", () => {
|
|
88
|
+
const parent = new And("parent", [new Regex("a", "A"), new Literal("b", "B")]);
|
|
89
|
+
const aClone = parent.findPattern(p => p.name === "a") as Pattern;
|
|
90
|
+
const tokens = aClone.getNextTokens();
|
|
91
|
+
|
|
92
|
+
expect(tokens).toEqual(["B"]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("Get Next Tokens With Null Parent", () => {
|
|
96
|
+
const a = new Regex("a", "A")
|
|
97
|
+
const tokens = a.getNextTokens();
|
|
98
|
+
|
|
99
|
+
expect(tokens).toEqual([]);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("Get Patterns", () => {
|
|
103
|
+
const a = new Regex("a", "A");
|
|
104
|
+
|
|
105
|
+
const tokens = a.getPatterns();
|
|
106
|
+
const expectedTokens = [a];
|
|
107
|
+
|
|
108
|
+
expect(tokens).toEqual(expectedTokens);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("Get Patterns After", () => {
|
|
112
|
+
const a = new Regex("a", "A")
|
|
113
|
+
const patterns = a.getPatternsAfter(new Literal("bogus", "bogus"));
|
|
114
|
+
|
|
115
|
+
expect(patterns).toEqual([]);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("Find Pattern", () => {
|
|
119
|
+
const a = new Regex("a", "A")
|
|
120
|
+
const pattern = a.findPattern(p => p.name === "other");
|
|
121
|
+
|
|
122
|
+
expect(pattern).toBeNull();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("Get Next Patterns", () => {
|
|
126
|
+
const parent = new And("parent", [new Regex("a", "A"), new Literal("b", "B")]);
|
|
127
|
+
const aClone = parent.findPattern(p => p.name === "a") as Pattern;
|
|
128
|
+
const bClone = parent.findPattern(p => p.name === "b") as Pattern;
|
|
129
|
+
const patterns = aClone.getNextPatterns();
|
|
130
|
+
|
|
131
|
+
expect(patterns.length).toBe(1);
|
|
132
|
+
expect(patterns[0]).toBe(bClone);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("Get Next Patterns With Null Parent", () => {
|
|
136
|
+
const a = new Regex("a", "A")
|
|
137
|
+
const patterns = a.getNextPatterns();
|
|
138
|
+
|
|
139
|
+
expect(patterns).toEqual([])
|
|
140
|
+
});
|
|
141
|
+
|
|
101
142
|
});
|
package/src/patterns/Regex.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Node } from "../ast/Node";
|
|
2
2
|
import { Pattern } from "./Pattern";
|
|
3
3
|
import { Cursor } from "./Cursor";
|
|
4
|
-
import { getNextPattern } from "./getNextPattern";
|
|
5
4
|
|
|
6
5
|
export class Regex implements Pattern {
|
|
7
6
|
private _type: string;
|
|
@@ -14,8 +13,6 @@ export class Regex implements Pattern {
|
|
|
14
13
|
private _cursor: Cursor | null = null;
|
|
15
14
|
private _substring: string = "";
|
|
16
15
|
private _tokens: string[] = [];
|
|
17
|
-
private _hasContextualTokenAggregation = false;
|
|
18
|
-
private _isRetrievingContextualTokens: boolean = false;
|
|
19
16
|
|
|
20
17
|
get type(): string {
|
|
21
18
|
return this._type;
|
|
@@ -71,12 +68,19 @@ export class Regex implements Pattern {
|
|
|
71
68
|
}
|
|
72
69
|
}
|
|
73
70
|
|
|
74
|
-
|
|
71
|
+
test(text: string) {
|
|
75
72
|
const cursor = new Cursor(text);
|
|
76
|
-
const ast = this.parse(cursor)
|
|
73
|
+
const ast = this.parse(cursor);
|
|
74
|
+
|
|
75
|
+
return ast?.value === text;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
exec(text: string) {
|
|
79
|
+
const cursor = new Cursor(text);
|
|
80
|
+
const ast = this.parse(cursor);
|
|
77
81
|
|
|
78
82
|
return {
|
|
79
|
-
ast,
|
|
83
|
+
ast: ast?.value === text ? ast : null,
|
|
80
84
|
cursor
|
|
81
85
|
};
|
|
82
86
|
}
|
|
@@ -114,7 +118,7 @@ export class Regex implements Pattern {
|
|
|
114
118
|
this._name,
|
|
115
119
|
currentIndex,
|
|
116
120
|
newIndex,
|
|
117
|
-
|
|
121
|
+
undefined,
|
|
118
122
|
result[0]
|
|
119
123
|
);
|
|
120
124
|
|
|
@@ -133,60 +137,48 @@ export class Regex implements Pattern {
|
|
|
133
137
|
clone(name = this._name, isOptional = this._isOptional) {
|
|
134
138
|
const pattern = new Regex(name, this._originalRegexString, isOptional);
|
|
135
139
|
pattern._tokens = this._tokens.slice();
|
|
136
|
-
pattern._hasContextualTokenAggregation =
|
|
137
|
-
this._hasContextualTokenAggregation;
|
|
138
140
|
|
|
139
141
|
return pattern;
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
getTokens() {
|
|
143
|
-
const parent = this._parent;
|
|
144
|
-
|
|
145
|
-
if (this._hasContextualTokenAggregation &&
|
|
146
|
-
parent != null &&
|
|
147
|
-
!this._isRetrievingContextualTokens
|
|
148
|
-
) {
|
|
149
|
-
this._isRetrievingContextualTokens = true;
|
|
150
|
-
|
|
151
|
-
const tokens = this._tokens;
|
|
152
|
-
const aggregateTokens: string[] = [];
|
|
153
|
-
const nextTokens = parent.getNextTokens(this);
|
|
154
|
-
|
|
155
|
-
for (let nextToken of nextTokens) {
|
|
156
|
-
for (let token of tokens) {
|
|
157
|
-
aggregateTokens.push(token + nextToken);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
this._isRetrievingContextualTokens = false
|
|
162
|
-
return aggregateTokens;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
145
|
return this._tokens;
|
|
166
146
|
}
|
|
167
147
|
|
|
168
|
-
|
|
148
|
+
getTokensAfter(_childReference: Pattern): string[] {
|
|
169
149
|
return [];
|
|
170
150
|
}
|
|
171
151
|
|
|
172
|
-
|
|
173
|
-
|
|
152
|
+
getNextTokens(): string[] {
|
|
153
|
+
if (this.parent == null) {
|
|
154
|
+
return []
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return this.parent.getTokensAfter(this);
|
|
174
158
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return
|
|
159
|
+
|
|
160
|
+
getPatterns(): Pattern[] {
|
|
161
|
+
return [this];
|
|
178
162
|
}
|
|
179
163
|
|
|
180
|
-
|
|
181
|
-
|
|
164
|
+
getPatternsAfter(_childReference: Pattern): Pattern[] {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
getNextPatterns(): Pattern[] {
|
|
169
|
+
if (this.parent == null) {
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return this.parent.getPatternsAfter(this)
|
|
182
174
|
}
|
|
183
175
|
|
|
184
|
-
|
|
185
|
-
|
|
176
|
+
findPattern(_predicate: (p: Pattern) => boolean): Pattern | null {
|
|
177
|
+
return null;
|
|
186
178
|
}
|
|
187
179
|
|
|
188
|
-
|
|
189
|
-
this.
|
|
180
|
+
setTokens(tokens: string[]) {
|
|
181
|
+
this._tokens = tokens;
|
|
190
182
|
}
|
|
191
183
|
|
|
192
184
|
}
|
|
@@ -3,6 +3,7 @@ import { And } from "./And";
|
|
|
3
3
|
import { Cursor } from "./Cursor";
|
|
4
4
|
import { findPattern } from "./findPattern";
|
|
5
5
|
import { Literal } from "./Literal";
|
|
6
|
+
import { Pattern } from "./Pattern";
|
|
6
7
|
import { Regex } from "./Regex";
|
|
7
8
|
import { Repeat } from "./Repeat";
|
|
8
9
|
|
|
@@ -96,31 +97,6 @@ describe("Repeat", () => {
|
|
|
96
97
|
expect(cursor.hasError).toBeFalsy()
|
|
97
98
|
});
|
|
98
99
|
|
|
99
|
-
test("Ast Reduction", () => {
|
|
100
|
-
const digit = new Regex("digit", "\\d");
|
|
101
|
-
const integer = new Repeat("number", digit);
|
|
102
|
-
const cursor = new Cursor("337");
|
|
103
|
-
|
|
104
|
-
integer.enableAstReduction();
|
|
105
|
-
|
|
106
|
-
let result = integer.parse(cursor);
|
|
107
|
-
let expected = new Node("repeat", "number", 0, 2, [], "337");
|
|
108
|
-
|
|
109
|
-
expect(result).toEqual(expected)
|
|
110
|
-
|
|
111
|
-
integer.disableAstReduction()
|
|
112
|
-
cursor.moveTo(0)
|
|
113
|
-
|
|
114
|
-
result = integer.parse(cursor);
|
|
115
|
-
expected = new Node("repeat", "number", 0, 2, [
|
|
116
|
-
new Node("regex", "digit", 0, 0, [], "3"),
|
|
117
|
-
new Node("regex", "digit", 1, 1, [], "3"),
|
|
118
|
-
new Node("regex", "digit", 2, 2, [], "7"),
|
|
119
|
-
]);
|
|
120
|
-
|
|
121
|
-
expect(result).toEqual(expected)
|
|
122
|
-
});
|
|
123
|
-
|
|
124
100
|
test("Get Tokens", () => {
|
|
125
101
|
const a = new Literal("a", "A");
|
|
126
102
|
const manyA = new Repeat("number", a);
|
|
@@ -130,16 +106,16 @@ describe("Repeat", () => {
|
|
|
130
106
|
expect(tokens).toEqual(expected)
|
|
131
107
|
});
|
|
132
108
|
|
|
133
|
-
test("Get
|
|
109
|
+
test("Get Tokens After With Bogus Pattern", () => {
|
|
134
110
|
const a = new Literal("a", "A");
|
|
135
111
|
const manyA = new Repeat("many-a", a);
|
|
136
|
-
const tokens = manyA.
|
|
112
|
+
const tokens = manyA.getTokensAfter(new Literal("bogus", "bogus"));
|
|
137
113
|
const expected: string[] = [];
|
|
138
114
|
|
|
139
115
|
expect(tokens).toEqual(expected)
|
|
140
116
|
});
|
|
141
117
|
|
|
142
|
-
test("Get
|
|
118
|
+
test("Get Tokens After With Divider", () => {
|
|
143
119
|
const a = new Literal("a", "A");
|
|
144
120
|
const b = new Literal("b", "B");
|
|
145
121
|
const divider = new Literal("divider", ",");
|
|
@@ -147,37 +123,30 @@ describe("Repeat", () => {
|
|
|
147
123
|
const parent = new And("parent", [manyA, b]);
|
|
148
124
|
|
|
149
125
|
const clonedManyA = findPattern(parent, p => p.name == "many-a");
|
|
150
|
-
let tokens = clonedManyA?.
|
|
126
|
+
let tokens = clonedManyA?.getTokensAfter(clonedManyA.children[0]);
|
|
151
127
|
let expected = [",", "B"];
|
|
152
128
|
|
|
153
129
|
expect(tokens).toEqual(expected)
|
|
154
130
|
|
|
155
|
-
tokens = clonedManyA?.
|
|
131
|
+
tokens = clonedManyA?.getTokensAfter(clonedManyA.children[1]);
|
|
156
132
|
expected = ["A"];
|
|
157
133
|
|
|
158
134
|
expect(tokens).toEqual(expected)
|
|
159
135
|
});
|
|
160
136
|
|
|
161
|
-
test("Get
|
|
137
|
+
test("Get Tokens After Without Divider", () => {
|
|
162
138
|
const a = new Literal("a", "A");
|
|
163
139
|
const b = new Literal("b", "B");
|
|
164
140
|
const manyA = new Repeat("many-a", a);
|
|
165
141
|
const parent = new And("parent", [manyA, b]);
|
|
166
142
|
|
|
167
143
|
const clonedManyA = findPattern(parent, p => p.name == "many-a");
|
|
168
|
-
const tokens = clonedManyA?.
|
|
144
|
+
const tokens = clonedManyA?.getTokensAfter(clonedManyA.children[0]);
|
|
169
145
|
const expected = ["A", "B"];
|
|
170
146
|
|
|
171
147
|
expect(tokens).toEqual(expected)
|
|
172
148
|
});
|
|
173
149
|
|
|
174
|
-
test("Get Next Pattern", () => {
|
|
175
|
-
const repeat = new Repeat("many-a", new Literal("a", "A"));
|
|
176
|
-
const nextPattern = repeat.getNextPattern();
|
|
177
|
-
|
|
178
|
-
expect(nextPattern).toBeNull()
|
|
179
|
-
});
|
|
180
|
-
|
|
181
150
|
test("Properties", () => {
|
|
182
151
|
const integer = new Repeat("integer", new Regex("digit", "\\d"));
|
|
183
152
|
|
|
@@ -188,9 +157,71 @@ describe("Repeat", () => {
|
|
|
188
157
|
expect(integer.children[0].name).toBe("digit");
|
|
189
158
|
});
|
|
190
159
|
|
|
191
|
-
test("
|
|
160
|
+
test("Exec", () => {
|
|
192
161
|
const integer = new Repeat("integer", new Regex("digit", "\\d"));
|
|
193
|
-
const { ast: result } = integer.
|
|
162
|
+
const { ast: result } = integer.exec("B");
|
|
194
163
|
expect(result).toBeNull()
|
|
195
164
|
});
|
|
165
|
+
|
|
166
|
+
test("Test With Match", () => {
|
|
167
|
+
const integer = new Repeat("integer", new Regex("digit", "\\d"));
|
|
168
|
+
const result = integer.test("1");
|
|
169
|
+
expect(result).toBeTruthy()
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("Test With No Match", () => {
|
|
173
|
+
const integer = new Repeat("integer", new Regex("digit", "\\d"));
|
|
174
|
+
const result = integer.test("b");
|
|
175
|
+
expect(result).toBeFalsy()
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("Get Next Tokens", () => {
|
|
179
|
+
const integer = new Repeat("integer", new Regex("digit", "\\d"));
|
|
180
|
+
const parent = new And("parent", [integer, new Literal("pow", "!")]);
|
|
181
|
+
const integerClone = parent.findPattern(p => p.name === "integer") as Pattern;
|
|
182
|
+
const tokens = integerClone.getNextTokens();
|
|
183
|
+
|
|
184
|
+
expect(tokens).toEqual(["!"])
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("Get Next Tokens With Null Parents", () => {
|
|
188
|
+
const integer = new Repeat("integer", new Regex("digit", "\\d"));
|
|
189
|
+
const tokens = integer.getNextTokens();
|
|
190
|
+
|
|
191
|
+
expect(tokens.length).toBe(0);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("Find Pattern", () => {
|
|
195
|
+
const integer = new Repeat("integer", new Regex("digit", "\\d"));
|
|
196
|
+
const digitClone = integer.findPattern(p => p.name === "digit") as Pattern;
|
|
197
|
+
|
|
198
|
+
expect(digitClone).not.toBeNull();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("Get Patterns", () => {
|
|
202
|
+
const a = new Literal("a", "A");
|
|
203
|
+
const manyA = new Repeat("number", a);
|
|
204
|
+
const patterns = manyA.getPatterns();
|
|
205
|
+
const expected = [manyA.findPattern(p => p.name === "a")];
|
|
206
|
+
|
|
207
|
+
expect(patterns).toEqual(expected)
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("Get Next Patterns", () => {
|
|
211
|
+
const integer = new Repeat("integer", new Regex("digit", "\\d"));
|
|
212
|
+
const parent = new And("parent", [integer, new Literal("pow", "!")]);
|
|
213
|
+
const integerClone = parent.findPattern(p => p.name === "integer") as Pattern;
|
|
214
|
+
const powClone = parent.findPattern(p => p.name === "pow") as Pattern;
|
|
215
|
+
const patterns = integerClone.getNextPatterns();
|
|
216
|
+
|
|
217
|
+
expect(patterns.length).toBe(1);
|
|
218
|
+
expect(patterns[0]).toBe(powClone);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("Get Next Patterns With Null Parents", () => {
|
|
222
|
+
const integer = new Repeat("integer", new Regex("digit", "\\d"));
|
|
223
|
+
const patterns = integer.getNextPatterns();
|
|
224
|
+
|
|
225
|
+
expect(patterns.length).toBe(0);
|
|
226
|
+
});
|
|
196
227
|
});
|
package/src/patterns/Repeat.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { Node } from "../ast/Node";
|
|
|
2
2
|
import { Cursor } from "./Cursor";
|
|
3
3
|
import { Pattern } from "./Pattern";
|
|
4
4
|
import { clonePatterns } from "./clonePatterns";
|
|
5
|
-
import { getNextPattern } from "./getNextPattern";
|
|
6
5
|
import { findPattern } from "./findPattern";
|
|
7
6
|
|
|
8
7
|
export class Repeat implements Pattern {
|
|
@@ -15,7 +14,6 @@ export class Repeat implements Pattern {
|
|
|
15
14
|
private _isOptional: boolean;
|
|
16
15
|
private _nodes: Node[];
|
|
17
16
|
private _firstIndex: number;
|
|
18
|
-
private _shouldReduceAst: boolean;
|
|
19
17
|
|
|
20
18
|
get type(): string {
|
|
21
19
|
return this._type;
|
|
@@ -54,7 +52,6 @@ export class Repeat implements Pattern {
|
|
|
54
52
|
this._pattern = children[0];
|
|
55
53
|
this._divider = children[1];
|
|
56
54
|
this._firstIndex = -1
|
|
57
|
-
this._shouldReduceAst = false
|
|
58
55
|
this._nodes = [];
|
|
59
56
|
}
|
|
60
57
|
|
|
@@ -64,12 +61,19 @@ export class Repeat implements Pattern {
|
|
|
64
61
|
}
|
|
65
62
|
}
|
|
66
63
|
|
|
67
|
-
|
|
64
|
+
test(text: string) {
|
|
68
65
|
const cursor = new Cursor(text);
|
|
69
|
-
const ast = this.parse(cursor)
|
|
66
|
+
const ast = this.parse(cursor);
|
|
67
|
+
|
|
68
|
+
return ast?.value === text;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
exec(text: string) {
|
|
72
|
+
const cursor = new Cursor(text);
|
|
73
|
+
const ast = this.parse(cursor);
|
|
70
74
|
|
|
71
75
|
return {
|
|
72
|
-
ast,
|
|
76
|
+
ast: ast?.value === text ? ast : null,
|
|
73
77
|
cursor
|
|
74
78
|
};
|
|
75
79
|
}
|
|
@@ -84,7 +88,7 @@ export class Repeat implements Pattern {
|
|
|
84
88
|
cursor.resolveError();
|
|
85
89
|
const node = this.createNode(cursor);
|
|
86
90
|
|
|
87
|
-
if (node) {
|
|
91
|
+
if (node != null) {
|
|
88
92
|
cursor.recordMatch(this, node);
|
|
89
93
|
}
|
|
90
94
|
|
|
@@ -110,7 +114,7 @@ export class Repeat implements Pattern {
|
|
|
110
114
|
if (cursor.hasError) {
|
|
111
115
|
const lastValidNode = this.getLastValidNode();
|
|
112
116
|
|
|
113
|
-
if (lastValidNode) {
|
|
117
|
+
if (lastValidNode != null) {
|
|
114
118
|
passed = true;
|
|
115
119
|
} else {
|
|
116
120
|
cursor.moveTo(runningCursorIndex);
|
|
@@ -129,13 +133,13 @@ export class Repeat implements Pattern {
|
|
|
129
133
|
|
|
130
134
|
cursor.next();
|
|
131
135
|
|
|
132
|
-
if (this._divider) {
|
|
136
|
+
if (this._divider != null) {
|
|
133
137
|
const dividerNode = this._divider.parse(cursor);
|
|
134
138
|
|
|
135
139
|
if (cursor.hasError) {
|
|
136
140
|
passed = true;
|
|
137
141
|
break;
|
|
138
|
-
} else if (dividerNode) {
|
|
142
|
+
} else if (dividerNode != null) {
|
|
139
143
|
this._nodes.push(dividerNode);
|
|
140
144
|
|
|
141
145
|
if (!cursor.hasNext()) {
|
|
@@ -171,17 +175,13 @@ export class Repeat implements Pattern {
|
|
|
171
175
|
const value = cursor.getChars(this._firstIndex, lastIndex);
|
|
172
176
|
cursor.moveTo(lastIndex);
|
|
173
177
|
|
|
174
|
-
if (this._shouldReduceAst) {
|
|
175
|
-
children = [];
|
|
176
|
-
}
|
|
177
|
-
|
|
178
178
|
return new Node(
|
|
179
179
|
"repeat",
|
|
180
180
|
this._name,
|
|
181
181
|
this._firstIndex,
|
|
182
182
|
lastIndex,
|
|
183
183
|
children,
|
|
184
|
-
|
|
184
|
+
undefined
|
|
185
185
|
);
|
|
186
186
|
}
|
|
187
187
|
|
|
@@ -195,65 +195,82 @@ export class Repeat implements Pattern {
|
|
|
195
195
|
return nodes[nodes.length - 1];
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
|
|
199
|
-
this.
|
|
198
|
+
getTokens(): string[] {
|
|
199
|
+
return this._pattern.getTokens();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
getTokensAfter(childReference: Pattern): string[] {
|
|
203
|
+
const patterns = this.getPatternsAfter(childReference);
|
|
204
|
+
const tokens: string[] = [];
|
|
205
|
+
|
|
206
|
+
patterns.forEach(p => tokens.push(...p.getTokens()));
|
|
207
|
+
|
|
208
|
+
return tokens;
|
|
200
209
|
}
|
|
201
210
|
|
|
202
|
-
|
|
203
|
-
this.
|
|
211
|
+
getNextTokens(): string[] {
|
|
212
|
+
if (this.parent == null) {
|
|
213
|
+
return []
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return this.parent.getTokensAfter(this);
|
|
204
217
|
}
|
|
205
218
|
|
|
206
|
-
|
|
207
|
-
return this._pattern.
|
|
219
|
+
getPatterns(): Pattern[] {
|
|
220
|
+
return this._pattern.getPatterns();
|
|
208
221
|
}
|
|
209
222
|
|
|
210
|
-
|
|
223
|
+
getPatternsAfter(childReference: Pattern): Pattern[] {
|
|
211
224
|
let index = -1;
|
|
212
|
-
const
|
|
225
|
+
const patterns: Pattern[] = [];
|
|
213
226
|
|
|
214
227
|
for (let i = 0; i < this._children.length; i++) {
|
|
215
|
-
if (this._children[i] ===
|
|
228
|
+
if (this._children[i] === childReference) {
|
|
216
229
|
index = i;
|
|
217
230
|
}
|
|
218
231
|
}
|
|
219
232
|
|
|
233
|
+
// If the last match isn't a child of this pattern.
|
|
220
234
|
if (index === -1) {
|
|
221
235
|
return [];
|
|
222
236
|
}
|
|
223
237
|
|
|
238
|
+
// If the last match was the repeated patterns, then suggest the divider.
|
|
224
239
|
if (index === 0 && this._divider) {
|
|
225
|
-
|
|
240
|
+
patterns.push(this._children[1]);
|
|
226
241
|
|
|
227
242
|
if (this._parent) {
|
|
228
|
-
|
|
243
|
+
patterns.push(...this._parent.getPatternsAfter(this));
|
|
229
244
|
}
|
|
230
245
|
}
|
|
231
246
|
|
|
247
|
+
// Suggest the pattern because the divider was the last match.
|
|
232
248
|
if (index === 1) {
|
|
233
|
-
|
|
249
|
+
patterns.push(this._children[0]);
|
|
234
250
|
}
|
|
235
251
|
|
|
236
252
|
if (index === 0 && !this._divider && this._parent) {
|
|
237
|
-
|
|
238
|
-
|
|
253
|
+
patterns.push(this._children[0]);
|
|
254
|
+
patterns.push(...this._parent.getPatternsAfter(this));
|
|
239
255
|
}
|
|
240
256
|
|
|
241
|
-
return
|
|
257
|
+
return patterns;
|
|
242
258
|
}
|
|
243
259
|
|
|
244
|
-
|
|
245
|
-
|
|
260
|
+
getNextPatterns(): Pattern[] {
|
|
261
|
+
if (this.parent == null) {
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return this.parent.getPatternsAfter(this)
|
|
246
266
|
}
|
|
247
267
|
|
|
248
|
-
findPattern(
|
|
249
|
-
return findPattern(this,
|
|
268
|
+
findPattern(predicate: (p: Pattern) => boolean): Pattern | null {
|
|
269
|
+
return findPattern(this, predicate);
|
|
250
270
|
}
|
|
251
271
|
|
|
252
272
|
clone(name = this._name, isOptional = this._isOptional): Pattern {
|
|
253
|
-
|
|
254
|
-
repeat._shouldReduceAst = this._shouldReduceAst;
|
|
255
|
-
|
|
256
|
-
return repeat;
|
|
273
|
+
return new Repeat(name, this._pattern, this._divider, isOptional);
|
|
257
274
|
}
|
|
258
275
|
}
|
|
259
276
|
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { And } from "./And";
|
|
2
|
-
import { findPattern } from "./findPattern";
|
|
3
|
-
import { Literal } from "./Literal";
|
|
4
|
-
|
|
5
|
-
describe("getNextPatter", ()=>{
|
|
6
|
-
test("No Parent", ()=>{
|
|
7
|
-
const a = new Literal("a", "A");
|
|
8
|
-
const nextPattern = a.getNextPattern();
|
|
9
|
-
|
|
10
|
-
expect(nextPattern).toBe(null);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test("No Next Sibling", ()=>{
|
|
14
|
-
const a = new Literal("a", "A");
|
|
15
|
-
const parent = new And("parent", [a]);
|
|
16
|
-
const nextPattern = parent.children[0].getNextPattern();
|
|
17
|
-
|
|
18
|
-
expect(nextPattern).toBe(null);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test("Get Parents Sibling", ()=>{
|
|
22
|
-
const a = new Literal("a", "A");
|
|
23
|
-
const parent = new And("parent", [a]);
|
|
24
|
-
const grandParent = new And("grand-parent", [parent]);
|
|
25
|
-
const clonedA = findPattern(grandParent, p=>p.name === "a");
|
|
26
|
-
const nextPattern = clonedA?.getNextPattern();
|
|
27
|
-
|
|
28
|
-
expect(nextPattern).toBe(null);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test("Get Sibling", ()=>{
|
|
32
|
-
const a = new Literal("a", "A");
|
|
33
|
-
const b = new Literal("b", "B");
|
|
34
|
-
const parent = new And("parent", [a, b]);
|
|
35
|
-
|
|
36
|
-
const nextPattern = parent.children[0].getNextPattern();
|
|
37
|
-
expect(nextPattern?.name).toBe("b");
|
|
38
|
-
});
|
|
39
|
-
});
|