Principalmente, estaba mal el manejo de scopes, y se notaba en particular al crear lambdas ya que las capturas no funcionaban debidamente. No encontré una solución fácil al problema, entendiendo por "fácil" el hecho de que se resuelva cambiando unas pocas líneas en alguna función. Tuve que cambiar la estructura de datos subyacente que usaba para representar los ambientes, pasando de un simple mapa a una clase que representa una jerarquía de mapas enlazados, y debido a la complejidad de su ciclo de vida pasando de la "allocación" estática de los mismos al uso de std::shared_ptr.
El problema fue muy fácil de encontrar (solo avanzar con los ejemplos del libro), pero muy difícil de corregir, ya que el código reflejaba el hecho de que yo mismo no tenía claro cómo se suponía que debía funcionar. Tuve que echar mano a un intérprete real (usé mit-scheme) para probar, comparar, explorar, y jugar un poco hasta terminar de descifrar la verdadera lógica detrás de todo esto. Y eso no hizo más que corroborar lo que había adelantado: no hay mejor forma de aprender cómo funciona algo que explicándoselo a alguien que no tiene idea al respecto. Ese alguien (o algo), para muchas cuestiones algorítmicas, puede ser como en este caso el "sr. compilador".
Ahora las lambdas capturan los valores que deben capturar y funcionan correctamente!
En resumen, la segunda versión de este intérprete incrementa la longitud de su fuente en un 30%, pero a cambio, agrega nuevos operadores predefinidos, algo más de documentación, unos pocos detalles de eficiencia, y, sobre todo, logra resolver correctamente todos los ejemplos del primer capítulo del libro (me tomé el trabajo de armar un script que los verifique, así que puedo asegurarlo). Ahora voy por el segundo capítulo, donde se empieza a jugar con estructuras de datos, partiendo de "cons", "car" y "cdr", y hasta el momento eso también viene funcionando como debe.
Finalmente, para seguir jugando con C++, hice otra cosa nunca hago: usar excepciones para el manejo de errores. He usado antes bibliotecas que tiran excepciones, y conozco en detalle cómo funcionan, pero por diferentes razones nunca las elijo para mis propias bibliotecas. Sin embargo, en este caso, parece ser efectivamente la forma más simple y recomendable.
La evaluación detecta errores (mayormente de lógica, no cubro todos los de
sintaxis) y los retorna al bucle principal mediante el mecanismo de excepciones.
Así que este post no fue más que una excusa para compartir la nueva versión del intérprete. Aunque no lo considero mucho más que un juguete, no quería quedarme con la versión anterior después de descubrir esos errores. Ahora sí cumple lo que promete, aunque no sé hasta cuando. Retomo el libro de tanto en tanto, así que seguramente encontraré otro contra-ejemplo más adelante y tendré que volver a retocarlo.
>> me tomé el trabajo de armar un script que los verifique
ResponderEliminarpor favor puedes compartir ese script
PD: no logro compilar el archivo, muestra
using namespace std::literals;
error: 'literals' is not a namespace-name
El error es por la versión de C++. Hay que usar C++14... con gcc es pasarle -std=c++14 (o -std=c++1y según la versión de gcc).
EliminarAhora no tengo el script a mano con los ejemplos, más tarde los subo.
Realizo los siguientes pasos, copio el código en un archivo en blanco en ZINJAI, luego trato de compilar, ¿en que parte le paso el parámetro std=c++14?
ResponderEliminarEn las opciones de compilación (menú ejecución->opciones, argumentos extra), o usando la plantilla de c++14.
Eliminar