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 <senf/config.hh>
35 #if HAVE_BOOST_SPIRIT_INCLUDE_CLASSIC_HPP
36 # include <boost/spirit/include/classic_file_iterator.hpp>
37 # include <boost/spirit/include/classic_position_iterator.hpp>
39 # include <boost/spirit/iterator/file_iterator.hpp>
40 # include <boost/spirit/iterator/position_iterator.hpp>
43 #include <senf/Utils/Exception.hh>
44 #include <senf/Utils/senfassert.hh>
46 //#include "Parse.mpp"
48 ///////////////////////////////cc.p////////////////////////////////////////
56 struct ParseDispatcher
58 ParseCommandInfo * info_;
61 BindInfo( ParseDispatcher & d, ParseCommandInfo & info)
62 : dispatcher (d) { dispatcher.info_ = &info; }
63 ~BindInfo() { dispatcher.info_ = 0; }
65 ParseDispatcher & dispatcher;
68 void beginCommand(std::vector<Token> & command)
70 info_->command(command); }
75 void pushToken(Token const & token)
76 { info_->addToken(token); }
78 void builtin_cd(std::vector<Token> & path)
80 info_->builtin(ParseCommandInfo::BuiltinCD);
81 setBuiltinPathArg(path); }
83 void builtin_ls(std::vector<Token> & path)
85 info_->builtin(ParseCommandInfo::BuiltinLS);
86 setBuiltinPathArg(path); }
88 void builtin_ll(std::vector<Token> & path)
90 info_->builtin(ParseCommandInfo::BuiltinLL);
91 setBuiltinPathArg(path); }
93 void builtin_lr(std::vector<Token> & path)
95 info_->builtin(ParseCommandInfo::BuiltinLR);
96 setBuiltinPathArg(path); }
99 { // Do NOT call clear since pushDirectory is set in ADDITION
100 // to an ordinary command (which may be only a directory name)
101 info_->builtin(ParseCommandInfo::BuiltinPUSHD); }
105 info_->builtin(ParseCommandInfo::BuiltinPOPD); }
109 info_->builtin(ParseCommandInfo::BuiltinEXIT); }
111 void builtin_help(std::vector<Token> & path)
113 info_->builtin(ParseCommandInfo::BuiltinHELP);
114 setBuiltinPathArg(path); }
116 void setBuiltinPathArg(std::vector<Token> & path)
118 info_->command(path);
119 // pushToken(ArgumentGroupOpenToken());
120 // for (std::vector<Token>::const_iterator i (path.begin());
121 // i != path.end(); ++i)
123 // pushToken(ArgumentGroupCloseToken());
131 ///////////////////////////////////////////////////////////////////////////
132 // senf::console::Token
134 prefix_ senf::console::Token::Token(TokenType type, std::string token,
135 detail::FilePositionWithIndex const & pos)
136 : type_(type), token_ (token), line_ (pos.line), column_ (pos.column), index_ (pos.index)
140 prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Token const & token)
142 static char const * tokenTypeName[] = {
146 "ArgumentGroupClose",
147 "DirectoryGroupOpen",
148 "DirectoryGroupClose",
154 // The real table is:
155 // static const int bitPosition[32] = {
156 // 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
157 // 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 };
158 // However, we have replaced all values >= sizeof(tokenTypeName) with 0
159 // and have added 1 to all the remaining values
160 static const int bitPosition[32] = {
161 1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 5, 9,
162 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 7, 0, 6, 0, 10 };
163 // We need to check token.type() against 0 explicitly since 0 and 1 will both be mapped to 0
164 os << tokenTypeName[ token.type()
165 ? bitPosition[(((token.type() & -token.type()) * 0x077CB531UL) >> 27) & 31]
173 ///////////////////////////////////////////////////////////////////////////
174 // senf::console::ParseCommandInfo
176 prefix_ std::ostream & senf::console::operator<<(std::ostream & stream,
177 ParseCommandInfo const & info)
179 if (info.builtin() == ParseCommandInfo::NoBuiltin) {
180 ParseCommandInfo::TokensRange::const_iterator i (info.commandPath().begin());
181 ParseCommandInfo::TokensRange::const_iterator const i_end (info.commandPath().end());
184 stream << i->value();
185 if ( ++i != i_end ) stream << "/";
191 char const * builtins[] = { 0, "cd", "ls", "lr", "pushd", "popd", "exit", "help" };
192 stream << "builtin-" << builtins[info.builtin()];
195 ParseCommandInfo::ArgumentsRange args (info.arguments());
196 for (ParseCommandInfo::argument_iterator i (args.begin()); i != args.end(); ++i) {
197 ParseCommandInfo::token_iterator j (i->begin());
199 if ( j != i->end() ) {
201 stream << "'" << j->value() << "'";
202 if ( ++j != i->end() ) stream << ' ';
212 ///////////////////////////////////////////////////////////////////////////
213 // senf::console::ParseCommandInfo::ArgumentIterator
215 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::setRange()
218 if (b_->is(Token::ArgumentGroupOpen)) {
222 if (e_->is(Token::ArgumentGroupOpen))
224 else if (e_->is(Token::ArgumentGroupClose)) {
235 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::decrement()
239 if (b_->is(Token::ArgumentGroupClose)) {
242 if (b_->is(Token::ArgumentGroupClose))
244 else if (b_->is(Token::ArgumentGroupOpen)) {
254 ///////////////////////////////////////////////////////////////////////////
255 // senf::console::CommandParser
259 struct senf::console::CommandParser::Impl
261 typedef detail::CommandGrammar<detail::ParseDispatcher> Grammar;
263 detail::ParseDispatcher dispatcher;
264 Grammar::Context context;
267 Impl() : dispatcher(), context(), grammar(dispatcher, context) {}
274 template <class Error>
275 void throwParserError(Error const & err)
277 static char const * msg [] = { "end of statement expected",
281 senf::console::detail::FilePositionWithIndex pos (err.where.get_position());
282 throw senf::console::CommandParser::ParserErrorException(msg[err.descriptor])
283 << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
290 #if HAVE_BOOST_SPIRIT_INCLUDE_CLASSIC_HPP
295 struct position_policy<senf::console::detail::FilePositionWithIndex>
296 : public position_policy<file_position>
298 typedef position_policy<file_position> Base;
300 void next_line(senf::console::detail::FilePositionWithIndex & pos)
302 Base::next_line(pos);
306 void next_char(senf::console::detail::FilePositionWithIndex & pos)
308 Base::next_char(pos);
312 void tabulation(senf::console::detail::FilePositionWithIndex & pos)
314 Base::tabulation(pos);
319 #if HAVE_BOOST_SPIRIT_INCLUDE_CLASSIC_HPP
324 prefix_ senf::console::CommandParser::CommandParser()
328 prefix_ senf::console::CommandParser::~CommandParser()
331 // This template member is placed here, since it is ONLY called from the implementation. Otherwise,
332 // we would need to expose the Impl member to the public, which we don't want to do.
334 template <class Iterator>
335 prefix_ Iterator senf::console::CommandParser::parseLoop(Iterator npb, Iterator npe,
336 std::string const & source, Callback cb)
338 typedef detail::boost_spirit::position_iterator<
339 Iterator, detail::FilePositionWithIndex> PositionIterator;
340 PositionIterator b (npb, npe, source);
341 PositionIterator e (npe, npe, source);
342 ParseCommandInfo info;
343 detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
344 detail::boost_spirit::parse_info<PositionIterator> result;
347 result = detail::boost_spirit::parse(
348 b, e, * impl().grammar.use_parser<Impl::Grammar::SkipParser>());
354 result = detail::boost_spirit::parse(b, e,
355 impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
356 impl().grammar.use_parser<Impl::Grammar::SkipParser>());
358 catch (detail::boost_spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
359 if (impl().grammar.incremental && ex.where == e)
362 throwParserError(ex);
364 // Otherwise the error handling in the parser is broken
365 SENF_ASSERT( result.hit, "Internal parser failure (error handling broken?)" );
370 catch (senf::ExceptionMixin & ex) {
371 detail::FilePositionWithIndex pos (result.stop.get_position());
372 ex << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
379 prefix_ void senf::console::CommandParser::parse(std::string const & command, Callback cb)
381 parseLoop(command.begin(), command.end(), "<unknown>", cb);
384 prefix_ void senf::console::CommandParser::parseFile(std::string const & filename, Callback cb)
386 // file_iterator sets errno to EINVAL and returns error when file size is 0
387 // so we check the file size before
389 if (stat( filename.c_str(), &statBuf) != 0)
390 throw SystemException(filename, errno SENF_EXC_DEBUGINFO);
391 if (statBuf.st_size == 0) return;
392 detail::boost_spirit::file_iterator<> i (filename);
395 // hmm.. errno==0 but the file_iterator is false; something is wrong but we
396 // do not know what exactly, so we throw a SystemeException with EINVAL
397 throw SystemException(filename, EINVAL SENF_EXC_DEBUGINFO);
399 throw SystemException(filename, errno SENF_EXC_DEBUGINFO);
401 detail::boost_spirit::file_iterator<> const i_end (i.make_end());
402 parseLoop(i, i_end, filename, cb);
405 prefix_ void senf::console::CommandParser::parseArguments(std::string const & arguments,
406 ParseCommandInfo & info)
408 typedef detail::boost_spirit::position_iterator<
409 std::string::const_iterator, detail::FilePositionWithIndex> PositionIterator;
410 PositionIterator b (arguments.begin(), arguments.end(), std::string("<unknown>"));
411 PositionIterator e (arguments.end(), arguments.end(), std::string("<unknown>"));
412 detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
413 detail::boost_spirit::parse_info<PositionIterator> result;
415 result = detail::boost_spirit::parse( b, e,
416 impl().grammar.use_parser<Impl::Grammar::ArgumentsParser>(),
417 impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
419 catch (detail::boost_spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
420 throwParserError(ex);
423 detail::FilePositionWithIndex pos (result.stop.get_position());
424 throw ParserErrorException("argument expected")
425 << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
429 prefix_ void senf::console::CommandParser::parsePath(std::string const & path,
430 ParseCommandInfo & info)
432 typedef detail::boost_spirit::position_iterator<
433 std::string::const_iterator, detail::FilePositionWithIndex> PositionIterator;
434 PositionIterator b (path.begin(), path.end(), std::string("<unknown>"));
435 PositionIterator e (path.end(), path.end(), std::string("<unknown>"));
436 detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
437 detail::boost_spirit::parse_info<PositionIterator> result;
439 result = detail::boost_spirit::parse( b, e,
440 impl().grammar.use_parser<Impl::Grammar::PathParser>(),
441 impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
443 catch (detail::boost_spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
444 throwParserError(ex);
447 detail::FilePositionWithIndex pos (result.stop.get_position());
448 throw ParserErrorException("path expected")
449 << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
453 struct senf::console::CommandParser::SetIncremental
455 SetIncremental(CommandParser & parser) : parser_ (parser) {
456 parser_.impl().grammar.incremental = true;
460 parser_.impl().grammar.incremental = false;
463 CommandParser & parser_;
466 prefix_ std::string::size_type
467 senf::console::CommandParser::parseIncremental(std::string const & commands, Callback cb)
469 SetIncremental si (*this);
470 return std::distance( commands.begin(),
471 parseLoop(commands.begin(), commands.end(), "<unknown>", cb) );
474 ///////////////////////////////////////////////////////////////////////////
477 prefix_ bool senf::console::CommandParser::isSpecialChar(char ch)
479 return Impl::Grammar::special_p.test(ch);
482 prefix_ bool senf::console::CommandParser::isPunctuationChar(char ch)
484 return Impl::Grammar::punctuation_p.test(ch);
487 prefix_ bool senf::console::CommandParser::isSpaceChar(char ch)
489 return Impl::Grammar::space_p.test(ch);
492 prefix_ bool senf::console::CommandParser::isInvalidChar(char ch)
494 return Impl::Grammar::invalid_p.test(ch);
497 prefix_ bool senf::console::CommandParser::isWordChar(char ch)
499 return Impl::Grammar::word_p.test(ch);
502 /////////////////////////////cc.e////////////////////////////////////////
504 //#include "Parse.mpp"
510 // comment-column: 40
511 // c-file-style: "senf"
512 // indent-tabs-mode: nil
513 // ispell-local-dictionary: "american"
514 // compile-command: "scons -u test"