clarity-pattern-parser 10.1.13 → 10.1.15

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.
@@ -22,7 +22,7 @@ export declare class Options implements Pattern {
22
22
  exec(text: string, record?: boolean): ParseResult;
23
23
  parse(cursor: Cursor): Node | null;
24
24
  private _tryToParse;
25
- private _shouldReverseOrder;
25
+ private _isBeyondRecursiveDepth;
26
26
  getTokens(): string[];
27
27
  getTokensAfter(_childReference: Pattern): string[];
28
28
  getNextTokens(): string[];
@@ -24,6 +24,7 @@ export declare class Sequence implements Pattern {
24
24
  };
25
25
  parse(cursor: Cursor): Node | null;
26
26
  private tryToParse;
27
+ private _isBeyondRecursiveDepth;
27
28
  private getLastValidNode;
28
29
  private areRemainingPatternsOptional;
29
30
  private createNode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clarity-pattern-parser",
3
- "version": "10.1.13",
3
+ "version": "10.1.15",
4
4
  "description": "Parsing Library for Typescript and Javascript.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.esm.js",
@@ -0,0 +1,16 @@
1
+ import { patterns } from "./patterns";
2
+
3
+ describe("Complex Grammar Tests", () => {
4
+ test("Nested", () => {
5
+ const { expression } = patterns`
6
+ integer = /[0-9]+/
7
+ variable = /[A-Za-z][A-Za-z0-9]*/
8
+ space = /\\s+/
9
+ or-expression = expression + space? + "||" + space? + expression
10
+ mult-expression = expression + space? + "*" + space? + expression
11
+ expression = or-expression | mult-expression | integer | variable
12
+ `;
13
+ const result = expression.exec("a * b || c");
14
+ expect(result.ast?.toString()).toBe("a * b || c");
15
+ });
16
+ });
@@ -9,6 +9,7 @@ import { Repeat } from "../patterns/Repeat";
9
9
  import { Grammar } from "./Grammar";
10
10
  import { Optional } from "../patterns/Optional";
11
11
  import { Context } from "../patterns/Context";
12
+ import { patterns } from "..";
12
13
 
13
14
  describe("Grammar", () => {
14
15
  test("Literal", () => {
@@ -569,4 +570,6 @@ describe("Grammar", () => {
569
570
  const result = fullname.exec("John Doe");
570
571
  expect(result?.ast?.value).toBe("John Doe");
571
572
  });
573
+
574
+
572
575
  });
@@ -490,4 +490,31 @@ describe("AutoComplete", () => {
490
490
  expect(results.options).toEqual(expected);
491
491
  });
492
492
 
493
- });
493
+ test("Furthest Error", () => {
494
+ const branchOne = new Sequence("branch-1", [
495
+ new Literal("space-1-1", " "),
496
+ new Literal("space-1-2", " "),
497
+ new Options('branch-1-options', [
498
+ new Literal("AA", "AA"),
499
+ new Literal("AB", "AB"),
500
+ ])
501
+ ]);
502
+ const branchTwo = new Sequence("branch-2", [
503
+ new Literal("space-2-1", " "),
504
+ new Literal("space-2-2", " "),
505
+ new Options('branch-2-options', [
506
+ new Literal("BA", "BA"),
507
+ new Literal("BB", "BB")
508
+ ])
509
+ ]);
510
+ const eitherBranch = new Options("either-branch", [branchOne, branchTwo]);
511
+
512
+ const autoComplete = new AutoComplete(eitherBranch);
513
+ const results = autoComplete.suggestFor(" B");
514
+ const expected = [
515
+ { startIndex: 3, text: "A" },
516
+ { startIndex: 3, text: "B" },
517
+ ];
518
+ expect(results.options).toEqual(expected);
519
+ })
520
+ });
@@ -76,9 +76,10 @@ export class AutoComplete {
76
76
  errorAtIndex = this._cursor.furthestError.endIndex;
77
77
  error = this._cursor.furthestError;
78
78
 
79
- errorAtIndex = options.reduce((errorAtIndex, option) =>
80
- Math.max(errorAtIndex, option.startIndex),
81
- errorAtIndex);
79
+ errorAtIndex = options.reduce(
80
+ (errorAtIndex, option) => Math.max(errorAtIndex, option.startIndex),
81
+ errorAtIndex
82
+ );
82
83
  }
