3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
10 namespace Zend\I18n\Translator\Plural
;
12 use Zend\I18n\Exception
;
17 * This plural rule parser is implemented after the article "Top Down Operator
18 * Precedence" described in <http://javascript.crockford.com/tdop/tdop.html>.
30 * Current lexer position in the string.
34 protected $currentPos;
41 protected $currentToken;
48 protected $symbolTable = array();
51 * Create a new plural parser.
54 public function __construct()
56 $this->populateSymbolTable();
60 * Populate the symbol table.
64 protected function populateSymbolTable()
67 $this->registerSymbol('?', 20)->setLeftDenotationGetter(
68 function (Symbol
$self, Symbol
$left) {
70 $self->second
= $self->parser
->expression();
71 $self->parser
->advance(':');
72 $self->third
= $self->parser
->expression();
76 $this->registerSymbol(':');
79 $this->registerLeftInfixSymbol('||', 30);
80 $this->registerLeftInfixSymbol('&&', 40);
83 $this->registerLeftInfixSymbol('==', 50);
84 $this->registerLeftInfixSymbol('!=', 50);
87 $this->registerLeftInfixSymbol('>', 50);
88 $this->registerLeftInfixSymbol('<', 50);
89 $this->registerLeftInfixSymbol('>=', 50);
90 $this->registerLeftInfixSymbol('<=', 50);
93 $this->registerLeftInfixSymbol('-', 60);
94 $this->registerLeftInfixSymbol('+', 60);
97 $this->registerLeftInfixSymbol('*', 70);
98 $this->registerLeftInfixSymbol('/', 70);
99 $this->registerLeftInfixSymbol('%', 70);
102 $this->registerPrefixSymbol('!', 80);
105 $this->registerSymbol('n')->setNullDenotationGetter(
106 function (Symbol
$self) {
110 $this->registerSymbol('number')->setNullDenotationGetter(
111 function (Symbol
$self) {
117 $this->registerSymbol('(')->setNullDenotationGetter(
118 function (Symbol
$self) {
119 $expression = $self->parser
->expression();
120 $self->parser
->advance(')');
124 $this->registerSymbol(')');
127 $this->registerSymbol('eof');
131 * Register a left infix symbol.
134 * @param int $leftBindingPower
137 protected function registerLeftInfixSymbol($id, $leftBindingPower)
139 $this->registerSymbol($id, $leftBindingPower)->setLeftDenotationGetter(
140 function (Symbol
$self, Symbol
$left) use ($leftBindingPower) {
141 $self->first
= $left;
142 $self->second
= $self->parser
->expression($leftBindingPower);
149 * Register a right infix symbol.
152 * @param int $leftBindingPower
155 protected function registerRightInfixSymbol($id, $leftBindingPower)
157 $this->registerSymbol($id, $leftBindingPower)->setLeftDenotationGetter(
158 function (Symbol
$self, Symbol
$left) use ($leftBindingPower) {
159 $self->first
= $left;
160 $self->second
= $self->parser
->expression($leftBindingPower - 1);
167 * Register a prefix symbol.
170 * @param int $leftBindingPower
173 protected function registerPrefixSymbol($id, $leftBindingPower)
175 $this->registerSymbol($id, $leftBindingPower)->setNullDenotationGetter(
176 function (Symbol
$self) use ($leftBindingPower) {
177 $self->first
= $self->parser
->expression($leftBindingPower);
178 $self->second
= null;
188 * @param int $leftBindingPower
191 protected function registerSymbol($id, $leftBindingPower = 0)
193 if (isset($this->symbolTable
[$id])) {
194 $symbol = $this->symbolTable
[$id];
195 $symbol->leftBindingPower
= max(
196 $symbol->leftBindingPower
,
200 $symbol = new Symbol($this, $id, $leftBindingPower);
201 $this->symbolTable
[$id] = $symbol;
212 protected function getSymbol($id)
214 if (!isset($this->symbolTable
[$id])) {
215 // Unkown symbol exception
218 return clone $this->symbolTable
[$id];
224 * @param string $string
227 public function parse($string)
229 $this->string = $string . "\0";
230 $this->currentPos
= 0;
231 $this->currentToken
= $this->getNextToken();
233 return $this->expression();
237 * Parse an expression.
239 * @param int $rightBindingPower
242 public function expression($rightBindingPower = 0)
244 $token = $this->currentToken
;
245 $this->currentToken
= $this->getNextToken();
246 $left = $token->getNullDenotation();
248 while ($rightBindingPower < $this->currentToken
->leftBindingPower
) {
249 $token = $this->currentToken
;
250 $this->currentToken
= $this->getNextToken();
251 $left = $token->getLeftDenotation($left);
258 * Advance the current token and optionally check the old token id.
262 * @throws Exception\ParseException
264 public function advance($id = null)
266 if ($id !== null && $this->currentToken
->id
!== $id) {
267 throw new Exception\
ParseException(sprintf(
268 'Expected token with id %s but received %s',
269 $id, $this->currentToken
->id
273 $this->currentToken
= $this->getNextToken();
277 * Get the next token.
280 * @throws Exception\ParseException
282 protected function getNextToken()
284 while ($this->string[$this->currentPos
] === ' ' ||
$this->string[$this->currentPos
] === "\t") {
288 $result = $this->string[$this->currentPos++
];
302 while (ctype_digit($this->string[$this->currentPos
])) {
303 $result .= $this->string[$this->currentPos++
];
307 $value = (int) $result;
313 if ($this->string[$this->currentPos
] === $result) {
315 $id = $result . $result;
324 if ($this->string[$this->currentPos
] === '=') {
353 throw new Exception\
ParseException(sprintf(
354 'Found invalid character "%s" in input stream',
360 $token = $this->getSymbol($id);
361 $token->value
= $value;