// $Id$ // // Copyright (C) 2008 // Fraunhofer Institute for Open Communication Systems (FOKUS) // // The contents of this file are subject to the Fraunhofer FOKUS Public License // Version 1.0 (the "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // http://senf.berlios.de/license.html // // The Fraunhofer FOKUS Public License Version 1.0 is based on, // but modifies the Mozilla Public License Version 1.1. // See the full license text for the amendments. // // Software distributed under the License is distributed on an "AS IS" basis, // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License // for the specific language governing rights and limitations under the License. // // The Original Code is Fraunhofer FOKUS code. // // The Initial Developer of the Original Code is Fraunhofer-Gesellschaft e.V. // (registered association), Hansastraße 27 c, 80686 Munich, Germany. // All Rights Reserved. // // Contributor(s): // Stefan Bund /** \file \brief Parse internal header */ #ifndef IH_SENF_Scheduler_Console_Parse_ #define IH_SENF_Scheduler_Console_Parse_ 1 // Custom includes #include #include #if HAVE_BOOST_SPIRIT_INCLUDE_CLASSIC_HPP # include # include # include # include #else # include # include # include # include #endif #include //-///////////////////////////////////////////////////////////////////////////////////////////////// namespace senf { namespace console { namespace detail { #if HAVE_BOOST_SPIRIT_INCLUDE_CLASSIC_HPP namespace boost_spirit = ::boost::spirit::classic; #else namespace boost_spirit = ::boost::spirit; #endif #ifndef DOXYGEN struct FilePositionWithIndex : public boost_spirit::file_position { int index; FilePositionWithIndex(std::string const & file_ = std::string(), int line_ = 1, int column_ = 1, int index_ = 0) : boost_spirit::file_position (file_, line_, column_), index (index_) {} bool operator==(const FilePositionWithIndex & fp) const { return boost_spirit::file_position::operator==(fp) && index == fp.index; } }; struct PositionOf { template struct result { typedef FilePositionWithIndex type; }; template FilePositionWithIndex operator()(A1 & a1) { return a1.get_position(); } FilePositionWithIndex operator()(char const * a1) { return FilePositionWithIndex(); } }; ::phoenix::function const positionOf; //-///////////////////////////////////////////////////////////////////////////////////////////// // Grammar template struct CommandGrammar : boost_spirit::grammar > { //-///////////////////////////////////////////////////////////////////////////////////////// // Start rules enum { CommandParser, SkipParser, ArgumentsParser, PathParser }; //-///////////////////////////////////////////////////////////////////////////////////////// // The parse context (variables needed while parsing) typedef Token::TokenType TokenType; struct Context { std::string str; std::vector path; char ch; Token token; FilePositionWithIndex pos; }; Context & context; //-///////////////////////////////////////////////////////////////////////////////////////// // Configuration bool incremental; //-///////////////////////////////////////////////////////////////////////////////////////// // Dispatching semantic actions ParseDispatcher & dispatcher; //-///////////////////////////////////////////////////////////////////////////////////////// // charachter sets static boost_spirit::chset<> & special_p() { static boost_spirit::chset<> p ("/(){};\""); return p; } static boost_spirit::chset<> & punctuation_p() { static boost_spirit::chset<> p (",="); return p; } static boost_spirit::chset<> & space_p() { static boost_spirit::chset<> p (" \t\n\r"); return p; } static boost_spirit::chset<> & invalid_p() { static boost_spirit::chset<> p ((boost_spirit::chset<>('\0') | boost_spirit::chset<>("\x01-\x20")) - space_p() ); return p; } static boost_spirit::chset<> & word_p() { static boost_spirit::chset<> p (boost_spirit::anychar_p - special_p() - punctuation_p() - space_p() - invalid_p()); return p; } static boost_spirit::distinct_parser<> & keyword_p() { static boost_spirit::distinct_parser<> p (word_p() | boost_spirit::ch_p('/')); return p; } //-///////////////////////////////////////////////////////////////////////////////////////// // Errors enum Errors { EndOfStatementExpected, PathExpected, ClosingParenExpected, QuoteExpected }; //-///////////////////////////////////////////////////////////////////////////////////////// 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 > { boost_spirit::rule command, path, argument, word, string, hexstring, word_or_string, token, punctuation, hexbyte, balanced_tokens, simple_argument, complex_argument, builtin, skip, statement, relpath, abspath, arguments, group_start, group_close, statement_end, opt_path; definition(CommandGrammar const & self) { using namespace boost_spirit; using namespace ::phoenix; using namespace senf::phoenix; typedef ParseDispatcher PD; actor< variable< char > > ch_ (self.context.ch); actor< variable< std::string > > str_ (self.context.str); actor< variable< std::vector > > path_ (self.context.path); actor< variable< Token > > token_ (self.context.token); actor< variable< FilePositionWithIndex > > pos_ (self.context.pos); actor< variable< ParseDispatcher > > d_ (self.dispatcher); assertion end_of_statement_expected (EndOfStatementExpected); assertion path_expected (PathExpected); assertion closing_paren_expected (ClosingParenExpected); assertion quote_expected (QuoteExpected); //-///////////////////////////////////////////////////////////////////////////////// // 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 >> end_of_statement_expected(statement_end) | group_close | ch_p(';') // Ignore empty commands | statement ; statement = path_expected(path) [ bind(&PD::beginCommand)(d_, path_) ] >> arguments >> end_of_statement_expected( ( group_start | statement_end ) [ bind(&PD::endCommand)(d_) ] ) ; builtin = self.keyword_p()("cd") >> path_expected(path) >> eps_p [ bind(&PD::builtin_cd)(d_, path_) ] | self.keyword_p()("ls") >> ! path >> eps_p [ bind(&PD::builtin_ls)(d_, path_) ] | self.keyword_p()("ll") >> ! path >> eps_p [ bind(&PD::builtin_ll)(d_, path_) ] | self.keyword_p()("lr") >> ! path >> eps_p [ bind(&PD::builtin_lr)(d_, path_) ] | self.keyword_p()("exit") [ bind(&PD::builtin_exit)(d_) ] | self.keyword_p()("help") >> ! path >> eps_p [ bind(&PD::builtin_help)(d_, path_) ] ; group_start = ch_p('{') [ bind(&PD::pushDirectory)(d_) ] ; group_close = ch_p('}') [ bind(&PD::popDirectory)(d_) ] ; arguments = * argument ; argument = simple_argument [ bind(&PD::pushToken)(d_, token_) ] | balanced_tokens ; simple_argument // All these return their value in context.token = string | hexstring | word ; string // Returns value in context.token = eps_p [ pos_ = positionOf(arg1) ][ clear(str_) ] >> lexeme_d [ ch_p('"') >> * ( ( lex_escape_ch_p[ ch_ = arg1 ] - '"' ) [ str_ += ch_ ] ) >> quote_expected(ch_p('"')) [ token_ = construct_(Token::BasicString, str_, pos_) ] ] ; hexstring // Returns value in context.token = eps_p [ pos_ = positionOf(arg1) ][ clear(str_) ] >> "x\"" >> * ( hexbyte - ch_p('"') ) >> quote_expected(ch_p('"')) [ token_ = construct_(Token::HexString, str_, pos_) ] ; opt_path = ! path [ bind(&PD::beginCommand)(d_, path_) ] [ bind(&PD::endCommand)(d_) ] ; path // Returns value in context.path = eps_p [ clear(path_) ] >> relpath | abspath ; relpath = ( word_or_string [ push_back(path_, token_) ] % +ch_p('/') ) >> ( ! (+ch_p('/') ) [ push_back(path_, construct_()) ] ) ; abspath = (+ch_p('/')) [ push_back(path_, construct_()) ] >> ( relpath | eps_p [ push_back(path_, construct_()) ] ) ; balanced_tokens = eps_p [ pos_ = positionOf(arg1) ] >> ch_p('(') [ token_ = construct_( Token::ArgumentGroupOpen, "(", pos_) ] [ bind(&PD::pushToken)(d_, token_) ] >> * token >> eps_p [ pos_ = positionOf(arg1) ] >> closing_paren_expected(ch_p(')')) [ token_ = construct_( Token::ArgumentGroupClose, ")", pos_) ] [ bind(&PD::pushToken)(d_, token_) ] ; token = simple_argument [ bind(&PD::pushToken)(d_, token_) ] | punctuation [ bind(&PD::pushToken)(d_, token_) ] | balanced_tokens ; punctuation // Returns value in context.str = eps_p [ pos_ = positionOf(arg1) ] >> ( ch_p('/') [ token_ = construct_( Token::PathSeparator, "/") ] | ch_p('{') [ token_ = construct_( Token::DirectoryGroupOpen, "{") ] | ch_p('}') [ token_ = construct_( Token::DirectoryGroupClose, "}") ] | ch_p(';') [ token_ = construct_( Token::CommandTerminator, ";") ] | self.punctuation_p() [ token_ = construct_( Token::OtherPunctuation, construct_(1u, arg1), pos_) ] ) ; word // Returns value in context.token = eps_p [ pos_ = positionOf(arg1) ] >> lexeme_d [ (+ self.word_p()) [ str_ = construct_(arg1, arg2) ] ] >> eps_p [ token_ = construct_( Token::Word, str_, pos_) ] ; word_or_string = word | string ; hexbyte = uint_parser() [ push_back(str_, arg1) ] ; statement_end = if_p(var(self.incremental)) [ ch_p(';') ] .else_p [ ch_p(';') | end_p ] ; skip = self.space_p() | comment_p('#') ; //-///////////////////////////////////////////////////////////////////////////////// this->start_parsers( command, // CommandParser skip, // SkipParser arguments, // ArgumentsParser opt_path // PathParser ); 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); } }; }; #endif }}} //-///////////////////////////////////////////////////////////////////////////////////////////////// #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: