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