Hoa central
Analyzer.php
Go to the documentation of this file.
1 <?php
2 
38 
39 use Hoa\Compiler;
40 
49 class Analyzer
50 {
56  protected $_createdRules = null;
57 
63  protected $_tokens = null;
64 
70  protected $_rules = null;
71 
77  protected $_rule = null;
78 
84  protected $_currentState = 0;
85 
86 
87 
94  public function __construct(Array $tokens)
95  {
96  $this->_tokens = $tokens;
97 
98  return;
99  }
100 
106  public function getCreatedRules()
107  {
108  return $this->_createdRules;
109  }
110 
118  public function analyzeRules(Array $rules)
119  {
120  if (empty($rules)) {
121  throw new Compiler\Exception\Rule('No rules specified!', 0);
122  }
123 
124  $tokens = ['default' =>
125  [
126  'skip' => '\s',
127  'or' => '\|',
128  'zero_or_one' => '\?',
129  'one_or_more' => '\+',
130  'zero_or_more' => '\*',
131  'n_to_m' => '\{[0-9]+,[0-9]+\}',
132  'zero_to_m' => '\{,[0-9]+\}',
133  'n_or_more' => '\{[0-9]+,\}',
134  'exactly_n' => '\{[0-9]+\}',
135  'skipped' => '::[a-zA-Z_][a-zA-Z0-9_]*(\[\d+\])?::',
136  'kept' => '<[a-zA-Z_][a-zA-Z0-9_]*(\[\d+\])?' . '>',
137  'named' => '[a-zA-Z_][a-zA-Z0-9_]*\(\)',
138  'node' => '#[a-zA-Z_][a-zA-Z0-9_]*(:[mM])?',
139  'capturing_' => '\(',
140  '_capturing' => '\)'
141  ]
142  ];
143 
144  $this->_createdRules = [];
145  $this->_rules = $rules;
146 
147  foreach ($rules as $key => $value) {
148  $lexer = new Compiler\Llk\Lexer();
149  $this->_tokenSequence = $lexer->lexMe($value, $tokens);
150  $this->_rule = $value;
151  $this->_currentState = 0;
152  $nodeId = null;
153 
154  if ('#' === $key[0]) {
155  $nodeId = $key;
156  $key = substr($key, 1);
157  }
158 
159  $pNodeId = $nodeId;
160  $rule = $this->rule($pNodeId);
161 
162  if (null === $rule) {
163  throw new Compiler\Exception(
164  'Error while parsing rule %s.',
165  1,
166  $key
167  );
168  }
169 
170  $zeRule = $this->_createdRules[$rule];
171  $zeRule->setName($key);
172  $zeRule->setPPRepresentation($value);
173 
174  if (null !== $nodeId) {
175  $zeRule->setDefaultId($nodeId);
176  }
177 
178  unset($this->_createdRules[$rule]);
179  $this->_createdRules[$key] = $zeRule;
180  }
181 
182  return $this->_createdRules;
183  }
184 
190  protected function rule(&$pNodeId)
191  {
192  return $this->choice($pNodeId);
193  }
194 
200  protected function choice(&$pNodeId)
201  {
202  $content = [];
203 
204  // concatenation() …
205  $nNodeId = $pNodeId;
206  $rule = $this->concatenation($nNodeId);
207 
208  if (null === $rule) {
209  return null;
210  }
211 
212  if (null !== $nNodeId) {
213  $this->_createdRules[$rule]->setNodeId($nNodeId);
214  }
215 
216  $content[] = $rule;
217  $others = false;
218 
219  // … ( ::or:: concatenation() )*
220  while ('or' === $this->getCurrentToken()) {
221  $this->consumeToken();
222  $others = true;
223  $nNodeId = $pNodeId;
224  $rule = $this->concatenation($nNodeId);
225 
226  if (null === $rule) {
227  return null;
228  }
229 
230  if (null !== $nNodeId) {
231  $this->_createdRules[$rule]->setNodeId($nNodeId);
232  }
233 
234  $content[] = $rule;
235  }
236 
237  $pNodeId = null;
238 
239  if (false === $others) {
240  return $rule;
241  }
242 
243  $name = count($this->_createdRules) + 1;
244  $this->_createdRules[$name] = new Choice($name, $content, null);
245 
246  return $name;
247  }
248 
254  protected function concatenation(&$pNodeId)
255  {
256  $content = [];
257 
258  // repetition() …
259  $rule = $this->repetition($pNodeId);
260 
261  if (null === $rule) {
262  return null;
263  }
264 
265  $content[] = $rule;
266  $others = false;
267 
268  // … repetition()*
269  while (null !== $r1 = $this->repetition($pNodeId)) {
270  $content[] = $r1;
271  $others = true;
272  }
273 
274  if (false === $others && null === $pNodeId) {
275  return $rule;
276  }
277 
278  $name = count($this->_createdRules) + 1;
279  $this->_createdRules[$name] = new Concatenation(
280  $name,
281  $content,
282  null
283  );
284 
285  return $name;
286  }
287 
294  protected function repetition(&$pNodeId)
295  {
296 
297  // simple() …
298  $content = $this->simple($pNodeId);
299 
300  if (null === $content) {
301  return null;
302  }
303 
304  // … quantifier()?
305  switch ($this->getCurrentToken()) {
306 
307  case 'zero_or_one':
308  $min = 0;
309  $max = 1;
310  $this->consumeToken();
311 
312  break;
313 
314  case 'one_or_more':
315  $min = 1;
316  $max = -1;
317  $this->consumeToken();
318 
319  break;
320 
321  case 'zero_or_more':
322  $min = 0;
323  $max = -1;
324  $this->consumeToken();
325 
326  break;
327 
328  case 'n_to_m':
329  $handle = trim($this->getCurrentToken('value'), '{}');
330  $nm = explode(',', $handle);
331  $min = (int) trim($nm[0]);
332  $max = (int) trim($nm[1]);
333  $this->consumeToken();
334 
335  break;
336 
337  case 'zero_to_m':
338  $min = 0;
339  $max = (int) trim($this->getCurrentToken('value'), '{,}');
340  $this->consumeToken();
341 
342  break;
343 
344  case 'n_or_more':
345  $min = (int) trim($this->getCurrentToken('value'), '{,}');
346  $max = -1;
347  $this->consumeToken();
348 
349  break;
350 
351  case 'exactly_n':
352  $handle = trim($this->getCurrentToken('value'), '{}');
353  $min = (int) $handle;
354  $max = $min;
355  $this->consumeToken();
356 
357  break;
358  }
359 
360  // … <node>?
361  if ('node' === $this->getCurrentToken()) {
362  $pNodeId = $this->getCurrentToken('value');
363  $this->consumeToken();
364  }
365 
366  if (!isset($min)) {
367  return $content;
368  }
369 
370  if (-1 != $max && $max < $min) {
371  throw new Compiler\Exception(
372  'Upper bound of iteration must be greater of ' .
373  'equal to lower bound',
374  2
375  );
376  }
377 
378  $name = count($this->_createdRules) + 1;
379  $this->_createdRules[$name] = new Repetition(
380  $name,
381  $min,
382  $max,
383  $content,
384  null
385  );
386 
387  return $name;
388  }
389 
397  protected function simple(&$pNodeId)
398  {
399  if ('capturing_' === $this->getCurrentToken()) {
400  $this->consumeToken();
401  $rule = $this->choice($pNodeId);
402 
403  if (null === $rule) {
404  return null;
405  }
406 
407  if ('_capturing' != $this->getCurrentToken()) {
408  return null;
409  }
410 
411  $this->consumeToken();
412 
413  return $rule;
414  }
415 
416  if ('skipped' === $this->getCurrentToken()) {
417  $tokenName = trim($this->getCurrentToken('value'), ':');
418 
419  if (']' === substr($tokenName, -1)) {
420  $uId = (int) substr($tokenName, strpos($tokenName, '[') + 1, -1);
421  $tokenName = substr($tokenName, 0, strpos($tokenName, '['));
422  } else {
423  $uId = -1;
424  }
425 
426  $exists = false;
427 
428  foreach ($this->_tokens as $namespace => $tokens) {
429  foreach ($tokens as $token => $value) {
430  if ($token === $tokenName ||
431  substr($token, 0, strpos($token, ':')) === $tokenName) {
432  $exists = true;
433 
434  break 2;
435  }
436  }
437  }
438 
439  if (false == $exists) {
440  throw new Compiler\Exception(
441  'Token ::%s:: does not exist in%s.',
442  3,
443  [$tokenName, $this->_rule]
444  );
445  }
446 
447  $name = count($this->_createdRules) + 1;
448  $this->_createdRules[$name] = new Token(
449  $name,
450  $tokenName,
451  null,
452  $uId
453  );
454  $this->consumeToken();
455 
456  return $name;
457  }
458 
459  if ('kept' === $this->getCurrentToken()) {
460  $tokenName = trim($this->getCurrentToken('value'), '<>');
461 
462  if (']' === substr($tokenName, -1)) {
463  $uId = (int) substr($tokenName, strpos($tokenName, '[') + 1, -1);
464  $tokenName = substr($tokenName, 0, strpos($tokenName, '['));
465  } else {
466  $uId = -1;
467  }
468 
469  $exists = false;
470 
471  foreach ($this->_tokens as $namespace => $tokens) {
472  foreach ($tokens as $token => $value) {
473  if ($token === $tokenName
474  || substr($token, 0, strpos($token, ':')) === $tokenName) {
475  $exists = true;
476 
477  break 2;
478  }
479  }
480  }
481 
482  if (false == $exists) {
483  throw new Compiler\Exception(
484  'Token <%s> does not exist in%s.',
485  4,
486  [$tokenName, $this->_rule]
487  );
488  }
489 
490  $name = count($this->_createdRules) + 1;
491  $token = new Token(
492  $name,
493  $tokenName,
494  null,
495  $uId
496  );
497  $token->setKept(true);
498  $this->_createdRules[$name] = $token;
499  $this->consumeToken();
500 
501  return $name;
502  }
503 
504  if ('named' === $this->getCurrentToken()) {
505  $tokenName = rtrim($this->getCurrentToken('value'), '()');
506 
507  if (false === array_key_exists($tokenName, $this->_rules) &&
508  false === array_key_exists('#' . $tokenName, $this->_rules)) {
509  throw new Compiler\Exception\Rule(
510  'Rule %s() does not exist.',
511  5,
512  $tokenName
513  );
514  }
515 
516  if (0 == $this->_currentState &&
517  'EOF' === $this->getNextToken()) {
518  $name = count($this->_createdRules) + 1;
519  $this->_createdRules[$name] = new Concatenation(
520  $name,
521  [$tokenName],
522  null
523  );
524  } else {
525  $name = $tokenName;
526  }
527 
528  $this->consumeToken();
529 
530  return $name;
531  }
532 
533  return null;
534  }
535 
542  public function getCurrentToken($kind = 'token')
543  {
544  return $this->_tokenSequence[$this->_currentState][$kind];
545  }
546 
553  public function getNextToken($kind = 'token')
554  {
555  return $this->_tokenSequence[$this->_currentState + 1][$kind];
556  }
557 
563  public function consumeToken()
564  {
565  return ++$this->_currentState;
566  }
567 }
getNextToken($kind= 'token')
Definition: Analyzer.php:553
$content
Definition: Hoa.php:119
getCurrentToken($kind= 'token')
Definition: Analyzer.php:542
__construct(Array $tokens)
Definition: Analyzer.php:94