text:
<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Search_Lucene
 * @subpackage Search
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: BooleanExpressionRecognizer.php 23775 2011-03-01 17:25:24Z ralph $
 */

/** Zend_Search_Lucene_FSM */
require_once 'Zend/Search/Lucene/FSM.php';

/**
 * @category   Zend
 * @package    Zend_Search_Lucene
 * @subpackage Search
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Search_Lucene_Search_BooleanExpressionRecognizer extends Zend_Search_Lucene_FSM
{
    /** State Machine states */
    const ST_START           = 0;
    const ST_LITERAL         = 1;
    const ST_NOT_OPERATOR    = 2;
    const ST_AND_OPERATOR    = 3;
    const ST_OR_OPERATOR     = 4;

    /** Input symbols */
    const IN_LITERAL         = 0;
    const IN_NOT_OPERATOR    = 1;
    const IN_AND_OPERATOR    = 2;
    const IN_OR_OPERATOR     = 3;

    /**
     * NOT operator signal
     *
     * @var boolean
     */
    private $_negativeLiteral = false;

    /**
     * Current literal
     *
     * @var mixed
     */
    private $_literal;

    /**
     * Set of boolean query conjunctions
     *
     * Each conjunction is an array of conjunction elements
     * Each conjunction element is presented with two-elements array:
     * array(<literal>, <is_negative>)
     *
     * So, it has a structure:
     * array( array( array(<literal>, <is_negative>), // first literal of first conjuction
     *               array(<literal>, <is_negative>), // second literal of first conjuction
     *               ...
     *               array(<literal>, <is_negative>)
     *             ), // end of first conjuction
     *        array( array(<literal>, <is_negative>), // first literal of second conjuction
     *               array(<literal>, <is_negative>), // second literal of second conjuction
     *               ...
     *               array(<literal>, <is_negative>)
     *             ), // end of second conjuction
     *        ...
     *      ) // end of structure
     *
     * @var array
     */
    private $_conjunctions = array();

    /**
     * Current conjuction
     *
     * @var array
     */
    private $_currentConjunction = array();

    /**
     * Object constructor
     */
    public function __construct()
    {
        parent::__construct( array(self::ST_START,
                                   self::ST_LITERAL,
                                   self::ST_NOT_OPERATOR,
                                   self::ST_AND_OPERATOR,
                                   self::ST_OR_OPERATOR),
                             array(self::IN_LITERAL,
                                   self::IN_NOT_OPERATOR,
                                   self::IN_AND_OPERATOR,
                                   self::IN_OR_OPERATOR));

        $emptyOperatorAction    = new Zend_Search_Lucene_FSMAction($this, 'emptyOperatorAction');
        $emptyNotOperatorAction = new Zend_Search_Lucene_FSMAction($this, 'emptyNotOperatorAction');

        $this->addRules(array( array(self::ST_START,        self::IN_LITERAL,        self::ST_LITERAL),
                               array(self::ST_START,        self::IN_NOT_OPERATOR,   self::ST_NOT_OPERATOR),

                               array(self::ST_LITERAL,      self::IN_AND_OPERATOR,   self::ST_AND_OPERATOR),
                               array(self::ST_LITERAL,      self::IN_OR_OPERATOR,    self::ST_OR_OPERATOR),
                               array(self::ST_LITERAL,      self::IN_LITERAL,        self::ST_LITERAL,      $emptyOperatorAction),
                               array(self::ST_LITERAL,      self::IN_NOT_OPERATOR,   self::ST_NOT_OPERATOR, $emptyNotOperatorAction),

                               array(self::ST_NOT_OPERATOR, self::IN_LITERAL,        self::ST_LITERAL),

                               array(self::ST_AND_OPERATOR, self::IN_LITERAL,        self::ST_LITERAL),
                               array(self::ST_AND_OPERATOR, self::IN_NOT_OPERATOR,   self::ST_NOT_OPERATOR),

                               array(self::ST_OR_OPERATOR,  self::IN_LITERAL,        self::ST_LITERAL),
                               array(self::ST_OR_OPERATOR,  self::IN_NOT_OPERATOR,   self::ST_NOT_OPERATOR),
                             ));

        $notOperatorAction     = new Zend_Search_Lucene_FSMAction($this, 'notOperatorAction');
        $orOperatorAction      = new Zend_Search_Lucene_FSMAction($this, 'orOperatorAction');
        $literalAction         = new Zend_Search_Lucene_FSMAction($this, 'literalAction');

        $this->addEntryAction(self::ST_NOT_OPERATOR, $notOperatorAction);
        $this->addEntryAction(self::ST_OR_OPERATOR,  $orOperatorAction);
        $this->addEntryAction(self::ST_LITERAL,      $literalAction);
    }

    /**
     * Process next operator.
     *
     * Operators are defined by class constants: IN_AND_OPERATOR, IN_OR_OPERATOR and IN_NOT_OPERATOR
     *
     * @param integer $operator
     */
    public function processOperator($operator)
    {
        $this->process($operator);
    }

    /**
     * Process expression literal.
     *
     * @param integer $operator
     */
    public function processLiteral($literal)
    {
        $this->_literal = $literal;

        $this->process(self::IN_LITERAL);
    }

    /**
     * Finish an expression and return result
     *
     * Result is a set of boolean query conjunctions
     *
     * Each conjunction is an array of conjunction elements
     * Each conjunction element is presented with two-elements array:
     * array(<literal>, <is_negative>)
     *
     * So, it has a structure:
     * array( array( array(<literal>, <is_negative>), // first literal of first conjuction
     *               array(<literal>, <is_negative>), // second literal of first conjuction
     *               ...
     *               array(<literal>, <is_negative>)
     *             ), // end of first conjuction
     *        array( array(<literal>, <is_negative>), // first literal of second conjuction
     *               array(<literal>, <is_negative>), // second literal of second conjuction
     *               ...
     *               array(<literal>, <is_negative>)
     *             ), // end of second conjuction
     *        ...
     *      ) // end of structure
     *
     * @return array
     * @throws Zend_Search_Lucene_Exception
     */
    public function finishExpression()
    {
        if ($this->getState() != self::ST_LITERAL) {
            require_once 'Zend/Search/Lucene/Exception.php';
            throw new Zend_Search_Lucene_Exception('Literal expected.');
        }

        $this->_conjunctions[] = $this->_currentConjunction;

        return $this->_conjunctions;
    }

    /*********************************************************************
     * Actions implementation
     *********************************************************************/

    /**
     * default (omitted) operator processing
     */
    public function emptyOperatorAction()
    {
        /** Zend_Search_Lucene_Search_QueryParser */
        require_once 'Zend/Search/Lucene/Search/QueryParser.php';

        if (Zend_Search_Lucene_Search_QueryParser::getDefaultOperator() == Zend_Search_Lucene_Search_QueryParser::B_AND) {
            // Do nothing
        } else {
            $this->orOperatorAction();
        }

        // Process literal
        $this->literalAction();
    }

    /**
     * default (omitted) + NOT operator processing
     */
    public function emptyNotOperatorAction()
    {
        /** Zend_Search_Lucene_Search_QueryParser */
        require_once 'Zend/Search/Lucene/Search/QueryParser.php';

        if (Zend_Search_Lucene_Search_QueryParser::getDefaultOperator() == Zend_Search_Lucene_Search_QueryParser::B_AND) {
            // Do nothing
        } else {
            $this->orOperatorAction();
        }

        // Process NOT operator
        $this->notOperatorAction();
    }

    /**
     * NOT operator processing
     */
    public function notOperatorAction()
    {
        $this->_negativeLiteral = true;
    }

    /**
     * OR operator processing
     * Close current conjunction
     */
    public function orOperatorAction()
    {
        $this->_conjunctions[]     = $this->_currentConjunction;
        $this->_currentConjunction = array();
    }

    /**
     * Literal processing
     */
    public function literalAction()
    {
        // Add literal to the current conjunction
        $this->_currentConjunction[] = array($this->_literal, !$this->_negativeLiteral);

        // Switch off negative signal
        $this->_negativeLiteral = false;
    }
}


