diff --git a/core/oql/oql-parser.php b/core/oql/oql-parser.php index 4fdffa704..c3995650c 100644 --- a/core/oql/oql-parser.php +++ b/core/oql/oql-parser.php @@ -96,7 +96,7 @@ class OQLParser_yyStackEntry // code external to the class is included here // declare_class is output here -#line 2 "oql-parser.y" +#line 24 "oql-parser.y" class OQLParserRaw#line 102 "oql-parser.php" { /* First off, code is included which follows the "include_class" declaration @@ -1221,118 +1221,118 @@ static public $yy_action = array( ** function yy_r0($yymsp){ ... } // User supplied code ** #line */ -#line 7 "oql-parser.y" +#line 29 "oql-parser.y" function yy_r0(){ $this->my_result = $this->yystack[$this->yyidx + 0]->minor; } #line 1230 "oql-parser.php" -#line 10 "oql-parser.y" - function yy_r2(){ - $this->_retvalue = new OqlQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); +#line 32 "oql-parser.y" + function yy_r2(){ + $this->_retvalue = new OqlQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); } #line 1235 "oql-parser.php" -#line 13 "oql-parser.y" - function yy_r3(){ - $this->_retvalue = new OqlQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); +#line 35 "oql-parser.y" + function yy_r3(){ + $this->_retvalue = new OqlQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); } #line 1240 "oql-parser.php" -#line 17 "oql-parser.y" +#line 39 "oql-parser.y" function yy_r4(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } #line 1243 "oql-parser.php" -#line 18 "oql-parser.y" +#line 40 "oql-parser.y" function yy_r5(){ $this->_retvalue = null; } #line 1246 "oql-parser.php" -#line 20 "oql-parser.y" - function yy_r6(){ - // insert the join statement on top of the existing list - array_unshift($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); - // and return the updated array - $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; +#line 42 "oql-parser.y" + function yy_r6(){ + // insert the join statement on top of the existing list + array_unshift($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); + // and return the updated array + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } #line 1254 "oql-parser.php" -#line 26 "oql-parser.y" - function yy_r7(){ - $this->_retvalue = Array($this->yystack[$this->yyidx + 0]->minor); +#line 48 "oql-parser.y" + function yy_r7(){ + $this->_retvalue = Array($this->yystack[$this->yyidx + 0]->minor); } #line 1259 "oql-parser.php" -#line 32 "oql-parser.y" - function yy_r9(){ - // create an array with one single item - $this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); +#line 54 "oql-parser.y" + function yy_r9(){ + // create an array with one single item + $this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); } #line 1265 "oql-parser.php" -#line 37 "oql-parser.y" - function yy_r10(){ - // create an array with one single item - $this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); +#line 59 "oql-parser.y" + function yy_r10(){ + // create an array with one single item + $this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); } #line 1271 "oql-parser.php" -#line 42 "oql-parser.y" +#line 64 "oql-parser.y" function yy_r11(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, '=', $this->yystack[$this->yyidx + 0]->minor); } #line 1274 "oql-parser.php" -#line 44 "oql-parser.y" +#line 66 "oql-parser.y" function yy_r12(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } #line 1277 "oql-parser.php" -#line 48 "oql-parser.y" +#line 70 "oql-parser.y" function yy_r15(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); } #line 1280 "oql-parser.php" -#line 49 "oql-parser.y" +#line 71 "oql-parser.y" function yy_r16(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; } #line 1283 "oql-parser.php" -#line 50 "oql-parser.y" +#line 72 "oql-parser.y" function yy_r17(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } #line 1286 "oql-parser.php" -#line 65 "oql-parser.y" - function yy_r26(){ - $this->_retvalue = new ListOqlExpression($this->yystack[$this->yyidx + -1]->minor); +#line 87 "oql-parser.y" + function yy_r26(){ + $this->_retvalue = new ListOqlExpression($this->yystack[$this->yyidx + -1]->minor); } #line 1291 "oql-parser.php" -#line 68 "oql-parser.y" - function yy_r27(){ - $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor); +#line 90 "oql-parser.y" + function yy_r27(){ + $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor); } #line 1296 "oql-parser.php" -#line 71 "oql-parser.y" - function yy_r28(){ - array_push($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); - $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor; +#line 93 "oql-parser.y" + function yy_r28(){ + array_push($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor; } #line 1302 "oql-parser.php" -#line 76 "oql-parser.y" - function yy_r29(){ - $this->_retvalue = array(); +#line 98 "oql-parser.y" + function yy_r29(){ + $this->_retvalue = array(); } #line 1307 "oql-parser.php" -#line 87 "oql-parser.y" +#line 109 "oql-parser.y" function yy_r33(){ $this->_retvalue = new IntervalOqlExpression($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } #line 1310 "oql-parser.php" -#line 96 "oql-parser.y" +#line 118 "oql-parser.y" function yy_r39(){ $this->_retvalue = new ScalarOqlExpression($this->yystack[$this->yyidx + 0]->minor); } #line 1313 "oql-parser.php" -#line 99 "oql-parser.y" +#line 121 "oql-parser.y" function yy_r41(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor); } #line 1316 "oql-parser.php" -#line 100 "oql-parser.y" +#line 122 "oql-parser.y" function yy_r42(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -2]->minor); } #line 1319 "oql-parser.php" -#line 101 "oql-parser.y" +#line 123 "oql-parser.y" function yy_r43(){ $this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; } #line 1322 "oql-parser.php" -#line 103 "oql-parser.y" - function yy_r44(){ - if ($this->yystack[$this->yyidx + 0]->minor[0] == '`') - { - $name = substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2); - } - else - { - $name = $this->yystack[$this->yyidx + 0]->minor; - } - $this->_retvalue = new OqlName($name, $this->m_iColPrev); +#line 125 "oql-parser.y" + function yy_r44(){ + if ($this->yystack[$this->yyidx + 0]->minor[0] == '`') + { + $name = substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2); + } + else + { + $name = $this->yystack[$this->yyidx + 0]->minor; + } + $this->_retvalue = new OqlName($name, $this->m_iColPrev); } #line 1335 "oql-parser.php" -#line 115 "oql-parser.y" +#line 137 "oql-parser.y" function yy_r45(){$this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; } #line 1338 "oql-parser.php" -#line 116 "oql-parser.y" +#line 138 "oql-parser.y" function yy_r46(){$this->_retvalue=stripslashes(substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2)); } #line 1341 "oql-parser.php" @@ -1446,9 +1446,9 @@ static public $yy_action = array( */ function yy_syntax_error($yymajor, $TOKEN) { -#line 3 "oql-parser.y" - -throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN); +#line 25 "oql-parser.y" + +throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN); #line 1457 "oql-parser.php" } @@ -1601,70 +1601,69 @@ throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCo } } while ($yymajor != self::YYNOCODE && $this->yyidx >= 0); } -}#line 164 "oql-parser.y" - - - -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(); - } -} - -#line 1677 "oql-parser.php" +}#line 186 "oql-parser.y" + + +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(); + } +} + +#line 1676 "oql-parser.php" diff --git a/core/oql/oql-parser.y b/core/oql/oql-parser.y index b86423b77..551a82ada 100644 --- a/core/oql/oql-parser.y +++ b/core/oql/oql-parser.y @@ -1,229 +1,250 @@ -%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 ::= query(X). { $this->my_result = X; } -result ::= condition(X). { $this->my_result = X; } - -query(A) ::= SELECT class_name(X) join_statement(J) where_statement(W). { - A = new OqlQuery(X, X, W, J); -} -query(A) ::= SELECT class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). { - A = new OqlQuery(X, Y, W, J); -} - -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); } - -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) ::= 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 scalar_list(X) PAR_CLOSE. { - A = new ListOqlExpression(X); -} -scalar_list(A) ::= scalar(X). { - A = array(X); -} -scalar_list(A) ::= scalar_list(L) COMA scalar(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_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; } - -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=X;} -str_value(A) ::= STRVAL(X). {A=stripslashes(substr(X, 1, strlen(X) - 2));} - - -operator1(A) ::= num_operator1(X). {A=X;} -operator2(A) ::= num_operator2(X). {A=X;} -operator2(A) ::= str_operator(X). {A=X;} -operator2(A) ::= EQ(X). {A=X;} -operator2(A) ::= NOT_EQ(X). {A=X;} -operator3(A) ::= LOG_AND(X). {A=X;} -operator4(A) ::= LOG_OR(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;} - -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_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; } - - -%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 ::= query(X). { $this->my_result = X; } +result ::= condition(X). { $this->my_result = X; } + +query(A) ::= SELECT class_name(X) join_statement(J) where_statement(W). { + A = new OqlQuery(X, X, W, J); +} +query(A) ::= SELECT class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). { + A = new OqlQuery(X, Y, W, J); +} + +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); } + +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) ::= 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 scalar_list(X) PAR_CLOSE. { + A = new ListOqlExpression(X); +} +scalar_list(A) ::= scalar(X). { + A = array(X); +} +scalar_list(A) ::= scalar_list(L) COMA scalar(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_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; } + +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=X;} +str_value(A) ::= STRVAL(X). {A=stripslashes(substr(X, 1, strlen(X) - 2));} + + +operator1(A) ::= num_operator1(X). {A=X;} +operator2(A) ::= num_operator2(X). {A=X;} +operator2(A) ::= str_operator(X). {A=X;} +operator2(A) ::= EQ(X). {A=X;} +operator2(A) ::= NOT_EQ(X). {A=X;} +operator3(A) ::= LOG_AND(X). {A=X;} +operator4(A) ::= LOG_OR(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;} + +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_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; } + + +%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(); + } +} + +}