¿Cuáles son los argumentos en contra de analizar el camino de Cthulhu?


24

Se me ha asignado la tarea de implementar un lenguaje específico de dominio para una herramienta que puede llegar a ser bastante importante para la empresa. El lenguaje es simple pero no trivial, ya permite bucles anidados, concatenación de cadenas, etc. y es prácticamente seguro que se agregarán otras construcciones a medida que avance el proyecto.

Sé por experiencia que escribir un lexer / analizador a mano, a menos que la gramática sea trivial, es un proceso lento y propenso a errores. Así que me quedaban dos opciones: un generador de analizadores a la yacc o una biblioteca combinada como Parsec. El primero también era bueno, pero elegí el segundo por varias razones e implementé la solución en un lenguaje funcional.

El resultado es bastante espectacular para mis ojos, el código es muy conciso, elegante y legible / fluido. Admito que puede parecer un poco extraño si nunca programó en otra cosa que no sea java / c #, pero esto sería cierto para cualquier cosa que no esté escrita en java / c #.

Sin embargo, en algún momento he sido literalmente atacado por un compañero de trabajo. Después de un rápido vistazo a mi pantalla, declaró que el código es incomprensible y que no debería reinventar el análisis, sino solo usar una pila y una cadena. Hizo mucho ruido y no pude convencerlo, en parte porque me sorprendió y no tuve una explicación clara, en parte porque su opinión era inmutable (sin juego de palabras). Incluso me ofrecí a explicarle el idioma, pero fue en vano.

Estoy seguro de que la discusión va a resurgir frente a la gerencia, así que estoy preparando algunos argumentos sólidos.

