00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00026 #include "Parse.hh"
00027 #include "Parse.ih"
00028
00029
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
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 {
00100
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
00120
00121
00122
00123
00124 }
00125 };
00126
00127 #endif
00128
00129 }}}
00130
00131
00132
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
00155
00156
00157
00158
00159
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
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
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
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
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
00332
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
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
00387
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
00396
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
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
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515