Esta es la última entrada sobre cómo hacer un intérprete LISP. Son las conclusiones.
Conclusiones
Empezamos describiendo los valores con los que íbamos a trabajar y dijimos que los guardaríamos en celdas. La idea de los valores elegidos era la homoiconicidad de forma que las propias expresiones del lenguaje fueran valores.
A continuación vimos cómo representar expresiones con celdas. También dijimos que la idea de la evaluación era convertir esas expresiones en otros valores.
Debido a que necesitábamos manipular las celdas empezamos a introducir operaciones sobre celdas.
Como también hacía falta inicializar y crear nuevas celdas, nos dedicamos a definir las operaciones correspondientes en el montículo.
Finalmente, en la quinta parte, nos dedicamos a definir la evaluación. La evaluación se apoyaba en la aplicación de las que teníamos dos variantes.
La primera era la predefinida. Estos eran los valores que llamábamos nativos y de los que dábamos la suma como ejemplo. También introdujimos el REPL como medio de comprobar que realmente la suma funcionaba.
La segunda forma de aplicación era usando una llamada compuesta. Es decir, definida por el usuario. Para eso teníamos las funciones lambda que guardábamos en objetos Lambda.
Para aplicar el objeto lambda realizábamos varios pasos: Creábamos un nuevo entorno local, vinculábamos los argumentos, comprobábamos la aridad y, finalmente, evaluábamos el cuerpo de la función lambda.
Hasta este punto habíamos hablado de entornos, pero no los habíamos definido. En la novena parte los definíamos.
Una vez que teníamos claro cómo usar los objetos lambda, lo único que nos faltaba era crearlos. Para eso introducíamos una nueva nativa.
Aunque mencionamos el REPL anteriormente, no lo habíamos especificado. En la undécima parte lo hacíamos.
En nuestra definición de celdas y entornos habíamos hablado repetidamente de los nombres. En la duodécima parte explicamos cómo usarlos y modificar su contenido. Además, introducimos la nativa que nos saca del REPL y otra que detiene la evaluación.
Cuando todo parecía terminado con las funciones lambda, nos damos cuenta que hace falta un pequeño detalle en nuestra implementación de los entornos. Debemos tener la capacidad de modificar nuestros entornos padre para poder realizar la clausura léxica correctamente.
La décimo cuarta parte es una colección de funciones nativas que posiblemente sean interesantes tener: operaciones con listas, formas especiales, formas especiales con booleanos y operaciones de inspección del estado del intérprete.
En la décimo quinta parte damos por concluida la realización del intérprete añadiendo las rutinas de lectura de expresiones y escritura de valores.
Las últimas cuatro partes (descontando ésta) las dedicamos a pensar en extensiones posibles.
Primero hablamos de mejoras básicas y casi siempre realizadas por un intérprete real: la recolección de basura y cache de celdas.
Luego, de otras extensiones menos básicas como las macros o carga de ficheros en tiempo de ejecución.
También exploramos la posibilidad de modificar la estructura del lenguaje. Vemos cómo podríamos llegar a Lua, Self o Smalltalk.
Finalmente, en vez de modificar el lenguaje, podríamos estandarizarlo. Hablábamos de los dos dialectos principales del lisp: Common Lisp y Scheme.
0 comentarios:
Publicar un comentario