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); }
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); }
86 info_->builtin(ParseCommandInfo::BuiltinPOPD); }
90 info_->builtin(ParseCommandInfo::BuiltinEXIT); }
92 void builtin_help(std::vector<Token> & path)
94 info_->builtin(ParseCommandInfo::BuiltinHELP);
95 setBuiltinPathArg(path); }
97 void setBuiltinPathArg(std::vector<Token> & path)
100 // pushToken(ArgumentGroupOpenToken());
101 // for (std::vector<Token>::const_iterator i (path.begin());
102 // i != path.end(); ++i)
104 // pushToken(ArgumentGroupCloseToken());
112 ///////////////////////////////////////////////////////////////////////////
113 // senf::console::Token
115 prefix_ senf::console::Token::Token(TokenType type, std::string token,
116 detail::FilePositionWithIndex const & pos)
117 : type_(type), token_ (token), line_ (pos.line), column_ (pos.column), index_ (pos.index)
121 prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Token const & token)
123 static char const * tokenTypeName[] = {
127 "ArgumentGroupClose",
128 "DirectoryGroupOpen",
129 "DirectoryGroupClose",
135 // The real table is:
136 // static const int bitPosition[32] = {
137 // 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
138 // 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 };
139 // However, we have replaced all values >= sizeof(tokenTypeName) with 0
140 // and have added 1 to all the remaining values
141 static const int bitPosition[32] = {
142 1, 2, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 5, 9,
143 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 7, 0, 6, 0, 10 };
144 // We need to check token.type() against 0 explicitly since 0 and 1 will both be mapped to 0
145 os << tokenTypeName[ token.type()
146 ? bitPosition[(((token.type() & -token.type()) * 0x077CB531UL) >> 27) & 31]
154 ///////////////////////////////////////////////////////////////////////////
155 // senf::console::ParseCommandInfo
157 prefix_ std::ostream & senf::console::operator<<(std::ostream & stream,
158 ParseCommandInfo const & info)
160 if (info.builtin() == ParseCommandInfo::NoBuiltin) {
161 ParseCommandInfo::TokensRange::const_iterator i (info.commandPath().begin());
162 ParseCommandInfo::TokensRange::const_iterator const i_end (info.commandPath().end());
165 stream << i->value();
166 if ( ++i != i_end ) stream << "/";
172 char const * builtins[] = { 0, "cd", "ls", "pushd", "popd", "exit", "help" };
173 stream << "builtin-" << builtins[info.builtin()];
176 ParseCommandInfo::ArgumentsRange args (info.arguments());
177 for (ParseCommandInfo::argument_iterator i (args.begin()); i != args.end(); ++i) {
178 ParseCommandInfo::token_iterator j (i->begin());
180 if ( j != i->end() ) {
182 stream << "'" << j->value() << "'";
183 if ( ++j != i->end() ) stream << ' ';
193 ///////////////////////////////////////////////////////////////////////////
194 // senf::console::ParseCommandInfo::ArgumentIterator
196 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::setRange()
199 if (b_->is(Token::ArgumentGroupOpen)) {
203 if (e_->is(Token::ArgumentGroupOpen))
205 else if (e_->is(Token::ArgumentGroupClose)) {
216 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::decrement()
220 if (b_->is(Token::ArgumentGroupClose)) {
223 if (b_->is(Token::ArgumentGroupClose))
225 else if (b_->is(Token::ArgumentGroupOpen)) {
235 ///////////////////////////////////////////////////////////////////////////
236 // senf::console::CommandParser
240 struct senf::console::CommandParser::Impl
242 typedef detail::CommandGrammar<detail::ParseDispatcher> Grammar;
244 detail::ParseDispatcher dispatcher;
245 Grammar::Context context;
248 Impl() : dispatcher(), context(), grammar(dispatcher, context) {}
255 template <class Error>
256 void throwParserError(Error const & err)
258 static char const * msg [] = { "end of statement expected",
262 senf::console::detail::FilePositionWithIndex pos (err.where.get_position());
263 throw senf::console::CommandParser::ParserErrorException(msg[err.descriptor])
264 << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
273 struct position_policy<senf::console::detail::FilePositionWithIndex>
274 : public position_policy<file_position>
276 typedef position_policy<file_position> Base;
278 void next_line(senf::console::detail::FilePositionWithIndex & pos)
280 Base::next_line(pos);
284 void next_char(senf::console::detail::FilePositionWithIndex & pos)
286 Base::next_char(pos);
290 void tabulation(senf::console::detail::FilePositionWithIndex & pos)
292 Base::tabulation(pos);
299 prefix_ senf::console::CommandParser::CommandParser()
303 prefix_ senf::console::CommandParser::~CommandParser()
306 // This template member is placed here, since it is ONLY called from the implementation. Otherwise,
307 // we would need to expose the Impl member to the public, which we don't want to do.
309 template <class Iterator>
310 prefix_ Iterator senf::console::CommandParser::parseLoop(Iterator npb, Iterator npe,
311 std::string const & source, Callback cb)
313 typedef boost::spirit::position_iterator<
314 Iterator, detail::FilePositionWithIndex> PositionIterator;
315 PositionIterator b (npb, npe, source);
316 PositionIterator e (npe, npe, source);
317 ParseCommandInfo info;
318 detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
319 boost::spirit::parse_info<PositionIterator> result;
322 result = boost::spirit::parse(
323 b, e, * impl().grammar.use_parser<Impl::Grammar::SkipParser>());
329 result = boost::spirit::parse(b, e,
330 impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
331 impl().grammar.use_parser<Impl::Grammar::SkipParser>());
333 catch (boost::spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
334 if (impl().grammar.incremental && ex.where == e)
337 throwParserError(ex);
339 // Otherwise the error handling in the parser is broken
340 SENF_ASSERT( result.hit );
345 catch (senf::ExceptionMixin & ex) {
346 detail::FilePositionWithIndex pos (result.stop.get_position());
347 ex << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
354 prefix_ void senf::console::CommandParser::parse(std::string const & command, Callback cb)
356 parseLoop(command.begin(), command.end(), "<unknown>", cb);
359 prefix_ void senf::console::CommandParser::parseFile(std::string const & filename, Callback cb)
361 boost::spirit::file_iterator<> i (filename);
362 if (!i) throw SystemException(ENOENT SENF_EXC_DEBUGINFO);
363 boost::spirit::file_iterator<> const i_end (i.make_end());
364 parseLoop(i, i_end, filename, cb);
367 prefix_ void senf::console::CommandParser::parseArguments(std::string const & arguments,
368 ParseCommandInfo & info)
370 typedef boost::spirit::position_iterator<
371 std::string::const_iterator, detail::FilePositionWithIndex> PositionIterator;
372 PositionIterator b (arguments.begin(), arguments.end(), std::string("<unknown>"));
373 PositionIterator e (arguments.end(), arguments.end(), std::string("<unknown>"));
374 detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info);
375 boost::spirit::parse_info<PositionIterator> result;
377 result = boost::spirit::parse( b, e,
378 impl().grammar.use_parser<Impl::Grammar::ArgumentsParser>(),
379 impl().grammar.use_parser<Impl::Grammar::SkipParser>() );
381 catch (boost::spirit::parser_error<Impl::Grammar::Errors, PositionIterator> & ex) {
382 throwParserError(ex);
385 detail::FilePositionWithIndex pos (result.stop.get_position());
386 throw ParserErrorException("argument expected")
387 << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
391 prefix_ void senf::console::CommandParser::parsePath(std::string const & path,
392 ParseCommandInfo & info)
394 typedef boost::spirit::position_iterator<
395 std::string::const_iterator, detail::FilePositionWithIndex> PositionIterator;
396 PositionIterator b (path.begin(), path.end(), std::string("<unknown>"));
397 PositionIterator e (path.end(), path.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::PathParser>(),
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("path expected")
411 << "\nat " << pos.file << ":" << pos.line << ":" << pos.column;
415 struct senf::console::CommandParser::SetIncremental
417 SetIncremental(CommandParser & parser) : parser_ (parser) {
418 parser_.impl().grammar.incremental = true;
422 parser_.impl().grammar.incremental = false;
425 CommandParser & parser_;
428 prefix_ std::string::size_type
429 senf::console::CommandParser::parseIncremental(std::string const & commands, Callback cb)
431 SetIncremental si (*this);
432 return std::distance( commands.begin(),
433 parseLoop(commands.begin(), commands.end(), "<unknown>", cb) );
436 ///////////////////////////////////////////////////////////////////////////
439 prefix_ bool senf::console::CommandParser::isSpecialChar(char ch)
441 return Impl::Grammar::special_p.test(ch);
444 prefix_ bool senf::console::CommandParser::isPunctuationChar(char ch)
446 return Impl::Grammar::punctuation_p.test(ch);
449 prefix_ bool senf::console::CommandParser::isSpaceChar(char ch)
451 return Impl::Grammar::space_p.test(ch);
454 prefix_ bool senf::console::CommandParser::isInvalidChar(char ch)
456 return Impl::Grammar::invalid_p.test(ch);
459 prefix_ bool senf::console::CommandParser::isWordChar(char ch)
461 return Impl::Grammar::word_p.test(ch);
464 /////////////////////////////cc.e////////////////////////////////////////
466 //#include "Parse.mpp"
472 // comment-column: 40
473 // c-file-style: "senf"
474 // indent-tabs-mode: nil
475 // ispell-local-dictionary: "american"
476 // compile-command: "scons -u test"