clarity-pattern-parser 11.3.13 → 11.4.1
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 +6 -2
- package/dist/grammar_v2/patterns/Grammar.d.ts +23 -0
- package/dist/grammar_v2/patterns/patterns/grammar.d.ts +87 -0
- package/dist/grammar_v2/patterns/patterns/grammar.test.d.ts +1 -0
- package/dist/index.browser.js +82 -13
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +82 -13
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +82 -13
- package/dist/index.js.map +1 -1
- package/dist/patterns/Cursor.d.ts +1 -1
- package/package.json +1 -1
- package/src/grammar/Grammar.test.ts +38 -0
- package/src/grammar/Grammar.ts +87 -8
- package/src/grammar_v2/patterns/Grammar.ts +170 -0
- package/src/grammar_v2/patterns/patterns/cpat.cpat +91 -0
- package/src/grammar_v2/patterns/patterns/grammar.test.ts +218 -0
- package/src/grammar_v2/patterns/patterns/grammar.ts +103 -0
- package/src/patterns/Cursor.ts +6 -6
- package/src/patterns/Regex.ts +4 -2
- package/src/patterns/Sequence.test.ts +11 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { grammar } from "./grammar";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { Node } from "../../../ast/Node";
|
|
5
|
+
|
|
6
|
+
const dir = path.join(__dirname, "./cpat.cpat");
|
|
7
|
+
|
|
8
|
+
describe("grammar", () => {
|
|
9
|
+
test("syntax version", () => {
|
|
10
|
+
const { ast } = grammar.exec("syntax 1.0");
|
|
11
|
+
|
|
12
|
+
expect(ast).toBeDefined();
|
|
13
|
+
expect(ast?.find((p: Node)=>p.name === "syntax")).toBeDefined();
|
|
14
|
+
expect(ast?.find((p: Node)=>p.name === "syntax")?.value).toBe("syntax");
|
|
15
|
+
expect(ast?.find((p: Node)=>p.name === "syntaxVersion")).toBeDefined();
|
|
16
|
+
expect(ast?.find((p: Node)=>p.name === "syntaxVersion")?.value).toBe("1.0");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("import", () => {
|
|
20
|
+
const { ast } = grammar.exec('import {pattern} from "resource"');
|
|
21
|
+
|
|
22
|
+
expect(ast).toBeDefined();
|
|
23
|
+
expect(ast?.find((p: Node)=>p.name === "importStatement")).toBeDefined();
|
|
24
|
+
expect(ast?.find((p: Node)=>p.name === "import")).toBeDefined();
|
|
25
|
+
expect(ast?.find((p: Node)=>p.name === "import")?.value).toBe("import");
|
|
26
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")).toBeDefined();
|
|
27
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")?.value).toBe("pattern");
|
|
28
|
+
expect(ast?.find((p: Node)=>p.name === "from")).toBeDefined();
|
|
29
|
+
expect(ast?.find((p: Node)=>p.name === "from")?.value).toBe("from");
|
|
30
|
+
expect(ast?.find((p: Node)=>p.name === "resource")).toBeDefined();
|
|
31
|
+
expect(ast?.find((p: Node)=>p.name === "resource")?.value).toBe('"resource"');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("import with params", () => {
|
|
35
|
+
const { ast } = grammar.exec('import {pattern} from "resource" with params {param = "value"}');
|
|
36
|
+
|
|
37
|
+
expect(ast).toBeDefined();
|
|
38
|
+
expect(ast?.find((p: Node)=>p.name === "importStatement")).toBeDefined();
|
|
39
|
+
expect(ast?.find((p: Node)=>p.name === "import")).toBeDefined();
|
|
40
|
+
expect(ast?.find((p: Node)=>p.name === "import")?.value).toBe("import");
|
|
41
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")).toBeDefined();
|
|
42
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")?.value).toBe("pattern");
|
|
43
|
+
expect(ast?.find((p: Node)=>p.name === "from")).toBeDefined();
|
|
44
|
+
expect(ast?.find((p: Node)=>p.name === "from")?.value).toBe("from");
|
|
45
|
+
expect(ast?.find((p: Node)=>p.name === "resource")).toBeDefined();
|
|
46
|
+
expect(ast?.find((p: Node)=>p.name === "resource")?.value).toBe('"resource"');
|
|
47
|
+
expect(ast?.find((p: Node)=>p.name === "withParamsExpr")).toBeDefined();
|
|
48
|
+
expect(ast?.find((p: Node)=>p.name === "withParamsExpr")?.value).toBe("with params {param = \"value\"}");
|
|
49
|
+
expect(ast?.find((p: Node)=>p.name === "statements")?.find((p: Node)=>p.name === "param = \"value\"")).toBeDefined();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("use params", () => {
|
|
53
|
+
const { ast } = grammar.exec('use params { param }');
|
|
54
|
+
|
|
55
|
+
expect(ast).toBeDefined();
|
|
56
|
+
expect(ast?.find((p: Node)=>p.name === "useParamsStatement")).toBeDefined();
|
|
57
|
+
expect(ast?.find((p: Node)=>p.name === "useParamsStatement")?.value).toBe("use params { param }");
|
|
58
|
+
expect(ast?.find((p: Node)=>p.name === "useParams")).toBeDefined();
|
|
59
|
+
expect(ast?.find((p: Node)=>p.name === "useParams")?.value).toBe("use params");
|
|
60
|
+
expect(ast?.find((p: Node)=>p.name === "useParamPatterns")).toBeDefined();
|
|
61
|
+
expect(ast?.find((p: Node)=>p.name === "useParamPatterns")?.value).toBe("param");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("literal", () => {
|
|
65
|
+
const { ast } = grammar.exec('literal = "Hello, world!"');
|
|
66
|
+
|
|
67
|
+
expect(ast).toBeDefined();
|
|
68
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")).toBeDefined();
|
|
69
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")?.value).toBe("literal = \"Hello, world!\"");
|
|
70
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")).toBeDefined();
|
|
71
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")?.value).toBe("literal");
|
|
72
|
+
expect(ast?.find((p: Node)=>p.name === "literal")).toBeDefined();
|
|
73
|
+
expect(ast?.find((p: Node)=>p.name === "literal")?.value).toBe("\"Hello, world!\"");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
test("regex", () => {
|
|
78
|
+
const { ast } = grammar.exec('regex = /Hello, world!/');
|
|
79
|
+
|
|
80
|
+
expect(ast).toBeDefined();
|
|
81
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")).toBeDefined();
|
|
82
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")?.value).toBe("regex = /Hello, world!/");
|
|
83
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")).toBeDefined();
|
|
84
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")?.value).toBe("regex");
|
|
85
|
+
expect(ast?.find((p: Node)=>p.name === "regex")).toBeDefined();
|
|
86
|
+
expect(ast?.find((p: Node)=>p.name === "regex")?.value).toBe("/Hello, world!/");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("comment", () => {
|
|
90
|
+
const { ast } = grammar.exec('# Comment');
|
|
91
|
+
|
|
92
|
+
expect(ast).toBeDefined();
|
|
93
|
+
expect(ast?.find((p: Node)=>p.name === "comment")).toBeDefined();
|
|
94
|
+
expect(ast?.find((p: Node)=>p.name === "comment")?.value).toBe("# Comment");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("sequence", () => {
|
|
98
|
+
const { ast } = grammar.exec('sequence = "Hello" + "world"');
|
|
99
|
+
|
|
100
|
+
expect(ast).toBeDefined();
|
|
101
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")).toBeDefined();
|
|
102
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")?.value).toBe("sequence = \"Hello\" + \"world\"");
|
|
103
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")).toBeDefined();
|
|
104
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")?.value).toBe("sequence");
|
|
105
|
+
expect(ast?.find((p: Node)=>p.name === "sequenceExpr")).toBeDefined();
|
|
106
|
+
expect(ast?.find((p: Node)=>p.name === "sequenceExpr")?.value).toBe("\"Hello\" + \"world\"");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("options", () => {
|
|
110
|
+
const { ast } = grammar.exec('options = "Hello" | "world"');
|
|
111
|
+
|
|
112
|
+
expect(ast).toBeDefined();
|
|
113
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")).toBeDefined();
|
|
114
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")?.value).toBe("options = \"Hello\" | \"world\"");
|
|
115
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")).toBeDefined();
|
|
116
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")?.value).toBe("options");
|
|
117
|
+
expect(ast?.find((p: Node)=>p.name === "optionsExpr")).toBeDefined();
|
|
118
|
+
expect(ast?.find((p: Node)=>p.name === "optionsExpr")?.value).toBe("\"Hello\" | \"world\"");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("greedy options", () => {
|
|
122
|
+
const { ast } = grammar.exec('greedyOptions = "Hello" <|> "world"');
|
|
123
|
+
|
|
124
|
+
expect(ast).toBeDefined();
|
|
125
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")).toBeDefined();
|
|
126
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")?.value).toBe("greedyOptions = \"Hello\" <|> \"world\"");
|
|
127
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")).toBeDefined();
|
|
128
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")?.value).toBe("greedyOptions");
|
|
129
|
+
expect(ast?.find((p: Node)=>p.name === "greedyOptionsExpr")).toBeDefined();
|
|
130
|
+
expect(ast?.find((p: Node)=>p.name === "greedyOptionsExpr")?.value).toBe("\"Hello\" <|> \"world\"");
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("repeat", () => {
|
|
134
|
+
const { ast } = grammar.exec('repeat = (Hello)+');
|
|
135
|
+
|
|
136
|
+
expect(ast).toBeDefined();
|
|
137
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")).toBeDefined();
|
|
138
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")?.value).toBe("repeat = (Hello)+");
|
|
139
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")).toBeDefined();
|
|
140
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")?.value).toBe("repeat");
|
|
141
|
+
expect(ast?.find((p: Node)=>p.name === "repeatExpr")).toBeDefined();
|
|
142
|
+
expect(ast?.find((p: Node)=>p.name === "repeatExpr")?.value).toBe("(Hello)+");
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("repeat zero or more", () => {
|
|
146
|
+
const { ast } = grammar.exec('repeat = (Hello)*');
|
|
147
|
+
|
|
148
|
+
expect(ast).toBeDefined();
|
|
149
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")).toBeDefined();
|
|
150
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")?.value).toBe("repeat = (Hello)*");
|
|
151
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")).toBeDefined();
|
|
152
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")?.value).toBe("repeat");
|
|
153
|
+
expect(ast?.find((p: Node)=>p.name === "repeatExpr")).toBeDefined();
|
|
154
|
+
expect(ast?.find((p: Node)=>p.name === "repeatExpr")?.value).toBe("(Hello)*");
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("repeat with bounds", () => {
|
|
158
|
+
const { ast } = grammar.exec('repeat = (Hello){2, 4}');
|
|
159
|
+
|
|
160
|
+
expect(ast).toBeDefined();
|
|
161
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")).toBeDefined();
|
|
162
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")?.value).toBe("repeat = (Hello){2, 4}");
|
|
163
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")).toBeDefined();
|
|
164
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")?.value).toBe("repeat");
|
|
165
|
+
expect(ast?.find((p: Node)=>p.name === "repeatExpr")).toBeDefined();
|
|
166
|
+
expect(ast?.find((p: Node)=>p.name === "repeatExpr")?.value).toBe("(Hello){2, 4}");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("repeat with upper limit", () => {
|
|
170
|
+
const { ast } = grammar.exec('repeat = (Hello){, 4}');
|
|
171
|
+
|
|
172
|
+
expect(ast).toBeDefined();
|
|
173
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")).toBeDefined();
|
|
174
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")?.value).toBe("repeat = (Hello){, 4}");
|
|
175
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")).toBeDefined();
|
|
176
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")?.value).toBe("repeat");
|
|
177
|
+
expect(ast?.find((p: Node)=>p.name === "repeatExpr")).toBeDefined();
|
|
178
|
+
expect(ast?.find((p: Node)=>p.name === "repeatExpr")?.value).toBe("(Hello){, 4}");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("repeat with lower limit", () => {
|
|
182
|
+
const { ast } = grammar.exec('repeat = (Hello){1,}');
|
|
183
|
+
|
|
184
|
+
expect(ast).toBeDefined();
|
|
185
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")).toBeDefined();
|
|
186
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")?.value).toBe("repeat = (Hello){1,}");
|
|
187
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")).toBeDefined();
|
|
188
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")?.value).toBe("repeat");
|
|
189
|
+
expect(ast?.find((p: Node)=>p.name === "repeatExpr")).toBeDefined();
|
|
190
|
+
expect(ast?.find((p: Node)=>p.name === "repeatExpr")?.value).toBe("(Hello){1,}");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("repeat with delimiter", () => {
|
|
194
|
+
const { ast } = grammar.exec('repeat = (Hello, "world")+');
|
|
195
|
+
|
|
196
|
+
expect(ast).toBeDefined();
|
|
197
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")).toBeDefined();
|
|
198
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")?.value).toBe("repeat = (Hello, \"world\")+");
|
|
199
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")).toBeDefined();
|
|
200
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")?.value).toBe("repeat");
|
|
201
|
+
expect(ast?.find((p: Node)=>p.name === "repeatExpr")).toBeDefined();
|
|
202
|
+
expect(ast?.find((p: Node)=>p.name === "repeatExpr")?.value).toBe("(Hello, \"world\")+");
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("repeat with trim delimiter", () => {
|
|
206
|
+
const { ast } = grammar.exec('repeat = (Hello, "world" trim)+');
|
|
207
|
+
|
|
208
|
+
expect(ast).toBeDefined();
|
|
209
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")).toBeDefined();
|
|
210
|
+
expect(ast?.find((p: Node)=>p.name === "patternAssignment")?.value).toBe("repeat = (Hello, \"world\" trim)+");
|
|
211
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")).toBeDefined();
|
|
212
|
+
expect(ast?.find((p: Node)=>p.name === "patternName")?.value).toBe("repeat");
|
|
213
|
+
expect(ast?.find((p: Node)=>p.name === "repeatExpr")).toBeDefined();
|
|
214
|
+
expect(ast?.find((p: Node)=>p.name === "repeatExpr")?.value).toBe("(Hello, \"world\" trim)+");
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Expression } from "../../../patterns/Expression";
|
|
2
|
+
import { Literal } from "../../../patterns/Literal";
|
|
3
|
+
import { Optional } from "../../../patterns/Optional";
|
|
4
|
+
import { Options } from "../../../patterns/Options";
|
|
5
|
+
import { Reference } from "../../../patterns/Reference";
|
|
6
|
+
import { Regex } from "../../../patterns/Regex";
|
|
7
|
+
import { Repeat } from "../../../patterns/Repeat";
|
|
8
|
+
import { Sequence } from "../../../patterns/Sequence";
|
|
9
|
+
|
|
10
|
+
export const syntax = new Literal("syntax", "syntax");
|
|
11
|
+
export const imprt = new Literal("import", "import");
|
|
12
|
+
export const useParams = new Literal("useParams", "use params");
|
|
13
|
+
export const withParams = new Literal("withParams", "with params");
|
|
14
|
+
export const from = new Literal("from", "from");
|
|
15
|
+
export const right = new Literal("right", "right");
|
|
16
|
+
export const ws = new Regex("ws", "\\s+");
|
|
17
|
+
export const ls = new Regex("ls", "[ \\t]+");
|
|
18
|
+
export const assign = new Literal("assign", "=");
|
|
19
|
+
export const bar = new Literal("|", "|");
|
|
20
|
+
export const greedyBar = new Literal("<|>", "<|>");
|
|
21
|
+
export const concat = new Literal("+", "+");
|
|
22
|
+
export const colon = new Literal(":", ":");
|
|
23
|
+
export const openParen = new Literal("(", "(");
|
|
24
|
+
export const closeParen = new Literal(")", ")");
|
|
25
|
+
export const openSquareBracket = new Literal("[", "[");
|
|
26
|
+
export const closeSquareBracket = new Literal("]", "]");
|
|
27
|
+
export const openBracket = new Literal("{", "{");
|
|
28
|
+
export const closeBracket = new Literal("}", "}");
|
|
29
|
+
export const comma = new Regex(",", "\\s*[,]\\s*");
|
|
30
|
+
export const trim = new Literal("trim", "trim");
|
|
31
|
+
export const not = new Literal("not", "!");
|
|
32
|
+
export const optional = new Literal("?", "?");
|
|
33
|
+
export const newLine = new Regex("newLine", "\\s*(\\r\\n|\\r|\\n)\\s*");
|
|
34
|
+
export const syntaxVersion = new Regex("syntaxVersion", "\\S*");
|
|
35
|
+
export const at = new Literal("@", "@");
|
|
36
|
+
export const optionalWS = new Optional("optionalWS", ws);
|
|
37
|
+
export const optionalLS = new Optional("optionalLS", ls);
|
|
38
|
+
|
|
39
|
+
export const literal = new Regex("literal", '"(?:\\\\.|[^"\\\\])*"');
|
|
40
|
+
export const regex = new Regex("regex", "\\/(\\\\|[^\\n\\r])*\\/");
|
|
41
|
+
|
|
42
|
+
export const integer = new Regex("integer", "[1-9][0-9]*|[0]");
|
|
43
|
+
export const name = new Regex("name", "[a-zA-Z_-]+[a-zA-Z0-9_-]*");
|
|
44
|
+
export const patternName = name.clone("patternName");
|
|
45
|
+
export const patternIdentifier = name.clone("patternIdentifier");
|
|
46
|
+
export const resource = literal.clone("resource");
|
|
47
|
+
|
|
48
|
+
export const comment = new Regex("comment", "#[^\\n\\r]*");
|
|
49
|
+
|
|
50
|
+
export const jsonString = literal.clone("jsonString");
|
|
51
|
+
export const jsonNumber = new Regex("jsonNumber", "-?\\d+(\\.\\d+)?");
|
|
52
|
+
export const trueLiteral = new Literal("trueLiteral", "true");
|
|
53
|
+
export const falseLiteral = new Literal("falseLiteral", "false");
|
|
54
|
+
export const jsonBoolean = new Options("jsonBoolean", [trueLiteral, falseLiteral]);
|
|
55
|
+
export const jsonNull = new Literal("jsonNull", "null");
|
|
56
|
+
export const jsonArrayItems = new Repeat("jsonArrayItems", new Reference("jsonValue"), { divider: comma, trimDivider: true });
|
|
57
|
+
export const jsonArray = new Sequence("jsonArray", [openSquareBracket, optionalWS, jsonArrayItems, optionalWS, closeSquareBracket]);
|
|
58
|
+
export const jsonObjectPropertyName = literal.clone("jsonObjectPropertyName");
|
|
59
|
+
export const jsonObjectProperty = new Sequence("jsonObjectProperty", [jsonObjectPropertyName, optionalWS, colon, optionalWS, new Reference("jsonValue")]);
|
|
60
|
+
export const jsonObjectProperties = new Repeat("jsonObjectProperties", jsonObjectProperty, { divider: comma, trimDivider: true });
|
|
61
|
+
export const jsonObject = new Sequence("jsonObject", [openBracket, optionalWS, jsonObjectProperties, optionalWS, closeBracket]);
|
|
62
|
+
export const jsonValue = new Options("jsonValue", [jsonString, jsonNumber, jsonBoolean, jsonNull, jsonArray, jsonObject]);
|
|
63
|
+
|
|
64
|
+
export const syntaxStatement = new Sequence("syntaxStatement", [syntax, optionalWS, syntaxVersion]);
|
|
65
|
+
|
|
66
|
+
export const decorationName = name.clone("decorationName");
|
|
67
|
+
export const decorationStatement = new Sequence("decorationStatement", [at, optionalWS, decorationName, optionalWS, openParen, optionalWS, jsonValue, optionalWS, closeParen]);
|
|
68
|
+
|
|
69
|
+
export const useParamPatterns = new Repeat("useParamPatterns", patternName, { divider: comma, trimDivider: true });
|
|
70
|
+
export const useParamsStatement = new Sequence("useParamsStatement", [useParams, optionalLS, openBracket, optionalWS, useParamPatterns, optionalWS, closeBracket]);
|
|
71
|
+
|
|
72
|
+
export const withParamStatements = new Repeat("withParamStatements", new Reference("patternAssignment"), { divider: newLine, trimDivider: true });
|
|
73
|
+
export const withParamsExpr = new Sequence("withParamsExpr", [withParams, optionalLS, openBracket, optionalWS, withParamStatements, optionalWS, closeBracket]);
|
|
74
|
+
|
|
75
|
+
export const patternNames = new Repeat("patternNames", patternName, { divider: comma, trimDivider: true });
|
|
76
|
+
export const importedPatterns = new Sequence("importedPatterns", [openBracket, optionalWS, patternNames, optionalWS, closeBracket]);
|
|
77
|
+
export const importStatement = new Sequence("importStatement", [imprt, optionalLS, importedPatterns, optionalLS, from, optionalLS, resource, optionalLS, new Optional("optionalWithParamsExpr", withParamsExpr)]);
|
|
78
|
+
|
|
79
|
+
export const notExpr = new Sequence("notExpr", [not, optionalLS, new Reference("unaryPatternExpr")]);
|
|
80
|
+
export const optionalExpr = new Sequence("optionalExpr", [new Reference("unaryPatternExpr"), optionalLS, optional]);
|
|
81
|
+
export const rightAssociationExpr = new Sequence("rightAssociationExpr", [new Reference("unaryPatternExpr"), optionalLS, right]);
|
|
82
|
+
export const unaryPatternExpr = new Expression("unaryPatternExpr", [notExpr, optionalExpr, rightAssociationExpr, patternIdentifier]);
|
|
83
|
+
|
|
84
|
+
export const repeatBounds = new Sequence("repeatBounds", [openBracket, optionalWS, new Optional("optionalInteger", integer), optionalWS, new Optional("optionalComma", comma), optionalWS, new Optional("optionalInteger", integer), optionalWS, closeBracket]);
|
|
85
|
+
export const oneOrMore = new Literal("oneOrMore", "+");
|
|
86
|
+
export const zeroOrMore = new Literal("zeroOrMore", "*");
|
|
87
|
+
export const repeatOptions = new Options("repeatOptions", [oneOrMore, zeroOrMore, repeatBounds]);
|
|
88
|
+
export const delimiter = new Sequence("delimiter", [comma, new Reference("patternExpr"), optionalWS, new Optional("optionalTrim", trim)]);
|
|
89
|
+
export const repeatExpr = new Sequence("repeatExpr", [openParen, optionalWS, new Reference("patternExpr"), optionalWS, new Optional("optionalDelimiter", delimiter), optionalWS, closeParen, repeatOptions]);
|
|
90
|
+
|
|
91
|
+
export const sequenceExpr = new Sequence("sequenceExpr", [new Reference("patternExpr"), optionalWS, concat, optionalWS, new Reference("patternExpr")]);
|
|
92
|
+
export const optionsExpr = new Sequence("optionsExpr", [new Reference("patternExpr"), optionalWS, bar, optionalWS, new Reference("patternExpr")]);
|
|
93
|
+
export const greedyOptionsExpr = new Sequence("greedyOptionsExpr", [new Reference("patternExpr"), optionalWS, greedyBar, optionalWS, new Reference("patternExpr")]);
|
|
94
|
+
export const patternGroupExpr = new Sequence("patternGroupExpr", [openParen, optionalWS, new Reference("patternExpr"), optionalWS, closeParen]);
|
|
95
|
+
|
|
96
|
+
export const exportPattern = patternName.clone("exportPattern");
|
|
97
|
+
export const patternExpr = new Expression("patternExpr", [sequenceExpr, optionsExpr, greedyOptionsExpr, repeatExpr, patternGroupExpr, literal, regex, unaryPatternExpr]);
|
|
98
|
+
export const patternAssignment = new Sequence("patternAssignment", [patternName, optionalWS, assign, optionalWS, patternExpr]);
|
|
99
|
+
export const statement = new Options("statement", [useParamsStatement, importStatement, patternAssignment, decorationStatement, exportPattern, comment]);
|
|
100
|
+
export const statements = new Repeat("statements", statement, { divider: newLine});
|
|
101
|
+
export const cpat = new Sequence("cpat", [optionalWS, new Optional("optionalSyntaxStatement", syntaxStatement), optionalWS, new Optional("optionalStatements", statements)]);
|
|
102
|
+
|
|
103
|
+
export const grammar = cpat;
|
package/src/patterns/Cursor.ts
CHANGED
|
@@ -4,13 +4,13 @@ import { ParseError } from "./ParseError";
|
|
|
4
4
|
import { Pattern } from "./Pattern";
|
|
5
5
|
|
|
6
6
|
export class Cursor {
|
|
7
|
-
private
|
|
7
|
+
private _chars: string[];
|
|
8
8
|
private _index: number;
|
|
9
9
|
private _length: number;
|
|
10
10
|
private _history: CursorHistory;
|
|
11
11
|
|
|
12
12
|
get text(): string {
|
|
13
|
-
return this.
|
|
13
|
+
return this._chars.join("");
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
get isOnFirst(): boolean {
|
|
@@ -74,13 +74,13 @@ export class Cursor {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
get currentChar(): string {
|
|
77
|
-
return this.
|
|
77
|
+
return this._chars[this._index];
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
constructor(text: string) {
|
|
81
|
-
this.
|
|
81
|
+
this._chars = [...text];
|
|
82
82
|
this._index = 0;
|
|
83
|
-
this._length =
|
|
83
|
+
this._length = this._chars.length;
|
|
84
84
|
this._history = new CursorHistory();
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -123,7 +123,7 @@ export class Cursor {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
getChars(first: number, last: number): string {
|
|
126
|
-
return this.
|
|
126
|
+
return this._chars.slice(first, last + 1).join("");
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
recordMatch(pattern: Pattern, node: Node): void {
|
package/src/patterns/Regex.ts
CHANGED
|
@@ -107,7 +107,7 @@ export class Regex implements Pattern {
|
|
|
107
107
|
|
|
108
108
|
private tryToParse(cursor: Cursor) {
|
|
109
109
|
const result = this._regex.exec(this._substring);
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
if (result != null && result[0].length > 0 && result.index === 0) {
|
|
112
112
|
this.processResult(cursor, result);
|
|
113
113
|
} else {
|
|
@@ -117,7 +117,9 @@ export class Regex implements Pattern {
|
|
|
117
117
|
|
|
118
118
|
private processResult(cursor: Cursor, result: RegExpExecArray) {
|
|
119
119
|
const currentIndex = cursor.index;
|
|
120
|
-
const
|
|
120
|
+
const match = result[0];
|
|
121
|
+
const matchLength = [...match].length;
|
|
122
|
+
const newIndex = currentIndex + matchLength - 1;
|
|
121
123
|
|
|
122
124
|
this._node = new Node(
|
|
123
125
|
"regex",
|
|
@@ -4,6 +4,7 @@ import { Literal } from "./Literal";
|
|
|
4
4
|
import { Node } from "../ast/Node";
|
|
5
5
|
import { Optional } from "./Optional";
|
|
6
6
|
import { Pattern } from "./Pattern";
|
|
7
|
+
import { Regex } from "./Regex";
|
|
7
8
|
|
|
8
9
|
describe("Sequence", () => {
|
|
9
10
|
test("No Patterns", () => {
|
|
@@ -334,4 +335,14 @@ describe("Sequence", () => {
|
|
|
334
335
|
expect(result.cursor.hasError).toBeFalsy();
|
|
335
336
|
});
|
|
336
337
|
|
|
338
|
+
test("Unicode Characters", ()=>{
|
|
339
|
+
const quote = new Literal("'", "'");
|
|
340
|
+
const content = new Regex("content", "[^']*");
|
|
341
|
+
const sequence = new Sequence("sequence", [quote, content, quote]);
|
|
342
|
+
const result = sequence.exec("'🔴'");
|
|
343
|
+
|
|
344
|
+
expect(result.ast).not.toBe(null);
|
|
345
|
+
expect(result.cursor.hasError).toBeFalsy();
|
|
346
|
+
})
|
|
347
|
+
|
|
337
348
|
});
|