clarity-pattern-parser 11.1.4 → 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/dist/grammar/Grammar.d.ts +2 -0
- package/dist/index.browser.js +98 -49
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +98 -49
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +98 -49
- package/dist/index.js.map +1 -1
- package/dist/patterns/Context.d.ts +1 -0
- package/package.json +1 -1
- package/src/grammar/Grammar.test.ts +82 -0
- package/src/grammar/Grammar.ts +113 -48
- package/src/grammar/patterns/import.ts +13 -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/grammar/patterns.test.ts +6 -6
- package/src/grammar/patterns.ts +1 -1
- package/src/patterns/Context.ts +7 -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", () => {
|
|
@@ -496,11 +497,83 @@ describe("Grammar", () => {
|
|
|
496
497
|
}
|
|
497
498
|
const patterns = await Grammar.parse(expression, { resolveImport });
|
|
498
499
|
const pattern = patterns["name"] as Literal;
|
|
500
|
+
const result = pattern.exec("Value");
|
|
501
|
+
expect(result.ast?.value).toBe("Value");
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
test("Default Params Resolves to Default Value", async () => {
|
|
505
|
+
const expression = `
|
|
506
|
+
use params {
|
|
507
|
+
value = default-value
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
default-value = "DefaultValue"
|
|
511
|
+
alias = value
|
|
512
|
+
`;
|
|
513
|
+
|
|
514
|
+
function resolveImport(_: string) {
|
|
515
|
+
return Promise.reject(new Error("No Import"));
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const patterns = await Grammar.parse(expression, { resolveImport });
|
|
519
|
+
const pattern = patterns["alias"] as Literal;
|
|
520
|
+
|
|
521
|
+
const result = pattern.exec("DefaultValue");
|
|
522
|
+
expect(result.ast?.value).toBe("DefaultValue");
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
test("Default Params Resolves to params imported", async () => {
|
|
526
|
+
const expression = `
|
|
527
|
+
use params {
|
|
528
|
+
value = default-value
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
default-value = "DefaultValue"
|
|
532
|
+
alias = value
|
|
533
|
+
`;
|
|
534
|
+
|
|
535
|
+
function resolveImport(_: string) {
|
|
536
|
+
return Promise.reject(new Error("No Import"));
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const patterns = await Grammar.parse(expression, { resolveImport, params: [new Literal("value", "Value")] });
|
|
540
|
+
const pattern = patterns["alias"] as Literal;
|
|
499
541
|
|
|
500
542
|
const result = pattern.exec("Value");
|
|
501
543
|
expect(result.ast?.value).toBe("Value");
|
|
502
544
|
});
|
|
503
545
|
|
|
546
|
+
test("Default Params Resolves to imported default value", async () => {
|
|
547
|
+
const expression = `
|
|
548
|
+
import { my-value as default-value } from "resource1"
|
|
549
|
+
use params {
|
|
550
|
+
value = default-value
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
default-value = "DefaultValue"
|
|
554
|
+
alias = value
|
|
555
|
+
`;
|
|
556
|
+
|
|
557
|
+
const resource1 = `
|
|
558
|
+
my-value = "MyValue"
|
|
559
|
+
`;
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
const pathMap: Record<string, string> = {
|
|
563
|
+
"resource1": resource1,
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
function resolveImport(resource: string) {
|
|
567
|
+
return Promise.resolve({ expression: pathMap[resource], resource });
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const patterns = await Grammar.parse(expression, { resolveImport });
|
|
571
|
+
const pattern = patterns["alias"] as Literal;
|
|
572
|
+
|
|
573
|
+
const result = pattern.exec("MyValue");
|
|
574
|
+
expect(result.ast?.value).toBe("MyValue");
|
|
575
|
+
});
|
|
576
|
+
|
|
504
577
|
test("Anonymous Patterns", () => {
|
|
505
578
|
const expression = `
|
|
506
579
|
complex-expression = !"NOT_THIS" + "Text"? + /regex/ + ("Text" <|> /regex/ <|> (pattern)+) + (pattern | pattern)
|
|
@@ -696,4 +769,13 @@ describe("Grammar", () => {
|
|
|
696
769
|
`;
|
|
697
770
|
});
|
|
698
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
|
+
|
|
699
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;
|
|
@@ -440,69 +468,106 @@ export class Grammar {
|
|
|
440
468
|
}
|
|
441
469
|
|
|
442
470
|
private async _resolveImports(ast: Node) {
|
|
471
|
+
const importStatements = ast.findAll(n => {
|
|
472
|
+
return n.name === "import-from" || n.name === "param-name-with-default-value";
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
for (const statement of importStatements) {
|
|
476
|
+
if (statement.name === "import-from") {
|
|
477
|
+
await this.processImport(statement);
|
|
478
|
+
} else {
|
|
479
|
+
this.processUseParams(statement);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
private async processImport(importStatement: Node) {
|
|
443
485
|
const parseContext = this._parseContext;
|
|
444
|
-
const
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
params,
|
|
455
|
-
decorators: this._parseContext.decorators
|
|
456
|
-
});
|
|
486
|
+
const resourceNode = importStatement.find(n => n.name === "resource") as Node;
|
|
487
|
+
const params = this._getParams(importStatement);
|
|
488
|
+
const resource = resourceNode.value.slice(1, -1);
|
|
489
|
+
const grammarFile = await this._resolveImport(resource, this._originResource || null);
|
|
490
|
+
const grammar = new Grammar({
|
|
491
|
+
resolveImport: this._resolveImport,
|
|
492
|
+
originResource: grammarFile.resource,
|
|
493
|
+
params,
|
|
494
|
+
decorators: this._parseContext.decorators
|
|
495
|
+
});
|
|
457
496
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
497
|
+
try {
|
|
498
|
+
const patterns = await grammar.parse(grammarFile.expression);
|
|
499
|
+
const importStatements = importStatement.findAll(n => n.name === "import-name" || n.name === "import-alias");
|
|
500
|
+
|
|
501
|
+
importStatements.forEach((node) => {
|
|
502
|
+
if (node.name === "import-name" && node.parent?.name === "import-alias") {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (node.name === "import-name" && node.parent?.name !== "import-alias") {
|
|
507
|
+
const importName = node.value;
|
|
461
508
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
return;
|
|
509
|
+
if (parseContext.importedPatternsByName.has(importName)) {
|
|
510
|
+
throw new Error(`'${importName}' was already used within another import.`);
|
|
465
511
|
}
|
|
466
512
|
|
|
467
|
-
|
|
468
|
-
|
|
513
|
+
const pattern = patterns[importName];
|
|
514
|
+
if (pattern == null) {
|
|
515
|
+
throw new Error(`Couldn't find pattern with name: ${importName}, from import: ${resource}.`);
|
|
516
|
+
}
|
|
469
517
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
518
|
+
parseContext.importedPatternsByName.set(importName, pattern);
|
|
519
|
+
} else {
|
|
520
|
+
const importNameNode = node.find(n => n.name === "import-name") as Node;
|
|
521
|
+
const importName = importNameNode.value;
|
|
522
|
+
const aliasNode = node.find(n => n.name === "import-name-alias") as Node;
|
|
523
|
+
const alias = aliasNode.value;
|
|
473
524
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
}
|
|
525
|
+
if (parseContext.importedPatternsByName.has(alias)) {
|
|
526
|
+
throw new Error(`'${alias}' was already used within another import.`);
|
|
527
|
+
}
|
|
478
528
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
const aliasNode = node.find(n => n.name === "import-name-alias") as Node;
|
|
484
|
-
const alias = aliasNode.value;
|
|
529
|
+
const pattern = patterns[importName];
|
|
530
|
+
if (pattern == null) {
|
|
531
|
+
throw new Error(`Couldn't find pattern with name: ${importName}, from import: ${resource}.`);
|
|
532
|
+
}
|
|
485
533
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
534
|
+
parseContext.importedPatternsByName.set(alias, pattern.clone(alias));
|
|
535
|
+
}
|
|
536
|
+
});
|
|
489
537
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
}
|
|
538
|
+
} catch (e: any) {
|
|
539
|
+
throw new Error(`Failed loading expression from: "${resource}". Error details: "${e.message}"`);
|
|
540
|
+
}
|
|
494
541
|
|
|
495
|
-
|
|
496
|
-
}
|
|
497
|
-
});
|
|
542
|
+
}
|
|
498
543
|
|
|
544
|
+
private processUseParams(paramName: Node) {
|
|
545
|
+
const defaultValueNode = paramName.find(n => n.name === "param-default");
|
|
546
|
+
if (defaultValueNode === null) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
499
549
|
|
|
550
|
+
const nameNode = paramName.find(n => n.name === "param-name");
|
|
551
|
+
const defaultNameNode = defaultValueNode.find(n => n.name === "default-param-name");
|
|
500
552
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
553
|
+
if (nameNode == null || defaultNameNode == null) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const name = nameNode.value;
|
|
558
|
+
const defaultName = defaultNameNode.value;
|
|
504
559
|
|
|
560
|
+
if (this._parseContext.paramsByName.has(name)) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
let pattern = this._parseContext.importedPatternsByName.get(defaultName);
|
|
565
|
+
|
|
566
|
+
if (pattern == null) {
|
|
567
|
+
pattern = new Reference(defaultName);
|
|
505
568
|
}
|
|
569
|
+
|
|
570
|
+
this._parseContext.importedPatternsByName.set(name, pattern);
|
|
506
571
|
}
|
|
507
572
|
|
|
508
573
|
private _applyDecorators(statementNode: Node, pattern: Pattern) {
|
|
@@ -615,7 +680,7 @@ export class Grammar {
|
|
|
615
680
|
|
|
616
681
|
// This solves the problem for an alias pointing to a reference.
|
|
617
682
|
if (aliasPattern.type === "reference") {
|
|
618
|
-
const reference =
|
|
683
|
+
const reference = aliasPattern.clone(name);
|
|
619
684
|
this._applyDecorators(statementNode, reference);
|
|
620
685
|
this._parseContext.patternsByName.set(name, reference);
|
|
621
686
|
} else {
|
|
@@ -23,12 +23,24 @@ const asKeyword = new Literal("as", "as");
|
|
|
23
23
|
const fromKeyword = new Literal("from", "from");
|
|
24
24
|
const openBracket = new Literal("open-bracket", "{");
|
|
25
25
|
const closeBracket = new Literal("close-bracket", "}");
|
|
26
|
+
const equal = new Literal("equal", "=");
|
|
26
27
|
|
|
27
28
|
const importNameAlias = name.clone("import-name-alias");
|
|
28
29
|
const importAlias = new Sequence("import-alias", [name, lineSpaces, asKeyword, lineSpaces, importNameAlias]);
|
|
29
30
|
const importedNames = new Repeat("imported-names", new Options("import-names", [importAlias, name]), { divider: importNameDivider });
|
|
30
31
|
const paramName = name.clone("param-name");
|
|
31
|
-
const
|
|
32
|
+
const defaultParamName = name.clone("default-param-name");
|
|
33
|
+
|
|
34
|
+
const paramNameWithDefault = new Sequence("param-name-with-default-value", [
|
|
35
|
+
paramName,
|
|
36
|
+
new Optional("optional-param-default", new Sequence("param-default", [
|
|
37
|
+
optionalLineSpaces,
|
|
38
|
+
equal,
|
|
39
|
+
optionalLineSpaces,
|
|
40
|
+
defaultParamName,
|
|
41
|
+
])),
|
|
42
|
+
]);
|
|
43
|
+
const paramNames = new Repeat("param-names", paramNameWithDefault, { divider: importNameDivider });
|
|
32
44
|
const resource = literal.clone("resource");
|
|
33
45
|
|
|
34
46
|
const useParams = new Sequence("import-params", [
|
|
@@ -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
|
+
]);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { patterns } from "./patterns";
|
|
2
2
|
|
|
3
|
-
describe("Patterns String Template Literal", ()=>{
|
|
4
|
-
test("Baseline", ()=>{
|
|
5
|
-
const {fullName} = patterns`
|
|
3
|
+
describe("Patterns String Template Literal", () => {
|
|
4
|
+
test("Baseline", () => {
|
|
5
|
+
const { fullName } = patterns`
|
|
6
6
|
first-name = "John"
|
|
7
7
|
last-name = "Doe"
|
|
8
8
|
space = /\\s+/
|
|
@@ -13,8 +13,8 @@ describe("Patterns String Template Literal", ()=>{
|
|
|
13
13
|
expect(result?.ast?.value).toBe("John Doe");
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
-
test("Simple Markup", ()=>{
|
|
17
|
-
const {body} = patterns`
|
|
16
|
+
test("Simple Markup", () => {
|
|
17
|
+
const { body } = patterns`
|
|
18
18
|
tag-name = /[a-zA-Z_-]+[a-zA-Z0-9_-]*/
|
|
19
19
|
space = /\\s+/
|
|
20
20
|
opening-tag = "<" + tag-name + space? + ">"
|
|
@@ -32,7 +32,7 @@ describe("Patterns String Template Literal", ()=>{
|
|
|
32
32
|
<div></div>
|
|
33
33
|
</div>
|
|
34
34
|
`, true);
|
|
35
|
-
result && result.ast && result.ast.findAll(n=>n.name.includes("space")).forEach(n=>n.remove());
|
|
35
|
+
result && result.ast && result.ast.findAll(n => n.name.includes("space")).forEach(n => n.remove());
|
|
36
36
|
expect(result?.ast?.value).toBe("<div><div></div><div></div></div>");
|
|
37
37
|
});
|
|
38
38
|
});
|
package/src/grammar/patterns.ts
CHANGED
package/src/patterns/Context.ts
CHANGED
|
@@ -9,6 +9,7 @@ export class Context implements Pattern {
|
|
|
9
9
|
private _id: string;
|
|
10
10
|
private _type: string;
|
|
11
11
|
private _name: string;
|
|
12
|
+
private _referencePatternName: string;
|
|
12
13
|
private _parent: Pattern | null;
|
|
13
14
|
private _children: Pattern[];
|
|
14
15
|
private _pattern: Pattern;
|
|
@@ -43,6 +44,10 @@ export class Context implements Pattern {
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
getPatternWithinContext(name: string): Pattern | null {
|
|
47
|
+
if (this._name === name || this._referencePatternName === name){
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
|
|
46
51
|
return this._patterns[name] || null;
|
|
47
52
|
}
|
|
48
53
|
|
|
@@ -56,6 +61,7 @@ export class Context implements Pattern {
|
|
|
56
61
|
this._name = name;
|
|
57
62
|
this._parent = null;
|
|
58
63
|
this._patterns = {};
|
|
64
|
+
this._referencePatternName = name;
|
|
59
65
|
|
|
60
66
|
const clonedPattern = pattern.clone();
|
|
61
67
|
context.forEach(p => this._patterns[p.name] = p);
|
|
@@ -79,6 +85,7 @@ export class Context implements Pattern {
|
|
|
79
85
|
|
|
80
86
|
clone(name = this._name): Pattern {
|
|
81
87
|
const clone = new Context(name, this._pattern.clone(name), Object.values(this._patterns));
|
|
88
|
+
clone._referencePatternName = this._referencePatternName;
|
|
82
89
|
clone._id = this._id;
|
|
83
90
|
return clone;
|
|
84
91
|
}
|
|
@@ -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
|
+
});
|