Utils: (membind) Fix weird casting error when binding base-class members
[senf.git] / Utils / Console / Parse.cc
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 non-inline non-template implementation */
25
26 #include "Parse.hh"
27 #include "Parse.ih"
28
29 // Custom includes
30 #include <cerrno>
31 #include <boost/iterator/transform_iterator.hpp>
32 #include <boost/spirit/iterator/file_iterator.hpp>
33 #include <boost/spirit/iterator/position_iterator.hpp>
34 #include "../../Utils/Exception.hh"
35 #include "../../Utils/senfassert.hh"
36
37 //#include "Parse.mpp"
38 #define prefix_
39 ///////////////////////////////cc.p////////////////////////////////////////
40
41 namespace senf {
42 namespace console {
43 namespace detail {
44
45 #ifndef DOXYGEN
46
47     struct ParseDispatcher
48     {
49         ParseCommandInfo * info_;
50
51         struct BindInfo {
52             BindInfo( ParseDispatcher & d, ParseCommandInfo & info)
53                 : dispatcher (d) { dispatcher.info_ = &info; }
54             ~BindInfo() { dispatcher.info_ = 0; }
55
56             ParseDispatcher & dispatcher;
57         };
58
59         void beginCommand(std::vector<Token> & command)
60             { info_->clear();
61               info_->command(command); }
62
63         void endCommand()
64             { }
65
66         void pushToken(Token const & token)
67             { info_->addToken(token); }
68
69         void builtin_cd(std::vector<Token> & path)
70             { info_->clear();
71               info_->builtin(ParseCommandInfo::BuiltinCD);
72               setBuiltinPathArg(path); }
73
74         void builtin_ls(std::vector<Token> & path)
75             { info_->clear();
76               info_->builtin(ParseCommandInfo::BuiltinLS);
77               setBuiltinPathArg(path); }
78
79         void pushDirectory()
80             { // Do NOT call clear since pushDirectory is set in ADDITION
81               // to an ordinary command (which may be only a directory name)
82               info_->builtin(ParseCommandInfo::BuiltinPUSHD); }
83
84         void popDirectory()
85             { info_->clear();
86               info_->builtin(ParseCommandInfo::BuiltinPOPD); }
87         
88         void builtin_exit()
89             { info_->clear();
90               info_->builtin(ParseCommandInfo::BuiltinEXIT); }
91
92         void builtin_help(std::vector<Token> & path)
93             { info_->clear();
94               info_->builtin(ParseCommandInfo::BuiltinHELP);
95               setBuiltinPathArg(path); }
96
97         void setBuiltinPathArg(std::vector<Token> & path)
98             {
99                 info_->command(path);
100 //                 pushToken(ArgumentGroupOpenToken());
101 //                 for (std::vector<Token>::const_iterator i (path.begin());
102 //                      i != path.end(); ++i)
103 //                     pushToken(*i);
104 //                 pushToken(ArgumentGroupCloseToken());
105             }
106     };
107
108 #endif
109
110 }}}
111
112 ///////////////////////////////////////////////////////////////////////////
113 // senf::console::Token
114
115 prefix_ senf::console::Token::Token(TokenType type, std::string token,
116                                     detail::FilePositionWithIndex const & pos)
117     : type_(type), token_ (token), line_ (pos.line), column_ (pos.column), index_ (pos.index)
118 {}
119
120
121 prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Token const & token)
122 {
123     static char const * tokenTypeName[] = {
124         "None",
125         "PathSeparator",
126         "ArgumentGroupOpen",
127         "ArgumentGroupClose",
128         "DirectoryGroupOpen",
129         "DirectoryGroupClose",
130         "CommandTerminator",
131         "OtherPunctuation",
132         "BasicString",
133         "HexString",
134         "Word" };
135     // The real table is:
136     //     static const int bitPosition[32] = {
137     //          0,  1, 28,  2, 29, 14, 24,  3, 30, 22, 20, 15, 25, 17,  4,  8, 
138     //         31, 27, 13, 23, 21, 19, 16,  7, 26, 12, 18,  6, 11,  5, 10,  9 };
139     // However, we have replaced all values >= sizeof(tokenTypeName) with 0
140     // and have added 1 to all the remaining values
141     static const int bitPosition[32] = {
142         1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 5, 9, 
143         0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 7, 0, 6, 0, 10 };
144     // We need to check token.type() against 0 explicitly since 0 and 1 will both be mapped to 0
145     os << tokenTypeName[ token.type() 
146                          ? bitPosition[(((token.type() & -token.type()) * 0x077CB531UL) >> 27) & 31]
147                          : 0 ]
148        << "('"
149        << token.value()
150        << "')";
151     return os;
152 }
153
154 ///////////////////////////////////////////////////////////////////////////
155 // senf::console::ParseCommandInfo
156
157 prefix_ std::ostream & senf::console::operator<<(std::ostream & stream,
158                                                  ParseCommandInfo const & info)
159 {
160     if (info.builtin() == ParseCommandInfo::NoBuiltin) {
161         ParseCommandInfo::TokensRange::const_iterator i (info.commandPath().begin());
162         ParseCommandInfo::TokensRange::const_iterator const i_end (info.commandPath().end());
163         if (i != i_end) {
164             for (;;) {
165                 stream << i->value();
166                 if ( ++i != i_end ) stream << "/";
167                 else                break;
168             }
169         }
170     }
171     else {
172         char const * builtins[] = { 0, "cd", "ls", "pushd", "popd", "exit", "help" };
173         stream << "builtin-" << builtins[info.builtin()];
174     }
175         
176     ParseCommandInfo::ArgumentsRange args (info.arguments());
177     for (ParseCommandInfo::argument_iterator i (args.begin()); i != args.end(); ++i) {
178         ParseCommandInfo::token_iterator j (i->begin());
179         stream << " [";
180         if ( j != i->end() ) {
181             for (;;) {
182                 stream << "'" << j->value() << "'";
183                 if ( ++j != i->end() ) stream << ' ';
184                 else                   break;
185             }
186         }
187         stream << "]";
188     }
189
190     return stream;
191 }
192
193 ///////////////////////////////////////////////////////////////////////////
194 // senf::console::ParseCommandInfo::ArgumentIterator
195
196 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::setRange()
197     const
198 {
199     if (b_->is(Token::ArgumentGroupOpen)) {
200         unsigned level (0);
201         e_ = b_;
202         for (;;) {
203             if (e_->is(Token::ArgumentGroupOpen))
204                 ++ level;
205             else if (e_->is(Token::ArgumentGroupClose)) {
206                 -- level;
207                 if (level == 0)
208                     break;
209             }
210             ++e_;
211         }
212     }
213     ++ e_;
214 }
215
216 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::decrement()
217 {
218     e_ = b_;
219     --b_;
220     if (b_->is(Token::ArgumentGroupClose)) {
221         unsigned level (0);
222         for (;;) {
223             if (b_->is(Token::ArgumentGroupClose))
224                 ++ level;
225             else if (b_->is(Token::ArgumentGroupOpen)) {
226                 -- level;
227                 if (level == 0)
228                     break;
229             }
230             --b_;
231         }
232     }
233 }
234
235 ///////////////////////////////////////////////////////////////////////////
236 // senf::console::CommandParser
237
238 #ifndef DOXYGEN
239
240 struct senf::console::CommandParser::Impl
241 {
242     typedef detail::CommandGrammar<detail::ParseDispatcher> Grammar;
243
244     detail::ParseDispatcher dispatcher;
245     Grammar::Context context;
246     Grammar grammar;
247
248     Impl() : dispatcher(), context(), grammar(dispatcher, context) {}
249 };
250
251 #endif
252
253 namespace {
254     
255     template <class Error>
256     void throwParserError(Error const & err) 
257     {
258         static char const * msg [] = { "end of statement expected",
259                                        "path expected",
260                                        "')' expected",
261                                        "'\"' expected" };
262         senf::console::detail::FilePositionWithIndex pos (err.where.get_position());
263         throw senf::console::CommandParser::ParserErrorException(msg[err.descriptor])
264             << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
265     }
266
267 }
268
269 namespace boost { 
270 namespace spirit {
271
272     template <>
273     struct position_policy<senf::console::detail::FilePositionWithIndex>
274         : public position_policy<file_position>
275     {
276         typedef position_policy<file_position> Base;
277
278         void next_line(senf::console::detail::FilePositionWithIndex & pos)
279             {
280                 Base::next_line(pos);
281                 pos.index ++;
282             }
283
284         void next_char(senf::console::detail::FilePositionWithIndex & pos)
285             {
286                 Base::next_char(pos);
287                 pos.index ++;
288             }
289
290         void tabulation(senf::console::detail::FilePositionWithIndex & pos)
291             {
292                 Base::tabulation(pos);
293                 pos.index ++;
294             }
295     };
296
297 }}
298
299 prefix_ senf::console::CommandParser::CommandParser()
300     : impl_ (new Impl())
301 {}
302
303 prefix_ senf::console::CommandParser::~CommandParser()
304 {}
305
306 // This template member is placed here, since it is ONLY called from the implementation.  Otherwise,
307 // we would need to expose the Impl member to the public, which we don't want to do.
308
309 template <class Iterator>
310 prefix_ Iterator senf::console::CommandParser::parseLoop(Iterator npb, Iterator npe, 
311                                                          std::string const & source, Callback cb)
312 {
313     typedef boost::spirit::position_iterator<
314         Iterator, detail::FilePositionWithIndex> PositionIterator;
315     PositionIterator b (npb, npe, source);
316     PositionIterator e (npe, npe, source);
317     ParseCommandInfo info;
318     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
319     boost::spirit::parse_info<PositionIterator> result;
320
321     for(;;) {
322         result = boost::spirit::parse(
323             b, e, * impl().grammar.use_parser<Impl::Grammar::SkipParser>());
324         b = result.stop;
325         if (b == e) 
326             return e.base();
327         info.clear();
328         try {
329             result = boost::spirit::parse(b, e,
330                                           impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
331                                           impl().grammar.use_parser<Impl::Grammar::SkipParser>());
332         }
333         catch (boost::spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
334             if (impl().grammar.incremental && ex.where == e)
335                 return b.base();
336             else
337                 throwParserError(ex);
338         }
339         // Otherwise the error handling in the parser is broken
340         SENF_ASSERT( result.hit );
341         if (! info.empty()) 
342             try {
343                 cb(info);
344             }
345             catch (senf::ExceptionMixin & ex) {
346                 detail::FilePositionWithIndex pos (result.stop.get_position());
347                 ex << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
348                 throw;
349             }
350         b = result.stop;
351     }
352 }
353
354 prefix_ void senf::console::CommandParser::parse(std::string const & command, Callback cb)
355 {
356     parseLoop(command.begin(), command.end(), "<unknown>", cb);
357 }
358
359 prefix_ void senf::console::CommandParser::parseFile(std::string const & filename, Callback cb)
360 {
361     boost::spirit::file_iterator<> i (filename);
362     if (!i) throw SystemException(ENOENT SENF_EXC_DEBUGINFO);
363     boost::spirit::file_iterator<> const i_end (i.make_end());
364     parseLoop(i, i_end, filename, cb);
365 }
366
367 prefix_ void senf::console::CommandParser::parseArguments(std::string const & arguments,
368                                                           ParseCommandInfo & info)
369 {
370     typedef boost::spirit::position_iterator<
371         std::string::const_iterator, detail::FilePositionWithIndex> PositionIterator;
372     PositionIterator b (arguments.begin(), arguments.end(), std::string("<unknown>"));
373     PositionIterator e (arguments.end(), arguments.end(), std::string("<unknown>"));
374     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
375     boost::spirit::parse_info<PositionIterator> result;
376     try {
377         result = boost::spirit::parse( b, e, 
378                                        impl().grammar.use_parser<Impl::Grammar::ArgumentsParser>(),
379                                        impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
380     }
381     catch (boost::spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
382         throwParserError(ex);
383     }
384     if (! result.full) {
385         detail::FilePositionWithIndex pos (result.stop.get_position());
386         throw ParserErrorException("argument expected")
387             << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
388     }
389 }
390
391 prefix_ void senf::console::CommandParser::parsePath(std::string const & path,
392                                                      ParseCommandInfo & info)
393 {
394     typedef boost::spirit::position_iterator<
395         std::string::const_iterator, detail::FilePositionWithIndex> PositionIterator;
396     PositionIterator b (path.begin(), path.end(), std::string("<unknown>"));
397     PositionIterator e (path.end(), path.end(), std::string("<unknown>"));
398     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
399     boost::spirit::parse_info<PositionIterator> result;
400     try {
401         result = boost::spirit::parse( b, e, 
402                                        impl().grammar.use_parser<Impl::Grammar::PathParser>(),
403                                        impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
404     }
405     catch (boost::spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
406         throwParserError(ex);
407     }
408     if (! result.full) {
409         detail::FilePositionWithIndex pos (result.stop.get_position());
410         throw ParserErrorException("path expected")
411             << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
412     }
413 }
414
415 struct senf::console::CommandParser::SetIncremental
416 {
417     SetIncremental(CommandParser & parser) : parser_ (parser) {
418         parser_.impl().grammar.incremental = true;
419     }
420
421     ~SetIncremental() {
422         parser_.impl().grammar.incremental = false;
423     }
424
425     CommandParser & parser_;
426 };
427
428 prefix_ std::string::size_type
429 senf::console::CommandParser::parseIncremental(std::string const & commands, Callback cb)
430 {
431     SetIncremental si (*this);
432     return std::distance( commands.begin(), 
433                           parseLoop(commands.begin(), commands.end(), "<unknown>", cb) );
434 }
435
436 ///////////////////////////////////////////////////////////////////////////
437 // Character sets
438
439 prefix_ bool senf::console::CommandParser::isSpecialChar(char ch)
440 {
441     return Impl::Grammar::special_p.test(ch);
442 }
443
444 prefix_ bool senf::console::CommandParser::isPunctuationChar(char ch)
445 {
446     return Impl::Grammar::punctuation_p.test(ch);
447 }
448
449 prefix_ bool senf::console::CommandParser::isSpaceChar(char ch)
450 {
451     return Impl::Grammar::space_p.test(ch);
452 }
453
454 prefix_ bool senf::console::CommandParser::isInvalidChar(char ch)
455 {
456     return Impl::Grammar::invalid_p.test(ch);
457 }
458
459 prefix_ bool senf::console::CommandParser::isWordChar(char ch)
460 {
461     return Impl::Grammar::word_p.test(ch);
462 }
463
464 /////////////////////////////cc.e////////////////////////////////////////
465 #undef prefix_
466 //#include "Parse.mpp"
467
468 \f
469 // Local Variables:
470 // mode: c++
471 // fill-column: 100
472 // comment-column: 40
473 // c-file-style: "senf"
474 // indent-tabs-mode: nil
475 // ispell-local-dictionary: "american"
476 // compile-command: "scons -u test"
477 // End: