* $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.
* 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
'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',
"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",
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
return null;
}
try {
- $compiledRules = CLDRPluralRuleEvaluator::compile( $rules );
+ $compiledRules = Evaluator::compile( $rules );
} catch ( CLDRPluralRuleError $e ) {
wfDebugLog( 'l10n', $e->getMessage() );
mb_internal_encoding( 'UTF-8' );
}
+use CLDRPluralRuleParser\Evaluator;
+
/**
* Internationalisation code
* @ingroup Language
*/
public function getPluralRuleIndexNumber( $number ) {
$pluralRules = $this->getCompiledPluralRules();
- $form = CLDRPluralRuleEvaluator::evaluateCompiled( $number, $pluralRules );
+ $form = Evaluator::evaluateCompiled( $number, $pluralRules );
return $form;
}
+++ /dev/null
-<?php
-/**
- * @author Niklas Laxström, Tim Starling
- *
- * @copyright Copyright © 2010-2012, Niklas Laxström
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- *
- * @file
- * @since 1.20
- */
-
-/**
- * Helper class for converting rules to reverse polish notation (RPN).
- */
-class CLDRPluralRuleConverter {
- /**
- * The input string
- *
- * @var string
- */
- public $rule;
-
- /**
- * The current position
- *
- * @var int
- */
- public $pos;
-
- /**
- * The past-the-end position
- *
- * @var int
- */
- public $end;
-
- /**
- * The operator stack
- *
- * @var array
- */
- public $operators = array();
-
- /**
- * The operand stack
- *
- * @var array
- */
- public $operands = array();
-
- /**
- * Precedence levels. Note that there's no need to worry about associativity
- * for the level 4 operators, since they return boolean and don't accept
- * boolean inputs.
- */
- private static $precedence = array(
- 'or' => 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 );
- }
-}
+++ /dev/null
-<?php
-/**
- * @author Niklas Laxström, Tim Starling
- *
- * @copyright Copyright © 2010-2012, Niklas Laxström
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- *
- * @file
- * @since 1.20
- */
-
-/**
- * Helper for CLDRPluralRuleConverter.
- * An expression object, representing a region of the input string (for error
- * messages), the RPN notation used to evaluate it, and the result type for
- * validation.
- */
-class CLDRPluralRuleConverterExpression extends CLDRPluralRuleConverterFragment {
- /** @var string */
- public $type;
-
- /** @var string */
- public $rpn;
-
- function __construct( $parser, $type, $rpn, $pos, $length ) {
- parent::__construct( $parser, $pos, $length );
- $this->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;
- }
-}
+++ /dev/null
-<?php
-/**
- * @author Niklas Laxström, Tim Starling
- *
- * @copyright Copyright © 2010-2012, Niklas Laxström
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- *
- * @file
- * @since 1.20
- */
-
-/**
- * Helper for CLDRPluralRuleConverter.
- * The base class for operators and expressions, describing a region of the input string.
- */
-class CLDRPluralRuleConverterFragment {
- public $parser, $pos, $length, $end;
-
- function __construct( $parser, $pos, $length ) {
- $this->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 );
- }
-}
+++ /dev/null
-<?php
-/**
- * @author Niklas Laxström, Tim Starling
- *
- * @copyright Copyright © 2010-2012, Niklas Laxström
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- *
- * @file
- * @since 1.20
- */
-
-/**
- * Helper for CLDRPluralRuleConverter.
- * An operator object, representing a region of the input string (for error
- * messages), and the binary operator at that location.
- */
-class CLDRPluralRuleConverterOperator extends CLDRPluralRuleConverterFragment {
- /** @var string The name */
- public $name;
-
- /**
- * Each op type has three characters: left operand type, right operand type and result type
- *
- * b = boolean
- * n = number
- * r = range
- *
- * A number is a kind of range.
- *
- * @var array
- */
- private static $opTypes = array(
- 'or' => '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;
- }
-}
+++ /dev/null
-<?php
-/**
- * @author Niklas Laxström, Tim Starling
- *
- * @copyright Copyright © 2010-2012, Niklas Laxström
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- *
- * @file
- * @since 1.20
- */
-
-/**
- * The exception class for all the classes in this file. This will be thrown
- * back to the caller if there is any validation error.
- */
-class CLDRPluralRuleError extends MWException {
- function __construct( $message ) {
- parent::__construct( 'CLDR plural rule error: ' . $message );
- }
-}
+++ /dev/null
-<?php
-
-/**
- * Parse and evaluate a plural rule.
- *
- * UTS #35 Revision 33
- * http://www.unicode.org/reports/tr35/tr35-33/tr35-numbers.html#Language_Plural_Rules
- *
- * @author Niklas Laxström, Tim Starling
- *
- * @copyright Copyright © 2010-2012, Niklas Laxström
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0
- * or later
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- *
- * @file
- * @since 1.20
- */
-class CLDRPluralRuleEvaluator {
- /**
- * Evaluate a number against a set of plural rules. If a rule passes,
- * return the index of plural rule.
- *
- * @param int $number The number to be evaluated against the rules
- * @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 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" );
- }
- }
-}
+++ /dev/null
-<?php
-/**
- * @author Niklas Laxström, Tim Starling
- *
- * @copyright Copyright © 2010-2012, Niklas Laxström
- * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- *
- * @file
- * @since 1.20
- */
-
-/**
- * Evaluator helper class representing a range list.
- */
-class CLDRPluralRuleEvaluatorRange {
- /**
- * The parts
- *
- * @var array
- */
- public $parts = array();
-
- /**
- * Initialize a new instance of CLDRPluralRuleEvaluatorRange
- *
- * @param int $start The start of the range
- * @param int|bool $end The end of the range, or false if the range is not bounded.
- */
- function __construct( $start, $end = false ) {
- if ( $end === false ) {
- $this->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;
- }
-}
+++ /dev/null
-<?php
-/**
- * @author Niklas Laxström
- * @file
- */
-
-/**
- * @covers CLDRPluralRuleEvaluator
- */
-class CLDRPluralRuleEvaluatorTest extends MediaWikiTestCase {
- /**
- * @dataProvider validTestCases
- */
- function testValidRules( $expected, $rules, $number, $comment ) {
- $result = CLDRPluralRuleEvaluator::evaluate( $number, (array)$rules );
- $this->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;
- }
-}