From: g0dil Date: Sat, 22 Mar 2008 11:00:39 +0000 (+0000) Subject: Console: Make the parser interface callback based X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=d620e7ff9b68377ea20ca266c23cc3f05781868c;p=senf.git Console: Make the parser interface callback based Console: Implement multiple-command statements and command grouping git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@756 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Console/Executor.cc b/Console/Executor.cc index 3865656..8203348 100644 --- a/Console/Executor.cc +++ b/Console/Executor.cc @@ -40,6 +40,8 @@ prefix_ bool senf::console::Executor::operator()(ParseCommandInfo const & comman { # warning Implement Executor::operator() SENF_LOG(( "Executing: " << command )); + if (command.builtin() == ParseCommandInfo::BuiltinEXIT) + throw ExitException(); return true; } diff --git a/Console/Executor.hh b/Console/Executor.hh index 7be8db3..08da10f 100644 --- a/Console/Executor.hh +++ b/Console/Executor.hh @@ -48,6 +48,8 @@ namespace console { typedef boost::iterator_range< ParseCommandInfo::argument_iterator> Arguments; + struct ExitException {}; // NOT derived from std::exception ! + /////////////////////////////////////////////////////////////////////////// bool operator()(ParseCommandInfo const & command, std::ostream & output); diff --git a/Console/Parse.cc b/Console/Parse.cc index 1f8a6b2..f143149 100644 --- a/Console/Parse.cc +++ b/Console/Parse.cc @@ -67,68 +67,76 @@ namespace detail { struct ParseDispatcher { - ParseDispatcher() - : info_ (0) {} - - ParseCommandInfo * info_; - - ParseCommandInfo & info() { - SENF_ASSERT( info_ ); - return *info_; - } + ParseCommandInfo info_; + CommandParser::Callback cb_; struct BindInfo { - BindInfo( ParseDispatcher & d, ParseCommandInfo & i) - : dispatcher (d) { dispatcher.info_ = &i; } - - ~BindInfo() { dispatcher.info_ = 0; } + BindInfo( ParseDispatcher & d, CommandParser::Callback cb) + : dispatcher (d) { dispatcher.cb_ = cb; } + ~BindInfo() { dispatcher.cb_ = 0; } ParseDispatcher & dispatcher; }; void beginCommand(std::vector & command) - { ParserAccess::init(info()); - ParserAccess::setCommand(info(), command); } + { ParserAccess::init(info_); + ParserAccess::setCommand(info_, command); } void endCommand() - { ParserAccess::finalize(info()); } + { ParserAccess::finalize(info_); cb_(info_); } void pushArgument(std::string const & argument) - { ParserAccess::startArgument(info()); - ParserAccess::addToken(info(), ParserAccess::makeToken(argument)); - ParserAccess::endArgument(info()); } + { ParserAccess::startArgument(info_); + ParserAccess::addToken(info_, ParserAccess::makeToken(argument)); + ParserAccess::endArgument(info_); } void openGroup() - { ParserAccess::startArgument(info()); } + { ParserAccess::startArgument(info_); } void closeGroup() - { ParserAccess::endArgument(info()); } + { ParserAccess::endArgument(info_); } void pushPunctuation(std::string const & token) - { ParserAccess::addToken(info(), ParserAccess::makeToken(token)); } + { ParserAccess::addToken(info_, ParserAccess::makeToken(token)); } void pushWord(std::string const & token) - { ParserAccess::addToken(info(), ParserAccess::makeToken(token)); } + { ParserAccess::addToken(info_, ParserAccess::makeToken(token)); } void builtin_cd(std::vector & path) - { ParserAccess::init(info()); - ParserAccess::setBuiltin(info(), ParseCommandInfo::BuiltinCD); + { ParserAccess::init(info_); + ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinCD); setBuiltinPathArg(path); - ParserAccess::finalize(info()); } + ParserAccess::finalize(info_); cb_(info_); } void builtin_ls(std::vector & path) - { ParserAccess::init(info()); - ParserAccess::setBuiltin(info(), ParseCommandInfo::BuiltinLS); + { ParserAccess::init(info_); + ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinLS); setBuiltinPathArg(path); - ParserAccess::finalize(info()); } + ParserAccess::finalize(info_); cb_(info_); } + + void pushDirectory(std::vector & path) + { ParserAccess::init(info_); + ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinPUSHD); + setBuiltinPathArg(path); + ParserAccess::finalize(info_); cb_(info_); } + + void popDirectory() + { ParserAccess::init(info_); + ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinPOPD); + ParserAccess::finalize(info_); cb_(info_); } + + void builtin_exit() + { ParserAccess::init(info_); + ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinEXIT); + ParserAccess::finalize(info_); cb_(info_); } void setBuiltinPathArg(std::vector & path) { - ParserAccess::startArgument(info()); + ParserAccess::startArgument(info_); for (std::vector::const_iterator i (path.begin()); i != path.end(); ++i) - ParserAccess::addToken(info(), ParserAccess::makeToken(*i)); - ParserAccess::endArgument(info()); + ParserAccess::addToken(info_, ParserAccess::makeToken(*i)); + ParserAccess::endArgument(info_); } }; @@ -139,13 +147,13 @@ namespace detail { struct senf::console::ParseCommandInfo::MakeRange { + typedef ParseCommandInfo::argument_value_type result_type; + MakeRange() {} MakeRange(ParseCommandInfo::token_iterator b) : b_ (b) {} - + senf::console::ParseCommandInfo::token_iterator b_; - - typedef ParseCommandInfo::argument_value_type result_type; - + result_type operator()(TempArguments::iterator::value_type const & v) const { return result_type( b_ + v.first, b_ + v.second ); } @@ -170,7 +178,7 @@ prefix_ std::ostream & senf::console::operator<<(std::ostream & stream, if (info.builtin() == ParseCommandInfo::NoBuiltin) stream << senf::stringJoin(info.commandPath(), "/"); else { - char const * builtins[] = { "", "cd", "ls" }; + char const * builtins[] = { "", "cd", "ls", "pushd", "popd", "exit" }; stream << "builtin-" << builtins[info.builtin()]; } @@ -192,9 +200,9 @@ prefix_ std::ostream & senf::console::operator<<(std::ostream & stream, } /////////////////////////////////////////////////////////////////////////// -// senf::console::SingleCommandParser +// senf::console::CommandParser -struct senf::console::SingleCommandParser::Impl +struct senf::console::CommandParser::Impl { typedef detail::CommandGrammar Grammar; @@ -205,17 +213,17 @@ struct senf::console::SingleCommandParser::Impl Impl() : dispatcher(), context(), grammar(dispatcher, context) {} }; -prefix_ senf::console::SingleCommandParser::SingleCommandParser() +prefix_ senf::console::CommandParser::CommandParser() : impl_ (new Impl()) {} -prefix_ senf::console::SingleCommandParser::~SingleCommandParser() +prefix_ senf::console::CommandParser::~CommandParser() {} -prefix_ bool senf::console::SingleCommandParser::parseCommand(std::string command, - ParseCommandInfo & info) +prefix_ bool senf::console::CommandParser::parse(std::string command, Callback cb) { - detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info); + detail::ParseDispatcher::BindInfo bind (impl().dispatcher, cb); +# warning don't use c_str() in parse and add istream parser. Implement error checking in parser. return boost::spirit::parse( command.c_str(), impl().grammar.use_parser(), impl().grammar.use_parser() diff --git a/Console/Parse.cci b/Console/Parse.cci index 0c7c0c6..c85fad8 100644 --- a/Console/Parse.cci +++ b/Console/Parse.cci @@ -117,7 +117,7 @@ prefix_ void senf::console::ParseCommandInfo::addToken(ArgumentToken const & tok /////////////////////////////////////////////////////////////////////////// // senf::console::SingleCommandParser -prefix_ senf::console::SingleCommandParser::Impl & senf::console::SingleCommandParser::impl() +prefix_ senf::console::CommandParser::Impl & senf::console::CommandParser::impl() { SENF_ASSERT(impl_); return *impl_; diff --git a/Console/Parse.hh b/Console/Parse.hh index 3b059af..9d22ede 100644 --- a/Console/Parse.hh +++ b/Console/Parse.hh @@ -32,6 +32,7 @@ #include #include #include +#include //#include "Parse.mpp" ///////////////////////////////hh.p//////////////////////////////////////// @@ -83,7 +84,12 @@ namespace console { typedef boost::iterator_range ArgumentsRange; typedef boost::iterator_range TokensRange; - enum BuiltinCommand { NoBuiltin, BuiltinCD, BuiltinLS }; + enum BuiltinCommand { NoBuiltin, + BuiltinCD, + BuiltinLS, + BuiltinPUSHD, + BuiltinPOPD, + BuiltinEXIT }; BuiltinCommand builtin() const; CommandPathRange commandPath() const; @@ -120,24 +126,26 @@ namespace console { /** \brief */ - class SingleCommandParser + class CommandParser : boost::noncopyable { public: /////////////////////////////////////////////////////////////////////////// // Types + typedef boost::function Callback; + /////////////////////////////////////////////////////////////////////////// ///\name Structors and default members ///@{ - SingleCommandParser(); - ~SingleCommandParser(); + CommandParser(); + ~CommandParser(); ///@} /////////////////////////////////////////////////////////////////////////// - bool parseCommand(std::string command, ParseCommandInfo & info); + bool parse(std::string command, Callback cb); private: struct Impl; diff --git a/Console/Parse.ih b/Console/Parse.ih index 54c2c45..3c5a1fc 100644 --- a/Console/Parse.ih +++ b/Console/Parse.ih @@ -124,25 +124,22 @@ namespace detail { : public boost::spirit::grammar_def< boost::spirit::rule, boost::spirit::rule > { - boost::spirit::rule command, path, argument, word, string, hexstring, token; - boost::spirit::rule punctuation, hexbyte, balanced_tokens, simple_argument; - boost::spirit::rule complex_argument, builtin, skip; - + boost::spirit::rule command, path, argument, word, string, hexstring, token, + punctuation, hexbyte, balanced_tokens, simple_argument, complex_argument, builtin, + skip, commands, block, statement; boost::spirit::chset<> special_p, punctuation_p, space_p, invalid_p, word_p; - boost::spirit::distinct_parser<> keyword_p; definition(CommandGrammar const & self) : // Characters with a special meaning within the parser - // and characters reserved for future extensions special_p ("/(){};"), // Characters which are returned as punctuation tokens punctuation_p (",="), - // Whitespace characters (we don't want newlines in there) - space_p (" \t"), + // Whitespace characters + space_p (" \t\n\r"), // Invalid characters: All chars below \x20 (space) which are not space_p // (don't put a \0 in the chset<> argument *string* ...) @@ -160,6 +157,15 @@ namespace detail { using namespace boost::spirit; typedef ParseDispatcher PD; + commands + = * command + ; + + command + = builtin + | path >> ( block | statement ) + ; + builtin = keyword_p("cd") >> path @@ -167,16 +173,23 @@ namespace detail { boost::ref(self.context.path)) ] | keyword_p("ls") >> ! path - >> eps_p [ self.dispatch(&PD::builtin_cd, + >> eps_p [ self.dispatch(&PD::builtin_ls, boost::ref(self.context.path)) ] + | keyword_p("exit") [ self.dispatch(&PD::builtin_exit) ] ; - command - = builtin - | path [ self.dispatch(&PD::beginCommand, + block + = ch_p('{') [ self.dispatch(&PD::pushDirectory, + boost::ref(self.context.path)) ] + >> * command + >> ch_p('}') [ self.dispatch(&PD::popDirectory) ] + ; + + statement + = eps_p [ self.dispatch(&PD::beginCommand, boost::ref(self.context.path)) ] >> * argument - >> ! ch_p(';') + >> (ch_p(';') | end_p) >> eps_p [ self.dispatch(&PD::endCommand) ] ; @@ -194,8 +207,8 @@ namespace detail { complex_argument // Argument consists of multiple tokens = ch_p('(') [ self.dispatch(&PD::openGroup) ] - >> * token - >> ch_p(')') [ self.dispatch(&PD::closeGroup) ] + >> * token + >> ch_p(')') [ self.dispatch(&PD::closeGroup) ] ; string // Returns value in context.str @@ -253,8 +266,7 @@ namespace detail { ; skip - = space_p - | comment_p('#') + = space_p | comment_p('#') ; BOOST_SPIRIT_DEBUG_TRACE_RULE(command,1); @@ -272,7 +284,7 @@ namespace detail { BOOST_SPIRIT_DEBUG_TRACE_RULE(builtin,1); start_parsers( - command, // CommandParser + commands, // CommandParser skip // SkipParser ); diff --git a/Console/Parse.test.cc b/Console/Parse.test.cc index 11d8a38..0143b70 100644 --- a/Console/Parse.test.cc +++ b/Console/Parse.test.cc @@ -51,6 +51,11 @@ namespace std::ostream & os_; + void pushDirectory(std::vector const & path) + { os_ << "pushDirectory( " << senf::stringJoin(path,"/") << " )\n"; } + void popDirectory() + { os_ << "popDirectory()\n"; } + void beginCommand(std::vector const & command) { os_ << "beginCommand( " << senf::stringJoin(command, "/") << " )\n"; } void endCommand() @@ -71,10 +76,12 @@ namespace { os_ << "builtin_cd( " << senf::stringJoin(path, "/") << " )\n"; } void builtin_ls(std::vector const & path) { os_ << "builtin_cd( " << senf::stringJoin(path, "/") << " )\n"; } + void builtin_exit() + { os_ << "builtin_exit()\n"; } }; } -BOOST_AUTO_UNIT_TEST(commandParser) +BOOST_AUTO_UNIT_TEST(commandGrammar) { senf::console::detail::CommandGrammar::Context context; std::stringstream ss; @@ -117,9 +124,15 @@ BOOST_AUTO_UNIT_TEST(commandParser) "endCommand()\n" ); } -BOOST_AUTO_UNIT_TEST(singleCommandParser) +namespace { + senf::console::ParseCommandInfo info; + void setInfo(senf::console::ParseCommandInfo const & i) + { info = i; } +} + +BOOST_AUTO_UNIT_TEST(commandParser) { - senf::console::SingleCommandParser parser; + senf::console::CommandParser parser; char const text[] = "# Comment\n" @@ -131,8 +144,7 @@ BOOST_AUTO_UNIT_TEST(singleCommandParser) " x\"01 02 # Inner comment\n" " 0304\""; - senf::console::ParseCommandInfo info; - BOOST_CHECK( parser.parseCommand(text, info) ); + BOOST_CHECK( parser.parse(text, &setInfo) ); char const * path[] = { "doo", "bii", "doo" }; diff --git a/Console/Server.cc b/Console/Server.cc index 5790d83..6cd21d9 100644 --- a/Console/Server.cc +++ b/Console/Server.cc @@ -32,6 +32,7 @@ #include #include #include +#include #include "../Utils/senfassert.hh" #include "../Utils/membind.hh" #include "../Utils/Logger/SenfLog.hh" @@ -134,17 +135,15 @@ prefix_ void senf::console::Client::clientData(ReadHelper::ptr hel tail_ = helper->tail(); boost::trim(data); // Gets rid of superfluous \r or \n characters - if (data == "exit") { + try { + if (! parser_.parse(data, boost::bind(boost::ref(executor_), _1, boost::ref(out_)))) + out_ << "syntax error" << std::endl; + } + catch (Executor::ExitException &) { // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER stopClient RETURNS stopClient(); return; - } - - ParseCommandInfo command; - if (parser_.parseCommand(data, command)) - executor_(command, out_); - else - out_ << "syntax error" << std::endl; + } out_ << name_ << "# " << std::flush; ReadHelper::dispatch( handle_, 16384u, ReadUntil("\n"), diff --git a/Console/Server.hh b/Console/Server.hh index e21eea6..5394302 100644 --- a/Console/Server.hh +++ b/Console/Server.hh @@ -116,7 +116,7 @@ namespace console { ClientHandle handle_; std::string tail_; - SingleCommandParser parser_; + CommandParser parser_; Executor executor_; std::string name_; diff --git a/Console/testServer.cc b/Console/testServer.cc index 8e9323a..3eab4dc 100644 --- a/Console/testServer.cc +++ b/Console/testServer.cc @@ -41,7 +41,7 @@ int main(int, char const **) senf::log::ConsoleTarget::instance().route< senf::SenfLog, senf::log::NOTICE >(); senf::console::Server::start( senf::INet4SocketAddress("127.0.0.1:23232") ) - .name("testServer "); + .name("testServer"); senf::Scheduler::instance().process(); }