Pegjs: Informe de errores de rediseño

Creado en 14 ago. 2013  ·  7Comentarios  ·  Fuente: pegjs/pegjs

Veo tres problemas con el sistema actual de informe de errores en PEG.js:

  1. Las acciones informan errores devolviendo null
Reporting using `null` is inflexible (it doesn't allow to carry any information about the kind of error) and makes it impossible to return `null` as a regular value from actions, which would be useful sometimes (for example [in a JSON parser](https://github.com/dmajda/pegjs/blob/791034fad92c9cd7a9d1c71187df03441bbfd521/examples/json.pegjs#L45)).

Véase también #17.

  1. No hay forma de producir errores con mensajes completamente personalizados.
For example, imagine a HTTP 1.1 parser that wants to report an error about missing `Host` header in a HTTP request with a message like "A HTTP 1.1 request must contain a Host header". Currently the only way to do this is to throw an exception with desired message manually. This is cumbersome and it does not interact well with the backtracking behavior (throwing an exception halts the parsing completely, even when it is possible to backtrack and try another alternatives).

  1. La propiedad expected de SyntaxError es difícil de procesar mecánicamente
The `expected` property of `SyntaxError` is either an array of expectations (each describing one alternative which the parser expected at the position of error) or `null` (meaning that the end of input was expected).

Cada expectativa está representada por una cadena. Esta cadena puede significar un literal esperado (representado usando su sintaxis PEG.js, una cadena entre comillas), una clase de carácter (representada usando su sintaxis PEG.js, [...] ) o cualquier carácter (representado por la cadena "any" ). Para diferenciar entre estos casos, uno tiene que analizar las representaciones de cadenas manualmente, lo que hace que la creación de herramientas basadas en PEG.js, como los autocompletadores, sea innecesariamente difícil.

Planeo rediseñar el sistema de notificación de errores para solucionar estos problemas. Antes de describir los cambios en los que estoy pensando, se necesita una pequeña descripción de cómo funciona el sistema actual.

¿Cómo funciona actualmente el informe de errores?

Cuando un analizador PEG.js intenta hacer coincidir un literal de cadena, una clase de carácter o . y falla, produce un _fallo de coincidencia_. Consiste en una _posición_ y una _expectativa_ (descripción de lo que el analizador intentó hacer coincidir).

Después de producir una falla de coincidencia, el analizador retrocede y posiblemente prueba otras alternativas. Si ninguno de ellos tiene éxito y no hay nada más que intentar, el analizador lanza la excepción SyntaxError . Al establecer sus propiedades (como posición, expectativas, mensaje de error, etc.), el analizador considera solo la falla de coincidencia más lejana (la que tiene la posición más grande). Si hay más fallas de este tipo, sus expectativas se combinan.

La situación es un poco más complicada si se usan reglas con nombre, pero esto es irrelevante para esta discusión.

Cambios propuestos

Estoy pensando en los siguientes cambios en el sistema de notificación de errores:

  1. Las expectativas en SyntaxError.expected no estarán representadas por cadenas, sino por objetos. Los objetos tendrán una propiedad type (con valores como "literal" , "class" , "any" , "eof" ) que determinará el tipo de expectativa. Para algunos tipos de expectativas, otras propiedades contendrán los detalles (por ejemplo, para la expectativa "literal" donde sería una propiedad value que contiene la cadena esperada). Esto simplificará el procesamiento mecánico de las expectativas.

Una alternativa a la propiedad type es usar clases. Pero creo que la propiedad type será más fácil de manejar para los usuarios.

  1. Se permitirá que las acciones devuelvan null . Será un valor regular y no significará un error.
  2. Las acciones podrán desencadenar una falla de coincidencia utilizando una nueva función error . Tomará un mensaje de error como su parámetro. Las fallas desencadenadas por esta función (llamadas _fallas de coincidencia personalizada_) consistirán en una _posición_ y un _mensaje de error_. No tendrán ninguna expectativa.

La función error no interrumpirá la ejecución de la acción, solo la marcará como fallida y guardará el mensaje de error. El fallo real se producirá sólo después de que finalice la ejecución de la acción. Si la función error se invoca varias veces, la última invocación ganará (se usará su mensaje de error).

Las fallas de coincidencias personalizadas se manejarán como fallas de coincidencias regulares, lo que significa que no detendrán el análisis por completo y permitirán que el analizador retroceda y posiblemente pruebe otras alternativas. Sin embargo, hay una diferencia: cuando finalmente se lanza la excepción SyntaxError , la regla de combinación de expectativas que se aplica a las coincidencias fallidas regulares no se aplicará a las personalizadas. Si en el conjunto de fallos de coincidencia con la posición más lejana hay al menos uno personalizado, simplemente anulará por completo a los regulares. Si hay más fallas personalizadas, ganará la que se produjo en último lugar.

Una excepción SyntaxError basada en una falla de coincidencia personalizada diferirá de las excepciones basadas en una falla regular. Su propiedad message será igual al mensaje de error del error y su propiedad expected será null .

Ejemplo

```
inicio = signo:[+-]? dígitos:[0-9]+ {
var resultado = parseInt((signo || "") + digits.join(""), 10);

 if (result % 2 == 0) {
   error("The number must be an odd integer.");
 }

 return result;

}
```

En la entrada 2 , el analizador generado a partir de la gramática anterior producirá un SyntaxError con message establecido en "The number must be an odd integer." y expected establecido en null

  1. Las acciones también podrán desencadenar una falla de coincidencia regular usando la función expected . Tomará una descripción del valor esperado como parámetro. Esta función se proporcionará principalmente como una conveniencia para situaciones en las que no es necesario generar un mensaje de error completo y un formulario generado automáticamente "Se esperaba _X_ pero se encontraron "2". es suficiente.

Una excepción SyntaxError basada en un error de coincidencia generado por la función expected será similar a las excepciones basadas en un error normal.

Ejemplo

```
inicio = signo:[+-]? dígitos:[0-9]+ {
var resultado = parseInt((signo || "") + digits.join(""), 10);

 if (result % 2 == 0) {
   expected("odd integer");
 }

 return result;

}
```

En la entrada 2 , el analizador generado a partir de la gramática anterior producirá un SyntaxError con message establecido en "Expected odd integer but "2" found." y expected establecido en [ { type: "user", description: "odd integer" } ]

Próximos pasos

Agradezco cualquier nota sobre los cambios propuestos; agréguelos como comentarios. Planeo comenzar a implementar la propuesta (o alguna versión modificada basada en los comentarios) pronto.

feature

Comentario más útil

Entonces, ¿todavía no hay una función de "advertencia"?

Todos 7 comentarios

Sería mejor si múltiples invocaciones de la función de error condujeran a múltiples errores. A continuación, podría continuar analizando tanto como sea posible.

string = '"' value:(!(eol / '"') .)+ '"' { return value; }
       / '"' value:(!(eol / '"') .)+     { error('unterminated string constant'); return value; }

También recomendaría agregar soporte para advertencias.

Sería mejor si múltiples invocaciones de la función de error condujeran a múltiples errores. A continuación, podría continuar analizando tanto como sea posible.

¿Podría nombrar algunos casos de uso en los que necesitaría esto? Me parece que el ejemplo que proporcionó también funcionaría con mi propuesta.

Ya tengo algunos casos de uso, pero no sé qué tan representativos son, así que me gustaría ver algunos más.

También recomendaría agregar soporte para advertencias.

Yo también estoy pensando en eso.

Un problema con múltiples errores y advertencias es que necesitarían una interfaz diferente a la simple e intuitiva " parse devuelve algún valor en caso de éxito o una excepción en caso de error". El analizador necesitaría informar múltiples errores en el análisis fallido, además de advertencias tanto en el análisis exitoso como en el fallido.

¿Qué tipo de API encontraría más intuitiva aquí? Una vez más, tengo algunas ideas, pero me gustaría ver qué piensan los demás.

¿Podría nombrar algunos casos de uso en los que necesitaría esto? Me parece que el ejemplo que proporcionó también funcionaría con mi propuesta.

Mi caso de uso principal es para errores de análisis no fatales. Cadenas no terminadas, identificadores que comienzan con un número, comentarios anidados, puntos y comas faltantes, etc.

En general, es mejor dejar que el analizador avance lo más posible, de modo que se le muestren al usuario tantos errores como sea posible. Si el analizador pudiera incluso terminar el análisis y permitir que los siguientes pasos (por ejemplo, la compilación) también informen errores, sería genial.

Esto ya está implementado . El script tools/impact informa el siguiente impacto en el rendimiento de todo el conjunto de confirmaciones:

Speed impact
------------
Before:     1144.21 kB/s
After:      999.89 kB/s
Difference: -12.62%

Size impact
-----------
Before:     863523 b
After:      1019968 b
Difference: 18.11%

(Measured by /tools/impact with Node.js v0.6.18 on x86_64 GNU/Linux.)

Creo que una penalización de velocidad del 12,62 % y una penalización de tamaño del 18,11 % está bien para resolver un conjunto de problemas de larga data.

Clausura.

@dmajda : ¡Es una gran noticia! Estoy muy contento de que null ya no sea una señal de falla.

Entonces, ¿todavía no hay una función de "advertencia"?

El tema de la función de advertencia se rastrea en el n.º 325

¿Fue útil esta página
0 / 5 - 0 calificaciones