mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-24 11:08:45 +02:00
Fix files using CrLf, convert them to Lf to have the whole repo using Lf
Warn your git config (core.autocrlf = input or true)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
rem must be run with current directory = the directory of the batch
|
||||
rem PEAR is required to build
|
||||
php -d include_path=".;C:\iTop\PHP\PEAR" ".\PHP\LexerGenerator\cli.php" ..\oql-lexer.plex
|
||||
php ".\PHP\ParserGenerator\cli.php" ..\oql-parser.y
|
||||
php -r "echo date('Y-m-d');" > ..\version.txt
|
||||
rem must be run with current directory = the directory of the batch
|
||||
rem PEAR is required to build
|
||||
php -d include_path=".;C:\iTop\PHP\PEAR" ".\PHP\LexerGenerator\cli.php" ..\oql-lexer.plex
|
||||
php ".\PHP\ParserGenerator\cli.php" ..\oql-parser.y
|
||||
php -r "echo date('Y-m-d');" > ..\version.txt
|
||||
pause
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,444 +1,444 @@
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* OQL syntax analyzer, to be used prior to run the lexical analyzer
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
// Notes (from the source file: oql-lexer.plex) - Romain
|
||||
//
|
||||
// The strval rule is a little bit cryptic.
|
||||
// This is due to both a bug in the lexer generator and the complexity of our need
|
||||
// The rule means: either a quoted string with ", or a quoted string with '
|
||||
// literal " (resp. ') must be escaped by a \
|
||||
// \ must be escaped by an additional \
|
||||
//
|
||||
// Here are the issues and limitation found in the lexer generator:
|
||||
// * Matching simple quotes is an issue, because regexp are not correctly escaped (and the ESC code is escaped itself)
|
||||
// Workaround: insert '.chr(39).' which will be a real ' in the end
|
||||
// * Matching an alternate regexp is an issue because you must specify "|^...."
|
||||
// and the regexp parser will not accept that syntax
|
||||
// Workaround: insert '.chr(94).' which will be a real ^
|
||||
//
|
||||
// Let's analyze an overview of the regexp, we have
|
||||
// 1) The strval rule in the lexer definition
|
||||
// /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
|
||||
// 2) Becomes the php expression in the lexer
|
||||
// (note the escaped double quotes, hopefully having no effect, but showing where the issue is!)
|
||||
// $myRegexp = '/^\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/';
|
||||
//
|
||||
// To be fixed in LexerGenerator/Parser.y, in doLongestMatch (doFirstMatch is ok)
|
||||
//
|
||||
//
|
||||
// Now, let's explain how the regexp has been designed.
|
||||
// Here is a simplified version, dealing with simple quotes, and based on the assumption that the lexer generator has been fixed!
|
||||
// The strval rule in the lexer definition
|
||||
// /'([^\\']*(\\')*(\\\\)*)*'/
|
||||
// This means anything containing \\ or \' or any other char but a standalone ' or \
|
||||
// This means ' or \ could not be found without a preceding \
|
||||
//
|
||||
class OQLLexerRaw
|
||||
{
|
||||
protected $data; // input string
|
||||
public $token; // token id
|
||||
public $value; // token string representation
|
||||
protected $line; // current line
|
||||
protected $count; // current column
|
||||
|
||||
function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->count = 0;
|
||||
$this->line = 1;
|
||||
}
|
||||
|
||||
/*!lex2php
|
||||
%input $this->data
|
||||
%counter $this->count
|
||||
%token $this->token
|
||||
%value $this->value
|
||||
%line $this->line
|
||||
%matchlongest 1
|
||||
whitespace = /[ \t\n\r]+/
|
||||
union = "UNION"
|
||||
select = "SELECT"
|
||||
from = "FROM"
|
||||
as_alias = "AS"
|
||||
where = "WHERE"
|
||||
join = "JOIN"
|
||||
on = "ON"
|
||||
coma = ","
|
||||
par_open = "("
|
||||
par_close = ")"
|
||||
math_div = "/"
|
||||
math_mult = "*"
|
||||
math_plus = "+"
|
||||
math_minus = "-"
|
||||
log_and = "AND"
|
||||
log_or = "OR"
|
||||
bitwise_and = "&"
|
||||
bitwise_or = "|"
|
||||
bitwise_xor = "^"
|
||||
bitwise_leftshift = "<<"
|
||||
bitwise_rightshift = ">>"
|
||||
regexp = "REGEXP"
|
||||
eq = "="
|
||||
not_eq = "!="
|
||||
gt = ">"
|
||||
lt = "<"
|
||||
ge = ">="
|
||||
le = "<="
|
||||
like = "LIKE"
|
||||
not_like = "NOT LIKE"
|
||||
in = "IN"
|
||||
not_in = "NOT IN"
|
||||
interval = "INTERVAL"
|
||||
f_if = "IF"
|
||||
f_elt = "ELT"
|
||||
f_coalesce = "COALESCE"
|
||||
f_isnull = "ISNULL"
|
||||
f_concat = "CONCAT"
|
||||
f_substr = "SUBSTR"
|
||||
f_trim = "TRIM"
|
||||
f_date = "DATE"
|
||||
f_date_format = "DATE_FORMAT"
|
||||
f_current_date = "CURRENT_DATE"
|
||||
f_now = "NOW"
|
||||
f_time = "TIME"
|
||||
f_to_days = "TO_DAYS"
|
||||
f_from_days = "FROM_DAYS"
|
||||
f_year = "YEAR"
|
||||
f_month = "MONTH"
|
||||
f_day = "DAY"
|
||||
f_hour = "HOUR"
|
||||
f_minute = "MINUTE"
|
||||
f_second = "SECOND"
|
||||
f_date_add = "DATE_ADD"
|
||||
f_date_sub = "DATE_SUB"
|
||||
f_round = "ROUND"
|
||||
f_floor = "FLOOR"
|
||||
f_inet_aton = "INET_ATON"
|
||||
f_inet_ntoa = "INET_NTOA"
|
||||
below = "BELOW"
|
||||
below_strict = "BELOW STRICT"
|
||||
not_below = "NOT BELOW"
|
||||
not_below_strict = "NOT BELOW STRICT"
|
||||
above = "ABOVE"
|
||||
above_strict = "ABOVE STRICT"
|
||||
not_above = "NOT ABOVE"
|
||||
not_above_strict = "NOT ABOVE STRICT"
|
||||
//
|
||||
// WARNING: there seems to be a bug in the Lexer about matching the longest pattern
|
||||
// when there are alternates in the regexp.
|
||||
//
|
||||
// For instance:
|
||||
// numval = /[0-9]+|0x[0-9a-fA-F]+/
|
||||
// Does not work: SELECT Toto WHERE name = 'Text0xCTest' => Fails because 0xC is recongnized as a numval (inside the string) instead of a strval !!
|
||||
//
|
||||
// Inserting a ^ after the alternate (see comment at the top of this file) does not work either
|
||||
// numval = /[0-9]+|'.chr(94).'0x[0-9a-fA-F]+/
|
||||
// SELECT Toto WHERE name = 'Text0xCTest' => works but
|
||||
// SELECT Toto WHERE id = 0xC => does not work, 'xC' is found as a name (apparently 0 is recognized as a numval and the remaining is a name !)
|
||||
//
|
||||
// numval = /([0-9]+|0x[0-9a-fA-F]+)/
|
||||
// Does not work either, the hexadecimal numbers are not matched properly
|
||||
// Anyhow let's distinguish the hexadecimal values from decimal integers, hex numbers will be stored as strings
|
||||
// and passed as-is to MySQL which enables us to pass 64-bit values without messing with them in PHP
|
||||
//
|
||||
hexval = /(0x[0-9a-fA-F]+)/
|
||||
numval = /([0-9]+)/
|
||||
strval = /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
|
||||
name = /([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/
|
||||
varname = /:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/
|
||||
dot = "."
|
||||
*/
|
||||
|
||||
/*!lex2php
|
||||
whitespace {
|
||||
return false;
|
||||
}
|
||||
union {
|
||||
$this->token = OQLParser::UNION;
|
||||
}
|
||||
select {
|
||||
$this->token = OQLParser::SELECT;
|
||||
}
|
||||
from {
|
||||
$this->token = OQLParser::FROM;
|
||||
}
|
||||
as_alias {
|
||||
$this->token = OQLParser::AS_ALIAS;
|
||||
}
|
||||
where {
|
||||
$this->token = OQLParser::WHERE;
|
||||
}
|
||||
join {
|
||||
$this->token = OQLParser::JOIN;
|
||||
}
|
||||
on {
|
||||
$this->token = OQLParser::ON;
|
||||
}
|
||||
math_div {
|
||||
$this->token = OQLParser::MATH_DIV;
|
||||
}
|
||||
math_mult {
|
||||
$this->token = OQLParser::MATH_MULT;
|
||||
}
|
||||
math_plus {
|
||||
$this->token = OQLParser::MATH_PLUS;
|
||||
}
|
||||
math_minus {
|
||||
$this->token = OQLParser::MATH_MINUS;
|
||||
}
|
||||
log_and {
|
||||
$this->token = OQLParser::LOG_AND;
|
||||
}
|
||||
log_or {
|
||||
$this->token = OQLParser::LOG_OR;
|
||||
}
|
||||
bitwise_or {
|
||||
$this->token = OQLParser::BITWISE_OR;
|
||||
}
|
||||
bitwise_and {
|
||||
$this->token = OQLParser::BITWISE_AND;
|
||||
}
|
||||
bitwise_xor {
|
||||
$this->token = OQLParser::BITWISE_XOR;
|
||||
}
|
||||
bitwise_leftshift {
|
||||
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
|
||||
}
|
||||
bitwise_rightshift {
|
||||
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
|
||||
}
|
||||
coma {
|
||||
$this->token = OQLParser::COMA;
|
||||
}
|
||||
par_open {
|
||||
$this->token = OQLParser::PAR_OPEN;
|
||||
}
|
||||
par_close {
|
||||
$this->token = OQLParser::PAR_CLOSE;
|
||||
}
|
||||
regexp {
|
||||
$this->token = OQLParser::REGEXP;
|
||||
}
|
||||
eq {
|
||||
$this->token = OQLParser::EQ;
|
||||
}
|
||||
not_eq {
|
||||
$this->token = OQLParser::NOT_EQ;
|
||||
}
|
||||
gt {
|
||||
$this->token = OQLParser::GT;
|
||||
}
|
||||
lt {
|
||||
$this->token = OQLParser::LT;
|
||||
}
|
||||
ge {
|
||||
$this->token = OQLParser::GE;
|
||||
}
|
||||
le {
|
||||
$this->token = OQLParser::LE;
|
||||
}
|
||||
like {
|
||||
$this->token = OQLParser::LIKE;
|
||||
}
|
||||
not_like {
|
||||
$this->token = OQLParser::NOT_LIKE;
|
||||
}
|
||||
in {
|
||||
$this->token = OQLParser::IN;
|
||||
}
|
||||
not_in {
|
||||
$this->token = OQLParser::NOT_IN;
|
||||
}
|
||||
interval {
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
}
|
||||
f_if {
|
||||
$this->token = OQLParser::F_IF;
|
||||
}
|
||||
f_elt {
|
||||
$this->token = OQLParser::F_ELT;
|
||||
}
|
||||
f_coalesce {
|
||||
$this->token = OQLParser::F_COALESCE;
|
||||
}
|
||||
f_isnull {
|
||||
$this->token = OQLParser::F_ISNULL;
|
||||
}
|
||||
f_concat {
|
||||
$this->token = OQLParser::F_CONCAT;
|
||||
}
|
||||
f_substr {
|
||||
$this->token = OQLParser::F_SUBSTR;
|
||||
}
|
||||
f_trim {
|
||||
$this->token = OQLParser::F_TRIM;
|
||||
}
|
||||
f_date {
|
||||
$this->token = OQLParser::F_DATE;
|
||||
}
|
||||
f_date_format {
|
||||
$this->token = OQLParser::F_DATE_FORMAT;
|
||||
}
|
||||
f_current_date {
|
||||
$this->token = OQLParser::F_CURRENT_DATE;
|
||||
}
|
||||
f_now {
|
||||
$this->token = OQLParser::F_NOW;
|
||||
}
|
||||
f_time {
|
||||
$this->token = OQLParser::F_TIME;
|
||||
}
|
||||
f_to_days {
|
||||
$this->token = OQLParser::F_TO_DAYS;
|
||||
}
|
||||
f_from_days {
|
||||
$this->token = OQLParser::F_FROM_DAYS;
|
||||
}
|
||||
f_year {
|
||||
$this->token = OQLParser::F_YEAR;
|
||||
}
|
||||
f_month {
|
||||
$this->token = OQLParser::F_MONTH;
|
||||
}
|
||||
f_day {
|
||||
$this->token = OQLParser::F_DAY;
|
||||
}
|
||||
f_hour {
|
||||
$this->token = OQLParser::F_HOUR;
|
||||
}
|
||||
f_minute {
|
||||
$this->token = OQLParser::F_MINUTE;
|
||||
}
|
||||
f_second {
|
||||
$this->token = OQLParser::F_SECOND;
|
||||
}
|
||||
f_date_add {
|
||||
$this->token = OQLParser::F_DATE_ADD;
|
||||
}
|
||||
f_date_sub {
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
}
|
||||
f_round {
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
}
|
||||
f_floor {
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
}
|
||||
f_inet_aton {
|
||||
$this->token = OQLParser::F_INET_ATON;
|
||||
}
|
||||
f_inet_ntoa {
|
||||
$this->token = OQLParser::F_INET_NTOA;
|
||||
}
|
||||
below {
|
||||
$this->token = OQLParser::BELOW;
|
||||
}
|
||||
below_strict {
|
||||
$this->token = OQLParser::BELOW_STRICT;
|
||||
}
|
||||
not_below {
|
||||
$this->token = OQLParser::NOT_BELOW;
|
||||
}
|
||||
not_below_strict {
|
||||
$this->token = OQLParser::NOT_BELOW_STRICT;
|
||||
}
|
||||
above {
|
||||
$this->token = OQLParser::ABOVE;
|
||||
}
|
||||
above_strict {
|
||||
$this->token = OQLParser::ABOVE_STRICT;
|
||||
}
|
||||
not_above {
|
||||
$this->token = OQLParser::NOT_ABOVE;
|
||||
}
|
||||
not_above_strict {
|
||||
$this->token = OQLParser::NOT_ABOVE_STRICT;
|
||||
}
|
||||
hexval {
|
||||
$this->token = OQLParser::HEXVAL;
|
||||
}
|
||||
numval {
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
}
|
||||
strval {
|
||||
$this->token = OQLParser::STRVAL;
|
||||
}
|
||||
name {
|
||||
$this->token = OQLParser::NAME;
|
||||
}
|
||||
varname {
|
||||
$this->token = OQLParser::VARNAME;
|
||||
}
|
||||
dot {
|
||||
$this->token = OQLParser::DOT;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
define('UNEXPECTED_INPUT_AT_LINE', 'Unexpected input at line');
|
||||
|
||||
class OQLLexerException extends OQLException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sUnexpected)
|
||||
{
|
||||
parent::__construct("Syntax error", $sInput, $iLine, $iCol, $sUnexpected);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLLexer extends OQLLexerRaw
|
||||
{
|
||||
public function getTokenPos()
|
||||
{
|
||||
return max(0, $this->count - strlen($this->value));
|
||||
}
|
||||
|
||||
function yylex()
|
||||
{
|
||||
try
|
||||
{
|
||||
return parent::yylex();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$sMessage = $e->getMessage();
|
||||
if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE)
|
||||
{
|
||||
$sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE));
|
||||
if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches))
|
||||
{
|
||||
$iLine = $aMatches[1];
|
||||
$sUnexpected = $aMatches[2];
|
||||
throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected);
|
||||
}
|
||||
}
|
||||
// Default: forward the exception
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<?php
|
||||
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* OQL syntax analyzer, to be used prior to run the lexical analyzer
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
// Notes (from the source file: oql-lexer.plex) - Romain
|
||||
//
|
||||
// The strval rule is a little bit cryptic.
|
||||
// This is due to both a bug in the lexer generator and the complexity of our need
|
||||
// The rule means: either a quoted string with ", or a quoted string with '
|
||||
// literal " (resp. ') must be escaped by a \
|
||||
// \ must be escaped by an additional \
|
||||
//
|
||||
// Here are the issues and limitation found in the lexer generator:
|
||||
// * Matching simple quotes is an issue, because regexp are not correctly escaped (and the ESC code is escaped itself)
|
||||
// Workaround: insert '.chr(39).' which will be a real ' in the end
|
||||
// * Matching an alternate regexp is an issue because you must specify "|^...."
|
||||
// and the regexp parser will not accept that syntax
|
||||
// Workaround: insert '.chr(94).' which will be a real ^
|
||||
//
|
||||
// Let's analyze an overview of the regexp, we have
|
||||
// 1) The strval rule in the lexer definition
|
||||
// /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
|
||||
// 2) Becomes the php expression in the lexer
|
||||
// (note the escaped double quotes, hopefully having no effect, but showing where the issue is!)
|
||||
// $myRegexp = '/^\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/';
|
||||
//
|
||||
// To be fixed in LexerGenerator/Parser.y, in doLongestMatch (doFirstMatch is ok)
|
||||
//
|
||||
//
|
||||
// Now, let's explain how the regexp has been designed.
|
||||
// Here is a simplified version, dealing with simple quotes, and based on the assumption that the lexer generator has been fixed!
|
||||
// The strval rule in the lexer definition
|
||||
// /'([^\\']*(\\')*(\\\\)*)*'/
|
||||
// This means anything containing \\ or \' or any other char but a standalone ' or \
|
||||
// This means ' or \ could not be found without a preceding \
|
||||
//
|
||||
class OQLLexerRaw
|
||||
{
|
||||
protected $data; // input string
|
||||
public $token; // token id
|
||||
public $value; // token string representation
|
||||
protected $line; // current line
|
||||
protected $count; // current column
|
||||
|
||||
function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->count = 0;
|
||||
$this->line = 1;
|
||||
}
|
||||
|
||||
/*!lex2php
|
||||
%input $this->data
|
||||
%counter $this->count
|
||||
%token $this->token
|
||||
%value $this->value
|
||||
%line $this->line
|
||||
%matchlongest 1
|
||||
whitespace = /[ \t\n\r]+/
|
||||
union = "UNION"
|
||||
select = "SELECT"
|
||||
from = "FROM"
|
||||
as_alias = "AS"
|
||||
where = "WHERE"
|
||||
join = "JOIN"
|
||||
on = "ON"
|
||||
coma = ","
|
||||
par_open = "("
|
||||
par_close = ")"
|
||||
math_div = "/"
|
||||
math_mult = "*"
|
||||
math_plus = "+"
|
||||
math_minus = "-"
|
||||
log_and = "AND"
|
||||
log_or = "OR"
|
||||
bitwise_and = "&"
|
||||
bitwise_or = "|"
|
||||
bitwise_xor = "^"
|
||||
bitwise_leftshift = "<<"
|
||||
bitwise_rightshift = ">>"
|
||||
regexp = "REGEXP"
|
||||
eq = "="
|
||||
not_eq = "!="
|
||||
gt = ">"
|
||||
lt = "<"
|
||||
ge = ">="
|
||||
le = "<="
|
||||
like = "LIKE"
|
||||
not_like = "NOT LIKE"
|
||||
in = "IN"
|
||||
not_in = "NOT IN"
|
||||
interval = "INTERVAL"
|
||||
f_if = "IF"
|
||||
f_elt = "ELT"
|
||||
f_coalesce = "COALESCE"
|
||||
f_isnull = "ISNULL"
|
||||
f_concat = "CONCAT"
|
||||
f_substr = "SUBSTR"
|
||||
f_trim = "TRIM"
|
||||
f_date = "DATE"
|
||||
f_date_format = "DATE_FORMAT"
|
||||
f_current_date = "CURRENT_DATE"
|
||||
f_now = "NOW"
|
||||
f_time = "TIME"
|
||||
f_to_days = "TO_DAYS"
|
||||
f_from_days = "FROM_DAYS"
|
||||
f_year = "YEAR"
|
||||
f_month = "MONTH"
|
||||
f_day = "DAY"
|
||||
f_hour = "HOUR"
|
||||
f_minute = "MINUTE"
|
||||
f_second = "SECOND"
|
||||
f_date_add = "DATE_ADD"
|
||||
f_date_sub = "DATE_SUB"
|
||||
f_round = "ROUND"
|
||||
f_floor = "FLOOR"
|
||||
f_inet_aton = "INET_ATON"
|
||||
f_inet_ntoa = "INET_NTOA"
|
||||
below = "BELOW"
|
||||
below_strict = "BELOW STRICT"
|
||||
not_below = "NOT BELOW"
|
||||
not_below_strict = "NOT BELOW STRICT"
|
||||
above = "ABOVE"
|
||||
above_strict = "ABOVE STRICT"
|
||||
not_above = "NOT ABOVE"
|
||||
not_above_strict = "NOT ABOVE STRICT"
|
||||
//
|
||||
// WARNING: there seems to be a bug in the Lexer about matching the longest pattern
|
||||
// when there are alternates in the regexp.
|
||||
//
|
||||
// For instance:
|
||||
// numval = /[0-9]+|0x[0-9a-fA-F]+/
|
||||
// Does not work: SELECT Toto WHERE name = 'Text0xCTest' => Fails because 0xC is recongnized as a numval (inside the string) instead of a strval !!
|
||||
//
|
||||
// Inserting a ^ after the alternate (see comment at the top of this file) does not work either
|
||||
// numval = /[0-9]+|'.chr(94).'0x[0-9a-fA-F]+/
|
||||
// SELECT Toto WHERE name = 'Text0xCTest' => works but
|
||||
// SELECT Toto WHERE id = 0xC => does not work, 'xC' is found as a name (apparently 0 is recognized as a numval and the remaining is a name !)
|
||||
//
|
||||
// numval = /([0-9]+|0x[0-9a-fA-F]+)/
|
||||
// Does not work either, the hexadecimal numbers are not matched properly
|
||||
// Anyhow let's distinguish the hexadecimal values from decimal integers, hex numbers will be stored as strings
|
||||
// and passed as-is to MySQL which enables us to pass 64-bit values without messing with them in PHP
|
||||
//
|
||||
hexval = /(0x[0-9a-fA-F]+)/
|
||||
numval = /([0-9]+)/
|
||||
strval = /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
|
||||
name = /([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/
|
||||
varname = /:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/
|
||||
dot = "."
|
||||
*/
|
||||
|
||||
/*!lex2php
|
||||
whitespace {
|
||||
return false;
|
||||
}
|
||||
union {
|
||||
$this->token = OQLParser::UNION;
|
||||
}
|
||||
select {
|
||||
$this->token = OQLParser::SELECT;
|
||||
}
|
||||
from {
|
||||
$this->token = OQLParser::FROM;
|
||||
}
|
||||
as_alias {
|
||||
$this->token = OQLParser::AS_ALIAS;
|
||||
}
|
||||
where {
|
||||
$this->token = OQLParser::WHERE;
|
||||
}
|
||||
join {
|
||||
$this->token = OQLParser::JOIN;
|
||||
}
|
||||
on {
|
||||
$this->token = OQLParser::ON;
|
||||
}
|
||||
math_div {
|
||||
$this->token = OQLParser::MATH_DIV;
|
||||
}
|
||||
math_mult {
|
||||
$this->token = OQLParser::MATH_MULT;
|
||||
}
|
||||
math_plus {
|
||||
$this->token = OQLParser::MATH_PLUS;
|
||||
}
|
||||
math_minus {
|
||||
$this->token = OQLParser::MATH_MINUS;
|
||||
}
|
||||
log_and {
|
||||
$this->token = OQLParser::LOG_AND;
|
||||
}
|
||||
log_or {
|
||||
$this->token = OQLParser::LOG_OR;
|
||||
}
|
||||
bitwise_or {
|
||||
$this->token = OQLParser::BITWISE_OR;
|
||||
}
|
||||
bitwise_and {
|
||||
$this->token = OQLParser::BITWISE_AND;
|
||||
}
|
||||
bitwise_xor {
|
||||
$this->token = OQLParser::BITWISE_XOR;
|
||||
}
|
||||
bitwise_leftshift {
|
||||
$this->token = OQLParser::BITWISE_LEFT_SHIFT;
|
||||
}
|
||||
bitwise_rightshift {
|
||||
$this->token = OQLParser::BITWISE_RIGHT_SHIFT;
|
||||
}
|
||||
coma {
|
||||
$this->token = OQLParser::COMA;
|
||||
}
|
||||
par_open {
|
||||
$this->token = OQLParser::PAR_OPEN;
|
||||
}
|
||||
par_close {
|
||||
$this->token = OQLParser::PAR_CLOSE;
|
||||
}
|
||||
regexp {
|
||||
$this->token = OQLParser::REGEXP;
|
||||
}
|
||||
eq {
|
||||
$this->token = OQLParser::EQ;
|
||||
}
|
||||
not_eq {
|
||||
$this->token = OQLParser::NOT_EQ;
|
||||
}
|
||||
gt {
|
||||
$this->token = OQLParser::GT;
|
||||
}
|
||||
lt {
|
||||
$this->token = OQLParser::LT;
|
||||
}
|
||||
ge {
|
||||
$this->token = OQLParser::GE;
|
||||
}
|
||||
le {
|
||||
$this->token = OQLParser::LE;
|
||||
}
|
||||
like {
|
||||
$this->token = OQLParser::LIKE;
|
||||
}
|
||||
not_like {
|
||||
$this->token = OQLParser::NOT_LIKE;
|
||||
}
|
||||
in {
|
||||
$this->token = OQLParser::IN;
|
||||
}
|
||||
not_in {
|
||||
$this->token = OQLParser::NOT_IN;
|
||||
}
|
||||
interval {
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
}
|
||||
f_if {
|
||||
$this->token = OQLParser::F_IF;
|
||||
}
|
||||
f_elt {
|
||||
$this->token = OQLParser::F_ELT;
|
||||
}
|
||||
f_coalesce {
|
||||
$this->token = OQLParser::F_COALESCE;
|
||||
}
|
||||
f_isnull {
|
||||
$this->token = OQLParser::F_ISNULL;
|
||||
}
|
||||
f_concat {
|
||||
$this->token = OQLParser::F_CONCAT;
|
||||
}
|
||||
f_substr {
|
||||
$this->token = OQLParser::F_SUBSTR;
|
||||
}
|
||||
f_trim {
|
||||
$this->token = OQLParser::F_TRIM;
|
||||
}
|
||||
f_date {
|
||||
$this->token = OQLParser::F_DATE;
|
||||
}
|
||||
f_date_format {
|
||||
$this->token = OQLParser::F_DATE_FORMAT;
|
||||
}
|
||||
f_current_date {
|
||||
$this->token = OQLParser::F_CURRENT_DATE;
|
||||
}
|
||||
f_now {
|
||||
$this->token = OQLParser::F_NOW;
|
||||
}
|
||||
f_time {
|
||||
$this->token = OQLParser::F_TIME;
|
||||
}
|
||||
f_to_days {
|
||||
$this->token = OQLParser::F_TO_DAYS;
|
||||
}
|
||||
f_from_days {
|
||||
$this->token = OQLParser::F_FROM_DAYS;
|
||||
}
|
||||
f_year {
|
||||
$this->token = OQLParser::F_YEAR;
|
||||
}
|
||||
f_month {
|
||||
$this->token = OQLParser::F_MONTH;
|
||||
}
|
||||
f_day {
|
||||
$this->token = OQLParser::F_DAY;
|
||||
}
|
||||
f_hour {
|
||||
$this->token = OQLParser::F_HOUR;
|
||||
}
|
||||
f_minute {
|
||||
$this->token = OQLParser::F_MINUTE;
|
||||
}
|
||||
f_second {
|
||||
$this->token = OQLParser::F_SECOND;
|
||||
}
|
||||
f_date_add {
|
||||
$this->token = OQLParser::F_DATE_ADD;
|
||||
}
|
||||
f_date_sub {
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
}
|
||||
f_round {
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
}
|
||||
f_floor {
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
}
|
||||
f_inet_aton {
|
||||
$this->token = OQLParser::F_INET_ATON;
|
||||
}
|
||||
f_inet_ntoa {
|
||||
$this->token = OQLParser::F_INET_NTOA;
|
||||
}
|
||||
below {
|
||||
$this->token = OQLParser::BELOW;
|
||||
}
|
||||
below_strict {
|
||||
$this->token = OQLParser::BELOW_STRICT;
|
||||
}
|
||||
not_below {
|
||||
$this->token = OQLParser::NOT_BELOW;
|
||||
}
|
||||
not_below_strict {
|
||||
$this->token = OQLParser::NOT_BELOW_STRICT;
|
||||
}
|
||||
above {
|
||||
$this->token = OQLParser::ABOVE;
|
||||
}
|
||||
above_strict {
|
||||
$this->token = OQLParser::ABOVE_STRICT;
|
||||
}
|
||||
not_above {
|
||||
$this->token = OQLParser::NOT_ABOVE;
|
||||
}
|
||||
not_above_strict {
|
||||
$this->token = OQLParser::NOT_ABOVE_STRICT;
|
||||
}
|
||||
hexval {
|
||||
$this->token = OQLParser::HEXVAL;
|
||||
}
|
||||
numval {
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
}
|
||||
strval {
|
||||
$this->token = OQLParser::STRVAL;
|
||||
}
|
||||
name {
|
||||
$this->token = OQLParser::NAME;
|
||||
}
|
||||
varname {
|
||||
$this->token = OQLParser::VARNAME;
|
||||
}
|
||||
dot {
|
||||
$this->token = OQLParser::DOT;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
define('UNEXPECTED_INPUT_AT_LINE', 'Unexpected input at line');
|
||||
|
||||
class OQLLexerException extends OQLException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sUnexpected)
|
||||
{
|
||||
parent::__construct("Syntax error", $sInput, $iLine, $iCol, $sUnexpected);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLLexer extends OQLLexerRaw
|
||||
{
|
||||
public function getTokenPos()
|
||||
{
|
||||
return max(0, $this->count - strlen($this->value));
|
||||
}
|
||||
|
||||
function yylex()
|
||||
{
|
||||
try
|
||||
{
|
||||
return parent::yylex();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$sMessage = $e->getMessage();
|
||||
if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE)
|
||||
{
|
||||
$sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE));
|
||||
if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches))
|
||||
{
|
||||
$iLine = $aMatches[1];
|
||||
$sUnexpected = $aMatches[2];
|
||||
throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected);
|
||||
}
|
||||
}
|
||||
// Default: forward the exception
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,303 +1,303 @@
|
||||
|
||||
/*
|
||||
|
||||
This is a LALR(1) grammar
|
||||
(seek for Lemon grammar to get some documentation from the Net)
|
||||
That doc was helpful: http://www.hwaci.com/sw/lemon/lemon.html
|
||||
|
||||
To handle operators precedence we could have used the %left directive
|
||||
(we took another option, because that one was discovered right after...
|
||||
which option is the best for us?)
|
||||
Example:
|
||||
%left LOG_AND.
|
||||
%left LOG_OR.
|
||||
%nonassoc EQ NE GT GE LT LE.
|
||||
%left PLUS MINUS.
|
||||
%left TIMES DIVIDE MOD.
|
||||
%right EXP NOT.
|
||||
|
||||
TODO : solve the 2 remaining shift-reduce conflicts (JOIN)
|
||||
|
||||
*/
|
||||
|
||||
%name OQLParser_
|
||||
%declare_class {class OQLParserRaw}
|
||||
%syntax_error {
|
||||
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
}
|
||||
|
||||
result ::= union(X). { $this->my_result = X; }
|
||||
result ::= query(X). { $this->my_result = X; }
|
||||
result ::= condition(X). { $this->my_result = X; }
|
||||
|
||||
union(A) ::= query(X) UNION query(Y). {
|
||||
A = new OqlUnionQuery(X, Y);
|
||||
}
|
||||
union(A) ::= query(X) UNION union(Y). {
|
||||
A = new OqlUnionQuery(X, Y);
|
||||
}
|
||||
|
||||
query(A) ::= SELECT class_name(X) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, X, W, J, array(X));
|
||||
}
|
||||
query(A) ::= SELECT class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, Y, W, J, array(Y));
|
||||
}
|
||||
|
||||
query(A) ::= SELECT class_list(E) FROM class_name(X) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, X, W, J, E);
|
||||
}
|
||||
query(A) ::= SELECT class_list(E) FROM class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, Y, W, J, E);
|
||||
}
|
||||
|
||||
|
||||
class_list(A) ::= class_name(X). {
|
||||
A = array(X);
|
||||
}
|
||||
class_list(A) ::= class_list(L) COMA class_name(X). {
|
||||
array_push(L, X);
|
||||
A = L;
|
||||
}
|
||||
|
||||
where_statement(A) ::= WHERE condition(C). { A = C;}
|
||||
where_statement(A) ::= . { A = null;}
|
||||
|
||||
join_statement(A) ::= join_item(J) join_statement(S). {
|
||||
// insert the join statement on top of the existing list
|
||||
array_unshift(S, J);
|
||||
// and return the updated array
|
||||
A = S;
|
||||
}
|
||||
join_statement(A) ::= join_item(J). {
|
||||
A = Array(J);
|
||||
}
|
||||
join_statement(A) ::= . { A = null;}
|
||||
|
||||
join_item(A) ::= JOIN class_name(X) AS_ALIAS class_name(Y) ON join_condition(C).
|
||||
{
|
||||
// create an array with one single item
|
||||
A = new OqlJoinSpec(X, Y, C);
|
||||
}
|
||||
join_item(A) ::= JOIN class_name(X) ON join_condition(C).
|
||||
{
|
||||
// create an array with one single item
|
||||
A = new OqlJoinSpec(X, X, C);
|
||||
}
|
||||
|
||||
join_condition(A) ::= field_id(X) EQ field_id(Y). { A = new BinaryOqlExpression(X, '=', Y); }
|
||||
join_condition(A) ::= field_id(X) BELOW field_id(Y). { A = new BinaryOqlExpression(X, 'BELOW', Y); }
|
||||
join_condition(A) ::= field_id(X) BELOW_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'BELOW_STRICT', Y); }
|
||||
join_condition(A) ::= field_id(X) NOT_BELOW field_id(Y). { A = new BinaryOqlExpression(X, 'NOT_BELOW', Y); }
|
||||
join_condition(A) ::= field_id(X) NOT_BELOW_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'NOT_BELOW_STRICT', Y); }
|
||||
join_condition(A) ::= field_id(X) ABOVE field_id(Y). { A = new BinaryOqlExpression(X, 'ABOVE', Y); }
|
||||
join_condition(A) ::= field_id(X) ABOVE_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'ABOVE_STRICT', Y); }
|
||||
join_condition(A) ::= field_id(X) NOT_ABOVE field_id(Y). { A = new BinaryOqlExpression(X, 'NOT_ABOVE', Y); }
|
||||
join_condition(A) ::= field_id(X) NOT_ABOVE_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'NOT_ABOVE_STRICT', Y); }
|
||||
|
||||
condition(A) ::= expression_prio4(X). { A = X; }
|
||||
|
||||
expression_basic(A) ::= scalar(X). { A = X; }
|
||||
expression_basic(A) ::= field_id(X). { A = X; }
|
||||
expression_basic(A) ::= var_name(X). { A = X; }
|
||||
expression_basic(A) ::= func_name(X) PAR_OPEN arg_list(Y) PAR_CLOSE. { A = new FunctionOqlExpression(X, Y); }
|
||||
expression_basic(A) ::= PAR_OPEN expression_prio4(X) PAR_CLOSE. { A = X; }
|
||||
expression_basic(A) ::= expression_basic(X) list_operator(Y) list(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio1(A) ::= expression_basic(X). { A = X; }
|
||||
expression_prio1(A) ::= expression_prio1(X) operator1(Y) expression_basic(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio2(A) ::= expression_prio1(X). { A = X; }
|
||||
expression_prio2(A) ::= expression_prio2(X) operator2(Y) expression_prio1(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio3(A) ::= expression_prio2(X). { A = X; }
|
||||
expression_prio3(A) ::= expression_prio3(X) operator3(Y) expression_prio2(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio4(A) ::= expression_prio3(X). { A = X; }
|
||||
expression_prio4(A) ::= expression_prio4(X) operator4(Y) expression_prio3(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
|
||||
list(A) ::= PAR_OPEN list_items(X) PAR_CLOSE. {
|
||||
A = new ListOqlExpression(X);
|
||||
}
|
||||
list_items(A) ::= expression_prio4(X). {
|
||||
A = array(X);
|
||||
}
|
||||
list_items(A) ::= list_items(L) COMA expression_prio4(X). {
|
||||
array_push(L, X);
|
||||
A = L;
|
||||
}
|
||||
|
||||
arg_list(A) ::= . {
|
||||
A = array();
|
||||
}
|
||||
arg_list(A) ::= argument(X). {
|
||||
A = array(X);
|
||||
}
|
||||
arg_list(A) ::= arg_list(L) COMA argument(X). {
|
||||
array_push(L, X);
|
||||
A = L;
|
||||
}
|
||||
argument(A) ::= expression_prio4(X). { A = X; }
|
||||
argument(A) ::= INTERVAL expression_prio4(X) interval_unit(Y). { A = new IntervalOqlExpression(X, Y); }
|
||||
|
||||
interval_unit(A) ::= F_SECOND(X). { A = X; }
|
||||
interval_unit(A) ::= F_MINUTE(X). { A = X; }
|
||||
interval_unit(A) ::= F_HOUR(X). { A = X; }
|
||||
interval_unit(A) ::= F_DAY(X). { A = X; }
|
||||
interval_unit(A) ::= F_MONTH(X). { A = X; }
|
||||
interval_unit(A) ::= F_YEAR(X). { A = X; }
|
||||
|
||||
scalar(A) ::= num_scalar(X). { A = X; }
|
||||
scalar(A) ::= str_scalar(X). { A = X; }
|
||||
|
||||
num_scalar(A) ::= num_value(X). { A = new ScalarOqlExpression(X); }
|
||||
str_scalar(A) ::= str_value(X). { A = new ScalarOqlExpression(X); }
|
||||
|
||||
field_id(A) ::= name(X). { A = new FieldOqlExpression(X); }
|
||||
field_id(A) ::= class_name(X) DOT name(Y). { A = new FieldOqlExpression(Y, X); }
|
||||
class_name(A) ::= name(X). { A=X; }
|
||||
|
||||
|
||||
var_name(A) ::= VARNAME(X). { A = new VariableOqlExpression(substr(X, 1)); }
|
||||
|
||||
name(A) ::= NAME(X). {
|
||||
if (X[0] == '`')
|
||||
{
|
||||
$name = substr(X, 1, strlen(X) - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
$name = X;
|
||||
}
|
||||
A = new OqlName($name, $this->m_iColPrev);
|
||||
}
|
||||
num_value(A) ::= NUMVAL(X). {A=(int)X;}
|
||||
num_value(A) ::= MATH_MINUS NUMVAL(X). {A=(int)-X;}
|
||||
num_value(A) ::= HEXVAL(X). {A=new OqlHexValue(X);}
|
||||
str_value(A) ::= STRVAL(X). {A=stripslashes(substr(X, 1, strlen(X) - 2));}
|
||||
|
||||
|
||||
operator1(A) ::= num_operator1(X). {A=X;}
|
||||
operator1(A) ::= bitwise_operator1(X). {A=X;}
|
||||
operator2(A) ::= num_operator2(X). {A=X;}
|
||||
operator2(A) ::= str_operator(X). {A=X;}
|
||||
operator2(A) ::= REGEXP(X). {A=X;}
|
||||
operator2(A) ::= EQ(X). {A=X;}
|
||||
operator2(A) ::= NOT_EQ(X). {A=X;}
|
||||
operator3(A) ::= LOG_AND(X). {A=X;}
|
||||
operator3(A) ::= bitwise_operator3(X). {A=X;}
|
||||
operator4(A) ::= LOG_OR(X). {A=X;}
|
||||
operator4(A) ::= bitwise_operator4(X). {A=X;}
|
||||
|
||||
num_operator1(A) ::= MATH_DIV(X). {A=X;}
|
||||
num_operator1(A) ::= MATH_MULT(X). {A=X;}
|
||||
num_operator2(A) ::= MATH_PLUS(X). {A=X;}
|
||||
num_operator2(A) ::= MATH_MINUS(X). {A=X;}
|
||||
num_operator2(A) ::= GT(X). {A=X;}
|
||||
num_operator2(A) ::= LT(X). {A=X;}
|
||||
num_operator2(A) ::= GE(X). {A=X;}
|
||||
num_operator2(A) ::= LE(X). {A=X;}
|
||||
|
||||
str_operator(A) ::= LIKE(X). {A=X;}
|
||||
str_operator(A) ::= NOT_LIKE(X). {A=X;}
|
||||
|
||||
bitwise_operator1(A) ::= BITWISE_LEFT_SHIFT(X). {A=X;}
|
||||
bitwise_operator1(A) ::= BITWISE_RIGHT_SHIFT(X). {A=X;}
|
||||
bitwise_operator3(A) ::= BITWISE_AND(X). {A=X;}
|
||||
bitwise_operator4(A) ::= BITWISE_OR(X). {A=X;}
|
||||
bitwise_operator4(A) ::= BITWISE_XOR(X). {A=X;}
|
||||
|
||||
list_operator(A) ::= IN(X). {A=X;}
|
||||
list_operator(A) ::= NOT_IN(X). {A=X;}
|
||||
|
||||
func_name(A) ::= F_IF(X). { A=X; }
|
||||
func_name(A) ::= F_ELT(X). { A=X; }
|
||||
func_name(A) ::= F_COALESCE(X). { A=X; }
|
||||
func_name(A) ::= F_ISNULL(X). { A=X; }
|
||||
func_name(A) ::= F_CONCAT(X). { A=X; }
|
||||
func_name(A) ::= F_SUBSTR(X). { A=X; }
|
||||
func_name(A) ::= F_TRIM(X). { A=X; }
|
||||
func_name(A) ::= F_DATE(X). { A=X; }
|
||||
func_name(A) ::= F_DATE_FORMAT(X). { A=X; }
|
||||
func_name(A) ::= F_CURRENT_DATE(X). { A=X; }
|
||||
func_name(A) ::= F_NOW(X). { A=X; }
|
||||
func_name(A) ::= F_TIME(X). { A=X; }
|
||||
func_name(A) ::= F_TO_DAYS(X). { A=X; }
|
||||
func_name(A) ::= F_FROM_DAYS(X). { A=X; }
|
||||
func_name(A) ::= F_YEAR(X). { A=X; }
|
||||
func_name(A) ::= F_MONTH(X). { A=X; }
|
||||
func_name(A) ::= F_DAY(X). { A=X; }
|
||||
func_name(A) ::= F_DATE_ADD(X). { A=X; }
|
||||
func_name(A) ::= F_DATE_SUB(X). { A=X; }
|
||||
func_name(A) ::= F_ROUND(X). { A=X; }
|
||||
func_name(A) ::= F_FLOOR(X). { A=X; }
|
||||
func_name(A) ::= F_INET_ATON(X). { A=X; }
|
||||
func_name(A) ::= F_INET_NTOA(X). { A=X; }
|
||||
|
||||
|
||||
%code {
|
||||
|
||||
class OQLParserException extends OQLException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParser extends OQLParserRaw
|
||||
{
|
||||
// dirty, but working for us (no other mean to get the final result :-(
|
||||
protected $my_result;
|
||||
|
||||
public function GetResult()
|
||||
{
|
||||
return $this->my_result;
|
||||
}
|
||||
|
||||
// More info on the source query and the current position while parsing it
|
||||
// Data used when an exception is raised
|
||||
protected $m_iLine; // still not used
|
||||
protected $m_iCol;
|
||||
protected $m_iColPrev; // this is the interesting one, because the parser will reduce on the next token
|
||||
protected $m_sSourceQuery;
|
||||
|
||||
public function __construct($sQuery)
|
||||
{
|
||||
$this->m_iLine = 0;
|
||||
$this->m_iCol = 0;
|
||||
$this->m_iColPrev = 0;
|
||||
$this->m_sSourceQuery = $sQuery;
|
||||
// no constructor - parent::__construct();
|
||||
}
|
||||
|
||||
public function doParse($token, $value, $iCurrPosition = 0)
|
||||
{
|
||||
$this->m_iColPrev = $this->m_iCol;
|
||||
$this->m_iCol = $iCurrPosition;
|
||||
|
||||
return parent::DoParse($token, $value);
|
||||
}
|
||||
|
||||
public function doFinish()
|
||||
{
|
||||
$this->doParse(0, 0);
|
||||
return $this->my_result;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
// Bug in the original destructor, causing an infinite loop !
|
||||
// This is a real issue when a fatal error occurs on the first token (the error could not be seen)
|
||||
if (is_null($this->yyidx))
|
||||
{
|
||||
$this->yyidx = -1;
|
||||
}
|
||||
parent::__destruct();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
This is a LALR(1) grammar
|
||||
(seek for Lemon grammar to get some documentation from the Net)
|
||||
That doc was helpful: http://www.hwaci.com/sw/lemon/lemon.html
|
||||
|
||||
To handle operators precedence we could have used the %left directive
|
||||
(we took another option, because that one was discovered right after...
|
||||
which option is the best for us?)
|
||||
Example:
|
||||
%left LOG_AND.
|
||||
%left LOG_OR.
|
||||
%nonassoc EQ NE GT GE LT LE.
|
||||
%left PLUS MINUS.
|
||||
%left TIMES DIVIDE MOD.
|
||||
%right EXP NOT.
|
||||
|
||||
TODO : solve the 2 remaining shift-reduce conflicts (JOIN)
|
||||
|
||||
*/
|
||||
|
||||
%name OQLParser_
|
||||
%declare_class {class OQLParserRaw}
|
||||
%syntax_error {
|
||||
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
}
|
||||
|
||||
result ::= union(X). { $this->my_result = X; }
|
||||
result ::= query(X). { $this->my_result = X; }
|
||||
result ::= condition(X). { $this->my_result = X; }
|
||||
|
||||
union(A) ::= query(X) UNION query(Y). {
|
||||
A = new OqlUnionQuery(X, Y);
|
||||
}
|
||||
union(A) ::= query(X) UNION union(Y). {
|
||||
A = new OqlUnionQuery(X, Y);
|
||||
}
|
||||
|
||||
query(A) ::= SELECT class_name(X) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, X, W, J, array(X));
|
||||
}
|
||||
query(A) ::= SELECT class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, Y, W, J, array(Y));
|
||||
}
|
||||
|
||||
query(A) ::= SELECT class_list(E) FROM class_name(X) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, X, W, J, E);
|
||||
}
|
||||
query(A) ::= SELECT class_list(E) FROM class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, Y, W, J, E);
|
||||
}
|
||||
|
||||
|
||||
class_list(A) ::= class_name(X). {
|
||||
A = array(X);
|
||||
}
|
||||
class_list(A) ::= class_list(L) COMA class_name(X). {
|
||||
array_push(L, X);
|
||||
A = L;
|
||||
}
|
||||
|
||||
where_statement(A) ::= WHERE condition(C). { A = C;}
|
||||
where_statement(A) ::= . { A = null;}
|
||||
|
||||
join_statement(A) ::= join_item(J) join_statement(S). {
|
||||
// insert the join statement on top of the existing list
|
||||
array_unshift(S, J);
|
||||
// and return the updated array
|
||||
A = S;
|
||||
}
|
||||
join_statement(A) ::= join_item(J). {
|
||||
A = Array(J);
|
||||
}
|
||||
join_statement(A) ::= . { A = null;}
|
||||
|
||||
join_item(A) ::= JOIN class_name(X) AS_ALIAS class_name(Y) ON join_condition(C).
|
||||
{
|
||||
// create an array with one single item
|
||||
A = new OqlJoinSpec(X, Y, C);
|
||||
}
|
||||
join_item(A) ::= JOIN class_name(X) ON join_condition(C).
|
||||
{
|
||||
// create an array with one single item
|
||||
A = new OqlJoinSpec(X, X, C);
|
||||
}
|
||||
|
||||
join_condition(A) ::= field_id(X) EQ field_id(Y). { A = new BinaryOqlExpression(X, '=', Y); }
|
||||
join_condition(A) ::= field_id(X) BELOW field_id(Y). { A = new BinaryOqlExpression(X, 'BELOW', Y); }
|
||||
join_condition(A) ::= field_id(X) BELOW_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'BELOW_STRICT', Y); }
|
||||
join_condition(A) ::= field_id(X) NOT_BELOW field_id(Y). { A = new BinaryOqlExpression(X, 'NOT_BELOW', Y); }
|
||||
join_condition(A) ::= field_id(X) NOT_BELOW_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'NOT_BELOW_STRICT', Y); }
|
||||
join_condition(A) ::= field_id(X) ABOVE field_id(Y). { A = new BinaryOqlExpression(X, 'ABOVE', Y); }
|
||||
join_condition(A) ::= field_id(X) ABOVE_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'ABOVE_STRICT', Y); }
|
||||
join_condition(A) ::= field_id(X) NOT_ABOVE field_id(Y). { A = new BinaryOqlExpression(X, 'NOT_ABOVE', Y); }
|
||||
join_condition(A) ::= field_id(X) NOT_ABOVE_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'NOT_ABOVE_STRICT', Y); }
|
||||
|
||||
condition(A) ::= expression_prio4(X). { A = X; }
|
||||
|
||||
expression_basic(A) ::= scalar(X). { A = X; }
|
||||
expression_basic(A) ::= field_id(X). { A = X; }
|
||||
expression_basic(A) ::= var_name(X). { A = X; }
|
||||
expression_basic(A) ::= func_name(X) PAR_OPEN arg_list(Y) PAR_CLOSE. { A = new FunctionOqlExpression(X, Y); }
|
||||
expression_basic(A) ::= PAR_OPEN expression_prio4(X) PAR_CLOSE. { A = X; }
|
||||
expression_basic(A) ::= expression_basic(X) list_operator(Y) list(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio1(A) ::= expression_basic(X). { A = X; }
|
||||
expression_prio1(A) ::= expression_prio1(X) operator1(Y) expression_basic(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio2(A) ::= expression_prio1(X). { A = X; }
|
||||
expression_prio2(A) ::= expression_prio2(X) operator2(Y) expression_prio1(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio3(A) ::= expression_prio2(X). { A = X; }
|
||||
expression_prio3(A) ::= expression_prio3(X) operator3(Y) expression_prio2(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio4(A) ::= expression_prio3(X). { A = X; }
|
||||
expression_prio4(A) ::= expression_prio4(X) operator4(Y) expression_prio3(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
|
||||
list(A) ::= PAR_OPEN list_items(X) PAR_CLOSE. {
|
||||
A = new ListOqlExpression(X);
|
||||
}
|
||||
list_items(A) ::= expression_prio4(X). {
|
||||
A = array(X);
|
||||
}
|
||||
list_items(A) ::= list_items(L) COMA expression_prio4(X). {
|
||||
array_push(L, X);
|
||||
A = L;
|
||||
}
|
||||
|
||||
arg_list(A) ::= . {
|
||||
A = array();
|
||||
}
|
||||
arg_list(A) ::= argument(X). {
|
||||
A = array(X);
|
||||
}
|
||||
arg_list(A) ::= arg_list(L) COMA argument(X). {
|
||||
array_push(L, X);
|
||||
A = L;
|
||||
}
|
||||
argument(A) ::= expression_prio4(X). { A = X; }
|
||||
argument(A) ::= INTERVAL expression_prio4(X) interval_unit(Y). { A = new IntervalOqlExpression(X, Y); }
|
||||
|
||||
interval_unit(A) ::= F_SECOND(X). { A = X; }
|
||||
interval_unit(A) ::= F_MINUTE(X). { A = X; }
|
||||
interval_unit(A) ::= F_HOUR(X). { A = X; }
|
||||
interval_unit(A) ::= F_DAY(X). { A = X; }
|
||||
interval_unit(A) ::= F_MONTH(X). { A = X; }
|
||||
interval_unit(A) ::= F_YEAR(X). { A = X; }
|
||||
|
||||
scalar(A) ::= num_scalar(X). { A = X; }
|
||||
scalar(A) ::= str_scalar(X). { A = X; }
|
||||
|
||||
num_scalar(A) ::= num_value(X). { A = new ScalarOqlExpression(X); }
|
||||
str_scalar(A) ::= str_value(X). { A = new ScalarOqlExpression(X); }
|
||||
|
||||
field_id(A) ::= name(X). { A = new FieldOqlExpression(X); }
|
||||
field_id(A) ::= class_name(X) DOT name(Y). { A = new FieldOqlExpression(Y, X); }
|
||||
class_name(A) ::= name(X). { A=X; }
|
||||
|
||||
|
||||
var_name(A) ::= VARNAME(X). { A = new VariableOqlExpression(substr(X, 1)); }
|
||||
|
||||
name(A) ::= NAME(X). {
|
||||
if (X[0] == '`')
|
||||
{
|
||||
$name = substr(X, 1, strlen(X) - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
$name = X;
|
||||
}
|
||||
A = new OqlName($name, $this->m_iColPrev);
|
||||
}
|
||||
num_value(A) ::= NUMVAL(X). {A=(int)X;}
|
||||
num_value(A) ::= MATH_MINUS NUMVAL(X). {A=(int)-X;}
|
||||
num_value(A) ::= HEXVAL(X). {A=new OqlHexValue(X);}
|
||||
str_value(A) ::= STRVAL(X). {A=stripslashes(substr(X, 1, strlen(X) - 2));}
|
||||
|
||||
|
||||
operator1(A) ::= num_operator1(X). {A=X;}
|
||||
operator1(A) ::= bitwise_operator1(X). {A=X;}
|
||||
operator2(A) ::= num_operator2(X). {A=X;}
|
||||
operator2(A) ::= str_operator(X). {A=X;}
|
||||
operator2(A) ::= REGEXP(X). {A=X;}
|
||||
operator2(A) ::= EQ(X). {A=X;}
|
||||
operator2(A) ::= NOT_EQ(X). {A=X;}
|
||||
operator3(A) ::= LOG_AND(X). {A=X;}
|
||||
operator3(A) ::= bitwise_operator3(X). {A=X;}
|
||||
operator4(A) ::= LOG_OR(X). {A=X;}
|
||||
operator4(A) ::= bitwise_operator4(X). {A=X;}
|
||||
|
||||
num_operator1(A) ::= MATH_DIV(X). {A=X;}
|
||||
num_operator1(A) ::= MATH_MULT(X). {A=X;}
|
||||
num_operator2(A) ::= MATH_PLUS(X). {A=X;}
|
||||
num_operator2(A) ::= MATH_MINUS(X). {A=X;}
|
||||
num_operator2(A) ::= GT(X). {A=X;}
|
||||
num_operator2(A) ::= LT(X). {A=X;}
|
||||
num_operator2(A) ::= GE(X). {A=X;}
|
||||
num_operator2(A) ::= LE(X). {A=X;}
|
||||
|
||||
str_operator(A) ::= LIKE(X). {A=X;}
|
||||
str_operator(A) ::= NOT_LIKE(X). {A=X;}
|
||||
|
||||
bitwise_operator1(A) ::= BITWISE_LEFT_SHIFT(X). {A=X;}
|
||||
bitwise_operator1(A) ::= BITWISE_RIGHT_SHIFT(X). {A=X;}
|
||||
bitwise_operator3(A) ::= BITWISE_AND(X). {A=X;}
|
||||
bitwise_operator4(A) ::= BITWISE_OR(X). {A=X;}
|
||||
bitwise_operator4(A) ::= BITWISE_XOR(X). {A=X;}
|
||||
|
||||
list_operator(A) ::= IN(X). {A=X;}
|
||||
list_operator(A) ::= NOT_IN(X). {A=X;}
|
||||
|
||||
func_name(A) ::= F_IF(X). { A=X; }
|
||||
func_name(A) ::= F_ELT(X). { A=X; }
|
||||
func_name(A) ::= F_COALESCE(X). { A=X; }
|
||||
func_name(A) ::= F_ISNULL(X). { A=X; }
|
||||
func_name(A) ::= F_CONCAT(X). { A=X; }
|
||||
func_name(A) ::= F_SUBSTR(X). { A=X; }
|
||||
func_name(A) ::= F_TRIM(X). { A=X; }
|
||||
func_name(A) ::= F_DATE(X). { A=X; }
|
||||
func_name(A) ::= F_DATE_FORMAT(X). { A=X; }
|
||||
func_name(A) ::= F_CURRENT_DATE(X). { A=X; }
|
||||
func_name(A) ::= F_NOW(X). { A=X; }
|
||||
func_name(A) ::= F_TIME(X). { A=X; }
|
||||
func_name(A) ::= F_TO_DAYS(X). { A=X; }
|
||||
func_name(A) ::= F_FROM_DAYS(X). { A=X; }
|
||||
func_name(A) ::= F_YEAR(X). { A=X; }
|
||||
func_name(A) ::= F_MONTH(X). { A=X; }
|
||||
func_name(A) ::= F_DAY(X). { A=X; }
|
||||
func_name(A) ::= F_DATE_ADD(X). { A=X; }
|
||||
func_name(A) ::= F_DATE_SUB(X). { A=X; }
|
||||
func_name(A) ::= F_ROUND(X). { A=X; }
|
||||
func_name(A) ::= F_FLOOR(X). { A=X; }
|
||||
func_name(A) ::= F_INET_ATON(X). { A=X; }
|
||||
func_name(A) ::= F_INET_NTOA(X). { A=X; }
|
||||
|
||||
|
||||
%code {
|
||||
|
||||
class OQLParserException extends OQLException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParser extends OQLParserRaw
|
||||
{
|
||||
// dirty, but working for us (no other mean to get the final result :-(
|
||||
protected $my_result;
|
||||
|
||||
public function GetResult()
|
||||
{
|
||||
return $this->my_result;
|
||||
}
|
||||
|
||||
// More info on the source query and the current position while parsing it
|
||||
// Data used when an exception is raised
|
||||
protected $m_iLine; // still not used
|
||||
protected $m_iCol;
|
||||
protected $m_iColPrev; // this is the interesting one, because the parser will reduce on the next token
|
||||
protected $m_sSourceQuery;
|
||||
|
||||
public function __construct($sQuery)
|
||||
{
|
||||
$this->m_iLine = 0;
|
||||
$this->m_iCol = 0;
|
||||
$this->m_iColPrev = 0;
|
||||
$this->m_sSourceQuery = $sQuery;
|
||||
// no constructor - parent::__construct();
|
||||
}
|
||||
|
||||
public function doParse($token, $value, $iCurrPosition = 0)
|
||||
{
|
||||
$this->m_iColPrev = $this->m_iCol;
|
||||
$this->m_iCol = $iCurrPosition;
|
||||
|
||||
return parent::DoParse($token, $value);
|
||||
}
|
||||
|
||||
public function doFinish()
|
||||
{
|
||||
$this->doParse(0, 0);
|
||||
return $this->my_result;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
// Bug in the original destructor, causing an infinite loop !
|
||||
// This is a real issue when a fatal error occurs on the first token (the error could not be seen)
|
||||
if (is_null($this->yyidx))
|
||||
{
|
||||
$this->yyidx = -1;
|
||||
}
|
||||
parent::__destruct();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,112 +1,112 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper to execute the parser, lexical analyzer and normalization of an OQL query
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
class OqlNormalizeException extends OQLException
|
||||
{
|
||||
public function __construct($sIssue, $sInput, OqlName $oName, $aExpecting = null)
|
||||
{
|
||||
parent::__construct($sIssue, $sInput, 0, $oName->GetPos(), $oName->GetValue(), $aExpecting);
|
||||
}
|
||||
}
|
||||
class UnknownClassOqlException extends OqlNormalizeException
|
||||
{
|
||||
public function __construct($sInput, OqlName $oName, $aExpecting = null)
|
||||
{
|
||||
parent::__construct('Unknown class', $sInput, $oName, $aExpecting);
|
||||
}
|
||||
|
||||
public function GetUserFriendlyDescription()
|
||||
{
|
||||
$sWrongClass = $this->GetWrongWord();
|
||||
$sSuggest = self::FindClosestString($sWrongClass, $this->GetSuggestions());
|
||||
|
||||
if ($sSuggest != '')
|
||||
{
|
||||
return Dict::Format('UI:OQL:UnknownClassAndFix', $sWrongClass, $sSuggest);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Dict::Format('UI:OQL:UnknownClassNoFix', $sWrongClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OqlInterpreterException extends OQLException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
class OqlInterpreter
|
||||
{
|
||||
public $m_sQuery;
|
||||
|
||||
public function __construct($sQuery)
|
||||
{
|
||||
$this->m_sQuery = $sQuery;
|
||||
}
|
||||
|
||||
// Note: this function is left public for unit test purposes
|
||||
public function Parse()
|
||||
{
|
||||
$oLexer = new OQLLexer($this->m_sQuery);
|
||||
$oParser = new OQLParser($this->m_sQuery);
|
||||
|
||||
while($oLexer->yylex())
|
||||
{
|
||||
$oParser->doParse($oLexer->token, $oLexer->value, $oLexer->getTokenPos());
|
||||
}
|
||||
$res = $oParser->doFinish();
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OqlQuery
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function ParseQuery()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
if (!$oRes instanceof OqlQuery)
|
||||
{
|
||||
throw new OQLException('Expecting an OQL query', $this->m_sQuery, 0, 0, get_class($oRes));
|
||||
}
|
||||
return $oRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Expression
|
||||
*/
|
||||
public function ParseExpression()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
if (!$oRes instanceof Expression)
|
||||
{
|
||||
throw new OQLException('Expecting an OQL expression', $this->m_sQuery, 0, 0, get_class($oRes), array('Expression'));
|
||||
}
|
||||
return $oRes;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper to execute the parser, lexical analyzer and normalization of an OQL query
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
class OqlNormalizeException extends OQLException
|
||||
{
|
||||
public function __construct($sIssue, $sInput, OqlName $oName, $aExpecting = null)
|
||||
{
|
||||
parent::__construct($sIssue, $sInput, 0, $oName->GetPos(), $oName->GetValue(), $aExpecting);
|
||||
}
|
||||
}
|
||||
class UnknownClassOqlException extends OqlNormalizeException
|
||||
{
|
||||
public function __construct($sInput, OqlName $oName, $aExpecting = null)
|
||||
{
|
||||
parent::__construct('Unknown class', $sInput, $oName, $aExpecting);
|
||||
}
|
||||
|
||||
public function GetUserFriendlyDescription()
|
||||
{
|
||||
$sWrongClass = $this->GetWrongWord();
|
||||
$sSuggest = self::FindClosestString($sWrongClass, $this->GetSuggestions());
|
||||
|
||||
if ($sSuggest != '')
|
||||
{
|
||||
return Dict::Format('UI:OQL:UnknownClassAndFix', $sWrongClass, $sSuggest);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Dict::Format('UI:OQL:UnknownClassNoFix', $sWrongClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OqlInterpreterException extends OQLException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
class OqlInterpreter
|
||||
{
|
||||
public $m_sQuery;
|
||||
|
||||
public function __construct($sQuery)
|
||||
{
|
||||
$this->m_sQuery = $sQuery;
|
||||
}
|
||||
|
||||
// Note: this function is left public for unit test purposes
|
||||
public function Parse()
|
||||
{
|
||||
$oLexer = new OQLLexer($this->m_sQuery);
|
||||
$oParser = new OQLParser($this->m_sQuery);
|
||||
|
||||
while($oLexer->yylex())
|
||||
{
|
||||
$oParser->doParse($oLexer->token, $oLexer->value, $oLexer->getTokenPos());
|
||||
}
|
||||
$res = $oParser->doFinish();
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OqlQuery
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function ParseQuery()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
if (!$oRes instanceof OqlQuery)
|
||||
{
|
||||
throw new OQLException('Expecting an OQL query', $this->m_sQuery, 0, 0, get_class($oRes));
|
||||
}
|
||||
return $oRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Expression
|
||||
*/
|
||||
public function ParseExpression()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
if (!$oRes instanceof Expression)
|
||||
{
|
||||
throw new OQLException('Expecting an OQL expression', $this->m_sQuery, 0, 0, get_class($oRes), array('Expression'));
|
||||
}
|
||||
return $oRes;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user