14 shift if UNIVERSAL::isa($_[0], 'Imager::Expr');
24 my %default_constants =
26 # too many digits, better than too few
27 pi=>3.14159265358979323846264338327950288419716939937510582097494
31 my ($class, $opts) = @_;
33 # possibly this is a very bad idea
34 my ($type) = grep exists $expr_types{$_}, keys %$opts;
35 die "Imager::Expr: No known expression type"
37 my $self = bless {}, $expr_types{$type};
38 $self->{variables} = [ @{$opts->{variables}} ];
39 $self->{constants} = { %default_constants, %{$opts->{constants} || {}} };
40 $self->{ops} = $self->compile($opts->{$type}, $opts)
44 $self->{code} = $self->assemble()
50 my ($pack, $name) = @_;
51 $expr_types{$name} = $pack;
55 return @{$_[0]->{variables}};
63 return $_[0]->{nregs};
67 return $_[0]->{cregs};
70 my $numre = '[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?';
80 my @ops = @{$self->{ops}};
82 # this function cannot current handle code with jumps
83 return 1 if grep $_->[0] =~ /^jump/, @ops;
85 # optimization - common sub-expression elimination
86 # it's possible to fold this into the code generation - but it will wait
88 my $max_opr = $Imager::Regops::MaxOperands;
89 my $attr = \%Imager::Regops::Attr;
98 my $desc = join(",", @{$op}[0..$max_opr]);
102 my $new = $seen{$desc};
104 for my $reg (@{$op}[1..$max_opr]) {
105 $reg = $new if $reg eq $old;
111 $seen{$desc} = $op->[-1];
118 # reduce division by a constant to multiplication by a constant
119 if ($op->[0] eq 'div' && $op->[2] =~ /^r(\d+)/
120 && defined($self->{"nregs"}[$1])) {
121 my $newreg = @{$self->{"nregs"}};
122 push(@{$self->{"nregs"}}, 1.0/$self->{"nregs"}[$1]);
124 $op->[2] = 'r'.$newreg;
127 $self->{ops} = \@ops;
133 my $attr = \%Imager::Regops::Attr;
134 my $max_opr = $Imager::Regops::MaxOperands;
135 my @ops = @{$self->{ops}};
137 $op->[0] = $attr->{$op->[0]}{opcode};
138 for (@{$op}[1..$max_opr+1]) { s/^[rpj]// }
140 my $pack = $Imager::Regops::PackCode x (2+$Imager::Regops::MaxOperands);
142 return join("", ,map { pack($pack, @$_, ) } @ops);
145 # converts stack code to register code
147 my ($self, @st_ops) = @_;
150 my @vars = $self->_variables();
151 my @nregs = (0) x scalar(@vars);
153 my $attr = \%Imager::Regops::Attr;
156 my $max_opr = $Imager::Regops::MaxOperands;
157 @vars{@vars} = map { "r$_" } 0..$#vars;
162 # combining constants makes the optimization below work
163 if (exists $nregs{$_}) {
164 push(@regstack, $nregs{$_});
167 $nregs{$_} = "r".@nregs;
168 push(@regstack,"r".@nregs);
172 elsif (exists $vars{$_}) {
173 push(@regstack, $vars{$_});
175 elsif (exists $attr->{$_} && length $attr->{$_}{types}) {
176 if (@regstack < $attr->{$_}{parms}) {
177 error("Imager::transform2: stack underflow on $_");
180 my @parms = splice(@regstack, -$attr->{$_}{parms});
181 my $types = join("", map {substr($_,0,1)} @parms);
182 if ($types ne $attr->{$_}{types}) {
183 if (exists $attr->{$_.'p'} && $types eq $attr->{$_.'p'}{types}) {
187 error("Imager::transform2: Call to $_ with incorrect types");
192 if ($attr->{$_}{result} eq 'r') {
193 $result = "r".@nregs;
197 $result = "p".@cregs;
200 push(@regstack, $result);
201 push(@parms, "0") while @parms < $max_opr;
202 push(@ops, [ $_, @parms, $result ]);
203 #print "$result <- $_ @parms\n";
207 error("Imager::transform2: stack underflow with $_");
210 $names{$1} = pop(@regstack);
212 elsif (/^\@(\w+)$/) {
213 if (exists $names{$1}) {
214 push(@regstack, $names{$1});
217 error("Imager::Expr: unknown storage \@$1");
222 error("Imager::Expr: unknown operator $_");
226 if (@regstack != 1) {
227 error("stack must have only one item at end");
230 if ($regstack[0] !~ /^p/) {
231 error("you must have a color value at the top of the stack at end");
234 push(@ops, [ "ret", $regstack[0], (-1) x $max_opr ]);
236 $self->{"nregs"} = \@nregs;
237 $self->{"cregs"} = \@cregs;
244 for my $op (@{$_[0]->{ops}}) {
245 $result .= "@{$op}\n";
250 # unassembles the compiled code
253 my $code = $self->{"code"};
254 my $attr = \%Imager::Regops::Attr;
255 my @code = unpack("${Imager::Regops::PackCode}*", $code);
256 my %names = map { $attr->{$_}{opcode}, $_ } keys %Imager::Regops::Attr;
257 my @vars = $self->_variables();
260 while (my @op = splice(@code, 0, 2+$Imager::Regops::MaxOperands)) {
261 my $opcode = shift @op;
262 my $name = $names{$opcode};
264 $result .= "j$index: $name($opcode)";
265 my @types = split //, $attr->{$name}{types};
266 for my $parm (@types) {
268 $result .= " $parm$reg";
271 $result.= "($vars[$reg])";
273 elsif (defined $self->{"nregs"}[$reg]) {
274 $result .= "($self->{\"nregs\"}[$reg])";
279 $result .= " -> $attr->{$name}{result}$op[-1]"
280 if $attr->{$name}{result};
284 $result .= "unknown($opcode) @op\n";
292 package Imager::Expr::Postfix;
294 @ISA = qw(Imager::Expr);
296 Imager::Expr::Postfix->register_type('rpnexpr');
298 my %op_names = ( '+'=>'add', '-'=>'subtract', '*'=>'mult', '/' => 'div',
299 '%'=>'mod', '**'=>'pow' );
302 my ($self, $expr, $opts) = @_;
304 $expr =~ s/#.*//; # remove comments
305 my @st_ops = split ' ', $expr;
308 $_ = $op_names{$_} if exists $op_names{$_};
309 $_ = $self->{constants}{$_} if exists $self->{constants}{$_};
311 return $self->stack_to_reg(@st_ops);
314 package Imager::Expr::Infix;
317 @ISA = qw(Imager::Expr);
318 use Imager::Regops qw(%Attr $MaxOperands);
321 eval "use Parse::RecDescent;";
322 __PACKAGE__->register_type('expr') if !$@;
324 # I really prefer bottom-up parsers
325 my $grammar = <<'GRAMMAR';
327 code : assigns 'return' expr
328 { $return = [ @item[1,3] ] }
330 assigns : assign(s?) { $return = [ @{$item[1]} ] }
332 assign : identifier '=' expr ';'
333 { $return = [ @item[1,3] ] }
337 relation : addition (relstuff)(s?)
340 for my $op(@{$item[2]}) { $return = [ $op->[0], $return, $op->[1] ] }
343 relstuff : relop addition { $return = [ @item[1,2] ] }
345 relop : '<=' { $return = 'le' }
346 | '<' { $return = 'lt' }
347 | '==' { $return = 'eq' }
348 | '>=' { $return = 'ge' }
349 | '>' { $return = 'gt' }
350 | '!=' { $return = 'ne' }
352 addition : multiply (addstuff)(s?)
355 # for my $op(@{$item[2]}) { $return .= " @{$op}[1,0]"; }
356 for my $op(@{$item[2]}) { $return = [ $op->[0], $return, $op->[1] ] }
358 addstuff : addop multiply { $return = [ @item[1,2] ] }
359 addop : '+' { $return = 'add' }
360 | '-' { $return = 'subtract' }
362 multiply : power mulstuff(s?)
363 { $return = $item[1];
364 # for my $op(@{$item[2]}) { $return .= " @{$op}[1,0]"; }
365 for my $op(@{$item[2]}) { $return = [ $op->[0], $return, $op->[1] ] }
368 mulstuff : mulop power { $return = [ @item[1,2] ] }
369 mulop : '*' { $return = 'mult' }
370 | '/' { $return = 'div' }
371 | '%' { $return = 'mod' }
373 power : powstuff(s?) atom
376 for my $op(reverse @{$item[1]}) { $return = [ @{$op}[1,0], $return ] }
379 powstuff : atom powop { $return = [ @item[1,2] ] }
380 powop : '**' { $return = 'pow' }
382 atom: '(' expr ')' { $return = $item[2] }
383 | '-' atom { $return = [ uminus=>$item[2] ] }
388 number : /[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/
390 exprlist : expr ',' exprlist { $return = [ $item[1], @{$item[3]} ] }
391 | expr { $return = [ $item[1] ] }
393 funccall : identifier '(' exprlist ')'
394 { $return = [ $item[1], @{$item[3]} ] }
396 identifier : /[^\W\d]\w*/ { $return = $item[1] }
404 $parser = Parse::RecDescent->new($grammar);
409 my ($self, $expr, $opts) = @_;
411 $parser = Parse::RecDescent->new($grammar);
413 my $optree = $parser->code($expr);
415 $self->error("Error in $expr\n");
419 @{$self->{inputs}}{$self->_variables} = ();
420 $self->{varregs} = {};
421 @{$self->{varregs}}{$self->_variables} = map { "r$_" } 0..$self->_variables-1;
422 $self->{"nregs"} = [ (undef) x $self->_variables ];
423 $self->{"cregs"} = [];
424 $self->{"lits"} = {};
427 # generate code for the assignments
428 for my $assign (@{$optree->[0]}) {
429 my ($varname, $tree) = @$assign;
430 if (exists $self->{inputs}{$varname}) {
431 $self->error("$varname is an input - you can't assign to it");
434 $self->{varregs}{$varname} = $self->gencode($tree);
437 # generate the final result
438 my $result = $self->gencode($optree->[1]);
439 if ($result !~ /^p\d+$/) {
440 $self->error("You must return a colour value");
443 push(@{$self->{genops}}, [ 'ret', $result, (0) x $MaxOperands ])
450 return $self->{genops};
454 my ($self, $tree) = @_;
457 my ($op, @parms) = @$tree;
459 if (!exists $Attr{$op}) {
460 die "Unknown operator or function $op";
463 for my $subtree (@parms) {
464 $subtree = $self->gencode($subtree);
466 my $types = join("", map {substr($_,0,1)} @parms);
468 if (length($types) < length($Attr{$op}{types})) {
469 die "Too few parameters in call to $op";
471 if ($types ne $Attr{$op}{types}) {
472 # some alternate operators have the same name followed by p
474 if (exists $Attr{$opp} &&
475 $types eq $Attr{$opp}{types}) {
479 die "Call to $_ with incorrect types";
483 if ($Attr{$op}{result} eq 'r') {
484 $result = "r".@{$self->{nregs}};
485 push(@{$self->{nregs}}, undef);
488 $result = "p".@{$self->{cregs}};
489 push(@{$self->{cregs}}, undef);
491 push(@parms, "0") while @parms < $MaxOperands;
492 push(@{$self->{genops}}, [ $op, @parms, $result]);
495 elsif (exists $self->{varregs}{$tree}) {
496 return $self->{varregs}{$tree};
498 elsif ($tree =~ /^$numre$/ || exists $self->{constants}{$tree}) {
499 $tree = $self->{constants}{$tree} if exists $self->{constants}{$tree};
501 if (exists $self->{lits}{$tree}) {
502 return $self->{lits}{$tree};
504 my $reg = "r".@{$self->{nregs}};
505 push(@{$self->{nregs}}, $tree);
506 $self->{lits}{$tree} = $reg;
518 Imager::Expr - implements expression parsing and compilation for the
519 expression evaluation engine used by Imager::transform2()
523 my $code = Imager::Expr->new({rpnexpr=>$someexpr})
524 or die "Cannot compile $someexpr: ",Imager::Expr::error();
528 This module is used internally by the Imager::transform2() function.
529 You shouldn't have much need to use it directly, but you may want to
532 To create a new Imager::Expr object, call:
535 my $expr = Imager::Expr->new(\%options)
536 or die Imager::Expr::error();
538 You will need to set an expression value and you may set any of the
547 A hashref defining extra constants for expression parsing. The names
548 of the constants must be valid identifiers (/[^\W\d]\w*/) and the
549 values must be valid numeric constants (that Perl recognizes in
552 Imager::Expr may define it's own constants (currently just pi.)
558 A reference to an array of variable names. These are allocated
559 numeric registers starting from register zero.
563 By default you can define a 'rpnexpr' key (which emulates RPN) or
564 'expr' (an infix expression). It's also possible to write other
565 expression parsers that will use other keys. Only one expression key
568 =head2 Instance methods
570 The Imager::Expr::error() method is used to retrieve the error if the
571 expression object cannot be created.
575 Imager::Expr provides only a few simple methods meant for external use:
581 Returns the compiled code.
585 Returns a reference to the array of numeric registers.
589 Returns a reference to the array of colour registers.
591 =item $expr->dumpops()
593 Returns a string with the generated VM "machine code".
595 =item $expr->dumpcode()
597 Returns a string with the unassembled VM "machine code".
601 =head2 Creating a new parser
603 I'll write this one day.
605 Methods used by parsers:
611 This is the main method you'll need to implement in a parser. See the
612 existing parsers for a guide.
614 It's supplied the following parameters:
620 $expr - the expression to be parsed
624 $options - the options hash supplied to transform2.
628 Return an array ref of array refs containing opcodes and operands.
630 =item @vars = $self->_variables()
632 A list (not a reference) of the input variables. This should be used
633 to allocate as many registers as there are variable as input
636 =item $self->error($message)
638 Set the return value of Imager::Expr::error()
640 =item @ops = $self->stack_to_reg(@stack_ops)
642 Converts marginally parsed RPN to register code.
646 Called to convert op codes into byte code.
650 Returns a regular expression that matches floating point numbers.
654 Optimizes the assembly code, including attempting common subexpression
655 elimination and strength reducing division by a constant into
656 multiplication by a constant.
660 Called by a new expression parser implementation to register itself,
663 YourClassName->register_type('type code');
665 where type code is the parameter that will accept the expression.
669 =head2 Future compatibility
671 Try to avoid doing your own optimization beyond literal folding - if
672 we add some sort of jump, the existing optimizer will need to be
673 rewritten, and any optimization you perform may well be broken too
674 (well, your code generation will probably be broken anyway <sigh>).