clarity-pattern-parser 10.3.7 → 11.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 (51) hide show
  1. package/dist/ast/Node.d.ts +2 -2
  2. package/dist/ast/compact.d.ts +2 -0
  3. package/dist/ast/remove.d.ts +2 -0
  4. package/dist/index.browser.js +417 -487
  5. package/dist/index.browser.js.map +1 -1
  6. package/dist/index.d.ts +3 -1
  7. package/dist/index.esm.js +416 -488
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/index.js +417 -487
  10. package/dist/index.js.map +1 -1
  11. package/dist/patterns/ExpressionPattern.d.ts +27 -25
  12. package/dist/patterns/FiniteRepeat.d.ts +1 -2
  13. package/dist/patterns/InfiniteRepeat.d.ts +1 -2
  14. package/dist/patterns/Literal.d.ts +0 -1
  15. package/dist/patterns/Not.d.ts +1 -2
  16. package/dist/patterns/Optional.d.ts +0 -1
  17. package/dist/patterns/Options.d.ts +1 -2
  18. package/dist/patterns/Pattern.d.ts +0 -1
  19. package/dist/patterns/PrecedenceTree.d.ts +28 -0
  20. package/dist/patterns/Reference.d.ts +1 -2
  21. package/dist/patterns/Regex.d.ts +1 -2
  22. package/dist/patterns/Repeat.d.ts +0 -3
  23. package/dist/patterns/Sequence.d.ts +3 -6
  24. package/dist/patterns/execPattern.d.ts +3 -0
  25. package/dist/patterns/testPattern.d.ts +2 -0
  26. package/package.json +1 -1
  27. package/src/ast/Node.test.ts +17 -17
  28. package/src/ast/Node.ts +7 -5
  29. package/src/ast/compact.ts +11 -0
  30. package/src/ast/remove.ts +11 -0
  31. package/src/grammar/Grammar.test.ts +0 -50
  32. package/src/grammar/Grammar.ts +0 -20
  33. package/src/grammar/patterns/statement.ts +1 -6
  34. package/src/index.ts +4 -0
  35. package/src/patterns/ExpressionPattern.test.ts +1 -1
  36. package/src/patterns/ExpressionPattern.ts +235 -384
  37. package/src/patterns/FiniteRepeat.ts +5 -22
  38. package/src/patterns/InfiniteRepeat.ts +6 -21
  39. package/src/patterns/Literal.ts +5 -19
  40. package/src/patterns/Not.ts +5 -16
  41. package/src/patterns/Optional.ts +0 -7
  42. package/src/patterns/Options.ts +5 -21
  43. package/src/patterns/Pattern.ts +0 -1
  44. package/src/patterns/PrecedenceTree.test.ts +162 -0
  45. package/src/patterns/PrecedenceTree.ts +207 -0
  46. package/src/patterns/Reference.ts +5 -17
  47. package/src/patterns/Regex.ts +5 -17
  48. package/src/patterns/Repeat.ts +1 -13
  49. package/src/patterns/Sequence.ts +7 -22
  50. package/src/patterns/execPattern.ts +16 -0
  51. package/src/patterns/testPattern.ts +11 -0
