clarity-pattern-parser 11.4.0 → 11.4.2
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_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 +73 -41
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +73 -41
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +73 -41
- package/dist/index.js.map +1 -1
- package/dist/intellisense/AutoComplete.d.ts +1 -1
- package/dist/patterns/Cursor.d.ts +6 -1
- package/dist/patterns/Literal.d.ts +0 -2
- package/package.json +1 -1
- 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/intellisense/AutoComplete.test.ts +3 -3
- package/src/intellisense/AutoComplete.ts +86 -86
- package/src/intellisense/javascript/stringLiteral.ts +2 -2
- package/src/patterns/Cursor.test.ts +10 -2
- package/src/patterns/Cursor.ts +48 -8
- package/src/patterns/Literal.test.ts +41 -1
- package/src/patterns/Literal.ts +20 -28
- package/src/patterns/Regex.ts +8 -6
- package/src/patterns/Sequence.test.ts +11 -0
- package/src/patterns/TakeUntil.ts +1 -1
- package/src/patterns/generate_error_message.ts +1 -1
- package/tsconfig.json +1 -1
|
@@ -42,7 +42,7 @@ export declare class AutoComplete {
|
|
|
42
42
|
* ie. sequence pattern segments ≈ [{look}, {an example}, {phrase}]
|
|
43
43
|
* fullText = "look an"
|
|
44
44
|
* remove {look} segment as its already been completed by the existing text.
|
|
45
|
-
|
|
45
|
+
*/
|
|
46
46
|
private _filterCompletedSubSegments;
|
|
47
47
|
private _getCompositeSuggestionsForPattern;
|
|
48
48
|
private _getCustomTokens;
|
|
@@ -4,6 +4,8 @@ import { ParseError } from "./ParseError";
|
|
|
4
4
|
import { Pattern } from "./Pattern";
|
|
5
5
|
export declare class Cursor {
|
|
6
6
|
private _text;
|
|
7
|
+
private _charSize;
|
|
8
|
+
private _charMap;
|
|
7
9
|
private _index;
|
|
8
10
|
private _length;
|
|
9
11
|
private _history;
|
|
@@ -33,10 +35,13 @@ export declare class Cursor {
|
|
|
33
35
|
moveToFirstChar(): void;
|
|
34
36
|
moveToLastChar(): void;
|
|
35
37
|
getLastIndex(): number;
|
|
36
|
-
|
|
38
|
+
substring(first: number, last: number): string;
|
|
37
39
|
recordMatch(pattern: Pattern, node: Node): void;
|
|
38
40
|
recordErrorAt(startIndex: number, lastIndex: number, onPattern: Pattern): void;
|
|
39
41
|
resolveError(): void;
|
|
40
42
|
startRecording(): void;
|
|
41
43
|
stopRecording(): void;
|
|
44
|
+
getCharStartIndex(index: number): number;
|
|
45
|
+
getCharEndIndex(index: number): number;
|
|
46
|
+
getCharLastIndex(index: number): number;
|
|
42
47
|
}
|
|
@@ -8,10 +8,8 @@ export declare class Literal implements Pattern {
|
|
|
8
8
|
private _name;
|
|
9
9
|
private _parent;
|
|
10
10
|
private _token;
|
|
11
|
-
private _runes;
|
|
12
11
|
private _firstIndex;
|
|
13
12
|
private _lastIndex;
|
|
14
|
-
private _endIndex;
|
|
15
13
|
get id(): string;
|
|
16
14
|
get type(): string;
|
|
17
15
|
get name(): string;
|
package/package.json
CHANGED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { Node } from "../../ast/Node";
|
|
2
|
+
import { tokens } from "../../grammar/decorators/tokens";
|
|
3
|
+
import { GrammarFile } from "../../grammar/Grammar";
|
|
4
|
+
import { generateErrorMessage } from "../../patterns/generate_error_message";
|
|
5
|
+
import { Pattern } from "../../patterns/Pattern";
|
|
6
|
+
import { grammar } from "./patterns/grammar"
|
|
7
|
+
|
|
8
|
+
let anonymousIndexId = 0;
|
|
9
|
+
|
|
10
|
+
function defaultImportResolver(_path: string, _basePath: string | null): Promise<GrammarFile> {
|
|
11
|
+
throw new Error("No import resolver supplied.");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function defaultImportResolverSync(_path: string, _basePath: string | null): GrammarFile {
|
|
15
|
+
throw new Error("No import resolver supplied.");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type Decorator = (pattern: Pattern, arg?: string | boolean | number | null | Record<string, any> | any[]) => void;
|
|
19
|
+
|
|
20
|
+
const defaultDecorators = {
|
|
21
|
+
tokens: tokens
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export interface GrammarOptions {
|
|
25
|
+
resolveImport?: (resource: string, originResource: string | null) => Promise<GrammarFile>;
|
|
26
|
+
resolveImportSync?: (resource: string, originResource: string | null) => GrammarFile;
|
|
27
|
+
originResource?: string | null;
|
|
28
|
+
params?: Pattern[];
|
|
29
|
+
decorators?: Record<string, Decorator>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class ParseContext {
|
|
33
|
+
patternsByName = new Map<string, Pattern>();
|
|
34
|
+
importedPatternsByName = new Map<string, Pattern>();
|
|
35
|
+
paramsByName = new Map<string, Pattern>();
|
|
36
|
+
decorators: Record<string, Decorator>;
|
|
37
|
+
|
|
38
|
+
constructor(params: Pattern[], decorators: Record<string, Decorator> = {}) {
|
|
39
|
+
params.forEach(p => this.paramsByName.set(p.name, p));
|
|
40
|
+
this.decorators = { ...decorators, ...defaultDecorators };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getImportedPatterns() {
|
|
44
|
+
return Array.from(this.importedPatternsByName.values());
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getParams() {
|
|
48
|
+
return Array.from(this.paramsByName.values());
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class Grammar {
|
|
54
|
+
private _options: GrammarOptions;
|
|
55
|
+
private _parseContext: ParseContext;
|
|
56
|
+
private _resolveImportSync: (resource: string, originResource: string | null) => GrammarFile;
|
|
57
|
+
|
|
58
|
+
constructor(options: GrammarOptions) {
|
|
59
|
+
this._options = options;
|
|
60
|
+
this._parseContext = new ParseContext(options.params || [], options.decorators || {});
|
|
61
|
+
this._resolveImportSync = options.resolveImportSync == null ? defaultImportResolverSync : options.resolveImportSync;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// parse(cpat: string): Promise<Pattern> { }
|
|
65
|
+
// import(path: string): Promise<Pattern> { }
|
|
66
|
+
|
|
67
|
+
// parseString(cpat: string): Pattern {
|
|
68
|
+
// const ast = this._tryToParse(cpat);
|
|
69
|
+
|
|
70
|
+
// this._flattenExpressions(ast);
|
|
71
|
+
|
|
72
|
+
// }
|
|
73
|
+
|
|
74
|
+
private _resolveImportsSync(ast: Node) {
|
|
75
|
+
const importStatements = ast.findAll(n => {
|
|
76
|
+
return n.name === "importStatement" || n.name === "useParamsStatement";
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
for (const importStatement of importStatements) {
|
|
80
|
+
if (importStatement.name === "importStatement") {
|
|
81
|
+
this._processImportSync(importStatement);
|
|
82
|
+
} else {
|
|
83
|
+
this._processUseParams(importStatement);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private _processImportSync(importStatement: Node) {
|
|
89
|
+
const parseContext = this._parseContext;
|
|
90
|
+
const resourceNode = importStatement.find(n => n.name === "resource") as Node;
|
|
91
|
+
const params = this._getWithParams(importStatement);
|
|
92
|
+
const resource = resourceNode.value.slice(1, -1);
|
|
93
|
+
const grammarFile = this._resolveImportSync(resource, this._options.originResource || null);
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if (resourceNode == null) {
|
|
97
|
+
throw new Error("Invalid import statement: resource is required");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private _getWithParams(importStatement: Node) {
|
|
104
|
+
const withParamsStatements = importStatement.find(n => n.name === "withParamsStatements");
|
|
105
|
+
|
|
106
|
+
if (withParamsStatements == null) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const expression = withParamsStatements.toString();
|
|
111
|
+
const importedValues = this._parseContext.getImportedPatterns();
|
|
112
|
+
|
|
113
|
+
const grammar = new Grammar({
|
|
114
|
+
params: [...importedValues, ...this._parseContext.paramsByName.values()],
|
|
115
|
+
originResource: this._options.originResource,
|
|
116
|
+
resolveImport: this._options.resolveImport,
|
|
117
|
+
decorators: this._parseContext.decorators
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// const patterns = grammar.parseString(expression);
|
|
121
|
+
// return Array.from(Object.values(patterns));
|
|
122
|
+
return[]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
private _processUseParams(useParamsStatement: Node) {
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private _tryToParse(cpat: string): Node {
|
|
130
|
+
const { ast, cursor } = grammar.exec(cpat, true);
|
|
131
|
+
|
|
132
|
+
if (ast == null) {
|
|
133
|
+
const message = generateErrorMessage(grammar, cursor);
|
|
134
|
+
throw new Error(`[Invalid Grammar] ${message}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return ast;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
private _flattenExpressions(node: Node) {
|
|
142
|
+
switch (node.name) {
|
|
143
|
+
case "sequenceExpr":
|
|
144
|
+
return this._unwrapNode("sequenceExpr", node);
|
|
145
|
+
case "optionsExpr":
|
|
146
|
+
return this._unwrapNode("optionsExpr", node);
|
|
147
|
+
case "greedyOptionsExpr":
|
|
148
|
+
return this._unwrapNode("greedyOptionsExpr", node);
|
|
149
|
+
default:
|
|
150
|
+
return node;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private _unwrapNode(type: string, node: Node) {
|
|
155
|
+
if (node.name !== type) {
|
|
156
|
+
return node;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
for (let x = 0; x < node.children.length; x++) {
|
|
160
|
+
const child = node.children[x];
|
|
161
|
+
|
|
162
|
+
if (child.name === type) {
|
|
163
|
+
node.spliceChildren(x, 1, ...child.children);
|
|
164
|
+
x--;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return node;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# syntax
|
|
2
|
+
syntax = "syntax"
|
|
3
|
+
import = "import"
|
|
4
|
+
useParams = "use params"
|
|
5
|
+
withParams = "with params"
|
|
6
|
+
from = "from"
|
|
7
|
+
right = "right"
|
|
8
|
+
ws = /\s+/
|
|
9
|
+
ls = /[ \t]+/
|
|
10
|
+
assign = "="
|
|
11
|
+
bar = "|"
|
|
12
|
+
concat = "+"
|
|
13
|
+
colon = ":"
|
|
14
|
+
openParen = "("
|
|
15
|
+
closeParen = ")"
|
|
16
|
+
openSquareBracket = "["
|
|
17
|
+
closeSquareBracket = "]"
|
|
18
|
+
openBracket = "{"
|
|
19
|
+
closeBracket = "}"
|
|
20
|
+
comma = /\s*[,]\s*/
|
|
21
|
+
trim = "trim"
|
|
22
|
+
not = "!"
|
|
23
|
+
optional = "?"
|
|
24
|
+
newLine = /\s*(\r\n|\r|\n)\s*/
|
|
25
|
+
syntaxVersion = /\S*/
|
|
26
|
+
at = "@"
|
|
27
|
+
|
|
28
|
+
literal = /"(?:\\.|[^"\\])*"/
|
|
29
|
+
regexLiteral = /\/(\\/|[^\n\r])*\//
|
|
30
|
+
|
|
31
|
+
integer = /[1-9][0-9]*|[0]/
|
|
32
|
+
name = /[a-zA-Z_-]+[a-zA-Z0-9_-]*/
|
|
33
|
+
patternName = name
|
|
34
|
+
patternIdentifier = name
|
|
35
|
+
resource = literal
|
|
36
|
+
|
|
37
|
+
comment = /#[^\n\r]*/
|
|
38
|
+
|
|
39
|
+
# JSON
|
|
40
|
+
jsonString = literal
|
|
41
|
+
jsonNumber = /-?\d+(\.\d+)?/
|
|
42
|
+
jsonBoolean = "true" | "false"
|
|
43
|
+
jsonNull = "null"
|
|
44
|
+
jsonValue = jsonString | jsonNumber | jsonBoolean | jsonNull | jsonArray | jsonObject
|
|
45
|
+
jsonArrayItems = (jsonValue, comma trim)+
|
|
46
|
+
jsonArray = openSquareBracket +ws? + jsonArrayItems? + ws? + closeSquareBracket
|
|
47
|
+
jsonObjectPropertyName = literal
|
|
48
|
+
jsonObjectProperty = jsonObjectPropertyName + ws? + colon + ws? + jsonValue
|
|
49
|
+
jsonObjectProperties = (jsonObjectProperty, comma trim)+
|
|
50
|
+
jsonObject = openBracket + ws? + jsonObjectProperties? + ws? + closeBracket
|
|
51
|
+
|
|
52
|
+
syntaxStatement = syntax + ws? + syntaxVersion
|
|
53
|
+
|
|
54
|
+
decorationName = name
|
|
55
|
+
decorationStatement = at + ws? + decorationName + ws? + openParen + ws? + jsonValue? + ws? + closeParen
|
|
56
|
+
|
|
57
|
+
useParamPatterns = (patternName, comma trim)+
|
|
58
|
+
useParamsStatement = useParams + ls? + openBracket + ws? + useParamPatterns + ws? + closeBracket
|
|
59
|
+
|
|
60
|
+
withParamStatements = (patternAssignment, newLine trim)+
|
|
61
|
+
withParamsExpr = withParams + ls? + openBracket + ws? + withParamStatements + ws? + closeBracket
|
|
62
|
+
|
|
63
|
+
patternNames = (patternName, comma trim)+
|
|
64
|
+
importedPatterns = openBracket + ws? + patternNames + ws? + closeBracket
|
|
65
|
+
importStatement = import + ls? + importedPatterns + ls? + from + ls? + resource + ls? + withParamsExpr?
|
|
66
|
+
|
|
67
|
+
notExpr = not + ls? + unaryPatternExpr
|
|
68
|
+
optionalExpr = unaryPatternExpr + ls? + optional
|
|
69
|
+
rightAssociationExpr = unaryPatternExpr + ls? + right
|
|
70
|
+
|
|
71
|
+
unaryPatternExpr = notExpr | optionalExpr | rightAssociationExpr | patternIdentifier
|
|
72
|
+
|
|
73
|
+
repeatBounds = openBracket + integer? + comma? + integer? + closeBracket
|
|
74
|
+
oneOrMore = "+"
|
|
75
|
+
zeroOrMore = "*"
|
|
76
|
+
repeatOptions = oneOrMore | zeroOrMore | repeatBounds
|
|
77
|
+
delimiter = comma + patternExpr + ws? + trim?
|
|
78
|
+
repeatExpr = openParen + ws? + patternExpr + ws? + delimiter? + ws? + closeParen + repeatOptions
|
|
79
|
+
|
|
80
|
+
sequenceExpr = patternExpr + ws? + "+" + ws? + patternExpr
|
|
81
|
+
optionsExpr = patternExpr + ws? + "|" + ws? + patternExpr
|
|
82
|
+
greedyOptionsExpr = patternExpr + ws? + "<|>" + ws? + patternExpr
|
|
83
|
+
patternGroupExpr = openParen + ws? + patternExpr + ws? + closeParen
|
|
84
|
+
|
|
85
|
+
exportPattern = patternName
|
|
86
|
+
patternExpr = sequenceExpr | optionsExpr | greedyOptionsExpr | repeatExpr | patternGroupExpr | literal | regexLiteral | unaryPatternExpr
|
|
87
|
+
patternAssignment = patternName + ws? + assign + ws? + patternExpr
|
|
88
|
+
statement = useParamsStatement | importStatement | patternAssignment | decorationStatement | exportPattern | comment
|
|
89
|
+
statements = (statement, newLine)+
|
|
90
|
+
cpat = ws? + syntaxStatement? + ws? + statements?
|
|
91
|
+
|
|
@@ -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;
|
|
@@ -194,7 +194,7 @@ describe("AutoComplete", () => {
|
|
|
194
194
|
|
|
195
195
|
expect(result.ast).toBeNull();
|
|
196
196
|
optionsMatchExpected(result.options, expectedOptions);
|
|
197
|
-
expect(result.errorAtIndex).toBe(text.length);
|
|
197
|
+
expect(result.errorAtIndex).toBe(text.length - 1);
|
|
198
198
|
expect(result.isComplete).toBeFalsy();
|
|
199
199
|
expect(result.cursor).not.toBeNull();
|
|
200
200
|
});
|
|
@@ -313,7 +313,7 @@ describe("AutoComplete", () => {
|
|
|
313
313
|
|
|
314
314
|
expect(result.ast).toBeNull();
|
|
315
315
|
optionsMatchExpected(result.options, expectedOptions);
|
|
316
|
-
expect(result.errorAtIndex).toBe(
|
|
316
|
+
expect(result.errorAtIndex).toBe(1);
|
|
317
317
|
expect(result.isComplete).toBeFalsy();
|
|
318
318
|
expect(result.cursor).not.toBeNull();
|
|
319
319
|
});
|
|
@@ -944,7 +944,7 @@ describe("AutoComplete", () => {
|
|
|
944
944
|
|
|
945
945
|
optionsMatchExpected(suggestion.options, expected);
|
|
946
946
|
|
|
947
|
-
expect(suggestion.error?.lastIndex).toBe(
|
|
947
|
+
expect(suggestion.error?.lastIndex).toBe(1);
|
|
948
948
|
});
|
|
949
949
|
|
|
950
950
|
test("Recursion With And", () => {
|