Console: Make the parser interface callback based
[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/bind.hpp>
36 #include <boost/function.hpp>
37 #include <boost/ref.hpp>
38
39 ///////////////////////////////ih.p////////////////////////////////////////
40
41 namespace senf {
42 namespace console {
43 namespace detail {
44
45     struct append_action
46     {
47         template <class T, class Value>
48         void act(T & ref, Value const & value) const
49             { ref += T(1, value); }
50
51         template <class T, class Iterator>
52         void act(T & ref, Iterator const & f, Iterator const & l) const
53             { ref += T(f,l); }
54     };
55
56     template <class T>
57     inline boost::spirit::ref_value_actor<T, append_action> 
58     append_a(T & ref)
59     {
60         return boost::spirit::ref_value_actor<T, append_action>(ref);
61     }
62     
63     template <class T, class Value>
64     inline boost::spirit::ref_const_ref_actor<T, Value, append_action> 
65     append_a(T & ref, Value const & value)
66     {
67         return boost::spirit::ref_const_ref_actor<T, Value, append_action>(ref, value);
68     }
69
70     template <class ParseDispatcher>
71     struct CommandGrammar : boost::spirit::grammar<CommandGrammar<ParseDispatcher> >
72     {
73         ///////////////////////////////////////////////////////////////////////////
74         // Start rules
75
76         enum { CommandParser, SkipParser };
77
78         ///////////////////////////////////////////////////////////////////////////
79         // The parse context (variables needed while parsing)
80
81         struct Context {
82             std::string str;
83             std::vector<std::string> path;
84             char ch;
85         };
86
87         Context & context;
88
89         ///////////////////////////////////////////////////////////////////////////
90         // Dispatching semantic actions
91
92         ParseDispatcher & dispatcher;
93
94         struct Dispatch_actor
95         {
96             Dispatch_actor(boost::function<void ()> fn_) : fn (fn_) {}
97
98             template <class Value>
99             void operator()(Value const & value) const
100                 { fn(); }
101
102             template <class Iterator>
103             void operator()(Iterator const & f, Iterator const & l) const
104                 { fn(); }
105
106             boost::function<void ()> fn;
107         };
108         
109         template <class Callback>
110         Dispatch_actor dispatch(Callback cb) const
111             { return Dispatch_actor(boost::bind(cb, boost::ref(dispatcher))); }
112
113         template <class Callback, class Arg>
114         Dispatch_actor dispatch(Callback cb, Arg const & arg) const
115             { return Dispatch_actor(boost::bind(cb, boost::ref(dispatcher), arg)); }
116
117         ///////////////////////////////////////////////////////////////////////////
118
119         CommandGrammar(ParseDispatcher & d, Context & c) 
120             : context(c), dispatcher(d) {}
121
122         template <class Scanner>
123         struct definition 
124             : public boost::spirit::grammar_def< boost::spirit::rule<Scanner>, 
125                                                  boost::spirit::rule<Scanner> >
126         {
127             boost::spirit::rule<Scanner> command, path, argument, word, string, hexstring, token,
128                 punctuation, hexbyte, balanced_tokens, simple_argument, complex_argument, builtin, 
129                 skip, commands, block, statement;
130             boost::spirit::chset<> special_p, punctuation_p, space_p, invalid_p, word_p;
131             boost::spirit::distinct_parser<> keyword_p;
132
133             definition(CommandGrammar const & self) : 
134
135                 // Characters with a special meaning within the parser
136                 special_p ("/(){};"),
137
138                 // Characters which are returned as punctuation tokens
139                 punctuation_p (",="),
140
141                 // Whitespace characters
142                 space_p (" \t\n\r"),
143
144                 // Invalid characters: All chars below \x20 (space) which are not space_p
145                 // (don't put a \0 in the chset<> argument *string* ...)
146                 invalid_p (
147                     boost::spirit::chset<>('\0') | boost::spirit::chset<>("\x01-\x20") - space_p ),
148
149                 // Valid word characters
150                 word_p (
151                     boost::spirit::anychar_p - special_p - punctuation_p - space_p - invalid_p),
152
153                 // Keywords must not be followed by a word char or '/'
154                 keyword_p ( word_p | boost::spirit::ch_p('/') )
155
156             {
157                 using namespace boost::spirit;
158                 typedef ParseDispatcher PD;
159
160                 commands
161                     =  * command
162                     ;
163
164                 command 
165                     =    builtin
166                     |    path  >> ( block | statement )
167                     ;
168
169                 builtin
170                     =    keyword_p("cd") 
171                       >> path
172                       >> eps_p                    [ self.dispatch(&PD::builtin_cd,
173                                                                   boost::ref(self.context.path)) ]
174                     |    keyword_p("ls") 
175                       >> ! path
176                       >> eps_p                    [ self.dispatch(&PD::builtin_ls,
177                                                                   boost::ref(self.context.path)) ]
178                     |    keyword_p("exit")        [ self.dispatch(&PD::builtin_exit) ]
179                     ;
180
181                 block
182                     =    ch_p('{')                [ self.dispatch(&PD::pushDirectory,
183                                                                   boost::ref(self.context.path)) ]
184                       >> * command 
185                       >> ch_p('}')                [ self.dispatch(&PD::popDirectory) ]
186                     ;
187
188                 statement
189                     = eps_p                       [ self.dispatch(&PD::beginCommand, 
190                                                                   boost::ref(self.context.path)) ]
191                       >> * argument
192                       >> (ch_p(';') | end_p)
193                       >> eps_p                    [ self.dispatch(&PD::endCommand) ]
194                     ;
195
196                 argument
197                     =    simple_argument          [ self.dispatch(&PD::pushArgument, 
198                                                                   boost::ref(self.context.str)) ]
199                     |    complex_argument
200                     ;
201                 
202                 simple_argument         // All these return their value in context.str
203                     =    string
204                     |    hexstring
205                     |    word
206                     ;
207                 
208                 complex_argument        // Argument consists of multiple tokens
209                     =    ch_p('(')                [ self.dispatch(&PD::openGroup) ]
210                       >> * token
211                       >> ch_p(')')                [ self.dispatch(&PD::closeGroup) ]
212                     ;
213
214                 string                  // Returns value in context.str
215                     =    eps_p                    [ clear_a(self.context.str) ]
216                       >> lexeme_d
217                          [
218                              ch_p('"')
219                           >> * ( ( lex_escape_ch_p[ assign_a(self.context.ch) ] 
220                                    - '"' 
221                                  )                [ append_a(self.context.str,
222                                                              self.context.ch) ] 
223                                )
224                           >> ch_p('"')
225                          ]
226                     ;
227
228                 hexstring               // Returns value in context.str
229                     =    eps_p                    [ clear_a(self.context.str) ]
230                       >> confix_p( "x\"", * hexbyte, '"' )
231                     ;
232
233                 path                    // Returns value in context.path
234                     =    eps_p                    [ clear_a(self.context.path) ]
235                       >> ( ! ch_p('/')            [ push_back_a(self.context.path, "") ] ) 
236                       >> (   word                 [ push_back_a(self.context.path) ] 
237                            % ch_p('/') )
238                       >> ( ! ch_p('/')            [ push_back_a(self.context.path,"") ] )
239                     ;
240
241                 balanced_tokens 
242                     =    ch_p('(')                [ self.dispatch(&PD::pushPunctuation, "(") ]
243                       >> * token
244                       >> ch_p(')')                [ self.dispatch(&PD::pushPunctuation, ")") ]
245                     ;
246
247                 token
248                     =    simple_argument          [ self.dispatch(&PD::pushWord, 
249                                                                   boost::ref(self.context.str)) ]
250                     |    punctuation              [ self.dispatch(&PD::pushPunctuation,
251                                                                   boost::ref(self.context.str)) ]
252                     |    balanced_tokens
253                     ;
254
255                 punctuation             // Returns value in context.str
256                     =    punctuation_p            [ assign_a(self.context.str) ]
257                     ;
258
259                 word                    // Returns value in context.str
260                     =    lexeme_d[ + word_p ]     [ assign_a(self.context.str) ]
261                     ;
262
263                 hexbyte
264                     =    uint_parser<char, 16, 2, 2>()
265                                                   [ append_a(self.context.str) ]
266                     ;
267
268                 skip
269                     =    space_p | comment_p('#')
270                     ;
271
272                 BOOST_SPIRIT_DEBUG_TRACE_RULE(command,1);
273                 BOOST_SPIRIT_DEBUG_TRACE_RULE(path,1);
274                 BOOST_SPIRIT_DEBUG_TRACE_RULE(argument,1);
275                 BOOST_SPIRIT_DEBUG_TRACE_RULE(word,1);
276                 BOOST_SPIRIT_DEBUG_TRACE_RULE(string,1);
277                 BOOST_SPIRIT_DEBUG_TRACE_RULE(hexstring,1);
278                 BOOST_SPIRIT_DEBUG_TRACE_RULE(token,1);
279                 BOOST_SPIRIT_DEBUG_TRACE_RULE(punctuation,1);
280                 BOOST_SPIRIT_DEBUG_TRACE_RULE(hexbyte,1);
281                 BOOST_SPIRIT_DEBUG_TRACE_RULE(balanced_tokens,1);
282                 BOOST_SPIRIT_DEBUG_TRACE_RULE(simple_argument,1);
283                 BOOST_SPIRIT_DEBUG_TRACE_RULE(complex_argument,1);
284                 BOOST_SPIRIT_DEBUG_TRACE_RULE(builtin,1);
285
286                 start_parsers(
287                     commands,           // CommandParser
288                     skip                // SkipParser
289                 );
290
291             }
292         };
293     };
294
295 }}}
296
297 ///////////////////////////////ih.e////////////////////////////////////////
298 #endif
299
300 \f
301 // Local Variables:
302 // mode: c++
303 // fill-column: 100
304 // comment-column: 40
305 // c-file-style: "senf"
306 // indent-tabs-mode: nil
307 // ispell-local-dictionary: "american"
308 // compile-command: "scons -u test"
309 // End: