X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=Console%2FParse.ih;h=5129197d9b824b1fae0dfb58bfefaf7805868e89;hb=456ee576285b76aa46240f8001f426757810dcc1;hp=54c2c450fd566fe13972456d839d0cb13211fa3a;hpb=412cf8e222086fb5d89b15cb11556799e131f390;p=senf.git diff --git a/Console/Parse.ih b/Console/Parse.ih index 54c2c45..5129197 100644 --- a/Console/Parse.ih +++ b/Console/Parse.ih @@ -42,6 +42,11 @@ namespace senf { namespace console { namespace detail { +#ifndef DOXYGEN + + /////////////////////////////////////////////////////////////////////////// + // append_a + struct append_action { template @@ -56,16 +61,15 @@ namespace detail { template inline boost::spirit::ref_value_actor append_a(T & ref) - { - return boost::spirit::ref_value_actor(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); - } + { return boost::spirit::ref_const_ref_actor(ref, value); } + + /////////////////////////////////////////////////////////////////////////// + // Grammar template struct CommandGrammar : boost::spirit::grammar > @@ -78,10 +82,21 @@ namespace detail { /////////////////////////////////////////////////////////////////////////// // The parse context (variables needed while parsing) + typedef ArgumentToken::TokenType TokenType; + struct Context { std::string str; std::vector path; char ch; + TokenType type; + + // 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 TokenType BasicString; + static const TokenType HexString; + static const TokenType Word; + static const std::string EmptyString; }; Context & context; @@ -114,6 +129,10 @@ namespace detail { 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)); } + /////////////////////////////////////////////////////////////////////////// CommandGrammar(ParseDispatcher & d, Context & c) @@ -124,25 +143,23 @@ namespace detail { : public boost::spirit::grammar_def< boost::spirit::rule, boost::spirit::rule > { - boost::spirit::rule command, path, argument, word, string, hexstring, token; - boost::spirit::rule punctuation, hexbyte, balanced_tokens, simple_argument; - boost::spirit::rule complex_argument, builtin, skip; - + boost::spirit::rule command, path, argument, word, string, hexstring, token, + punctuation, hexbyte, balanced_tokens, simple_argument, complex_argument, builtin, + skip, commands, block, statement, relpath, abspath; 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 - // and characters reserved for future extensions special_p ("/(){};"), // Characters which are returned as punctuation tokens - punctuation_p (",="), + // (only allowed within '()') + punctuation_p (",=/{};"), - // Whitespace characters (we don't want newlines in there) - space_p (" \t"), + // 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* ...) @@ -160,28 +177,87 @@ namespace detail { using namespace boost::spirit; typedef ParseDispatcher PD; + /////////////////////////////////////////////////////////////////// + // Spirit grammar + // + // Syntax summary: + // This is EBNF with some minor tweaks to accommodate C++ syntax + // + // * and + like EBNF but they precede their argument + // >> is followed by + // ! optional + // a % b match any number of a's separated by b + // a - b match 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 + + commands + = * command + ; + + command + = builtin >> (ch_p(';') | end_p) + | path >> ( block | statement ) + | 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") + | keyword_p("ls") >> ! path - >> eps_p [ self.dispatch(&PD::builtin_cd, + >> 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)) ] ; - command - = builtin - | path [ self.dispatch(&PD::beginCommand, + block + = ch_p('{') [ self.dispatch(&PD::pushDirectory, + boost::ref(self.context.path)) ] + >> * command + >> ch_p('}') [ self.dispatch(&PD::popDirectory) ] + ; + + statement + = eps_p [ self.dispatch(&PD::beginCommand, boost::ref(self.context.path)) ] >> * argument - >> ! ch_p(';') + >> (ch_p(';') | end_p) >> eps_p [ self.dispatch(&PD::endCommand) ] ; argument = simple_argument [ self.dispatch(&PD::pushArgument, + boost::ref(self.context.type), boost::ref(self.context.str)) ] | complex_argument ; @@ -194,12 +270,14 @@ namespace detail { complex_argument // Argument consists of multiple tokens = ch_p('(') [ self.dispatch(&PD::openGroup) ] - >> * token - >> ch_p(')') [ self.dispatch(&PD::closeGroup) ] + >> * token + >> ch_p(')') [ self.dispatch(&PD::closeGroup) ] ; string // Returns value in context.str = eps_p [ clear_a(self.context.str) ] + >> eps_p [ assign_a(self.context.type, + self.context.BasicString) ] >> lexeme_d [ ch_p('"') @@ -214,15 +292,28 @@ namespace detail { hexstring // Returns value in context.str = eps_p [ clear_a(self.context.str) ] + >> eps_p [ assign_a(self.context.type, + self.context.HexString) ] >> confix_p( "x\"", * hexbyte, '"' ) ; path // Returns value in context.path = eps_p [ clear_a(self.context.path) ] - >> ( ! ch_p('/') [ push_back_a(self.context.path, "") ] ) - >> ( word [ push_back_a(self.context.path) ] + >> relpath | abspath + ; + + relpath + = ( word [ push_back_a(self.context.path) ] % ch_p('/') ) - >> ( ! ch_p('/') [ push_back_a(self.context.path,"") ] ) + >> ( ! ch_p('/') [ push_back_a(self.context.path, + self.context.EmptyString) ] ) + ; + + abspath + = ch_p('/') [ push_back_a(self.context.path, + self.context.EmptyString) ] + >> ( relpath + | eps_p [ push_back_a(self.context.path, "") ] ) ; balanced_tokens @@ -232,7 +323,8 @@ namespace detail { ; token - = simple_argument [ self.dispatch(&PD::pushWord, + = simple_argument [ self.dispatch(&PD::pushWord, + boost::ref(self.context.type), boost::ref(self.context.str)) ] | punctuation [ self.dispatch(&PD::pushPunctuation, boost::ref(self.context.str)) ] @@ -244,7 +336,12 @@ namespace detail { ; word // Returns value in context.str - = lexeme_d[ + word_p ] [ assign_a(self.context.str) ] + = lexeme_d + [ + eps_p [ assign_a(self.context.type, + self.context.Word) ] + >> (+ word_p) [ assign_a(self.context.str) ] + ] ; hexbyte @@ -253,10 +350,16 @@ namespace detail { ; skip - = space_p - | comment_p('#') + = space_p | comment_p('#') ; + /////////////////////////////////////////////////////////////////// + + start_parsers( + commands, // CommandParser + skip // SkipParser + ); + BOOST_SPIRIT_DEBUG_TRACE_RULE(command,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(path,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(argument,1); @@ -270,16 +373,32 @@ namespace detail { BOOST_SPIRIT_DEBUG_TRACE_RULE(simple_argument,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(complex_argument,1); BOOST_SPIRIT_DEBUG_TRACE_RULE(builtin,1); - - start_parsers( - command, // CommandParser - skip // SkipParser - ); - + 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 + ArgumentToken::TokenType const CommandGrammar::Context::BasicString ( + ArgumentToken::BasicString); + + template + ArgumentToken::TokenType const CommandGrammar::Context::HexString( + ArgumentToken::HexString); + + template + ArgumentToken::TokenType const CommandGrammar::Context::Word( + ArgumentToken::Word); + + template + std::string const CommandGrammar::Context::EmptyString; + +#endif + }}} ///////////////////////////////ih.e////////////////////////////////////////