Parsear e interpretar o LISP reduzido.
Toda a <EXPRESSION>
da linguagem é ou atômica ou composta.
Formas atômicas
Um átomo é um símbolo (dá pra representar como uma String
) sem parênteses e
que pode ser:
- Um número (pensei em representar todos os números com
BigDecimal
).
- Um booleano
true
ou false
.
- Se um átomo não for nenhum dos dois, consideramos ele como uma variável.
No geral átomos se avaliam sozinhos.
Avaliar uma variável é buscar o seu valor definido no escopo atual do Environment
.
Formas compostas
Toda forma composta é delimitada por parênteses e dependendo do que houver na primeira subexpressão são avaliadas de formas especiais ou não:
-
(begin <FIRST> <REST...>)
Subexpressões são avaliadas na ordem em que estão escritas, com pelo menos uma expressão.
O valor resultante ("retorno") é o
da última expressão.
-
(define symbol <VAL>)
Associa um símbolo ao valor de uma expressão, colocando essa definição no Frame
mais interno (via Environment
) do interpretador.
O retorno é indefinido e para isso vamos usar um valor especial VOID
.
-
(if <COND> <CONSEQ> <ALT>)
Avalia a condição e se essa tiver um valor associado à verdadeiro então avalia a consequência, caso contrário avalia a alternativa.
O retorno é igual ao da última expressão avaliada, ou seja o da consequência ou da alternativa.
-
(lambda (a0 a1 ... aN) <FIRST> <REST...>)
Retorna um valor Procedure
que armazena o escopo léxico atual, ou seja um closure.
Isso significa que a avaliação do corpo do lambda é deixada pra mais tarde, quando for invocado e houverem valores dados como argumentos.
-
Se a forma composta não for caracterizada por alguma das palavras reservadas acima, então é a aplicação de um procedimento, por exemplo (foo 1 (bar 2) 3)
Aplicação de procedimento
O resultado da avaliação de uma expressão composta usual é dado por aplicar o procedimento (obtido pela avaliação da primeira subexpressão) considerando os valores das demais subexpressões como a lista de argumentos dessa aplicação.
Para fazer o equivalente à substituir os parâmetros formais pelos argumentos fornecidos, basta criar uma nova Frame
na qual estão associados os nomes dos parâmetros aos valores passados na evocação.
Depois, o resultado da aplicação é o mesmo de avaliar o corpo do procedimento nesse novo Environment
extendido.