clarity-pattern-parser 10.1.0 → 10.1.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.
@@ -0,0 +1,31 @@
1
+ import { Node } from "../ast/Node";
2
+ import { Cursor } from "./Cursor";
3
+ import { ParseResult } from "./ParseResult";
4
+ import { Pattern } from "./Pattern";
5
+ export declare class Context implements Pattern {
6
+ private _id;
7
+ private _type;
8
+ private _name;
9
+ private _parent;
10
+ private _children;
11
+ private _pattern;
12
+ get id(): string;
13
+ get type(): string;
14
+ get name(): string;
15
+ get parent(): Pattern | null;
16
+ set parent(pattern: Pattern | null);
17
+ get children(): Pattern[];
18
+ constructor(name: string, pattern: Pattern, context?: Pattern[]);
19
+ parse(cursor: Cursor): Node | null;
20
+ exec(text: string, record?: boolean | undefined): ParseResult;
21
+ test(text: string, record?: boolean | undefined): boolean;
22
+ clone(name?: string): Pattern;
23
+ getTokens(): string[];
24
+ getTokensAfter(childReference: Pattern): string[];
25
+ getNextTokens(): string[];
26
+ getPatterns(): Pattern[];
27
+ getPatternsAfter(childReference: Pattern): Pattern[];
28
+ getNextPatterns(): Pattern[];
29
+ find(predicate: (pattern: Pattern) => boolean): Pattern | null;
30
+ isEqual(pattern: Pattern): boolean;
31
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clarity-pattern-parser",
3
- "version": "10.1.0",
3
+ "version": "10.1.2",
4
4
  "description": "Parsing Library for Typescript and Javascript.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.esm.js",
@@ -8,6 +8,7 @@ import { Regex } from "../patterns/Regex";
8
8
  import { Repeat } from "../patterns/Repeat";
9
9
  import { Grammar } from "./Grammar";
10
10
  import { Optional } from "../patterns/Optional";
11
+ import { Context } from "../patterns/Context";
11
12
 
12
13
  describe("Grammar", () => {
13
14
  test("Literal", () => {
@@ -17,7 +18,8 @@ describe("Grammar", () => {
17
18
 
18
19
  const patterns = Grammar.parseString(expression);
19
20
  const namePattern = patterns["name"];
20
- const expected = new Literal("name", "John");
21
+ const name = new Literal("name", "John");
22
+ const expected = new Context("name", name);
21
23
 
22
24
  expect(namePattern.isEqual(expected)).toBeTruthy();
23
25
  });
@@ -28,7 +30,8 @@ describe("Grammar", () => {
28
30
  `;
29
31
  const patterns = Grammar.parseString(expression);
30
32
  const namePattern = patterns["chars"];
31
- const expected = new Literal("chars", "\n\r\t\b\f\v\0\x00\u0000\"\\");
33
+ const chars = new Literal("chars", "\n\r\t\b\f\v\0\x00\u0000\"\\");
34
+ const expected = new Context('chars', chars);
32
35
 
33
36
  expect(namePattern.isEqual(expected)).toBeTruthy();
34
37
  });
@@ -39,7 +42,8 @@ describe("Grammar", () => {
39
42
  `;
40
43
  const patterns = Grammar.parseString(expression);
41
44
  const namePattern = patterns["content"];
42
- const expected = new Literal("content", "With Con\"tent");
45
+ const content = new Literal("content", "With Con\"tent");
46
+ const expected = new Context(`content`, content);
43
47
 
44
48
  expect(namePattern.isEqual(expected)).toBeTruthy();
45
49
  });
@@ -52,8 +56,9 @@ describe("Grammar", () => {
52
56
  const patterns = Grammar.parseString(expression);
53
57
  const pattern = patterns["name"];
54
58
  const name = new Regex("name", "\\w");
59
+ const expected = new Context(`name`, name);
55
60
 
56
- expect(pattern.isEqual(name)).toBeTruthy();
61
+ expect(pattern.isEqual(expected)).toBeTruthy();
57
62
  });
58
63
 
59
64
  test("Or", () => {
@@ -68,8 +73,9 @@ describe("Grammar", () => {
68
73
  const john = new Literal("john", "John");
69
74
  const jane = new Literal("jane", "Jane");
70
75
  const names = new Options("names", [john, jane], true);
76
+ const expected = new Context("names", names, [john, jane]);
71
77
 
72
- expect(pattern.isEqual(names)).toBeTruthy();
78
+ expect(pattern.isEqual(expected)).toBeTruthy();
73
79
  });
74
80
 
75
81
  test("And", () => {
@@ -86,8 +92,9 @@ describe("Grammar", () => {
86
92
  const firstName = new Regex("first-name", "\\w");
87
93
  const lastName = new Regex("last-name", "\\w");
88
94
  const fullName = new Sequence("full-name", [firstName, space, lastName]);
95
+ const expected = new Context("full-name", fullName, [space, firstName, lastName]);
89
96
 
90
- expect(pattern.isEqual(fullName)).toBeTruthy();
97
+ expect(pattern.isEqual(expected)).toBeTruthy();
91
98
  });
92
99
 
93
100
  test("And With Optional Pattern", () => {
@@ -106,10 +113,12 @@ describe("Grammar", () => {
106
113
  const firstName = new Regex("first-name", "\\w");
107
114
  const lastName = new Regex("last-name", "\\w");
108
115
  const middleName = new Regex("middle-name", "\\w");
109
- const middleNameWithSpace = new Optional("optional-middle-name-with-space", new Sequence("middle-name-with-space", [middleName, space]));
110
- const fullName = new Sequence("full-name", [firstName, space, middleNameWithSpace, lastName]);
116
+ const middleNameWithSpace = new Sequence("middle-name-with-space", [middleName, space]);
117
+ const optionalMiddleNameWithSpace = new Optional("optional-middle-name-with-space", middleNameWithSpace);
118
+ const fullName = new Sequence("full-name", [firstName, space, optionalMiddleNameWithSpace, lastName]);
119
+ const expected = new Context("full-name", fullName, [space, firstName, lastName, middleName, middleNameWithSpace]);
111
120
 
112
- expect(pattern.isEqual(fullName)).toBeTruthy();
121
+ expect(pattern.isEqual(expected)).toBeTruthy();
113
122
  });
114
123
 
115
124
  test("And With Not Pattern", () => {
@@ -130,11 +139,12 @@ describe("Grammar", () => {
130
139
  const lastName = new Regex("last-name", "\\w");
131
140
  const middleName = new Regex("middle-name", "\\w");
132
141
  const jack = new Literal("jack", "Jack");
133
- const notJack = new Not("not-jack", jack);
134
- const middleNameWithSpace = new Optional("optional-middle-name-with-space", new Sequence("middle-name-with-space", [middleName, space]));
135
- const fullName = new Sequence("full-name", [notJack, firstName, space, middleNameWithSpace, lastName]);
142
+ const middleNameWithSpace = new Sequence("middle-name-with-space", [middleName, space]);
143
+ const optionalMiddleNameWithSpace = new Optional("optional-middle-name-with-space", middleNameWithSpace);
144
+ const fullName = new Sequence("full-name", [new Not("not-jack", jack), firstName, space, optionalMiddleNameWithSpace, lastName]);
145
+ const expected = new Context("full-name", fullName, [space, firstName, lastName, middleName, jack, middleNameWithSpace]);
136
146
 
137
- expect(pattern.isEqual(fullName)).toBeTruthy();
147
+ expect(pattern.isEqual(expected)).toBeTruthy();
138
148
  });
139
149
 
140
150
  test("Repeat", () => {
@@ -147,8 +157,9 @@ describe("Grammar", () => {
147
157
  const pattern = patterns["digits"];
148
158
  const digit = new Regex("digit", "\\d");
149
159
  const digits = new Repeat("digits", digit);
160
+ const expected = new Context("digits", digits, [digit]);
150
161
 
151
- expect(pattern.isEqual(digits)).toBeTruthy();
162
+ expect(pattern.isEqual(expected)).toBeTruthy();
152
163
  });
153
164
 
154
165
  test("Repeat Zero Or More", () => {
@@ -162,7 +173,9 @@ describe("Grammar", () => {
162
173
  const digit = new Regex("digit", "\\d");
163
174
  const digits = new Optional("digits", new Repeat("digits", digit, { min: 0 }));
164
175
 
165
- expect(pattern.isEqual(digits)).toBeTruthy();
176
+ const expected = new Context("digits", digits, [digit]);
177
+
178
+ expect(pattern.isEqual(expected)).toBeTruthy();
166
179
  });
167
180
 
168
181
  test("Repeat Lower Limit", () => {
@@ -175,8 +188,9 @@ describe("Grammar", () => {
175
188
  const pattern = patterns["digits"];
176
189
  const digit = new Regex("digit", "\\d+");
177
190
  const digits = new Repeat("digits", digit, { min: 1 });
191
+ const expected = new Context("digits", digits, [digit]);
178
192
 
179
- expect(pattern.isEqual(digits)).toBeTruthy();
193
+ expect(pattern.isEqual(expected)).toBeTruthy();
180
194
  });
181
195
 
182
196
  test("Repeat Bounded", () => {
@@ -189,8 +203,9 @@ describe("Grammar", () => {
189
203
  const pattern = patterns["digits"];
190
204
  const digit = new Regex("digit", "\\d+");
191
205
  const digits = new Repeat("digits", digit, { min: 1, max: 3 });
206
+ const expected = new Context("digits", digits, [digit]);
192
207
 
193
- expect(pattern.isEqual(digits)).toBeTruthy();
208
+ expect(pattern.isEqual(expected)).toBeTruthy();
194
209
  });
195
210
 
196
211
  test("Repeat Upper Limit", () => {
@@ -203,8 +218,9 @@ describe("Grammar", () => {
203
218
  const pattern = patterns["digits"];
204
219
  const digit = new Regex("digit", "\\d+");
205
220
  const digits = new Repeat("digits", digit, { min: 0, max: 3 });
221
+ const expected = new Context("digits", digits, [digit]);
206
222
 
207
- expect(pattern.isEqual(digits)).toBeTruthy();
223
+ expect(pattern.isEqual(expected)).toBeTruthy();
208
224
  });
209
225
 
210
226
  test("Repeat Exact", () => {
@@ -217,8 +233,9 @@ describe("Grammar", () => {
217
233
  const pattern = patterns["digits"];
218
234
  const digit = new Regex("digit", "\\d+");
219
235
  const digits = new Repeat("digits", digit, { min: 3, max: 3 });
236
+ const expected = new Context("digits", digits, [digit]);
220
237
 
221
- expect(pattern.isEqual(digits)).toBeTruthy();
238
+ expect(pattern.isEqual(expected)).toBeTruthy();
222
239
  });
223
240
 
224
241
  test("Repeat Divider", () => {
@@ -231,10 +248,11 @@ describe("Grammar", () => {
231
248
  const patterns = Grammar.parseString(expression);
232
249
  const pattern = patterns["digits"];
233
250
  const digit = new Regex("digit", "\\d+");
234
- const divider = new Literal("comma", ",");
235
- const digits = new Repeat("digits", digit, { divider, min: 3, max: 3 });
251
+ const comma = new Literal("comma", ",");
252
+ const digits = new Repeat("digits", digit, { divider: comma, min: 3, max: 3 });
253
+ const expected = new Context("digits", digits, [digit, comma]);
236
254
 
237
- expect(pattern.isEqual(digits)).toBeTruthy();
255
+ expect(pattern.isEqual(expected)).toBeTruthy();
238
256
  });
239
257
 
240
258
  test("Repeat Divider With Trim Divider", () => {
@@ -247,10 +265,11 @@ describe("Grammar", () => {
247
265
  const patterns = Grammar.parseString(expression);
248
266
  const pattern = patterns["digits"];
249
267
  const digit = new Regex("digit", "\\d+");
250
- const divider = new Literal("comma", ",");
251
- const digits = new Repeat("digits", digit, { divider, min: 1, trimDivider: true });
268
+ const comma = new Literal("comma", ",");
269
+ const digits = new Repeat("digits", digit, { divider: comma, min: 1, trimDivider: true });
270
+ const expected = new Context("digits", digits, [digit, comma]);
252
271
 
253
- expect(pattern.isEqual(digits)).toBeTruthy();
272
+ expect(pattern.isEqual(expected)).toBeTruthy();
254
273
  });
255
274
 
256
275
  test("Repeat Divider With Trim Divider And Bounds", () => {
@@ -265,8 +284,9 @@ describe("Grammar", () => {
265
284
  const digit = new Regex("digit", "\\d+");
266
285
  const divider = new Literal("comma", ",");
267
286
  const digits = new Repeat("digits", digit, { divider, min: 3, max: 3, trimDivider: true });
287
+ const expected = new Context("digits", digits, [digit, divider]);
268
288
 
269
- expect(pattern.isEqual(digits)).toBeTruthy();
289
+ expect(pattern.isEqual(expected)).toBeTruthy();
270
290
  });
271
291
 
272
292
  test("Reference", () => {
@@ -302,10 +322,14 @@ describe("Grammar", () => {
302
322
  const expectedName = new Regex("name", "regex");
303
323
 
304
324
  const alias = patterns["alias"];
325
+
305
326
  const expectedAlias = new Regex("alias", "regex");
306
327
 
307
- expect(name.isEqual(expectedName)).toBeTruthy();
308
- expect(alias.isEqual(expectedAlias)).toBeTruthy();
328
+ const contextualAlias = new Context("alias", expectedAlias, [expectedName]);
329
+ const contextualName = new Context("name", expectedName, [expectedAlias]);
330
+
331
+ expect(name.isEqual(contextualName)).toBeTruthy();
332
+ expect(alias.isEqual(contextualAlias)).toBeTruthy();
309
333
  });
310
334
 
311
335
  test("Bad Grammar At Beginning", () => {
@@ -481,7 +505,7 @@ describe("Grammar", () => {
481
505
  `;
482
506
 
483
507
  const patterns = Grammar.parseString(expression);
484
- const expected = new Sequence("complex-expression", [
508
+ const expected = new Context("complex-expression", new Sequence("complex-expression", [
485
509
  new Not("not-NOT_THIS", new Literal("NOT_THIS", "NOT_THIS")),
486
510
  new Optional("Text", new Literal("Text", "Text")),
487
511
  new Regex("regex", "regex"),
@@ -496,7 +520,7 @@ describe("Grammar", () => {
496
520
  new Reference("pattern"),
497
521
  new Reference("pattern")
498
522
  ])
499
- ]);
523
+ ]));
500
524
 
501
525
  expect(patterns["complex-expression"].isEqual(expected)).toBeTruthy();
502
526
  });
@@ -10,6 +10,7 @@ import { Sequence } from "../patterns/Sequence";
10
10
  import { Repeat, RepeatOptions } from "../patterns/Repeat";
11
11
  import { AutoComplete } from "../intellisense/AutoComplete";
12
12
  import { Optional } from "../patterns/Optional";
13
+ import { Context } from "../patterns/Context";
13
14
 
14
15
  let anonymousIndexId = 0;
15
16
 
@@ -88,7 +89,18 @@ export class Grammar {
88
89
  await this._resolveImports(ast);
89
90
  this._buildPatterns(ast);
90
91
 
91
- return Object.fromEntries(this._parseContext.patternsByName) as Record<string, Pattern>;
92
+ return this._buildPatternRecord();
93
+ }
94
+
95
+ private _buildPatternRecord() {
96
+ const patterns: Record<string, Pattern> = {};
97
+ const allPatterns = Array.from(this._parseContext.patternsByName.values());
98
+
99
+ allPatterns.forEach(p => {
100
+ patterns[p.name] = new Context(p.name, p, allPatterns.filter(o => o !== p));
101
+ });
102
+
103
+ return patterns;
92
104
  }
93
105
 
94
106
  parseString(expression: string) {
@@ -98,8 +110,10 @@ export class Grammar {
98
110
  if (this._hasImports(ast)) {
99
111
  throw new Error("Cannot use imports on parseString, use parse instead.");
100
112
  }
113
+
101
114
  this._buildPatterns(ast);
102
- return Object.fromEntries(this._parseContext.patternsByName) as Record<string, Pattern>;
115
+
116
+ return this._buildPatternRecord();
103
117
  }
104
118
 
105
119
  private _tryToParse(expression: string): Node {
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ import { CursorHistory, Match } from "./patterns/CursorHistory";
18
18
  import { ParseResult } from "./patterns/ParseResult";
19
19
  import { grammar } from "./grammar/patterns/grammar";
20
20
  import { patterns } from "./grammar/patterns";
21
+ import { Context } from "./patterns/Context";
21
22
 
22
23
  export {
23
24
  Node,
@@ -30,6 +31,7 @@ export {
30
31
  Cursor,
31
32
  CursorHistory,
32
33
  Match,
34
+ Context,
33
35
  Literal,
34
36
  Not,
35
37
  Options,
@@ -0,0 +1,35 @@
1
+ import { patterns } from "../grammar/patterns";
2
+ import { Context } from "./Context";
3
+ import { Literal } from "./Literal";
4
+ import { Reference } from "./Reference";
5
+ import { Sequence } from "./Sequence";
6
+
7
+ describe("Context", () => {
8
+ test("Basic", () => {
9
+ const john = new Literal("john", "John");
10
+ const space = new Literal("space", " ");
11
+ const jane = new Literal("jane", "Jane");
12
+ const spaceReference = new Reference("space");
13
+
14
+ const names = new Sequence("names", [john, spaceReference, jane]);
15
+ const context = new Context("context", names, [space, names]);
16
+
17
+ const { ast } = context.exec("John Jane");
18
+
19
+ expect(ast?.toString()).toBe("John Jane");
20
+ });
21
+
22
+ test("With Grammar", ()=>{
23
+ const {names} = patterns`
24
+ names = john + space + jane
25
+ john = "John"
26
+ jane = "Jane"
27
+ space = " "
28
+ `;
29
+
30
+ const { ast } = names.exec("John Jane");
31
+
32
+ expect(ast?.toString()).toBe("John Jane");
33
+ });
34
+
35
+ });
@@ -0,0 +1,105 @@
1
+ import { Node } from "../ast/Node";
2
+ import { Cursor } from "./Cursor";
3
+ import { ParseResult } from "./ParseResult";
4
+ import { Pattern } from "./Pattern";
5
+
6
+ let contextId = 0;
7
+
8
+ export class Context implements Pattern {
9
+ private _id: string;
10
+ private _type: string;
11
+ private _name: string;
12
+ private _parent: Pattern | null;
13
+ private _children: Pattern[];
14
+ private _pattern: Pattern;
15
+
16
+ get id(): string {
17
+ return this._id;
18
+ }
19
+
20
+ get type(): string {
21
+ return this._type;
22
+ }
23
+
24
+ get name(): string {
25
+ return this._name;
26
+ }
27
+
28
+ get parent(): Pattern | null {
29
+ return this._parent;
30
+ }
31
+
32
+ set parent(pattern: Pattern | null) {
33
+ this._parent = pattern;
34
+ }
35
+
36
+ get children(): Pattern[] {
37
+ return this._children;
38
+ }
39
+
40
+ constructor(name: string, pattern: Pattern, context: Pattern[] = []) {
41
+ this._id = `context-${contextId++}`;
42
+ this._type = "context";
43
+ this._name = name;
44
+ this._parent = null;
45
+
46
+ const clonedContext = context.map(p => p.clone());
47
+ const clonedPattern = pattern.clone();
48
+
49
+ clonedContext.forEach(p => p.parent = this);
50
+ clonedPattern.parent = this;
51
+
52
+ this._pattern = clonedPattern;
53
+ this._children = [...clonedContext, clonedPattern];
54
+ }
55
+
56
+ parse(cursor: Cursor): Node | null {
57
+ return this._pattern.parse(cursor);
58
+ }
59
+
60
+ exec(text: string, record?: boolean | undefined): ParseResult {
61
+ return this._pattern.exec(text, record);
62
+ }
63
+
64
+ test(text: string, record?: boolean | undefined): boolean {
65
+ return this._pattern.test(text, record);
66
+ }
67
+
68
+ clone(name = this._name): Pattern {
69
+ const clone = new Context(name, this._pattern, this._children.slice(0, -1));
70
+ return clone;
71
+ }
72
+
73
+ getTokens(): string[] {
74
+ return this._pattern.getTokens();
75
+ }
76
+
77
+ getTokensAfter(childReference: Pattern): string[] {
78
+ return this._pattern.getTokensAfter(childReference);
79
+ }
80
+
81
+ getNextTokens(): string[] {
82
+ return this._pattern.getNextTokens();
83
+ }
84
+
85
+ getPatterns(): Pattern[] {
86
+ return this._pattern.getPatterns();
87
+ }
88
+
89
+ getPatternsAfter(childReference: Pattern): Pattern[] {
90
+ return this._pattern.getPatternsAfter(childReference);
91
+ }
92
+
93
+ getNextPatterns(): Pattern[] {
94
+ return this._pattern.getNextPatterns();
95
+ }
96
+
97
+ find(predicate: (pattern: Pattern) => boolean): Pattern | null {
98
+ return this._pattern.find(predicate);
99
+ }
100
+
101
+ isEqual(pattern: Pattern): boolean {
102
+ return pattern.type === this.type && this.children.every((c, index) => c.isEqual(pattern.children[index]));
103
+ }
104
+
105
+ }
@@ -97,10 +97,28 @@ export class Reference implements Pattern {
97
97
  }
98
98
 
99
99
  private _findPattern(): Pattern | null {
100
- const root = this._getRoot();
100
+ let pattern = this._parent;
101
+
102
+ while (pattern != null) {
103
+ if (pattern.type !== "context") {
104
+ pattern = pattern.parent;
105
+ continue;
106
+ }
101
107
 
108
+ const foundPattern = findPattern(pattern, (pattern: Pattern) => {
109
+ return pattern.name === this._name && pattern.type !== "reference" && pattern.type !== "context";
110
+ });
111
+
112
+ if (foundPattern != null) {
113
+ return foundPattern;
114
+ }
115
+
116
+ pattern = pattern.parent;
117
+ }
118
+
119
+ const root = this._getRoot();
102
120
  return findPattern(root, (pattern: Pattern) => {
103
- return pattern.name === this._name && pattern.type !== "reference";
121
+ return pattern.name === this._name && pattern.type !== "reference" && pattern.type !== "context";
104
122
  });
105
123
  }
106
124