@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.
- package/dist/exports/Exports.d.ts +3 -0
- package/dist/exports/Exports.d.ts.map +1 -0
- package/dist/exports/Exports.js +3 -0
- package/dist/exports/Exports.js.map +1 -0
- package/dist/{exports → segmentation}/TextSegmentation.d.ts +0 -1
- package/dist/segmentation/TextSegmentation.d.ts.map +1 -0
- package/dist/segmentation/TextSegmentation.js +575 -0
- package/dist/segmentation/TextSegmentation.js.map +1 -0
- package/dist/segmentation/WordSequence.d.ts.map +1 -0
- package/dist/segmentation/WordSequence.js.map +1 -0
- package/dist/tests/Test.js +2 -2
- package/dist/tests/Test.js.map +1 -1
- package/dist/utilities/Timer.d.ts +7 -3
- package/dist/utilities/Timer.d.ts.map +1 -1
- package/dist/utilities/Timer.js +41 -39
- package/dist/utilities/Timer.js.map +1 -1
- package/package.json +4 -4
- package/src/exports/Exports.ts +2 -0
- package/src/segmentation/TextSegmentation.ts +743 -0
- package/src/tests/Test.ts +2 -2
- package/src/utilities/Timer.ts +50 -48
- package/dist/Test.d.ts +0 -2
- package/dist/Test.d.ts.map +0 -1
- package/dist/Test.js +0 -120
- package/dist/Test.js.map +0 -1
- package/dist/exports/TextSegmentation.d.ts.map +0 -1
- package/dist/exports/TextSegmentation.js +0 -369
- package/dist/exports/TextSegmentation.js.map +0 -1
- package/dist/exports/WordSequence.d.ts.map +0 -1
- package/dist/exports/WordSequence.js.map +0 -1
- package/src/exports/TextSegmentation.ts +0 -527
- /package/dist/{exports → segmentation}/WordSequence.d.ts +0 -0
- /package/dist/{exports → segmentation}/WordSequence.js +0 -0
- /package/src/{exports → segmentation}/WordSequence.ts +0 -0
|
@@ -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 @@
|
|
|
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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"}
|