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

View of /trunk/pg/lib/AlgParser.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1079 - (download) (as text) (annotate)
Mon Jun 9 17:36:12 2003 UTC (9 years, 11 months ago) by apizer
File size: 19696 byte(s)
removed unneccesary shebang lines

    1 
    2 
    3 ## Last modification: 8/3/00 by akp
    4 ## Originally written by Daniel Martin, Dept of Math, John Hopkins
    5 ## Additions and modifications were made by James Martino, Dept of Math, John Hopkins
    6 ## Additions and modifications were made by Arnold Pizer, Dept of Math, Univ of Rochester
    7 
    8 #use Data::Dumper;
    9 
   10 package AlgParser;
   11 use HTML::Entities;
   12 
   13 %close = ();
   14 
   15 sub new {
   16   my $package = shift;
   17   my (%ret);
   18   $ret{string} = "";
   19   $ret{posarray} = [];
   20   $ret{parseerror} = "";
   21   $ret{parseresult} = [];
   22   bless \%ret, $package;
   23   return \%ret;
   24 }
   25 
   26 sub inittokenizer {
   27   my($self, $string) = @_;
   28   $self->{string} =~ m/\G.*$/g;
   29   $self->{string} = undef;
   30   $self->{string} = $string;
   31   $self->{string} =~ m/\G.*$/g;
   32   $self->{string} =~ m/^/g;
   33 }
   34 
   35 $close{'{'} = '}';
   36 $close{'['} = ']';
   37 $close{'('} = ')';
   38 
   39 $binoper3 = '(?:\\^|\\*\\*)';
   40 $binoper2 = '[/*]';
   41 $binoper1 = '[-+]';
   42 $openparen = '[{(\\[]';
   43 $closeparen = '[})\\]]';
   44 $varname = '[A-Za-z](?:_[0-9]+)?';
   45 $specialvalue = '(?:e|pi)';
   46 $numberplain = '(?:\d+(?:\.\d*)?|\.\d+)';
   47 $numberE = '(?:' . $numberplain . 'E[-+]?\d+)';
   48 $number = '(?:' . $numberE . '|' . $numberplain . ')';
   49 $trigfname = '(?:cosh|sinh|tanh|cot|(?:a(?:rc)?)?cos|(?:a(?:rc)?)?sin|' .
   50     '(?:a(?:rc)?)?tan|sech?)';
   51 $otherfunc = '(?:exp|abs|logten|log|ln|sqrt|sgn|step|fact)';
   52 $funcname = '(?:' . $otherfunc . '|' . $trigfname . ')';
   53 
   54 $tokenregexp = "(?:($binoper3)|($binoper2)|($binoper1)|($openparen)|" .
   55     "($closeparen)|($funcname)|($specialvalue)|($varname)|" .
   56     "($numberE)|($number))";
   57 
   58 sub nexttoken {
   59   my($self) = shift;
   60   $self->{string} =~ m/\G\s+/gc;
   61   my($p1) = pos($self->{string}) || 0;
   62   if(scalar($self->{string} =~ m/\G$tokenregexp/gc)) {
   63         push @{$self->{posarray}}, [$p1, pos($self->{string})];
   64   if (defined($1)) {return ['binop3',  $1];}
   65   if (defined($2)) {return ['binop2',  $2];}
   66   if (defined($3)) {return ['binop1',  $3];}
   67   if (defined($4)) {return ['openp',   $4];}
   68   if (defined($5)) {return ['closep',  $5];}
   69   if (defined($6)) {return ['func1',   $6];}
   70   if (defined($7)) {return ['special', $7];}
   71   if (defined($8)) {return ['varname', $8];}
   72   if (defined($9)) {return ['numberE', $9];}
   73   if (defined($10)) {return ['number', $10];}
   74   }
   75   else {
   76     push @{$self->{posarray}}, [$p1, undef];
   77     return undef;
   78   }
   79 }
   80 
   81 sub parse {
   82   my $self = shift;
   83   $self->{parseerror} = "";
   84   $self->{posarray} = [];
   85   $self->{parseresult} = ['top', undef];
   86   my (@backtrace) = (\$self->{parseresult});
   87   my (@pushback) = ();
   88 
   89   my $currentref = \$self->{parseresult}->[1];
   90   my $curenttok;
   91 
   92   my $sstring = shift;
   93   $self->inittokenizer($sstring);
   94   $currenttok = $self->nexttoken;
   95   if (!$currenttok) {
   96     if ($self->{string} =~ m/\G$/g) {
   97       return $self->error("empty");
   98     } else {
   99       my($mark) = pop @{$self->{posarray}};
  100       my $position = 1+$mark->[0];
  101       return $self->error("Illegal character at position $position", $mark);
  102     }
  103   }
  104   # so I can assume we got a token
  105   local $_;
  106   while ($currenttok) {
  107     $_ = $currenttok->[0];
  108     /binop1/ && do {
  109       # check if we have a binary or unary operation here.
  110       if (defined(${$currentref})) {
  111         # binary - walk up the tree until we hit an open paren or the top
  112         while (${$currentref}->[0] !~ /^(openp|top)/) {
  113           $currentref = pop @backtrace;
  114         }
  115   my $index = ((${$currentref}->[0] eq 'top')?1:3);
  116         ${$currentref}->[$index] = ['binop1', $currenttok->[1],
  117                                     ${$currentref}->[$index], undef];
  118         push @backtrace, $currentref;
  119         push @backtrace, \${$currentref}->[$index];
  120         $currentref = \${$currentref}->[$index]->[3];
  121       } else {
  122         # unary
  123         ${$currentref} = ['unop1', $currenttok->[1], undef];
  124         push @backtrace, $currentref;
  125         $currentref = \${$currentref}->[2];
  126       }
  127     };
  128     /binop2/ && do {
  129       if (defined(${$currentref})) {
  130         # walk up the tree until an open paren, the top, binop1 or unop1
  131         # I decide arbitrarily that -3*4 should be parsed as -(3*4)
  132         # instead of as (-3)*4.  Not that it makes a difference.
  133 
  134         while (${$currentref}->[0] !~ /^(openp|top|binop1)/) {
  135           $currentref = pop @backtrace;
  136         }
  137         my $a = ${$currentref}->[0];
  138         my $index = (($a eq 'top')?1:3);
  139         ${$currentref}->[$index] = ['binop2', $currenttok->[1],
  140                                     ${$currentref}->[$index], undef];
  141         push @backtrace, $currentref;
  142         push @backtrace, \${$currentref}->[$index];
  143         $currentref = \${$currentref}->[$index]->[3];
  144       } else {
  145         # Error
  146         my($mark) = pop @{$self->{posarray}};
  147         my $position =1+$mark->[0];
  148         return $self->error("Didn't expect " . $currenttok->[1] .
  149                             " at position $position" , $mark);
  150       }
  151     };
  152     /binop3/ && do {
  153       if (defined(${$currentref})) {
  154         # walk up the tree until we need to stop
  155         # Note that the right-associated nature of ^ means we need to
  156         # stop walking backwards when we hit a ^ as well.
  157         while (${$currentref}->[0] !~ /^(openp|top|binop[123]|unop1)/) {
  158           $currentref = pop @backtrace;
  159         }
  160         my $a = ${$currentref}->[0];
  161         my $index = ($a eq 'top')?1:($a eq 'unop1')?2:3;
  162         ${$currentref}->[$index] = ['binop3', $currenttok->[1],
  163                                     ${$currentref}->[$index], undef];
  164         push @backtrace, $currentref;
  165         push @backtrace, \${$currentref}->[$index];
  166         $currentref = \${$currentref}->[$index]->[3];
  167       } else {
  168         # Error
  169         my($mark) = pop @{$self->{posarray}};
  170         my $position = 1+$mark->[0];
  171         return $self->error("Didn't expect " . $currenttok->[1] .
  172                             " at position $position", $mark);
  173       }
  174     };
  175     /openp/ && do {
  176       if (defined(${$currentref})) {
  177         # we weren't expecting this - must be implicit
  178         # multiplication.
  179         push @pushback, $currenttok;
  180         $currenttok = ['binop2', 'implicit'];
  181         next;
  182       } else {
  183         my($me) = pop @{$self->{posarray}};
  184         ${$currentref} = [$currenttok->[0], $currenttok->[1], $me, undef];
  185         push @backtrace, $currentref;
  186         $currentref = \${$currentref}->[3];
  187       }
  188     };
  189     /func1/ && do {
  190       if (defined(${$currentref})) {
  191         # we weren't expecting this - must be implicit
  192         # multiplication.
  193         push @pushback, $currenttok;
  194         $currenttok = ['binop2', 'implicit'];
  195         next;
  196       } else {
  197         # just like a unary operator
  198         ${$currentref} = [$currenttok->[0], $currenttok->[1], undef];
  199         push @backtrace, $currentref;
  200         $currentref = \${$currentref}->[2];
  201       }
  202     };
  203     /closep/ && do {
  204       if (defined(${$currentref})) {
  205         # walk up the tree until we need to stop
  206         while (${$currentref}->[0] !~ /^(openp|top)/) {
  207           $currentref = pop @backtrace;
  208         }
  209         my $a = ${$currentref}->[0];
  210         if ($a eq 'top') {
  211           my($mark) = pop @{$self->{posarray}};
  212           my $position = 1+$mark->[0];
  213           return $self->error("Unmatched close " . $currenttok->[1] .
  214                               " at position $position", $mark);
  215         } elsif ($close{${$currentref}->[1]} ne $currenttok->[1]) {
  216           my($mark) = pop @{$self->{posarray}};
  217           my $position = 1+$mark->[0];
  218           return $self->error("Mismatched parens at position $position"
  219                               , ${$currentref}->[2], $mark);
  220         } else {
  221           ${$currentref}->[0] = 'closep';
  222           ${$currentref}->[2] = pop @{${$currentref}};
  223         }
  224       } else {
  225         # Error - something like (3+4*)
  226         my($mark) = pop @{$self->{posarray}};
  227         my $position = 1+$mark->[0];
  228         return $self->error("Premature close " . $currenttok->[1] .
  229                             " at position $position", $mark);
  230       }
  231     };
  232     /special|varname|numberE?/ && do {
  233       if (defined(${$currentref})) {
  234         # we weren't expecting this - must be implicit
  235         # multiplication.
  236         push @pushback, $currenttok;
  237         $currenttok = ['binop2', 'implicit'];
  238         next;
  239       } else {
  240         ${$currentref} = [$currenttok->[0], $currenttok->[1]];
  241       }
  242     };
  243     if (@pushback) {
  244       $currenttok = pop @pushback;
  245     } else {
  246       $currenttok = $self->nexttoken;
  247     }
  248   }
  249   # ok, we stopped parsing.  Now we need to see why.
  250   if ($self->{parseresult}->[0] eq 'top') {
  251     $self->{parseresult} = $self->arraytoexpr($self->{parseresult}->[1]);
  252   } else {
  253     return $self->error("Internal consistency error; not at top when done");
  254   }
  255   if ($self->{string} =~ m/\G\s*$/g) {
  256     if (!defined(${$currentref})) {
  257       $self->{string} .= " ";
  258       return $self->error("I was expecting more at the end of the line",
  259                         [length($self->{string})-1, length($self->{string})]);
  260     } else {
  261       # check that all the parens were closed
  262       while (@backtrace) {
  263         $currentref = pop @backtrace;
  264         if (${$currentref}->[0] eq 'openp') {
  265           my($mark) = ${$currentref}->[2];
  266           my $position = 1+$mark->[0];
  267           return $self->error("Unclosed parentheses beginning at position $position"
  268                          , $mark);
  269         }
  270       }
  271       # Ok, we must really have parsed something
  272       return $self->{parseresult};
  273     }
  274   } else {
  275       my($mark) = pop @{$self->{posarray}};
  276       my $position = 1+$mark->[0];
  277       return $self->error("Illegal character at position $position",$mark);
  278   }
  279 }
  280 
  281 sub arraytoexpr {
  282   my ($self) = shift;
  283   return Expr->fromarray(@_);
  284 }
  285 
  286 sub error {
  287   my($self, $errstr, @markers) = @_;
  288 #  print STDERR Data::Dumper->Dump([\@markers],
  289 #                                  ['$markers']);
  290   $self->{parseerror} = $errstr;
  291   my($htmledstring) = '<tt class="parseinput">';
  292   my($str) = $self->{string};
  293 #  print STDERR Data::Dumper->Dump([$str], ['$str']);
  294   my($lastpos) = 0;
  295   $str =~ s/ /\240/g;
  296   while(@markers) {
  297     my($ref) = shift @markers;
  298     my($pos1) = $ref->[0];
  299     my($pos2) = $ref->[1];
  300     if (!defined($pos2)) {$pos2 = $pos1+1;}
  301     $htmledstring .= encode_entities(substr($str,$lastpos,$pos1-$lastpos)) .
  302            '<b class="parsehilight">' .
  303            encode_entities(substr($str,$pos1,$pos2-$pos1)) .
  304            '</b>';
  305     $lastpos = $pos2;
  306   }
  307 #  print STDERR Data::Dumper->Dump([$str, $htmledstring, $lastpos],
  308 #                                  ['$str', '$htmledstring', '$lastpos']);
  309   $htmledstring .= encode_entities(substr($str,$lastpos));
  310   $htmledstring .= '</tt>';
  311 #  $self->{htmlerror} = '<p class="parseerr">' . "\n" .
  312 #                       '<span class="parsedesc">' .
  313 #                       encode_entities($errstr) . '</span><br>' . "\n" .
  314 #                       $htmledstring . "\n" . '</p>' . "\n";
  315   $self->{htmlerror} =  $htmledstring ;
  316   $self->{htmlerror} =  'empty' if $errstr eq 'empty';
  317   $self->{error_msg} = $errstr;
  318 
  319 #  warn $errstr . "\n";
  320   return undef;
  321 }
  322 
  323 sub tostring {
  324   my ($self) = shift;
  325   return $self->{parseresult}->tostring(@_);
  326 }
  327 
  328 sub tolatex {
  329   my ($self) = shift;
  330   return $self->{parseresult}->tolatex(@_);
  331 }
  332 
  333 sub tolatexstring { return tolatex(@_);}
  334 
  335 sub exprtolatexstr {
  336   return exprtolatex(@_);
  337 }
  338 
  339 sub exprtolatex {
  340   my($expr) = shift;
  341   my($exprobj);
  342   if ((ref $expr) eq 'ARRAY') {
  343     $exprobj = Expr->new(@$expr);
  344   } else {
  345     $exprobj = $expr;
  346   }
  347   return $exprobj->tolatex();
  348 }
  349 
  350 sub exprtostr {
  351   my($expr) = shift;
  352   my($exprobj);
  353   if ((ref $expr) eq 'ARRAY') {
  354     $exprobj = Expr->new(@$expr);
  355   } else {
  356     $exprobj = $expr;
  357   }
  358   return $exprobj->tostring();
  359 }
  360 
  361 sub normalize {
  362   my ($self, $degree) = @_;
  363   $self->{parseresult} = $self->{parseresult}->normalize($degree);
  364 }
  365 
  366 sub normalize_expr {
  367   my($expr, $degree) = @_;
  368   my($exprobj);
  369   if ((ref $expr) eq 'ARRAY') {
  370     $exprobj = Expr->new(@$expr);
  371   } else {
  372     $exprobj = $expr;
  373   }
  374   return $exprobj->normalize($degree);
  375 }
  376 
  377 package AlgParserWithImplicitExpand;
  378 @ISA=qw(AlgParser);
  379 
  380 sub arraytoexpr {
  381   my ($self) = shift;
  382   my ($foo) = ExprWithImplicitExpand->fromarray(@_);
  383 # print STDERR Data::Dumper->Dump([$foo],['retval']);
  384   return $foo;
  385 }
  386 
  387 package Expr;
  388 
  389 sub new {
  390   my($class) = shift;
  391   my(@args) = @_;
  392   my($ret) = [@args];
  393   return (bless $ret, $class);
  394 }
  395 
  396 sub head {
  397   my($self) = shift;
  398   return ($self->[0]);
  399 }
  400 
  401 
  402 sub normalize {
  403 #print STDERR "normalize\n";
  404 #print STDERR Data::Dumper->Dump([@_]);
  405 
  406   my($self, $degree) = @_;
  407   my($class) = ref $self;
  408   $degree = $degree || 0;
  409   my($type, @args) = @$self;
  410   local $_;
  411   $_ = $type;
  412   my ($ret) = [$type, @args];
  413 
  414 
  415   if(/closep/) {
  416     $ret = $args[1]->normalize($degree);
  417   } elsif (/unop1/) {
  418     $ret = $class->new($type, $args[0], $args[1]->normalize($degree));
  419   } elsif (/binop/) {
  420     $ret = $class->new($type, $args[0], $args[1]->normalize($degree),
  421                              $args[2]->normalize($degree));
  422   } elsif (/func1/) {
  423     $args[0] =~ s/^arc/a/;
  424     $ret = $class->new($type, $args[0], $args[1]->normalize($degree));
  425   }
  426 
  427 
  428   if ($degree < 0) {return $ret;}
  429 
  430 
  431   ($type, @args) = @$ret;
  432   $ret = $class->new($type, @args);
  433   $_ = $type;
  434   if (/binop1/ && ($args[2]->[0] =~ 'unop1')) {
  435     my($h1, $h2) = ($args[0], $args[2]->[1]);
  436     my($s1, $s2) = ($h1 eq '-', $h2 eq '-');
  437     my($eventual) = ($s1==$s2);
  438     if ($eventual) {
  439       $ret = $class->new('binop1', '+', $args[1], $args[2]->[2] );
  440     } else {
  441       $ret = $class->new('binop1', '-', $args[1], $args[2]->[2] );
  442     }
  443   } elsif (/binop2/ && ($args[1]->[0] =~ 'unop1')) {
  444     $ret = $class->new('unop1', '-',
  445                        $class->new($type, $args[0], $args[1]->[2],
  446                                    $args[2])->normalize($degree) );
  447   } elsif (/binop[12]/ && ($args[2]->[0] eq $type) &&
  448                           ($args[0] =~ /[+*]/)) {
  449 # Remove frivolous right-association
  450 # For example, fix 3+(4-5) or 3*(4x)
  451     $ret = $class->new($type, $args[2]->[1],
  452                        $class->new($type, $args[0], $args[1],
  453                                    $args[2]->[2])->normalize($degree),
  454                        $args[2]->[3]);
  455   } elsif (/unop1/ && ($args[0] eq '+')) {
  456     $ret = $args[1];
  457   } elsif (/unop1/ && ($args[1]->[0] =~ 'unop1')) {
  458     $ret = $args[1]->[2];
  459   }
  460   if ($degree > 0) {
  461   }
  462   return $ret;
  463 }
  464 
  465 sub tostring {
  466 # print STDERR "Expr::tostring\n";
  467 # print STDERR Data::Dumper->Dump([@_]);
  468   my($self) = shift;
  469   my($type, @args) = @$self;
  470   local $_;
  471   $_ = $type;
  472   /binop1/ && do {
  473     my ($p1, $p2) = ('','');
  474     if ($args[2]->[0] eq 'binop1') {($p1,$p2)=qw{ ( ) };}
  475     return ($args[1]->tostring() . $args[0] . $p1 .
  476             $args[2]->tostring() . $p2);
  477   };
  478   /unop1/ && do {
  479     my ($p1, $p2) = ('','');
  480     if ($args[1]->[0] =~ /binop1/) {($p1,$p2)=qw{ ( ) };}
  481     return ($args[0] . $p1 . $args[1]->tostring() . $p2);
  482   };
  483   /binop2/ && do {
  484     my ($p1, $p2, $p3, $p4)=('','','','');
  485     if ($args[0] =~ /implicit/) {$args[0] = ' ';}
  486     if ($args[1]->[0] =~ /binop1/) {($p1,$p2)=qw{ ( ) };}
  487 #    if ($args[2]->[0] =~ /binop[12]/) {($p3,$p4)=qw{ ( ) };}
  488     if ($args[2]->[0] =~ /binop[12]|unop1/) {($p3,$p4)=qw{ ( ) };}
  489     return ($p1 . $args[1]->tostring() . $p2 . $args[0] . $p3 .
  490             $args[2]->tostring() . $p4);
  491   };
  492   /binop3/ && do {
  493     my ($p1, $p2, $p3, $p4)=('','','','');
  494 #    if ($args[1]->[0] =~ /binop[123]|numberE/) {($p1,$p2)=qw{ ( ) };}
  495     if ($args[1]->[0] =~ /binop[123]|unop1|numberE/) {($p1,$p2)=qw{ ( ) };}
  496 #    if ($args[2]->[0] =~ /binop[12]|numberE/) {($p3,$p4)=qw{ ( ) };}
  497     if ($args[2]->[0] =~ /binop[12]|unop1|numberE/) {($p3,$p4)=qw{ ( ) };}
  498     return ($p1 . $args[1]->tostring() . $p2 . $args[0] . $p3 .
  499             $args[2]->tostring() . $p4);
  500   };
  501   /func1/ && do {
  502     return ($args[0] . '(' . $args[1]->tostring() . ')');
  503   };
  504   /special|varname|numberE?/ && return $args[0];
  505   /closep/ && do {
  506     my(%close) = %AlgParser::close;
  507 
  508 
  509 
  510     return ($args[0] . $args[1]->tostring() . $close{$args[0]});
  511   };
  512 }
  513 
  514 sub tolatex {
  515   my($self) = shift;
  516   my($type, @args) = @$self;
  517   local $_;
  518   $_ = $type;
  519   /binop1/ && do {
  520     my ($p1, $p2) = ('','');
  521     if ($args[2]->[0] eq 'binop1') {($p1,$p2)=qw{ \left( \right) };}
  522     return ($args[1]->tolatex() . $args[0] . $p1 .
  523             $args[2]->tolatex() . $p2);
  524   };
  525   /unop1/ && do {
  526     my ($p1, $p2) = ('','');
  527     if ($args[1]->[0] =~ /binop1/) {($p1,$p2)=qw{ \left( \right) };}
  528     return ($args[0] . $p1 . $args[1]->tolatex() . $p2);
  529   };
  530   /binop2/ && do {
  531     my ($p1, $p2, $p3, $p4) = ('','','','');
  532     if ($args[0] =~ /implicit/) {
  533       if ( (($args[1]->head eq qq(number)) &&
  534             ($args[2]->head eq qq(number))) ||
  535            (($args[1]->head eq qq(binop2)) &&
  536             ($args[1]->[2]->head eq qq(number))) ) {
  537         $args[0] = '\\,';
  538       } else {
  539         $args[0] = ' ';
  540       }
  541     }
  542     if ($args[1]->[0] =~ /binop1|numberE/)
  543       {($p1,$p2)=qw{ \left( \right) };}
  544  #   if ($args[2]->[0] =~ /binop[12]|numberE/)
  545   if ($args[2]->[0] =~ /binop[12]|numberE|unop1/)
  546       {($p3,$p4)=qw{ \left( \right) };}
  547     if ($args[0] eq '/'){
  548   return('\frac{' . $p1 . $args[1]->tolatex() . $p2 . '}'.
  549                '{' . $p3 . $args[2]->tolatex() . $p4 . '}' );
  550     }
  551     else{
  552     return ($p1 . $args[1]->tolatex() . $p2 . $args[0] . $p3 .
  553             $args[2]->tolatex() . $p4);
  554     }
  555   };
  556   /binop3/ && do {
  557     my ($p1, $p2, $p3, $p4)=('','','','');
  558 #    if ($args[1]->[0] =~ /binop[123]|numberE/) {($p1,$p2)=qw{ \left( \right) };}
  559   if ($args[1]->[0] =~ /binop[123]|unop1|numberE/) {($p1,$p2)=qw{ \left( \right) };}
  560 # Not necessary in latex
  561 #   if ($args[2]->[0] =~ /binop[12]/) {($p3,$p4)=qw{ \left( \right) };}
  562     return ($p1 . $args[1]->tolatex() . $p2 . "^{" . $p3 .
  563             $args[2]->tolatex() . $p4 . "}");
  564   };
  565   /func1/ && do {
  566       my($p1,$p2);
  567       if($args[0] eq "sqrt"){($p1,$p2)=qw{ \left{ \right} };}
  568       else {($p1,$p2)=qw{ \left( \right) };}
  569 
  570       $specialfunc = '(?:abs|logten|asin|acos|atan|sech|sgn|step|fact)';
  571 
  572 
  573       if ($args[0] =~ /$specialfunc/) {
  574          return ('\mbox{' . $args[0] .'}'. $p1 . $args[1]->tolatex() . $p2);
  575       }
  576       else {
  577         return ('\\' . $args[0] . $p1 . $args[1]->tolatex() . $p2);
  578       }
  579   };
  580   /special/ && do {
  581     if ($args[0] eq 'pi') {return '\pi';} else {return $args[0];}
  582   };
  583   /varname|(:?number$)/ && return $args[0];
  584   /numberE/ && do {
  585     $args[0] =~ m/($AlgParser::numberplain)E([-+]?\d+)/;
  586     return ($1 . '\times 10^{' . $2 . '}');
  587   };
  588   /closep/ && do {
  589     my($backslash) = '';
  590     my(%close) = %AlgParser::close;
  591     if ($args[0] eq '{') {$backslash = '\\';}
  592 #This is for editors to match: }
  593     return ('\left' . $backslash . $args[0] . $args[1]->tolatex() .
  594             '\right' . $backslash . $close{$args[0]});
  595   };
  596 }
  597 
  598 sub fromarray {
  599   my($class) = shift;
  600   my($expr) = shift;
  601   if ((ref $expr) ne qq{ARRAY}) {
  602     die "Program error; fromarray not passed an array ref.";
  603   }
  604   my($type, @args) = @$expr;
  605   foreach my $i (@args) {
  606     if (ref $i) {
  607       $i = $class->fromarray($i);
  608     }
  609   }
  610   return $class->new($type, @args);
  611 }
  612 
  613 package ExprWithImplicitExpand;
  614 @ISA=qw(Expr);
  615 
  616 
  617 sub tostring {
  618 # print STDERR "ExprWIE::tostring\n";
  619 # print STDERR Data::Dumper->Dump([@_]);
  620   my ($self) = shift;
  621 
  622   my($type, @args) = @$self;
  623 
  624   if (($type eq qq(binop2)) && ($args[0] eq qq(implicit))) {
  625     my ($p1, $p2, $p3, $p4)=('','','','');
  626     if ($args[1]->head =~ /binop1/) {($p1,$p2)=qw{ ( ) };}
  627 #    if ($args[2]->head =~ /binop[12]/) {($p3,$p4)=qw{ ( ) };}
  628     if ($args[2]->head =~ /binop[12]|unop1/) {($p3,$p4)=qw{ ( ) };}
  629     return ($p1 . $args[1]->tostring() . $p2 . '*' . $p3 .
  630             $args[2]->tostring() . $p4);
  631   } else {
  632     return $self->SUPER::tostring(@_);
  633   }
  634 }

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9