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