clarity-pattern-parser 8.4.9 → 8.4.10

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.
@@ -11,38 +11,41 @@ import { Repeat, RepeatOptions } from "../patterns/Repeat";
11
11
  import { AutoComplete } from "../intellisense/AutoComplete";
12
12
 
13
13
  class ParseContext {
14
+ constructor(params: Pattern[]) {
15
+ params.forEach(p => this.paramsByName.set(p.name, p));
16
+ }
14
17
  patternsByName = new Map<string, Pattern>();
15
18
  importedPatternsByName = new Map<string, Pattern>();
19
+ paramsByName = new Map<string, Pattern>();
16
20
  }
17
21
 
18
22
  function defaultImportResolver(_path: string, _basePath: string | null): Promise<GrammarFile> {
19
23
  throw new Error("No import resolver supplied.");
20
24
  }
21
25
 
22
- export interface GrammarMeta {
23
- originPath?: string;
24
- }
25
-
26
26
  export interface GrammarFile {
27
- path: string;
27
+ resource: string;
28
28
  expression: string;
29
29
  }
30
30
 
31
31
  export interface GrammarOptions {
32
- resolveImport?: (path: string, originPath: string | null) => Promise<GrammarFile>;
33
- meta?: GrammarMeta | null;
32
+ resolveImport?: (resource: string, originResource: string | null) => Promise<GrammarFile>;
33
+ originResource?: string | null;
34
+ params?: Pattern[];
34
35
  }
35
36
 
