Próximos recursos de expressão regular

A ES2015 introduziu muitos novos recursos ao JavaScript, incluindo melhorias significativas na sintaxe de expressões regulares com as flags Unicode (/u) e sticky (/y). Mas o desenvolvimento não parou desde então. Em estreita colaboração com outros membros do TC39 (o órgão de padrões ECMAScript), a equipe do V8 propôs e co-projetou vários novos recursos para tornar as expressões regulares ainda mais poderosas.

No momento, esses recursos estão sendo propostos para inclusão na especificação do JavaScript. Embora as propostas não tenham sido totalmente aceitas, elas já estão na etapa 3 do processo TC39. Implementamos esses recursos com uma flag (confira abaixo) para podermos fornecer feedback de design e implementação aos autores das propostas antes que a especificação seja finalizada.

Esta postagem do blog mostra uma prévia desse futuro incrível. Se você quiser seguir os exemplos futuros, ative os recursos experimentais do JavaScript em chrome://flags/#enable-javascript-harmony.

Capturas nomeadas

As expressões regulares podem conter capturas (ou grupos), que podem capturar uma parte do texto correspondente. Até agora, os desenvolvedores só podiam se referir a essas capturas pelo índice, que é determinado pela posição da captura no padrão.

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'

No entanto, as expressões regulares já são conhecidas por serem difíceis de ler, escrever e manter, e as referências numéricas podem complicar ainda mais as coisas. Por exemplo, em padrões mais longos, pode ser difícil determinar o índice de uma captura específica:

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

Pior ainda, as mudanças em um padrão podem mudar os índices de todas as capturas:

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

As capturas nomeadas são um recurso que ajuda a reduzir esses problemas, permitindo que os desenvolvedores atribuam nomes às capturas. A sintaxe é semelhante a Perl, Java, .Net e 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'

As capturas nomeadas também podem ser referenciadas por referências cruzadas nomeadas e por 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'

Confira todos os detalhes do novo recurso na proposta de especificação.

Flag dotAll

Por padrão, o átomo . em expressões regulares corresponde a qualquer caractere, exceto terminadores de linha:

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

Uma proposta introduz o modo dotAll, ativado pela flag /s. No modo dotAll, . também corresponde a terminadores de linha.

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

Confira todos os detalhes do novo recurso na proposta de especificação.

Escapes de propriedade Unicode

Com a detecção de Unicode introduzida no ES2015, de repente, há muitos mais caracteres que podem ser considerados números, por exemplo, o dígito um circulado: ①; ou caracteres de palavra, por exemplo, o caractere chinês para neve: 雪.

Nenhum deles pode ser associado a \d ou \w. Mudar o significado dessas abreviações vai quebrar os padrões de expressão regular atuais.

Em vez disso, novas sequências de escape de propriedade estão sendo introduzidas. Elas estão disponíveis apenas para expressões regulares compatíveis com Unicode indicadas pela flag /u.

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

O inverso pode ser combinado com \P.

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

O consórcio Unicode define muitas outras propriedades, por exemplo, para símbolos matemáticos ou caracteres hiragana japoneses:

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

A lista completa de classes de propriedades Unicode compatíveis pode ser encontrada na proposta de especificação atual. Para mais exemplos, consulte este artigo informativo.

Declarações de lookbehind

As declarações de previsão fazem parte da sintaxe de expressão regular do JavaScript desde o início. A contraparte, as declarações de lookbehind, está finalmente sendo introduzida. Alguns de vocês talvez se lembrem de que isso já faz parte do V8 há algum tempo. Nós até usamos declarações de lookbehind por trás para implementar a flag Unicode especificada no ES2015.

O nome já descreve bem o significado. Ele oferece uma maneira de restringir um padrão para que ele só corresponda se for precedido pelo padrão no grupo de busca anterior. Ele está disponível em sabores correspondentes e não correspondentes:

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

Para mais detalhes, confira nossa postagem anterior do blog dedicada a declarações de lookbehind e exemplos em casos de teste do V8 relacionados.

Agradecimentos

Esta postagem do blog não estaria completa sem mencionar algumas das pessoas que trabalharam muito para que isso acontecesse: especialmente os campeões de linguagem Mathias Bynens, Dan Ehrenberg, Claude Pache, Brian Terlson, Thomas Wood, Gorkem Yakin e o guru do Irregexp Erik Corry; mas também todos os outros que contribuíram para a especificação da linguagem e a implementação desses recursos no V8.

Esperamos que você goste desses novos recursos de expressão regular tanto quanto nós.