157 lines
3.7 KiB
PHP
157 lines
3.7 KiB
PHP
<?php
|
|
class Doctrine_Query_Parser
|
|
{
|
|
/**
|
|
* The minimum number of tokens read after last detected error before
|
|
* another error can be reported.
|
|
*
|
|
* @var int
|
|
*/
|
|
const MIN_ERROR_DISTANCE = 2;
|
|
|
|
/**
|
|
* A scanner object.
|
|
*
|
|
* @var Doctrine_Query_Scanner
|
|
*/
|
|
protected $_scanner;
|
|
|
|
/**
|
|
* An array of production objects with their names as keys.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_productions = array();
|
|
|
|
/**
|
|
* The next token in the query string.
|
|
*
|
|
* @var Doctrine_Query_Token
|
|
*/
|
|
public $lookahead;
|
|
|
|
/**
|
|
* Array containing syntax and semantical errors detected in the query
|
|
* string during parsing process.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_errors = array();
|
|
|
|
/**
|
|
* The number of tokens read since last error in the input string
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $_errorDistance = self::MIN_ERROR_DISTANCE;
|
|
|
|
/**
|
|
* A query printer object used to print a parse tree from the input string.
|
|
*
|
|
* @var Doctrine_Query_Printer
|
|
*/
|
|
protected $_printer;
|
|
|
|
/**
|
|
* Creates a new query parser object.
|
|
*
|
|
* @param string $input query string to be parsed
|
|
*/
|
|
public function __construct($input)
|
|
{
|
|
$this->_scanner = new Doctrine_Query_Scanner($input);
|
|
$this->_printer = new Doctrine_Query_Printer(true);
|
|
}
|
|
|
|
public function getProduction($name)
|
|
{
|
|
if ( ! isset($this->_productions[$name])) {
|
|
$class = 'Doctrine_Query_Production_' . $name;
|
|
$this->_productions[$name] = new $class($this);
|
|
}
|
|
|
|
return $this->_productions[$name];
|
|
}
|
|
|
|
/**
|
|
* Attempts to match the given token with the current lookahead token.
|
|
*
|
|
* If they match, updates the lookahead token; otherwise raises a syntax
|
|
* error.
|
|
*
|
|
* @param int|string token type or value
|
|
*/
|
|
public function match($token)
|
|
{
|
|
if (is_string($token)) {
|
|
$isMatch = ($this->lookahead['value'] === $token);
|
|
} else {
|
|
$isMatch = ($this->lookahead['type'] === $token);
|
|
}
|
|
|
|
if ($isMatch) {
|
|
//$this->_printer->println($this->lookahead['value']);
|
|
$this->lookahead = $this->_scanner->scan();
|
|
$this->_errorDistance++;
|
|
} else {
|
|
$this->syntaxError();
|
|
}
|
|
}
|
|
|
|
public function syntaxError()
|
|
{
|
|
$this->_error('Unexpected "' . $this->lookahead['value'] . '"');
|
|
}
|
|
|
|
public function semanticalError($message)
|
|
{
|
|
$this->_error($message);
|
|
}
|
|
|
|
protected function _error($message)
|
|
{
|
|
if ($this->_errorDistance >= self::MIN_ERROR_DISTANCE) {
|
|
$message .= 'at line ' . $this->lookahead['line']
|
|
. ', column ' . $this->lookahead['column'];
|
|
$this->_errors[] = $message;
|
|
}
|
|
|
|
$this->_errorDistance = 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the scanner object associated with this object.
|
|
*
|
|
* @return Doctrine_Query_Scanner
|
|
*/
|
|
public function getScanner()
|
|
{
|
|
return $this->_scanner;
|
|
}
|
|
|
|
public function getPrinter()
|
|
{
|
|
return $this->_printer;
|
|
}
|
|
|
|
/**
|
|
* Parses a query string.
|
|
*
|
|
* @throws Doctrine_Query_Parser_Exception if errors were detected in the query string
|
|
*/
|
|
public function parse()
|
|
{
|
|
$this->lookahead = $this->_scanner->scan();
|
|
|
|
$this->getProduction('QueryLanguage')->execute();
|
|
|
|
$this->match(Doctrine_Query_Token::T_EOS);
|
|
|
|
if (count($this->_errors)) {
|
|
$msg = 'Query string parsing failed ('
|
|
. implode('; ', $this->_errors) . ').';
|
|
throw new Doctrine_Query_Parser_Exception($msg);
|
|
}
|
|
}
|
|
}
|