[system] / trunk / pg / lib / Parser / BOP.pm Repository:
ViewVC logotype

View of /trunk/pg/lib/Parser/BOP.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3591 - (download) (as text) (annotate)
Tue Aug 30 12:21:36 2005 UTC (7 years, 9 months ago) by dpvc
File size: 10535 byte(s)
The perl method for UOP and BOP now put spaces around the operator, to
prevent Perl from thinking that things like -e is a file test and
*Parser::Function->call is a name reference.  (Some of these had been
done by hand earlier, but now the base BOP and UOP classes handle it,
so we should not have problems in the future).  I removed the ad hoc
fixes from several other locations (Parser/Function.pm,
Parser/Context/Default.pm).

Also extended the operator definitions to allow operators to create
function-call syntax in perl mode (for when the operator doesn't
correspond to a perl operator).

    1 #########################################################################
    2 #
    3 #  Implements the base Binary Operator class
    4 #
    5 
    6 package Parser::BOP;
    7 use strict; use vars qw(@ISA);
    8 @ISA = qw(Parser::Item);
    9 
   10 $Parser::class->{BOP} = 'Parser::BOP';
   11 
   12 #
   13 #  Make a new instance of a BOP
   14 #
   15 #  Make left and right operands into lists if they are comma operators
   16 #    and this operator isn't itself a comma.
   17 #
   18 sub new {
   19   my $self = shift; my $class = ref($self) || $self;
   20   my $equation = shift; my $context = $equation->{context};
   21   my ($bop,$lop,$rop,$ref) = @_;
   22   my $def = $context->{operators}{$bop};
   23   if (!$def->{isComma}) {
   24     $lop = $context->{parser}{List}->new($equation,[$lop->makeList],
   25        $lop->{isConstant},$context->{parens}{start}) if ($lop->type eq 'Comma');
   26     $rop = $context->{parser}{List}->new($equation,[$rop->makeList],$rop->{isConstant},
   27        $context->{parens}{start}) if ($rop->type eq 'Comma');
   28   }
   29   my $BOP = bless {
   30     bop => $bop, lop => $lop, rop => $rop,
   31     def => $def, ref => $ref, equation => $equation,
   32   }, $def->{class};
   33   $BOP->_check;
   34   $BOP->{isConstant} = 1 if ($lop->{isConstant} && $rop->{isConstant});
   35   $BOP = $context->{parser}{Value}->new($equation,[$BOP->eval])
   36     if $BOP->{isConstant} && !$def->{isComma} && $context->flag('reduceConstants');
   37   return $BOP;
   38 }
   39 
   40 #
   41 #  Stub for checking if the BOP can operate on the given operands.
   42 #  (Implemented in subclasses.)
   43 #
   44 sub _check {}
   45 
   46 ##################################################
   47 
   48 #
   49 #  Evaluate the left and right operands and peform the
   50 #  required operation on the results.
   51 #
   52 sub eval {
   53   my $self = shift;
   54   $self->_eval($self->{lop}->eval,$self->{rop}->eval);
   55 }
   56 #
   57 #  Stub for sub-classes.
   58 #
   59 sub _eval {return $_[1]}
   60 
   61 #
   62 #  Reduce the left and right operands.
   63 #  If they are constant (and it's not a comma), make a constant value of them.
   64 #  Otherwise, reduce the result.
   65 #
   66 sub reduce {
   67   my $self = shift; my $bop = $self->{def};
   68   $self->{lop} = $self->{lop}->reduce;
   69   $self->{rop} = $self->{rop}->reduce;
   70   my $equation = $self->{equation};
   71   return $equation->{context}{parser}{Value}->new($equation,[$self->eval])
   72     if (!$bop->{isComma} && $self->{lop}{isConstant} && $self->{rop}{isConstant});
   73   $self->_reduce;
   74 }
   75 #
   76 #  Stub for sub-classes.
   77 #
   78 sub _reduce {shift}
   79 
   80 #
   81 #  Substitute in the left and right operands.
   82 #
   83 sub substitute {
   84   my $self = shift; my $bop = $self->{def};
   85   $self->{lop} = $self->{lop}->substitute;
   86   $self->{rop} = $self->{rop}->substitute;
   87   my $equation = $self->{equation}; my $context = $equation->{context};
   88   return $context->{parser}{Value}->new($equation,[$self->eval])
   89     if !$bop->{isComma} && $self->{lop}{isConstant} && $self->{rop}{isConstant} &&
   90         $context->flag('reduceConstants');
   91   return $self;
   92 }
   93 
   94 #
   95 #  Copy the left and right operands as well as the rest
   96 #    of the equations.
   97 #
   98 sub copy {
   99   my $self = shift; my $equation = shift;
  100   my $new = $self->SUPER::copy($equation);
  101   $new->{lop} = $self->{lop}->copy($equation);
  102   $new->{rop} = $self->{rop}->copy($equation);
  103   return $new;
  104 }
  105 
  106 ##################################################
  107 #
  108 #  Service routines for checking the types of operands.
  109 #
  110 
  111 
  112 #
  113 #  Error if one of the operands is a string.
  114 #
  115 sub checkStrings {
  116   my $self = shift;
  117   my $ltype = $self->{lop}->typeRef; my $rtype = $self->{rop}->typeRef;
  118   my $name = $self->{def}{string} || $self->{bop};
  119   if ($ltype->{name} eq 'String') {
  120     $self->Error("Operands of '%s' can't be %s",$name,
  121      ($self->{lop}{isInfinite}? 'infinities': 'words'));
  122     return 1;
  123   }
  124   if ($rtype->{name} eq 'String') {
  125     $self->Error("Operands of '%s' can't be %s",$name,
  126      ($self->{rop}{isInfinite}? 'infinities': 'words'));
  127     return 1;
  128   }
  129   return 0;
  130 }
  131 
  132 #
  133 #  Error if one of the operands is a list.
  134 #
  135 sub checkLists {
  136   my $self = shift;
  137   my $ltype = $self->{lop}->typeRef; my $rtype = $self->{rop}->typeRef;
  138   return 0 if ($ltype->{name} ne 'List' and $rtype->{name} ne 'List');
  139   my $name = $self->{def}{string} || $self->{bop};
  140   $self->Error("Operands of '%s' can't be lists",$name);
  141   return 1;
  142 }
  143 
  144 #
  145 #  Determine if both operands are numbers, and promote to
  146 #    complex numbers if one is complex.
  147 #
  148 sub checkNumbers {
  149   my $self = shift;
  150   return 0 if !($self->{lop}->isNumber && $self->{rop}->isNumber);
  151   if ($self->{lop}->isComplex || $self->{rop}->isComplex) {
  152     $self->{type} = $Value::Type{complex};
  153   } else {
  154     $self->{type} = $Value::Type{number};
  155   }
  156   return 1;
  157 }
  158 
  159 #
  160 #  Check if two matrices can be multiplied.
  161 #
  162 sub checkMatrixSize {
  163   my $self = shift;
  164   my ($lm,$rm) = @_;
  165   my ($ltype,$rtype) = ($lm->{entryType},$rm->{entryType});
  166   if ($ltype->{entryType}{name} eq 'Number' &&
  167       $rtype->{entryType}{name} eq 'Number') {
  168     my ($lr,$lc) = ($lm->{length},$ltype->{length});
  169     my ($rr,$rc) = ($rm->{length},$rtype->{length});
  170     if ($lc == $rr) {
  171       my $rowType = Value::Type('Matrix',$rc,$Value::Type{number},formMatrix=>1);
  172       $self->{type} = Value::Type('Matrix',$lr,$rowType,formMatrix=>1);
  173     } else {$self->Error("Matrices of dimensions %dx%d and %dx%d can't be multiplied",$lr,$lc,$rr,$rc)}
  174   } else {$self->Error("Matrices are too deep to be multiplied")}
  175 }
  176 
  177 #
  178 #  Promote point operands to vectors or matrices.
  179 #
  180 sub promotePoints {
  181   my $self = shift; my $class = shift;
  182   my $ltype = $self->{lop}->typeRef;
  183   my $rtype = $self->{rop}->typeRef;
  184   if ($ltype->{name} eq 'Point' ||
  185       ($ltype->{name} eq 'Matrix' && !$ltype->{entryType}{entryType})) {
  186     $ltype = {%{$ltype}, name => 'Vector'};
  187     $ltype = Value::Type($class,1,Value::Type($class,1,$ltype->{entryType}))
  188       if ($ltype->{length} == 1 && $class);
  189   }
  190   if ($rtype->{name} eq 'Point' ||
  191       ($rtype->{name} eq 'Matrix' && !$rtype->{entryType}{entryType})) {
  192     $rtype = {%{$rtype}, name => 'Vector'};
  193     $rtype = Value::Type($class,1,Value::Type($class,1,$rtype->{entryType}))
  194       if ($rtype->{length} == 1 && $class);
  195   }
  196   return ($ltype,$rtype);
  197 }
  198 
  199 #
  200 #  Report an error if the operand types don't match.
  201 #
  202 sub matchError {
  203   my $self = shift;
  204   my ($ltype,$rtype) = @_;
  205   my ($op,$ref) = ($self->{bop});
  206   if ($ltype->{name} eq $rtype->{name})
  207        {$self->Error("Operands for '%s' must be of the same length",$op)}
  208   else {$self->Error("Operands for '%s' must be of the same type",$op)}
  209 }
  210 
  211 ##################################################
  212 #
  213 #  Service routines for adjusting the values of operands.
  214 #
  215 
  216 #
  217 #  Return a zero, or a list of zeros of the proper length.
  218 #
  219 sub makeZero {
  220   my $self = shift; my $op = shift; my $zero = shift;
  221   return $zero if ($op->isNumber);
  222   if ($zero->isNumber && $op->type =~ m/Point|Vector/) {
  223     $op->{coords} = []; $op->{isZero} = 1;
  224     foreach my $i (0..($op->length-1)) {push(@{$op->{coords}},$zero)}
  225     return $op
  226   }
  227   return $self;
  228 }
  229 
  230 #
  231 #  Produce a negated version of a BOP.
  232 #
  233 sub makeNeg {
  234   my $self = shift;
  235   $self->{lop} = shift; $self->{rop} = shift;
  236   return Parser::UOP::Neg($self);
  237 }
  238 
  239 #
  240 #  Reverse the operands (left <=> right).
  241 #
  242 sub swapOps {
  243   my $self = shift;
  244   my $tmp = $self->{lop}; $self->{lop} = $self->{rop}; $self->{rop} = $tmp;
  245   return $self;
  246 }
  247 
  248 #
  249 #  Get the variables from the two operands
  250 #
  251 sub getVariables {
  252   my $self = shift;
  253   return {%{$self->{lop}->getVariables},%{$self->{rop}->getVariables}};
  254 }
  255 
  256 ##################################################
  257 #
  258 #  Generate the various output formats.
  259 #
  260 
  261 
  262 #
  263 #  Produce a string version of the BOP.
  264 #
  265 #  Parentheses are added when either:
  266 #    we are told to from our parent
  267 #    the BOP says to (fullparens)
  268 #    the BOP's precedence is lower than it's parent's, or
  269 #    the precedences are equal and either
  270 #       the associativity is right
  271 #       or we are supposed to show parens for the same precedence
  272 #
  273 sub string {
  274   my ($self,$precedence,$showparens,$position,$outerRight) = @_;
  275   my $string; my $bop = $self->{def};
  276   $position = '' unless defined($position);
  277   $showparens = '' unless defined($showparens);
  278   my $extraParens = $self->{equation}{context}->flag('showExtraParens');
  279   my $addparens =
  280       defined($precedence) &&
  281       ($showparens eq 'all' || (($showparens eq 'extra' || $bop->{fullparens}) && $extraParens) ||
  282        $precedence > $bop->{precedence} || ($precedence == $bop->{precedence} &&
  283         ($bop->{associativity} eq 'right' || $showparens eq 'same')));
  284   $outerRight = !$addparens && ($outerRight || $position eq 'right');
  285 
  286   $string = $self->{lop}->string($bop->{precedence},$bop->{leftparens},'left',$outerRight).
  287             $bop->{string}.
  288             $self->{rop}->string($bop->{precedence},$bop->{rightparens},'right');
  289 
  290   $string = $self->addParens($string) if ($addparens);
  291   return $string;
  292 }
  293 
  294 #
  295 #  Produce the TeX version of the BOP.
  296 #
  297 sub TeX {
  298   my ($self,$precedence,$showparens,$position,$outerRight) = @_;
  299   my $TeX; my $bop = $self->{def};
  300   $position = '' unless defined($position);
  301   $showparens = '' unless defined($showparens);
  302   my $extraParens = $self->{equation}{context}->flag('showExtraParens');
  303   my $addparens =
  304       defined($precedence) &&
  305       (($showparens eq 'all' && $extraParens) || $precedence > $bop->{precedence} ||
  306       ($precedence == $bop->{precedence} &&
  307         ($bop->{associativity} eq 'right' || $showparens eq 'same')));
  308   $outerRight = !$addparens && ($outerRight || $position eq 'right');
  309 
  310   $TeX = $self->{lop}->TeX($bop->{precedence},$bop->{leftparens},'left',$outerRight).
  311          (defined($bop->{TeX}) ? $bop->{TeX} : $bop->{string}) .
  312          $self->{rop}->TeX($bop->{precedence},$bop->{rightparens},'right');
  313 
  314   $TeX = '\left('.$TeX.'\right)' if ($addparens);
  315   return $TeX;
  316 }
  317 
  318 #
  319 #  Produce the perl version of the BOP.
  320 #
  321 sub perl {
  322   my $self= shift; my $parens = shift;
  323   my $bop = $self->{def}; my $perl;
  324   if ($bop->{isCommand}) {
  325     $perl =
  326       ($bop->{perl} || ref($self).'->call').
  327         '('.$self->{lop}->perl.','.$self->{rop}->perl.')';
  328   } else {
  329     $perl =
  330         $self->{lop}->perl(1).
  331   " ".($bop->{perl} || $bop->{string})." ".
  332         $self->{rop}->perl(2);
  333   }
  334   $perl = '('.$perl.')' if $parens;
  335   return $perl;
  336 }
  337 
  338 #########################################################################
  339 #
  340 #  Load the subclasses.
  341 #
  342 
  343 use Parser::BOP::undefined;
  344 use Parser::BOP::comma;
  345 use Parser::BOP::union;
  346 use Parser::BOP::add;
  347 use Parser::BOP::subtract;
  348 use Parser::BOP::multiply;
  349 use Parser::BOP::divide;
  350 use Parser::BOP::power;
  351 use Parser::BOP::cross;
  352 use Parser::BOP::dot;
  353 use Parser::BOP::underscore;
  354 use Parser::BOP::equality;
  355 
  356 #########################################################################
  357 
  358 1;
  359 

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9