Console: Refactor argument parsing into iterator
[senf.git] / Console / Parse.hh
1 // $Id$
2 //
3 // Copyright (C) 2008 
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief Parse public header */
25
26 #ifndef HH_Parse_
27 #define HH_Parse_ 1
28
29 /** \defgroup console_parser The parser
30
31     The console/config library defines a simple language used to interact with the console or to
32     configure the application.  The parser is not concerned about interpreting commands or
33     arguments, checking that a command exists or managing directories. The parser just takes the
34     input and parses it.
35
36     \autotoc
37
38     \section console_language The Language
39
40     The config/console language is used in configuration files and interactively at the
41     console. Some features of the language are more useful in config files, others at the
42     interactive console but the language is the same in both cases.
43
44     Let's start with a sample of the config/console language. The following is written as a
45     configuration file
46     \code
47     # My someserver configuration file
48     
49     /server/port 1234;
50     
51     /logger/targets {
52         console {
53             accept senf::log::Debug IMPORTANT;
54             accept server::ServerLog CRITICAL;
55         }
56
57         provide serverlog senf::log::FileTarget "/var/log/server.log";
58         serverlog {
59             reject senf::log::Debug senf::Console::Server NOTICE;
60             accept senf::log::Debug NOTICE;
61             accept server::ServerLog;
62         }
63     }
64
65     /server/stuffing (UDPPacket x"01 02 03 04");
66     /server/allow_hosts 10.1.2.3               # our internal server
67                         10.2.3.4 10.4.3.5      # client workstations
68         ;
69
70     /help/infoUrl "http://senf.j32.de/src/doc";
71     \endcode
72
73     The interactive syntax is the same with some notes:
74     \li All commands must be complete on a single line. This includes grouping constructs which must
75         be closed on the same line they are opened.
76     \li The last ';' is optional. However, multiple commands may be entered on a single line when
77         they are separated by ';'.
78     \li An empty line on the interactive console will repeat the last command.
79
80     The language consists of a small number of syntactic entities:
81
82     \subsection console_special_chars Special characters
83
84     These are characters, which have a special meaning. Some are used internally, others are just
85     returned as punctuation tokens
86     
87     <table class="senf">
88     <tr><td>/</td><td>path component separator</td></tr>
89     <tr><td>( )</td><td>argument grouping</td></tr>
90     <tr><td>{ }</td><td>directory grouping</td></tr>
91     <tr><td>;</td><td>command terminator</td></tr>
92     <tr><td>, =</td><td>punctuation tokens</td></tr>
93     </table>
94
95     \subsection console_basic Basic elements
96
97     A <b>word</b> is \e any sequence of consecutive characters which does not include any special
98     character. Examples for words are thus
99     <pre>
100     12.34
101     jens@fokus.fraunhofer.de
102     eth0
103     1>2
104     </pre>
105
106     The following are \e not valid words:
107     <pre>
108     a/b/c
109     a,b
110     </pre>
111
112     A <b>string literal</b> is just that: A double-quoted string (C/C++ style) possibly with
113     embedded escape chars:
114     <pre>
115     "\"foo\nbar\""
116     "\x04test"
117     </pre>
118     
119     A <b>hex-string literal</b> is used to represent binary data. It looks like a string which has
120     only hexadecimal bytes or whitespace as contents (comments and newlines are Ok when not read
121     from the interactive console)
122     <pre>
123     x"01 02 03 0405"
124     x"01 02   # ID header
125       0405    # payload
126       "
127     </pre>
128
129     A <b>token</b> is a \e word, \e string or \e hex-string, or a single special character (that's
130     true, any special character is allowed as a token). '(' and ')' must be properly nested.
131
132     A <b>path</b> is a sequence of \e words separated by '/' (and optional whitespace). A path may
133     have an optional initial and/or a terminating '/'.
134     <pre>
135     a/b/c
136     foo / bar /
137     /server
138     </pre>
139
140     \subsection console_statements Statements
141     
142     There are several types of statements:
143     \li The bulk of all statements are \e path statements
144     \li There are some \e built-in statements which are mostly useful at the interactive console
145     \li A special form of statement is the <em>directory group</em>
146
147     A <b>path</b> statement consists of a (possibly relative) path followed by any number of
148     arguments and terminated with a ';' (or end-of-input)
149     <pre>
150     /path/to/command arg1 "arg2" (complex=(1 2) another) ;
151     </pre>
152     Every argument is either
153     \li A single word, string or hex-string
154     \li or a parenthesized list of tokens.
155
156     So above command has three arguments: 'arg1', 'arg2' (a single token each) and one argument with
157     the 7 tokens 'complex', '=', '(', '1', '2', ')', 'another'. The interpretation of the arguments
158     is completely up to the command.
159
160     A <b>built-in</b> statement is one of
161     
162     <table class="senf">
163     <tr><td>\c cd \e path</td><td>Change current directory</td></tr>
164     <tr><td>\c ls [ \e path ]</td><td>List contents of \e path or current directory</td></tr>
165     <tr><td>\c exit</td><td>Exit interactive console</td></tr>
166     <tr><td>\c help [ \e path ]</td><td>Show help for \e path or current directory</td></tr>
167     </table>
168
169     A <b>directory group</b> statement is a block of statements all executed relatively to a fixed
170     directory.
171     <pre>
172     /some/path {
173         statement ;
174         . . .
175     }
176     </pre>
177     At the beginning of the block, the current directory is saved and the directory is changed to
178     the given directory. All commands are executed and at the end of the block, the saved directory
179     is restored.
180
181     \section console_parse_api The parser API
182
183     The senf::console::CommandParser is responsible for taking text input and turning it into a
184     sequence of senf::console::ParseCommandInfo structures. The structures are returned by passing
185     them successively to a callback function.
186
187     Every statement is returned as a senf::console::ParseCommandInfo instance. Directory groups are
188     handled specially: They are divided into two special built-in commands called PUSHD and POPD.
189  */
190
191 // Custom includes
192 #include <string>
193 #include <vector>
194 #include <boost/utility.hpp>
195 #include <boost/scoped_ptr.hpp>
196 #include <boost/range/iterator_range.hpp>
197 #include <boost/iterator/iterator_facade.hpp>
198 #include <boost/function.hpp>
199
200 //#include "Parse.mpp"
201 ///////////////////////////////hh.p////////////////////////////////////////
202
203 namespace senf {
204 namespace console {
205
206     namespace detail { struct ParserAccess; }
207
208     /** \brief Single argument token
209
210         All command arguments are split into tokens by the parser. Each token is returned as an
211         ArgumentToken instance. 
212
213         \ingroup console_parser
214       */
215     class ArgumentToken
216     {
217     public:
218         enum TokenType { 
219             PathSeparator       = 0x0001,
220             ArgumentGroupOpen   = 0x0002,
221             ArgumentGroupClose  = 0x0004,
222             DirectoryGroupOpen  = 0x0008,
223             DirectoryGroupClose = 0x0010,
224             CommandTerminator   = 0x0020,
225             OtherPunctuation    = 0x0040,
226             BasicString         = 0x0080,
227             HexString           = 0x0100,
228             Word                = 0x0200
229         };
230
231         enum TokenGroup {
232             ArgumentGrouper     = ArgumentGroupOpen 
233                                 | ArgumentGroupClose,
234
235             DirectoryGrouper    = DirectoryGroupOpen 
236                                 | DirectoryGroupClose,
237
238             Punctuation         = DirectoryGroupOpen 
239                                 | DirectoryGroupClose 
240                                 | PathSeparator 
241                                 | CommandTerminator 
242                                 | OtherPunctuation,
243
244             String              = BasicString 
245                                 | HexString,
246
247             SimpleArgument      = Word 
248                                 | BasicString 
249                                 | HexString
250         };
251         
252         std::string const & value() const; ///< String value of token
253                                         /**< This value is properly unquoted */
254
255         TokenType type() const;         ///< Token type
256
257         bool is(unsigned tokens) const; ///< Check, whether tokens type matches \a tokens
258                                         /**< \a tokens is a bit-mask of token types to check. */
259
260     protected:
261
262     private:
263         ArgumentToken(TokenType type, std::string token);
264
265         TokenType type_;
266         std::string token_;
267
268         friend class detail::ParserAccess;
269     };
270
271     /** \brief Single parsed console command
272
273         Every command parsed is returned in a ParseCommandInfo instance. This information is purely
274         taken from the parser, no semantic information is attached at this point, the config/console
275         node tree is not involved in any why. ParseCommandInfo consist of
276         
277         \li the type of command: built-in or normal command represented by a possibly relative path
278             into the command tree.
279         \li the command
280         \li the arguments. Every argument consists of a range of ArgumentToken instances.
281
282         \ingroup console_parser
283       */
284     class ParseCommandInfo
285     {
286         typedef std::vector<ArgumentToken> Tokens;
287         typedef std::vector<std::string> CommandPath;
288         class ArgumentIterator; 
289
290     public:
291         typedef CommandPath::const_iterator path_iterator;
292         typedef Tokens::const_iterator token_iterator;
293         typedef ArgumentIterator argument_iterator;
294         typedef Tokens::size_type size_type;
295
296         typedef boost::iterator_range<path_iterator> CommandPathRange;
297         typedef boost::iterator_range<argument_iterator> ArgumentsRange;
298         typedef boost::iterator_range<token_iterator> TokensRange;
299
300         enum BuiltinCommand { NoBuiltin, 
301                               BuiltinCD, 
302                               BuiltinLS, 
303                               BuiltinPUSHD, 
304                               BuiltinPOPD,
305                               BuiltinEXIT,
306                               BuiltinHELP };
307
308         BuiltinCommand builtin() const; ///< Command type
309                                         /**< \returns \c NoBuiltin, if the command is an ordinary
310                                              command, otherwise the id of the built-in command */
311         CommandPathRange commandPath() const; ///< Command path
312                                         /**< This is the path to the command if it is not a built-in
313                                              command. Every element of the returned range
314                                              constitutes one path element. If the first element is
315                                              empty, the path is an absolute path, otherwise it is
316                                              relative. If the last element is an empty string, the
317                                              path ends with a '/' char. */
318         ArgumentsRange arguments() const; ///< Command arguments
319                                         /**< The returned range contains one TokensRange for each
320                                              argument. */
321         TokensRange tokens() const;     ///< All argument tokens
322                                         /**< The returned range contains \e all argument tokens in a
323                                              single range not divided into separate arguments. */
324     protected:
325
326     private:
327         void init();
328         void setBuiltin(BuiltinCommand builtin);
329         void setCommand(std::vector<std::string> & commandPath);
330         void addToken(ArgumentToken const & token);
331
332         struct MakeRange;
333
334         std::vector<std::string> commandPath_;
335         BuiltinCommand builtin_;
336         Tokens tokens_;
337
338         friend class detail::ParserAccess;
339     };
340
341     class ParseCommandInfo::ArgumentIterator
342         : public boost::iterator_facade< ParseCommandInfo::ArgumentIterator, 
343                                          ParseCommandInfo::TokensRange,
344                                          boost::bidirectional_traversal_tag,
345                                          ParseCommandInfo::TokensRange >
346     {
347         ArgumentIterator();
348
349     private:
350         ArgumentIterator(ParseCommandInfo::TokensRange::iterator i);
351
352         reference dereference() const;
353         bool equal(ArgumentIterator const & other) const;
354         void increment();
355         void decrement();
356
357         mutable ParseCommandInfo::TokensRange::iterator b_;
358         mutable ParseCommandInfo::TokensRange::iterator e_;
359
360         void setRange() const;
361
362         friend class boost::iterator_core_access;
363         friend class ParseCommandInfo;
364     };
365
366     /**< \brief Output ParseCommandInfo instance
367          \related ParseCommandInfo
368       */
369     std::ostream & operator<<(std::ostream & stream, ParseCommandInfo const & info);
370
371     /** \brief Parse commands
372
373         This class implements a parser for the console/config language. It supports parsing strings
374         as well as files. For every parsed command, a callback function is called.
375
376         \implementation The implementation is based on Boost.Spirit. See the file \ref Parse.ih for
377             the formal language grammar.
378
379         \implementation Parsing an arbitrary iostream is not supported since arbitrary streams are
380             not seekable. If this is needed, it can however be provided using stream iterators and
381             some special iterator adaptors from Boost.Spirit. However, the amount of backtracking
382             needs to be analyzed before this is viable.
383
384         \todo Implement more detailed error reporting and error recovery.
385
386         \ingroup console_parser
387       */
388     class CommandParser
389         : boost::noncopyable
390     {
391     public:
392         ///////////////////////////////////////////////////////////////////////////
393         // Types
394
395         typedef boost::function<void (ParseCommandInfo const &)> Callback;
396
397         ///////////////////////////////////////////////////////////////////////////
398         ///\name Structors and default members
399         ///@{
400
401         CommandParser();
402         ~CommandParser();
403
404         ///@}
405         ///////////////////////////////////////////////////////////////////////////
406
407         bool parse(std::string command, Callback cb); ///< Parse string
408         bool parseFile(std::string filename, Callback cb); ///< Parse file
409                                         /**< \throws SystemException if the file cannot be
410                                              read. */
411
412     private:
413         struct Impl;
414
415         Impl & impl();
416
417         boost::scoped_ptr<Impl> impl_;
418     };
419
420 }}
421
422 ///////////////////////////////hh.e////////////////////////////////////////
423 #include "Parse.cci"
424 //#include "Parse.ct"
425 //#include "Parse.cti"
426 #endif
427
428 \f
429 // Local Variables:
430 // mode: c++
431 // fill-column: 100
432 // comment-column: 40
433 // c-file-style: "senf"
434 // indent-tabs-mode: nil
435 // ispell-local-dictionary: "american"
436 // compile-command: "scons -u test"
437 // End: