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