A referência autorizada sobre questões pragmáticas por trás da implementação de mecanismos de expressão regular é uma série de três postagens de blog de Russ Cox . Conforme descrito lá, como as referências anteriores tornam seu idioma não regular, elas são implementadas usando o retorno .
Lookaheads e lookbehinds, como muitos recursos dos mecanismos de correspondência de padrões regex, não se encaixam no paradigma de decidir se uma string é ou não membro de um idioma ou não. Em vez de regexes, geralmente procuramos substrings dentro de uma string maior. As "correspondências" são substrings que são membros do idioma e o valor de retorno são os pontos inicial e final da substring na cadeia maior.
O objetivo de olhar para trás e olhar para trás não é tanto introduzir a capacidade de combinar idiomas não regulares, mas sim ajustar o local em que o mecanismo relata os pontos inicial e final da substring correspondente.
Estou contando com a descrição em http://www.regular-expressions.info/lookaround.html . Os mecanismos de expressão regular que suportam esse recurso (Perl, TCL, Python, Ruby, ...) parecem basear-se no retorno (ou seja, eles suportam um conjunto muito maior de idiomas do que apenas os idiomas regulares). Eles parecem estar implementando esse recurso como uma extensão relativamente "simples" do retorno, em vez de tentar construir autômatos finitos reais para executar a tarefa.
Lookahead positivo
A sintaxe da aparência positiva é (?=
regex)
. Portanto, por exemplo, q(?=u)
corresponde q
apenas se for seguido por u
, mas não corresponde a u
. Eu imagino que eles implementem isso com uma variação no retorno. Crie um FSM para a expressão antes da aparência positiva. Quando isso corresponder, lembre-se de onde terminou e inicie um novo FSM que representa a expressão dentro da cabeça positiva. Se isso corresponder, você terá uma "correspondência", mas a correspondência "terminará" imediatamente antes da posição em que a correspondência positiva do lookahead começou.
A única parte disso que seria difícil sem retroceder é que você precisa se lembrar do ponto da entrada em que o cabeçote inicia e mover a fita de entrada de volta para esta posição depois de terminar a partida.
Lookahead negativo
A sintaxe para lookahead negativo é (?!
regex)
. Portanto, por exemplo, q(?!u)
corresponde q
apenas se não for seguido por u
. Pode ser um q
seguido por outro caractere ou um q
no final da string. Eu imagino que isso seja implementado criando um NFA para a expressão lookahead, tendo êxito apenas se o NFA falhar na correspondência com a sequência subsequente.
Se você quiser fazê-lo sem depender de voltar atrás, poderá negar o NFA da expressão lookahead, então trate-o da mesma maneira que você trata lookahead positivo.
Lookbehind positivo
(?<=
)
(?=q)u
u
q
q
nnn
Você pode implementar isso sem retroceder fazendo a interseção de "string que termina com regex " com qualquer parte do regex que vem antes do operador lookbehind. Isso vai ser complicado, porque o regex que está por trás pode precisar olhar mais para trás do que o início atual da entrada.
Lookbehind negativo
A sintaxe para lookbehind negativo é (?<!
regex)
. Assim, por exemplo, (?<!q)u
corresponde u
, mas apenas se não for precedido por q
. Portanto, combinaria o u
in umbrella
e o u
in doubt
, mas não o u
in quick
. Novamente, isso parece ser feito calculando o comprimento da regex , fazendo o backup de muitos caracteres, testando a correspondência com a regex , mas agora falhando a correspondência inteira se o lookbehind corresponder.
Você pode implementar isso sem retroceder tomando a negação do regex e fazendo o mesmo que faria para olhar para trás.