36
37
  export class Grammar {
37
- private _meta: GrammarMeta | null;
38
- private _resolveImport: (path: string, basePath: string | null) => Promise<GrammarFile>;
38
+ private _params: Pattern[];
39
+ private _originResource?: string | null;
40
+ private _resolveImport: (resource: string, originResource: string | null) => Promise<GrammarFile>;
39
41
  private _parseContext: ParseContext;
40
42
  private _autoComplete: AutoComplete;
41
43
 
42
44
  constructor(options: GrammarOptions = {}) {
43
- this._meta = options.meta == null ? null : options.meta;
45
+ this._params = options?.params == null ? [] : options.params;
46
+ this._originResource = options?.originResource == null ? null : options.originResource;
44
47
  this._resolveImport = options.resolveImport == null ? defaultImportResolver : options.resolveImport;
45
- this._parseContext = new ParseContext();
48
+ this._parseContext = new ParseContext(this._params);
46
49
  this._autoComplete = new AutoComplete(grammar, {
47
50
  greedyPatternNames: ["spaces", "optional-spaces", "whitespace", "new-line"],
48
51
  customTokens: {
@@ -56,13 +59,17 @@ export class Grammar {
56
59
 
57
60
  async import(path: string) {
58
61
  const grammarFile = await this._resolveImport(path, null);
59
- const grammar = new Grammar({ resolveImport: this._resolveImport, meta: { originPath: grammarFile.path } });
62
+ const grammar = new Grammar({
63
+ resolveImport: this._resolveImport,
64
+ originResource: grammarFile.resource,
65
+ params: this._params
66
+ });
60
67
 
61
68
  return grammar.parse(grammarFile.expression);
62
69
  }
63
70
 
64
71
  async parse(expression: string) {
65
- this._parseContext = new ParseContext();
72
+ this._parseContext = new ParseContext(this._params);
66
73
  const ast = this._tryToParse(expression);
67
74
 
68
75
  await this._resolveImports(ast);
@@ -72,7 +79,7 @@ export class Grammar {
72
79
  }
73
80
 
74
81
  parseString(expression: string) {
75
- this._parseContext = new ParseContext();
82
+ this._parseContext = new ParseContext(this._params);
76
83
  const ast = this._tryToParse(expression);
77
84
 
78
85
  if (this._hasImports(ast)) {
@@ -112,7 +119,7 @@ export class Grammar {
112
119
  }
113
120
 
114
121
  private _buildPatterns(ast: Node) {
115
- ast.children.forEach((n) => {
122
+ ast.findAll(n => n.name === "statement").forEach((n) => {
116
123
  const typeNode = n.find(n => n.name.includes("literal"));
117
124
  const type = typeNode?.name || "unknown";
118
125
 
@@ -150,14 +157,18 @@ export class Grammar {
150
157
 
151
158
  private async _resolveImports(ast: Node) {
152
159
  const parseContext = this._parseContext;
153
- const importStatements = ast.findAll(n => n.name === "import-statement");
160
+ const importStatements = ast.findAll(n => n.name === "import-from");
154
161
 
155
162
  for (const importStatement of importStatements) {
156
- const urlNode = importStatement.find(n => n.name === "url") as Node;
157
-
158
- const url = urlNode.value.slice(1, -1);
159
- const grammarFile = await this._resolveImport(url, this._meta?.originPath || null);
160
- const grammar = new Grammar({ resolveImport: this._resolveImport, meta: { originPath: grammarFile.path } });
163
+ const resourceNode = importStatement.find(n => n.name === "resource") as Node;
164
+ const params = this._getParams(importStatement);
165
+ const resource = resourceNode.value.slice(1, -1);
166
+ const grammarFile = await this._resolveImport(resource, this._originResource || null);
167
+ const grammar = new Grammar({
168
+ resolveImport: this._resolveImport,
169
+ originResource: grammarFile.resource,
170
+ params
171
+ });
161
172
 
162
173
  try {
163
174
  const patterns = await grammar.parse(grammarFile.expression);
@@ -170,19 +181,48 @@ export class Grammar {
170
181
 
171
182
  const pattern = patterns.get(importName);
172
183
  if (pattern == null) {
173
- throw new Error(`Couldn't find pattern with name: ${importName}, from import: ${url}.`);
184
+ throw new Error(`Couldn't find pattern with name: ${importName}, from import: ${resource}.`);
174
185
  }
175
186
 
176
187
  parseContext.importedPatternsByName.set(importName, pattern);
177
188
  })
178
189
 
179
190
  } catch (e: any) {
180
- throw new Error(`Failed loading expression from: "${url}". Error details: "${e.message}"`);
191
+ throw new Error(`Failed loading expression from: "${resource}". Error details: "${e.message}"`);
181
192
  }
182
193
 
183
194
  }
184
195
  }
185
196
 
197
+ private _getParams(importStatement: Node) {
198
+ let params: Pattern[] = [];
199
+ const paramsStatement = importStatement.find(n => n.name === "with-params-statement");
200
+
201
+ if (paramsStatement != null) {
202
+ const statements = paramsStatement.find(n => n.name === "body");
203
+
204
+ if (statements != null) {
205
+ const expression = statements.toString();
206
+ const importedValues = Array.from(this
207
+ ._parseContext
208
+ .importedPatternsByName
209
+ .values()
210
+ )
211
+
212
+ const grammar = new Grammar({
213
+ params: importedValues,
214
+ originResource: this._originResource,
215
+ resolveImport: this._resolveImport
216
+ });
217
+
218
+ const patterns = grammar.parseString(expression);
219
+ params = Array.from(patterns.values());
220
+ }
221
+ }
222
+
223
+ return params;
224
+ }
225
+
186
226
  private _buildLiteral(statementNode: Node) {
187
227
  const nameNode = statementNode.find(n => n.name === "name") as Node;
188
228
  const literalNode = statementNode.find(n => n.name === "literal") as Node;
@@ -223,6 +263,10 @@ export class Grammar {
223
263
  pattern = this._parseContext.importedPatternsByName.get(name);
224
264
  }
225
265
 
266
+ if (pattern == null) {
267
+ pattern = this._parseContext.paramsByName.get(name);
268
+ }
269
+
226
270
  if (pattern == null) {
227
271
  return new Reference(name);
228
272
  }
@@ -334,8 +378,8 @@ export class Grammar {
334
378
  return grammar.import(path);
335
379
  }
336
380
 
337
- static parseString(expression: string) {
338
- const grammar = new Grammar();
381
+ static parseString(expression: string, options?: GrammarOptions) {
382
+ const grammar = new Grammar(options);
339
383
  return grammar.parseString(expression);
340
384
  }
341
385
 
@@ -0,0 +1,19 @@
1
+ import { And } from "../../patterns/And";
2
+ import { Or } from "../../patterns/Or";
3
+ import { Repeat } from "../../patterns/Repeat";
4
+ import { comment } from "./comment";
5
+ import { lineSpaces, newLine } from "./spaces";
6
+ import { statement } from "./statement";
7
+
8
+ const bodyLineContent = new Or("body-line-content", [
9
+ comment,
10
+ statement
11
+ ]);
12
+
13
+ const bodyLine = new And("body-line", [
14
+ lineSpaces.clone("line-spaces", true),
15
+ bodyLineContent,
16
+ lineSpaces.clone("line-spaces", true),
17
+ ]);
18
+
19
+ export const body = new Repeat("body", bodyLine, {divider: newLine, min: 0});
@@ -2,21 +2,38 @@ import { Or } from "../../patterns/Or";
2
2
  import { Regex } from "../../patterns/Regex";
3
3
  import { Repeat } from "../../patterns/Repeat";
4
4
  import { comment } from "./comment";
5
- import { statement } from "./statement";
6
5
  import { importStatement } from './import';
6
+ import { And } from "../../patterns/And";
7
+ import { allSpaces } from "./spaces";
8
+ import { body } from "./body";
7
9
 
8
- const whitespace = new Regex("whitespace", "[ \\t]+((\\r?\\n)+)?");
10
+ const tabs = new Regex("tabs", "\\t+");
11
+ const spaces = new Regex("spaces", "[ ]+");
9
12
  const newLine = new Regex("new-line", "(\\r?\\n)+");
10
13
 
11
- whitespace.setTokens([" "]);
14
+ spaces.setTokens([" "]);
15
+ tabs.setTokens(["\t"]);
12
16
  newLine.setTokens(["\n"]);
13
17
 
14
- const line = new Or("line", [
15
- newLine,
16
- whitespace,
18
+ const lineSpaces = new Repeat("line-spaces", new Or("line-space", [tabs, spaces]));
19
+
20
+ const headLineContent = new Or("head-line-content", [
17
21
  comment,
18
- importStatement,
19
- statement
22
+ importStatement
23
+ ]);
24
+
25
+ const headLine = new And("head-line-content", [
26
+ lineSpaces.clone("line-spaces", true),
27
+ headLineContent,
28
+ lineSpaces.clone("line-spaces", true),
20
29
  ]);
21
30
 
22
- export const grammar = new Repeat("grammer", line);
31
+ const head = new Repeat("head", headLine, { divider: newLine, min: 0 });
32
+
33
+ export const grammar = new And("grammar", [
34
+ allSpaces,
35
+ head,
36
+ allSpaces,
37
+ body,
38
+ allSpaces
39
+ ]);
@@ -3,27 +3,64 @@ import { Repeat } from "../../patterns/Repeat";
3
3
  import { Literal } from "../../patterns/Literal";
4
4
  import { Regex } from "../../patterns/Regex";
5
5
  import { literal } from "./literal";
6
+ import { Or } from "../../patterns/Or";
7
+ import { body } from "./body";
8
+ import { allSpaces, lineSpaces } from "./spaces";
6
9
 
7
- const spaces = new Regex("spaces", "\\s+", true);
8
10
  const importNameDivider = new Regex("import-name-divider", "(\\s+)?,(\\s+)?");
9
11
  const importKeyword = new Literal("import", "import");
12
+ const useParamsKeyword = new Literal("use-params", "use params");
10
13
  const fromKeyword = new Literal("from", "from");
11
14
  const openBracket = new Literal("open-bracket", "{");
12
15
  const closeBracket = new Literal("close-bracket", "}");
13
16
  const name = new Regex("import-name", "[^}\\s,]+");
14
17
  const importedNames = new Repeat("imported-names", name, { divider: importNameDivider });
15
- const optionalSpaces = spaces.clone("optional-spaces", true);
18
+ const paramName = name.clone("param-name");
19
+ const paramNames = new Repeat("param-names", paramName, { divider: importNameDivider });
20
+ const optionalSpaces = allSpaces.clone("optional-spaces", true);
21
+ const optionalLineSpaces = lineSpaces.clone("options-line-spaces", true);
22
+ const resource = literal.clone("resource");
16
23
 
17
- export const importStatement = new And("import-statement", [
18
- importKeyword,
24
+ const useParams = new And("import-params", [
25
+ useParamsKeyword,
26
+ optionalLineSpaces,
27
+ openBracket,
19
28
  optionalSpaces,
29
+ paramNames,
30
+ optionalSpaces,
31
+ closeBracket
32
+ ]);
33
+
34
+ const withParamsKeyword = new Literal("with-params", "with params");
35
+ const withParamsStatement = new And("with-params-statement", [
36
+ withParamsKeyword,
37
+ optionalLineSpaces,
38
+ openBracket,
39
+ optionalSpaces,
40
+ body,
41
+ optionalSpaces,
42
+ closeBracket
43
+ ], true);
44
+
45
+ const importFromStatement = new And("import-from", [
46
+ importKeyword,
47
+ optionalLineSpaces,
20
48
  openBracket,
21
49
  optionalSpaces,
22
50
  importedNames,
23
51
  optionalSpaces,
24
52
  closeBracket,
25
- optionalSpaces,
53
+ optionalLineSpaces,
26
54
  fromKeyword,
27
- spaces,
28
- literal.clone("url"),
55
+ optionalLineSpaces,
56
+ resource,
57
+ optionalLineSpaces,
58
+ withParamsStatement
29
59
  ]);
60
+
61
+ export const importStatement = new Or("import-statement", [
62
+ useParams,
63
+ importFromStatement
64
+ ]);
65
+
66
+
@@ -1,4 +1,14 @@
1
+ import { Or } from "../../patterns/Or";
1
2
  import { Regex } from "../../patterns/Regex";
3
+ import { Repeat } from "../../patterns/Repeat";
2
4
 
3
- export const spaces = new Regex("spaces", "[ \\t]+");
4
- spaces.setTokens([" "]);
5
+ export const tabs = new Regex("tabs", "\\t+");
6
+ export const spaces = new Regex("spaces", "[ ]+");
7
+ export const newLine = new Regex("new-line", "(\\r?\\n)+");
8
+
9
+ spaces.setTokens([" "]);
10
+ tabs.setTokens(["\t"]);
11
+ newLine.setTokens(["\n"]);
12
+
13
+ export const lineSpaces = new Repeat("line-spaces", new Or("line-space", [tabs, spaces]));
14
+ export const allSpaces = new Regex("all-spaces", "\\s+", true);
@@ -8,11 +8,9 @@ import { regexLiteral } from "./regexLiteral";
8
8
  import { repeatLiteral } from "./repeatLiteral";
9
9
  import { spaces } from "./spaces";
10
10
  import { literal } from "./literal";
11
- import { comment } from "./comment";
12
11
 
13
12
  const optionalSpaces = spaces.clone("optional-spaces", true);
14
13
  const assignOperator = new Literal("assign-operator", "=");
15
- const optionalComment = comment.clone("inline-comment", true);
16
14
 
17
15
  const statements = new Or("statements", [
18
16
  literal,
@@ -29,8 +27,5 @@ export const statement = new And("statement", [
29
27
  optionalSpaces,
30
28
  assignOperator,
31
29
  optionalSpaces,
32
- statements,
33
- optionalSpaces,
34
- optionalComment,
35
- optionalSpaces,
30
+ statements
36
31
  ]);
@@ -164,3 +164,27 @@ import { last-name } from "https://some.cdn.com/some/last-name.cpat"
164
164
 
165
165
  full-name = first-name & spaces & last-name
166
166
  ```
167
+
168
+ ### Import Params
169
+ This allows you to inject patterns within another pattern.
170
+ ```ts
171
+ const firstName = new Literal("first-name", "Jared");
172
+ const grammar = Grammar.import('file.cpat', {params: [firstName, LastName]})
173
+ ```
174
+ file.cpat
175
+ ```
176
+ import params { first-names, last-names }
177
+
178
+ full-name = first-names & spaces & last-names
179
+ ```
180
+
181
+ ### Imports with Params
182
+ ```
183
+ import params { other-pattern }
184
+ import { first-names, last-names } from "some-file.cpat" with params {
185
+ some-pattern = "Some Pattern"
186
+ some-pattern2 = other-pattern
187
+ }
188
+
189
+ full-name = first-names & spaces & last-names
190
+ ```