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 internal header */
31 #include <boost/regex.hpp>
32 #include <boost/spirit.hpp>
33 #include <boost/spirit/utility/grammar_def.hpp>
34 #include <boost/spirit/actor.hpp>
35 #include <boost/bind.hpp>
36 #include <boost/function.hpp>
37 #include <boost/ref.hpp>
39 ///////////////////////////////ih.p////////////////////////////////////////
47 ///////////////////////////////////////////////////////////////////////////
52 template <class T, class Value>
53 void act(T & ref, Value const & value) const
54 { ref += T(1, value); }
56 template <class T, class Iterator>
57 void act(T & ref, Iterator const & f, Iterator const & l) const
62 inline boost::spirit::ref_value_actor<T, append_action>
64 { return boost::spirit::ref_value_actor<T, append_action>(ref); }
66 template <class T, class Value>
67 inline boost::spirit::ref_const_ref_actor<T, Value, append_action>
68 append_a(T & ref, Value const & value)
69 { return boost::spirit::ref_const_ref_actor<T, Value, append_action>(ref, value); }
71 ///////////////////////////////////////////////////////////////////////////
74 template <class ParseDispatcher>
75 struct CommandGrammar : boost::spirit::grammar<CommandGrammar<ParseDispatcher> >
77 ///////////////////////////////////////////////////////////////////////////
80 enum { CommandParser, SkipParser };
82 ///////////////////////////////////////////////////////////////////////////
83 // The parse context (variables needed while parsing)
85 typedef ArgumentToken::TokenType TokenType;
89 std::vector<std::string> path;
93 // OUCH ... This is sooooo stupid .. push_back_a and assign_a take their
94 // arguments by const-reference and STORE the REFERENCE ... they do NOT accept
95 // literal values !!!!!!
96 static const TokenType BasicString;
97 static const TokenType HexString;
98 static const TokenType Word;
99 static const std::string EmptyString;
104 ///////////////////////////////////////////////////////////////////////////
105 // Dispatching semantic actions
107 ParseDispatcher & dispatcher;
109 struct Dispatch_actor
111 Dispatch_actor(boost::function<void ()> fn_) : fn (fn_) {}
113 template <class Value>
114 void operator()(Value const & value) const
117 template <class Iterator>
118 void operator()(Iterator const & f, Iterator const & l) const
121 boost::function<void ()> fn;
124 template <class Callback>
125 Dispatch_actor dispatch(Callback cb) const
126 { return Dispatch_actor(boost::bind(cb, boost::ref(dispatcher))); }
128 template <class Callback, class Arg>
129 Dispatch_actor dispatch(Callback cb, Arg const & arg) const
130 { return Dispatch_actor(boost::bind(cb, boost::ref(dispatcher), arg)); }
132 template <class Callback, class Arg1, class Arg2>
133 Dispatch_actor dispatch(Callback cb, Arg1 const & arg1, Arg2 const & arg2) const
134 { return Dispatch_actor(boost::bind(cb, boost::ref(dispatcher), arg1, arg2)); }
136 ///////////////////////////////////////////////////////////////////////////
138 CommandGrammar(ParseDispatcher & d, Context & c)
139 : context(c), dispatcher(d) {}
141 template <class Scanner>
143 : public boost::spirit::grammar_def< boost::spirit::rule<Scanner>,
144 boost::spirit::rule<Scanner> >
146 boost::spirit::rule<Scanner> command, path, argument, word, string, hexstring, token,
147 punctuation, hexbyte, balanced_tokens, simple_argument, complex_argument, builtin,
148 skip, commands, block, statement, relpath, abspath;
149 boost::spirit::chset<> special_p, punctuation_p, space_p, invalid_p, word_p;
150 boost::spirit::distinct_parser<> keyword_p;
152 definition(CommandGrammar const & self) :
154 // Characters with a special meaning within the parser
155 special_p ("/(){};"),
157 // Characters which are returned as punctuation tokens
158 // (only allowed within '()')
159 punctuation_p (",=/{};"),
161 // Whitespace characters
164 // Invalid characters: All chars below \x20 (space) which are not space_p
165 // (don't put a \0 in the chset<> argument *string* ...)
167 boost::spirit::chset<>('\0') | boost::spirit::chset<>("\x01-\x20") - space_p ),
169 // Valid word characters
171 boost::spirit::anychar_p - special_p - punctuation_p - space_p - invalid_p),
173 // Keywords must not be followed by a word char or '/'
174 keyword_p ( word_p | boost::spirit::ch_p('/') )
177 using namespace boost::spirit;
178 typedef ParseDispatcher PD;
180 ///////////////////////////////////////////////////////////////////
184 // This is EBNF with some minor tweaks to accommodate C++ syntax
186 // * and + like EBNF but they precede their argument
189 // a % b match any number of a's separated by b
190 // a - b match a but not b
192 // Beside this, we use some special parsers (ch_p, eps_p, confix_p, lex_escape_ch_p,
193 // keyword_p, comment_p) and directives (lexeme_d), however, the parser should be
196 // ch_p match character
197 // eps_p always matches nothing (to attach unconditional actions)
198 // confix_p(a,b,c) match b, preceded by a and terminated by c. Used to parse
199 // string literals and comments
200 // lex_escape_ch_p match a lex style escape char. This is like a C++ style
201 // literal string escape char, however \x will be replaced by 'x'
202 // for any char 'x' if it has no special meaning.
203 // keyword_p match a delimited keyword
204 // comment_p(a,b) match comment starting with a and terminated with b. b
205 // defaults to end-of-line
207 // lexeme_d don't skip whitespace (as defined by the skip parser)
209 // Aligned to the right at column 50 are semantic actions.
211 // For clarity, I have used 'ch_p' explicitly throughout even though it is optional
214 // More info is in the Boost.Spirit documentation
221 = builtin >> (ch_p(';') | end_p)
222 | path >> ( block | statement )
223 | ch_p(';') // Ignore empty commands
229 >> eps_p [ self.dispatch(&PD::builtin_cd,
230 boost::ref(self.context.path)) ]
233 >> eps_p [ self.dispatch(&PD::builtin_ls,
234 boost::ref(self.context.path)) ]
235 | keyword_p("exit") [ self.dispatch(&PD::builtin_exit) ]
239 >> eps_p [ self.dispatch(&PD::builtin_help,
240 boost::ref(self.context.path)) ]
244 = ch_p('{') [ self.dispatch(&PD::pushDirectory,
245 boost::ref(self.context.path)) ]
247 >> ch_p('}') [ self.dispatch(&PD::popDirectory) ]
251 = eps_p [ self.dispatch(&PD::beginCommand,
252 boost::ref(self.context.path)) ]
254 >> (ch_p(';') | end_p)
255 >> eps_p [ self.dispatch(&PD::endCommand) ]
259 = simple_argument [ self.dispatch(&PD::pushArgument,
260 boost::ref(self.context.type),
261 boost::ref(self.context.str)) ]
265 simple_argument // All these return their value in context.str
271 complex_argument // Argument consists of multiple tokens
272 = ch_p('(') [ self.dispatch(&PD::openGroup) ]
274 >> ch_p(')') [ self.dispatch(&PD::closeGroup) ]
277 string // Returns value in context.str
278 = eps_p [ clear_a(self.context.str) ]
279 >> eps_p [ assign_a(self.context.type,
280 self.context.BasicString) ]
284 >> * ( ( lex_escape_ch_p[ assign_a(self.context.ch) ]
286 ) [ append_a(self.context.str,
293 hexstring // Returns value in context.str
294 = eps_p [ clear_a(self.context.str) ]
295 >> eps_p [ assign_a(self.context.type,
296 self.context.HexString) ]
297 >> confix_p( "x\"", * hexbyte, '"' )
300 path // Returns value in context.path
301 = eps_p [ clear_a(self.context.path) ]
306 = ( word [ push_back_a(self.context.path) ]
308 >> ( ! ch_p('/') [ push_back_a(self.context.path,
309 self.context.EmptyString) ] )
313 = ch_p('/') [ push_back_a(self.context.path,
314 self.context.EmptyString) ]
316 | eps_p [ push_back_a(self.context.path, "") ] )
320 = ch_p('(') [ self.dispatch(&PD::pushPunctuation, "(") ]
322 >> ch_p(')') [ self.dispatch(&PD::pushPunctuation, ")") ]
326 = simple_argument [ self.dispatch(&PD::pushWord,
327 boost::ref(self.context.type),
328 boost::ref(self.context.str)) ]
329 | punctuation [ self.dispatch(&PD::pushPunctuation,
330 boost::ref(self.context.str)) ]
334 punctuation // Returns value in context.str
335 = punctuation_p [ assign_a(self.context.str) ]
338 word // Returns value in context.str
341 eps_p [ assign_a(self.context.type,
343 >> (+ word_p) [ assign_a(self.context.str) ]
348 = uint_parser<char, 16, 2, 2>()
349 [ append_a(self.context.str) ]
353 = space_p | comment_p('#')
356 ///////////////////////////////////////////////////////////////////
359 commands, // CommandParser
363 BOOST_SPIRIT_DEBUG_TRACE_RULE(command,1);
364 BOOST_SPIRIT_DEBUG_TRACE_RULE(path,1);
365 BOOST_SPIRIT_DEBUG_TRACE_RULE(argument,1);
366 BOOST_SPIRIT_DEBUG_TRACE_RULE(word,1);
367 BOOST_SPIRIT_DEBUG_TRACE_RULE(string,1);
368 BOOST_SPIRIT_DEBUG_TRACE_RULE(hexstring,1);
369 BOOST_SPIRIT_DEBUG_TRACE_RULE(token,1);
370 BOOST_SPIRIT_DEBUG_TRACE_RULE(punctuation,1);
371 BOOST_SPIRIT_DEBUG_TRACE_RULE(hexbyte,1);
372 BOOST_SPIRIT_DEBUG_TRACE_RULE(balanced_tokens,1);
373 BOOST_SPIRIT_DEBUG_TRACE_RULE(simple_argument,1);
374 BOOST_SPIRIT_DEBUG_TRACE_RULE(complex_argument,1);
375 BOOST_SPIRIT_DEBUG_TRACE_RULE(builtin,1);
376 BOOST_SPIRIT_DEBUG_TRACE_RULE(commands,1);
377 BOOST_SPIRIT_DEBUG_TRACE_RULE(block,1);
378 BOOST_SPIRIT_DEBUG_TRACE_RULE(statement,1);
379 BOOST_SPIRIT_DEBUG_TRACE_RULE(relpath,1);
380 BOOST_SPIRIT_DEBUG_TRACE_RULE(abspath,1);
385 template <class ParseDispatcher>
386 ArgumentToken::TokenType const CommandGrammar<ParseDispatcher>::Context::BasicString (
387 ArgumentToken::BasicString);
389 template <class ParseDispatcher>
390 ArgumentToken::TokenType const CommandGrammar<ParseDispatcher>::Context::HexString(
391 ArgumentToken::HexString);
393 template <class ParseDispatcher>
394 ArgumentToken::TokenType const CommandGrammar<ParseDispatcher>::Context::Word(
395 ArgumentToken::Word);
397 template <class ParseDispatcher>
398 std::string const CommandGrammar<ParseDispatcher>::Context::EmptyString;
404 ///////////////////////////////ih.e////////////////////////////////////////
411 // comment-column: 40
412 // c-file-style: "senf"
413 // indent-tabs-mode: nil
414 // ispell-local-dictionary: "american"
415 // compile-command: "scons -u test"