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