Implementando seu próprio Lisp

Implementar seu próprio dialeto Lisp é um exercício legal! E é divertido ver muitos avisos de recursão profunda do ” que-runs-si executar-a-lisp-que-é escrito-em-lispteste .

Alguns exemplos do shell PerLisp

(bind to-ten '(1 2 3 4 5 6 7 8 9 10))
(1 2 3 4 5 6 7 8 9 10)

(define (add-1 x) (+ x 1))

Function: (x) -> (+ x 1)

(map add-1 to-ten)

(2 3 4 5 6 7 8 9 10 11)


(filter (lambda (n) (= (% n 2) 0)) to-ten)

(2 4 6 8 10)


(define (sum l) (reduce + l 0))

Function: (l) -> (reduce + l 0)

(sum '
(1 -1))
0
(sum to-ten)
55

(define (product l) (reduce * l 1))
Function: (l) -> (reduce * l 1)
(product '(6 7))
42

(product to-ten)

3628800

Partes de codigo

O evalmétodo da minha classe PerLisp. Eu acho que é muito simples: ele transforma uma string de entrada em um fluxo de tokens que serão analisados ​​em uma lista de expressões. Todas as expressões devem implementar um evalmétodo que pode depender do contexto circundante. O valor de retorno do método é a lista de valores resultante ou (no contexto escalar) o primeiro valor:

sub eval {
my ($self, $string) = @_;

# lex
my $token_stream = $self->lexer->lex($string);

# parse
my @exprs = $self->parser->parse($token_stream);

# eval
my @values = map { $_->eval($self->context) } @exprs;

# return
return wantarray ? @values : shift @values;
}

Uma expressão de símbolo, por exemplo, procura-se no contexto fornecido:

sub eval {
my ($self, $context) = @_;
return $context->get($self->name);
}