Il riferimento autorevole sulle questioni pragmatiche alla base dell'implementazione dei motori regex è una serie di tre post sul blog di Russ Cox . Come descritto qui, poiché i riferimenti secondari rendono la tua lingua non regolare, vengono implementati utilizzando il backtracking .
Lookaheads e lookbehinds, come molte caratteristiche dei motori di corrispondenza dei pattern regex, non si adattano perfettamente al paradigma di decidere se una stringa è o meno membro di un linguaggio. Piuttosto con regex di solito stiamo cercando sottostringhe all'interno di una stringa più grande. Le "corrispondenze" sono sottostringhe che sono membri della lingua e il valore restituito è il punto iniziale e finale della sottostringa all'interno della stringa più grande.
Lo scopo di lookaheads e lookbehinds non è tanto quello di introdurre la capacità di abbinare le lingue non regolari, ma piuttosto di regolare dove il motore riporta i punti di inizio e fine della sottostringa abbinata.
Mi affido alla descrizione su http://www.regular-expressions.info/lookaround.html . I motori regex che supportano questa funzione (Perl, TCL, Python, Ruby, ...) sembrano tutti basati sul backtracking (ovvero, supportano un set di lingue molto più ampio rispetto alle sole lingue normali). Sembrano implementare questa funzionalità come un'estensione relativamente "semplice" del backtracking, piuttosto che cercare di costruire automi finiti reali per eseguire l'attività.
Lookahead positivo
La sintassi per lookahead positivo è (?=
regex)
. Quindi, ad esempio, q(?=u)
corrisponde q
solo se è seguito da u
, ma non corrisponde a u
. Immagino che lo implementino con una variazione sul backtracking. Crea un FSM per l'espressione prima del lookahead positivo. Quando le corrispondenze ricordano dove è finita e inizia un nuovo FSM che rappresenta l'espressione all'interno del lookahead positivo. Se corrisponde, allora hai una "corrispondenza", ma la corrispondenza "termina" appena prima della posizione in cui è iniziata la partita di lookahead positiva.
L'unica parte di ciò che sarebbe difficile senza backtracking è che è necessario ricordare il punto nell'input in cui inizia il lookahead e spostare il nastro di input in questa posizione dopo aver terminato la corrispondenza.
Lookahead negativo
La sintassi per lookahead negativo è (?!
regex)
. Quindi, ad esempio, q(?!u)
corrisponde q
solo se non è seguito da u
. Questo potrebbe essere o q
seguito da un altro personaggio o q
alla fine della stringa. Immagino che questo sia implementato creando un NFA per l'espressione lookahead, quindi riuscendo solo se l'NFA non riesce a corrispondere alla stringa successiva.
Se vuoi farlo senza fare affidamento sul backtracking, potresti annullare l'NFA dell'espressione lookahead, quindi trattarlo allo stesso modo in cui tratti lo sguardo positivo.
Lookbehind positivo
La sintassi per lookbehind positivo è (?<=
regex)
. Quindi, ad esempio, (?=q)u
corrisponde u
, ma solo se è preceduto da q
, ma non corrisponde a q
. Apparentemente questo è implementato come un hack completo in cui il motore regex esegue il backup di caratteri e cerca di abbinare regex a quegli caratteri. Ciò significa che regex deve essere tale da corrispondere solo a stringhe di lunghezza .n nnnn
Potresti essere in grado di implementarlo senza tornare indietro prendendo l'intersezione di "stringa che termina con regex " con qualsiasi parte del regex che precede l'operatore lookbehind. Questo sarà però complicato, perché la regex lookbehind potrebbe aver bisogno di guardare più indietro rispetto all'attuale inizio dell'input.
Lookbehind negativo
La sintassi per lookbehind negativo è (?<!
regex)
. Quindi, ad esempio, (?<!q)u
corrisponde u
, ma solo se non è preceduto da q
. Quindi corrisponderebbe a u
in umbrella
e u
in doubt
, ma non a u
in quick
. Ancora una volta, questo sembra essere fatto calcolando la lunghezza di regex , eseguendo il backup di molti personaggi, testando la partita con regex , ma ora fallendo l'intera partita se il lookbehind corrisponde.
Potresti essere in grado di implementarlo senza tornare indietro prendendo la negazione di regex e quindi facendo lo stesso che faresti per un lookbehind positivo.