MediaWiki  1.23.2
CLDRPluralRuleConverter.php
Go to the documentation of this file.
1 <?php
2 
22  public $rule;
23 
29  public $pos;
30 
36  public $end;
37 
43  public $operators = array();
44 
50  public $operands = array();
51 
57  static $precedence = array(
58  'or' => 2,
59  'and' => 3,
60  'is' => 4,
61  'is-not' => 4,
62  'in' => 4,
63  'not-in' => 4,
64  'within' => 4,
65  'not-within' => 4,
66  'mod' => 5,
67  ',' => 6,
68  '..' => 7,
69  );
70 
74  const WHITESPACE_CLASS = " \t\r\n";
75 
80  const NUMBER_CLASS = '0123456789';
81 
85  const OPERAND_SYMBOLS = 'nivwft';
86 
90  const WORD_REGEX = '/[a-zA-Z@]+/A';
91 
98  public static function convert( $rule ) {
99  $parser = new self( $rule );
100  return $parser->doConvert();
101  }
102 
106  protected function __construct( $rule ) {
107  $this->rule = $rule;
108  $this->pos = 0;
109  $this->end = strlen( $rule );
110  }
111 
117  protected function doConvert() {
118  $expectOperator = true;
119 
120  // Iterate through all tokens, saving the operators and operands to a
121  // stack per Dijkstra's shunting yard algorithm.
123  while ( false !== ( $token = $this->nextToken() ) ) {
124  // In this grammar, there are only binary operators, so every valid
125  // rule string will alternate between operator and operand tokens.
126  $expectOperator = !$expectOperator;
127 
128  if ( $token instanceof CLDRPluralRuleConverter_Expression ) {
129  // Operand
130  if ( $expectOperator ) {
131  $token->error( 'unexpected operand' );
132  }
133  $this->operands[] = $token;
134  continue;
135  } else {
136  // Operator
137  if ( !$expectOperator ) {
138  $token->error( 'unexpected operator' );
139  }
140  // Resolve higher precedence levels
141  $lastOp = end( $this->operators );
142  while ( $lastOp && self::$precedence[$token->name] <= self::$precedence[$lastOp->name] ) {
143  $this->doOperation( $lastOp, $this->operands );
144  array_pop( $this->operators );
145  $lastOp = end( $this->operators );
146  }
147  $this->operators[] = $token;
148  }
149  }
150 
151  // Finish off the stack
152  while ( $op = array_pop( $this->operators ) ) {
153  $this->doOperation( $op, $this->operands );
154  }
155 
156  // Make sure the result is sane. The first case is possible for an empty
157  // string input, the second should be unreachable.
158  if ( !count( $this->operands ) ) {
159  $this->error( 'condition expected' );
160  } elseif ( count( $this->operands ) > 1 ) {
161  $this->error( 'missing operator or too many operands' );
162  }
163 
164  $value = $this->operands[0];
165  if ( $value->type !== 'boolean' ) {
166  $this->error( 'the result must have a boolean type' );
167  }
168 
169  return $this->operands[0]->rpn;
170  }
171 
177  protected function nextToken() {
178  if ( $this->pos >= $this->end ) {
179  return false;
180  }
181 
182  // Whitespace
183  $length = strspn( $this->rule, self::WHITESPACE_CLASS, $this->pos );
184  $this->pos += $length;
185 
186  if ( $this->pos >= $this->end ) {
187  return false;
188  }
189 
190  // Number
191  $length = strspn( $this->rule, self::NUMBER_CLASS, $this->pos );
192  if ( $length !== 0 ) {
193  $token = $this->newNumber( substr( $this->rule, $this->pos, $length ), $this->pos );
194  $this->pos += $length;
195  return $token;
196  }
197 
198  // Two-character operators
199  $op2 = substr( $this->rule, $this->pos, 2 );
200  if ( $op2 === '..' || $op2 === '!=' ) {
201  $token = $this->newOperator( $op2, $this->pos, 2 );
202  $this->pos += 2;
203  return $token;
204  }
205 
206  // Single-character operators
207  $op1 = $this->rule[$this->pos];
208  if ( $op1 === ',' || $op1 === '=' || $op1 === '%' ) {
209  $token = $this->newOperator( $op1, $this->pos, 1 );
210  $this->pos ++;
211  return $token;
212  }
213 
214  // Word
215  if ( !preg_match( self::WORD_REGEX, $this->rule, $m, 0, $this->pos ) ) {
216  $this->error( 'unexpected character "' . $this->rule[$this->pos] . '"' );
217  }
218  $word1 = strtolower( $m[0] );
219  $word2 = '';
220  $nextTokenPos = $this->pos + strlen( $word1 );
221  if ( $word1 === 'not' || $word1 === 'is' ) {
222  // Look ahead one word
223  $nextTokenPos += strspn( $this->rule, self::WHITESPACE_CLASS, $nextTokenPos );
224  if ( $nextTokenPos < $this->end
225  && preg_match( self::WORD_REGEX, $this->rule, $m, 0, $nextTokenPos )
226  ) {
227  $word2 = strtolower( $m[0] );
228  $nextTokenPos += strlen( $word2 );
229  }
230  }
231 
232  // Two-word operators like "is not" take precedence over single-word operators like "is"
233  if ( $word2 !== '' ) {
234  $bothWords = "{$word1}-{$word2}";
235  if ( isset( self::$precedence[$bothWords] ) ) {
236  $token = $this->newOperator( $bothWords, $this->pos, $nextTokenPos - $this->pos );
237  $this->pos = $nextTokenPos;
238  return $token;
239  }
240  }
241 
242  // Single-word operators
243  if ( isset( self::$precedence[$word1] ) ) {
244  $token = $this->newOperator( $word1, $this->pos, strlen( $word1 ) );
245  $this->pos += strlen( $word1 );
246  return $token;
247  }
248 
249  // The single-character operand symbols
250  if ( strpos( self::OPERAND_SYMBOLS, $word1 ) !== false ) {
251  $token = $this->newNumber( $word1, $this->pos );
252  $this->pos ++;
253  return $token;
254  }
255 
256  // Samples
257  if ( $word1 === '@integer' || $word1 === '@decimal' ) {
258  // Samples are like comments, they have no effect on rule evaluation.
259  // They run from the first sample indicator to the end of the string.
260  $this->pos = $this->end;
261  return false;
262  }
263 
264  $this->error( 'unrecognised word' );
265  }
266 
274  protected function doOperation( $op ) {
275  if ( count( $this->operands ) < 2 ) {
276  $op->error( 'missing operand' );
277  }
278  $right = array_pop( $this->operands );
279  $left = array_pop( $this->operands );
280  $result = $op->operate( $left, $right );
281  $this->operands[] = $result;
282  }
283 
291  protected function newNumber( $text, $pos ) {
292  return new CLDRPluralRuleConverter_Expression( $this, 'number', $text, $pos, strlen( $text ) );
293  }
294 
303  protected function newOperator( $type, $pos, $length ) {
304  return new CLDRPluralRuleConverter_Operator( $this, $type, $pos, $length );
305  }
306 
310  protected function error( $message ) {
311  throw new CLDRPluralRuleError( $message );
312  }
313 }
CLDRPluralRuleConverter\$rule
string $rule
The input string.
Definition: CLDRPluralRuleConverter.php:21
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. $reader:XMLReader object $logInfo:Array of information Return false to stop further processing of the tag 'ImportHandlePageXMLTag':When parsing a XML tag in a page. $reader:XMLReader object $pageInfo:Array of information Return false to stop further processing of the tag 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information Return false to stop further processing of the tag 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. $reader:XMLReader object Return false to stop further processing of the tag 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. $reader:XMLReader object $revisionInfo:Array of information Return false to stop further processing of the tag 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. $title:Title object for the current page $request:WebRequest $ignoreRedirect:boolean to skip redirect check $target:Title/string of redirect target $article:Article object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) $article:article(object) being checked 'IsTrustedProxy':Override the result of wfIsTrustedProxy() $ip:IP being check $result:Change this value to override the result of wfIsTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of User::isValidEmailAddr(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetMagic':DEPRECATED, use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetSpecialPageAliases':DEPRECATED, use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Associative array mapping language codes to prefixed links of the form "language:title". & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LinkBegin':Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1528
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
CLDRPluralRuleConverter\doConvert
doConvert()
Do the operation.
Definition: CLDRPluralRuleConverter.php:112
CLDRPluralRuleConverter\error
error( $message)
Throw an error.
Definition: CLDRPluralRuleConverter.php:305
CLDRPluralRuleConverter\$operators
array $operators
The operator stack.
Definition: CLDRPluralRuleConverter.php:39
$right
return false if a UserGetRights hook might remove the named right $right
Definition: hooks.txt:2798
CLDRPluralRuleConverter\doOperation
doOperation( $op)
For the binary operator $op, pop its operands off the stack and push a fragment with rpn and type mem...
Definition: CLDRPluralRuleConverter.php:269
CLDRPluralRuleConverter\convert
static convert( $rule)
Convert a rule to RPN.
Definition: CLDRPluralRuleConverter.php:93
CLDRPluralRuleConverter\$precedence
static $precedence
Precedence levels.
Definition: CLDRPluralRuleConverter.php:52
CLDRPluralRuleConverter
Helper class for converting rules to reverse polish notation (RPN).
Definition: CLDRPluralRuleConverter.php:16
CLDRPluralRuleConverter\WHITESPACE_CLASS
const WHITESPACE_CLASS
A character list defining whitespace, for use in strspn() etc.
Definition: CLDRPluralRuleConverter.php:69
CLDRPluralRuleConverter\nextToken
nextToken()
Fetch the next token from the input string.
Definition: CLDRPluralRuleConverter.php:172
CLDRPluralRuleError
The exception class for all the classes in this file.
Definition: CLDRPluralRuleError.php:17
$parser
do that in ParserLimitReportFormat instead $parser
Definition: hooks.txt:1956
CLDRPluralRuleConverter_Expression
Helper for CLDRPluralRuleConverter.
Definition: CLDRPluralRuleConverter_Expression.php:19
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
$value
$value
Definition: styleTest.css.php:45
CLDRPluralRuleConverter\$operands
array $operands
The operand stack.
Definition: CLDRPluralRuleConverter.php:45
CLDRPluralRuleConverter\OPERAND_SYMBOLS
const OPERAND_SYMBOLS
A character list of symbolic operands.
Definition: CLDRPluralRuleConverter.php:80
CLDRPluralRuleConverter\NUMBER_CLASS
const NUMBER_CLASS
Same for digits.
Definition: CLDRPluralRuleConverter.php:75
CLDRPluralRuleConverter\newOperator
newOperator( $type, $pos, $length)
Create a binary operator.
Definition: CLDRPluralRuleConverter.php:298
CLDRPluralRuleConverter\__construct
__construct( $rule)
Private constructor.
Definition: CLDRPluralRuleConverter.php:101
operators
This technique is used by the more ambitious MediaWiki site operators
Definition: skin.txt:75
CLDRPluralRuleConverter\newNumber
newNumber( $text, $pos)
Create a numerical expression object.
Definition: CLDRPluralRuleConverter.php:286
CLDRPluralRuleConverter\$end
int $end
The past-the-end position.
Definition: CLDRPluralRuleConverter.php:33
CLDRPluralRuleConverter_Operator
Helper for CLDRPluralRuleConverter.
Definition: CLDRPluralRuleConverter_Operator.php:18
CLDRPluralRuleConverter\WORD_REGEX
const WORD_REGEX
An anchored regular expression which matches a word at the current offset.
Definition: CLDRPluralRuleConverter.php:85
CLDRPluralRuleConverter\$pos
int $pos
The current position.
Definition: CLDRPluralRuleConverter.php:27
$type
$type
Definition: testCompression.php:46