File Coverage

blib/lib/App/Scheme79asm/Compiler.pm
Criterion Covered Total %
statement 130 130 100.0
branch 48 48 100.0
condition n/a
subroutine 28 28 100.0
pod 0 15 0.0
total 206 221 93.2


line stmt bran cond sub pod time code
1             package App::Scheme79asm::Compiler;
2              
3 1     1   53999 use 5.014000;
  1         11  
4 1     1   4 use strict;
  1         1  
  1         25  
5 1     1   4 use warnings;
  1         2  
  1         30  
6 1     1   365 use parent qw/Exporter/;
  1         263  
  1         4  
7              
8             our @EXPORT_OK = qw/pretty_print/;
9             our $VERSION = '0.005';
10              
11 1     1   64 use Carp qw/croak/;
  1         9  
  1         37  
12 1     1   499 use Data::Dumper qw/Dumper/;
  1         5478  
  1         48  
13 1     1   6 use Scalar::Util qw/looks_like_number/;
  1         2  
  1         36  
14              
15 1     1   346 use Data::SExpression qw/cons consp scalarp/;
  1         12645  
  1         4  
16 1     1   481 use List::MoreUtils qw/firstidx/;
  1         9637  
  1         4  
17              
18             our @PRIMOPS = qw/car cdr cons atom progn reverse-list/;
19              
20             BEGIN {
21 1     1   823 *cons = *Data::SExpression::cons;
22 1         2 *consp = *Data::SExpression::consp;
23 1         1102 *scalarp = *Data::SExpression::scalarp;
24             }
25              
26             # list processing routines
27             sub append {
28 30     30 0 463 my ($expr, $rest) = @_;
29 30 100       45 if (defined $expr) {
30 20         268 cons $expr->car, append($expr->cdr, $rest)
31             } else {
32 10         18 $rest
33             }
34             }
35              
36             sub mapcar (&@);
37              
38             sub mapcar (&@) {
39 41     41 0 129 my ($block, $expr) = @_;
40 41 100       64 if (defined $expr) {
41 24         24 my $result;
42 24         26 do {
43 24         309 local $_ = $expr->car;
44 24         90 $result = $block->()
45             };
46 24     10   416 cons $result, mapcar { $block->($_) } $expr->cdr
  10         13  
47             } else {
48             undef
49 17         33 }
50             }
51              
52             sub revacc {
53 14     14 0 219 my ($expr, $acc) = @_;
54 14 100       24 if (defined $expr) {
55 8         105 revacc ($expr->cdr, cons($expr->car, $acc))
56             } else {
57 6         14 $acc
58             }
59             }
60              
61             sub rev {
62 6     6 0 10 my ($expr) = @_;
63 6         10 revacc $expr, undef;
64             }
65              
66             sub positionacc {
67 37     37 0 110 my ($expr, $list, $acc) = @_;
68 37 100       497 if (!defined $list) {
    100          
69             undef
70 1         14 } elsif ($list->car eq $expr) {
71 16         869 $acc
72             } else {
73 20         1339 positionacc($expr, $list->cdr, $acc + 1)
74             }
75             }
76              
77             sub position {
78 17     17 0 24 my ($expr, $list) = @_;
79 17         25 positionacc $expr, $list, 0
80             }
81              
82             sub pretty_print {
83 354     354 0 6323 my ($expr) = @_;
84 354 100       560 if (!defined $expr) {
    100          
    100          
85 1         3 '()'
86             } elsif (scalarp $expr) {
87 217         1123 "$expr"
88             } elsif (ref $expr eq 'ARRAY') {
89 128         608 '(' . join (' ', map { pretty_print($_) } @$expr). ')'
  305         397  
90             } else {
91 8         132 my $cdr = $expr->cdr;
92 8         119 my $car = $expr->car;
93 8         27 my $acc = '(' . pretty_print($car);
94 8         254 while (defined $cdr) {
95 14         242 $car = $cdr->car;
96 14         519 $cdr = $cdr->cdr;
97 14         72 $acc .= ' ' . pretty_print($car);
98             }
99 8         181 $acc . ')'
100             }
101             }
102             # end list processing routines
103              
104             sub new {
105 27     27 0 114 my ($class) = @_;
106 27         85 my %self = (
107             symbols => ['', '', 'T'],
108             nsymbols => 3,
109             symbol_map => {},
110             );
111 27         82 bless \%self, $class;
112             }
113              
114             sub process_quoted {
115 47     47 0 2988 my ($self, $expr) = @_;
116 47 100       97 if (!defined $expr) { # nil
    100          
    100          
117 9         125 [LIST => 0]
118             } elsif (scalarp $expr) {
119 24         205 $expr = uc $expr;
120 24 100       331 if (!exists $self->{symbol_map}{$expr}) {
121 22         40 $self->{symbol_map}{$expr} = $self->{nsymbols};
122 22         26 $self->{nsymbols}++;
123 22         23 push @{$self->{symbols}}, $expr;
  22         50  
124             }
125 24         171 [SYMBOL => $self->{symbol_map}{$expr}]
126             } elsif (consp $expr) {
127 13         263 [LIST => $self->process_quoted($expr->cdr), $self->process_quoted($expr->car)]
128             } else {
129 1         15 croak 'argument to process_quoted is not a scalar, cons, or nil: ', Dumper($expr);
130             }
131             }
132              
133             sub process_proc {
134 6     6 0 12 my ($self, $func_name, $func_args, $func_body, $env) = @_;
135 6         10 my $new_env = append cons($func_name, rev $func_args), $env;
136 6         123 $self->process_toplevel($func_body, $new_env)
137             }
138              
139             sub rest_of_funcall {
140 28     28 0 633 my ($self, $func, $args) = @_;
141 28 100       46 if (!defined $args) {
142 16         206 $func
143             } else {
144 12         152 [MORE => $self->rest_of_funcall($func, $args->cdr), $args->car]
145             }
146             }
147              
148             sub process_funcall {
149 17     17 0 292 my ($self, $func_name, $func_args, $env) = @_;
150 17     59   68 my $prim_idx = firstidx { uc $_ eq uc $func_name } @PRIMOPS;
  59         1059  
151             my $processed_args =
152 17     24   506 mapcar { $self->process_toplevel($_, $env) } $func_args;
  24         55  
153 17 100       276 if ($prim_idx > -1) {
154 13 100       42 if (!defined $processed_args) {
155 1         3 croak "Cannot call primitive $func_name with no arguments";
156             }
157 12         39 [CALL => $self->rest_of_funcall([uc $func_name, 0], $processed_args->cdr), $processed_args->car]
158             } else {
159 4         8 my $final_args = append $processed_args, cons ($self->process_toplevel($func_name, $env), undef);
160 4         122 [CALL => $self->rest_of_funcall([FUNCALL => 0], $final_args->cdr), $final_args->car]
161             }
162             }
163              
164             sub process_toplevel {
165 68     68 0 15571 my ($self, $expr, $env) = @_;
166 68 100       176 if (!defined $expr) {
    100          
167 2         23 [LIST => 0]
168             } elsif (scalarp $expr) {
169 25 100       155 if (looks_like_number $expr) {
    100          
    100          
170 2         5 $self->process_quoted($expr);
171             } elsif (uc $expr eq 'T') {
172 4         272 [SYMBOL => 2]
173             } elsif (uc $expr eq 'NIL') {
174 2         211 [LIST => 0]
175             } else {
176 17         1582 my $position = position $expr, $env;
177 17 100       25 if (defined $position) {
178 16         74 [VAR => -1 - $position]
179             } else {
180 1         10 croak "Variable $expr not in environment";
181             }
182             }
183             } else {
184 41         699 my $func = uc $expr->car;
185 41 100       1421 if ($func eq 'QUOTE') {
    100          
    100          
186 14         179 $self->process_quoted($expr->cdr->car)
187             } elsif ($func eq 'LAMBDA') {
188 6         78 my $func_name = $expr->cdr->car;
189 6         177 my $func_args = $expr->cdr->cdr->car;
190 6         175 my $func_body = $expr->cdr->cdr->cdr->car; ## no critic (ProhibitLongChainsOfMethodCalls)
191 6         116 [PROC => $self->process_proc($func_name, $func_args, $func_body, $env)]
192             } elsif ($func eq 'IF') {
193             my ($if_cond, $if_then, $if_else) =
194 4         57 map { $self->process_toplevel($_, $env) }
  12         333  
195             ($expr->cdr->car, $expr->cdr->cdr->car, $expr->cdr->cdr->cdr->car); ## no critic (ProhibitLongChainsOfMethodCalls)
196 4         75 [IF => [LIST => $if_else, $if_then], $if_cond]
197             } else {
198 17         224 $self->process_funcall($expr->car, $expr->cdr, $env)
199             }
200             }
201             }
202              
203             sub compile_sexp {
204 10     10 0 19 my ($self, $expr) = @_;
205 10         21 $self->process_toplevel($expr, undef)
206             }
207              
208             sub compile_string {
209 10     10 0 15 my ($self, $string) = @_;
210 10         35 my $sexp = Data::SExpression->new(
211             {fold_lists => 0, use_symbol_class => 1}
212             );
213 10         959 my $expr = $sexp->read($string);
214 10         13796 $self->compile_sexp($expr)
215             }
216              
217             1;
218             __END__