/**
  * @param SymbolSet $set
  * @param NormalizedProduction $production
  *
  * @return bool
  */
 public function processProduction(SymbolSet $set, NormalizedProduction $production)
 {
     $nonTerminal = $production->getLeftHandSide();
     if ($set->contains($nonTerminal)) {
         return false;
     }
     //directly generates epsilon (rhs is epsilon)
     if ($this->directlyGeneratesEpsilon($production)) {
         $set->add($nonTerminal);
         return true;
     }
     //indirectly generates epsilon (all from rhs must generate epsilon)
     if ($this->indirectlyGeneratesEpsilon($production, $set)) {
         $set->add($nonTerminal);
         return true;
     }
     return false;
 }
 /**
  * Calculates the first set of $list and adds it's elements to $set
  *
  * @param \Helstern\Nomsky\Grammar\Symbol\SymbolSet $newFirstSet
  * @param array|NormalizedProduction[] $list
  * @param \Helstern\Nomsky\GrammarAnalysis\ParseSets\ParseSets $firstSets
  *
  * @return bool if the epsilon symbol was added to $set
  */
 public function processSymbolList(SymbolSet $newFirstSet, array $list, ParseSets $firstSets)
 {
     if (0 == count($list)) {
         $newFirstSet->add(new EpsilonSymbol());
         return true;
     }
     if (1 == count($list)) {
         /** @var Symbol $symbol */
         $symbol = $list[0];
         return $this->processSymbol($newFirstSet, $symbol, $firstSets);
     }
     $symbolIsEpsilon = SymbolIsEpsilon::singletonInstance();
     $symbolsIsNonTerminal = SymbolTypeEquals::newInstanceMatchingNonTerminals();
     /** @var SymbolSet $lastSet */
     $lastSet = null;
     $epsilonCounter = new MatchCountingInterceptor($symbolIsEpsilon);
     $acceptor = Inverter::newInstance($epsilonCounter);
     //we assume there is no epsilon symbol in the $list list
     $previousSymbol = reset($list);
     //add the first non-terminal and continue past this one later on
     if ($symbolsIsNonTerminal->matchSymbol($previousSymbol)) {
         $lastSet = $firstSets->filterTerminalSet($previousSymbol, $acceptor);
         $newFirstSet->addAll($lastSet);
     } else {
         $newFirstSet->add($previousSymbol);
     }
     //as long as the previous symbol was a non-terminal, process the next symbol
     for (next($list); !is_null(key($list)) && $symbolsIsNonTerminal->matchSymbol($previousSymbol) && $epsilonCounter->getMatchCount() > 0; next($list)) {
         $previousSymbol = current($list);
         $epsilonCounter = new MatchCountingInterceptor($symbolIsEpsilon);
         $acceptor = Inverter::newInstance($epsilonCounter);
         if ($symbolsIsNonTerminal->matchSymbol($previousSymbol)) {
             $lastSet = $firstSets->filterTerminalSet($previousSymbol, $acceptor);
             $newFirstSet->addAll($lastSet);
         } else {
             $newFirstSet->add($previousSymbol);
         }
     }
     if (is_null(key($list)) && $symbolsIsNonTerminal->matchSymbol($previousSymbol) && $epsilonCounter->getMatchCount() > 0) {
         $newFirstSet->add(new EpsilonSymbol());
         return true;
     }
     return false;
 }