Search:

SENF Extensible Network Framework

  • Home
  • Download
  • Wiki
  • BerliOS
  • ChangeLog
  • Browse SVN
  • Bug Tracker
  • Overview
  • Examples
  • HowTos
  • Glossary
  • PPI
  • Packets
  • Scheduler
  • Socket
  • Utils
  • Console
  • Daemon
  • Logger
  • Termlib
  • Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

Parse.cc

Go to the documentation of this file.
00001 // $Id: Parse.cc 1772 2011-03-10 12:45:21Z tho $
00002 //
00003 // Copyright (C) 2008
00004 // Fraunhofer (FOKUS)
00005 // Competence Center NETwork research (NET), St. Augustin, GERMANY
00006 //     Stefan Bund <g0dil@berlios.de>
00007 //
00008 // This program is free software; you can redistribute it and/or modify
00009 // it under the terms of the GNU General Public License as published by
00010 // the Free Software Foundation; either version 2 of the License, or
00011 // (at your option) any later version.
00012 //
00013 // This program is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 // GNU General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU General Public License
00019 // along with this program; if not, write to the
00020 // Free Software Foundation, Inc.,
00021 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00022 
00026 #include "Parse.hh"
00027 #include "Parse.ih"
00028 
00029 // Custom includes
00030 #include <cerrno>
00031 #include <sys/stat.h>
00032 #include <boost/iterator/transform_iterator.hpp>
00033 #include <senf/config.hh>
00034 
00035 #if HAVE_BOOST_SPIRIT_INCLUDE_CLASSIC_HPP
00036 #  include <boost/spirit/include/classic_file_iterator.hpp>
00037 #  include <boost/spirit/include/classic_position_iterator.hpp>
00038 #else
00039 #  include <boost/spirit/iterator/file_iterator.hpp>
00040 #  include <boost/spirit/iterator/position_iterator.hpp>
00041 #endif
00042 
00043 #include <senf/Utils/Exception.hh>
00044 #include <senf/Utils/senfassert.hh>
00045 
00046 //#include "Parse.mpp"
00047 #define prefix_
00048 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00049 
00050 namespace senf {
00051 namespace console {
00052 namespace detail {
00053 
00054 #ifndef DOXYGEN
00055 
00056     struct ParseDispatcher
00057     {
00058         ParseCommandInfo * info_;
00059 
00060         struct BindInfo {
00061             BindInfo( ParseDispatcher & d, ParseCommandInfo & info)
00062                 : dispatcher (d) { dispatcher.info_ = &info; }
00063             ~BindInfo() { dispatcher.info_ = 0; }
00064 
00065             ParseDispatcher & dispatcher;
00066         };
00067 
00068         void beginCommand(std::vector<Token> & command)
00069             { info_->clear();
00070               info_->command(command); }
00071 
00072         void endCommand()
00073             { }
00074 
00075         void pushToken(Token const & token)
00076             { info_->addToken(token); }
00077 
00078         void builtin_cd(std::vector<Token> & path)
00079             { info_->clear();
00080               info_->builtin(ParseCommandInfo::BuiltinCD);
00081               setBuiltinPathArg(path); }
00082 
00083         void builtin_ls(std::vector<Token> & path)
00084             { info_->clear();
00085               info_->builtin(ParseCommandInfo::BuiltinLS);
00086               setBuiltinPathArg(path); }
00087 
00088         void builtin_ll(std::vector<Token> & path)
00089             { info_->clear();
00090               info_->builtin(ParseCommandInfo::BuiltinLL);
00091               setBuiltinPathArg(path); }
00092 
00093         void builtin_lr(std::vector<Token> & path)
00094             { info_->clear();
00095               info_->builtin(ParseCommandInfo::BuiltinLR);
00096               setBuiltinPathArg(path); }
00097 
00098         void pushDirectory()
00099             { // Do NOT call clear since pushDirectory is set in ADDITION
00100               // to an ordinary command (which may be only a directory name)
00101               info_->builtin(ParseCommandInfo::BuiltinPUSHD); }
00102 
00103         void popDirectory()
00104             { info_->clear();
00105               info_->builtin(ParseCommandInfo::BuiltinPOPD); }
00106 
00107         void builtin_exit()
00108             { info_->clear();
00109               info_->builtin(ParseCommandInfo::BuiltinEXIT); }
00110 
00111         void builtin_help(std::vector<Token> & path)
00112             { info_->clear();
00113               info_->builtin(ParseCommandInfo::BuiltinHELP);
00114               setBuiltinPathArg(path); }
00115 
00116         void setBuiltinPathArg(std::vector<Token> & path)
00117             {
00118                 info_->command(path);
00119 //                 pushToken(ArgumentGroupOpenToken());
00120 //                 for (std::vector<Token>::const_iterator i (path.begin());
00121 //                      i != path.end(); ++i)
00122 //                     pushToken(*i);
00123 //                 pushToken(ArgumentGroupCloseToken());
00124             }
00125     };
00126 
00127 #endif
00128 
00129 }}}
00130 
00131 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00132 // senf::console::Token
00133 
00134 prefix_ senf::console::Token::Token(TokenType type, std::string token,
00135                                     detail::FilePositionWithIndex const & pos)
00136     : type_(type), token_ (token), line_ (pos.line), column_ (pos.column), index_ (pos.index)
00137 {}
00138 
00139 
00140 prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Token const & token)
00141 {
00142     static char const * tokenTypeName[] = {
00143         "None",
00144         "PathSeparator",
00145         "ArgumentGroupOpen",
00146         "ArgumentGroupClose",
00147         "DirectoryGroupOpen",
00148         "DirectoryGroupClose",
00149         "CommandTerminator",
00150         "OtherPunctuation",
00151         "BasicString",
00152         "HexString",
00153         "Word" };
00154     // The real table is:
00155     //     static const int bitPosition[32] = {
00156     //          0,  1, 28,  2, 29, 14, 24,  3, 30, 22, 20, 15, 25, 17,  4,  8,
00157     //         31, 27, 13, 23, 21, 19, 16,  7, 26, 12, 18,  6, 11,  5, 10,  9 };
00158     // However, we have replaced all values >= sizeof(tokenTypeName) with 0
00159     // and have added 1 to all the remaining values
00160     static const int bitPosition[32] = {
00161         1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 5, 9,
00162         0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 7, 0, 6, 0, 10 };
00163     // We need to check token.type() against 0 explicitly since 0 and 1 will both be mapped to 0
00164     os << tokenTypeName[ token.type()
00165                          ? bitPosition[(((token.type() & -token.type()) * 0x077CB531UL) >> 27) & 31]
00166                          : 0 ]
00167        << "('"
00168        << token.value()
00169        << "')";
00170     return os;
00171 }
00172 
00173 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00174 // senf::console::ParseCommandInfo
00175 
00176 prefix_ std::ostream & senf::console::operator<<(std::ostream & stream,
00177                                                  ParseCommandInfo const & info)
00178 {
00179     if (info.builtin() == ParseCommandInfo::NoBuiltin) {
00180         ParseCommandInfo::TokensRange::const_iterator i (info.commandPath().begin());
00181         ParseCommandInfo::TokensRange::const_iterator const i_end (info.commandPath().end());
00182         if (i != i_end) {
00183             for (;;) {
00184                 stream << i->value();
00185                 if ( ++i != i_end ) stream << "/";
00186                 else                break;
00187             }
00188         }
00189     }
00190     else {
00191         char const * builtins[] = { 0, "cd", "ls", "lr", "pushd", "popd", "exit", "help" };
00192         stream << "builtin-" << builtins[info.builtin()];
00193     }
00194 
00195     ParseCommandInfo::ArgumentsRange args (info.arguments());
00196     for (ParseCommandInfo::argument_iterator i (args.begin()); i != args.end(); ++i) {
00197         ParseCommandInfo::token_iterator j (i->begin());
00198         stream << " [";
00199         if ( j != i->end() ) {
00200             for (;;) {
00201                 stream << "'" << j->value() << "'";
00202                 if ( ++j != i->end() ) stream << ' ';
00203                 else                   break;
00204             }
00205         }
00206         stream << "]";
00207     }
00208 
00209     return stream;
00210 }
00211 
00212 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00213 // senf::console::ParseCommandInfo::ArgumentIterator
00214 
00215 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::setRange()
00216     const
00217 {
00218     if (b_->is(Token::ArgumentGroupOpen)) {
00219         unsigned level (0);
00220         e_ = b_;
00221         for (;;) {
00222             if (e_->is(Token::ArgumentGroupOpen))
00223                 ++ level;
00224             else if (e_->is(Token::ArgumentGroupClose)) {
00225                 -- level;
00226                 if (level == 0)
00227                     break;
00228             }
00229             ++e_;
00230         }
00231     }
00232     ++ e_;
00233 }
00234 
00235 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::decrement()
00236 {
00237     e_ = b_;
00238     --b_;
00239     if (b_->is(Token::ArgumentGroupClose)) {
00240         unsigned level (0);
00241         for (;;) {
00242             if (b_->is(Token::ArgumentGroupClose))
00243                 ++ level;
00244             else if (b_->is(Token::ArgumentGroupOpen)) {
00245                 -- level;
00246                 if (level == 0)
00247                     break;
00248             }
00249             --b_;
00250         }
00251     }
00252 }
00253 
00254 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00255 // senf::console::CommandParser
00256 
00257 #ifndef DOXYGEN
00258 
00259 struct senf::console::CommandParser::Impl
00260 {
00261     typedef detail::CommandGrammar<detail::ParseDispatcher> Grammar;
00262 
00263     detail::ParseDispatcher dispatcher;
00264     Grammar::Context context;
00265     Grammar grammar;
00266 
00267     Impl() : dispatcher(), context(), grammar(dispatcher, context) {}
00268 };
00269 
00270 #endif
00271 
00272 namespace {
00273 
00274     template <class Error>
00275     void throwParserError(Error const & err)
00276     {
00277         static char const * msg [] = { "end of statement expected",
00278                                        "path expected",
00279                                        "')' expected",
00280                                        "'\"' expected" };
00281         senf::console::detail::FilePositionWithIndex pos (err.where.get_position());
00282         throw senf::console::CommandParser::ParserErrorException(msg[err.descriptor])
00283             << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
00284     }
00285 
00286 }
00287 
00288 namespace boost {
00289 namespace spirit {
00290 #if HAVE_BOOST_SPIRIT_INCLUDE_CLASSIC_HPP
00291 namespace classic {
00292 #endif
00293 
00294     template <>
00295     struct position_policy<senf::console::detail::FilePositionWithIndex>
00296         : public position_policy<file_position>
00297     {
00298         typedef position_policy<file_position> Base;
00299 
00300         void next_line(senf::console::detail::FilePositionWithIndex & pos)
00301             {
00302                 Base::next_line(pos);
00303                 pos.index ++;
00304             }
00305 
00306         void next_char(senf::console::detail::FilePositionWithIndex & pos)
00307             {
00308                 Base::next_char(pos);
00309                 pos.index ++;
00310             }
00311 
00312         void tabulation(senf::console::detail::FilePositionWithIndex & pos)
00313             {
00314                 Base::tabulation(pos);
00315                 pos.index ++;
00316             }
00317     };
00318 
00319 #if HAVE_BOOST_SPIRIT_INCLUDE_CLASSIC_HPP
00320 }
00321 #endif
00322 }}
00323 
00324 prefix_ senf::console::CommandParser::CommandParser()
00325     : impl_ (new Impl())
00326 {}
00327 
00328 prefix_ senf::console::CommandParser::~CommandParser()
00329 {}
00330 
00331 // This template member is placed here, since it is ONLY called from the implementation.  Otherwise,
00332 // we would need to expose the Impl member to the public, which we don't want to do.
00333 
00334 template <class Iterator>
00335 prefix_ Iterator senf::console::CommandParser::parseLoop(Iterator npb, Iterator npe,
00336                                                          std::string const & source, Callback cb)
00337 {
00338     typedef detail::boost_spirit::position_iterator<
00339         Iterator, detail::FilePositionWithIndex> PositionIterator;
00340     PositionIterator b (npb, npe, source);
00341     PositionIterator e (npe, npe, source);
00342     ParseCommandInfo info;
00343     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
00344     detail::boost_spirit::parse_info<PositionIterator> result;
00345 
00346     for (;;) {
00347         result = detail::boost_spirit::parse(
00348             b, e, * impl().grammar.use_parser<Impl::Grammar::SkipParser>());
00349         b = result.stop;
00350         if (b == e)
00351             return e.base();
00352         info.clear();
00353         try {
00354             result = detail::boost_spirit::parse(b, e,
00355                                           impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
00356                                           impl().grammar.use_parser<Impl::Grammar::SkipParser>());
00357         }
00358         catch (detail::boost_spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
00359             if (impl().grammar.incremental && ex.where == e)
00360                 return b.base();
00361             else
00362                 throwParserError(ex);
00363         }
00364         // Otherwise the error handling in the parser is broken
00365         SENF_ASSERT( result.hit, "Internal parser failure (error handling broken?)" );
00366         if (! info.empty())
00367             try {
00368                 cb(info);
00369             }
00370             catch (senf::ExceptionMixin & ex) {
00371                 detail::FilePositionWithIndex pos (result.stop.get_position());
00372                 ex << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
00373                 throw;
00374             }
00375         b = result.stop;
00376     }
00377 }
00378 
00379 prefix_ void senf::console::CommandParser::parse(std::string const & command, Callback cb)
00380 {
00381     parseLoop(command.begin(), command.end(), "<unknown>", cb);
00382 }
00383 
00384 prefix_ void senf::console::CommandParser::parseFile(std::string const & filename, Callback cb)
00385 {
00386     // file_iterator sets errno to EINVAL and returns error when file size is 0
00387     // so we check the file size before
00388     struct stat statBuf;
00389     if (stat( filename.c_str(), &statBuf) != 0)
00390         throw SystemException(filename, errno SENF_EXC_DEBUGINFO);
00391     if (statBuf.st_size == 0) return;
00392     detail::boost_spirit::file_iterator<> i (filename);
00393     if (!i) {
00394         if (errno == 0)
00395             // hmm.. errno==0 but the file_iterator is false; something is wrong but we
00396             // do not know what exactly, so we throw a SystemeException with EINVAL
00397             throw SystemException(filename, EINVAL SENF_EXC_DEBUGINFO);
00398         else
00399             throw SystemException(filename, errno SENF_EXC_DEBUGINFO);
00400     }
00401     detail::boost_spirit::file_iterator<> const i_end (i.make_end());
00402     parseLoop(i, i_end, filename, cb);
00403 }
00404 
00405 prefix_ void senf::console::CommandParser::parseArguments(std::string const & arguments,
00406                                                           ParseCommandInfo & info)
00407 {
00408     typedef detail::boost_spirit::position_iterator<
00409         std::string::const_iterator, detail::FilePositionWithIndex> PositionIterator;
00410     PositionIterator b (arguments.begin(), arguments.end(), std::string("<unknown>"));
00411     PositionIterator e (arguments.end(), arguments.end(), std::string("<unknown>"));
00412     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
00413     detail::boost_spirit::parse_info<PositionIterator> result;
00414     try {
00415         result = detail::boost_spirit::parse( b, e,
00416                                        impl().grammar.use_parser<Impl::Grammar::ArgumentsParser>(),
00417                                        impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
00418     }
00419     catch (detail::boost_spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
00420         throwParserError(ex);
00421     }
00422     if (! result.full) {
00423         detail::FilePositionWithIndex pos (result.stop.get_position());
00424         throw ParserErrorException("argument expected")
00425             << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
00426     }
00427 }
00428 
00429 prefix_ void senf::console::CommandParser::parsePath(std::string const & path,
00430                                                      ParseCommandInfo & info)
00431 {
00432     typedef detail::boost_spirit::position_iterator<
00433         std::string::const_iterator, detail::FilePositionWithIndex> PositionIterator;
00434     PositionIterator b (path.begin(), path.end(), std::string("<unknown>"));
00435     PositionIterator e (path.end(), path.end(), std::string("<unknown>"));
00436     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
00437     detail::boost_spirit::parse_info<PositionIterator> result;
00438     try {
00439         result = detail::boost_spirit::parse( b, e,
00440                                        impl().grammar.use_parser<Impl::Grammar::PathParser>(),
00441                                        impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
00442     }
00443     catch (detail::boost_spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
00444         throwParserError(ex);
00445     }
00446     if (! result.full) {
00447         detail::FilePositionWithIndex pos (result.stop.get_position());
00448         throw ParserErrorException("path expected")
00449             << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
00450     }
00451 }
00452 
00453 struct senf::console::CommandParser::SetIncremental
00454 {
00455     SetIncremental(CommandParser & parser) : parser_ (parser) {
00456         parser_.impl().grammar.incremental = true;
00457     }
00458 
00459     ~SetIncremental() {
00460         parser_.impl().grammar.incremental = false;
00461     }
00462 
00463     CommandParser & parser_;
00464 };
00465 
00466 prefix_ std::string::size_type
00467 senf::console::CommandParser::parseIncremental(std::string const & commands, Callback cb)
00468 {
00469     SetIncremental si (*this);
00470     return std::distance( commands.begin(),
00471                           parseLoop(commands.begin(), commands.end(), "<unknown>", cb) );
00472 }
00473 
00474 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00475 // Character sets
00476 
00477 prefix_ bool senf::console::CommandParser::isSpecialChar(char ch)
00478 {
00479     return Impl::Grammar::special_p.test(ch);
00480 }
00481 
00482 prefix_ bool senf::console::CommandParser::isPunctuationChar(char ch)
00483 {
00484     return Impl::Grammar::punctuation_p.test(ch);
00485 }
00486 
00487 prefix_ bool senf::console::CommandParser::isSpaceChar(char ch)
00488 {
00489     return Impl::Grammar::space_p.test(ch);
00490 }
00491 
00492 prefix_ bool senf::console::CommandParser::isInvalidChar(char ch)
00493 {
00494     return Impl::Grammar::invalid_p.test(ch);
00495 }
00496 
00497 prefix_ bool senf::console::CommandParser::isWordChar(char ch)
00498 {
00499     return Impl::Grammar::word_p.test(ch);
00500 }
00501 
00502 //-/////////////////////////////////////////////////////////////////////////////////////////////////
00503 #undef prefix_
00504 //#include "Parse.mpp"
00505 
00506 
00507 // Local Variables:
00508 // mode: c++
00509 // fill-column: 100
00510 // comment-column: 40
00511 // c-file-style: "senf"
00512 // indent-tabs-mode: nil
00513 // ispell-local-dictionary: "american"
00514 // compile-command: "scons -u test"
00515 // End:

Contact: senf-dev@lists.berlios.de | © 2006-2010 Fraunhofer Institute for Open Communication Systems, Network Research