e36c7c52e2bab5fa79a9e0e77497e4eceaf11cdf
[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     class position_policy<senf::console::detail::FilePositionWithIndex>
301         : public position_policy<file_position>
302     {
303     public:
304         typedef position_policy<file_position> Base;
305
306         void next_line(senf::console::detail::FilePositionWithIndex & pos)
307             {
308                 Base::next_line(pos);
309                 pos.index ++;
310             }
311
312         void next_char(senf::console::detail::FilePositionWithIndex & pos)
313             {
314                 Base::next_char(pos);
315                 pos.index ++;
316             }
317
318         void tabulation(senf::console::detail::FilePositionWithIndex & pos)
319             {
320                 Base::tabulation(pos);
321                 pos.index ++;
322             }
323     };
324
325 #if HAVE_BOOST_SPIRIT_INCLUDE_CLASSIC_HPP
326 }
327 #endif
328 }}
329
330 prefix_ senf::console::CommandParser::CommandParser()
331     : impl_ (new Impl())
332 {}
333
334 prefix_ senf::console::CommandParser::~CommandParser()
335 {}
336
337 // This template member is placed here, since it is ONLY called from the implementation.  Otherwise,
338 // we would need to expose the Impl member to the public, which we don't want to do.
339
340 template <class Iterator>
341 prefix_ Iterator senf::console::CommandParser::parseLoop(Iterator npb, Iterator npe,
342                                                          std::string const & source, Callback cb)
343 {
344     typedef detail::boost_spirit::position_iterator<
345         Iterator, detail::FilePositionWithIndex> PositionIterator;
346     PositionIterator b (npb, npe, source);
347     PositionIterator e (npe, npe, source);
348     ParseCommandInfo info;
349     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
350     detail::boost_spirit::parse_info<PositionIterator> result;
351
352     for (;;) {
353         result = detail::boost_spirit::parse(
354             b, e, * impl().grammar.use_parser<Impl::Grammar::SkipParser>());
355         b = result.stop;
356         if (b == e)
357             return e.base();
358         info.clear();
359         try {
360             result = detail::boost_spirit::parse(b, e,
361                                           impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
362                                           impl().grammar.use_parser<Impl::Grammar::SkipParser>());
363         }
364         catch (detail::boost_spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
365             if (impl().grammar.incremental && ex.where == e)
366                 return b.base();
367             else
368                 throwParserError(ex);
369         }
370         // Otherwise the error handling in the parser is broken
371         SENF_ASSERT( result.hit, "Internal parser failure (error handling broken?)" );
372         if (! info.empty())
373             try {
374                 cb(info);
375             }
376             catch (senf::ExceptionMixin & ex) {
377                 detail::FilePositionWithIndex pos (result.stop.get_position());
378                 ex << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
379                 throw;
380             }
381         b = result.stop;
382     }
383 }
384
385 prefix_ void senf::console::CommandParser::parse(std::string const & command, Callback cb)
386 {
387     parseLoop(command.begin(), command.end(), "<unknown>", cb);
388 }
389
390 prefix_ void senf::console::CommandParser::parseFile(std::string const & filename, Callback cb)
391 {
392     // file_iterator sets errno to EINVAL and returns error when file size is 0
393     // so we check the file size before
394     struct stat statBuf;
395     if (stat( filename.c_str(), &statBuf) != 0)
396         throw SystemException(filename, errno SENF_EXC_DEBUGINFO);
397     if (statBuf.st_size == 0) return;
398     detail::boost_spirit::file_iterator<> i (filename);
399     if (!i) {
400         if (errno == 0)
401             // hmm.. errno==0 but the file_iterator is false; something is wrong but we
402             // do not know what exactly, so we throw a SystemeException with EINVAL
403             throw SystemException(filename, EINVAL SENF_EXC_DEBUGINFO);
404         else
405             throw SystemException(filename, errno SENF_EXC_DEBUGINFO);
406     }
407     detail::boost_spirit::file_iterator<> const i_end (i.make_end());
408     parseLoop(i, i_end, filename, cb);
409 }
410
411 prefix_ void senf::console::CommandParser::parseArguments(std::string const & arguments,
412                                                           ParseCommandInfo & info)
413 {
414     typedef detail::boost_spirit::position_iterator<
415         std::string::const_iterator, detail::FilePositionWithIndex> PositionIterator;
416     PositionIterator b (arguments.begin(), arguments.end(), std::string("<unknown>"));
417     PositionIterator e (arguments.end(), arguments.end(), std::string("<unknown>"));
418     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
419     detail::boost_spirit::parse_info<PositionIterator> result;
420     try {
421         result = detail::boost_spirit::parse( b, e,
422                                        impl().grammar.use_parser<Impl::Grammar::ArgumentsParser>(),
423                                        impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
424     }
425     catch (detail::boost_spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
426         throwParserError(ex);
427     }
428     if (! result.full) {
429         detail::FilePositionWithIndex pos (result.stop.get_position());
430         throw ParserErrorException("argument expected")
431             << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
432     }
433 }
434
435 prefix_ void senf::console::CommandParser::parsePath(std::string const & path,
436                                                      ParseCommandInfo & info)
437 {
438     typedef detail::boost_spirit::position_iterator<
439         std::string::const_iterator, detail::FilePositionWithIndex> PositionIterator;
440     PositionIterator b (path.begin(), path.end(), std::string("<unknown>"));
441     PositionIterator e (path.end(), path.end(), std::string("<unknown>"));
442     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
443     detail::boost_spirit::parse_info<PositionIterator> result;
444     try {
445         result = detail::boost_spirit::parse( b, e,
446                                        impl().grammar.use_parser<Impl::Grammar::PathParser>(),
447                                        impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
448     }
449     catch (detail::boost_spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
450         throwParserError(ex);
451     }
452     if (! result.full) {
453         detail::FilePositionWithIndex pos (result.stop.get_position());
454         throw ParserErrorException("path expected")
455             << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
456     }
457 }
458
459 struct senf::console::CommandParser::SetIncremental
460 {
461     SetIncremental(CommandParser & parser) : parser_ (parser) {
462         parser_.impl().grammar.incremental = true;
463     }
464
465     ~SetIncremental() {
466         parser_.impl().grammar.incremental = false;
467     }
468
469     CommandParser & parser_;
470 };
471
472 prefix_ std::string::size_type
473 senf::console::CommandParser::parseIncremental(std::string const & commands, Callback cb)
474 {
475     SetIncremental si (*this);
476     return std::distance( commands.begin(),
477                           parseLoop(commands.begin(), commands.end(), "<unknown>", cb) );
478 }
479
480 //-/////////////////////////////////////////////////////////////////////////////////////////////////
481 // Character sets
482
483 prefix_ bool senf::console::CommandParser::isSpecialChar(char ch)
484 {
485     return Impl::Grammar::special_p().test(ch);
486 }
487
488 prefix_ bool senf::console::CommandParser::isPunctuationChar(char ch)
489 {
490     return Impl::Grammar::punctuation_p().test(ch);
491 }
492
493 prefix_ bool senf::console::CommandParser::isSpaceChar(char ch)
494 {
495     return Impl::Grammar::space_p().test(ch);
496 }
497
498 prefix_ bool senf::console::CommandParser::isInvalidChar(char ch)
499 {
500     return Impl::Grammar::invalid_p().test(ch);
501 }
502
503 prefix_ bool senf::console::CommandParser::isWordChar(char ch)
504 {
505     return Impl::Grammar::word_p().test(ch);
506 }
507
508 //-/////////////////////////////////////////////////////////////////////////////////////////////////
509 #undef prefix_
510 //#include "Parse.mpp"
511
512 \f
513 // Local Variables:
514 // mode: c++
515 // fill-column: 100
516 // comment-column: 40
517 // c-file-style: "senf"
518 // indent-tabs-mode: nil
519 // ispell-local-dictionary: "american"
520 // compile-command: "scons -u test"
521 // End: