Un simple intérprete Lisp en Umka

El desarrollo de mi lenguaje de secuencias de comandos escrito estáticamente, Umka, ha entrado en la etapa en la que se requería probar las capacidades del lenguaje utilizando ejemplos más complejos que secuencias de comandos en un par de docenas de líneas. Para ello, decidí implementar el intérprete Lisp en mi idioma . Me inspiré en un experimento pedagógico de Rob Pike, uno de los creadores del lenguaje Go. Pike publicó recientemente un pequeño intérprete Lisp en Go . Me impresionó particularmente el comentario de Pike de que la descripción del intérprete estaba en una página 13 del antiguo manual Lisp 1.5.... Dada la afinidad sintáctica de Umka y Go, fue difícil resistir la tentación de construir un intérprete de este tipo en Umka, no llevando literalmente el código de Pike, sino completamente desde cero. Espero que los conocedores del Lisp y los lenguajes funcionales me perdonen el ingenuo asombro de estar en contacto con la belleza.

Para los no iniciados, Lisp puede parecer tan cercano al shock. ¿Dónde está la línea entre el código y los datos? ¿Dónde están los bucles? ¿Dónde está la pila? La única estructura de datos es un árbol. También puede representar una lista. También se convierte en un árbol de sintaxis abstracto cuando se analiza el programa. También reemplaza la pila al evaluar expresiones. Se puede intentar ejecutar cualquier árbol como código o utilizarlo como datos. En lugar de bucles, recursividad. Ni siquiera hay aritmética en el núcleo del idioma. Sin embargo, es un lenguaje completo de Turing que puede expandirse infinitamente y espolvorearse con azúcar sintáctica.

Definir un intérprete Lisp mínimo realmente toma menos de una página. En un tramo, por supuesto: utiliza funciones definidas en varias páginas anteriores. El creador de Lisp, John McCarthy, parece haber hecho todo lo posible para superarse a sí mismo en concisión y, finalmente, publicó un micro-manual de Lisp que contiene la definición del idioma junto con la fuente del intérprete: dos páginas de revista en total. Es cierto, agregó al título: " No toda la verdad ".

El núcleo del lenguaje (aquí estamos hablando de los dialectos más antiguos y simples) requiere cinco funciones elementales, cuatro palabras clave y dos constantes que no se pueden expresar por medio del propio lenguaje.

Construcciones lingüísticas básicas para quienes no las conocen
  • (car x) - resaltando el encabezado de la lista x

  • (cdr x)x

  • (cons x y)x y

  • (atom x)x

  • (eq x y)x y

  • (cond (a x) (b y))x y a b

  • (quote x)x ,

  • ((lambda (x) a) y)a, x y

  • ((label ff (lambda (x) a)) y)ff

  • t

  • nil

, . , , , 6:

((label fac (lambda (n) (cond ((eq n 0) 1) ((quote t) (mul n (fac (sub n 1))))))) 6)

Lisp, . Lisp 1.5 13 , . . , REPL . , , , label defn, , . Lisp .

Dado que todo el intérprete está plagado de recursividad y procesamiento de árboles, sirvió como una prueba excelente para muchas de las características del lenguaje Umka, desde el apilado hasta la recolección de basura. Creo que a Umka le fue bien en la prueba. Tuvimos que corregir solo dos o tres errores menores relacionados con la exportación de nombres y declaraciones de tipo avanzadas. Todo el código del intérprete tenía menos de 400 líneas.

Para aquellos que quieran jugar con mi intérprete y transmitir la inspiración de Rob Pike a través del relevo, recomiendo tomar la carpeta con ejemplos de la rama maestra , y no de la última versión .




All Articles