83
84
 
84
85
  return {
@@ -98,17 +99,19 @@ export class AutoComplete {
98
99
 
99
100
  private _getAllOptions() {
100
101
  const errorMatches = this._getOptionsFromErrors();
101
- const leafMatches = this._cursor.leafMatches.map((m) => this._createSuggestionsFromMatch(m)).flat();
102
- const finalResults: SuggestionOption[] = [];
102
+ const validLeafMatches = this._cursor.leafMatches.filter(v => v.node?.lastIndex === this._cursor.getLastIndex())
103
103
 
104
- [...leafMatches, ...errorMatches].forEach(m=>{
105
- const index = finalResults.findIndex(f=> m.text === f.text);
104
+ const leafMatchSuggestions = validLeafMatches.map((m) => this._createSuggestionsFromMatch(m)).flat();
105
+ const uniqueResults: SuggestionOption[] = [];
106
+
107
+ [...leafMatchSuggestions, ...errorMatches].forEach(m=>{
108
+ const index = uniqueResults.findIndex(f => m.text === f.text);
106
109
  if (index === -1){
107
- finalResults.push(m);
110
+ uniqueResults.push(m);
108
111
  }
109
112
  });
110
113
 
111
- return finalResults;
114
+ return uniqueResults;
112
115
  }
113
116
 
114
117
  private _getOptionsFromErrors() {
@@ -118,9 +121,9 @@ export class AutoComplete {
118
121
  const tokens = this._getTokensForPattern(e.pattern);
119
122
  const adjustedTokens = tokens.map(t => t.slice(e.endIndex - e.startIndex));
120
123
  return this._createSuggestions(e.endIndex, adjustedTokens);
121
- });
124
+ }).flat();
122
125
 
123
- return suggestions.flat();
126
+ return suggestions;
124
127
  }
125
128
 
126
129
  private _createSuggestionsFromRoot(): SuggestionOption[] {
@@ -220,6 +220,4 @@ describe("Ecmascript 3", () => {
220
220
  result = expression.exec(`name.prop.anotherProp["Ha"][coolYo] === 1`);
221
221
  expect(result.ast?.value).toBe(`name.prop.anotherProp["Ha"][coolYo] === 1`);
222
222
  });
223
-
224
-
225
- });
223
+ });
@@ -4,13 +4,10 @@ import { Pattern } from "./Pattern";
4
4
  import { clonePatterns } from "./clonePatterns";
5
5
  import { findPattern } from "./findPattern";
6
6
  import { ParseResult } from "./ParseResult";
7
- import { DepthCache } from './DepthCache';
8
7
 
9
8
  /*
10
9
  The following is created to reduce the overhead of recursion check.
11
10
  */
12
-
13
- const depthCache = new DepthCache();
14
11
  let idIndex = 0;
15
12
 
16
13
  export class Options implements Pattern {
@@ -90,13 +87,9 @@ export class Options implements Pattern {
90
87
 
91
88
  parse(cursor: Cursor): Node | null {
92
89
  // This is a cache to help with speed
93
- this._firstIndex = cursor.index;
94
- depthCache.incrementDepth(this._id, this._firstIndex);
95
-
96
90
  this._firstIndex = cursor.index;
97
91
  const node = this._tryToParse(cursor);
98
92
 
99
- depthCache.decrementDepth(this._id, this._firstIndex);
100
93
 
101
94
  if (node != null) {
102
95
  cursor.moveTo(node.lastIndex);
@@ -108,19 +101,11 @@ export class Options implements Pattern {
108
101
  return null;
109
102
  }
110
103
 
111
-
112
-
113
104
  private _tryToParse(cursor: Cursor): Node | null {
114
- const shouldReverseOrder = this._shouldReverseOrder();
115
- let children = this._children;
105
+ let children = this.children;
116
106
 
117
- if (shouldReverseOrder) {
118
- children = this._children.slice().reverse();
119
- }
120
-
121
- if (depthCache.getDepth(this._id, this._firstIndex) > 2) {
122
- cursor.recordErrorAt(this._firstIndex, this._firstIndex, this);
123
- return null;
107
+ if (this._isBeyondRecursiveDepth()) {
108
+ children = children.slice().reverse();
124
109
  }
125
110
 
126
111
  const results: (Node | null)[] = [];
@@ -148,18 +133,23 @@ export class Options implements Pattern {
148
133
  return nonNullResults[0] || null;
149
134
  }
150
135
 
151
- private _shouldReverseOrder() {
152
- let count = 0;
153
- let pattern = this._parent;
136
+ private _isBeyondRecursiveDepth() {
137
+ let depth = 0;
138
+ let pattern: Pattern | null = this;
154
139
 
155
140
  while (pattern != null) {
156
141
  if (pattern.id === this.id) {
157
- count++;
142
+ depth++;
158
143
  }
144
+
145
+ if (depth >= this.children.length) {
146
+ return true;
147
+ }
148
+
159
149
  pattern = pattern.parent;
160
150
  }
161
151
 
162
- return count % 2 === 1;
152
+ return false;
163
153
  }
164
154
 
165
155
  getTokens(): string[] {
@@ -4,9 +4,7 @@ import { Node } from "../ast/Node";
4
4
  import { clonePatterns } from "./clonePatterns";
5
5
  import { filterOutNull } from "./filterOutNull";
6
6
  import { findPattern } from "./findPattern";
7
- import { DepthCache } from "./DepthCache";
8
7
 
9
- const depthCache = new DepthCache();
10
8
  let idIndex = 0;
11
9
 
12
10
  export class Sequence implements Pattern {
@@ -87,12 +85,8 @@ export class Sequence implements Pattern {
87
85
  parse(cursor: Cursor): Node | null {
88
86
  // This is a cache to help with speed
89
87
  this._firstIndex = cursor.index;
90
- depthCache.incrementDepth(this._id, this._firstIndex);
91
-
92
88
  this._nodes = [];
93
-
94
89
  const passed = this.tryToParse(cursor);
95
- depthCache.decrementDepth(this._id, this._firstIndex);
96
90
 
97
91
  if (passed) {
98
92
  const node = this.createNode(cursor);
@@ -108,7 +102,7 @@ export class Sequence implements Pattern {
108
102
  }
109
103
 
110
104
  private tryToParse(cursor: Cursor): boolean {
111
- if (depthCache.getDepth(this._id, this._firstIndex) > 1) {
105
+ if (this._isBeyondRecursiveDepth()) {
112
106
  cursor.recordErrorAt(this._firstIndex, this._firstIndex, this);
113
107
  return false;
114
108
  }
@@ -173,6 +167,25 @@ export class Sequence implements Pattern {
173
167
  return passed;
174
168
  }
175
169
 
170
+ private _isBeyondRecursiveDepth() {
171
+ let depth = 0;
172
+ let pattern: Pattern | null = this;
173
+
174
+ while (pattern != null) {
175
+ if (pattern.id === this.id && this._firstIndex === (pattern as Sequence)._firstIndex) {
176
+ depth++;
177
+ }
178
+
179
+ if (depth > 1) {
180
+ return true;
181
+ }
182
+
183
+ pattern = pattern.parent;
184
+ }
185
+
186
+ return false;
187
+ }
188
+
176
189
  private getLastValidNode(): Node | null {
177
190
  const nodes = filterOutNull(this._nodes);
178
191
 
@@ -1,26 +0,0 @@
1
- export class DepthCache {
2
- private _depthMap: Record<string, Record<number, number>> = {};
3
-
4
- getDepth(name: string, cursorIndex: number) {
5
- if (this._depthMap[name] == null) {
6
- this._depthMap[name] = {};
7
- }
8
-
9
- if (this._depthMap[name][cursorIndex] == null) {
10
- this._depthMap[name][cursorIndex] = 0;
11
- }
12
-
13
-
14
- return this._depthMap[name][cursorIndex];
15
- }
16
-
17
- incrementDepth(name: string, cursorIndex: number) {
18
- const depth = this.getDepth(name, cursorIndex);
19
- this._depthMap[name][cursorIndex] = depth + 1;
20
- }
21
-
22
- decrementDepth(name: string, cursorIndex: number) {
23
- const depth = this.getDepth(name, cursorIndex);
24
- this._depthMap[name][cursorIndex] = depth - 1;
25
- }
26
- }