diff options
Diffstat (limited to 'text/word.c')
| -rw-r--r-- | text/word.c | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/text/word.c b/text/word.c new file mode 100644 index 0000000..e1f0977 --- /dev/null +++ b/text/word.c @@ -0,0 +1,75 @@ +/* Word boundaries (UAX #29, Unicode 15.1): text/text.h. As with lines, + * each boundary goes to the first rule that decides it; extends, formats + * and joiners fold into the character before them (WB4), so the rules after + * it see through them. */ +#include "text.h" +#include <stdlib.h> + +enum { OTHER = VEGA_WB_OTHER, CR = VEGA_WB_CR, LF = VEGA_WB_LF, NEWLINE = VEGA_WB_NEWLINE, EXTEND = VEGA_WB_EXTEND, + ZWJ = VEGA_WB_ZWJ, RI = VEGA_WB_RI, FORMAT = VEGA_WB_FORMAT, KATAKANA = VEGA_WB_KATAKANA, + HEBREW = VEGA_WB_HEBREW_LETTER, ALETTER = VEGA_WB_ALETTER, SINGLE_QUOTE = VEGA_WB_SINGLE_QUOTE, + DOUBLE_QUOTE = VEGA_WB_DOUBLE_QUOTE, MIDNUMLET = VEGA_WB_MIDNUMLET, MIDLETTER = VEGA_WB_MIDLETTER, + MIDNUM = VEGA_WB_MIDNUM, NUMERIC = VEGA_WB_NUMERIC, EXTENDNUMLET = VEGA_WB_EXTENDNUMLET, + WSEGSPACE = VEGA_WB_WSEGSPACE }; + +static bool letter(unsigned c) { return c == ALETTER || c == HEBREW; } /* AHLetter */ +static bool mid_letter(unsigned c) { return c == MIDLETTER || c == MIDNUMLET || c == SINGLE_QUOTE; } +static bool mid_number(unsigned c) { return c == MIDNUM || c == MIDNUMLET || c == SINGLE_QUOTE; } +static bool ignored(unsigned c) { return c == EXTEND || c == FORMAT || c == ZWJ; } +static bool line_end(unsigned c) { return c == CR || c == LF || c == NEWLINE; } + +bool vega_words(const uint32_t *codes, size_t count, uint8_t *breaks) { + breaks[0] = count > 0; /* WB1 */ + if (!count) return true; + /* Per character: its class; its unit's (WB4); where its unit starts; + * regional indicators ending at its unit. */ + uint8_t *raw = malloc(2 * count); + size_t *start = malloc(2 * count * sizeof(size_t)); + if (!raw || !start) { + free(raw); + free(start); + return false; + } + uint8_t *eff = raw + count; + size_t *ri = start + count; + for (size_t i = 0; i < count; ++i) { + raw[i] = vega_word(codes[i]); + bool folds = ignored(raw[i]) && i && !line_end(raw[start[i - 1]]); + eff[i] = folds ? eff[i - 1] : raw[i]; + start[i] = folds ? start[i - 1] : i; + ri[i] = folds ? ri[i - 1] : eff[i] == RI ? (i ? ri[i - 1] : 0) + 1 : 0; + } + for (size_t i = 1; i < count; ++i) { + unsigned a = raw[i - 1], b = raw[i], A = eff[i - 1]; + size_t unit = start[i - 1], next = i + 1; + while (next < count && start[next] != next) next++; + unsigned after = next < count ? eff[next] : OTHER, earlier = unit ? eff[unit - 1] : OTHER; + bool join; + if (a == CR && b == LF) join = true; /* WB3 */ + else if (line_end(a) || line_end(b)) join = false; /* WB3a, WB3b */ + else if (a == ZWJ && vega_extended_pictographic(codes[i])) join = true; /* WB3c */ + else if (a == WSEGSPACE && b == WSEGSPACE) join = true; /* WB3d */ + else if (ignored(b)) join = true; /* WB4 */ + else if (letter(A) && letter(b)) join = true; /* WB5 */ + else if (letter(A) && (mid_letter(b)) && letter(after)) join = true; /* WB6 */ + else if (mid_letter(A) && letter(b) && unit && letter(earlier)) join = true; /* WB7 */ + else if (A == HEBREW && b == SINGLE_QUOTE) join = true; /* WB7a */ + else if (A == HEBREW && b == DOUBLE_QUOTE && after == HEBREW) join = true; /* WB7b */ + else if (A == DOUBLE_QUOTE && b == HEBREW && unit && earlier == HEBREW) join = true; /* WB7c */ + else if (A == NUMERIC && b == NUMERIC) join = true; /* WB8 */ + else if (letter(A) && b == NUMERIC) join = true; /* WB9 */ + else if (A == NUMERIC && letter(b)) join = true; /* WB10 */ + else if (mid_number(A) && b == NUMERIC && unit && earlier == NUMERIC) join = true; /* WB11 */ + else if (A == NUMERIC && mid_number(b) && after == NUMERIC) join = true; /* WB12 */ + else if (A == KATAKANA && b == KATAKANA) join = true; /* WB13 */ + else if ((letter(A) || A == NUMERIC || A == KATAKANA || A == EXTENDNUMLET) && b == EXTENDNUMLET) join = true; /* WB13a */ + else if (A == EXTENDNUMLET && (letter(b) || b == NUMERIC || b == KATAKANA)) join = true; /* WB13b */ + else if (A == RI && b == RI && ri[i - 1] % 2) join = true; /* WB15, WB16 */ + else join = false; /* WB999 */ + breaks[i] = !join; + } + breaks[count] = 1; /* WB2 */ + free(raw); + free(start); + return true; +} |
