Socket: Documentation for new addressing classes
[senf.git] / Console / Parse.cc
index f143149..57a5532 100644 (file)
 #include "Parse.ih"
 
 // Custom includes
-#include "../Utils/String.hh"
+#include <cerrno>
 #include <boost/iterator/transform_iterator.hpp>
+#include <boost/spirit/iterator/file_iterator.hpp>
+#include "../Utils/Exception.hh"
 
 //#include "Parse.mpp"
 #define prefix_
@@ -38,147 +40,127 @@ namespace senf {
 namespace console {
 namespace detail {
 
-    struct ParserAccess
-    {
-        static void init(ParseCommandInfo & info)
-            { info.init(); }
-
-        static void setBuiltin(ParseCommandInfo & info, ParseCommandInfo::BuiltinCommand builtin)
-            { info.setBuiltin(builtin); }
-
-        static void setCommand(ParseCommandInfo & info, std::vector<std::string> & commandPath)
-            { info.setCommand(commandPath); }
-
-        static void startArgument(ParseCommandInfo & info)
-            { info.startArgument(); }
-
-        static void endArgument(ParseCommandInfo & info)
-            { info.endArgument(); }
-
-        static void addToken(ParseCommandInfo & info, ArgumentToken const & token)
-            { info.addToken(token); }
-
-        static void finalize(ParseCommandInfo & info)
-            { info.finalize(); }
-
-        static ArgumentToken makeToken(std::string const & token)
-            { return ArgumentToken(token); }
-    };
+#ifndef DOXYGEN
 
     struct ParseDispatcher
     {
-        ParseCommandInfo info_;
-        CommandParser::Callback cb_;
+        ParseCommandInfo * info_;
 
         struct BindInfo {
-            BindInfo( ParseDispatcher & d, CommandParser::Callback cb)
-                : dispatcher (d) { dispatcher.cb_ = cb; }
-            ~BindInfo() { dispatcher.cb_  = 0; }
+            BindInfo( ParseDispatcher & d, ParseCommandInfo & info)
+                : dispatcher (d) { dispatcher.info_ = &info; }
+            ~BindInfo() { dispatcher.info_ = 0; }
 
             ParseDispatcher & dispatcher;
         };
 
-        void beginCommand(std::vector<std::string> & command)
-            { ParserAccess::init(info_);
-              ParserAccess::setCommand(info_, command); }
+        void beginCommand(std::vector<Token> & command)
+            { info_->clear();
+              info_->command(command); }
 
         void endCommand()
-            { ParserAccess::finalize(info_); cb_(info_); }
-
-        void pushArgument(std::string const & argument)
-            { ParserAccess::startArgument(info_); 
-              ParserAccess::addToken(info_, ParserAccess::makeToken(argument)); 
-              ParserAccess::endArgument(info_); }
+            { }
 
-        void openGroup()
-            { ParserAccess::startArgument(info_); }
+        void pushToken(Token const & token)
+            { info_->addToken(token); }
 
-        void closeGroup()
-            { ParserAccess::endArgument(info_); }
+        void builtin_cd(std::vector<Token> & path)
+            { info_->clear();
+              info_->builtin(ParseCommandInfo::BuiltinCD);
+              setBuiltinPathArg(path); }
 
-        void pushPunctuation(std::string const & token)
-            { ParserAccess::addToken(info_, ParserAccess::makeToken(token)); }
+        void builtin_ls(std::vector<Token> & path)
+            { info_->clear();
+              info_->builtin(ParseCommandInfo::BuiltinLS);
+              setBuiltinPathArg(path); }
 
-        void pushWord(std::string const & token)
-            { ParserAccess::addToken(info_, ParserAccess::makeToken(token)); }
-
-        void builtin_cd(std::vector<std::string> & path)
-            { ParserAccess::init(info_);
-              ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinCD);
-              setBuiltinPathArg(path);
-              ParserAccess::finalize(info_); cb_(info_); }
-
-        void builtin_ls(std::vector<std::string> & path)
-            { ParserAccess::init(info_);
-              ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinLS);
-              setBuiltinPathArg(path);
-              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 pushDirectory(std::vector<Token> & path)
+            { info_->clear();
+              info_->builtin(ParseCommandInfo::BuiltinPUSHD);
+              setBuiltinPathArg(path); }
 
         void popDirectory()
-            { ParserAccess::init(info_);
-              ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinPOPD);
-              ParserAccess::finalize(info_); cb_(info_); }
+            { info_->clear();
+              info_->builtin(ParseCommandInfo::BuiltinPOPD); }
         
         void builtin_exit()
-            { ParserAccess::init(info_);
-              ParserAccess::setBuiltin(info_, ParseCommandInfo::BuiltinEXIT);
-              ParserAccess::finalize(info_); cb_(info_); }
+            { info_->clear();
+              info_->builtin(ParseCommandInfo::BuiltinEXIT); }
 
-        void setBuiltinPathArg(std::vector<std::string> & path)
+        void builtin_help(std::vector<Token> & path)
+            { info_->clear();
+              info_->builtin(ParseCommandInfo::BuiltinHELP);
+              setBuiltinPathArg(path); }
+
+        void setBuiltinPathArg(std::vector<Token> & path)
             {
-                ParserAccess::startArgument(info_);
-                for (std::vector<std::string>::const_iterator i (path.begin());
+                pushToken(ArgumentGroupOpenToken());
+                for (std::vector<Token>::const_iterator i (path.begin());
                      i != path.end(); ++i)
-                    ParserAccess::addToken(info_, ParserAccess::makeToken(*i));
-                ParserAccess::endArgument(info_);
+                    pushToken(*i);
+                pushToken(ArgumentGroupCloseToken());
             }
     };
 
+#endif
+
 }}}
 
 ///////////////////////////////////////////////////////////////////////////
