Console: Documentation of the configuration support
[senf.git] / Console / Parse.hh
index 1dc7498..c246ce1 100644 (file)
@@ -26,7 +26,7 @@
 #ifndef HH_Parse_
 #define HH_Parse_ 1
 
-/** \defgroup console_parser The console/config parser
+/** \defgroup console_parser The parser
 
     The console/config library defines a simple language used to interact with the console or to
     configure the application.  The parser is not concerned about interpreting commands or
 #include <boost/utility.hpp>
 #include <boost/scoped_ptr.hpp>
 #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////////////////////////////////////////
@@ -207,56 +209,141 @@ 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 { 
+            None                = 0,
+            PathSeparator       = 0x0001, // '/'
+            ArgumentGroupOpen   = 0x0002, // '('
+            ArgumentGroupClose  = 0x0004, // ')'
+            DirectoryGroupOpen  = 0x0008, // '{'
+            DirectoryGroupClose = 0x0010, // '}'
+            CommandTerminator   = 0x0020, // ';'
+            OtherPunctuation    = 0x0040,
+            BasicString         = 0x0080,
+            HexString           = 0x0100,
+            Word                = 0x0200
+        };
+
+        enum TokenGroup {
+            ArgumentGrouper     = ArgumentGroupOpen 
+                                | ArgumentGroupClose,
+
+            DirectoryGrouper    = DirectoryGroupOpen 
+                                | DirectoryGroupClose,
+
+            Punctuation         = DirectoryGroupOpen 
+                                | DirectoryGroupClose 
+                                | PathSeparator 
+                                | CommandTerminator 
+                                | OtherPunctuation,
+
+            String              = BasicString 
+                                | HexString,
+
+            SimpleArgument      = Word 
+                                | BasicString 
+                                | 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 */
 
+        TokenType type() const;         ///< Token type
+
+        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:
-        explicit ArgumentToken(std::string token);
-
+        TokenType type_;
         std::string token_;
-
-        friend class detail::ParserAccess;
     };
 
-    /** \brief Console command
+    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
         taken from the parser, no semantic information is attached at this point, the config/console
-        is not involved in any why. ParseCommandInfo consist of
+        node tree is not involved in any why. ParseCommandInfo consist of
         
         \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;
-        
+
     public:
+        class ArgumentIterator; 
+
         typedef CommandPath::const_iterator path_iterator;
         typedef Tokens::const_iterator token_iterator;
-        typedef boost::iterator_range<token_iterator> argument_value_type;
-
-
-    private:
-        typedef std::vector<argument_value_type> Arguments;
-
-    public:
-        typedef Arguments::const_iterator argument_iterator;
-        typedef Arguments::size_type size_type;
+        typedef ArgumentIterator argument_iterator;
+        typedef Tokens::size_type size_type;
 
         typedef boost::iterator_range<path_iterator> CommandPathRange;
         typedef boost::iterator_range<argument_iterator> ArgumentsRange;
@@ -270,46 +357,201 @@ 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
                                              empty, the path is an absolute path, otherwise it is
                                              relative. If the last element is an empty string, the
-                                             path ends in a '/' char. */
+                                             path ends with a '/' char. */
         ArgumentsRange arguments() const; ///< Command arguments
-                                        /**< The returned range contains one token range for each
+                                        /**< The returned range contains one TokensRange for each
                                              argument. */
         TokensRange tokens() const;     ///< All argument tokens
                                         /**< The returned range contains \e all argument tokens in a
                                              single range not divided into separate arguments. */
-    protected:
 
-    private:
-        void init();
-        void setBuiltin(BuiltinCommand builtin);
-        void setCommand(std::vector<std::string> & commandPath);
-        void startArgument();
-        void endArgument();
-        void addToken(ArgumentToken const & token);
-        void finalize();
+        void clear();                   ///< Clear all data members
 
-        struct MakeRange;
+        void builtin(BuiltinCommand builtin); ///< Assign builtin command 
+        void command(std::vector<Token> & commandPath); ///< Assign non-builtin command
 
-        std::vector<std::string> commandPath_;
+        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. */
 
-        typedef std::pair<Tokens::size_type, Tokens::size_type> TempArgumentRange;
-        typedef std::vector<TempArgumentRange> TempArguments;
+    protected:
 
+    private:
+        struct MakeRange;
+
+        std::vector<Token> commandPath_;
         BuiltinCommand builtin_;
         Tokens tokens_;
-        Arguments arguments_;
-        TempArguments tempArguments_;
+    };
+
+    /** \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:
+        reference dereference() const;
+        bool equal(ArgumentIterator const & other) const;
+        void increment();
+        void decrement();
+
+        mutable ParseCommandInfo::TokensRange::iterator b_;
+        mutable ParseCommandInfo::TokensRange::iterator e_;
+
+        void setRange() const;
+
+        friend class boost::iterator_core_access;
+        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. 
 
-        friend class detail::ParserAccess;
+        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
@@ -358,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;