clarity-pattern-parser 4.0.2 → 5.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 +176 -1
- package/TODO.md +22 -2
- package/dist/ast/Node.d.ts +43 -11
- package/dist/ast/Visitor.d.ts +31 -31
- package/dist/index.browser.js +1248 -1495
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +12 -16
- package/dist/index.esm.js +1218 -1460
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1217 -1464
- package/dist/index.js.map +1 -1
- package/dist/patterns/And.d.ts +37 -24
- package/dist/patterns/Cursor.d.ts +35 -0
- package/dist/patterns/CursorHistory.d.ts +30 -0
- package/dist/patterns/Literal.d.ts +36 -19
- package/dist/patterns/Not.d.ts +26 -11
- package/dist/patterns/Or.d.ts +31 -22
- package/dist/patterns/ParseError.d.ts +6 -8
- package/dist/patterns/ParseResult.d.ts +6 -0
- package/dist/patterns/Pattern.d.ts +17 -26
- package/dist/patterns/Reference.d.ts +31 -12
- package/dist/patterns/Regex.d.ts +42 -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 +254 -0
- package/src/ast/Node.ts +171 -23
- package/src/index.ts +11 -19
- package/src/intellisense/AutoComplete.test.ts +72 -0
- package/src/intellisense/AutoComplete.ts +146 -0
- package/src/intellisense/Suggestion.ts +13 -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 +50 -0
- package/src/intellisense/javascript/exponent.ts +26 -0
- package/src/intellisense/javascript/expression.ts +87 -0
- package/src/intellisense/javascript/expressionStatement.ts +29 -0
- package/src/intellisense/javascript/fraction.ts +13 -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 +299 -0
- package/src/patterns/And.ts +222 -119
- package/src/patterns/Cursor.test.ts +93 -0
- package/src/patterns/Cursor.ts +130 -0
- package/src/patterns/CursorHistory.test.ts +54 -0
- package/src/patterns/CursorHistory.ts +95 -0
- package/src/patterns/Literal.test.ts +134 -0
- package/src/patterns/Literal.ts +151 -61
- package/src/patterns/Not.test.ts +88 -0
- package/src/patterns/Not.ts +74 -33
- package/src/patterns/Or.test.ts +105 -0
- package/src/patterns/Or.ts +106 -98
- package/src/patterns/ParseError.ts +3 -7
- package/src/patterns/ParseResult.ts +7 -0
- package/src/patterns/Pattern.ts +18 -150
- package/src/patterns/Reference.test.ts +104 -0
- package/src/patterns/Reference.ts +94 -94
- package/src/patterns/Regex.test.ts +101 -0
- package/src/patterns/Regex.ts +129 -60
- package/src/patterns/Repeat.test.ts +196 -0
- package/src/patterns/Repeat.ts +208 -104
- package/src/patterns/clonePatterns.ts +5 -0
- package/src/patterns/filterOutNull.ts +13 -0
- package/src/patterns/findPattern.ts +25 -0
- package/src/patterns/getNextPattern.test.ts +39 -0
- package/src/patterns/getNextPattern.ts +18 -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
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Cursor } from "./Cursor";
|
|
2
|
+
import { Node } from "../ast/Node";
|
|
3
|
+
import { Literal } from "./Literal";
|
|
4
|
+
import { Or } from "./Or";
|
|
5
|
+
import { And } from "./And";
|
|
6
|
+
|
|
7
|
+
describe("Or", () => {
|
|
8
|
+
test("Empty Options", () => {
|
|
9
|
+
expect(() => {
|
|
10
|
+
new Or("bad", []);
|
|
11
|
+
}).toThrowError();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("One Option Successful", () => {
|
|
15
|
+
const a = new Or("a", [new Literal("a", "A")]);
|
|
16
|
+
const cursor = new Cursor("A");
|
|
17
|
+
const result = a.parse(cursor);
|
|
18
|
+
const expected = new Node("literal", "a", 0, 0, [], "A")
|
|
19
|
+
|
|
20
|
+
expect(result).toEqual(expected);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("One Option Failed", () => {
|
|
24
|
+
const a = new Or("a", [new Literal("a", "A")]);
|
|
25
|
+
const cursor = new Cursor("B");
|
|
26
|
+
const result = a.parse(cursor);
|
|
27
|
+
|
|
28
|
+
expect(result).toEqual(null);
|
|
29
|
+
expect(cursor.index).toBe(0);
|
|
30
|
+
expect(cursor.hasError).toBeTruthy();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("Two Option", () => {
|
|
34
|
+
const a = new Or("a-b", [new Literal("a", "A"), new Literal("b", "B")]);
|
|
35
|
+
const cursor = new Cursor("AB");
|
|
36
|
+
let result = a.parse(cursor);
|
|
37
|
+
let expected = new Node("literal", "a", 0, 0, [], "A")
|
|
38
|
+
|
|
39
|
+
expect(result).toEqual(expected);
|
|
40
|
+
|
|
41
|
+
cursor.next();
|
|
42
|
+
|
|
43
|
+
result = a.parse(cursor);
|
|
44
|
+
expected = new Node("or", "a-b", 0, 0, [
|
|
45
|
+
new Node("literal", "b", 0, 0, [], "B")
|
|
46
|
+
], "B");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("Failed (Optional)", () => {
|
|
50
|
+
const a = new Or("a", [new Literal("a", "A")], true);
|
|
51
|
+
const cursor = new Cursor("B");
|
|
52
|
+
const result = a.parse(cursor);
|
|
53
|
+
|
|
54
|
+
expect(result).toBeNull();
|
|
55
|
+
expect(cursor.hasError).toBeFalsy();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("Get Tokens", () => {
|
|
59
|
+
const aOrB = new Or("a-b", [new Literal("a", "A"), new Literal("b", "B")]);
|
|
60
|
+
const tokens = aOrB.getTokens();
|
|
61
|
+
const expected = ["A", "B"];
|
|
62
|
+
|
|
63
|
+
expect(tokens).toEqual(expected);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("Get Next Tokens", () => {
|
|
67
|
+
const a = new Or("a", [new Literal("a", "A")]);
|
|
68
|
+
const parent = new And("parent", [a, new Literal("b", "B")]);
|
|
69
|
+
const tokens = parent.children[0].getNextTokens(parent.children[0].children[0]);
|
|
70
|
+
const expected = ["B"];
|
|
71
|
+
|
|
72
|
+
expect(tokens).toEqual(expected);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("Get Next Tokens Without A Parent", () => {
|
|
76
|
+
const a = new Or("a", [new Literal("a", "A")]);
|
|
77
|
+
const tokens = a.getNextTokens(a.children[0]);
|
|
78
|
+
const expected: string[] = [];
|
|
79
|
+
|
|
80
|
+
expect(tokens).toEqual(expected);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("Get Next Pattern", () => {
|
|
84
|
+
const a = new Or("a", [new Literal("a", "A")]);
|
|
85
|
+
const nextToken = a.getNextPattern();
|
|
86
|
+
|
|
87
|
+
expect(nextToken).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("Properties", () => {
|
|
91
|
+
const a = new Or("a", [new Literal("a", "A")]);
|
|
92
|
+
|
|
93
|
+
expect(a.type).toBe("or");
|
|
94
|
+
expect(a.name).toBe("a");
|
|
95
|
+
expect(a.isOptional).toBeFalsy();
|
|
96
|
+
expect(a.parent).toBeNull();
|
|
97
|
+
expect(a.children[0].name).toBe("a");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("Parse Text", () => {
|
|
101
|
+
const a = new Or("a", [new Literal("a", "A")]);
|
|
102
|
+
const { ast: result } = a.parseText("B");
|
|
103
|
+
expect(result).toBeNull();
|
|
104
|
+
});
|
|
105
|
+
});
|
package/src/patterns/Or.ts
CHANGED
|
@@ -1,132 +1,140 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (this._children.length < 2) {
|
|
21
|
-
throw new Error(
|
|
22
|
-
"Invalid Argument: OrValue needs to have more than one value pattern."
|
|
23
|
-
);
|
|
24
|
-
}
|
|
1
|
+
import { Node } from "../ast/Node";
|
|
2
|
+
import { Cursor } from "./Cursor";
|
|
3
|
+
import { Pattern } from "./Pattern";
|
|
4
|
+
import { clonePatterns } from "./clonePatterns";
|
|
5
|
+
import { getNextPattern } from "./getNextPattern";
|
|
6
|
+
import { findPattern } from "./findPattern";
|
|
7
|
+
|
|
8
|
+
export class Or implements Pattern {
|
|
9
|
+
private _type: string;
|
|
10
|
+
private _name: string;
|
|
11
|
+
private _parent: Pattern | null;
|
|
12
|
+
private _children: Pattern[];
|
|
13
|
+
private _isOptional: boolean;
|
|
14
|
+
private _node: Node | null;
|
|
15
|
+
private _firstIndex: number;
|
|
16
|
+
|
|
17
|
+
get type(): string {
|
|
18
|
+
return this._type;
|
|
19
|
+
}
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
get name(): string {
|
|
22
|
+
return this._name;
|
|
23
|
+
}
|
|
29
24
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
25
|
+
get parent(): Pattern | null {
|
|
26
|
+
return this._parent;
|
|
33
27
|
}
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
this.
|
|
37
|
-
this.errors = [];
|
|
38
|
-
this.node = null;
|
|
39
|
-
this.cursor = cursor;
|
|
40
|
-
this.mark = cursor.mark();
|
|
29
|
+
set parent(pattern: Pattern | null) {
|
|
30
|
+
this._parent = pattern;
|
|
41
31
|
}
|
|
42
32
|
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
get children(): Pattern[] {
|
|
34
|
+
return this._children;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get isOptional(): boolean {
|
|
38
|
+
return this._isOptional;
|
|
39
|
+
}
|
|
45
40
|
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
constructor(name: string, options: Pattern[], isOptional: boolean = false) {
|
|
42
|
+
if (options.length === 0) {
|
|
43
|
+
throw new Error("Need at least one pattern with an 'or' pattern.");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const children = clonePatterns(options, false);
|
|
47
|
+
this._assignChildrenToParent(children);
|
|
48
|
+
|
|
49
|
+
this._type = "or";
|
|
50
|
+
this._name = name;
|
|
51
|
+
this._parent = null;
|
|
52
|
+
this._children = children;
|
|
53
|
+
this._isOptional = isOptional;
|
|
54
|
+
this._node = null;
|
|
55
|
+
this._firstIndex = 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private _assignChildrenToParent(children: Pattern[]): void {
|
|
59
|
+
for (const child of children) {
|
|
60
|
+
child.parent = this;
|
|
48
61
|
}
|
|
49
|
-
return cursor;
|
|
50
62
|
}
|
|
51
63
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.
|
|
64
|
+
parseText(text: string) {
|
|
65
|
+
const cursor = new Cursor(text);
|
|
66
|
+
const ast = this.parse(cursor)
|
|
55
67
|
|
|
56
|
-
return
|
|
68
|
+
return {
|
|
69
|
+
ast,
|
|
70
|
+
cursor
|
|
71
|
+
};
|
|
57
72
|
}
|
|
58
73
|
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
parse(cursor: Cursor): Node | null {
|
|
75
|
+
this._firstIndex = cursor.index;
|
|
76
|
+
this._node = null;
|
|
61
77
|
|
|
62
|
-
|
|
63
|
-
const pattern = this._children[this.patternIndex];
|
|
64
|
-
const node = pattern.parse(cursor);
|
|
65
|
-
const hasError = cursor.hasUnresolvedError();
|
|
78
|
+
const node = this._tryToParse(cursor);
|
|
66
79
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
80
|
+
if (node != null) {
|
|
81
|
+
cursor.resolveError();
|
|
82
|
+
return node
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!this._isOptional) {
|
|
86
|
+
cursor.recordErrorAt(this._firstIndex, this)
|
|
87
|
+
return null;
|
|
76
88
|
}
|
|
89
|
+
|
|
90
|
+
cursor.resolveError();
|
|
91
|
+
cursor.moveTo(this._firstIndex);
|
|
92
|
+
return null;
|
|
77
93
|
}
|
|
78
94
|
|
|
79
|
-
private
|
|
80
|
-
const
|
|
81
|
-
|
|
95
|
+
private _tryToParse(cursor: Cursor): Node | null {
|
|
96
|
+
for (const pattern of this._children) {
|
|
97
|
+
cursor.moveTo(this._firstIndex);
|
|
98
|
+
const result = pattern.parse(cursor);
|
|
82
99
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
cursor.resolveError();
|
|
86
|
-
cursor.moveToMark(this.mark);
|
|
87
|
-
return false;
|
|
88
|
-
} else {
|
|
89
|
-
if (this._isOptional) {
|
|
90
|
-
cursor.resolveError();
|
|
91
|
-
cursor.moveToMark(this.mark);
|
|
100
|
+
if (!cursor.hasError) {
|
|
101
|
+
return result;
|
|
92
102
|
}
|
|
93
|
-
|
|
94
|
-
|
|
103
|
+
|
|
104
|
+
cursor.resolveError();
|
|
95
105
|
}
|
|
106
|
+
|
|
107
|
+
return null
|
|
96
108
|
}
|
|
97
109
|
|
|
98
|
-
|
|
99
|
-
const
|
|
110
|
+
getTokens(): string[] {
|
|
111
|
+
const tokens: string[] = [];
|
|
100
112
|
|
|
101
|
-
this.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
node.startIndex,
|
|
105
|
-
node.endIndex,
|
|
106
|
-
[node],
|
|
107
|
-
node.value
|
|
108
|
-
);
|
|
113
|
+
for (const child of this._children) {
|
|
114
|
+
tokens.push(...child.getTokens());
|
|
115
|
+
}
|
|
109
116
|
|
|
110
|
-
|
|
111
|
-
cursor.addMatch(this, this.node);
|
|
117
|
+
return tokens;
|
|
112
118
|
}
|
|
113
119
|
|
|
114
|
-
|
|
115
|
-
if (
|
|
116
|
-
|
|
120
|
+
getNextTokens(_lastMatched: Pattern): string[] {
|
|
121
|
+
if (this._parent === null) {
|
|
122
|
+
return [];
|
|
117
123
|
}
|
|
118
124
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
125
|
+
return this._parent.getNextTokens(this);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getNextPattern(): Pattern | null {
|
|
129
|
+
return getNextPattern(this)
|
|
130
|
+
}
|
|
122
131
|
|
|
123
|
-
|
|
132
|
+
findPattern(isMatch: (p: Pattern)=>boolean): Pattern | null{
|
|
133
|
+
return findPattern(this, isMatch);
|
|
124
134
|
}
|
|
125
135
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
[]
|
|
130
|
-
);
|
|
136
|
+
clone(name = this._name, isOptional = this._isOptional): Pattern {
|
|
137
|
+
const or = new Or(name, this._children, isOptional);
|
|
138
|
+
return or;
|
|
131
139
|
}
|
|
132
140
|
}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import Pattern from "./Pattern";
|
|
1
|
+
import { Pattern } from "./Pattern";
|
|
2
2
|
|
|
3
|
-
export
|
|
4
|
-
public message: string;
|
|
5
|
-
public name: string;
|
|
3
|
+
export class ParseError {
|
|
6
4
|
public index: number;
|
|
7
5
|
public pattern: Pattern;
|
|
8
6
|
|
|
9
|
-
constructor(
|
|
10
|
-
this.name = "ParseError";
|
|
11
|
-
this.message = message;
|
|
7
|
+
constructor(index: number, pattern: Pattern) {
|
|
12
8
|
this.index = index;
|
|
13
9
|
this.pattern = pattern;
|
|
14
10
|
}
|
package/src/patterns/Pattern.ts
CHANGED
|
@@ -1,151 +1,19 @@
|
|
|
1
|
-
import Cursor from "
|
|
2
|
-
import Node from "../ast/Node";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
isOptional = false
|
|
20
|
-
) {
|
|
21
|
-
this._type = type;
|
|
22
|
-
this._name = name;
|
|
23
|
-
this._children = [];
|
|
24
|
-
this._parent = null;
|
|
25
|
-
this._isOptional = isOptional;
|
|
26
|
-
this.children = children;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
abstract parse(cursor: Cursor): Node | null;
|
|
30
|
-
|
|
31
|
-
exec(text: string) {
|
|
32
|
-
const cursor = new Cursor(text);
|
|
33
|
-
const node = this.parse(cursor);
|
|
34
|
-
|
|
35
|
-
if (cursor.didSuccessfullyParse()) {
|
|
36
|
-
return node;
|
|
37
|
-
} else {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
test(text: string) {
|
|
43
|
-
return this.exec(text) != null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
get name() {
|
|
47
|
-
return this._name;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
get type() {
|
|
51
|
-
return this._type;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
get parent() {
|
|
55
|
-
return this._parent;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
set parent(value: Pattern | null) {
|
|
59
|
-
this._parent = value;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
get children() {
|
|
63
|
-
return this._children;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
set children(value) {
|
|
67
|
-
this._children = value;
|
|
68
|
-
this.cloneChildren();
|
|
69
|
-
this.assignAsParent();
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
abstract clone(name?: string): Pattern;
|
|
73
|
-
abstract getTokens(): string[];
|
|
74
|
-
|
|
75
|
-
getTokenValue(): string | null {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
getNextTokens(): string[] {
|
|
80
|
-
const parent = this._parent;
|
|
81
|
-
|
|
82
|
-
if (parent != null) {
|
|
83
|
-
const siblings = parent.children;
|
|
84
|
-
const index = siblings.findIndex((c) => c === this);
|
|
85
|
-
const nextSibling = siblings[index + 1];
|
|
86
|
-
|
|
87
|
-
// I don't like this, so I think we need to rethink this.
|
|
88
|
-
if (parent.type.indexOf("repeat") === 0) {
|
|
89
|
-
const tokens = parent.getNextTokens();
|
|
90
|
-
if (index === 0 && siblings.length > 1) {
|
|
91
|
-
return nextSibling.getTokens().concat(tokens);
|
|
92
|
-
} else if (index === 1) {
|
|
93
|
-
return siblings[0].getTokens();
|
|
94
|
-
} else {
|
|
95
|
-
return this.getTokens().concat(tokens);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Another thing I don't like.
|
|
100
|
-
if (
|
|
101
|
-
this._parent?.type?.indexOf("and") === 0 &&
|
|
102
|
-
nextSibling != null &&
|
|
103
|
-
nextSibling.isOptional
|
|
104
|
-
) {
|
|
105
|
-
let tokens: string[] = [];
|
|
106
|
-
|
|
107
|
-
for (let x = index + 1; x < siblings.length; x++) {
|
|
108
|
-
const child = siblings[x];
|
|
109
|
-
|
|
110
|
-
if (child.isOptional) {
|
|
111
|
-
tokens = tokens.concat(child.getTokens());
|
|
112
|
-
} else {
|
|
113
|
-
tokens = tokens.concat(child.getTokens());
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (x === siblings.length - 1) {
|
|
118
|
-
tokens = tokens.concat(this._parent.getNextTokens());
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return tokens;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// If you are an or you have already qualified.
|
|
126
|
-
if (parent.type.indexOf("or") === 0) {
|
|
127
|
-
return parent.getNextTokens();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (nextSibling != null) {
|
|
131
|
-
return nextSibling.getTokens();
|
|
132
|
-
} else {
|
|
133
|
-
return parent.getNextTokens();
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return [];
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
private cloneChildren() {
|
|
141
|
-
this._children = this._children.map((pattern) => {
|
|
142
|
-
return pattern.clone();
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
Object.freeze(this._children);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
private assignAsParent() {
|
|
149
|
-
this._children.forEach((child) => (child.parent = this));
|
|
150
|
-
}
|
|
1
|
+
import { Cursor } from "./Cursor";
|
|
2
|
+
import { Node } from "../ast/Node";
|
|
3
|
+
import { ParseResult } from "./ParseResult"
|
|
4
|
+
|
|
5
|
+
export interface Pattern {
|
|
6
|
+
type: string;
|
|
7
|
+
name: string;
|
|
8
|
+
parent: Pattern | null;
|
|
9
|
+
children: Pattern[];
|
|
10
|
+
isOptional: boolean;
|
|
11
|
+
|
|
12
|
+
parse(cursor: Cursor): Node | null;
|
|
13
|
+
parseText(text: string): ParseResult;
|
|
14
|
+
clone(name?: string, isOptional?: boolean): Pattern;
|
|
15
|
+
getTokens(): string[];
|
|
16
|
+
getNextTokens(lastMatched: Pattern): string[];
|
|
17
|
+
getNextPattern(): Pattern | null;
|
|
18
|
+
findPattern(isMatch:(p: Pattern)=>boolean): Pattern | null;
|
|
151
19
|
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Cursor, Repeat } from "..";
|
|
2
|
+
import { Node } from "../ast/Node";
|
|
3
|
+
import { And } from "./And";
|
|
4
|
+
import { findPattern } from "./findPattern";
|
|
5
|
+
import { Literal } from "./Literal";
|
|
6
|
+
import { Or } from "./Or";
|
|
7
|
+
import { Reference } from "./Reference";
|
|
8
|
+
import { Regex } from "./Regex";
|
|
9
|
+
|
|
10
|
+
function createValuePattern() {
|
|
11
|
+
const number = new Regex("number", "\\d+");
|
|
12
|
+
const openBracket = new Literal("open-bracket", "[");
|
|
13
|
+
const closeBracket = new Literal("close-bracket", "]");
|
|
14
|
+
const divider = new Regex("divider", "\\s*,\\s+");
|
|
15
|
+
const valueRef = new Reference("value");
|
|
16
|
+
const values = new Repeat("values", valueRef, divider);
|
|
17
|
+
const array = new And("array", [openBracket, values, closeBracket]);
|
|
18
|
+
const value = new Or("value", [number, array]);
|
|
19
|
+
|
|
20
|
+
number.setTokens(["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]);
|
|
21
|
+
divider.setTokens([", "])
|
|
22
|
+
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe("Reference", () => {
|
|
27
|
+
test("One Deep Successful Parse", () => {
|
|
28
|
+
const value = createValuePattern();
|
|
29
|
+
const cursor = new Cursor("[1, 2]");
|
|
30
|
+
const result = value.parse(cursor);
|
|
31
|
+
|
|
32
|
+
const expected = new Node("and", "array", 0, 5, [
|
|
33
|
+
new Node("literal", "open-bracket", 0, 0, [], "["),
|
|
34
|
+
new Node("repeat", "values", 1, 4, [
|
|
35
|
+
new Node("regex", "number", 1, 1, [], "1"),
|
|
36
|
+
new Node("regex", "divider", 2, 3, [], ", "),
|
|
37
|
+
new Node("regex", "number", 4, 4, [], "2")
|
|
38
|
+
]),
|
|
39
|
+
new Node("literal", "close-bracket", 5, 5, [], "]"),
|
|
40
|
+
])
|
|
41
|
+
|
|
42
|
+
expect(result).toEqual(expected);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("No Reference Pattern", () => {
|
|
46
|
+
const ref = new Reference("bad-reference");
|
|
47
|
+
|
|
48
|
+
expect(() => {
|
|
49
|
+
ref.parse(new Cursor("text"))
|
|
50
|
+
}).toThrowError()
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("Get Tokens", () => {
|
|
54
|
+
const value = createValuePattern();
|
|
55
|
+
const ref = findPattern(value, (p) => p.type === "reference");
|
|
56
|
+
const tokens = ref?.getTokens();
|
|
57
|
+
const expected = ["["];
|
|
58
|
+
|
|
59
|
+
expect(tokens).toEqual(expected);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("Get Next Tokens", () => {
|
|
63
|
+
const value = createValuePattern();
|
|
64
|
+
const ref = findPattern(value, (p) => p.type === "reference");
|
|
65
|
+
|
|
66
|
+
// The value passed to getNextTokens doesn't matter.
|
|
67
|
+
// I just needed it to be a pattern.
|
|
68
|
+
const tokens = ref?.getNextTokens(value);
|
|
69
|
+
const expected = ["]"];
|
|
70
|
+
|
|
71
|
+
expect(tokens).toEqual(expected);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("Get Next Tokens With No Parent", () => {
|
|
75
|
+
const ref = new Reference("bad-reference");
|
|
76
|
+
const tokens = ref.getNextTokens(new Literal("bogus", "bogus"))
|
|
77
|
+
|
|
78
|
+
expect(tokens).toEqual([]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("Get Next Pattern", () => {
|
|
82
|
+
const ref = new Reference("ref");
|
|
83
|
+
const nextPattern = ref.getNextPattern();
|
|
84
|
+
|
|
85
|
+
expect(nextPattern).toBeNull()
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("Properties", () => {
|
|
89
|
+
const ref = new Reference("ref");
|
|
90
|
+
|
|
91
|
+
expect(ref.type).toBe("reference");
|
|
92
|
+
expect(ref.name).toBe("ref");
|
|
93
|
+
expect(ref.isOptional).toBeFalsy();
|
|
94
|
+
expect(ref.parent).toBe(null)
|
|
95
|
+
expect(ref.children).toEqual([])
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("Parse Text", () => {
|
|
99
|
+
const value = createValuePattern();
|
|
100
|
+
const reference = findPattern(value, p => p.type === "reference") as Reference
|
|
101
|
+
const { ast: result } = reference.parseText("B");
|
|
102
|
+
expect(result).toBeNull()
|
|
103
|
+
});
|
|
104
|
+
});
|