text:
<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Search_Lucene
 * @subpackage Search
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: QueryParserContext.php 23775 2011-03-01 17:25:24Z ralph $
 */

/** Zend_Search_Lucene_Search_QueryToken */
require_once 'Zend/Search/Lucene/Search/QueryToken.php';

/**
 * @category   Zend
 * @package    Zend_Search_Lucene
 * @subpackage Search
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Search_Lucene_Search_QueryParserContext
{
    /**
     * Default field for the context.
     *
     * null means, that term should be searched through all fields
     * Zend_Search_Lucene_Search_Query::rewriteQuery($index) transletes such queries to several
     *
     * @var string|null
     */
    private $_defaultField;

    /**
     * Field specified for next entry
     *
     * @var string
     */
    private $_nextEntryField = null;

    /**
     * True means, that term is required.
     * False means, that term is prohibited.
     * null means, that term is neither prohibited, nor required
     *
     * @var boolean
     */
    private $_nextEntrySign = null;

    /**
     * Entries grouping mode
     */
    const GM_SIGNS   = 0;  // Signs mode: '+term1 term2 -term3 +(subquery1) -(subquery2)'
    const GM_BOOLEAN = 1;  // Boolean operators mode: 'term1 and term2  or  (subquery1) and not (subquery2)'

    /**
     * Grouping mode
     *
     * @var integer
     */
    private $_mode = null;

    /**
     * Entries signs.
     * Used in GM_SIGNS grouping mode
     *
     * @var arrays
     */
    private $_signs = array();

    /**
     * Query entries
     * Each entry is a Zend_Search_Lucene_Search_QueryEntry object or
     * boolean operator (Zend_Search_Lucene_Search_QueryToken class constant)
     *
     * @var array
     */
    private $_entries = array();

    /**
     * Query string encoding
     *
     * @var string
     */
    private $_encoding;

    /**
     * Context object constructor
     *
     * @param string $encoding
     * @param string|null $defaultField
     */
    public function __construct($encoding, $defaultField = null)
    {
        $this->_encoding     = $encoding;
        $this->_defaultField = $defaultField;
    }

    /**
     * Get context default field
     *
     * @return string|null
     */
    public function getField()
    {
        return ($this->_nextEntryField !== null)  ?  $this->_nextEntryField : $this->_defaultField;
    }

    /**
     * Set field for next entry
     *
     * @param string $field
     */
    public function setNextEntryField($field)
    {
        $this->_nextEntryField = $field;
    }

    /**
     * Set sign for next entry
     *
     * @param integer $sign
     * @throws Zend_Search_Lucene_Exception
     */
    public function setNextEntrySign($sign)
    {
        if ($this->_mode === self::GM_BOOLEAN) {
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('It\'s not allowed to mix boolean and signs styles in the same subquery.');
        }

        $this->_mode = self::GM_SIGNS;

        if ($sign == Zend_Search_Lucene_Search_QueryToken::TT_REQUIRED) {
            $this->_nextEntrySign = true;
        } else if ($sign == Zend_Search_Lucene_Search_QueryToken::TT_PROHIBITED) {
            $this->_nextEntrySign = false;
        } else {
            require_once 'Zend/Search/Lucene/Exception.php';
            throw new Zend_Search_Lucene_Exception('Unrecognized sign type.');
        }
    }

    /**
     * Add entry to a query
     *
     * @param Zend_Search_Lucene_Search_QueryEntry $entry
     */
    public function addEntry(Zend_Search_Lucene_Search_QueryEntry $entry)
    {
        if ($this->_mode !== self::GM_BOOLEAN) {
            $this->_signs[] = $this->_nextEntrySign;
        }

        $this->_entries[] = $entry;

        $this->_nextEntryField = null;
        $this->_nextEntrySign  = null;
    }

    /**
     * Process fuzzy search or proximity search modifier
     *
     * @throws Zend_Search_Lucene_Search_QueryParserException
     */
    public function processFuzzyProximityModifier($parameter = null)
    {
        // Check, that modifier has came just after word or phrase
        if ($this->_nextEntryField !== null  ||  $this->_nextEntrySign !== null) {
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('\'~\' modifier must follow word or phrase.');
        }

        $lastEntry = array_pop($this->_entries);

        if (!$lastEntry instanceof Zend_Search_Lucene_Search_QueryEntry) {
            // there are no entries or last entry is boolean operator
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('\'~\' modifier must follow word or phrase.');
        }

        $lastEntry->processFuzzyProximityModifier($parameter);

        $this->_entries[] = $lastEntry;
    }

    /**
     * Set boost factor to the entry
     *
     * @param float $boostFactor
     */
    public function boost($boostFactor)
    {
        // Check, that modifier has came just after word or phrase
        if ($this->_nextEntryField !== null  ||  $this->_nextEntrySign !== null) {
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('\'^\' modifier must follow word, phrase or subquery.');
        }

        $lastEntry = array_pop($this->_entries);

        if (!$lastEntry instanceof Zend_Search_Lucene_Search_QueryEntry) {
            // there are no entries or last entry is boolean operator
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('\'^\' modifier must follow word, phrase or subquery.');
        }

        $lastEntry->boost($boostFactor);

        $this->_entries[] = $lastEntry;
    }

    /**
     * Process logical operator
     *
     * @param integer $operator
     */
    public function addLogicalOperator($operator)
    {
        if ($this->_mode === self::GM_SIGNS) {
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('It\'s not allowed to mix boolean and signs styles in the same subquery.');
        }

        $this->_mode = self::GM_BOOLEAN;

        $this->_entries[] = $operator;
    }

    /**
     * Generate 'signs style' query from the context
     * '+term1 term2 -term3 +(<subquery1>) ...'
     *
     * @return Zend_Search_Lucene_Search_Query
     */
    public function _signStyleExpressionQuery()
    {
        require_once 'Zend/Search/Lucene/Search/Query/Boolean.php';
        $query = new Zend_Search_Lucene_Search_Query_Boolean();

        require_once 'Zend/Search/Lucene/Search/QueryParser.php';
        if (Zend_Search_Lucene_Search_QueryParser::getDefaultOperator() == Zend_Search_Lucene_Search_QueryParser::B_AND) {
            $defaultSign = true; // required
        } else {
            // Zend_Search_Lucene_Search_QueryParser::B_OR
            $defaultSign = null; // optional
        }

        foreach ($this->_entries as $entryId => $entry) {
            $sign = ($this->_signs[$entryId] !== null) ?  $this->_signs[$entryId] : $defaultSign;
            $query->addSubquery($entry->getQuery($this->_encoding), $sign);
        }

        return $query;
    }

    /**
     * Generate 'boolean style' query from the context
     * 'term1 and term2   or   term3 and (<subquery1>) and not (<subquery2>)'
     *
     * @return Zend_Search_Lucene_Search_Query
     * @throws Zend_Search_Lucene
     */
    private function _booleanExpressionQuery()
    {
        /**
         * We treat each level of an expression as a boolean expression in
         * a Disjunctive Normal Form
         *
         * AND operator has higher precedence than OR
         *
         * Thus logical query is a disjunction of one or more conjunctions of
         * one or more query entries
         */

        require_once 'Zend/Search/Lucene/Search/BooleanExpressionRecognizer.php';
        $expressionRecognizer = new Zend_Search_Lucene_Search_BooleanExpressionRecognizer();

        require_once 'Zend/Search/Lucene/Exception.php';
        try {
            foreach ($this->_entries as $entry) {
                if ($entry instanceof Zend_Search_Lucene_Search_QueryEntry) {
                    $expressionRecognizer->processLiteral($entry);
                } else {
                    switch ($entry) {
                        case Zend_Search_Lucene_Search_QueryToken::TT_AND_LEXEME:
                            $expressionRecognizer->processOperator(Zend_Search_Lucene_Search_BooleanExpressionRecognizer::IN_AND_OPERATOR);
                            break;

                        case Zend_Search_Lucene_Search_QueryToken::TT_OR_LEXEME:
                            $expressionRecognizer->processOperator(Zend_Search_Lucene_Search_BooleanExpressionRecognizer::IN_OR_OPERATOR);
                            break;

                        case Zend_Search_Lucene_Search_QueryToken::TT_NOT_LEXEME:
                            $expressionRecognizer->processOperator(Zend_Search_Lucene_Search_BooleanExpressionRecognizer::IN_NOT_OPERATOR);
                            break;

                        default:
                            throw new Zend_Search_Lucene('Boolean expression error. Unknown operator type.');
                    }
                }
            }

            $conjuctions = $expressionRecognizer->finishExpression();
        } catch (Zend_Search_Exception $e) {
            // throw new Zend_Search_Lucene_Search_QueryParserException('Boolean expression error. Error message: \'' .
            //                                                          $e->getMessage() . '\'.' );
            // It's query syntax error message and it should be user friendly. So FSM message is omitted
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('Boolean expression error.', 0, $e);
        }

        // Remove 'only negative' conjunctions
        foreach ($conjuctions as $conjuctionId => $conjuction) {
            $nonNegativeEntryFound = false;

            foreach ($conjuction as $conjuctionEntry) {
                if ($conjuctionEntry[1]) {
                    $nonNegativeEntryFound = true;
                    break;
                }
            }

            if (!$nonNegativeEntryFound) {
                unset($conjuctions[$conjuctionId]);
            }
        }

        $subqueries = array();
        foreach ($conjuctions as  $conjuction) {
            // Check, if it's a one term conjuction
            if (count($conjuction) == 1) {
                $subqueries[] = $conjuction[0][0]->getQuery($this->_encoding);
            } else {
                require_once 'Zend/Search/Lucene/Search/Query/Boolean.php';
                $subquery = new Zend_Search_Lucene_Search_Query_Boolean();

                foreach ($conjuction as $conjuctionEntry) {
                    $subquery->addSubquery($conjuctionEntry[0]->getQuery($this->_encoding), $conjuctionEntry[1]);
                }

                $subqueries[] = $subquery;
            }
        }

        if (count($subqueries) == 0) {
            require_once 'Zend/Search/Lucene/Search/Query/Insignificant.php';
            return new Zend_Search_Lucene_Search_Query_Insignificant();
        }

        if (count($subqueries) == 1) {
            return $subqueries[0];
        }

        require_once 'Zend/Search/Lucene/Search/Query/Boolean.php';
        $query = new Zend_Search_Lucene_Search_Query_Boolean();

        foreach ($subqueries as $subquery) {
            // Non-requirered entry/subquery
            $query->addSubquery($subquery);
        }

        return $query;
    }

    /**
     * Generate query from current context
     *
     * @return Zend_Search_Lucene_Search_Query
     */
    public function getQuery()
    {
        if ($this->_mode === self::GM_BOOLEAN) {
            return $this->_booleanExpressionQuery();
        } else {
            return $this->_signStyleExpressionQuery();
        }
    }
}

