Nadchodzące funkcje wyrażeń regularnych

W specyfikacji ES2015 wprowadzono wiele nowych funkcji języka JavaScript, w tym znaczne ulepszenia składni wyrażeń regularnych dzięki flagom Unicode (/u) i sticky (/y). Od tego czasu prace nad nim nie ustały. W ścisłej współpracy z innymi członkami TC39 (organizacji zajmującej się standardami ECMAScript) zespół V8 zaproponował i opracował kilka nowych funkcji, które mają zwiększyć możliwości wyrażeń regularnych.

Obecnie rozważamy uwzględnienie tych funkcji w specyfikacji JavaScript. Chociaż propozycje nie zostały w pełni zaakceptowane, są już na etapie 3 procesu TC39. Wdrożyliśmy te funkcje za pomocą flagi (patrz poniżej), aby móc na czas przekazać autorom propozycji opinie na temat projektu i wdrażania przed sfinalizowaniem specyfikacji.

W tym poście na blogu znajdziesz zapowiedź tej ekscytującej przyszłości. Jeśli chcesz śledzić kolejne przykłady, włącz eksperymentalne funkcje JavaScriptu na stronie chrome://flags/#enable-javascript-harmony.

Nazwane ujęcia

Wyrażenia regularne mogą zawierać tak zwane uchwyty (lub grupy), które mogą uchwycić część dopasowanego tekstu. Do tej pory deweloperzy mogli odwoływać się do tych rejestracji tylko za pomocą ich indeksu, który jest określany przez pozycję rejestracji w wzorze.

const pattern = /(\d{4})-(\d{2})-(\d{2})/u;
const result = pattern.exec('2017-07-10');
// result[0] === '2017-07-10'
// result[1] === '2017'
// result[2] === '07'
// result[3] === '10'

Wyrażenia regularne są jednak trudne do odczytania, pisania i utrzymywania, a odwołania numeryczne mogą jeszcze bardziej skomplikować sprawę. Na przykład w dłuższych wzorach trudno jest określić indeks konkretnego przechwycenia:

/(?:(.)(.(?<=[^(])(.)))/  // Index of the last capture?

Co gorsza, zmiany w wzorze mogą potencjalnie zmienić indeksy wszystkich dotychczasowych przechwyceń:

/(a)(b)(c)\3\2\1/     // A few simple numbered backreferences.
/(.)(a)(b)(c)\4\3\2/  // All need to be updated.

Nagrania z nazwami to funkcja, która wkrótce zostanie udostępniona. Pomoże ona rozwiązać te problemy, ponieważ pozwoli deweloperom przypisywać nazwy do nagrań. Składnia jest podobna do tej w przypadku Perl, Java, .Net i Ruby:

const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
const result = pattern.exec('2017-07-10');
// result.groups.year === '2017'
// result.groups.month === '07'
// result.groups.day === '10'

Do nazwanych przechwyceń można też odwoływać się za pomocą nazwanych odwołań wstecznych i za pomocą:String.prototype.replace:

// Named backreferences.
/(?<LowerCaseX>x)y\k<LowerCaseX>/.test('xyx');  // true

// String replacement.
const pattern = /(?<fst>a)(?<snd>b)/;
'ab'.replace(pattern, '$<snd>$<fst>');                              // 'ba'
'ab'.replace(pattern, (m, p1, p2, o, s, {fst, snd}) => fst + snd);  // 'ba'

Szczegółowe informacje o tej nowej funkcji znajdziesz w propozycji specyfikacji.

flaga dotAll

Domyślnie atom . w wyrażeniach regularnych pasuje do dowolnego znaku oprócz znaków końca wiersza:

/foo.bar/u.test('foo\nbar');   // false

Propozycja wprowadza tryb dotAll, który jest włączany za pomocą flagi /s. W trybie dotAll . odpowiada też znakom końca linii.

/foo.bar/su.test('foo\nbar');  // true

Szczegółowe informacje o tej nowej funkcji znajdziesz w propozycji specyfikacji.

Ucieczki właściwości w Unicode

W wersji ES2015 wprowadzono obsługę Unicode, dzięki czemu nagle pojawiło się wiele znaków, które można uznać za liczby, np. cyfrę 1 w kółku: ①, lub za znaki słowne, np. chiński znak oznaczający śnieg: 雪.

Żadnego z nich nie można dopasować do \d ani \w. Zmiana znaczenia tych skrótów spowodowałaby nieprawidłowe działanie istniejących wzorów wyrażeń regularnych.

Zamiast tego wprowadzamy nowe sekwencje ucieczki właściwości. Pamiętaj, że są one dostępne tylko w przypadku wyrażeń regularnych obsługujących Unicode, oznaczonych flagą /u.

/\p{Number}/u.test('①');      // true
/\p{Alphabetic}/u.test('雪');  // true

Odwrotność można dopasować za pomocą funkcji \P.

/\P{Number}/u.test('①');      // false
/\P{Alphabetic}/u.test('雪');  // false

Konsorcjum Unicode definiuje znacznie więcej właściwości, na przykład dla symboli matematycznych lub znaków japońskiej hiragana:

/^\p{Math}+$/u.test('∛∞∉');                            // true
/^\p{Script_Extensions=Hiragana}+$/u.test('ひらがな');  // true

Pełną listę obsługiwanych klas właściwości Unicode znajdziesz w bieżącej propozycji specyfikacji. Więcej przykładów znajdziesz w tym przydatnym artykule.

Założenia dotyczące danych historycznych

Założenia wyprzedzające były od początku częścią składni wyrażeń regularnych w JavaScriptzie. Ich odpowiednikiem są założenia dotyczące danych historycznych. Niektórzy z Was mogą pamiętać, że ta funkcja jest już od jakiegoś czasu częścią V8. W ramach implementacji flagi Unicode określonej w ES2015 używamy nawet twierdzeń lookbehind.

Nazwa już dobrze opisuje jej znaczenie. Umożliwia ona ograniczenie dopasowania wzorca tylko do przypadku, gdy poprzedza go wzorzec w grupie lookbehind. Dostępne są dwa rodzaje funkcji:

/(?<=\$)\d+/.exec('$1 is worth about ¥123');  // ['1']
/(?<!\$)\d+/.exec('$1 is worth about ¥123');  // ['123']

Więcej informacji znajdziesz w poprzednim poście na blogu poświęconym twierdzeniom lookbehind oraz w przypadkach testowych V8.

Podziękowania

W tym poście na blogu nie sposób nie wspomnieć o osobach, które ciężko pracowały nad tą funkcją. Chodzi tu zwłaszcza o twórców języka Mathiasa Bynensa, Dana Ehrenberga, Claude’a Pache, Briana Terlsona, Thomasa Wooda, Gorkema Yakina i Erika Corry’ego, którzy pracowali nad specyfikacją języka i wdrożeniem tych funkcji w V8. Chodzi też o wszystkich innych, którzy przyczynili się do specyfikacji języka i wdrożenia tych funkcji w V8.

Mamy nadzieję, że te nowe funkcje wyrażeń regularnych są tak samo ekscytujące jak dla nas.