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