Um intérprete Lisp simples em Umka

O desenvolvimento da minha linguagem de script Umka digitada estaticamente entrou no estágio em que foi necessário testar os recursos da linguagem usando exemplos mais complexos do que scripts em algumas dezenas de linhas. Para isso, decidi implementar o interpretador Lisp na minha linguagem . Fui inspirado por um experimento pedagógico de Rob Pike, um dos criadores da linguagem Go. Pike publicou recentemente um pequeno intérprete Lisp em Go . Fiquei particularmente impressionado com a observação de Pike de que a descrição do intérprete estava em uma página 13 do antigo manual do Lisp 1.5.... Dada a afinidade sintática de Umka e Go, era difícil resistir à tentação de construir tal interpretador em Umka, não transferindo literalmente o código de Pike, mas completamente do zero. Espero que os conhecedores do Lisp e das linguagens funcionais me perdoem o espanto ingênuo de estar em contato com a beleza.

Para os não iniciados, Lisp pode chegar perto do choque. Onde está a linha entre código e dados? Onde estão os loops? Onde está a pilha? A única estrutura de dados é uma árvore. Também pode representar uma lista. Também se torna uma árvore de sintaxe abstrata quando o programa é analisado. Ele também substitui a pilha ao avaliar expressões. Qualquer árvore pode ser tentada para ser executada como código ou usada como dados. Em vez de loops - recursão. Não há nem aritmética no cerne da linguagem. Ainda assim, é uma linguagem Turing completa que pode ser expandida infinitamente e polvilhada com açúcar sintático.

Definir um interpretador Lisp mínimo realmente leva menos de uma página. Em um trecho, é claro: ele usa funções definidas em várias páginas anteriores. O criador do Lisp, John McCarthy, parece ter saído de seu caminho para se superar em concisão e eventualmente publicou um micro-manual Lisp contendo a definição da linguagem junto com a fonte do intérprete - duas páginas de revista no total. É verdade, ele acrescentou ao título: " Não é toda a verdade ".

O núcleo da linguagem (aqui estamos falando dos dialetos mais antigos e simples) requer cinco funções elementares, quatro palavras-chave e duas constantes que não podem ser expressas por meio da própria linguagem.

Construções básicas de linguagem para quem não está familiarizado com elas
  • (car x) - destacando o topo da 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 .

Como o interpretador inteiro está repleto de recursão e processamento de árvore, ele serviu como um excelente teste para muitos dos recursos da linguagem Umka, desde o empilhamento até a coleta de lixo. Acho que Umka se saiu bem no teste. Tivemos que corrigir apenas dois ou três pequenos bugs relacionados à exportação de nomes e declarações de tipo avançadas. Todo o código do interpretador tinha menos de 400 linhas.

Para aqueles que desejam brincar com meu intérprete e passar a inspiração de Rob Pike por meio do relé, recomendo pegar a pasta com exemplos do branch master , e não da versão mais recente .




All Articles