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.
Files changed (46) hide show
  1. package/TODO.md +9 -7
  2. package/dist/index.browser.js +72 -22
  3. package/dist/index.browser.js.map +1 -1
  4. package/dist/index.esm.js +72 -22
  5. package/dist/index.esm.js.map +1 -1
  6. package/dist/index.js +72 -22
  7. package/dist/index.js.map +1 -1
  8. package/dist/intellisense/AutoComplete.d.ts +12 -6
  9. package/dist/intellisense/Suggestion.d.ts +1 -2
  10. package/dist/patterns/And.d.ts +1 -0
  11. package/dist/patterns/Literal.d.ts +1 -0
  12. package/dist/patterns/Not.d.ts +1 -0
  13. package/dist/patterns/Or.d.ts +1 -0
  14. package/dist/patterns/Pattern.d.ts +2 -1
  15. package/dist/patterns/Reference.d.ts +1 -0
  16. package/dist/patterns/Regex.d.ts +1 -0
  17. package/dist/patterns/Repeat.d.ts +1 -0
  18. package/package.json +1 -1
  19. package/src/ast/Node.test.ts +6 -0
  20. package/src/intellisense/AutoComplete.test.ts +104 -37
  21. package/src/intellisense/AutoComplete.ts +51 -24
  22. package/src/intellisense/Suggestion.ts +1 -1
  23. package/src/intellisense/javascript/Javascript.test.ts +35 -11
  24. package/src/intellisense/javascript/{expressionStatement.ts → assignment.ts} +7 -8
  25. package/src/intellisense/javascript/expression.ts +44 -26
  26. package/src/intellisense/javascript/infixOperator.ts +6 -2
  27. package/src/intellisense/javascript/keywords.ts +3 -0
  28. package/src/intellisense/javascript/objectAccess.ts +9 -0
  29. package/src/intellisense/javascript/objectLiteral.ts +3 -3
  30. package/src/intellisense/javascript/propertyAccess.ts +8 -3
  31. package/src/intellisense/javascript/stringLiteral.ts +16 -8
  32. package/src/patterns/And.test.ts +13 -0
  33. package/src/patterns/And.ts +14 -0
  34. package/src/patterns/Literal.test.ts +9 -0
  35. package/src/patterns/Literal.ts +4 -0
  36. package/src/patterns/Not.test.ts +11 -0
  37. package/src/patterns/Not.ts +4 -0
  38. package/src/patterns/Or.test.ts +11 -0
  39. package/src/patterns/Or.ts +10 -0
  40. package/src/patterns/Pattern.ts +2 -1
  41. package/src/patterns/Reference.test.ts +10 -0
  42. package/src/patterns/Reference.ts +4 -0
  43. package/src/patterns/Regex.test.ts +9 -0
  44. package/src/patterns/Regex.ts +4 -0
  45. package/src/patterns/Repeat.test.ts +9 -0
  46. 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.suggest("");
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.nextPatterns[0]).toBe(name);
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.suggest("John Doe");
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.nextPatterns.length).toBe(0);
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.suggest("John ");
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.length).toBe(2);
50
- expect(result.nextPatterns.length).toBe(1);
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.suggest("John Doe");
69
-
70
- expect(result.ast?.value).toBe("John Doe");
71
- expect(result.options.length).toBe(1);
72
- expect(result.nextPatterns.length).toBe(result.options.length);
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
- let result = autoComplete.suggest("Na");
91
+ const result = autoComplete.suggestFor("Na");
92
+ const expectedOptions = [{
93
+ text: "me",
94
+ startIndex: 2
95
+ }];
81
96
 
82
- expect(result.options[0].text).toBe("me");
83
- expect(result.options[0].startIndex).toBe(2);
84
- expect(result.nextPatterns[0]).toBe(name);
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
- let result = autoComplete.suggest("Ni");
107
+ const result = autoComplete.suggestFor("Ni");
108
+
109
+ const expectedOptions = [{
110
+ text: "ame",
111
+ startIndex: 1
112
+ }];
92
113
 
93
- expect(result.options[0].text).toBe("ame");
94
- expect(result.options[0].startIndex).toBe(1);
95
- //expect(result.nextPattern).toBe(name);
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
- let result = autoComplete.suggest("Name");
124
+ const text = "Name"
125
+ const result = autoComplete.suggestFor(text);
103
126
 
