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.
Files changed (61) hide show
  1. package/README.md +328 -38
  2. package/TODO.md +63 -7
  3. package/dist/ast/Node.d.ts +8 -2
  4. package/dist/index.browser.js +520 -205
  5. package/dist/index.browser.js.map +1 -1
  6. package/dist/index.d.ts +6 -1
  7. package/dist/index.esm.js +519 -206
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/index.js +520 -205
  10. package/dist/index.js.map +1 -1
  11. package/dist/intellisense/AutoComplete.d.ts +34 -0
  12. package/dist/intellisense/Suggestion.d.ts +10 -0
  13. package/dist/intellisense/SuggestionOption.d.ts +4 -0
  14. package/dist/patterns/And.d.ts +8 -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 +8 -8
  18. package/dist/patterns/Not.d.ts +9 -5
  19. package/dist/patterns/Or.d.ts +8 -5
  20. package/dist/patterns/Pattern.d.ts +8 -4
  21. package/dist/patterns/Reference.d.ts +11 -7
  22. package/dist/patterns/Regex.d.ts +8 -8
  23. package/dist/patterns/Repeat.d.ts +8 -7
  24. package/package.json +1 -1
  25. package/src/ast/Node.test.ts +116 -0
  26. package/src/ast/Node.ts +71 -5
  27. package/src/index.ts +14 -3
  28. package/src/intellisense/AutoComplete.test.ts +168 -23
  29. package/src/intellisense/AutoComplete.ts +102 -21
  30. package/src/intellisense/Suggestion.ts +3 -4
  31. package/src/intellisense/javascript/Javascript.test.ts +86 -62
  32. package/src/intellisense/javascript/{expressionStatement.ts → assignment.ts} +7 -8
  33. package/src/intellisense/javascript/escapedCharacter.ts +0 -1
  34. package/src/intellisense/javascript/exponent.ts +0 -2
  35. package/src/intellisense/javascript/expression.ts +44 -26
  36. package/src/intellisense/javascript/fraction.ts +0 -2
  37. package/src/intellisense/javascript/infixOperator.ts +6 -2
  38. package/src/intellisense/javascript/keywords.ts +3 -0
  39. package/src/intellisense/javascript/objectAccess.ts +9 -0
  40. package/src/intellisense/javascript/objectLiteral.ts +3 -3
  41. package/src/intellisense/javascript/propertyAccess.ts +8 -3
  42. package/src/intellisense/javascript/stringLiteral.ts +16 -8
  43. package/src/patterns/And.test.ts +74 -50
  44. package/src/patterns/And.ts +72 -36
  45. package/src/patterns/Cursor.ts +17 -14
  46. package/src/patterns/CursorHistory.ts +8 -8
  47. package/src/patterns/Literal.test.ts +79 -38
  48. package/src/patterns/Literal.ts +34 -41
  49. package/src/patterns/Not.test.ts +99 -8
  50. package/src/patterns/Not.ts +58 -14
  51. package/src/patterns/Or.test.ts +128 -13
  52. package/src/patterns/Or.ts +46 -13
  53. package/src/patterns/Pattern.ts +8 -4
  54. package/src/patterns/Reference.test.ts +127 -28
  55. package/src/patterns/Reference.ts +62 -32
  56. package/src/patterns/Regex.test.ts +76 -35
  57. package/src/patterns/Regex.ts +35 -43
  58. package/src/patterns/Repeat.test.ts +72 -41
  59. package/src/patterns/Repeat.ts +55 -38
  60. package/src/patterns/getNextPattern.test.ts +0 -39
  61. package/src/patterns/getNextPattern.ts +0 -18
@@ -15,6 +15,25 @@ describe("Node", () => {
15
15
  expect(node.children.length).toBe(0);
16
16
  });
17
17
 
