From 4a3fd2e42a1d38a60a8ebb99369c0f2fd5761036 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Niklas=20Laxstr=C3=B6m?= Date: Thu, 24 Sep 2015 09:15:49 +0200 Subject: [PATCH] Use wikimedia/cldr-plural-rule-parser Replaces the parser included in MediaWiki with same code in a library. Change-Id: I1d2675466a543269e17faf213aa68d2b7afaf78e --- RELEASE-NOTES-1.26 | 2 + autoload.php | 7 - composer.json | 1 + includes/cache/LocalisationCache.php | 3 +- languages/Language.php | 4 +- languages/utils/CLDRPluralRuleConverter.php | 322 ------------------ .../CLDRPluralRuleConverterExpression.php | 41 --- .../utils/CLDRPluralRuleConverterFragment.php | 34 -- .../utils/CLDRPluralRuleConverterOperator.php | 114 ------- languages/utils/CLDRPluralRuleError.php | 20 -- languages/utils/CLDRPluralRuleEvaluator.php | 187 ---------- .../utils/CLDRPluralRuleEvaluatorRange.php | 110 ------ .../utils/CLDRPluralRuleEvaluatorTest.php | 151 -------- 13 files changed, 8 insertions(+), 988 deletions(-) delete mode 100644 languages/utils/CLDRPluralRuleConverter.php delete mode 100644 languages/utils/CLDRPluralRuleConverterExpression.php delete mode 100644 languages/utils/CLDRPluralRuleConverterFragment.php delete mode 100644 languages/utils/CLDRPluralRuleConverterOperator.php delete mode 100644 languages/utils/CLDRPluralRuleError.php delete mode 100644 languages/utils/CLDRPluralRuleEvaluator.php delete mode 100644 languages/utils/CLDRPluralRuleEvaluatorRange.php delete mode 100644 tests/phpunit/languages/utils/CLDRPluralRuleEvaluatorTest.php diff --git a/RELEASE-NOTES-1.26 b/RELEASE-NOTES-1.26 index 99fc2eb70d..1df0d03c4c 100644 --- a/RELEASE-NOTES-1.26 +++ b/RELEASE-NOTES-1.26 @@ -44,6 +44,7 @@ production. * $wgBlockAllowsUTEdit is now set to true by default. This allows blocked users to edit their talk pages unless explicitly disabled when they are being blocked. +* CLDRPluralRule* classes have been replaced with wikimedia/cldr-plural-rule-parser. === New features in 1.26 === * (T51506) Now action=info gives estimates of actual watchers for a page. @@ -98,6 +99,7 @@ production. * Upgrade jQuery Client from v1.0.0 to v2.0.0. * Added mediawiki/at-ease 1.0.0. * Update QUnit from v1.17.1 to v1.18.0. +* Added wikimedia/cldr-plural-rule-parser 1.0.0 === Bug fixes in 1.26 === * (T53283) load.php sometimes sends 304 response without full headers diff --git a/autoload.php b/autoload.php index e5e7cda5bd..1c25a81c8c 100644 --- a/autoload.php +++ b/autoload.php @@ -184,13 +184,6 @@ $wgAutoloadLocalClasses = array( 'BmpHandler' => __DIR__ . '/includes/media/BMP.php', 'BrokenRedirectsPage' => __DIR__ . '/includes/specials/SpecialBrokenRedirects.php', 'BufferingStatsdDataFactory' => __DIR__ . '/includes/libs/BufferingStatsdDataFactory.php', - 'CLDRPluralRuleConverter' => __DIR__ . '/languages/utils/CLDRPluralRuleConverter.php', - 'CLDRPluralRuleConverterExpression' => __DIR__ . '/languages/utils/CLDRPluralRuleConverterExpression.php', - 'CLDRPluralRuleConverterFragment' => __DIR__ . '/languages/utils/CLDRPluralRuleConverterFragment.php', - 'CLDRPluralRuleConverterOperator' => __DIR__ . '/languages/utils/CLDRPluralRuleConverterOperator.php', - 'CLDRPluralRuleError' => __DIR__ . '/languages/utils/CLDRPluralRuleError.php', - 'CLDRPluralRuleEvaluator' => __DIR__ . '/languages/utils/CLDRPluralRuleEvaluator.php', - 'CLDRPluralRuleEvaluatorRange' => __DIR__ . '/languages/utils/CLDRPluralRuleEvaluatorRange.php', 'CLIParser' => __DIR__ . '/maintenance/parse.php', 'CSSMin' => __DIR__ . '/includes/libs/CSSMin.php', 'CacheDependency' => __DIR__ . '/includes/cache/CacheDependency.php', diff --git a/composer.json b/composer.json index e41b6c7feb..ba9a8928c7 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,7 @@ "psr/log": "1.0.0", "wikimedia/assert": "0.2.2", "wikimedia/cdb": "1.3.0", + "wikimedia/cldr-plural-rule-parser": "1.0.0", "wikimedia/composer-merge-plugin": "1.2.1", "wikimedia/ip-set": "1.0.1", "wikimedia/utfnormal": "1.0.3", diff --git a/includes/cache/LocalisationCache.php b/includes/cache/LocalisationCache.php index 276e84aaa9..f5b235055e 100644 --- a/includes/cache/LocalisationCache.php +++ b/includes/cache/LocalisationCache.php @@ -23,6 +23,7 @@ use Cdb\Exception as CdbException; use Cdb\Reader as CdbReader; use Cdb\Writer as CdbWriter; +use CLDRPluralRuleParser\Evaluator; /** * Class for caching the contents of localisation files, Messages*.php @@ -576,7 +577,7 @@ class LocalisationCache { return null; } try { - $compiledRules = CLDRPluralRuleEvaluator::compile( $rules ); + $compiledRules = Evaluator::compile( $rules ); } catch ( CLDRPluralRuleError $e ) { wfDebugLog( 'l10n', $e->getMessage() ); diff --git a/languages/Language.php b/languages/Language.php index 1613536129..e6baac0155 100644 --- a/languages/Language.php +++ b/languages/Language.php @@ -34,6 +34,8 @@ if ( function_exists( 'mb_strtoupper' ) ) { mb_internal_encoding( 'UTF-8' ); } +use CLDRPluralRuleParser\Evaluator; + /** * Internationalisation code * @ingroup Language @@ -4968,7 +4970,7 @@ class Language { */ public function getPluralRuleIndexNumber( $number ) { $pluralRules = $this->getCompiledPluralRules(); - $form = CLDRPluralRuleEvaluator::evaluateCompiled( $number, $pluralRules ); + $form = Evaluator::evaluateCompiled( $number, $pluralRules ); return $form; } diff --git a/languages/utils/CLDRPluralRuleConverter.php b/languages/utils/CLDRPluralRuleConverter.php deleted file mode 100644 index 2eabcab14b..0000000000 --- a/languages/utils/CLDRPluralRuleConverter.php +++ /dev/null @@ -1,322 +0,0 @@ - 2, - 'and' => 3, - 'is' => 4, - 'is-not' => 4, - 'in' => 4, - 'not-in' => 4, - 'within' => 4, - 'not-within' => 4, - 'mod' => 5, - ',' => 6, - '..' => 7, - ); - - /** - * A character list defining whitespace, for use in strspn() etc. - */ - const WHITESPACE_CLASS = " \t\r\n"; - - /** - * Same for digits. Note that the grammar given in UTS #35 doesn't allow - * negative numbers or decimal separators. - */ - const NUMBER_CLASS = '0123456789'; - - /** - * A character list of symbolic operands. - */ - const OPERAND_SYMBOLS = 'nivwft'; - - /** - * An anchored regular expression which matches a word at the current offset. - */ - const WORD_REGEX = '/[a-zA-Z@]+/A'; - - /** - * Convert a rule to RPN. This is the only public entry point. - * - * @param string $rule The rule to convert - * @return string The RPN representation of the rule - */ - public static function convert( $rule ) { - $parser = new self( $rule ); - - return $parser->doConvert(); - } - - /** - * Private constructor. - * @param string $rule - */ - protected function __construct( $rule ) { - $this->rule = $rule; - $this->pos = 0; - $this->end = strlen( $rule ); - } - - /** - * Do the operation. - * - * @return string The RPN representation of the rule (e.g. "5 3 mod n is") - */ - protected function doConvert() { - $expectOperator = true; - - // Iterate through all tokens, saving the operators and operands to a - // stack per Dijkstra's shunting yard algorithm. - /** @var CLDRPluralRuleConverterOperator $token */ - while ( false !== ( $token = $this->nextToken() ) ) { - // In this grammar, there are only binary operators, so every valid - // rule string will alternate between operator and operand tokens. - $expectOperator = !$expectOperator; - - if ( $token instanceof CLDRPluralRuleConverterExpression ) { - // Operand - if ( $expectOperator ) { - $token->error( 'unexpected operand' ); - } - $this->operands[] = $token; - continue; - } else { - // Operator - if ( !$expectOperator ) { - $token->error( 'unexpected operator' ); - } - // Resolve higher precedence levels - $lastOp = end( $this->operators ); - while ( $lastOp && self::$precedence[$token->name] <= self::$precedence[$lastOp->name] ) { - $this->doOperation( $lastOp, $this->operands ); - array_pop( $this->operators ); - $lastOp = end( $this->operators ); - } - $this->operators[] = $token; - } - } - - // Finish off the stack - while ( $op = array_pop( $this->operators ) ) { - $this->doOperation( $op, $this->operands ); - } - - // Make sure the result is sane. The first case is possible for an empty - // string input, the second should be unreachable. - if ( !count( $this->operands ) ) { - $this->error( 'condition expected' ); - } elseif ( count( $this->operands ) > 1 ) { - $this->error( 'missing operator or too many operands' ); - } - - $value = $this->operands[0]; - if ( $value->type !== 'boolean' ) { - $this->error( 'the result must have a boolean type' ); - } - - return $this->operands[0]->rpn; - } - - /** - * Fetch the next token from the input string. - * - * @return CLDRPluralRuleConverterFragment The next token - */ - protected function nextToken() { - if ( $this->pos >= $this->end ) { - return false; - } - - // Whitespace - $length = strspn( $this->rule, self::WHITESPACE_CLASS, $this->pos ); - $this->pos += $length; - - if ( $this->pos >= $this->end ) { - return false; - } - - // Number - $length = strspn( $this->rule, self::NUMBER_CLASS, $this->pos ); - if ( $length !== 0 ) { - $token = $this->newNumber( substr( $this->rule, $this->pos, $length ), $this->pos ); - $this->pos += $length; - - return $token; - } - - // Two-character operators - $op2 = substr( $this->rule, $this->pos, 2 ); - if ( $op2 === '..' || $op2 === '!=' ) { - $token = $this->newOperator( $op2, $this->pos, 2 ); - $this->pos += 2; - - return $token; - } - - // Single-character operators - $op1 = $this->rule[$this->pos]; - if ( $op1 === ',' || $op1 === '=' || $op1 === '%' ) { - $token = $this->newOperator( $op1, $this->pos, 1 ); - $this->pos++; - - return $token; - } - - // Word - if ( !preg_match( self::WORD_REGEX, $this->rule, $m, 0, $this->pos ) ) { - $this->error( 'unexpected character "' . $this->rule[$this->pos] . '"' ); - } - $word1 = strtolower( $m[0] ); - $word2 = ''; - $nextTokenPos = $this->pos + strlen( $word1 ); - if ( $word1 === 'not' || $word1 === 'is' ) { - // Look ahead one word - $nextTokenPos += strspn( $this->rule, self::WHITESPACE_CLASS, $nextTokenPos ); - if ( $nextTokenPos < $this->end - && preg_match( self::WORD_REGEX, $this->rule, $m, 0, $nextTokenPos ) - ) { - $word2 = strtolower( $m[0] ); - $nextTokenPos += strlen( $word2 ); - } - } - - // Two-word operators like "is not" take precedence over single-word operators like "is" - if ( $word2 !== '' ) { - $bothWords = "{$word1}-{$word2}"; - if ( isset( self::$precedence[$bothWords] ) ) { - $token = $this->newOperator( $bothWords, $this->pos, $nextTokenPos - $this->pos ); - $this->pos = $nextTokenPos; - - return $token; - } - } - - // Single-word operators - if ( isset( self::$precedence[$word1] ) ) { - $token = $this->newOperator( $word1, $this->pos, strlen( $word1 ) ); - $this->pos += strlen( $word1 ); - - return $token; - } - - // The single-character operand symbols - if ( strpos( self::OPERAND_SYMBOLS, $word1 ) !== false ) { - $token = $this->newNumber( $word1, $this->pos ); - $this->pos++; - - return $token; - } - - // Samples - if ( $word1 === '@integer' || $word1 === '@decimal' ) { - // Samples are like comments, they have no effect on rule evaluation. - // They run from the first sample indicator to the end of the string. - $this->pos = $this->end; - - return false; - } - - $this->error( 'unrecognised word' ); - } - - /** - * For the binary operator $op, pop its operands off the stack and push - * a fragment with rpn and type members describing the result of that - * operation. - * - * @param CLDRPluralRuleConverterOperator $op - */ - protected function doOperation( $op ) { - if ( count( $this->operands ) < 2 ) { - $op->error( 'missing operand' ); - } - $right = array_pop( $this->operands ); - $left = array_pop( $this->operands ); - $result = $op->operate( $left, $right ); - $this->operands[] = $result; - } - - /** - * Create a numerical expression object - * - * @param string $text - * @param int $pos - * @return CLDRPluralRuleConverterExpression The numerical expression - */ - protected function newNumber( $text, $pos ) { - return new CLDRPluralRuleConverterExpression( $this, 'number', $text, $pos, strlen( $text ) ); - } - - /** - * Create a binary operator - * - * @param string $type - * @param int $pos - * @param int $length - * @return CLDRPluralRuleConverterOperator The operator - */ - protected function newOperator( $type, $pos, $length ) { - return new CLDRPluralRuleConverterOperator( $this, $type, $pos, $length ); - } - - /** - * Throw an error - * @param string $message - */ - protected function error( $message ) { - throw new CLDRPluralRuleError( $message ); - } -} diff --git a/languages/utils/CLDRPluralRuleConverterExpression.php b/languages/utils/CLDRPluralRuleConverterExpression.php deleted file mode 100644 index 1ee6b4c54e..0000000000 --- a/languages/utils/CLDRPluralRuleConverterExpression.php +++ /dev/null @@ -1,41 +0,0 @@ -type = $type; - $this->rpn = $rpn; - } - - public function isType( $type ) { - if ( $type === 'range' && ( $this->type === 'range' || $this->type === 'number' ) ) { - return true; - } - if ( $type === $this->type ) { - return true; - } - - return false; - } -} diff --git a/languages/utils/CLDRPluralRuleConverterFragment.php b/languages/utils/CLDRPluralRuleConverterFragment.php deleted file mode 100644 index df299cbdf4..0000000000 --- a/languages/utils/CLDRPluralRuleConverterFragment.php +++ /dev/null @@ -1,34 +0,0 @@ -parser = $parser; - $this->pos = $pos; - $this->length = $length; - $this->end = $pos + $length; - } - - public function error( $message ) { - $text = $this->getText(); - throw new CLDRPluralRuleError( "$message at position " . ( $this->pos + 1 ) . ": \"$text\"" ); - } - - public function getText() { - return substr( $this->parser->rule, $this->pos, $this->length ); - } -} diff --git a/languages/utils/CLDRPluralRuleConverterOperator.php b/languages/utils/CLDRPluralRuleConverterOperator.php deleted file mode 100644 index de17f29112..0000000000 --- a/languages/utils/CLDRPluralRuleConverterOperator.php +++ /dev/null @@ -1,114 +0,0 @@ - 'bbb', - 'and' => 'bbb', - 'is' => 'nnb', - 'is-not' => 'nnb', - 'in' => 'nrb', - 'not-in' => 'nrb', - 'within' => 'nrb', - 'not-within' => 'nrb', - 'mod' => 'nnn', - ',' => 'rrr', - '..' => 'nnr', - ); - - /** - * Map converting from the abbrevation to the full form. - * - * @var array - */ - private static $typeSpecMap = array( - 'b' => 'boolean', - 'n' => 'number', - 'r' => 'range', - ); - - /** - * Map for converting the new operators introduced in Rev 33 to the old forms - */ - private static $aliasMap = array( - '%' => 'mod', - '!=' => 'not-in', - '=' => 'in' - ); - - /** - * Initialize a new instance of a CLDRPluralRuleConverterOperator object - * - * @param CLDRPluralRuleConverter $parser The parser - * @param string $name The operator name - * @param int $pos The length - * @param int $length - */ - function __construct( $parser, $name, $pos, $length ) { - parent::__construct( $parser, $pos, $length ); - if ( isset( self::$aliasMap[$name] ) ) { - $name = self::$aliasMap[$name]; - } - $this->name = $name; - } - - /** - * Compute the operation - * - * @param CLDRPluralRuleConverterExpression $left The left part of the expression - * @param CLDRPluralRuleConverterExpression $right The right part of the expression - * @return CLDRPluralRuleConverterExpression The result of the operation - */ - public function operate( $left, $right ) { - $typeSpec = self::$opTypes[$this->name]; - - $leftType = self::$typeSpecMap[$typeSpec[0]]; - $rightType = self::$typeSpecMap[$typeSpec[1]]; - $resultType = self::$typeSpecMap[$typeSpec[2]]; - - $start = min( $this->pos, $left->pos, $right->pos ); - $end = max( $this->end, $left->end, $right->end ); - $length = $end - $start; - - $newExpr = new CLDRPluralRuleConverterExpression( $this->parser, $resultType, - "{$left->rpn} {$right->rpn} {$this->name}", - $start, $length ); - - if ( !$left->isType( $leftType ) ) { - $newExpr->error( "invalid type for left operand: expected $leftType, got {$left->type}" ); - } - - if ( !$right->isType( $rightType ) ) { - $newExpr->error( "invalid type for right operand: expected $rightType, got {$right->type}" ); - } - - return $newExpr; - } -} diff --git a/languages/utils/CLDRPluralRuleError.php b/languages/utils/CLDRPluralRuleError.php deleted file mode 100644 index cc0b5d2fb2..0000000000 --- a/languages/utils/CLDRPluralRuleError.php +++ /dev/null @@ -1,20 +0,0 @@ - rule format. - * @return int The index of the plural form which passed the evaluation - */ - public static function evaluate( $number, array $rules ) { - $rules = self::compile( $rules ); - - return self::evaluateCompiled( $number, $rules ); - } - - /** - * Convert a set of rules to a compiled form which is optimised for - * fast evaluation. The result will be an array of strings, and may be cached. - * - * @param array $rules The rules to compile - * @return array An array of compile rules. - */ - public static function compile( array $rules ) { - // We can't use array_map() for this because it generates a warning if - // there is an exception. - foreach ( $rules as &$rule ) { - $rule = CLDRPluralRuleConverter::convert( $rule ); - } - - return $rules; - } - - /** - * Evaluate a compiled set of rules returned by compile(). Do not allow - * the user to edit the compiled form, or else PHP errors may result. - * - * @param string $number The number to be evaluated against the rules, in English, or it - * may be a type convertible to string. - * @param array $rules The associative array of plural rules in pluralform => rule format. - * @return int The index of the plural form which passed the evaluation - */ - public static function evaluateCompiled( $number, array $rules ) { - // Calculate the values of the operand symbols - $number = strval( $number ); - if ( !preg_match( '/^ -? ( ([0-9]+) (?: \. ([0-9]+) )? )$/x', $number, $m ) ) { - wfDebug( __METHOD__ . ": invalid number input, returning 'other'\n" ); - - return count( $rules ); - } - if ( !isset( $m[3] ) ) { - $operandSymbols = array( - 'n' => intval( $m[1] ), - 'i' => intval( $m[1] ), - 'v' => 0, - 'w' => 0, - 'f' => 0, - 't' => 0 - ); - } else { - $absValStr = $m[1]; - $intStr = $m[2]; - $fracStr = $m[3]; - $operandSymbols = array( - 'n' => floatval( $absValStr ), - 'i' => intval( $intStr ), - 'v' => strlen( $fracStr ), - 'w' => strlen( rtrim( $fracStr, '0' ) ), - 'f' => intval( $fracStr ), - 't' => intval( rtrim( $fracStr, '0' ) ), - ); - } - - // The compiled form is RPN, with tokens strictly delimited by - // spaces, so this is a simple RPN evaluator. - foreach ( $rules as $i => $rule ) { - $stack = array(); - $zero = ord( '0' ); - $nine = ord( '9' ); - foreach ( StringUtils::explode( ' ', $rule ) as $token ) { - $ord = ord( $token ); - if ( isset( $operandSymbols[$token] ) ) { - $stack[] = $operandSymbols[$token]; - } elseif ( $ord >= $zero && $ord <= $nine ) { - $stack[] = intval( $token ); - } else { - $right = array_pop( $stack ); - $left = array_pop( $stack ); - $result = self::doOperation( $token, $left, $right ); - $stack[] = $result; - } - } - if ( $stack[0] ) { - return $i; - } - } - // None of the provided rules match. The number belongs to category - // 'other', which comes last. - return count( $rules ); - } - - /** - * Do a single operation - * - * @param string $token The token string - * @param mixed $left The left operand. If it is an object, its state may be destroyed. - * @param mixed $right The right operand - * @throws CLDRPluralRuleError - * @return mixed The operation result - */ - private static function doOperation( $token, $left, $right ) { - if ( in_array( $token, array( 'in', 'not-in', 'within', 'not-within' ) ) ) { - if ( !( $right instanceof CLDRPluralRuleEvaluatorRange ) ) { - $right = new CLDRPluralRuleEvaluatorRange( $right ); - } - } - switch ( $token ) { - case 'or': - return $left || $right; - case 'and': - return $left && $right; - case 'is': - return $left == $right; - case 'is-not': - return $left != $right; - case 'in': - return $right->isNumberIn( $left ); - case 'not-in': - return !$right->isNumberIn( $left ); - case 'within': - return $right->isNumberWithin( $left ); - case 'not-within': - return !$right->isNumberWithin( $left ); - case 'mod': - if ( is_int( $left ) ) { - return (int)fmod( $left, $right ); - } - - return fmod( $left, $right ); - case ',': - if ( $left instanceof CLDRPluralRuleEvaluatorRange ) { - $range = $left; - } else { - $range = new CLDRPluralRuleEvaluatorRange( $left ); - } - $range->add( $right ); - - return $range; - case '..': - return new CLDRPluralRuleEvaluatorRange( $left, $right ); - default: - throw new CLDRPluralRuleError( "Invalid RPN token" ); - } - } -} diff --git a/languages/utils/CLDRPluralRuleEvaluatorRange.php b/languages/utils/CLDRPluralRuleEvaluatorRange.php deleted file mode 100644 index 996c22e349..0000000000 --- a/languages/utils/CLDRPluralRuleEvaluatorRange.php +++ /dev/null @@ -1,110 +0,0 @@ -parts[] = $start; - } else { - $this->parts[] = array( $start, $end ); - } - } - - /** - * Determine if the given number is inside the range. - * - * @param int $number The number to check - * @param bool $integerConstraint If true, also asserts the number is an integer; - * otherwise, number simply has to be inside the range. - * @return bool True if the number is inside the range; otherwise, false. - */ - function isNumberIn( $number, $integerConstraint = true ) { - foreach ( $this->parts as $part ) { - if ( is_array( $part ) ) { - if ( ( !$integerConstraint || floor( $number ) === (float)$number ) - && $number >= $part[0] && $number <= $part[1] - ) { - return true; - } - } else { - if ( $number == $part ) { - return true; - } - } - } - - return false; - } - - /** - * Readable alias for isNumberIn( $number, false ), and the implementation - * of the "within" operator. - * - * @param int $number The number to check - * @return bool True if the number is inside the range; otherwise, false. - */ - function isNumberWithin( $number ) { - return $this->isNumberIn( $number, false ); - } - - /** - * Add another part to this range. - * - * @param CLDRPluralRuleEvaluatorRange|int $other The part to add, either - * a range object itself or a single number. - */ - function add( $other ) { - if ( $other instanceof self ) { - $this->parts = array_merge( $this->parts, $other->parts ); - } else { - $this->parts[] = $other; - } - } - - /** - * Returns the string representation of the rule evaluator range. - * The purpose of this method is to help debugging. - * - * @return string The string representation of the rule evaluator range - */ - function __toString() { - $s = 'Range('; - foreach ( $this->parts as $i => $part ) { - if ( $i ) { - $s .= ', '; - } - if ( is_array( $part ) ) { - $s .= $part[0] . '..' . $part[1]; - } else { - $s .= $part; - } - } - $s .= ')'; - - return $s; - } -} diff --git a/tests/phpunit/languages/utils/CLDRPluralRuleEvaluatorTest.php b/tests/phpunit/languages/utils/CLDRPluralRuleEvaluatorTest.php deleted file mode 100644 index 8e3b1145cf..0000000000 --- a/tests/phpunit/languages/utils/CLDRPluralRuleEvaluatorTest.php +++ /dev/null @@ -1,151 +0,0 @@ -assertEquals( $expected, $result, $comment ); - } - - /** - * @dataProvider invalidTestCases - * @expectedException CLDRPluralRuleError - */ - function testInvalidRules( $rules, $comment ) { - CLDRPluralRuleEvaluator::evaluate( 1, (array)$rules ); - } - - function validTestCases() { - $tests = array( - # expected, rule, number, comment - array( 0, 'n is 1', 1, 'integer number and is' ), - array( 0, 'n is 1', "1", 'string integer number and is' ), - array( 0, 'n is 1', 1.0, 'float number and is' ), - array( 0, 'n is 1', "1.0", 'string float number and is' ), - array( 1, 'n is 1', 1.1, 'float number and is' ), - array( 1, 'n is 1', 2, 'float number and is' ), - - array( 0, 'n in 1,3,5', 3, '' ), - array( 1, 'n not in 1,3,5', 5, '' ), - - array( 1, 'n in 1,3,5', 2, '' ), - array( 0, 'n not in 1,3,5', 4, '' ), - - array( 0, 'n in 1..3', 2, '' ), - array( 0, 'n in 1..3', 3, 'in is inclusive' ), - array( 1, 'n in 1..3', 0, '' ), - - array( 1, 'n not in 1..3', 2, '' ), - array( 1, 'n not in 1..3', 3, 'in is inclusive' ), - array( 0, 'n not in 1..3', 0, '' ), - - array( 1, 'n is not 1 and n is not 2 and n is not 3', 1, 'and relation' ), - array( 0, 'n is not 1 and n is not 2 and n is not 4', 3, 'and relation' ), - - array( 0, 'n is not 1 or n is 1', 1, 'or relation' ), - array( 1, 'n is 1 or n is 2', 3, 'or relation' ), - - array( 0, 'n is 1', 1, 'extra whitespace' ), - - array( 0, 'n mod 3 is 1', 7, 'mod' ), - array( 0, 'n mod 3 is not 1', 4.3, 'mod with floats' ), - - array( 0, 'n within 1..3', 2, 'within with integer' ), - array( 0, 'n within 1..3', 2.5, 'within with float' ), - array( 0, 'n in 1..3', 2, 'in with integer' ), - array( 1, 'n in 1..3', 2.5, 'in with float' ), - - array( 0, 'n in 3 or n is 4 and n is 5', 3, 'and binds more tightly than or' ), - array( 1, 'n is 3 or n is 4 and n is 5', 4, 'and binds more tightly than or' ), - - array( 0, 'n mod 10 in 3..4,9 and n mod 100 not in 10..19,70..79,90..99', 24, 'breton rule' ), - array( 1, 'n mod 10 in 3..4,9 and n mod 100 not in 10..19,70..79,90..99', 25, 'breton rule' ), - - array( 0, 'n within 0..2 and n is not 2', 0, 'french rule' ), - array( 0, 'n within 0..2 and n is not 2', 1, 'french rule' ), - array( 0, 'n within 0..2 and n is not 2', 1.2, 'french rule' ), - array( 1, 'n within 0..2 and n is not 2', 2, 'french rule' ), - - array( 1, 'n in 3..10,13..19', 2, 'scottish rule - ranges with comma' ), - array( 0, 'n in 3..10,13..19', 4, 'scottish rule - ranges with comma' ), - array( 1, 'n in 3..10,13..19', 12.999, 'scottish rule - ranges with comma' ), - array( 0, 'n in 3..10,13..19', 13, 'scottish rule - ranges with comma' ), - - array( 0, '5 mod 3 is n', 2, 'n as result of mod - no need to pass' ), - - # Revision 33 new operand examples - # expected, rule, number, comment - array( 0, 'i is 1', '1.00', 'new operand i' ), - array( 0, 'v is 2', '1.00', 'new operand v' ), - array( 0, 'w is 0', '1.00', 'new operand w' ), - array( 0, 'f is 0', '1.00', 'new operand f' ), - array( 0, 't is 0', '1.00', 'new operand t' ), - - array( 0, 'i is 1', '1.30', 'new operand i' ), - array( 0, 'v is 2', '1.30', 'new operand v' ), - array( 0, 'w is 1', '1.30', 'new operand w' ), - array( 0, 'f is 30', '1.30', 'new operand f' ), - array( 0, 't is 3', '1.30', 'new operand t' ), - - array( 0, 'i is 1', '1.03', 'new operand i' ), - array( 0, 'v is 2', '1.03', 'new operand v' ), - array( 0, 'w is 2', '1.03', 'new operand w' ), - array( 0, 'f is 3', '1.03', 'new operand f' ), - array( 0, 't is 3', '1.03', 'new operand t' ), - - # Revision 33 new operator aliases - # expected, rule, number, comment - array( 0, 'n % 3 is 1', 7, 'new % operator' ), - array( 0, 'n = 1,3,5', 3, 'new = operator' ), - array( 1, 'n != 1,3,5', 5, 'new != operator' ), - - # Revision 33 samples - # expected, rule, number, comment - // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong - array( 0, 'n in 1,3,5@integer 3~10, 103~110, 1003, … @decimal 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 103.0, 1003.0, …', 3, 'samples' ), - // @codingStandardsIgnoreEnd - - # Revision 33 some test cases from CLDR - array( 0, 'i = 1 and v = 0 or i = 0 and t = 1', '0.1', 'pt one' ), - array( 0, 'i = 1 and v = 0 or i = 0 and t = 1', '0.01', 'pt one' ), - array( 0, 'i = 1 and v = 0 or i = 0 and t = 1', '0.10', 'pt one' ), - array( 0, 'i = 1 and v = 0 or i = 0 and t = 1', '0.010', 'pt one' ), - array( 0, 'i = 1 and v = 0 or i = 0 and t = 1', '0.100', 'pt one' ), - array( 1, 'i = 1 and v = 0 or i = 0 and t = 1', '0.0', 'pt other' ), - array( 1, 'i = 1 and v = 0 or i = 0 and t = 1', '0.2', 'pt other' ), - array( 1, 'i = 1 and v = 0 or i = 0 and t = 1', '10.0', 'pt other' ), - array( 1, 'i = 1 and v = 0 or i = 0 and t = 1', '100.0', 'pt other' ), - // @codingStandardsIgnoreStart Ignore Generic.Files.LineLength.TooLong - array( 0, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '2', 'bs few' ), - array( 0, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '4', 'bs few' ), - array( 0, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '22', 'bs few' ), - array( 0, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '102', 'bs few' ), - array( 0, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '0.2', 'bs few' ), - array( 0, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '0.4', 'bs few' ), - array( 0, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '10.2', 'bs few' ), - array( 1, 'v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14', '10.0', 'bs other' ), - // @codingStandardsIgnoreEnd - ); - - return $tests; - } - - function invalidTestCases() { - $tests = array( - array( 'n mod mod 5 is 1', 'mod mod' ), - array( 'n', 'just n' ), - array( 'n is in 5', 'is in' ), - ); - - return $tests; - } -} -- 2.20.1