clarity-pattern-parser 11.1.5 → 11.2.0
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/package.json +1 -1
- package/src/grammar/Grammar.test.ts +10 -0
- package/src/grammar/Grammar.ts +29 -1
- package/src/grammar/patterns/literals.ts +1 -0
- package/src/grammar/patterns/pattern.ts +3 -1
- package/src/grammar/patterns/takeUtilLiteral.ts +21 -0
- package/src/patterns/TakeUntil.test.ts +62 -0
- package/src/patterns/TakeUntil.ts +166 -0
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ import { Optional } from "../patterns/Optional";
|
|
|
11
11
|
import { Context } from "../patterns/Context";
|
|
12
12
|
import { createPatternsTemplate, patterns } from "./patterns";
|
|
13
13
|
import { Expression } from "../patterns/Expression";
|
|
14
|
+
import { Cursor } from "../patterns/Cursor";
|
|
14
15
|
|
|
15
16
|
describe("Grammar", () => {
|
|
16
17
|
test("Literal", () => {
|
|
@@ -768,4 +769,13 @@ describe("Grammar", () => {
|
|
|
768
769
|
`;
|
|
769
770
|
});
|
|
770
771
|
|
|
772
|
+
test("Take Until", () => {
|
|
773
|
+
const { scriptText } = patterns`
|
|
774
|
+
script-text = ?->| "</script"
|
|
775
|
+
`;
|
|
776
|
+
const result = scriptText.parse(new Cursor("function(){}</script"));
|
|
777
|
+
|
|
778
|
+
expect(result?.value).toBe("function(){}");
|
|
779
|
+
});
|
|
780
|
+
|
|
771
781
|
});
|
package/src/grammar/Grammar.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { Repeat, RepeatOptions } from "../patterns/Repeat";
|
|
|
11
11
|
import { Optional } from "../patterns/Optional";
|
|
12
12
|
import { Context } from "../patterns/Context";
|
|
13
13
|
import { Expression } from "../patterns/Expression";
|
|
14
|
+
import { TakeUntil } from "../patterns/TakeUntil";
|
|
14
15
|
import { RightAssociated } from "../patterns/RightAssociated";
|
|
15
16
|
import { generateErrorMessage } from "../patterns/generate_error_message";
|
|
16
17
|
import { tokens } from "./decorators/tokens";
|
|
@@ -30,6 +31,7 @@ const patternNodes: Record<string, boolean> = {
|
|
|
30
31
|
"sequence-literal": true,
|
|
31
32
|
"repeat-literal": true,
|
|
32
33
|
"alias-literal": true,
|
|
34
|
+
"take-until-literal": true,
|
|
33
35
|
"configurable-anonymous-pattern": true
|
|
34
36
|
};
|
|
35
37
|
|
|
@@ -180,6 +182,10 @@ export class Grammar {
|
|
|
180
182
|
this._saveAlias(n);
|
|
181
183
|
break;
|
|
182
184
|
}
|
|
185
|
+
case "take-until-literal": {
|
|
186
|
+
this._saveTakeUntil(n);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
183
189
|
case "configurable-anonymous-pattern": {
|
|
184
190
|
this._saveConfigurableAnonymous(n);
|
|
185
191
|
break;
|
|
@@ -196,6 +202,7 @@ export class Grammar {
|
|
|
196
202
|
});
|
|
197
203
|
}
|
|
198
204
|
|
|
205
|
+
|
|
199
206
|
private _saveLiteral(statementNode: Node) {
|
|
200
207
|
const nameNode = statementNode.find(n => n.name === "name") as Node;
|
|
201
208
|
const literalNode = statementNode.find(n => n.name === "literal") as Node;
|
|
@@ -322,6 +329,9 @@ export class Grammar {
|
|
|
322
329
|
case "sequence-literal": {
|
|
323
330
|
return this._buildSequence(name, node);
|
|
324
331
|
}
|
|
332
|
+
case "take-until-literal": {
|
|
333
|
+
return this._buildTakeUntil(name, node);
|
|
334
|
+
}
|
|
325
335
|
case "complex-anonymous-pattern": {
|
|
326
336
|
return this._buildComplexAnonymousPattern(node);
|
|
327
337
|
}
|
|
@@ -422,6 +432,24 @@ export class Grammar {
|
|
|
422
432
|
return isOptional ? new Optional(name, new Repeat(`inner-optional-${name}`, pattern, options)) : new Repeat(name, pattern, options);
|
|
423
433
|
}
|
|
424
434
|
|
|
435
|
+
private _saveTakeUntil(statementNode: Node) {
|
|
436
|
+
const nameNode = statementNode.find(n => n.name === "name") as Node;
|
|
437
|
+
const name = nameNode.value;
|
|
438
|
+
const takeUntilNode = statementNode.find(n => n.name === "take-until-literal") as Node;
|
|
439
|
+
const takeUntil = this._buildTakeUntil(name, takeUntilNode);
|
|
440
|
+
|
|
441
|
+
this._applyDecorators(statementNode, takeUntil);
|
|
442
|
+
this._parseContext.patternsByName.set(name, takeUntil);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
private _buildTakeUntil(name: string, takeUntilNode: Node) {
|
|
447
|
+
const patternNode = takeUntilNode.children[takeUntilNode.children.length - 1];
|
|
448
|
+
const untilPattern = this._buildPattern(patternNode);
|
|
449
|
+
|
|
450
|
+
return new TakeUntil(name, untilPattern);
|
|
451
|
+
}
|
|
452
|
+
|
|
425
453
|
private _saveConfigurableAnonymous(node: Node) {
|
|
426
454
|
const nameNode = node.find(n => n.name === "name") as Node;
|
|
427
455
|
const name = nameNode.value;
|
|
@@ -441,7 +469,7 @@ export class Grammar {
|
|
|
441
469
|
|
|
442
470
|
private async _resolveImports(ast: Node) {
|
|
443
471
|
const importStatements = ast.findAll(n => {
|
|
444
|
-
return n.name === "import-from" || n.name === "param-name-with-default-value"
|
|
472
|
+
return n.name === "import-from" || n.name === "param-name-with-default-value";
|
|
445
473
|
});
|
|
446
474
|
|
|
447
475
|
for (const statement of importStatements) {
|
|
@@ -14,6 +14,7 @@ export const anonymousLiterals = new Options("anonymous-literals", [
|
|
|
14
14
|
]);
|
|
15
15
|
|
|
16
16
|
export const anonymousWrappedLiterals = new Options("anonymous-wrapped-literals", [
|
|
17
|
+
new Reference("take-until-literal"),
|
|
17
18
|
new Reference("options-literal"),
|
|
18
19
|
new Reference("sequence-literal"),
|
|
19
20
|
new Reference("complex-anonymous-pattern")
|
|
@@ -5,6 +5,7 @@ import { repeatLiteral } from "./repeatLiteral";
|
|
|
5
5
|
import { sequenceLiteral } from "./sequenceLiteral";
|
|
6
6
|
import { optionsLiteral } from "./optionsLiteral";
|
|
7
7
|
import { anonymousPattern } from "./anonymousPattern";
|
|
8
|
+
import { takeUntilLiteral } from "./takeUtilLiteral";
|
|
8
9
|
import { Sequence } from "../../patterns/Sequence";
|
|
9
10
|
import { Literal } from "../../patterns/Literal";
|
|
10
11
|
import { name } from "./name";
|
|
@@ -20,8 +21,9 @@ export const pattern = new Options("pattern", [
|
|
|
20
21
|
literal,
|
|
21
22
|
regexLiteral,
|
|
22
23
|
repeatLiteral,
|
|
24
|
+
takeUntilLiteral,
|
|
23
25
|
aliasLiteral,
|
|
24
26
|
optionsLiteral,
|
|
25
27
|
sequenceLiteral,
|
|
26
28
|
configurableAnonymousPattern,
|
|
27
|
-
],
|
|
29
|
+
], true);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Literal } from "../../patterns/Literal"
|
|
2
|
+
import { Optional } from "../../patterns/Optional";
|
|
3
|
+
import { Sequence } from "../../patterns/Sequence";
|
|
4
|
+
import { lineSpaces } from "./spaces";
|
|
5
|
+
import { name } from "./name";
|
|
6
|
+
import { Reference } from "../../patterns/Reference";
|
|
7
|
+
|
|
8
|
+
const anyChar = new Literal("any-char", "?");
|
|
9
|
+
const upTo = new Literal("up-to", "->");
|
|
10
|
+
const wall = new Literal("wall", "|");
|
|
11
|
+
const optionalLineSpaces = new Optional("optional-line-spaces", lineSpaces);
|
|
12
|
+
|
|
13
|
+
export const takeUntilLiteral = new Sequence("take-until-literal", [
|
|
14
|
+
anyChar,
|
|
15
|
+
optionalLineSpaces,
|
|
16
|
+
upTo,
|
|
17
|
+
optionalLineSpaces,
|
|
18
|
+
wall,
|
|
19
|
+
optionalLineSpaces,
|
|
20
|
+
new Reference("pattern")
|
|
21
|
+
]);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Cursor } from "./Cursor";
|
|
2
|
+
import { Literal } from "./Literal";
|
|
3
|
+
import { Options } from "./Options";
|
|
4
|
+
import { TakeUntil } from "./TakeUntil";
|
|
5
|
+
|
|
6
|
+
describe("TakeUntil", () => {
|
|
7
|
+
test("Take With No End", () => {
|
|
8
|
+
const takeUntilScript = new TakeUntil(
|
|
9
|
+
"script-content",
|
|
10
|
+
new Literal("close-script-tag", "</script")
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const result = takeUntilScript.exec("function(){}");
|
|
14
|
+
|
|
15
|
+
expect(result.ast?.value).toBe("function(){}");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("Take Until Terminating Match", () => {
|
|
19
|
+
const takeUntilScript = new TakeUntil(
|
|
20
|
+
"script-content",
|
|
21
|
+
new Literal("close-script-tag", "</script")
|
|
22
|
+
);
|
|
23
|
+
const cursor = new Cursor("function(){}function(){}</script");
|
|
24
|
+
const result = takeUntilScript.parse(cursor);
|
|
25
|
+
|
|
26
|
+
expect(result?.value).toBe("function(){}function(){}");
|
|
27
|
+
expect(cursor.index).toBe(23);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("Take Until Terminating Complex Match", () => {
|
|
31
|
+
const takeUntilScript = new TakeUntil(
|
|
32
|
+
"script-content",
|
|
33
|
+
new Options("end-tags", [
|
|
34
|
+
new Literal("close-script-tag", "</script"),
|
|
35
|
+
new Literal("close-style-tag", "</style")
|
|
36
|
+
])
|
|
37
|
+
);
|
|
38
|
+
let cursor = new Cursor("function(){}function(){}</script");
|
|
39
|
+
let result = takeUntilScript.parse(cursor);
|
|
40
|
+
|
|
41
|
+
expect(result?.value).toBe("function(){}function(){}");
|
|
42
|
+
expect(cursor.index).toBe(23);
|
|
43
|
+
|
|
44
|
+
cursor = new Cursor("function(){}function(){}</style");
|
|
45
|
+
result = takeUntilScript.parse(cursor);
|
|
46
|
+
|
|
47
|
+
expect(result?.value).toBe("function(){}function(){}");
|
|
48
|
+
expect(cursor.index).toBe(23);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("Error", () => {
|
|
52
|
+
const takeUntilScript = new TakeUntil(
|
|
53
|
+
"script-content",
|
|
54
|
+
new Literal("close-script-tag", "</script")
|
|
55
|
+
);
|
|
56
|
+
const cursor = new Cursor("</script");
|
|
57
|
+
const result = takeUntilScript.parse(cursor);
|
|
58
|
+
|
|
59
|
+
expect(result).toBeNull();
|
|
60
|
+
expect(cursor.index).toBe(0);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { Node } from "../ast/Node";
|
|
2
|
+
import { Cursor } from "./Cursor";
|
|
3
|
+
import { execPattern } from "./execPattern";
|
|
4
|
+
import { ParseResult } from "./ParseResult";
|
|
5
|
+
import { Pattern } from "./Pattern";
|
|
6
|
+
import { testPattern } from "./testPattern";
|
|
7
|
+
|
|
8
|
+
let idIndex = 0;
|
|
9
|
+
|
|
10
|
+
export class TakeUntil implements Pattern {
|
|
11
|
+
private _id: string;
|
|
12
|
+
private _type: string;
|
|
13
|
+
private _name: string;
|
|
14
|
+
private _parent: Pattern | null;
|
|
15
|
+
private _children: Pattern[];
|
|
16
|
+
private _startedOnIndex: number;
|
|
17
|
+
private _terminatingPattern: Pattern;
|
|
18
|
+
private _tokens: string[];
|
|
19
|
+
|
|
20
|
+
get id(): string {
|
|
21
|
+
return this._id;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get type(): string {
|
|
25
|
+
return this._type;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get name(): string {
|
|
29
|
+
return this._name;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get children(): Pattern[] {
|
|
33
|
+
return this._children;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get parent(): Pattern | null {
|
|
37
|
+
return this._parent;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
set parent(pattern: Pattern | null) {
|
|
41
|
+
this._parent = pattern;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get startedOnIndex(): number {
|
|
45
|
+
return this._startedOnIndex;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
constructor(name: string, terminatingPattern: Pattern) {
|
|
49
|
+
this._id = String(idIndex++);
|
|
50
|
+
this._type = "take-until";
|
|
51
|
+
this._name = name;
|
|
52
|
+
this._parent = null;
|
|
53
|
+
this._terminatingPattern = terminatingPattern;
|
|
54
|
+
this._children = [this._terminatingPattern];
|
|
55
|
+
this._tokens = [];
|
|
56
|
+
this._startedOnIndex = 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
parse(cursor: Cursor): Node | null {
|
|
60
|
+
let cursorIndex = cursor.index;
|
|
61
|
+
let foundMatch = false;
|
|
62
|
+
this._startedOnIndex = cursor.index;
|
|
63
|
+
|
|
64
|
+
let terminatingResult = this._terminatingPattern.parse(cursor);
|
|
65
|
+
|
|
66
|
+
if (terminatingResult == null) {
|
|
67
|
+
foundMatch = true;
|
|
68
|
+
|
|
69
|
+
cursor.moveTo(cursorIndex);
|
|
70
|
+
cursorIndex += 1;
|
|
71
|
+
cursor.hasNext() && cursor.next();
|
|
72
|
+
|
|
73
|
+
cursor.resolveError();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
while (true) {
|
|
77
|
+
terminatingResult = this._terminatingPattern.parse(cursor);
|
|
78
|
+
|
|
79
|
+
if (terminatingResult == null) {
|
|
80
|
+
cursor.moveTo(cursorIndex);
|
|
81
|
+
cursorIndex += 1;
|
|
82
|
+
|
|
83
|
+
if (cursor.hasNext()) {
|
|
84
|
+
cursor.next();
|
|
85
|
+
} else {
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
cursor.resolveError();
|
|
90
|
+
} else {
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (foundMatch) {
|
|
96
|
+
cursor.moveTo(cursorIndex - 1);
|
|
97
|
+
const value = cursor.getChars(this.startedOnIndex, cursorIndex - 1);
|
|
98
|
+
return Node.createValueNode(this._type, this._name, value);
|
|
99
|
+
} else {
|
|
100
|
+
cursor.resolveError();
|
|
101
|
+
cursor.moveTo(this.startedOnIndex);
|
|
102
|
+
cursor.recordErrorAt(this._startedOnIndex, this._startedOnIndex, this);
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
exec(text: string, record?: boolean | undefined): ParseResult {
|
|
108
|
+
return execPattern(this, text, record);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
test(text: string, record?: boolean | undefined): boolean {
|
|
112
|
+
return testPattern(this, text, record);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
clone(name = this.name): Pattern {
|
|
116
|
+
const clone = new TakeUntil(name, this._terminatingPattern);
|
|
117
|
+
clone._id = this._id;
|
|
118
|
+
|
|
119
|
+
return clone;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
getTokens() {
|
|
123
|
+
return this._tokens;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
getTokensAfter(_childReference: Pattern): string[] {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
getNextTokens(): string[] {
|
|
131
|
+
if (this.parent == null) {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return this.parent.getTokensAfter(this);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
getPatterns(): Pattern[] {
|
|
139
|
+
return [this];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
getPatternsAfter(_childReference: Pattern): Pattern[] {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
getNextPatterns(): Pattern[] {
|
|
147
|
+
if (this.parent == null) {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return this.parent.getPatternsAfter(this);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
find(_predicate: (p: Pattern) => boolean): Pattern | null {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
setTokens(tokens: string[]) {
|
|
159
|
+
this._tokens = tokens;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
isEqual(pattern: Pattern): boolean {
|
|
163
|
+
return pattern.type === this.type && this.children.every((c, index) => c.isEqual(pattern.children[index]));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
}
|