104
- expect(result.options.length).toBe(0);
105
- expect(result.nextPatterns.length).toBe(0);
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 = "John";
151
+ const text = "Jack";
127
152
  const autoComplete = new AutoComplete(fullName, autoCompleteOptions);
128
- const { options, ast, nextPatterns } = autoComplete.suggest(text);
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
- "John Doe",
138
- "John Smith",
139
- "John Sparrow",
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(nextPatterns.length).toBe(1);
145
- expect(nextPatterns[0].name).toBe("space");
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: string[];
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: Record<string, string[]>;
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.createSuggestionsFromRoot(),
39
- nextPatterns: [this._pattern],
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.createSuggestionsFromTokens();
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
- nextPatterns,
79
+ errorAtIndex,
63
80
  cursor: this._cursor,
64
81
  ast,
65
82
  }
66
83
  }
67
84
 
68
- private createSuggestionsFromRoot(): SuggestionOption[] {
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.createSuggestion("", token));
90
+ suggestions.push(this._createSuggestion("", token));
74
91
  }
75
92
 
76
93
  return suggestions;
77
94
  }
78
95
 
79
- private createSuggestionsFromTokens(): SuggestionOption[] {
96
+ private _createSuggestionsFromTokens(): SuggestionOption[] {
80
97
  const leafMatch = this._cursor.leafMatch;
81
98
 
82
99
  if (!leafMatch.pattern) {
83
- return this.createSuggestions(-1, this._getTokensForPattern(this._pattern));
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.createSuggestions(leafMatch.node.lastIndex, tokens);
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
- if (this._options.greedyPatternNames.includes(pattern.name)) {
106
- const greedyTokens = pattern.getTokens();
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 greedyTokens){
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
- const tokens = pattern.getTokens();
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 createSuggestions(lastIndex: number, tokens: string[]): SuggestionOption[] {
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.createSuggestion(this._cursor.text, suggestion));
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 createSuggestion(fullText: string, suggestion: string): SuggestionOption {
180
+ private _createSuggestion(fullText: string, suggestion: string): SuggestionOption {
154
181
  const furthestMatch = findMatchIndex(suggestion, fullText);
155
182
  const text = suggestion.slice(furthestMatch);
156
183
 
@@ -6,7 +6,7 @@ import { SuggestionOption } from "./SuggestionOption";
6
6
  export interface Suggestion {
7
7
  isComplete: boolean;
8
8
  options: SuggestionOption[];
9
- nextPatterns: Pattern[];
9
+ errorAtIndex: number | null;
10
10
  cursor: Cursor | null;
11
11
  ast: Node | null;
12
12
  }
@@ -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 { expressionStatement } from "./expressionStatement";
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("Array Literal", () => {
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 = expressionStatement.exec(`name = "John"`);
201
+ let result = assignment.exec(`name = "John"`);
186
202
  expect(result.ast?.value).toBe(`name = "John"`);
187
203
 
188
- result = expressionStatement.exec(`name = othername = "John"`)
204
+ result = assignment.exec(`name = othername = "John"`)
189
205
  expect(result.ast?.value).toBe(`name = othername = "John"`);
190
206
 
191
- result = expressionStatement.exec(`name = othername.prop = "John"`)
207
+ result = assignment.exec(`name = othername.prop = "John"`)
192
208
  expect(result.ast?.value).toBe(`name = othername.prop = "John"`);
193
209
 
194
- result = expressionStatement.exec(`name = othername.prop += 2`)
210
+ result = assignment.exec(`name = othername.prop += 2`)
195
211
  expect(result.ast?.value).toBe(`name = othername.prop += 2`);
196
-
197
- result = expressionStatement.exec(`name.prop().method(blah) = blah.prop()`)
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 { Repeat } from "../../patterns/Repeat";
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
- export { expressionStatement }
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 optionalInfix = new And("infix-expression", [
45
- infixOperator,
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 expression = new And("expression", [
67
+ const expressionBody = new And("expression-body", [
80
68
  expressions,
81
- optionalInfix,
82
- optionalTernary,
83
- optionalMemberAccesses
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,3 @@
1
+ import { Literal } from "../../patterns/Literal";
2
+
3
+ export const nullKeyword = new Literal("null", "null");
@@ -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 properties = new Repeat("properties", property, divider, true);
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
- properties,
25
+ optionalProperties,
26
26
  optionalSpaces,
27
27
  new Literal("close-curly-bracket", "}"),
28
28
  ]);