clarity-pattern-parser 11.3.10 → 11.3.11

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.
Binary file
@@ -2723,13 +2723,36 @@
2723
2723
  }
2724
2724
  _compileAtomNode() {
2725
2725
  let node = this._atomNode;
2726
- if (this._prefixNode != null && this._atomNode != null) {
2727
- node = this._prefixNode.findRoot();
2728
- this._prefixPlaceholder.replaceWith(this._atomNode);
2726
+ if (this._prefixNode != null && this._postfixNode != null && this._atomNode != null) {
2727
+ let firstNode = this._prefixNode;
2728
+ let secondNode = this._postfixNode;
2729
+ let firstPlaceholder = this._prefixPlaceholder;
2730
+ let secondPlaceholder = this._postfixPlaceholder;
2731
+ const prefixName = this._prefixNode.name;
2732
+ const postfixName = this._postfixNode.name;
2733
+ const prefixPrecedence = this._getPrecedence(prefixName);
2734
+ const postfixPrecedence = this._getPrecedence(postfixName);
2735
+ // The Precedence is the index of the patterns, so its lower not higher :\
2736
+ if (prefixPrecedence > postfixPrecedence) {
2737
+ firstNode = this._postfixNode;
2738
+ secondNode = this._prefixNode;
2739
+ firstPlaceholder = this._postfixPlaceholder;
2740
+ secondPlaceholder = this._prefixPlaceholder;
2741
+ }
2742
+ node = firstNode.findRoot();
2743
+ firstPlaceholder.replaceWith(this._atomNode);
2744
+ secondPlaceholder.replaceWith(node);
2745
+ node = secondNode;
2729
2746
  }
2730
- if (this._postfixNode != null && node != null) {
2731
- this._postfixPlaceholder.replaceWith(node);
2732
- node = this._postfixNode;
2747
+ else {
2748
+ if (this._prefixNode != null && this._atomNode != null) {
2749
+ node = this._prefixNode.findRoot();
2750
+ this._prefixPlaceholder.replaceWith(this._atomNode);
2751
+ }
2752
+ if (this._postfixNode != null && node != null) {
2753
+ this._postfixPlaceholder.replaceWith(node);
2754
+ node = this._postfixNode;
2755
+ }
2733
2756
  }
2734
2757
  this._prefixNode = null;
2735
2758
  this._atomNode = null;
@@ -2837,7 +2860,7 @@
2837
2860
  }
2838
2861
  _organizePatterns(patterns) {
2839
2862
  const finalPatterns = [];
2840
- patterns.forEach((pattern) => {
2863
+ patterns.forEach((pattern, index) => {
2841
2864
  if (this._isAtom(pattern)) {
2842
2865
  const atom = pattern.clone();
2843
2866
  atom.parent = this;
@@ -2848,6 +2871,7 @@
2848
2871
  const name = this._extractName(pattern);
2849
2872
  const prefix = this._extractPrefix(pattern);
2850
2873
  prefix.parent = this;
2874
+ this._precedenceMap[name] = index;
2851
2875
  this._prefixPatterns.push(prefix);
2852
2876
  this._prefixNames.push(name);
2853
2877
  finalPatterns.push(prefix);
@@ -2856,6 +2880,7 @@
2856
2880
  const name = this._extractName(pattern);
2857
2881
  const postfix = this._extractPostfix(pattern);
2858
2882
  postfix.parent = this;
2883
+ this._precedenceMap[name] = index;
2859
2884
  this._postfixPatterns.push(postfix);
2860
2885
  this._postfixNames.push(name);
2861
2886
  finalPatterns.push(postfix);
@@ -2864,7 +2889,7 @@
2864
2889
  const name = this._extractName(pattern);
2865
2890
  const binary = this._extractBinary(pattern);
2866
2891
  binary.parent = this;
2867
- this._precedenceMap[name] = this._binaryPatterns.length;
2892
+ this._precedenceMap[name] = index;
2868
2893
  this._binaryPatterns.push(binary);
2869
2894
  this._binaryNames.push(name);
2870
2895
  if (pattern.type === "right-associated") {
@@ -3662,6 +3687,8 @@
3662
3687
  return this._isRecursivePattern(name, pattern);
3663
3688
  }
3664
3689
  _isRecursivePattern(name, pattern) {
3690
+ // Because we don't know if the pattern is a sequence with a reference we have to just assume it is.
3691
+ // The better solution here would be to not have options at all and just use expresssion pattern instead.
3665
3692
  if (pattern.type === "reference") {
3666
3693
  return true;
3667
3694
  }
@@ -4013,26 +4040,30 @@
4013
4040
  this._options = options;
4014
4041
  this._text = "";
4015
4042
  }
4043
+ suggestFor(text) {
4044
+ return this.suggestForWithCursor(new Cursor(text));
4045
+ }
4016
4046
  suggestForWithCursor(cursor) {
4017
4047
  cursor.moveTo(0);
4018
4048
  this._cursor = cursor;
4019
4049
  this._text = cursor.text;
4020
4050
  this._cursor.startRecording();
4021
4051
  if (cursor.length === 0) {
4022
- return {
4052
+ const suggestion = {
4023
4053
  isComplete: false,
4024
- options: this._createSuggestionsFromRoot(),
4054
+ options: this._createSuggestionOptionsFromMatch(),
4025
4055
  error: new ParseError(0, 0, this._pattern),
4026
4056
  errorAtIndex: 0,
4027
4057
  cursor,
4028
4058
  ast: null
4029
4059
  };
4060
+ return suggestion;
4030
4061
  }
4031
4062
  let errorAtIndex = null;
4032
4063
  let error = null;
4033
4064
  const ast = this._pattern.parse(this._cursor);
4034
4065
  const isComplete = (ast === null || ast === void 0 ? void 0 : ast.value) === this._text;
4035
- const options = this._getAllOptions();
4066
+ const options = this._getAllSuggestionsOptions();
4036
4067
  if (!isComplete && options.length > 0 && !this._cursor.hasError) {
4037
4068
  const startIndex = options.reduce((lowestIndex, o) => {
4038
4069
  return Math.min(lowestIndex, o.startIndex);
@@ -4079,14 +4110,11 @@
4079
4110
  }
4080
4111
  return 0;
4081
4112
  }
4082
- suggestFor(text) {
4083
- return this.suggestForWithCursor(new Cursor(text));
4084
- }
4085
- _getAllOptions() {
4086
- const errorMatches = this._getOptionsFromErrors();
4087
- const leafMatches = this._cursor.leafMatches.map((m) => this._createSuggestionsFromMatch(m)).flat();
4113
+ _getAllSuggestionsOptions() {
4114
+ const errorMatchOptions = this._createSuggestionOptionsFromErrors();
4115
+ const leafMatchOptions = this._cursor.leafMatches.map((m) => this._createSuggestionOptionsFromMatch(m)).flat();
4088
4116
  const finalResults = [];
4089
- [...leafMatches, ...errorMatches].forEach(m => {
4117
+ [...leafMatchOptions, ...errorMatchOptions].forEach(m => {
4090
4118
  const index = finalResults.findIndex(f => m.text === f.text);
4091
4119
  if (index === -1) {
4092
4120
  finalResults.push(m);
@@ -4094,135 +4122,190 @@
4094
4122
  });
4095
4123
  return finalResults;
4096
4124
  }
4097
- _getOptionsFromErrors() {
4125
+ _createSuggestionOptionsFromErrors() {
4098
4126
  // These errored because the length of the string.
4099
4127
  const errors = this._cursor.errors.filter(e => e.lastIndex === this._cursor.length);
4100
- const suggestions = errors.map(e => {
4101
- const tokens = this._getTokensForPattern(e.pattern);
4102
- const adjustedTokens = new Set();
4103
- const currentText = this._cursor.getChars(e.startIndex, e.lastIndex);
4104
- tokens.forEach((token) => {
4105
- if (token.startsWith(currentText) && token.length > currentText.length) {
4106
- const difference = token.length - currentText.length;
4107
- const suggestedText = token.slice(-difference);
4108
- adjustedTokens.add(suggestedText);
4109
- }
4110
- });
4111
- return Array.from(adjustedTokens).map(t => {
4112
- return {
4113
- text: t,
4114
- startIndex: e.lastIndex,
4115
- };
4116
- });
4117
- });
4118
- return suggestions.flat();
4119
- }
4120
- _createSuggestionsFromRoot() {
4121
- const suggestions = [];
4122
- const tokens = [...this._pattern.getTokens(), ...this._getTokensForPattern(this._pattern)];
4123
- for (const token of tokens) {
4124
- if (suggestions.findIndex(s => s.text === token) === -1) {
4125
- suggestions.push(this._createSuggestion("", token));
4126
- }
4127
- }
4128
- return suggestions;
4129
- }
4130
- _createSuggestionsFromMatch(match) {
4131
- if (match.pattern == null) {
4132
- return this._createSuggestions(-1, this._getTokensForPattern(this._pattern));
4133
- }
4134
- if (match.node != null) {
4135
- const textStartingMatch = this._text.slice(match.node.startIndex, match.node.endIndex);
4136
- const currentPatternsTokens = this._getTokensForPattern(match.pattern);
4137
- /**
4138
- * Compares tokens to current text and extracts remainder tokens
4139
- * - IE. **currentText:** *abc*, **baseToken:** *abcdef*, **trailingToken:** *def*
4140
- */
4141
- const trailingTokens = currentPatternsTokens.reduce((acc, token) => {
4142
- if (token.startsWith(textStartingMatch)) {
4143
- const sliced = token.slice(textStartingMatch.length);
4144
- if (sliced !== '') {
4145
- acc.push(sliced);
4146
- }
4147
- }
4148
- return acc;
4149
- }, []);
4128
+ const errorSuggestionOptions = errors.map(parseError => {
4129
+ const currentText = this._cursor.getChars(parseError.startIndex, parseError.lastIndex);
4130
+ const compositeSuggestions = this._getCompositeSuggestionsForPattern(parseError.pattern);
4131
+ const trimmedErrorCompositeSuggestions = this._trimSuggestionsByExistingText(currentText, compositeSuggestions);
4132
+ return this._createSuggestions(parseError.lastIndex, trimmedErrorCompositeSuggestions);
4133
+ }).flat();
4134
+ const dedupedErrorSuggestionOptions = this._deDupeCompositeSuggestions(errorSuggestionOptions);
4135
+ return dedupedErrorSuggestionOptions;
4136
+ }
4137
+ _createSuggestionOptionsFromMatch(match) {
4138
+ if ((match === null || match === void 0 ? void 0 : match.pattern) == null) {
4139
+ const compositeSuggestions = this._getCompositeSuggestionsForPattern(this._pattern);
4140
+ return this._createSuggestions(-1, compositeSuggestions);
4141
+ }
4142
+ if ((match === null || match === void 0 ? void 0 : match.node) != null) {
4143
+ const currentText = this._text.slice(match.node.startIndex, match.node.endIndex);
4144
+ /**Captures suggestions for a "completed" match pattern that still has existing possible suggestions.
4145
+ * particularly relevant when working with set/custom tokens.
4146
+ */
4147
+ const matchCompositeSuggestions = this._getCompositeSuggestionsForPattern(match.pattern);
4148
+ const trimmedMatchCompositeSuggestions = this._trimSuggestionsByExistingText(currentText, matchCompositeSuggestions);
4150
4149
  const leafPatterns = match.pattern.getNextPatterns();
4151
- const leafTokens = leafPatterns.reduce((acc, leafPattern) => {
4152
- acc.push(...this._getTokensForPattern(leafPattern));
4153
- return acc;
4154
- }, []);
4155
- const allTokens = [...trailingTokens, ...leafTokens];
4156
- return this._createSuggestions(match.node.lastIndex, allTokens);
4150
+ const leafCompositeSuggestions = leafPatterns.flatMap(leafPattern => this._getCompositeSuggestionsForPattern(leafPattern));
4151
+ const allCompositeSuggestions = [...leafCompositeSuggestions, ...trimmedMatchCompositeSuggestions,];
4152
+ const dedupedCompositeSuggestions = this._deDupeCompositeSuggestions(allCompositeSuggestions);
4153
+ return this._createSuggestions(match.node.lastIndex, dedupedCompositeSuggestions);
4157
4154
  }
4158
4155
  else {
4159
4156
  return [];
4160
4157
  }
4161
4158
  }
4162
- _getTokensForPattern(pattern) {
4163
- const augmentedTokens = this._getAugmentedTokens(pattern);
4159
+ /**
4160
+ * Compares suggestions with provided text and removes completed sub-sequences and preceding text
4161
+ * - IE. **currentText:** *abc*, **sequence:** *[{ab}{cd}{ef}*
4162
+ * - refines to {d}{ef}
4163
+ */
4164
+ _trimSuggestionsByExistingText(currentText, compositeSuggestions) {
4165
+ const trimmedSuggestions = compositeSuggestions.reduce((acc, compositeSuggestion) => {
4166
+ if (compositeSuggestion.text.startsWith(currentText)) {
4167
+ const filteredSegments = this._filterCompletedSubSegments(currentText, compositeSuggestion);
4168
+ const slicedSuggestionText = compositeSuggestion.text.slice(currentText.length);
4169
+ if (slicedSuggestionText !== '') {
4170
+ const refinedCompositeSuggestion = {
4171
+ text: slicedSuggestionText,
4172
+ suggestionSequence: filteredSegments,
4173
+ };
4174
+ acc.push(refinedCompositeSuggestion);
4175
+ }
4176
+ }
4177
+ return acc;
4178
+ }, []);
4179
+ return trimmedSuggestions;
4180
+ }
4181
+ /** Removed segments already accounted for in the existing text.
4182
+ * ie. sequence pattern segments ≈ [{look}, {an example}, {phrase}]
4183
+ * fullText = "look an"
4184
+ * remove {look} segment as its already been completed by the existing text.
4185
+ */
4186
+ _filterCompletedSubSegments(currentText, compositeSuggestion) {
4187
+ let elementsToRemove = [];
4188
+ let workingText = currentText;
4189
+ compositeSuggestion.suggestionSequence.forEach(segment => {
4190
+ /**sub segment has been completed, remove it from the sequence */
4191
+ if (workingText.startsWith(segment.text)) {
4192
+ workingText = workingText.slice(segment.text.length);
4193
+ elementsToRemove.push(segment);
4194
+ }
4195
+ });
4196
+ const filteredSegments = compositeSuggestion.suggestionSequence.filter(segment => !elementsToRemove.includes(segment));
4197
+ return filteredSegments;
4198
+ }
4199
+ _getCompositeSuggestionsForPattern(pattern) {
4200
+ const suggestionsToReturn = [];
4201
+ const leafPatterns = pattern.getPatterns();
4202
+ // for when pattern has no leafPatterns and only returns itself
4203
+ if (leafPatterns.length === 1 && leafPatterns[0].id === pattern.id) {
4204
+ const currentCustomTokens = this._getCustomTokens(pattern);
4205
+ const currentTokens = pattern.getTokens();
4206
+ const allTokens = [...currentCustomTokens, ...currentTokens];
4207
+ const leafCompositeSuggestions = allTokens.map(token => {
4208
+ const segment = {
4209
+ text: token,
4210
+ pattern: pattern,
4211
+ };
4212
+ const compositeSuggestion = {
4213
+ text: token,
4214
+ suggestionSequence: [segment],
4215
+ };
4216
+ return compositeSuggestion;
4217
+ });
4218
+ suggestionsToReturn.push(...leafCompositeSuggestions);
4219
+ }
4220
+ else {
4221
+ const currentCustomTokens = this._getCustomTokens(pattern);
4222
+ const patternsSuggestionList = currentCustomTokens.map(token => {
4223
+ const segment = {
4224
+ text: token,
4225
+ pattern: pattern,
4226
+ };
4227
+ const patternSuggestion = {
4228
+ text: token,
4229
+ suggestionSequence: [segment],
4230
+ };
4231
+ return patternSuggestion;
4232
+ });
4233
+ const leafCompositeSuggestions = leafPatterns.map(lp => this._getCompositeSuggestionsForPattern(lp)).flat();
4234
+ suggestionsToReturn.push(...patternsSuggestionList, ...leafCompositeSuggestions);
4235
+ }
4164
4236
  if (this._options.greedyPatternNames != null && this._options.greedyPatternNames.includes(pattern.name)) {
4165
4237
  const nextPatterns = pattern.getNextPatterns();
4166
- const nextPatternTokens = nextPatterns.reduce((acc, pattern) => {
4167
- acc.push(...this._getTokensForPattern(pattern));
4238
+ const nextPatternedTokensList = nextPatterns.reduce((acc, pattern) => {
4239
+ const patternedTokensList = this._getCompositeSuggestionsForPattern(pattern);
4240
+ acc.push(...patternedTokensList);
4168
4241
  return acc;
4169
4242
  }, []);
4170
- // using set to prevent duplicates
4171
- const tokens = new Set();
4172
- for (const token of augmentedTokens) {
4173
- for (const nextPatternToken of nextPatternTokens) {
4174
- tokens.add(token + nextPatternToken);
4243
+ const compositeSuggestionList = [];
4244
+ for (const currentSuggestion of suggestionsToReturn) {
4245
+ for (const nextSuggestionWithSubElements of nextPatternedTokensList) {
4246
+ const augmentedTokenWithPattern = {
4247
+ text: currentSuggestion.text + nextSuggestionWithSubElements.text,
4248
+ suggestionSequence: [...currentSuggestion.suggestionSequence, ...nextSuggestionWithSubElements.suggestionSequence],
4249
+ };
4250
+ compositeSuggestionList.push(augmentedTokenWithPattern);
4175
4251
  }
4176
4252
  }
4177
- return [...tokens];
4253
+ return compositeSuggestionList;
4178
4254
  }
4179
4255
  else {
4180
- return augmentedTokens;
4256
+ const dedupedSuggestions = this._deDupeCompositeSuggestions(suggestionsToReturn);
4257
+ return dedupedSuggestions;
4181
4258
  }
4182
4259
  }
4183
- _getAugmentedTokens(pattern) {
4184
- var _a, _b;
4260
+ _getCustomTokens(pattern) {
4261
+ var _a;
4185
4262
  const customTokensMap = this._options.customTokens || {};
4186
- const leafPatterns = pattern.getPatterns();
4187
- /** Using Set to
4188
- * - prevent duplicates
4189
- * - prevent mutation of original customTokensMap
4190
- */
4191
- const customTokensForPattern = new Set((_a = customTokensMap[pattern.name]) !== null && _a !== void 0 ? _a : []);
4192
- for (const lp of leafPatterns) {
4193
- const augmentedTokens = (_b = customTokensMap[lp.name]) !== null && _b !== void 0 ? _b : [];
4194
- const lpsCombinedTokens = [...lp.getTokens(), ...augmentedTokens];
4195
- for (const token of lpsCombinedTokens) {
4196
- customTokensForPattern.add(token);
4263
+ const customTokens = (_a = customTokensMap[pattern.name]) !== null && _a !== void 0 ? _a : [];
4264
+ const allTokens = [...customTokens];
4265
+ return allTokens;
4266
+ }
4267
+ _deDupeCompositeSuggestions(suggestions) {
4268
+ if (this._options.disableDedupe) {
4269
+ return suggestions;
4270
+ }
4271
+ const seen = new Set();
4272
+ const unique = [];
4273
+ for (const suggestion of suggestions) {
4274
+ // Create a unique key based on text and subElements
4275
+ const subElementsKey = suggestion.suggestionSequence
4276
+ .map(sub => ` ${sub.pattern.name} - ${sub.text} `)
4277
+ .sort()
4278
+ .join('|');
4279
+ const key = `${suggestion.text}|${subElementsKey}`;
4280
+ if (!seen.has(key)) {
4281
+ seen.add(key);
4282
+ unique.push(suggestion);
4197
4283
  }
4198
4284
  }
4199
- return [...customTokensForPattern];
4285
+ return unique;
4200
4286
  }
4201
- _createSuggestions(lastIndex, tokens) {
4287
+ _createSuggestions(lastIndex, compositeSuggestionList) {
4202
4288
  let textToIndex = lastIndex === -1 ? "" : this._cursor.getChars(0, lastIndex);
4203
- const suggestionStrings = [];
4204
4289
  const options = [];
4205
- for (const token of tokens) {
4290
+ for (const compositeSuggestion of compositeSuggestionList) {
4206
4291
  // concatenated for start index identification inside createSuggestion
4207
- const suggestion = textToIndex + token;
4208
- const alreadyExist = suggestionStrings.includes(suggestion);
4209
- const isSameAsText = suggestion === this._text;
4210
- if (!alreadyExist && !isSameAsText) {
4211
- suggestionStrings.push(suggestion);
4212
- const suggestionOption = this._createSuggestion(this._cursor.text, suggestion);
4213
- options.push(suggestionOption);
4214
- }
4292
+ const existingTextWithSuggestion = textToIndex + compositeSuggestion.text;
4293
+ existingTextWithSuggestion === this._text;
4294
+ const suggestionOption = this._createSuggestionOption(this._cursor.text, existingTextWithSuggestion, compositeSuggestion.suggestionSequence);
4295
+ options.push(suggestionOption);
4296
+ // }
4215
4297
  }
4216
4298
  const reducedOptions = getFurthestOptions(options);
4217
4299
  reducedOptions.sort((a, b) => a.text.localeCompare(b.text));
4218
4300
  return reducedOptions;
4219
4301
  }
4220
- _createSuggestion(fullText, suggestion) {
4302
+ _createSuggestionOption(fullText, suggestion, segments) {
4221
4303
  const furthestMatch = findMatchIndex(suggestion, fullText);
4222
4304
  const text = suggestion.slice(furthestMatch);
4223
4305
  const option = {
4224
4306
  text: text,
4225
4307
  startIndex: furthestMatch,
4308
+ suggestionSequence: segments,
4226
4309
  };
4227
4310
  return option;
4228
4311
  }