File Coverage

blib/lib/Imager/Expr/Assem.pm
Criterion Covered Total %
statement 79 104 75.9
branch 38 54 70.3
condition 6 15 40.0
subroutine 6 6 100.0
pod 1 1 100.0
total 130 180 72.2


line stmt bran cond sub pod time code
1             package Imager::Expr::Assem;
2 3     3   58723 use 5.006;
  3         16  
3 3     3   15 use strict;
  3         5  
  3         53  
4 3     3   708 use Imager::Expr;
  3         6  
  3         72  
5 3     3   19 use Imager::Regops;
  3         6  
  3         3843  
6              
7             our $VERSION = "1.004";
8              
9             our @ISA = qw(Imager::Expr);
10              
11             __PACKAGE__->register_type('assem');
12              
13             sub compile {
14 1     1 1 3 my ($self, $expr, $opts) = @_;
15 1         2 my %nregs;
16 1         7 my @vars = $self->_variables();
17 1         3 my @nregs = (0) x @vars;
18 1         2 my @cregs;
19             my %vars;
20 1         4 @vars{@vars} = map { "r$_" } 0..$#vars;
  2         8  
21 1         4 my %labels;
22             my @ops;
23 1         0 my @msgs;
24 1         2 my $attr = \%Imager::Regops::Attr;
25              
26             # initially produce [ $linenum, $result, $opcode, @parms ]
27 1         1 my $lineno = 0;
28 1         7 while ($expr =~ s/^([^\n]+)(?:\n|$)//) {
29 10         15 ++$lineno;
30 10         18 my $line = $1;
31 10         13 $line =~ s/#.*//;
32 10 100       26 next if $line =~ /^\s*$/;
33 9         72 for my $op (split /;/, $line) {
34 10 100       33 if (my ($name, $type) = $op =~ /^\s*var\s+([^:]+):(\S+)\s*$/) {
35 3 50       8 if (exists $vars{$name}) {
36 0         0 push(@msgs, "$lineno: duplicate variable name '$name'");
37 0         0 next;
38             }
39 3 100 66     18 if ($type eq 'num' || $type eq 'n') {
    50 33        
      33        
40 2         7 $vars{$name} = 'r'.@nregs;
41 2         6 push(@nregs, undef);
42 2         7 next;
43             }
44             elsif ($type eq 'pixel' || $type eq 'p' || $type eq 'c') {
45 1         3 $vars{$name} = 'p'.@cregs;
46 1         2 push(@cregs, undef);
47 1         4 next;
48             }
49 0         0 push(@msgs, "$lineno: unknown variable type $type");
50 0         0 next;
51             }
52             # any statement can have a label
53 7 100       14 if ($op =~ s/^\s*(\w+):\s*//) {
54 1 50       5 if ($labels{$1}) {
55 0         0 push(@msgs,
56             "$lineno: duplicate label $1 (previous on $labels{$1}[1])");
57 0         0 next;
58             }
59 1         3 $labels{$1} = [ scalar @ops, $lineno ];
60             }
61 7 100       20 next if $op =~ /^\s*$/;
62             # jumps have special operand handling
63 6 50       55 if ($op =~ /^\s*jump\s+(\w+)\s*$/) {
    100          
    50          
    100          
    100          
    50          
64 0         0 push(@ops, [$lineno, "", "jump", $1]);
65             }
66             elsif (my ($code, $reg, $targ) =
67             ($op =~ /^\s*(jumpz|jumpnz)\s+(\S+)\s+(\S+)\s*$/)) {
68 1         6 push(@ops, [$lineno, "", $code, $reg, $targ]);
69             }
70             elsif ($op =~ /^\s*print\s+(\S+)\s*/) {
71 0         0 push(@ops, [$lineno, "", 'print', $1 ]);
72             }
73             elsif ($op =~ /^\s*ret\s+(\S+)\s*/) {
74 1         14 push(@ops, [$lineno, "", 'ret', $1]);
75             }
76             elsif ($op =~ s/\s*(\S+)\s*=\s*(\S+)\s*$//) {
77             # simple assignment
78 1         8 push(@ops, [$lineno, $1, "set", $2]);
79             }
80             elsif ($op =~ s/\s*(\S+)\s*=\s*(\S+)\s*//) {
81             # some normal ops finally
82 3         10 my ($result, $opcode) = ($1, $2);
83 3 50       9 unless ($attr->{$opcode}) {
84 0         0 push(@msgs, "$lineno: unknown operator $opcode");
85 0         0 next;
86             }
87 3         4 my @oper;
88 3         12 while ($op =~ s/(\S+)\s*//) {
89 6         19 push(@oper, $1);
90             }
91 3         26 push(@ops, [$lineno, $result, $opcode, @oper]);
92             }
93             else {
94 0         0 push(@msgs, "$lineno: invalid statement '$op'");
95             }
96             }
97             }
98              
99 1         3 my $max_opr = $Imager::Regops::MaxOperands;
100 1         9 my $numre = $self->numre;
101             my $trans =
102             sub {
103             # translate a name/number to a
104 13     13   18 my ($name) = @_;
105             $name = $self->{constants}{$name}
106 13 100       24 if exists $self->{constants}{$name};
107 13 100       48 if ($vars{$name}) {
    50          
108 10         30 return $vars{$name};
109             }
110             elsif ($name =~ /^$numre$/) {
111 3         11 $vars{$name} = 'r'.@nregs;
112 3         6 push(@nregs, $name);
113 3         8 return $vars{$name};
114             }
115             else {
116 0         0 push(@msgs, "$lineno: undefined variable $name");
117 0         0 return '';
118             }
119 1         6 };
120             # now to translate symbols and so on
121 1         3 OP: for my $op (@ops) {
122 6         10 $lineno = shift @$op;
123 6 50       22 if ($op->[1] eq 'jump') {
    100          
    50          
    100          
124 0 0       0 unless (exists $labels{$op->[2]}) {
125 0         0 push(@msgs, "$lineno: unknown label $op->[2]");
126 0         0 next;
127             }
128 0         0 $op = [ 'jump', "j$labels{$op->[2]}[0]", (0) x $max_opr ];
129             }
130             elsif ($op->[1] =~ /^jump/) {
131 1 50       5 unless (exists $labels{$op->[3]}) {
132 0         0 push(@msgs, "$lineno: unknown label $op->[2]");
133 0         0 next;
134             }
135 1         3 $op = [ $op->[1], $trans->($op->[2]), "j$labels{$op->[3]}[0]",
136             (0) x ($max_opr-1) ];
137             }
138             elsif ($op->[1] eq 'print') {
139 0         0 $op = [ $op->[1], $trans->($op->[2]), (0) x $max_opr ];
140             }
141             elsif ($op->[1] eq 'ret') {
142 1         3 $op = [ 'ret', $trans->($op->[2]), (0) x $max_opr ];
143             }
144             else {
145             # a normal operator
146 4         9 my ($result, $name, @parms) = @$op;
147              
148 4 50       56 if ($result =~ /^$numre$/) {
149 0         0 push(@msgs, "$lineno: target of operator cannot be a constant");
150 0         0 next;
151             }
152 4         10 $result = $trans->($result);
153 4         8 for my $parm (@parms) {
154 7         9 $parm = $trans->($parm);
155             }
156 4         8 push(@parms, (0) x ($max_opr-@parms));
157 4         13 $op = [ $op->[1], @parms, $result ];
158             }
159             }
160              
161             # more validation than a real assembler
162             # not trying to solve the halting problem...
163 1 50 33     11 if (@ops && $ops[-1][0] ne 'ret' && $ops[-1][0] ne 'jump') {
      33        
164 0         0 push(@msgs, ": the last instruction must be ret or jump");
165             }
166              
167 1         3 $self->{nregs} = \@nregs;
168 1         3 $self->{cregs} = \@cregs;
169              
170 1 50       9 if (@msgs) {
171 0         0 $self->error(join("\n", @msgs));
172 0         0 return 0;
173             }
174              
175 1         14 return \@ops;
176             }
177              
178             1;
179              
180             __END__