/**
  * Creates a new collection of rules.
  *
  * You must provide a list of rules, the following formats are accepted:
  *
  * #### Rules as arrays
  *
  * ``php
  * [
  *     'S -> a A' => function (&$info) { },
  *     'A -> b B' => function (&$info) { },
  *     'B -> c', // semantic routine is optional
  *     ....
  * ]
  * ```
  *
  * #### Rules as a single string
  *
  * In this case no semantic routine is given for each rule
  *
  * ```php
  * [
  *     'S -> a A',
  *     'A -> b B',
  *     'C -> c',
  * ]
  * ```
  *
  * #### Rules as a rule objects
  *
  * ```php
  * [
  *     new Rule('S -> a A', function (&$info) { }),
  *     new Rule('A -> b B', function (&$info) { }),
  *     new Rule('B -> c'), // semantic routine is optional
  * ]
  * ```
  *
  * ---
  *
  * Of course you can combine formats:
  *
  * ```php
  * [
  *     'S -> a A' => function($info) { },
  *     'A -> b B',
  *     new Rule('B -> c'),
  * ]
  * ```
  *
  * @param array $collection List of rules given as an array
  * @return void
  */
 public function __construct(array $collection = [])
 {
     if (empty($collection)) {
         return;
     }
     $i = 0;
     foreach ($collection as $index => $rule) {
         if (!$rule instanceof Rule) {
             if (is_string($index) && is_callable($rule)) {
                 // 'S -> a' => function()
                 $left = $index;
                 $right = $rule;
             } elseif (is_integer($index) && is_string($rule)) {
                 // 0 => 'S -> a'
                 $left = $rule;
                 $right = '';
             }
             if (isset($left) && isset($right)) {
                 $rule = new Rule($left, $right);
                 unset($left, $right);
             }
         }
         if (!$rule instanceof Rule) {
             continue;
         }
         array_unshift($this->_variables, $rule->lhs());
         if ($i == 0) {
             $this->_startVariable = $rule->lhs();
         }
         $this->_rules[] = $rule;
         $i++;
     }
     $this->_variables = array_unique($this->_variables);
     foreach ($this->_rules as $rule) {
         $rhs = $rule->rhs();
         $rhs = explode(' ', $rhs);
         $rhs = array_diff($rhs, $this->_variables);
         $this->_terminals = array_merge($this->_terminals, $rhs);
     }
     $this->_terminals = array_unique($this->_terminals);
 }
 /**
  * Calculates the closure set for the given set of rules
  *
  * @param \Phparser\Rule\RulesCollection $set Set of rules
  * @return array
  */
 protected function _closure(RulesCollection $set)
 {
     $regex = implode('|', $this->variables());
     $first = $this->first();
     $hasChanged = true;
     while ($hasChanged) {
         $hasChanged = false;
         foreach ($set as $rule) {
             $rhs = $rule->rhs();
             $result = preg_match('/\\.\\b(' . $regex . ')\\b/', $rhs, $matches);
             if ($result) {
                 $rightSymbols = end(explode($matches[1], $rhs));
                 $variable = str_replace('.', '', $matches[1]);
                 foreach ($this->_rules as $r) {
                     $lhs = $r->lhs();
                     if ($lhs == $variable) {
                         $seq = trim($rightSymbols . ' ' . $rule->lookahead());
                         foreach ($this->_first($first, $seq) as $lookahead) {
                             $newRHS = '.' . $r->rhs();
                             $newPro = new Rule("{$variable} -> {$newRHS}");
                             $newPro->lookahead($lookahead);
                             if ($set->push($newPro)) {
                                 $hasChanged = true;
                             }
                         }
                     }
                 }
             }
         }
     }
     return $set;
 }