clarity-pattern-parser 8.4.9 → 8.4.11
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/TODO.md +12 -2
- package/dist/grammar/Grammar.d.ts +8 -8
- package/dist/grammar/patterns/body.d.ts +2 -0
- package/dist/grammar/patterns/grammar.d.ts +2 -2
- package/dist/grammar/patterns/import.d.ts +2 -2
- package/dist/grammar/patterns/spaces.d.ts +5 -0
- package/dist/grammar/patterns/statement.d.ts +2 -2
- package/dist/index.browser.js +172 -52
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +172 -52
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +172 -52
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/grammar/Grammar.test.ts +96 -7
- package/src/grammar/Grammar.ts +113 -36
- package/src/grammar/patterns/body.ts +19 -0
- package/src/grammar/patterns/grammar.ts +26 -9
- package/src/grammar/patterns/import.ts +49 -8
- package/src/grammar/patterns/spaces.ts +12 -2
- package/src/grammar/patterns/statement.ts +5 -8
- package/src/grammar/spec.md +24 -0
|
@@ -271,9 +271,9 @@ describe("Grammar", () => {
|
|
|
271
271
|
test("Bad Grammar At Beginning", () => {
|
|
272
272
|
|
|
273
273
|
expect(() => {
|
|
274
|
-
const expression =
|
|
274
|
+
const expression = `//`;
|
|
275
275
|
Grammar.parseString(expression);
|
|
276
|
-
}).
|
|
276
|
+
}).toThrow();
|
|
277
277
|
|
|
278
278
|
});
|
|
279
279
|
|
|
@@ -296,9 +296,9 @@ describe("Grammar", () => {
|
|
|
296
296
|
space = " "
|
|
297
297
|
full-name = first-name & space & last-name
|
|
298
298
|
`
|
|
299
|
-
function resolveImport(
|
|
300
|
-
expect(
|
|
301
|
-
return Promise.resolve({ expression: importExpression,
|
|
299
|
+
function resolveImport(resource: string) {
|
|
300
|
+
expect(resource).toBe("some/path/to/file.cpat");
|
|
301
|
+
return Promise.resolve({ expression: importExpression, resource });
|
|
302
302
|
}
|
|
303
303
|
|
|
304
304
|
const patterns = await Grammar.parse(expression, { resolveImport });
|
|
@@ -323,8 +323,8 @@ describe("Grammar", () => {
|
|
|
323
323
|
last-name = "Doe"
|
|
324
324
|
full-name = first-name & space & last-name
|
|
325
325
|
`
|
|
326
|
-
function resolveImport(
|
|
327
|
-
return Promise.resolve({ expression: pathMap[
|
|
326
|
+
function resolveImport(resource: string) {
|
|
327
|
+
return Promise.resolve({ expression: pathMap[resource], resource });
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
const patterns = await Grammar.parse(expression, { resolveImport });
|
|
@@ -333,4 +333,93 @@ describe("Grammar", () => {
|
|
|
333
333
|
expect(result?.ast?.value).toBe("John Doe");
|
|
334
334
|
});
|
|
335
335
|
|
|
336
|
+
test("Imports with Params", async () => {
|
|
337
|
+
const importExpression = `first-name = "John"`;
|
|
338
|
+
const spaceExpression = `
|
|
339
|
+
use params { custom-space }
|
|
340
|
+
space = custom-space
|
|
341
|
+
`
|
|
342
|
+
const expression = `
|
|
343
|
+
import { first-name } from "first-name.cpat"
|
|
344
|
+
import { space } from "space.cpat" with params { custom-space = " " }
|
|
345
|
+
last-name = "Doe"
|
|
346
|
+
full-name = first-name & space & last-name
|
|
347
|
+
`
|
|
348
|
+
|
|
349
|
+
const pathMap: Record<string, string> = {
|
|
350
|
+
"space.cpat": spaceExpression,
|
|
351
|
+
"first-name.cpat": importExpression
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
function resolveImport(resource: string) {
|
|
355
|
+
return Promise.resolve({ expression: pathMap[resource], resource });
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const patterns = await Grammar.parse(expression, { resolveImport });
|
|
359
|
+
const fullname = patterns.get("full-name") as Pattern;
|
|
360
|
+
const result = fullname.exec("John Doe");
|
|
361
|
+
expect(result?.ast?.value).toBe("John Doe");
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test("Export Name", async () => {
|
|
365
|
+
const expression = `
|
|
366
|
+
import { use-this } from "resource1"
|
|
367
|
+
import {name} from "resource2" with params {
|
|
368
|
+
use-this
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
name
|
|
372
|
+
`
|
|
373
|
+
|
|
374
|
+
const resource1 = `
|
|
375
|
+
use-this = "Use This"
|
|
376
|
+
`;
|
|
377
|
+
|
|
378
|
+
const resource2 = `
|
|
379
|
+
use params {
|
|
380
|
+
use-this
|
|
381
|
+
}
|
|
382
|
+
name = use-this
|
|
383
|
+
`;
|
|
384
|
+
|
|
385
|
+
const pathMap: Record<string, string> = {
|
|
386
|
+
"resource1": resource1,
|
|
387
|
+
"resource2": resource2
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
function resolveImport(resource: string) {
|
|
391
|
+
return Promise.resolve({ expression: pathMap[resource], resource });
|
|
392
|
+
}
|
|
393
|
+
const patterns = await Grammar.parse(expression, { resolveImport });
|
|
394
|
+
const pattern = patterns.get("name") as Literal;
|
|
395
|
+
|
|
396
|
+
const result = pattern.exec("Use This");
|
|
397
|
+
|
|
398
|
+
expect(result.ast?.value).toBe("Use This");
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
test("Import Alias", async () => {
|
|
402
|
+
const expression = `
|
|
403
|
+
import { value as alias } from "resource1"
|
|
404
|
+
name = alias
|
|
405
|
+
`
|
|
406
|
+
|
|
407
|
+
const resource1 = `
|
|
408
|
+
value = "Value"
|
|
409
|
+
`;
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
const pathMap: Record<string, string> = {
|
|
413
|
+
"resource1": resource1,
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
function resolveImport(resource: string) {
|
|
417
|
+
return Promise.resolve({ expression: pathMap[resource], resource });
|
|
418
|
+
}
|
|
419
|
+
const patterns = await Grammar.parse(expression, { resolveImport });
|
|
420
|
+
const pattern = patterns.get("name") as Literal;
|
|
421
|
+
|
|
422
|
+
const result = pattern.exec("Value");
|
|
423
|
+
expect(result.ast?.value).toBe("Value");
|
|
424
|
+
});
|
|
336
425
|
});
|
package/src/grammar/Grammar.ts
CHANGED
|
@@ -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
|
-
|
|
27
|
+
resource: string;
|
|
28
28
|
expression: string;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export interface GrammarOptions {
|
|
32
|
-
resolveImport?: (
|
|
33
|
-
|
|
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
|
|
38
|
-
private
|
|
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.
|
|
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({
|
|
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,9 +119,15 @@ export class Grammar {
|
|
|
112
119
|
}
|
|
113
120
|
|
|
114
121
|
private _buildPatterns(ast: Node) {
|
|
115
|
-
ast.
|
|
122
|
+
const body = ast.find(n => n.name === "body");
|
|
123
|
+
|
|
124
|
+
if (body == null) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
body.findAll(n => n.name === "assign-statement" || n.name === "export-name").forEach((n) => {
|
|
116
129
|
const typeNode = n.find(n => n.name.includes("literal"));
|
|
117
|
-
const type = typeNode?.name || "unknown";
|
|
130
|
+
const type = n.name === "export-name" ? "export-name" : typeNode?.name || "unknown";
|
|
118
131
|
|
|
119
132
|
switch (type) {
|
|
120
133
|
case "literal": {
|
|
@@ -141,6 +154,11 @@ export class Grammar {
|
|
|
141
154
|
this._buildAlias(n)
|
|
142
155
|
break;
|
|
143
156
|
}
|
|
157
|
+
case "export-name": {
|
|
158
|
+
const pattern = this._getPattern(n.value);
|
|
159
|
+
this._parseContext.patternsByName.set(n.value, pattern);
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
144
162
|
default: {
|
|
145
163
|
break;
|
|
146
164
|
}
|
|
@@ -150,39 +168,94 @@ export class Grammar {
|
|
|
150
168
|
|
|
151
169
|
private async _resolveImports(ast: Node) {
|
|
152
170
|
const parseContext = this._parseContext;
|
|
153
|
-
const importStatements = ast.findAll(n => n.name === "import-
|
|
171
|
+
const importStatements = ast.findAll(n => n.name === "import-from");
|
|
154
172
|
|
|
155
173
|
for (const importStatement of importStatements) {
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
const grammarFile = await this._resolveImport(
|
|
160
|
-
const grammar = new Grammar({
|
|
174
|
+
const resourceNode = importStatement.find(n => n.name === "resource") as Node;
|
|
175
|
+
const params = this._getParams(importStatement);
|
|
176
|
+
const resource = resourceNode.value.slice(1, -1);
|
|
177
|
+
const grammarFile = await this._resolveImport(resource, this._originResource || null);
|
|
178
|
+
const grammar = new Grammar({
|
|
179
|
+
resolveImport: this._resolveImport,
|
|
180
|
+
originResource: grammarFile.resource,
|
|
181
|
+
params
|
|
182
|
+
});
|
|
161
183
|
|
|
162
184
|
try {
|
|
163
185
|
const patterns = await grammar.parse(grammarFile.expression);
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (
|
|
168
|
-
|
|
186
|
+
const importStatements = importStatement.findAll(n => n.name === "import-name" || n.name === "import-alias");
|
|
187
|
+
|
|
188
|
+
importStatements.forEach((node) => {
|
|
189
|
+
if (node.name === "import-name") {
|
|
190
|
+
const importName = node.value;
|
|
191
|
+
|
|
192
|
+
if (parseContext.importedPatternsByName.has(importName)) {
|
|
193
|
+
throw new Error(`'${importName}' was already used within another import.`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const pattern = patterns.get(importName);
|
|
197
|
+
if (pattern == null) {
|
|
198
|
+
throw new Error(`Couldn't find pattern with name: ${importName}, from import: ${resource}.`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
parseContext.importedPatternsByName.set(importName, pattern);
|
|
202
|
+
} else {
|
|
203
|
+
const importNameNode = node.find(n => n.name === "import-name") as Node;
|
|
204
|
+
const importName = importNameNode.value;
|
|
205
|
+
const aliasNode = node.find(n => n.name === "import-name-alias") as Node;
|
|
206
|
+
const alias = aliasNode.value;
|
|
207
|
+
|
|
208
|
+
if (parseContext.importedPatternsByName.has(alias)) {
|
|
209
|
+
throw new Error(`'${alias}' was already used within another import.`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const pattern = patterns.get(importName);
|
|
213
|
+
if (pattern == null) {
|
|
214
|
+
throw new Error(`Couldn't find pattern with name: ${importName}, from import: ${resource}.`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
parseContext.importedPatternsByName.set(alias, pattern);
|
|
169
218
|
}
|
|
219
|
+
});
|
|
170
220
|
|
|
171
|
-
const pattern = patterns.get(importName);
|
|
172
|
-
if (pattern == null) {
|
|
173
|
-
throw new Error(`Couldn't find pattern with name: ${importName}, from import: ${url}.`);
|
|
174
|
-
}
|
|
175
221
|
|
|
176
|
-
parseContext.importedPatternsByName.set(importName, pattern);
|
|
177
|
-
})
|
|
178
222
|
|
|
179
223
|
} catch (e: any) {
|
|
180
|
-
throw new Error(`Failed loading expression from: "${
|
|
224
|
+
throw new Error(`Failed loading expression from: "${resource}". Error details: "${e.message}"`);
|
|
181
225
|
}
|
|
182
226
|
|
|
183
227
|
}
|
|
184
228
|
}
|
|
185
229
|
|
|
230
|
+
private _getParams(importStatement: Node) {
|
|
231
|
+
let params: Pattern[] = [];
|
|
232
|
+
const paramsStatement = importStatement.find(n => n.name === "with-params-statement");
|
|
233
|
+
|
|
234
|
+
if (paramsStatement != null) {
|
|
235
|
+
const statements = paramsStatement.find(n => n.name === "with-params-body");
|
|
236
|
+
|
|
237
|
+
if (statements != null) {
|
|
238
|
+
const expression = statements.toString();
|
|
239
|
+
const importedValues = Array.from(this
|
|
240
|
+
._parseContext
|
|
241
|
+
.importedPatternsByName
|
|
242
|
+
.values()
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
const grammar = new Grammar({
|
|
246
|
+
params: importedValues,
|
|
247
|
+
originResource: this._originResource,
|
|
248
|
+
resolveImport: this._resolveImport
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const patterns = grammar.parseString(expression);
|
|
252
|
+
params = Array.from(patterns.values());
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return params;
|
|
257
|
+
}
|
|
258
|
+
|
|
186
259
|
private _buildLiteral(statementNode: Node) {
|
|
187
260
|
const nameNode = statementNode.find(n => n.name === "name") as Node;
|
|
188
261
|
const literalNode = statementNode.find(n => n.name === "literal") as Node;
|
|
@@ -223,6 +296,10 @@ export class Grammar {
|
|
|
223
296
|
pattern = this._parseContext.importedPatternsByName.get(name);
|
|
224
297
|
}
|
|
225
298
|
|
|
299
|
+
if (pattern == null) {
|
|
300
|
+
pattern = this._parseContext.paramsByName.get(name);
|
|
301
|
+
}
|
|
302
|
+
|
|
226
303
|
if (pattern == null) {
|
|
227
304
|
return new Reference(name);
|
|
228
305
|
}
|
|
@@ -334,8 +411,8 @@ export class Grammar {
|
|
|
334
411
|
return grammar.import(path);
|
|
335
412
|
}
|
|
336
413
|
|
|
337
|
-
static parseString(expression: string) {
|
|
338
|
-
const grammar = new Grammar();
|
|
414
|
+
static parseString(expression: string, options?: GrammarOptions) {
|
|
415
|
+
const grammar = new Grammar(options);
|
|
339
416
|
return grammar.parseString(expression);
|
|
340
417
|
}
|
|
341
418
|
|
|
@@ -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
|
|
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
|
-
|
|
14
|
+
spaces.setTokens([" "]);
|
|
15
|
+
tabs.setTokens(["\t"]);
|
|
12
16
|
newLine.setTokens(["\n"]);
|
|
13
17
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,68 @@ 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";
|
|
9
|
+
|
|
10
|
+
const optionalSpaces = allSpaces.clone("optional-spaces", true);
|
|
11
|
+
const optionalLineSpaces = lineSpaces.clone("options-line-spaces", true);
|
|
6
12
|
|
|
7
|
-
const spaces = new Regex("spaces", "\\s+", true);
|
|
8
13
|
const importNameDivider = new Regex("import-name-divider", "(\\s+)?,(\\s+)?");
|
|
9
14
|
const importKeyword = new Literal("import", "import");
|
|
15
|
+
const useParamsKeyword = new Literal("use-params", "use params");
|
|
16
|
+
const asKeyword = new Literal("as", "as");
|
|
10
17
|
const fromKeyword = new Literal("from", "from");
|
|
11
18
|
const openBracket = new Literal("open-bracket", "{");
|
|
12
19
|
const closeBracket = new Literal("close-bracket", "}");
|
|
13
20
|
const name = new Regex("import-name", "[^}\\s,]+");
|
|
14
|
-
const
|
|
15
|
-
const
|
|
21
|
+
const importNameAlias = name.clone("import-name-alias");
|
|
22
|
+
const importAlias = new And("import-alias", [name, lineSpaces, asKeyword, lineSpaces, importNameAlias]);
|
|
23
|
+
const importedNames = new Repeat("imported-names", new Or("import-names", [importAlias, name]), { divider: importNameDivider });
|
|
24
|
+
const paramName = name.clone("param-name");
|
|
25
|
+
const paramNames = new Repeat("param-names", paramName, { divider: importNameDivider });
|
|
26
|
+
const resource = literal.clone("resource");
|
|
16
27
|
|
|
17
|
-
|
|
18
|
-
|
|
28
|
+
const useParams = new And("import-params", [
|
|
29
|
+
useParamsKeyword,
|
|
30
|
+
optionalLineSpaces,
|
|
31
|
+
openBracket,
|
|
32
|
+
optionalSpaces,
|
|
33
|
+
paramNames,
|
|
34
|
+
optionalSpaces,
|
|
35
|
+
closeBracket
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
const withParamsKeyword = new Literal("with-params", "with params");
|
|
39
|
+
const withParamsStatement = new And("with-params-statement", [
|
|
40
|
+
withParamsKeyword,
|
|
41
|
+
optionalLineSpaces,
|
|
42
|
+
openBracket,
|
|
19
43
|
optionalSpaces,
|
|
44
|
+
body.clone("with-params-body"),
|
|
45
|
+
optionalSpaces,
|
|
46
|
+
closeBracket
|
|
47
|
+
], true);
|
|
48
|
+
|
|
49
|
+
const importFromStatement = new And("import-from", [
|
|
50
|
+
importKeyword,
|
|
51
|
+
optionalLineSpaces,
|
|
20
52
|
openBracket,
|
|
21
53
|
optionalSpaces,
|
|
22
54
|
importedNames,
|
|
23
55
|
optionalSpaces,
|
|
24
56
|
closeBracket,
|
|
25
|
-
|
|
57
|
+
optionalLineSpaces,
|
|
26
58
|
fromKeyword,
|
|
27
|
-
|
|
28
|
-
|
|
59
|
+
optionalLineSpaces,
|
|
60
|
+
resource,
|
|
61
|
+
optionalLineSpaces,
|
|
62
|
+
withParamsStatement
|
|
29
63
|
]);
|
|
64
|
+
|
|
65
|
+
export const importStatement = new Or("import-statement", [
|
|
66
|
+
useParams,
|
|
67
|
+
importFromStatement
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
|
|
@@ -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
|
|
4
|
-
spaces
|
|
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,
|
|
@@ -23,14 +21,13 @@ const statements = new Or("statements", [
|
|
|
23
21
|
name.clone("alias-literal"),
|
|
24
22
|
]);
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
const assignStatement = new And("assign-statement", [
|
|
27
25
|
optionalSpaces,
|
|
28
26
|
name,
|
|
29
27
|
optionalSpaces,
|
|
30
28
|
assignOperator,
|
|
31
29
|
optionalSpaces,
|
|
32
|
-
statements
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
]);
|
|
30
|
+
statements
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
export const statement = new Or("statement", [assignStatement, name.clone("export-name")]);
|
package/src/grammar/spec.md
CHANGED
|
@@ -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
|
+
```
|