package Lisp::Interpreter; use strict; use vars qw($DEBUG @EXPORT_OK $VERSION); $VERSION = sprintf("%d.%02d", q$Revision: 1.8 $ =~ /(\d+)\.(\d+)/); use Lisp::Symbol qw(symbol symbolp); use Lisp::Printer qw(lisp_print); use Lisp::Special qw(specialp); require Exporter; *import = \&Exporter::import; @EXPORT_OK = qw(lisp_eval lisp_read_eval_print); my $macro = symbol("macro"); my $lambda = symbol("lambda"); my $nil = symbol("nil"); # symbols in the argument list my $opt = symbol("&optional"); my $rest = symbol("&rest"); my $evalno = 0; sub lisp_eval { my $form = shift; my $no = ++$evalno; if ($DEBUG) { print "lisp_eval $evalno ", lisp_print($form), "\n"; } return $form unless ref($form); # a string or a number return $form->value if symbolp($form); my @args = @$form; my $func = shift(@args); while (symbolp($func)) { if ($func == $macro) { shift(@args); last; } elsif ($func == $lambda) { last; } else { $func = $func->function; } } unless (specialp($func) || $func == $macro) { # evaluate all arguments for (@args) { if (ref($_)) { if (symbolp($_)) { $_ = $_->value; } elsif (ref($_) eq "ARRAY") { $_ = lisp_eval($_); } else { # leave it as it is } } } } my $res; if (UNIVERSAL::isa($func, "CODE")) { $res = &$func(@args); } elsif (ref($func) eq "ARRAY") { if ($func->[0] == $lambda) { $res = lambda($func, \@args) } else { die "invalid-list-function (@{[lisp_print($func)]})"; } } else { die "invalid-function (@{[lisp_print($func)]})"; } if ($DEBUG) { print " $no ==> @{[lisp_print($res)]}\n"; } $res; } sub lambda # calling a lambda expression { my($lambda, $args) = @_; # set local variables require Lisp::Localize; my $local = Lisp::Localize->new; my $localvar = $lambda->[1]; my $do_opt; my $do_rest; my $i = 0; for my $sym (@$localvar) { if ($sym == $opt) { $do_opt++; } elsif ($sym == $rest) { $do_rest++; } elsif ($do_rest) { $local->save_and_set($sym, [ @{$args}[$i .. @$args-1] ] ); last; } elsif ($i < @$args || $do_opt) { $local->save_and_set($sym, $args->[$i]); $i++; } else { die "too-few-arguments"; } } if (!$do_rest && @$args > $i) { die "too-many-arguments"; } # execute the function body my $res = $nil; my $pc = 2; # starting here (0=lambda, 1=local variables) while ($pc < @$lambda) { $res = lisp_eval($lambda->[$pc]); $pc++; } $res; } sub lisp_read_eval_print { require Lisp::Reader; my $form = Lisp::Reader::lisp_read(join(" ", @_)); unshift(@$form, symbol("progn")) if ref($form->[0]) eq "ARRAY"; lisp_print(lisp_eval($form)); } 1;