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