183 # Unescape inequality operators
184 $expr = strtr( $expr, [
'<' =>
'<',
'>' =>
'>',
185 '−' =>
'-',
'−' =>
'-' ] );
188 $end = strlen( $expr );
189 $expecting =
'expression';
192 while ( $p < $end ) {
193 if ( count( $operands ) > $this->maxStackSize || count( $operators ) > $this->maxStackSize ) {
194 throw new ExprError(
'stack_exhausted' );
197 $char2 = substr( $expr, $p, 2 );
211 if ( $expecting !==
'expression' ) {
212 throw new ExprError(
'unexpected_number' );
218 $operands[] = (float)substr( $expr, $p, $length );
220 $expecting =
'operator';
222 } elseif ( ctype_alpha( $char ) ) {
225 $remaining = substr( $expr, $p );
226 if ( !preg_match(
'/^[A-Za-z]*/', $remaining,
$matches ) ) {
228 throw new ExprError(
'preg_match_failure' );
231 $p += strlen( $word );
234 if ( !isset( $this->words[$word] ) ) {
235 throw new ExprError(
'unrecognised_word', $word );
237 $op = $this->words[$word];
241 if ( $expecting !==
'expression' ) {
244 $operands[] = exp( 1 );
245 $expecting =
'operator';
248 if ( $expecting !==
'expression' ) {
249 throw new ExprError(
'unexpected_number' );
252 $expecting =
'operator';
269 if ( $expecting !==
'expression' ) {
270 throw new ExprError(
'unexpected_operator', $word );
277 } elseif ( $char2 ===
'<=' ) {
281 } elseif ( $char2 ===
'>=' ) {
285 } elseif ( $char2 ===
'<>' || $char2 ===
'!=' ) {
289 } elseif ( $char ===
'+' ) {
291 if ( $expecting ===
'expression' ) {
299 } elseif ( $char ===
'-' ) {
301 if ( $expecting ===
'expression' ) {
309 } elseif ( $char ===
'*' ) {
313 } elseif ( $char ===
'/' ) {
317 } elseif ( $char ===
'^' ) {
321 } elseif ( $char ===
'(' ) {
322 if ( $expecting ===
'operator' ) {
323 throw new ExprError(
'unexpected_operator',
'(' );
328 } elseif ( $char ===
')' ) {
329 $lastOp = end( $operators );
330 while ( $lastOp && $lastOp !=
EXPR_OPEN ) {
332 array_pop( $operators );
333 $lastOp = end( $operators );
336 array_pop( $operators );
338 throw new ExprError(
'unexpected_closing_bracket' );
340 $expecting =
'operator';
343 } elseif ( $char ===
'=' ) {
347 } elseif ( $char ===
'<' ) {
351 } elseif ( $char ===
'>' ) {
356 $utfExpr = Validator::cleanUp( substr( $expr, $p ) );
357 throw new ExprError(
'unrecognised_punctuation', mb_substr( $utfExpr, 0, 1 ) );
361 if ( $expecting ===
'expression' ) {
362 throw new ExprError(
'unexpected_operator', $name );
366 $lastOp = end( $operators );
367 while ( $lastOp && $this->precedence[$op] <= $this->precedence[$lastOp] ) {
369 array_pop( $operators );
370 $lastOp = end( $operators );
373 $expecting =
'expression';
378 while ( $op = array_pop( $operators ) ) {
381 throw new ExprError(
'unclosed_bracket' );
386 return implode(
"<br />\n", $operands );
397 if ( count( $stack ) < 1 ) {
398 throw new ExprError(
'missing_operand', $this->names[$op] );
400 $arg = array_pop( $stack );
404 if ( count( $stack ) < 1 ) {
405 throw new ExprError(
'missing_operand', $this->names[$op] );
409 if ( count( $stack ) < 2 ) {
410 throw new ExprError(
'missing_operand', $this->names[$op] );
412 $right = array_pop( $stack );
413 $left = array_pop( $stack );
414 $stack[] = $left * $right;
417 if ( count( $stack ) < 2 ) {
418 throw new ExprError(
'missing_operand', $this->names[$op] );
420 $right = array_pop( $stack );
421 $left = array_pop( $stack );
423 throw new ExprError(
'division_by_zero', $this->names[$op] );
425 $stack[] = $left / $right;
428 if ( count( $stack ) < 2 ) {
429 throw new ExprError(
'missing_operand', $this->names[$op] );
431 $right = (int)array_pop( $stack );
432 $left = (int)array_pop( $stack );
434 throw new ExprError(
'division_by_zero', $this->names[$op] );
436 $stack[] = $left % $right;
439 if ( count( $stack ) < 2 ) {
440 throw new ExprError(
'missing_operand', $this->names[$op] );
442 $right = (double)array_pop( $stack );
443 $left = (double)array_pop( $stack );
445 throw new ExprError(
'division_by_zero', $this->names[$op] );
447 $stack[] = fmod( $left, $right );
450 if ( count( $stack ) < 2 ) {
451 throw new ExprError(
'missing_operand', $this->names[$op] );
453 $right = array_pop( $stack );
454 $left = array_pop( $stack );
455 $stack[] = $left + $right;
458 if ( count( $stack ) < 2 ) {
459 throw new ExprError(
'missing_operand', $this->names[$op] );
461 $right = array_pop( $stack );
462 $left = array_pop( $stack );
463 $stack[] = $left - $right;
466 if ( count( $stack ) < 2 ) {
467 throw new ExprError(
'missing_operand', $this->names[$op] );
469 $right = array_pop( $stack );
470 $left = array_pop( $stack );
471 $stack[] = ( $left && $right ) ? 1 : 0;
474 if ( count( $stack ) < 2 ) {
475 throw new ExprError(
'missing_operand', $this->names[$op] );
477 $right = array_pop( $stack );
478 $left = array_pop( $stack );
479 $stack[] = ( $left || $right ) ? 1 : 0;
482 if ( count( $stack ) < 2 ) {
483 throw new ExprError(
'missing_operand', $this->names[$op] );
485 $right = array_pop( $stack );
486 $left = array_pop( $stack );
487 $stack[] = ( $left == $right ) ? 1 : 0;
490 if ( count( $stack ) < 1 ) {
491 throw new ExprError(
'missing_operand', $this->names[$op] );
493 $arg = array_pop( $stack );
494 $stack[] = ( !$arg ) ? 1 : 0;
497 if ( count( $stack ) < 2 ) {
498 throw new ExprError(
'missing_operand', $this->names[$op] );
500 $digits = (int)array_pop( $stack );
501 $value = array_pop( $stack );
502 $stack[] = round(
$value, $digits );
505 if ( count( $stack ) < 2 ) {
506 throw new ExprError(
'missing_operand', $this->names[$op] );
508 $right = array_pop( $stack );
509 $left = array_pop( $stack );
510 $stack[] = ( $left < $right ) ? 1 : 0;
513 if ( count( $stack ) < 2 ) {
514 throw new ExprError(
'missing_operand', $this->names[$op] );
516 $right = array_pop( $stack );
517 $left = array_pop( $stack );
518 $stack[] = ( $left > $right ) ? 1 : 0;
521 if ( count( $stack ) < 2 ) {
522 throw new ExprError(
'missing_operand', $this->names[$op] );
524 $right = array_pop( $stack );
525 $left = array_pop( $stack );
526 $stack[] = ( $left <= $right ) ? 1 : 0;
529 if ( count( $stack ) < 2 ) {
530 throw new ExprError(
'missing_operand', $this->names[$op] );
532 $right = array_pop( $stack );
533 $left = array_pop( $stack );
534 $stack[] = ( $left >= $right ) ? 1 : 0;
537 if ( count( $stack ) < 2 ) {
538 throw new ExprError(
'missing_operand', $this->names[$op] );
540 $right = array_pop( $stack );
541 $left = array_pop( $stack );
542 $stack[] = ( $left != $right ) ? 1 : 0;
545 if ( count( $stack ) < 2 ) {
546 throw new ExprError(
'missing_operand', $this->names[$op] );
548 $right = array_pop( $stack );
549 $left = array_pop( $stack );
550 $stack[] = $left * pow( 10, $right );
553 if ( count( $stack ) < 1 ) {
554 throw new ExprError(
'missing_operand', $this->names[$op] );
556 $arg = array_pop( $stack );
557 $stack[] = sin( $arg );
560 if ( count( $stack ) < 1 ) {
561 throw new ExprError(
'missing_operand', $this->names[$op] );
563 $arg = array_pop( $stack );
564 $stack[] = cos( $arg );
567 if ( count( $stack ) < 1 ) {
568 throw new ExprError(
'missing_operand', $this->names[$op] );
570 $arg = array_pop( $stack );
571 $stack[] = tan( $arg );
574 if ( count( $stack ) < 1 ) {
575 throw new ExprError(
'missing_operand', $this->names[$op] );
577 $arg = array_pop( $stack );
578 if ( $arg < -1 || $arg > 1 ) {
579 throw new ExprError(
'invalid_argument', $this->names[$op] );
581 $stack[] = asin( $arg );
584 if ( count( $stack ) < 1 ) {
585 throw new ExprError(
'missing_operand', $this->names[$op] );
587 $arg = array_pop( $stack );
588 if ( $arg < -1 || $arg > 1 ) {
589 throw new ExprError(
'invalid_argument', $this->names[$op] );
591 $stack[] = acos( $arg );
594 if ( count( $stack ) < 1 ) {
595 throw new ExprError(
'missing_operand', $this->names[$op] );
597 $arg = array_pop( $stack );
598 $stack[] = atan( $arg );
601 if ( count( $stack ) < 1 ) {
602 throw new ExprError(
'missing_operand', $this->names[$op] );
604 $arg = array_pop( $stack );
605 $stack[] = exp( $arg );
608 if ( count( $stack ) < 1 ) {
609 throw new ExprError(
'missing_operand', $this->names[$op] );
611 $arg = array_pop( $stack );
613 throw new ExprError(
'invalid_argument_ln', $this->names[$op] );
615 $stack[] = log( $arg );
618 if ( count( $stack ) < 1 ) {
619 throw new ExprError(
'missing_operand', $this->names[$op] );
621 $arg = array_pop( $stack );
622 $stack[] = abs( $arg );
625 if ( count( $stack ) < 1 ) {
626 throw new ExprError(
'missing_operand', $this->names[$op] );
628 $arg = array_pop( $stack );
629 $stack[] = floor( $arg );
632 if ( count( $stack ) < 1 ) {
633 throw new ExprError(
'missing_operand', $this->names[$op] );
635 $arg = array_pop( $stack );
636 $stack[] = (int)$arg;
639 if ( count( $stack ) < 1 ) {
640 throw new ExprError(
'missing_operand', $this->names[$op] );
642 $arg = array_pop( $stack );
643 $stack[] = ceil( $arg );
646 if ( count( $stack ) < 2 ) {
647 throw new ExprError(
'missing_operand', $this->names[$op] );
649 $right = array_pop( $stack );
650 $left = array_pop( $stack );
651 $result = pow( $left, $right );
652 if ( $result ===
false ) {
653 throw new ExprError(
'division_by_zero', $this->names[$op] );
658 if ( count( $stack ) < 1 ) {
659 throw new ExprError(
'missing_operand', $this->names[$op] );
661 $arg = array_pop( $stack );
662 $result = sqrt( $arg );
663 if ( is_nan( $result ) ) {
664 throw new ExprError(
'not_a_number', $this->names[$op] );