@echogarden/text-segmentation 0.7.0 → 0.8.0

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 (34) hide show
  1. package/dist/exports/Exports.d.ts +3 -0
  2. package/dist/exports/Exports.d.ts.map +1 -0
  3. package/dist/exports/Exports.js +3 -0
  4. package/dist/exports/Exports.js.map +1 -0
  5. package/dist/{exports → segmentation}/TextSegmentation.d.ts +0 -1
  6. package/dist/segmentation/TextSegmentation.d.ts.map +1 -0
  7. package/dist/segmentation/TextSegmentation.js +575 -0
  8. package/dist/segmentation/TextSegmentation.js.map +1 -0
  9. package/dist/segmentation/WordSequence.d.ts.map +1 -0
  10. package/dist/segmentation/WordSequence.js.map +1 -0
  11. package/dist/tests/Test.js +2 -2
  12. package/dist/tests/Test.js.map +1 -1
  13. package/dist/utilities/Timer.d.ts +7 -3
  14. package/dist/utilities/Timer.d.ts.map +1 -1
  15. package/dist/utilities/Timer.js +41 -39
  16. package/dist/utilities/Timer.js.map +1 -1
  17. package/package.json +4 -4
  18. package/src/exports/Exports.ts +2 -0
  19. package/src/segmentation/TextSegmentation.ts +743 -0
  20. package/src/tests/Test.ts +2 -2
  21. package/src/utilities/Timer.ts +50 -48
  22. package/dist/Test.d.ts +0 -2
  23. package/dist/Test.d.ts.map +0 -1
  24. package/dist/Test.js +0 -120
  25. package/dist/Test.js.map +0 -1
  26. package/dist/exports/TextSegmentation.d.ts.map +0 -1
  27. package/dist/exports/TextSegmentation.js +0 -369
  28. package/dist/exports/TextSegmentation.js.map +0 -1
  29. package/dist/exports/WordSequence.d.ts.map +0 -1
  30. package/dist/exports/WordSequence.js.map +0 -1
  31. package/src/exports/TextSegmentation.ts +0 -527
  32. /package/dist/{exports → segmentation}/WordSequence.d.ts +0 -0
  33. /package/dist/{exports → segmentation}/WordSequence.js +0 -0
  34. /package/src/{exports → segmentation}/WordSequence.ts +0 -0
@@ -0,0 +1,3 @@
1
+ export * from '../segmentation/TextSegmentation.js';
2
+ export { WordSequence, type WordEntry } from '../segmentation/WordSequence.js';
3
+ //# sourceMappingURL=Exports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Exports.d.ts","sourceRoot":"","sources":["../../src/exports/Exports.ts"],"names":[],"mappings":"AAAA,cAAc,qCAAqC,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,iCAAiC,CAAA"}
@@ -0,0 +1,3 @@
1
+ export * from '../segmentation/TextSegmentation.js';
2
+ export { WordSequence } from '../segmentation/WordSequence.js';
3
+ //# sourceMappingURL=Exports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Exports.js","sourceRoot":"","sources":["../../src/exports/Exports.ts"],"names":[],"mappings":"AAAA,cAAc,qCAAqC,CAAA;AACnD,OAAO,EAAE,YAAY,EAAkB,MAAM,iCAAiC,CAAA"}
@@ -1,5 +1,4 @@
1
1
  import { WordSequence } from './WordSequence.js';
2
- export { WordSequence, type WordEntry } from './WordSequence.js';
3
2
  export declare function segmentText(text: string, options?: SegmentationOptions): Promise<SegmentationResult>;
4
3
  export declare function segmentWordSequence(wordSequence: WordSequence, options?: WordSequenceSegmentationOptions): Promise<SegmentationResult>;
5
4
  export declare function splitToWords(text: string, options?: SegmentationOptions): Promise<WordSequence>;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TextSegmentation.d.ts","sourceRoot":"","sources":["../../src/segmentation/TextSegmentation.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAWhD,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,+BAM5E;AAKD,wBAAsB,mBAAmB,CAAC,YAAY,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,+BAA+B,+BA8P9G;AAQD,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,yBAuH7E;AAOD,wBAAgB,wCAAwC,CAAC,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM;;;EA2EtG;AAkLD,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,YAAY,CAAA;IACnB,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,qBAAqB,EAAE,KAAK,EAAE,CAAA;CAC9B;AAID,qBAAa,YAAY;IAExB,SAAS,EAAE,KAAK,CAAA;IAEhB,KAAK,EAAE,YAAY,CAAA;IAGnB,YAAY,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAGhD;IAGD,IAAI,IAAI,WAEP;IAID,IAAI,SAAS,IAAI,KAAK,CAKrB;CACD;AAID,qBAAa,QAAS,SAAQ,YAAY;IAEzC,OAAO,EAAE,MAAM,EAAE,CAAK;CACtB;AAID,qBAAa,MAAO,SAAQ,YAAY;CACvC;AAID,MAAM,WAAW,KAAK;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;CACX;AAGD,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC7B,6BAA6B,CAAC,EAAE,OAAO,CAAA;CACvC;AAGD,eAAO,MAAM,0BAA0B,EAAE,mBAIxC,CAAA;AAGD,MAAM,WAAW,+BAA+B;IAC/C,mCAAmC,EAAE,OAAO,CAAA;CAC5C;AAGD,eAAO,MAAM,sCAAsC,EAAE,+BAEpD,CAAA"}
