clarity-pattern-parser 5.0.0 → 6.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.
Files changed (53) hide show
  1. package/README.md +328 -38
  2. package/TODO.md +55 -1
  3. package/dist/ast/Node.d.ts +8 -2
  4. package/dist/index.browser.js +470 -205
  5. package/dist/index.browser.js.map +1 -1
  6. package/dist/index.d.ts +6 -1
  7. package/dist/index.esm.js +469 -206
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/index.js +470 -205
  10. package/dist/index.js.map +1 -1
  11. package/dist/intellisense/AutoComplete.d.ts +28 -0
  12. package/dist/intellisense/Suggestion.d.ts +11 -0
  13. package/dist/intellisense/SuggestionOption.d.ts +4 -0
  14. package/dist/patterns/And.d.ts +7 -7
  15. package/dist/patterns/Cursor.d.ts +6 -4
  16. package/dist/patterns/CursorHistory.d.ts +2 -2
  17. package/dist/patterns/Literal.d.ts +7 -8
  18. package/dist/patterns/Not.d.ts +8 -5
  19. package/dist/patterns/Or.d.ts +7 -5
  20. package/dist/patterns/Pattern.d.ts +7 -4
  21. package/dist/patterns/Reference.d.ts +10 -7
  22. package/dist/patterns/Regex.d.ts +7 -8
  23. package/dist/patterns/Repeat.d.ts +7 -7
  24. package/package.json +1 -1
  25. package/src/ast/Node.test.ts +110 -0
  26. package/src/ast/Node.ts +71 -5
  27. package/src/index.ts +14 -3
  28. package/src/intellisense/AutoComplete.test.ts +90 -12
  29. package/src/intellisense/AutoComplete.ts +66 -12
  30. package/src/intellisense/Suggestion.ts +3 -4
  31. package/src/intellisense/javascript/Javascript.test.ts +56 -56
  32. package/src/intellisense/javascript/escapedCharacter.ts +0 -1
  33. package/src/intellisense/javascript/exponent.ts +0 -2
  34. package/src/intellisense/javascript/fraction.ts +0 -2
  35. package/src/patterns/And.test.ts +63 -52
  36. package/src/patterns/And.ts +58 -36
  37. package/src/patterns/Cursor.ts +17 -14
  38. package/src/patterns/CursorHistory.ts +8 -8
  39. package/src/patterns/Literal.test.ts +70 -38
  40. package/src/patterns/Literal.ts +31 -42
  41. package/src/patterns/Not.test.ts +88 -8
  42. package/src/patterns/Not.ts +54 -14
  43. package/src/patterns/Or.test.ts +117 -13
  44. package/src/patterns/Or.ts +36 -13
  45. package/src/patterns/Pattern.ts +7 -4
  46. package/src/patterns/Reference.test.ts +117 -28
  47. package/src/patterns/Reference.ts +58 -32
  48. package/src/patterns/Regex.test.ts +67 -35
  49. package/src/patterns/Regex.ts +31 -43
  50. package/src/patterns/Repeat.test.ts +63 -41
  51. package/src/patterns/Repeat.ts +51 -38
  52. package/src/patterns/getNextPattern.test.ts +0 -39
  53. package/src/patterns/getNextPattern.ts +0 -18
@@ -3,7 +3,6 @@ import { Pattern } from "./Pattern";
3
3
  import { Node } from "../ast/Node";
4
4
  import { clonePatterns } from "./clonePatterns";
5
5
  import { filterOutNull } from "./filterOutNull";
6
- import { getNextPattern } from "./getNextPattern";
7
6
  import { findPattern } from "./findPattern";
8
7
 