repo_name: michael-romer/zf-boilerplate

repo_language: PHP

mime_type: text/html


text:
<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Search_Lucene
 * @subpackage Search
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Weight.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * Calculate query weights and build query scorers.
 *
 * A Weight is constructed by a query Query->createWeight().
 * The sumOfSquaredWeights() method is then called on the top-level
 * query to compute the query normalization factor Similarity->queryNorm(float).
 * This factor is then passed to normalize(float).  At this point the weighting
 * is complete.
 *
 * @category   Zend
 * @package    Zend_Search_Lucene
 * @subpackage Search
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
abstract class Zend_Search_Lucene_Search_Weight
{
    /**
     * Normalization factor.
     * This value is stored only for query expanation purpose and not used in any other place
     *
     * @var float
     */
    protected $_queryNorm;

    /**
     * Weight value
     *
     * Weight value may be initialized in sumOfSquaredWeights() or normalize()
     * because they both are invoked either in Query::_initWeight (for top-level query) or
     * in corresponding methods of parent query's weights
     *
     * @var float
     */
    protected $_value;

    /**
     * The weight for this query.
     *
     * @return float
     */
    public function getValue()
    {
        return $this->_value;
    }

    /**
     * The sum of squared weights of contained query clauses.
     *
     * @return float
     */
    abstract public function sumOfSquaredWeights();

    /**
     * Assigns the query normalization factor to this.
     *
     * @param float $norm
     */
    abstract public function normalize($norm);
}


text:
<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Search_Lucene
 * @subpackage Search
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: Similarity.php 23775 2011-03-01 17:25:24Z ralph $
 */

/**
 * @category   Zend
 * @package    Zend_Search_Lucene
 * @subpackage Search
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
abstract class Zend_Search_Lucene_Search_Similarity
{
    /**
     * The Similarity implementation used by default.
     *
     * @var Zend_Search_Lucene_Search_Similarity
     */
    private static $_defaultImpl;

    /**
     * Cache of decoded bytes.
     * Array of floats
     *
     * @var array
     */
    private static $_normTable = array( 0   => 0.0,
                                        1   => 5.820766E-10,
                                        2   => 6.9849193E-10,
                                        3   => 8.1490725E-10,
                                        4   => 9.313226E-10,
                                        5   => 1.1641532E-9,
                                        6   => 1.3969839E-9,
                                        7   => 1.6298145E-9,
                                        8   => 1.8626451E-9,
                                        9   => 2.3283064E-9,
                                        10  => 2.7939677E-9,
                                        11  => 3.259629E-9,
                                        12  => 3.7252903E-9,
                                        13  => 4.656613E-9,
                                        14  => 5.5879354E-9,
                                        15  => 6.519258E-9,
                                        16  => 7.4505806E-9,
                                        17  => 9.313226E-9,
                                        18  => 1.1175871E-8,
                                        19  => 1.3038516E-8,
                                        20  => 1.4901161E-8,
                                        21  => 1.8626451E-8,
                                        22  => 2.2351742E-8,
                                        23  => 2.6077032E-8,
                                        24  => 2.9802322E-8,
                                        25  => 3.7252903E-8,
                                        26  => 4.4703484E-8,
                                        27  => 5.2154064E-8,
                                        28  => 5.9604645E-8,
                                        29  => 7.4505806E-8,
                                        30  => 8.940697E-8,
                                        31  => 1.0430813E-7,
                                        32  => 1.1920929E-7,
                                        33  => 1.4901161E-7,
                                        34  => 1.7881393E-7,
                                        35  => 2.0861626E-7,
                                        36  => 2.3841858E-7,
                                        37  => 2.9802322E-7,
                                        38  => 3.5762787E-7,
                                        39  => 4.172325E-7,
                                        40  => 4.7683716E-7,
                                        41  => 5.9604645E-7,
                                        42  => 7.1525574E-7,
                                        43  => 8.34465E-7,
                                        44  => 9.536743E-7,
                                        45  => 1.1920929E-6,
                                        46  => 1.4305115E-6,
                                        47  => 1.66893E-6,
                                        48  => 1.9073486E-6,
                                        49  => 2.3841858E-6,
                                        50  => 2.861023E-6,
                                        51  => 3.33786E-6,
                                        52  => 3.8146973E-6,
                                        53  => 4.7683716E-6,
                                        54  => 5.722046E-6,
                                        55  => 6.67572E-6,
                                        56  => 7.6293945E-6,
                                        57  => 9.536743E-6,
                                        58  => 1.1444092E-5,
                                        59  => 1.335144E-5,
                                        60  => 1.5258789E-5,
                                        61  => 1.9073486E-5,
                                        62  => 2.2888184E-5,
                                        63  => 2.670288E-5,
                                        64  => 3.0517578E-5,
                                        65  => 3.8146973E-5,
                                        66  => 4.5776367E-5,
                                        67  => 5.340576E-5,
                                        68  => 6.1035156E-5,
                                        69  => 7.6293945E-5,
                                        70  => 9.1552734E-5,
                                        71  => 1.0681152E-4,
                                        72  => 1.2207031E-4,
                                        73  => 1.5258789E-4,
                                        74  => 1.8310547E-4,
                                        75  => 2.1362305E-4,
                                        76  => 2.4414062E-4,
                                        77  => 3.0517578E-4,
                                        78  => 3.6621094E-4,
                                        79  => 4.272461E-4,
                                        80  => 4.8828125E-4,
                                        81  => 6.1035156E-4,
                                        82  => 7.324219E-4,
                                        83  => 8.544922E-4,
                                        84  => 9.765625E-4,
                                        85  => 0.0012207031,
                                        86  => 0.0014648438,
                                        87  => 0.0017089844,
                                        88  => 0.001953125,
                                        89  => 0.0024414062,
                                        90  => 0.0029296875,
                                        91  => 0.0034179688,
                                        92  => 0.00390625,
                                        93  => 0.0048828125,
                                        94  => 0.005859375,
                                        95  => 0.0068359375,
                                        96  => 0.0078125,
                                        97  => 0.009765625,
                                        98  => 0.01171875,
                                        99  => 0.013671875,
                                        100 => 0.015625,
                                        101 => 0.01953125,
                                        102 => 0.0234375,
                                        103 => 0.02734375,
                                        104 => 0.03125,
                                        105 => 0.0390625,
                                        106 => 0.046875,
                                        107 => 0.0546875,
                                        108 => 0.0625,
                                        109 => 0.078125,
                                        110 => 0.09375,
                                        111 => 0.109375,
                                        112 => 0.125,
                                        113 => 0.15625,
                                        114 => 0.1875,
                                        115 => 0.21875,
                                        116 => 0.25,
                                        117 => 0.3125,
                                        118 => 0.375,
                                        119 => 0.4375,
                                        120 => 0.5,
                                        121 => 0.625,
                                        122 => 0.75,
                                        123 => 0.875,
                                        124 => 1.0,
                                        125 => 1.25,
                                        126 => 1.5,
                                        127 => 1.75,
                                        128 => 2.0,
                                        129 => 2.5,
                                        130 => 3.0,
                                        131 => 3.5,
                                        132 => 4.0,
                                        133 => 5.0,
                                        134 => 6.0,
                                        135 => 7.0,
                                        136 => 8.0,
                                        137 => 10.0,
                                        138 => 12.0,
                                        139 => 14.0,
                                        140 => 16.0,
                                        141 => 20.0,
                                        142 => 24.0,
                                        143 => 28.0,
                                        144 => 32.0,
                                        145 => 40.0,
                                        146 => 48.0,
                                        147 => 56.0,
                                        148 => 64.0,
                                        149 => 80.0,
                                        150 => 96.0,
                                        151 => 112.0,
                                        152 => 128.0,
                                        153 => 160.0,
                                        154 => 192.0,
                                        155 => 224.0,
                                        156 => 256.0,
                                        157 => 320.0,
                                        158 => 384.0,
                                        159 => 448.0,
                                        160 => 512.0,
                                        161 => 640.0,
                                        162 => 768.0,
                                        163 => 896.0,
                                        164 => 1024.0,
                                        165 => 1280.0,
                                        166 => 1536.0,
                                        167 => 1792.0,
                                        168 => 2048.0,
                                        169 => 2560.0,
                                        170 => 3072.0,
                                        171 => 3584.0,
                                        172 => 4096.0,
                                        173 => 5120.0,
                                        174 => 6144.0,
                                        175 => 7168.0,
                                        176 => 8192.0,
                                        177 => 10240.0,
                                        178 => 12288.0,
                                        179 => 14336.0,
                                        180 => 16384.0,
                                        181 => 20480.0,
                                        182 => 24576.0,
                                        183 => 28672.0,
                                        184 => 32768.0,
                                        185 => 40960.0,
                                        186 => 49152.0,
                                        187 => 57344.0,
                                        188 => 65536.0,
                                        189 => 81920.0,
                                        190 => 98304.0,
                                        191 => 114688.0,
                                        192 => 131072.0,
                                        193 => 163840.0,
                                        194 => 196608.0,
                                        195 => 229376.0,
                                        196 => 262144.0,
                                        197 => 327680.0,
                                        198 => 393216.0,
                                        199 => 458752.0,
                                        200 => 524288.0,
                                        201 => 655360.0,
                                        202 => 786432.0,
                                        203 => 917504.0,
                                        204 => 1048576.0,
                                        205 => 1310720.0,
                                        206 => 1572864.0,
                                        207 => 1835008.0,
                                        208 => 2097152.0,
                                        209 => 2621440.0,
                                        210 => 3145728.0,
                                        211 => 3670016.0,
                                        212 => 4194304.0,
                                        213 => 5242880.0,
                                        214 => 6291456.0,
                                        215 => 7340032.0,
                                        216 => 8388608.0,
                                        217 => 1.048576E7,
                                        218 => 1.2582912E7,
                                        219 => 1.4680064E7,
                                        220 => 1.6777216E7,
                                        221 => 2.097152E7,
                                        222 => 2.5165824E7,
                                        223 => 2.9360128E7,
                                        224 => 3.3554432E7,
                                        225 => 4.194304E7,
                                        226 => 5.0331648E7,
                                        227 => 5.8720256E7,
                                        228 => 6.7108864E7,
                                        229 => 8.388608E7,
                                        230 => 1.00663296E8,
                                        231 => 1.17440512E8,
                                        232 => 1.34217728E8,
                                        233 => 1.6777216E8,
                                        234 => 2.01326592E8,
                                        235 => 2.34881024E8,
                                        236 => 2.68435456E8,
                                        237 => 3.3554432E8,
                                        238 => 4.02653184E8,
                                        239 => 4.69762048E8,
                                        240 => 5.3687091E8,
                                        241 => 6.7108864E8,
                                        242 => 8.0530637E8,
                                        243 => 9.395241E8,
                                        244 => 1.07374182E9,
                                        245 => 1.34217728E9,
                                        246 => 1.61061274E9,
                                        247 => 1.87904819E9,
                                        248 => 2.14748365E9,
                                        249 => 2.68435456E9,
                                        250 => 3.22122547E9,
                                        251 => 3.75809638E9,
                                        252 => 4.2949673E9,
                                        253 => 5.3687091E9,
                                        254 => 6.4424509E9,
                                        255 => 7.5161928E9 );

    /**
     * Set the default Similarity implementation used by indexing and search
     * code.
     *
     * @param Zend_Search_Lucene_Search_Similarity $similarity
     */
    public static function setDefault(Zend_Search_Lucene_Search_Similarity $similarity)
    {
        self::$_defaultImpl = $similarity;
    }

    /**
     * Return the default Similarity implementation used by indexing and search
     * code.
     *
     * @return Zend_Search_Lucene_Search_Similarity
     */
    public static function getDefault()
    {
        if (!self::$_defaultImpl instanceof Zend_Search_Lucene_Search_Similarity) {
            require_once 'Zend/Search/Lucene/Search/Similarity/Default.php';
            self::$_defaultImpl = new Zend_Search_Lucene_Search_Similarity_Default();
        }

        return self::$_defaultImpl;
    }

    /**
     * Computes the normalization value for a field given the total number of
     * terms contained in a field.  These values, together with field boosts, are
     * stored in an index and multipled into scores for hits on each field by the
     * search code.
     *
     * Matches in longer fields are less precise, so implemenations of this
     * method usually return smaller values when 'numTokens' is large,
     * and larger values when 'numTokens' is small.
     *
     * That these values are computed under
     * IndexWriter::addDocument(Document) and stored then using
     * encodeNorm(float).  Thus they have limited precision, and documents
     * must be re-indexed if this method is altered.
     *
     * fieldName - name of field
     * numTokens - the total number of tokens contained in fields named
     *             'fieldName' of 'doc'.
     * Returns a normalization factor for hits on this field of this document
     *
     * @param string $fieldName
     * @param integer $numTokens
     * @return float
     */
    abstract public function lengthNorm($fieldName, $numTokens);

    /**
     * Computes the normalization value for a query given the sum of the squared
     * weights of each of the query terms.  This value is then multipled into the
     * weight of each query term.
     *
     * This does not affect ranking, but rather just attempts to make scores
     * from different queries comparable.
     *
     * sumOfSquaredWeights - the sum of the squares of query term weights
     * Returns a normalization factor for query weights
     *
     * @param float $sumOfSquaredWeights
     * @return float
     */
    abstract public function queryNorm($sumOfSquaredWeights);

    /**
     *  Decodes a normalization factor stored in an index.
     *
     * @param integer $byte
     * @return float
     */
    public static function decodeNorm($byte)
    {
        return self::$_normTable[$byte & 0xFF];
    }

    /**
     * Encodes a normalization factor for storage in an index.
     *
     * The encoding uses a five-bit exponent and three-bit mantissa, thus
     * representing values from around 7x10^9 to 2x10^-9 with about one
     * significant decimal digit of accuracy.  Zero is also represented.
     * Negative numbers are rounded up to zero.  Values too large to represent
     * are rounded down to the largest representable value.  Positive values too
     * small to represent are rounded up to the smallest positive representable
     * value.
     *
     * @param float $f
     * @return integer
     */
    static function encodeNorm($f)
    {
      return self::_floatToByte($f);
    }

    /**
     * Float to byte conversion
     *
     * @param integer $b
     * @return float
     */
    private static function _floatToByte($f)
    {
        // round negatives up to zero
        if ($f <= 0.0) {
            return 0;
        }

        // search for appropriate value
        $lowIndex = 0;
        $highIndex = 255;
        while ($highIndex >= $lowIndex) {
            // $mid = ($highIndex - $lowIndex)/2;
            $mid = ($highIndex + $lowIndex) >> 1;
            $delta = $f - self::$_normTable[$mid];

            if ($delta < 0) {
                $highIndex = $mid-1;
            } elseif ($delta > 0) {
                $lowIndex  = $mid+1;
            } else {
                return $mid; // We got it!
            }
        }

        // round to closest value
        if ($highIndex != 255 &&
            $f - self::$_normTable[$highIndex] > self::$_normTable[$highIndex+1] - $f ) {
            return $highIndex + 1;
        } else {
            return $highIndex;
        }
    }

    /**
     * Computes a score factor based on a term or phrase's frequency in a
     * document.  This value is multiplied by the idf(Term, Searcher)
     * factor for each term in the query and these products are then summed to
     * form the initial score for a document.
     *
     * Terms and phrases repeated in a document indicate the topic of the
     * document, so implementations of this method usually return larger values
     * when 'freq' is large, and smaller values when 'freq'
     * is small.
     *
     * freq - the frequency of a term within a document
     * Returns a score factor based on a term's within-document frequency
     *
     * @param float $freq
     * @return float
     */
    abstract public function tf($freq);

    /**
     * Computes the amount of a sloppy phrase match, based on an edit distance.
     * This value is summed for each sloppy phrase match in a document to form
     * the frequency that is passed to tf(float).
     *
     * A phrase match with a small edit distance to a document passage more
     * closely matches the document, so implementations of this method usually
     * return larger values when the edit distance is small and smaller values
     * when it is large.
     *
     * distance - the edit distance of this sloppy phrase match
     * Returns the frequency increment for this match
     *
     * @param integer $distance
     * @return float
     */
    abstract public function sloppyFreq($distance);

    /**
     * Computes a score factor for a simple term or a phrase.
     *
     * The default implementation is:
     *   return idfFreq(searcher.docFreq(term), searcher.maxDoc());
     *
     * input - the term in question or array of terms
     * reader - reader the document collection being searched
     * Returns a score factor for the term
     *
     * @param mixed $input
     * @param Zend_Search_Lucene_Interface $reader
     * @return a score factor for the term
     */
    public function idf($input, Zend_Search_Lucene_Interface $reader)
    {
        if (!is_array($input)) {
            return $this->idfFreq($reader->docFreq($input), $reader->count());
        } else {
            $idf = 0.0;
            foreach ($input as $term) {
                $idf += $this->idfFreq($reader->docFreq($term), $reader->count());
            }
            return $idf;
        }
    }

    /**
     * Computes a score factor based on a term's document frequency (the number
     * of documents which contain the term).  This value is multiplied by the
     * tf(int) factor for each term in the query and these products are
     * then summed to form the initial score for a document.
     *
     * Terms that occur in fewer documents are better indicators of topic, so
     * implemenations of this method usually return larger values for rare terms,
     * and smaller values for common terms.
     *
     * docFreq - the number of documents which contain the term
     * numDocs - the total number of documents in the collection
     * Returns a score factor based on the term's document frequency
     *
     * @param integer $docFreq
     * @param integer $numDocs
     * @return float
     */
    abstract public function idfFreq($docFreq, $numDocs);

    /**
     * Computes a score factor based on the fraction of all query terms that a
     * document contains.  This value is multiplied into scores.
     *
     * The presence of a large portion of the query terms indicates a better
     * match with the query, so implemenations of this method usually return
     * larger values when the ratio between these parameters is large and smaller
     * values when the ratio between them is small.
     *
     * overlap - the number of query terms matched in the document
     * maxOverlap - the total number of terms in the query
     * Returns a score factor based on term overlap with the query
     *
     * @param integer $overlap
     * @param integer $maxOverlap
     * @return float
     */
    abstract public function coord($overlap, $maxOverlap);
}


