/** * Sets up the finite state machine according to rules for the Lucene query * syntax. * * This part of the algorithm is derived from the Zend Framework module, * Zend_Search_Lucene. Some modifications have been made. * * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ private function setupFiniteStateMachine() { $this->fsm = new xfFiniteStateMachine(); // These are our possible states. $this->fsm->addStates(array(self::ST_WHITE, self::ST_SYNTAX, self::ST_LEXEME, self::ST_QUOTED, self::ST_ESCAPED_CHAR, self::ST_ESCAPED_QCHAR, self::ST_MODIFIER, self::ST_NUMBER, self::ST_MANTISSA, self::ST_ERROR)); // Start at this state. $this->fsm->setInitialState(self::ST_WHITE); // These are the possible transitions. $this->fsm->addTransitions(array(array(self::ST_WHITE, self::IN_WHITE, self::ST_WHITE), array(self::ST_WHITE, self::IN_SYNTAX, self::ST_SYNTAX), array(self::ST_WHITE, self::IN_MODIFIER, self::ST_MODIFIER), array(self::ST_WHITE, self::IN_ESCAPE_CHAR, self::ST_ESCAPED_CHAR), array(self::ST_WHITE, self::IN_QUOTE, self::ST_QUOTED), array(self::ST_WHITE, self::IN_DECIMAL, self::ST_LEXEME), array(self::ST_WHITE, self::IN_NUMBER, self::ST_LEXEME), array(self::ST_WHITE, self::IN_CHAR, self::ST_LEXEME), array(self::ST_WHITE, self::IN_MUTABLE, self::ST_SYNTAX), array(self::ST_SYNTAX, self::IN_WHITE, self::ST_WHITE), array(self::ST_SYNTAX, self::IN_SYNTAX, self::ST_SYNTAX), array(self::ST_SYNTAX, self::IN_MODIFIER, self::ST_MODIFIER), array(self::ST_SYNTAX, self::IN_ESCAPE_CHAR, self::ST_ESCAPED_CHAR), array(self::ST_SYNTAX, self::IN_QUOTE, self::ST_QUOTED), array(self::ST_SYNTAX, self::IN_DECIMAL, self::ST_LEXEME), array(self::ST_SYNTAX, self::IN_NUMBER, self::ST_LEXEME), array(self::ST_SYNTAX, self::IN_CHAR, self::ST_LEXEME), array(self::ST_SYNTAX, self::IN_MUTABLE, self::ST_SYNTAX), array(self::ST_LEXEME, self::IN_WHITE, self::ST_WHITE), array(self::ST_LEXEME, self::IN_SYNTAX, self::ST_SYNTAX), array(self::ST_LEXEME, self::IN_MODIFIER, self::ST_MODIFIER), array(self::ST_LEXEME, self::IN_ESCAPE_CHAR, self::ST_ESCAPED_CHAR), array(self::ST_LEXEME, self::IN_QUOTE, self::ST_ERROR), array(self::ST_LEXEME, self::IN_DECIMAL, self::ST_LEXEME), array(self::ST_LEXEME, self::IN_NUMBER, self::ST_LEXEME), array(self::ST_LEXEME, self::IN_CHAR, self::ST_LEXEME), array(self::ST_LEXEME, self::IN_MUTABLE, self::ST_LEXEME), array(self::ST_QUOTED, self::IN_WHITE, self::ST_QUOTED), array(self::ST_QUOTED, self::IN_SYNTAX, self::ST_QUOTED), array(self::ST_QUOTED, self::IN_MODIFIER, self::ST_QUOTED), array(self::ST_QUOTED, self::IN_ESCAPE_CHAR, self::ST_ESCAPED_QCHAR), array(self::ST_QUOTED, self::IN_QUOTE, self::ST_WHITE), array(self::ST_QUOTED, self::IN_DECIMAL, self::ST_QUOTED), array(self::ST_QUOTED, self::IN_NUMBER, self::ST_QUOTED), array(self::ST_QUOTED, self::IN_CHAR, self::ST_QUOTED), array(self::ST_QUOTED, self::IN_MUTABLE, self::ST_QUOTED), array(self::ST_ESCAPED_CHAR, self::IN_WHITE, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_SYNTAX, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_MODIFIER, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_ESCAPE_CHAR, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_QUOTE, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_DECIMAL, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_NUMBER, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_CHAR, self::ST_LEXEME), array(self::ST_ESCAPED_CHAR, self::IN_MUTABLE, self::ST_LEXEME), array(self::ST_ESCAPED_QCHAR, self::IN_WHITE, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_SYNTAX, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_MODIFIER, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_ESCAPE_CHAR, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_QUOTE, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_DECIMAL, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_NUMBER, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_CHAR, self::ST_QUOTED), array(self::ST_ESCAPED_QCHAR, self::IN_MUTABLE, self::ST_QUOTED), array(self::ST_MODIFIER, self::IN_WHITE, self::ST_WHITE), array(self::ST_MODIFIER, self::IN_SYNTAX, self::ST_SYNTAX), array(self::ST_MODIFIER, self::IN_MODIFIER, self::ST_MODIFIER), array(self::ST_MODIFIER, self::IN_ESCAPE_CHAR, self::ST_ERROR), array(self::ST_MODIFIER, self::IN_QUOTE, self::ST_ERROR), array(self::ST_MODIFIER, self::IN_DECIMAL, self::ST_MANTISSA), array(self::ST_MODIFIER, self::IN_NUMBER, self::ST_NUMBER), array(self::ST_MODIFIER, self::IN_CHAR, self::ST_ERROR), array(self::ST_MODIFIER, self::IN_MUTABLE, self::ST_SYNTAX), array(self::ST_NUMBER, self::IN_WHITE, self::ST_WHITE), array(self::ST_NUMBER, self::IN_SYNTAX, self::ST_SYNTAX), array(self::ST_NUMBER, self::IN_MODIFIER, self::ST_MODIFIER), array(self::ST_NUMBER, self::IN_ESCAPE_CHAR, self::ST_ERROR), array(self::ST_NUMBER, self::IN_QUOTE, self::ST_ERROR), array(self::ST_NUMBER, self::IN_DECIMAL, self::ST_MANTISSA), array(self::ST_NUMBER, self::IN_NUMBER, self::ST_NUMBER), array(self::ST_NUMBER, self::IN_CHAR, self::ST_ERROR), array(self::ST_NUMBER, self::IN_MUTABLE, self::ST_SYNTAX), array(self::ST_MANTISSA, self::IN_WHITE, self::ST_WHITE), array(self::ST_MANTISSA, self::IN_SYNTAX, self::ST_SYNTAX), array(self::ST_MANTISSA, self::IN_MODIFIER, self::ST_MODIFIER), array(self::ST_MANTISSA, self::IN_ESCAPE_CHAR, self::ST_ERROR), array(self::ST_MANTISSA, self::IN_QUOTE, self::ST_ERROR), array(self::ST_MANTISSA, self::IN_DECIMAL, self::ST_ERROR), array(self::ST_MANTISSA, self::IN_NUMBER, self::ST_MANTISSA), array(self::ST_MANTISSA, self::IN_CHAR, self::ST_ERROR), array(self::ST_MANTISSA, self::IN_MUTABLE, self::ST_SYNTAX))); // Actions for when an error occurs. $quoteWithinError = new xfParserFSMError('A quote within a lexeme is not allowed.'); $modifierError = new xfParserFSMError('Only a number, white space, or syntax can follow a modifier.'); $wrongNumberError = new xfParserFSMError('Invalid number syntax: make sure the number only has one decimal point and contains only digits.'); // Action for adding lexemes. $addChar = new xfLexemeBuilderAddChar($this->builder); $addLexeme = new xfLexemeBuilderAddLexeme($this->builder, xfLexemeLucene::WORD); $addModifier = new xfLexemeBuilderAddLexeme($this->builder, xfLexemeLucene::SYNTAX); $addQuoted = new xfLexemeBuilderAddLexeme($this->builder, xfLexemeLucene::PHRASE); $addNumber = new xfLexemeBuilderAddLexeme($this->builder, xfLexemeLucene::NUMBER); $addSyntax = new xfLexemeBuilderLuceneAddSyntax($this->builder); // Bind actions for when an error occurs. $this->fsm->addInputAction(self::ST_LEXEME, self::IN_QUOTE, $quoteWithinError); $this->fsm->addInputAction(self::ST_MODIFIER, self::IN_ESCAPE_CHAR, $modifierError); $this->fsm->addInputAction(self::ST_MODIFIER, self::IN_QUOTE, $modifierError); $this->fsm->addInputAction(self::ST_MODIFIER, self::IN_CHAR, $modifierError); $this->fsm->addInputAction(self::ST_NUMBER, self::IN_ESCAPE_CHAR, $wrongNumberError); $this->fsm->addInputAction(self::ST_NUMBER, self::IN_QUOTE, $wrongNumberError); $this->fsm->addInputAction(self::ST_NUMBER, self::IN_CHAR, $wrongNumberError); $this->fsm->addInputAction(self::ST_MANTISSA, self::IN_ESCAPE_CHAR, $wrongNumberError); $this->fsm->addInputAction(self::ST_MANTISSA, self::IN_QUOTE, $wrongNumberError); $this->fsm->addInputAction(self::ST_MANTISSA, self::IN_DECIMAL, $wrongNumberError); $this->fsm->addInputAction(self::ST_MANTISSA, self::IN_CHAR, $wrongNumberError); // Bind actions for adding a character. $this->fsm->addEntryAction(self::ST_LEXEME, $addChar); $this->fsm->addTransitionAction(self::ST_LEXEME, self::ST_LEXEME, $addChar); $this->fsm->addTransitionAction(self::ST_QUOTED, self::ST_QUOTED, $addChar); $this->fsm->addTransitionAction(self::ST_ESCAPED_QCHAR, self::ST_QUOTED, $addChar); $this->fsm->addEntryAction(self::ST_NUMBER, $addChar); $this->fsm->addEntryAction(self::ST_MANTISSA, $addChar); $this->fsm->addTransitionAction(self::ST_NUMBER, self::ST_NUMBER, $addChar); $this->fsm->addTransitionAction(self::ST_MANTISSA, self::ST_MANTISSA, $addChar); // Bind actions for adding a lexeme. $this->fsm->addTransitionAction(self::ST_LEXEME, self::ST_WHITE, $addLexeme); $this->fsm->addTransitionAction(self::ST_LEXEME, self::ST_SYNTAX, $addLexeme); $this->fsm->addTransitionAction(self::ST_LEXEME, self::ST_QUOTED, $addLexeme); $this->fsm->addTransitionAction(self::ST_LEXEME, self::ST_MODIFIER, $addLexeme); $this->fsm->addTransitionAction(self::ST_LEXEME, self::ST_NUMBER, $addLexeme); $this->fsm->addTransitionAction(self::ST_LEXEME, self::ST_MANTISSA, $addLexeme); // Bind actions for adding a quoted lexeme. $this->fsm->addTransitionAction(self::ST_QUOTED, self::ST_WHITE, $addQuoted); // Bind actions for adding a number. $this->fsm->addTransitionAction(self::ST_NUMBER, self::ST_WHITE, $addNumber); $this->fsm->addTransitionAction(self::ST_NUMBER, self::ST_SYNTAX, $addNumber); $this->fsm->addTransitionAction(self::ST_NUMBER, self::ST_MODIFIER, $addNumber); $this->fsm->addTransitionAction(self::ST_MANTISSA, self::ST_WHITE, $addNumber); $this->fsm->addTransitionAction(self::ST_MANTISSA, self::ST_SYNTAX, $addNumber); $this->fsm->addTransitionAction(self::ST_MANTISSA, self::ST_MODIFIER, $addNumber); // Bind actions for adding a modifier $this->fsm->addEntryAction(self::ST_MODIFIER, $addChar); $this->fsm->addEntryAction(self::ST_MODIFIER, $addModifier); // Bind actions for adding a syntax lexeme. $this->fsm->addEntryAction(self::ST_SYNTAX, $addSyntax); $this->fsm->addTransitionAction(self::ST_SYNTAX, self::ST_SYNTAX, $addSyntax); }
$fsm->reset(); $t->is($fsm->getState(), 'off', '->reset() resets the state'); $t->diag('exit actions'); $fsm->reset(); $exit = new CounterAction(); $fsm->addExitAction('off', $exit); $fsm->process('push'); $t->is($exit->counter, 1, '->process() calls an exit action when leaving a state'); $fsm->process('push'); $t->is($exit->counter, 1, '->process() does not call an exit action when not leaving the state'); $fsm->process('wait'); $t->is($exit->counter, 1, '->process() does not call an exit action when the state does not change'); $t->diag('entry actions'); $fsm->reset(); $enter = new CounterAction(); $fsm->addEntryAction('broken', $enter); $fsm->process('smash'); $t->is($enter->counter, 1, '->process() calls an entry action when entering the state'); $fsm->process('wait'); $t->is($enter->counter, 1, '->process() does not call an entry action when the state does not change'); $fsm->process('replace'); $t->is($enter->counter, 1, '->process() does not call an entry action when leaving the state'); $t->diag('transition actions'); $fsm->reset(); $transition = new CounterAction(); $fsm->addTransitionAction('off', 'on', $transition); $fsm->process('push'); $t->is($transition->counter, 1, '->process() calls transition actions when matching'); $fsm->process('push'); $t->is($transition->counter, 1, '->process() does not call transition actions when they do not match'); $t->diag('input actions');