clarity-pattern-parser 4.0.3 → 6.0.0
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/README.md +466 -1
- package/TODO.md +76 -2
- package/dist/ast/Node.d.ts +49 -11
- package/dist/ast/Visitor.d.ts +31 -31
- package/dist/index.browser.js +1513 -1495
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +17 -17
- package/dist/index.esm.js +1480 -1459
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1481 -1463
- package/dist/index.js.map +1 -1
- package/dist/intellisense/AutoComplete.d.ts +28 -0
- package/dist/intellisense/Suggestion.d.ts +11 -0
- package/dist/intellisense/SuggestionOption.d.ts +4 -0
- package/dist/patterns/And.d.ts +37 -24
- package/dist/patterns/Cursor.d.ts +37 -0
- package/dist/patterns/CursorHistory.d.ts +30 -0
- package/dist/patterns/Literal.d.ts +35 -19
- package/dist/patterns/Not.d.ts +29 -11
- package/dist/patterns/Or.d.ts +33 -22
- package/dist/patterns/ParseError.d.ts +6 -8
- package/dist/patterns/ParseResult.d.ts +6 -0
- package/dist/patterns/Pattern.d.ts +20 -26
- package/dist/patterns/Reference.d.ts +34 -12
- package/dist/patterns/Regex.d.ts +41 -21
- package/dist/patterns/Repeat.d.ts +38 -20
- package/dist/patterns/clonePatterns.d.ts +2 -0
- package/dist/patterns/filterOutNull.d.ts +2 -0
- package/dist/patterns/findPattern.d.ts +2 -0
- package/dist/patterns/getNextPattern.d.ts +2 -0
- package/jest.config.js +2 -1
- package/package.json +4 -5
- package/rollup.config.js +1 -1
- package/src/ast/Node.test.ts +364 -0
- package/src/ast/Node.ts +237 -23
- package/src/index.ts +25 -27
- package/src/intellisense/AutoComplete.test.ts +150 -0
- package/src/intellisense/AutoComplete.ts +200 -0
- package/src/intellisense/Suggestion.ts +12 -0
- package/src/intellisense/SuggestionOption.ts +4 -0
- package/src/{tests/cssPatterns → intellisense/css}/cssValue.ts +1 -1
- package/src/{tests/cssPatterns → intellisense/css}/divider.ts +2 -1
- package/src/intellisense/css/hex.ts +6 -0
- package/src/{tests/cssPatterns → intellisense/css}/method.ts +8 -9
- package/src/intellisense/css/name.ts +5 -0
- package/src/{tests/javascriptPatterns → intellisense/css}/number.ts +3 -3
- package/src/intellisense/css/spaces.ts +6 -0
- package/src/intellisense/css/unit.ts +10 -0
- package/src/{tests/cssPatterns → intellisense/css}/value.ts +1 -1
- package/src/{tests/cssPatterns → intellisense/css}/values.ts +1 -1
- package/src/intellisense/javascript/Javascript.test.ts +203 -0
- package/src/intellisense/javascript/arrayLiteral.ts +25 -0
- package/src/intellisense/javascript/deleteStatement.ts +14 -0
- package/src/intellisense/javascript/escapedCharacter.ts +49 -0
- package/src/intellisense/javascript/exponent.ts +24 -0
- package/src/intellisense/javascript/expression.ts +87 -0
- package/src/intellisense/javascript/expressionStatement.ts +29 -0
- package/src/intellisense/javascript/fraction.ts +11 -0
- package/src/intellisense/javascript/infixOperator.ts +36 -0
- package/src/intellisense/javascript/integer.ts +7 -0
- package/src/intellisense/javascript/invocation.ts +28 -0
- package/src/intellisense/javascript/literal.ts +14 -0
- package/src/intellisense/javascript/name.ts +3 -0
- package/src/intellisense/javascript/numberLiteral.ts +10 -0
- package/src/intellisense/javascript/objectLiteral.ts +30 -0
- package/src/intellisense/javascript/optionalSpaces.ts +3 -0
- package/src/intellisense/javascript/parameters.ts +20 -0
- package/src/intellisense/javascript/prefixOperator.ts +13 -0
- package/src/intellisense/javascript/propertyAccess.ts +23 -0
- package/src/intellisense/javascript/stringLiteral.ts +28 -0
- package/src/patterns/And.test.ts +310 -0
- package/src/patterns/And.ts +244 -119
- package/src/patterns/Cursor.test.ts +93 -0
- package/src/patterns/Cursor.ts +133 -0
- package/src/patterns/CursorHistory.test.ts +54 -0
- package/src/patterns/CursorHistory.ts +95 -0
- package/src/patterns/Literal.test.ts +166 -0
- package/src/patterns/Literal.ts +141 -62
- package/src/patterns/Not.test.ts +168 -0
- package/src/patterns/Not.ts +113 -32
- package/src/patterns/Or.test.ts +209 -0
- package/src/patterns/Or.ts +128 -97
- package/src/patterns/ParseError.ts +3 -7
- package/src/patterns/ParseResult.ts +7 -0
- package/src/patterns/Pattern.ts +21 -150
- package/src/patterns/Reference.test.ts +193 -0
- package/src/patterns/Reference.ts +114 -88
- package/src/patterns/Regex.test.ts +133 -0
- package/src/patterns/Regex.ts +117 -60
- package/src/patterns/Repeat.test.ts +218 -0
- package/src/patterns/Repeat.ts +220 -103
- package/src/patterns/clonePatterns.ts +5 -0
- package/src/patterns/filterOutNull.ts +13 -0
- package/src/patterns/findPattern.ts +25 -0
- package/src/Cursor.ts +0 -141
- package/src/CursorHistory.ts +0 -146
- package/src/TextSuggester.ts +0 -317
- package/src/ast/Visitor.ts +0 -271
- package/src/patterns/LookAhead.ts +0 -32
- package/src/patterns/Recursive.ts +0 -92
- package/src/tests/And.test.ts +0 -180
- package/src/tests/ComplexExamples.test.ts +0 -86
- package/src/tests/CssPatterns.test.ts +0 -90
- package/src/tests/CursorHistory.test.ts +0 -107
- package/src/tests/Cusor.test.ts +0 -174
- package/src/tests/HtmlPatterns.test.ts +0 -34
- package/src/tests/Literal.test.ts +0 -79
- package/src/tests/LookAhead.test.ts +0 -44
- package/src/tests/Not.test.ts +0 -51
- package/src/tests/Or.test.ts +0 -113
- package/src/tests/Pattern.test.ts +0 -290
- package/src/tests/Recursive.test.ts +0 -64
- package/src/tests/Reference.test.ts +0 -16
- package/src/tests/Repeat.test.ts +0 -75
- package/src/tests/SpeedTest.test.ts +0 -31
- package/src/tests/TextSuggester.test.ts +0 -297
- package/src/tests/Visitor.test.ts +0 -331
- package/src/tests/cssPatterns/hex.ts +0 -5
- package/src/tests/cssPatterns/name.ts +0 -5
- package/src/tests/cssPatterns/number.ts +0 -8
- package/src/tests/cssPatterns/spaces.ts +0 -5
- package/src/tests/cssPatterns/unit.ts +0 -8
- package/src/tests/htmlPatterns/element.ts +0 -49
- package/src/tests/javascriptPatterns/boolean.ts +0 -10
- package/src/tests/javascriptPatterns/json.ts +0 -67
- package/src/tests/javascriptPatterns/name.ts +0 -5
- package/src/tests/javascriptPatterns/objectLiteral.ts +0 -40
- package/src/tests/javascriptPatterns/string.ts +0 -84
- package/src/tests/javascriptPatterns/unit.ts +0 -8
- package/src/tests/javascriptPatterns/whitespace.ts +0 -44
- package/src/tests/naturalLanguage/filter.ts +0 -37
- package/src/tests/patterns/sentence.ts +0 -37
- /package/src/{tests/cssPatterns → intellisense/css}/optionalSpaces.ts +0 -0
|
@@ -1,127 +1,153 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
1
|
+
import { Node } from "../ast/Node";
|
|
2
|
+
import { Cursor } from "./Cursor";
|
|
3
|
+
import { Pattern } from "./Pattern";
|
|
4
|
+
import { findPattern } from "./findPattern";
|
|
5
|
+
|
|
6
|
+
export class Reference implements Pattern {
|
|
7
|
+
private _type: string;
|
|
8
|
+
private _name: string;
|
|
9
|
+
private _parent: Pattern | null;
|
|
10
|
+
private _isOptional: boolean;
|
|
11
|
+
private _pattern: Pattern | null;
|
|
12
|
+
private _children: Pattern[];
|
|
13
|
+
|
|
14
|
+
get type(): string {
|
|
15
|
+
return this._type;
|
|
16
|
+
}
|
|
4
17
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
super("reference", name, [], isOptional);
|
|
18
|
+
get name(): string {
|
|
19
|
+
return this._name;
|
|
8
20
|
}
|
|
9
21
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
while (node != null) {
|
|
13
|
-
if (node.parent == null) {
|
|
14
|
-
return node;
|
|
15
|
-
}
|
|
16
|
-
node = node.parent;
|
|
17
|
-
}
|
|
18
|
-
return node;
|
|
22
|
+
get parent(): Pattern | null {
|
|
23
|
+
return this._parent;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
set parent(pattern: Pattern | null) {
|
|
27
|
+
this._parent = pattern;
|
|
28
|
+
}
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
get children(): Pattern[] {
|
|
31
|
+
return this._children
|
|
32
|
+
}
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
pattern != this &&
|
|
33
|
-
pattern.type != "reference"
|
|
34
|
-
) {
|
|
35
|
-
result = pattern;
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
return true;
|
|
39
|
-
});
|
|
34
|
+
get isOptional(): boolean {
|
|
35
|
+
return this._isOptional;
|
|
36
|
+
}
|
|
40
37
|
|
|
41
|
-
|
|
38
|
+
constructor(name: string, isOptional: boolean = false) {
|
|
39
|
+
this._type = "reference";
|
|
40
|
+
this._name = name;
|
|
41
|
+
this._parent = null;
|
|
42
|
+
this._isOptional = isOptional;
|
|
43
|
+
this._pattern = null;
|
|
44
|
+
this._children = [];
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
) {
|
|
48
|
-
for (let x = 0; x < pattern.children.length; x++) {
|
|
49
|
-
const p = pattern.children[x];
|
|
50
|
-
const continueWalking = this.walkTheTree(p, callback);
|
|
47
|
+
test(text: string) {
|
|
48
|
+
const cursor = new Cursor(text);
|
|
49
|
+
const ast = this.parse(cursor);
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
return ast?.value === text;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
exec(text: string) {
|
|
55
|
+
const cursor = new Cursor(text);
|
|
56
|
+
const ast = this.parse(cursor);
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
ast: ast?.value === text ? ast : null,
|
|
60
|
+
cursor
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
parse(cursor: Cursor): Node | null {
|
|
65
|
+
return this._getPatternSafely().parse(cursor);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private _getPatternSafely(): Pattern {
|
|
69
|
+
if (this._pattern === null) {
|
|
70
|
+
const pattern = this._findPattern();
|
|
71
|
+
|
|
72
|
+
if (pattern === null) {
|
|
73
|
+
throw new Error(`Couldn't find '${this._name}' pattern within tree.`);
|
|
54
74
|
}
|
|
75
|
+
|
|
76
|
+
const clonedPattern = pattern.clone(this._name, this._isOptional);
|
|
77
|
+
clonedPattern.parent = this;
|
|
78
|
+
|
|
79
|
+
this._pattern = clonedPattern;
|
|
80
|
+
this._children = [this._pattern];
|
|
55
81
|
}
|
|
56
82
|
|
|
57
|
-
return
|
|
83
|
+
return this._pattern;
|
|
58
84
|
}
|
|
59
85
|
|
|
60
|
-
|
|
61
|
-
const
|
|
86
|
+
private _findPattern(): Pattern | null {
|
|
87
|
+
const root = this._getRoot();
|
|
62
88
|
|
|
63
|
-
|
|
64
|
-
|
|
89
|
+
return findPattern(root, (pattern: Pattern) => {
|
|
90
|
+
return pattern.name === this._name && pattern.type !== "reference";
|
|
91
|
+
});
|
|
92
|
+
}
|
|
65
93
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
94
|
+
private _getRoot(): Pattern {
|
|
95
|
+
let node: Pattern = this;
|
|
69
96
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
cursor.moveToMark(mark);
|
|
73
|
-
}
|
|
97
|
+
while (true) {
|
|
98
|
+
const parent = node.parent;
|
|
74
99
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (this._isOptional) {
|
|
78
|
-
cursor.moveToMark(mark);
|
|
100
|
+
if (parent == null) {
|
|
101
|
+
break;
|
|
79
102
|
} else {
|
|
80
|
-
|
|
81
|
-
new ParserError(
|
|
82
|
-
`Couldn't find reference pattern to parse, with the name ${this.name}.`,
|
|
83
|
-
cursor.index,
|
|
84
|
-
this as Pattern
|
|
85
|
-
)
|
|
86
|
-
);
|
|
103
|
+
node = parent
|
|
87
104
|
}
|
|
88
|
-
|
|
89
|
-
return null;
|
|
90
105
|
}
|
|
106
|
+
|
|
107
|
+
return node;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
getTokens(): string[] {
|
|
111
|
+
return this._getPatternSafely().getTokens();
|
|
91
112
|
}
|
|
92
113
|
|
|
93
|
-
|
|
94
|
-
if (
|
|
95
|
-
|
|
114
|
+
getTokensAfter(_lastMatched: Pattern): string[] {
|
|
115
|
+
if (this._parent == null) {
|
|
116
|
+
return [];
|
|
96
117
|
}
|
|
97
118
|
|
|
98
|
-
|
|
99
|
-
|
|
119
|
+
return this._parent.getTokensAfter(this);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
getNextTokens(): string[] {
|
|
123
|
+
if (this.parent == null) {
|
|
124
|
+
return []
|
|
100
125
|
}
|
|
101
126
|
|
|
102
|
-
return
|
|
127
|
+
return this.parent.getTokensAfter(this);
|
|
103
128
|
}
|
|
104
129
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
130
|
+
getPatternsAfter(_childReference: Pattern): Pattern[] {
|
|
131
|
+
if (this._parent == null) {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
108
134
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (reference == null) {
|
|
112
|
-
throw new Error(
|
|
113
|
-
`Couldn't find reference pattern, with the name ${this.name}.`
|
|
114
|
-
);
|
|
115
|
-
}
|
|
135
|
+
return this._parent.getPatternsAfter(this);
|
|
136
|
+
}
|
|
116
137
|
|
|
117
|
-
|
|
118
|
-
|
|
138
|
+
getNextPatterns(): Pattern[] {
|
|
139
|
+
if (this.parent == null) {
|
|
140
|
+
return [];
|
|
119
141
|
}
|
|
120
142
|
|
|
121
|
-
return
|
|
143
|
+
return this.parent.getPatternsAfter(this)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
findPattern(_predicate: (p: Pattern) => boolean): Pattern | null {
|
|
147
|
+
return null;
|
|
122
148
|
}
|
|
123
149
|
|
|
124
|
-
|
|
125
|
-
return
|
|
150
|
+
clone(name = this._name, isOptional = this._isOptional): Pattern {
|
|
151
|
+
return new Reference(name, isOptional);
|
|
126
152
|
}
|
|
127
153
|
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Cursor } from "./Cursor";
|
|
2
|
+
import { Regex } from "./Regex";
|
|
3
|
+
import { Node } from "../ast/Node"
|
|
4
|
+
import { And } from "./And";
|
|
5
|
+
import { Literal } from "./Literal";
|
|
6
|
+
import { Pattern } from "./Pattern";
|
|
7
|
+
|
|
8
|
+
describe("Regex", () => {
|
|
9
|
+
test("Empty String", () => {
|
|
10
|
+
expect(() => new Regex("empty", "")).toThrowError()
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("Starts With ^", () => {
|
|
14
|
+
expect(() => new Regex("carrot", "^")).toThrowError()
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("Ends With $", () => {
|
|
18
|
+
expect(() => new Regex("money", ".$")).toThrowError()
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("Successful Parse", () => {
|
|
22
|
+
const number = new Regex("number", "\\d");
|
|
23
|
+
const cursor = new Cursor("1");
|
|
24
|
+
const result = number.parse(cursor);
|
|
25
|
+
const expected = new Node("regex", "number", 0, 0, [], "1");
|
|
26
|
+
|
|
27
|
+
expect(result).toEqual(expected);
|
|
28
|
+
expect(cursor.hasError).toBeFalsy()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
test("Failed Parse", () => {
|
|
32
|
+
const number = new Regex("number", "\\d");
|
|
33
|
+
const cursor = new Cursor("F");
|
|
34
|
+
const result = number.parse(cursor);
|
|
35
|
+
|
|
36
|
+
expect(result).toBeNull();
|
|
37
|
+
expect(cursor.hasError).toBeTruthy()
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
test("Get Tokens", () => {
|
|
42
|
+
const a = new Regex("a", "A");
|
|
43
|
+
|
|
44
|
+
a.setTokens(["A"]);
|
|
45
|
+
|
|
46
|
+
const tokens = a.getTokens();
|
|
47
|
+
const expectedTokens = ["A"];
|
|
48
|
+
|
|
49
|
+
expect(tokens).toEqual(expectedTokens);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("Get Tokens After", () => {
|
|
53
|
+
const regex = new Regex("a", "A");
|
|
54
|
+
const tokens = regex.getTokensAfter(new Literal("bogus", "bogus"));
|
|
55
|
+
const expected: string[] = [];
|
|
56
|
+
|
|
57
|
+
expect(tokens).toEqual(expected)
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("Properties", () => {
|
|
61
|
+
const regex = new Regex("a", "A");
|
|
62
|
+
|
|
63
|
+
expect(regex.type).toBe("regex");
|
|
64
|
+
expect(regex.name).toBe("a");
|
|
65
|
+
expect(regex.parent).toBeNull();
|
|
66
|
+
expect(regex.children).toEqual([]);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("Exec", () => {
|
|
70
|
+
const regex = new Regex("a", "A");
|
|
71
|
+
const { ast: result } = regex.exec("B");
|
|
72
|
+
expect(result).toBeNull();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("Test With Match", () => {
|
|
76
|
+
const regex = new Regex("a", "A");
|
|
77
|
+
const result = regex.test("A");
|
|
78
|
+
expect(result).toBeTruthy();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("Test With No Match", () => {
|
|
82
|
+
const regex = new Regex("a", "A");
|
|
83
|
+
const result = regex.test("B");
|
|
84
|
+
expect(result).toBeFalsy();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("Get Next Tokens", () => {
|
|
88
|
+
const parent = new And("parent", [new Regex("a", "A"), new Literal("b", "B")]);
|
|
89
|
+
const aClone = parent.findPattern(p => p.name === "a") as Pattern;
|
|
90
|
+
const tokens = aClone.getNextTokens();
|
|
91
|
+
|
|
92
|
+
expect(tokens).toEqual(["B"]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("Get Next Tokens With Null Parent", () => {
|
|
96
|
+
const a = new Regex("a", "A")
|
|
97
|
+
const tokens = a.getNextTokens();
|
|
98
|
+
|
|
99
|
+
expect(tokens).toEqual([]);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("Get Patterns After", () => {
|
|
103
|
+
const a = new Regex("a", "A")
|
|
104
|
+
const patterns = a.getPatternsAfter(new Literal("bogus", "bogus"));
|
|
105
|
+
|
|
106
|
+
expect(patterns).toEqual([]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("Find Pattern", () => {
|
|
110
|
+
const a = new Regex("a", "A")
|
|
111
|
+
const pattern = a.findPattern(p => p.name === "other");
|
|
112
|
+
|
|
113
|
+
expect(pattern).toBeNull();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("Get Next Patterns", () => {
|
|
117
|
+
const parent = new And("parent", [new Regex("a", "A"), new Literal("b", "B")]);
|
|
118
|
+
const aClone = parent.findPattern(p => p.name === "a") as Pattern;
|
|
119
|
+
const bClone = parent.findPattern(p => p.name === "b") as Pattern;
|
|
120
|
+
const patterns = aClone.getNextPatterns();
|
|
121
|
+
|
|
122
|
+
expect(patterns.length).toBe(1);
|
|
123
|
+
expect(patterns[0]).toBe(bClone);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("Get Next Patterns With Null Parent", () => {
|
|
127
|
+
const a = new Regex("a", "A")
|
|
128
|
+
const patterns = a.getNextPatterns();
|
|
129
|
+
|
|
130
|
+
expect(patterns).toEqual([])
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
});
|
package/src/patterns/Regex.ts
CHANGED
|
@@ -1,123 +1,180 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
private
|
|
8
|
-
private
|
|
9
|
-
private
|
|
10
|
-
private
|
|
11
|
-
private
|
|
1
|
+
import { Node } from "../ast/Node";
|
|
2
|
+
import { Pattern } from "./Pattern";
|
|
3
|
+
import { Cursor } from "./Cursor";
|
|
4
|
+
|
|
5
|
+
export class Regex implements Pattern {
|
|
6
|
+
private _type: string;
|
|
7
|
+
private _name: string;
|
|
8
|
+
private _isOptional: boolean;
|
|
9
|
+
private _parent: Pattern | null;
|
|
10
|
+
private _originalRegexString: string;
|
|
11
|
+
private _regex: RegExp;
|
|
12
|
+
private _node: Node | null = null;
|
|
13
|
+
private _cursor: Cursor | null = null;
|
|
14
|
+
private _substring: string = "";
|
|
15
|
+
private _tokens: string[] = [];
|
|
16
|
+
|
|
17
|
+
get type(): string {
|
|
18
|
+
return this._type;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get name(): string {
|
|
22
|
+
return this._name;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get parent(): Pattern | null {
|
|
26
|
+
return this._parent;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
set parent(pattern: Pattern | null) {
|
|
30
|
+
this._parent = pattern;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get children(): Pattern[] {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get isOptional(): boolean {
|
|
38
|
+
return this._isOptional;
|
|
39
|
+
}
|
|
12
40
|
|
|
13
41
|
constructor(name: string, regex: string, isOptional = false) {
|
|
14
|
-
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
42
|
+
this._type = "regex"
|
|
43
|
+
this._name = name;
|
|
44
|
+
this._isOptional = isOptional;
|
|
45
|
+
this._parent = null;
|
|
46
|
+
this._originalRegexString = regex;
|
|
47
|
+
this._regex = new RegExp(`^${regex}`, "g");
|
|
17
48
|
this.assertArguments();
|
|
18
49
|
}
|
|
19
50
|
|
|
20
51
|
private assertArguments() {
|
|
21
|
-
if (this.
|
|
52
|
+
if (this._originalRegexString.length < 1) {
|
|
22
53
|
throw new Error(
|
|
23
54
|
"Invalid Arguments: The regex string argument needs to be at least one character long."
|
|
24
55
|
);
|
|
25
56
|
}
|
|
26
57
|
|
|
27
|
-
if (this.
|
|
58
|
+
if (this._originalRegexString.charAt(0) === "^") {
|
|
28
59
|
throw new Error(
|
|
29
60
|
"Invalid Arguments: The regex string cannot start with a '^' because it is expected to be in the middle of a string."
|
|
30
61
|
);
|
|
31
62
|
}
|
|
32
63
|
|
|
33
|
-
if (this.
|
|
64
|
+
if (this._originalRegexString.charAt(this._originalRegexString.length - 1) === "$") {
|
|
34
65
|
throw new Error(
|
|
35
66
|
"Invalid Arguments: The regex string cannot end with a '$' because it is expected to be in the middle of a string."
|
|
36
67
|
);
|
|
37
68
|
}
|
|
38
69
|
}
|
|
39
70
|
|
|
71
|
+
test(text: string) {
|
|
72
|
+
const cursor = new Cursor(text);
|
|
73
|
+
const ast = this.parse(cursor);
|
|
74
|
+
|
|
75
|
+
return ast?.value === text;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
exec(text: string) {
|
|
79
|
+
const cursor = new Cursor(text);
|
|
80
|
+
const ast = this.parse(cursor);
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
ast: ast?.value === text ? ast : null,
|
|
84
|
+
cursor
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
40
88
|
parse(cursor: Cursor) {
|
|
41
89
|
this.resetState(cursor);
|
|
42
|
-
this.tryToParse();
|
|
90
|
+
this.tryToParse(cursor);
|
|
43
91
|
|
|
44
|
-
return this.
|
|
92
|
+
return this._node;
|
|
45
93
|
}
|
|
46
94
|
|
|
47
95
|
private resetState(cursor: Cursor) {
|
|
48
|
-
this.
|
|
49
|
-
this.
|
|
50
|
-
this.
|
|
51
|
-
this.
|
|
96
|
+
this._cursor = cursor;
|
|
97
|
+
this._regex.lastIndex = 0;
|
|
98
|
+
this._substring = this._cursor.text.substr(this._cursor.index);
|
|
99
|
+
this._node = null;
|
|
52
100
|
}
|
|
53
101
|
|
|
54
|
-
private tryToParse() {
|
|
55
|
-
const result = this.
|
|
102
|
+
private tryToParse(cursor: Cursor) {
|
|
103
|
+
const result = this._regex.exec(this._substring);
|
|
56
104
|
|
|
57
105
|
if (result != null && result.index === 0) {
|
|
58
|
-
this.processResult(result);
|
|
106
|
+
this.processResult(cursor, result);
|
|
59
107
|
} else {
|
|
60
|
-
this.processError();
|
|
108
|
+
this.processError(cursor);
|
|
61
109
|
}
|
|
62
110
|
}
|
|
63
111
|
|
|
64
|
-
private processResult(result: RegExpExecArray) {
|
|
65
|
-
const
|
|
66
|
-
const currentIndex = cursor.getIndex();
|
|
112
|
+
private processResult(cursor: Cursor, result: RegExpExecArray) {
|
|
113
|
+
const currentIndex = cursor.index;
|
|
67
114
|
const newIndex = currentIndex + result[0].length - 1;
|
|
68
115
|
|
|
69
|
-
this.
|
|
116
|
+
this._node = new Node(
|
|
70
117
|
"regex",
|
|
71
|
-
this.
|
|
118
|
+
this._name,
|
|
72
119
|
currentIndex,
|
|
73
120
|
newIndex,
|
|
74
|
-
|
|
121
|
+
undefined,
|
|
75
122
|
result[0]
|
|
76
123
|
);
|
|
77
124
|
|
|
78
|
-
cursor.
|
|
79
|
-
cursor.
|
|
125
|
+
cursor.moveTo(newIndex);
|
|
126
|
+
cursor.recordMatch(this, this._node);
|
|
80
127
|
}
|
|
81
128
|
|
|
82
|
-
private processError() {
|
|
83
|
-
const cursor = this.safelyGetCursor();
|
|
84
|
-
|
|
129
|
+
private processError(cursor: Cursor) {
|
|
85
130
|
if (!this._isOptional) {
|
|
86
|
-
|
|
87
|
-
const parseError = new ParseError(message, cursor.getIndex(), this);
|
|
88
|
-
|
|
89
|
-
cursor.throwError(parseError);
|
|
131
|
+
cursor.recordErrorAt(cursor.index, this);
|
|
90
132
|
}
|
|
91
133
|
|
|
92
|
-
this.
|
|
134
|
+
this._node = null;
|
|
93
135
|
}
|
|
94
136
|
|
|
95
|
-
|
|
96
|
-
const
|
|
137
|
+
clone(name = this._name, isOptional = this._isOptional) {
|
|
138
|
+
const pattern = new Regex(name, this._originalRegexString, isOptional);
|
|
139
|
+
pattern._tokens = this._tokens.slice();
|
|
97
140
|
|
|
98
|
-
|
|
99
|
-
throw new Error("Couldn't find cursor.");
|
|
100
|
-
}
|
|
101
|
-
return cursor;
|
|
141
|
+
return pattern;
|
|
102
142
|
}
|
|
103
143
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
144
|
+
getTokens() {
|
|
145
|
+
return this._tokens;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
getTokensAfter(_childReference: Pattern): string[] {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
getNextTokens(): string[] {
|
|
153
|
+
if (this.parent == null) {
|
|
154
|
+
return []
|
|
107
155
|
}
|
|
108
156
|
|
|
109
|
-
|
|
110
|
-
|
|
157
|
+
return this.parent.getTokensAfter(this);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
getPatternsAfter(_childReference: Pattern): Pattern[] {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
getNextPatterns(): Pattern[] {
|
|
165
|
+
if (this.parent == null) {
|
|
166
|
+
return [];
|
|
111
167
|
}
|
|
112
168
|
|
|
113
|
-
return
|
|
169
|
+
return this.parent.getPatternsAfter(this)
|
|
114
170
|
}
|
|
115
171
|
|
|
116
|
-
|
|
117
|
-
return
|
|
172
|
+
findPattern(_predicate: (p: Pattern) => boolean): Pattern | null {
|
|
173
|
+
return null;
|
|
118
174
|
}
|
|
119
175
|
|
|
120
|
-
|
|
121
|
-
|
|
176
|
+
setTokens(tokens: string[]) {
|
|
177
|
+
this._tokens = tokens;
|
|
122
178
|
}
|
|
123
|
-
|
|
179
|
+
|
|
180
|
+
}
|