text:
<?php
/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Search_Lucene
 * @subpackage Search
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: QueryParser.php 23775 2011-03-01 17:25:24Z ralph $
 */

/** Internally used classes */

/** Zend_Search_Lucene_Analysis_Analyzer */
require_once 'Zend/Search/Lucene/Analysis/Analyzer.php';

/** Zend_Search_Lucene_Search_QueryToken */
require_once 'Zend/Search/Lucene/Search/QueryToken.php';

/** Zend_Search_Lucene_FSM */
require_once 'Zend/Search/Lucene/FSM.php';

/**
 * @category   Zend
 * @package    Zend_Search_Lucene
 * @subpackage Search
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Search_Lucene_Search_QueryParser extends Zend_Search_Lucene_FSM
{
    /**
     * Parser instance
     *
     * @var Zend_Search_Lucene_Search_QueryParser
     */
    private static $_instance = null;

    /**
     * Query lexer
     *
     * @var Zend_Search_Lucene_Search_QueryLexer
     */
    private $_lexer;

    /**
     * Tokens list
     * Array of Zend_Search_Lucene_Search_QueryToken objects
     *
     * @var array
     */
    private $_tokens;

    /**
     * Current token
     *
     * @var integer|string
     */
    private $_currentToken;

    /**
     * Last token
     *
     * It can be processed within FSM states, but this addirional state simplifies FSM
     *
     * @var Zend_Search_Lucene_Search_QueryToken
     */
    private $_lastToken = null;

    /**
     * Range query first term
     *
     * @var string
     */
    private $_rqFirstTerm = null;

    /**
     * Current query parser context
     *
     * @var Zend_Search_Lucene_Search_QueryParserContext
     */
    private $_context;

    /**
     * Context stack
     *
     * @var array
     */
    private $_contextStack;

    /**
     * Query string encoding
     *
     * @var string
     */
    private $_encoding;

    /**
     * Query string default encoding
     *
     * @var string
     */
    private $_defaultEncoding = '';

    /**
     * Defines query parsing mode.
     *
     * If this option is turned on, then query parser suppress query parser exceptions
     * and constructs multi-term query using all words from a query.
     *
     * That helps to avoid exceptions caused by queries, which don't conform to query language,
     * but limits possibilities to check, that query entered by user has some inconsistencies.
     *
     *
     * Default is true.
     *
     * Use {@link Zend_Search_Lucene::suppressQueryParsingExceptions()},
     * {@link Zend_Search_Lucene::dontSuppressQueryParsingExceptions()} and
     * {@link Zend_Search_Lucene::checkQueryParsingExceptionsSuppressMode()} to operate
     * with this setting.
     *
     * @var boolean
     */
    private $_suppressQueryParsingExceptions = true;

    /**
     * Boolean operators constants
     */
    const B_OR  = 0;
    const B_AND = 1;

    /**
     * Default boolean queries operator
     *
     * @var integer
     */
    private $_defaultOperator = self::B_OR;

    /** Query parser State Machine states */
    const ST_COMMON_QUERY_ELEMENT       = 0;   // Terms, phrases, operators
    const ST_CLOSEDINT_RQ_START         = 1;   // Range query start (closed interval) - '['
    const ST_CLOSEDINT_RQ_FIRST_TERM    = 2;   // First term in '[term1 to term2]' construction
    const ST_CLOSEDINT_RQ_TO_TERM       = 3;   // 'TO' lexeme in '[term1 to term2]' construction
    const ST_CLOSEDINT_RQ_LAST_TERM     = 4;   // Second term in '[term1 to term2]' construction
    const ST_CLOSEDINT_RQ_END           = 5;   // Range query end (closed interval) - ']'
    const ST_OPENEDINT_RQ_START         = 6;   // Range query start (opened interval) - '{'
    const ST_OPENEDINT_RQ_FIRST_TERM    = 7;   // First term in '{term1 to term2}' construction
    const ST_OPENEDINT_RQ_TO_TERM       = 8;   // 'TO' lexeme in '{term1 to term2}' construction
    const ST_OPENEDINT_RQ_LAST_TERM     = 9;   // Second term in '{term1 to term2}' construction
    const ST_OPENEDINT_RQ_END           = 10;  // Range query end (opened interval) - '}'

    /**
     * Parser constructor
     */
    public function __construct()
    {
        parent::__construct(array(self::ST_COMMON_QUERY_ELEMENT,
                                  self::ST_CLOSEDINT_RQ_START,
                                  self::ST_CLOSEDINT_RQ_FIRST_TERM,
                                  self::ST_CLOSEDINT_RQ_TO_TERM,
                                  self::ST_CLOSEDINT_RQ_LAST_TERM,
                                  self::ST_CLOSEDINT_RQ_END,
                                  self::ST_OPENEDINT_RQ_START,
                                  self::ST_OPENEDINT_RQ_FIRST_TERM,
                                  self::ST_OPENEDINT_RQ_TO_TERM,
                                  self::ST_OPENEDINT_RQ_LAST_TERM,
                                  self::ST_OPENEDINT_RQ_END
                                 ),
                            Zend_Search_Lucene_Search_QueryToken::getTypes());

        $this->addRules(
             array(array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_WORD,             self::ST_COMMON_QUERY_ELEMENT),
                   array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_PHRASE,           self::ST_COMMON_QUERY_ELEMENT),
                   array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_FIELD,            self::ST_COMMON_QUERY_ELEMENT),
                   array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_REQUIRED,         self::ST_COMMON_QUERY_ELEMENT),
                   array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_PROHIBITED,       self::ST_COMMON_QUERY_ELEMENT),
                   array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_FUZZY_PROX_MARK,  self::ST_COMMON_QUERY_ELEMENT),
                   array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_BOOSTING_MARK,    self::ST_COMMON_QUERY_ELEMENT),
                   array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_RANGE_INCL_START, self::ST_CLOSEDINT_RQ_START),
                   array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_RANGE_EXCL_START, self::ST_OPENEDINT_RQ_START),
                   array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_SUBQUERY_START,   self::ST_COMMON_QUERY_ELEMENT),
                   array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_SUBQUERY_END,     self::ST_COMMON_QUERY_ELEMENT),
                   array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_AND_LEXEME,       self::ST_COMMON_QUERY_ELEMENT),
                   array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_OR_LEXEME,        self::ST_COMMON_QUERY_ELEMENT),
                   array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_NOT_LEXEME,       self::ST_COMMON_QUERY_ELEMENT),
                   array(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_NUMBER,           self::ST_COMMON_QUERY_ELEMENT)
                  ));
        $this->addRules(
             array(array(self::ST_CLOSEDINT_RQ_START,      Zend_Search_Lucene_Search_QueryToken::TT_WORD,           self::ST_CLOSEDINT_RQ_FIRST_TERM),
                   array(self::ST_CLOSEDINT_RQ_FIRST_TERM, Zend_Search_Lucene_Search_QueryToken::TT_TO_LEXEME,      self::ST_CLOSEDINT_RQ_TO_TERM),
                   array(self::ST_CLOSEDINT_RQ_TO_TERM,    Zend_Search_Lucene_Search_QueryToken::TT_WORD,           self::ST_CLOSEDINT_RQ_LAST_TERM),
                   array(self::ST_CLOSEDINT_RQ_LAST_TERM,  Zend_Search_Lucene_Search_QueryToken::TT_RANGE_INCL_END, self::ST_COMMON_QUERY_ELEMENT)
                  ));
        $this->addRules(
             array(array(self::ST_OPENEDINT_RQ_START,      Zend_Search_Lucene_Search_QueryToken::TT_WORD,           self::ST_OPENEDINT_RQ_FIRST_TERM),
                   array(self::ST_OPENEDINT_RQ_FIRST_TERM, Zend_Search_Lucene_Search_QueryToken::TT_TO_LEXEME,      self::ST_OPENEDINT_RQ_TO_TERM),
                   array(self::ST_OPENEDINT_RQ_TO_TERM,    Zend_Search_Lucene_Search_QueryToken::TT_WORD,           self::ST_OPENEDINT_RQ_LAST_TERM),
                   array(self::ST_OPENEDINT_RQ_LAST_TERM,  Zend_Search_Lucene_Search_QueryToken::TT_RANGE_EXCL_END, self::ST_COMMON_QUERY_ELEMENT)
                  ));

        $addTermEntryAction             = new Zend_Search_Lucene_FSMAction($this, 'addTermEntry');
        $addPhraseEntryAction           = new Zend_Search_Lucene_FSMAction($this, 'addPhraseEntry');
        $setFieldAction                 = new Zend_Search_Lucene_FSMAction($this, 'setField');
        $setSignAction                  = new Zend_Search_Lucene_FSMAction($this, 'setSign');
        $setFuzzyProxAction             = new Zend_Search_Lucene_FSMAction($this, 'processFuzzyProximityModifier');
        $processModifierParameterAction = new Zend_Search_Lucene_FSMAction($this, 'processModifierParameter');
        $subqueryStartAction            = new Zend_Search_Lucene_FSMAction($this, 'subqueryStart');
        $subqueryEndAction              = new Zend_Search_Lucene_FSMAction($this, 'subqueryEnd');
        $logicalOperatorAction          = new Zend_Search_Lucene_FSMAction($this, 'logicalOperator');
        $openedRQFirstTermAction        = new Zend_Search_Lucene_FSMAction($this, 'openedRQFirstTerm');
        $openedRQLastTermAction         = new Zend_Search_Lucene_FSMAction($this, 'openedRQLastTerm');
        $closedRQFirstTermAction        = new Zend_Search_Lucene_FSMAction($this, 'closedRQFirstTerm');
        $closedRQLastTermAction         = new Zend_Search_Lucene_FSMAction($this, 'closedRQLastTerm');

        $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_WORD,            $addTermEntryAction);
        $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_PHRASE,          $addPhraseEntryAction);
        $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_FIELD,           $setFieldAction);
        $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_REQUIRED,        $setSignAction);
        $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_PROHIBITED,      $setSignAction);
        $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_FUZZY_PROX_MARK, $setFuzzyProxAction);
        $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_NUMBER,          $processModifierParameterAction);
        $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_SUBQUERY_START,  $subqueryStartAction);
        $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_SUBQUERY_END,    $subqueryEndAction);
        $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_AND_LEXEME,      $logicalOperatorAction);
        $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_OR_LEXEME,       $logicalOperatorAction);
        $this->addInputAction(self::ST_COMMON_QUERY_ELEMENT, Zend_Search_Lucene_Search_QueryToken::TT_NOT_LEXEME,      $logicalOperatorAction);

        $this->addEntryAction(self::ST_OPENEDINT_RQ_FIRST_TERM, $openedRQFirstTermAction);
        $this->addEntryAction(self::ST_OPENEDINT_RQ_LAST_TERM,  $openedRQLastTermAction);
        $this->addEntryAction(self::ST_CLOSEDINT_RQ_FIRST_TERM, $closedRQFirstTermAction);
        $this->addEntryAction(self::ST_CLOSEDINT_RQ_LAST_TERM,  $closedRQLastTermAction);

        require_once 'Zend/Search/Lucene/Search/QueryLexer.php';
        $this->_lexer = new Zend_Search_Lucene_Search_QueryLexer();
    }

    /**
     * Get query parser instance
     *
     * @return Zend_Search_Lucene_Search_QueryParser
     */
    private static function _getInstance()
    {
        if (self::$_instance === null) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    /**
     * Set query string default encoding
     *
     * @param string $encoding
     */
    public static function setDefaultEncoding($encoding)
    {
        self::_getInstance()->_defaultEncoding = $encoding;
    }

    /**
     * Get query string default encoding
     *
     * @return string
     */
    public static function getDefaultEncoding()
    {
       return self::_getInstance()->_defaultEncoding;
    }

    /**
     * Set default boolean operator
     *
     * @param integer $operator
     */
    public static function setDefaultOperator($operator)
    {
        self::_getInstance()->_defaultOperator = $operator;
    }

    /**
     * Get default boolean operator
     *
     * @return integer
     */
    public static function getDefaultOperator()
    {
        return self::_getInstance()->_defaultOperator;
    }

    /**
     * Turn on 'suppress query parser exceptions' mode.
     */
    public static function suppressQueryParsingExceptions()
    {
        self::_getInstance()->_suppressQueryParsingExceptions = true;
    }
    /**
     * Turn off 'suppress query parser exceptions' mode.
     */
    public static function dontSuppressQueryParsingExceptions()
    {
        self::_getInstance()->_suppressQueryParsingExceptions = false;
    }
    /**
     * Check 'suppress query parser exceptions' mode.
     * @return boolean
     */
    public static function queryParsingExceptionsSuppressed()
    {
        return self::_getInstance()->_suppressQueryParsingExceptions;
    }

    /**
     * Escape keyword to force it to be parsed as one term
     *
     * @param string $keyword
     * @return string
     */
    public static function escape($keyword)
    {
        return '\\' . implode('\\', str_split($keyword));
    }

    /**
     * Parses a query string
     *
     * @param string $strQuery
     * @param string $encoding
     * @return Zend_Search_Lucene_Search_Query
     * @throws Zend_Search_Lucene_Search_QueryParserException
     */
    public static function parse($strQuery, $encoding = null)
    {
        self::_getInstance();

        // Reset FSM if previous parse operation didn't return it into a correct state
        self::$_instance->reset();

        require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
        try {
            require_once 'Zend/Search/Lucene/Search/QueryParserContext.php';

            self::$_instance->_encoding     = ($encoding !== null) ? $encoding : self::$_instance->_defaultEncoding;
            self::$_instance->_lastToken    = null;
            self::$_instance->_context      = new Zend_Search_Lucene_Search_QueryParserContext(self::$_instance->_encoding);
            self::$_instance->_contextStack = array();
            self::$_instance->_tokens       = self::$_instance->_lexer->tokenize($strQuery, self::$_instance->_encoding);

            // Empty query
            if (count(self::$_instance->_tokens) == 0) {
                require_once 'Zend/Search/Lucene/Search/Query/Insignificant.php';
                return new Zend_Search_Lucene_Search_Query_Insignificant();
            }

            foreach (self::$_instance->_tokens as $token) {
                try {
                    self::$_instance->_currentToken = $token;
                    self::$_instance->process($token->type);

                    self::$_instance->_lastToken = $token;
                } catch (Exception $e) {
                    if (strpos($e->getMessage(), 'There is no any rule for') !== false) {
                        throw new Zend_Search_Lucene_Search_QueryParserException( 'Syntax error at char position ' . $token->position . '.', 0, $e);
                    }

                    require_once 'Zend/Search/Lucene/Exception.php';
                    throw new Zend_Search_Lucene_Exception($e->getMessage(), $e->getCode(), $e);
                }
            }

            if (count(self::$_instance->_contextStack) != 0) {
                throw new Zend_Search_Lucene_Search_QueryParserException('Syntax Error: mismatched parentheses, every opening must have closing.' );
            }

            return self::$_instance->_context->getQuery();
        } catch (Zend_Search_Lucene_Search_QueryParserException $e) {
            if (self::$_instance->_suppressQueryParsingExceptions) {
                $queryTokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($strQuery, self::$_instance->_encoding);

                require_once 'Zend/Search/Lucene/Search/Query/MultiTerm.php';
                $query = new Zend_Search_Lucene_Search_Query_MultiTerm();
                $termsSign = (self::$_instance->_defaultOperator == self::B_AND) ? true /* required term */ :
                                                                                null /* optional term */;

                require_once 'Zend/Search/Lucene/Index/Term.php';
                foreach ($queryTokens as $token) {
                    $query->addTerm(new Zend_Search_Lucene_Index_Term($token->getTermText()), $termsSign);
                }

                return $query;
            } else {
                require_once 'Zend/Search/Lucene/Exception.php';
                throw new Zend_Search_Lucene_Exception($e->getMessage(), $e->getCode(), $e);
            }
        }
    }

    /*********************************************************************
     * Actions implementation
     *
     * Actions affect on recognized lexemes list
     *********************************************************************/

    /**
     * Add term to a query
     */
    public function addTermEntry()
    {
        require_once 'Zend/Search/Lucene/Search/QueryEntry/Term.php';
        $entry = new Zend_Search_Lucene_Search_QueryEntry_Term($this->_currentToken->text, $this->_context->getField());
        $this->_context->addEntry($entry);
    }

    /**
     * Add phrase to a query
     */
    public function addPhraseEntry()
    {
        require_once 'Zend/Search/Lucene/Search/QueryEntry/Phrase.php';
        $entry = new Zend_Search_Lucene_Search_QueryEntry_Phrase($this->_currentToken->text, $this->_context->getField());
        $this->_context->addEntry($entry);
    }

    /**
     * Set entry field
     */
    public function setField()
    {
        $this->_context->setNextEntryField($this->_currentToken->text);
    }

    /**
     * Set entry sign
     */
    public function setSign()
    {
        $this->_context->setNextEntrySign($this->_currentToken->type);
    }

    /**
     * Process fuzzy search/proximity modifier - '~'
     */
    public function processFuzzyProximityModifier()
    {
        $this->_context->processFuzzyProximityModifier();
    }

    /**
     * Process modifier parameter
     *
     * @throws Zend_Search_Lucene_Exception
     */
    public function processModifierParameter()
    {
        if ($this->_lastToken === null) {
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('Lexeme modifier parameter must follow lexeme modifier. Char position 0.' );
        }

        switch ($this->_lastToken->type) {
            case Zend_Search_Lucene_Search_QueryToken::TT_FUZZY_PROX_MARK:
                $this->_context->processFuzzyProximityModifier($this->_currentToken->text);
                break;

            case Zend_Search_Lucene_Search_QueryToken::TT_BOOSTING_MARK:
                $this->_context->boost($this->_currentToken->text);
                break;

            default:
                // It's not a user input exception
                require_once 'Zend/Search/Lucene/Exception.php';
                throw new Zend_Search_Lucene_Exception('Lexeme modifier parameter must follow lexeme modifier. Char position 0.' );
        }
    }

    /**
     * Start subquery
     */
    public function subqueryStart()
    {
        require_once 'Zend/Search/Lucene/Search/QueryParserContext.php';

        $this->_contextStack[] = $this->_context;
        $this->_context        = new Zend_Search_Lucene_Search_QueryParserContext($this->_encoding, $this->_context->getField());
    }

    /**
     * End subquery
     */
    public function subqueryEnd()
    {
        if (count($this->_contextStack) == 0) {
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('Syntax Error: mismatched parentheses, every opening must have closing. Char position ' . $this->_currentToken->position . '.' );
        }

        $query          = $this->_context->getQuery();
        $this->_context = array_pop($this->_contextStack);

        require_once 'Zend/Search/Lucene/Search/QueryEntry/Subquery.php';
        $this->_context->addEntry(new Zend_Search_Lucene_Search_QueryEntry_Subquery($query));
    }

    /**
     * Process logical operator
     */
    public function logicalOperator()
    {
        $this->_context->addLogicalOperator($this->_currentToken->type);
    }

    /**
     * Process first range query term (opened interval)
     */
    public function openedRQFirstTerm()
    {
        $this->_rqFirstTerm = $this->_currentToken->text;
    }

    /**
     * Process last range query term (opened interval)
     *
     * @throws Zend_Search_Lucene_Search_QueryParserException
     */
    public function openedRQLastTerm()
    {
        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_rqFirstTerm, $this->_encoding);
        if (count($tokens) > 1) {
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('Range query boundary terms must be non-multiple word terms');
        } else if (count($tokens) == 1) {
            require_once 'Zend/Search/Lucene/Index/Term.php';
            $from = new Zend_Search_Lucene_Index_Term(reset($tokens)->getTermText(), $this->_context->getField());
        } else {
            $from = null;
        }

        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_currentToken->text, $this->_encoding);
        if (count($tokens) > 1) {
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('Range query boundary terms must be non-multiple word terms');
        } else if (count($tokens) == 1) {
            require_once 'Zend/Search/Lucene/Index/Term.php';
            $to = new Zend_Search_Lucene_Index_Term(reset($tokens)->getTermText(), $this->_context->getField());
        } else {
            $to = null;
        }

        if ($from === null  &&  $to === null) {
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('At least one range query boundary term must be non-empty term');
        }

        require_once 'Zend/Search/Lucene/Search/Query/Range.php';
        $rangeQuery = new Zend_Search_Lucene_Search_Query_Range($from, $to, false);
        require_once 'Zend/Search/Lucene/Search/QueryEntry/Subquery.php';
        $entry      = new Zend_Search_Lucene_Search_QueryEntry_Subquery($rangeQuery);
        $this->_context->addEntry($entry);
    }

    /**
     * Process first range query term (closed interval)
     */
    public function closedRQFirstTerm()
    {
        $this->_rqFirstTerm = $this->_currentToken->text;
    }

    /**
     * Process last range query term (closed interval)
     *
     * @throws Zend_Search_Lucene_Search_QueryParserException
     */
    public function closedRQLastTerm()
    {
        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_rqFirstTerm, $this->_encoding);
        if (count($tokens) > 1) {
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('Range query boundary terms must be non-multiple word terms');
        } else if (count($tokens) == 1) {
            require_once 'Zend/Search/Lucene/Index/Term.php';
            $from = new Zend_Search_Lucene_Index_Term(reset($tokens)->getTermText(), $this->_context->getField());
        } else {
            $from = null;
        }

        $tokens = Zend_Search_Lucene_Analysis_Analyzer::getDefault()->tokenize($this->_currentToken->text, $this->_encoding);
        if (count($tokens) > 1) {
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('Range query boundary terms must be non-multiple word terms');
        } else if (count($tokens) == 1) {
            require_once 'Zend/Search/Lucene/Index/Term.php';
            $to = new Zend_Search_Lucene_Index_Term(reset($tokens)->getTermText(), $this->_context->getField());
        } else {
            $to = null;
        }

        if ($from === null  &&  $to === null) {
            require_once 'Zend/Search/Lucene/Search/QueryParserException.php';
            throw new Zend_Search_Lucene_Search_QueryParserException('At least one range query boundary term must be non-empty term');
        }

        require_once 'Zend/Search/Lucene/Search/Query/Range.php';
        $rangeQuery = new Zend_Search_Lucene_Search_Query_Range($from, $to, true);
        require_once 'Zend/Search/Lucene/Search/QueryEntry/Subquery.php';
        $entry      = new Zend_Search_Lucene_Search_QueryEntry_Subquery($rangeQuery);
        $this->_context->addEntry($entry);
    }
}