Console: More simple argument parsing (argument iterator wrapper)
[senf.git] / Console / Parse.hh
1 // $Id$
2 //
3 // Copyright (C) 2008 
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief Parse public header */
25
26 #ifndef HH_Parse_
27 #define HH_Parse_ 1
28
29 /** \defgroup console_parser The parser
30
31     The console/config library defines a simple language used to interact with the console or to
32     configure the application.  The parser is not concerned about interpreting commands or
33     arguments, checking that a command exists or managing directories. The parser just takes the
34     input and parses it.
35
36     \autotoc
37
38     \section console_language The Language
39
40     The config/console language is used in configuration files and interactively at the
41     console. Some features of the language are more useful in config files, others at the
42     interactive console but the language is the same in both cases.
43
44     Let's start with a sample of the config/console language. The following is written as a
45     configuration file
46     \code
47     # My someserver configuration file
48     
49     /server/port 1234;
50     
51     /logger/targets {
52         console {
53             accept senf::log::Debug IMPORTANT;
54             accept server::ServerLog CRITICAL;
55         }
56
57         provide serverlog senf::log::FileTarget "/var/log/server.log";
58         serverlog {
59             reject senf::log::Debug senf::Console::Server NOTICE;
60             accept senf::log::Debug NOTICE;
61             accept server::ServerLog;
62         }
63     }
64
65     /server/stuffing (UDPPacket x"01 02 03 04");
66     /server/allow_hosts 10.1.2.3               # our internal server
67                         10.2.3.4 10.4.3.5      # client workstations
68         ;
69
70     /help/infoUrl "http://senf.j32.de/src/doc";
71     \endcode
72
73     The interactive syntax is the same with some notes:
74     \li All commands must be complete on a single line. This includes grouping constructs which must
75         be closed on the same line they are opened.
76     \li The last ';' is optional. However, multiple commands may be entered on a single line when
77         they are separated by ';'.
78     \li An empty line on the interactive console will repeat the last command.
79
80     The language consists of a small number of syntactic entities:
81
82     \subsection console_special_chars Special characters
83
84     These are characters, which have a special meaning. Some are used internally, others are just
85     returned as punctuation tokens
86     
87     <table class="senf">
88     <tr><td>/</td><td>path component separator</td></tr>
89     <tr><td>( )</td><td>argument grouping</td></tr>
90     <tr><td>{ }</td><td>directory grouping</td></tr>
91     <tr><td>;</td><td>command terminator</td></tr>
92     <tr><td>, =</td><td>punctuation tokens</td></tr>
93     </table>
94
95     \subsection console_basic Basic elements
96
97     A <b>word</b> is \e any sequence of consecutive characters which does not include any special
98     character. Examples for words are thus
99     <pre>
100     12.34
101     jens@fokus.fraunhofer.de
102     eth0
103     1>2
104     </pre>
105
106     The following are \e not valid words:
107     <pre>
108     a/b/c
109     a,b
110     </pre>
111
112     A <b>string literal</b> is just that: A double-quoted string (C/C++ style) possibly with
113     embedded escape chars:
114     <pre>
115     "\"foo\nbar\""
116     "\x04test"
117     </pre>
118     
119     A <b>hex-string literal</b> is used to represent binary data. It looks like a string which has
120     only hexadecimal bytes or whitespace as contents (comments and newlines are Ok when not read
121     from the interactive console)
122     <pre>
123     x"01 02 03 0405"
124     x"01 02   # ID header
125       0405    # payload
126       "
127     </pre>
128
129     A <b>token</b> is a \e word, \e string or \e hex-string, or a single special character (that's
130     true, any special character is allowed as a token). '(' and ')' must be properly nested.
131
132     A <b>path</b> is a sequence of \e words separated by '/' (and optional whitespace). A path may
133     have an optional initial and/or a terminating '/'.
134     <pre>
135     a/b/c
136     foo / bar /
137     /server
138     </pre>
139
140     \subsection console_statements Statements
141     
142     There are several types of statements:
143     \li The bulk of all statements are \e path statements
144     \li There are some \e built-in statements which are mostly useful at the interactive console
145     \li A special form of statement is the <em>directory group</em>
146
147     A <b>path</b> statement consists of a (possibly relative) path followed by any number of
148     arguments and terminated with a ';' (or end-of-input)
149     <pre>
150     /path/to/command arg1 "arg2" (complex=(1 2) another) ;
151     </pre>
152     Every argument is either
153     \li A single word, string or hex-string
154     \li or a parenthesized list of tokens.
155
156     So above command has three arguments: 'arg1', 'arg2' (a single token each) and one argument with
157     the 7 tokens 'complex', '=', '(', '1', '2', ')', 'another'. The interpretation of the arguments
158     is completely up to the command.
159
160     A <b>built-in</b> statement is one of
161     
162     <table class="senf">
163     <tr><td>\c cd \e path</td><td>Change current directory</td></tr>
164     <tr><td>\c ls [ \e path ]</td><td>List contents of \e path or current directory</td></tr>
165     <tr><td>\c exit</td><td>Exit interactive console</td></tr>
166     <tr><td>\c help [ \e path ]</td><td>Show help for \e path or current directory</td></tr>
167     </table>
168
169     A <b>directory group</b> statement is a block of statements all executed relatively to a fixed
170     directory.
171     <pre>
172     /some/path {
173         statement ;
174         . . .
175     }
176     </pre>
177     At the beginning of the block, the current directory is saved and the directory is changed to
178     the given directory. All commands are executed and at the end of the block, the saved directory
179     is restored.
180
181     \section console_parse_api The parser API
182
183     The senf::console::CommandParser is responsible for taking text input and turning it into a
184     sequence of senf::console::ParseCommandInfo structures. The structures are returned by passing
185     them successively to a callback function.
186
187     Every statement is returned as a senf::console::ParseCommandInfo instance. Directory groups are
188     handled specially: They are divided into two special built-in commands called PUSHD and POPD.
189  */
190
191 // Custom includes
192 #include <string>
193 #include <vector>
194 #include <boost/utility.hpp>
195 #include <boost/scoped_ptr.hpp>
196 #include <boost/range/iterator_range.hpp>
197 #include <boost/iterator/iterator_facade.hpp>
198 #include <boost/function.hpp>
199 #include "../Utils/safe_bool.hh"
200
201 //#include "Parse.mpp"
202 ///////////////////////////////hh.p////////////////////////////////////////
203
204 namespace senf {
205 namespace console {
206
207     namespace detail { struct ParserAccess; }
208
209     /** \brief Single argument token
210
211         All command arguments are split into tokens by the parser. Each token is returned as an
212         ArgumentToken instance. 
213
214         \ingroup console_parser
215       */
216     class ArgumentToken
217     {
218     public:
219         enum TokenType { 
220             PathSeparator       = 0x0001,
221             ArgumentGroupOpen   = 0x0002,
222             ArgumentGroupClose  = 0x0004,
223             DirectoryGroupOpen  = 0x0008,
224             DirectoryGroupClose = 0x0010,
225             CommandTerminator   = 0x0020,
226             OtherPunctuation    = 0x0040,
227             BasicString         = 0x0080,
228             HexString           = 0x0100,
229             Word                = 0x0200
230         };
231
232         enum TokenGroup {
233             ArgumentGrouper     = ArgumentGroupOpen 
234                                 | ArgumentGroupClose,
235
236             DirectoryGrouper    = DirectoryGroupOpen 
237                                 | DirectoryGroupClose,
238
239             Punctuation         = DirectoryGroupOpen 
240                                 | DirectoryGroupClose 
241                                 | PathSeparator 
242                                 | CommandTerminator 
243                                 | OtherPunctuation,
244
245             String              = BasicString 
246                                 | HexString,
247
248             SimpleArgument      = Word 
249                                 | BasicString 
250                                 | HexString
251         };
252         
253         std::string const & value() const; ///< String value of token
254                                         /**< This value is properly unquoted */
255
256         TokenType type() const;         ///< Token type
257
258         bool is(unsigned tokens) const; ///< Check, whether tokens type matches \a tokens
259                                         /**< \a tokens is a bit-mask of token types to check. */
260
261     protected:
262
263     private:
264         ArgumentToken(TokenType type, std::string token);
265
266         TokenType type_;
267         std::string token_;
268
269         friend class detail::ParserAccess;
270     };
271
272     /** \brief Single parsed console command
273
274         Every command parsed is returned in a ParseCommandInfo instance. This information is purely
275         taken from the parser, no semantic information is attached at this point, the config/console
276         node tree is not involved in any why. ParseCommandInfo consist of
277         
278         \li the type of command: built-in or normal command represented by a possibly relative path
279             into the command tree.
280         \li the command
281         \li the arguments. Every argument consists of a range of ArgumentToken instances.
282
283         \ingroup console_parser
284       */
285     class ParseCommandInfo
286     {
287         typedef std::vector<ArgumentToken> Tokens;
288         typedef std::vector<std::string> CommandPath;
289
290     public:
291         class ArgumentIterator; 
292
293         typedef CommandPath::const_iterator path_iterator;
294         typedef Tokens::const_iterator token_iterator;
295         typedef ArgumentIterator argument_iterator;
296         typedef Tokens::size_type size_type;
297
298         typedef boost::iterator_range<path_iterator> CommandPathRange;
299         typedef boost::iterator_range<argument_iterator> ArgumentsRange;
300         typedef boost::iterator_range<token_iterator> TokensRange;
301
302         enum BuiltinCommand { NoBuiltin, 
303                               BuiltinCD, 
304                               BuiltinLS, 
305                               BuiltinPUSHD, 
306                               BuiltinPOPD,
307                               BuiltinEXIT,
308                               BuiltinHELP };
309
310         BuiltinCommand builtin() const; ///< Command type
311                                         /**< \returns \c NoBuiltin, if the command is an ordinary
312                                              command, otherwise the id of the built-in command */
313         CommandPathRange commandPath() const; ///< Command path
314                                         /**< This is the path to the command if it is not a built-in
315                                              command. Every element of the returned range
316                                              constitutes one path element. If the first element is
317                                              empty, the path is an absolute path, otherwise it is
318                                              relative. If the last element is an empty string, the
319                                              path ends with a '/' char. */
320         ArgumentsRange arguments() const; ///< Command arguments
321                                         /**< The returned range contains one TokensRange for each
322                                              argument. */
323         TokensRange tokens() const;     ///< All argument tokens
324                                         /**< The returned range contains \e all argument tokens in a
325                                              single range not divided into separate arguments. */
326     protected:
327
328     private:
329         void init();
330         void setBuiltin(BuiltinCommand builtin);
331         void setCommand(std::vector<std::string> & commandPath);
332         void addToken(ArgumentToken const & token);
333
334         struct MakeRange;
335
336         std::vector<std::string> commandPath_;
337         BuiltinCommand builtin_;
338         Tokens tokens_;
339
340         friend class detail::ParserAccess;
341     };
342
343     /** \brief Iterator parsing argument groups
344
345         This special iterator parses a token range returned by the parser into argument ranges. An
346         argument range is either a single token or it is a range of tokens enclosed in matching
347         parenthesis. The ParseCommandInfo::arguments() uses this iterator type. To recursively parse
348         complex arguments, you can however use this iterator to divide a multi-token argument into
349         further argument groups (e.g. to parse a list or vector of items).
350
351         This iterator is a bidirectional iterator \e not a random access iterator.
352      */
353     class ParseCommandInfo::ArgumentIterator
354         : public boost::iterator_facade< ParseCommandInfo::ArgumentIterator, 
355                                          ParseCommandInfo::TokensRange,
356                                          boost::bidirectional_traversal_tag,
357                                          ParseCommandInfo::TokensRange >
358     {
359     public:
360         ArgumentIterator();
361         explicit ArgumentIterator(ParseCommandInfo::TokensRange::iterator i);
362
363     private:
364         reference dereference() const;
365         bool equal(ArgumentIterator const & other) const;
366         void increment();
367         void decrement();
368
369         mutable ParseCommandInfo::TokensRange::iterator b_;
370         mutable ParseCommandInfo::TokensRange::iterator e_;
371
372         void setRange() const;
373
374         friend class boost::iterator_core_access;
375         friend class ParseCommandInfo;
376     };
377
378     /**  \brief Syntax error parsing command arguments exception
379
380         All errors while parsing the arguments of a command must be signaled by throwing an instance
381         of SyntaxErrorException. This is important, so command overloading works.
382      */
383     struct SyntaxErrorException : public std::exception
384     {
385         explicit SyntaxErrorException(std::string const & msg = "");
386         virtual ~SyntaxErrorException() throw();
387
388         virtual char const * what() const throw();
389         std::string const & message() const;
390
391     private:
392         std::string message_;
393     };
394
395     /** \brief Wrapper checking argument iterator access for validity
396         
397         CheckedArgumentIteratorWrapper is a wrapper around a range of arguments parsed using the
398         ParseCommandInfo::ArgumentIterator. It is used to parse arguments either in a command
399         (registered with manual argument parsing) or when defining a custom parser.
400         \code
401         void fn(std::ostream & out, senf::console::ParseCommandInfo command)
402         {
403             std:;string arg1;
404             unsigned arg2 (0);
405             
406             {
407                 senf::console::CheckedArgumentIteratorWrapper arg (command.arguments());
408                 senf::console::parse( *(arg++), arg1 );
409                 senf::console::parse( *(arg++), arg2 );
410             }
411
412             // ...
413         }
414         \endcode
415
416         To use the wrapper, you must ensure that:
417         \li You increment the iterator \e past all arguments you parse. The iterator must point to
418             the end of the range when parsing is complete.
419         \li The iterator wrapper is destroyed after parsing but before executing the command itself
420             begins. 
421
422         Accessing a non-existent argument or failing to parse all arguments will raise a
423         senf::console::SyntaxErrorException.
424
425         \see \link console_arg_custom Example customer parser \endlink
426       */
427     class CheckedArgumentIteratorWrapper
428         : boost::noncopyable,
429           public boost::iterator_facade< CheckedArgumentIteratorWrapper,
430                                          ParseCommandInfo::TokensRange,
431                                          boost::forward_traversal_tag,
432                                          ParseCommandInfo::TokensRange >,
433           public senf::safe_bool<CheckedArgumentIteratorWrapper>
434
435     {
436         typedef boost::iterator_facade< CheckedArgumentIteratorWrapper,
437                                         ParseCommandInfo::TokensRange,
438                                         boost::forward_traversal_tag,
439                                         ParseCommandInfo::TokensRange > IteratorFacade;
440
441     public:
442         explicit CheckedArgumentIteratorWrapper(
443             ParseCommandInfo::ArgumentsRange const & range,
444             std::string const & msg = "invalid number of arguments");
445                                         ///< Make wrapper from ArgumentsRange
446                                         /**< This constructs a wrapper from a
447                                              ParseCommandInfo::ArgumentsRange. 
448                                              \param[in] range Range of arguments to parse
449                                              \param[in] msg Error message */
450         explicit CheckedArgumentIteratorWrapper(
451             ParseCommandInfo::TokensRange const & range,
452             std::string const & msg = "invalid number of arguments");
453                                         ///< Make wrapper from TokensRange
454                                         /**< This constructs a wrapper from a
455                                              ParseCommandInfo::TokensRange. The TokensRange is first
456                                              converted into an ParseCommandInfo::ArgumentsRange
457                                              which is then wrapped.
458                                              \param[in] range Range of tokens to  parse
459                                              \param[in] msg Error message */
460
461         ~CheckedArgumentIteratorWrapper(); ///< Check, if all arguments are parsed
462                                         /**< The destructor validates, that all arguments are parsed
463                                              correctly when leaving the scope, in which the wrapper
464                                              is instantiated normally (not by an exception).
465
466                                              \warning This destructor will throw a
467                                              SyntaxErrorException, if not all arguments are parsed
468                                              and when no other exception is in progress. */
469
470         operator ParseCommandInfo::ArgumentIterator(); 
471                                         ///< Use wrapper as ParseCommandInfo::ArgumentIterator
472
473         bool boolean_test() const;      ///< \c true, if more arguments are available
474         bool done() const;              ///< \c true, if all arguments are parsed
475
476         void clear();                   ///< Set range empty
477                                         /**< This call will point the current iterator to the end of
478                                              the tokens range.
479                                              \post done() == \c true; */
480
481         bool operator==(ParseCommandInfo::ArgumentIterator const & other) const;
482                                         ///< Compare wrapper against ArgumentIterator
483         bool operator!=(ParseCommandInfo::ArgumentIterator const & other) const;
484                                         ///< Compare wrapper against ArgumentIterator
485         
486         using IteratorFacade::operator++;
487         ParseCommandInfo::ArgumentIterator operator++(int);
488         
489     private:
490         reference dereference() const;
491         void increment();
492
493         ParseCommandInfo::ArgumentIterator i_;
494         ParseCommandInfo::ArgumentIterator e_;
495         std::string msg_;
496
497         friend class boost::iterator_core_access;
498     };
499
500     /**< \brief Output ParseCommandInfo instance
501          \related ParseCommandInfo
502       */
503     std::ostream & operator<<(std::ostream & stream, ParseCommandInfo const & info);
504
505     /** \brief Parse commands
506
507         This class implements a parser for the console/config language. It supports parsing strings
508         as well as files. For every parsed command, a callback function is called.
509
510         \implementation The implementation is based on Boost.Spirit. See the file \ref Parse.ih for
511             the formal language grammar.
512
513         \implementation Parsing an arbitrary iostream is not supported since arbitrary streams are
514             not seekable. If this is needed, it can however be provided using stream iterators and
515             some special iterator adaptors from Boost.Spirit. However, the amount of backtracking
516             needs to be analyzed before this is viable.
517
518         \todo Implement more detailed error reporting and error recovery.
519
520         \ingroup console_parser
521       */
522     class CommandParser
523         : boost::noncopyable
524     {
525     public:
526         ///////////////////////////////////////////////////////////////////////////
527         // Types
528
529         typedef boost::function<void (ParseCommandInfo const &)> Callback;
530
531         ///////////////////////////////////////////////////////////////////////////
532         ///\name Structors and default members
533         ///@{
534
535         CommandParser();
536         ~CommandParser();
537
538         ///@}
539         ///////////////////////////////////////////////////////////////////////////
540
541         bool parse(std::string command, Callback cb); ///< Parse string
542         bool parseFile(std::string filename, Callback cb); ///< Parse file
543                                         /**< \throws SystemException if the file cannot be
544                                              read. */
545
546     private:
547         struct Impl;
548
549         Impl & impl();
550
551         boost::scoped_ptr<Impl> impl_;
552     };
553
554 }}
555
556 ///////////////////////////////hh.e////////////////////////////////////////
557 #include "Parse.cci"
558 //#include "Parse.ct"
559 //#include "Parse.cti"
560 #endif
561
562 \f
563 // Local Variables:
564 // mode: c++
565 // fill-column: 100
566 // comment-column: 40
567 // c-file-style: "senf"
568 // indent-tabs-mode: nil
569 // ispell-local-dictionary: "american"
570 // compile-command: "scons -u test"
571 // End: