replaced some tabs with spaces
[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(std::vector<Token> & path)
80             { info_->clear();
81               info_->builtin(ParseCommandInfo::BuiltinPUSHD);
82               setBuiltinPathArg(path); }
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                 pushToken(ArgumentGroupOpenToken());
100                 for (std::vector<Token>::const_iterator i (path.begin());
101                      i != path.end(); ++i)
102                     pushToken(*i);
103                 pushToken(ArgumentGroupCloseToken());
104             }
105     };
106
107 #endif
108
109 }}}
110
111 ///////////////////////////////////////////////////////////////////////////
112 // senf::console::Token
113
114 prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Token const & token)
115 {
116     static char const * tokenTypeName[] = {
117         "None",
118         "PathSeparator",
119         "ArgumentGroupOpen",
120         "ArgumentGroupClose",
121         "DirectoryGroupOpen",
122         "DirectoryGroupClose",
123         "CommandTerminator",
124         "OtherPunctuation",
125         "BasicString",
126         "HexString",
127         "Word" };
128     // The real table is:
129     //     static const int bitPosition[32] = {
130     //          0,  1, 28,  2, 29, 14, 24,  3, 30, 22, 20, 15, 25, 17,  4,  8, 
131     //         31, 27, 13, 23, 21, 19, 16,  7, 26, 12, 18,  6, 11,  5, 10,  9 };
132     // However, we have replaced all values >= sizeof(tokenTypeName) with 0
133     // and have added 1 to all the remaining values
134     static const int bitPosition[32] = {
135         1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 5, 9, 
136         0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 7, 0, 6, 0, 10 };
137     // We need to check token.type() against 0 explicitly since 0 and 1 will both be mapped to 0
138     os << tokenTypeName[ token.type() 
139                          ? bitPosition[(((token.type() & -token.type()) * 0x077CB531UL) >> 27) & 31]
140                          : 0 ]
141        << "('"
142        << token.value()
143        << "')";
144     return os;
145 }
146
147 ///////////////////////////////////////////////////////////////////////////
148 // senf::console::ParseCommandInfo
149
150 prefix_ std::ostream & senf::console::operator<<(std::ostream & stream,
151                                                  ParseCommandInfo const & info)
152 {
153     if (info.builtin() == ParseCommandInfo::NoBuiltin) {
154         ParseCommandInfo::TokensRange::const_iterator i (info.commandPath().begin());
155         ParseCommandInfo::TokensRange::const_iterator const i_end (info.commandPath().end());
156         if (i != i_end) {
157             for (;;) {
158                 stream << i->value();
159                 if ( ++i != i_end ) stream << "/";
160                 else                break;
161             }
162         }
163     }
164     else {
165         char const * builtins[] = { 0, "cd", "ls", "pushd", "popd", "exit", "help" };
166         stream << "builtin-" << builtins[info.builtin()];
167     }
168         
169     ParseCommandInfo::ArgumentsRange args (info.arguments());
170     for (ParseCommandInfo::argument_iterator i (args.begin()); i != args.end(); ++i) {
171         ParseCommandInfo::token_iterator j (i->begin());
172         stream << " [";
173         if ( j != i->end() ) {
174             for (;;) {
175                 stream << "'" << j->value() << "'";
176                 if ( ++j != i->end() ) stream << ' ';
177                 else                   break;
178             }
179         }
180         stream << "]";
181     }
182
183     return stream;
184 }
185
186 ///////////////////////////////////////////////////////////////////////////
187 // senf::console::ParseCommandInfo::ArgumentIterator
188
189 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::setRange()
190     const
191 {
192     if (b_->is(Token::ArgumentGroupOpen)) {
193         unsigned level (0);
194         e_ = b_;
195         for (;;) {
196             if (e_->is(Token::ArgumentGroupOpen))
197                 ++ level;
198             else if (e_->is(Token::ArgumentGroupClose)) {
199                 -- level;
200                 if (level == 0)
201                     break;
202             }
203             ++e_;
204         }
205     }
206     ++ e_;
207 }
208
209 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::decrement()
210 {
211     e_ = b_;
212     --b_;
213     if (b_->is(Token::ArgumentGroupClose)) {
214         unsigned level (0);
215         for (;;) {
216             if (b_->is(Token::ArgumentGroupClose))
217                 ++ level;
218             else if (b_->is(Token::ArgumentGroupOpen)) {
219                 -- level;
220                 if (level == 0)
221                     break;
222             }
223             --b_;
224         }
225     }
226 }
227
228 ///////////////////////////////////////////////////////////////////////////
229 // senf::console::CommandParser
230
231 #ifndef DOXYGEN
232
233 struct senf::console::CommandParser::Impl
234 {
235     typedef detail::CommandGrammar<detail::ParseDispatcher> Grammar;
236
237     detail::ParseDispatcher dispatcher;
238     Grammar::Context context;
239     Grammar grammar;
240
241     Impl() : dispatcher(), context(), grammar(dispatcher, context) {}
242 };
243
244 #endif
245
246 namespace {
247     
248     template <class Error>
249     void throwParserError(Error const & err) 
250     {
251         static char const * msg [] = { "end of statement expected",
252                                        "'{' or arguments expected",
253                                        "path expected",
254                                        "')' expected",
255                                        "'\"' expected" };
256         boost::spirit::file_position pos (err.where.get_position());
257         throw senf::console::CommandParser::ParserErrorException(msg[err.descriptor])
258             << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
259     }
260 }
261
262 prefix_ senf::console::CommandParser::CommandParser()
263     : impl_ (new Impl())
264 {}
265
266 prefix_ senf::console::CommandParser::~CommandParser()
267 {}
268
269 // This template member is placed here, since it is ONLY called from the implementation.  Otherwise,
270 // we would need to expose the Impl member to the public, which we don't want to do.
271
272 template <class Iterator>
273 prefix_ Iterator senf::console::CommandParser::parseLoop(Iterator npb, Iterator npe, 
274                                                          std::string const & source, Callback cb)
275 {
276     typedef boost::spirit::position_iterator<Iterator> PositionIterator;
277     PositionIterator b (npb, npe, source);
278     PositionIterator e (npe, npe, source);
279     ParseCommandInfo info;
280     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
281     boost::spirit::parse_info<PositionIterator> result;
282
283     for(;;) {
284         result = boost::spirit::parse(
285             b, e, * impl().grammar.use_parser<Impl::Grammar::SkipParser>());
286         b = result.stop;
287         if (b == e) 
288             return e.base();
289         info.clear();
290         try {
291             result = boost::spirit::parse(b, e,
292                                           impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
293                                           impl().grammar.use_parser<Impl::Grammar::SkipParser>());
294         }
295         catch (boost::spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
296             if (impl().grammar.incremental && ex.where == e)
297                 return b.base();
298             else
299                 throwParserError(ex);
300         }
301         // Otherwise the error handling in the parser is broken
302         SENF_ASSERT( result.hit );
303         if (! info.empty()) 
304             try {
305                 cb(info);
306             }
307             catch (senf::ExceptionMixin & ex) {
308                 boost::spirit::file_position pos (result.stop.get_position());
309                 ex << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
310                 throw;
311             }
312         b = result.stop;
313     }
314 }
315
316 prefix_ void senf::console::CommandParser::parse(std::string const & command, Callback cb)
317 {
318     parseLoop(command.begin(), command.end(), "<unknown>", cb);
319 }
320
321 prefix_ void senf::console::CommandParser::parseFile(std::string const & filename, Callback cb)
322 {
323     boost::spirit::file_iterator<> i (filename);
324     if (!i) throw SystemException(ENOENT SENF_EXC_DEBUGINFO);
325     boost::spirit::file_iterator<> const i_end (i.make_end());
326     parseLoop(i, i_end, filename, cb);
327 }
328
329 prefix_ void senf::console::CommandParser::parseArguments(std::string const & arguments,
330                                                           ParseCommandInfo & info)
331 {
332     typedef boost::spirit::position_iterator<std::string::const_iterator> PositionIterator;
333     PositionIterator b (arguments.begin(), arguments.end(), std::string("<unknown>"));
334     PositionIterator e (arguments.end(), arguments.end(), std::string("<unknown>"));
335     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
336     boost::spirit::parse_info<PositionIterator> result;
337     try {
338         result = boost::spirit::parse( b, e, 
339                                        impl().grammar.use_parser<Impl::Grammar::ArgumentsParser>(),
340                                        impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
341     }
342     catch (boost::spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
343         throwParserError(ex);
344     }
345     if (! result.full) {
346         boost::spirit::file_position pos (result.stop.get_position());
347         throw ParserErrorException("argument expected")
348             << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
349     }
350 }
351
352 struct senf::console::CommandParser::SetIncremental
353 {
354     SetIncremental(CommandParser & parser) : parser_ (parser) {
355         parser_.impl().grammar.incremental = true;
356     }
357
358     ~SetIncremental() {
359         parser_.impl().grammar.incremental = false;
360     }
361
362     CommandParser & parser_;
363 };
364
365 prefix_ std::string::size_type
366 senf::console::CommandParser::parseIncremental(std::string const & commands, Callback cb)
367 {
368     SetIncremental si (*this);
369     return std::distance( commands.begin(), 
370                           parseLoop(commands.begin(), commands.end(), "<unknown>", cb) );
371 }
372
373 ///////////////////////////////cc.e////////////////////////////////////////
374 #undef prefix_
375 //#include "Parse.mpp"
376
377 \f
378 // Local Variables:
379 // mode: c++
380 // fill-column: 100
381 // comment-column: 40
382 // c-file-style: "senf"
383 // indent-tabs-mode: nil
384 // ispell-local-dictionary: "american"
385 // compile-command: "scons -u test"
386 // End: