• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

src/xpath/XPathParser.cpp

00001 /*
00002  *   This file is part of the Standard Portable Library (SPL).
00003  *
00004  *   SPL is free software: you can redistribute it and/or modify
00005  *   it under the terms of the GNU General Public License as published by
00006  *   the Free Software Foundation, either version 3 of the License, or
00007  *   (at your option) any later version.
00008  *
00009  *   SPL is distributed in the hope that it will be useful,
00010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *   GNU General Public License for more details.
00013  *
00014  *   You should have received a copy of the GNU General Public License
00015  *   along with SPL.  If not, see <http://www.gnu.org/licenses/>.
00016  */
00017 #include <spl/Int32.h>
00018 #include <spl/xml/xpath/private/XPathOpAttrib.h>
00019 #include <spl/xml/xpath/private/XPathOpChildern.h>
00020 #include <spl/xml/xpath/private/XPathOpChildTree.h>
00021 #include <spl/xml/xpath/private/XPathOpError.h>
00022 #include <spl/xml/xpath/private/XPathOpNamedNode.h>
00023 #include <spl/xml/xpath/private/XPathOpRoot.h>
00024 #include <spl/xml/xpath/private/XPathParser.h>
00025 #include <spl/xml/xpath/private/XPathOpPredicate.h>
00026 
00027 XPathParser::XPathParser()
00028 : m_lex(), m_exprStack(), m_out(NULL)
00029 {
00030 }
00031 
00032 XPathParser::XPathParser(const XPathParser& parser)
00033 : m_lex(parser.m_lex), m_out(NULL), m_exprStack(parser.m_exprStack)
00034 {
00035 }
00036 
00037 XPathParser::~XPathParser()
00038 {
00039 }
00040 
00041 XPathParser& XPathParser::operator =(const XPathParser& parser)
00042 {
00043         m_lex = parser.m_lex;
00044         m_exprStack = parser.m_exprStack;
00045         return *this;
00046 }
00047 
00048 Array<XPathOperatorPtr> XPathParser::Parse(const String& expr)
00049 {
00050         Vector<XPathOperatorPtr> ops;
00051         m_out = &ops;
00052         
00053         m_lex.Tokenize(expr);
00054         Expr();
00055         if (m_lex.HasMoreTokens())
00056         {
00057                 throw new XmlException("Parsing stopped at char position " + *Int32::ToString(m_lex.CurrentLexumInputCharPos()), 1, m_lex.CurrentLexumInputCharPos());
00058         }
00059         return Array<XPathOperatorPtr>(*ops.ToArray());
00060 }
00061 
00062 #if defined(DEBUG) || defined(_DEBUG)
00063 void XPathParser::CheckMem() const
00064 {
00065         m_lex.CheckMem();
00066         m_exprStack.CheckMem();
00067 }
00068 
00069 void XPathParser::ValidateMem() const
00070 {
00071         m_lex.ValidateMem();
00072         m_exprStack.ValidateMem();
00073 }
00074 #endif
00075 
00076 void XPathParser::NodeSpec()
00077 {
00078         if (m_lex.CurrentToken() == XPathLex::XP_LITERAL)
00079         {
00080                 String nodename = m_lex.CurrentLexum();
00081 
00082                 if (m_lex.HasMoreTokens() && m_lex.GetRelativeToken(1) == XPathLex::XP_COLON)
00083                 {
00084                         // namespace
00085                         m_lex.Match(XPathLex::XP_LITERAL);
00086                         if (m_lex.Match(XPathLex::XP_COLON) != XPathLex::XP_LITERAL)
00087                         {
00088                                 throw new XmlException("Expected node name after namespace " + nodename, 1, m_lex.CurrentLexumInputCharPos());
00089                         }
00090                         nodename = nodename + ":" + m_lex.CurrentLexum();
00091                 }
00092                 else if (m_lex.HasMoreTokens() && m_lex.GetRelativeToken(1) == XPathLex::XP_LPAR)
00093                 {
00094                         // function call
00095                         String functionName = m_lex.CurrentLexum();
00096 
00097                         m_lex.Match(XPathLex::XP_LITERAL);
00098                         m_lex.Match(XPathLex::XP_LPAR);
00099                         ArgList();
00100                         m_lex.Match(XPathLex::XP_RPAR);
00101                         return;
00102                 }
00103 
00104                 m_out->Add(XPathOpNamedNodePtr(new XPathOpNamedNode(nodename)));
00105                 m_lex.Match(XPathLex::XP_LITERAL);
00106                 return;
00107         }
00108         
00109         if (m_lex.CurrentToken() == XPathLex::XP_DOT)
00110         {
00111                 // match current node
00112                 m_lex.Match(XPathLex::XP_DOT);
00113                 return;
00114         }
00115 
00116         if (m_lex.CurrentToken() == XPathLex::XP_STAR)
00117         {
00118                 // everything, but no operation needed.
00119                 m_lex.Match(XPathLex::XP_STAR);
00120                 return;
00121         }
00122 
00123         if (m_lex.CurrentToken() == XPathLex::XP_DOTDOT)
00124         {
00125                 // match parent node
00126                 m_lex.Match(XPathLex::XP_DOTDOT);
00127                 return;
00128         }
00129 
00130         if (m_lex.CurrentToken() == XPathLex::XP_AT)
00131         {
00132                 m_lex.Match(XPathLex::XP_AT);
00133                 m_out->Add(XPathOpAttribPtr(new XPathOpAttrib()));              
00134                 return;
00135         }
00136 
00137         throw new XmlException("Syntax error, expected node specifier.", 1, m_lex.CurrentLexumInputCharPos());
00138 }
00139 
00140 void XPathParser::NodePath()
00141 {
00142         if (m_lex.CurrentToken() == XPathLex::XP_SLASHSLASH)
00143         {
00144                 // select all
00145                 m_lex.Match(XPathLex::XP_SLASHSLASH);
00146                 m_out->Add(XPathOpChildTreePtr(new XPathOpChildTree()));
00147         }
00148         else if (m_lex.CurrentToken() == XPathLex::XP_SLASH)
00149         {
00150                 // select root
00151                 m_lex.Match(XPathLex::XP_SLASH);
00152                 m_out->Add(XPathOpRootPtr(new XPathOpRoot()));
00153                 if (m_lex.HasMoreTokens())
00154                 {
00155                         // select root's childern
00156                         m_out->Add(XPathOpChildernPtr(new XPathOpChildern()));
00157                 }
00158         }
00159         else if (m_lex.CurrentToken() == XPathLex::XP_LITERAL)
00160         {
00161                 // select root's childern
00162                 m_out->Add(XPathOpChildernPtr(new XPathOpChildern()));
00163         }
00164         else if (m_lex.CurrentToken() == XPathLex::XP_AT)
00165         {
00166                 m_lex.Match(XPathLex::XP_AT);
00167                 m_out->Add(XPathOpAttribPtr(new XPathOpAttrib()));
00168         }
00169         MorePath();
00170 }
00171 
00172 void XPathParser::MorePath()
00173 {
00174         if (!m_lex.HasMoreTokens())
00175         {
00176                 return;
00177         }
00178         if (m_lex.CurrentToken() == XPathLex::XP_LITERAL && m_lex.GetRelativeToken(1) == XPathLex::XP_LPAR)
00179         {
00180                 // function call
00181                 String functionName = m_lex.CurrentLexum();
00182                 m_lex.Match(XPathLex::XP_LITERAL);
00183                 m_lex.Match(XPathLex::XP_LPAR);
00184                 ArgList();
00185                 m_lex.Match(XPathLex::XP_RPAR);
00186         }
00187         else
00188         {
00189                 Axis();
00190                 NodeSpec();
00191                 Predicate();
00192         }
00193         MoreMorePath();
00194 }
00195 
00196 bool XPathParser::Axis()
00197 {
00198         if (m_lex.CurrentToken() != XPathLex::XP_LITERAL)
00199         {
00200                 return false;
00201         }
00202         if (m_lex.GetRelativeToken(1) != XPathLex::XP_COLONCOLON)
00203         {
00204                 return false;
00205         }
00206 
00207         m_out->Add(XPathOpErrorPtr(new XPathOpError("axis operator not supported")));
00208 
00209         String axis = m_lex.CurrentLexum();
00210         m_lex.Match(XPathLex::XP_LITERAL);
00211         m_lex.Match(XPathLex::XP_COLONCOLON);
00212         
00213         return true;
00214 }
00215 
00216 bool XPathParser::Predicate()
00217 {
00218         if (m_lex.CurrentToken() != XPathLex::XP_LBRAC)
00219         {
00220                 return false;
00221         }
00222         m_lex.Match(XPathLex::XP_LBRAC);
00223         
00224         Vector<XPathOperatorPtr> *outbak = m_out;
00225         XPathOpPredicatePtr oppred(new XPathOpPredicate());
00226         m_out = &oppred->Operators();
00227         
00228         Expr();
00229         
00230         if (m_exprStack.Count() != 2)
00231         {
00232                 outbak->Add(XPathOpErrorPtr(new XPathOpError("Unsupported predicated expression.")));
00233         }
00234         else
00235         {       
00236                 *oppred->Arg() = m_exprStack.Peek();
00237                 String op = m_exprStack.Tail();
00238                 
00239                 if (op.Equals('='))
00240                 {
00241                         oppred->BinOp() = XPathOpPredicate::OP_EQ;
00242                 }
00243                 else if (op.Equals('<'))
00244                 {
00245                         oppred->BinOp() = XPathOpPredicate::OP_LT;
00246                 }
00247                 else if (op.Equals('>'))
00248                 {
00249                         oppred->BinOp() = XPathOpPredicate::OP_GT;
00250                 }
00251                 else if (op.Equals("<="))
00252                 {
00253                         oppred->BinOp() = XPathOpPredicate::OP_LTEQ;
00254                 }
00255                 else if (op.Equals(">="))
00256                 {
00257                         oppred->BinOp() = XPathOpPredicate::OP_GTEQ;
00258                 }
00259                 else if (op.Equals("!="))
00260                 {
00261                         oppred->BinOp() = XPathOpPredicate::OP_NEQ;
00262                 }
00263                 else
00264                 {
00265                         throw new SyntaxException("Unsupported predicated operator " + op);             
00266                 }
00267         }
00268         
00269         m_exprStack.Clear();
00270         m_out = outbak;
00271         m_out->Add(oppred);
00272         
00273         m_lex.Match(XPathLex::XP_RBRAC);
00274 
00275         return true;
00276 }
00277 
00278 bool XPathParser::MoreMorePath()
00279 {
00280         if (m_lex.CurrentToken() == XPathLex::XP_SLASH)
00281         {
00282                 m_lex.Match(XPathLex::XP_SLASH);
00283                 m_out->Add(XPathOpChildernPtr(new XPathOpChildern()));
00284                 
00285                 if (! m_lex.HasMoreTokens())
00286                 {
00287                         throw new SyntaxException("xpath cannot end with a slash");
00288                 }
00289         }
00290         else if (m_lex.CurrentToken() == XPathLex::XP_SLASHSLASH)
00291         {
00292                 m_lex.Match(XPathLex::XP_SLASHSLASH);
00293                 m_out->Add(XPathOpChildTreePtr(new XPathOpChildTree()));
00294                 
00295                 if (! m_lex.HasMoreTokens())
00296                 {
00297                         throw new SyntaxException("xpath cannot end with a slash slash");
00298                 }
00299         }
00300         else if (m_lex.CurrentToken() == XPathLex::XP_AT)
00301         {
00302                 m_lex.Match(XPathLex::XP_AT);
00303                 m_out->Add(XPathOpAttribPtr(new XPathOpAttrib()));              
00304         }
00305         else if (m_lex.CurrentToken() == XPathLex::XP_STAR)
00306         {
00307                 // everything, do nothing
00308                 m_lex.Match(XPathLex::XP_STAR);
00309         }
00310         else
00311         {
00312                 return false;
00313         }
00314         
00315         MorePath();
00316 
00317         return true;
00318 }
00319 
00320 bool XPathParser::Expr()
00321 {
00322         if (!m_lex.HasMoreTokens())
00323         {
00324                 return false;
00325         }
00326 
00327         if (m_lex.CurrentToken() == XPathLex::XP_LPAR)
00328         {
00329                 return false;
00330         }
00331 
00332         LogOp();
00333 
00334         if (MoreLogOp())
00335         {
00336                 MoreExpr();
00337         }
00338 
00339         return true;
00340 }
00341 
00342 bool XPathParser::MoreExpr()
00343 {
00344         if (m_lex.CurrentToken() == XPathLex::XP_PIPE)
00345         {
00346                 m_lex.Match(XPathLex::XP_PIPE);
00347                 Expr();
00348                 return true;
00349         }
00350 
00351         return false;
00352 }
00353 
00354 void XPathParser::LogOp()
00355 {
00356         RelOp();
00357         MoreRelOps();
00358 }
00359 
00360 bool XPathParser::MoreLogOp()
00361 {
00362         switch(m_lex.CurrentToken())
00363         {
00364         case XPathLex::XP_AND:
00365                 m_lex.Match(XPathLex::XP_AND);
00366                 break;
00367         case XPathLex::XP_OR:
00368                 m_lex.Match(XPathLex::XP_OR);
00369                 break;
00370         case XPathLex::XP_NEQ:
00371                 m_exprStack.Add(m_lex.CurrentLexum());
00372                 m_lex.Match(XPathLex::XP_NEQ);
00373                 break;
00374         case XPathLex::XP_EQ:
00375                 m_exprStack.Add(m_lex.CurrentLexum());
00376                 m_lex.Match(XPathLex::XP_EQ);
00377                 break;
00378         default:
00379                 return false;
00380         }
00381 
00382         LogOp();
00383         MoreLogOp();
00384 
00385         return true;
00386 }
00387 
00388 bool XPathParser::MoreRelOps()
00389 {
00390         switch(m_lex.CurrentToken())
00391         {
00392         case XPathLex::XP_LT:
00393                 m_exprStack.Add(m_lex.CurrentLexum());
00394                 m_lex.Match(XPathLex::XP_LT);
00395                 break;
00396         case XPathLex::XP_GT:
00397                 m_exprStack.Add(m_lex.CurrentLexum());
00398                 m_lex.Match(XPathLex::XP_GT);
00399                 break;
00400         case XPathLex::XP_LTEQ:
00401                 m_exprStack.Add(m_lex.CurrentLexum());
00402                 m_lex.Match(XPathLex::XP_LTEQ);
00403                 break;
00404         case XPathLex::XP_GTEQ:
00405                 m_exprStack.Add(m_lex.CurrentLexum());
00406                 m_lex.Match(XPathLex::XP_GTEQ);
00407                 break;
00408         default:
00409                 return false;
00410         }
00411 
00412         RelOp();
00413         MoreRelOps();
00414 
00415         return true;
00416 }
00417 
00418 void XPathParser::RelOp()
00419 {
00420         Term();
00421         MoreTerms();
00422 }
00423 
00424 void XPathParser::Term()
00425 {
00426         Factor();
00427         MoreFactors();
00428 }
00429 
00430 bool XPathParser::MoreTerms()
00431 {
00432         if (m_lex.CurrentToken() == XPathLex::XP_PLUS)
00433         {
00434                 m_lex.Match(XPathLex::XP_PLUS);
00435         }
00436         else if (m_lex.CurrentToken() == XPathLex::XP_MIN)
00437         {
00438                 m_lex.Match(XPathLex::XP_MIN);
00439         }
00440         else
00441         {
00442                 return false;
00443         }
00444 
00445         Term();
00446         MoreTerms();
00447         return true;
00448 }
00449 
00450 void XPathParser::Factor()
00451 {
00452         PathExpr();
00453         MoreFactors();
00454 }
00455 
00456 bool XPathParser::MoreFactors()
00457 {
00458         if (!m_lex.HasMoreTokens())
00459         {
00460                 return false;
00461         }
00462 
00463         switch(m_lex.CurrentToken())
00464         {
00465         case XPathLex::XP_STAR:
00466                 m_lex.Match(XPathLex::XP_STAR);
00467                 break;
00468         case XPathLex::XP_SLASH:
00469                 m_lex.Match(XPathLex::XP_SLASH);
00470                 break;
00471         case XPathLex::XP_MOD:
00472                 m_lex.Match(XPathLex::XP_MOD);
00473                 break;
00474         case XPathLex::XP_DIV:
00475                 m_lex.Match(XPathLex::XP_DIV);
00476                 break;
00477         default:
00478                 return false;
00479         }
00480 
00481         Factor();
00482         MoreFactors();
00483 
00484         return true;
00485 }
00486 
00487 bool XPathParser::PathExpr()
00488 {
00489         switch(m_lex.CurrentToken())
00490         {
00491         case XPathLex::XP_DOLLAR:
00492                 m_lex.Match(XPathLex::XP_DOLLAR);
00493                 m_lex.Match(XPathLex::XP_LITERAL);
00494                 break;
00495         case XPathLex::XP_LPAR:
00496                 m_lex.Match(XPathLex::XP_LPAR);
00497                 Expr();
00498                 m_lex.Match(XPathLex::XP_RPAR);
00499                 break;
00500         case XPathLex::XP_INT:
00501                 m_exprStack.Add(m_lex.CurrentLexum());
00502                 m_lex.Match(XPathLex::XP_INT);
00503                 break;
00504         case XPathLex::XP_FLOAT:
00505                 m_exprStack.Add(m_lex.CurrentLexum());
00506                 m_lex.Match(XPathLex::XP_FLOAT);
00507                 break;
00508         case XPathLex::XP_STRING:
00509                 m_exprStack.Add(m_lex.CurrentLexum());
00510                 m_lex.Match(XPathLex::XP_STRING);
00511                 break;
00512         case XPathLex::XP_SLASHSLASH:
00513         case XPathLex::XP_SLASH:
00514         case XPathLex::XP_LITERAL:
00515         case XPathLex::XP_LBRAC:
00516         case XPathLex::XP_DOT:
00517         case XPathLex::XP_DOTDOT:
00518         case XPathLex::XP_STAR:
00519         case XPathLex::XP_AT:
00520                 NodePath();
00521                 break;
00522         default:
00523                 return false;
00524         }
00525 
00526         return true;
00527 }
00528 
00529 bool XPathParser::ArgList()
00530 {
00531         if (m_lex.CurrentToken() == XPathLex::XP_RPAR)
00532         {
00533                 return false;
00534         }
00535 
00536         Expr();
00537         MoreArgs();
00538 
00539         return true;
00540 }
00541 
00542 bool XPathParser::MoreArgs()
00543 {
00544         if (m_lex.CurrentToken() == XPathLex::XP_COMMA)
00545         {
00546                 return false;
00547         }
00548 
00549         while (m_lex.CurrentToken() == XPathLex::XP_COMMA)
00550         {
00551                 m_lex.Match(XPathLex::XP_COMMA);
00552                 Expr();
00553         }
00554 
00555         return true;
00556 }