2 use warnings FATAL
=> qw(all);
3 no if $] >= 5.018, warnings
=> "experimental::smartmatch";
5 use lib dirname
(__FILE__
);
6 use List
::Util
qw(pairs pairmap);
7 use readline qw(mal_readline set_rl_mode);
8 use feature
qw(switch);
11 use types
qw($nil $true $false _sequential_Q _symbol_Q _list_Q _clone);
16 use interop qw(pl_to_mal);
21 return reader
::read_str
($str);
27 return _sequential_Q
($x) && @
$x;
33 return Mal
::List
->new([Mal
::Symbol
->new("quote"), $ast]);
34 } elsif (_symbol_Q
($ast->[0]) && ${$ast->[0]} eq 'unquote') {
36 } elsif (is_pair
($ast->[0]) && _symbol_Q
($ast->[0]->[0]) &&
37 ${$ast->[0]->[0]} eq 'splice-unquote') {
38 return Mal
::List
->new([Mal
::Symbol
->new("concat"),
40 quasiquote
($ast->rest())]);
42 return Mal
::List
->new([Mal
::Symbol
->new("cons"),
43 quasiquote
($ast->[0]),
44 quasiquote
($ast->rest())]);
51 _symbol_Q
($ast->[0]) &&
52 $env->find($ast->[0])) {
53 my ($f) = $env->get($ast->[0]);
54 if ($f->isa('Mal::Function')) {
63 while (is_macro_call
($ast, $env)) {
65 my $mac = $env->get(shift @args);
74 if ($ast->isa('Mal::Symbol')) {
75 return $env->get($ast);
76 } elsif ($ast->isa('Mal::Sequence')) {
77 return ref($ast)->new([ map { EVAL
($_, $env) } @
$ast ]);
78 } elsif ($ast->isa('Mal::HashMap')) {
79 return Mal
::HashMap
->new({ pairmap
{ $a => EVAL
($b, $env) } %$ast });
90 #print "EVAL: " . printer::_pr_str($ast) . "\n";
91 if (! _list_Q
($ast)) {
92 return eval_ast
($ast, $env);
97 $ast = macroexpand
($ast, $env);
98 if (! _list_Q
($ast)) {
99 return eval_ast
($ast, $env);
102 my ($a0, $a1, $a2, $a3) = @
$ast;
103 if (!$a0) { return $ast; }
104 given ($a0->isa('Mal::Symbol') ?
$$a0 : $a0) {
106 my $res = EVAL
($a2, $env);
107 return $env->set($a1, $res);
110 my $let_env = Mal
::Env
->new($env);
111 foreach my $pair (pairs @
$a1) {
112 my ($k, $v) = @
$pair;
113 $let_env->set($k, EVAL
($v, $let_env));
117 # Continue loop (TCO)
122 when ('quasiquote') {
123 $ast = quasiquote
($a1);
124 # Continue loop (TCO)
127 my $func = _clone
(EVAL
($a2, $env));
128 $func->{ismacro
} = 1;
129 return $env->set($a1, $func);
131 when ('macroexpand') {
132 return macroexpand
($a1, $env);
135 return pl_to_mal
(eval(${$a1}));
142 use autodie
; # always "throw" errors
143 $ret = EVAL
($a1, $env);
147 if ($a2 && ${$a2->[0]} eq 'catch*') {
152 $exc = Mal
::String
->new(substr $err, 0, -1);
155 Mal
::Env
->new($env, Mal
::List
->new([$a2->[1]]),
156 Mal
::List
->new([$exc]));
157 return EVAL
($a2->[2], $catch_env);
166 eval_ast
($ast->slice(1, $#$ast-1), $env);
167 $ast = $ast->[$#$ast];
168 # Continue loop (TCO)
171 my $cond = EVAL
($a1, $env);
172 if ($cond eq $nil || $cond eq $false) {
173 $ast = $a3 ?
$a3 : $nil;
177 # Continue loop (TCO)
180 return Mal
::Function
->new(\
&EVAL
, $a2, $env, $a1);
183 my @el = @
{eval_ast
($ast, $env)};
185 if ($f->isa('Mal::Function')) {
187 $env = $f->gen_env(\
@el);
188 # Continue loop (TCO)
201 return printer
::_pr_str
($exp);
205 my $repl_env = Mal
::Env
->new();
208 return PRINT
(EVAL
(READ
($str), $repl_env));
211 # core.pl: defined using perl
212 foreach my $n (keys %core::ns
) {
213 $repl_env->set(Mal
::Symbol
->new($n), $core::ns
{$n});
215 $repl_env->set(Mal
::Symbol
->new('eval'),
216 bless sub { EVAL
($_[0], $repl_env); }, 'Mal::CoreFunction');
217 my @_argv = map {Mal
::String
->new($_)} @ARGV[1..$#ARGV];
218 $repl_env->set(Mal
::Symbol
->new('*ARGV*'), Mal
::List
->new(\
@_argv));
220 # core.mal: defined using the language itself
221 REP
(q
[(def
! *host
-language
* "perl")]);
222 REP
(q
[(def
! not (fn
* (a
) (if a false true
)))]);
223 REP
(q
[(def
! load
-file
(fn
* (f
) (eval (read-string
(str
"(do " (slurp f
) ")")))))]);
224 REP
(q
[(defmacro
! cond
(fn
* (& xs
) (if (> (count xs
) 0) (list
'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw "odd number of forms to cond")) (cons 'cond
(rest
(rest xs
)))))))]);
226 if (@ARGV && $ARGV[0] eq "--raw") {
231 REP
(qq[(load
-file
"$ARGV[0]")]);
234 REP
(q
[(println
(str
"Mal [" *host
-language
* "]"))]);
236 my $line = mal_readline
("user> ");
237 if (! defined $line) { last; }
242 use autodie
; # always "throw" errors
243 print(REP
($line), "\n");
247 if ($err->isa('Mal::BlankException')) {
248 # ignore and continue
251 print "Error: ".printer
::_pr_str
($err)."\n";
254 print "Error: $err\n";