clarity-pattern-parser 5.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 (61) hide show
  1. package/README.md +328 -38
  2. package/TODO.md +63 -7
  3. package/dist/ast/Node.d.ts +8 -2
  4. package/dist/index.browser.js +520 -205
  5. package/dist/index.browser.js.map +1 -1
  6. package/dist/index.d.ts +6 -1
  7. package/dist/index.esm.js +519 -206
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/index.js +520 -205
  10. package/dist/index.js.map +1 -1
  11. package/dist/intellisense/AutoComplete.d.ts +34 -0
  12. package/dist/intellisense/Suggestion.d.ts +10 -0
  13. package/dist/intellisense/SuggestionOption.d.ts +4 -0
  14. package/dist/patterns/And.d.ts +8 -7
  15. package/dist/patterns/Cursor.d.ts +6 -4
  16. package/dist/patterns/CursorHistory.d.ts +2 -2
  17. package/dist/patterns/Literal.d.ts +8 -8
  18. package/dist/patterns/Not.d.ts +9 -5
  19. package/dist/patterns/Or.d.ts +8 -5
  20. package/dist/patterns/Pattern.d.ts +8 -4
  21. package/dist/patterns/Reference.d.ts +11 -7
  22. package/dist/patterns/Regex.d.ts +8 -8
  23. package/dist/patterns/Repeat.d.ts +8 -7
  24. package/package.json +1 -1
  25. package/src/ast/Node.test.ts +116 -0
  26. package/src/ast/Node.ts +71 -5
  27. package/src/index.ts +14 -3
  28. package/src/intellisense/AutoComplete.test.ts +168 -23
  29. package/src/intellisense/AutoComplete.ts +102 -21
  30. package/src/intellisense/Suggestion.ts +3 -4
  31. package/src/intellisense/javascript/Javascript.test.ts +86 -62
  32. package/src/intellisense/javascript/{expressionStatement.ts → assignment.ts} +7 -8
  33. package/src/intellisense/javascript/escapedCharacter.ts +0 -1
  34. package/src/intellisense/javascript/exponent.ts +0 -2
  35. package/src/intellisense/javascript/expression.ts +44 -26
  36. package/src/intellisense/javascript/fraction.ts +0 -2
  37. package/src/intellisense/javascript/infixOperator.ts +6 -2
  38. package/src/intellisense/javascript/keywords.ts +3 -0
  39. package/src/intellisense/javascript/objectAccess.ts +9 -0
  40. package/src/intellisense/javascript/objectLiteral.ts +3 -3
  41. package/src/intellisense/javascript/propertyAccess.ts +8 -3
  42. package/src/intellisense/javascript/stringLiteral.ts +16 -8
  43. package/src/patterns/And.test.ts +74 -50
  44. package/src/patterns/And.ts +72 -36
  45. package/src/patterns/Cursor.ts +17 -14
  46. package/src/patterns/CursorHistory.ts +8 -8
  47. package/src/patterns/Literal.test.ts +79 -38
  48. package/src/patterns/Literal.ts +34 -41
  49. package/src/patterns/Not.test.ts +99 -8
  50. package/src/patterns/Not.ts +58 -14
  51. package/src/patterns/Or.test.ts +128 -13
  52. package/src/patterns/Or.ts +46 -13
  53. package/src/patterns/Pattern.ts +8 -4
  54. package/src/patterns/Reference.test.ts +127 -28
  55. package/src/patterns/Reference.ts +62 -32
  56. package/src/patterns/Regex.test.ts +76 -35
  57. package/src/patterns/Regex.ts +35 -43
  58. package/src/patterns/Repeat.test.ts +72 -41
  59. package/src/patterns/Repeat.ts +55 -38
  60. package/src/patterns/getNextPattern.test.ts +0 -39
  61. package/src/patterns/getNextPattern.ts +0 -18
@@ -1,79 +1,160 @@
1
1
  import { Cursor } from "../patterns/Cursor";
2
- import { ParseError } from "../patterns/ParseError";
3
2
  import { Pattern } from "../patterns/Pattern";
4
3
  import { Suggestion } from "./Suggestion";
5
4
  import { SuggestionOption } from "./SuggestionOption";
6
5
 
6
+ export interface AutoCompleteOptions {
7
+ /**
8
+ * Allows for certain patterns to combine their tokens with the next tokens.
9
+ * Be very careful, this can explode to infinity pretty quick. Usually useful
10
+ * for dividers and spaces.
11
+ */
12
+ greedyPatternNames?: string[];
13
+ /**
14
+ * Allows for custom suggestions for patterns. The key is the name of the pattern
15
+ * and the string array are the tokens suggested for that pattern.
16
+ */
17
+ customTokens?: Record<string, string[]>;
18
+ }
19
+
20
+ const defaultOptions = { greedyPatternNames: [], customTokens: {} };
21
+
7
22
  export class AutoComplete {
8
23
  private _pattern: Pattern;
24
+ private _options: AutoCompleteOptions;
9
25
  private _cursor!: Cursor;
10
26
  private _text: string;
11
27
 
12
- constructor(pattern: Pattern) {
28
+ constructor(pattern: Pattern, options: AutoCompleteOptions = defaultOptions) {
13
29
  this._pattern = pattern;
30
+ this._options = options;
14
31
  this._text = "";
15
32
  }
16
33
 
34
+ /**
35
+ * @deprecated Use suggestFor instead.
36
+ * @param text The text to suggest for.
37
+ */
17
38
  suggest(text: string): Suggestion {
39
+ return this.suggestFor(text);
40
+ }
41
+
42
+ suggestFor(text: string): Suggestion {
18
43
  if (text.length === 0) {
19
44
  return {
20
45
  isComplete: false,
21
- options: this.createSuggestionsFromRoot(),
22
- nextPattern: this._pattern,
46
+ options: this._createSuggestionsFromRoot(),
47
+ errorAtIndex: 0,
23
48
  cursor: null,
24
- error: new ParseError(0, this._pattern),
49
+ ast: null
25
50
  }
26
51
  }
27
52
 
28
53
  this._text = text;
29
54
  this._cursor = new Cursor(text);
30
- this._pattern.parse(this._cursor);
55
+ const ast = this._pattern.parse(this._cursor);
31
56
 
32
57
  const leafPattern = this._cursor.leafMatch.pattern;
33
- const rootMatch = this._cursor.rootMatch.pattern;
34
- const isComplete = this._cursor.isOnLast && rootMatch === this._pattern;
35
- const options = this.createSuggestionsFromTokens();
36
- const nextPattern = isComplete ? null : leafPattern?.getNextPattern() || this._pattern;
58
+ const isComplete = ast?.value === text;
59
+ const options = this._createSuggestionsFromTokens();
60
+
61
+ let nextPatterns = [this._pattern];
62
+ let errorAtIndex = null;
63
+
64
+ if (leafPattern != null) {
65
+ nextPatterns = leafPattern.getNextPatterns();
66
+ }
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
+ }
37
75
 
38
76
  return {
39
77
  isComplete: isComplete,
40
78
  options: options,
41
- nextPattern,
79
+ errorAtIndex,
42
80
  cursor: this._cursor,
43
- error: this._cursor.furthestError
81
+ ast,
44
82
  }
45
83
  }
46
84
 
47
- private createSuggestionsFromRoot(): SuggestionOption[] {
85
+ private _createSuggestionsFromRoot(): SuggestionOption[] {
48
86
  const suggestions: SuggestionOption[] = [];
49
87
  const tokens = this._pattern.getTokens();
50
88
 
51
89
  for (const token of tokens) {
52
- suggestions.push(this.createSuggestion("", token));
90
+ suggestions.push(this._createSuggestion("", token));
53
91
  }
54
92
 
55
93
  return suggestions;
56
94
  }
57
95
 
58
- private createSuggestionsFromTokens(): SuggestionOption[] {
96
+ private _createSuggestionsFromTokens(): SuggestionOption[] {
59
97
  const leafMatch = this._cursor.leafMatch;
60
98
 
61
99
  if (!leafMatch.pattern) {
62
- return this.createSuggestions(-1, this._pattern.getTokens());
100
+ return this._createSuggestions(-1, this._getTokensForPattern(this._pattern));
63
101
  }
64
102
 
65
103
  const leafPattern = leafMatch.pattern;
66
104
  const parent = leafMatch.pattern.parent;
67
105
 
68
106
  if (parent !== null && leafMatch.node != null) {
69
- const tokens = parent.getNextTokens(leafPattern);
70
- return this.createSuggestions(leafMatch.node.lastIndex, tokens);
107
+ const patterns = leafPattern.getNextPatterns();
108
+
109
+ const tokens = patterns.reduce((acc: string[], pattern) => {
110
+ acc.push(...this._getTokensForPattern(pattern));
111
+ return acc;
112
+ }, []);
113
+
114
+ return this._createSuggestions(leafMatch.node.lastIndex, tokens);
71
115
  } else {
72
116
  return [];
73
117
  }
74
118
  }
75
119
 
76
- private createSuggestions(lastIndex: number, tokens: string[]): SuggestionOption[] {
120
+ private _getTokensForPattern(pattern: Pattern) {
121
+ const augmentedTokens = this._getAugmentedTokens(pattern)
122
+
123
+ if (this._options.greedyPatternNames != null && this._options.greedyPatternNames.includes(pattern.name)) {
124
+ const nextPatterns = pattern.getNextPatterns();
125
+ const tokens: string[] = [];
126
+
127
+ const nextPatternTokens = nextPatterns.reduce((acc: string[], pattern) => {
128
+ acc.push(...this._getTokensForPattern(pattern));
129
+ return acc;
130
+ }, []);
131
+
132
+ for (let token of augmentedTokens) {
133
+ for (let nextPatternToken of nextPatternTokens) {
134
+ tokens.push(token + nextPatternToken);
135
+ }
136
+ }
137
+
138
+ return tokens;
139
+ } else {
140
+ return augmentedTokens;
141
+ }
142
+ }
143
+
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[] {
77
158
  let substring = lastIndex === -1 ? "" : this._cursor.getChars(0, lastIndex);
78
159
  const suggestionStrings: string[] = [];
79
160
  const options: SuggestionOption[] = [];
@@ -86,7 +167,7 @@ export class AutoComplete {
86
167
 
87
168
  if (startsWith && !alreadyExist && !isSameAsText) {
88
169
  suggestionStrings.push(suggestion);
89
- options.push(this.createSuggestion(this._cursor.text, suggestion));
170
+ options.push(this._createSuggestion(this._cursor.text, suggestion));
90
171
  }
91
172
  }
92
173
 
@@ -96,7 +177,7 @@ export class AutoComplete {
96
177
  return reducedOptions;
97
178
  }
98
179
 
99
- private createSuggestion(fullText: string, suggestion: string): SuggestionOption {
180
+ private _createSuggestion(fullText: string, suggestion: string): SuggestionOption {
100
181
  const furthestMatch = findMatchIndex(suggestion, fullText);
101
182
  const text = suggestion.slice(furthestMatch);
102
183
 
@@ -1,13 +1,12 @@
1
+ import { Node } from "../ast/Node";
1
2
  import { Cursor } from "../patterns/Cursor";
2
- import { Match } from "../patterns/CursorHistory";
3
- import { ParseError } from "../patterns/ParseError";
4
3
  import { Pattern } from "../patterns/Pattern";
5
4
  import { SuggestionOption } from "./SuggestionOption";
6
5
 
7
6
  export interface Suggestion {
8
7
  isComplete: boolean;
9
8
  options: SuggestionOption[];
10
- nextPattern: Pattern | null;
9
+ errorAtIndex: number | null;
11
10
  cursor: Cursor | null;
12
- error: ParseError | null;
11
+ ast: Node | null;
13
12
  }
@@ -1,202 +1,226 @@
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", () => {
13
- let result = escapedCharacter.parseText(`\\"`);
15
+ let result = escapedCharacter.exec(`\\"`);
14
16
  expect(result.ast?.value).toBe(`\\"`)
15
17
 
16
- result = escapedCharacter.parseText(`\\'`)
18
+ result = escapedCharacter.exec(`\\'`)
17
19
  expect(result.ast?.value).toBe(`\\'`)
18
20
 
19
- result = escapedCharacter.parseText(`\\\\`)
21
+ result = escapedCharacter.exec(`\\\\`)
20
22
  expect(result.ast?.value).toBe(`\\\\`)
21
23
 
22
- result = escapedCharacter.parseText(`\\/`)
24
+ result = escapedCharacter.exec(`\\/`)
23
25
  expect(result.ast?.value).toBe(`\\/`)
24
26
 
25
- result = escapedCharacter.parseText(`\\f`)
27
+ result = escapedCharacter.exec(`\\f`)
26
28
  expect(result.ast?.value).toBe(`\\f`)
27
29
 
28
- result = escapedCharacter.parseText(`\\t`)
30
+ result = escapedCharacter.exec(`\\t`)
29
31
  expect(result.ast?.value).toBe(`\\t`)
30
32
 
31
- result = escapedCharacter.parseText(`\\u00E9`)
33
+ result = escapedCharacter.exec(`\\u00E9`)
32
34
  expect(result.ast?.value).toBe(`\\u00E9`)
33
35
  });
34
36
 
35
37
  test("Exponent", () => {
36
- let result = exponent.parseText("e+1");
38
+ let result = exponent.exec("e+1");
37
39
  expect(result.ast?.value).toBe("e+1")
38
40
 
39
- result = exponent.parseText("e-1");
41
+ result = exponent.exec("e-1");
40
42
  expect(result.ast?.value).toBe("e-1")
41
43
 
42
- result = exponent.parseText("E+1");
44
+ result = exponent.exec("E+1");
43
45
  expect(result.ast?.value).toBe("E+1")
44
46
 
45
- result = exponent.parseText("E-1");
47
+ result = exponent.exec("E-1");
46
48
  expect(result.ast?.value).toBe("E-1")
47
49
 
48
- result = exponent.parseText("e+11");
50
+ result = exponent.exec("e+11");
49
51
  expect(result.ast?.value).toBe("e+11")
50
52
 
51
- result = exponent.parseText("11");
53
+ result = exponent.exec("11");
52
54
  expect(result.ast).toBeNull()
53
55
  });
54
56
 
55
57
  test("Integer", () => {
56
- let result = integer.parseText("0");
58
+ let result = integer.exec("0");
57
59
  expect(result.ast?.value).toBe("0");
58
60
 
59
- result = integer.parseText("1");
61
+ result = integer.exec("1");
60
62
  expect(result.ast?.value).toBe("1");
61
63
 
62
- result = integer.parseText("100");
64
+ result = integer.exec("100");
63
65
  expect(result.ast?.value).toBe("100");
64
66
 
65
- result = integer.parseText(".1");
67
+ result = integer.exec(".1");
66
68
  expect(result.ast).toBeNull();
67
69
  });
68
70
 
69
71
  test("Infix Operator", () => {
70
- let result = infixOperator.parseText("*");
72
+ let result = infixOperator.exec("*");
71
73
  expect(result.ast?.value).toBe("*");
72
74
 
73
- result = infixOperator.parseText("/");
75
+ result = infixOperator.exec("/");
74
76
  expect(result.ast?.value).toBe("/");
75
77
 
76
- result = infixOperator.parseText("%");
78
+ result = infixOperator.exec("%");
77
79
  expect(result.ast?.value).toBe("%");
78
80
 
79
- result = infixOperator.parseText("+");
81
+ result = infixOperator.exec("+");
80
82
  expect(result.ast?.value).toBe("+");
81
83
 
82
- result = infixOperator.parseText("-");
84
+ result = infixOperator.exec("-");
83
85
  expect(result.ast?.value).toBe("-");
84
86
 
85
- result = infixOperator.parseText(">=");
87
+ result = infixOperator.exec(">=");
86
88
  expect(result.ast?.value).toBe(">=");
87
89
 
88
- result = infixOperator.parseText("<=");
90
+ result = infixOperator.exec("<=");
89
91
  expect(result.ast?.value).toBe("<=");
90
92
 
91
- result = infixOperator.parseText(">");
93
+ result = infixOperator.exec(">");
92
94
  expect(result.ast?.value).toBe(">");
93
95
 
94
- result = infixOperator.parseText("<");
96
+ result = infixOperator.exec("<");
95
97
  expect(result.ast?.value).toBe("<");
96
98
 
97
- result = infixOperator.parseText("===");
99
+ result = infixOperator.exec("===");
98
100
  expect(result.ast?.value).toBe("===");
99
101
 
100
- result = infixOperator.parseText("!==");
102
+ result = infixOperator.exec("!==");
101
103
  expect(result.ast?.value).toBe("!==");
102
104
 
103
- result = infixOperator.parseText("||");
105
+ result = infixOperator.exec("||");
104
106
  expect(result.ast?.value).toBe("||");
105
107
 
106
- result = infixOperator.parseText("&&");
108
+ result = infixOperator.exec("&&");
107
109
  expect(result.ast?.value).toBe("&&");
108
110
 
109
- result = infixOperator.parseText("bad");
111
+ result = infixOperator.exec("bad");
110
112
  expect(result.ast).toBeNull();
111
113
  });
112
114
 
113
115
 
114
116
  test("Name", () => {
115
- let result = name.parseText("p_0");
117
+ let result = name.exec("p_0");
116
118
  expect(result.ast?.value).toBe("p_0");
117
119
 
118
- result = name.parseText("_0");
120
+ result = name.exec("_0");
119
121
  expect(result.ast?.value).toBe("_0");
120
122
 
121
- result = name.parseText("0");
123
+ result = name.exec("0");
122
124
  expect(result.ast).toBeNull();
123
125
 
124
- result = name.parseText("_");
126
+ result = name.exec("_");
125
127
  expect(result.ast?.value).toBe("_");
126
128
  });
127
129
 
128
130
  test("Parameters", () => {
129
- let result = parameters.parseText("(param1)");
131
+ let result = parameters.exec("(param1)");
130
132
  expect(result.ast?.value).toBe("(param1)");
131
133
 
132
- result = parameters.parseText("(param1, param2)");
134
+ result = parameters.exec("(param1, param2)");
133
135
  expect(result.ast?.value).toBe("(param1, param2)");
134
136
 
135
- result = parameters.parseText("(param1, param2,)");
137
+ result = parameters.exec("(param1, param2,)");
136
138
  expect(result.ast).toBeNull();
137
139
  });
138
140
 
139
141
  test("Prefix Operator", () => {
140
- let result = prefixOperator.parseText("typeof ");
142
+ let result = prefixOperator.exec("typeof ");
141
143
  expect(result.ast?.value).toBe("typeof ");
142
144
 
143
- result = prefixOperator.parseText("+");
145
+ result = prefixOperator.exec("+");
144
146
  expect(result.ast?.value).toBe("+");
145
147
 
146
- result = prefixOperator.parseText("-");
148
+ result = prefixOperator.exec("-");
147
149
  expect(result.ast?.value).toBe("-");
148
150
 
149
- result = prefixOperator.parseText("!");
151
+ result = prefixOperator.exec("!");
150
152
  expect(result.ast?.value).toBe("!");
151
153
 
152
- result = prefixOperator.parseText("a");
154
+ result = prefixOperator.exec("a");
153
155
  expect(result.ast).toBeNull();
154
156
  });
155
157
 
156
158
  test("Object Literal", () => {
157
- let result = expression.parseText(`{}`)
159
+ let result = expression.exec(`{}`)
158
160
  expect(result.ast?.value).toBe("{}");
159
161
 
160
- result = expression.parseText(`{prop:{}}`)
162
+ result = expression.exec(`{prop:{}}`)
161
163
  expect(result.ast?.value).toBe("{prop:{}}");
162
164
 
163
- result = expression.parseText(`{prop:"value"}`)
165
+ result = expression.exec(`{prop:"value"}`)
164
166
  expect(result.ast?.value).toBe(`{prop:"value"}`);
165
167
 
166
- result = expression.parseText(`{prop:0.9}`)
168
+ result = expression.exec(`{prop:0.9}`)
167
169
  expect(result.ast?.value).toBe(`{prop:0.9}`);
168
170
 
169
- result = expression.parseText(`{prop:1}`)
171
+ result = expression.exec(`{prop:1}`)
170
172
  expect(result.ast?.value).toBe(`{prop:1}`);
171
173
 
172
- result = expression.parseText(`{"prop":1}`)
174
+ result = expression.exec(`{"prop":1}`)
173
175
  expect(result.ast?.value).toBe(`{"prop":1}`);
174
176
  });
175
177
 
176
- test("Array Literal", () => {
177
- let result = expression.parseText("[]")
178
+ test("Expression", () => {
179
+
180
+ let result = expression.exec("[]")
178
181
  expect(result.ast?.value).toBe("[]");
179
182
 
180
- result = expression.parseText("[{}, 9, 0.9e-10, [1, 2]]")
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.parseText(`name = "John"`);
201
+ let result = assignment.exec(`name = "John"`);
186
202
  expect(result.ast?.value).toBe(`name = "John"`);
187
203
 
188
- result = expressionStatement.parseText(`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.parseText(`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.parseText(`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.parseText(`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 }
@@ -42,7 +42,6 @@ const specialCharacter = new Or("special-character", [
42
42
  ]);
43
43
 
44
44
  const escapedCharacter = new And("escaped-character", [backslash, specialCharacter])
45
- escapedCharacter.enableAstReduction();
46
45
 
47
46
  export {
48
47
  escapedCharacter
@@ -21,6 +21,4 @@ const exponent = new And("exponent", [
21
21
  digit,
22
22
  ]);
23
23
 
24
- exponent.enableAstReduction();
25
-
26
24
  export { exponent }
@@ -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 }
@@ -6,8 +6,6 @@ const period = new Literal("period", ".");
6
6
  const digit = new Regex("digit", "\\d+");
7
7
  const fraction = new And("fraction", [period, digit]);
8
8
 
9
- fraction.enableAstReduction();
10
-
11
9
  export {
12
10
  fraction
13
11
  }