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 */
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"
37 //#include "Parse.mpp"
39 ///////////////////////////////cc.p////////////////////////////////////////
47 struct ParseDispatcher
49 ParseCommandInfo * info_;
52 BindInfo( ParseDispatcher & d, ParseCommandInfo & info)
53 : dispatcher (d) { dispatcher.info_ = &info; }
54 ~BindInfo() { dispatcher.info_ = 0; }
56 ParseDispatcher & dispatcher;
59 void beginCommand(std::vector<Token> & command)
61 info_->command(command); }
66 void pushToken(Token const & token)
67 { info_->addToken(token); }
69 void builtin_cd(std::vector<Token> & path)
71 info_->builtin(ParseCommandInfo::BuiltinCD);
72 setBuiltinPathArg(path); }
74 void builtin_ls(std::vector<Token> & path)
76 info_->builtin(ParseCommandInfo::BuiltinLS);
77 setBuiltinPathArg(path); }
79 void builtin_lr(std::vector<Token> & path)
81 info_->builtin(ParseCommandInfo::BuiltinLR);
82 setBuiltinPathArg(path); }
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); }
91 info_->builtin(ParseCommandInfo::BuiltinPOPD); }
95 info_->builtin(ParseCommandInfo::BuiltinEXIT); }
97 void builtin_help(std::vector<Token> & path)
99 info_->builtin(ParseCommandInfo::BuiltinHELP);
100 setBuiltinPathArg(path); }
102 void setBuiltinPathArg(std::vector<Token> & path)
104 info_->command(path);
105 // pushToken(ArgumentGroupOpenToken());
106 // for (std::vector<Token>::const_iterator i (path.begin());
107 // i != path.end(); ++i)
109 // pushToken(ArgumentGroupCloseToken());
117 ///////////////////////////////////////////////////////////////////////////
118 // senf::console::Token
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)
126 prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Token const & token)
128 static char const * tokenTypeName[] = {
132 "ArgumentGroupClose",
133 "DirectoryGroupOpen",
134 "DirectoryGroupClose",
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]
159 ///////////////////////////////////////////////////////////////////////////
160 // senf::console::ParseCommandInfo
162 prefix_ std::ostream & senf::console::operator<<(std::ostream & stream,
163 ParseCommandInfo const & info)
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());
170 stream << i->value();
171 if ( ++i != i_end ) stream << "/";
177 char const * builtins[] = { 0, "cd", "ls", "lr", "pushd", "popd", "exit", "help" };
178 stream << "builtin-" << builtins[info.builtin()];
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());
185 if ( j != i->end() ) {
187 stream << "'" << j->value() << "'";
188 if ( ++j != i->end() ) stream << ' ';
198 ///////////////////////////////////////////////////////////////////////////
199 // senf::console::ParseCommandInfo::ArgumentIterator
201 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::setRange()
204 if (b_->is(Token::ArgumentGroupOpen)) {
208 if (e_->is(Token::ArgumentGroupOpen))
210 else if (e_->is(Token::ArgumentGroupClose)) {
221 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::decrement()
225 if (b_->is(Token::ArgumentGroupClose)) {
228 if (b_->is(Token::ArgumentGroupClose))
230 else if (b_->is(Token::ArgumentGroupOpen)) {
240 ///////////////////////////////////////////////////////////////////////////
241 // senf::console::CommandParser
245 struct senf::console::CommandParser::Impl
247 typedef detail::CommandGrammar<detail::ParseDispatcher> Grammar;
249 detail::ParseDispatcher dispatcher;
250 Grammar::Context context;
253 Impl() : dispatcher(), context(), grammar(dispatcher, context) {}
260 template <class Error>
261 void throwParserError(Error const & err)
263 static char const * msg [] = { "end of statement 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;
278 struct position_policy<senf::console::detail::FilePositionWithIndex>
279 : public position_policy<file_position>
281 typedef position_policy<file_position> Base;
283 void next_line(senf::console::detail::FilePositionWithIndex & pos)
285 Base::next_line(pos);
289 void next_char(senf::console::detail::FilePositionWithIndex & pos)
291 Base::next_char(pos);
295 void tabulation(senf::console::detail::FilePositionWithIndex & pos)
297 Base::tabulation(pos);
304 prefix_ senf::console::CommandParser::CommandParser()
308 prefix_ senf::console::CommandParser::~CommandParser()
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.
314 template <class Iterator>
315 prefix_ Iterator senf::console::CommandParser::parseLoop(Iterator npb, Iterator npe,
316 std::string const & source, Callback cb)
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;
327 result = boost::spirit::parse(
328 b, e, * impl().grammar.use_parser<Impl::Grammar::SkipParser>());
334 result = boost::spirit::parse(b, e,
335 impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
336 impl().grammar.use_parser<Impl::Grammar::SkipParser>());
338 catch (boost::spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
339 if (impl().grammar.incremental && ex.where == e)
342 throwParserError(ex);
344 // Otherwise the error handling in the parser is broken
345 SENF_ASSERT( result.hit );
350 catch (senf::ExceptionMixin & ex) {
351 detail::FilePositionWithIndex pos (result.stop.get_position());
352 ex << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
359 prefix_ void senf::console::CommandParser::parse(std::string const & command, Callback cb)
361 parseLoop(command.begin(), command.end(), "<unknown>", cb);
364 prefix_ void senf::console::CommandParser::parseFile(std::string const & filename, Callback cb)
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);
372 prefix_ void senf::console::CommandParser::parseArguments(std::string const & arguments,
373 ParseCommandInfo & info)
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;
382 result = boost::spirit::parse( b, e,
383 impl().grammar.use_parser<Impl::Grammar::ArgumentsParser>(),
384 impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
386 catch (boost::spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
387 throwParserError(ex);
390 detail::FilePositionWithIndex pos (result.stop.get_position());
391 throw ParserErrorException("argument expected")
392 << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
396 prefix_ void senf::console::CommandParser::parsePath(std::string const & path,
397 ParseCommandInfo & info)
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;
406 result = boost::spirit::parse( b, e,
407 impl().grammar.use_parser<Impl::Grammar::PathParser>(),
408 impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
410 catch (boost::spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
411 throwParserError(ex);
414 detail::FilePositionWithIndex pos (result.stop.get_position());
415 throw ParserErrorException("path expected")
416 << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
420 struct senf::console::CommandParser::SetIncremental
422 SetIncremental(CommandParser & parser) : parser_ (parser) {
423 parser_.impl().grammar.incremental = true;
427 parser_.impl().grammar.incremental = false;
430 CommandParser & parser_;
433 prefix_ std::string::size_type
434 senf::console::CommandParser::parseIncremental(std::string const & commands, Callback cb)
436 SetIncremental si (*this);
437 return std::distance( commands.begin(),
438 parseLoop(commands.begin(), commands.end(), "<unknown>", cb) );
441 ///////////////////////////////////////////////////////////////////////////
444 prefix_ bool senf::console::CommandParser::isSpecialChar(char ch)
446 return Impl::Grammar::special_p.test(ch);
449 prefix_ bool senf::console::CommandParser::isPunctuationChar(char ch)
451 return Impl::Grammar::punctuation_p.test(ch);
454 prefix_ bool senf::console::CommandParser::isSpaceChar(char ch)
456 return Impl::Grammar::space_p.test(ch);
459 prefix_ bool senf::console::CommandParser::isInvalidChar(char ch)
461 return Impl::Grammar::invalid_p.test(ch);
464 prefix_ bool senf::console::CommandParser::isWordChar(char ch)
466 return Impl::Grammar::word_p.test(ch);
469 /////////////////////////////cc.e////////////////////////////////////////
471 //#include "Parse.mpp"
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"