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