| 957 | } |
| 958 | |
| 959 | std::vector<std::string> unicode_regex_split(const std::string & text, const std::vector<std::string> & regex_exprs) { |
| 960 | // unicode categories |
| 961 | static const std::map<std::string, int> k_ucat_enum = { |
| 962 | { "\\p{N}", unicode_cpt_flags::NUMBER }, |
| 963 | { "\\p{L}", unicode_cpt_flags::LETTER }, |
| 964 | { "\\p{P}", unicode_cpt_flags::PUNCTUATION }, |
| 965 | { "\\p{M}", unicode_cpt_flags::ACCENT_MARK }, |
| 966 | { "\\p{S}", unicode_cpt_flags::SYMBOL }, |
| 967 | { "\\p{Lu}", unicode_cpt_flags::LETTER }, // Uppercase letter |
| 968 | { "\\p{Ll}", unicode_cpt_flags::LETTER }, // Lowercase letter |
| 969 | { "\\p{Lt}", unicode_cpt_flags::LETTER }, // Titlecase letter |
| 970 | { "\\p{Lm}", unicode_cpt_flags::LETTER }, // Modifier letter |
| 971 | { "\\p{Lo}", unicode_cpt_flags::LETTER }, // Other letter |
| 972 | }; |
| 973 | |
| 974 | static const std::map<int, int> k_ucat_cpt = { |
| 975 | { unicode_cpt_flags::NUMBER, 0xD1 }, |
| 976 | { unicode_cpt_flags::LETTER, 0xD2 }, |
| 977 | { unicode_cpt_flags::PUNCTUATION, 0xD3 }, |
| 978 | { unicode_cpt_flags::ACCENT_MARK, 0xD4 }, |
| 979 | { unicode_cpt_flags::SYMBOL, 0xD5 }, |
| 980 | }; |
| 981 | |
| 982 | static const std::map<int, std::string> k_ucat_map = { |
| 983 | { unicode_cpt_flags::NUMBER, "\x30-\x39" }, // 0-9 |
| 984 | { unicode_cpt_flags::LETTER, "\x41-\x5A\x61-\x7A" }, // A-Za-z |
| 985 | { unicode_cpt_flags::PUNCTUATION, "\x21-\x23\x25-\x2A\x2C-\x2F\x3A-\x3B\x3F-\x40\\\x5B-\\\x5D\x5F\\\x7B\\\x7D" }, // !-#%-*,-/:-;?-@\[-\]_\{\} |
| 986 | { unicode_cpt_flags::ACCENT_MARK, "" }, // no sub-128 codepoints |
| 987 | { unicode_cpt_flags::SYMBOL, "\\\x24\\\x2B\x3C-\x3E\x5E\x60\\\x7C" }, // $+<=>^`| |
| 988 | }; |
| 989 | |
| 990 | // compute collapsed codepoints only if needed by at least one regex |
| 991 | bool need_collapse = false; |
| 992 | for (const auto & regex_expr : regex_exprs) { |
| 993 | // search for unicode categories |
| 994 | for (const auto & ucat : k_ucat_enum) { |
| 995 | if (std::string::npos != regex_expr.find(ucat.first)) { |
| 996 | need_collapse = true; |
| 997 | break; |
| 998 | } |
| 999 | } |
| 1000 | } |
| 1001 | |
| 1002 | const auto cpts = unicode_cpts_from_utf8(text); |
| 1003 | |
| 1004 | // generate a "collapsed" representation of the text, where all codepoints are replaced by a single byte |
| 1005 | // ref: https://github.com/ggml-org/llama.cpp/pull/6920#issuecomment-2081479935 |
| 1006 | std::string text_collapsed; |
| 1007 | if (need_collapse) { |
| 1008 | // collapse all unicode categories |
| 1009 | text_collapsed.resize(cpts.size()); |
| 1010 | |
| 1011 | for (size_t i = 0; i < cpts.size(); ++i) { |
| 1012 | // keep single-byte codepoints as is |
| 1013 | if (cpts[i] < 128) { |
| 1014 | text_collapsed[i] = cpts[i]; |
| 1015 | continue; |
| 1016 | } |
no test coverage detected