4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 // Stefan Bund <g0dil@berlios.de>
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.
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.
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.
24 \brief Parse non-inline non-template implementation */
32 #include <boost/iterator/transform_iterator.hpp>
33 #include <boost/spirit/iterator/file_iterator.hpp>
34 #include <boost/spirit/iterator/position_iterator.hpp>
35 #include <senf/Utils/Exception.hh>
36 #include <senf/Utils/senfassert.hh>
38 //#include "Parse.mpp"
40 ///////////////////////////////cc.p////////////////////////////////////////
48 struct ParseDispatcher
50 ParseCommandInfo * info_;
53 BindInfo( ParseDispatcher & d, ParseCommandInfo & info)
54 : dispatcher (d) { dispatcher.info_ = &info; }
55 ~BindInfo() { dispatcher.info_ = 0; }
57 ParseDispatcher & dispatcher;
60 void beginCommand(std::vector<Token> & command)
62 info_->command(command); }
67 void pushToken(Token const & token)
68 { info_->addToken(token); }
70 void builtin_cd(std::vector<Token> & path)
72 info_->builtin(ParseCommandInfo::BuiltinCD);
73 setBuiltinPathArg(path); }
75 void builtin_ls(std::vector<Token> & path)
77 info_->builtin(ParseCommandInfo::BuiltinLS);
78 setBuiltinPathArg(path); }
80 void builtin_ll(std::vector<Token> & path)
82 info_->builtin(ParseCommandInfo::BuiltinLL);
83 setBuiltinPathArg(path); }
85 void builtin_lr(std::vector<Token> & path)
87 info_->builtin(ParseCommandInfo::BuiltinLR);
88 setBuiltinPathArg(path); }
91 { // Do NOT call clear since pushDirectory is set in ADDITION
92 // to an ordinary command (which may be only a directory name)
93 info_->builtin(ParseCommandInfo::BuiltinPUSHD); }
97 info_->builtin(ParseCommandInfo::BuiltinPOPD); }
101 info_->builtin(ParseCommandInfo::BuiltinEXIT); }
103 void builtin_help(std::vector<Token> & path)
105 info_->builtin(ParseCommandInfo::BuiltinHELP);
106 setBuiltinPathArg(path); }
108 void setBuiltinPathArg(std::vector<Token> & path)
110 info_->command(path);
111 // pushToken(ArgumentGroupOpenToken());
112 // for (std::vector<Token>::const_iterator i (path.begin());
113 // i != path.end(); ++i)
115 // pushToken(ArgumentGroupCloseToken());
123 ///////////////////////////////////////////////////////////////////////////
124 // senf::console::Token
126 prefix_ senf::console::Token::Token(TokenType type, std::string token,
127 detail::FilePositionWithIndex const & pos)
128 : type_(type), token_ (token), line_ (pos.line), column_ (pos.column), index_ (pos.index)
132 prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Token const & token)
134 static char const * tokenTypeName[] = {
138 "ArgumentGroupClose",
139 "DirectoryGroupOpen",
140 "DirectoryGroupClose",
146 // The real table is:
147 // static const int bitPosition[32] = {
148 // 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
149 // 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 };
150 // However, we have replaced all values >= sizeof(tokenTypeName) with 0
151 // and have added 1 to all the remaining values
152 static const int bitPosition[32] = {
153 1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 5, 9,
154 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 7, 0, 6, 0, 10 };
155 // We need to check token.type() against 0 explicitly since 0 and 1 will both be mapped to 0
156 os << tokenTypeName[ token.type()
157 ? bitPosition[(((token.type() & -token.type()) * 0x077CB531UL) >> 27) & 31]
165 ///////////////////////////////////////////////////////////////////////////
166 // senf::console::ParseCommandInfo
168 prefix_ std::ostream & senf::console::operator<<(std::ostream & stream,
169 ParseCommandInfo const & info)
171 if (info.builtin() == ParseCommandInfo::NoBuiltin) {
172 ParseCommandInfo::TokensRange::const_iterator i (info.commandPath().begin());
173 ParseCommandInfo::TokensRange::const_iterator const i_end (info.commandPath().end());
176 stream << i->value();
177 if ( ++i != i_end ) stream << "/";
183 char const * builtins[] = { 0, "cd", "ls", "lr", "pushd", "popd", "exit", "help" };
184 stream << "builtin-" << builtins[info.builtin()];
187 ParseCommandInfo::ArgumentsRange args (info.arguments());
188 for (ParseCommandInfo::argument_iterator i (args.begin()); i != args.end(); ++i) {
189 ParseCommandInfo::token_iterator j (i->begin());
191 if ( j != i->end() ) {
193 stream << "'" << j->value() << "'";
194 if ( ++j != i->end() ) stream << ' ';
204 ///////////////////////////////////////////////////////////////////////////
205 // senf::console::ParseCommandInfo::ArgumentIterator
207 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::setRange()
210 if (b_->is(Token::ArgumentGroupOpen)) {
214 if (e_->is(Token::ArgumentGroupOpen))
216 else if (e_->is(Token::ArgumentGroupClose)) {
227 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::decrement()
231 if (b_->is(Token::ArgumentGroupClose)) {
234 if (b_->is(Token::ArgumentGroupClose))
236 else if (b_->is(Token::ArgumentGroupOpen)) {
246 ///////////////////////////////////////////////////////////////////////////
247 // senf::console::CommandParser
251 struct senf::console::CommandParser::Impl
253 typedef detail::CommandGrammar<detail::ParseDispatcher> Grammar;
255 detail::ParseDispatcher dispatcher;
256 Grammar::Context context;
259 Impl() : dispatcher(), context(), grammar(dispatcher, context) {}
266 template <class Error>
267 void throwParserError(Error const & err)
269 static char const * msg [] = { "end of statement expected",
273 senf::console::detail::FilePositionWithIndex pos (err.where.get_position());
274 throw senf::console::CommandParser::ParserErrorException(msg[err.descriptor])
275 << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
284 struct position_policy<senf::console::detail::FilePositionWithIndex>
285 : public position_policy<file_position>
287 typedef position_policy<file_position> Base;
289 void next_line(senf::console::detail::FilePositionWithIndex & pos)
291 Base::next_line(pos);
295 void next_char(senf::console::detail::FilePositionWithIndex & pos)
297 Base::next_char(pos);
301 void tabulation(senf::console::detail::FilePositionWithIndex & pos)
303 Base::tabulation(pos);
310 prefix_ senf::console::CommandParser::CommandParser()
314 prefix_ senf::console::CommandParser::~CommandParser()
317 // This template member is placed here, since it is ONLY called from the implementation. Otherwise,
318 // we would need to expose the Impl member to the public, which we don't want to do.
320 template <class Iterator>
321 prefix_ Iterator senf::console::CommandParser::parseLoop(Iterator npb, Iterator npe,
322 std::string const & source, Callback cb)
324 typedef boost::spirit::position_iterator<
325 Iterator, detail::FilePositionWithIndex> PositionIterator;
326 PositionIterator b (npb, npe, source);
327 PositionIterator e (npe, npe, source);
328 ParseCommandInfo info;
329 detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
330 boost::spirit::parse_info<PositionIterator> result;
333 result = boost::spirit::parse(
334 b, e, * impl().grammar.use_parser<Impl::Grammar::SkipParser>());
340 result = boost::spirit::parse(b, e,
341 impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
342 impl().grammar.use_parser<Impl::Grammar::SkipParser>());
344 catch (boost::spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
345 if (impl().grammar.incremental && ex.where == e)
348 throwParserError(ex);
350 // Otherwise the error handling in the parser is broken
351 SENF_ASSERT( result.hit );
356 catch (senf::ExceptionMixin & ex) {
357 detail::FilePositionWithIndex pos (result.stop.get_position());
358 ex << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
365 prefix_ void senf::console::CommandParser::parse(std::string const & command, Callback cb)
367 parseLoop(command.begin(), command.end(), "<unknown>", cb);
370 prefix_ void senf::console::CommandParser::parseFile(std::string const & filename, Callback cb)
372 // file_iterator sets errno to EINVAL and returns error when file size is 0
373 // so we check the file size before
375 if (stat( filename.c_str(), &statBuf) != 0)
376 throw SystemException(filename, errno SENF_EXC_DEBUGINFO);
377 if (statBuf.st_size == 0) return;
378 boost::spirit::file_iterator<> i (filename);
381 // hmm.. errno==0 but the file_iterator is false; something is wrong but we
382 // do not know what exactly, so we throw a SystemeException with EINVAL
383 throw SystemException(filename, EINVAL SENF_EXC_DEBUGINFO);
385 throw SystemException(filename, errno SENF_EXC_DEBUGINFO);
387 boost::spirit::file_iterator<> const i_end (i.make_end());
388 parseLoop(i, i_end, filename, cb);
391 prefix_ void senf::console::CommandParser::parseArguments(std::string const & arguments,
392 ParseCommandInfo & info)
394 typedef boost::spirit::position_iterator<
395 std::string::const_iterator, detail::FilePositionWithIndex> PositionIterator;
396 PositionIterator b (arguments.begin(), arguments.end(), std::string("<unknown>"));
397 PositionIterator e (arguments.end(), arguments.end(), std::string("<unknown>"));
398 detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
399 boost::spirit::parse_info<PositionIterator> result;
401 result = boost::spirit::parse( b, e,
402 impl().grammar.use_parser<Impl::Grammar::ArgumentsParser>(),
403 impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
405 catch (boost::spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
406 throwParserError(ex);
409 detail::FilePositionWithIndex pos (result.stop.get_position());
410 throw ParserErrorException("argument expected")
411 << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
415 prefix_ void senf::console::CommandParser::parsePath(std::string const & path,
416 ParseCommandInfo & info)
418 typedef boost::spirit::position_iterator<
419 std::string::const_iterator, detail::FilePositionWithIndex> PositionIterator;
420 PositionIterator b (path.begin(), path.end(), std::string("<unknown>"));
421 PositionIterator e (path.end(), path.end(), std::string("<unknown>"));
422 detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
423 boost::spirit::parse_info<PositionIterator> result;
425 result = boost::spirit::parse( b, e,
426 impl().grammar.use_parser<Impl::Grammar::PathParser>(),
427 impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
429 catch (boost::spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
430 throwParserError(ex);
433 detail::FilePositionWithIndex pos (result.stop.get_position());
434 throw ParserErrorException("path expected")
435 << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
439 struct senf::console::CommandParser::SetIncremental
441 SetIncremental(CommandParser & parser) : parser_ (parser) {
442 parser_.impl().grammar.incremental = true;
446 parser_.impl().grammar.incremental = false;
449 CommandParser & parser_;
452 prefix_ std::string::size_type
453 senf::console::CommandParser::parseIncremental(std::string const & commands, Callback cb)
455 SetIncremental si (*this);
456 return std::distance( commands.begin(),
457 parseLoop(commands.begin(), commands.end(), "<unknown>", cb) );
460 ///////////////////////////////////////////////////////////////////////////
463 prefix_ bool senf::console::CommandParser::isSpecialChar(char ch)
465 return Impl::Grammar::special_p.test(ch);
468 prefix_ bool senf::console::CommandParser::isPunctuationChar(char ch)
470 return Impl::Grammar::punctuation_p.test(ch);
473 prefix_ bool senf::console::CommandParser::isSpaceChar(char ch)
475 return Impl::Grammar::space_p.test(ch);
478 prefix_ bool senf::console::CommandParser::isInvalidChar(char ch)
480 return Impl::Grammar::invalid_p.test(ch);
483 prefix_ bool senf::console::CommandParser::isWordChar(char ch)
485 return Impl::Grammar::word_p.test(ch);
488 /////////////////////////////cc.e////////////////////////////////////////
490 //#include "Parse.mpp"
496 // comment-column: 40
497 // c-file-style: "senf"
498 // indent-tabs-mode: nil
499 // ispell-local-dictionary: "american"
500 // compile-command: "scons -u test"