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.
- package/dist/index.browser.js +44 -77
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +44 -77
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +44 -77
- package/dist/index.js.map +1 -1
- package/dist/patterns/Cursor.d.ts +0 -11
- package/dist/patterns/CursorHistory.d.ts +0 -7
- package/dist/patterns/DepthCache.d.ts +6 -0
- package/package.json +1 -1
- package/src/patterns/Cursor.ts +1 -51
- package/src/patterns/CursorHistory.ts +0 -16
- package/src/patterns/DepthCache.ts +26 -0
- package/src/patterns/FiniteRepeat.ts +0 -5
- package/src/patterns/InfiniteRepeat.ts +0 -5
- package/src/patterns/Literal.ts +0 -4
- package/src/patterns/Not.ts +0 -3
- package/src/patterns/Optional.ts +0 -4
- package/src/patterns/Options.test.ts +2 -3
- package/src/patterns/Options.ts +19 -10
- package/src/patterns/Regex.ts +0 -3
- package/src/patterns/Sequence.ts +11 -4
|
@@ -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
|
}
|
package/package.json
CHANGED
package/src/patterns/Cursor.ts
CHANGED
|
@@ -1,27 +1,13 @@
|
|
|
1
1
|
import { Node } from "../ast/Node";
|
|
2
|
-
import { CursorHistory, Match
|
|
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
|
|
package/src/patterns/Literal.ts
CHANGED
|
@@ -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
|
|
package/src/patterns/Not.ts
CHANGED
|
@@ -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
|
|
package/src/patterns/Optional.ts
CHANGED
|
@@ -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
|
|
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", () => {
|
package/src/patterns/Options.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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);
|
package/src/patterns/Regex.ts
CHANGED
|
@@ -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
|
|
package/src/patterns/Sequence.ts
CHANGED
|
@@ -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
|
-
|
|
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++) {
|