-// senf::console::ParseCommandInfo
-
-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_;
-    
-    result_type operator()(TempArguments::iterator::value_type const & v) const {
-        return result_type( b_ + v.first, b_ + v.second );
-    }
-};
+// senf::console::Token
 
-prefix_ void senf::console::ParseCommandInfo::finalize()
+prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Token const & token)
 {
-    arguments_.resize( tempArguments_.size() );
-
-    std::copy( boost::make_transform_iterator( tempArguments_.begin(), 
-                                               MakeRange(tokens_.begin()) ),
-               boost::make_transform_iterator( tempArguments_.end(), 
-                                               MakeRange() ),
-               arguments_.begin() );
-
-    tempArguments_.clear();
+    static char const * tokenTypeName[] = {
+        "None",
+        "PathSeparator",
+        "ArgumentGroupOpen",
+        "ArgumentGroupClose",
+        "DirectoryGroupOpen",
+        "DirectoryGroupClose",
+        "CommandTerminator",
+        "OtherPunctuation",
+        "BasicString",
+        "HexString",
+        "Word" };
+    // The real table is:
+    //     static const int bitPosition[32] = {
+    //          0,  1, 28,  2, 29, 14, 24,  3, 30, 22, 20, 15, 25, 17,  4,  8, 
+    //         31, 27, 13, 23, 21, 19, 16,  7, 26, 12, 18,  6, 11,  5, 10,  9 };
+    // However, we have replaced all values >= sizeof(tokenTypeName) with 0
+    // and have added 1 to all the remaining values
+    static const int bitPosition[32] = {
+        1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 5, 9, 
+        0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 7, 0, 6, 0, 10 };
+    // We need to check token.type() against 0 explicitly since 0 and 1 will both be mapped to 0
+    os << tokenTypeName[ token.type() 
+                         ? bitPosition[((token.type() & -token.type()) * 0x077CB531UL) >> 27]
+                         : 0 ]
+       << "('"
+       << token.value()
+       << "')";
+    return os;
 }
 
