Move sourcecode into 'senf/' directory
[senf.git] / senf / Utils / Console / Parse.ih
diff --git a/senf/Utils/Console/Parse.ih b/senf/Utils/Console/Parse.ih
new file mode 100644 (file)
index 0000000..fe95008
--- /dev/null
@@ -0,0 +1,442 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// 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_SENF_Scheduler_Console_Parse_
+#define IH_SENF_Scheduler_Console_Parse_ 1
+
+// Custom includes
+#include <vector>
+#include "../../config.hh"
+#include <boost/spirit.hpp>
+#include <boost/spirit/utility/grammar_def.hpp>
+#include <boost/spirit/dynamic.hpp>
+#include <boost/spirit/phoenix.hpp>
+#include "../../Utils/Phoenix.hh"
+
+///////////////////////////////ih.p////////////////////////////////////////
+
+namespace senf {
+namespace console {
+namespace detail {
+
+#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 <class A1> struct result { typedef FilePositionWithIndex type; };
+        template <class A1> FilePositionWithIndex operator()(A1 & a1) { return a1.get_position(); }
+        FilePositionWithIndex operator()(char const * a1) { return FilePositionWithIndex(); }
+    };
+
+    ::phoenix::function<PositionOf> const positionOf;
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Grammar
+
+    template <class ParseDispatcher>
+    struct CommandGrammar : boost::spirit::grammar<CommandGrammar<ParseDispatcher> >
+    {
+        ///////////////////////////////////////////////////////////////////////////
+        // 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<Token> 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<> punctuation_p;
+        static boost::spirit::chset<> space_p;
+        static boost::spirit::chset<> invalid_p;
+        static boost::spirit::chset<> word_p;
+        static boost::spirit::distinct_parser<> keyword_p;
+
+        ///////////////////////////////////////////////////////////////////////////
+        // Errors
+
+        enum Errors {
+            EndOfStatementExpected,
+            PathExpected,
+            ClosingParenExpected,
+            QuoteExpected
+        };
+
+        ///////////////////////////////////////////////////////////////////////////
+
+        CommandGrammar(ParseDispatcher & d, Context & c) 
+            : context(c), incremental(false), dispatcher(d) {}
+
+        template <class Scanner>
+        struct definition 
+            : public boost::spirit::grammar_def< boost::spirit::rule<Scanner>, 
+                                                 boost::spirit::rule<Scanner>,
+                                                 boost::spirit::rule<Scanner>,
+                                                 boost::spirit::rule<Scanner> >
+        {
+            boost::spirit::rule<Scanner> 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, 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<Token> > >    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<Errors> end_of_statement_expected   (EndOfStatementExpected);
+                assertion<Errors> path_expected               (PathExpected);
+                assertion<Errors> closing_paren_expected      (ClosingParenExpected);
+                assertion<Errors> 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>(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>(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                 [ push_back(path_, token_) ]
+                           % ch_p('/') )
+                      >> ( ! ch_p('/')            [ push_back(path_, construct_<Token>()) ] )
+                    ;
+
+                abspath
+                    =    ch_p('/')                [ push_back(path_, construct_<Token>()) ]
+                      >> ( relpath
+                         | eps_p                  [ push_back(path_, construct_<Token>()) ] )
+                    ;
+
+                balanced_tokens 
+                    =    eps_p                    [ pos_ = positionOf(arg1) ]
+                      >> ch_p('(')                [ token_ = construct_<Token>(
+                                                        Token::ArgumentGroupOpen,
+                                                        "(",
+                                                        pos_) ]
+                                                  [ bind(&PD::pushToken)(d_, token_) ]
+                      >> * token
+                      >> eps_p                    [ pos_ = positionOf(arg1) ]
+                      >> closing_paren_expected(ch_p(')'))
+                                                  [ token_ = construct_<Token>(
+                                                        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>(
+                                                          Token::PathSeparator,
+                                                          "/") ]
+                         | ch_p('{')                [ token_ = construct_<Token>(
+                                                          Token::DirectoryGroupOpen,
+                                                          "{") ]
+                         | ch_p('}')                [ token_ = construct_<Token>(
+                                                          Token::DirectoryGroupClose,
+                                                          "}") ]
+                         | ch_p(';')                [ token_ = construct_<Token>(
+                                                          Token::CommandTerminator,
+                                                          ";") ]
+                         | self.punctuation_p       [ token_ = construct_<Token>(
+                                                          Token::OtherPunctuation,
+                                                          construct_<std::string>(1u, arg1),
+                                                          pos_) ]
+                        )
+                    ;
+
+                word                    // Returns value in context.token
+                    =    eps_p                    [ pos_ = positionOf(arg1) ]
+                      >> lexeme_d
+                         [
+                             (+ self.word_p)      [ str_ = construct_<std::string>(arg1, arg2) ]
+                         ]
+                      >> eps_p                    [ token_ = construct_<Token>(
+                                                        Token::Word, 
+                                                        str_,
+                                                        pos_) ]
+                    ;
+
+                hexbyte
+                    =    uint_parser<char, 16, 2, 2>()
+                                                  [ 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('#')
+                    ;
+
+                ///////////////////////////////////////////////////////////////////
+
+                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);
+            }
+        };
+    };
+
+    template <class PD> boost::spirit::chset<> CommandGrammar<PD>::special_p (
+        "/(){};\"");
+    template <class PD> boost::spirit::chset<> CommandGrammar<PD>::punctuation_p (
+        ",=");
+    template <class PD> boost::spirit::chset<> CommandGrammar<PD>::space_p (
+        " \t\n\r");
+    template <class PD> boost::spirit::chset<> CommandGrammar<PD>::invalid_p ( 
+        (boost::spirit::chset<>('\0') | boost::spirit::chset<>("\x01-\x20")) - space_p );
+    template <class PD> boost::spirit::chset<> CommandGrammar<PD>::word_p (
+        boost::spirit::anychar_p - special_p - punctuation_p - space_p - invalid_p);
+    template <class PD> boost::spirit::distinct_parser<> CommandGrammar<PD>::keyword_p (
+        word_p | boost::spirit::ch_p('/'));
+
+#endif
+
+}}}
+
+///////////////////////////////ih.e////////////////////////////////////////
+#endif
+
+\f
+// 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: