[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/extensions/ParserFunctions/ -> Expr.php (source)

   1  <?php
   2  
   3  if ( !defined( 'MEDIAWIKI' ) ) {
   4      die( 'This file is a MediaWiki extension, it is not a valid entry point' );
   5  }
   6  
   7  // Character classes
   8  define( 'EXPR_WHITE_CLASS', " \t\r\n" );
   9  define( 'EXPR_NUMBER_CLASS', '0123456789.' );
  10  
  11  // Token types
  12  define( 'EXPR_WHITE', 1 );
  13  define( 'EXPR_NUMBER', 2 );
  14  define( 'EXPR_NEGATIVE', 3 );
  15  define( 'EXPR_POSITIVE', 4 );
  16  define( 'EXPR_PLUS', 5 );
  17  define( 'EXPR_MINUS', 6 );
  18  define( 'EXPR_TIMES', 7 );
  19  define( 'EXPR_DIVIDE', 8 );
  20  define( 'EXPR_MOD', 9 );
  21  define( 'EXPR_OPEN', 10 );
  22  define( 'EXPR_CLOSE', 11 );
  23  define( 'EXPR_AND', 12 );
  24  define( 'EXPR_OR', 13 );
  25  define( 'EXPR_NOT', 14 );
  26  define( 'EXPR_EQUALITY', 15 );
  27  define( 'EXPR_LESS', 16 );
  28  define( 'EXPR_GREATER', 17 );
  29  define( 'EXPR_LESSEQ', 18 );
  30  define( 'EXPR_GREATEREQ', 19 );
  31  define( 'EXPR_NOTEQ', 20 );
  32  define( 'EXPR_ROUND', 21 );
  33  define( 'EXPR_EXPONENT', 22 );
  34  define( 'EXPR_SINE', 23 );
  35  define( 'EXPR_COSINE', 24 );
  36  define( 'EXPR_TANGENS', 25 );
  37  define( 'EXPR_ARCSINE', 26 );
  38  define( 'EXPR_ARCCOS', 27 );
  39  define( 'EXPR_ARCTAN', 28 );
  40  define( 'EXPR_EXP', 29 );
  41  define( 'EXPR_LN', 30 );
  42  define( 'EXPR_ABS', 31 );
  43  define( 'EXPR_FLOOR', 32 );
  44  define( 'EXPR_TRUNC', 33 );
  45  define( 'EXPR_CEIL', 34 );
  46  define( 'EXPR_POW', 35 );
  47  define( 'EXPR_PI', 36 );
  48  define( 'EXPR_FMOD', 37 );
  49  define( 'EXPR_SQRT' , 38 );
  50  
  51  class ExprError extends MWException {
  52      /**
  53       * @param $msg string
  54       * @param $parameter string
  55       */
  56  	public function __construct( $msg, $parameter = '' ) {
  57          // Give grep a chance to find the usages:
  58          // pfunc_expr_stack_exhausted, pfunc_expr_unexpected_number, pfunc_expr_preg_match_failure,
  59          // pfunc_expr_unrecognised_word, pfunc_expr_unexpected_operator, pfunc_expr_missing_operand,
  60          // pfunc_expr_unexpected_closing_bracket, pfunc_expr_unrecognised_punctuation,
  61          // pfunc_expr_unclosed_bracket, pfunc_expr_division_by_zero, pfunc_expr_invalid_argument,
  62          // pfunc_expr_invalid_argument_ln, pfunc_expr_unknown_error, pfunc_expr_not_a_number
  63          $this->message = wfMessage( "pfunc_expr_$msg", $parameter )->inContentLanguage()->text();
  64      }
  65  }
  66  
  67  class ExprParser {
  68      public $maxStackSize = 100;
  69  
  70      public $precedence = array(
  71          EXPR_NEGATIVE => 10,
  72          EXPR_POSITIVE => 10,
  73          EXPR_EXPONENT => 10,
  74          EXPR_SINE => 9,
  75          EXPR_COSINE => 9,
  76          EXPR_TANGENS => 9,
  77          EXPR_ARCSINE => 9,
  78          EXPR_ARCCOS => 9,
  79          EXPR_ARCTAN => 9,
  80          EXPR_EXP => 9,
  81          EXPR_LN => 9,
  82          EXPR_ABS => 9,
  83          EXPR_FLOOR => 9,
  84          EXPR_TRUNC => 9,
  85          EXPR_CEIL => 9,
  86          EXPR_NOT => 9,
  87          EXPR_SQRT => 9,
  88          EXPR_POW => 8,
  89          EXPR_TIMES => 7,
  90          EXPR_DIVIDE => 7,
  91          EXPR_MOD => 7,
  92          EXPR_FMOD => 7,
  93          EXPR_PLUS => 6,
  94          EXPR_MINUS => 6,
  95          EXPR_ROUND => 5,
  96          EXPR_EQUALITY => 4,
  97          EXPR_LESS => 4,
  98          EXPR_GREATER => 4,
  99          EXPR_LESSEQ => 4,
 100          EXPR_GREATEREQ => 4,
 101          EXPR_NOTEQ => 4,
 102          EXPR_AND => 3,
 103          EXPR_OR => 2,
 104          EXPR_PI => 0,
 105          EXPR_OPEN => -1,
 106          EXPR_CLOSE => -1,
 107      );
 108  
 109      public $names = array(
 110          EXPR_NEGATIVE => '-',
 111          EXPR_POSITIVE => '+',
 112          EXPR_NOT => 'not',
 113          EXPR_TIMES => '*',
 114          EXPR_DIVIDE => '/',
 115          EXPR_MOD => 'mod',
 116          EXPR_FMOD => 'fmod',
 117          EXPR_PLUS => '+',
 118          EXPR_MINUS => '-',
 119          EXPR_ROUND => 'round',
 120          EXPR_EQUALITY => '=',
 121          EXPR_LESS => '<',
 122          EXPR_GREATER => '>',
 123          EXPR_LESSEQ => '<=',
 124          EXPR_GREATEREQ => '>=',
 125          EXPR_NOTEQ => '<>',
 126          EXPR_AND => 'and',
 127          EXPR_OR => 'or',
 128          EXPR_EXPONENT => 'e',
 129          EXPR_SINE => 'sin',
 130          EXPR_COSINE => 'cos',
 131          EXPR_TANGENS => 'tan',
 132          EXPR_ARCSINE => 'asin',
 133          EXPR_ARCCOS => 'acos',
 134          EXPR_ARCTAN => 'atan',
 135          EXPR_LN => 'ln',
 136          EXPR_EXP => 'exp',
 137          EXPR_ABS => 'abs',
 138          EXPR_FLOOR => 'floor',
 139          EXPR_TRUNC => 'trunc',
 140          EXPR_CEIL => 'ceil',
 141          EXPR_POW => '^',
 142          EXPR_PI => 'pi',
 143          EXPR_SQRT => 'sqrt',
 144      );
 145  
 146      public $words = array(
 147          'mod' => EXPR_MOD,
 148          'fmod' => EXPR_FMOD,
 149          'and' => EXPR_AND,
 150          'or' => EXPR_OR,
 151          'not' => EXPR_NOT,
 152          'round' => EXPR_ROUND,
 153          'div' => EXPR_DIVIDE,
 154          'e' => EXPR_EXPONENT,
 155          'sin' => EXPR_SINE,
 156          'cos' => EXPR_COSINE,
 157          'tan' => EXPR_TANGENS,
 158          'asin' => EXPR_ARCSINE,
 159          'acos' => EXPR_ARCCOS,
 160          'atan' => EXPR_ARCTAN,
 161          'exp' => EXPR_EXP,
 162          'ln' => EXPR_LN,
 163          'abs' => EXPR_ABS,
 164          'trunc' => EXPR_TRUNC,
 165          'floor' => EXPR_FLOOR,
 166          'ceil' => EXPR_CEIL,
 167          'pi' => EXPR_PI,
 168          'sqrt' => EXPR_SQRT,
 169      );
 170  
 171      /**
 172       * Evaluate a mathematical expression
 173       *
 174       * The algorithm here is based on the infix to RPN algorithm given in
 175       * http://montcs.bloomu.edu/~bobmon/Information/RPN/infix2rpn.shtml
 176       * It's essentially the same as Dijkstra's shunting yard algorithm.
 177       * @param $expr string
 178       * @throws ExprError
 179       * @return string
 180       */
 181  	function doExpression( $expr ) {
 182          $operands = array();
 183          $operators = array();
 184  
 185          # Unescape inequality operators
 186          $expr = strtr( $expr, array( '&lt;' => '<', '&gt;' => '>',
 187              '&minus;' => '-', '−' => '-' ) );
 188  
 189          $p = 0;
 190          $end = strlen( $expr );
 191          $expecting = 'expression';
 192          $name = '';
 193  
 194          while ( $p < $end ) {
 195              if ( count( $operands ) > $this->maxStackSize || count( $operators ) > $this->maxStackSize ) {
 196                  throw new ExprError( 'stack_exhausted' );
 197              }
 198              $char = $expr[$p];
 199              $char2 = substr( $expr, $p, 2 );
 200  
 201              // Mega if-elseif-else construct
 202              // Only binary operators fall through for processing at the bottom, the rest
 203              // finish their processing and continue
 204  
 205              // First the unlimited length classes
 206  
 207              if ( false !== strpos( EXPR_WHITE_CLASS, $char ) ) {
 208                  // Whitespace
 209                  $p += strspn( $expr, EXPR_WHITE_CLASS, $p );
 210                  continue;
 211              } elseif ( false !== strpos( EXPR_NUMBER_CLASS, $char ) ) {
 212                  // Number
 213                  if ( $expecting != 'expression' ) {
 214                      throw new ExprError( 'unexpected_number' );
 215                  }
 216  
 217                  // Find the rest of it
 218                  $length = strspn( $expr, EXPR_NUMBER_CLASS, $p );
 219                  // Convert it to float, silently removing double decimal points
 220                  $operands[] = floatval( substr( $expr, $p, $length ) );
 221                  $p += $length;
 222                  $expecting = 'operator';
 223                  continue;
 224              } elseif ( ctype_alpha( $char ) ) {
 225                  // Word
 226                  // Find the rest of it
 227                  $remaining = substr( $expr, $p );
 228                  if ( !preg_match( '/^[A-Za-z]*/', $remaining, $matches ) ) {
 229                      // This should be unreachable
 230                      throw new ExprError( 'preg_match_failure' );
 231                  }
 232                  $word = strtolower( $matches[0] );
 233                  $p += strlen( $word );
 234  
 235                  // Interpret the word
 236                  if ( !isset( $this->words[$word] ) ) {
 237                      throw new ExprError( 'unrecognised_word', $word );
 238                  }
 239                  $op = $this->words[$word];
 240                  switch( $op ) {
 241                  // constant
 242                  case EXPR_EXPONENT:
 243                      if ( $expecting != 'expression' ) {
 244                          continue;
 245                      }
 246                      $operands[] = exp( 1 );
 247                      $expecting = 'operator';
 248                      continue 2;
 249                  case EXPR_PI:
 250                      if ( $expecting != 'expression' ) {
 251                          throw new ExprError( 'unexpected_number' );
 252                      }
 253                      $operands[] = pi();
 254                      $expecting = 'operator';
 255                      continue 2;
 256                  // Unary operator
 257                  case EXPR_NOT:
 258                  case EXPR_SINE:
 259                  case EXPR_COSINE:
 260                  case EXPR_TANGENS:
 261                  case EXPR_ARCSINE:
 262                  case EXPR_ARCCOS:
 263                  case EXPR_ARCTAN:
 264                  case EXPR_EXP:
 265                  case EXPR_LN:
 266                  case EXPR_ABS:
 267                  case EXPR_FLOOR:
 268                  case EXPR_TRUNC:
 269                  case EXPR_CEIL:
 270                  case EXPR_SQRT:
 271                      if ( $expecting != 'expression' ) {
 272                          throw new ExprError( 'unexpected_operator', $word );
 273                      }
 274                      $operators[] = $op;
 275                      continue 2;
 276                  }
 277                  // Binary operator, fall through
 278                  $name = $word;
 279              }
 280  
 281              // Next the two-character operators
 282  
 283              elseif ( $char2 == '<=' ) {
 284                  $name = $char2;
 285                  $op = EXPR_LESSEQ;
 286                  $p += 2;
 287              } elseif ( $char2 == '>=' ) {
 288                  $name = $char2;
 289                  $op = EXPR_GREATEREQ;
 290                  $p += 2;
 291              } elseif ( $char2 == '<>' || $char2 == '!=' ) {
 292                  $name = $char2;
 293                  $op = EXPR_NOTEQ;
 294                  $p += 2;
 295              }
 296  
 297              // Finally the single-character operators
 298  
 299              elseif ( $char == '+' ) {
 300                  ++$p;
 301                  if ( $expecting == 'expression' ) {
 302                      // Unary plus
 303                      $operators[] = EXPR_POSITIVE;
 304                      continue;
 305                  } else {
 306                      // Binary plus
 307                      $op = EXPR_PLUS;
 308                  }
 309              } elseif ( $char == '-' ) {
 310                  ++$p;
 311                  if ( $expecting == 'expression' ) {
 312                      // Unary minus
 313                      $operators[] = EXPR_NEGATIVE;
 314                      continue;
 315                  } else {
 316                      // Binary minus
 317                      $op = EXPR_MINUS;
 318                  }
 319              } elseif ( $char == '*' ) {
 320                  $name = $char;
 321                  $op = EXPR_TIMES;
 322                  ++$p;
 323              } elseif ( $char == '/' ) {
 324                  $name = $char;
 325                  $op = EXPR_DIVIDE;
 326                  ++$p;
 327              } elseif ( $char == '^' ) {
 328                  $name = $char;
 329                  $op = EXPR_POW;
 330                  ++$p;
 331              } elseif ( $char == '(' )  {
 332                  if ( $expecting == 'operator' ) {
 333                      throw new ExprError( 'unexpected_operator', '(' );
 334                  }
 335                  $operators[] = EXPR_OPEN;
 336                  ++$p;
 337                  continue;
 338              } elseif ( $char == ')' ) {
 339                  $lastOp = end( $operators );
 340                  while ( $lastOp && $lastOp != EXPR_OPEN ) {
 341                      $this->doOperation( $lastOp, $operands );
 342                      array_pop( $operators );
 343                      $lastOp = end( $operators );
 344                  }
 345                  if ( $lastOp ) {
 346                      array_pop( $operators );
 347                  } else {
 348                      throw new ExprError( 'unexpected_closing_bracket' );
 349                  }
 350                  $expecting = 'operator';
 351                  ++$p;
 352                  continue;
 353              } elseif ( $char == '=' ) {
 354                  $name = $char;
 355                  $op = EXPR_EQUALITY;
 356                  ++$p;
 357              } elseif ( $char == '<' ) {
 358                  $name = $char;
 359                  $op = EXPR_LESS;
 360                  ++$p;
 361              } elseif ( $char == '>' ) {
 362                  $name = $char;
 363                  $op = EXPR_GREATER;
 364                  ++$p;
 365              } else {
 366                  throw new ExprError( 'unrecognised_punctuation', UtfNormal::cleanUp( $char ) );
 367              }
 368  
 369              // Binary operator processing
 370              if ( $expecting == 'expression' ) {
 371                  throw new ExprError( 'unexpected_operator', $name );
 372              }
 373  
 374              // Shunting yard magic
 375              $lastOp = end( $operators );
 376              while ( $lastOp && $this->precedence[$op] <= $this->precedence[$lastOp] ) {
 377                  $this->doOperation( $lastOp, $operands );
 378                  array_pop( $operators );
 379                  $lastOp = end( $operators );
 380              }
 381              $operators[] = $op;
 382              $expecting = 'expression';
 383          }
 384  
 385          // Finish off the operator array
 386          while ( $op = array_pop( $operators ) ) {
 387              if ( $op == EXPR_OPEN ) {
 388                  throw new ExprError( 'unclosed_bracket' );
 389              }
 390              $this->doOperation( $op, $operands );
 391          }
 392  
 393          return implode( "<br />\n", $operands );
 394      }
 395  
 396      /**
 397       * @param $op int
 398       * @param $stack array
 399       * @throws ExprError
 400       */
 401  	function doOperation( $op, &$stack ) {
 402          switch ( $op ) {
 403              case EXPR_NEGATIVE:
 404                  if ( count( $stack ) < 1 ) {
 405                      throw new ExprError( 'missing_operand', $this->names[$op] );
 406                  }
 407                  $arg = array_pop( $stack );
 408                  $stack[] = -$arg;
 409                  break;
 410              case EXPR_POSITIVE:
 411                  if ( count( $stack ) < 1 ) {
 412                      throw new ExprError( 'missing_operand', $this->names[$op] );
 413                  }
 414                  break;
 415              case EXPR_TIMES:
 416                  if ( count( $stack ) < 2 ) {
 417                      throw new ExprError( 'missing_operand', $this->names[$op] );
 418                  }
 419                  $right = array_pop( $stack );
 420                  $left = array_pop( $stack );
 421                  $stack[] = $left * $right;
 422                      break;
 423              case EXPR_DIVIDE:
 424                  if ( count( $stack ) < 2 ) {
 425                      throw new ExprError( 'missing_operand', $this->names[$op] );
 426                  }
 427                  $right = array_pop( $stack );
 428                  $left = array_pop( $stack );
 429                  if ( !$right ) {
 430                      throw new ExprError( 'division_by_zero', $this->names[$op] );
 431                  }
 432                  $stack[] = $left / $right;
 433                  break;
 434              case EXPR_MOD:
 435                  if ( count( $stack ) < 2 ) {
 436                      throw new ExprError( 'missing_operand', $this->names[$op] );
 437                  }
 438                  $right = (int)array_pop( $stack );
 439                  $left = (int)array_pop( $stack );
 440                  if ( !$right ) {
 441                      throw new ExprError( 'division_by_zero', $this->names[$op] );
 442                  }
 443                  $stack[] = $left % $right;
 444                  break;
 445              case EXPR_FMOD:
 446                  if ( count( $stack ) < 2 ) {
 447                      throw new ExprError( 'missing_operand', $this->names[$op] );
 448                  }
 449                  $right = (double)array_pop( $stack );
 450                  $left = (double)array_pop( $stack );
 451                  if ( !$right ) {
 452                      throw new ExprError( 'division_by_zero', $this->names[$op] );
 453                  }
 454                  $stack[] = fmod( $left, $right );
 455                  break;
 456              case EXPR_PLUS:
 457                  if ( count( $stack ) < 2 ) {
 458                      throw new ExprError( 'missing_operand', $this->names[$op] );
 459                  }
 460                  $right = array_pop( $stack );
 461                  $left = array_pop( $stack );
 462                  $stack[] = $left + $right;
 463                  break;
 464              case EXPR_MINUS:
 465                  if ( count( $stack ) < 2 ) {
 466                      throw new ExprError( 'missing_operand', $this->names[$op] );
 467                  }
 468                  $right = array_pop( $stack );
 469                  $left = array_pop( $stack );
 470                  $stack[] = $left - $right;
 471                  break;
 472              case EXPR_AND:
 473                  if ( count( $stack ) < 2 ) {
 474                      throw new ExprError( 'missing_operand', $this->names[$op] );
 475                  }
 476                  $right = array_pop( $stack );
 477                  $left = array_pop( $stack );
 478                  $stack[] = ( $left && $right ) ? 1 : 0;
 479                  break;
 480              case EXPR_OR:
 481                  if ( count( $stack ) < 2 ) {
 482                      throw new ExprError( 'missing_operand', $this->names[$op] );
 483                  }
 484                  $right = array_pop( $stack );
 485                  $left = array_pop( $stack );
 486                  $stack[] = ( $left || $right ) ? 1 : 0;
 487                  break;
 488              case EXPR_EQUALITY:
 489                  if ( count( $stack ) < 2 ) {
 490                      throw new ExprError( 'missing_operand', $this->names[$op] );
 491                  }
 492                  $right = array_pop( $stack );
 493                  $left = array_pop( $stack );
 494                  $stack[] = ( $left == $right ) ? 1 : 0;
 495                  break;
 496              case EXPR_NOT:
 497                  if ( count( $stack ) < 1 ) {
 498                      throw new ExprError( 'missing_operand', $this->names[$op] );
 499                  }
 500                  $arg = array_pop( $stack );
 501                  $stack[] = ( !$arg ) ? 1 : 0;
 502                  break;
 503              case EXPR_ROUND:
 504                  if ( count( $stack ) < 2 ) {
 505                      throw new ExprError( 'missing_operand', $this->names[$op] );
 506                  }
 507                  $digits = intval( array_pop( $stack ) );
 508                  $value = array_pop( $stack );
 509                  $stack[] = round( $value, $digits );
 510                  break;
 511              case EXPR_LESS:
 512                  if ( count( $stack ) < 2 ) {
 513                      throw new ExprError( 'missing_operand', $this->names[$op] );
 514                  }
 515                  $right = array_pop( $stack );
 516                  $left = array_pop( $stack );
 517                  $stack[] = ( $left < $right ) ? 1 : 0;
 518                  break;
 519              case EXPR_GREATER:
 520                  if ( count( $stack ) < 2 ) {
 521                      throw new ExprError( 'missing_operand', $this->names[$op] );
 522                  }
 523                  $right = array_pop( $stack );
 524                  $left = array_pop( $stack );
 525                  $stack[] = ( $left > $right ) ? 1 : 0;
 526                  break;
 527              case EXPR_LESSEQ:
 528                  if ( count( $stack ) < 2 ) {
 529                      throw new ExprError( 'missing_operand', $this->names[$op] );
 530                  }
 531                  $right = array_pop( $stack );
 532                  $left = array_pop( $stack );
 533                  $stack[] = ( $left <= $right ) ? 1 : 0;
 534                  break;
 535              case EXPR_GREATEREQ:
 536                  if ( count( $stack ) < 2 ) {
 537                      throw new ExprError( 'missing_operand', $this->names[$op] );
 538                  }
 539                  $right = array_pop( $stack );
 540                  $left = array_pop( $stack );
 541                  $stack[] = ( $left >= $right ) ? 1 : 0;
 542                  break;
 543              case EXPR_NOTEQ:
 544                  if ( count( $stack ) < 2 ) {
 545                      throw new ExprError( 'missing_operand', $this->names[$op] );
 546                  }
 547                  $right = array_pop( $stack );
 548                  $left = array_pop( $stack );
 549                  $stack[] = ( $left != $right ) ? 1 : 0;
 550                  break;
 551              case EXPR_EXPONENT:
 552                  if ( count( $stack ) < 2 ) {
 553                      throw new ExprError( 'missing_operand', $this->names[$op] );
 554                  }
 555                  $right = array_pop( $stack );
 556                  $left = array_pop( $stack );
 557                  $stack[] = $left * pow( 10, $right );
 558                  break;
 559              case EXPR_SINE:
 560                  if ( count( $stack ) < 1 ) {
 561                      throw new ExprError( 'missing_operand', $this->names[$op] );
 562                  }
 563                  $arg = array_pop( $stack );
 564                  $stack[] = sin( $arg );
 565                  break;
 566              case EXPR_COSINE:
 567                  if ( count( $stack ) < 1 ) {
 568                      throw new ExprError( 'missing_operand', $this->names[$op] );
 569                  }
 570                  $arg = array_pop( $stack );
 571                  $stack[] = cos( $arg );
 572                  break;
 573              case EXPR_TANGENS:
 574                  if ( count( $stack ) < 1 ) {
 575                      throw new ExprError( 'missing_operand', $this->names[$op] );
 576                  }
 577                  $arg = array_pop( $stack );
 578                  $stack[] = tan( $arg );
 579                  break;
 580              case EXPR_ARCSINE:
 581                  if ( count( $stack ) < 1 ) {
 582                      throw new ExprError( 'missing_operand', $this->names[$op] );
 583                  }
 584                  $arg = array_pop( $stack );
 585                  if ( $arg < -1 || $arg > 1 ) {
 586                      throw new ExprError( 'invalid_argument', $this->names[$op] );
 587                  }
 588                  $stack[] = asin( $arg );
 589                  break;
 590              case EXPR_ARCCOS:
 591                  if ( count( $stack ) < 1 ) {
 592                      throw new ExprError( 'missing_operand', $this->names[$op] );
 593                  }
 594                  $arg = array_pop( $stack );
 595                  if ( $arg < -1 || $arg > 1 ) {
 596                      throw new ExprError( 'invalid_argument', $this->names[$op] );
 597                  }
 598                  $stack[] = acos( $arg );
 599                  break;
 600              case EXPR_ARCTAN:
 601                  if ( count( $stack ) < 1 ) {
 602                      throw new ExprError( 'missing_operand', $this->names[$op] );
 603                  }
 604                  $arg = array_pop( $stack );
 605                  $stack[] = atan( $arg );
 606                  break;
 607              case EXPR_EXP:
 608                  if ( count( $stack ) < 1 ) {
 609                      throw new ExprError( 'missing_operand', $this->names[$op] );
 610                  }
 611                  $arg = array_pop( $stack );
 612                  $stack[] = exp( $arg );
 613                  break;
 614              case EXPR_LN:
 615                  if ( count( $stack ) < 1 ) {
 616                      throw new ExprError( 'missing_operand', $this->names[$op] );
 617                  }
 618                  $arg = array_pop( $stack );
 619                  if ( $arg <= 0 ) {
 620                      throw new ExprError( 'invalid_argument_ln', $this->names[$op] );
 621                  }
 622                  $stack[] = log( $arg );
 623                  break;
 624              case EXPR_ABS:
 625                  if ( count( $stack ) < 1 ) {
 626                      throw new ExprError( 'missing_operand', $this->names[$op] );
 627                  }
 628                  $arg = array_pop( $stack );
 629                  $stack[] = abs( $arg );
 630                  break;
 631              case EXPR_FLOOR:
 632                  if ( count( $stack ) < 1 ) {
 633                      throw new ExprError( 'missing_operand', $this->names[$op] );
 634                  }
 635                  $arg = array_pop( $stack );
 636                  $stack[] = floor( $arg );
 637                  break;
 638              case EXPR_TRUNC:
 639                  if ( count( $stack ) < 1 ) {
 640                      throw new ExprError( 'missing_operand', $this->names[$op] );
 641                  }
 642                  $arg = array_pop( $stack );
 643                  $stack[] = (int)$arg;
 644                  break;
 645              case EXPR_CEIL:
 646                  if ( count( $stack ) < 1 ) {
 647                      throw new ExprError( 'missing_operand', $this->names[$op] );
 648                  }
 649                  $arg = array_pop( $stack );
 650                  $stack[] = ceil( $arg );
 651                  break;
 652              case EXPR_POW:
 653                  if ( count( $stack ) < 2 ) {
 654                      throw new ExprError( 'missing_operand', $this->names[$op] );
 655                  }
 656                  $right = array_pop( $stack );
 657                  $left = array_pop( $stack );
 658                  if ( false === ( $stack[] = pow( $left, $right ) ) ) {
 659                      throw new ExprError( 'division_by_zero', $this->names[$op] );
 660                  }
 661                  break;
 662              case EXPR_SQRT:
 663                  if ( count( $stack ) < 1 ) {
 664                      throw new ExprError( 'missing_operand', $this->names[$op] );
 665                  }
 666                  $arg = array_pop( $stack );
 667                  $result = sqrt( $arg );
 668                  if ( is_nan( $result ) ) {
 669                      throw new ExprError( 'not_a_number', $this->names[$op] );
 670                  }
 671                  $stack[] = $result;
 672                  break;
 673              default:
 674                  // Should be impossible to reach here.
 675                  throw new ExprError( 'unknown_error' );
 676          }
 677      }
 678  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1