Console: Documentation of the configuration support
[senf.git] / Console / Parse.hh
index 8c2ab32..c246ce1 100644 (file)
 #include <boost/range/iterator_range.hpp>
 #include <boost/iterator/iterator_facade.hpp>
 #include <boost/function.hpp>
+#include "../Utils/safe_bool.hh"
 
 //#include "Parse.mpp"
 ///////////////////////////////hh.p////////////////////////////////////////
@@ -208,20 +209,21 @@ namespace console {
     /** \brief Single argument token
 
         All command arguments are split into tokens by the parser. Each token is returned as an
-        ArgumentToken instance. 
+        Token instance. 
 
         \ingroup console_parser
       */
-    class ArgumentToken
+    class Token
     {
     public:
         enum TokenType { 
-            PathSeparator       = 0x0001,
-            ArgumentGroupOpen   = 0x0002,
-            ArgumentGroupClose  = 0x0004,
-            DirectoryGroupOpen  = 0x0008,
-            DirectoryGroupClose = 0x0010,
-            CommandTerminator   = 0x0020,
+            None                = 0,
+            PathSeparator       = 0x0001, // '/'
+            ArgumentGroupOpen   = 0x0002, // '('
+            ArgumentGroupClose  = 0x0004, // ')'
+            DirectoryGroupOpen  = 0x0008, // '{'
+            DirectoryGroupClose = 0x0010, // '}'
+            CommandTerminator   = 0x0020, // ';'
             OtherPunctuation    = 0x0040,
             BasicString         = 0x0080,
             HexString           = 0x0100,
@@ -249,6 +251,10 @@ namespace console {
                                 | HexString
         };
         
+        Token();                        ///< Create empty token
+        Token(TokenType type, std::string token); ///< Create token with given type and value
+
+
         std::string const & value() const; ///< String value of token
                                         /**< This value is properly unquoted */
 
@@ -257,17 +263,62 @@ namespace console {
         bool is(unsigned tokens) const; ///< Check, whether tokens type matches \a tokens
                                         /**< \a tokens is a bit-mask of token types to check. */
 
+        bool operator==(Token const & other) const;
+        bool operator!=(Token const & other) const;
+
     protected:
 
     private:
-        ArgumentToken(TokenType type, std::string token);
-
         TokenType type_;
         std::string token_;
-
-        friend class detail::ParserAccess;
     };
 
+    std::ostream & operator<<(std::ostream & os, Token const & token);
+
+    /** \brief Create a \c None token
+        \related Token */
+    Token NoneToken();
+
+    /** \brief Create a \c PathSeparator ['/'] token
+        \related Token */
+    Token PathSeparatorToken();
+
+    /** \brief Create an \c ArgumentGroupOpen ['('] token
+        \related Token */
+    Token ArgumentGroupOpenToken();
+
+    /** \brief Create a \c ArgumentGroupClose [')'] token
+        \related Token */
+    Token ArgumentGroupCloseToken();
+
+    /** \brief Create a \c DirectoryGroupOpen ['{'] token
+        \related Token */
+    Token DirectoryGroupOpenToken();
+
+    /** \brief Create a \c DirectoryGroupClose ['}'] token
+        \related Token */
+    Token DirectoryGroupCloseToken();
+
+    /** \brief Create a \c CommandTerminator [';'] token
+        \related Token */
+    Token CommandTerminatorToken();
+
+    /** \brief Create a \c OtherPunctuation ['=', ','] token with the given \a value
+        \related Token */
+    Token OtherPunctuationToken(std::string const & value);
+
+    /** \brief Create a \c BasicString token with the given \a value
+        \related Token */
+    Token BasicStringToken(std::string const & value);
+
+    /** \brief Create a \c HexString token with the given \a value
+        \related Token */
+    Token HexStringToken(std::string const & value);
+
+    /** \brief Create a \c Word token with the given \a value
+        \related Token */
+    Token WordToken(std::string const & value);
+
     /** \brief Single parsed console command
 
         Every command parsed is returned in a ParseCommandInfo instance. This information is purely
@@ -277,17 +328,18 @@ namespace console {
         \li the type of command: built-in or normal command represented by a possibly relative path
             into the command tree.
         \li the command
-        \li the arguments. Every argument consists of a range of ArgumentToken instances.
+        \li the arguments. Every argument consists of a range of Token instances.
 
         \ingroup console_parser
       */
     class ParseCommandInfo
     {
-        typedef std::vector<ArgumentToken> Tokens;
+        typedef std::vector<Token> Tokens;
         typedef std::vector<std::string> CommandPath;
-        class ArgumentIterator; 
 
     public:
+        class ArgumentIterator; 
+
         typedef CommandPath::const_iterator path_iterator;
         typedef Tokens::const_iterator token_iterator;
         typedef ArgumentIterator argument_iterator;
@@ -305,10 +357,12 @@ namespace console {
                               BuiltinEXIT,
                               BuiltinHELP };
 
+        ParseCommandInfo();
+
         BuiltinCommand builtin() const; ///< Command type
                                         /**< \returns \c NoBuiltin, if the command is an ordinary
                                              command, otherwise the id of the built-in command */
-        CommandPathRange commandPath() const; ///< Command path
+        TokensRange commandPath() const; ///< Command path
                                         /**< This is the path to the command if it is not a built-in
                                              command. Every element of the returned range
                                              constitutes one path element. If the first element is
@@ -321,34 +375,49 @@ namespace console {
         TokensRange tokens() const;     ///< All argument tokens
                                         /**< The returned range contains \e all argument tokens in a
                                              single range not divided into separate arguments. */
+
+        void clear();                   ///< Clear all data members
+
+        void builtin(BuiltinCommand builtin); ///< Assign builtin command 
+        void command(std::vector<Token> & commandPath); ///< Assign non-builtin command
+
+        void addToken(Token const & token); ///< Add argument token
+                                        /**< You \e must ensure, that the resulting argument tokens
+                                             are properly nested regarding '()' groups, otherwise
+                                             interpreting arguments using the arguments() call will
+                                             crash the program. */
+
     protected:
 
     private:
-        void init();
-        void setBuiltin(BuiltinCommand builtin);
-        void setCommand(std::vector<std::string> & commandPath);
-        void addToken(ArgumentToken const & token);
-
         struct MakeRange;
 
-        std::vector<std::string> commandPath_;
+        std::vector<Token> commandPath_;
         BuiltinCommand builtin_;
         Tokens tokens_;
-
-        friend class detail::ParserAccess;
     };
 
+    /** \brief Iterator parsing argument groups
+
+        This special iterator parses a token range returned by the parser into argument ranges. An
+        argument range is either a single token or it is a range of tokens enclosed in matching
+        parenthesis. The ParseCommandInfo::arguments() uses this iterator type. To recursively parse
+        complex arguments, you can however use this iterator to divide a multi-token argument into
+        further argument groups (e.g. to parse a list or vector of items).
+
+        This iterator is a bidirectional iterator \e not a random access iterator.
+     */
     class ParseCommandInfo::ArgumentIterator
         : public boost::iterator_facade< ParseCommandInfo::ArgumentIterator, 
                                          ParseCommandInfo::TokensRange,
                                          boost::bidirectional_traversal_tag,
                                          ParseCommandInfo::TokensRange >
     {
+    public:
         ArgumentIterator();
+        explicit ArgumentIterator(ParseCommandInfo::TokensRange::iterator i);
 
     private:
-        ArgumentIterator(ParseCommandInfo::TokensRange::iterator i);
-
         reference dereference() const;
         bool equal(ArgumentIterator const & other) const;
         void increment();
@@ -363,6 +432,128 @@ namespace console {
         friend class ParseCommandInfo;
     };
 
+    /**  \brief Syntax error parsing command arguments exception
+
+        All errors while parsing the arguments of a command must be signaled by throwing an instance
+        of SyntaxErrorException. This is important, so command overloading works.
+     */
+    struct SyntaxErrorException : public std::exception
+    {
+        explicit SyntaxErrorException(std::string const & msg = "");
+        virtual ~SyntaxErrorException() throw();
+
+        virtual char const * what() const throw();
+        std::string const & message() const;
+
+    private:
+        std::string message_;
+    };
+
+    /** \brief Wrapper checking argument iterator access for validity
+        
+        CheckedArgumentIteratorWrapper is a wrapper around a range of arguments parsed using the
+        ParseCommandInfo::ArgumentIterator. It is used to parse arguments either in a command
+        (registered with manual argument parsing) or when defining a custom parser.
+        \code
+        void fn(std::ostream & out, senf::console::ParseCommandInfo command)
+        {
+            std:;string arg1;
+            unsigned arg2 (0);
+            
+            {
+                senf::console::CheckedArgumentIteratorWrapper arg (command.arguments());
+                senf::console::parse( *(arg++), arg1 );
+                senf::console::parse( *(arg++), arg2 );
+            }
+
+            // ...
+        }
+        \endcode
+
+        To use the wrapper, you must ensure that:
+        \li You increment the iterator \e past all arguments you parse. The iterator must point to
+            the end of the range when parsing is complete.
+        \li The iterator wrapper is destroyed after parsing but before executing the command itself
+            begins. 
+
+        Accessing a non-existent argument or failing to parse all arguments will raise a
+        senf::console::SyntaxErrorException.
+
+        \see \link console_arg_custom Example customer parser \endlink
+      */
+    class CheckedArgumentIteratorWrapper
+        : boost::noncopyable,
+          public boost::iterator_facade< CheckedArgumentIteratorWrapper,
+                                         ParseCommandInfo::TokensRange,
+                                         boost::forward_traversal_tag,
+                                         ParseCommandInfo::TokensRange >,
+          public senf::safe_bool<CheckedArgumentIteratorWrapper>
+
+    {
+        typedef boost::iterator_facade< CheckedArgumentIteratorWrapper,
+                                        ParseCommandInfo::TokensRange,
+                                        boost::forward_traversal_tag,
+                                        ParseCommandInfo::TokensRange > IteratorFacade;
+
+    public:
+        explicit CheckedArgumentIteratorWrapper(
+            ParseCommandInfo::ArgumentsRange const & range,
+            std::string const & msg = "invalid number of arguments");
+                                        ///< Make wrapper from ArgumentsRange
+                                        /**< This constructs a wrapper from a
+                                             ParseCommandInfo::ArgumentsRange. 
+                                             \param[in] range Range of arguments to parse
+                                             \param[in] msg Error message */
+        explicit CheckedArgumentIteratorWrapper(
+            ParseCommandInfo::TokensRange const & range,
+            std::string const & msg = "invalid number of arguments");
+                                        ///< Make wrapper from TokensRange
+                                        /**< This constructs a wrapper from a
+                                             ParseCommandInfo::TokensRange. The TokensRange is first
+                                             converted into an ParseCommandInfo::ArgumentsRange
+                                             which is then wrapped.
+                                             \param[in] range Range of tokens to  parse
+                                             \param[in] msg Error message */
+
+        ~CheckedArgumentIteratorWrapper(); ///< Check, if all arguments are parsed
+                                        /**< The destructor validates, that all arguments are parsed
+                                             correctly when leaving the scope, in which the wrapper
+                                             is instantiated normally (not by an exception).
+
+                                             \warning This destructor will throw a
+                                             SyntaxErrorException, if not all arguments are parsed
+                                             and when no other exception is in progress. */
+
+        operator ParseCommandInfo::ArgumentIterator(); 
+                                        ///< Use wrapper as ParseCommandInfo::ArgumentIterator
+
+        bool boolean_test() const;      ///< \c true, if more arguments are available
+        bool done() const;              ///< \c true, if all arguments are parsed
+
+        void clear();                   ///< Set range empty
+                                        /**< This call will point the current iterator to the end of
+                                             the tokens range.
+                                             \post done() == \c true; */
+
+        bool operator==(ParseCommandInfo::ArgumentIterator const & other) const;
+                                        ///< Compare wrapper against ArgumentIterator
+        bool operator!=(ParseCommandInfo::ArgumentIterator const & other) const;
+                                        ///< Compare wrapper against ArgumentIterator
+        
+        using IteratorFacade::operator++;
+        ParseCommandInfo::ArgumentIterator operator++(int);
+        
+    private:
+        reference dereference() const;
+        void increment();
+
+        ParseCommandInfo::ArgumentIterator i_;
+        ParseCommandInfo::ArgumentIterator e_;
+        std::string msg_;
+
+        friend class boost::iterator_core_access;
+    };
+
     /**< \brief Output ParseCommandInfo instance
          \related ParseCommandInfo
       */
@@ -409,6 +600,13 @@ namespace console {
                                         /**< \throws SystemException if the file cannot be
                                              read. */
 
+        bool parseArguments(std::string arguments, ParseCommandInfo & info);
+                                        ///< Parse \a argumtns
+                                        /**< parseArguments() parses the string \a arguments which
+                                             contains arbitrary command arguments (without the name
+                                             of the command). The argument tokens are written into
+                                             \a info. */
+
     private:
         struct Impl;