New Upstream Snapshot - node-regexpu-core
Ready changes
Summary
Merged new upstream version: 5.3.1 (was: 5.2.2).
Diff
diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index 6f9f40f..0000000
--- a/.editorconfig
+++ /dev/null
@@ -1,12 +0,0 @@
-root = true
-
-[*]
-charset = utf-8
-indent_style = tab
-end_of_line = lf
-insert_final_newline = true
-trim_trailing_whitespace = true
-
-[{README.md,.travis.yml}]
-indent_style = space
-indent_size = 2
diff --git a/.npmrc b/.npmrc
deleted file mode 100644
index 43c97e7..0000000
--- a/.npmrc
+++ /dev/null
@@ -1 +0,0 @@
-package-lock=false
diff --git a/README.md b/README.md
index fe5419f..bab65e2 100644
--- a/README.md
+++ b/README.md
@@ -146,6 +146,14 @@ Once these features become stable (when the proposals are accepted as part of EC
// → '(?:(?![f-h])[\s\S])' (to be used without /u)
```
+- `modifiers` - [Inline `m`/`s`/`i` modifiers](https://github.com/tc39/proposal-regexp-modifiers)
+
+ ```js
+ rewritePattern('(?i:[a-z])[a-z]', '', {
+ modifiers: 'transform'
+ });
+ // → '(?:[a-zA-Z])([a-z])'
+ ```
#### Miscellaneous options
@@ -163,6 +171,20 @@ Once these features become stable (when the proposals are accepted as part of EC
});
```
+- `onNewFlags`
+
+ This option is a function that gets called to pass the flags that the resulting pattern must be interpreted with.
+
+ ```js
+ rewritePattern('abc', 'um', '', {
+ unicodeFlag: 'transform',
+ onNewFlags(flags) {
+ console.log(flags);
+ // → 'm'
+ }
+ })
+ ```
+
### Caveats
- [Lookbehind assertions](https://github.com/tc39/proposal-regexp-lookbehind) cannot be transformed to older syntax.
diff --git a/debian/changelog b/debian/changelog
index 69b5a78..c057352 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+node-regexpu-core (5.3.1-1) UNRELEASED; urgency=low
+
+ * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk> Sun, 26 Feb 2023 23:00:55 -0000
+
node-regexpu-core (5.2.2-2) unstable; urgency=medium
[ Debian Janitor ]
diff --git a/demo.js b/demo.js
deleted file mode 100644
index 1fb999f..0000000
--- a/demo.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict';
-
-const rewritePattern = require('./rewrite-pattern.js');
-const parse = require('regjsparser').parse;
-const generate = require('regjsgen').generate;
-const regenerate = require('regenerate');
-
-const pattern = String.raw`\p{RGI_Emoji}`;
-
-const processedPattern = rewritePattern(pattern, 'v', {
- 'unicodeSetsFlag': 'transform'
-});
-
-console.log(JSON.stringify(processedPattern));
-
diff --git a/package.json b/package.json
index 2791e50..b483113 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "regexpu-core",
- "version": "5.2.2",
+ "version": "5.3.1",
"description": "regexpu’s core functionality (i.e. `rewritePattern(pattern, flag)`), capable of translating ES6 Unicode regular expressions to ES5.",
"homepage": "https://mths.be/regexpu",
"main": "rewrite-pattern.js",
@@ -50,7 +50,7 @@
"dependencies": {
"regenerate": "^1.4.2",
"regenerate-unicode-properties": "^10.1.0",
- "regjsgen": "^0.7.1",
+ "@babel/regjsgen": "^0.8.0",
"regjsparser": "^0.9.1",
"unicode-match-property-ecmascript": "^2.0.0",
"unicode-match-property-value-ecmascript": "^2.1.0"
@@ -61,7 +61,7 @@
"jsesc": "^3.0.2",
"lodash": "^4.17.21",
"mocha": "^10.1.0",
- "regexpu-fixtures": "2.1.4",
+ "regexpu-fixtures": "^2.1.6",
"@unicode/unicode-15.0.0": "^1.3.1"
}
}
diff --git a/property-escapes.md b/property-escapes.md
deleted file mode 100644
index 88b8163..0000000
--- a/property-escapes.md
+++ /dev/null
@@ -1,312 +0,0 @@
-# Unicode property escapes in _regexpu_
-
-To opt-in to experimental support for [Unicode property escapes](https://github.com/mathiasbynens/es-regexp-unicode-property-escapes), enable [the `unicodePropertyEscape` option](README.md#unicodepropertyescape-default-false).
-
-```js
-rewritePattern('\\p{Script_Extensions=Anatolian_Hieroglyphs}', 'u', {
- 'unicodePropertyEscape': true
-});
-// → '(?:\\uD811[\\uDC00-\\uDE46])'
-```
-
-If you’re targeting ES2015 environments exclusively, consider enabling [the `useUnicodeFlag` option](README.md#useunicodeflag-default-false) for simpler (but not necessarily more compact) output.
-
-```js
-rewritePattern('\\p{Script_Extensions=Anatolian_Hieroglyphs}', 'u', {
- 'unicodePropertyEscape': true,
- 'useUnicodeFlag': true
-});
-// → '[\\u{14400}-\\u{14646}]'
-```
-
-[An online demo is available.](https://mothereff.in/regexpu#input=var+regex+%3D+/%5Cp%7BScript_Extensions%3DGreek%7D/u%3B&unicodePropertyEscape=1)
-
-Note that this feature is non-standard. This implementation may or may not reflect what eventually gets specified.
-
-What follows is an exhaustive overview of the Unicode properties and values that _regexpu_ supports in `\p{…}` and `\P{…}` expressions in regular expressions with the `u` flag.
-
-## Non-binary properties
-
-### `General_Category`
-
-Possible values:
-
-```sh
-$ node -e 'require("regenerate-unicode-properties").get("General_Category").forEach(c => { console.log(`\\p{${c}}`); })'
-\p{Cased_Letter}
-\p{Close_Punctuation}
-\p{Connector_Punctuation}
-\p{Control}
-\p{Currency_Symbol}
-\p{Dash_Punctuation}
-\p{Decimal_Number}
-\p{Enclosing_Mark}
-\p{Final_Punctuation}
-\p{Format}
-\p{Initial_Punctuation}
-\p{Letter}
-\p{Letter_Number}
-\p{Line_Separator}
-\p{Lowercase_Letter}
-\p{Mark}
-\p{Math_Symbol}
-\p{Modifier_Letter}
-\p{Modifier_Symbol}
-\p{Nonspacing_Mark}
-\p{Number}
-\p{Open_Punctuation}
-\p{Other}
-\p{Other_Letter}
-\p{Other_Number}
-\p{Other_Punctuation}
-\p{Other_Symbol}
-\p{Paragraph_Separator}
-\p{Private_Use}
-\p{Punctuation}
-\p{Separator}
-\p{Space_Separator}
-\p{Spacing_Mark}
-\p{Surrogate}
-\p{Symbol}
-\p{Titlecase_Letter}
-\p{Unassigned}
-\p{Uppercase_Letter}
-```
-
-Note that the `General_Category=` prefix may be used, e.g. `\p{General_Category=Cased_Letter}`.
-
-Category aliases may be used, e.g. `\p{Lc}` or `\p{General_Category=Lc}`, although IMHO it’s more readable to stick to the canonical category names listed above.
-
-### `Script` & `Script_Extensions`
-
-The sets of possible values for `Script` and `Script_Extensions` are identical:
-
-```sh
-$ node -e 'require("regenerate-unicode-properties").get("Script_Extensions").forEach(s => { console.log(`\\p{Script_Extensions=${s}}`); })'
-\p{Script_Extensions=Adlam}
-\p{Script_Extensions=Ahom}
-\p{Script_Extensions=Anatolian_Hieroglyphs}
-\p{Script_Extensions=Arabic}
-\p{Script_Extensions=Armenian}
-\p{Script_Extensions=Avestan}
-\p{Script_Extensions=Balinese}
-\p{Script_Extensions=Bamum}
-\p{Script_Extensions=Bassa_Vah}
-\p{Script_Extensions=Batak}
-\p{Script_Extensions=Bengali}
-\p{Script_Extensions=Bhaiksuki}
-\p{Script_Extensions=Bopomofo}
-\p{Script_Extensions=Brahmi}
-\p{Script_Extensions=Braille}
-\p{Script_Extensions=Buginese}
-\p{Script_Extensions=Buhid}
-\p{Script_Extensions=Canadian_Aboriginal}
-\p{Script_Extensions=Carian}
-\p{Script_Extensions=Caucasian_Albanian}
-\p{Script_Extensions=Chakma}
-\p{Script_Extensions=Cham}
-\p{Script_Extensions=Cherokee}
-\p{Script_Extensions=Chorasmian}
-\p{Script_Extensions=Common}
-\p{Script_Extensions=Coptic}
-\p{Script_Extensions=Cuneiform}
-\p{Script_Extensions=Cypriot}
-\p{Script_Extensions=Cypro_Minoan}
-\p{Script_Extensions=Cyrillic}
-\p{Script_Extensions=Deseret}
-\p{Script_Extensions=Devanagari}
-\p{Script_Extensions=Dives_Akuru}
-\p{Script_Extensions=Dogra}
-\p{Script_Extensions=Duployan}
-\p{Script_Extensions=Egyptian_Hieroglyphs}
-\p{Script_Extensions=Elbasan}
-\p{Script_Extensions=Elymaic}
-\p{Script_Extensions=Ethiopic}
-\p{Script_Extensions=Georgian}
-\p{Script_Extensions=Glagolitic}
-\p{Script_Extensions=Gothic}
-\p{Script_Extensions=Grantha}
-\p{Script_Extensions=Greek}
-\p{Script_Extensions=Gujarati}
-\p{Script_Extensions=Gunjala_Gondi}
-\p{Script_Extensions=Gurmukhi}
-\p{Script_Extensions=Han}
-\p{Script_Extensions=Hangul}
-\p{Script_Extensions=Hanifi_Rohingya}
-\p{Script_Extensions=Hanunoo}
-\p{Script_Extensions=Hatran}
-\p{Script_Extensions=Hebrew}
-\p{Script_Extensions=Hiragana}
-\p{Script_Extensions=Imperial_Aramaic}
-\p{Script_Extensions=Inherited}
-\p{Script_Extensions=Inscriptional_Pahlavi}
-\p{Script_Extensions=Inscriptional_Parthian}
-\p{Script_Extensions=Javanese}
-\p{Script_Extensions=Kaithi}
-\p{Script_Extensions=Kannada}
-\p{Script_Extensions=Katakana}
-\p{Script_Extensions=Kayah_Li}
-\p{Script_Extensions=Kharoshthi}
-\p{Script_Extensions=Khitan_Small_Script}
-\p{Script_Extensions=Khmer}
-\p{Script_Extensions=Khojki}
-\p{Script_Extensions=Khudawadi}
-\p{Script_Extensions=Lao}
-\p{Script_Extensions=Latin}
-\p{Script_Extensions=Lepcha}
-\p{Script_Extensions=Limbu}
-\p{Script_Extensions=Linear_A}
-\p{Script_Extensions=Linear_B}
-\p{Script_Extensions=Lisu}
-\p{Script_Extensions=Lycian}
-\p{Script_Extensions=Lydian}
-\p{Script_Extensions=Mahajani}
-\p{Script_Extensions=Makasar}
-\p{Script_Extensions=Malayalam}
-\p{Script_Extensions=Mandaic}
-\p{Script_Extensions=Manichaean}
-\p{Script_Extensions=Marchen}
-\p{Script_Extensions=Masaram_Gondi}
-\p{Script_Extensions=Medefaidrin}
-\p{Script_Extensions=Meetei_Mayek}
-\p{Script_Extensions=Mende_Kikakui}
-\p{Script_Extensions=Meroitic_Cursive}
-\p{Script_Extensions=Meroitic_Hieroglyphs}
-\p{Script_Extensions=Miao}
-\p{Script_Extensions=Modi}
-\p{Script_Extensions=Mongolian}
-\p{Script_Extensions=Mro}
-\p{Script_Extensions=Multani}
-\p{Script_Extensions=Myanmar}
-\p{Script_Extensions=Nabataean}
-\p{Script_Extensions=Nandinagari}
-\p{Script_Extensions=New_Tai_Lue}
-\p{Script_Extensions=Newa}
-\p{Script_Extensions=Nko}
-\p{Script_Extensions=Nushu}
-\p{Script_Extensions=Nyiakeng_Puachue_Hmong}
-\p{Script_Extensions=Ogham}
-\p{Script_Extensions=Ol_Chiki}
-\p{Script_Extensions=Old_Hungarian}
-\p{Script_Extensions=Old_Italic}
-\p{Script_Extensions=Old_North_Arabian}
-\p{Script_Extensions=Old_Permic}
-\p{Script_Extensions=Old_Persian}
-\p{Script_Extensions=Old_Sogdian}
-\p{Script_Extensions=Old_South_Arabian}
-\p{Script_Extensions=Old_Turkic}
-\p{Script_Extensions=Old_Uyghur}
-\p{Script_Extensions=Oriya}
-\p{Script_Extensions=Osage}
-\p{Script_Extensions=Osmanya}
-\p{Script_Extensions=Pahawh_Hmong}
-\p{Script_Extensions=Palmyrene}
-\p{Script_Extensions=Pau_Cin_Hau}
-\p{Script_Extensions=Phags_Pa}
-\p{Script_Extensions=Phoenician}
-\p{Script_Extensions=Psalter_Pahlavi}
-\p{Script_Extensions=Rejang}
-\p{Script_Extensions=Runic}
-\p{Script_Extensions=Samaritan}
-\p{Script_Extensions=Saurashtra}
-\p{Script_Extensions=Sharada}
-\p{Script_Extensions=Shavian}
-\p{Script_Extensions=Siddham}
-\p{Script_Extensions=SignWriting}
-\p{Script_Extensions=Sinhala}
-\p{Script_Extensions=Sogdian}
-\p{Script_Extensions=Sora_Sompeng}
-\p{Script_Extensions=Soyombo}
-\p{Script_Extensions=Sundanese}
-\p{Script_Extensions=Syloti_Nagri}
-\p{Script_Extensions=Syriac}
-\p{Script_Extensions=Tagalog}
-\p{Script_Extensions=Tagbanwa}
-\p{Script_Extensions=Tai_Le}
-\p{Script_Extensions=Tai_Tham}
-\p{Script_Extensions=Tai_Viet}
-\p{Script_Extensions=Takri}
-\p{Script_Extensions=Tamil}
-\p{Script_Extensions=Tangsa}
-\p{Script_Extensions=Tangut}
-\p{Script_Extensions=Telugu}
-\p{Script_Extensions=Thaana}
-\p{Script_Extensions=Thai}
-\p{Script_Extensions=Tibetan}
-\p{Script_Extensions=Tifinagh}
-\p{Script_Extensions=Tirhuta}
-\p{Script_Extensions=Toto}
-\p{Script_Extensions=Ugaritic}
-\p{Script_Extensions=Vai}
-\p{Script_Extensions=Vithkuqi}
-\p{Script_Extensions=Wancho}
-\p{Script_Extensions=Warang_Citi}
-\p{Script_Extensions=Yezidi}
-\p{Script_Extensions=Yi}
-\p{Script_Extensions=Zanabazar_Square}
-```
-
-Note that script name aliases may be used as well, e.g. `\p{Script_Extensions=Aghb}`, although IMHO it’s more readable to stick to the canonical script names listed above.
-
-## Binary properties
-
-The following binary properties are supported:
-
-```sh
-$ node -e 'require("regenerate-unicode-properties").get("Binary_Property").forEach(p => { console.log(`\\p{${p}}`); })'
-\p{ASCII}
-\p{ASCII_Hex_Digit}
-\p{Alphabetic}
-\p{Any}
-\p{Assigned}
-\p{Bidi_Control}
-\p{Bidi_Mirrored}
-\p{Case_Ignorable}
-\p{Cased}
-\p{Changes_When_Casefolded}
-\p{Changes_When_Casemapped}
-\p{Changes_When_Lowercased}
-\p{Changes_When_NFKC_Casefolded}
-\p{Changes_When_Titlecased}
-\p{Changes_When_Uppercased}
-\p{Dash}
-\p{Default_Ignorable_Code_Point}
-\p{Deprecated}
-\p{Diacritic}
-\p{Emoji}
-\p{Emoji_Component}
-\p{Emoji_Modifier}
-\p{Emoji_Modifier_Base}
-\p{Emoji_Presentation}
-\p{Extended_Pictographic}
-\p{Extender}
-\p{Grapheme_Base}
-\p{Grapheme_Extend}
-\p{Hex_Digit}
-\p{IDS_Binary_Operator}
-\p{IDS_Trinary_Operator}
-\p{ID_Continue}
-\p{ID_Start}
-\p{Ideographic}
-\p{Join_Control}
-\p{Logical_Order_Exception}
-\p{Lowercase}
-\p{Math}
-\p{Noncharacter_Code_Point}
-\p{Pattern_Syntax}
-\p{Pattern_White_Space}
-\p{Quotation_Mark}
-\p{Radical}
-\p{Regional_Indicator}
-\p{Sentence_Terminal}
-\p{Soft_Dotted}
-\p{Terminal_Punctuation}
-\p{Unified_Ideograph}
-\p{Uppercase}
-\p{Variation_Selector}
-\p{White_Space}
-\p{XID_Continue}
-\p{XID_Start}
-```
-
-Note that property name aliases may be used as well, e.g. `\p{AHex}`, although IMHO it’s more readable to stick to the canonical property names listed above.
diff --git a/rewrite-pattern.js b/rewrite-pattern.js
index 2b95ea3..c6969ab 100644
--- a/rewrite-pattern.js
+++ b/rewrite-pattern.js
@@ -1,6 +1,6 @@
'use strict';
-const generate = require('regjsgen').generate;
+const generate = require('@babel/regjsgen').generate;
const parse = require('regjsparser').parse;
const regenerate = require('regenerate');
const unicodeMatchProperty = require('unicode-match-property-ecmascript');
@@ -27,16 +27,20 @@ const SPECIAL_CHARS = /([\\^$.*+?()[\]{}|])/g;
// character classes (if any).
const UNICODE_SET = regenerate().addRange(0x0, 0x10FFFF);
+const ASTRAL_SET = regenerate().addRange(0x10000, 0x10FFFF);
+
+const NEWLINE_SET = regenerate().add(
+ // `LineTerminator`s (https://mths.be/es6#sec-line-terminators):
+ 0x000A, // Line Feed <LF>
+ 0x000D, // Carriage Return <CR>
+ 0x2028, // Line Separator <LS>
+ 0x2029 // Paragraph Separator <PS>
+);
+
// Prepare a Regenerate set containing all code points that are supposed to be
// matched by `/./u`. https://mths.be/es6#sec-atom
const DOT_SET_UNICODE = UNICODE_SET.clone() // all Unicode code points
- .remove(
- // minus `LineTerminator`s (https://mths.be/es6#sec-line-terminators):
- 0x000A, // Line Feed <LF>
- 0x000D, // Carriage Return <CR>
- 0x2028, // Line Separator <LS>
- 0x2029 // Paragraph Separator <PS>
- );
+ .remove(NEWLINE_SET);
const getCharacterClassEscapeSet = (character, unicode, ignoreCase) => {
if (unicode) {
@@ -126,12 +130,23 @@ const getUnicodePropertyEscapeCharacterClassData = (property, isNegative) => {
return data;
};
+function configNeedCaseFoldAscii() {
+ return !!config.modifiersData.i;
+}
+
+function configNeedCaseFoldUnicode() {
+ // config.modifiersData.i : undefined | false
+ if (config.modifiersData.i === false) return false;
+ if (!config.transform.unicodeFlag) return false;
+ return Boolean(config.modifiersData.i || config.flags.ignoreCase);
+}
+
// Given a range of code points, add any case-folded code points in that range
// to a set.
regenerate.prototype.iuAddRange = function(min, max) {
const $this = this;
do {
- const folded = caseFold(min);
+ const folded = caseFold(min, configNeedCaseFoldAscii(), configNeedCaseFoldUnicode());
if (folded) {
$this.add(folded);
}
@@ -141,7 +156,7 @@ regenerate.prototype.iuAddRange = function(min, max) {
regenerate.prototype.iuRemoveRange = function(min, max) {
const $this = this;
do {
- const folded = caseFold(min);
+ const folded = caseFold(min, configNeedCaseFoldAscii(), configNeedCaseFoldUnicode());
if (folded) {
$this.remove(folded);
}
@@ -150,7 +165,13 @@ regenerate.prototype.iuRemoveRange = function(min, max) {
};
const update = (item, pattern) => {
- let tree = parse(pattern, config.useUnicodeFlag ? 'u' : '');
+ let tree = parse(pattern, config.useUnicodeFlag ? 'u' : '', {
+ lookbehind: true,
+ namedGroups: true,
+ unicodePropertyEscape: true,
+ unicodeSet: true,
+ modifiers: true,
+ });
switch (tree.type) {
case 'characterClass':
case 'group':
@@ -174,8 +195,17 @@ const wrap = (tree, pattern) => {
};
};
-const caseFold = (codePoint) => {
- return iuMappings.get(codePoint) || false;
+const caseFold = (codePoint, includeAscii, includeUnicode) => {
+ let folded = (includeUnicode ? iuMappings.get(codePoint) : undefined) || [];
+ if (typeof folded === 'number') folded = [folded];
+ if (includeAscii) {
+ if (codePoint >= 0x41 && codePoint <= 0x5A) {
+ folded.push(codePoint + 0x20);
+ } else if (codePoint >= 0x61 && codePoint <= 0x7A) {
+ folded.push(codePoint - 0x20);
+ }
+ }
+ return folded.length == 0 ? false : folded;
};
const buildHandler = (action) => {
@@ -316,8 +346,11 @@ const getCharacterClassEmptyData = () => ({
});
const maybeFold = (codePoint) => {
- if (config.flags.ignoreCase && config.transform.unicodeFlag) {
- const folded = caseFold(codePoint);
+ const caseFoldAscii = configNeedCaseFoldAscii();
+ const caseFoldUnicode = configNeedCaseFoldUnicode();
+
+ if (caseFoldAscii || caseFoldUnicode) {
+ const folded = caseFold(codePoint, caseFoldAscii, caseFoldUnicode);
if (folded) {
return [codePoint, folded];
}
@@ -328,6 +361,9 @@ const maybeFold = (codePoint) => {
const computeClassStrings = (classStrings, regenerateOptions) => {
let data = getCharacterClassEmptyData();
+ const caseFoldAscii = configNeedCaseFoldAscii();
+ const caseFoldUnicode = configNeedCaseFoldUnicode();
+
for (const string of classStrings.strings) {
if (string.characters.length === 1) {
maybeFold(string.characters[0].codePoint).forEach((cp) => {
@@ -335,11 +371,11 @@ const computeClassStrings = (classStrings, regenerateOptions) => {
});
} else {
let stringifiedString;
- if (config.flags.ignoreCase && config.transform.unicodeFlag) {
+ if (caseFoldUnicode || caseFoldAscii) {
stringifiedString = '';
for (const ch of string.characters) {
let set = regenerate(ch.codePoint);
- const folded = caseFold(ch.codePoint);
+ const folded = maybeFold(ch.codePoint);
if (folded) set.add(folded);
stringifiedString += set.toString(regenerateOptions);
}
@@ -381,6 +417,9 @@ const computeCharacterClass = (characterClassItem, regenerateOptions) => {
throw new Error(`Unknown character class kind: ${ characterClassItem.kind }`);
}
+ const caseFoldAscii = configNeedCaseFoldAscii();
+ const caseFoldUnicode = configNeedCaseFoldUnicode();
+
for (const item of characterClassItem.body) {
switch (item.type) {
case 'value':
@@ -392,8 +431,9 @@ const computeCharacterClass = (characterClassItem, regenerateOptions) => {
const min = item.min.codePoint;
const max = item.max.codePoint;
handlePositive.range(data, min, max);
- if (config.flags.ignoreCase && config.transform.unicodeFlag) {
+ if (caseFoldAscii || caseFoldUnicode) {
handlePositive.iuRange(data, min, max);
+ data.transformed = true;
}
break;
case 'characterClassEscape':
@@ -452,7 +492,38 @@ const processCharacterClass = (
if (config.useUnicodeFlag) {
update(characterClassItem, `[^${setStr[0] === '[' ? setStr.slice(1, -1) : setStr}]`)
} else {
- update(characterClassItem, `(?!${setStr})[\\s\\S]`)
+ if (config.flags.unicode) {
+ if (config.flags.ignoreCase) {
+ const astralCharsSet = singleChars.clone().intersection(ASTRAL_SET);
+ // Assumption: singleChars do not contain lone surrogates.
+ // Regex like /[^\ud800]/u is not supported
+ const surrogateOrBMPSetStr = singleChars
+ .clone()
+ .remove(astralCharsSet)
+ .addRange(0xd800, 0xdfff)
+ .toString({ bmpOnly: true });
+ // Don't generate negative lookahead for astral characters
+ // because the case folding is not working anyway as we break
+ // code points into surrogate pairs.
+ const astralNegativeSetStr = ASTRAL_SET
+ .clone()
+ .remove(astralCharsSet)
+ .toString(regenerateOptions);
+ // The transform here does not support lone surrogates.
+ update(
+ characterClassItem,
+ `(?!${surrogateOrBMPSetStr})[\\s\\S]|${astralNegativeSetStr}`
+ );
+ } else {
+ // Generate negative set directly when case folding is not involved.
+ update(
+ characterClassItem,
+ UNICODE_SET.clone().remove(singleChars).toString(regenerateOptions)
+ );
+ }
+ } else {
+ update(characterClassItem, `(?!${setStr})[\\s\\S]`);
+ }
}
} else {
const hasEmptyString = longStrings.has('');
@@ -475,15 +546,40 @@ const assertNoUnmatchedReferences = (groups) => {
}
};
+const processModifiers = (item, regenerateOptions, groups) => {
+ const enabling = item.modifierFlags.enabling;
+ const disabling = item.modifierFlags.disabling;
+
+ delete item.modifierFlags;
+ item.behavior = 'ignore';
+
+ const oldData = Object.assign({}, config.modifiersData);
+
+ enabling.split('').forEach(flag => {
+ config.modifiersData[flag] = true;
+ });
+ disabling.split('').forEach(flag => {
+ config.modifiersData[flag] = false;
+ });
+
+ item.body = item.body.map(term => {
+ return processTerm(term, regenerateOptions, groups);
+ });
+
+ config.modifiersData = oldData;
+
+ return item;
+}
+
const processTerm = (item, regenerateOptions, groups) => {
switch (item.type) {
case 'dot':
if (config.transform.unicodeFlag) {
update(
item,
- getUnicodeDotSet(config.flags.dotAll).toString(regenerateOptions)
+ getUnicodeDotSet(config.flags.dotAll || config.modifiersData.s).toString(regenerateOptions)
);
- } else if (config.transform.dotAllFlag) {
+ } else if (config.transform.dotAllFlag || config.modifiersData.s) {
// TODO: consider changing this at the regenerate level.
update(item, '[\\s\\S]');
}
@@ -554,6 +650,9 @@ const processTerm = (item, regenerateOptions, groups) => {
delete groups.unmatchedReferences[name];
}
}
+ if (item.modifierFlags && config.transform.modifiers) {
+ return processModifiers(item, regenerateOptions, groups);
+ }
/* falls through */
case 'quantifier':
item.body = item.body.map(term => {
@@ -577,12 +676,8 @@ const processTerm = (item, regenerateOptions, groups) => {
case 'value':
const codePoint = item.codePoint;
const set = regenerate(codePoint);
- if (config.flags.ignoreCase && config.transform.unicodeFlag) {
- const folded = caseFold(codePoint);
- if (folded) {
- set.add(folded);
- }
- }
+ const folded = maybeFold(codePoint);
+ set.add(folded);
update(item, set.toString(regenerateOptions));
break;
case 'reference':
@@ -622,8 +717,14 @@ const processTerm = (item, regenerateOptions, groups) => {
}
break;
case 'anchor':
+ if (config.modifiersData.m) {
+ if (item.kind == 'start') {
+ update(item, `(?:^|(?<=${NEWLINE_SET.toString()}))`);
+ } else if (item.kind == 'end') {
+ update(item, `(?:$|(?=${NEWLINE_SET.toString()}))`);
+ }
+ }
case 'empty':
- case 'group':
// Nothing to do here.
break;
// The `default` clause is only here as a safeguard; it should never be
@@ -641,6 +742,7 @@ const config = {
'unicode': false,
'unicodeSets': false,
'dotAll': false,
+ 'multiline': false,
},
'transform': {
'dotAllFlag': false,
@@ -648,6 +750,12 @@ const config = {
'unicodeSetsFlag': false,
'unicodePropertyEscapes': false,
'namedGroups': false,
+ 'modifiers': false,
+ },
+ 'modifiersData': {
+ 'i': undefined,
+ 's': undefined,
+ 'm': undefined,
},
get useUnicodeFlag() {
return (this.flags.unicode || this.flags.unicodeSets) && !this.transform.unicodeFlag;
@@ -668,14 +776,16 @@ const validateOptions = (options) => {
throw new Error(`.${key} must be false (default) or 'transform'.`);
}
break;
+ case 'modifiers':
case 'unicodeSetsFlag':
if (value != null && value !== false && value !== 'parse' && value !== 'transform') {
throw new Error(`.${key} must be false (default), 'parse' or 'transform'.`);
}
break;
case 'onNamedGroup':
+ case 'onNewFlags':
if (value != null && typeof value !== 'function') {
- throw new Error('.onNamedGroup must be a function.');
+ throw new Error(`.${key} must be a function.`);
}
break;
default:
@@ -694,6 +804,7 @@ const rewritePattern = (pattern, flags, options) => {
config.flags.unicodeSets = hasFlag(flags, 'v');
config.flags.ignoreCase = hasFlag(flags, 'i');
config.flags.dotAll = hasFlag(flags, 's');
+ config.flags.multiline = hasFlag(flags, 'm');
config.transform.dotAllFlag = config.flags.dotAll && transform(options, 'dotAllFlag');
config.transform.unicodeFlag = (config.flags.unicode || config.flags.unicodeSets) && transform(options, 'unicodeFlag');
@@ -704,9 +815,15 @@ const rewritePattern = (pattern, flags, options) => {
transform(options, 'unicodeFlag') || transform(options, 'unicodePropertyEscapes')
);
config.transform.namedGroups = transform(options, 'namedGroups');
+ config.transform.modifiers = transform(options, 'modifiers');
+
+ config.modifiersData.i = undefined;
+ config.modifiersData.s = undefined;
+ config.modifiersData.m = undefined;
const regjsparserFeatures = {
'unicodeSet': Boolean(options && options.unicodeSetsFlag),
+ 'modifiers': Boolean(options && options.modifiers),
// Enable every stable RegExp feature by default
'unicodePropertyEscape': true,
@@ -728,9 +845,57 @@ const rewritePattern = (pattern, flags, options) => {
};
const tree = parse(pattern, flags, regjsparserFeatures);
+
+ if (config.transform.modifiers) {
+ if (/\(\?[a-z]*-[a-z]+:/.test(pattern)) {
+ // the pattern _likely_ contain inline disabled modifiers
+ // we need to traverse to make sure that they are actually modifiers and to collect them
+ const allDisabledModifiers = Object.create(null)
+ const itemStack = [tree];
+ let node;
+ while (node = itemStack.pop(), node != undefined) {
+ if (Array.isArray(node)) {
+ Array.prototype.push.apply(itemStack, node);
+ } else if (typeof node == 'object' && node != null) {
+ for (const key of Object.keys(node)) {
+ const value = node[key];
+ if (key == 'modifierFlags') {
+ if (value.disabling.length > 0){
+ value.disabling.split('').forEach((flag)=>{
+ allDisabledModifiers[flag] = true
+ });
+ }
+ } else if (typeof value == 'object' && value != null) {
+ itemStack.push(value);
+ }
+ }
+ }
+ }
+ for (const flag of Object.keys(allDisabledModifiers)) {
+ config.modifiersData[flag] = true;
+ }
+ }
+ }
+
// Note: `processTerm` mutates `tree` and `groups`.
processTerm(tree, regenerateOptions, groups);
assertNoUnmatchedReferences(groups);
+
+ const onNewFlags = options && options.onNewFlags;
+ if (onNewFlags) {
+ let newFlags = flags.split('').filter((flag) => !config.modifiersData[flag]).join('');
+ if (config.transform.unicodeSetsFlag) {
+ newFlags = newFlags.replace('v', 'u');
+ }
+ if (config.transform.unicodeFlag) {
+ newFlags = newFlags.replace('u', '');
+ }
+ if (config.transform.dotAllFlag === 'transform') {
+ newFlags = newFlags.replace('s', '');
+ }
+ onNewFlags(newFlags);
+ }
+
return generate(tree);
};
diff --git a/scripts/character-class-escape-sets.js b/scripts/character-class-escape-sets.js
deleted file mode 100644
index 9e0f6a3..0000000
--- a/scripts/character-class-escape-sets.js
+++ /dev/null
@@ -1,135 +0,0 @@
-'use strict';
-
-const fs = require('fs');
-const jsesc = require('jsesc');
-const regenerate = require('regenerate');
-
-const Zs = require('@unicode/unicode-15.0.0/General_Category/Space_Separator/code-points.js');
-
-const iuMappings = require('../data/iu-mappings.js');
-
-const caseFold = (codePoint) => {
- return iuMappings.get(codePoint) || false;
-};
-
-// Prepare a Regenerate set containing all code points, used for negative
-// character classes (if any).
-const UNICODE_SET = regenerate().addRange(0x0, 0x10FFFF);
-// Without the `u` flag, the range stops at 0xFFFF.
-// https://mths.be/es#sec-pattern-semantics
-const BMP_SET = regenerate().addRange(0x0, 0xFFFF);
-
-const ESCAPE_CHARS = {};
-const ESCAPE_CHARS_UNICODE = {};
-const ESCAPE_CHARS_UNICODE_IGNORE_CASE = {};
-const addCharacterClassEscape = (lower, set) => {
- ESCAPE_CHARS[lower] = ESCAPE_CHARS_UNICODE[lower] = set;
- const upper = lower.toUpperCase();
- ESCAPE_CHARS[upper] = BMP_SET.clone().remove(set);
- ESCAPE_CHARS_UNICODE[upper] = UNICODE_SET.clone().remove(set);
- // Check if one or more symbols in this set fold to another one. If so,
- // a copy of the set including the mapped symbols is created for use with
- // regular expressions that have both the `u` and `i` flags set.
- const codePoints = set.toArray();
- const iuSet = regenerate();
- let containsFoldingSymbols = false;
- for (const codePoint of codePoints) {
- let folded = caseFold(codePoint);
- if (folded) {
- containsFoldingSymbols = true;
- iuSet.add(folded);
- folded = caseFold(folded);
- if (folded) {
- iuSet.add(folded);
- }
- }
- }
- const iuLowerSet = containsFoldingSymbols ?
- iuSet.clone().add(set) :
- set;
- const iuUpperSet = UNICODE_SET.clone().remove(iuLowerSet);
- ESCAPE_CHARS_UNICODE_IGNORE_CASE[lower] = iuLowerSet;
- ESCAPE_CHARS_UNICODE_IGNORE_CASE[upper] = iuUpperSet;
-}
-
-// Prepare a Regenerate set for every existing character class escape.
-// https://mths.be/es#sec-characterclassescape
-addCharacterClassEscape(
- 'd', // `\d` and `\D`
- regenerate().addRange('0', '9')
-);
-addCharacterClassEscape(
- 's', // `\s` and `\S`
- regenerate(
- // https://mths.be/es#sec-white-space
- 0x0009,
- 0x000B,
- 0x000C,
- 0x0020,
- 0x00A0,
- 0xFEFF,
- Zs,
- // https://mths.be/es#sec-line-terminators
- 0x000A,
- 0x000D,
- 0x2028,
- 0x2029
- )
-);
-addCharacterClassEscape(
- 'w', // `\w` and `\W`
- regenerate('_').addRange('a', 'z').addRange('A', 'Z').addRange('0', '9')
-);
-
-/*----------------------------------------------------------------------------*/
-
-const codePointToString = (codePoint) => {
- return '0x' + codePoint.toString(16).toUpperCase();
-};
-
-// Regenerate plugin that turns a set into some JavaScript source code that
-// generates that set.
-regenerate.prototype.toCode = function() {
- const data = this.data;
- // Iterate over the data per `(start, end)` pair.
- let index = 0;
- let start;
- let end;
- const length = data.length;
- const loneCodePoints = [];
- const ranges = [];
- while (index < length) {
- start = data[index];
- end = data[index + 1] - 1; // Note: the `- 1` makes `end` inclusive.
- if (start == end) {
- loneCodePoints.push(codePointToString(start));
- } else {
- ranges.push(
- 'addRange(' + codePointToString(start) +
- ', ' + codePointToString(end) + ')'
- );
- }
- index += 2;
- }
- return 'regenerate(' + loneCodePoints.join(', ') + ')' +
- (ranges.length ? '\n\t\t.' + ranges.join('\n\t\t.') : '');
-};
-
-const stringify = (name, object) => {
- const source = 'exports.' + name + ' = new Map([\n\t' + Object.keys(object).map((character) => {
- const set = object[character];
- return '[' + jsesc(character, { 'wrap': true }) + ', ' + set.toCode() + ']';
- }).join(',\n\t') + '\n]);';
- return source;
-};
-
-const source = [
- '// Generated using `npm run build`. Do not edit.\n' +
- `'use strict';\n\nconst regenerate = require('regenerate');`,
- stringify('REGULAR', ESCAPE_CHARS),
- stringify('UNICODE', ESCAPE_CHARS_UNICODE),
- stringify('UNICODE_IGNORE_CASE', ESCAPE_CHARS_UNICODE_IGNORE_CASE)
-].join('\n\n');
-
-// Save the precompiled sets to a static file.
-fs.writeFileSync('data/character-class-escape-sets.js', source + '\n');
diff --git a/scripts/iu-mappings.js b/scripts/iu-mappings.js
deleted file mode 100644
index ce3f953..0000000
--- a/scripts/iu-mappings.js
+++ /dev/null
@@ -1,179 +0,0 @@
-'use strict';
-
-const fs = require('fs');
-const _ = require('lodash');
-const jsesc = require('jsesc');
-
-const hex = (number) => {
- return `0x${ number.toString(16).toUpperCase() }`;
-};
-
-const writeMap = (fileName, map) => {
- // Sort map by key.
- const sortedMap = new Map([...map].sort((a, b) => a[0] - b[0]));
- fs.writeFileSync(
- fileName,
- `module.exports = ${ jsesc(sortedMap, {
- 'compact': false,
- 'numbers': 'hexadecimal'
- }) };\n`
- );
-}
-
-// Given two code points, check if both are in the ASCII range and if one is
-// the uppercased version of the other. In that case, ES5 engines know about
-// this mapping, so it’s only needed to include one of the two in a
-// case-insensitive regular expression.
-const isES5CasedVariant = (a, b) => {
- return (a < 0x80 && b < 0x80) &&
- (oneWayMappings.get(a) == b || oneWayMappings.get(b) == a);
-};
-
-const extend = (map, key, value, callback) => {
- if (map.has(key)) {
- const currentValue = map.get(key);
- if (Array.isArray(currentValue)) {
- if (currentValue.indexOf(value) > -1) {
- return;
- }
- if (callback) {
- const skip = currentValue.some((codePoint) => {
- return callback(codePoint, value);
- });
- if (skip) {
- return;
- }
- }
- currentValue.push(value);
- } else {
- if (currentValue == value) {
- return;
- }
- if (callback) {
- if (callback(currentValue, value)) {
- return;
- }
- }
- map.set(key, [currentValue, value]);
- }
- } else {
- map.set(key, value);
- }
-};
-
-// From <http://unicode.org/Public/UCD/latest/ucd/CaseFolding.txt>:
-//
-// The status field is:
-// C: common case folding, common mappings shared by both simple and full
-// mappings.
-// F: full case folding, mappings that cause strings to grow in length. Multiple
-// characters are separated by spaces.
-// S: simple case folding, mappings to single characters where different from F.
-// T: special case for uppercase I and dotted uppercase I
-// - For non-Turkic languages, this mapping is normally not used.
-// - For Turkic languages (tr, az), this mapping can be used instead of the
-// normal mapping for these characters. Note that the Turkic mappings do
-// not maintain canonical equivalence without additional processing.
-// See the discussions of case mapping in the Unicode Standard for more
-// information.
-//
-// Usage:
-// A. To do a simple case folding, use the mappings with status C + S.
-// B. To do a full case folding, use the mappings with status C + F.
-
-const commonMappings = require('@unicode/unicode-15.0.0/Case_Folding/C/code-points.js');
-const simpleMappings = require('@unicode/unicode-15.0.0/Case_Folding/S/code-points.js');
-
-// We want the `C` mappings in both directions (i.e. `A` should fold to `a`
-// and `a` to `A`), and the `S` mappings in both directions (i.e. `ẞ` should
-// fold to `ß` and `ß` to `ẞ`). Let’s start with the simple case folding (in
-// one direction) first, then filter the set, and then deal with the inverse.
-const oneWayMappings = new Map();
-for (const [from, to] of commonMappings) {
- oneWayMappings.set(from, to);
-}
-for (const [from, to] of simpleMappings) {
- oneWayMappings.set(from, to);
-}
-// Note: various code points can fold into the same code point, so it’s not
-// possible to simply invert `oneWayMappings` — some entries would be lost in
-// the process.
-
-// In case-insignificant matches when `Unicode` is `true` (i.e. when the `u`
-// flag is enabled), all characters are implicitly case-folded using the
-// simple mapping provided by the Unicode standard immediately before they
-// are compared. The simple mapping always maps to a single code point, so it
-// does not map, for example, `ß` (U+00DF) to `SS`. It may however map a code
-// point outside the Basic Latin range to a character within, for example, `ſ`
-// (U+017F) to `s`. Such characters are not mapped if `Unicode` is `false`.
-// This prevents Unicode code points such as U+017F and U+212A from matching
-// regular expressions such as `/[a‑z]/i`, but they will match `/[a‑z]/ui`.
-// https://mths.be/es6#sec-runtime-semantics-canonicalize-abstract-operation
-// Get the mappings that are unique to regular expressions that have both the
-// `i` and `u` flags set. In addition to the above, this includes all mappings
-// for astral code points.
-const filteredMappings = new Map();
-for (const [from, to] of oneWayMappings) {
- // Case folding is applied to both the pattern and the string being matched.
- // Because of that e.g. `/[A-Z]/iu` matches U+017F and U+212A, just like
- // `/[a-z]/iu` would, even though no symbol in the range from `A` to `Z`
- // folds to U+017F or U+212A directly. Since we’re only transpiling regular
- // expressions and not strings, we have to account for this in regular
- // expressions only. This can be done as per this example:
- // 1. `oneWayMappings` already maps `S` to `s`. (83 → 115)
- // 2. `oneWayMappings` already maps `ſ` to `s`. (383 → 115)
- // 3. So, in the generated mappings, make sure `S` maps to `ſ`. (83 → 383)
- // Check if there are any other code points that map to the same `to` value.
- for (const [otherFrom, otherTo] of oneWayMappings) {
- if (otherFrom != from && otherTo == to) {
- // Note: we could use `extend` here, but it’s not necessary as there can
- // only be a single value for the key `from` at this point.
- filteredMappings.set(from, otherFrom);
- }
- }
- if (
- // Include astral code points.
- (from > 0xFFFF || to > 0xFFFF) ||
- // Exclude ES5 mappings as per the above comment.
- // https://mths.be/es6#sec-runtime-semantics-canonicalize-abstract-operation
- (
- // TODO: Make this not depend on the engine in which this build script
- // runs. (If V8 has a bug, then the generated data has the same bug.)
- !RegExp(String.fromCodePoint(from), 'i').test(String.fromCodePoint(to))
- )
- ) {
- extend(filteredMappings, from, to);
- } else {
- const stringFrom = String.fromCodePoint(from);
- const stringTo = String.fromCodePoint(to);
- const code = `/${
- jsesc(stringFrom)
- }/i.test(${
- jsesc(stringTo, { 'wrap': true })
- })`;
- console.log(
- `Skipping ${ hex(from) } → ${ hex(to) } since ${ code } is already \`true\`.`
- );
- // The following snippet was used to create https://mths.be/demo/regex-i.
- // https://github.com/mathiasbynens/regexpu-core/issues/7#issuecomment-225894534
- // console.log(
- // `console.assert(${ code }, ${ JSON.stringify(code) });`
- // );
- }
-}
-
-// Create a new object containing all `filteredMappings` and their inverse.
-const iuMappings = new Map();
-for (const [from, to] of filteredMappings) {
- if (Array.isArray(to)) {
- for (const codePoint of to) {
- extend(iuMappings, from, codePoint, isES5CasedVariant);
- extend(iuMappings, codePoint, from, isES5CasedVariant);
- }
- } else {
- extend(iuMappings, from, to, isES5CasedVariant);
- extend(iuMappings, to, from, isES5CasedVariant);
- }
-}
-
-writeMap('data/iu-mappings.js', iuMappings);
diff --git a/tests/tests.js b/tests/tests.js
deleted file mode 100644
index 9812394..0000000
--- a/tests/tests.js
+++ /dev/null
@@ -1,1445 +0,0 @@
-'use strict';
-
-const assert = require('assert');
-const regenerate = require('regenerate');
-const rewritePattern = require('../rewrite-pattern.js');
-const fixtures = require('regexpu-fixtures');
-
-const BMP_SET = regenerate().addRange(0x0, 0xFFFF);
-const BMP_PATTERN = BMP_SET.toString({ 'bmpOnly': true });
-const UNICODE_SET = regenerate().addRange(0x0, 0x10FFFF);
-const UNICODE_PATTERN = UNICODE_SET.toString();
-
-describe('rewritePattern { unicodeFlag }', () => {
- const options = {
- 'unicodeFlag': 'transform'
- };
- for (const fixture of fixtures) {
- const pattern = fixture.pattern;
- for (const flag of fixture.flags) {
- if (flag.includes('u')) {
- it('rewrites `/' + pattern + '/' + flag + '` correctly', () => {
- assert.equal(rewritePattern(pattern, flag, options), fixture.transpiled);
- });
- } else {
- it('leaves `/' + pattern + '/' + flag + '` as-is', () => {
- // TODO: Update regexpu-fixtures
- const expected = pattern.replace(/^\uD834\uDF06/g, '\\uD834\\uDF06');
- assert.equal(rewritePattern(pattern, flag, options), expected);
- });
- }
- }
- }
-});
-
-const unicodePropertyEscapeFixtures = [
- // https://unicode.org/reports/tr18/#RL1.2 item 1
- {
- 'path': 'General_Category/Uppercase_Letter',
- 'expressions': [
- 'gc=Lu',
- 'gc=Uppercase_Letter',
- 'General_Category=Lu',
- 'General_Category=Uppercase_Letter',
- 'Lu',
- 'Uppercase_Letter'
- ]
- },
- // https://unicode.org/reports/tr18/#RL1.2 item 2a
- {
- 'path': 'Script/Greek',
- 'expressions': [
- 'sc=Grek',
- 'sc=Greek',
- 'Script=Grek',
- 'Script=Greek'
- ]
- },
- {
- 'path': 'Script/Hiragana',
- 'expressions': [
- 'sc=Hira',
- 'sc=Hiragana',
- 'Script=Hira',
- 'Script=Hiragana'
- ]
- },
- {
- 'path': 'Script/Kawi',
- 'expressions': [
- 'sc=Kawi',
- ]
- },
- // https://unicode.org/reports/tr18/#RL1.2 item 2b
- {
- 'path': 'Script_Extensions/Greek',
- 'expressions': [
- 'scx=Grek',
- 'scx=Greek',
- 'Script_Extensions=Grek',
- 'Script_Extensions=Greek'
- ]
- },
- // https://unicode.org/reports/tr18/#RL1.2 item 3
- {
- 'path': 'Binary_Property/Alphabetic',
- 'expressions': [
- 'Alpha',
- 'Alphabetic'
- ]
- },
- // https://unicode.org/reports/tr18/#RL1.2 item 4
- {
- 'path': 'Binary_Property/Uppercase',
- 'expressions': [
- 'Upper',
- 'Uppercase'
- ]
- },
- // https://unicode.org/reports/tr18/#RL1.2 item 5
- {
- 'path': 'Binary_Property/Lowercase',
- 'expressions': [
- 'Lower',
- 'Lowercase'
- ]
- },
- // https://unicode.org/reports/tr18/#RL1.2 item 6
- {
- 'path': 'Binary_Property/White_Space',
- 'expressions': [
- 'WSpace',
- 'White_Space'
- ]
- },
- // https://unicode.org/reports/tr18/#RL1.2 item 7
- {
- 'path': 'Binary_Property/Noncharacter_Code_Point',
- 'expressions': [
- 'NChar',
- 'Noncharacter_Code_Point'
- ]
- },
- // https://unicode.org/reports/tr18/#RL1.2 item 8
- {
- 'path': 'Binary_Property/Default_Ignorable_Code_Point',
- 'expressions': [
- 'DI',
- 'Default_Ignorable_Code_Point'
- ]
- },
- // https://unicode.org/reports/tr18/#RL1.2 item 9a
- {
- 'path': 'Binary_Property/Any',
- 'expressions': [
- 'Any'
- ]
- },
- // https://unicode.org/reports/tr18/#RL1.2 item 9b
- {
- 'path': 'Binary_Property/ASCII',
- 'expressions': [
- 'ASCII'
- ]
- },
- // https://unicode.org/reports/tr18/#RL1.2 item 9c
- {
- 'path': 'Binary_Property/Assigned',
- 'expressions': [
- 'Assigned'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/ASCII_Hex_Digit',
- 'expressions': [
- 'ASCII_Hex_Digit',
- 'AHex'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- // {
- // 'path': 'Bidi_Class/Arabic_Letter',
- // 'expressions': [
- // 'bc=AL',
- // 'bc=Arabic_Letter',
- // 'Bidi_Class=AL',
- // 'Bidi_Class=Arabic_Letter'
- // ]
- // },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Bidi_Control',
- 'expressions': [
- 'Bidi_C',
- 'Bidi_Control'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Bidi_Mirrored',
- 'expressions': [
- 'Bidi_M',
- 'Bidi_Mirrored'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Case_Ignorable',
- 'expressions': [
- 'CI',
- 'Case_Ignorable',
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Cased',
- 'expressions': [
- 'Cased'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Changes_When_NFKC_Casefolded',
- 'expressions': [
- 'CWKCF',
- 'Changes_When_NFKC_Casefolded'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Changes_When_Casefolded',
- 'expressions': [
- 'CWCF',
- 'Changes_When_Casefolded'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Changes_When_Casemapped',
- 'expressions': [
- 'CWCM',
- 'Changes_When_Casemapped'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Changes_When_Lowercased',
- 'expressions': [
- 'CWL',
- 'Changes_When_Lowercased'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Changes_When_Titlecased',
- 'expressions': [
- 'CWT',
- 'Changes_When_Titlecased'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Changes_When_Uppercased',
- 'expressions': [
- 'CWU',
- 'Changes_When_Uppercased'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Dash',
- 'expressions': [
- 'Dash'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Deprecated',
- 'expressions': [
- 'Dep',
- 'Deprecated'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Diacritic',
- 'expressions': [
- 'Dia',
- 'Diacritic'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Extender',
- 'expressions': [
- 'Ext',
- 'Extender'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Grapheme_Base',
- 'expressions': [
- 'Gr_Base',
- 'Grapheme_Base'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Grapheme_Extend',
- 'expressions': [
- 'Gr_Ext',
- 'Grapheme_Extend'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Hex_Digit',
- 'expressions': [
- 'Hex',
- 'Hex_Digit'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/ID_Continue',
- 'expressions': [
- 'IDC',
- 'ID_Continue'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/ID_Start',
- 'expressions': [
- 'IDS',
- 'ID_Start'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Ideographic',
- 'expressions': [
- 'Ideo',
- 'Ideographic'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/IDS_Binary_Operator',
- 'expressions': [
- 'IDSB',
- 'IDS_Binary_Operator'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/IDS_Trinary_Operator',
- 'expressions': [
- 'IDST',
- 'IDS_Trinary_Operator'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Join_Control',
- 'expressions': [
- 'Join_C',
- 'Join_Control'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Logical_Order_Exception',
- 'expressions': [
- 'LOE',
- 'Logical_Order_Exception'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Math',
- 'expressions': [
- 'Math'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Pattern_Syntax',
- 'expressions': [
- 'Pat_Syn',
- 'Pattern_Syntax'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Pattern_White_Space',
- 'expressions': [
- 'Pat_WS',
- 'Pattern_White_Space'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Quotation_Mark',
- 'expressions': [
- 'QMark',
- 'Quotation_Mark'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Radical',
- 'expressions': [
- 'Radical'
- ]
- },
- {
- 'path': 'Binary_Property/Regional_Indicator',
- 'expressions': [
- 'RI',
- 'Regional_Indicator'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Soft_Dotted',
- 'expressions': [
- 'SD',
- 'Soft_Dotted'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Sentence_Terminal',
- 'expressions': [
- 'STerm',
- 'Sentence_Terminal'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Terminal_Punctuation',
- 'expressions': [
- 'Term',
- 'Terminal_Punctuation'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Unified_Ideograph',
- 'expressions': [
- 'UIdeo',
- 'Unified_Ideograph'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/Variation_Selector',
- 'expressions': [
- 'VS',
- 'Variation_Selector'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/XID_Continue',
- 'expressions': [
- 'XIDC',
- 'XID_Continue'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- {
- 'path': 'Binary_Property/XID_Start',
- 'expressions': [
- 'XIDS',
- 'XID_Start'
- ]
- },
- // https://unicode.org/reports/tr18/#RL2.7
- // {
- // 'path': 'Bidi_Paired_Bracket_Type/Open',
- // 'expressions': [
- // 'bpt=o',
- // 'bpt=Open',
- // 'Bidi_Paired_Bracket_Type=o',
- // 'Bidi_Paired_Bracket_Type=Open'
- // ]
- // },
- // https://unicode.org/reports/tr51/
- {
- 'path': 'Binary_Property/Emoji',
- 'expressions': [
- 'Emoji'
- ]
- },
- // https://unicode.org/reports/tr51/
- {
- 'path': 'Binary_Property/Emoji_Component',
- 'expressions': [
- 'Emoji_Component'
- ]
- },
- // https://unicode.org/reports/tr51/
- {
- 'path': 'Binary_Property/Emoji_Modifier',
- 'expressions': [
- 'Emoji_Modifier'
- ]
- },
- // https://unicode.org/reports/tr51/
- {
- 'path': 'Binary_Property/Emoji_Modifier_Base',
- 'expressions': [
- 'Emoji_Modifier_Base'
- ]
- },
- // https://unicode.org/reports/tr51/
- {
- 'path': 'Binary_Property/Emoji_Presentation',
- 'expressions': [
- 'Emoji_Presentation'
- ]
- },
- // https://unicode.org/reports/tr51/proposed.html
- {
- 'path': 'Binary_Property/Extended_Pictographic',
- 'expressions': [
- 'Extended_Pictographic'
- ]
- },
- {
- 'path': 'Script_Extensions/Yezidi',
- 'expressions': [
- 'scx=Yezi',
- 'scx=Yezidi',
- 'Script_Extensions=Yezi',
- 'Script_Extensions=Yezidi',
- ]
- },
- {
- 'path': 'Script_Extensions/Toto',
- 'expressions': [
- 'scx=Toto',
- 'Script_Extensions=Toto',
- ]
- },
-];
-
-const getPropertyValuePattern = (path) => {
- const codePoints = require(`@unicode/unicode-15.0.0/${
- path }/code-points.js`);
- return {
- 'p': regenerate(codePoints).toString(),
- 'P': UNICODE_SET.clone().remove(codePoints).toString()
- };
-};
-
-describe('unicodePropertyEscapes', () => {
- const features = {
- 'unicodePropertyEscapes': 'transform',
- 'unicodeFlag': 'transform'
- };
- for (const fixture of unicodePropertyEscapeFixtures) {
- const expected = getPropertyValuePattern(fixture.path);
- for (const pattern of fixture.expressions) {
- const p = `\\p{${ pattern }}`;
- it('rewrites `/' + p + '/u` correctly', () => {
- const transpiled = rewritePattern(p, 'u', features);
- if (transpiled != '(?:' + expected.p + ')') {
- assert.equal(transpiled, expected.p);
- }
- });
- const P = `\\P{${ pattern }}`;
- it('rewrites `/' + P + '/u` correctly', () => {
- const transpiled = rewritePattern(P, 'u', features);
- if (transpiled != '(?:' + expected.P + ')') {
- assert.equal(transpiled, expected.P);
- }
- });
- }
- }
- it('transpiles Unicode property escapes within various constructions', () => {
- assert.equal(
- rewritePattern('\\p{ASCII_Hex_Digit}', 'u', features),
- '[0-9A-Fa-f]'
- );
- assert.equal(
- rewritePattern('\\p{Script_Extensions=Anatolian_Hieroglyphs}', 'u', features),
- '(?:\\uD811[\\uDC00-\\uDE46])'
- );
- assert.equal(
- rewritePattern('\\p{ASCII_Hex_Digit}+', 'u', features),
- '[0-9A-Fa-f]+'
- );
- assert.equal(
- rewritePattern('\\p{Script_Extensions=Anatolian_Hieroglyphs}+', 'u', features),
- '(?:\\uD811[\\uDC00-\\uDE46])+'
- );
- assert.equal(
- rewritePattern('[\\p{ASCII_Hex_Digit}_]', 'u', features),
- '[0-9A-F_a-f]'
- );
- assert.equal(
- rewritePattern('[^\\p{ASCII_Hex_Digit}_]', 'u', features),
- '(?:(?![0-9A-F_a-f])[\\s\\S])'
- );
- assert.equal(
- rewritePattern('[\\P{Script_Extensions=Anatolian_Hieroglyphs}]', 'u', features),
- '(?:[\\0-\\uD7FF\\uE000-\\uFFFF]|[\\uD800-\\uD810\\uD812-\\uDBFF][\\uDC00-\\uDFFF]|\\uD811[\\uDE47-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF])'
- );
- assert.equal(
- rewritePattern('[\\p{Script_Extensions=Anatolian_Hieroglyphs}_]', 'u', features),
- '(?:_|\\uD811[\\uDC00-\\uDE46])'
- );
- assert.equal(
- rewritePattern('[\\P{Script_Extensions=Anatolian_Hieroglyphs}_]', 'u', features),
- '(?:[\\0-\\uD7FF\\uE000-\\uFFFF]|[\\uD800-\\uD810\\uD812-\\uDBFF][\\uDC00-\\uDFFF]|\\uD811[\\uDE47-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF])'
- );
- assert.equal(
- rewritePattern('(?:\\p{ASCII_Hex_Digit})', 'u', features),
- '(?:[0-9A-Fa-f])'
- );
- assert.equal(
- rewritePattern('(?:\\p{Script_Extensions=Anatolian_Hieroglyphs})', 'u', features),
- '(?:(?:\\uD811[\\uDC00-\\uDE46]))'
- );
- assert.equal(
- rewritePattern('(?:\\p{Script_Extensions=Wancho})', 'u', features),
- '(?:(?:\\uD838[\\uDEC0-\\uDEF9\\uDEFF]))'
- );
- });
- it('throws on unknown binary properties', () => {
- assert.throws(() => {
- rewritePattern('\\p{UnknownBinaryProperty}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\P{UnknownBinaryProperty}', 'u', features);
- }, Error);
- });
- it('throws on explicitly unsupported properties', () => {
- // https://github.com/tc39/proposal-regexp-unicode-property-escapes/issues/27
- assert.throws(() => {
- rewritePattern('\\P{Composition_Exclusion}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\p{Expands_On_NFC}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\p{Expands_On_NFD}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\p{Expands_On_NFKC}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\p{Expands_On_NFKD}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\p{FC_NFKC_Closure}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\p{Full_Composition_Exclusion}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\P{Grapheme_Link}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\P{Hyphen}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\P{Other_Alphabetic}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\P{Other_Default_Ignorable_Code_Point}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\P{Other_Grapheme_Extend}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\P{Other_ID_Continue}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\P{Other_ID_Start}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\P{Other_Lowercase}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\P{Other_Math}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\P{Other_Uppercase}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\P{Prepended_Concatenation_Mark}', 'u', features);
- }, Error);
- });
- it('throws on non-binary properties without a value', () => {
- assert.throws(() => {
- rewritePattern('\\p{General_Category}', 'u', features);
- }, Error);
- });
- it('throws on unknown property values', () => {
- assert.throws(() => {
- rewritePattern('\\p{General_Category=UnknownCategory}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\P{General_Category=UnknownCategory}', 'u', features);
- }, Error);
- });
- it('throws when loose matching is attempted', () => {
- assert.throws(() => {
- rewritePattern('\\p{gc=uppercaseletter}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\p{Block=Superscripts and Subscripts}', 'u', features);
- }, Error);
- assert.throws(() => {
- rewritePattern('\\P{_-_lOwEr_C-A_S-E_-_}', 'u', features);
- }, Error);
- });
- it('simplifies the output using Unicode code point escapes when not transforming the u flag', () => {
- assert.equal(
- rewritePattern('\\p{Script_Extensions=Anatolian_Hieroglyphs}', 'u', {
- 'unicodePropertyEscapes': 'transform',
- }),
- '[\\u{14400}-\\u{14646}]'
- );
- assert.equal(
- rewritePattern('[\\p{Script_Extensions=Anatolian_Hieroglyphs}]', 'u', {
- 'unicodePropertyEscapes': 'transform',
- }),
- '[\\u{14400}-\\u{14646}]'
- );
- });
- it('should not transpile unicode property when unicodePropertyEscapes is not enabled', () => {
- assert.equal(
- rewritePattern('\\p{ASCII_Hex_Digit}\\P{ASCII_Hex_Digit}', 'u'),
- '\\p{ASCII_Hex_Digit}\\P{ASCII_Hex_Digit}'
- );
- });
- it('should transpile to minimal case-insensitive set', () => {
- assert.equal(
- rewritePattern('\u03B8', 'iu', {
- 'unicodeFlag': 'transform'
- }),
- '[\\u03B8\\u03F4]'
- );
- assert.equal(
- rewritePattern('\u03B8', 'iu'),
- '\\u03B8'
- );
- });
-});
-
-const dotAllFlagFixtures = [
- {
- 'pattern': '.',
- 'flags': 's',
- 'expected': '[\\s\\S]'
- },
- {
- 'pattern': '.',
- 'flags': 'gimsy',
- 'expected': '[\\s\\S]'
- },
- {
- 'pattern': '.',
- 'flags': 's',
- 'expected': '[\\s\\S]',
- 'options': { unicodeFlag: 'transform' }
- },
- {
- 'pattern': '.',
- 'flags': 'gimsy',
- 'expected': '[\\s\\S]',
- 'options': { unicodeFlag: 'transform' }
- },
- {
- 'pattern': '.',
- 'flags': 'su',
- 'expected': UNICODE_PATTERN,
- 'options': { unicodeFlag: 'transform' }
- },
- {
- 'pattern': '.',
- 'flags': 'gimsuy',
- 'expected': UNICODE_PATTERN,
- 'options': { unicodeFlag: 'transform' }
- }
-];
-
-describe('dotAllFlag', () => {
- for (const fixture of dotAllFlagFixtures) {
- const pattern = fixture.pattern;
- const flags = fixture.flags;
- const options = Object.assign({
- 'dotAllFlag': 'transform'
- }, fixture.options);
- it('rewrites `/' + pattern + '/' + flags + '` correctly', () => {
- const transpiled = rewritePattern(pattern, flags, options);
- const expected = fixture.expected;
- if (transpiled != '(?:' + expected + ')') {
- assert.strictEqual(transpiled, expected);
- }
- });
- }
-
- it('not transformed', () => {
- it('leaves `/./su` as-is', () => {
- assert.equal(rewritePattern('.', 'su'), '.');
- });
- });
-});
-
-const namedGroupFixtures = [
- {
- 'pattern': '(?<name>)\\k<name>',
- 'flags': '',
- 'expected': '()\\1',
- 'expectedGroups': [
- ['name', 1]
- ]
- },
- {
- 'pattern': '(?<name1>)(?<name2>)\\k<name1>\\k<name2>',
- 'flags': '',
- 'expected': '()()\\1\\2',
- 'expectedGroups': [
- ['name1', 1],
- ['name2', 2]
- ]
- },
- {
- 'pattern': '()(?<name>)\\k<name>',
- 'flags': '',
- 'expected': '()()\\2',
- 'expectedGroups': [
- ['name', 2]
- ]
- },
- {
- 'pattern': '(?<name>)()\\1',
- 'flags': '',
- 'expected': '()()\\1'
- },
- {
- 'pattern': '\\k<name>\\k<name>(?<name>)\\k<name>',
- 'flags': '',
- 'expected': '(?:)(?:)()\\1'
- },
- {
- 'pattern': '(?<name>\\k<name>)',
- 'flags': '',
- 'expected': '(\\1)'
- },
- {
- 'pattern': '(?<$𐒤>a)b\\k<$𐒤>',
- 'flags': '',
- 'expected': '(a)b\\1'
- },
- {
- 'pattern': '(?<=a)(?<!b)(?=c)(?!d)(?:e)(?<name>f)\\k<name>',
- 'flags': '',
- 'expected': '(?<=a)(?<!b)(?=c)(?!d)(?:e)(f)\\1',
- 'expectedGroups': [
- ['name', 1]
- ]
- },
- {
- 'pattern': '(?:(?<a>x)|(?<a>y))\\k<a>',
- 'flags': '',
- 'expected': '(?:(x)|(y))\\1\\2',
- 'expectedGroups': [
- ['a', 1],
- ['a', 2]
- ]
- },
- {
- 'pattern': '(?:(?<a>x)\\k<a>|(?<a>y)\\k<a>)',
- 'flags': '',
- 'expected': '(?:(x)\\1|(y)\\1\\2)',
- 'expectedGroups': [
- ['a', 1],
- ['a', 2]
- ]
- }
-];
-
-describe('namedGroups', () => {
- for (const fixture of namedGroupFixtures) {
- const {
- pattern,
- flags,
- expected,
- expectedGroups,
- options = {}
- } = fixture;
- const groups = [];
-
- Object.assign(options, {
- namedGroups: 'transform',
- 'onNamedGroup': (name, index) => {
- groups.push([ name, index ]);
- }
- });
-
- it('rewrites `/' + pattern + '/' + flags + '` correctly', () => {
- const transpiled = rewritePattern(pattern, flags, options);
- assert.strictEqual(transpiled, expected);
- if (expectedGroups) {
- assert.deepStrictEqual(groups, expectedGroups);
- }
- });
- }
-
- it('onNamedGroup is optional', () => {
- let transpiled;
- const expected = '()';
- assert.doesNotThrow(() => {
- transpiled = rewritePattern('(?<name>)', '', {
- namedGroups: 'transform'
- });
- });
- assert.strictEqual(transpiled, expected);
- });
-
- it('multiple groups with the same name in the same disjunction are disallowed', () => {
- assert.throws(() => {
- rewritePattern('(?<name>)(?<name>)', '', {
- namedGroups: 'transform'
- });
- });
-
- assert.throws(() => {
- rewritePattern('(?<name>)(?:a|(?<name>))', '', {
- namedGroups: 'transform'
- });
- });
-
- assert.throws(() => {
- rewritePattern('(?:b|(?<name>))(?:a|(?<name>))', '', {
- namedGroups: 'transform'
- });
- });
- });
-
- it('multiple groups with the same name in the different disjunctions are allowed', () => {
- assert.doesNotThrow(() => {
- rewritePattern('(?<name>)|(?<name>)', '', {
- namedGroups: 'transform'
- });
- });
-
- assert.doesNotThrow(() => {
- rewritePattern('(?:b|(?<name>))|(?:a|(?<name>))', '', {
- namedGroups: 'transform'
- });
- });
- });
-
- it('named references must reference a group', () => {
- assert.throws(() => {
- rewritePattern('\\k<name>', '', {
- namedGroups: 'transform'
- });
- });
- });
-
- it('should not transpile when namedGroups is not enabled', () => {
- const pattern = '(?<name>)';
- let transpiled;
- assert.doesNotThrow(() => {
- transpiled = rewritePattern(pattern, '');
- });
- assert.strictEqual(pattern, transpiled);
- });
-
- it('should support named group references when namedGroups is not enabled', () => {
- const pattern = '(?<name>)\\k<name>';
- let transpiled;
- assert.doesNotThrow(() => {
- transpiled = rewritePattern(pattern, '');
- });
- assert.strictEqual(pattern, transpiled);
- });
-
- it('should validate named group references when namedGroups is not enabled', () => {
- assert.throws(() => rewritePattern('\\k<foo>', ''), /Unknown group names: foo/);
- });
-
- it('shold call onNamedGroup even if namedGroups is not enabled', () => {
- let called = false;
- rewritePattern('(?<name>)', '', {
- onNamedGroup() {
- called = true;
- },
- });
- assert.strictEqual(called, true);
- })
-});
-
-const characterClassFixtures = [
- {
- pattern: '[^K]', // LATIN CAPITAL LETTER K
- flags: 'iu',
- expected: '(?![K\\u212A])[\\s\\S]',
- 'options': { unicodeFlag: 'transform' }
- },
- {
- pattern: '[^k]', // LATIN SMALL LETTER K
- flags: 'iu',
- expected: '(?![k\\u212A])[\\s\\S]',
- 'options': { unicodeFlag: 'transform' }
- },
- {
- pattern: '[^\u212a]', // KELVIN SIGN
- flags: 'iu',
- expected: '(?![K\\u212A])[\\s\\S]',
- 'options': { unicodeFlag: 'transform' }
- },
- {
- pattern: '[^K]', // LATIN CAPITAL LETTER K
- flags: 'iu',
- expected: '[^K]',
- 'options': {}
- },
- {
- pattern: '[^k]', // LATIN SMALL LETTER K
- flags: 'iu',
- expected: '[^k]',
- 'options': {}
- },
- {
- pattern: '[^\u212a]', // KELVIN SIGN
- flags: 'iu',
- expected: '[^\u212a]',
- 'options': {}
- },
- {
- pattern: '[^K]', // LATIN CAPITAL LETTER K
- flags: 'u',
- expected: '(?!K)[\\s\\S]',
- 'options': { unicodeFlag: 'transform' }
- },
- {
- pattern: '[^k]', // LATIN SMALL LETTER K
- flags: 'u',
- expected: '(?!k)[\\s\\S]',
- 'options': { unicodeFlag: 'transform' }
- },
- {
- pattern: '[^\u212a]', // KELVIN SIGN
- flags: 'u',
- expected: '(?!\\u212A)[\\s\\S]',
- 'options': { unicodeFlag: 'transform' }
- },
- {
- pattern: '[^K]', // LATIN CAPITAL LETTER K
- flags: 'u',
- expected: '[^K]',
- 'options': {}
- },
- {
- pattern: '[^k]', // LATIN SMALL LETTER K
- flags: 'u',
- expected: '[^k]',
- 'options': {}
- },
- {
- pattern: '[^\u212a]', // KELVIN SIGN
- flags: 'u',
- expected: '[^\u212a]',
- 'options': {}
- }
-];
-
-describe('character classes', () => {
- for (const fixture of characterClassFixtures) {
- const pattern = fixture.pattern;
- const flags = fixture.flags;
- const options = fixture.options;
- const transformUnicodeFlag = options.unicodeFlag === 'transform';
- it('rewrites `/' + pattern + '/' + flags + '` with' + (transformUnicodeFlag ? 'out' : '') + ' unicode correctly', () => {
- const transpiled = rewritePattern(pattern, flags, options);
- const expected = fixture.expected;
- if (transpiled != '(?:' + expected + ')') {
- assert.strictEqual(transpiled, expected);
- }
- });
- }
-});
-
-const TRANSFORM_U = { unicodeFlag: 'transform', unicodeSetsFlag: 'transform' };
-
-const Basic_Emoji = {
- get all() { return `${this.strings}|[${this.chars}]` },
- strings: "🅰️|🅱️|🅾️|🅿️|🈂️|🈷️|🌡️|🌤️|🌥️|🌦️|🌧️|🌨️|🌩️|🌪️|🌫️|🌬️|🌶️|🍽️|🎖️|🎗️|🎙️|🎚️|🎛️|🎞️|🎟️|🏋️|🏌️|🏍️|🏎️|🏔️|🏕️|🏖️|🏗️|🏘️|🏙️|🏚️|🏛️|🏜️|🏝️|🏞️|🏟️|🏳️|🏵️|🏷️|🐿️|👁️|📽️|🕉️|🕊️|🕯️|🕰️|🕳️|🕴️|🕵️|🕶️|🕷️|🕸️|🕹️|🖇️|🖊️|🖋️|🖌️|🖍️|🖐️|🖥️|🖨️|🖱️|🖲️|🖼️|🗂️|🗃️|🗄️|🗑️|🗒️|🗓️|🗜️|🗝️|🗞️|🗡️|🗣️|🗨️|🗯️|🗳️|🗺️|🛋️|🛍️|🛎️|🛏️|🛠️|🛡️|🛢️|🛣️|🛤️|🛥️|🛩️|🛰️|🛳️|©️|®️|‼️|⁉️|™️|ℹ️|↔️|↕️|↖️|↗️|↘️|↙️|↩️|↪️|⌨️|⏏️|⏭️|⏮️|⏯️|⏱️|⏲️|⏸️|⏹️|⏺️|Ⓜ️|▪️|▫️|▶️|◀️|◻️|◼️|☀️|☁️|☂️|☃️|☄️|☎️|☑️|☘️|☝️|☠️|☢️|☣️|☦️|☪️|☮️|☯️|☸️|☹️|☺️|♀️|♂️|♟️|♠️|♣️|♥️|♦️|♨️|♻️|♾️|⚒️|⚔️|⚕️|⚖️|⚗️|⚙️|⚛️|⚜️|⚠️|⚧️|⚰️|⚱️|⛈️|⛏️|⛑️|⛓️|⛩️|⛰️|⛱️|⛴️|⛷️|⛸️|⛹️|✂️|✈️|✉️|✌️|✍️|✏️|✒️|✔️|✖️|✝️|✡️|✳️|✴️|❄️|❇️|❣️|❤️|➡️|⤴️|⤵️|⬅️|⬆️|⬇️|〰️|〽️|㊗️|㊙️",
- chars: "\\u231A\\u231B\\u23E9-\\u23EC\\u23F0\\u23F3\\u25FD\\u25FE\\u2614\\u2615\\u2648-\\u2653\\u267F\\u2693\\u26A1\\u26AA\\u26AB\\u26BD\\u26BE\\u26C4\\u26C5\\u26CE\\u26D4\\u26EA\\u26F2\\u26F3\\u26F5\\u26FA\\u26FD\\u2705\\u270A\\u270B\\u2728\\u274C\\u274E\\u2753-\\u2755\\u2757\\u2795-\\u2797\\u27B0\\u27BF\\u2B1B\\u2B1C\\u2B50\\u2B55\\u{1F004}\\u{1F0CF}\\u{1F18E}\\u{1F191}-\\u{1F19A}\\u{1F201}\\u{1F21A}\\u{1F22F}\\u{1F232}-\\u{1F236}\\u{1F238}-\\u{1F23A}\\u{1F250}\\u{1F251}\\u{1F300}-\\u{1F320}\\u{1F32D}-\\u{1F335}\\u{1F337}-\\u{1F37C}\\u{1F37E}-\\u{1F393}\\u{1F3A0}-\\u{1F3CA}\\u{1F3CF}-\\u{1F3D3}\\u{1F3E0}-\\u{1F3F0}\\u{1F3F4}\\u{1F3F8}-\\u{1F43E}\\u{1F440}\\u{1F442}-\\u{1F4FC}\\u{1F4FF}-\\u{1F53D}\\u{1F54B}-\\u{1F54E}\\u{1F550}-\\u{1F567}\\u{1F57A}\\u{1F595}\\u{1F596}\\u{1F5A4}\\u{1F5FB}-\\u{1F64F}\\u{1F680}-\\u{1F6C5}\\u{1F6CC}\\u{1F6D0}-\\u{1F6D2}\\u{1F6D5}-\\u{1F6D7}\\u{1F6DC}-\\u{1F6DF}\\u{1F6EB}\\u{1F6EC}\\u{1F6F4}-\\u{1F6FC}\\u{1F7E0}-\\u{1F7EB}\\u{1F7F0}\\u{1F90C}-\\u{1F93A}\\u{1F93C}-\\u{1F945}\\u{1F947}-\\u{1F9FF}\\u{1FA70}-\\u{1FA7C}\\u{1FA80}-\\u{1FA88}\\u{1FA90}-\\u{1FABD}\\u{1FABF}-\\u{1FAC5}\\u{1FACE}-\\u{1FADB}\\u{1FAE0}-\\u{1FAE8}\\u{1FAF0}-\\u{1FAF8}"
-};
-
-const unicodeSetFixtures = [
- {
- pattern: '[[a-h]&&[f-z]]',
- expected: '[f-h]'
- },
- {
- pattern: '[[a-h]&&[f-z]&&[p-z]]',
- expected: '[]'
- },
- {
- pattern: '[[a-h]&&[b]]',
- expected: 'b'
- },
- {
- pattern: '[[a-h]&&b]',
- expected: 'b'
- },
- {
- pattern: '[[g-z]&&b]',
- expected: '[]'
- },
- {
- pattern: '[[a-h]&&[^f-z]]',
- expected: '[a-e]'
- },
- {
- pattern: '[[a-h]&&[^f-z]&&[p-z]]',
- expected: '[]'
- },
- {
- pattern: '[[a-h]&&[^f-z]&&[^p-z]]',
- expected: '[a-e]'
- },
- {
- pattern: '[[a-h]&&[^b]]',
- expected: '[ac-h]'
- },
- {
- pattern: '[[a-h]--[f-z]]',
- expected: '[a-e]'
- },
- {
- pattern: '[[a-h]--[f-z]--[p-z]]',
- expected: '[a-e]'
- },
- {
- pattern: '[[a-z]--[d-k]--[s-w]]',
- expected: '[a-cl-rx-z]'
- },
- {
- pattern: '[[a-h]--[b]]',
- expected: '[ac-h]'
- },
- {
- pattern: '[[b]--[a-h]]',
- expected: '[]'
- },
- {
- pattern: '[[a-h]--b]',
- expected: '[ac-h]'
- },
- {
- pattern: '[b--[a-h]]',
- expected: '[]'
- },
- {
- pattern: '[[g-z]--b]',
- expected: '[g-z]'
- },
- {
- pattern: '[b--[g-z]]',
- expected: 'b'
- },
- {
- pattern: '[[a-h]--[^f-z]]',
- expected: '[f-h]'
- },
- {
- pattern: '[[a-h]--[^f-z]--[p-z]]',
- expected: '[f-h]'
- },
- {
- pattern: '[[a-h]--[^f-z]--[^p-z]]',
- expected: '[]'
- },
- {
- pattern: '[[a-h]--[^b]]',
- expected: 'b'
- },
- {
- pattern: '[[a-z][f-h]]',
- expected: '[a-z]'
- },
- {
- pattern: '[^[a-z][f-h]]',
- expected: '[^a-z]'
- },
- {
- pattern: '[^[a-z][f-h]]',
- expected: '(?:(?![a-z])[\\s\\S])',
- options: TRANSFORM_U
- },
- {
- pattern: '[[^a-z][f-h]]',
- expected: '[\\0-`f-h\\{-\\u{10FFFF}]'
- },
- {
- pattern: '[[^a-z][f-h]]',
- expected: '(?:[\\0-`f-h\\{-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF])',
- options: TRANSFORM_U
- },
- {
- pattern: '[\\q{A|AB|B|ABC|BC}ab]',
- expected: '(?:ABC|AB|BC|[ABab])'
- },
- {
- pattern: '[\\q{A|AB}a\\q{B|ABC|BC}b]',
- expected: '(?:ABC|AB|BC|[ABab])'
- },
- {
- pattern: '[\\q{A|AB}ab\\q{B|ABC|BC}]',
- expected: '(?:ABC|AB|BC|[ABab])'
- },
- {
- pattern: '[[\\q{A|AB}a]b\\q{B|ABC|BC}]',
- expected: '(?:ABC|AB|BC|[ABab])'
- },
- {
- pattern: '[\\q{👩🏿✈️|🚲|🇧🇪}]',
- expected: '(?:👩🏿✈️|🇧🇪|\\u{1F6B2})'
- },
- {
- pattern: '[ab\\q{}]',
- expected: '(?:[ab]|)'
- },
- {
- pattern: '[ab\\q{|}]',
- expected: '(?:[ab]|)'
- },
- {
- pattern: '[ab\\q{|A|AB}]',
- expected: '(?:AB|[Aab]|)'
- },
- {
- pattern: '[\\q{sA}asb]',
- flags: 'iv',
- expected: '(?:sA|[abs])'
- },
- {
- pattern: '[\\q{sA}asb]',
- flags: 'iv',
- options: TRANSFORM_U,
- expected: '(?:[s\\u017F]A|[abs\\u017F])'
- },
- {
- pattern: '[[ab\\q{cd}]--a]',
- expected: '(?:cd|b)'
- },
- {
- pattern: '[[ab\\q{cd}]--[ab]]',
- expected: '(?:cd)'
- },
- {
- pattern: '[[ab\\q{cd}]--[cd]]',
- expected: '(?:cd|[ab])'
- },
- {
- pattern: '[[ab\\q{cd}]--\\q{cd}]',
- expected: '[ab]'
- },
- {
- pattern: '[[ab\\q{cd}]--[a\\q{cd}]]',
- expected: 'b'
- },
- {
- pattern: '[[ab\\q{cd}]--[ab\\q{cd}]]',
- expected: '[]'
- },
- {
- pattern: '[[ab]--[ab\\q{cd}]]',
- expected: '[]'
- },
- {
- pattern: '[\\q{cd}--[ab\\q{cd}]]',
- expected: '[]'
- },
- {
- pattern: '[\\q{cd}--[ab\\q{dc}]]',
- expected: '(?:cd)'
- },
- {
- pattern: '[\\q{ab|cd|abc}--\\q{abc|cd}]',
- expected: '(?:ab)'
- },
- {
- pattern: '[\\q{ab|cd|abc}--\\q{abc}--\\q{cd}]',
- expected: '(?:ab)'
- },
- {
- pattern: '[a&&\\q{a}]',
- expected: 'a'
- },
- {
- pattern: '[a&&\\q{ab}]',
- expected: '[]'
- },
- {
- pattern: '[\\q{ab}&&\\q{ab}]',
- expected: '(?:ab)'
- },
- {
- pattern: '[\\q{ab|cd|abc}&&\\q{ab|bc|abc}]',
- expected: '(?:abc|ab)'
- },
- {
- pattern: '[[a\\q{ab}]&&\\q{ab}]',
- expected: '(?:ab)'
- },
- {
- pattern: '[[a\\q{ab}]&&a]',
- expected: 'a'
- },
- {
- pattern: '[^\\q{a}]',
- expected: '[^a]'
- },
- {
- pattern: '[^\\q{abc}]',
- throws: /Cannot negate set containing strings/
- },
- {
- pattern: '[^\\q{}]',
- throws: /Cannot negate set containing strings/
- },
- {
- pattern: '[^\\q{a|}]',
- throws: /Cannot negate set containing strings/
- },
- {
- pattern: '[^\\q{}--\\q{}]',
- throws: /Cannot negate set containing strings/
- },
- {
- pattern: '[^\\q{ab}--\\q{ab}]',
- throws: /Cannot negate set containing strings/
- },
- {
- pattern: '[^\\q{a}--\\q{ab}]',
- expected: '[^a]'
- },
- {
- pattern: '[^[\\q{ab}--\\q{ab}]]',
- throws: /Cannot negate set containing strings/
- },
- {
- pattern: '[^\\q{}&&a]',
- expected: '[^]'
- },
- {
- pattern: '[^\\q{ab}&&a]',
- expected: '[^]'
- },
- {
- pattern: '[^\\q{}&&\\q{}]',
- throws: /Cannot negate set containing strings/
- },
- {
- pattern: '[^\\q{ab}&&\\q{ab}]',
- throws: /Cannot negate set containing strings/
- },
- {
- pattern: '[^\\q{}&&\\q{ab}]',
- throws: /Cannot negate set containing strings/
- },
- {
- pattern: '\\p{Basic_Emoji}',
- expected: `(?:${Basic_Emoji.all})`
- },
- {
- pattern: '[\\p{Basic_Emoji}]',
- expected: `(?:${Basic_Emoji.all})`
- },
- {
- pattern: '[\\p{Basic_Emoji}&&\\p{Basic_Emoji}]',
- expected: `(?:${Basic_Emoji.all})`
- },
- {
- pattern: '[\\p{Basic_Emoji}&&[\\u{0}-\\u{10ffff}]]',
- expected: `[${Basic_Emoji.chars}]`
- },
- {
- pattern: '[\\p{Basic_Emoji}\\p{Emoji}]',
- expected: `(?:${Basic_Emoji.strings}|[#\\*0-9\\xA9\\xAE\\u203C\\u2049\\u2122\\u2139\\u2194-\\u2199\\u21A9\\u21AA\\u231A\\u231B\\u2328\\u23CF\\u23E9-\\u23F3\\u23F8-\\u23FA\\u24C2\\u25AA\\u25AB\\u25B6\\u25C0\\u25FB-\\u25FE\\u2600-\\u2604\\u260E\\u2611\\u2614\\u2615\\u2618\\u261D\\u2620\\u2622\\u2623\\u2626\\u262A\\u262E\\u262F\\u2638-\\u263A\\u2640\\u2642\\u2648-\\u2653\\u265F\\u2660\\u2663\\u2665\\u2666\\u2668\\u267B\\u267E\\u267F\\u2692-\\u2697\\u2699\\u269B\\u269C\\u26A0\\u26A1\\u26A7\\u26AA\\u26AB\\u26B0\\u26B1\\u26BD\\u26BE\\u26C4\\u26C5\\u26C8\\u26CE\\u26CF\\u26D1\\u26D3\\u26D4\\u26E9\\u26EA\\u26F0-\\u26F5\\u26F7-\\u26FA\\u26FD\\u2702\\u2705\\u2708-\\u270D\\u270F\\u2712\\u2714\\u2716\\u271D\\u2721\\u2728\\u2733\\u2734\\u2744\\u2747\\u274C\\u274E\\u2753-\\u2755\\u2757\\u2763\\u2764\\u2795-\\u2797\\u27A1\\u27B0\\u27BF\\u2934\\u2935\\u2B05-\\u2B07\\u2B1B\\u2B1C\\u2B50\\u2B55\\u3030\\u303D\\u3297\\u3299\\u{1F004}\\u{1F0CF}\\u{1F170}\\u{1F171}\\u{1F17E}\\u{1F17F}\\u{1F18E}\\u{1F191}-\\u{1F19A}\\u{1F1E6}-\\u{1F1FF}\\u{1F201}\\u{1F202}\\u{1F21A}\\u{1F22F}\\u{1F232}-\\u{1F23A}\\u{1F250}\\u{1F251}\\u{1F300}-\\u{1F321}\\u{1F324}-\\u{1F393}\\u{1F396}\\u{1F397}\\u{1F399}-\\u{1F39B}\\u{1F39E}-\\u{1F3F0}\\u{1F3F3}-\\u{1F3F5}\\u{1F3F7}-\\u{1F4FD}\\u{1F4FF}-\\u{1F53D}\\u{1F549}-\\u{1F54E}\\u{1F550}-\\u{1F567}\\u{1F56F}\\u{1F570}\\u{1F573}-\\u{1F57A}\\u{1F587}\\u{1F58A}-\\u{1F58D}\\u{1F590}\\u{1F595}\\u{1F596}\\u{1F5A4}\\u{1F5A5}\\u{1F5A8}\\u{1F5B1}\\u{1F5B2}\\u{1F5BC}\\u{1F5C2}-\\u{1F5C4}\\u{1F5D1}-\\u{1F5D3}\\u{1F5DC}-\\u{1F5DE}\\u{1F5E1}\\u{1F5E3}\\u{1F5E8}\\u{1F5EF}\\u{1F5F3}\\u{1F5FA}-\\u{1F64F}\\u{1F680}-\\u{1F6C5}\\u{1F6CB}-\\u{1F6D2}\\u{1F6D5}-\\u{1F6D7}\\u{1F6DC}-\\u{1F6E5}\\u{1F6E9}\\u{1F6EB}\\u{1F6EC}\\u{1F6F0}\\u{1F6F3}-\\u{1F6FC}\\u{1F7E0}-\\u{1F7EB}\\u{1F7F0}\\u{1F90C}-\\u{1F93A}\\u{1F93C}-\\u{1F945}\\u{1F947}-\\u{1F9FF}\\u{1FA70}-\\u{1FA7C}\\u{1FA80}-\\u{1FA88}\\u{1FA90}-\\u{1FABD}\\u{1FABF}-\\u{1FAC5}\\u{1FACE}-\\u{1FADB}\\u{1FAE0}-\\u{1FAE8}\\u{1FAF0}-\\u{1FAF8}])`
- },
- {
- pattern: '[\\p{Basic_Emoji}&&\\q{🇮🇴|dog}]',
- expected: '[]'
- },
- {
- pattern: '[\\p{RGI_Emoji_Flag_Sequence}&&\\q{🇮🇴|dog}]',
- expected: '🇮🇴'
- },
- {
- pattern: '[\\p{Basic_Emoji}\\q{JavaScript|ECMAScript}]',
- expected: `(?:JavaScript|ECMAScript|${Basic_Emoji.all})`
- },
- {
- pattern: '[\\p{Basic_Emoji}&&\\q{😷|©️|dog}]',
- expected: '(?:©️|\\u{1F637})'
- },
- {
- pattern: '\\P{Basic_Emoji}',
- throws: /Cannot negate Unicode property of strings/
- },
- {
- pattern: '[^\\p{Basic_Emoji}]',
- throws: /Cannot negate set containing strings/
- },
- {
- pattern: '\\p{RGI_Emoji}',
- // keycaps like *️⃣ give problems
- expected: '(?:👨🏻❤️💋👨🏻|👨🏻❤️💋👨🏼|👨🏻❤️💋👨🏽|👨🏻❤️💋👨🏾|👨🏻❤️💋👨🏿|👨🏼❤️💋👨🏻|👨🏼❤️💋👨🏼|👨🏼❤️💋👨🏽|👨🏼❤️💋👨🏾|👨🏼❤️💋👨🏿|👨🏽❤️💋👨🏻|👨🏽❤️💋👨🏼|👨🏽❤️💋👨🏽|👨🏽❤️💋👨🏾|👨🏽❤️💋👨🏿|👨🏾❤️💋👨🏻|👨🏾❤️💋👨🏼|👨🏾❤️💋👨🏽|👨🏾❤️💋👨🏾|👨🏾❤️💋👨🏿|👨🏿❤️💋👨🏻|👨🏿❤️💋👨🏼|👨🏿❤️💋👨🏽|👨🏿❤️💋👨🏾|👨🏿❤️💋👨🏿|👩🏻❤️💋👨🏻|👩🏻❤️💋👨🏼|👩🏻❤️💋👨🏽|👩🏻❤️💋👨🏾|👩🏻❤️💋👨🏿|👩🏻❤️💋👩🏻|👩🏻❤️💋👩🏼|👩🏻❤️💋👩🏽|👩🏻❤️💋👩🏾|👩🏻❤️💋👩🏿|👩🏼❤️💋👨🏻|👩🏼❤️💋👨🏼|👩🏼❤️💋👨🏽|👩🏼❤️💋👨🏾|👩🏼❤️💋👨🏿|👩🏼❤️💋👩🏻|👩🏼❤️💋👩🏼|👩🏼❤️💋👩🏽|👩🏼❤️💋👩🏾|👩🏼❤️💋👩🏿|👩🏽❤️💋👨🏻|👩🏽❤️💋👨🏼|👩🏽❤️💋👨🏽|👩🏽❤️💋👨🏾|👩🏽❤️💋👨🏿|👩🏽❤️💋👩🏻|👩🏽❤️💋👩🏼|👩🏽❤️💋👩🏽|👩🏽❤️💋👩🏾|👩🏽❤️💋👩🏿|👩🏾❤️💋👨🏻|👩🏾❤️💋👨🏼|👩🏾❤️💋👨🏽|👩🏾❤️💋👨🏾|👩🏾❤️💋👨🏿|👩🏾❤️💋👩🏻|👩🏾❤️💋👩🏼|👩🏾❤️💋👩🏽|👩🏾❤️💋👩🏾|👩🏾❤️💋👩🏿|👩🏿❤️💋👨🏻|👩🏿❤️💋👨🏼|👩🏿❤️💋👨🏽|👩🏿❤️💋👨🏾|👩🏿❤️💋👨🏿|👩🏿❤️💋👩🏻|👩🏿❤️💋👩🏼|👩🏿❤️💋👩🏽|👩🏿❤️💋👩🏾|👩🏿❤️💋👩🏿|🧑🏻❤️💋🧑🏼|🧑🏻❤️💋🧑🏽|🧑🏻❤️💋🧑🏾|🧑🏻❤️💋🧑🏿|🧑🏼❤️💋🧑🏻|🧑🏼❤️💋🧑🏽|🧑🏼❤️💋🧑🏾|🧑🏼❤️💋🧑🏿|🧑🏽❤️💋🧑🏻|🧑🏽❤️💋🧑🏼|🧑🏽❤️💋🧑🏾|🧑🏽❤️💋🧑🏿|🧑🏾❤️💋🧑🏻|🧑🏾❤️💋🧑🏼|🧑🏾❤️💋🧑🏽|🧑🏾❤️💋🧑🏿|🧑🏿❤️💋🧑🏻|🧑🏿❤️💋🧑🏼|🧑🏿❤️💋🧑🏽|🧑🏿❤️💋🧑🏾|🏴|🏴|🏴|👨🏻❤️👨🏻|👨🏻❤️👨🏼|👨🏻❤️👨🏽|👨🏻❤️👨🏾|👨🏻❤️👨🏿|👨🏻🤝👨🏼|👨🏻🤝👨🏽|👨🏻🤝👨🏾|👨🏻🤝👨🏿|👨🏼❤️👨🏻|👨🏼❤️👨🏼|👨🏼❤️👨🏽|👨🏼❤️👨🏾|👨🏼❤️👨🏿|👨🏼🤝👨🏻|👨🏼🤝👨🏽|👨🏼🤝👨🏾|👨🏼🤝👨🏿|👨🏽❤️👨🏻|👨🏽❤️👨🏼|👨🏽❤️👨🏽|👨🏽❤️👨🏾|👨🏽❤️👨🏿|👨🏽🤝👨🏻|👨🏽🤝👨🏼|👨🏽🤝👨🏾|👨🏽🤝👨🏿|👨🏾❤️👨🏻|👨🏾❤️👨🏼|👨🏾❤️👨🏽|👨🏾❤️👨🏾|👨🏾❤️👨🏿|👨🏾🤝👨🏻|👨🏾🤝👨🏼|👨🏾🤝👨🏽|👨🏾🤝👨🏿|👨🏿❤️👨🏻|👨🏿❤️👨🏼|👨🏿❤️👨🏽|👨🏿❤️👨🏾|👨🏿❤️👨🏿|👨🏿🤝👨🏻|👨🏿🤝👨🏼|👨🏿🤝👨🏽|👨🏿🤝👨🏾|👩🏻❤️👨🏻|👩🏻❤️👨🏼|👩🏻❤️👨🏽|👩🏻❤️👨🏾|👩🏻❤️👨🏿|👩🏻❤️👩🏻|👩🏻❤️👩🏼|👩🏻❤️👩🏽|👩🏻❤️👩🏾|👩🏻❤️👩🏿|👩🏻🤝👨🏼|👩🏻🤝👨🏽|👩🏻🤝👨🏾|👩🏻🤝👨🏿|👩🏻🤝👩🏼|👩🏻🤝👩🏽|👩🏻🤝👩🏾|👩🏻🤝👩🏿|👩🏼❤️👨🏻|👩🏼❤️👨🏼|👩🏼❤️👨🏽|👩🏼❤️👨🏾|👩🏼❤️👨🏿|👩🏼❤️👩🏻|👩🏼❤️👩🏼|👩🏼❤️👩🏽|👩🏼❤️👩🏾|👩🏼❤️👩🏿|👩🏼🤝👨🏻|👩🏼🤝👨🏽|👩🏼🤝👨🏾|👩🏼🤝👨🏿|👩🏼🤝👩🏻|👩🏼🤝👩🏽|👩🏼🤝👩🏾|👩🏼🤝👩🏿|👩🏽❤️👨🏻|👩🏽❤️👨🏼|👩🏽❤️👨🏽|👩🏽❤️👨🏾|👩🏽❤️👨🏿|👩🏽❤️👩🏻|👩🏽❤️👩🏼|👩🏽❤️👩🏽|👩🏽❤️👩🏾|👩🏽❤️👩🏿|👩🏽🤝👨🏻|👩🏽🤝👨🏼|👩🏽🤝👨🏾|👩🏽🤝👨🏿|👩🏽🤝👩🏻|👩🏽🤝👩🏼|👩🏽🤝👩🏾|👩🏽🤝👩🏿|👩🏾❤️👨🏻|👩🏾❤️👨🏼|👩🏾❤️👨🏽|👩🏾❤️👨🏾|👩🏾❤️👨🏿|👩🏾❤️👩🏻|👩🏾❤️👩🏼|👩🏾❤️👩🏽|👩🏾❤️👩🏾|👩🏾❤️👩🏿|👩🏾🤝👨🏻|👩🏾🤝👨🏼|👩🏾🤝👨🏽|👩🏾🤝👨🏿|👩🏾🤝👩🏻|👩🏾🤝👩🏼|👩🏾🤝👩🏽|👩🏾🤝👩🏿|👩🏿❤️👨🏻|👩🏿❤️👨🏼|👩🏿❤️👨🏽|👩🏿❤️👨🏾|👩🏿❤️👨🏿|👩🏿❤️👩🏻|👩🏿❤️👩🏼|👩🏿❤️👩🏽|👩🏿❤️👩🏾|👩🏿❤️👩🏿|👩🏿🤝👨🏻|👩🏿🤝👨🏼|👩🏿🤝👨🏽|👩🏿🤝👨🏾|👩🏿🤝👩🏻|👩🏿🤝👩🏼|👩🏿🤝👩🏽|👩🏿🤝👩🏾|🧑🏻❤️🧑🏼|🧑🏻❤️🧑🏽|🧑🏻❤️🧑🏾|🧑🏻❤️🧑🏿|🧑🏻🤝🧑🏻|🧑🏻🤝🧑🏼|🧑🏻🤝🧑🏽|🧑🏻🤝🧑🏾|🧑🏻🤝🧑🏿|🧑🏼❤️🧑🏻|🧑🏼❤️🧑🏽|🧑🏼❤️🧑🏾|🧑🏼❤️🧑🏿|🧑🏼🤝🧑🏻|🧑🏼🤝🧑🏼|🧑🏼🤝🧑🏽|🧑🏼🤝🧑🏾|🧑🏼🤝🧑🏿|🧑🏽❤️🧑🏻|🧑🏽❤️🧑🏼|🧑🏽❤️🧑🏾|🧑🏽❤️🧑🏿|🧑🏽🤝🧑🏻|🧑🏽🤝🧑🏼|🧑🏽🤝🧑🏽|🧑🏽🤝🧑🏾|🧑🏽🤝🧑🏿|🧑🏾❤️🧑🏻|🧑🏾❤️🧑🏼|🧑🏾❤️🧑🏽|🧑🏾❤️🧑🏿|🧑🏾🤝🧑🏻|🧑🏾🤝🧑🏼|🧑🏾🤝🧑🏽|🧑🏾🤝🧑🏾|🧑🏾🤝🧑🏿|🧑🏿❤️🧑🏻|🧑🏿❤️🧑🏼|🧑🏿❤️🧑🏽|🧑🏿❤️🧑🏾|🧑🏿🤝🧑🏻|🧑🏿🤝🧑🏼|🧑🏿🤝🧑🏽|🧑🏿🤝🧑🏾|🧑🏿🤝🧑🏿|👨❤️💋👨|👨👨👦👦|👨👨👧👦|👨👨👧👧|👨👩👦👦|👨👩👧👦|👨👩👧👧|👩❤️💋👨|👩❤️💋👩|👩👩👦👦|👩👩👧👦|👩👩👧👧|🫱🏻🫲🏼|🫱🏻🫲🏽|🫱🏻🫲🏾|🫱🏻🫲🏿|🫱🏼🫲🏻|🫱🏼🫲🏽|🫱🏼🫲🏾|🫱🏼🫲🏿|🫱🏽🫲🏻|🫱🏽🫲🏼|🫱🏽🫲🏾|🫱🏽🫲🏿|🫱🏾🫲🏻|🫱🏾🫲🏼|🫱🏾🫲🏽|🫱🏾🫲🏿|🫱🏿🫲🏻|🫱🏿🫲🏼|🫱🏿🫲🏽|🫱🏿🫲🏾|👨❤️👨|👨👦👦|👨👧👦|👨👧👧|👨👨👦|👨👨👧|👨👩👦|👨👩👧|👩❤️👨|👩❤️👩|👩👦👦|👩👧👦|👩👧👧|👩👩👦|👩👩👧|🧑🤝🧑|🏃🏻♀️|🏃🏻♂️|🏃🏼♀️|🏃🏼♂️|🏃🏽♀️|🏃🏽♂️|🏃🏾♀️|🏃🏾♂️|🏃🏿♀️|🏃🏿♂️|🏄🏻♀️|🏄🏻♂️|🏄🏼♀️|🏄🏼♂️|🏄🏽♀️|🏄🏽♂️|🏄🏾♀️|🏄🏾♂️|🏄🏿♀️|🏄🏿♂️|🏊🏻♀️|🏊🏻♂️|🏊🏼♀️|🏊🏼♂️|🏊🏽♀️|🏊🏽♂️|🏊🏾♀️|🏊🏾♂️|🏊🏿♀️|🏊🏿♂️|🏋🏻♀️|🏋🏻♂️|🏋🏼♀️|🏋🏼♂️|🏋🏽♀️|🏋🏽♂️|🏋🏾♀️|🏋🏾♂️|🏋🏿♀️|🏋🏿♂️|🏌🏻♀️|🏌🏻♂️|🏌🏼♀️|🏌🏼♂️|🏌🏽♀️|🏌🏽♂️|🏌🏾♀️|🏌🏾♂️|🏌🏿♀️|🏌🏿♂️|👁️🗨️|👨🏻⚕️|👨🏻⚖️|👨🏻✈️|👨🏻🌾|👨🏻🍳|👨🏻🍼|👨🏻🎓|👨🏻🎤|👨🏻🎨|👨🏻🏫|👨🏻🏭|👨🏻💻|👨🏻💼|👨🏻🔧|👨🏻🔬|👨🏻🚀|👨🏻🚒|👨🏻🦯|👨🏻🦰|👨🏻🦱|👨🏻🦲|👨🏻🦳|👨🏻🦼|👨🏻🦽|👨🏼⚕️|👨🏼⚖️|👨🏼✈️|👨🏼🌾|👨🏼🍳|👨🏼🍼|👨🏼🎓|👨🏼🎤|👨🏼🎨|👨🏼🏫|👨🏼🏭|👨🏼💻|👨🏼💼|👨🏼🔧|👨🏼🔬|👨🏼🚀|👨🏼🚒|👨🏼🦯|👨🏼🦰|👨🏼🦱|👨🏼🦲|👨🏼🦳|👨🏼🦼|👨🏼🦽|👨🏽⚕️|👨🏽⚖️|👨🏽✈️|👨🏽🌾|👨🏽🍳|👨🏽🍼|👨🏽🎓|👨🏽🎤|👨🏽🎨|👨🏽🏫|👨🏽🏭|👨🏽💻|👨🏽💼|👨🏽🔧|👨🏽🔬|👨🏽🚀|👨🏽🚒|👨🏽🦯|👨🏽🦰|👨🏽🦱|👨🏽🦲|👨🏽🦳|👨🏽🦼|👨🏽🦽|👨🏾⚕️|👨🏾⚖️|👨🏾✈️|👨🏾🌾|👨🏾🍳|👨🏾🍼|👨🏾🎓|👨🏾🎤|👨🏾🎨|👨🏾🏫|👨🏾🏭|👨🏾💻|👨🏾💼|👨🏾🔧|👨🏾🔬|👨🏾🚀|👨🏾🚒|👨🏾🦯|👨🏾🦰|👨🏾🦱|👨🏾🦲|👨🏾🦳|👨🏾🦼|👨🏾🦽|👨🏿⚕️|👨🏿⚖️|👨🏿✈️|👨🏿🌾|👨🏿🍳|👨🏿🍼|👨🏿🎓|👨🏿🎤|👨🏿🎨|👨🏿🏫|👨🏿🏭|👨🏿💻|👨🏿💼|👨🏿🔧|👨🏿🔬|👨🏿🚀|👨🏿🚒|👨🏿🦯|👨🏿🦰|👨🏿🦱|👨🏿🦲|👨🏿🦳|👨🏿🦼|👨🏿🦽|👩🏻⚕️|👩🏻⚖️|👩🏻✈️|👩🏻🌾|👩🏻🍳|👩🏻🍼|👩🏻🎓|👩🏻🎤|👩🏻🎨|👩🏻🏫|👩🏻🏭|👩🏻💻|👩🏻💼|👩🏻🔧|👩🏻🔬|👩🏻🚀|👩🏻🚒|👩🏻🦯|👩🏻🦰|👩🏻🦱|👩🏻🦲|👩🏻🦳|👩🏻🦼|👩🏻🦽|👩🏼⚕️|👩🏼⚖️|👩🏼✈️|👩🏼🌾|👩🏼🍳|👩🏼🍼|👩🏼🎓|👩🏼🎤|👩🏼🎨|👩🏼🏫|👩🏼🏭|👩🏼💻|👩🏼💼|👩🏼🔧|👩🏼🔬|👩🏼🚀|👩🏼🚒|👩🏼🦯|👩🏼🦰|👩🏼🦱|👩🏼🦲|👩🏼🦳|👩🏼🦼|👩🏼🦽|👩🏽⚕️|👩🏽⚖️|👩🏽✈️|👩🏽🌾|👩🏽🍳|👩🏽🍼|👩🏽🎓|👩🏽🎤|👩🏽🎨|👩🏽🏫|👩🏽🏭|👩🏽💻|👩🏽💼|👩🏽🔧|👩🏽🔬|👩🏽🚀|👩🏽🚒|👩🏽🦯|👩🏽🦰|👩🏽🦱|👩🏽🦲|👩🏽🦳|👩🏽🦼|👩🏽🦽|👩🏾⚕️|👩🏾⚖️|👩🏾✈️|👩🏾🌾|👩🏾🍳|👩🏾🍼|👩🏾🎓|👩🏾🎤|👩🏾🎨|👩🏾🏫|👩🏾🏭|👩🏾💻|👩🏾💼|👩🏾🔧|👩🏾🔬|👩🏾🚀|👩🏾🚒|👩🏾🦯|👩🏾🦰|👩🏾🦱|👩🏾🦲|👩🏾🦳|👩🏾🦼|👩🏾🦽|👩🏿⚕️|👩🏿⚖️|👩🏿✈️|👩🏿🌾|👩🏿🍳|👩🏿🍼|👩🏿🎓|👩🏿🎤|👩🏿🎨|👩🏿🏫|👩🏿🏭|👩🏿💻|👩🏿💼|👩🏿🔧|👩🏿🔬|👩🏿🚀|👩🏿🚒|👩🏿🦯|👩🏿🦰|👩🏿🦱|👩🏿🦲|👩🏿🦳|👩🏿🦼|👩🏿🦽|👮🏻♀️|👮🏻♂️|👮🏼♀️|👮🏼♂️|👮🏽♀️|👮🏽♂️|👮🏾♀️|👮🏾♂️|👮🏿♀️|👮🏿♂️|👰🏻♀️|👰🏻♂️|👰🏼♀️|👰🏼♂️|👰🏽♀️|👰🏽♂️|👰🏾♀️|👰🏾♂️|👰🏿♀️|👰🏿♂️|👱🏻♀️|👱🏻♂️|👱🏼♀️|👱🏼♂️|👱🏽♀️|👱🏽♂️|👱🏾♀️|👱🏾♂️|👱🏿♀️|👱🏿♂️|👳🏻♀️|👳🏻♂️|👳🏼♀️|👳🏼♂️|👳🏽♀️|👳🏽♂️|👳🏾♀️|👳🏾♂️|👳🏿♀️|👳🏿♂️|👷🏻♀️|👷🏻♂️|👷🏼♀️|👷🏼♂️|👷🏽♀️|👷🏽♂️|👷🏾♀️|👷🏾♂️|👷🏿♀️|👷🏿♂️|💁🏻♀️|💁🏻♂️|💁🏼♀️|💁🏼♂️|💁🏽♀️|💁🏽♂️|💁🏾♀️|💁🏾♂️|💁🏿♀️|💁🏿♂️|💂🏻♀️|💂🏻♂️|💂🏼♀️|💂🏼♂️|💂🏽♀️|💂🏽♂️|💂🏾♀️|💂🏾♂️|💂🏿♀️|💂🏿♂️|💆🏻♀️|💆🏻♂️|💆🏼♀️|💆🏼♂️|💆🏽♀️|💆🏽♂️|💆🏾♀️|💆🏾♂️|💆🏿♀️|💆🏿♂️|💇🏻♀️|💇🏻♂️|💇🏼♀️|💇🏼♂️|💇🏽♀️|💇🏽♂️|💇🏾♀️|💇🏾♂️|💇🏿♀️|💇🏿♂️|🕵🏻♀️|🕵🏻♂️|🕵🏼♀️|🕵🏼♂️|🕵🏽♀️|🕵🏽♂️|🕵🏾♀️|🕵🏾♂️|🕵🏿♀️|🕵🏿♂️|🙅🏻♀️|🙅🏻♂️|🙅🏼♀️|🙅🏼♂️|🙅🏽♀️|🙅🏽♂️|🙅🏾♀️|🙅🏾♂️|🙅🏿♀️|🙅🏿♂️|🙆🏻♀️|🙆🏻♂️|🙆🏼♀️|🙆🏼♂️|🙆🏽♀️|🙆🏽♂️|🙆🏾♀️|🙆🏾♂️|🙆🏿♀️|🙆🏿♂️|🙇🏻♀️|🙇🏻♂️|🙇🏼♀️|🙇🏼♂️|🙇🏽♀️|🙇🏽♂️|🙇🏾♀️|🙇🏾♂️|🙇🏿♀️|🙇🏿♂️|🙋🏻♀️|🙋🏻♂️|🙋🏼♀️|🙋🏼♂️|🙋🏽♀️|🙋🏽♂️|🙋🏾♀️|🙋🏾♂️|🙋🏿♀️|🙋🏿♂️|🙍🏻♀️|🙍🏻♂️|🙍🏼♀️|🙍🏼♂️|🙍🏽♀️|🙍🏽♂️|🙍🏾♀️|🙍🏾♂️|🙍🏿♀️|🙍🏿♂️|🙎🏻♀️|🙎🏻♂️|🙎🏼♀️|🙎🏼♂️|🙎🏽♀️|🙎🏽♂️|🙎🏾♀️|🙎🏾♂️|🙎🏿♀️|🙎🏿♂️|🚣🏻♀️|🚣🏻♂️|🚣🏼♀️|🚣🏼♂️|🚣🏽♀️|🚣🏽♂️|🚣🏾♀️|🚣🏾♂️|🚣🏿♀️|🚣🏿♂️|🚴🏻♀️|🚴🏻♂️|🚴🏼♀️|🚴🏼♂️|🚴🏽♀️|🚴🏽♂️|🚴🏾♀️|🚴🏾♂️|🚴🏿♀️|🚴🏿♂️|🚵🏻♀️|🚵🏻♂️|🚵🏼♀️|🚵🏼♂️|🚵🏽♀️|🚵🏽♂️|🚵🏾♀️|🚵🏾♂️|🚵🏿♀️|🚵🏿♂️|🚶🏻♀️|🚶🏻♂️|🚶🏼♀️|🚶🏼♂️|🚶🏽♀️|🚶🏽♂️|🚶🏾♀️|🚶🏾♂️|🚶🏿♀️|🚶🏿♂️|🤦🏻♀️|🤦🏻♂️|🤦🏼♀️|🤦🏼♂️|🤦🏽♀️|🤦🏽♂️|🤦🏾♀️|🤦🏾♂️|🤦🏿♀️|🤦🏿♂️|🤵🏻♀️|🤵🏻♂️|🤵🏼♀️|🤵🏼♂️|🤵🏽♀️|🤵🏽♂️|🤵🏾♀️|🤵🏾♂️|🤵🏿♀️|🤵🏿♂️|🤷🏻♀️|🤷🏻♂️|🤷🏼♀️|🤷🏼♂️|🤷🏽♀️|🤷🏽♂️|🤷🏾♀️|🤷🏾♂️|🤷🏿♀️|🤷🏿♂️|🤸🏻♀️|🤸🏻♂️|🤸🏼♀️|🤸🏼♂️|🤸🏽♀️|🤸🏽♂️|🤸🏾♀️|🤸🏾♂️|🤸🏿♀️|🤸🏿♂️|🤹🏻♀️|🤹🏻♂️|🤹🏼♀️|🤹🏼♂️|🤹🏽♀️|🤹🏽♂️|🤹🏾♀️|🤹🏾♂️|🤹🏿♀️|🤹🏿♂️|🤽🏻♀️|🤽🏻♂️|🤽🏼♀️|🤽🏼♂️|🤽🏽♀️|🤽🏽♂️|🤽🏾♀️|🤽🏾♂️|🤽🏿♀️|🤽🏿♂️|🤾🏻♀️|🤾🏻♂️|🤾🏼♀️|🤾🏼♂️|🤾🏽♀️|🤾🏽♂️|🤾🏾♀️|🤾🏾♂️|🤾🏿♀️|🤾🏿♂️|🦸🏻♀️|🦸🏻♂️|🦸🏼♀️|🦸🏼♂️|🦸🏽♀️|🦸🏽♂️|🦸🏾♀️|🦸🏾♂️|🦸🏿♀️|🦸🏿♂️|🦹🏻♀️|🦹🏻♂️|🦹🏼♀️|🦹🏼♂️|🦹🏽♀️|🦹🏽♂️|🦹🏾♀️|🦹🏾♂️|🦹🏿♀️|🦹🏿♂️|🧍🏻♀️|🧍🏻♂️|🧍🏼♀️|🧍🏼♂️|🧍🏽♀️|🧍🏽♂️|🧍🏾♀️|🧍🏾♂️|🧍🏿♀️|🧍🏿♂️|🧎🏻♀️|🧎🏻♂️|🧎🏼♀️|🧎🏼♂️|🧎🏽♀️|🧎🏽♂️|🧎🏾♀️|🧎🏾♂️|🧎🏿♀️|🧎🏿♂️|🧏🏻♀️|🧏🏻♂️|🧏🏼♀️|🧏🏼♂️|🧏🏽♀️|🧏🏽♂️|🧏🏾♀️|🧏🏾♂️|🧏🏿♀️|🧏🏿♂️|🧑🏻⚕️|🧑🏻⚖️|🧑🏻✈️|🧑🏻🌾|🧑🏻🍳|🧑🏻🍼|🧑🏻🎄|🧑🏻🎓|🧑🏻🎤|🧑🏻🎨|🧑🏻🏫|🧑🏻🏭|🧑🏻💻|🧑🏻💼|🧑🏻🔧|🧑🏻🔬|🧑🏻🚀|🧑🏻🚒|🧑🏻🦯|🧑🏻🦰|🧑🏻🦱|🧑🏻🦲|🧑🏻🦳|🧑🏻🦼|🧑🏻🦽|🧑🏼⚕️|🧑🏼⚖️|🧑🏼✈️|🧑🏼🌾|🧑🏼🍳|🧑🏼🍼|🧑🏼🎄|🧑🏼🎓|🧑🏼🎤|🧑🏼🎨|🧑🏼🏫|🧑🏼🏭|🧑🏼💻|🧑🏼💼|🧑🏼🔧|🧑🏼🔬|🧑🏼🚀|🧑🏼🚒|🧑🏼🦯|🧑🏼🦰|🧑🏼🦱|🧑🏼🦲|🧑🏼🦳|🧑🏼🦼|🧑🏼🦽|🧑🏽⚕️|🧑🏽⚖️|🧑🏽✈️|🧑🏽🌾|🧑🏽🍳|🧑🏽🍼|🧑🏽🎄|🧑🏽🎓|🧑🏽🎤|🧑🏽🎨|🧑🏽🏫|🧑🏽🏭|🧑🏽💻|🧑🏽💼|🧑🏽🔧|🧑🏽🔬|🧑🏽🚀|🧑🏽🚒|🧑🏽🦯|🧑🏽🦰|🧑🏽🦱|🧑🏽🦲|🧑🏽🦳|🧑🏽🦼|🧑🏽🦽|🧑🏾⚕️|🧑🏾⚖️|🧑🏾✈️|🧑🏾🌾|🧑🏾🍳|🧑🏾🍼|🧑🏾🎄|🧑🏾🎓|🧑🏾🎤|🧑🏾🎨|🧑🏾🏫|🧑🏾🏭|🧑🏾💻|🧑🏾💼|🧑🏾🔧|🧑🏾🔬|🧑🏾🚀|🧑🏾🚒|🧑🏾🦯|🧑🏾🦰|🧑🏾🦱|🧑🏾🦲|🧑🏾🦳|🧑🏾🦼|🧑🏾🦽|🧑🏿⚕️|🧑🏿⚖️|🧑🏿✈️|🧑🏿🌾|🧑🏿🍳|🧑🏿🍼|🧑🏿🎄|🧑🏿🎓|🧑🏿🎤|🧑🏿🎨|🧑🏿🏫|🧑🏿🏭|🧑🏿💻|🧑🏿💼|🧑🏿🔧|🧑🏿🔬|🧑🏿🚀|🧑🏿🚒|🧑🏿🦯|🧑🏿🦰|🧑🏿🦱|🧑🏿🦲|🧑🏿🦳|🧑🏿🦼|🧑🏿🦽|🧔🏻♀️|🧔🏻♂️|🧔🏼♀️|🧔🏼♂️|🧔🏽♀️|🧔🏽♂️|🧔🏾♀️|🧔🏾♂️|🧔🏿♀️|🧔🏿♂️|🧖🏻♀️|🧖🏻♂️|🧖🏼♀️|🧖🏼♂️|🧖🏽♀️|🧖🏽♂️|🧖🏾♀️|🧖🏾♂️|🧖🏿♀️|🧖🏿♂️|🧗🏻♀️|🧗🏻♂️|🧗🏼♀️|🧗🏼♂️|🧗🏽♀️|🧗🏽♂️|🧗🏾♀️|🧗🏾♂️|🧗🏿♀️|🧗🏿♂️|🧘🏻♀️|🧘🏻♂️|🧘🏼♀️|🧘🏼♂️|🧘🏽♀️|🧘🏽♂️|🧘🏾♀️|🧘🏾♂️|🧘🏿♀️|🧘🏿♂️|🧙🏻♀️|🧙🏻♂️|🧙🏼♀️|🧙🏼♂️|🧙🏽♀️|🧙🏽♂️|🧙🏾♀️|🧙🏾♂️|🧙🏿♀️|🧙🏿♂️|🧚🏻♀️|🧚🏻♂️|🧚🏼♀️|🧚🏼♂️|🧚🏽♀️|🧚🏽♂️|🧚🏾♀️|🧚🏾♂️|🧚🏿♀️|🧚🏿♂️|🧛🏻♀️|🧛🏻♂️|🧛🏼♀️|🧛🏼♂️|🧛🏽♀️|🧛🏽♂️|🧛🏾♀️|🧛🏾♂️|🧛🏿♀️|🧛🏿♂️|🧜🏻♀️|🧜🏻♂️|🧜🏼♀️|🧜🏼♂️|🧜🏽♀️|🧜🏽♂️|🧜🏾♀️|🧜🏾♂️|🧜🏿♀️|🧜🏿♂️|🧝🏻♀️|🧝🏻♂️|🧝🏼♀️|🧝🏼♂️|🧝🏽♀️|🧝🏽♂️|🧝🏾♀️|🧝🏾♂️|🧝🏿♀️|🧝🏿♂️|⛹🏻♀️|⛹🏻♂️|⛹🏼♀️|⛹🏼♂️|⛹🏽♀️|⛹🏽♂️|⛹🏾♀️|⛹🏾♂️|⛹🏿♀️|⛹🏿♂️|🏋️♀️|🏋️♂️|🏌️♀️|🏌️♂️|🏳️⚧️|🏳️🌈|🕵️♀️|🕵️♂️|😶🌫️|⛹️♀️|⛹️♂️|❤️🔥|❤️🩹|🏃♀️|🏃♂️|🏄♀️|🏄♂️|🏊♀️|🏊♂️|🏴☠️|🐕🦺|🐻❄️|👨⚕️|👨⚖️|👨✈️|👨🌾|👨🍳|👨🍼|👨🎓|👨🎤|👨🎨|👨🏫|👨🏭|👨👦|👨👧|👨💻|👨💼|👨🔧|👨🔬|👨🚀|👨🚒|👨🦯|👨🦰|👨🦱|👨🦲|👨🦳|👨🦼|👨🦽|👩⚕️|👩⚖️|👩✈️|👩🌾|👩🍳|👩🍼|👩🎓|👩🎤|👩🎨|👩🏫|👩🏭|👩👦|👩👧|👩💻|👩💼|👩🔧|👩🔬|👩🚀|👩🚒|👩🦯|👩🦰|👩🦱|👩🦲|👩🦳|👩🦼|👩🦽|👮♀️|👮♂️|👯♀️|👯♂️|👰♀️|👰♂️|👱♀️|👱♂️|👳♀️|👳♂️|👷♀️|👷♂️|💁♀️|💁♂️|💂♀️|💂♂️|💆♀️|💆♂️|💇♀️|💇♂️|😮💨|😵💫|🙅♀️|🙅♂️|🙆♀️|🙆♂️|🙇♀️|🙇♂️|🙋♀️|🙋♂️|🙍♀️|🙍♂️|🙎♀️|🙎♂️|🚣♀️|🚣♂️|🚴♀️|🚴♂️|🚵♀️|🚵♂️|🚶♀️|🚶♂️|🤦♀️|🤦♂️|🤵♀️|🤵♂️|🤷♀️|🤷♂️|🤸♀️|🤸♂️|🤹♀️|🤹♂️|🤼♀️|🤼♂️|🤽♀️|🤽♂️|🤾♀️|🤾♂️|🦸♀️|🦸♂️|🦹♀️|🦹♂️|🧍♀️|🧍♂️|🧎♀️|🧎♂️|🧏♀️|🧏♂️|🧑⚕️|🧑⚖️|🧑✈️|🧑🌾|🧑🍳|🧑🍼|🧑🎄|🧑🎓|🧑🎤|🧑🎨|🧑🏫|🧑🏭|🧑💻|🧑💼|🧑🔧|🧑🔬|🧑🚀|🧑🚒|🧑🦯|🧑🦰|🧑🦱|🧑🦲|🧑🦳|🧑🦼|🧑🦽|🧔♀️|🧔♂️|🧖♀️|🧖♂️|🧗♀️|🧗♂️|🧘♀️|🧘♂️|🧙♀️|🧙♂️|🧚♀️|🧚♂️|🧛♀️|🧛♂️|🧜♀️|🧜♂️|🧝♀️|🧝♂️|🧞♀️|🧞♂️|🧟♀️|🧟♂️|\\*️⃣|🇦🇨|🇦🇩|🇦🇪|🇦🇫|🇦🇬|🇦🇮|🇦🇱|🇦🇲|🇦🇴|🇦🇶|🇦🇷|🇦🇸|🇦🇹|🇦🇺|🇦🇼|🇦🇽|🇦🇿|🇧🇦|🇧🇧|🇧🇩|🇧🇪|🇧🇫|🇧🇬|🇧🇭|🇧🇮|🇧🇯|🇧🇱|🇧🇲|🇧🇳|🇧🇴|🇧🇶|🇧🇷|🇧🇸|🇧🇹|🇧🇻|🇧🇼|🇧🇾|🇧🇿|🇨🇦|🇨🇨|🇨🇩|🇨🇫|🇨🇬|🇨🇭|🇨🇮|🇨🇰|🇨🇱|🇨🇲|🇨🇳|🇨🇴|🇨🇵|🇨🇷|🇨🇺|🇨🇻|🇨🇼|🇨🇽|🇨🇾|🇨🇿|🇩🇪|🇩🇬|🇩🇯|🇩🇰|🇩🇲|🇩🇴|🇩🇿|🇪🇦|🇪🇨|🇪🇪|🇪🇬|🇪🇭|🇪🇷|🇪🇸|🇪🇹|🇪🇺|🇫🇮|🇫🇯|🇫🇰|🇫🇲|🇫🇴|🇫🇷|🇬🇦|🇬🇧|🇬🇩|🇬🇪|🇬🇫|🇬🇬|🇬🇭|🇬🇮|🇬🇱|🇬🇲|🇬🇳|🇬🇵|🇬🇶|🇬🇷|🇬🇸|🇬🇹|🇬🇺|🇬🇼|🇬🇾|🇭🇰|🇭🇲|🇭🇳|🇭🇷|🇭🇹|🇭🇺|🇮🇨|🇮🇩|🇮🇪|🇮🇱|🇮🇲|🇮🇳|🇮🇴|🇮🇶|🇮🇷|🇮🇸|🇮🇹|🇯🇪|🇯🇲|🇯🇴|🇯🇵|🇰🇪|🇰🇬|🇰🇭|🇰🇮|🇰🇲|🇰🇳|🇰🇵|🇰🇷|🇰🇼|🇰🇾|🇰🇿|🇱🇦|🇱🇧|🇱🇨|🇱🇮|🇱🇰|🇱🇷|🇱🇸|🇱🇹|🇱🇺|🇱🇻|🇱🇾|🇲🇦|🇲🇨|🇲🇩|🇲🇪|🇲🇫|🇲🇬|🇲🇭|🇲🇰|🇲🇱|🇲🇲|🇲🇳|🇲🇴|🇲🇵|🇲🇶|🇲🇷|🇲🇸|🇲🇹|🇲🇺|🇲🇻|🇲🇼|🇲🇽|🇲🇾|🇲🇿|🇳🇦|🇳🇨|🇳🇪|🇳🇫|🇳🇬|🇳🇮|🇳🇱|🇳🇴|🇳🇵|🇳🇷|🇳🇺|🇳🇿|🇴🇲|🇵🇦|🇵🇪|🇵🇫|🇵🇬|🇵🇭|🇵🇰|🇵🇱|🇵🇲|🇵🇳|🇵🇷|🇵🇸|🇵🇹|🇵🇼|🇵🇾|🇶🇦|🇷🇪|🇷🇴|🇷🇸|🇷🇺|🇷🇼|🇸🇦|🇸🇧|🇸🇨|🇸🇩|🇸🇪|🇸🇬|🇸🇭|🇸🇮|🇸🇯|🇸🇰|🇸🇱|🇸🇲|🇸🇳|🇸🇴|🇸🇷|🇸🇸|🇸🇹|🇸🇻|🇸🇽|🇸🇾|🇸🇿|🇹🇦|🇹🇨|🇹🇩|🇹🇫|🇹🇬|🇹🇭|🇹🇯|🇹🇰|🇹🇱|🇹🇲|🇹🇳|🇹🇴|🇹🇷|🇹🇹|🇹🇻|🇹🇼|🇹🇿|🇺🇦|🇺🇬|🇺🇲|🇺🇳|🇺🇸|🇺🇾|🇺🇿|🇻🇦|🇻🇨|🇻🇪|🇻🇬|🇻🇮|🇻🇳|🇻🇺|🇼🇫|🇼🇸|🇽🇰|🇾🇪|🇾🇹|🇿🇦|🇿🇲|🇿🇼|🎅🏻|🎅🏼|🎅🏽|🎅🏾|🎅🏿|🏂🏻|🏂🏼|🏂🏽|🏂🏾|🏂🏿|🏃🏻|🏃🏼|🏃🏽|🏃🏾|🏃🏿|🏄🏻|🏄🏼|🏄🏽|🏄🏾|🏄🏿|🏇🏻|🏇🏼|🏇🏽|🏇🏾|🏇🏿|🏊🏻|🏊🏼|🏊🏽|🏊🏾|🏊🏿|🏋🏻|🏋🏼|🏋🏽|🏋🏾|🏋🏿|🏌🏻|🏌🏼|🏌🏽|🏌🏾|🏌🏿|🐈⬛|🐦⬛|👂🏻|👂🏼|👂🏽|👂🏾|👂🏿|👃🏻|👃🏼|👃🏽|👃🏾|👃🏿|👆🏻|👆🏼|👆🏽|👆🏾|👆🏿|👇🏻|👇🏼|👇🏽|👇🏾|👇🏿|👈🏻|👈🏼|👈🏽|👈🏾|👈🏿|👉🏻|👉🏼|👉🏽|👉🏾|👉🏿|👊🏻|👊🏼|👊🏽|👊🏾|👊🏿|👋🏻|👋🏼|👋🏽|👋🏾|👋🏿|👌🏻|👌🏼|👌🏽|👌🏾|👌🏿|👍🏻|👍🏼|👍🏽|👍🏾|👍🏿|👎🏻|👎🏼|👎🏽|👎🏾|👎🏿|👏🏻|👏🏼|👏🏽|👏🏾|👏🏿|👐🏻|👐🏼|👐🏽|👐🏾|👐🏿|👦🏻|👦🏼|👦🏽|👦🏾|👦🏿|👧🏻|👧🏼|👧🏽|👧🏾|👧🏿|👨🏻|👨🏼|👨🏽|👨🏾|👨🏿|👩🏻|👩🏼|👩🏽|👩🏾|👩🏿|👫🏻|👫🏼|👫🏽|👫🏾|👫🏿|👬🏻|👬🏼|👬🏽|👬🏾|👬🏿|👭🏻|👭🏼|👭🏽|👭🏾|👭🏿|👮🏻|👮🏼|👮🏽|👮🏾|👮🏿|👰🏻|👰🏼|👰🏽|👰🏾|👰🏿|👱🏻|👱🏼|👱🏽|👱🏾|👱🏿|👲🏻|👲🏼|👲🏽|👲🏾|👲🏿|👳🏻|👳🏼|👳🏽|👳🏾|👳🏿|👴🏻|👴🏼|👴🏽|👴🏾|👴🏿|👵🏻|👵🏼|👵🏽|👵🏾|👵🏿|👶🏻|👶🏼|👶🏽|👶🏾|👶🏿|👷🏻|👷🏼|👷🏽|👷🏾|👷🏿|👸🏻|👸🏼|👸🏽|👸🏾|👸🏿|👼🏻|👼🏼|👼🏽|👼🏾|👼🏿|💁🏻|💁🏼|💁🏽|💁🏾|💁🏿|💂🏻|💂🏼|💂🏽|💂🏾|💂🏿|💃🏻|💃🏼|💃🏽|💃🏾|💃🏿|💅🏻|💅🏼|💅🏽|💅🏾|💅🏿|💆🏻|💆🏼|💆🏽|💆🏾|💆🏿|💇🏻|💇🏼|💇🏽|💇🏾|💇🏿|💏🏻|💏🏼|💏🏽|💏🏾|💏🏿|💑🏻|💑🏼|💑🏽|💑🏾|💑🏿|💪🏻|💪🏼|💪🏽|💪🏾|💪🏿|🕴🏻|🕴🏼|🕴🏽|🕴🏾|🕴🏿|🕵🏻|🕵🏼|🕵🏽|🕵🏾|🕵🏿|🕺🏻|🕺🏼|🕺🏽|🕺🏾|🕺🏿|🖐🏻|🖐🏼|🖐🏽|🖐🏾|🖐🏿|🖕🏻|🖕🏼|🖕🏽|🖕🏾|🖕🏿|🖖🏻|🖖🏼|🖖🏽|🖖🏾|🖖🏿|🙅🏻|🙅🏼|🙅🏽|🙅🏾|🙅🏿|🙆🏻|🙆🏼|🙆🏽|🙆🏾|🙆🏿|🙇🏻|🙇🏼|🙇🏽|🙇🏾|🙇🏿|🙋🏻|🙋🏼|🙋🏽|🙋🏾|🙋🏿|🙌🏻|🙌🏼|🙌🏽|🙌🏾|🙌🏿|🙍🏻|🙍🏼|🙍🏽|🙍🏾|🙍🏿|🙎🏻|🙎🏼|🙎🏽|🙎🏾|🙎🏿|🙏🏻|🙏🏼|🙏🏽|🙏🏾|🙏🏿|🚣🏻|🚣🏼|🚣🏽|🚣🏾|🚣🏿|🚴🏻|🚴🏼|🚴🏽|🚴🏾|🚴🏿|🚵🏻|🚵🏼|🚵🏽|🚵🏾|🚵🏿|🚶🏻|🚶🏼|🚶🏽|🚶🏾|🚶🏿|🛀🏻|🛀🏼|🛀🏽|🛀🏾|🛀🏿|🛌🏻|🛌🏼|🛌🏽|🛌🏾|🛌🏿|🤌🏻|🤌🏼|🤌🏽|🤌🏾|🤌🏿|🤏🏻|🤏🏼|🤏🏽|🤏🏾|🤏🏿|🤘🏻|🤘🏼|🤘🏽|🤘🏾|🤘🏿|🤙🏻|🤙🏼|🤙🏽|🤙🏾|🤙🏿|🤚🏻|🤚🏼|🤚🏽|🤚🏾|🤚🏿|🤛🏻|🤛🏼|🤛🏽|🤛🏾|🤛🏿|🤜🏻|🤜🏼|🤜🏽|🤜🏾|🤜🏿|🤝🏻|🤝🏼|🤝🏽|🤝🏾|🤝🏿|🤞🏻|🤞🏼|🤞🏽|🤞🏾|🤞🏿|🤟🏻|🤟🏼|🤟🏽|🤟🏾|🤟🏿|🤦🏻|🤦🏼|🤦🏽|🤦🏾|🤦🏿|🤰🏻|🤰🏼|🤰🏽|🤰🏾|🤰🏿|🤱🏻|🤱🏼|🤱🏽|🤱🏾|🤱🏿|🤲🏻|🤲🏼|🤲🏽|🤲🏾|🤲🏿|🤳🏻|🤳🏼|🤳🏽|🤳🏾|🤳🏿|🤴🏻|🤴🏼|🤴🏽|🤴🏾|🤴🏿|🤵🏻|🤵🏼|🤵🏽|🤵🏾|🤵🏿|🤶🏻|🤶🏼|🤶🏽|🤶🏾|🤶🏿|🤷🏻|🤷🏼|🤷🏽|🤷🏾|🤷🏿|🤸🏻|🤸🏼|🤸🏽|🤸🏾|🤸🏿|🤹🏻|🤹🏼|🤹🏽|🤹🏾|🤹🏿|🤽🏻|🤽🏼|🤽🏽|🤽🏾|🤽🏿|🤾🏻|🤾🏼|🤾🏽|🤾🏾|🤾🏿|🥷🏻|🥷🏼|🥷🏽|🥷🏾|🥷🏿|🦵🏻|🦵🏼|🦵🏽|🦵🏾|🦵🏿|🦶🏻|🦶🏼|🦶🏽|🦶🏾|🦶🏿|🦸🏻|🦸🏼|🦸🏽|🦸🏾|🦸🏿|🦹🏻|🦹🏼|🦹🏽|🦹🏾|🦹🏿|🦻🏻|🦻🏼|🦻🏽|🦻🏾|🦻🏿|🧍🏻|🧍🏼|🧍🏽|🧍🏾|🧍🏿|🧎🏻|🧎🏼|🧎🏽|🧎🏾|🧎🏿|🧏🏻|🧏🏼|🧏🏽|🧏🏾|🧏🏿|🧑🏻|🧑🏼|🧑🏽|🧑🏾|🧑🏿|🧒🏻|🧒🏼|🧒🏽|🧒🏾|🧒🏿|🧓🏻|🧓🏼|🧓🏽|🧓🏾|🧓🏿|🧔🏻|🧔🏼|🧔🏽|🧔🏾|🧔🏿|🧕🏻|🧕🏼|🧕🏽|🧕🏾|🧕🏿|🧖🏻|🧖🏼|🧖🏽|🧖🏾|🧖🏿|🧗🏻|🧗🏼|🧗🏽|🧗🏾|🧗🏿|🧘🏻|🧘🏼|🧘🏽|🧘🏾|🧘🏿|🧙🏻|🧙🏼|🧙🏽|🧙🏾|🧙🏿|🧚🏻|🧚🏼|🧚🏽|🧚🏾|🧚🏿|🧛🏻|🧛🏼|🧛🏽|🧛🏾|🧛🏿|🧜🏻|🧜🏼|🧜🏽|🧜🏾|🧜🏿|🧝🏻|🧝🏼|🧝🏽|🧝🏾|🧝🏿|🫃🏻|🫃🏼|🫃🏽|🫃🏾|🫃🏿|🫄🏻|🫄🏼|🫄🏽|🫄🏾|🫄🏿|🫅🏻|🫅🏼|🫅🏽|🫅🏾|🫅🏿|🫰🏻|🫰🏼|🫰🏽|🫰🏾|🫰🏿|🫱🏻|🫱🏼|🫱🏽|🫱🏾|🫱🏿|🫲🏻|🫲🏼|🫲🏽|🫲🏾|🫲🏿|🫳🏻|🫳🏼|🫳🏽|🫳🏾|🫳🏿|🫴🏻|🫴🏼|🫴🏽|🫴🏾|🫴🏿|🫵🏻|🫵🏼|🫵🏽|🫵🏾|🫵🏿|🫶🏻|🫶🏼|🫶🏽|🫶🏾|🫶🏿|🫷🏻|🫷🏼|🫷🏽|🫷🏾|🫷🏿|🫸🏻|🫸🏼|🫸🏽|🫸🏾|🫸🏿|#️⃣|0️⃣|1️⃣|2️⃣|3️⃣|4️⃣|5️⃣|6️⃣|7️⃣|8️⃣|9️⃣|☝🏻|☝🏼|☝🏽|☝🏾|☝🏿|⛹🏻|⛹🏼|⛹🏽|⛹🏾|⛹🏿|✊🏻|✊🏼|✊🏽|✊🏾|✊🏿|✋🏻|✋🏼|✋🏽|✋🏾|✋🏿|✌🏻|✌🏼|✌🏽|✌🏾|✌🏿|✍🏻|✍🏼|✍🏽|✍🏾|✍🏿|🅰️|🅱️|🅾️|🅿️|🈂️|🈷️|🌡️|🌤️|🌥️|🌦️|🌧️|🌨️|🌩️|🌪️|🌫️|🌬️|🌶️|🍽️|🎖️|🎗️|🎙️|🎚️|🎛️|🎞️|🎟️|🏋️|🏌️|🏍️|🏎️|🏔️|🏕️|🏖️|🏗️|🏘️|🏙️|🏚️|🏛️|🏜️|🏝️|🏞️|🏟️|🏳️|🏵️|🏷️|🐿️|👁️|📽️|🕉️|🕊️|🕯️|🕰️|🕳️|🕴️|🕵️|🕶️|🕷️|🕸️|🕹️|🖇️|🖊️|🖋️|🖌️|🖍️|🖐️|🖥️|🖨️|🖱️|🖲️|🖼️|🗂️|🗃️|🗄️|🗑️|🗒️|🗓️|🗜️|🗝️|🗞️|🗡️|🗣️|🗨️|🗯️|🗳️|🗺️|🛋️|🛍️|🛎️|🛏️|🛠️|🛡️|🛢️|🛣️|🛤️|🛥️|🛩️|🛰️|🛳️|©️|®️|‼️|⁉️|™️|ℹ️|↔️|↕️|↖️|↗️|↘️|↙️|↩️|↪️|⌨️|⏏️|⏭️|⏮️|⏯️|⏱️|⏲️|⏸️|⏹️|⏺️|Ⓜ️|▪️|▫️|▶️|◀️|◻️|◼️|☀️|☁️|☂️|☃️|☄️|☎️|☑️|☘️|☝️|☠️|☢️|☣️|☦️|☪️|☮️|☯️|☸️|☹️|☺️|♀️|♂️|♟️|♠️|♣️|♥️|♦️|♨️|♻️|♾️|⚒️|⚔️|⚕️|⚖️|⚗️|⚙️|⚛️|⚜️|⚠️|⚧️|⚰️|⚱️|⛈️|⛏️|⛑️|⛓️|⛩️|⛰️|⛱️|⛴️|⛷️|⛸️|⛹️|✂️|✈️|✉️|✌️|✍️|✏️|✒️|✔️|✖️|✝️|✡️|✳️|✴️|❄️|❇️|❣️|❤️|➡️|⤴️|⤵️|⬅️|⬆️|⬇️|〰️|〽️|㊗️|㊙️|[\\u231A\\u231B\\u23E9-\\u23EC\\u23F0\\u23F3\\u25FD\\u25FE\\u2614\\u2615\\u2648-\\u2653\\u267F\\u2693\\u26A1\\u26AA\\u26AB\\u26BD\\u26BE\\u26C4\\u26C5\\u26CE\\u26D4\\u26EA\\u26F2\\u26F3\\u26F5\\u26FA\\u26FD\\u2705\\u270A\\u270B\\u2728\\u274C\\u274E\\u2753-\\u2755\\u2757\\u2795-\\u2797\\u27B0\\u27BF\\u2B1B\\u2B1C\\u2B50\\u2B55\\u{1F004}\\u{1F0CF}\\u{1F18E}\\u{1F191}-\\u{1F19A}\\u{1F201}\\u{1F21A}\\u{1F22F}\\u{1F232}-\\u{1F236}\\u{1F238}-\\u{1F23A}\\u{1F250}\\u{1F251}\\u{1F300}-\\u{1F320}\\u{1F32D}-\\u{1F335}\\u{1F337}-\\u{1F37C}\\u{1F37E}-\\u{1F393}\\u{1F3A0}-\\u{1F3CA}\\u{1F3CF}-\\u{1F3D3}\\u{1F3E0}-\\u{1F3F0}\\u{1F3F4}\\u{1F3F8}-\\u{1F43E}\\u{1F440}\\u{1F442}-\\u{1F4FC}\\u{1F4FF}-\\u{1F53D}\\u{1F54B}-\\u{1F54E}\\u{1F550}-\\u{1F567}\\u{1F57A}\\u{1F595}\\u{1F596}\\u{1F5A4}\\u{1F5FB}-\\u{1F64F}\\u{1F680}-\\u{1F6C5}\\u{1F6CC}\\u{1F6D0}-\\u{1F6D2}\\u{1F6D5}-\\u{1F6D7}\\u{1F6DC}-\\u{1F6DF}\\u{1F6EB}\\u{1F6EC}\\u{1F6F4}-\\u{1F6FC}\\u{1F7E0}-\\u{1F7EB}\\u{1F7F0}\\u{1F90C}-\\u{1F93A}\\u{1F93C}-\\u{1F945}\\u{1F947}-\\u{1F9FF}\\u{1FA70}-\\u{1FA7C}\\u{1FA80}-\\u{1FA88}\\u{1FA90}-\\u{1FABD}\\u{1FABF}-\\u{1FAC5}\\u{1FACE}-\\u{1FADB}\\u{1FAE0}-\\u{1FAE8}\\u{1FAF0}-\\u{1FAF8}])'
- }
-];
-
-describe('unicodeSets (v) flag', () => {
- for (const fixture of unicodeSetFixtures) {
- const pattern = fixture.pattern;
- const flags = fixture.flags || 'v';
- const options = fixture.options || { unicodeSetsFlag: 'transform' };
- const transformUnicodeFlag = options.unicodeFlag === 'transform';
-
- const inputRE = `/${pattern}/${flags}`;
-
- const expected = fixture.expected;
- const throws = fixture.throws;
-
- if (throws) {
- if (expected) {
- throw new Error(`TEST ERROR: ${inputRE} cannot both throw and have an expected output.`);
- }
- it(`throws for \`${inputRE}\` ${transformUnicodeFlag ? 'without ' : ''}using the u flag`, () => {
- assert.throws(() => {
- rewritePattern(pattern, flags, options);
- }, throws);
- });
- } else {
- it(`rewrites \`${inputRE}\` correctly ${transformUnicodeFlag ? 'without ' : ''}using the u flag`, () => {
- const transpiled = rewritePattern(pattern, flags, options);
- if (transpiled != '(?:' + expected + ')') {
- assert.strictEqual(transpiled, expected);
- }
- });
- }
- }
-
- it('Property of strings used without the v flag', () => {
- assert.throws(() => {
- rewritePattern('\\p{Basic_Emoji}', 'u')
- }, /Properties of strings are only supported when using the unicodeSets \(v\) flag/);
- })
-});
-
More details
Historical runs
- failed: Error [ERR_REQUIRE_ESM]: require() of ES Module /usr/share/nodejs/supports-color/index.js from /usr/share/nodejs/mocha/lib/reporters/base.js not supported.
- too-many-requests: Unexpected HTTP status 429 for https://salsa.debian.org/js-team/node-regexpu-core.git/info/refs?service=git-upload-pack: Unable to handle http code: Too Many Requests
- nothing-to-do: Last upstream version 5.2.1 already imported.
- nothing-to-do: Last upstream version 5.2.1 already imported.
- nothing-to-do: Last upstream version 5.2.1 already imported.
- nothing-to-do: Last upstream version 5.0.1 already imported.
- nothing-to-do: Last upstream version 5.0.1 already imported.
- nothing-to-do: Last upstream version 5.0.1 already imported.
- nothing-to-do: Last upstream version 5.0.1 already imported.
- command-failed: Command 'SCHROOT=unstable-amd64-sbuild PYTHONPATH=/:/code:/code/breezy:/code/dulwich:/code/lintian-brush:/code/ognibuild:/code/silver-platter:/code/buildlog-consultant:/code/upstream-ontologist:/code/debmutate:/code/python-debian/lib:/usr/lib/python39.zip:/usr/lib/python3.9:/usr/lib/python3.9/lib-dynload:/usr/local/lib/python3.9/dist-packages:/usr/lib/python3/dist-packages /usr/bin/python3 -m janitor.dist --packaging=/tmp/janitor2ak04b54/node-regexpu-core/debian' returned non-zero exit status 1.