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.
@@ -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;
@@ -4,13 +4,13 @@ import { ParseError } from "./ParseError";
4
4
  import { Pattern } from "./Pattern";
5
5
 
6
6
  export class Cursor {
7
- private _text: string;
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._text;
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._text[this._index];
77
+ return this._chars[this._index];
78
78
  }
79
79
 
80
80
  constructor(text: string) {
81
- this._text = text;
81
+ this._chars = [...text];
82
82
  this._index = 0;
83
- this._length = [...text].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._text.slice(first, last + 1);
126
+ return this._chars.slice(first, last + 1).join("");
127
127
  }
128
128
 
129
129
  recordMatch(pattern: Pattern, node: Node): void {
@@ -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 newIndex = currentIndex + result[0].length - 1;
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
  });