clarity-pattern-parser 11.3.12 → 11.4.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.
@@ -33,8 +33,8 @@ export declare class Repeat implements Pattern {
33
33
  get options(): InternalRepeatOptions;
34
34
  constructor(name: string, pattern: Pattern, options?: RepeatOptions);
35
35
  parse(cursor: Cursor): Node | null;
36
- exec(text: string): ParseResult;
37
- test(text: string): boolean;
36
+ exec(text: string, record?: boolean): ParseResult;
37
+ test(text: string, record?: boolean): boolean;
38
38
  clone(name?: string): Repeat;
39
39
  getTokens(): string[];
40
40
  getTokensAfter(_childReference: Pattern): string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clarity-pattern-parser",
3
- "version": "11.3.12",
3
+ "version": "11.4.0",
4
4
  "description": "Parsing Library for Typescript and Javascript.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.esm.js",
@@ -779,4 +779,42 @@ describe("Grammar", () => {
779
779
  expect(result?.value).toBe("function(){}");
780
780
  });
781
781
 
782
+ test("Import Sync", () => {
783
+ function resolveImportSync(path: string, importer: string | null) {
784
+ return {
785
+ expression: pathMap[path],
786
+ resource: path,
787
+ }
788
+ }
789
+
790
+ const rootExpression = `
791
+ import { name } from "first-name.cpat"
792
+ full-name = name
793
+ `;
794
+
795
+ const firstNameExpression = `
796
+ import { last-name } from "last-name.cpat"
797
+ first-name = "John"
798
+ name = first-name + " " + last-name
799
+ `;
800
+
801
+ const lastNameExpression = `
802
+ last-name = "Doe"
803
+ `;
804
+
805
+ const pathMap: Record<string, string> = {
806
+ "first-name.cpat": firstNameExpression,
807
+ "last-name.cpat": lastNameExpression,
808
+ };
809
+
810
+ const patterns = Grammar.parseString(rootExpression, {
811
+ resolveImportSync: resolveImportSync,
812
+ originResource: "/root.cpat",
813
+ });
814
+
815
+ expect(patterns["full-name"]).not.toBeNull();
816
+ const result = patterns["full-name"].exec("John Doe");
817
+ expect(result?.ast?.value).toBe("John Doe");
818
+ });
819
+
782
820
  });
@@ -50,6 +50,10 @@ function defaultImportResolver(_path: string, _basePath: string | null): Promise
50
50
  throw new Error("No import resolver supplied.");
51
51
  }
52
52
 
53
+ function defaultImportResolverSync(_path: string, _basePath: string | null): GrammarFile {
54
+ throw new Error("No import resolver supplied.");
55
+ }
56
+
53
57
  export interface GrammarFile {
54
58
  resource: string;
55
59
  expression: string;
@@ -57,6 +61,7 @@ export interface GrammarFile {
57
61
 
58
62
  export interface GrammarOptions {
59
63
  resolveImport?: (resource: string, originResource: string | null) => Promise<GrammarFile>;
64
+ resolveImportSync?: (resource: string, originResource: string | null) => GrammarFile;
60
65
  originResource?: string | null;
61
66
  params?: Pattern[];
62
67
  decorators?: Record<string, Decorator>;
@@ -66,12 +71,14 @@ export class Grammar {
66
71
  private _params: Pattern[];
67
72
  private _originResource?: string | null;
68
73
  private _resolveImport: (resource: string, originResource: string | null) => Promise<GrammarFile>;
74
+ private _resolveImportSync: (resource: string, originResource: string | null) => GrammarFile;
69
75
  private _parseContext: ParseContext;
70
76
 
71
77
  constructor(options: GrammarOptions = {}) {
72
78
  this._params = options?.params == null ? [] : options.params;
73
79
  this._originResource = options?.originResource == null ? null : options.originResource;
74
80
  this._resolveImport = options.resolveImport == null ? defaultImportResolver : options.resolveImport;
81
+ this._resolveImportSync = options.resolveImportSync == null ? defaultImportResolverSync : options.resolveImportSync;
75
82
  this._parseContext = new ParseContext(this._params, options.decorators || {});
76
83
  }
77
84
 
@@ -101,10 +108,7 @@ export class Grammar {
101
108
  this._parseContext = new ParseContext(this._params, this._parseContext.decorators);
102
109
  const ast = this._tryToParse(expression);
103
110
 
104
- if (this._hasImports(ast)) {
105
- throw new Error("Cannot use imports on parseString, use parse instead.");
106
- }
107
-
111
+ this._resolveImportsSync(ast);
108
112
  this._buildPatterns(ast);
109
113
 
110
114
  return this._buildPatternRecord();
@@ -476,14 +480,89 @@ export class Grammar {
476
480
 
477
481
  for (const statement of importStatements) {
478
482
  if (statement.name === "import-from") {
479
- await this.processImport(statement);
483
+ await this._processImport(statement);
480
484
  } else {
481
- this.processUseParams(statement);
485
+ this._processUseParams(statement);
482
486
  }
483
487
  }
484
488
  }
485
489
 
486
- private async processImport(importStatement: Node) {
490
+ private _resolveImportsSync(ast: Node) {
491
+ const importStatements = ast.findAll(n => {
492
+ return n.name === "import-from" || n.name === "param-name-with-default-value";
493
+ });
494
+
495
+ for (const statement of importStatements) {
496
+ if (statement.name === "import-from") {
497
+ this._processImportSync(statement);
498
+ } else {
499
+ this._processUseParams(statement);
500
+ }
501
+ }
502
+ }
503
+
504
+ private _processImportSync(importStatement: Node) {
505
+ const parseContext = this._parseContext;
506
+ const resourceNode = importStatement.find(n => n.name === "resource") as Node;
507
+ const params = this._getParams(importStatement);
508
+ const resource = resourceNode.value.slice(1, -1);
509
+ const grammarFile = this._resolveImportSync(resource, this._originResource || null);
510
+ const grammar = new Grammar({
511
+ resolveImport: this._resolveImport,
512
+ resolveImportSync: this._resolveImportSync,
513
+ originResource: grammarFile.resource,
514
+ params,
515
+ decorators: this._parseContext.decorators
516
+ });
517
+
518
+ try {
519
+ const patterns = grammar.parseString(grammarFile.expression);
520
+ const importStatements = importStatement.findAll(n => n.name === "import-name" || n.name === "import-alias");
521
+
522
+ importStatements.forEach((node) => {
523
+ if (node.name === "import-name" && node.parent?.name === "import-alias") {
524
+ return;
525
+ }
526
+
527
+ if (node.name === "import-name" && node.parent?.name !== "import-alias") {
528
+ const importName = node.value;
529
+
530
+ if (parseContext.importedPatternsByName.has(importName)) {
531
+ throw new Error(`'${importName}' was already used within another import.`);
532
+ }
533
+
534
+ const pattern = patterns[importName];
535
+ if (pattern == null) {
536
+ throw new Error(`Couldn't find pattern with name: ${importName}, from import: ${resource}.`);
537
+ }
538
+
539
+ parseContext.importedPatternsByName.set(importName, pattern);
540
+ } else {
541
+ const importNameNode = node.find(n => n.name === "import-name") as Node;
542
+ const importName = importNameNode.value;
543
+ const aliasNode = node.find(n => n.name === "import-name-alias") as Node;
544
+ const alias = aliasNode.value;
545
+
546
+ if (parseContext.importedPatternsByName.has(alias)) {
547
+ throw new Error(`'${alias}' was already used within another import.`);
548
+ }
549
+
550
+ const pattern = patterns[importName];
551
+ if (pattern == null) {
552
+ throw new Error(`Couldn't find pattern with name: ${importName}, from import: ${resource}.`);
553
+ }
554
+
555
+ parseContext.importedPatternsByName.set(alias, pattern.clone(alias));
556
+ }
557
+ });
558
+
559
+ } catch (e: any) {
560
+ throw new Error(`Failed loading expression from: "${resource}". Error details: "${e.message}"`);
561
+ }
562
+
563
+ }
564
+
565
+ private async _processImport(importStatement: Node) {
487
566
  const parseContext = this._parseContext;
488
567
  const resourceNode = importStatement.find(n => n.name === "resource") as Node;
489
568
  const params = this._getParams(importStatement);
@@ -543,7 +622,7 @@ export class Grammar {
543
622
 
544
623
  }
545
624
 
546
- private processUseParams(paramName: Node) {
625
+ private _processUseParams(paramName: Node) {
547
626
  const defaultValueNode = paramName.find(n => n.name === "param-default");
548
627
  if (defaultValueNode === null) {
549
628
  return;
@@ -96,12 +96,12 @@ export class Repeat implements Pattern {
96
96
  return this._repeatPattern.parse(cursor);
97
97
  }
98
98
 
99
- exec(text: string): ParseResult {
100
- return this._repeatPattern.exec(text);
99
+ exec(text: string, record = false): ParseResult {
100
+ return this._repeatPattern.exec(text, record);
101
101
  }
102
102
 
103
- test(text: string): boolean {
104
- return this._repeatPattern.test(text);
103
+ test(text: string, record = false): boolean {
104
+ return this._repeatPattern.test(text, record);
105
105
  }
106
106
 
107
107
  clone(name = this.name) {