Utils/Console: Implement command node return value support
[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_ std::ostream & senf::console::operator<<(std::ostream & os, Token const & token)
116 {
117     static char const * tokenTypeName[] = {
118         "None",
119         "PathSeparator",
120         "ArgumentGroupOpen",
121         "ArgumentGroupClose",
122         "DirectoryGroupOpen",
123         "DirectoryGroupClose",
124         "CommandTerminator",
125         "OtherPunctuation",
126         "BasicString",
127         "HexString",
128         "Word" };
129     // The real table is:
130     //     static const int bitPosition[32] = {
131     //          0,  1, 28,  2, 29, 14, 24,  3, 30, 22, 20, 15, 25, 17,  4,  8, 
132     //         31, 27, 13, 23, 21, 19, 16,  7, 26, 12, 18,  6, 11,  5, 10,  9 };
133     // However, we have replaced all values >= sizeof(tokenTypeName) with 0
134     // and have added 1 to all the remaining values
135     static const int bitPosition[32] = {
136         1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 5, 9, 
137         0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 7, 0, 6, 0, 10 };
138     // We need to check token.type() against 0 explicitly since 0 and 1 will both be mapped to 0
139     os << tokenTypeName[ token.type() 
140                          ? bitPosition[(((token.type() & -token.type()) * 0x077CB531UL) >> 27) & 31]
141                          : 0 ]
142        << "('"
143        << token.value()
144        << "')";
145     return os;
146 }
147
148 ///////////////////////////////////////////////////////////////////////////
149 // senf::console::ParseCommandInfo
150
151 prefix_ std::ostream & senf::console::operator<<(std::ostream & stream,
152                                                  ParseCommandInfo const & info)
153 {
154     if (info.builtin() == ParseCommandInfo::NoBuiltin) {
155         ParseCommandInfo::TokensRange::const_iterator i (info.commandPath().begin());
156         ParseCommandInfo::TokensRange::const_iterator const i_end (info.commandPath().end());
157         if (i != i_end) {
158             for (;;) {
159                 stream << i->value();
160                 if ( ++i != i_end ) stream << "/";
161                 else                break;
162             }
163         }
164     }
165     else {
166         char const * builtins[] = { 0, "cd", "ls", "pushd", "popd", "exit", "help" };
167         stream << "builtin-" << builtins[info.builtin()];
168     }
169         
170     ParseCommandInfo::ArgumentsRange args (info.arguments());
171     for (ParseCommandInfo::argument_iterator i (args.begin()); i != args.end(); ++i) {
172         ParseCommandInfo::token_iterator j (i->begin());
173         stream << " [";
174         if ( j != i->end() ) {
175             for (;;) {
176                 stream << "'" << j->value() << "'";
177                 if ( ++j != i->end() ) stream << ' ';
178                 else                   break;
179             }
180         }
181         stream << "]";
182     }
183
184     return stream;
185 }
186
187 ///////////////////////////////////////////////////////////////////////////
188 // senf::console::ParseCommandInfo::ArgumentIterator
189
190 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::setRange()
191     const
192 {
193     if (b_->is(Token::ArgumentGroupOpen)) {
194         unsigned level (0);
195         e_ = b_;
196         for (;;) {
197             if (e_->is(Token::ArgumentGroupOpen))
198                 ++ level;
199             else if (e_->is(Token::ArgumentGroupClose)) {
200                 -- level;
201                 if (level == 0)
202                     break;
203             }
204             ++e_;
205         }
206     }
207     ++ e_;
208 }
209
210 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::decrement()
211 {
212     e_ = b_;
213     --b_;
214     if (b_->is(Token::ArgumentGroupClose)) {
215         unsigned level (0);
216         for (;;) {
217             if (b_->is(Token::ArgumentGroupClose))
218                 ++ level;
219             else if (b_->is(Token::ArgumentGroupOpen)) {
220                 -- level;
221                 if (level == 0)
222                     break;
223             }
224             --b_;
225         }
226     }
227 }
228
229 ///////////////////////////////////////////////////////////////////////////
230 // senf::console::CommandParser
231
232 #ifndef DOXYGEN
233
234 struct senf::console::CommandParser::Impl
235 {
236     typedef detail::CommandGrammar<detail::ParseDispatcher> Grammar;
237
238     detail::ParseDispatcher dispatcher;
239     Grammar::Context context;
240     Grammar grammar;
241
242     Impl() : dispatcher(), context(), grammar(dispatcher, context) {}
243 };
244
245 #endif
246
247 namespace {
248     
249     template <class Error>
250     void throwParserError(Error const & err) 
251     {
252         static char const * msg [] = { "end of statement 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 prefix_ void senf::console::CommandParser::parsePath(std::string const & path,
353                                                      ParseCommandInfo & info)
354 {
355     typedef boost::spirit::position_iterator<std::string::const_iterator> PositionIterator;
356     PositionIterator b (path.begin(), path.end(), std::string("<unknown>"));
357     PositionIterator e (path.end(), path.end(), std::string("<unknown>"));
358     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
359     boost::spirit::parse_info<PositionIterator> result;
360     try {
361         result = boost::spirit::parse( b, e, 
362                                        impl().grammar.use_parser<Impl::Grammar::PathParser>(),
363                                        impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
364     }
365     catch (boost::spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
366         throwParserError(ex);
367     }
368     if (! result.full) {
369         boost::spirit::file_position pos (result.stop.get_position());
370         throw ParserErrorException("path expected")
371             << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
372     }
373 }
374
375 struct senf::console::CommandParser::SetIncremental
376 {
377     SetIncremental(CommandParser & parser) : parser_ (parser) {
378         parser_.impl().grammar.incremental = true;
379     }
380
381     ~SetIncremental() {
382         parser_.impl().grammar.incremental = false;
383     }
384
385     CommandParser & parser_;
386 };
387
388 prefix_ std::string::size_type
389 senf::console::CommandParser::parseIncremental(std::string const & commands, Callback cb)
390 {
391     SetIncremental si (*this);
392     return std::distance( commands.begin(), 
393                           parseLoop(commands.begin(), commands.end(), "<unknown>", cb) );
394 }
395
396 ///////////////////////////////cc.e////////////////////////////////////////
397 #undef prefix_
398 //#include "Parse.mpp"
399
400 \f
401 // Local Variables:
402 // mode: c++
403 // fill-column: 100
404 // comment-column: 40
405 // c-file-style: "senf"
406 // indent-tabs-mode: nil
407 // ispell-local-dictionary: "american"
408 // compile-command: "scons -u test"
409 // End: