clarity-pattern-parser 10.0.5 → 10.0.6

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
  }
@@ -22,6 +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 _isBeyondRecursiveLimit;
25
26
  getTokens(): string[];
26
27
  getTokensAfter(_childReference: Pattern): string[];
27
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 _isBeyondRecursiveLimit;
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.0.5",
3
+ "version": "10.0.6",
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
  }
@@ -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", () => {
@@ -83,37 +83,35 @@ export class Options implements Pattern {
83
83
  }
84
84
 
85
85
  parse(cursor: Cursor): Node | null {
86
- cursor.startParseWith(this);
87
-
88
86
  this._firstIndex = cursor.index;
89
-
90
87
  const node = this._tryToParse(cursor);
91
88
 
92
89
  if (node != null) {
93
90
  cursor.moveTo(node.lastIndex);
94
91
  cursor.resolveError();
95
92
 
96
- cursor.endParse();
97
93
  return node;
98
94
  }
99
95
 
100
96
  cursor.recordErrorAt(this._firstIndex, this._firstIndex, this);
101
- cursor.endParse();
102
97
  return null;
103
98
  }
104
99
 
100
+
101
+
105
102
  private _tryToParse(cursor: Cursor): Node | null {
103
+ if (this._isBeyondRecursiveLimit()){
104
+ cursor.recordErrorAt(cursor.index, cursor.index, this);
105
+ return null;
106
+ }
107
+
106
108
  const results: (Node | null)[] = [];
107
109
 
108
110
  for (const pattern of this._children) {
109
111
  cursor.moveTo(this._firstIndex);
110
112
  let result = null;
111
113
 
112
- try {
113
- result = pattern.parse(cursor);
114
- } catch {
115
- continue;
116
- }
114
+ result = pattern.parse(cursor);
117
115
 
118
116
  if (this._isGreedy) {
119
117
  results.push(result);
@@ -132,6 +130,32 @@ export class Options implements Pattern {
132
130
  return nonNullResults[0] || null;
133
131
  }
134
132
 
133
+ private _isBeyondRecursiveLimit(){
134
+ let pattern: Pattern = this;
135
+ const matches: Pattern[] = [];
136
+
137
+ while(pattern.parent != null){
138
+ if (pattern.type !== "options"){
139
+ pattern = pattern.parent;
140
+ continue;
141
+ }
142
+
143
+ const optionsPattern = pattern as Options;
144
+
145
+ if (pattern.id === this.id && optionsPattern._firstIndex === this._firstIndex){
146
+ matches.push(pattern);
147
+
148
+ if (matches.length > 2){
149
+ return true;
150
+ }
151
+ }
152
+
153
+ pattern = pattern.parent;
154
+ }
155
+
156
+ return false;
157
+ }
158
+
135
159
  getTokens(): string[] {
136
160
  const tokens: string[] = [];
137
161
 
@@ -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
 
@@ -83,11 +83,14 @@ export class Sequence implements Pattern {
83
83
  }
84
84
 
85
85
  parse(cursor: Cursor): Node | null {
86
- cursor.startParseWith(this);
87
-
88
86
  this._firstIndex = cursor.index;
89
87
  this._nodes = [];
90
88
 
89
+ if (this._isBeyondRecursiveLimit()) {
90
+ cursor.recordErrorAt(cursor.index, cursor.index, this);
91
+ return null;
92
+ }
93
+
91
94
  const passed = this.tryToParse(cursor);
92
95
 
93
96
  if (passed) {
@@ -97,15 +100,15 @@ export class Sequence implements Pattern {
97
100
  cursor.recordMatch(this, node);
98
101
  }
99
102
 
100
- cursor.endParse();
101
103
  return node;
102
104
  }
103
105
 
104
- cursor.endParse();
105
106
  return null;
106
107
  }
107
108
 
108
109
  private tryToParse(cursor: Cursor): boolean {
110
+
111
+
109
112
  let passed = false;
110
113
 
111
114
  for (let i = 0; i < this._children.length; i++) {
@@ -166,6 +169,32 @@ export class Sequence implements Pattern {
166
169
  return passed;
167
170
  }
168
171
 
172
+ private _isBeyondRecursiveLimit() {
173
+ let pattern: Pattern = this;
174
+ const matches: Pattern[] = [];
175
+
176
+ while (pattern.parent != null) {
177
+ if (pattern.type !== "sequence") {
178
+ pattern = pattern.parent;
179
+ continue;
180
+ }
181
+
182
+ const sequencePattern = pattern as Sequence;
183
+
184
+ if (pattern.id === this.id && sequencePattern._firstIndex === this._firstIndex) {
185
+ matches.push(pattern);
186
+
187
+ if (matches.length > 1) {
188
+ return true;
189
+ }
190
+ }
191
+
192
+ pattern = pattern.parent;
193
+ }
194
+
195
+ return false;
196
+ }
197
+
169
198
  private getLastValidNode(): Node | null {
170
199
  const nodes = filterOutNull(this._nodes);
171
200