Cero o más / uno o más modificadores y retroceso

Estaba agregando cero o más y uno o más modificadores a mi PEG analizador, que es sencillo ya que hay muy poco retroceso en PEG. Las iteraciones anteriores nunca se reconsideran, por lo que un simple while bucle es suficiente.

Sin embargo, en otros contextos, los modificadores cero o más y uno o más requieren retroceder. Por ejemplo, tome la siguiente expresión regular:

(aa|aaa)+

Esta expresión debería poder coincidir con avidez con una cadena de siete a's: hay varias formas de sumar 2 y 3 para obtener 7. Pero para llegar allí, es necesario reconsiderar las iteraciones anteriores. Por ejemplo, si la expresión coincide con tres aes la primera vez y tres aes la segunda vez, solo una a permanece, que no se puede igualar. Retrocede los últimos tres ay empareja dos aen su lugar, sin embargo, y cinco aestán emparejados. Entonces los dos ultimos aLos 's también pueden coincidir (es decir, 3 + 2 + 2 = 7).

Afortunadamente, la expresión regular abandona su búsqueda una vez que coincide con la cadena. Pero que tal un EBNF analizador? Si la gramática es ambigua, el analizador debe usar el retroceso para encontrar todos los árboles de sintaxis posibles! Si tenemos la produccion

( "aa" | "aaa" )*

y una cadena de siete a, un analizador totalmente retroactivo devolvería todas las formas posibles de expresar 7 en términos de 2 y 3. Y eso es solo para siete a's: haga coincidir una cuerda un poco más larga, y el N-arbol de posibilidades crece otro nivel. Considera N = 6:

S : ( T )*
  ;

T : A
  | B
  | C
  | D
  | E
  | F
  ;

¡Una terrorífica explosión combinatoria!

Podría esto verdaderamente aunque sea el caso? ¿No existen restricciones sobre los modificadores cero o más y uno o más en EBNF? Implementarlos como se describe sería mucho más trabajo que el simple while() bucle del analizador PEG, así que tengo que preguntarme ...

preguntado el 27 de agosto de 11 a las 21:08

1 Respuestas

sí; dar marcha atrás puede darte muchos resultados. Soy el autor de lepl, que es un analizador decente recursivo que felizmente retrocederá y producirá un "bosque de análisis" de todos los AST posibles. y no hay restricción en EBNF (que es solo un lenguaje de especificación y no está vinculado a ninguna implementación de analizador en particular).

pero no todos los algoritmos de análisis retroceden. muchas implementaciones de expresiones regulares lo hacen, pero no siempre es necesario. de hecho, para una expresión regular "simple" (una que realmente se limita a las gramáticas regulares) es posible hacer coincidir sin retroceder en absoluto; el truco es, en cierto sentido, ejecutar las cosas en paralelo.

Hay dos formas (equivalentes) de hacer esto: "compilando" la expresión regular (calculando cuál sería la expresión si el trabajo en paralelo fuera explícito) o haciendo malabarismos con coincidencias paralelas en tiempo de ejecución. el enfoque de compilación traduce la expresión regular a un DFA (autómata finito determinista). más exactamente, un NFA (no determinista ...) es vagamente como una versión gráfica de la expresión regular, y es probablemente la forma en que imagina que funcionan las expresiones regulares; la coincidencia con un NFA requiere retroceder, pero puede traducir el NFA a un DFA que no lo hace.

sin embargo, hacer esto en tiempo de ejecución es más fácil de entender (y tiende a ser más útil en la práctica) y se explica en tres impresionante artículos que realmente debería leer si quiere entender esto mejor: http://swtch.com/~rsc/regexp/regexp3.html y los enlaces al comienzo de eso.

No puedo enfatizar esto lo suficiente, necesitas leer esos artículos ...

ps vagamente relacionado: puede hacer que el retroceso sea más eficiente almacenando en caché los resultados que podría necesitar más adelante (cuando termine llegando al mismo texto y expresión a través de una ruta diferente). esto se llama "parsing packrat" cuando se aplica a un análisis decente recursivo (aunque, para ser honesto, no vale la pena un nombre por separado, en realidad solo está usando un caché). el almacenamiento en caché evita tiempos de ejecución exponenciales: hay un documento en alguna parte de norvig (el tipo de Google, pero esto fue escrito mucho antes) que explica que: http://acl.ldc.upenn.edu/J/J91/J91-1004.pdf

Respondido 28 ago 11, 05:08

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.