Console: Make the parser interface callback based
g0dil [Sat, 22 Mar 2008 11:00:39 +0000 (11:00 +0000)]
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

Console/Executor.cc
Console/Executor.hh
Console/Parse.cc
Console/Parse.cci
Console/Parse.hh
Console/Parse.ih
Console/Parse.test.cc
Console/Server.cc
Console/Server.hh
Console/testServer.cc

index 3865656..8203348 100644 (file)
@@ -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;
 }
 
index 7be8db3..08da10f 100644 (file)
@@ -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);
index 1f8a6b2..f143149 100644 (file)
@@ -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<std::string> & 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<std::string> & 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<std::string> & 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<std::string> & 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<std::string> & path)
             {
-                ParserAccess::startArgument(info());
+                ParserAccess::startArgument(info_);
                 for (std::vector<std::string>::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<detail::ParseDispatcher> 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::CommandParser>(),
                                  impl().grammar.use_parser<Impl::Grammar::SkipParser>()
index 0c7c0c6..c85fad8 100644 (file)
@@ -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_;
index 3b059af..9d22ede 100644 (file)
@@ -32,6 +32,7 @@
 #include <boost/utility.hpp>
 #include <boost/scoped_ptr.hpp>
 #include <boost/range/iterator_range.hpp>
+#include <boost/function.hpp>
 
 //#include "Parse.mpp"
 ///////////////////////////////hh.p////////////////////////////////////////
@@ -83,7 +84,12 @@ namespace console {
         typedef boost::iterator_range<argument_iterator> ArgumentsRange;
         typedef boost::iterator_range<token_iterator> 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<void (ParseCommandInfo const &)> 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;
index 54c2c45..3c5a1fc 100644 (file)
@@ -124,25 +124,22 @@ namespace detail {
             : public boost::spirit::grammar_def< boost::spirit::rule<Scanner>, 
                                                  boost::spirit::rule<Scanner> >
         {
-            boost::spirit::rule<Scanner> command, path, argument, word, string, hexstring, token;
-            boost::spirit::rule<Scanner> punctuation, hexbyte, balanced_tokens, simple_argument;
-            boost::spirit::rule<Scanner> complex_argument, builtin, skip;
-
+            boost::spirit::rule<Scanner> 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
                 );
 
index 11d8a38..0143b70 100644 (file)
@@ -51,6 +51,11 @@ namespace
 
         std::ostream & os_;
 
+        void pushDirectory(std::vector<std::string> const & path)
+            { os_ << "pushDirectory( " << senf::stringJoin(path,"/") << " )\n"; }
+        void popDirectory()
+            { os_ << "popDirectory()\n"; }
+
         void beginCommand(std::vector<std::string> 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<std::string> 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<TestParseDispatcher>::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" };
 
index 5790d83..6cd21d9 100644 (file)
@@ -32,6 +32,7 @@
 #include <boost/algorithm/string/trim.hpp>
 #include <boost/iostreams/device/file_descriptor.hpp>
 #include <boost/iostreams/stream.hpp>
+#include <boost/bind.hpp>
 #include "../Utils/senfassert.hh"
 #include "../Utils/membind.hh"
 #include "../Utils/Logger/SenfLog.hh"
@@ -134,17 +135,15 @@ prefix_ void senf::console::Client::clientData(ReadHelper<ClientHandle>::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<void>(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<ClientHandle>::dispatch( handle_, 16384u, ReadUntil("\n"),
index e21eea6..5394302 100644 (file)
@@ -116,7 +116,7 @@ namespace console {
         
         ClientHandle handle_;
         std::string tail_;
-        SingleCommandParser parser_;
+        CommandParser parser_;
         Executor executor_;
         std::string name_;
 
index 8e9323a..3eab4dc 100644 (file)
@@ -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();
 }