diff --git a/CHANGELOG.md b/CHANGELOG.md index 56fcf27..a8f3835 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Provide `--add-missing-ids` extraction option to update source code with auto-generated identifiers - Add `Util\FormatHelper` that provides `getReader()` and `getWriter()` methods - Introduce `Format\Format` final static class for format constants +- Port [@formatjs/icu-messageformat-parser](https://www.npmjs.com/package/@formatjs/icu-messageformat-parser) to FormatPHP (`FormatPHP\Icu\MessageFormat\Parser`) ### Changed diff --git a/src/Icu/MessageFormat/Parser/Util/AbstractCodePointMatcher.php b/src/Icu/MessageFormat/Parser/Util/AbstractCodePointMatcher.php deleted file mode 100644 index c656ba1..0000000 --- a/src/Icu/MessageFormat/Parser/Util/AbstractCodePointMatcher.php +++ /dev/null @@ -1,44 +0,0 @@ - - * @license https://opensource.org/licenses/MIT MIT License - */ - -declare(strict_types=1); - -namespace FormatPHP\Icu\MessageFormat\Parser\Util; - -use function in_array; - -/** - * @internal - */ -abstract class AbstractCodePointMatcher implements CodePointMatcherInterface -{ - /** - * @return int[] - */ - abstract protected function getCodePoints(): array; - - /** - * Checks whether the code point matches a set of known code points - */ - public function matches(int $codepoint): bool - { - return in_array($codepoint, $this->getCodePoints(), true); - } -} diff --git a/src/Icu/MessageFormat/Parser/Util/CodePointHelper.php b/src/Icu/MessageFormat/Parser/Util/CodePointHelper.php index 404de34..5b8d11f 100644 --- a/src/Icu/MessageFormat/Parser/Util/CodePointHelper.php +++ b/src/Icu/MessageFormat/Parser/Util/CodePointHelper.php @@ -30,6 +30,8 @@ use function mb_ord; /** + * A helper for working with code points + * * @internal */ class CodePointHelper @@ -49,32 +51,61 @@ public function __construct() $this->isPotentialElementNameChar = new IsPotentialElementNameChar(); } + /** + * Checks whether a code point is an alphabet character + * + * @see IsAlpha::matches() + */ public function isAlpha(int $codepoint): bool { return $this->isAlpha->matches($codepoint); } + /** + * Checks whether a code point is an alphabet character or a forward slash ("/") + * + * @see IsAlpha::matches() + */ public function isAlphaOrSlash(int $codepoint): bool { - return $this->isAlpha->matchesWithSlash($codepoint); + return $codepoint === 0x002f || $this->isAlpha->matches($codepoint); } + /** + * Checks whether a code point is in the Unicode Character Database + * White_Space and Pattern_White_Space groups + * + * @see IsWhiteSpace::matches() + */ public function isWhiteSpace(int $codepoint): bool { return $this->isWhiteSpace->matches($codepoint); } + /** + * Checks whether a code point is in the Unicode Character Database + * Pattern_Syntax group + * + * @see IsPatternSyntax::matches() + */ public function isPatternSyntax(int $codepoint): bool { return $this->isPatternSyntax->matches($codepoint); } + /** + * Checks whether the code point could be used in an HTML tag element name + * + * @see IsPotentialElementNameChar::matches() + */ public function isPotentialElementNameChar(int $codepoint): bool { return $this->isPotentialElementNameChar->matches($codepoint); } /** + * Returns the code point for a character in a string array at a given offset + * * @param string[] $stringArray */ public function charCodeAt(array $stringArray, int $offset): ?int @@ -92,6 +123,9 @@ public function charCodeAt(array $stringArray, int $offset): ?int return null; } + /** + * Returns the string character for a given code point + */ public function fromCharCode(int $code): ?string { $char = mb_chr($code, Parser::ENCODING); @@ -104,6 +138,8 @@ public function fromCharCode(int $code): ?string } /** + * Returns a string of characters for the given code points + * * @throws InvalidUtf8CodePointException */ public function fromCodePoint(int ...$codePoints): string diff --git a/src/Icu/MessageFormat/Parser/Util/IsAlpha.php b/src/Icu/MessageFormat/Parser/Util/IsAlpha.php index 73600e6..9817c6b 100644 --- a/src/Icu/MessageFormat/Parser/Util/IsAlpha.php +++ b/src/Icu/MessageFormat/Parser/Util/IsAlpha.php @@ -23,85 +23,19 @@ namespace FormatPHP\Icu\MessageFormat\Parser\Util; /** + * Checks whether a code point is an alphabet character + * * @internal */ -class IsAlpha extends AbstractCodePointMatcher +class IsAlpha implements CodePointMatcherInterface { /** - * Latin alphabet code points - * - * This array provides fast lookup of values. - */ - protected const CODE_POINTS = [ - 0x0041, - 0x0042, - 0x0043, - 0x0044, - 0x0045, - 0x0046, - 0x0047, - 0x0048, - 0x0049, - 0x004a, - 0x004b, - 0x004c, - 0x004d, - 0x004e, - 0x004f, - 0x0050, - 0x0051, - 0x0052, - 0x0053, - 0x0054, - 0x0055, - 0x0056, - 0x0057, - 0x0058, - 0x0059, - 0x005a, - 0x0061, - 0x0062, - 0x0063, - 0x0064, - 0x0065, - 0x0066, - 0x0067, - 0x0068, - 0x0069, - 0x006a, - 0x006b, - 0x006c, - 0x006d, - 0x006e, - 0x006f, - 0x0070, - 0x0071, - 0x0072, - 0x0073, - 0x0074, - 0x0075, - 0x0076, - 0x0077, - 0x0078, - 0x0079, - 0x007a, - ]; - - /** - * Checks whether the code point is a forward slash ("/") or an uppercase - * or lowercase alphabet character - */ - public function matchesWithSlash(int $codepoint): bool - { - return $codepoint === 0x002f /* '/' */ - || $this->matches($codepoint); - } - - /** - * @inheritdoc + * Returns true if the code point is a Latin uppercase letter A-Z or + * lowercase letter a-z */ - protected function getCodePoints(): array + public function matches(int $codepoint): bool { - return self::CODE_POINTS; + return ($codepoint >= 0x0041 && $codepoint <= 0x005a) + || ($codepoint >= 0x0061 && $codepoint <= 0x007a); } } diff --git a/src/Icu/MessageFormat/Parser/Util/IsPatternSyntax.php b/src/Icu/MessageFormat/Parser/Util/IsPatternSyntax.php index ce3655c..e6903aa 100644 --- a/src/Icu/MessageFormat/Parser/Util/IsPatternSyntax.php +++ b/src/Icu/MessageFormat/Parser/Util/IsPatternSyntax.php @@ -23,2787 +23,53 @@ namespace FormatPHP\Icu\MessageFormat\Parser\Util; /** + * Checks whether a code point is in the Unicode Character Database + * Pattern_Syntax group + * * @internal */ -class IsPatternSyntax extends AbstractCodePointMatcher +class IsPatternSyntax implements CodePointMatcherInterface { /** - * Pattern syntax code points + * Returns true if the code point is in the Pattern_Syntax group in the + * Unicode Character Database (UCD) * - * This list is derived from the Pattern_Syntax code points listed in the - * Unicode Character Database PropList. We converted it to an array for - * better performance. + * This function matches exactly 2760 code points. The values are derived + * from PropList-14.0.0.txt, dated 2021-08-12, 23:13:05 GMT. Ranges have + * been collapsed for optimal performance. * - * @link https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt UCD PropList - */ - protected const CODE_POINTS = [ - 0x0021, - 0x0022, - 0x0023, - 0x0024, - 0x0025, - 0x0026, - 0x0027, - 0x0028, - 0x0029, - 0x002a, - 0x002b, - 0x002c, - 0x002d, - 0x002e, - 0x002f, - 0x003a, - 0x003b, - 0x003c, - 0x003d, - 0x003e, - 0x003f, - 0x0040, - 0x005b, - 0x005c, - 0x005d, - 0x005e, - 0x0060, - 0x007b, - 0x007c, - 0x007d, - 0x007e, - 0x00a1, - 0x00a2, - 0x00a3, - 0x00a4, - 0x00a5, - 0x00a6, - 0x00a7, - 0x00a9, - 0x00ab, - 0x00ac, - 0x00ae, - 0x00b0, - 0x00b1, - 0x00b6, - 0x00bb, - 0x00bf, - 0x00d7, - 0x00f7, - 0x2010, - 0x2011, - 0x2012, - 0x2013, - 0x2014, - 0x2015, - 0x2016, - 0x2017, - 0x2018, - 0x2019, - 0x201a, - 0x201b, - 0x201c, - 0x201d, - 0x201e, - 0x201f, - 0x2020, - 0x2021, - 0x2022, - 0x2023, - 0x2024, - 0x2025, - 0x2026, - 0x2027, - 0x2030, - 0x2031, - 0x2032, - 0x2033, - 0x2034, - 0x2035, - 0x2036, - 0x2037, - 0x2038, - 0x2039, - 0x203a, - 0x203b, - 0x203c, - 0x203d, - 0x203e, - 0x2041, - 0x2042, - 0x2043, - 0x2044, - 0x2045, - 0x2046, - 0x2047, - 0x2048, - 0x2049, - 0x204a, - 0x204b, - 0x204c, - 0x204d, - 0x204e, - 0x204f, - 0x2050, - 0x2051, - 0x2052, - 0x2053, - 0x2055, - 0x2056, - 0x2057, - 0x2058, - 0x2059, - 0x205a, - 0x205b, - 0x205c, - 0x205d, - 0x205e, - 0x2190, - 0x2191, - 0x2192, - 0x2193, - 0x2194, - 0x2195, - 0x2196, - 0x2197, - 0x2198, - 0x2199, - 0x219a, - 0x219b, - 0x219c, - 0x219d, - 0x219e, - 0x219f, - 0x21a0, - 0x21a1, - 0x21a2, - 0x21a3, - 0x21a4, - 0x21a5, - 0x21a6, - 0x21a7, - 0x21a8, - 0x21a9, - 0x21aa, - 0x21ab, - 0x21ac, - 0x21ad, - 0x21ae, - 0x21af, - 0x21b0, - 0x21b1, - 0x21b2, - 0x21b3, - 0x21b4, - 0x21b5, - 0x21b6, - 0x21b7, - 0x21b8, - 0x21b9, - 0x21ba, - 0x21bb, - 0x21bc, - 0x21bd, - 0x21be, - 0x21bf, - 0x21c0, - 0x21c1, - 0x21c2, - 0x21c3, - 0x21c4, - 0x21c5, - 0x21c6, - 0x21c7, - 0x21c8, - 0x21c9, - 0x21ca, - 0x21cb, - 0x21cc, - 0x21cd, - 0x21ce, - 0x21cf, - 0x21d0, - 0x21d1, - 0x21d2, - 0x21d3, - 0x21d4, - 0x21d5, - 0x21d6, - 0x21d7, - 0x21d8, - 0x21d9, - 0x21da, - 0x21db, - 0x21dc, - 0x21dd, - 0x21de, - 0x21df, - 0x21e0, - 0x21e1, - 0x21e2, - 0x21e3, - 0x21e4, - 0x21e5, - 0x21e6, - 0x21e7, - 0x21e8, - 0x21e9, - 0x21ea, - 0x21eb, - 0x21ec, - 0x21ed, - 0x21ee, - 0x21ef, - 0x21f0, - 0x21f1, - 0x21f2, - 0x21f3, - 0x21f4, - 0x21f5, - 0x21f6, - 0x21f7, - 0x21f8, - 0x21f9, - 0x21fa, - 0x21fb, - 0x21fc, - 0x21fd, - 0x21fe, - 0x21ff, - 0x2200, - 0x2201, - 0x2202, - 0x2203, - 0x2204, - 0x2205, - 0x2206, - 0x2207, - 0x2208, - 0x2209, - 0x220a, - 0x220b, - 0x220c, - 0x220d, - 0x220e, - 0x220f, - 0x2210, - 0x2211, - 0x2212, - 0x2213, - 0x2214, - 0x2215, - 0x2216, - 0x2217, - 0x2218, - 0x2219, - 0x221a, - 0x221b, - 0x221c, - 0x221d, - 0x221e, - 0x221f, - 0x2220, - 0x2221, - 0x2222, - 0x2223, - 0x2224, - 0x2225, - 0x2226, - 0x2227, - 0x2228, - 0x2229, - 0x222a, - 0x222b, - 0x222c, - 0x222d, - 0x222e, - 0x222f, - 0x2230, - 0x2231, - 0x2232, - 0x2233, - 0x2234, - 0x2235, - 0x2236, - 0x2237, - 0x2238, - 0x2239, - 0x223a, - 0x223b, - 0x223c, - 0x223d, - 0x223e, - 0x223f, - 0x2240, - 0x2241, - 0x2242, - 0x2243, - 0x2244, - 0x2245, - 0x2246, - 0x2247, - 0x2248, - 0x2249, - 0x224a, - 0x224b, - 0x224c, - 0x224d, - 0x224e, - 0x224f, - 0x2250, - 0x2251, - 0x2252, - 0x2253, - 0x2254, - 0x2255, - 0x2256, - 0x2257, - 0x2258, - 0x2259, - 0x225a, - 0x225b, - 0x225c, - 0x225d, - 0x225e, - 0x225f, - 0x2260, - 0x2261, - 0x2262, - 0x2263, - 0x2264, - 0x2265, - 0x2266, - 0x2267, - 0x2268, - 0x2269, - 0x226a, - 0x226b, - 0x226c, - 0x226d, - 0x226e, - 0x226f, - 0x2270, - 0x2271, - 0x2272, - 0x2273, - 0x2274, - 0x2275, - 0x2276, - 0x2277, - 0x2278, - 0x2279, - 0x227a, - 0x227b, - 0x227c, - 0x227d, - 0x227e, - 0x227f, - 0x2280, - 0x2281, - 0x2282, - 0x2283, - 0x2284, - 0x2285, - 0x2286, - 0x2287, - 0x2288, - 0x2289, - 0x228a, - 0x228b, - 0x228c, - 0x228d, - 0x228e, - 0x228f, - 0x2290, - 0x2291, - 0x2292, - 0x2293, - 0x2294, - 0x2295, - 0x2296, - 0x2297, - 0x2298, - 0x2299, - 0x229a, - 0x229b, - 0x229c, - 0x229d, - 0x229e, - 0x229f, - 0x22a0, - 0x22a1, - 0x22a2, - 0x22a3, - 0x22a4, - 0x22a5, - 0x22a6, - 0x22a7, - 0x22a8, - 0x22a9, - 0x22aa, - 0x22ab, - 0x22ac, - 0x22ad, - 0x22ae, - 0x22af, - 0x22b0, - 0x22b1, - 0x22b2, - 0x22b3, - 0x22b4, - 0x22b5, - 0x22b6, - 0x22b7, - 0x22b8, - 0x22b9, - 0x22ba, - 0x22bb, - 0x22bc, - 0x22bd, - 0x22be, - 0x22bf, - 0x22c0, - 0x22c1, - 0x22c2, - 0x22c3, - 0x22c4, - 0x22c5, - 0x22c6, - 0x22c7, - 0x22c8, - 0x22c9, - 0x22ca, - 0x22cb, - 0x22cc, - 0x22cd, - 0x22ce, - 0x22cf, - 0x22d0, - 0x22d1, - 0x22d2, - 0x22d3, - 0x22d4, - 0x22d5, - 0x22d6, - 0x22d7, - 0x22d8, - 0x22d9, - 0x22da, - 0x22db, - 0x22dc, - 0x22dd, - 0x22de, - 0x22df, - 0x22e0, - 0x22e1, - 0x22e2, - 0x22e3, - 0x22e4, - 0x22e5, - 0x22e6, - 0x22e7, - 0x22e8, - 0x22e9, - 0x22ea, - 0x22eb, - 0x22ec, - 0x22ed, - 0x22ee, - 0x22ef, - 0x22f0, - 0x22f1, - 0x22f2, - 0x22f3, - 0x22f4, - 0x22f5, - 0x22f6, - 0x22f7, - 0x22f8, - 0x22f9, - 0x22fa, - 0x22fb, - 0x22fc, - 0x22fd, - 0x22fe, - 0x22ff, - 0x2300, - 0x2301, - 0x2302, - 0x2303, - 0x2304, - 0x2305, - 0x2306, - 0x2307, - 0x2308, - 0x2309, - 0x230a, - 0x230b, - 0x230c, - 0x230d, - 0x230e, - 0x230f, - 0x2310, - 0x2311, - 0x2312, - 0x2313, - 0x2314, - 0x2315, - 0x2316, - 0x2317, - 0x2318, - 0x2319, - 0x231a, - 0x231b, - 0x231c, - 0x231d, - 0x231e, - 0x231f, - 0x2320, - 0x2321, - 0x2322, - 0x2323, - 0x2324, - 0x2325, - 0x2326, - 0x2327, - 0x2328, - 0x2329, - 0x232a, - 0x232b, - 0x232c, - 0x232d, - 0x232e, - 0x232f, - 0x2330, - 0x2331, - 0x2332, - 0x2333, - 0x2334, - 0x2335, - 0x2336, - 0x2337, - 0x2338, - 0x2339, - 0x233a, - 0x233b, - 0x233c, - 0x233d, - 0x233e, - 0x233f, - 0x2340, - 0x2341, - 0x2342, - 0x2343, - 0x2344, - 0x2345, - 0x2346, - 0x2347, - 0x2348, - 0x2349, - 0x234a, - 0x234b, - 0x234c, - 0x234d, - 0x234e, - 0x234f, - 0x2350, - 0x2351, - 0x2352, - 0x2353, - 0x2354, - 0x2355, - 0x2356, - 0x2357, - 0x2358, - 0x2359, - 0x235a, - 0x235b, - 0x235c, - 0x235d, - 0x235e, - 0x235f, - 0x2360, - 0x2361, - 0x2362, - 0x2363, - 0x2364, - 0x2365, - 0x2366, - 0x2367, - 0x2368, - 0x2369, - 0x236a, - 0x236b, - 0x236c, - 0x236d, - 0x236e, - 0x236f, - 0x2370, - 0x2371, - 0x2372, - 0x2373, - 0x2374, - 0x2375, - 0x2376, - 0x2377, - 0x2378, - 0x2379, - 0x237a, - 0x237b, - 0x237c, - 0x237d, - 0x237e, - 0x237f, - 0x2380, - 0x2381, - 0x2382, - 0x2383, - 0x2384, - 0x2385, - 0x2386, - 0x2387, - 0x2388, - 0x2389, - 0x238a, - 0x238b, - 0x238c, - 0x238d, - 0x238e, - 0x238f, - 0x2390, - 0x2391, - 0x2392, - 0x2393, - 0x2394, - 0x2395, - 0x2396, - 0x2397, - 0x2398, - 0x2399, - 0x239a, - 0x239b, - 0x239c, - 0x239d, - 0x239e, - 0x239f, - 0x23a0, - 0x23a1, - 0x23a2, - 0x23a3, - 0x23a4, - 0x23a5, - 0x23a6, - 0x23a7, - 0x23a8, - 0x23a9, - 0x23aa, - 0x23ab, - 0x23ac, - 0x23ad, - 0x23ae, - 0x23af, - 0x23b0, - 0x23b1, - 0x23b2, - 0x23b3, - 0x23b4, - 0x23b5, - 0x23b6, - 0x23b7, - 0x23b8, - 0x23b9, - 0x23ba, - 0x23bb, - 0x23bc, - 0x23bd, - 0x23be, - 0x23bf, - 0x23c0, - 0x23c1, - 0x23c2, - 0x23c3, - 0x23c4, - 0x23c5, - 0x23c6, - 0x23c7, - 0x23c8, - 0x23c9, - 0x23ca, - 0x23cb, - 0x23cc, - 0x23cd, - 0x23ce, - 0x23cf, - 0x23d0, - 0x23d1, - 0x23d2, - 0x23d3, - 0x23d4, - 0x23d5, - 0x23d6, - 0x23d7, - 0x23d8, - 0x23d9, - 0x23da, - 0x23db, - 0x23dc, - 0x23dd, - 0x23de, - 0x23df, - 0x23e0, - 0x23e1, - 0x23e2, - 0x23e3, - 0x23e4, - 0x23e5, - 0x23e6, - 0x23e7, - 0x23e8, - 0x23e9, - 0x23ea, - 0x23eb, - 0x23ec, - 0x23ed, - 0x23ee, - 0x23ef, - 0x23f0, - 0x23f1, - 0x23f2, - 0x23f3, - 0x23f4, - 0x23f5, - 0x23f6, - 0x23f7, - 0x23f8, - 0x23f9, - 0x23fa, - 0x23fb, - 0x23fc, - 0x23fd, - 0x23fe, - 0x23ff, - 0x2400, - 0x2401, - 0x2402, - 0x2403, - 0x2404, - 0x2405, - 0x2406, - 0x2407, - 0x2408, - 0x2409, - 0x240a, - 0x240b, - 0x240c, - 0x240d, - 0x240e, - 0x240f, - 0x2410, - 0x2411, - 0x2412, - 0x2413, - 0x2414, - 0x2415, - 0x2416, - 0x2417, - 0x2418, - 0x2419, - 0x241a, - 0x241b, - 0x241c, - 0x241d, - 0x241e, - 0x241f, - 0x2420, - 0x2421, - 0x2422, - 0x2423, - 0x2424, - 0x2425, - 0x2426, - 0x2427, - 0x2428, - 0x2429, - 0x242a, - 0x242b, - 0x242c, - 0x242d, - 0x242e, - 0x242f, - 0x2430, - 0x2431, - 0x2432, - 0x2433, - 0x2434, - 0x2435, - 0x2436, - 0x2437, - 0x2438, - 0x2439, - 0x243a, - 0x243b, - 0x243c, - 0x243d, - 0x243e, - 0x243f, - 0x2440, - 0x2441, - 0x2442, - 0x2443, - 0x2444, - 0x2445, - 0x2446, - 0x2447, - 0x2448, - 0x2449, - 0x244a, - 0x244b, - 0x244c, - 0x244d, - 0x244e, - 0x244f, - 0x2450, - 0x2451, - 0x2452, - 0x2453, - 0x2454, - 0x2455, - 0x2456, - 0x2457, - 0x2458, - 0x2459, - 0x245a, - 0x245b, - 0x245c, - 0x245d, - 0x245e, - 0x245f, - 0x2500, - 0x2501, - 0x2502, - 0x2503, - 0x2504, - 0x2505, - 0x2506, - 0x2507, - 0x2508, - 0x2509, - 0x250a, - 0x250b, - 0x250c, - 0x250d, - 0x250e, - 0x250f, - 0x2510, - 0x2511, - 0x2512, - 0x2513, - 0x2514, - 0x2515, - 0x2516, - 0x2517, - 0x2518, - 0x2519, - 0x251a, - 0x251b, - 0x251c, - 0x251d, - 0x251e, - 0x251f, - 0x2520, - 0x2521, - 0x2522, - 0x2523, - 0x2524, - 0x2525, - 0x2526, - 0x2527, - 0x2528, - 0x2529, - 0x252a, - 0x252b, - 0x252c, - 0x252d, - 0x252e, - 0x252f, - 0x2530, - 0x2531, - 0x2532, - 0x2533, - 0x2534, - 0x2535, - 0x2536, - 0x2537, - 0x2538, - 0x2539, - 0x253a, - 0x253b, - 0x253c, - 0x253d, - 0x253e, - 0x253f, - 0x2540, - 0x2541, - 0x2542, - 0x2543, - 0x2544, - 0x2545, - 0x2546, - 0x2547, - 0x2548, - 0x2549, - 0x254a, - 0x254b, - 0x254c, - 0x254d, - 0x254e, - 0x254f, - 0x2550, - 0x2551, - 0x2552, - 0x2553, - 0x2554, - 0x2555, - 0x2556, - 0x2557, - 0x2558, - 0x2559, - 0x255a, - 0x255b, - 0x255c, - 0x255d, - 0x255e, - 0x255f, - 0x2560, - 0x2561, - 0x2562, - 0x2563, - 0x2564, - 0x2565, - 0x2566, - 0x2567, - 0x2568, - 0x2569, - 0x256a, - 0x256b, - 0x256c, - 0x256d, - 0x256e, - 0x256f, - 0x2570, - 0x2571, - 0x2572, - 0x2573, - 0x2574, - 0x2575, - 0x2576, - 0x2577, - 0x2578, - 0x2579, - 0x257a, - 0x257b, - 0x257c, - 0x257d, - 0x257e, - 0x257f, - 0x2580, - 0x2581, - 0x2582, - 0x2583, - 0x2584, - 0x2585, - 0x2586, - 0x2587, - 0x2588, - 0x2589, - 0x258a, - 0x258b, - 0x258c, - 0x258d, - 0x258e, - 0x258f, - 0x2590, - 0x2591, - 0x2592, - 0x2593, - 0x2594, - 0x2595, - 0x2596, - 0x2597, - 0x2598, - 0x2599, - 0x259a, - 0x259b, - 0x259c, - 0x259d, - 0x259e, - 0x259f, - 0x25a0, - 0x25a1, - 0x25a2, - 0x25a3, - 0x25a4, - 0x25a5, - 0x25a6, - 0x25a7, - 0x25a8, - 0x25a9, - 0x25aa, - 0x25ab, - 0x25ac, - 0x25ad, - 0x25ae, - 0x25af, - 0x25b0, - 0x25b1, - 0x25b2, - 0x25b3, - 0x25b4, - 0x25b5, - 0x25b6, - 0x25b7, - 0x25b8, - 0x25b9, - 0x25ba, - 0x25bb, - 0x25bc, - 0x25bd, - 0x25be, - 0x25bf, - 0x25c0, - 0x25c1, - 0x25c2, - 0x25c3, - 0x25c4, - 0x25c5, - 0x25c6, - 0x25c7, - 0x25c8, - 0x25c9, - 0x25ca, - 0x25cb, - 0x25cc, - 0x25cd, - 0x25ce, - 0x25cf, - 0x25d0, - 0x25d1, - 0x25d2, - 0x25d3, - 0x25d4, - 0x25d5, - 0x25d6, - 0x25d7, - 0x25d8, - 0x25d9, - 0x25da, - 0x25db, - 0x25dc, - 0x25dd, - 0x25de, - 0x25df, - 0x25e0, - 0x25e1, - 0x25e2, - 0x25e3, - 0x25e4, - 0x25e5, - 0x25e6, - 0x25e7, - 0x25e8, - 0x25e9, - 0x25ea, - 0x25eb, - 0x25ec, - 0x25ed, - 0x25ee, - 0x25ef, - 0x25f0, - 0x25f1, - 0x25f2, - 0x25f3, - 0x25f4, - 0x25f5, - 0x25f6, - 0x25f7, - 0x25f8, - 0x25f9, - 0x25fa, - 0x25fb, - 0x25fc, - 0x25fd, - 0x25fe, - 0x25ff, - 0x2600, - 0x2601, - 0x2602, - 0x2603, - 0x2604, - 0x2605, - 0x2606, - 0x2607, - 0x2608, - 0x2609, - 0x260a, - 0x260b, - 0x260c, - 0x260d, - 0x260e, - 0x260f, - 0x2610, - 0x2611, - 0x2612, - 0x2613, - 0x2614, - 0x2615, - 0x2616, - 0x2617, - 0x2618, - 0x2619, - 0x261a, - 0x261b, - 0x261c, - 0x261d, - 0x261e, - 0x261f, - 0x2620, - 0x2621, - 0x2622, - 0x2623, - 0x2624, - 0x2625, - 0x2626, - 0x2627, - 0x2628, - 0x2629, - 0x262a, - 0x262b, - 0x262c, - 0x262d, - 0x262e, - 0x262f, - 0x2630, - 0x2631, - 0x2632, - 0x2633, - 0x2634, - 0x2635, - 0x2636, - 0x2637, - 0x2638, - 0x2639, - 0x263a, - 0x263b, - 0x263c, - 0x263d, - 0x263e, - 0x263f, - 0x2640, - 0x2641, - 0x2642, - 0x2643, - 0x2644, - 0x2645, - 0x2646, - 0x2647, - 0x2648, - 0x2649, - 0x264a, - 0x264b, - 0x264c, - 0x264d, - 0x264e, - 0x264f, - 0x2650, - 0x2651, - 0x2652, - 0x2653, - 0x2654, - 0x2655, - 0x2656, - 0x2657, - 0x2658, - 0x2659, - 0x265a, - 0x265b, - 0x265c, - 0x265d, - 0x265e, - 0x265f, - 0x2660, - 0x2661, - 0x2662, - 0x2663, - 0x2664, - 0x2665, - 0x2666, - 0x2667, - 0x2668, - 0x2669, - 0x266a, - 0x266b, - 0x266c, - 0x266d, - 0x266e, - 0x266f, - 0x2670, - 0x2671, - 0x2672, - 0x2673, - 0x2674, - 0x2675, - 0x2676, - 0x2677, - 0x2678, - 0x2679, - 0x267a, - 0x267b, - 0x267c, - 0x267d, - 0x267e, - 0x267f, - 0x2680, - 0x2681, - 0x2682, - 0x2683, - 0x2684, - 0x2685, - 0x2686, - 0x2687, - 0x2688, - 0x2689, - 0x268a, - 0x268b, - 0x268c, - 0x268d, - 0x268e, - 0x268f, - 0x2690, - 0x2691, - 0x2692, - 0x2693, - 0x2694, - 0x2695, - 0x2696, - 0x2697, - 0x2698, - 0x2699, - 0x269a, - 0x269b, - 0x269c, - 0x269d, - 0x269e, - 0x269f, - 0x26a0, - 0x26a1, - 0x26a2, - 0x26a3, - 0x26a4, - 0x26a5, - 0x26a6, - 0x26a7, - 0x26a8, - 0x26a9, - 0x26aa, - 0x26ab, - 0x26ac, - 0x26ad, - 0x26ae, - 0x26af, - 0x26b0, - 0x26b1, - 0x26b2, - 0x26b3, - 0x26b4, - 0x26b5, - 0x26b6, - 0x26b7, - 0x26b8, - 0x26b9, - 0x26ba, - 0x26bb, - 0x26bc, - 0x26bd, - 0x26be, - 0x26bf, - 0x26c0, - 0x26c1, - 0x26c2, - 0x26c3, - 0x26c4, - 0x26c5, - 0x26c6, - 0x26c7, - 0x26c8, - 0x26c9, - 0x26ca, - 0x26cb, - 0x26cc, - 0x26cd, - 0x26ce, - 0x26cf, - 0x26d0, - 0x26d1, - 0x26d2, - 0x26d3, - 0x26d4, - 0x26d5, - 0x26d6, - 0x26d7, - 0x26d8, - 0x26d9, - 0x26da, - 0x26db, - 0x26dc, - 0x26dd, - 0x26de, - 0x26df, - 0x26e0, - 0x26e1, - 0x26e2, - 0x26e3, - 0x26e4, - 0x26e5, - 0x26e6, - 0x26e7, - 0x26e8, - 0x26e9, - 0x26ea, - 0x26eb, - 0x26ec, - 0x26ed, - 0x26ee, - 0x26ef, - 0x26f0, - 0x26f1, - 0x26f2, - 0x26f3, - 0x26f4, - 0x26f5, - 0x26f6, - 0x26f7, - 0x26f8, - 0x26f9, - 0x26fa, - 0x26fb, - 0x26fc, - 0x26fd, - 0x26fe, - 0x26ff, - 0x2700, - 0x2701, - 0x2702, - 0x2703, - 0x2704, - 0x2705, - 0x2706, - 0x2707, - 0x2708, - 0x2709, - 0x270a, - 0x270b, - 0x270c, - 0x270d, - 0x270e, - 0x270f, - 0x2710, - 0x2711, - 0x2712, - 0x2713, - 0x2714, - 0x2715, - 0x2716, - 0x2717, - 0x2718, - 0x2719, - 0x271a, - 0x271b, - 0x271c, - 0x271d, - 0x271e, - 0x271f, - 0x2720, - 0x2721, - 0x2722, - 0x2723, - 0x2724, - 0x2725, - 0x2726, - 0x2727, - 0x2728, - 0x2729, - 0x272a, - 0x272b, - 0x272c, - 0x272d, - 0x272e, - 0x272f, - 0x2730, - 0x2731, - 0x2732, - 0x2733, - 0x2734, - 0x2735, - 0x2736, - 0x2737, - 0x2738, - 0x2739, - 0x273a, - 0x273b, - 0x273c, - 0x273d, - 0x273e, - 0x273f, - 0x2740, - 0x2741, - 0x2742, - 0x2743, - 0x2744, - 0x2745, - 0x2746, - 0x2747, - 0x2748, - 0x2749, - 0x274a, - 0x274b, - 0x274c, - 0x274d, - 0x274e, - 0x274f, - 0x2750, - 0x2751, - 0x2752, - 0x2753, - 0x2754, - 0x2755, - 0x2756, - 0x2757, - 0x2758, - 0x2759, - 0x275a, - 0x275b, - 0x275c, - 0x275d, - 0x275e, - 0x275f, - 0x2760, - 0x2761, - 0x2762, - 0x2763, - 0x2764, - 0x2765, - 0x2766, - 0x2767, - 0x2768, - 0x2769, - 0x276a, - 0x276b, - 0x276c, - 0x276d, - 0x276e, - 0x276f, - 0x2770, - 0x2771, - 0x2772, - 0x2773, - 0x2774, - 0x2775, - 0x2794, - 0x2795, - 0x2796, - 0x2797, - 0x2798, - 0x2799, - 0x279a, - 0x279b, - 0x279c, - 0x279d, - 0x279e, - 0x279f, - 0x27a0, - 0x27a1, - 0x27a2, - 0x27a3, - 0x27a4, - 0x27a5, - 0x27a6, - 0x27a7, - 0x27a8, - 0x27a9, - 0x27aa, - 0x27ab, - 0x27ac, - 0x27ad, - 0x27ae, - 0x27af, - 0x27b0, - 0x27b1, - 0x27b2, - 0x27b3, - 0x27b4, - 0x27b5, - 0x27b6, - 0x27b7, - 0x27b8, - 0x27b9, - 0x27ba, - 0x27bb, - 0x27bc, - 0x27bd, - 0x27be, - 0x27bf, - 0x27c0, - 0x27c1, - 0x27c2, - 0x27c3, - 0x27c4, - 0x27c5, - 0x27c6, - 0x27c7, - 0x27c8, - 0x27c9, - 0x27ca, - 0x27cb, - 0x27cc, - 0x27cd, - 0x27ce, - 0x27cf, - 0x27d0, - 0x27d1, - 0x27d2, - 0x27d3, - 0x27d4, - 0x27d5, - 0x27d6, - 0x27d7, - 0x27d8, - 0x27d9, - 0x27da, - 0x27db, - 0x27dc, - 0x27dd, - 0x27de, - 0x27df, - 0x27e0, - 0x27e1, - 0x27e2, - 0x27e3, - 0x27e4, - 0x27e5, - 0x27e6, - 0x27e7, - 0x27e8, - 0x27e9, - 0x27ea, - 0x27eb, - 0x27ec, - 0x27ed, - 0x27ee, - 0x27ef, - 0x27f0, - 0x27f1, - 0x27f2, - 0x27f3, - 0x27f4, - 0x27f5, - 0x27f6, - 0x27f7, - 0x27f8, - 0x27f9, - 0x27fa, - 0x27fb, - 0x27fc, - 0x27fd, - 0x27fe, - 0x27ff, - 0x2800, - 0x2801, - 0x2802, - 0x2803, - 0x2804, - 0x2805, - 0x2806, - 0x2807, - 0x2808, - 0x2809, - 0x280a, - 0x280b, - 0x280c, - 0x280d, - 0x280e, - 0x280f, - 0x2810, - 0x2811, - 0x2812, - 0x2813, - 0x2814, - 0x2815, - 0x2816, - 0x2817, - 0x2818, - 0x2819, - 0x281a, - 0x281b, - 0x281c, - 0x281d, - 0x281e, - 0x281f, - 0x2820, - 0x2821, - 0x2822, - 0x2823, - 0x2824, - 0x2825, - 0x2826, - 0x2827, - 0x2828, - 0x2829, - 0x282a, - 0x282b, - 0x282c, - 0x282d, - 0x282e, - 0x282f, - 0x2830, - 0x2831, - 0x2832, - 0x2833, - 0x2834, - 0x2835, - 0x2836, - 0x2837, - 0x2838, - 0x2839, - 0x283a, - 0x283b, - 0x283c, - 0x283d, - 0x283e, - 0x283f, - 0x2840, - 0x2841, - 0x2842, - 0x2843, - 0x2844, - 0x2845, - 0x2846, - 0x2847, - 0x2848, - 0x2849, - 0x284a, - 0x284b, - 0x284c, - 0x284d, - 0x284e, - 0x284f, - 0x2850, - 0x2851, - 0x2852, - 0x2853, - 0x2854, - 0x2855, - 0x2856, - 0x2857, - 0x2858, - 0x2859, - 0x285a, - 0x285b, - 0x285c, - 0x285d, - 0x285e, - 0x285f, - 0x2860, - 0x2861, - 0x2862, - 0x2863, - 0x2864, - 0x2865, - 0x2866, - 0x2867, - 0x2868, - 0x2869, - 0x286a, - 0x286b, - 0x286c, - 0x286d, - 0x286e, - 0x286f, - 0x2870, - 0x2871, - 0x2872, - 0x2873, - 0x2874, - 0x2875, - 0x2876, - 0x2877, - 0x2878, - 0x2879, - 0x287a, - 0x287b, - 0x287c, - 0x287d, - 0x287e, - 0x287f, - 0x2880, - 0x2881, - 0x2882, - 0x2883, - 0x2884, - 0x2885, - 0x2886, - 0x2887, - 0x2888, - 0x2889, - 0x288a, - 0x288b, - 0x288c, - 0x288d, - 0x288e, - 0x288f, - 0x2890, - 0x2891, - 0x2892, - 0x2893, - 0x2894, - 0x2895, - 0x2896, - 0x2897, - 0x2898, - 0x2899, - 0x289a, - 0x289b, - 0x289c, - 0x289d, - 0x289e, - 0x289f, - 0x28a0, - 0x28a1, - 0x28a2, - 0x28a3, - 0x28a4, - 0x28a5, - 0x28a6, - 0x28a7, - 0x28a8, - 0x28a9, - 0x28aa, - 0x28ab, - 0x28ac, - 0x28ad, - 0x28ae, - 0x28af, - 0x28b0, - 0x28b1, - 0x28b2, - 0x28b3, - 0x28b4, - 0x28b5, - 0x28b6, - 0x28b7, - 0x28b8, - 0x28b9, - 0x28ba, - 0x28bb, - 0x28bc, - 0x28bd, - 0x28be, - 0x28bf, - 0x28c0, - 0x28c1, - 0x28c2, - 0x28c3, - 0x28c4, - 0x28c5, - 0x28c6, - 0x28c7, - 0x28c8, - 0x28c9, - 0x28ca, - 0x28cb, - 0x28cc, - 0x28cd, - 0x28ce, - 0x28cf, - 0x28d0, - 0x28d1, - 0x28d2, - 0x28d3, - 0x28d4, - 0x28d5, - 0x28d6, - 0x28d7, - 0x28d8, - 0x28d9, - 0x28da, - 0x28db, - 0x28dc, - 0x28dd, - 0x28de, - 0x28df, - 0x28e0, - 0x28e1, - 0x28e2, - 0x28e3, - 0x28e4, - 0x28e5, - 0x28e6, - 0x28e7, - 0x28e8, - 0x28e9, - 0x28ea, - 0x28eb, - 0x28ec, - 0x28ed, - 0x28ee, - 0x28ef, - 0x28f0, - 0x28f1, - 0x28f2, - 0x28f3, - 0x28f4, - 0x28f5, - 0x28f6, - 0x28f7, - 0x28f8, - 0x28f9, - 0x28fa, - 0x28fb, - 0x28fc, - 0x28fd, - 0x28fe, - 0x28ff, - 0x2900, - 0x2901, - 0x2902, - 0x2903, - 0x2904, - 0x2905, - 0x2906, - 0x2907, - 0x2908, - 0x2909, - 0x290a, - 0x290b, - 0x290c, - 0x290d, - 0x290e, - 0x290f, - 0x2910, - 0x2911, - 0x2912, - 0x2913, - 0x2914, - 0x2915, - 0x2916, - 0x2917, - 0x2918, - 0x2919, - 0x291a, - 0x291b, - 0x291c, - 0x291d, - 0x291e, - 0x291f, - 0x2920, - 0x2921, - 0x2922, - 0x2923, - 0x2924, - 0x2925, - 0x2926, - 0x2927, - 0x2928, - 0x2929, - 0x292a, - 0x292b, - 0x292c, - 0x292d, - 0x292e, - 0x292f, - 0x2930, - 0x2931, - 0x2932, - 0x2933, - 0x2934, - 0x2935, - 0x2936, - 0x2937, - 0x2938, - 0x2939, - 0x293a, - 0x293b, - 0x293c, - 0x293d, - 0x293e, - 0x293f, - 0x2940, - 0x2941, - 0x2942, - 0x2943, - 0x2944, - 0x2945, - 0x2946, - 0x2947, - 0x2948, - 0x2949, - 0x294a, - 0x294b, - 0x294c, - 0x294d, - 0x294e, - 0x294f, - 0x2950, - 0x2951, - 0x2952, - 0x2953, - 0x2954, - 0x2955, - 0x2956, - 0x2957, - 0x2958, - 0x2959, - 0x295a, - 0x295b, - 0x295c, - 0x295d, - 0x295e, - 0x295f, - 0x2960, - 0x2961, - 0x2962, - 0x2963, - 0x2964, - 0x2965, - 0x2966, - 0x2967, - 0x2968, - 0x2969, - 0x296a, - 0x296b, - 0x296c, - 0x296d, - 0x296e, - 0x296f, - 0x2970, - 0x2971, - 0x2972, - 0x2973, - 0x2974, - 0x2975, - 0x2976, - 0x2977, - 0x2978, - 0x2979, - 0x297a, - 0x297b, - 0x297c, - 0x297d, - 0x297e, - 0x297f, - 0x2980, - 0x2981, - 0x2982, - 0x2983, - 0x2984, - 0x2985, - 0x2986, - 0x2987, - 0x2988, - 0x2989, - 0x298a, - 0x298b, - 0x298c, - 0x298d, - 0x298e, - 0x298f, - 0x2990, - 0x2991, - 0x2992, - 0x2993, - 0x2994, - 0x2995, - 0x2996, - 0x2997, - 0x2998, - 0x2999, - 0x299a, - 0x299b, - 0x299c, - 0x299d, - 0x299e, - 0x299f, - 0x29a0, - 0x29a1, - 0x29a2, - 0x29a3, - 0x29a4, - 0x29a5, - 0x29a6, - 0x29a7, - 0x29a8, - 0x29a9, - 0x29aa, - 0x29ab, - 0x29ac, - 0x29ad, - 0x29ae, - 0x29af, - 0x29b0, - 0x29b1, - 0x29b2, - 0x29b3, - 0x29b4, - 0x29b5, - 0x29b6, - 0x29b7, - 0x29b8, - 0x29b9, - 0x29ba, - 0x29bb, - 0x29bc, - 0x29bd, - 0x29be, - 0x29bf, - 0x29c0, - 0x29c1, - 0x29c2, - 0x29c3, - 0x29c4, - 0x29c5, - 0x29c6, - 0x29c7, - 0x29c8, - 0x29c9, - 0x29ca, - 0x29cb, - 0x29cc, - 0x29cd, - 0x29ce, - 0x29cf, - 0x29d0, - 0x29d1, - 0x29d2, - 0x29d3, - 0x29d4, - 0x29d5, - 0x29d6, - 0x29d7, - 0x29d8, - 0x29d9, - 0x29da, - 0x29db, - 0x29dc, - 0x29dd, - 0x29de, - 0x29df, - 0x29e0, - 0x29e1, - 0x29e2, - 0x29e3, - 0x29e4, - 0x29e5, - 0x29e6, - 0x29e7, - 0x29e8, - 0x29e9, - 0x29ea, - 0x29eb, - 0x29ec, - 0x29ed, - 0x29ee, - 0x29ef, - 0x29f0, - 0x29f1, - 0x29f2, - 0x29f3, - 0x29f4, - 0x29f5, - 0x29f6, - 0x29f7, - 0x29f8, - 0x29f9, - 0x29fa, - 0x29fb, - 0x29fc, - 0x29fd, - 0x29fe, - 0x29ff, - 0x2a00, - 0x2a01, - 0x2a02, - 0x2a03, - 0x2a04, - 0x2a05, - 0x2a06, - 0x2a07, - 0x2a08, - 0x2a09, - 0x2a0a, - 0x2a0b, - 0x2a0c, - 0x2a0d, - 0x2a0e, - 0x2a0f, - 0x2a10, - 0x2a11, - 0x2a12, - 0x2a13, - 0x2a14, - 0x2a15, - 0x2a16, - 0x2a17, - 0x2a18, - 0x2a19, - 0x2a1a, - 0x2a1b, - 0x2a1c, - 0x2a1d, - 0x2a1e, - 0x2a1f, - 0x2a20, - 0x2a21, - 0x2a22, - 0x2a23, - 0x2a24, - 0x2a25, - 0x2a26, - 0x2a27, - 0x2a28, - 0x2a29, - 0x2a2a, - 0x2a2b, - 0x2a2c, - 0x2a2d, - 0x2a2e, - 0x2a2f, - 0x2a30, - 0x2a31, - 0x2a32, - 0x2a33, - 0x2a34, - 0x2a35, - 0x2a36, - 0x2a37, - 0x2a38, - 0x2a39, - 0x2a3a, - 0x2a3b, - 0x2a3c, - 0x2a3d, - 0x2a3e, - 0x2a3f, - 0x2a40, - 0x2a41, - 0x2a42, - 0x2a43, - 0x2a44, - 0x2a45, - 0x2a46, - 0x2a47, - 0x2a48, - 0x2a49, - 0x2a4a, - 0x2a4b, - 0x2a4c, - 0x2a4d, - 0x2a4e, - 0x2a4f, - 0x2a50, - 0x2a51, - 0x2a52, - 0x2a53, - 0x2a54, - 0x2a55, - 0x2a56, - 0x2a57, - 0x2a58, - 0x2a59, - 0x2a5a, - 0x2a5b, - 0x2a5c, - 0x2a5d, - 0x2a5e, - 0x2a5f, - 0x2a60, - 0x2a61, - 0x2a62, - 0x2a63, - 0x2a64, - 0x2a65, - 0x2a66, - 0x2a67, - 0x2a68, - 0x2a69, - 0x2a6a, - 0x2a6b, - 0x2a6c, - 0x2a6d, - 0x2a6e, - 0x2a6f, - 0x2a70, - 0x2a71, - 0x2a72, - 0x2a73, - 0x2a74, - 0x2a75, - 0x2a76, - 0x2a77, - 0x2a78, - 0x2a79, - 0x2a7a, - 0x2a7b, - 0x2a7c, - 0x2a7d, - 0x2a7e, - 0x2a7f, - 0x2a80, - 0x2a81, - 0x2a82, - 0x2a83, - 0x2a84, - 0x2a85, - 0x2a86, - 0x2a87, - 0x2a88, - 0x2a89, - 0x2a8a, - 0x2a8b, - 0x2a8c, - 0x2a8d, - 0x2a8e, - 0x2a8f, - 0x2a90, - 0x2a91, - 0x2a92, - 0x2a93, - 0x2a94, - 0x2a95, - 0x2a96, - 0x2a97, - 0x2a98, - 0x2a99, - 0x2a9a, - 0x2a9b, - 0x2a9c, - 0x2a9d, - 0x2a9e, - 0x2a9f, - 0x2aa0, - 0x2aa1, - 0x2aa2, - 0x2aa3, - 0x2aa4, - 0x2aa5, - 0x2aa6, - 0x2aa7, - 0x2aa8, - 0x2aa9, - 0x2aaa, - 0x2aab, - 0x2aac, - 0x2aad, - 0x2aae, - 0x2aaf, - 0x2ab0, - 0x2ab1, - 0x2ab2, - 0x2ab3, - 0x2ab4, - 0x2ab5, - 0x2ab6, - 0x2ab7, - 0x2ab8, - 0x2ab9, - 0x2aba, - 0x2abb, - 0x2abc, - 0x2abd, - 0x2abe, - 0x2abf, - 0x2ac0, - 0x2ac1, - 0x2ac2, - 0x2ac3, - 0x2ac4, - 0x2ac5, - 0x2ac6, - 0x2ac7, - 0x2ac8, - 0x2ac9, - 0x2aca, - 0x2acb, - 0x2acc, - 0x2acd, - 0x2ace, - 0x2acf, - 0x2ad0, - 0x2ad1, - 0x2ad2, - 0x2ad3, - 0x2ad4, - 0x2ad5, - 0x2ad6, - 0x2ad7, - 0x2ad8, - 0x2ad9, - 0x2ada, - 0x2adb, - 0x2adc, - 0x2add, - 0x2ade, - 0x2adf, - 0x2ae0, - 0x2ae1, - 0x2ae2, - 0x2ae3, - 0x2ae4, - 0x2ae5, - 0x2ae6, - 0x2ae7, - 0x2ae8, - 0x2ae9, - 0x2aea, - 0x2aeb, - 0x2aec, - 0x2aed, - 0x2aee, - 0x2aef, - 0x2af0, - 0x2af1, - 0x2af2, - 0x2af3, - 0x2af4, - 0x2af5, - 0x2af6, - 0x2af7, - 0x2af8, - 0x2af9, - 0x2afa, - 0x2afb, - 0x2afc, - 0x2afd, - 0x2afe, - 0x2aff, - 0x2b00, - 0x2b01, - 0x2b02, - 0x2b03, - 0x2b04, - 0x2b05, - 0x2b06, - 0x2b07, - 0x2b08, - 0x2b09, - 0x2b0a, - 0x2b0b, - 0x2b0c, - 0x2b0d, - 0x2b0e, - 0x2b0f, - 0x2b10, - 0x2b11, - 0x2b12, - 0x2b13, - 0x2b14, - 0x2b15, - 0x2b16, - 0x2b17, - 0x2b18, - 0x2b19, - 0x2b1a, - 0x2b1b, - 0x2b1c, - 0x2b1d, - 0x2b1e, - 0x2b1f, - 0x2b20, - 0x2b21, - 0x2b22, - 0x2b23, - 0x2b24, - 0x2b25, - 0x2b26, - 0x2b27, - 0x2b28, - 0x2b29, - 0x2b2a, - 0x2b2b, - 0x2b2c, - 0x2b2d, - 0x2b2e, - 0x2b2f, - 0x2b30, - 0x2b31, - 0x2b32, - 0x2b33, - 0x2b34, - 0x2b35, - 0x2b36, - 0x2b37, - 0x2b38, - 0x2b39, - 0x2b3a, - 0x2b3b, - 0x2b3c, - 0x2b3d, - 0x2b3e, - 0x2b3f, - 0x2b40, - 0x2b41, - 0x2b42, - 0x2b43, - 0x2b44, - 0x2b45, - 0x2b46, - 0x2b47, - 0x2b48, - 0x2b49, - 0x2b4a, - 0x2b4b, - 0x2b4c, - 0x2b4d, - 0x2b4e, - 0x2b4f, - 0x2b50, - 0x2b51, - 0x2b52, - 0x2b53, - 0x2b54, - 0x2b55, - 0x2b56, - 0x2b57, - 0x2b58, - 0x2b59, - 0x2b5a, - 0x2b5b, - 0x2b5c, - 0x2b5d, - 0x2b5e, - 0x2b5f, - 0x2b60, - 0x2b61, - 0x2b62, - 0x2b63, - 0x2b64, - 0x2b65, - 0x2b66, - 0x2b67, - 0x2b68, - 0x2b69, - 0x2b6a, - 0x2b6b, - 0x2b6c, - 0x2b6d, - 0x2b6e, - 0x2b6f, - 0x2b70, - 0x2b71, - 0x2b72, - 0x2b73, - 0x2b74, - 0x2b75, - 0x2b76, - 0x2b77, - 0x2b78, - 0x2b79, - 0x2b7a, - 0x2b7b, - 0x2b7c, - 0x2b7d, - 0x2b7e, - 0x2b7f, - 0x2b80, - 0x2b81, - 0x2b82, - 0x2b83, - 0x2b84, - 0x2b85, - 0x2b86, - 0x2b87, - 0x2b88, - 0x2b89, - 0x2b8a, - 0x2b8b, - 0x2b8c, - 0x2b8d, - 0x2b8e, - 0x2b8f, - 0x2b90, - 0x2b91, - 0x2b92, - 0x2b93, - 0x2b94, - 0x2b95, - 0x2b96, - 0x2b97, - 0x2b98, - 0x2b99, - 0x2b9a, - 0x2b9b, - 0x2b9c, - 0x2b9d, - 0x2b9e, - 0x2b9f, - 0x2ba0, - 0x2ba1, - 0x2ba2, - 0x2ba3, - 0x2ba4, - 0x2ba5, - 0x2ba6, - 0x2ba7, - 0x2ba8, - 0x2ba9, - 0x2baa, - 0x2bab, - 0x2bac, - 0x2bad, - 0x2bae, - 0x2baf, - 0x2bb0, - 0x2bb1, - 0x2bb2, - 0x2bb3, - 0x2bb4, - 0x2bb5, - 0x2bb6, - 0x2bb7, - 0x2bb8, - 0x2bb9, - 0x2bba, - 0x2bbb, - 0x2bbc, - 0x2bbd, - 0x2bbe, - 0x2bbf, - 0x2bc0, - 0x2bc1, - 0x2bc2, - 0x2bc3, - 0x2bc4, - 0x2bc5, - 0x2bc6, - 0x2bc7, - 0x2bc8, - 0x2bc9, - 0x2bca, - 0x2bcb, - 0x2bcc, - 0x2bcd, - 0x2bce, - 0x2bcf, - 0x2bd0, - 0x2bd1, - 0x2bd2, - 0x2bd3, - 0x2bd4, - 0x2bd5, - 0x2bd6, - 0x2bd7, - 0x2bd8, - 0x2bd9, - 0x2bda, - 0x2bdb, - 0x2bdc, - 0x2bdd, - 0x2bde, - 0x2bdf, - 0x2be0, - 0x2be1, - 0x2be2, - 0x2be3, - 0x2be4, - 0x2be5, - 0x2be6, - 0x2be7, - 0x2be8, - 0x2be9, - 0x2bea, - 0x2beb, - 0x2bec, - 0x2bed, - 0x2bee, - 0x2bef, - 0x2bf0, - 0x2bf1, - 0x2bf2, - 0x2bf3, - 0x2bf4, - 0x2bf5, - 0x2bf6, - 0x2bf7, - 0x2bf8, - 0x2bf9, - 0x2bfa, - 0x2bfb, - 0x2bfc, - 0x2bfd, - 0x2bfe, - 0x2bff, - 0x2e00, - 0x2e01, - 0x2e02, - 0x2e03, - 0x2e04, - 0x2e05, - 0x2e06, - 0x2e07, - 0x2e08, - 0x2e09, - 0x2e0a, - 0x2e0b, - 0x2e0c, - 0x2e0d, - 0x2e0e, - 0x2e0f, - 0x2e10, - 0x2e11, - 0x2e12, - 0x2e13, - 0x2e14, - 0x2e15, - 0x2e16, - 0x2e17, - 0x2e18, - 0x2e19, - 0x2e1a, - 0x2e1b, - 0x2e1c, - 0x2e1d, - 0x2e1e, - 0x2e1f, - 0x2e20, - 0x2e21, - 0x2e22, - 0x2e23, - 0x2e24, - 0x2e25, - 0x2e26, - 0x2e27, - 0x2e28, - 0x2e29, - 0x2e2a, - 0x2e2b, - 0x2e2c, - 0x2e2d, - 0x2e2e, - 0x2e2f, - 0x2e30, - 0x2e31, - 0x2e32, - 0x2e33, - 0x2e34, - 0x2e35, - 0x2e36, - 0x2e37, - 0x2e38, - 0x2e39, - 0x2e3a, - 0x2e3b, - 0x2e3c, - 0x2e3d, - 0x2e3e, - 0x2e3f, - 0x2e40, - 0x2e41, - 0x2e42, - 0x2e43, - 0x2e44, - 0x2e45, - 0x2e46, - 0x2e47, - 0x2e48, - 0x2e49, - 0x2e4a, - 0x2e4b, - 0x2e4c, - 0x2e4d, - 0x2e4e, - 0x2e4f, - 0x2e50, - 0x2e51, - 0x2e52, - 0x2e53, - 0x2e54, - 0x2e55, - 0x2e56, - 0x2e57, - 0x2e58, - 0x2e59, - 0x2e5a, - 0x2e5b, - 0x2e5c, - 0x2e5d, - 0x2e5e, - 0x2e5f, - 0x2e60, - 0x2e61, - 0x2e62, - 0x2e63, - 0x2e64, - 0x2e65, - 0x2e66, - 0x2e67, - 0x2e68, - 0x2e69, - 0x2e6a, - 0x2e6b, - 0x2e6c, - 0x2e6d, - 0x2e6e, - 0x2e6f, - 0x2e70, - 0x2e71, - 0x2e72, - 0x2e73, - 0x2e74, - 0x2e75, - 0x2e76, - 0x2e77, - 0x2e78, - 0x2e79, - 0x2e7a, - 0x2e7b, - 0x2e7c, - 0x2e7d, - 0x2e7e, - 0x2e7f, - 0x3001, - 0x3002, - 0x3003, - 0x3008, - 0x3009, - 0x300a, - 0x300b, - 0x300c, - 0x300d, - 0x300e, - 0x300f, - 0x3010, - 0x3011, - 0x3012, - 0x3013, - 0x3014, - 0x3015, - 0x3016, - 0x3017, - 0x3018, - 0x3019, - 0x301a, - 0x301b, - 0x301c, - 0x301d, - 0x301e, - 0x301f, - 0x3020, - 0x3030, - 0xfd3e, - 0xfd3f, - 0xfe45, - 0xfe46, - ]; - - /** - * @inheritdoc + * @link https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt UCD PropList, Latest + * @link https://www.unicode.org/Public/14.0.0/ucd/PropList.txt UCD PropList, 14.0.0 */ - protected function getCodePoints(): array + public function matches(int $codepoint): bool { - return self::CODE_POINTS; + return ($codepoint >= 0x0021 && $codepoint <= 0x002f) + || ($codepoint >= 0x003a && $codepoint <= 0x0040) + || ($codepoint >= 0x005b && $codepoint <= 0x005e) + || $codepoint === 0x0060 + || ($codepoint >= 0x007b && $codepoint <= 0x007e) + || ($codepoint >= 0x00a1 && $codepoint <= 0x00a7) + || $codepoint === 0x00a9 + || ($codepoint >= 0x00ab && $codepoint <= 0x00ac) + || $codepoint === 0x00ae + || ($codepoint >= 0x00b0 && $codepoint <= 0x00b1) + || $codepoint === 0x00b6 + || $codepoint === 0x00bb + || $codepoint === 0x00bf + || $codepoint === 0x00d7 + || $codepoint === 0x00f7 + || ($codepoint >= 0x2010 && $codepoint <= 0x2027) + || ($codepoint >= 0x2030 && $codepoint <= 0x203e) + || ($codepoint >= 0x2041 && $codepoint <= 0x2053) + || ($codepoint >= 0x2055 && $codepoint <= 0x205e) + || ($codepoint >= 0x2190 && $codepoint <= 0x245f) + || ($codepoint >= 0x2500 && $codepoint <= 0x2775) + || ($codepoint >= 0x2794 && $codepoint <= 0x2bff) + || ($codepoint >= 0x2e00 && $codepoint <= 0x2e7f) + || ($codepoint >= 0x3001 && $codepoint <= 0x3003) + || ($codepoint >= 0x3008 && $codepoint <= 0x3020) + || $codepoint === 0x3030 + || ($codepoint >= 0xfd3e && $codepoint <= 0xfd3f) + || ($codepoint >= 0xfe45 && $codepoint <= 0xfe46); } } diff --git a/src/Icu/MessageFormat/Parser/Util/IsPotentialElementNameChar.php b/src/Icu/MessageFormat/Parser/Util/IsPotentialElementNameChar.php index 17f91e1..a7e7960 100644 --- a/src/Icu/MessageFormat/Parser/Util/IsPotentialElementNameChar.php +++ b/src/Icu/MessageFormat/Parser/Util/IsPotentialElementNameChar.php @@ -23,30 +23,47 @@ namespace FormatPHP\Icu\MessageFormat\Parser\Util; /** + * Checks whether the code point could be used in an HTML tag element name + * * @internal */ class IsPotentialElementNameChar implements CodePointMatcherInterface { /** - * Checks whether the code point could be used in an element name + * Returns true if the code point could be used in an HTML tag element name + * + * This function matches exactly 971,632 code points. + * + * A tag name must start with an ASCII lower/upper case letter. The grammar + * is based on the HTML custom element name except that a dash is NOT always + * mandatory and uppercase letters are accepted: + * + * ``` + * tag ::= "<" tagName (whitespace)* "/>" | + * "<" tagName (whitespace)* ">" message "" + * tagName ::= [a-z] (PENChar)* + * PENChar ::= + * "-" | "." | [0-9] | "_" | [a-z] | [A-Z] | #xB7 | [#xC0-#xD6] | + * [#xD8-#xF6] | [#xF8-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | + * [#x203F-#x2040] | [#x2070-#x218F] | [#x2C00-#x2FEF] | + * [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | + * [#x10000-#xEFFFF] + * ``` * - * We could convert this to an array, like we did for the other code point - * matchers, but this list has 54,128 code points in it, so we have opted - * to use a logical statement instead. + * @link https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name HTML custom element name */ public function matches(int $codepoint): bool { - return $codepoint === 45 /* '-' */ - || $codepoint === 46 /* '.' */ - || ($codepoint >= 48 && $codepoint <= 57) /* 0..9 */ - || $codepoint === 95 /* '_' */ - || ($codepoint >= 97 && $codepoint <= 122) /* a..z */ - || ($codepoint >= 65 && $codepoint <= 90) /* A..Z */ - || $codepoint === 0xb7 - || ($codepoint >= 0xc0 && $codepoint <= 0xd6) - || ($codepoint >= 0xd8 && $codepoint <= 0xf6) - || ($codepoint >= 0xf8 && $codepoint <= 0x37d) - || ($codepoint >= 0x37f && $codepoint <= 0x1fff) + return ($codepoint >= 0x002d && $codepoint <= 0x002e) + || ($codepoint >= 0x0030 && $codepoint <= 0x0039) + || ($codepoint >= 0x0041 && $codepoint <= 0x005a) + || $codepoint === 0x005f + || ($codepoint >= 0x0061 && $codepoint <= 0x007a) + || $codepoint === 0x00b7 + || ($codepoint >= 0x00c0 && $codepoint <= 0x00d6) + || ($codepoint >= 0x00d8 && $codepoint <= 0x00f6) + || ($codepoint >= 0x00f8 && $codepoint <= 0x037d) + || ($codepoint >= 0x037f && $codepoint <= 0x1fff) || ($codepoint >= 0x200c && $codepoint <= 0x200d) || ($codepoint >= 0x203f && $codepoint <= 0x2040) || ($codepoint >= 0x2070 && $codepoint <= 0x218f) diff --git a/src/Icu/MessageFormat/Parser/Util/IsWhiteSpace.php b/src/Icu/MessageFormat/Parser/Util/IsWhiteSpace.php index d30a047..69a5238 100644 --- a/src/Icu/MessageFormat/Parser/Util/IsWhiteSpace.php +++ b/src/Icu/MessageFormat/Parser/Util/IsWhiteSpace.php @@ -23,38 +23,52 @@ namespace FormatPHP\Icu\MessageFormat\Parser\Util; /** + * Checks whether a code point is in the Unicode Character Database + * White_Space and Pattern_White_Space groups + * * @internal */ -class IsWhiteSpace extends AbstractCodePointMatcher +class IsWhiteSpace implements CodePointMatcherInterface { /** - * White space code points + * Returns true if the code point is in either the White_Space or + * Pattern_White_Space group in the Unicode Character Database (UCD) * - * This list is derived from the White_Space code points listed in the - * Unicode Character Database PropList. We converted it to an array for - * better performance. + * FormatJS claims its `_isWhiteSpace()` function is the code point + * equivalent of `\p{White_Space}`, but this is incorrect. In reality, + * their function, as implemented in JavaScript, is the code point + * equivalent of `\p{Pattern_White_Space}`, which is a subset of + * `\p{White_Space}`, along with `0x200e` (left-to-right bidi mark) and + * `0x200f` (right-to-left bidi mark) added. * - * @link https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt UCD PropList - */ - protected const CODE_POINTS = [ - 0x0009, - 0x000a, - 0x000b, - 0x000c, - 0x000d, - 0x0020, - 0x0085, - 0x200e, - 0x200f, - 0x2028, - 0x2029, - ]; - - /** - * @inheritdoc + * FormatJS uses this function in the case the user's browser doesn't + * support the native `\p{White_Space}` regular expression pattern. If the + * browser does support it, then it uses `\p{White_Space}` instead. + * + * In our implementation, we've combined both `White_Space` and + * `Pattern_White_Space` code points to accommodate both sets, for maximum + * interoperability. + * + * This function matches exactly 27 code points. The values are derived + * from PropList-14.0.0.txt, dated 2021-08-12, 23:13:05 GMT. Ranges have + * been collapsed for optimal performance. + * + * @link https://github.com/formatjs/formatjs/blob/9c50fd13c5a4966adc7e9c22ed21553b7fef5337/packages/icu-messageformat-parser/parser.ts#L1334-L1347 FormatJS _isWhiteSpace() + * @link https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt UCD PropList, Latest + * @link https://www.unicode.org/Public/14.0.0/ucd/PropList.txt UCD PropList, 14.0.0 */ - protected function getCodePoints(): array + public function matches(int $codepoint): bool { - return self::CODE_POINTS; + return ($codepoint >= 0x0009 && $codepoint <= 0x000d) + || $codepoint === 0x0020 + || $codepoint === 0x0085 + || $codepoint === 0x00a0 + || $codepoint === 0x1680 + || ($codepoint >= 0x2000 && $codepoint <= 0x200a) + || ($codepoint >= 0x200e && $codepoint <= 0x200f) + || ($codepoint >= 0x2028 && $codepoint <= 0x2029) + || $codepoint === 0x202f + || $codepoint === 0x205f + || $codepoint === 0x3000; } } diff --git a/tests/Icu/MessageFormat/Parser/Util/CodePointHelperTest.php b/tests/Icu/MessageFormat/Parser/Util/CodePointHelperTest.php new file mode 100644 index 0000000..15fc9f5 --- /dev/null +++ b/tests/Icu/MessageFormat/Parser/Util/CodePointHelperTest.php @@ -0,0 +1,227 @@ +codePointHelper = new CodePointHelper(); + } + + /** + * @dataProvider isAlphaProvider + */ + public function testIsAlpha(int $value, bool $expected): void + { + $this->assertSame($expected, $this->codePointHelper->isAlpha($value)); + } + + /** + * @return non-empty-list + */ + public function isAlphaProvider(): array + { + $values = []; + + foreach (range('a', 'z') as $char) { + $values[] = [mb_ord($char, 'UTF-8'), true]; + } + + foreach (range('A', 'Z') as $char) { + $values[] = [mb_ord($char, 'UTF-8'), true]; + } + + foreach (range(0, 9) as $char) { + $values[] = [mb_ord((string) $char, 'UTF-8'), false]; + } + + foreach (range('-', '!') as $char) { + $values[] = [mb_ord($char, 'UTF-8'), false]; + } + + return $values; + } + + /** + * @dataProvider isAlphaOrSlashProvider + */ + public function testIsAlphaOrSlash(int $value, bool $expected): void + { + $this->assertSame($expected, $this->codePointHelper->isAlphaOrSlash($value)); + } + + /** + * @return non-empty-list + */ + public function isAlphaOrSlashProvider(): array + { + return [ + [mb_ord('a', 'UTF-8'), true], + [mb_ord('z', 'UTF-8'), true], + [mb_ord('A', 'UTF-8'), true], + [mb_ord('Z', 'UTF-8'), true], + [mb_ord('/', 'UTF-8'), true], + [mb_ord('\\', 'UTF-8'), false], + [mb_ord('0', 'UTF-8'), false], + [mb_ord('!', 'UTF-8'), false], + [mb_ord('-', 'UTF-8'), false], + ]; + } + + /** + * @dataProvider isWhiteSpaceProvider + */ + public function testIsWhiteSpace(int $value, bool $expected): void + { + $this->assertSame($expected, $this->codePointHelper->isWhiteSpace($value)); + } + + /** + * @return non-empty-list + */ + public function isWhiteSpaceProvider(): array + { + return [ + [0x0009, true], + [0x000a, true], + [0x000b, true], + [0x000c, true], + [0x000d, true], + [0x0020, true], + [0x0085, true], + [0x00A0, true], + [0x1680, true], + [0x2000, true], + [0x2001, true], + [0x2002, true], + [0x2003, true], + [0x2004, true], + [0x2005, true], + [0x2006, true], + [0x2007, true], + [0x2008, true], + [0x2009, true], + [0x200a, true], + [0x200e, true], + [0x200f, true], + [0x2028, true], + [0x2029, true], + [0x202F, true], + [0x205F, true], + [0x3000, true], + [mb_ord('A', 'UTF-8'), false], + [mb_ord('Z', 'UTF-8'), false], + [mb_ord('!', 'UTF-8'), false], + [mb_ord('.', 'UTF-8'), false], + [mb_ord('_', 'UTF-8'), false], + ]; + } + + /** + * @dataProvider isPatternSyntaxProvider + */ + public function testIsPatternSyntax(int $value, bool $expected): void + { + $this->assertSame($expected, $this->codePointHelper->isPatternSyntax($value)); + } + + /** + * @return non-empty-list + */ + public function isPatternSyntaxProvider(): array + { + $values = []; + + foreach (range(0x2190, 0x245f) as $char) { + $values[] = [$char, true]; + } + + foreach (range(0x1000, 0x107f) as $char) { + $values[] = [$char, false]; + } + + return $values; + } + + /** + * @dataProvider isPotentialElementNameCharProvider + */ + public function testIsPotentialElementNameChar(int $value, bool $expected): void + { + $this->assertSame($expected, $this->codePointHelper->isPotentialElementNameChar($value)); + } + + /** + * @return non-empty-list + */ + public function isPotentialElementNameCharProvider(): array + { + $values = []; + + foreach (range(0x00f8, 0x037d) as $char) { + $values[] = [$char, true]; + } + + foreach (range(0xe000, 0xe07f) as $char) { + $values[] = [$char, false]; + } + + return $values; + } + + public function testCharCodeAt(): void + { + $this->assertSame(0x0062, $this->codePointHelper->charCodeAt(['a', 'b', 'c'], 1)); + } + + public function testCharCodeAtReturnsNull(): void + { + $this->assertNull($this->codePointHelper->charCodeAt(['a', 'b', 'c'], 3)); + } + + public function testFromCharCode(): void + { + $this->assertSame('ã', $this->codePointHelper->fromCharCode(0x00e3)); + } + + public function testFromCharCodeReturnsNull(): void + { + $this->assertNull($this->codePointHelper->fromCharCode(PHP_INT_MAX)); + } + + public function testFromCodePointThrowsException(): void + { + $this->expectException(InvalidUtf8CodePointException::class); + $this->expectExceptionMessage('Code ' . PHP_INT_MAX . ' is an invalid UTF-8 code point'); + + $this->codePointHelper->fromCodePoint(PHP_INT_MAX, PHP_INT_MIN); + } + + public function testFromCodePointReturnsEmptyString(): void + { + $this->assertSame('', $this->codePointHelper->fromCodePoint()); + } + + public function testFromCodePoint(): void + { + $this->assertSame( + 'foobar', + $this->codePointHelper->fromCodePoint(0x0066, 0x006f, 0x006f, 0x0062, 0x0061, 0x0072), + ); + } +} diff --git a/tests/Icu/MessageFormat/Parser/Util/IsAlphaTest.php b/tests/Icu/MessageFormat/Parser/Util/IsAlphaTest.php new file mode 100644 index 0000000..d7c2f51 --- /dev/null +++ b/tests/Icu/MessageFormat/Parser/Util/IsAlphaTest.php @@ -0,0 +1,31 @@ +matches($codepoint)) { + $matches++; + } + } + + $this->assertSame(52, $matches); + } +} diff --git a/tests/Icu/MessageFormat/Parser/Util/IsPatternSyntaxTest.php b/tests/Icu/MessageFormat/Parser/Util/IsPatternSyntaxTest.php new file mode 100644 index 0000000..c8f31d1 --- /dev/null +++ b/tests/Icu/MessageFormat/Parser/Util/IsPatternSyntaxTest.php @@ -0,0 +1,31 @@ +matches($codepoint)) { + $matches++; + } + } + + $this->assertSame(2760, $matches); + } +} diff --git a/tests/Icu/MessageFormat/Parser/Util/IsPotentialElementNameCharTest.php b/tests/Icu/MessageFormat/Parser/Util/IsPotentialElementNameCharTest.php new file mode 100644 index 0000000..0722a35 --- /dev/null +++ b/tests/Icu/MessageFormat/Parser/Util/IsPotentialElementNameCharTest.php @@ -0,0 +1,31 @@ +matches($codepoint)) { + $matches++; + } + } + + $this->assertSame(971_632, $matches); + } +} diff --git a/tests/Icu/MessageFormat/Parser/Util/IsWhiteSpaceTest.php b/tests/Icu/MessageFormat/Parser/Util/IsWhiteSpaceTest.php new file mode 100644 index 0000000..8b5e890 --- /dev/null +++ b/tests/Icu/MessageFormat/Parser/Util/IsWhiteSpaceTest.php @@ -0,0 +1,31 @@ +matches($codepoint)) { + $matches++; + } + } + + $this->assertSame(27, $matches); + } +}