de-compromise 0.1.0 → 0.1.1

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.
@@ -429,7 +429,7 @@
429
429
  }
430
430
  Object.assign(View.prototype, methods$m);
431
431
 
432
- var version$1 = '14.16.0';
432
+ var version$1 = '14.17.0';
433
433
 
434
434
  const isObject$6 = function (item) {
435
435
  return item && typeof item === 'object' && !Array.isArray(item)
@@ -3549,9 +3549,9 @@
3549
3549
  if (reg.tag !== undefined && cache.has('#' + reg.tag) === false) {
3550
3550
  return true
3551
3551
  }
3552
- // perform a speedup for fast-or
3553
- if (reg.fastOr && anyIntersection(reg.fastOr, cache) === false) {
3554
- return false
3552
+ // are all of the fast-or words missing?
3553
+ if (reg.fastOr !== undefined && anyIntersection(reg.fastOr, cache) === false) {
3554
+ return true
3555
3555
  }
3556
3556
  }
3557
3557
  return false
@@ -3711,10 +3711,6 @@
3711
3711
  if (term.machine !== null && term.machine === reg.word) {
3712
3712
  return true
3713
3713
  }
3714
- // term aliases for slashes and things
3715
- if (term.alias !== undefined && term.alias.hasOwnProperty(reg.word)) {
3716
- return true
3717
- }
3718
3714
  // support ~ fuzzy match
3719
3715
  if (reg.fuzzy === true) {
3720
3716
  if (reg.word === term.root) {
@@ -3812,11 +3808,14 @@
3812
3808
  const reg = Object.assign({}, state.regs[state.r], { start: false, end: false });
3813
3809
  const start = state.t;
3814
3810
  for (; state.t < state.terms.length; state.t += 1) {
3815
- //stop for next-reg match
3811
+ // the number of terms we've matched, if we stop here
3812
+ const count = state.t - start + 1;
3813
+ //stop for next-reg match - unless we're still under our min
3816
3814
  if (endReg && wrapMatch(state.terms[state.t], endReg, state.start_i + state.t, state.phrase_length)) {
3817
- return state.t
3815
+ if (reg.min === undefined || count >= reg.min) {
3816
+ return state.t
3817
+ }
3818
3818
  }
3819
- const count = state.t - start + 1;
3820
3819
  // is it max-length now?
3821
3820
  if (reg.max !== undefined && count === reg.max) {
3822
3821
  return state.t
@@ -3830,6 +3829,10 @@
3830
3829
  return state.t
3831
3830
  }
3832
3831
  }
3832
+ // we ran out of terms - did we reach our min?
3833
+ if (reg.min !== undefined && state.t - start + 1 < reg.min) {
3834
+ return null
3835
+ }
3833
3836
  return state.t
3834
3837
  };
3835
3838
 
@@ -3898,7 +3901,9 @@
3898
3901
  // set the group result
3899
3902
  if (state.hasGroup === true) {
3900
3903
  const g = getGroup$1(state, state.t);
3901
- g.length = skipto - state.t;
3904
+ // accumulate onto any tokens already captured before the wildcard,
3905
+ // so '[one .* after]' keeps its leading (and trailing) tokens
3906
+ g.length += skipto - state.t;
3902
3907
  }
3903
3908
  state.t = skipto;
3904
3909
  // log(`✓ |greedy|`)
@@ -3909,48 +3914,64 @@
3909
3914
  return Object.prototype.toString.call(arr) === '[object Array]'
3910
3915
  };
3911
3916
 
3912
- const doOrBlock = function (state, skipN = 0) {
3917
+ // try to match a list of tokens, starting at state.t + skipN
3918
+ // returns the number of terms it consumed, or 0 for no-match
3919
+ const tryChoice = function (state, regs, skipN) {
3920
+ let len = 0;
3921
+ for (let w = 0; w < regs.length; w += 1) {
3922
+ const cr = regs[w];
3923
+ const t = state.t + skipN + len;
3924
+ if (state.terms[t] === undefined) {
3925
+ return 0
3926
+ }
3927
+ if (wrapMatch(state.terms[t], cr, state.start_i + t, state.phrase_length) !== true) {
3928
+ return 0
3929
+ }
3930
+ len += 1;
3931
+ // this can be greedy - '(foo+ bar)'
3932
+ if (cr.greedy === true) {
3933
+ // like getGreedy, anchors should not apply to the repeated terms
3934
+ const gr = Object.assign({}, cr, { start: false, end: false });
3935
+ for (let i = t + 1; i < state.terms.length; i += 1) {
3936
+ if (wrapMatch(state.terms[i], gr, state.start_i + i, state.phrase_length) !== true) {
3937
+ break
3938
+ }
3939
+ len += 1;
3940
+ }
3941
+ }
3942
+ }
3943
+ return len
3944
+ };
3945
+
3946
+ // match the first choice that works - '(a b|c)'
3947
+ const tryChoices = function (state, skipN) {
3913
3948
  const block = state.regs[state.r];
3914
- let wasFound = false;
3915
- // do each multiword sequence
3916
3949
  for (let c = 0; c < block.choices.length; c += 1) {
3917
- // try to match this list of tokens
3918
3950
  const regs = block.choices[c];
3919
3951
  if (!isArray$4(regs)) {
3920
- return false
3952
+ return 0
3921
3953
  }
3922
- wasFound = regs.every((cr, w_index) => {
3923
- let extra = 0;
3924
- const t = state.t + w_index + skipN + extra;
3925
- if (state.terms[t] === undefined) {
3926
- return false
3927
- }
3928
- const foundBlock = wrapMatch(state.terms[t], cr, t + state.start_i, state.phrase_length);
3929
- // this can be greedy - '(foo+ bar)'
3930
- if (foundBlock === true && cr.greedy === true) {
3931
- for (let i = 1; i < state.terms.length; i += 1) {
3932
- const term = state.terms[t + i];
3933
- if (term) {
3934
- const keepGoing = wrapMatch(term, cr, state.start_i + i, state.phrase_length);
3935
- if (keepGoing === true) {
3936
- extra += 1;
3937
- } else {
3938
- break
3939
- }
3940
- }
3941
- }
3942
- }
3943
- skipN += extra;
3944
- return foundBlock
3945
- });
3946
- if (wasFound) {
3947
- skipN += regs.length;
3948
- break
3954
+ const len = tryChoice(state, regs, skipN);
3955
+ if (len > 0) {
3956
+ return len
3949
3957
  }
3950
3958
  }
3951
- // we found a match - is it greedy though?
3952
- if (wasFound && block.greedy === true) {
3953
- return doOrBlock(state, skipN) // try it again!
3959
+ return 0
3960
+ };
3961
+
3962
+ const doOrBlock = function (state) {
3963
+ const block = state.regs[state.r];
3964
+ let skipN = tryChoices(state, 0);
3965
+ if (skipN === 0) {
3966
+ return 0
3967
+ }
3968
+ // greedy or-block - keep matching choices - '(a b|c)+'
3969
+ if (block.greedy === true) {
3970
+ let more = tryChoices(state, skipN);
3971
+ while (more > 0) {
3972
+ skipN += more;
3973
+ more = tryChoices(state, skipN);
3974
+ }
3954
3975
  }
3955
3976
  return skipN
3956
3977
  };
@@ -3966,7 +3987,7 @@
3966
3987
  if (state.terms[tryTerm] === undefined) {
3967
3988
  return false
3968
3989
  }
3969
- return wrapMatch(state.terms[tryTerm], cr, tryTerm, state.phrase_length)
3990
+ return wrapMatch(state.terms[tryTerm], cr, state.start_i + tryTerm, state.phrase_length)
3970
3991
  });
3971
3992
  if (allWords === true && block.length > longest) {
3972
3993
  longest = block.length;
@@ -4005,7 +4026,16 @@
4005
4026
  state.t += skipNum;
4006
4027
  // log(`✓ |found-or|`)
4007
4028
  return true
4008
- } else if (!reg.optional) {
4029
+ }
4030
+ // we didn't find it - for a negative-block, that's good news
4031
+ if (reg.negative === true) {
4032
+ // a '!(a b)?' can pass-through without consuming anything
4033
+ if (!reg.optional) {
4034
+ state.t += 1;
4035
+ }
4036
+ return true
4037
+ }
4038
+ if (!reg.optional) {
4009
4039
  return null //die
4010
4040
  }
4011
4041
  return true
@@ -4028,15 +4058,24 @@
4028
4058
  }
4029
4059
  // ensure we're at the end
4030
4060
  if (reg.end === true) {
4031
- const end = state.phrase_length - 1;
4032
- if (state.t + state.start_i !== end) {
4061
+ const end = state.phrase_length;
4062
+ if (state.t + state.start_i + skipNum !== end) {
4033
4063
  return null
4034
4064
  }
4035
4065
  }
4036
4066
  state.t += skipNum;
4037
4067
  // log(`✓ |found-and|`)
4038
4068
  return true
4039
- } else if (!reg.optional) {
4069
+ }
4070
+ // we didn't find it - for a negative-block, that's good news
4071
+ if (reg.negative === true) {
4072
+ // a '!(a && b)?' can pass-through without consuming anything
4073
+ if (!reg.optional) {
4074
+ state.t += 1;
4075
+ }
4076
+ return true
4077
+ }
4078
+ if (!reg.optional) {
4040
4079
  return null //die
4041
4080
  }
4042
4081
  return true
@@ -4045,7 +4084,7 @@
4045
4084
  const negGreedy = function (state, reg, nextReg) {
4046
4085
  let skip = 0;
4047
4086
  for (let t = state.t; t < state.terms.length; t += 1) {
4048
- let found = wrapMatch(state.terms[t], reg, state.start_i + state.t, state.phrase_length);
4087
+ let found = wrapMatch(state.terms[t], reg, state.start_i + t, state.phrase_length);
4049
4088
  // we don't want a match, here
4050
4089
  if (found) {
4051
4090
  break//stop going
@@ -4053,7 +4092,7 @@
4053
4092
  // are we doing 'greedy-to'?
4054
4093
  // - "!foo+ after" should stop at 'after'
4055
4094
  if (nextReg) {
4056
- found = wrapMatch(state.terms[t], nextReg, state.start_i + state.t, state.phrase_length);
4095
+ found = wrapMatch(state.terms[t], nextReg, state.start_i + t, state.phrase_length);
4057
4096
  if (found) {
4058
4097
  break
4059
4098
  }
@@ -4130,7 +4169,7 @@
4130
4169
  // but does the next reg match the next term??
4131
4170
  // only skip if it doesn't
4132
4171
  const nextTerm = state.terms[state.t + 1];
4133
- if (!nextTerm || !wrapMatch(nextTerm, regs[state.r + 1], state.start_i + state.t, state.phrase_length)) {
4172
+ if (!nextTerm || !wrapMatch(nextTerm, regs[state.r + 1], state.start_i + state.t + 1, state.phrase_length)) {
4134
4173
  state.r += 1;
4135
4174
  }
4136
4175
  }
@@ -4140,14 +4179,11 @@
4140
4179
  const greedyMatch = function (state) {
4141
4180
  const { regs, phrase_length } = state;
4142
4181
  const reg = regs[state.r];
4182
+ // foo{2,4} min-lengths are enforced inside getGreedy
4143
4183
  state.t = getGreedy(state, regs[state.r + 1]);
4144
4184
  if (state.t === null) {
4145
4185
  return null //greedy was too short
4146
4186
  }
4147
- // foo{2,4} - has a greed-minimum
4148
- if (reg.min && reg.min > state.t) {
4149
- return null //greedy was too short
4150
- }
4151
4187
  // 'foo+$' - if also an end-anchor, ensure we really reached the end
4152
4188
  if (reg.end === true && state.start_i + state.t !== phrase_length) {
4153
4189
  return null //greedy didn't reach the end
@@ -5288,6 +5324,7 @@
5288
5324
  };
5289
5325
 
5290
5326
  const lastBrace = /\{(?=[^{]*$)/; // split on the last { only
5327
+ const comment = /\}[ \t]*#.*$/; // an optional '# comment' after the last {tags} block
5291
5328
 
5292
5329
  // parse the spec output
5293
5330
  const parseLine = function (line = '') {
@@ -5295,6 +5332,7 @@
5295
5332
  if (tags === undefined) {
5296
5333
  return { text, tags: [] } // no {tags} block on this line
5297
5334
  }
5335
+ tags = tags.replace(comment, '}'); // drop the comment - only ever one, always last
5298
5336
  tags = tags.split(',').map(tag => tag.trim());
5299
5337
  let lastTag = tags[tags.length - 1];
5300
5338
  tags[tags.length - 1] = lastTag.replace(/\}$/, '');
@@ -6633,22 +6671,43 @@
6633
6671
  };
6634
6672
 
6635
6673
  // split by periods, question marks, unicode ⁇, etc
6636
- const initSplit = /([.!?\u203D\u2E18\u203C\u2047-\u2049\u3002]+\s)/g;
6674
+ // also ।॥ (devanagari), ؟ (arabic), ۔ (urdu), ։ (armenian), ።፧ (ethiopic), ။ (burmese), ។ (khmer)
6675
+ const initSplit = /([.!?\u203D\u2E18\u203C\u2047-\u2049\u0964\u0965\u061F\u06D4\u0589\u1362\u1367\u104B\u17D4\u3002]+\s)/g;
6637
6676
  // merge these back into prev sentence
6638
- const splitsOnly = /^[.!?\u203D\u2E18\u203C\u2047-\u2049\u3002]+\s$/;
6677
+ const splitsOnly = /^[.!?\u203D\u2E18\u203C\u2047-\u2049\u0964\u0965\u061F\u06D4\u0589\u1362\u1367\u104B\u17D4\u3002]+\s$/;
6639
6678
  const newLine = /((?:\r?\n|\r)+)/; // Match different new-line formats
6640
6679
 
6680
+ // CJK full-stops 。!?。 are never used in numbers or abbreviations,
6681
+ // so they can end a sentence without any whitespace after them.
6682
+ // A full-stop followed by a closing bracket 」』)” only ends the sentence when the
6683
+ // bracket is followed by whitespace, another opening bracket, or the end of the text
6684
+ // - '「行きません。」と言った' stays together, '「はい。」「いいえ。」' splits
6685
+ const hasCjkStop = /[\u3002\uFF01\uFF1F\uFF61]/;
6686
+ const cjkStops = '\\u3002\\uFF01\\uFF1F\\uFF61'; // 。!?。
6687
+ const allStops = '.!?\\u203D\\u2E18\\u203C\\u2047-\\u2049' + '\u0964\u0965\u061F\u06D4\u0589\u1362\u1367\u104B\u17D4' + cjkStops;
6688
+ const openers = '\\u300C\\u300E\\uFF08\\u3010\\u3014\\u300A\\u3008\\u201C'; // 「『(【〔《〈“
6689
+ const closers = '\\u300D\\u300F\\uFF09\\u3011\\u3015\\u300B\\u3009\\u201D'; // 」』)】〕》〉”
6690
+ const initSplitCjk = new RegExp(
6691
+ `([${allStops}]+\\s|[${cjkStops}]+(?![${closers}${cjkStops}])|[${cjkStops}]+[${closers}]+(?=[\\s${openers}]|$))`,
6692
+ 'g'
6693
+ );
6694
+ const splitsOnlyCjk = new RegExp(`^(?:[${allStops}]+\\s|[${cjkStops}]+[${closers}]*)$`);
6695
+
6641
6696
  // Start with a regex:
6642
6697
  const basicSplit = function (text) {
6643
6698
  const all = [];
6699
+ // japanese/chinese text has no whitespace after its full-stops
6700
+ const isCjk = hasCjkStop.test(text);
6701
+ const splitReg = isCjk ? initSplitCjk : initSplit;
6702
+ const onlyReg = isCjk ? splitsOnlyCjk : splitsOnly;
6644
6703
  //first, split by newline
6645
6704
  const lines = text.split(newLine);
6646
6705
  for (let i = 0; i < lines.length; i++) {
6647
6706
  //split by period, question-mark, and exclamation-mark
6648
- const arr = lines[i].split(initSplit);
6707
+ const arr = lines[i].split(splitReg);
6649
6708
  for (let o = 0; o < arr.length; o++) {
6650
6709
  // merge 'foo' + '.'
6651
- if (arr[o + 1] && splitsOnly.test(arr[o + 1]) === true) {
6710
+ if (arr[o + 1] && onlyReg.test(arr[o + 1]) === true) {
6652
6711
  arr[o] += arr[o + 1];
6653
6712
  arr[o + 1] = '';
6654
6713
  }
@@ -6660,7 +6719,8 @@
6660
6719
  return all
6661
6720
  };
6662
6721
 
6663
- const hasLetter$2 = /[a-z0-9\u00C0-\u00FF\u00a9\u00ae\u2000-\u3300\ud000-\udfff]/i;
6722
+ // a letter, number, or symbol/emoji in any script - otherwise it's only punctuation
6723
+ const hasLetter$2 = /[\p{L}\p{N}\p{So}]/u;
6664
6724
  const hasSomething$1 = /\S/;
6665
6725
 
6666
6726
  const notEmpty = function (splits) {
@@ -6738,6 +6798,8 @@
6738
6798
  '\u301D': '\u301E', // 'PrimeDoubleQuotes'
6739
6799
  // '\u0060': '\u00B4', // 'PrimeSingleQuotes'
6740
6800
  '\u301F': '\u301E', // 'LowPrimeDoubleQuotesReversed'
6801
+ '\u300C': '\u300D', // 'CornerBrackets' 「」
6802
+ '\u300E': '\u300F', // 'WhiteCornerBrackets' 『』
6741
6803
  };
6742
6804
  const openQuote = RegExp('[' + Object.keys(pairs).join('') + ']', 'g');
6743
6805
  const closeQuote = RegExp('[' + Object.values(pairs).join('') + ']', 'g');
@@ -6762,6 +6824,12 @@
6762
6824
  // do we have an open-quote and not a closed one?
6763
6825
  const m = split.match(openQuote);
6764
6826
  if (m !== null && m.length === 1) {
6827
+ // is the quote already closed in this chunk? - '“Yes!” said Tom. “No!” said Ann.'
6828
+ const closed = split.match(closeQuote);
6829
+ if (closed !== null && closed[0] !== m[0]) {
6830
+ arr.push(split);
6831
+ continue
6832
+ }
6765
6833
 
6766
6834
  // look at the next sentence for a closing quote,
6767
6835
  if (closesQuote(splits[i + 1]) && splits[i + 1].length < MAX_QUOTE) {
@@ -6794,8 +6862,8 @@
6794
6862
 
6795
6863
  // support unicode variants?
6796
6864
  // https://stackoverflow.com/questions/13535172/list-of-all-unicodes-open-close-brackets
6797
- const hasOpen = /\(/g;
6798
- const hasClosed = /\)/g;
6865
+ const hasOpen = /[(\uFF08]/g;
6866
+ const hasClosed = /[)\uFF09]/g;
6799
6867
  const mergeParens = function (splits) {
6800
6868
  const arr = [];
6801
6869
  for (let i = 0; i < splits.length; i += 1) {
@@ -6833,8 +6901,6 @@
6833
6901
  if (!text || typeof text !== 'string' || hasSomething.test(text) === false) {
6834
6902
  return []
6835
6903
  }
6836
- // cleanup unicode-spaces
6837
- text = text.replace('\xa0', ' ');
6838
6904
  // First do a greedy-split..
6839
6905
  const splits = basicSplit(text);
6840
6906
  // Filter-out the crap ones
@@ -7649,6 +7715,10 @@
7649
7715
  unicode$1[s] = k;
7650
7716
  });
7651
7717
  });
7718
+ // fullwidth ascii - 'Hello 2024' to 'Hello 2024'
7719
+ for (let i = 0x21; i <= 0x7E; i += 1) {
7720
+ unicode$1[String.fromCharCode(i + 0xFEE0)] = String.fromCharCode(i);
7721
+ }
7652
7722
 
7653
7723
  // https://util.unicode.org/UnicodeJsps/list-unicodeset.jsp?a=%5Cp%7Bpunctuation%7D
7654
7724
 
@@ -11765,7 +11835,7 @@
11765
11835
  api,
11766
11836
  };
11767
11837
 
11768
- var version = '0.1.0';
11838
+ var version = '0.1.1';
11769
11839
 
11770
11840
  nlp.plugin(tokenizer);
11771
11841
  nlp.plugin(tagset);