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.
@@ -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
- getChars(first: number, last: number): string;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clarity-pattern-parser",
3
- "version": "11.4.0",
3
+ "version": "11.4.2",
4
4
  "description": "Parsing Library for Typescript and Javascript.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.esm.js",
@@ -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(2);
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(2);
947
+ expect(suggestion.error?.lastIndex).toBe(1);
948
948
  });
949
949
 
950
950
  test("Recursion With And", () => {