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
fuente
Respuestas:
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
: DSu 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.
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
fuente
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
fuente
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
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.
fuente
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.
fuente
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.
fuente
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.
fuente