+///////////////////////////////////////////////////////////////////////////
+// senf::console::ParseCommandInfo
+
 prefix_ std::ostream & senf::console::operator<<(std::ostream & stream,
                                                  ParseCommandInfo const & info)
 {
-    if (info.builtin() == ParseCommandInfo::NoBuiltin) 
-        stream << senf::stringJoin(info.commandPath(), "/");
+    if (info.builtin() == ParseCommandInfo::NoBuiltin) {
+        ParseCommandInfo::TokensRange::const_iterator i (info.commandPath().begin());
+        ParseCommandInfo::TokensRange::const_iterator const i_end (info.commandPath().end());
+        if (i != i_end) {
+            for (;;) {
+                stream << i->value();
+                if ( ++i != i_end ) stream << "/";
+                else                break;
+            }
+        }
+    }
     else {
-        char const * builtins[] = { "", "cd", "ls", "pushd", "popd", "exit" };
+        char const * builtins[] = { 0, "cd", "ls", "pushd", "popd", "exit", "help" };
         stream << "builtin-" << builtins[info.builtin()];
     }
         
@@ -200,8 +182,52 @@ prefix_ std::ostream & senf::console::operator<<(std::ostream & stream,
 }
 
 ///////////////////////////////////////////////////////////////////////////
+// senf::console::ParseCommandInfo::ArgumentIterator
+
+prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::setRange()
+    const
+{
+    if (b_->is(Token::ArgumentGroupOpen)) {
+        unsigned level (0);
+        e_ = b_;
+        for (;;) {
+            if (e_->is(Token::ArgumentGroupOpen))
+                ++ level;
+            else if (e_->is(Token::ArgumentGroupClose)) {
+                -- level;
+                if (level == 0)
+                    break;
+            }
+            ++e_;
+        }
+    }
+    ++ e_;
+}
+
+prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::decrement()
+{
+    e_ = b_;
+    --b_;
+    if (b_->is(Token::ArgumentGroupClose)) {
+        unsigned level (0);
+        for (;;) {
+            if (b_->is(Token::ArgumentGroupClose))
+                ++ level;
+            else if (b_->is(Token::ArgumentGroupOpen)) {
+                -- level;
+                if (level == 0)
+                    break;
+            }
+            --b_;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
 // senf::console::CommandParser
 
+#ifndef DOXYGEN
+
 struct senf::console::CommandParser::Impl
 {
     typedef detail::CommandGrammar<detail::ParseDispatcher> Grammar;
@@ -213,6 +239,8 @@ struct senf::console::CommandParser::Impl
     Impl() : dispatcher(), context(), grammar(dispatcher, context) {}
 };
 
+#endif
+
 prefix_ senf::console::CommandParser::CommandParser()
     : impl_ (new Impl())
 {}
@@ -220,16 +248,88 @@ prefix_ senf::console::CommandParser::CommandParser()
 prefix_ senf::console::CommandParser::~CommandParser()
 {}
 
-prefix_ bool senf::console::CommandParser::parse(std::string command, Callback cb)
+// This template member is placed here, since it is ONLY called from the implementation.  Otherwise,
+// we would need to expose the Impl member to the public, which we don't want to do.
+
+template <class Iterator>
+prefix_ Iterator senf::console::CommandParser::parseLoop(Iterator b, Iterator e, Callback cb)
+{
+    ParseCommandInfo info;
+    detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
+    boost::spirit::parse_info<Iterator> result;
+
+    for(;;) {
+        result = boost::spirit::parse(
+            b, e, * impl().grammar.use_parser<Impl::Grammar::SkipParser>());
+        b = result.stop;
+        if (b == e) 
+            return e;
+        info.clear();
+        result = boost::spirit::parse(b, e,
+                                      impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
+                                      impl().grammar.use_parser<Impl::Grammar::SkipParser>());
+        if (! result.hit) 
+            return b;
+        if (! info.empty()) 
+            cb(info);
+        b = result.stop;
+    }
+}
+
+prefix_ bool senf::console::CommandParser::parse(std::string const & command, Callback cb)
 {
-    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>(),
+    return parseLoop(command.begin(), command.end(), cb) == command.end();
+}
+
+prefix_ bool senf::console::CommandParser::parseFile(std::string const & filename, Callback cb)
+{
+    boost::spirit::file_iterator<> i (filename);
+    if (!i) throw SystemException(ENOENT SENF_EXC_DEBUGINFO);
+    boost::spirit::file_iterator<> const i_end (i.make_end());
+    
+    return parseLoop(i, i_end, cb) == i_end;
+}
+
+prefix_ bool senf::console::CommandParser::parseArguments(std::string const & arguments,
+                                                          ParseCommandInfo & info)
+{
+    detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
+    return boost::spirit::parse( arguments.begin(), arguments.end(), 
+                                 impl().grammar.use_parser<Impl::Grammar::ArgumentsParser>(),
                                  impl().grammar.use_parser<Impl::Grammar::SkipParser>()
         ).full;
 }
 
+struct senf::console::CommandParser::SetIncremental
+{
+    SetIncremental(CommandParser & parser) : parser_ (parser) {
+        parser_.impl().grammar.incremental = true;
+    }
+
+    ~SetIncremental() {
+        parser_.impl().grammar.incremental = false;
+    }
+
+    CommandParser & parser_;
+};
+
+prefix_ std::string::size_type
+senf::console::CommandParser::parseIncremental(std::string const & commands, Callback cb)
+{
+    SetIncremental si (*this);
+    return std::distance( commands.begin(), 
+                          parseLoop(commands.begin(), commands.end(), cb) );
+}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::SyntaxErrorException
+
+prefix_ char const * senf::console::SyntaxErrorException::what()
+    const throw()
+{
+    return message().empty() ? "syntax error" : message().c_str();
+}
+
 ///////////////////////////////cc.e////////////////////////////////////////
 #undef prefix_
 //#include "Parse.mpp"