19 use UtfNormal\Validator;
22 define(
'EXPR_WHITE_CLASS',
" \t\r\n" );
23 define(
'EXPR_NUMBER_CLASS',
'0123456789.' );
26 define(
'EXPR_WHITE', 1 );
27 define(
'EXPR_NUMBER', 2 );
28 define(
'EXPR_NEGATIVE', 3 );
29 define(
'EXPR_POSITIVE', 4 );
30 define(
'EXPR_PLUS', 5 );
31 define(
'EXPR_MINUS', 6 );
32 define(
'EXPR_TIMES', 7 );
33 define(
'EXPR_DIVIDE', 8 );
34 define(
'EXPR_MOD', 9 );
35 define(
'EXPR_OPEN', 10 );
36 define(
'EXPR_CLOSE', 11 );
37 define(
'EXPR_AND', 12 );
38 define(
'EXPR_OR', 13 );
39 define(
'EXPR_NOT', 14 );
40 define(
'EXPR_EQUALITY', 15 );
41 define(
'EXPR_LESS', 16 );
42 define(
'EXPR_GREATER', 17 );
43 define(
'EXPR_LESSEQ', 18 );
44 define(
'EXPR_GREATEREQ', 19 );
45 define(
'EXPR_NOTEQ', 20 );
46 define(
'EXPR_ROUND', 21 );
47 define(
'EXPR_EXPONENT', 22 );
48 define(
'EXPR_SINE', 23 );
49 define(
'EXPR_COSINE', 24 );
50 define(
'EXPR_TANGENS', 25 );
51 define(
'EXPR_ARCSINE', 26 );
52 define(
'EXPR_ARCCOS', 27 );
53 define(
'EXPR_ARCTAN', 28 );
54 define(
'EXPR_EXP', 29 );
55 define(
'EXPR_LN', 30 );
56 define(
'EXPR_ABS', 31 );
57 define(
'EXPR_FLOOR', 32 );
58 define(
'EXPR_TRUNC', 33 );
59 define(
'EXPR_CEIL', 34 );
60 define(
'EXPR_POW', 35 );
61 define(
'EXPR_PI', 36 );
62 define(
'EXPR_FMOD', 37 );
63 define(
'EXPR_SQRT', 38 );
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' ) {
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 ) {
400 $arg = array_pop( $stack );
404 if (
count( $stack ) < 1 ) {
409 if (
count( $stack ) < 2 ) {
412 $right = array_pop( $stack );
413 $left = array_pop( $stack );
414 $stack[] = $left * $right;
417 if (
count( $stack ) < 2 ) {
420 $right = array_pop( $stack );
421 $left = array_pop( $stack );
425 $stack[] = $left / $right;
428 if (
count( $stack ) < 2 ) {
431 $right = (int)array_pop( $stack );
432 $left = (int)array_pop( $stack );
436 $stack[] = $left % $right;
439 if (
count( $stack ) < 2 ) {
442 $right = (double)array_pop( $stack );
443 $left = (double)array_pop( $stack );
447 $stack[] = fmod( $left, $right );
450 if (
count( $stack ) < 2 ) {
453 $right = array_pop( $stack );
454 $left = array_pop( $stack );
455 $stack[] = $left + $right;
458 if (
count( $stack ) < 2 ) {
461 $right = array_pop( $stack );
462 $left = array_pop( $stack );
463 $stack[] = $left - $right;
466 if (
count( $stack ) < 2 ) {
469 $right = array_pop( $stack );
470 $left = array_pop( $stack );
471 $stack[] = ( $left && $right ) ? 1 : 0;
474 if (
count( $stack ) < 2 ) {
477 $right = array_pop( $stack );
478 $left = array_pop( $stack );
479 $stack[] = ( $left || $right ) ? 1 : 0;
482 if (
count( $stack ) < 2 ) {
485 $right = array_pop( $stack );
486 $left = array_pop( $stack );
487 $stack[] = ( $left == $right ) ? 1 : 0;
490 if (
count( $stack ) < 1 ) {
493 $arg = array_pop( $stack );
494 $stack[] = ( !$arg ) ? 1 : 0;
497 if (
count( $stack ) < 2 ) {
500 $digits = (int)array_pop( $stack );
501 $value = array_pop( $stack );
502 $stack[] = round(
$value, $digits );
505 if (
count( $stack ) < 2 ) {
508 $right = array_pop( $stack );
509 $left = array_pop( $stack );
510 $stack[] = ( $left < $right ) ? 1 : 0;
513 if (
count( $stack ) < 2 ) {
516 $right = array_pop( $stack );
517 $left = array_pop( $stack );
518 $stack[] = ( $left > $right ) ? 1 : 0;
521 if (
count( $stack ) < 2 ) {
524 $right = array_pop( $stack );
525 $left = array_pop( $stack );
526 $stack[] = ( $left <= $right ) ? 1 : 0;
529 if (
count( $stack ) < 2 ) {
532 $right = array_pop( $stack );
533 $left = array_pop( $stack );
534 $stack[] = ( $left >= $right ) ? 1 : 0;
537 if (
count( $stack ) < 2 ) {
540 $right = array_pop( $stack );
541 $left = array_pop( $stack );
542 $stack[] = ( $left != $right ) ? 1 : 0;
545 if (
count( $stack ) < 2 ) {
548 $right = array_pop( $stack );
549 $left = array_pop( $stack );
550 $stack[] = $left * pow( 10, $right );
553 if (
count( $stack ) < 1 ) {
556 $arg = array_pop( $stack );
557 $stack[] = sin( $arg );
560 if (
count( $stack ) < 1 ) {
563 $arg = array_pop( $stack );
564 $stack[] = cos( $arg );
567 if (
count( $stack ) < 1 ) {
570 $arg = array_pop( $stack );
571 $stack[] = tan( $arg );
574 if (
count( $stack ) < 1 ) {
577 $arg = array_pop( $stack );
578 if ( $arg < -1 || $arg > 1 ) {
581 $stack[] = asin( $arg );
584 if (
count( $stack ) < 1 ) {
587 $arg = array_pop( $stack );
588 if ( $arg < -1 || $arg > 1 ) {
591 $stack[] = acos( $arg );
594 if (
count( $stack ) < 1 ) {
597 $arg = array_pop( $stack );
598 $stack[] = atan( $arg );
601 if (
count( $stack ) < 1 ) {
604 $arg = array_pop( $stack );
605 $stack[] = exp( $arg );
608 if (
count( $stack ) < 1 ) {
611 $arg = array_pop( $stack );
615 $stack[] = log( $arg );
618 if (
count( $stack ) < 1 ) {
621 $arg = array_pop( $stack );
622 $stack[] = abs( $arg );
625 if (
count( $stack ) < 1 ) {
628 $arg = array_pop( $stack );
629 $stack[] = floor( $arg );
632 if (
count( $stack ) < 1 ) {
635 $arg = array_pop( $stack );
636 $stack[] = (int)$arg;
639 if (
count( $stack ) < 1 ) {
642 $arg = array_pop( $stack );
643 $stack[] = ceil( $arg );
646 if (
count( $stack ) < 2 ) {
649 $right = array_pop( $stack );
650 $left = array_pop( $stack );
651 $result = pow( $left, $right );
658 if (
count( $stack ) < 1 ) {
661 $arg = array_pop( $stack );