Add 'unflatten' to doxygen/dot processing
[senf.git] / Utils / 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_SENF_Scheduler_Console_Parse_
27 #define HH_SENF_Scheduler_Console_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>Comments are marked with '#' and continue to the end of the line</td></tr>
89         <tr><td>/</td><td>path component separator</td></tr>
90         <tr><td>( )</td><td>argument grouping</td></tr>
91         <tr><td>{ }</td><td>directory grouping</td></tr>
92         <tr><td>;</td><td>command terminator</td></tr>
93         <tr><td>, =</td><td>punctuation tokens</td></tr>
94     </table>
95
96     \subsection console_basic Basic elements
97
98     A <b>word</b> is \e any sequence of consecutive characters which does not include any special
99     character. Examples for words are thus
100     <pre>
101     12.34
102     jens@fokus.fraunhofer.de
103     eth0
104     1>2
105     </pre>
106
107     The following are \e not valid words:
108     <pre>
109     a/b/c
110     a,b
111     </pre>
112
113     A <b>string literal</b> is just that: A double-quoted string (C/C++ style) possibly with
114     embedded escape chars:
115     <pre>
116     "\"foo\nbar\""
117     "\x04test"
118     </pre>
119     
120     A <b>hex-string literal</b> is used to represent binary data. It looks like a string which has
121     only hexadecimal bytes or whitespace as contents (comments and newlines are Ok when not read
122     from the interactive console)
123     <pre>
124     x"01 02 03 0405"
125     x"01 02   # ID header
126       0405    # payload
127       "
128     </pre>
129
130     A <b>token</b> is a \e word, \e string or \e hex-string, or a single special character (that's
131     true, any special character is allowed as a token). '(' and ')' must be properly nested.
132
133     A <b>path</b> is a sequence of \e words separated by '/' (and optional whitespace). A path may
134     have an optional initial and/or a terminating '/'.
135     <pre>
136     a/b/c
137     foo / bar /
138     /server
139     </pre>
140
141     \subsection console_statements Statements
142     
143     There are several types of statements:
144     \li The bulk of all statements are \e path statements
145     \li There are some \e built-in statements which are mostly useful at the interactive console
146     \li A special form of statement is the <em>directory group</em>
147
148     A <b>path</b> statement consists of a (possibly relative) path followed by any number of
149     arguments and terminated with a ';' (or end-of-input)
150     <pre>
151     /path/to/command arg1 "arg2" (complex=(1 2) another) ;
152     </pre>
153     Every argument is either
154     \li A single word, string or hex-string
155     \li or a parenthesized list of tokens.
156
157     So above command has three arguments: 'arg1', 'arg2' (a single token each) and one argument with
158     the 7 tokens 'complex', '=', '(', '1', '2', ')', 'another'. The interpretation of the arguments
159     is completely up to the command.
160
161     A <b>built-in</b> statement is one of
162     
163     <table class="senf">
164     <tr><td>\c cd \e path</td><td>Change current directory</td></tr>
165     <tr><td>\c ls [ \e path ]</td><td>List contents of \e path or current directory</td></tr>
166     <tr><td>\c exit</td><td>Exit interactive console</td></tr>
167     <tr><td>\c help [ \e path ]</td><td>Show help for \e path or current directory</td></tr>
168     </table>
169
170     A <b>directory group</b> statement is a block of statements all executed relatively to a fixed
171     directory.
172     <pre>
173     /some/path {
174         statement ;
175         . . .
176     }
177     </pre>
178     At the beginning of the block, the current directory is saved and the directory is changed to
179     the given directory. All commands are executed and at the end of the block, the saved directory
180     is restored.
181
182     \section console_parse_api The parser API
183
184     The senf::console::CommandParser is responsible for taking text input and turning it into a
185     sequence of senf::console::ParseCommandInfo structures. The structures are returned by passing
186     them successively to a callback function.
187
188     Every statement is returned as a senf::console::ParseCommandInfo instance. Directory groups are
189     handled specially: They are divided into two special built-in commands called PUSHD and POPD.
190  */
191
192 // Custom includes
193 #include <string>
194 #include <vector>
195 #include <boost/utility.hpp>
196 #include <boost/scoped_ptr.hpp>
197 #include <boost/range/iterator_range.hpp>
198 #include <boost/iterator/iterator_facade.hpp>
199 #include <boost/function.hpp>
200 #include "../../Utils/safe_bool.hh"
201 #include "../../Utils/Exception.hh"
202
203 //#include "Parse.mpp"
204 ///////////////////////////////hh.p////////////////////////////////////////
205
206 namespace senf {
207 namespace console {
208
209     namespace detail { class FilePositionWithIndex; }
210
211     namespace detail { struct ParserAccess; }
212
213     /** \brief Single argument token
214
215         All command arguments are split into tokens by the parser. Each token is returned as an
216         Token instance. 
217
218         \ingroup console_parser
219       */
220     class Token
221     {
222     public:
223         enum TokenType { 
224             None                = 0,
225             PathSeparator       = 0x0001, // '/'
226             ArgumentGroupOpen   = 0x0002, // '('
227             ArgumentGroupClose  = 0x0004, // ')'
228             DirectoryGroupOpen  = 0x0008, // '{'
229             DirectoryGroupClose = 0x0010, // '}'
230             CommandTerminator   = 0x0020, // ';'
231             OtherPunctuation    = 0x0040,
232             BasicString         = 0x0080,
233             HexString           = 0x0100,
234             Word                = 0x0200
235         };
236
237         enum TokenGroup {
238             ArgumentGrouper     = ArgumentGroupOpen 
239                                 | ArgumentGroupClose,
240
241             DirectoryGrouper    = DirectoryGroupOpen 
242                                 | DirectoryGroupClose,
243
244             Punctuation         = DirectoryGroupOpen 
245                                 | DirectoryGroupClose 
246                                 | PathSeparator 
247                                 | CommandTerminator 
248                                 | OtherPunctuation,
249
250             String              = BasicString 
251                                 | HexString,
252
253             SimpleArgument      = Word 
254                                 | BasicString 
255                                 | HexString
256         };
257         
258         Token();                        ///< Create empty token
259         Token(TokenType type, std::string token); 
260                                         ///< Create token with given type and value
261         Token(TokenType type, std::string token, detail::FilePositionWithIndex const & pos); 
262                                         ///< Create token with given type and value
263
264
265         std::string const & value() const; ///< String value of token
266                                         /**< This value is properly unquoted */
267
268         TokenType type() const;         ///< Token type
269
270         unsigned line() const;          ///< Line number of token in source
271         unsigned column() const;        ///< Column number of token in source
272         unsigned index() const;         ///< Index (char count) of token in source
273
274         bool is(unsigned tokens) const; ///< Check, whether tokens type matches \a tokens
275                                         /**< \a tokens is a bit-mask of token types to check. */
276
277         bool operator==(Token const & other) const;
278         bool operator!=(Token const & other) const;
279
280     protected:
281
282     private:
283         TokenType type_;
284         std::string token_;
285         unsigned line_;
286         unsigned column_;
287         unsigned index_;
288     };
289
290     std::ostream & operator<<(std::ostream & os, Token const & token);
291
292     /** \brief Create a \c None token
293         \related Token */
294     Token NoneToken();
295
296     /** \brief Create a \c PathSeparator ['/'] token
297         \related Token */
298     Token PathSeparatorToken();
299
300     /** \brief Create an \c ArgumentGroupOpen ['('] token
301         \related Token */
302     Token ArgumentGroupOpenToken();
303
304     /** \brief Create a \c ArgumentGroupClose [')'] token
305         \related Token */
306     Token ArgumentGroupCloseToken();
307
308     /** \brief Create a \c DirectoryGroupOpen ['{'] token
309         \related Token */
310     Token DirectoryGroupOpenToken();
311
312     /** \brief Create a \c DirectoryGroupClose ['}'] token
313         \related Token */
314     Token DirectoryGroupCloseToken();
315
316     /** \brief Create a \c CommandTerminator [';'] token
317         \related Token */
318     Token CommandTerminatorToken();
319
320     /** \brief Create a \c OtherPunctuation ['=', ','] token with the given \a value
321         \related Token */
322     Token OtherPunctuationToken(std::string const & value);
323
324     /** \brief Create a \c BasicString token with the given \a value
325         \related Token */
326     Token BasicStringToken(std::string const & value);
327
328     /** \brief Create a \c HexString token with the given \a value
329         \related Token */
330     Token HexStringToken(std::string const & value);
331
332     /** \brief Create a \c Word token with the given \a value
333         \related Token */
334     Token WordToken(std::string const & value);
335
336     /** \brief Single parsed console command
337
338         Every command parsed is returned in a ParseCommandInfo instance. This information is purely
339         taken from the parser, no semantic information is attached at this point, the config/console
340         node tree is not involved in any way. ParseCommandInfo consist of
341         
342         \li the type of command: built-in or normal command represented by a possibly relative path
343             into the command tree.
344         \li the command
345         \li the arguments. Every argument consists of a range of Token instances.
346
347         \ingroup console_parser
348       */
349     class ParseCommandInfo
350     {
351         typedef std::vector<Token> Tokens;
352         typedef std::vector<std::string> CommandPath;
353
354     public:
355         class ArgumentIterator; 
356
357         typedef CommandPath::const_iterator path_iterator;
358         typedef Tokens::const_iterator token_iterator;
359         typedef ArgumentIterator argument_iterator;
360         typedef Tokens::size_type size_type;
361
362         typedef boost::iterator_range<path_iterator> CommandPathRange;
363         typedef boost::iterator_range<argument_iterator> ArgumentsRange;
364         typedef boost::iterator_range<token_iterator> TokensRange;
365
366         enum BuiltinCommand { NoBuiltin, 
367                               BuiltinCD, 
368                               BuiltinLS, 
369                               BuiltinLR,
370                               BuiltinPUSHD, 
371                               BuiltinPOPD,
372                               BuiltinEXIT,
373                               BuiltinHELP };
374
375         ParseCommandInfo();
376
377         BuiltinCommand builtin() const; ///< Command type
378                                         /**< \returns \c NoBuiltin, if the command is an ordinary
379                                              command, otherwise the id of the built-in command */
380         TokensRange commandPath() const; ///< Command path
381                                         /**< This is the path to the command if it is not a built-in
382                                              command. Every element of the returned range
383                                              constitutes one path element. If the first element is
384                                              empty, the path is an absolute path, otherwise it is
385                                              relative. If the last element is an empty string, the
386                                              path ends with a '/' char. */
387         ArgumentsRange arguments() const; ///< Command arguments
388                                         /**< The returned range contains one TokensRange for each
389                                              argument. */
390         TokensRange tokens() const;     ///< All argument tokens
391                                         /**< The returned range contains \e all argument tokens in a
392                                              single range not divided into separate arguments. */
393
394         void clear();                   ///< Clear all data members
395         bool empty();                   ///< \c true, if the data is empty
396
397         void builtin(BuiltinCommand builtin); ///< Assign builtin command 
398         void command(std::vector<Token> & commandPath); ///< Assign non-builtin command
399
400         void addToken(Token const & token); ///< Add argument token
401                                         /**< You \e must ensure, that the resulting argument tokens
402                                              are properly nested regarding '()' groups, otherwise
403                                              interpreting arguments using the arguments() call will
404                                              crash the program. */
405
406     protected:
407
408     private:
409         struct MakeRange;
410
411         std::vector<Token> commandPath_;
412         BuiltinCommand builtin_;
413         Tokens tokens_;
414     };
415
416     /** \brief Iterator parsing argument groups
417
418         This special iterator parses a token range returned by the parser into argument ranges. An
419         argument range is either a single token or it is a range of tokens enclosed in matching
420         parenthesis. The ParseCommandInfo::arguments() uses this iterator type. To recursively parse
421         complex arguments, you can however use this iterator to divide a multi-token argument into
422         further argument groups (e.g. to parse a list or vector of items).
423
424         This iterator is a bidirectional iterator \e not a random access iterator.
425      */
426     class ParseCommandInfo::ArgumentIterator
427         : public boost::iterator_facade< ParseCommandInfo::ArgumentIterator, 
428                                          ParseCommandInfo::TokensRange,
429                                          boost::bidirectional_traversal_tag,
430                                          ParseCommandInfo::TokensRange >
431     {
432     public:
433         ArgumentIterator();
434         explicit ArgumentIterator(ParseCommandInfo::TokensRange::iterator i);
435
436     private:
437         reference dereference() const;
438         bool equal(ArgumentIterator const & other) const;
439         void increment();
440         void decrement();
441
442         mutable ParseCommandInfo::TokensRange::iterator b_;
443         mutable ParseCommandInfo::TokensRange::iterator e_;
444
445         void setRange() const;
446
447         friend class boost::iterator_core_access;
448         friend class ParseCommandInfo;
449     };
450
451     /**  \brief Syntax error parsing command arguments exception
452
453         All errors while parsing the arguments of a command must be signaled by throwing an instance
454         of SyntaxErrorException. This is important, so command overloading works.
455      */
456     struct SyntaxErrorException : public senf::Exception
457     { explicit SyntaxErrorException(std::string const & msg = "syntax error") 
458           : senf::Exception(msg) {} };
459
460     /** \brief Wrapper checking argument iterator access for validity
461         
462         CheckedArgumentIteratorWrapper is a wrapper around a range of arguments parsed using the
463         ParseCommandInfo::ArgumentIterator. It is used to parse arguments either in a command
464         (registered with manual argument parsing) or when defining a custom parser.
465         \code
466         void fn(std::ostream & out, senf::console::ParseCommandInfo command)
467         {
468             std:;string arg1;
469             unsigned arg2 (0);
470             
471             {
472                 senf::console::CheckedArgumentIteratorWrapper arg (command.arguments());
473                 senf::console::parse( *(arg++), arg1 );
474                 senf::console::parse( *(arg++), arg2 );
475             }
476
477             // ...
478         }
479         \endcode
480
481         To use the wrapper, you must ensure that:
482         \li You increment the iterator \e past all arguments you parse. The iterator must point to
483             the end of the range when parsing is complete.
484         \li The iterator wrapper is destroyed after parsing but before executing the command itself
485             begins. 
486
487         Accessing a non-existent argument or failing to parse all arguments will raise a
488         senf::console::SyntaxErrorException.
489
490         \see \link console_arg_custom Example customer parser \endlink
491       */
492     class CheckedArgumentIteratorWrapper
493         : boost::noncopyable,
494           public boost::iterator_facade< CheckedArgumentIteratorWrapper,
495                                          ParseCommandInfo::TokensRange,
496                                          boost::forward_traversal_tag,
497                                          ParseCommandInfo::TokensRange >,
498           public senf::safe_bool<CheckedArgumentIteratorWrapper>
499
500     {
501         typedef boost::iterator_facade< CheckedArgumentIteratorWrapper,
502                                         ParseCommandInfo::TokensRange,
503                                         boost::forward_traversal_tag,
504                                         ParseCommandInfo::TokensRange > IteratorFacade;
505
506     public:
507         explicit CheckedArgumentIteratorWrapper(
508             ParseCommandInfo::ArgumentsRange const & range,
509             std::string const & msg = "invalid number of arguments");
510                                         ///< Make wrapper from ArgumentsRange
511                                         /**< This constructs a wrapper from a
512                                              ParseCommandInfo::ArgumentsRange. 
513                                              \param[in] range Range of arguments to parse
514                                              \param[in] msg Error message */
515         explicit CheckedArgumentIteratorWrapper(
516             ParseCommandInfo::TokensRange const & range,
517             std::string const & msg = "invalid number of arguments");
518                                         ///< Make wrapper from TokensRange
519                                         /**< This constructs a wrapper from a
520                                              ParseCommandInfo::TokensRange. The TokensRange is first
521                                              converted into an ParseCommandInfo::ArgumentsRange
522                                              which is then wrapped.
523                                              \param[in] range Range of tokens to  parse
524                                              \param[in] msg Error message */
525
526         ~CheckedArgumentIteratorWrapper(); ///< Check, if all arguments are parsed
527                                         /**< The destructor validates, that all arguments are parsed
528                                              correctly when leaving the scope, in which the wrapper
529                                              is instantiated normally (not by an exception).
530
531                                              \warning This destructor will throw a
532                                              SyntaxErrorException, if not all arguments are parsed
533                                              and when no other exception is in progress. */
534
535         operator ParseCommandInfo::ArgumentIterator(); 
536                                         ///< Use wrapper as ParseCommandInfo::ArgumentIterator
537
538         bool boolean_test() const;      ///< \c true, if more arguments are available
539         bool done() const;              ///< \c true, if all arguments are parsed
540
541         void clear();                   ///< Set range empty
542                                         /**< This call will point the current iterator to the end of
543                                              the tokens range.
544                                              \post done() == \c true; */
545
546         bool operator==(ParseCommandInfo::ArgumentIterator const & other) const;
547                                         ///< Compare wrapper against ArgumentIterator
548         bool operator!=(ParseCommandInfo::ArgumentIterator const & other) const;
549                                         ///< Compare wrapper against ArgumentIterator
550         
551         using IteratorFacade::operator++;
552         ParseCommandInfo::ArgumentIterator operator++(int);
553
554     private:
555         reference dereference() const;
556         void increment();
557
558         ParseCommandInfo::ArgumentIterator i_;
559         ParseCommandInfo::ArgumentIterator e_;
560         std::string msg_;
561
562         friend class boost::iterator_core_access;
563     };
564
565     /**< \brief Output ParseCommandInfo instance
566          \related ParseCommandInfo
567       */
568     std::ostream & operator<<(std::ostream & stream, ParseCommandInfo const & info);
569
570     /** \brief Parse commands
571
572         This class implements a parser for the console/config language. It supports parsing strings
573         as well as files. For every parsed command, a callback function is called.
574
575         \implementation The implementation is based on Boost.Spirit. See the file \ref Parse.ih for
576             the formal language grammar.
577
578         \implementation Parsing an arbitrary iostream is not supported since arbitrary streams are
579             not seekable. If this is needed, it can however be provided using stream iterators and
580             some special iterator adaptors from Boost.Spirit. However, the amount of backtracking
581             needs to be analyzed before this is viable.
582
583         \ingroup console_parser
584       */
585     class CommandParser
586         : boost::noncopyable
587     {
588     public:
589         ///////////////////////////////////////////////////////////////////////////
590         // Types
591
592         typedef boost::function<void (ParseCommandInfo const &)> Callback;
593
594         ///////////////////////////////////////////////////////////////////////////
595         ///\name Structors and default members
596         ///@{
597
598         CommandParser();
599         ~CommandParser();
600
601         ///@}
602         ///////////////////////////////////////////////////////////////////////////
603
604         void parse(std::string const & command, Callback cb); ///< Parse string
605         void parseFile(std::string const & filename, Callback cb); ///< Parse file
606                                         /**< \throws SystemException if the file cannot be
607                                              read. */
608
609         void parseArguments(std::string const & arguments, ParseCommandInfo & info);
610                                         ///< Parse \a arguments
611                                         /**< parseArguments() parses the string \a arguments which
612                                              contains arbitrary command arguments (without the name
613                                              of the command). The argument tokens are written into
614                                              \a info. */
615
616         void parsePath(std::string const & path, ParseCommandInfo & info);
617                                         ///< Parse \a path
618                                         /**< parsePath() parses the string \a path as an arbitrary
619                                              command path. The result is written into \a info. */
620
621         std::string::size_type parseIncremental(std::string const & commands, Callback cb);
622                                         ///< Incremental parse
623                                         /**< An incremental parse will parse all complete statements
624                                              in \a commands. parseIncremental() will return the
625                                              number of characters successfully parsed from \a
626                                              commands. 
627                                              
628                                              \note The incremental parser \e requires all statements
629                                                  to be terminated explicitly. This means, that the
630                                                  last ';' is \e not optional in this case. */
631
632         static bool isSpecialChar(char ch); ///< Check, if \a ch is a special character
633         static bool isPunctuationChar(char ch); ///< Check, if \a ch is a punctuation character
634         static bool isSpaceChar(char ch); ///< Check, if \a ch is a space character
635         static bool isInvalidChar(char ch); ///< Check, if \a ch is an invalid character
636         static bool isWordChar(char ch); ///< Check, if \a ch is a word character
637
638         /** \brief Exception thrown when the parser detects an error */
639         struct ParserErrorException : public SyntaxErrorException
640         { explicit ParserErrorException(std::string const & msg) : SyntaxErrorException(msg) {} };
641
642     private:
643         struct Impl;
644         struct SetIncremental;
645
646         template <class Iterator>
647         Iterator parseLoop(Iterator b, Iterator e, std::string const & source, Callback cb);
648
649         Impl & impl();
650
651         boost::scoped_ptr<Impl> impl_;
652
653         friend class SetIncremental;
654     };
655
656 }}
657
658 ///////////////////////////////hh.e////////////////////////////////////////
659 #include "Parse.cci"
660 //#include "Parse.ct"
661 //#include "Parse.cti"
662 #endif
663
664 \f
665 // Local Variables:
666 // mode: c++
667 // fill-column: 100
668 // comment-column: 40
669 // c-file-style: "senf"
670 // indent-tabs-mode: nil
671 // ispell-local-dictionary: "american"
672 // compile-command: "scons -u test"
673 // End: