clarity-pattern-parser 11.3.9 → 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
@@ -1257,12 +1257,10 @@
1257
1257
  }
1258
1258
  }
1259
1259
  }
1260
- if (this._trimDivider && this._hasDivider) {
1261
- const isDividerLastMatch = this.children.length > 1 && nodes.length > 1 && nodes[nodes.length - 1].name === this.children[1].name;
1262
- if (isDividerLastMatch) {
1263
- const node = nodes.pop();
1264
- cursor.moveTo(node.firstIndex);
1265
- }
1260
+ const endedOnDivider = this._hasDivider && nodes.length % modulo === 0;
1261
+ if (this._trimDivider && endedOnDivider) {
1262
+ const node = nodes.pop();
1263
+ cursor.moveTo(node.firstIndex);
1266
1264
  }
1267
1265
  if (matchCount < this._min) {
1268
1266
  const lastIndex = cursor.index;
@@ -1398,6 +1396,7 @@
1398
1396
  this._firstIndex = 0;
1399
1397
  this._nodes = [];
1400
1398
  this._trimDivider = options.trimDivider == null ? false : options.trimDivider;
1399
+ this._patterns = [];
1401
1400
  }
1402
1401
  _assignChildrenToParent(children) {
1403
1402
  for (const child of children) {
@@ -1413,6 +1412,7 @@
1413
1412
  parse(cursor) {
1414
1413
  this._firstIndex = cursor.index;
1415
1414
  this._nodes = [];
1415
+ this._patterns = [];
1416
1416
  const passed = this._tryToParse(cursor);
1417
1417
  if (passed) {
1418
1418
  cursor.resolveError();
@@ -1464,6 +1464,7 @@
1464
1464
  }
1465
1465
  if (repeatNode != null) {
1466
1466
  this._nodes.push(repeatNode);
1467
+ this._patterns.push(this._pattern);
1467
1468
  if (!cursor.hasNext()) {
1468
1469
  passed = true;
1469
1470
  break;
@@ -1488,6 +1489,7 @@
1488
1489
  }
1489
1490
  else {
1490
1491
  this._nodes.push(dividerNode);
1492
+ this._patterns.push(this._divider);
1491
1493
  if (!cursor.hasNext()) {
1492
1494
  passed = true;
1493
1495
  break;
@@ -1510,11 +1512,11 @@
1510
1512
  return passed;
1511
1513
  }
1512
1514
  _createNode(cursor) {
1513
- var _a;
1514
1515
  const hasDivider = this._divider != null;
1516
+ const lastPattern = this._patterns[this._patterns.length - 1];
1515
1517
  if (hasDivider &&
1516
1518
  this._trimDivider &&
1517
- this._nodes[this._nodes.length - 1].name === ((_a = this._divider) === null || _a === void 0 ? void 0 : _a.name)) {
1519
+ lastPattern === this._divider) {
1518
1520
  const dividerNode = this._nodes.pop();
1519
1521
  cursor.moveTo(dividerNode.firstIndex);
1520
1522
  }
@@ -2721,13 +2723,36 @@
2721
2723
  }
2722
2724
  _compileAtomNode() {
2723
2725
  let node = this._atomNode;
2724
- if (this._prefixNode != null && this._atomNode != null) {
2725
- node = this._prefixNode.findRoot();
2726
- 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;
2727
2746
  }
2728
- if (this._postfixNode != null && node != null) {
2729
- this._postfixPlaceholder.replaceWith(node);
2730
- 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
+ }
2731
2756
  }
2732
2757
  this._prefixNode = null;
2733
2758
  this._atomNode = null;
@@ -2835,7 +2860,7 @@
2835
2860
  }
2836
2861
  _organizePatterns(patterns) {
2837
2862
  const finalPatterns = [];
2838
- patterns.forEach((pattern) => {
2863
+ patterns.forEach((pattern, index) => {
2839
2864
  if (this._isAtom(pattern)) {
2840
2865
  const atom = pattern.clone();
2841
2866
  atom.parent = this;
@@ -2846,6 +2871,7 @@
2846
2871
  const name = this._extractName(pattern);
2847
2872
  const prefix = this._extractPrefix(pattern);
2848
2873
  prefix.parent = this;
2874
+ this._precedenceMap[name] = index;
2849
2875
  this._prefixPatterns.push(prefix);
2850
2876
  this._prefixNames.push(name);
2851
2877
  finalPatterns.push(prefix);
@@ -2854,6 +2880,7 @@
2854
2880
  const name = this._extractName(pattern);
2855
2881
  const postfix = this._extractPostfix(pattern);
2856
2882
  postfix.parent = this;
2883
+ this._precedenceMap[name] = index;
2857
2884
  this._postfixPatterns.push(postfix);
2858
2885
  this._postfixNames.push(name);
2859
2886
  finalPatterns.push(postfix);
@@ -2862,7 +2889,7 @@
2862
2889
  const name = this._extractName(pattern);
2863
2890
  const binary = this._extractBinary(pattern);
2864
2891
  binary.parent = this;
2865
- this._precedenceMap[name] = this._binaryPatterns.length;
2892
+ this._precedenceMap[name] = index;
2866
2893
  this._binaryPatterns.push(binary);
2867
2894
  this._binaryNames.push(name);
2868
2895
  if (pattern.type === "right-associated") {
@@ -3660,6 +3687,8 @@
3660
3687
  return this._isRecursivePattern(name, pattern);
3661
3688
  }
3662
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.
3663
3692
  if (pattern.type === "reference") {
3664
3693
  return true;
3665
3694
  }
@@ -4011,26 +4040,30 @@
4011
4040
  this._options = options;
4012
4041
  this._text = "";
4013
4042
  }
4043
+ suggestFor(text) {
4044
+ return this.suggestForWithCursor(new Cursor(text));
4045
+ }
4014
4046
  suggestForWithCursor(cursor) {
4015
4047
  cursor.moveTo(0);
4016
4048
  this._cursor = cursor;
4017
4049
  this._text = cursor.text;
4018
4050
  this._cursor.startRecording();
4019
4051
  if (cursor.length === 0) {
4020
- return {
4052
+ const suggestion = {
4021
4053
  isComplete: false,
4022
- options: this._createSuggestionsFromRoot(),
4054
+ options: this._createSuggestionOptionsFromMatch(),
4023
4055
  error: new ParseError(0, 0, this._pattern),
4024
4056
  errorAtIndex: 0,
4025
4057
  cursor,
4026
4058
  ast: null
4027
4059
  };
4060
+ return suggestion;
4028
4061
  }
4029
4062
  let errorAtIndex = null;
4030
4063
  let error = null;
4031
4064
  const ast = this._pattern.parse(this._cursor);
4032
4065
  const isComplete = (ast === null || ast === void 0 ? void 0 : ast.value) === this._text;
4033
- const options = this._getAllOptions();
4066
+ const options = this._getAllSuggestionsOptions();
4034
4067
  if (!isComplete && options.length > 0 && !this._cursor.hasError) {
4035
4068
  const startIndex = options.reduce((lowestIndex, o) => {
4036
4069
  return Math.min(lowestIndex, o.startIndex);
@@ -4077,14 +4110,11 @@
4077
4110
  }
4078
4111
  return 0;
4079
4112
  }
4080
- suggestFor(text) {
4081
- return this.suggestForWithCursor(new Cursor(text));
4082
- }
4083
- _getAllOptions() {
4084
- const errorMatches = this._getOptionsFromErrors();
4085
- 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();
4086
4116
  const finalResults = [];
4087
- [...leafMatches, ...errorMatches].forEach(m => {
4117
+ [...leafMatchOptions, ...errorMatchOptions].forEach(m => {
4088
4118
  const index = finalResults.findIndex(f => m.text === f.text);
4089
4119
  if (index === -1) {
4090
4120
  finalResults.push(m);
@@ -4092,135 +4122,190 @@
4092
4122
  });
4093
4123
  return finalResults;
4094
4124
  }
4095
- _getOptionsFromErrors() {
4125
+ _createSuggestionOptionsFromErrors() {
4096
4126
  // These errored because the length of the string.
4097
4127
  const errors = this._cursor.errors.filter(e => e.lastIndex === this._cursor.length);
4098
- const suggestions = errors.map(e => {
4099
- const tokens = this._getTokensForPattern(e.pattern);
4100
- const adjustedTokens = new Set();
4101
- const currentText = this._cursor.getChars(e.startIndex, e.lastIndex);
4102
- tokens.forEach((token) => {
4103
- if (token.startsWith(currentText) && token.length > currentText.length) {
4104
- const difference = token.length - currentText.length;
4105
- const suggestedText = token.slice(-difference);
4106
- adjustedTokens.add(suggestedText);
4107
- }
4108
- });
4109
- return Array.from(adjustedTokens).map(t => {
4110
- return {
4111
- text: t,
4112
- startIndex: e.lastIndex,
4113
- };
4114
- });
4115
- });
4116
- return suggestions.flat();
4117
- }
4118
- _createSuggestionsFromRoot() {
4119
- const suggestions = [];
4120
- const tokens = [...this._pattern.getTokens(), ...this._getTokensForPattern(this._pattern)];
4121
- for (const token of tokens) {
4122
- if (suggestions.findIndex(s => s.text === token) === -1) {
4123
- suggestions.push(this._createSuggestion("", token));
4124
- }
4125
- }
4126
- return suggestions;
4127
- }
4128
- _createSuggestionsFromMatch(match) {
4129
- if (match.pattern == null) {
4130
- return this._createSuggestions(-1, this._getTokensForPattern(this._pattern));
4131
- }
4132
- if (match.node != null) {
4133
- const textStartingMatch = this._text.slice(match.node.startIndex, match.node.endIndex);
4134
- const currentPatternsTokens = this._getTokensForPattern(match.pattern);
4135
- /**
4136
- * Compares tokens to current text and extracts remainder tokens
4137
- * - IE. **currentText:** *abc*, **baseToken:** *abcdef*, **trailingToken:** *def*
4138
- */
4139
- const trailingTokens = currentPatternsTokens.reduce((acc, token) => {
4140
- if (token.startsWith(textStartingMatch)) {
4141
- const sliced = token.slice(textStartingMatch.length);
4142
- if (sliced !== '') {
4143
- acc.push(sliced);
4144
- }
4145
- }
4146
- return acc;
4147
- }, []);
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);
4148
4149
  const leafPatterns = match.pattern.getNextPatterns();
4149
- const leafTokens = leafPatterns.reduce((acc, leafPattern) => {
4150
- acc.push(...this._getTokensForPattern(leafPattern));
4151
- return acc;
4152
- }, []);
4153
- const allTokens = [...trailingTokens, ...leafTokens];
4154
- 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);
4155
4154
  }
4156
4155
  else {
4157
4156
  return [];
4158
4157
  }
4159
4158
  }
4160
- _getTokensForPattern(pattern) {
4161
- 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
+ }
4162
4236
  if (this._options.greedyPatternNames != null && this._options.greedyPatternNames.includes(pattern.name)) {
4163
4237
  const nextPatterns = pattern.getNextPatterns();
4164
- const nextPatternTokens = nextPatterns.reduce((acc, pattern) => {
4165
- acc.push(...this._getTokensForPattern(pattern));
4238
+ const nextPatternedTokensList = nextPatterns.reduce((acc, pattern) => {
4239
+ const patternedTokensList = this._getCompositeSuggestionsForPattern(pattern);
4240
+ acc.push(...patternedTokensList);
4166
4241
  return acc;
4167
4242
  }, []);
4168
- // using set to prevent duplicates
4169
- const tokens = new Set();
4170
- for (const token of augmentedTokens) {
4171
- for (const nextPatternToken of nextPatternTokens) {
4172
- 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);
4173
4251
  }
4174
4252
  }
4175
- return [...tokens];
4253
+ return compositeSuggestionList;
4176
4254
  }
4177
4255
  else {
4178
- return augmentedTokens;
4256
+ const dedupedSuggestions = this._deDupeCompositeSuggestions(suggestionsToReturn);
4257
+ return dedupedSuggestions;
4179
4258
  }
4180
4259
  }
4181
- _getAugmentedTokens(pattern) {
4182
- var _a, _b;
4260
+ _getCustomTokens(pattern) {
4261
+ var _a;
4183
4262
  const customTokensMap = this._options.customTokens || {};
4184
- const leafPatterns = pattern.getPatterns();
4185
- /** Using Set to
4186
- * - prevent duplicates
4187
- * - prevent mutation of original customTokensMap
4188
- */
4189
- const customTokensForPattern = new Set((_a = customTokensMap[pattern.name]) !== null && _a !== void 0 ? _a : []);
4190
- for (const lp of leafPatterns) {
4191
- const augmentedTokens = (_b = customTokensMap[lp.name]) !== null && _b !== void 0 ? _b : [];
4192
- const lpsCombinedTokens = [...lp.getTokens(), ...augmentedTokens];
4193
- for (const token of lpsCombinedTokens) {
4194
- 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);
4195
4283
  }
4196
4284
  }
4197
- return [...customTokensForPattern];
4285
+ return unique;
4198
4286
  }
4199
- _createSuggestions(lastIndex, tokens) {
4287
+ _createSuggestions(lastIndex, compositeSuggestionList) {
4200
4288
  let textToIndex = lastIndex === -1 ? "" : this._cursor.getChars(0, lastIndex);
4201
- const suggestionStrings = [];
4202
4289
  const options = [];
4203
- for (const token of tokens) {
4290
+ for (const compositeSuggestion of compositeSuggestionList) {
4204
4291
  // concatenated for start index identification inside createSuggestion
4205
- const suggestion = textToIndex + token;
4206
- const alreadyExist = suggestionStrings.includes(suggestion);
4207
- const isSameAsText = suggestion === this._text;
4208
- if (!alreadyExist && !isSameAsText) {
4209
- suggestionStrings.push(suggestion);
4210
- const suggestionOption = this._createSuggestion(this._cursor.text, suggestion);
4211
- options.push(suggestionOption);
4212
- }
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
+ // }
4213
4297
  }
4214
4298
  const reducedOptions = getFurthestOptions(options);
4215
4299
  reducedOptions.sort((a, b) => a.text.localeCompare(b.text));
4216
4300
  return reducedOptions;
4217
4301
  }
4218
- _createSuggestion(fullText, suggestion) {
4302
+ _createSuggestionOption(fullText, suggestion, segments) {
4219
4303
  const furthestMatch = findMatchIndex(suggestion, fullText);
4220
4304
  const text = suggestion.slice(furthestMatch);
4221
4305
  const option = {
4222
4306
  text: text,
4223
4307
  startIndex: furthestMatch,
4308
+ suggestionSequence: segments,
4224
4309
  };
4225
4310
  return option;
4226
4311
  }