clarity-pattern-parser 11.1.5 → 11.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clarity-pattern-parser",
3
- "version": "11.1.5",
3
+ "version": "11.2.0",
4
4
  "description": "Parsing Library for Typescript and Javascript.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.esm.js",
@@ -11,6 +11,7 @@ import { Optional } from "../patterns/Optional";
11
11
  import { Context } from "../patterns/Context";
12
12
  import { createPatternsTemplate, patterns } from "./patterns";
13
13
  import { Expression } from "../patterns/Expression";
14
+ import { Cursor } from "../patterns/Cursor";
14
15
 
15
16
  describe("Grammar", () => {
16
17
  test("Literal", () => {
@@ -768,4 +769,13 @@ describe("Grammar", () => {
768
769
  `;
769
770
  });
770
771
 
772
+ test("Take Until", () => {
773
+ const { scriptText } = patterns`
774
+ script-text = ?->| "</script"
775
+ `;
776
+ const result = scriptText.parse(new Cursor("function(){}</script"));
777
+
778
+ expect(result?.value).toBe("function(){}");
779
+ });
780
+
771
781
  });
@@ -11,6 +11,7 @@ import { Repeat, RepeatOptions } from "../patterns/Repeat";
11
11
  import { Optional } from "../patterns/Optional";
12
12
  import { Context } from "../patterns/Context";
13
13
  import { Expression } from "../patterns/Expression";
14
+ import { TakeUntil } from "../patterns/TakeUntil";
14
15
  import { RightAssociated } from "../patterns/RightAssociated";
15
16
  import { generateErrorMessage } from "../patterns/generate_error_message";
16
17
  import { tokens } from "./decorators/tokens";
@@ -30,6 +31,7 @@ const patternNodes: Record<string, boolean> = {
30
31
  "sequence-literal": true,
31
32
  "repeat-literal": true,
32
33
  "alias-literal": true,
34
+ "take-until-literal": true,
33
35
  "configurable-anonymous-pattern": true
34
36
  };
35
37
 
@@ -180,6 +182,10 @@ export class Grammar {
180
182
  this._saveAlias(n);
181
183
  break;
182
184
  }
185
+ case "take-until-literal": {
186
+ this._saveTakeUntil(n);
187
+ break;
188
+ }
183
189
  case "configurable-anonymous-pattern": {
184
190
  this._saveConfigurableAnonymous(n);
185
191
  break;
@@ -196,6 +202,7 @@ export class Grammar {
196
202
  });
197
203
  }
198
204
 
205
+
199
206
  private _saveLiteral(statementNode: Node) {
200
207
  const nameNode = statementNode.find(n => n.name === "name") as Node;
201
208
  const literalNode = statementNode.find(n => n.name === "literal") as Node;
@@ -322,6 +329,9 @@ export class Grammar {
322
329
  case "sequence-literal": {
323
330
  return this._buildSequence(name, node);
324
331
  }
332
+ case "take-until-literal": {
333
+ return this._buildTakeUntil(name, node);
334
+ }
325
335
  case "complex-anonymous-pattern": {
326
336
  return this._buildComplexAnonymousPattern(node);
327
337
  }
@@ -422,6 +432,24 @@ export class Grammar {
422
432
  return isOptional ? new Optional(name, new Repeat(`inner-optional-${name}`, pattern, options)) : new Repeat(name, pattern, options);
423
433
  }
424
434
 
435
+ private _saveTakeUntil(statementNode: Node) {
436
+ const nameNode = statementNode.find(n => n.name === "name") as Node;
437
+ const name = nameNode.value;
438
+ const takeUntilNode = statementNode.find(n => n.name === "take-until-literal") as Node;
439
+ const takeUntil = this._buildTakeUntil(name, takeUntilNode);
440
+
441
+ this._applyDecorators(statementNode, takeUntil);
442
+ this._parseContext.patternsByName.set(name, takeUntil);
443
+ }
444
+
445
+
446
+ private _buildTakeUntil(name: string, takeUntilNode: Node) {
447
+ const patternNode = takeUntilNode.children[takeUntilNode.children.length - 1];
448
+ const untilPattern = this._buildPattern(patternNode);
449
+
450
+ return new TakeUntil(name, untilPattern);
451
+ }
452
+
425
453
  private _saveConfigurableAnonymous(node: Node) {
426
454
  const nameNode = node.find(n => n.name === "name") as Node;
427
455
  const name = nameNode.value;
@@ -441,7 +469,7 @@ export class Grammar {
441
469
 
442
470
  private async _resolveImports(ast: Node) {
443
471
  const importStatements = ast.findAll(n => {
444
- return n.name === "import-from" || n.name === "param-name-with-default-value"
472
+ return n.name === "import-from" || n.name === "param-name-with-default-value";
445
473
  });
446
474
 
447
475
  for (const statement of importStatements) {
@@ -14,6 +14,7 @@ export const anonymousLiterals = new Options("anonymous-literals", [
14
14
  ]);
15
15
 
16
16
  export const anonymousWrappedLiterals = new Options("anonymous-wrapped-literals", [
17
+ new Reference("take-until-literal"),
17
18
  new Reference("options-literal"),
18
19
  new Reference("sequence-literal"),
19
20
  new Reference("complex-anonymous-pattern")
@@ -5,6 +5,7 @@ import { repeatLiteral } from "./repeatLiteral";
5
5
  import { sequenceLiteral } from "./sequenceLiteral";
6
6
  import { optionsLiteral } from "./optionsLiteral";
7
7
  import { anonymousPattern } from "./anonymousPattern";
8
+ import { takeUntilLiteral } from "./takeUtilLiteral";
8
9
  import { Sequence } from "../../patterns/Sequence";
9
10
  import { Literal } from "../../patterns/Literal";
10
11
  import { name } from "./name";
@@ -20,8 +21,9 @@ export const pattern = new Options("pattern", [
20
21
  literal,
21
22
  regexLiteral,
22
23
  repeatLiteral,
24
+ takeUntilLiteral,
23
25
  aliasLiteral,
24
26
  optionsLiteral,
25
27
  sequenceLiteral,
26
28
  configurableAnonymousPattern,
27
- ], true);
29
+ ], true);
@@ -0,0 +1,21 @@
1
+ import { Literal } from "../../patterns/Literal"
2
+ import { Optional } from "../../patterns/Optional";
3
+ import { Sequence } from "../../patterns/Sequence";
4
+ import { lineSpaces } from "./spaces";
5
+ import { name } from "./name";
6
+ import { Reference } from "../../patterns/Reference";
7
+
8
+ const anyChar = new Literal("any-char", "?");
9
+ const upTo = new Literal("up-to", "->");
10
+ const wall = new Literal("wall", "|");
11
+ const optionalLineSpaces = new Optional("optional-line-spaces", lineSpaces);
12
+
13
+ export const takeUntilLiteral = new Sequence("take-until-literal", [
14
+ anyChar,
15
+ optionalLineSpaces,
16
+ upTo,
17
+ optionalLineSpaces,
18
+ wall,
19
+ optionalLineSpaces,
20
+ new Reference("pattern")
21
+ ]);
@@ -0,0 +1,62 @@
1
+ import { Cursor } from "./Cursor";
2
+ import { Literal } from "./Literal";
3
+ import { Options } from "./Options";
4
+ import { TakeUntil } from "./TakeUntil";
5
+
6
+ describe("TakeUntil", () => {
7
+ test("Take With No End", () => {
8
+ const takeUntilScript = new TakeUntil(
9
+ "script-content",
10
+ new Literal("close-script-tag", "</script")
11
+ );
12
+
13
+ const result = takeUntilScript.exec("function(){}");
14
+
15
+ expect(result.ast?.value).toBe("function(){}");
16
+ });
17
+
18
+ test("Take Until Terminating Match", () => {
19
+ const takeUntilScript = new TakeUntil(
20
+ "script-content",
21
+ new Literal("close-script-tag", "</script")
22
+ );
23
+ const cursor = new Cursor("function(){}function(){}</script");
24
+ const result = takeUntilScript.parse(cursor);
25
+
26
+ expect(result?.value).toBe("function(){}function(){}");
27
+ expect(cursor.index).toBe(23);
28
+ });
29
+
30
+ test("Take Until Terminating Complex Match", () => {
31
+ const takeUntilScript = new TakeUntil(
32
+ "script-content",
33
+ new Options("end-tags", [
34
+ new Literal("close-script-tag", "</script"),
35
+ new Literal("close-style-tag", "</style")
36
+ ])
37
+ );
38
+ let cursor = new Cursor("function(){}function(){}</script");
39
+ let result = takeUntilScript.parse(cursor);
40
+
41
+ expect(result?.value).toBe("function(){}function(){}");
42
+ expect(cursor.index).toBe(23);
43
+
44
+ cursor = new Cursor("function(){}function(){}</style");
45
+ result = takeUntilScript.parse(cursor);
46
+
47
+ expect(result?.value).toBe("function(){}function(){}");
48
+ expect(cursor.index).toBe(23);
49
+ });
50
+
51
+ test("Error", () => {
52
+ const takeUntilScript = new TakeUntil(
53
+ "script-content",
54
+ new Literal("close-script-tag", "</script")
55
+ );
56
+ const cursor = new Cursor("</script");
57
+ const result = takeUntilScript.parse(cursor);
58
+
59
+ expect(result).toBeNull();
60
+ expect(cursor.index).toBe(0);
61
+ });
62
+ });
@@ -0,0 +1,166 @@
1
+ import { Node } from "../ast/Node";
2
+ import { Cursor } from "./Cursor";
3
+ import { execPattern } from "./execPattern";
4
+ import { ParseResult } from "./ParseResult";
5
+ import { Pattern } from "./Pattern";
6
+ import { testPattern } from "./testPattern";
7
+
8
+ let idIndex = 0;
9
+
10
+ export class TakeUntil implements Pattern {
11
+ private _id: string;
12
+ private _type: string;
13
+ private _name: string;
14
+ private _parent: Pattern | null;
15
+ private _children: Pattern[];
16
+ private _startedOnIndex: number;
17
+ private _terminatingPattern: Pattern;
18
+ private _tokens: string[];
19
+
20
+ get id(): string {
21
+ return this._id;
22
+ }
23
+
24
+ get type(): string {
25
+ return this._type;
26
+ }
27
+
28
+ get name(): string {
29
+ return this._name;
30
+ }
31
+
32
+ get children(): Pattern[] {
33
+ return this._children;
34
+ }
35
+
36
+ get parent(): Pattern | null {
37
+ return this._parent;
38
+ }
39
+
40
+ set parent(pattern: Pattern | null) {
41
+ this._parent = pattern;
42
+ }
43
+
44
+ get startedOnIndex(): number {
45
+ return this._startedOnIndex;
46
+ }
47
+
48
+ constructor(name: string, terminatingPattern: Pattern) {
49
+ this._id = String(idIndex++);
50
+ this._type = "take-until";
51
+ this._name = name;
52
+ this._parent = null;
53
+ this._terminatingPattern = terminatingPattern;
54
+ this._children = [this._terminatingPattern];
55
+ this._tokens = [];
56
+ this._startedOnIndex = 0;
57
+ }
58
+
59
+ parse(cursor: Cursor): Node | null {
60
+ let cursorIndex = cursor.index;
61
+ let foundMatch = false;
62
+ this._startedOnIndex = cursor.index;
63
+
64
+ let terminatingResult = this._terminatingPattern.parse(cursor);
65
+
66
+ if (terminatingResult == null) {
67
+ foundMatch = true;
68
+
69
+ cursor.moveTo(cursorIndex);
70
+ cursorIndex += 1;
71
+ cursor.hasNext() && cursor.next();
72
+
73
+ cursor.resolveError();
74
+ }
75
+
76
+ while (true) {
77
+ terminatingResult = this._terminatingPattern.parse(cursor);
78
+
79
+ if (terminatingResult == null) {
80
+ cursor.moveTo(cursorIndex);
81
+ cursorIndex += 1;
82
+
83
+ if (cursor.hasNext()) {
84
+ cursor.next();
85
+ } else {
86
+ break;
87
+ }
88
+
89
+ cursor.resolveError();
90
+ } else {
91
+ break;
92
+ }
93
+ }
94
+
95
+ if (foundMatch) {
96
+ cursor.moveTo(cursorIndex - 1);
97
+ const value = cursor.getChars(this.startedOnIndex, cursorIndex - 1);
98
+ return Node.createValueNode(this._type, this._name, value);
99
+ } else {
100
+ cursor.resolveError();
101
+ cursor.moveTo(this.startedOnIndex);
102
+ cursor.recordErrorAt(this._startedOnIndex, this._startedOnIndex, this);
103
+ return null;
104
+ }
105
+ }
106
+
107
+ exec(text: string, record?: boolean | undefined): ParseResult {
108
+ return execPattern(this, text, record);
109
+ }
110
+
111
+ test(text: string, record?: boolean | undefined): boolean {
112
+ return testPattern(this, text, record);
113
+ }
114
+
115
+ clone(name = this.name): Pattern {
116
+ const clone = new TakeUntil(name, this._terminatingPattern);
117
+ clone._id = this._id;
118
+
119
+ return clone;
120
+ }
121
+
122
+ getTokens() {
123
+ return this._tokens;
124
+ }
125
+
126
+ getTokensAfter(_childReference: Pattern): string[] {
127
+ return [];
128
+ }
129
+
130
+ getNextTokens(): string[] {
131
+ if (this.parent == null) {
132
+ return [];
133
+ }
134
+
135
+ return this.parent.getTokensAfter(this);
136
+ }
137
+
138
+ getPatterns(): Pattern[] {
139
+ return [this];
140
+ }
141
+
142
+ getPatternsAfter(_childReference: Pattern): Pattern[] {
143
+ return [];
144
+ }
145
+
146
+ getNextPatterns(): Pattern[] {
147
+ if (this.parent == null) {
148
+ return [];
149
+ }
150
+
151
+ return this.parent.getPatternsAfter(this);
152
+ }
153
+
154
+ find(_predicate: (p: Pattern) => boolean): Pattern | null {
155
+ return null;
156
+ }
157
+
158
+ setTokens(tokens: string[]) {
159
+ this._tokens = tokens;
160
+ }
161
+
162
+ isEqual(pattern: Pattern): boolean {
163
+ return pattern.type === this.type && this.children.every((c, index) => c.isEqual(pattern.children[index]));
164
+ }
165
+
166
+ }