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