9
8
  export class And implements Pattern {
@@ -14,7 +13,6 @@ export class And implements Pattern {
14
13
  private _isOptional: boolean;
15
14
  private _nodes: (Node | null)[];
16
15
  private _firstIndex: number;
17
- private _shouldReduceAst: boolean;
18
16
 
19
17
  get type(): string {
20
18
  return this._type;
@@ -54,7 +52,6 @@ export class And implements Pattern {
54
52
  this._parent = null;
55
53
  this._children = children;
56
54
  this._firstIndex = -1
57
- this._shouldReduceAst = false;
58
55
  this._nodes = [];
59
56
  }
60
57
 
@@ -64,12 +61,19 @@ export class And implements Pattern {
64
61
  }
65
62
  }
66
63
 
67
- parseText(text: string) {
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
  }
@@ -115,32 +119,41 @@ export class And implements Pattern {
115
119
  if (hasMorePatterns) {
116
120
  if (hadMatch) {
117
121
  if (cursor.hasNext()) {
122
+ // We had a match. Increment the cursor and use the next pattern.
118
123
  cursor.next();
119
124
  continue;
120
125
  } else {
126
+ // We are at the end of the text, it may still be valid, if all the
127
+ // following patterns are optional.
121
128
  if (this.areRemainingPatternsOptional(i)) {
122
129
  passed = true;
123
130
  break;
124
131
  }
125
132
 
133
+ // We didn't finish the parsing sequence.
126
134
  cursor.recordErrorAt(cursor.index + 1, this);
127
135
  break;
128
136
  }
129
137
  } else {
138
+ // An optional pattern did not matched, try from the same spot on the next
139
+ // pattern.
130
140
  cursor.moveTo(runningCursorIndex);
131
141
  continue;
132
142
  }
133
143
  } else {
144
+ // If we don't have any results from what we parsed then record error.
134
145
  const lastNode = this.getLastValidNode();
135
146
  if (lastNode === null) {
136
147
  cursor.recordErrorAt(cursor.index, this);
137
148
  break;
138
149
  }
139
150
 
151
+ // The sequence was parsed fully.
140
152
  passed = true;
141
153
  break;
142
154
  }
143
155
  } else {
156
+ // The pattern failed.
144
157
  cursor.moveTo(this._firstIndex);
145
158
  break;
146
159
  }
@@ -181,28 +194,15 @@ export class And implements Pattern {
181
194
 
182
195
  cursor.moveTo(lastIndex)
183
196
 
184
- if (this._shouldReduceAst) {
185
- children.length = 0;
186
- }
187
-
188
197
  return new Node(
189
198
  "and",
190
199
  this._name,
191
200
  this._firstIndex,
192
201
  lastIndex,
193
- children,
194
- this._shouldReduceAst ? value : undefined
202
+ children
195
203
  );
196
204
  }
197
205
 
198
- enableAstReduction(): void {
199
- this._shouldReduceAst = true;
200
- }
201
-
202
- disableAstReduction(): void {
203
- this._shouldReduceAst = false;
204
- }
205
-
206
206
  getTokens(): string[] {
207
207
  const tokens: string[] = [];
208
208
 
@@ -217,14 +217,31 @@ export class And implements Pattern {
217
217
  return tokens;
218
218
  }
219
219
 
220
- getNextTokens(lastMatched: Pattern): string[] {
220
+ getTokensAfter(childReference: Pattern): string[] {
221
+ const patterns = this.getPatternsAfter(childReference);
222
+ const tokens: string[] = [];
223
+
224
+ patterns.forEach(p => tokens.push(...p.getTokens()));
225
+
226
+ return tokens;
227
+ }
228
+
229
+ getNextTokens(): string[] {
230
+ if (this.parent == null) {
231
+ return []
232
+ }
233
+
234
+ return this.parent.getTokensAfter(this);
235
+ }
236
+
237
+ getPatternsAfter(childReference: Pattern): Pattern[] {
221
238
  let nextSibling: Pattern | null = null;
222
239
  let nextSiblingIndex = -1;
223
240
  let index = -1;
224
- const tokens: string[] = [];
241
+ const patterns: Pattern[] = [];
225
242
 
226
243
  for (let i = 0; i < this._children.length; i++) {
227
- if (this._children[i] === lastMatched) {
244
+ if (this._children[i] === childReference) {
228
245
  if (i + 1 < this._children.length) {
229
246
  nextSibling = this._children[i + 1];
230
247
  }
@@ -234,48 +251,53 @@ export class And implements Pattern {
234
251
  }
235
252
  }
236
253
 
254
+ // The child reference isn't one of the child patterns.
237
255
  if (index === -1) {
238
256
  return [];
239
257
  }
240
258
 
259
+ // The reference pattern is the last child. So ask the parent for the next pattern.
241
260
  if (nextSiblingIndex === this._children.length && this._parent !== null) {
242
- return this._parent.getNextTokens(this);
261
+ return this._parent.getPatternsAfter(this);
243
262
  }
244
263
 
264
+ // Next pattern isn't optional so send it back as the next patterns.
245
265
  if (nextSibling !== null && !nextSibling.isOptional) {
246
- return nextSibling.getTokens();
266
+ return [nextSibling];
247
267
  }
248
268
 
269
+ // Send back as many optional patterns as possible.
249
270
  if (nextSibling !== null && nextSibling.isOptional) {
250
271
  for (let i = nextSiblingIndex; i < this._children.length; i++) {
251
272
  const child = this._children[i];
252
- tokens.push(...child.getTokens());
273
+ patterns.push(child);
253
274
 
254
275
  if (!child.isOptional) {
255
276
  break;
256
277
  }
257
278
 
258
279
  if (i === this._children.length - 1 && this._parent !== null) {
259
- tokens.push(...this._parent.getNextTokens(this));
280
+ patterns.push(...this._parent.getPatternsAfter(this));
260
281
  }
261
282
  }
262
283
  }
263
284
 
264
- return tokens;
285
+ return patterns;
265
286
  }
266
287
 
267
- getNextPattern(): Pattern | null {
268
- return getNextPattern(this)
288
+ getNextPatterns(): Pattern[] {
289
+ if (this.parent == null) {
290
+ return [];
291
+ }
292
+
293
+ return this.parent.getPatternsAfter(this)
269
294
  }
270
295
 
271
- findPattern(isMatch: (p: Pattern)=>boolean): Pattern | null{
272
- return findPattern(this, isMatch);
296
+ findPattern(predicate: (p: Pattern) => boolean): Pattern | null {
297
+ return findPattern(this, predicate);
273
298
  }
274
299
 
275
300
  clone(name = this._name, isOptional = this._isOptional): Pattern {
276
- const and = new And(name, this._children, isOptional)
277
- and._shouldReduceAst = this._shouldReduceAst;
278
-
279
- return and
301
+ return new And(name, this._children, isOptional)
280
302
  }
281
303
  }
@@ -1,5 +1,7 @@
1
- import { Pattern, Node, ParseError } from "..";
1
+ import { Node } from "../ast/Node";
2
2
  import { CursorHistory, Match } from "./CursorHistory";
3
+ import { ParseError } from "./ParseError";
4
+ import { Pattern } from "./Pattern";
3
5
 
4
6
  export class Cursor {
5
7
  private _text: string;
@@ -16,21 +18,21 @@ export class Cursor {
16
18
  }
17
19
 
18
20
  get isOnLast(): boolean {
19
- return this._index === this._getLastIndex();
21
+ return this._index === this.getLastIndex();
20
22
  }
21
23
 
22
24
  get isRecording(): boolean {
23
25
  return this._history.isRecording;
24
26
  }
25
27
 
26
- get leafMatch(): Match {
27
- return this._history.leafMatch;
28
- }
29
-
30
28
  get rootMatch(): Match {
31
29
  return this._history.rootMatch;
32
30
  }
33
31
 
32
+ get leafMatch(): Match {
33
+ return this._history.leafMatch;
34
+ }
35
+
34
36
  get furthestError(): ParseError | null {
35
37
  return this._history.furthestError;
36
38
  }
@@ -70,16 +72,16 @@ export class Cursor {
70
72
  return this._index + 1 < this._length;
71
73
  }
72
74
 
73
- hasPrevious(): boolean {
74
- return this._index - 1 >= 0;
75
- }
76
-
77
75
  next(): void {
78
76
  if (this.hasNext()) {
79
77
  this._index++;
80
78
  }
81
79
  }
82
80
 
81
+ hasPrevious(): boolean {
82
+ return this._index - 1 >= 0;
83
+ }
84
+
83
85
  previous(): void {
84
86
  if (this.hasPrevious()) {
85
87
  this._index--;
@@ -97,7 +99,11 @@ export class Cursor {
97
99
  }
98
100
 
99
101
  moveToLastChar(): void {
100
- this._index = this._getLastIndex();
102
+ this._index = this.getLastIndex();
103
+ }
104
+
105
+ getLastIndex(): number {
106
+ return this._length - 1;
101
107
  }
102
108
 
103
109
  getChars(first: number, last: number): string {
@@ -124,7 +130,4 @@ export class Cursor {
124
130
  this._history.stopRecording();
125
131
  }
126
132
 
127
- private _getLastIndex(): number {
128
- return this._length - 1;
129
- }
130
133
  }
@@ -17,6 +17,14 @@ export class CursorHistory {
17
17
  private _nodes: Node[] = [];
18
18
  private _errors: ParseError[] = [];
19
19
 
20
+ get isRecording(): boolean {
21
+ return this._isRecording;
22
+ }
23
+
24
+ get rootMatch(): Match {
25
+ return this._rootMatch;
26
+ }
27
+
20
28
  get leafMatch(): Match {
21
29
  return this._leafMatch;
22
30
  }
@@ -25,10 +33,6 @@ export class CursorHistory {
25
33
  return this._furthestError;
26
34
  }
27
35
 
28
- get isRecording(): boolean {
29
- return this._isRecording;
30
- }
31
-
32
36
  get errors(): ParseError[] {
33
37
  return this._errors;
34
38
  }
@@ -45,10 +49,6 @@ export class CursorHistory {
45
49
  return this._patterns;
46
50
  }
47
51
 
48
- get rootMatch(): Match {
49
- return this._rootMatch;
50
- }
51
-
52
52
  recordMatch(pattern: Pattern, node: Node): void {
53
53
  if (this._isRecording) {
54
54
  this._patterns.push(pattern);
@@ -70,53 +70,22 @@ describe("Literal", () => {
70
70
  });
71
71
 
72
72
  test("Get Tokens", () => {
73
- const parent = new And("parent", [
74
- new Literal("a", "A"),
75
- new Literal("b", "B")
76
- ]);
73
+ const a = new Literal("a", "A");
77
74
 
78
- const a = parent.children[0] as Literal;
79
- const b = parent.children[1];
80
-
81
- let tokens = a.getTokens();
82
- let expectedTokens = ["A"];
83
-
84
- expect(tokens).toEqual(expectedTokens);
85
-
86
- a.enableContextualTokenAggregation();
87
-
88
- tokens = a.getTokens();
89
- expectedTokens = ["AB"];
90
-
91
- expect(tokens).toEqual(expectedTokens);
92
-
93
- a.disableContextualTokenAggregation();
94
-
95
- tokens = a.getTokens();
96
- expectedTokens = ["A"];
75
+ const tokens = a.getTokens();
76
+ const expectedTokens = ["A"];
97
77
 
98
78
  expect(tokens).toEqual(expectedTokens);
99
79
  });
100
80
 
101
- test("Get Next Tokens", () => {
81
+ test("Get Tokens After", () => {
102
82
  const literal = new Literal("a", "A");
103
- const tokens = literal.getNextTokens(new Literal("bogus", "bogus"));
83
+ const tokens = literal.getTokensAfter(new Literal("bogus", "bogus"));
104
84
  const expected: string[] = [];
105
85
 
106
86
  expect(tokens).toEqual(expected)
107
87
  });
108
88
 
109
- test("Get Next Pattern", () => {
110
- const parent = new And("parent", [
111
- new Literal("a", "A"),
112
- new Literal("b", "B")
113
- ]);
114
-
115
- const nextPattern = parent.children[0].getNextPattern();
116
-
117
- expect(nextPattern?.name).toBe("b")
118
- });
119
-
120
89
  test("Properties", () => {
121
90
  const literal = new Literal("a", "A");
122
91
 
@@ -126,9 +95,72 @@ describe("Literal", () => {
126
95
  expect(literal.children).toEqual([]);
127
96
  });
128
97
 
129
- test("Parse Text", () => {
98
+ test("Exec", () => {
130
99
  const literal = new Literal("a", "A");
131
- const { ast: result } = literal.parseText("B");
100
+ const { ast: result } = literal.exec("B");
132
101
  expect(result).toBeNull()
133
102
  });
103
+
104
+ test("Test With No Match", () => {
105
+ const literal = new Literal("a", "A");
106
+ const isMatch = literal.test("B");
107
+
108
+ expect(isMatch).toBeFalsy();
109
+ });
110
+
111
+ test("Test With Match", () => {
112
+ const literal = new Literal("a", "A");
113
+ const isMatch = literal.test("A");
114
+
115
+ expect(isMatch).toBeTruthy();
116
+ });
117
+
118
+ test("Get Next Tokens", () => {
119
+ const sequence = new And("sequence", [new Literal("a", "A")]);
120
+ const parent = new And("parent", [sequence, new Literal("b", "B")]);
121
+
122
+ const a = parent.findPattern(p => p.name === "a");
123
+ const tokens = a?.getNextTokens() || [];
124
+
125
+ expect(tokens[0]).toBe("B");
126
+ });
127
+
128
+ test("Get Next Tokens With Null Parent", () => {
129
+ const a = new Literal("a", "A");
130
+ const tokens = a.getNextTokens();
131
+
132
+ expect(tokens.length).toBe(0);
133
+ });
134
+
135
+ test("Get Next Patterns", () => {
136
+ const sequence = new And("sequence", [new Literal("a", "A")]);
137
+ const parent = new And("parent", [sequence, new Literal("b", "B")]);
138
+
139
+ const a = parent.findPattern(p => p.name === "a");
140
+ const nextPatterns = a?.getNextPatterns() || [];
141
+ const b = parent.findPattern(p => p.name === "b")
142
+
143
+ expect(nextPatterns[0]).toBe(b);
144
+ });
145
+
146
+ test("Get Next Patterns With Null Parent", () => {
147
+ const a = new Literal("a", "A");
148
+ const nextPatterns = a.getNextPatterns();
149
+
150
+ expect(nextPatterns.length).toBe(0);
151
+ });
152
+
153
+ test("Get Patterns After", () => {
154
+ const a = new Literal("a", "A");
155
+ const patterns = a.getPatternsAfter();
156
+
157
+ expect(patterns.length).toBe(0);
158
+ });
159
+
160
+ test("Find Pattern", () => {
161
+ const a = new Literal("a", "A");
162
+ const pattern = a.findPattern(p => p.name === "nada");
163
+
164
+ expect(pattern).toBeNull();
165
+ });
134
166
  });
@@ -1,6 +1,5 @@
1
1
  import { Node } from "../ast/Node";
2
2
  import { Cursor } from "./Cursor";
3
- import { getNextPattern } from "./getNextPattern";
4
3
  import { Pattern } from "./Pattern";
5
4
 
6
5
  export class Literal implements Pattern {
@@ -12,8 +11,6 @@ export class Literal implements Pattern {
12
11
  private _runes: string[];
13
12
  private _firstIndex: number;
14
13
  private _lastIndex: number;
15
- private _hasContextualTokenAggregation: boolean;
16
- private _isRetrievingContextualTokens: boolean;
17
14
 
18
15
  get type(): string {
19
16
  return this._type;
@@ -40,10 +37,10 @@ export class Literal implements Pattern {
40
37
  }
41
38
 
42
39
  constructor(name: string, value: string, isOptional = false) {
43
- if (value.length === 0){
40
+ if (value.length === 0) {
44
41
  throw new Error("Value Cannot be empty.");
45
42
  }
46
-
43
+
47
44
  this._type = "literal";
48
45
  this._name = name;
49
46
  this._literal = value;
@@ -52,16 +49,21 @@ export class Literal implements Pattern {
52
49
  this._parent = null;
53
50
  this._firstIndex = 0;
54
51
  this._lastIndex = 0;
55
- this._hasContextualTokenAggregation = false;
56
- this._isRetrievingContextualTokens = false;
57
52
  }
58
53
 
59
- parseText(text: string) {
54
+ test(text: string) {
55
+ const cursor = new Cursor(text);
56
+ const ast = this.parse(cursor);
57
+
58
+ return ast?.value === text;
59
+ }
60
+
61
+ exec(text: string) {
60
62
  const cursor = new Cursor(text);
61
- const ast = this.parse(cursor)
63
+ const ast = this.parse(cursor);
62
64
 
63
65
  return {
64
- ast,
66
+ ast: ast?.value === text ? ast : null,
65
67
  cursor
66
68
  };
67
69
  }
@@ -123,59 +125,46 @@ export class Literal implements Pattern {
123
125
  this._name,
124
126
  this._firstIndex,
125
127
  this._lastIndex,
126
- [],
128
+ undefined,
127
129
  this._literal
128
130
  );
129
131
  }
130
132
 
131
133
  clone(name = this._name, isOptional = this._isOptional): Pattern {
132
134
  const clone = new Literal(name, this._literal, isOptional);
133
- clone._hasContextualTokenAggregation = this._hasContextualTokenAggregation;
134
135
  return clone;
135
136
  }
136
137
 
137
138
  getTokens(): string[] {
138
- const parent = this._parent;
139
-
140
- if (
141
- this._hasContextualTokenAggregation &&
142
- parent != null &&
143
- !this._isRetrievingContextualTokens
144
- ) {
145
- this._isRetrievingContextualTokens = true;
146
-
147
- const aggregateTokens: string[] = [];
148
- const nextTokens = parent.getNextTokens(this);
149
-
150
- for (const nextToken of nextTokens) {
151
- aggregateTokens.push(this._literal + nextToken);
152
- }
153
-
154
- this._isRetrievingContextualTokens = false;
155
- return aggregateTokens;
156
- } else {
157
- return [this._literal];
158
- }
139
+ return [this._literal];
159
140
  }
160
141
 
161
- getNextTokens(_lastMatched: Pattern): string[] {
142
+ getTokensAfter(_lastMatched: Pattern): string[] {
162
143
  return [];
163
144
  }
164
145
 
165
- getNextPattern(): Pattern | null {
166
- return getNextPattern(this)
146
+ getNextTokens(): string[] {
147
+ if (this.parent == null) {
148
+ return []
149
+ }
150
+
151
+ return this.parent.getTokensAfter(this);
167
152
  }
168
153
 
169
- findPattern(_isMatch: (p: Pattern) => boolean): Pattern | null {
170
- return null;
154
+ getPatternsAfter(): Pattern[] {
155
+ return []
171
156
  }
172
157
 
173
- enableContextualTokenAggregation(): void {
174
- this._hasContextualTokenAggregation = true;
158
+ getNextPatterns(): Pattern[] {
159
+ if (this.parent == null) {
160
+ return [];
161
+ }
162
+
163
+ return this.parent.getPatternsAfter(this)
175
164
  }
176
165
 
177
- disableContextualTokenAggregation(): void {
178
- this._hasContextualTokenAggregation = false;
166
+ findPattern(_predicate: (p: Pattern) => boolean): Pattern | null {
167
+ return null;
179
168
  }
180
169
 
181
170
  }