18
+ test("Properties", () => {
19
+ const child = new Node("child", "child", 0, 0, undefined, "Content")
20
+ const parent = new Node("parent", "parent", 0, 0, [
21
+ child,
22
+ ]);
23
+
24
+ expect(parent.hasChildren).toBeTruthy();
25
+ expect(child.parent).toBe(parent);
26
+ expect(parent.children[0]).toBe(child);
27
+ expect(child.type).toBe("child");
28
+ expect(parent.value).toBe("Content");
29
+ expect(child.value).toBe("Content");
30
+ expect(child.name).toBe("child");
31
+ expect(child.firstIndex).toBe(0);
32
+ expect(child.lastIndex).toBe(0);
33
+ expect(child.startIndex).toBe(0);
34
+ expect(child.endIndex).toBe(1);
35
+ });
36
+
18
37
  test("Create Tree", () => {
19
38
  const child = new Node("child", "child", 0, 0, [], "Child");
20
39
  const parent = new Node("parent", "parent", 0, 0, [child]);
@@ -164,6 +183,56 @@ describe("Node", () => {
164
183
  expect(b.value).toBe("B");
165
184
  });
166
185
 
186
+ test("Next Sibling", () => {
187
+ const a = new Node("a", "a", 0, 0, [], "A");
188
+ const b = new Node("b", "b", 0, 0, [], "B");
189
+ new Node("parent", "parent", 0, 0, [a, b]);
190
+
191
+ const nextSibling = a.nextSibling()
192
+
193
+ expect(nextSibling).toBe(b);
194
+ });
195
+
196
+ test("Next Sibling (No Parent)", () => {
197
+ const a = new Node("a", "a", 0, 0, [], "A");
198
+
199
+ const nextSibling = a.nextSibling()
200
+ expect(nextSibling).toBeNull;
201
+ });
202
+
203
+ test("Next Sibling (Last Child)", () => {
204
+ const a = new Node("a", "a", 0, 0, [], "A");
205
+ const b = new Node("b", "b", 0, 0, [], "B");
206
+ new Node("parent", "parent", 0, 0, [a, b]);
207
+
208
+ const nextSibling = b.nextSibling()
209
+ expect(nextSibling).toBeNull;
210
+ });
211
+
212
+ test("Previous Sibling", () => {
213
+ const a = new Node("a", "a", 0, 0, [], "A");
214
+ const b = new Node("b", "b", 0, 0, [], "B");
215
+ new Node("parent", "parent", 0, 0, [a, b]);
216
+
217
+ const previousSibling = b.previousSibling()
218
+
219
+ expect(previousSibling).toBe(a);
220
+ });
221
+
222
+ test("Previous Sibling (No Parent)", () => {
223
+ const a = new Node("a", "a", 0, 0, [], "A");
224
+ const previousSibling = a.previousSibling()
225
+ expect(previousSibling).toBeNull();
226
+ });
227
+
228
+ test("Previous Sibling (First Child)", () => {
229
+ const a = new Node("a", "a", 0, 0, [], "A");
230
+ const b = new Node("b", "b", 0, 0, [], "B");
231
+ new Node("parent", "parent", 0, 0, [a, b]);
232
+
233
+ const previousSibling = a.previousSibling()
234
+ expect(previousSibling).toBeNull;
235
+ });
167
236
 
168
237
  test("Find", () => {
169
238
  const a = new Node("a", "a", 0, 0, [], "A");
@@ -251,4 +320,51 @@ describe("Node", () => {
251
320
  expect(result).toEqual(expected)
252
321
  });
253
322
 
323
+ test("Reduce", () => {
324
+ const parent = new Node("parent", "parent", 0, 6, [
325
+ new Node("child", "child", 0, 6, undefined, "Content")
326
+ ]);
327
+
328
+ parent.reduce();
329
+
330
+ expect(parent.hasChildren).toBeFalsy();
331
+ expect(parent.value).toBe("Content")
332
+ });
333
+
334
+ test("Flatten", () => {
335
+ const a = new Node("a", "a", 0, 0, [], "A");
336
+ const b = new Node("b", "b", 1, 1, [], "B");
337
+ const c = new Node("c", "c", 2, 2, [], "C");
338
+
339
+ const parent = new Node("parent", "parent", 0, 1, [
340
+ a,
341
+ b,
342
+ ]);
343
+
344
+ const grandParent = new Node("grand-parent", "grand-parent", 0, 2, [
345
+ parent,
346
+ c,
347
+ ]);
348
+
349
+ const nodes = grandParent.flatten();
350
+ const expected = [a, b, c];
351
+
352
+ expect(nodes).toEqual(expected)
353
+ });
354
+
355
+ test("Find Ancester", () => {
356
+ const child = new Node("child", "child", 0, 0, []);
357
+ const parent = new Node("parent", "parent", 0, 0, [child]);
358
+ const grandParent = new Node("grand-parent", "grand-parent", 0, 0, [parent]);
359
+ const result = child.findAncester(p => p.name === "grand-parent")
360
+
361
+ expect(result).toBe(grandParent)
362
+ });
363
+
364
+ test("Find Ancester Without Parent", () => {
365
+ const child = new Node("child", "child", 0, 0, []);
366
+ const result = child.findAncester(p => p.name === "parent")
367
+ expect(result).toBeNull()
368
+ });
369
+
254
370
  });
package/src/ast/Node.ts CHANGED
@@ -50,6 +50,10 @@ export class Node {
50
50
  return this._children;
51
51
  }
52
52
 
53
+ get hasChildren(): boolean {
54
+ return this._children.length > 0;
55
+ }
56
+
53
57
  get value() {
54
58
  return this.toString();
55
59
  }
@@ -120,25 +124,69 @@ export class Node {
120
124
  spliceChildren(index: number, deleteCount: number, ...items: Node[]) {
121
125
  const removedItems = this._children.splice(index, deleteCount, ...items);
122
126
 
123
- items.forEach(i => i._parent = this);
124
127
  removedItems.forEach(i => i._parent = null);
128
+ items.forEach(i => i._parent = this);
125
129
 
126
130
  return removedItems;
127
131
  }
128
132
 
129
- find(isMatch: (node: Node) => boolean): Node | null {
130
- return this.findAll(isMatch)[0] || null
133
+ nextSibling() {
134
+ if (this._parent == null) {
135
+ return null;
136
+ }
137
+
138
+ const children = this._parent._children;
139
+ const index = children.indexOf(this);
140
+
141
+ if (index > -1 && index < children.length - 1) {
142
+ return children[index + 1]
143
+ }
144
+
145
+ return null
146
+ }
147
+
148
+ previousSibling() {
149
+ if (this._parent == null) {
150
+ return null;
151
+ }
152
+
153
+ const children = this._parent._children;
154
+ const index = children.indexOf(this);
155
+
156
+ if (index > -1 && index > 0) {
157
+ return children[index - 1]
158
+ }
159
+
160
+ return null
131
161
  }
132
162
 
133
- findAll(isMatch: (node: Node) => boolean): Node[] {
163
+ find(predicate: (node: Node) => boolean): Node | null {
164
+ return this.findAll(predicate)[0] || null
165
+ }
166
+
167
+ findAll(predicate: (node: Node) => boolean): Node[] {
134
168
  const matches: Node[] = [];
135
169
  this.walkUp(n => {
136
- if (isMatch(n)) { matches.push(n); }
170
+ if (predicate(n)) { matches.push(n); }
137
171
  })
138
172
 
139
173
  return matches;
140
174
  }
141
175
 
176
+ findAncester(predicate: (node: Node) => boolean){
177
+ let parent = this._parent;
178
+
179
+ while(parent != null){
180
+ if (predicate(parent)){
181
+ return parent;
182
+ }
183
+
184
+ parent = parent._parent;
185
+ }
186
+
187
+ return null;
188
+ }
189
+
142
190
  walkUp(callback: (node: Node) => void) {
143
191
  this.children.forEach(c => c.walkUp(callback))
144
192
  callback(this);
@@ -149,6 +197,24 @@ export class Node {
149
197
  this.children.forEach(c => c.walkDown(callback))
150
198
  }
151
199
 
200
+ flatten(){
201
+ const nodes: Node[] = [];
202
+
203
+ this.walkDown((node: Node)=>{
204
+ if (!node.hasChildren){
205
+ nodes.push(node);
206
+ }
207
+ });
208
+
209
+ return nodes;
210
+ }
211
+
212
+ reduce() {
213
+ const value = this.toString();
214
+ this.removeAllChildren();
215
+ this._value = value;
216
+ }
217
+
152
218
  clone(): Node {
153
219
  return new Node(
154
220
  this._type,
package/src/index.ts CHANGED
@@ -9,17 +9,28 @@ import { Repeat } from "./patterns/Repeat";
9
9
  import { ParseError } from "./patterns/ParseError";
10
10
  import { Pattern } from "./patterns/Pattern";
11
11
  import { Reference } from "./patterns/Reference";
12
+ import { AutoComplete } from './intellisense/AutoComplete';
13
+ import { CursorHistory, Match } from "./patterns/CursorHistory";
14
+ import { ParseResult } from "./patterns/ParseResult";
15
+ import { Suggestion } from "./intellisense/Suggestion";
16
+ import { SuggestionOption } from "./intellisense/SuggestionOption";
12
17
 
13
18
  export {
14
19
  Node,
15
- Cursor,
16
- Regex,
20
+ AutoComplete,
21
+ Suggestion,
22
+ SuggestionOption,
17
23
  And,
24
+ Cursor,
25
+ CursorHistory,
26
+ Match,
18
27
  Literal,
19
28
  Not,
20
29
  Or,
21
- Repeat,
22
30
  ParseError,
31
+ ParseResult,
23
32
  Pattern,
24
33
  Reference,
34
+ Regex,
35
+ Repeat,
25
36
  };
@@ -2,17 +2,19 @@ import { And } from "../patterns/And";
2
2
  import { findPattern } from "../patterns/findPattern";
3
3
  import { Literal } from "../patterns/Literal";
4
4
  import { Or } from "../patterns/Or";
5
- import { AutoComplete } from "./AutoComplete";
5
+ import { Regex } from "../patterns/Regex";
6
+ import { Repeat } from "../patterns/Repeat";
7
+ import { AutoComplete, AutoCompleteOptions } from "./AutoComplete";
6
8
 
7
9
  describe("AutoComplete", () => {
8
10
  test("No Text", () => {
9
11
  const name = new Literal("name", "Name");
10
12
  const autoComplete = new AutoComplete(name);
11
- let result = autoComplete.suggest("");
13
+ let result = autoComplete.suggestFor("");
12
14
 
13
15
  expect(result.options[0].text).toBe("Name");
14
16
  expect(result.options[0].startIndex).toBe(0);
15
- expect(result.nextPattern).toBe(name);
17
+ expect(result.errorAtIndex).toBe(0);
16
18
  expect(result.isComplete).toBeFalsy();
17
19
  });
18
20
 
@@ -21,52 +23,195 @@ describe("AutoComplete", () => {
21
23
  const space = new Literal("space", " ");
22
24
  const doe = new Literal("doe", "Doe");
23
25
  const smith = new Literal("smith", "Smith");
26
+ const name = new And("name", [john, space, new Or("last-name", [smith, doe])]);
27
+
28
+ const autoComplete = new AutoComplete(name);
29
+ const result = autoComplete.suggestFor("John Doe");
24
30
 
25
- space.enableContextualTokenAggregation();
31
+ expect(result.ast?.value).toBe("John Doe");
32
+ expect(result.options.length).toBe(0);
33
+ expect(result.errorAtIndex).toBeNull();
34
+ expect(result.isComplete).toBeTruthy();
35
+ expect(result.cursor).not.toBeNull();
36
+ });
26
37
 
38
+ test("More Than One Option", () => {
39
+ const john = new Literal("john", "John");
40
+ const space = new Literal("space", " ");
41
+ const doe = new Literal("doe", "Doe");
42
+ const smith = new Literal("smith", "Smith");
27
43
  const name = new And("name", [john, space, new Or("last-name", [smith, doe])]);
28
44
 
45
+ const text = "John "
29
46
  const autoComplete = new AutoComplete(name);
30
- let result = autoComplete.suggest("John");
31
-
32
- expect(result.options.length).toBe(2);
33
- expect(result.nextPattern).toBe(findPattern(name, p=>p.name === "space"));
34
- expect(result.options[0].text).toBe(" Doe");
35
- expect(result.options[0].startIndex).toBe(4);
36
- expect(result.options[1].text).toBe(" Smith");
37
- expect(result.options[1].startIndex).toBe(4);
47
+ const result = autoComplete.suggestFor(text);
48
+ const expectedOptions = [{
49
+ text: "Doe",
50
+ startIndex: 5
51
+ }, {
52
+ text: "Smith",
53
+ startIndex: 5
54
+ }];
55
+
56
+ expect(result.ast).toBeNull();
57
+ expect(result.options).toEqual(expectedOptions);
58
+ expect(result.errorAtIndex).toBe(text.length)
38
59
  expect(result.isComplete).toBeFalsy();
60
+ expect(result.cursor).not.toBeNull();
61
+ });
62
+
63
+ test("Full Pattern Match With Root Repeat", () => {
64
+ const john = new Literal("john", "John");
65
+ const space = new Literal("space", " ");
66
+ const doe = new Literal("doe", "Doe");
67
+ const smith = new Literal("smith", "Smith");
68
+ const name = new And("name", [john, space, new Or("last-name", [smith, doe])]);
69
+ const divider = new Regex("divider", "\\s+,\\s+");
70
+
71
+ divider.setTokens([", "])
72
+
73
+ const text = "John Doe";
74
+ const autoComplete = new AutoComplete(new Repeat("last-names", name, divider));
75
+ const result = autoComplete.suggestFor(text);
76
+ const expectedOptions = [{
77
+ text: ", ",
78
+ startIndex: 8
79
+ }];
80
+
81
+ expect(result.ast?.value).toBe(text);
82
+ expect(result.options).toEqual(expectedOptions);
83
+ expect(result.errorAtIndex).toBeNull()
84
+ expect(result.isComplete).toBeTruthy();
85
+ expect(result.cursor).not.toBeNull();
39
86
  });
40
87
 
41
88
  test("Partial", () => {
42
89
  const name = new Literal("name", "Name");
43
90
  const autoComplete = new AutoComplete(name);
44
- let result = autoComplete.suggest("Na");
91
+ const result = autoComplete.suggestFor("Na");
92
+ const expectedOptions = [{
93
+ text: "me",
94
+ startIndex: 2
95
+ }];
45
96
 
46
- expect(result.options[0].text).toBe("me");
47
- expect(result.options[0].startIndex).toBe(2);
48
- expect(result.nextPattern).toBe(name);
97
+ expect(result.ast).toBeNull();
98
+ expect(result.options).toEqual(expectedOptions);
99
+ expect(result.errorAtIndex).toBe(2);
49
100
  expect(result.isComplete).toBeFalsy();
101
+ expect(result.cursor).not.toBeNull();
50
102
  });
51
103
 
52
104
  test("Partial Match With Bad Characters", () => {
53
105
  const name = new Literal("name", "Name");
54
106
  const autoComplete = new AutoComplete(name);
55
- let result = autoComplete.suggest("Ni");
107
+ const result = autoComplete.suggestFor("Ni");
108
+
109
+ const expectedOptions = [{
110
+ text: "ame",
111
+ startIndex: 1
112
+ }];
56
113
 
57
- expect(result.options[0].text).toBe("ame");
58
- expect(result.options[0].startIndex).toBe(1);
59
- expect(result.nextPattern).toBe(name);
114
+ expect(result.ast).toBeNull();
115
+ expect(result.options).toEqual(expectedOptions);
116
+ expect(result.errorAtIndex).toBe(1);
60
117
  expect(result.isComplete).toBeFalsy();
118
+ expect(result.cursor).not.toBeNull();
61
119
  });
62
120
 
63
121
  test("Complete", () => {
64
122
  const name = new Literal("name", "Name");
65
123
  const autoComplete = new AutoComplete(name);
66
- let result = autoComplete.suggest("Name");
124
+ const text = "Name"
125
+ const result = autoComplete.suggestFor(text);
67
126
 
68
- expect(result.options.length).toBe(0);
69
- expect(result.nextPattern).toBe(null);
127
+ expect(result.ast?.value).toBe(text);
128
+ expect(result.options).toEqual([]);
129
+ expect(result.errorAtIndex).toBeNull();
70
130
  expect(result.isComplete).toBeTruthy();
131
+ expect(result.cursor).not.toBeNull();
71
132
  });
133
+
134
+ test("Options AutoComplete on Composing Pattern", () => {
135
+ const autoCompleteOptions: AutoCompleteOptions = {
136
+ greedyPatternNames: ["space"],
137
+ customTokens: {
138
+ "last-name": ["Sparrow"]
139
+ }
140
+ };
141
+
142
+ const jack = new Literal("jack", "Jack");
143
+ const john = new Literal("john", "John");
144
+ const space = new Literal("space", " ");
145
+ const doe = new Literal("doe", "Doe");
146
+ const smith = new Literal("smith", "Smith");
147
+ const firstName = new Or("first-name", [jack, john]);
148
+ const lastName = new Or("last-name", [doe, smith]);
149
+ const fullName = new And("full-name", [firstName, space, lastName]);
150
+
151
+ const text = "Jack";
152
+ const autoComplete = new AutoComplete(fullName, autoCompleteOptions);
153
+ const { options, ast, errorAtIndex } = autoComplete.suggestFor(text);
154
+
155
+ const expectedOptions = [
156
+ { text: " Doe", startIndex: 4 },
157
+ { text: " Smith", startIndex: 4 },
158
+ { text: " Sparrow", startIndex: 4 },
159
+ ];
160
+
161
+ const results = expectedOptions.map(o => text.slice(0, o.startIndex) + o.text);
162
+ const expectedResults = [
163
+ "Jack Doe",
164
+ "Jack Smith",
165
+ "Jack Sparrow",
166
+ ]
167
+
168
+ expect(ast).toBeNull();
169
+ expect(errorAtIndex).toBe(4);
170
+ expect(options).toEqual(expectedOptions);
171
+ expect(results).toEqual(expectedResults);
172
+
173
+ });
174
+
175
+ test("Options AutoComplete On Leaf Pattern", () => {
176
+ const autoCompleteOptions: AutoCompleteOptions = {
177
+ greedyPatternNames: ["space"],
178
+ customTokens: {
179
+ "space": [" "]
180
+ }
181
+ };
182
+
183
+ const jack = new Literal("jack", "Jack");
184
+ const john = new Literal("john", "John");
185
+ const space = new Literal("space", " ");
186
+ const doe = new Literal("doe", "Doe");
187
+ const smith = new Literal("smith", "Smith");
188
+ const firstName = new Or("first-name", [jack, john]);
189
+ const lastName = new Or("last-name", [doe, smith]);
190
+ const fullName = new And("full-name", [firstName, space, lastName]);
191
+
192
+ const text = "Jack";
193
+ const autoComplete = new AutoComplete(fullName, autoCompleteOptions);
194
+ const { options, ast, errorAtIndex } = autoComplete.suggestFor(text);
195
+ const expectedOptions = [
196
+ { text: " Doe", startIndex: 4 },
197
+ { text: " Smith", startIndex: 4 },
198
+ { text: " Doe", startIndex: 4 },
199
+ { text: " Smith", startIndex: 4 },
200
+ ];
201
+
202
+ const results = expectedOptions.map(o => text.slice(0, o.startIndex) + o.text);
203
+ const expectedResults = [
204
+ "Jack Doe",
205
+ "Jack Smith",
206
+ "Jack Doe",
207
+ "Jack Smith",
208
+ ]
209
+
210
+ expect(ast).toBeNull();
211
+ expect(errorAtIndex).toBe(4);
212
+ expect(options).toEqual(expectedOptions);
213
+ expect(results).toEqual(expectedResults)
214
+
215
+ });
216
+
72
217
  });