1 package Imager::Expr::Assem;
7 @ISA = qw(Imager::Expr);
9 __PACKAGE__->register_type('assem');
12 my ($self, $expr, $opts) = @_;
14 my @vars = $self->_variables();
15 my @nregs = (0) x @vars;
18 @vars{@vars} = map { "r$_" } 0..$#vars;
22 my $attr = \%Imager::Regops::Attr;
24 # initially produce [ $linenum, $result, $opcode, @parms ]
26 while ($expr =~ s/^([^\n]+)(?:\n|$)//) {
30 next if $line =~ /^\s*$/;
31 for my $op (split /;/, $line) {
32 if (my ($name, $type) = $op =~ /^\s*var\s+([^:]+):(\S+)\s*$/) {
33 if (exists $vars{$name}) {
34 push(@msgs, "$lineno: duplicate variable name '$name'");
37 if ($type eq 'num' || $type eq 'n') {
38 $vars{$name} = 'r'.@nregs;
42 elsif ($type eq 'pixel' || $type eq 'p' || $type eq 'c') {
43 $vars{$name} = 'p'.@cregs;
47 push(@msgs, "$lineno: unknown variable type $type");
50 # any statement can have a label
51 if ($op =~ s/^\s*(\w+):\s*//) {
54 "$lineno: duplicate label $1 (previous on $labels{$1}[1])");
57 $labels{$1} = [ scalar @ops, $lineno ];
59 next if $op =~ /^\s*$/;
60 # jumps have special operand handling
61 if ($op =~ /^\s*jump\s+(\w+)\s*$/) {
62 push(@ops, [$lineno, "", "jump", $1]);
64 elsif (my ($code, $reg, $targ) =
65 ($op =~ /^\s*(jumpz|jumpnz)\s+(\S+)\s+(\S+)\s*$/)) {
66 push(@ops, [$lineno, "", $code, $reg, $targ]);
68 elsif ($op =~ /^\s*print\s+(\S+)\s*/) {
69 push(@ops, [$lineno, "", 'print', $1 ]);
71 elsif ($op =~ /^\s*ret\s+(\S+)\s*/) {
72 push(@ops, [$lineno, "", 'ret', $1]);
74 elsif ($op =~ s/\s*(\S+)\s*=\s*(\S+)\s*$//) {
76 push(@ops, [$lineno, $1, "set", $2]);
78 elsif ($op =~ s/\s*(\S+)\s*=\s*(\S+)\s*//) {
79 # some normal ops finally
80 my ($result, $opcode) = ($1, $2);
81 unless ($attr->{$opcode}) {
82 push(@msgs, "$lineno: unknown operator $opcode");
86 while ($op =~ s/(\S+)\s*//) {
89 push(@ops, [$lineno, $result, $opcode, @oper]);
92 push(@msgs, "$lineno: invalid statement '$op'");
97 my $max_opr = $Imager::Regops::MaxOperands;
98 my $numre = $self->numre;
101 # translate a name/number to a <type><digits>
103 $name = $self->{constants}{$name}
104 if exists $self->{constants}{$name};
108 elsif ($name =~ /^$numre$/) {
109 $vars{$name} = 'r'.@nregs;
114 push(@msgs, "$lineno: undefined variable $name");
118 # now to translate symbols and so on
119 OP: for my $op (@ops) {
120 $lineno = shift @$op;
121 if ($op->[1] eq 'jump') {
122 unless (exists $labels{$op->[2]}) {
123 push(@msgs, "$lineno: unknown label $op->[2]");
126 $op = [ 'jump', "j$labels{$op->[2]}[0]", (0) x $max_opr ];
128 elsif ($op->[1] =~ /^jump/) {
129 unless (exists $labels{$op->[3]}) {
130 push(@msgs, "$lineno: unknown label $op->[2]");
133 $op = [ $op->[1], $trans->($op->[2]), "j$labels{$op->[3]}[0]",
134 (0) x ($max_opr-1) ];
136 elsif ($op->[1] eq 'print') {
137 $op = [ $op->[1], $trans->($op->[2]), (0) x $max_opr ];
139 elsif ($op->[1] eq 'ret') {
140 $op = [ 'ret', $trans->($op->[2]), (0) x $max_opr ];
144 my ($result, $name, @parms) = @$op;
146 if ($result =~ /^$numre$/) {
147 push(@msgs, "$lineno: target of operator cannot be a constant");
150 $result = $trans->($result);
151 for my $parm (@parms) {
152 $parm = $trans->($parm);
154 push(@parms, (0) x ($max_opr-@parms));
155 $op = [ $op->[1], @parms, $result ];
159 # more validation than a real assembler
160 # not trying to solve the halting problem...
161 if (@ops && $ops[-1][0] ne 'ret' && $ops[-1][0] ne 'jump') {
162 push(@msgs, ": the last instruction must be ret or jump");
165 $self->{nregs} = \@nregs;
166 $self->{cregs} = \@cregs;
169 $self->error(join("\n", @msgs));
182 Imager::Expr::Assem - an assembler for producing code for the Imager
187 use Imager::Expr::Assem;
188 my $expr = Imager::Expr->new(assem=>'...', ...)
192 This module is a simple Imager::Expr compiler that compiles a
193 low-level language that has a nearly 1-to-1 relationship to the
194 internal representation used for compiled regmach code.
198 Each line can contain multiple statements separated by semi-colons.
200 Anything after '#' in a line is ignored.
206 =item variable definition
210 C<var> I<name>:I<type>
214 defines variable I<name> to have I<type>, which can be any of 'n' or
215 'num' for a numeric type or 'pixel', 'p' or 'c' for a pixel or color
218 Variable names cannot include whitespace.
222 Operators can be split into 3 basic types, those that have a result
223 value, those that don't and the null operator, eg. jump has no value.
225 The format for operators that return a value is typically:
229 I<result> = I<operator> I<operand> ...
233 and for those that don't return a value:
237 I<operator> I<operand>
241 where operator is any valid regmach operator, result is any variable
242 defined with C<var>, and operands are variables, constants or
243 literals, or for jump operators, labels.
245 The set operator can be simplified to:
249 I<result> = I<operator>
253 All operators maybe preceded by a label, which is any non-whitespace
254 text immediately followed by a colon (':').
260 Note that the current optimizer may produce incorrect optimization for
261 your code, fortunately the optimizer will disable itself if you
262 include any jump operator in your code. A single jump to anywhere
263 after your final ret operator can be used to disable the optimizer
264 without slowing down your code.
266 There's currently no high-level code generation that can generate code
267 with loops or real conditions.
271 Imager(3), transform.perl, regmach.c
275 Tony Cook <tony@develop-help.com>