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