clarity-pattern-parser 4.0.2 → 5.0.0
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 +176 -1
- package/TODO.md +22 -2
- package/dist/ast/Node.d.ts +43 -11
- package/dist/ast/Visitor.d.ts +31 -31
- package/dist/index.browser.js +1248 -1495
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +12 -16
- package/dist/index.esm.js +1218 -1460
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1217 -1464
- package/dist/index.js.map +1 -1
- package/dist/patterns/And.d.ts +37 -24
- package/dist/patterns/Cursor.d.ts +35 -0
- package/dist/patterns/CursorHistory.d.ts +30 -0
- package/dist/patterns/Literal.d.ts +36 -19
- package/dist/patterns/Not.d.ts +26 -11
- package/dist/patterns/Or.d.ts +31 -22
- package/dist/patterns/ParseError.d.ts +6 -8
- package/dist/patterns/ParseResult.d.ts +6 -0
- package/dist/patterns/Pattern.d.ts +17 -26
- package/dist/patterns/Reference.d.ts +31 -12
- package/dist/patterns/Regex.d.ts +42 -21
- package/dist/patterns/Repeat.d.ts +38 -20
- package/dist/patterns/clonePatterns.d.ts +2 -0
- package/dist/patterns/filterOutNull.d.ts +2 -0
- package/dist/patterns/findPattern.d.ts +2 -0
- package/dist/patterns/getNextPattern.d.ts +2 -0
- package/jest.config.js +2 -1
- package/package.json +4 -5
- package/rollup.config.js +1 -1
- package/src/ast/Node.test.ts +254 -0
- package/src/ast/Node.ts +171 -23
- package/src/index.ts +11 -19
- package/src/intellisense/AutoComplete.test.ts +72 -0
- package/src/intellisense/AutoComplete.ts +146 -0
- package/src/intellisense/Suggestion.ts +13 -0
- package/src/intellisense/SuggestionOption.ts +4 -0
- package/src/{tests/cssPatterns → intellisense/css}/cssValue.ts +1 -1
- package/src/{tests/cssPatterns → intellisense/css}/divider.ts +2 -1
- package/src/intellisense/css/hex.ts +6 -0
- package/src/{tests/cssPatterns → intellisense/css}/method.ts +8 -9
- package/src/intellisense/css/name.ts +5 -0
- package/src/{tests/javascriptPatterns → intellisense/css}/number.ts +3 -3
- package/src/intellisense/css/spaces.ts +6 -0
- package/src/intellisense/css/unit.ts +10 -0
- package/src/{tests/cssPatterns → intellisense/css}/value.ts +1 -1
- package/src/{tests/cssPatterns → intellisense/css}/values.ts +1 -1
- package/src/intellisense/javascript/Javascript.test.ts +203 -0
- package/src/intellisense/javascript/arrayLiteral.ts +25 -0
- package/src/intellisense/javascript/deleteStatement.ts +14 -0
- package/src/intellisense/javascript/escapedCharacter.ts +50 -0
- package/src/intellisense/javascript/exponent.ts +26 -0
- package/src/intellisense/javascript/expression.ts +87 -0
- package/src/intellisense/javascript/expressionStatement.ts +29 -0
- package/src/intellisense/javascript/fraction.ts +13 -0
- package/src/intellisense/javascript/infixOperator.ts +36 -0
- package/src/intellisense/javascript/integer.ts +7 -0
- package/src/intellisense/javascript/invocation.ts +28 -0
- package/src/intellisense/javascript/literal.ts +14 -0
- package/src/intellisense/javascript/name.ts +3 -0
- package/src/intellisense/javascript/numberLiteral.ts +10 -0
- package/src/intellisense/javascript/objectLiteral.ts +30 -0
- package/src/intellisense/javascript/optionalSpaces.ts +3 -0
- package/src/intellisense/javascript/parameters.ts +20 -0
- package/src/intellisense/javascript/prefixOperator.ts +13 -0
- package/src/intellisense/javascript/propertyAccess.ts +23 -0
- package/src/intellisense/javascript/stringLiteral.ts +28 -0
- package/src/patterns/And.test.ts +299 -0
- package/src/patterns/And.ts +222 -119
- package/src/patterns/Cursor.test.ts +93 -0
- package/src/patterns/Cursor.ts +130 -0
- package/src/patterns/CursorHistory.test.ts +54 -0
- package/src/patterns/CursorHistory.ts +95 -0
- package/src/patterns/Literal.test.ts +134 -0
- package/src/patterns/Literal.ts +151 -61
- package/src/patterns/Not.test.ts +88 -0
- package/src/patterns/Not.ts +74 -33
- package/src/patterns/Or.test.ts +105 -0
- package/src/patterns/Or.ts +106 -98
- package/src/patterns/ParseError.ts +3 -7
- package/src/patterns/ParseResult.ts +7 -0
- package/src/patterns/Pattern.ts +18 -150
- package/src/patterns/Reference.test.ts +104 -0
- package/src/patterns/Reference.ts +94 -94
- package/src/patterns/Regex.test.ts +101 -0
- package/src/patterns/Regex.ts +129 -60
- package/src/patterns/Repeat.test.ts +196 -0
- package/src/patterns/Repeat.ts +208 -104
- package/src/patterns/clonePatterns.ts +5 -0
- package/src/patterns/filterOutNull.ts +13 -0
- package/src/patterns/findPattern.ts +25 -0
- package/src/patterns/getNextPattern.test.ts +39 -0
- package/src/patterns/getNextPattern.ts +18 -0
- package/src/Cursor.ts +0 -141
- package/src/CursorHistory.ts +0 -146
- package/src/TextSuggester.ts +0 -317
- package/src/ast/Visitor.ts +0 -271
- package/src/patterns/LookAhead.ts +0 -32
- package/src/patterns/Recursive.ts +0 -92
- package/src/tests/And.test.ts +0 -180
- package/src/tests/ComplexExamples.test.ts +0 -86
- package/src/tests/CssPatterns.test.ts +0 -90
- package/src/tests/CursorHistory.test.ts +0 -107
- package/src/tests/Cusor.test.ts +0 -174
- package/src/tests/HtmlPatterns.test.ts +0 -34
- package/src/tests/Literal.test.ts +0 -79
- package/src/tests/LookAhead.test.ts +0 -44
- package/src/tests/Not.test.ts +0 -51
- package/src/tests/Or.test.ts +0 -113
- package/src/tests/Pattern.test.ts +0 -290
- package/src/tests/Recursive.test.ts +0 -64
- package/src/tests/Reference.test.ts +0 -16
- package/src/tests/Repeat.test.ts +0 -75
- package/src/tests/SpeedTest.test.ts +0 -31
- package/src/tests/TextSuggester.test.ts +0 -297
- package/src/tests/Visitor.test.ts +0 -331
- package/src/tests/cssPatterns/hex.ts +0 -5
- package/src/tests/cssPatterns/name.ts +0 -5
- package/src/tests/cssPatterns/number.ts +0 -8
- package/src/tests/cssPatterns/spaces.ts +0 -5
- package/src/tests/cssPatterns/unit.ts +0 -8
- package/src/tests/htmlPatterns/element.ts +0 -49
- package/src/tests/javascriptPatterns/boolean.ts +0 -10
- package/src/tests/javascriptPatterns/json.ts +0 -67
- package/src/tests/javascriptPatterns/name.ts +0 -5
- package/src/tests/javascriptPatterns/objectLiteral.ts +0 -40
- package/src/tests/javascriptPatterns/string.ts +0 -84
- package/src/tests/javascriptPatterns/unit.ts +0 -8
- package/src/tests/javascriptPatterns/whitespace.ts +0 -44
- package/src/tests/naturalLanguage/filter.ts +0 -37
- package/src/tests/patterns/sentence.ts +0 -37
- /package/src/{tests/cssPatterns → intellisense/css}/optionalSpaces.ts +0 -0
package/src/patterns/And.ts
CHANGED
|
@@ -1,178 +1,281 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
1
|
+
import { Cursor } from "./Cursor";
|
|
2
|
+
import { Pattern } from "./Pattern";
|
|
3
|
+
import { Node } from "../ast/Node";
|
|
4
|
+
import { clonePatterns } from "./clonePatterns";
|
|
5
|
+
import { filterOutNull } from "./filterOutNull";
|
|
6
|
+
import { getNextPattern } from "./getNextPattern";
|
|
7
|
+
import { findPattern } from "./findPattern";
|
|
8
|
+
|
|
9
|
+
export class And implements Pattern {
|
|
10
|
+
private _type: string;
|
|
11
|
+
private _name: string;
|
|
12
|
+
private _parent: Pattern | null;
|
|
13
|
+
private _children: Pattern[];
|
|
14
|
+
private _isOptional: boolean;
|
|
15
|
+
private _nodes: (Node | null)[];
|
|
16
|
+
private _firstIndex: number;
|
|
17
|
+
private _shouldReduceAst: boolean;
|
|
18
|
+
|
|
19
|
+
get type(): string {
|
|
20
|
+
return this._type;
|
|
21
|
+
}
|
|
5
22
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
public node: Node | null = null;
|
|
10
|
-
public cursor: Cursor | null = null;
|
|
11
|
-
public mark: number = 0;
|
|
23
|
+
get name(): string {
|
|
24
|
+
return this._name;
|
|
25
|
+
}
|
|
12
26
|
|
|
13
|
-
|
|
14
|
-
|
|
27
|
+
get parent(): Pattern | null {
|
|
28
|
+
return this._parent;
|
|
15
29
|
}
|
|
16
30
|
|
|
17
|
-
|
|
18
|
-
this.
|
|
19
|
-
|
|
31
|
+
set parent(pattern: Pattern | null) {
|
|
32
|
+
this._parent = pattern;
|
|
33
|
+
}
|
|
20
34
|
|
|
21
|
-
|
|
35
|
+
get children(): Pattern[] {
|
|
36
|
+
return this._children;
|
|
22
37
|
}
|
|
23
38
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
39
|
+
get isOptional(): boolean {
|
|
40
|
+
return this._isOptional;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
constructor(name: string, sequence: Pattern[], isOptional = false) {
|
|
44
|
+
if (sequence.length === 0) {
|
|
45
|
+
throw new Error("Need at least one pattern with an 'and' pattern.");
|
|
27
46
|
}
|
|
28
47
|
|
|
29
|
-
|
|
30
|
-
|
|
48
|
+
const children = clonePatterns(sequence);
|
|
49
|
+
this._assignChildrenToParent(children)
|
|
50
|
+
|
|
51
|
+
this._type = "and";
|
|
52
|
+
this._name = name;
|
|
53
|
+
this._isOptional = isOptional;
|
|
54
|
+
this._parent = null;
|
|
55
|
+
this._children = children;
|
|
56
|
+
this._firstIndex = -1
|
|
57
|
+
this._shouldReduceAst = false;
|
|
58
|
+
this._nodes = [];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private _assignChildrenToParent(children: Pattern[]): void {
|
|
62
|
+
for (const child of children) {
|
|
63
|
+
child.parent = this;
|
|
31
64
|
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
parseText(text: string) {
|
|
68
|
+
const cursor = new Cursor(text);
|
|
69
|
+
const ast = this.parse(cursor)
|
|
32
70
|
|
|
33
|
-
return
|
|
71
|
+
return {
|
|
72
|
+
ast,
|
|
73
|
+
cursor
|
|
74
|
+
};
|
|
34
75
|
}
|
|
35
76
|
|
|
36
|
-
|
|
37
|
-
|
|
77
|
+
parse(cursor: Cursor): Node | null {
|
|
78
|
+
this._firstIndex = cursor.index;
|
|
79
|
+
this._nodes = [];
|
|
80
|
+
|
|
81
|
+
const passed = this.tryToParse(cursor);
|
|
82
|
+
|
|
83
|
+
if (passed) {
|
|
84
|
+
const node = this.createNode(cursor);
|
|
85
|
+
|
|
86
|
+
if (node !== null) {
|
|
87
|
+
cursor.recordMatch(this, node);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return node;
|
|
91
|
+
}
|
|
38
92
|
|
|
39
|
-
|
|
40
|
-
|
|
93
|
+
if (this._isOptional) {
|
|
94
|
+
cursor.resolveError();
|
|
95
|
+
}
|
|
41
96
|
|
|
42
|
-
|
|
43
|
-
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private tryToParse(cursor: Cursor): boolean {
|
|
101
|
+
let passed = false;
|
|
102
|
+
|
|
103
|
+
for (let i = 0; i < this._children.length; i++) {
|
|
104
|
+
const runningCursorIndex = cursor.index;
|
|
105
|
+
const nextPatternIndex = i + 1;
|
|
106
|
+
const hasMorePatterns = nextPatternIndex < this._children.length;
|
|
107
|
+
|
|
108
|
+
const node = this._children[i].parse(cursor);
|
|
109
|
+
const hasNoError = !cursor.hasError;
|
|
110
|
+
const hadMatch = node !== null;
|
|
111
|
+
|
|
112
|
+
if (hasNoError) {
|
|
113
|
+
this._nodes.push(node);
|
|
114
|
+
|
|
115
|
+
if (hasMorePatterns) {
|
|
116
|
+
if (hadMatch) {
|
|
117
|
+
if (cursor.hasNext()) {
|
|
118
|
+
cursor.next();
|
|
119
|
+
continue;
|
|
120
|
+
} else {
|
|
121
|
+
if (this.areRemainingPatternsOptional(i)) {
|
|
122
|
+
passed = true;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
cursor.recordErrorAt(cursor.index + 1, this);
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
cursor.moveTo(runningCursorIndex);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
const lastNode = this.getLastValidNode();
|
|
135
|
+
if (lastNode === null) {
|
|
136
|
+
cursor.recordErrorAt(cursor.index, this);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
passed = true;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
44
143
|
} else {
|
|
45
|
-
|
|
144
|
+
cursor.moveTo(this._firstIndex);
|
|
46
145
|
break;
|
|
47
146
|
}
|
|
48
147
|
}
|
|
49
148
|
|
|
50
|
-
return
|
|
149
|
+
return passed;
|
|
51
150
|
}
|
|
52
151
|
|
|
53
|
-
private
|
|
54
|
-
|
|
55
|
-
this.nodes = [];
|
|
56
|
-
this.node = null;
|
|
57
|
-
this.cursor = cursor;
|
|
58
|
-
this.mark = this.cursor.mark();
|
|
59
|
-
}
|
|
152
|
+
private getLastValidNode(): Node | null {
|
|
153
|
+
const nodes = filterOutNull(this._nodes);
|
|
60
154
|
|
|
61
|
-
|
|
62
|
-
|
|
155
|
+
if (nodes.length === 0) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
63
158
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const node = pattern.parse(cursor);
|
|
159
|
+
return nodes[nodes.length - 1];
|
|
160
|
+
}
|
|
67
161
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
} else {
|
|
72
|
-
this.nodes.push(node);
|
|
73
|
-
}
|
|
162
|
+
private areRemainingPatternsOptional(fromIndex: number): boolean {
|
|
163
|
+
const startOnIndex = fromIndex + 1;
|
|
164
|
+
const length = this._children.length;
|
|
74
165
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
166
|
+
for (let i = startOnIndex; i < length; i++) {
|
|
167
|
+
const pattern = this._children[i];
|
|
168
|
+
if (!pattern.isOptional) {
|
|
169
|
+
return false;
|
|
78
170
|
}
|
|
79
171
|
}
|
|
172
|
+
|
|
173
|
+
return true;
|
|
80
174
|
}
|
|
81
175
|
|
|
82
|
-
private
|
|
83
|
-
const
|
|
176
|
+
private createNode(cursor: Cursor): Node | null {
|
|
177
|
+
const children = filterOutNull(this._nodes);
|
|
178
|
+
|
|
179
|
+
const lastIndex = children[children.length - 1].lastIndex;
|
|
180
|
+
const value = cursor.getChars(this._firstIndex, lastIndex);
|
|
84
181
|
|
|
85
|
-
|
|
86
|
-
|
|
182
|
+
cursor.moveTo(lastIndex)
|
|
183
|
+
|
|
184
|
+
if (this._shouldReduceAst) {
|
|
185
|
+
children.length = 0;
|
|
87
186
|
}
|
|
88
|
-
|
|
187
|
+
|
|
188
|
+
return new Node(
|
|
189
|
+
"and",
|
|
190
|
+
this._name,
|
|
191
|
+
this._firstIndex,
|
|
192
|
+
lastIndex,
|
|
193
|
+
children,
|
|
194
|
+
this._shouldReduceAst ? value : undefined
|
|
195
|
+
);
|
|
89
196
|
}
|
|
90
197
|
|
|
91
|
-
|
|
92
|
-
|
|
198
|
+
enableAstReduction(): void {
|
|
199
|
+
this._shouldReduceAst = true;
|
|
200
|
+
}
|
|
93
201
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
} else {
|
|
97
|
-
this.processSuccess();
|
|
98
|
-
}
|
|
202
|
+
disableAstReduction(): void {
|
|
203
|
+
this._shouldReduceAst = false;
|
|
99
204
|
}
|
|
100
205
|
|
|
101
|
-
|
|
102
|
-
const
|
|
206
|
+
getTokens(): string[] {
|
|
207
|
+
const tokens: string[] = [];
|
|
103
208
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
209
|
+
for (const child of this._children) {
|
|
210
|
+
tokens.push(...child.getTokens());
|
|
211
|
+
|
|
212
|
+
if (!child.isOptional) {
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
107
215
|
}
|
|
108
|
-
this.node = null;
|
|
109
|
-
}
|
|
110
216
|
|
|
111
|
-
|
|
112
|
-
|
|
217
|
+
return tokens;
|
|
218
|
+
}
|
|
113
219
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
220
|
+
getNextTokens(lastMatched: Pattern): string[] {
|
|
221
|
+
let nextSibling: Pattern | null = null;
|
|
222
|
+
let nextSiblingIndex = -1;
|
|
223
|
+
let index = -1;
|
|
224
|
+
const tokens: string[] = [];
|
|
117
225
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
226
|
+
for (let i = 0; i < this._children.length; i++) {
|
|
227
|
+
if (this._children[i] === lastMatched) {
|
|
228
|
+
if (i + 1 < this._children.length) {
|
|
229
|
+
nextSibling = this._children[i + 1];
|
|
121
230
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
} else if (wasOptional) {
|
|
126
|
-
this.onPatternIndex++;
|
|
127
|
-
return true;
|
|
231
|
+
nextSiblingIndex = i + 1;
|
|
232
|
+
index = i;
|
|
233
|
+
break;
|
|
128
234
|
}
|
|
235
|
+
}
|
|
129
236
|
|
|
130
|
-
|
|
131
|
-
return
|
|
132
|
-
} else {
|
|
133
|
-
return false;
|
|
237
|
+
if (index === -1) {
|
|
238
|
+
return [];
|
|
134
239
|
}
|
|
135
|
-
}
|
|
136
240
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
241
|
+
if (nextSiblingIndex === this._children.length && this._parent !== null) {
|
|
242
|
+
return this._parent.getNextTokens(this);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (nextSibling !== null && !nextSibling.isOptional) {
|
|
246
|
+
return nextSibling.getTokens();
|
|
247
|
+
}
|
|
140
248
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
249
|
+
if (nextSibling !== null && nextSibling.isOptional) {
|
|
250
|
+
for (let i = nextSiblingIndex; i < this._children.length; i++) {
|
|
251
|
+
const child = this._children[i];
|
|
252
|
+
tokens.push(...child.getTokens());
|
|
144
253
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
this.onPatternIndex,
|
|
149
|
-
this
|
|
150
|
-
);
|
|
254
|
+
if (!child.isOptional) {
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
151
257
|
|
|
152
|
-
|
|
258
|
+
if (i === this._children.length - 1 && this._parent !== null) {
|
|
259
|
+
tokens.push(...this._parent.getNextTokens(this));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
153
262
|
}
|
|
154
|
-
}
|
|
155
263
|
|
|
156
|
-
|
|
157
|
-
return this.children
|
|
158
|
-
.slice(this.onPatternIndex + 1)
|
|
159
|
-
.map((p) => p.isOptional)
|
|
160
|
-
.every((r) => r);
|
|
264
|
+
return tokens;
|
|
161
265
|
}
|
|
162
266
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
this.nodes = nodes;
|
|
267
|
+
getNextPattern(): Pattern | null {
|
|
268
|
+
return getNextPattern(this)
|
|
269
|
+
}
|
|
167
270
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const value = nodes.map((node) => node.value).join("");
|
|
271
|
+
findPattern(isMatch: (p: Pattern)=>boolean): Pattern | null{
|
|
272
|
+
return findPattern(this, isMatch);
|
|
273
|
+
}
|
|
172
274
|
|
|
173
|
-
|
|
275
|
+
clone(name = this._name, isOptional = this._isOptional): Pattern {
|
|
276
|
+
const and = new And(name, this._children, isOptional)
|
|
277
|
+
and._shouldReduceAst = this._shouldReduceAst;
|
|
174
278
|
|
|
175
|
-
|
|
176
|
-
cursor.addMatch(this, this.node);
|
|
279
|
+
return and
|
|
177
280
|
}
|
|
178
281
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Cursor } from "./Cursor";
|
|
2
|
+
import { Literal } from "./Literal";
|
|
3
|
+
import { Node } from "../ast/Node"
|
|
4
|
+
|
|
5
|
+
describe("Cursor", () => {
|
|
6
|
+
test("Empty Text", () => {
|
|
7
|
+
expect(() => {
|
|
8
|
+
new Cursor("");
|
|
9
|
+
}).toThrowError()
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("Move Cursor", () => {
|
|
13
|
+
const cursor = new Cursor("Hello World!");
|
|
14
|
+
cursor.moveTo(6);
|
|
15
|
+
|
|
16
|
+
expect(cursor.currentChar).toBe("W")
|
|
17
|
+
|
|
18
|
+
cursor.moveToFirstChar();
|
|
19
|
+
cursor.previous();
|
|
20
|
+
|
|
21
|
+
expect(cursor.isOnFirst).toBeTruthy();
|
|
22
|
+
expect(cursor.currentChar).toBe("H");
|
|
23
|
+
|
|
24
|
+
cursor.next();
|
|
25
|
+
|
|
26
|
+
expect(cursor.currentChar).toBe("e");
|
|
27
|
+
|
|
28
|
+
cursor.moveToLastChar();
|
|
29
|
+
cursor.next();
|
|
30
|
+
|
|
31
|
+
expect(cursor.isOnLast).toBeTruthy()
|
|
32
|
+
expect(cursor.currentChar).toBe("!");
|
|
33
|
+
|
|
34
|
+
cursor.previous();
|
|
35
|
+
|
|
36
|
+
expect(cursor.currentChar).toBe("d");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("Error Handling", () => {
|
|
40
|
+
const pattern = new Literal("a", "A");
|
|
41
|
+
const cursor = new Cursor("Hello World!");
|
|
42
|
+
|
|
43
|
+
cursor.recordErrorAt(0, pattern);
|
|
44
|
+
|
|
45
|
+
expect(cursor.hasError).toBeTruthy();
|
|
46
|
+
expect(cursor.error?.index).toBe(0);
|
|
47
|
+
expect(cursor.error?.pattern).toBe(pattern);
|
|
48
|
+
|
|
49
|
+
cursor.resolveError();
|
|
50
|
+
|
|
51
|
+
expect(cursor.hasError).toBeFalsy();
|
|
52
|
+
expect(cursor.error).toBe(null);
|
|
53
|
+
expect(cursor.furthestError?.index).toBe(0);
|
|
54
|
+
expect(cursor.furthestError?.pattern).toBe(pattern);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("Error Handling", () => {
|
|
58
|
+
const pattern = new Literal("h", "H");
|
|
59
|
+
const node = new Node("literal", "h", 0, 0, [], "H");
|
|
60
|
+
|
|
61
|
+
const cursor = new Cursor("Hello World!");
|
|
62
|
+
|
|
63
|
+
cursor.recordMatch(pattern, node)
|
|
64
|
+
|
|
65
|
+
expect(cursor.leafMatch.node).toBe(node);
|
|
66
|
+
expect(cursor.leafMatch.pattern).toBe(pattern);
|
|
67
|
+
|
|
68
|
+
expect(cursor.rootMatch.node).toBe(node);
|
|
69
|
+
expect(cursor.rootMatch.pattern).toBe(pattern);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("Recording", () => {
|
|
73
|
+
const cursor = new Cursor("Hello World!");
|
|
74
|
+
cursor.startRecording();
|
|
75
|
+
|
|
76
|
+
expect(cursor.isRecording).toBeTruthy();
|
|
77
|
+
|
|
78
|
+
cursor.stopRecording();
|
|
79
|
+
|
|
80
|
+
expect(cursor.isRecording).toBeFalsy();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
test("Text Information", () => {
|
|
85
|
+
const cursor = new Cursor("Hello World!");
|
|
86
|
+
const hello = cursor.getChars(0, 4);
|
|
87
|
+
|
|
88
|
+
expect(hello).toBe("Hello");
|
|
89
|
+
expect(cursor.length).toBe(12);
|
|
90
|
+
expect(cursor.text).toBe("Hello World!")
|
|
91
|
+
expect(cursor.index).toBe(0);
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Pattern, Node, ParseError } from "..";
|
|
2
|
+
import { CursorHistory, Match } from "./CursorHistory";
|
|
3
|
+
|
|
4
|
+
export class Cursor {
|
|
5
|
+
private _text: string;
|
|
6
|
+
private _index: number;
|
|
7
|
+
private _length: number;
|
|
8
|
+
private _history: CursorHistory;
|
|
9
|
+
|
|
10
|
+
get text(): string {
|
|
11
|
+
return this._text;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get isOnFirst(): boolean {
|
|
15
|
+
return this._index === 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get isOnLast(): boolean {
|
|
19
|
+
return this._index === this._getLastIndex();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get isRecording(): boolean {
|
|
23
|
+
return this._history.isRecording;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get leafMatch(): Match {
|
|
27
|
+
return this._history.leafMatch;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get rootMatch(): Match {
|
|
31
|
+
return this._history.rootMatch;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get furthestError(): ParseError | null {
|
|
35
|
+
return this._history.furthestError;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get error() {
|
|
39
|
+
return this._history.error;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get index(): number {
|
|
43
|
+
return this._index;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get length(): number {
|
|
47
|
+
return this._length;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get hasError(): boolean {
|
|
51
|
+
return this._history.error != null
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get currentChar(): string {
|
|
55
|
+
return this._text[this._index]
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
constructor(text: string) {
|
|
59
|
+
if (text.length === 0) {
|
|
60
|
+
throw new Error("Cannot have a empty string.");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this._text = text;
|
|
64
|
+
this._index = 0;
|
|
65
|
+
this._length = text.length;
|
|
66
|
+
this._history = new CursorHistory();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
hasNext(): boolean {
|
|
70
|
+
return this._index + 1 < this._length;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
hasPrevious(): boolean {
|
|
74
|
+
return this._index - 1 >= 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
next(): void {
|
|
78
|
+
if (this.hasNext()) {
|
|
79
|
+
this._index++;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
previous(): void {
|
|
84
|
+
if (this.hasPrevious()) {
|
|
85
|
+
this._index--;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
moveTo(position: number): void {
|
|
90
|
+
if (position >= 0 && position < this._length) {
|
|
91
|
+
this._index = position;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
moveToFirstChar(): void {
|
|
96
|
+
this._index = 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
moveToLastChar(): void {
|
|
100
|
+
this._index = this._getLastIndex();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getChars(first: number, last: number): string {
|
|
104
|
+
return this._text.slice(first, last + 1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
recordMatch(pattern: Pattern, node: Node): void {
|
|
108
|
+
this._history.recordMatch(pattern, node);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
recordErrorAt(index: number, onPattern: Pattern): void {
|
|
112
|
+
this._history.recordErrorAt(index, onPattern);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
resolveError(): void {
|
|
116
|
+
this._history.resolveError()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
startRecording(): void {
|
|
120
|
+
this._history.startRecording();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
stopRecording(): void {
|
|
124
|
+
this._history.stopRecording();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private _getLastIndex(): number {
|
|
128
|
+
return this._length - 1;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Node } from "../ast/Node";
|
|
2
|
+
import { CursorHistory } from "./CursorHistory"
|
|
3
|
+
import { Literal } from "./Literal";
|
|
4
|
+
|
|
5
|
+
describe("CursorHistory", () => {
|
|
6
|
+
test("Add Match", () => {
|
|
7
|
+
const history = new CursorHistory();
|
|
8
|
+
const pattern = new Literal("a", "A");
|
|
9
|
+
const node = new Node("literal", "a", 0, 0, [], "A");
|
|
10
|
+
|
|
11
|
+
history.startRecording();
|
|
12
|
+
history.recordMatch(pattern, node);
|
|
13
|
+
|
|
14
|
+
expect(history.isRecording).toBeTruthy();
|
|
15
|
+
|
|
16
|
+
expect(history.leafMatch.node).toBe(node);
|
|
17
|
+
expect(history.leafMatch.pattern).toBe(pattern);
|
|
18
|
+
|
|
19
|
+
expect(history.rootMatch.node).toBe(node);
|
|
20
|
+
expect(history.rootMatch.pattern).toBe(pattern);
|
|
21
|
+
|
|
22
|
+
expect(history.nodes[0]).toBe(node);
|
|
23
|
+
expect(history.patterns[0]).toBe(pattern);
|
|
24
|
+
|
|
25
|
+
history.stopRecording();
|
|
26
|
+
|
|
27
|
+
expect(history.isRecording).toBeFalsy();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("Add Error At", () => {
|
|
31
|
+
const history = new CursorHistory();
|
|
32
|
+
const pattern = new Literal("a", "A");
|
|
33
|
+
|
|
34
|
+
history.startRecording();
|
|
35
|
+
history.recordErrorAt(0, pattern);
|
|
36
|
+
|
|
37
|
+
expect(history.error?.index).toBe(0);
|
|
38
|
+
expect(history.error?.pattern).toBe(pattern);
|
|
39
|
+
expect(history.errors[0]?.index).toBe(0);
|
|
40
|
+
expect(history.errors[0]?.pattern).toBe(pattern);
|
|
41
|
+
|
|
42
|
+
history.stopRecording()
|
|
43
|
+
history.resolveError();
|
|
44
|
+
|
|
45
|
+
expect(history.isRecording).toBeFalsy();
|
|
46
|
+
expect(history.error).toBeNull();
|
|
47
|
+
|
|
48
|
+
expect(history.errors[0]?.index).toBe(0);
|
|
49
|
+
expect(history.errors[0]?.pattern).toBe(pattern);
|
|
50
|
+
|
|
51
|
+
expect(history.furthestError?.index).toBe(0);
|
|
52
|
+
expect(history.furthestError?.pattern).toBe(pattern);
|
|
53
|
+
});
|
|
54
|
+
});
|