Console: Implement short-option and non-option parsing
[senf.git] / 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 "../Utils/Exception.hh"
34
35 //#include "Parse.mpp"
36 #define prefix_
37 ///////////////////////////////cc.p////////////////////////////////////////
38
39 namespace senf {
40 namespace console {
41 namespace detail {
42
43 #ifndef DOXYGEN
44
45     struct ParseDispatcher
46     {
47         ParseCommandInfo * info_;
48         CommandParser::Callback cb_;
49
50         struct BindInfo {
51             BindInfo( ParseDispatcher & d, ParseCommandInfo & info, CommandParser::Callback cb)
52                 : dispatcher (d) { dispatcher.info_ = &info; dispatcher.cb_ = cb; }
53             ~BindInfo() { dispatcher.info_ = 0; dispatcher.cb_  = 0; }
54
55             ParseDispatcher & dispatcher;
56         };
57
58         void beginCommand(std::vector<Token> & command)
59             { info_->clear();
60               info_->command(command); }
61
62         void endCommand()
63             { cb_(*info_); }
64
65         void pushToken(Token const & token)
66             { info_->addToken(token); }
67
68         void builtin_cd(std::vector<Token> & path)
69             { info_->clear();
70               info_->builtin(ParseCommandInfo::BuiltinCD);
71               setBuiltinPathArg(path);
72               cb_(*info_); }
73
74         void builtin_ls(std::vector<Token> & path)
75             { info_->clear();
76               info_->builtin(ParseCommandInfo::BuiltinLS);
77               setBuiltinPathArg(path);
78               cb_(*info_); }
79
80         void pushDirectory(std::vector<Token> & path)
81             { info_->clear();
82               info_->builtin(ParseCommandInfo::BuiltinPUSHD);
83               setBuiltinPathArg(path);
84               cb_(*info_); }
85
86         void popDirectory()
87             { info_->clear();
88               info_->builtin(ParseCommandInfo::BuiltinPOPD);
89               cb_(*info_); }
90         
91         void builtin_exit()
92             { info_->clear();
93               info_->builtin(ParseCommandInfo::BuiltinEXIT);
94               cb_(*info_); }
95
96         void builtin_help(std::vector<Token> & path)
97             { info_->clear();
98               info_->builtin(ParseCommandInfo::BuiltinHELP);
99               setBuiltinPathArg(path);
100               cb_(*info_); }
101
102         void setBuiltinPathArg(std::vector<Token> & path)
103             {
104                 pushToken(ArgumentGroupOpenToken());
105                 for (std::vector<Token>::const_iterator i (path.begin());
106                      i != path.end(); ++i)
107                     pushToken(*i);
108                 pushToken(ArgumentGroupCloseToken());
109             }
110     };
111
112 #endif
113
114 }}}
115
116 ///////////////////////////////////////////////////////////////////////////
117 // senf::console::Token
118
119 prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Token const & token)
120 {
121     static char const * tokenTypeName[] = {
122         "None",
123         "PathSeparator",
124         "ArgumentGroupOpen",
125         "ArgumentGroupClose",
126         "DirectoryGroupOpen",
127         "DirectoryGroupClose",
128         "CommandTerminator",
129         "OtherPunctuation",
130         "BasicString",
131         "HexString",
132         "Word" };
133     // The real table is:
134     //     static const int bitPosition[32] = {
135     //         0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 
136     //         31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 };
137     // However, we have replaced all values > sizeof(tokenTypeName) with 0
138     static const int bitPosition[32] = {
139         0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 4, 8, 
140         0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 6, 0, 5, 10, 9 };
141     os << tokenTypeName[ token.type() 
142                          ? bitPosition[((token.type() & -token.type()) * 0x077CB531UL) >> 27]+1
143                          : 0 ]
144        << "('"
145        << token.value()
146        << "')";
147     return os;
148 }
149
150 ///////////////////////////////////////////////////////////////////////////
151 // senf::console::ParseCommandInfo
152
153 prefix_ std::ostream & senf::console::operator<<(std::ostream & stream,
154                                                  ParseCommandInfo const & info)
155 {
156     if (info.builtin() == ParseCommandInfo::NoBuiltin) {
157         ParseCommandInfo::TokensRange::const_iterator i (info.commandPath().begin());
158         ParseCommandInfo::TokensRange::const_iterator const i_end (info.commandPath().end());
159         if (i != i_end) {
160             for (;;) {
161                 stream << i->value();
162                 if ( ++i != i_end ) stream << "/";
163                 else                break;
164             }
165         }
166     }
167     else {
168         char const * builtins[] = { "", "cd", "ls", "pushd", "popd", "exit", "help" };
169         stream << "builtin-" << builtins[info.builtin()];
170     }
171         
172     ParseCommandInfo::ArgumentsRange args (info.arguments());
173     for (ParseCommandInfo::argument_iterator i (args.begin()); i != args.end(); ++i) {
174         ParseCommandInfo::token_iterator j (i->begin());
175         stream << " [";
176         if ( j != i->end() ) {
177             for (;;) {
178                 stream << "'" << j->value() << "'";
179                 if ( ++j != i->end() ) stream << ' ';
180                 else                   break;
181             }
182         }
183         stream << "]";
184     }
185
186     return stream;
187 }
188
189 ///////////////////////////////////////////////////////////////////////////
190 // senf::console::ParseCommandInfo::ArgumentIterator
191
192 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::setRange()
193     const
194 {
195     if (b_->is(Token::ArgumentGroupOpen)) {
196         unsigned level (0);
197         e_ = b_;
198         for (;;) {
199             if (e_->is(Token::ArgumentGroupOpen))
200                 ++ level;
201             else if (e_->is(Token::ArgumentGroupClose)) {
202                 -- level;
203                 if (level == 0)
204                     break;
205             }
206             ++e_;
207         }
208     }
209     ++ e_;
210 }
211
212 prefix_ void senf::console::ParseCommandInfo::ArgumentIterator::decrement()
213 {
214     e_ = b_;
215     --b_;
216     if (b_->is(Token::ArgumentGroupClose)) {
217         unsigned level (0);
218         for (;;) {
219             if (b_->is(Token::ArgumentGroupClose))
220                 ++ level;
221             else if (b_->is(Token::ArgumentGroupOpen)) {
222                 -- level;
223                 if (level == 0)
224                     break;
225             }
226             --b_;
227         }
228     }
229 }
230
231 ///////////////////////////////////////////////////////////////////////////
232 // senf::console::CommandParser
233
234 #ifndef DOXYGEN
235
236 struct senf::console::CommandParser::Impl
237 {
238     typedef detail::CommandGrammar<detail::ParseDispatcher> Grammar;
239
240     detail::ParseDispatcher dispatcher;
241     Grammar::Context context;
242     Grammar grammar;
243
244     Impl() : dispatcher(), context(), grammar(dispatcher, context) {}
245 };
246
247 #endif
248
249 prefix_ senf::console::CommandParser::CommandParser()
250     : impl_ (new Impl())
251 {}
252
253 prefix_ senf::console::CommandParser::~CommandParser()
254 {}
255
256 prefix_ bool senf::console::CommandParser::parse(std::string command, Callback cb)
257 {
258     ParseCommandInfo info;
259     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info, cb);
260     return boost::spirit::parse( command.begin(), command.end(), 
261                                  impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
262                                  impl().grammar.use_parser<Impl::Grammar::SkipParser>()
263         ).full;
264 }
265
266 prefix_ bool senf::console::CommandParser::parseFile(std::string filename, Callback cb)
267 {
268     ParseCommandInfo info;
269     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info, cb);
270     boost::spirit::file_iterator<> i (filename);
271     if (!i) throw SystemException(ENOENT SENF_EXC_DEBUGINFO);
272     boost::spirit::file_iterator<> const i_end (i.make_end());
273     
274     return boost::spirit::parse( i, i_end, 
275                                  impl().grammar.use_parser<Impl::Grammar::CommandParser>(),
276                                  impl().grammar.use_parser<Impl::Grammar::SkipParser>()
277         ).full;
278 }
279
280 prefix_ bool senf::console::CommandParser::parseArguments(std::string arguments,
281                                                           ParseCommandInfo & info)
282 {
283     detail::ParseDispatcher::BindInfo bind (impl().dispatcher, info, 0);
284     return boost::spirit::parse( arguments.begin(), arguments.end(), 
285                                  impl().grammar.use_parser<Impl::Grammar::ArgumentsParser>(),
286                                  impl().grammar.use_parser<Impl::Grammar::SkipParser>()
287         ).full;
288 }
289
290 ///////////////////////////////////////////////////////////////////////////
291 // senf::console::SyntaxErrorException
292
293 prefix_ char const * senf::console::SyntaxErrorException::what()
294     const throw()
295 {
296     return message().empty() ? "syntax error" : message().c_str();
297 }
298
299 ///////////////////////////////cc.e////////////////////////////////////////
300 #undef prefix_
301 //#include "Parse.mpp"
302
303 \f
304 // Local Variables:
305 // mode: c++
306 // fill-column: 100
307 // comment-column: 40
308 // c-file-style: "senf"
309 // indent-tabs-mode: nil
310 // ispell-local-dictionary: "american"
311 // compile-command: "scons -u test"
312 // End: