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