@@ -0,0 +1,207 @@
1
+ import { Node } from "../ast/Node";
2
+
3
+ export enum Association {
4
+ left = 0,
5
+ right,
6
+ }
7
+
8
+ export class PrecedenceTree {
9
+ private _prefixPlaceholder: Node;
10
+ private _prefixNode: Node | null;
11
+ private _postfixPlaceholder: Node;
12
+ private _postfixNode: Node | null;
13
+ private _binaryPlaceholder: Node;
14
+ private _binaryNode: Node | null;
15
+ private _atomNode: Node | null;
16
+ private _orphanedAtom: Node | null;
17
+ private _precedenceMap: Record<string, number>;
18
+ private _associationMap: Record<string, Association>;
19
+
20
+ constructor(precedenceMap: Record<string, number> = {}, associationMap: Record<string, Association> = {}) {
21
+ this._prefixPlaceholder = Node.createNode("placeholder", "prefix-placeholder");
22
+ this._prefixNode = null;
23
+ this._postfixPlaceholder = Node.createNode("placeholder", "postfix-placeholder");
24
+ this._postfixNode = null;
25
+ this._binaryPlaceholder = Node.createNode("placeholder", "binary-placeholder");
26
+ this._atomNode = null;
27
+ this._binaryNode = null;
28
+ this._orphanedAtom = null;
29
+ this._precedenceMap = precedenceMap;
30
+ this._associationMap = associationMap;
31
+ }
32
+
33
+ addPrefix(name: string, ...prefix: Node[]) {
34
+ const lastPrefixNode = this._prefixNode;
35
+
36
+ if (lastPrefixNode == null) {
37
+ const node = Node.createNode("expression", name, [...prefix]);
38
+ this._prefixNode = node;
39
+ this._prefixNode.append(this._prefixPlaceholder);
40
+ return;
41
+ }
42
+
43
+ const node = Node.createNode("expression", name, [...prefix]);
44
+ this._prefixPlaceholder.replaceWith(node);
45
+ node.append(this._prefixPlaceholder);
46
+
47
+ this._prefixNode = node;
48
+ }
49
+
50
+ addPostfix(name: string, ...postfix: Node[]) {
51
+ const lastPostfixNode = this._postfixNode;
52
+
53
+ if (lastPostfixNode == null) {
54
+ const node = Node.createNode("expression", name, [this._postfixPlaceholder, ...postfix]);
55
+ this._postfixNode = node;
56
+ return;
57
+ }
58
+
59
+ const node = Node.createNode("expression", name, [lastPostfixNode, ...postfix]);
60
+ this._postfixNode = node;
61
+ }
62
+
63
+ addBinary(name: string, ...delimiterNode: Node[]) {
64
+ const lastBinaryNode = this._binaryNode;
65
+ const lastPrecendece = this._getPrecedenceFromNode(this._binaryNode);
66
+ const precedence = this._getPrecedence(name);
67
+ const association = this._associationMap[name];
68
+ const lastAtomNode = this._compileAtomNode();
69
+
70
+ if (lastAtomNode == null) {
71
+ throw new Error("Cannot add a binary without an atom node.");
72
+ }
73
+
74
+ this._binaryPlaceholder.remove();
75
+ this._orphanedAtom = lastAtomNode;
76
+
77
+ if (lastBinaryNode == null) {
78
+ const node = Node.createNode("expression", name, [lastAtomNode, ...delimiterNode, this._binaryPlaceholder]);
79
+
80
+ this._binaryNode = node;
81
+ return;
82
+ }
83
+
84
+ if (precedence === lastPrecendece && association === Association.right) {
85
+ const node = Node.createNode("expression", name, [lastAtomNode, ...delimiterNode, this._binaryPlaceholder]);
86
+
87
+ lastBinaryNode.appendChild(node);
88
+
89
+ this._binaryNode = node;
90
+ } else if (precedence === lastPrecendece) {
91
+ const node = Node.createNode("expression", name, []);
92
+
93
+ lastBinaryNode.replaceWith(node);
94
+ lastBinaryNode.appendChild(lastAtomNode);
95
+
96
+ node.append(lastBinaryNode, ...delimiterNode, this._binaryPlaceholder);
97
+ this._binaryNode = node;
98
+ } else if (precedence > lastPrecendece) {
99
+ let ancestor = lastBinaryNode.parent;
100
+ let root = lastBinaryNode;
101
+
102
+ while (ancestor != null) {
103
+ const nodePrecedence = this._precedenceMap[ancestor.name];
104
+
105
+ if (nodePrecedence > precedence) {
106
+ break;
107
+ }
108
+ root = ancestor;
109
+ ancestor = ancestor.parent;
110
+ }
111
+
112
+ lastBinaryNode.appendChild(lastAtomNode);
113
+
114
+ const node = Node.createNode("expression", name, []);
115
+ root.replaceWith(node);
116
+ node.append(root, ...delimiterNode, this._binaryPlaceholder);
117
+
118
+ this._binaryNode = node;
119
+
120
+
121
+ } else {
122
+ const node = Node.createNode("expression", name, [lastAtomNode, ...delimiterNode, this._binaryPlaceholder]);
123
+ lastBinaryNode.appendChild(node);
124
+
125
+ this._binaryNode = node;
126
+ }
127
+
128
+ }
129
+
130
+ private _getPrecedenceFromNode(node: Node | null) {
131
+ if (node == null) {
132
+ return 0;
133
+ }
134
+
135
+ return this._getPrecedence(node.name);
136
+ }
137
+
138
+ private _getPrecedence(name: string) {
139
+ if (this._precedenceMap[name] != null) {
140
+ return this._precedenceMap[name];
141
+ }
142
+
143
+ return 0;
144
+ }
145
+
146
+ private _compileAtomNode() {
147
+ let node = this._atomNode;
148
+
149
+ if (this._prefixNode != null && this._atomNode != null) {
150
+ node = this._prefixNode;
151
+ this._prefixPlaceholder.replaceWith(this._atomNode);
152
+ }
153
+
154
+ if (this._postfixNode != null && node != null) {
155
+ this._postfixPlaceholder.replaceWith(node);
156
+ node = this._postfixNode;
157
+ }
158
+
159
+ this._prefixNode = null;
160
+ this._atomNode = null;
161
+ this._postfixNode = null;
162
+
163
+ if (node == null) {
164
+ return null;
165
+ }
166
+
167
+ return node.findRoot();
168
+ }
169
+
170
+ addAtom(node: Node) {
171
+ this._atomNode = node;
172
+ }
173
+
174
+ hasAtom(){
175
+ return this._atomNode != null;
176
+ }
177
+
178
+ commit() {
179
+ if (this._binaryNode == null) {
180
+ return this._compileAtomNode();
181
+ }
182
+
183
+ const atomNode = this._compileAtomNode();
184
+
185
+ if (atomNode == null) {
186
+ let root = this._binaryPlaceholder.findRoot();
187
+ this._binaryPlaceholder.parent?.replaceWith(this._orphanedAtom as Node);
188
+ this.reset();
189
+ return root;
190
+ } else {
191
+ this._binaryPlaceholder.replaceWith(atomNode);
192
+ const root = this._binaryNode.findRoot();
193
+ this.reset();
194
+ return root;
195
+ }
196
+
197
+ }
198
+
199
+ private reset() {
200
+ this._prefixNode = null;
201
+ this._atomNode = null;
202
+ this._orphanedAtom = null;
203
+ this._postfixNode = null;
204
+ this._binaryNode = null;
205
+ }
206
+
207
+ }
@@ -4,6 +4,8 @@ import { Pattern } from "./Pattern";
4
4
  import { findPattern } from "./findPattern";
5
5
  import { ParseResult } from "./ParseResult";
6
6
  import { Context } from "./Context";
7
+ import { testPattern } from "./testPattern";
8
+ import { execPattern } from "./execPattern";
7
9
 
8
10
  let idIndex = 0;
9
11
 
@@ -17,8 +19,6 @@ export class Reference implements Pattern {
17
19
  private _children: Pattern[];
18
20
  private _firstIndex: number;
19
21
 
20
- shouldCompactAst = false;
21
-
22
22
  get id(): string {
23
23
  return this._id;
24
24
  }
@@ -58,23 +58,12 @@ export class Reference implements Pattern {
58
58
  this._firstIndex = 0;
59
59
  }
60
60
 
61
- test(text: string) {
62
- const cursor = new Cursor(text);
63
- const ast = this.parse(cursor);
64
-
65
- return ast?.value === text;
61
+ test(text: string, record = false): boolean {
62
+ return testPattern(this, text, record);
66
63
  }
67
64
 
68
65
  exec(text: string, record = false): ParseResult {
69
- const cursor = new Cursor(text);
70
- record && cursor.startRecording();
71
-
72
- const ast = this.parse(cursor);
73
-
74
- return {
75
- ast: ast?.value === text ? ast : null,
76
- cursor
77
- };
66
+ return execPattern(this, text, record);
78
67
  }
79
68
 
80
69
  parse(cursor: Cursor): Node | null {
@@ -205,7 +194,6 @@ export class Reference implements Pattern {
205
194
  clone(name = this._name): Pattern {
206
195
  const clone = new Reference(name);
207
196
  clone._id = this._id;
208
- clone.shouldCompactAst = this.shouldCompactAst;
209
197
 
210
198
  // Optimize future clones, by caching the pattern we already found.
211
199
  if (this._pattern != null) {
@@ -2,6 +2,8 @@ import { Node } from "../ast/Node";
2
2
  import { Pattern } from "./Pattern";
3
3
  import { Cursor } from "./Cursor";
4
4
  import { ParseResult } from "./ParseResult";
5
+ import { testPattern } from "./testPattern";
6
+ import { execPattern } from "./execPattern";
5
7
 
6
8
  let idIndex = 0;
7
9
 
@@ -18,8 +20,6 @@ export class Regex implements Pattern {
18
20
  private _substring = "";
19
21
  private _tokens: string[] = [];
20
22
 
21
- shouldCompactAst = false;
22
-
23
23
  get id(): string {
24
24
  return this._id;
25
25
  }
@@ -82,23 +82,12 @@ export class Regex implements Pattern {
82
82
  }
83
83
  }
84
84
 
85
- test(text: string) {
86
- const cursor = new Cursor(text);
87
- const ast = this.parse(cursor);
88
-
89
- return ast?.value === text;
85
+ test(text: string, record = false): boolean {
86
+ return testPattern(this, text, record);
90
87
  }
91
88
 
92
89
  exec(text: string, record = false): ParseResult {
93
- const cursor = new Cursor(text);
94
- record && cursor.startRecording();
95
-
96
- const ast = this.parse(cursor);
97
-
98
- return {
99
- ast: ast?.value === text ? ast : null,
100
- cursor
101
- };
90
+ return execPattern(this, text, record);
102
91
  }
103
92
 
104
93
  parse(cursor: Cursor) {
@@ -152,7 +141,6 @@ export class Regex implements Pattern {
152
141
  const clone = new Regex(name, this._originalRegexString);
153
142
  clone._tokens = this._tokens.slice();
154
143
  clone._id = this._id;
155
- clone.shouldCompactAst = this.shouldCompactAst;
156
144
 
157
145
  return clone;
158
146
  }
@@ -27,16 +27,6 @@ export class Repeat implements Pattern {
27
27
  private _pattern: Pattern;
28
28
  private _options: InternalRepeatOptions;
29
29
  private _children: Pattern[];
30
- private _shouldCompactAst: boolean;
31
-
32
- get shouldCompactAst() {
33
- return this._shouldCompactAst;
34
- }
35
-
36
- set shouldCompactAst(value: boolean) {
37
- this._shouldCompactAst = value;
38
- this._repeatPattern.shouldCompactAst = value;
39
- }
40
30
 
41
31
  get id() {
42
32
  return this._id;
@@ -70,7 +60,7 @@ export class Repeat implements Pattern {
70
60
  return this._options.max;
71
61
  }
72
62
 
73
- get startedOnIndex(){
63
+ get startedOnIndex() {
74
64
  return this._repeatPattern.startedOnIndex;
75
65
  }
76
66
 
@@ -78,7 +68,6 @@ export class Repeat implements Pattern {
78
68
  this._id = `repeat-${idIndex++}`;
79
69
  this._pattern = pattern;
80
70
  this._parent = null;
81
- this._shouldCompactAst = false;
82
71
  this._options = {
83
72
  ...options,
84
73
  min: options.min == null ? 1 : options.min,
@@ -112,7 +101,6 @@ export class Repeat implements Pattern {
112
101
  const clone = new Repeat(name, this._pattern, { ...this._options, min });
113
102
 
114
103
  clone._id = this._id;
115
- clone.shouldCompactAst = this.shouldCompactAst;
116
104
  return clone;
117
105
  }
118
106
 
@@ -5,6 +5,9 @@ import { clonePatterns } from "./clonePatterns";
5
5
  import { filterOutNull } from "./filterOutNull";
6
6
  import { findPattern } from "./findPattern";
7
7
  import { isRecursivePattern } from "./isRecursivePattern";
8
+ import { testPattern } from "./testPattern";
9
+ import { execPattern } from "./execPattern";
10
+ import { ParseResult } from "./ParseResult";
8
11
 
9
12
  let idIndex = 0;
10
13
 
@@ -17,8 +20,6 @@ export class Sequence implements Pattern {
17
20
  private _nodes: (Node | null)[];
18
21
  private _firstIndex: number;
19
22
 
20
- shouldCompactAst = false;
21
-
22
23
  get id(): string {
23
24
  return this._id;
24
25
  }
@@ -70,23 +71,12 @@ export class Sequence implements Pattern {
70
71
  }
71
72
  }
72
73
 
73
- test(text: string) {
74
- const cursor = new Cursor(text);
75
- const ast = this.parse(cursor);
76
-
77
- return ast?.value === text;
74
+ test(text: string, record = false): boolean {
75
+ return testPattern(this, text, record);
78
76
  }
79
77
 
80
- exec(text: string, record = false) {
81
- const cursor = new Cursor(text);
82
- record && cursor.startRecording();
83
-
84
- const ast = this.parse(cursor);
85
-
86
- return {
87
- ast: ast?.value === text ? ast : null,
88
- cursor
89
- };
78
+ exec(text: string, record = false): ParseResult {
79
+ return execPattern(this, text, record);
90
80
  }
91
81
 
92
82
  parse(cursor: Cursor): Node | null {
@@ -99,10 +89,6 @@ export class Sequence implements Pattern {
99
89
 
100
90
  if (node !== null) {
101
91
  cursor.recordMatch(this, node);
102
-
103
- if (this.shouldCompactAst) {
104
- node.compact();
105
- }
106
92
  }
107
93
 
108
94
  return node;
@@ -344,7 +330,6 @@ export class Sequence implements Pattern {
344
330
  clone(name = this._name): Pattern {
345
331
  const clone = new Sequence(name, this._children);
346
332
  clone._id = this._id;
347
- clone.shouldCompactAst = this.shouldCompactAst;
348
333
 
349
334
  return clone;
350
335
  }
@@ -0,0 +1,16 @@
1
+ import { Cursor } from "./Cursor";
2
+ import { ParseResult } from "./ParseResult";
3
+ import { Pattern } from "./Pattern";
4
+
5
+ export function execPattern(pattern: Pattern, text: string, record = false): ParseResult {
6
+ const cursor = new Cursor(text);
7
+ record && cursor.startRecording();
8
+
9
+ const ast = pattern.parse(cursor);
10
+ const isMatch = ast?.value.length === text.length;
11
+
12
+ return {
13
+ ast: isMatch ? ast : null,
14
+ cursor
15
+ };
16
+ }
@@ -0,0 +1,11 @@
1
+ import { Cursor } from "./Cursor";
2
+ import { Pattern } from "./Pattern";
3
+
4
+ export function testPattern (pattern: Pattern, text: string, record = false): boolean{
5
+ const cursor = new Cursor(text);
6
+ record && cursor.startRecording();
7
+
8
+ const ast = pattern.parse(cursor);
9
+
10
+ return ast?.value.length === text.length;
11
+ }