3 use UtfNormal\Validator;
5 if ( !defined(
'MEDIAWIKI' ) ) {
6 die(
'This file is a MediaWiki extension, it is not a valid entry point' );
10 define(
'EXPR_WHITE_CLASS',
" \t\r\n" );
11 define(
'EXPR_NUMBER_CLASS',
'0123456789.' );
14 define(
'EXPR_WHITE', 1 );
15 define(
'EXPR_NUMBER', 2 );
16 define(
'EXPR_NEGATIVE', 3 );
17 define(
'EXPR_POSITIVE', 4 );
18 define(
'EXPR_PLUS', 5 );
19 define(
'EXPR_MINUS', 6 );
20 define(
'EXPR_TIMES', 7 );
21 define(
'EXPR_DIVIDE', 8 );
22 define(
'EXPR_MOD', 9 );
23 define(
'EXPR_OPEN', 10 );
24 define(
'EXPR_CLOSE', 11 );
25 define(
'EXPR_AND', 12 );
26 define(
'EXPR_OR', 13 );
27 define(
'EXPR_NOT', 14 );
28 define(
'EXPR_EQUALITY', 15 );
29 define(
'EXPR_LESS', 16 );
30 define(
'EXPR_GREATER', 17 );
31 define(
'EXPR_LESSEQ', 18 );
32 define(
'EXPR_GREATEREQ', 19 );
33 define(
'EXPR_NOTEQ', 20 );
34 define(
'EXPR_ROUND', 21 );
35 define(
'EXPR_EXPONENT', 22 );
36 define(
'EXPR_SINE', 23 );
37 define(
'EXPR_COSINE', 24 );
38 define(
'EXPR_TANGENS', 25 );
39 define(
'EXPR_ARCSINE', 26 );
40 define(
'EXPR_ARCCOS', 27 );
41 define(
'EXPR_ARCTAN', 28 );
42 define(
'EXPR_EXP', 29 );
43 define(
'EXPR_LN', 30 );
44 define(
'EXPR_ABS', 31 );
45 define(
'EXPR_FLOOR', 32 );
46 define(
'EXPR_TRUNC', 33 );
47 define(
'EXPR_CEIL', 34 );
48 define(
'EXPR_POW', 35 );
49 define(
'EXPR_PI', 36 );
50 define(
'EXPR_FMOD', 37 );
51 define(
'EXPR_SQRT', 38 );
65 $this->
message =
wfMessage(
"pfunc_expr_$msg", $parameter )->inContentLanguage()->text();
187 # Unescape inequality operators
188 $expr = strtr( $expr, [
'<' =>
'<',
'>' =>
'>',
189 '−' =>
'-',
'−' =>
'-' ] );
192 $end = strlen( $expr );
193 $expecting =
'expression';
196 while ( $p < $end ) {
197 if (
count( $operands ) > $this->maxStackSize ||
count( $operators ) > $this->maxStackSize ) {
198 throw new ExprError(
'stack_exhausted' );
201 $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 );
284 elseif ( $char2 ===
'<=' ) {
288 } elseif ( $char2 ===
'>=' ) {
292 } elseif ( $char2 ===
'<>' || $char2 ===
'!=' ) {
299 elseif ( $char ===
'+' ) {
301 if ( $expecting ===
'expression' ) {
309 } elseif ( $char ===
'-' ) {
311 if ( $expecting ===
'expression' ) {
319 } elseif ( $char ===
'*' ) {
323 } elseif ( $char ===
'/' ) {
327 } elseif ( $char ===
'^' ) {
331 } elseif ( $char ===
'(' ) {
332 if ( $expecting ===
'operator' ) {
333 throw new ExprError(
'unexpected_operator',
'(' );
338 } elseif ( $char ===
')' ) {
339 $lastOp = end( $operators );
340 while ( $lastOp && $lastOp !=
EXPR_OPEN ) {
342 array_pop( $operators );
343 $lastOp = end( $operators );
346 array_pop( $operators );
348 throw new ExprError(
'unexpected_closing_bracket' );
350 $expecting =
'operator';
353 } elseif ( $char ===
'=' ) {
357 } elseif ( $char ===
'<' ) {
361 } elseif ( $char ===
'>' ) {
366 throw new ExprError(
'unrecognised_punctuation', Validator::cleanUp( $char ) );
370 if ( $expecting ===
'expression' ) {
375 $lastOp = end( $operators );
376 while ( $lastOp && $this->precedence[$op] <= $this->precedence[$lastOp] ) {
378 array_pop( $operators );
379 $lastOp = end( $operators );
382 $expecting =
'expression';
387 while ( $op = array_pop( $operators ) ) {
390 throw new ExprError(
'unclosed_bracket' );
395 return implode(
"<br />\n", $operands );
406 if (
count( $stack ) < 1 ) {
409 $arg = array_pop( $stack );
413 if (
count( $stack ) < 1 ) {
418 if (
count( $stack ) < 2 ) {
421 $right = array_pop( $stack );
422 $left = array_pop( $stack );
423 $stack[] = $left * $right;
426 if (
count( $stack ) < 2 ) {
429 $right = array_pop( $stack );
430 $left = array_pop( $stack );
434 $stack[] = $left / $right;
437 if (
count( $stack ) < 2 ) {
440 $right = (int)array_pop( $stack );
441 $left = (int)array_pop( $stack );
445 $stack[] = $left % $right;
448 if (
count( $stack ) < 2 ) {
451 $right = (double)array_pop( $stack );
452 $left = (double)array_pop( $stack );
456 $stack[] = fmod( $left, $right );
459 if (
count( $stack ) < 2 ) {
462 $right = array_pop( $stack );
463 $left = array_pop( $stack );
464 $stack[] = $left + $right;
467 if (
count( $stack ) < 2 ) {
470 $right = array_pop( $stack );
471 $left = array_pop( $stack );
472 $stack[] = $left - $right;
475 if (
count( $stack ) < 2 ) {
478 $right = array_pop( $stack );
479 $left = array_pop( $stack );
480 $stack[] = ( $left && $right ) ? 1 : 0;
483 if (
count( $stack ) < 2 ) {
486 $right = array_pop( $stack );
487 $left = array_pop( $stack );
488 $stack[] = ( $left || $right ) ? 1 : 0;
491 if (
count( $stack ) < 2 ) {
494 $right = array_pop( $stack );
495 $left = array_pop( $stack );
496 $stack[] = ( $left == $right ) ? 1 : 0;
499 if (
count( $stack ) < 1 ) {
502 $arg = array_pop( $stack );
503 $stack[] = ( !$arg ) ? 1 : 0;
506 if (
count( $stack ) < 2 ) {
509 $digits = (int)array_pop( $stack );
510 $value = array_pop( $stack );
511 $stack[] = round(
$value, $digits );
514 if (
count( $stack ) < 2 ) {
517 $right = array_pop( $stack );
518 $left = array_pop( $stack );
519 $stack[] = ( $left < $right ) ? 1 : 0;
522 if (
count( $stack ) < 2 ) {
525 $right = array_pop( $stack );
526 $left = array_pop( $stack );
527 $stack[] = ( $left > $right ) ? 1 : 0;
530 if (
count( $stack ) < 2 ) {
533 $right = array_pop( $stack );
534 $left = array_pop( $stack );
535 $stack[] = ( $left <= $right ) ? 1 : 0;
538 if (
count( $stack ) < 2 ) {
541 $right = array_pop( $stack );
542 $left = array_pop( $stack );
543 $stack[] = ( $left >= $right ) ? 1 : 0;
546 if (
count( $stack ) < 2 ) {
549 $right = array_pop( $stack );
550 $left = array_pop( $stack );
551 $stack[] = ( $left != $right ) ? 1 : 0;
554 if (
count( $stack ) < 2 ) {
557 $right = array_pop( $stack );
558 $left = array_pop( $stack );
559 $stack[] = $left * pow( 10, $right );
562 if (
count( $stack ) < 1 ) {
565 $arg = array_pop( $stack );
566 $stack[] = sin( $arg );
569 if (
count( $stack ) < 1 ) {
572 $arg = array_pop( $stack );
573 $stack[] = cos( $arg );
576 if (
count( $stack ) < 1 ) {
579 $arg = array_pop( $stack );
580 $stack[] = tan( $arg );
583 if (
count( $stack ) < 1 ) {
586 $arg = array_pop( $stack );
587 if ( $arg < -1 || $arg > 1 ) {
590 $stack[] = asin( $arg );
593 if (
count( $stack ) < 1 ) {
596 $arg = array_pop( $stack );
597 if ( $arg < -1 || $arg > 1 ) {
600 $stack[] = acos( $arg );
603 if (
count( $stack ) < 1 ) {
606 $arg = array_pop( $stack );
607 $stack[] = atan( $arg );
610 if (
count( $stack ) < 1 ) {
613 $arg = array_pop( $stack );
614 $stack[] = exp( $arg );
617 if (
count( $stack ) < 1 ) {
620 $arg = array_pop( $stack );
624 $stack[] = log( $arg );
627 if (
count( $stack ) < 1 ) {
630 $arg = array_pop( $stack );
631 $stack[] = abs( $arg );
634 if (
count( $stack ) < 1 ) {
637 $arg = array_pop( $stack );
638 $stack[] = floor( $arg );
641 if (
count( $stack ) < 1 ) {
644 $arg = array_pop( $stack );
645 $stack[] = (int)$arg;
648 if (
count( $stack ) < 1 ) {
651 $arg = array_pop( $stack );
652 $stack[] = ceil( $arg );
655 if (
count( $stack ) < 2 ) {
658 $right = array_pop( $stack );
659 $left = array_pop( $stack );
660 $result = pow( $left, $right );
667 if (
count( $stack ) < 1 ) {
670 $arg = array_pop( $stack );