@@ -0,0 +1,575 @@
1
+ import { buildWordOrNumberPattern as buildWordSplitterPattern, phraseSeparatorRegExp, sentenceSeparatorTrailingPunctuationCharacterRegExp, sentenceSeparatorCharacterRegExp, whitespacePatternRegExp, letterPatternGlobalRegExp } from '../patterns/Patterns.js';
2
+ import { cldrSuppressions, additionalSuppressions, leadingApostropheContractionSuppressions, nounSuppressions, tldSuppressions } from '../patterns/Suppressions.js';
3
+ import { eastAsianCharRangesRegExp } from '../patterns/EastAsianCharacterPatterns.js';
4
+ import { WordSequence } from './WordSequence.js';
5
+ import { getShortLanguageCode } from '../utilities/Utilities.js';
6
+ import { buildRegExp } from 'regexp-composer';
7
+ ////////////////////////////////////////////////////////////////////////////////////////////////
8
+ // Exported methods
9
+ ////////////////////////////////////////////////////////////////////////////////////////////////
10
+ // Splits a text string into words and then segments the resulting word sequence into sentences and phrases
11
+ // (this is the main high-level entry point; it is async because splitting may involve ICU postprocessing)
12
+ export async function segmentText(text, options) {
13
+ // Split the text into a sequence of individual words (this may await East Asian postprocessing)
14
+ const wordSequence = await splitToWords(text, options);
15
+ // Segment the word sequence into segments (paragraphs), sentences, and phrases, and return the result
16
+ return segmentWordSequence(wordSequence);
17
+ }
18
+ // Segments an already-tokenized word sequence into segments (paragraphs), sentences, and phrases
19
+ // This is the core segmentation algorithm, working purely on the word level
20
+ // (options only control how colons/semicolons are treated when splitting phrases)
21
+ export async function segmentWordSequence(wordSequence, options) {
22
+ // Fill in any option values the caller didn't provide with their defaults
23
+ options = { ...defaultWordSequenceSegmentationOptions, ...options };
24
+ // This will hold the start and end word offsets of each paragraph (called a "segment" here)
25
+ const segmentWordRanges = [];
26
+ // Step 1: find segment (paragraph) boundaries.
27
+ // A new segment begins right after a newline character that follows at least one non-whitespace word.
28
+ {
29
+ // The word offset at which the current segment starts
30
+ let segmentStartWordOffset = 0;
31
+ // Whether at least one non-whitespace word has been seen in the current segment
32
+ let nonWhitespaceWordSeen = false;
33
+ // Whether a newline has been seen in the current segment (i.e. we may be at a boundary)
34
+ let newlineSeenInCurrentSegment = false;
35
+ // Walk through every word of the sequence
36
+ for (let wordIndex = 0; wordIndex < wordSequence.length; wordIndex++) {
37
+ // Get the text of the word at the current index
38
+ const word = wordSequence.getWordAt(wordIndex);
39
+ // If we haven't seen a newline yet, the segment already has content, and this word is a newline, mark the newline as seen
40
+ if (!newlineSeenInCurrentSegment && nonWhitespaceWordSeen && word.endsWith('\n')) {
41
+ newlineSeenInCurrentSegment = true;
42
+ }
43
+ // If a newline was seen, decide whether the segment actually ends at this word
44
+ if (newlineSeenInCurrentSegment) {
45
+ // Look at the word after the current one (or an empty string if the current word is last)
46
+ const nextWord = wordIndex < wordSequence.length - 1 ? wordSequence.getWordAt(wordIndex + 1) : '';
47
+ // End the segment here if the newline is followed by real content (or by nothing at all)
48
+ if (nextWord === '' || !whitespacePatternRegExp.test(nextWord)) {
49
+ // Record the current segment, ending after the current (newline) word
50
+ segmentWordRanges.push({ start: segmentStartWordOffset, end: wordIndex + 1 });
51
+ // The next segment starts at the word after the newline
52
+ segmentStartWordOffset = wordIndex + 1;
53
+ // Reset the newline flag for the new segment
54
+ newlineSeenInCurrentSegment = false;
55
+ }
56
+ }
57
+ // If no non-whitespace word has been seen yet, check whether the current word is non-whitespace
58
+ if (nonWhitespaceWordSeen === false) {
59
+ nonWhitespaceWordSeen = !whitespacePatternRegExp.test(word);
60
+ }
61
+ }
62
+ // If the last segment didn't reach the end of the sequence, close it with the remaining words
63
+ if (segmentStartWordOffset < wordSequence.length) {
64
+ segmentWordRanges.push({ start: segmentStartWordOffset, end: wordSequence.length });
65
+ }
66
+ }
67
+ // These will hold the word offsets of each sentence, and (per segment) the range of sentence indices in it
68
+ const sentenceWordRanges = [];
69
+ const segmentSentenceRanges = [];
70
+ // Step 2: find sentence boundaries inside each segment.
71
+ // A sentence ends at a sentence-separator character (like '.', '!', '?') once it contains a minimum number of letters.
72
+ {
73
+ // A sentence must contain at least this many letters before a separator may end it
74
+ const minimumSentenceLetterCount = 2;
75
+ // Process each segment independently
76
+ for (let segmentIndex = 0; segmentIndex < segmentWordRanges.length; segmentIndex++) {
77
+ // Get the word range of the current segment
78
+ const segmentWordRange = segmentWordRanges[segmentIndex];
79
+ // Record the segment's first and last word index
80
+ const segmentStartWordIndex = segmentWordRange.start;
81
+ const segmentEndWordIndex = segmentWordRange.end;
82
+ // Add a placeholder entry for this segment's sentence range, starting (and initially ending) at the current sentence count
83
+ segmentSentenceRanges.push({ start: sentenceWordRanges.length, end: sentenceWordRanges.length });
84
+ // Whether a non-whitespace word has been seen in the current sentence
85
+ let nonWhitespaceWordSeen = false;
86
+ // The word offset at which the current sentence starts
87
+ let sentenceStartWordOffset = segmentStartWordIndex;
88
+ // The number of letters accumulated in the current sentence
89
+ let currentSentenceLetterCount = 0;
90
+ // Walk through the words of this segment
91
+ for (let wordIndex = segmentStartWordIndex; wordIndex < segmentEndWordIndex; wordIndex++) {
92
+ // Get the text of the current word
93
+ const word = wordSequence.getWordAt(wordIndex);
94
+ // If no non-whitespace word has been seen yet, check whether the current word is non-whitespace
95
+ if (nonWhitespaceWordSeen === false) {
96
+ nonWhitespaceWordSeen = !whitespacePatternRegExp.test(word);
97
+ }
98
+ // Count letters, but only while the sentence hasn't yet reached the minimum (to save work)
99
+ if (currentSentenceLetterCount < minimumSentenceLetterCount) {
100
+ // Find every letter character in the current word
101
+ const matches = word.matchAll(letterPatternGlobalRegExp);
102
+ // Count the letters, stopping early once the minimum is reached
103
+ for (const _ of matches) {
104
+ currentSentenceLetterCount += 1;
105
+ if (currentSentenceLetterCount >= minimumSentenceLetterCount) {
106
+ break;
107
+ }
108
+ }
109
+ }
110
+ // End the sentence here if it has content, enough letters, and the current word is a sentence-separator character
111
+ if (nonWhitespaceWordSeen && currentSentenceLetterCount >= minimumSentenceLetterCount && sentenceSeparatorCharacterRegExp.test(word)) {
112
+ // Start scanning at the separator; this index marks where the sentence's trailing punctuation ends
113
+ let trailingSequenceEndIndex = wordIndex;
114
+ // Consume any trailing punctuation words (closing quotes, brackets, etc.) that belong to this sentence
115
+ while (trailingSequenceEndIndex < segmentEndWordIndex) {
116
+ // Get the text of the candidate trailing-punctuation word
117
+ const trailingWord = wordSequence.getWordAt(trailingSequenceEndIndex);
118
+ // Keep consuming it if it is trailing punctuation; otherwise stop
119
+ if (sentenceSeparatorTrailingPunctuationCharacterRegExp.test(trailingWord)) {
120
+ trailingSequenceEndIndex++;
121
+ }
122
+ else {
123
+ break;
124
+ }
125
+ }
126
+ // Record the completed sentence, from its start offset up to (but not including) the trailing punctuation
127
+ sentenceWordRanges.push({
128
+ start: sentenceStartWordOffset,
129
+ end: trailingSequenceEndIndex
130
+ });
131
+ // Extend the current segment's sentence range to include the sentence we just recorded
132
+ segmentSentenceRanges[segmentSentenceRanges.length - 1].end += 1;
133
+ // The next sentence starts right after the trailing punctuation
134
+ sentenceStartWordOffset = trailingSequenceEndIndex;
135
+ // Reset the letter count for the next sentence
136
+ currentSentenceLetterCount = 0;
137
+ // Skip the words already consumed by the trailing-punctuation scan (the for loop will increment again)
138
+ wordIndex = trailingSequenceEndIndex - 1;
139
+ }
140
+ }
141
+ // If the segment ends with words that don't complete a new sentence, close the final sentence at the segment end
142
+ if (sentenceStartWordOffset < segmentEndWordIndex) {
143
+ sentenceWordRanges.push({ start: sentenceStartWordOffset, end: segmentEndWordIndex });
144
+ segmentSentenceRanges[segmentSentenceRanges.length - 1].end += 1;
145
+ }
146
+ }
147
+ }
148
+ // This will hold the Sentence objects built from the sentence word ranges
149
+ const sentences = [];
150
+ // Build a Sentence object for every sentence range found above
151
+ for (const wordRange of sentenceWordRanges) {
152
+ // Extract the slice of the word sequence that belongs to this sentence
153
+ const sentenceWordSequence = wordSequence.slice(wordRange.start, wordRange.end);
154
+ // Create the Sentence for this range and append it to the list
155
+ sentences.push(new Sentence(wordRange, sentenceWordSequence));
156
+ }
157
+ // Step 3: split each sentence into phrases at phrase-separator characters (commas, colons, semicolons, etc.)
158
+ for (const sentence of sentences) {
159
+ // This will hold the start and end word offsets of each phrase in the current sentence
160
+ const phraseWordRanges = [];
161
+ // The word offset just past the end of this sentence
162
+ let sentenceEndWordOffset = sentence.wordRange.end;
163
+ // The word offset at which the current phrase starts
164
+ let phraseStartWordOffset = sentence.wordRange.start;
165
+ // Walk through the words of this sentence
166
+ for (let wordIndex = phraseStartWordOffset; wordIndex < sentenceEndWordOffset; wordIndex++) {
167
+ // Get the text of the current word
168
+ const currentWord = wordSequence.getWordAt(wordIndex);
169
+ // Determine whether this word is a phrase separator:
170
+ // it must match the phrase-separator pattern, and if the option is set, ':' or ';' only count when followed by whitespace
171
+ const isCurrentWordPhraseSeparator = phraseSeparatorRegExp.test(currentWord) &&
172
+ (!options.requireSpaceAfterColonsOrSemicolons ||
173
+ (currentWord !== ':' && currentWord !== ';') ||
174
+ whitespacePatternRegExp.test(wordSequence.getWordAt(wordIndex + 1)));
175
+ // If the current word is a phrase separator, close the current phrase at this word
176
+ if (isCurrentWordPhraseSeparator) {
177
+ // Tracks whether at least one whitespace word was seen while scanning trailing characters
178
+ let whitespaceSeenOnce = false;
179
+ // Include any trailing punctuation (closing quotes, brackets, etc.) in the current phrase
180
+ while (wordIndex < sentenceEndWordOffset - 1) {
181
+ // Get the text of the next word
182
+ const nextWord = wordSequence.getWordAt(wordIndex + 1);
183
+ // Stop consuming if the next word is not trailing punctuation
184
+ if (!sentenceSeparatorTrailingPunctuationCharacterRegExp.test(nextWord)) {
185
+ break;
186
+ }
187
+ // Remember if this trailing word is whitespace
188
+ if (whitespacePatternRegExp.test(nextWord)) {
189
+ whitespaceSeenOnce = true;
190
+ }
191
+ // A closing double quote that follows whitespace starts the next phrase, so stop before it
192
+ if (nextWord === '"' && whitespaceSeenOnce) {
193
+ break;
194
+ }
195
+ // Otherwise consume this trailing-punctuation word
196
+ wordIndex += 1;
197
+ }
198
+ // Record the phrase, from its start offset through the separator (and any trailing punctuation)
199
+ phraseWordRanges.push({
200
+ start: phraseStartWordOffset,
201
+ end: wordIndex + 1
202
+ });
203
+ // The next phrase starts right after the current separator
204
+ phraseStartWordOffset = wordIndex + 1;
205
+ }
206
+ }
207
+ // If the sentence ends with words that weren't part of any phrase, close the final phrase at the sentence end
208
+ if (phraseStartWordOffset < sentenceEndWordOffset) {
209
+ phraseWordRanges.push({ start: phraseStartWordOffset, end: sentenceEndWordOffset });
210
+ }
211
+ // Build a Phrase object for every phrase range and attach it to the sentence
212
+ for (const wordRange of phraseWordRanges) {
213
+ // Extract the slice of the word sequence that belongs to this phrase
214
+ const phraseWordSequence = wordSequence.slice(wordRange.start, wordRange.end);
215
+ // Create the Phrase for this range and add it to the sentence's phrase list
216
+ sentence.phrases.push(new Phrase(wordRange, phraseWordSequence));
217
+ }
218
+ }
219
+ // Assemble the final result: the full word sequence, the list of sentences, and the segment-to-sentence mapping
220
+ const result = {
221
+ words: wordSequence,
222
+ sentences,
223
+ segmentSentenceRanges,
224
+ };
225
+ // Return the assembled segmentation result
226
+ return result;
227
+ }
228
+ // Cache of compiled word-splitter regular expressions, keyed by the JSON-serialized options
229
+ // (compiling a regex is expensive, so the same options reuse the same compiled expression)
230
+ const cachedWordSplitterRegExps = new Map();
231
+ // Splits a string of text into a WordSequence of individual words and punctuation marks
232
+ // The returned sequence contains both the matched words and the punctuation found between/beside them
233
+ export async function splitToWords(text, options) {
234
+ // If no options were given, start from an empty options object
235
+ if (!options) {
236
+ options = {};
237
+ }
238
+ // Fill in any option values the caller didn't provide with their defaults
239
+ options = { ...defaultSegmentationOptions, ...options };
240
+ // If a language was specified, normalize it to its short base code (e.g. "en-US" becomes "en")
241
+ // so that language-specific data is looked up consistently
242
+ if (options.language) {
243
+ options.language = getShortLanguageCode(options.language);
244
+ }
245
+ // Serialize the (now normalized) options to JSON so they can be used as a cache key
246
+ const optionsAsJson = JSON.stringify(options);
247
+ // Look up a previously compiled splitter expression for these exact options
248
+ let wordSplitterRegExp = cachedWordSplitterRegExps.get(optionsAsJson);
249
+ // If no cached expression exists, compile a new one and store it in the cache
250
+ if (!wordSplitterRegExp) {
251
+ wordSplitterRegExp = buildWordSplitterRegExpForOptions(options);
252
+ cachedWordSplitterRegExps.set(optionsAsJson, wordSplitterRegExp);
253
+ }
254
+ // Start with an empty word sequence that will accumulate the results
255
+ let wordSequence = new WordSequence();
256
+ // Adds the words found in a run of text between two matched words (typically punctuation and whitespace)
257
+ // to the word sequence. Spaces are skipped; every other character becomes its own punctuation word,
258
+ // except that consecutive punctuation characters are grouped into a single word.
259
+ function addPunctuationWordsBetween(startOffset, endOffset) {
260
+ // Extract the substring that lies between the two given character offsets
261
+ const punctuationWordSubstring = text.substring(startOffset, endOffset);
262
+ // The current character offset within the source text
263
+ let charOffset = startOffset;
264
+ // The character offset at which the current punctuation word starts
265
+ let punctuationWordStartOffset = startOffset;
266
+ // If any characters have been accumulated since the last flush, add them as a single punctuation word
267
+ function addPunctuationWordIfNeeded() {
268
+ // Check whether there are pending characters to add
269
+ if (charOffset > punctuationWordStartOffset) {
270
+ // Extract the pending word text from the source
271
+ const wordText = text.substring(punctuationWordStartOffset, charOffset);
272
+ // Add it to the sequence, marking it as punctuation
273
+ wordSequence.addWord(wordText, punctuationWordStartOffset, true);
274
+ // The next pending word (if any) starts at the current character offset
275
+ punctuationWordStartOffset = charOffset;
276
+ }
277
+ }
278
+ // Iterate over every character (codepoint) in the gap between words
279
+ for (const char of punctuationWordSubstring) {
280
+ // Spaces are skipped: they just advance the character offset and are not added as words
281
+ if (char === ' ') {
282
+ charOffset += 1;
283
+ continue;
284
+ }
285
+ // Flush any pending punctuation word before the current character
286
+ addPunctuationWordIfNeeded();
287
+ // Advance the character offset past the current character (char.length handles astral codepoints)
288
+ charOffset += char.length;
289
+ // Flush again so this single character becomes its own punctuation word
290
+ addPunctuationWordIfNeeded();
291
+ }
292
+ // Flush any remaining pending punctuation word at the end of the gap
293
+ addPunctuationWordIfNeeded();
294
+ }
295
+ // Find all word matches (as defined by the splitter expression) throughout the text
296
+ const wordMatches = text.matchAll(wordSplitterRegExp);
297
+ // Track the character offset at which the last matched word ended
298
+ let lastMatchEndOffset = 0;
299
+ // Process each matched word in order of appearance
300
+ if (wordMatches) {
301
+ for (const match of wordMatches) {
302
+ // Read the match's start and end character offsets (available because the regex has the 'd' flag)
303
+ const offsets = match.indices[0];
304
+ const matchStartOffset = offsets[0];
305
+ const matchEndOffset = offsets[1];
306
+ // If there is a gap between the previous match and this one, add the gap's punctuation as words
307
+ if (matchStartOffset > lastMatchEndOffset) {
308
+ addPunctuationWordsBetween(lastMatchEndOffset, matchStartOffset);
309
+ }
310
+ // Extract the matched word's text from the source
311
+ const wordText = text.substring(matchStartOffset, matchEndOffset);
312
+ // Add the matched word to the sequence (it is a real word, not punctuation)
313
+ wordSequence.addWord(wordText, matchStartOffset, false);
314
+ // Remember where this match ended so gaps after it can be found
315
+ lastMatchEndOffset = matchEndOffset;
316
+ }
317
+ // If the text extends beyond the last match, add the trailing punctuation as words
318
+ addPunctuationWordsBetween(lastMatchEndOffset, text.length);
319
+ }
320
+ // If East Asian postprocessing is enabled, further split runs of East Asian characters into subwords using ICU word breaking
321
+ if (options.enableEastAsianPostprocessing) {
322
+ wordSequence = await postprocessEastAsianWords(text, wordSequence);
323
+ }
324
+ // Return the resulting word sequence
325
+ return wordSequence;
326
+ }
327
+ // Add any missing punctuation words to a word sequence
328
+ // Given a word sequence that was built from the source text (with its character offsets) and the source text itself,
329
+ // produce a new word sequence that also includes every punctuation character found between, before, and after the words.
330
+ // Also returns a mapping from each new entry's index back to the original word index it came from
331
+ // (punctuation-only entries are not present as keys in this mapping).
332
+ export function addMissingPunctuationWordsToWordSequence(wordSequence, sourceText) {
333
+ // Maps each index in the new (punctuation-included) sequence to the index of the original word it was copied from
334
+ const originalWordsReverseMapping = new Map();
335
+ // The new word sequence that will contain both the original words and the missing punctuation words
336
+ const wordSequenceWithPunctuation = new WordSequence();
337
+ // Adds entries for every character of a text slice, grouping runs of non-space characters into single punctuation words
338
+ // (spaces are merged into the preceding punctuation word, preserving them for offset correctness)
339
+ function addWordEntriesForTextSlice(textSlice, initialCharOffset) {
340
+ // The current character offset within the source text
341
+ let charOffset = initialCharOffset;
342
+ // Add entry for every codepoint (this will correctly treat characters beyond BMP)
343
+ for (const char of textSlice) {
344
+ // Compute the source-text offset just past this character
345
+ const charEndOffset = charOffset + char.length;
346
+ // Get the last entry added so far (if any)
347
+ const lastEntry = wordSequenceWithPunctuation.lastEntry;
348
+ // If this is a space and the last entry is a punctuation word that already starts with a space, extend that entry
349
+ if (char === ' ' && lastEntry && lastEntry.isPunctuation && lastEntry.text[0] === ' ') {
350
+ wordSequenceWithPunctuation.lastEntry.text += ' ';
351
+ wordSequenceWithPunctuation.lastEntry.endOffset = charEndOffset;
352
+ }
353
+ else {
354
+ // Otherwise, extract this single character from the source and add it as a new punctuation word
355
+ const wordText = sourceText.substring(charOffset, charEndOffset);
356
+ wordSequenceWithPunctuation.addWord(wordText, charOffset, true);
357
+ }
358
+ // Advance the character offset past the current character
359
+ charOffset = charEndOffset;
360
+ }
361
+ }
362
+ // Process each original word in order
363
+ for (let wordIndex = 0; wordIndex < wordSequence.length; wordIndex++) {
364
+ // Get the current word entry and the offset where it starts in the source text
365
+ const wordEntry = wordSequence.getEntryAt(wordIndex);
366
+ const wordStartOffset = wordEntry.startOffset;
367
+ // Get the end offset of the previous word (or 0 if this is the first word)
368
+ const previousWordEndOffset = wordIndex > 0 ? wordSequence.entries[wordIndex - 1].endOffset : 0;
369
+ // Add entries for any punctuation characters between the current and previous word (or the start of the text)
370
+ if (previousWordEndOffset !== wordStartOffset) {
371
+ // Extract the slice of source text lying between the two words
372
+ const textSlice = sourceText.substring(previousWordEndOffset, wordStartOffset);
373
+ // Add entries for the punctuation characters in that slice
374
+ addWordEntriesForTextSlice(textSlice, previousWordEndOffset);
375
+ }
376
+ // Copy the original word entry into the new sequence
377
+ wordSequenceWithPunctuation.entries.push(wordEntry);
378
+ // Record that this new entry corresponds to the current original word index
379
+ originalWordsReverseMapping.set(wordSequenceWithPunctuation.length - 1, wordIndex);
380
+ // If last word, add entries for any trailing punctuation characters
381
+ if (wordIndex === wordSequence.length - 1) {
382
+ // If the source text extends beyond the last word, the remainder is trailing punctuation
383
+ if (sourceText.length !== wordEntry.endOffset) {
384
+ // Extract the slice of source text after the last word
385
+ const textSlice = sourceText.substring(wordEntry.endOffset, sourceText.length);
386
+ // Add entries for the trailing punctuation characters
387
+ addWordEntriesForTextSlice(textSlice, wordEntry.endOffset);
388
+ }
389
+ }
390
+ }
391
+ // Return the enriched word sequence along with the reverse mapping to the original words
392
+ return { wordSequenceWithPunctuation, originalWordsReverseMapping };
393
+ }
394
+ ////////////////////////////////////////////////////////////////////////////////////////////////
395
+ // Helper methods
396
+ ////////////////////////////////////////////////////////////////////////////////////////////////
397
+ // Builds a global regular expression that matches words in the given language, using the configured options
398
+ function buildWordSplitterRegExpForOptions(options) {
399
+ // Get the CLDR suppression list (standard abbreviations like "Dr.") for this language, or an empty list if none exists
400
+ const cldrSuppressionsForLang = cldrSuppressions[options.language ?? ''] ?? [];
401
+ // Get the extended, project-specific suppression list for this language, or an empty list
402
+ const extendedSuppressionsForLang = additionalSuppressions[options.language ?? ''] ?? [];
403
+ // Get the leading-apostrophe contraction suppressions for this language, or an empty list
404
+ const contractionSuppressionsForLang = leadingApostropheContractionSuppressions[options.language ?? ''] ?? [];
405
+ // Build a variant of those contractions that uses the typographic apostrophe (') instead of the straight one (')
406
+ const contractionSuppressionsForLangWithSingleQuote = contractionSuppressionsForLang.map(str => str.replaceAll(`'`, `’`));
407
+ // Get any custom suppressions the caller provided, or an empty list
408
+ const customSuppressions = options.customSuppressions ?? [];
409
+ // Combine all suppression sources into one flat list
410
+ let suppressions = [
411
+ ...customSuppressions,
412
+ ...cldrSuppressionsForLang,
413
+ ...extendedSuppressionsForLang,
414
+ ...contractionSuppressionsForLang,
415
+ ...contractionSuppressionsForLangWithSingleQuote,
416
+ ...nounSuppressions,
417
+ ...tldSuppressions,
418
+ ];
419
+ // Build the word pattern: match any suppression (in original, lowercase, and uppercase forms) or a regular word
420
+ const wordPattern = buildWordSplitterPattern([
421
+ ...suppressions,
422
+ ...suppressions.map(word => word.toLocaleLowerCase()),
423
+ ...suppressions.map(word => word.toLocaleUpperCase()),
424
+ ]);
425
+ // Compile the pattern into a global regular expression (the 'd' flag, added by regexp-composer, gives match indices)
426
+ const wordSplitterRegExp = buildRegExp(wordPattern, { global: true });
427
+ // Return the compiled regular expression
428
+ return wordSplitterRegExp;
429
+ }
430
+ // Tries to load the ICU segmentation WebAssembly module; returns undefined if it is not installed
431
+ async function getIcuSegmentation() {
432
+ // Attempt to dynamically import the ICU segmentation module
433
+ try {
434
+ const icuSegmentation = await import('@echogarden/icu-segmentation-wasm');
435
+ // If the import succeeded, return the module
436
+ return icuSegmentation;
437
+ }
438
+ catch {
439
+ // If the import failed (module not available), signal that ICU segmentation is unavailable
440
+ return undefined;
441
+ }
442
+ }
443
+ // Further splits words that contain East Asian characters (Chinese, Japanese, Thai, Khmer) into smaller subwords
444
+ // using ICU's word-break rules, which are much better suited to these scripts than the generic splitter pattern
445
+ async function postprocessEastAsianWords(containingText, wordSequence) {
446
+ // Try to load the ICU segmentation module
447
+ const icuSegmentation = await getIcuSegmentation();
448
+ // If ICU segmentation is unavailable, return the original word sequence unchanged
449
+ if (icuSegmentation === undefined) {
450
+ return wordSequence;
451
+ }
452
+ // Tracks whether the ICU module has been initialized (initialization only needs to happen once)
453
+ let icuInitialized = false;
454
+ // A new word sequence that will hold the postprocessed words
455
+ const newWordSequence = new WordSequence();
456
+ // Process each word in the original sequence
457
+ for (let wordIndex = 0; wordIndex < wordSequence.length; wordIndex++) {
458
+ // Get the current word entry and the offset where it starts in the source text
459
+ const wordEntry = wordSequence.entries[wordIndex];
460
+ const wordStartOffset = wordEntry.startOffset;
461
+ // Get the text of the current word
462
+ const word = wordSequence.getWordAt(wordIndex);
463
+ // If the word contains East Asian characters, split it into subwords
464
+ if (eastAsianCharRangesRegExp.test(word)) {
465
+ // Initialize the ICU module on first use
466
+ if (!icuInitialized) {
467
+ await icuSegmentation.initialize();
468
+ icuInitialized = true;
469
+ }
470
+ // Get the word-break positions within the word (offsets into the word string)
471
+ const wordBreaks = [...icuSegmentation.createWordBreakIterator(word)];
472
+ // Create a subword for every pair of consecutive break positions
473
+ for (let i = 0; i < wordBreaks.length - 1; i++) {
474
+ // Compute the subword's start and end offsets within the source text
475
+ const subwordStartOffset = wordStartOffset + wordBreaks[i];
476
+ const subwordEndOffset = wordStartOffset + wordBreaks[i + 1];
477
+ // Extract the subword text from the containing text
478
+ const subwordText = containingText.substring(subwordStartOffset, subwordEndOffset);
479
+ // Add the subword to the new sequence (it is a real word, not punctuation)
480
+ newWordSequence.addWord(subwordText, subwordStartOffset, false);
481
+ }
482
+ }
483
+ else {
484
+ // Otherwise, extract the word text from the source and copy it to the new sequence unchanged
485
+ const wordText = containingText.substring(wordEntry.startOffset, wordEntry.endOffset);
486
+ newWordSequence.addWord(wordText, wordEntry.startOffset, wordEntry.isPunctuation);
487
+ }
488
+ }
489
+ // Return the postprocessed word sequence
490
+ return newWordSequence;
491
+ }
492
+ // Computes the character ranges of all punctuation in the text.
493
+ // This includes gaps between words, entries that are themselves punctuation, and any leading/trailing text
494
+ // that is not covered by a word entry.
495
+ function getPunctuationRanges(wordSequence, text) {
496
+ // This will hold the character ranges of the punctuation found
497
+ const punctuationRanges = [];
498
+ // Get the list of word entries
499
+ const wordEntries = wordSequence.entries;
500
+ // If the text starts before the first word, the leading part is punctuation
501
+ if (wordEntries[0].startOffset > 0) {
502
+ punctuationRanges.push({ start: 0, end: wordEntries[0].startOffset });
503
+ }
504
+ // Examine every word entry
505
+ for (let i = 0; i < wordEntries.length; i++) {
506
+ // Get the current entry
507
+ const entry = wordEntries[i];
508
+ // Get the end offset of the previous entry (or 0 for the first entry)
509
+ const previousEndOffset = wordEntries[i - 1]?.endOffset ?? 0;
510
+ // If there is a gap between the previous entry and this one, that gap is punctuation
511
+ if (entry.startOffset > previousEndOffset) {
512
+ punctuationRanges.push({ start: previousEndOffset, end: entry.startOffset });
513
+ }
514
+ // If the current entry is itself a punctuation word, its whole range is punctuation
515
+ if (entry.isPunctuation) {
516
+ punctuationRanges.push({ start: entry.startOffset, end: entry.endOffset });
517
+ }
518
+ }
519
+ {
520
+ // Get the end offset of the last entry (if any)
521
+ const lastEndOffset = wordEntries[wordEntries.length - 1]?.endOffset;
522
+ // If the text extends beyond the last entry, the trailing part is punctuation
523
+ if (lastEndOffset && lastEndOffset < text.length) {
524
+ punctuationRanges.push({ start: lastEndOffset, end: text.length });
525
+ }
526
+ }
527
+ // Return the collected punctuation ranges
528
+ return punctuationRanges;
529
+ }
530
+ // A fragment of text (either a sentence or a phrase): it holds the word range it occupies and its words
531
+ // (this is the shared base class of Sentence and Phrase)
532
+ export class TextFragment {
533
+ // The range of word indices this fragment covers (in the overall word sequence)
534
+ wordRange;
535
+ // The words that make up this fragment
536
+ words;
537
+ // Creates a fragment from a word range and the corresponding slice of words
538
+ constructor(wordRange, words) {
539
+ this.wordRange = wordRange;
540
+ this.words = words;
541
+ }
542
+ // Returns the fragment's text by concatenating all of its words
543
+ get text() {
544
+ return this.words.text;
545
+ }
546
+ // Returns the character range of this fragment within the source text,
547
+ // from the start of its first word to the end of its last word
548
+ get charRange() {
549
+ return {
550
+ start: this.words.firstEntry.startOffset,
551
+ end: this.words.lastEntry.endOffset
552
+ };
553
+ }
554
+ }
555
+ // A sentence: a text fragment that additionally contains the phrases it was split into
556
+ // (phrases are filled in during segmentation)
557
+ export class Sentence extends TextFragment {
558
+ // The phrases that make up this sentence
559
+ phrases = [];
560
+ }
561
+ // A phrase: a text fragment representing one part of a sentence
562
+ // (it has no additional members; it exists to give phrases their own type)
563
+ export class Phrase extends TextFragment {
564
+ }
565
+ // Default options for word splitting: no language, no custom suppressions, and East Asian postprocessing enabled
566
+ export const defaultSegmentationOptions = {
567
+ language: '',
568
+ customSuppressions: [],
569
+ enableEastAsianPostprocessing: true,
570
+ };
571
+ // Default options for word-sequence segmentation: ':' and ';' only count as phrase separators when followed by a space
572
+ export const defaultWordSequenceSegmentationOptions = {
573
+ requireSpaceAfterColonsOrSemicolons: true
574
+ };
575
+ //# sourceMappingURL=TextSegmentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TextSegmentation.js","sourceRoot":"","sources":["../../src/segmentation/TextSegmentation.ts"],"names":[],"mappings":"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"}