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