| 148 | // \s+ |
| 149 | |
| 150 | std::vector<std::string> Tokenizer::pre_tokenize(const std::string & text) const { |
| 151 | std::vector<std::string> pieces; |
| 152 | const char * s = text.c_str(); |
| 153 | const size_t len = text.size(); |
| 154 | size_t pos = 0; |
| 155 | |
| 156 | auto peek_cp = [&](size_t p, int * cplen) -> uint32_t { |
| 157 | if (p >= len) { *cplen = 0; return 0; } |
| 158 | return utf8_decode(s + p, len - p, cplen); |
| 159 | }; |
| 160 | |
| 161 | while (pos < len) { |
| 162 | size_t start = pos; |
| 163 | int cplen = 0; |
| 164 | uint32_t cp = peek_cp(pos, &cplen); |
| 165 | |
| 166 | // Pattern 1: English contractions 's 't 're 've 'm 'll 'd |
| 167 | if (cp == '\'') { |
| 168 | size_t save = pos; |
| 169 | pos++; |
| 170 | bool matched = false; |
| 171 | if (pos < len) { |
| 172 | char c = s[pos] | 0x20; // lowercase |
| 173 | if (c == 's' || c == 't' || c == 'm' || c == 'd') { |
| 174 | pos++; |
| 175 | matched = true; |
| 176 | } else if (c == 'r' && pos + 1 < len && (s[pos+1] | 0x20) == 'e') { |
| 177 | pos += 2; |
| 178 | matched = true; |
| 179 | } else if (c == 'v' && pos + 1 < len && (s[pos+1] | 0x20) == 'e') { |
| 180 | pos += 2; |
| 181 | matched = true; |
| 182 | } else if (c == 'l' && pos + 1 < len && (s[pos+1] | 0x20) == 'l') { |
| 183 | pos += 2; |
| 184 | matched = true; |
| 185 | } |
| 186 | } |
| 187 | if (matched) { |
| 188 | pieces.push_back(text.substr(start, pos - start)); |
| 189 | continue; |
| 190 | } |
| 191 | pos = save; // reset, try other patterns |
| 192 | } |
| 193 | |
| 194 | // Pattern 2: [^\r\n\p{L}\p{N}]?[\p{L}\p{M}]+ |
| 195 | { |
| 196 | size_t p = pos; |
| 197 | int cl = 0; |
| 198 | uint32_t c = peek_cp(p, &cl); |
| 199 | // Optional leading non-letter, non-digit, non-newline char |
| 200 | if (cl > 0 && !is_newline(c) && !is_letter(c) && !is_digit(c)) { |
| 201 | p += cl; |
| 202 | c = peek_cp(p, &cl); |
| 203 | } |
| 204 | // One or more letter/mark chars |
| 205 | if (cl > 0 && (is_letter(c) || is_mark(c))) { |
| 206 | while (cl > 0 && (is_letter(c) || is_mark(c))) { |
| 207 | p += cl; |
nothing calls this directly
no test coverage detected