// $Id$ // // Copyright (C) 2008 // Fraunhofer Institute for Open Communication Systems (FOKUS) // Competence Center NETwork research (NET), St. Augustin, GERMANY // Stefan Bund // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the // Free Software Foundation, Inc., // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. /** \file \brief Parse internal header */ #ifndef IH_Parse_ #define IH_Parse_ 1 // Custom includes #include #include #include #include #include #include #include #include #include #include ///////////////////////////////ih.p//////////////////////////////////////// namespace senf { namespace console { namespace detail { #ifndef DOXYGEN /////////////////////////////////////////////////////////////////////////// // append_a struct append_action { template void act(T & ref, Value const & value) const { ref += T(1, value); } template void act(T & ref, Iterator const & f, Iterator const & l) const { ref += T(f,l); } }; template inline boost::spirit::ref_value_actor append_a(T & ref) { return boost::spirit::ref_value_actor(ref); } template inline boost::spirit::ref_const_ref_actor append_a(T & ref, Value const & value) { return boost::spirit::ref_const_ref_actor(ref, value); } /////////////////////////////////////////////////////////////////////////// // Grammar template struct CommandGrammar : boost::spirit::grammar > { /////////////////////////////////////////////////////////////////////////// // Start rules enum { CommandParser, SkipParser, ArgumentsParser }; /////////////////////////////////////////////////////////////////////////// // The parse context (variables needed while parsing) typedef Token::TokenType TokenType; struct Context { std::string str; std::vector path; char ch; Token token; // OUCH ... This is sooooo stupid .. push_back_a and assign_a take their // arguments by const-reference and STORE the REFERENCE ... they do NOT accept // literal values !!!!!! static const Token EmptyToken; }; Context & context; /////////////////////////////////////////////////////////////////////////// // Configuration bool incremental; /////////////////////////////////////////////////////////////////////////// // Dispatching semantic actions ParseDispatcher & dispatcher; struct Dispatch_actor { Dispatch_actor(boost::function fn_) : fn (fn_) {} template void operator()(Value const & value) const { fn(); } template void operator()(Iterator const & f, Iterator const & l) const { fn(); } boost::function fn; }; template Dispatch_actor dispatch(Callback cb) const { return Dispatch_actor(boost::bind(cb, boost::ref(dispatcher))); } template Dispatch_actor dispatch(Callback cb, Arg const & arg) const { return Dispatch_actor(boost::bind(cb, boost::ref(dispatcher), arg)); } template Dispatch_actor dispatch(Callback cb, Arg1 const & arg1, Arg2 const & arg2) const { return Dispatch_actor(boost::bind(cb, boost::ref(dispatcher), arg1, arg2)); } struct TokenSetter_actor { TokenSetter_actor(Context & c, TokenType t) : c_ (c), t_ (t) {} void operator()(std::string const & value) const { c_.token = Token(t_, value); } void operator()(char value) const { c_.token = Token(t_, std::string(1,value)); } template void operator()(Iterator const & f, Iterator const & l) const { c_.token = Token(t_, std::string(f,l)); } Context & c_; TokenType t_; }; struct TokenSetter_value_actor : public TokenSetter_actor { TokenSetter_value_actor(Context & c, TokenType t, std::string & v) : TokenSetter_actor(c,t), v_ (v) {} template void operator()(Value const &) const { TokenSetter_actor::operator()(v_); } template void operator()(Iterator const &, Iterator const &) const { TokenSetter_actor::operator()(v_); } std::string & v_; }; TokenSetter_actor set_token_a(TokenType t) const { return TokenSetter_actor(context, t); } TokenSetter_value_actor set_token_a(TokenType t, std::string & arg) const { return TokenSetter_value_actor(context, t, arg); } /////////////////////////////////////////////////////////////////////////// CommandGrammar(ParseDispatcher & d, Context & c) : context(c), incremental(false), dispatcher(d) {} template struct definition : public boost::spirit::grammar_def< boost::spirit::rule, boost::spirit::rule, boost::spirit::rule > { boost::spirit::rule command, path, argument, word, string, hexstring, token, punctuation, hexbyte, balanced_tokens, simple_argument, complex_argument, builtin, skip, statement, relpath, abspath, arguments, group_start, group_close, statement_end; boost::spirit::chset<> special_p, punctuation_p, space_p, invalid_p, word_p; boost::spirit::distinct_parser<> keyword_p; definition(CommandGrammar const & self) : // Characters with a special meaning within the parser special_p ("/(){};"), // Additional characters which are returned as punctuation tokens // (only allowed within '()'). punctuation_p (",="), // Whitespace characters space_p (" \t\n\r"), // Invalid characters: All chars below \x20 (space) which are not space_p // (don't put a \0 in the chset<> argument *string* ...) invalid_p ( boost::spirit::chset<>('\0') | boost::spirit::chset<>("\x01-\x20") - space_p ), // Valid word characters word_p ( boost::spirit::anychar_p - special_p - punctuation_p - space_p - invalid_p), // Keywords must not be followed by a word char or '/' keyword_p ( word_p | boost::spirit::ch_p('/') ) { using namespace boost::spirit; using namespace phoenix; typedef ParseDispatcher PD; typedef Token AT; /////////////////////////////////////////////////////////////////// // Spirit grammar // // Syntax summary: // This is EBNF with some minor tweaks to accommodate C++ syntax // // * a any number of a's // + a at least one a // ! a an optional a // a >> b a followed by b // a | b a or b // a % b any number of a's separated by b's // a - b a but not b // // Beside this, we use some special parsers (ch_p, eps_p, confix_p, lex_escape_ch_p, // keyword_p, comment_p) and directives (lexeme_d), however, the parser should be // quite readable. // // ch_p match character // eps_p always matches nothing (to attach unconditional actions) // confix_p(a,b,c) match b, preceded by a and terminated by c. Used to parse // string literals and comments // lex_escape_ch_p match a lex style escape char. This is like a C++ style // literal string escape char, however \x will be replaced by 'x' // for any char 'x' if it has no special meaning. // keyword_p match a delimited keyword // comment_p(a,b) match comment starting with a and terminated with b. b // defaults to end-of-line // // lexeme_d don't skip whitespace (as defined by the skip parser) // // Aligned to the right at column 50 are semantic actions. // // For clarity, I have used 'ch_p' explicitly throughout even though it is optional // in most cases. // // More info is in the Boost.Spirit documentation command = builtin >> statement_end | path >> ( group_start | statement ) | group_close | ch_p(';') // Ignore empty commands ; builtin = keyword_p("cd") >> path >> eps_p [ self.dispatch(&PD::builtin_cd, boost::ref(self.context.path)) ] | keyword_p("ls") >> ! path >> eps_p [ self.dispatch(&PD::builtin_ls, boost::ref(self.context.path)) ] | keyword_p("exit") [ self.dispatch(&PD::builtin_exit) ] | keyword_p("help") >> ! path >> eps_p [ self.dispatch(&PD::builtin_help, boost::ref(self.context.path)) ] ; group_start = ch_p('{') [ self.dispatch(&PD::pushDirectory, boost::ref(self.context.path)) ] ; group_close = ch_p('}') [ self.dispatch(&PD::popDirectory) ] ; statement = eps_p [ self.dispatch(&PD::beginCommand, boost::ref(self.context.path)) ] >> arguments >> statement_end [ self.dispatch(&PD::endCommand) ] ; arguments = * argument ; argument = simple_argument [ self.dispatch(&PD::pushToken, boost::ref(self.context.token)) ] | balanced_tokens ; simple_argument // All these return their value in context.token = string | hexstring | word ; string // Returns value in context.token = eps_p [ clear_a(self.context.str) ] >> lexeme_d [ ch_p('"') >> * ( ( lex_escape_ch_p[ assign_a(self.context.ch) ] - '"' ) [ append_a(self.context.str, self.context.ch) ] ) >> ch_p('"') [ self.set_token_a(AT::BasicString, self.context.str) ] ] ; hexstring // Returns value in context.token = eps_p [ clear_a(self.context.str) ] >> confix_p( "x\"", * hexbyte, '"' ) [ self.set_token_a(AT::HexString, self.context.str) ] ; path // Returns value in context.path = eps_p [ clear_a(self.context.path) ] >> relpath | abspath ; relpath = ( word [ push_back_a(self.context.path, self.context.token) ] % ch_p('/') ) >> ( ! ch_p('/') [ push_back_a(self.context.path, self.context.EmptyToken) ] ) ; abspath = ch_p('/') [ push_back_a(self.context.path, self.context.EmptyToken) ] >> ( relpath | eps_p [ push_back_a(self.context.path, self.context.EmptyToken) ] ) ; balanced_tokens = ch_p('(') [ self.set_token_a(AT::ArgumentGroupOpen) ] [ self.dispatch(&PD::pushToken, boost::ref(self.context.token)) ] >> * token >> ch_p(')') [ self.set_token_a(AT::ArgumentGroupClose) ] [ self.dispatch(&PD::pushToken, boost::ref(self.context.token)) ] ; token = simple_argument [ self.dispatch(&PD::pushToken, boost::ref(self.context.token)) ] | punctuation [ self.dispatch(&PD::pushToken, boost::ref(self.context.token)) ] | balanced_tokens ; punctuation // Returns value in context.str = ch_p('/') [ self.set_token_a(AT::PathSeparator) ] | ch_p('{') [ self.set_token_a(AT::DirectoryGroupOpen) ] | ch_p('}') [ self.set_token_a(AT::DirectoryGroupClose) ] | ch_p(';') [ self.set_token_a(AT::CommandTerminator) ] | punctuation_p [ self.set_token_a(AT::OtherPunctuation) ] ; word // Returns value in context.token = lexeme_d [ (+ word_p) [ assign_a(self.context.str) ] ] >> eps_p [ self.set_token_a(AT::Word, self.context.str) ] ; hexbyte = uint_parser() [ append_a(self.context.str) ] ; statement_end = if_p(var(self.incremental)) [ ch_p(';') ] .else_p [ ch_p(';') | end_p ] ; skip = space_p | comment_p('#') ; /////////////////////////////////////////////////////////////////// start_parsers( command, // CommandParser skip, // SkipParser arguments // ArgumentsParser ); BOOST_SPIRIT_DEBUG_TRACE_RULE(command,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(path,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(argument,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(word,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(string,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(hexstring,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(token,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(punctuation,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(hexbyte,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(balanced_tokens,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(simple_argument,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(complex_argument,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(builtin,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(commands,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(block,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(statement,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(relpath,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(abspath,1); } }; }; template Token const CommandGrammar::Context::EmptyToken; #endif }}} ///////////////////////////////ih.e//////////////////////////////////////// #endif // Local Variables: // mode: c++ // fill-column: 100 // comment-column: 40 // c-file-style: "senf" // indent-tabs-mode: nil // ispell-local-dictionary: "american" // compile-command: "scons -u test" // End: