Utils/Logger: Remove dependency on libboost_datetime
[senf.git] / Console / Parse.ih
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 internal header */
25
26 #ifndef IH_Parse_
27 #define IH_Parse_ 1
28
29 // Custom includes
30 #include <vector>
31 #include <boost/regex.hpp>
32 #include <boost/spirit.hpp>
33 #include <boost/spirit/utility/grammar_def.hpp>
34 #include <boost/spirit/actor.hpp>
35 #include <boost/bind.hpp>
36 #include <boost/function.hpp>
37 #include <boost/ref.hpp>
38
39 ///////////////////////////////ih.p////////////////////////////////////////
40
41 namespace senf {
42 namespace console {
43 namespace detail {
44
45 #ifndef DOXYGEN
46
47     ///////////////////////////////////////////////////////////////////////////
48     // append_a
49
50     struct append_action
51     {
52         template <class T, class Value>
53         void act(T & ref, Value const & value) const
54             { ref += T(1, value); }
55
56         template <class T, class Iterator>
57         void act(T & ref, Iterator const & f, Iterator const & l) const
58             { ref += T(f,l); }
59     };
60
61     template <class T>
62     inline boost::spirit::ref_value_actor<T, append_action> 
63     append_a(T & ref)
64     { return boost::spirit::ref_value_actor<T, append_action>(ref); }
65     
66     template <class T, class Value>
67     inline boost::spirit::ref_const_ref_actor<T, Value, append_action> 
68     append_a(T & ref, Value const & value)
69     { return boost::spirit::ref_const_ref_actor<T, Value, append_action>(ref, value); }
70
71     ///////////////////////////////////////////////////////////////////////////
72     // Grammar
73
74     template <class ParseDispatcher>
75     struct CommandGrammar : boost::spirit::grammar<CommandGrammar<ParseDispatcher> >
76     {
77         ///////////////////////////////////////////////////////////////////////////
78         // Start rules
79
80         enum { CommandParser, SkipParser };
81
82         ///////////////////////////////////////////////////////////////////////////
83         // The parse context (variables needed while parsing)
84
85         typedef ArgumentToken::TokenType TokenType;
86
87         struct Context {
88             std::string str;
89             std::vector<std::string> path;
90             char ch;
91             TokenType type;
92             
93             // OUCH ... This is sooooo stupid .. push_back_a and assign_a take their 
94             // arguments by const-reference and STORE the REFERENCE ... they do NOT accept
95             // literal values !!!!!! 
96             static const TokenType BasicString;
97             static const TokenType HexString;
98             static const TokenType Word;
99             static const std::string EmptyString;
100         };
101
102         Context & context;
103
104         ///////////////////////////////////////////////////////////////////////////
105         // Dispatching semantic actions
106
107         ParseDispatcher & dispatcher;
108
109         struct Dispatch_actor
110         {
111             Dispatch_actor(boost::function<void ()> fn_) : fn (fn_) {}
112
113             template <class Value>
114             void operator()(Value const & value) const
115                 { fn(); }
116
117             template <class Iterator>
118             void operator()(Iterator const & f, Iterator const & l) const
119                 { fn(); }
120
121             boost::function<void ()> fn;
122         };
123         
124         template <class Callback>
125         Dispatch_actor dispatch(Callback cb) const
126             { return Dispatch_actor(boost::bind(cb, boost::ref(dispatcher))); }
127
128         template <class Callback, class Arg>
129         Dispatch_actor dispatch(Callback cb, Arg const & arg) const
130             { return Dispatch_actor(boost::bind(cb, boost::ref(dispatcher), arg)); }
131
132         template <class Callback, class Arg1, class Arg2>
133         Dispatch_actor dispatch(Callback cb, Arg1 const & arg1, Arg2 const & arg2) const
134             { return Dispatch_actor(boost::bind(cb, boost::ref(dispatcher), arg1, arg2)); }
135
136         ///////////////////////////////////////////////////////////////////////////
137
138         CommandGrammar(ParseDispatcher & d, Context & c) 
139             : context(c), dispatcher(d) {}
140
141         template <class Scanner>
142         struct definition 
143             : public boost::spirit::grammar_def< boost::spirit::rule<Scanner>, 
144                                                  boost::spirit::rule<Scanner> >
145         {
146             boost::spirit::rule<Scanner> command, path, argument, word, string, hexstring, token,
147                 punctuation, hexbyte, balanced_tokens, simple_argument, complex_argument, builtin, 
148                 skip, commands, block, statement, relpath, abspath;
149             boost::spirit::chset<> special_p, punctuation_p, space_p, invalid_p, word_p;
150             boost::spirit::distinct_parser<> keyword_p;
151
152             definition(CommandGrammar const & self) : 
153
154                 // Characters with a special meaning within the parser
155                 special_p ("/(){};"),
156
157                 // Characters which are returned as punctuation tokens
158                 // (only allowed within '()')
159                 punctuation_p (",=/{};"),
160
161                 // Whitespace characters
162                 space_p (" \t\n\r"),
163
164                 // Invalid characters: All chars below \x20 (space) which are not space_p
165                 // (don't put a \0 in the chset<> argument *string* ...)
166                 invalid_p (
167                     boost::spirit::chset<>('\0') | boost::spirit::chset<>("\x01-\x20") - space_p ),
168
169                 // Valid word characters
170                 word_p (
171                     boost::spirit::anychar_p - special_p - punctuation_p - space_p - invalid_p),
172
173                 // Keywords must not be followed by a word char or '/'
174                 keyword_p ( word_p | boost::spirit::ch_p('/') )
175
176             {
177                 using namespace boost::spirit;
178                 typedef ParseDispatcher PD;
179
180                 ///////////////////////////////////////////////////////////////////
181                 // Spirit grammar
182                 //
183                 // Syntax summary:
184                 // This is EBNF with some minor tweaks to accommodate C++ syntax
185                 //
186                 //   * and +    like EBNF but they precede their argument
187                 //   >>         is followed by
188                 //   !          optional
189                 //   a % b      match any number of a's separated by b
190                 //   a - b      match a but not b
191                 //
192                 // Beside this, we use some special parsers (ch_p, eps_p, confix_p, lex_escape_ch_p,
193                 // keyword_p, comment_p) and directives (lexeme_d), however, the parser should be
194                 // quite readable.
195                 //   
196                 //   ch_p             match character
197                 //   eps_p            always matches nothing (to attach unconditional actions)
198                 //   confix_p(a,b,c)  match b, preceded by a and terminated by c. Used to parse
199                 //                    string literals and comments
200                 //   lex_escape_ch_p  match a lex style escape char. This is like a C++ style
201                 //                    literal string escape char, however \x will be replaced by 'x'
202                 //                    for any char 'x' if it has no special meaning.
203                 //   keyword_p        match a delimited keyword
204                 //   comment_p(a,b)   match comment starting with a and terminated with b. b
205                 //                    defaults to end-of-line
206                 //
207                 //   lexeme_d         don't skip whitespace (as defined by the skip parser)
208                 //
209                 // Aligned to the right at column 50 are semantic actions.
210                 //
211                 // For clarity, I have used 'ch_p' explicitly throughout even though it is optional
212                 // in most cases.
213                 //
214                 // More info is in the Boost.Spirit documentation
215
216                 commands
217                     =  * command
218                     ;
219
220                 command 
221                     =    builtin >> (ch_p(';') | end_p)
222                     |    path  >> ( block | statement )
223                     |    ch_p(';') // Ignore empty commands
224                     ;
225
226                 builtin
227                     =    keyword_p("cd") 
228                       >> path
229                       >> eps_p                    [ self.dispatch(&PD::builtin_cd,
230                                                                   boost::ref(self.context.path)) ]
231                     |    keyword_p("ls")
232                       >> ! path
233                       >> eps_p                    [ self.dispatch(&PD::builtin_ls,
234                                                                   boost::ref(self.context.path)) ]
235                     |    keyword_p("exit")        [ self.dispatch(&PD::builtin_exit) ]
236                     
237                     |    keyword_p("help")
238                       >> ! path
239                       >> eps_p                    [ self.dispatch(&PD::builtin_help,
240                                                                   boost::ref(self.context.path)) ]
241                     ;
242
243                 block
244                     =    ch_p('{')                [ self.dispatch(&PD::pushDirectory,
245                                                                   boost::ref(self.context.path)) ]
246                       >> * command 
247                       >> ch_p('}')                [ self.dispatch(&PD::popDirectory) ]
248                     ;
249
250                 statement
251                     = eps_p                       [ self.dispatch(&PD::beginCommand, 
252                                                                   boost::ref(self.context.path)) ]
253                       >> * argument
254                       >> (ch_p(';') | end_p)
255                       >> eps_p                    [ self.dispatch(&PD::endCommand) ]
256                     ;
257
258                 argument
259                     =    simple_argument          [ self.dispatch(&PD::pushArgument, 
260                                                                   boost::ref(self.context.type),
261                                                                   boost::ref(self.context.str)) ]
262                     |    complex_argument
263                     ;
264                 
265                 simple_argument         // All these return their value in context.str
266                     =    string
267                     |    hexstring
268                     |    word
269                     ;
270                 
271                 complex_argument        // Argument consists of multiple tokens
272                     =    ch_p('(')                [ self.dispatch(&PD::openGroup) ]
273                       >> * token
274                       >> ch_p(')')                [ self.dispatch(&PD::closeGroup) ]
275                     ;
276
277                 string                  // Returns value in context.str
278                     =    eps_p                    [ clear_a(self.context.str) ]
279                       >> eps_p                    [ assign_a(self.context.type, 
280                                                              self.context.BasicString) ]
281                       >> lexeme_d
282                          [
283                              ch_p('"')
284                           >> * ( ( lex_escape_ch_p[ assign_a(self.context.ch) ] 
285                                    - '"' 
286                                  )                [ append_a(self.context.str,
287                                                              self.context.ch) ] 
288                                )
289                           >> ch_p('"')
290                          ]
291                     ;
292
293                 hexstring               // Returns value in context.str
294                     =    eps_p                    [ clear_a(self.context.str) ]
295                       >> eps_p                    [ assign_a(self.context.type, 
296                                                              self.context.HexString) ]
297                       >> confix_p( "x\"", * hexbyte, '"' )
298                     ;
299
300                 path                    // Returns value in context.path
301                     =    eps_p                    [ clear_a(self.context.path) ]
302                       >> relpath | abspath
303                     ;
304
305                 relpath
306                     =    (   word                 [ push_back_a(self.context.path) ]
307                            % ch_p('/') )
308                       >> ( ! ch_p('/')            [ push_back_a(self.context.path,
309                                                                 self.context.EmptyString) ] )
310                     ;
311
312                 abspath
313                     =    ch_p('/')                [ push_back_a(self.context.path,
314                                                                 self.context.EmptyString) ]
315                       >> ( relpath
316                          | eps_p                  [ push_back_a(self.context.path, "") ] )
317                     ;
318
319                 balanced_tokens 
320                     =    ch_p('(')                [ self.dispatch(&PD::pushPunctuation, "(") ]
321                       >> * token
322                       >> ch_p(')')                [ self.dispatch(&PD::pushPunctuation, ")") ]
323                     ;
324
325                 token
326                     =    simple_argument          [ self.dispatch(&PD::pushWord,
327                                                                   boost::ref(self.context.type), 
328                                                                   boost::ref(self.context.str)) ]
329                     |    punctuation              [ self.dispatch(&PD::pushPunctuation,
330                                                                   boost::ref(self.context.str)) ]
331                     |    balanced_tokens
332                     ;
333
334                 punctuation             // Returns value in context.str
335                     =    punctuation_p            [ assign_a(self.context.str) ]
336                     ;
337
338                 word                    // Returns value in context.str
339                     =    lexeme_d
340                          [
341                              eps_p                [ assign_a(self.context.type,
342                                                              self.context.Word) ]
343                           >> (+ word_p)           [ assign_a(self.context.str) ]
344                          ]
345                     ;
346
347                 hexbyte
348                     =    uint_parser<char, 16, 2, 2>()
349                                                   [ append_a(self.context.str) ]
350                     ;
351
352                 skip
353                     =    space_p | comment_p('#')
354                     ;
355
356                 ///////////////////////////////////////////////////////////////////
357
358                 start_parsers(
359                     commands,           // CommandParser
360                     skip                // SkipParser
361                 );
362
363                 BOOST_SPIRIT_DEBUG_TRACE_RULE(command,1);
364                 BOOST_SPIRIT_DEBUG_TRACE_RULE(path,1);
365                 BOOST_SPIRIT_DEBUG_TRACE_RULE(argument,1);
366                 BOOST_SPIRIT_DEBUG_TRACE_RULE(word,1);
367                 BOOST_SPIRIT_DEBUG_TRACE_RULE(string,1);
368                 BOOST_SPIRIT_DEBUG_TRACE_RULE(hexstring,1);
369                 BOOST_SPIRIT_DEBUG_TRACE_RULE(token,1);
370                 BOOST_SPIRIT_DEBUG_TRACE_RULE(punctuation,1);
371                 BOOST_SPIRIT_DEBUG_TRACE_RULE(hexbyte,1);
372                 BOOST_SPIRIT_DEBUG_TRACE_RULE(balanced_tokens,1);
373                 BOOST_SPIRIT_DEBUG_TRACE_RULE(simple_argument,1);
374                 BOOST_SPIRIT_DEBUG_TRACE_RULE(complex_argument,1);
375                 BOOST_SPIRIT_DEBUG_TRACE_RULE(builtin,1);
376                 BOOST_SPIRIT_DEBUG_TRACE_RULE(commands,1);
377                 BOOST_SPIRIT_DEBUG_TRACE_RULE(block,1);
378                 BOOST_SPIRIT_DEBUG_TRACE_RULE(statement,1);
379                 BOOST_SPIRIT_DEBUG_TRACE_RULE(relpath,1);
380                 BOOST_SPIRIT_DEBUG_TRACE_RULE(abspath,1);
381             }
382         };
383     };
384
385     template <class ParseDispatcher>
386     ArgumentToken::TokenType const CommandGrammar<ParseDispatcher>::Context::BasicString (
387         ArgumentToken::BasicString);
388
389     template <class ParseDispatcher>
390     ArgumentToken::TokenType const CommandGrammar<ParseDispatcher>::Context::HexString(
391         ArgumentToken::HexString);
392
393     template <class ParseDispatcher>
394     ArgumentToken::TokenType const CommandGrammar<ParseDispatcher>::Context::Word(
395         ArgumentToken::Word);
396
397     template <class ParseDispatcher>
398     std::string const CommandGrammar<ParseDispatcher>::Context::EmptyString;
399
400 #endif
401
402 }}}
403
404 ///////////////////////////////ih.e////////////////////////////////////////
405 #endif
406
407 \f
408 // Local Variables:
409 // mode: c++
410 // fill-column: 100
411 // comment-column: 40
412 // c-file-style: "senf"
413 // indent-tabs-mode: nil
414 // ispell-local-dictionary: "american"
415 // compile-command: "scons -u test"
416 // End: