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