///|
fn word_ignored(property : Int32) -> Bool {
property is (WbExtend | WbFormat | WbZWJ)
}
///|
fn word_letter(property : Int32) -> Bool {
property is (WbALetter | WbHebrewLetter)
}
///|
fn word_newline(property : Int32) -> Bool {
property is (WbCR | WbLF | WbNewline)
}
///|
fn word_segment_ends(text : StringView) -> Array[Int32] {
let input = text_break_input(text, word_break_value)
let properties = input.properties
let length = properties.length()
// Nearest non-ignored characters on each side of a position (WB4).
// Preparing these once avoids rescanning arbitrarily long Extend runs.
let before = Array::make(length + 1, -1)
let after = Array::make(length + 1, length)
for i in 0....0 {
after[i] = if word_ignored(properties[i]) { after[i + 1] } else { i }
}
let ends = []
let mut regional_count = 0
for i in 1.. 0 {
ends.push(text.length())
}
ends
}
///|
/// Apply WB3-WB999 in order at an interior code point boundary.
fn word_boundary(
input : TextBreakInput,
before : Array[Int32],
after : Array[Int32],
index : Int32,
regional_count : Int32,
) -> Bool {
let properties = input.properties
let raw_left = properties[index - 1]
let right = properties[index]
// WB3, WB3a, WB3b: these precede the ignore rule.
if raw_left == WbCR && right == WbLF {
return false
}
if word_newline(raw_left) || word_newline(right) {
return true
}
// WB3c and WB3d use adjacent characters, not WB4's reduced context.
if raw_left == WbZWJ &&
(grapheme_break_value(input.points[index]) & ExtendedPictographicBit) != 0 {
return false
}
if raw_left == WbWSegSpace && right == WbWSegSpace {
return false
}
// WB4: leading ignored characters form their own segment.
if word_ignored(right) {
return false
}
let left_index = before[index]
if left_index < 0 {
return true
}
let left = properties[left_index]
let previous_index = before[left_index]
let previous = if previous_index < 0 { 0 } else { properties[previous_index] }
let following_index = after[index + 1]
let following = if following_index == properties.length() {
0
} else {
properties[following_index]
}
// WB5-WB7: letters and medial punctuation.
if word_letter(left) && word_letter(right) {
return false
}
if word_letter(left) &&
right is (WbMidLetter | WbMidNumLet | WbSingleQuote) &&
word_letter(following) {
return false
}
if word_letter(previous) &&
left is (WbMidLetter | WbMidNumLet | WbSingleQuote) &&
word_letter(right) {
return false
}
// WB7a-WB7c: Hebrew quotes.
if left == WbHebrewLetter && right == WbSingleQuote {
return false
}
if left == WbHebrewLetter &&
right == WbDoubleQuote &&
following == WbHebrewLetter {
return false
}
if previous == WbHebrewLetter &&
left == WbDoubleQuote &&
right == WbHebrewLetter {
return false
}
// WB8-WB12: numbers, adjoining letters, and numeric punctuation.
if left == WbNumeric && right == WbNumeric {
return false
}
if (word_letter(left) && right == WbNumeric) ||
(left == WbNumeric && word_letter(right)) {
return false
}
if previous == WbNumeric &&
left is (WbMidNum | WbMidNumLet | WbSingleQuote) &&
right == WbNumeric {
return false
}
if left == WbNumeric &&
right is (WbMidNum | WbMidNumLet | WbSingleQuote) &&
following == WbNumeric {
return false
}
// WB13-WB13b: Katakana and connector punctuation.
if left == WbKatakana && right == WbKatakana {
return false
}
if left
is (WbALetter | WbHebrewLetter | WbNumeric | WbKatakana | WbExtendNumLet) &&
right == WbExtendNumLet {
return false
}
if left == WbExtendNumLet &&
right is (WbALetter | WbHebrewLetter | WbNumeric | WbKatakana) {
return false
}
// WB15-WB16: pair regional indicators, ignoring WB4 characters.
if left == WbRegionalIndicator &&
right == WbRegionalIndicator &&
regional_count % 2 == 1 {
return false
}
true // WB999
}