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.
- package/dist/index.browser.js +47 -77
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +47 -77
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +47 -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/Options.d.ts +1 -0
- package/dist/patterns/Sequence.d.ts +1 -0
- package/package.json +1 -1
- package/src/patterns/Cursor.ts +1 -51
- package/src/patterns/CursorHistory.ts +0 -16
- 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 +34 -10
- package/src/patterns/Regex.ts +0 -3
- package/src/patterns/Sequence.ts +33 -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
|
}
|
|
@@ -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[];
|
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
|
}
|
|
@@ -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
|
@@ -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
|
-
|
|
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
|
|
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
|
@@ -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
|
|