X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=Console%2FParse.ih;h=5129197d9b824b1fae0dfb58bfefaf7805868e89;hb=456ee576285b76aa46240f8001f426757810dcc1;hp=d5a9e2d65c71d49ef80b0eac60f85940c867419a;hpb=327ff174bdc67db20c64d92c9a171abfa5443333;p=senf.git diff --git a/Console/Parse.ih b/Console/Parse.ih index d5a9e2d..5129197 100644 --- a/Console/Parse.ih +++ b/Console/Parse.ih @@ -27,9 +27,10 @@ #define IH_Parse_ 1 // Custom includes +#include #include #include -#include +#include #include #include #include @@ -41,6 +42,11 @@ namespace senf { namespace console { namespace detail { +#ifndef DOXYGEN + + /////////////////////////////////////////////////////////////////////////// + // append_a + struct append_action { template @@ -55,26 +61,42 @@ 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 > { /////////////////////////////////////////////////////////////////////////// + // Start rules + + enum { CommandParser, SkipParser }; + + /////////////////////////////////////////////////////////////////////////// // 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; @@ -107,50 +129,155 @@ 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) : context(c), dispatcher(d) {} template - struct definition + struct definition + : 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; + 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 + special_p ("/(){};"), + + // Characters which are returned as punctuation tokens + // (only allowed within '()') + punctuation_p (",=/{};"), - definition(CommandGrammar const & self) { + // 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; 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 - = path [ self.dispatch(&PD::beginCommand, - boost::cref(self.context.str)) ] + = 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") + >> ! 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)) ] + ; + + 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::cref(self.context.str)) ] + boost::ref(self.context.type), + boost::ref(self.context.str)) ] | complex_argument ; simple_argument // All these return their value in context.str = string | hexstring - | path + | word ; 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('"') @@ -165,16 +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.str - = eps_p [ clear_a(self.context.str) ] - >> ( ! ch_p('/') [ append_a(self.context.str) ] - ) - >> ( word [ append_a(self.context.str) ] - % ch_p('/') [ append_a(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) ] + % ch_p('/') ) + >> ( ! 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 @@ -184,19 +323,25 @@ namespace detail { ; token - = simple_argument [ self.dispatch(&PD::pushWord, - boost::cref(self.context.str)) ] + = simple_argument [ self.dispatch(&PD::pushWord, + boost::ref(self.context.type), + boost::ref(self.context.str)) ] | punctuation [ self.dispatch(&PD::pushPunctuation, - boost::cref(self.context.str)) ] + boost::ref(self.context.str)) ] | balanced_tokens ; punctuation // Returns value in context.str - = regex_p("[,=]") [ assign_a(self.context.str) ] + = punctuation_p [ assign_a(self.context.str) ] ; - word - = regex_p("[^ \t\n\r;,=(){}/\"]+") + word // Returns value in context.str + = lexeme_d + [ + eps_p [ assign_a(self.context.type, + self.context.Word) ] + >> (+ word_p) [ assign_a(self.context.str) ] + ] ; hexbyte @@ -204,42 +349,55 @@ namespace detail { [ append_a(self.context.str) ] ; - BOOST_SPIRIT_DEBUG_RULE(command); - BOOST_SPIRIT_DEBUG_RULE(path); - BOOST_SPIRIT_DEBUG_RULE(argument); - BOOST_SPIRIT_DEBUG_RULE(word); - BOOST_SPIRIT_DEBUG_RULE(string); - BOOST_SPIRIT_DEBUG_RULE(hexstring); - BOOST_SPIRIT_DEBUG_RULE(token); - BOOST_SPIRIT_DEBUG_RULE(punctuation); - BOOST_SPIRIT_DEBUG_RULE(hexbyte); - BOOST_SPIRIT_DEBUG_RULE(balanced_tokens); - BOOST_SPIRIT_DEBUG_RULE(simple_argument); - BOOST_SPIRIT_DEBUG_RULE(complex_argument); - } + skip + = space_p | comment_p('#') + ; - boost::spirit::rule const & start() const { return command; } + /////////////////////////////////////////////////////////////////// + + 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); + 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); + } }; }; - struct SkipGrammar - : public boost::spirit::grammar - { - template - struct definition - { - boost::spirit::rule rule; + template + ArgumentToken::TokenType const CommandGrammar::Context::BasicString ( + ArgumentToken::BasicString); - definition(SkipGrammar const & self) { - rule - = boost::spirit::regex_p("[ \t]+") - | boost::spirit::comment_p('#') - ; - } + template + ArgumentToken::TokenType const CommandGrammar::Context::HexString( + ArgumentToken::HexString); - boost::spirit::rule const & start() const { return rule; } - }; - }; + template + ArgumentToken::TokenType const CommandGrammar::Context::Word( + ArgumentToken::Word); + + template + std::string const CommandGrammar::Context::EmptyString; + +#endif }}}