clarity-pattern-parser 6.0.0 → 6.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/TODO.md +9 -7
- package/dist/index.browser.js +72 -22
- package/dist/index.browser.js.map +1 -1
- package/dist/index.esm.js +72 -22
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +72 -22
- package/dist/index.js.map +1 -1
- package/dist/intellisense/AutoComplete.d.ts +12 -6
- package/dist/intellisense/Suggestion.d.ts +1 -2
- package/dist/patterns/And.d.ts +1 -0
- package/dist/patterns/Literal.d.ts +1 -0
- package/dist/patterns/Not.d.ts +1 -0
- package/dist/patterns/Or.d.ts +1 -0
- package/dist/patterns/Pattern.d.ts +2 -1
- package/dist/patterns/Reference.d.ts +1 -0
- package/dist/patterns/Regex.d.ts +1 -0
- package/dist/patterns/Repeat.d.ts +1 -0
- package/package.json +1 -1
- package/src/ast/Node.test.ts +6 -0
- package/src/intellisense/AutoComplete.test.ts +104 -37
- package/src/intellisense/AutoComplete.ts +51 -24
- package/src/intellisense/Suggestion.ts +1 -1
- package/src/intellisense/javascript/Javascript.test.ts +35 -11
- package/src/intellisense/javascript/{expressionStatement.ts → assignment.ts} +7 -8
- package/src/intellisense/javascript/expression.ts +44 -26
- package/src/intellisense/javascript/infixOperator.ts +6 -2
- package/src/intellisense/javascript/keywords.ts +3 -0
- package/src/intellisense/javascript/objectAccess.ts +9 -0
- package/src/intellisense/javascript/objectLiteral.ts +3 -3
- package/src/intellisense/javascript/propertyAccess.ts +8 -3
- package/src/intellisense/javascript/stringLiteral.ts +16 -8
- package/src/patterns/And.test.ts +13 -0
- package/src/patterns/And.ts +14 -0
- package/src/patterns/Literal.test.ts +9 -0
- package/src/patterns/Literal.ts +4 -0
- package/src/patterns/Not.test.ts +11 -0
- package/src/patterns/Not.ts +4 -0
- package/src/patterns/Or.test.ts +11 -0
- package/src/patterns/Or.ts +10 -0
- package/src/patterns/Pattern.ts +2 -1
- package/src/patterns/Reference.test.ts +10 -0
- package/src/patterns/Reference.ts +4 -0
- package/src/patterns/Regex.test.ts +9 -0
- package/src/patterns/Regex.ts +4 -0
- package/src/patterns/Repeat.test.ts +9 -0
- package/src/patterns/Repeat.ts +4 -0
|
@@ -10,11 +10,11 @@ describe("AutoComplete", () => {
|
|
|
10
10
|
test("No Text", () => {
|
|
11
11
|
const name = new Literal("name", "Name");
|
|
12
12
|
const autoComplete = new AutoComplete(name);
|
|
13
|
-
let result = autoComplete.
|
|
13
|
+
let result = autoComplete.suggestFor("");
|
|
14
14
|
|
|
15
15
|
expect(result.options[0].text).toBe("Name");
|
|
16
16
|
expect(result.options[0].startIndex).toBe(0);
|
|
17
|
-
expect(result.
|
|
17
|
+
expect(result.errorAtIndex).toBe(0);
|
|
18
18
|
expect(result.isComplete).toBeFalsy();
|
|
19
19
|
});
|
|
20
20
|
|
|
@@ -26,11 +26,11 @@ describe("AutoComplete", () => {
|
|
|
26
26
|
const name = new And("name", [john, space, new Or("last-name", [smith, doe])]);
|
|
27
27
|
|
|
28
28
|
const autoComplete = new AutoComplete(name);
|
|
29
|
-
const result = autoComplete.
|
|
29
|
+
const result = autoComplete.suggestFor("John Doe");
|
|
30
30
|
|
|
31
31
|
expect(result.ast?.value).toBe("John Doe");
|
|
32
32
|
expect(result.options.length).toBe(0);
|
|
33
|
-
expect(result.
|
|
33
|
+
expect(result.errorAtIndex).toBeNull();
|
|
34
34
|
expect(result.isComplete).toBeTruthy();
|
|
35
35
|
expect(result.cursor).not.toBeNull();
|
|
36
36
|
});
|
|
@@ -42,14 +42,20 @@ describe("AutoComplete", () => {
|
|
|
42
42
|
const smith = new Literal("smith", "Smith");
|
|
43
43
|
const name = new And("name", [john, space, new Or("last-name", [smith, doe])]);
|
|
44
44
|
|
|
45
|
+
const text = "John "
|
|
45
46
|
const autoComplete = new AutoComplete(name);
|
|
46
|
-
const result = autoComplete.
|
|
47
|
+
const result = autoComplete.suggestFor(text);
|
|
48
|
+
const expectedOptions = [{
|
|
49
|
+
text: "Doe",
|
|
50
|
+
startIndex: 5
|
|
51
|
+
}, {
|
|
52
|
+
text: "Smith",
|
|
53
|
+
startIndex: 5
|
|
54
|
+
}];
|
|
47
55
|
|
|
48
56
|
expect(result.ast).toBeNull();
|
|
49
|
-
expect(result.options
|
|
50
|
-
expect(result.
|
|
51
|
-
expect(result.nextPatterns[0].type).toBe("or");
|
|
52
|
-
expect(result.nextPatterns[0].name).toBe("last-name");
|
|
57
|
+
expect(result.options).toEqual(expectedOptions);
|
|
58
|
+
expect(result.errorAtIndex).toBe(text.length)
|
|
53
59
|
expect(result.isComplete).toBeFalsy();
|
|
54
60
|
expect(result.cursor).not.toBeNull();
|
|
55
61
|
});
|
|
@@ -64,12 +70,17 @@ describe("AutoComplete", () => {
|
|
|
64
70
|
|
|
65
71
|
divider.setTokens([", "])
|
|
66
72
|
|
|
73
|
+
const text = "John Doe";
|
|
67
74
|
const autoComplete = new AutoComplete(new Repeat("last-names", name, divider));
|
|
68
|
-
const result = autoComplete.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
const result = autoComplete.suggestFor(text);
|
|
76
|
+
const expectedOptions = [{
|
|
77
|
+
text: ", ",
|
|
78
|
+
startIndex: 8
|
|
79
|
+
}];
|
|
80
|
+
|
|
81
|
+
expect(result.ast?.value).toBe(text);
|
|
82
|
+
expect(result.options).toEqual(expectedOptions);
|
|
83
|
+
expect(result.errorAtIndex).toBeNull()
|
|
73
84
|
expect(result.isComplete).toBeTruthy();
|
|
74
85
|
expect(result.cursor).not.toBeNull();
|
|
75
86
|
});
|
|
@@ -77,36 +88,50 @@ describe("AutoComplete", () => {
|
|
|
77
88
|
test("Partial", () => {
|
|
78
89
|
const name = new Literal("name", "Name");
|
|
79
90
|
const autoComplete = new AutoComplete(name);
|
|
80
|
-
|
|
91
|
+
const result = autoComplete.suggestFor("Na");
|
|
92
|
+
const expectedOptions = [{
|
|
93
|
+
text: "me",
|
|
94
|
+
startIndex: 2
|
|
95
|
+
}];
|
|
81
96
|
|
|
82
|
-
expect(result.
|
|
83
|
-
expect(result.options
|
|
84
|
-
expect(result.
|
|
97
|
+
expect(result.ast).toBeNull();
|
|
98
|
+
expect(result.options).toEqual(expectedOptions);
|
|
99
|
+
expect(result.errorAtIndex).toBe(2);
|
|
85
100
|
expect(result.isComplete).toBeFalsy();
|
|
101
|
+
expect(result.cursor).not.toBeNull();
|
|
86
102
|
});
|
|
87
103
|
|
|
88
104
|
test("Partial Match With Bad Characters", () => {
|
|
89
105
|
const name = new Literal("name", "Name");
|
|
90
106
|
const autoComplete = new AutoComplete(name);
|
|
91
|
-
|
|
107
|
+
const result = autoComplete.suggestFor("Ni");
|
|
108
|
+
|
|
109
|
+
const expectedOptions = [{
|
|
110
|
+
text: "ame",
|
|
111
|
+
startIndex: 1
|
|
112
|
+
}];
|
|
92
113
|
|
|
93
|
-
expect(result.
|
|
94
|
-
expect(result.options
|
|
95
|
-
|
|
114
|
+
expect(result.ast).toBeNull();
|
|
115
|
+
expect(result.options).toEqual(expectedOptions);
|
|
116
|
+
expect(result.errorAtIndex).toBe(1);
|
|
96
117
|
expect(result.isComplete).toBeFalsy();
|
|
118
|
+
expect(result.cursor).not.toBeNull();
|
|
97
119
|
});
|
|
98
120
|
|
|
99
121
|
test("Complete", () => {
|
|
100
122
|
const name = new Literal("name", "Name");
|
|
101
123
|
const autoComplete = new AutoComplete(name);
|
|
102
|
-
|
|
124
|
+
const text = "Name"
|
|
125
|
+
const result = autoComplete.suggestFor(text);
|
|
103
126
|
|
|
104
|
-
expect(result.
|
|
105
|
-
expect(result.
|
|
127
|
+
expect(result.ast?.value).toBe(text);
|
|
128
|
+
expect(result.options).toEqual([]);
|
|
129
|
+
expect(result.errorAtIndex).toBeNull();
|
|
106
130
|
expect(result.isComplete).toBeTruthy();
|
|
131
|
+
expect(result.cursor).not.toBeNull();
|
|
107
132
|
});
|
|
108
133
|
|
|
109
|
-
test("Options", ()=>{
|
|
134
|
+
test("Options AutoComplete on Composing Pattern", () => {
|
|
110
135
|
const autoCompleteOptions: AutoCompleteOptions = {
|
|
111
136
|
greedyPatternNames: ["space"],
|
|
112
137
|
customTokens: {
|
|
@@ -123,26 +148,68 @@ describe("AutoComplete", () => {
|
|
|
123
148
|
const lastName = new Or("last-name", [doe, smith]);
|
|
124
149
|
const fullName = new And("full-name", [firstName, space, lastName]);
|
|
125
150
|
|
|
126
|
-
const text = "
|
|
151
|
+
const text = "Jack";
|
|
127
152
|
const autoComplete = new AutoComplete(fullName, autoCompleteOptions);
|
|
128
|
-
const { options, ast,
|
|
153
|
+
const { options, ast, errorAtIndex } = autoComplete.suggestFor(text);
|
|
154
|
+
|
|
129
155
|
const expectedOptions = [
|
|
130
|
-
{text: " Doe", startIndex: 4},
|
|
131
|
-
{text: " Smith", startIndex: 4},
|
|
132
|
-
{text: " Sparrow", startIndex: 4},
|
|
156
|
+
{ text: " Doe", startIndex: 4 },
|
|
157
|
+
{ text: " Smith", startIndex: 4 },
|
|
158
|
+
{ text: " Sparrow", startIndex: 4 },
|
|
133
159
|
];
|
|
134
160
|
|
|
135
|
-
const results = expectedOptions.map(o=>text.slice(0, o.startIndex) + o.text);
|
|
161
|
+
const results = expectedOptions.map(o => text.slice(0, o.startIndex) + o.text);
|
|
136
162
|
const expectedResults = [
|
|
137
|
-
"
|
|
138
|
-
"
|
|
139
|
-
"
|
|
163
|
+
"Jack Doe",
|
|
164
|
+
"Jack Smith",
|
|
165
|
+
"Jack Sparrow",
|
|
140
166
|
]
|
|
141
167
|
|
|
168
|
+
expect(ast).toBeNull();
|
|
169
|
+
expect(errorAtIndex).toBe(4);
|
|
142
170
|
expect(options).toEqual(expectedOptions);
|
|
171
|
+
expect(results).toEqual(expectedResults);
|
|
172
|
+
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("Options AutoComplete On Leaf Pattern", () => {
|
|
176
|
+
const autoCompleteOptions: AutoCompleteOptions = {
|
|
177
|
+
greedyPatternNames: ["space"],
|
|
178
|
+
customTokens: {
|
|
179
|
+
"space": [" "]
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const jack = new Literal("jack", "Jack");
|
|
184
|
+
const john = new Literal("john", "John");
|
|
185
|
+
const space = new Literal("space", " ");
|
|
186
|
+
const doe = new Literal("doe", "Doe");
|
|
187
|
+
const smith = new Literal("smith", "Smith");
|
|
188
|
+
const firstName = new Or("first-name", [jack, john]);
|
|
189
|
+
const lastName = new Or("last-name", [doe, smith]);
|
|
190
|
+
const fullName = new And("full-name", [firstName, space, lastName]);
|
|
191
|
+
|
|
192
|
+
const text = "Jack";
|
|
193
|
+
const autoComplete = new AutoComplete(fullName, autoCompleteOptions);
|
|
194
|
+
const { options, ast, errorAtIndex } = autoComplete.suggestFor(text);
|
|
195
|
+
const expectedOptions = [
|
|
196
|
+
{ text: " Doe", startIndex: 4 },
|
|
197
|
+
{ text: " Smith", startIndex: 4 },
|
|
198
|
+
{ text: " Doe", startIndex: 4 },
|
|
199
|
+
{ text: " Smith", startIndex: 4 },
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
const results = expectedOptions.map(o => text.slice(0, o.startIndex) + o.text);
|
|
203
|
+
const expectedResults = [
|
|
204
|
+
"Jack Doe",
|
|
205
|
+
"Jack Smith",
|
|
206
|
+
"Jack Doe",
|
|
207
|
+
"Jack Smith",
|
|
208
|
+
]
|
|
209
|
+
|
|
143
210
|
expect(ast).toBeNull();
|
|
144
|
-
expect(
|
|
145
|
-
expect(
|
|
211
|
+
expect(errorAtIndex).toBe(4);
|
|
212
|
+
expect(options).toEqual(expectedOptions);
|
|
146
213
|
expect(results).toEqual(expectedResults)
|
|
147
214
|
|
|
148
215
|
});
|
|
@@ -9,12 +9,12 @@ export interface AutoCompleteOptions {
|
|
|
9
9
|
* Be very careful, this can explode to infinity pretty quick. Usually useful
|
|
10
10
|
* for dividers and spaces.
|
|
11
11
|
*/
|
|
12
|
-
greedyPatternNames
|
|
12
|
+
greedyPatternNames?: string[];
|
|
13
13
|
/**
|
|
14
14
|
* Allows for custom suggestions for patterns. The key is the name of the pattern
|
|
15
15
|
* and the string array are the tokens suggested for that pattern.
|
|
16
16
|
*/
|
|
17
|
-
customTokens
|
|
17
|
+
customTokens?: Record<string, string[]>;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const defaultOptions = { greedyPatternNames: [], customTokens: {} };
|
|
@@ -31,12 +31,20 @@ export class AutoComplete {
|
|
|
31
31
|
this._text = "";
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
/**
|
|
35
|
+
* @deprecated Use suggestFor instead.
|
|
36
|
+
* @param text The text to suggest for.
|
|
37
|
+
*/
|
|
34
38
|
suggest(text: string): Suggestion {
|
|
39
|
+
return this.suggestFor(text);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
suggestFor(text: string): Suggestion {
|
|
35
43
|
if (text.length === 0) {
|
|
36
44
|
return {
|
|
37
45
|
isComplete: false,
|
|
38
|
-
options: this.
|
|
39
|
-
|
|
46
|
+
options: this._createSuggestionsFromRoot(),
|
|
47
|
+
errorAtIndex: 0,
|
|
40
48
|
cursor: null,
|
|
41
49
|
ast: null
|
|
42
50
|
}
|
|
@@ -48,43 +56,51 @@ export class AutoComplete {
|
|
|
48
56
|
|
|
49
57
|
const leafPattern = this._cursor.leafMatch.pattern;
|
|
50
58
|
const isComplete = ast?.value === text;
|
|
51
|
-
const options = this.
|
|
59
|
+
const options = this._createSuggestionsFromTokens();
|
|
52
60
|
|
|
53
61
|
let nextPatterns = [this._pattern];
|
|
62
|
+
let errorAtIndex = null;
|
|
54
63
|
|
|
55
64
|
if (leafPattern != null) {
|
|
56
65
|
nextPatterns = leafPattern.getNextPatterns();
|
|
57
66
|
}
|
|
58
67
|
|
|
68
|
+
if (this._cursor.hasError && this._cursor.furthestError != null) {
|
|
69
|
+
errorAtIndex = this._cursor.furthestError.index;
|
|
70
|
+
|
|
71
|
+
errorAtIndex = options.reduce((errorAtIndex, option) =>
|
|
72
|
+
Math.max(errorAtIndex, option.startIndex),
|
|
73
|
+
errorAtIndex);
|
|
74
|
+
}
|
|
75
|
+
|
|
59
76
|
return {
|
|
60
77
|
isComplete: isComplete,
|
|
61
78
|
options: options,
|
|
62
|
-
|
|
79
|
+
errorAtIndex,
|
|
63
80
|
cursor: this._cursor,
|
|
64
81
|
ast,
|
|
65
82
|
}
|
|
66
83
|
}
|
|
67
84
|
|
|
68
|
-
private
|
|
85
|
+
private _createSuggestionsFromRoot(): SuggestionOption[] {
|
|
69
86
|
const suggestions: SuggestionOption[] = [];
|
|
70
87
|
const tokens = this._pattern.getTokens();
|
|
71
88
|
|
|
72
89
|
for (const token of tokens) {
|
|
73
|
-
suggestions.push(this.
|
|
90
|
+
suggestions.push(this._createSuggestion("", token));
|
|
74
91
|
}
|
|
75
92
|
|
|
76
93
|
return suggestions;
|
|
77
94
|
}
|
|
78
95
|
|
|
79
|
-
private
|
|
96
|
+
private _createSuggestionsFromTokens(): SuggestionOption[] {
|
|
80
97
|
const leafMatch = this._cursor.leafMatch;
|
|
81
98
|
|
|
82
99
|
if (!leafMatch.pattern) {
|
|
83
|
-
return this.
|
|
100
|
+
return this._createSuggestions(-1, this._getTokensForPattern(this._pattern));
|
|
84
101
|
}
|
|
85
102
|
|
|
86
103
|
const leafPattern = leafMatch.pattern;
|
|
87
|
-
const leafNode = leafMatch.node;
|
|
88
104
|
const parent = leafMatch.pattern.parent;
|
|
89
105
|
|
|
90
106
|
if (parent !== null && leafMatch.node != null) {
|
|
@@ -95,39 +111,50 @@ export class AutoComplete {
|
|
|
95
111
|
return acc;
|
|
96
112
|
}, []);
|
|
97
113
|
|
|
98
|
-
return this.
|
|
114
|
+
return this._createSuggestions(leafMatch.node.lastIndex, tokens);
|
|
99
115
|
} else {
|
|
100
116
|
return [];
|
|
101
117
|
}
|
|
102
118
|
}
|
|
103
119
|
|
|
104
120
|
private _getTokensForPattern(pattern: Pattern) {
|
|
105
|
-
|
|
106
|
-
|
|
121
|
+
const augmentedTokens = this._getAugmentedTokens(pattern)
|
|
122
|
+
|
|
123
|
+
if (this._options.greedyPatternNames != null && this._options.greedyPatternNames.includes(pattern.name)) {
|
|
107
124
|
const nextPatterns = pattern.getNextPatterns();
|
|
108
125
|
const tokens: string[] = [];
|
|
109
126
|
|
|
110
|
-
const nextPatternTokens = nextPatterns.reduce((acc: string[], pattern)=>{
|
|
127
|
+
const nextPatternTokens = nextPatterns.reduce((acc: string[], pattern) => {
|
|
111
128
|
acc.push(...this._getTokensForPattern(pattern));
|
|
112
129
|
return acc;
|
|
113
130
|
}, []);
|
|
114
131
|
|
|
115
|
-
for (let token of
|
|
116
|
-
for (let nextPatternToken of nextPatternTokens){
|
|
132
|
+
for (let token of augmentedTokens) {
|
|
133
|
+
for (let nextPatternToken of nextPatternTokens) {
|
|
117
134
|
tokens.push(token + nextPatternToken);
|
|
118
135
|
}
|
|
119
136
|
}
|
|
120
137
|
|
|
121
138
|
return tokens;
|
|
122
139
|
} else {
|
|
123
|
-
|
|
124
|
-
const customTokens = this._options.customTokens[pattern.name] || [];
|
|
125
|
-
tokens.push(...customTokens);
|
|
126
|
-
return tokens;
|
|
140
|
+
return augmentedTokens;
|
|
127
141
|
}
|
|
128
142
|
}
|
|
129
143
|
|
|
130
|
-
private
|
|
144
|
+
private _getAugmentedTokens(pattern: Pattern) {
|
|
145
|
+
const customTokensMap: any = this._options.customTokens || {};
|
|
146
|
+
const leafPatterns = pattern.getPatterns();
|
|
147
|
+
const tokens: string[] = customTokensMap[pattern.name] || [];
|
|
148
|
+
|
|
149
|
+
leafPatterns.forEach(p => {
|
|
150
|
+
const augmentedTokens = customTokensMap[p.name] || [];
|
|
151
|
+
tokens.push(...p.getTokens(), ...augmentedTokens);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return tokens;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private _createSuggestions(lastIndex: number, tokens: string[]): SuggestionOption[] {
|
|
131
158
|
let substring = lastIndex === -1 ? "" : this._cursor.getChars(0, lastIndex);
|
|
132
159
|
const suggestionStrings: string[] = [];
|
|
133
160
|
const options: SuggestionOption[] = [];
|
|
@@ -140,7 +167,7 @@ export class AutoComplete {
|
|
|
140
167
|
|
|
141
168
|
if (startsWith && !alreadyExist && !isSameAsText) {
|
|
142
169
|
suggestionStrings.push(suggestion);
|
|
143
|
-
options.push(this.
|
|
170
|
+
options.push(this._createSuggestion(this._cursor.text, suggestion));
|
|
144
171
|
}
|
|
145
172
|
}
|
|
146
173
|
|
|
@@ -150,7 +177,7 @@ export class AutoComplete {
|
|
|
150
177
|
return reducedOptions;
|
|
151
178
|
}
|
|
152
179
|
|
|
153
|
-
private
|
|
180
|
+
private _createSuggestion(fullText: string, suggestion: string): SuggestionOption {
|
|
154
181
|
const furthestMatch = findMatchIndex(suggestion, fullText);
|
|
155
182
|
const text = suggestion.slice(furthestMatch);
|
|
156
183
|
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { Cursor } from "../../patterns/Cursor";
|
|
1
2
|
import { escapedCharacter } from "./escapedCharacter";
|
|
2
3
|
import { exponent } from "./exponent";
|
|
3
4
|
import { expression } from "./expression";
|
|
4
|
-
import {
|
|
5
|
+
import { assignment } from "./assignment";
|
|
5
6
|
import { infixOperator } from "./infixOperator";
|
|
6
7
|
import { integer } from "./integer";
|
|
7
8
|
import { name } from "./name";
|
|
8
9
|
import { parameters } from "./parameters";
|
|
9
10
|
import { prefixOperator } from "./prefixOperator";
|
|
11
|
+
import { objectAccess } from "./objectAccess";
|
|
10
12
|
|
|
11
13
|
describe("Ecmascript 3", () => {
|
|
12
14
|
test("Escaped Character", () => {
|
|
@@ -173,30 +175,52 @@ describe("Ecmascript 3", () => {
|
|
|
173
175
|
expect(result.ast?.value).toBe(`{"prop":1}`);
|
|
174
176
|
});
|
|
175
177
|
|
|
176
|
-
test("
|
|
178
|
+
test("Expression", () => {
|
|
179
|
+
|
|
177
180
|
let result = expression.exec("[]")
|
|
178
181
|
expect(result.ast?.value).toBe("[]");
|
|
179
182
|
|
|
180
183
|
result = expression.exec("[{}, 9, 0.9e-10, [1, 2]]")
|
|
181
184
|
expect(result.ast?.value).toBe("[{}, 9, 0.9e-10, [1, 2]]");
|
|
182
|
-
|
|
185
|
+
|
|
186
|
+
result = expression.exec(`"John"`);
|
|
187
|
+
expect(result.ast?.value).toBe(`"John"`)
|
|
188
|
+
|
|
189
|
+
result = expression.exec(`variableName.property`);
|
|
190
|
+
expect(result.ast?.value).toBe(`variableName.property`);
|
|
191
|
+
|
|
192
|
+
result = expression.exec(`{property: ""}`);
|
|
193
|
+
expect(result.ast?.value).toBe(`{property: ""}`);
|
|
194
|
+
|
|
195
|
+
const cursor = new Cursor(`name() == name.property === name2 ? {prop: name, blah: [ 0.9e-10 ]} : name`);
|
|
196
|
+
cursor.startRecording();
|
|
197
|
+
const ast = expression.parse(cursor);
|
|
198
|
+
})
|
|
183
199
|
|
|
184
200
|
test("Expression Statement", () => {
|
|
185
|
-
let result =
|
|
201
|
+
let result = assignment.exec(`name = "John"`);
|
|
186
202
|
expect(result.ast?.value).toBe(`name = "John"`);
|
|
187
203
|
|
|
188
|
-
result =
|
|
204
|
+
result = assignment.exec(`name = othername = "John"`)
|
|
189
205
|
expect(result.ast?.value).toBe(`name = othername = "John"`);
|
|
190
206
|
|
|
191
|
-
result =
|
|
207
|
+
result = assignment.exec(`name = othername.prop = "John"`)
|
|
192
208
|
expect(result.ast?.value).toBe(`name = othername.prop = "John"`);
|
|
193
209
|
|
|
194
|
-
result =
|
|
210
|
+
result = assignment.exec(`name = othername.prop += 2`)
|
|
195
211
|
expect(result.ast?.value).toBe(`name = othername.prop += 2`);
|
|
196
|
-
|
|
197
|
-
result =
|
|
198
|
-
expect(result.ast?.value).toBe(`name.prop().method(blah) = blah.prop()`);
|
|
199
|
-
|
|
212
|
+
|
|
213
|
+
result = assignment.exec(`name.prop().method(blah) = blah.prop() == ha ? first : second`)
|
|
214
|
+
expect(result.ast?.value).toBe(`name.prop().method(blah) = blah.prop() == ha ? first : second`);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
test("Object Access", () => {
|
|
219
|
+
let result = expression.exec("name.prop");
|
|
220
|
+
expect(result.ast?.value).toBe(`name.prop`);
|
|
221
|
+
|
|
222
|
+
result = expression.exec(`name.prop.anotherProp["Ha"][coolYo] === 1`);
|
|
223
|
+
expect(result.ast?.value).toBe(`name.prop.anotherProp["Ha"][coolYo] === 1`);
|
|
200
224
|
});
|
|
201
225
|
|
|
202
226
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { And } from "../../patterns/And";
|
|
2
2
|
import { Literal } from "../../patterns/Literal";
|
|
3
3
|
import { Or } from "../../patterns/Or";
|
|
4
|
-
import {
|
|
4
|
+
import { Reference } from "../../patterns/Reference";
|
|
5
5
|
import { expression } from "./expression";
|
|
6
6
|
import { optionalSpaces } from "./optionalSpaces";
|
|
7
7
|
|
|
@@ -16,14 +16,13 @@ const assignment = new And("assignment", [
|
|
|
16
16
|
optionalSpaces,
|
|
17
17
|
assignmentOperators,
|
|
18
18
|
optionalSpaces,
|
|
19
|
+
new Or("assignment-right-operand", [
|
|
20
|
+
new Reference("assignment"),
|
|
21
|
+
expression
|
|
22
|
+
]),
|
|
19
23
|
]);
|
|
20
24
|
|
|
21
|
-
const expressionStatement = new And("expressionStatement", [
|
|
22
|
-
new Repeat("assignments",
|
|
23
|
-
assignment),
|
|
24
|
-
optionalSpaces,
|
|
25
|
-
expression
|
|
26
|
-
]);
|
|
27
25
|
|
|
28
26
|
|
|
29
|
-
|
|
27
|
+
|
|
28
|
+
export { assignment }
|
|
@@ -6,8 +6,10 @@ import { Reference } from "../../patterns/Reference";
|
|
|
6
6
|
import { Repeat } from "../../patterns/Repeat";
|
|
7
7
|
import { infixOperator } from "./infixOperator";
|
|
8
8
|
import { invocation } from "./invocation";
|
|
9
|
+
import { nullKeyword } from "./keywords";
|
|
9
10
|
import { literal } from "./literal";
|
|
10
11
|
import { name } from "./name";
|
|
12
|
+
import { objectAccess } from "./objectAccess";
|
|
11
13
|
import { optionalSpaces } from "./optionalSpaces";
|
|
12
14
|
import { prefixOperator } from "./prefixOperator";
|
|
13
15
|
import { propertyAccess } from "./propertyAccess";
|
|
@@ -41,46 +43,62 @@ const prefixExpression = new And("prefix-expression", [
|
|
|
41
43
|
new Reference("expression")
|
|
42
44
|
]);
|
|
43
45
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
optionalSpaces,
|
|
47
|
-
new Reference("expression"),
|
|
48
|
-
], true);
|
|
49
|
-
|
|
50
|
-
const optionalTernary = new And("ternary", [
|
|
51
|
-
new Literal("question-mark", "?"),
|
|
52
|
-
optionalSpaces,
|
|
53
|
-
new Reference("expression"),
|
|
54
|
-
optionalSpaces,
|
|
55
|
-
new Literal("colon", ":"),
|
|
56
|
-
optionalSpaces,
|
|
57
|
-
new Reference("expression")
|
|
58
|
-
], true);
|
|
59
|
-
|
|
60
|
-
const optionalMemberAccesses = new Repeat("object-member-accesses",
|
|
61
|
-
new Or("object-member-access", [
|
|
46
|
+
const memberAccess = new Repeat("member-access",
|
|
47
|
+
new Or("member-access", [
|
|
62
48
|
invocation,
|
|
63
49
|
propertyAccess,
|
|
64
|
-
])
|
|
65
|
-
undefined, true
|
|
50
|
+
])
|
|
66
51
|
);
|
|
67
52
|
|
|
53
|
+
|
|
68
54
|
var variableName = name.clone("variable-name");
|
|
69
55
|
|
|
70
56
|
const expressions = new Or("expressions", [
|
|
71
57
|
newExpression,
|
|
72
58
|
deleteExpression,
|
|
73
|
-
groupExpression,
|
|
74
|
-
prefixExpression,
|
|
75
59
|
literal,
|
|
60
|
+
nullKeyword,
|
|
61
|
+
objectAccess,
|
|
76
62
|
variableName,
|
|
63
|
+
groupExpression,
|
|
64
|
+
prefixExpression
|
|
77
65
|
]);
|
|
78
66
|
|
|
79
|
-
const
|
|
67
|
+
const expressionBody = new And("expression-body", [
|
|
80
68
|
expressions,
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
69
|
+
memberAccess.clone(undefined, true),
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
const infixExpression = new And("infix-expression", [
|
|
73
|
+
expressionBody,
|
|
74
|
+
optionalSpaces,
|
|
75
|
+
infixOperator,
|
|
76
|
+
optionalSpaces,
|
|
77
|
+
new Or("infix-right-operand", [
|
|
78
|
+
new Reference("infix-expression"),
|
|
79
|
+
expressionBody,
|
|
80
|
+
])
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
const ternaryExpression = new And("ternary", [
|
|
84
|
+
new Or("ternary-condition", [
|
|
85
|
+
infixExpression,
|
|
86
|
+
expressionBody
|
|
87
|
+
]),
|
|
88
|
+
optionalSpaces,
|
|
89
|
+
new Literal("question-mark", "?"),
|
|
90
|
+
optionalSpaces,
|
|
91
|
+
new Reference("expression"),
|
|
92
|
+
optionalSpaces,
|
|
93
|
+
new Literal("colon", ":"),
|
|
94
|
+
optionalSpaces,
|
|
95
|
+
new Reference("expression")
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
const expression = new Or("expression", [
|
|
99
|
+
ternaryExpression,
|
|
100
|
+
infixExpression,
|
|
101
|
+
expressionBody
|
|
84
102
|
]);
|
|
85
103
|
|
|
86
104
|
export { expression }
|
|
@@ -10,8 +10,10 @@ const greaterOrEqual = new Literal("greater-or-equal", ">=");
|
|
|
10
10
|
const lessOrEqual = new Literal("less-or-equal", "<=");
|
|
11
11
|
const greater = new Literal("greater", ">");
|
|
12
12
|
const less = new Literal("less", "<");
|
|
13
|
-
const equal = new Literal("equal", "
|
|
14
|
-
const notEqual = new Literal("not-equal", "
|
|
13
|
+
const equal = new Literal("equal", "==");
|
|
14
|
+
const notEqual = new Literal("not-equal", "!=");
|
|
15
|
+
const strictEqual = new Literal("strict-equal", "===");
|
|
16
|
+
const strictNotEqual = new Literal("strict-not-equal", "!==");
|
|
15
17
|
const logicalOr = new Literal("logical-or", "||");
|
|
16
18
|
const logicalAnd = new Literal("logical-and", "&&");
|
|
17
19
|
|
|
@@ -25,6 +27,8 @@ const infixOperator = new Or("infix-operator", [
|
|
|
25
27
|
lessOrEqual,
|
|
26
28
|
greater,
|
|
27
29
|
less,
|
|
30
|
+
strictEqual,
|
|
31
|
+
strictNotEqual,
|
|
28
32
|
equal,
|
|
29
33
|
notEqual,
|
|
30
34
|
logicalOr,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { And } from "../../patterns/And";
|
|
2
|
+
import { Reference } from "../../patterns/Reference";
|
|
3
|
+
import { name } from "./name";
|
|
4
|
+
import { propertyAccess } from "./propertyAccess";
|
|
5
|
+
|
|
6
|
+
export const objectAccess = new And("object-access", [
|
|
7
|
+
name.clone("variable-name"),
|
|
8
|
+
propertyAccess,
|
|
9
|
+
]);
|
|
@@ -8,7 +8,7 @@ import { name } from "./name";
|
|
|
8
8
|
import { optionalSpaces } from "./optionalSpaces";
|
|
9
9
|
import { stringLiteral } from "./stringLiteral";
|
|
10
10
|
|
|
11
|
-
const propertyName = new Or("property-name", [stringLiteral, name]);
|
|
11
|
+
const propertyName = new Or("property-name", [stringLiteral.clone("object-property"), name.clone("object-property")]);
|
|
12
12
|
const property = new And("property", [
|
|
13
13
|
propertyName,
|
|
14
14
|
optionalSpaces,
|
|
@@ -17,12 +17,12 @@ const property = new And("property", [
|
|
|
17
17
|
new Reference("expression"),
|
|
18
18
|
]);
|
|
19
19
|
const divider = new Regex("property-divider", "\\s*,\\s*");
|
|
20
|
-
const
|
|
20
|
+
const optionalProperties = new Repeat("properties", property, divider, true);
|
|
21
21
|
|
|
22
22
|
const objectLiteral = new And("object-literal", [
|
|
23
23
|
new Literal("open-curly-bracket", "{"),
|
|
24
24
|
optionalSpaces,
|
|
25
|
-
|
|
25
|
+
optionalProperties,
|
|
26
26
|
optionalSpaces,
|
|
27
27
|
new Literal("close-curly-bracket", "}"),
|
|
28
28
|
]);
|