From 23d9a3e36c09dea06804fb990bdf8cc13eae6923 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Tue, 10 Dec 2024 18:04:54 +0100 Subject: [PATCH 01/25] ICU-22986 GL takes CM --- icu4c/source/data/brkitr/rules/line.txt | 2 +- icu4c/source/test/testdata/break_rules/line.txt | 2 +- icu4c/source/test/testdata/rbbitst.txt | 4 ++++ .../src/test/resources/com/ibm/icu/dev/test/rbbi/rbbitst.txt | 4 ++++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/icu4c/source/data/brkitr/rules/line.txt b/icu4c/source/data/brkitr/rules/line.txt index 9f3e44984eae..e43e70b36311 100644 --- a/icu4c/source/data/brkitr/rules/line.txt +++ b/icu4c/source/data/brkitr/rules/line.txt @@ -297,7 +297,7 @@ $LB20NonBreaks = [$LB18NonBreaks - $CB]; # and then to default UAX #14 behaviour (UTC-179-C32). # ^($HY | $HH) $CM* $ALPlus; -$GL ($HY | $HH) $CM* $ALPlus; +$GL $CM* ($HY | $HH) $CM* $ALPlus; # Non-breaking CB from LB8a: $CB $CM* $ZWJ ($HY | $HH) $CM* $ALPlus; # Non-breaking SP from LB14: diff --git a/icu4c/source/test/testdata/break_rules/line.txt b/icu4c/source/test/testdata/break_rules/line.txt index 9f85b7917139..e2154abf6309 100644 --- a/icu4c/source/test/testdata/break_rules/line.txt +++ b/icu4c/source/test/testdata/break_rules/line.txt @@ -176,7 +176,7 @@ LB11.2: SP WJ; LB11.3: WJ CM* [^CM]; # Needs to apply before LB12, because the new monkeys are not greedy. -LB20a.2: GL (HY | HH) CM* AL; +LB20a.2: GL CM* (HY | HH) CM* AL; LB12: GL CM* [^CM]; LB12a: [^SP BA HY] CM* GL; diff --git a/icu4c/source/test/testdata/rbbitst.txt b/icu4c/source/test/testdata/rbbitst.txt index 1c7fe9975699..781ce068be7b 100644 --- a/icu4c/source/test/testdata/rbbitst.txt +++ b/icu4c/source/test/testdata/rbbitst.txt @@ -2214,3 +2214,7 @@ Bangkok)• •« Complex »« chaining » • •« .618 »• # Interaction with the ICU tailoring to break before such numbers. +# A hyphen following non-breaking space that carries an intervening combining +# mark is treated as word-initial; by LB20a it has no break opportunity after +# it. A bug in ICU 76 incorrectly handled that case (ICU-22986). +• ̄-k• \ No newline at end of file diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/rbbitst.txt b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/rbbitst.txt index 1c7fe9975699..781ce068be7b 100644 --- a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/rbbitst.txt +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/rbbitst.txt @@ -2214,3 +2214,7 @@ Bangkok)• •« Complex »« chaining » • •« .618 »• # Interaction with the ICU tailoring to break before such numbers. +# A hyphen following non-breaking space that carries an intervening combining +# mark is treated as word-initial; by LB20a it has no break opportunity after +# it. A bug in ICU 76 incorrectly handled that case (ICU-22986). +• ̄-k• \ No newline at end of file From ea8748c942413ecf15d876f40bccba0e4e8719fb Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Tue, 10 Dec 2024 18:15:25 +0100 Subject: [PATCH 02/25] Regenerate line.brk (@markusicu to flip the bytes) --- .../ibm/icu/impl/data/icudata/brkitr/line.brk | Bin 73088 -> 73232 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/icu4j/main/core/src/main/resources/com/ibm/icu/impl/data/icudata/brkitr/line.brk b/icu4j/main/core/src/main/resources/com/ibm/icu/impl/data/icudata/brkitr/line.brk index 8d0172d055cb3cd09d4b1c3b090c4672c7f0b662..ab4a491c49bd389cf1b519aacf365dfda6aa43e4 100644 GIT binary patch literal 73232 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 WjNF@cMC|4fI&`YFE7ron?0*4Dd>*?1 From b644118712d9b454791b7020266bdcf2e4c336a7 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Tue, 10 Dec 2024 18:35:39 +0100 Subject: [PATCH 03/25] Fix the old monkeys from Java who are still hardcoded --- .../test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java index 56a4801bea29..2fd9bf9e7e62 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java @@ -1276,7 +1276,6 @@ int next(int startPos) { fLF.contains(fText.codePointAt(breakObliviousPrevPosX2)) || fNL.contains(fText.codePointAt(breakObliviousPrevPosX2)) || fSP.contains(fText.codePointAt(breakObliviousPrevPosX2)) || - fGL.contains(fText.codePointAt(breakObliviousPrevPosX2)) || fZW.contains(fText.codePointAt(breakObliviousPrevPosX2))) { setAppliedRule(pos, "LB 20a"); continue; @@ -1285,7 +1284,8 @@ int next(int startPos) { fCM.contains(fText.codePointAt(breakObliviousPrevPosX2))) { breakObliviousPrevPosX2 = moveIndex32(fText, breakObliviousPrevPosX2, -1); } - if (fCB.contains(fText.codePointAt(breakObliviousPrevPosX2))) { + if (fCB.contains(fText.codePointAt(breakObliviousPrevPosX2)) || + fGL.contains(fText.codePointAt(breakObliviousPrevPosX2))) { setAppliedRule(pos, "LB 20a"); continue; } From 46c29d1c57a0664c0910e1b1f85140b9c5e0fc0e Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Tue, 10 Dec 2024 23:35:36 +0100 Subject: [PATCH 04/25] meow --- .../ibm/icu/dev/test/rbbi/RBBITestMonkey.java | 237 +++++++++++++++++- icu4j/tools/build/pom.xml | 10 - 2 files changed, 236 insertions(+), 11 deletions(-) diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java index 56a4801bea29..271cba70238b 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java @@ -19,7 +19,7 @@ import org.junit.runners.JUnit4; // Monkey testing of RuleBasedBreakIterator. -// The old, original monkey test. TODO: remove +// The old monkey test, now using regexes generated by the Unicode tools. // The new monkey test is class RBBIMonkeyTest. import com.ibm.icu.dev.test.CoreTestFmwk; @@ -2136,6 +2136,241 @@ static int nextCP(StringBuffer s, int i) { } + + abstract class SegmentationRule { + enum Resolution { + BREAK, + NO_BREAK, + } + class BreakContext { + BreakContext(int index) { + indexInRemapped = index; + } + int indexInRemapped; + SegmentationRule appliedRule = null; + }; + + SegmentationRule(String name) { + name_ = name; + } + + void apply(String remapped, List resolved); + Resolution resolution(); + String name() { return name_; } + + private final String name_; + } + + class RemapRule extends SegmentationRule { + RemapRule(String name, String pattern, String replacement) { + super(name); + replacement_ = replacement; + pattern_ = + Pattern.compile(pattern, UREGEX_COMMENTS | UREGEX_DOTALL, parseError, status); + } + + @Override + void apply(StringBuilder remapped, List resolved) { + int i = 0; + int offset = 0; + // We find all matches of the `pattern_` and replace them according to + // the `replacement_`, producing the new remapped string `result`. + // For every position i in the original string, + // `resolved[i].indexInRemapped` is nullopt if i lies within a replaced + // match, and is set to the new index in `result` otherwise, by adding + // the accumulated difference `offset` between match lengths and + // replacement lengths. + // Consider a 4-codepoint, 6 code unit string s = ⟨ 𒀀, ◌́, ␠, ◌𝅲 ⟩, where + // ␠ stands for U+0020 and U+12000 𒀀 and U+1D172 ◌𝅲 each require two code + // units, and apply the following two rules: + // 1. (?\P{lb=SP}) \p{lb=CM}* → ${X} + // 2. \p{lb=CM} → A + // The string remapped and the indexInRemapped values change as follows: + // indexInRemapped remapped string rule final + // (aligned on the initial string) applied offset + // 𒀀 ◌́ ␠ ◌𝅲 + // 0 1 2 3 4 5 6 ⟨ 𒀀, ◌́, ␠, ◌𝅲 ⟩ (none) + // 0 - - 2 3 4 5 ⟨ 𒀀, ␠, ◌𝅲 ⟩ 1 -1 + // 0 - - 2 3 - 4 ⟨ 𒀀, ␠, A ⟩ 2 -1 + // + // Note that the last indexInRemapped is always equal to the length of + // the remapped string. + var matcher = new Matcher(pattern_.matcher(remapped, status)); + while (matcher.find()) { + for (;; ++i) { + if (resolved[i].indexInRemapped == null) { + continue; + } + if (resolved[i].indexInRemapped != null && + resolved[i].indexInRemapped > matcher.start(status)) { + break; + } + resolved[i].indexInRemapped += offset; + } + for (;; ++i) { + if (resolved[i].indexInRemapped == null) { + continue; + } + // Note that + // `*resolved[i].indexInRemapped > matcher.end(status)` should + // never happen with ordinary rules, but could in principle + // happen with rules that remap to code point sequences, e.g., + // 1. BC → TYZ + // 2. AT → X + // applied to ⟨ A, B, C ⟩: + // indexInRemapped remapped rule + // A B C + // 0 1 2 3 ⟨ A, B, C ⟩ (none) + // 0 1 - 4 ⟨ A, T, Y, Z ⟩ 1 + // 0 - - 3 ⟨ X, Y, Z ⟩ 2 + // Where for the application of rule 2, the match ends at + // position 2 in remapped, which does not correspond to a + // position in the original string. + if (resolved[i].indexInRemapped != null && + resolved[i].indexInRemapped >= matcher.end(status)) { + break; + } + if (resolved[i].appliedRule != null && + resolved[i].appliedRule.resolution() == BREAK) { + throw new IllegalArgumentException( + "Replacement rule at remapped indices " + + matcher.start(status) + + " sqq. spans a break"); + } + resolved[i].appliedRule = this; + resolved[i].indexInRemapped = null; + } + matcher.appendReplacement(result, replacement_, status); + offset = result.length() - resolved[i].indexInRemapped; + } + for (; i < resolved.size(); ++i) { + if (resolved[i].indexInRemapped == null) { + continue; + } + resolved[i].indexInRemapped += offset; + } + matcher.appendTail(result); + if (resolved.back().indexInRemapped != result.length()) { + StringBuilder indices; + for (final var r : resolved) { + indices += r.indexInRemapped.has_value() ? r.indexInRemapped.toString() : "null"; + indices += ","; + } + throw new IllegalArgumentException(("Inconsistent indexInRemapped " + indices + " for new remapped string " + + result.toUTF8String(s)) + .c_str()); + } + remapped = result; + if (U_FAILURE(status)) { + puts(("Failed to apply rule " + name()).c_str()); + } + } + + @Override + Resolution resolution() { return Resolution.NO_BREAK; } + + private final Pattern pattern_; + private final String replacement_; + }; + + class RegexRule extends SegmentationRule { + RegexRule(String name, String before, char resolution, + String after) { + super(name); + resolution_ = resolution; + before_ = + Pattern.compile(before, UREGEX_COMMENTS | UREGEX_DOTALL, parseError, status); + endsWithBefore_ = Pattern.compile( + ".*(" + before + ")", UREGEX_COMMENTS | UREGEX_DOTALL, parseError, status); + after_ = Pattern.compile(after, UREGEX_COMMENTS | UREGEX_DOTALL, parseError, status); + } + + @Override + void apply(UnicodeString remapped, List resolved) { + UErrorCode status = U_ZERO_ERROR; + // The unicodetools implementation simply tries, for each index, to + // match the string up to the index against /.*(before)/ (with + // `matches`) and the beginning of the string after the index against + // /after/ (with `lookingAt`), but that is very slow, especially for + // nonempty /before/. While the old monkeys are not a production + // implementation, we still do not want them to be too slow, since we + // need to test millions of sample strings. Instead we search for + // /before/ and /after/, and check resulting candidates. This speeds + // things up by a factor of ~40. + // We need to be careful about greedy matching: The first position where + // the rule matches may be before the end of the first /before/ match. + // However, it is both: + // 1. within a /before/ match or at its bounds, + // 2. at the beginning of an /after/ match. + // Further, the /before/ context of the rule matches within the + // aforementioned /before/ match. Note that we need to look for + // overlapping matches, thus calls to `find` are always preceded by a + // reset via `region`. + final Matcher beforeSearch = before_.matcher(remapped, status); + final Matcher afterSearch = after_.matcher(remapped, status); + beforeSearch.useAnchoringBounds(false); + afterSearch.useAnchoringBounds(false); + if (beforeSearch.find() && afterSearch.find()) { + for (;;) { + if (afterSearch.start(status) < beforeSearch.start(status)) { + afterSearch.region(beforeSearch.start(status), remapped.length(), status); + if (!afterSearch.find()) { + break; + } + } else if (afterSearch.start(status) > beforeSearch.end(status)) { + if (beforeSearch.start(status) == remapped.length()) { + break; + } + beforeSearch.region(remapped.moveIndex32(beforeSearch.start(status), 1), + remapped.length(), status); + if (!beforeSearch.find()) { + break; + } + } else {/* + auto const it = std::find_if(resolved.begin(), resolved.end(), [&](auto r) { + return r.indexInRemapped == afterSearch.start(status); + }); + if (it == resolved.end()) { + puts(("Rule " + name() + + " found a break at a position which does not correspond to an index in " + "the original string") + .c_str()); + std::terminate(); + }*/ + U_ASSERT(U_SUCCESS(status)); + if (it.appliedRule == nullptr && + endsWithBefore_.matcher(remapped, status) + .useAnchoringBounds(false) + .region(beforeSearch.start(status), afterSearch.start(status), status) + .matches(status)) { + it.appliedRule = this; + } + if (afterSearch.start(status) == remapped.length()) { + break; + } + afterSearch.region(remapped.moveIndex32(afterSearch.start(status), 1), + remapped.length(), status); + if (!afterSearch.find()) { + break; + } + } + U_ASSERT(U_SUCCESS(status)); + } + } + if (U_FAILURE(status)) { + puts(("Failed to apply rule " + name()).c_str()); + } + } + + @Override + Resolution resolution() { return resolution_; } + + private final Pattern before_; + private final Pattern endsWithBefore_; + private final Pattern after_; + private final Resolution resolution_; + }; + /** * random number generator. Not using Java's built-in Randoms for two reasons: * 1. Using this code allows obtaining the same sequences as those from the ICU4C monkey test. diff --git a/icu4j/tools/build/pom.xml b/icu4j/tools/build/pom.xml index 3e1ab5dbe7c3..fc844b1ffcd1 100644 --- a/icu4j/tools/build/pom.xml +++ b/icu4j/tools/build/pom.xml @@ -18,14 +18,4 @@ ${project.basedir}/../.. - - - jdk.tools - jdk.tools - 1.8 - system - ${JAVA_HOME}/lib/tools.jar - - - From 6e992c34dbdb485b3a9ff67c22b136ae1214830d Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Thu, 12 Dec 2024 15:35:09 +0100 Subject: [PATCH 05/25] Something that compiles at last --- .../ibm/icu/dev/test/rbbi/RBBITestMonkey.java | 232 ------------------ .../com/ibm/icu/dev/test/rbbi/RegexRule.java | 106 ++++++++ .../com/ibm/icu/dev/test/rbbi/RemapRule.java | 125 ++++++++++ .../icu/dev/test/rbbi/SegmentationRule.java | 35 +++ 4 files changed, 266 insertions(+), 232 deletions(-) create mode 100644 icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RegexRule.java create mode 100644 icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RemapRule.java create mode 100644 icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java index 271cba70238b..87c1c372cc64 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java @@ -2137,239 +2137,7 @@ static int nextCP(StringBuffer s, int i) { - abstract class SegmentationRule { - enum Resolution { - BREAK, - NO_BREAK, - } - class BreakContext { - BreakContext(int index) { - indexInRemapped = index; - } - int indexInRemapped; - SegmentationRule appliedRule = null; - }; - - SegmentationRule(String name) { - name_ = name; - } - - void apply(String remapped, List resolved); - Resolution resolution(); - String name() { return name_; } - - private final String name_; - } - - class RemapRule extends SegmentationRule { - RemapRule(String name, String pattern, String replacement) { - super(name); - replacement_ = replacement; - pattern_ = - Pattern.compile(pattern, UREGEX_COMMENTS | UREGEX_DOTALL, parseError, status); - } - - @Override - void apply(StringBuilder remapped, List resolved) { - int i = 0; - int offset = 0; - // We find all matches of the `pattern_` and replace them according to - // the `replacement_`, producing the new remapped string `result`. - // For every position i in the original string, - // `resolved[i].indexInRemapped` is nullopt if i lies within a replaced - // match, and is set to the new index in `result` otherwise, by adding - // the accumulated difference `offset` between match lengths and - // replacement lengths. - // Consider a 4-codepoint, 6 code unit string s = ⟨ 𒀀, ◌́, ␠, ◌𝅲 ⟩, where - // ␠ stands for U+0020 and U+12000 𒀀 and U+1D172 ◌𝅲 each require two code - // units, and apply the following two rules: - // 1. (?\P{lb=SP}) \p{lb=CM}* → ${X} - // 2. \p{lb=CM} → A - // The string remapped and the indexInRemapped values change as follows: - // indexInRemapped remapped string rule final - // (aligned on the initial string) applied offset - // 𒀀 ◌́ ␠ ◌𝅲 - // 0 1 2 3 4 5 6 ⟨ 𒀀, ◌́, ␠, ◌𝅲 ⟩ (none) - // 0 - - 2 3 4 5 ⟨ 𒀀, ␠, ◌𝅲 ⟩ 1 -1 - // 0 - - 2 3 - 4 ⟨ 𒀀, ␠, A ⟩ 2 -1 - // - // Note that the last indexInRemapped is always equal to the length of - // the remapped string. - var matcher = new Matcher(pattern_.matcher(remapped, status)); - while (matcher.find()) { - for (;; ++i) { - if (resolved[i].indexInRemapped == null) { - continue; - } - if (resolved[i].indexInRemapped != null && - resolved[i].indexInRemapped > matcher.start(status)) { - break; - } - resolved[i].indexInRemapped += offset; - } - for (;; ++i) { - if (resolved[i].indexInRemapped == null) { - continue; - } - // Note that - // `*resolved[i].indexInRemapped > matcher.end(status)` should - // never happen with ordinary rules, but could in principle - // happen with rules that remap to code point sequences, e.g., - // 1. BC → TYZ - // 2. AT → X - // applied to ⟨ A, B, C ⟩: - // indexInRemapped remapped rule - // A B C - // 0 1 2 3 ⟨ A, B, C ⟩ (none) - // 0 1 - 4 ⟨ A, T, Y, Z ⟩ 1 - // 0 - - 3 ⟨ X, Y, Z ⟩ 2 - // Where for the application of rule 2, the match ends at - // position 2 in remapped, which does not correspond to a - // position in the original string. - if (resolved[i].indexInRemapped != null && - resolved[i].indexInRemapped >= matcher.end(status)) { - break; - } - if (resolved[i].appliedRule != null && - resolved[i].appliedRule.resolution() == BREAK) { - throw new IllegalArgumentException( - "Replacement rule at remapped indices " + - matcher.start(status) + - " sqq. spans a break"); - } - resolved[i].appliedRule = this; - resolved[i].indexInRemapped = null; - } - matcher.appendReplacement(result, replacement_, status); - offset = result.length() - resolved[i].indexInRemapped; - } - for (; i < resolved.size(); ++i) { - if (resolved[i].indexInRemapped == null) { - continue; - } - resolved[i].indexInRemapped += offset; - } - matcher.appendTail(result); - if (resolved.back().indexInRemapped != result.length()) { - StringBuilder indices; - for (final var r : resolved) { - indices += r.indexInRemapped.has_value() ? r.indexInRemapped.toString() : "null"; - indices += ","; - } - throw new IllegalArgumentException(("Inconsistent indexInRemapped " + indices + " for new remapped string " + - result.toUTF8String(s)) - .c_str()); - } - remapped = result; - if (U_FAILURE(status)) { - puts(("Failed to apply rule " + name()).c_str()); - } - } - - @Override - Resolution resolution() { return Resolution.NO_BREAK; } - - private final Pattern pattern_; - private final String replacement_; - }; - - class RegexRule extends SegmentationRule { - RegexRule(String name, String before, char resolution, - String after) { - super(name); - resolution_ = resolution; - before_ = - Pattern.compile(before, UREGEX_COMMENTS | UREGEX_DOTALL, parseError, status); - endsWithBefore_ = Pattern.compile( - ".*(" + before + ")", UREGEX_COMMENTS | UREGEX_DOTALL, parseError, status); - after_ = Pattern.compile(after, UREGEX_COMMENTS | UREGEX_DOTALL, parseError, status); - } - - @Override - void apply(UnicodeString remapped, List resolved) { - UErrorCode status = U_ZERO_ERROR; - // The unicodetools implementation simply tries, for each index, to - // match the string up to the index against /.*(before)/ (with - // `matches`) and the beginning of the string after the index against - // /after/ (with `lookingAt`), but that is very slow, especially for - // nonempty /before/. While the old monkeys are not a production - // implementation, we still do not want them to be too slow, since we - // need to test millions of sample strings. Instead we search for - // /before/ and /after/, and check resulting candidates. This speeds - // things up by a factor of ~40. - // We need to be careful about greedy matching: The first position where - // the rule matches may be before the end of the first /before/ match. - // However, it is both: - // 1. within a /before/ match or at its bounds, - // 2. at the beginning of an /after/ match. - // Further, the /before/ context of the rule matches within the - // aforementioned /before/ match. Note that we need to look for - // overlapping matches, thus calls to `find` are always preceded by a - // reset via `region`. - final Matcher beforeSearch = before_.matcher(remapped, status); - final Matcher afterSearch = after_.matcher(remapped, status); - beforeSearch.useAnchoringBounds(false); - afterSearch.useAnchoringBounds(false); - if (beforeSearch.find() && afterSearch.find()) { - for (;;) { - if (afterSearch.start(status) < beforeSearch.start(status)) { - afterSearch.region(beforeSearch.start(status), remapped.length(), status); - if (!afterSearch.find()) { - break; - } - } else if (afterSearch.start(status) > beforeSearch.end(status)) { - if (beforeSearch.start(status) == remapped.length()) { - break; - } - beforeSearch.region(remapped.moveIndex32(beforeSearch.start(status), 1), - remapped.length(), status); - if (!beforeSearch.find()) { - break; - } - } else {/* - auto const it = std::find_if(resolved.begin(), resolved.end(), [&](auto r) { - return r.indexInRemapped == afterSearch.start(status); - }); - if (it == resolved.end()) { - puts(("Rule " + name() + - " found a break at a position which does not correspond to an index in " - "the original string") - .c_str()); - std::terminate(); - }*/ - U_ASSERT(U_SUCCESS(status)); - if (it.appliedRule == nullptr && - endsWithBefore_.matcher(remapped, status) - .useAnchoringBounds(false) - .region(beforeSearch.start(status), afterSearch.start(status), status) - .matches(status)) { - it.appliedRule = this; - } - if (afterSearch.start(status) == remapped.length()) { - break; - } - afterSearch.region(remapped.moveIndex32(afterSearch.start(status), 1), - remapped.length(), status); - if (!afterSearch.find()) { - break; - } - } - U_ASSERT(U_SUCCESS(status)); - } - } - if (U_FAILURE(status)) { - puts(("Failed to apply rule " + name()).c_str()); - } - } - - @Override - Resolution resolution() { return resolution_; } - private final Pattern before_; - private final Pattern endsWithBefore_; - private final Pattern after_; - private final Resolution resolution_; - }; /** * random number generator. Not using Java's built-in Randoms for two reasons: diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RegexRule.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RegexRule.java new file mode 100644 index 000000000000..b25299c35fb8 --- /dev/null +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RegexRule.java @@ -0,0 +1,106 @@ +// © 2024 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +package com.ibm.icu.dev.test.rbbi; + +import java.util.Arrays; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A regex rule expressed as in UAXes #14 and #29. + * + * The rule consists of two regexes for context before and after a position in the remapped text, + * and of a resolution (break or not) that applies to the corresponding position in the original + * string if both match. + */ +class RegexRule extends SegmentationRule { + RegexRule(String name, String before, Resolution resolution, + String after) { + super(name); + resolution_ = resolution; + before_ = + Pattern.compile(before, Pattern.COMMENTS | Pattern.DOTALL); + endsWithBefore_ = Pattern.compile( + ".*(" + before + ")", Pattern.COMMENTS | Pattern.DOTALL); + after_ = Pattern.compile(after, Pattern.COMMENTS | Pattern.DOTALL); + } + + @Override + void apply(StringBuilder remapped, BreakContext[] resolved) { + // The unicodetools implementation simply tries, for each index, to + // match the string up to the index against /.*(before)/ (with + // `matches`) and the beginning of the string after the index against + // /after/ (with `lookingAt`), but that is very slow, especially for + // nonempty /before/. While the old monkeys are not a production + // implementation, we still do not want them to be too slow, since we + // need to test millions of sample strings. Instead we search for + // /before/ and /after/, and check resulting candidates. This speeds + // things up by a factor of ~40. + // We need to be careful about greedy matching: The first position where + // the rule matches may be before the end of the first /before/ match. + // However, it is both: + // 1. within a /before/ match or at its bounds, + // 2. at the beginning of an /after/ match. + // Further, the /before/ context of the rule matches within the + // aforementioned /before/ match. Note that we need to look for + // overlapping matches, thus calls to `find` are always preceded by a + // reset via `region`. + final Matcher beforeSearch = before_.matcher(remapped); + final Matcher afterSearch = after_.matcher(remapped); + beforeSearch.useAnchoringBounds(false); + afterSearch.useAnchoringBounds(false); + if (beforeSearch.find() && afterSearch.find()) { + for (;;) { + if (afterSearch.start() < beforeSearch.start()) { + afterSearch.region(beforeSearch.start(), remapped.length()); + if (!afterSearch.find()) { + break; + } + } else if (afterSearch.start() > beforeSearch.end()) { + if (beforeSearch.start() == remapped.length()) { + break; + } + beforeSearch.region(remapped.offsetByCodePoints(beforeSearch.start(), 1), + remapped.length()); + if (!beforeSearch.find()) { + break; + } + } else { + final Optional position = Arrays.stream(resolved) + .filter(r -> r.indexInRemapped == afterSearch.start()) + .findFirst(); + if (!position.isPresent()) { + throw new IllegalArgumentException(("Rule " + name() + + " found a break at a position which does not correspond to an index in " + + "the original string")); + } + if (position.get().appliedRule == null && + endsWithBefore_.matcher(remapped) + .useAnchoringBounds(false) + .region(beforeSearch.start(), afterSearch.start()) + .matches()) { + position.get().appliedRule = this; + } + if (afterSearch.start() == remapped.length()) { + break; + } + afterSearch.region(remapped.offsetByCodePoints(afterSearch.start(), 1), + remapped.length()); + if (!afterSearch.find()) { + break; + } + } + } + } + } + + @Override + Resolution resolution() { return resolution_; } + + private final Pattern before_; + private final Pattern endsWithBefore_; + private final Pattern after_; + private final Resolution resolution_; +} \ No newline at end of file diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RemapRule.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RemapRule.java new file mode 100644 index 000000000000..5eebd49c675b --- /dev/null +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RemapRule.java @@ -0,0 +1,125 @@ +// © 2024 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +package com.ibm.icu.dev.test.rbbi; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A segmentation rule expressed as in UAXes #14 and #29. + * + * A remap rule performs normal a regex replacement applied to the remapped string. + * This replacement may use capturing groups. Any positions in the original string that correspond + * to positions within the replaced text are resolved to NO_BREAK by this rule. + */ +public class RemapRule extends SegmentationRule { + RemapRule(String name, String pattern, String replacement) { + super(name); + replacement_ = replacement; + pattern_ = Pattern.compile(pattern, Pattern.COMMENTS | Pattern.DOTALL); + } + + @Override + void apply(StringBuilder remapped, BreakContext[] resolved) { + // This one has to be a StringBuffer rather than a StringBuilder because the overload of + // AppendReplacement that takes a StringBuilder is new in Java 9. + StringBuffer result = new StringBuffer(); + int i = 0; + int offset = 0; + // We find all matches of the `pattern_` and replace them according to + // the `replacement_`, producing the new remapped string `result`. + // For every position i in the original string, + // `resolved[i].indexInRemapped` is null if i lies within a replaced + // match, and is set to the new index in `result` otherwise, by adding + // the accumulated difference `offset` between match lengths and + // replacement lengths. + // Consider a 4-codepoint, 6 code unit string s = ⟨ 𒀀, ◌́, ␠, ◌𝅲 ⟩, where + // ␠ stands for U+0020 and U+12000 𒀀 and U+1D172 ◌𝅲 each require two code + // units, and apply the following two rules: + // 1. (?\P{lb=SP}) \p{lb=CM}* → ${X} + // 2. \p{lb=CM} → A + // The string remapped and the indexInRemapped values change as follows: + // indexInRemapped remapped string rule final + // (aligned on the initial string) applied offset + // 𒀀 ◌́ ␠ ◌𝅲 + // 0 1 2 3 4 5 6 ⟨ 𒀀, ◌́, ␠, ◌𝅲 ⟩ (none) + // 0 - - 2 3 4 5 ⟨ 𒀀, ␠, ◌𝅲 ⟩ 1 -1 + // 0 - - 2 3 - 4 ⟨ 𒀀, ␠, A ⟩ 2 -1 + // + // Note that the last indexInRemapped is always equal to the length of + // the remapped string. + final Matcher matcher = pattern_.matcher(remapped); + while (matcher.find()) { + for (;; ++i) { + if (resolved[i].indexInRemapped == null) { + continue; + } + if (resolved[i].indexInRemapped != null && + resolved[i].indexInRemapped > matcher.start()) { + break; + } + resolved[i].indexInRemapped += offset; + } + for (;; ++i) { + if (resolved[i].indexInRemapped == null) { + continue; + } + // Note that + // `*resolved[i].indexInRemapped > matcher.end()` should + // never happen with ordinary rules, but could in principle + // happen with rules that remap to code point sequences, e.g., + // 1. BC → TYZ + // 2. AT → X + // applied to ⟨ A, B, C ⟩: + // indexInRemapped remapped rule + // A B C + // 0 1 2 3 ⟨ A, B, C ⟩ (none) + // 0 1 - 4 ⟨ A, T, Y, Z ⟩ 1 + // 0 - - 3 ⟨ X, Y, Z ⟩ 2 + // Where for the application of rule 2, the match ends at + // position 2 in remapped, which does not correspond to a + // position in the original string. + if (resolved[i].indexInRemapped != null && + resolved[i].indexInRemapped >= matcher.end()) { + break; + } + if (resolved[i].appliedRule != null && + resolved[i].appliedRule.resolution() == Resolution.BREAK) { + throw new IllegalArgumentException( + "Replacement rule at remapped indices " + + matcher.start() + + " sqq. spans a break"); + } + resolved[i].appliedRule = this; + resolved[i].indexInRemapped = null; + } + matcher.appendReplacement(result, replacement_); + offset = result.length() - resolved[i].indexInRemapped; + } + for (; i < resolved.length; ++i) { + if (resolved[i].indexInRemapped == null) { + continue; + } + resolved[i].indexInRemapped += offset; + } + matcher.appendTail(result); + if (resolved[resolved.length - 1].indexInRemapped != result.length()) { + StringBuilder indices = new StringBuilder(); + for (final BreakContext r : resolved) { + indices.append(r.indexInRemapped == null ? "null" : r.indexInRemapped.toString()); + indices.append(","); + } + throw new IllegalArgumentException("Inconsistent indexInRemapped " + indices + " for new remapped string " + + result); + } + remapped.setLength(0); + remapped.append(result); + } + + @Override + Resolution resolution() { return Resolution.NO_BREAK; } + + private final Pattern pattern_; + private final String replacement_; +} \ No newline at end of file diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java new file mode 100644 index 000000000000..b60c17304c22 --- /dev/null +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java @@ -0,0 +1,35 @@ +// © 2024 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +package com.ibm.icu.dev.test.rbbi; + +/** + * A segmentation rule expressed as in UAXes #14 and #29. + * + * Rules are applied sequentially. + * Rules operate on a mutable remapped string (which the caller should initially set to the string + * to be segmented), and can resolve positions in the original string to either BREAK or NO_BREAK. + */ +public abstract class SegmentationRule { + enum Resolution { + BREAK, + NO_BREAK, + } + class BreakContext { + BreakContext(int index) { + indexInRemapped = index; + } + Integer indexInRemapped; + SegmentationRule appliedRule = null; + }; + + SegmentationRule(String name) { + name_ = name; + } + + abstract void apply(StringBuilder remapped, BreakContext[] resolved); + abstract Resolution resolution(); + String name() { return name_; } + + private final String name_; +} From e3a81367e52c4f3aa4c1238cbec14f7542dc1b36 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Thu, 12 Dec 2024 15:40:17 +0100 Subject: [PATCH 06/25] Update the tailorings too --- icu4c/source/data/brkitr/rules/line_cj.txt | 2 +- icu4c/source/data/brkitr/rules/line_loose.txt | 2 +- icu4c/source/data/brkitr/rules/line_loose_cj.txt | 2 +- icu4c/source/data/brkitr/rules/line_loose_phrase_cj.txt | 2 +- icu4c/source/data/brkitr/rules/line_normal.txt | 2 +- icu4c/source/data/brkitr/rules/line_normal_cj.txt | 2 +- icu4c/source/data/brkitr/rules/line_normal_phrase_cj.txt | 2 +- icu4c/source/data/brkitr/rules/line_phrase_cj.txt | 2 +- icu4c/source/test/testdata/break_rules/line_cj.txt | 2 +- icu4c/source/test/testdata/break_rules/line_loose.txt | 2 +- icu4c/source/test/testdata/break_rules/line_loose_cj.txt | 2 +- icu4c/source/test/testdata/break_rules/line_normal.txt | 2 +- icu4c/source/test/testdata/break_rules/line_normal_cj.txt | 2 +- .../resources/com/ibm/icu/dev/test/rbbi/break_rules/line.txt | 2 +- .../resources/com/ibm/icu/dev/test/rbbi/break_rules/line_cj.txt | 2 +- .../com/ibm/icu/dev/test/rbbi/break_rules/line_loose.txt | 2 +- .../com/ibm/icu/dev/test/rbbi/break_rules/line_loose_cj.txt | 2 +- .../com/ibm/icu/dev/test/rbbi/break_rules/line_normal.txt | 2 +- .../com/ibm/icu/dev/test/rbbi/break_rules/line_normal_cj.txt | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/icu4c/source/data/brkitr/rules/line_cj.txt b/icu4c/source/data/brkitr/rules/line_cj.txt index fc615f55db21..793163898e00 100644 --- a/icu4c/source/data/brkitr/rules/line_cj.txt +++ b/icu4c/source/data/brkitr/rules/line_cj.txt @@ -298,7 +298,7 @@ $LB20NonBreaks = [$LB18NonBreaks - $CB]; # and then to default UAX #14 behaviour (UTC-179-C32). # ^($HY | $HH) $CM* $ALPlus; -$GL ($HY | $HH) $CM* $ALPlus; +$GL $CM* ($HY | $HH) $CM* $ALPlus; # Non-breaking CB from LB8a: $CB $CM* $ZWJ ($HY | $HH) $CM* $ALPlus; # Non-breaking SP from LB14: diff --git a/icu4c/source/data/brkitr/rules/line_loose.txt b/icu4c/source/data/brkitr/rules/line_loose.txt index 2bb9be5845f8..9ff4e17eb3a5 100644 --- a/icu4c/source/data/brkitr/rules/line_loose.txt +++ b/icu4c/source/data/brkitr/rules/line_loose.txt @@ -306,7 +306,7 @@ $LB20NonBreaks = [$LB18NonBreaks - $CB]; # and then to default UAX #14 behaviour (UTC-179-C32). # ^($HY | $HH) $CM* $ALPlus; -$GL ($HY | $HH) $CM* $ALPlus; +$GL $CM* ($HY | $HH) $CM* $ALPlus; # Non-breaking CB from LB8a: $CB $CM* $ZWJ ($HY | $HH) $CM* $ALPlus; # Non-breaking SP from LB14: diff --git a/icu4c/source/data/brkitr/rules/line_loose_cj.txt b/icu4c/source/data/brkitr/rules/line_loose_cj.txt index 15715a225123..428d225f16d9 100644 --- a/icu4c/source/data/brkitr/rules/line_loose_cj.txt +++ b/icu4c/source/data/brkitr/rules/line_loose_cj.txt @@ -318,7 +318,7 @@ $LB20NonBreaks = [$LB18NonBreaks - $CB]; # and then to default UAX #14 behaviour (UTC-179-C32). # ^($HY | $HH) $CM* $ALPlus; -$GL ($HY | $HH) $CM* $ALPlus; +$GL $CM* ($HY | $HH) $CM* $ALPlus; # Non-breaking CB from LB8a: $CB $CM* $ZWJ ($HY | $HH) $CM* $ALPlus; # Non-breaking SP from LB14: diff --git a/icu4c/source/data/brkitr/rules/line_loose_phrase_cj.txt b/icu4c/source/data/brkitr/rules/line_loose_phrase_cj.txt index 87ab33b48a1c..2edf4b3bc33a 100644 --- a/icu4c/source/data/brkitr/rules/line_loose_phrase_cj.txt +++ b/icu4c/source/data/brkitr/rules/line_loose_phrase_cj.txt @@ -331,7 +331,7 @@ $LB20NonBreaks = [$LB18NonBreaks - $CB]; # and then to default UAX #14 behaviour (UTC-179-C32). # ^($HY | $HH) $CM* $ALPlus; -$GL ($HY | $HH) $CM* $ALPlus; +$GL $CM* ($HY | $HH) $CM* $ALPlus; # Non-breaking CB from LB8a: $CB $CM* $ZWJ ($HY | $HH) $CM* $ALPlus; # Non-breaking SP from LB14: diff --git a/icu4c/source/data/brkitr/rules/line_normal.txt b/icu4c/source/data/brkitr/rules/line_normal.txt index c41280c28d1c..bf6dee8c05cd 100644 --- a/icu4c/source/data/brkitr/rules/line_normal.txt +++ b/icu4c/source/data/brkitr/rules/line_normal.txt @@ -299,7 +299,7 @@ $LB20NonBreaks = [$LB18NonBreaks - $CB]; # and then to default UAX #14 behaviour (UTC-179-C32). # ^($HY | $HH) $CM* $ALPlus; -$GL ($HY | $HH) $CM* $ALPlus; +$GL $CM* ($HY | $HH) $CM* $ALPlus; # Non-breaking CB from LB8a: $CB $CM* $ZWJ ($HY | $HH) $CM* $ALPlus; # Non-breaking SP from LB14: diff --git a/icu4c/source/data/brkitr/rules/line_normal_cj.txt b/icu4c/source/data/brkitr/rules/line_normal_cj.txt index 31dd65854cb1..f596454621d0 100644 --- a/icu4c/source/data/brkitr/rules/line_normal_cj.txt +++ b/icu4c/source/data/brkitr/rules/line_normal_cj.txt @@ -304,7 +304,7 @@ $LB20NonBreaks = [$LB18NonBreaks - $CB]; # and then to default UAX #14 behaviour (UTC-179-C32). # ^($HY | $HH) $CM* $ALPlus; -$GL ($HY | $HH) $CM* $ALPlus; +$GL $CM* ($HY | $HH) $CM* $ALPlus; # Non-breaking CB from LB8a: $CB $CM* $ZWJ ($HY | $HH) $CM* $ALPlus; # Non-breaking SP from LB14: diff --git a/icu4c/source/data/brkitr/rules/line_normal_phrase_cj.txt b/icu4c/source/data/brkitr/rules/line_normal_phrase_cj.txt index 85d771fcdbf9..e0bbd00025f9 100644 --- a/icu4c/source/data/brkitr/rules/line_normal_phrase_cj.txt +++ b/icu4c/source/data/brkitr/rules/line_normal_phrase_cj.txt @@ -317,7 +317,7 @@ $LB20NonBreaks = [$LB18NonBreaks - $CB]; # and then to default UAX #14 behaviour (UTC-179-C32). # ^($HY | $HH) $CM* $ALPlus; -$GL ($HY | $HH) $CM* $ALPlus; +$GL $CM* ($HY | $HH) $CM* $ALPlus; # Non-breaking CB from LB8a: $CB $CM* $ZWJ ($HY | $HH) $CM* $ALPlus; # Non-breaking SP from LB14: diff --git a/icu4c/source/data/brkitr/rules/line_phrase_cj.txt b/icu4c/source/data/brkitr/rules/line_phrase_cj.txt index 41e05bf4963f..14b118789e7c 100644 --- a/icu4c/source/data/brkitr/rules/line_phrase_cj.txt +++ b/icu4c/source/data/brkitr/rules/line_phrase_cj.txt @@ -310,7 +310,7 @@ $LB20NonBreaks = [$LB18NonBreaks - $CB]; # and then to default UAX #14 behaviour (UTC-179-C32). # ^($HY | $HH) $CM* $ALPlus; -$GL ($HY | $HH) $CM* $ALPlus; +$GL $CM* ($HY | $HH) $CM* $ALPlus; # Non-breaking CB from LB8a: $CB $CM* $ZWJ ($HY | $HH) $CM* $ALPlus; # Non-breaking SP from LB14: diff --git a/icu4c/source/test/testdata/break_rules/line_cj.txt b/icu4c/source/test/testdata/break_rules/line_cj.txt index 7aad76ecf107..bb0a6880ea29 100644 --- a/icu4c/source/test/testdata/break_rules/line_cj.txt +++ b/icu4c/source/test/testdata/break_rules/line_cj.txt @@ -180,7 +180,7 @@ LB11.2: SP WJ; LB11.3: WJ CM* [^CM]; # Needs to apply before LB12, because the new monkeys are not greedy. -LB20a.2: GL (HY | HH) CM* AL; +LB20a.2: GL CM* (HY | HH) CM* AL; LB12: GL CM* [^CM]; LB12a: [^SP BA HY] CM* GL; diff --git a/icu4c/source/test/testdata/break_rules/line_loose.txt b/icu4c/source/test/testdata/break_rules/line_loose.txt index 72e7563c9274..f9152060bf2d 100644 --- a/icu4c/source/test/testdata/break_rules/line_loose.txt +++ b/icu4c/source/test/testdata/break_rules/line_loose.txt @@ -181,7 +181,7 @@ LB11.2: SP WJ; LB11.3: WJ CM* [^CM]; # Needs to apply before LB12, because the new monkeys are not greedy. -LB20a.2: GL (HY | HH) CM* AL; +LB20a.2: GL CM* (HY | HH) CM* AL; LB12: GL CM* [^CM]; LB12a: [^SP BA HY] CM* GL; diff --git a/icu4c/source/test/testdata/break_rules/line_loose_cj.txt b/icu4c/source/test/testdata/break_rules/line_loose_cj.txt index 99d01874d1fb..b04236532bbd 100644 --- a/icu4c/source/test/testdata/break_rules/line_loose_cj.txt +++ b/icu4c/source/test/testdata/break_rules/line_loose_cj.txt @@ -200,7 +200,7 @@ LB11.2: SP WJ; LB11.3: WJ CM* [^CM]; # Needs to apply before LB12, because the new monkeys are not greedy. -LB20a.2: GL (HY | HH) CM* AL; +LB20a.2: GL CM* (HY | HH) CM* AL; LB12: GL CM* [^CM]; LB12a: [^SP BA BAX HY] CM* GL; diff --git a/icu4c/source/test/testdata/break_rules/line_normal.txt b/icu4c/source/test/testdata/break_rules/line_normal.txt index 211298539797..c7c518d5b68b 100644 --- a/icu4c/source/test/testdata/break_rules/line_normal.txt +++ b/icu4c/source/test/testdata/break_rules/line_normal.txt @@ -182,7 +182,7 @@ LB11.2: SP WJ; LB11.3: WJ CM* [^CM]; # Needs to apply before LB12, because the new monkeys are not greedy. -LB20a.2: GL (HY | HH) CM* AL; +LB20a.2: GL CM* (HY | HH) CM* AL; LB12: GL CM* [^CM]; LB12a: [^SP BA HY] CM* GL; diff --git a/icu4c/source/test/testdata/break_rules/line_normal_cj.txt b/icu4c/source/test/testdata/break_rules/line_normal_cj.txt index 2061f9170848..cfa9c7968e1b 100644 --- a/icu4c/source/test/testdata/break_rules/line_normal_cj.txt +++ b/icu4c/source/test/testdata/break_rules/line_normal_cj.txt @@ -186,7 +186,7 @@ LB11.2: SP WJ; LB11.3: WJ CM* [^CM]; # Needs to apply before LB12, because the new monkeys are not greedy. -LB20a.2: GL (HY | HH) CM* AL; +LB20a.2: GL CM* (HY | HH) CM* AL; LB12: GL CM* [^CM]; LB12a: [^SP BA HY] CM* GL; diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line.txt b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line.txt index 9f85b7917139..e2154abf6309 100644 --- a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line.txt +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line.txt @@ -176,7 +176,7 @@ LB11.2: SP WJ; LB11.3: WJ CM* [^CM]; # Needs to apply before LB12, because the new monkeys are not greedy. -LB20a.2: GL (HY | HH) CM* AL; +LB20a.2: GL CM* (HY | HH) CM* AL; LB12: GL CM* [^CM]; LB12a: [^SP BA HY] CM* GL; diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_cj.txt b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_cj.txt index 7aad76ecf107..bb0a6880ea29 100644 --- a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_cj.txt +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_cj.txt @@ -180,7 +180,7 @@ LB11.2: SP WJ; LB11.3: WJ CM* [^CM]; # Needs to apply before LB12, because the new monkeys are not greedy. -LB20a.2: GL (HY | HH) CM* AL; +LB20a.2: GL CM* (HY | HH) CM* AL; LB12: GL CM* [^CM]; LB12a: [^SP BA HY] CM* GL; diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_loose.txt b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_loose.txt index 72e7563c9274..f9152060bf2d 100644 --- a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_loose.txt +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_loose.txt @@ -181,7 +181,7 @@ LB11.2: SP WJ; LB11.3: WJ CM* [^CM]; # Needs to apply before LB12, because the new monkeys are not greedy. -LB20a.2: GL (HY | HH) CM* AL; +LB20a.2: GL CM* (HY | HH) CM* AL; LB12: GL CM* [^CM]; LB12a: [^SP BA HY] CM* GL; diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_loose_cj.txt b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_loose_cj.txt index 99d01874d1fb..b04236532bbd 100644 --- a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_loose_cj.txt +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_loose_cj.txt @@ -200,7 +200,7 @@ LB11.2: SP WJ; LB11.3: WJ CM* [^CM]; # Needs to apply before LB12, because the new monkeys are not greedy. -LB20a.2: GL (HY | HH) CM* AL; +LB20a.2: GL CM* (HY | HH) CM* AL; LB12: GL CM* [^CM]; LB12a: [^SP BA BAX HY] CM* GL; diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_normal.txt b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_normal.txt index 211298539797..c7c518d5b68b 100644 --- a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_normal.txt +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_normal.txt @@ -182,7 +182,7 @@ LB11.2: SP WJ; LB11.3: WJ CM* [^CM]; # Needs to apply before LB12, because the new monkeys are not greedy. -LB20a.2: GL (HY | HH) CM* AL; +LB20a.2: GL CM* (HY | HH) CM* AL; LB12: GL CM* [^CM]; LB12a: [^SP BA HY] CM* GL; diff --git a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_normal_cj.txt b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_normal_cj.txt index 2061f9170848..cfa9c7968e1b 100644 --- a/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_normal_cj.txt +++ b/icu4j/main/core/src/test/resources/com/ibm/icu/dev/test/rbbi/break_rules/line_normal_cj.txt @@ -186,7 +186,7 @@ LB11.2: SP WJ; LB11.3: WJ CM* [^CM]; # Needs to apply before LB12, because the new monkeys are not greedy. -LB20a.2: GL (HY | HH) CM* AL; +LB20a.2: GL CM* (HY | HH) CM* AL; LB12: GL CM* [^CM]; LB12a: [^SP BA HY] CM* GL; From 6f6fdde3e883f45e0c8e7d3e3c1b8b14780356e0 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Thu, 12 Dec 2024 15:59:46 +0100 Subject: [PATCH 07/25] Update .brk files (to be flipped by @markusicu) --- .../icu/impl/data/icudata/brkitr/line_cj.brk | Bin 73120 -> 73264 bytes .../impl/data/icudata/brkitr/line_loose.brk | Bin 75232 -> 75384 bytes .../data/icudata/brkitr/line_loose_cj.brk | Bin 80768 -> 80920 bytes .../icudata/brkitr/line_loose_phrase_cj.brk | Bin 86176 -> 86328 bytes .../impl/data/icudata/brkitr/line_normal.brk | Bin 72976 -> 73120 bytes .../data/icudata/brkitr/line_normal_cj.brk | Bin 74080 -> 74216 bytes .../icudata/brkitr/line_normal_phrase_cj.brk | Bin 79264 -> 79416 bytes .../data/icudata/brkitr/line_phrase_cj.brk | Bin 78272 -> 78416 bytes 8 files changed, 0 insertions(+), 0 deletions(-) diff --git a/icu4j/main/core/src/main/resources/com/ibm/icu/impl/data/icudata/brkitr/line_cj.brk b/icu4j/main/core/src/main/resources/com/ibm/icu/impl/data/icudata/brkitr/line_cj.brk index dbbbc0dfbae18cc57f37e207c81140f40c36e11f..ca6e43ba42477d4f3e5027c35a16ac34e68400d6 100644 GIT binary patch literal 73264 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 b2uS3FV1wwnH*5`A(Q_^!Wcg~bkNbZCq&^1g literal 73120 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 literal 80768 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 diff --git a/icu4j/main/core/src/main/resources/com/ibm/icu/impl/data/icudata/brkitr/line_loose_phrase_cj.brk b/icu4j/main/core/src/main/resources/com/ibm/icu/impl/data/icudata/brkitr/line_loose_phrase_cj.brk index bebfe7285a2f0182dc6a901317f0ef49b8ecb254..bba43a75fc6c08bcf31ada147e2dff82247e11e4 100644 GIT binary patch literal 86328 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 literal 86176 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 diff --git a/icu4j/main/core/src/main/resources/com/ibm/icu/impl/data/icudata/brkitr/line_normal.brk b/icu4j/main/core/src/main/resources/com/ibm/icu/impl/data/icudata/brkitr/line_normal.brk index 0229e2cb2f2d453b597f44db4f1ec6ee7d36ee63..371a487782a92080441cf53c6d9475534d069c24 100644 GIT binary patch literal 73120 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 I$J?jh1bZo;w^R@TbAu>TSS(OB$F6B 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 Z$4BmUTS9j8j2DoycqN|jg8Pu?{x9zbb*BIT diff --git a/icu4j/main/core/src/main/resources/com/ibm/icu/impl/data/icudata/brkitr/line_normal_phrase_cj.brk b/icu4j/main/core/src/main/resources/com/ibm/icu/impl/data/icudata/brkitr/line_normal_phrase_cj.brk index 7cbc69987714c53bcc657256718752b40801061b..49fa8e0416b3e0cc2dc24ed4f16368e379fb54c2 100644 GIT binary patch literal 79416 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 literal 79264 zcmeHw3t$~bdG4&v(ep_1LzX4mvMtGa9oe${N*voUwygLeN46C!w&N&{d~6@v!m{K@ zGKq-;1PW;gAtWIrn5H~ZQV1!Kgi@e@L!c>89^nxn(9%K*7r1TUUeZt?f&Tx@&TDsO z&+h6N)O$NeXLp|eeDlpeuicp=@|N$ml39c}q=0N1J5gUm2uV7G)OU}bIx{wO?D#}| zTX(vC#j@oW*JnnK)DLGSvSVxO_lyi39zBv>Qr|Ojcqt7Soj9Hy8(&)AJ9IcZGM=q( zsfUOs$LmLr)}J2FuB|^lF>z{bXXo|TU%&J;OT2V+>{#bvp3TzZ6DNo3A@@h#g&2r) z(R8?Ofo~rn-+YRYr|StR0l$A*M##pG5oi8?5>hh@?mvMz;BPa|X$ZLsz8m11vc;N0 z{skmKlJF@cMWmRNkWx|xxXVcesU*{&q%+7&QU!HZ4PmoM4XGt_Al_X1od?%CGM_9U z3&B=T8b~8qM4Cu5X(6qojigDt`&mqukPfnxbdqIcImB84`4oU(Gg(Pi!4-5sm>@(K z2}C6j|6)MIOtDB!D53DPZQuPSINX?eeD#%)J@LEIv zbuv(~)d_pOYm>FcJlaJJyG2EGX$U>43e=_He$OJzN#K^yf5=0Qd_@lYa5{T zUt($fC?QVkf0?EAZw!TqwKa3=?-Cp}&|-#d{TnTaHu(^t*lyAKSr5q(@g9;fJP&Sl zN& z?;-oim7qs^V1BeW2cmrf5$lhxf_z#iD2d248`*8)Xum+jG@F;t#1RkETmRJp5et{& znnc7?svPYHPZLK$qsV}mu?3GJ*Tic5wo&AO;ON?b{%9~@Ttf+Q`lADuT01BZ@zyWr z*=m1eA89io5fRTk`=ABUA#zxmAL)Je5m9SFJ!BRn#F=Lw(h$ix?NJX$sDrqM(lz@S z_#B5Xj7UsAjH(x zh9e>(biJk`IvEquum#bG0TJe>*P7%A!-~l$IR#&|)sgEAh%iiDuTdvl)LI(>w~3=M z10ug3c@gm(aK<)nK*Yjj4{6e)F%1#wA~O?Y!fk7(!4~~f7bA^77ge*vuClA?Dx1%0 z^>u%|9@4h%e}$*kN?q<$Jqn(UT<@zj|47ScY`kW0R{ILAKjI;T+#nF0p`!>7Q4#Uk z2oKZyqgR3iHj1$Dup{ja5Z$ENHwr{?M-h||XI#5c<4E?|XMKpUq~6sL>$BzaBgO_> z9o+;Hc%L0*b#&H(=v5v>n3uox^HGE$(QED13L*?Q)tagGzY5}-YV9=wQLfd|amy+j zCB&(<*H}2ZSs)6ULk4m5+7KKuZuLIan?fK$iC%LXc$hf4Jp`hvKtykjfJmJm-EKj2 zhX)ZGX>ENrE2rMp-l8E2|NQ6o50A|mgT2rgAcbPsq+&E_!(xz~e;KN~?r`numXuH8%Gt^4n_)Y^R>M2Iz7 z5BYWjN36uQnLq0x?}JDtj?Nhnp}gQ)V>#lrj=UoTA|4-W?ay4tIk=k;-S0u9)|&bx z0^eqt+uENCL}A++d*gS%R%^2LzcUmf+4}vf>~}>*#GXUG(}L*T5fS-U+3$&o=-nD3 zUay2a;6cPgxE|qKh~U-HUkF4jt?ZAaW^<|P{Q+=z_2|9e_n-%nS&#lQ;29fAh%>8w z(85vp_q6X51=Pp2e7&bd@yOBpLvi#_07obx4oB|~$&tC%N&wOOENcQA!1A&DV(-_QNNB#;VJPa%Jde@iD{4q2QU-rKLVaOeQI_dRc z`o8}I8X^ja)*8MK&OP>j5Ms%B0ZYxUb>fY3z3VO88uPG?{py=&b?guS=Gdm$>wzP_ z%0>(lk-zn;W54aqu@_NvJwilr^ypHh)>Mvy-a^Q64c|i8G#e$vL1cRi@gb1M^ytI% z`H`8Up!XsuAr7JsSvcA#5CyHWQLB+7X7nC1-*4B$BC^*SB8tP&Mhi#b-}kF}#JI}e z`+k>vdh1pln*9;#nxXlb9Cit&*wRl;r?xdeN2c{!&?tg?BI4BAB6BOfJy z6N{rqEv>&tAj-9G#HL62-Zz4+k#U5GqzT%`rpC*)j}7NXcH^D-V@y9sQ8gPmilf<+ z-&O7l}@VmzT)`KYQuCY&85Pi~vh|qohQ4oE~hlnw$?$R*575yYcGmRpj7Kr#e zNNoMG*7&;LzN`Mc2a#z_YeMvC3!<=^ZC{I^W~*(D)mrYe5c*y}`DFC*P#hhD@*IaR zGrDGfCMHLZgD_LA?e`&Kxq6@0;0lr(9Z|k_A9@i*?;#P9T5GKJ=dQK=mbUhU;E1<= zouk~(YCkJ#jiog8N2Z-DPgoFrE(fCACs_V2CZf+-5FHSRa_!K>QN*gKW*_^p*5b4^eK&NWsJ5dXjxfMKKY9v$zXsnw#BS)Xdk~pNTK+uy8-}x@D1mYG zP4YDP7Wp>$4m~US4EZkkp8NSn@=x@v==bSa(WmK~ny-W3cgR1Je}St5CsonB7rs9T zg^0gj`=J5RvjWk-k{^*D!+VkcMG^f3-W~naeRuRT^1qeSt3RTMegJ;W1_*~WfE z6sN7d+`>`#UG;oci>IKasUs(2a%9_8??n`yBSaL3qhSk2;rID-j!hLQTP)qHjZ8?5OI!NvXtM_;6CcU z5!(qCjLUjRJX0f0(Az8-3tK;$h48IM3mEy-kGX^1X0aIh89y&a(ObXdDB6te2Br0@ znjQX>k8OUGpHn_^EfVgO4_k}Kr!|c2e4g^*^+$*wk*%%S=Giw1&5l-U`ne;?(W^k7 zckK6%Yka-NN}{%aI+E{mM_xpB&9-rbh~o4|ud-e-O8&fvqH~0Z;&61kg`+z>9Fg2Q0k!sP@h&@P*VtRhZ(?zDx7OBVDZ`&$l^k8z)2m)Y_WsB=_TzclIC^xCg`;}| zYE3`EVjF2uLL5YsKToUHnymFY@^(O!YjtFrjo8l9dQbR9uQfyzhok!}wRX-^YrOR8 zY=oW5wsDm26TV(V(K$jyQ8@ao`-_$56ppag(YzPFFM2q7iLejn=q2}e{a%6-bv&$C z*j|Dj@^>KNC6HL}`ZkHsG}zb~Kc2EvguMhgzXUTj7NS05yWfMz#*zIb)SrWdXycmc z6f`o#OjtzT^_qMOfrz5i8vi9`{am&j`@@~fmSeyF&GEY;b7VS~%^4yj9HOAH9}&gj zNIxkqIeIt9Q`c)|J(3(<_>{_MJ~hx`knM@$z?Yg(x#()IvYn_Z{c z`X2~YYeDlPL=;Dl9#DEn6_LdJ$Fy-o*k?Z&iX%MVZ<`;Xgg6}8&iDVGBLbNAUl|Vi z#Q~HM2hr~hwFVqH4o778^?Wg7a|(bX%kO7X2{8~sI{&ZdW39bE3?hO%qHMK>5@H~N zoc)LZkdxquoMb?)@jY6G-G@{{3`CGlBI0NDk)scAM7-9VLi}jYk6^|IJ|$$f8&=Hj zG#>rM0SJZJh#N|u)zYvu_|w@4+?Pyd+fwymw9ggAOsr09`?hF7DbP7l0QNdC09(L?8 z%)f!~QIHUA=Feue`k7OR=1g6<6W!PGqX+}iCpUlJ_k{SKY@y7+C1{!Qav>0+n z!{0*SNNYQpX-35U-;tQHLCd$#j}TFuTC*Yggs;|6vrQ*6Z5;irhoi}!6*VI&_nls~ zbA*Ugj>4XUoc!rk8%Lk=aYR)HF1EL#EZlZ_)q9?nulv0xeC-?|qBtCV($dyG4f5EN z2)>hr=@IX<`HaoJulBr$BhxBde^$#lvYn^(BH|ozM0{McbA*Ugj#%r@eKPaY8b^h^ zs>$P_IATw0cp|+&`bc6I2_p!eGcU5PyJX)xn|3e*2Yo3&(r!5Mdb()#o@?y z!uLrZM`lF!dG_Z)LY#TFwVda&ooaz7l4e8BX3Y-Ek$nz{9L1?M8%MKq za3nQbJ;B1*z-ygT6O*Ia7LL9s5al`rjbp#z`4K@0aX7M^f_7@Hh#qt1h*dV8%eI^p zcjh|t;vj;Y&3g1Dfhc%QizDqH=7et)MCMxia?Dzr{F!4LM_&zz$a{9(cINmi5o*oM zQCLLYv+EW_PF*NO?g?LKzW+Qel|b8?v%p#C)H@AMBc7*q7CB8$v-@drTJb!s(}pK} zorQRw)~Rz^okl0^v_pL5gs-zW3?lJ_-EhL!St4pJ(g|P4*?hwH>tPVV|NmvP+6#3G zx9a8offIGOBkCq76MqsB z_rJoZh+Y+mBgBEZOOv0|8S+FND@ay; zvmUW9ouho5g-{W_P9O>z*SH>;5d8^Ah%<*&_xY=cyt|<>PkUQ4A`1A;Kwe?3PMETh1t0D@wi#15I?~19}>MmBb zKMLA?_=o#%9x6xg^wpYa1t~eYF#C^GMDLD>=tAxGVLR2~y-0*#K!Mef)8U`7k!PVy zY{s_K>2#KnCTBUFv8^C!XQlftdzEvM`~?)Jll=k;_%%BhJF6jn(UhBw@Mmn1IWn#w z1xF;>Sot|5sWCrN5lxNx5&LBy*8fh8`H{-e)R>K^h^EGDL`5_;#(ovi)EN6! zL{nqzR}oE(kyb@CRYqFY7vc3e&)CmH^wm)bc&pF+@&B(elmFlL@glOnJF-0){W%}| zBaUm&DdQThueiB1G)I=F$7DT7;Ol;rYI+CBbK~#((S0d=`TI2$QI9~x*0gNqBv(iL z9i;ugon7ujWdEA6{uO404|97Nv)F(J%t85AtE?z`- zj;`==#AV30>)eNPgzi50nrdyoKos;O0^6ERkFJi1Xukmw)*Ak2CiRUUIH^I7<;#7Qsh zw3vgVNQBd3&dZn_$XT8i!(YfpgRTEXog-%h`|uvp*}%@P!M#L;6>}T-((`L@#~tG| zEv&M+j5PSO9pl`mlZQdbi|}oB(=di8s3~&cnr+|+MnKck8tjislxP0x^X%UN%W?Xn z7d1VS)cl^;8o&%bFJhY?p@cYyOy@-am$T7|=rN~Dyg9}bEVehto1E@Ah#+Tw>&Ld{ za76qpgsIl(`!x$kR6-1nAe|o(a&#F-6DFrbnzt!f~y_i^x5N#}T>femJEEt=|pP&iLV(8VHSi#?N@B z#)!x?(gy8frKf!2)Y{}v`KV8{WRG8$*Hb5N9OZeZ zuK`h1ju25Cjx2kRm7Tu9BdvWlG8YiVnT=R>`Z`yFJoPzb^x4QBfhgKYd!;tgicugm zNA}qWaulc5Y#i;)!BOtn$iA2y?X_@J=R;)DY09!s64Bk8hb`nL}z^*p^n-1 zXz_JF)5HAj8C4NYjXe>CqN$N7vS(D~Xlgw3R}oE(T^cH)sj*8#MKm?`jH-yH#+w=y z(bU*Cq9U3a`$p9MXlm>kRXLg(J6TjjQ)MR$nRqMQx% zZC1GbM_0LWE`?Nl|B*UlyEM!kGEk3jk5&|p+}{`nF64|2-|VA-Iiz=wme?7_-}uR0 zV;Ax_##xYYpJY{YT}?G>ilaw1jt=MGD0f>s5|g9D7LLTOF;Qz#`|MtU zD4HJGc8z6oaFn~x#u2LAKYRUH&jDB11L&O}Mdb()#o+)s6^6Y$PzP*9go!RD-{L zc+xT(3CodvR*M|PsWlr%qd7RrJ*zzxlcP}!N8#7Bwzihm_QV5T)WQFXNTp;KCU50aca%R(RdDya*t~h zF*zEya1?$vVry&po{jhsMQv+{C{C@}=11XYBQ}omJsa^Oipmipio=m@eiXj-+c?U1 z>-Qsy$`K-p!;!5&3g7x|9Ob+9`w>Ou2oc5MNbQfXx37b559{{nmHm#D;xQR(L82g8 zSd>IlTvA$=Oco|n$?{}HvU1w=8HF>eW>r^#-|WI3kF|mLOu_2nFSZY=(&NT7zG}rnM&6 zfLeo`N?@mMajolF3iX8lW=*d(AqakLN2z{~Ao!ORQ2pF9iXhM1czuuX*6n+Pu&l%5t1iVtm#ae-HtBd9gaV67o{(oj3hDOsZ> zNk2!JAVT%SMj;i^G@g~zBkn`*21K;20Z5M^{71EDatXo%WYh?bbU}paQ3yd?k`Ux4 z8#*>F2>%%i{3f$76#+#v2qIQNw5>rdLNeGnN09^tA@Xp<^U6gwj~9f9#sNXIu?V>p zk{8Wwv}Vmlg%k$2joZm~ha%t$6-*LCq7_YRB2)+y zgdH0zT6MMXhK>#1BVZ=n@ap#z&Cu3FSL^MwuN)g%NeWWC+RG9L$@yY@FR+oknwQnIz=rjk#VyjZ%f^v2Rpl>Vk{RoO(@N6Ma0 ztsv)9W2w7RkEXuM^CaiX=c2uQo%G?h@*B(VD}S>5rHb_x*HwIooUiy9IbS)evaj+j zl}}dweA?V;S0_5B-81cL(`QV-V*2Z+{~b9$qmZSYv6;(o?@!El{fy7f_~p#yGe?-u z%s<2X%n#4}3Er!!+;}cORa>iWsru}!#H>xTUNh?>vwl|Hu7+11tG)u}X%=|g?_s_p~{kxuz%s*DklpcK-a=&>lf7zlJoVq)&E07S;M-9SC*|V+fz1Hc4yfm z@cDGv_sV{os(?>rYH6N7J5@9$d--QgzT7WSz-^|x^I@-V(HCIuFwA^QYrLW8E_mML zYxXOju=u+p`8t(3SO zU}f!S`e+l(D*31BX>K#!S=pSnvdvBZN^^r8or{vsI(O=^n-k45S=`DO6Tf6OJ%)5m zzoNOZc|~*Av~$fnng^OasGDCgFUxhGr)hozw}rhozZqjT)A_J1P4k1kklyBx&Ul$~ zm(O3Y6&zCjr@`UmJk05boYmf%lBt@Xly=5qV=41#{#N<*!Mw_p%vS!_rufre^u|NC z(?^f+(L6-S1f>Pd#84OXfmT$HEqLsA#0{s5jn|ju{*6FSDTGRBivUN>s59)90{?=<+54R4w{w$PUY3O+C zYnYa|-qLzc>$@TTBdzCKzXJE~yZ+C$zSx#(t8S}nTimv$t*31t^KZL)&Nt`0bh_)}FyKQybGi^U@`*pfBJx9l%PD^)s zP3!)2PkKM|NgqfLpe=m>z9-ZB(`V8*($8&eRc))%=U55aRu%E7(^1>)>!(s6>YIKGrL-tGDjeFu$FgPhOPVlN?VROujk! z!Q>OkXOh1ztSP*>@XErm!n+C|E&OugVB%omzvGpEib{%FxIJ;OXo<9YZACrYr|59e znW8(39`Sr01^3sBe#B$h?nN&Y&o1uZp~aVR8{Uie6laTHS^PGzeGtB1p?=SmBui!| z4whUD-(L6*m%ORu;gTx>5|e8^3>!$r91TWr3XsSdVKEE-Kwkfe)3f5Cpi;f zf4u0C(kIjy%wGCK-LLdlJWbqt=@Tk*aqML^32xvWb5SvO$UD3%NR?Nl$||R&%He76 z%qn4~Pfw*%rSP?x%JONpAnIS4oRy*(70K+pw(81?^73*TeRe9DN>zZphDoi3TP3r` zzoINxo+`xPr3R-MZZFsf?rJq|H8ZMLLeCDVc7yeRu&(LAT`E=1g=kq>9mxd5hrd#R zW{%yUd<@0AFics%Sy54e`l4i2nkX#|(jF>9i7%}}1FI^ksvw#`C$a@gDbK6QwP5Dd zno3ClY6@yM%961p2xgNZ5p0SOPpSZ5o|wFJP$n?bk;sJGPP{Q$p+%xGh+KzQ=te1x#m-fPxsx>WLYlfsn!s{E_2P7V zQYIjM<*W#jVY8+`HtL_>SNcG(Ihl-NvgtxyeTa0UTI0DSlk+#=rrr%34Wo+(Mrmmh zS_@2_p#;?lseGPj7N?Hi94U}DOoH5UV{(_4N?5_%@J>(y368IaVk+H-cbYPPTp(~? znTyv(OM{+XabCBqINohbAJqmVNenDEuUi+bvx;~YL^HfX@V<~Ih0eBKm}Ig6_cb>N zBa#Ro2nJ6tJGxt>LW^HtMRtJ}RY+r_o`Kn=rr>sni6!#Q;k{IwC30VXKY#_}d1!Y` z;?e^*j7E(XM_bx!Aw~yAr-eu+tKq&V(UeFugRKQ5&#tYU4TJS;%%ruoHCe=-lvKlN zni`0Znm->yr&rFJ&ur*I1Dlq?AU|!?Ei<%vReH*Bl#sTfHGT_tE{@+co#%C7LtH(E$d++Z!4*5le&8n8H8` zwm@$X0&BpKPJb9?jRu2xI_AT!+)urWh^i;RmrjuuizSV4(c(&fw6#O&q5OD7XF~^C zC?Dv7e-faZ6UJ@_|4wkz)D4h9L&LIVot-GcYy<{Ra7T)UAQfmSqXvdYM1yrLKc0ll ziRmoH6TqBa`EBcq>f&%`~w6xHO5Ysgx&K2Wz|4bTxbDv!q*jCgmOFHe>9_aV_{bX*YJQM;A7oDves3W;e~!Xsl> zN4q=T=kWl3r>zMcYJ>_N8s%P7Bj1mRE;fPy)~?x=C-YC0)mVC5`eMwtis6#Y7cKIWgUbl8XD;GOL?jzHH?O&0+%mppq8tw zb;y{{>*=F1nFMl&Kz;eAxnLowxkEU_BP$vgpI})ZAJP~~O;G%_;zDT|FD@;VB#jB| zzRYeeM3_>Hl=rb1hDCk^hKZte=XA)_lAsK$nUj#OQ0ePba^~t0g%Oodbk8!aDy-P#+u>bu2u9s+@B6(Nx2?oZaE!B842k` z`QQS-HEWdam&Qsgbe|&8)g5-Pu91BlJiUS^SUm5P`b-2K89~=5f>Q8=j!M;Dzd(N^ ziv(k-@>-21g>ZaQg$yWtPz?^&3?hBJi&CiY6fZ(b*r<=|f*Z3zWR@N=;dfywRnMR@ z#4wCt$y~@3qAWBH47H}xaQZE z{@{&i+#N%ev%rGkveqzEv;o1s3ySAUmvCz_s%tjFiwD(g29v@I7`VfjsD|PQsd-_4 zr$vBzp>CQl*x*ht=*9rJV3B1U7Jye-2Q*YK8SahH1S$H8A(fR#HG8PUNT=6zic!4L zx=weu^X}I7U`P=`t(STXEbFu=YL~ACtkdAJXz)l(G0e40`f!Zc-%{U>4t9gWiAxRhir8VZzo!&4*oIXFe4l!?J*;!v;)idGuN zWg+xRYZ7hfPYqNc$_Fp3N*amgM{TTj_1b6g8KzX~Wv=0FcIUqQu7(0yS}J;J2PznR zS^~cNq#CT>bLj!vC zfZwyhty z=JbkX%a>u)O@^qOvPVb9XhxfK{5vuuM=}$mV`m1bOq$mUlYr5Q;~3bjOYa^XnHU;5 zmK~Xp>ASaU=^@IIp^2f`Q%w4C^jhY#Q{EuhHhiC3T-Sv!_7E4-Hd{J-WK}j2s=h zVQ}Z^li4w*uiN!9Z12Tfx9`NVPX&? z(6D|W{F>7%mo4wcYZvPCPJpWF$Ij7_acmZ-Jsl8w6~@`4<);*67if%{de^D!h(Wmy zgOI*mnvlNH@d*zreY$Ev^Yuiy0&u9gT>;&Tt1ca^g&uZw8S>k$_cFV)$5z4V{T9A^Hy3bO8`|IEol zqrIx7@_;``r7sn=sZ56MaL6C-#hF z#>a<_jbx9|*x;KTIRd>thVf=|QwyZ;9>G-I&=aVY2%H}p8i8JJa7SkB1hg-}FaQ

?M&_petxZ>5rOFymGU_nYN!QLntkP?n`umzTUEb8a z8@eaxg`j@0qJYJ&ombKrwg_FlgIjj>_U-~D^$Gxy-Mug@HEq7CXKD>g$ktdZ6iwJur$lZQBoB7Ie z-6CL_Zd|@>*-cc8h`d1xbwi=BP*zCQ?hU#YJQsUC(JiEb()R&tj51e>@T19v3({S} z@k*I2F$4*b*jm&qSi8Yy1GUrN*WSsh4IF|U8nzw$dF3%Ai`P*Ev^seS_iaZyWNuJ1 zEEZa@nt+C-IF2(5#Uf)P7E~Fmv;#1z0%co!-SlA5C3k~|!Yw!%pXjea=?~?7YU|cs zTIh5ecCmd!H?@E#qXERF7cY&K3K{ieeyueFTZlo3MhOLcR05{;2(^qg-it+J4VJZ5 zrKy3^-IyIcN(TYaP9`smR>$PU1QyZCVVqPZex-7IOJdJDavDR%M)Is#Mu~06tf@OQ zvSoC5c=URlAZz_B?fOuIu(#k{KGPtzXJ>;I9Se3sPX(!Z`mqyJ1Y_4v`z{(t+b7H$ zERhDZ`s)CJ2SB1L7}E}@qPg};$v9U*QXo^U-$o&nEVQ&iiOxc!NK-J-x?zlhh2#q1 z+_VDwoaM4_1aj0qi1w7&Yhxs(-*@eZ+9QC9zG9Dy0W2s`$)H6Jogf@KB=2Z;))@a>X)bkkz^EfK~i~b=S7RxONtv9k5zwii?Ax*}r=eMFIL`4wr+AF+9!) z18QTG6)-Fq<3s7C=BjmtC9-rSw!D==ev*SbQ+x0&+Sby0d>3b)4R+RekJw3l14y5;Dhnpl-n_YrKW*Ef}qO~2U4;o4yhRZ zng^#siGwNV^byO^)qfTqeV!fo;k|D-`;HEnKW>LXD!+2o`Du8FfP_y5HnX04^A5uU5y}OGEM12W$^Q#F CQ{{O8 diff --git a/icu4j/main/core/src/main/resources/com/ibm/icu/impl/data/icudata/brkitr/line_phrase_cj.brk b/icu4j/main/core/src/main/resources/com/ibm/icu/impl/data/icudata/brkitr/line_phrase_cj.brk index b9f1fa48e7d7b582af05b8c5a447e548dbef7eef..3920cf5e5dbacf1429d2c3ddbca66619fe9bbbf0 100644 GIT binary patch literal 78416 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 literal 78272 zcmeHw31C#mnPxq8>ugCJLI{BXNwiK70%02)%pqh>3y{Is#u$qR1SklJ2r;%}?AV#y zwqqMRiN`aUNt}D_$pKC#lQ_n4Jl;%B+sW+a*v*}tnQWZQj+tZ>oZbGiJ=vZM86_&pJa&_kBuMhKEShCd}QL-NF(I_@pmHz;w@bQ z-|OKsK*)1nAmsU3gp~d}A>W!xNbgkQ&4%!qzlLWc!~s8>@lHUPl43P4>+sz^1N0_B`Wrjr_|v04b5N$SWfQV;QF)6X3EZXk2XJTf0_ zjiiY*lLaJAT1YEtBkiPvbke_tWD)5ii%B}E#{94E|auIw({Razq^MOJ`=*BAP)l!1z^ zLD(C8o2)PN==6P~9x9&8W9Uy!r2edmtv_6!5!>bPQT2!8T^+!qws+aS)8b7A-!o<&J3!Z*~hmWVOc(&$*r{98STMRtW zRtkyZ!TApwjtAS<4huiK1sUct|e5&h3##Q230-20!smipQ!@bLC7 z=g(^YV;@g5vG9<<{CS@R&wg@1ng8hh^FdKxQT^c%NN}G&@7M6i+3KMHKd5WCD$@1& zF!&sS500mpw=@l87$ii`pBMVkRee4Ler$b#i%n@>6$=mJ2UD^2j!fS`j^>2tRUpFD zzD6AJ5W4-Rqi)K|IBo2oz2^NpKA^%WdX`FxDm367_KqV+#KgpivBo?Ga6!9!F$ ze7?cM^#12HAc2h+EIi(LdNV{e>GQ1uPp;zyN^l>)Zq@ja{qw019xP>Wy~Fxvx&OnM zVC$XRKmzZdc`A&@=sXu=z@I>h|*B>TXudmk&JZ%0PUZ0xg z&wr}%!x0nmhFEymcp=vp>iG4>obbHCg6B;EJcu-`&r##on*|d*ZlJf`&yt1sEUB>N5Iw7}!i-tX7&NO=!{ zRqppz8(tTL8NZ| z;h!1!VdXW?=UIRF5JWNY^RNLAN{g;9mLGmMkU!6ghsVbH`U~IjFg#6o-W9;3)|a}U zfbW~k?dvZEo_Otx?LWUut1sE!|0)(9+1|tJ<##*cVf%-FWx?|vM?B&6@*_Fnd5?yN z*C-+H4dCG+Tz~L=LG*g(eF6_lEBha=&s?VZd@neH`tyG9`#=DXS${qlu|I|q+~=zw zu<#TA{pp89@$~U4KkrXbIP&x1Sp58#2!2q4J3k+e$&b0d$^g%YEb9y8#~sgd1rLs( zoS#QS{5(eRkLxqvpMQ*=2Vh~PGzPObkiP~AkHK2I(f4KZc?{+H{21ho?ruK@uHPTk z@K89kzVIz=_VN8A5KE5zEH(RX5bt>#eQ(*m(8D&qt8a(Z@jd?gT$?_x0)F^<84*Z4 zoFDW>qmJ*k_qjnl?)rmx-1X;jrM^^tqTUzC@eAJ<*z_4CxZ|q1iu9UDGr_lfy2-6r200snqrr=Et=DiW)EQe1dy@P5yj>O`kUa9@!tpU7y;{ zCxp*SxblN|-1)KLiGN-~)n|2oKELNB&Oqr{e;C})JWC!I?+39z{Dft`@k#PGIq-AF z(%v@@Oc|oeh`m4Ka)RiW1Ao5_qx^ID$?;E8u$>(drIPXzD~dVbyo&u2q;7>n|}gz0_d6A;NXUOXxA@b`n*-erC9 z^}GGN`ndp}@cPt*=Sd5mxcY2gU!Xp#?Tgh{w)+M8`8v6u`BW@^4nt{ICzvumec-;F##G}?1Ywy|WYqzC+JuUd*?Oo?5`~K?Z zMSZc9;r_>Tp5$o@o@cV)$$lT??{dQPj0MkLfhXH}NE|Or=YLRwdwto?Lq2Q8bC zvA)3jM7H(C7s%h|fah5YKVKAhz7(nl0c*Ox8)e09=zzLpc7uUhcLtuOod zj`iiUW7yi8Q3jsXl@s#gBf8B7u5+yKxzCr$h{5SI7 z$v5f!%5RZxlkd>K|3UsIy8tOVdD(<;0Keym}CiJ3olWou3g4Kk?7cbAC`))bodvKR++oCiGOk>UOhZ&5gMw z@nfs0)b{=&u+8=p*!+hJ53hG{JY|NRk7D))@^gd!WIrGEA|yAhzk>A@SAUMfc`bJT z9qY?oe?~3(Gj8A~JbslyX;l4T+~9Y9?mI|%K|FE!!8&97;Qb4Ce#RAkus2D4tXEs-~8}jYVm%hxnw%krnay6_b+Vva}zBc zp5I_5-g?<~|00aXb-jal-1WzXC;oj98$YiRcsM^kQDXkX-Rk)b+kFs>%lbpSpCV1v z`z9F++dG>1@M%N~7`b1EuBi7-7>-8OpCCW(?OpQYG9SBHY40kY`1fUO^Pha&myzp> zc=u)4`awUxvxU?=T))`?AfSZxj0LQeXQ09m&t_AT2n)hsQ6z{$k}&^}d1R z_x?^0k6oW_{2(6p{^xd!KHnku3BG?O$)X%fs37)3;SP+kUeIXuqe(u!jOY#%%eiLu+tfaa==l6b7JUmhBWyIso z&*bl0srz;6dgqNof0`(L;rWK?{+I2(Roweu!Tl&XpGQ3I{MhiszyD>MZ{*|t7jJ=t z1o7D0mu);nJnsCw$hmsenEK}+9(R5ak2^niS@^jp20t~C z{JbqTKk9s5zpp2m3*vF-2l2S`bB~3edjtHK?+-?eU-`H{DESHFah(l(+uY4e)zW!ajUyeGj z^&t6m4*WczwJ%x1`1d^}KNs@8XAqCQ|FMnlc>mO0f8K84=baJtrQZj!ji)HV9na+N zpQ`mG>$`#c8Q_Ut@0jKrw)>~S`>^iyg?QZgdB{><4+rXtmt381u=~q4e)4-CHi*Za zAH)-%pWpbu$M~?q57suC_rvGq06(t~_Q&~o#sB@fSKvMx4=WY6SD-)qEl79;BsTiK zX%Wijt2|w|2zdqaeFf%YEJWQOdshIDjUW4cqQ3+QF5{Q!zA&=GOk6y{^_P5KfOz89 z7ymtB{r<8X-{ak1mg9T)eeSy*`7zyJ=Ijs>4^Pzij(FVp(eG1Be%=Gp)b*EHe2Sk`q6Zzu#@^58o&BhiO50eJZs>+TIJ+7T>A1_xHxCuc-MC;&IoX_bUCN zibo>;WiO` zGMx9L5}ffsDv5{RWk-G<<#_n-^Lt7B)AoO0J_bHzWTqch%APbHy^jx}FyHV)>HaDW z^XD57x`?{F{TRXf_({M~h{ni|^8XiL`{MThBfs{s%Jiig|r`5$Aio z(unqj65REtSkWH^5BVs^L$cj}x3w>n;EV_ImHMpU@ye`t&U)oy{T^}u)T{7P?syBlwBXANpGt(B6Gv`hM>xL4wPCp3PVF`?;*e z<9JaS!oz;&gRgh^{jbO9c)`N7-vjZgK!Wq{fk3{pz7&4E>JT1Af{G{d_jbG~f*+PL z{JsgtD%C)L2>FKbAU{(>cntf8k?kENIJYmzH;e}{$JHNue~9|yj0f^H;_;@1_+iZQ z`pSA=#+wcj^!=6?zXuEX8u5_$_ZhK2WJYgae=G1b(crNDnBG^~?lYS41m}lQ^LfPM zUSBpmpAOX*>a*!Sqm7@>1o)ZU{Yo>Q%FumJ`}`2`sQkpcKREgOo;H3y8{)@TGjV=Q z-#=ubw)>vJ`=@;U9=s20=Lhk)^YetIeLV@%*nR>(Pr~#^ZST?hhvx$PgxAX^er)$o zgLpVU91kDA?ED}el^@pLv)yNWQsbwXS21}i7C&sig(uScpU>sQ&r=|*#TTfIry)Fc zer)^mh{v6ur!;{9mhxwRY ze-Mv5KQ=tiWZ_4Sr#61_d;c_y$CV$%>&^4#dyQTb-aqx4y#-#H{mOE!~3w_JiLGE&GA~jCa>M=fY{1?Sg$h< z9OvacYtjF!2e%~isyDGeh@3pEu_iEeFypLHiXkehS zQ2Du2;Nj~*R(7-gurQsU*MS6m|A({3&r5K(dOq_`@Z{?q6;HhL^Gu)3^TXE*JTCh` z>iKyUPw>1kmc`z_SV?t$@Vs#}Kl*w(eEwO*6FlF`a@5=Vh5r9IR6N1+v?e?k`v2ol z@!Tu)na_U^(I58z<52O$J1-ig&!>g{xahNbUR3RWqR#vMq5i)Km7jR$Z6rSz;{1<_ zC*JuMiRZ$cw_&Hi;r)ff?}5O4!&?-dkC7jN{;>I2m$%sKCTVX8osTW$`p{T}ak*dl17$o_`6-0?kBX-d=0EJ$ZP>_A z2=gD6pF)^#sCWutzMb55V!@c{cU;%<&B+8XVt4=j;6clP0J?7xMqRQTZ7V z{IK-}1qxq5Ja&Gr3h~2bM6H(xAd;!Rb_+aF>vwElHvPFiCp_x^N6-1e`YMF~4~XMI zUT+or@b#c=KiqPjmP&BnZ&Ck0--I%ME`{11R9{$&vVCR!Jz;No zNS|%}50gyK*FnC<_bJ|`S@2|k|8-eTcrMlOupK36U*3uU9)1^z_doJ})1~5el6Jqz zTL}_k-*56RXHp0G7(nFZQx7qTfr00Khw0Z zKIJm_`DgC#<{=ROW%#uCDHuBxQbBxieKzm|Bb}}PDYNYN{uUT^?|)v_^heV2J5gW1 z2hOAJJJ{wwD8U_%>AnM?@>W~%ob}d-_osOO!uI}jt=H?02l5TKcWhro;E5P7==&xM zKU9JT@{j1`H6qu!B$^+z3<>{67>~qUuV6|qP{*7QD0lU zD|4u?O-g;K{PYVvTO-<6)O`??;I7a87CiF)MUWr$_s3A<%3!=;Mjiheyb8d>)`Lwn zID8(-aQ<1lf1&cTjq}6rH~D$;`9}8n*mi-3B@N>-ocHs#O_Cq$`6$Yddwr?*Q&fI} z>%ri9Is5!DydI1*U)BGABGv=p_*EIKFaOS%T3_rw8nkymOuLVU_d(q6qd~sm@ym$E zI{&ftKY%BP_GQ8Y`sZ*zV{%y?|`LmNL+a^h$5_hoE&@_Jv!;eLv( zzVdoM#em1Pz7UUl|6{o?<8Xe}#!p_)uNv^U@`HHX`LUd*_2QpjwegeJ^Q#6tuKXY# zcYZAAX_fPq>85y-lwQ|3gP^Qil-3HZ>arGA)JR) z`6-0+Br2Z5I8P$ZL&Cj`;QY`qf9Cu1rt^^UK8V*9|2(AEsho%8`#-q9;e^L@{!rp^ zIZp!l%Ket|0-OJ+cnV?uqv9!q`48VC#A#3=oQG8TDTMikil-3f8!Dbc7*AC^g)p9~ zcnV?sQt=eV_$3xR4(D5J`%(El-x5A=V;E1Pem{Ypw{hR!nEZJg8$bCtZzJm~eE!Fk zAH?I%kAD6~oVN*{|KTTM4Cie=B*L`wHhBKW;k?Z?zLXWpc^h^96FiS(Ki^_|Ux4S4 z;`8JG{xk3(=Rc+X8yc981Uq+%ck=-i09hSe4f$8>nr>F|k0j1Zn8yqI`Hc-wBKP+x>Uktpp9eWV0*`C^+9&Y1w6DQQ@?&pb$d9}J z*!an0;U|0h+Mg3Y84Ew+e3Ym!SN+){@VNBPw)0U3vhb6=e?BPixaiM;N%CXYALPef ze{B2=W#K2g{@@!c*4|zC8M5#b|2&(mf6niDw!@qsFL=K0tan88&v?Gh);|w>ugamm z4qNz%f1b^@zR2%+wlE%Zd$<06orieb_1Sj5F8+Bo8$bCt&&FB;e0yO$uKXY#cYgHq zbrMhf^9eS7@_RlZjK`H9#N*D7?Yu<%^9eS7@_RlZjK`H9#N*D7dS0TUz}Bbg{4oCd z%T{0cU4Mn~xYifqaj!4idNBU_%f?TB*I!{guKXY#cYbW^L9zbIdA_Ez$3_Gm*6YXHPdI9sZ^Y%tK3_$C-0RE6&#^50WS_5&=ETo23qSGKr?&Q$-}Px2 zk8ArvJnr>nTQA37pW67z@A@>1$CV$%(ekESAGzWJ3qGda{TqF zji3CkPs4az`9VDH{MgpZVttzP_%#f8+{Z85`g9DWO>X=;F7UXFUt^a3IW9l;@eBEJ zuP+-vug=0x_VH^xCw^XS;V1rl!`8m?JKqT7acy6S$GyI6^Pl+h4I4lCoo|HkxblN| z-1)K1f8w`y8$bDN?_oTy{2(58er)|u{Pu3+C%^4IjK`H9#N*D7+W)YAy#XGZn3aCB z-%L_GCSff~6eWvGl4wfH$}5t|;$$jWnXF1yPnkNcczVr@+8XeiS)44ZtxJ~9s;w<9 zP8OG!)>l>5R?RLgO_mnVnWNsCoCFk}R4X2L@Fzx6?K}}Ys{%nuJpG%8a^)h282CXs z`QVx1A-uleM~Z2E2_~SvAfqxkf?7I@d6q*B;eTYM*O!n3zw)Bgdq58SlsMG8@9823 z^aoSbLYWZ{@%4vFLOCJ*5qLlk2{404D~P82^ba-#6)Jo6G{<5|YzJcsen zI8a|RQQ@-cA(!K~Q?ov!9&&@npzS>S%4KL@yk9l-&sUERFC#^%?VbA*`^dRc z+{s@{UQ5me{(j{xhqtS~ulGS_^S)a&ujt022gtdiXNrE8SeDqDxF_+I#LLNr$xQNH z$?p`;F1`*v55eb~#j{IlO0Fuox8!T3Ri*u*BQ{O8lt-Z1U+ z(_Wgsbowasnf@2}oc^)t|Afz)8b6-TM9s#UH`RQ8MqK50H(!Yo5zBy~wtX;EC&w8OgG3%xJxwC#$ zzqdY7e^1?L{p0XV`0-fm|M1`S=l|a-&wrNYKRN@mFTz$9{WtsY?056FJp21|Dyp8J zvx&Rsym8LCIlphXtl^f1GY$Vbw`cD7+%t3kW!@4NKJW0n56=7OyuX=uZr-Ew&d&SH zf4cI@#7pxR@p}0G{VChw{1F&C&dh&o{?AlS=D&ij#>*Q|HGT@7KW<7l9cX&6>FX8C zD|S_kSKMFmark?(;yV>Dr>fwuI<+{@f7?_vCVS=QO}^YOQN(Sgr}wd-Z^_@o++l?I zme&PC(Oq=j@L6MduN5^KMgk zvz)6f2$0+ZOCyaCE_~3-0nBSn%fy z9-Z>=f=?`XX2CbAs#$!mXTgsbDBQzL^Fe!Ec89h$=>**iD?2*m| z)4xdnKEcY`)iR}Jo{ZVDSlYujRyMD_Vtvahw6|;p4lhpr$h%*U+j2w8Q5LuQmBdTT zrpJ)3saLh!+HzM5)MLvBS{`o+plN^E55rfZmVd;UE&mo3zvcI#(Eipb z(^h+@L;fHu&R^x5v&Dir{jj$@SW_}p>wIZvEH>|FKCRuAH%0R*Q!-oSCrt6D(!Mkp z7yj?TOF);Gx2|C=2E$vow+=?nEnedJ%w^RSZXs$UUA?B;GAlVePT?a|iT zq17_Koek+7Gsmwx4W&ef#P5N7_H;`=6cNRsZ7b?)InK zpMzX}+5Ww@#qGbKm>0Ld+_t!*l*Ox0bWmOEXhwJYeH}d=>pFIH?C+rCkSs&T>lFLx zwlf`%bev&69iQxY9Bmz+gwL}bXF6W!_y+y^ar;R7JsmHx613mV^6M;WKioMD?NVOb z?QKgs=XX*B^@+}OU79ZVQ#jaNfsyCH5Vxdo?UEySPzb>vTzNGl-;_>3s#b=7YlGvBnSNtM=^S_d^l2&d{>?>I$?Lk|~X6{pR zpyZa4`%69^_&Wpc7fOD@W7(c1zbu_u+Qmalui!R(mhLJYDt%4qJHYl4_=O9&!)K)I?y|?qzFhXR^2+i>nEk70TIjy@@SNXfhbLCHP zCcyqw$;Zo|R%0-G`44r!@?Y^ZxjxIER+-DiUQw6e23{tYlyZlB!mEN*WmT%8dP=Gi zc6+DS2s3qRDwQgSkIht9PO$}1|LWw76wRnaW*4;8R##P4R?_G*Q^{1S3hZ@E>MVFv zGi&bOD9e|p3Nd)8!6}A22-d+}tH!NmM)gYQ*&)?_upSWCH5Ir^r7F1)Ei0=dnSl84 zuUw$1w;Pm?q4*SrDGNBOs;W?5l&s1V<>gV@LuDxOrB!HPO;t?|L=)&lwqPmedDZw9 z%$!&|Ra&EX(JQsfKd9x_EDkxbe-M;SR9!7z$Y?trS$+PbAYTDJ3YGo?=zvurgCIYW^}Q1ZnxRP8P!blr&r>l{OkhOR5M(tAZjB zlHJL&!Eg9TLDEJ=$YKkLK733Sh%BOHv+zno35pm^9y&s~i_rp7346BO0^vDju9Z}Q z9FU#W=@*z{P>ZhBrLpMHvoq;P+9V83j3q!Q6auwUi7MZEE~_2Qn$@eU@P~$d`|{KWk3juo_G{LxPVtl}4oNL1#?O|09hg*IU2R=mvKGeD z^72|b9pe!Bu z#p(E@Oho$X84i-+Xl6Jz>R;Gb`arNbnT%qx=|X*daC%U!34A4!b64Z(-PLOhqe}or zd3h3A3rwA%1homNe1T{dr;gtoDUvr#g4}Xra+jA&Skc_@PEZ1gj<1JeD&2>7nlgV} zAaGxqOVCD3gPuWgLAR_p-fc@C)dnO<3@o>xTNkafig@O^8D1gyoX?X&XWJ-DGTDTu zn45$VNrVpsgJ+N(-7Qj~C9JO^yGV;Fq`6to!0b{}a681r5{2gQL8{FXxo@~1z=H8S zv?nI<>467EqehFPE$y`sqXVPULL`&5@LZ5cClW1SYX!+OXI0OH!FncU($?0NEMYq( zwXm9|1|p>9&c)EF)idTY8@kZINo6p|PdjzX%&C)q8G2gSa6wa5*}*LA8~0(r5C3UK z%C*>dPX3dxaujc|^icfFR@)NS92Eci=)z* z!axePz+eypYr>H3a2RHd27`Gz=EJ_ZpZXLLRZoB~ogyt3OB&&##g+bO>x9xn`SBZ_ zO1TSfSbb(O_nhOqClZ zW6@#)2@(|*GiS}51r-TjGiPBhh9;G)hKSJNVXx>{EBcq?f&%`bX>FwuA*OF&j!J{d z%d4lbzd&XtWNWP^Wetf4P_u}L7Ar`VrQ16LX6d3uot?d%YZor!f1RD&*0T@-MSiT? zrpXhX=-t}`4K&remj8*E0ZVVdtvOj@>gC7l?ZpwZr>D1fZ4Vc15n=Hb8JL7)Ajs}r zOL+;f5#@v~Y;s0aL?>jg87`N{;O@9jQQ&x%xMEp1pZm8lA5faO2IzIyBt+sN^r7r9(OLyhQ?5WD1KUTv9ydAmljHr#sseGvzrSMrW7L;+;77$=e;#d6t5TOvFKRL z9rTGZtOkyd2t!}Q23)Cn@MZsah*egsfEkl|`HnpouW|ldv}jRJ&$49;7hS{v)WQZA z{D}u04EQ7lpPewJ@kfY7or_YK*q`Kxaig>2Vr797t6;%M?Wtv*D28SR`E*_)Ob?EF z%M=e0;#(F%^en+J8H0a4YsJi=N2~OZgXYQ0XL|C{)%b+MUEBewIyzGR1V9!S>R~y! z-~k`%jWxr=eXHnuxIZ1nl5(NV+;X0cG7{2>^1%gumtLxLzcf~2zP~+1myOuH`bPF~ zuq_5#V?6Jay4eDIQP4GtpcHKVQK{N{9q5l_kzg!U-q+Ej5RO}5$bixZ)!|^xAkuwf zltP84co9;TnwScFB;Ium=w%G0K@MSho!p|D?zH1LKKxH%IhbrLwT^TTIJ3F-~ zYL~ACtkB@GXt39&80K0ceKeur7kj|>k|huk|8>LuBl_~l_(TW&baq~jhNhBV zu$e~%fDAM;fZCX+AAo@vMMeum@9M-qG&I0RNIl>Sp{XlbtVU)8X2cKMi};0GskqaK z-_(f6;8K$LXedzT4ck%v20KNfl!?J*;!v;;idGuNWg+yN)+E}{pBkt@ln-85l{6B~ zkJ?!6>b1|}Gfb(}D&KH7dvag?R6_wREfxLh1S%MOS^S0GuWX3ZzpkEIdIesLwe`sR(01X__18J(k zf$^dJnG-`a!cJ3!ofDby2@GD>s|8bBgT03UHHL25v}(@{CwrDGU4l{98KSNmIy5#; zGg_zP-<9E7b*uh+}4ejWAPswG>$m3m&Md)|CtBy-FMwKBa? zukno|Cn>|5bni_=`^SfF8tfmM;I*||6T0b^<41-lu1z}en|ct!re%8UW$3y23e9u# z!J)Cky?=zl+pKHa=FvmLHxF(*d2DE$rQV{$-_nm6Zn;Ji zx#b$_dA06&HG1}M&^)mf4Q?13I*4K0bkW;}Zk`y#2s8{}scNuoZ1e#xGc7T;Kq#BXHHD?o*2%ILhp9`)}hR* z4Y#r9wf=L{Z5Zq3iGkq*6T3z;Cr%6>9vwPJV}tL|=t1b(F^o5k+gc%g-zcW)gXXSQ zB5=Nccoe#_!L6C`qo8ttVGmSHE5zCg@ZlReaxj<_ins?Jd%N~vH1ykqAXmUQ;=VF7 zkvWlSF?#guE4(N!Wzk&L}iUJmUw_QzR*dp}y4{q4r-@hG{ zG$;T>_VvTqlU{#adQ*RT^Un0n-Ovi4|KF3|KF}rgbZ`2qT`*LqH|05NTGfx6c)+~sXBu} zH-hKFpeMS8G*J2hV2x4cOA&rFxo|yu{k4cYcWV*B$s(fvcF<42 z4NO8gg<~OjE(FE%)q2g!b|vf-YA$N)#(rApj&|%~2ZnEJ1y4o;i0fFmIHy#|Xdv?& ztr^%t3_>(YDBwd9Fs({&afF=T8c&zhx6Y(r-0zRc)`v5}Fnn{XUtnFh;>@u}3)9^w=&32 zQj%j=Wh&SR;z z(9{rX?;7Zdc2mXJL?=1{G@$Z+3kOTPKh*_z{Y>Y!@%9fE)+=^ukzh@}gB5D?6{lct z20mLmK#t;~c@Jy{&w(AMU^-0&>q0aa6X(a}{BUQxCZ%HyxVH}=n^Y>evHb#h9cutB zbef<&U^xX8oPv;!4scRGzcgjzHo z09`y{nW;S7b)g`3Z0_E(M_);a5k4@yLx-)7@X(9HNCQnrNJ4>-`_?X)KW>3RD!+2o f`DuKJfP_y5*0Y{_{Z_+<2;~Ao7O%vea4`QDbdIbr From ed3d6d549678de375c68a767f2cf67d7042ac906 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Thu, 12 Dec 2024 19:32:19 +0100 Subject: [PATCH 08/25] Somehow it compiles --- .../ibm/icu/dev/test/rbbi/RBBITestMonkey.java | 1078 +++-------------- .../icu/dev/test/rbbi/SegmentationRule.java | 2 +- 2 files changed, 195 insertions(+), 885 deletions(-) diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java index 87c1c372cc64..564ed350acb3 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java @@ -23,6 +23,8 @@ // The new monkey test is class RBBIMonkeyTest. import com.ibm.icu.dev.test.CoreTestFmwk; +import com.ibm.icu.dev.test.rbbi.SegmentationRule.BreakContext; +import com.ibm.icu.dev.test.rbbi.SegmentationRule.Resolution; import com.ibm.icu.lang.UCharacter; import com.ibm.icu.lang.UProperty; import com.ibm.icu.text.BreakIterator; @@ -676,166 +678,15 @@ int next(int prevPos) { static class RBBILineMonkey extends RBBIMonkeyKind { - // UnicodeSets for each of the Line Breaking character classes. - // Order matches that of Unicode UAX 14, Table 1, which makes it a little easier - // to verify that they are all accounted for. - - // XUnicodeSet is like UnicodeSet, except that the method contains(int codePoint) does not - // throw exceptions on out-of-range codePoints. This matches ICU4C behavior. - // The LineMonkey test (ported from ICU4C) relies on this behavior, it uses a value of -1 - // to represent a non-codepoint that is not included in any of the property sets. - // This happens for rule 30a. - class XUnicodeSet extends UnicodeSet { - XUnicodeSet(String pattern) { super(pattern); } - XUnicodeSet() { super(); } - @Override - public boolean contains(int codePoint) { - return codePoint < UnicodeSet.MIN_VALUE || codePoint > UnicodeSet.MAX_VALUE ? - false : super.contains(codePoint); - } - } - - // Declare these variables as XUnicodeSet, not merely as UnicodeSet, - // so that when we copy a new declaration from C++ (where only UnicodeSet exists), - // the missing 'X' prefix is visible; - // and when the prefix is there and we copy a new initializer we get a compiler error. - // (Otherwise we rely on the caller catching the IAE from using codePoint=-1 - // and failing with a message that tells us what to do.) - XUnicodeSet fBK; - XUnicodeSet fCR; - XUnicodeSet fLF; - XUnicodeSet fCM; - XUnicodeSet fNL; - XUnicodeSet fSG; - XUnicodeSet fWJ; - XUnicodeSet fZW; - XUnicodeSet fGL; - XUnicodeSet fSP; - XUnicodeSet fB2; - XUnicodeSet fBA; - XUnicodeSet fBB; - XUnicodeSet fHH; - XUnicodeSet fHY; - XUnicodeSet fCB; - XUnicodeSet fCL; - XUnicodeSet fCP; - XUnicodeSet fEX; - XUnicodeSet fIN; - XUnicodeSet fNS; - XUnicodeSet fOP; - XUnicodeSet fQU; - XUnicodeSet fIS; - XUnicodeSet fNU; - XUnicodeSet fPO; - XUnicodeSet fPR; - XUnicodeSet fSY; - XUnicodeSet fAI; - XUnicodeSet fAL; - XUnicodeSet fCJ; - XUnicodeSet fH2; - XUnicodeSet fH3; - XUnicodeSet fHL; - XUnicodeSet fID; - XUnicodeSet fJL; - XUnicodeSet fJV; - XUnicodeSet fJT; - XUnicodeSet fRI; - XUnicodeSet fXX; - XUnicodeSet fEB; - XUnicodeSet fEM; - XUnicodeSet fZWJ; - XUnicodeSet fOP30; - XUnicodeSet fCP30; - XUnicodeSet fExtPictUnassigned; - XUnicodeSet fAK; - XUnicodeSet fAP; - XUnicodeSet fAS; - XUnicodeSet fVF; - XUnicodeSet fVI; - XUnicodeSet fPi; - XUnicodeSet fPf; - XUnicodeSet feaFWH; + List rules; + SegmentationRule.BreakContext[] resolved; StringBuffer fText; - int fOrigPositions; RBBILineMonkey() { fCharProperty = UProperty.LINE_BREAK; - fBK = new XUnicodeSet("[\\p{Line_Break=BK}]"); - fCR = new XUnicodeSet("[\\p{Line_break=CR}]"); - fLF = new XUnicodeSet("[\\p{Line_break=LF}]"); - fCM = new XUnicodeSet("[\\p{Line_break=CM}]"); - fNL = new XUnicodeSet("[\\p{Line_break=NL}]"); - fSG = new XUnicodeSet("[\\ud800-\\udfff]"); - fWJ = new XUnicodeSet("[\\p{Line_break=WJ}]"); - fZW = new XUnicodeSet("[\\p{Line_break=ZW}]"); - fGL = new XUnicodeSet("[\\p{Line_break=GL}]"); - fSP = new XUnicodeSet("[\\p{Line_break=SP}]"); - fB2 = new XUnicodeSet("[\\p{Line_break=B2}]"); - fBA = new XUnicodeSet("[\\p{Line_break=BA}]"); - fBB = new XUnicodeSet("[\\p{Line_break=BB}]"); - fHH = new XUnicodeSet(); - fHY = new XUnicodeSet("[\\p{Line_break=HY}]"); - fCB = new XUnicodeSet("[\\p{Line_break=CB}]"); - fCL = new XUnicodeSet("[\\p{Line_break=CL}]"); - fCP = new XUnicodeSet("[\\p{Line_break=CP}]"); - fEX = new XUnicodeSet("[\\p{Line_break=EX}]"); - fIN = new XUnicodeSet("[\\p{Line_break=IN}]"); - fNS = new XUnicodeSet("[\\p{Line_break=NS}]"); - fOP = new XUnicodeSet("[\\p{Line_break=OP}]"); - fQU = new XUnicodeSet("[\\p{Line_break=QU}]"); - fIS = new XUnicodeSet("[\\p{Line_break=IS}]"); - fNU = new XUnicodeSet("[\\p{Line_break=NU}]"); - fPO = new XUnicodeSet("[\\p{Line_break=PO}]"); - fPR = new XUnicodeSet("[\\p{Line_break=PR}]"); - fSY = new XUnicodeSet("[\\p{Line_break=SY}]"); - fAI = new XUnicodeSet("[\\p{Line_break=AI}]"); - fAL = new XUnicodeSet("[\\p{Line_break=AL}]"); - fCJ = new XUnicodeSet("[\\p{Line_break=CJ}]"); - fH2 = new XUnicodeSet("[\\p{Line_break=H2}]"); - fH3 = new XUnicodeSet("[\\p{Line_break=H3}]"); - fHL = new XUnicodeSet("[\\p{Line_break=HL}]"); - fID = new XUnicodeSet("[\\p{Line_break=ID}]"); - fJL = new XUnicodeSet("[\\p{Line_break=JL}]"); - fJV = new XUnicodeSet("[\\p{Line_break=JV}]"); - fJT = new XUnicodeSet("[\\p{Line_break=JT}]"); - fRI = new XUnicodeSet("[\\p{Line_break=RI}]"); - fXX = new XUnicodeSet("[\\p{Line_break=XX}]"); - fEB = new XUnicodeSet("[\\p{Line_break=EB}]"); - fEM = new XUnicodeSet("[\\p{Line_break=EM}]"); - fZWJ = new XUnicodeSet("[\\p{Line_break=ZWJ}]"); - fOP30 = new XUnicodeSet("[\\p{Line_break=OP}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"); - fCP30 = new XUnicodeSet("[\\p{Line_break=CP}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"); - fExtPictUnassigned = new XUnicodeSet("[\\p{Extended_Pictographic}&\\p{Cn}]"); - fAK = new XUnicodeSet("[\\p{Line_Break=AK}]"); - fAP = new XUnicodeSet("[\\p{Line_Break=AP}]"); - fAS = new XUnicodeSet("[\\p{Line_Break=AS}]"); - fVF = new XUnicodeSet("[\\p{Line_Break=VF}]"); - fVI = new XUnicodeSet("[\\p{Line_Break=VI}]"); - - fPi = new XUnicodeSet("[\\p{Pi}]"); - fPf = new XUnicodeSet("[\\p{Pf}]"); - - feaFWH = new XUnicodeSet("[\\p{ea=F}\\p{ea=W}\\p{ea=H}]"); - - // Remove dictionary characters. - // The monkey test reference implementation of line break does not replicate the dictionary behavior, - // so dictionary characters are omitted from the monkey test data. - @SuppressWarnings("unused") - UnicodeSet dictionarySet = new UnicodeSet( - "[[:LineBreak = Complex_Context:] & [[:Script = Thai:][:Script = Lao:][:Script = Khmer:] [:script = Myanmar:]]]"); - - fAL.addAll(fXX); // Default behavior for XX is identical to AL - fAL.addAll(fAI); // Default behavior for AI is identical to AL - fAL.addAll(fSG); // Default behavior for SG (unpaired surrogates) is AL - - fNS.addAll(fCJ); // Default behavior for CJ is identical to NS. - fCM.addAll(fZWJ); // ZWJ behaves as a CM. - - fHH.add('\u2010'); // Hyphen, '‐' - class NamedSet { String name; UnicodeSet set; @@ -847,40 +698,180 @@ class NamedSet { this(name, new UnicodeSet(pattern)); } }; - - final List interestingSets = new ArrayList<>(); - interestingSets.add(new NamedSet("eastAsian", "[\\p{ea=F}\\p{ea=W}\\p{ea=H}]")); - interestingSets.add(new NamedSet("Pi", "\\p{Pi}")); - interestingSets.add(new NamedSet("Pf", "\\p{Pf}")); - interestingSets.add(new NamedSet("DOTTEDC.", "[◌]")); - interestingSets.add(new NamedSet("HYPHEN", "[\\u2010]")); - interestingSets.add(new NamedSet("ExtPictCn", "[\\p{Extended_Pictographic}&\\p{Cn}]")); - final List partition = new ArrayList<>(); - for (int lb = 0; lb < UCharacter.LineBreak.COUNT; ++lb) { - final String lbValueShortName = - UCharacter.getPropertyValueName(UProperty.LINE_BREAK, lb, UProperty.NameChoice.SHORT); - if (lbValueShortName.equals("SA")) { + List partition = new ArrayList<>(); + rules = new ArrayList<>(); + + rules.add(new RegexRule("sot ÷ contra LB2", "^", Resolution.BREAK, "")); + // This one could be part of the rules. + rules.add(new RegexRule("LB3 ÷ eot", "", Resolution.BREAK, "$")); + + + // --- NOLI ME TANGERE --- + // Generated by GenerateBreakTest.java in the Unicode tools. + partition.add(new NamedSet("AI_EastAsian", new UnicodeSet("([\\p{Line_Break=Ambiguous}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("AImEastAsian", new UnicodeSet("([\\p{Line_Break=Ambiguous}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("AK", new UnicodeSet("([\\p{Line_Break=Aksara}])"))); + partition.add(new NamedSet("ALorig_EastAsian", new UnicodeSet("([\\p{Line_Break=Alphabetic}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("ALorig_DottedCircle", new UnicodeSet("([\\p{Line_Break=Alphabetic}&[◌]])"))); + partition.add(new NamedSet("ALorigmEastAsianmDottedCircle", new UnicodeSet("([\\p{Line_Break=Alphabetic}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]-[◌]])"))); + partition.add(new NamedSet("AP", new UnicodeSet("([\\p{Line_Break=Aksara_Prebase}])"))); + partition.add(new NamedSet("AS", new UnicodeSet("([\\p{Line_Break=Aksara_Start}])"))); + partition.add(new NamedSet("B2", new UnicodeSet("([\\p{Line_Break=Break_Both}])"))); + partition.add(new NamedSet("BA_EastAsian", new UnicodeSet("([\\p{Line_Break=Break_After}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("BA_Hyphen", new UnicodeSet("([\\p{Line_Break=Break_After}&[\\u2010]])"))); + partition.add(new NamedSet("BAmEastAsianmHyphen", new UnicodeSet("([\\p{Line_Break=Break_After}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]-[\\u2010]])"))); + partition.add(new NamedSet("BB", new UnicodeSet("([\\p{Line_Break=Break_Before}])"))); + partition.add(new NamedSet("BK", new UnicodeSet("([\\p{Line_Break=Mandatory_Break}])"))); + partition.add(new NamedSet("CB", new UnicodeSet("([\\p{Line_Break=Contingent_Break}])"))); + partition.add(new NamedSet("CL_EastAsian", new UnicodeSet("([\\p{Line_Break=Close_Punctuation}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("CLmEastAsian", new UnicodeSet("([\\p{Line_Break=Close_Punctuation}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("CP", new UnicodeSet("([\\p{Line_Break=CP}])"))); + partition.add(new NamedSet("CMorig_EastAsian", new UnicodeSet("([\\p{Line_Break=Combining_Mark}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("CMorigmEastAsian", new UnicodeSet("([\\p{Line_Break=Combining_Mark}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("CR", new UnicodeSet("([\\p{Line_Break=Carriage_Return}])"))); + partition.add(new NamedSet("EX_EastAsian", new UnicodeSet("([\\p{Line_Break=Exclamation}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("EXmEastAsian", new UnicodeSet("([\\p{Line_Break=Exclamation}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("GL_EastAsian", new UnicodeSet("([\\p{Line_Break=Glue}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("GLmEastAsian", new UnicodeSet("([\\p{Line_Break=Glue}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("H2", new UnicodeSet("([\\p{Line_Break=H2}])"))); + partition.add(new NamedSet("H3", new UnicodeSet("([\\p{Line_Break=H3}])"))); + partition.add(new NamedSet("HL", new UnicodeSet("([\\p{Line_Break=HL}])"))); + partition.add(new NamedSet("HY", new UnicodeSet("([\\p{Line_Break=Hyphen}])"))); + partition.add(new NamedSet("ID_EastAsian", new UnicodeSet("([\\p{Line_Break=Ideographic}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("ID_ExtPictUnassigned", new UnicodeSet("([\\p{Line_Break=Ideographic}&[\\p{Extended_Pictographic}&\\p{gc=Cn}]])"))); + partition.add(new NamedSet("IDmEastAsianmExtPictUnassigned", new UnicodeSet("([\\p{Line_Break=Ideographic}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]-[\\p{Extended_Pictographic}&\\p{gc=Cn}]])"))); + partition.add(new NamedSet("IN_EastAsian", new UnicodeSet("([\\p{Line_Break=Inseparable}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("INmEastAsian", new UnicodeSet("([\\p{Line_Break=Inseparable}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("IS", new UnicodeSet("([\\p{Line_Break=Infix_Numeric}])"))); + partition.add(new NamedSet("JL", new UnicodeSet("([\\p{Line_Break=JL}])"))); + partition.add(new NamedSet("JT", new UnicodeSet("([\\p{Line_Break=JT}])"))); + partition.add(new NamedSet("JV", new UnicodeSet("([\\p{Line_Break=JV}])"))); + partition.add(new NamedSet("LF", new UnicodeSet("([\\p{Line_Break=Line_Feed}])"))); + partition.add(new NamedSet("NL", new UnicodeSet("([\\p{Line_Break=Next_Line}])"))); + partition.add(new NamedSet("NSorig_EastAsian", new UnicodeSet("([\\p{Line_Break=Nonstarter}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("NSorigmEastAsian", new UnicodeSet("([\\p{Line_Break=Nonstarter}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("NU", new UnicodeSet("([\\p{Line_Break=Numeric}])"))); + partition.add(new NamedSet("OP_EastAsian", new UnicodeSet("([\\p{Line_Break=Open_Punctuation}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("OPmEastAsian", new UnicodeSet("([\\p{Line_Break=Open_Punctuation}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("PO_EastAsian", new UnicodeSet("([\\p{Line_Break=Postfix_Numeric}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("POmEastAsian", new UnicodeSet("([\\p{Line_Break=Postfix_Numeric}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("PR_EastAsian", new UnicodeSet("([\\p{Line_Break=Prefix_Numeric}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("PRmEastAsian", new UnicodeSet("([\\p{Line_Break=Prefix_Numeric}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("QU_Pi", new UnicodeSet("([\\p{Line_Break=Quotation}&\\p{gc=Pi}])"))); + partition.add(new NamedSet("QU_Pf", new UnicodeSet("([\\p{Line_Break=Quotation}&\\p{gc=Pf}])"))); + partition.add(new NamedSet("QUmPimPf", new UnicodeSet("([\\p{Line_Break=Quotation}-\\p{gc=Pi}-\\p{gc=Pf}])"))); + partition.add(new NamedSet("SA_Mn", new UnicodeSet("([[\\p{Line_Break=Complex_Context}&\\p{gc=Mn}]])"))); + partition.add(new NamedSet("SA_Mc", new UnicodeSet("([[\\p{Line_Break=Complex_Context}&\\p{gc=Mc}]])"))); + partition.add(new NamedSet("SAmMnmMc", new UnicodeSet("([[\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]])"))); + partition.add(new NamedSet("SG", new UnicodeSet("([\\p{Line_Break=Surrogate}])"))); + partition.add(new NamedSet("SP", new UnicodeSet("([\\p{Line_Break=Space}])"))); + partition.add(new NamedSet("SY", new UnicodeSet("([\\p{Line_Break=Break_Symbols}])"))); + partition.add(new NamedSet("VF", new UnicodeSet("([\\p{Line_Break=Virama_Final}])"))); + partition.add(new NamedSet("VI", new UnicodeSet("([\\p{Line_Break=Virama}])"))); + partition.add(new NamedSet("WJ", new UnicodeSet("([\\p{Line_Break=Word_Joiner}])"))); + partition.add(new NamedSet("XX_ExtPictUnassigned", new UnicodeSet("([\\p{Line_Break=Unknown}&[\\p{Extended_Pictographic}&\\p{gc=Cn}]])"))); + partition.add(new NamedSet("XXmExtPictUnassigned", new UnicodeSet("([\\p{Line_Break=Unknown}-[\\p{Extended_Pictographic}&\\p{gc=Cn}]])"))); + partition.add(new NamedSet("ZW", new UnicodeSet("([\\p{Line_Break=ZWSpace}])"))); + partition.add(new NamedSet("CJ", new UnicodeSet("([\\p{Line_Break=Conditional_Japanese_Starter}])"))); + partition.add(new NamedSet("RI", new UnicodeSet("([\\p{Line_Break=Regional_Indicator}])"))); + partition.add(new NamedSet("EB_EastAsian", new UnicodeSet("([\\p{Line_Break=E_Base}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("EBmEastAsian", new UnicodeSet("([\\p{Line_Break=E_Base}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); + partition.add(new NamedSet("EM", new UnicodeSet("([\\p{Line_Break=E_Modifier}])"))); + partition.add(new NamedSet("ZWJ", new UnicodeSet("([\\p{Line_Break=ZWJ}])"))); + + rules.add(new RegexRule("$BK ÷", "\\p{Line_Break=Mandatory_Break}", Resolution.BREAK, "")); + rules.add(new RegexRule("$CR × $LF", "\\p{Line_Break=Carriage_Return}", Resolution.NO_BREAK, "\\p{Line_Break=Line_Feed}")); + rules.add(new RegexRule("$CR ÷", "\\p{Line_Break=Carriage_Return}", Resolution.BREAK, "")); + rules.add(new RegexRule("$LF ÷", "\\p{Line_Break=Line_Feed}", Resolution.BREAK, "")); + rules.add(new RegexRule("$NL ÷", "\\p{Line_Break=Next_Line}", Resolution.BREAK, "")); + rules.add(new RegexRule("× ( $BK | $CR | $LF | $NL )", "", Resolution.NO_BREAK, "( \\p{Line_Break=Mandatory_Break} | \\p{Line_Break=Carriage_Return} | \\p{Line_Break=Line_Feed} | \\p{Line_Break=Next_Line} )")); + rules.add(new RegexRule("× $SP", "", Resolution.NO_BREAK, "\\p{Line_Break=Space}")); + rules.add(new RegexRule("× $ZW", "", Resolution.NO_BREAK, "\\p{Line_Break=ZWSpace}")); + rules.add(new RegexRule("$ZW $SP* ÷", "\\p{Line_Break=ZWSpace} \\p{Line_Break=Space}*", Resolution.BREAK, "")); + rules.add(new RegexRule("$ZWJ ×", "\\p{Line_Break=ZWJ}", Resolution.NO_BREAK, "")); + rules.add(new RemapRule("(?[^$BK $CR $LF $NL $SP $ZW]) ( $CM | $ZWJ )* → ${X}", "(?[^\\p{Line_Break=Mandatory_Break} \\p{Line_Break=Carriage_Return} \\p{Line_Break=Line_Feed} \\p{Line_Break=Next_Line} \\p{Line_Break=Space} \\p{Line_Break=ZWSpace}]) ( [\\p{Line_Break=Combining_Mark} [\\p{Line_Break=Complex_Context}&\\p{gc=Mn}] [\\p{Line_Break=Complex_Context}&\\p{gc=Mc}]] | \\p{Line_Break=ZWJ} )*", "${X}")); + rules.add(new RemapRule("( $CM | $ZWJ ) → A", "( [\\p{Line_Break=Combining_Mark} [\\p{Line_Break=Complex_Context}&\\p{gc=Mn}] [\\p{Line_Break=Complex_Context}&\\p{gc=Mc}]] | \\p{Line_Break=ZWJ} )", "A")); + rules.add(new RegexRule("× $WJ", "", Resolution.NO_BREAK, "\\p{Line_Break=Word_Joiner}")); + rules.add(new RegexRule("$WJ ×", "\\p{Line_Break=Word_Joiner}", Resolution.NO_BREAK, "")); + rules.add(new RegexRule("$GL ×", "\\p{Line_Break=Glue}", Resolution.NO_BREAK, "")); + rules.add(new RegexRule("[^ $SP $BA $HY] × $GL", "[^ \\p{Line_Break=Space} \\p{Line_Break=Break_After} \\p{Line_Break=Hyphen}]", Resolution.NO_BREAK, "\\p{Line_Break=Glue}")); + rules.add(new RegexRule("× $EX", "", Resolution.NO_BREAK, "\\p{Line_Break=Exclamation}")); + rules.add(new RegexRule("× $CL", "", Resolution.NO_BREAK, "\\p{Line_Break=Close_Punctuation}")); + rules.add(new RegexRule("× $CP", "", Resolution.NO_BREAK, "\\p{Line_Break=CP}")); + rules.add(new RegexRule("× $SY", "", Resolution.NO_BREAK, "\\p{Line_Break=Break_Symbols}")); + rules.add(new RegexRule("$OP $SP* ×", "\\p{Line_Break=Open_Punctuation} \\p{Line_Break=Space}*", Resolution.NO_BREAK, "")); + rules.add(new RegexRule("( $sot | $BK | $CR | $LF | $NL | $OP | $QU | $GL | $SP | $ZW ) $QU_Pi $SP* ×", "( ^ | \\p{Line_Break=Mandatory_Break} | \\p{Line_Break=Carriage_Return} | \\p{Line_Break=Line_Feed} | \\p{Line_Break=Next_Line} | \\p{Line_Break=Open_Punctuation} | \\p{Line_Break=Quotation} | \\p{Line_Break=Glue} | \\p{Line_Break=Space} | \\p{Line_Break=ZWSpace} ) [\\p{Line_Break=Quotation} & \\p{gc=Pi}] \\p{Line_Break=Space}*", Resolution.NO_BREAK, "")); + rules.add(new RegexRule("× $QU_Pf ( $SP | $GL | $WJ | $CL | $QU | $CP | $EX | $IS | $SY | $BK | $CR | $LF | $NL | $ZW | $eot )", "", Resolution.NO_BREAK, "[\\p{Line_Break=Quotation} & \\p{gc=Pf}] ( \\p{Line_Break=Space} | \\p{Line_Break=Glue} | \\p{Line_Break=Word_Joiner} | \\p{Line_Break=Close_Punctuation} | \\p{Line_Break=Quotation} | \\p{Line_Break=CP} | \\p{Line_Break=Exclamation} | \\p{Line_Break=Infix_Numeric} | \\p{Line_Break=Break_Symbols} | \\p{Line_Break=Mandatory_Break} | \\p{Line_Break=Carriage_Return} | \\p{Line_Break=Line_Feed} | \\p{Line_Break=Next_Line} | \\p{Line_Break=ZWSpace} | (?!.) )")); + rules.add(new RegexRule("$SP ÷ $IS $NU", "\\p{Line_Break=Space}", Resolution.BREAK, "\\p{Line_Break=Infix_Numeric} \\p{Line_Break=Numeric}")); + rules.add(new RegexRule("× $IS", "", Resolution.NO_BREAK, "\\p{Line_Break=Infix_Numeric}")); + rules.add(new RegexRule("($CL | $CP) $SP* × $NS", "(\\p{Line_Break=Close_Punctuation} | \\p{Line_Break=CP}) \\p{Line_Break=Space}*", Resolution.NO_BREAK, "[\\p{Line_Break=Nonstarter} \\p{Line_Break=Conditional_Japanese_Starter}]")); + rules.add(new RegexRule("$B2 $SP* × $B2", "\\p{Line_Break=Break_Both} \\p{Line_Break=Space}*", Resolution.NO_BREAK, "\\p{Line_Break=Break_Both}")); + rules.add(new RegexRule("$SP ÷", "\\p{Line_Break=Space}", Resolution.BREAK, "")); + rules.add(new RegexRule("× $QUmPi", "", Resolution.NO_BREAK, "[\\p{Line_Break=Quotation} - \\p{gc=Pi}]")); + rules.add(new RegexRule("$QUmPf ×", "[\\p{Line_Break=Quotation} - \\p{gc=Pf}]", Resolution.NO_BREAK, "")); + rules.add(new RegexRule("[^$EastAsian] × $QU", "[^[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]", Resolution.NO_BREAK, "\\p{Line_Break=Quotation}")); + rules.add(new RegexRule("× $QU ( [^$EastAsian] | $eot )", "", Resolution.NO_BREAK, "\\p{Line_Break=Quotation} ( [^[\\p{ea=F}\\p{ea=W}\\p{ea=H}]] | (?!.) )")); + rules.add(new RegexRule("$QU × [^$EastAsian]", "\\p{Line_Break=Quotation}", Resolution.NO_BREAK, "[^[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]")); + rules.add(new RegexRule("( $sot | [^$EastAsian] ) $QU ×", "( ^ | [^[\\p{ea=F}\\p{ea=W}\\p{ea=H}]] ) \\p{Line_Break=Quotation}", Resolution.NO_BREAK, "")); + rules.add(new RegexRule("÷ $CB", "", Resolution.BREAK, "\\p{Line_Break=Contingent_Break}")); + rules.add(new RegexRule("$CB ÷", "\\p{Line_Break=Contingent_Break}", Resolution.BREAK, "")); + rules.add(new RegexRule("( $sot | $BK | $CR | $LF | $NL | $SP | $ZW | $CB | $GL ) ( $HY | $Hyphen ) × $AL", "( ^ | \\p{Line_Break=Mandatory_Break} | \\p{Line_Break=Carriage_Return} | \\p{Line_Break=Line_Feed} | \\p{Line_Break=Next_Line} | \\p{Line_Break=Space} | \\p{Line_Break=ZWSpace} | \\p{Line_Break=Contingent_Break} | \\p{Line_Break=Glue} ) ( \\p{Line_Break=Hyphen} | [\\u2010] )", Resolution.NO_BREAK, "[\\p{Line_Break=Ambiguous} \\p{Line_Break=Alphabetic} \\p{Line_Break=Surrogate} \\p{Line_Break=Unknown} [\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]]")); + rules.add(new RegexRule("× $BA", "", Resolution.NO_BREAK, "\\p{Line_Break=Break_After}")); + rules.add(new RegexRule("× $HY", "", Resolution.NO_BREAK, "\\p{Line_Break=Hyphen}")); + rules.add(new RegexRule("× $NS", "", Resolution.NO_BREAK, "[\\p{Line_Break=Nonstarter} \\p{Line_Break=Conditional_Japanese_Starter}]")); + rules.add(new RegexRule("$BB ×", "\\p{Line_Break=Break_Before}", Resolution.NO_BREAK, "")); + rules.add(new RegexRule("$HL ($HY | $NonEastAsianBA) × [^$HL]", "\\p{Line_Break=HL} (\\p{Line_Break=Hyphen} | [\\p{Line_Break=Break_After} & [^[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]])", Resolution.NO_BREAK, "[^\\p{Line_Break=HL}]")); + rules.add(new RegexRule("$SY × $HL", "\\p{Line_Break=Break_Symbols}", Resolution.NO_BREAK, "\\p{Line_Break=HL}")); + rules.add(new RegexRule("× $IN", "", Resolution.NO_BREAK, "\\p{Line_Break=Inseparable}")); + rules.add(new RegexRule("($AL | $HL) × $NU", "([\\p{Line_Break=Ambiguous} \\p{Line_Break=Alphabetic} \\p{Line_Break=Surrogate} \\p{Line_Break=Unknown} [\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]] | \\p{Line_Break=HL})", Resolution.NO_BREAK, "\\p{Line_Break=Numeric}")); + rules.add(new RegexRule("$NU × ($AL | $HL)", "\\p{Line_Break=Numeric}", Resolution.NO_BREAK, "([\\p{Line_Break=Ambiguous} \\p{Line_Break=Alphabetic} \\p{Line_Break=Surrogate} \\p{Line_Break=Unknown} [\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]] | \\p{Line_Break=HL})")); + rules.add(new RegexRule("$PR × ($ID | $EB | $EM)", "\\p{Line_Break=Prefix_Numeric}", Resolution.NO_BREAK, "(\\p{Line_Break=Ideographic} | \\p{Line_Break=E_Base} | \\p{Line_Break=E_Modifier})")); + rules.add(new RegexRule("($ID | $EB | $EM) × $PO", "(\\p{Line_Break=Ideographic} | \\p{Line_Break=E_Base} | \\p{Line_Break=E_Modifier})", Resolution.NO_BREAK, "\\p{Line_Break=Postfix_Numeric}")); + rules.add(new RegexRule("($PR | $PO) × ($AL | $HL)", "(\\p{Line_Break=Prefix_Numeric} | \\p{Line_Break=Postfix_Numeric})", Resolution.NO_BREAK, "([\\p{Line_Break=Ambiguous} \\p{Line_Break=Alphabetic} \\p{Line_Break=Surrogate} \\p{Line_Break=Unknown} [\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]] | \\p{Line_Break=HL})")); + rules.add(new RegexRule("($AL | $HL) × ($PR | $PO)", "([\\p{Line_Break=Ambiguous} \\p{Line_Break=Alphabetic} \\p{Line_Break=Surrogate} \\p{Line_Break=Unknown} [\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]] | \\p{Line_Break=HL})", Resolution.NO_BREAK, "(\\p{Line_Break=Prefix_Numeric} | \\p{Line_Break=Postfix_Numeric})")); + rules.add(new RegexRule("$NU ( $SY | $IS )* $CL × $PO", "\\p{Line_Break=Numeric} ( \\p{Line_Break=Break_Symbols} | \\p{Line_Break=Infix_Numeric} )* \\p{Line_Break=Close_Punctuation}", Resolution.NO_BREAK, "\\p{Line_Break=Postfix_Numeric}")); + rules.add(new RegexRule("$NU ( $SY | $IS )* $CP × $PO", "\\p{Line_Break=Numeric} ( \\p{Line_Break=Break_Symbols} | \\p{Line_Break=Infix_Numeric} )* \\p{Line_Break=CP}", Resolution.NO_BREAK, "\\p{Line_Break=Postfix_Numeric}")); + rules.add(new RegexRule("$NU ( $SY | $IS )* $CL × $PR", "\\p{Line_Break=Numeric} ( \\p{Line_Break=Break_Symbols} | \\p{Line_Break=Infix_Numeric} )* \\p{Line_Break=Close_Punctuation}", Resolution.NO_BREAK, "\\p{Line_Break=Prefix_Numeric}")); + rules.add(new RegexRule("$NU ( $SY | $IS )* $CP × $PR", "\\p{Line_Break=Numeric} ( \\p{Line_Break=Break_Symbols} | \\p{Line_Break=Infix_Numeric} )* \\p{Line_Break=CP}", Resolution.NO_BREAK, "\\p{Line_Break=Prefix_Numeric}")); + rules.add(new RegexRule("$NU ( $SY | $IS )* × $PO", "\\p{Line_Break=Numeric} ( \\p{Line_Break=Break_Symbols} | \\p{Line_Break=Infix_Numeric} )*", Resolution.NO_BREAK, "\\p{Line_Break=Postfix_Numeric}")); + rules.add(new RegexRule("$NU ( $SY | $IS )* × $PR", "\\p{Line_Break=Numeric} ( \\p{Line_Break=Break_Symbols} | \\p{Line_Break=Infix_Numeric} )*", Resolution.NO_BREAK, "\\p{Line_Break=Prefix_Numeric}")); + rules.add(new RegexRule("$PO × $OP $NU", "\\p{Line_Break=Postfix_Numeric}", Resolution.NO_BREAK, "\\p{Line_Break=Open_Punctuation} \\p{Line_Break=Numeric}")); + rules.add(new RegexRule("$PO × $OP $IS $NU", "\\p{Line_Break=Postfix_Numeric}", Resolution.NO_BREAK, "\\p{Line_Break=Open_Punctuation} \\p{Line_Break=Infix_Numeric} \\p{Line_Break=Numeric}")); + rules.add(new RegexRule("$PO × $NU", "\\p{Line_Break=Postfix_Numeric}", Resolution.NO_BREAK, "\\p{Line_Break=Numeric}")); + rules.add(new RegexRule("$PR × $OP $NU", "\\p{Line_Break=Prefix_Numeric}", Resolution.NO_BREAK, "\\p{Line_Break=Open_Punctuation} \\p{Line_Break=Numeric}")); + rules.add(new RegexRule("$PR × $OP $IS $NU", "\\p{Line_Break=Prefix_Numeric}", Resolution.NO_BREAK, "\\p{Line_Break=Open_Punctuation} \\p{Line_Break=Infix_Numeric} \\p{Line_Break=Numeric}")); + rules.add(new RegexRule("$PR × $NU", "\\p{Line_Break=Prefix_Numeric}", Resolution.NO_BREAK, "\\p{Line_Break=Numeric}")); + rules.add(new RegexRule("$HY × $NU", "\\p{Line_Break=Hyphen}", Resolution.NO_BREAK, "\\p{Line_Break=Numeric}")); + rules.add(new RegexRule("$IS × $NU", "\\p{Line_Break=Infix_Numeric}", Resolution.NO_BREAK, "\\p{Line_Break=Numeric}")); + rules.add(new RegexRule("$NU ( $SY | $IS )* × $NU", "\\p{Line_Break=Numeric} ( \\p{Line_Break=Break_Symbols} | \\p{Line_Break=Infix_Numeric} )*", Resolution.NO_BREAK, "\\p{Line_Break=Numeric}")); + rules.add(new RegexRule("$JL × $JL | $JV | $H2 | $H3", "\\p{Line_Break=JL}", Resolution.NO_BREAK, "\\p{Line_Break=JL} | \\p{Line_Break=JV} | \\p{Line_Break=H2} | \\p{Line_Break=H3}")); + rules.add(new RegexRule("$JV | $H2 × $JV | $JT", "\\p{Line_Break=JV} | \\p{Line_Break=H2}", Resolution.NO_BREAK, "\\p{Line_Break=JV} | \\p{Line_Break=JT}")); + rules.add(new RegexRule("$JT | $H3 × $JT", "\\p{Line_Break=JT} | \\p{Line_Break=H3}", Resolution.NO_BREAK, "\\p{Line_Break=JT}")); + rules.add(new RegexRule("$JL | $JV | $JT | $H2 | $H3 × $PO", "\\p{Line_Break=JL} | \\p{Line_Break=JV} | \\p{Line_Break=JT} | \\p{Line_Break=H2} | \\p{Line_Break=H3}", Resolution.NO_BREAK, "\\p{Line_Break=Postfix_Numeric}")); + rules.add(new RegexRule("$PR × $JL | $JV | $JT | $H2 | $H3", "\\p{Line_Break=Prefix_Numeric}", Resolution.NO_BREAK, "\\p{Line_Break=JL} | \\p{Line_Break=JV} | \\p{Line_Break=JT} | \\p{Line_Break=H2} | \\p{Line_Break=H3}")); + rules.add(new RegexRule("($AL | $HL) × ($AL | $HL)", "([\\p{Line_Break=Ambiguous} \\p{Line_Break=Alphabetic} \\p{Line_Break=Surrogate} \\p{Line_Break=Unknown} [\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]] | \\p{Line_Break=HL})", Resolution.NO_BREAK, "([\\p{Line_Break=Ambiguous} \\p{Line_Break=Alphabetic} \\p{Line_Break=Surrogate} \\p{Line_Break=Unknown} [\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]] | \\p{Line_Break=HL})")); + rules.add(new RegexRule("$AP × ($AK | $DottedCircle | $AS)", "\\p{Line_Break=Aksara_Prebase}", Resolution.NO_BREAK, "(\\p{Line_Break=Aksara} | [◌] | \\p{Line_Break=Aksara_Start})")); + rules.add(new RegexRule("($AK | $DottedCircle | $AS) × ($VF | $VI)", "(\\p{Line_Break=Aksara} | [◌] | \\p{Line_Break=Aksara_Start})", Resolution.NO_BREAK, "(\\p{Line_Break=Virama_Final} | \\p{Line_Break=Virama})")); + rules.add(new RegexRule("($AK | $DottedCircle | $AS) $VI × ($AK | $DottedCircle)", "(\\p{Line_Break=Aksara} | [◌] | \\p{Line_Break=Aksara_Start}) \\p{Line_Break=Virama}", Resolution.NO_BREAK, "(\\p{Line_Break=Aksara} | [◌])")); + rules.add(new RegexRule("($AK | $DottedCircle | $AS) × ($AK | $DottedCircle | $AS) $VF", "(\\p{Line_Break=Aksara} | [◌] | \\p{Line_Break=Aksara_Start})", Resolution.NO_BREAK, "(\\p{Line_Break=Aksara} | [◌] | \\p{Line_Break=Aksara_Start}) \\p{Line_Break=Virama_Final}")); + rules.add(new RegexRule("$IS × ($AL | $HL)", "\\p{Line_Break=Infix_Numeric}", Resolution.NO_BREAK, "([\\p{Line_Break=Ambiguous} \\p{Line_Break=Alphabetic} \\p{Line_Break=Surrogate} \\p{Line_Break=Unknown} [\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]] | \\p{Line_Break=HL})")); + rules.add(new RegexRule("($AL | $HL | $NU) × $OPmEastAsian", "([\\p{Line_Break=Ambiguous} \\p{Line_Break=Alphabetic} \\p{Line_Break=Surrogate} \\p{Line_Break=Unknown} [\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]] | \\p{Line_Break=HL} | \\p{Line_Break=Numeric})", Resolution.NO_BREAK, "[\\p{Line_Break=Open_Punctuation}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]")); + rules.add(new RegexRule("$CPmEastAsian × ($AL | $HL | $NU)", "[\\p{Line_Break=CP}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]", Resolution.NO_BREAK, "([\\p{Line_Break=Ambiguous} \\p{Line_Break=Alphabetic} \\p{Line_Break=Surrogate} \\p{Line_Break=Unknown} [\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]] | \\p{Line_Break=HL} | \\p{Line_Break=Numeric})")); + rules.add(new RegexRule("$sot ($RI $RI)* $RI × $RI", "^ (\\p{Line_Break=Regional_Indicator} \\p{Line_Break=Regional_Indicator})* \\p{Line_Break=Regional_Indicator}", Resolution.NO_BREAK, "\\p{Line_Break=Regional_Indicator}")); + rules.add(new RegexRule("[^$RI] ($RI $RI)* $RI × $RI", "[^\\p{Line_Break=Regional_Indicator}] (\\p{Line_Break=Regional_Indicator} \\p{Line_Break=Regional_Indicator})* \\p{Line_Break=Regional_Indicator}", Resolution.NO_BREAK, "\\p{Line_Break=Regional_Indicator}")); + rules.add(new RegexRule("$RI ÷ $RI", "\\p{Line_Break=Regional_Indicator}", Resolution.BREAK, "\\p{Line_Break=Regional_Indicator}")); + rules.add(new RegexRule("$EB × $EM", "\\p{Line_Break=E_Base}", Resolution.NO_BREAK, "\\p{Line_Break=E_Modifier}")); + rules.add(new RegexRule("$ExtPictUnassigned × $EM", "[\\p{Extended_Pictographic}&\\p{gc=Cn}]", Resolution.NO_BREAK, "\\p{Line_Break=E_Modifier}")); + // --- End of generated code. --- + + // TODO(egg): This could just as well be part of the rules… + rules.add(new RegexRule("(ALL ÷ / ÷ ALL)", "", Resolution.BREAK, "")); + + final UnicodeSet lbSA = new UnicodeSet("\\p{lb=SA}"); + for (final NamedSet part : partition) { + if (lbSA.containsAll(part.set)) { continue; } - partition.add(new NamedSet(lbValueShortName, "\\p{lb=" + lbValueShortName + "}")); - } - for (final NamedSet refinement : interestingSets) { - for (int i = 0; i < partition.size();) { - final String name = partition.get(i).name; - final UnicodeSet set = partition.get(i).set; - final UnicodeSet intersection = new UnicodeSet(set).retainAll(refinement.set); - final UnicodeSet complement = new UnicodeSet(set).removeAll(refinement.set); - if (!intersection.isEmpty() && !complement.isEmpty()) { - partition.add(i, new NamedSet(name, complement)); - partition.add(i + 1, new NamedSet(name + "&" + refinement.name, intersection)); - partition.remove(i + 2); - i += 2; - } else { - ++i; - } - } - } - for (final NamedSet part : partition) { fSets.add(part.set); fClassNames.add(part.name); } @@ -890,6 +881,20 @@ class NamedSet { void setText(StringBuffer s) { fText = s; prepareAppliedRules(s.length()); + StringBuilder remapped = new StringBuilder(s.toString()); + resolved = new BreakContext[s.length() + 1]; + for (int i = 0; i < resolved.length; ++i) { + resolved[i] = new BreakContext(i); + } + for (final SegmentationRule rule : rules) { + rule.apply(remapped, resolved); + } + for (int i = 0; i < resolved.length; ++i) { + if (resolved[i].appliedRule == null) { + throw new IllegalArgumentException("Failed to resolve at " + i); + } + setAppliedRule(i, resolved[i].appliedRule.name()); + } } @@ -897,707 +902,12 @@ void setText(StringBuffer s) { @Override int next(int startPos) { - int pos; // Index of the char following a potential break position - int thisChar; // Character at above position "pos" - - int prevPos; // Index of the char preceding a potential break position - int prevChar; // Character at above position. Note that prevChar - // // and thisChar may not be adjacent because combining - // // characters between them will be ignored. - - int prevPosX2; - int prevCharX2; // Character before prevChar, more context for LB 21a - - int nextPos; // Index of the next character following pos. - // // Usually skips over combining marks. - int tPos; // temp value. - int matchVals[] = null; // Number Expression Match Results - - - if (startPos >= fText.length()) { - return -1; - } - - - // Initial values for loop. Loop will run the first time without finding breaks, - // while the invalid values shift out and the "this" and - // "prev" positions are filled in with good values. - pos = prevPos = prevPosX2 = -1; // Invalid value, serves as flag for initial loop iteration. - thisChar = prevChar = prevCharX2 = 0; - nextPos = startPos; - - - // Loop runs once per position in the test text, until a break position - // is found. In each iteration, we are testing for a possible break - // just preceding the character at index "pos". The character preceding - // this char is at position "prevPos"; because of combining sequences, - // "prevPos" can be arbitrarily far before "pos". - for (;;) { - // Advance to the next position to be tested. - prevPosX2 = prevPos; - prevCharX2 = prevChar; - prevPos = pos; - prevChar = thisChar; - pos = nextPos; - nextPos = moveIndex32(fText, pos, 1); - - if (pos >= fText.length()) { - setAppliedRule(pos, "LB 2 Break at end of text"); - break; - } - - // We do this rule out-of-order because the adjustment does - // not effect the way that rules LB 3 through LB 6 match, - // and doing it here rather than after LB 6 is substantially - // simpler when combining sequences do occur. - - - // LB 9 Keep combining sequences together. - // advance over any CM class chars at "pos", - // result is "nextPos" for the following loop iteration. - thisChar = UTF16.charAt(fText, pos); - if (!(fSP.contains(thisChar) || fBK.contains(thisChar) || thisChar==0x0d || - thisChar==0x0a || fNL.contains(thisChar) || fZW.contains(thisChar) )) { - for (;;) { - if (nextPos == fText.length()) { - break; - } - int nextChar = UTF16.charAt(fText, nextPos); - if (!fCM.contains(nextChar)) { - break; - } - nextPos = moveIndex32(fText, nextPos, 1); - } - } - - // LB 9 Treat X CM* as if it were X - // No explicit action required. - - // LB 10 Treat any remaining combining mark as lb=AL, ea=Na - if (fCM.contains(thisChar)) { - thisChar = 'A'; - } - - - // If the loop is still warming up - if we haven't shifted the initial - // -1 positions out of prevPos yet - loop back to advance the - // position in the input without any further looking for breaks. - if (prevPos == -1) { - setAppliedRule(pos, "LB 9 adjust for combining sequences."); - continue; - } - - if (fBK.contains(prevChar)) { - setAppliedRule(pos, "LB 4 Always break after hard line breaks"); - break; - } - - if (fCR.contains(prevChar) && fLF.contains(thisChar)) { - setAppliedRule(pos, "LB 5 Break after CR, LF, NL, but not inside CR LF"); - continue; - } - if (fCR.contains(prevChar) || - fLF.contains(prevChar) || - fNL.contains(prevChar)) { - setAppliedRule(pos, "LB 5 Break after CR, LF, NL, but not inside CR LF"); - break; - } - - if (fBK.contains(thisChar) || fCR.contains(thisChar) || - fLF.contains(thisChar) || fNL.contains(thisChar) ) { - setAppliedRule(pos, "LB 6 Don't break before hard line breaks"); - continue; - } - - - if (fSP.contains(thisChar)) { - setAppliedRule(pos, "LB 7 Don't break before spaces or zero-width space"); - continue; - } - - if (fZW.contains(thisChar)) { - setAppliedRule(pos, "LB 7 Don't break before spaces or zero-width space"); - continue; - } - - // ZW SP* ÷ - // Scan backwards from prevChar for SP* ZW - tPos = prevPos; - while (tPos > 0 && fSP.contains(UTF16.charAt(fText, tPos))) { - tPos = moveIndex32(fText, tPos, -1); - } - if (fZW.contains(UTF16.charAt(fText, tPos))) { - setAppliedRule(pos, "LB 8 Break after zero width space"); - break; - } - - // The monkey test's way of ignoring combining characters doesn't work - // for this rule. ZWJ is also a CM. Need to get the actual character - // preceding "thisChar", not ignoring combining marks, possibly ZWJ. - { - int prevC = fText.codePointBefore(pos); - if (fZWJ.contains(prevC)) { - setAppliedRule(pos, "LB 8a ZWJ x"); - continue; - } + for (int i = startPos + 1; i < resolved.length; ++i) { + if (resolved[i].appliedRule.resolution() == Resolution.BREAK) { + return i; } - - // appliedRule: "LB 9, 10"; // Already done, at top of loop."; - - - // x WJ - // WJ x - if (fWJ.contains(thisChar) || fWJ.contains(prevChar)) { - setAppliedRule(pos, "LB 11 Do not break before or after WORD JOINER and related characters."); - continue; - } - - - if (fGL.contains(prevChar)) { - setAppliedRule(pos, "LB 12 GL x"); - continue; - } - - if (!(fSP.contains(prevChar) || - fBA.contains(prevChar) || - fHY.contains(prevChar) ) && fGL.contains(thisChar)) { - setAppliedRule(pos, "LB 12a [^SP BA HY] x GL"); - continue; - } - - if (fCL.contains(thisChar) || - fCP.contains(thisChar) || - fEX.contains(thisChar) || - fSY.contains(thisChar)) { - setAppliedRule(pos, "LB 13 Don't break before closings"); - continue; - } - - // Scan backwards, checking for this sequence. - // The OP char could include combining marks, so we actually check for - // OP CM* SP* x - tPos = prevPos; - if (fSP.contains(prevChar)) { - while (tPos > 0 && fSP.contains(UTF16.charAt(fText, tPos))) { - tPos=moveIndex32(fText, tPos, -1); - } - } - while (tPos > 0 && fCM.contains(UTF16.charAt(fText, tPos))) { - tPos=moveIndex32(fText, tPos, -1); - } - if (fOP.contains(UTF16.charAt(fText, tPos))) { - setAppliedRule(pos, "LB 14 Don't break after OP SP*"); - continue; - } - - // Same as LB 14, scan backward for - // (sot | BK | CR | LF | NL | OP CM*| QU CM* | GL CM* | SP) [\p{Pi}&QU] CM* SP*. - tPos = prevPos; - // SP* (with the aforementioned Twist). - if (fSP.contains(prevChar)) { - while (tPos > 0 && fSP.contains(UTF16.charAt(fText, tPos))) { - tPos = moveIndex32(fText, tPos, -1); - } - } - // CM*. - while (tPos > 0 && fCM.contains(UTF16.charAt(fText, tPos))) { - tPos = moveIndex32(fText, tPos, -1); - } - // [\p{Pi}&QU]. - if (fPi.contains(UTF16.charAt(fText, tPos)) && fQU.contains(UTF16.charAt(fText, tPos))) { - if (tPos == 0) { - setAppliedRule(pos, "LB 15a sot [\\p{Pi}&QU] SP* ×"); - continue; - } else { - tPos = moveIndex32(fText, tPos, -1); - if (fBK.contains(UTF16.charAt(fText, tPos)) || fCR.contains(UTF16.charAt(fText, tPos)) || - fLF.contains(UTF16.charAt(fText, tPos)) || fNL.contains(UTF16.charAt(fText, tPos)) || - fSP.contains(UTF16.charAt(fText, tPos)) || fZW.contains(UTF16.charAt(fText, tPos))) { - setAppliedRule(pos, "LB 15a (BK | CR | LF | NL | SP | ZW) [\\p{Pi}&QU] SP* ×"); - continue; - } - } - // CM*. - while (tPos > 0 && fCM.contains(UTF16.charAt(fText, tPos))) { - tPos = moveIndex32(fText, tPos, -1); - } - if (fOP.contains(UTF16.charAt(fText, tPos)) || fQU.contains(UTF16.charAt(fText, tPos)) || - fGL.contains(UTF16.charAt(fText, tPos))) { - setAppliedRule(pos, "LB 15a (OP | QU | GL) [\\p{Pi}&QU] SP* ×"); - continue; - } - } - - if (fPf.contains(thisChar) && fQU.contains(thisChar)) { - int nextChar = (nextPos < fText.length())? UTF16.charAt(fText, nextPos): 0; - if (nextPos == fText.length() || fSP.contains(nextChar) || fGL.contains(nextChar) || - fWJ.contains(nextChar) || fCL.contains(nextChar) || fQU.contains(nextChar) || - fCP.contains(nextChar) || fEX.contains(nextChar) || fIS.contains(nextChar) || - fSY.contains(nextChar) || fBK.contains(nextChar) || fCR.contains(nextChar) || - fLF.contains(nextChar) || fNL.contains(nextChar) || fZW.contains(nextChar)) { - setAppliedRule(pos, "LB 15b × [\\p{Pf}&QU] ( SP | GL | WJ | CL | QU | CP | EX | IS | SY | BK | CR | LF | NL | ZW | eot)"); - continue; - } - } - - if (nextPos < fText.length()) { - int nextChar = fText.codePointAt(nextPos); - if (fSP.contains(prevChar) && fIS.contains(thisChar) && fNU.contains(nextChar)) { - setAppliedRule(pos, "LB 15c Break before an IS that begins a number and follows a space"); - break; - } - } - - if (fIS.contains(thisChar)) { - setAppliedRule(pos, "LB 15d Do not break before numeric separators, even after spaces"); - continue; - } - - if (fNS.contains(thisChar)) { - tPos = prevPos; - while (tPos > 0 && fSP.contains(UTF16.charAt(fText, tPos))) { - tPos = moveIndex32(fText, tPos, -1); - } - while (tPos > 0 && fCM.contains(UTF16.charAt(fText, tPos))) { - tPos = moveIndex32(fText, tPos, -1); - } - if (fCL.contains(UTF16.charAt(fText, tPos)) || fCP.contains(UTF16.charAt(fText, tPos))) { - setAppliedRule(pos, "LB 16 (CL | CP) SP* x NS"); - continue; - } - } - - - if (fB2.contains(thisChar)) { - tPos = prevPos; - while (tPos > 0 && fSP.contains(UTF16.charAt(fText, tPos))) { - tPos = moveIndex32(fText, tPos, -1); - } - while (tPos > 0 && fCM.contains(UTF16.charAt(fText, tPos))) { - tPos = moveIndex32(fText, tPos, -1); - } - if (fB2.contains(UTF16.charAt(fText, tPos))) { - setAppliedRule(pos, "LB 17 B2 SP* x B2"); - continue; - } - } - - if (fSP.contains(prevChar)) { - setAppliedRule(pos, "LB 18 break after space"); - break; - } - - // LB 19 - // × [QU-\p{Pi}] - if (fQU.contains(thisChar) && !fPi.contains(thisChar)) { - setAppliedRule(pos, "LB 19 × [QU-\\p{Pi}]"); - continue; - } - // [QU-\p{Pf}] × - if (fQU.contains(prevChar) && !fPf.contains(prevChar)) { - setAppliedRule(pos, "LB 19 [QU-\\p{Pf}] ×"); - continue; - } - - // LB 19a - // [^\p{ea=F}\p{ea=W}\p{ea=H}] × QU - if (!feaFWH.contains(prevChar) && fQU.contains(thisChar)) { - setAppliedRule(pos, "LB 19a [^\\p{ea=F}\\p{ea=W}\\p{ea=H}] × QU"); - continue; - } - // × QU ( [^\p{ea=F}\p{ea=W}\p{ea=H}] | eot ) - if (fQU.contains(thisChar)) { - if (nextPos < fText.length()) { - int nextChar = fText.codePointAt(nextPos); - if (!feaFWH.contains(nextChar)) { - setAppliedRule(pos, "LB 19a × QU [^\\p{ea=F}\\p{ea=W}\\p{ea=H}]"); - continue; - } - } else { - setAppliedRule(pos, "LB 19 × QU eot"); - continue; - } - } - // QU × [^\p{ea=F}\p{ea=W}\p{ea=H}] - if (fQU.contains(prevChar) && !feaFWH.contains(thisChar)) { - setAppliedRule(pos, "LB 19a QU × [^\\p{ea=F}\\p{ea=W}\\p{ea=H}]"); - continue; - } - // ( sot | [^\p{ea=F}\p{ea=W}\p{ea=H}] ) QU × - if (fQU.contains(prevChar)) { - if (prevPos == 0) { - setAppliedRule(pos, "LB 19a sot QU ×"); - continue; - } - // prevPosX2 is -1 if there was a break, and prevCharX2 is 0; but the UAX #14 rules can - // look through breaks. - int breakObliviousPrevPosX2 = moveIndex32(fText, prevPos, -1); - while (fCM.contains(fText.codePointAt(breakObliviousPrevPosX2))) { - if (breakObliviousPrevPosX2 == 0) { - break; - } - int beforeCM = moveIndex32(fText, breakObliviousPrevPosX2, -1); - if (fBK.contains(fText.codePointAt(beforeCM)) || - fCR.contains(fText.codePointAt(beforeCM)) || - fLF.contains(fText.codePointAt(beforeCM)) || - fNL.contains(fText.codePointAt(beforeCM)) || - fSP.contains(fText.codePointAt(beforeCM)) || - fZW.contains(fText.codePointAt(beforeCM))) { - break; - } - breakObliviousPrevPosX2 = beforeCM; - } - if (!feaFWH.contains(fText.codePointAt(breakObliviousPrevPosX2)) || - fCM.contains(fText.codePointAt(breakObliviousPrevPosX2))) { - setAppliedRule(pos, "LB 19a [^\\p{ea=F}\\p{ea=W}\\p{ea=H}] QU ×"); - continue; - } - } - - if (fCB.contains(thisChar) || fCB.contains(prevChar)) { - setAppliedRule(pos, "LB 20 Break around a CB"); - break; - } - - // Don't break between Hyphens and letters if a break or a space precedes the hyphen. - // Formerly this was a Finnish tailoring. - // (sot | BK | CR | LF | NL | SP | ZW | CB | GL) ( HY | [\u2010] ) × AL - if (fAL.contains(thisChar) && (fHY.contains(prevChar) || fHH.contains(prevChar))) { - // sot ( HY | [\u2010] ) × AL. - if (prevPos == 0) { - setAppliedRule(pos, "LB 20a"); - continue; - } - // prevPosX2 is -1 if there was a break; but the UAX #14 rules can - // look through breaks. - int breakObliviousPrevPosX2 = moveIndex32(fText, prevPos, -1); - if (fBK.contains(fText.codePointAt(breakObliviousPrevPosX2)) || - fCR.contains(fText.codePointAt(breakObliviousPrevPosX2)) || - fLF.contains(fText.codePointAt(breakObliviousPrevPosX2)) || - fNL.contains(fText.codePointAt(breakObliviousPrevPosX2)) || - fSP.contains(fText.codePointAt(breakObliviousPrevPosX2)) || - fGL.contains(fText.codePointAt(breakObliviousPrevPosX2)) || - fZW.contains(fText.codePointAt(breakObliviousPrevPosX2))) { - setAppliedRule(pos, "LB 20a"); - continue; - } - while (breakObliviousPrevPosX2 > 0 && - fCM.contains(fText.codePointAt(breakObliviousPrevPosX2))) { - breakObliviousPrevPosX2 = moveIndex32(fText, breakObliviousPrevPosX2, -1); - } - if (fCB.contains(fText.codePointAt(breakObliviousPrevPosX2))) { - setAppliedRule(pos, "LB 20a"); - continue; - } - } - - if (fBA.contains(thisChar) || - fHY.contains(thisChar) || - fNS.contains(thisChar) || - fBB.contains(prevChar) ) { - setAppliedRule(pos, "LB 21"); - continue; - } - - if (fHL.contains(prevCharX2) && - (fHY.contains(prevChar) || - (fBA.contains(prevChar) && !feaFWH.contains(prevChar))) && - !fHL.contains(thisChar)) { - setAppliedRule(pos, "LB 21a HL (HY | BA) x [^HL]"); - continue; - } - - if (fSY.contains(prevChar) && fHL.contains(thisChar)) { - setAppliedRule(pos, "LB 21b SY x HL"); - continue; - } - - if (fIN.contains(thisChar)) { - setAppliedRule(pos, "LB 22"); - continue; - } - - // (AL | HL) x NU - // NU x (AL | HL) - if ((fAL.contains(prevChar) || fHL.contains(prevChar)) && fNU.contains(thisChar)) { - setAppliedRule(pos, "LB 23"); - continue; - } - if (fNU.contains(prevChar) && (fAL.contains(thisChar) || fHL.contains(thisChar))) { - setAppliedRule(pos, "LB 23"); - continue; - } - - // Do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes. - // PR x (ID | EB | EM) - // (ID | EB | EM) x PO - if (fPR.contains(prevChar) && - (fID.contains(thisChar) || fEB.contains(thisChar) || fEM.contains(thisChar))) { - setAppliedRule(pos, "LB 23a"); - continue; - } - if ((fID.contains(prevChar) || fEB.contains(prevChar) || fEM.contains(prevChar)) && - fPO.contains(thisChar)) { - setAppliedRule(pos, "LB 23a"); - continue; - } - - // Do not break between prefix and letters or ideographs. - // (PR | PO) x (AL | HL) - // (AL | HL) x (PR | PO) - if ((fPR.contains(prevChar) || fPO.contains(prevChar)) && - (fAL.contains(thisChar) || fHL.contains(thisChar))) { - setAppliedRule(pos, "LB 24 no break between prefix and letters or ideographs"); - continue; - } - if ((fAL.contains(prevChar) || fHL.contains(prevChar)) && - (fPR.contains(thisChar) || fPO.contains(thisChar))) { - setAppliedRule(pos, "LB 24 no break between prefix and letters or ideographs"); - continue; - } - - boolean continueToNextPosition = false; - // LB 25. - for (XUnicodeSet[] pair : new XUnicodeSet[][]{ - new XUnicodeSet[]{fCL, fPO}, // 1. NU (SY | IS)* CL × PO - new XUnicodeSet[]{fCP, fPO}, // 2. NU (SY | IS)* CP × PO - new XUnicodeSet[]{fCL, fPR}, // 3. NU (SY | IS)* CL × PR - new XUnicodeSet[]{fCP, fPR}, // 4. NU (SY | IS)* CP × PR - }) { - XUnicodeSet left = pair[0]; - XUnicodeSet right = pair[1]; - if (left.contains(prevChar) && right.contains(thisChar)) { - // Check for the NU (SY | IS)* part. - boolean leftHandSideMatches = false; - tPos = moveIndex32(fText, prevPos, -1); - for (;;) { - while (tPos > 0 && fCM.contains(fText.codePointAt(tPos))) { - tPos = moveIndex32(fText, tPos, -1); - } - final int tChar = fText.codePointAt(tPos); - if (fSY.contains(tChar) || fIS.contains(tChar)) { - if (tPos == 0) { - leftHandSideMatches = false; - break; - } - tPos = moveIndex32(fText, tPos, -1); - } else if (fNU.contains(tChar)) { - leftHandSideMatches = true; - break; - } else { - leftHandSideMatches = false; - break; - } - } - if (leftHandSideMatches) { - setAppliedRule(pos, "LB 25/1..4"); - continueToNextPosition = true; - break; - } - } - } - if (continueToNextPosition) { - continue; - } - // 5. NU (SY | IS)* × PO - // 6. NU (SY | IS)* × PR - // 13. NU (SY | IS)* × NU - boolean leftHandSideMatches; - tPos = prevPos; - for (;;) { - while (tPos > 0 && fCM.contains(fText.codePointAt(tPos))) { - tPos = moveIndex32(fText, tPos, -1); - } - final int tChar = fText.codePointAt(tPos); - if (fSY.contains(tChar) || fIS.contains(tChar)) { - if (tPos == 0) { - leftHandSideMatches = false; - break; - } - tPos = moveIndex32(fText, tPos, -1); - } else if (fNU.contains(tChar)) { - leftHandSideMatches = true; - break; - } else { - leftHandSideMatches = false; - break; - } - } - if (leftHandSideMatches && - (fPO.contains(thisChar) || fPR.contains(thisChar) || fNU.contains(thisChar))) { - setAppliedRule(pos, "LB 25/5,6,13,14"); - continue; - } - if (nextPos < fText.length()) { - final int nextChar = fText.codePointAt(nextPos); - // 7. PO × OP NU - if (fPO.contains(prevChar) && fOP.contains(thisChar) && fNU.contains(nextChar)) { - setAppliedRule(pos, "LB 25/7"); - continue; - } - // 9. PR × OP NU - if (fPR.contains(prevChar) && fOP.contains(thisChar) && fNU.contains(nextChar)) { - setAppliedRule(pos, "LB 25/9"); - continue; - } - int nextPosX2 = moveIndex32(fText, nextPos, 1); - while (nextPosX2 < fText.length() && fCM.contains(fText.codePointAt(nextPosX2))) { - nextPosX2 = moveIndex32(fText, nextPosX2, 1); - } - - if (nextPosX2 < fText.length()) { - final int nextCharX2 = fText.codePointAt(nextPosX2); - // 7bis. PO × OP IS NU - if (fPO.contains(prevChar) && fOP.contains(thisChar) && fIS.contains(nextChar) && - fNU.contains(nextCharX2)) { - setAppliedRule(pos, "LB 25/7bis"); - continue; - } - // 9bis. PR × OP IS NU - if (fPR.contains(prevChar) && fOP.contains(thisChar) && fIS.contains(nextChar) && - fNU.contains(nextCharX2)) { - setAppliedRule(pos, "LB 25/9bis"); - continue; - } - } - } - for (XUnicodeSet[] pair : new XUnicodeSet[][]{ - new XUnicodeSet[]{fPO, fNU}, // 8. PO × NU - new XUnicodeSet[]{fPR, fNU}, // 10. PR × NU - new XUnicodeSet[]{fHY, fNU}, // 11. HY × NU - new XUnicodeSet[]{fIS, fNU}, // 12. IS × NU - }) { - XUnicodeSet left = pair[0]; - XUnicodeSet right = pair[1]; - if (left.contains(prevChar) && right.contains(thisChar)) { - continueToNextPosition = true; - break; - } - } - if (continueToNextPosition) { - continue; - } - - if (fJL.contains(prevChar) && (fJL.contains(thisChar) || - fJV.contains(thisChar) || - fH2.contains(thisChar) || - fH3.contains(thisChar))) { - setAppliedRule(pos, "LB 26 Do not break a Korean syllable."); - continue; - } - - if ((fJV.contains(prevChar) || fH2.contains(prevChar)) && - (fJV.contains(thisChar) || fJT.contains(thisChar))) { - setAppliedRule(pos, "LB 26 Do not break a Korean syllable."); - continue; - } - - if ((fJT.contains(prevChar) || fH3.contains(prevChar)) && - fJT.contains(thisChar)) { - setAppliedRule(pos, "LB 26 Do not break a Korean syllable."); - continue; - } - - if ((fJL.contains(prevChar) || fJV.contains(prevChar) || - fJT.contains(prevChar) || fH2.contains(prevChar) || fH3.contains(prevChar)) && - fPO.contains(thisChar)) { - setAppliedRule(pos, "LB 27 Treat a Korean Syllable Block the same as ID."); - continue; - } - if (fPR.contains(prevChar) && (fJL.contains(thisChar) || fJV.contains(thisChar) || - fJT.contains(thisChar) || fH2.contains(thisChar) || fH3.contains(thisChar))) { - setAppliedRule(pos, "LB 27 Treat a Korean Syllable Block the same as ID."); - continue; - } - - - - if ((fAL.contains(prevChar) || fHL.contains(prevChar)) && (fAL.contains(thisChar) || fHL.contains(thisChar))) { - setAppliedRule(pos, "LB 28 Do not break between alphabetics"); - continue; - } - - if (fAP.contains(prevChar) && - (fAK.contains(thisChar) || thisChar == '◌' || fAS.contains(thisChar))) { - setAppliedRule(pos, "LB 28a.1 AP x (AK | ◌ | AS)"); - continue; - } - - if ((fAK.contains(prevChar) || prevChar == '◌' || fAS.contains(prevChar)) && - (fVF.contains(thisChar) || fVI.contains(thisChar))) { - setAppliedRule(pos, "LB 28a.2 (AK | ◌ | AS) x (VF | VI)"); - continue; - } - - if ((fAK.contains(prevCharX2) || prevCharX2 == '◌' || fAS.contains(prevCharX2)) && - fVI.contains(prevChar) && - (fAK.contains(thisChar) || thisChar == '◌')) { - setAppliedRule(pos, "LB 28a.3 (AK | ◌ | AS) VI x (AK | ◌)"); - continue; - } - - if (nextPos < fText.length()) { - // note: UnicodeString::char32At(length) returns ffff, not distinguishable - // from a legit ffff noncharacter. So test length separately. - int nextChar = UTF16.charAt(fText, nextPos); - if ((fAK.contains(prevChar) || prevChar == '◌' || fAS.contains(prevChar)) && - (fAK.contains(thisChar) || thisChar == '◌' || fAS.contains(thisChar)) && - fVF.contains(nextChar)) { - setAppliedRule(pos, "LB 28a.4 (AK | ◌ | AS) x (AK | ◌ | AS) VF"); - continue; - } - } - - if (fIS.contains(prevChar) && (fAL.contains(thisChar) || fHL.contains(thisChar))) { - setAppliedRule(pos, "LB 29 Do not break between numeric punctuation and alphabetics"); - continue; - } - - // (AL | NU) x OP - // CP x (AL | NU) - if ((fAL.contains(prevChar) || fHL.contains(prevChar) || fNU.contains(prevChar)) && - fOP30.contains(thisChar)) { - setAppliedRule(pos, "LB 30 Do not break between letters, numbers, or ordinary symbols and opening or closing punctuation."); - continue; - } - if (fCP30.contains(prevChar) && - (fAL.contains(thisChar) || fHL.contains(thisChar) || fNU.contains(thisChar))) { - setAppliedRule(pos, "LB 30 Do not break between letters, numbers, or ordinary symbols and opening or closing punctuation."); - continue; - } - - // RI RI ÷ RI - // RI x RI - if (fRI.contains(prevCharX2) && fRI.contains(prevChar) && fRI.contains(thisChar)) { - setAppliedRule(pos, "LB 30a Break between pairs of Regional Indicators."); - break; - } - if (fRI.contains(prevChar) && fRI.contains(thisChar)) { - // Two Regional Indicators have been paired. - // Over-write the trailing one (thisChar) to prevent it from forming another pair with a - // following RI. This is a hack. - thisChar = -1; - setAppliedRule(pos, "LB 30a Break between pairs of Regional Indicators."); - continue; - } - - // LB30b Do not break between an emoji base (or potential emoji) and an emoji modifier. - if (fEB.contains(prevChar) && fEM.contains(thisChar)) { - setAppliedRule(pos, "LB 30b Emoji Base x Emoji Modifier"); - continue; - } - - if (fExtPictUnassigned.contains(prevChar) && fEM.contains(thisChar)) { - setAppliedRule(pos, "LB30b [\\p{Extended_Pictographic}&\\p{Cn}] × EM"); - continue; - } - - // LB 31 Break everywhere else - setAppliedRule(pos, "LB 31 Break everywhere else"); - break; } - - return pos; + return -1; } diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java index b60c17304c22..c3494e99d98b 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java @@ -15,7 +15,7 @@ enum Resolution { BREAK, NO_BREAK, } - class BreakContext { + static class BreakContext { BreakContext(int index) { indexInRemapped = index; } From 71eb398e97317ccc68f652ad23029ddc119c9fbb Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Thu, 12 Dec 2024 21:55:56 +0100 Subject: [PATCH 09/25] It seems to work --- .../ibm/icu/dev/test/rbbi/RBBITestMonkey.java | 152 ++++++------ .../com/ibm/icu/dev/test/rbbi/RegexRule.java | 175 +++++++------- .../com/ibm/icu/dev/test/rbbi/RemapRule.java | 219 +++++++++--------- .../icu/dev/test/rbbi/SegmentationRule.java | 90 +++++-- 4 files changed, 349 insertions(+), 287 deletions(-) diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java index 564ed350acb3..d522923e8fb1 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java @@ -701,6 +701,14 @@ class NamedSet { List partition = new ArrayList<>(); rules = new ArrayList<>(); + // /$/ matches ( BK | CR | LF | NL ) eot, so in this case we need to apply LB6 before + // LB3 gets incorrectly applied. + rules.add(new RegexRule( + "[^ BK CR LF NL ] × [ BK CR LF NL ] eot", + "[^ \\p{lb=BK} \\p{lb=CR} \\p{lb=LF} \\p{lb=NL} ]", + Resolution.NO_BREAK, + "[ \\p{lb=BK} \\p{lb=CR} \\p{lb=LF} \\p{lb=NL} ] $")); + rules.add(new RegexRule("sot ÷ contra LB2", "^", Resolution.BREAK, "")); // This one could be part of the rules. rules.add(new RegexRule("LB3 ÷ eot", "", Resolution.BREAK, "$")); @@ -708,76 +716,76 @@ class NamedSet { // --- NOLI ME TANGERE --- // Generated by GenerateBreakTest.java in the Unicode tools. - partition.add(new NamedSet("AI_EastAsian", new UnicodeSet("([\\p{Line_Break=Ambiguous}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("AImEastAsian", new UnicodeSet("([\\p{Line_Break=Ambiguous}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("AK", new UnicodeSet("([\\p{Line_Break=Aksara}])"))); - partition.add(new NamedSet("ALorig_EastAsian", new UnicodeSet("([\\p{Line_Break=Alphabetic}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("ALorig_DottedCircle", new UnicodeSet("([\\p{Line_Break=Alphabetic}&[◌]])"))); - partition.add(new NamedSet("ALorigmEastAsianmDottedCircle", new UnicodeSet("([\\p{Line_Break=Alphabetic}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]-[◌]])"))); - partition.add(new NamedSet("AP", new UnicodeSet("([\\p{Line_Break=Aksara_Prebase}])"))); - partition.add(new NamedSet("AS", new UnicodeSet("([\\p{Line_Break=Aksara_Start}])"))); - partition.add(new NamedSet("B2", new UnicodeSet("([\\p{Line_Break=Break_Both}])"))); - partition.add(new NamedSet("BA_EastAsian", new UnicodeSet("([\\p{Line_Break=Break_After}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("BA_Hyphen", new UnicodeSet("([\\p{Line_Break=Break_After}&[\\u2010]])"))); - partition.add(new NamedSet("BAmEastAsianmHyphen", new UnicodeSet("([\\p{Line_Break=Break_After}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]-[\\u2010]])"))); - partition.add(new NamedSet("BB", new UnicodeSet("([\\p{Line_Break=Break_Before}])"))); - partition.add(new NamedSet("BK", new UnicodeSet("([\\p{Line_Break=Mandatory_Break}])"))); - partition.add(new NamedSet("CB", new UnicodeSet("([\\p{Line_Break=Contingent_Break}])"))); - partition.add(new NamedSet("CL_EastAsian", new UnicodeSet("([\\p{Line_Break=Close_Punctuation}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("CLmEastAsian", new UnicodeSet("([\\p{Line_Break=Close_Punctuation}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("CP", new UnicodeSet("([\\p{Line_Break=CP}])"))); - partition.add(new NamedSet("CMorig_EastAsian", new UnicodeSet("([\\p{Line_Break=Combining_Mark}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("CMorigmEastAsian", new UnicodeSet("([\\p{Line_Break=Combining_Mark}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("CR", new UnicodeSet("([\\p{Line_Break=Carriage_Return}])"))); - partition.add(new NamedSet("EX_EastAsian", new UnicodeSet("([\\p{Line_Break=Exclamation}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("EXmEastAsian", new UnicodeSet("([\\p{Line_Break=Exclamation}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("GL_EastAsian", new UnicodeSet("([\\p{Line_Break=Glue}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("GLmEastAsian", new UnicodeSet("([\\p{Line_Break=Glue}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("H2", new UnicodeSet("([\\p{Line_Break=H2}])"))); - partition.add(new NamedSet("H3", new UnicodeSet("([\\p{Line_Break=H3}])"))); - partition.add(new NamedSet("HL", new UnicodeSet("([\\p{Line_Break=HL}])"))); - partition.add(new NamedSet("HY", new UnicodeSet("([\\p{Line_Break=Hyphen}])"))); - partition.add(new NamedSet("ID_EastAsian", new UnicodeSet("([\\p{Line_Break=Ideographic}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("ID_ExtPictUnassigned", new UnicodeSet("([\\p{Line_Break=Ideographic}&[\\p{Extended_Pictographic}&\\p{gc=Cn}]])"))); - partition.add(new NamedSet("IDmEastAsianmExtPictUnassigned", new UnicodeSet("([\\p{Line_Break=Ideographic}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]-[\\p{Extended_Pictographic}&\\p{gc=Cn}]])"))); - partition.add(new NamedSet("IN_EastAsian", new UnicodeSet("([\\p{Line_Break=Inseparable}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("INmEastAsian", new UnicodeSet("([\\p{Line_Break=Inseparable}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("IS", new UnicodeSet("([\\p{Line_Break=Infix_Numeric}])"))); - partition.add(new NamedSet("JL", new UnicodeSet("([\\p{Line_Break=JL}])"))); - partition.add(new NamedSet("JT", new UnicodeSet("([\\p{Line_Break=JT}])"))); - partition.add(new NamedSet("JV", new UnicodeSet("([\\p{Line_Break=JV}])"))); - partition.add(new NamedSet("LF", new UnicodeSet("([\\p{Line_Break=Line_Feed}])"))); - partition.add(new NamedSet("NL", new UnicodeSet("([\\p{Line_Break=Next_Line}])"))); - partition.add(new NamedSet("NSorig_EastAsian", new UnicodeSet("([\\p{Line_Break=Nonstarter}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("NSorigmEastAsian", new UnicodeSet("([\\p{Line_Break=Nonstarter}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("NU", new UnicodeSet("([\\p{Line_Break=Numeric}])"))); - partition.add(new NamedSet("OP_EastAsian", new UnicodeSet("([\\p{Line_Break=Open_Punctuation}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("OPmEastAsian", new UnicodeSet("([\\p{Line_Break=Open_Punctuation}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("PO_EastAsian", new UnicodeSet("([\\p{Line_Break=Postfix_Numeric}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("POmEastAsian", new UnicodeSet("([\\p{Line_Break=Postfix_Numeric}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("PR_EastAsian", new UnicodeSet("([\\p{Line_Break=Prefix_Numeric}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("PRmEastAsian", new UnicodeSet("([\\p{Line_Break=Prefix_Numeric}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("QU_Pi", new UnicodeSet("([\\p{Line_Break=Quotation}&\\p{gc=Pi}])"))); - partition.add(new NamedSet("QU_Pf", new UnicodeSet("([\\p{Line_Break=Quotation}&\\p{gc=Pf}])"))); - partition.add(new NamedSet("QUmPimPf", new UnicodeSet("([\\p{Line_Break=Quotation}-\\p{gc=Pi}-\\p{gc=Pf}])"))); - partition.add(new NamedSet("SA_Mn", new UnicodeSet("([[\\p{Line_Break=Complex_Context}&\\p{gc=Mn}]])"))); - partition.add(new NamedSet("SA_Mc", new UnicodeSet("([[\\p{Line_Break=Complex_Context}&\\p{gc=Mc}]])"))); - partition.add(new NamedSet("SAmMnmMc", new UnicodeSet("([[\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]])"))); - partition.add(new NamedSet("SG", new UnicodeSet("([\\p{Line_Break=Surrogate}])"))); - partition.add(new NamedSet("SP", new UnicodeSet("([\\p{Line_Break=Space}])"))); - partition.add(new NamedSet("SY", new UnicodeSet("([\\p{Line_Break=Break_Symbols}])"))); - partition.add(new NamedSet("VF", new UnicodeSet("([\\p{Line_Break=Virama_Final}])"))); - partition.add(new NamedSet("VI", new UnicodeSet("([\\p{Line_Break=Virama}])"))); - partition.add(new NamedSet("WJ", new UnicodeSet("([\\p{Line_Break=Word_Joiner}])"))); - partition.add(new NamedSet("XX_ExtPictUnassigned", new UnicodeSet("([\\p{Line_Break=Unknown}&[\\p{Extended_Pictographic}&\\p{gc=Cn}]])"))); - partition.add(new NamedSet("XXmExtPictUnassigned", new UnicodeSet("([\\p{Line_Break=Unknown}-[\\p{Extended_Pictographic}&\\p{gc=Cn}]])"))); - partition.add(new NamedSet("ZW", new UnicodeSet("([\\p{Line_Break=ZWSpace}])"))); - partition.add(new NamedSet("CJ", new UnicodeSet("([\\p{Line_Break=Conditional_Japanese_Starter}])"))); - partition.add(new NamedSet("RI", new UnicodeSet("([\\p{Line_Break=Regional_Indicator}])"))); - partition.add(new NamedSet("EB_EastAsian", new UnicodeSet("([\\p{Line_Break=E_Base}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("EBmEastAsian", new UnicodeSet("([\\p{Line_Break=E_Base}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]])"))); - partition.add(new NamedSet("EM", new UnicodeSet("([\\p{Line_Break=E_Modifier}])"))); - partition.add(new NamedSet("ZWJ", new UnicodeSet("([\\p{Line_Break=ZWJ}])"))); + partition.add(new NamedSet("AI_EastAsian", new UnicodeSet("[\\p{Line_Break=Ambiguous}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("AImEastAsian", new UnicodeSet("[\\p{Line_Break=Ambiguous}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("AK", new UnicodeSet("[\\p{Line_Break=Aksara}]"))); + partition.add(new NamedSet("ALorig_EastAsian", new UnicodeSet("[\\p{Line_Break=Alphabetic}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("ALorig_DottedCircle", new UnicodeSet("[\\p{Line_Break=Alphabetic}&[◌]]"))); + partition.add(new NamedSet("ALorigmEastAsianmDottedCircle", new UnicodeSet("[\\p{Line_Break=Alphabetic}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]-[◌]]"))); + partition.add(new NamedSet("AP", new UnicodeSet("[\\p{Line_Break=Aksara_Prebase}]"))); + partition.add(new NamedSet("AS", new UnicodeSet("[\\p{Line_Break=Aksara_Start}]"))); + partition.add(new NamedSet("B2", new UnicodeSet("[\\p{Line_Break=Break_Both}]"))); + partition.add(new NamedSet("BA_EastAsian", new UnicodeSet("[\\p{Line_Break=Break_After}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("BA_Hyphen", new UnicodeSet("[\\p{Line_Break=Break_After}&[\\u2010]]"))); + partition.add(new NamedSet("BAmEastAsianmHyphen", new UnicodeSet("[\\p{Line_Break=Break_After}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]-[\\u2010]]"))); + partition.add(new NamedSet("BB", new UnicodeSet("[\\p{Line_Break=Break_Before}]"))); + partition.add(new NamedSet("BK", new UnicodeSet("[\\p{Line_Break=Mandatory_Break}]"))); + partition.add(new NamedSet("CB", new UnicodeSet("[\\p{Line_Break=Contingent_Break}]"))); + partition.add(new NamedSet("CL_EastAsian", new UnicodeSet("[\\p{Line_Break=Close_Punctuation}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("CLmEastAsian", new UnicodeSet("[\\p{Line_Break=Close_Punctuation}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("CP", new UnicodeSet("[\\p{Line_Break=CP}]"))); + partition.add(new NamedSet("CMorig_EastAsian", new UnicodeSet("[\\p{Line_Break=Combining_Mark}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("CMorigmEastAsian", new UnicodeSet("[\\p{Line_Break=Combining_Mark}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("CR", new UnicodeSet("[\\p{Line_Break=Carriage_Return}]"))); + partition.add(new NamedSet("EX_EastAsian", new UnicodeSet("[\\p{Line_Break=Exclamation}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("EXmEastAsian", new UnicodeSet("[\\p{Line_Break=Exclamation}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("GL_EastAsian", new UnicodeSet("[\\p{Line_Break=Glue}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("GLmEastAsian", new UnicodeSet("[\\p{Line_Break=Glue}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("H2", new UnicodeSet("[\\p{Line_Break=H2}]"))); + partition.add(new NamedSet("H3", new UnicodeSet("[\\p{Line_Break=H3}]"))); + partition.add(new NamedSet("HL", new UnicodeSet("[\\p{Line_Break=HL}]"))); + partition.add(new NamedSet("HY", new UnicodeSet("[\\p{Line_Break=Hyphen}]"))); + partition.add(new NamedSet("ID_EastAsian", new UnicodeSet("[\\p{Line_Break=Ideographic}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("ID_ExtPictUnassigned", new UnicodeSet("[\\p{Line_Break=Ideographic}&[\\p{Extended_Pictographic}&\\p{gc=Cn}]]"))); + partition.add(new NamedSet("IDmEastAsianmExtPictUnassigned", new UnicodeSet("[\\p{Line_Break=Ideographic}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]-[\\p{Extended_Pictographic}&\\p{gc=Cn}]]"))); + partition.add(new NamedSet("IN_EastAsian", new UnicodeSet("[\\p{Line_Break=Inseparable}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("INmEastAsian", new UnicodeSet("[\\p{Line_Break=Inseparable}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("IS", new UnicodeSet("[\\p{Line_Break=Infix_Numeric}]"))); + partition.add(new NamedSet("JL", new UnicodeSet("[\\p{Line_Break=JL}]"))); + partition.add(new NamedSet("JT", new UnicodeSet("[\\p{Line_Break=JT}]"))); + partition.add(new NamedSet("JV", new UnicodeSet("[\\p{Line_Break=JV}]"))); + partition.add(new NamedSet("LF", new UnicodeSet("[\\p{Line_Break=Line_Feed}]"))); + partition.add(new NamedSet("NL", new UnicodeSet("[\\p{Line_Break=Next_Line}]"))); + partition.add(new NamedSet("NSorig_EastAsian", new UnicodeSet("[\\p{Line_Break=Nonstarter}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("NSorigmEastAsian", new UnicodeSet("[\\p{Line_Break=Nonstarter}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("NU", new UnicodeSet("[\\p{Line_Break=Numeric}]"))); + partition.add(new NamedSet("OP_EastAsian", new UnicodeSet("[\\p{Line_Break=Open_Punctuation}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("OPmEastAsian", new UnicodeSet("[\\p{Line_Break=Open_Punctuation}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("PO_EastAsian", new UnicodeSet("[\\p{Line_Break=Postfix_Numeric}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("POmEastAsian", new UnicodeSet("[\\p{Line_Break=Postfix_Numeric}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("PR_EastAsian", new UnicodeSet("[\\p{Line_Break=Prefix_Numeric}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("PRmEastAsian", new UnicodeSet("[\\p{Line_Break=Prefix_Numeric}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("QU_Pi", new UnicodeSet("[\\p{Line_Break=Quotation}&\\p{gc=Pi}]"))); + partition.add(new NamedSet("QU_Pf", new UnicodeSet("[\\p{Line_Break=Quotation}&\\p{gc=Pf}]"))); + partition.add(new NamedSet("QUmPimPf", new UnicodeSet("[\\p{Line_Break=Quotation}-\\p{gc=Pi}-\\p{gc=Pf}]"))); + partition.add(new NamedSet("SA_Mn", new UnicodeSet("[[\\p{Line_Break=Complex_Context}&\\p{gc=Mn}]]"))); + partition.add(new NamedSet("SA_Mc", new UnicodeSet("[[\\p{Line_Break=Complex_Context}&\\p{gc=Mc}]]"))); + partition.add(new NamedSet("SAmMnmMc", new UnicodeSet("[[\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]]"))); + partition.add(new NamedSet("SG", new UnicodeSet("[\\p{Line_Break=Surrogate}]"))); + partition.add(new NamedSet("SP", new UnicodeSet("[\\p{Line_Break=Space}]"))); + partition.add(new NamedSet("SY", new UnicodeSet("[\\p{Line_Break=Break_Symbols}]"))); + partition.add(new NamedSet("VF", new UnicodeSet("[\\p{Line_Break=Virama_Final}]"))); + partition.add(new NamedSet("VI", new UnicodeSet("[\\p{Line_Break=Virama}]"))); + partition.add(new NamedSet("WJ", new UnicodeSet("[\\p{Line_Break=Word_Joiner}]"))); + partition.add(new NamedSet("XX_ExtPictUnassigned", new UnicodeSet("[\\p{Line_Break=Unknown}&[\\p{Extended_Pictographic}&\\p{gc=Cn}]]"))); + partition.add(new NamedSet("XXmExtPictUnassigned", new UnicodeSet("[\\p{Line_Break=Unknown}-[\\p{Extended_Pictographic}&\\p{gc=Cn}]]"))); + partition.add(new NamedSet("ZW", new UnicodeSet("[\\p{Line_Break=ZWSpace}]"))); + partition.add(new NamedSet("CJ", new UnicodeSet("[\\p{Line_Break=Conditional_Japanese_Starter}]"))); + partition.add(new NamedSet("RI", new UnicodeSet("[\\p{Line_Break=Regional_Indicator}]"))); + partition.add(new NamedSet("EB_EastAsian", new UnicodeSet("[\\p{Line_Break=E_Base}&[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("EBmEastAsian", new UnicodeSet("[\\p{Line_Break=E_Base}-[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]"))); + partition.add(new NamedSet("EM", new UnicodeSet("[\\p{Line_Break=E_Modifier}]"))); + partition.add(new NamedSet("ZWJ", new UnicodeSet("[\\p{Line_Break=ZWJ}]"))); rules.add(new RegexRule("$BK ÷", "\\p{Line_Break=Mandatory_Break}", Resolution.BREAK, "")); rules.add(new RegexRule("$CR × $LF", "\\p{Line_Break=Carriage_Return}", Resolution.NO_BREAK, "\\p{Line_Break=Line_Feed}")); @@ -1783,7 +1791,9 @@ void RunMonkey(BreakIterator bi, RBBIMonkeyKind mk, String name, int seed, int int c; // Char from test data for (ci = startContext; ci <= endContext && ci != -1; ci = nextCP(testText, ci)) { - + if (ci == testText.length()) { + break; // TODO(egg): The index dance above seems wrong. + } c = testText.codePointAt(ci); buffer.append((ci == i) ? " --→" : " ") .append(String.format(" %3d : ", ci)) diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RegexRule.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RegexRule.java index b25299c35fb8..cbe59071f08b 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RegexRule.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RegexRule.java @@ -11,96 +11,99 @@ /** * A regex rule expressed as in UAXes #14 and #29. * - * The rule consists of two regexes for context before and after a position in the remapped text, - * and of a resolution (break or not) that applies to the corresponding position in the original + * The rule consists of two regexes for context before and after a position in + * the remapped text, + * and of a resolution (break or not) that applies to the corresponding position + * in the original * string if both match. */ class RegexRule extends SegmentationRule { - RegexRule(String name, String before, Resolution resolution, - String after) { - super(name); - resolution_ = resolution; - before_ = - Pattern.compile(before, Pattern.COMMENTS | Pattern.DOTALL); - endsWithBefore_ = Pattern.compile( - ".*(" + before + ")", Pattern.COMMENTS | Pattern.DOTALL); - after_ = Pattern.compile(after, Pattern.COMMENTS | Pattern.DOTALL); - } + RegexRule(String name, String before, Resolution resolution, + String after) { + super(name); + resolution_ = resolution; + before_ = Pattern.compile(expandUnicodeSets(before), Pattern.COMMENTS | Pattern.DOTALL); + endsWithBefore_ = Pattern.compile( + ".*(" + expandUnicodeSets(before) + ")", Pattern.COMMENTS | Pattern.DOTALL); + after_ = Pattern.compile(expandUnicodeSets(after), Pattern.COMMENTS | Pattern.DOTALL); + } - @Override - void apply(StringBuilder remapped, BreakContext[] resolved) { - // The unicodetools implementation simply tries, for each index, to - // match the string up to the index against /.*(before)/ (with - // `matches`) and the beginning of the string after the index against - // /after/ (with `lookingAt`), but that is very slow, especially for - // nonempty /before/. While the old monkeys are not a production - // implementation, we still do not want them to be too slow, since we - // need to test millions of sample strings. Instead we search for - // /before/ and /after/, and check resulting candidates. This speeds - // things up by a factor of ~40. - // We need to be careful about greedy matching: The first position where - // the rule matches may be before the end of the first /before/ match. - // However, it is both: - // 1. within a /before/ match or at its bounds, - // 2. at the beginning of an /after/ match. - // Further, the /before/ context of the rule matches within the - // aforementioned /before/ match. Note that we need to look for - // overlapping matches, thus calls to `find` are always preceded by a - // reset via `region`. - final Matcher beforeSearch = before_.matcher(remapped); - final Matcher afterSearch = after_.matcher(remapped); - beforeSearch.useAnchoringBounds(false); - afterSearch.useAnchoringBounds(false); - if (beforeSearch.find() && afterSearch.find()) { - for (;;) { - if (afterSearch.start() < beforeSearch.start()) { - afterSearch.region(beforeSearch.start(), remapped.length()); - if (!afterSearch.find()) { - break; - } - } else if (afterSearch.start() > beforeSearch.end()) { - if (beforeSearch.start() == remapped.length()) { - break; - } - beforeSearch.region(remapped.offsetByCodePoints(beforeSearch.start(), 1), - remapped.length()); - if (!beforeSearch.find()) { - break; - } - } else { - final Optional position = Arrays.stream(resolved) - .filter(r -> r.indexInRemapped == afterSearch.start()) - .findFirst(); - if (!position.isPresent()) { - throw new IllegalArgumentException(("Rule " + name() + - " found a break at a position which does not correspond to an index in " + - "the original string")); - } - if (position.get().appliedRule == null && - endsWithBefore_.matcher(remapped) - .useAnchoringBounds(false) - .region(beforeSearch.start(), afterSearch.start()) - .matches()) { - position.get().appliedRule = this; - } - if (afterSearch.start() == remapped.length()) { - break; - } - afterSearch.region(remapped.offsetByCodePoints(afterSearch.start(), 1), - remapped.length()); - if (!afterSearch.find()) { - break; - } - } - } - } - } + @Override + void apply(StringBuilder remapped, BreakContext[] resolved) { + // The unicodetools implementation simply tries, for each index, to + // match the string up to the index against /.*(before)/ (with + // `matches`) and the beginning of the string after the index against + // /after/ (with `lookingAt`), but that is very slow, especially for + // nonempty /before/. While the old monkeys are not a production + // implementation, we still do not want them to be too slow, since we + // need to test millions of sample strings. Instead we search for + // /before/ and /after/, and check resulting candidates. This speeds + // things up by a factor of ~40. + // We need to be careful about greedy matching: The first position where + // the rule matches may be before the end of the first /before/ match. + // However, it is both: + // 1. within a /before/ match or at its bounds, + // 2. at the beginning of an /after/ match. + // Further, the /before/ context of the rule matches within the + // aforementioned /before/ match. Note that we need to look for + // overlapping matches, thus calls to `find` are always preceded by a + // reset via `region`. + final Matcher beforeSearch = before_.matcher(remapped); + final Matcher afterSearch = after_.matcher(remapped); + beforeSearch.useAnchoringBounds(false); + afterSearch.useAnchoringBounds(false); + if (beforeSearch.find() && afterSearch.find()) { + for (;;) { + if (afterSearch.start() < beforeSearch.start()) { + afterSearch.region(beforeSearch.start(), remapped.length()); + if (!afterSearch.find()) { + break; + } + } else if (afterSearch.start() > beforeSearch.end()) { + if (beforeSearch.start() == remapped.length()) { + break; + } + beforeSearch.region(remapped.offsetByCodePoints(beforeSearch.start(), 1), + remapped.length()); + if (!beforeSearch.find()) { + break; + } + } else { + final Optional position = Arrays.stream(resolved) + .filter(r -> r.indexInRemapped != null && r.indexInRemapped == afterSearch.start()) + .findFirst(); + if (!position.isPresent()) { + throw new IllegalArgumentException(("Rule " + name() + + " found a break at a position which does not correspond to an index in " + + "the original string")); + } + if (position.get().appliedRule == null && + endsWithBefore_.matcher(remapped) + .useAnchoringBounds(false) + .region(beforeSearch.start(), afterSearch.start()) + .matches()) { + position.get().appliedRule = this; + } + if (afterSearch.start() == remapped.length()) { + break; + } + afterSearch.region(remapped.offsetByCodePoints(afterSearch.start(), 1), + remapped.length()); + if (!afterSearch.find()) { + break; + } + } + } + } + } - @Override - Resolution resolution() { return resolution_; } + @Override + Resolution resolution() { + return resolution_; + } - private final Pattern before_; - private final Pattern endsWithBefore_; - private final Pattern after_; - private final Resolution resolution_; + private final Pattern before_; + private final Pattern endsWithBefore_; + private final Pattern after_; + private final Resolution resolution_; } \ No newline at end of file diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RemapRule.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RemapRule.java index 5eebd49c675b..03de85c6814b 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RemapRule.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RemapRule.java @@ -9,117 +9,122 @@ /** * A segmentation rule expressed as in UAXes #14 and #29. * - * A remap rule performs normal a regex replacement applied to the remapped string. - * This replacement may use capturing groups. Any positions in the original string that correspond + * A remap rule performs normal a regex replacement applied to the remapped + * string. + * This replacement may use capturing groups. Any positions in the original + * string that correspond * to positions within the replaced text are resolved to NO_BREAK by this rule. */ public class RemapRule extends SegmentationRule { - RemapRule(String name, String pattern, String replacement) { - super(name); - replacement_ = replacement; - pattern_ = Pattern.compile(pattern, Pattern.COMMENTS | Pattern.DOTALL); - } + RemapRule(String name, String pattern, String replacement) { + super(name); + replacement_ = replacement; + pattern_ = Pattern.compile(expandUnicodeSets(pattern), Pattern.COMMENTS | Pattern.DOTALL); + } - @Override - void apply(StringBuilder remapped, BreakContext[] resolved) { - // This one has to be a StringBuffer rather than a StringBuilder because the overload of - // AppendReplacement that takes a StringBuilder is new in Java 9. - StringBuffer result = new StringBuffer(); - int i = 0; - int offset = 0; - // We find all matches of the `pattern_` and replace them according to - // the `replacement_`, producing the new remapped string `result`. - // For every position i in the original string, - // `resolved[i].indexInRemapped` is null if i lies within a replaced - // match, and is set to the new index in `result` otherwise, by adding - // the accumulated difference `offset` between match lengths and - // replacement lengths. - // Consider a 4-codepoint, 6 code unit string s = ⟨ 𒀀, ◌́, ␠, ◌𝅲 ⟩, where - // ␠ stands for U+0020 and U+12000 𒀀 and U+1D172 ◌𝅲 each require two code - // units, and apply the following two rules: - // 1. (?\P{lb=SP}) \p{lb=CM}* → ${X} - // 2. \p{lb=CM} → A - // The string remapped and the indexInRemapped values change as follows: - // indexInRemapped remapped string rule final - // (aligned on the initial string) applied offset - // 𒀀 ◌́ ␠ ◌𝅲 - // 0 1 2 3 4 5 6 ⟨ 𒀀, ◌́, ␠, ◌𝅲 ⟩ (none) - // 0 - - 2 3 4 5 ⟨ 𒀀, ␠, ◌𝅲 ⟩ 1 -1 - // 0 - - 2 3 - 4 ⟨ 𒀀, ␠, A ⟩ 2 -1 - // - // Note that the last indexInRemapped is always equal to the length of - // the remapped string. - final Matcher matcher = pattern_.matcher(remapped); - while (matcher.find()) { - for (;; ++i) { - if (resolved[i].indexInRemapped == null) { - continue; - } - if (resolved[i].indexInRemapped != null && - resolved[i].indexInRemapped > matcher.start()) { - break; - } - resolved[i].indexInRemapped += offset; - } - for (;; ++i) { - if (resolved[i].indexInRemapped == null) { - continue; - } - // Note that - // `*resolved[i].indexInRemapped > matcher.end()` should - // never happen with ordinary rules, but could in principle - // happen with rules that remap to code point sequences, e.g., - // 1. BC → TYZ - // 2. AT → X - // applied to ⟨ A, B, C ⟩: - // indexInRemapped remapped rule - // A B C - // 0 1 2 3 ⟨ A, B, C ⟩ (none) - // 0 1 - 4 ⟨ A, T, Y, Z ⟩ 1 - // 0 - - 3 ⟨ X, Y, Z ⟩ 2 - // Where for the application of rule 2, the match ends at - // position 2 in remapped, which does not correspond to a - // position in the original string. - if (resolved[i].indexInRemapped != null && - resolved[i].indexInRemapped >= matcher.end()) { - break; - } - if (resolved[i].appliedRule != null && - resolved[i].appliedRule.resolution() == Resolution.BREAK) { - throw new IllegalArgumentException( - "Replacement rule at remapped indices " + - matcher.start() + - " sqq. spans a break"); - } - resolved[i].appliedRule = this; - resolved[i].indexInRemapped = null; - } - matcher.appendReplacement(result, replacement_); - offset = result.length() - resolved[i].indexInRemapped; - } - for (; i < resolved.length; ++i) { - if (resolved[i].indexInRemapped == null) { - continue; - } - resolved[i].indexInRemapped += offset; - } - matcher.appendTail(result); - if (resolved[resolved.length - 1].indexInRemapped != result.length()) { - StringBuilder indices = new StringBuilder(); - for (final BreakContext r : resolved) { - indices.append(r.indexInRemapped == null ? "null" : r.indexInRemapped.toString()); - indices.append(","); - } - throw new IllegalArgumentException("Inconsistent indexInRemapped " + indices + " for new remapped string " + - result); - } - remapped.setLength(0); - remapped.append(result); - } + @Override + void apply(StringBuilder remapped, BreakContext[] resolved) { + // This one has to be a StringBuffer rather than a StringBuilder because the + // overload of + // AppendReplacement that takes a StringBuilder is new in Java 9. + StringBuffer result = new StringBuffer(); + int i = 0; + int offset = 0; + // We find all matches of the `pattern_` and replace them according to + // the `replacement_`, producing the new remapped string `result`. + // For every position i in the original string, + // `resolved[i].indexInRemapped` is null if i lies within a replaced + // match, and is set to the new index in `result` otherwise, by adding + // the accumulated difference `offset` between match lengths and + // replacement lengths. + // Consider a 4-codepoint, 6 code unit string s = ⟨ 𒀀, ◌́, ␠, ◌𝅲 ⟩, where + // ␠ stands for U+0020 and U+12000 𒀀 and U+1D172 ◌𝅲 each require two code + // units, and apply the following two rules: + // 1. (?\P{lb=SP}) \p{lb=CM}* → ${X} + // 2. \p{lb=CM} → A + // The string remapped and the indexInRemapped values change as follows: + // indexInRemapped remapped string rule final + // (aligned on the initial string) applied offset + // 𒀀 ◌́ ␠ ◌𝅲 + // 0 1 2 3 4 5 6 ⟨ 𒀀, ◌́, ␠, ◌𝅲 ⟩ (none) + // 0 - - 2 3 4 5 ⟨ 𒀀, ␠, ◌𝅲 ⟩ 1 -1 + // 0 - - 2 3 - 4 ⟨ 𒀀, ␠, A ⟩ 2 -1 + // + // Note that the last indexInRemapped is always equal to the length of + // the remapped string. + final Matcher matcher = pattern_.matcher(remapped); + while (matcher.find()) { + for (;; ++i) { + if (resolved[i].indexInRemapped == null) { + continue; + } + if (resolved[i].indexInRemapped != null && + resolved[i].indexInRemapped > matcher.start()) { + break; + } + resolved[i].indexInRemapped += offset; + } + for (;; ++i) { + if (resolved[i].indexInRemapped == null) { + continue; + } + // Note that + // `*resolved[i].indexInRemapped > matcher.end()` should + // never happen with ordinary rules, but could in principle + // happen with rules that remap to code point sequences, e.g., + // 1. BC → TYZ + // 2. AT → X + // applied to ⟨ A, B, C ⟩: + // indexInRemapped remapped rule + // A B C + // 0 1 2 3 ⟨ A, B, C ⟩ (none) + // 0 1 - 4 ⟨ A, T, Y, Z ⟩ 1 + // 0 - - 3 ⟨ X, Y, Z ⟩ 2 + // Where for the application of rule 2, the match ends at + // position 2 in remapped, which does not correspond to a + // position in the original string. + if (resolved[i].indexInRemapped != null && + resolved[i].indexInRemapped >= matcher.end()) { + break; + } + if (resolved[i].appliedRule != null && + resolved[i].appliedRule.resolution() == Resolution.BREAK) { + throw new IllegalArgumentException( + "Replacement rule at remapped indices " + + matcher.start() + + " sqq. spans a break"); + } + resolved[i].appliedRule = this; + resolved[i].indexInRemapped = null; + } + matcher.appendReplacement(result, replacement_); + offset = result.length() - resolved[i].indexInRemapped; + } + for (; i < resolved.length; ++i) { + if (resolved[i].indexInRemapped == null) { + continue; + } + resolved[i].indexInRemapped += offset; + } + matcher.appendTail(result); + if (resolved[resolved.length - 1].indexInRemapped != result.length()) { + StringBuilder indices = new StringBuilder(); + for (final BreakContext r : resolved) { + indices.append(r.indexInRemapped == null ? "null" : r.indexInRemapped.toString()); + indices.append(","); + } + throw new IllegalArgumentException("Inconsistent indexInRemapped " + indices + " for new remapped string " + + result); + } + remapped.setLength(0); + remapped.append(result); + } - @Override - Resolution resolution() { return Resolution.NO_BREAK; } + @Override + Resolution resolution() { + return Resolution.NO_BREAK; + } - private final Pattern pattern_; - private final String replacement_; + private final Pattern pattern_; + private final String replacement_; } \ No newline at end of file diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java index c3494e99d98b..1200318134b3 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java @@ -3,33 +3,77 @@ package com.ibm.icu.dev.test.rbbi; +import java.text.ParsePosition; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.ibm.icu.impl.Utility; +import com.ibm.icu.text.UnicodeSet; +import com.ibm.icu.text.UTF16; + /** * A segmentation rule expressed as in UAXes #14 and #29. * * Rules are applied sequentially. - * Rules operate on a mutable remapped string (which the caller should initially set to the string - * to be segmented), and can resolve positions in the original string to either BREAK or NO_BREAK. + * Rules operate on a mutable remapped string (which the caller should initially + * set to the string + * to be segmented), and can resolve positions in the original string to either + * BREAK or NO_BREAK. */ public abstract class SegmentationRule { - enum Resolution { - BREAK, - NO_BREAK, - } - static class BreakContext { - BreakContext(int index) { - indexInRemapped = index; - } - Integer indexInRemapped; - SegmentationRule appliedRule = null; - }; - - SegmentationRule(String name) { - name_ = name; - } - - abstract void apply(StringBuilder remapped, BreakContext[] resolved); - abstract Resolution resolution(); - String name() { return name_; } - - private final String name_; + enum Resolution { + BREAK, + NO_BREAK, + } + + static class BreakContext { + BreakContext(int index) { + indexInRemapped = index; + } + + Integer indexInRemapped; + SegmentationRule appliedRule = null; + }; + + SegmentationRule(String name) { + name_ = name; + } + + protected String expandUnicodeSets(String regex) { + // StringBuffer rather than StringBuilder since the Matcher methods that take StringBuilder + // are Java 9. + StringBuffer result = new StringBuffer(); + int i = 0; + while (i < regex.length()) { + if (regex.charAt(i) == '[' || regex.charAt(i) == '\\') { + ParsePosition pp = new ParsePosition(i); + Matcher matcher = nonBMPEscape.matcher( + new UnicodeSet(regex, pp, null) + ._generatePattern(new StringBuffer(), true)); + while (matcher.find()) { + int codePoint = matcher.group().length() == 1 ? matcher.group().codePointAt(0) : Integer.parseInt(matcher.group(1), 16); + matcher.appendReplacement(result, "\\\\u" + Utility.hex(UTF16.getLeadSurrogate(codePoint)) + "\\\\u" + + Utility.hex(UTF16.getTrailSurrogate(codePoint))); + } + matcher.appendTail(result); + i = pp.getIndex(); + } else { + result.append(regex.charAt(i++)); + } + } + return result.toString(); + } + + abstract void apply(StringBuilder remapped, BreakContext[] resolved); + + abstract Resolution resolution(); + + String name() { + return name_; + } + + private final String name_; + + private static Pattern nonBMPEscape = Pattern.compile("\\\\U([0-9A-F]{8})|#"); + } From 9befb3298c99e2d075f40a7c1ae2377cba2d4819 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 00:44:04 +0100 Subject: [PATCH 10/25] Dumber escaping --- .../icu/dev/test/rbbi/SegmentationRule.java | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java index 1200318134b3..e7abdbe7e301 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/SegmentationRule.java @@ -4,11 +4,12 @@ package com.ibm.icu.dev.test.rbbi; import java.text.ParsePosition; -import java.util.regex.Matcher; -import java.util.regex.Pattern; + +import javax.swing.RowFilter.Entry; import com.ibm.icu.impl.Utility; import com.ibm.icu.text.UnicodeSet; +import com.ibm.icu.text.UnicodeSet.EntryRange; import com.ibm.icu.text.UTF16; /** @@ -39,23 +40,34 @@ static class BreakContext { name_ = name; } + // Returns "\\uhhhh" for a BMP code point and "\\uDhhh\\uDhhh" (UTF-16) for other code points. + private String javaUEscape(int codePoint) { + if (codePoint <= 0xFFFF) { + return "\\u" + Utility.hex(codePoint); + } else { + return "\\u" + Utility.hex(UTF16.getLeadSurrogate(codePoint)) + "\\u" + + Utility.hex(UTF16.getTrailSurrogate(codePoint)); + } + } + protected String expandUnicodeSets(String regex) { - // StringBuffer rather than StringBuilder since the Matcher methods that take StringBuilder - // are Java 9. - StringBuffer result = new StringBuffer(); + StringBuilder result = new StringBuilder(); int i = 0; while (i < regex.length()) { if (regex.charAt(i) == '[' || regex.charAt(i) == '\\') { ParsePosition pp = new ParsePosition(i); - Matcher matcher = nonBMPEscape.matcher( - new UnicodeSet(regex, pp, null) - ._generatePattern(new StringBuffer(), true)); - while (matcher.find()) { - int codePoint = matcher.group().length() == 1 ? matcher.group().codePointAt(0) : Integer.parseInt(matcher.group(1), 16); - matcher.appendReplacement(result, "\\\\u" + Utility.hex(UTF16.getLeadSurrogate(codePoint)) + "\\\\u" - + Utility.hex(UTF16.getTrailSurrogate(codePoint))); + final UnicodeSet set = new UnicodeSet(regex, pp, null); + // Escape everything. We could use _generatePattern, but then we would have to + // convert \U escapes to sequences of \‌u escapes, and to escape # ourselves. + result.append('['); + for (EntryRange range : set.ranges()) { + result.append(javaUEscape(range.codepoint)); + if (range.codepointEnd != range.codepoint) { + result.append('-'); + result.append(javaUEscape(range.codepointEnd)); + } } - matcher.appendTail(result); + result.append(']'); i = pp.getIndex(); } else { result.append(regex.charAt(i++)); @@ -73,7 +85,4 @@ String name() { } private final String name_; - - private static Pattern nonBMPEscape = Pattern.compile("\\\\U([0-9A-F]{8})|#"); - } From d428c8dcfa8d4527d1de47201388f0e84720a060 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 00:45:29 +0100 Subject: [PATCH 11/25] =?UTF-8?q?=F0=9F=8D=8E.xml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- icu4j/tools/build/pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/icu4j/tools/build/pom.xml b/icu4j/tools/build/pom.xml index fc844b1ffcd1..3e1ab5dbe7c3 100644 --- a/icu4j/tools/build/pom.xml +++ b/icu4j/tools/build/pom.xml @@ -18,4 +18,14 @@ ${project.basedir}/../.. + + + jdk.tools + jdk.tools + 1.8 + system + ${JAVA_HOME}/lib/tools.jar + + + From 938ef97a067a20e6c1067a2435cc8e5f233f58e1 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 00:55:36 +0100 Subject: [PATCH 12/25] (?!.) ftw --- icu4c/source/test/intltest/rbbitst.cpp | 16 ++++++---------- .../ibm/icu/dev/test/rbbi/RBBITestMonkey.java | 13 +++---------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/icu4c/source/test/intltest/rbbitst.cpp b/icu4c/source/test/intltest/rbbitst.cpp index c043a0a5d838..022f5140b01a 100644 --- a/icu4c/source/test/intltest/rbbitst.cpp +++ b/icu4c/source/test/intltest/rbbitst.cpp @@ -2906,20 +2906,16 @@ RBBILineMonkey::RBBILineMonkey() : std::list> partition; - // TODO(egg): The following two workarounds for what seems to be ICU bugs; - // with UREGEX_DOTALL (but not UREGEX_MULTILINE): - // 1. /.*\u000A/ does not match CR LF; - // 2. /$/ matches ( BK | CR | LF | NL ) eot. + // TODO(egg): The following is a workaround for what seems to be an ICU bug: + // with UREGEX_DOTALL (but not UREGEX_MULTILINE), /.*\u000A/ does not match + // CR LF. rules.push_back(std::make_unique(uR"(CR LF ÷)", uR"(\u000D\u000A)", u'÷', uR"()")); - rules.push_back(std::make_unique( - uR"([^ BK CR LF NL ] × [ BK CR LF NL ] eot)", - uR"([^ \p{lb=BK} \p{lb=CR} \p{lb=LF} \p{lb=NL} ])", - u'×', - uR"([ \p{lb=BK} \p{lb=CR} \p{lb=LF} \p{lb=NL} ] $)")); rules.push_back(std::make_unique(uR"(sot ÷ contra LB2)", uR"(^)", u'÷', uR"()")); // This one could be part of the rules. - rules.push_back(std::make_unique(uR"(LB3 ÷ eot)", uR"()", u'÷', uR"($)")); + // Note that /$/ matches ( BK | CR | LF | NL ) eot, so we use (?!.) instead. + // The generated rules use the same (?!.). + rules.push_back(std::make_unique(uR"(LB3 ÷ eot)", uR"()", u'÷', uR"((?!.))")); // --- NOLI ME TANGERE --- // Generated by GenerateBreakTest.java in the Unicode tools. diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java index d522923e8fb1..87e7e8b4d358 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java @@ -701,18 +701,11 @@ class NamedSet { List partition = new ArrayList<>(); rules = new ArrayList<>(); - // /$/ matches ( BK | CR | LF | NL ) eot, so in this case we need to apply LB6 before - // LB3 gets incorrectly applied. - rules.add(new RegexRule( - "[^ BK CR LF NL ] × [ BK CR LF NL ] eot", - "[^ \\p{lb=BK} \\p{lb=CR} \\p{lb=LF} \\p{lb=NL} ]", - Resolution.NO_BREAK, - "[ \\p{lb=BK} \\p{lb=CR} \\p{lb=LF} \\p{lb=NL} ] $")); - rules.add(new RegexRule("sot ÷ contra LB2", "^", Resolution.BREAK, "")); // This one could be part of the rules. - rules.add(new RegexRule("LB3 ÷ eot", "", Resolution.BREAK, "$")); - + // Note that /$/ matches ( BK | CR | LF | NL ) eot, so we use (?!.) instead. + // The generated rules use the same (?!.). + rules.add(new RegexRule("LB3 ÷ eot", "", Resolution.BREAK, "(?!.)")); // --- NOLI ME TANGERE --- // Generated by GenerateBreakTest.java in the Unicode tools. From fe75c00abcb3a2001707c3692580400c409b006c Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 04:56:30 +0100 Subject: [PATCH 13/25] Greedier regices, prevent remap rules from creating surrogate pairs --- icu4c/source/test/intltest/rbbitst.cpp | 70 ++++++++++++++++++++------ 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/icu4c/source/test/intltest/rbbitst.cpp b/icu4c/source/test/intltest/rbbitst.cpp index c043a0a5d838..a2ff32030d2e 100644 --- a/icu4c/source/test/intltest/rbbitst.cpp +++ b/icu4c/source/test/intltest/rbbitst.cpp @@ -1705,7 +1705,34 @@ class RemapRule : public SegmentationRule { resolved[i].appliedRule = this; resolved[i].indexInRemapped.reset(); } + // While replacing, we need to check that we are not creating + // surrogate pairs. Since appendReplacement performs two + // concatenations (the unreplaced segment and the replacement), we + // need to check in two places: whether the unreplaced segment + // starts with a trailing surrogate that ends up after a leading + // surrogate, and whether the replaced segment starts with a leading + // surrogate that ends up after a trailing surrogate. + // We break the pair by replacing one of the surrogates with U+FFFF, + // which has the same properties for all but line breaking, and the + // same behaviour in line breaking (lb=SG and lb=XX are both treated + // as lb=AL). + std::optional trailing_lead; + if (result.length() > 0 && U16_IS_LEAD(result[result.length() - 1])) { + trailing_lead = result.length() - 1; + } + matcher->appendReplacement(result, replacement_, status); + + if (trailing_lead && *trailing_lead + 1 < result.length() && + U16_IS_TRAIL(result[*trailing_lead + 1])) { + result.setCharAt(*trailing_lead, u'\uFFFF'); + } + + if (matcher->start(status) + offset > 0 && + U16_IS_LEAD(result[matcher->start(status) + offset - 1]) && + U16_IS_TRAIL(result[matcher->start(status) + offset])) { + result.setCharAt(matcher->start(status) + offset, u'\uFFFF'); + } offset = result.length() - *resolved[i].indexInRemapped; } for (; i < static_cast(resolved.size()); ++i) { @@ -1714,7 +1741,17 @@ class RemapRule : public SegmentationRule { } *resolved[i].indexInRemapped += offset; } + + std::optional trailing_lead; + if (result.length() > 0 && U16_IS_LEAD(result[result.length() - 1])) { + trailing_lead = result.length() - 1; + } matcher->appendTail(result); + if (trailing_lead && *trailing_lead + 1 < result.length() && + U16_IS_TRAIL(result[*trailing_lead + 1])) { + result.setCharAt(*trailing_lead, u'\uFFFF'); + } + if (resolved.back().indexInRemapped != result.length()) { std::string indices; for (const auto r : resolved) { @@ -2906,20 +2943,16 @@ RBBILineMonkey::RBBILineMonkey() : std::list> partition; - // TODO(egg): The following two workarounds for what seems to be ICU bugs; - // with UREGEX_DOTALL (but not UREGEX_MULTILINE): - // 1. /.*\u000A/ does not match CR LF; - // 2. /$/ matches ( BK | CR | LF | NL ) eot. + // TODO(egg): The following is a workaround for what seems to be an ICU bug: + // with UREGEX_DOTALL (but not UREGEX_MULTILINE), /.*\u000A/ does not match + // CR LF. rules.push_back(std::make_unique(uR"(CR LF ÷)", uR"(\u000D\u000A)", u'÷', uR"()")); - rules.push_back(std::make_unique( - uR"([^ BK CR LF NL ] × [ BK CR LF NL ] eot)", - uR"([^ \p{lb=BK} \p{lb=CR} \p{lb=LF} \p{lb=NL} ])", - u'×', - uR"([ \p{lb=BK} \p{lb=CR} \p{lb=LF} \p{lb=NL} ] $)")); rules.push_back(std::make_unique(uR"(sot ÷ contra LB2)", uR"(^)", u'÷', uR"()")); // This one could be part of the rules. - rules.push_back(std::make_unique(uR"(LB3 ÷ eot)", uR"()", u'÷', uR"($)")); + // Note that /$/ matches ( BK | CR | LF | NL ) eot, so we use (?!.) instead. + // The generated rules use the same (?!.). + rules.push_back(std::make_unique(uR"(LB3 ÷ eot)", uR"()", u'÷', uR"((?!.))")); // --- NOLI ME TANGERE --- // Generated by GenerateBreakTest.java in the Unicode tools. @@ -3015,7 +3048,7 @@ RBBILineMonkey::RBBILineMonkey() : rules.push_back(std::make_unique(uR"(× $CP)", uR"()", u'×', uR"(\p{Line_Break=CP})")); rules.push_back(std::make_unique(uR"(× $SY)", uR"()", u'×', uR"(\p{Line_Break=Break_Symbols})")); rules.push_back(std::make_unique(uR"($OP $SP* ×)", uR"(\p{Line_Break=Open_Punctuation} \p{Line_Break=Space}*)", u'×', uR"()")); - rules.push_back(std::make_unique(uR"(( $sot | $BK | $CR | $LF | $NL | $OP | $QU | $GL | $SP | $ZW ) $QU_Pi $SP* ×)", uR"(( ^ | \p{Line_Break=Mandatory_Break} | \p{Line_Break=Carriage_Return} | \p{Line_Break=Line_Feed} | \p{Line_Break=Next_Line} | \p{Line_Break=Open_Punctuation} | \p{Line_Break=Quotation} | \p{Line_Break=Glue} | \p{Line_Break=Space} | \p{Line_Break=ZWSpace} ) [\p{Line_Break=Quotation} && \p{gc=Pi}] \p{Line_Break=Space}*)", u'×', uR"()")); + rules.push_back(std::make_unique(uR"(( $BK | $CR | $LF | $NL | $OP | $QU | $GL | $SP | $ZW | $sot ) $QU_Pi $SP* ×)", uR"(( \p{Line_Break=Mandatory_Break} | \p{Line_Break=Carriage_Return} | \p{Line_Break=Line_Feed} | \p{Line_Break=Next_Line} | \p{Line_Break=Open_Punctuation} | \p{Line_Break=Quotation} | \p{Line_Break=Glue} | \p{Line_Break=Space} | \p{Line_Break=ZWSpace} | ^ ) [\p{Line_Break=Quotation} && \p{gc=Pi}] \p{Line_Break=Space}*)", u'×', uR"()")); rules.push_back(std::make_unique(uR"(× $QU_Pf ( $SP | $GL | $WJ | $CL | $QU | $CP | $EX | $IS | $SY | $BK | $CR | $LF | $NL | $ZW | $eot ))", uR"()", u'×', uR"([\p{Line_Break=Quotation} && \p{gc=Pf}] ( \p{Line_Break=Space} | \p{Line_Break=Glue} | \p{Line_Break=Word_Joiner} | \p{Line_Break=Close_Punctuation} | \p{Line_Break=Quotation} | \p{Line_Break=CP} | \p{Line_Break=Exclamation} | \p{Line_Break=Infix_Numeric} | \p{Line_Break=Break_Symbols} | \p{Line_Break=Mandatory_Break} | \p{Line_Break=Carriage_Return} | \p{Line_Break=Line_Feed} | \p{Line_Break=Next_Line} | \p{Line_Break=ZWSpace} | (?!.) ))")); rules.push_back(std::make_unique(uR"($SP ÷ $IS $NU)", uR"(\p{Line_Break=Space})", u'÷', uR"(\p{Line_Break=Infix_Numeric} \p{Line_Break=Numeric})")); rules.push_back(std::make_unique(uR"(× $IS)", uR"()", u'×', uR"(\p{Line_Break=Infix_Numeric})")); @@ -3027,10 +3060,10 @@ RBBILineMonkey::RBBILineMonkey() : rules.push_back(std::make_unique(uR"([^$EastAsian] × $QU)", uR"([^[\p{ea=F}\p{ea=W}\p{ea=H}]])", u'×', uR"(\p{Line_Break=Quotation})")); rules.push_back(std::make_unique(uR"(× $QU ( [^$EastAsian] | $eot ))", uR"()", u'×', uR"(\p{Line_Break=Quotation} ( [^[\p{ea=F}\p{ea=W}\p{ea=H}]] | (?!.) ))")); rules.push_back(std::make_unique(uR"($QU × [^$EastAsian])", uR"(\p{Line_Break=Quotation})", u'×', uR"([^[\p{ea=F}\p{ea=W}\p{ea=H}]])")); - rules.push_back(std::make_unique(uR"(( $sot | [^$EastAsian] ) $QU ×)", uR"(( ^ | [^[\p{ea=F}\p{ea=W}\p{ea=H}]] ) \p{Line_Break=Quotation})", u'×', uR"()")); + rules.push_back(std::make_unique(uR"(( [^$EastAsian] | $sot ) $QU ×)", uR"(( [^[\p{ea=F}\p{ea=W}\p{ea=H}]] | ^ ) \p{Line_Break=Quotation})", u'×', uR"()")); rules.push_back(std::make_unique(uR"(÷ $CB)", uR"()", u'÷', uR"(\p{Line_Break=Contingent_Break})")); rules.push_back(std::make_unique(uR"($CB ÷)", uR"(\p{Line_Break=Contingent_Break})", u'÷', uR"()")); - rules.push_back(std::make_unique(uR"(( $sot | $BK | $CR | $LF | $NL | $SP | $ZW | $CB | $GL ) ( $HY | $Hyphen ) × $AL)", uR"(( ^ | \p{Line_Break=Mandatory_Break} | \p{Line_Break=Carriage_Return} | \p{Line_Break=Line_Feed} | \p{Line_Break=Next_Line} | \p{Line_Break=Space} | \p{Line_Break=ZWSpace} | \p{Line_Break=Contingent_Break} | \p{Line_Break=Glue} ) ( \p{Line_Break=Hyphen} | [\u2010] ))", u'×', uR"([\p{Line_Break=Ambiguous} \p{Line_Break=Alphabetic} \p{Line_Break=Surrogate} \p{Line_Break=Unknown} [\p{Line_Break=Complex_Context}--\p{gc=Mn}--\p{gc=Mc}]])")); + rules.push_back(std::make_unique(uR"(( $BK | $CR | $LF | $NL | $SP | $ZW | $CB | $GL | $sot ) ( $HY | $Hyphen ) × $AL)", uR"(( \p{Line_Break=Mandatory_Break} | \p{Line_Break=Carriage_Return} | \p{Line_Break=Line_Feed} | \p{Line_Break=Next_Line} | \p{Line_Break=Space} | \p{Line_Break=ZWSpace} | \p{Line_Break=Contingent_Break} | \p{Line_Break=Glue} | ^ ) ( \p{Line_Break=Hyphen} | [\u2010] ))", u'×', uR"([\p{Line_Break=Ambiguous} \p{Line_Break=Alphabetic} \p{Line_Break=Surrogate} \p{Line_Break=Unknown} [\p{Line_Break=Complex_Context}--\p{gc=Mn}--\p{gc=Mc}]])")); rules.push_back(std::make_unique(uR"(× $BA)", uR"()", u'×', uR"(\p{Line_Break=Break_After})")); rules.push_back(std::make_unique(uR"(× $HY)", uR"()", u'×', uR"(\p{Line_Break=Hyphen})")); rules.push_back(std::make_unique(uR"(× $NS)", uR"()", u'×', uR"([\p{Line_Break=Nonstarter} \p{Line_Break=Conditional_Japanese_Starter}])")); @@ -3080,6 +3113,7 @@ RBBILineMonkey::RBBILineMonkey() : // --- End of generated code. --- + // TODO(egg): This could just as well be part of the rules… rules.push_back(std::make_unique(uR"(ALL ÷ / ÷ ALL)", uR"()", u'÷', @@ -3122,8 +3156,14 @@ void RBBILineMonkey::setText(const UnicodeString &s) { } for (std::size_t i = 0; i < resolved.size(); ++i) { if (resolved[i].appliedRule == nullptr) { - printf("Failed to resolve at %zu" , i); - std::terminate(); + printf("Failed to resolve at %zu between U+%04X and U+%04X ", i, s.char32At(i-1), s.char32At(i)); + if (resolved[i].indexInRemapped.has_value()) { + printf("which is remapped %zu between U+%04X and U+%04X", *resolved[i].indexInRemapped, + remapped.char32At(*resolved[i].indexInRemapped - 1), + remapped.char32At(*resolved[i].indexInRemapped)); + } + resolved[i].appliedRule = rules[0].get(); + //std::terminate(); } else { setAppliedRule(i, resolved[i].appliedRule->name().c_str()); } From 24ec66fe1c13809a130d0472d90b241fc0b6705d Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 05:09:17 +0100 Subject: [PATCH 14/25] =?UTF-8?q?I=E2=80=99ll=20be=20back?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- icu4c/source/test/intltest/rbbitst.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/icu4c/source/test/intltest/rbbitst.cpp b/icu4c/source/test/intltest/rbbitst.cpp index a2ff32030d2e..a8beefae2f9e 100644 --- a/icu4c/source/test/intltest/rbbitst.cpp +++ b/icu4c/source/test/intltest/rbbitst.cpp @@ -3162,8 +3162,7 @@ void RBBILineMonkey::setText(const UnicodeString &s) { remapped.char32At(*resolved[i].indexInRemapped - 1), remapped.char32At(*resolved[i].indexInRemapped)); } - resolved[i].appliedRule = rules[0].get(); - //std::terminate(); + std::terminate(); } else { setAppliedRule(i, resolved[i].appliedRule->name().c_str()); } From 29351ce7228e668b340b8cfb70281b76204bf94e Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 11:29:14 +0100 Subject: [PATCH 15/25] monkeys --- .../ibm/icu/dev/test/rbbi/RBBITestMonkey.java | 196 ++---------------- 1 file changed, 17 insertions(+), 179 deletions(-) diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java index 87e7e8b4d358..0b4f3f026e07 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java @@ -898,9 +898,6 @@ void setText(StringBuffer s) { } } - - - @Override int next(int startPos) { for (int i = startPos + 1; i < resolved.length; ++i) { @@ -911,167 +908,6 @@ int next(int startPos) { return -1; } - - - // Match the following regular expression in the input text. - // ((PR | PO) CM*)? ((OP | HY) CM*)? (IS CM*)? NU CM* ((NU | IS | SY) CM*) * ((CL | CP) CM*)? (PR | PO) CM*)? - // 0 0 1 4 4 4 5 5 7 7 7 7 9 9 9 11 11 (match states) - // retVals array [0] index of the start of the match, or -1 if no match - // [1] index of first char following the match. - // Can not use Java regex because need supplementary character support, - // and because Unicode char properties version must be the same as in - // the version of ICU being tested. - private int[] LBNumberCheck(StringBuffer s, int startIdx, int[] retVals) { - if (retVals == null) { - retVals = new int[2]; - } - retVals[0] = -1; // Indicates no match. - int matchState = 0; - int idx = startIdx; - - matchLoop: for (idx = startIdx; idx= 7) { - retVals[0] = startIdx; - retVals[1] = idx; - } - return retVals; - } - - @Override List charClasses() { return fSets; @@ -1465,6 +1301,9 @@ private static int m_rand() return (m_seed >>> 16) % 32768; } + private final static String[] monkeys = new String[] { + "🙈", "🙉", "🙊", "🐵", "🐒"}; + // Helper function for formatting error output. // Append a string into a fixed-size field in a StringBuffer. // Blank-pad the string if it is shorter than the field. @@ -1528,6 +1367,7 @@ void RunMonkey(BreakIterator bi, RBBIMonkeyKind mk, String name, int seed, int boolean[] precedingBreaks = new boolean[TESTSTRINGLEN*2 + 1]; int i; int loopCount = 0; + int errorCount = 0; boolean printTestData = false; boolean printBreaksFromBI = false; @@ -1567,16 +1407,13 @@ void RunMonkey(BreakIterator bi, RBBIMonkeyKind mk, String name, int seed, int // For minimizing width of class name output. int classNameSize = mk.maxClassNameSize(); - - int dotsOnLine = 0; while (loopCount < numIterations || numIterations == -1) { if (numIterations == -1 && loopCount % 10 == 0) { // If test is running in an infinite loop, display a periodic tic so // we can tell that it is making progress. - System.out.print("."); - if (dotsOnLine++ >= 80){ - System.out.println(); - dotsOnLine = 0; + System.out.print(monkeys[m_rand() % monkeys.length]); + if (loopCount % 1000_000_000 == 0) { + System.out.println("\nTested " + loopCount / 1000_000_000 + " million random strings with " + errorCount + " errors"); } } // Save current random number seed, so that we can recreate the random numbers @@ -1737,6 +1574,7 @@ void RunMonkey(BreakIterator bi, RBBIMonkeyKind mk, String name, int seed, int } if (errorType != null) { + ++errorCount; // Format a range of the test text that includes the failure as // a data item that can be included in the rbbi test data file. @@ -1779,7 +1617,7 @@ void RunMonkey(BreakIterator bi, RBBIMonkeyKind mk, String name, int seed, int buffer.append("\n") .append((expectedBreaks[i] ? "Break expected but not found." : "Break found but not expected.")) .append( - String.format(" at index %d. Parameters to reproduce: @\"type=%s seed=%d loop=1\"\n", + String.format(" at index %d. Parameters to reproduce: -Dtest=RBBITestMonkey#Test%sMonkey -Dseed=%d -Dloop=1\n", i, name, seed)); int c; // Char from test data @@ -1830,7 +1668,7 @@ public void TestCharMonkey() { RBBICharMonkey m = new RBBICharMonkey(); BreakIterator bi = BreakIterator.getCharacterInstance(Locale.US); - RunMonkey(bi, m, "char", seed, loopCount); + RunMonkey(bi, m, "Char", seed, loopCount); } @Test @@ -1841,7 +1679,7 @@ public void TestWordMonkey() { logln("Word Break Monkey Test"); RBBIWordMonkey m = new RBBIWordMonkey(); BreakIterator bi = BreakIterator.getWordInstance(Locale.US); - RunMonkey(bi, m, "word", seed, loopCount); + RunMonkey(bi, m, "Word", seed, loopCount); } @Test @@ -1853,7 +1691,7 @@ public void TestLineMonkey() { RBBILineMonkey m = new RBBILineMonkey(); BreakIterator bi = BreakIterator.getLineInstance(Locale.US); try { - RunMonkey(bi, m, "line", seed, loopCount); + RunMonkey(bi, m, "Line", seed, loopCount); } catch(IllegalArgumentException e) { if (e.getMessage().equals("Invalid code point U+-000001")) { // Looks like you used class UnicodeSet instead of class XUnicodeSet @@ -1874,7 +1712,7 @@ public void TestSentMonkey() { logln("Sentence Break Monkey Test"); RBBISentenceMonkey m = new RBBISentenceMonkey(); BreakIterator bi = BreakIterator.getSentenceInstance(Locale.US); - RunMonkey(bi, m, "sent", seed, loopCount); + RunMonkey(bi, m, "Sent", seed, loopCount); } // // Round-trip monkey tests. @@ -1895,7 +1733,7 @@ public void TestRTCharMonkey() { BreakIterator bi = BreakIterator.getCharacterInstance(Locale.US); String rules = bi.toString(); BreakIterator rtbi = new RuleBasedBreakIterator(rules); - RunMonkey(rtbi, m, "char", seed, loopCount); + RunMonkey(rtbi, m, "RTChar", seed, loopCount); } @Test @@ -1908,7 +1746,7 @@ public void TestRTWordMonkey() { BreakIterator bi = BreakIterator.getWordInstance(Locale.US); String rules = bi.toString(); BreakIterator rtbi = new RuleBasedBreakIterator(rules); - RunMonkey(rtbi, m, "word", seed, loopCount); + RunMonkey(rtbi, m, "RTWord", seed, loopCount); } @Test @@ -1922,7 +1760,7 @@ public void TestRTLineMonkey() { String rules = bi.toString(); BreakIterator rtbi = new RuleBasedBreakIterator(rules); try { - RunMonkey(rtbi, m, "line", seed, loopCount); + RunMonkey(rtbi, m, "RTLine", seed, loopCount); } catch(IllegalArgumentException e) { if (e.getMessage().equals("Invalid code point U+-000001")) { // Looks like you used class UnicodeSet instead of class XUnicodeSet @@ -1945,6 +1783,6 @@ public void TestRTSentMonkey() { BreakIterator bi = BreakIterator.getSentenceInstance(Locale.US); String rules = bi.toString(); BreakIterator rtbi = new RuleBasedBreakIterator(rules); - RunMonkey(rtbi, m, "sent", seed, loopCount); + RunMonkey(rtbi, m, "RTSent", seed, loopCount); } } From 3decf2cb255e2718894ae5684eb6dc0207d1c13a Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 11:29:58 +0100 Subject: [PATCH 16/25] =?UTF-8?q?=F0=9F=90=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- icu4c/source/test/intltest/rbbitst.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/icu4c/source/test/intltest/rbbitst.cpp b/icu4c/source/test/intltest/rbbitst.cpp index a8beefae2f9e..1009bd4aecf9 100644 --- a/icu4c/source/test/intltest/rbbitst.cpp +++ b/icu4c/source/test/intltest/rbbitst.cpp @@ -1716,16 +1716,16 @@ class RemapRule : public SegmentationRule { // which has the same properties for all but line breaking, and the // same behaviour in line breaking (lb=SG and lb=XX are both treated // as lb=AL). - std::optional trailing_lead; + std::optional trailingLead; if (result.length() > 0 && U16_IS_LEAD(result[result.length() - 1])) { - trailing_lead = result.length() - 1; + trailingLead = result.length() - 1; } matcher->appendReplacement(result, replacement_, status); - if (trailing_lead && *trailing_lead + 1 < result.length() && - U16_IS_TRAIL(result[*trailing_lead + 1])) { - result.setCharAt(*trailing_lead, u'\uFFFF'); + if (trailingLead && *trailingLead + 1 < result.length() && + U16_IS_TRAIL(result[*trailingLead + 1])) { + result.setCharAt(*trailingLead, u'\uFFFF'); } if (matcher->start(status) + offset > 0 && @@ -1742,14 +1742,14 @@ class RemapRule : public SegmentationRule { *resolved[i].indexInRemapped += offset; } - std::optional trailing_lead; + std::optional trailingLead; if (result.length() > 0 && U16_IS_LEAD(result[result.length() - 1])) { - trailing_lead = result.length() - 1; + trailingLead = result.length() - 1; } matcher->appendTail(result); - if (trailing_lead && *trailing_lead + 1 < result.length() && - U16_IS_TRAIL(result[*trailing_lead + 1])) { - result.setCharAt(*trailing_lead, u'\uFFFF'); + if (trailingLead && *trailingLead + 1 < result.length() && + U16_IS_TRAIL(result[*trailingLead + 1])) { + result.setCharAt(*trailingLead, u'\uFFFF'); } if (resolved.back().indexInRemapped != result.length()) { From 1301f94cb3a0b240bb12548383acd4f6c4a101e4 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 11:43:06 +0100 Subject: [PATCH 17/25] Port the surrogate assembly preventer --- .../com/ibm/icu/dev/test/rbbi/RemapRule.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RemapRule.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RemapRule.java index 03de85c6814b..e4bc8e79913b 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RemapRule.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RemapRule.java @@ -97,7 +97,34 @@ void apply(StringBuilder remapped, BreakContext[] resolved) { resolved[i].appliedRule = this; resolved[i].indexInRemapped = null; } + // While replacing, we need to check that we are not creating + // surrogate pairs. Since appendReplacement performs two + // concatenations (the unreplaced segment and the replacement), we + // need to check in two places: whether the unreplaced segment + // starts with a trailing surrogate that ends up after a leading + // surrogate, and whether the replaced segment starts with a leading + // surrogate that ends up after a trailing surrogate. + // We break the pair by replacing one of the surrogates with U+FFFF, + // which has the same properties for all but line breaking, and the + // same behaviour in line breaking (lb=SG and lb=XX are both treated + // as lb=AL). + Integer trailingLead = null; + if (result.length() > 0 && Character.isHighSurrogate(result.charAt(result.length() - 1))) { + trailingLead = result.length() - 1; + } + matcher.appendReplacement(result, replacement_); + + if (trailingLead != null && trailingLead + 1 < result.length() && + Character.isLowSurrogate(result.charAt(trailingLead + 1))) { + result.setCharAt(trailingLead, '\uFFFF'); + } + + if (matcher.start() + offset > 0 && + Character.isHighSurrogate(result.charAt(matcher.start() + offset - 1)) && + Character.isLowSurrogate(result.charAt(matcher.start() + offset))) { + result.setCharAt(matcher.start() + offset, '\uFFFF'); + } offset = result.length() - resolved[i].indexInRemapped; } for (; i < resolved.length; ++i) { @@ -106,7 +133,17 @@ void apply(StringBuilder remapped, BreakContext[] resolved) { } resolved[i].indexInRemapped += offset; } + + Integer trailingLead = null; + if (result.length() > 0 && Character.isHighSurrogate(result.charAt(result.length() - 1))) { + trailingLead = result.length() - 1; + } matcher.appendTail(result); + if (trailingLead != null && trailingLead + 1 < result.length() && + Character.isLowSurrogate(result.charAt(trailingLead + 1))) { + result.setCharAt(trailingLead, '\uFFFF'); + } + if (resolved[resolved.length - 1].indexInRemapped != result.length()) { StringBuilder indices = new StringBuilder(); for (final BreakContext r : resolved) { From eb6c9b1867b0667849887714096f262a028c3c44 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 11:47:27 +0100 Subject: [PATCH 18/25] sot last --- .../test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java index 0b4f3f026e07..5b3489e4369e 100644 --- a/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java +++ b/icu4j/main/core/src/test/java/com/ibm/icu/dev/test/rbbi/RBBITestMonkey.java @@ -801,7 +801,7 @@ class NamedSet { rules.add(new RegexRule("× $CP", "", Resolution.NO_BREAK, "\\p{Line_Break=CP}")); rules.add(new RegexRule("× $SY", "", Resolution.NO_BREAK, "\\p{Line_Break=Break_Symbols}")); rules.add(new RegexRule("$OP $SP* ×", "\\p{Line_Break=Open_Punctuation} \\p{Line_Break=Space}*", Resolution.NO_BREAK, "")); - rules.add(new RegexRule("( $sot | $BK | $CR | $LF | $NL | $OP | $QU | $GL | $SP | $ZW ) $QU_Pi $SP* ×", "( ^ | \\p{Line_Break=Mandatory_Break} | \\p{Line_Break=Carriage_Return} | \\p{Line_Break=Line_Feed} | \\p{Line_Break=Next_Line} | \\p{Line_Break=Open_Punctuation} | \\p{Line_Break=Quotation} | \\p{Line_Break=Glue} | \\p{Line_Break=Space} | \\p{Line_Break=ZWSpace} ) [\\p{Line_Break=Quotation} & \\p{gc=Pi}] \\p{Line_Break=Space}*", Resolution.NO_BREAK, "")); + rules.add(new RegexRule("( $BK | $CR | $LF | $NL | $OP | $QU | $GL | $SP | $ZW | $sot ) $QU_Pi $SP* ×", "( \\p{Line_Break=Mandatory_Break} | \\p{Line_Break=Carriage_Return} | \\p{Line_Break=Line_Feed} | \\p{Line_Break=Next_Line} | \\p{Line_Break=Open_Punctuation} | \\p{Line_Break=Quotation} | \\p{Line_Break=Glue} | \\p{Line_Break=Space} | \\p{Line_Break=ZWSpace} | ^ ) [\\p{Line_Break=Quotation} & \\p{gc=Pi}] \\p{Line_Break=Space}*", Resolution.NO_BREAK, "")); rules.add(new RegexRule("× $QU_Pf ( $SP | $GL | $WJ | $CL | $QU | $CP | $EX | $IS | $SY | $BK | $CR | $LF | $NL | $ZW | $eot )", "", Resolution.NO_BREAK, "[\\p{Line_Break=Quotation} & \\p{gc=Pf}] ( \\p{Line_Break=Space} | \\p{Line_Break=Glue} | \\p{Line_Break=Word_Joiner} | \\p{Line_Break=Close_Punctuation} | \\p{Line_Break=Quotation} | \\p{Line_Break=CP} | \\p{Line_Break=Exclamation} | \\p{Line_Break=Infix_Numeric} | \\p{Line_Break=Break_Symbols} | \\p{Line_Break=Mandatory_Break} | \\p{Line_Break=Carriage_Return} | \\p{Line_Break=Line_Feed} | \\p{Line_Break=Next_Line} | \\p{Line_Break=ZWSpace} | (?!.) )")); rules.add(new RegexRule("$SP ÷ $IS $NU", "\\p{Line_Break=Space}", Resolution.BREAK, "\\p{Line_Break=Infix_Numeric} \\p{Line_Break=Numeric}")); rules.add(new RegexRule("× $IS", "", Resolution.NO_BREAK, "\\p{Line_Break=Infix_Numeric}")); @@ -813,10 +813,10 @@ class NamedSet { rules.add(new RegexRule("[^$EastAsian] × $QU", "[^[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]", Resolution.NO_BREAK, "\\p{Line_Break=Quotation}")); rules.add(new RegexRule("× $QU ( [^$EastAsian] | $eot )", "", Resolution.NO_BREAK, "\\p{Line_Break=Quotation} ( [^[\\p{ea=F}\\p{ea=W}\\p{ea=H}]] | (?!.) )")); rules.add(new RegexRule("$QU × [^$EastAsian]", "\\p{Line_Break=Quotation}", Resolution.NO_BREAK, "[^[\\p{ea=F}\\p{ea=W}\\p{ea=H}]]")); - rules.add(new RegexRule("( $sot | [^$EastAsian] ) $QU ×", "( ^ | [^[\\p{ea=F}\\p{ea=W}\\p{ea=H}]] ) \\p{Line_Break=Quotation}", Resolution.NO_BREAK, "")); + rules.add(new RegexRule("( [^$EastAsian] | $sot ) $QU ×", "( [^[\\p{ea=F}\\p{ea=W}\\p{ea=H}]] | ^ ) \\p{Line_Break=Quotation}", Resolution.NO_BREAK, "")); rules.add(new RegexRule("÷ $CB", "", Resolution.BREAK, "\\p{Line_Break=Contingent_Break}")); rules.add(new RegexRule("$CB ÷", "\\p{Line_Break=Contingent_Break}", Resolution.BREAK, "")); - rules.add(new RegexRule("( $sot | $BK | $CR | $LF | $NL | $SP | $ZW | $CB | $GL ) ( $HY | $Hyphen ) × $AL", "( ^ | \\p{Line_Break=Mandatory_Break} | \\p{Line_Break=Carriage_Return} | \\p{Line_Break=Line_Feed} | \\p{Line_Break=Next_Line} | \\p{Line_Break=Space} | \\p{Line_Break=ZWSpace} | \\p{Line_Break=Contingent_Break} | \\p{Line_Break=Glue} ) ( \\p{Line_Break=Hyphen} | [\\u2010] )", Resolution.NO_BREAK, "[\\p{Line_Break=Ambiguous} \\p{Line_Break=Alphabetic} \\p{Line_Break=Surrogate} \\p{Line_Break=Unknown} [\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]]")); + rules.add(new RegexRule("( $BK | $CR | $LF | $NL | $SP | $ZW | $CB | $GL | $sot ) ( $HY | $Hyphen ) × $AL", "( \\p{Line_Break=Mandatory_Break} | \\p{Line_Break=Carriage_Return} | \\p{Line_Break=Line_Feed} | \\p{Line_Break=Next_Line} | \\p{Line_Break=Space} | \\p{Line_Break=ZWSpace} | \\p{Line_Break=Contingent_Break} | \\p{Line_Break=Glue} | ^ ) ( \\p{Line_Break=Hyphen} | [\\u2010] )", Resolution.NO_BREAK, "[\\p{Line_Break=Ambiguous} \\p{Line_Break=Alphabetic} \\p{Line_Break=Surrogate} \\p{Line_Break=Unknown} [\\p{Line_Break=Complex_Context}-\\p{gc=Mn}-\\p{gc=Mc}]]")); rules.add(new RegexRule("× $BA", "", Resolution.NO_BREAK, "\\p{Line_Break=Break_After}")); rules.add(new RegexRule("× $HY", "", Resolution.NO_BREAK, "\\p{Line_Break=Hyphen}")); rules.add(new RegexRule("× $NS", "", Resolution.NO_BREAK, "[\\p{Line_Break=Nonstarter} \\p{Line_Break=Conditional_Japanese_Starter}]")); From b102ed7c35340481d07df166209afe4562f051ed Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 11:56:36 +0100 Subject: [PATCH 19/25] Joys of 4-space indent --- icu4c/source/test/intltest/rbbitst.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/icu4c/source/test/intltest/rbbitst.cpp b/icu4c/source/test/intltest/rbbitst.cpp index 1009bd4aecf9..2ef039405f5e 100644 --- a/icu4c/source/test/intltest/rbbitst.cpp +++ b/icu4c/source/test/intltest/rbbitst.cpp @@ -1724,7 +1724,7 @@ class RemapRule : public SegmentationRule { matcher->appendReplacement(result, replacement_, status); if (trailingLead && *trailingLead + 1 < result.length() && - U16_IS_TRAIL(result[*trailingLead + 1])) { + U16_IS_TRAIL(result[*trailingLead + 1])) { result.setCharAt(*trailingLead, u'\uFFFF'); } @@ -1748,7 +1748,7 @@ class RemapRule : public SegmentationRule { } matcher->appendTail(result); if (trailingLead && *trailingLead + 1 < result.length() && - U16_IS_TRAIL(result[*trailingLead + 1])) { + U16_IS_TRAIL(result[*trailingLead + 1])) { result.setCharAt(*trailingLead, u'\uFFFF'); } From e0332c777013400968e5d383c8554013db91912a Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 14:55:52 +0100 Subject: [PATCH 20/25] charred monkey --- icu4c/source/test/intltest/rbbitst.cpp | 1354 ++++-------------------- 1 file changed, 235 insertions(+), 1119 deletions(-) diff --git a/icu4c/source/test/intltest/rbbitst.cpp b/icu4c/source/test/intltest/rbbitst.cpp index 2ef039405f5e..9e3e69b2d461 100644 --- a/icu4c/source/test/intltest/rbbitst.cpp +++ b/icu4c/source/test/intltest/rbbitst.cpp @@ -1494,104 +1494,6 @@ void RBBITest::checkUnicodeTestCase(const char *testFileName, int lineNumber, #if !UCONFIG_NO_REGULAR_EXPRESSIONS -//--------------------------------------------------------------------------------------- -// -// class RBBIMonkeyKind -// -// Monkey Test for Break Iteration -// Abstract interface class. Concrete derived classes independently -// implement the break rules for different iterator types. -// -// The Monkey Test itself uses doesn't know which type of break iterator it is -// testing, but works purely in terms of the interface defined here. -// -//--------------------------------------------------------------------------------------- -class RBBIMonkeyKind { -public: - // Return a UVector of UnicodeSets, representing the character classes used - // for this type of iterator. - virtual const std::vector& charClasses() = 0; - - // Set the test text on which subsequent calls to next() will operate - virtual void setText(const UnicodeString &s) = 0; - - // Find the next break position, starting from the prev break position, or from zero. - // Return -1 after reaching end of string. - virtual int32_t next(int32_t i) = 0; - - // Name of each character class, parallel with charClasses. Used for debugging output - // of characters. - virtual std::vector& characterClassNames(); - - void setAppliedRule(int32_t position, const char* value); - - std::string getAppliedRule(int32_t position); - - virtual ~RBBIMonkeyKind(); - UErrorCode deferredStatus; - - std::string classNameFromCodepoint(const UChar32 c); - unsigned int maxClassNameSize(); - - protected: - RBBIMonkeyKind(); - std::vector classNames; - std::vector appliedRules; - - // Clear `appliedRules` and fill it with empty strings in the size of test text. - void prepareAppliedRules(int32_t size ); - - private: - -}; - -RBBIMonkeyKind::RBBIMonkeyKind() { - deferredStatus = U_ZERO_ERROR; -} - -RBBIMonkeyKind::~RBBIMonkeyKind() { -} - -std::vector& RBBIMonkeyKind::characterClassNames() { - return classNames; -} - -void RBBIMonkeyKind::prepareAppliedRules(int32_t size) { - // Remove all the information in the `appliedRules`. - appliedRules.clear(); - appliedRules.resize(size + 1); -} - -void RBBIMonkeyKind::setAppliedRule(int32_t position, const char* value) { - appliedRules[position] = value; -} - -std::string RBBIMonkeyKind::getAppliedRule(int32_t position){ - return appliedRules[position]; -} - -std::string RBBIMonkeyKind::classNameFromCodepoint(const UChar32 c) { - // Simply iterate through charClasses to find character's class - for (std::size_t aClassNum = 0; aClassNum < charClasses().size(); aClassNum++) { - const UnicodeSet& classSet = charClasses()[aClassNum]; - if (classSet.contains(c)) { - return classNames[aClassNum]; - } - } - U_ASSERT(false); // This should not happen. - return "bad class name"; -} - -unsigned int RBBIMonkeyKind::maxClassNameSize() { - unsigned int maxSize = 0; - for (std::size_t aClassNum = 0; aClassNum < charClasses().size(); aClassNum++) { - auto aClassNumSize = static_cast(classNames[aClassNum].size()); - if (aClassNumSize > maxSize) { - maxSize = aClassNumSize; - } - } - return maxSize; -} namespace { @@ -1697,7 +1599,7 @@ class RemapRule : public SegmentationRule { break; } if (resolved[i].appliedRule != nullptr && - resolved[i].appliedRule->resolution() == BREAK) { + resolved[i].appliedRule->resolution() == BREAK) { printf("Replacement rule at remapped indices %d sqq. spans a break", matcher->start(status)); std::terminate(); @@ -1724,13 +1626,13 @@ class RemapRule : public SegmentationRule { matcher->appendReplacement(result, replacement_, status); if (trailingLead && *trailingLead + 1 < result.length() && - U16_IS_TRAIL(result[*trailingLead + 1])) { + U16_IS_TRAIL(result[*trailingLead + 1])) { result.setCharAt(*trailingLead, u'\uFFFF'); } if (matcher->start(status) + offset > 0 && - U16_IS_LEAD(result[matcher->start(status) + offset - 1]) && - U16_IS_TRAIL(result[matcher->start(status) + offset])) { + U16_IS_LEAD(result[matcher->start(status) + offset - 1]) && + U16_IS_TRAIL(result[matcher->start(status) + offset])) { result.setCharAt(matcher->start(status) + offset, u'\uFFFF'); } offset = result.length() - *resolved[i].indexInRemapped; @@ -1748,7 +1650,7 @@ class RemapRule : public SegmentationRule { } matcher->appendTail(result); if (trailingLead && *trailingLead + 1 < result.length() && - U16_IS_TRAIL(result[*trailingLead + 1])) { + U16_IS_TRAIL(result[*trailingLead + 1])) { result.setCharAt(*trailingLead, u'\uFFFF'); } @@ -1887,13 +1789,160 @@ class RegexRule : public SegmentationRule { const Resolution resolution_; }; -} // namespace +} // namespace + +//--------------------------------------------------------------------------------------- +// +// class RBBIMonkeyKind +// +// Monkey Test for Break Iteration +// Abstract interface class. Concrete derived classes independently +// implement the break rules for different iterator types. +// +// The Monkey Test itself uses doesn't know which type of break iterator it is +// testing, but works purely in terms of the interface defined here. +// +//--------------------------------------------------------------------------------------- +class RBBIMonkeyKind { + public: + // Return a vector of UnicodeSets, representing the character classes used + // for this type of iterator. + const std::vector &charClasses(); + + // Set the test text on which subsequent calls to next() will operate + void setText(const UnicodeString &s); + + // Find the next break position, starting from the prev break position, or from zero. + // Return -1 after reaching end of string. + int32_t next(int32_t i); + + // Name of each character class, parallel with charClasses. Used for debugging output + // of characters. + std::vector &characterClassNames(); + + void setAppliedRule(int32_t position, const char *value); + + std::string getAppliedRule(int32_t position); + + virtual ~RBBIMonkeyKind(); + UErrorCode deferredStatus; + + std::string classNameFromCodepoint(const UChar32 c); + unsigned int maxClassNameSize(); + + protected: + RBBIMonkeyKind(); + std::vector classNames; + std::vector sets; + std::vector> rules; + + // Clear `appliedRules` and fill it with empty strings in the size of test text. + void prepareAppliedRules(int32_t size); + + private: + std::vector appliedRules; + UnicodeString text; + std::vector resolved; +}; + +RBBIMonkeyKind::RBBIMonkeyKind() { + deferredStatus = U_ZERO_ERROR; +} + +RBBIMonkeyKind::~RBBIMonkeyKind() { +} + +const std::vector &RBBIMonkeyKind::charClasses() { + return sets; +} + +void RBBIMonkeyKind::setText(const UnicodeString &s) { + text = s; + prepareAppliedRules(s.length()); + UnicodeString remapped = s; + resolved.clear(); + resolved.reserve(s.length() + 1); + for (int i = 0; i < s.length() + 1; ++i) { + resolved.emplace_back(i); + } + for (const auto &rule : rules) { + rule->apply(remapped, resolved); + } + for (std::size_t i = 0; i < resolved.size(); ++i) { + if (resolved[i].appliedRule == nullptr) { + if (i > 0 && U16_IS_LEAD(s[i-1]) && U16_IS_TRAIL(s[i])) { + continue; + } + printf("Failed to resolve at %zu between U+%04X and U+%04X ", i, s.char32At(i - 1), + s.char32At(i)); + if (resolved[i].indexInRemapped.has_value()) { + printf("which is remapped %zu between U+%04X and U+%04X", *resolved[i].indexInRemapped, + remapped.char32At(*resolved[i].indexInRemapped - 1), + remapped.char32At(*resolved[i].indexInRemapped)); + } + std::terminate(); + } else { + setAppliedRule(i, resolved[i].appliedRule->name().c_str()); + } + } +} + +int32_t RBBIMonkeyKind::next(int32_t startPos) { + for (std::size_t i = startPos + 1; i < resolved.size(); ++i) { + if (resolved[i].appliedRule != nullptr && + resolved[i].appliedRule->resolution() == SegmentationRule::BREAK) { + return i; + } + } + return -1; +} + +std::vector &RBBIMonkeyKind::characterClassNames() { + return classNames; +} + +void RBBIMonkeyKind::prepareAppliedRules(int32_t size) { + // Remove all the information in the `appliedRules`. + appliedRules.clear(); + appliedRules.resize(size + 1); +} + +void RBBIMonkeyKind::setAppliedRule(int32_t position, const char* value) { + appliedRules[position] = value; +} + +std::string RBBIMonkeyKind::getAppliedRule(int32_t position){ + return appliedRules[position]; +} + +std::string RBBIMonkeyKind::classNameFromCodepoint(const UChar32 c) { + // Simply iterate through charClasses to find character's class + for (std::size_t aClassNum = 0; aClassNum < sets.size(); aClassNum++) { + const UnicodeSet &classSet = sets[aClassNum]; + if (classSet.contains(c)) { + return classNames[aClassNum]; + } + } + U_ASSERT(false); // This should not happen. + return "bad class name"; +} + +unsigned int RBBIMonkeyKind::maxClassNameSize() { + unsigned int maxSize = 0; + for (std::size_t aClassNum = 0; aClassNum < classNames.size(); aClassNum++) { + auto aClassNumSize = static_cast(classNames[aClassNum].size()); + if (aClassNumSize > maxSize) { + maxSize = aClassNumSize; + } + } + return maxSize; +} //---------------------------------------------------------------------------------------- // // Random Numbers. We need a long cycle length since we run overnight tests over // millions of strings involving 1000 random generations per string -// (a 32-bit LCG will not do!), we want and a reasonably small state +// (a 32-bit LCG will not do!), and we want a reasonably small state // so that we can output it to reproduce failures. // //--------------------------------------------------------------------------------------- @@ -1926,652 +1975,103 @@ std::string serialize(const RandomNumberGenerator& generator) { class RBBICharMonkey: public RBBIMonkeyKind { public: RBBICharMonkey(); - virtual ~RBBICharMonkey(); - virtual const std::vector& charClasses() override; - virtual void setText(const UnicodeString &s) override; - virtual int32_t next(int32_t i) override; -private: - std::vector sets; - - UnicodeSet *fCRLFSet; - UnicodeSet *fControlSet; - UnicodeSet *fExtendSet; - UnicodeSet *fZWJSet; - UnicodeSet *fRegionalIndicatorSet; - UnicodeSet *fPrependSet; - UnicodeSet *fSpacingSet; - UnicodeSet *fLSet; - UnicodeSet *fVSet; - UnicodeSet *fTSet; - UnicodeSet *fLVSet; - UnicodeSet *fLVTSet; - UnicodeSet *fHangulSet; - UnicodeSet *fExtendedPictSet; - UnicodeSet *fInCBLinkerSet; - UnicodeSet *fInCBConsonantSet; - UnicodeSet *fInCBExtendSet; - UnicodeSet *fAnySet; - - const UnicodeString *fText; }; RBBICharMonkey::RBBICharMonkey() { - UErrorCode status = U_ZERO_ERROR; - - fText = nullptr; - - fCRLFSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\r\\n]"), status); - fControlSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[[\\p{Grapheme_Cluster_Break = Control}]]"), status); - fExtendSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[[\\p{Grapheme_Cluster_Break = Extend}]]"), status); - fZWJSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Grapheme_Cluster_Break = ZWJ}]"), status); - fRegionalIndicatorSet = - new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Grapheme_Cluster_Break = Regional_Indicator}]"), status); - fPrependSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Grapheme_Cluster_Break = Prepend}]"), status); - fSpacingSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Grapheme_Cluster_Break = SpacingMark}]"), status); - fLSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Grapheme_Cluster_Break = L}]"), status); - fVSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Grapheme_Cluster_Break = V}]"), status); - fTSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Grapheme_Cluster_Break = T}]"), status); - fLVSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Grapheme_Cluster_Break = LV}]"), status); - fLVTSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Grapheme_Cluster_Break = LVT}]"), status); - fHangulSet = new UnicodeSet(); - fHangulSet->addAll(*fLSet); - fHangulSet->addAll(*fVSet); - fHangulSet->addAll(*fTSet); - fHangulSet->addAll(*fLVSet); - fHangulSet->addAll(*fLVTSet); - - fExtendedPictSet = new UnicodeSet(u"[:Extended_Pictographic:]", status); - fInCBLinkerSet = new UnicodeSet(u"[\\p{InCB=Linker}]", status); - fInCBConsonantSet = new UnicodeSet(u"[\\p{InCB=Consonant}]", status); - fInCBExtendSet = new UnicodeSet(u"[\\p{InCB=Extend}]", status); - fAnySet = new UnicodeSet(0, 0x10ffff); - - // Create sets of characters, and add the names of the above character sets. - // In each new ICU release, add new names corresponding to the sets above. - - // Important: Keep class names the same as the class contents. - // TODO(egg): Use logic similar to line breaking. - sets.emplace_back(*fCRLFSet); classNames.emplace_back("CRLF"); - sets.emplace_back(*fControlSet); classNames.emplace_back("Control"); - sets.emplace_back(*fExtendSet); classNames.emplace_back("Extended"); - sets.emplace_back(*fRegionalIndicatorSet); classNames.emplace_back("RegionalIndicator"); - if (!fPrependSet->isEmpty()) { - sets.emplace_back(*fPrependSet); classNames.emplace_back("Prepend"); - } - sets.emplace_back(*fSpacingSet); classNames.emplace_back("Spacing"); - sets.emplace_back(*fHangulSet); classNames.emplace_back("Hangul"); - sets.emplace_back(*fZWJSet); classNames.emplace_back("ZWJ"); - sets.emplace_back(*fExtendedPictSet); classNames.emplace_back("ExtendedPict"); - sets.emplace_back(*fInCBLinkerSet); classNames.emplace_back("InCB=Linker"); - sets.emplace_back(*fInCBConsonantSet); classNames.emplace_back("InCB=Consonant"); - sets.emplace_back(*fInCBExtendSet); classNames.emplace_back("InCB=Extend"); - sets.emplace_back(*fAnySet); classNames.emplace_back("Any"); - - if (U_FAILURE(status)) { - deferredStatus = status; - } -} - - -void RBBICharMonkey::setText(const UnicodeString &s) { - fText = &s; - prepareAppliedRules(s.length()); -} + UErrorCode status = U_ZERO_ERROR; + std::list> partition; + // TODO(egg): The following is a workaround for what seems to be an ICU bug: + // with UREGEX_DOTALL (but not UREGEX_MULTILINE), /.*\u000A/ does not match + // CR LF. + rules.push_back(std::make_unique(uR"(CR LF ÷)", uR"(\u000D\u000A)", u'÷', uR"()")); -int32_t RBBICharMonkey::next(int32_t prevPos) { - int p0, p1, p2, p3; // Indices of the significant code points around the - // break position being tested. The candidate break - // location is before p2. + // These two could be part of the rules. + rules.push_back(std::make_unique(uR"(GB1 sot ÷ Any)", uR"(^)", u'÷', uR"()")); + // Note that /$/ matches ( BK | CR | LF | NL ) eot, so we use (?!.) instead. + // The generated rules use the same (?!.). + rules.push_back(std::make_unique(uR"(GB2 Any ÷ eot)", uR"()", u'÷', uR"((?!.))")); - int breakPos = -1; + // --- NOLI ME TANGERE --- + // Generated by GenerateBreakTest.java in the Unicode tools. + partition.emplace_back("CR", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=CR}])", status)); + partition.emplace_back("LF", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=LF}])", status)); + partition.emplace_back("Control", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=Control}])", status)); + partition.emplace_back("Extend_ConjunctLinker", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=Extend}&\p{Indic_Conjunct_Break=Linker}])", status)); + partition.emplace_back("Extend_ConjunctExtendermConjunctLinker", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=Extend}&[\p{Indic_Conjunct_Break=Linker}\p{Indic_Conjunct_Break=Extend}]-\p{Indic_Conjunct_Break=Linker}])", status)); + partition.emplace_back("ExtendmConjunctLinkermConjunctExtender", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=Extend}-\p{Indic_Conjunct_Break=Linker}-[\p{Indic_Conjunct_Break=Linker}\p{Indic_Conjunct_Break=Extend}]])", status)); + partition.emplace_back("ZWJ", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=ZWJ}])", status)); + partition.emplace_back("RI", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=Regional_Indicator}])", status)); + partition.emplace_back("Prepend", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=Prepend}])", status)); + partition.emplace_back("SpacingMark", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=SpacingMark}])", status)); + partition.emplace_back("L", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=L}])", status)); + partition.emplace_back("V", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=V}])", status)); + partition.emplace_back("T", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=T}])", status)); + partition.emplace_back("LV", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=LV}])", status)); + partition.emplace_back("LVT", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=LVT}])", status)); + partition.emplace_back("LinkingConsonant", UnicodeSet(uR"([\p{Indic_Conjunct_Break=Consonant}])", status)); + partition.emplace_back("ExtPict", UnicodeSet(uR"([\p{Extended_Pictographic}])", status)); + partition.emplace_back("XXmLinkingConsonantmExtPict", UnicodeSet(uR"([\p{Grapheme_Cluster_Break=Other}-\p{Indic_Conjunct_Break=Consonant}-\p{Extended_Pictographic}])", status)); + + rules.push_back(std::make_unique(uR"($CR × $LF)", uR"(\p{Grapheme_Cluster_Break=CR})", u'×', uR"(\p{Grapheme_Cluster_Break=LF})")); + rules.push_back(std::make_unique(uR"(( $Control | $CR | $LF ) ÷)", uR"(( \p{Grapheme_Cluster_Break=Control} | \p{Grapheme_Cluster_Break=CR} | \p{Grapheme_Cluster_Break=LF} ))", u'÷', uR"()")); + rules.push_back(std::make_unique(uR"(÷ ( $Control | $CR | $LF ))", uR"()", u'÷', uR"(( \p{Grapheme_Cluster_Break=Control} | \p{Grapheme_Cluster_Break=CR} | \p{Grapheme_Cluster_Break=LF} ))")); + rules.push_back(std::make_unique(uR"($L × ( $L | $V | $LV | $LVT ))", uR"(\p{Grapheme_Cluster_Break=L})", u'×', uR"(( \p{Grapheme_Cluster_Break=L} | \p{Grapheme_Cluster_Break=V} | \p{Grapheme_Cluster_Break=LV} | \p{Grapheme_Cluster_Break=LVT} ))")); + rules.push_back(std::make_unique(uR"(( $LV | $V ) × ( $V | $T ))", uR"(( \p{Grapheme_Cluster_Break=LV} | \p{Grapheme_Cluster_Break=V} ))", u'×', uR"(( \p{Grapheme_Cluster_Break=V} | \p{Grapheme_Cluster_Break=T} ))")); + rules.push_back(std::make_unique(uR"(( $LVT | $T) × $T)", uR"(( \p{Grapheme_Cluster_Break=LVT} | \p{Grapheme_Cluster_Break=T}))", u'×', uR"(\p{Grapheme_Cluster_Break=T})")); + rules.push_back(std::make_unique(uR"(× ($Extend | $ZWJ))", uR"()", u'×', uR"((\p{Grapheme_Cluster_Break=Extend} | \p{Grapheme_Cluster_Break=ZWJ}))")); + rules.push_back(std::make_unique(uR"(× $SpacingMark)", uR"()", u'×', uR"(\p{Grapheme_Cluster_Break=SpacingMark})")); + rules.push_back(std::make_unique(uR"($Prepend ×)", uR"(\p{Grapheme_Cluster_Break=Prepend})", u'×', uR"()")); + rules.push_back(std::make_unique(uR"($LinkingConsonant $ConjunctExtender* $ConjunctLinker $ConjunctExtender* × $LinkingConsonant)", uR"(\p{Indic_Conjunct_Break=Consonant} [\p{Indic_Conjunct_Break=Linker}\p{Indic_Conjunct_Break=Extend}]* \p{Indic_Conjunct_Break=Linker} [\p{Indic_Conjunct_Break=Linker}\p{Indic_Conjunct_Break=Extend}]*)", u'×', uR"(\p{Indic_Conjunct_Break=Consonant})")); + rules.push_back(std::make_unique(uR"($ExtPict $Extend* $ZWJ × $ExtPict)", uR"(\p{Extended_Pictographic} \p{Grapheme_Cluster_Break=Extend}* \p{Grapheme_Cluster_Break=ZWJ})", u'×', uR"(\p{Extended_Pictographic})")); + rules.push_back(std::make_unique(uR"(^ ($RI $RI)* $RI × $RI)", uR"(^ (\p{Grapheme_Cluster_Break=Regional_Indicator} \p{Grapheme_Cluster_Break=Regional_Indicator})* \p{Grapheme_Cluster_Break=Regional_Indicator})", u'×', uR"(\p{Grapheme_Cluster_Break=Regional_Indicator})")); + rules.push_back(std::make_unique(uR"([^$RI] ($RI $RI)* $RI × $RI)", uR"([^\p{Grapheme_Cluster_Break=Regional_Indicator}] (\p{Grapheme_Cluster_Break=Regional_Indicator} \p{Grapheme_Cluster_Break=Regional_Indicator})* \p{Grapheme_Cluster_Break=Regional_Indicator})", u'×', uR"(\p{Grapheme_Cluster_Break=Regional_Indicator})")); + // --- End of generated code. --- - UChar32 c0, c1, c2, c3; // The code points at p0, p1, p2 & p3. - UChar32 cBase; // for (X Extend*) patterns, the X character. + // TODO(egg): This could just as well be part of the rules… + rules.push_back(std::make_unique(uR"(ALL ÷ / ÷ ALL)", uR"()", u'÷', uR"()")); - if (U_FAILURE(deferredStatus)) { - return -1; + const UnicodeSet lbSA(uR"(\p{lb=SA})", status); + for (auto it = partition.begin(); it != partition.end();) { + if (lbSA.containsAll(it->second)) { + it = partition.erase(it); + } else { + ++it; + } } - // Previous break at end of string. return DONE. - if (prevPos >= fText->length()) { - return -1; + for (const auto &[name, set] : partition) { + sets.push_back(set); + classNames.push_back(name); } - p0 = p1 = p2 = p3 = prevPos; - c3 = fText->char32At(prevPos); - c0 = c1 = c2 = cBase = 0; - (void)p0; // suppress set but not used warning. - (void)c0; + if (U_FAILURE(status)) { + deferredStatus = status; + } +} - // Loop runs once per "significant" character position in the input text. - for (;;) { - // Move all of the positions forward in the input string. - p0 = p1; c0 = c1; - p1 = p2; c1 = c2; - p2 = p3; c2 = c3; - - // Advance p3 by one codepoint - p3 = fText->moveIndex32(p3, 1); - c3 = fText->char32At(p3); - - if (p1 == p2) { - // Still warming up the loop. (won't work with zero length strings, but we don't care) - continue; - } - - if (p2 == fText->length()) { - setAppliedRule(p2, "End of String"); - break; - } - - // No Extend or Format characters may appear between the CR and LF, - // which requires the additional check for p2 immediately following p1. - // - if (c1==0x0D && c2==0x0A && p1==(p2-1)) { - setAppliedRule(p2, "GB3 CR x LF"); - continue; - } - - if (fControlSet->contains(c1) || - c1 == 0x0D || - c1 == 0x0A) { - setAppliedRule(p2, "GB4 ( Control | CR | LF ) "); - break; - } - - if (fControlSet->contains(c2) || - c2 == 0x0D || - c2 == 0x0A) { - setAppliedRule(p2, "GB5 ( Control | CR | LF )"); - break; - } - - if (fLSet->contains(c1) && - (fLSet->contains(c2) || - fVSet->contains(c2) || - fLVSet->contains(c2) || - fLVTSet->contains(c2))) { - setAppliedRule(p2, "GB6 L x ( L | V | LV | LVT )"); - continue; - } - - if ((fLVSet->contains(c1) || fVSet->contains(c1)) && - (fVSet->contains(c2) || fTSet->contains(c2))) { - setAppliedRule(p2, "GB7 ( LV | V ) x ( V | T )"); - continue; - } - - if ((fLVTSet->contains(c1) || fTSet->contains(c1)) && - fTSet->contains(c2)) { - setAppliedRule(p2, "GB8 ( LVT | T) x T"); - continue; - } - - if (fExtendSet->contains(c2) || fZWJSet->contains(c2)) { - if (!fExtendSet->contains(c1)) { - cBase = c1; - } - setAppliedRule(p2, "GB9 x (Extend | ZWJ)"); - continue; - } - - if (fSpacingSet->contains(c2)) { - setAppliedRule(p2, "GB9a x SpacingMark"); - continue; - } - - if (fPrependSet->contains(c1)) { - setAppliedRule(p2, "GB9b Prepend x"); - continue; - } - - if (fInCBConsonantSet->contains(c2)) { - int pi = p1; - bool sawVirama = false; - while (pi > 0 && (fInCBExtendSet->contains(fText->char32At(pi)) || - fInCBLinkerSet->contains(fText->char32At(pi)))) { - if (fInCBLinkerSet->contains(fText->char32At(pi))) { - sawVirama = true; - } - pi = fText->moveIndex32(pi, -1); - } - if (sawVirama && fInCBConsonantSet->contains(fText->char32At(pi))) { - setAppliedRule( - p2, R"(GB9c \p{InCB=Consonant} [ \p{InCB=Extend} \p{InCB=Linker} ]* \p{InCB=Linker} [ \p{InCB=Extend} \p{InCB=Linker} ]* x \p{InCB=Consonant})"); - continue; - } - } - - if (fExtendedPictSet->contains(cBase) && fZWJSet->contains(c1) && fExtendedPictSet->contains(c2)) { - setAppliedRule(p2, "GB11 Extended_Pictographic Extend * ZWJ x Extended_Pictographic"); - continue; - } - - // Note: The first if condition is a little tricky. We only need to force - // a break if there are three or more contiguous RIs. If there are - // only two, a break following will occur via other rules, and will include - // any trailing extend characters, which is needed behavior. - if (fRegionalIndicatorSet->contains(c0) && fRegionalIndicatorSet->contains(c1) - && fRegionalIndicatorSet->contains(c2)) { - setAppliedRule(p2, "GB12-13 Regional_Indicator x Regional_Indicator"); - break; - } - if (fRegionalIndicatorSet->contains(c1) && fRegionalIndicatorSet->contains(c2)) { - setAppliedRule(p2, "GB12-13 Regional_Indicator x Regional_Indicator"); - continue; - } - - setAppliedRule(p2, "GB999 Any Any"); - break; - } - - breakPos = p2; - return breakPos; -} - - - -const std::vector& RBBICharMonkey::charClasses() { - return sets; -} - -RBBICharMonkey::~RBBICharMonkey() { - delete fCRLFSet; - delete fControlSet; - delete fExtendSet; - delete fRegionalIndicatorSet; - delete fPrependSet; - delete fSpacingSet; - delete fLSet; - delete fVSet; - delete fTSet; - delete fLVSet; - delete fLVTSet; - delete fHangulSet; - delete fAnySet; - delete fZWJSet; - delete fExtendedPictSet; - delete fInCBLinkerSet; - delete fInCBConsonantSet; - delete fInCBExtendSet; -} - -//------------------------------------------------------------------------------------------ -// -// class RBBIWordMonkey Word Break specific implementation -// of RBBIMonkeyKind. -// -//------------------------------------------------------------------------------------------ -class RBBIWordMonkey: public RBBIMonkeyKind { -public: - RBBIWordMonkey(); - virtual ~RBBIWordMonkey(); - virtual const std::vector& charClasses() override; - virtual void setText(const UnicodeString &s) override; - virtual int32_t next(int32_t i) override; -private: - std::vector sets; - - UnicodeSet *fCRSet; - UnicodeSet *fLFSet; - UnicodeSet *fNewlineSet; - UnicodeSet *fRegionalIndicatorSet; - UnicodeSet *fKatakanaSet; - UnicodeSet *fHebrew_LetterSet; - UnicodeSet *fALetterSet; - UnicodeSet *fSingle_QuoteSet; - UnicodeSet *fDouble_QuoteSet; - UnicodeSet *fMidNumLetSet; - UnicodeSet *fMidLetterSet; - UnicodeSet *fMidNumSet; - UnicodeSet *fNumericSet; - UnicodeSet *fFormatSet; - UnicodeSet *fOtherSet = nullptr; - UnicodeSet *fExtendSet; - UnicodeSet *fExtendNumLetSet; - UnicodeSet *fWSegSpaceSet; - UnicodeSet *fDictionarySet = nullptr; - UnicodeSet *fZWJSet; - UnicodeSet *fExtendedPictSet; - - const UnicodeString *fText; -}; - - -RBBIWordMonkey::RBBIWordMonkey() -{ - UErrorCode status = U_ZERO_ERROR; - - fCRSet = new UnicodeSet(u"[\\p{Word_Break = CR}]", status); - fLFSet = new UnicodeSet(u"[\\p{Word_Break = LF}]", status); - fNewlineSet = new UnicodeSet(u"[\\p{Word_Break = Newline}]", status); - fKatakanaSet = new UnicodeSet(u"[\\p{Word_Break = Katakana}]", status); - fRegionalIndicatorSet = new UnicodeSet(u"[\\p{Word_Break = Regional_Indicator}]", status); - fHebrew_LetterSet = new UnicodeSet(u"[\\p{Word_Break = Hebrew_Letter}]", status); - fALetterSet = new UnicodeSet(u"[\\p{Word_Break = ALetter}]", status); - fSingle_QuoteSet = new UnicodeSet(u"[\\p{Word_Break = Single_Quote}]", status); - fDouble_QuoteSet = new UnicodeSet(u"[\\p{Word_Break = Double_Quote}]", status); - fMidNumLetSet = new UnicodeSet(u"[\\p{Word_Break = MidNumLet}]", status); - fMidLetterSet = new UnicodeSet(u"[\\p{Word_Break = MidLetter}]", status); - fMidNumSet = new UnicodeSet(u"[\\p{Word_Break = MidNum}]", status); - fNumericSet = new UnicodeSet(u"[\\p{Word_Break = Numeric}]", status); - fFormatSet = new UnicodeSet(u"[\\p{Word_Break = Format}]", status); - fExtendNumLetSet = new UnicodeSet(u"[\\p{Word_Break = ExtendNumLet}]", status); - // There are some sc=Hani characters with WB=Extend. - // The break rules need to pick one or the other because - // Extend overlapping with something else is messy. - // For Unicode 13, we chose to keep U+16FF0 & U+16FF1 - // in $Han (for $dictionary) and out of $Extend. - fExtendSet = new UnicodeSet(u"[\\p{Word_Break = Extend}-[:Hani:]]", status); - fWSegSpaceSet = new UnicodeSet(u"[\\p{Word_Break = WSegSpace}]", status); - - fZWJSet = new UnicodeSet(u"[\\p{Word_Break = ZWJ}]", status); - fExtendedPictSet = new UnicodeSet(u"[:Extended_Pictographic:]", status); - if(U_FAILURE(status)) { - IntlTest::gTest->errln("%s:%d %s", __FILE__, __LINE__, u_errorName(status)); - deferredStatus = status; - return; - } - - fDictionarySet = new UnicodeSet(u"[[\\uac00-\\ud7a3][:Han:][:Hiragana:]]", status); - fDictionarySet->addAll(*fKatakanaSet); - fDictionarySet->addAll(UnicodeSet(u"[\\p{LineBreak = Complex_Context}]", status)); - - fALetterSet->removeAll(*fDictionarySet); - - fOtherSet = new UnicodeSet(); - if(U_FAILURE(status)) { - IntlTest::gTest->errln("%s:%d %s", __FILE__, __LINE__, u_errorName(status)); - deferredStatus = status; - return; - } - - fOtherSet->complement(); - fOtherSet->removeAll(*fCRSet); - fOtherSet->removeAll(*fLFSet); - fOtherSet->removeAll(*fNewlineSet); - fOtherSet->removeAll(*fKatakanaSet); - fOtherSet->removeAll(*fHebrew_LetterSet); - fOtherSet->removeAll(*fALetterSet); - fOtherSet->removeAll(*fSingle_QuoteSet); - fOtherSet->removeAll(*fDouble_QuoteSet); - fOtherSet->removeAll(*fMidLetterSet); - fOtherSet->removeAll(*fMidNumSet); - fOtherSet->removeAll(*fNumericSet); - fOtherSet->removeAll(*fExtendNumLetSet); - fOtherSet->removeAll(*fWSegSpaceSet); - fOtherSet->removeAll(*fFormatSet); - fOtherSet->removeAll(*fExtendSet); - fOtherSet->removeAll(*fRegionalIndicatorSet); - fOtherSet->removeAll(*fZWJSet); - fOtherSet->removeAll(*fExtendedPictSet); - - // Inhibit dictionary characters from being tested at all. - fOtherSet->removeAll(*fDictionarySet); - - // Add classes and their names - sets.emplace_back(*fCRSet); classNames.emplace_back("CR"); - sets.emplace_back(*fLFSet); classNames.emplace_back("LF"); - sets.emplace_back(*fNewlineSet); classNames.emplace_back("Newline"); - sets.emplace_back(*fRegionalIndicatorSet); classNames.emplace_back("RegionalIndicator"); - sets.emplace_back(*fHebrew_LetterSet); classNames.emplace_back("Hebrew"); - sets.emplace_back(*fALetterSet); classNames.emplace_back("ALetter"); - sets.emplace_back(*fSingle_QuoteSet); classNames.emplace_back("Single Quote"); - sets.emplace_back(*fDouble_QuoteSet); classNames.emplace_back("Double Quote"); - // Omit Katakana from fSets, which omits Katakana characters - // from the test data. They are all in the dictionary set, - // which this (old, to be retired) monkey test cannot handle. - //sets.emplace_back(*fKatakanaSet); - - sets.emplace_back(*fMidLetterSet); classNames.emplace_back("MidLetter"); - sets.emplace_back(*fMidNumLetSet); classNames.emplace_back("MidNumLet"); - sets.emplace_back(*fMidNumSet); classNames.emplace_back("MidNum"); - sets.emplace_back(*fNumericSet); classNames.emplace_back("Numeric"); - sets.emplace_back(*fFormatSet); classNames.emplace_back("Format"); - sets.emplace_back(*fExtendSet); classNames.emplace_back("Extend"); - sets.emplace_back(*fOtherSet); classNames.emplace_back("Other"); - sets.emplace_back(*fExtendNumLetSet); classNames.emplace_back("ExtendNumLet"); - sets.emplace_back(*fWSegSpaceSet); classNames.emplace_back("WSegSpace"); - - sets.emplace_back(*fZWJSet); classNames.emplace_back("ZWJ"); - sets.emplace_back(*fExtendedPictSet); classNames.emplace_back("ExtendedPict"); - - if (U_FAILURE(status)) { - deferredStatus = status; - } -} - -void RBBIWordMonkey::setText(const UnicodeString &s) { - fText = &s; - prepareAppliedRules(s.length()); -} - - -int32_t RBBIWordMonkey::next(int32_t prevPos) { - int p0, p1, p2, p3; // Indices of the significant code points around the - // break position being tested. The candidate break - // location is before p2. - - int breakPos = -1; - - UChar32 c0, c1, c2, c3; // The code points at p0, p1, p2 & p3. - - if (U_FAILURE(deferredStatus)) { - return -1; - } - - // Prev break at end of string. return DONE. - if (prevPos >= fText->length()) { - return -1; - } - p0 = p1 = p2 = p3 = prevPos; - c3 = fText->char32At(prevPos); - c0 = c1 = c2 = 0; - (void)p0; // Suppress set but not used warning. - - // Loop runs once per "significant" character position in the input text. - for (;;) { - // Move all of the positions forward in the input string. - p0 = p1; c0 = c1; - p1 = p2; c1 = c2; - p2 = p3; c2 = c3; - - // Advance p3 by X(Extend | Format)* Rule 4 - // But do not advance over Extend & Format following a new line. (Unicode 5.1 change) - do { - p3 = fText->moveIndex32(p3, 1); - c3 = fText->char32At(p3); - if (fCRSet->contains(c2) || fLFSet->contains(c2) || fNewlineSet->contains(c2)) { - break; - } - } - while (fFormatSet->contains(c3) || fExtendSet->contains(c3) || fZWJSet->contains(c3)); - - - if (p1 == p2) { - // Still warming up the loop. (won't work with zero length strings, but we don't care) - continue; - } - - if (p2 == fText->length()) { - // Reached end of string. Always a break position. - break; - } - - // No Extend or Format characters may appear between the CR and LF, - // which requires the additional check for p2 immediately following p1. - // - if (c1==0x0D && c2==0x0A) { - setAppliedRule(p2, "WB3 CR x LF"); - continue; - } - - if (fCRSet->contains(c1) || fLFSet->contains(c1) || fNewlineSet->contains(c1)) { - setAppliedRule(p2, "WB3a Break before and after newlines (including CR and LF)"); - break; - } - if (fCRSet->contains(c2) || fLFSet->contains(c2) || fNewlineSet->contains(c2)) { - setAppliedRule(p2, "WB3a Break before and after newlines (including CR and LF)"); - break; - } - - // Not ignoring extend chars, so peek into input text to - // get the potential ZWJ, the character immediately preceding c2. - // Sloppy UChar32 indexing: p2-1 may reference trail half - // but char32At will get the full code point. - if (fZWJSet->contains(fText->char32At(p2 - 1)) && fExtendedPictSet->contains(c2)){ - setAppliedRule(p2, "WB3c ZWJ x Extended_Pictographic"); - continue; - } - - if (fWSegSpaceSet->contains(fText->char32At(p2-1)) && fWSegSpaceSet->contains(c2)) { - setAppliedRule(p2, "WB3d Keep horizontal whitespace together."); - continue; - } - - if ((fALetterSet->contains(c1) || fHebrew_LetterSet->contains(c1)) && - (fALetterSet->contains(c2) || fHebrew_LetterSet->contains(c2))) { - setAppliedRule(p2, "WB4 (ALetter | Hebrew_Letter) x (ALetter | Hebrew_Letter)"); - continue; - } - - if ( (fALetterSet->contains(c1) || fHebrew_LetterSet->contains(c1)) && - (fMidLetterSet->contains(c2) || fMidNumLetSet->contains(c2) || fSingle_QuoteSet->contains(c2)) && - (fALetterSet->contains(c3) || fHebrew_LetterSet->contains(c3))) { - setAppliedRule(p2, - "WB6 (ALetter | Hebrew_Letter) x (MidLetter | MidNumLet | Single_Quote) (ALetter _Letter)"); - continue; - } - - if ((fALetterSet->contains(c0) || fHebrew_LetterSet->contains(c0)) && - (fMidLetterSet->contains(c1) || fMidNumLetSet->contains(c1) || fSingle_QuoteSet->contains(c1)) && - (fALetterSet->contains(c2) || fHebrew_LetterSet->contains(c2))) { - setAppliedRule(p2, - "WB7 (ALetter | Hebrew_Letter) (MidLetter | MidNumLet | Single_Quote) x (ALetter | Hebrew_Letter)"); - continue; - } - - if (fHebrew_LetterSet->contains(c1) && fSingle_QuoteSet->contains(c2)) { - setAppliedRule(p2, "WB7a Hebrew_Letter x Single_Quote"); - continue; - } - - if (fHebrew_LetterSet->contains(c1) && fDouble_QuoteSet->contains(c2) && fHebrew_LetterSet->contains(c3)) { - setAppliedRule(p2, "WB7b Hebrew_Letter x Double_Quote Hebrew_Letter"); - continue; - } - - if (fHebrew_LetterSet->contains(c0) && fDouble_QuoteSet->contains(c1) && fHebrew_LetterSet->contains(c2)) { - setAppliedRule(p2, "WB7c Hebrew_Letter Double_Quote x Hebrew_Letter"); - continue; - } - - if (fNumericSet->contains(c1) && - fNumericSet->contains(c2)) { - setAppliedRule(p2, "WB8 Numeric x Numeric"); - continue; - } - - if ((fALetterSet->contains(c1) || fHebrew_LetterSet->contains(c1)) && - fNumericSet->contains(c2)) { - setAppliedRule(p2, "WB9 (ALetter | Hebrew_Letter) x Numeric"); - continue; - } - - if (fNumericSet->contains(c1) && - (fALetterSet->contains(c2) || fHebrew_LetterSet->contains(c2))) { - setAppliedRule(p2, "WB10 Numeric x (ALetter | Hebrew_Letter)"); - continue; - } - - if (fNumericSet->contains(c0) && - (fMidNumSet->contains(c1) || fMidNumLetSet->contains(c1) || fSingle_QuoteSet->contains(c1)) && - fNumericSet->contains(c2)) { - setAppliedRule(p2, "WB11 Numeric (MidNum | MidNumLet | Single_Quote) x Numeric"); - continue; - } - - if (fNumericSet->contains(c1) && - (fMidNumSet->contains(c2) || fMidNumLetSet->contains(c2) || fSingle_QuoteSet->contains(c2)) && - fNumericSet->contains(c3)) { - setAppliedRule(p2, "WB12 Numeric x (MidNum | MidNumLet | SingleQuote) Numeric"); - continue; - } - - // Note: matches UAX 29 rules, but doesn't come into play for ICU because - // all Katakana are handled by the dictionary breaker. - if (fKatakanaSet->contains(c1) && - fKatakanaSet->contains(c2)) { - setAppliedRule(p2, "WB13 Katakana x Katakana"); - continue; - } - - if ((fALetterSet->contains(c1) || fHebrew_LetterSet->contains(c1) ||fNumericSet->contains(c1) || - fKatakanaSet->contains(c1) || fExtendNumLetSet->contains(c1)) && - fExtendNumLetSet->contains(c2)) { - setAppliedRule(p2, - "WB13a (ALetter | Hebrew_Letter | Numeric | KataKana | ExtendNumLet) x ExtendNumLet"); - continue; - } - - if (fExtendNumLetSet->contains(c1) && - (fALetterSet->contains(c2) || fHebrew_LetterSet->contains(c2) || - fNumericSet->contains(c2) || fKatakanaSet->contains(c2))) { - setAppliedRule(p2, "WB13b ExtendNumLet x (ALetter | Hebrew_Letter | Numeric | Katakana)"); - continue; - } - - if (fRegionalIndicatorSet->contains(c0) && fRegionalIndicatorSet->contains(c1)) { - setAppliedRule(p2, "WB15 - WB17 Group pairs of Regional Indicators."); - break; - } - if (fRegionalIndicatorSet->contains(c1) && fRegionalIndicatorSet->contains(c2)) { - setAppliedRule(p2, "WB15 - WB17 Group pairs of Regional Indicators."); - continue; - } - - setAppliedRule(p2, "WB999"); - break; - } - - breakPos = p2; - return breakPos; -} - - -const std::vector& RBBIWordMonkey::charClasses() { - return sets; -} - -RBBIWordMonkey::~RBBIWordMonkey() { - delete fCRSet; - delete fLFSet; - delete fNewlineSet; - delete fKatakanaSet; - delete fHebrew_LetterSet; - delete fALetterSet; - delete fSingle_QuoteSet; - delete fDouble_QuoteSet; - delete fMidNumLetSet; - delete fMidLetterSet; - delete fMidNumSet; - delete fNumericSet; - delete fFormatSet; - delete fExtendSet; - delete fExtendNumLetSet; - delete fWSegSpaceSet; - delete fRegionalIndicatorSet; - delete fDictionarySet; - delete fOtherSet; - delete fZWJSet; - delete fExtendedPictSet; -} +//------------------------------------------------------------------------------------------ +// +// class RBBIWordMonkey Word Break specific implementation +// of RBBIMonkeyKind. +// +//------------------------------------------------------------------------------------------ +class RBBIWordMonkey: public RBBIMonkeyKind { +public: + RBBIWordMonkey(); +}; +RBBIWordMonkey::RBBIWordMonkey() +{ + UErrorCode status = U_ZERO_ERROR; + UnicodeSet dictionarySet = UnicodeSet(uR"([[\uac00-\ud7a3][:Han:][:Hiragana:]])", status); + dictionarySet.addAll(UnicodeSet(uR"([\p{Word_Break = Katakana}])", status)); + dictionarySet.addAll(UnicodeSet(uR"([\p{LineBreak = Complex_Context}])", status)); +} //------------------------------------------------------------------------------------------ // @@ -2582,323 +2082,12 @@ RBBIWordMonkey::~RBBIWordMonkey() { class RBBISentMonkey: public RBBIMonkeyKind { public: RBBISentMonkey(); - virtual ~RBBISentMonkey(); - virtual const std::vector& charClasses() override; - virtual void setText(const UnicodeString &s) override; - virtual int32_t next(int32_t i) override; -private: - int moveBack(int posFrom); - int moveForward(int posFrom); - UChar32 cAt(int pos); - - std::vector sets; - - UnicodeSet *fSepSet; - UnicodeSet *fFormatSet; - UnicodeSet *fSpSet; - UnicodeSet *fLowerSet; - UnicodeSet *fUpperSet; - UnicodeSet *fOLetterSet; - UnicodeSet *fNumericSet; - UnicodeSet *fATermSet; - UnicodeSet *fSContinueSet; - UnicodeSet *fSTermSet; - UnicodeSet *fCloseSet; - UnicodeSet *fOtherSet; - UnicodeSet *fExtendSet; - - const UnicodeString *fText; }; RBBISentMonkey::RBBISentMonkey() { UErrorCode status = U_ZERO_ERROR; - - // Separator Set Note: Beginning with Unicode 5.1, CR and LF were removed from the separator - // set and made into character classes of their own. For the monkey impl, - // they remain in SEP, since Sep always appears with CR and LF in the rules. - fSepSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Sentence_Break = Sep} \\u000a \\u000d]"), status); - fFormatSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Sentence_Break = Format}]"), status); - fSpSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Sentence_Break = Sp}]"), status); - fLowerSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Sentence_Break = Lower}]"), status); - fUpperSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Sentence_Break = Upper}]"), status); - fOLetterSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Sentence_Break = OLetter}]"), status); - fNumericSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Sentence_Break = Numeric}]"), status); - fATermSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Sentence_Break = ATerm}]"), status); - fSContinueSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Sentence_Break = SContinue}]"), status); - fSTermSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Sentence_Break = STerm}]"), status); - fCloseSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Sentence_Break = Close}]"), status); - fExtendSet = new UnicodeSet(UNICODE_STRING_SIMPLE("[\\p{Sentence_Break = Extend}]"), status); - fOtherSet = new UnicodeSet(); - - if(U_FAILURE(status)) { - deferredStatus = status; - return; - } - - fOtherSet->complement(); - fOtherSet->removeAll(*fSepSet); - fOtherSet->removeAll(*fFormatSet); - fOtherSet->removeAll(*fSpSet); - fOtherSet->removeAll(*fLowerSet); - fOtherSet->removeAll(*fUpperSet); - fOtherSet->removeAll(*fOLetterSet); - fOtherSet->removeAll(*fNumericSet); - fOtherSet->removeAll(*fATermSet); - fOtherSet->removeAll(*fSContinueSet); - fOtherSet->removeAll(*fSTermSet); - fOtherSet->removeAll(*fCloseSet); - fOtherSet->removeAll(*fExtendSet); - - sets.emplace_back(*fSepSet); classNames.emplace_back("Sep"); - sets.emplace_back(*fFormatSet); classNames.emplace_back("Format"); - sets.emplace_back(*fSpSet); classNames.emplace_back("Sp"); - sets.emplace_back(*fLowerSet); classNames.emplace_back("Lower"); - sets.emplace_back(*fUpperSet); classNames.emplace_back("Upper"); - sets.emplace_back(*fOLetterSet); classNames.emplace_back("OLetter"); - sets.emplace_back(*fNumericSet); classNames.emplace_back("Numeric"); - sets.emplace_back(*fATermSet); classNames.emplace_back("ATerm"); - sets.emplace_back(*fSContinueSet); classNames.emplace_back("SContinue"); - sets.emplace_back(*fSTermSet); classNames.emplace_back("STerm"); - sets.emplace_back(*fCloseSet); classNames.emplace_back("Close"); - sets.emplace_back(*fOtherSet); classNames.emplace_back("Other"); - sets.emplace_back(*fExtendSet); classNames.emplace_back("Extend"); - - if (U_FAILURE(status)) { - deferredStatus = status; - } -} - - - -void RBBISentMonkey::setText(const UnicodeString &s) { - fText = &s; - prepareAppliedRules(s.length()); -} - -const std::vector& RBBISentMonkey::charClasses() { - return sets; -} - -// moveBack() Find the "significant" code point preceding the index i. -// Skips over ($Extend | $Format)* . -// -int RBBISentMonkey::moveBack(int i) { - if (i <= 0) { - return -1; - } - UChar32 c; - int32_t j = i; - do { - j = fText->moveIndex32(j, -1); - c = fText->char32At(j); - } - while (j>0 &&(fFormatSet->contains(c) || fExtendSet->contains(c))); - return j; - - } - - -int RBBISentMonkey::moveForward(int i) { - if (i>=fText->length()) { - return fText->length(); - } - UChar32 c; - int32_t j = i; - do { - j = fText->moveIndex32(j, 1); - c = cAt(j); - } - while (fFormatSet->contains(c) || fExtendSet->contains(c)); - return j; -} - -UChar32 RBBISentMonkey::cAt(int pos) { - if (pos<0 || pos>=fText->length()) { - return -1; - } else { - return fText->char32At(pos); - } -} - -int32_t RBBISentMonkey::next(int32_t prevPos) { - int p0, p1, p2, p3; // Indices of the significant code points around the - // break position being tested. The candidate break - // location is before p2. - - int breakPos = -1; - - UChar32 c0, c1, c2, c3; // The code points at p0, p1, p2 & p3. - UChar32 c; - - if (U_FAILURE(deferredStatus)) { - return -1; - } - - // Prev break at end of string. return DONE. - if (prevPos >= fText->length()) { - return -1; - } - p0 = p1 = p2 = p3 = prevPos; - c3 = fText->char32At(prevPos); - c0 = c1 = c2 = 0; - (void)p0; // Suppress set but not used warning. - - // Loop runs once per "significant" character position in the input text. - for (;;) { - // Move all of the positions forward in the input string. - p0 = p1; c0 = c1; - p1 = p2; c1 = c2; - p2 = p3; c2 = c3; - - // Advance p3 by X(Extend | Format)* Rule 4 - p3 = moveForward(p3); - c3 = cAt(p3); - - if (c1==0x0d && c2==0x0a && p2==(p1+1)) { - setAppliedRule(p2, "SB3 CR x LF"); - continue; - } - - if (fSepSet->contains(c1)) { - p2 = p1+1; // Separators don't combine with Extend or Format. - - setAppliedRule(p2, "SB4 Sep "); - break; - } - - if (p2 >= fText->length()) { - // Reached end of string. Always a break position. - setAppliedRule(p2, "SB4 Sep "); - break; - } - - if (p2 == prevPos) { - // Still warming up the loop. (won't work with zero length strings, but we don't care) - setAppliedRule(p2, "SB4 Sep "); - continue; - } - - if (fATermSet->contains(c1) && fNumericSet->contains(c2)) { - setAppliedRule(p2, "SB6 ATerm x Numeric"); - continue; - } - - if ((fUpperSet->contains(c0) || fLowerSet->contains(c0)) && - fATermSet->contains(c1) && fUpperSet->contains(c2)) { - setAppliedRule(p2, "SB7 (Upper | Lower) ATerm x Uppper"); - continue; - } - - // Note: STerm | ATerm are added to the negated part of the expression by a - // note to the Unicode 5.0 documents. - int p8 = p1; - while (fSpSet->contains(cAt(p8))) { - p8 = moveBack(p8); - } - while (fCloseSet->contains(cAt(p8))) { - p8 = moveBack(p8); - } - if (fATermSet->contains(cAt(p8))) { - p8=p2; - for (;;) { - c = cAt(p8); - if (c==-1 || fOLetterSet->contains(c) || fUpperSet->contains(c) || - fLowerSet->contains(c) || fSepSet->contains(c) || - fATermSet->contains(c) || fSTermSet->contains(c)) { - - setAppliedRule(p2, - "SB8 ATerm Close* Sp* x (not (OLettter | Upper | Lower | Sep | STerm | ATerm))* "); - break; - } - p8 = moveForward(p8); - } - if (fLowerSet->contains(cAt(p8))) { - - setAppliedRule(p2, - "SB8 ATerm Close* Sp* x (not (OLettter | Upper | Lower | Sep | STerm | ATerm))* "); - continue; - } - } - - if (fSContinueSet->contains(c2) || fSTermSet->contains(c2) || fATermSet->contains(c2)) { - p8 = p1; - while (fSpSet->contains(cAt(p8))) { - p8 = moveBack(p8); - } - while (fCloseSet->contains(cAt(p8))) { - p8 = moveBack(p8); - } - c = cAt(p8); - if (fSTermSet->contains(c) || fATermSet->contains(c)) { - setAppliedRule(p2, "SB8a (STerm | ATerm) Close* Sp* x (SContinue | STerm | ATerm)"); - continue; - } - } - - int p9 = p1; - while (fCloseSet->contains(cAt(p9))) { - p9 = moveBack(p9); - } - c = cAt(p9); - if ((fSTermSet->contains(c) || fATermSet->contains(c))) { - if (fCloseSet->contains(c2) || fSpSet->contains(c2) || fSepSet->contains(c2)) { - - setAppliedRule(p2, "SB9 (STerm | ATerm) Close* x (Close | Sp | Sep | CR | LF)"); - continue; - } - } - - int p10 = p1; - while (fSpSet->contains(cAt(p10))) { - p10 = moveBack(p10); - } - while (fCloseSet->contains(cAt(p10))) { - p10 = moveBack(p10); - } - if (fSTermSet->contains(cAt(p10)) || fATermSet->contains(cAt(p10))) { - if (fSpSet->contains(c2) || fSepSet->contains(c2)) { - setAppliedRule(p2, "SB10 (Sterm | ATerm) Close* Sp* x (Sp | Sep | CR | LF)"); - continue; - } - } - - int p11 = p1; - if (fSepSet->contains(cAt(p11))) { - p11 = moveBack(p11); - } - while (fSpSet->contains(cAt(p11))) { - p11 = moveBack(p11); - } - while (fCloseSet->contains(cAt(p11))) { - p11 = moveBack(p11); - } - if (fSTermSet->contains(cAt(p11)) || fATermSet->contains(cAt(p11))) { - setAppliedRule(p2, "SB11 (STerm | ATerm) Close* Sp* (Sep | CR | LF)? "); - break; - } - - setAppliedRule(p2, "SB12 Any x Any"); - } - - breakPos = p2; - return breakPos; -} - -RBBISentMonkey::~RBBISentMonkey() { - delete fSepSet; - delete fFormatSet; - delete fSpSet; - delete fLowerSet; - delete fUpperSet; - delete fOLetterSet; - delete fNumericSet; - delete fATermSet; - delete fSContinueSet; - delete fSTermSet; - delete fCloseSet; - delete fOtherSet; - delete fExtendSet; + // TODO(egg): Initialize } //------------------------------------------------------------------------------------------- @@ -2910,25 +2099,11 @@ RBBISentMonkey::~RBBISentMonkey() { class RBBILineMonkey: public RBBIMonkeyKind { public: RBBILineMonkey(); - virtual ~RBBILineMonkey(); - virtual const std::vector& charClasses() override; - virtual void setText(const UnicodeString &s) override; - virtual int32_t next(int32_t i) override; private: - std::vector sets; - std::vector> rules; - std::vector resolved; - - BreakIterator *fCharBI; - const UnicodeString *fText; }; RBBILineMonkey::RBBILineMonkey() : - RBBIMonkeyKind(), - - fCharBI(nullptr), - fText(nullptr) - + RBBIMonkeyKind() { if (U_FAILURE(deferredStatus)) { return; @@ -3133,68 +2308,9 @@ RBBILineMonkey::RBBILineMonkey() : classNames.push_back(name); } - fCharBI = BreakIterator::createCharacterInstance(Locale::getEnglish(), status); - if (U_FAILURE(status)) { deferredStatus = status; } - -} - -void RBBILineMonkey::setText(const UnicodeString &s) { - fText = &s; - fCharBI->setText(s); - prepareAppliedRules(s.length()); - UnicodeString remapped = s; - resolved.clear(); - resolved.reserve(s.length() + 1); - for (int i = 0; i < s.length() + 1; ++i) { - resolved.emplace_back(i); - } - for (const auto& rule : rules) { - rule->apply(remapped, resolved); - } - for (std::size_t i = 0; i < resolved.size(); ++i) { - if (resolved[i].appliedRule == nullptr) { - printf("Failed to resolve at %zu between U+%04X and U+%04X ", i, s.char32At(i-1), s.char32At(i)); - if (resolved[i].indexInRemapped.has_value()) { - printf("which is remapped %zu between U+%04X and U+%04X", *resolved[i].indexInRemapped, - remapped.char32At(*resolved[i].indexInRemapped - 1), - remapped.char32At(*resolved[i].indexInRemapped)); - } - std::terminate(); - } else { - setAppliedRule(i, resolved[i].appliedRule->name().c_str()); - } - } -} - -int32_t RBBILineMonkey::next(int32_t startPos) { - for (std::size_t i = startPos + 1; i < resolved.size(); ++i) { - if (resolved[i].appliedRule != nullptr && - resolved[i].appliedRule->resolution() == SegmentationRule::BREAK) { - return i; - } - } - return -1; -} - - -const std::vector& RBBILineMonkey::charClasses() { - return sets; -} - - -RBBILineMonkey::~RBBILineMonkey() { - constexpr bool debuggingOldMonkeyPerformance = false; - if (debuggingOldMonkeyPerformance) { - for (auto const &rule : rules) { - puts((rule->name() + " : " + std::to_string(rule->timeSpent() / std::chrono::milliseconds(1)) + - " ms").c_str()); - } - } - - delete fCharBI; } From 09df0984e673f89006bd866b340a3710a8b8a1d6 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 17:47:48 +0100 Subject: [PATCH 21/25] No exclusion --- icu4c/source/test/intltest/rbbitst.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/icu4c/source/test/intltest/rbbitst.cpp b/icu4c/source/test/intltest/rbbitst.cpp index 9e3e69b2d461..ede09c195e51 100644 --- a/icu4c/source/test/intltest/rbbitst.cpp +++ b/icu4c/source/test/intltest/rbbitst.cpp @@ -2033,15 +2033,6 @@ RBBICharMonkey::RBBICharMonkey() { // TODO(egg): This could just as well be part of the rules… rules.push_back(std::make_unique(uR"(ALL ÷ / ÷ ALL)", uR"()", u'÷', uR"()")); - const UnicodeSet lbSA(uR"(\p{lb=SA})", status); - for (auto it = partition.begin(); it != partition.end();) { - if (lbSA.containsAll(it->second)) { - it = partition.erase(it); - } else { - ++it; - } - } - for (const auto &[name, set] : partition) { sets.push_back(set); classNames.push_back(name); From be3d0422fcfa205ac19c473574d87de8fe3e66df Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 18:35:46 +0100 Subject: [PATCH 22/25] strongly worded monkeys --- icu4c/source/test/intltest/rbbitst.cpp | 84 ++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/icu4c/source/test/intltest/rbbitst.cpp b/icu4c/source/test/intltest/rbbitst.cpp index ede09c195e51..f262baa740c2 100644 --- a/icu4c/source/test/intltest/rbbitst.cpp +++ b/icu4c/source/test/intltest/rbbitst.cpp @@ -2059,9 +2059,93 @@ RBBIWordMonkey::RBBIWordMonkey() { UErrorCode status = U_ZERO_ERROR; + std::list> partition; + + // TODO(egg): The following is a workaround for what seems to be an ICU bug: + // with UREGEX_DOTALL (but not UREGEX_MULTILINE), /.*\u000A/ does not match + // CR LF. + rules.push_back(std::make_unique(uR"(CR LF ÷)", uR"(\u000D\u000A)", u'÷', uR"()")); + + // These two could be part of the rules. + rules.push_back(std::make_unique(uR"(WB1 sot ÷ Any)", uR"(^)", u'÷', uR"()")); + // Note that /$/ matches ( BK | CR | LF | NL ) eot, so we use (?!.) instead. + // The generated rules use the same (?!.). + rules.push_back(std::make_unique(uR"(WB2 Any ÷ eot)", uR"()", u'÷', uR"((?!.))")); + + // --- NOLI ME TANGERE --- + // Generated by GenerateBreakTest.java in the Unicode tools. + partition.emplace_back("CR", UnicodeSet(uR"([\p{Word_Break=CR}])", status)); + partition.emplace_back("LF", UnicodeSet(uR"([\p{Word_Break=LF}])", status)); + partition.emplace_back("Newline", UnicodeSet(uR"([\p{Word_Break=Newline}])", status)); + partition.emplace_back("Extend", UnicodeSet(uR"([\p{Word_Break=Extend}])", status)); + partition.emplace_back("Format", UnicodeSet(uR"([[\p{Word_Break=Format}]])", status)); + partition.emplace_back("Katakana", UnicodeSet(uR"([\p{Word_Break=Katakana}])", status)); + partition.emplace_back("ALetter_ExtPict", UnicodeSet(uR"([\p{Word_Break=ALetter}&\p{Extended_Pictographic}])", status)); + partition.emplace_back("ALettermExtPict", UnicodeSet(uR"([\p{Word_Break=ALetter}-\p{Extended_Pictographic}])", status)); + partition.emplace_back("MidLetter", UnicodeSet(uR"([\p{Word_Break=MidLetter}])", status)); + partition.emplace_back("MidNum", UnicodeSet(uR"([\p{Word_Break=MidNum}])", status)); + partition.emplace_back("MidNumLet", UnicodeSet(uR"([\p{Word_Break=MidNumLet}])", status)); + partition.emplace_back("Numeric", UnicodeSet(uR"([\p{Word_Break=Numeric}])", status)); + partition.emplace_back("ExtendNumLet", UnicodeSet(uR"([\p{Word_Break=ExtendNumLet}])", status)); + partition.emplace_back("RI", UnicodeSet(uR"([\p{Word_Break=Regional_Indicator}])", status)); + partition.emplace_back("Hebrew_Letter", UnicodeSet(uR"([\p{Word_Break=Hebrew_Letter}])", status)); + partition.emplace_back("Double_Quote", UnicodeSet(uR"([\p{Word_Break=Double_Quote}])", status)); + partition.emplace_back("Single_Quote", UnicodeSet(uR"([\p{Word_Break=Single_Quote}])", status)); + partition.emplace_back("ZWJ", UnicodeSet(uR"([\p{Word_Break=ZWJ}])", status)); + partition.emplace_back("ExtPictmALetter", UnicodeSet(uR"([\p{Extended_Pictographic}-\p{Word_Break=ALetter}])", status)); + partition.emplace_back("WSegSpace", UnicodeSet(uR"([\p{Word_Break=WSegSpace}])", status)); + partition.emplace_back("XXmExtPict", UnicodeSet(uR"([\p{Word_Break=Other}-\p{Extended_Pictographic}])", status)); + + rules.push_back(std::make_unique(uR"($CR × $LF)", uR"(\p{Word_Break=CR})", u'×', uR"(\p{Word_Break=LF})")); + rules.push_back(std::make_unique(uR"(($Newline | $CR | $LF) ÷)", uR"((\p{Word_Break=Newline} | \p{Word_Break=CR} | \p{Word_Break=LF}))", u'÷', uR"()")); + rules.push_back(std::make_unique(uR"(÷ ($Newline | $CR | $LF))", uR"()", u'÷', uR"((\p{Word_Break=Newline} | \p{Word_Break=CR} | \p{Word_Break=LF}))")); + rules.push_back(std::make_unique(uR"($ZWJ × $ExtPict)", uR"(\p{Word_Break=ZWJ})", u'×', uR"(\p{Extended_Pictographic})")); + rules.push_back(std::make_unique(uR"($WSegSpace × $WSegSpace)", uR"(\p{Word_Break=WSegSpace})", u'×', uR"(\p{Word_Break=WSegSpace})")); + rules.push_back(std::make_unique(uR"((?[^$CR $LF $Newline]) ($Extend | $Format | $ZWJ)* → ${X})", uR"((?[^\p{Word_Break=CR} \p{Word_Break=LF} \p{Word_Break=Newline}]) (\p{Word_Break=Extend} | [\p{Word_Break=Format}] | \p{Word_Break=ZWJ})*)", uR"(${X})")); + rules.push_back(std::make_unique(uR"($AHLetter × $AHLetter)", uR"([\p{Word_Break=ALetter} \p{Word_Break=Hebrew_Letter}])", u'×', uR"([\p{Word_Break=ALetter} \p{Word_Break=Hebrew_Letter}])")); + rules.push_back(std::make_unique(uR"($AHLetter × ($MidLetter | $MidNumLetQ) $AHLetter)", uR"([\p{Word_Break=ALetter} \p{Word_Break=Hebrew_Letter}])", u'×', uR"((\p{Word_Break=MidLetter} | [\p{Word_Break=MidNumLet} \p{Word_Break=Single_Quote}]) [\p{Word_Break=ALetter} \p{Word_Break=Hebrew_Letter}])")); + rules.push_back(std::make_unique(uR"($AHLetter ($MidLetter | $MidNumLetQ) × $AHLetter)", uR"([\p{Word_Break=ALetter} \p{Word_Break=Hebrew_Letter}] (\p{Word_Break=MidLetter} | [\p{Word_Break=MidNumLet} \p{Word_Break=Single_Quote}]))", u'×', uR"([\p{Word_Break=ALetter} \p{Word_Break=Hebrew_Letter}])")); + rules.push_back(std::make_unique(uR"($Hebrew_Letter × $Single_Quote)", uR"(\p{Word_Break=Hebrew_Letter})", u'×', uR"(\p{Word_Break=Single_Quote})")); + rules.push_back(std::make_unique(uR"($Hebrew_Letter × $Double_Quote $Hebrew_Letter)", uR"(\p{Word_Break=Hebrew_Letter})", u'×', uR"(\p{Word_Break=Double_Quote} \p{Word_Break=Hebrew_Letter})")); + rules.push_back(std::make_unique(uR"($Hebrew_Letter $Double_Quote × $Hebrew_Letter)", uR"(\p{Word_Break=Hebrew_Letter} \p{Word_Break=Double_Quote})", u'×', uR"(\p{Word_Break=Hebrew_Letter})")); + rules.push_back(std::make_unique(uR"($Numeric × $Numeric)", uR"(\p{Word_Break=Numeric})", u'×', uR"(\p{Word_Break=Numeric})")); + rules.push_back(std::make_unique(uR"($AHLetter × $Numeric)", uR"([\p{Word_Break=ALetter} \p{Word_Break=Hebrew_Letter}])", u'×', uR"(\p{Word_Break=Numeric})")); + rules.push_back(std::make_unique(uR"($Numeric × $AHLetter)", uR"(\p{Word_Break=Numeric})", u'×', uR"([\p{Word_Break=ALetter} \p{Word_Break=Hebrew_Letter}])")); + rules.push_back(std::make_unique(uR"($Numeric ($MidNum | $MidNumLetQ) × $Numeric)", uR"(\p{Word_Break=Numeric} (\p{Word_Break=MidNum} | [\p{Word_Break=MidNumLet} \p{Word_Break=Single_Quote}]))", u'×', uR"(\p{Word_Break=Numeric})")); + rules.push_back(std::make_unique(uR"($Numeric × ($MidNum | $MidNumLetQ) $Numeric)", uR"(\p{Word_Break=Numeric})", u'×', uR"((\p{Word_Break=MidNum} | [\p{Word_Break=MidNumLet} \p{Word_Break=Single_Quote}]) \p{Word_Break=Numeric})")); + rules.push_back(std::make_unique(uR"($Katakana × $Katakana)", uR"(\p{Word_Break=Katakana})", u'×', uR"(\p{Word_Break=Katakana})")); + rules.push_back(std::make_unique(uR"(($AHLetter | $Numeric | $Katakana | $ExtendNumLet) × $ExtendNumLet)", uR"(([\p{Word_Break=ALetter} \p{Word_Break=Hebrew_Letter}] | \p{Word_Break=Numeric} | \p{Word_Break=Katakana} | \p{Word_Break=ExtendNumLet}))", u'×', uR"(\p{Word_Break=ExtendNumLet})")); + rules.push_back(std::make_unique(uR"($ExtendNumLet × ($AHLetter | $Numeric | $Katakana))", uR"(\p{Word_Break=ExtendNumLet})", u'×', uR"(([\p{Word_Break=ALetter} \p{Word_Break=Hebrew_Letter}] | \p{Word_Break=Numeric} | \p{Word_Break=Katakana}))")); + rules.push_back(std::make_unique(uR"(^ ($RI $RI)* $RI × $RI)", uR"(^ (\p{Word_Break=Regional_Indicator} \p{Word_Break=Regional_Indicator})* \p{Word_Break=Regional_Indicator})", u'×', uR"(\p{Word_Break=Regional_Indicator})")); + rules.push_back(std::make_unique(uR"([^$RI] ($RI $RI)* $RI × $RI)", uR"([^\p{Word_Break=Regional_Indicator}] (\p{Word_Break=Regional_Indicator} \p{Word_Break=Regional_Indicator})* \p{Word_Break=Regional_Indicator})", u'×', uR"(\p{Word_Break=Regional_Indicator})")); + // --- End of generated code. --- + + // TODO(egg): This could just as well be part of the rules… + rules.push_back(std::make_unique(uR"(ALL ÷ / ÷ ALL)", uR"()", u'÷', uR"()")); + + UnicodeSet dictionarySet = UnicodeSet(uR"([[\uac00-\ud7a3][:Han:][:Hiragana:]])", status); dictionarySet.addAll(UnicodeSet(uR"([\p{Word_Break = Katakana}])", status)); dictionarySet.addAll(UnicodeSet(uR"([\p{LineBreak = Complex_Context}])", status)); + + + for (auto it = partition.begin(); it != partition.end();) { + it->second.removeAll(dictionarySet); + if (it->second.isEmpty()) { + it = partition.erase(it); + } else { + ++it; + } + } + + for (const auto &[name, set] : partition) { + sets.push_back(set); + classNames.push_back(name); + } + + if (U_FAILURE(status)) { + deferredStatus = status; + } } //------------------------------------------------------------------------------------------ From f0ec9ee0ec6bdd724f4e25040e2581d012a1b2a5 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 20:28:27 +0100 Subject: [PATCH 23/25] Sentenced monkeys, factor dictionarySet_, known issues on 20 year old test cases --- icu4c/source/test/intltest/rbbitst.cpp | 104 ++++++++++++++++++------- 1 file changed, 76 insertions(+), 28 deletions(-) diff --git a/icu4c/source/test/intltest/rbbitst.cpp b/icu4c/source/test/intltest/rbbitst.cpp index f262baa740c2..51b6b147a274 100644 --- a/icu4c/source/test/intltest/rbbitst.cpp +++ b/icu4c/source/test/intltest/rbbitst.cpp @@ -1809,6 +1809,8 @@ class RBBIMonkeyKind { // for this type of iterator. const std::vector &charClasses(); + const UnicodeSet &dictionarySet() const; + // Set the test text on which subsequent calls to next() will operate void setText(const UnicodeString &s); @@ -1835,6 +1837,7 @@ class RBBIMonkeyKind { std::vector classNames; std::vector sets; std::vector> rules; + UnicodeSet dictionarySet_; // Clear `appliedRules` and fill it with empty strings in the size of test text. void prepareAppliedRules(int32_t size); @@ -1853,7 +1856,10 @@ RBBIMonkeyKind::~RBBIMonkeyKind() { } const std::vector &RBBIMonkeyKind::charClasses() { - return sets; + return sets; } + +const UnicodeSet &RBBIMonkeyKind::dictionarySet() const { + return dictionarySet_; } void RBBIMonkeyKind::setText(const UnicodeString &s) { @@ -2061,10 +2067,14 @@ RBBIWordMonkey::RBBIWordMonkey() std::list> partition; + dictionarySet_ = UnicodeSet(uR"([[\uac00-\ud7a3][:Han:][:Hiragana:]])", status); + dictionarySet_.addAll(UnicodeSet(uR"([\p{Word_Break = Katakana}])", status)); + dictionarySet_.addAll(UnicodeSet(uR"([\p{LineBreak = Complex_Context}])", status)); + // TODO(egg): The following is a workaround for what seems to be an ICU bug: // with UREGEX_DOTALL (but not UREGEX_MULTILINE), /.*\u000A/ does not match // CR LF. - rules.push_back(std::make_unique(uR"(CR LF ÷)", uR"(\u000D\u000A)", u'÷', uR"()")); + //rules.push_back(std::make_unique(uR"(CR LF ÷)", uR"(\u000D\u000A)", u'÷', uR"()")); // These two could be part of the rules. rules.push_back(std::make_unique(uR"(WB1 sot ÷ Any)", uR"(^)", u'÷', uR"()")); @@ -2123,21 +2133,6 @@ RBBIWordMonkey::RBBIWordMonkey() // TODO(egg): This could just as well be part of the rules… rules.push_back(std::make_unique(uR"(ALL ÷ / ÷ ALL)", uR"()", u'÷', uR"()")); - - UnicodeSet dictionarySet = UnicodeSet(uR"([[\uac00-\ud7a3][:Han:][:Hiragana:]])", status); - dictionarySet.addAll(UnicodeSet(uR"([\p{Word_Break = Katakana}])", status)); - dictionarySet.addAll(UnicodeSet(uR"([\p{LineBreak = Complex_Context}])", status)); - - - for (auto it = partition.begin(); it != partition.end();) { - it->second.removeAll(dictionarySet); - if (it->second.isEmpty()) { - it = partition.erase(it); - } else { - ++it; - } - } - for (const auto &[name, set] : partition) { sets.push_back(set); classNames.push_back(name); @@ -2161,8 +2156,55 @@ class RBBISentMonkey: public RBBIMonkeyKind { RBBISentMonkey::RBBISentMonkey() { - UErrorCode status = U_ZERO_ERROR; - // TODO(egg): Initialize + UErrorCode status = U_ZERO_ERROR; + + std::list> partition; + + // These two could be part of the rules. + rules.push_back(std::make_unique(uR"(SB1 sot ÷ Any)", uR"(^)", u'÷', uR"()")); + // Note that /$/ matches ( BK | CR | LF | NL ) eot, so we use (?!.) instead. + // The generated rules use the same (?!.). + rules.push_back(std::make_unique(uR"(SB2 Any ÷ eot)", uR"()", u'÷', uR"((?!.))")); + + // --- NOLI ME TANGERE --- + // Generated by GenerateBreakTest.java in the Unicode tools. + partition.emplace_back("CR", UnicodeSet(uR"([\p{Sentence_Break=CR}])", status)); + partition.emplace_back("LF", UnicodeSet(uR"([\p{Sentence_Break=LF}])", status)); + partition.emplace_back("Extend", UnicodeSet(uR"([\p{Sentence_Break=Extend}])", status)); + partition.emplace_back("Format", UnicodeSet(uR"([\p{Sentence_Break=Format}])", status)); + partition.emplace_back("Sep", UnicodeSet(uR"([\p{Sentence_Break=Sep}])", status)); + partition.emplace_back("Sp", UnicodeSet(uR"([\p{Sentence_Break=Sp}])", status)); + partition.emplace_back("Lower", UnicodeSet(uR"([\p{Sentence_Break=Lower}])", status)); + partition.emplace_back("Upper", UnicodeSet(uR"([\p{Sentence_Break=Upper}])", status)); + partition.emplace_back("OLetter", UnicodeSet(uR"([\p{Sentence_Break=OLetter}])", status)); + partition.emplace_back("Numeric", UnicodeSet(uR"([\p{Sentence_Break=Numeric}])", status)); + partition.emplace_back("ATerm", UnicodeSet(uR"([\p{Sentence_Break=ATerm}])", status)); + partition.emplace_back("STerm", UnicodeSet(uR"([\p{Sentence_Break=STerm}])", status)); + partition.emplace_back("Close", UnicodeSet(uR"([\p{Sentence_Break=Close}])", status)); + partition.emplace_back("SContinue", UnicodeSet(uR"([\p{Sentence_Break=SContinue}])", status)); + partition.emplace_back("XX", UnicodeSet(uR"([\p{Sentence_Break=Other}])", status)); + + rules.push_back(std::make_unique(uR"($CR × $LF)", uR"(\p{Sentence_Break=CR})", u'×', uR"(\p{Sentence_Break=LF})")); + rules.push_back(std::make_unique(uR"($ParaSep ÷)", uR"([\p{Sentence_Break=Sep} \p{Sentence_Break=CR} \p{Sentence_Break=LF}])", u'÷', uR"()")); + rules.push_back(std::make_unique(uR"((?[^$ParaSep]) ( $Extend | $Format )* → ${X})", uR"((?[^[\p{Sentence_Break=Sep} \p{Sentence_Break=CR} \p{Sentence_Break=LF}]]) ( \p{Sentence_Break=Extend} | \p{Sentence_Break=Format} )*)", uR"(${X})")); + rules.push_back(std::make_unique(uR"($ATerm × $Numeric)", uR"(\p{Sentence_Break=ATerm})", u'×', uR"(\p{Sentence_Break=Numeric})")); + rules.push_back(std::make_unique(uR"(($Upper | $Lower) $ATerm × $Upper)", uR"((\p{Sentence_Break=Upper} | \p{Sentence_Break=Lower}) \p{Sentence_Break=ATerm})", u'×', uR"(\p{Sentence_Break=Upper})")); + rules.push_back(std::make_unique(uR"($ATerm $Close* $Sp* × [^ $OLetter $Upper $Lower $ParaSep $SATerm]* $Lower)", uR"(\p{Sentence_Break=ATerm} \p{Sentence_Break=Close}* \p{Sentence_Break=Sp}*)", u'×', uR"([^ \p{Sentence_Break=OLetter} \p{Sentence_Break=Upper} \p{Sentence_Break=Lower} [\p{Sentence_Break=Sep} \p{Sentence_Break=CR} \p{Sentence_Break=LF}] [\p{Sentence_Break=STerm} \p{Sentence_Break=ATerm}]]* \p{Sentence_Break=Lower})")); + rules.push_back(std::make_unique(uR"($SATerm $Close* $Sp* × ($SContinue | $SATerm))", uR"([\p{Sentence_Break=STerm} \p{Sentence_Break=ATerm}] \p{Sentence_Break=Close}* \p{Sentence_Break=Sp}*)", u'×', uR"((\p{Sentence_Break=SContinue} | [\p{Sentence_Break=STerm} \p{Sentence_Break=ATerm}]))")); + rules.push_back(std::make_unique(uR"($SATerm $Close* × ( $Close | $Sp | $ParaSep ))", uR"([\p{Sentence_Break=STerm} \p{Sentence_Break=ATerm}] \p{Sentence_Break=Close}*)", u'×', uR"(( \p{Sentence_Break=Close} | \p{Sentence_Break=Sp} | [\p{Sentence_Break=Sep} \p{Sentence_Break=CR} \p{Sentence_Break=LF}] ))")); + rules.push_back(std::make_unique(uR"($SATerm $Close* $Sp* × ( $Sp | $ParaSep ))", uR"([\p{Sentence_Break=STerm} \p{Sentence_Break=ATerm}] \p{Sentence_Break=Close}* \p{Sentence_Break=Sp}*)", u'×', uR"(( \p{Sentence_Break=Sp} | [\p{Sentence_Break=Sep} \p{Sentence_Break=CR} \p{Sentence_Break=LF}] ))")); + rules.push_back(std::make_unique(uR"($SATerm $Close* $Sp* $ParaSep? ÷)", uR"([\p{Sentence_Break=STerm} \p{Sentence_Break=ATerm}] \p{Sentence_Break=Close}* \p{Sentence_Break=Sp}* [\p{Sentence_Break=Sep} \p{Sentence_Break=CR} \p{Sentence_Break=LF}]?)", u'÷', uR"()")); + rules.push_back(std::make_unique(uR"(× $Any)", uR"()", u'×', uR"(.)")); + // --- End of generated code. --- + + for (const auto &[name, set] : partition) { + sets.push_back(set); + classNames.push_back(name); + } + + if (U_FAILURE(status)) { + deferredStatus = status; + } } //------------------------------------------------------------------------------------------- @@ -2196,7 +2238,7 @@ RBBILineMonkey::RBBILineMonkey() : // TODO(egg): The following is a workaround for what seems to be an ICU bug: // with UREGEX_DOTALL (but not UREGEX_MULTILINE), /.*\u000A/ does not match // CR LF. - rules.push_back(std::make_unique(uR"(CR LF ÷)", uR"(\u000D\u000A)", u'÷', uR"()")); + //rules.push_back(std::make_unique(uR"(CR LF ÷)", uR"(\u000D\u000A)", u'÷', uR"()")); rules.push_back(std::make_unique(uR"(sot ÷ contra LB2)", uR"(^)", u'÷', uR"()")); // This one could be part of the rules. @@ -2369,14 +2411,7 @@ RBBILineMonkey::RBBILineMonkey() : uR"()", u'÷', uR"()")); - const UnicodeSet lbSA(uR"(\p{lb=SA})", status); - for (auto it = partition.begin(); it != partition.end();) { - if (lbSA.containsAll(it->second)) { - it = partition.erase(it); - } else { - ++it; - } - } + dictionarySet_ = UnicodeSet(uR"(\p{lb=SA})", status); for (const auto &[name, set] : partition) { sets.push_back(set); @@ -2573,6 +2608,16 @@ void RBBITest::TestWordBreaks() UnicodeString ustr = CharsToUnicodeString(strlist[loop]); // RBBICharMonkey monkey; RBBIWordMonkey monkey; + if (monkey.dictionarySet().containsSome(ustr)) { + // Some of these twenty-year-old random examples depend on the + // monkey tests not looking across dictionary/non-dictionary + // boundaries for context when applying the rules. + // The monkeys are not designed to work with dictionary characters, + // so this behaviour is out of scope for testing against the + // monkeys. + logKnownIssue("ICU-22984"); + continue; + } int expected[50]; int expectedcount = 0; @@ -3077,6 +3122,9 @@ void RBBITest::RunMonkey(BreakIterator *bi, RBBIMonkeyKind &mk, const char *name errln("%s:%d c < 0", __FILE__, __LINE__); break; } + if (mk.dictionarySet().contains(c)) { + continue; + } if (scalarsOnly && U16_IS_SURROGATE(c)) { continue; } From cfe6d4a395cb2c95451388a3d5e2ef36be4d4c2c Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 20:38:35 +0100 Subject: [PATCH 24/25] I hallucinated that bug, apparently. --- icu4c/source/test/intltest/rbbitst.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/icu4c/source/test/intltest/rbbitst.cpp b/icu4c/source/test/intltest/rbbitst.cpp index 51b6b147a274..e94f76b2ce2e 100644 --- a/icu4c/source/test/intltest/rbbitst.cpp +++ b/icu4c/source/test/intltest/rbbitst.cpp @@ -1989,11 +1989,6 @@ RBBICharMonkey::RBBICharMonkey() { std::list> partition; - // TODO(egg): The following is a workaround for what seems to be an ICU bug: - // with UREGEX_DOTALL (but not UREGEX_MULTILINE), /.*\u000A/ does not match - // CR LF. - rules.push_back(std::make_unique(uR"(CR LF ÷)", uR"(\u000D\u000A)", u'÷', uR"()")); - // These two could be part of the rules. rules.push_back(std::make_unique(uR"(GB1 sot ÷ Any)", uR"(^)", u'÷', uR"()")); // Note that /$/ matches ( BK | CR | LF | NL ) eot, so we use (?!.) instead. @@ -2071,11 +2066,6 @@ RBBIWordMonkey::RBBIWordMonkey() dictionarySet_.addAll(UnicodeSet(uR"([\p{Word_Break = Katakana}])", status)); dictionarySet_.addAll(UnicodeSet(uR"([\p{LineBreak = Complex_Context}])", status)); - // TODO(egg): The following is a workaround for what seems to be an ICU bug: - // with UREGEX_DOTALL (but not UREGEX_MULTILINE), /.*\u000A/ does not match - // CR LF. - //rules.push_back(std::make_unique(uR"(CR LF ÷)", uR"(\u000D\u000A)", u'÷', uR"()")); - // These two could be part of the rules. rules.push_back(std::make_unique(uR"(WB1 sot ÷ Any)", uR"(^)", u'÷', uR"()")); // Note that /$/ matches ( BK | CR | LF | NL ) eot, so we use (?!.) instead. @@ -2235,11 +2225,6 @@ RBBILineMonkey::RBBILineMonkey() : std::list> partition; - // TODO(egg): The following is a workaround for what seems to be an ICU bug: - // with UREGEX_DOTALL (but not UREGEX_MULTILINE), /.*\u000A/ does not match - // CR LF. - //rules.push_back(std::make_unique(uR"(CR LF ÷)", uR"(\u000D\u000A)", u'÷', uR"()")); - rules.push_back(std::make_unique(uR"(sot ÷ contra LB2)", uR"(^)", u'÷', uR"()")); // This one could be part of the rules. // Note that /$/ matches ( BK | CR | LF | NL ) eot, so we use (?!.) instead. From 71098c6dcf01ef6e9a68d6da4fcaa9884df63e90 Mon Sep 17 00:00:00 2001 From: Robin Leroy Date: Fri, 13 Dec 2024 20:40:36 +0100 Subject: [PATCH 25/25] Apparently I hallucinated that bug --- icu4c/source/test/intltest/rbbitst.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/icu4c/source/test/intltest/rbbitst.cpp b/icu4c/source/test/intltest/rbbitst.cpp index 2ef039405f5e..7d992198fa74 100644 --- a/icu4c/source/test/intltest/rbbitst.cpp +++ b/icu4c/source/test/intltest/rbbitst.cpp @@ -2943,11 +2943,6 @@ RBBILineMonkey::RBBILineMonkey() : std::list> partition; - // TODO(egg): The following is a workaround for what seems to be an ICU bug: - // with UREGEX_DOTALL (but not UREGEX_MULTILINE), /.*\u000A/ does not match - // CR LF. - rules.push_back(std::make_unique(uR"(CR LF ÷)", uR"(\u000D\u000A)", u'÷', uR"()")); - rules.push_back(std::make_unique(uR"(sot ÷ contra LB2)", uR"(^)", u'÷', uR"()")); // This one could be part of the rules. // Note that /$/ matches ( BK | CR | LF | NL ) eot, so we use (?!.) instead.