3 * Parse and evaluate a plural rule
5 * @author Niklas Laxstrom
7 * @copyright Copyright © 2010-2012, Niklas Laxström
8 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
12 class CLDRPluralRuleEvaluator
{
14 * Evaluate a number against a set of plural rules. If a rule passes,
15 * return the index of plural rule.
17 * @param int The number to be evaluated against the rules
18 * @param array The associative array of plural rules in pluralform => rule format.
19 * @return int The index of the plural form which passed the evaluation
21 public static function evaluate( $number, $rules ) {
26 foreach ( $rules as $form => $rule ) {
27 $parsedRule = self
::parseCLDRRule( $rule, $number );
29 if ( eval( "return $parsedRule;" ) ) {
36 private static function parseCLDRRule( $rule ) {
37 $rule = preg_replace( '/\bn\b/', '$number', $rule );
38 $rule = preg_replace( '/([^ ]+) mod (\d+)/', 'self::mod(\1,\2)', $rule );
39 $rule = preg_replace( '/([^ ]+) is not (\d+)/' , '\1!=\2', $rule );
40 $rule = preg_replace( '/([^ ]+) is (\d+)/', '\1==\2', $rule );
41 $rule = preg_replace( '/([^ ]+) not in (\d+)\.\.(\d+)/', '!self::in(\1,\2,\3)', $rule );
42 $rule = preg_replace( '/([^ ]+) not within (\d+)\.\.(\d+)/', '!self::within(\1,\2,\3)', $rule );
43 $rule = preg_replace( '/([^ ]+) in (\d+)\.\.(\d+)/', 'self::in(\1,\2,\3)', $rule );
44 $rule = preg_replace( '/([^ ]+) within (\d+)\.\.(\d+)/', 'self::within(\1,\2,\3)', $rule );
45 // AND takes precedence over OR
46 $andrule = '/([^ ]+) and ([^ ]+)/i';
47 while ( preg_match( $andrule, $rule ) ) {
48 $rule = preg_replace( $andrule, '(\1&&\2)', $rule );
50 $orrule = '/([^ ]+) or ([^ ]+)/i';
51 while ( preg_match( $orrule, $rule ) ) {
52 $rule = preg_replace( $orrule, '(\1||\2)', $rule );
58 private static function in( $num, $low, $high ) {
59 return is_int( $num ) && $num >= $low && $num <= $high;
62 private static function within( $num, $low, $high ) {
63 return $num >= $low && $num <= $high;
66 private static function mod( $num, $mod ) {
67 if ( is_int( $num ) ) {
68 return (int) fmod( $num, $mod );
70 return fmod( $num, $mod );