21 use UtfNormal\Validator;
24 define(
'EXPR_WHITE_CLASS',
" \t\r\n" );
25 define(
'EXPR_NUMBER_CLASS',
'0123456789.' );
28 define(
'EXPR_WHITE', 1 );
29 define(
'EXPR_NUMBER', 2 );
30 define(
'EXPR_NEGATIVE', 3 );
31 define(
'EXPR_POSITIVE', 4 );
32 define(
'EXPR_PLUS', 5 );
33 define(
'EXPR_MINUS', 6 );
34 define(
'EXPR_TIMES', 7 );
35 define(
'EXPR_DIVIDE', 8 );
36 define(
'EXPR_MOD', 9 );
37 define(
'EXPR_OPEN', 10 );
38 define(
'EXPR_CLOSE', 11 );
39 define(
'EXPR_AND', 12 );
40 define(
'EXPR_OR', 13 );
41 define(
'EXPR_NOT', 14 );
42 define(
'EXPR_EQUALITY', 15 );
43 define(
'EXPR_LESS', 16 );
44 define(
'EXPR_GREATER', 17 );
45 define(
'EXPR_LESSEQ', 18 );
46 define(
'EXPR_GREATEREQ', 19 );
47 define(
'EXPR_NOTEQ', 20 );
48 define(
'EXPR_ROUND', 21 );
49 define(
'EXPR_EXPONENT', 22 );
50 define(
'EXPR_SINE', 23 );
51 define(
'EXPR_COSINE', 24 );
52 define(
'EXPR_TANGENS', 25 );
53 define(
'EXPR_ARCSINE', 26 );
54 define(
'EXPR_ARCCOS', 27 );
55 define(
'EXPR_ARCTAN', 28 );
56 define(
'EXPR_EXP', 29 );
57 define(
'EXPR_LN', 30 );
58 define(
'EXPR_ABS', 31 );
59 define(
'EXPR_FLOOR', 32 );
60 define(
'EXPR_TRUNC', 33 );
61 define(
'EXPR_CEIL', 34 );
62 define(
'EXPR_POW', 35 );
63 define(
'EXPR_PI', 36 );
64 define(
'EXPR_FMOD', 37 );
65 define(
'EXPR_SQRT', 38 );
185 # Unescape inequality operators
186 $expr = strtr( $expr, [
'<' =>
'<',
'>' =>
'>',
187 '−' =>
'-',
'−' =>
'-' ] );
190 $end = strlen( $expr );
191 $expecting =
'expression';
194 while ( $p < $end ) {
195 if (
count( $operands ) > $this->maxStackSize ||
count( $operators ) > $this->maxStackSize ) {
196 throw new ExprError(
'stack_exhausted' );
199 $char2 = substr( $expr, $p, 2 );
215 if ( $expecting !==
'expression' ) {
216 throw new ExprError(
'unexpected_number' );
222 $operands[] = (float)substr( $expr, $p, $length );
224 $expecting =
'operator';
226 } elseif ( ctype_alpha( $char ) ) {
229 $remaining = substr( $expr, $p );
230 if ( !preg_match(
'/^[A-Za-z]*/', $remaining,
$matches ) ) {
232 throw new ExprError(
'preg_match_failure' );
235 $p += strlen( $word );
238 if ( !isset( $this->
words[$word] ) ) {
239 throw new ExprError(
'unrecognised_word', $word );
241 $op = $this->
words[$word];
245 if ( $expecting !==
'expression' ) {
248 $operands[] = exp( 1 );
249 $expecting =
'operator';
252 if ( $expecting !==
'expression' ) {
253 throw new ExprError(
'unexpected_number' );
256 $expecting =
'operator';
273 if ( $expecting !==
'expression' ) {
274 throw new ExprError(
'unexpected_operator', $word );
281 } elseif ( $char2 ===
'<=' ) {
285 } elseif ( $char2 ===
'>=' ) {
289 } elseif ( $char2 ===
'<>' || $char2 ===
'!=' ) {
293 } elseif ( $char ===
'+' ) {
295 if ( $expecting ===
'expression' ) {
303 } elseif ( $char ===
'-' ) {
305 if ( $expecting ===
'expression' ) {
313 } elseif ( $char ===
'*' ) {
317 } elseif ( $char ===
'/' ) {
321 } elseif ( $char ===
'^' ) {
325 } elseif ( $char ===
'(' ) {
326 if ( $expecting ===
'operator' ) {
327 throw new ExprError(
'unexpected_operator',
'(' );
332 } elseif ( $char ===
')' ) {
333 $lastOp = end( $operators );
334 while ( $lastOp && $lastOp !=
EXPR_OPEN ) {
336 array_pop( $operators );
337 $lastOp = end( $operators );
340 array_pop( $operators );
342 throw new ExprError(
'unexpected_closing_bracket' );
344 $expecting =
'operator';
347 } elseif ( $char ===
'=' ) {
351 } elseif ( $char ===
'<' ) {
355 } elseif ( $char ===
'>' ) {
360 $utfExpr = Validator::cleanUp( substr( $expr, $p ) );
361 throw new ExprError(
'unrecognised_punctuation', mb_substr( $utfExpr, 0, 1 ) );
365 if ( $expecting ===
'expression' ) {
370 $lastOp = end( $operators );
371 while ( $lastOp && $this->precedence[$op] <= $this->precedence[$lastOp] ) {
373 array_pop( $operators );
374 $lastOp = end( $operators );
377 $expecting =
'expression';
382 while ( $op = array_pop( $operators ) ) {
385 throw new ExprError(
'unclosed_bracket' );
390 return implode(
"<br />\n", $operands );
401 if (
count( $stack ) < 1 ) {
404 $arg = array_pop( $stack );
408 if (
count( $stack ) < 1 ) {
413 if (
count( $stack ) < 2 ) {
416 $right = array_pop( $stack );
417 $left = array_pop( $stack );
418 $stack[] = $left * $right;
421 if (
count( $stack ) < 2 ) {
424 $right = array_pop( $stack );
425 $left = array_pop( $stack );
429 $stack[] = $left / $right;
432 if (
count( $stack ) < 2 ) {
435 $right = (int)array_pop( $stack );
436 $left = (int)array_pop( $stack );
440 $stack[] = $left % $right;
443 if (
count( $stack ) < 2 ) {
446 $right = (double)array_pop( $stack );
447 $left = (double)array_pop( $stack );
451 $stack[] = fmod( $left, $right );
454 if (
count( $stack ) < 2 ) {
457 $right = array_pop( $stack );
458 $left = array_pop( $stack );
459 $stack[] = $left + $right;
462 if (
count( $stack ) < 2 ) {
465 $right = array_pop( $stack );
466 $left = array_pop( $stack );
467 $stack[] = $left - $right;
470 if (
count( $stack ) < 2 ) {
473 $right = array_pop( $stack );
474 $left = array_pop( $stack );
475 $stack[] = ( $left && $right ) ? 1 : 0;
478 if (
count( $stack ) < 2 ) {
481 $right = array_pop( $stack );
482 $left = array_pop( $stack );
483 $stack[] = ( $left || $right ) ? 1 : 0;
486 if (
count( $stack ) < 2 ) {
489 $right = array_pop( $stack );
490 $left = array_pop( $stack );
491 $stack[] = ( $left == $right ) ? 1 : 0;
494 if (
count( $stack ) < 1 ) {
497 $arg = array_pop( $stack );
498 $stack[] = ( !$arg ) ? 1 : 0;
501 if (
count( $stack ) < 2 ) {
504 $digits = (int)array_pop( $stack );
505 $value = array_pop( $stack );
506 $stack[] = round(
$value, $digits );
509 if (
count( $stack ) < 2 ) {
512 $right = array_pop( $stack );
513 $left = array_pop( $stack );
514 $stack[] = ( $left < $right ) ? 1 : 0;
517 if (
count( $stack ) < 2 ) {
520 $right = array_pop( $stack );
521 $left = array_pop( $stack );
522 $stack[] = ( $left > $right ) ? 1 : 0;
525 if (
count( $stack ) < 2 ) {
528 $right = array_pop( $stack );
529 $left = array_pop( $stack );
530 $stack[] = ( $left <= $right ) ? 1 : 0;
533 if (
count( $stack ) < 2 ) {
536 $right = array_pop( $stack );
537 $left = array_pop( $stack );
538 $stack[] = ( $left >= $right ) ? 1 : 0;
541 if (
count( $stack ) < 2 ) {
544 $right = array_pop( $stack );
545 $left = array_pop( $stack );
546 $stack[] = ( $left != $right ) ? 1 : 0;
549 if (
count( $stack ) < 2 ) {
552 $right = array_pop( $stack );
553 $left = array_pop( $stack );
554 $stack[] = $left * pow( 10, $right );
557 if (
count( $stack ) < 1 ) {
560 $arg = array_pop( $stack );
561 $stack[] = sin( $arg );
564 if (
count( $stack ) < 1 ) {
567 $arg = array_pop( $stack );
568 $stack[] = cos( $arg );
571 if (
count( $stack ) < 1 ) {
574 $arg = array_pop( $stack );
575 $stack[] = tan( $arg );
578 if (
count( $stack ) < 1 ) {
581 $arg = array_pop( $stack );
582 if ( $arg < -1 || $arg > 1 ) {
585 $stack[] = asin( $arg );
588 if (
count( $stack ) < 1 ) {
591 $arg = array_pop( $stack );
592 if ( $arg < -1 || $arg > 1 ) {
595 $stack[] = acos( $arg );
598 if (
count( $stack ) < 1 ) {
601 $arg = array_pop( $stack );
602 $stack[] = atan( $arg );
605 if (
count( $stack ) < 1 ) {
608 $arg = array_pop( $stack );
609 $stack[] = exp( $arg );
612 if (
count( $stack ) < 1 ) {
615 $arg = array_pop( $stack );
619 $stack[] = log( $arg );
622 if (
count( $stack ) < 1 ) {
625 $arg = array_pop( $stack );
626 $stack[] = abs( $arg );
629 if (
count( $stack ) < 1 ) {
632 $arg = array_pop( $stack );
633 $stack[] = floor( $arg );
636 if (
count( $stack ) < 1 ) {
639 $arg = array_pop( $stack );
640 $stack[] = (int)$arg;
643 if (
count( $stack ) < 1 ) {
646 $arg = array_pop( $stack );
647 $stack[] = ceil( $arg );
650 if (
count( $stack ) < 2 ) {
653 $right = array_pop( $stack );
654 $left = array_pop( $stack );
655 $result = pow( $left, $right );
662 if (
count( $stack ) < 1 ) {
665 $arg = array_pop( $stack );