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.
- 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/index.browser.js +132 -42
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +132 -42
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +132 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/grammar/Grammar.test.ts +38 -5
- package/src/grammar/Grammar.ts +69 -25
- package/src/grammar/patterns/body.ts +19 -0
- package/src/grammar/patterns/grammar.ts +26 -9
- package/src/grammar/patterns/import.ts +44 -7
- package/src/grammar/patterns/spaces.ts +12 -2
- package/src/grammar/patterns/statement.ts +1 -6
- package/src/grammar/spec.md +24 -0
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,7 +119,7 @@ export class Grammar {
|
|
|
112
119
|
}
|
|
113
120
|
|
|
114
121
|
private _buildPatterns(ast: Node) {
|
|
115
|
-
ast.
|
|
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-
|
|
160
|
+
const importStatements = ast.findAll(n => n.name === "import-from");
|
|
154
161
|
|
|
155
162
|
for (const importStatement of importStatements) {
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
const grammarFile = await this._resolveImport(
|
|
160
|
-
const grammar = new Grammar({
|
|
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: ${
|
|
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: "${
|
|
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
|
|
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,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
|
|
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
|
-
|
|
18
|
-
|
|
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
|
-
|
|
53
|
+
optionalLineSpaces,
|
|
26
54
|
fromKeyword,
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
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,
|
|
@@ -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
|
]);
|
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
|
+
```
|