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