clarity-pattern-parser 10.0.5 → 10.0.7

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.
@@ -2,18 +2,11 @@ import { Node } from "../ast/Node";
2
2
  import { Match } from "./CursorHistory";
3
3
  import { ParseError } from "./ParseError";
4
4
  import { Pattern } from "./Pattern";
5
- export declare class CyclicalParseError extends Error {
6
- readonly patternId: string;
7
- readonly patternName: string;
8
- readonly cursorIndex: number;
9
- constructor(patternId: string, patternName: string, cursorIndex: number);
10
- }
11
5
  export declare class Cursor {
12
6
  private _text;
13
7
  private _index;
14
8
  private _length;
15
9
  private _history;
16
- private _stackTrace;
17
10
  get text(): string;
18
11
  get isOnFirst(): boolean;
19
12
  get isOnLast(): boolean;
@@ -45,8 +38,4 @@ export declare class Cursor {
45
38
  resolveError(): void;
46
39
  startRecording(): void;
47
40
  stopRecording(): void;
48
- startParseWith(pattern: Pattern): void;
49
- endParse(): void;
50
- audit(): string[];
51
- private _buildPatternContext;
52
41
  }
@@ -1,10 +1,6 @@
1
1
  import { Node } from "../ast/Node";
2
2
  import { ParseError } from "./ParseError";
3
3
  import { Pattern } from "./Pattern";
4
- export interface Trace {
5
- pattern: Pattern;
6
- cursorIndex: number;
7
- }
8
4
  export interface Match {
9
5
  pattern: Pattern | null;
10
6
  node: Node | null;
@@ -18,7 +14,6 @@ export declare class CursorHistory {
18
14
  private _patterns;
19
15
  private _nodes;
20
16
  private _errors;
21
- private _trace;
22
17
  get isRecording(): boolean;
23
18
  get rootMatch(): Match;
24
19
  get leafMatch(): Match;
@@ -28,11 +23,9 @@ export declare class CursorHistory {
28
23
  get error(): ParseError | null;
29
24
  get nodes(): Node[];
30
25
  get patterns(): Pattern[];
31
- get trace(): Trace[];
32
26
  recordMatch(pattern: Pattern, node: Node): void;
33
27
  recordErrorAt(startIndex: number, endIndex: number, pattern: Pattern): void;
34
28
  startRecording(): void;
35
29
  stopRecording(): void;
36
30
  resolveError(): void;
37
- pushStackTrace(trace: Trace): void;
38
31
  }
@@ -0,0 +1,6 @@
1
+ export declare class DepthCache {
2
+ private _depthMap;
3
+ getDepth(name: string, cursorIndex: number): number;
4
+ incrementDepth(name: string, cursorIndex: number): void;
5
+ decrementDepth(name: string, cursorIndex: number): void;
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clarity-pattern-parser",
3
- "version": "10.0.5",
3
+ "version": "10.0.7",
4
4
  "description": "Parsing Library for Typescript and Javascript.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.esm.js",
@@ -1,27 +1,13 @@
1
1
  import { Node } from "../ast/Node";
2
- import { CursorHistory, Match, Trace } from "./CursorHistory";
2
+ import { CursorHistory, Match } from "./CursorHistory";
3
3
  import { ParseError } from "./ParseError";
4
4
  import { Pattern } from "./Pattern";
5
5
 
6
- export class CyclicalParseError extends Error {
7
- readonly patternId: string;
8
- readonly patternName: string;
9
- readonly cursorIndex: number;
10
-
11
- constructor(patternId: string, patternName: string, cursorIndex: number) {
12
- super("Cyclical Parse Error");
13
- this.patternId = patternId;
14
- this.patternName = patternName;
15
- this.cursorIndex = cursorIndex;
16
- }
17
- }
18
-
19
6
  export class Cursor {
20
7
  private _text: string;
21
8
  private _index: number;
22
9
  private _length: number;
23
10
  private _history: CursorHistory;
24
- private _stackTrace: Trace[];
25
11
 
26
12
  get text(): string {
27
13
  return this._text;
@@ -92,7 +78,6 @@ export class Cursor {
92
78
  this._index = 0;
93
79
  this._length = [...text].length;
94
80
  this._history = new CursorHistory();
95
- this._stackTrace = [];
96
81
  }
97
82
 
98
83
  hasNext(): boolean {
@@ -157,39 +142,4 @@ export class Cursor {
157
142
  this._history.stopRecording();
158
143
  }
159
144
 
160
- startParseWith(pattern: Pattern) {
161
- const trace = {
162
- pattern,
163
- cursorIndex: this.index
164
- };
165
-
166
- const hasCycle = this._stackTrace.filter(t => t.pattern.id === pattern.id && this.index === t.cursorIndex).length > 1;
167
- if (hasCycle) {
168
- throw new CyclicalParseError(pattern.id, pattern.name, this.index);
169
- }
170
-
171
- this._history.pushStackTrace(trace);
172
- this._stackTrace.push(trace);
173
- }
174
-
175
- endParse() {
176
- this._stackTrace.pop();
177
- }
178
-
179
- audit() {
180
- return this._history.trace.map(t => {
181
- const onChar = this.getChars(t.cursorIndex, t.cursorIndex);
182
- const restChars = this.getChars(t.cursorIndex + 1, t.cursorIndex + 5);
183
- const context = `{${t.cursorIndex}}[${onChar}]${restChars}`;
184
- return `${this._buildPatternContext(t.pattern)}-->${context}`;
185
- });
186
- }
187
-
188
- private _buildPatternContext(pattern: Pattern) {
189
- if (pattern.parent != null) {
190
- return `${pattern.parent.name}.${pattern.name}`;
191
- }
192
- return pattern.name;
193
- }
194
-
195
145
  }
@@ -2,11 +2,6 @@ import { Node } from "../ast/Node";
2
2
  import { ParseError } from "./ParseError";
3
3
  import { Pattern } from "./Pattern";
4
4
 
5
- export interface Trace {
6
- pattern: Pattern;
7
- cursorIndex: number;
8
- }
9
-
10
5
  export interface Match {
11
6
  pattern: Pattern | null;
12
7
  node: Node | null;
@@ -21,7 +16,6 @@ export class CursorHistory {
21
16
  private _patterns: Pattern[] = [];
22
17
  private _nodes: Node[] = [];
23
18
  private _errors: ParseError[] = [];
24
- private _trace: Trace[] = [];
25
19
 
26
20
  get isRecording(): boolean {
27
21
  return this._isRecording;
@@ -59,10 +53,6 @@ export class CursorHistory {
59
53
  return this._patterns;
60
54
  }
61
55
 
62
- get trace(): Trace[] {
63
- return this._trace;
64
- }
65
-
66
56
  recordMatch(pattern: Pattern, node: Node): void {
67
57
  if (this._isRecording) {
68
58
  this._patterns.push(pattern);
@@ -131,10 +121,4 @@ export class CursorHistory {
131
121
  this._currentError = null;
132
122
  }
133
123
 
134
- pushStackTrace(trace: Trace) {
135
- if (this._isRecording) {
136
- this._trace.push(trace);
137
- }
138
- }
139
-
140
124
  }
@@ -0,0 +1,26 @@
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
+ }
@@ -83,8 +83,6 @@ export class FiniteRepeat implements Pattern {
83
83
  }
84
84
 
85
85
  parse(cursor: Cursor): Node | null {
86
- cursor.startParseWith(this);
87
-
88
86
  const startIndex = cursor.index;
89
87
  const nodes: Node[] = [];
90
88
  const modulo = this._hasDivider ? 2 : 1;
@@ -128,13 +126,11 @@ export class FiniteRepeat implements Pattern {
128
126
  const lastIndex = cursor.index;
129
127
  cursor.moveTo(startIndex);
130
128
  cursor.recordErrorAt(startIndex, lastIndex, this);
131
- cursor.endParse();
132
129
  return null;
133
130
  }
134
131
 
135
132
  if (nodes.length === 0 && !cursor.hasError) {
136
133
  cursor.moveTo(startIndex);
137
- cursor.endParse();
138
134
  return null;
139
135
  }
140
136
 
@@ -143,7 +139,6 @@ export class FiniteRepeat implements Pattern {
143
139
 
144
140
  cursor.resolveError();
145
141
  cursor.moveTo(lastIndex);
146
- cursor.endParse();
147
142
 
148
143
  return new Node(this._type, this.name, firstIndex, lastIndex, nodes);
149
144
  }
@@ -106,8 +106,6 @@ export class InfiniteRepeat implements Pattern {
106
106
  }
107
107
 
108
108
  parse(cursor: Cursor): Node | null {
109
- cursor.startParseWith(this);
110
-
111
109
  this._firstIndex = cursor.index;
112
110
  this._nodes = [];
113
111
 
@@ -122,17 +120,14 @@ export class InfiniteRepeat implements Pattern {
122
120
  cursor.recordMatch(this, node);
123
121
  }
124
122
 
125
- cursor.endParse();
126
123
  return node;
127
124
  }
128
125
 
129
126
  if (this._min > 0) {
130
- cursor.endParse();
131
127
  return null;
132
128
  }
133
129
 
134
130
  cursor.resolveError();
135
- cursor.endParse();
136
131
  return null;
137
132
  }
138
133
 
@@ -82,8 +82,6 @@ export class Literal implements Pattern {
82
82
  }
83
83
 
84
84
  parse(cursor: Cursor): Node | null {
85
- cursor.startParseWith(this);
86
-
87
85
  this._firstIndex = cursor.index;
88
86
  const passed = this._tryToParse(cursor);
89
87
 
@@ -92,12 +90,10 @@ export class Literal implements Pattern {
92
90
  const node = this._createNode();
93
91
  cursor.recordMatch(this, node);
94
92
 
95
- cursor.endParse();
96
93
  return node;
97
94
  }
98
95
 
99
96
  cursor.recordErrorAt(this._firstIndex, this._endIndex, this);
100
- cursor.endParse();
101
97
  return null;
102
98
  }
103
99
 
@@ -65,8 +65,6 @@ export class Not implements Pattern {
65
65
  }
66
66
 
67
67
  parse(cursor: Cursor): Node | null {
68
- cursor.startParseWith(this);
69
-
70
68
  const firstIndex = cursor.index;
71
69
  this._children[0].parse(cursor);
72
70
 
@@ -79,7 +77,6 @@ export class Not implements Pattern {
79
77
  cursor.recordErrorAt(firstIndex, firstIndex, this);
80
78
  }
81
79
 
82
- cursor.endParse();
83
80
  return null;
84
81
  }
85
82
 
@@ -65,8 +65,6 @@ export class Optional implements Pattern {
65
65
  }
66
66
 
67
67
  parse(cursor: Cursor): Node | null {
68
- cursor.startParseWith(this);
69
-
70
68
  const firstIndex = cursor.index;
71
69
  const node = this._children[0].parse(cursor);
72
70
 
@@ -74,10 +72,8 @@ export class Optional implements Pattern {
74
72
  cursor.resolveError();
75
73
  cursor.moveTo(firstIndex);
76
74
 
77
- cursor.endParse();
78
75
  return null;
79
76
  } else {
80
- cursor.endParse();
81
77
  return node;
82
78
  }
83
79
 
@@ -291,9 +291,8 @@ describe("Options", () => {
291
291
  result = expression.exec("John ? Jane : John");
292
292
  expect(result.ast?.toString()).toBe("John ? Jane : John");
293
293
 
294
-
295
- result = expression.exec("John ? Jane : John ? Jane : John");
296
- expect(result.ast?.toString()).toBe("John ? Jane : John ? Jane : John");
294
+ result = expression.exec("John ? John ? Jane : John ? Jane : John : John");
295
+ expect(result.ast?.toString()).toBe("John ? John ? Jane : John ? Jane : John : John");
297
296
  });
298
297
 
299
298
  test("Deeper Cyclical Error Recorvery", () => {
@@ -4,7 +4,13 @@ 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';
7
8
 
9
+ /*
10
+ The following is created to reduce the overhead of recursion check.
11
+ */
12
+
13
+ const depthCache = new DepthCache();
8
14
  let idIndex = 0;
9
15
 
10
16
  export class Options implements Pattern {
@@ -83,37 +89,40 @@ export class Options implements Pattern {
83
89
  }
84
90
 
85
91
  parse(cursor: Cursor): Node | null {
86
- cursor.startParseWith(this);
87
-
92
+ // This is a cache to help with speed
88
93
  this._firstIndex = cursor.index;
94
+ depthCache.incrementDepth(this._id, this._firstIndex);
89
95
 
96
+ this._firstIndex = cursor.index;
90
97
  const node = this._tryToParse(cursor);
91
98
 
99
+ depthCache.decrementDepth(this._id, this._firstIndex);
100
+
92
101
  if (node != null) {
93
102
  cursor.moveTo(node.lastIndex);
94
103
  cursor.resolveError();
95
-
96
- cursor.endParse();
97
104
  return node;
98
105
  }
99
106
 
100
107
  cursor.recordErrorAt(this._firstIndex, this._firstIndex, this);
101
- cursor.endParse();
102
108
  return null;
103
109
  }
104
110
 
111
+
112
+
105
113
  private _tryToParse(cursor: Cursor): Node | null {
114
+ if (depthCache.getDepth(this._id, this._firstIndex) > 2) {
115
+ cursor.recordErrorAt(this._firstIndex, this._firstIndex, this);
116
+ return null;
117
+ }
118
+
106
119
  const results: (Node | null)[] = [];
107
120
 
108
121
  for (const pattern of this._children) {
109
122
  cursor.moveTo(this._firstIndex);
110
123
  let result = null;
111
124
 
112
- try {
113
- result = pattern.parse(cursor);
114
- } catch {
115
- continue;
116
- }
125
+ result = pattern.parse(cursor);
117
126
 
118
127
  if (this._isGreedy) {
119
128
  results.push(result);
@@ -96,13 +96,10 @@ export class Regex implements Pattern {
96
96
  }
97
97
 
98
98
  parse(cursor: Cursor) {
99
- cursor.startParseWith(this);
100
-
101
99
  this._firstIndex = cursor.index;
102
100
  this.resetState(cursor);
103
101
  this.tryToParse(cursor);
104
102
 
105
- cursor.endParse();
106
103
  return this._node;
107
104
  }
108
105
 
@@ -4,7 +4,9 @@ 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";
7
8
 
9
+ const depthCache = new DepthCache();
8
10
  let idIndex = 0;
9
11
 
10
12
  export class Sequence implements Pattern {
@@ -83,12 +85,14 @@ export class Sequence implements Pattern {
83
85
  }
84
86
 
85
87
  parse(cursor: Cursor): Node | null {
86
- cursor.startParseWith(this);
87
-
88
+ // This is a cache to help with speed
88
89
  this._firstIndex = cursor.index;
90
+ depthCache.incrementDepth(this._id, this._firstIndex);
91
+
89
92
  this._nodes = [];
90
93
 
91
94
  const passed = this.tryToParse(cursor);
95
+ depthCache.decrementDepth(this._id, this._firstIndex);
92
96
 
93
97
  if (passed) {
94
98
  const node = this.createNode(cursor);
@@ -97,15 +101,18 @@ export class Sequence implements Pattern {
97
101
  cursor.recordMatch(this, node);
98
102
  }
99
103
 
100
- cursor.endParse();
101
104
  return node;
102
105
  }
103
106
 
104
- cursor.endParse();
105
107
  return null;
106
108
  }
107
109
 
108
110
  private tryToParse(cursor: Cursor): boolean {
111
+ if (depthCache.getDepth(this._id, this._firstIndex) > 1) {
112
+ cursor.recordErrorAt(this._firstIndex, this._firstIndex, this);
113
+ return false;
114
+ }
115
+
109
116
  let passed = false;
110
117
 
111
118
  for (let i = 0; i < this._children.length; i++) {