#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////////////////////////////////////////
/** \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;
};
+ 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
\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
-
- \todo Completely change the 'arguments()' member implementation: let the parser just
- generate a flat list of tokens and implement an 'argument iterator' with the following
- features: 1. return argument ranges, automatically detecting paranthesis 2. trying to
- increment the iterator beyond it's range just throws an argument syntax error. For this
- to work, the parser needs to not drop the outermost '()' pair 3. detect bad paranthesis
- (should not be necessary since the parser already does this). This allows to use this
- same iterator to parse nested complex arguments.
*/
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;
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
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
+ bool empty(); ///< \c true, if the data is empty
- struct MakeRange;
+ 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. */
- std::vector<std::string> commandPath_;
+ protected:
- typedef std::pair<Tokens::size_type, Tokens::size_type> TempArgumentRange;
- typedef std::vector<TempArgumentRange> TempArguments;
+ 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_;
+ };
- friend class detail::ParserAccess;
+ /** \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
///@}
///////////////////////////////////////////////////////////////////////////
- bool parse(std::string command, Callback cb); ///< Parse string
- bool parseFile(std::string filename, Callback cb); ///< Parse file
+ bool parse(std::string const & command, Callback cb); ///< Parse string
+ bool parseFile(std::string const & filename, Callback cb); ///< Parse file
/**< \throws SystemException if the file cannot be
read. */
+ bool parseArguments(std::string const & 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. */
+
+ std::string::size_type parseIncremental(std::string const & commands, Callback cb);
+ ///< Incremental parse
+ /**< An incremental parse will parse all complete statements
+ in \a commands. parseIncremental() will return the
+ number of characters successfully parsed from \a
+ commands.
+
+ \note The incremental parser \e requires all statements
+ to be terminated explicitly. This means, that the
+ last ';' is \e not optional in this case. */
+
private:
struct Impl;
+ struct SetIncremental;
+
+ template <class Iterator>
+ Iterator parseLoop(Iterator b, Iterator e, Callback cb);
Impl & impl();
boost::scoped_ptr<Impl> impl_;
+
+ friend class SetIncremental;
};
}}