/** * @param Rule $rule */ public function addRule(Rule $rule) { if (array_key_exists($rule->getTokenName(), $this->rules)) { $this->rules[$rule->getTokenName()]->addPatterns($rule->getPatterns()); } else { $this->rules[$rule->getTokenName()] = $rule; } }
/** * @inheritdoc */ public function getRules() { $data = $this->data; $rules = new Rules(); foreach ($data->rule as $ruleData) { $rule = new Rule((string) $ruleData->token); $patternIdx = 0; foreach ($ruleData->patterns->pattern as $patternData) { $patternName = "Pattern {$rule->getTokenName()}#{$patternIdx}"; $hasStartToken = false; $potentialStartTokenIdxs = []; $tokens = []; foreach ($patternData->token as $tokenData) { $tokenName = (string) $tokenData; $isStartToken = (bool) $tokenData['is_start_token']; $tokens[] = ['name' => $tokenName, 'is_start_token' => $isStartToken]; if ($isStartToken) { if ($tokenName !== $rule->getTokenName()) { throw new ConfigurationException("{$patternName}: Only {$rule->getTokenName()} tokens can have the 'is_start_token' attribute."); } if ($hasStartToken) { throw new ConfigurationException("{$patternName}: Multiple {$rule->getTokenName()} tokens with 'is_start_token' attribute found. Only one is allowed."); } else { $hasStartToken = true; } } if ($tokenName === $rule->getTokenName()) { $potentialStartTokenIdxs[] = count($tokens) - 1; } } if (!$hasStartToken) { if (count($potentialStartTokenIdxs) === 0) { throw new ConfigurationException("Pattern {$rule->getTokenName()}/#{$patternIdx} must have unambiguous start token. No {$rule->getTokenName()} token found."); } elseif (count($potentialStartTokenIdxs) > 1) { throw new ConfigurationException("Pattern {$rule->getTokenName()}/#{$patternIdx} must have unambiguous start token. Multiple {$rule->getTokenName()} tokens found."); } else { $potentialStartTokenIdx = $potentialStartTokenIdxs[0]; $tokens[$potentialStartTokenIdx]['is_start_token'] = true; } } $pattern = new RulePattern((int) $patternData['probability']); foreach ($tokens as $token) { $pattern->addToken(new RulePatternToken($token['name'], $token['is_start_token'])); } $rule->addPattern($pattern); $patternIdx++; } $rules->addRule($rule); } return $rules; }
/** * Parse current rule. * * @param \Hoa\Compiler\Llk\Rule $zeRule Current rule. * @param int $next Next rule index. * @return bool */ protected function _parse(Rule $zeRule, $next) { if ($zeRule instanceof Rule\Token) { $name = $this->getCurrentToken(); if ($zeRule->getTokenName() !== $name) { return false; } $value = $this->getCurrentToken('value'); if (0 <= ($unification = $zeRule->getUnificationIndex())) { for ($skip = 0, $i = count($this->_trace) - 1; $i >= 0; --$i) { $trace = $this->_trace[$i]; if ($trace instanceof Rule\Entry) { if (false === $trace->isTransitional()) { if ($trace->getDepth() <= $this->_depth) { break; } --$skip; } } elseif ($trace instanceof Rule\Ekzit && false === $trace->isTransitional()) { $skip += $trace->getDepth() > $this->_depth; } if (0 < $skip) { continue; } if ($trace instanceof Rule\Token && $unification === $trace->getUnificationIndex() && $value !== $trace->getValue()) { return false; } } } $namespace = $this->getCurrentToken('namespace'); $zzeRule = clone $zeRule; $zzeRule->setValue($value); $zzeRule->setNamespace($namespace); if (isset($this->_tokens[$namespace][$name])) { $zzeRule->setRepresentation($this->_tokens[$namespace][$name]); } else { foreach ($this->_tokens[$namespace] as $_name => $regex) { if (false === ($pos = strpos($_name, ':'))) { continue; } $_name = substr($_name, 0, $pos); if ($_name === $name) { break; } } $zzeRule->setRepresentation($regex); } array_pop($this->_todo); $this->_trace[] = $zzeRule; $this->_errorState = ++$this->_currentState; return true; } elseif ($zeRule instanceof Rule\Concatenation) { if (false === $zeRule->isTransitional()) { ++$this->_depth; } $this->_trace[] = new Rule\Entry($zeRule->getName(), 0, null, $this->_depth); $content = $zeRule->getContent(); for ($i = count($content) - 1; $i >= 0; --$i) { $nextRule = $content[$i]; $this->_todo[] = new Rule\Ekzit($nextRule, 0); $this->_todo[] = new Rule\Entry($nextRule, 0); } return true; } elseif ($zeRule instanceof Rule\Choice) { $content = $zeRule->getContent(); if ($next >= count($content)) { return false; } if (false === $zeRule->isTransitional()) { ++$this->_depth; } $this->_trace[] = new Rule\Entry($zeRule->getName(), $next, $this->_todo, $this->_depth); $nextRule = $content[$next]; $this->_todo[] = new Rule\Ekzit($nextRule, 0); $this->_todo[] = new Rule\Entry($nextRule, 0); return true; } elseif ($zeRule instanceof Rule\Repetition) { $nextRule = $zeRule->getContent(); if (0 === $next) { $name = $zeRule->getName(); $min = $zeRule->getMin(); if (false === $zeRule->isTransitional()) { ++$this->_depth; } $this->_trace[] = new Rule\Entry($name, $min, null, $this->_depth); array_pop($this->_todo); $this->_todo[] = new Rule\Ekzit($name, $min, $this->_todo); for ($i = 0; $i < $min; ++$i) { $this->_todo[] = new Rule\Ekzit($nextRule, 0); $this->_todo[] = new Rule\Entry($nextRule, 0); } return true; } else { $max = $zeRule->getMax(); if (-1 != $max && $next > $max) { return false; } $this->_todo[] = new Rule\Ekzit($zeRule->getName(), $next, $this->_todo); $this->_todo[] = new Rule\Ekzit($nextRule, 0); $this->_todo[] = new Rule\Entry($nextRule, 0); return true; } } return false; }