142060d26961cd60f7e5cbc9d74ee205186494a7
[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         Token instance. 
213
214         \ingroup console_parser
215       */
216     class Token
217     {
218     public:
219         enum TokenType { 
220             None                = 0,
221             PathSeparator       = 0x0001, // '/'
222             ArgumentGroupOpen   = 0x0002, // '('
223             ArgumentGroupClose  = 0x0004, // ')'
224             DirectoryGroupOpen  = 0x0008, // '{'
225             DirectoryGroupClose = 0x0010, // '}'
226             CommandTerminator   = 0x0020, // ';'
227             OtherPunctuation    = 0x0040,
228             BasicString         = 0x0080,
229             HexString           = 0x0100,
230             Word                = 0x0200
231         };
232
233         enum TokenGroup {
234             ArgumentGrouper     = ArgumentGroupOpen 
235                                 | ArgumentGroupClose,
236
237             DirectoryGrouper    = DirectoryGroupOpen 
238                                 | DirectoryGroupClose,
239
240             Punctuation         = DirectoryGroupOpen 
241                                 | DirectoryGroupClose 
242                                 | PathSeparator 
243                                 | CommandTerminator 
244                                 | OtherPunctuation,
245
246             String              = BasicString 
247                                 | HexString,
248
249             SimpleArgument      = Word 
250                                 | BasicString 
251                                 | HexString
252         };
253         
254         Token();
255         Token(TokenType type, std::string token);
256
257         std::string const & value() const; ///< String value of token
258                                         /**< This value is properly unquoted */
259
260         TokenType type() const;         ///< Token type
261
262         bool is(unsigned tokens) const; ///< Check, whether tokens type matches \a tokens
263                                         /**< \a tokens is a bit-mask of token types to check. */
264
265         bool operator==(Token const & other) const;
266         bool operator!=(Token const & other) const;
267
268     protected:
269
270     private:
271         TokenType type_;
272         std::string token_;
273     };
274
275     std::ostream & operator<<(std::ostream & os, Token const & token);
276
277     Token NoneToken();
278     Token PathSeparatorToken();
279     Token ArgumentGroupOpenToken();
280     Token ArgumentGroupCloseToken();
281     Token DirectoryGroupOpenToken();
282     Token DirectoryGroupCloseToken();
283     Token CommandTerminatorToken();
284     Token OtherPunctuationToken(std::string const & value);
285     Token BasicStringToken(std::string const & value);
286     Token HexStringToken(std::string const & value);
287     Token WordToken(std::string const & value);
288
289     /** \brief Single parsed console command
290
291         Every command parsed is returned in a ParseCommandInfo instance. This information is purely
292         taken from the parser, no semantic information is attached at this point, the config/console
293         node tree is not involved in any why. ParseCommandInfo consist of
294         
295         \li the type of command: built-in or normal command represented by a possibly relative path
296             into the command tree.
297         \li the command
298         \li the arguments. Every argument consists of a range of Token instances.
299
300         \ingroup console_parser
301       */
302     class ParseCommandInfo
303     {
304         typedef std::vector<Token> Tokens;
305         typedef std::vector<std::string> CommandPath;
306
307     public:
308         class ArgumentIterator; 
309
310         typedef CommandPath::const_iterator path_iterator;
311         typedef Tokens::const_iterator token_iterator;
312         typedef ArgumentIterator argument_iterator;
313         typedef Tokens::size_type size_type;
314
315         typedef boost::iterator_range<path_iterator> CommandPathRange;
316         typedef boost::iterator_range<argument_iterator> ArgumentsRange;
317         typedef boost::iterator_range<token_iterator> TokensRange;
318
319         enum BuiltinCommand { NoBuiltin, 
320                               BuiltinCD, 
321                               BuiltinLS, 
322                               BuiltinPUSHD, 
323                               BuiltinPOPD,
324                               BuiltinEXIT,
325                               BuiltinHELP };
326
327         ParseCommandInfo();
328
329         BuiltinCommand builtin() const; ///< Command type
330                                         /**< \returns \c NoBuiltin, if the command is an ordinary
331                                              command, otherwise the id of the built-in command */
332         TokensRange commandPath() const; ///< Command path
333                                         /**< This is the path to the command if it is not a built-in
334                                              command. Every element of the returned range
335                                              constitutes one path element. If the first element is
336                                              empty, the path is an absolute path, otherwise it is
337                                              relative. If the last element is an empty string, the
338                                              path ends with a '/' char. */
339         ArgumentsRange arguments() const; ///< Command arguments
340                                         /**< The returned range contains one TokensRange for each
341                                              argument. */
342         TokensRange tokens() const;     ///< All argument tokens
343                                         /**< The returned range contains \e all argument tokens in a
344                                              single range not divided into separate arguments. */
345
346         void clear();
347
348         void builtin(BuiltinCommand builtin);
349         void command(std::vector<Token> & commandPath);
350
351         void addToken(Token const & token);
352
353     protected:
354
355     private:
356         struct MakeRange;
357
358         std::vector<Token> commandPath_;
359         BuiltinCommand builtin_;
360         Tokens tokens_;
361     };
362
363     /** \brief Iterator parsing argument groups
364
365         This special iterator parses a token range returned by the parser into argument ranges. An
366         argument range is either a single token or it is a range of tokens enclosed in matching
367         parenthesis. The ParseCommandInfo::arguments() uses this iterator type. To recursively parse
368         complex arguments, you can however use this iterator to divide a multi-token argument into
369         further argument groups (e.g. to parse a list or vector of items).
370
371         This iterator is a bidirectional iterator \e not a random access iterator.
372      */
373     class ParseCommandInfo::ArgumentIterator
374         : public boost::iterator_facade< ParseCommandInfo::ArgumentIterator, 
375                                          ParseCommandInfo::TokensRange,
376                                          boost::bidirectional_traversal_tag,
377                                          ParseCommandInfo::TokensRange >
378     {
379     public:
380         ArgumentIterator();
381         explicit ArgumentIterator(ParseCommandInfo::TokensRange::iterator i);
382
383     private:
384         reference dereference() const;
385         bool equal(ArgumentIterator const & other) const;
386         void increment();
387         void decrement();
388
389         mutable ParseCommandInfo::TokensRange::iterator b_;
390         mutable ParseCommandInfo::TokensRange::iterator e_;
391
392         void setRange() const;
393
394         friend class boost::iterator_core_access;
395         friend class ParseCommandInfo;
396     };
397
398     /**  \brief Syntax error parsing command arguments exception
399
400         All errors while parsing the arguments of a command must be signaled by throwing an instance
401         of SyntaxErrorException. This is important, so command overloading works.
402      */
403     struct SyntaxErrorException : public std::exception
404     {
405         explicit SyntaxErrorException(std::string const & msg = "");
406         virtual ~SyntaxErrorException() throw();
407
408         virtual char const * what() const throw();
409         std::string const & message() const;
410
411     private:
412         std::string message_;
413     };
414
415     /** \brief Wrapper checking argument iterator access for validity
416         
417         CheckedArgumentIteratorWrapper is a wrapper around a range of arguments parsed using the
418         ParseCommandInfo::ArgumentIterator. It is used to parse arguments either in a command
419         (registered with manual argument parsing) or when defining a custom parser.
420         \code
421         void fn(std::ostream & out, senf::console::ParseCommandInfo command)
422         {
423             std:;string arg1;
424             unsigned arg2 (0);
425             
426             {
427                 senf::console::CheckedArgumentIteratorWrapper arg (command.arguments());
428                 senf::console::parse( *(arg++), arg1 );
429                 senf::console::parse( *(arg++), arg2 );
430             }
431
432             // ...
433         }
434         \endcode
435
436         To use the wrapper, you must ensure that:
437         \li You increment the iterator \e past all arguments you parse. The iterator must point to
438             the end of the range when parsing is complete.
439         \li The iterator wrapper is destroyed after parsing but before executing the command itself
440             begins. 
441
442         Accessing a non-existent argument or failing to parse all arguments will raise a
443         senf::console::SyntaxErrorException.
444
445         \see \link console_arg_custom Example customer parser \endlink
446       */
447     class CheckedArgumentIteratorWrapper
448         : boost::noncopyable,
449           public boost::iterator_facade< CheckedArgumentIteratorWrapper,
450                                          ParseCommandInfo::TokensRange,
451                                          boost::forward_traversal_tag,
452                                          ParseCommandInfo::TokensRange >,
453           public senf::safe_bool<CheckedArgumentIteratorWrapper>
454
455     {
456         typedef boost::iterator_facade< CheckedArgumentIteratorWrapper,
457                                         ParseCommandInfo::TokensRange,
458                                         boost::forward_traversal_tag,
459                                         ParseCommandInfo::TokensRange > IteratorFacade;
460
461     public:
462         explicit CheckedArgumentIteratorWrapper(
463             ParseCommandInfo::ArgumentsRange const & range,
464             std::string const & msg = "invalid number of arguments");
465                                         ///< Make wrapper from ArgumentsRange
466                                         /**< This constructs a wrapper from a
467                                              ParseCommandInfo::ArgumentsRange. 
468                                              \param[in] range Range of arguments to parse
469                                              \param[in] msg Error message */
470         explicit CheckedArgumentIteratorWrapper(
471             ParseCommandInfo::TokensRange const & range,
472             std::string const & msg = "invalid number of arguments");
473                                         ///< Make wrapper from TokensRange
474                                         /**< This constructs a wrapper from a
475                                              ParseCommandInfo::TokensRange. The TokensRange is first
476                                              converted into an ParseCommandInfo::ArgumentsRange
477                                              which is then wrapped.
478                                              \param[in] range Range of tokens to  parse
479                                              \param[in] msg Error message */
480
481         ~CheckedArgumentIteratorWrapper(); ///< Check, if all arguments are parsed
482                                         /**< The destructor validates, that all arguments are parsed
483                                              correctly when leaving the scope, in which the wrapper
484                                              is instantiated normally (not by an exception).
485
486                                              \warning This destructor will throw a
487                                              SyntaxErrorException, if not all arguments are parsed
488                                              and when no other exception is in progress. */
489
490         operator ParseCommandInfo::ArgumentIterator(); 
491                                         ///< Use wrapper as ParseCommandInfo::ArgumentIterator
492
493         bool boolean_test() const;      ///< \c true, if more arguments are available
494         bool done() const;              ///< \c true, if all arguments are parsed
495
496         void clear();                   ///< Set range empty
497                                         /**< This call will point the current iterator to the end of
498                                              the tokens range.
499                                              \post done() == \c true; */
500
501         bool operator==(ParseCommandInfo::ArgumentIterator const & other) const;
502                                         ///< Compare wrapper against ArgumentIterator
503         bool operator!=(ParseCommandInfo::ArgumentIterator const & other) const;
504                                         ///< Compare wrapper against ArgumentIterator
505         
506         using IteratorFacade::operator++;
507         ParseCommandInfo::ArgumentIterator operator++(int);
508         
509     private:
510         reference dereference() const;
511         void increment();
512
513         ParseCommandInfo::ArgumentIterator i_;
514         ParseCommandInfo::ArgumentIterator e_;
515         std::string msg_;
516
517         friend class boost::iterator_core_access;
518     };
519
520     /**< \brief Output ParseCommandInfo instance
521          \related ParseCommandInfo
522       */
523     std::ostream & operator<<(std::ostream & stream, ParseCommandInfo const & info);
524
525     /** \brief Parse commands
526
527         This class implements a parser for the console/config language. It supports parsing strings
528         as well as files. For every parsed command, a callback function is called.
529
530         \implementation The implementation is based on Boost.Spirit. See the file \ref Parse.ih for
531             the formal language grammar.
532
533         \implementation Parsing an arbitrary iostream is not supported since arbitrary streams are
534             not seekable. If this is needed, it can however be provided using stream iterators and
535             some special iterator adaptors from Boost.Spirit. However, the amount of backtracking
536             needs to be analyzed before this is viable.
537
538         \todo Implement more detailed error reporting and error recovery.
539
540         \ingroup console_parser
541       */
542     class CommandParser
543         : boost::noncopyable
544     {
545     public:
546         ///////////////////////////////////////////////////////////////////////////
547         // Types
548
549         typedef boost::function<void (ParseCommandInfo const &)> Callback;
550
551         ///////////////////////////////////////////////////////////////////////////
552         ///\name Structors and default members
553         ///@{
554
555         CommandParser();
556         ~CommandParser();
557
558         ///@}
559         ///////////////////////////////////////////////////////////////////////////
560
561         bool parse(std::string command, Callback cb); ///< Parse string
562         bool parseFile(std::string filename, Callback cb); ///< Parse file
563                                         /**< \throws SystemException if the file cannot be
564                                              read. */
565
566         bool parseArguments(std::string arguments, ParseCommandInfo & info);
567
568     private:
569         struct Impl;
570
571         Impl & impl();
572
573         boost::scoped_ptr<Impl> impl_;
574     };
575
576 }}
577
578 ///////////////////////////////hh.e////////////////////////////////////////
579 #include "Parse.cci"
580 //#include "Parse.ct"
581 //#include "Parse.cti"
582 #endif
583
584 \f
585 // Local Variables:
586 // mode: c++
587 // fill-column: 100
588 // comment-column: 40
589 // c-file-style: "senf"
590 // indent-tabs-mode: nil
591 // ispell-local-dictionary: "american"
592 // compile-command: "scons -u test"
593 // End: