La referencia autorizada sobre los problemas pragmáticos detrás de la implementación de motores regex es una serie de tres publicaciones de blog de Russ Cox . Como se describe allí, ya que hacen referencias hacia atrás su idioma no regular, que se implementan utilizando vuelta hacia atrás .
Lookaheads y lookbehinds, como muchas características de los motores de coincidencia de patrones regex, no encajan en el paradigma de decidir si una cadena es miembro de un lenguaje o no. En lugar de expresiones regulares, generalmente estamos buscando subcadenas dentro de una cadena más grande. Las "coincidencias" son subcadenas que son miembros del lenguaje, y el valor de retorno son los puntos inicial y final de la subcadena dentro de la cadena más grande.
El punto de mira hacia atrás y hacia atrás no es tanto para introducir la capacidad de hacer coincidir idiomas no regulares, sino más bien para ajustar dónde el motor informa los puntos de inicio y finalización de la subcadena coincidente.
Confío en la descripción en http://www.regular-expressions.info/lookaround.html . Los motores regex que admiten esta función (Perl, TCL, Python, Ruby, ...) parecen estar basados en el retroceso (es decir, admiten un conjunto de idiomas mucho más grande que solo los idiomas normales). Parecen estar implementando esta característica como una extensión relativamente "simple" de retroceso, en lugar de tratar de construir autómatas finitos reales para realizar la tarea.
Lookahead positivo
La sintaxis para la búsqueda anticipada positiva es (?=
regex)
. Entonces, por ejemplo q(?=u)
, q
solo coincide si es seguido por u
, pero no coincide con u
. Me imagino que implementan esto con una variación en el retroceso. Cree un FSM para la expresión antes de la búsqueda anticipada positiva. Cuando eso coincida, recuerde dónde terminó y comience un nuevo FSM que represente la expresión dentro de la búsqueda anticipada positiva. Si eso coincide, entonces tiene una "coincidencia", pero la coincidencia "termina" justo antes de la posición donde comenzó la coincidencia positiva anticipada.
La única parte de esto que sería difícil sin retroceder es que debe recordar el punto en la entrada donde comienza la búsqueda anticipada y mover la cinta de entrada a esta posición después de que haya terminado con la coincidencia.
Lookahead negativo
La sintaxis para el lookahead negativo es (?!
regex)
. Entonces, por ejemplo, q(?!u)
coincide q
solo si no es seguido por u
. Esto podría ser q
seguido por algún otro personaje o q
al final de la cadena. Me imagino que esto se implementa creando un NFA para la expresión de búsqueda anticipada, y luego tiene éxito solo si el NFA no coincide con la cadena posterior.
Si desea hacerlo sin depender de retroceder, podría negar el NFA de la expresión anticipada, luego trátelo de la misma manera que trata la anticipación positiva.
Mirada hacia atrás positiva
(?<=
)
(?=q)u
u
q
q
nortenortenorte
Es posible que pueda implementar esto sin retroceder tomando la intersección de "cadena que termina con regex " con cualquier parte de la expresión regular que viene antes del operador retrospectivo. Sin embargo, esto va a ser complicado, porque la expresión regex de retrospectiva puede necesitar mirar más atrás que el comienzo actual de la entrada.
Mirada hacia atrás negativa
La sintaxis para la mirada hacia atrás negativa es (?<!
regex)
. Entonces, por ejemplo, (?<!q)u
coincide u
, pero solo si no está precedido por q
. Por lo tanto, coincidiría con el u
in umbrella
y el u
in doubt
, pero no con el u
in quick
. Nuevamente, esto parece hacerse calculando la longitud de la expresión regular , haciendo una copia de seguridad de esa cantidad de caracteres, probando la coincidencia con la expresión regular , pero ahora fallando toda la coincidencia si el aspecto posterior coincide.
Es posible que pueda implementar esto sin retroceder tomando la negación de la expresión regular y luego haciendo lo mismo que haría para una mirada positiva hacia atrás.