11 shift if UNIVERSAL::isa($_[0], 'Imager::Expr');
21 my %default_constants =
23 # too many digits, better than too few
24 pi=>3.14159265358979323846264338327950288419716939937510582097494
28 my ($class, $opts) = @_;
30 # possibly this is a very bad idea
31 my ($type) = grep exists $expr_types{$_}, keys %$opts;
32 die "Imager::Expr: No known expression type"
34 my $self = bless {}, $expr_types{$type};
35 $self->{variables} = [ @{$opts->{variables}} ];
36 $self->{constants} = { %default_constants, %{$opts->{constants} || {}} };
37 $self->{ops} = $self->compile($opts->{$type}, $opts)
41 $self->{code} = $self->assemble()
47 my ($pack, $name) = @_;
48 $expr_types{$name} = $pack;
52 return @{$_[0]->{variables}};
60 return $_[0]->{nregs};
64 return $_[0]->{cregs};
67 my $numre = '[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?';
77 my @ops = @{$self->{ops}};
79 # this function cannot current handle code with jumps
80 return 1 if grep $_->[0] =~ /^jump/, @ops;
82 # optimization - common sub-expression elimination
83 # it's possible to fold this into the code generation - but it will wait
85 my $max_opr = $Imager::Regops::MaxOperands;
86 my $attr = \%Imager::Regops::Attr;
95 my $desc = join(",", @{$op}[0..$max_opr]);
99 my $new = $seen{$desc};
101 for my $reg (@{$op}[1..$max_opr]) {
102 $reg = $new if $reg eq $old;
108 $seen{$desc} = $op->[-1];
115 # reduce division by a constant to multiplication by a constant
116 if ($op->[0] eq 'div' && $op->[2] =~ /^r(\d+)/
117 && defined($self->{"nregs"}[$1])) {
118 my $newreg = @{$self->{"nregs"}};
119 push(@{$self->{"nregs"}}, 1.0/$self->{"nregs"}[$1]);
121 $op->[2] = 'r'.$newreg;
124 $self->{ops} = \@ops;
130 my $attr = \%Imager::Regops::Attr;
131 my $max_opr = $Imager::Regops::MaxOperands;
132 my @ops = @{$self->{ops}};
134 $op->[0] = $attr->{$op->[0]}{opcode};
135 for (@{$op}[1..$max_opr+1]) { s/^[rpj]// }
137 my $pack = $Imager::Regops::PackCode x (2+$Imager::Regops::MaxOperands);
139 return join("", ,map { pack($pack, @$_, ) } @ops);
142 # converts stack code to register code
144 my ($self, @st_ops) = @_;
147 my @vars = $self->_variables();
148 my @nregs = (0) x scalar(@vars);
150 my $attr = \%Imager::Regops::Attr;
153 my $max_opr = $Imager::Regops::MaxOperands;
154 @vars{@vars} = map { "r$_" } 0..$#vars;
159 # combining constants makes the optimization below work
160 if (exists $nregs{$_}) {
161 push(@regstack, $nregs{$_});
164 $nregs{$_} = "r".@nregs;
165 push(@regstack,"r".@nregs);
169 elsif (exists $vars{$_}) {
170 push(@regstack, $vars{$_});
172 elsif (exists $attr->{$_} && length $attr->{$_}{types}) {
173 if (@regstack < $attr->{$_}{parms}) {
174 error("Imager::transform2: stack underflow on $_");
177 my @parms = splice(@regstack, -$attr->{$_}{parms});
178 my $types = join("", map {substr($_,0,1)} @parms);
179 if ($types ne $attr->{$_}{types}) {
180 if (exists $attr->{$_.'p'} && $types eq $attr->{$_.'p'}{types}) {
184 error("Imager::transform2: Call to $_ with incorrect types");
189 if ($attr->{$_}{result} eq 'r') {
190 $result = "r".@nregs;
194 $result = "p".@cregs;
197 push(@regstack, $result);
198 push(@parms, "0") while @parms < $max_opr;
199 push(@ops, [ $_, @parms, $result ]);
200 #print "$result <- $_ @parms\n";
204 error("Imager::transform2: stack underflow with $_");
207 $names{$1} = pop(@regstack);
209 elsif (/^\@(\w+)$/) {
210 if (exists $names{$1}) {
211 push(@regstack, $names{$1});
214 error("Imager::Expr: unknown storage \@$1");
219 error("Imager::Expr: unknown operator $_");
223 if (@regstack != 1) {
224 error("stack must have only one item at end");
227 if ($regstack[0] !~ /^p/) {
228 error("you must have a color value at the top of the stack at end");
231 push(@ops, [ "ret", $regstack[0], (-1) x $max_opr ]);
233 $self->{"nregs"} = \@nregs;
234 $self->{"cregs"} = \@cregs;
241 for my $op (@{$_[0]->{ops}}) {
242 $result .= "@{$op}\n";
247 # unassembles the compiled code
250 my $code = $self->{"code"};
251 my $attr = \%Imager::Regops::Attr;
252 my @code = unpack("${Imager::Regops::PackCode}*", $code);
253 my %names = map { $attr->{$_}{opcode}, $_ } keys %Imager::Regops::Attr;
254 my @vars = $self->_variables();
257 while (my @op = splice(@code, 0, 2+$Imager::Regops::MaxOperands)) {
258 my $opcode = shift @op;
259 my $name = $names{$opcode};
261 $result .= "j$index: $name($opcode)";
262 my @types = split //, $attr->{$name}{types};
263 for my $parm (@types) {
265 $result .= " $parm$reg";
268 $result.= "($vars[$reg])";
270 elsif (defined $self->{"nregs"}[$reg]) {
271 $result .= "($self->{\"nregs\"}[$reg])";
276 $result .= " -> $attr->{$name}{result}$op[-1]"
277 if $attr->{$name}{result};
281 $result .= "unknown($opcode) @op\n";
289 package Imager::Expr::Postfix;
291 @ISA = qw(Imager::Expr);
293 Imager::Expr::Postfix->register_type('rpnexpr');
295 my %op_names = ( '+'=>'add', '-'=>'subtract', '*'=>'mult', '/' => 'div',
296 '%'=>'mod', '**'=>'pow' );
299 my ($self, $expr, $opts) = @_;
301 $expr =~ s/#.*//; # remove comments
302 my @st_ops = split ' ', $expr;
305 $_ = $op_names{$_} if exists $op_names{$_};
306 $_ = $self->{constants}{$_} if exists $self->{constants}{$_};
308 return $self->stack_to_reg(@st_ops);
311 package Imager::Expr::Infix;
314 @ISA = qw(Imager::Expr);
315 use Imager::Regops qw(%Attr $MaxOperands);
318 eval "use Parse::RecDescent;";
319 __PACKAGE__->register_type('expr') if !$@;
321 # I really prefer bottom-up parsers
322 my $grammar = <<'GRAMMAR';
324 code : assigns 'return' expr
325 { $return = [ @item[1,3] ] }
327 assigns : assign(s?) { $return = [ @{$item[1]} ] }
329 assign : identifier '=' expr ';'
330 { $return = [ @item[1,3] ] }
334 relation : addition (relstuff)(s?)
337 for my $op(@{$item[2]}) { $return = [ $op->[0], $return, $op->[1] ] }
340 relstuff : relop addition { $return = [ @item[1,2] ] }
342 relop : '<=' { $return = 'le' }
343 | '<' { $return = 'lt' }
344 | '==' { $return = 'eq' }
345 | '>=' { $return = 'ge' }
346 | '>' { $return = 'gt' }
347 | '!=' { $return = 'ne' }
349 addition : multiply (addstuff)(s?)
352 # for my $op(@{$item[2]}) { $return .= " @{$op}[1,0]"; }
353 for my $op(@{$item[2]}) { $return = [ $op->[0], $return, $op->[1] ] }
355 addstuff : addop multiply { $return = [ @item[1,2] ] }
356 addop : '+' { $return = 'add' }
357 | '-' { $return = 'subtract' }
359 multiply : power mulstuff(s?)
360 { $return = $item[1];
361 # for my $op(@{$item[2]}) { $return .= " @{$op}[1,0]"; }
362 for my $op(@{$item[2]}) { $return = [ $op->[0], $return, $op->[1] ] }
365 mulstuff : mulop power { $return = [ @item[1,2] ] }
366 mulop : '*' { $return = 'mult' }
367 | '/' { $return = 'div' }
368 | '%' { $return = 'mod' }
370 power : powstuff(s?) atom
373 for my $op(reverse @{$item[1]}) { $return = [ @{$op}[1,0], $return ] }
376 powstuff : atom powop { $return = [ @item[1,2] ] }
377 powop : '**' { $return = 'pow' }
379 atom: '(' expr ')' { $return = $item[2] }
380 | '-' atom { $return = [ uminus=>$item[2] ] }
385 number : /[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/
387 exprlist : expr ',' exprlist { $return = [ $item[1], @{$item[3]} ] }
388 | expr { $return = [ $item[1] ] }
390 funccall : identifier '(' exprlist ')'
391 { $return = [ $item[1], @{$item[3]} ] }
393 identifier : /[^\W\d]\w*/ { $return = $item[1] }
401 $parser = Parse::RecDescent->new($grammar);
406 my ($self, $expr, $opts) = @_;
408 $parser = Parse::RecDescent->new($grammar);
410 my $optree = $parser->code($expr);
412 $self->error("Error in $expr\n");
416 @{$self->{inputs}}{$self->_variables} = ();
417 $self->{varregs} = {};
418 @{$self->{varregs}}{$self->_variables} = map { "r$_" } 0..$self->_variables-1;
419 $self->{"nregs"} = [ (undef) x $self->_variables ];
420 $self->{"cregs"} = [];
421 $self->{"lits"} = {};
424 # generate code for the assignments
425 for my $assign (@{$optree->[0]}) {
426 my ($varname, $tree) = @$assign;
427 if (exists $self->{inputs}{$varname}) {
428 $self->error("$varname is an input - you can't assign to it");
431 $self->{varregs}{$varname} = $self->gencode($tree);
434 # generate the final result
435 my $result = $self->gencode($optree->[1]);
436 if ($result !~ /^p\d+$/) {
437 $self->error("You must return a colour value");
440 push(@{$self->{genops}}, [ 'ret', $result, (0) x $MaxOperands ])
447 return $self->{genops};
451 my ($self, $tree) = @_;
454 my ($op, @parms) = @$tree;
456 if (!exists $Attr{$op}) {
457 die "Unknown operator or function $op";
460 for my $subtree (@parms) {
461 $subtree = $self->gencode($subtree);
463 my $types = join("", map {substr($_,0,1)} @parms);
465 if (length($types) < length($Attr{$op}{types})) {
466 die "Too few parameters in call to $op";
468 if ($types ne $Attr{$op}{types}) {
469 # some alternate operators have the same name followed by p
471 if (exists $Attr{$opp} &&
472 $types eq $Attr{$opp}{types}) {
476 die "Call to $_ with incorrect types";
480 if ($Attr{$op}{result} eq 'r') {
481 $result = "r".@{$self->{nregs}};
482 push(@{$self->{nregs}}, undef);
485 $result = "p".@{$self->{cregs}};
486 push(@{$self->{cregs}}, undef);
488 push(@parms, "0") while @parms < $MaxOperands;
489 push(@{$self->{genops}}, [ $op, @parms, $result]);
492 elsif (exists $self->{varregs}{$tree}) {
493 return $self->{varregs}{$tree};
495 elsif ($tree =~ /^$numre$/ || exists $self->{constants}{$tree}) {
496 $tree = $self->{constants}{$tree} if exists $self->{constants}{$tree};
498 if (exists $self->{lits}{$tree}) {
499 return $self->{lits}{$tree};
501 my $reg = "r".@{$self->{nregs}};
502 push(@{$self->{nregs}}, $tree);
503 $self->{lits}{$tree} = $reg;
515 Imager::Expr - implements expression parsing and compilation for the
516 expression evaluation engine used by Imager::transform2()
520 my $code = Imager::Expr->new({rpnexpr=>$someexpr})
521 or die "Cannot compile $someexpr: ",Imager::Expr::error();
525 This module is used internally by the Imager::transform2() function.
526 You shouldn't have much need to use it directly, but you may want to
529 To create a new Imager::Expr object, call:
532 my $expr = Imager::Expr->new(\%options)
533 or die Imager::Expr::error();
535 You will need to set an expression value and you may set any of the
542 A hashref defining extra constants for expression parsing. The names
543 of the constants must be valid identifiers (/[^\W\d]\w*/) and the
544 values must be valid numeric constants (that Perl recognizes in
547 Imager::Expr may define it's own constants (currently just pi.)
551 A reference to an array of variable names. These are allocated
552 numeric registers starting from register zero.
556 By default you can define a 'rpnexpr' key (which emulates RPN) or
557 'expr' (an infix expression). It's also possible to write other
558 expression parsers that will use other keys. Only one expression key
561 =head2 Instance methods
563 The Imager::Expr::error() method is used to retrieve the error if the
564 expression object cannot be created.
568 Imager::Expr provides only a few simple methods meant for external use:
574 Returns the compiled code.
578 Returns a reference to the array of numeric registers.
582 Returns a reference to the array of colour registers.
584 =item $expr->dumpops()
586 Returns a string with the generated VM "machine code".
588 =item $expr->dumpcode()
590 Returns a string with the unassembled VM "machine code".
594 =head2 Creating a new parser
596 I'll write this one day.
598 Methods used by parsers:
602 =item @vars = $self->_variables()
604 A list (not a reference) of the input variables. This should be used
605 to allocate as many registers as there are variable as input
608 =item $self->error($message)
610 Set the return value of Imager::Expr::error()
612 =item @ops = $self->stack_to_reg(@stack_ops)
614 Converts marginally parsed RPN to register code.
618 =head2 Future compatibility
620 Try to avoid doing your own optimization beyond literal folding - if
621 we add some sort of jump, the existing optimizer will need to be
622 rewritten, and any optimization you perform may well be broken too
623 (well, your code generation will probably be broken anyway <sigh>).