clarity-pattern-parser 8.4.15 → 9.1.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/TODO.md +4 -1
- package/dist/grammar/Grammar.d.ts +20 -12
- package/dist/grammar/patterns/andLiteral.d.ts +2 -0
- package/dist/grammar/patterns/anonymousPattern.d.ts +2 -0
- package/dist/grammar/patterns/grammar.d.ts +2 -2
- package/dist/grammar/patterns/import.d.ts +2 -2
- package/dist/grammar/patterns/inlinePattern.d.ts +1 -0
- package/dist/grammar/patterns/literals.d.ts +3 -0
- package/dist/grammar/patterns/optionsLiteral.d.ts +2 -0
- package/dist/grammar/patterns/pattern.d.ts +2 -2
- package/dist/grammar/patterns/repeatLiteral.d.ts +2 -2
- package/dist/grammar/patterns/sequenceLiteral.d.ts +4 -0
- package/dist/grammar/patterns/statement.d.ts +2 -2
- package/dist/grammar/patterns.d.ts +2 -0
- package/dist/index.browser.js +752 -382
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.esm.js +748 -381
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +752 -382
- package/dist/index.js.map +1 -1
- package/dist/patterns/And.d.ts +4 -1
- package/dist/patterns/Cursor.d.ts +5 -0
- package/dist/patterns/CursorHistory.d.ts +7 -0
- package/dist/patterns/FiniteRepeat.d.ts +7 -4
- package/dist/patterns/InfiniteRepeat.d.ts +6 -6
- package/dist/patterns/Literal.d.ts +9 -9
- package/dist/patterns/Not.d.ts +5 -4
- package/dist/patterns/Optional.d.ts +30 -0
- package/dist/patterns/Options.d.ts +34 -0
- package/dist/patterns/Or.d.ts +5 -4
- package/dist/patterns/Pattern.d.ts +5 -4
- package/dist/patterns/Reference.d.ts +7 -8
- package/dist/patterns/Regex.d.ts +8 -8
- package/dist/patterns/Repeat.d.ts +4 -2
- package/dist/patterns/Sequence.d.ts +39 -0
- package/dist/patterns/arePatternsEqual.d.ts +2 -0
- package/dist/patterns/clonePatterns.d.ts +1 -1
- package/package.json +1 -1
- package/src/grammar/Grammar.test.ts +117 -74
- package/src/grammar/Grammar.ts +241 -158
- package/src/grammar/patterns/anonymousPattern.ts +23 -0
- package/src/grammar/patterns/body.ts +9 -6
- package/src/grammar/patterns/comment.ts +3 -2
- package/src/grammar/patterns/grammar.ts +15 -12
- package/src/grammar/patterns/import.ts +18 -12
- package/src/grammar/patterns/literal.ts +2 -1
- package/src/grammar/patterns/literals.ts +20 -0
- package/src/grammar/patterns/optionsLiteral.ts +19 -0
- package/src/grammar/patterns/pattern.ts +23 -9
- package/src/grammar/patterns/regexLiteral.ts +1 -0
- package/src/grammar/patterns/repeatLiteral.ts +30 -25
- package/src/grammar/patterns/sequenceLiteral.ts +24 -0
- package/src/grammar/patterns/spaces.ts +8 -6
- package/src/grammar/patterns/statement.ts +8 -20
- package/src/grammar/patterns.test.ts +38 -0
- package/src/grammar/patterns.ts +24 -0
- package/src/grammar/spec.md +4 -12
- package/src/index.ts +11 -5
- package/src/intellisense/AutoComplete.test.ts +41 -40
- package/src/intellisense/css/method.ts +2 -2
- package/src/intellisense/css/unit.ts +2 -2
- package/src/intellisense/css/value.ts +1 -1
- package/src/intellisense/javascript/Javascript.test.ts +31 -32
- package/src/intellisense/javascript/arrayLiteral.ts +7 -6
- package/src/intellisense/javascript/assignment.ts +6 -6
- package/src/intellisense/javascript/deleteStatement.ts +2 -2
- package/src/intellisense/javascript/escapedCharacter.ts +6 -6
- package/src/intellisense/javascript/exponent.ts +6 -6
- package/src/intellisense/javascript/expression.ts +18 -17
- package/src/intellisense/javascript/fraction.ts +3 -3
- package/src/intellisense/javascript/infixOperator.ts +10 -10
- package/src/intellisense/javascript/integer.ts +1 -1
- package/src/intellisense/javascript/invocation.ts +8 -7
- package/src/intellisense/javascript/literal.ts +3 -3
- package/src/intellisense/javascript/numberLiteral.ts +5 -4
- package/src/intellisense/javascript/objectAccess.ts +2 -3
- package/src/intellisense/javascript/objectLiteral.ts +8 -7
- package/src/intellisense/javascript/optionalSpaces.ts +2 -1
- package/src/intellisense/javascript/parameters.ts +5 -5
- package/src/intellisense/javascript/prefixOperator.ts +3 -4
- package/src/intellisense/javascript/propertyAccess.ts +9 -8
- package/src/intellisense/javascript/stringLiteral.ts +14 -15
- package/src/patterns/Cursor.ts +42 -4
- package/src/patterns/CursorHistory.ts +20 -4
- package/src/patterns/FiniteRepeat.test.ts +52 -51
- package/src/patterns/FiniteRepeat.ts +60 -38
- package/src/patterns/InfiniteRepeat.test.ts +36 -49
- package/src/patterns/InfiniteRepeat.ts +70 -37
- package/src/patterns/Literal.test.ts +16 -27
- package/src/patterns/Literal.ts +38 -27
- package/src/patterns/Not.test.ts +7 -7
- package/src/patterns/Not.ts +24 -6
- package/src/patterns/Optional.test.ts +164 -0
- package/src/patterns/Optional.ts +143 -0
- package/src/patterns/{Or.test.ts → Options.test.ts} +51 -49
- package/src/patterns/{Or.ts → Options.ts} +32 -23
- package/src/patterns/Pattern.ts +6 -5
- package/src/patterns/Reference.test.ts +21 -22
- package/src/patterns/Reference.ts +26 -15
- package/src/patterns/Regex.test.ts +15 -15
- package/src/patterns/Regex.ts +33 -19
- package/src/patterns/Repeat.test.ts +12 -22
- package/src/patterns/Repeat.ts +22 -21
- package/src/patterns/{And.test.ts → Sequence.test.ts} +78 -78
- package/src/patterns/{And.ts → Sequence.ts} +40 -29
- package/src/patterns/arePatternsEqual.ts +12 -0
- package/src/patterns/clonePatterns.ts +2 -2
- package/src/grammar/patterns/andLiteral.ts +0 -8
- package/src/grammar/patterns/orLiteral.ts +0 -8
package/src/patterns/Literal.ts
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import { Node } from "../ast/Node";
|
|
2
2
|
import { Cursor } from "./Cursor";
|
|
3
|
+
import { ParseResult } from "./ParseResult";
|
|
3
4
|
import { Pattern } from "./Pattern";
|
|
4
5
|
|
|
6
|
+
let idIndex = 0;
|
|
7
|
+
|
|
5
8
|
export class Literal implements Pattern {
|
|
9
|
+
private _id: string;
|
|
6
10
|
private _type: string;
|
|
7
11
|
private _name: string;
|
|
8
12
|
private _parent: Pattern | null;
|
|
9
|
-
private
|
|
10
|
-
private _literal: string;
|
|
13
|
+
private _text: string;
|
|
11
14
|
private _runes: string[];
|
|
12
15
|
private _firstIndex: number;
|
|
13
16
|
private _lastIndex: number;
|
|
14
17
|
private _endIndex: number;
|
|
15
18
|
|
|
19
|
+
get id(): string {
|
|
20
|
+
return this._id;
|
|
21
|
+
}
|
|
22
|
+
|
|
16
23
|
get type(): string {
|
|
17
24
|
return this._type;
|
|
18
25
|
}
|
|
@@ -21,6 +28,10 @@ export class Literal implements Pattern {
|
|
|
21
28
|
return this._name;
|
|
22
29
|
}
|
|
23
30
|
|
|
31
|
+
get value(): string {
|
|
32
|
+
return this._text;
|
|
33
|
+
}
|
|
34
|
+
|
|
24
35
|
get parent(): Pattern | null {
|
|
25
36
|
return this._parent;
|
|
26
37
|
}
|
|
@@ -33,20 +44,16 @@ export class Literal implements Pattern {
|
|
|
33
44
|
return [];
|
|
34
45
|
}
|
|
35
46
|
|
|
36
|
-
|
|
37
|
-
return this._isOptional;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
constructor(name: string, value: string, isOptional = false) {
|
|
47
|
+
constructor(name: string, value: string) {
|
|
41
48
|
if (value.length === 0) {
|
|
42
49
|
throw new Error("Value Cannot be empty.");
|
|
43
50
|
}
|
|
44
51
|
|
|
52
|
+
this._id = `literal-${idIndex++}`;
|
|
45
53
|
this._type = "literal";
|
|
46
54
|
this._name = name;
|
|
47
|
-
this.
|
|
55
|
+
this._text = value;
|
|
48
56
|
this._runes = Array.from(value);
|
|
49
|
-
this._isOptional = isOptional;
|
|
50
57
|
this._parent = null;
|
|
51
58
|
this._firstIndex = 0;
|
|
52
59
|
this._lastIndex = 0;
|
|
@@ -60,8 +67,10 @@ export class Literal implements Pattern {
|
|
|
60
67
|
return ast?.value === text;
|
|
61
68
|
}
|
|
62
69
|
|
|
63
|
-
exec(text: string) {
|
|
70
|
+
exec(text: string, record = false): ParseResult {
|
|
64
71
|
const cursor = new Cursor(text);
|
|
72
|
+
record && cursor.startRecording();
|
|
73
|
+
|
|
65
74
|
const ast = this.parse(cursor);
|
|
66
75
|
|
|
67
76
|
return {
|
|
@@ -71,6 +80,8 @@ export class Literal implements Pattern {
|
|
|
71
80
|
}
|
|
72
81
|
|
|
73
82
|
parse(cursor: Cursor): Node | null {
|
|
83
|
+
cursor.startParseWith(this);
|
|
84
|
+
|
|
74
85
|
this._firstIndex = cursor.index;
|
|
75
86
|
|
|
76
87
|
const passed = this._tryToParse(cursor);
|
|
@@ -80,16 +91,12 @@ export class Literal implements Pattern {
|
|
|
80
91
|
const node = this._createNode();
|
|
81
92
|
cursor.recordMatch(this, node);
|
|
82
93
|
|
|
94
|
+
cursor.endParse();
|
|
83
95
|
return node;
|
|
84
96
|
}
|
|
85
97
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
cursor.resolveError();
|
|
92
|
-
cursor.moveTo(this._firstIndex);
|
|
98
|
+
cursor.recordErrorAt(this._firstIndex, this._endIndex, this);
|
|
99
|
+
cursor.endParse();
|
|
93
100
|
return null;
|
|
94
101
|
}
|
|
95
102
|
|
|
@@ -103,11 +110,11 @@ export class Literal implements Pattern {
|
|
|
103
110
|
|
|
104
111
|
if (literalRune !== cursorRune) {
|
|
105
112
|
this._endIndex = cursor.index;
|
|
106
|
-
break
|
|
113
|
+
break;
|
|
107
114
|
}
|
|
108
115
|
|
|
109
116
|
if (i + 1 === literalRuneLength) {
|
|
110
|
-
this._lastIndex = this._firstIndex + this.
|
|
117
|
+
this._lastIndex = this._firstIndex + this._text.length - 1;
|
|
111
118
|
passed = true;
|
|
112
119
|
break;
|
|
113
120
|
}
|
|
@@ -120,7 +127,7 @@ export class Literal implements Pattern {
|
|
|
120
127
|
cursor.next();
|
|
121
128
|
}
|
|
122
129
|
|
|
123
|
-
return passed
|
|
130
|
+
return passed;
|
|
124
131
|
}
|
|
125
132
|
|
|
126
133
|
private _createNode(): Node {
|
|
@@ -130,17 +137,18 @@ export class Literal implements Pattern {
|
|
|
130
137
|
this._firstIndex,
|
|
131
138
|
this._lastIndex,
|
|
132
139
|
undefined,
|
|
133
|
-
this.
|
|
140
|
+
this._text
|
|
134
141
|
);
|
|
135
142
|
}
|
|
136
143
|
|
|
137
|
-
clone(name = this._name
|
|
138
|
-
const clone = new Literal(name, this.
|
|
144
|
+
clone(name = this._name): Pattern {
|
|
145
|
+
const clone = new Literal(name, this._text);
|
|
146
|
+
clone._id = this._id;
|
|
139
147
|
return clone;
|
|
140
148
|
}
|
|
141
149
|
|
|
142
150
|
getTokens(): string[] {
|
|
143
|
-
return [this.
|
|
151
|
+
return [this._text];
|
|
144
152
|
}
|
|
145
153
|
|
|
146
154
|
getTokensAfter(_lastMatched: Pattern): string[] {
|
|
@@ -149,7 +157,7 @@ export class Literal implements Pattern {
|
|
|
149
157
|
|
|
150
158
|
getNextTokens(): string[] {
|
|
151
159
|
if (this.parent == null) {
|
|
152
|
-
return []
|
|
160
|
+
return [];
|
|
153
161
|
}
|
|
154
162
|
|
|
155
163
|
return this.parent.getTokensAfter(this);
|
|
@@ -160,7 +168,7 @@ export class Literal implements Pattern {
|
|
|
160
168
|
}
|
|
161
169
|
|
|
162
170
|
getPatternsAfter(): Pattern[] {
|
|
163
|
-
return []
|
|
171
|
+
return [];
|
|
164
172
|
}
|
|
165
173
|
|
|
166
174
|
getNextPatterns(): Pattern[] {
|
|
@@ -168,11 +176,14 @@ export class Literal implements Pattern {
|
|
|
168
176
|
return [];
|
|
169
177
|
}
|
|
170
178
|
|
|
171
|
-
return this.parent.getPatternsAfter(this)
|
|
179
|
+
return this.parent.getPatternsAfter(this);
|
|
172
180
|
}
|
|
173
181
|
|
|
174
182
|
find(_predicate: (p: Pattern) => boolean): Pattern | null {
|
|
175
183
|
return null;
|
|
176
184
|
}
|
|
177
185
|
|
|
186
|
+
isEqual(pattern: Literal) {
|
|
187
|
+
return pattern.type === this.type && pattern._text === this._text;
|
|
188
|
+
}
|
|
178
189
|
}
|
package/src/patterns/Not.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Sequence } from "./Sequence";
|
|
2
2
|
import { Cursor } from "./Cursor";
|
|
3
3
|
import { Literal } from "./Literal";
|
|
4
4
|
import { Not } from "./Not";
|
|
@@ -86,7 +86,7 @@ describe("Not", () => {
|
|
|
86
86
|
|
|
87
87
|
test("Get Next Tokens", () => {
|
|
88
88
|
const notAboutUs = new Not("not-about-us", new Literal("about-us", "About Us"));
|
|
89
|
-
const sequence = new
|
|
89
|
+
const sequence = new Sequence("sequence", [notAboutUs, new Literal("about-them", "About Them")]);
|
|
90
90
|
|
|
91
91
|
const cloneNotAboutUs = sequence.find(p => p.name === "not-about-us") as Pattern;
|
|
92
92
|
const nextTokens = cloneNotAboutUs.getNextTokens() || [];
|
|
@@ -103,7 +103,7 @@ describe("Not", () => {
|
|
|
103
103
|
|
|
104
104
|
test("Get Tokens", () => {
|
|
105
105
|
const notAboutUs = new Not("not-about-us", new Literal("about-us", "About Us"));
|
|
106
|
-
const sequence = new
|
|
106
|
+
const sequence = new Sequence("sequence", [notAboutUs, new Literal("about-them", "About Them")]);
|
|
107
107
|
|
|
108
108
|
const cloneNotAboutUs = sequence.find(p => p.name === "not-about-us") as Pattern;
|
|
109
109
|
const nextTokens = cloneNotAboutUs.getTokens() || [];
|
|
@@ -113,7 +113,7 @@ describe("Not", () => {
|
|
|
113
113
|
|
|
114
114
|
test("Get Tokens After", () => {
|
|
115
115
|
const notAboutUs = new Not("not-about-us", new Literal("about-us", "About Us"));
|
|
116
|
-
const sequence = new
|
|
116
|
+
const sequence = new Sequence("sequence", [notAboutUs, new Literal("about-them", "About Them")]);
|
|
117
117
|
const notAboutUsClone = sequence.find(p => p.name === "not-about-us") as Pattern;
|
|
118
118
|
const aboutUsClone = sequence.find(p => p.name === "about-us") as Pattern;
|
|
119
119
|
const nextTokens = notAboutUsClone.getTokensAfter(aboutUsClone) || [];
|
|
@@ -130,7 +130,7 @@ describe("Not", () => {
|
|
|
130
130
|
|
|
131
131
|
test("Get Patterns", () => {
|
|
132
132
|
const notAboutUs = new Not("not-about-us", new Literal("about-us", "About Us"));
|
|
133
|
-
const sequence = new
|
|
133
|
+
const sequence = new Sequence("sequence", [notAboutUs, new Literal("about-them", "About Them")]);
|
|
134
134
|
|
|
135
135
|
const cloneNotAboutUs = sequence.find(p => p.name === "not-about-us") as Pattern;
|
|
136
136
|
const nextPatterns = cloneNotAboutUs.getPatterns();
|
|
@@ -141,7 +141,7 @@ describe("Not", () => {
|
|
|
141
141
|
|
|
142
142
|
test("Get Next Patterns", () => {
|
|
143
143
|
const notAboutUs = new Not("not-about-us", new Literal("about-us", "About Us"));
|
|
144
|
-
const sequence = new
|
|
144
|
+
const sequence = new Sequence("sequence", [notAboutUs, new Literal("about-them", "About Them")]);
|
|
145
145
|
|
|
146
146
|
const cloneNotAboutUs = sequence.find(p => p.name === "not-about-us") as Pattern;
|
|
147
147
|
const patterns = cloneNotAboutUs.getNextPatterns() || [];
|
|
@@ -159,7 +159,7 @@ describe("Not", () => {
|
|
|
159
159
|
|
|
160
160
|
test("Get Patterns After", () => {
|
|
161
161
|
const notAboutUs = new Not("not-about-us", new Literal("about-us", "About Us"));
|
|
162
|
-
const sequence = new
|
|
162
|
+
const sequence = new Sequence("sequence", [notAboutUs, new Literal("about-them", "About Them")]);
|
|
163
163
|
const notAboutUsClone = sequence.find(p => p.name === "not-about-us") as Pattern;
|
|
164
164
|
const aboutUsClone = sequence.find(p => p.name === "about-us") as Pattern;
|
|
165
165
|
const patterns = notAboutUsClone.getPatternsAfter(aboutUsClone) || [];
|
package/src/patterns/Not.ts
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { Node } from "../ast/Node";
|
|
2
2
|
import { Cursor } from "./Cursor";
|
|
3
|
+
import { ParseResult } from "./ParseResult";
|
|
3
4
|
import { Pattern } from "./Pattern";
|
|
4
5
|
|
|
6
|
+
let idIndex = 0;
|
|
7
|
+
|
|
5
8
|
export class Not implements Pattern {
|
|
9
|
+
private _id: string;
|
|
6
10
|
private _type: string;
|
|
7
11
|
private _name: string;
|
|
8
12
|
private _parent: Pattern | null;
|
|
9
13
|
private _children: Pattern[];
|
|
10
14
|
|
|
15
|
+
get id(): string {
|
|
16
|
+
return this._id;
|
|
17
|
+
}
|
|
18
|
+
|
|
11
19
|
get type(): string {
|
|
12
20
|
return this._type;
|
|
13
21
|
}
|
|
@@ -33,10 +41,11 @@ export class Not implements Pattern {
|
|
|
33
41
|
}
|
|
34
42
|
|
|
35
43
|
constructor(name: string, pattern: Pattern) {
|
|
44
|
+
this._id = `not-${idIndex++}`;
|
|
36
45
|
this._type = "not";
|
|
37
46
|
this._name = name;
|
|
38
47
|
this._parent = null;
|
|
39
|
-
this._children = [pattern.clone(
|
|
48
|
+
this._children = [pattern.clone()];
|
|
40
49
|
this._children[0].parent = this;
|
|
41
50
|
}
|
|
42
51
|
|
|
@@ -47,17 +56,21 @@ export class Not implements Pattern {
|
|
|
47
56
|
return !cursor.hasError;
|
|
48
57
|
}
|
|
49
58
|
|
|
50
|
-
exec(text: string) {
|
|
59
|
+
exec(text: string, record = false): ParseResult {
|
|
51
60
|
const cursor = new Cursor(text);
|
|
61
|
+
record && cursor.startRecording();
|
|
62
|
+
|
|
52
63
|
const ast = this.parse(cursor);
|
|
53
64
|
|
|
54
65
|
return {
|
|
55
|
-
ast,
|
|
66
|
+
ast: ast?.value === text ? ast : null,
|
|
56
67
|
cursor
|
|
57
68
|
};
|
|
58
69
|
}
|
|
59
70
|
|
|
60
71
|
parse(cursor: Cursor): Node | null {
|
|
72
|
+
cursor.startParseWith(this);
|
|
73
|
+
|
|
61
74
|
const firstIndex = cursor.index;
|
|
62
75
|
this._children[0].parse(cursor);
|
|
63
76
|
|
|
@@ -70,11 +83,13 @@ export class Not implements Pattern {
|
|
|
70
83
|
cursor.recordErrorAt(firstIndex, firstIndex, this);
|
|
71
84
|
}
|
|
72
85
|
|
|
86
|
+
cursor.endParse();
|
|
73
87
|
return null;
|
|
74
88
|
}
|
|
75
89
|
|
|
76
90
|
clone(name = this._name): Pattern {
|
|
77
91
|
const not = new Not(name, this._children[0]);
|
|
92
|
+
not._id = this._id;
|
|
78
93
|
return not;
|
|
79
94
|
}
|
|
80
95
|
|
|
@@ -100,7 +115,7 @@ export class Not implements Pattern {
|
|
|
100
115
|
|
|
101
116
|
getNextTokens(): string[] {
|
|
102
117
|
if (this.parent == null) {
|
|
103
|
-
return []
|
|
118
|
+
return [];
|
|
104
119
|
}
|
|
105
120
|
|
|
106
121
|
return this.parent.getTokensAfter(this);
|
|
@@ -117,7 +132,7 @@ export class Not implements Pattern {
|
|
|
117
132
|
return parent.getPatternsAfter(this);
|
|
118
133
|
}
|
|
119
134
|
|
|
120
|
-
return []
|
|
135
|
+
return [];
|
|
121
136
|
}
|
|
122
137
|
|
|
123
138
|
getNextPatterns(): Pattern[] {
|
|
@@ -125,11 +140,14 @@ export class Not implements Pattern {
|
|
|
125
140
|
return [];
|
|
126
141
|
}
|
|
127
142
|
|
|
128
|
-
return this.parent.getPatternsAfter(this)
|
|
143
|
+
return this.parent.getPatternsAfter(this);
|
|
129
144
|
}
|
|
130
145
|
|
|
131
146
|
find(predicate: (p: Pattern) => boolean): Pattern | null {
|
|
132
147
|
return predicate(this._children[0]) ? this._children[0] : null;
|
|
133
148
|
}
|
|
134
149
|
|
|
150
|
+
isEqual(pattern: Not): boolean {
|
|
151
|
+
return pattern.type === this.type && this.children.every((c, index) => c.isEqual(pattern.children[index]));
|
|
152
|
+
}
|
|
135
153
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { Literal } from "./Literal";
|
|
2
|
+
import { Not } from "./Not";
|
|
3
|
+
import { Optional } from "./Optional";
|
|
4
|
+
import { Pattern } from "./Pattern";
|
|
5
|
+
import { Sequence } from "./Sequence";
|
|
6
|
+
|
|
7
|
+
describe("Optional", () => {
|
|
8
|
+
test("Match", () => {
|
|
9
|
+
const text = new Literal("text", "Text");
|
|
10
|
+
const optional = new Optional("optional-text", text);
|
|
11
|
+
const result = optional.exec("Text");
|
|
12
|
+
|
|
13
|
+
expect(result?.ast?.value).toBe("Text");
|
|
14
|
+
expect(result.cursor.index).toBe(3);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("No Match", () => {
|
|
18
|
+
const text = new Literal("text", "Text");
|
|
19
|
+
const optional = new Optional("optional-text", text);
|
|
20
|
+
const result = optional.exec("Bad Text");
|
|
21
|
+
|
|
22
|
+
expect(result.ast).toBe(null);
|
|
23
|
+
expect(result.cursor.index).toBe(0);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("Test Match", () => {
|
|
27
|
+
const text = new Literal("text", "Text");
|
|
28
|
+
const optional = new Optional("optional-text", text);
|
|
29
|
+
const result = optional.test("Text");
|
|
30
|
+
|
|
31
|
+
expect(result).toBeTruthy();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("Test No Match", () => {
|
|
35
|
+
const text = new Literal("text", "Text");
|
|
36
|
+
const optional = new Optional("optional-text", text);
|
|
37
|
+
const result = optional.test("Bad Text");
|
|
38
|
+
|
|
39
|
+
expect(result).toBeTruthy();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("Clone", () => {
|
|
43
|
+
const a = new Literal("a", "A");
|
|
44
|
+
const optionalA = new Optional("optional-a", a);
|
|
45
|
+
const clone = optionalA.clone();
|
|
46
|
+
|
|
47
|
+
expect(clone.name).toBe("optional-a");
|
|
48
|
+
expect(clone).not.toBe(optionalA);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("Tokens", () => {
|
|
52
|
+
const a = new Literal("a", "A");
|
|
53
|
+
const optionalA = new Optional("optional-a", a);
|
|
54
|
+
const tokens = optionalA.getTokens();
|
|
55
|
+
const nextTokens = optionalA.getTokensAfter(new Literal("bogus", "bogus"));
|
|
56
|
+
const emptyArray: string[] = [];
|
|
57
|
+
|
|
58
|
+
expect(tokens).toEqual(["A"]);
|
|
59
|
+
expect(nextTokens).toEqual(emptyArray);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("Properties", () => {
|
|
63
|
+
const a = new Literal("a", "A");
|
|
64
|
+
const optionalA = new Optional("optional-a", a);
|
|
65
|
+
|
|
66
|
+
expect(optionalA.type).toBe("optional");
|
|
67
|
+
expect(optionalA.name).toBe("optional-a");
|
|
68
|
+
expect(optionalA.parent).toBeNull();
|
|
69
|
+
expect(optionalA.children[0].name).toBe("a");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("Get Next Tokens", () => {
|
|
73
|
+
const optionalAboutUs = new Optional("optional-about-us", new Literal("about-us", "About Us"));
|
|
74
|
+
const sequence = new Sequence("sequence", [optionalAboutUs, new Literal("about-them", "About Them")]);
|
|
75
|
+
|
|
76
|
+
const cloneOptionalAboutUs = sequence.find(p => p.name === "optional-about-us") as Pattern;
|
|
77
|
+
const nextTokens = cloneOptionalAboutUs.getNextTokens() || [];
|
|
78
|
+
|
|
79
|
+
expect(nextTokens[0]).toBe("About Them");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("Get Next Tokens With No Parent", () => {
|
|
83
|
+
const optionalAboutUs = new Optional("optional-about-us", new Literal("about-us", "About Us"));
|
|
84
|
+
const nextTokens = optionalAboutUs.getNextTokens() || [];
|
|
85
|
+
|
|
86
|
+
expect(nextTokens.length).toBe(0);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("Get Tokens", () => {
|
|
90
|
+
const optionalAboutUs = new Optional("optional-about-us", new Literal("about-us", "About Us"));
|
|
91
|
+
const sequence = new Sequence("sequence", [optionalAboutUs, new Literal("about-them", "About Them")]);
|
|
92
|
+
|
|
93
|
+
const cloneAboutUs = sequence.find(p => p.name === "optional-about-us") as Pattern;
|
|
94
|
+
const nextTokens = cloneAboutUs.getTokens() || [];
|
|
95
|
+
|
|
96
|
+
expect(nextTokens[0]).toBe("About Us");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("Get Tokens After", () => {
|
|
100
|
+
const optionalAboutUs = new Optional("optional-about-us", new Literal("about-us", "About Us"));
|
|
101
|
+
const sequence = new Sequence("sequence", [optionalAboutUs, new Literal("about-them", "About Them")]);
|
|
102
|
+
const optionalAboutUsClone = sequence.find(p => p.name === "optional-about-us") as Pattern;
|
|
103
|
+
const aboutUsClone = sequence.find(p => p.name === "about-us") as Pattern;
|
|
104
|
+
const nextTokens = optionalAboutUsClone.getTokensAfter(aboutUsClone) || [];
|
|
105
|
+
|
|
106
|
+
expect(nextTokens[0]).toBe("About Them");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("Find Pattern", () => {
|
|
110
|
+
const optionalAboutUs = new Optional("optional-about-us", new Literal("about-us", "About Us"));
|
|
111
|
+
const child = optionalAboutUs.find(p => p.name === "about-us");
|
|
112
|
+
|
|
113
|
+
expect(child).not.toBeNull();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("Get Patterns", () => {
|
|
117
|
+
const optionalAboutUs = new Optional("optional-about-us", new Literal("about-us", "About Us"));
|
|
118
|
+
const sequence = new Sequence("sequence", [optionalAboutUs, new Literal("about-them", "About Them")]);
|
|
119
|
+
|
|
120
|
+
const cloneNotAboutUs = sequence.find(p => p.name === "optional-about-us") as Pattern;
|
|
121
|
+
const nextPatterns = cloneNotAboutUs.getPatterns();
|
|
122
|
+
const expected = [sequence.find(p => p.name === "about-us")];
|
|
123
|
+
|
|
124
|
+
expect(nextPatterns).toEqual(expected);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("Get Next Patterns", () => {
|
|
128
|
+
const optionalAboutUs = new Optional("optional-about-us", new Literal("about-us", "About Us"));
|
|
129
|
+
const sequence = new Sequence("sequence", [optionalAboutUs, new Literal("about-them", "About Them")]);
|
|
130
|
+
|
|
131
|
+
const cloneNotAboutUs = sequence.find(p => p.name === "optional-about-us") as Pattern;
|
|
132
|
+
const patterns = cloneNotAboutUs.getNextPatterns() || [];
|
|
133
|
+
|
|
134
|
+
expect(patterns.length).toBe(1);
|
|
135
|
+
expect(patterns[0].name).toBe("about-them");
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("Get Next Patterns With No Parent", () => {
|
|
139
|
+
const optionalAboutUs = new Optional("optional-about-us", new Literal("about-us", "About Us"));
|
|
140
|
+
const patterns = optionalAboutUs.getNextPatterns() || [];
|
|
141
|
+
|
|
142
|
+
expect(patterns.length).toBe(0);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("Get Patterns After", () => {
|
|
146
|
+
const optionalAboutUs = new Optional("optional-about-us", new Literal("about-us", "About Us"));
|
|
147
|
+
const sequence = new Sequence("sequence", [optionalAboutUs, new Literal("about-them", "About Them")]);
|
|
148
|
+
const optionalAboutUsClone = sequence.find(p => p.name === "optional-about-us") as Pattern;
|
|
149
|
+
const aboutUsClone = sequence.find(p => p.name === "about-us") as Pattern;
|
|
150
|
+
const patterns = optionalAboutUsClone.getPatternsAfter(aboutUsClone) || [];
|
|
151
|
+
|
|
152
|
+
expect(patterns.length).toBe(1);
|
|
153
|
+
expect(patterns[0].name).toBe("about-them");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("Get Patterns After With Null Parent", () => {
|
|
157
|
+
const optionalAboutUs = new Optional("optional-about-us", new Literal("about-us", "About Us"));
|
|
158
|
+
const aboutUsClone = optionalAboutUs.find(p => p.name === "about-us") as Pattern;
|
|
159
|
+
const patterns = optionalAboutUs.getPatternsAfter(aboutUsClone) || [];
|
|
160
|
+
|
|
161
|
+
expect(patterns.length).toBe(0);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Node } from "../ast/Node";
|
|
2
|
+
import { Cursor } from "./Cursor";
|
|
3
|
+
import { ParseResult } from "./ParseResult";
|
|
4
|
+
import { Pattern } from "./Pattern";
|
|
5
|
+
|
|
6
|
+
let idIndex = 0;
|
|
7
|
+
|
|
8
|
+
export class Optional implements Pattern {
|
|
9
|
+
private _id: string;
|
|
10
|
+
private _type: string;
|
|
11
|
+
private _name: string;
|
|
12
|
+
private _parent: Pattern | null;
|
|
13
|
+
private _children: Pattern[];
|
|
14
|
+
|
|
15
|
+
get id(): string {
|
|
16
|
+
return this._id;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
get type(): string {
|
|
20
|
+
return this._type;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get name(): string {
|
|
24
|
+
return this._name;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get parent(): Pattern | null {
|
|
28
|
+
return this._parent;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
set parent(pattern: Pattern | null) {
|
|
32
|
+
this._parent = pattern;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get children(): Pattern[] {
|
|
36
|
+
return this._children;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
constructor(name: string, pattern: Pattern) {
|
|
40
|
+
this._id = `optional-${idIndex++}`;
|
|
41
|
+
this._type = "optional";
|
|
42
|
+
this._name = name;
|
|
43
|
+
this._parent = null;
|
|
44
|
+
this._children = [pattern.clone()];
|
|
45
|
+
this._children[0].parent = this;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
test(text: string) {
|
|
49
|
+
const cursor = new Cursor(text);
|
|
50
|
+
this.parse(cursor);
|
|
51
|
+
|
|
52
|
+
return !cursor.hasError;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
exec(text: string, record = false): ParseResult {
|
|
56
|
+
const cursor = new Cursor(text);
|
|
57
|
+
record && cursor.startRecording();
|
|
58
|
+
|
|
59
|
+
const ast = this.parse(cursor);
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
ast: ast?.value === text ? ast : null,
|
|
63
|
+
cursor
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
parse(cursor: Cursor): Node | null {
|
|
68
|
+
cursor.startParseWith(this);
|
|
69
|
+
|
|
70
|
+
const firstIndex = cursor.index;
|
|
71
|
+
const node = this._children[0].parse(cursor);
|
|
72
|
+
|
|
73
|
+
if (cursor.hasError) {
|
|
74
|
+
cursor.resolveError();
|
|
75
|
+
cursor.moveTo(firstIndex);
|
|
76
|
+
|
|
77
|
+
cursor.endParse();
|
|
78
|
+
return null;
|
|
79
|
+
} else {
|
|
80
|
+
cursor.endParse();
|
|
81
|
+
return node;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
clone(name = this._name): Pattern {
|
|
87
|
+
const optional = new Optional(name, this._children[0]);
|
|
88
|
+
optional._id = this._id;
|
|
89
|
+
return optional;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getTokens(): string[] {
|
|
93
|
+
return this._children[0].getTokens();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
getTokensAfter(_childReference: Pattern): string[] {
|
|
97
|
+
const parent = this._parent;
|
|
98
|
+
|
|
99
|
+
if (parent != null) {
|
|
100
|
+
return parent.getTokensAfter(this);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
getNextTokens(): string[] {
|
|
107
|
+
if (this.parent == null) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return this.parent.getTokensAfter(this);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
getPatterns(): Pattern[] {
|
|
115
|
+
return this._children[0].getPatterns();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
getPatternsAfter(_childReference: Pattern): Pattern[] {
|
|
119
|
+
const parent = this._parent;
|
|
120
|
+
|
|
121
|
+
if (parent != null) {
|
|
122
|
+
return parent.getPatternsAfter(this);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getNextPatterns(): Pattern[] {
|
|
129
|
+
if (this.parent == null) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return this.parent.getPatternsAfter(this);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
find(predicate: (p: Pattern) => boolean): Pattern | null {
|
|
137
|
+
return predicate(this._children[0]) ? this._children[0] : null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
isEqual(pattern: Optional): boolean {
|
|
141
|
+
return pattern.type === this.type && this.children.every((c, index) => c.isEqual(pattern.children[index]));
|
|
142
|
+
}
|
|
143
|
+
}
|