File Coverage

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


line stmt bran cond sub pod time code
1             package App::Scheme79asm;
2              
3 1     1   56392 use 5.014000;
  1         3  
4 1     1   5 use strict;
  1         1  
  1         16  
5 1     1   4 use warnings;
  1         1  
  1         34  
6 1     1   5 use re '/s';
  1         1  
  1         72  
7 1     1   5 use Carp qw/croak/;
  1         1  
  1         48  
8              
9 1     1   526 use Data::Dumper qw/Dumper/;
  1         5887  
  1         62  
10 1     1   388 use Data::SExpression qw/consp scalarp/;
  1         14067  
  1         7  
11 1     1   39 use Scalar::Util qw/looks_like_number/;
  1         2  
  1         1243  
12              
13             our $VERSION = '1.000';
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 57 my ($self, $sexp, $location) = @_;
46 36 100       77 die 'Toplevel is not a list: ', Dumper($sexp), "\n" unless ref $sexp eq 'ARRAY';
47 35         59 my ($type, @addrs) = @$sexp;
48 35         43 my $addr;
49              
50 35 100       58 die 'Type of toplevel is not atom: '. Dumper($type), "\n" unless scalarp($type);
51              
52 34 100       185 if (@addrs > 1) {
53 8         10 $addr = $self->{freeptr} + 1;
54 8         11 $self->{freeptr} += @addrs;
55 8         25 $self->process($addrs[$_], $addr + $_) for 0 .. $#addrs;
56             } else {
57 26         34 $addr = $addrs[0];
58             }
59              
60 34 100       67 $addr = $self->process($addr) if ref $addr eq 'ARRAY';
61 34 100       54 die 'Addr of toplevel is not atom: ', Dumper($addr), "\n" unless scalarp($addr);
62 33         147 my ($comment_type, $comment_addr) = ($type, $addr);
63 33 100       77 die 'Computed addr is not a number: ', Dumper($addr), "\n" unless looks_like_number $addr;
64              
65 32 100       96 if (!looks_like_number $type) {
66 30 100       1089 die "No such type: $type\n" unless exists $TYPES{$type};
67 29         944 $type = $TYPES{$type};
68             }
69              
70 31 100       975 $addr += (1 << $self->{addr_bits}) if $addr < 0;
71 31 100       82 die "Type too large: $type\n" if $type >= (1 << $self->{type_bits});
72 30 100       73 die "Addr too large: $addr\n" if $addr >= (1 << $self->{addr_bits});
73 29         34 my $result = ($type << $self->{addr_bits}) + $addr;
74              
75 29 100       39 unless ($location) {
76 13         15 $self->{freeptr}++;
77             $location = $self->{freeptr}
78 13         24 }
79 29         46 $self->{memory}[$location] = $result;
80 29         53 $self->{comment}[$location] = "$comment_type $comment_addr";
81 29         997 $location
82             }
83              
84             sub parse {
85 15     15 1 20 my ($self, $string) = @_;
86 15         75 my $ds = Data::SExpression->new({symbol_case => 'up', use_symbol_class => 1, fold_lists => 1});
87              
88 15         1677 my $sexp;
89 15         20 while () {
90 24 100       273 last if $string =~ /^\s*$/;
91 15         33 ($sexp, $string) = $ds->read($string);
92 15         24833 $self->process($sexp)
93             }
94             }
95              
96             sub finish {
97 9     9 1 14 my ($self) = @_;
98 9         15 $self->{memory}[5] = $self->{memory}[$self->{freeptr}];
99 9         13 $self->{comment}[5] = $self->{comment}[$self->{freeptr}];
100 9         12 $self->{memory}[4] = $self->{freeptr};
101 9         16 delete $self->{memory}[$self->{freeptr}]
102             }
103              
104             sub new {
105 16     16 1 8771 my ($class, %args) = @_;
106 16   100     95 $args{type_bits} //= 3;
107 16   100     42 $args{addr_bits} //= 8;
108 16   100     46 $args{freeptr} //= 6;
109 16   100     65 $args{memory} //= [0, 0, (1<<$args{addr_bits}), (1<<$args{addr_bits}), 0, 0, 0];
110 16         34 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         37 for (0 .. $#default_comments) {
112 112         161 $args{comment}[$_] = $default_comments[$_]
113             }
114 16         57 bless \%args, $class
115             }
116              
117             sub print_binary16 {
118 5     5 1 8 my ($self, $fh) = @_;
119 5   50     10 $fh //= \*STDOUT; # uncoverable condition right
120              
121 5 100       20 die "addr_bits + type_bits >= 16\n"if $self->{addr_bits} + $self->{type_bits} > 16;
122              
123 4         4 my $length = @{$self->{memory}};
  4         7  
124 4 50       21 print $fh pack 'n', $length or croak "Failed to print memory size: $!"; # uncoverable branch true
125 4         7 for (@{$self->{memory}}) {
  4         8  
126 41 50       70 print $fh pack 'n', $_ or croak "Failed to print memory: $!" # uncoverable branch true
127             }
128             }
129              
130             sub print_verilog {
131 4     4 1 6 my ($self, $fh) = @_;
132 4   50     8 $fh //= \*STDOUT; # uncoverable condition right
133              
134 4         9 my $bits = $self->{type_bits} + $self->{addr_bits};
135 4         4 my $index_length = length $#{$self->{memory}};
  4         9  
136 4         10 my $index_format = '%' . $index_length . 'd';
137 4         7 for my $index (0 .. $#{$self->{memory}}) {
  4         10  
138 41         53 my $val = $self->{memory}[$index];
139 41         44 my $comment = $self->{comment}[$index];
140 41 100       50 if ($index == 4) {
141 4         7 $val = "${bits}'d$val"
142             } else {
143 37 100       78 $val = $val ? sprintf "%d'b%0${bits}b", $bits, $val : '0';
144             }
145 41         62 my $spaces = ' ' x ($bits + 5 - (length $val));
146 41         59 $index = sprintf $index_format, $index;
147              
148 41         53 my $string = "mem[$index] <= $val;";
149 41 100       74 $string .= "$spaces // $comment" if defined $comment;
150 41 50       104 say $fh $string or croak "Failed to print verilog: $!"; # uncoverable branch true
151             }
152              
153             }
154             sub parse_and_print_binary16 {
155 11     11 1 37 my ($self, $string, $fh) = @_;
156 11         25 $self->parse($string);
157 5         16 $self->finish;
158 5         11 $self->print_binary16($fh);
159             }
160              
161             sub parse_and_print_verilog {
162 4     4 1 15 my ($self, $string, $fh) = @_;
163 4         10 $self->parse($string);
164 4         11 $self->finish;
165 4         10 $self->print_verilog($fh);
166             }
167              
168             1;
169             __END__