/**
  * 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);
 }