Estas son las primeras razones que se me ocurren para evitar una solución basada en String.Split:

  • necesita muchos ifs para manejar casos especiales y las cosas se descontrolan rápidamente
  • muchos índices de matriz codificados dificultan el mantenimiento
  • extremadamente difícil de manejar cosas como una llamada a función como argumento de método (ej. add ((add a, b), c)
  • Muy difícil proporcionar mensajes de error significativos en caso de errores de sintaxis (muy probable que suceda)
  • Estoy a favor de la simplicidad, la claridad y evitar cosas innecesariamente inteligentes y crípticas, pero también creo que es un error tonificar cada parte de la base de código para que incluso una hamburguesa pueda entenderlo. Es el mismo argumento que escucho por no usar interfaces, no adoptar la separación de preocupaciones, copiar y pegar código, etc. Después de todo, se requiere un mínimo de competencia técnica y disposición para aprender para trabajar en un proyecto de software. (No usaré este argumento, ya que probablemente sonará ofensivo, y comenzar una guerra no ayudará a nadie)

¿Cuáles son sus argumentos favoritos en contra de analizar el estilo Cthulhu ? *

* por supuesto, si puedes convencerme de que tiene razón, también seré perfectamente feliz


99
¡Me parece que su compañero de trabajo se ofrece como voluntario para hacer el proyecto DSL por usted!
GrandmasterB

23
"No debería reinventar el análisis, sino usar una pila y una cadena. Separar como todos" - maldita sea, ese tipo debería alegrarse de que la ignorancia no duela ...
Michael Borgwardt

44
Aconseje a su compañero de trabajo que no regrese a esta discusión a menos que lea todo el Libro del Dragón y pase una prueba. De lo contrario, no tiene derecho a discutir nada relacionado con el análisis.
SK-logic

44
perdón, ¿quién estaba reinventando el análisis?
rwong

2
Creo que mi cabeza explotará literalmente la próxima vez que vea a alguien usando la palabra "literalmente" en sentido figurado.

Respuestas:


33

La diferencia crítica entre los dos enfoques es que el que él considera la única forma correcta es imperativo y el suyo es declarativo.

  • Su enfoque declara explícitamente reglas, es decir, las reglas de la gramática están (casi) directamente codificadas en su código, y la biblioteca del analizador transforma automáticamente la entrada sin procesar en salida analizada, mientras se ocupa del estado y otras cosas que son difíciles de manejar. Su código está escrito dentro de una sola capa de abstracción, que coincide con el dominio del problema: análisis. Es razonable asumir la corrección de parsec, lo que significa que el único margen de error aquí es que su definición gramatical es incorrecta. Pero, de nuevo, tiene objetos de regla totalmente calificados y se prueban fácilmente de forma aislada. También vale la pena señalar que las bibliotecas de analizadores maduras se entregan con una característica importante: informes de errores. La recuperación de error decente cuando el análisis salió mal no es trivial. Como prueba, invoco PHP parse error, unexpected T_PAAMAYIM_NEKUDOTAYIM: D

  • Su enfoque manipula cadenas, mantiene explícitamente el estado y eleva la entrada sin formato manualmente a la entrada analizada. Debe escribir todo usted mismo, incluidos los informes de errores. Y cuando algo sale mal, estás totalmente perdido.

La ironía consiste en que la exactitud de un analizador escrito con su enfoque es relativamente fácil de probar. En su caso, es casi imposible.

Hay dos formas de construir un diseño de software: una es hacerla tan simple que obviamente no haya deficiencias, y la otra es hacerla tan complicada que no haya deficiencias obvias. El primer método es mucho más difícil.

COCHE Hoare

Su enfoque es el más simple. Todo lo que le impide es que amplíe un poco su horizonte. El resultado de su enfoque siempre será complicado, sin importar cuán amplio sea su horizonte.
Para ser honesto, me parece que el tipo es simplemente un tonto ignorante, que sufre del síndrome blub , lo suficientemente arrogante como para asumir que estás equivocado y gritarte, si no te entiende.

Al final, sin embargo, la pregunta es: ¿quién va a tener que mantenerlo? Si eres tú, entonces es tu decisión, no importa lo que digan. Si va a ser él, entonces solo hay dos posibilidades: encontrar una manera de hacerle entender la biblioteca del analizador o escribir un analizador imperativo para él. Le sugiero que lo genere a partir de su estructura de analizador: D


Excelente explicación de la diferencia entre los dos enfoques.
smarmy53

66
Aparentemente te has vinculado a TVTropes para programadores. Adiós tarde ...
Izkata

10

Una gramática de expresión de análisis (como el enfoque del analizador Packrat) o el combinador de analizador no reinventa el análisis. Estas son técnicas bien establecidas en el mundo de la programación funcional y, en las manos adecuadas, puede ser más legible que las alternativas. Hace unos años, he visto una demostración bastante convincente de PEG en C # que realmente la convertiría en mi herramienta de primer recurso para gramáticas relativamente simples.

Si tiene una solución elegante usando combinadores de analizador sintáctico o un PEG, debería ser una venta relativamente fácil: es bastante extensible, generalmente relativamente fácil de leer una vez que supera su miedo a la programación funcional, y a veces es más fácil de leer que el generador de analizador típico oferta de herramientas, aunque eso depende mucho de la gramática y del nivel de experiencia que tenga con cualquiera de los conjuntos de herramientas. También es bastante fácil escribir pruebas para. Por supuesto, hay algunas ambigüedades gramaticales que pueden dar como resultado un rendimiento de análisis bastante horrible en el peor de los casos (o un gran consumo de memoria con Packrat), pero el caso promedio es bastante decente y, de hecho, algunas ambigüedades gramaticales se manejan mejor con PEG que LALR, ya que Recuerdo.

Usar Split y una pila funciona con algunas gramáticas más simples que un PEG o puede admitir, pero es muy probable que con el tiempo reinicies mal el descenso recursivo o tengas un conjunto de comportamientos inestables que atacarás. ayuda para la presentación a costa de un código extremadamente desestructurado. Si solo tiene reglas de tokenización simples, probablemente no sea tan malo, pero a medida que agrega complejidad, probablemente será la solución menos mantenible. En su lugar, buscaría un generador de analizador.

Personalmente, mi primera inclinación cuando necesito construir un DSL sería usar algo como Boo (.Net) o Groovy (JVM), ya que obtengo toda la fuerza de un lenguaje de programación existente y una increíble personalización mediante la creación de macros y ajustes simples. a la canalización del compilador, sin tener que implementar las tediosas cosas que terminaría haciendo si comenzara desde cero (bucles, variables, modelo de objetos, etc.). Si estuviera en una tienda haciendo desarrollo de Ruby o Lisp, solo usaría los modismos que tienen sentido allí (metaprogramación, etc.)

Pero sospecho que tu problema real es sobre la cultura o el ego. ¿Estás seguro de que tu compañero de trabajo tampoco se habría asustado si hubieras usado Antlr o Flex / Bison? Sospecho que "discutir" por su solución puede ser una batalla perdida; Es posible que deba pasar más tiempo haciendo un enfoque más suave que utilice técnicas de creación de consenso en lugar de apelar a su autoridad de gestión local. Empareje la programación y demuestre la rapidez con la que puede realizar ajustes en la gramática sin sacrificar la capacidad de mantenimiento, y hacer una bolsa marrón para explicar la técnica, su historial, etc., puede ir más allá de 10 puntos de bala y un "Q&A grosero" en algunos reunión de confrontación


9

No conozco bien los algoritmos de análisis y similares, pero creo que la prueba del budín está en comer. Entonces, si todo lo demás falla, puedes ofrecerle que implemente el analizador a su manera. Luego

  • compare el tiempo invertido en cualquiera de las soluciones,
  • ejecutar ambas soluciones a través de una prueba de aceptación integral para ver cuál tiene menos errores, y
  • haga que un juez independiente compare el código resultante en tamaño y claridad con el suyo.

Para que las pruebas sean realmente justas, es posible que desee que ambas soluciones implementen la misma API y utilicen un banco de pruebas común (o un marco de prueba de unidad conocido por ambos). Ambos podrían escribir cualquier número y tipo de casos de prueba funcionales y asegurarse de que su propia solución los supere a todos. Y, por supuesto, idealmente ninguno de ustedes debería tener acceso a la implementación del otro antes de la fecha límite. La prueba decisiva sería entonces realizar una prueba cruzada de ambas soluciones utilizando el conjunto de pruebas desarrollado por el otro desarrollador.


esta es una gran idea! También sería fácil usar un marco de prueba de unidad commont.
smarmy53

1
+1 por hacer que el compañero de trabajo haga la versión dividida ... El OP fue el encargado de crearlo, por lo que es el que probablemente tendrá que apoyarlo, no el compañero de trabajo. Solo sugerírselo encima de su otro trabajo podría ser suficiente para quitárselo de encima.
Izkata

7

Ha hecho esto como si tuviera una pregunta técnica, pero como probablemente ya sabía, aquí no hay ninguna pregunta técnica. Su enfoque es muy superior a piratear algo a nivel de personaje.

El verdadero problema es que su colega (presumiblemente más experimentado) es inseguro y se siente amenazado por su conocimiento. No lo persuadirá con argumentos técnicos ; eso solo lo pondrá más a la defensiva. En cambio, tendrá que encontrar alguna manera de aliviar sus temores. No puedo ofrecer muchas sugerencias, pero puede intentar mostrar un gran respeto por su conocimiento del código heredado.

Finalmente, si su gerente está de acuerdo con sus argumentos técnicos engañosos y descarta su solución, entonces creo que tendrá que buscar otro puesto. Claramente, sería más valioso y más valorado en una organización más sofisticada.


Tienes razón, ya sabía que mi enfoque es superior, sin embargo, no pude dar una buena y convincente explicación: esa es la información técnica que estoy buscando. Convino en que el lado del problema de "interacción humana" es tan importante como el técnico (si no más).
smarmy53

4

Seré breve:

Analizar el camino de Cthulhu es difícil. Ese es el argumento más simple y convincente en su contra.

Puede hacer el truco para idiomas simples; digamos, idiomas regulares. Sin embargo, probablemente no será más fácil que una expresión regular.

También puede hacer el truco para idiomas un poco más complejos.

Sin embargo, me gustaría ver un analizador de Cthulhu para cualquier lenguaje con anidamiento, o simplemente "significativamente significativo" - expresiones matemáticas, o su ejemplo (llamadas de función anidadas).

Imagine lo que sucedería si alguien tratara de interpretar un analizador sintáctico para dicho lenguaje (no trivial sin contexto). Siempre que sea lo suficientemente inteligente como para escribir un analizador correcto, apuesto a que durante la codificación "descubrirá" primero el tokenizaton y luego el análisis de descenso recursivo, de alguna forma.

Después de eso, la cosa es simple: "¡Mira, has escrito algo que se llama un analizador de descenso recursivo! ¿Sabes que se puede generar automáticamente a partir de una simple descripción gramatical, al igual que las expresiones regulares?


Larga historia corta: lo
único que puede evitar que alguien use el enfoque civilizado es su ignorancia.


1

Quizás trabajar en una buena semántica DSL también es importante (la sintaxis importa, pero también la semántica). Si no está familiarizado con estos temas, le sugiero que lea algunos libros, como Programming Language Pragmatics (por M.Scott) y Christian Queinnec. Lisp En Piezas Pequeñas . Cambridge University Press, 1996.

Leer artículos recientes en las conferencias de DSL, por ejemplo, DSL2011 también debería ayudar.

Diseñar e implementar un lenguaje específico de dominio es difícil (¡y la mayor parte de la dificultad no es analizar!).

Realmente no entiendo lo que quieres decir al analizar el camino de Cthulhu ; Supongo que solo quieres analizar de alguna manera extraña.


Buenos enlaces. En cuanto a Cthulhu, lo siento, olvidé el enlace. Es una referencia a un artículo clásico de codinghorror: codinghorror.com/blog/2009/11/parsing-html-the-cthulhu-way.html . Actualicé la publicación original.
smarmy53
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.