/** * This method runs the machine using the specified sigma (i.e. the input alphabet/sequence). * * @access public * @param \Unicity\Common\IList $sigma the sigma to be processed * @param \Unicity\Common\Mutable\IList $path the path through which the pattern * was found * @return boolean whether the machine finished in * a goal state * @throws \Unicity\Throwable\InvalidArgument\Exception indicates that no sigma has been * specified * @throws \Unicity\Throwable\Parse\Exception indicates that the machine failed * to parse */ public function run(Common\IList $sigma, Common\Mutable\IList $path = null) { if ($sigma === null || $sigma->isEmpty()) { throw new Throwable\InvalidArgument\Exception('No sigma has been defined.'); } $count = $sigma->count(); if ($this->initials === null || $this->initials->isEmpty()) { throw new Throwable\Parse\Exception('Machine failed. No initial state has been defined.'); } $stack = new Common\Mutable\Stack($path); $states = $this->states->getValues($this->initials); usort($states, function (Core\IComparable $c0, Core\IComparable $c1) { return $c0->compareTo($c1); }); $state = $states[0]; $stack->push($state->getId()); $this->onStart($this, $state); for ($i = 0; $i < $count; $i++) { $transitions = $this->transitions->getValues($state->getTransitions()); usort($transitions, function (Core\IComparable $c0, Core\IComparable $c1) { return $c0->compareTo($c1); }); $hasTransitioned = false; foreach ($transitions as $transition) { if ($transition->isTraversable($sigma, $i)) { $this->onExit($this, $state); $this->onTransition($this, $transition); $targets = $this->states->getValues($transition->getTargets()); usort($targets, function (Core\IComparable $c0, Core\IComparable $c1) { return $c0->compareTo($c1); }); $state = $targets[0]; $stack->push($state->getId()); $this->onEntry($this, $state); $hasTransitioned = true; break; } } if (!$hasTransitioned) { $stack->clear(); throw new Throwable\Parse\Exception('Machine failed. Unable to transition between states.'); } } $this->onCompletion($this, $state); if (Automaton\StateType::goal()->__equals($state->getType())) { return true; } $stack->clear(); return false; }
/** * This method recursively traverses the machine. * * @access protected * @param \Unicity\Common\IList $sigma the sigma to be processed * @param integer $i the index to the input symbol * @param \Unicity\Automaton\IState $state the current a set of target states * @param \Unicity\Common\Mutable\Stack $stack the path through which the pattern * was found * @return boolean whether the machine finished in * a goal state * a goal state * @throws \Unicity\Throwable\Parse\Exception indicates that the machine failed * to parse */ protected function traverse(Common\IList $sigma, $i, Automaton\IState $state, Common\Mutable\Stack $stack) { if ($i >= $sigma->count()) { return Automaton\StateType::goal()->__equals($state->getType()); } $transitions = $this->transitions->getValues($state->getTransitions()); usort($transitions, function (Core\IComparable $c0, Core\IComparable $c1) { return $c0->compareTo($c1); }); $hasTransitioned = false; foreach ($transitions as $transition) { if ($transition->isTraversable($sigma, $i)) { $targets = $this->states->getValues($transition->getTargets()); usort($targets, function (Core\IComparable $c0, Core\IComparable $c1) { return $c0->compareTo($c1); }); foreach ($targets as $target) { $stack->push($target->getId()); if ($this->traverse($sigma, $i + 1, $target, $stack)) { return true; } $stack->pop(); } $hasTransitioned = true; break; } } if (!$hasTransitioned) { throw new Throwable\Parse\Exception('Machine failed. Unable to transition between states.'); } return false; }
/** * This method logs any changes between the source and target objects. * * @access protected * @param Common\IList $source the source object to be evaluated * @param Common\IList $target the target object to be evaluated * @param string $path the current path * @param Common\Mutable\IList $log a reference to the log */ protected function compareLists(Common\IList $source, Common\IList $target, $path, Common\Mutable\IList $log) { foreach ($source as $index => $source_value) { $new_path = static::buildPath($path, $index); if ($this->doLog($new_path)) { if ($target->hasIndex($index)) { $target_value = $target->getValue($index); if ($source_value instanceof Common\IList && $target_value instanceof Common\IList) { $this->compareLists($source_value, $target_value, $new_path, $log); } else { if ($source_value instanceof Common\IMap && $target_value instanceof Common\IMap) { $this->compareMaps($source_value, $target_value, $new_path, $log); } else { $this->compareValues($source_value, $target_value, $new_path, $log); } } } else { $log->addValue(array('body' => strtr('Target index ":index" is missing in list.', array(':index' => $index)), 'level' => Log\Level::warning()->__name(), 'path' => $new_path, 'time' => date('c'))); } } } }
/** * This method removes an action from the transition. * * @access public * @param \Unicity\Automaton\IAction $action the action to be removed */ public function removeAction(Automaton\IAction $action) { if ($action !== null) { $this->actions->removeValue($action); } }