File Coverage

blib/lib/App/Scheme79asm.pm
Criterion Covered Total %
statement 106 106 100.0
branch 37 40 92.5
condition 10 12 100.0
subroutine 16 16 100.0
pod 8 8 100.0
total 177 182 98.3


line stmt bran cond sub pod time code
1             package App::Scheme79asm;
2              
3 1     1   79400 use 5.014000;
  1         4  
4 1     1   6 use strict;
  1         3  
  1         24  
5 1     1   6 use warnings;
  1         2  
  1         52  
6 1     1   8 use re '/s';
  1         2  
  1         97  
7 1     1   8 use Carp qw/croak/;
  1         2  
  1         65  
8              
9 1     1   672 use Data::Dumper qw/Dumper/;
  1         7937  
  1         85  
10 1     1   520 use Data::SExpression qw/consp scalarp/;
  1         18936  
  1         10  
11 1     1   54 use Scalar::Util qw/looks_like_number/;
  1         2  
  1         1607  
12              
13             our $VERSION = '0.005001';
14              
15             our %TYPES = (
16             LIST => 0,
17             SYMBOL => 1,
18             NUMBER => 1,
19             VAR => 2,
20             VARIABLE => 2,
21             CLOSURE => 3,
22             PROC => 4,
23             PROCEDURE => 4,
24             IF => 5,
25             COND => 5,
26             CONDITIONAL => 5,
27             CALL => 6,
28             QUOTE => 7,
29             QUOTED => 7,
30              
31             MORE => 0,
32             CAR => 1,
33             CDR => 2,
34             CONS => 3,
35             ATOM => 4,
36             PROGN => 5,
37             'REVERSE-LIST' => 6,
38             FUNCALL => 7,
39             );
40              
41             *consp = *Data::SExpression::consp;
42             *scalarp = *Data::SExpression::scalarp;
43              
44             sub process {
45 36     36 1 92 my ($self, $sexp, $location) = @_;
46 36 100       123 die 'Toplevel is not a list: ', Dumper($sexp), "\n" unless ref $sexp eq 'ARRAY';
47 35         144 my ($type, @addrs) = @$sexp;
48 35         69 my $addr;
49              
50 35 100       120 die 'Type of toplevel is not atom: '. Dumper($type), "\n" unless scalarp($type);
51              
52 34 100       321 if (@addrs > 1) {
53 8         18 $addr = $self->{freeptr} + 1;
54 8         18 $self->{freeptr} += @addrs;
55 8         43 $self->process($addrs[$_], $addr + $_) for 0 .. $#addrs;
56             } else {
57 26         56 $addr = $addrs[0];
58             }
59              
60 34 100       110 $addr = $self->process($addr) if ref $addr eq 'ARRAY';
61 34 100       84 die 'Addr of toplevel is not atom: ', Dumper($addr), "\n" unless scalarp($addr);
62 33         236 my ($comment_type, $comment_addr) = ($type, $addr);
63 33 100       123 die 'Computed addr is not a number: ', Dumper($addr), "\n" unless looks_like_number $addr;
64              
65 32 100       154 if (!looks_like_number $type) {
66 30 100       1738 die "No such type: $type\n" unless exists $TYPES{$type};
67 29         1560 $type = $TYPES{$type};
68             }
69              
70 31 100       1525 $addr += (1 << $self->{addr_bits}) if $addr < 0;
71 31 100       136 die "Type too large: $type\n" if $type >= (1 << $self->{type_bits});
72 30 100       124 die "Addr too large: $addr\n" if $addr >= (1 << $self->{addr_bits});
73 29         60 my $result = ($type << $self->{addr_bits}) + $addr;
74              
75 29 100       76 unless ($location) {
76 13         22 $self->{freeptr}++;
77             $location = $self->{freeptr}
78 13         36 }
79 29         67 $self->{memory}[$location] = $result;
80 29         85 $self->{comment}[$location] = "$comment_type $comment_addr";
81 29         1637 $location
82             }
83              
84             sub parse {
85 15     15 1 40 my ($self, $string) = @_;
86 15         119 my $ds = Data::SExpression->new({symbol_case => 'up', use_symbol_class => 1, fold_lists => 1});
87              
88 15         2738 my $sexp;
89 15         30 while () {
90 24 100       455 last if $string =~ /^\s*$/;
91 15         51 ($sexp, $string) = $ds->read($string);
92 15         39567 $self->process($sexp)
93             }
94             }
95              
96             sub finish {
97 9     9 1 23 my ($self) = @_;
98 9         27 $self->{memory}[5] = $self->{memory}[$self->{freeptr}];
99 9         29 $self->{comment}[5] = $self->{comment}[$self->{freeptr}];
100 9         19 $self->{memory}[4] = $self->{freeptr};
101 9         28 delete $self->{memory}[$self->{freeptr}]
102             }
103              
104             sub new {
105 16     16 1 13705 my ($class, %args) = @_;
106 16   100     137 $args{type_bits} //= 3;
107 16   100     86 $args{addr_bits} //= 8;
108 16   100     81 $args{freeptr} //= 6;
109 16   100     100 $args{memory} //= [0, 0, (1<<$args{addr_bits}), (1<<$args{addr_bits}), 0, 0, 0];
110 16         61 my @default_comments = ('(cdr part of NIL)', '(car part of NIL)', '(cdr part of T)', '(car part of T)', '(free storage pointer)', '', '(result of computation)');
111 16         56 for (0 .. $#default_comments) {
112 112         261 $args{comment}[$_] = $default_comments[$_]
113             }
114 16         98 bless \%args, $class
115             }
116              
117             sub print_binary16 {
118 5     5 1 11 my ($self, $fh) = @_;
119 5   50     18 $fh //= \*STDOUT; # uncoverable condition right
120              
121 5 100       32 die "addr_bits + type_bits >= 16\n"if $self->{addr_bits} + $self->{type_bits} > 16;
122              
123 4         7 my $length = @{$self->{memory}};
  4         10  
124 4 50       35 print $fh pack 'n', $length or croak "Failed to print memory size: $!";
125 4         9 for (@{$self->{memory}}) {
  4         15  
126 41 50       126 print $fh pack 'n', $_ or croak "Failed to print memory: $!"
127             }
128             }
129              
130             sub print_verilog {
131 4     4 1 13 my ($self, $fh) = @_;
132 4   50     14 $fh //= \*STDOUT; # uncoverable condition right
133              
134 4         13 my $bits = $self->{type_bits} + $self->{addr_bits};
135 4         8 my $index_length = length $#{$self->{memory}};
  4         16  
136 4         15 my $index_format = '%' . $index_length . 'd';
137 4         9 for my $index (0 .. $#{$self->{memory}}) {
  4         17  
138 41         82 my $val = $self->{memory}[$index];
139 41         76 my $comment = $self->{comment}[$index];
140 41 100       90 if ($index == 4) {
141 4         10 $val = "${bits}'d$val"
142             } else {
143 37 100       121 $val = $val ? sprintf "%d'b%0${bits}b", $bits, $val : '0';
144             }
145 41         99 my $spaces = ' ' x ($bits + 5 - (length $val));
146 41         101 $index = sprintf $index_format, $index;
147              
148 41         100 my $string = "mem[$index] <= $val;";
149 41 100       107 $string .= "$spaces // $comment" if defined $comment;
150 41 50       166 say $fh $string or croak "Failed to print verilog: $!";
151             }
152              
153             }
154             sub parse_and_print_binary16 {
155 11     11 1 59 my ($self, $string, $fh) = @_;
156 11         41 $self->parse($string);
157 5         26 $self->finish;
158 5         19 $self->print_binary16($fh);
159             }
160              
161             sub parse_and_print_verilog {
162 4     4 1 26 my ($self, $string, $fh) = @_;
163 4         15 $self->parse($string);
164 4         21 $self->finish;
165 4         16 $self->print_verilog($fh);
166             }
167              
168             1;
169             __END__