189 # Unescape inequality operators
190 $expr = strtr( $expr, [
'<' =>
'<',
'>' =>
'>',
191 '−' =>
'-',
'−' =>
'-' ] );
194 $end = strlen( $expr );
195 $expecting =
'expression';
198 while ( $p < $end ) {
199 if ( count( $operands ) > $this->maxStackSize || count( $operators ) > $this->maxStackSize ) {
200 throw new ExprError(
'stack_exhausted' );
203 $char2 = substr( $expr, $p, 2 );
219 if ( $expecting !==
'expression' ) {
220 throw new ExprError(
'unexpected_number' );
226 $operands[] = (float)substr( $expr, $p, $length );
228 $expecting =
'operator';
230 } elseif ( ctype_alpha( $char ) ) {
233 $remaining = substr( $expr, $p );
234 if ( !preg_match(
'/^[A-Za-z]*/', $remaining,
$matches ) ) {
236 throw new ExprError(
'preg_match_failure' );
239 $p += strlen( $word );
242 if ( !isset( $this->words[$word] ) ) {
243 throw new ExprError(
'unrecognised_word', $word );
245 $op = $this->words[$word];
249 if ( $expecting !==
'expression' ) {
252 $operands[] = exp( 1 );
253 $expecting =
'operator';
256 if ( $expecting !==
'expression' ) {
257 throw new ExprError(
'unexpected_number' );
260 $expecting =
'operator';
277 if ( $expecting !==
'expression' ) {
278 throw new ExprError(
'unexpected_operator', $word );
285 } elseif ( $char2 ===
'<=' ) {
289 } elseif ( $char2 ===
'>=' ) {
293 } elseif ( $char2 ===
'<>' || $char2 ===
'!=' ) {
297 } elseif ( $char ===
'+' ) {
299 if ( $expecting ===
'expression' ) {
307 } elseif ( $char ===
'-' ) {
309 if ( $expecting ===
'expression' ) {
317 } elseif ( $char ===
'*' ) {
321 } elseif ( $char ===
'/' ) {
325 } elseif ( $char ===
'^' ) {
329 } elseif ( $char ===
'(' ) {
330 if ( $expecting ===
'operator' ) {
331 throw new ExprError(
'unexpected_operator',
'(' );
336 } elseif ( $char ===
')' ) {
337 $lastOp = end( $operators );
338 while ( $lastOp && $lastOp !=
EXPR_OPEN ) {
340 array_pop( $operators );
341 $lastOp = end( $operators );
344 array_pop( $operators );
346 throw new ExprError(
'unexpected_closing_bracket' );
348 $expecting =
'operator';
351 } elseif ( $char ===
'=' ) {
355 } elseif ( $char ===
'<' ) {
359 } elseif ( $char ===
'>' ) {
364 $utfExpr = Validator::cleanUp( substr( $expr, $p ) );
365 throw new ExprError(
'unrecognised_punctuation', mb_substr( $utfExpr, 0, 1 ) );
369 if ( $expecting ===
'expression' ) {
370 throw new ExprError(
'unexpected_operator', $name );
374 $lastOp = end( $operators );
375 while ( $lastOp && $this->precedence[$op] <= $this->precedence[$lastOp] ) {
377 array_pop( $operators );
378 $lastOp = end( $operators );
381 $expecting =
'expression';
386 while ( $op = array_pop( $operators ) ) {
389 throw new ExprError(
'unclosed_bracket' );
394 return implode(
"<br />\n", $operands );
405 if ( count( $stack ) < 1 ) {
406 throw new ExprError(
'missing_operand', $this->names[$op] );
408 $arg = array_pop( $stack );
412 if ( count( $stack ) < 1 ) {
413 throw new ExprError(
'missing_operand', $this->names[$op] );
417 if ( count( $stack ) < 2 ) {
418 throw new ExprError(
'missing_operand', $this->names[$op] );
420 $right = array_pop( $stack );
421 $left = array_pop( $stack );
422 $stack[] = $left * $right;
425 if ( count( $stack ) < 2 ) {
426 throw new ExprError(
'missing_operand', $this->names[$op] );
428 $right = array_pop( $stack );
429 $left = array_pop( $stack );
431 throw new ExprError(
'division_by_zero', $this->names[$op] );
433 $stack[] = $left / $right;
436 if ( count( $stack ) < 2 ) {
437 throw new ExprError(
'missing_operand', $this->names[$op] );
439 $right = (int)array_pop( $stack );
440 $left = (int)array_pop( $stack );
442 throw new ExprError(
'division_by_zero', $this->names[$op] );
444 $stack[] = $left % $right;
447 if ( count( $stack ) < 2 ) {
448 throw new ExprError(
'missing_operand', $this->names[$op] );
450 $right = (double)array_pop( $stack );
451 $left = (double)array_pop( $stack );
453 throw new ExprError(
'division_by_zero', $this->names[$op] );
455 $stack[] = fmod( $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;
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 ) < 2 ) {
491 throw new ExprError(
'missing_operand', $this->names[$op] );
493 $right = array_pop( $stack );
494 $left = array_pop( $stack );
495 $stack[] = ( $left == $right ) ? 1 : 0;
498 if ( count( $stack ) < 1 ) {
499 throw new ExprError(
'missing_operand', $this->names[$op] );
501 $arg = array_pop( $stack );
502 $stack[] = ( !$arg ) ? 1 : 0;
505 if ( count( $stack ) < 2 ) {
506 throw new ExprError(
'missing_operand', $this->names[$op] );
508 $digits = (int)array_pop( $stack );
509 $value = array_pop( $stack );
510 $stack[] = round( $value, $digits );
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 != $right ) ? 1 : 0;
553 if ( count( $stack ) < 2 ) {
554 throw new ExprError(
'missing_operand', $this->names[$op] );
556 $right = array_pop( $stack );
557 $left = array_pop( $stack );
558 $stack[] = $left * pow( 10, $right );
561 if ( count( $stack ) < 1 ) {
562 throw new ExprError(
'missing_operand', $this->names[$op] );
564 $arg = array_pop( $stack );
565 $stack[] = sin( $arg );
568 if ( count( $stack ) < 1 ) {
569 throw new ExprError(
'missing_operand', $this->names[$op] );
571 $arg = array_pop( $stack );
572 $stack[] = cos( $arg );
575 if ( count( $stack ) < 1 ) {
576 throw new ExprError(
'missing_operand', $this->names[$op] );
578 $arg = array_pop( $stack );
579 $stack[] = tan( $arg );
582 if ( count( $stack ) < 1 ) {
583 throw new ExprError(
'missing_operand', $this->names[$op] );
585 $arg = array_pop( $stack );
586 if ( $arg < -1 || $arg > 1 ) {
587 throw new ExprError(
'invalid_argument', $this->names[$op] );
589 $stack[] = asin( $arg );
592 if ( count( $stack ) < 1 ) {
593 throw new ExprError(
'missing_operand', $this->names[$op] );
595 $arg = array_pop( $stack );
596 if ( $arg < -1 || $arg > 1 ) {
597 throw new ExprError(
'invalid_argument', $this->names[$op] );
599 $stack[] = acos( $arg );
602 if ( count( $stack ) < 1 ) {
603 throw new ExprError(
'missing_operand', $this->names[$op] );
605 $arg = array_pop( $stack );
606 $stack[] = atan( $arg );
609 if ( count( $stack ) < 1 ) {
610 throw new ExprError(
'missing_operand', $this->names[$op] );
612 $arg = array_pop( $stack );
613 $stack[] = exp( $arg );
616 if ( count( $stack ) < 1 ) {
617 throw new ExprError(
'missing_operand', $this->names[$op] );
619 $arg = array_pop( $stack );
621 throw new ExprError(
'invalid_argument_ln', $this->names[$op] );
623 $stack[] = log( $arg );
626 if ( count( $stack ) < 1 ) {
627 throw new ExprError(
'missing_operand', $this->names[$op] );
629 $arg = array_pop( $stack );
630 $stack[] = abs( $arg );
633 if ( count( $stack ) < 1 ) {
634 throw new ExprError(
'missing_operand', $this->names[$op] );
636 $arg = array_pop( $stack );
637 $stack[] = floor( $arg );
640 if ( count( $stack ) < 1 ) {
641 throw new ExprError(
'missing_operand', $this->names[$op] );
643 $arg = array_pop( $stack );
644 $stack[] = (int)$arg;
647 if ( count( $stack ) < 1 ) {
648 throw new ExprError(
'missing_operand', $this->names[$op] );
650 $arg = array_pop( $stack );
651 $stack[] = ceil( $arg );
654 if ( count( $stack ) < 2 ) {
655 throw new ExprError(
'missing_operand', $this->names[$op] );
657 $right = array_pop( $stack );
658 $left = array_pop( $stack );
659 $result = pow( $left, $right );
660 if ( $result ===
false ) {
661 throw new ExprError(
'division_by_zero', $this->names[$op] );
666 if ( count( $stack ) < 1 ) {
667 throw new ExprError(
'missing_operand', $this->names[$op] );
669 $arg = array_pop( $stack );
670 $result = sqrt( $arg );
671 if ( is_nan( $result ) ) {
672 throw new ExprError(
'not_a_number', $this->names[$op] );